diff --git a/WindowsForms/Managed/System/Resources/IAliasResolver.cs b/WindowsForms/Managed/System/Resources/IAliasResolver.cs new file mode 100644 index 000000000..a62209d33 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/IAliasResolver.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + using System.Runtime.Serialization; + using System; + using System.Reflection; + /// + /// + /// Summary of IAliasResolver. + /// + internal interface IAliasResolver { + AssemblyName ResolveAlias(string alias); + void PushAlias(string alias, AssemblyName name); + } +} + diff --git a/WindowsForms/Managed/System/Resources/ResXDataNode.cs b/WindowsForms/Managed/System/Resources/ResXDataNode.cs new file mode 100644 index 000000000..713992dcf --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXDataNode.cs @@ -0,0 +1,1077 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; + using System; + using System.Windows.Forms; + using System.Reflection; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.Text; + using System.ComponentModel; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + using System.Resources; + using System.Xml; + using System.ComponentModel.Design; + using System.Globalization; + using System.Security.Permissions; + using System.Runtime.Versioning; +#if SYSTEM_WEB + using System.Web; // This is needed to access the SR resource strings +#endif + + + /// + /// + /// + /// + [Serializable] + [PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] +#if SYSTEM_WEB + internal sealed class ResXDataNode : ISerializable { +#else + public sealed class ResXDataNode : ISerializable { +#endif + + private static readonly char[] SpecialChars = new char[]{' ', '\r', '\n'}; + + private DataNodeInfo nodeInfo; + + private string name; + private string comment; +#if UNUSED + private string mimeType; + private string valueData; +#endif + + private string typeName; // is only used when we create a resxdatanode manually with an object and contains the FQN + + private string fileRefFullPath; + private string fileRefType; + private string fileRefTextEncoding; + + private object value; + private ResXFileRef fileRef; + + private IFormatter binaryFormatter = null; + + // this is going to be used to check if a ResXDataNode is of type ResXFileRef + private static ITypeResolutionService internalTypeResolver = new AssemblyNamesTypeResolutionService(new AssemblyName[] { new AssemblyName("System.Windows.Forms") }); + + // call back function to get type name for multitargeting. + // No public property to force using constructors for the following reasons: + // 1. one of the constructors needs this field (if used) to initialize the object, make it consistent with the other ctrs to avoid errors. + // 2. once the object is constructed the delegate should not be changed to avoid getting inconsistent results. + private Func typeNameConverter; + + // constructors + + private ResXDataNode() { + } + + // + // this is a deep clone + // + internal ResXDataNode DeepClone() { + ResXDataNode result = new ResXDataNode(); + result.nodeInfo = (this.nodeInfo != null) ? this.nodeInfo.Clone() : null; // nodeinfo is just made up of immutable objects, we don't need to clone it + result.name = this.name; + result.comment = this.comment; +#if UNUSED + result.mimeType = this.mimeType; + result.valueData = this.valueData; +#endif + result.typeName = this.typeName; + result.fileRefFullPath = this.fileRefFullPath; + result.fileRefType = this.fileRefType; + result.fileRefTextEncoding = this.fileRefTextEncoding; + result.value = this.value; // we don't clone the value, because we don't know how + result.fileRef = (this.fileRef != null) ? this.fileRef.Clone() : null; + result.typeNameConverter = this.typeNameConverter; + return result; + } + + /// + /// + /// + /// + public ResXDataNode(string name, object value) : this(name, value, null) { + } + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "name" is the name of the param passed in. + // So we don't have to localize it. + ] + public ResXDataNode(string name, object value, Func typeNameConverter) { + if(name == null) { + throw (new ArgumentNullException("name")); + } + if(name.Length == 0) { + throw (new ArgumentException("name")); + } + + this.typeNameConverter = typeNameConverter; + + Type valueType = (value == null) ? typeof(object) : value.GetType(); + if (value != null && !valueType.IsSerializable) { + throw new InvalidOperationException(SR.GetString(SR.NotSerializableType, name, valueType.FullName)); + } else if (value!= null) { + this.typeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter); + } + + this.name = name; + this.value = value; + } + + /// + /// + /// + /// + public ResXDataNode(string name, ResXFileRef fileRef) : this(name, fileRef, null) { + } + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "name" is the name of the param passed in. + // So we don't have to localize it. + ] + public ResXDataNode(string name, ResXFileRef fileRef, Func typeNameConverter) { + if(name == null) { + throw (new ArgumentNullException("name")); + } + if(fileRef == null) { + throw (new ArgumentNullException("fileRef")); + } + if(name.Length == 0) { + throw (new ArgumentException("name")); + } + this.name = name; + this.fileRef = fileRef; + this.typeNameConverter = typeNameConverter; + } + + internal ResXDataNode(DataNodeInfo nodeInfo, string basePath) { + this.nodeInfo = nodeInfo; + InitializeDataNode(basePath); + } + + private void InitializeDataNode(string basePath) { + + // we can only use our internal type resolver here + // because we only want to check if this is a ResXFileRef node + // and we can't be sure that we have a typeResolutionService that can + // recognize this. It's not very clean but this should work. + Type nodeType = null; + if(!string.IsNullOrEmpty(nodeInfo.TypeName)) // can be null if we have a string (default for string is TypeName == null) + nodeType = internalTypeResolver.GetType(nodeInfo.TypeName, false, true); + if(nodeType != null && nodeType.Equals(typeof(ResXFileRef))) { + // we have a fileref, split the value data and populate the fields + string[] fileRefDetails = ResXFileRef.Converter.ParseResxFileRefString(nodeInfo.ValueData); + if(fileRefDetails != null && fileRefDetails.Length > 1) { + if(!Path.IsPathRooted(fileRefDetails[0]) && basePath != null) { + fileRefFullPath = Path.Combine(basePath, fileRefDetails[0]); + } else { + fileRefFullPath = fileRefDetails[0]; + } + fileRefType = fileRefDetails[1]; + if(fileRefDetails.Length > 2) { + fileRefTextEncoding = fileRefDetails[2]; + } + } + } + } + + + /// + /// + /// + /// + public string Comment { + get { + string result = comment; + if(result == null && nodeInfo != null) { + result = nodeInfo.Comment; + } + return (result == null ? "" : result); + } + set { + comment= value; + } + } + + /// + /// + /// + /// + public string Name { + get { + string result = name; + if(result == null && nodeInfo != null) { + result = nodeInfo.Name; + } + return result; + } + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "Name" is the name of the property. + // So we don't have to localize it. + ] + set { + if(value == null) { + throw (new ArgumentNullException("Name")); + } + if(value.Length == 0) { + throw (new ArgumentException("Name")); + } + name = value; + } + } + +#if UNUSED + private string MimeType { + get { + string result = mimeType; + if(result == null && nodeInfo != null) { + result = nodeInfo.MimeType; + } + return result; + } + } + + private string ValueData { + get { + string result = valueData; + if(result == null && nodeInfo != null) { + result = nodeInfo.ValueData; + } + return result; + } + } +#endif + + /// + /// + /// + /// + public ResXFileRef FileRef { + get { + if(FileRefFullPath==null) { + return null; + } + if(fileRef == null) { + if(String.IsNullOrEmpty(fileRefTextEncoding)) + { + fileRef = new ResXFileRef(FileRefFullPath, FileRefType); + } else { + fileRef = new ResXFileRef(FileRefFullPath, FileRefType, Encoding.GetEncoding(FileRefTextEncoding)); + } + } + return fileRef; + } + } + + + private string FileRefFullPath { + get { + string result = (fileRef==null ? null : fileRef.FileName); + if(result == null) { + result = fileRefFullPath; + } + return result; + } + } + + private string FileRefType { + get { + string result = (fileRef==null ? null : fileRef.TypeName); + if(result == null) { + result = fileRefType; + } + return result; + } + } + + private string FileRefTextEncoding { + get { + string result = (fileRef==null ? null : (fileRef.TextFileEncoding == null ? null : fileRef.TextFileEncoding.BodyName)); + if(result == null) { + result = fileRefTextEncoding; + } + + return result; + } + } + + +#if !SYSTEM_WEB // System.Web does not link with the Soap assembly + /// + /// As a performance optimization, we isolate the soap class here in a separate + /// function. We don't care about the binary formatter because it lives in + /// mscorlib, which is already loaded. The soap formatter lives in a separate + /// assembly, however, so there is value in preventing it from needlessly + /// being loaded. + /// + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private IFormatter CreateSoapFormatter() { + return new System.Runtime.Serialization.Formatters.Soap.SoapFormatter(); + } +#endif + + + private static string ToBase64WrappedString(byte[] data) { + const int lineWrap = 80; + const string crlf = "\r\n"; + const string prefix = " "; + string raw = Convert.ToBase64String(data); + if (raw.Length > lineWrap) { + StringBuilder output = new StringBuilder(raw.Length + (raw.Length / lineWrap) * 3); // word wrap on lineWrap chars, \r\n + int current = 0; + for (; current < raw.Length - lineWrap; current+=lineWrap) { + output.Append(crlf); + output.Append(prefix); + output.Append(raw, current, lineWrap); + } + output.Append(crlf); + output.Append(prefix); + output.Append(raw, current, raw.Length - current); + output.Append(crlf); + return output.ToString(); + } + else { + return raw; + } + } + + private void FillDataNodeInfoFromObject(DataNodeInfo nodeInfo, object value) { + CultureInfo ci = value as CultureInfo; + if( ci != null) { // special-case CultureInfo, cannot use CultureInfoConverter for serialization (see DevDiv#578701). + nodeInfo.ValueData = ci.Name; + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(CultureInfo), this.typeNameConverter); + } + else if (value is string) { + nodeInfo.ValueData = (string)value; + if(value == null) { + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter); + } + } + else if (value is byte[]) { + nodeInfo.ValueData = ToBase64WrappedString((byte[])value); + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(byte[]), this.typeNameConverter); + } + else { + Type valueType = (value == null) ? typeof(object) : value.GetType(); + if (value != null && !valueType.IsSerializable) { + throw new InvalidOperationException(SR.GetString(SR.NotSerializableType, name, valueType.FullName)); + } + TypeConverter tc = TypeDescriptor.GetConverter(valueType); + bool toString = tc.CanConvertTo(typeof(string)); + bool fromString = tc.CanConvertFrom(typeof(string)); + try { + if (toString && fromString) { + nodeInfo.ValueData = tc.ConvertToInvariantString(value); + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter); + return; + } + } + catch (Exception ex) { + // Some custom type converters will throw in ConvertTo(string) + // to indicate that this object should be serialized through ISeriazable + // instead of as a string. This is semi-wrong, but something we will have to + // live with to allow user created Cursors to be serializable. + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + + bool toByteArray = tc.CanConvertTo(typeof(byte[])); + bool fromByteArray = tc.CanConvertFrom(typeof(byte[])); + if (toByteArray && fromByteArray) { + byte[] data = (byte[])tc.ConvertTo(value, typeof(byte[])); + string text = ToBase64WrappedString(data); + nodeInfo.ValueData = text; + nodeInfo.MimeType = ResXResourceWriter.ByteArraySerializedObjectMimeType; + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter); + return; + } + + if (value == null) { + nodeInfo.ValueData = string.Empty; + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter); + } + else { + if (binaryFormatter == null) { + binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + binaryFormatter.Binder = new ResXSerializationBinder(this.typeNameConverter); + } + + MemoryStream ms = new MemoryStream(); + binaryFormatter.Serialize(ms, value); + string text = ToBase64WrappedString(ms.ToArray()); + nodeInfo.ValueData = text; + nodeInfo.MimeType = ResXResourceWriter.DefaultSerializedObjectMimeType; + } + } + + } + + + private object GenerateObjectFromDataNodeInfo(DataNodeInfo dataNodeInfo, ITypeResolutionService typeResolver) { + object result = null; + string mimeTypeName = dataNodeInfo.MimeType; + // default behavior: if we dont have a type name, it's a string + string typeName = (dataNodeInfo.TypeName == null || dataNodeInfo.TypeName.Length==0 ? MultitargetUtil.GetAssemblyQualifiedName(typeof(string), this.typeNameConverter) : dataNodeInfo.TypeName); + + if (mimeTypeName != null && mimeTypeName.Length > 0) { + if (String.Equals(mimeTypeName, ResXResourceWriter.BinSerializedObjectMimeType) + || String.Equals(mimeTypeName, ResXResourceWriter.Beta2CompatSerializedObjectMimeType) + || String.Equals(mimeTypeName, ResXResourceWriter.CompatBinSerializedObjectMimeType)) { + string text = dataNodeInfo.ValueData; + byte[] serializedData; + serializedData = FromBase64WrappedString(text); + + if (binaryFormatter == null) { + binaryFormatter = new BinaryFormatter(); + binaryFormatter.Binder = new ResXSerializationBinder(typeResolver); + } + IFormatter formatter = binaryFormatter; + if (serializedData != null && serializedData.Length > 0) { + result = formatter.Deserialize(new MemoryStream(serializedData)); + if (result is ResXNullRef) { + result = null; + } + } + } +#if !SYSTEM_WEB // System.Web does not link with the Soap assembly + else if (String.Equals(mimeTypeName, ResXResourceWriter.SoapSerializedObjectMimeType) + || String.Equals(mimeTypeName, ResXResourceWriter.CompatSoapSerializedObjectMimeType)) { + string text = dataNodeInfo.ValueData; + byte[] serializedData; + serializedData = FromBase64WrappedString(text); + + if (serializedData != null && serializedData.Length > 0) { + + // Performance : don't inline a new SoapFormatter here. That will always bring in + // the soap assembly, which we don't want. Throw this in another + // function so the class doesn't have to get loaded. + // + IFormatter formatter = CreateSoapFormatter(); + result = formatter.Deserialize(new MemoryStream(serializedData)); + if (result is ResXNullRef) { + result = null; + } + } + } +#endif + else if (String.Equals(mimeTypeName, ResXResourceWriter.ByteArraySerializedObjectMimeType)) { + if (typeName != null && typeName.Length > 0) { + Type type = ResolveType(typeName, typeResolver); + if (type != null) { + TypeConverter tc = TypeDescriptor.GetConverter(type); + if (tc.CanConvertFrom(typeof(byte[]))) { + string text = dataNodeInfo.ValueData; + byte[] serializedData; + serializedData = FromBase64WrappedString(text); + + if (serializedData != null) { + result = tc.ConvertFrom(serializedData); + } + } + } + else { + string newMessage = SR.GetString(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); + XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); + TypeLoadException newTle = new TypeLoadException(newMessage, xml); + + throw newTle; + } + } + } + } + else if (typeName != null && typeName.Length > 0) { + Type type = ResolveType(typeName, typeResolver); + if (type != null) { + if (type == typeof(ResXNullRef)) { + result = null; + } + else if (typeName.IndexOf("System.Byte[]") != -1 && typeName.IndexOf("mscorlib") != -1) { + // Handle byte[]'s, which are stored as base-64 encoded strings. + // We can't hard-code byte[] type name due to version number + // updates & potential whitespace issues with ResX files. + result = FromBase64WrappedString(dataNodeInfo.ValueData); + } + else { + TypeConverter tc = TypeDescriptor.GetConverter(type); + if (tc.CanConvertFrom(typeof(string))) { + string text = dataNodeInfo.ValueData; + try { + result = tc.ConvertFromInvariantString(text); + } catch (NotSupportedException nse) { + string newMessage = SR.GetString(SR.NotSupported, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X, nse.Message); + XmlException xml = new XmlException(newMessage, nse, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); + NotSupportedException newNse = new NotSupportedException(newMessage, xml); + throw newNse; + } + } + else { + Debug.WriteLine("Converter for " + type.FullName + " doesn't support string conversion"); + } + } + } + else { + string newMessage = SR.GetString(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); + XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X); + TypeLoadException newTle = new TypeLoadException(newMessage, xml); + + throw newTle; + } + } + else { + // if mimeTypeName and typeName are not filled in, the value must be a string + Debug.Assert(value is string, "Resource entries with no Type or MimeType must be encoded as strings"); + } + return result; + } + + internal DataNodeInfo GetDataNodeInfo() { + bool shouldSerialize = true; + if(nodeInfo != null) { + shouldSerialize = false; + } else { + nodeInfo = new DataNodeInfo(); + } + nodeInfo.Name = Name; + nodeInfo.Comment = Comment; + + // We always serialize if this node represents a FileRef. This is because FileRef is a public property, + // so someone could have modified it. + if(shouldSerialize || FileRefFullPath != null) { + // if we dont have a datanodeinfo it could be either + // a direct object OR a fileref + if(FileRefFullPath != null) { + nodeInfo.ValueData = FileRef.ToString(); + nodeInfo.MimeType = null; + nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXFileRef), this.typeNameConverter); + } else { + // serialize to string inside the nodeInfo + FillDataNodeInfoFromObject(nodeInfo, value); + } + + } + return nodeInfo; + } + + /// + /// + /// Might return the position in the resx file of the current node, if known + /// otherwise, will return Point(0,0) since point is a struct + /// + public Point GetNodePosition() { + if(nodeInfo == null) { + return new Point(); + } else { + return nodeInfo.ReaderPosition; + } + } + + /// + /// + /// Get the FQ type name for this datanode. + /// We return typeof(object) for ResXNullRef + /// + public string GetValueTypeName(ITypeResolutionService typeResolver) { + // the type name here is always a FQN + if(typeName != null && typeName.Length >0) { + if (typeName.Equals(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter))) { + return MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter); + } else { + return typeName; + } + } + string result = FileRefType; + Type objectType = null; + // do we have a fileref? + if(result != null) { + // try to resolve this type + objectType = ResolveType(FileRefType, typeResolver); + } else if(nodeInfo != null) { + + // we dont have a fileref, try to resolve the type of the datanode + result = nodeInfo.TypeName; + // if typename is null, the default is just a string + if(result == null || result.Length==0) { + // we still dont know... do we have a mimetype? if yes, our only option is to + // deserialize to know what we're dealing with... very inefficient... + if(nodeInfo.MimeType != null && nodeInfo.MimeType.Length > 0) { + object insideObject = null; + + try { + insideObject = GenerateObjectFromDataNodeInfo(nodeInfo, typeResolver); + } catch (Exception ex) { // it'd be better to catch SerializationException but the underlying type resolver + // can throw things like FileNotFoundException which is kinda confusing, so I am catching all here.. + if(ClientUtils.IsCriticalException(ex)) { + throw; + } + // something went wrong, type is not specified at all or stream is corrupted + // return system.object + result = MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter); + } + + if(insideObject != null) { + result = MultitargetUtil.GetAssemblyQualifiedName(insideObject.GetType(), this.typeNameConverter); + } + } else { + // no typename, no mimetype, we have a string... + result = MultitargetUtil.GetAssemblyQualifiedName(typeof(string), this.typeNameConverter); + } + } else { + objectType = ResolveType(nodeInfo.TypeName, typeResolver); + } + } + if(objectType != null) { + if(objectType == typeof(ResXNullRef)) { + result = MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter); + } else { + result = MultitargetUtil.GetAssemblyQualifiedName(objectType, this.typeNameConverter); + } + } + return result; + } + + /// + /// + /// Get the FQ type name for this datanode + /// + public string GetValueTypeName(AssemblyName[] names) { + return GetValueTypeName(new AssemblyNamesTypeResolutionService(names)); + } + + /// + /// + /// Get the value contained in this datanode + /// + public object GetValue(ITypeResolutionService typeResolver) { + + if(value != null) { + return value; + } + + object result = null; + if(FileRefFullPath != null) { + Type objectType = ResolveType(FileRefType , typeResolver); + if(objectType != null) { + // we have the FQN for this type + if(FileRefTextEncoding != null) { + fileRef = new ResXFileRef(FileRefFullPath, FileRefType, Encoding.GetEncoding(FileRefTextEncoding)); + } else { + fileRef = new ResXFileRef(FileRefFullPath, FileRefType); + } + TypeConverter tc = TypeDescriptor.GetConverter(typeof(ResXFileRef)); + result = tc.ConvertFrom(fileRef.ToString()); + } else { + string newMessage = SR.GetString(SR.TypeLoadExceptionShort, FileRefType); + TypeLoadException newTle = new TypeLoadException(newMessage); + throw (newTle); + } + } else if(result == null && nodeInfo.ValueData!= null) { + // it's embedded, we deserialize it + result = GenerateObjectFromDataNodeInfo(nodeInfo, typeResolver); + } else { + // schema is wrong and say minOccur for Value is 0, + // but it's too late to change it... + // we need to return null here vswhidbey 180605 + return null; + } + return result; + } + + /// + /// + /// Get the value contained in this datanode + /// + public object GetValue(AssemblyName[] names) { + return GetValue(new AssemblyNamesTypeResolutionService(names)); + } + + private static byte[] FromBase64WrappedString(string text) { + + if (text.IndexOfAny(SpecialChars) != -1) { + StringBuilder sb = new StringBuilder(text.Length); + for (int i=0; i= 2) { + string partialName = typeParts[0].Trim(); + string assemblyName = typeParts[1].Trim(); + partialName = partialName + ", " + assemblyName; + t = typeResolver.GetType(partialName, false); + } + } + } + + if (t == null) { + t = Type.GetType(typeName, false); + } + + return t; + } + + + /// + /// + /// Get the value contained in this datanode + /// + // NOTE: No LinkDemand for SerializationFormatter necessary here, since this class already + // has a FullTrust LinkDemand. + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + DataNodeInfo nodeInfo = GetDataNodeInfo(); + si.AddValue("Name", nodeInfo.Name, typeof(string)); + si.AddValue("Comment", nodeInfo.Comment, typeof(string)); + si.AddValue("TypeName", nodeInfo.TypeName, typeof(string)); + si.AddValue("MimeType", nodeInfo.MimeType, typeof(string)); + si.AddValue("ValueData", nodeInfo.ValueData, typeof(string)); + } + + private ResXDataNode(SerializationInfo info, StreamingContext context) { + DataNodeInfo nodeInfo = new DataNodeInfo(); + nodeInfo.Name = (string)info.GetValue("Name", typeof(string)); + nodeInfo.Comment = (string)info.GetValue("Comment", typeof(string)); + nodeInfo.TypeName = (string)info.GetValue("TypeName", typeof(string)); + nodeInfo.MimeType = (string)info.GetValue("MimeType", typeof(string)); + nodeInfo.ValueData = (string)info.GetValue("ValueData", typeof(string)); + this.nodeInfo = nodeInfo; + InitializeDataNode(null); + } + } + + internal class DataNodeInfo { + internal string Name; + internal string Comment; + internal string TypeName; + internal string MimeType; + internal string ValueData; + internal Point ReaderPosition; //only used to track position in the reader + + internal DataNodeInfo Clone() { + DataNodeInfo result = new DataNodeInfo(); + result.Name = this.Name; + result.Comment = this.Comment; + result.TypeName = this.TypeName; + result.MimeType = this.MimeType; + result.ValueData = this.ValueData; + result.ReaderPosition = new Point(this.ReaderPosition.X, this.ReaderPosition.Y); + return result; + } + } + + // This class implements a partial type resolver for the BinaryFormatter. + // This is needed to be able to read binary serialized content from older + // NDP types and map them to newer versions. + // + internal class ResXSerializationBinder : SerializationBinder { + private ITypeResolutionService typeResolver; + private Func typeNameConverter; + + internal ResXSerializationBinder(ITypeResolutionService typeResolver) { + this.typeResolver = typeResolver; + } + + internal ResXSerializationBinder(Func typeNameConverter) { + this.typeNameConverter = typeNameConverter; + } + + public override Type BindToType(string assemblyName, string typeName) { + if (typeResolver == null) { + return null; + } + + typeName = typeName + ", " + assemblyName; + + Type t = typeResolver.GetType(typeName); + if (t == null) { + string[] typeParts = typeName.Split(new char[] {','}); + + // Break up the assembly name from the rest of the assembly strong name. + // we try 1) FQN 2) FQN without a version 3) just the short name + if (typeParts != null && typeParts.Length > 2) { + string partialName = typeParts[0].Trim(); + + for (int i = 1; i < typeParts.Length; ++i) { + string s = typeParts[i].Trim(); + if (!s.StartsWith("Version=") && !s.StartsWith("version=")) { + partialName = partialName + ", " + s; + } + } + t = typeResolver.GetType(partialName); + if(t == null) { + t = typeResolver.GetType(typeParts[0].Trim()); + } + } + } + + // Binder couldn't handle it, let the default loader take over. + return t; + } + + // + // Get the multitarget-aware string representation for the give type. + public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { + // Normally we don't change typeName when changing the target framework, + // only assembly version or assembly name might change, thus we are setting + // typeName only if it changed with the framework version. + // If binder passes in a null, BinaryFormatter will use the original value or + // for un-serializable types will redirect to another type. + // For example: + // + // Encoding = Encoding.GetEncoding("shift_jis"); + // public Encoding Encoding { get; set; } + // property type (Encoding) is abstract, but the value is instantiated to a specific class, + // and should be serialized as a specific class in order to be able to instantiate the result. + // + // another example are singleton objects like DBNull.Value which are serialized by System.UnitySerializationHolder + typeName = null; + if (typeNameConverter != null) { + string assemblyQualifiedTypeName = MultitargetUtil.GetAssemblyQualifiedName(serializedType, typeNameConverter); + if (!string.IsNullOrEmpty(assemblyQualifiedTypeName)) { + int pos = assemblyQualifiedTypeName.IndexOf(','); + if (pos > 0 && pos < assemblyQualifiedTypeName.Length - 1) { + assemblyName = assemblyQualifiedTypeName.Substring(pos + 1).TrimStart(); + string newTypeName = assemblyQualifiedTypeName.Substring(0, pos); + if (!string.Equals(newTypeName, serializedType.FullName, StringComparison.InvariantCulture)) { + typeName = newTypeName; + } + return; + } + } + } + + base.BindToName(serializedType, out assemblyName, out typeName); + } + } + + + internal class AssemblyNamesTypeResolutionService : ITypeResolutionService { + private AssemblyName[] names; + private Hashtable cachedAssemblies; + private Hashtable cachedTypes; + + private static string NetFrameworkPath = Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"), "Microsoft.Net\\Framework"); + + internal AssemblyNamesTypeResolutionService(AssemblyName[] names) { + this.names = names; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public Assembly GetAssembly(AssemblyName name) { + return GetAssembly(name, true); + } + + // + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public Assembly GetAssembly(AssemblyName name, bool throwOnError) { + + Assembly result = null; + + if (cachedAssemblies == null) { + cachedAssemblies = Hashtable.Synchronized(new Hashtable()); + } + + if (cachedAssemblies.Contains(name)) { + result = cachedAssemblies[name] as Assembly; + } + + if (result == null) { + // try to load it first from the gac +#pragma warning disable 0618 + //Although LoadWithPartialName is obsolete, we still have to call it: changing + //this would be breaking in cases where people edited their resource files by + //hand. + result = Assembly.LoadWithPartialName(name.FullName); +#pragma warning restore 0618 + if(result != null) { + cachedAssemblies[name] = result; + } + else if (names != null) { + for(int i=0;i 0 && pos < name.Length - 1) { + string fullName = name.Substring(pos + 1).Trim(); + AssemblyName assemblyName = null; + try { + assemblyName = new AssemblyName(fullName); + } + catch { + } + + if (assemblyName != null) { + List assemblyList = new List(names.Length); + for (int i = 0; i < names.Length; i++) { + if (string.Compare(assemblyName.Name, names[i].Name, StringComparison.OrdinalIgnoreCase) == 0) { + assemblyList.Insert(0, names[i]); + } + else { + assemblyList.Add(names[i]); + } + } + names = assemblyList.ToArray(); + } + } + + // Search each reference assembly + for(int i = 0; i < names.Length; i++) { + Assembly a = GetAssembly(names[i], false); + if (a != null) { + result = a.GetType(name, false, ignoreCase); + if(result == null) { + int indexOfComma = name.IndexOf(","); + if(indexOfComma != -1) { + string shortName = name.Substring(0, indexOfComma ); + result = a.GetType(shortName, false, ignoreCase); + } + } + } + + if(result != null) + break; + } + } + + if(result == null && throwOnError) { + throw new ArgumentException(SR.GetString(SR.InvalidResXNoType, name)); + } + + if(result != null) { + // Only cache types from .Net framework or GAC because they don't need to update. + // For simplicity, don't cache custom types + if (result.Assembly.GlobalAssemblyCache || IsNetFrameworkAssembly(result.Assembly.Location)) { + cachedTypes[name] = result; + } + } + + return result; + } + + /// + /// This is matching %windir%\Microsoft.NET\Framework*, so both 32bit and 64bit framework will be covered. + /// + private bool IsNetFrameworkAssembly(string assemblyPath) + { + return assemblyPath != null && assemblyPath.StartsWith(NetFrameworkPath, StringComparison.OrdinalIgnoreCase); + } + + public void ReferenceAssembly(AssemblyName name) { + throw new NotSupportedException(); + } + + } +} diff --git a/WindowsForms/Managed/System/Resources/ResXFileRef.cs b/WindowsForms/Managed/System/Resources/ResXFileRef.cs new file mode 100644 index 000000000..0beae38e9 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXFileRef.cs @@ -0,0 +1,337 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Windows.Forms; + using System.Reflection; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.ComponentModel; + using System.Collections; + using System.Resources; + using System.Text; + using System.Globalization; + using System.Runtime.Serialization; + using System.Runtime.Versioning; + + /// + /// + /// ResX File Reference class. This allows the developer to represent + /// a link to an external resource. When the resource manager asks + /// for the value of the resource item, the external resource is loaded. + /// + [TypeConverterAttribute(typeof(ResXFileRef.Converter)), Serializable] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] +#if SYSTEM_WEB + internal class ResXFileRef { +#else + public class ResXFileRef { +#endif + private string fileName; + private string typeName; + [OptionalField(VersionAdded = 2)] + private Encoding textFileEncoding; + + /// + /// + /// Creates a new ResXFileRef that points to the specified file. + /// The type refered to by typeName must support a constructor + /// that accepts a System.IO.Stream as a parameter. + /// + public ResXFileRef(string fileName, string typeName) { + if(fileName == null) { + throw (new ArgumentNullException("fileName")); + } + if(typeName == null) { + throw (new ArgumentNullException("typeName")); + } + this.fileName = fileName; + this.typeName = typeName; + } + + + [OnDeserializing] + private void OnDeserializing(StreamingContext ctx) { + textFileEncoding = null; + } + + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMethodsAsStatic")] + [OnDeserialized] + private void OnDeserialized(StreamingContext ctx) { + } + + /// + /// + /// Creates a new ResXFileRef that points to the specified file. + /// The type refered to by typeName must support a constructor + /// that accepts a System.IO.Stream as a parameter. + /// + public ResXFileRef(string fileName, string typeName, Encoding textFileEncoding) : this(fileName, typeName) { + this.textFileEncoding = textFileEncoding; + } + + internal ResXFileRef Clone() { + return new ResXFileRef(fileName, typeName, textFileEncoding); + } + + + /// + /// + /// [To be supplied.] + /// + public string FileName { + get { + return fileName; + } + } + + /// + /// + /// [To be supplied.] + /// + public string TypeName { + get { + return typeName; + } + } + + /// + /// + /// [To be supplied.] + /// + public Encoding TextFileEncoding { + get { + return textFileEncoding; + } + } + + + /// + /// + /// path1+result = path2 + /// A string which is the relative path difference between path1 and + /// path2 such that if path1 and the calculated difference are used + /// as arguments to Combine(), path2 is returned + /// + private static string PathDifference(string path1, string path2, bool compareCase) { + int i; + int si = -1; + + for (i = 0; (i < path1.Length) && (i < path2.Length); ++i) { + if ((path1[i] != path2[i]) && (compareCase || (Char.ToLower(path1[i], CultureInfo.InvariantCulture) != Char.ToLower(path2[i], CultureInfo.InvariantCulture)))) + { + break; + + } else if (path1[i] == Path.DirectorySeparatorChar) { + si = i; + } + } + + if (i == 0) { + return path2; + } + if ((i == path1.Length) && (i == path2.Length)) { + return String.Empty; + } + + StringBuilder relPath = new StringBuilder(); + + for (; i < path1.Length; ++i) { + if (path1[i] == Path.DirectorySeparatorChar) { + relPath.Append(".."+Path.DirectorySeparatorChar); + } + } + return relPath.ToString() + path2.Substring(si + 1); + } + + + internal void MakeFilePathRelative(string basePath) { + + if(basePath==null || basePath.Length == 0) { + return; + } + fileName = PathDifference(basePath, fileName, false); + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + string result = ""; + + if(fileName.IndexOf(";") != -1 || fileName.IndexOf("\"") != -1) { + result += ("\""+ fileName + "\";"); + } else { + result += (fileName + ";"); + } + result += typeName; + if(textFileEncoding != null) { + result += (";" + textFileEncoding.WebName); + } + return result; + } + + + + + /// + /// + /// [To be supplied.] + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class Converter : TypeConverter { + /// + /// + /// [To be supplied.] + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, + Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return false; + } + + /// + /// + /// [To be supplied.] + /// + public override bool CanConvertTo(ITypeDescriptorContext context, + Type destinationType) { + if (destinationType == typeof(string)) { + return true; + } + return false; + } + + /// + /// + /// [To be supplied.] + /// + public override Object ConvertTo(ITypeDescriptorContext context, + CultureInfo culture, + Object value, + Type destinationType) { + Object created = null; + if (destinationType == typeof(string)) { + created = ((ResXFileRef)value).ToString(); + } + return created; + } + + // "value" is the parameter name of ConvertFrom, which calls this method. + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + internal static string[] ParseResxFileRefString(string stringValue) { + string[] result = null; + if(stringValue != null ) { + stringValue = stringValue.Trim(); + string fileName; + string remainingString; + if(stringValue.StartsWith("\"")) { + int lastIndexOfQuote = stringValue.LastIndexOf("\""); + if (lastIndexOfQuote-1<0) + throw new ArgumentException("value"); + fileName = stringValue.Substring(1, lastIndexOfQuote-1); // remove the quotes in" ..... " + if(lastIndexOfQuote+2>stringValue.Length) + throw new ArgumentException("value"); + remainingString = stringValue.Substring(lastIndexOfQuote+2); + } else { + int nextSemiColumn = stringValue.IndexOf(";"); + if(nextSemiColumn == -1) + throw new ArgumentException("value"); + fileName = stringValue.Substring(0,nextSemiColumn); + if(nextSemiColumn+1>stringValue.Length) + throw new ArgumentException("value"); + remainingString = stringValue.Substring(nextSemiColumn+1); + } + string[] parts = remainingString.Split(new char[] {';'}); + if(parts.Length > 1) { + result = new string[] { fileName, parts[0], parts[1] }; + } else if(parts.Length > 0) { + result = new string[] { fileName, parts[0] }; + } else { + result = new string[] { fileName }; + } + } + return result; + } + + /// + /// + /// [To be supplied.] + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public override Object ConvertFrom(ITypeDescriptorContext context, + CultureInfo culture, + Object value) { + Object created = null; + string stringValue = value as string; + if (stringValue != null) { + string[] parts = ResXFileRef.Converter.ParseResxFileRefString(stringValue); + string fileName = parts[0]; + Type toCreate = Type.GetType(parts[1], true); + + // special case string and byte[] + if(toCreate.Equals(typeof(string))) { + // we have a string, now we need to check the encoding + Encoding textFileEncoding = Encoding.Default; + if(parts.Length > 2) { + textFileEncoding = Encoding.GetEncoding(parts[2]); + } + using (StreamReader sr = new StreamReader(fileName, textFileEncoding)) { + created = sr.ReadToEnd(); + } + } else { + + // this is a regular file, we call it's constructor with a stream as a parameter + // or if it's a byte array we just return that + byte[] temp = null; + + using (FileStream s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { + Debug.Assert(s != null, "Couldn't open " + fileName); + temp = new byte[s.Length]; + s.Read(temp, 0, (int)s.Length); + } + + if(toCreate.Equals(typeof(byte[]))) { + created = temp; + } else { + MemoryStream memStream = new MemoryStream(temp); + if(toCreate.Equals(typeof(MemoryStream))) { + return memStream; + } else if(toCreate.Equals(typeof(System.Drawing.Bitmap)) && fileName.EndsWith(".ico")) { + // we special case the .ico bitmaps because GDI+ destroy the alpha channel component and + // we don't want that to happen + Icon ico = new Icon(memStream); + created = ico.ToBitmap(); + } else { + created = Activator.CreateInstance(toCreate, BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null, new Object[] {memStream}, null); + } + } + } + } + return created; + } + + } + } +} + + diff --git a/WindowsForms/Managed/System/Resources/ResXNullRef.cs b/WindowsForms/Managed/System/Resources/ResXNullRef.cs new file mode 100644 index 000000000..ea2062409 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXNullRef.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + + using System; + using System.Windows.Forms; + using System.Reflection; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.ComponentModel; + using System.Collections; + using System.Resources; + using System.Globalization; + + /// + /// + /// ResX Null Reference class. This class allows ResX to store null values. + /// It is a placeholder that is written into the file. On read, it is replaced + /// with null. + /// + [Serializable] + internal sealed class ResXNullRef { + } +} + diff --git a/WindowsForms/Managed/System/Resources/ResXResourceReader.cs b/WindowsForms/Managed/System/Resources/ResXResourceReader.cs new file mode 100644 index 000000000..ce489a437 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXResourceReader.cs @@ -0,0 +1,700 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + using System.Runtime.Serialization; + using System; + using System.Windows.Forms; + using System.Reflection; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.Text; + using System.ComponentModel; + using System.Collections; + using System.Collections.Specialized; + using System.Runtime.CompilerServices; + using System.Resources; + using System.Xml; + using System.ComponentModel.Design; + using System.Globalization; +#if SYSTEM_WEB + using System.Web; // This is needed to access the SR resource strings +#endif + + /// + /// + /// ResX resource reader. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] +#if SYSTEM_WEB + internal class ResXResourceReader : IResourceReader { +#else + public class ResXResourceReader : IResourceReader { +#endif + + //static readonly char[] SpecialChars = new char[]{' ', '\r', '\n'}; + + //IFormatter binaryFormatter = null; + string fileName = null; + TextReader reader = null; + Stream stream = null; + string fileContents = null; + AssemblyName[] assemblyNames; + string basePath; + bool isReaderDirty = false; + + ITypeResolutionService typeResolver; + IAliasResolver aliasResolver =null; + + ListDictionary resData = null; + ListDictionary resMetadata = null; + string resHeaderVersion = null; + string resHeaderMimeType = null; + string resHeaderReaderType = null; + string resHeaderWriterType = null; + bool useResXDataNodes = false; + + + private ResXResourceReader(ITypeResolutionService typeResolver) { + this.typeResolver = typeResolver; + this.aliasResolver = new ReaderAliasResolver(); + } + + private ResXResourceReader(AssemblyName[] assemblyNames) { + this.assemblyNames = assemblyNames; + this.aliasResolver = new ReaderAliasResolver(); + } + + + public ResXResourceReader(string fileName) : this(fileName, (ITypeResolutionService)null, (IAliasResolver)null) { + } + public ResXResourceReader(string fileName, ITypeResolutionService typeResolver) : this(fileName, typeResolver, (IAliasResolver)null) { + } + internal ResXResourceReader(string fileName, ITypeResolutionService typeResolver, IAliasResolver aliasResolver) { + this.fileName = fileName; + this.typeResolver = typeResolver; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + public ResXResourceReader(TextReader reader) : this(reader, (ITypeResolutionService)null, (IAliasResolver)null) { + } + public ResXResourceReader(TextReader reader, ITypeResolutionService typeResolver) : this(reader, typeResolver, (IAliasResolver)null) { + } + internal ResXResourceReader(TextReader reader, ITypeResolutionService typeResolver, IAliasResolver aliasResolver) { + this.reader = reader; + this.typeResolver = typeResolver; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + + public ResXResourceReader(Stream stream) : this(stream, (ITypeResolutionService)null, (IAliasResolver)null) { + } + public ResXResourceReader(Stream stream, ITypeResolutionService typeResolver) : this(stream, typeResolver, (IAliasResolver)null) { + } + internal ResXResourceReader(Stream stream, ITypeResolutionService typeResolver, IAliasResolver aliasResolver) { + this.stream = stream; + this.typeResolver = typeResolver; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + public ResXResourceReader(Stream stream, AssemblyName[] assemblyNames) : this(stream, assemblyNames, (IAliasResolver)null){ + } + internal ResXResourceReader(Stream stream, AssemblyName[] assemblyNames, IAliasResolver aliasResolver) { + this.stream = stream; + this.assemblyNames = assemblyNames; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + public ResXResourceReader(TextReader reader, AssemblyName[] assemblyNames) : this(reader, assemblyNames, (IAliasResolver)null){ + } + internal ResXResourceReader(TextReader reader, AssemblyName[] assemblyNames, IAliasResolver aliasResolver) { + this.reader = reader; + this.assemblyNames = assemblyNames; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + public ResXResourceReader(string fileName, AssemblyName[] assemblyNames) : this(fileName, assemblyNames, (IAliasResolver)null){ + } + internal ResXResourceReader(string fileName, AssemblyName[] assemblyNames, IAliasResolver aliasResolver) { + this.fileName = fileName; + this.assemblyNames = assemblyNames; + this.aliasResolver = aliasResolver; + if(this.aliasResolver == null) { + this.aliasResolver = new ReaderAliasResolver(); + } + } + + + + /// + ~ResXResourceReader() { + Dispose(false); + } + + /// + /// + /// BasePath for relatives filepaths with ResXFileRefs. + /// + public string BasePath { + get { + return basePath; + } + set { + if(isReaderDirty) { + throw new InvalidOperationException(SR.GetString(SR.InvalidResXBasePathOperation)); + } + basePath = value; + } + } + +#if UNUSED + /// + /// Retrieves the resource data set. This will demand load it. + /// + private ListDictionary ResData { + get { + EnsureResData(); + return resData; + } + } + + /// + /// Returns the typeResolver used to find types defined in the ResX contents. + /// + private ITypeResolutionService TypeResolver { + get { + return this.typeResolver; + } + } +#endif + + /// + /// + /// ResXFileRef's TypeConverter automatically unwraps it, creates the referenced + /// object and returns it. This property gives the user control over whether this unwrapping should + /// happen, or a ResXFileRef object should be returned. Default is true for backward compat and common case + /// scenario. + /// + public bool UseResXDataNodes { + get { + return this.useResXDataNodes; + } + set { + if(isReaderDirty) { + throw new InvalidOperationException(SR.GetString(SR.InvalidResXBasePathOperation)); + } + this.useResXDataNodes = value; + } + } + + /// + /// + /// Closes and files or streams being used by the reader. + /// + // NOTE: Part of IResourceReader - not protected by class level LinkDemand. + public void Close() { + ((IDisposable)this).Dispose(); + } + + /// + /// + // NOTE: Part of IDisposable - not protected by class level LinkDemand. + void IDisposable.Dispose() { + GC.SuppressFinalize(this); + Dispose(true); + } + + /// + protected virtual void Dispose(bool disposing) { + if (disposing) { + if (fileName != null && stream != null) { + stream.Close(); + stream = null; + } + + if (reader != null) { + reader.Close(); + reader = null; + } + } + } + + private void SetupNameTable(XmlReader reader) { + reader.NameTable.Add(ResXResourceWriter.TypeStr); + reader.NameTable.Add(ResXResourceWriter.NameStr); + reader.NameTable.Add(ResXResourceWriter.DataStr); + reader.NameTable.Add(ResXResourceWriter.MetadataStr); + reader.NameTable.Add(ResXResourceWriter.MimeTypeStr); + reader.NameTable.Add(ResXResourceWriter.ValueStr); + reader.NameTable.Add(ResXResourceWriter.ResHeaderStr); + reader.NameTable.Add(ResXResourceWriter.VersionStr); + reader.NameTable.Add(ResXResourceWriter.ResMimeTypeStr); + reader.NameTable.Add(ResXResourceWriter.ReaderStr); + reader.NameTable.Add(ResXResourceWriter.WriterStr); + reader.NameTable.Add(ResXResourceWriter.BinSerializedObjectMimeType); + reader.NameTable.Add(ResXResourceWriter.SoapSerializedObjectMimeType); + reader.NameTable.Add(ResXResourceWriter.AssemblyStr); + reader.NameTable.Add(ResXResourceWriter.AliasStr); + } + + /// + /// Demand loads the resource data. + /// + private void EnsureResData() { + if (resData == null) { + resData = new ListDictionary(); + resMetadata = new ListDictionary(); + + XmlTextReader contentReader = null; + + try { + // Read data in any which way + if (fileContents != null) { + contentReader = new XmlTextReader(new StringReader(fileContents)); + } + else if (reader != null) { + contentReader = new XmlTextReader(reader); + } + else if (fileName != null || stream != null) { + if (stream == null) { + stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + } + + contentReader = new XmlTextReader(stream); + } + + SetupNameTable(contentReader); + contentReader.WhitespaceHandling = WhitespaceHandling.None; + ParseXml(contentReader); + } + finally { + if (fileName != null && stream != null) { + stream.Close(); + stream = null; + } + } + } + } + + + + /// + /// + /// Creates a reader with the specified file contents. + /// + public static ResXResourceReader FromFileContents(string fileContents) { + return FromFileContents(fileContents, (ITypeResolutionService)null); + } + + /// + /// + /// + /// Creates a reader with the specified file contents. + /// + public static ResXResourceReader FromFileContents(string fileContents, ITypeResolutionService typeResolver) { + ResXResourceReader result = new ResXResourceReader(typeResolver); + result.fileContents = fileContents; + return result; + } + + /// + /// + /// + /// Creates a reader with the specified file contents. + /// + public static ResXResourceReader FromFileContents(string fileContents, AssemblyName[] assemblyNames) { + ResXResourceReader result = new ResXResourceReader(assemblyNames); + result.fileContents = fileContents; + return result; + } + + /// + /// + // NOTE: Part of IEnumerable - not protected by class level LinkDemand. + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + // NOTE: Part of IResourceReader - not protected by class level LinkDemand. + public IDictionaryEnumerator GetEnumerator() { + isReaderDirty = true; + EnsureResData(); + return resData.GetEnumerator(); + } + + /// + /// + /// Returns a dictionary enumerator that can be used to enumerate the elements in the .resx file. + /// + public IDictionaryEnumerator GetMetadataEnumerator() { + EnsureResData(); + return resMetadata.GetEnumerator(); + } + + /// + /// Attempts to return the line and column (Y, X) of the XML reader. + /// + private Point GetPosition(XmlReader reader) { + Point pt = new Point(0, 0); + IXmlLineInfo lineInfo = reader as IXmlLineInfo; + + if (lineInfo != null) { + pt.Y = lineInfo.LineNumber; + pt.X = lineInfo.LinePosition; + } + + return pt; + } + + + private void ParseXml(XmlTextReader reader) { + bool success = false; + try { + try { + while (reader.Read()) { + if (reader.NodeType == XmlNodeType.Element) { + string s = reader.LocalName; + + if (reader.LocalName.Equals(ResXResourceWriter.AssemblyStr)) { + ParseAssemblyNode(reader, false); + } + else if (reader.LocalName.Equals(ResXResourceWriter.DataStr)) { + ParseDataNode(reader, false); + } + else if (reader.LocalName.Equals(ResXResourceWriter.ResHeaderStr)) { + ParseResHeaderNode(reader); + } + else if (reader.LocalName.Equals(ResXResourceWriter.MetadataStr)) { + ParseDataNode(reader, true); + } + } + } + + success = true; + } + catch (SerializationException se) { + Point pt = GetPosition(reader); + string newMessage = SR.GetString(SR.SerializationException, reader[ResXResourceWriter.TypeStr], pt.Y, pt.X, se.Message); + XmlException xml = new XmlException(newMessage, se, pt.Y, pt.X); + SerializationException newSe = new SerializationException(newMessage, xml); + + throw newSe; + } + catch (TargetInvocationException tie) { + Point pt = GetPosition(reader); + string newMessage = SR.GetString(SR.InvocationException, reader[ResXResourceWriter.TypeStr], pt.Y, pt.X, tie.InnerException.Message); + XmlException xml = new XmlException(newMessage, tie.InnerException, pt.Y, pt.X); + TargetInvocationException newTie = new TargetInvocationException(newMessage, xml); + + throw newTie; + } + catch (XmlException e) { + throw new ArgumentException(SR.GetString(SR.InvalidResXFile, e.Message), e); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } else { + Point pt = GetPosition(reader); + XmlException xmlEx = new XmlException(e.Message, e, pt.Y, pt.X); + throw new ArgumentException(SR.GetString(SR.InvalidResXFile, xmlEx.Message), xmlEx); + } + } + } + finally { + if (!success) { + resData = null; + resMetadata = null; + } + } + + bool validFile = false; + + if (object.Equals(resHeaderMimeType, ResXResourceWriter.ResMimeType)) { + + Type readerType = typeof(ResXResourceReader); + Type writerType = typeof(ResXResourceWriter); + + string readerTypeName = resHeaderReaderType; + string writerTypeName = resHeaderWriterType; + if (readerTypeName != null &&readerTypeName.IndexOf(',') != -1) { + readerTypeName = readerTypeName.Split(new char[] {','})[0].Trim(); + } + if (writerTypeName != null && writerTypeName.IndexOf(',') != -1) { + writerTypeName = writerTypeName.Split(new char[] {','})[0].Trim(); + } + +// Don't check validity, since our reader/writer classes are in System.Web.Compilation, +// while the file format has them in System.Resources. +#if SYSTEM_WEB + validFile = true; +#else + if (readerTypeName != null && + writerTypeName != null && + readerTypeName.Equals(readerType.FullName) && + writerTypeName.Equals(writerType.FullName)) { + validFile = true; + } +#endif + } + + if (!validFile) { + resData = null; + resMetadata = null; + throw new ArgumentException(SR.GetString(SR.InvalidResXFileReaderWriterTypes)); + } + } + + private void ParseResHeaderNode(XmlReader reader) { + string name = reader[ResXResourceWriter.NameStr]; + if (name != null) { + reader.ReadStartElement(); + + // The "1.1" schema requires the correct casing of the strings + // in the resheader, however the "1.0" schema had a different + // casing. By checking the Equals first, we should + // see significant performance improvements. + // + + if (object.Equals(name, ResXResourceWriter.VersionStr)) { + if (reader.NodeType == XmlNodeType.Element) { + resHeaderVersion = reader.ReadElementString(); + } + else { + resHeaderVersion = reader.Value.Trim(); + } + } + else if (object.Equals(name, ResXResourceWriter.ResMimeTypeStr)) { + if (reader.NodeType == XmlNodeType.Element) { + resHeaderMimeType = reader.ReadElementString(); + } + else { + resHeaderMimeType = reader.Value.Trim(); + } + } + else if (object.Equals(name, ResXResourceWriter.ReaderStr)) { + if (reader.NodeType == XmlNodeType.Element) { + resHeaderReaderType = reader.ReadElementString(); + } + else { + resHeaderReaderType = reader.Value.Trim(); + } + } + else if (object.Equals(name, ResXResourceWriter.WriterStr)) { + if (reader.NodeType == XmlNodeType.Element) { + resHeaderWriterType = reader.ReadElementString(); + } + else { + resHeaderWriterType = reader.Value.Trim(); + } + } + else { + switch (name.ToLower(CultureInfo.InvariantCulture)) { + case ResXResourceWriter.VersionStr: + if (reader.NodeType == XmlNodeType.Element) { + resHeaderVersion = reader.ReadElementString(); + } + else { + resHeaderVersion = reader.Value.Trim(); + } + break; + case ResXResourceWriter.ResMimeTypeStr: + if (reader.NodeType == XmlNodeType.Element) { + resHeaderMimeType = reader.ReadElementString(); + } + else { + resHeaderMimeType = reader.Value.Trim(); + } + break; + case ResXResourceWriter.ReaderStr: + if (reader.NodeType == XmlNodeType.Element) { + resHeaderReaderType = reader.ReadElementString(); + } + else { + resHeaderReaderType = reader.Value.Trim(); + } + break; + case ResXResourceWriter.WriterStr: + if (reader.NodeType == XmlNodeType.Element) { + resHeaderWriterType = reader.ReadElementString(); + } + else { + resHeaderWriterType = reader.Value.Trim(); + } + break; + } + } + } + } + +#if UNUSED + private string GetSimpleName(string typeName) { + int indexStart = typeName.IndexOf(","); + return typeName.Substring(0, indexStart); + } +#endif + + private void ParseAssemblyNode(XmlReader reader, bool isMetaData) + { + string alias = reader[ResXResourceWriter.AliasStr]; + string typeName = reader[ResXResourceWriter.NameStr]; + + AssemblyName assemblyName = new AssemblyName(typeName); + + if (string.IsNullOrEmpty(alias)) { + alias = assemblyName.Name; + } + aliasResolver.PushAlias(alias, assemblyName); + } + + + private void ParseDataNode(XmlTextReader reader, bool isMetaData) { + DataNodeInfo nodeInfo = new DataNodeInfo(); + + nodeInfo.Name = reader[ResXResourceWriter.NameStr]; + string typeName = reader[ResXResourceWriter.TypeStr]; + + string alias = null; + AssemblyName assemblyName = null; + + if (!string.IsNullOrEmpty(typeName)) { + alias = GetAliasFromTypeName(typeName); + } + if (!string.IsNullOrEmpty(alias)) { + assemblyName = aliasResolver.ResolveAlias(alias); + } + if (assemblyName != null ) + { + nodeInfo.TypeName = GetTypeFromTypeName(typeName) + ", " + assemblyName.FullName; + } + else { + nodeInfo.TypeName = reader[ResXResourceWriter.TypeStr]; + } + + nodeInfo.MimeType = reader[ResXResourceWriter.MimeTypeStr]; + + bool finishedReadingDataNode = false; + nodeInfo.ReaderPosition = GetPosition(reader); + while(!finishedReadingDataNode && reader.Read()) { + if(reader.NodeType == XmlNodeType.EndElement && ( reader.LocalName.Equals(ResXResourceWriter.DataStr) || reader.LocalName.Equals(ResXResourceWriter.MetadataStr) )) { + // we just found , quit or + finishedReadingDataNode = true; + } else { + // could be a or a + if (reader.NodeType == XmlNodeType.Element) { + if (reader.Name.Equals(ResXResourceWriter.ValueStr)) { + WhitespaceHandling oldValue = reader.WhitespaceHandling; + try { + // based on the documentation at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemxmlxmltextreaderclasswhitespacehandlingtopic.asp + // this is ok because: + // "Because the XmlTextReader does not have DTD information available to it, + // SignificantWhitepsace nodes are only returned within the an xml:space='preserve' scope." + // the xml:space would not be present for anything else than string and char (see ResXResourceWriter) + // so this would not cause any breaking change while reading data from Everett (we never outputed + // xml:space then) or from whidbey that is not specifically either a string or a char. + // However please note that manually editing a resx file in Everett and in Whidbey because of the addition + // of xml:space=preserve might have different consequences... + reader.WhitespaceHandling = WhitespaceHandling.Significant; + nodeInfo.ValueData = reader.ReadString(); + } finally { + reader.WhitespaceHandling = oldValue; + } + } else if (reader.Name.Equals(ResXResourceWriter.CommentStr)) { + nodeInfo.Comment = reader.ReadString(); + } + } else { + // weird, no tag, just the inside of as text + nodeInfo.ValueData = reader.Value.Trim(); + } + } + } + + if (nodeInfo.Name==null) { + throw new ArgumentException(SR.GetString(SR.InvalidResXResourceNoName, nodeInfo.ValueData)); + } + + ResXDataNode dataNode = new ResXDataNode(nodeInfo, BasePath); + + if(UseResXDataNodes) { + resData[nodeInfo.Name] = dataNode; + } else { + IDictionary data = (isMetaData ? resMetadata : resData); + if(assemblyNames == null) { + data[nodeInfo.Name] = dataNode.GetValue(typeResolver); + } else { + data[nodeInfo.Name] = dataNode.GetValue(assemblyNames); + } + } + } + + private string GetAliasFromTypeName(string typeName) { + + int indexStart = typeName.IndexOf(","); + return typeName.Substring(indexStart + 2); + + } + + private string GetTypeFromTypeName(string typeName) { + + int indexStart = typeName.IndexOf(","); + return typeName.Substring(0, indexStart); + + } + + + private sealed class ReaderAliasResolver : IAliasResolver { + private Hashtable cachedAliases; + + internal ReaderAliasResolver() { + this.cachedAliases = new Hashtable(); + } + + public AssemblyName ResolveAlias(string alias) { + + AssemblyName result = null; + if(cachedAliases != null) { + result = (AssemblyName)cachedAliases[alias]; + } + return result; + } + + public void PushAlias(string alias, AssemblyName name) { + if (this.cachedAliases != null && !string.IsNullOrEmpty(alias)) { + cachedAliases[alias] = name; + } + } + + } + } +} + diff --git a/WindowsForms/Managed/System/Resources/ResXResourceSet.cs b/WindowsForms/Managed/System/Resources/ResXResourceSet.cs new file mode 100644 index 000000000..c4f989fb5 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXResourceSet.cs @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if !SYSTEM_WEB + +namespace System.Resources { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Windows.Forms; + using System.Reflection; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.ComponentModel; + using System.Collections; + using System.Resources; + + /// + /// + /// ResX resource set. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class ResXResourceSet : ResourceSet { + + /// + /// + /// Creates a resource set for the specified file. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Shipped like this in Everett. + ] + public ResXResourceSet(String fileName) { + this.Reader = new ResXResourceReader(fileName); + this.Table = new Hashtable(); + ReadResources(); + } + + /// + /// + /// Creates a resource set for the specified stream. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Shipped like this in Everett. + ] + public ResXResourceSet(Stream stream) { + this.Reader = new ResXResourceReader(stream); + this.Table = new Hashtable(); + ReadResources(); + } + + /// + /// + /// Gets the default reader type associated with this set. + /// + public override Type GetDefaultReader() { + return typeof(ResXResourceReader); + } + + /// + /// + /// Gets the default writer type associated with this set. + /// + public override Type GetDefaultWriter() { + return typeof(ResXResourceWriter); + } + } +} + +#endif // !SYSTEM_WEB diff --git a/WindowsForms/Managed/System/Resources/ResXResourceWriter.cs b/WindowsForms/Managed/System/Resources/ResXResourceWriter.cs new file mode 100644 index 000000000..d06d33d13 --- /dev/null +++ b/WindowsForms/Managed/System/Resources/ResXResourceWriter.cs @@ -0,0 +1,776 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#if SYSTEM_WEB // See DevDiv 9030 +namespace System.PrivateResources { +#else +namespace System.Resources { +#endif + + using System.Diagnostics; + using System.Reflection; + using System; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Drawing; + using System.IO; + using System.Text; + using System.ComponentModel; + using System.Collections; + using System.Resources; + using System.Xml; + using System.Runtime.Serialization; + using System.Diagnostics.CodeAnalysis; +#if SYSTEM_WEB + using System.Web; // This is needed to access the SR resource strings +#endif + + /// + /// + /// ResX resource writer. See the text in "ResourceSchema" for more + /// information. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] +#if SYSTEM_WEB + internal class ResXResourceWriter : IResourceWriter { +#else + public class ResXResourceWriter : IResourceWriter { +#endif + internal const string TypeStr = "type"; + internal const string NameStr = "name"; + internal const string DataStr = "data"; + internal const string MetadataStr = "metadata"; + internal const string MimeTypeStr = "mimetype"; + internal const string ValueStr = "value"; + internal const string ResHeaderStr = "resheader"; + internal const string VersionStr = "version"; + internal const string ResMimeTypeStr = "resmimetype"; + internal const string ReaderStr = "reader"; + internal const string WriterStr = "writer"; + internal const string CommentStr = "comment"; + internal const string AssemblyStr ="assembly"; + internal const string AliasStr= "alias" ; + + private Hashtable cachedAliases; + + private static TraceSwitch ResValueProviderSwitch = new TraceSwitch("ResX", "Debug the resource value provider"); + + // + + + + + + + + internal static readonly string Beta2CompatSerializedObjectMimeType = "text/microsoft-urt/psuedoml-serialized/base64"; + + // These two "compat" mimetypes are here. In Beta 2 and RTM we used the term "URT" + // internally to refer to parts of the .NET Framework. Since these references + // will be in Beta 2 ResX files, and RTM ResX files for customers that had + // early access to releases, we don't want to break that. We will read + // and parse these types correctly in version 1.0, but will always + // write out the new version. So, opening and editing a ResX file in VS will + // update it to the new types. + // + internal static readonly string CompatBinSerializedObjectMimeType = "text/microsoft-urt/binary-serialized/base64"; + internal static readonly string CompatSoapSerializedObjectMimeType = "text/microsoft-urt/soap-serialized/base64"; + + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64"; + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64"; + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType; + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64"; + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string ResMimeType = "text/microsoft-resx"; + /// + /// + /// [To be supplied.] + /// + public static readonly string Version = "2.0"; + + /// + /// + /// + /// [To be supplied.] + /// + public static readonly string ResourceSchema = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; + + IFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter (); + string fileName; + Stream stream; + TextWriter textWriter; + XmlTextWriter xmlTextWriter; + string basePath; + + bool hasBeenSaved; + bool initialized; + + private Func typeNameConverter; // no public property to be consistent with ResXDataNode class. + + /// + /// + /// Base Path for ResXFileRefs. + /// + public string BasePath { + get { + return basePath; + } + set { + basePath = value; + } + } + + /// + /// + /// Creates a new ResXResourceWriter that will write to the specified file. + /// + public ResXResourceWriter(string fileName) { + this.fileName = fileName; + } + public ResXResourceWriter(string fileName, Func typeNameConverter) { + this.fileName = fileName; + this.typeNameConverter = typeNameConverter; + } + + /// + /// + /// Creates a new ResXResourceWriter that will write to the specified stream. + /// + public ResXResourceWriter(Stream stream) { + this.stream = stream; + } + public ResXResourceWriter(Stream stream, Func typeNameConverter) { + this.stream = stream; + this.typeNameConverter = typeNameConverter; + } + + /// + /// + /// Creates a new ResXResourceWriter that will write to the specified TextWriter. + /// + public ResXResourceWriter(TextWriter textWriter) { + this.textWriter = textWriter; + } + public ResXResourceWriter(TextWriter textWriter, Func typeNameConverter) { + this.textWriter = textWriter; + this.typeNameConverter = typeNameConverter; + } + + /// + ~ResXResourceWriter() { + Dispose(false); + } + + private void InitializeWriter() { + if (xmlTextWriter == null) { + // + + bool writeHeaderHack = false; + + if (textWriter != null) { + textWriter.WriteLine(""); + writeHeaderHack = true; + + xmlTextWriter = new XmlTextWriter(textWriter); + } + else if (stream != null) { + xmlTextWriter = new XmlTextWriter(stream, System.Text.Encoding.UTF8); + } + else { + Debug.Assert(fileName != null, "Nothing to output to"); + xmlTextWriter = new XmlTextWriter(fileName, System.Text.Encoding.UTF8); + } + xmlTextWriter.Formatting = Formatting.Indented; + xmlTextWriter.Indentation = 2; + + if (!writeHeaderHack) { + xmlTextWriter.WriteStartDocument(); // writes + } + } + else { + xmlTextWriter.WriteStartDocument(); + } + + xmlTextWriter.WriteStartElement("root"); + XmlTextReader reader = new XmlTextReader(new StringReader(ResourceSchema)); + reader.WhitespaceHandling = WhitespaceHandling.None; + xmlTextWriter.WriteNode(reader, true); + + xmlTextWriter.WriteStartElement(ResHeaderStr); { + xmlTextWriter.WriteAttributeString(NameStr, ResMimeTypeStr); + xmlTextWriter.WriteStartElement(ValueStr); { + xmlTextWriter.WriteString(ResMimeType); + } + xmlTextWriter.WriteEndElement(); + } + xmlTextWriter.WriteEndElement(); + xmlTextWriter.WriteStartElement(ResHeaderStr); { + xmlTextWriter.WriteAttributeString(NameStr, VersionStr); + xmlTextWriter.WriteStartElement(ValueStr); { + xmlTextWriter.WriteString(Version); + } + xmlTextWriter.WriteEndElement(); + } + xmlTextWriter.WriteEndElement(); + xmlTextWriter.WriteStartElement(ResHeaderStr); { + xmlTextWriter.WriteAttributeString(NameStr, ReaderStr); + xmlTextWriter.WriteStartElement(ValueStr); { + xmlTextWriter.WriteString(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXResourceReader), this.typeNameConverter)); + } + xmlTextWriter.WriteEndElement(); + } + xmlTextWriter.WriteEndElement(); + xmlTextWriter.WriteStartElement(ResHeaderStr); { + xmlTextWriter.WriteAttributeString(NameStr, WriterStr); + xmlTextWriter.WriteStartElement(ValueStr); { + xmlTextWriter.WriteString(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXResourceWriter), this.typeNameConverter)); + } + xmlTextWriter.WriteEndElement(); + } + xmlTextWriter.WriteEndElement(); + + initialized = true; + } + + private XmlWriter Writer { + get { + if (!initialized) { + InitializeWriter(); + } + return xmlTextWriter; + } + } + + /// + /// + /// Adds aliases to the resource file... + /// + public virtual void AddAlias(string aliasName, AssemblyName assemblyName) { + if (assemblyName == null) { + throw new ArgumentNullException("assemblyName"); + } + + if (cachedAliases == null) { + cachedAliases = new Hashtable(); + } + + cachedAliases[assemblyName.FullName] = aliasName; + } + + + /// + /// + /// Adds the given value to the collection of metadata. These name/value pairs + /// will be emitted to the elements in the .resx file. + /// + public void AddMetadata(string name, byte[] value) { + AddDataRow(MetadataStr, name, value); + } + + /// + /// + /// Adds the given value to the collection of metadata. These name/value pairs + /// will be emitted to the elements in the .resx file. + /// + public void AddMetadata(string name, string value) { + AddDataRow(MetadataStr, name, value); + } + + /// + /// + /// Adds the given value to the collection of metadata. These name/value pairs + /// will be emitted to the elements in the .resx file. + /// + public void AddMetadata(string name, object value) { + AddDataRow(MetadataStr, name, value); + } + + /// + /// + /// Adds a blob resource to the resources. + /// + // NOTE: Part of IResourceWriter - not protected by class level LinkDemand. + public void AddResource(string name, byte[] value) { + AddDataRow(DataStr, name, value); + } + + /// + /// + /// Adds a resource to the resources. If the resource is a string, + /// it will be saved that way, otherwise it will be serialized + /// and stored as in binary. + /// + // NOTE: Part of IResourceWriter - not protected by class level LinkDemand. + public void AddResource(string name, object value) { + if (value is ResXDataNode) { + AddResource((ResXDataNode)value); + } + else { + AddDataRow(DataStr, name, value); + } + } + + /// + /// + /// Adds a string resource to the resources. + /// + // NOTE: Part of IResourceWriter - not protected by class level LinkDemand. + public void AddResource(string name, string value) { + AddDataRow(DataStr, name, value); + } + + /// + /// + /// Adds a string resource to the resources. + /// + public void AddResource(ResXDataNode node) { + // we're modifying the node as we're adding it to the resxwriter + // this is BAD, so we clone it. adding it to a writer doesnt change it + // we're messing with a copy + ResXDataNode nodeClone = node.DeepClone(); + + ResXFileRef fileRef = nodeClone.FileRef; + string modifiedBasePath = BasePath; + + if (!String.IsNullOrEmpty(modifiedBasePath)) { + if (!(modifiedBasePath.EndsWith("\\"))) + { + modifiedBasePath += "\\"; + } + if (fileRef != null) { + fileRef.MakeFilePathRelative(modifiedBasePath); + } + } + DataNodeInfo info = nodeClone.GetDataNodeInfo(); + AddDataRow(DataStr, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); + } + + /// + /// Adds a blob resource to the resources. + /// + private void AddDataRow(string elementName, string name, byte[] value) { + AddDataRow(elementName, name, ToBase64WrappedString(value), TypeNameWithAssembly(typeof(byte[])), null, null); + } + + /// + /// Adds a resource to the resources. If the resource is a string, + /// it will be saved that way, otherwise it will be serialized + /// and stored as in binary. + /// + private void AddDataRow(string elementName, string name, object value) { + Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, " resx: adding resource " + name); + if (value is string) { + AddDataRow(elementName, name, (string)value); + } + else if (value is byte[]) { + AddDataRow(elementName, name, (byte[])value); + } + else if(value is ResXFileRef) { + ResXFileRef fileRef = (ResXFileRef)value; + ResXDataNode node = new ResXDataNode(name, fileRef, this.typeNameConverter); + if (fileRef != null) { + fileRef.MakeFilePathRelative(BasePath); + } + DataNodeInfo info = node.GetDataNodeInfo(); + AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); + } else { + ResXDataNode node = new ResXDataNode(name, value, this.typeNameConverter); + DataNodeInfo info = node.GetDataNodeInfo(); + AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment); + } + } + + /// + /// Adds a string resource to the resources. + /// + private void AddDataRow(string elementName, string name, string value) { + if(value == null) { + // if it's a null string, set it here as a resxnullref + AddDataRow(elementName, name, value, MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter), null, null); + } else { + AddDataRow(elementName, name, value, null, null, null); + } + } + + /// + /// Adds a new row to the Resources table. This helper is used because + /// we want to always late bind to the columns for greater flexibility. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void AddDataRow(string elementName, string name, string value, string type, string mimeType, string comment) { + if (hasBeenSaved) + throw new InvalidOperationException(SR.GetString(SR.ResXResourceWriterSaved)); + + string alias = null; + if (!string.IsNullOrEmpty(type) && elementName == DataStr) + { + string assemblyName = GetFullName(type); + if(string.IsNullOrEmpty(assemblyName)) { + try { + Type typeObject = Type.GetType(type); + if(typeObject == typeof(string)) { + type = null; + } else if(typeObject != null) { + assemblyName = GetFullName(MultitargetUtil.GetAssemblyQualifiedName(typeObject, this.typeNameConverter)); + alias = GetAliasFromName(new AssemblyName(assemblyName)); + } + } catch { + } + } else { + alias = GetAliasFromName(new AssemblyName(GetFullName(type))); + } + //AddAssemblyRow(AssemblyStr, alias, GetFullName(type)); + } + + Writer.WriteStartElement(elementName); { + Writer.WriteAttributeString(NameStr, name); + + if (!string.IsNullOrEmpty(alias) && !string.IsNullOrEmpty(type) && elementName == DataStr) { + // CHANGE: we still output version information. This might have + // to change in 3.2 + string typeName = GetTypeName(type); + string typeValue = typeName + ", " + alias; + Writer.WriteAttributeString(TypeStr, typeValue); + } + else { + if (type != null) + { + Writer.WriteAttributeString(TypeStr, type); + } + } + + if (mimeType != null) { + Writer.WriteAttributeString(MimeTypeStr, mimeType); + } + + if((type == null && mimeType == null) || (type != null && type.StartsWith("System.Char", StringComparison.Ordinal))) { + Writer.WriteAttributeString("xml", "space", null, "preserve"); + } + + Writer.WriteStartElement(ValueStr); { + if(!string.IsNullOrEmpty(value)) { + Writer.WriteString(value); + } + } + Writer.WriteEndElement(); + if(!string.IsNullOrEmpty(comment)) { + Writer.WriteStartElement(CommentStr); { + Writer.WriteString(comment); + } + Writer.WriteEndElement(); + } + } + Writer.WriteEndElement(); + } + + + private void AddAssemblyRow(string elementName, string alias, string name) + { + + Writer.WriteStartElement(elementName); { + if (!string.IsNullOrEmpty(alias)) { + Writer.WriteAttributeString(AliasStr, alias); + } + + if (!string.IsNullOrEmpty(name)) { + Writer.WriteAttributeString(NameStr, name); + } + //Writer.WriteEndElement(); + } + Writer.WriteEndElement(); + } + + private string GetAliasFromName(AssemblyName assemblyName) + { + if (cachedAliases == null) + { + cachedAliases = new Hashtable(); + } + string alias = (string) cachedAliases[assemblyName.FullName]; + if (string.IsNullOrEmpty(alias)) + { + alias = assemblyName.Name; + AddAlias(alias, assemblyName); + AddAssemblyRow(AssemblyStr, alias, assemblyName.FullName); + } + return alias; + } + + /// + /// + /// Closes any files or streams locked by the writer. + /// + // NOTE: Part of IResourceWriter - not protected by class level LinkDemand. + public void Close() { + Dispose(); + } + + /// + /// + /// [To be supplied.] + /// + // NOTE: Part of IDisposable - not protected by class level LinkDemand. + public virtual void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) { + if (disposing) { + if (!hasBeenSaved) { + Generate(); + } + if (xmlTextWriter != null) { + xmlTextWriter.Close(); + xmlTextWriter = null; + } + if (stream != null) { + stream.Close(); + stream = null; + } + if (textWriter != null) { + textWriter.Close(); + textWriter = null; + } + } + } + + private string GetTypeName(string typeName) { + int indexStart = typeName.IndexOf(","); + return ((indexStart == -1) ? typeName : typeName.Substring(0, indexStart)); + } + + + private string GetFullName(string typeName) { + int indexStart = typeName.IndexOf(","); + if(indexStart == -1) + return null; + return typeName.Substring(indexStart + 2); + } + +#if UNUSED + private string GetSimpleName(string typeName) { + int indexStart = typeName.IndexOf(","); + int indexEnd = typeName.IndexOf(",", indexStart + 1); + return typeName.Substring(indexStart + 2, indexEnd - indexStart - 3); + } + + static string StripVersionInformation(string typeName) { + int indexStart = typeName.IndexOf(" Version="); + if(indexStart ==-1) + indexStart = typeName.IndexOf("Version="); + if(indexStart ==-1) + indexStart = typeName.IndexOf("version="); + int indexEnd = -1; + string result = typeName; + if(indexStart != -1) { + // foudn version + indexEnd = typeName.IndexOf(",", indexStart); + if(indexEnd != -1) { + result = typeName.Remove(indexStart, indexEnd-indexStart+1); + } + } + return result; + + } +#endif + + + static string ToBase64WrappedString(byte[] data) { + const int lineWrap = 80; + const string crlf = "\r\n"; + const string prefix = " "; + string raw = Convert.ToBase64String(data); + if (raw.Length > lineWrap) { + StringBuilder output = new StringBuilder(raw.Length + (raw.Length / lineWrap) * 3); // word wrap on lineWrap chars, \r\n + int current = 0; + for (; current < raw.Length - lineWrap; current+=lineWrap) { + output.Append(crlf); + output.Append(prefix); + output.Append(raw, current, lineWrap); + } + output.Append(crlf); + output.Append(prefix); + output.Append(raw, current, raw.Length - current); + output.Append(crlf); + return output.ToString(); + } + else { + return raw; + } + } + + private string TypeNameWithAssembly(Type type) { + // + + + + + + + + + + + + string result = MultitargetUtil.GetAssemblyQualifiedName(type, this.typeNameConverter); + return result; + } + + /// + /// + /// Writes the resources out to the file or stream. + /// + // NOTE: Part of IResourceWriter - not protected by class level LinkDemand. + public void Generate() { + if (hasBeenSaved) + throw new InvalidOperationException(SR.GetString(SR.ResXResourceWriterSaved)); + + hasBeenSaved = true; + Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, "writing XML"); + + Writer.WriteEndElement(); + Writer.Flush(); + + Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, "done"); + } + } +} + + + diff --git a/WindowsForms/Managed/System/WinForms/AccessibleEvents.cs b/WindowsForms/Managed/System/WinForms/AccessibleEvents.cs new file mode 100644 index 000000000..445086a3b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleEvents.cs @@ -0,0 +1,397 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Specifies + /// events that are reported by accessible applications. + /// + + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum AccessibleEvents { + + // + // EVENT_SYSTEM_SOUND + // Sent when a sound is played. Currently nothing is generating this, we + // are going to be cleaning up the SOUNDSENTRY feature in the control panel + // and will use this at that time. Applications implementing WinEvents + // are perfectly welcome to use it. Clients of IAccessible* will simply + // turn around and get back a non-visual object that describes the sound. + // + /// + /// + /// [To be supplied.] + /// + SystemSound = 0x0001, + + // + // EVENT_SYSTEM_ALERT + // Sent when an alert needs to be given to the user. MessageBoxes generate + // alerts for example. + // + /// + /// + /// [To be supplied.] + /// + SystemAlert = 0x0002, + + // + // EVENT_SYSTEM_FOREGROUND + // Sent when the foreground (active) window changes, even if it is changing + // to another window in the same thread as the previous one. + // + /// + /// + /// [To be supplied.] + /// + SystemForeground = 0x0003, + + // + // EVENT_SYSTEM_MENUSTART + // EVENT_SYSTEM_MENUEND + // Sent when entering into and leaving from menu mode (system, app bar, and + // track popups). + // + /// + /// + /// [To be supplied.] + /// + SystemMenuStart = 0x0004, + /// + /// + /// [To be supplied.] + /// + SystemMenuEnd = 0x0005, + + // + // EVENT_SYSTEM_MENUPOPUPSTART + // EVENT_SYSTEM_MENUPOPUPEND + // Sent when a menu popup comes up and just before it is taken down. Note + // that for a call to TrackPopupMenu(), a client will see EVENT_SYSTEM_MENUSTART + // followed almost immediately by EVENT_SYSTEM_MENUPOPUPSTART for the popup + // being shown. + // + /// + /// + /// [To be supplied.] + /// + SystemMenuPopupStart = 0x0006, + /// + /// + /// [To be supplied.] + /// + SystemMenuPopupEnd = 0x0007, + + + // + // EVENT_SYSTEM_CAPTURESTART + // EVENT_SYSTEM_CAPTUREEND + // Sent when a window takes the capture and releases the capture. + // + /// + /// + /// [To be supplied.] + /// + SystemCaptureStart = 0x0008, + /// + /// + /// [To be supplied.] + /// + SystemCaptureEnd = 0x0009, + + // + // EVENT_SYSTEM_MOVESIZESTART + // EVENT_SYSTEM_MOVESIZEEND + // Sent when a window enters and leaves move-size dragging mode. + // + /// + /// + /// [To be supplied.] + /// + SystemMoveSizeStart = 0x000A, + /// + /// + /// [To be supplied.] + /// + SystemMoveSizeEnd = 0x000B, + + // + // EVENT_SYSTEM_CONTEXTHELPSTART + // EVENT_SYSTEM_CONTEXTHELPEND + // Sent when a window enters and leaves context sensitive help mode. + // + /// + /// + /// [To be supplied.] + /// + SystemContextHelpStart = 0x000C, + /// + /// + /// [To be supplied.] + /// + SystemContextHelpEnd = 0x000D, + + // + // EVENT_SYSTEM_DRAGDROPSTART + // EVENT_SYSTEM_DRAGDROPEND + // Sent when a window enters and leaves drag drop mode. Note that it is up + // to apps and OLE to generate this, since the system doesn't know. Like + // EVENT_SYSTEM_SOUND, it will be a while before this is prevalent. + // + /// + /// + /// [To be supplied.] + /// + SystemDragDropStart = 0x000E, + /// + /// + /// [To be supplied.] + /// + SystemDragDropEnd = 0x000F, + + // + // EVENT_SYSTEM_DIALOGSTART + // EVENT_SYSTEM_DIALOGEND + // Sent when a dialog comes up and just before it goes away. + // + /// + /// + /// [To be supplied.] + /// + SystemDialogStart = 0x0010, + /// + /// + /// [To be supplied.] + /// + SystemDialogEnd = 0x0011, + + // + // EVENT_SYSTEM_SCROLLINGSTART + // EVENT_SYSTEM_SCROLLINGEND + // Sent when beginning and ending the tracking of a scrollbar in a window, + // and also for scrollbar controls. + // + /// + /// + /// [To be supplied.] + /// + SystemScrollingStart = 0x0012, + /// + /// + /// [To be supplied.] + /// + SystemScrollingEnd = 0x0013, + + // + // EVENT_SYSTEM_SWITCHSTART + // EVENT_SYSTEM_SWITCHEND + // Sent when beginning and ending alt-tab mode with the switch window. + // + /// + /// + /// [To be supplied.] + /// + SystemSwitchStart = 0x0014, + /// + /// + /// [To be supplied.] + /// + SystemSwitchEnd = 0x0015, + + // + // EVENT_SYSTEM_MINIMIZESTART + // EVENT_SYSTEM_MINIMIZEEND + // Sent when a window minimizes and just before it restores. + // + /// + /// + /// [To be supplied.] + /// + SystemMinimizeStart = 0x0016, + /// + /// + /// [To be supplied.] + /// + SystemMinimizeEnd = 0x0017, + + + + // + // Object events + // + // The system AND apps generate these. The system generates these for + // real windows. Apps generate these for objects within their window which + // act like a separate control, e.g. an item in a list view. + // + // For all events, if you want detailed accessibility information, callers + // should + // * Call AccessibleObjectFromWindow() with the hwnd, idObject parameters + // of the event, and IID_IAccessible as the REFIID, to get back an + // IAccessible* to talk to + // * Initialize and fill in a VARIANT as VT_I4 with lVal the idChild + // parameter of the event. + // * If idChild isn't zero, call get_accChild() in the container to see + // if the child is an object in its own right. If so, you will get + // back an IDispatch* object for the child. You should release the + // parent, and call QueryInterface() on the child object to get its + // IAccessible*. Then you talk directly to the child. Otherwise, + // if get_accChild() returns you nothing, you should continue to + // use the child VARIANT. You will ask the container for the properties + // of the child identified by the VARIANT. In other words, the + // child in this case is accessible but not a full-blown object. + // Like a button on a titlebar which is 'small' and has no children. + // + + // + /// + /// + /// [To be supplied.] + /// + Create = 0x8000, // hwnd + ID + idChild is created item + /// + /// + /// [To be supplied.] + /// + Destroy = 0x8001, // hwnd + ID + idChild is destroyed item + /// + /// + /// [To be supplied.] + /// + Show = 0x8002, // hwnd + ID + idChild is shown item + /// + /// + /// [To be supplied.] + /// + Hide = 0x8003, // hwnd + ID + idChild is hidden item + /// + /// + /// [To be supplied.] + /// + Reorder = 0x8004, // hwnd + ID + idChild is parent of zordering children + // + // NOTE: + // Minimize the number of notifications! + // + // When you are hiding a parent object, obviously all child objects are no + // longer visible on screen. They still have the same "visible" status, + // but are not truly visible. Hence do not send HIDE notifications for the + // children also. One implies all. The same goes for SHOW. + // + + + /// + /// + /// [To be supplied.] + /// + Focus = 0x8005, // hwnd + ID + idChild is focused item + /// + /// + /// [To be supplied.] + /// + Selection = 0x8006, // hwnd + ID + idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex + /// + /// + /// [To be supplied.] + /// + SelectionAdd = 0x8007, // hwnd + ID + idChild is item added + /// + /// + /// [To be supplied.] + /// + SelectionRemove = 0x8008, // hwnd + ID + idChild is item removed + /// + /// + /// [To be supplied.] + /// + SelectionWithin = 0x8009, // hwnd + ID + idChild is parent of changed selected items + + // + // NOTES: + // There is only one "focused" child item in a parent. This is the place + // keystrokes are going at a given moment. Hence only send a notification + // about where the NEW focus is going. A NEW item getting the focus already + // implies that the OLD item is losing it. + // + // SELECTION however can be multiple. Hence the different SELECTION + // notifications. Here's when to use each: + // + // (1) Send a SELECTION notification in the simple single selection + // case (like the focus) when the item with the selection is + // merely moving to a different item within a container. hwnd + ID + // is the container control, idChildItem is the new child with the + // selection. + // + // (2) Send a SELECTIONADD notification when a new item has simply been added + // to the selection within a container. This is appropriate when the + // number of newly selected items is very small. hwnd + ID is the + // container control, idChildItem is the new child added to the selection. + // + // (3) Send a SELECTIONREMOVE notification when a new item has simply been + // removed from the selection within a container. This is appropriate + // when the number of newly selected items is very small, just like + // SELECTIONADD. hwnd + ID is the container control, idChildItem is the + // new child removed from the selection. + // + // (4) Send a SELECTIONWITHIN notification when the selected items within a + // control have changed substantially. Rather than propagate a large + // number of changes to reflect removal for some items, addition of + // others, just tell somebody who cares that a lot happened. It will + // be faster an easier for somebody watching to just turn around and + // query the container control what the new bunch of selected items + // are. + // + + /// + /// + /// [To be supplied.] + /// + StateChange = 0x800A, // hwnd + ID + idChild is item w/ state change + /// + /// + /// [To be supplied.] + /// + LocationChange = 0x800B, // hwnd + ID + idChild is moved/sized item + + /// + /// + /// [To be supplied.] + /// + NameChange = 0x800C, // hwnd + ID + idChild is item w/ name change + /// + /// + /// [To be supplied.] + /// + DescriptionChange = 0x800D, // hwnd + ID + idChild is item w/ desc change + /// + /// + /// [To be supplied.] + /// + ValueChange = 0x800E, // hwnd + ID + idChild is item w/ value change + /// + /// + /// [To be supplied.] + /// + ParentChange = 0x800F, // hwnd + ID + idChild is item w/ new parent + /// + /// + /// [To be supplied.] + /// + HelpChange = 0x8010, // hwnd + ID + idChild is item w/ help change + /// + /// + /// [To be supplied.] + /// + DefaultActionChange = 0x8011, // hwnd + ID + idChild is item w/ def action change + /// + /// + /// [To be supplied.] + /// + AcceleratorChange = 0x8012, // hwnd + ID + idChild is item w/ keybd accel change + } +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleNavigation.cs b/WindowsForms/Managed/System/WinForms/AccessibleNavigation.cs new file mode 100644 index 000000000..87c64ddbd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleNavigation.cs @@ -0,0 +1,111 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies values for navigating between accessible objects. + /// + /// + public enum AccessibleNavigation { + + /// + /// + /// + /// Navigation + /// to a sibling object located + /// below the + /// starting object. + /// + /// + Down = 0x2, + + /// + /// + /// + /// Navigation to + /// the + /// first child of the object. + /// + /// + FirstChild = 0x7, + + /// + /// + /// + /// Navigation to + /// the + /// last child of the object + /// + /// + LastChild = 0x8, + + /// + /// + /// + /// Navigation + /// to the sibling object located + /// to the left + /// of the + /// starting object. + /// + /// + Left = 0x3, + + /// + /// + /// + /// Navigation + /// to the next logical object, generally from a + /// sibling + /// object to the + /// starting object. + /// + /// + Next = 0x5, + + /// + /// + /// + /// Navigation to the previous logical object, generally + /// from + /// a sibling + /// object to the + /// starting object. + /// + /// + Previous = 0x6, + + /// + /// + /// + /// Navigation to the sibling object + /// located to the right of the + /// starting object. + /// + /// + Right = 0x4, + + /// + /// + /// + /// Navigation to a sibling object + /// located above the + /// starting object. + /// + /// + Up = 0x1, + } +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleObject.cs b/WindowsForms/Managed/System/WinForms/AccessibleObject.cs new file mode 100644 index 000000000..83586d54a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleObject.cs @@ -0,0 +1,3927 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using Accessibility; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Collections; + using System.Drawing; + using System.Windows.Forms; + using Microsoft.Win32; + using System.ComponentModel; + using System.Reflection; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using Automation; + + /// + /// + /// Provides an implementation for an object that can be inspected by an + /// accessibility application. + /// + [ComVisible(true)] + public class AccessibleObject : StandardOleMarshalObject, + IReflect, + IAccessible, + UnsafeNativeMethods.IAccessibleEx, + UnsafeNativeMethods.IServiceProvider, + UnsafeNativeMethods.IRawElementProviderSimple, + UnsafeNativeMethods.IRawElementProviderFragment, + UnsafeNativeMethods.IRawElementProviderFragmentRoot, + UnsafeNativeMethods.IInvokeProvider, + UnsafeNativeMethods.IValueProvider, + UnsafeNativeMethods.IRangeValueProvider, + UnsafeNativeMethods.IExpandCollapseProvider, + UnsafeNativeMethods.IToggleProvider, + UnsafeNativeMethods.ITableProvider, + UnsafeNativeMethods.ITableItemProvider, + UnsafeNativeMethods.IGridProvider, + UnsafeNativeMethods.IGridItemProvider, + UnsafeNativeMethods.IEnumVariant, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.ILegacyIAccessibleProvider, + UnsafeNativeMethods.ISelectionProvider, + UnsafeNativeMethods.ISelectionItemProvider, + UnsafeNativeMethods.IRawElementProviderHwndOverride, + UnsafeNativeMethods.IScrollItemProvider { + + // Member variables + + /// + /// + /// Specifies the interface used by this . + /// + private IAccessible systemIAccessible = null; + + /// + /// + /// Specifies the + /// used by this . + /// + private UnsafeNativeMethods.IEnumVariant systemIEnumVariant = null; + private UnsafeNativeMethods.IEnumVariant enumVariant = null; + + // IOleWindow interface of the 'inner' system IAccessible object that we are wrapping + private UnsafeNativeMethods.IOleWindow systemIOleWindow = null; + + private bool systemWrapper = false; // Indicates this object is being used ONLY to wrap a system IAccessible + + private int accObjId = NativeMethods.OBJID_CLIENT; // Indicates what kind of 'inner' system accessible object we are using + + // The support for the UIA Notification event begins in RS3. + // Assume the UIA Notification event is available until we learn otherwise. + // If we learn that the UIA Notification event is not available, + // controls should not attempt to raise it. + private static bool notificationEventAvailable = true; + + internal const int RuntimeIDFirstItem = 0x2a; + + /// + public AccessibleObject() { + } + + // This constructor is used ONLY for wrapping system IAccessible objects + // + private AccessibleObject(IAccessible iAcc) { + this.systemIAccessible = iAcc; + this.systemWrapper = true; + } + + // Properties + + /// + /// + /// Gets the bounds of the accessible object, in screen coordinates. + /// + public virtual Rectangle Bounds { + get { + // Use the system provided bounds + if (systemIAccessible != null) { + int left = 0; + int top = 0; + int width = 0; + int height = 0; + try { + systemIAccessible.accLocation(out left, out top, out width, out height, NativeMethods.CHILDID_SELF); + return new Rectangle(left, top, width, height); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return Rectangle.Empty; + } + } + + /// + /// + /// Gets a description of the default action for an object. + /// + public virtual string DefaultAction { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDefaultAction(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets a description + /// of the object's visual appearance to the user. + /// + public virtual string Description { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDescription(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + private UnsafeNativeMethods.IEnumVariant EnumVariant { + get { + if (enumVariant == null) { + enumVariant = new EnumVariantObject(this); + } + return enumVariant; + } + } + + /// + /// + /// Gets a description of what the object does or how the object is used. + /// + public virtual string Help { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelp(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets the object shortcut key or access key + /// for an accessible object. + /// + public virtual string KeyboardShortcut { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accKeyboardShortcut(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets + /// or sets the object name. + /// + public virtual string Name { + // Does nothing by default + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accName(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + + set { + if (systemIAccessible != null) { + try { + systemIAccessible.set_accName(NativeMethods.CHILDID_SELF, value); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + } + } + + /// + /// + /// When overridden in a derived class, gets or sets the parent of an accessible object. + /// + public virtual AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (systemIAccessible != null) { + return WrapIAccessible(systemIAccessible.accParent); + } + else { + return null; + } + } + } + + /// + /// + /// Gets the role of this accessible object. + /// + public virtual AccessibleRole Role { + get { + if (systemIAccessible != null) { + return (AccessibleRole) systemIAccessible.get_accRole(NativeMethods.CHILDID_SELF); + } + else { + return AccessibleRole.None; + } + } + } + + /// + /// + /// Gets + /// the state of this accessible object. + /// + public virtual AccessibleStates State { + get { + if (systemIAccessible != null) { + return (AccessibleStates) systemIAccessible.get_accState(NativeMethods.CHILDID_SELF); + } + else { + return AccessibleStates.None; + } + } + } + + /// + /// + /// Gets or sets the value of an accessible object. + /// + public virtual string Value { + // Does nothing by default + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accValue(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return ""; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set { + if (systemIAccessible != null) { + try { + systemIAccessible.set_accValue(NativeMethods.CHILDID_SELF, value); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + } + } + + // Methods + + /// + /// + /// When overridden in a derived class, gets the accessible child corresponding to the specified + /// index. + /// + public virtual AccessibleObject GetChild(int index) { + return null; + } + + /// + /// + /// When overridden in a derived class, gets the number of children + /// belonging to an accessible object. + /// + public virtual int GetChildCount() { + return -1; + } + + /// + /// + /// Mechanism for overriding default IEnumVariant behavior of the 'inner' system accessible object + /// (IEnumVariant is how a system accessible object exposes its ordered list of child objects). + /// + /// USAGE: Overridden method in derived class should return array of integers representing new order + /// to be imposed on the child accessible object collection returned by the system (which + /// we assume will be a set of accessible objects that represent the child windows, in + /// z-order). Each array element contains the original z-order based rank of the child window + /// that is to appear at that position in the new ordering. Note: This array could also be + /// used to filter out unwanted child windows too, if necessary (not recommended). + /// + internal virtual int[] GetSysChildOrder() { + return null; + } + + /// + /// + /// Mechanism for overriding default IAccessible.accNavigate behavior of the 'inner' system accessible + /// object (accNavigate is how you move between parent, child and sibling accessible objects). + /// + /// USAGE: 'navdir' indicates navigation operation to perform, relative to this accessible object. + /// If operation is unsupported, return false to allow fall-back to default system behavior. Otherwise + /// return destination object in the out parameter, or null to indicate 'off end of list'. + /// + internal virtual bool GetSysChild(AccessibleNavigation navdir, out AccessibleObject accessibleObject) { + accessibleObject = null; + return false; + } + + /// + /// + /// When overridden in a derived class, + /// gets the object that has the keyboard focus. + /// + public virtual AccessibleObject GetFocused() { + + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && ((child.State & AccessibleStates.Focused) != 0)) { + return child; + } + } + if ((this.State & AccessibleStates.Focused) != 0) { + return this; + } + return null; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accFocus); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// Gets an identifier for a Help topic and the path to the Help file associated + /// with this accessible object. + /// + public virtual int GetHelpTopic(out string fileName) { + if (systemIAccessible != null) { + try { + int retVal = systemIAccessible.get_accHelpTopic(out fileName, NativeMethods.CHILDID_SELF); + if (fileName != null && fileName.Length > 0) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + return retVal; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + fileName = null; + return -1; + } + + /// + /// + /// When overridden in + /// a derived class, gets the currently selected child. + /// + public virtual AccessibleObject GetSelected() { + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && ((child.State & AccessibleStates.Selected) != 0)) { + return child; + } + } + if ((this.State & AccessibleStates.Selected) != 0) { + return this; + } + return null; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accSelection); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// Return the child object at the given screen coordinates. + /// + public virtual AccessibleObject HitTest(int x, int y) { + + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && child.Bounds.Contains(x, y)) { + return child; + } + } + return this; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accHitTest(x, y)); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + if (this.Bounds.Contains(x, y)) { + return this; + } + + return null; + } + + // + // UIAutomation support helpers + // + + internal virtual bool IsIAccessibleExSupported() { + // Override this, in your derived class, to enable IAccessibleEx support + return false; + } + + internal virtual bool IsPatternSupported(int patternId) { + // Override this, in your derived class, if you implement UIAutomation patterns + + if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_InvokePatternId) { + return IsInvokePatternAvailable; + } + + return false; + } + + // + // Overridable methods for IRawElementProviderSimple interface + // + + internal virtual int[] RuntimeId { + get { + return null; + } + } + + internal virtual int ProviderOptions { + get { + return (int)(UnsafeNativeMethods.ProviderOptions.ServerSideProvider | UnsafeNativeMethods.ProviderOptions.UseComThreading); + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + return null; + } + } + + internal virtual object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsInvokePatternAvailablePropertyId) { + return IsInvokePatternAvailable; + } + + return null; + } + + private bool IsInvokePatternAvailable { + get { + // MSAA Proxy determines the availability of invoke pattern based on Role/DefaultAction properties. + // Below code emulates the same rules. + switch (Role) { + case AccessibleRole.MenuItem: + case AccessibleRole.Link: + case AccessibleRole.PushButton: + case AccessibleRole.ButtonDropDown: + case AccessibleRole.ButtonMenu: + case AccessibleRole.ButtonDropDownGrid: + case AccessibleRole.Clock: + case AccessibleRole.SplitButton: + return true; + + case AccessibleRole.Default: + case AccessibleRole.None: + case AccessibleRole.Sound: + case AccessibleRole.Cursor: + case AccessibleRole.Caret: + case AccessibleRole.Alert: + case AccessibleRole.Client: + case AccessibleRole.Chart: + case AccessibleRole.Dialog: + case AccessibleRole.Border: + case AccessibleRole.Column: + case AccessibleRole.Row: + case AccessibleRole.HelpBalloon: + case AccessibleRole.Character: + case AccessibleRole.PageTab: + case AccessibleRole.PropertyPage: + case AccessibleRole.DropList: + case AccessibleRole.Dial: + case AccessibleRole.HotkeyField: + case AccessibleRole.Diagram: + case AccessibleRole.Animation: + case AccessibleRole.Equation: + case AccessibleRole.WhiteSpace: + case AccessibleRole.IpAddress: + case AccessibleRole.OutlineButton: + return false; + + default: + return !string.IsNullOrEmpty(DefaultAction); + } + } + } + + // + // Overridable methods for ILegacyAccessible interface + // + + internal virtual int GetChildId() { + return NativeMethods.CHILDID_SELF; + } + + // + // Overridable methods for IRawElementProviderFragment interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetEmbeddedFragmentRoots() { + return null; + } + + internal virtual void SetFocus() { + } + + internal virtual Rectangle BoundingRectangle { + get { + return this.Bounds; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return null; + } + } + + // + // Overridable methods for IRawElementProviderFragmentRoot interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + return this; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return null; + } + + // + // Overridable methods for IExpandCollapseProvider pattern interface + // + + internal virtual void Expand() { + } + + internal virtual void Collapse() { + } + + internal virtual UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + // + // Overridable methods for IToggleProvider pattern interface + // + + internal virtual void Toggle() { + } + + internal virtual UnsafeNativeMethods.ToggleState ToggleState { + get { + return UnsafeNativeMethods.ToggleState.ToggleState_Indeterminate; + } + } + + // + // Overridable methods for ITableProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders() { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaders() { + return null; + } + + internal virtual UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor { + get { + return UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor; + } + } + + // + // Overridable methods for ITableItemProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaderItems() { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaderItems() { + return null; + } + + // + // Overridable methods for IGridProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column) { + return null; + } + + internal virtual int RowCount { + get { + return -1; + } + } + + internal virtual int ColumnCount { + get { + return -1; + } + } + + // + // Overridable methods for IGridItemProvider pattern interface + // + + internal virtual int Row { + get { + return -1; + } + } + + internal virtual int Column { + get { + return -1; + } + } + + internal virtual int RowSpan { + get { + return 1; + } + } + + internal virtual int ColumnSpan { + get { + return 1; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple ContainingGrid { + get { + return null; + } + } + + // + // Overridable methods for IInvokeProvider pattern interface + // + + internal virtual void Invoke() { + // Calling DoDefaultAction here is consistent with MSAA Proxy implementation. + DoDefaultAction(); + } + + // Overridable methods for IValueProvider pattern interface + + internal virtual bool IsReadOnly { + get { + return false; + } + } + + internal virtual void SetValue(string newValue) { + this.Value = newValue; + } + + // + // Overridable methods for IRawElementProviderHwndOverride interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd) { + return null; + } + + // + // Overridable methods for IRangeValueProvider pattern interface + // + + internal virtual void SetValue(double newValue) { + } + + internal virtual double LargeChange { + get { + return double.NaN; + } + } + + internal virtual double Maximum { + get { + return double.NaN; + } + } + + internal virtual double Minimum { + get { + return double.NaN; + } + } + + internal virtual double SmallChange { + get { + return double.NaN; + } + } + + internal virtual double RangeValue { + get { + return double.NaN; + } + } + + // + // Overridable methods for ISelectionProvider interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetSelection() { + return null; + } + + internal virtual bool CanSelectMultiple { + get { + return false; + } + } + + internal virtual bool IsSelectionRequired { + get { + return false; + } + } + + // + // Overridable methods for ISelectionItemProvider interface + // + + internal virtual void SelectItem() { + } + + internal virtual void AddToSelection() { + } + + internal virtual void RemoveFromSelection() { + } + + internal virtual bool IsItemSelected { + get { + return false; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple ItemSelectionContainer { + get { + return null; + } + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal virtual void SetParent(AccessibleObject parent) + { + } + + /// + /// Sets the detachable child accessible object which may be added or removed to/from hierachy nodes. + /// + /// The child accessible object. + internal virtual void SetDetachableChild(AccessibleObject child) + { + } + + int UnsafeNativeMethods.IServiceProvider.QueryService(ref Guid service, ref Guid riid, out IntPtr ppvObject) { + + int hr = NativeMethods.E_NOINTERFACE; + ppvObject = IntPtr.Zero; + + if (IsIAccessibleExSupported()) { + if (service.Equals(UnsafeNativeMethods.guid_IAccessibleEx) && + riid.Equals(UnsafeNativeMethods.guid_IAccessibleEx)) { + // We want to return the internal, secure, object, which we don't have access here + // Return non-null, which will be interpreted in internal method, to mean returning casted object to IAccessibleEx + + ppvObject = Marshal.GetComInterfaceForObject(this, typeof(UnsafeNativeMethods.IAccessibleEx)); + hr = NativeMethods.S_OK; + } + } + + return hr; + } + + object UnsafeNativeMethods.IAccessibleEx.GetObjectForChild(int childId) { + return GetObjectForChild(childId); + } + + internal virtual object GetObjectForChild(int childId) { + return null; + } + + // This method is never called + int UnsafeNativeMethods.IAccessibleEx.GetIAccessiblePair(out object ppAcc, out int pidChild) { + + // No need to implement this for patterns and properties + ppAcc = null; + pidChild = 0; + return NativeMethods.E_POINTER; + } + + int[] UnsafeNativeMethods.IAccessibleEx.GetRuntimeId() { + return RuntimeId; + } + + int UnsafeNativeMethods.IAccessibleEx.ConvertReturnedElement(object pIn, out object ppRetValOut) { + + // No need to implement this for patterns and properties + ppRetValOut = null; + return NativeMethods.E_NOTIMPL; + } + + // + // IRawElementProviderSimple interface + // + + UnsafeNativeMethods.ProviderOptions UnsafeNativeMethods.IRawElementProviderSimple.ProviderOptions { + get { + return (UnsafeNativeMethods.ProviderOptions) ProviderOptions; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderSimple.HostRawElementProvider { + get { + return HostRawElementProvider; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPatternProvider(int patternId) { + if (IsPatternSupported(patternId)) { + return this; + } + else { + return null; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPropertyValue(int propertyID) { + return GetPropertyValue(propertyID); + } + + // + // IRawElementProviderFragment interface + // + + object UnsafeNativeMethods.IRawElementProviderFragment.Navigate(UnsafeNativeMethods.NavigateDirection direction) { + return FragmentNavigate(direction); + } + + int[] UnsafeNativeMethods.IRawElementProviderFragment.GetRuntimeId() { + return RuntimeId; + } + + object[] UnsafeNativeMethods.IRawElementProviderFragment.GetEmbeddedFragmentRoots() { + return GetEmbeddedFragmentRoots(); + } + + void UnsafeNativeMethods.IRawElementProviderFragment.SetFocus() { + SetFocus(); + } + + NativeMethods.UiaRect UnsafeNativeMethods.IRawElementProviderFragment.BoundingRectangle { + get { + return new NativeMethods.UiaRect(BoundingRectangle); + } + } + + UnsafeNativeMethods.IRawElementProviderFragmentRoot UnsafeNativeMethods.IRawElementProviderFragment.FragmentRoot { + get { + return FragmentRoot; + } + } + + // + // IRawElementProviderFragmentRoot interface + // + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y) { + return ElementProviderFromPoint(x, y); + } + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.GetFocus() { + return GetFocus(); + } + + // + // ILegacyIAccessibleProvider interface + // + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.DefaultAction { + get { + return this.DefaultAction; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Description { + get { + return this.Description; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Help { + get { + return this.Help; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.KeyboardShortcut { + get { + return this.KeyboardShortcut; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Name { + get { + return this.Name; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.Role { + get { + return (uint)this.Role; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.State { + get { + return (uint)this.State; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Value { + get { + return this.Value; + } + } + + int UnsafeNativeMethods.ILegacyIAccessibleProvider.ChildId { + get { + return GetChildId(); + } + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.DoDefaultAction() { + this.DoDefaultAction(); + } + + IAccessible UnsafeNativeMethods.ILegacyIAccessibleProvider.GetIAccessible() { + return this.AsIAccessible(this); + } + + object[] UnsafeNativeMethods.ILegacyIAccessibleProvider.GetSelection() { + return new UnsafeNativeMethods.IRawElementProviderSimple[] { + this.GetSelected() as UnsafeNativeMethods.IRawElementProviderSimple + }; + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.Select(int flagsSelect) { + this.Select((AccessibleSelection)flagsSelect); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.SetValue(string szValue) { + this.SetValue(szValue); + } + + // + // IExpandCollapseProvider interface + // + + void UnsafeNativeMethods.IExpandCollapseProvider.Expand() { + Expand(); + } + void UnsafeNativeMethods.IExpandCollapseProvider.Collapse() { + Collapse(); + } + + UnsafeNativeMethods.ExpandCollapseState UnsafeNativeMethods.IExpandCollapseProvider.ExpandCollapseState { + get { + return ExpandCollapseState; + } + } + + // + // IInvokeProvider interface + // + + void UnsafeNativeMethods.IInvokeProvider.Invoke() { + Invoke(); + } + + // + // IValueProvider interface + // + + bool UnsafeNativeMethods.IValueProvider.IsReadOnly { + get { + return IsReadOnly; + } + } + + string UnsafeNativeMethods.IValueProvider.Value { + get { + return Value; + } + } + + void UnsafeNativeMethods.IValueProvider.SetValue(string newValue) { + SetValue(newValue); + } + + // + // IToggleProvider implementation + // + + void UnsafeNativeMethods.IToggleProvider.Toggle() { + Toggle(); + } + + UnsafeNativeMethods.ToggleState UnsafeNativeMethods.IToggleProvider.ToggleState { + get { + return ToggleState; + } + } + + // + // ITableProvider implementation + // + + object[] UnsafeNativeMethods.ITableProvider.GetRowHeaders() { + return GetRowHeaders(); + } + + object[] UnsafeNativeMethods.ITableProvider.GetColumnHeaders() { + return GetColumnHeaders(); + } + + UnsafeNativeMethods.RowOrColumnMajor UnsafeNativeMethods.ITableProvider.RowOrColumnMajor { + get { + return RowOrColumnMajor; + } + } + + // + // ITableItemProvider implementation + // + + object[] UnsafeNativeMethods.ITableItemProvider.GetRowHeaderItems() { + return GetRowHeaderItems(); + } + + object[] UnsafeNativeMethods.ITableItemProvider.GetColumnHeaderItems() { + return GetColumnHeaderItems(); + } + + // + // IGridProvider implementation + // + + object UnsafeNativeMethods.IGridProvider.GetItem(int row, int column) { + return GetItem(row, column); + } + + int UnsafeNativeMethods.IGridProvider.RowCount { + get { + return RowCount; + } + } + + int UnsafeNativeMethods.IGridProvider.ColumnCount { + get { + return ColumnCount; + } + } + + // + // IGridItemProvider implementation + // + + int UnsafeNativeMethods.IGridItemProvider.Row { + get { + return Row; + } + } + + int UnsafeNativeMethods.IGridItemProvider.Column { + get { + return Column; + } + } + + int UnsafeNativeMethods.IGridItemProvider.RowSpan { + get { + return RowSpan; + } + } + + int UnsafeNativeMethods.IGridItemProvider.ColumnSpan { + get { + return ColumnSpan; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IGridItemProvider.ContainingGrid { + get { + return ContainingGrid; + } + } + + /// + /// + /// + /// + /// Perform the default action + /// + /// + void IAccessible.accDoDefaultAction(Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccDoDefaultAction: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // If the default action is to be performed on self, do it. + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + DoDefaultAction(); + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.DoDefaultAction(); + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accDoDefaultAction(childID); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + } + + /// + /// + /// + /// + /// Perform a hit test + /// + /// + Object IAccessible.accHitTest( + int xLeft, + int yTop) { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccHitTest: this = " + + this.ToString()); + + AccessibleObject obj = HitTest(xLeft, yTop); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accHitTest(xLeft, yTop); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// The location of the Accessible object + /// + /// + void IAccessible.accLocation( + out int pxLeft, + out int pyTop, + out int pcxWidth, + out int pcyHeight, + Object childID) { + + pxLeft = 0; + pyTop = 0; + pcxWidth = 0; + pcyHeight = 0; + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Use the Location function's return value if available + // + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + Rectangle bounds = this.Bounds; + pxLeft = bounds.X; + pyTop = bounds.Y; + pcxWidth = bounds.Width; + pcyHeight = bounds.Height; + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Returning " + + bounds.ToString()); + + return; + } + + // If we have an accessible object collection, get the appropriate child + // + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + Rectangle bounds = child.Bounds; + pxLeft = bounds.X; + pyTop = bounds.Y; + pcxWidth = bounds.Width; + pcyHeight = bounds.Height; + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Returning " + + bounds.ToString()); + + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accLocation(out pxLeft, out pyTop, out pcxWidth, out pcyHeight, childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Setting " + + pxLeft.ToString(CultureInfo.InvariantCulture) + ", " + + pyTop.ToString(CultureInfo.InvariantCulture) + ", " + + pcxWidth.ToString(CultureInfo.InvariantCulture) + ", " + + pcyHeight.ToString(CultureInfo.InvariantCulture)); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + + return; + } + } + + /// + /// + /// + /// + /// Navigate to another accessible object. + /// + /// + Object IAccessible.accNavigate( + int navDir, + Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccNavigate: this = " + + this.ToString() + ", navdir = " + navDir.ToString(CultureInfo.InvariantCulture) + ", childID = " + childID.ToString()); + + // Use the Navigate function's return value if available + // + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + AccessibleObject newObject = Navigate((AccessibleNavigation)navDir); + if (newObject != null) { + return AsVariant(newObject); + } + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return AsVariant(child.Navigate((AccessibleNavigation)navDir)); + } + } + + if (systemIAccessible != null) { + try { + Object retObject; + if (!SysNavigate(navDir, childID, out retObject)) + retObject = systemIAccessible.accNavigate(navDir, childID); + return retObject; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Select an accessible object. + /// + /// + void IAccessible.accSelect(int flagsSelect, Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccSelect: this = " + + this.ToString() + ", flagsSelect = " + flagsSelect.ToString(CultureInfo.InvariantCulture) + ", childID = " + childID.ToString()); + + // If the selection is self, do it. + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + Select((AccessibleSelection)flagsSelect); // Uses an Enum which matches SELFLAG + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Select((AccessibleSelection)flagsSelect); + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accSelect(flagsSelect, childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + + } + + /// + /// + /// Performs the default action associated with this accessible object. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual void DoDefaultAction() { + // By default, just does the system default action if available + // + if (systemIAccessible != null) { + try { + systemIAccessible.accDoDefaultAction(0); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + /// + /// + /// + /// + /// Returns a child Accessible object + /// + /// + object IAccessible.get_accChild(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccChild: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Return self for CHILDID_SELF + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return AsIAccessible(this); + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + // Make sure we're not returning ourselves as our own child + // + Debug.Assert(child != this, "An accessible object is returning itself as its own child. This can cause Accessibility client applications to hang."); + if (child == this) { + return null; + } + + return AsIAccessible(child); + } + } + + // Otherwise, return the default system child for this control (if any) + if (systemIAccessible != null) { + return systemIAccessible.get_accChild(childID); + } + + return null; + } + + /// + /// + /// + /// Return the number of children + /// + int IAccessible.accChildCount { + get { + int childCount = -1; + + if (IsClientObject) { + childCount = GetChildCount(); + } + + if (childCount == -1) { + if (systemIAccessible != null) { + childCount = systemIAccessible.accChildCount; + } + else { + childCount = 0; + } + } + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.accHildCount: this = " + this.ToString() + ", returning " + childCount.ToString(CultureInfo.InvariantCulture)); + + return childCount; + } + } + + /// + /// + /// + /// + /// Return the default action + /// + /// + string IAccessible.get_accDefaultAction(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the default action property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return DefaultAction; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.DefaultAction; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDefaultAction(childID); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child description + /// + /// + string IAccessible.get_accDescription(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the description property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Description; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Description; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDescription(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// Returns the appropriate child from the Accessible Child Collection, if available + /// + private AccessibleObject GetAccessibleChild(object childID) { + if (!childID.Equals(NativeMethods.CHILDID_SELF)) { + int index = (int)childID - 1; // The first child is childID == 1 (index == 0) + if (index >= 0 && index < GetChildCount()) { + return GetChild(index); + } + } + return null; + } + + /// + /// + /// + /// Return the object or child focus + /// + object IAccessible.accFocus { + get { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccFocus: this = " + + this.ToString()); + + AccessibleObject obj = GetFocused(); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accFocus; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + } + + /// + /// + /// + /// + /// Return help for this accessible object. + /// + /// + string IAccessible.get_accHelp(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Help; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Help; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelp(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child help topic + /// + /// + int IAccessible.get_accHelpTopic(out string pszHelpFile, Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return GetHelpTopic(out pszHelpFile); + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.GetHelpTopic(out pszHelpFile); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelpTopic(out pszHelpFile, childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + pszHelpFile = null; + return -1; + } + + /// + /// + /// + /// + /// Return the object or child keyboard shortcut + /// + /// + string IAccessible.get_accKeyboardShortcut(Object childID) { + return get_accKeyboardShortcutInternal(childID); + } + + internal virtual string get_accKeyboardShortcutInternal(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return KeyboardShortcut; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.KeyboardShortcut; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accKeyboardShortcut(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child name + /// + /// + string IAccessible.get_accName(object childID) { + return get_accNameInternal(childID); + } + + /// + /// + internal virtual string get_accNameInternal(object childID) { + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.get_accName: this = " + this.ToString() + + ", childID = " + childID.ToString()); + + // Return the name property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Name; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Name; + } + } + + // Otherwise, use the system provided name + if (systemIAccessible != null) { + string retval = systemIAccessible.get_accName(childID); + + if (IsClientObject) { + if (retval == null || retval.Length == 0) { + retval = Name; // Name the child after its parent + } + } + + return retval; + } + + return null; + } + + /// + /// + /// + /// Return the parent object + /// + object IAccessible.accParent { + get { + IntSecurity.UnmanagedCode.Demand(); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.accParent: this = " + this.ToString()); + AccessibleObject parent = Parent; + if (parent != null) { + // Some debugging related tests + // + Debug.Assert(parent != this, "An accessible object is returning itself as its own parent. This can cause accessibility clients to hang."); + if (parent == this) { + parent = null; // This should prevent accessibility clients from hanging + } + } + + return AsIAccessible(parent); + } + } + + /// + /// + /// + /// + /// The role property describes an object's purpose in terms of its + /// relationship with sibling or child objects. + /// + /// + object IAccessible.get_accRole(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the role property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return (int)Role; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return (int)child.Role; + } + } + + if (systemIAccessible != null) { + return systemIAccessible.get_accRole(childID); + } + + return null; + } + + /// + /// + /// + /// Return the object or child selection + /// + object IAccessible.accSelection { + get { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccSelection: this = " + + this.ToString()); + + AccessibleObject obj = GetSelected(); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accSelection; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + } + + /// + /// + /// + /// + /// Return the object or child state + /// + /// + object IAccessible.get_accState(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccState: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Return the state property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return (int)State; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return (int)child.State; + } + } + + if (systemIAccessible != null) { + return systemIAccessible.get_accState(childID); + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child value + /// + /// + string IAccessible.get_accValue(object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the value property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Value; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Value; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accValue(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Set the object or child name + /// + /// + void IAccessible.set_accName( + Object childID, + string newName) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Set the name property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + // Attempt to set the name property + Name = newName; + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Name = newName; + return; + } + } + + if (systemIAccessible != null) { + systemIAccessible.set_accName(childID, newName); + return; + } + } + + /// + /// + /// + /// + /// Set the object or child value + /// + /// + void IAccessible.set_accValue( + Object childID, + string newValue) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Set the value property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + // Attempt to set the value property + Value = newValue; + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Value = newValue; + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.set_accValue(childID, newValue); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + /// + /// + /// [Microsoft - Apr04] + /// + /// Now that AccessibleObject is used to wrap all system-provided (OLEACC.DLL) accessible + /// objects, it needs to implement IOleWindow and pass this down to the inner object. This is + /// necessary because the OS function WindowFromAccessibleObject() walks up the parent chain + /// looking for the first object that implements IOleWindow, and uses that to get the hwnd. + /// + /// But this creates a new problem for AccessibleObjects that do NOT have windows, ie. which + /// represent simple elements. To the OS, these simple elements will now appear to implement + /// IOleWindow, so it will try to get hwnds from them - which they simply cannot provide. + /// + /// To work around this problem, the AccessibleObject for a simple element will delegate all + /// IOleWindow calls up the parent chain itself. This will stop at the first window-based + /// accessible object, which will be able to return an hwnd back to the OS. So we are + /// effectively 'preempting' what WindowFromAccessibleObject() would do. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + // See if we have an inner object that can provide the window handle + if (systemIOleWindow != null) { + return systemIOleWindow.GetWindow(out hwnd); + } + + // Otherwise delegate to the parent object + AccessibleObject parent = this.Parent; + if (parent is UnsafeNativeMethods.IOleWindow) { + return (parent as UnsafeNativeMethods.IOleWindow).GetWindow(out hwnd); + } + + // Or fail if there is no parent + hwnd = IntPtr.Zero; + return NativeMethods.E_FAIL; + } + + /// + /// + /// See GetWindow() above for details. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + // See if we have an inner object that can provide help + if (systemIOleWindow != null) { + systemIOleWindow.ContextSensitiveHelp(fEnterMode); + return; + } + + // Otherwise delegate to the parent object + AccessibleObject parent = this.Parent; + if (parent is UnsafeNativeMethods.IOleWindow) { + (parent as UnsafeNativeMethods.IOleWindow).ContextSensitiveHelp(fEnterMode); + return; + } + + // Or do nothing if there is no parent + } + + /// + /// + /// + /// Clone this accessible object. + /// + /// + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + EnumVariant.Clone(v); + } + + /// + /// + /// + /// Obtain the next n children of this accessible object. + /// + /// + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + return EnumVariant.Next(n, rgvar, ns); + } + + /// + /// + /// Resets the child accessible object enumerator. + /// + void UnsafeNativeMethods.IEnumVariant.Reset() { + EnumVariant.Reset(); + } + + /// + /// + /// + /// Skip the next n child accessible objects + /// + /// + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + EnumVariant.Skip(n); + } + + /// + /// + /// When overridden in a derived class, + /// navigates to another object. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual AccessibleObject Navigate(AccessibleNavigation navdir) { + + // Some default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + switch(navdir) { + case AccessibleNavigation.FirstChild: + return GetChild(0); + case AccessibleNavigation.LastChild: + return GetChild(GetChildCount() - 1); + case AccessibleNavigation.Previous: + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + if (Parent.GetChildCount() > 0) { + return null; + } + break; + case AccessibleNavigation.Next: + case AccessibleNavigation.Down: + case AccessibleNavigation.Right: + if (Parent.GetChildCount() > 0) { + return null; + } + break; + } + } + + if (systemIAccessible != null) { + try { + object retObject = null; + if (!SysNavigate((int)navdir, NativeMethods.CHILDID_SELF, out retObject)) + retObject = systemIAccessible.accNavigate((int)navdir, NativeMethods.CHILDID_SELF); + return WrapIAccessible(retObject); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + + /// + /// + /// + /// Selects this accessible object. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual void Select(AccessibleSelection flags) { + + // By default, do the system behavior + // + if (systemIAccessible != null) { + try { + systemIAccessible.accSelect((int)flags, 0); + } + catch (COMException e) { + // Not all objects provide the select function + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + private object AsVariant(AccessibleObject obj) { + if (obj == this) { + return NativeMethods.CHILDID_SELF; + } + return AsIAccessible(obj); + } + + private IAccessible AsIAccessible(AccessibleObject obj) { + if (obj != null && obj.systemWrapper) { + return obj.systemIAccessible; + } + return obj; + } + + /// + /// + /// Indicates what kind of 'inner' system accessible object we are using as our fall-back + /// implementation of IAccessible (when the systemIAccessible member is not null). The inner + /// object is provided by OLEACC.DLL. Note that although the term 'id' is used, this value + /// really represents a category or type of accessible object. Ids are only unique among + /// accessible objects associated with the same window handle. Currently supported ids are... + /// + /// OBJID_CLIENT - represents the window's client area (including any child windows) + /// OBJID_WINDOW - represents the window's non-client area (including caption, frame controls and scrollbars) + /// + /// NOTE: When the id is OBJID_WINDOW, we short-circuit most of the virtual override behavior of + /// AccessibleObject, and turn the object into a simple wrapper around the inner system object. So + /// for a *user-defined* accessible object, that has NO inner object, its important that the id is + /// left as OBJID_CLIENT, otherwise the object will be short-circuited into a total NOP! + /// + /// + internal int AccessibleObjectId { + get { + return accObjId; + } + set { + accObjId = value; + } + } + + /// + /// + /// Indicates whether this accessible object represents the client area of the window. + /// + /// + internal bool IsClientObject { + get { + return AccessibleObjectId == NativeMethods.OBJID_CLIENT; + } + } + + /// + /// + /// Indicates whether this accessible object represents the non-client area of the window. + /// + /// + internal bool IsNonClientObject { + get { + return AccessibleObjectId == NativeMethods.OBJID_WINDOW; + } + } + + /// + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal IAccessible GetSystemIAccessibleInternal() { + return this.systemIAccessible; + } + + /// + /// + /// [To be supplied.] + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected void UseStdAccessibleObjects(IntPtr handle) { + UseStdAccessibleObjects(handle, AccessibleObjectId); + } + + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected void UseStdAccessibleObjects(IntPtr handle, int objid) { + // Get a standard accessible Object + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + object acc = null; + int result = UnsafeNativeMethods.CreateStdAccessibleObject( + new HandleRef(this, handle), + objid, + ref IID_IAccessible, + ref acc); + + // Get the IEnumVariant interface + Guid IID_IEnumVariant = new Guid(NativeMethods.uuid_IEnumVariant); + object en = null; + result = UnsafeNativeMethods.CreateStdAccessibleObject( + new HandleRef(this, handle), + objid, + ref IID_IEnumVariant, + ref en); + + Debug.Assert(acc != null, "SystemIAccessible is null"); + Debug.Assert(en != null, "SystemIEnumVariant is null"); + + if (acc != null || en != null) { + systemIAccessible = (IAccessible)acc; + systemIEnumVariant = (UnsafeNativeMethods.IEnumVariant)en; + systemIOleWindow = acc as UnsafeNativeMethods.IOleWindow; + } + } + + /// + /// Performs custom navigation between parent/child/sibling accessible objects. This is basically + /// just a wrapper for GetSysChild(), that does some of the dirty work, such as wrapping the + /// returned object in a VARIANT. Usage is similar to GetSysChild(). Called prior to calling + /// IAccessible.accNavigate on the 'inner' system accessible object. + /// + /// + private bool SysNavigate(int navDir, Object childID, out Object retObject) { + retObject = null; + + // Only override system navigation relative to ourselves (since we can't interpret OLEACC child ids) + if (!childID.Equals(NativeMethods.CHILDID_SELF)) + return false; + + // Perform any supported navigation operation (fall back on system for unsupported navigation ops) + AccessibleObject newObject; + if (!GetSysChild((AccessibleNavigation) navDir, out newObject)) + return false; + + // If object found, wrap in a VARIANT. Otherwise return null for 'end of list' (OLEACC expects this) + retObject = (newObject == null) ? null : AsVariant(newObject); + + // Tell caller not to fall back on system behavior now + return true; + } + + /// + /// + /// Make sure that the childID is valid. + /// + internal void ValidateChildID(ref object childID) { + // An empty childID is considered to be the same as CHILDID_SELF. + // Some accessibility programs pass null into our functions, so we + // need to convert them here. + if (childID == null) { + childID = NativeMethods.CHILDID_SELF; + } + else if (childID.Equals(NativeMethods.DISP_E_PARAMNOTFOUND)) { + childID = 0; + } + else if (!(childID is Int32)) { + // AccExplorer seems to occasionally pass in objects instead of an int ChildID. + // + childID = 0; + } + } + + private AccessibleObject WrapIAccessible(object iacc) { + IAccessible accessible = iacc as IAccessible; + if (accessible == null) { + return null; + } + + // Check to see if this object already wraps iacc + // + if (this.systemIAccessible == iacc) { + return this; + } + + return new AccessibleObject(accessible); + } + + /// + /// + /// + /// Return the requested method if it is implemented by the Reflection object. The + /// match is based upon the name and DescriptorInfo which describes the signature + /// of the method. + /// + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { + return typeof(IAccessible).GetMethod(name, bindingAttr, binder, types, modifiers); + } + + /// + /// + /// + /// Return the requested method if it is implemented by the Reflection object. The + /// match is based upon the name of the method. If the object implementes multiple methods + /// with the same name an AmbiguousMatchException is thrown. + /// + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetMethod(name, bindingAttr); + } + + /// + /// + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return typeof(IAccessible).GetMethods(bindingAttr); + } + + /// + /// + /// + /// Return the requestion field if it is implemented by the Reflection object. The + /// match is based upon a name. There cannot be more than a single field with + /// a name. + /// + FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetField(name, bindingAttr); + } + + /// + /// + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return typeof(IAccessible).GetFields(bindingAttr); + } + + /// + /// + /// + /// Return the property based upon name. If more than one property has the given + /// name an AmbiguousMatchException will be thrown. Returns null if no property + /// is found. + /// + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetProperty(name, bindingAttr); + } + + /// + /// + /// + /// Return the property based upon the name and Descriptor info describing the property + /// indexing. Return null if no property is found. + /// + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return typeof(IAccessible).GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + /// + /// + /// + /// Returns an array of PropertyInfos for all the properties defined on + /// the Reflection object. + /// + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return typeof(IAccessible).GetProperties(bindingAttr); + } + + /// + /// + /// + /// Return an array of members which match the passed in name. + /// + MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetMember(name, bindingAttr); + } + + /// + /// + /// + /// Return an array of all of the members defined for this object. + /// + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return typeof(IAccessible).GetMembers(bindingAttr); + } + + /// + /// + /// + /// Description of the Binding Process. + /// We must invoke a method that is accessable and for which the provided + /// parameters have the most specific match. A method may be called if + /// 1. The number of parameters in the method declaration equals the number of + /// arguments provided to the invocation + /// 2. The type of each argument can be converted by the binder to the + /// type of the type of the parameter. + /// + /// The binder will find all of the matching methods. These method are found based + /// upon the type of binding requested (MethodInvoke, Get/Set Properties). The set + /// of methods is filtered by the name, number of arguments and a set of search modifiers + /// defined in the Binder. + /// + /// After the method is selected, it will be invoked. Accessability is checked + /// at that point. The search may be control which set of methods are searched based + /// upon the accessibility attribute associated with the method. + /// + /// The BindToMethod method is responsible for selecting the method to be invoked. + /// For the default binder, the most specific method will be selected. + /// + /// This will invoke a specific member... + /// @exception If invokeAttr is CreateInstance then all other + /// Access types must be undefined. If not we throw an ArgumentException. + /// @exception If the invokeAttr is not CreateInstance then an + /// ArgumentException when name is null. + /// @exception ArgumentException when invokeAttr does not specify the type + /// @exception ArgumentException when invokeAttr specifies both get and set of + /// a property or field. + /// @exception ArgumentException when invokeAttr specifies property set and + /// invoke method. + /// + object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { + + if (args.Length == 0) { + MemberInfo[] member = typeof(IAccessible).GetMember(name); + if (member != null && member.Length > 0 && member[0] is PropertyInfo) { + MethodInfo getMethod = ((PropertyInfo)member[0]).GetGetMethod(); + if (getMethod != null && getMethod.GetParameters().Length > 0) { + args = new object[getMethod.GetParameters().Length]; + for (int i = 0; i < args.Length; i++) { + args[i] = NativeMethods.CHILDID_SELF; + } + } + } + } + return typeof(IAccessible).InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + + /// + /// + /// + /// Return the underlying Type that represents the IReflect Object. For expando object, + /// this is the (Object) IReflectInstance.GetType(). For Type object it is this. + /// + Type IReflect.UnderlyingSystemType { + get { + return typeof(IAccessible); + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd) { + return GetOverrideProviderForHwnd(hwnd); + } + + bool UnsafeNativeMethods.IRangeValueProvider.IsReadOnly { + get { + return IsReadOnly; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.LargeChange { + get { + return LargeChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Maximum { + get { + return Maximum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Minimum { + get { + return Minimum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.SmallChange { + get { + return SmallChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Value { + get { + return RangeValue; + } + } + + void UnsafeNativeMethods.IRangeValueProvider.SetValue(double value) { + SetValue(value); + } + + object[] UnsafeNativeMethods.ISelectionProvider.GetSelection() { + return GetSelection(); + } + + bool UnsafeNativeMethods.ISelectionProvider.CanSelectMultiple { + get { + return CanSelectMultiple; + } + } + + bool UnsafeNativeMethods.ISelectionProvider.IsSelectionRequired { + get { + return IsSelectionRequired; + } + } + + void UnsafeNativeMethods.ISelectionItemProvider.Select() { + SelectItem(); + } + + void UnsafeNativeMethods.ISelectionItemProvider.AddToSelection() { + AddToSelection(); + } + + void UnsafeNativeMethods.ISelectionItemProvider.RemoveFromSelection() { + RemoveFromSelection(); + } + + bool UnsafeNativeMethods.ISelectionItemProvider.IsSelected { + get { + return IsItemSelected; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.ISelectionItemProvider.SelectionContainer { + get { + return ItemSelectionContainer; + } + } + + /// + /// Raises the UIA Notification event. + /// The event is available starting with Windows 10, version 1709. + /// + /// The type of notification + /// Indicates how to process notifications + /// Notification text + /// + /// True if operation succeeds. + /// False if the underlying windows infrastructure is not available or the operation had failed. + /// Use Marshal.GetLastWin32Error for details. + /// + public bool RaiseAutomationNotification( + AutomationNotificationKind notificationKind, + AutomationNotificationProcessing notificationProcessing, + string notificationText) { + + if (!AccessibilityImprovements.Level3 || !notificationEventAvailable) { + return false; + } + + int result = NativeMethods.S_FALSE; + try { + // The activityId can be any string. It cannot be null. It isn�t used currently. + result = UnsafeNativeMethods.UiaRaiseNotificationEvent( + this, + notificationKind, + notificationProcessing, + notificationText, + String.Empty); + } + catch (EntryPointNotFoundException) { + // The UIA Notification event is not available, so don't attempt to raise it again. + notificationEventAvailable = false; + } + + return result == NativeMethods.S_OK; + } + + /// + /// Raises the LiveRegionChanged UIA event. + /// This method must be overridden in derived classes that support the UIA live region feature. + /// + /// True if operation succeeds, False otherwise. + public virtual bool RaiseLiveRegionChanged() { + throw new NotSupportedException(SR.GetString(SR.AccessibleObjectLiveRegionNotSupported)); + } + + internal bool RaiseAutomationEvent(int eventId) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseAutomationEvent(this, eventId); + return result == NativeMethods.S_OK; + } + + return false; + } + + internal bool RaiseAutomationPropertyChangedEvent(int propertyId, object oldValue, object newValue) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseAutomationPropertyChangedEvent(this, propertyId, oldValue, newValue); + return result == NativeMethods.S_OK; + } + + return false; + } + + internal bool RaiseStructureChangedEvent(UnsafeNativeMethods.StructureChangeType structureChangeType, int[] runtimeId) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseStructureChangedEvent(this, structureChangeType, runtimeId, runtimeId == null ? 0 : runtimeId.Length); + return result == NativeMethods.S_OK; + } + + return false; + } + + #region IScrollItemProvider implementation + + void UnsafeNativeMethods.IScrollItemProvider.ScrollIntoView() { + ScrollIntoView(); + } + + internal virtual void ScrollIntoView() { + Debug.Fail($"{nameof(this.ScrollIntoView)}() is not overriden"); + } + + #endregion + + private class EnumVariantObject : UnsafeNativeMethods.IEnumVariant { + + private int currentChild = 0; + private AccessibleObject owner; + + public EnumVariantObject(AccessibleObject owner) { + Debug.Assert(owner != null, "Cannot create EnumVariantObject with a null owner"); + this.owner = owner; + } + + public EnumVariantObject(AccessibleObject owner, int currentChild) { + Debug.Assert(owner != null, "Cannot create EnumVariantObject with a null owner"); + this.owner = owner; + this.currentChild = currentChild; + } + + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + v[0] = new EnumVariantObject(owner, currentChild); + } + + /// + /// + /// Resets the child accessible object enumerator. + /// + void UnsafeNativeMethods.IEnumVariant.Reset() { + currentChild = 0; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + if (owner.systemIEnumVariant != null) + owner.systemIEnumVariant.Reset(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// Skips the next n child accessible objects. + /// + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + currentChild += n; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + if (owner.systemIEnumVariant != null) + owner.systemIEnumVariant.Skip(n); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// Gets the next n child accessible objects. + /// + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + // NOTE: rgvar is a pointer to an array of variants + + if (owner.IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "EnumVariantObject: owner = " + owner.ToString() + ", n = " + n); + + Debug.Indent(); + + int childCount; + int[] newOrder; + + if ((childCount = owner.GetChildCount()) >= 0) + NextFromChildCollection(n, rgvar, ns, childCount); + else if (owner.systemIEnumVariant == null) + NextEmpty(n, rgvar, ns); + else if ((newOrder = owner.GetSysChildOrder()) != null) + NextFromSystemReordered(n, rgvar, ns, newOrder); + else + NextFromSystem(n, rgvar, ns); + + Debug.Unindent(); + } + else { + NextFromSystem(n, rgvar, ns); + } + + // Tell caller whether requested number of items was returned. Once list of items has + // been exhausted, we return S_FALSE so that caller knows to stop calling this method. + return (ns[0] == n) ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + + /// + /// When we have the IEnumVariant of an accessible proxy provided by the system (ie. + /// OLEACC.DLL), we can fall back on that to return the children. Generally, the system + /// proxy will enumerate the child windows, create a suitable kind of child accessible + /// proxy for each one, and return a set of IDispatch interfaces to these proxy objects. + /// + private void NextFromSystem(int n, IntPtr rgvar, int[] ns) { + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + owner.systemIEnumVariant.Next(n, rgvar, ns); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + currentChild += ns[0]; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: Delegating to systemIEnumVariant"); + } + + /// + /// Sometimes we want to rely on the system-provided behavior to create and return child + /// accessible objects, but we want to impose a new order on those objects (or even filter + /// some objects out). + /// + /// This method takes an array of ints that dictates the new order. It queries the system + /// for each child individually, and inserts the result into the correct *new* position. + /// + /// Note: This code has to make certain *assumptions* about OLEACC.DLL proxy object behavior. + /// However, this behavior is well documented. We *assume* the proxy will return a set of + /// child accessible objects that correspond 1:1 with the owning control's child windows, + /// and that the default order it returns these objects in is z-order (which also happens + /// to be the order that children appear in the Control.Controls[] collection). + /// + private void NextFromSystemReordered(int n, IntPtr rgvar, int[] ns, int[] newOrder) { + int i; + + for (i = 0; i < n && currentChild < newOrder.Length; ++i) { + if (!GotoItem(owner.systemIEnumVariant, newOrder[currentChild], GetAddressOfVariantAtIndex(rgvar, i))) + break; + ++currentChild; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: adding sys child " + currentChild + " of " + newOrder.Length); + } + + ns[0] = i; + } + + /// + /// If we have our own custom accessible child collection, return a set of 1-based integer + /// child ids, that the caller will eventually pass back to us via IAccessible.get_accChild(). + /// + private void NextFromChildCollection(int n, IntPtr rgvar, int[] ns, int childCount) { + int i; + + for (i = 0; i < n && currentChild < childCount; ++i) { + ++currentChild; + Marshal.GetNativeVariantForObject(((object) currentChild), GetAddressOfVariantAtIndex(rgvar, i)); + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: adding own child " + currentChild + " of " + childCount); + } + + ns[0] = i; + } + + /// + /// Default behavior if there is no custom child collection or system-provided + /// proxy to fall back on. In this case, we return an empty child collection. + /// + private void NextEmpty(int n, IntPtr rgvar, int[] ns) { + ns[0] = 0; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: no children to add"); + } + + /// + /// Given an IEnumVariant interface, this method jumps to a specific + /// item in the collection and extracts the result for that one item. + /// + private static bool GotoItem(UnsafeNativeMethods.IEnumVariant iev, int index, IntPtr variantPtr) { + int[] ns = new int[1]; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + iev.Reset(); + iev.Skip(index); + iev.Next(1, variantPtr, ns); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return (ns[0] == 1); + } + + /// + /// Given an array of pointers to variants, calculate address of a given array element. + /// + private static IntPtr GetAddressOfVariantAtIndex(IntPtr variantArrayPtr, int index) { + int variantSize = 8 + (IntPtr.Size * 2); + return (IntPtr) ((ulong) variantArrayPtr + ((ulong) index) * ((ulong) variantSize)); + } + + } + + } // end class AccessibleObject + + /// + /// Internal object passed out to OLEACC clients via WM_GETOBJECT. + /// NOTE: THIS CLASS IS INTERNAL FOR SECURITY REASONS AND SHOULD REMAIN SO! + /// + internal sealed class InternalAccessibleObject : StandardOleMarshalObject, + UnsafeNativeMethods.IAccessibleInternal, + System.Reflection.IReflect, + UnsafeNativeMethods.IServiceProvider, + UnsafeNativeMethods.IAccessibleEx, + UnsafeNativeMethods.IRawElementProviderSimple, + UnsafeNativeMethods.IRawElementProviderFragment, + UnsafeNativeMethods.IRawElementProviderFragmentRoot, + UnsafeNativeMethods.IInvokeProvider, + UnsafeNativeMethods.IValueProvider, + UnsafeNativeMethods.IRangeValueProvider, + UnsafeNativeMethods.IExpandCollapseProvider, + UnsafeNativeMethods.IToggleProvider, + UnsafeNativeMethods.ITableProvider, + UnsafeNativeMethods.ITableItemProvider, + UnsafeNativeMethods.IGridProvider, + UnsafeNativeMethods.IGridItemProvider, + UnsafeNativeMethods.IEnumVariant, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.ILegacyIAccessibleProvider, + UnsafeNativeMethods.ISelectionProvider, + UnsafeNativeMethods.ISelectionItemProvider, + UnsafeNativeMethods.IRawElementProviderHwndOverride { + + private IAccessible publicIAccessible; // AccessibleObject as IAccessible + private UnsafeNativeMethods.IEnumVariant publicIEnumVariant; // AccessibleObject as IEnumVariant + private UnsafeNativeMethods.IOleWindow publicIOleWindow; // AccessibleObject as IOleWindow + private IReflect publicIReflect; // AccessibleObject as IReflect + + private UnsafeNativeMethods.IServiceProvider publicIServiceProvider; // AccessibleObject as IServiceProvider + private UnsafeNativeMethods.IAccessibleEx publicIAccessibleEx; // AccessibleObject as IAccessibleEx + + // UIAutomation + private UnsafeNativeMethods.IRawElementProviderSimple publicIRawElementProviderSimple; // AccessibleObject as IRawElementProviderSimple + private UnsafeNativeMethods.IRawElementProviderFragment publicIRawElementProviderFragment;// AccessibleObject as IRawElementProviderFragment + private UnsafeNativeMethods.IRawElementProviderFragmentRoot publicIRawElementProviderFragmentRoot;// AccessibleObject as IRawElementProviderFragmentRoot + private UnsafeNativeMethods.IInvokeProvider publicIInvokeProvider; // AccessibleObject as IInvokeProvider + private UnsafeNativeMethods.IValueProvider publicIValueProvider; // AccessibleObject as IValueProvider + private UnsafeNativeMethods.IRangeValueProvider publicIRangeValueProvider; // AccessibleObject as IRangeValueProvider + private UnsafeNativeMethods.IExpandCollapseProvider publicIExpandCollapseProvider; // AccessibleObject as IExpandCollapseProvider + private UnsafeNativeMethods.IToggleProvider publicIToggleProvider; // AccessibleObject as IToggleProvider + private UnsafeNativeMethods.ITableProvider publicITableProvider; // AccessibleObject as ITableProvider + private UnsafeNativeMethods.ITableItemProvider publicITableItemProvider; // AccessibleObject as ITableItemProvider + private UnsafeNativeMethods.IGridProvider publicIGridProvider; // AccessibleObject as IGridProvider + private UnsafeNativeMethods.IGridItemProvider publicIGridItemProvider; // AccessibleObject as IGridItemProvider + private UnsafeNativeMethods.ILegacyIAccessibleProvider publicILegacyIAccessibleProvider; // AccessibleObject as ILegayAccessibleProvider + private UnsafeNativeMethods.ISelectionProvider publicISelectionProvider; // AccessibleObject as ISelectionProvider + private UnsafeNativeMethods.ISelectionItemProvider publicISelectionItemProvider; // AccessibleObject as ISelectionItemProvider + private UnsafeNativeMethods.IRawElementProviderHwndOverride publicIRawElementProviderHwndOverride; // AccessibleObject as IRawElementProviderHwndOverride + + /// + /// Create a new wrapper. Protect this with UnmanagedCode Permission + /// + [ + SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + internal InternalAccessibleObject(AccessibleObject accessibleImplemention) { + // Get all the casts done here to catch any issues early + publicIAccessible = (IAccessible) accessibleImplemention; + publicIEnumVariant = (UnsafeNativeMethods.IEnumVariant) accessibleImplemention; + publicIOleWindow = (UnsafeNativeMethods.IOleWindow) accessibleImplemention; + publicIReflect = (IReflect) accessibleImplemention; + publicIServiceProvider = (UnsafeNativeMethods.IServiceProvider) accessibleImplemention; + publicIAccessibleEx = (UnsafeNativeMethods.IAccessibleEx) accessibleImplemention; + publicIRawElementProviderSimple = (UnsafeNativeMethods.IRawElementProviderSimple) accessibleImplemention; + publicIRawElementProviderFragment = (UnsafeNativeMethods.IRawElementProviderFragment)accessibleImplemention; + publicIRawElementProviderFragmentRoot = (UnsafeNativeMethods.IRawElementProviderFragmentRoot)accessibleImplemention; + publicIInvokeProvider = (UnsafeNativeMethods.IInvokeProvider)accessibleImplemention; + publicIValueProvider = (UnsafeNativeMethods.IValueProvider) accessibleImplemention; + publicIRangeValueProvider = (UnsafeNativeMethods.IRangeValueProvider)accessibleImplemention; + publicIExpandCollapseProvider = (UnsafeNativeMethods.IExpandCollapseProvider) accessibleImplemention; + publicIToggleProvider = (UnsafeNativeMethods.IToggleProvider)accessibleImplemention; + publicITableProvider = (UnsafeNativeMethods.ITableProvider)accessibleImplemention; + publicITableItemProvider = (UnsafeNativeMethods.ITableItemProvider)accessibleImplemention; + publicIGridProvider = (UnsafeNativeMethods.IGridProvider)accessibleImplemention; + publicIGridItemProvider = (UnsafeNativeMethods.IGridItemProvider)accessibleImplemention; + publicILegacyIAccessibleProvider = (UnsafeNativeMethods.ILegacyIAccessibleProvider)accessibleImplemention; + publicISelectionProvider = (UnsafeNativeMethods.ISelectionProvider)accessibleImplemention; + publicISelectionItemProvider = (UnsafeNativeMethods.ISelectionItemProvider)accessibleImplemention; + publicIRawElementProviderHwndOverride = (UnsafeNativeMethods.IRawElementProviderHwndOverride)accessibleImplemention; + // Note: Deliberately not holding onto AccessibleObject to enforce all access through the interfaces + } + + /// + /// If the given object is an AccessibleObject return it as a InternalAccessibleObject + /// This ensures we wrap all AccessibleObjects before handing them out to OLEACC + /// + private object AsNativeAccessible(object accObject) { + if (accObject is AccessibleObject) { + return new InternalAccessibleObject(accObject as AccessibleObject); + } + else { + return accObject; + } + } + + /// + /// Wraps AccessibleObject elements of a given array into InternalAccessibleObjects + /// + private object[] AsArrayOfNativeAccessibles(object[] accObjectArray) { + if (accObjectArray != null && accObjectArray.Length > 0) { + for (int i = 0; i < accObjectArray.Length; i++) { + accObjectArray[i] = AsNativeAccessible(accObjectArray[i]); + } + } + return accObjectArray; + } + + // + // IAccessibleInternal implementation... + // + + void UnsafeNativeMethods.IAccessibleInternal.accDoDefaultAction(object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accDoDefaultAction(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.accHitTest(int xLeft, int yTop) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accHitTest(xLeft, yTop)); + } + + void UnsafeNativeMethods.IAccessibleInternal.accLocation(out int l, out int t, out int w, out int h, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accLocation(out l, out t, out w, out h, childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.accNavigate(int navDir, object childID) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accNavigate(navDir, childID)); + } + + void UnsafeNativeMethods.IAccessibleInternal.accSelect(int flagsSelect, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accSelect(flagsSelect, childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accChild(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.get_accChild(childID)); + } + + int UnsafeNativeMethods.IAccessibleInternal.get_accChildCount() { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.accChildCount; + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accDefaultAction(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accDefaultAction(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accDescription(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accDescription(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accFocus() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accFocus); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accHelp(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accHelp(childID); + } + + int UnsafeNativeMethods.IAccessibleInternal.get_accHelpTopic(out string pszHelpFile, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accHelpTopic(out pszHelpFile, childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accKeyboardShortcut(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accKeyboardShortcut(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accName(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accName(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accParent() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accParent); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accRole(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accRole(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accSelection() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accSelection); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accState(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accState(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accValue(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accValue(childID); + } + + void UnsafeNativeMethods.IAccessibleInternal.set_accName(Object childID, string newName) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.set_accName(childID, newName); + } + + void UnsafeNativeMethods.IAccessibleInternal.set_accValue(Object childID, string newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.set_accValue(childID, newValue); + } + + // + // IEnumVariant implementation... + // + + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Clone(v); + } + + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + IntSecurity.UnmanagedCode.Assert(); + return publicIEnumVariant.Next(n, rgvar, ns); + } + + void UnsafeNativeMethods.IEnumVariant.Reset() { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Reset(); + } + + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Skip(n); + } + + // + // IOleWindow implementation... + // + + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + IntSecurity.UnmanagedCode.Assert(); + return publicIOleWindow.GetWindow(out hwnd); + } + + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + IntSecurity.UnmanagedCode.Assert(); + publicIOleWindow.ContextSensitiveHelp(fEnterMode); + } + + // + // IReflect implementation... + // + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { + return publicIReflect.GetMethod(name, bindingAttr, binder, types, modifiers); + } + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) { + return publicIReflect.GetMethod(name, bindingAttr); + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return publicIReflect.GetMethods(bindingAttr); + } + + FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) { + return publicIReflect.GetField(name, bindingAttr); + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return publicIReflect.GetFields(bindingAttr); + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) { + return publicIReflect.GetProperty(name, bindingAttr); + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return publicIReflect.GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return publicIReflect.GetProperties(bindingAttr); + } + + MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) { + return publicIReflect.GetMember(name, bindingAttr); + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return publicIReflect.GetMembers(bindingAttr); + } + + object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { + IntSecurity.UnmanagedCode.Demand(); + return publicIReflect.InvokeMember(name, invokeAttr, binder, publicIAccessible, args, modifiers, culture, namedParameters); + } + + Type IReflect.UnderlyingSystemType { + get { + IReflect r = publicIReflect; + return publicIReflect.UnderlyingSystemType; + } + } + + // + // IServiceProvider implementation + // + + int UnsafeNativeMethods.IServiceProvider.QueryService(ref Guid service, ref Guid riid, out IntPtr ppvObject) { + IntSecurity.UnmanagedCode.Assert(); + + ppvObject = IntPtr.Zero; + int hr = publicIServiceProvider.QueryService(ref service, ref riid, out ppvObject); + if (hr >= NativeMethods.S_OK) { + // we always want to return the internal accessible object + ppvObject = Marshal.GetComInterfaceForObject(this, typeof(UnsafeNativeMethods.IAccessibleEx)); + } + + return hr; + } + + // + // IAccessibleEx implementation + // + + object UnsafeNativeMethods.IAccessibleEx.GetObjectForChild(int idChild) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.GetObjectForChild(idChild); + } + + int UnsafeNativeMethods.IAccessibleEx.GetIAccessiblePair(out object ppAcc, out int pidChild) { + + IntSecurity.UnmanagedCode.Assert(); + + // We always want to return the internal accessible object + ppAcc = this; + pidChild = NativeMethods.CHILDID_SELF; + return NativeMethods.S_OK; + } + + int[] UnsafeNativeMethods.IAccessibleEx.GetRuntimeId() { + + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.GetRuntimeId(); + } + + int UnsafeNativeMethods.IAccessibleEx.ConvertReturnedElement(object pIn, out object ppRetValOut) { + + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.ConvertReturnedElement(pIn, out ppRetValOut); + } + + // + // IRawElementProviderSimple implementation + // + + UnsafeNativeMethods.ProviderOptions UnsafeNativeMethods.IRawElementProviderSimple.ProviderOptions { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.ProviderOptions; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderSimple.HostRawElementProvider { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.HostRawElementProvider; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPatternProvider(int patternId) { + IntSecurity.UnmanagedCode.Assert(); + + object obj = publicIRawElementProviderSimple.GetPatternProvider(patternId); + if (obj != null) { + + // we always want to return the internal accessible object + + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + return (UnsafeNativeMethods.IExpandCollapseProvider)this; + } + else if (patternId == NativeMethods.UIA_ValuePatternId) { + return (UnsafeNativeMethods.IValueProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_RangeValuePatternId) { + return (UnsafeNativeMethods.IRangeValueProvider)this; + } + else if (patternId == NativeMethods.UIA_TogglePatternId) { + return (UnsafeNativeMethods.IToggleProvider)this; + } + else if (patternId == NativeMethods.UIA_TablePatternId) { + return (UnsafeNativeMethods.ITableProvider)this; + } + else if (patternId == NativeMethods.UIA_TableItemPatternId) { + return (UnsafeNativeMethods.ITableItemProvider)this; + } + else if (patternId == NativeMethods.UIA_GridPatternId) { + return (UnsafeNativeMethods.IGridProvider)this; + } + else if (patternId == NativeMethods.UIA_GridItemPatternId) { + return (UnsafeNativeMethods.IGridItemProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_InvokePatternId) { + return (UnsafeNativeMethods.IInvokeProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_LegacyIAccessiblePatternId) { + return (UnsafeNativeMethods.ILegacyIAccessibleProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_SelectionPatternId) { + return (UnsafeNativeMethods.ISelectionProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_SelectionItemPatternId) { + return (UnsafeNativeMethods.ISelectionItemProvider)this; + } + else { + return null; + } + } + else { + return null; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPropertyValue(int propertyID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.GetPropertyValue(propertyID); + } + + // + // IRawElementProviderFragment implementation + // + + object UnsafeNativeMethods.IRawElementProviderFragment.Navigate(UnsafeNativeMethods.NavigateDirection direction) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragment.Navigate(direction)); + } + + int[] UnsafeNativeMethods.IRawElementProviderFragment.GetRuntimeId() { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderFragment.GetRuntimeId(); + } + + object[] UnsafeNativeMethods.IRawElementProviderFragment.GetEmbeddedFragmentRoots() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicIRawElementProviderFragment.GetEmbeddedFragmentRoots()); + } + + void UnsafeNativeMethods.IRawElementProviderFragment.SetFocus() { + IntSecurity.UnmanagedCode.Assert(); + publicIRawElementProviderFragment.SetFocus(); + } + + NativeMethods.UiaRect UnsafeNativeMethods.IRawElementProviderFragment.BoundingRectangle { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderFragment.BoundingRectangle; + } + } + + UnsafeNativeMethods.IRawElementProviderFragmentRoot UnsafeNativeMethods.IRawElementProviderFragment.FragmentRoot { + get { + IntSecurity.UnmanagedCode.Assert(); + if (AccessibilityImprovements.Level3) { + return publicIRawElementProviderFragment.FragmentRoot; + } + + return AsNativeAccessible(publicIRawElementProviderFragment.FragmentRoot) as UnsafeNativeMethods.IRawElementProviderFragmentRoot; + } + } + + // + // IRawElementProviderFragmentRoot implementation + // + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragmentRoot.ElementProviderFromPoint(x, y)); + } + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.GetFocus() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragmentRoot.GetFocus()); + } + + // + // ILegacyIAccessibleProvider implementation + // + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.DefaultAction { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.DefaultAction; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Description { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Description; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Help { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Help; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.KeyboardShortcut { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.KeyboardShortcut; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Name { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Name; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.Role { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Role; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.State { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.State; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Value; + } + } + + int UnsafeNativeMethods.ILegacyIAccessibleProvider.ChildId { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.ChildId; + } + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.DoDefaultAction() { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.DoDefaultAction(); + } + + IAccessible UnsafeNativeMethods.ILegacyIAccessibleProvider.GetIAccessible() { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.GetIAccessible(); + } + + object[] UnsafeNativeMethods.ILegacyIAccessibleProvider.GetSelection() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicILegacyIAccessibleProvider.GetSelection()); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.Select(int flagsSelect) { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.Select(flagsSelect); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.SetValue(string szValue) { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.SetValue(szValue); + } + + // + // IInvokeProvider implementation + // + + void UnsafeNativeMethods.IInvokeProvider.Invoke() { + IntSecurity.UnmanagedCode.Assert(); + publicIInvokeProvider.Invoke(); + } + + // + // IValueProvider implementation + // + + bool UnsafeNativeMethods.IValueProvider.IsReadOnly { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.IsReadOnly; + } + } + + string UnsafeNativeMethods.IValueProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.Value; + } + } + + void UnsafeNativeMethods.IValueProvider.SetValue(string newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIValueProvider.SetValue(newValue); + } + + // + // IRangeValueProvider implementation + // + + bool UnsafeNativeMethods.IRangeValueProvider.IsReadOnly { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.IsReadOnly; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.LargeChange { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.LargeChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Maximum { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Maximum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Minimum { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Minimum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.SmallChange { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.SmallChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Value; + } + } + + void UnsafeNativeMethods.IRangeValueProvider.SetValue(double newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIRangeValueProvider.SetValue(newValue); + } + + // + // IExpandCollapseProvider implementation + // + + void UnsafeNativeMethods.IExpandCollapseProvider.Expand() { + IntSecurity.UnmanagedCode.Assert(); + publicIExpandCollapseProvider.Expand(); + } + + void UnsafeNativeMethods.IExpandCollapseProvider.Collapse() { + IntSecurity.UnmanagedCode.Assert(); + publicIExpandCollapseProvider.Collapse(); + } + + UnsafeNativeMethods.ExpandCollapseState UnsafeNativeMethods.IExpandCollapseProvider.ExpandCollapseState { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIExpandCollapseProvider.ExpandCollapseState; + } + } + + // + // IToggleProvider implementation + // + + void UnsafeNativeMethods.IToggleProvider.Toggle() { + IntSecurity.UnmanagedCode.Assert(); + publicIToggleProvider.Toggle(); + } + + UnsafeNativeMethods.ToggleState UnsafeNativeMethods.IToggleProvider.ToggleState { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIToggleProvider.ToggleState; + } + } + + // + // ITableProvider implementation + // + + object[] UnsafeNativeMethods.ITableProvider.GetRowHeaders() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableProvider.GetRowHeaders()); + } + + object[] UnsafeNativeMethods.ITableProvider.GetColumnHeaders() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableProvider.GetColumnHeaders()); + } + + UnsafeNativeMethods.RowOrColumnMajor UnsafeNativeMethods.ITableProvider.RowOrColumnMajor { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicITableProvider.RowOrColumnMajor; + } + } + + // + // ITableItemProvider implementation + // + + object[] UnsafeNativeMethods.ITableItemProvider.GetRowHeaderItems() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableItemProvider.GetRowHeaderItems()); + } + + object[] UnsafeNativeMethods.ITableItemProvider.GetColumnHeaderItems() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableItemProvider.GetColumnHeaderItems()); + } + + // + // IGridProvider implementation + // + + object UnsafeNativeMethods.IGridProvider.GetItem(int row, int column) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIGridProvider.GetItem(row, column)); + } + + int UnsafeNativeMethods.IGridProvider.RowCount { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridProvider.RowCount; + } + } + + int UnsafeNativeMethods.IGridProvider.ColumnCount { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridProvider.ColumnCount; + } + } + + // + // IGridItemProvider implementation + // + + int UnsafeNativeMethods.IGridItemProvider.Row { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.Row; + } + } + + int UnsafeNativeMethods.IGridItemProvider.Column { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.Column; + } + } + + int UnsafeNativeMethods.IGridItemProvider.RowSpan { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.RowSpan; + } + } + + int UnsafeNativeMethods.IGridItemProvider.ColumnSpan { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.ColumnSpan; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IGridItemProvider.ContainingGrid { + get { + IntSecurity.UnmanagedCode.Assert(); + + // Do not wrap returned UIA provider by InternalAccessibleObject in Level 3. + if (AccessibilityImprovements.Level3) { + return publicIGridItemProvider.ContainingGrid; + } + + return AsNativeAccessible(publicIGridItemProvider.ContainingGrid) as UnsafeNativeMethods.IRawElementProviderSimple; + } + } + + // + // ISelectionProvider implementation + // + + /// + /// Get the currently selected elements + /// + /// An AutomationElement array containing the currently selected elements + object[] UnsafeNativeMethods.ISelectionProvider.GetSelection() { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.GetSelection(); + } + + /// + /// Indicates whether the control allows more than one element to be selected + /// + /// Boolean indicating whether the control allows more than one element to be selected + /// If this is false, then the control is a single-select ccntrol + bool UnsafeNativeMethods.ISelectionProvider.CanSelectMultiple { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.CanSelectMultiple; + } + } + + /// + /// Indicates whether the control requires at least one element to be selected + /// + /// Boolean indicating whether the control requires at least one element to be selected + /// If this is false, then the control allows all elements to be unselected + bool UnsafeNativeMethods.ISelectionProvider.IsSelectionRequired { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.IsSelectionRequired; + } + } + + // + // ISelectionItemProvider implementation + // + + /// + /// Sets the current element as the selection + /// This clears the selection from other elements in the container. + /// + void UnsafeNativeMethods.ISelectionItemProvider.Select() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.Select(); + } + + /// + /// Adds current element to selection. + /// + void UnsafeNativeMethods.ISelectionItemProvider.AddToSelection() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.AddToSelection(); + } + + /// + /// Removes current element from selection. + /// + void UnsafeNativeMethods.ISelectionItemProvider.RemoveFromSelection() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.RemoveFromSelection(); + } + + /// + /// Check whether an element is selected. + /// + /// Returns true if the element is selected. + bool UnsafeNativeMethods.ISelectionItemProvider.IsSelected { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionItemProvider.IsSelected; + } + } + + /// + /// The logical element that supports the SelectionPattern for this Item. + /// + /// Returns a IRawElementProviderSimple. + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.ISelectionItemProvider.SelectionContainer { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionItemProvider.SelectionContainer; + } + } + + // + // IRawElementProviderHwndOverride implementation + // + + /// + /// Request a provider for the specified component. The returned provider can supply additional + /// properties or override properties of the specified component. + /// + /// The window handle of the component. + /// Return the provider for the specified component, or null if the component is not being overridden. + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd) { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderHwndOverride.GetOverrideProviderForHwnd(hwnd); + } + + } // end class InternalAccessibleObject + +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleObject.cs.back b/WindowsForms/Managed/System/WinForms/AccessibleObject.cs.back new file mode 100644 index 000000000..dd9c805b1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleObject.cs.back @@ -0,0 +1,3927 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using Accessibility; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Collections; + using System.Drawing; + using System.Windows.Forms; + using Microsoft.Win32; + using System.ComponentModel; + using System.Reflection; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using Automation; + + /// + /// + /// Provides an implementation for an object that can be inspected by an + /// accessibility application. + /// + [ComVisible(true)] + public class AccessibleObject : StandardOleMarshalObject, + IReflect, + IAccessible, + UnsafeNativeMethods.IAccessibleEx, + UnsafeNativeMethods.IServiceProvider, + UnsafeNativeMethods.IRawElementProviderSimple, + UnsafeNativeMethods.IRawElementProviderFragment, + UnsafeNativeMethods.IRawElementProviderFragmentRoot, + UnsafeNativeMethods.IInvokeProvider, + UnsafeNativeMethods.IValueProvider, + UnsafeNativeMethods.IRangeValueProvider, + UnsafeNativeMethods.IExpandCollapseProvider, + UnsafeNativeMethods.IToggleProvider, + UnsafeNativeMethods.ITableProvider, + UnsafeNativeMethods.ITableItemProvider, + UnsafeNativeMethods.IGridProvider, + UnsafeNativeMethods.IGridItemProvider, + UnsafeNativeMethods.IEnumVariant, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.ILegacyIAccessibleProvider, + UnsafeNativeMethods.ISelectionProvider, + UnsafeNativeMethods.ISelectionItemProvider, + UnsafeNativeMethods.IRawElementProviderHwndOverride, + UnsafeNativeMethods.IScrollItemProvider { + + // Member variables + + /// + /// + /// Specifies the interface used by this . + /// + private IAccessible systemIAccessible = null; + + /// + /// + /// Specifies the + /// used by this . + /// + private UnsafeNativeMethods.IEnumVariant systemIEnumVariant = null; + private UnsafeNativeMethods.IEnumVariant enumVariant = null; + + // IOleWindow interface of the 'inner' system IAccessible object that we are wrapping + private UnsafeNativeMethods.IOleWindow systemIOleWindow = null; + + private bool systemWrapper = false; // Indicates this object is being used ONLY to wrap a system IAccessible + + private int accObjId = NativeMethods.OBJID_CLIENT; // Indicates what kind of 'inner' system accessible object we are using + + // The support for the UIA Notification event begins in RS3. + // Assume the UIA Notification event is available until we learn otherwise. + // If we learn that the UIA Notification event is not available, + // controls should not attempt to raise it. + private static bool notificationEventAvailable = true; + + internal const int RuntimeIDFirstItem = 0x2a; + + /// + public AccessibleObject() { + } + + // This constructor is used ONLY for wrapping system IAccessible objects + // + private AccessibleObject(IAccessible iAcc) { + this.systemIAccessible = iAcc; + this.systemWrapper = true; + } + + // Properties + + /// + /// + /// Gets the bounds of the accessible object, in screen coordinates. + /// + public virtual Rectangle Bounds { + get { + // Use the system provided bounds + if (systemIAccessible != null) { + int left = 0; + int top = 0; + int width = 0; + int height = 0; + try { + systemIAccessible.accLocation(out left, out top, out width, out height, NativeMethods.CHILDID_SELF); + return new Rectangle(left, top, width, height); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return Rectangle.Empty; + } + } + + /// + /// + /// Gets a description of the default action for an object. + /// + public virtual string DefaultAction { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDefaultAction(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets a description + /// of the object's visual appearance to the user. + /// + public virtual string Description { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDescription(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + private UnsafeNativeMethods.IEnumVariant EnumVariant { + get { + if (enumVariant == null) { + enumVariant = new EnumVariantObject(this); + } + return enumVariant; + } + } + + /// + /// + /// Gets a description of what the object does or how the object is used. + /// + public virtual string Help { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelp(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets the object shortcut key or access key + /// for an accessible object. + /// + public virtual string KeyboardShortcut { + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accKeyboardShortcut(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + } + + /// + /// + /// Gets + /// or sets the object name. + /// + public virtual string Name { + // Does nothing by default + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accName(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + + set { + if (systemIAccessible != null) { + try { + systemIAccessible.set_accName(NativeMethods.CHILDID_SELF, value); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + } + } + + /// + /// + /// When overridden in a derived class, gets or sets the parent of an accessible object. + /// + public virtual AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (systemIAccessible != null) { + return WrapIAccessible(systemIAccessible.accParent); + } + else { + return null; + } + } + } + + /// + /// + /// Gets the role of this accessible object. + /// + public virtual AccessibleRole Role { + get { + if (systemIAccessible != null) { + return (AccessibleRole) systemIAccessible.get_accRole(NativeMethods.CHILDID_SELF); + } + else { + return AccessibleRole.None; + } + } + } + + /// + /// + /// Gets + /// the state of this accessible object. + /// + public virtual AccessibleStates State { + get { + if (systemIAccessible != null) { + return (AccessibleStates) systemIAccessible.get_accState(NativeMethods.CHILDID_SELF); + } + else { + return AccessibleStates.None; + } + } + } + + /// + /// + /// Gets or sets the value of an accessible object. + /// + public virtual string Value { + // Does nothing by default + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accValue(NativeMethods.CHILDID_SELF); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return ""; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set { + if (systemIAccessible != null) { + try { + systemIAccessible.set_accValue(NativeMethods.CHILDID_SELF, value); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + } + } + + // Methods + + /// + /// + /// When overridden in a derived class, gets the accessible child corresponding to the specified + /// index. + /// + public virtual AccessibleObject GetChild(int index) { + return null; + } + + /// + /// + /// When overridden in a derived class, gets the number of children + /// belonging to an accessible object. + /// + public virtual int GetChildCount() { + return -1; + } + + /// + /// + /// Mechanism for overriding default IEnumVariant behavior of the 'inner' system accessible object + /// (IEnumVariant is how a system accessible object exposes its ordered list of child objects). + /// + /// USAGE: Overridden method in derived class should return array of integers representing new order + /// to be imposed on the child accessible object collection returned by the system (which + /// we assume will be a set of accessible objects that represent the child windows, in + /// z-order). Each array element contains the original z-order based rank of the child window + /// that is to appear at that position in the new ordering. Note: This array could also be + /// used to filter out unwanted child windows too, if necessary (not recommended). + /// + internal virtual int[] GetSysChildOrder() { + return null; + } + + /// + /// + /// Mechanism for overriding default IAccessible.accNavigate behavior of the 'inner' system accessible + /// object (accNavigate is how you move between parent, child and sibling accessible objects). + /// + /// USAGE: 'navdir' indicates navigation operation to perform, relative to this accessible object. + /// If operation is unsupported, return false to allow fall-back to default system behavior. Otherwise + /// return destination object in the out parameter, or null to indicate 'off end of list'. + /// + internal virtual bool GetSysChild(AccessibleNavigation navdir, out AccessibleObject accessibleObject) { + accessibleObject = null; + return false; + } + + /// + /// + /// When overridden in a derived class, + /// gets the object that has the keyboard focus. + /// + public virtual AccessibleObject GetFocused() { + + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && ((child.State & AccessibleStates.Focused) != 0)) { + return child; + } + } + if ((this.State & AccessibleStates.Focused) != 0) { + return this; + } + return null; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accFocus); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// Gets an identifier for a Help topic and the path to the Help file associated + /// with this accessible object. + /// + public virtual int GetHelpTopic(out string fileName) { + if (systemIAccessible != null) { + try { + int retVal = systemIAccessible.get_accHelpTopic(out fileName, NativeMethods.CHILDID_SELF); + if (fileName != null && fileName.Length > 0) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + return retVal; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + fileName = null; + return -1; + } + + /// + /// + /// When overridden in + /// a derived class, gets the currently selected child. + /// + public virtual AccessibleObject GetSelected() { + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && ((child.State & AccessibleStates.Selected) != 0)) { + return child; + } + } + if ((this.State & AccessibleStates.Selected) != 0) { + return this; + } + return null; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accSelection); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// Return the child object at the given screen coordinates. + /// + public virtual AccessibleObject HitTest(int x, int y) { + + // Default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + Debug.Assert(child != null, "GetChild(" + index.ToString(CultureInfo.InvariantCulture) + ") returned null!"); + if (child != null && child.Bounds.Contains(x, y)) { + return child; + } + } + return this; + } + + if (systemIAccessible != null) { + try { + return WrapIAccessible(systemIAccessible.accHitTest(x, y)); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + if (this.Bounds.Contains(x, y)) { + return this; + } + + return null; + } + + // + // UIAutomation support helpers + // + + internal virtual bool IsIAccessibleExSupported() { + // Override this, in your derived class, to enable IAccessibleEx support + return false; + } + + internal virtual bool IsPatternSupported(int patternId) { + // Override this, in your derived class, if you implement UIAutomation patterns + + if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_InvokePatternId) { + return IsInvokePatternAvailable; + } + + return false; + } + + // + // Overridable methods for IRawElementProviderSimple interface + // + + internal virtual int[] RuntimeId { + get { + return null; + } + } + + internal virtual int ProviderOptions { + get { + return (int)(UnsafeNativeMethods.ProviderOptions.ServerSideProvider | UnsafeNativeMethods.ProviderOptions.UseComThreading); + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + return null; + } + } + + internal virtual object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsInvokePatternAvailablePropertyId) { + return IsInvokePatternAvailable; + } + + return null; + } + + private bool IsInvokePatternAvailable { + get { + // MSAA Proxy determines the availability of invoke pattern based on Role/DefaultAction properties. + // Below code emulates the same rules. + switch (Role) { + case AccessibleRole.MenuItem: + case AccessibleRole.Link: + case AccessibleRole.PushButton: + case AccessibleRole.ButtonDropDown: + case AccessibleRole.ButtonMenu: + case AccessibleRole.ButtonDropDownGrid: + case AccessibleRole.Clock: + case AccessibleRole.SplitButton: + return true; + + case AccessibleRole.Default: + case AccessibleRole.None: + case AccessibleRole.Sound: + case AccessibleRole.Cursor: + case AccessibleRole.Caret: + case AccessibleRole.Alert: + case AccessibleRole.Client: + case AccessibleRole.Chart: + case AccessibleRole.Dialog: + case AccessibleRole.Border: + case AccessibleRole.Column: + case AccessibleRole.Row: + case AccessibleRole.HelpBalloon: + case AccessibleRole.Character: + case AccessibleRole.PageTab: + case AccessibleRole.PropertyPage: + case AccessibleRole.DropList: + case AccessibleRole.Dial: + case AccessibleRole.HotkeyField: + case AccessibleRole.Diagram: + case AccessibleRole.Animation: + case AccessibleRole.Equation: + case AccessibleRole.WhiteSpace: + case AccessibleRole.IpAddress: + case AccessibleRole.OutlineButton: + return false; + + default: + return !string.IsNullOrEmpty(DefaultAction); + } + } + } + + // + // Overridable methods for ILegacyAccessible interface + // + + internal virtual int GetChildId() { + return NativeMethods.CHILDID_SELF; + } + + // + // Overridable methods for IRawElementProviderFragment interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetEmbeddedFragmentRoots() { + return null; + } + + internal virtual void SetFocus() { + } + + internal virtual Rectangle BoundingRectangle { + get { + return this.Bounds; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return null; + } + } + + // + // Overridable methods for IRawElementProviderFragmentRoot interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + return this; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return null; + } + + // + // Overridable methods for IExpandCollapseProvider pattern interface + // + + internal virtual void Expand() { + } + + internal virtual void Collapse() { + } + + internal virtual UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + // + // Overridable methods for IToggleProvider pattern interface + // + + internal virtual void Toggle() { + } + + internal virtual UnsafeNativeMethods.ToggleState ToggleState { + get { + return UnsafeNativeMethods.ToggleState.ToggleState_Indeterminate; + } + } + + // + // Overridable methods for ITableProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders() { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaders() { + return null; + } + + internal virtual UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor { + get { + return UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor; + } + } + + // + // Overridable methods for ITableItemProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaderItems() { + return null; + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaderItems() { + return null; + } + + // + // Overridable methods for IGridProvider pattern interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column) { + return null; + } + + internal virtual int RowCount { + get { + return -1; + } + } + + internal virtual int ColumnCount { + get { + return -1; + } + } + + // + // Overridable methods for IGridItemProvider pattern interface + // + + internal virtual int Row { + get { + return -1; + } + } + + internal virtual int Column { + get { + return -1; + } + } + + internal virtual int RowSpan { + get { + return 1; + } + } + + internal virtual int ColumnSpan { + get { + return 1; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple ContainingGrid { + get { + return null; + } + } + + // + // Overridable methods for IInvokeProvider pattern interface + // + + internal virtual void Invoke() { + // Calling DoDefaultAction here is consistent with MSAA Proxy implementation. + DoDefaultAction(); + } + + // Overridable methods for IValueProvider pattern interface + + internal virtual bool IsReadOnly { + get { + return false; + } + } + + internal virtual void SetValue(string newValue) { + this.Value = newValue; + } + + // + // Overridable methods for IRawElementProviderHwndOverride interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd) { + return null; + } + + // + // Overridable methods for IRangeValueProvider pattern interface + // + + internal virtual void SetValue(double newValue) { + } + + internal virtual double LargeChange { + get { + return double.NaN; + } + } + + internal virtual double Maximum { + get { + return double.NaN; + } + } + + internal virtual double Minimum { + get { + return double.NaN; + } + } + + internal virtual double SmallChange { + get { + return double.NaN; + } + } + + internal virtual double RangeValue { + get { + return double.NaN; + } + } + + // + // Overridable methods for ISelectionProvider interface + // + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple[] GetSelection() { + return null; + } + + internal virtual bool CanSelectMultiple { + get { + return false; + } + } + + internal virtual bool IsSelectionRequired { + get { + return false; + } + } + + // + // Overridable methods for ISelectionItemProvider interface + // + + internal virtual void SelectItem() { + } + + internal virtual void AddToSelection() { + } + + internal virtual void RemoveFromSelection() { + } + + internal virtual bool IsItemSelected { + get { + return false; + } + } + + internal virtual UnsafeNativeMethods.IRawElementProviderSimple ItemSelectionContainer { + get { + return null; + } + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal virtual void SetParent(AccessibleObject parent) + { + } + + /// + /// Sets the detachable child accessible object which may be added or removed to/from hierachy nodes. + /// + /// The child accessible object. + internal virtual void SetDetachableChild(AccessibleObject child) + { + } + + int UnsafeNativeMethods.IServiceProvider.QueryService(ref Guid service, ref Guid riid, out IntPtr ppvObject) { + + int hr = NativeMethods.E_NOINTERFACE; + ppvObject = IntPtr.Zero; + + if (IsIAccessibleExSupported()) { + if (service.Equals(UnsafeNativeMethods.guid_IAccessibleEx) && + riid.Equals(UnsafeNativeMethods.guid_IAccessibleEx)) { + // We want to return the internal, secure, object, which we don't have access here + // Return non-null, which will be interpreted in internal method, to mean returning casted object to IAccessibleEx + + ppvObject = Marshal.GetComInterfaceForObject(this, typeof(UnsafeNativeMethods.IAccessibleEx)); + hr = NativeMethods.S_OK; + } + } + + return hr; + } + + object UnsafeNativeMethods.IAccessibleEx.GetObjectForChild(int childId) { + return GetObjectForChild(childId); + } + + internal virtual object GetObjectForChild(int childId) { + return null; + } + + // This method is never called + int UnsafeNativeMethods.IAccessibleEx.GetIAccessiblePair(out object ppAcc, out int pidChild) { + + // No need to implement this for patterns and properties + ppAcc = null; + pidChild = 0; + return NativeMethods.E_POINTER; + } + + int[] UnsafeNativeMethods.IAccessibleEx.GetRuntimeId() { + return RuntimeId; + } + + int UnsafeNativeMethods.IAccessibleEx.ConvertReturnedElement(object pIn, out object ppRetValOut) { + + // No need to implement this for patterns and properties + ppRetValOut = null; + return NativeMethods.E_NOTIMPL; + } + + // + // IRawElementProviderSimple interface + // + + UnsafeNativeMethods.ProviderOptions UnsafeNativeMethods.IRawElementProviderSimple.ProviderOptions { + get { + return (UnsafeNativeMethods.ProviderOptions) ProviderOptions; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderSimple.HostRawElementProvider { + get { + return HostRawElementProvider; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPatternProvider(int patternId) { + if (IsPatternSupported(patternId)) { + return this; + } + else { + return null; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPropertyValue(int propertyID) { + return GetPropertyValue(propertyID); + } + + // + // IRawElementProviderFragment interface + // + + object UnsafeNativeMethods.IRawElementProviderFragment.Navigate(UnsafeNativeMethods.NavigateDirection direction) { + return FragmentNavigate(direction); + } + + int[] UnsafeNativeMethods.IRawElementProviderFragment.GetRuntimeId() { + return RuntimeId; + } + + object[] UnsafeNativeMethods.IRawElementProviderFragment.GetEmbeddedFragmentRoots() { + return GetEmbeddedFragmentRoots(); + } + + void UnsafeNativeMethods.IRawElementProviderFragment.SetFocus() { + SetFocus(); + } + + NativeMethods.UiaRect UnsafeNativeMethods.IRawElementProviderFragment.BoundingRectangle { + get { + return new NativeMethods.UiaRect(BoundingRectangle); + } + } + + UnsafeNativeMethods.IRawElementProviderFragmentRoot UnsafeNativeMethods.IRawElementProviderFragment.FragmentRoot { + get { + return FragmentRoot; + } + } + + // + // IRawElementProviderFragmentRoot interface + // + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y) { + return ElementProviderFromPoint(x, y); + } + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.GetFocus() { + return GetFocus(); + } + + // + // ILegacyIAccessibleProvider interface + // + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.DefaultAction { + get { + return this.DefaultAction; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Description { + get { + return this.Description; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Help { + get { + return this.Help; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.KeyboardShortcut { + get { + return this.KeyboardShortcut; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Name { + get { + return this.Name; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.Role { + get { + return (uint)this.Role; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.State { + get { + return (uint)this.State; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Value { + get { + return this.Value; + } + } + + int UnsafeNativeMethods.ILegacyIAccessibleProvider.ChildId { + get { + return GetChildId(); + } + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.DoDefaultAction() { + this.DoDefaultAction(); + } + + IAccessible UnsafeNativeMethods.ILegacyIAccessibleProvider.GetIAccessible() { + return this.AsIAccessible(this); + } + + object[] UnsafeNativeMethods.ILegacyIAccessibleProvider.GetSelection() { + return new UnsafeNativeMethods.IRawElementProviderSimple[] { + this.GetSelected() as UnsafeNativeMethods.IRawElementProviderSimple + }; + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.Select(int flagsSelect) { + this.Select((AccessibleSelection)flagsSelect); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.SetValue(string szValue) { + this.SetValue(szValue); + } + + // + // IExpandCollapseProvider interface + // + + void UnsafeNativeMethods.IExpandCollapseProvider.Expand() { + Expand(); + } + void UnsafeNativeMethods.IExpandCollapseProvider.Collapse() { + Collapse(); + } + + UnsafeNativeMethods.ExpandCollapseState UnsafeNativeMethods.IExpandCollapseProvider.ExpandCollapseState { + get { + return ExpandCollapseState; + } + } + + // + // IInvokeProvider interface + // + + void UnsafeNativeMethods.IInvokeProvider.Invoke() { + Invoke(); + } + + // + // IValueProvider interface + // + + bool UnsafeNativeMethods.IValueProvider.IsReadOnly { + get { + return IsReadOnly; + } + } + + string UnsafeNativeMethods.IValueProvider.Value { + get { + return Value; + } + } + + void UnsafeNativeMethods.IValueProvider.SetValue(string newValue) { + SetValue(newValue); + } + + // + // IToggleProvider implementation + // + + void UnsafeNativeMethods.IToggleProvider.Toggle() { + Toggle(); + } + + UnsafeNativeMethods.ToggleState UnsafeNativeMethods.IToggleProvider.ToggleState { + get { + return ToggleState; + } + } + + // + // ITableProvider implementation + // + + object[] UnsafeNativeMethods.ITableProvider.GetRowHeaders() { + return GetRowHeaders(); + } + + object[] UnsafeNativeMethods.ITableProvider.GetColumnHeaders() { + return GetColumnHeaders(); + } + + UnsafeNativeMethods.RowOrColumnMajor UnsafeNativeMethods.ITableProvider.RowOrColumnMajor { + get { + return RowOrColumnMajor; + } + } + + // + // ITableItemProvider implementation + // + + object[] UnsafeNativeMethods.ITableItemProvider.GetRowHeaderItems() { + return GetRowHeaderItems(); + } + + object[] UnsafeNativeMethods.ITableItemProvider.GetColumnHeaderItems() { + return GetColumnHeaderItems(); + } + + // + // IGridProvider implementation + // + + object UnsafeNativeMethods.IGridProvider.GetItem(int row, int column) { + return GetItem(row, column); + } + + int UnsafeNativeMethods.IGridProvider.RowCount { + get { + return RowCount; + } + } + + int UnsafeNativeMethods.IGridProvider.ColumnCount { + get { + return ColumnCount; + } + } + + // + // IGridItemProvider implementation + // + + int UnsafeNativeMethods.IGridItemProvider.Row { + get { + return Row; + } + } + + int UnsafeNativeMethods.IGridItemProvider.Column { + get { + return Column; + } + } + + int UnsafeNativeMethods.IGridItemProvider.RowSpan { + get { + return RowSpan; + } + } + + int UnsafeNativeMethods.IGridItemProvider.ColumnSpan { + get { + return ColumnSpan; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IGridItemProvider.ContainingGrid { + get { + return ContainingGrid; + } + } + + /// + /// + /// + /// + /// Perform the default action + /// + /// + void IAccessible.accDoDefaultAction(Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccDoDefaultAction: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // If the default action is to be performed on self, do it. + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + DoDefaultAction(); + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.DoDefaultAction(); + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accDoDefaultAction(childID); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + } + + /// + /// + /// + /// + /// Perform a hit test + /// + /// + Object IAccessible.accHitTest( + int xLeft, + int yTop) { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccHitTest: this = " + + this.ToString()); + + AccessibleObject obj = HitTest(xLeft, yTop); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accHitTest(xLeft, yTop); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// The location of the Accessible object + /// + /// + void IAccessible.accLocation( + out int pxLeft, + out int pyTop, + out int pcxWidth, + out int pcyHeight, + Object childID) { + + pxLeft = 0; + pyTop = 0; + pcxWidth = 0; + pcyHeight = 0; + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Use the Location function's return value if available + // + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + Rectangle bounds = this.Bounds; + pxLeft = bounds.X; + pyTop = bounds.Y; + pcxWidth = bounds.Width; + pcyHeight = bounds.Height; + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Returning " + + bounds.ToString()); + + return; + } + + // If we have an accessible object collection, get the appropriate child + // + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + Rectangle bounds = child.Bounds; + pxLeft = bounds.X; + pyTop = bounds.Y; + pcxWidth = bounds.Width; + pcyHeight = bounds.Height; + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Returning " + + bounds.ToString()); + + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accLocation(out pxLeft, out pyTop, out pcxWidth, out pcyHeight, childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccLocation: Setting " + + pxLeft.ToString(CultureInfo.InvariantCulture) + ", " + + pyTop.ToString(CultureInfo.InvariantCulture) + ", " + + pcxWidth.ToString(CultureInfo.InvariantCulture) + ", " + + pcyHeight.ToString(CultureInfo.InvariantCulture)); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + + return; + } + } + + /// + /// + /// + /// + /// Navigate to another accessible object. + /// + /// + Object IAccessible.accNavigate( + int navDir, + Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccNavigate: this = " + + this.ToString() + ", navdir = " + navDir.ToString(CultureInfo.InvariantCulture) + ", childID = " + childID.ToString()); + + // Use the Navigate function's return value if available + // + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + AccessibleObject newObject = Navigate((AccessibleNavigation)navDir); + if (newObject != null) { + return AsVariant(newObject); + } + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return AsVariant(child.Navigate((AccessibleNavigation)navDir)); + } + } + + if (systemIAccessible != null) { + try { + Object retObject; + if (!SysNavigate(navDir, childID, out retObject)) + retObject = systemIAccessible.accNavigate(navDir, childID); + return retObject; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Select an accessible object. + /// + /// + void IAccessible.accSelect(int flagsSelect, Object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.AccSelect: this = " + + this.ToString() + ", flagsSelect = " + flagsSelect.ToString(CultureInfo.InvariantCulture) + ", childID = " + childID.ToString()); + + // If the selection is self, do it. + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + Select((AccessibleSelection)flagsSelect); // Uses an Enum which matches SELFLAG + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Select((AccessibleSelection)flagsSelect); + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.accSelect(flagsSelect, childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + + } + + /// + /// + /// Performs the default action associated with this accessible object. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual void DoDefaultAction() { + // By default, just does the system default action if available + // + if (systemIAccessible != null) { + try { + systemIAccessible.accDoDefaultAction(0); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + /// + /// + /// + /// + /// Returns a child Accessible object + /// + /// + object IAccessible.get_accChild(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccChild: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Return self for CHILDID_SELF + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return AsIAccessible(this); + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + // Make sure we're not returning ourselves as our own child + // + Debug.Assert(child != this, "An accessible object is returning itself as its own child. This can cause Accessibility client applications to hang."); + if (child == this) { + return null; + } + + return AsIAccessible(child); + } + } + + // Otherwise, return the default system child for this control (if any) + if (systemIAccessible != null) { + return systemIAccessible.get_accChild(childID); + } + + return null; + } + + /// + /// + /// + /// Return the number of children + /// + int IAccessible.accChildCount { + get { + int childCount = -1; + + if (IsClientObject) { + childCount = GetChildCount(); + } + + if (childCount == -1) { + if (systemIAccessible != null) { + childCount = systemIAccessible.accChildCount; + } + else { + childCount = 0; + } + } + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.accHildCount: this = " + this.ToString() + ", returning " + childCount.ToString(CultureInfo.InvariantCulture)); + + return childCount; + } + } + + /// + /// + /// + /// + /// Return the default action + /// + /// + string IAccessible.get_accDefaultAction(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the default action property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return DefaultAction; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.DefaultAction; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDefaultAction(childID); + } + catch (COMException e) { + // Not all objects provide a default action + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child description + /// + /// + string IAccessible.get_accDescription(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the description property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Description; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Description; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accDescription(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// Returns the appropriate child from the Accessible Child Collection, if available + /// + private AccessibleObject GetAccessibleChild(object childID) { + if (!childID.Equals(NativeMethods.CHILDID_SELF)) { + int index = (int)childID - 1; // The first child is childID == 1 (index == 0) + if (index >= 0 && index < GetChildCount()) { + return GetChild(index); + } + } + return null; + } + + /// + /// + /// + /// Return the object or child focus + /// + object IAccessible.accFocus { + get { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccFocus: this = " + + this.ToString()); + + AccessibleObject obj = GetFocused(); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accFocus; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + } + + /// + /// + /// + /// + /// Return help for this accessible object. + /// + /// + string IAccessible.get_accHelp(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Help; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Help; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelp(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child help topic + /// + /// + int IAccessible.get_accHelpTopic(out string pszHelpFile, Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return GetHelpTopic(out pszHelpFile); + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.GetHelpTopic(out pszHelpFile); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accHelpTopic(out pszHelpFile, childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + pszHelpFile = null; + return -1; + } + + /// + /// + /// + /// + /// Return the object or child keyboard shortcut + /// + /// + string IAccessible.get_accKeyboardShortcut(Object childID) { + return get_accKeyboardShortcutInternal(childID); + } + + internal virtual string get_accKeyboardShortcutInternal(Object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return KeyboardShortcut; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.KeyboardShortcut; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accKeyboardShortcut(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child name + /// + /// + string IAccessible.get_accName(object childID) { + return get_accNameInternal(childID); + } + + /// + /// + internal virtual string get_accNameInternal(object childID) { + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.get_accName: this = " + this.ToString() + + ", childID = " + childID.ToString()); + + // Return the name property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Name; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Name; + } + } + + // Otherwise, use the system provided name + if (systemIAccessible != null) { + string retval = systemIAccessible.get_accName(childID); + + if (IsClientObject) { + if (retval == null || retval.Length == 0) { + retval = Name; // Name the child after its parent + } + } + + return retval; + } + + return null; + } + + /// + /// + /// + /// Return the parent object + /// + object IAccessible.accParent { + get { + IntSecurity.UnmanagedCode.Demand(); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.accParent: this = " + this.ToString()); + AccessibleObject parent = Parent; + if (parent != null) { + // Some debugging related tests + // + Debug.Assert(parent != this, "An accessible object is returning itself as its own parent. This can cause accessibility clients to hang."); + if (parent == this) { + parent = null; // This should prevent accessibility clients from hanging + } + } + + return AsIAccessible(parent); + } + } + + /// + /// + /// + /// + /// The role property describes an object's purpose in terms of its + /// relationship with sibling or child objects. + /// + /// + object IAccessible.get_accRole(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the role property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return (int)Role; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return (int)child.Role; + } + } + + if (systemIAccessible != null) { + return systemIAccessible.get_accRole(childID); + } + + return null; + } + + /// + /// + /// + /// Return the object or child selection + /// + object IAccessible.accSelection { + get { + + if (IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccSelection: this = " + + this.ToString()); + + AccessibleObject obj = GetSelected(); + if (obj != null) { + return AsVariant(obj); + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.accSelection; + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + } + + /// + /// + /// + /// + /// Return the object or child state + /// + /// + object IAccessible.get_accState(object childID) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.GetAccState: this = " + + this.ToString() + ", childID = " + childID.ToString()); + + // Return the state property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return (int)State; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return (int)child.State; + } + } + + if (systemIAccessible != null) { + return systemIAccessible.get_accState(childID); + } + + return null; + } + + /// + /// + /// + /// + /// Return the object or child value + /// + /// + string IAccessible.get_accValue(object childID) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Return the value property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + return Value; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + return child.Value; + } + } + + if (systemIAccessible != null) { + try { + return systemIAccessible.get_accValue(childID); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// Set the object or child name + /// + /// + void IAccessible.set_accName( + Object childID, + string newName) { + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Set the name property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + // Attempt to set the name property + Name = newName; + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Name = newName; + return; + } + } + + if (systemIAccessible != null) { + systemIAccessible.set_accName(childID, newName); + return; + } + } + + /// + /// + /// + /// + /// Set the object or child value + /// + /// + void IAccessible.set_accValue( + Object childID, + string newValue) { + + IntSecurity.UnmanagedCode.Demand(); + + if (IsClientObject) { + ValidateChildID(ref childID); + + // Set the value property if available + if (childID.Equals(NativeMethods.CHILDID_SELF)) { + // Attempt to set the value property + Value = newValue; + return; + } + + // If we have an accessible object collection, get the appropriate child + AccessibleObject child = GetAccessibleChild(childID); + if (child != null) { + child.Value = newValue; + return; + } + } + + if (systemIAccessible != null) { + try { + systemIAccessible.set_accValue(childID, newValue); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + /// + /// + /// [Microsoft - Apr04] + /// + /// Now that AccessibleObject is used to wrap all system-provided (OLEACC.DLL) accessible + /// objects, it needs to implement IOleWindow and pass this down to the inner object. This is + /// necessary because the OS function WindowFromAccessibleObject() walks up the parent chain + /// looking for the first object that implements IOleWindow, and uses that to get the hwnd. + /// + /// But this creates a new problem for AccessibleObjects that do NOT have windows, ie. which + /// represent simple elements. To the OS, these simple elements will now appear to implement + /// IOleWindow, so it will try to get hwnds from them - which they simply cannot provide. + /// + /// To work around this problem, the AccessibleObject for a simple element will delegate all + /// IOleWindow calls up the parent chain itself. This will stop at the first window-based + /// accessible object, which will be able to return an hwnd back to the OS. So we are + /// effectively 'preempting' what WindowFromAccessibleObject() would do. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + // See if we have an inner object that can provide the window handle + if (systemIOleWindow != null) { + return systemIOleWindow.GetWindow(out hwnd); + } + + // Otherwise delegate to the parent object + AccessibleObject parent = this.Parent; + if (parent is UnsafeNativeMethods.IOleWindow) { + return (parent as UnsafeNativeMethods.IOleWindow).GetWindow(out hwnd); + } + + // Or fail if there is no parent + hwnd = IntPtr.Zero; + return NativeMethods.E_FAIL; + } + + /// + /// + /// See GetWindow() above for details. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + // See if we have an inner object that can provide help + if (systemIOleWindow != null) { + systemIOleWindow.ContextSensitiveHelp(fEnterMode); + return; + } + + // Otherwise delegate to the parent object + AccessibleObject parent = this.Parent; + if (parent is UnsafeNativeMethods.IOleWindow) { + (parent as UnsafeNativeMethods.IOleWindow).ContextSensitiveHelp(fEnterMode); + return; + } + + // Or do nothing if there is no parent + } + + /// + /// + /// + /// Clone this accessible object. + /// + /// + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + EnumVariant.Clone(v); + } + + /// + /// + /// + /// Obtain the next n children of this accessible object. + /// + /// + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + return EnumVariant.Next(n, rgvar, ns); + } + + /// + /// + /// Resets the child accessible object enumerator. + /// + void UnsafeNativeMethods.IEnumVariant.Reset() { + EnumVariant.Reset(); + } + + /// + /// + /// + /// Skip the next n child accessible objects + /// + /// + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + EnumVariant.Skip(n); + } + + /// + /// + /// When overridden in a derived class, + /// navigates to another object. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual AccessibleObject Navigate(AccessibleNavigation navdir) { + + // Some default behavior for objects with AccessibleObject children + // + if (GetChildCount() >= 0) { + switch(navdir) { + case AccessibleNavigation.FirstChild: + return GetChild(0); + case AccessibleNavigation.LastChild: + return GetChild(GetChildCount() - 1); + case AccessibleNavigation.Previous: + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + if (Parent.GetChildCount() > 0) { + return null; + } + break; + case AccessibleNavigation.Next: + case AccessibleNavigation.Down: + case AccessibleNavigation.Right: + if (Parent.GetChildCount() > 0) { + return null; + } + break; + } + } + + if (systemIAccessible != null) { + try { + object retObject = null; + if (!SysNavigate((int)navdir, NativeMethods.CHILDID_SELF, out retObject)) + retObject = systemIAccessible.accNavigate((int)navdir, NativeMethods.CHILDID_SELF); + return WrapIAccessible(retObject); + } + catch (COMException e) { + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + } + return null; + } + + /// + /// + /// + /// Selects this accessible object. + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public virtual void Select(AccessibleSelection flags) { + + // By default, do the system behavior + // + if (systemIAccessible != null) { + try { + systemIAccessible.accSelect((int)flags, 0); + } + catch (COMException e) { + // Not all objects provide the select function + // + if (e.ErrorCode != NativeMethods.DISP_E_MEMBERNOTFOUND) { + throw e; + } + } + return; + } + } + + private object AsVariant(AccessibleObject obj) { + if (obj == this) { + return NativeMethods.CHILDID_SELF; + } + return AsIAccessible(obj); + } + + private IAccessible AsIAccessible(AccessibleObject obj) { + if (obj != null && obj.systemWrapper) { + return obj.systemIAccessible; + } + return obj; + } + + /// + /// + /// Indicates what kind of 'inner' system accessible object we are using as our fall-back + /// implementation of IAccessible (when the systemIAccessible member is not null). The inner + /// object is provided by OLEACC.DLL. Note that although the term 'id' is used, this value + /// really represents a category or type of accessible object. Ids are only unique among + /// accessible objects associated with the same window handle. Currently supported ids are... + /// + /// OBJID_CLIENT - represents the window's client area (including any child windows) + /// OBJID_WINDOW - represents the window's non-client area (including caption, frame controls and scrollbars) + /// + /// NOTE: When the id is OBJID_WINDOW, we short-circuit most of the virtual override behavior of + /// AccessibleObject, and turn the object into a simple wrapper around the inner system object. So + /// for a *user-defined* accessible object, that has NO inner object, its important that the id is + /// left as OBJID_CLIENT, otherwise the object will be short-circuited into a total NOP! + /// + /// + internal int AccessibleObjectId { + get { + return accObjId; + } + set { + accObjId = value; + } + } + + /// + /// + /// Indicates whether this accessible object represents the client area of the window. + /// + /// + internal bool IsClientObject { + get { + return AccessibleObjectId == NativeMethods.OBJID_CLIENT; + } + } + + /// + /// + /// Indicates whether this accessible object represents the non-client area of the window. + /// + /// + internal bool IsNonClientObject { + get { + return AccessibleObjectId == NativeMethods.OBJID_WINDOW; + } + } + + /// + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal IAccessible GetSystemIAccessibleInternal() { + return this.systemIAccessible; + } + + /// + /// + /// [To be supplied.] + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected void UseStdAccessibleObjects(IntPtr handle) { + UseStdAccessibleObjects(handle, AccessibleObjectId); + } + + /// + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected void UseStdAccessibleObjects(IntPtr handle, int objid) { + // Get a standard accessible Object + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + object acc = null; + int result = UnsafeNativeMethods.CreateStdAccessibleObject( + new HandleRef(this, handle), + objid, + ref IID_IAccessible, + ref acc); + + // Get the IEnumVariant interface + Guid IID_IEnumVariant = new Guid(NativeMethods.uuid_IEnumVariant); + object en = null; + result = UnsafeNativeMethods.CreateStdAccessibleObject( + new HandleRef(this, handle), + objid, + ref IID_IEnumVariant, + ref en); + + Debug.Assert(acc != null, "SystemIAccessible is null"); + Debug.Assert(en != null, "SystemIEnumVariant is null"); + + if (acc != null || en != null) { + systemIAccessible = (IAccessible)acc; + systemIEnumVariant = (UnsafeNativeMethods.IEnumVariant)en; + systemIOleWindow = acc as UnsafeNativeMethods.IOleWindow; + } + } + + /// + /// Performs custom navigation between parent/child/sibling accessible objects. This is basically + /// just a wrapper for GetSysChild(), that does some of the dirty work, such as wrapping the + /// returned object in a VARIANT. Usage is similar to GetSysChild(). Called prior to calling + /// IAccessible.accNavigate on the 'inner' system accessible object. + /// + /// + private bool SysNavigate(int navDir, Object childID, out Object retObject) { + retObject = null; + + // Only override system navigation relative to ourselves (since we can't interpret OLEACC child ids) + if (!childID.Equals(NativeMethods.CHILDID_SELF)) + return false; + + // Perform any supported navigation operation (fall back on system for unsupported navigation ops) + AccessibleObject newObject; + if (!GetSysChild((AccessibleNavigation) navDir, out newObject)) + return false; + + // If object found, wrap in a VARIANT. Otherwise return null for 'end of list' (OLEACC expects this) + retObject = (newObject == null) ? null : AsVariant(newObject); + + // Tell caller not to fall back on system behavior now + return true; + } + + /// + /// + /// Make sure that the childID is valid. + /// + internal void ValidateChildID(ref object childID) { + // An empty childID is considered to be the same as CHILDID_SELF. + // Some accessibility programs pass null into our functions, so we + // need to convert them here. + if (childID == null) { + childID = NativeMethods.CHILDID_SELF; + } + else if (childID.Equals(NativeMethods.DISP_E_PARAMNOTFOUND)) { + childID = 0; + } + else if (!(childID is Int32)) { + // AccExplorer seems to occasionally pass in objects instead of an int ChildID. + // + childID = 0; + } + } + + private AccessibleObject WrapIAccessible(object iacc) { + IAccessible accessible = iacc as IAccessible; + if (accessible == null) { + return null; + } + + // Check to see if this object already wraps iacc + // + if (this.systemIAccessible == iacc) { + return this; + } + + return new AccessibleObject(accessible); + } + + /// + /// + /// + /// Return the requested method if it is implemented by the Reflection object. The + /// match is based upon the name and DescriptorInfo which describes the signature + /// of the method. + /// + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { + return typeof(IAccessible).GetMethod(name, bindingAttr, binder, types, modifiers); + } + + /// + /// + /// + /// Return the requested method if it is implemented by the Reflection object. The + /// match is based upon the name of the method. If the object implementes multiple methods + /// with the same name an AmbiguousMatchException is thrown. + /// + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetMethod(name, bindingAttr); + } + + /// + /// + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return typeof(IAccessible).GetMethods(bindingAttr); + } + + /// + /// + /// + /// Return the requestion field if it is implemented by the Reflection object. The + /// match is based upon a name. There cannot be more than a single field with + /// a name. + /// + FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetField(name, bindingAttr); + } + + /// + /// + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return typeof(IAccessible).GetFields(bindingAttr); + } + + /// + /// + /// + /// Return the property based upon name. If more than one property has the given + /// name an AmbiguousMatchException will be thrown. Returns null if no property + /// is found. + /// + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetProperty(name, bindingAttr); + } + + /// + /// + /// + /// Return the property based upon the name and Descriptor info describing the property + /// indexing. Return null if no property is found. + /// + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return typeof(IAccessible).GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + /// + /// + /// + /// Returns an array of PropertyInfos for all the properties defined on + /// the Reflection object. + /// + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return typeof(IAccessible).GetProperties(bindingAttr); + } + + /// + /// + /// + /// Return an array of members which match the passed in name. + /// + MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) { + return typeof(IAccessible).GetMember(name, bindingAttr); + } + + /// + /// + /// + /// Return an array of all of the members defined for this object. + /// + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return typeof(IAccessible).GetMembers(bindingAttr); + } + + /// + /// + /// + /// Description of the Binding Process. + /// We must invoke a method that is accessable and for which the provided + /// parameters have the most specific match. A method may be called if + /// 1. The number of parameters in the method declaration equals the number of + /// arguments provided to the invocation + /// 2. The type of each argument can be converted by the binder to the + /// type of the type of the parameter. + /// + /// The binder will find all of the matching methods. These method are found based + /// upon the type of binding requested (MethodInvoke, Get/Set Properties). The set + /// of methods is filtered by the name, number of arguments and a set of search modifiers + /// defined in the Binder. + /// + /// After the method is selected, it will be invoked. Accessability is checked + /// at that point. The search may be control which set of methods are searched based + /// upon the accessibility attribute associated with the method. + /// + /// The BindToMethod method is responsible for selecting the method to be invoked. + /// For the default binder, the most specific method will be selected. + /// + /// This will invoke a specific member... + /// @exception If invokeAttr is CreateInstance then all other + /// Access types must be undefined. If not we throw an ArgumentException. + /// @exception If the invokeAttr is not CreateInstance then an + /// ArgumentException when name is null. + /// @exception ArgumentException when invokeAttr does not specify the type + /// @exception ArgumentException when invokeAttr specifies both get and set of + /// a property or field. + /// @exception ArgumentException when invokeAttr specifies property set and + /// invoke method. + /// + object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { + + if (args.Length == 0) { + MemberInfo[] member = typeof(IAccessible).GetMember(name); + if (member != null && member.Length > 0 && member[0] is PropertyInfo) { + MethodInfo getMethod = ((PropertyInfo)member[0]).GetGetMethod(); + if (getMethod != null && getMethod.GetParameters().Length > 0) { + args = new object[getMethod.GetParameters().Length]; + for (int i = 0; i < args.Length; i++) { + args[i] = NativeMethods.CHILDID_SELF; + } + } + } + } + return typeof(IAccessible).InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + + /// + /// + /// + /// Return the underlying Type that represents the IReflect Object. For expando object, + /// this is the (Object) IReflectInstance.GetType(). For Type object it is this. + /// + Type IReflect.UnderlyingSystemType { + get { + return typeof(IAccessible); + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd) { + return GetOverrideProviderForHwnd(hwnd); + } + + bool UnsafeNativeMethods.IRangeValueProvider.IsReadOnly { + get { + return IsReadOnly; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.LargeChange { + get { + return LargeChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Maximum { + get { + return Maximum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Minimum { + get { + return Minimum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.SmallChange { + get { + return SmallChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Value { + get { + return RangeValue; + } + } + + void UnsafeNativeMethods.IRangeValueProvider.SetValue(double value) { + SetValue(value); + } + + object[] UnsafeNativeMethods.ISelectionProvider.GetSelection() { + return GetSelection(); + } + + bool UnsafeNativeMethods.ISelectionProvider.CanSelectMultiple { + get { + return CanSelectMultiple; + } + } + + bool UnsafeNativeMethods.ISelectionProvider.IsSelectionRequired { + get { + return IsSelectionRequired; + } + } + + void UnsafeNativeMethods.ISelectionItemProvider.Select() { + SelectItem(); + } + + void UnsafeNativeMethods.ISelectionItemProvider.AddToSelection() { + AddToSelection(); + } + + void UnsafeNativeMethods.ISelectionItemProvider.RemoveFromSelection() { + RemoveFromSelection(); + } + + bool UnsafeNativeMethods.ISelectionItemProvider.IsSelected { + get { + return IsItemSelected; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.ISelectionItemProvider.SelectionContainer { + get { + return ItemSelectionContainer; + } + } + + /// + /// Raises the UIA Notification event. + /// The event is available starting with Windows 10, version 1709. + /// + /// The type of notification + /// Indicates how to process notifications + /// Notification text + /// + /// True if operation succeeds. + /// False if the underlying windows infrastructure is not available or the operation had failed. + /// Use Marshal.GetLastWin32Error for details. + /// + public bool RaiseAutomationNotification( + AutomationNotificationKind notificationKind, + AutomationNotificationProcessing notificationProcessing, + string notificationText) { + + if (!AccessibilityImprovements.Level3 || !notificationEventAvailable) { + return false; + } + + int result = NativeMethods.S_FALSE; + try { + // The activityId can be any string. It cannot be null. It isn’t used currently. + result = UnsafeNativeMethods.UiaRaiseNotificationEvent( + this, + notificationKind, + notificationProcessing, + notificationText, + String.Empty); + } + catch (EntryPointNotFoundException) { + // The UIA Notification event is not available, so don't attempt to raise it again. + notificationEventAvailable = false; + } + + return result == NativeMethods.S_OK; + } + + /// + /// Raises the LiveRegionChanged UIA event. + /// This method must be overridden in derived classes that support the UIA live region feature. + /// + /// True if operation succeeds, False otherwise. + public virtual bool RaiseLiveRegionChanged() { + throw new NotSupportedException(SR.GetString(SR.AccessibleObjectLiveRegionNotSupported)); + } + + internal bool RaiseAutomationEvent(int eventId) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseAutomationEvent(this, eventId); + return result == NativeMethods.S_OK; + } + + return false; + } + + internal bool RaiseAutomationPropertyChangedEvent(int propertyId, object oldValue, object newValue) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseAutomationPropertyChangedEvent(this, propertyId, oldValue, newValue); + return result == NativeMethods.S_OK; + } + + return false; + } + + internal bool RaiseStructureChangedEvent(UnsafeNativeMethods.StructureChangeType structureChangeType, int[] runtimeId) { + if (UnsafeNativeMethods.UiaClientsAreListening()) { + int result = UnsafeNativeMethods.UiaRaiseStructureChangedEvent(this, structureChangeType, runtimeId, runtimeId == null ? 0 : runtimeId.Length); + return result == NativeMethods.S_OK; + } + + return false; + } + + #region IScrollItemProvider implementation + + void UnsafeNativeMethods.IScrollItemProvider.ScrollIntoView() { + ScrollIntoView(); + } + + internal virtual void ScrollIntoView() { + Debug.Fail($"{nameof(this.ScrollIntoView)}() is not overriden"); + } + + #endregion + + private class EnumVariantObject : UnsafeNativeMethods.IEnumVariant { + + private int currentChild = 0; + private AccessibleObject owner; + + public EnumVariantObject(AccessibleObject owner) { + Debug.Assert(owner != null, "Cannot create EnumVariantObject with a null owner"); + this.owner = owner; + } + + public EnumVariantObject(AccessibleObject owner, int currentChild) { + Debug.Assert(owner != null, "Cannot create EnumVariantObject with a null owner"); + this.owner = owner; + this.currentChild = currentChild; + } + + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + v[0] = new EnumVariantObject(owner, currentChild); + } + + /// + /// + /// Resets the child accessible object enumerator. + /// + void UnsafeNativeMethods.IEnumVariant.Reset() { + currentChild = 0; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + if (owner.systemIEnumVariant != null) + owner.systemIEnumVariant.Reset(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// Skips the next n child accessible objects. + /// + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + currentChild += n; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + if (owner.systemIEnumVariant != null) + owner.systemIEnumVariant.Skip(n); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// Gets the next n child accessible objects. + /// + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + // NOTE: rgvar is a pointer to an array of variants + + if (owner.IsClientObject) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "EnumVariantObject: owner = " + owner.ToString() + ", n = " + n); + + Debug.Indent(); + + int childCount; + int[] newOrder; + + if ((childCount = owner.GetChildCount()) >= 0) + NextFromChildCollection(n, rgvar, ns, childCount); + else if (owner.systemIEnumVariant == null) + NextEmpty(n, rgvar, ns); + else if ((newOrder = owner.GetSysChildOrder()) != null) + NextFromSystemReordered(n, rgvar, ns, newOrder); + else + NextFromSystem(n, rgvar, ns); + + Debug.Unindent(); + } + else { + NextFromSystem(n, rgvar, ns); + } + + // Tell caller whether requested number of items was returned. Once list of items has + // been exhausted, we return S_FALSE so that caller knows to stop calling this method. + return (ns[0] == n) ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + + /// + /// When we have the IEnumVariant of an accessible proxy provided by the system (ie. + /// OLEACC.DLL), we can fall back on that to return the children. Generally, the system + /// proxy will enumerate the child windows, create a suitable kind of child accessible + /// proxy for each one, and return a set of IDispatch interfaces to these proxy objects. + /// + private void NextFromSystem(int n, IntPtr rgvar, int[] ns) { + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + owner.systemIEnumVariant.Next(n, rgvar, ns); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + currentChild += ns[0]; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: Delegating to systemIEnumVariant"); + } + + /// + /// Sometimes we want to rely on the system-provided behavior to create and return child + /// accessible objects, but we want to impose a new order on those objects (or even filter + /// some objects out). + /// + /// This method takes an array of ints that dictates the new order. It queries the system + /// for each child individually, and inserts the result into the correct *new* position. + /// + /// Note: This code has to make certain *assumptions* about OLEACC.DLL proxy object behavior. + /// However, this behavior is well documented. We *assume* the proxy will return a set of + /// child accessible objects that correspond 1:1 with the owning control's child windows, + /// and that the default order it returns these objects in is z-order (which also happens + /// to be the order that children appear in the Control.Controls[] collection). + /// + private void NextFromSystemReordered(int n, IntPtr rgvar, int[] ns, int[] newOrder) { + int i; + + for (i = 0; i < n && currentChild < newOrder.Length; ++i) { + if (!GotoItem(owner.systemIEnumVariant, newOrder[currentChild], GetAddressOfVariantAtIndex(rgvar, i))) + break; + ++currentChild; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: adding sys child " + currentChild + " of " + newOrder.Length); + } + + ns[0] = i; + } + + /// + /// If we have our own custom accessible child collection, return a set of 1-based integer + /// child ids, that the caller will eventually pass back to us via IAccessible.get_accChild(). + /// + private void NextFromChildCollection(int n, IntPtr rgvar, int[] ns, int childCount) { + int i; + + for (i = 0; i < n && currentChild < childCount; ++i) { + ++currentChild; + Marshal.GetNativeVariantForObject(((object) currentChild), GetAddressOfVariantAtIndex(rgvar, i)); + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: adding own child " + currentChild + " of " + childCount); + } + + ns[0] = i; + } + + /// + /// Default behavior if there is no custom child collection or system-provided + /// proxy to fall back on. In this case, we return an empty child collection. + /// + private void NextEmpty(int n, IntPtr rgvar, int[] ns) { + ns[0] = 0; + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibleObject.IEV.Next: no children to add"); + } + + /// + /// Given an IEnumVariant interface, this method jumps to a specific + /// item in the collection and extracts the result for that one item. + /// + private static bool GotoItem(UnsafeNativeMethods.IEnumVariant iev, int index, IntPtr variantPtr) { + int[] ns = new int[1]; + + // SECREVIEW: This assert is safe, and is needed to fix VSWhidbey#126319 + // (allow access to children of an AccessibleObject in partial trust). + // + IntSecurity.UnmanagedCode.Assert(); + try { + iev.Reset(); + iev.Skip(index); + iev.Next(1, variantPtr, ns); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return (ns[0] == 1); + } + + /// + /// Given an array of pointers to variants, calculate address of a given array element. + /// + private static IntPtr GetAddressOfVariantAtIndex(IntPtr variantArrayPtr, int index) { + int variantSize = 8 + (IntPtr.Size * 2); + return (IntPtr) ((ulong) variantArrayPtr + ((ulong) index) * ((ulong) variantSize)); + } + + } + + } // end class AccessibleObject + + /// + /// Internal object passed out to OLEACC clients via WM_GETOBJECT. + /// NOTE: THIS CLASS IS INTERNAL FOR SECURITY REASONS AND SHOULD REMAIN SO! + /// + internal sealed class InternalAccessibleObject : StandardOleMarshalObject, + UnsafeNativeMethods.IAccessibleInternal, + System.Reflection.IReflect, + UnsafeNativeMethods.IServiceProvider, + UnsafeNativeMethods.IAccessibleEx, + UnsafeNativeMethods.IRawElementProviderSimple, + UnsafeNativeMethods.IRawElementProviderFragment, + UnsafeNativeMethods.IRawElementProviderFragmentRoot, + UnsafeNativeMethods.IInvokeProvider, + UnsafeNativeMethods.IValueProvider, + UnsafeNativeMethods.IRangeValueProvider, + UnsafeNativeMethods.IExpandCollapseProvider, + UnsafeNativeMethods.IToggleProvider, + UnsafeNativeMethods.ITableProvider, + UnsafeNativeMethods.ITableItemProvider, + UnsafeNativeMethods.IGridProvider, + UnsafeNativeMethods.IGridItemProvider, + UnsafeNativeMethods.IEnumVariant, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.ILegacyIAccessibleProvider, + UnsafeNativeMethods.ISelectionProvider, + UnsafeNativeMethods.ISelectionItemProvider, + UnsafeNativeMethods.IRawElementProviderHwndOverride { + + private IAccessible publicIAccessible; // AccessibleObject as IAccessible + private UnsafeNativeMethods.IEnumVariant publicIEnumVariant; // AccessibleObject as IEnumVariant + private UnsafeNativeMethods.IOleWindow publicIOleWindow; // AccessibleObject as IOleWindow + private IReflect publicIReflect; // AccessibleObject as IReflect + + private UnsafeNativeMethods.IServiceProvider publicIServiceProvider; // AccessibleObject as IServiceProvider + private UnsafeNativeMethods.IAccessibleEx publicIAccessibleEx; // AccessibleObject as IAccessibleEx + + // UIAutomation + private UnsafeNativeMethods.IRawElementProviderSimple publicIRawElementProviderSimple; // AccessibleObject as IRawElementProviderSimple + private UnsafeNativeMethods.IRawElementProviderFragment publicIRawElementProviderFragment;// AccessibleObject as IRawElementProviderFragment + private UnsafeNativeMethods.IRawElementProviderFragmentRoot publicIRawElementProviderFragmentRoot;// AccessibleObject as IRawElementProviderFragmentRoot + private UnsafeNativeMethods.IInvokeProvider publicIInvokeProvider; // AccessibleObject as IInvokeProvider + private UnsafeNativeMethods.IValueProvider publicIValueProvider; // AccessibleObject as IValueProvider + private UnsafeNativeMethods.IRangeValueProvider publicIRangeValueProvider; // AccessibleObject as IRangeValueProvider + private UnsafeNativeMethods.IExpandCollapseProvider publicIExpandCollapseProvider; // AccessibleObject as IExpandCollapseProvider + private UnsafeNativeMethods.IToggleProvider publicIToggleProvider; // AccessibleObject as IToggleProvider + private UnsafeNativeMethods.ITableProvider publicITableProvider; // AccessibleObject as ITableProvider + private UnsafeNativeMethods.ITableItemProvider publicITableItemProvider; // AccessibleObject as ITableItemProvider + private UnsafeNativeMethods.IGridProvider publicIGridProvider; // AccessibleObject as IGridProvider + private UnsafeNativeMethods.IGridItemProvider publicIGridItemProvider; // AccessibleObject as IGridItemProvider + private UnsafeNativeMethods.ILegacyIAccessibleProvider publicILegacyIAccessibleProvider; // AccessibleObject as ILe----AccessibleProvider + private UnsafeNativeMethods.ISelectionProvider publicISelectionProvider; // AccessibleObject as ISelectionProvider + private UnsafeNativeMethods.ISelectionItemProvider publicISelectionItemProvider; // AccessibleObject as ISelectionItemProvider + private UnsafeNativeMethods.IRawElementProviderHwndOverride publicIRawElementProviderHwndOverride; // AccessibleObject as IRawElementProviderHwndOverride + + /// + /// Create a new wrapper. Protect this with UnmanagedCode Permission + /// + [ + SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + internal InternalAccessibleObject(AccessibleObject accessibleImplemention) { + // Get all the casts done here to catch any issues early + publicIAccessible = (IAccessible) accessibleImplemention; + publicIEnumVariant = (UnsafeNativeMethods.IEnumVariant) accessibleImplemention; + publicIOleWindow = (UnsafeNativeMethods.IOleWindow) accessibleImplemention; + publicIReflect = (IReflect) accessibleImplemention; + publicIServiceProvider = (UnsafeNativeMethods.IServiceProvider) accessibleImplemention; + publicIAccessibleEx = (UnsafeNativeMethods.IAccessibleEx) accessibleImplemention; + publicIRawElementProviderSimple = (UnsafeNativeMethods.IRawElementProviderSimple) accessibleImplemention; + publicIRawElementProviderFragment = (UnsafeNativeMethods.IRawElementProviderFragment)accessibleImplemention; + publicIRawElementProviderFragmentRoot = (UnsafeNativeMethods.IRawElementProviderFragmentRoot)accessibleImplemention; + publicIInvokeProvider = (UnsafeNativeMethods.IInvokeProvider)accessibleImplemention; + publicIValueProvider = (UnsafeNativeMethods.IValueProvider) accessibleImplemention; + publicIRangeValueProvider = (UnsafeNativeMethods.IRangeValueProvider)accessibleImplemention; + publicIExpandCollapseProvider = (UnsafeNativeMethods.IExpandCollapseProvider) accessibleImplemention; + publicIToggleProvider = (UnsafeNativeMethods.IToggleProvider)accessibleImplemention; + publicITableProvider = (UnsafeNativeMethods.ITableProvider)accessibleImplemention; + publicITableItemProvider = (UnsafeNativeMethods.ITableItemProvider)accessibleImplemention; + publicIGridProvider = (UnsafeNativeMethods.IGridProvider)accessibleImplemention; + publicIGridItemProvider = (UnsafeNativeMethods.IGridItemProvider)accessibleImplemention; + publicILegacyIAccessibleProvider = (UnsafeNativeMethods.ILegacyIAccessibleProvider)accessibleImplemention; + publicISelectionProvider = (UnsafeNativeMethods.ISelectionProvider)accessibleImplemention; + publicISelectionItemProvider = (UnsafeNativeMethods.ISelectionItemProvider)accessibleImplemention; + publicIRawElementProviderHwndOverride = (UnsafeNativeMethods.IRawElementProviderHwndOverride)accessibleImplemention; + // Note: Deliberately not holding onto AccessibleObject to enforce all access through the interfaces + } + + /// + /// If the given object is an AccessibleObject return it as a InternalAccessibleObject + /// This ensures we wrap all AccessibleObjects before handing them out to OLEACC + /// + private object AsNativeAccessible(object accObject) { + if (accObject is AccessibleObject) { + return new InternalAccessibleObject(accObject as AccessibleObject); + } + else { + return accObject; + } + } + + /// + /// Wraps AccessibleObject elements of a given array into InternalAccessibleObjects + /// + private object[] AsArrayOfNativeAccessibles(object[] accObjectArray) { + if (accObjectArray != null && accObjectArray.Length > 0) { + for (int i = 0; i < accObjectArray.Length; i++) { + accObjectArray[i] = AsNativeAccessible(accObjectArray[i]); + } + } + return accObjectArray; + } + + // + // IAccessibleInternal implementation... + // + + void UnsafeNativeMethods.IAccessibleInternal.accDoDefaultAction(object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accDoDefaultAction(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.accHitTest(int xLeft, int yTop) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accHitTest(xLeft, yTop)); + } + + void UnsafeNativeMethods.IAccessibleInternal.accLocation(out int l, out int t, out int w, out int h, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accLocation(out l, out t, out w, out h, childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.accNavigate(int navDir, object childID) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accNavigate(navDir, childID)); + } + + void UnsafeNativeMethods.IAccessibleInternal.accSelect(int flagsSelect, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.accSelect(flagsSelect, childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accChild(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.get_accChild(childID)); + } + + int UnsafeNativeMethods.IAccessibleInternal.get_accChildCount() { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.accChildCount; + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accDefaultAction(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accDefaultAction(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accDescription(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accDescription(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accFocus() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accFocus); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accHelp(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accHelp(childID); + } + + int UnsafeNativeMethods.IAccessibleInternal.get_accHelpTopic(out string pszHelpFile, Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accHelpTopic(out pszHelpFile, childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accKeyboardShortcut(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accKeyboardShortcut(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accName(Object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accName(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accParent() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accParent); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accRole(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accRole(childID); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accSelection() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIAccessible.accSelection); + } + + object UnsafeNativeMethods.IAccessibleInternal.get_accState(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accState(childID); + } + + string UnsafeNativeMethods.IAccessibleInternal.get_accValue(object childID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessible.get_accValue(childID); + } + + void UnsafeNativeMethods.IAccessibleInternal.set_accName(Object childID, string newName) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.set_accName(childID, newName); + } + + void UnsafeNativeMethods.IAccessibleInternal.set_accValue(Object childID, string newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIAccessible.set_accValue(childID, newValue); + } + + // + // IEnumVariant implementation... + // + + void UnsafeNativeMethods.IEnumVariant.Clone(UnsafeNativeMethods.IEnumVariant[] v) { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Clone(v); + } + + int UnsafeNativeMethods.IEnumVariant.Next(int n, IntPtr rgvar, int[] ns) { + IntSecurity.UnmanagedCode.Assert(); + return publicIEnumVariant.Next(n, rgvar, ns); + } + + void UnsafeNativeMethods.IEnumVariant.Reset() { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Reset(); + } + + void UnsafeNativeMethods.IEnumVariant.Skip(int n) { + IntSecurity.UnmanagedCode.Assert(); + publicIEnumVariant.Skip(n); + } + + // + // IOleWindow implementation... + // + + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + IntSecurity.UnmanagedCode.Assert(); + return publicIOleWindow.GetWindow(out hwnd); + } + + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + IntSecurity.UnmanagedCode.Assert(); + publicIOleWindow.ContextSensitiveHelp(fEnterMode); + } + + // + // IReflect implementation... + // + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { + return publicIReflect.GetMethod(name, bindingAttr, binder, types, modifiers); + } + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) { + return publicIReflect.GetMethod(name, bindingAttr); + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return publicIReflect.GetMethods(bindingAttr); + } + + FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) { + return publicIReflect.GetField(name, bindingAttr); + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return publicIReflect.GetFields(bindingAttr); + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) { + return publicIReflect.GetProperty(name, bindingAttr); + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return publicIReflect.GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return publicIReflect.GetProperties(bindingAttr); + } + + MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) { + return publicIReflect.GetMember(name, bindingAttr); + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return publicIReflect.GetMembers(bindingAttr); + } + + object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { + IntSecurity.UnmanagedCode.Demand(); + return publicIReflect.InvokeMember(name, invokeAttr, binder, publicIAccessible, args, modifiers, culture, namedParameters); + } + + Type IReflect.UnderlyingSystemType { + get { + IReflect r = publicIReflect; + return publicIReflect.UnderlyingSystemType; + } + } + + // + // IServiceProvider implementation + // + + int UnsafeNativeMethods.IServiceProvider.QueryService(ref Guid service, ref Guid riid, out IntPtr ppvObject) { + IntSecurity.UnmanagedCode.Assert(); + + ppvObject = IntPtr.Zero; + int hr = publicIServiceProvider.QueryService(ref service, ref riid, out ppvObject); + if (hr >= NativeMethods.S_OK) { + // we always want to return the internal accessible object + ppvObject = Marshal.GetComInterfaceForObject(this, typeof(UnsafeNativeMethods.IAccessibleEx)); + } + + return hr; + } + + // + // IAccessibleEx implementation + // + + object UnsafeNativeMethods.IAccessibleEx.GetObjectForChild(int idChild) { + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.GetObjectForChild(idChild); + } + + int UnsafeNativeMethods.IAccessibleEx.GetIAccessiblePair(out object ppAcc, out int pidChild) { + + IntSecurity.UnmanagedCode.Assert(); + + // We always want to return the internal accessible object + ppAcc = this; + pidChild = NativeMethods.CHILDID_SELF; + return NativeMethods.S_OK; + } + + int[] UnsafeNativeMethods.IAccessibleEx.GetRuntimeId() { + + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.GetRuntimeId(); + } + + int UnsafeNativeMethods.IAccessibleEx.ConvertReturnedElement(object pIn, out object ppRetValOut) { + + IntSecurity.UnmanagedCode.Assert(); + return publicIAccessibleEx.ConvertReturnedElement(pIn, out ppRetValOut); + } + + // + // IRawElementProviderSimple implementation + // + + UnsafeNativeMethods.ProviderOptions UnsafeNativeMethods.IRawElementProviderSimple.ProviderOptions { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.ProviderOptions; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderSimple.HostRawElementProvider { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.HostRawElementProvider; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPatternProvider(int patternId) { + IntSecurity.UnmanagedCode.Assert(); + + object obj = publicIRawElementProviderSimple.GetPatternProvider(patternId); + if (obj != null) { + + // we always want to return the internal accessible object + + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + return (UnsafeNativeMethods.IExpandCollapseProvider)this; + } + else if (patternId == NativeMethods.UIA_ValuePatternId) { + return (UnsafeNativeMethods.IValueProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_RangeValuePatternId) { + return (UnsafeNativeMethods.IRangeValueProvider)this; + } + else if (patternId == NativeMethods.UIA_TogglePatternId) { + return (UnsafeNativeMethods.IToggleProvider)this; + } + else if (patternId == NativeMethods.UIA_TablePatternId) { + return (UnsafeNativeMethods.ITableProvider)this; + } + else if (patternId == NativeMethods.UIA_TableItemPatternId) { + return (UnsafeNativeMethods.ITableItemProvider)this; + } + else if (patternId == NativeMethods.UIA_GridPatternId) { + return (UnsafeNativeMethods.IGridProvider)this; + } + else if (patternId == NativeMethods.UIA_GridItemPatternId) { + return (UnsafeNativeMethods.IGridItemProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_InvokePatternId) { + return (UnsafeNativeMethods.IInvokeProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_LegacyIAccessiblePatternId) { + return (UnsafeNativeMethods.ILegacyIAccessibleProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_SelectionPatternId) { + return (UnsafeNativeMethods.ISelectionProvider)this; + } + else if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_SelectionItemPatternId) { + return (UnsafeNativeMethods.ISelectionItemProvider)this; + } + else { + return null; + } + } + else { + return null; + } + } + + object UnsafeNativeMethods.IRawElementProviderSimple.GetPropertyValue(int propertyID) { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderSimple.GetPropertyValue(propertyID); + } + + // + // IRawElementProviderFragment implementation + // + + object UnsafeNativeMethods.IRawElementProviderFragment.Navigate(UnsafeNativeMethods.NavigateDirection direction) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragment.Navigate(direction)); + } + + int[] UnsafeNativeMethods.IRawElementProviderFragment.GetRuntimeId() { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderFragment.GetRuntimeId(); + } + + object[] UnsafeNativeMethods.IRawElementProviderFragment.GetEmbeddedFragmentRoots() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicIRawElementProviderFragment.GetEmbeddedFragmentRoots()); + } + + void UnsafeNativeMethods.IRawElementProviderFragment.SetFocus() { + IntSecurity.UnmanagedCode.Assert(); + publicIRawElementProviderFragment.SetFocus(); + } + + NativeMethods.UiaRect UnsafeNativeMethods.IRawElementProviderFragment.BoundingRectangle { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderFragment.BoundingRectangle; + } + } + + UnsafeNativeMethods.IRawElementProviderFragmentRoot UnsafeNativeMethods.IRawElementProviderFragment.FragmentRoot { + get { + IntSecurity.UnmanagedCode.Assert(); + if (AccessibilityImprovements.Level3) { + return publicIRawElementProviderFragment.FragmentRoot; + } + + return AsNativeAccessible(publicIRawElementProviderFragment.FragmentRoot) as UnsafeNativeMethods.IRawElementProviderFragmentRoot; + } + } + + // + // IRawElementProviderFragmentRoot implementation + // + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragmentRoot.ElementProviderFromPoint(x, y)); + } + + object UnsafeNativeMethods.IRawElementProviderFragmentRoot.GetFocus() { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIRawElementProviderFragmentRoot.GetFocus()); + } + + // + // ILegacyIAccessibleProvider implementation + // + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.DefaultAction { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.DefaultAction; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Description { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Description; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Help { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Help; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.KeyboardShortcut { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.KeyboardShortcut; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Name { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Name; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.Role { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Role; + } + } + + uint UnsafeNativeMethods.ILegacyIAccessibleProvider.State { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.State; + } + } + + string UnsafeNativeMethods.ILegacyIAccessibleProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.Value; + } + } + + int UnsafeNativeMethods.ILegacyIAccessibleProvider.ChildId { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.ChildId; + } + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.DoDefaultAction() { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.DoDefaultAction(); + } + + IAccessible UnsafeNativeMethods.ILegacyIAccessibleProvider.GetIAccessible() { + IntSecurity.UnmanagedCode.Assert(); + return publicILegacyIAccessibleProvider.GetIAccessible(); + } + + object[] UnsafeNativeMethods.ILegacyIAccessibleProvider.GetSelection() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicILegacyIAccessibleProvider.GetSelection()); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.Select(int flagsSelect) { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.Select(flagsSelect); + } + + void UnsafeNativeMethods.ILegacyIAccessibleProvider.SetValue(string szValue) { + IntSecurity.UnmanagedCode.Assert(); + publicILegacyIAccessibleProvider.SetValue(szValue); + } + + // + // IInvokeProvider implementation + // + + void UnsafeNativeMethods.IInvokeProvider.Invoke() { + IntSecurity.UnmanagedCode.Assert(); + publicIInvokeProvider.Invoke(); + } + + // + // IValueProvider implementation + // + + bool UnsafeNativeMethods.IValueProvider.IsReadOnly { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.IsReadOnly; + } + } + + string UnsafeNativeMethods.IValueProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.Value; + } + } + + void UnsafeNativeMethods.IValueProvider.SetValue(string newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIValueProvider.SetValue(newValue); + } + + // + // IRangeValueProvider implementation + // + + bool UnsafeNativeMethods.IRangeValueProvider.IsReadOnly { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIValueProvider.IsReadOnly; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.LargeChange { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.LargeChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Maximum { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Maximum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Minimum { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Minimum; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.SmallChange { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.SmallChange; + } + } + + double UnsafeNativeMethods.IRangeValueProvider.Value { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIRangeValueProvider.Value; + } + } + + void UnsafeNativeMethods.IRangeValueProvider.SetValue(double newValue) { + IntSecurity.UnmanagedCode.Assert(); + publicIRangeValueProvider.SetValue(newValue); + } + + // + // IExpandCollapseProvider implementation + // + + void UnsafeNativeMethods.IExpandCollapseProvider.Expand() { + IntSecurity.UnmanagedCode.Assert(); + publicIExpandCollapseProvider.Expand(); + } + + void UnsafeNativeMethods.IExpandCollapseProvider.Collapse() { + IntSecurity.UnmanagedCode.Assert(); + publicIExpandCollapseProvider.Collapse(); + } + + UnsafeNativeMethods.ExpandCollapseState UnsafeNativeMethods.IExpandCollapseProvider.ExpandCollapseState { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIExpandCollapseProvider.ExpandCollapseState; + } + } + + // + // IToggleProvider implementation + // + + void UnsafeNativeMethods.IToggleProvider.Toggle() { + IntSecurity.UnmanagedCode.Assert(); + publicIToggleProvider.Toggle(); + } + + UnsafeNativeMethods.ToggleState UnsafeNativeMethods.IToggleProvider.ToggleState { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIToggleProvider.ToggleState; + } + } + + // + // ITableProvider implementation + // + + object[] UnsafeNativeMethods.ITableProvider.GetRowHeaders() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableProvider.GetRowHeaders()); + } + + object[] UnsafeNativeMethods.ITableProvider.GetColumnHeaders() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableProvider.GetColumnHeaders()); + } + + UnsafeNativeMethods.RowOrColumnMajor UnsafeNativeMethods.ITableProvider.RowOrColumnMajor { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicITableProvider.RowOrColumnMajor; + } + } + + // + // ITableItemProvider implementation + // + + object[] UnsafeNativeMethods.ITableItemProvider.GetRowHeaderItems() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableItemProvider.GetRowHeaderItems()); + } + + object[] UnsafeNativeMethods.ITableItemProvider.GetColumnHeaderItems() { + IntSecurity.UnmanagedCode.Assert(); + return AsArrayOfNativeAccessibles(publicITableItemProvider.GetColumnHeaderItems()); + } + + // + // IGridProvider implementation + // + + object UnsafeNativeMethods.IGridProvider.GetItem(int row, int column) { + IntSecurity.UnmanagedCode.Assert(); + return AsNativeAccessible(publicIGridProvider.GetItem(row, column)); + } + + int UnsafeNativeMethods.IGridProvider.RowCount { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridProvider.RowCount; + } + } + + int UnsafeNativeMethods.IGridProvider.ColumnCount { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridProvider.ColumnCount; + } + } + + // + // IGridItemProvider implementation + // + + int UnsafeNativeMethods.IGridItemProvider.Row { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.Row; + } + } + + int UnsafeNativeMethods.IGridItemProvider.Column { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.Column; + } + } + + int UnsafeNativeMethods.IGridItemProvider.RowSpan { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.RowSpan; + } + } + + int UnsafeNativeMethods.IGridItemProvider.ColumnSpan { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicIGridItemProvider.ColumnSpan; + } + } + + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IGridItemProvider.ContainingGrid { + get { + IntSecurity.UnmanagedCode.Assert(); + + // Do not wrap returned UIA provider by InternalAccessibleObject in Level 3. + if (AccessibilityImprovements.Level3) { + return publicIGridItemProvider.ContainingGrid; + } + + return AsNativeAccessible(publicIGridItemProvider.ContainingGrid) as UnsafeNativeMethods.IRawElementProviderSimple; + } + } + + // + // ISelectionProvider implementation + // + + /// + /// Get the currently selected elements + /// + /// An AutomationElement array containing the currently selected elements + object[] UnsafeNativeMethods.ISelectionProvider.GetSelection() { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.GetSelection(); + } + + /// + /// Indicates whether the control allows more than one element to be selected + /// + /// Boolean indicating whether the control allows more than one element to be selected + /// If this is false, then the control is a single-select ccntrol + bool UnsafeNativeMethods.ISelectionProvider.CanSelectMultiple { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.CanSelectMultiple; + } + } + + /// + /// Indicates whether the control requires at least one element to be selected + /// + /// Boolean indicating whether the control requires at least one element to be selected + /// If this is false, then the control allows all elements to be unselected + bool UnsafeNativeMethods.ISelectionProvider.IsSelectionRequired { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionProvider.IsSelectionRequired; + } + } + + // + // ISelectionItemProvider implementation + // + + /// + /// Sets the current element as the selection + /// This clears the selection from other elements in the container. + /// + void UnsafeNativeMethods.ISelectionItemProvider.Select() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.Select(); + } + + /// + /// Adds current element to selection. + /// + void UnsafeNativeMethods.ISelectionItemProvider.AddToSelection() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.AddToSelection(); + } + + /// + /// Removes current element from selection. + /// + void UnsafeNativeMethods.ISelectionItemProvider.RemoveFromSelection() { + IntSecurity.UnmanagedCode.Assert(); + publicISelectionItemProvider.RemoveFromSelection(); + } + + /// + /// Check whether an element is selected. + /// + /// Returns true if the element is selected. + bool UnsafeNativeMethods.ISelectionItemProvider.IsSelected { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionItemProvider.IsSelected; + } + } + + /// + /// The logical element that supports the SelectionPattern for this Item. + /// + /// Returns a IRawElementProviderSimple. + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.ISelectionItemProvider.SelectionContainer { + get { + IntSecurity.UnmanagedCode.Assert(); + return publicISelectionItemProvider.SelectionContainer; + } + } + + // + // IRawElementProviderHwndOverride implementation + // + + /// + /// Request a provider for the specified component. The returned provider can supply additional + /// properties or override properties of the specified component. + /// + /// The window handle of the component. + /// Return the provider for the specified component, or null if the component is not being overridden. + UnsafeNativeMethods.IRawElementProviderSimple UnsafeNativeMethods.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd) { + IntSecurity.UnmanagedCode.Assert(); + return publicIRawElementProviderHwndOverride.GetOverrideProviderForHwnd(hwnd); + } + + } // end class InternalAccessibleObject + +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleRoles.cs b/WindowsForms/Managed/System/WinForms/AccessibleRoles.cs new file mode 100644 index 000000000..c82c49e0a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleRoles.cs @@ -0,0 +1,698 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Drawing; + using Microsoft.Win32; + + // ---------------------------------------------------------------------------------------------------------------------------- + // WARNING: if adding to this enumeration please update Control & ToolStripItem AccessibleRole to ensure the new member is valid. + // ----------------------------------------------------------------------------------------------------------------------------- + + /// + /// + /// + /// Specifies values representing possible roles for an accessible object. + /// + /// + public enum AccessibleRole { + + /// + /// + /// + /// A system provided role. + /// + /// + Default = -1, + + /// + /// + /// + /// No role. + /// + /// + None = 0, + + /// + /// + /// + /// A title or caption bar + /// for a window. + /// + /// + TitleBar = ( 0x1 ), + + /// + /// + /// + /// A menu bar, usually beneath the + /// title bar of a window, from which menus can be selected + /// by the user. + /// + /// + MenuBar = ( 0x2 ), + + /// + /// + /// + /// A vertical or horizontal scroll bar, which + /// can be either part of the client area or used + /// in a control. + /// + /// + ScrollBar = ( 0x3 ), + + /// + /// + /// + /// A special mouse pointer, which + /// allows a user to manipulate user interface elements such as a window. For + /// example, a user can click and drag a sizing grip in the lower-right corner of a + /// window to + /// resize it. + /// + /// + Grip = ( 0x4 ), + + /// + /// + /// + /// A system sound, which is associated with + /// various + /// system events. + /// + /// + Sound = ( 0x5 ), + + /// + /// + /// + /// A mouse pointer. + /// + /// + Cursor = ( 0x6 ), + + /// + /// + /// + /// A caret, which is a flashing line, block, or bitmap + /// that marks the location of the insertion point in a + /// window's client area. + /// + /// + Caret = ( 0x7 ), + + /// + /// + /// + /// An alert or condition that a + /// user should be notified about. This role should be used only for objects that + /// embody an alert but are not associated with another user interface element, such + /// as a message box, graphic, text, + /// or sound. + /// + /// + Alert = ( 0x8 ), + + /// + /// + /// + /// A window frame, which usually contains child + /// objects such as a title bar, client, and other objects typically contained in + /// a window. + /// + /// + Window = ( 0x9 ), + + /// + /// + /// + /// A + /// window's user area. + /// + /// + Client = ( 0xa ), + + /// + /// + /// + /// A menu, which presents a list of + /// options from which the user can make a selection to perform an action. All menu + /// types must have this role, including drop-down menus that are displayed by + /// selection from a menu bar, and shortcut menus that are displayed when the right + /// mouse button is + /// clicked. + /// + /// + MenuPopup = ( 0xb ), + + /// + /// + /// + /// A menu item, which is an entry in + /// a menu that a user can choose to carry out a command, select + /// an option, or display another menu. + /// Functionally, a menu item can be equivalent to a push button, radio button, check + /// box, or menu. + /// + /// + MenuItem = ( 0xc ), + + /// + /// + /// A tool tip, which is a small rectangular pop-up + /// window that displays a brief description of a command bar button's + /// purpose. + /// + ToolTip = ( 0xd ), + + /// + /// + /// + /// The main window for + /// an application. + /// + /// + Application = ( 0xe ), + + /// + /// + /// + /// A + /// document window, which is always + /// contained within an application window. This role applies only to + /// multiple document interface (MDI) windows and refers to an object that contains + /// the MDI title bar. + /// + /// + Document = ( 0xf ), + + /// + /// + /// + /// One of the separate areas in a frame, a split document + /// window, or a rectangular area of the status + /// bar that can be used to display + /// information. Users can navigate between panes and within the contents of the current + /// pane, but cannot navigate between items in different panes. Thus, panes + /// represent a level of grouping lower than frame windows or documents, but above + /// individual controls. Typically the user navigates between panes by pressing TAB, + /// F6, or CTRL+TAB, depending on the + /// context. + /// + /// + Pane = ( 0x10 ), + + /// + /// + /// + /// A graphical image used to + /// represent data. + /// + /// + Chart = ( 0x11 ), + + /// + /// + /// + /// A dialog box + /// or message box. + /// + /// + Dialog = ( 0x12 ), + + /// + /// + /// + /// A window border. The entire border + /// is represented by a single object, rather than by separate objects for + /// each side. + /// + /// + Border = ( 0x13 ), + + /// + /// + /// + /// Objects grouped in a logical + /// manner. There can be a parent-child relationship between the grouping object and the + /// objects + /// it contains. + /// + /// + Grouping = ( 0x14 ), + + /// + /// + /// + /// Visually divides a space into two regions, such as a separator menu item or a + /// bar dividing split panes within a window. + /// + /// + Separator = ( 0x15 ), + + /// + /// + /// + /// A toolbar, which is a grouping of controls that provide + /// easy access to frequently + /// used features. + /// + /// + ToolBar = ( 0x16 ), + + /// + /// + /// + /// A status bar, which is an area typically at the bottom + /// of an application window + /// that displays information about the current operation, state of the application, + /// or selected object. The status bar can have multiple fields that display different + /// kinds of information, such as an explanation of the currently selected menu + /// command in the status bar. + /// + /// + StatusBar = ( 0x17 ), + + /// + /// + /// + /// A table containing rows and columns of cells, and + /// optionally, row headers and column headers. + /// + /// + Table = ( 0x18 ), + + /// + /// + /// + /// A column header, which provides a visual label for a column + /// in a table. + /// + /// + ColumnHeader = ( 0x19 ), + + /// + /// + /// + /// A row header, which provides a visual + /// label for + /// a table row. + /// + /// + RowHeader = ( 0x1a ), + + /// + /// + /// + /// A column of cells within a table. + /// + /// + Column = ( 0x1b ), + + /// + /// + /// + /// A row of cells within + /// a table. + /// + /// + Row = ( 0x1c ), + + /// + /// + /// + /// A cell within + /// a table. + /// + /// + Cell = ( 0x1d ), + + /// + /// + /// + /// A link, which is a connection between a source + /// document and a destination document. This + /// object might look like text or a graphic, but it acts like + /// a button. + /// + /// + Link = ( 0x1e ), + + /// + /// + /// + /// A Help display in the form of a ToolTip or Help + /// balloon, which + /// contains + /// buttons and labels that users can click to open + /// custom Help topics. + /// + /// + HelpBalloon = ( 0x1f ), + + /// + /// + /// + /// A cartoon-like graphic object, + /// such as Microsoft Office Assistant, which is typically displayed to provide help + /// to users of + /// an application. + /// + /// + Character = ( 0x20 ), + + /// + /// + /// + /// A list box, which allows the user + /// to select one or + /// more items. + /// + /// + List = ( 0x21 ), + + /// + /// + /// + /// An item in a list box or the list + /// portion of a combo box, drop-down list box, or + /// drop-down combo box. + /// + /// + ListItem = ( 0x22 ), + + /// + /// + /// + /// An outline or tree structure, such + /// as a tree view control, which displays a hierarchical list and usually allows + /// the + /// user to expand and collapse branches. + /// + /// + Outline = ( 0x23 ), + + /// + /// + /// + /// An item + /// in an outline or tree structure. + /// + /// + OutlineItem = ( 0x24 ), + + /// + /// + /// + /// A property page that allows a user + /// to view the attributes for a page, such as the page's title, whether it is a + /// home page, or + /// whether the page has been modified. + /// Normally the only child of this control is a grouped object that contains + /// the contents of the associated page. + /// + /// + PageTab = ( 0x25 ), + + /// + /// + /// + /// A property page, which is a dialog box that + /// controls the appearance and the behavior of an object, such as a file or + /// resource. A property page's appearance differs according + /// to its purpose. + /// + /// + PropertyPage = ( 0x26 ), + + /// + /// + /// + /// An indicator, such as a pointer + /// graphic, that points to the + /// current item. + /// + /// + Indicator = ( 0x27 ), + + /// + /// + /// + /// A picture. + /// + /// + Graphic = ( 0x28 ), + + /// + /// + /// + /// Read-only text, + /// such as in a label, for other + /// controls or instructions in a dialog box. Static text cannot be modified + /// or selected. + /// + /// + StaticText = ( 0x29 ), + + /// + /// + /// + /// Selectable text + /// that can be editable or read-only. + /// + /// + Text = ( 0x2a ), + + /// + /// + /// + /// A push button control, which is a + /// small rectangular control that a user can turn on or off. A push button, also known + /// as a command button, has a raised appearance in its default off state and + /// a sunken appearance when it is turned + /// on. + /// + /// + PushButton = ( 0x2b ), + + /// + /// + /// + /// A check box control, which is an option + /// that can be turned on or off independently + /// of other options. + /// + /// + CheckButton = ( 0x2c ), + + /// + /// + /// + /// An option button, also known as a + /// radio button. All objects sharing a single parent that have this attribute are + /// assumed to be part of a single mutually exclusive group. You can use + /// grouped objects to divide option buttons into separate groups when + /// necessary. + /// + /// + RadioButton = ( 0x2d ), + + /// + /// + /// + /// A + /// combo box, which is an edit + /// control with an associated list box that provides a set of predefined + /// choices. + /// + /// + ComboBox = ( 0x2e ), + + /// + /// + /// + /// A drop-down list box. This control shows one + /// item and allows the user to display and select another + /// from a list of alternative choices. + /// + /// + DropList = ( 0x2f ), + + /// + /// + /// + /// A progress bar, which indicates the progress of a + /// lengthy operation by displaying a colored bar inside a horizontal rectangle. The + /// length of the bar in relation + /// to the length of the rectangle corresponds + /// to the percentage of the operation that is complete. This control does + /// not take user + /// input. + /// + /// + ProgressBar = ( 0x30 ), + + /// + /// + /// + /// A dial or knob. This can also be a + /// read-only object, like a speedometer. + /// + /// + Dial = ( 0x31 ), + + /// + /// + /// + /// A hot-key field that allows the user to enter a combination or sequence of + /// keystrokes to be used as a hot key, which enables users to perform an action + /// quickly. A hot-key control displays the keystrokes entered by the user and + /// ensures that the user selects a valid key combination. + /// + /// + HotkeyField = ( 0x32 ), + + /// + /// + /// + /// A control, sometimes called a trackbar, + /// that allows a user to adjust a setting in given increments between + /// minimum and maximum values by moving a slider. The volume controls in the + /// Windows operating system are slider + /// controls. + /// + /// + Slider = ( 0x33 ), + + /// + /// + /// + /// A spin box, also + /// known as an up-down control, which contains a pair of arrow buttons that a + /// user click with a mouse to increment or decrement a value. A spin button control is + /// most often used with a companion control, called a buddy window, where the current + /// value is + /// displayed. + /// + /// + SpinButton = ( 0x34 ), + + /// + /// + /// + /// A graphical image used to diagram data. + /// + /// + Diagram = ( 0x35 ), + + /// + /// + /// + /// An animation control, which + /// contains content that is changing over time, such as a control that displays a + /// series of bitmap frames, like a film strip. Animation controls are usually + /// displayed when files are being copied, or when some other time-consuming task is + /// being + /// performed. + /// + /// + Animation = ( 0x36 ), + + /// + /// + /// + /// A mathematical equation. + /// + /// + Equation = ( 0x37 ), + + /// + /// + /// + /// A button that + /// drops down a list of items. + /// + /// + ButtonDropDown = ( 0x38 ), + + /// + /// + /// + /// A button that drops down a menu. + /// + /// + ButtonMenu = ( 0x39 ), + + /// + /// + /// + /// A button that drops down a grid. + /// + /// + ButtonDropDownGrid = ( 0x3a ), + + /// + /// + /// + /// A blank space between other objects. + /// + /// + WhiteSpace = ( 0x3b ), + + /// + /// + /// + /// A container of page tab controls. + /// + /// + PageTabList = ( 0x3c ), + + /// + /// + /// + /// A + /// control that displays the time. + /// + /// + Clock = ( 0x3d ), + + /// + /// + /// + /// A toolbar button that jas a drop-down list icon directly adjacent to the button. + /// + /// + SplitButton = ( 0x3e ), + + /// + /// + /// + /// A control designed for entering Internet Protocol (IP) addresses. + /// + /// + [SuppressMessage("Microsoft.Naming", "CA1706:ShortAcronymsShouldBeUppercase")] + IpAddress = ( 0x3f ), + + /// + /// + /// + /// A control that navigates like an outline item. + /// + /// + OutlineButton = ( 0x40 ), + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleSelection.cs b/WindowsForms/Managed/System/WinForms/AccessibleSelection.cs new file mode 100644 index 000000000..74af1a20f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleSelection.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how an accessible object will be selected or receive focus. + /// + /// + [Flags] + public enum AccessibleSelection { + + /// + /// + /// + /// The selection or focus of an object is unchanged. + /// + /// + None = 0, + + /// + /// + /// + /// Assigns focus to an object and makes + /// it the anchor, which is the starting point for + /// the selection. Can be combined with , + /// , , or + /// . + /// + /// + TakeFocus = 1, + + /// + /// + /// + /// Selects the object and deselects all other objects in the container. + /// + /// + TakeSelection = 2, + + /// + /// + /// + /// Selects all objects between the anchor and the selected object. + /// + /// + ExtendSelection = 4, + + /// + /// + /// + /// Adds the object to the selection. + /// + /// + AddSelection = 8, + + /// + /// + /// + /// Removes the object from the selection. + /// + /// + RemoveSelection = 16, + } +} diff --git a/WindowsForms/Managed/System/WinForms/AccessibleStates.cs b/WindowsForms/Managed/System/WinForms/AccessibleStates.cs new file mode 100644 index 000000000..b3826117b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AccessibleStates.cs @@ -0,0 +1,385 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies values representing possible states for an accessible object + /// + /// + [Flags] + public enum AccessibleStates { + + /// + /// + /// + /// No state. + /// + /// + None = 0, + + + /// + /// + /// + /// An unavailable object. + /// + /// + Unavailable = ( 0x1 ), + + + /// + /// + /// + /// A selected object. + /// + /// + Selected = ( 0x2 ), + + + /// + /// + /// + /// An object with the keyboard focus. + /// + /// + Focused = ( 0x4 ), + + + /// + /// + /// + /// A pressed object. + /// + /// + Pressed = ( 0x8 ), + + + /// + /// + /// + /// An object with a + /// selected check box. + /// + /// + Checked = ( 0x10 ), + + + /// + /// + /// + /// A three-state check box or toolbar button + /// whose state is indeterminate. The check box is neither checked nor unchecked and + /// it is in the + /// third or mixed state. + /// + /// + Mixed = ( 0x20 ), + + + /// + /// + /// + /// A three-state check box or toolbar button + /// whose state is indeterminate. The check box is neither checked nor unchecked and it + /// is in the third + /// or mixed state. + /// + /// + Indeterminate = ( Mixed ), + + + /// + /// + /// + /// A read-only object. + /// + /// + ReadOnly = ( 0x40 ), + + + /// + /// + /// + /// The object is hot-tracked by + /// the mouse, meaning its appearance is highlighted to indicate the mouse + /// pointer is located over it. + /// + /// + HotTracked = ( 0x80 ), + + + /// + /// + /// + /// The + /// default button or menu item. + /// + /// + Default = ( 0x100 ), + + + /// + /// + /// + /// Children of the object that are items in an outline or tree + /// structure are displayed. + /// + /// + Expanded = ( 0x200 ), + + + /// + /// + /// + /// Children of the object that are items in an outline or tree structure are + /// hidden. + /// + /// + Collapsed = ( 0x400 ), + + + /// + /// + /// + /// A + /// control + /// that cannot accept input in its current condition. + /// + /// + Busy = ( 0x800 ), + + + /// + /// + /// + /// The object is not fixed to the boundary of its parent object, and + /// does not move automatically along with the parent. + /// + /// + Floating = ( 0x1000 ), + + + /// + /// + /// + /// An object with scrolling + /// or moving text or graphics. + /// + /// + Marqueed = ( 0x2000 ), + + + /// + /// + /// + /// The object has a rapidly or constantly changing + /// appearance. Graphics that are occasionally animated, but not always, should be + /// defined as | + /// . This state should + /// not be used to indicate that + /// the object's location is changing. + /// + /// + Animated = ( 0x4000 ), + + + /// + /// + /// + /// An object that is currently invisible. + /// + /// + Invisible = ( 0x8000 ), + + + /// + /// + /// + /// No on-screen representation. A + /// sound or alert object would have this state, or a + /// hidden window that is never made visible. + /// + /// + Offscreen = ( 0x10000 ), + + + /// + /// + /// + /// A sizable object. + /// + /// + Sizeable = ( 0x20000 ), + + + /// + /// + /// + /// A movable object. + /// + /// + Moveable = ( 0x40000 ), + + + /// + /// + /// + /// The object or child can use text-to-speech (TTS) to describe itself. A + /// speech-based accessibility aid should not announce information when an object + /// with this state has the focus because the object will automatically announce + /// information about itself. + /// + /// + SelfVoicing = ( 0x80000 ), + + + /// + /// + /// + /// The object is on the active window + /// and can receive keyboard focus. + /// + /// + Focusable = ( 0x100000 ), + + + /// + /// + /// + /// An object that can accept selection. + /// + /// + Selectable = ( 0x200000 ), + + + /// + /// + /// + /// A linked object that has not + /// been previously selected. + /// + /// + Linked = ( 0x400000 ), + + + /// + /// + /// + /// A linked object that has previously been selected. + /// + /// + Traversed = ( 0x800000 ), + + + /// + /// + /// + /// An object + /// that accepts multiple selected items. + /// + /// + MultiSelectable = ( 0x1000000 ), + + + /// + /// + /// + /// Alters the selection so that all objects + /// between the selection anchor, which is the object with the + /// keyboard focus, and this object take on the anchor object's selection + /// state. If the anchor object is not selected, the objects are removed from + /// the selection. If the anchor object is selected, the selection is extended to + /// include this object and all the objects in between. You can set the selection + /// state by combining this flag with or . This flag does + /// not change the focus or the selection anchor unless it is combined with + /// . The behavior of + /// | + /// is equivalent to adding an item + /// to a selection manually by holding down the SHIFT key and clicking an unselected + /// object. This flag may not be combined with + /// + /// . + /// + /// + ExtSelectable = ( 0x2000000 ), + + + /// + /// + /// + /// Low-priority information that may not be important to the user. + /// + /// + AlertLow = ( 0x4000000 ), + + + /// + /// + /// + /// Important information that does not need to be conveyed to the user + /// immediately. For example, when a battery level indicator is starting to reach a + /// low level, it could generate a medium-level alert. Blind access utilities could + /// then generate a sound to let the user know that important information is + /// available, without actually interrupting the user's work. The user could then + /// query the alert information at his or her leisure. + /// + /// + AlertMedium = ( 0x8000000 ), + + + /// + /// + /// + /// Important information that should be conveyed to the user immediately. For + /// example, a battery level indicator reaching a critical low level would + /// transition to this state, in which case a blind access utility would announce + /// this information immediately to the user, and a screen magnification program + /// would scroll the screen so that the battery indicator is in view. This state is + /// also appropriate for any prompt or operation that must be completed before the + /// user can continue. + /// + /// + AlertHigh = ( 0x10000000 ), + + + /// + /// + /// + /// A password-protected edit control. + /// + /// + Protected = ( 0x20000000 ), + + + /// + /// + /// + /// Object displays a pop-up menu or window when invoked. + /// + /// + HasPopup = ( 0x40000000 ), + + + /// + /// + /// + [Obsolete("This enumeration value has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] + Valid = ( 0x3fffffff ), + + } +} diff --git a/WindowsForms/Managed/System/WinForms/AlphaSortedEnumConverter.cs b/WindowsForms/Managed/System/WinForms/AlphaSortedEnumConverter.cs new file mode 100644 index 000000000..25bc8efef --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AlphaSortedEnumConverter.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Collections; + + internal class AlphaSortedEnumConverter : EnumConverter { + public AlphaSortedEnumConverter(Type type) : base(type) { + } + + protected override IComparer Comparer { + get { + return EnumValAlphaComparer.Default; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/AmbientProperties.cs b/WindowsForms/Managed/System/WinForms/AmbientProperties.cs new file mode 100644 index 000000000..5100b70be --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AmbientProperties.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1999, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides ambient property values to top-level controls. + /// + /// + // + // NOTE: internally, this class does double duty as storage for Control's inherited properties. + public sealed class AmbientProperties { + + // Public ambient properties + private Color backColor; + private Color foreColor; + private Cursor cursor; + private Font font; + + /// + /// + /// + /// Gets the ambient BackColor, or Color.Empty if there is none. + /// + /// + public Color BackColor { + get { + return backColor; + } + set { + backColor = value; + } + } + + /// + /// + /// + /// Gets the ambient BackColor, or null if there is none. + /// + /// + public Cursor Cursor { + get { + return cursor; + } + set { + cursor = value; + } + } + + /// + /// + /// + /// Gets the ambient Font, or null if there is none. + /// + /// + public Font Font { + get { + return font; + } + set { + font = value; + } + } + + /// + /// + /// + /// Gets the ambient ForeColor, or Color.Empty if there is none. + /// + /// + public Color ForeColor { + get { + return foreColor; + } + set { + foreColor = value; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/AnchorStyles.cs b/WindowsForms/Managed/System/WinForms/AnchorStyles.cs new file mode 100644 index 000000000..e29c69320 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AnchorStyles.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Windows.Forms.Design; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how + /// a + /// control anchors to the edges of its container. + /// + /// + [ + Editor("System.Windows.Forms.Design.AnchorEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + ] + [Flags] + public enum AnchorStyles { + + /// + /// + /// + /// The control is anchored to the top edge of its container. + /// + /// + Top = 0x01, + /// + /// + /// + /// The control is anchored to the bottom edge of its container. + /// + /// + Bottom = 0x02, + /// + /// + /// + /// The control is anchored to the left edge of its container. + /// + /// + Left = 0x04, + /// + /// + /// + /// The control is anchored to the right edge of its container. + /// + /// + Right = 0x08, + + /// + /// + /// + /// The control is not anchored to any edges of its container. + /// + /// + None = 0, + } +} diff --git a/WindowsForms/Managed/System/WinForms/AppContextDefaultValues.Defaults.cs b/WindowsForms/Managed/System/WinForms/AppContextDefaultValues.Defaults.cs new file mode 100644 index 000000000..e3fa29646 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AppContextDefaultValues.Defaults.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System { + using System.Windows.Forms; + + internal static partial class AppContextDefaultValues { + static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version) { + // When defining a new switch you should add it to the last known version. + // For instance, if you are adding a switch in .NET 4.6.1 (the release after 4.6) you should define your switch + // like this: + // if (version <= 40600) ... + // This ensures that all previous versions of that platform (up-to 4.6) will get the old behavior by default + // NOTE: When adding a default value for a switch please make sure that the default value is added to ALL of the existing platforms! + // NOTE: When adding a new if statement for the version please ensure that ALL previous switches are enabled (i.e. don't use else if) + switch (platformIdentifier) { + case ".NETFramework": { + if (version <= 40600) { + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DontSupportReentrantFilterMessageSwitchName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DoNotSupportSelectAllShortcutInMultilineTextBoxSwitchName, true); + } + if (version <= 40602) { + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DoNotLoadLatestRichEditControlSwitchName, true); + } + if (version <= 40700) { + LocalAppContext.DefineSwitchDefault(AccessibilityImprovements.UseLegacyAccessibilityFeaturesSwitchName, true); + } + if (version <= 40701) { + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.UseLegacyContextMenuStripSourceControlValueSwitchName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DomainUpDownUseLegacyScrollingSwitchName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.AllowUpdateChildControlIndexForTabControlsSwitchName, true); + LocalAppContext.DefineSwitchDefault(AccessibilityImprovements.UseLegacyAccessibilityFeatures2SwitchName, true); + } + if (version <= 40702) { + LocalAppContext.DefineSwitchDefault(AccessibilityImprovements.UseLegacyAccessibilityFeatures3SwitchName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.UseLegacyImagesSwitchName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.EnableVisualStyleValidationSwitchName, true); + } + + // When this switch is set to false, it enables the keyboard tooltips feature which is an "opt-in" feature. + // UI that depends on this feature should be designed appropriately to prevent clutter. + // That's why this switch is set to true by default for all versions of the framework. + LocalAppContext.DefineSwitchDefault(AccessibilityImprovements.UseLegacyToolTipDisplaySwitchName, true); + + break; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Appearance.cs b/WindowsForms/Managed/System/WinForms/Appearance.cs new file mode 100644 index 000000000..d431e2109 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Appearance.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies + /// the appearance of a control. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum Appearance { + + /// + /// + /// + /// The default appearance defined by the control + /// class. + /// + /// + Normal = 0, + + /// + /// + /// + /// The appearance of a Windows + /// button. + /// + /// + Button = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Application.cs b/WindowsForms/Managed/System/WinForms/Application.cs new file mode 100644 index 000000000..eaef3a029 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Application.cs @@ -0,0 +1,4393 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +/* + */ +namespace System.Windows.Forms { + using System.Text; + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.ConstrainedExecution; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.IO; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Collections; + using System.Globalization; + using System.Runtime.Versioning; + + using System.Reflection; + using System.ComponentModel; + using System.Windows.Forms.Layout; + using System.Windows.Forms.VisualStyles; + using Directory = System.IO.Directory; + + using System.Deployment.Internal.Isolation; + using System.Collections.Generic; + + /// + /// + /// Provides + /// methods and properties + /// to manage an application, such as methods to run and quit an application, + /// to process Windows messages, and properties to get information about an application. This + /// class cannot be inherited. + /// + public sealed class Application { + /// + /// + /// Hash table for our event list + /// + static EventHandlerList eventHandlers; + static string startupPath; + static string executablePath; + static object appFileVersion; + static Type mainType; + static string companyName; + static string productName; + static string productVersion; + static string safeTopLevelCaptionSuffix; + static bool useVisualStyles = false; + static bool comCtlSupportsVisualStylesInitialized = false; + static bool comCtlSupportsVisualStyles = false; + static FormCollection forms = null; + private static object internalSyncObject = new object(); + static bool useWaitCursor = false; + + private static bool useEverettThreadAffinity = false; + private static bool checkedThreadAffinity = false; + private const string everettThreadAffinityValue = "EnableSystemEventsThreadAffinityCompatibility"; + + /// + /// + /// in case Application.exit gets called recursively + /// + private static bool exiting; + + /// + /// + /// Events the user can hook into + /// + private static readonly object EVENT_APPLICATIONEXIT = new object(); + private static readonly object EVENT_THREADEXIT = new object(); + + + // Constant string used in Application.Restart() + private const string IEEXEC = "ieexec.exe"; + + // Constant string used for accessing ClickOnce app's data directory + private const string CLICKONCE_APPS_DATADIRECTORY = "DataDirectory"; + +#pragma warning disable 414 + // This property is used in VS ( via. private reflection) to identify that this release of framework version contains the + // changes required to create parking window according to the DpiAwarenessContext of the parent-less child windows being created. + // Any change to this property need be validated in VisualStudio. + private static bool parkingWindowSupportsPMAv2 = true; +#pragma warning restore 414 + + // Defines a new callback delegate type + [EditorBrowsable(EditorBrowsableState.Advanced)] + public delegate bool MessageLoopCallback(); + + /// + /// + /// This class is static, there is no need to ever create it. + /// + private Application() { + } + + /// + /// + /// + /// Determines if the caller should be allowed to quit the application. This will return false, + /// for example, if being called from a windows forms control being hosted within a web browser. The + /// windows forms control should not attempt to quit the application. + /// + /// + /// + public static bool AllowQuit { + get { + return ThreadContext.FromCurrent().GetAllowQuit(); + } + } + + /// + /// Returns True if it is OK to continue idle processing. Typically called in an Application.Idle event handler. + /// + internal static bool CanContinueIdle + { + get + { + return ThreadContext.FromCurrent().ComponentManager.FContinueIdle(); + } + } + + /// Typically, you shouldn't need to use this directly - use RenderWithVisualStyles instead. + internal static bool ComCtlSupportsVisualStyles { + get { + if (!comCtlSupportsVisualStylesInitialized) { + comCtlSupportsVisualStyles = InitializeComCtlSupportsVisualStyles(); + comCtlSupportsVisualStylesInitialized = true; + } + return comCtlSupportsVisualStyles; + } + } + + private static bool InitializeComCtlSupportsVisualStyles() { + if (useVisualStyles && OSFeature.Feature.IsPresent(OSFeature.Themes)) { + //NOTE: At this point, we may not have loaded ComCtl6 yet, but it will eventually + // be loaded, so we return true here. This works because UseVisualStyles, once + // set, cannot be turned off. If that changes (unlikely), this may not work. + return true; + } + + //to see if we are comctl6, we look for a function that is exposed only from comctl6 + // we do not call DllGetVersion or any direct p/invoke, because the binding will be + // cached. + // The GetModuleHandle function returns a handle to a mapped module without incrementing its reference count. + + IntPtr hModule = UnsafeNativeMethods.GetModuleHandle(ExternDll.Comctl32); + if (hModule != IntPtr.Zero) { + try { + IntPtr pFunc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, hModule), "ImageList_WriteEx"); + return (pFunc != IntPtr.Zero); + } catch { + } + } else { + // Load comctl since GetModuleHandle failed to find it + hModule = UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(ExternDll.Comctl32); + if (hModule != IntPtr.Zero) { + IntPtr pFunc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, hModule), "ImageList_WriteEx"); + return (pFunc != IntPtr.Zero); + } + } + return false; + } + + /// + /// + /// Gets the registry + /// key for the application data that is shared among all users. + /// + public static RegistryKey CommonAppDataRegistry { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Registry.LocalMachine.CreateSubKey(CommonAppDataRegistryKeyName); + } + } + + internal static string CommonAppDataRegistryKeyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + string template = @"Software\{0}\{1}\{2}"; + return string.Format(CultureInfo.CurrentCulture, template, + CompanyName, + ProductName, + ProductVersion); + } + } + + internal static bool UseEverettThreadAffinity { + get { + if (!checkedThreadAffinity) { + checkedThreadAffinity = true; + try { + //We need access to be able to read from the registry here. We're not creating a + //registry key, nor are we returning information from the registry to the user. + new RegistryPermission(PermissionState.Unrestricted).Assert(); + RegistryKey key = Registry.LocalMachine.OpenSubKey(CommonAppDataRegistryKeyName); + if (key != null) { + object value = key.GetValue(everettThreadAffinityValue); + key.Close(); + + if (value != null && (int)value != 0) { + useEverettThreadAffinity = true; + } + } + } + catch (SecurityException) { + // Can't read the key: use default value (false) + } + catch (InvalidCastException) { + // Key is of wrong type: use default value (false) + } + } + return useEverettThreadAffinity; + } + } + + /// + /// + /// Gets the path for the application data that is shared among all users. + /// + public static string CommonAppDataPath { + // NOTE : Don't obsolete these. GetDataPath isn't on SystemInformation, and it + // : provides the Win2K logo required adornments to the directory (Company\Product\Version) + // + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.AppDomain | ResourceScope.Machine)] + get { + try { + if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed) { + string data = AppDomain.CurrentDomain.GetData(CLICKONCE_APPS_DATADIRECTORY) as string; + if (data != null) { + return data; + } + } + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + return GetDataPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + } + } + + /// + /// + /// Gets the company name associated with the application. + /// + public static string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + lock(internalSyncObject) { + if (companyName == null) { + + // custom attribute + // + Assembly entryAssembly = Assembly.GetEntryAssembly(); + if (entryAssembly != null) { + object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attrs != null && attrs.Length > 0) { + companyName = ((AssemblyCompanyAttribute)attrs[0]).Company; + } + } + + // win32 version + // + if (companyName == null || companyName.Length == 0) { + companyName = GetAppFileVersionInfo().CompanyName; + if (companyName != null) { + companyName = companyName.Trim(); + } + } + + // fake it with a namespace + // won't work with MC++ see GetAppMainType. + if (companyName == null || companyName.Length == 0) { + Type t = GetAppMainType(); + + if (t != null) { + string ns = t.Namespace; + + if (!string.IsNullOrEmpty(ns)){ + int firstDot = ns.IndexOf("."); + if( firstDot != -1 ){ + companyName = ns.Substring(0, firstDot); + } + else{ + companyName = ns; + } + } + else { + // last ditch... no namespace, use product name... + // + companyName = ProductName; + } + } + } + } + } + return companyName; + } + } + + /// + /// + /// Gets + /// or sets the locale information for the current thread. + /// + public static CultureInfo CurrentCulture { + get { + return Thread.CurrentThread.CurrentCulture; + } + set { + Thread.CurrentThread.CurrentCulture = value; + } + } + + + /// + /// + /// Gets or + /// sets the current input language for the current thread. + /// + public static InputLanguage CurrentInputLanguage { + get { + return InputLanguage.CurrentInputLanguage; + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded"); + IntSecurity.AffectThreadBehavior.Demand(); + InputLanguage.CurrentInputLanguage = value; + } + } + + internal static bool CustomThreadExceptionHandlerAttached { + get { + return ThreadContext.FromCurrent().CustomThreadExceptionHandlerAttached; + } + } + + /// + /// + /// + /// Gets the + /// path for the executable file that started the application. + /// + /// + /// SECREVIEW ReviewImperitiveSecurity: + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: the return value of GetModuleFileName shouldnt change between when we called it and when we demanded permissions. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + public static string ExecutablePath { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (executablePath == null) { + Assembly asm = Assembly.GetEntryAssembly(); + if (asm == null) { + StringBuilder sb = UnsafeNativeMethods.GetModuleFileNameLongPath(NativeMethods.NullHandleRef); + executablePath = IntSecurity.UnsafeGetFullPath(sb.ToString()); + } + else { + String cb = asm.CodeBase; + Uri codeBase = new Uri(cb); + if (codeBase.IsFile) { + executablePath = codeBase.LocalPath + Uri.UnescapeDataString(codeBase.Fragment); ; + } + else { + executablePath = codeBase.ToString(); + } + } + } + Uri exeUri = new Uri(executablePath); + if (exeUri.Scheme == "file") { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + executablePath + ") Demanded"); + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, executablePath).Demand(); + } + return executablePath; + } + } + + /// + /// + /// Gets the path for the application data specific to a local, non-roaming user. + /// + public static string LocalUserAppDataPath { + // NOTE : Don't obsolete these. GetDataPath isn't on SystemInformation, and it + // : provides the Win2K logo required adornments to the directory (Company\Product\Version) + // + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.AppDomain)] + get { + try { + if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed) { + string data = AppDomain.CurrentDomain.GetData(CLICKONCE_APPS_DATADIRECTORY) as string; + if (data != null) { + return data; + } + } + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + return GetDataPath(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); + } + } + + /// + /// + /// + /// Determines if a message loop exists on this thread. + /// + /// + public static bool MessageLoop { + get { + return ThreadContext.FromCurrent().GetMessageLoop(); + } + } + + /// + /// + /// + /// Gets the forms collection associated with this application. + /// + /// + public static FormCollection OpenForms { + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + get { + return OpenFormsInternal; + } + } + + /// + /// + /// Internal version of OpenForms without the security demand. + /// + /// + internal static FormCollection OpenFormsInternal { + get { + if (forms == null) { + forms = new FormCollection(); + } + + return forms; + } + } + + /// + /// + /// Thread safe addition of form to the OpenForms collection. + /// + /// + internal static void OpenFormsInternalAdd(Form form) + { + OpenFormsInternal.Add(form); + } + + /// + /// + /// Thread safe removal of form from the OpenForms collection. + /// + /// + internal static void OpenFormsInternalRemove(Form form) + { + OpenFormsInternal.Remove(form); + } + + /// + /// + /// + /// Gets + /// the product name associated with this application. + /// + /// + public static string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + lock(internalSyncObject) { + if (productName == null) { + + // custom attribute + // + Assembly entryAssembly = Assembly.GetEntryAssembly(); + if (entryAssembly != null) { + object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attrs != null && attrs.Length > 0) { + productName = ((AssemblyProductAttribute)attrs[0]).Product; + } + } + + // win32 version info + // + if (productName == null || productName.Length == 0) { + productName = GetAppFileVersionInfo().ProductName; + if (productName != null) { + productName = productName.Trim(); + } + } + + // fake it with namespace + // won't work with MC++ see GetAppMainType. + if (productName == null || productName.Length == 0) { + Type t = GetAppMainType(); + + if (t != null) { + string ns = t.Namespace; + + if (!string.IsNullOrEmpty(ns)) { + int lastDot = ns.LastIndexOf("."); + if (lastDot != -1 && lastDot < ns.Length - 1) { + productName = ns.Substring(lastDot+1); + } + else { + productName = ns; + } + } + else{ + // last ditch... use the main type + // + productName = t.Name; + } + } + } + } + } + + return productName; + } + } + + /// + /// + /// + /// Gets + /// the product version associated with this application. + /// + /// + public static string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + lock(internalSyncObject) { + if (productVersion == null) { + + // custom attribute + // + Assembly entryAssembly = Assembly.GetEntryAssembly(); + if (entryAssembly != null) { + object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); + if (attrs != null && attrs.Length > 0) { + productVersion = ((AssemblyInformationalVersionAttribute)attrs[0]).InformationalVersion; + } + } + + // win32 version info + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = GetAppFileVersionInfo().ProductVersion; + if (productVersion != null) { + productVersion = productVersion.Trim(); + } + } + + // fake it + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = "1.0.0.0"; + } + } + } + return productVersion; + } + } + + // Allows the hosting environment to register a callback + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + public static void RegisterMessageLoop(MessageLoopCallback callback) { + ThreadContext.FromCurrent().RegisterMessageLoop(callback); + } + + /// + /// + /// Magic property that answers a simple question - are my controls currently going to render with + // visual styles? If you are doing visual styles rendering, use this to be consistent with the rest + // of the controls in your app. + /// + public static bool RenderWithVisualStyles { + get { + return (ComCtlSupportsVisualStyles && VisualStyles.VisualStyleRenderer.IsSupported); + } + } + + /// + /// + /// Gets or sets the format string to apply to top level window captions + /// when they are displayed with a warning banner. + /// + public static string SafeTopLevelCaptionFormat { + get { + if (safeTopLevelCaptionSuffix == null) { + safeTopLevelCaptionSuffix = SR.GetString(SR.SafeTopLevelCaptionFormat); // 0 - original, 1 - zone, 2 - site + } + return safeTopLevelCaptionSuffix; + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "WindowAdornmentModification Demanded"); + IntSecurity.WindowAdornmentModification.Demand(); + if (value == null) value = string.Empty; + safeTopLevelCaptionSuffix = value; + } + } + + /// + /// + /// + /// Gets the + /// path for the executable file that started the application. + /// + /// + + /// SECREVIEW ReviewImperitiveSecurity: + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: the return value of GetModuleFileName shouldnt change between when we called it and when we demanded permissions. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + public static string StartupPath { + get { + if (startupPath == null) { + StringBuilder sb = UnsafeNativeMethods.GetModuleFileNameLongPath(NativeMethods.NullHandleRef); + startupPath = Path.GetDirectoryName(sb.ToString()); + } + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + startupPath + ") Demanded"); + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, startupPath).Demand(); + return startupPath; + } + } + + // Allows the hosting environment to unregister a callback + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + public static void UnregisterMessageLoop() { + ThreadContext.FromCurrent().RegisterMessageLoop(null); + } + + /// + /// + /// Gets + /// or sets whether the wait cursor is used for all open forms of the application. + /// + public static bool UseWaitCursor { + get { + return useWaitCursor; + } + set { + lock (FormCollection.CollectionSyncRoot) + { + useWaitCursor = value; + // Set the WaitCursor of all forms. + foreach (Form f in OpenFormsInternal) + { + f.UseWaitCursor = useWaitCursor; + } + } + } + } + + /// + /// + /// Gets the path for the application data specific to the roaming user. + /// + public static string UserAppDataPath { + // NOTE : Don't obsolete these. GetDataPath isn't on SystemInformation, and it + // : provides the Win2K logo required adornments to the directory (Company\Product\Version) + // + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.AppDomain)] + get { + try { + if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed) { + string data = AppDomain.CurrentDomain.GetData(CLICKONCE_APPS_DATADIRECTORY) as string; + if (data != null) { + return data; + } + } + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + return GetDataPath(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + } + } + + /// + /// + /// Gets the registry key of + /// the application data specific to the roaming user. + /// + public static RegistryKey UserAppDataRegistry { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.AppDomain)] + get { + string template = @"Software\{0}\{1}\{2}"; + return Registry.CurrentUser.CreateSubKey(string.Format(CultureInfo.CurrentCulture, template, CompanyName, ProductName, ProductVersion)); + } + } + + internal static bool UseVisualStyles { + get { + return useVisualStyles; + } + } + + internal static string WindowsFormsVersion { + get { + // Notice : Don't never ever change this name, since window class of Microsoft control is dependent on this. + // And lots of partner team are related to window class of Microsoft control. Changing this will introduce breaking. + // If there is some reason need to change this, should take the accountability to notify partner team. + return "WindowsForms10"; + } + } + + internal static string WindowMessagesVersion { + get { + return "WindowsForms12"; + } + } + + /// + /// + /// + /// Use this property to determine how visual styles will be applied to this application. + /// This property is meaningful only if visual styles are supported on the current + /// platform (VisualStyleInformation.SupportedByOS is true). + /// + /// This property can be set only to one of the S.W.F.VisualStyles.VisualStyleState enum values. + /// + /// + public static VisualStyleState VisualStyleState { + get { + if (!VisualStyleInformation.IsSupportedByOS) { + return VisualStyleState.NoneEnabled; + } + + VisualStyleState vState = (VisualStyleState) SafeNativeMethods.GetThemeAppProperties(); + return vState; + } + + set { + if (VisualStyleInformation.IsSupportedByOS) + { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)VisualStyleState.NoneEnabled, (int)VisualStyleState.ClientAndNonClientAreasEnabled) + && LocalAppContextSwitches.EnableVisualStyleValidation) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(VisualStyleState)); + } + + SafeNativeMethods.SetThemeAppProperties((int)value); + + //248887 we need to send a WM_THEMECHANGED to the top level windows of this application. + //We do it this way to ensure that we get all top level windows -- whether we created them or not. + SafeNativeMethods.EnumThreadWindowsCallback callback = new SafeNativeMethods.EnumThreadWindowsCallback(Application.SendThemeChanged); + SafeNativeMethods.EnumWindows(callback, IntPtr.Zero); + + GC.KeepAlive(callback); + + } + } + } + + /// + /// This helper broadcasts out a WM_THEMECHANGED to appropriate top level windows of this app. + /// + private static bool SendThemeChanged(IntPtr handle, IntPtr extraParameter) { + int processId; + int thisPID = SafeNativeMethods.GetCurrentProcessId(); + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, handle), out processId); + if (processId == thisPID && SafeNativeMethods.IsWindowVisible(new HandleRef(null, handle))) { + + SendThemeChangedRecursive(handle, IntPtr.Zero); + SafeNativeMethods.RedrawWindow(new HandleRef(null, handle), + null, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_FRAME | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + return true; + } + + /// + /// This helper broadcasts out a WM_THEMECHANGED this window and all children. + /// it is assumed at this point that the handle belongs to the current process and has a visible top level window. + /// + private static bool SendThemeChangedRecursive(IntPtr handle, IntPtr lparam) { + //first send to all children... + UnsafeNativeMethods.EnumChildWindows(new HandleRef(null, handle), + new NativeMethods.EnumChildrenCallback(Application.SendThemeChangedRecursive), + NativeMethods.NullHandleRef); + + //then do myself. + UnsafeNativeMethods.SendMessage(new HandleRef(null, handle), NativeMethods.WM_THEMECHANGED, 0, 0); + + return true; + } + + /// + /// + /// Occurs when the application is about to shut down. + /// + public static event EventHandler ApplicationExit { + add { + AddEventHandler(EVENT_APPLICATIONEXIT, value); + } + remove { + RemoveEventHandler(EVENT_APPLICATIONEXIT, value); + } + } + + private static void AddEventHandler(object key, Delegate value) { + lock(internalSyncObject) { + if (null == eventHandlers) { + eventHandlers = new EventHandlerList(); + } + eventHandlers.AddHandler(key, value); + } + } + private static void RemoveEventHandler(object key, Delegate value) { + lock(internalSyncObject) { + if (null == eventHandlers) { + return; + } + eventHandlers.RemoveHandler(key, value); + } + } + + /// + /// + /// Adds a message filter to monitor Windows messages as they are routed to their + /// destinations. + /// + public static void AddMessageFilter(IMessageFilter value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ManipulateWndProcAndHandles Demanded"); + IntSecurity.UnmanagedCode.Demand(); + ThreadContext.FromCurrent().AddMessageFilter(value); + } + + /// + /// Processes all message filters for given message + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference") // using ref is OK. + ] + public static bool FilterMessage(ref Message message) { + + bool modified; + + // Create copy of MSG structure + NativeMethods.MSG msg = new NativeMethods.MSG(); + msg.hwnd = message.HWnd; + msg.message = message.Msg; + msg.wParam = message.WParam; + msg.lParam = message.LParam; + + bool processed = ThreadContext.FromCurrent().ProcessFilters(ref msg, out modified); + if (modified) { + message.HWnd = msg.hwnd; + message.Msg = msg.message; + message.WParam = msg.wParam; + message.LParam = msg.lParam; + } + + return processed; + } + + /// + /// + /// + /// Occurs when the application has finished processing and is about to enter the + /// idle state. + /// + /// + public static event EventHandler Idle { + add { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.idleHandler += value; + // This just ensures that the component manager is hooked up. We + // need it for idle time processing. + // + object o = current.ComponentManager; + } + } + remove { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.idleHandler -= value; + } + } + } + + /// + /// + /// + /// Occurs when the application is about to enter a modal state + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static event EventHandler EnterThreadModal { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + add { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.enterModalHandler += value; + } + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + remove { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.enterModalHandler -= value; + } + } + } + + /// + /// + /// + /// Occurs when the application is about to leave a modal state + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static event EventHandler LeaveThreadModal { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + add { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.leaveModalHandler += value; + } + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + remove { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.leaveModalHandler -= value; + } + } + } + + /// + /// + /// Occurs when an untrapped thread exception is thrown. + /// + public static event ThreadExceptionEventHandler ThreadException { + add { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded"); + IntSecurity.AffectThreadBehavior.Demand(); + + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.threadExceptionHandler = value; + } + } + remove { + ThreadContext current = ThreadContext.FromCurrent(); + lock(current) { + current.threadExceptionHandler -= value; + } + } + } + + /// + /// + /// Occurs when a thread is about to shut down. When + /// the main thread for an application is about to be shut down, + /// this event will be raised first, followed by an ApplicationExit + /// event. + /// + public static event EventHandler ThreadExit { + add { + AddEventHandler(EVENT_THREADEXIT, value); + } + remove { + RemoveEventHandler(EVENT_THREADEXIT, value); + } + } + + /// + /// + /// Called immediately before we begin pumping messages for a modal message loop. + /// Does not actually start a message pump; that's the caller's responsibility. + /// + /// + internal static void BeginModalMessageLoop() { + ThreadContext.FromCurrent().BeginModalMessageLoop(null); + } + + /// + /// + /// Processes + /// all Windows messages currently in the message queue. + /// + public static void DoEvents() { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopDoEvents, null); + } + + /// + /// + internal static void DoEventsModal() { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopDoEventsModal, null); + } + + /// + /// + /// + /// Enables visual styles for all subsequent Application.Run() and CreateHandle() calls. + /// Uses the default theming manifest file shipped with the redist. + /// + /// + public static void EnableVisualStyles() { + string assemblyLoc = null; + + // SECREVIEW : This Assert is ok, getting the module path is a safe operation, + // the result is provided by the system. + // + FileIOPermission fiop = new FileIOPermission(PermissionState.None); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + try { + assemblyLoc = typeof(Application).Assembly.Location; + } + finally { + CodeAccessPermission.RevertAssert(); + } + // Pull manifest from our resources + if (assemblyLoc != null) { + EnableVisualStylesInternal(assemblyLoc, 101); + } + } + + /// + /// Internal version ***WITHOUT SECURITY DEMAND***. + /// + private static void EnableVisualStylesInternal(string assemblyFileName, int nativeResourceID) { + //Note that if the following call fails, we don't throw an exception. + //Theming scope won't work, thats all. + useVisualStyles = UnsafeNativeMethods.ThemingScope.CreateActivationContext(assemblyFileName, nativeResourceID); + } + + /// + /// + /// Called immediately after we stop pumping messages for a modal message loop. + /// Does not actually end the message pump itself. + /// + /// + internal static void EndModalMessageLoop() { + ThreadContext.FromCurrent().EndModalMessageLoop(null); + } + + /// + /// + /// Overload of Exit that does not care about e.Cancel. + /// + public static void Exit() { + Exit(null); + } + + /// + /// + /// Informs all message pumps that they are to terminate and + /// then closes all application windows after the messages have been processed. + /// e.Cancel indicates whether any of the open forms cancelled the exit call. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers") + ] + public static void Exit(CancelEventArgs e) { + Assembly entryAssembly = Assembly.GetEntryAssembly(); + Assembly callingAssembly = Assembly.GetCallingAssembly(); + if (entryAssembly == null || callingAssembly == null || !entryAssembly.Equals(callingAssembly)) + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded"); + IntSecurity.AffectThreadBehavior.Demand(); + } + bool cancelExit = ExitInternal(); + if (e != null) { + e.Cancel = cancelExit; + } + } + + /// + /// Private version of Exit which does not do any security checks. + /// + private static bool ExitInternal() { + bool cancelExit = false; + lock (internalSyncObject) { + if (exiting) { + return false; + } + exiting = true; + + try + { + // Raise the FormClosing and FormClosed events for each open form + if (forms != null) { + foreach (Form f in OpenFormsInternal) { + if (f.RaiseFormClosingOnAppExit()) { + cancelExit = true; + break; // quit the loop as soon as one form refuses to close + } + } + } + if (!cancelExit) { + if (forms != null) { + while (OpenFormsInternal.Count > 0) { + OpenFormsInternal[0].RaiseFormClosedOnAppExit(); // OnFormClosed removes the form from the FormCollection + } + } + ThreadContext.ExitApplication(); + } + } + finally { + exiting = false; + } + } + return cancelExit; + } + + /// + /// + /// Exits the message loop on the + /// current thread and closes all windows on the thread. + /// + public static void ExitThread() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded"); + IntSecurity.AffectThreadBehavior.Demand(); + + ExitThreadInternal(); + } + + private static void ExitThreadInternal() { + ThreadContext context = ThreadContext.FromCurrent(); + if (context.ApplicationContext != null) { + context.ApplicationContext.ExitThread(); + } + else { + context.Dispose(true); + } + } + + // When a Form receives a WM_ACTIVATE message, it calls this method so we can do the + // appropriate MsoComponentManager activation magic + internal static void FormActivated(bool modal, bool activated) { + if (modal) { + return; + } + + ThreadContext.FromCurrent().FormActivated(activated); + } + + /// + /// + /// Retrieves the FileVersionInfo associated with the main module for + /// the application. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static FileVersionInfo GetAppFileVersionInfo() { + lock (internalSyncObject) { + if (appFileVersion == null) { + Type t = GetAppMainType(); + if (t != null) { + // SECREVIEW : This Assert is ok, getting the module's version is a safe operation, + // the result is provided by the system. + // + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read; + fiop.Assert(); + + try { + appFileVersion = FileVersionInfo.GetVersionInfo(t.Module.FullyQualifiedName); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + else { + appFileVersion = FileVersionInfo.GetVersionInfo(ExecutablePath); + } + } + } + + return(FileVersionInfo)appFileVersion; + } + + /// + /// + /// Retrieves the Type that contains the "Main" method. + /// + private static Type GetAppMainType() { + lock(internalSyncObject) { + if (mainType == null) { + Assembly exe = Assembly.GetEntryAssembly(); + + // Get Main type...This doesn't work in MC++ because Main is a global function and not + // a class static method (it doesn't belong to a Type). + if (exe != null) { + mainType = exe.EntryPoint.ReflectedType; + } + } + } + + return mainType; + } + + /// + /// Locates a thread context given a window handle. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + private static ThreadContext GetContextForHandle(HandleRef handle) { + + int pid; + int id = SafeNativeMethods.GetWindowThreadProcessId(handle, out pid); + ThreadContext cxt = ThreadContext.FromId(id); + Debug.Assert(cxt != null, "No thread context for handle. This is expected if you saw a previous assert about the handle being invalid."); + return cxt; + } + + /// + /// + /// Returns a string that is the combination of the + /// basePath + CompanyName + ProducName + ProductVersion. This + /// will also create the directory if it doesn't exist. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static string GetDataPath(String basePath) { + string template = @"{0}\{1}\{2}\{3}"; + + string company = CompanyName; + string product = ProductName; + string version = ProductVersion; + + string path = string.Format(CultureInfo.CurrentCulture, template, new object[] {basePath, company, product, version}); + lock(internalSyncObject) { + if (!Directory.Exists(path)) { + Directory.CreateDirectory(path); + } + } + return path; + } + + + /// + /// + /// Called by the last thread context before it shuts down. + /// + private static void RaiseExit() { + if (eventHandlers != null) { + Delegate exit = eventHandlers[EVENT_APPLICATIONEXIT]; + if (exit != null) + ((EventHandler)exit)(null, EventArgs.Empty); + } + } + + /// + /// + /// Called by the each thread context before it shuts down. + /// + private static void RaiseThreadExit() { + if (eventHandlers != null) { + Delegate exit = eventHandlers[EVENT_THREADEXIT]; + if (exit != null) { + ((EventHandler)exit)(null, EventArgs.Empty); + } + } + } + + + /// + /// + /// + /// "Parks" the given HWND to a temporary HWND. This allows WS_CHILD windows to + /// be parked. + /// + internal static void ParkHandle(HandleRef handle, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { + Debug.Assert(UnsafeNativeMethods.IsWindow(handle), "Handle being parked is not a valid window handle"); + Debug.Assert(((int)UnsafeNativeMethods.GetWindowLong(handle, NativeMethods.GWL_STYLE) & NativeMethods.WS_CHILD) != 0, "Only WS_CHILD windows should be parked."); + + ThreadContext cxt = GetContextForHandle(handle); + if (cxt != null) { + cxt.GetParkingWindow(dpiAwarenessContext).ParkHandle(handle); + } + } + + /// + /// Park control handle on a parkingwindow that has matching DpiAwareness. + /// + /// create params for control handle + /// dpi awareness + internal static void ParkHandle(CreateParams cp, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { + + ThreadContext cxt = ThreadContext.FromCurrent(); + if (cxt != null) { + cp.Parent = cxt.GetParkingWindow(dpiAwarenessContext).Handle; + } + } + + /// + /// + /// + /// Initializes OLE on the current thread. + /// + /// + public static System.Threading.ApartmentState OleRequired() { + return ThreadContext.FromCurrent().OleRequired(); + } + + /// + /// + /// Raises the event. + /// + public static void OnThreadException(Exception t) { + ThreadContext.FromCurrent().OnThreadException(t); + } + + /// + /// + /// + /// "Unparks" the given HWND to a temporary HWND. This allows WS_CHILD windows to + /// be parked. + /// + internal static void UnparkHandle(HandleRef handle, DpiAwarenessContext context) { + ThreadContext cxt = GetContextForHandle(handle); + if (cxt != null) { + cxt.GetParkingWindow(context).UnparkHandle(handle); + } + } + + /// + /// + /// Raises the Idle event. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers"), + SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate") + ] + public static void RaiseIdle(EventArgs e) { + ThreadContext current = ThreadContext.FromCurrent(); + if (current.idleHandler != null) { + current.idleHandler(Thread.CurrentThread, e); + } + } + + /// + /// + /// Removes a message + /// filter from the application's message pump. + /// + public static void RemoveMessageFilter(IMessageFilter value) { + ThreadContext.FromCurrent().RemoveMessageFilter(value); + } + + /// + /// + /// Restarts the application. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + public static void Restart() + { + if (Assembly.GetEntryAssembly() == null) + { + throw new NotSupportedException(SR.GetString(SR.RestartNotSupported)); + } + + bool hrefExeCase = false; + + Process process = Process.GetCurrentProcess(); + Debug.Assert(process != null); + if (String.Equals(process.MainModule.ModuleName, IEEXEC, StringComparison.OrdinalIgnoreCase)) + { + string clrPath = string.Empty; + + // SECREVIEW : This Assert is ok, getting the module path is a safe operation, + // the result is provided by the system. + // + new FileIOPermission(PermissionState.Unrestricted).Assert(); + try + { + clrPath = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + if (String.Equals(clrPath + "\\" + IEEXEC, process.MainModule.FileName, StringComparison.OrdinalIgnoreCase)) + { + // HRef exe case + hrefExeCase = true; + ExitInternal(); + string launchUrl = AppDomain.CurrentDomain.GetData("APP_LAUNCH_URL") as string; + if (launchUrl != null) + { + Process.Start(process.MainModule.FileName, launchUrl); + } + } + } + + if (!hrefExeCase) + { + if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed) + { + // ClickOnce app case + string appFullName = System.Deployment.Application.ApplicationDeployment.CurrentDeployment.UpdatedApplicationFullName; + UInt32 hostType = (UInt32) Application.ClickOnceUtility.GetHostTypeFromMetaData(appFullName); + ExitInternal(); + UnsafeNativeMethods.CorLaunchApplication(hostType, appFullName, 0, null, 0, null, new UnsafeNativeMethods.PROCESS_INFORMATION()); + } + else + { + // Regular app case + String[] arguments = Environment.GetCommandLineArgs(); + Debug.Assert(arguments != null && arguments.Length > 0); + StringBuilder sb = new StringBuilder((arguments.Length - 1) * 16); + for (int argumentIndex = 1; argumentIndex < arguments.Length - 1; argumentIndex++) + { + sb.Append('"'); + sb.Append(arguments[argumentIndex]); + sb.Append("\" "); + } + if (arguments.Length > 1) + { + sb.Append('"'); + sb.Append(arguments[arguments.Length - 1]); + sb.Append('"'); + } + ProcessStartInfo currentStartInfo = Process.GetCurrentProcess().StartInfo; + currentStartInfo.FileName = Application.ExecutablePath; + // Microsoft: use this according to spec. + // String executable = Assembly.GetEntryAssembly().CodeBase; + // Debug.Assert(executable != null); + if (sb.Length > 0) + { + currentStartInfo.Arguments = sb.ToString(); + } + ExitInternal(); + Process.Start(currentStartInfo); + } + } + } + + /// + /// + /// Begins running a + /// standard + /// application message loop on the current thread, + /// without a form. + /// + public static void Run() { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopMain, new ApplicationContext()); + } + + /// + /// + /// Begins running a standard application message loop on the current + /// thread, and makes the specified form visible. + /// + public static void Run(Form mainForm) { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopMain, new ApplicationContext(mainForm)); + } + + /// + /// + /// Begins running a + /// standard + /// application message loop on the current thread, + /// without a form. + /// + public static void Run(ApplicationContext context) { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopMain, context); + } + + /// + /// + /// Runs a modal dialog. This starts a special type of message loop that runs until + /// the dialog has a valid DialogResult. This is called internally by a form + /// when an application calls System.Windows.Forms.Form.ShowDialog(). + /// + /// + internal static void RunDialog(Form form) { + ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopModalForm, new ModalApplicationContext(form)); + } + + /// + /// + /// Sets the static UseCompatibleTextRenderingDefault field on Control to the value passed in. + /// This switch determines the default text rendering engine to use by some controls that support + /// switching rendering engine. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public static void SetCompatibleTextRenderingDefault(bool defaultValue) + { + if (NativeWindow.AnyHandleCreated) + { + throw new InvalidOperationException(SR.GetString(SR.Win32WindowAlreadyCreated)); + } + Control.UseCompatibleTextRenderingDefault = defaultValue; + } + + /// + /// + /// Sets the suspend/hibernate state of the machine. + /// Returns true if the call succeeded, else false. + /// + public static bool SetSuspendState(PowerState state, bool force, bool disableWakeEvent) { + IntSecurity.AffectMachineState.Demand(); + return UnsafeNativeMethods.SetSuspendState(state == PowerState.Hibernate, force, disableWakeEvent); + } + + /// + /// Overload version of SetUnhandledExceptionMode that sets the UnhandledExceptionMode + /// mode at the current thread level. + /// + public static void SetUnhandledExceptionMode(UnhandledExceptionMode mode) + { + SetUnhandledExceptionMode(mode, true /*threadScope*/); + } + + /// + /// This method can be used to modify the exception handling behavior of + /// NativeWindow. By default, NativeWindow will detect if an application + /// is running under a debugger, or is running on a machine with a debugger + /// installed. In this case, an unhandled exception in the NativeWindow's + /// WndProc method will remain unhandled so the debugger can trap it. If + /// there is no debugger installed NativeWindow will trap the exception + /// and route it to the Application class's unhandled exception filter. + /// + /// You can control this behavior via a config file, or directly through + /// code using this method. Setting the unhandled exception mode does + /// not change the behavior of any NativeWindow objects that are currently + /// connected to window handles; it only affects new handle connections. + /// + /// The parameter threadScope defines the scope of the setting: either + /// the current thread or the application. + /// When a thread exception mode isn't UnhandledExceptionMode.Automatic, it takes + /// precedence over the application exception mode. + /// + public static void SetUnhandledExceptionMode(UnhandledExceptionMode mode, bool threadScope) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded"); + IntSecurity.AffectThreadBehavior.Demand(); + + NativeWindow.SetUnhandledExceptionModeInternal(mode, threadScope); + } + + // Exposes GetHostTypeFromMetaData provided by the ClickOnce team. + // Used to restart ClickOnce applications in Application.Restart(). + private class ClickOnceUtility + { + public enum HostType + { + Default = 0x0, + AppLaunch = 0x1, + CorFlag = 0x2 + } + + private ClickOnceUtility() + { + } + + public static HostType GetHostTypeFromMetaData(string appFullName) + { + HostType ht = HostType.Default; + try + { + // Convert into IDefinitionAppId. + IDefinitionAppId defAppId = IsolationInterop.AppIdAuthority.TextToDefinition(0, appFullName); + bool isFullTrust = GetPropertyBoolean(defAppId, "IsFullTrust"); + ht = isFullTrust ? HostType.CorFlag : HostType.AppLaunch; + } + catch + { + // Eating exceptions. IsFullTrust metadata is not present. + } + return ht; + } + + // Get metadata property as boolean. + private static bool GetPropertyBoolean(IDefinitionAppId appId, string propName) + { + string boolStr = GetPropertyString(appId, propName); + if (string.IsNullOrEmpty(boolStr)) + { + return false; + } + try + { + return Convert.ToBoolean(boolStr, CultureInfo.InvariantCulture); + } + catch + { + return false; + } + } + + // Get metadata property as string. + private static string GetPropertyString(IDefinitionAppId appId, string propName) + { + // Retrieve property and convert to string. + byte[] bytes = IsolationInterop.UserStore.GetDeploymentProperty(0, appId, + InstallReference, new Guid("2ad613da-6fdb-4671-af9e-18ab2e4df4d8"), propName); + + // Check for valid Unicode string. Must end with L'\0'. + int length = bytes.Length; + if ((length == 0) || ((bytes.Length % 2) != 0) || + (bytes[length - 2] != 0) || (bytes[length - 1] != 0)) + { + return null; + } + return Encoding.Unicode.GetString(bytes, 0, length - 2); + } + + // Get the ClickOnce-specific constant install reference. + private static StoreApplicationReference InstallReference + { + get + { + return new StoreApplicationReference( + IsolationInterop.GUID_SXS_INSTALL_REFERENCE_SCHEME_OPAQUESTRING, + "{3f471841-eef2-47d6-89c0-d028f03a4ad5}", null); + } + } + } + + /// + /// + /// This is our implementation of the MSO ComponentManager. The Componoent Manager is + /// an object that is responsible for handling all message loop activity in a process. + /// The idea is that someone in the process implements the component manager and then + /// anyone who wants access to the message loop can get to it. We implement this + /// so we have good interop with office and VS. The first time we need a + /// component manager, we search the OLE message filter for one. If that fails, we + /// create our own and install it in the message filter. + /// + /// This class is not used when running inside the Visual Studio shell. + /// + private class ComponentManager : UnsafeNativeMethods.IMsoComponentManager + { + + // ComponentManager instance data. + // + private class ComponentHashtableEntry { + public UnsafeNativeMethods.IMsoComponent component; + public NativeMethods.MSOCRINFOSTRUCT componentInfo; + } + + private Hashtable oleComponents; + private int cookieCounter = 0; + private UnsafeNativeMethods.IMsoComponent activeComponent = null; + private UnsafeNativeMethods.IMsoComponent trackingComponent = null; + private int currentState = 0; + + private Hashtable OleComponents { + get { + if (oleComponents == null) { + oleComponents = new Hashtable(); + cookieCounter = 0; + } + + return oleComponents; + } + } + + /// + /// + /// Return in *ppvObj an implementation of interface iid for service + /// guidService (same as IServiceProvider::QueryService). + /// Return NOERROR if the requested service is supported, otherwise return + /// NULL in *ppvObj and an appropriate error (eg E_FAIL, E_NOINTERFACE). + /// + int UnsafeNativeMethods.IMsoComponentManager.QueryService( + ref Guid guidService, + ref Guid iid, + out object ppvObj) { + + ppvObj = null; + return NativeMethods.E_NOINTERFACE; + + } + + /// + /// + /// Standard FDebugMessage method. + /// Since IMsoComponentManager is a reference counted interface, + /// MsoDWGetChkMemCounter should be used when processing the + /// msodmWriteBe message. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FDebugMessage( + IntPtr hInst, + int msg, + IntPtr wparam, + IntPtr lparam) { + + return true; + } + + /// + /// + /// Register component piComponent and its registration info pcrinfo with + /// this component manager. Return in *pdwComponentID a cookie which will + /// identify the component when it calls other IMsoComponentManager + /// methods. + /// Return TRUE if successful, FALSE otherwise. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FRegisterComponent(UnsafeNativeMethods.IMsoComponent component, + NativeMethods.MSOCRINFOSTRUCT pcrinfo, + out IntPtr dwComponentID) { + + // Construct Hashtable entry for this component + // + ComponentHashtableEntry entry = new ComponentHashtableEntry(); + entry.component = component; + entry.componentInfo = pcrinfo; + OleComponents.Add(++cookieCounter, entry); + + // Return the cookie + // + dwComponentID = (IntPtr)cookieCounter; + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager: Component registered. ID: " + cookieCounter.ToString(CultureInfo.InvariantCulture)); + return true; + } + + /// + /// + /// Undo the registration of the component identified by dwComponentID + /// (the cookie returned from the FRegisterComponent method). + /// Return TRUE if successful, FALSE otherwise. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FRevokeComponent(IntPtr dwComponentID) { + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager: Revoking component " + dwLocalComponentID.ToString(CultureInfo.InvariantCulture)); + + ComponentHashtableEntry entry = (ComponentHashtableEntry)OleComponents[dwLocalComponentID]; + if (entry == null) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Compoenent not registered."); + return false; + } + + if (entry.component == activeComponent) { + activeComponent = null; + } + if (entry.component == trackingComponent) { + trackingComponent = null; + } + + OleComponents.Remove(dwLocalComponentID); + + return true; + + } + + /// + /// + /// Update the registration info of the component identified by + /// dwComponentID (the cookie returned from FRegisterComponent) with the + /// new registration information pcrinfo. + /// Typically this is used to update the idle time registration data, but + /// can be used to update other registration data as well. + /// Return TRUE if successful, FALSE otherwise. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FUpdateComponentRegistration( + IntPtr dwComponentID, + NativeMethods.MSOCRINFOSTRUCT info + ) { + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + // Update the registration info + // + ComponentHashtableEntry entry = (ComponentHashtableEntry)OleComponents[dwLocalComponentID]; + if (entry == null) { + return false; + } + + entry.componentInfo = info; + + return true; + } + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) has been activated. + /// The active component gets the chance to process messages before they + /// are dispatched (via IMsoComponent::FPreTranslateMessage) and typically + /// gets first chance at idle time after the host. + /// This method fails if another component is already Exclusively Active. + /// In this case, FALSE is returned and SetLastError is set to + /// msoerrACompIsXActive (comp usually need not take any special action + /// in this case). + /// Return TRUE if successful. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FOnComponentActivate(IntPtr dwComponentID) { + + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager: Component activated. ID: " + dwLocalComponentID.ToString(CultureInfo.InvariantCulture)); + + ComponentHashtableEntry entry = (ComponentHashtableEntry)OleComponents[dwLocalComponentID]; + if (entry == null) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "*** Component not registered ***"); + return false; + } + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "New active component is : " + entry.component.ToString()); + activeComponent = entry.component; + return true; + } + + /// + /// + /// Called to inform component manager that component identified by + /// dwComponentID (cookie returned from FRegisterComponent) wishes + /// to perform a tracking operation (such as mouse tracking). + /// The component calls this method with fTrack == TRUE to begin the + /// tracking operation and with fTrack == FALSE to end the operation. + /// During the tracking operation the component manager routes messages + /// to the tracking component (via IMsoComponent::FPreTranslateMessage) + /// rather than to the active component. When the tracking operation ends, + /// the component manager should resume routing messages to the active + /// component. + /// Note: component manager should perform no idle time processing during a + /// tracking operation other than give the tracking component idle + /// time via IMsoComponent::FDoIdle. + /// Note: there can only be one tracking component at a time. + /// Return TRUE if successful, FALSE otherwise. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FSetTrackingComponent(IntPtr dwComponentID, bool fTrack) { + + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + ComponentHashtableEntry entry = (ComponentHashtableEntry)OleComponents[dwLocalComponentID]; + if (entry == null) { + return false; + } + + if (entry.component == trackingComponent ^ fTrack) { + return false; + } + + if (fTrack) { + trackingComponent = entry.component; + } + else { + trackingComponent = null; + } + + return true; + } + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) is entering the state + /// identified by uStateID (msocstateXXX value). (For convenience when + /// dealing with sub CompMgrs, the host can call this method passing 0 for + /// dwComponentID.) + /// Component manager should notify all other interested components within + /// the state context indicated by uContext (a msoccontextXXX value), + /// excluding those within the state context of a CompMgr in rgpicmExclude, + /// via IMsoComponent::OnEnterState (see "Comments on State Contexts", + /// above). + /// Component Manager should also take appropriate action depending on the + /// value of uStateID (see msocstate comments, above). + /// dwReserved is reserved for future use and should be zero. + /// + /// rgpicmExclude (can be NULL) is an array of cpicmExclude CompMgrs (can + /// include root CompMgr and/or sub CompMgrs); components within the state + /// context of a CompMgr appearing in this array should NOT be notified of + /// the state change (note: if uContext is msoccontextMine, the only + /// CompMgrs in rgpicmExclude that are checked for exclusion are those that + /// are sub CompMgrs of this Component Manager, since all other CompMgrs + /// are outside of this Component Manager's state context anyway.) + /// + /// Note: Calls to this method are symmetric with calls to + /// FOnComponentExitState. + /// That is, if n OnComponentEnterState calls are made, the component is + /// considered to be in the state until n FOnComponentExitState calls are + /// made. Before revoking its registration a component must make a + /// sufficient number of FOnComponentExitState calls to offset any + /// outstanding OnComponentEnterState calls it has made. + /// + /// Note: inplace objects should not call this method with + /// uStateID == msocstateModal when entering modal state. Such objects + /// should call IOleInPlaceFrame::EnableModeless instead. + /// + void UnsafeNativeMethods.IMsoComponentManager.OnComponentEnterState( + IntPtr dwComponentID, + int uStateID, + int uContext, + int cpicmExclude, + int rgpicmExclude, // IMsoComponentManger** + int dwReserved) { + + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + currentState |= uStateID; + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager: Component enter state. ID: " + dwLocalComponentID.ToString(CultureInfo.InvariantCulture) + " state: " + uStateID.ToString(CultureInfo.InvariantCulture)); + + if (uContext == NativeMethods.MSOCM.msoccontextAll || uContext == NativeMethods.MSOCM.msoccontextMine) { + + Debug.Indent(); + + // We should notify all components we contain that the state has changed. + // + foreach (ComponentHashtableEntry entry in OleComponents.Values) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Notifying " + entry.component.ToString()); + entry.component.OnEnterState(uStateID, true); + } + + Debug.Unindent(); + } + } + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) is exiting the state + /// identified by uStateID (a msocstateXXX value). (For convenience when + /// dealing with sub CompMgrs, the host can call this method passing 0 for + /// dwComponentID.) + /// uContext, cpicmExclude, and rgpicmExclude are as they are in + /// OnComponentEnterState. + /// Component manager should notify all appropriate interested components + /// (taking into account uContext, cpicmExclude, rgpicmExclude) via + /// IMsoComponent::OnEnterState (see "Comments on State Contexts", above). + /// Component Manager should also take appropriate action depending on + /// the value of uStateID (see msocstate comments, above). + /// Return TRUE if, at the end of this call, the state is still in effect + /// at the root of this component manager's state context + /// (because the host or some other component is still in the state), + /// otherwise return FALSE (ie. return what FInState would return). + /// Caller can normally ignore the return value. + /// + /// Note: n calls to this method are symmetric with n calls to + /// OnComponentEnterState (see OnComponentEnterState comments, above). + /// + bool UnsafeNativeMethods.IMsoComponentManager.FOnComponentExitState( + IntPtr dwComponentID, + int uStateID, + int uContext, + int cpicmExclude, + int rgpicmExclude // IMsoComponentManager** + ) { + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + currentState &= ~uStateID; + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager: Component exit state. ID: " + dwLocalComponentID.ToString(CultureInfo.InvariantCulture) + " state: " + uStateID.ToString(CultureInfo.InvariantCulture)); + + if (uContext == NativeMethods.MSOCM.msoccontextAll || uContext == NativeMethods.MSOCM.msoccontextMine) { + + Debug.Indent(); + + // We should notify all components we contain that the state has changed. + // + foreach (ComponentHashtableEntry entry in OleComponents.Values) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Notifying " + entry.component.ToString()); + entry.component.OnEnterState(uStateID, false); + } + + Debug.Unindent(); + } + + return false; + } + + /// + /// + /// Return TRUE if the state identified by uStateID (a msocstateXXX value) + /// is in effect at the root of this component manager's state context, + /// FALSE otherwise (see "Comments on State Contexts", above). + /// pvoid is reserved for future use and should be NULL. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FInState(int uStateID, IntPtr pvoid) { + return(currentState & uStateID) != 0; + } + + /// + /// + /// Called periodically by a component during IMsoComponent::FDoIdle. + /// Return TRUE if component can continue its idle time processing, + /// FALSE if not (in which case component returns from FDoIdle.) + /// + bool UnsafeNativeMethods.IMsoComponentManager.FContinueIdle() { + + // Essentially, if we have a message on queue, then don't continue + // idle processing. + // + NativeMethods.MSG msg = new NativeMethods.MSG(); + return !UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE); + } + + /// + /// + /// Component identified by dwComponentID (cookie returned from + /// FRegisterComponent) wishes to push a message loop for reason uReason. + /// uReason is one the values from the msoloop enumeration (above). + /// pvLoopData is data private to the component. + /// The component manager should push its message loop, + /// calling IMsoComponent::FContinueMessageLoop(uReason, pvLoopData) + /// during each loop iteration (see IMsoComponent::FContinueMessageLoop + /// comments). When IMsoComponent::FContinueMessageLoop returns FALSE, the + /// component manager terminates the loop. + /// Returns TRUE if component manager terminates loop because component + /// told it to (by returning FALSE from IMsoComponent::FContinueMessageLoop), + /// FALSE if it had to terminate the loop for some other reason. In the + /// latter case, component should perform any necessary action (such as + /// cleanup). + /// + bool UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop( + IntPtr dwComponentID, + int reason, + int pvLoopData // PVOID + ) { + + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + // Hold onto old state to allow restore before we exit... + // + int currentLoopState = currentState; + bool continueLoop = true; + + if (!OleComponents.ContainsKey(dwLocalComponentID)) { + return false; + } + + UnsafeNativeMethods.IMsoComponent prevActive = this.activeComponent; + + try { + // Execute the message loop until the active component tells us to stop. + // + NativeMethods.MSG msg = new NativeMethods.MSG(); + NativeMethods.MSG[] rgmsg = new NativeMethods.MSG[] {msg}; + bool unicodeWindow = false; + UnsafeNativeMethods.IMsoComponent requestingComponent; + + ComponentHashtableEntry entry = (ComponentHashtableEntry)OleComponents[dwLocalComponentID]; + if (entry == null) { + return false; + } + + + + requestingComponent = entry.component; + + this.activeComponent = requestingComponent; + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Pushing message loop " + reason.ToString(CultureInfo.InvariantCulture)); + Debug.Indent(); + + while (continueLoop) { + + // Determine the component to route the message to + // + UnsafeNativeMethods.IMsoComponent component; + + if (trackingComponent != null) { + component = trackingComponent; + } + else if (activeComponent != null) { + component = activeComponent; + } + else { + component = requestingComponent; + } + + bool peeked = UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE); + + if (peeked) { + + rgmsg[0] = msg; + continueLoop = component.FContinueMessageLoop(reason, pvLoopData, rgmsg); + + // If the component wants us to process the message, do it. + // The component manager hosts windows from many places. We must be sensitive + // to ansi / Unicode windows here. + // + if (continueLoop) { + if (msg.hwnd != IntPtr.Zero && SafeNativeMethods.IsWindowUnicode(new HandleRef(null, msg.hwnd))) { + unicodeWindow = true; + UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0); + } + else { + unicodeWindow = false; + UnsafeNativeMethods.GetMessageA(ref msg, NativeMethods.NullHandleRef, 0, 0); + } + + if (msg.message == NativeMethods.WM_QUIT) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Normal message loop termination"); + + Application.ThreadContext.FromCurrent().DisposeThreadWindows(); + + if (reason != NativeMethods.MSOCM.msoloopMain) { + UnsafeNativeMethods.PostQuitMessage((int)msg.wParam); + } + + continueLoop = false; + break; + } + + // Now translate and dispatch the message. + // + // Reading through the rather sparse documentation, + // it seems we should only call FPreTranslateMessage + // on the active component. But frankly, I'm afraid of what that might break. + // See ASURT 29415 for more background. + if (!component.FPreTranslateMessage(ref msg)) { + UnsafeNativeMethods.TranslateMessage(ref msg); + if (unicodeWindow) { + UnsafeNativeMethods.DispatchMessageW(ref msg); + } + else { + UnsafeNativeMethods.DispatchMessageA(ref msg); + } + } + } + } + else { + + // If this is a DoEvents loop, then get out. There's nothing left + // for us to do. + // + if (reason == NativeMethods.MSOCM.msoloopDoEvents || + reason == NativeMethods.MSOCM.msoloopDoEventsModal) { + break; + } + + // Nothing is on the message queue. Perform idle processing + // and then do a WaitMessage. + // + bool continueIdle = false; + + if (OleComponents != null) { + IEnumerator enumerator = OleComponents.Values.GetEnumerator(); + + while (enumerator.MoveNext()) { + ComponentHashtableEntry idleEntry = (ComponentHashtableEntry)enumerator.Current; + continueIdle |= idleEntry.component.FDoIdle(-1); + } + } + + // give the component one more chance to terminate the + // message loop. + // + continueLoop = component.FContinueMessageLoop(reason, pvLoopData, null); + + if (continueLoop) { + if (continueIdle) { + // If someone has asked for idle time, give it to them. However, + // don't cycle immediately; wait up to 100ms. Why? Because we don't + // want someone to attach to idle, forget to detach, and then ---- + // the CPU. For Windows Forms this generally isn't an issue because + // our component always returns false from its idle request + UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 100, NativeMethods.QS_ALLINPUT, NativeMethods.MWMO_INPUTAVAILABLE); + } + else { + // We should call GetMessage here, but we cannot because + // the component manager requires that we notify the + // active component before we pull the message off the + // queue. This is a bit of a problem, because WaitMessage + // waits for a NEW message to appear on the queue. If a + // message appeared between processing and now WaitMessage + // would wait for the next message. We minimize this here + // by calling PeekMessage. + // + if (!UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE)) { + UnsafeNativeMethods.WaitMessage(); + } + } + } + } + } + + Debug.Unindent(); + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : message loop " + reason.ToString(CultureInfo.InvariantCulture) + " complete."); + } + finally { + currentState = currentLoopState; + this.activeComponent = prevActive; + } + + return !continueLoop; + } + + /// + /// + /// Cause the component manager to create a "sub" component manager, which + /// will be one of its children in the hierarchical tree of component + /// managers used to maintiain state contexts (see "Comments on State + /// Contexts", above). + /// piunkOuter is the controlling unknown (can be NULL), riid is the + /// desired IID, and *ppvObj returns the created sub component manager. + /// piunkServProv (can be NULL) is a ptr to an object supporting + /// IServiceProvider interface to which the created sub component manager + /// will delegate its IMsoComponentManager::QueryService calls. + /// (see objext.h or docobj.h for definition of IServiceProvider). + /// Returns TRUE if successful. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FCreateSubComponentManager( + object punkOuter, + object punkServProv, + ref Guid riid, + out IntPtr ppvObj) { + + // We do not support sub component managers. + // + ppvObj = IntPtr.Zero; + return false; + } + + /// + /// + /// Return in *ppicm an AddRef'ed ptr to this component manager's parent + /// in the hierarchical tree of component managers used to maintain state + /// contexts (see "Comments on State Contexts", above). + /// Returns TRUE if the parent is returned, FALSE if no parent exists or + /// some error occurred. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FGetParentComponentManager(out UnsafeNativeMethods.IMsoComponentManager ppicm) { + ppicm = null; + return false; + } + + /// + /// + /// Return in *ppic an AddRef'ed ptr to the current active or tracking + /// component (as indicated by dwgac (a msogacXXX value)), and + /// its registration information in *pcrinfo. ppic and/or pcrinfo can be + /// NULL if caller is not interested these values. If pcrinfo is not NULL, + /// caller should set pcrinfo->cbSize before calling this method. + /// Returns TRUE if the component indicated by dwgac exists, FALSE if no + /// such component exists or some error occurred. + /// dwReserved is reserved for future use and should be zero. + /// + bool UnsafeNativeMethods.IMsoComponentManager.FGetActiveComponent( + int dwgac, + UnsafeNativeMethods.IMsoComponent[] ppic, + NativeMethods.MSOCRINFOSTRUCT info, + int dwReserved) { + + UnsafeNativeMethods.IMsoComponent component = null; + + if (dwgac == NativeMethods.MSOCM.msogacActive) { + component = activeComponent; + } + else if (dwgac == NativeMethods.MSOCM.msogacTracking) { + component = trackingComponent; + } + else if (dwgac == NativeMethods.MSOCM.msogacTrackingOrActive) { + if (trackingComponent != null) { + component = trackingComponent; + } + else { + component = activeComponent; + } + } + else { + Debug.Fail("Unknown dwgac in FGetActiveComponent"); + } + + if (ppic != null) { + ppic[0] = component; + } + if (info != null && component != null) { + foreach(ComponentHashtableEntry entry in OleComponents.Values) { + if (entry.component == component) { + info = entry.componentInfo; + break; + } + } + } + + return component != null; + } + } + + /// + /// + /// This class is the embodiment of TLS for windows forms. We do not expose this to end users because + /// TLS is really just an unfortunate artifact of using Win 32. We want the world to be free + /// threaded. + /// + /// + internal sealed class ThreadContext : MarshalByRefObject, UnsafeNativeMethods.IMsoComponent { + + private const int STATE_OLEINITIALIZED = 0x00000001; + private const int STATE_EXTERNALOLEINIT = 0x00000002; + private const int STATE_INTHREADEXCEPTION = 0x00000004; + private const int STATE_POSTEDQUIT = 0x00000008; + private const int STATE_FILTERSNAPSHOTVALID = 0x00000010; + private const int STATE_TRACKINGCOMPONENT = 0x00000020; + private const int INVALID_ID = unchecked((int)0xFFFFFFFF); + + private static Hashtable contextHash = new Hashtable(); + + // When this gets to zero, we'll invoke a full garbage + // collect and check for root/window leaks. + // + private static object tcInternalSyncObject = new object(); + + private static int totalMessageLoopCount; + private static int baseLoopReason; + + [ ThreadStatic ] + private static ThreadContext currentThreadContext; + + internal ThreadExceptionEventHandler threadExceptionHandler; + internal EventHandler idleHandler; + internal EventHandler enterModalHandler; + internal EventHandler leaveModalHandler; + private ApplicationContext applicationContext; + + // Parking window list + private List parkingWindows = new List(); + private Control marshalingControl; + private CultureInfo culture; + private ArrayList messageFilters; + private IMessageFilter[] messageFilterSnapshot; + private int inProcessFilters = 0; + private IntPtr handle; + private int id; + private int messageLoopCount; + private int threadState; + private int modalCount; + + // used for correct restoration of focus after modality + private WeakReference activatingControlRef; + + // IMsoComponentManager stuff + // + private UnsafeNativeMethods.IMsoComponentManager componentManager; + private bool externalComponentManager; + private bool fetchingComponentManager; + + // IMsoComponent stuff + private int componentID = INVALID_ID; + private Form currentForm; + private ThreadWindows threadWindows; + private NativeMethods.MSG tempMsg = new NativeMethods.MSG(); + private int disposeCount; // To make sure that we don't allow + // reentrancy in Dispose() + + // Debug helper variable +#if DEBUG + private int debugModalCounter; +#endif + // Refer VSWhidbey 258748 + // Refer VS Whidbey 337882 + // We need to set this flag if we have started the ModalMessageLoop so that we dont create the ThreadWindows + // when the ComponentManager calls on us (as IMSOComponent) during the OnEnterState. + private bool ourModalLoop; + + // A private field on Application that stores the callback delegate + private MessageLoopCallback messageLoopCallback = null; + + /// + /// + /// Creates a new thread context object. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public ThreadContext() { + IntPtr address = IntPtr.Zero; + + UnsafeNativeMethods.DuplicateHandle(new HandleRef(null, SafeNativeMethods.GetCurrentProcess()), new HandleRef(null, SafeNativeMethods.GetCurrentThread()), + new HandleRef(null, SafeNativeMethods.GetCurrentProcess()), ref address, 0, false, + NativeMethods.DUPLICATE_SAME_ACCESS); + + handle = address; + + id = SafeNativeMethods.GetCurrentThreadId(); + messageLoopCount = 0; + currentThreadContext = this; + contextHash[id] = this; + } + + public ApplicationContext ApplicationContext { + get { + return applicationContext; + } + } + + /// + /// + /// Retrieves the component manager for this process. If there is no component manager + /// currently installed, we install our own. + /// + internal UnsafeNativeMethods.IMsoComponentManager ComponentManager { + get { + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Application.ComponentManager.Get:"); + + if (componentManager == null) { + + // The CLR is a good COM citizen and will pump messages when things are waiting. + // This is nice; it keeps the world responsive. But, it is also very hard for + // us because most of the code below causes waits, and the likelihood that + // a message will come in and need a component manager is very high. Recursing + // here is very very bad, and will almost certainly lead to application failure + // later on as we come out of the recursion. So, we guard it here and return + // null. EVERYONE who accesses the component manager must handle a NULL return! + // + if (fetchingComponentManager) { + return null; + } + + fetchingComponentManager = true; + try { + UnsafeNativeMethods.IMsoComponentManager msocm = null; + Application.OleRequired(); + + // Attempt to obtain the Host Application MSOComponentManager + // + IntPtr msgFilterPtr = (IntPtr)0; + + if (NativeMethods.Succeeded(UnsafeNativeMethods.CoRegisterMessageFilter(NativeMethods.NullHandleRef, ref msgFilterPtr)) && msgFilterPtr != (IntPtr)0) { + + IntPtr dummy = (IntPtr)0; + UnsafeNativeMethods.CoRegisterMessageFilter(new HandleRef(null, msgFilterPtr), ref dummy); + + object msgFilterObj = Marshal.GetObjectForIUnknown(msgFilterPtr); + Marshal.Release(msgFilterPtr); + + UnsafeNativeMethods.IOleServiceProvider sp = msgFilterObj as UnsafeNativeMethods.IOleServiceProvider; + if (sp != null) { + try { + IntPtr retval = IntPtr.Zero; + + // PERF ALERT (Microsoft): Using typeof() of COM object spins up COM at JIT time. + // Guid compModGuid = typeof(UnsafeNativeMethods.SMsoComponentManager).GUID; + // + Guid compModGuid = new Guid("000C060B-0000-0000-C000-000000000046"); + Guid iid = new Guid("{000C0601-0000-0000-C000-000000000046}"); + int hr = sp.QueryService( + ref compModGuid, + ref iid, + out retval); + + if (NativeMethods.Succeeded(hr) && retval != IntPtr.Zero) { + + // Now query for hte message filter. + + IntPtr pmsocm; + + try { + Guid IID_IMsoComponentManager = typeof(UnsafeNativeMethods.IMsoComponentManager).GUID; + hr = Marshal.QueryInterface(retval, ref IID_IMsoComponentManager, out pmsocm); + } + finally { + Marshal.Release(retval); + } + + if (NativeMethods.Succeeded(hr) && pmsocm != IntPtr.Zero) { + + // Ok, we have a native component manager. Hand this over to + // our broker object to get a proxy we can use + try { + msocm = ComponentManagerBroker.GetComponentManager(pmsocm); + } + finally { + Marshal.Release(pmsocm); + } + } + + if (msocm != null) { + + // If the resulting service is the same pUnk as the + // message filter (a common implementation technique), + // then we want to null msgFilterObj at this point so + // we don't call RelaseComObject on it below. That would + // also release the RCW for the component manager pointer. + if (msgFilterPtr == retval) { + msgFilterObj = null; + } + + externalComponentManager = true; + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Using MSO Component manager"); + + // Now attach app domain unload events so we can + // detect when we need to revoke our component + // + AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnDomainUnload); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnDomainUnload); + } + } + } + catch { + } + } + + if (msgFilterObj != null && Marshal.IsComObject(msgFilterObj)) { + Marshal.ReleaseComObject(msgFilterObj); + } + } + + // Otherwise, we implement component manager ourselves + // + if (msocm == null) { + msocm = new ComponentManager(); + externalComponentManager = false; + + // We must also store this back into the message filter for others + // to use. + // + // + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Using our own component manager"); + } + + if (msocm != null && componentID == INVALID_ID) { + // Finally, if we got a compnent manager, register ourselves with it. + // + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "Registering MSO component with the component manager"); + NativeMethods.MSOCRINFOSTRUCT info = new NativeMethods.MSOCRINFOSTRUCT(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MSOCRINFOSTRUCT)); + info.uIdleTimeInterval = 0; + info.grfcrf = NativeMethods.MSOCM.msocrfPreTranslateAll | NativeMethods.MSOCM.msocrfNeedIdleTime; + info.grfcadvf = NativeMethods.MSOCM.msocadvfModal; + + IntPtr localComponentID; + bool result = msocm.FRegisterComponent(this, info, out localComponentID); + componentID = unchecked((int)(long)localComponentID); + Debug.Assert(componentID != INVALID_ID, "Our ID sentinel was returned as a valid ID"); + + if (result && !(msocm is ComponentManager)) { + messageLoopCount++; + } + + Debug.Assert(result, "Failed to register WindowsForms with the ComponentManager -- DoEvents and modal dialogs will be broken. size: " + info.cbSize); + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager.FRegisterComponent returned " + result.ToString()); + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager.FRegisterComponent assigned a componentID == [0x" + Convert.ToString(componentID, 16) + "]"); + componentManager = msocm; + } + } + finally { + fetchingComponentManager = false; + } + } + + return componentManager; + } + } + + internal bool CustomThreadExceptionHandlerAttached { + get { + return threadExceptionHandler != null; + } + } + + /// + /// + /// Retrieves the actual parking form. This will demand create the parking window + /// if it needs to. + /// + /// + internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) { + + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + var parkingWindow = GetParkingWindowForContext(context); + if (parkingWindow == null) { +#if DEBUG + // if we use Debug.WriteLine instead of "if", we need extra security permissions + // to get the stack trace! + if (CoreSwitches.PerfTrack.Enabled) { + Debug.WriteLine("Creating parking form!"); + Debug.WriteLine(CoreSwitches.PerfTrack.Enabled, Environment.StackTrace); + } +#endif + + // SECREVIEW : We need to create the parking window. Since this is a top + // : level hidden form, we must assert this. However, the parking + // : window is complete internal to the assembly, so no one can + // : ever get at it. + // + IntSecurity.ManipulateWndProcAndHandles.Assert(); + try { + using (DpiHelper.EnterDpiAwarenessScope(context)) { + parkingWindow = new ParkingWindow(); + } + + parkingWindows.Add(parkingWindow); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + return parkingWindow; + } + } + + /// + /// Returns parking window that matches dpi awareness context. return null if not found. + /// + /// return matching parking window from list. returns null if not found + internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) { + + if (parkingWindows.Count == 0) { + return null; + } + + // Legacy OS/target framework scenario where ControlDpiContext is set to DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNSPECIFIED + // because of 'ThreadContextDpiAwareness' API unavailability or this feature is not enabled. + + if (!DpiHelper.EnableDpiChangedHighDpiImprovements || CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) { + + Debug.Assert(parkingWindows.Count == 1, "parkingWindows count can not be > 1 for legacy OS/target framework versions"); + return parkingWindows[0]; + } + + // Supported OS scenario. + foreach (var p in parkingWindows) { + if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(p.DpiAwarenessContext, context)) { + return p; + } + } + + // parking window is not yet created for the requested DpiAwarenessContext + return null; + } + + internal Control ActivatingControl { + get { + if ((activatingControlRef != null) && (activatingControlRef.IsAlive)) { + return activatingControlRef.Target as Control; + } + return null; + } + set { + if (value != null) { + activatingControlRef = new WeakReference(value); + } + else { + activatingControlRef = null; + } + } + } + + + /// + /// + /// Retrieves the actual parking form. This will demand create the MarshalingControl window + /// if it needs to. + /// + /// + internal Control MarshalingControl { + get { + lock (this) { + if (marshalingControl == null) { +#if DEBUG + // if we use Debug.WriteLine instead of "if", we need extra security permissions + // to get the stack trace! + if (CoreSwitches.PerfTrack.Enabled) { + Debug.WriteLine("Creating marshalling control!"); + Debug.WriteLine(CoreSwitches.PerfTrack.Enabled, Environment.StackTrace); + } +#endif + + marshalingControl = new System.Windows.Forms.Application.MarshalingControl(); + } + return marshalingControl; + } + } + } + + /// + /// + /// Allows you to setup a message filter for the application's message pump. This + /// installs the filter on the current thread. + /// + /// + internal void AddMessageFilter(IMessageFilter f) { + if (messageFilters == null) { + messageFilters = new ArrayList(); + } + if (f != null) { + SetState(STATE_FILTERSNAPSHOTVALID, false); + if (messageFilters.Count > 0 && f is IMessageModifyAndFilter) { + // insert the IMessageModifyAndFilter filters first + messageFilters.Insert(0, f); + } + else { + messageFilters.Add(f); + } + } + } + + // Called immediately before we begin pumping messages for a modal message loop. + internal void BeginModalMessageLoop(ApplicationContext context) { +#if DEBUG + debugModalCounter++; +#endif + // Set the ourModalLoop flag so that the "IMSOComponent.OnEnterState" is a NOOP since we started the ModalMessageLoop. + bool wasOurLoop = ourModalLoop; + ourModalLoop = true; + try + { + UnsafeNativeMethods.IMsoComponentManager cm = ComponentManager; + if (cm != null) { + cm.OnComponentEnterState((IntPtr)componentID, NativeMethods.MSOCM.msocstateModal, NativeMethods.MSOCM.msoccontextAll, 0, 0, 0); + } + } + finally + { + ourModalLoop = wasOurLoop; + } + // This will initialize the ThreadWindows with proper flags. + DisableWindowsForModalLoop(false, context); // onlyWinForms = false + + modalCount++; + + if (enterModalHandler != null && modalCount == 1) { + enterModalHandler(Thread.CurrentThread, EventArgs.Empty); + } + + } + + // Disables windows in preparation of going modal. If parameter is true, we disable all + // windows, if false, only windows forms windows (i.e., windows controlled by this MsoComponent). + // See also IMsoComponent.OnEnterState. + internal void DisableWindowsForModalLoop(bool onlyWinForms, ApplicationContext context) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Entering modal state"); + ThreadWindows old = threadWindows; + threadWindows = new ThreadWindows(onlyWinForms); + threadWindows.Enable(false); + threadWindows.previousThreadWindows = old; + + ModalApplicationContext modalContext = context as ModalApplicationContext; + if (modalContext != null) { + modalContext.DisableThreadWindows(true, onlyWinForms); + } + } + + /// + /// + /// Disposes this thread context object. Note that this will marshal to the owning thread. + /// + /// + internal void Dispose(bool postQuit) { + + // need to avoid multiple threads coming in here or we'll leak the thread + // handle. + // + lock (this) { + try { + if (disposeCount++ == 0) { // make sure that we are not reentrant + // Unravel our message loop. this will marshal us over to + // the right thread, making the dispose() method async. + if (messageLoopCount > 0 && postQuit) { + PostQuit(); + } + else { + bool ourThread = SafeNativeMethods.GetCurrentThreadId() == id; + + try { + // We can only clean up if we're being called on our + // own thread. + // + if (ourThread) { + + // If we had a component manager, detach from it. + // + if (componentManager != null) { + RevokeComponent(); + + // + + } + + // DisposeAssociatedComponents(); + DisposeThreadWindows(); + + try { + Application.RaiseThreadExit(); + } + finally { + if (GetState(STATE_OLEINITIALIZED) && !GetState(STATE_EXTERNALOLEINIT)) { + SetState(STATE_OLEINITIALIZED, false); + UnsafeNativeMethods.OleUninitialize(); + } + } + } + } + finally { + // We can always clean up this handle, though + // + if (handle != IntPtr.Zero) { + UnsafeNativeMethods.CloseHandle(new HandleRef(this, handle)); + handle = IntPtr.Zero; + } + + try { + if (totalMessageLoopCount == 0) { + Application.RaiseExit(); + } + } + finally { + lock (tcInternalSyncObject) { + contextHash.Remove((object)id); + } + if (currentThreadContext == this) { + currentThreadContext = null; + } + } + } + } + + GC.SuppressFinalize(this); + } + } + finally { + disposeCount--; + } + } + } + + /// + /// + /// Disposes of this thread's parking form. + /// + /// + private void DisposeParkingWindow() { + if (parkingWindows.Count != 0) { + + // We take two paths here. If we are on the same thread as + // the parking window, we can destroy its handle. If not, + // we just null it and let it GC. When it finalizes it + // will disconnect its handle and post a WM_CLOSE. + // + // It is important that we just call DestroyHandle here + // and do not call Dispose. Otherwise we would destroy + // controls that are living on the parking window. + // + int pid; + int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(parkingWindows[0], parkingWindows[0].Handle), out pid); + int currentThread = SafeNativeMethods.GetCurrentThreadId(); + + for(int i=0; i< parkingWindows.Count; i++) { + if (hwndThread == currentThread) { + parkingWindows[i].Destroy(); + } + else { + parkingWindows[i] = null; + } + } + parkingWindows.Clear(); + } + } + + /// + /// + /// Gets rid of all windows in this thread context. Nulls out + /// window objects that we hang on to. + /// + internal void DisposeThreadWindows() { + + // We dispose the main window first, so it can perform any + // cleanup that it may need to do. + // + try { + if (applicationContext != null) { + applicationContext.Dispose(); + applicationContext = null; + } + + // Then, we rudely destroy all of the windows on the thread + // + ThreadWindows tw = new ThreadWindows(true); + tw.Dispose(); + + // And dispose the parking form, if it isn't already + // + DisposeParkingWindow(); + } + catch { + } + } + + // Enables windows in preparation of stopping modal. If parameter is true, we enable all windows, + // if false, only windows forms windows (i.e., windows controlled by this MsoComponent). + // See also IMsoComponent.OnEnterState. + internal void EnableWindowsForModalLoop(bool onlyWinForms, ApplicationContext context) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Leaving modal state"); + if (threadWindows != null) { + threadWindows.Enable(true); + Debug.Assert(threadWindows != null, "OnEnterState recursed, but it's not supposed to be reentrant"); + threadWindows = threadWindows.previousThreadWindows; + } + + ModalApplicationContext modalContext = context as ModalApplicationContext; + if (modalContext != null) { + modalContext.DisableThreadWindows(false, onlyWinForms); + } + } + + // Called immediately after we end pumping messages for a modal message loop. + internal void EndModalMessageLoop(ApplicationContext context) { +#if DEBUG + debugModalCounter--; + Debug.Assert(debugModalCounter >= 0, "Mis-matched calls to Application.BeginModalMessageLoop() and Application.EndModalMessageLoop()"); +#endif + // This will re-enable the windows... + EnableWindowsForModalLoop(false, context); // onlyWinForms = false + + bool wasOurLoop = ourModalLoop; + ourModalLoop = true; + try { + + // If We started the ModalMessageLoop .. this will call us back on the IMSOComponent.OnStateEnter and not do anything ... + UnsafeNativeMethods.IMsoComponentManager cm = ComponentManager; + if (cm != null) { + cm.FOnComponentExitState((IntPtr)componentID, NativeMethods.MSOCM.msocstateModal, NativeMethods.MSOCM.msoccontextAll, 0, 0); + } + } + finally + { + // Reset the flag since we are exiting out of a ModalMesaageLoop.. + ourModalLoop = wasOurLoop; + } + + modalCount--; + + if (leaveModalHandler != null && modalCount == 0) { + leaveModalHandler(Thread.CurrentThread, EventArgs.Empty); + } + } + + /// + /// + /// Exits the program by disposing of all thread contexts and message loops. + /// + /// + internal static void ExitApplication() { + ExitCommon(true /*disposing*/); + } + + private static void ExitCommon(bool disposing) { + lock(tcInternalSyncObject) { + if (contextHash != null) { + ThreadContext[] ctxs = new ThreadContext[contextHash.Values.Count]; + contextHash.Values.CopyTo(ctxs, 0); + for (int i = 0; i < ctxs.Length; ++i) { + if (ctxs[i].ApplicationContext != null) { + ctxs[i].ApplicationContext.ExitThread(); + } + else { + ctxs[i].Dispose(disposing); + } + } + } + } + } + + /// + /// + /// Exits the program by disposing of all thread contexts and message loops. + /// + /// + internal static void ExitDomain() { + ExitCommon(false /*disposing*/); + } + + /// + /// + /// Our finalization. Minimal stuff... this shouldn't be called... We should always be disposed. + /// + /// + ~ThreadContext() { + + // VSWhidbey 169487 We used to call OleUninitialize() here if we were + // still STATE_OLEINITIALIZED, but that's never the correct thing to do. + // At this point we're on the wrong thread and we should never have been + // called here in the first place. + + // We can always clean up this handle, though + // + if (handle != IntPtr.Zero) { + UnsafeNativeMethods.CloseHandle(new HandleRef(this, handle)); + handle = IntPtr.Zero; + } + } + + // When a Form receives a WM_ACTIVATE message, it calls this method so we can do the + // appropriate MsoComponentManager activation magic + internal void FormActivated(bool activate) { + if (activate) { + UnsafeNativeMethods.IMsoComponentManager cm = ComponentManager; + if (cm != null && !(cm is ComponentManager)) { + cm.FOnComponentActivate((IntPtr)componentID); + } + } + } + + // Sets this component as the tracking component - trumping any active component + // for message filtering. + internal void TrackInput(bool track) { + // VSWhidbey 409264 + // protect against double setting, as this causes asserts in the VS component manager. + if (track != GetState(STATE_TRACKINGCOMPONENT)) { + UnsafeNativeMethods.IMsoComponentManager cm = ComponentManager; + if (cm != null && !(cm is ComponentManager)) { + cm.FSetTrackingComponent((IntPtr)componentID, track); + SetState(STATE_TRACKINGCOMPONENT, track); + } + } + } + /// + /// + /// Retrieves a ThreadContext object for the current thread + /// + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal static ThreadContext FromCurrent() { + ThreadContext context = currentThreadContext; + + if (context == null) { + context = new ThreadContext(); + } + + return context; + } + + /// + /// + /// Retrieves a ThreadContext object for the given thread ID + /// + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal static ThreadContext FromId(int id) { + ThreadContext context = (ThreadContext)contextHash[(object)id]; + if (context == null && id == SafeNativeMethods.GetCurrentThreadId()) { + context = new ThreadContext(); + } + + return context; + } + + /// + /// + /// Determines if it is OK to allow an application to quit and shutdown + /// the runtime. We only allow this if we own the base message pump. + /// + internal bool GetAllowQuit() { + return totalMessageLoopCount > 0 && baseLoopReason == NativeMethods.MSOCM.msoloopMain; + } + + /// + /// + /// Retrieves the handle to this thread. + /// + /// + internal IntPtr GetHandle() { + return handle; + } + + /// + /// + /// Retrieves the ID of this thread. + /// + /// + internal int GetId() { + return id; + } + + /// + /// + /// Retrieves the culture for this thread. + /// + /// + [ResourceExposure(ResourceScope.AppDomain)] + [ResourceConsumption(ResourceScope.AppDomain)] + internal CultureInfo GetCulture() { + if (culture == null || culture.LCID != SafeNativeMethods.GetThreadLocale()) + culture = new CultureInfo(SafeNativeMethods.GetThreadLocale()); + return culture; + } + + /// + /// + /// Determines if a message loop exists on this thread. + /// + internal bool GetMessageLoop() { + return GetMessageLoop(false); + } + + /// + /// + /// Determines if a message loop exists on this thread. + /// + internal bool GetMessageLoop(bool mustBeActive) { + + // If we are already running a loop, we're fine. + // If we are running in external manager we may need to make sure first the loop is active + // + if (messageLoopCount > (mustBeActive && externalComponentManager ? 1 : 0)) + { + return true; + } + + // Also, access the ComponentManager property to demand create it, and we're also + // fine if it is an external manager, because it has already pushed a loop. + // + if (ComponentManager != null && externalComponentManager) { + if (mustBeActive == false) { + return true; + } + + UnsafeNativeMethods.IMsoComponent[] activeComponents = new UnsafeNativeMethods.IMsoComponent[1]; + if (ComponentManager.FGetActiveComponent(NativeMethods.MSOCM.msogacActive, activeComponents, null, 0) && + activeComponents[0] == this) { + return true; + } + } + + // Finally, check if a message loop has been registered + MessageLoopCallback callback = messageLoopCallback; + if (callback != null) { + return callback(); + } + + // Otherwise, we do not have a loop running. + // + return false; + } + + private bool GetState(int bit) { + return(threadState & bit) != 0; + } + + /// + /// Keep the object alive forever. + /// + public override object InitializeLifetimeService() { + return null; + } + + /// + /// A method of determining whether we are handling messages that does not demand register + /// the componentmanager + /// + /// + internal bool IsValidComponentId() { + return (componentID != INVALID_ID); + } + + internal System.Threading.ApartmentState OleRequired() { + Thread current = Thread.CurrentThread; + if (!GetState(STATE_OLEINITIALIZED)) { + + int ret = UnsafeNativeMethods.OleInitialize(); + +#if false + // PERFTRACK : Microsoft, 7/26/1999 - To avoid constructing the string in + // : non-failure cases (vast majority), we will do the debug.fail + // : inside an if statement. + // + if (!(ret == NativeMethods.S_OK || ret == NativeMethods.S_FALSE || ret == NativeMethods.RPC_E_CHANGED_MODE)) { + Debug.Assert(ret == NativeMethods.S_OK || ret == NativeMethods.S_FALSE || ret == NativeMethods.RPC_E_CHANGED_MODE, + "OLE Failed to Initialize!. RetCode: 0x" + Convert.ToString(ret, 16) + + " LastError: " + Marshal.GetLastWin32Error().ToString()); + } +#endif + + SetState(STATE_OLEINITIALIZED, true); + if (ret == NativeMethods.RPC_E_CHANGED_MODE) { + // This could happen if the thread was already initialized for MTA + // and then we call OleInitialize which tries to initialized it for STA + // This currently happens while profiling... + SetState(STATE_EXTERNALOLEINIT, true); + } + + } + + if ( GetState( STATE_EXTERNALOLEINIT )) { + return System.Threading.ApartmentState.MTA; + } + else { + return System.Threading.ApartmentState.STA; + } + } + + private void OnAppThreadExit(object sender, EventArgs e) { + Dispose(true); + } + + /// + /// Revokes our component if needed. + /// + [PrePrepareMethod] + private void OnDomainUnload(object sender, EventArgs e) { + RevokeComponent(); + ExitDomain(); + } + + /// + /// + /// Called when an untrapped exception occurs in a thread. This allows the + /// programmer to trap these, and, if left untrapped, throws a standard error + /// dialog. + /// + /// + internal void OnThreadException(Exception t) { + if (GetState(STATE_INTHREADEXCEPTION)) return; + + SetState(STATE_INTHREADEXCEPTION, true); + try { + if (threadExceptionHandler != null) { + threadExceptionHandler(Thread.CurrentThread, new ThreadExceptionEventArgs(t)); + } + else { + if (SystemInformation.UserInteractive) { + ThreadExceptionDialog td = new ThreadExceptionDialog(t); + DialogResult result = DialogResult.OK; + + // SECREVIEW : This Assert is ok, this dialog shows up when the application is about to die. + // ShowDialog will generate a call to ContainerControl.SetActiveControl which demands + // ModifyFocus permission, need to assert it here. + // + IntSecurity.ModifyFocus.Assert(); + try { + result = td.ShowDialog(); + } + finally { + CodeAccessPermission.RevertAssert(); + td.Dispose(); + } + switch (result) { + case DialogResult.Abort: + // SECREVIEW : The user clicked "Quit" in response to a exception dialog. + // : We only show the quit option when we own the message pump, + // : so this won't ever tear down IE or any other ActiveX host. + // : + // : This has a potential problem where a component could + // : cause a failure, then try a "trick" the user into hitting + // : quit. However, no component or application outside of + // : the windows forms assembly can modify the dialog, so this is + // : a really minor concern. + // + Application.ExitInternal(); + + // SECREVIEW : We can't revert this assert... after Exit(0) is called, no + // : more code is executed... + // + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + Environment.Exit(0); + break; + case DialogResult.Yes: + WarningException w = t as WarningException; + if (w != null) { + Help.ShowHelp(null, w.HelpUrl, w.HelpTopic); + } + break; + } + } + else { + // Ignore unhandled thread exceptions. The user can + // override if they really care. + // + } + + } + } + finally { + SetState(STATE_INTHREADEXCEPTION, false); + } + } + + internal void PostQuit() { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Attempting to terminate message loop"); + + // Per http://support.microsoft.com/support/kb/articles/Q183/1/16.ASP + // + // WM_QUIT may be consumed by another message pump under very specific circumstances. + // When that occurs, we rely on the STATE_POSTEDQUIT to be caught in the next + // idle, at which point we can tear down. + // + // We can't follow the KB article exactly, becasue we don't have an HWND to PostMessage + // to. + // + UnsafeNativeMethods.PostThreadMessage(id, NativeMethods.WM_QUIT, IntPtr.Zero, IntPtr.Zero); + SetState(STATE_POSTEDQUIT, true); + } + + // Allows the hosting environment to register a callback + internal void RegisterMessageLoop(MessageLoopCallback callback) { + messageLoopCallback = callback; + } + + /// + /// + /// Removes a message filter previously installed with addMessageFilter. + /// + /// + internal void RemoveMessageFilter(IMessageFilter f) { + if (messageFilters != null) { + SetState(STATE_FILTERSNAPSHOTVALID, false); + messageFilters.Remove(f); + } + } + + /// + /// + /// Starts a message loop for the given reason. + /// + /// + internal void RunMessageLoop(int reason, ApplicationContext context) { + // Ensure that we attempt to apply theming before doing anything + // that might create a window. + + IntPtr userCookie = IntPtr.Zero; + if (useVisualStyles) { + userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + } + + try { + RunMessageLoopInner(reason, context); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + + private void RunMessageLoopInner(int reason, ApplicationContext context) { + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ThreadContext.PushMessageLoop {"); + Debug.Indent(); + + if (reason == NativeMethods.MSOCM.msoloopModalForm && !SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + + // if we've entered because of a Main message loop being pushed + // (different than a modal message loop or DoEVents loop) + // then clear the QUIT flag to allow normal processing. + // this flag gets set during loop teardown for another form. + if (reason == NativeMethods.MSOCM.msoloopMain) { + SetState(STATE_POSTEDQUIT, false); + } + + if (totalMessageLoopCount++ == 0) { + baseLoopReason = reason; + } + + messageLoopCount++; + + if (reason == NativeMethods.MSOCM.msoloopMain) { + // If someone has tried to push another main message loop on this thread, ignore + // it. + if (messageLoopCount != 1) { + throw new InvalidOperationException(SR.GetString(SR.CantNestMessageLoops)); + } + + applicationContext = context; + + applicationContext.ThreadExit += new EventHandler(OnAppThreadExit); + + if (applicationContext.MainForm != null) { + applicationContext.MainForm.Visible = true; + } + + DpiHelper.InitializeDpiHelperForWinforms(); + + AccessibilityImprovements.ValidateLevels(); + } + + Form oldForm = currentForm; + if (context != null) { + currentForm = context.MainForm; + } + + bool fullModal = false; + bool localModal = false; + HandleRef hwndOwner = new HandleRef(null, IntPtr.Zero); + + if (reason == NativeMethods.MSOCM.msoloopDoEventsModal) { + localModal = true; + } + + if (reason == NativeMethods.MSOCM.msoloopModalForm || reason == NativeMethods.MSOCM.msoloopModalAlert) { + fullModal = true; + + // We're about to disable all windows in the thread so our modal dialog can be the top dog. Because this can interact + // with external MSO things, and also because the modal dialog could have already had its handle created, + // Check to see if the handle exists and if the window is currently enabled. We remember this so we can set the + // window back to enabled after disabling everyone else. This is just a precaution against someone doing the + // wrong thing and disabling our dialog. + // + bool modalEnabled = currentForm != null && currentForm.Enabled; + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "[0x" + Convert.ToString(componentID, 16) + "] Notifying component manager that we are entering a modal loop"); + BeginModalMessageLoop(context); + + // If the owner window of the dialog is still enabled, disable it now. + // This can happen if the owner window is from a different thread or + // process. + hwndOwner = new HandleRef(null, UnsafeNativeMethods.GetWindowLong(new HandleRef(currentForm, currentForm.Handle), NativeMethods.GWL_HWNDPARENT)); + if (hwndOwner.Handle != IntPtr.Zero) { + if (SafeNativeMethods.IsWindowEnabled(hwndOwner)) { + SafeNativeMethods.EnableWindow(hwndOwner, false); + } + else { + // reset hwndOwner so we are not tempted to + // fiddle with it + hwndOwner = new HandleRef(null, IntPtr.Zero); + } + } + + // The second half of the the modalEnabled flag above. Here, if we were previously + // enabled, make sure that's still the case. + // + if (currentForm != null && + currentForm.IsHandleCreated && + SafeNativeMethods.IsWindowEnabled(new HandleRef(currentForm, currentForm.Handle)) != modalEnabled) { + SafeNativeMethods.EnableWindow(new HandleRef(currentForm, currentForm.Handle), modalEnabled); + } + } + + try { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "[0x" + Convert.ToString(componentID, 16) + "] Calling ComponentManager.FPushMessageLoop..."); + bool result; + + // Register marshaller for background tasks. At this point, + // need to be able to successfully get the handle to the + // parking window. Only do it when we're entering the first + // message loop for this thread. + if (messageLoopCount == 1) { + WindowsFormsSynchronizationContext.InstallIfNeeded(); + } + + //need to do this in a try/finally. Also good to do after we installed the synch context. + if (fullModal && currentForm != null) { + currentForm.Visible = true; + } + + if ((!fullModal && !localModal) || ComponentManager is ComponentManager) { + result = ComponentManager.FPushMessageLoop((IntPtr)componentID, reason, 0); + } + else if (reason == NativeMethods.MSOCM.msoloopDoEvents || + reason == NativeMethods.MSOCM.msoloopDoEventsModal) { + result = LocalModalMessageLoop(null); + } + else { + result = LocalModalMessageLoop(currentForm); + } + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "[0x" + Convert.ToString(componentID, 16) + "] ComponentManager.FPushMessageLoop returned " + result.ToString()); + } + finally { + + if (fullModal) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "[0x" + Convert.ToString(componentID, 16) + "] Notifying component manager that we are exiting a modal loop"); + EndModalMessageLoop(context); + + // Again, if the hwndOwner was valid and disabled above, re-enable it. + if (hwndOwner.Handle != IntPtr.Zero) { + SafeNativeMethods.EnableWindow(hwndOwner, true); + } + } + + currentForm = oldForm; + totalMessageLoopCount--; + messageLoopCount--; + + if (messageLoopCount == 0) { + // If last message loop shutting down, install the + // previous op sync context in place before we started the first + // message loop. + WindowsFormsSynchronizationContext.Uninstall(false); + } + + if (reason == NativeMethods.MSOCM.msoloopMain) { + Dispose(true); + } + else if (messageLoopCount == 0 && componentManager != null) { + // If we had a component manager, detach from it. + // + RevokeComponent(); + } + } + + Debug.Unindent(); + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "}"); + } + + private bool LocalModalMessageLoop(Form form) { + try { + // Execute the message loop until the active component tells us to stop. + // + NativeMethods.MSG msg = new NativeMethods.MSG(); + bool unicodeWindow = false; + bool continueLoop = true; + + while (continueLoop) { + + bool peeked = UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE); + + if (peeked) { + + // If the component wants us to process the message, do it. + // The component manager hosts windows from many places. We must be sensitive + // to ansi / Unicode windows here. + // + if (msg.hwnd != IntPtr.Zero && SafeNativeMethods.IsWindowUnicode(new HandleRef(null, msg.hwnd))) { + unicodeWindow = true; + if (!UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0)) { + continue; + } + + } + else { + unicodeWindow = false; + if (!UnsafeNativeMethods.GetMessageA(ref msg, NativeMethods.NullHandleRef, 0, 0)) { + continue; + } + } + + if (!PreTranslateMessage(ref msg)) { + UnsafeNativeMethods.TranslateMessage(ref msg); + if (unicodeWindow) { + UnsafeNativeMethods.DispatchMessageW(ref msg); + } + else { + UnsafeNativeMethods.DispatchMessageA(ref msg); + } + } + + if (form != null) { + continueLoop = !form.CheckCloseDialog(false); + } + } + else if (form == null) { + break; + } + else if (!UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE)) { + UnsafeNativeMethods.WaitMessage(); + } + } + return continueLoop; + } + catch { + return false; + } + } + + internal bool ProcessFilters(ref NativeMethods.MSG msg, out bool modified) { + bool filtered = false; + + modified = false; + + // Account for the case where someone removes a message filter + // as a result of PreFilterMessage. the message filter will be + // removed from _the next_ message. + // If message filter is added or removed inside the user-provided PreFilterMessage function, + // and user code pumps messages, we might re-enter ProcessFilter on the same stack, we + // should not update the snapshot until the next message. + if (messageFilters != null && !GetState(STATE_FILTERSNAPSHOTVALID) && (LocalAppContextSwitches.DontSupportReentrantFilterMessage || inProcessFilters == 0)) { + if (messageFilters.Count > 0) { + messageFilterSnapshot = new IMessageFilter[messageFilters.Count]; + messageFilters.CopyTo(messageFilterSnapshot); + } + else { + messageFilterSnapshot = null; + } + SetState(STATE_FILTERSNAPSHOTVALID, true); + } + + inProcessFilters++; + try { + if (messageFilterSnapshot != null) { + IMessageFilter f; + int count = messageFilterSnapshot.Length; + + Message m = Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam); + + for (int i = 0; i < count; i++) { + f = (IMessageFilter)messageFilterSnapshot[i]; + bool filterMessage = f.PreFilterMessage(ref m); + // make sure that we update the msg struct with the new result after the call to + // PreFilterMessage. + if (f is IMessageModifyAndFilter) { + msg.hwnd = m.HWnd; + msg.message = m.Msg; + msg.wParam = m.WParam; + msg.lParam = m.LParam; + modified = true; + } + + if (filterMessage) { + // consider : m is by ref here, so the user can change it. But we + // are discarding changes by not propagating to msg. Should we? + filtered = true; + break; + } + } + } + } + finally { + inProcessFilters--; + } + + return filtered; + } + + + /// + /// Message filtering routine that is called before dispatching a message. + /// If this returns true, the message is already processed. If it returns + /// false, the message should be allowed to continue through the dispatch + /// mechanism. + /// + internal bool PreTranslateMessage(ref NativeMethods.MSG msg) { + bool modified = false; + if (ProcessFilters(ref msg, out modified)) { + return true; + } + + if (msg.message >= NativeMethods.WM_KEYFIRST + && msg.message <= NativeMethods.WM_KEYLAST) { + if (msg.message == NativeMethods.WM_CHAR) { + int breakLParamMask = 0x1460000; // 1 = extended keyboard, 46 = scan code + if (unchecked( (int) (long)msg.wParam) == 3 && ( unchecked( (int) (long)msg.lParam) & breakLParamMask) == breakLParamMask) { // ctrl-brk + // wParam is the key character, which for ctrl-brk is the same as ctrl-C. + // So we need to go to the lparam to distinguish the two cases. + // You might also be able to do this with WM_KEYDOWN (again with wParam=3) + + if (Debugger.IsAttached) { + Debugger.Break(); + } + } + } + Control target = Control.FromChildHandleInternal(msg.hwnd); + bool retValue = false; + + Message m = Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam); + + if (target != null) + { + if (NativeWindow.WndProcShouldBeDebuggable) { + // we don't want to do a catch in the debuggable case. + // + if (Control.PreProcessControlMessageInternal(target, ref m) == PreProcessControlState.MessageProcessed) { + retValue = true; + } + } + else { + try + { + if (Control.PreProcessControlMessageInternal(target, ref m) == PreProcessControlState.MessageProcessed) { + retValue = true; + } + } + catch (Exception e) { + OnThreadException(e); + } + } + } + else + { + // See if this is a dialog message -- this is for handling any native dialogs that are launched from + // Microsoft code. This can happen with ActiveX controls that launch dialogs specificially + // + + // first, get the first top-level window in the hierarchy. + // + IntPtr hwndRoot = UnsafeNativeMethods.GetAncestor(new HandleRef(null, msg.hwnd), NativeMethods.GA_ROOT); + + // if we got a valid HWND, then call IsDialogMessage on it. If that returns true, it's been processed + // so we should return true to prevent Translate/Dispatch from being called. + // + if (hwndRoot != IntPtr.Zero && UnsafeNativeMethods.IsDialogMessage(new HandleRef(null, hwndRoot), ref msg)) + { + return true; + } + } + + msg.wParam = m.WParam; + msg.lParam = m.LParam; + + if (retValue) { + return true; + } + } + + return false; + } + + /// + /// Revokes our component from the active component manager. Does + /// nothing if there is no active component manager or we are + /// already invoked. + /// + private void RevokeComponent() { + if (componentManager != null && componentID != INVALID_ID) { + int id = componentID; + UnsafeNativeMethods.IMsoComponentManager msocm = componentManager; + + try { + msocm.FRevokeComponent((IntPtr)id); + if (Marshal.IsComObject(msocm)) { + Marshal.ReleaseComObject(msocm); + } + } + finally { + componentManager = null; + componentID = INVALID_ID; + } + } + } + + /// + /// + /// Sets the culture for this thread. + /// + /// + [ResourceExposure(ResourceScope.AppDomain)] + [ResourceConsumption(ResourceScope.AppDomain)] + internal void SetCulture(CultureInfo culture) { + if (culture != null && culture.LCID != SafeNativeMethods.GetThreadLocale()) { + SafeNativeMethods.SetThreadLocale(culture.LCID); + } + } + + private void SetState(int bit, bool value) { + if (value) { + threadState |= bit; + } + else { + threadState &= (~bit); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + + + + /**************************************************************************************** + * + * IMsoComponent + * + ****************************************************************************************/ + + // Things to test in VS when you change this code: + // - You can bring up dialogs multiple times (ie, the editor for TextBox.Lines -- ASURT 41876) + // - Double-click DataFormWizard, cancel wizard (ASURT 41562) + // - When a dialog is open and you switch to another application, when you switch + // back to VS the dialog gets the focus (ASURT 38961) + // - If one modal dialog launches another, they are all modal (ASURT 37154. Try web forms Table\Rows\Cell) + // - When a dialog is up, VS is completely disabled, including moving and resizing VS. + // - After doing all this, you can ctrl-shift-N start a new project and VS is enabled. + + /// + /// + /// Standard FDebugMessage method. + /// Since IMsoComponentManager is a reference counted interface, + /// MsoDWGetChkMemCounter should be used when processing the + /// msodmWriteBe message. + /// + bool UnsafeNativeMethods.IMsoComponent.FDebugMessage(IntPtr hInst, int msg, IntPtr wparam, IntPtr lparam) + { + + return false; + } + + /// + /// + /// Give component a chance to process the message pMsg before it is + /// translated and dispatched. Component can do TranslateAccelerator + /// do IsDialogMessage, modify pMsg, or take some other action. + /// Return TRUE if the message is consumed, FALSE otherwise. + /// + bool UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(ref NativeMethods.MSG msg) { + return PreTranslateMessage(ref msg); + } + + /// + /// + /// Notify component when app enters or exits (as indicated by fEnter) + /// the state identified by uStateID (a value from olecstate enumeration). + /// Component should take action depending on value of uStateID + /// (see olecstate comments, above). + /// + /// Note: If n calls are made with TRUE fEnter, component should consider + /// the state to be in effect until n calls are made with FALSE fEnter. + /// + /// Note: Components should be aware that it is possible for this method to + /// be called with FALSE fEnter more times than it was called with TRUE + /// fEnter (so, for example, if component is maintaining a state counter + /// (incremented when this method is called with TRUE fEnter, decremented + /// when called with FALSE fEnter), the counter should not be decremented + /// for FALSE fEnter if it is already at zero.) + /// + void UnsafeNativeMethods.IMsoComponent.OnEnterState(int uStateID, bool fEnter) { + + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : OnEnterState(" + uStateID + ", " + fEnter + ")"); + + // Return if our (Microsoft) Modal Loop is still running. + if (ourModalLoop) + { + return; + } + if (uStateID == NativeMethods.MSOCM.msocstateModal) { + // We should only be messing with windows we own. See the "ctrl-shift-N" test above. + if (fEnter) { + DisableWindowsForModalLoop(true, null); // WinFormsOnly = true + } + else { + EnableWindowsForModalLoop(true, null); // WinFormsOnly = true + } + } + } + + /// + /// + /// Notify component when the host application gains or loses activation. + /// If fActive is TRUE, the host app is being activated and dwOtherThreadID + /// is the ID of the thread owning the window being deactivated. + /// If fActive is FALSE, the host app is being deactivated and + /// dwOtherThreadID is the ID of the thread owning the window being + /// activated. + /// Note: this method is not called when both the window being activated + /// and the one being deactivated belong to the host app. + /// + void UnsafeNativeMethods.IMsoComponent.OnAppActivate(bool fActive, int dwOtherThreadID) { + } + + /// + /// + /// Notify the active component that it has lost its active status because + /// the host or another component has become active. + /// + void UnsafeNativeMethods.IMsoComponent.OnLoseActivation() { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Our component is losing activation."); + } + + /// + /// + /// Notify component when a new object is being activated. + /// If pic is non-NULL, then it is the component that is being activated. + /// In this case, fSameComponent is TRUE if pic is the same component as + /// the callee of this method, and pcrinfo is the reg info of pic. + /// If pic is NULL and fHostIsActivating is TRUE, then the host is the + /// object being activated, and pchostinfo is its host info. + /// If pic is NULL and fHostIsActivating is FALSE, then there is no current + /// active object. + /// + /// If pic is being activated and pcrinfo->grf has the + /// olecrfExclusiveBorderSpace bit set, component should hide its border + /// space tools (toolbars, status bars, etc.); + /// component should also do this if host is activating and + /// pchostinfo->grfchostf has the olechostfExclusiveBorderSpace bit set. + /// In either of these cases, component should unhide its border space + /// tools the next time it is activated. + /// + /// if pic is being activated and pcrinfo->grf has the + /// olecrfExclusiveActivation bit is set, then pic is being activated in + /// "ExclusiveActive" mode. + /// Component should retrieve the top frame window that is hosting pic + /// (via pic->HwndGetWindow(olecWindowFrameToplevel, 0)). + /// If this window is different from component's own top frame window, + /// component should disable its windows and do other things it would do + /// when receiving OnEnterState(olecstateModal, TRUE) notification. + /// Otherwise, if component is top-level, + /// it should refuse to have its window activated by appropriately + /// processing WM_MOUSEACTIVATE (but see WM_MOUSEACTIVATE NOTE, above). + /// Component should remain in one of these states until the + /// ExclusiveActive mode ends, indicated by a future call to + /// OnActivationChange with ExclusiveActivation bit not set or with NULL + /// pcrinfo. + /// + void UnsafeNativeMethods.IMsoComponent.OnActivationChange(UnsafeNativeMethods.IMsoComponent component, bool fSameComponent, + int pcrinfo, + bool fHostIsActivating, + int pchostinfo, + int dwReserved) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : OnActivationChange"); + } + + /// + /// + /// Give component a chance to do idle time tasks. grfidlef is a group of + /// bit flags taken from the enumeration of oleidlef values (above), + /// indicating the type of idle tasks to perform. + /// Component may periodically call IOleComponentManager::FContinueIdle; + /// if this method returns FALSE, component should terminate its idle + /// time processing and return. + /// Return TRUE if more time is needed to perform the idle time tasks, + /// FALSE otherwise. + /// Note: If a component reaches a point where it has no idle tasks + /// and does not need FDoIdle calls, it should remove its idle task + /// registration via IOleComponentManager::FUpdateComponentRegistration. + /// Note: If this method is called on while component is performing a + /// tracking operation, component should only perform idle time tasks that + /// it deems are appropriate to perform during tracking. + /// + bool UnsafeNativeMethods.IMsoComponent.FDoIdle(int grfidlef) { + if (idleHandler != null) { + idleHandler(Thread.CurrentThread, EventArgs.Empty); + } + return false; + } + + /// + /// + /// Called during each iteration of a message loop that the component + /// pushed. uReason and pvLoopData are the reason and the component private + /// data that were passed to IOleComponentManager::FPushMessageLoop. + /// This method is called after peeking the next message in the queue + /// (via PeekMessage) but before the message is removed from the queue. + /// The peeked message is passed in the pMsgPeeked param (NULL if no + /// message is in the queue). This method may be additionally called when + /// the next message has already been removed from the queue, in which case + /// pMsgPeeked is passed as NULL. + /// Return TRUE if the message loop should continue, FALSE otherwise. + /// If FALSE is returned, the component manager terminates the loop without + /// removing pMsgPeeked from the queue. + /// + bool UnsafeNativeMethods.IMsoComponent.FContinueMessageLoop(int reason, int pvLoopData, NativeMethods.MSG[] msgPeeked) { + + bool continueLoop = true; + + // If we get a null message, and we have previously posted the WM_QUIT message, + // then someone ate the message... + // + if (msgPeeked == null && GetState(STATE_POSTEDQUIT)) { + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Abnormal loop termination, no WM_QUIT received"); + continueLoop = false; + } + else { + switch (reason) { + case NativeMethods.MSOCM.msoloopFocusWait: + + // For focus wait, check to see if we are now the active application. + // + int pid; + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, UnsafeNativeMethods.GetActiveWindow()), out pid); + if (pid == SafeNativeMethods.GetCurrentProcessId()) { + continueLoop = false; + } + break; + + case NativeMethods.MSOCM.msoloopModalAlert: + case NativeMethods.MSOCM.msoloopModalForm: + + // For modal forms, check to see if the current active form has been + // dismissed. If there is no active form, then it is an error that + // we got into here, so we terminate the loop. + // + if (currentForm == null || currentForm.CheckCloseDialog(false)) { + continueLoop = false; + } + break; + + case NativeMethods.MSOCM.msoloopDoEvents: + case NativeMethods.MSOCM.msoloopDoEventsModal: + // For DoEvents, just see if there are more messages on the queue. + // + if (!UnsafeNativeMethods.PeekMessage(ref tempMsg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE)) { + continueLoop = false; + } + + break; + } + } + + return continueLoop; + } + + + /// + /// + /// Called when component manager wishes to know if the component is in a + /// state in which it can terminate. If fPromptUser is FALSE, component + /// should simply return TRUE if it can terminate, FALSE otherwise. + /// If fPromptUser is TRUE, component should return TRUE if it can + /// terminate without prompting the user; otherwise it should prompt the + /// user, either 1.) asking user if it can terminate and returning TRUE + /// or FALSE appropriately, or 2.) giving an indication as to why it + /// cannot terminate and returning FALSE. + /// + bool UnsafeNativeMethods.IMsoComponent.FQueryTerminate(bool fPromptUser) { + return true; + } + + /// + /// + /// Called when component manager wishes to terminate the component's + /// registration. Component should revoke its registration with component + /// manager, release references to component manager and perform any + /// necessary cleanup. + /// + void UnsafeNativeMethods.IMsoComponent.Terminate() { + if (this.messageLoopCount > 0 && !(ComponentManager is ComponentManager)) { + this.messageLoopCount--; + } + + Dispose(false); + } + + /// + /// + /// Called to retrieve a window associated with the component, as specified + /// by dwWhich, a olecWindowXXX value (see olecWindow, above). + /// dwReserved is reserved for future use and should be zero. + /// Component should return the desired window or NULL if no such window + /// exists. + /// + IntPtr UnsafeNativeMethods.IMsoComponent.HwndGetWindow(int dwWhich, int dwReserved) { + return IntPtr.Zero; + } + } + + /// + /// This class allows us to handle sends/posts in our winformssynchcontext on the correct thread via + /// control.invoke(). + /// + /// + internal sealed class MarshalingControl : Control { + internal MarshalingControl() + : base(false) { + Visible = false; + SetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED, false); + SetTopLevel(true); + CreateControl(); + CreateHandle(); + } + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + + // Message only windows are cheaper and have fewer issues than + // full blown invisible windows. But, they are only supported + // on NT. + if (Environment.OSVersion.Platform == PlatformID.Win32NT) { + cp.Parent = (IntPtr)NativeMethods.HWND_MESSAGE; + } + return cp; + } + } + + protected override void OnLayout(LayoutEventArgs levent) { + } + + protected override void OnSizeChanged(EventArgs e) { + + // don't do anything here -- small perf game of avoiding layout, etc. + } + } + + /// + /// + /// This class embodies our parking window, which we create when the + /// first message loop is pushed onto the thread. + /// + /// + internal sealed class ParkingWindow : ContainerControl, IArrangedElement { + + // WHIDBEY CHANGES + // in whidbey we now aggressively tear down the parking window + // when the last control has been removed off of it. + // + // + + + private const int WM_CHECKDESTROY = NativeMethods.WM_USER + 0x01; + + private int childCount = 0; + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // The parking window is invisible. + // So we don't have to localize its Text. + ] + public ParkingWindow() { + SetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED, false); + SetState(STATE_TOPLEVEL, true); + Text = "WindowsFormsParkingWindow"; + Visible = false; + } + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + + // Message only windows are cheaper and have fewer issues than + // full blown invisible windows. But, they are only supported + // on NT. + if (Environment.OSVersion.Platform == PlatformID.Win32NT) { + cp.Parent = (IntPtr)NativeMethods.HWND_MESSAGE; + } + return cp; + } + } + + internal override void AddReflectChild() { + if (childCount < 0) { + Debug.Fail("How did parkingwindow childcount go negative???"); + childCount = 0; + } + childCount++; + } + + internal override void RemoveReflectChild() { + childCount--; + if (childCount < 0) { + Debug.Fail("How did parkingwindow childcount go negative???"); + childCount = 0; + } + if (childCount == 0) { + if (IsHandleCreated) { + //check to see if we are running on the thread that owns the parkingwindow. + //if so, we can destroy immediately. + //This is important for scenarios where apps leak controls until after the + //messagepump is gone and then decide to clean them up. We should clean + //up the parkingwindow in this case and a postmessage won't do it. + int lpdwProcessId; //unused + int id = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, HandleInternal), out lpdwProcessId); + Application.ThreadContext ctx = Application.ThreadContext.FromId(id); + + //We only do this if the ThreadContext tells us that we are currently + //handling a window message. + if (ctx == null || + !Object.ReferenceEquals(ctx, Application.ThreadContext.FromCurrent())) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, HandleInternal), WM_CHECKDESTROY, IntPtr.Zero, IntPtr.Zero); + } + else { + CheckDestroy(); + } + } + } + } + + private void CheckDestroy() { + if (childCount == 0) { + IntPtr hwndChild = UnsafeNativeMethods.GetWindow(new HandleRef(this, Handle), NativeMethods.GW_CHILD); + if (hwndChild == IntPtr.Zero) { + DestroyHandle(); + } + } + } + + public void Destroy() { + DestroyHandle(); + } + + /// + /// "Parks" the given HWND to a temporary HWND. This allows WS_CHILD windows to + /// be parked. + /// + internal void ParkHandle(HandleRef handle) { + if (!IsHandleCreated) { + CreateHandle(); + } + + UnsafeNativeMethods.SetParent(handle, new HandleRef(this, Handle)); + } + + /// + /// "Unparks" the given HWND to a temporary HWND. This allows WS_CHILD windows to + /// be parked. + /// + internal void UnparkHandle(HandleRef handle) { + if (IsHandleCreated) { + Debug.Assert(UnsafeNativeMethods.GetParent(handle) != Handle, "Always set the handle's parent to someone else before calling UnparkHandle"); + // If there are no child windows in this handle any longer, destroy the parking window. + CheckDestroy(); + } + } + + // Do nothing on layout to reduce the calls into the LayoutEngine while debugging. + protected override void OnLayout(LayoutEventArgs levent) {} + void IArrangedElement.PerformLayout(IArrangedElement affectedElement, string affectedProperty) {} + + protected override void WndProc(ref Message m) { + if (m.Msg != NativeMethods.WM_SHOWWINDOW) { + base.WndProc(ref m); + if (m.Msg == NativeMethods.WM_PARENTNOTIFY) { + if (NativeMethods.Util.LOWORD(unchecked( (int) (long)m.WParam)) == NativeMethods.WM_DESTROY) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), WM_CHECKDESTROY, IntPtr.Zero, IntPtr.Zero); + } + } + else if (m.Msg == WM_CHECKDESTROY) { + CheckDestroy(); + } + } + } + } + + /// + /// + /// This class enables or disables all windows in the current thread. We use this to + /// disable other windows on the thread when a modal dialog is to be shown. It can also + /// be used to dispose all windows in a thread, which we do before returning from a message + /// loop. + /// + /// + private sealed class ThreadWindows { + private IntPtr[] windows; + private int windowCount; + private IntPtr activeHwnd; + private IntPtr focusedHwnd; + internal ThreadWindows previousThreadWindows; + private bool onlyWinForms = true; + + internal ThreadWindows(bool onlyWinForms) { + windows = new IntPtr[16]; + this.onlyWinForms = onlyWinForms; + UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), + new NativeMethods.EnumThreadWindowsCallback(this.Callback), + NativeMethods.NullHandleRef); + } + + private bool Callback(IntPtr hWnd, IntPtr lparam) { + + // We only do visible and enabled windows. Also, we only do top level windows. + // Finally, we only include windows that are DNA windows, since other MSO components + // will be responsible for disabling their own windows. + // + if (SafeNativeMethods.IsWindowVisible(new HandleRef(null, hWnd)) && SafeNativeMethods.IsWindowEnabled(new HandleRef(null, hWnd))) { + bool add = true; + + if (onlyWinForms) { + Control c = Control.FromHandleInternal(hWnd); + if (c == null) add = false; + } + + if (add) { + if (windowCount == windows.Length) { + IntPtr[] newWindows = new IntPtr[windowCount * 2]; + Array.Copy(windows, 0, newWindows, 0, windowCount); + windows = newWindows; + } + windows[windowCount++] = hWnd; + } + } + return true; + } + + // Disposes all top-level Controls on this thread + internal void Dispose() { + for (int i = 0; i < windowCount; i++) { + IntPtr hWnd = windows[i]; + if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))) { + Control c = Control.FromHandleInternal(hWnd); + if (c != null) { + c.Dispose(); + } + } + } + } + + // Enables/disables all top-level Controls on this thread + internal void Enable(bool state) { + + if (!onlyWinForms && !state) { + activeHwnd = UnsafeNativeMethods.GetActiveWindow(); + Control activatingControl = Application.ThreadContext.FromCurrent().ActivatingControl; + if (activatingControl != null) { + focusedHwnd = activatingControl.Handle; + } + else { + focusedHwnd = UnsafeNativeMethods.GetFocus(); + } + } + + for (int i = 0; i < windowCount; i++) { + IntPtr hWnd = windows[i]; + Debug.WriteLineIf(CompModSwitches.MSOComponentManager.TraceInfo, "ComponentManager : Changing enabled on window: " + hWnd.ToString() + " : " + state.ToString()); + if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))) SafeNativeMethods.EnableWindow(new HandleRef(null, hWnd), state); + } + + // VSWhidbey 258748 :OpenFileDialog is not returning the focus the way other dialogs do. + // Important that we re-activate the old window when we are closing + // our modal dialog. + // + // VS Whidbey 337882: edit mode forever with Excel application + // But, DON'T dink around with other people's state when we're simply + // responding to external MSOCM events about modality. When we are, + // we are created with a TRUE for onlyWinForms. + if (!onlyWinForms && state) { + if (activeHwnd != IntPtr.Zero && UnsafeNativeMethods.IsWindow(new HandleRef(null, activeHwnd))) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, activeHwnd)); + } + + if (focusedHwnd != IntPtr.Zero && UnsafeNativeMethods.IsWindow(new HandleRef(null, focusedHwnd))) { + UnsafeNativeMethods.SetFocus(new HandleRef(null, focusedHwnd)); + } + } + } + } + + private class ModalApplicationContext : ApplicationContext { + + private ThreadContext parentWindowContext; + + private delegate void ThreadWindowCallback(ThreadContext context, bool onlyWinForms); + + public ModalApplicationContext(Form modalForm) : base(modalForm) { + } + + public void DisableThreadWindows(bool disable, bool onlyWinForms) { + + Control parentControl = null; + + // VSWhidbey 400973 get ahold of the parent HWND -- if it's a different thread we need to do + // do the disable over there too. Note we only do this if we're parented by a Windows Forms + // parent. + // + if (MainForm != null && MainForm.IsHandleCreated) { + + // get ahold of the parenting control + // + IntPtr parentHandle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, MainForm.Handle), NativeMethods.GWL_HWNDPARENT); + + parentControl = Control.FromHandleInternal(parentHandle); + + if (parentControl != null && parentControl.InvokeRequired) { + parentWindowContext = GetContextForHandle(new HandleRef(this, parentHandle)); + } + else { + parentWindowContext = null; + } + } + + // if we got a thread context, that means our parent is in a different thread, make the call on that thread. + // + if (parentWindowContext != null) { + + // in case we've already torn down, ask the context for this. + // + if (parentControl == null) { + + parentControl = parentWindowContext.ApplicationContext.MainForm; + } + + if (disable) { + parentControl.Invoke(new ThreadWindowCallback(DisableThreadWindowsCallback), new object[]{parentWindowContext, onlyWinForms}); + } + else { + parentControl.Invoke(new ThreadWindowCallback(EnableThreadWindowsCallback), new object[]{parentWindowContext, onlyWinForms}); + } + } + } + + private void DisableThreadWindowsCallback(ThreadContext context, bool onlyWinForms) { + context.DisableWindowsForModalLoop(onlyWinForms, this); + } + + private void EnableThreadWindowsCallback(ThreadContext context, bool onlyWinForms) { + context.EnableWindowsForModalLoop(onlyWinForms, this); + } + + + protected override void ExitThreadCore() { + // do nothing... modal dialogs exit by setting dialog result + } + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ApplicationContext.cs b/WindowsForms/Managed/System/WinForms/ApplicationContext.cs new file mode 100644 index 000000000..c89ab2de3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ApplicationContext.cs @@ -0,0 +1,160 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + /// + /// ApplicationContext provides contextual information about an application + /// thread. Specifically this allows an application author to redifine what + /// circurmstances cause a message loop to exit. By default the application + /// context listens to the close event on the mainForm, then exits the + /// thread's message loop. + /// + public class ApplicationContext : IDisposable { + Form mainForm; + object userData; + + /// + /// + /// Creates a new ApplicationContext with no mainForm. + /// + public ApplicationContext() : this(null) { + } + + /// + /// + /// Creates a new ApplicationContext with the specified mainForm. + /// If OnMainFormClosed is not overriden, the thread's message + /// loop will be terminated when mainForm is closed. + /// + public ApplicationContext(Form mainForm) { + this.MainForm = mainForm; + } + + /// + ~ApplicationContext() { + Dispose(false); + } + + /// + /// + /// Determines the mainForm for this context. This may be changed + /// at anytime. + /// If OnMainFormClosed is not overriden, the thread's message + /// loop will be terminated when mainForm is closed. + /// + public Form MainForm { + get { + return mainForm; + } + set { + EventHandler onClose = new EventHandler(OnMainFormDestroy); + if (mainForm != null) { + mainForm.HandleDestroyed -= onClose; + } + + mainForm = value; + + if (mainForm != null) { + mainForm.HandleDestroyed += onClose; + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Is raised when the thread's message loop should be terminated. + /// This is raised by calling ExitThread. + /// + public event EventHandler ThreadExit; + + /// + /// + /// Disposes the context. This should dispose the mainForm. This is + /// called immediately after the thread's message loop is terminated. + /// Application will dispose all forms on this thread by default. + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) { + if (disposing) { + if (mainForm != null) { + if (!mainForm.IsDisposed) { + mainForm.Dispose(); + } + mainForm = null; + } + } + } + + /// + /// + /// Causes the thread's message loop to be terminated. This + /// will call ExitThreadCore. + /// + public void ExitThread() { + ExitThreadCore(); + } + + /// + /// + /// Causes the thread's message loop to be terminated. + /// + protected virtual void ExitThreadCore() { + if (ThreadExit != null) { + ThreadExit(this, EventArgs.Empty); + } + } + + /// + /// + /// Called when the mainForm is closed. The default implementation + /// of this will call ExitThreadCore. + /// + protected virtual void OnMainFormClosed(object sender, EventArgs e) { + ExitThreadCore(); + } + + /// + /// Called when the mainForm is closed. The default implementation + /// of this will call ExitThreadCore. + /// + private void OnMainFormDestroy(object sender, EventArgs e) { + Form form = (Form)sender; + if (!form.RecreatingHandle) { + form.HandleDestroyed -= new EventHandler(OnMainFormDestroy); + OnMainFormClosed(sender, e); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ArrangeDirection.cs b/WindowsForms/Managed/System/WinForms/ArrangeDirection.cs new file mode 100644 index 000000000..1109ce9a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ArrangeDirection.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1997, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Specifies the direction the system uses to arrange + /// minimized windows. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + [Flags] + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum ArrangeDirection { + + /// + /// + /// + /// Arranges vertically, from top to bottom. + /// + /// + Down = NativeMethods.ARW_DOWN, + + /// + /// + /// + /// Arranges horizontally, from left to right. + /// + /// + Left = NativeMethods.ARW_LEFT, + + /// + /// + /// + /// Arranges horizontally, from right to left. + /// + /// + Right = NativeMethods.ARW_RIGHT, + + /// + /// + /// + /// Arranges vertically, from bottom to top. + /// + /// + Up = NativeMethods.ARW_UP, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ArrangeStartingPosition.cs b/WindowsForms/Managed/System/WinForms/ArrangeStartingPosition.cs new file mode 100644 index 000000000..911f0d132 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ArrangeStartingPosition.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Specifies the starting position that the system uses to + /// arrange minimized + /// windows. + /// + /// + [Flags] + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum ArrangeStartingPosition { + + /// + /// + /// + /// Starts at the lower-left corner of the screen, which is the default position. + /// + /// + BottomLeft = NativeMethods.ARW_BOTTOMLEFT, + + /// + /// + /// + /// Starts at the lower-right corner of the screen. + /// + /// + BottomRight = NativeMethods.ARW_BOTTOMRIGHT, + + /// + /// + /// + /// Hides minimized windows by moving them off the visible area of the + /// screen. + /// + /// + Hide = NativeMethods.ARW_HIDE, + + /// + /// + /// + /// Starts at the upper-left corner of the screen. + /// + /// + + TopLeft = NativeMethods.ARW_TOPLEFT, + + /// + /// + /// + /// Starts at the upper-right corner of the screen. + /// + /// + TopRight = NativeMethods.ARW_TOPRIGHT, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ArrangedElement.cs b/WindowsForms/Managed/System/WinForms/ArrangedElement.cs new file mode 100644 index 000000000..8acbc2170 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ArrangedElement.cs @@ -0,0 +1,179 @@ + +namespace System.Windows.Forms { + + + + using System.Drawing; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + + + internal abstract class ArrangedElement : Component, IArrangedElement { + + /// + /// + /// + + private Rectangle bounds = Rectangle.Empty; + private IArrangedElement parent = null; + private BitVector32 state = new BitVector32(); + private PropertyStore propertyStore = new PropertyStore(); // Contains all properties that are not always set. + private int suspendCount = 0; + + private static readonly int stateVisible = BitVector32.CreateMask(); + private static readonly int stateDisposing = BitVector32.CreateMask(stateVisible); + private static readonly int stateLocked = BitVector32.CreateMask(stateDisposing); + + + private static readonly int PropControlsCollection = PropertyStore.CreateKey(); + private Control spacer = new Control(); + + internal ArrangedElement() { + this.Padding = DefaultPadding; + this.Margin = DefaultMargin; + state[stateVisible] = true; + } + + public Rectangle Bounds { + get { + return bounds; + } + } + + ArrangedElementCollection IArrangedElement.Children { + get { return GetChildren(); } + } + + IArrangedElement IArrangedElement.Container { + get { return GetContainer(); } + } + + protected virtual Padding DefaultMargin { + get { return Padding.Empty; } + } + + protected virtual Padding DefaultPadding { + get { return Padding.Empty; } + } + + public virtual Rectangle DisplayRectangle { + get { + Rectangle displayRectangle = this.Bounds; + return displayRectangle; + } + } + + public abstract LayoutEngine LayoutEngine { + get; + } + + public Padding Margin { + get { return CommonProperties.GetMargin(this); } + set { + + Debug.Assert((value.Right >= 0 && value.Left >= 0 && value.Top >= 0 && value.Bottom >=0), "who's setting margin negative?"); + value = LayoutUtils.ClampNegativePaddingToZero(value); + if (Margin != value ) { CommonProperties.SetMargin(this, value); } + + } + } + + public virtual Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + Debug.Assert((value.Right >= 0 && value.Left >= 0 && value.Top >= 0 && value.Bottom >=0), "who's setting padding negative?"); + value = LayoutUtils.ClampNegativePaddingToZero(value); + if (Padding != value) { CommonProperties.SetPadding(this, value); } + } + } + + public virtual IArrangedElement Parent { + get { + return parent; + } + set { + parent = value as IArrangedElement; + } + } + + public virtual bool ParticipatesInLayout { + get { + return Visible; + } + } + + PropertyStore IArrangedElement.Properties { + get { + return this.Properties; + } + } + + + private PropertyStore Properties { + get { + return propertyStore; + } + } + + public virtual bool Visible { + get { + return state[stateVisible]; + } + set { + if ( state[stateVisible] != value) { + state[stateVisible] = value; + if (Parent != null){ + LayoutTransaction.DoLayout(this.Parent, this, PropertyNames.Visible); + } + } + } + } + + protected abstract IArrangedElement GetContainer(); + + protected abstract ArrangedElementCollection GetChildren(); + + public virtual Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = LayoutEngine.GetPreferredSize(this, constrainingSize - Padding.Size) + Padding.Size; + + return preferredSize; + } + + public virtual void PerformLayout(IArrangedElement container, string propertyName) { + if (suspendCount <= 0) { + OnLayout(new LayoutEventArgs(container, propertyName)); + } + } + + protected virtual void OnLayout(LayoutEventArgs e) { + bool parentNeedsLayout = LayoutEngine.Layout(this, e); + } + + protected virtual void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { + ((IArrangedElement)this).PerformLayout((IArrangedElement)this, PropertyNames.Size); + } + + public void SetBounds(Rectangle bounds, BoundsSpecified specified) { + // in this case the parent is telling us to refresh our bounds - dont + // call PerformLayout + SetBoundsCore(bounds, specified); + } + + + protected virtual void SetBoundsCore(Rectangle bounds, BoundsSpecified specified) { + if (bounds != this.bounds) { + Rectangle oldBounds = this.bounds; + + this.bounds = bounds; + OnBoundsChanged(oldBounds, bounds); + } + } + + } + +} + + diff --git a/WindowsForms/Managed/System/WinForms/ArrowDirection.cs b/WindowsForms/Managed/System/WinForms/ArrowDirection.cs new file mode 100644 index 000000000..67e45c88b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ArrowDirection.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + // this enum is tightly coupled to Orientation so you can determine quickly + // an orientation from a direction. (direction & Orientation.Vertical == Orientation.Vertical) + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum ArrowDirection { + /// + Up = 0x00 | (int)Orientation.Vertical, + /// + Down = 0x10 | (int)Orientation.Vertical, + /// + Left = 0x00 | (int)Orientation.Horizontal, + /// + Right =0x10 | (int)Orientation.Horizontal, + } +} diff --git a/WindowsForms/Managed/System/WinForms/AssemblyAttributes.cs b/WindowsForms/Managed/System/WinForms/AssemblyAttributes.cs new file mode 100644 index 000000000..701cb9ff9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AssemblyAttributes.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +// Always hard bind to System.dll and System.Drawing.dll + +using System.Runtime.CompilerServices; + +[assembly:DependencyAttribute("System,", LoadHint.Always)] +[assembly:DependencyAttribute("System.Drawing,", LoadHint.Always)] +[assembly:DependencyAttribute("System.Core", LoadHint.Sometimes)] +// This is now trun on by default, use source file NO_RUNTIMECOMPATIBILITY_ATTRIBUTE flag to control this +// [assembly:RuntimeCompatibility(WrapNonExceptionThrows = true)] +[assembly:System.Runtime.CompilerServices.StringFreezingAttribute()] +[assembly:System.Runtime.InteropServices.TypeLibVersion(2, 4)] + +// Opts into the VS loading icons from the Icon Satellite assembly: System.Windows.Forms.VisualStudio..0.dll +[assembly:System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute()] + diff --git a/WindowsForms/Managed/System/WinForms/AutoCompleteMode.cs b/WindowsForms/Managed/System/WinForms/AutoCompleteMode.cs new file mode 100644 index 000000000..f0eefb404 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoCompleteMode.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// Specifies the autocomplete mode for ComboBox and TextBox AutoComplete Feature. + /// + /// + public enum AutoCompleteMode { + + /// + /// + /// + /// Disables the AutoComplete Feature for ComboBox and TextBox. + /// + /// + None = 0, + + /// + /// + /// + /// Displays the auxiliary drop-down list associated with the edit control, this drop-down is populated + /// with one or more suggested completed strings. + /// + /// + Suggest = 0x1, + + /// + /// + /// + /// Appends the remainder of the most likely candidate string to the existing characters, + /// hightlighting the appended characters. + /// + /// + Append = 0x2, + + /// + /// + /// + /// The AutoSuggest and AutoAppend are applied in conjuction. + /// + /// + SuggestAppend = Suggest | Append + } +} + diff --git a/WindowsForms/Managed/System/WinForms/AutoCompleteSource.cs b/WindowsForms/Managed/System/WinForms/AutoCompleteSource.cs new file mode 100644 index 000000000..19b756bb6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoCompleteSource.cs @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + + /// + /// + /// + /// Specifies the AutoCompleteSource for ComboBox and TextBox AutoComplete Feature. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum AutoCompleteSource { + + /// + /// + /// + /// This option includes the file system as the source. + /// + /// + FileSystem = 0x00000001, + + + /// + /// + /// + /// This option includes the URL's in the users history list. + /// + /// + HistoryList = 0x00000002, + + + /// + /// + /// + /// This option includes the URL's in the users recently used list. + /// + /// + RecentlyUsedList = 0x00000004, + + /// + /// + /// + /// This option is equivalent to HistoryList | RecentlyUsedList. + /// + /// + AllUrl = HistoryList | RecentlyUsedList, + + /// + /// + /// + /// This option is equivalent to FILESYSTEM | AllUrl. This is the default value + /// when the AutoCompleteMode has been set to a non default value. + /// + /// + AllSystemSources = FileSystem | AllUrl, + + + /// + /// + /// + /// This option is allows to autoComplete just directory names and not the files inside. + /// + /// + FileSystemDirectories = 0x00000020, + + + /// + /// + /// + /// This option includes stirngs from a built in String Collection object. + /// + /// + CustomSource = 0x00000040, + + /// + /// + /// + /// The default value specifying the no AutoCompleteSource is currently in use. + /// + /// + None = 0x00000080, + + /// + /// + /// + /// The items of the combobox represent the source. + /// + /// + ListItems = 0x00000100 + } +} diff --git a/WindowsForms/Managed/System/WinForms/AutoCompleteStringCollection.cs b/WindowsForms/Managed/System/WinForms/AutoCompleteStringCollection.cs new file mode 100644 index 000000000..c511cda0a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoCompleteStringCollection.cs @@ -0,0 +1,275 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Collections; + using System.Security.Permissions; + using System.ComponentModel; + + /// + /// + /// Represents a collection of strings. + /// + public class AutoCompleteStringCollection : IList { + + CollectionChangeEventHandler onCollectionChanged; + private ArrayList data = new ArrayList(); + + public AutoCompleteStringCollection() + { + + } + + /// + /// + /// Represents the entry at the specified index of the . + /// + public string this[int index] { + get { + return ((string)data[index]); + } + set { + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, data[index])); + data[index] = value; + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, value)); + } + } + + /// + /// + /// Gets the number of strings in the + /// . + /// + public int Count { + get { + return data.Count; + } + } + + /// + bool IList.IsReadOnly + { + get + { + return false; + } + } + + /// + bool IList.IsFixedSize + { + get + { + return false; + } + } + + + /// + /// + /// [To be supplied.] + /// + public event CollectionChangeEventHandler CollectionChanged + { + add + { + this.onCollectionChanged += value; + } + remove + { + this.onCollectionChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + protected void OnCollectionChanged(CollectionChangeEventArgs e) + { + if (this.onCollectionChanged != null) + { + this.onCollectionChanged(this, e); + } + } + + + /// + /// + /// Adds a string with the specified value to the + /// . + /// + public int Add(string value) { + int index = data.Add(value); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, value)); + return index; + } + + /// + /// + /// Copies the elements of a string array to the end of the . + /// + public void AddRange(string[] value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + data.AddRange(value); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// Removes all the strings from the + /// . + /// + public void Clear() { + data.Clear(); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// Gets a value indicating whether the + /// contains a string with the specified + /// value. + /// + public bool Contains(string value) { + return data.Contains(value); + } + + /// + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + public void CopyTo(string[] array, int index) { + data.CopyTo(array, index); + } + + /// + /// + /// Returns the index of the first occurrence of a string in + /// the . + /// + public int IndexOf(string value) { + return data.IndexOf(value); + } + + /// + /// + /// Inserts a string into the at the specified + /// index. + /// + public void Insert(int index, string value) { + data.Insert(index, value); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, value)); + } + + /// + /// + /// Gets a value indicating whether the is read-only. + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Gets a value indicating whether access to the + /// + /// is synchronized (thread-safe). + /// + public bool IsSynchronized { + get { + return false; + } + } + + /// + /// + /// Removes a specific string from the + /// . + /// + public void Remove(string value) { + data.Remove(value); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, value)); + } + + /// + /// + /// Removes the string at the specified index of the . + /// + public void RemoveAt(int index) { + string value = (string)data[index]; + data.RemoveAt(index); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, value)); + } + + /// + /// + /// Gets an object that can be used to synchronize access to the . + /// + public object SyncRoot { + [HostProtection(Synchronization=true)] + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + get { + return this; + } + } + + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + this[index] = (string)value; + } + } + + /// + int IList.Add(object value) { + return Add((string)value); + } + + /// + bool IList.Contains(object value) { + return Contains((string) value); + } + + + /// + int IList.IndexOf(object value) { + return IndexOf((string)value); + } + + /// + void IList.Insert(int index, object value) { + Insert(index, (string)value); + } + + /// + void IList.Remove(object value) { + Remove((string)value); + } + + /// + void ICollection.CopyTo(Array array, int index) { + data.CopyTo(array, index); + } + + /// + public IEnumerator GetEnumerator() { + return data.GetEnumerator(); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/AutoScaleMode.cs b/WindowsForms/Managed/System/WinForms/AutoScaleMode.cs new file mode 100644 index 000000000..cb373e53e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoScaleMode.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// Specifies the auto scaling mode used by a container control. + /// + public enum AutoScaleMode { + + /// + /// + /// AutoScale is turned off. + /// + None, + + /// + /// + /// Controls scale according to the dimensions of the font they are using. + /// + Font, + + /// + /// + /// Controls scale according to the display Dpi. + /// + Dpi, + + /// + /// + /// Controls scale according to their parent's scaling mode. If there is no parent, + /// This behaves as if AutoScaleMode.None were set. + /// + Inherit + } +} + diff --git a/WindowsForms/Managed/System/WinForms/AutoSizeMode.cs b/WindowsForms/Managed/System/WinForms/AutoSizeMode.cs new file mode 100644 index 000000000..2336ed2af --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoSizeMode.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + /// + /// + /// + /// Specifies how the control will behave when its AutoSize property is enabled + /// + /// + public enum AutoSizeMode { + + /// + /// The same behavior as you get for controls with AutoSize and no AutoSizeMode property. + /// The control will grow or shrink to encompass the contents (e.g. text for a Button, child + /// controls for a container). The MinimumSize and MaximumSize are followed, but the current + // value of the Size property is ignored. + /// + GrowAndShrink, + + + /// + /// The control will grow as much as it needs to encompass its contents (e.g. text for a button, + /// child controls for a container), but will not shrink smaller than its Size, whichever is larger. + /// + GrowOnly + } +} + + + diff --git a/WindowsForms/Managed/System/WinForms/AutoValidate.cs b/WindowsForms/Managed/System/WinForms/AutoValidate.cs new file mode 100644 index 000000000..70e8f9f79 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AutoValidate.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + /// + /// + /// For a given container control, determines whether the data in child controls + /// will automatically be validated when the user attempts to change the focus. + /// + public enum AutoValidate { + + /// + /// + /// Controls in this container will not be validated when the focus changes. + /// + Disable = 0, + + /// + /// + /// Controls in this container will be validated when the focus changes. + /// If a validation error occurs, the focus is forced to stay in the current control. + /// + EnablePreventFocusChange = 1, + + /// + /// + /// Controls in this container will be validated when the focus changes. + /// If a validation error occurs, the focus is allowed to move to the other control. + /// + EnableAllowFocusChange = 2, + + /// + /// + /// AutoValidate setting for this container is determined by its parent container. + /// + Inherit = -1, + } +} diff --git a/WindowsForms/Managed/System/WinForms/AxHost.cs b/WindowsForms/Managed/System/WinForms/AxHost.cs new file mode 100644 index 000000000..84e9275b1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/AxHost.cs @@ -0,0 +1,7510 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Configuration.Assemblies; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Imaging; + using System.Drawing.Design; + using System.Globalization; + using System.IO; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Serialization.Formatters.Binary; + using System.Runtime.Serialization; + using System.Security; + using System.Security.Permissions; + using System.Threading; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.ComponentModel.Com2Interop; + using System.Windows.Forms.Design; + using System.Runtime.Versioning; + + /// + /// + /// + /// + /// Wraps ActiveX controls and exposes them as + /// fully featured windows forms controls. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + ToolboxItem(false), + DesignTimeVisible(false), + DefaultEvent("Enter"), + Designer("System.Windows.Forms.Design.AxHostDesigner, " + AssemblyRef.SystemDesign), + PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), + PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust") + ] + public abstract class AxHost : Control, ISupportInitialize, ICustomTypeDescriptor { + + private static TraceSwitch AxHTraceSwitch = new TraceSwitch("AxHTrace", "ActiveX handle tracing"); + private static TraceSwitch AxPropTraceSwitch = new TraceSwitch("AxPropTrace", "ActiveX property tracing"); + private static TraceSwitch AxHostSwitch = new TraceSwitch("AxHost", "ActiveX host creation"); + private static BooleanSwitch AxIgnoreTMSwitch = new BooleanSwitch("AxIgnoreTM", "ActiveX switch to ignore thread models"); + private static BooleanSwitch AxAlwaysSaveSwitch = new BooleanSwitch("AxAlwaysSave", "ActiveX to save all controls regardless of their IsDirty function return value"); + + /// + /// Flags which may be passed to the AxHost constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class AxFlags { + /// + /// Indicates that the context menu for the control should not contain an + /// "Edit" verb unless the activeX controls itself decides to proffer it. + /// By default, all wrapped activeX controls will contain an edit verb. + /// + internal const int PreventEditMode = 0x1; + + /// + /// Indicated that the context menu for the control should contain + /// a "Properties..." verb which may be used to show the property + /// pages for the control. Note that even if this flag is + /// specified, the verb will not appear unless the control + /// proffers a set of property pages. + /// [Since most activeX controls alreay have their own properties verb + /// on the context menu, the default is not to include one specified by + /// this flag.] + /// + internal const int IncludePropertiesVerb = 0x2; + + /// + /// + /// + internal const int IgnoreThreadModel = 0x10000000; + } + + private static COMException E_NOTIMPL = new COMException(SR.GetString(SR.AXNotImplemented), unchecked((int)0x80000001)); + private static COMException E_INVALIDARG = new COMException(SR.GetString(SR.AXInvalidArgument), unchecked((int)0x80070057)); + private static COMException E_FAIL = new COMException(SR.GetString(SR.AXUnknownError), unchecked((int)0x80004005)); + private static COMException E_NOINTERFACE = new COMException(SR.GetString(SR.AxInterfaceNotSupported), unchecked((int)0x80004002)); + + private const int INPROC_SERVER = 1; + private const int OC_PASSIVE = 0; + private const int OC_LOADED = 1; // handler, but no server [ocx created] + private const int OC_RUNNING = 2; // server running, invisible [iqa & depersistance] + private const int OC_INPLACE = 4; // server in-place active [inplace] + private const int OC_UIACTIVE = 8;// server is UI active [uiactive] + private const int OC_OPEN = 16; // server is being open edited [not used] + + private const int EDITM_NONE = 0; // object not being edited + private const int EDITM_OBJECT = 1; // object provided an edit verb and we invoked it + private const int EDITM_HOST = 2; // we invoked our own edit verb + + private const int STG_UNKNOWN = -1; + private const int STG_STREAM = 0; + private const int STG_STREAMINIT = 1; + private const int STG_STORAGE = 2; + + private const int OLEIVERB_SHOW = -1; + private const int OLEIVERB_HIDE = -3; + private const int OLEIVERB_UIACTIVATE = -4; + private const int OLEIVERB_INPLACEACTIVATE =-5; + private const int OLEIVERB_PROPERTIES = -7; + private const int OLEIVERB_PRIMARY = 0; + + private readonly int REGMSG_MSG = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_subclassCheck"); + private const int REGMSG_RETVAL = 123; + + private static int logPixelsX = -1; + private static int logPixelsY = -1; + + private static Guid icf2_Guid = typeof(UnsafeNativeMethods.IClassFactory2).GUID; + private static Guid ifont_Guid = typeof(UnsafeNativeMethods.IFont).GUID; + private static Guid ifontDisp_Guid = typeof(SafeNativeMethods.IFontDisp).GUID; + private static Guid ipicture_Guid = typeof(UnsafeNativeMethods.IPicture).GUID; + private static Guid ipictureDisp_Guid = typeof(UnsafeNativeMethods.IPictureDisp).GUID; + private static Guid ivbformat_Guid = typeof(UnsafeNativeMethods.IVBFormat).GUID; + private static Guid ioleobject_Guid = typeof(UnsafeNativeMethods.IOleObject).GUID; + private static Guid dataSource_Guid = new Guid("{7C0FFAB3-CD84-11D0-949A-00A0C91110ED}"); + private static Guid windowsMediaPlayer_Clsid = new Guid("{22d6f312-b0f6-11d0-94ab-0080c74c7e95}"); + private static Guid comctlImageCombo_Clsid = new Guid("{a98a24c0-b06f-3684-8c12-c52ae341e0bc}"); + private static Guid maskEdit_Clsid = new Guid("{c932ba85-4374-101b-a56c-00aa003668dc}"); + + // Static state for perf optimization + // + private static Hashtable fontTable; + + // BitVector32 masks for various internal state flags. + // + private static readonly int ocxStateSet = BitVector32.CreateMask(); + private static readonly int editorRefresh = BitVector32.CreateMask(ocxStateSet); + private static readonly int listeningToIdle = BitVector32.CreateMask(editorRefresh); + private static readonly int refreshProperties = BitVector32.CreateMask(listeningToIdle); + + private static readonly int checkedIppb = BitVector32.CreateMask(refreshProperties); + private static readonly int checkedCP = BitVector32.CreateMask(checkedIppb); + private static readonly int fNeedOwnWindow = BitVector32.CreateMask(checkedCP); + private static readonly int fOwnWindow = BitVector32.CreateMask(fNeedOwnWindow); + + private static readonly int fSimpleFrame = BitVector32.CreateMask(fOwnWindow); + private static readonly int fFakingWindow = BitVector32.CreateMask(fSimpleFrame); + private static readonly int rejectSelection = BitVector32.CreateMask(fFakingWindow); + private static readonly int ownDisposing = BitVector32.CreateMask(rejectSelection); + + private static readonly int sinkAttached = BitVector32.CreateMask(ownDisposing); + private static readonly int disposed = BitVector32.CreateMask(sinkAttached); + private static readonly int manualUpdate = BitVector32.CreateMask(disposed); + private static readonly int addedSelectionHandler = BitVector32.CreateMask(manualUpdate); + + private static readonly int valueChanged = BitVector32.CreateMask(addedSelectionHandler); + private static readonly int handlePosRectChanged = BitVector32.CreateMask(valueChanged); + private static readonly int siteProcessedInputKey = BitVector32.CreateMask(handlePosRectChanged); + private static readonly int needLicenseKey = BitVector32.CreateMask(siteProcessedInputKey); + + private static readonly int inTransition = BitVector32.CreateMask(needLicenseKey); + private static readonly int processingKeyUp = BitVector32.CreateMask(inTransition); + private static readonly int assignUniqueID = BitVector32.CreateMask(processingKeyUp); + private static readonly int renameEventHooked = BitVector32.CreateMask(assignUniqueID); + + + private BitVector32 axState = new BitVector32(); + + private int storageType = STG_UNKNOWN; + private int ocState = OC_PASSIVE; + private int miscStatusBits; + private int freezeCount = 0; + private int flags = 0; + private int selectionStyle = 0; + private int editMode = EDITM_NONE; + private int noComponentChange = 0; + + private IntPtr wndprocAddr = IntPtr.Zero; + + private Guid clsid; + private string text = ""; + private string licenseKey = null; + + private readonly OleInterfaces oleSite; + private AxComponentEditor editor; + private AxContainer container; + private ContainerControl containingControl; + private ContainerControl newParent; + private AxContainer axContainer; + private State ocxState; + private IntPtr hwndFocus = IntPtr.Zero; + + // CustomTypeDescriptor related state + // + private Hashtable properties = null; + private Hashtable propertyInfos = null; + private PropertyDescriptorCollection propsStash = null; + private Attribute[] attribsStash = null; + + // interface pointers to the ocx + // + private Object instance; + private UnsafeNativeMethods.IOleInPlaceObject iOleInPlaceObject; + private UnsafeNativeMethods.IOleObject iOleObject; + private UnsafeNativeMethods.IOleControl iOleControl; + private UnsafeNativeMethods.IOleInPlaceActiveObject iOleInPlaceActiveObject; + private UnsafeNativeMethods.IOleInPlaceActiveObject iOleInPlaceActiveObjectExternal; + private NativeMethods.IPerPropertyBrowsing iPerPropertyBrowsing; + private NativeMethods.ICategorizeProperties iCategorizeProperties; + private UnsafeNativeMethods.IPersistPropertyBag iPersistPropBag; + private UnsafeNativeMethods.IPersistStream iPersistStream; + private UnsafeNativeMethods.IPersistStreamInit iPersistStreamInit; + private UnsafeNativeMethods.IPersistStorage iPersistStorage; + + private AboutBoxDelegate aboutBoxDelegate = null; + private EventHandler selectionChangeHandler; + + private bool isMaskEdit; + private bool ignoreDialogKeys; + + private EventHandler onContainerVisibleChanged; + + // These should be in the order given by the PROPCAT_X values + // Also, note that they are not to be localized... + + private static CategoryAttribute[] categoryNames = new CategoryAttribute [] { + null, + new WinCategoryAttribute("Default"), + new WinCategoryAttribute("Default"), + new WinCategoryAttribute("Font"), + new WinCategoryAttribute("Layout"), + new WinCategoryAttribute("Appearance"), + new WinCategoryAttribute("Behavior"), + new WinCategoryAttribute("Data"), + new WinCategoryAttribute("List"), + new WinCategoryAttribute("Text"), + new WinCategoryAttribute("Scale"), + new WinCategoryAttribute("DDE") + }; + + private Hashtable objectDefinedCategoryNames = null; // Integer -> String + +#if DEBUG + static AxHost() { + Debug.Assert((int)DockStyle.None == (int)NativeMethods.ActiveX.ALIGN_NO_CHANGE,"align value mismatch"); + Debug.Assert((int)DockStyle.Top == (int)NativeMethods.ActiveX.ALIGN_TOP,"align value mismatch"); + Debug.Assert((int)DockStyle.Bottom == (int)NativeMethods.ActiveX.ALIGN_BOTTOM,"align value mismatch"); + Debug.Assert((int)DockStyle.Left == (int)NativeMethods.ActiveX.ALIGN_LEFT,"align value mismatch"); + Debug.Assert((int)DockStyle.Right == (int)NativeMethods.ActiveX.ALIGN_RIGHT,"align value mismatch"); + Debug.Assert((int)MouseButtons.Left == 0x00100000, "mb.left mismatch"); + Debug.Assert((int)MouseButtons.Right == 0x00200000, "mb.right mismatch"); + Debug.Assert((int)MouseButtons.Middle == 0x00400000, "mb.middle mismatch"); + Debug.Assert((int)Keys.Shift == 0x00010000, "key.shift mismatch"); + Debug.Assert((int)Keys.Control == 0x00020000, "key.control mismatch"); + Debug.Assert((int)Keys.Alt == 0x00040000, "key.alt mismatch"); + } +#endif + + /// + /// + /// Creates a new instance of a control which wraps an activeX control given by the + /// clsid parameter and flags of 0. + /// + protected AxHost(string clsid) : this(clsid, 0) { + } + + /// + /// + /// Creates a new instance of a control which wraps an activeX control given by the + /// clsid and flags parameters. + /// + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + protected AxHost(string clsid, int flags) : base() { + if (Application.OleRequired() != ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.AXMTAThread, clsid)); + } + + this.oleSite = new OleInterfaces(this); + this.selectionChangeHandler = new EventHandler(this.OnNewSelection); + this.clsid = new Guid(clsid); + this.flags = flags; + + this.axState[assignUniqueID] = !this.GetType().GUID.Equals(comctlImageCombo_Clsid); + this.axState[needLicenseKey] = true; + this.axState[rejectSelection] = true; + + isMaskEdit = this.clsid.Equals(AxHost.maskEdit_Clsid); + this.onContainerVisibleChanged = new EventHandler(this.OnContainerVisibleChanged); + } + + private bool CanUIActivate { + get { + return IsUserMode() || editMode != EDITM_NONE; + } + } + + /// + /// + /// Returns the CreateParams used to create the handle for this control. + /// + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + if (axState[fOwnWindow] && IsUserMode()) { + cp.Style = cp.Style & (~NativeMethods.WS_VISIBLE); + } + return cp; + } + } + + private bool GetAxState(int mask) { + return this.axState[mask]; + } + + private void SetAxState(int mask, bool value) { + this.axState[mask] = value; + } + + /// + /// + /// AxHost will call this when it is ready to create the underlying ActiveX object. + /// Wrappers will override this and cast the pointer obtained by calling getOcx() to + /// their own interfaces. getOcx() should not usually be called before this function. + /// Note: calling begin will result in a call to this function. + /// + protected virtual void AttachInterfaces() { + } + + private void RealizeStyles() { + SetStyle(ControlStyles.UserPaint, false); + int bits = 0; + int hr = GetOleObject().GetMiscStatus(NativeMethods.ActiveX.DVASPECT_CONTENT, out bits); + if (!NativeMethods.Failed(hr)) { + miscStatusBits = bits; + ParseMiscBits(miscStatusBits); + } + } + + // Control overrides: + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + return base.BackColor; + } + + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// Hide ImeMode: it doesn't make sense for this control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public ImeMode ImeMode + { + get + { + return base.ImeMode; + } + set + { + base.ImeMode = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseClick")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseDoubleClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseDoubleClick")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Cursor Cursor { + get { + return base.Cursor; + } + + set { + base.Cursor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ContextMenu ContextMenu { + get { + return base.ContextMenu; + } + + set { + base.ContextMenu = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(75, 23); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual new bool Enabled { + get { + return base.Enabled; + } + + set { + base.Enabled = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + + set { + base.Font = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + + set { + base.ForeColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + Localizable(true) + ] + public new virtual bool RightToLeft { + get { + RightToLeft rtol = base.RightToLeft; + return rtol == System.Windows.Forms.RightToLeft.Yes; + } + + set { + base.RightToLeft = (value) ? System.Windows.Forms.RightToLeft.Yes : System.Windows.Forms.RightToLeft.No; + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override string Text { + get { + return text; + } + + set { + text = value; + } + } + + internal override bool CanAccessProperties { + get { + int ocState = GetOcState(); + return(axState[fOwnWindow] && + (ocState > OC_RUNNING || (IsUserMode() && ocState >= OC_RUNNING)) || + ocState >= OC_INPLACE); + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected bool PropsValid() { + return CanAccessProperties; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void BeginInit() { + } + + /// + /// + /// Signals the object that loading of all peer components and property + /// sets are complete. + /// It should be possible to invoke any property get or set after calling this method. + /// Note that a sideeffect of this method is the creation of the parent control's + /// handle, therefore, this control must be parented before begin is called + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void EndInit() { + if (ParentInternal != null) { + ParentInternal.CreateControl(true); + + ContainerControl f = ContainingControl; + if (f != null) { + f.VisibleChanged += this.onContainerVisibleChanged; + } + } + } + + private void OnContainerVisibleChanged(object sender, EventArgs e) { + ContainerControl f = ContainingControl; + if (f != null) { + if (f.Visible && Visible && !axState[fOwnWindow]) { + MakeVisibleWithShow(); + } + else if (!f.Visible && Visible && IsHandleCreated && GetOcState() >= OC_INPLACE) { + HideAxControl(); + } + else if (f.Visible && !GetState(STATE_VISIBLE) && IsHandleCreated && GetOcState() >= OC_INPLACE) { + HideAxControl(); + } + } + } + + // + /// + /// + /// Determines if the control is in edit mode. + /// + [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool EditMode { + get { + return editMode != EDITM_NONE; + } + } + + /// + /// + /// Determines if this control has an about box. + /// + [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool HasAboutBox { + get { + return aboutBoxDelegate != null; + } + } + + private int NoComponentChangeEvents { + get { + return noComponentChange; + } + + set { + noComponentChange = value; + } + } + + // + /// + /// + /// Shows the about box for this control. + /// + public void ShowAboutBox() { + if (aboutBoxDelegate != null) { + aboutBoxDelegate(); + } + } + + // + /// + /// Retrieves the OCX control flags. + /// +#if false + // FxCop: Currently not used + [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + private int OcxFlags { + get { + return flags; + } + } +#endif + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackColorChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackgroundImageChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackgroundImageLayoutChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BindingContextChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BindingContextChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ContextMenuChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ContextMenuChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CursorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "CursorChanged")); + } + remove { + } + } + + /// + /// + /// Occurs when the control is enabled. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler EnabledChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "EnabledChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "FontChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ForeColorChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler RightToLeftChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "RightToLeftChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "TextChanged")); + } + remove { + } + } + + /// + /// + /// Occurs when the control is clicked. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler Click { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Click")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragDrop { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragDrop")); + } + remove { + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragEnter { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragEnter")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragOver { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragOver")); + } + remove { + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DragLeave { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragLeave")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event GiveFeedbackEventHandler GiveFeedback { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "GiveFeedback")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event HelpEventHandler HelpRequested { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "HelpRequested")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event PaintEventHandler Paint { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Paint")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event QueryContinueDragEventHandler QueryContinueDrag { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "QueryContinueDrag")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "QueryAccessibilityHelp")); + } + remove { + } + } + + /// + /// + /// Occurs when the control is double clicked. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DoubleClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DoubleClick")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ImeModeChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ImeModeChanged")); + } + remove { + } + } + + + /// + /// + /// Occurs when a key is pressed down while the control has focus. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyEventHandler KeyDown { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyDown")); + } + remove { + } + } + + + /// + /// + /// Occurs when a key is pressed while the control has focus. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyPressEventHandler KeyPress { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyPress")); + } + remove { + } + } + + + /// + /// + /// Occurs when a key is released while the control has focus. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyEventHandler KeyUp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyUp")); + } + remove { + } + } + + + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event LayoutEventHandler Layout { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Layout")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is + /// pressed. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseDown { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseDown")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer enters the AxHost. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseEnter { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseEnter")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer leaves the AxHost. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseLeave { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseLeave")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer hovers over the contro. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseHover { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseHover")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer is moved over the AxHost. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseMove { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseMove")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is released. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseUp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseUp")); + } + remove { + } + } + + /// + /// + /// Occurs when the mouse wheel moves while the control has focus. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseWheel { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseWheel")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event UICuesEventHandler ChangeUICues { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ChangeUICues")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler StyleChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "StyleChanged")); + } + remove { + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnForeColorChanged(EventArgs e) { + base.OnForeColorChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + } + + private void AmbientChanged(int dispid) { + if (GetOcx() != null) { + try { + Invalidate(); + GetOleControl().OnAmbientPropertyChange(dispid); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + } + } + + private bool OwnWindow() { + return axState[fOwnWindow] || axState[fFakingWindow]; + } + + private IntPtr GetHandleNoCreate() { + if (IsHandleCreated) return Handle; + return IntPtr.Zero; + } + + private ISelectionService GetSelectionService() { + return GetSelectionService(this); + } + + private static ISelectionService GetSelectionService(Control ctl) { + ISite site = ctl.Site; + if (site != null) { + Object o = site.GetService(typeof(ISelectionService)); + Debug.Assert(o == null || o is ISelectionService, "service must implement ISelectionService"); + //Note: if o is null, we want to return null anyway. Happy day. + return o as ISelectionService; + } + return null; + } + + private void AddSelectionHandler() { + if (axState[addedSelectionHandler]) return; + ISelectionService iss = GetSelectionService(); + if (iss != null) { + iss.SelectionChanging += selectionChangeHandler; + } + axState[addedSelectionHandler] = true; + } + + private void OnComponentRename(object sender, ComponentRenameEventArgs e) + { + // When we're notified of a rename, see if this is the componnent that is being + // renamed. + // + if (e.Component == this) + { + // if it is, call DISPID_AMBIENT_DISPLAYNAME directly on the + // control itself. + // + UnsafeNativeMethods.IOleControl oleCtl = this.GetOcx() as UnsafeNativeMethods.IOleControl; + if (oleCtl != null) + { + oleCtl.OnAmbientPropertyChange(NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYNAME); + } + } + } + + + private bool RemoveSelectionHandler() { + if (!axState[addedSelectionHandler]) return false; + ISelectionService iss = GetSelectionService(); + if (iss != null) { + iss.SelectionChanging -= selectionChangeHandler; + } + axState[addedSelectionHandler] = false; + return true; + } + + private void SyncRenameNotification(bool hook) { + if (DesignMode && hook != axState[renameEventHooked]) + { + // if we're in design mode, listen to the following events from the component change service + // + IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + + if (changeService != null) + { + if (hook) { + changeService.ComponentRename += new ComponentRenameEventHandler(OnComponentRename); + } + else { + changeService.ComponentRename -= new ComponentRenameEventHandler(OnComponentRename); + } + axState[renameEventHooked] = hook; + } + } + } + + /// + /// + /// Sets the site of this component. A non-null value indicates that the + /// component has been added to a container, and a null value indicates that + /// the component is being removed from a container. + /// + public override ISite Site { + set { + // If we are disposed then just return. + if (axState[disposed]) + { + return; + } + bool reAddHandler = RemoveSelectionHandler(); + bool olduMode = IsUserMode(); + + // clear the old hook + // + SyncRenameNotification(false); + + base.Site = value; + bool newuMode = IsUserMode(); + if (!newuMode) GetOcxCreate(); + if (reAddHandler) AddSelectionHandler(); + + SyncRenameNotification(value != null); + + // For inherited forms we create the OCX first in User mode + // and then we get sited. At that time, we have to re-activate + // the OCX by transitioning down to and up to the current state. + // + if (value != null && !newuMode && olduMode != newuMode && GetOcState() > OC_LOADED) { + TransitionDownTo(OC_LOADED); + TransitionUpTo(OC_INPLACE); + ContainerControl f = ContainingControl; + if (f != null && f.Visible && Visible) + MakeVisibleWithShow(); + } + + if (olduMode != newuMode && !IsHandleCreated && !axState[disposed]) { + if (GetOcx() != null) { + RealizeStyles(); + } + } + if (!newuMode) { + //SetupClass_Info(this); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLostFocus(EventArgs e) { + // ASURT 93669 + // Office WebControl and MS DDS control create a child window that gains + // focus in order to handle keyboard input. Since, UIDeactivate() could + // destroy that window, these controls will crash trying to process WM_CHAR. + // We now check to see if we are losing focus to a child, and if so, not call + // UIDeactivate(). + // + + bool uiDeactivate = (GetHandleNoCreate() != hwndFocus); + + if (uiDeactivate && IsHandleCreated) { + uiDeactivate = !UnsafeNativeMethods.IsChild(new HandleRef(this, GetHandleNoCreate()), new HandleRef(null, hwndFocus)); + } + + base.OnLostFocus(e); + if (uiDeactivate) { + UiDeactivate(); + } + + } + + private void OnNewSelection(Object sender, EventArgs e) { + if (IsUserMode()) return; + ISelectionService iss = GetSelectionService(); + // What we care about: + // if we are uiactive and we lose selection, then we need to uideactivate ourselves... + if (iss != null) { + if (GetOcState() >= OC_UIACTIVE && !iss.GetComponentSelected(this)) { + // need to deactivate... + int hr = UiDeactivate(); + if (NativeMethods.Failed(hr)) { + // not much we can do here... + Debug.Fail("Failed to UiDeactivate: " + hr.ToString(CultureInfo.InvariantCulture)); + } + } + if (!iss.GetComponentSelected(this)) { + if (editMode != EDITM_NONE) { + GetParentContainer().OnExitEditMode(this); + editMode = EDITM_NONE; + } + // need to exit edit mode... + SetSelectionStyle(1); + RemoveSelectionHandler(); + } + else { + // The AX Host designer will offer an extender property called "SelectionStyle" + // + PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["SelectionStyle"]; + + if (prop != null && prop.PropertyType == typeof(int)) { + int curSelectionStyle = (int)prop.GetValue(this); + if (curSelectionStyle != this.selectionStyle) { + prop.SetValue(this, selectionStyle); + } + } + } + } + } + + //DrawToBitmap doesn't work for this control, so we should hide it. We'll + //still call base so that this has a chance to work if it can. + [EditorBrowsable(EditorBrowsableState.Never)] + new public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) + { + base.DrawToBitmap(bitmap, targetBounds); + } + + /// + /// + /// Creates a handle for this control. This method is called by the .NET framework, this should + /// not be called. + /// + protected override void CreateHandle() { + if (!IsHandleCreated) { + + TransitionUpTo(OC_RUNNING); + if (!axState[fOwnWindow]) { + if (axState[fNeedOwnWindow]) { + Debug.Assert(!Visible, "if we were visible we would not be needing a fake window..."); + axState[fNeedOwnWindow] = false; + axState[fFakingWindow] = true; + base.CreateHandle(); + // note that we do not need to attach the handle because the work usually done in there + // will be done in Control's wndProc on WM_CREATE... + } + else { + TransitionUpTo(OC_INPLACE); + // it is possible that we were hidden while in place activating, in which case we don't + // really have a handle now because the act of hiding could have destroyed it + // so, just call ourselves again recursively, and if we dont't have a handle, we will + // just take the "axState[fNeedOwnWindow]" path above... + if (axState[fNeedOwnWindow]) { + Debug.Assert(!IsHandleCreated, "if we need a fake window, we can't have a real one"); + CreateHandle(); + return; + } + } + } + else { + SetState(STATE_VISIBLE, false); + base.CreateHandle(); + } + GetParentContainer().ControlCreated(this); + } + } + + private NativeMethods.COMRECT GetClipRect(NativeMethods.COMRECT clipRect) { + if (clipRect != null) { + FillInRect(clipRect, new Rectangle(0, 0, 32000, 32000)); + } + return clipRect; + } + + private static int SetupLogPixels(bool force) { + if (logPixelsX == -1 || force) { + IntPtr hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + if (hDC == IntPtr.Zero) + return NativeMethods.E_FAIL; + logPixelsX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSX); + logPixelsY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSY); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "log pixels are: "+logPixelsX.ToString(CultureInfo.InvariantCulture)+" "+logPixelsY.ToString(CultureInfo.InvariantCulture)); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); + } + + return NativeMethods.S_OK; + } + + private void HiMetric2Pixel(NativeMethods.tagSIZEL sz, NativeMethods.tagSIZEL szout) { + NativeMethods._POINTL phm = new NativeMethods._POINTL(); + phm.x = sz.cx; + phm.y = sz.cy; + NativeMethods.tagPOINTF pcont = new NativeMethods.tagPOINTF(); + ((UnsafeNativeMethods.IOleControlSite)oleSite).TransformCoords(phm, pcont, NativeMethods.ActiveX.XFORMCOORDS_SIZE | NativeMethods.ActiveX.XFORMCOORDS_HIMETRICTOCONTAINER); + szout.cx = (int)pcont.x; + szout.cy = (int)pcont.y; + } + + private void Pixel2hiMetric(NativeMethods.tagSIZEL sz, NativeMethods.tagSIZEL szout) { + NativeMethods.tagPOINTF pcont = new NativeMethods.tagPOINTF(); + pcont.x = (float) sz.cx; + pcont.y = (float) sz.cy; + NativeMethods._POINTL phm = new NativeMethods._POINTL(); + ((UnsafeNativeMethods.IOleControlSite)oleSite).TransformCoords(phm, pcont, NativeMethods.ActiveX.XFORMCOORDS_SIZE | NativeMethods.ActiveX.XFORMCOORDS_CONTAINERTOHIMETRIC); + szout.cx = phm.x; + szout.cy = phm.y; + } + + private static int Pixel2Twip(int v, bool xDirection) { + SetupLogPixels(false); + int logP = xDirection ? logPixelsX : logPixelsY; + return(int) ((((double)v) / logP) * 72.0 * 20.0); + } + + private static int Twip2Pixel(double v, bool xDirection) { + SetupLogPixels(false); + int logP = xDirection ? logPixelsX : logPixelsY; + return(int) (((v / 20.0) / 72.0) * logP); + } + + private static int Twip2Pixel(int v, bool xDirection) { + SetupLogPixels(false); + int logP = xDirection ? logPixelsX : logPixelsY; + return(int) (((((double) v) / 20.0) / 72.0) * logP); + } + + + private Size SetExtent(int width, int height) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "setting extent to "+width.ToString(CultureInfo.InvariantCulture)+" "+height.ToString(CultureInfo.InvariantCulture)); + NativeMethods.tagSIZEL sz = new NativeMethods.tagSIZEL(); + sz.cx = width; + sz.cy = height; + bool resetExtents = !IsUserMode(); + try { + Pixel2hiMetric(sz, sz); + GetOleObject().SetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + } + catch (COMException) { + resetExtents = true; + } + if (resetExtents) { + GetOleObject().GetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + try { + GetOleObject().SetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + } + catch (COMException e) { + Debug.Fail(e.ToString()); + } + } + return GetExtent(); + } + + + private Size GetExtent() { + NativeMethods.tagSIZEL sz = new NativeMethods.tagSIZEL(); + GetOleObject().GetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + HiMetric2Pixel(sz, sz); + return new Size(sz.cx, sz.cy); + } + + /// + /// + /// ActiveX controls scale themselves, so GetScaledBounds simply returns their + /// original unscaled bounds. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + return bounds; + } + + private void SetObjectRects(Rectangle bounds) { + if (GetOcState() < OC_INPLACE) return; + GetInPlaceObject().SetObjectRects(FillInRect(new NativeMethods.COMRECT(), bounds), GetClipRect(new NativeMethods.COMRECT())); + } + + /// + /// + /// Performs the work of setting the bounds of this control. + /// User code should usually not call this function. + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + // We have already been in this Code so please avoid re-entering this CODE PATH or else the + // IOleObject will "give a Catastrophic error" in SetObjectRects( ). + // Please refer : VsWhidbey 341212. + + if (GetAxState(AxHost.handlePosRectChanged)) + return; + + axState[handlePosRectChanged] = true; + + // Provide control with an opportunity to apply self imposed constraints on its size. + Size adjustedSize = ApplySizeConstraints(width, height); + width = adjustedSize.Width; + height = adjustedSize.Height; + + try { + if (axState[fFakingWindow]) { + base.SetBoundsCore(x, y, width, height, specified); + return; + } + Rectangle oldBounds = Bounds; + + if (oldBounds.X == x && oldBounds.Y == y && oldBounds.Width == width && + oldBounds.Height == height) { + return; + } + if (!IsHandleCreated) { + UpdateBounds(x, y, width, height); + return; + } + + if (GetOcState() > OC_RUNNING) { + CheckSubclassing(); + if (width != oldBounds.Width || height != oldBounds.Height) { + Size p = SetExtent(width, height); + width = p.Width; + height = p.Height; + } + } + if (axState[manualUpdate]) { + SetObjectRects(new Rectangle(x, y, width, height)); + CheckSubclassing(); + UpdateBounds(); + } + else { + SetObjectRects(new Rectangle(x, y, width, height)); + base.SetBoundsCore(x, y, width, height, specified); + Invalidate(); + } + } + finally { + axState[handlePosRectChanged] = false; + } + } + + + private bool CheckSubclassing() { + if (!IsHandleCreated || wndprocAddr == IntPtr.Zero) return true; + IntPtr handle = Handle; + IntPtr currentWndproc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); + if (currentWndproc == wndprocAddr) return true; + if (unchecked( (int) (long)SendMessage(REGMSG_MSG, 0, 0)) == REGMSG_RETVAL) { + wndprocAddr = currentWndproc; + return true; + } + // yikes, we were resubclassed... + Debug.WriteLineIf(AxHostSwitch.TraceVerbose, "The horrible control subclassed itself w/o calling the old wndproc..."); + // we need to resubclass outselves now... + Debug.Assert(!OwnWindow(), "why are we here if we own our window?"); + this.WindowReleaseHandle(); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC, new HandleRef(this, currentWndproc)); + this.WindowAssignHandle(handle, axState[assignUniqueID]); + InformOfNewHandle(); + axState[manualUpdate] = true; + return false; + } + + /// + /// + /// Destroys the handle associated with this control. + /// User code should in general not call this function. + /// + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + // security review: this is a breaking change, but should it be fixed? + protected override void DestroyHandle() { + if (axState[fOwnWindow]) { + base.DestroyHandle(); + } + else { + if (IsHandleCreated) { + TransitionDownTo(OC_RUNNING); + } + } + } + +#if false + // FxCop: Currently not used + private void TransitionTo(int state) { + if (state > GetOcState()) { + TransitionUpTo(state); + } + else if (state < GetOcState()) { + TransitionDownTo(state); + } + } +#endif + + private void TransitionDownTo(int state) { + if (axState[inTransition]) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Recursively entering TransitionDownTo..."); + return; + } + + try { + axState[inTransition] = true; + + while (state < GetOcState()) { + switch (GetOcState()) { + case OC_OPEN: + Debug.Fail("how did we ever get into the open state?"); + SetOcState(OC_UIACTIVE); + break; + case OC_UIACTIVE: + int hr = UiDeactivate(); + Debug.Assert(NativeMethods.Succeeded(hr), "Failed in UiDeactivate: " + hr.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose && GetOcState() == OC_INPLACE, "failed transition"); + SetOcState(OC_INPLACE); + break; + case OC_INPLACE: + if (axState[fFakingWindow]) { + DestroyFakeWindow(); + SetOcState(OC_RUNNING); + } + else { + InPlaceDeactivate(); + } + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose && GetOcState() == OC_RUNNING, "failed transition"); + SetOcState(OC_RUNNING); + break; + case OC_RUNNING: + StopEvents(); + DisposeAxControl(); + Debug.Assert(GetOcState() == OC_LOADED," failed transition"); + SetOcState(OC_LOADED); + break; + case OC_LOADED: + ReleaseAxControl(); + Debug.Assert(GetOcState() == OC_PASSIVE," failed transition"); + SetOcState(OC_PASSIVE); + break; + default: + Debug.Fail("bad state"); + SetOcState(GetOcState() - 1); + break; + } + } + } + finally { + axState[inTransition] = false; + } + } + + private void TransitionUpTo(int state) { + if (axState[inTransition]) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Recursively entering TransitionUpTo..."); + return; + } + + try { + axState[inTransition] = true; + + while (state > GetOcState()) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Transitioning up from: " + GetOcState().ToString(CultureInfo.InvariantCulture) + " to: " + state.ToString(CultureInfo.InvariantCulture)); + switch (GetOcState()) { + case OC_PASSIVE: + axState[disposed] = false; + GetOcxCreate(); + Debug.Assert(GetOcState() == OC_LOADED, " failed transition"); + SetOcState(OC_LOADED); + break; + case OC_LOADED: + ActivateAxControl(); + Debug.Assert(GetOcState() == OC_RUNNING, " failed transition"); + SetOcState(OC_RUNNING); + if (IsUserMode()) { + // start the events flowing! + //createSink(); + StartEvents(); + } + break; + case OC_RUNNING: + axState[ownDisposing] = false; + Debug.Assert(!axState[fOwnWindow], "If we are invis at runtime, we should never be going beynd OC_RUNNING"); + if (!axState[fOwnWindow]) { + InPlaceActivate(); + + if (!Visible && ContainingControl != null && ContainingControl.Visible) { + HideAxControl(); + } + else { + // if we do this in both codepaths, then we will force handle creation of the fake window + // even if we don't need it... + // This optimization will break, however, if: + // a) the hWnd goes away on a OLEIVERB_HIDE and + // b) this is a simple frame control + // However, if you satisfy both of these conditions then you must be REALLY + // brain dead and you don't deserve to work anyway... + CreateControl(true); + // if our default size is wrong for the control, let's resize ourselves... + // Note: some controls haven't updated their extents at this time + // (even though they got it from the DoVerb call and + // also from GetWindowContext) so we don't poke in a new value. + // The reason to do this at design time is that that's the only way we + // can find out if the control has a default which we have to obey. + if (!IsUserMode() && !axState[ocxStateSet]) { + Size p = GetExtent(); + Rectangle b = Bounds; + + if ((b.Size.Equals(DefaultSize)) && (!b.Size.Equals(p))) { + b.Width = p.Width; + b.Height = p.Height; + Bounds = b; + } + } + } + } + + //Debug.Assert(GetOcState() == OC_INPLACE, " failed transition"); + if (GetOcState() < OC_INPLACE) { + SetOcState(OC_INPLACE); + } + OnInPlaceActive(); + break; + case OC_INPLACE: + DoVerb(OLEIVERB_SHOW); + Debug.Assert(GetOcState() == OC_UIACTIVE, " failed transition"); + SetOcState(OC_UIACTIVE); + break; + default: + Debug.Fail("bad state"); + SetOcState(GetOcState() + 1); + break; + } + } + } + finally { + axState[inTransition] = false; + } + } + + /// + /// + /// [To be supplied.] + /// + /// + protected virtual void OnInPlaceActive() { + } + + private void InPlaceActivate() { + try { + DoVerb(OLEIVERB_INPLACEACTIVATE); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw new TargetInvocationException(SR.GetString(SR.AXNohWnd,GetType().Name), t); + } + EnsureWindowPresent(); + } + + + private void InPlaceDeactivate() { + axState[ownDisposing] = true; + ContainerControl f = ContainingControl; + if (f != null) { + if (f.ActiveControl == this) { + f.ActiveControl = null; + } + } + + try { + GetInPlaceObject().InPlaceDeactivate(); + } + catch(Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception calling InPlaceDeactivate: "+ e.ToString()); + } + } + + private void UiActivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "calling uiActivate for "+this.ToString()); + Debug.Assert(GetOcState() >= OC_INPLACE, "we have to be in place in order to ui activate..."); + Debug.Assert(CanUIActivate, "we have to be able to uiactivate"); + if (CanUIActivate) { + DoVerb(OLEIVERB_UIACTIVATE); + } + } + + private void DestroyFakeWindow() { + Debug.Assert(axState[fFakingWindow], "have to be faking it in order to destroy it..."); + + // ASURT 70740: The problem seems to be that when we try to destroy the fake window, + // we recurse in and transition the control down to OC_RUNNING. This causes the control's + // new window to get destroyed also, and the control never shows up. + // We now prevent this by changing our state about the fakeWindow _before_ we actually + // destroy the window. + // + axState[fFakingWindow] = false; + base.DestroyHandle(); + } + + private void EnsureWindowPresent() { + // if the ctl didn't call showobject, we need to do it for it... + if (!IsHandleCreated) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Naughty control didn't call showObject..."); + try { + ((UnsafeNativeMethods.IOleClientSite)oleSite).ShowObject(); + } + catch { + // The exception, if any was already dumped in ShowObject + } + } + if (IsHandleCreated) return; + + if (ParentInternal != null) { // ==> we are in a valid state + Debug.Fail("extremely naughty ctl is refusing to give us an hWnd... giving up..."); + throw new NotSupportedException(SR.GetString(SR.AXNohWnd,GetType().Name)); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void SetVisibleCore(bool value) { + if (GetState(STATE_VISIBLE) != value) { + bool oldVisible = Visible; + if ((IsHandleCreated || value) && ParentInternal != null && ParentInternal.Created) { + if (!axState[fOwnWindow]) { + TransitionUpTo(OC_RUNNING); + if (value) { + if (axState[fFakingWindow]) { + // first we need to destroy the fake window... + DestroyFakeWindow(); + } + // We want to avoid using SHOW since that may uiactivate us, and we don't + // want that... + if (!IsHandleCreated) { + // So, if we don't have a handle, we just try to create it and hope that this will make + // us appear... + try { + SetExtent(Width, Height); + InPlaceActivate(); + CreateControl(true); + } + catch { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Could not make ctl visible by using INPLACE. Will try SHOW"); + MakeVisibleWithShow(); + } + } + else { + // if, otoh, we had a handle to begin with, we need to use show since INPLACE is just + // a noop... + MakeVisibleWithShow(); + } + } + else { + Debug.Assert(!axState[fFakingWindow], "if we were visible, we could not have had a fake window..."); + HideAxControl(); + } + } + } + if (!value) { + axState[fNeedOwnWindow] = false; + } + if (!axState[fOwnWindow]) { + SetState(STATE_VISIBLE, value); + if (Visible != oldVisible) + OnVisibleChanged(EventArgs.Empty); + } + } + } + + private void MakeVisibleWithShow() { + ContainerControl f = ContainingControl; + Control ctl = f == null ? null : f.ActiveControl; + try { + DoVerb(OLEIVERB_SHOW); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw new TargetInvocationException(SR.GetString(SR.AXNohWnd,GetType().Name), t); + } + + EnsureWindowPresent(); + CreateControl(true); + if (f != null && f.ActiveControl != ctl) { + f.ActiveControl = ctl; + } + } + + private void HideAxControl() { + Debug.Assert(!axState[fOwnWindow], "can't own our window when hiding"); + Debug.Assert(IsHandleCreated, "gotta have a window to hide"); + Debug.Assert(GetOcState() >= OC_INPLACE, "have to be in place in order to hide."); + + DoVerb(OLEIVERB_HIDE); + if (GetOcState() < OC_INPLACE) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Naughty control inplace deactivated on a hide verb..."); + Debug.Assert(!IsHandleCreated, "if we are inplace deactivated we should not have a window."); + // all we do here is set a flag saying that we need the window to be created if + // create handle is ever called... + axState[fNeedOwnWindow] = true; + + // also, set the state to our "pretend oc_inplace state" + // + SetOcState(OC_INPLACE); + } + } + + /// + /// + /// Determines if charCode is an input character that the control + /// wants. This method is called during window message pre-processing to + /// determine whether the given input character should be pre-processed or + /// sent directly to the control. If isInputChar returns true, the + /// given character is sent directly to the control. If isInputChar + /// returns false, the character is pre-processed and only sent to the + /// control if it is not consumed by the pre-processing phase. The + /// pre-processing of a character includes checking whether the character + /// is a mnemonic of another control. + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool IsInputChar(char charCode) { + return true; + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) + { + return ignoreDialogKeys ? false : base.ProcessDialogKey(keyData); + } + + /// + /// + /// This method is called by the application's message loop to pre-process + /// input messages before they are dispatched. Possible values for the + /// msg.message field are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR. + /// If this method processes the message it must return true, in which case + /// the message loop will not dispatch the message. + /// This method should not be called directly by the user. + /// + /// The keyboard processing of input keys to AxHost controls go in 3 steps inside AxHost.PreProcessMessage() + /// + /// (1) Call the OCX's TranslateAccelarator. This may or may not call back into us using IOleControlSite::TranslateAccelarator() + /// + /// (2) If the control completely processed this without calling us back: + /// -- If this returns S_OK, then it means that the control already processed this message and we return true, + /// forcing us to not do any more processing or dispatch the message. + /// -- If this returns S_FALSE, then it means that the control wants us to dispatch the message without doing any processing on our side. + /// + /// (3) If the control completely processed this by calling us back: + /// -- If this returns S_OK, then it means that the control processed this message and we return true, + /// forcing us to not do any more processing or dispatch the message. + /// -- If this returns S_FALSE, then it means that the control did not process this message, + /// but we did, and so we should route it through our PreProcessMessage(). + /// + public override bool PreProcessMessage(ref Message msg) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "AxHost.PreProcessMessage " + msg.ToString()); + + if (IsUserMode()) { + if (axState[siteProcessedInputKey]) { + // In this case, the control called the us back through the IControlSite + // and giving us a chance to see if we want to process it. We in turn + // call the base implementation which normally would call the control's + // IsInputKey() or IsInputChar(). So, we short-circuit those to return false + // and only return true, if the container-chain wanted to process the keystroke + // (e.g. tab, accelarators etc.) + // + return base.PreProcessMessage(ref msg); + } + + NativeMethods.MSG win32Message = new NativeMethods.MSG(); + win32Message.message = msg.Msg; + win32Message.wParam = msg.WParam; + win32Message.lParam = msg.LParam; + win32Message.hwnd = msg.HWnd; + + axState[siteProcessedInputKey] = false; + try { + UnsafeNativeMethods.IOleInPlaceActiveObject activeObj = GetInPlaceActiveObject(); + if (activeObj != null) + { + int hr = activeObj.TranslateAccelerator(ref win32Message); + + msg.Msg = win32Message.message; + msg.WParam = win32Message.wParam; + msg.LParam = win32Message.lParam; + msg.HWnd = win32Message.hwnd; + + if (hr == NativeMethods.S_OK) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message translated by control to " + msg); + return true; + } + else if (hr == NativeMethods.S_FALSE) { + bool ret = false; + + ignoreDialogKeys = true; + try { + ret = base.PreProcessMessage(ref msg); + } + finally { + ignoreDialogKeys = false; + } + return ret; + } + else if (axState[siteProcessedInputKey]) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message processed by site. Calling base.PreProcessMessage() " + msg); + return base.PreProcessMessage (ref msg); + } + else { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message not processed by site. Returning false. " + msg); + return false; + } + } + } + finally { + axState[siteProcessedInputKey] = false; + } + } + + return false; + } + + /// + /// + /// Process a mnemonic character. + /// This is done by manufacturing a WM_SYSKEYDOWN message and passing it + /// to the ActiveX control. + /// + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "In AxHost.ProcessMnemonic: " + (int)charCode); + if (CanSelect) { + try { + NativeMethods.tagCONTROLINFO ctlInfo = new NativeMethods.tagCONTROLINFO(); + int hr = GetOleControl().GetControlInfo(ctlInfo); + if (NativeMethods.Failed(hr)) { + return false; + } + NativeMethods.MSG msg = new NativeMethods.MSG(); + // Sadly, we don't have a message so we must fake one ourselves... + // A bit of ugliness here (a bit? more like a bucket...) + // The message we are faking is a WM_SYSKEYDOWN w/ the right alt key setting... + msg.hwnd = (ContainingControl == null) ? IntPtr.Zero : ContainingControl.Handle; + msg.message = NativeMethods.WM_SYSKEYDOWN; + msg.wParam = (IntPtr) Char.ToUpper(charCode, CultureInfo.CurrentCulture); + msg.lParam = (IntPtr) 0x20180001; + msg.time = SafeNativeMethods.GetTickCount(); + NativeMethods.POINT p = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(p); + msg.pt_x = p.x; + msg.pt_y = p.y; + if (SafeNativeMethods.IsAccelerator(new HandleRef(ctlInfo, ctlInfo.hAccel), ctlInfo.cAccel, ref msg, null)) { + GetOleControl().OnMnemonic(ref msg); + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Processed mnemonic " + msg); + Focus(); + return true; + } + } + catch (Exception t) { + Debug.Fail("error in processMnemonic"); + Debug.Fail(t.ToString()); + return false; + } + } + return false; + } + + // misc methods: + + /// + /// + /// Sets the delegate which will be called when the user selects the "About..." + /// entry on the context menu. + /// + protected void SetAboutBoxDelegate(AboutBoxDelegate d) { + aboutBoxDelegate += d; + } + + /// + /// + /// Sets the persisted state of the control. + /// This should either be null, obtained from getOcxState, or + /// read from a resource. The value of this property will + /// be used after the control is created but before it is + /// shown. + /// Computes the persisted state of the underlying ActiveX control and + /// returns it in the encapsulated State object. + /// If the control has been modified since it was last saved to a + /// persisted state, it will be asked to save itself. + /// + [ + DefaultValue(null), + RefreshProperties(RefreshProperties.All), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) + ] + public State OcxState { + get { + if (IsDirty() || ocxState == null) { + Debug.Assert(!axState[disposed], "we chould not be asking for the object when we are axState[disposed]..."); + ocxState = CreateNewOcxState(ocxState); + } + return ocxState; + } + + set { + axState[ocxStateSet] = true; + + if (value == null) return; + + if (storageType != STG_UNKNOWN && storageType != value.type) { + Debug.Fail("Trying to reload with a OcxState that is of a different type."); + throw new InvalidOperationException(SR.GetString(SR.AXOcxStateLoaded)); + } + + if (this.ocxState == value) + return; + + this.ocxState = value; + + if (this.ocxState != null) { + this.axState[manualUpdate] = ocxState._GetManualUpdate(); + this.licenseKey = ocxState._GetLicenseKey(); + } + else { + this.axState[manualUpdate] = false; + this.licenseKey = null; + } + + if (this.ocxState != null && GetOcState() >= OC_RUNNING) { + DepersistControl(); + } + } + } + + private State CreateNewOcxState(State oldOcxState) { + NoComponentChangeEvents++; + + try { + if (GetOcState() < OC_RUNNING) { + return null; + } + + try { + PropertyBagStream propBag = null; + + if (iPersistPropBag != null) { + propBag = new PropertyBagStream(); + iPersistPropBag.Save(propBag, true, true); + } + + MemoryStream ms = null; + switch (storageType) { + case STG_STREAM: + case STG_STREAMINIT: + ms = new MemoryStream(); + if (storageType == STG_STREAM) { + iPersistStream.Save(new UnsafeNativeMethods.ComStreamFromDataStream(ms), true); + } + else { + iPersistStreamInit.Save(new UnsafeNativeMethods.ComStreamFromDataStream(ms), true); + } + break; + case STG_STORAGE: + Debug.Assert(oldOcxState != null, "we got to have an old state which holds out scribble storage..."); + if (oldOcxState != null) return oldOcxState.RefreshStorage(iPersistStorage); + return null; + default: + Debug.Fail("unknown storage type."); + return null; + } + if (ms != null) { + return new State(ms, storageType, this, propBag); + } + else if (propBag != null) { + return new State(propBag); + } + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Could not create new OCX State: " + e.ToString()); + } + } + finally { + NoComponentChangeEvents--; + } + + return null; + } + + /// + /// + /// Returns this control's logicaly containing form. + /// At design time this is always the form being designed. + /// At runtime it is either the form set with setContainingForm or, + /// by default, the parent form. + /// Sets the form which is the logical container of this control. + /// By default, the parent form performs that function. It is + /// however possible for another form higher in the parent chain + /// to serve in that role. The logical container of this + /// control determines the set of logical sibling control. + /// In general this property exists only to enable some speficic + /// behaviours of ActiveX controls and should in general not be set + /// by the user. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ContainerControl ContainingControl { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + if (containingControl == null) { + containingControl = FindContainerControlInternal(); + } + + return containingControl; + } + + set { + containingControl = value; + } + } + + /// + /// + /// Determines if the Text property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeText() { + bool ret = false; + try { + ret = (Text.Length != 0); + } + catch (COMException) { + } + return ret; + } + + /// + /// Determines whether to persist the ContainingControl property. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeContainingControl() { + return ContainingControl != ParentInternal; + } + + private ContainerControl FindContainerControlInternal() { + if (Site != null) { + IDesignerHost host = (IDesignerHost)Site.GetService(typeof(IDesignerHost)); + if (host != null) { + ContainerControl rootControl = host.RootComponent as ContainerControl; + if (rootControl != null) + { + return rootControl; + } + } + } + + ContainerControl cc = null; + Control control = this; + while (control != null) { + ContainerControl tempCC = control as ContainerControl; + if (tempCC != null) + { + cc = tempCC; + break; + } + control = control.ParentInternal; + } + return cc; + } + + private bool IsDirty() { + if (GetOcState() < OC_RUNNING) return false; + Debug.Assert(storageType != STG_UNKNOWN, "if we are loaded, out storage type must be set!"); + + if (axState[valueChanged]) { + axState[valueChanged] = false; + return true; + } + +#if DEBUG + if (AxAlwaysSaveSwitch.Enabled) return true; +#endif + int hr = NativeMethods.E_FAIL; + switch (storageType) { + case STG_STREAM: + hr = iPersistStream.IsDirty(); + break; + case STG_STREAMINIT: + hr = iPersistStreamInit.IsDirty(); + break; + case STG_STORAGE: + hr = iPersistStorage.IsDirty(); + break; + default: + Debug.Fail("unknown storage type"); + return true; + } + if (hr == NativeMethods.S_FALSE) { + // NOTE: This was a note from the old AxHost codebase. The problem + // with doing this is that the some controls that do not run in + // unlicensed mode (e.g. ProtoView ScheduleX pvtaskpad.ocx) will + // always return S_FALSE to disallow design-time support. + + // Sadly, some controls lie and never say that they are dirty... + // SO, we don't believe them unless they told us that they were + // dirty at least once... + return false; + } + else if (NativeMethods.Failed(hr)) { + return true; + } + return true; + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + internal bool IsUserMode() { + ISite site = Site; + return site == null || !site.DesignMode; + } + + private Object GetAmbientProperty(int dispid) { + + Control richParent = ParentInternal; + + switch (dispid) { + case NativeMethods.ActiveX.DISPID_AMBIENT_USERMODE: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for usermode"); + return IsUserMode(); + case NativeMethods.ActiveX.DISPID_AMBIENT_AUTOCLIP: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for autoclip"); + return true; + case NativeMethods.ActiveX.DISPID_AMBIENT_MESSAGEREFLECT: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for message reflect"); + return true; + case NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for uidead"); + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for displayasdefault"); + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_FONT: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for font"); + if (richParent != null) { + return GetIFontFromFont(richParent.Font); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_SHOWGRABHANDLES: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for showGrabHandles"); + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_SHOWHATCHING: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for showHatching"); + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR: + if (richParent != null) { + return GetOleColorFromColor(richParent.BackColor); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR: + if (richParent != null) { + return GetOleColorFromColor(richParent.ForeColor); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYNAME: + string rval = GetParentContainer().GetNameForControl(this); + if (rval == null) rval = ""; + return rval; + case NativeMethods.ActiveX.DISPID_AMBIENT_LOCALEID: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for localeid"); + return Thread.CurrentThread.CurrentCulture.LCID; + case NativeMethods.ActiveX.DISPID_AMBIENT_RIGHTTOLEFT: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "asked for right to left"); + Control ctl = this; + while (ctl != null) { + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.No) + return false; + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.Yes) + return true; + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.Inherit) + ctl = ctl.Parent; + } + return null; + default: + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "unsupported ambient "+dispid.ToString(CultureInfo.InvariantCulture)); + return null; + } + } + + + /// + /// + /// [To be supplied.] + /// + public void DoVerb(int verb) { + Control parent = ParentInternal; + GetOleObject().DoVerb(verb, IntPtr.Zero, oleSite, -1, parent != null ? parent.Handle : IntPtr.Zero, FillInRect(new NativeMethods.COMRECT(), Bounds)); + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private bool AwaitingDefreezing() { + return freezeCount > 0; + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private void Freeze(bool v) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "freezing "+v.ToString()); + if (v) { + try { + GetOleControl().FreezeEvents(-1); + } + catch (COMException t) { + Debug.Fail(t.ToString()); + } + freezeCount ++; + } + else { + try { + GetOleControl().FreezeEvents(0); + } + catch (COMException t) { + Debug.Fail(t.ToString()); + } + freezeCount --; + } + Debug.Assert(freezeCount >=0, "invalid freeze count!"); + } + + private int UiDeactivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "calling uiDeactivate for "+this.ToString()); + bool ownDispose = this.axState[ownDisposing]; + this.axState[ownDisposing] = true; + int hr = 0; + try { + hr = GetInPlaceObject().UIDeactivate(); + } + finally { + this.axState[ownDisposing] = ownDispose; + } + return hr; + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private int GetOcState() { + return ocState; + } + + private void SetOcState(int nv) { + ocState = nv; + } + + private string GetLicenseKey() { + return GetLicenseKey(this.clsid); + } + + private string GetLicenseKey(Guid clsid) { + if (licenseKey != null || !axState[needLicenseKey]) { + return licenseKey; + } + + try { + UnsafeNativeMethods.IClassFactory2 icf2 = UnsafeNativeMethods.CoGetClassObject(ref clsid, INPROC_SERVER, 0, ref icf2_Guid); + NativeMethods.tagLICINFO licInfo = new NativeMethods.tagLICINFO(); + icf2.GetLicInfo(licInfo); + if (licInfo.fRuntimeAvailable != 0) { + string[] rval = new string[1]; + icf2.RequestLicKey(0, rval); + licenseKey = rval[0]; + return licenseKey; + } + } + catch (COMException e) { + if (e.ErrorCode == E_NOINTERFACE.ErrorCode) return null; + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to get the license key: " + e.ToString()); + axState[needLicenseKey] = false; + } + catch (Exception t) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to get the license key: " + t.ToString()); + axState[needLicenseKey] = false; + } + return null; + } + + private void CreateWithoutLicense(Guid clsid) { + object ret = null; + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Creating object without license: " + clsid.ToString()); + ret = UnsafeNativeMethods.CoCreateInstance(ref clsid, null, INPROC_SERVER, ref NativeMethods.ActiveX.IID_IUnknown); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\t" + (ret != null).ToString()); + instance = ret; + } + + private void CreateWithLicense(string license, Guid clsid) { + if (license != null) { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Creating object with license: " + clsid.ToString()); + UnsafeNativeMethods.IClassFactory2 icf2 = UnsafeNativeMethods.CoGetClassObject(ref clsid, INPROC_SERVER, 0, ref icf2_Guid); + + if (icf2 != null) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\tClassFactory" + (icf2 != null).ToString()); + icf2.CreateInstanceLic(null, null, ref NativeMethods.ActiveX.IID_IUnknown, license, out instance); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\t" + (instance != null).ToString()); + } + } + catch (Exception t) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to create with license: " + t.ToString()); + } + } + + if (instance == null) { + CreateWithoutLicense(clsid); + } + } + + private void CreateInstance() { + Debug.Assert(instance == null, "instance must be null"); + //Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "before created "+Windows.GetCurrentThreadId()); + //Debug.WriteStackTraceIf("AxHTrace"); + //checkThreadingModel(); + try { + instance = CreateInstanceCore(this.clsid); + Debug.Assert(instance != null, "w/o an exception being thrown we must have an object..."); + } + catch (ExternalException e) { + if (e.ErrorCode == unchecked((int)0x80040112)) { // CLASS_E_NOTLICENSED + throw new LicenseException(GetType(), this, SR.GetString(SR.AXNoLicenseToUse)); + } + throw; + } + + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "created"); + SetOcState(OC_LOADED); + } + + + /// + /// Called to create the ActiveX control. Override this member to perform your own creation logic + /// or call base to do the default creation logic. + /// + protected virtual object CreateInstanceCore(Guid clsid) { + if (IsUserMode()) { + CreateWithLicense(licenseKey, clsid); + } + else { + CreateWithoutLicense(clsid); + } + return instance; + } + + + private CategoryAttribute GetCategoryForDispid(int dispid) { + NativeMethods.ICategorizeProperties icp = GetCategorizeProperties(); + if (icp == null) return null; + CategoryAttribute rval = null; + int propcat = 0; + try { + icp.MapPropertyToCategory(dispid, ref propcat); + if (propcat != 0) { + int cat = -propcat; + if (cat > 0 && cat < categoryNames.Length && categoryNames[cat] != null) { + return categoryNames[cat]; + } + cat = - cat; + Int32 key = cat; + if (objectDefinedCategoryNames != null) { + rval = (CategoryAttribute) objectDefinedCategoryNames[key]; + if (rval != null) return rval; + } + + string name = null; + int hr = icp.GetCategoryName(cat, CultureInfo.CurrentCulture.LCID, out name); + if (hr == NativeMethods.S_OK && name != null) { + rval = new CategoryAttribute(name); + if (objectDefinedCategoryNames == null) { + objectDefinedCategoryNames = new Hashtable(); + } + objectDefinedCategoryNames.Add(key, rval); + return rval; + } + } + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + return null; + } + + private void SetSelectionStyle(int selectionStyle) { + if (!this.IsUserMode()) { + // selectionStyle can be 0 (not selected), 1 (selected) or 2 (active) + Debug.Assert(selectionStyle >= 0 && selectionStyle <= 2, "Invalid selection style"); + + ISelectionService iss = GetSelectionService(); + this.selectionStyle = selectionStyle; + if (iss != null && iss.GetComponentSelected(this)) { + // The AX Host designer will offer an extender property called "SelectionStyle" + // + PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["SelectionStyle"]; + if (prop != null && prop.PropertyType == typeof(int)) { + prop.SetValue(this, selectionStyle); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void InvokeEditMode() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "invoking EditMode for "+this.ToString()); + Debug.Assert((flags & AxFlags.PreventEditMode) == 0, "edit mode should have been disabled"); + if (editMode != EDITM_NONE) return; + AddSelectionHandler(); + editMode = EDITM_HOST; + SetSelectionStyle(2); + IntPtr hwndFocus = UnsafeNativeMethods.GetFocus(); + try { + UiActivate(); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + // It so happens that some controls don't get focus in this case, so + // we have got to force it onto them... + // int hwndFocusNow = NativeMethods.GetFocus(); + // Windows.SetFocus(getHandle()); + // while (hwndFocusNow != getHandle()) { + // if (hwndFocusNow == 0) { + // break; + // } + // hwndFocusNow = Windows.GetParent(hwndFocusNow); + // } + } + + // + // ICustomTypeDescriptor implementation. + // + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + AttributeCollection ICustomTypeDescriptor.GetAttributes() { + if (!axState[editorRefresh] && HasPropertyPages()) { + axState[editorRefresh] = true; + TypeDescriptor.Refresh(this.GetType()); + } + return TypeDescriptor.GetAttributes(this, true); + } + + /// + /// + /// + /// Retrieves the class name for this object. If null is returned, + /// the type name is used. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + string ICustomTypeDescriptor.GetClassName() { + return null; + } + + /// + /// + /// + /// Retrieves the name for this object. If null is returned, + /// the default is used. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + string ICustomTypeDescriptor.GetComponentName() { + return null; + } + + /// + /// + /// + /// Retrieves the type converter for this object. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + TypeConverter ICustomTypeDescriptor.GetConverter() { + return null; + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { + return TypeDescriptor.GetDefaultEvent(this, true); + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { + return TypeDescriptor.GetDefaultProperty(this, true); + } + + /// + /// + /// + /// Retrieves the an editor for this object. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + Object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { + if (editorBaseType != typeof(ComponentEditor)) + return null; + + if (editor != null) + return editor; + + if (editor == null && HasPropertyPages()) + editor = new AxComponentEditor(); + + return editor; + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { + return TypeDescriptor.GetEvents(this, true); + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { + return TypeDescriptor.GetEvents(this, attributes, true); + } + + private void OnIdle(object sender, EventArgs e) { + if (axState[refreshProperties]) { + TypeDescriptor.Refresh(this.GetType()); + } + } + + private bool RefreshAllProperties { + get { + return axState[refreshProperties]; + } + set { + axState[refreshProperties] = value; + if (value && !axState[listeningToIdle]) { + Application.Idle += new EventHandler(this.OnIdle); + axState[listeningToIdle] = true; + } + else if (!value && axState[listeningToIdle]) { + Application.Idle -= new EventHandler(this.OnIdle); + axState[listeningToIdle] = false; + } + } + } + + private PropertyDescriptorCollection FillProperties(Attribute[] attributes) { + if (RefreshAllProperties) { + RefreshAllProperties = false; + propsStash = null; + attribsStash = null; + } + else if (propsStash != null) { + if (attributes == null && attribsStash == null) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Returning stashed values for : " + ""); + return propsStash; + } + else if (attributes != null && attribsStash != null && attributes.Length == attribsStash.Length) { + bool attribsEqual = true; + int i = 0; + foreach(Attribute attrib in attributes) { + if (!attrib.Equals(attribsStash[i++])) { + attribsEqual = false; + break; + } + } + + if (attribsEqual) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Returning stashed values for : " + attributes.Length); + return propsStash; + } + } + } + + ArrayList retProps = new ArrayList(); + + if (properties == null) + properties = new Hashtable(); + + if (propertyInfos == null) { + propertyInfos = new Hashtable(); + + PropertyInfo[] propInfos = this.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); + + foreach(PropertyInfo propInfo in propInfos) + propertyInfos.Add(propInfo.Name, propInfo); + } + + PropertyDescriptorCollection baseProps = TypeDescriptor.GetProperties(this, null, true); + if (baseProps != null) { + for (int i = 0; i < baseProps.Count; ++i) { + Debug.Assert(baseProps[i] != null, "Null base prop at location: " + i.ToString(CultureInfo.InvariantCulture)); + + if (baseProps[i].DesignTimeOnly) { + retProps.Add(baseProps[i]); + continue; + } + + string propName = baseProps[i].Name; + PropertyDescriptor prop = null; + PropertyInfo propInfo = (PropertyInfo)propertyInfos[propName]; + + // We do not support "write-only" properties that some activex controls support. + if (propInfo != null && !propInfo.CanRead) + continue; + + if (!properties.ContainsKey(propName)) { + if (propInfo != null) { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Added AxPropertyDescriptor for: " + propName); + prop = new AxPropertyDescriptor(baseProps[i], this); + ((AxPropertyDescriptor)prop).UpdateAttributes(); + } + else { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Added PropertyDescriptor for: " + propName); + prop = baseProps[i]; + } + properties.Add(propName, prop); + retProps.Add(prop); + } + else { + PropertyDescriptor propDesc = (PropertyDescriptor)properties[propName]; + Debug.Assert(propDesc != null, "Cannot find cached entry for: " + propName); + AxPropertyDescriptor axPropDesc = propDesc as AxPropertyDescriptor; + if ((propInfo == null && axPropDesc != null) || (propInfo != null && axPropDesc == null)) { + Debug.Fail("Duplicate property with same name: " + propName); + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Duplicate property with same name: " + propName); + } + else { + if (axPropDesc != null) { + axPropDesc.UpdateAttributes(); + } + retProps.Add(propDesc); + } + } + } + + // Filter only the Browsable attribute, since that is the only + // one we mess with. + // + if (attributes != null) { + Attribute browse = null; + foreach(Attribute attr in attributes) { + if (attr is BrowsableAttribute) { + browse = attr; + } + } + + if (browse != null) { + ArrayList removeList = null; + + foreach(PropertyDescriptor prop in retProps) { + if (prop is AxPropertyDescriptor) { + Attribute attr = prop.Attributes[typeof(BrowsableAttribute)]; + if (attr != null && !attr.Equals(browse)) { + if (removeList == null) { + removeList = new ArrayList(); + } + removeList.Add(prop); + } + } + } + + if (removeList != null) { + foreach(object prop in removeList) + retProps.Remove(prop); + } + } + } + } + + PropertyDescriptor[] temp = new PropertyDescriptor[retProps.Count]; + retProps.CopyTo(temp, 0); + + // Update our stashed values. + // + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Updating stashed values for : " + ((attributes != null) ? attributes.Length.ToString(CultureInfo.InvariantCulture) : "")); + propsStash = new PropertyDescriptorCollection(temp); + attribsStash = attributes; + + return propsStash; + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { + return FillProperties(null); + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { + return FillProperties(attributes); + } + + /// + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + Object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { + return this; + } + + private AxPropertyDescriptor GetPropertyDescriptorFromDispid(int dispid) { + Debug.Assert(dispid != NativeMethods.ActiveX.DISPID_UNKNOWN, "Wrong dispid sent to GetPropertyDescriptorFromDispid"); + + PropertyDescriptorCollection props = FillProperties(null); + foreach (PropertyDescriptor prop in props) { + AxPropertyDescriptor axprop = prop as AxPropertyDescriptor; + if (axprop != null && axprop.Dispid == dispid) { + return axprop; + } + } + + return null; + } + +#if false + // FxCop: Currently not used + private void CheckThreadingModel() { +#if DEBUG + if (AxIgnoreTMSwitch.Enabled) return; +#endif + if ((flags & AxFlags.IgnoreThreadModel) != 0) return; + + bool singleThreaded = true; + + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try { + try { + using (RegistryKey rk = Registry.ClassesRoot.OpenSubKey("CLSID\\" + clsid.ToString() + "\\InprocServer32", /*writable*/false)) { + object value = rk.GetValue("ThreadingModel"); + + if (value != null && value is string) { + if (value.Equals("Both") + || value.Equals("Apartment") + || value.Equals("Free")) { + + singleThreaded = false; + } + } + } + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw new InvalidOperationException(SR.GetString(SR.AXNoThreadInfo)); + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + if (singleThreaded) { + throw new InvalidOperationException(SR.GetString(SR.AXSingleThreaded)); + } + } +#endif + + private void ActivateAxControl() { + if (QuickActivate()) { + DepersistControl(); + } + else { + SlowActivate(); + } + + SetOcState(OC_RUNNING); + } + + + private void DepersistFromIPropertyBag(UnsafeNativeMethods.IPropertyBag propBag) { + iPersistPropBag.Load(propBag, null); + } + + private void DepersistFromIStream(UnsafeNativeMethods.IStream istream) { + storageType = STG_STREAM; + iPersistStream.Load(istream); + } + + private void DepersistFromIStreamInit(UnsafeNativeMethods.IStream istream) { + storageType = STG_STREAMINIT; + iPersistStreamInit.Load(istream); + } + + private void DepersistFromIStorage(UnsafeNativeMethods.IStorage storage) { + storageType = STG_STORAGE; + + // ASURT 65913 Looks like MapPoint control does not create a valid IStorage + // until some property has changed. Since we end up creating a bogus (empty) + // storage, we end up not being able to re-create a valid one and this would + // fail. + // + if (storage != null) { + int hr = iPersistStorage.Load(storage); + if (hr != NativeMethods.S_OK) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Error trying load depersist from IStorage: " + hr); + } + } + } + + private void DepersistControl() { + Freeze(true); + + if (ocxState == null) { + // must init new: + // + if (instance is UnsafeNativeMethods.IPersistStreamInit) { + iPersistStreamInit = (UnsafeNativeMethods.IPersistStreamInit) instance; + try { + storageType = STG_STREAMINIT; + iPersistStreamInit.InitNew(); + } + catch (Exception e1) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistStreamInit.InitNew(). Is this good?" + e1.ToString()); + } + return; + } + if (instance is UnsafeNativeMethods.IPersistStream) { + storageType = STG_STREAM; + iPersistStream = (UnsafeNativeMethods.IPersistStream) instance; + return; + } + if (instance is UnsafeNativeMethods.IPersistStorage) { + storageType = STG_STORAGE; + ocxState = new State(this); + iPersistStorage = (UnsafeNativeMethods.IPersistStorage) instance; + try { + iPersistStorage.InitNew(ocxState.GetStorage()); + } + catch (Exception e2) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistStorage.InitNew(). Is this good?" + e2.ToString()); + } + return; + } + if (instance is UnsafeNativeMethods.IPersistPropertyBag) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, this + " supports IPersistPropertyBag."); + iPersistPropBag = (UnsafeNativeMethods.IPersistPropertyBag) instance; + try { + iPersistPropBag.InitNew(); + } + catch (Exception e1) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistPropertyBag.InitNew(). Is this good?" + e1.ToString()); + } + } + + Debug.Fail("no implemented persitance interfaces on object"); + throw new InvalidOperationException(SR.GetString(SR.UnableToInitComponent)); + } + + // Otherwise, we have state to deperist from: + switch (ocxState.Type) { + case STG_STREAM: + try { + iPersistStream = (UnsafeNativeMethods.IPersistStream) instance; + DepersistFromIStream(ocxState.GetStream()); + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistStream.DepersistFromIStream(). Is this good?" + e.ToString()); + } + break; + case STG_STREAMINIT: + if (instance is UnsafeNativeMethods.IPersistStreamInit) { + try { + iPersistStreamInit = (UnsafeNativeMethods.IPersistStreamInit) instance; + DepersistFromIStreamInit(ocxState.GetStream()); + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistStreamInit.DepersistFromIStreamInit(). Is this good?" + e.ToString()); + } + GetControlEnabled(); + } + else { + ocxState.Type = STG_STREAM; + DepersistControl(); + return; + } + break; + case STG_STORAGE: + try { + iPersistStorage = (UnsafeNativeMethods.IPersistStorage) instance; + DepersistFromIStorage(ocxState.GetStorage()); + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistStorage.DepersistFromIStorage(). Is this good?" + e.ToString()); + } + break; + default: + Debug.Fail("unknown storage type."); + throw new InvalidOperationException(SR.GetString(SR.UnableToInitComponent)); + } + + if (ocxState.GetPropBag() != null) { + try { + iPersistPropBag = (UnsafeNativeMethods.IPersistPropertyBag)instance; + DepersistFromIPropertyBag(ocxState.GetPropBag()); + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Exception thrown trying to IPersistPropertyBag.DepersistFromIPropertyBag(). Is this good?" + e.ToString()); + } + } + } + + /// + /// + /// Returns the IUnknown pointer to the enclosed ActiveX control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public object GetOcx() { + return instance; + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private Object GetOcxCreate() { + if (instance == null) { + CreateInstance(); + RealizeStyles(); + AttachInterfaces(); + oleSite.OnOcxCreate(); + } + return instance; + } + + private void StartEvents() { + if (!axState[sinkAttached]) { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Creating sink for events..."); + CreateSink(); + oleSite.StartEvents(); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + axState[sinkAttached] = true; + } + } + + private void StopEvents() { + if (axState[sinkAttached]) { + try { + DetachSink(); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + axState[sinkAttached] = false; + } + oleSite.StopEvents(); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void CreateSink() { + // nop... windows forms wrapper will override... + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void DetachSink() { + // nop... windows forms wrapper will override... + } + + + private bool CanShowPropertyPages() { + if (GetOcState() < OC_RUNNING) return false; + return(GetOcx() is NativeMethods.ISpecifyPropertyPages); + } + + + /// + /// + /// [To be supplied.] + /// + public bool HasPropertyPages() { + if (!CanShowPropertyPages()) return false; + NativeMethods.ISpecifyPropertyPages ispp = (NativeMethods.ISpecifyPropertyPages) GetOcx(); + try { + NativeMethods.tagCAUUID uuids = new NativeMethods.tagCAUUID(); + try { + ispp.GetPages(uuids); + if (uuids.cElems > 0) return true; + } + finally { + if (uuids.pElems != IntPtr.Zero) { + Marshal.FreeCoTaskMem(uuids.pElems); + } + } + } + catch { + } + return false; + } + + unsafe private void ShowPropertyPageForDispid(int dispid, Guid guid) { + try { + IntPtr pUnk = Marshal.GetIUnknownForObject(GetOcx()); + NativeMethods.OCPFIPARAMS opcparams = new NativeMethods.OCPFIPARAMS(); + opcparams.hwndOwner = (ContainingControl == null) ? IntPtr.Zero : ContainingControl.Handle; + opcparams.lpszCaption = Name; + opcparams.ppUnk = (IntPtr) (long) &pUnk; + opcparams.uuid = (IntPtr)(long) &guid; + opcparams.dispidInitial = dispid; + UnsafeNativeMethods.OleCreatePropertyFrameIndirect(opcparams); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw t; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void MakeDirty() { + ISite isite = Site; + if (isite == null) + return; + + IComponentChangeService ccs = (IComponentChangeService)isite.GetService(typeof(IComponentChangeService)); + if (ccs == null) + return; + + ccs.OnComponentChanging(this, null); + + ccs.OnComponentChanged(this, null, null, null); + } + + /// + /// + /// [To be supplied.] + /// + public void ShowPropertyPages() { + if (ParentInternal == null) return; + if (!ParentInternal.IsHandleCreated) return; + ShowPropertyPages(ParentInternal); + } + + /// + /// + /// [To be supplied.] + /// + public void ShowPropertyPages(Control control) { + try { + if (!CanShowPropertyPages()) return; + NativeMethods.ISpecifyPropertyPages ispp = (NativeMethods.ISpecifyPropertyPages) GetOcx(); + NativeMethods.tagCAUUID uuids = new NativeMethods.tagCAUUID(); + try { + ispp.GetPages(uuids); + if (uuids.cElems <= 0) return; + } + catch { + return; + } + + // State oldOcxState = OcxState; + + IDesignerHost host = null; + if (Site != null) + host = (IDesignerHost)Site.GetService(typeof(IDesignerHost)); + + DesignerTransaction trans = null; + try { + if (host != null) + trans = host.CreateTransaction(SR.GetString(SR.AXEditProperties)); + + string name = null; + object o = GetOcx(); + IntPtr handle = (ContainingControl == null) ? IntPtr.Zero : ContainingControl.Handle; + SafeNativeMethods.OleCreatePropertyFrame(new HandleRef(this, handle), 0, 0, name, 1, ref o, uuids.cElems, new HandleRef(null, uuids.pElems), Application.CurrentCulture.LCID, 0, IntPtr.Zero); + } + finally { + if (oleSite != null) + ((UnsafeNativeMethods.IPropertyNotifySink)oleSite).OnChanged(NativeMethods.MEMBERID_NIL); + + if (trans != null) + trans.Commit(); + + if (uuids.pElems != IntPtr.Zero) + Marshal.FreeCoTaskMem(uuids.pElems); + } + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw t; + } + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal override IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) + { + if (isMaskEdit) { + return base.InitializeDCForWmCtlColor(dc, msg); + } + else { + return IntPtr.Zero; // bypass Control's anti-reflect logic + } + } + + /// + /// + /// AxHost wndProc. All messages are sent to wndProc after getting filtered + /// through the preProcessMessage function. + /// Certain messages are forwarder directly to the ActiveX control, + /// others are first processed by the wndProc of Control + /// + + protected override void WndProc(ref Message m) { + +#pragma warning disable 162, 429 +// Ignore the warnings generated by the following code (unreachable code, and unreachable expression) + if (false && (axState[manualUpdate] && IsUserMode())) { + DefWndProc(ref m); + return; + } +#pragma warning restore 162, 429 + + switch (m.Msg) { + // Things we explicitly ignore and pass to the ocx's windproc + case NativeMethods.WM_ERASEBKGND: + + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFYFORMAT: + + case NativeMethods.WM_SETCURSOR: + case NativeMethods.WM_SYSCOLORCHANGE: + + // Some of the MSComCtl controls respond to this message + // to do some custom painting. So, we should just pass this message + // through. + // + case NativeMethods.WM_DRAWITEM: + + case NativeMethods.WM_LBUTTONDBLCLK: + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_MBUTTONDBLCLK: + case NativeMethods.WM_MBUTTONUP: + case NativeMethods.WM_RBUTTONDBLCLK: + case NativeMethods.WM_RBUTTONUP: + DefWndProc(ref m); + break; + + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + if (IsUserMode()) { + Focus(); + } + DefWndProc(ref m); + break; + + case NativeMethods.WM_KILLFOCUS: + { + hwndFocus = m.WParam; + try { + base.WndProc(ref m); + } + finally { + hwndFocus = IntPtr.Zero; + } + break; + } + + case NativeMethods.WM_COMMAND: + if (!ReflectMessageInternal(m.LParam, ref m)) + DefWndProc(ref m); + break; + + case NativeMethods.WM_CONTEXTMENU: + DefWndProc(ref m); + break; + + case NativeMethods.WM_DESTROY: +#if DEBUG + if (!OwnWindow()) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "WM_DESTROY naughty control is destroying the window from under us..." + GetType().ToString()); + } +#endif + // + // If we are currently in a state of InPlaceActive or above, + // we should first reparent the ActiveX control to our parking + // window before we transition to a state below InPlaceActive. + // Otherwise we face all sorts of problems when we try to + // transition back to a state >= InPlaceActive. + // + if (GetOcState() >= OC_INPLACE) { + UnsafeNativeMethods.IOleInPlaceObject ipo = GetInPlaceObject(); + IntPtr hwnd; + if (NativeMethods.Succeeded(ipo.GetWindow(out hwnd))) { + Application.ParkHandle(new HandleRef(ipo, hwnd)); + } + } + + bool visible = GetState(STATE_VISIBLE); + + TransitionDownTo(OC_RUNNING); + DetachAndForward(ref m); + + if (visible != GetState(STATE_VISIBLE)) { + SetState(STATE_VISIBLE, visible); + } + + break; + case NativeMethods.WM_HELP: + // We want to both fire the event, and let the ocx have the message... + base.WndProc(ref m); + DefWndProc(ref m); + break; + + case NativeMethods.WM_KEYUP: + if (axState[processingKeyUp]) + break; + + axState[processingKeyUp] = true; + try { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) + DefWndProc(ref m); + } + finally { + axState[processingKeyUp] = false; + } + + break; + + case NativeMethods.WM_NCDESTROY: +#if DEBUG + if (!OwnWindow()) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "WM_NCDESTROY naughty control is destroying the window from under us..." + GetType().ToString()); + } +#endif + // need to detach it now... + DetachAndForward(ref m); + break; + + default: + if (m.Msg == REGMSG_MSG) { + m.Result = (IntPtr)REGMSG_RETVAL; + return; + } + // Other things we may care about and we will pass them to the Control's wndProc + base.WndProc(ref m); + break; + } + } + + private void DetachAndForward(ref Message m) { + IntPtr handle = GetHandleNoCreate(); + DetachWindow(); + if (handle != IntPtr.Zero) { + IntPtr wndProc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); + m.Result = UnsafeNativeMethods.CallWindowProc(wndProc, handle, m.Msg, m.WParam, m.LParam); + } + } + + private void DetachWindow() { + if (IsHandleCreated) { + OnHandleDestroyed(EventArgs.Empty); + for (Control c = this; c != null; c = c.ParentInternal) { + /* NOT NEEDED + if (c.GetAxState(STATE_HANDLEHOOK)) { + ((IHandleHook)c.Site.GetService(IHandleHook.class)).OnDestroyHandle(GetHandle()); + break; + } + */ + } + this.WindowReleaseHandle(); + } + } + + private void InformOfNewHandle() { + Debug.Assert(IsHandleCreated, "we got to have a handle to be here..."); + for (Control c = this; c != null; c = c.ParentInternal) { + /* NOT NEEDED + if (c.GetAxState(STATE_HANDLEHOOK)) { + ((IHandleHook)c.Site.GetService(IHandleHook.class)).OnCreateHandle(GetHandle()); + break; + } + */ + } + wndprocAddr = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_WNDPROC); + /* NOT NEEDED + SetAxState(STATE_CREATENOTIFIED, true); + */ + } + + private void AttachWindow(IntPtr hwnd) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "attaching window for "+this.ToString()+" "+hwnd.ToString()); + if (!axState[fFakingWindow]) { + this.WindowAssignHandle(hwnd, axState[assignUniqueID]); + } + UpdateZOrder(); + + // Get the latest bounds set by the user. + Size setExtent = Size; + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "SetBounds " + setExtent.ToString()); + + // Get the default bounds set by the ActiveX control. + UpdateBounds(); + Size ocxExtent = GetExtent(); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "OcxBounds " + ocxExtent.ToString()); + + Point location = Location; + + // Choose the setBounds unless it is smaller than the default bounds. + if (setExtent.Width < ocxExtent.Width || setExtent.Height < ocxExtent.Height) + Bounds = new Rectangle(location.X, location.Y, ocxExtent.Width, ocxExtent.Height); + else { + Size newSize = SetExtent(setExtent.Width, setExtent.Height); + if (!newSize.Equals(setExtent)) { + Bounds = new Rectangle(location.X, location.Y, newSize.Width, newSize.Height); + } + } + + OnHandleCreated(EventArgs.Empty); + InformOfNewHandle(); + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. + /// Call base.OnHandleCreated first. + /// + protected override void OnHandleCreated(EventArgs e) { + // ASURT 43741 This is needed to prevent some controls (for e.g. Office Web Components) from + // failing to InPlaceActivate() when they call RegisterDragDrop() but do not call + // OleInitialize(). The EE calls CoInitializeEx() on the thread, but I believe + // that is not good enough for DragDrop. + // + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + + SetAcceptDrops(AllowDrop); + RaiseCreateHandleEvent(e); + } + + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public sealed class ClsidAttribute : Attribute { + private String val; + + /// + public ClsidAttribute(String clsid) { + val = clsid; + } + + /// + public String Value { + get { + return val; + } + } + } + + /// + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class TypeLibraryTimeStampAttribute : Attribute { + private DateTime val; + + /// + public TypeLibraryTimeStampAttribute(string timestamp) { + val = DateTime.Parse(timestamp, CultureInfo.InvariantCulture); + } + + /// + public DateTime Value { + get { + return val; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class ConnectionPointCookie { + private UnsafeNativeMethods.IConnectionPoint connectionPoint; + private int cookie; + internal int threadId; +#if DEBUG + private string callStack; +#endif + /// + /// + /// Creates a connection point to of the given interface type. + /// which will call on a managed code sink that implements that interface. + /// + public ConnectionPointCookie(object source, object sink, Type eventInterface) + : this(source, sink, eventInterface, true) { + } + + internal ConnectionPointCookie(object source, object sink, Type eventInterface, bool throwException) { + if (source is UnsafeNativeMethods.IConnectionPointContainer) { + UnsafeNativeMethods.IConnectionPointContainer cpc = (UnsafeNativeMethods.IConnectionPointContainer)source; + + try { + Guid tmp = eventInterface.GUID; + if (cpc.FindConnectionPoint(ref tmp, out connectionPoint) != NativeMethods.S_OK) { + connectionPoint = null; + } + } + catch { + connectionPoint = null; + } + + if (connectionPoint == null) { + if (throwException) { + throw new ArgumentException(SR.GetString(SR.AXNoEventInterface, eventInterface.Name)); + } + } + else if (sink == null || !eventInterface.IsInstanceOfType(sink)) { + if (throwException) { + throw new InvalidCastException(SR.GetString(SR.AXNoSinkImplementation, eventInterface.Name)); + } + } + else { + int hr = connectionPoint.Advise(sink, ref cookie); + if (hr == NativeMethods.S_OK) { + threadId = Thread.CurrentThread.ManagedThreadId; + } + else { + cookie = 0; + Marshal.ReleaseComObject(connectionPoint); + connectionPoint = null; + if (throwException) { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.AXNoSinkAdvise, eventInterface.Name), hr)); + } + } + } + } + else { + if (throwException) { + throw new InvalidCastException(SR.GetString(SR.AXNoConnectionPointContainer)); + } + } + + + if (connectionPoint == null || cookie == 0) { + if (connectionPoint != null) { + Marshal.ReleaseComObject(connectionPoint); + } + + if (throwException) { + throw new ArgumentException(SR.GetString(SR.AXNoConnectionPoint, eventInterface.Name)); + } + } +#if DEBUG + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + callStack = Environment.StackTrace; + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } +#endif + } + + /// + /// + /// Disconnect the current connection point. If the object is not connected, + /// this method will do nothing. + /// + public void Disconnect() { + if (connectionPoint != null && cookie != 0) { + try { + connectionPoint.Unadvise(cookie); + } + catch (Exception ex) { + if (ClientUtils.IsCriticalException(ex)) { + throw; + } + } + finally { + cookie = 0; + } + + try { + Marshal.ReleaseComObject(connectionPoint); + } + catch (Exception ex) { + if (ClientUtils.IsCriticalException(ex)) { + throw; + } + } + finally { + connectionPoint = null; + } + } + } + + /// + /// + ~ConnectionPointCookie(){ + if (connectionPoint != null && cookie != 0) { + if (!AppDomain.CurrentDomain.IsFinalizingForUnload()) { + SynchronizationContext context = SynchronizationContext.Current; + if (context == null) { + Debug.Fail("Attempted to disconnect ConnectionPointCookie from the finalizer with no SynchronizationContext."); + } + else { + context.Post(new SendOrPostCallback(AttemptDisconnect), null); + } + } + } + } + + void AttemptDisconnect(object trash) { + if (threadId == Thread.CurrentThread.ManagedThreadId) { + Disconnect(); + } + else { + Debug.Fail("Attempted to disconnect ConnectionPointCookie from the wrong thread (finalizer)."); + } + } + + internal bool Connected { + get { + return connectionPoint != null && cookie != 0; + } + } + } + + /// + public enum ActiveXInvokeKind { + /// + MethodInvoke, + /// + PropertyGet, + /// + PropertySet + } + + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class InvalidActiveXStateException : Exception { + private string name; + private ActiveXInvokeKind kind; + + /// + public InvalidActiveXStateException(string name, ActiveXInvokeKind kind) { + this.name = name; + this.kind = kind; + } + + /// + public InvalidActiveXStateException() { + } + + /// + public override string ToString() { + switch (kind) { + case ActiveXInvokeKind.MethodInvoke: + return SR.GetString(SR.AXInvalidMethodInvoke, name); + case ActiveXInvokeKind.PropertyGet: + return SR.GetString(SR.AXInvalidPropertyGet, name); + case ActiveXInvokeKind.PropertySet: + return SR.GetString(SR.AXInvalidPropertySet, name); + default: + return base.ToString(); + } + } + } + + // This private class encapsulates all of the ole interfaces so that users + // will not be able to access and call them directly... + + /// + /// + private class OleInterfaces + : UnsafeNativeMethods.IOleControlSite, UnsafeNativeMethods.IOleClientSite, UnsafeNativeMethods.IOleInPlaceSite, UnsafeNativeMethods.ISimpleFrameSite, UnsafeNativeMethods.IVBGetControl, UnsafeNativeMethods.IGetVBAObject, UnsafeNativeMethods.IPropertyNotifySink, IReflect, IDisposable { + + private AxHost host; + private ConnectionPointCookie connectionPoint; + + internal OleInterfaces(AxHost host) { + if (host == null) + throw new ArgumentNullException("host"); + this.host = host; + } + + private void Dispose(bool disposing) { + if (disposing) { + if (!AppDomain.CurrentDomain.IsFinalizingForUnload()) { + SynchronizationContext context = SynchronizationContext.Current; + if (context == null) { + Debug.Fail("Attempted to disconnect ConnectionPointCookie from the finalizer with no SynchronizationContext."); + } + else { + context.Post(new SendOrPostCallback(AttemptStopEvents), null); + } + } + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal AxHost GetAxHost() { + return host; + } + + internal void OnOcxCreate() { + StartEvents(); + } + + internal void StartEvents() { + if (connectionPoint != null) + return; + + Object nativeObject = host.GetOcx(); + + try { + connectionPoint = new ConnectionPointCookie(nativeObject, this, typeof(UnsafeNativeMethods.IPropertyNotifySink)); + } + catch { + } + } + + void AttemptStopEvents(object trash) { + if( connectionPoint == null ){ + return; + } + + if (connectionPoint.threadId == Thread.CurrentThread.ManagedThreadId) { + StopEvents(); + } + else { + Debug.Fail("Attempted to disconnect ConnectionPointCookie from the wrong thread (finalizer)."); + } + } + + internal void StopEvents() { + if (connectionPoint != null) { + connectionPoint.Disconnect(); + connectionPoint = null; + } + } + + // IGetVBAObject methods: + + int UnsafeNativeMethods.IGetVBAObject.GetObject(ref Guid riid, UnsafeNativeMethods.IVBFormat[] rval, int dwReserved) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetObject"); + + if (rval == null || riid.Equals(Guid.Empty)) + return NativeMethods.E_INVALIDARG; + + if (riid.Equals(ivbformat_Guid)) { + rval[0] = new VBFormat(); + return NativeMethods.S_OK; + } + else { + rval[0] = null; + return NativeMethods.E_NOINTERFACE; + } + } + + // IVBGetControl methods: + + int UnsafeNativeMethods.IVBGetControl.EnumControls(int dwOleContF, int dwWhich, out UnsafeNativeMethods.IEnumUnknown ppenum) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in EnumControls"); + ppenum = null; + ppenum = host.GetParentContainer().EnumControls(host, dwOleContF, dwWhich); + return NativeMethods.S_OK; + } + + // ISimpleFrameSite methods: + + int UnsafeNativeMethods.ISimpleFrameSite.PreMessageFilter(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref IntPtr plResult, ref int pdwCookie) { + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.ISimpleFrameSite.PostMessageFilter(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref IntPtr plResult, int dwCookie) { + return NativeMethods.S_FALSE; + } + + // IReflect methods: + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr,Binder binder, Type[] types,ParameterModifier[] modifiers) { + return null; + } + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr) { + return null; + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return new MethodInfo[0]; + } + + FieldInfo IReflect.GetField(String name, BindingFlags bindingAttr) { + return null; + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return new FieldInfo[0]; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr) { + return null; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return null; + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return new PropertyInfo[0]; + } + + MemberInfo[] IReflect.GetMember(String name, BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + Object IReflect.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, + Object target, Object[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters) { + if (name.StartsWith("[DISPID=")) { + int endIndex = name.IndexOf("]"); + int dispid = Int32.Parse(name.Substring(8, endIndex - 8), CultureInfo.InvariantCulture); + object ambient = host.GetAmbientProperty(dispid); + if (ambient != null) return ambient; + } + + throw E_FAIL; + } + + Type IReflect.UnderlyingSystemType { + get { + return null; + } + } + + // IOleControlSite methods: + + + int UnsafeNativeMethods.IOleControlSite.OnControlInfoChanged() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnControlInfoChanged"); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleControlSite.LockInPlaceActive(int fLock) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in LockInPlaceActive"); + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleControlSite.GetExtendedControl(out object ppDisp) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetExtendedControl " + host.ToString()); + ppDisp = host.GetParentContainer().GetProxyForControl(host); + if (ppDisp == null) + return NativeMethods.E_NOTIMPL; + + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleControlSite.TransformCoords(NativeMethods._POINTL pPtlHimetric, NativeMethods.tagPOINTF pPtfContainer, int dwFlags) { + int hr = SetupLogPixels(false); + if (NativeMethods.Failed(hr)) + return hr; + + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_HIMETRICTOCONTAINER) != 0) { + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_SIZE) != 0) { + pPtfContainer.x = (float) host.HM2Pix(pPtlHimetric.x, logPixelsX); + pPtfContainer.y = (float) host.HM2Pix(pPtlHimetric.y, logPixelsY); + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_POSITION) != 0) { + pPtfContainer.x = (float) host.HM2Pix(pPtlHimetric.x, logPixelsX); + pPtfContainer.y = (float) host.HM2Pix(pPtlHimetric.y, logPixelsY); + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose,"\t dwFlags not supported: " + dwFlags); + return NativeMethods.E_INVALIDARG; + } + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_CONTAINERTOHIMETRIC) != 0) { + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_SIZE) != 0) { + pPtlHimetric.x = host.Pix2HM((int)pPtfContainer.x, logPixelsX); + pPtlHimetric.y = host.Pix2HM((int)pPtfContainer.y, logPixelsY); + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_POSITION) != 0) { + pPtlHimetric.x = host.Pix2HM((int)pPtfContainer.x, logPixelsX); + pPtlHimetric.y = host.Pix2HM((int)pPtfContainer.y, logPixelsY); + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\t dwFlags not supported: " + dwFlags); + return NativeMethods.E_INVALIDARG; + } + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\t dwFlags not supported: " + dwFlags); + return NativeMethods.E_INVALIDARG; + } + + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleControlSite.TranslateAccelerator(ref NativeMethods.MSG pMsg, int grfModifiers) { + Debug.Assert(!host.GetAxState(AxHost.siteProcessedInputKey), "Re-entering UnsafeNativeMethods.IOleControlSite.TranslateAccelerator!!!"); + host.SetAxState(AxHost.siteProcessedInputKey, true); + + Message msg = new Message(); + msg.Msg = pMsg.message; + msg.WParam = pMsg.wParam; + msg.LParam = pMsg.lParam; + msg.HWnd = pMsg.hwnd; + + try { + bool f = ((Control)host).PreProcessMessage(ref msg); + return f ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + finally { + host.SetAxState(AxHost.siteProcessedInputKey, false); + } + } + + int UnsafeNativeMethods.IOleControlSite.OnFocus(int fGotFocus) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnFocus " + ((fGotFocus == 0) ? "lost" : "gained")); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleControlSite.ShowPropertyFrame() { + if (host.CanShowPropertyPages()) { + host.ShowPropertyPages(); + return NativeMethods.S_OK; + } + return NativeMethods.E_NOTIMPL; + } + + // IOleClientSite methods: + + int UnsafeNativeMethods.IOleClientSite.SaveObject() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in SaveObject"); + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleClientSite.GetMoniker(int dwAssign, int dwWhichMoniker, out Object moniker) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMoniker"); + moniker = null; + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleClientSite.GetContainer(out UnsafeNativeMethods.IOleContainer container) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getContainer"); + container = host.GetParentContainer(); + return NativeMethods.S_OK; + } + + + int UnsafeNativeMethods.IOleClientSite.ShowObject() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in ShowObject"); + if (host.GetAxState(AxHost.fOwnWindow)) { + Debug.Fail("we can't be in showobject if we own our window..."); + return NativeMethods.S_OK; + } + if (host.GetAxState(AxHost.fFakingWindow)) { + // we really should not be here... + // this means that the ctl inplace deactivated and didn't call on inplace activate before calling showobject + // so we need to destroy our fake window first... + host.DestroyFakeWindow(); + + // ASURT 46393 + // The fact that we have a fake window means that the OCX inplace deactivated when we hid it. It means + // that we have to bring it back from RUNNING to INPLACE so that it can re-create its handle properly. + // + host.TransitionDownTo(OC_LOADED); + host.TransitionUpTo(OC_INPLACE); + } + if (host.GetOcState() < OC_INPLACE) + return NativeMethods.S_OK; + + IntPtr hwnd; + if (NativeMethods.Succeeded(host.GetInPlaceObject().GetWindow(out hwnd))) { + if (host.GetHandleNoCreate() != hwnd) { + host.DetachWindow(); + if (hwnd != IntPtr.Zero) { + host.AttachWindow(hwnd); + } + } + } + else if (host.GetInPlaceObject() is UnsafeNativeMethods.IOleInPlaceObjectWindowless) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Windowless control."); + throw new InvalidOperationException(SR.GetString(SR.AXWindowlessControl)); + } + + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleClientSite.OnShowWindow(int fShow) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnShowWindow"); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleClientSite.RequestNewObjectLayout() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in RequestNewObjectLayout"); + return NativeMethods.E_NOTIMPL; + } + + // IOleInPlaceSite methods: + + /// + /// + /// [To be supplied.] + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + IntPtr UnsafeNativeMethods.IOleInPlaceSite.GetWindow() { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetWindow"); + Control parent = host.ParentInternal; + return parent != null ? parent.Handle : IntPtr.Zero; + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw t; + } + } + + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceSite.ContextSensitiveHelp(int fEnterMode) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in ContextSensitiveHelp"); + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceSite.CanInPlaceActivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in CanInPlaceActivate"); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.OnInPlaceActivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnInPlaceActivate"); + host.SetAxState(AxHost.ownDisposing, false); + host.SetAxState(AxHost.rejectSelection, false); + host.SetOcState(OC_INPLACE); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.OnUIActivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnUIActivate for " + host.ToString()); + host.SetOcState(OC_UIACTIVE); + host.GetParentContainer().OnUIActivate(host); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.GetWindowContext(out UnsafeNativeMethods.IOleInPlaceFrame ppFrame, out UnsafeNativeMethods.IOleInPlaceUIWindow ppDoc, + NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect, NativeMethods.tagOIFI lpFrameInfo) { + ppDoc = null; + ppFrame = host.GetParentContainer(); + FillInRect(lprcPosRect, host.Bounds); + host.GetClipRect(lprcClipRect); + if (lpFrameInfo != null) { + lpFrameInfo.cb = Marshal.SizeOf(typeof(NativeMethods.tagOIFI)); + lpFrameInfo.fMDIApp = false; + lpFrameInfo.hAccel = IntPtr.Zero; + lpFrameInfo.cAccelEntries = 0; + lpFrameInfo.hwndFrame = host.ParentInternal.Handle; + } + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.Scroll(NativeMethods.tagSIZE scrollExtant) { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in Scroll"); + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw t; + } + return(NativeMethods.S_FALSE); + } + + int UnsafeNativeMethods.IOleInPlaceSite.OnUIDeactivate(int fUndoable) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnUIDeactivate for " + host.ToString()); + host.GetParentContainer().OnUIDeactivate(host); + if (host.GetOcState() > OC_INPLACE) { + host.SetOcState(OC_INPLACE); + } + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.OnInPlaceDeactivate() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnInPlaceDeactivate"); + if (host.GetOcState() == OC_UIACTIVE) { + ((UnsafeNativeMethods.IOleInPlaceSite)this).OnUIDeactivate(0); + } + + host.GetParentContainer().OnInPlaceDeactivate(host); + host.DetachWindow(); + host.SetOcState(OC_RUNNING); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.DiscardUndoState() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in DiscardUndoState"); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceSite.DeactivateAndUndo() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in DeactivateAndUndo for "+host.ToString()); + return host.GetInPlaceObject().UIDeactivate(); + } + + int UnsafeNativeMethods.IOleInPlaceSite.OnPosRectChange(NativeMethods.COMRECT lprcPosRect) { + // ASURT 68752 + // The MediaPlayer control has a AllowChangeDisplaySize property that users + // can set to control size changes at runtime, but the control itself ignores that and sets the new size. + // We prevent this by not allowing controls to call OnPosRectChange(), unless we instantiated the resize. + // visual basic6 does the same. + // + bool useRect = true; + if (AxHost.windowsMediaPlayer_Clsid.Equals(host.clsid)) + useRect = host.GetAxState(AxHost.handlePosRectChanged); + + if (useRect) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnPosRectChange" + lprcPosRect.ToString()); + host.GetInPlaceObject().SetObjectRects(lprcPosRect, host.GetClipRect(new NativeMethods.COMRECT())); + host.MakeDirty(); + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Control directly called OnPosRectChange... ignoring the new size"); + } + return NativeMethods.S_OK; + } + + // IPropertyNotifySink methods + + void UnsafeNativeMethods.IPropertyNotifySink.OnChanged(int dispid) { + // Some controls fire OnChanged() notifications when getting values of some properties. ASURT 20190. + // To prevent this kind of recursion, we check to see if we are already inside a OnChanged() call. + // + if (host.NoComponentChangeEvents != 0) + return; + + host.NoComponentChangeEvents++; + try { + AxPropertyDescriptor prop = null; + + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnChanged"); + + if (dispid != NativeMethods.ActiveX.DISPID_UNKNOWN) { + prop = host.GetPropertyDescriptorFromDispid(dispid); + if (prop != null) { + prop.OnValueChanged(this.host); + if (!prop.SettingValue) { + prop.UpdateTypeConverterAndTypeEditor(true); + } + } + } + else { + // update them all for DISPID_UNKNOWN. + // + PropertyDescriptorCollection props = ((ICustomTypeDescriptor)host).GetProperties(); + foreach(PropertyDescriptor p in props) { + prop = p as AxPropertyDescriptor; + if (prop != null && !prop.SettingValue) { + prop.UpdateTypeConverterAndTypeEditor(true); + } + } + } + + ISite site = host.Site; + if (site != null) { + IComponentChangeService changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + + if (changeService != null) { + try { + changeService.OnComponentChanging(host, prop); + } + catch (CheckoutException coEx) { + if (coEx == CheckoutException.Canceled) { + return; + } + throw coEx; + } + + // Now notify the change service that the change was successful. + // + changeService.OnComponentChanged(host, prop, null, ((prop != null) ? prop.GetValue(host) : null)); + } + } + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw t; + } + finally { + host.NoComponentChangeEvents--; + } + } + + int UnsafeNativeMethods.IPropertyNotifySink.OnRequestEdit(int dispid) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in OnRequestEdit for "+host.ToString()); + return NativeMethods.S_OK; + } + } + + private const int HMperInch = 2540; + private int Pix2HM(int pix, int logP) { + return(HMperInch * pix + ( logP >> 1)) / logP; + } + + private int HM2Pix(int hm, int logP) { + return(logP * hm + HMperInch / 2) / HMperInch; + } + + private bool QuickActivate() { + if (!(instance is UnsafeNativeMethods.IQuickActivate)) return false; + UnsafeNativeMethods.IQuickActivate iqa = (UnsafeNativeMethods.IQuickActivate) instance; + + UnsafeNativeMethods.tagQACONTAINER qaContainer = new UnsafeNativeMethods.tagQACONTAINER(); + UnsafeNativeMethods.tagQACONTROL qaControl = new UnsafeNativeMethods.tagQACONTROL(); + + qaContainer.pClientSite = oleSite; + qaContainer.pPropertyNotifySink = oleSite; + // qaContainer.pControlSite = oleSite; + // qaContainer.pAdviseSink = null; + // qaContainer.pUnkEventSink = null; + // qaContainer.pUndoMgr = null; + // qaContainer.pBindHost = null; + // qaContainer.pServiveProvider = null; + // qaContainer.hpal = 0; + qaContainer.pFont = GetIFontFromFont(GetParentContainer().parent.Font); + qaContainer.dwAppearance = 0; + qaContainer.lcid = Application.CurrentCulture.LCID; + + Control p = ParentInternal; + + if (p != null) { + qaContainer.colorFore = GetOleColorFromColor(p.ForeColor); + qaContainer.colorBack = GetOleColorFromColor(p.BackColor); + } + else { + qaContainer.colorFore = GetOleColorFromColor(SystemColors.WindowText); + qaContainer.colorBack = GetOleColorFromColor(SystemColors.Window); + } + qaContainer.dwAmbientFlags = NativeMethods.ActiveX.QACONTAINER_AUTOCLIP | NativeMethods.ActiveX.QACONTAINER_MESSAGEREFLECT | + NativeMethods.ActiveX.QACONTAINER_SUPPORTSMNEMONICS; + if (IsUserMode()) { + qaContainer.dwAmbientFlags |= NativeMethods.ActiveX.QACONTAINER_USERMODE; + } + else { + // Can't set ui dead becuase MFC controls return NOWHERE on NCHITTEST which + // messes up the designer... + // But, without this the FarPoint SpreadSheet and the Office SpreadSheet + // controls take keyboard input at design time. + //qaContainer.dwAmbientFlags |= NativeMethods.ActiveX.QACONTAINER_UIDEAD; + // qaContainer.dwAmbientFlags |= ActiveX.QACONTAINER_SHOWHATCHING; + } + + try { + iqa.QuickActivate(qaContainer, qaControl); + } + catch (Exception t) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to QuickActivate: " + t.ToString()); + DisposeAxControl(); + return false; + } + miscStatusBits = qaControl.dwMiscStatus; + ParseMiscBits(miscStatusBits); + return true; + } + + internal override void DisposeAxControls() { + axState[rejectSelection] = true; + base.DisposeAxControls(); + TransitionDownTo(OC_PASSIVE); + } + + + private bool GetControlEnabled() { + try { + return IsHandleCreated; + } + catch (Exception t) { + Debug.Fail(t.ToString()); + return true; + } + } + + internal override bool CanSelectCore() { + if (!GetControlEnabled() || axState[rejectSelection]) return false; + return base.CanSelectCore(); + } + + /// + /// + /// Frees all resources assocaited with this control. This method may not be + /// called at runtime. Any resources used by the control should be setup to + /// be released when the control is garbage collected. Inheriting classes should always + /// call base.dispose. + /// + protected override void Dispose(bool disposing) { + + if (disposing) { + TransitionDownTo(OC_PASSIVE); + if (newParent != null) + { + newParent.Dispose(); + } + + if (oleSite != null) + { + oleSite.Dispose(); + } + } + base.Dispose(disposing); + } + + private bool GetSiteOwnsDeactivation() { + return axState[ownDisposing]; + } + + private void DisposeAxControl() { + if (GetParentContainer() != null) { + GetParentContainer().RemoveControl(this); + } + TransitionDownTo(OC_RUNNING); + if (GetOcState() == OC_RUNNING) { + GetOleObject().SetClientSite(null); + SetOcState(OC_LOADED); + } + } + + private void ReleaseAxControl() { + // This line is like a bit of magic... + // sometimes, we crash with it on, + // sometimes, with it off... + // Lately, I have decided to leave it on... + // (oh, yes, and the crashes seemed to disappear...) + //cpr: ComLib.Release(instance); + + this.NoComponentChangeEvents++; + + ContainerControl f = ContainingControl; + if (f != null) { + f.VisibleChanged -= this.onContainerVisibleChanged; + } + + try { + if (instance != null) { + Marshal.FinalReleaseComObject(instance); + instance = null; + iOleInPlaceObject = null; + iOleObject = null; + iOleControl = null; + iOleInPlaceActiveObject = null; + iOleInPlaceActiveObjectExternal = null; + iPerPropertyBrowsing = null; + iCategorizeProperties = null; + iPersistStream = null; + iPersistStreamInit = null; + iPersistStorage = null; + } + + axState[checkedIppb] = false; + axState[checkedCP] = false; + axState[disposed] = true; + + freezeCount = 0; + axState[sinkAttached] = false; + wndprocAddr = IntPtr.Zero; + + SetOcState(OC_PASSIVE); + } + finally { + this.NoComponentChangeEvents--; + } + } + + private void ParseMiscBits(int bits) { + axState[fOwnWindow] = ((bits & NativeMethods.ActiveX.OLEMISC_INVISIBLEATRUNTIME) != 0) && IsUserMode(); + axState[fSimpleFrame] = ((bits & NativeMethods.ActiveX.OLEMISC_SIMPLEFRAME) != 0); + } + + private void SlowActivate() { + + bool setClientSite = false; + + if ((miscStatusBits & NativeMethods.ActiveX.OLEMISC_SETCLIENTSITEFIRST) != 0) { + GetOleObject().SetClientSite(oleSite); + setClientSite = true; + } + + DepersistControl(); + + if (!setClientSite) { + GetOleObject().SetClientSite(oleSite); + } + } + + + private static NativeMethods.COMRECT FillInRect(NativeMethods.COMRECT dest, Rectangle source) { + dest.left = source.X; + dest.top = source.Y; + dest.right = source.Width + source.X; + dest.bottom = source.Height + source.Y; + return dest; + } + + private AxContainer GetParentContainer() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + if (container == null) { + container = AxContainer.FindContainerForControl(this); + } + if (container == null) { + ContainerControl f = ContainingControl; + if (f == null) { + // ContainingCointrol can be null if the AxHost is still not parented to a containerControl + // In everett we used to return a parking window. + // now we just set the containingControl to a dummyValue. + if (newParent == null) + { + newParent = new ContainerControl(); + axContainer = newParent.CreateAxContainer(); + axContainer.AddControl(this); + } + return axContainer; + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "calling upon "+f.ToString()+" to create a container"); + container = f.CreateAxContainer(); + container.AddControl(this); + containingControl = f; + } + } + return container; + } + + private UnsafeNativeMethods.IOleControl GetOleControl() { + if (iOleControl == null) { + Debug.Assert(instance != null, "must have the ocx"); + iOleControl = (UnsafeNativeMethods.IOleControl) instance; + } + return iOleControl; + } + + private UnsafeNativeMethods.IOleInPlaceActiveObject GetInPlaceActiveObject() { + // if our AxContainer was set an external active object then use it. + if(iOleInPlaceActiveObjectExternal != null) { + return iOleInPlaceActiveObjectExternal; + } + + // otherwise use our instance. + if (iOleInPlaceActiveObject == null) { + Debug.Assert(instance != null, "must have the ocx"); + try { + iOleInPlaceActiveObject = (UnsafeNativeMethods.IOleInPlaceActiveObject)instance; + } + catch (InvalidCastException e) { + Debug.Fail("Invalid cast in GetInPlaceActiveObject: " + e.ToString()); + } + } + return iOleInPlaceActiveObject; + } + + private UnsafeNativeMethods.IOleObject GetOleObject() { + if (iOleObject == null) { + Debug.Assert(instance != null, "must have the ocx"); + iOleObject = (UnsafeNativeMethods.IOleObject) instance; + } + return iOleObject; + } + + private UnsafeNativeMethods.IOleInPlaceObject GetInPlaceObject() { + if (iOleInPlaceObject == null) { + Debug.Assert(instance != null, "must have the ocx"); + iOleInPlaceObject = (UnsafeNativeMethods.IOleInPlaceObject) instance; + +#if DEBUG + if (iOleInPlaceObject is UnsafeNativeMethods.IOleInPlaceObjectWindowless) + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, GetType().FullName + " Can also be a Windowless control."); +#endif //DEBUG + } + return iOleInPlaceObject; + } + + private NativeMethods.ICategorizeProperties GetCategorizeProperties() { + if (iCategorizeProperties == null && !axState[checkedCP] && instance != null) { + axState[checkedCP] = true; + if (instance is NativeMethods.ICategorizeProperties) { + iCategorizeProperties = (NativeMethods.ICategorizeProperties) instance; + } + } + return iCategorizeProperties; + } + + private NativeMethods.IPerPropertyBrowsing GetPerPropertyBrowsing() { + if (iPerPropertyBrowsing == null && !axState[checkedIppb] && instance != null) { + axState[checkedIppb] = true; + if (instance is NativeMethods.IPerPropertyBrowsing) { + iPerPropertyBrowsing = (NativeMethods.IPerPropertyBrowsing) instance; + } + } + return iPerPropertyBrowsing; + } + + + // Mapping functions: + +#if false + // FxCop: Currently not used + private static IntPtr CopyPalette(IntPtr hPal) { + if (hPal == IntPtr.Zero) return IntPtr.Zero; + int[] nEntries = new int[1]; + UnsafeNativeMethods.GetObject(new HandleRef(null, hPal), 4, nEntries); + IntPtr memory = Marshal.AllocHGlobal(nEntries[0] * 4 + 8); + IntPtr ret = IntPtr.Zero; + + try { + Marshal.WriteInt32(memory, 0, 0x300); + Marshal.WriteInt32(memory, 4, nEntries[0]); + SafeNativeMethods.GetPaletteEntries(new HandleRef(null, hPal), 0, nEntries[0], (IntPtr)((long)memory + 8)); + ret = SafeNativeMethods.CreatePalette(new HandleRef(null, memory)); + } + finally { + Marshal.FreeHGlobal(memory); + } + return ret; + } +#endif + + private static Object GetPICTDESCFromPicture(Image image) { + Bitmap bmp = image as Bitmap; + if (bmp != null) { + return new NativeMethods.PICTDESCbmp(bmp); + } + + Metafile mf = image as Metafile; + if (mf != null) + { + return new NativeMethods.PICTDESCemf(mf); + } + throw new ArgumentException(SR.GetString(SR.AXUnknownImage), "image"); + } + + /// + /// + /// Maps from a System.Drawing.Image to an OLE IPicture + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected static object GetIPictureFromPicture(Image image) { + if (image == null) return null; + Object pictdesc = GetPICTDESCFromPicture(image); + return UnsafeNativeMethods.OleCreateIPictureIndirect(pictdesc, ref ipicture_Guid, true); + } + + /// + /// + /// Maps from a System.Drawing.Cursor to an OLE IPicture + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected static object GetIPictureFromCursor(Cursor cursor) { + if (cursor == null) return null; + NativeMethods.PICTDESCicon pictdesc = new NativeMethods.PICTDESCicon(Icon.FromHandle(cursor.Handle)); + return UnsafeNativeMethods.OleCreateIPictureIndirect(pictdesc, ref ipicture_Guid, true); + } + + /// + /// + /// Maps from a System.Drawing.Image to an OLE IPictureDisp + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected static object GetIPictureDispFromPicture(Image image) { + if (image == null) return null; + Object pictdesc = GetPICTDESCFromPicture(image); + return UnsafeNativeMethods.OleCreateIPictureDispIndirect(pictdesc, ref ipictureDisp_Guid, true); + } + + /// + /// + /// Maps from an OLE IPicture to a System.Drawing.Image + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected static Image GetPictureFromIPicture(object picture) { + if (picture == null) return null; + IntPtr hPal = IntPtr.Zero; + UnsafeNativeMethods.IPicture pict = (UnsafeNativeMethods.IPicture)picture; + int type = pict.GetPictureType(); + if (type == NativeMethods.Ole.PICTYPE_BITMAP) { + try { + hPal = pict.GetHPal(); + } + catch (COMException) { + } + } + return GetPictureFromParams(pict, pict.GetHandle(), type, hPal, pict.GetWidth(), pict.GetHeight()); + } + + /// + /// + /// Maps from an OLE IPictureDisp to a System.Drawing.Image + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected static Image GetPictureFromIPictureDisp(object picture) { + if (picture == null) return null; + IntPtr hPal = IntPtr.Zero; + UnsafeNativeMethods.IPictureDisp pict = (UnsafeNativeMethods.IPictureDisp)picture; + int type = pict.PictureType; + if (type == NativeMethods.Ole.PICTYPE_BITMAP) { + try { + hPal = pict.HPal; + } + catch (COMException) { + } + } + return GetPictureFromParams(pict, pict.Handle, type, hPal, pict.Width, pict.Height); + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static Image GetPictureFromParams(object pict, IntPtr handle, int type, IntPtr paletteHandle, int width, int height) { + switch (type) { + case NativeMethods.Ole.PICTYPE_ICON: + return(Image)(Icon.FromHandle(handle)).Clone(); + case NativeMethods.Ole.PICTYPE_METAFILE: + WmfPlaceableFileHeader header = new WmfPlaceableFileHeader(); + header.BboxRight = (short)width; + header.BboxBottom = (short)height; + return(Image)(new Metafile(handle, header, false)).Clone(); + case NativeMethods.Ole.PICTYPE_ENHMETAFILE: + return(Image)(new Metafile(handle, false)).Clone(); + case NativeMethods.Ole.PICTYPE_BITMAP: + return Image.FromHbitmap(handle, paletteHandle); + case NativeMethods.Ole.PICTYPE_NONE: + // MSDN sez this should not be a valid value, but comctl32 returns it... + return null; + case NativeMethods.Ole.PICTYPE_UNINITIALIZED: + return null; + default: + Debug.Fail("Invalid image type "+ type.ToString(CultureInfo.InvariantCulture)); + throw new ArgumentException(SR.GetString(SR.AXUnknownImage), "type"); + } + } + + private static NativeMethods.FONTDESC GetFONTDESCFromFont(Font font) { + NativeMethods.FONTDESC fdesc = null; + + if (fontTable == null) { + fontTable = new Hashtable(); + } + else { + fdesc = (NativeMethods.FONTDESC)fontTable[font]; + } + + if (fdesc == null) { + fdesc = new NativeMethods.FONTDESC(); + fdesc.lpstrName = font.Name; + fdesc.cySize = (long)(font.SizeInPoints * 10000); + NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); + font.ToLogFont(logfont); + fdesc.sWeight = (short) logfont.lfWeight; + fdesc.sCharset = logfont.lfCharSet; + fdesc.fItalic = font.Italic; + fdesc.fUnderline = font.Underline; + fdesc.fStrikethrough = font.Strikeout; + + fontTable[font] = fdesc; + } + + return fdesc; + } + + /// + /// + /// Maps from an OLE COLOR to a System.Drawing.Color + /// + [CLSCompliantAttribute(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static Color GetColorFromOleColor(uint color) { + return ColorTranslator.FromOle((int)color); + } + + /// + /// + /// Maps from an System.Drawing.Color to an OLE COLOR + /// + [CLSCompliantAttribute(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static uint GetOleColorFromColor(Color color) { + return (uint)ColorTranslator.ToOle(color); + } + + /// + /// + /// Maps from a System.Drawing.Font object to an OLE IFont + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static object GetIFontFromFont(Font font) { + if (font == null) return null; + + if (font.Unit != GraphicsUnit.Point) + throw new ArgumentException(SR.GetString(SR.AXFontUnitNotPoint), "font"); + + try { + return (UnsafeNativeMethods.IFont)UnsafeNativeMethods.OleCreateIFontIndirect(GetFONTDESCFromFont(font), ref ifont_Guid); + } + catch { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to create IFrom from font: " + font.ToString()); + return null; + } + } + + + /// + /// + /// Maps from an OLE IFont to a System.Drawing.Font object + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static Font GetFontFromIFont(object font) { + if (font == null) return null; + + UnsafeNativeMethods.IFont oleFont = (UnsafeNativeMethods.IFont)font; + try { + Font f = Font.FromHfont(oleFont.GetHFont()); + + if (f.Unit != GraphicsUnit.Point) + f = new Font(f.Name, f.SizeInPoints, f.Style, GraphicsUnit.Point, f.GdiCharSet, f.GdiVerticalFont); + + return f; + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Could not create font." + e.Message); + return DefaultFont; + } + } + + + /// + /// + /// Maps from a System.Drawing.Font object to an OLE IFontDisp + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static object GetIFontDispFromFont(Font font) { + if (font == null) return null; + + if (font.Unit != GraphicsUnit.Point) + throw new ArgumentException(SR.GetString(SR.AXFontUnitNotPoint), "font"); + + SafeNativeMethods.IFontDisp rval = SafeNativeMethods.OleCreateIFontDispIndirect(GetFONTDESCFromFont(font), ref ifontDisp_Guid); + return rval; + } + + /// + /// + /// Maps from an IFontDisp to a System.Drawing.Font object + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static Font GetFontFromIFontDisp(object font) { + if (font == null) + { + return null; + } + + UnsafeNativeMethods.IFont ifont = font as UnsafeNativeMethods.IFont; + if (ifont != null) + { + return GetFontFromIFont(ifont); + } + + SafeNativeMethods.IFontDisp oleFont = (SafeNativeMethods.IFontDisp)font; + FontStyle style = FontStyle.Regular; + + Font f = null; + try { + if (oleFont.Bold) + style |= FontStyle.Bold; + + if (oleFont.Italic) + style |= FontStyle.Italic; + + if (oleFont.Underline) + style |= FontStyle.Underline; + + if (oleFont.Strikethrough) + style |= FontStyle.Strikeout; + + if ((int) oleFont.Weight >= 700) // bold + style |= FontStyle.Bold; + + f = new Font(oleFont.Name, (float)oleFont.Size/(float)10000, style, GraphicsUnit.Point, (byte)oleFont.Charset); + return f; + } + catch(Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Could not create font from: " + oleFont.Name + ". " + e.Message); + return DefaultFont; + } + } + + + /// + /// + /// Maps from a DateTime object to an OLE DATE (expressed as a double) + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static double GetOADateFromTime(DateTime time) { + return time.ToOADate(); + } + + /// + /// + /// Maps from an OLE DATE (expressed as a double) to a DateTime object + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static DateTime GetTimeFromOADate(double date) { + return DateTime.FromOADate(date); + } + + /// + /// + /// + private int Convert2int(Object o, bool xDirection) { + o = ((Array)o).GetValue(0); + // yacky yacky yacky... + // so, usercontrols & other visual basic related controls give us coords as floats in twips + // but mfc controls give us integers as pixels... + if (o.GetType() == typeof(Single)) { + return Twip2Pixel(Convert.ToDouble(o, CultureInfo.InvariantCulture), xDirection); + } + return Convert.ToInt32(o, CultureInfo.InvariantCulture); + } + + /// + /// + /// + private short Convert2short(Object o) { + o = ((Array)o).GetValue(0); + return Convert.ToInt16(o, CultureInfo.InvariantCulture); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced), SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + protected void RaiseOnMouseMove(Object o1, Object o2, Object o3, Object o4) { + RaiseOnMouseMove(Convert2short(o1), Convert2short(o2), Convert2int(o3, true), Convert2int(o4, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseMove(short button, short shift, float x, float y) { + RaiseOnMouseMove(button, shift, Twip2Pixel((int) x, true), Twip2Pixel((int) y, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseMove(short button, short shift, int x, int y) { + base.OnMouseMove(new MouseEventArgs( (MouseButtons)(((int)button) << 20), 1, x, y, 0)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced), SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + protected void RaiseOnMouseUp(Object o1, Object o2, Object o3, Object o4) { + RaiseOnMouseUp(Convert2short(o1), Convert2short(o2), Convert2int(o3, true), Convert2int(o4, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseUp(short button, short shift, float x, float y) { + RaiseOnMouseUp(button, shift, Twip2Pixel((int) x, true), Twip2Pixel((int) y, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseUp(short button, short shift, int x, int y) { + base.OnMouseUp(new MouseEventArgs((MouseButtons)(((int)button) << 20), 1, x, y, 0)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced), SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + protected void RaiseOnMouseDown(Object o1, Object o2, Object o3, Object o4) { + RaiseOnMouseDown(Convert2short(o1), Convert2short(o2), Convert2int(o3, true), Convert2int(o4, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseDown(short button, short shift, float x, float y) { + RaiseOnMouseDown(button, shift, Twip2Pixel((int) x,true), Twip2Pixel((int) y, false)); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseOnMouseDown(short button, short shift, int x, int y) { + base.OnMouseDown(new MouseEventArgs((MouseButtons)(((int)button) << 20), 1, x, y, 0)); + } + + /// + /// + private class VBFormat : UnsafeNativeMethods.IVBFormat { + + // IVBFormat methods: + // + int UnsafeNativeMethods.IVBFormat.Format(ref Object var, IntPtr pszFormat, IntPtr lpBuffer, short cpBuffer, int lcid, short firstD, short firstW, short[] result) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in Format"); + if (result == null) + return NativeMethods.E_INVALIDARG; + + result[0] = 0; + if (lpBuffer == IntPtr.Zero || cpBuffer < 2) + return NativeMethods.E_INVALIDARG; + + IntPtr pbstr = IntPtr.Zero; + int hr = UnsafeNativeMethods.VarFormat(ref var, new HandleRef(null, pszFormat), firstD, firstW, 32 /* VAR_FORMAT_NOSUBSTITUTE */, ref pbstr); + + try { + int i = 0; + if (pbstr != IntPtr.Zero) { + short ch = 0; + cpBuffer --; + for (;i < cpBuffer && (ch = Marshal.ReadInt16(pbstr, i * 2)) != 0; i++) { + Marshal.WriteInt16(lpBuffer, i * 2, ch); + } + } + Marshal.WriteInt16(lpBuffer, i * 2, (short) 0); + result[0] = (short) i; + } + finally { + SafeNativeMethods.SysFreeString(new HandleRef(null, pbstr)); + } + + return NativeMethods.S_OK; + } + } + + + + /// + /// + internal class EnumUnknown : UnsafeNativeMethods.IEnumUnknown { + private Object[] arr; + private int loc; + private int size; + + internal EnumUnknown(Object[] arr) { + //if (AxHTraceSwitch.TraceVerbose) Debug.WriteObject(arr); + this.arr = arr; + loc = 0; + size = (arr == null) ? 0 : arr.Length; + } + + private EnumUnknown(Object[] arr, int loc) : this(arr) { + this.loc = loc; + } + + unsafe int UnsafeNativeMethods.IEnumUnknown.Next(int celt, IntPtr rgelt, IntPtr pceltFetched) { + if (pceltFetched != IntPtr.Zero) + Marshal.WriteInt32(pceltFetched, 0, 0); + + if (celt < 0) { + return NativeMethods.E_INVALIDARG; + } + + int fetched = 0; + if (loc >= size) { + fetched = 0; + } + else { + for (; loc < size && fetched < celt; ++loc) { + if (arr[loc] != null) { + Marshal.WriteIntPtr(rgelt, Marshal.GetIUnknownForObject(arr[loc])); + rgelt = (IntPtr)((long)rgelt + (long)sizeof(IntPtr)); + ++fetched; + } + } + } + + if (pceltFetched != IntPtr.Zero) + Marshal.WriteInt32(pceltFetched, 0, fetched); + + if (fetched != celt) { + return(NativeMethods.S_FALSE); + } + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IEnumUnknown.Skip(int celt) { + loc += celt; + if (loc >= size) { + return(NativeMethods.S_FALSE); + } + return NativeMethods.S_OK; + } + + void UnsafeNativeMethods.IEnumUnknown.Reset() { + loc = 0; + } + + void UnsafeNativeMethods.IEnumUnknown.Clone(out UnsafeNativeMethods.IEnumUnknown ppenum) { + ppenum = new EnumUnknown(arr, loc); + } + } + + + /// + /// + internal class AxContainer : UnsafeNativeMethods.IOleContainer, UnsafeNativeMethods.IOleInPlaceFrame, IReflect { + internal ContainerControl parent; + private IContainer assocContainer; // associated IContainer... + // the assocContainer may be null, in which case all this container does is + // forward [de]activation messages to the requisite container... + private AxHost siteUIActive; + private AxHost siteActive; + private bool formAlreadyCreated = false; + private Hashtable containerCache = new Hashtable(); // name -> Control + private int lockCount = 0; + private Hashtable components = null; // Control -> any + private Hashtable proxyCache = null; + private AxHost ctlInEditMode = null; + + private const int GC_CHILD = 0x1; + private const int GC_LASTSIBLING = 0x2; + private const int GC_FIRSTSIBLING = 0x4; + private const int GC_CONTAINER = 0x20; + private const int GC_PREVSIBLING = 0x40; + private const int GC_NEXTSIBLING = 0x80; + + internal AxContainer(ContainerControl parent) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in constructor. Parent created : "+parent.Created.ToString()); + this.parent = parent; + if (parent.Created) FormCreated(); + } + + // IReflect methods: + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr,Binder binder, Type[] types,ParameterModifier[] modifiers) { + return null; + } + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr) { + return null; + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return new MethodInfo[0]; + } + + FieldInfo IReflect.GetField(String name, BindingFlags bindingAttr) { + return null; + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return new FieldInfo[0]; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr) { + return null; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return null; + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + return new PropertyInfo[0]; + } + + MemberInfo[] IReflect.GetMember(String name, BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + Object IReflect.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, + Object target, Object[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters) { + + foreach(DictionaryEntry e in containerCache) { + string ctlName = GetNameForControl((Control)e.Key); + if (ctlName.Equals(name)) { + return GetProxyForControl((Control)e.Value); + } + } + + throw E_FAIL; + } + + Type IReflect.UnderlyingSystemType { + get { + return null; + } + } + + /// + /// [To be supplied.] + /// + internal UnsafeNativeMethods.IExtender GetProxyForControl(Control ctl) { + UnsafeNativeMethods.IExtender rval = null; + if (proxyCache == null) { + proxyCache = new Hashtable(); + } + else { + rval = (UnsafeNativeMethods.IExtender) proxyCache[ctl]; + } + if (rval == null) { + if (ctl != parent && !GetControlBelongs(ctl)) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "!parent || !belongs NYI"); + AxContainer c = FindContainerForControl(ctl); + if (c != null) { + rval = new ExtenderProxy(ctl, c); + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "unable to find proxy, returning null"); + return null; + } + } + else { + rval = new ExtenderProxy(ctl, this); + } + proxyCache.Add(ctl, rval); + } + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "found proxy "+rval.ToString()); + return rval; + } + + /// + /// [To be supplied.] + /// + internal string GetNameForControl(Control ctl) { + string name = (ctl.Site != null) ? ctl.Site.Name : ctl.Name; + return (name == null) ? "" : name; + } + + /// + /// [To be supplied.] + /// + internal Object GetProxyForContainer() { + return this; + } + + /// + /// [To be supplied.] + /// + internal void AddControl(Control ctl) { + // + lock(this) { + if (containerCache.Contains(ctl)) + throw new ArgumentException(SR.GetString(SR.AXDuplicateControl, GetNameForControl(ctl)), "ctl"); + + containerCache.Add(ctl, ctl); + + if (assocContainer == null) { + ISite site = ctl.Site; + if (site != null) { + assocContainer = site.Container; + IComponentChangeService ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (ccs != null) { + ccs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved); + } + } + } + else { +#if DEBUG + ISite site = ctl.Site; + if (site != null && assocContainer != site.Container) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "mismatch between assoc container & added control"); + } +#endif + } + } + } + + internal void RemoveControl(Control ctl) { + // + lock(this) { + if (containerCache.Contains(ctl)) { + containerCache.Remove(ctl); + } + } + } + + + /// + /// [To be supplied.] + /// + private void LockComponents() { + lockCount++; + } + + /// + /// [To be supplied.] + /// + private void UnlockComponents() { + lockCount--; + if (lockCount == 0) { + components = null; + } + } + + /// + /// [To be supplied.] + /// + internal UnsafeNativeMethods.IEnumUnknown EnumControls(Control ctl, int dwOleContF, int dwWhich) { + GetComponents(); + + LockComponents(); + try { + ArrayList l = null; + bool selected = (dwWhich & NativeMethods.ActiveX.GC_WCH_FSELECTED) != 0; + bool reverse = (dwWhich & NativeMethods.ActiveX.GC_WCH_FREVERSEDIR) != 0; + // Note that visual basic actually ignores the next/prev flags... we will not + bool onlyNext = (dwWhich & NativeMethods.ActiveX.GC_WCH_FONLYNEXT) != 0; + bool onlyPrev = (dwWhich & NativeMethods.ActiveX.GC_WCH_FONLYPREV) != 0; + dwWhich = dwWhich & ~(NativeMethods.ActiveX.GC_WCH_FSELECTED | NativeMethods.ActiveX.GC_WCH_FREVERSEDIR | + NativeMethods.ActiveX.GC_WCH_FONLYNEXT | NativeMethods.ActiveX.GC_WCH_FONLYPREV); + if (onlyNext && onlyPrev) { + Debug.Fail("onlyNext && onlyPrev are both set!"); + throw E_INVALIDARG; + } + if (dwWhich == NativeMethods.ActiveX.GC_WCH_CONTAINER || dwWhich == NativeMethods.ActiveX.GC_WCH_CONTAINED) { + if (onlyNext || onlyPrev) { + Debug.Fail("GC_WCH_FONLYNEXT or FONLYPREV used with CONTANER or CONATINED"); + throw E_INVALIDARG; + } + } + int first = 0; + int last = -1; // meaning all + Control[] ctls = null; + switch (dwWhich) { + default: + Debug.Fail("Bad GC_WCH"); + throw E_INVALIDARG; + case NativeMethods.ActiveX.GC_WCH_CONTAINED: + ctls = ctl.GetChildControlsInTabOrder(false); + ctl = null; + break; + case NativeMethods.ActiveX.GC_WCH_SIBLING: + Control p = ctl.ParentInternal; + if (p != null) { + ctls = p.GetChildControlsInTabOrder(false); + if (onlyPrev) { + last = ctl.TabIndex; + } + else if (onlyNext) { + first = ctl.TabIndex + 1; + } + } + else { + ctls = new Control[0]; + } + ctl = null; + break; + case NativeMethods.ActiveX.GC_WCH_CONTAINER: + l = new ArrayList(); + MaybeAdd(l, ctl, selected, dwOleContF, false); + while (ctl != null) { + AxContainer cont = FindContainerForControl(ctl); + if (cont != null) { + MaybeAdd(l, cont.parent, selected, dwOleContF, true); + ctl = cont.parent; + } + else { + break; + } + } + break; + case NativeMethods.ActiveX.GC_WCH_ALL: + Hashtable htbl = GetComponents(); + ctls = new Control[htbl.Keys.Count]; + htbl.Keys.CopyTo(ctls, 0); + ctl = parent; + break; + } + if (l == null) { + l = new ArrayList(); + if (last == -1 && ctls != null) last = ctls.Length; + if (ctl != null) MaybeAdd(l, ctl, selected, dwOleContF, false); + for (int i = first; i < last; i++) { + MaybeAdd(l, ctls[i], selected, dwOleContF, false); + } + } + Object[] rval = new Object[l.Count]; + l.CopyTo(rval, 0); + if (reverse) { + for (int i = 0, j = rval.Length - 1; i < j; i++, j--) { + Object temp = rval[i]; + rval[i] = rval[j]; + rval[j] = temp; + } + } + return new EnumUnknown(rval); + } + finally { + UnlockComponents(); + } + } + + /// + /// [To be supplied.] + /// + private void MaybeAdd(ArrayList l, Control ctl, bool selected, int dwOleContF, bool ignoreBelong) { + if (!ignoreBelong && ctl != parent && !GetControlBelongs(ctl)) return; + if (selected) { + ISelectionService iss = GetSelectionService(ctl); + if (iss == null || !iss.GetComponentSelected(this)) return; + } + AxHost hostctl = ctl as AxHost; + if (hostctl != null && (dwOleContF & NativeMethods.ActiveX.OLECONTF_EMBEDDINGS) != 0) { + l.Add(hostctl.GetOcx()); + } + else if ((dwOleContF & NativeMethods.ActiveX.OLECONTF_OTHERS) != 0) { + Object item = GetProxyForControl(ctl); + if (item != null) l.Add(item); + } + } + + /// + /// [To be supplied.] + /// + private void FillComponentsTable(IContainer container) { + if (container != null) { + ComponentCollection comps = container.Components; + if (comps != null) { + components = new Hashtable(); + foreach (IComponent comp in comps) { + if (comp is Control && comp != parent && comp.Site != null) { + components.Add(comp, comp); + } + } + return; + } + } + + Debug.Assert(parent.Site == null, "Parent is sited but we could not find IContainer!!!"); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Did not find a container in FillComponentsTable!!!"); + + bool checkHashTable = true; + Control[] ctls = new Control[containerCache.Values.Count]; + containerCache.Values.CopyTo(ctls, 0); + if (ctls != null) { + if (ctls.Length > 0 && components == null) { + components = new Hashtable(); + checkHashTable = false; + } + for (int i = 0; i < ctls.Length; i ++) { + if (checkHashTable && !components.Contains(ctls[i])) { + components.Add(ctls[i], ctls[i]); + } + } + } + + GetAllChildren(this.parent); + } + + private void GetAllChildren(Control ctl) { + if (ctl == null) + return; + + if (components == null) { + components = new Hashtable(); + } + + if (ctl != this.parent && !components.Contains(ctl)) + components.Add(ctl, ctl); + + foreach(Control c in ctl.Controls) { + GetAllChildren(c); + } + } + + /// + /// [To be supplied.] + /// + private Hashtable GetComponents() { + return GetComponents(GetParentsContainer()); + } + + /// + /// [To be supplied.] + /// + private Hashtable GetComponents(IContainer cont) { + if (lockCount == 0) { + FillComponentsTable(cont); + } + return components; + } + + /// + /// [To be supplied.] + /// + private bool GetControlBelongs(Control ctl) { + Hashtable comps = GetComponents(); + return comps[ctl] != null; + } + + /// + /// [To be supplied.] + /// + private IContainer GetParentIsDesigned() { + ISite site = parent.Site; + if (site != null && site.DesignMode) return site.Container; + return null; + } + + /// + /// [To be supplied.] + /// + private IContainer GetParentsContainer() { + IContainer rval = GetParentIsDesigned(); + Debug.Assert(rval == null || assocContainer == null || (rval == assocContainer), + "mismatch between getIPD & aContainer"); + return rval == null ? assocContainer : rval; + } + + /// + /// [To be supplied.] + /// + private bool RegisterControl(AxHost ctl) { + ISite site = ctl.Site; + if (site != null) { + IContainer cont = site.Container; + if (cont != null) { + if (assocContainer != null) { + return cont == assocContainer; + } + else { + assocContainer = cont; + IComponentChangeService ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (ccs != null) { + ccs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved); + } + return true; + } + } + } + return false; + } + + private void OnComponentRemoved(object sender, ComponentEventArgs e) { + Control c = e.Component as Control; + if (sender == assocContainer && c != null) { + RemoveControl(c); + } + } + + /// + /// [To be supplied.] + /// + internal static AxContainer FindContainerForControl(Control ctl) { + AxHost axctl = ctl as AxHost; + if (axctl != null) + { + if (axctl.container != null) return axctl.container; + ContainerControl f = axctl.ContainingControl; + if (f != null) { + AxContainer container = f.CreateAxContainer(); + if (container.RegisterControl(axctl)) { + container.AddControl(axctl); + return container; + } + } + } + return null; + } + +#if false + // FxCop: Currently not used + private void OnOldActiveControl(Control valueOld, Control valueNew) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "onOAC called with old: "+valueOld.ToString()+" new: "+valueNew.ToString()); + if (!(valueOld is AxHost)) return; + AxContainer c = FindContainerForControl(valueOld); + if (c != null) { + c.OnOldActiveControlInternal(valueOld); + } + else { + Debug.Fail("control w/o a container... pretty bad..."+valueOld.ToString()); + } + } + + /// + /// [To be supplied.] + /// + // FxCop: Currently not used + private void OnOldActiveControlInternal(Control valueOld) { + if (siteUIActive == valueOld) siteUIActive.UiDeactivate(); + } + + // FxCop: Currently not used + private void OnNewActiveControl(Control value) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "onNAC called with new: "+value.ToString()); + if (!(value is AxHost)) return; + AxContainer c = FindContainerForControl(value); + if (c != null) { + c.OnNewActiveControlInternal((AxHost)value); + } + else { + Debug.Fail("control w/o a container... pretty bad..."+value.ToString()); + } + } + + /// + /// [To be supplied.] + /// + // FxCop: Currently not used + private void OnNewActiveControlInternal(AxHost value) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "New active control. siteUIActive is" +siteUIActive.ToString()+" Control is "+value.ToString()); + if (siteUIActive != null && siteUIActive != value) { + Debug.Fail("we should not have an ui active site on this container!"+parent.ToString()); + siteUIActive.UiDeactivate(); + } +#if DEBUG + if (siteUIActive != null && siteUIActive != value) Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Why is "+siteUIActive.ToString()+" still active?"); + if (!value.CanUIActivate) Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "why can't "+value.ToString()+" be uiactivated?"); +#endif + if (siteUIActive == null && value.CanUIActivate) { + // we need to uiactivate it ourselves... + try { + ((AxHost)value).UiActivate(); + } + catch (Exception e) { + Debug.Fail(e.ToString()); + } + } +#if DEBUG + if (siteUIActive != parent.ActiveControl && value.CanUIActivate) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "form's active control is "+parent.ActiveControl.ToString()); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "could not reconcile active controls... bad things loom on the horizon..."); + } +#endif + if (siteUIActive == null) { + // so now the form thinks that value is the active control but it's not ui active... + // this can often lead to bad things unless we are carefull... + siteActive = value; + // basically, when siteActive goes inplacedeactivate, we need to treat it the same as a + // siteuiactive going uiactivedeactivate... + } + } +#endif + + /// + /// [To be supplied.] + /// + internal void OnInPlaceDeactivate(AxHost site) { + if (siteActive == site) { + siteActive = null; + if (site.GetSiteOwnsDeactivation()) { + parent.ActiveControl = null; + } + else { + // we need to tell the form to switch activation to the next thingie... + Debug.Fail("what pathological control is calling inplacedeactivate by itself?"); + } + } + } + + /// + /// [To be supplied.] + /// + internal void OnUIDeactivate(AxHost site) { +#if DEBUG + if (siteUIActive != null) + Debug.Assert(siteUIActive == site, "deactivating when not active..."); +#endif // DEBUG + + siteUIActive = null; + site.RemoveSelectionHandler(); + site.SetSelectionStyle(1); + site.editMode = EDITM_NONE; + if (site.GetSiteOwnsDeactivation()) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, " our site owns deactivation "); + ContainerControl f = site.ContainingControl; + Debug.Assert(f != null, "a control has to be on a ContainerControl..."); + if (f != null) { + // f.setActiveControl(null); + } + } + } + + /// + /// [To be supplied.] + /// + internal void OnUIActivate(AxHost site) { + // The ShDocVw control repeatedly calls OnUIActivate() with the same + // site. This causes the assert below to fire. + // + if (siteUIActive == site) + return; + + if (siteUIActive != null && siteUIActive != site) { + AxHost tempSite = siteUIActive; + bool ownDisposing = tempSite.GetAxState(AxHost.ownDisposing); + try { + tempSite.SetAxState(AxHost.ownDisposing, true); + tempSite.GetInPlaceObject().UIDeactivate(); + } + finally { + tempSite.SetAxState(AxHost.ownDisposing, ownDisposing); + } + } + site.AddSelectionHandler(); + Debug.Assert(siteUIActive == null, "Object did not call OnUIDeactivate"); + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "active Object is now "+site.ToString()); + siteUIActive = site; + ContainerControl f = site.ContainingControl; + Debug.Assert(f != null, "a control has to be on a ContainerControl..."); + if (f != null) { + f.ActiveControl = site; + } + } + + /// + /// [To be supplied.] + /// + private void ListAxControls(ArrayList list, bool fuseOcx) { + Hashtable components = GetComponents(); + if (components == null) return; + Control[] ctls = new Control[components.Keys.Count]; + components.Keys.CopyTo(ctls, 0); + if (ctls != null) { + for (int i = 0; i < ctls.Length; i++) { + Control ctl = ctls[i]; + AxHost hostctl = ctl as AxHost; + if (hostctl != null) { + if (fuseOcx) { + list.Add(hostctl.GetOcx()); + } + else { + list.Add(ctl); + } + } + } + } + } + + /// + /// [To be supplied.] + /// + internal void ControlCreated(AxHost invoker) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in controlCreated for "+invoker.ToString()+" fAC: "+formAlreadyCreated.ToString()); + if (formAlreadyCreated) { + if (invoker.IsUserMode() && invoker.AwaitingDefreezing()) { + invoker.Freeze(false); + } + } + else { + // the form will be created in the future + parent.CreateAxContainer(); + } + } + + internal void FormCreated() { + if (formAlreadyCreated) return; + formAlreadyCreated = true; + ArrayList l = new ArrayList(); + ListAxControls(l, false); + AxHost[] axControls = new AxHost[l.Count]; + l.CopyTo(axControls, 0); + for (int i = 0; i < axControls.Length; i++) { + AxHost control = axControls[i]; + if (control.GetOcState() >= OC_RUNNING && control.IsUserMode() && control.AwaitingDefreezing()) { + control.Freeze(false); + } + } + } + + + // IOleContainer methods: + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleContainer.ParseDisplayName(Object pbc, string pszDisplayName, int[] pchEaten, Object[] ppmkOut) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in ParseDisplayName"); + if (ppmkOut != null) + ppmkOut[0] = null; + return NativeMethods.E_NOTIMPL; + } + + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleContainer.EnumObjects(int grfFlags, out UnsafeNativeMethods.IEnumUnknown ppenum) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in EnumObjects"); + ppenum = null; + if ((grfFlags & 1) != 0) { // 1 == OLECONTF_EMBEDDINGS + Debug.Assert(parent != null, "gotta have it..."); + ArrayList list = new ArrayList(); + ListAxControls(list, true); + if (list.Count > 0) { + Object[] temp = new Object[list.Count]; + list.CopyTo(temp, 0); + ppenum = new EnumUnknown(temp); + return NativeMethods.S_OK; + } + } + ppenum = new EnumUnknown(null); + return NativeMethods.S_OK; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleContainer.LockContainer(bool fLock) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in LockContainer"); + return NativeMethods.E_NOTIMPL; + } + + // IOleInPlaceFrame methods: + + IntPtr UnsafeNativeMethods.IOleInPlaceFrame.GetWindow() { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetWindow"); + return parent.Handle; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.ContextSensitiveHelp(int fEnterMode) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in ContextSensitiveHelp"); + return NativeMethods.S_OK; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.GetBorder(NativeMethods.COMRECT lprectBorder) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetBorder"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.RequestBorderSpace(NativeMethods.COMRECT pborderwidths) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in RequestBorderSpace"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.SetBorderSpace(NativeMethods.COMRECT pborderwidths) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in SetBorderSpace"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// [To be supplied.] + /// + internal void OnExitEditMode(AxHost ctl) { + Debug.Assert(ctlInEditMode == null || ctlInEditMode == ctl, "who is exiting edit mode?"); + if (ctlInEditMode == null || ctlInEditMode != ctl) return; + ctlInEditMode = null; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.SetActiveObject(UnsafeNativeMethods.IOleInPlaceActiveObject pActiveObject, string pszObjName) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in SetActiveObject " + ((pszObjName == null) ? "" : pszObjName)); + if (siteUIActive != null) { + if (siteUIActive.iOleInPlaceActiveObjectExternal != pActiveObject) { + if (siteUIActive.iOleInPlaceActiveObjectExternal != null) { + Marshal.ReleaseComObject(siteUIActive.iOleInPlaceActiveObjectExternal); + } + siteUIActive.iOleInPlaceActiveObjectExternal = pActiveObject; + } + } + if (pActiveObject == null) { + if (ctlInEditMode != null) { + ctlInEditMode.editMode = EDITM_NONE; + ctlInEditMode = null; + } + return NativeMethods.S_OK; + } + AxHost ctl = null; + if (pActiveObject is UnsafeNativeMethods.IOleObject) { + UnsafeNativeMethods.IOleObject oleObject = (UnsafeNativeMethods.IOleObject) pActiveObject; + UnsafeNativeMethods.IOleClientSite clientSite = null; + try { + clientSite = oleObject.GetClientSite(); + if (clientSite is OleInterfaces) { + ctl = ((OleInterfaces)(clientSite)).GetAxHost(); + } + } + catch (COMException t) { + Debug.Fail(t.ToString()); + } + if (ctlInEditMode != null) { + Debug.Fail("control " + ctlInEditMode.ToString() + " did not reset its edit mode to null"); + ctlInEditMode.SetSelectionStyle(1); + ctlInEditMode.editMode = EDITM_NONE; + } + + if (ctl == null) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "control w/o a valid site called setactiveobject"); + ctlInEditMode = null; + } + else { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "resolved to " + ctl.ToString()); + if (!ctl.IsUserMode()) { + ctlInEditMode = ctl; + ctl.editMode = EDITM_OBJECT; + ctl.AddSelectionHandler(); + ctl.SetSelectionStyle(2); + } + } + } + return NativeMethods.S_OK; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.InsertMenus(IntPtr hmenuShared, NativeMethods.tagOleMenuGroupWidths lpMenuWidths) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in InsertMenus"); + return NativeMethods.S_OK; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.SetMenu(IntPtr hmenuShared, IntPtr holemenu, IntPtr hwndActiveObject) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in SetMenu"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.RemoveMenus(IntPtr hmenuShared) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in RemoveMenus"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.SetStatusText(string pszStatusText) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in SetStatusText"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.EnableModeless(bool fEnable) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in EnableModeless"); + return NativeMethods.E_NOTIMPL; + } + /// + /// + /// [To be supplied.] + /// + int UnsafeNativeMethods.IOleInPlaceFrame.TranslateAccelerator(ref NativeMethods.MSG lpmsg, short wID) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in IOleInPlaceFrame.TranslateAccelerator"); + return NativeMethods.S_FALSE; + } + + // EXPOSED + + /// + /// + private class ExtenderProxy : UnsafeNativeMethods.IExtender, UnsafeNativeMethods.IVBGetControl, UnsafeNativeMethods.IGetVBAObject, UnsafeNativeMethods.IGetOleObject, IReflect { + private WeakReference pRef; + private WeakReference pContainer; + + internal ExtenderProxy(Control principal, AxContainer container) { + pRef = new WeakReference(principal); + pContainer = new WeakReference(container); + } + + private Control GetP() { + return(Control) pRef.Target; + } + + private AxContainer GetC() { + return(AxContainer) pContainer.Target; + } + + int UnsafeNativeMethods.IVBGetControl.EnumControls(int dwOleContF, int dwWhich, out UnsafeNativeMethods.IEnumUnknown ppenum) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in EnumControls for proxy"); + ppenum = GetC().EnumControls(GetP(), dwOleContF, dwWhich); + return NativeMethods.S_OK; + } + + object UnsafeNativeMethods.IGetOleObject.GetOleObject(ref Guid riid) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetOleObject for proxy"); + if (!riid.Equals(ioleobject_Guid)) + throw E_INVALIDARG; + + Control ctl = GetP(); + if (ctl != null && ctl is AxHost) { + return ((AxHost)ctl).GetOcx(); + } + + throw E_FAIL; + } + + int UnsafeNativeMethods.IGetVBAObject.GetObject(ref Guid riid, UnsafeNativeMethods.IVBFormat[] rval, int dwReserved) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetObject for proxy"); + if (rval == null || riid.Equals(Guid.Empty)) + return NativeMethods.E_INVALIDARG; + + if (riid.Equals(ivbformat_Guid)) { + rval[0] = new VBFormat(); + return NativeMethods.S_OK; + } + else { + rval[0] = null; + return NativeMethods.E_NOINTERFACE; + } + } + + public int Align { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getAlign for proxy for "+ GetP().ToString()); + int rval = (int)((Control)GetP()).Dock; + if (rval < NativeMethods.ActiveX.ALIGN_MIN || rval > NativeMethods.ActiveX.ALIGN_MAX) { + rval = NativeMethods.ActiveX.ALIGN_NO_CHANGE; + } + return rval; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setAlign for proxy for "+ GetP().ToString()+" "+ + value.ToString(CultureInfo.InvariantCulture)); + GetP().Dock = (DockStyle)value; + } + } + + public UInt32 BackColor { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getBackColor for proxy for "+ GetP().ToString()); + return AxHost.GetOleColorFromColor(((Control)GetP()).BackColor); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setBackColor for proxy for "+ GetP().ToString()+" "+ + value.ToString(CultureInfo.InvariantCulture)); + GetP().BackColor = AxHost.GetColorFromOleColor(value); + } + } + + public bool Enabled { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getEnabled for proxy for "+ GetP().ToString()); + return GetP().Enabled; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setEnabled for proxy for "+ GetP().ToString()+" "+value.ToString()); + GetP().Enabled = value; + } + } + + public UInt32 ForeColor { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getForeColor for proxy for "+ GetP().ToString()); + return AxHost.GetOleColorFromColor(((Control)GetP()).ForeColor); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setForeColor for proxy for "+ GetP().ToString()+" "+ + value.ToString(CultureInfo.InvariantCulture)); + GetP().ForeColor = AxHost.GetColorFromOleColor(value); + } + } + + public int Height { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getHeight for proxy for "+ GetP().ToString()); + return Pixel2Twip(GetP().Height, false); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setHeight for proxy for "+ GetP().ToString()+" "+ + Twip2Pixel(value,false).ToString(CultureInfo.InvariantCulture)); + GetP().Height = Twip2Pixel(value, false); + } + } + + public int Left { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getLeft for proxy for "+ GetP().ToString()); + return Pixel2Twip(GetP().Left, true); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setLeft for proxy for "+ GetP().ToString()+" "+ + Twip2Pixel(value, true).ToString(CultureInfo.InvariantCulture)); + GetP().Left = Twip2Pixel(value, true); + } + } + + public object Parent { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getParent for proxy for "+ GetP().ToString()); + return GetC().GetProxyForControl(GetC().parent); + } + } + + public short TabIndex { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getTabIndex for proxy for "+ GetP().ToString()); + return (short)GetP().TabIndex; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setTabIndex for proxy for "+ GetP().ToString()+" "+ + value.ToString(CultureInfo.InvariantCulture)); + GetP().TabIndex = (int)value; + } + } + + public bool TabStop { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getTabStop for proxy for "+ GetP().ToString()); + return GetP().TabStop; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setTabStop for proxy for "+ GetP().ToString()+" "+value.ToString()); + GetP().TabStop = value; + } + } + + public int Top { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getTop for proxy for "+ GetP().ToString()); + return Pixel2Twip(GetP().Top, false); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setTop for proxy for "+ GetP().ToString()+" "+ + Twip2Pixel(value, false).ToString(CultureInfo.InvariantCulture)); + GetP().Top = Twip2Pixel(value, false); + } + } + + public bool Visible { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getVisible for proxy for "+ GetP().ToString()); + return GetP().Visible; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setVisible for proxy for "+ GetP().ToString()+" "+value.ToString()); + GetP().Visible = value; + } + } + + public int Width { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getWidth for proxy for "+ GetP().ToString()); + return Pixel2Twip(GetP().Width,true); + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setWidth for proxy for "+ GetP().ToString()+" "+ + Twip2Pixel(value, true).ToString(CultureInfo.InvariantCulture)); + GetP().Width = Twip2Pixel(value, true); + } + } + + public string Name { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getName for proxy for "+ GetP().ToString()); + return GetC().GetNameForControl(GetP()); + } + } + + public IntPtr Hwnd { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getHwnd for proxy for "+ GetP().ToString()); + return GetP().Handle; + } + } + + public object Container { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getContainer for proxy for "+ GetP().ToString()); + return GetC().GetProxyForContainer(); + } + } + + public string Text { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getText for proxy for "+ GetP().ToString()); + return GetP().Text; + } + + set { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in setText for proxy for "+ GetP().ToString()); + GetP().Text = value; + } + } + + public void Move(Object left, Object top, Object width, Object height) { + } + + // IReflect methods: + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr,Binder binder, Type[] types,ParameterModifier[] modifiers) { + return null; + } + + MethodInfo IReflect.GetMethod(String name,BindingFlags bindingAttr) { + return null; + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) { + return new MethodInfo[] {this.GetType().GetMethod("Move")}; + } + + FieldInfo IReflect.GetField(String name, BindingFlags bindingAttr) { + return null; + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { + return new FieldInfo[0]; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr) { + PropertyInfo prop = GetP().GetType().GetProperty(name, bindingAttr); + if (prop == null) { + prop = this.GetType().GetProperty(name, bindingAttr); + } + return prop; + } + + PropertyInfo IReflect.GetProperty(String name, BindingFlags bindingAttr, Binder binder,Type returnType, Type[] types, ParameterModifier[] modifiers) { + PropertyInfo prop = GetP().GetType().GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + if (prop == null) { + prop = this.GetType().GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + return prop; + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) { + PropertyInfo[] extenderProps = this.GetType().GetProperties(bindingAttr); + PropertyInfo[] ctlProps = GetP().GetType().GetProperties(bindingAttr); + + if (extenderProps == null) { + return ctlProps; + } + else if (ctlProps == null) { + return extenderProps; + } + else { + int iProp = 0; + PropertyInfo[] props = new PropertyInfo[extenderProps.Length + ctlProps.Length]; + + foreach(PropertyInfo prop in extenderProps) { + props[iProp++] = prop; + } + + foreach(PropertyInfo prop in ctlProps) { + props[iProp++] = prop; + } + + return props; + } + } + + MemberInfo[] IReflect.GetMember(String name, BindingFlags bindingAttr) { + MemberInfo[] memb = GetP().GetType().GetMember(name, bindingAttr); + if (memb == null) { + memb = this.GetType().GetMember(name, bindingAttr); + } + return memb; + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { + MemberInfo[] extenderMembs = this.GetType().GetMembers(bindingAttr); + MemberInfo[] ctlMembs = GetP().GetType().GetMembers(bindingAttr); + + if (extenderMembs == null) { + return ctlMembs; + } + else if (ctlMembs == null) { + return extenderMembs; + } + else { + MemberInfo[] membs = new MemberInfo[extenderMembs.Length + ctlMembs.Length]; + + Array.Copy(extenderMembs, 0, membs, 0, extenderMembs.Length); + Array.Copy(ctlMembs, 0, membs, extenderMembs.Length, ctlMembs.Length); + + return membs; + } + } + + Object IReflect.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, + Object target, Object[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters) { + try { + return this.GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + catch(MissingMethodException) { + return this.GetP().GetType().InvokeMember(name, invokeAttr, binder, GetP(), args, modifiers, culture, namedParameters); + } + } + + Type IReflect.UnderlyingSystemType { + get { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "In UnderlyingSystemType"); + return null; + } + } + } + } + + /// + /// + /// StateConverter is a class that can be used to convert + /// State from one data type to another. Access this + /// class through the TypeDescriptor. + /// + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class StateConverter : TypeConverter { + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object in the given source type to the native type of the converter + /// using the context. + /// + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(byte[])) { + return true; + } + + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(byte[])) { + return true; + } + + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is byte[]) { + MemoryStream ms = new MemoryStream((byte[])value); + return new State(ms); + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(byte[])) { + if (value != null) { + MemoryStream ms = new MemoryStream(); + State state = (State)value; + state.Save(ms); + ms.Close(); + return ms.ToArray(); + } + else + return new byte[0]; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + + /// + /// + /// The class which encapsulates the persisted state of the underlying activeX control + /// An instance of this class my be obtained either by calling getOcxState on an + /// AxHost object, or by reading in from a stream. + /// + [ + TypeConverterAttribute(typeof(TypeConverter)), + Serializable + ] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + [SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly")] + public class State : ISerializable { + private int VERSION = 1; + private int length; + private byte[] buffer; + internal int type; + private MemoryStream ms; + private UnsafeNativeMethods.IStorage storage; + private UnsafeNativeMethods.ILockBytes iLockBytes; + private bool manualUpdate = false; + private string licenseKey = null; + private PropertyBagStream propBag; + + // create on save from ipersist stream + /// + /// [To be supplied.] + /// + internal State(MemoryStream ms, int storageType, AxHost ctl, PropertyBagStream propBag) { + type = storageType; + this.propBag = propBag; + // dangerous? + length = (int)ms.Length; + this.ms = ms; + this.manualUpdate = ctl.GetAxState(AxHost.manualUpdate); + this.licenseKey = ctl.GetLicenseKey(); + } + + internal State(PropertyBagStream propBag) { + this.propBag = propBag; + } + + internal State(MemoryStream ms) { + this.ms = ms; + this.length = (int)ms.Length; + InitializeFromStream(ms); + } + + // create on init new w/ storage... + /// + /// [To be supplied.] + /// + internal State(AxHost ctl) { + CreateStorage(); + manualUpdate = ctl.GetAxState(AxHost.manualUpdate); + licenseKey = ctl.GetLicenseKey(); + type = STG_STORAGE; + } + + /// + /// + /// [To be supplied.] + /// + public State(Stream ms, int storageType, bool manualUpdate, string licKey) { + type = storageType; + // dangerous? + length = (int)ms.Length; + this.manualUpdate = manualUpdate; + this.licenseKey = licKey; + + InitializeBufferFromStream(ms); + } + + /** + * Constructor used in deserialization + */ + protected State(SerializationInfo info, StreamingContext context) { + SerializationInfoEnumerator sie = info.GetEnumerator(); + if (sie == null) { + return; + } + for (; sie.MoveNext();) { + if (String.Compare(sie.Name, "Data", true, CultureInfo.InvariantCulture) == 0) { + try { + byte[] dat = (byte[])sie.Value; + if (dat != null) { + InitializeFromStream(new MemoryStream(dat)); + } + + } + catch (Exception e) { + Debug.Fail("failure: " + e.ToString()); + } + } + else if (String.Compare(sie.Name, "PropertyBagBinary", true, CultureInfo.InvariantCulture) == 0) { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Loading up property bag from stream..."); + byte[] dat = (byte[])sie.Value; + if (dat != null) { + this.propBag = new PropertyBagStream(); + propBag.Read(new MemoryStream(dat)); + } + + } + catch (Exception e) { + Debug.Fail("failure: " + e.ToString()); + } + } + } + } + + /// + /// [To be supplied.] + /// + internal int Type { + get { + return type; + } + set { + type = value; + } + } + + internal bool _GetManualUpdate() { + return manualUpdate; + } + + internal string _GetLicenseKey() { + return licenseKey; + } + + /// + /// [To be supplied.] + /// + private void CreateStorage() { + Debug.Assert(storage == null, "but we already have a storage!!!"); + IntPtr hglobal = IntPtr.Zero; + if (buffer != null) { + hglobal = UnsafeNativeMethods.GlobalAlloc(NativeMethods.GMEM_MOVEABLE, length); + IntPtr pointer = UnsafeNativeMethods.GlobalLock(new HandleRef(null, hglobal)); + try { + if (pointer != IntPtr.Zero) { + Marshal.Copy(buffer, 0, pointer, length); + } + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, hglobal)); + } + } + bool failed = false; + try { + iLockBytes = UnsafeNativeMethods.CreateILockBytesOnHGlobal(new HandleRef(null, hglobal), true); + if (buffer == null) { + storage = UnsafeNativeMethods.StgCreateDocfileOnILockBytes(iLockBytes, + NativeMethods.STGM_CREATE | NativeMethods.STGM_READWRITE | NativeMethods.STGM_SHARE_EXCLUSIVE, 0); + } + else { + storage = UnsafeNativeMethods.StgOpenStorageOnILockBytes(iLockBytes, + null, NativeMethods.STGM_READWRITE | NativeMethods.STGM_SHARE_EXCLUSIVE, 0, 0); + } + } + catch (Exception t) { + Debug.Fail(t.ToString()); + failed = true; + } + if (failed) { + if (iLockBytes == null && hglobal != IntPtr.Zero) { + UnsafeNativeMethods.GlobalFree(new HandleRef(null, hglobal)); + } + else { + iLockBytes = null; + } + storage = null; + } + } + + internal UnsafeNativeMethods.IPropertyBag GetPropBag() { + return propBag; + } + + internal UnsafeNativeMethods.IStorage GetStorage() { + if (storage == null) + CreateStorage(); + return storage; + } + + internal UnsafeNativeMethods.IStream GetStream() { + if (ms == null) { + Debug.Assert(buffer != null, "gotta have the buffer already..."); + if (buffer == null) return null; + ms = new MemoryStream(buffer); + } + else { + ms.Seek(0, SeekOrigin.Begin); + } + return new UnsafeNativeMethods.ComStreamFromDataStream(ms); + } + + private void InitializeFromStream(Stream ids) { + BinaryReader br = new BinaryReader(ids); + + type = br.ReadInt32(); + int version = br.ReadInt32(); + manualUpdate = br.ReadBoolean(); + int cc = br.ReadInt32(); + if (cc != 0) { + licenseKey = new string(br.ReadChars(cc)); + } + for (int skipUnits = br.ReadInt32(); skipUnits > 0; skipUnits --) { + int len = br.ReadInt32(); + ids.Position = ids.Position + len; + } + + length = br.ReadInt32(); + if (length > 0) + buffer = br.ReadBytes(length); + } + + private void InitializeBufferFromStream(Stream ids) { + BinaryReader br = new BinaryReader(ids); + + length = br.ReadInt32(); + if (length > 0) + buffer = br.ReadBytes(length); + } + + internal State RefreshStorage(UnsafeNativeMethods.IPersistStorage iPersistStorage) { + Debug.Assert(storage != null, "how can we not have a storage object?"); + Debug.Assert(iLockBytes != null, "how can we have a storage w/o ILockBytes?"); + if (storage == null || iLockBytes == null) return null; + iPersistStorage.Save(storage, true); + storage.Commit(0); + iPersistStorage.HandsOffStorage(); + try { + buffer = null; + ms = null; + NativeMethods.STATSTG stat = new NativeMethods.STATSTG(); + iLockBytes.Stat(stat, NativeMethods.Ole.STATFLAG_NONAME); + length = (int) stat.cbSize; + buffer = new byte[length]; + IntPtr hglobal = UnsafeNativeMethods.GetHGlobalFromILockBytes(iLockBytes); + IntPtr pointer = UnsafeNativeMethods.GlobalLock(new HandleRef(null, hglobal)); + try { + if (pointer != IntPtr.Zero) { + Marshal.Copy(pointer, buffer, 0, length); + } + else { + length = 0; + buffer = null; + } + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, hglobal)); + } + } + finally { + iPersistStorage.SaveCompleted(storage); + } + return this; + } + + internal void Save(MemoryStream stream) { + BinaryWriter bw = new BinaryWriter(stream); + + bw.Write(type); + bw.Write(VERSION); + bw.Write(manualUpdate); + if (licenseKey != null) { + bw.Write(licenseKey.Length); + bw.Write(licenseKey.ToCharArray()); + } + else { + bw.Write((int)0); + } + bw.Write((int)0); // skip units + bw.Write(length); + if (buffer != null) { + bw.Write(buffer); + } + else if (ms != null) { + ms.Position = 0; + ms.WriteTo(stream); + } + else { + Debug.Assert(length == 0, "if we have no data, then our length has to be 0"); + } + } + + /// + /// + /// ISerializable private implementation + /// + /// + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + IntSecurity.UnmanagedCode.Demand(); + MemoryStream stream = new MemoryStream(); + Save(stream); + + si.AddValue("Data", stream.ToArray()); + + if (propBag != null) { + try { + stream = new MemoryStream(); + propBag.Write(stream); + si.AddValue("PropertyBagBinary", stream.ToArray()); + } + catch (Exception e) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Failed to serialize the property bag into ResX : " + e.ToString()); + } + } + } + } + + internal class PropertyBagStream : UnsafeNativeMethods.IPropertyBag { + private Hashtable bag = new Hashtable(); + + internal void Read(Stream stream) { + BinaryFormatter formatter = new BinaryFormatter(); + try { + bag = (Hashtable)formatter.Deserialize(stream); + } + catch { + // Error reading. Just init an empty hashtable. + bag = new Hashtable(); + } + } + + int UnsafeNativeMethods.IPropertyBag.Read(string pszPropName, ref object pVar, UnsafeNativeMethods.IErrorLog pErrorLog) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Reading property " + pszPropName + " from OCXState propertybag."); + + if (!bag.Contains(pszPropName)) + return NativeMethods.E_INVALIDARG; + + pVar = bag[pszPropName]; + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\tValue=" + ((pVar == null) ? "" : pVar.ToString())); + + // The EE returns a VT_EMPTY for a null. The problem is that visual basic6 expects the caller to respect the + // "hint" it gives in the VariantType. For eg., for a VT_BSTR, it expects that the callee will null + // out the BSTR field of the variant. Since, the EE or us cannot do anything about this, we will return + // a E_INVALIDARG rather than let visual basic6 crash. + // + return (pVar == null) ? NativeMethods.E_INVALIDARG : NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IPropertyBag.Write(string pszPropName, ref object pVar) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "Writing property " + pszPropName + " [" + pVar + "] into OCXState propertybag."); + if (pVar != null && !pVar.GetType().IsSerializable) { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "\t " + pVar.GetType().FullName + " is not serializable."); + return NativeMethods.S_OK; + } + + bag[pszPropName] = pVar; + return NativeMethods.S_OK; + } + + internal void Write(Stream stream) { + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, bag); + } + } + + /// + protected delegate void AboutBoxDelegate(); + + /// + /// + /// [To be supplied.] + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + [ComVisible(false)] + public class AxComponentEditor : WindowsFormsComponentEditor { + /// + /// + /// [To be supplied.] + /// + public override bool EditComponent(ITypeDescriptorContext context, object obj, IWin32Window parent) { + AxHost host = obj as AxHost; + if (host != null) + { + try { + Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in AxComponentEditor.EditComponent"); + ((UnsafeNativeMethods.IOleControlSite)host.oleSite).ShowPropertyFrame(); + return true; + } + catch (Exception t) { + Debug.Fail(t.ToString()); + throw; + } + } + return false; + } + } + + /// + /// + internal class AxPropertyDescriptor : PropertyDescriptor { + private PropertyDescriptor baseProp; + internal AxHost owner; + private DispIdAttribute dispid; + + private TypeConverter converter; + private UITypeEditor editor; + private ArrayList updateAttrs = new ArrayList(); + private int flags = 0; + + private const int FlagUpdatedEditorAndConverter = 0x00000001; + private const int FlagCheckGetter = 0x00000002; + private const int FlagGettterThrew = 0x00000004; + private const int FlagIgnoreCanAccessProperties = 0x00000008; + private const int FlagSettingValue = 0x00000010; + + + + + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Shipped in Everett + ] + internal AxPropertyDescriptor(PropertyDescriptor baseProp, AxHost owner) : base(baseProp) { + this.baseProp = baseProp; + this.owner = owner; + + // Get the category for this dispid. + // + dispid = (DispIdAttribute)baseProp.Attributes[typeof(DispIdAttribute)]; + if (dispid != null) { + // Look to see if this property has a property page. + // If it does, then it needs to be Browsable(true). + // + if (!this.IsBrowsable && !this.IsReadOnly) { + Guid g = GetPropertyPage(dispid.Value); + + if (!Guid.Empty.Equals(g)) { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Making property: " + this.Name + " browsable because we found an property page."); + AddAttribute(new BrowsableAttribute(true)); + } + } + + // Use the CategoryAttribute provided by the OCX. + // + CategoryAttribute cat = owner.GetCategoryForDispid(dispid.Value); + if (cat != null) { + AddAttribute(cat); + } + + // Check to see if this a DataSource property. + // If it is, we can always get and set the value of this property. + // + if (this.PropertyType.GUID.Equals(dataSource_Guid)) { + SetFlag(FlagIgnoreCanAccessProperties, true); + } + } + } + + public override Type ComponentType { + get { + return baseProp.ComponentType; + } + } + + public override TypeConverter Converter { + get { + + if (dispid != null) { + UpdateTypeConverterAndTypeEditorInternal(false, Dispid); + } + return (converter != null) ? converter : base.Converter; + } + } + + internal int Dispid { + get { + DispIdAttribute dispid = (DispIdAttribute)baseProp.Attributes[typeof(DispIdAttribute)]; + if (dispid != null) { + return dispid.Value; + } + + return NativeMethods.ActiveX.DISPID_UNKNOWN; + } + } + + public override bool IsReadOnly { + get { + return baseProp.IsReadOnly; + } + } + + public override Type PropertyType { + get { + return baseProp.PropertyType; + } + } + + internal bool SettingValue { + get { + return GetFlag(FlagSettingValue); + } + } + + private void AddAttribute(Attribute attr) { + updateAttrs.Add(attr); + } + + public override bool CanResetValue(object o) { + return baseProp.CanResetValue(o); + } + + public override object GetEditor(Type editorBaseType) { + + UpdateTypeConverterAndTypeEditorInternal(false, dispid.Value); + + if (editorBaseType.Equals(typeof(UITypeEditor)) && editor != null) { + return editor; + } + + return base.GetEditor(editorBaseType); + } + + private bool GetFlag(int flagValue) { + return ((flags & flagValue) == flagValue); + } + + private Guid GetPropertyPage(int dispid) { + try { + NativeMethods.IPerPropertyBrowsing ippb = owner.GetPerPropertyBrowsing(); + if (ippb == null) return Guid.Empty; + Guid rval; + if (NativeMethods.Succeeded(ippb.MapPropertyToPage(dispid, out rval))) { + return rval; + } + } + catch (COMException) { + } + catch (Exception t) { + Debug.Fail(t.ToString()); + } + return Guid.Empty; + } + + public override object GetValue(object component) { + if ((!GetFlag(FlagIgnoreCanAccessProperties) && !owner.CanAccessProperties) || GetFlag(FlagGettterThrew)) { + return null; + } + + try { + // Some controls fire OnChanged() notifications when getting values of some properties. ASURT 20190. + // To prevent this kind of recursion, we check to see if we are already inside a OnChanged() call. + // + owner.NoComponentChangeEvents++; + return baseProp.GetValue(component); + } + catch (Exception e) { + if (!GetFlag(FlagCheckGetter)) { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Get failed for : " + Name + " with exception: " + e.Message + " .Making property non-browsable."); + SetFlag(FlagCheckGetter, true); + AddAttribute(new BrowsableAttribute(false)); + owner.RefreshAllProperties = true; + SetFlag(FlagGettterThrew, true); + } + throw e; + } + finally { + owner.NoComponentChangeEvents--; + } + } + + public void OnValueChanged(object component) { + this.OnValueChanged(component, EventArgs.Empty); + } + + public override void ResetValue(object o) { + baseProp.ResetValue(o); + } + + private void SetFlag(int flagValue, bool value) { + if (value) { + flags |= flagValue; + } + else { + flags &= ~flagValue; + } + } + + public override void SetValue(Object component, Object value) { + if (!GetFlag(FlagIgnoreCanAccessProperties) && !owner.CanAccessProperties) { + return; + } + + // State oldOcxState = owner.OcxState; + + try { + SetFlag(FlagSettingValue, true); + if (this.PropertyType.IsEnum && (value.GetType() != this.PropertyType)) { + baseProp.SetValue(component, Enum.ToObject(this.PropertyType, value)); + } + else { + baseProp.SetValue(component, value); + } + } + finally { + SetFlag(FlagSettingValue, false); + } + + OnValueChanged(component); + if (owner == component) { + owner.SetAxState(AxHost.valueChanged, true); + } + } + + public override bool ShouldSerializeValue(object o) { + return baseProp.ShouldSerializeValue(o); + } + + internal void UpdateAttributes() { + if (updateAttrs.Count == 0) + return; + + ArrayList attributes = new ArrayList(AttributeArray); + foreach(Attribute attr in updateAttrs) { + attributes.Add(attr); + } + + Attribute[] temp = new Attribute[attributes.Count]; + attributes.CopyTo(temp, 0); + AttributeArray = temp; + + updateAttrs.Clear(); + } + + + /// + /// Called externally to update the editor or type converter. + /// This simply sets flags so this will happen, it doesn't actually to the update... + /// we wait and do that on-demand for perf. + /// + internal void UpdateTypeConverterAndTypeEditor(bool force) { + // if this is an external request, flip the flag to false so we do the update on demand. + // + if (GetFlag(FlagUpdatedEditorAndConverter) && force) { + SetFlag(FlagUpdatedEditorAndConverter, false); + } + } + + /// + /// Called externally to update the editor or type converter. + /// This simply sets flags so this will happen, it doesn't actually to the update... + /// we wait and do that on-demand for perf. + /// + internal void UpdateTypeConverterAndTypeEditorInternal(bool force, int dispid) { + + // check to see if we're being forced here or if the work really + // needs to be done. + // + if (GetFlag(FlagUpdatedEditorAndConverter) && !force) { + return; + } + + if (owner.GetOcx() == null) { + return; + } + + + try { + NativeMethods.IPerPropertyBrowsing ppb = owner.GetPerPropertyBrowsing(); + + if (ppb != null) { + bool hasStrings = false; + + // check for enums + NativeMethods.CA_STRUCT caStrings = new NativeMethods.CA_STRUCT(); + NativeMethods.CA_STRUCT caCookies = new NativeMethods.CA_STRUCT(); + + int hr = NativeMethods.S_OK; + + try { + hr = ppb.GetPredefinedStrings(dispid, caStrings, caCookies); + } + catch(ExternalException ex) { + hr = ex.ErrorCode; + Debug.Fail("An exception occurred inside IPerPropertyBrowsing::GetPredefinedStrings(dispid=" + + dispid + "), object type=" + new ComNativeDescriptor().GetClassName(ppb)); + } + + + if (hr != NativeMethods.S_OK) { + hasStrings = false; + // Destroy the existing editor if we created the current one + // so if the items have disappeared, we don't hold onto the old + // items. + if (converter is Com2EnumConverter) { + converter = null; + } + } + else { + hasStrings = true; + } + + if (hasStrings) { + OleStrCAMarshaler stringMarshaler = new OleStrCAMarshaler(caStrings); + Int32CAMarshaler intMarshaler = new Int32CAMarshaler(caCookies); + + if (stringMarshaler.Count > 0 && intMarshaler.Count > 0) { + if (converter == null) { + converter = new AxEnumConverter(this, new AxPerPropertyBrowsingEnum(this, owner, stringMarshaler, intMarshaler, true)); + } + else if (converter is AxEnumConverter){ + ((AxEnumConverter)converter).RefreshValues(); + AxPerPropertyBrowsingEnum axEnum = ((AxEnumConverter)converter).com2Enum as AxPerPropertyBrowsingEnum; + if (axEnum != null) { + axEnum.RefreshArrays(stringMarshaler, intMarshaler); + } + + } + + } + else { + //hasStrings = false; + } + } + else { + // if we didn't get any strings, try the proppage edtior + // + // Check to see if this is a property that we have already massaged to be a + // .Net type. If it is, don't bother with custom property pages. We already + // have a .Net Editor for this type. + // + ComAliasNameAttribute comAlias = (ComAliasNameAttribute)baseProp.Attributes[typeof(ComAliasNameAttribute)]; + if (comAlias == null) { + Guid g = GetPropertyPage(dispid); + + if (!Guid.Empty.Equals(g)) { + editor = new AxPropertyTypeEditor(this, g); + + // Show any non-browsable property that has an editor through a + // property page. + // + if (!this.IsBrowsable) { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "Making property: " + this.Name + " browsable because we found an editor."); + AddAttribute(new BrowsableAttribute(true)); + } + } + } + } + } + + SetFlag(FlagUpdatedEditorAndConverter, true); + } + catch (Exception e) { + Debug.WriteLineIf(AxPropTraceSwitch.TraceVerbose, "could not get the type editor for property: " + this.Name + " Exception: " + e); + } + } + } + + private class AxPropertyTypeEditor : UITypeEditor { + private AxPropertyDescriptor propDesc; + private Guid guid; + + public AxPropertyTypeEditor(AxPropertyDescriptor pd, Guid guid) { + propDesc = pd; + this.guid = guid; + } + + /// + /// + /// Takes the value returned from valueAccess.getValue() and modifies or replaces + /// the value, passing the result into valueAccess.setValue(). This is where + /// an editor can launch a modal dialog or create a drop down editor to allow + /// the user to modify the value. Host assistance in presenting UI to the user + /// can be found through the valueAccess.getService function. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + try { + object instance = context.Instance; + propDesc.owner.ShowPropertyPageForDispid(propDesc.Dispid, this.guid); + } + catch (Exception ex1) { + if (provider != null) { + IUIService uiSvc = (IUIService)provider.GetService(typeof(IUIService)); + if (uiSvc != null){ + uiSvc.ShowError(ex1, SR.GetString(SR.ErrorTypeConverterFailed)); + } + } + } + return value; + } + + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + } + + /// + /// simple derivation of the com2enumconverter that allows us to intercept + /// the call to GetStandardValues so we can on-demand update the enum values. + /// + private class AxEnumConverter : Com2EnumConverter { + private AxPropertyDescriptor target; + + public AxEnumConverter(AxPropertyDescriptor target, Com2Enum com2Enum) : base(com2Enum) { + this.target = target; + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + + // make sure the converter has been properly refreshed -- calling + // the Converter property does this. + // + TypeConverter tc = this; + tc = target.Converter; + return base.GetStandardValues(context); + + } + + } + + // This exists for perf reasons. We delay doing this until we + // are actually asked for the array of values. + // + private class AxPerPropertyBrowsingEnum : Com2Enum { + private AxPropertyDescriptor target; + private AxHost owner; + private OleStrCAMarshaler nameMarshaller; + private Int32CAMarshaler valueMarshaller; + private bool arraysFetched; + + public AxPerPropertyBrowsingEnum(AxPropertyDescriptor targetObject, AxHost owner, OleStrCAMarshaler names, Int32CAMarshaler values, bool allowUnknowns) : base(new string[0], new object[0], allowUnknowns) { + this.target = targetObject; + this.nameMarshaller = names; + this.valueMarshaller = values; + this.owner = owner; + this.arraysFetched = false; + } + + /// + /// + /// Retrieve a copy of the value array + /// + public override object[] Values { + get { + EnsureArrays(); + return base.Values; + } + } + + /// + /// Retrieve a copy of the nme array. + /// + public override string[] Names { + get { + EnsureArrays(); + return base.Names; + } + } + + // ensure that we have processed the caStructs into arrays + // of values and strings + // + private void EnsureArrays() { + if (this.arraysFetched) { + return; + } + + this.arraysFetched = true; + + try { + + // marshal the items. + object[] nameItems = nameMarshaller.Items; + object[] cookieItems= valueMarshaller.Items; + NativeMethods.IPerPropertyBrowsing ppb = (NativeMethods.IPerPropertyBrowsing)owner.GetPerPropertyBrowsing(); + int itemCount = 0; + + Debug.Assert(cookieItems != null && nameItems != null, "An item array is null"); + + if (nameItems.Length > 0) { + object[] valueItems = new object[cookieItems.Length]; + NativeMethods.VARIANT var = new NativeMethods.VARIANT(); + int cookie; + + Debug.Assert(cookieItems.Length == nameItems.Length, "Got uneven names and cookies"); + + // for each name item, we ask the object for it's corresponding value. + // + for (int i = 0; i < nameItems.Length; i++) { + cookie = (int)cookieItems[i]; + if (nameItems[i] == null || !(nameItems[i] is string)) { + Debug.Fail("Bad IPerPropertyBrowsing item [" + i.ToString(CultureInfo.InvariantCulture) + "], name=" + (nameItems == null ? "(unknown)" : nameItems[i].ToString())); + continue; + } + var.vt = (short)NativeMethods.tagVT.VT_EMPTY; + int hr = ppb.GetPredefinedValue(target.Dispid, cookie, var); + if (hr == NativeMethods.S_OK && var.vt != (short)NativeMethods.tagVT.VT_EMPTY) { + valueItems[i] = var.ToObject(); + } + var.Clear(); + itemCount++; + } + + // pass this data down to the base Com2Enum object... + if (itemCount > 0) { + string[] strings = new string[itemCount]; + Array.Copy(nameItems, 0, strings, 0, itemCount); + base.PopulateArrays(strings, valueItems); + } + } + } + catch (Exception ex) { + Debug.Fail("Failed to build IPerPropertyBrowsing editor. " + ex.GetType().Name + ", " + ex.Message); + } + } + + internal void RefreshArrays( OleStrCAMarshaler names, Int32CAMarshaler values) { + this.nameMarshaller = names; + this.valueMarshaller = values; + this.arraysFetched = false; + } + +#if false + // FxCop: Currently not used + private string GetDisplayString(int dispid, ref bool success) { + NativeMethods.IPerPropertyBrowsing ppb = (NativeMethods.IPerPropertyBrowsing)owner.GetPerPropertyBrowsing(); + string[] strVal = new string[1]; + int hr = ppb.GetDisplayString(dispid, strVal); + if (hr == NativeMethods.S_OK) { + success = (strVal[0] != null); + //Debug.Assert(success, "IPerPropertyBrowsing::GetDisplayString returned NULL and S_OK -- this is not a valid state. This component does not property implement IPerPropertyBrowsing. (component class=" + TypeDescriptor.GetClassName(ppb) + ")"); + return strVal[0]; + } + else { + success = false; + } + return null; + } +#endif + + protected override void PopulateArrays(string[] names, object[] values) { + // we call base.PopulateArrays directly when we actually want to do this. + } + + public override object FromString(string s) { + EnsureArrays(); + return base.FromString(s); + } + + public override string ToString(object v) { + EnsureArrays(); + return base.ToString(v); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BaseCollection.cs b/WindowsForms/Managed/System/WinForms/BaseCollection.cs new file mode 100644 index 000000000..a37673058 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BaseCollection.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + + using System; + using System.ComponentModel; + using System.Collections; + using ArrayList = System.Collections.ArrayList; + + /// + /// + /// Provides the base functionality for creating collections. + /// + public class BaseCollection : MarshalByRefObject, ICollection { + + //================================================== + // the ICollection methods + //================================================== + /// + /// + /// Gets the total number of elements in a collection. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual int Count { + get { + return List.Count; + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array ar, int index) { + List.CopyTo(ar, index); + } + + /// + /// + /// Gets an IEnumerator for the collection. + /// + public IEnumerator GetEnumerator() { + return List.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) + ] + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsSynchronized { + get { + // so the user will know that it has to lock this object + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public object SyncRoot { + get { + return this; + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual ArrayList List { + get { + return null; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindToObject.cs b/WindowsForms/Managed/System/WinForms/BindToObject.cs new file mode 100644 index 000000000..da46c5aea --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindToObject.cs @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections; + + internal class BindToObject { + private PropertyDescriptor fieldInfo; + private BindingMemberInfo dataMember; + private Object dataSource; + private BindingManagerBase bindingManager; + private Binding owner; + private string errorText = string.Empty; + + private bool dataSourceInitialized = false; + private bool waitingOnDataSource = false; + + private void PropValueChanged(object sender, EventArgs e) { + if(this.bindingManager != null) { + this.bindingManager.OnCurrentChanged(EventArgs.Empty); + } + } + + private bool IsDataSourceInitialized { + get { + Debug.Assert(this.dataSource != null, "how can we determine if DataSource is initialized or not if we have no data source?"); + + if (this.dataSourceInitialized) { + return true; + } + + ISupportInitializeNotification ds = this.dataSource as ISupportInitializeNotification; + if (ds == null || ds.IsInitialized) { + this.dataSourceInitialized = true; + return true; + } + + // We have an ISupportInitializeNotification which was not initialized yet. + + // We already hooked up the Initialized event and the data source is not initialized yet. + if (this.waitingOnDataSource) { + return false; + } + + // Hook up the Initialized event. + ds.Initialized += new EventHandler(DataSource_Initialized); + this.waitingOnDataSource = true; + return false; + } + } + + internal BindToObject(Binding owner, Object dataSource, string dataMember) { + this.owner = owner; + this.dataSource = dataSource; + this.dataMember = new BindingMemberInfo(dataMember); + CheckBinding(); + } + + private void DataSource_Initialized(object sender, EventArgs e) { + Debug.Assert(sender == this.dataSource, "data source should not change"); + Debug.Assert(this.dataSource is ISupportInitializeNotification, "data source should not change on the BindToObject"); + Debug.Assert(this.waitingOnDataSource); + + ISupportInitializeNotification ds = this.dataSource as ISupportInitializeNotification; + // Unhook the Initialized event. + if (ds != null) { + ds.Initialized -= new EventHandler(DataSource_Initialized); + } + + // The wait is over: DataSource is initialized. + this.waitingOnDataSource = false; + this.dataSourceInitialized = true; + + // Rebind. + CheckBinding(); + } + + internal void SetBindingManagerBase(BindingManagerBase lManager) { + if (bindingManager == lManager) + { + return; + } + + // remove notification from the backEnd + if (bindingManager != null && fieldInfo != null && bindingManager.IsBinding && !(bindingManager is CurrencyManager)) { + fieldInfo.RemoveValueChanged(bindingManager.Current, new EventHandler(PropValueChanged)); + fieldInfo = null; + } + + this.bindingManager = lManager; + CheckBinding(); + } + + internal string DataErrorText + { + get + { + return this.errorText; + } + } + + // Returns any data error info on the data source for the bound data field in the current row + string GetErrorText(object value) { + + IDataErrorInfo errorInfo = value as IDataErrorInfo; + string text = string.Empty; + + if (errorInfo != null) { + // Get the row error if there is no DataMember + if (fieldInfo == null) { + text = errorInfo.Error; + } + // Get the column error if there is a DataMember. + // The DataTable uses its own Locale to lookup column names . + // So passing the DataMember from the BindingField could cause problems. + // Pass the name from the PropertyDescriptor that the DataTable gave us. + // (If there is no fieldInfo, data binding would have failed already ) + else { + text = errorInfo[fieldInfo.Name]; + } + } + + return text ?? string.Empty; + } + + internal Object GetValue() { + object obj = bindingManager.Current; + + // Update IDataErrorInfo text: it's ok to get this now because we're going to need + // this as part of the BindingCompleteEventArgs anyway. + this.errorText = GetErrorText(obj); + + if (fieldInfo != null) { + obj = fieldInfo.GetValue(obj); + } + + return obj; + } + + internal Type BindToType { + get { + if (dataMember.BindingField.Length == 0) { + // if we are bound to a list w/o any properties, then + // take the type from the BindingManager + Type type = this.bindingManager.BindType; + if (typeof(Array).IsAssignableFrom(type)) + type = type.GetElementType(); + return type; + } + else + return fieldInfo == null ? null : fieldInfo.PropertyType; + } + } + + internal void SetValue(Object value) { + object obj = null; + + if (fieldInfo != null) { + obj = bindingManager.Current; + if (obj is IEditableObject) + ((IEditableObject) obj).BeginEdit(); + //(bug 112947) .. checkif the Column is readonly while pushing data... + if (!fieldInfo.IsReadOnly) { + fieldInfo.SetValue(obj, value); + } + + } + else { + CurrencyManager cm = bindingManager as CurrencyManager; + if (cm != null) + { + cm[cm.Position] = value; + obj = value; + } + } + + // Update IDataErrorInfo text. + this.errorText = GetErrorText(obj); + } + + internal BindingMemberInfo BindingMemberInfo { + get { + return this.dataMember; + } + } + + internal Object DataSource { + get { + return dataSource; + } + } + + internal PropertyDescriptor FieldInfo { + get { + return fieldInfo; + } + } + + internal BindingManagerBase BindingManagerBase { + get { + return this.bindingManager; + } + } + + internal void CheckBinding() { + + // At design time, don't check anything. + // + if (owner != null && + owner.BindableComponent != null && + owner.ControlAtDesignTime()) { + + return; + } + + // force Column to throw if it's currently a bad column. + //DataColumn tempColumn = this.Column; + + // remove propertyChangedNotification when this binding is deleted + if (this.owner.BindingManagerBase != null && + this.fieldInfo != null && + this.owner.BindingManagerBase.IsBinding && + !(this.owner.BindingManagerBase is CurrencyManager)) { + + fieldInfo.RemoveValueChanged(owner.BindingManagerBase.Current, new EventHandler(PropValueChanged)); + } + + if (owner != null && + owner.BindingManagerBase != null && + owner.BindableComponent != null && + owner.ComponentCreated && + this.IsDataSourceInitialized) { + + string dataField = dataMember.BindingField; + + fieldInfo = owner.BindingManagerBase.GetItemProperties().Find(dataField, true); + if (owner.BindingManagerBase.DataSource != null && fieldInfo == null && dataField.Length > 0) { + throw new ArgumentException(SR.GetString(SR.ListBindingBindField, dataField), "dataMember"); + } + + // Do not add propertyChange notification if + // the fieldInfo is null + // + // we add an event handler to the dataSource in the BindingManagerBase because + // if the binding is of the form (Control, ControlProperty, DataSource, Property1.Property2.Property3) + // then we want to get notification from Current.Property1.Property2 and not from DataSource + // when we get the backEnd notification we push the new value into the Control's property + // + if (fieldInfo != null && + owner.BindingManagerBase.IsBinding && + !(this.owner.BindingManagerBase is CurrencyManager)) { + + fieldInfo.AddValueChanged(this.owner.BindingManagerBase.Current, new EventHandler(PropValueChanged)); + } + } + else { + fieldInfo = null; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Binding.cs b/WindowsForms/Managed/System/WinForms/Binding.cs new file mode 100644 index 000000000..ad13d2ca0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Binding.cs @@ -0,0 +1,1175 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections; + using System.Globalization; + using System.Security.Permissions; + using System.Security; + + /// + /// + /// + /// Represents a simple binding of a value in a list + /// and the property of a control. + /// + /// + [TypeConverterAttribute(typeof(ListBindingConverter))] + public class Binding { + + // the two collection owners that this binding belongs to. + private IBindableComponent control; + private BindingManagerBase bindingManagerBase; + + private BindToObject bindToObject = null; + + private string propertyName = ""; + + private PropertyDescriptor propInfo; + private PropertyDescriptor propIsNullInfo; + private EventDescriptor validateInfo; + private TypeConverter propInfoConverter; + + private bool formattingEnabled = false; + + private bool bound = false; + private bool modified = false; + + //Recursion guards + private bool inSetPropValue = false; + private bool inPushOrPull = false; + private bool inOnBindingComplete = false; + + // formatting stuff + private string formatString = String.Empty; + private IFormatProvider formatInfo = null; + private object nullValue = null; + private object dsNullValue = Formatter.GetDefaultDataSourceNullValue(null); + private bool dsNullValueSet; + private ConvertEventHandler onParse = null; + private ConvertEventHandler onFormat = null; + + // binding stuff + private ControlUpdateMode controlUpdateMode = ControlUpdateMode.OnPropertyChanged; + private DataSourceUpdateMode dataSourceUpdateMode = DataSourceUpdateMode.OnValidation; + private BindingCompleteEventHandler onComplete = null; + + /// + /// + /// Initializes a new instance of the class + /// that binds a property on the owning control to a property on a data source. + /// + public Binding(string propertyName, Object dataSource, string dataMember) : this(propertyName, dataSource, dataMember, false, 0, null, String.Empty, null) { + } + + /// + public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled) : this(propertyName, dataSource, dataMember, formattingEnabled, 0, null, String.Empty, null) { + } + + /// + public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, String.Empty, null) { + } + + /// + public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, String.Empty, null) { + } + + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'formatString' is an appropriate name, since its a string passed to the Format method + public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null) { + } + + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] // By design (no-one should be subclassing this class) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'formatString' is an appropriate name, since its a string passed to the Format method + public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo) { + this.bindToObject = new BindToObject(this, dataSource, dataMember); + + this.propertyName = propertyName; + this.formattingEnabled = formattingEnabled; + this.formatString = formatString; + this.nullValue = nullValue; + this.formatInfo = formatInfo; + this.formattingEnabled = formattingEnabled; + this.dataSourceUpdateMode = dataSourceUpdateMode; + + CheckBinding(); + } + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + private Binding() { + } + + internal BindToObject BindToObject { + get { + return this.bindToObject; + } + } + + /// + /// + /// [To be supplied.] + /// + public object DataSource { + get { + return this.bindToObject.DataSource; + } + } + + /// + /// + /// [To be supplied.] + /// + public BindingMemberInfo BindingMemberInfo { + get { + return this.bindToObject.BindingMemberInfo; + } + } + + /// + /// + /// + /// Gets the control to which the binding belongs. + /// + /// + [ + DefaultValue(null) + ] + public IBindableComponent BindableComponent { + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + return this.control; + } + } + + /// + /// + /// + /// Gets the control to which the binding belongs. + /// + /// + [ + DefaultValue(null) + ] + public Control Control { + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + return this.control as Control; + } + } + + // Is the binadable component in a 'created' (ready-to-use) state? For controls, + // this depends on whether the window handle has been created yet. For everything + // else, we'll assume they are always in a created state. + internal static bool IsComponentCreated(IBindableComponent component) { + Control ctrl = (component as Control); + + if (ctrl != null) { + return ctrl.Created; + } + else { + return true; + } + } + + // Instance-specific property equivalent to the static method above + internal bool ComponentCreated { + get { + return IsComponentCreated(this.control); + } + } + + private void FormLoaded(object sender, EventArgs e) { + Debug.Assert(sender == control, "which other control can send us the Load event?"); + // update the binding + CheckBinding(); + } + + internal void SetBindableComponent(IBindableComponent value) { + if (this.control != value) { + IBindableComponent oldTarget = control; + BindTarget(false); + this.control = value; + BindTarget(true); + try { + CheckBinding(); + } + catch { + BindTarget(false); + control = oldTarget; + BindTarget(true); + throw; + } + + // We are essentially doing to the listManager what we were doing to the + // BindToObject: bind only when the control is created and it has a BindingContext + BindingContext.UpdateBinding((control != null && IsComponentCreated(control) ? control.BindingContext: null), this); + Form form = value as Form; + if (form != null) { + form.Load += new EventHandler(FormLoaded); + } + } + } + + /// + /// + /// + /// Gets a value indicating whether the binding is active. + /// + /// + public bool IsBinding { + get { + return bound; + } + } + + /// + /// + /// + /// Gets the + /// of this binding that allows enumeration of a set of + /// bindings. + /// + /// + public BindingManagerBase BindingManagerBase{ + get { + return bindingManagerBase; + } + } + + internal void SetListManager(BindingManagerBase bindingManagerBase) { + if (this.bindingManagerBase is CurrencyManager) { + ((CurrencyManager)this.bindingManagerBase).MetaDataChanged -= new EventHandler(binding_MetaDataChanged); + } + + this.bindingManagerBase = bindingManagerBase; + + if (this.bindingManagerBase is CurrencyManager ) { + ((CurrencyManager)this.bindingManagerBase).MetaDataChanged += new EventHandler(binding_MetaDataChanged); + } + + this.BindToObject.SetBindingManagerBase(bindingManagerBase); + CheckBinding(); + } + + /// + /// + /// + /// Gets or sets the property on the control to bind to. + /// + /// + [DefaultValue("")] + public string PropertyName { + get { + return propertyName; + } + } + + /// + /// + /// [To be supplied.] + /// + public event BindingCompleteEventHandler BindingComplete { + add { + onComplete += value; + } + remove { + onComplete -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event ConvertEventHandler Parse { + add { + onParse += value; + } + remove { + onParse -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event ConvertEventHandler Format { + add { + onFormat += value; + } + remove { + onFormat -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [DefaultValue(false)] + public bool FormattingEnabled { + + // A note about FormattingEnabled: This flag was introduced in Whidbey, to enable new + // formatting features. However, it is also used to trigger other new Whidbey binding + // behavior not related to formatting (such as error handling). This preserves Everett + // legacy behavior for old bindings (where FormattingEnabled = false). + + get { + return formattingEnabled; + } + set { + if (formattingEnabled != value) { + formattingEnabled = value; + if (IsBinding) { + PushData(); + } + } + } + } + + /// + [DefaultValue(null)] + public IFormatProvider FormatInfo { + get { + return this.formatInfo; + } + set { + if (formatInfo != value) { + this.formatInfo = value; + if (IsBinding) { + PushData(); + } + } + } + } + + /// + public string FormatString { + get { + return this.formatString; + } + set { + if (value == null) + value = String.Empty; + if (!value.Equals(formatString)) { + this.formatString = value; + if (IsBinding) { + PushData(); + } + } + } + } + + /// + public object NullValue { + get { + return this.nullValue; + } + set { + // Try to compare logical values, not object references... + if (!Object.Equals(nullValue, value)) { + this.nullValue = value; + + // If data member is currently DBNull, force update of bound + // control property so that it displays the new NullValue + // + if (IsBinding && Formatter.IsNullData(bindToObject.GetValue(), this.dsNullValue)) { + PushData(); + } + } + } + } + + /// + public object DataSourceNullValue { + get { + return this.dsNullValue; + } + set { + // Try to compare logical values, not object references... + if (!Object.Equals(this.dsNullValue, value)) { + + // Save old Value + object oldValue = this.dsNullValue; + + // Set value + this.dsNullValue = value; + + this.dsNullValueSet = true; + + // If control's property is capable of displaying a special value for DBNull, + // and the DBNull status of data source's property has changed, force the + // control property to refresh itself from the data source property. + // + if (IsBinding) { + object dsValue = bindToObject.GetValue(); + + // Check previous DataSourceNullValue for null + if (Formatter.IsNullData(dsValue, oldValue)) { + // Update DataSource Value to new DataSourceNullValue + WriteValue(); + } + + // Check current DataSourceNullValue + if (Formatter.IsNullData(dsValue, value)) { + // Update Control because the DataSource is now null + ReadValue(); + } + } + } + } + } + + /// + [DefaultValue(ControlUpdateMode.OnPropertyChanged)] + public ControlUpdateMode ControlUpdateMode { + get { + return this.controlUpdateMode; + } + set { + if (this.controlUpdateMode != value) { + this.controlUpdateMode = value; + + // Refresh the control from the data source, to reflect the new update mode + if (IsBinding) { + PushData(); + } + } + } + } + + /// + [DefaultValue(DataSourceUpdateMode.OnValidation)] + public DataSourceUpdateMode DataSourceUpdateMode { + get { + return this.dataSourceUpdateMode; + } + set { + if (this.dataSourceUpdateMode != value) { + this.dataSourceUpdateMode = value; + } + } + } + + private void BindTarget(bool bind) { + if (bind) { + if (IsBinding) { + if (propInfo != null && control != null) { + EventHandler handler = new EventHandler(this.Target_PropertyChanged); + propInfo.AddValueChanged(control, handler); + } + if (validateInfo != null) { + CancelEventHandler handler = new CancelEventHandler(this.Target_Validate); + validateInfo.AddEventHandler(control, handler); + } + } + } + else { + if (propInfo != null && control != null) { + EventHandler handler = new EventHandler(this.Target_PropertyChanged); + propInfo.RemoveValueChanged(control, handler); + } + if (validateInfo != null) { + CancelEventHandler handler = new CancelEventHandler(this.Target_Validate); + validateInfo.RemoveEventHandler(control, handler); + } + } + } + + private void binding_MetaDataChanged(object sender, EventArgs e) { + Debug.Assert(sender == this.bindingManagerBase, "we should only receive notification from our binding manager base"); + CheckBinding(); + } + + private void CheckBinding() { + this.bindToObject.CheckBinding(); + + if (control != null && propertyName.Length > 0) { + control.DataBindings.CheckDuplicates(this); + + Type controlClass = control.GetType(); + + // Check Properties + string propertyNameIsNull = propertyName + "IsNull"; + Type propType = null; + PropertyDescriptor tempPropInfo = null; + PropertyDescriptor tempPropIsNullInfo = null; + PropertyDescriptorCollection propInfos; + + // If the control is being inherited, then get the properties for + // the control's type rather than for the control itself. Getting + // properties for the control will merge the control's properties with + // those of its designer. Normally we want that, but for + // inherited controls we don't because an inherited control should + // "act" like a runtime control. + // + InheritanceAttribute attr = (InheritanceAttribute)TypeDescriptor.GetAttributes(control)[typeof(InheritanceAttribute)]; + if (attr != null && attr.InheritanceLevel != InheritanceLevel.NotInherited) { + propInfos = TypeDescriptor.GetProperties(controlClass); + } + else { + propInfos = TypeDescriptor.GetProperties(control); + } + + for (int i = 0; i < propInfos.Count; i++) { + if(tempPropInfo==null && String.Equals (propInfos[i].Name, propertyName, StringComparison.OrdinalIgnoreCase)) { + tempPropInfo = propInfos[i]; + if (tempPropIsNullInfo != null) + break; + } + if(tempPropIsNullInfo == null && String.Equals (propInfos[i].Name, propertyNameIsNull, StringComparison.OrdinalIgnoreCase)) { + tempPropIsNullInfo = propInfos[i]; + if (tempPropInfo != null) + break; + } + } + + if (tempPropInfo == null) { + throw new ArgumentException(SR.GetString(SR.ListBindingBindProperty, propertyName), "PropertyName"); + } + if (tempPropInfo.IsReadOnly && this.controlUpdateMode != ControlUpdateMode.Never) { + throw new ArgumentException(SR.GetString(SR.ListBindingBindPropertyReadOnly, propertyName), "PropertyName"); + } + + propInfo = tempPropInfo; + propType = propInfo.PropertyType; + propInfoConverter = propInfo.Converter; + + if (tempPropIsNullInfo != null && tempPropIsNullInfo.PropertyType == typeof(bool) && !tempPropIsNullInfo.IsReadOnly) + propIsNullInfo = tempPropIsNullInfo; + + // Check events + EventDescriptor tempValidateInfo = null; + string validateName = "Validating"; + EventDescriptorCollection eventInfos = TypeDescriptor.GetEvents(control); + for (int i = 0; i < eventInfos.Count; i++) { + if(tempValidateInfo==null && String.Equals (eventInfos[i].Name, validateName, StringComparison.OrdinalIgnoreCase)) { + tempValidateInfo = eventInfos[i]; + break; + } + } + validateInfo = tempValidateInfo; + } + else { + propInfo = null; + validateInfo = null; + } + + // go see if we become bound now. + UpdateIsBinding(); + } + + internal bool ControlAtDesignTime() { + IComponent comp = (this.control as IComponent); + if (comp == null) + return false; + + ISite site = comp.Site; + if (site == null) + return false; + + return site.DesignMode; + } + + private object GetDataSourceNullValue(Type type) { + return this.dsNullValueSet ? this.dsNullValue : Formatter.GetDefaultDataSourceNullValue(type); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] // Perfectly acceptible when dealing with PropertyDescriptors + private Object GetPropValue() { + bool isNull = false; + if (propIsNullInfo != null) { + isNull = (bool) propIsNullInfo.GetValue(control); + } + Object value; + if (isNull) { + value = DataSourceNullValue; + } + else { + value = propInfo.GetValue(control); + // bug 92443: the code before was changing value to DBNull if the value + // was the empty string. we can't do this, because we need to format the value + // in the property in the control and then push it back into the control. + if (value == null) { + value = DataSourceNullValue; + } + } + return value; + } + + private BindingCompleteEventArgs CreateBindingCompleteEventArgs(BindingCompleteContext context, Exception ex) { + bool cancel = false; + string errorText = String.Empty; + BindingCompleteState state = BindingCompleteState.Success; + + if (ex != null) { + // If an exception was provided, report that + errorText = ex.Message; + state = BindingCompleteState.Exception; + cancel = true; + } + else { + // If data error info on data source for this binding, report that + errorText = this.BindToObject.DataErrorText; + + // We should not cancel with an IDataErrorInfo error - we didn't in Everett + if (!String.IsNullOrEmpty(errorText)) { + state = BindingCompleteState.DataError; + } + } + + return new BindingCompleteEventArgs(this, state, context, errorText, ex, cancel); + } + + /// + protected virtual void OnBindingComplete(BindingCompleteEventArgs e) { + // This recursion guard will only be in effect if FormattingEnabled because this method + // is only called if formatting is enabled. + if (!inOnBindingComplete) { + try { + inOnBindingComplete = true; + if (onComplete != null) { + onComplete(this, e); + } + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + + // Fix for VSWhidbey#303304. BindingComplete event is intended primarily as an "FYI" event with support for cancellation. + // User code should not be throwing exceptions from this event as a way to signal new error conditions (they should use + // things like the Format or Parse events for that). Exceptions thrown here can mess up currency manager behavior big time. + // For now, eat any non-critical exceptions and instead just cancel the current push/pull operation. + e.Cancel = true; + } + finally { + inOnBindingComplete = false; + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnParse(ConvertEventArgs cevent) { + if (onParse != null) { + onParse(this, cevent); + } + + if (!formattingEnabled) { + if (!(cevent.Value is System.DBNull) && cevent.Value != null && cevent.DesiredType != null && !cevent.DesiredType.IsInstanceOfType(cevent.Value) && (cevent.Value is IConvertible)) { + cevent.Value = Convert.ChangeType(cevent.Value, cevent.DesiredType, CultureInfo.CurrentCulture); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnFormat(ConvertEventArgs cevent) { + if (onFormat!= null) { + onFormat(this, cevent); + } + if (!formattingEnabled) { + if (!(cevent.Value is System.DBNull) && cevent.DesiredType != null && !cevent.DesiredType.IsInstanceOfType(cevent.Value) && (cevent.Value is IConvertible)) { + cevent.Value = Convert.ChangeType(cevent.Value, cevent.DesiredType, CultureInfo.CurrentCulture); + } + } + } + + /// + /// [To be supplied.] + /// + private object ParseObject(object value) { + Type type = this.bindToObject.BindToType; + + if (formattingEnabled) { + // ------------------------------- + // Behavior for Whidbey and beyond + // ------------------------------- + + // Fire the Parse event so that user code gets a chance to supply the parsed value for us + ConvertEventArgs e = new ConvertEventArgs(value, type); + OnParse(e); + + object newValue = e.Value; + + if (!object.Equals(value, newValue)) { + // If event handler replaced formatted value with parsed value, use that + return newValue; + } + else { + // Otherwise parse the formatted value ourselves + TypeConverter fieldInfoConverter = null; + if (bindToObject.FieldInfo != null) { + fieldInfoConverter = bindToObject.FieldInfo.Converter; + } + return Formatter.ParseObject(value, type, (value == null ? propInfo.PropertyType : value.GetType()), fieldInfoConverter, propInfoConverter, formatInfo, nullValue, GetDataSourceNullValue(type)); + } + + } else { + // ---------------------------- + // Behavior for RTM and Everett [DO NOT MODIFY!] + // ---------------------------- + + ConvertEventArgs e = new ConvertEventArgs(value, type); + // first try: use the OnParse event + OnParse(e); + // bug 75825: if the user choose to push a null in to the back end, then we should push it like it is. + if (e.Value != null && (e.Value.GetType().IsSubclassOf(type) || e.Value.GetType() == type || e.Value is System.DBNull)) + return e.Value; + // second try: use the TypeConverter + TypeConverter typeConverter = TypeDescriptor.GetConverter(value != null ? value.GetType() : typeof(Object)); + if (typeConverter != null && typeConverter.CanConvertTo(type)) { + return typeConverter.ConvertTo(value, type); + } + // last try: use Convert.ToType + if (value is IConvertible) { + object ret = Convert.ChangeType(value, type, CultureInfo.CurrentCulture); + if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type)) + return ret; + } + // time to fail: (RTM/Everett just returns null, whereas Whidbey throws an exception) + return null; + } + } + + /// + /// [To be supplied.] + /// + private object FormatObject(object value) { + // We will not format the object when the control is in design time. + // This is because if we bind a boolean property on a control to a + // row that is full of DBNulls then we cause problems in the shell. + if (ControlAtDesignTime()) + return value; + + Type type = propInfo.PropertyType; + + if (formattingEnabled) { + // ------------------------------- + // Behavior for Whidbey and beyond + // ------------------------------- + + // Fire the Format event so that user code gets a chance to supply the formatted value for us + ConvertEventArgs e = new ConvertEventArgs(value, type); + OnFormat(e); + + if (e.Value != value) { + // If event handler replaced parsed value with formatted value, use that + return e.Value; + } + else { + // Otherwise format the parsed value ourselves + TypeConverter fieldInfoConverter = null; + if (bindToObject.FieldInfo != null) { + fieldInfoConverter = bindToObject.FieldInfo.Converter; + } + return Formatter.FormatObject(value, type, fieldInfoConverter, propInfoConverter, formatString, formatInfo, nullValue, dsNullValue); + } + + } else { + // ---------------------------- + // Behavior for RTM and Everett [DO NOT MODIFY!] + // ---------------------------- + + // first try: use the Format event + ConvertEventArgs e = new ConvertEventArgs(value, type); + OnFormat(e); + object ret = e.Value; + + // Approved breaking-change behavior between RTM and Everett: Fire the Format event even if the control property is of type + // Object (RTM used to skip the event for properties of this type). NOTE: This change contains a bug (fixed in the new + // Whidbey logic above); Everett always returns the *original* object in this case, ignoring any attempt by the event handler + // to replace this with a different object. + if (type == typeof(object)) + return value; + + // stop now if we have a value of a compatible type + if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type)) + return ret; + // second try: use type converter for the desiredType + TypeConverter typeConverter = TypeDescriptor.GetConverter(value != null ? value.GetType() : typeof(Object)); + if (typeConverter != null && typeConverter.CanConvertTo(type)) { + ret = typeConverter.ConvertTo(value, type); + return ret; + } + // last try: use Convert.ChangeType + if (value is IConvertible) { + ret = Convert.ChangeType(value, type, CultureInfo.CurrentCulture); + if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type)) + return ret; + } + + // time to fail: + throw new FormatException(SR.GetString(SR.ListBindingFormatFailed)); + } + } + + // + // PullData() + // + // Pulls data from control property into data source. Returns bool indicating whether caller + // should cancel the higher level operation. Raises a BindingComplete event regardless of + // success or failure. + // + // When the user leaves the control, it will raise a Validating event, calling the Binding.Target_Validate + // method, which in turn calls PullData. PullData is also called by the binding manager when pulling data + // from all bounds properties in one go. + // + + internal bool PullData() { + return PullData(true, false); + } + + internal bool PullData(bool reformat) { + return PullData(reformat, false); + } + + internal bool PullData(bool reformat, bool force) { + //Don't update the control if the control update mode is never. + if (ControlUpdateMode == ControlUpdateMode.Never) { + reformat = false; + } + bool parseFailed = false; + object parsedValue = null; + Exception lastException = null; + + // Check whether binding has been suspended or is simply not possible right now + if (!IsBinding) { + return false; + } + + // If caller is not FORCING us to pull, determine whether we want to pull right now... + if (!force) { + // If control property supports change events, only pull if the value has been changed since + // the last update (ie. its dirty). For properties that do NOT support change events, we cannot + // track the dirty state, so we just have to pull all the time. + if (propInfo.SupportsChangeEvents && !modified) { + return false; + } + + // Don't pull if the update mode is 'Never' (ie. read-only binding) + if (DataSourceUpdateMode == DataSourceUpdateMode.Never) { + return false; + } + } + + // Re-entrancy check between push and pull (new for Whidbey - requires FormattingEnabled) + if (inPushOrPull && formattingEnabled) { + return false; + } + + inPushOrPull = true; + + // Get the value from the bound control property + Object value = GetPropValue(); + + // Attempt to parse the property value into a format suitable for the data source + try { + parsedValue = ParseObject(value); + } + catch (Exception ex) { + lastException = ex; + + // ...pre-Whidbey behavior was to eat parsing exceptions. This behavior is preserved. + } + + try { + // If parse failed, reset control property value back to original data source value. + // An exception always indicates a parsing failure. A parsed value of null only indicates + // a parsing failure when following pre-Whidbey behavior (ie. FormattingEnabled=False) since + // in Whidbey we now support writing null back to the data source (eg. for business objects). + if (lastException != null || (!FormattingEnabled && parsedValue == null)) { + parseFailed = true; + parsedValue = this.bindToObject.GetValue(); + } + + // Format the parsed value to be re-displayed in the control + if (reformat) { + if (FormattingEnabled && parseFailed) { + // New behavior for Whidbey (ie. requires FormattingEnabled=true). If parsing + // fails, do NOT push the original data source value back into the control. + // This blows away the invalid value before the user gets a chance to see + // what needs correcting, which was the Everett behavior. + } + else { + object formattedObject = FormatObject(parsedValue); + + // New behavior for Whidbey (Bug#194609). Do not push the re-formatted + // value into the control if it is identical to the current formatted + // value unless we're forced to (thereby avoiding unnecessary property sets). + if (force || !FormattingEnabled || !Object.Equals(formattedObject, value)) { + SetPropValue(formattedObject); + } + } + } + + // Put the value into the data model + if (!parseFailed) { + this.bindToObject.SetValue(parsedValue); + } + } + catch (Exception ex) { + lastException = ex; + + // This try/catch is new for Whidbey. To preserve Everett behavior, re-throw the + // exception unless this binding has formatting enabled (new Whidbey feature). + if (!FormattingEnabled) { + throw; + } + } + finally { + inPushOrPull = false; + } + + if (FormattingEnabled) { + // Whidbey... + + // Raise the BindingComplete event, giving listeners a chance to process any + // errors that occured and decide whether the operation should be cancelled. + BindingCompleteEventArgs args = CreateBindingCompleteEventArgs(BindingCompleteContext.DataSourceUpdate, lastException); + OnBindingComplete(args); + + // If the operation completed successfully (and was not cancelled), we can clear the dirty flag + // on this binding because we know the value in the control was valid and has been accepted by + // the data source. But if the operation failed (or was cancelled), we must leave the dirty flag + // alone, so that the control's value will continue to be re-validated and re-pulled later. + if (args.BindingCompleteState == BindingCompleteState.Success && args.Cancel == false) + modified = false; + + return args.Cancel; + } + else { + // Everett... + + // Do not emit BindingComplete events, or allow the operation to be cancelled. + // If we get this far, treat the operation as successful and clear the dirty flag. + modified = false; + return false; + } + } + + // + // PushData() + // + // Pushes data from data source into control property. Returns bool indicating whether caller + // should cancel the higher level operation. Raises a BindingComplete event regardless of + // success or failure. + // + + internal bool PushData() { + return PushData(false); + } + + internal bool PushData(bool force) { + Object dataSourceValue = null; + Exception lastException = null; + + // Don't push if update mode is 'Never' (unless caller is FORCING us to push) + if (!force && ControlUpdateMode == ControlUpdateMode.Never) { + return false; + } + + // Re-entrancy check between push and pull (new for Whidbey - requires FormattingEnabled) + if (inPushOrPull && formattingEnabled) { + return false; + } + + inPushOrPull = true; + + try { + if (IsBinding) { + dataSourceValue = bindToObject.GetValue(); + object controlValue = FormatObject(dataSourceValue); + SetPropValue(controlValue); + modified = false; + } + else { + SetPropValue(null); + } + } + catch (Exception ex) { + lastException = ex; + + // This try/catch is new for Whidbey. To preserve Everett behavior, re-throw the + // exception unless this binding has formatting enabled (new Whidbey feature). + if (!FormattingEnabled) { + throw; + } + } + finally { + inPushOrPull = false; + } + + if (FormattingEnabled) { + // Whidbey... + + // Raise the BindingComplete event, giving listeners a chance to process any errors that occured, and decide + // whether the operation should be cancelled. But don't emit the event if we didn't actually update the control. + BindingCompleteEventArgs args = CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, lastException); + OnBindingComplete(args); + + return args.Cancel; + } + else { + // Everett... + + // Do not emit BindingComplete events, or allow the operation to be cancelled. + return false; + } + } + + /// + /// + /// Reads current value from data source, and sends this to the control. + /// + public void ReadValue() { + PushData(/*force:*/ true); + } + + /// + /// + /// Takes current value from control, and writes this out to the data source. + /// + public void WriteValue() { + // PullData has a guard for ControlUpdateMode == Never. + + PullData(/*reformat:*/ true, /*force:*/ true); + } + + private void SetPropValue(Object value) { + // we will not pull the data from the back end into the control + // when the control is in design time. this is because if we bind a boolean property on a control + // to a row that is full of DBNulls then we cause problems in the shell. + if (ControlAtDesignTime()) + return; + + inSetPropValue = true; + + try { + bool isNull = value == null || Formatter.IsNullData(value, DataSourceNullValue); + if (isNull) { + if (propIsNullInfo != null) { + propIsNullInfo.SetValue(control, true); + } + else { + if (propInfo.PropertyType == typeof(object)) { + propInfo.SetValue(control, DataSourceNullValue); + } + else { + propInfo.SetValue(control, null); + } + + } + } + else { + propInfo.SetValue(control, value); + } + } + finally { + inSetPropValue = false; + } + } + + private bool ShouldSerializeFormatString() { + return formatString != null && formatString.Length > 0; + + } + + private bool ShouldSerializeNullValue() { + return nullValue != null; + } + + private bool ShouldSerializeDataSourceNullValue() { + return this.dsNullValueSet && this.dsNullValue != Formatter.GetDefaultDataSourceNullValue(null); + } + + private void Target_PropertyChanged(Object sender, EventArgs e) { + if (inSetPropValue) + return; + + if (IsBinding) { + //dataSource.BeginEdit(); + + modified = true; + + // If required, update data source every time control property changes. + // NOTE: We need modified=true to be set both before pulling data + // (so that pull will work) and afterwards (so that validation will + // still occur later on). + if (DataSourceUpdateMode == DataSourceUpdateMode.OnPropertyChanged) { + PullData(false); // false = don't reformat property (bad user experience!!) + modified = true; + } + } + } + + // Event handler for the Control.Validating event on the control that we are bound to. + // + // If value in control has changed, we want to send that value back up to the data source + // when the control undergoes validation (eg. on loss of focus). If an error occurs, we + // will set e.Cancel=true to make validation fail and force focus to remain on the control. + // + // NOTE: If no error occurs, we MUST leave e.Cancel alone, to respect any value put in there + // by event handlers high up the event chain. + // + private void Target_Validate(Object sender, CancelEventArgs e) { + try { + if (PullData(true)) { + e.Cancel = true; + } + } + catch { + e.Cancel = true; + } + } + + internal bool IsBindable { + get { + return (control != null && propertyName.Length > 0 && + bindToObject.DataSource != null && bindingManagerBase != null); + } + } + + internal void UpdateIsBinding() { + bool newBound = IsBindable && ComponentCreated && bindingManagerBase.IsBinding; + if (bound != newBound) { + bound = newBound; + BindTarget(newBound); + if (bound) { + if (controlUpdateMode == ControlUpdateMode.Never) { + PullData(false, true); //Don't reformat, force pull + } + else { + PushData(); + } + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindingCompleteContext.cs b/WindowsForms/Managed/System/WinForms/BindingCompleteContext.cs new file mode 100644 index 000000000..c1969e831 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingCompleteContext.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + /// + /// Indicates the direction of a binding operation. + /// + public enum BindingCompleteContext { + + /// + /// + /// Control value is being updated from data source value. + /// + ControlUpdate = 0, + + /// + /// + /// Data source value is being updated from control value. + /// + DataSourceUpdate = 1, + } +} \ No newline at end of file diff --git a/WindowsForms/Managed/System/WinForms/BindingCompleteEventArgs.cs b/WindowsForms/Managed/System/WinForms/BindingCompleteEventArgs.cs new file mode 100644 index 000000000..3124024a3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingCompleteEventArgs.cs @@ -0,0 +1,116 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// Provides information about a Binding Completed event. + /// + public class BindingCompleteEventArgs : CancelEventArgs { + private Binding binding; + private BindingCompleteState state; + private BindingCompleteContext context; + private string errorText; + private Exception exception; + + /// + /// + /// Constructor for BindingCompleteEventArgs. + /// + public BindingCompleteEventArgs(Binding binding, + BindingCompleteState state, + BindingCompleteContext context, + string errorText, + Exception exception, + bool cancel) : base(cancel) { + this.binding = binding; + this.state = state; + this.context = context; + this.errorText = (errorText == null) ? string.Empty : errorText; + this.exception = exception; + } + + /// + /// + /// Constructor for BindingCompleteEventArgs. + /// + public BindingCompleteEventArgs(Binding binding, + BindingCompleteState state, + BindingCompleteContext context, + string errorText, + Exception exception) : this(binding, state, context, errorText, exception, true) { + } + + /// + /// + /// Constructor for BindingCompleteEventArgs. + /// + public BindingCompleteEventArgs(Binding binding, + BindingCompleteState state, + BindingCompleteContext context, + string errorText) : this(binding, state, context, errorText, null, true) { + } + + /// + /// + /// Constructor for BindingCompleteEventArgs. + /// + public BindingCompleteEventArgs(Binding binding, + BindingCompleteState state, + BindingCompleteContext context) : this(binding, state, context, string.Empty, null, false) { + } + + /// + /// + /// + public Binding Binding { + get { + return this.binding; + } + } + + /// + /// + /// + public BindingCompleteState BindingCompleteState { + get { + return this.state; + } + } + + /// + /// + /// + public BindingCompleteContext BindingCompleteContext { + get { + return this.context; + } + } + + /// + /// + /// + public string ErrorText { + get { + return this.errorText; + } + } + + /// + /// + /// + public Exception Exception + { + get { + return this.exception; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindingCompleteEventHandler.cs b/WindowsForms/Managed/System/WinForms/BindingCompleteEventHandler.cs new file mode 100644 index 000000000..daf479a7b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingCompleteEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// Represents a method that will handle the Binding Complete event. + /// + public delegate void BindingCompleteEventHandler(object sender, BindingCompleteEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/BindingCompleteState.cs b/WindowsForms/Managed/System/WinForms/BindingCompleteState.cs new file mode 100644 index 000000000..3ab84f90c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingCompleteState.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + /// + /// Indicates the result of a completed binding operation. + /// + public enum BindingCompleteState { + + /// + /// + /// Binding operation completed successfully. + /// + Success = 0, + + /// + /// + /// Binding operation failed with a data error. + /// + DataError = 1, + + /// + /// + /// Binding operation failed with an exception. + /// + Exception = 2, + } +} \ No newline at end of file diff --git a/WindowsForms/Managed/System/WinForms/BindingContext.cs b/WindowsForms/Managed/System/WinForms/BindingContext.cs new file mode 100644 index 000000000..90819e5a9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingContext.cs @@ -0,0 +1,449 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.ComponentModel; + using System.Collections; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Manages the collection of System.Windows.Forms.BindingManagerBase + /// objects for a Win Form. + /// + [DefaultEvent("CollectionChanged")] + public class BindingContext : ICollection { + + private Hashtable listManagers; + + /// + /// + /// + /// + /// Gets the total number of System.Windows.Forms.BindingManagerBases + /// objects. + /// + /// + int ICollection.Count { + get { + ScrubWeakRefs(); + return listManagers.Count; + } + } + + /// + /// + /// + /// + /// Copies the elements of the collection into a specified array, starting + /// at the collection index. + /// + /// + void ICollection.CopyTo(Array ar, int index) + { + IntSecurity.UnmanagedCode.Demand(); + ScrubWeakRefs(); + listManagers.CopyTo(ar, index); + } + + /// + /// + /// + /// + /// Gets an enumerator for the collection. + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + IntSecurity.UnmanagedCode.Demand(); + ScrubWeakRefs(); + return listManagers.GetEnumerator(); + } + + /// + /// + /// + /// + /// Gets a value indicating whether the collection is read-only. + /// + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// + /// + /// Gets a value indicating whether the collection is synchronized. + /// + /// + bool ICollection.IsSynchronized { + get { + // so the user will know that it has to lock this object + return false; + } + } + + /// + /// + /// + /// Gets an object to use for synchronization (thread safety). + /// + object ICollection.SyncRoot { + get { + return null; + } + } + + + /// + /// + /// Initializes a new instance of the System.Windows.Forms.BindingContext class. + /// + public BindingContext() { + listManagers = new Hashtable(); + } + + /// + /// + /// + /// Gets the System.Windows.Forms.BindingManagerBase + /// associated with the specified data source. + /// + /// + public BindingManagerBase this[object dataSource] { + get { + return this[dataSource, ""]; + } + } + + /// + /// + /// Gets the System.Windows.Forms.BindingManagerBase associated with the specified data source and + /// data member. + /// + public BindingManagerBase this[object dataSource, string dataMember] { + get { + return EnsureListManager(dataSource, dataMember); + } + } + + /// + /// + /// Adds the listManager to the collection. An ArgumentNullException is thrown if this listManager + /// is null. An exception is thrown if a listManager to the same target and Property as an existing listManager or + /// if the listManager's column isn't a valid column given this DataSource.Table's schema. + /// Fires the CollectionChangedEvent. + /// + internal protected void Add(object dataSource, BindingManagerBase listManager) { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + AddCore(dataSource, listManager); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataSource)); + } + + /// + /// + /// + protected virtual void AddCore(object dataSource, BindingManagerBase listManager) { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + if (dataSource == null) + throw new ArgumentNullException("dataSource"); + if (listManager == null) + throw new ArgumentNullException("listManager"); + + // listManagers[dataSource] = listManager; + listManagers[GetKey(dataSource, "")] = new WeakReference(listManager, false); + } + + + /// + /// + /// + /// Occurs when the collection has changed. + /// + /// + [SRDescription(SR.collectionChangedEventDescr), EditorBrowsable(EditorBrowsableState.Never), Browsable(false)] + public event CollectionChangeEventHandler CollectionChanged { + /* !!THIS EVENT IS OBSOLETE AND UNUSED!! */ + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + add { + throw new NotImplementedException(); + } + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + remove { + } + } + + /// + /// + /// Clears the collection of any bindings. + /// Fires the CollectionChangedEvent. + /// + internal protected void Clear() { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + ClearCore(); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// + /// Clears the collection. + /// + /// + protected virtual void ClearCore() { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + listManagers.Clear(); + } + + /// + /// + /// Gets a value indicating whether the System.Windows.Forms.BindingContext + /// contains the specified + /// data source. + /// + public bool Contains(object dataSource) { + return Contains(dataSource, ""); + } + + /// + /// + /// Gets a value indicating whether the System.Windows.Forms.BindingContext + /// contains the specified data source and data member. + /// + public bool Contains(object dataSource, string dataMember) { + return listManagers.ContainsKey(GetKey(dataSource, dataMember)); + } + + internal HashKey GetKey(object dataSource, string dataMember) { + return new HashKey(dataSource, dataMember); + } + + /// + /// + /// + /// + // + internal class HashKey { + WeakReference wRef; + int dataSourceHashCode; + string dataMember; + + internal HashKey(object dataSource, string dataMember) { + if (dataSource == null) + throw new ArgumentNullException("dataSource"); + if (dataMember == null) + dataMember = ""; + // The dataMember should be case insensitive. + // so convert the dataMember to lower case + // + this.wRef = new WeakReference(dataSource, false); + this.dataSourceHashCode = dataSource.GetHashCode(); + this.dataMember = dataMember.ToLower(CultureInfo.InvariantCulture); + } + + /// + /// + /// + /// + public override int GetHashCode() { + return dataSourceHashCode * dataMember.GetHashCode(); + } + + /// + /// + /// + /// + public override bool Equals(object target) { + if (target is HashKey) { + HashKey keyTarget = (HashKey)target; + return wRef.Target == keyTarget.wRef.Target && dataMember == keyTarget.dataMember; + } + return false; + } + } + + /// + /// + /// + /// This method is called whenever the collection changes. Overriders + /// of this method should call the base implementation of this method. + /// NOTE: This shipped in Everett, so we need to keep it, but we don't do + /// anything here. + /// + protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent) { + } + + /// + /// + /// Removes the given listManager from the collection. + /// An ArgumentNullException is thrown if this listManager is null. An ArgumentException is thrown + /// if this listManager doesn't belong to this collection. + /// The CollectionChanged event is fired if it succeeds. + /// + internal protected void Remove(object dataSource) { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + RemoveCore(dataSource); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataSource)); + } + + /// + /// + /// + /// + protected virtual void RemoveCore(object dataSource) { + /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */ + listManagers.Remove(GetKey(dataSource, "")); + } + + /// + /// + /// Create a suitable binding manager for the specified dataSource/dataMember combination. + /// - If one has already been created and cached by this BindingContext, return that instead. + /// - If the data source is an ICurrencyManagerProvider, just delegate to the data source. + /// + internal BindingManagerBase EnsureListManager(object dataSource, string dataMember) { + BindingManagerBase bindingManagerBase = null; + + if (dataMember == null) + dataMember = ""; + + // Check whether data source wants to provide its own binding managers + // (but fall through to old logic if it fails to provide us with one) + // + if (dataSource is ICurrencyManagerProvider) { + bindingManagerBase = (dataSource as ICurrencyManagerProvider).GetRelatedCurrencyManager(dataMember); + + if (bindingManagerBase != null) { + return bindingManagerBase; + } + } + + // Check for previously created binding manager + // + HashKey key = GetKey(dataSource, dataMember); + WeakReference wRef; + wRef = listManagers[key] as WeakReference; + if (wRef != null) + bindingManagerBase = (BindingManagerBase) wRef.Target; + if (bindingManagerBase != null) { + return bindingManagerBase; + } + + if (dataMember.Length == 0) { + // No data member specified, so create binding manager directly on the data source + // + if (dataSource is IList || dataSource is IListSource) { + // IListSource so we can bind the dataGrid to a table and a dataSet + bindingManagerBase = new CurrencyManager(dataSource); + } + else { + // Otherwise assume simple property binding + bindingManagerBase = new PropertyManager(dataSource); + } + } + else { + // Data member specified, so get data source's binding manager, and hook a 'related' binding manager to it + // + int lastDot = dataMember.LastIndexOf("."); + string dataPath = (lastDot == -1) ? "" : dataMember.Substring(0, lastDot); + string dataField = dataMember.Substring(lastDot + 1); + + BindingManagerBase formerManager = EnsureListManager(dataSource, dataPath); + + PropertyDescriptor prop = formerManager.GetItemProperties().Find(dataField, true); + if (prop == null) + throw new ArgumentException(SR.GetString(SR.RelatedListManagerChild, dataField)); + + if (typeof(IList).IsAssignableFrom(prop.PropertyType)) + bindingManagerBase = new RelatedCurrencyManager(formerManager, dataField); + else + bindingManagerBase = new RelatedPropertyManager(formerManager, dataField); + } + + // if wRef == null, then it is the first time we want this bindingManagerBase: so add it + // if wRef != null, then the bindingManagerBase was GC'ed at some point: keep the old wRef and change its target + if (wRef == null) + listManagers.Add(key, new WeakReference(bindingManagerBase, false)); + else + wRef.Target = bindingManagerBase; + + IntSecurity.UnmanagedCode.Demand(); + ScrubWeakRefs(); + // Return the final binding manager + return bindingManagerBase; + } + + // may throw + private static void CheckPropertyBindingCycles(BindingContext newBindingContext, Binding propBinding) { + if (newBindingContext == null || propBinding == null) + return; + if (newBindingContext.Contains(propBinding.BindableComponent, "")) { + // this way we do not add a bindingManagerBase to the + // bindingContext if there isn't one already + BindingManagerBase bindingManagerBase = newBindingContext.EnsureListManager(propBinding.BindableComponent, ""); + for (int i = 0; i < bindingManagerBase.Bindings.Count; i++) { + Binding binding = bindingManagerBase.Bindings[i]; + if (binding.DataSource == propBinding.BindableComponent) { + if (propBinding.BindToObject.BindingMemberInfo.BindingMember.Equals(binding.PropertyName)) + throw new ArgumentException(SR.GetString(SR.DataBindingCycle, binding.PropertyName), "propBinding"); + } else if (propBinding.BindToObject.BindingManagerBase is PropertyManager) + CheckPropertyBindingCycles(newBindingContext, binding); + } + } + } + + private void ScrubWeakRefs() { + ArrayList cleanupList = null; + foreach (DictionaryEntry de in listManagers) { + WeakReference wRef = (WeakReference) de.Value; + if (wRef.Target == null) { + if (cleanupList == null) { + cleanupList = new ArrayList(); + } + cleanupList.Add(de.Key); + } + } + + if (cleanupList != null) { + foreach (object o in cleanupList) { + listManagers.Remove(o); + } + } + } + + /// + /// + /// Associates a Binding with a different BindingContext. Intended for use by components that support + /// IBindableComponent, to update their Bindings when the value of IBindableComponent.BindingContext + /// is changed. + /// + public static void UpdateBinding(BindingContext newBindingContext, Binding binding) { + BindingManagerBase oldManager = binding.BindingManagerBase; + if (oldManager != null) { + oldManager.Bindings.Remove(binding); + } + + if (newBindingContext != null) { + // we need to first check for cycles before adding this binding to the collection + // of bindings. + if (binding.BindToObject.BindingManagerBase is PropertyManager) + CheckPropertyBindingCycles(newBindingContext, binding); + + BindToObject bindTo = binding.BindToObject; + BindingManagerBase newManager = newBindingContext.EnsureListManager(bindTo.DataSource, bindTo.BindingMemberInfo.BindingPath); + newManager.Bindings.Add(binding); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindingMAnagerBase.cs b/WindowsForms/Managed/System/WinForms/BindingMAnagerBase.cs new file mode 100644 index 000000000..9d7046c22 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingMAnagerBase.cs @@ -0,0 +1,447 @@ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + using System.ComponentModel; + using System.Collections; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// [To be supplied.] + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors")] // Shipped in Everett + public abstract class BindingManagerBase + { + private BindingsCollection bindings; + private bool pullingData = false; + + /// + /// + /// [To be supplied.] + /// + protected EventHandler onCurrentChangedHandler; + + /// + /// + /// [To be supplied.] + /// + protected EventHandler onPositionChangedHandler; + + // Hook BindingComplete events on all owned Binding objects, and propagate those events through our own BindingComplete event + private BindingCompleteEventHandler onBindingCompleteHandler = null; + + // same deal about the new currentItemChanged event + internal EventHandler onCurrentItemChangedHandler; + + // Event handler for the DataError event + internal BindingManagerDataErrorEventHandler onDataErrorHandler; + + /// + /// + /// [To be supplied.] + /// + public BindingsCollection Bindings { + get { + if (bindings == null) { + bindings = new ListManagerBindingsCollection(this); + + // Hook collection change events on collection, so we can hook or unhook the BindingComplete events on individual bindings + bindings.CollectionChanging += new CollectionChangeEventHandler(OnBindingsCollectionChanging); + bindings.CollectionChanged += new CollectionChangeEventHandler(OnBindingsCollectionChanged); + } + + return bindings; + } + } + + /// + /// + /// [To be supplied.] + /// + internal protected void OnBindingComplete(BindingCompleteEventArgs args) { + if (onBindingCompleteHandler != null) { + onBindingCompleteHandler(this, args); + } + } + + /// + /// + /// [To be supplied.] + /// + internal protected abstract void OnCurrentChanged(EventArgs e); + + /// + /// + /// [To be supplied.] + /// + internal protected abstract void OnCurrentItemChanged(EventArgs e); + + /// + /// + /// [To be supplied.] + /// + internal protected void OnDataError(Exception e) { + if (onDataErrorHandler != null) { + onDataErrorHandler(this, new BindingManagerDataErrorEventArgs(e)); + } + } + + /// + /// + /// [To be supplied.] + /// + public abstract Object Current { + get; + } + + internal abstract void SetDataSource(Object dataSource); + + /// + /// + /// [To be supplied.] + /// + public BindingManagerBase() { } + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not call SetDataSource + // it would be a breaking change. + ] + internal BindingManagerBase(Object dataSource) { + this.SetDataSource(dataSource); + } + + internal abstract Type BindType{ + get; + } + + internal abstract PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors); + + /// + /// + /// [To be supplied.] + /// + public virtual PropertyDescriptorCollection GetItemProperties() { + return GetItemProperties(null); + } + + /// + protected internal virtual PropertyDescriptorCollection GetItemProperties(ArrayList dataSources, ArrayList listAccessors) { + IList list = null; + if (this is CurrencyManager) { + list = ((CurrencyManager)this).List; + } + if (list is ITypedList) { + PropertyDescriptor[] properties = new PropertyDescriptor[listAccessors.Count]; + listAccessors.CopyTo(properties, 0); + return ((ITypedList)list).GetItemProperties(properties); + } + return this.GetItemProperties(this.BindType, 0, dataSources, listAccessors); + } + + // listType is the type of the top list in the list.list.list.list reference + // offset is how far we are in the listAccessors + // listAccessors is the list of accessors (duh) + // + /// + protected virtual PropertyDescriptorCollection GetItemProperties(Type listType, int offset, ArrayList dataSources, ArrayList listAccessors) { + if (listAccessors.Count < offset) + return null; + + if (listAccessors.Count == offset) { + if (typeof(IList).IsAssignableFrom(listType)) { + System.Reflection.PropertyInfo[] itemProps = listType.GetProperties(); + // PropertyDescriptorCollection itemProps = TypeDescriptor.GetProperties(listType); + for (int i = 0; i < itemProps.Length; i ++) { + if ("Item".Equals(itemProps[i].Name) && itemProps[i].PropertyType != typeof(object)) + return TypeDescriptor.GetProperties(itemProps[i].PropertyType, new Attribute[] {new BrowsableAttribute(true)}); + } + // return the properties on the type of the first element in the list + IList list = dataSources[offset - 1] as IList; + if (list != null && list.Count > 0) + return TypeDescriptor.GetProperties(list[0]); + } else { + return TypeDescriptor.GetProperties(listType); + } + return null; + } + + System.Reflection.PropertyInfo[] props = listType.GetProperties(); + // PropertyDescriptorCollection props = TypeDescriptor.GetProperties(listType); + if (typeof(IList).IsAssignableFrom(listType)) { + PropertyDescriptorCollection itemProps = null; + for (int i = 0; i < props.Length; i++) { + if ("Item".Equals(props[i].Name) && props[i].PropertyType != typeof(object)) { + // get all the properties that are not marked as Browsable(false) + // + itemProps = TypeDescriptor.GetProperties(props[i].PropertyType, new Attribute[] {new BrowsableAttribute(true)}); + } + } + + if (itemProps == null) { + // use the properties on the type of the first element in the list + // if offset == 0, then this means that the first dataSource did not have a strongly typed Item property. + // the dataSources are added only for relatedCurrencyManagers, so in this particular case + // we need to use the dataSource in the currencyManager. See ASURT 83035. + IList list; + if (offset == 0) + list = this.DataSource as IList; + else + list = dataSources[offset - 1] as IList; + if (list != null && list.Count > 0) { + itemProps = TypeDescriptor.GetProperties(list[0]); + } + } + + if (itemProps != null) { + for (int j=0; j + /// + /// [To be supplied.] + /// + public event BindingCompleteEventHandler BindingComplete { + add { + onBindingCompleteHandler += value; + } + remove { + onBindingCompleteHandler -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler CurrentChanged { + add { + onCurrentChangedHandler += value; + } + remove { + onCurrentChangedHandler -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler CurrentItemChanged { + add { + onCurrentItemChangedHandler += value; + } + remove { + onCurrentItemChangedHandler -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event BindingManagerDataErrorEventHandler DataError { + add { + onDataErrorHandler += value; + } + remove { + onDataErrorHandler -= value; + } + } + + internal abstract String GetListName(); + /// + /// + /// [To be supplied.] + /// + public abstract void CancelCurrentEdit(); + /// + /// + /// [To be supplied.] + /// + public abstract void EndCurrentEdit(); + + /// + /// + /// [To be supplied.] + /// + public abstract void AddNew(); + /// + /// + /// [To be supplied.] + /// + public abstract void RemoveAt(int index); + + /// + /// + /// [To be supplied.] + /// + public abstract int Position{get; set;} + + /// + /// + /// [To be supplied.] + /// + public event EventHandler PositionChanged { + add { + this.onPositionChangedHandler += value; + } + remove { + this.onPositionChangedHandler -= value; + } + } + /// + /// + /// [To be supplied.] + /// + protected abstract void UpdateIsBinding(); + + /// + /// + /// [To be supplied.] + /// + protected internal abstract String GetListName(ArrayList listAccessors); + + /// + public abstract void SuspendBinding(); + + /// + public abstract void ResumeBinding(); + + /// + /// + /// [To be supplied.] + /// + protected void PullData() { + bool success; + PullData(out success); + } + + /// + /// + /// [To be supplied.] + /// + internal void PullData(out bool success) { + success = true; + pullingData = true; + + try { + UpdateIsBinding(); + + int numLinks = Bindings.Count; + for (int i = 0; i < numLinks; i++) { + if (Bindings[i].PullData()) { + success = false; + } + } + } + finally { + pullingData = false; + } + } + + /// + /// + /// [To be supplied.] + /// + protected void PushData() { + bool success; + PushData(out success); + } + + /// + /// + /// [To be supplied.] + /// + internal void PushData(out bool success) { + success = true; + + if (pullingData) + return; + + UpdateIsBinding(); + + int numLinks = Bindings.Count; + for (int i = 0; i < numLinks; i++) { + if (Bindings[i].PushData()) { + success = false; + } + } + } + + internal abstract object DataSource { + get; + } + + internal abstract bool IsBinding { + get; + } + + /// + /// + /// + public bool IsBindingSuspended { + get { + return !IsBinding; + } + } + + /// + public abstract int Count { + get; + } + + // BindingComplete events on individual Bindings are propagated up through the BindingComplete event on + // the owning BindingManagerBase. To do this, we have to track changes to the bindings collection, adding + // or removing handlers on items in the collection as appropriate. + // + // For the Add and Remove cases, we hook the collection 'changed' event, and add or remove handler for + // specific binding. + // + // For the Refresh case, we hook both the 'changing' and 'changed' events, removing handlers for all + // items that were in the collection before the change, then adding handlers for whatever items are + // in the collection after the change. + // + private void OnBindingsCollectionChanged(object sender, CollectionChangeEventArgs e) { + Binding b = e.Element as Binding; + + switch (e.Action) { + case CollectionChangeAction.Add: + b.BindingComplete += new BindingCompleteEventHandler(Binding_BindingComplete); + break; + case CollectionChangeAction.Remove: + b.BindingComplete -= new BindingCompleteEventHandler(Binding_BindingComplete); + break; + case CollectionChangeAction.Refresh: + foreach (Binding bi in bindings) { + bi.BindingComplete += new BindingCompleteEventHandler(Binding_BindingComplete); + } + break; + } + } + + private void OnBindingsCollectionChanging(object sender, CollectionChangeEventArgs e) { + if (e.Action == CollectionChangeAction.Refresh) { + foreach (Binding bi in bindings) { + bi.BindingComplete -= new BindingCompleteEventHandler(Binding_BindingComplete); + } + } + } + + internal void Binding_BindingComplete(object sender, BindingCompleteEventArgs args) { + this.OnBindingComplete(args); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventArgs.cs b/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventArgs.cs new file mode 100644 index 000000000..6c97d4c3f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventArgs.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + public class BindingManagerDataErrorEventArgs : EventArgs { + private Exception exception; + + /// + /// + /// + public BindingManagerDataErrorEventArgs(Exception exception) { + this.exception = exception; + } + + /// + /// + /// + public Exception Exception + { + get { + return this.exception; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventHandler.cs b/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventHandler.cs new file mode 100644 index 000000000..5a132880a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingManagerDataErrorEventHandler.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + public delegate void BindingManagerDataErrorEventHandler(object sender, BindingManagerDataErrorEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/BindingMemberInfo.cs b/WindowsForms/Managed/System/WinForms/BindingMemberInfo.cs new file mode 100644 index 000000000..4777335f5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingMemberInfo.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Globalization; + + /// + /// + /// [To be supplied.] + /// + public struct BindingMemberInfo { + private string dataList; + private string dataField; + + /// + /// + /// [To be supplied.] + /// + public BindingMemberInfo(string dataMember) { + if (dataMember == null) + dataMember = ""; + + int lastDot = dataMember.LastIndexOf("."); + if (lastDot != -1) { + dataList = dataMember.Substring(0,lastDot); + dataField = dataMember.Substring(lastDot+1); + } + else { + dataList = ""; + dataField = dataMember; + } + } + + /// + /// + /// [To be supplied.] + /// + public string BindingPath { + get { + return (dataList != null ? dataList : ""); + } + } + + /// + /// + /// [To be supplied.] + /// + public string BindingField { + get { + return (dataField != null ? dataField : ""); + } + } + + /// + /// + /// [To be supplied.] + /// + public string BindingMember { + get { + return (BindingPath.Length > 0 ? BindingPath + "." + BindingField : BindingField); + } + } + + /// + /// + /// [To be supplied.] + /// + public override bool Equals(object otherObject) { + if (otherObject is BindingMemberInfo) { + BindingMemberInfo otherMember = (BindingMemberInfo) otherObject; + return (String.Equals(this.BindingMember, otherMember.BindingMember, StringComparison.OrdinalIgnoreCase)); + } + return false; + } + + public static bool operator ==(BindingMemberInfo a, BindingMemberInfo b) { + return a.Equals(b); + } + + public static bool operator !=(BindingMemberInfo a, BindingMemberInfo b) { + return !a.Equals(b); + } + + /// + /// + /// [To be supplied.] + /// + public override int GetHashCode() { + return base.GetHashCode(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/BindingNavigator.cs b/WindowsForms/Managed/System/WinForms/BindingNavigator.cs new file mode 100644 index 000000000..1a6acb61c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingNavigator.cs @@ -0,0 +1,854 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Globalization; + using System.Runtime.InteropServices; + + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("BindingSource"), + DefaultEvent("RefreshItems"), + Designer("System.Windows.Forms.Design.BindingNavigatorDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionBindingNavigator) + ] + /// + /// + /// Creates an empty BindingNavigator tool strip. + /// Call AddStandardItems() to add standard tool strip items. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public BindingNavigator() : this(false) { + } + + /// + /// + /// Creates a BindingNavigator strip containing standard items, bound to the specified BindingSource. + /// + public BindingNavigator(BindingSource bindingSource) : this(true) { + BindingSource = bindingSource; + } + + /// + /// + /// Creates an empty BindingNavigator tool strip, and adds the strip to the specified container. + /// Call AddStandardItems() to add standard tool strip items. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public BindingNavigator(IContainer container) : this(false) { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + /// + /// + /// Creates a BindingNavigator strip, optionally containing a set of standard tool strip items. + /// + public BindingNavigator(bool addStandardItems) { + if (addStandardItems) { + AddStandardItems(); + } + } + + /// + /// + /// ISupportInitialize support. Disables updates to tool strip items during initialization. + /// + public void BeginInit() { + initializing = true; + } + + /// + /// + /// ISupportInitialize support. Enables updates to tool strip items after initialization. + /// + public void EndInit() { + initializing = false; + RefreshItemsInternal(); + } + + /// + /// Unhooks the BindingNavigator from the BindingSource. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + BindingSource = null; // ...unwires from events of any prior BindingSource + } + + base.Dispose(disposing); + } + + /// + /// + /// Adds standard set of tool strip items to a BindingNavigator tool strip, for basic + /// navigation operations such as Move First, Move Next, Add New, etc. + /// + /// This method is called by the Windows Form Designer when a new BindingNavigator is + /// added to a Form. When creating a BindingNavigator programmatically, this method + /// must be called explicitly. + /// + /// Override this method in derived classes to define additional or alternative standard items. + /// To ensure optimal design-time support for your derived class, make sure each item has a + /// meaningful value in its Name property. At design time, this will be used to generate a unique + /// name for the corresponding member variable. The item's Name property will then be updated + /// to match the name given to the member variable. + /// + /// Note: This method does NOT remove any previous items from the strip, or suspend + /// layout while items are being added. Those are responsibilities of the caller. + /// + public virtual void AddStandardItems() { + + // + // Create items + // + + MoveFirstItem = new System.Windows.Forms.ToolStripButton(); + MovePreviousItem = new System.Windows.Forms.ToolStripButton(); + MoveNextItem = new System.Windows.Forms.ToolStripButton(); + MoveLastItem = new System.Windows.Forms.ToolStripButton(); + PositionItem = new System.Windows.Forms.ToolStripTextBox(); + CountItem = new System.Windows.Forms.ToolStripLabel(); + AddNewItem = new System.Windows.Forms.ToolStripButton(); + DeleteItem = new System.Windows.Forms.ToolStripButton(); + + ToolStripSeparator separator1 = new System.Windows.Forms.ToolStripSeparator(); + ToolStripSeparator separator2 = new System.Windows.Forms.ToolStripSeparator(); + ToolStripSeparator separator3 = new System.Windows.Forms.ToolStripSeparator(); + + // + // Set up strings + // + + // Hacky workaround for VSWhidbey 407243 + // Default to lowercase for null name, because C# dev is more likely to create controls programmatically than + // vb dev. + char ch = string.IsNullOrEmpty(Name) || char.IsLower(Name[0]) ? 'b' : 'B'; + + MoveFirstItem.Name = ch + "indingNavigatorMoveFirstItem"; + MovePreviousItem.Name = ch + "indingNavigatorMovePreviousItem"; + MoveNextItem.Name = ch + "indingNavigatorMoveNextItem"; + MoveLastItem.Name = ch + "indingNavigatorMoveLastItem"; + PositionItem.Name = ch + "indingNavigatorPositionItem"; + CountItem.Name = ch + "indingNavigatorCountItem"; + AddNewItem.Name = ch + "indingNavigatorAddNewItem"; + DeleteItem.Name = ch + "indingNavigatorDeleteItem"; + separator1.Name = ch + "indingNavigatorSeparator"; + separator2.Name = ch + "indingNavigatorSeparator"; + separator3.Name = ch + "indingNavigatorSeparator"; + + MoveFirstItem.Text = SR.GetString(SR.BindingNavigatorMoveFirstItemText); + MovePreviousItem.Text = SR.GetString(SR.BindingNavigatorMovePreviousItemText); + MoveNextItem.Text = SR.GetString(SR.BindingNavigatorMoveNextItemText); + MoveLastItem.Text = SR.GetString(SR.BindingNavigatorMoveLastItemText); + AddNewItem.Text = SR.GetString(SR.BindingNavigatorAddNewItemText); + DeleteItem.Text = SR.GetString(SR.BindingNavigatorDeleteItemText); + + CountItem.ToolTipText = SR.GetString(SR.BindingNavigatorCountItemTip); + PositionItem.ToolTipText = SR.GetString(SR.BindingNavigatorPositionItemTip); + CountItem.AutoToolTip = false; + PositionItem.AutoToolTip = false; + + PositionItem.AccessibleName = SR.GetString(SR.BindingNavigatorPositionAccessibleName); + // + // Set up images + // + + Bitmap moveFirstImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.MoveFirst.bmp"); + Bitmap movePreviousImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.MovePrevious.bmp"); + Bitmap moveNextImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.MoveNext.bmp"); + Bitmap moveLastImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.MoveLast.bmp"); + Bitmap addNewImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.AddNew.bmp"); + Bitmap deleteImage = new Bitmap(typeof(BindingNavigator), "BindingNavigator.Delete.bmp"); + + moveFirstImage.MakeTransparent(System.Drawing.Color.Magenta); + movePreviousImage.MakeTransparent(System.Drawing.Color.Magenta); + moveNextImage.MakeTransparent(System.Drawing.Color.Magenta); + moveLastImage.MakeTransparent(System.Drawing.Color.Magenta); + addNewImage.MakeTransparent(System.Drawing.Color.Magenta); + deleteImage.MakeTransparent(System.Drawing.Color.Magenta); + + MoveFirstItem.Image = moveFirstImage; + MovePreviousItem.Image = movePreviousImage; + MoveNextItem.Image = moveNextImage; + MoveLastItem.Image = moveLastImage; + AddNewItem.Image = addNewImage; + DeleteItem.Image = deleteImage; + + MoveFirstItem.RightToLeftAutoMirrorImage = true; + MovePreviousItem.RightToLeftAutoMirrorImage = true; + MoveNextItem.RightToLeftAutoMirrorImage = true; + MoveLastItem.RightToLeftAutoMirrorImage = true; + AddNewItem.RightToLeftAutoMirrorImage = true; + DeleteItem.RightToLeftAutoMirrorImage = true; + + MoveFirstItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + MovePreviousItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + MoveNextItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + MoveLastItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + AddNewItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + DeleteItem.DisplayStyle = ToolStripItemDisplayStyle.Image; + + // + // Set other random properties + // + PositionItem.AutoSize = false; + PositionItem.Width = 50; + + // + // Add items to strip + // + + Items.AddRange(new ToolStripItem[] { + MoveFirstItem, + MovePreviousItem, + separator1, + PositionItem, + CountItem, + separator2, + MoveNextItem, + MoveLastItem, + separator3, + AddNewItem, + DeleteItem, + }); + } + + /// + /// + /// The BindingSource who's list we are currently navigating, or null. + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.BindingNavigatorBindingSourcePropDescr), + TypeConverter(typeof(ReferenceConverter)), + SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly") + ] + public BindingSource BindingSource { + get { + return bindingSource; + } + + set { + WireUpBindingSource(ref bindingSource, value); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Move first' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorMoveFirstItemPropDescr) + ] + public ToolStripItem MoveFirstItem { + get { + if (moveFirstItem != null && moveFirstItem.IsDisposed) { + moveFirstItem = null; + } + return moveFirstItem; + } + + set { + WireUpButton(ref moveFirstItem, value, new EventHandler(OnMoveFirst)); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Move previous' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorMovePreviousItemPropDescr) + ] + public ToolStripItem MovePreviousItem { + get { + + if (movePreviousItem != null && movePreviousItem.IsDisposed) { + movePreviousItem = null; + } + + return movePreviousItem; + } + + set { + WireUpButton(ref movePreviousItem, value, new EventHandler(OnMovePrevious)); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Move next' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorMoveNextItemPropDescr) + ] + public ToolStripItem MoveNextItem { + get { + if (moveNextItem != null && moveNextItem.IsDisposed) { + moveNextItem = null; + } + return moveNextItem; + } + + set { + WireUpButton(ref moveNextItem, value, new EventHandler(OnMoveNext)); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Move last' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorMoveLastItemPropDescr) + ] + public ToolStripItem MoveLastItem { + get { + if (moveLastItem != null && moveLastItem.IsDisposed) { + moveLastItem = null; + } + return moveLastItem; + } + + set { + WireUpButton(ref moveLastItem, value, new EventHandler(OnMoveLast)); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Add new' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorAddNewItemPropDescr) + ] + public ToolStripItem AddNewItem { + get { + if (addNewItem != null && addNewItem.IsDisposed) { + addNewItem = null; + } + return addNewItem; + } + + set { + if (addNewItem != value && value != null) { + value.InternalEnabledChanged += new System.EventHandler(OnAddNewItemEnabledChanged); + addNewItemUserEnabled = value.Enabled; + } + WireUpButton(ref addNewItem, value, new EventHandler(OnAddNew)); + } + } + + /// + /// + /// The ToolStripItem that triggers the 'Delete' action, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorDeleteItemPropDescr) + ] + public ToolStripItem DeleteItem { + get { + if (deleteItem != null && deleteItem.IsDisposed) { + deleteItem = null; + } + return deleteItem; + } + + set { + if (deleteItem != value && value != null) { + value.InternalEnabledChanged += new System.EventHandler(OnDeleteItemEnabledChanged); + deleteItemUserEnabled = value.Enabled; + } + WireUpButton(ref deleteItem, value, new EventHandler(OnDelete)); + + } + } + + /// + /// + /// The ToolStripItem that displays the current position, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorPositionItemPropDescr) + ] + public ToolStripItem PositionItem { + get { + if (positionItem != null && positionItem.IsDisposed) { + positionItem = null; + } + return positionItem; + } + + set { + WireUpTextBox(ref positionItem, value, new KeyEventHandler(OnPositionKey), new EventHandler(OnPositionLostFocus)); + } + } + + /// + /// + /// The ToolStripItem that displays the total number of items, or null. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatItems), + SRDescription(SR.BindingNavigatorCountItemPropDescr) + ] + public ToolStripItem CountItem { + get { + if (countItem != null && countItem.IsDisposed) { + countItem = null; + } + return countItem; + } + + set { + WireUpLabel(ref countItem, value); + } + } + + /// + /// + /// Formatting to apply to count displayed in the CountItem tool strip item. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.BindingNavigatorCountItemFormatPropDescr) + ] + public String CountItemFormat { + get { + return countItemFormat; + } + + set { + if (countItemFormat != value) { + countItemFormat = value; + RefreshItemsInternal(); + } + } + } + + /// + /// + /// Event raised when the state of the tool strip items needs to be + /// refreshed to reflect the current state of the data. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.BindingNavigatorRefreshItemsEventDescr) + ] + public event EventHandler RefreshItems { + add { + onRefreshItems += value; + } + remove { + onRefreshItems -= value; + } + } + + /// + /// + /// Refreshes the state of the standard items to reflect the current state of the data. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void RefreshItemsCore() { + int count, position; + bool allowNew, allowRemove; + + // Get state info from the binding source (if any) + if (bindingSource == null) { + count = 0; + position = 0; + allowNew = false; + allowRemove = false; + } + else { + count = bindingSource.Count; + position = bindingSource.Position + 1; + allowNew = (bindingSource as IBindingList).AllowNew; + allowRemove = (bindingSource as IBindingList).AllowRemove; + } + + // Enable or disable items (except when in design mode) + if (!DesignMode) { + if (MoveFirstItem != null) moveFirstItem.Enabled = (position > 1); + if (MovePreviousItem != null) movePreviousItem.Enabled = (position > 1); + if (MoveNextItem != null) moveNextItem.Enabled = (position < count); + if (MoveLastItem != null) moveLastItem.Enabled = (position < count); + + if (AddNewItem != null) { + System.EventHandler handler = new System.EventHandler(OnAddNewItemEnabledChanged); + addNewItem.InternalEnabledChanged -= handler; + addNewItem.Enabled = (addNewItemUserEnabled && allowNew); + addNewItem.InternalEnabledChanged += handler; + } + + if (DeleteItem != null) { + System.EventHandler handler = new System.EventHandler(OnDeleteItemEnabledChanged); + deleteItem.InternalEnabledChanged -= handler; + deleteItem.Enabled = (deleteItemUserEnabled && allowRemove && count > 0); + deleteItem.InternalEnabledChanged += handler; + } + + if (PositionItem != null) positionItem.Enabled = (position > 0 && count > 0); + if (CountItem != null) countItem.Enabled = (count > 0); + } + + // Update current position indicator + if (positionItem != null) { + positionItem.Text = position.ToString(CultureInfo.CurrentCulture); + } + + // Update record count indicator + if (countItem != null) { + countItem.Text = DesignMode ? CountItemFormat : String.Format(CultureInfo.CurrentCulture, CountItemFormat, count); + } + } + + /// + /// + /// Called when the state of the tool strip items needs to be refreshed to reflect the current state of the data. + /// Calls to refresh the state of the standard items, then raises the RefreshItems event. + /// + protected virtual void OnRefreshItems() { + // Refresh all the standard items + RefreshItemsCore(); + + // Raise the public event + if (onRefreshItems != null) { + onRefreshItems(this, EventArgs.Empty); + } + } + + /// + /// + /// Triggers form validation. Used by the BindingNavigator's standard items when clicked. If a validation error occurs + /// on the form, focus remains on the active control and the standard item does not perform its standard click action. + /// Custom items may also use this method to trigger form validation and check for success before performing an action. + /// + public bool Validate() { + bool validatedControlAllowsFocusChange; + return this.ValidateActiveControl(out validatedControlAllowsFocusChange); + } + + /// + /// Accept new row position entered into PositionItem. + /// + private void AcceptNewPosition() { + // If no position item or binding source, do nothing + if (positionItem == null || bindingSource == null) { + return; + } + + // Default to old position, in case new position turns out to be garbage + int newPosition = bindingSource.Position; + + try { + // Read new position from item text (and subtract one!) + newPosition = Convert.ToInt32(positionItem.Text, CultureInfo.CurrentCulture) - 1; + } + catch (System.FormatException) { + // Ignore bad user input + } + catch (System.OverflowException) { + // Ignore bad user input + } + + // If user has managed to enter a valid number, that is not the same as the current position, try + // navigating to that position. Let the BindingSource validate the new position to keep it in range. + if (newPosition != bindingSource.Position) { + bindingSource.Position = newPosition; + } + + // Update state of all items to reflect final position. If the user entered a bad position, + // this will effectively reset the Position item back to showing the current position. + RefreshItemsInternal(); + } + + /// + /// Cancel new row position entered into PositionItem. + /// + private void CancelNewPosition() { + // Just refresh state of all items to reflect current position + // (causing position item's new value to get blasted away) + RefreshItemsInternal(); + } + + /// + /// Navigates to first item in BindingSource's list when the MoveFirstItem is clicked. + /// + private void OnMoveFirst(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.MoveFirst(); + RefreshItemsInternal(); + } + } + } + + /// + /// Navigates to previous item in BindingSource's list when the MovePreviousItem is clicked. + /// + private void OnMovePrevious(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.MovePrevious(); + RefreshItemsInternal(); + } + } + } + + /// + /// Navigates to next item in BindingSource's list when the MoveNextItem is clicked. + /// + private void OnMoveNext(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.MoveNext(); + RefreshItemsInternal(); + } + } + } + + /// + /// Navigates to last item in BindingSource's list when the MoveLastItem is clicked. + /// + private void OnMoveLast(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.MoveLast(); + RefreshItemsInternal(); + } + } + } + + /// + /// Adds new item to BindingSource's list when the AddNewItem is clicked. + /// + private void OnAddNew(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.AddNew(); + RefreshItemsInternal(); + } + } + } + + /// + /// Deletes current item from BindingSource's list when the DeleteItem is clicked. + /// + private void OnDelete(object sender, EventArgs e) { + if (Validate()) { + if (bindingSource != null) { + bindingSource.RemoveCurrent(); + RefreshItemsInternal(); + } + } + } + + /// + /// Navigates to specific item in BindingSource's list when a value is entered into the PositionItem. + /// + private void OnPositionKey(object sender, KeyEventArgs e) { + switch (e.KeyCode) { + case Keys.Enter: + AcceptNewPosition(); + break; + case Keys.Escape: + CancelNewPosition(); + break; + } + } + + /// + /// Navigates to specific item in BindingSource's list when a value is entered into the PositionItem. + /// + private void OnPositionLostFocus(object sender, EventArgs e) { + AcceptNewPosition(); + } + + /// + /// Refresh tool strip items when something changes in the BindingSource. + /// + private void OnBindingSourceStateChanged(object sender, EventArgs e) { + RefreshItemsInternal(); + } + + /// + /// Refresh tool strip items when something changes in the BindingSource's list. + /// + private void OnBindingSourceListChanged(object sender, ListChangedEventArgs e) { + RefreshItemsInternal(); + } + + /// + /// Refresh the state of the items when the state of the data changes. + /// + private void RefreshItemsInternal() { + // Block all updates during initialization + if (initializing) { + return; + } + + // Call method that updates the items (overridable) + OnRefreshItems(); + } + + private void ResetCountItemFormat() { + countItemFormat = SR.GetString(SR.BindingNavigatorCountItemFormat); + } + + private bool ShouldSerializeCountItemFormat() { + return countItemFormat != SR.GetString(SR.BindingNavigatorCountItemFormat); + } + + private void OnAddNewItemEnabledChanged(object sender, EventArgs e) { + if (AddNewItem != null) { + addNewItemUserEnabled = addNewItem.Enabled; + } + } + + private void OnDeleteItemEnabledChanged(object sender, EventArgs e) { + if (DeleteItem != null) { + deleteItemUserEnabled = deleteItem.Enabled; + } + } + + /// + /// Wire up some member variable to the specified button item, hooking events + /// on the new button and unhooking them from the previous button, if required. + /// + private void WireUpButton(ref ToolStripItem oldButton, ToolStripItem newButton, EventHandler clickHandler) { + if (oldButton == newButton) { + return; + } + + if (oldButton != null) { + oldButton.Click -= clickHandler; + } + + if (newButton != null) { + newButton.Click += clickHandler; + } + + oldButton = newButton; + RefreshItemsInternal(); + } + + /// + /// Wire up some member variable to the specified text box item, hooking events + /// on the new text box and unhooking them from the previous text box, if required. + /// + private void WireUpTextBox(ref ToolStripItem oldTextBox, ToolStripItem newTextBox, KeyEventHandler keyUpHandler, EventHandler lostFocusHandler) { + if (oldTextBox != newTextBox) { + ToolStripControlHost oldCtrl = oldTextBox as ToolStripControlHost; + ToolStripControlHost newCtrl = newTextBox as ToolStripControlHost; + + if (oldCtrl != null) { + oldCtrl.KeyUp -= keyUpHandler; + oldCtrl.LostFocus -= lostFocusHandler; + } + + if (newCtrl != null) { + newCtrl.KeyUp += keyUpHandler; + newCtrl.LostFocus += lostFocusHandler; + } + + oldTextBox = newTextBox; + RefreshItemsInternal(); + } + } + + /// + /// Wire up some member variable to the specified label item, hooking events + /// on the new label and unhooking them from the previous label, if required. + /// + private void WireUpLabel(ref ToolStripItem oldLabel, ToolStripItem newLabel) { + if (oldLabel != newLabel) { + oldLabel = newLabel; + RefreshItemsInternal(); + } + } + + /// + /// Wire up some member variable to the specified binding source, hooking events + /// on the new binding source and unhooking them from the previous one, if required. + /// + private void WireUpBindingSource(ref BindingSource oldBindingSource, BindingSource newBindingSource) { + if (oldBindingSource != newBindingSource) { + if (oldBindingSource != null) { + oldBindingSource.PositionChanged -= new EventHandler(OnBindingSourceStateChanged); + oldBindingSource.CurrentChanged -= new EventHandler(OnBindingSourceStateChanged); + oldBindingSource.CurrentItemChanged -= new EventHandler(OnBindingSourceStateChanged); + oldBindingSource.DataSourceChanged -= new EventHandler(OnBindingSourceStateChanged); + oldBindingSource.DataMemberChanged -= new EventHandler(OnBindingSourceStateChanged); + oldBindingSource.ListChanged -= new ListChangedEventHandler(OnBindingSourceListChanged); + } + + if (newBindingSource != null) { + newBindingSource.PositionChanged += new EventHandler(OnBindingSourceStateChanged); + newBindingSource.CurrentChanged += new EventHandler(OnBindingSourceStateChanged); + newBindingSource.CurrentItemChanged += new EventHandler(OnBindingSourceStateChanged); + newBindingSource.DataSourceChanged += new EventHandler(OnBindingSourceStateChanged); + newBindingSource.DataMemberChanged += new EventHandler(OnBindingSourceStateChanged); + newBindingSource.ListChanged += new ListChangedEventHandler(OnBindingSourceListChanged); + } + + oldBindingSource = newBindingSource; + RefreshItemsInternal(); + } + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/BindingSource.cs b/WindowsForms/Managed/System/WinForms/BindingSource.cs new file mode 100644 index 000000000..51a2a2265 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BindingSource.cs @@ -0,0 +1,2129 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ISupportInitialize.EndInit():System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ICancelAddNew.CancelNew(System.Int32):System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ICancelAddNew.EndNew(System.Int32):System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ISupportInitialize.BeginInit():System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ISupportInitializeNotification.get_IsInitialized():System.Boolean")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.IBindingList.AddIndex(System.ComponentModel.PropertyDescriptor):System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ISupportInitializeNotification.add_Initialized(System.EventHandler):System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.ISupportInitializeNotification.remove_Initialized(System.EventHandler):System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope="member", Target="System.Windows.Forms.BindingSource.System.ComponentModel.IBindingList.RemoveIndex(System.ComponentModel.PropertyDescriptor):System.Void")] + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Globalization; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + using System.Text; + + [ + DefaultProperty("DataSource"), + DefaultEvent("CurrentChanged"), + ComplexBindingProperties("DataSource", "DataMember"), + Designer("System.Windows.Forms.Design.BindingSourceDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionBindingSource), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1035:ICollectionImplementationsHaveStronglyTypedMembers"), // ICollection.CopyTo: Its just a wrapper class, it doesn't have a specific member type + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), // ICollection: We don't want class name to have to end in 'Collection' + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1039:ListsAreStronglyTyped"), // IList.Add: Its just a wrapper class, it doesn't have a specific member type + ] + /// relatedBindingSources; + + // Support for user-overriding of the AllowNew property + private bool allowNewIsSet = false; + private bool allowNewSetValue = true; + + // Support for property change event hooking on list items + private object currentItemHookedForItemChange = null; + private object lastCurrentItem = null; + private EventHandler listItemPropertyChangedHandler; + + // State data + private int addNewPos = -1; + private bool initializing = false; + private bool needToSetList = false; + private bool recursionDetectionFlag = false; + private bool innerListChanging = false; + private bool endingEdit = false; + + + /////////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + /////////////////////////////////////////////////////////////////////////////// + + /// (); + } + + // Look for an existing binding source that uses this data member, and return that + foreach (string key in this.relatedBindingSources.Keys) { + if (String.Equals(key, dataMember, StringComparison.OrdinalIgnoreCase)) { + return this.relatedBindingSources[key]; + } + } + + // Otherwise create the related binding source, cache it, and return it + BindingSource bs = new BindingSource(this, dataMember); + this.relatedBindingSources[dataMember] = bs; + return bs; + } + + /// + [Browsable(false)] + public object Current { + get { + return currencyManager.Count > 0 ? currencyManager.Current : null; + } + } + + [ + SRCategory(SR.CatData), + DefaultValue(""), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.BindingSourceDataMemberDescr) + ] + /// + [Browsable(false)] + public bool IsBindingSuspended { + get { + return currencyManager.IsBindingSuspended; + } + } + + [Browsable(false)] + /// + public string Sort { + get { + return this.sort; + } + set { + this.sort = value; + InnerListSort = value; + } + } + + #region Events + /////////////////////////////////////////////////////////////////////////////// + // + // Events + // + /////////////////////////////////////////////////////////////////////////////// + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceAddingNewEventHandlerDescr) + ] + /// + public event AddingNewEventHandler AddingNew { + add { + Events.AddHandler(EVENT_ADDINGNEW, value); + } + remove { + Events.RemoveHandler(EVENT_ADDINGNEW, value); + } + } + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceBindingCompleteEventHandlerDescr) + ] + public event BindingCompleteEventHandler BindingComplete { + add { + Events.AddHandler(EVENT_BINDINGCOMPLETE, value); + } + remove { + Events.RemoveHandler(EVENT_BINDINGCOMPLETE, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceDataErrorEventHandlerDescr) + ] + /// + public event BindingManagerDataErrorEventHandler DataError { + add { + Events.AddHandler(EVENT_DATAERROR, value); + } + remove { + Events.RemoveHandler(EVENT_DATAERROR, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceDataSourceChangedEventHandlerDescr) + ] + /// + public event EventHandler DataSourceChanged { + add { + Events.AddHandler(EVENT_DATASOURCECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DATASOURCECHANGED, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceDataMemberChangedEventHandlerDescr) + ] + /// + public event EventHandler DataMemberChanged { + add { + Events.AddHandler(EVENT_DATAMEMBERCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DATAMEMBERCHANGED, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceCurrentChangedEventHandlerDescr) + ] + /// + public event EventHandler CurrentChanged { + add { + Events.AddHandler(EVENT_CURRENTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CURRENTCHANGED, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceCurrentItemChangedEventHandlerDescr) + ] + /// + public event EventHandler CurrentItemChanged { + add { + Events.AddHandler(EVENT_CURRENTITEMCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CURRENTITEMCHANGED, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourceListChangedEventHandlerDescr) + ] + /// + public event ListChangedEventHandler ListChanged { + add { + Events.AddHandler(EVENT_LISTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_LISTCHANGED, value); + } + } + + [ + SRCategory(SR.CatData), + SRDescription(SR.BindingSourcePositionChangedEventHandlerDescr) + ] + /// + public event EventHandler PositionChanged { + add { + Events.AddHandler(EVENT_POSITIONCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_POSITIONCHANGED, value); + } + } + #endregion Events + + #region Methods + /////////////////////////////////////////////////////////////////////////////// + // + // Methods + // + /////////////////////////////////////////////////////////////////////////////// + + /* CUT: + + // + // AutoSetDataMember() + // + // Used when data source changes. If data member is not set, and the data source + // is a list of lists, arbitrarily point the data member at one of these lists. + // + private void AutoSetDataMember() { + // Data member already assigned! + if (!String.IsNullOrEmpty(this.dataMember)) { + return; + } + + // Get the list of lists + IListSource listSource = this.dataSource as IListSource; + + // Not a list of lists! + if (listSource == null || !listSource.ContainsListCollection) { + return; + } + + // Get properties of data source + PropertyDescriptorCollection props = ListBindingHelper.GetListItemProperties(listSource); + + // Walk properties of data source, looking for one that returns IList (but ignoring ones that return Array) + for (int i = 0; i < props.Count; ++i) { + PropertyDescriptor prop = props[i]; + + if (typeof(IList).IsAssignableFrom(prop.PropertyType) && !typeof(Array).IsAssignableFrom(prop.PropertyType)) { + // Bingo - got one! + this.dataMember = prop.Name; + OnDataMemberChanged(EventArgs.Empty); + break; + } + } + } + + */ + + private static string BuildSortString(ListSortDescriptionCollection sortsColln) { + if (sortsColln == null) { + return String.Empty; + } + + StringBuilder sb = new StringBuilder(sortsColln.Count); + + for (int i = 0; i < sortsColln.Count; ++i) { + sb.Append(sortsColln[i].PropertyDescriptor.Name + + ((sortsColln[i].SortDirection == ListSortDirection.Ascending) ? " ASC" : " DESC") + + ((i < sortsColln.Count - 1) ? "," : String.Empty)); + } + + return sb.ToString(); + } + + /// + public void CancelEdit() { + currencyManager.CancelCurrentEdit(); + } + + // Walks the BindingSource::DataSource chain until + // 1. there is a break in the chain ( BindingSource::DataSource is not a BindingSource ), or + // 2. detects a cycle in the chain. + // If a cycle is detected we throw the BindingSourceRecursionDetected exception + private void ThrowIfBindingSourceRecursionDetected(object newDataSource) { + BindingSource bindingSource = newDataSource as BindingSource; + + while (bindingSource != null) { + if (bindingSource == this) { + throw new InvalidOperationException(SR.GetString(SR.BindingSourceRecursionDetected)); + } + bindingSource = bindingSource.DataSource as BindingSource; + } + } + + private void ClearInvalidDataMember() { + if (!IsDataMemberValid()) { + this.dataMember = ""; + OnDataMemberChanged(EventArgs.Empty); + } + } + + // Creates an instance of BindingList where T is only known at run time, not compile time + private static IList CreateBindingList(Type type) { + Type genericType = typeof(BindingList<>); + Type bindingType = genericType.MakeGenericType(new Type[] { type }); + + return (IList) SecurityUtils.SecureCreateInstance(bindingType); + } + + // Create an object of the given type. Throw an exception if this fails. + private static object CreateInstanceOfType(Type type) { + object instancedObject = null; + Exception instanceException = null; + + try { + instancedObject = SecurityUtils.SecureCreateInstance(type); + } + catch (TargetInvocationException ex) { + instanceException = ex; // Default ctor threw an exception + } + catch (MethodAccessException ex) { + instanceException = ex; // Default ctor was not public + } + catch (MissingMethodException ex) { + instanceException = ex; // No default ctor defined + } + + if (instanceException != null) { + throw new NotSupportedException(SR.GetString(SR.BindingSourceInstanceError), instanceException); + } + + return instancedObject; + } + + private void CurrencyManager_PositionChanged(object sender, EventArgs e) { + Debug.Assert(sender == this.currencyManager, "only receive notifications from the currency manager"); + OnPositionChanged(e); + } + + private void CurrencyManager_CurrentChanged(object sender, EventArgs e) { + OnCurrentChanged(EventArgs.Empty); + } + + private void CurrencyManager_CurrentItemChanged(object sender, EventArgs e) { + OnCurrentItemChanged(EventArgs.Empty); + } + + private void CurrencyManager_BindingComplete(object sender, BindingCompleteEventArgs e) { + OnBindingComplete(e); + } + + private void CurrencyManager_DataError(object sender, BindingManagerDataErrorEventArgs e) { + OnDataError(e); + } + + /// + /// Unhook BindingSource from its data source, since the data source could be some + /// global object who's lifetime exceeds the lifetime of the parent form. Otherwise + /// the BindingSource (and any components bound through it) will end up in limbo, + /// still processing list change events, etc. And when unhooking from the data source, + /// take care not to trigger any events that could confuse compoents bound to us. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + UnwireDataSource(); + UnwireInnerList(); + UnhookItemChangedEventsForOldCurrent(); + UnwireCurrencyManager(this.currencyManager); + this.dataSource = null; + this.sort = null; + this.dataMember = null; + this._innerList = null; + this.isBindingList = false; + this.needToSetList = true; + this.raiseListChangedEvents = false; + } + disposedOrFinalized = true; + base.Dispose(disposing); + } + + /// + public void EndEdit() { + if (endingEdit) { + return; + } + try { + endingEdit = true; + currencyManager.EndCurrentEdit(); + } + finally { + endingEdit = false; + } + } + + // + // EnsureInnerList() + // + // Ensures that the inner list has been set up. Handles the case of ResetList() being called during + // initialization, which sets a flag to defer ResetList() work until after initialization is complete. + // + private void EnsureInnerList() { + if (!this.initializing && needToSetList) { + needToSetList = false; + ResetList(); + } + } + + // + // Find() + // + // Overload of IBindingList.Find that takes a string instead of a property descriptor (for convenience). + // + public int Find(String propertyName, object key) { + PropertyDescriptor pd = (itemShape == null) ? null : itemShape.Find(propertyName, true); + + if (pd == null) { + throw new System.ArgumentException(SR.GetString(SR.DataSourceDataMemberPropNotFound, propertyName)); + } + + return (this as IBindingList).Find(pd, key); + } + + // + // GetListFromType() + // + // Given a type, create a list based on that type. If the type represents a list type, + // we create an instance of that type (or throw if we cannot instance that type). + // Otherwise we assume the type represents the item type, in which case we create + // a typed BindingList of that item type. + // + + private static IList GetListFromType(Type type) { + IList list = null; + + if (typeof(ITypedList).IsAssignableFrom(type) && typeof(IList).IsAssignableFrom(type)) { + list = CreateInstanceOfType(type) as IList; + } + else if (typeof(IListSource).IsAssignableFrom(type)) { + list = (CreateInstanceOfType(type) as IListSource).GetList(); + } + else { + list = CreateBindingList(ListBindingHelper.GetListItemType(type)); + } + + return list; + } + + // + // GetListFromEnumerable() + // + // Creates a list based on an enumerable object. We rip through the enumerable, + // extract all its items, and stuff these items into a typed BindingList, using + // the type of the first item to determine the type of the list. + // + private static IList GetListFromEnumerable(IEnumerable enumerable) { + IList list = null; + + foreach (object item in enumerable) { + if (list == null) { + list = CreateBindingList(item.GetType()); + } + + list.Add(item); + } + + return list; + } + + // + // IsDataMemberValid() + // + // Used when we change data sources or when the properties of the current data source change. + // Decides whether this would be a good time to blow away the data member field, since it + // might not refer to a valid data source property any more. + // + private bool IsDataMemberValid() { + // Don't mess with things during initialization because the data + // member property can get set before the data source property. + if (this.initializing) { + return true; + } + + // If data member has not been specified, leave the data member property alone + if (String.IsNullOrEmpty(this.dataMember)) { + return true; + } + + // See if data member corresponds to a valid property on the specified data source + PropertyDescriptorCollection dsProps = ListBindingHelper.GetListItemProperties(this.dataSource); + PropertyDescriptor dmProp = dsProps[this.dataMember]; + if (dmProp != null) { + return true; + } + + return false; + } + + private void InnerList_ListChanged(object sender, ListChangedEventArgs e) { + // Set recursive flag - see VSWhidbey 455276 + // Basically, we can have computed columns that cause our parent + // to change when our list changes. This can cause recursion because we update + // when our parent updates which then causes our parent to update which + // then causes us to update which then causes our parent to update which + // then causes us to update which then causes our parent to update... + if (!this.innerListChanging) { + try { + this.innerListChanging = true; + OnListChanged(e); + } + finally { + this.innerListChanging = false; + } + } + } + + private void ListItem_PropertyChanged(object sender, EventArgs e) { + int index; + + // Performance: If the item that changed is the current item, we can avoid a potentially expensive call to IndexOf() + if (sender == currentItemHookedForItemChange) { + index = this.Position; + Debug.Assert(index >= 0, "BindingSource.ListItem_PropertyChanged - no current item."); + Debug.Assert(index == ((IList) this).IndexOf(sender), "BindingSource.ListItem_PropertyChanged - unexpected current item."); + } + else { + index = ((IList) this).IndexOf(sender); + } + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); + } + + /// + public void MoveFirst() { + Position = 0; + } + + /// + public void MoveLast() { + Position = Count - 1; + } + + /// + public void MoveNext() { + ++Position; + } + + /// + public void MovePrevious() { + --Position; + } + + // This method is used to fire ListChanged events when the inner list + // is not an IBindingList (and therefore cannot fire them itself). + // + private void OnSimpleListChanged(ListChangedType listChangedType, int newIndex) { + if (!isBindingList) { + OnListChanged(new ListChangedEventArgs(listChangedType, newIndex)); + } + } + + /// + protected virtual void OnAddingNew(AddingNewEventArgs e) { + AddingNewEventHandler eh = (AddingNewEventHandler) Events[EVENT_ADDINGNEW]; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnBindingComplete(BindingCompleteEventArgs e) { + BindingCompleteEventHandler eh = (BindingCompleteEventHandler) Events[EVENT_BINDINGCOMPLETE]; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnCurrentChanged(EventArgs e) { + // Unhook change events for old current item (recorded by currentItemHookedForItemChange) + UnhookItemChangedEventsForOldCurrent(); + + // Hook change events for new current item (as indicated now by this.Current) + HookItemChangedEventsForNewCurrent(); + + EventHandler eh = (EventHandler) Events[EVENT_CURRENTCHANGED]; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnCurrentItemChanged(EventArgs e) { + EventHandler eh = (EventHandler) Events[EVENT_CURRENTITEMCHANGED]; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnDataError(BindingManagerDataErrorEventArgs e) { + BindingManagerDataErrorEventHandler eh = Events[EVENT_DATAERROR] as BindingManagerDataErrorEventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnDataMemberChanged(EventArgs e) { + EventHandler eh = Events[EVENT_DATAMEMBERCHANGED] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnDataSourceChanged(EventArgs e) { + EventHandler eh = Events[EVENT_DATASOURCECHANGED] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnListChanged(ListChangedEventArgs e) { + // Sometimes we are required to suppress ListChanged events + if (!this.raiseListChangedEvents || this.initializing) { + return; + } + + ListChangedEventHandler eh = (ListChangedEventHandler)Events[EVENT_LISTCHANGED]; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnPositionChanged(EventArgs e) { + EventHandler eh = (EventHandler) Events[EVENT_POSITIONCHANGED]; + if (eh != null) + eh(this,e); + } + + // + // ParentCurrencyManager_CurrentItemChanged() + // + // When the data member is set, and the data source signals a change of current item, + // we need to query its new current item for the list specified by the data member. + // Or if there is no longer a current item on the data source, we use an empty list. + // In either case, we only have to change lists, not metadata, since we can assume + // that the new list has the same item properties as the old list. + // + private void ParentCurrencyManager_CurrentItemChanged(object sender, EventArgs e) { + if (this.initializing) + return; + + // Commit pending changes in prior list (VSWhidbey #418447) + if (parentsCurrentItemChanging) { + return; + } + try { + parentsCurrentItemChanging = true; + // Do what RelatedCurrencyManager does when the parent changes: + // 1. PullData from the controls into the back end. + // 2. Don't EndEdit the transaction. + bool success; + this.currencyManager.PullData(out success); + } + finally { + parentsCurrentItemChanging = false; + } + + CurrencyManager cm = (CurrencyManager)sender; + + // track if the current list changed + bool currentItemChanged = true; + + if (!String.IsNullOrEmpty(this.dataMember)) { + object currentValue = null; + IList currentList = null; + + if (cm.Count > 0) { + // If parent list has a current item, get the sub-list from the relevant property on that item + PropertyDescriptorCollection dsProps = cm.GetItemProperties(); + PropertyDescriptor dmProp = dsProps[this.dataMember]; + if (dmProp != null) { + currentValue = ListBindingHelper.GetList(dmProp.GetValue(cm.Current)); + currentList = currentValue as IList; + } + } + + if (currentList != null) { + // Yippeeee, the current item gave us a list to bind to! + // [NOTE: Specify applySortAndFilter=TRUE to apply our sort/filter settings to new list] + SetList(currentList, false, true); + } + else if (currentValue != null) { + // Ok, we didn't get a list, but we did get something, so wrap it in a list + // [NOTE: Specify applySortAndFilter=FALSE to stop BindingList from throwing] + SetList(WrapObjectInBindingList(currentValue), false, false); + } + else { + // Nothing to bind to (no current item, or item's property returned null). + // Create an empty list, using the previously determined item type. + // [NOTE: Specify applySortAndFilter=FALSE to stop BindingList from throwing] + SetList(CreateBindingList(this.itemType), false, false); + } + + // After a change of child lists caused by a change in the current parent item, we + // should reset the list position (a la RelatedCurrencyManager). But we have to do + // this explicitly, because a CurrencyManager normally tries to preserve its position + // after a list reset event. + + // Only reset the position if the list really changed or if the list + // position is incorrect + currentItemChanged = ((null == lastCurrentItem) || (cm.Count == 0) || (lastCurrentItem != cm.Current) || (this.Position >= this.Count)); + + // Save last current item + lastCurrentItem = cm.Count > 0 ? cm.Current : null; + + if (currentItemChanged) { + this.Position = (this.Count > 0 ? 0 : -1); + } + } + + OnCurrentItemChanged(EventArgs.Empty); + } + + // + // ParentCurrencyManager_MetaDataChanged() + // + // When the data source signals a change of metadata, we need to re-query for the list specified + // by the data member field. If the data member is no longer valid under the data source's new + // metadata, we have no choice but to clear the data member field and just bind directly to the + // data source itself. + // + private void ParentCurrencyManager_MetaDataChanged(object sender, EventArgs e) { + ClearInvalidDataMember(); + ResetList(); + } + + // << Some of this code is taken from System.Data.DataTable::ParseSortString method >> + private ListSortDescriptionCollection ParseSortString(string sortString) { + if (String.IsNullOrEmpty(sortString)) { + return new ListSortDescriptionCollection(); + } + + ArrayList sorts = new ArrayList(); + PropertyDescriptorCollection props = this.currencyManager.GetItemProperties(); + + string[] split = sortString.Split(new char[] {','}); + for (int i = 0; i < split.Length; i++) { + string current = split[i].Trim(); + + // Handle ASC and DESC + int length = current.Length; + bool ascending = true; + if (length >= 5 && String.Compare(current, length - 4, " ASC", 0, 4, true, CultureInfo.InvariantCulture) == 0) { + current = current.Substring(0, length - 4).Trim(); + } + else if (length >= 6 && String.Compare(current, length - 5, " DESC", 0, 5, true, CultureInfo.InvariantCulture) == 0) { + ascending = false; + current = current.Substring(0, length - 5).Trim(); + } + + // Handle brackets + if (current.StartsWith("[")) { + if (current.EndsWith("]")) { + current = current.Substring(1, current.Length - 2); + } + else { + throw new ArgumentException(SR.GetString(SR.BindingSourceBadSortString)); + } + } + + // Find the property + PropertyDescriptor prop = props.Find(current, true); + if (prop == null) { + throw new ArgumentException(SR.GetString(SR.BindingSourceSortStringPropertyNotInIBindingList)); + } + + // Add the sort description + sorts.Add(new ListSortDescription(prop, ascending ? ListSortDirection.Ascending : ListSortDirection.Descending)); + } + + ListSortDescription[] result = new ListSortDescription[sorts.Count]; + sorts.CopyTo(result); + return new ListSortDescriptionCollection(result); + } + + /// + public void RemoveCurrent() { + if (!(this as IBindingList).AllowRemove) { + throw new InvalidOperationException(SR.GetString(SR.BindingSourceRemoveCurrentNotAllowed)); + } + + if (Position < 0 || Position >= Count) { + throw new InvalidOperationException(SR.GetString(SR.BindingSourceRemoveCurrentNoCurrentItem)); + } + + RemoveAt(Position); + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual void ResetAllowNew() { + this.allowNewIsSet = false; + this.allowNewSetValue = true; + } + + /// + public void ResumeBinding() { + currencyManager.ResumeBinding(); + } + + /// + public void SuspendBinding() { + currencyManager.SuspendBinding(); + } + + // + // ResetList() + // + // Binds the BindingSource to the list specified by its DataSource and DataMember properties. + // + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] // List is cast to IEnumerable twice. Acceptible trade-off versus code clarity (this method contains *critical* logic). + private void ResetList() { + + // + // Don't bind during initialization, since the data source may not have been initialized yet. + // Instead, set a flag that causes binding to occur on first post-init attempt to access list. + // + if (this.initializing) { + needToSetList = true; + return; + } + else { + needToSetList = false; + } + + // + // Find the list identified by the current DataSource and DataMember properties. + // + // If the DataSource only specifies a Type, we actually create an + // instance from that Type and obtain the list from that instance. + // + // Note: The method below will throw an exception if a data member is specified + // but does not correspond to a valid property on the data source. + // + object dataSourceInstance = (this.dataSource is Type) ? GetListFromType(this.dataSource as Type) : this.dataSource; + object list = ListBindingHelper.GetList(dataSourceInstance, this.dataMember); + this.listExtractedFromEnumerable = false; + + // + // Convert the candidate list into an IList, if necessary... + // + IList bindingList = null; + if (list is IList) { + // If its already an IList then we're done! + bindingList = list as IList; + } + else { + if (list is IListSource) { + bindingList = (list as IListSource).GetList(); + } + else if (list is IEnumerable) { + // If its an enumerable list, extract its contents and put them in a new list + bindingList = GetListFromEnumerable(list as IEnumerable); + + // GetListFromEnumerable returns null if there are no elements + // Don't consider it a list of enumerables in this case + if (bindingList != null) + this.listExtractedFromEnumerable = true; + } + // If it's not an IList, IListSource or IEnumerable + if (bindingList == null) { + if (list != null) { + // If its some random non-list object, just wrap it in a list + bindingList = WrapObjectInBindingList(list); + } + else { + // Can't get any list from the data source (eg. data member specifies related sub-list but the + // data source's list is empty, so there is no current item to get the sub-list from). In this + // case we simply determine what the list's item type would be, and create empty list with that + // same item type. If the item type cannot be determined, we end up with an item type of 'Object'. + Type type = ListBindingHelper.GetListItemType(this.dataSource, this.dataMember); + bindingList = GetListFromType(type); + if (bindingList == null) { + bindingList = CreateBindingList (type); + } + } + } + } + + // + // Bind to this list now + // + SetList(bindingList, true, true); + } + + // + // SetList() + // + // Binds the BindingSource to the specified list, rewiring internal event handlers, + // firing any appropriate external events, and updating all relevant field members. + // + + private void SetList(IList list, bool metaDataChanged, bool applySortAndFilter) { + if (list == null) { + // The list argument should never be null! We will handle null gracefully + // at run-time, but we will complain bitterly about this in debug builds! + Debug.Fail("BindingSource.SetList() was called with illegal list argument!"); + list = CreateBindingList(this.itemType); + } + + // Unwire stuff from the old list + UnwireInnerList(); + UnhookItemChangedEventsForOldCurrent(); + + // Bind to the new list + IList listInternal = ListBindingHelper.GetList(list) as IList; + if (listInternal == null) { + listInternal = list; + } + + this._innerList = listInternal; + + // Remember whether the new list implements IBindingList + this.isBindingList = (listInternal is IBindingList); + + // + // Determine whether the new list converts PropertyChanged events on its items into ListChanged events. + // If it does, then the BindingSource won't need to hook the PropertyChanged events itself. If the list + // implements IRaiseItemChangedEvents, we can ask it directly. Otherwise we will assume that any list + // which impements IBindingList automatically supports this capability. + // + if (listInternal is IRaiseItemChangedEvents) { + this.listRaisesItemChangedEvents = (listInternal as IRaiseItemChangedEvents).RaisesItemChangedEvents; + } + else { + this.listRaisesItemChangedEvents = this.isBindingList; + } + + // If list schema may have changed, update list item info now + if (metaDataChanged) { + this.itemType = ListBindingHelper.GetListItemType(List); + this.itemShape = ListBindingHelper.GetListItemProperties(List); + this.itemConstructor = this.itemType.GetConstructor(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.CreateInstance, + null, new Type[0], null); + } + + // Wire stuff up to the new list + WireInnerList(); + HookItemChangedEventsForNewCurrent(); + + // Fire list reset and/or metadata changed events + ResetBindings(metaDataChanged); + + // + // Apply any custom Sort and Filter values to the new list. + // + // NOTE: The list will throw a NotSupportedException here if it rejects the new sort + // and filter settings (either because it doesn't support sorting and filtering, or + // because the sort or filter values were invalid). + // + if (applySortAndFilter) { + if (this.Sort != null) { + this.InnerListSort = this.Sort; + } + if (this.Filter != null) { + this.InnerListFilter = this.Filter; + } + } + } + + private static IList WrapObjectInBindingList(object obj) { + IList list = CreateBindingList(obj.GetType()); + list.Add(obj); + return list; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeAllowNew() { + return this.allowNewIsSet; + } + + /////////////////////////////////////////////////////////////////////////////// + // + // ItemChanged event support + // + /////////////////////////////////////////////////////////////////////////////// + + // Hooks property changed events for the NEW current item, if nececssary + private void HookItemChangedEventsForNewCurrent() { + Debug.Assert(this.currentItemHookedForItemChange == null, "BindingSource trying to hook new current item before unhooking old current item!"); + + if (!this.listRaisesItemChangedEvents) { + if (this.Position >= 0 && this.Position <= this.Count - 1) { + this.currentItemHookedForItemChange = this.Current; + WirePropertyChangedEvents(this.currentItemHookedForItemChange); + } + else { + this.currentItemHookedForItemChange = null; + } + } + } + + // Unhooks property changed events for the OLD current item, if necessary + private void UnhookItemChangedEventsForOldCurrent() { + if (!this.listRaisesItemChangedEvents) { + UnwirePropertyChangedEvents(this.currentItemHookedForItemChange); + this.currentItemHookedForItemChange = null; + } + } + + /////////////////////////////////////////////////////////////////////////////// + // + // Event wiring and unwiring methods + // + /////////////////////////////////////////////////////////////////////////////// + + private void WireCurrencyManager(CurrencyManager cm) { + if (cm != null) { + cm.PositionChanged += new EventHandler(CurrencyManager_PositionChanged); + cm.CurrentChanged += new EventHandler(CurrencyManager_CurrentChanged); + cm.CurrentItemChanged += new EventHandler(CurrencyManager_CurrentItemChanged); + cm.BindingComplete += new BindingCompleteEventHandler(CurrencyManager_BindingComplete); + cm.DataError += new BindingManagerDataErrorEventHandler(CurrencyManager_DataError); + } + } + + private void UnwireCurrencyManager(CurrencyManager cm) { + if (cm != null) { + cm.PositionChanged -= new EventHandler(CurrencyManager_PositionChanged); + cm.CurrentChanged -= new EventHandler(CurrencyManager_CurrentChanged); + cm.CurrentItemChanged -= new EventHandler(CurrencyManager_CurrentItemChanged); + cm.BindingComplete -= new BindingCompleteEventHandler(CurrencyManager_BindingComplete); + cm.DataError -= new BindingManagerDataErrorEventHandler(CurrencyManager_DataError); + } + } + + private void WireDataSource() { + if (dataSource is ICurrencyManagerProvider) { + CurrencyManager cm = (dataSource as ICurrencyManagerProvider).CurrencyManager; + cm.CurrentItemChanged += new EventHandler(ParentCurrencyManager_CurrentItemChanged); + cm.MetaDataChanged += new EventHandler(ParentCurrencyManager_MetaDataChanged); + } + } + + private void UnwireDataSource() { + if (dataSource is ICurrencyManagerProvider) { + CurrencyManager cm = (dataSource as ICurrencyManagerProvider).CurrencyManager; + cm.CurrentItemChanged -= new EventHandler(ParentCurrencyManager_CurrentItemChanged); + cm.MetaDataChanged -= new EventHandler(ParentCurrencyManager_MetaDataChanged); + } + } + + private void WireInnerList() { + if (_innerList is IBindingList) { + IBindingList list = _innerList as IBindingList; + list.ListChanged += new ListChangedEventHandler(InnerList_ListChanged); + } + } + + private void UnwireInnerList() { + if (_innerList is IBindingList) { + IBindingList list = _innerList as IBindingList; + list.ListChanged -= new ListChangedEventHandler(InnerList_ListChanged); + } + } + + private void WirePropertyChangedEvents(object item) { + if (item != null && this.itemShape != null) { + for (int j = 0; j < this.itemShape.Count; j++) { + this.itemShape[j].AddValueChanged(item, listItemPropertyChangedHandler); + } + } + } + + private void UnwirePropertyChangedEvents(object item) { + if (item != null && this.itemShape != null) { + for (int j = 0; j < this.itemShape.Count; j++) { + this.itemShape[j].RemoveValueChanged(item, listItemPropertyChangedHandler); + } + } + } + #endregion Methods + + #region ISupportInitialize/ISupportInitializeNotification + /////////////////////////////////////////////////////////////////////////////// + // + // ISupportInitialize and ISupportInitializeNotification interfaces + // + /////////////////////////////////////////////////////////////////////////////// + + // Begin bulk member initialization - deferring calculation of inner list until EndInit is reached + // + void ISupportInitialize.BeginInit() { + initializing = true; + } + + // End bulk member initialization - updating the inner list and notifying any dependents of our completion + // + private void EndInitCore() { + initializing = false; + EnsureInnerList(); + OnInitialized(); + } + + // Check to see if DataSource has completed its initialization, before ending our initialization. + // If DataSource is still initializing, hook its Initialized event and wait for it to signal completion. + // If DataSource is already initialized, just go ahead and complete our initialization now. + // + void ISupportInitialize.EndInit() { + ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); + + if (dsInit != null && !dsInit.IsInitialized) { + dsInit.Initialized += new EventHandler(DataSource_Initialized); + } + else { + EndInitCore(); + } + } + + // Respond to late completion of the DataSource's initialization, by completing our own initialization. + // This situation can arise if the call to the DataSource's EndInit() method comes after the call to the + // BindingSource's EndInit() method (since code-generated ordering of these calls is non-deterministic). + // + private void DataSource_Initialized(object sender, EventArgs e) { + ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); + + Debug.Assert(dsInit != null, "BindingSource: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!"); + Debug.Assert(dsInit.IsInitialized, "BindingSource: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing."); + + if (dsInit != null) { + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + } + + EndInitCore(); + } + + // Report to any dependents whether we are still in bulk member initialization + // + bool ISupportInitializeNotification.IsInitialized { + get { + return !initializing; + } + } + + // Event used to signal to our dependents that we have completed bulk member initialization and updated our inner list + // + event EventHandler ISupportInitializeNotification.Initialized { + add { + Events.AddHandler(EVENT_INITIALIZED, value); + } + remove { + Events.RemoveHandler(EVENT_INITIALIZED, value); + } + } + + // Method used to raise the Initialized event above + // + private void OnInitialized() { + EventHandler eh = (EventHandler) Events[EVENT_INITIALIZED]; + if (eh != null) + eh(this, EventArgs.Empty); + } + #endregion ISupportInitialize/ISupportInitializeNotification + + #region IEnumerable + /////////////////////////////////////////////////////////////////////////////// + // + // IEnumerable interface + // + /////////////////////////////////////////////////////////////////////////////// + + public virtual IEnumerator GetEnumerator() { + return List.GetEnumerator(); + } + #endregion + + #region ICollection + /////////////////////////////////////////////////////////////////////////////// + // + // ICollection interface + // + /////////////////////////////////////////////////////////////////////////////// + + public virtual void CopyTo(Array arr, int index) { + List.CopyTo(arr, index); + } + + [Browsable(false)] + public virtual int Count { + get { + try + { + if (disposedOrFinalized) + { + return 0; + } + if (recursionDetectionFlag) + { + throw new InvalidOperationException(SR.GetString(SR.BindingSourceRecursionDetected)); + } + recursionDetectionFlag = true; + + return List.Count; + } + finally + { + recursionDetectionFlag = false; + } + } + } + + [Browsable(false)] + public virtual bool IsSynchronized + { + get { + return List.IsSynchronized; + } + } + + [Browsable(false)] + public virtual object SyncRoot { + get { + return List.SyncRoot; + } + } + + #endregion ICollection + + #region IList + /////////////////////////////////////////////////////////////////////////////// + // + // IList interface + // + /////////////////////////////////////////////////////////////////////////////// + + /// + public virtual object AddNew() { + // Throw if adding new items has been disabled + if (!AllowNewInternal(false)) { + throw new InvalidOperationException(SR.GetString(SR.BindingSourceBindingListWrapperAddToReadOnlyList)); + } + + if (!AllowNewInternal(true)) { + throw new InvalidOperationException(SR.GetString( + SR.BindingSourceBindingListWrapperNeedToSetAllowNew, + itemType == null ? "(null)" : itemType.FullName + )); + } + + // Remember this since EndEdit() below will clear it + int saveAddNew = this.addNewPos; + + // Commit any uncomitted list changes now + EndEdit(); + + // We just committed a new item; mimic DataView and fire an ItemAdded event for it here + if (saveAddNew != -1) { + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, saveAddNew)); + } + + // Raise the AddingNew event in case listeners want to supply the new item for us + AddingNewEventArgs addingNew = new AddingNewEventArgs(); + int oldCount = List.Count; + OnAddingNew(addingNew); + object addNewItem = addingNew.NewObject; + + // + // If no item came back from AddingNew event, we must create the new item ourselves... + // + if (addNewItem == null) { + // If the inner list is an IBindingList, let it create and add the new item for us. + // Then make the new item the current item (...assuming, as CurrencyManager does, + // that the new item was added at the *bottom* of the list). + if (isBindingList) { + addNewItem = (List as IBindingList).AddNew(); + this.Position = this.Count - 1; + return addNewItem; + } + + // Throw if we don't know how to create items of the current item type + if (this.itemConstructor == null) { + throw new InvalidOperationException(SR.GetString( + SR.BindingSourceBindingListWrapperNeedAParameterlessConstructor, + itemType == null ? "(null)" : itemType.FullName + )); + } + + // Create new item using default ctor for current item type + addNewItem = this.itemConstructor.Invoke(null); + } + + if (List.Count > oldCount) { + // If event handler has already added item to list, then simply record the item's position + this.addNewPos = this.Position; + } + else { + // If event handler has not yet added item to list, then add it + // ourselves, make it the current item, and record its position. + this.addNewPos = this.Add(addNewItem); + this.Position = this.addNewPos; + } + + return addNewItem; + } + + [Browsable(false)] + public virtual bool AllowEdit { + get { + if (isBindingList) { + return ((IBindingList) List).AllowEdit; + } + else { + return !List.IsReadOnly; + } + } + } + + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.BindingSourceAllowNewDescr), + ] + /// + public virtual bool AllowNew { + get { + //we check to ensure we have a valid default constructor (if we get that far). + return AllowNewInternal(true); + } + + set { + // If value was previously set and isn't changing now, do nothing + if (this.allowNewIsSet && value == this.allowNewSetValue) { + return; + } + + // Don't let user set value to true if inner list can never support adding of items + // do NOT check for a default constructor because someone will set AllowNew=True + // when they have overridden OnAddingNew (which we cannot detect). + if (value == true && !isBindingList && !IsListWriteable(false)) { + throw new InvalidOperationException(SR.GetString(SR.NoAllowNewOnReadOnlyList)); + } + + // Record new value, which will now override inner list's value + this.allowNewIsSet = true; + this.allowNewSetValue = value; + + // Mimic the DataView class and fire a list reset event now + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); + } + } + + [Browsable(false)] + public virtual bool AllowRemove { + get { + if (isBindingList) { + return ((IBindingList) List).AllowRemove; + } + else { + return !List.IsReadOnly && !List.IsFixedSize; + } + } + } + + [Browsable(false)] + public virtual bool SupportsChangeNotification { + get { + return true; + } + } + + [Browsable(false)] + public virtual bool SupportsSearching { + get { + if (isBindingList) { + return ((IBindingList) List).SupportsSearching; + } + else { + return false; + } + } + } + + [Browsable(false)] + public virtual bool SupportsSorting { + get { + if (isBindingList) { + return ((IBindingList) List).SupportsSorting; + } + else { + return false; + } + } + } + + [Browsable(false)] + public virtual bool IsSorted { + get { + if (isBindingList) { + return ((IBindingList) List).IsSorted; + } + else { + return false; + } + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual PropertyDescriptor SortProperty { + get { + if (isBindingList) { + return ((IBindingList) List).SortProperty; + } + else { + return null; + } + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual ListSortDirection SortDirection { + get { + if (isBindingList) { + return ((IBindingList) List).SortDirection; + } + else { + return ListSortDirection.Ascending; + } + } + } + + void IBindingList.AddIndex(PropertyDescriptor property) { + if (isBindingList) { + ((IBindingList) List).AddIndex(property); + } + else { + throw new NotSupportedException(SR.GetString(SR.OperationRequiresIBindingList)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ApplySort(PropertyDescriptor property, ListSortDirection sort) { + if (isBindingList) { + ((IBindingList) List).ApplySort(property, sort); + } + else { + throw new NotSupportedException(SR.GetString(SR.OperationRequiresIBindingList)); + } + } + + public virtual int Find(PropertyDescriptor prop, object key) { + if (isBindingList) { + return ((IBindingList) List).Find(prop, key); + } + else { + throw new NotSupportedException(SR.GetString(SR.OperationRequiresIBindingList)); + } + } + + void IBindingList.RemoveIndex(PropertyDescriptor prop) { + if (isBindingList) { + ((IBindingList) List).RemoveIndex(prop); + } + else { + throw new NotSupportedException(SR.GetString(SR.OperationRequiresIBindingList)); + } + } + + public virtual void RemoveSort() { + this.sort = null; + + if (isBindingList) { + ((IBindingList) List).RemoveSort(); + } + } + #endregion IBindingList + + #region IBindingListView + /////////////////////////////////////////////////////////////////////////////// + // + // IBindingListView interface + // + /////////////////////////////////////////////////////////////////////////////// + + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ApplySort(ListSortDescriptionCollection sorts) { + IBindingListView iblw = List as IBindingListView; + if (iblw != null) { + iblw.ApplySort(sorts); + } + else { + throw new NotSupportedException(SR.GetString(SR.OperationRequiresIBindingListView)); + } + } + + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual ListSortDescriptionCollection SortDescriptions { + get { + IBindingListView iblw = List as IBindingListView; + if (iblw != null) { + return iblw.SortDescriptions; + } else { + return null; + } + } + } + + [ + SRCategory(SR.CatData), + DefaultValue(null), + SRDescription(SR.BindingSourceFilterDescr) + ] + /// + /// + /// Represents a collection of data bindings on a control. + /// + [DefaultEvent("CollectionChanged")] + public class BindingsCollection : System.Windows.Forms.BaseCollection { + + private ArrayList list; + private CollectionChangeEventHandler onCollectionChanging; + private CollectionChangeEventHandler onCollectionChanged; + + // internalonly + internal BindingsCollection() { + } + + /// + public override int Count { + get { + if (list == null) { + return 0; + } + return base.Count; + } + } + + /// + /// + /// + /// + /// Gets the bindings in the collection as an object. + /// + /// + protected override ArrayList List { + get { + if (list == null) + list = new ArrayList(); + return list; + } + } + + /// + /// + /// Gets the at the specified index. + /// + public Binding this[int index] { + get { + return (Binding) List[index]; + } + } + + /// + // internalonly + internal protected void Add(Binding binding) { + CollectionChangeEventArgs ccevent = new CollectionChangeEventArgs(CollectionChangeAction.Add, binding); + OnCollectionChanging(ccevent); + AddCore(binding); + OnCollectionChanged(ccevent); + } + + // internalonly + /// + /// + /// + /// + /// Adds a + /// to the collection. + /// + /// + protected virtual void AddCore(Binding dataBinding) { + if (dataBinding == null) + throw new ArgumentNullException("dataBinding"); + + List.Add(dataBinding); + } + + /// + /// + /// + /// Occurs when the collection is about to change. + /// + /// + [SRDescription(SR.collectionChangingEventDescr)] + public event CollectionChangeEventHandler CollectionChanging { + add { + onCollectionChanging += value; + } + remove { + onCollectionChanging -= value; + } + } + + /// + /// + /// + /// Occurs when the collection is changed. + /// + /// + [SRDescription(SR.collectionChangedEventDescr)] + public event CollectionChangeEventHandler CollectionChanged { + add { + onCollectionChanged += value; + } + remove { + onCollectionChanged -= value; + } + } + + // internalonly + /// + internal protected void Clear() { + CollectionChangeEventArgs ccevent = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null); + OnCollectionChanging(ccevent); + ClearCore(); + OnCollectionChanged(ccevent); + } + + // internalonly + /// + /// + /// + /// + /// Clears the collection of any members. + /// + /// + protected virtual void ClearCore() { + List.Clear(); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnCollectionChanging(CollectionChangeEventArgs e) { + if (onCollectionChanging != null) { + onCollectionChanging(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent) { + if (onCollectionChanged != null) { + onCollectionChanged(this, ccevent); + } + } + + /// + // internalonly + internal protected void Remove(Binding binding) { + CollectionChangeEventArgs ccevent = new CollectionChangeEventArgs(CollectionChangeAction.Remove, binding); + OnCollectionChanging(ccevent); + RemoveCore(binding); + OnCollectionChanged(ccevent); + } + + + /// + // internalonly + internal protected void RemoveAt(int index) { + Remove(this[index]); + } + + // internalonly + /// + /// + /// + /// + /// Removes the specified from the collection. + /// + /// + protected virtual void RemoveCore(Binding dataBinding) { + List.Remove(dataBinding); + } + + + /// + // internalonly + internal protected bool ShouldSerializeMyAll() { + return Count > 0; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/BootMode.cs b/WindowsForms/Managed/System/WinForms/BootMode.cs new file mode 100644 index 000000000..7a4157e6f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BootMode.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + /// + /// + /// + /// Specifies the mode to start the computer + /// in. + /// + /// + public enum BootMode { + /// + /// + /// + /// Starts the computer in standard mode. + /// + /// + Normal = 0, + /// + /// + /// + /// Starts the computer by using only the basic + /// files and + /// drivers. + /// + /// + FailSafe = 1, + /// + /// + /// + /// Starts the computer by using the basic files, drivers and + /// the services and drivers + /// necessary to start networking. + /// + /// + FailSafeWithNetwork = 2, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Border3DSide.cs b/WindowsForms/Managed/System/WinForms/Border3DSide.cs new file mode 100644 index 000000000..d94f076d6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Border3DSide.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies + /// the sides of a rectangle to apply a three-dimensional border to. + /// + /// + [System.Runtime.InteropServices.ComVisible(true), Flags] + public enum Border3DSide { + + /// + /// + /// + /// A three-dimensional border on + /// the left edge + /// of the control. + /// + /// + Left = NativeMethods.BF_LEFT, + + /// + /// + /// + /// A three-dimensional border on + /// the top edge + /// of the rectangle. + /// + /// + Top = NativeMethods.BF_TOP, + + /// + /// + /// + /// A three-dimensional border on + /// the right side + /// of the rectangle. + /// + /// + Right = NativeMethods.BF_RIGHT, + + /// + /// + /// + /// A three-dimensional border on + /// the bottom side + /// of the rectangle. + /// + /// + Bottom = NativeMethods.BF_BOTTOM, + + /// + /// + /// + /// The interior of the rectangle is filled with the + /// color defined for three-dimensional controls instead of the + /// background color + /// for the form. + /// + /// + Middle = NativeMethods.BF_MIDDLE, + + /// + /// + /// + /// A three-dimensional border on all four + /// edges and fill the middle of + /// the rectangle with + /// the color defeined for three-dimensional controls. + /// + /// + All = Left | Top | Right | Bottom | Middle, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Border3DStyle.cs b/WindowsForms/Managed/System/WinForms/Border3DStyle.cs new file mode 100644 index 000000000..9b3939360 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Border3DStyle.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies the style of a three-dimensional border. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum Border3DStyle { + + /// + /// + /// + /// The border is drawn + /// outside the + /// specified rectangle, preserving the dimensions of the rectangle for drawing. + /// + /// + Adjust = NativeMethods.BF_ADJUST, + + /// + /// + /// + /// The border has + /// a raised outer edge and a sunken inner edge. + /// + /// + Bump = NativeMethods.EDGE_BUMP, + + /// + /// + /// + /// The border has a + /// with a sunken inner edge and a raised outer edge. + /// + /// + Etched = NativeMethods.EDGE_ETCHED, + + /// + /// + /// + /// The border has a with no three-dimensional effects. + /// + /// + Flat = NativeMethods.BF_FLAT | NativeMethods.EDGE_SUNKEN, + + /// + /// + /// + /// The border has a with + /// raised inner and outer edges. + /// + /// + Raised = NativeMethods.EDGE_RAISED, + + /// + /// + /// + /// The border has a with a raised inner edge and no outer edge. + /// + /// + RaisedInner = NativeMethods.BDR_RAISEDINNER, + + /// + /// + /// + /// The border has a + /// with a raised outer edge and no inner edge. + /// + /// + RaisedOuter = NativeMethods.BDR_RAISEDOUTER, + + /// + /// + /// + /// The border has a with sunken inner and outer edges. + /// + /// + Sunken = NativeMethods.EDGE_SUNKEN, + + /// + /// + /// + /// The border has a with + /// a sunken inner edge and no outer edge. + /// + /// + SunkenInner = NativeMethods.BDR_SUNKENINNER, + + /// + /// + /// + /// The border has a with a sunken outer edge and no inner edge. + /// + /// + SunkenOuter = NativeMethods.BDR_SUNKENOUTER, + } +} diff --git a/WindowsForms/Managed/System/WinForms/BorderStyle.cs b/WindowsForms/Managed/System/WinForms/BorderStyle.cs new file mode 100644 index 000000000..98587de0d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BorderStyle.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the + /// border style for a control or form. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum BorderStyle { + + /// + /// + /// + /// No border. + /// + /// + None = 0, + + /// + /// + /// + /// A single-line border. + /// + /// + FixedSingle = 1, + + /// + /// + /// + /// A three-dimensional border. + /// + /// + Fixed3D = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/BoundsSpecified.cs b/WindowsForms/Managed/System/WinForms/BoundsSpecified.cs new file mode 100644 index 000000000..3975468e7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/BoundsSpecified.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// Specifies the bounds of the control to + /// use when defining a control's size and position. + /// + /// + [Flags] + public enum BoundsSpecified { + /// + /// + /// + /// Specifies the + /// left + /// edge of the + /// control is defined. + /// + /// + X = 0x1, + /// + /// + /// + /// Specifies the + /// top edge of the + /// control of the + /// control is defined. + /// + /// + Y = 0x2, + /// + /// + /// + /// Specifies + /// the width + /// of the control is defined. + /// + /// + Width = 0x4, + /// + /// + /// + /// Specifies + /// the + /// height of the control is defined. + /// + /// + Height = 0x8, + /// + /// + /// + /// Both and coordinates of the control are + /// defined. + /// + /// + Location = X | Y, + /// + /// + /// + /// Both and property values of the control are + /// defined. + /// + /// + Size = Width | Height, + /// + /// + /// + /// Both and property values are + /// defined. + /// + /// + All = Location | Size, + /// + /// + /// + /// No + /// bounds + /// are specified. + /// + /// + None = 0, + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/Button.cs b/WindowsForms/Managed/System/WinForms/Button.cs new file mode 100644 index 000000000..f3b298868 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Button.cs @@ -0,0 +1,423 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Windows.Forms.ButtonInternal; + using System.ComponentModel.Design; + using System.ComponentModel; + using System.Windows.Forms.Layout; + + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + + /// + /// + /// Represents a + /// Windows button. + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + SRDescription(SR.DescriptionButton), + Designer("System.Windows.Forms.Design.ButtonBaseDesigner, " + AssemblyRef.SystemDesign) + ] + public class Button : ButtonBase, IButtonControl { + + /// + /// + /// The dialog result that will be sent to the parent dialog form when + /// we are clicked. + /// + private DialogResult dialogResult; + + private const int InvalidDimensionValue = Int32.MinValue; + + /// + /// + /// For buttons whose FaltStyle = FlatStyle.Flat, this property specifies the size, in pixels + /// of the border around the button. + /// + private Size systemSize = new Size(InvalidDimensionValue, InvalidDimensionValue); + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public Button() : base() { + // Buttons shouldn't respond to right clicks, so we need to do all our own click logic + SetStyle(ControlStyles.StandardClick | + ControlStyles.StandardDoubleClick, + false); + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true), + SRDescription(SR.ControlAutoSizeModeDescr) + ] + public AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + if(ParentInternal != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(ParentInternal.LayoutEngine == DefaultLayout.Instance) { + ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize); + } + } + } + } + + internal override ButtonBaseAdapter CreateFlatAdapter() { + return new ButtonFlatAdapter(this); + } + + internal override ButtonBaseAdapter CreatePopupAdapter() { + return new ButtonPopupAdapter(this); + } + + internal override ButtonBaseAdapter CreateStandardAdapter() { + return new ButtonStandardAdapter(this); + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + if(FlatStyle != FlatStyle.System) { + Size prefSize = base.GetPreferredSizeCore(proposedConstraints); + return AutoSizeMode == AutoSizeMode.GrowAndShrink ? prefSize : LayoutUtils.UnionSizes(prefSize, Size); + } + + if (systemSize.Width == InvalidDimensionValue) { + Size requiredSize; + // Note: The result from the BCM_GETIDEALSIZE message isn't accurate if the font has been + // changed, because this method is called before the font is set into the device context. + // Commenting this line is the fix for bug VSWhidbey#228843. + //if(UnsafeNativeMethods.SendMessage(hWnd, NativeMethods.BCM_GETIDEALSIZE, 0, size) != IntPtr.Zero) { + // requiredSize = size.ToSize(); ... + requiredSize = TextRenderer.MeasureText(this.Text, this.Font); + requiredSize = SizeFromClientSize(requiredSize); + + // This padding makes FlatStyle.System about the same size as FlatStyle.Standard + // with an 8px font. + requiredSize.Width += 14; + requiredSize.Height += 9; + systemSize = requiredSize; + } + Size paddedSize = systemSize + Padding.Size; + return AutoSizeMode == AutoSizeMode.GrowAndShrink ? paddedSize : LayoutUtils.UnionSizes(paddedSize, Size); + } + + /// + /// + /// + /// + /// This is called when creating a window. Inheriting classes can overide + /// this to add extra functionality, but should not forget to first call + /// base.CreateParams() to make sure the control continues to work + /// correctly. + /// + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "BUTTON"; + if (GetStyle(ControlStyles.UserPaint)) { + cp.Style |= NativeMethods.BS_OWNERDRAW; + } + else { + cp.Style |= NativeMethods.BS_PUSHBUTTON; + if (IsDefault) { + cp.Style |= NativeMethods.BS_DEFPUSHBUTTON; + } + } + return cp; + } + } + + /// + /// + /// + /// Gets or sets a value that is returned to the + /// parent form when the button + /// is clicked. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DialogResult.None), + SRDescription(SR.ButtonDialogResultDescr) + ] + public virtual DialogResult DialogResult { + get { + return dialogResult; + } + + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DialogResult.None, (int) DialogResult.No)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DialogResult)); + } + dialogResult = value; + } + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseEnter(EventArgs e) { + base.OnMouseEnter(e); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + /// + /// Notifies the + /// whether it is the default button so that it can adjust its appearance + /// accordingly. + /// + /// + /// + public virtual void NotifyDefault(bool value) { + if (IsDefault != value) { + IsDefault = value; + } + } + + /// + /// + /// + /// + /// This method actually raises the Click event. Inheriting classes should + /// override this if they wish to be notified of a Click event. (This is far + /// preferable to actually adding an event handler.) They should not, + /// however, forget to call base.onClick(e); before exiting, to ensure that + /// other recipients do actually get the event. + /// + /// + /// + protected override void OnClick(EventArgs e) { + Form form = FindFormInternal(); + if (form != null) form.DialogResult = dialogResult; + + + // accessibility stuff + // + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + + base.OnClick(e); + } + + protected override void OnFontChanged(EventArgs e) { + systemSize = new Size(InvalidDimensionValue, InvalidDimensionValue); + base.OnFontChanged(e); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + if (mevent.Button == MouseButtons.Left && MouseIsPressed) { + bool isMouseDown = base.MouseIsDown; + + if (GetStyle(ControlStyles.UserPaint)) { + //Paint in raised state... + ResetFlagsandPaint(); + } + if (isMouseDown) { + Point pt = PointToScreen(new Point(mevent.X, mevent.Y)); + if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle && !ValidationCancelled) { + if (GetStyle(ControlStyles.UserPaint)) { + OnClick(mevent); + } + OnMouseClick(mevent); + } + } + } + base.OnMouseUp(mevent); + } + + protected override void OnTextChanged(EventArgs e) { + systemSize = new Size(InvalidDimensionValue, InvalidDimensionValue); + base.OnTextChanged(e); + } + + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on. + /// + /// Old DPI value + /// New DPI value + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + // reset cached boundary size - it needs to be recalculated for new DPI + systemSize = new Size(InvalidDimensionValue, InvalidDimensionValue); + } + } + + /// + /// + /// + /// Generates a event for a + /// button. + /// + /// + public void PerformClick() { + if (CanSelect) { + bool validatedControlAllowsFocusChange; + bool validate = ValidateActiveControl(out validatedControlAllowsFocusChange); + if (!ValidationCancelled && (validate || validatedControlAllowsFocusChange)) + { + //Paint in raised state... + // + ResetFlagsandPaint(); + OnClick(EventArgs.Empty); + } + } + } + + /// + /// + /// + /// + /// Lets a control process mnmemonic characters. Inheriting classes can + /// override this to add extra functionality, but should not forget to call + /// base.ProcessMnemonic(charCode); to ensure basic functionality + /// remains unchanged. + /// + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (UseMnemonic && CanProcessMnemonic() && IsMnemonic(charCode, Text)) { + PerformClick(); + return true; + } + return base.ProcessMnemonic(charCode); + } + + /// + /// + /// + /// + /// Provides some interesting information for the Button control in + /// String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Text: " + Text; + } + + /// + /// + /// The button's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the button continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.BN_CLICKED) { + Debug.Assert(!GetStyle(ControlStyles.UserPaint), "Shouldn't get BN_CLICKED when UserPaint"); + if (!ValidationCancelled) { + OnClick(EventArgs.Empty); + } + } + break; + case NativeMethods.WM_ERASEBKGND: + DefWndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ButtonBase.cs b/WindowsForms/Managed/System/WinForms/ButtonBase.cs new file mode 100644 index 000000000..db8952a0b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonBase.cs @@ -0,0 +1,1366 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Drawing.Imaging; + using System; + using System.Security.Permissions; + using System.Drawing.Drawing2D; + using System.Drawing; + using System.Drawing.Design; + using System.Windows.Forms.ButtonInternal; + using System.Windows.Forms.Layout; + using System.ComponentModel; + using Microsoft.Win32; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// + /// Implements the basic functionality required by a button control. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.ButtonBaseDesigner, " + AssemblyRef.SystemDesign) + ] + public abstract class ButtonBase : Control { + + private FlatStyle flatStyle = System.Windows.Forms.FlatStyle.Standard; + private ContentAlignment imageAlign = ContentAlignment.MiddleCenter; + private ContentAlignment textAlign = ContentAlignment.MiddleCenter; + private TextImageRelation textImageRelation = TextImageRelation.Overlay; + private ImageList.Indexer imageIndex = new ImageList.Indexer(); + private FlatButtonAppearance flatAppearance; + private ImageList imageList; + private Image image; + + private const int FlagMouseOver = 0x0001; + private const int FlagMouseDown = 0x0002; + private const int FlagMousePressed = 0x0004; + private const int FlagInButtonUp = 0x0008; + private const int FlagCurrentlyAnimating = 0x0010; + private const int FlagAutoEllipsis = 0x0020; + private const int FlagIsDefault = 0x0040; + private const int FlagUseMnemonic = 0x0080; + private const int FlagShowToolTip = 0x0100; + private int state = 0; + + private ToolTip textToolTip; + + //this allows the user to disable visual styles for the button so that it inherits its background color + private bool enableVisualStyleBackground = true; + + private bool isEnableVisualStyleBackgroundSet = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + protected ButtonBase() { + // If Button doesn't want double-clicks, we should introduce a StandardDoubleClick style. + // Checkboxes probably want double-click's (#26120), and RadioButtons certainly do + // (useful e.g. on a Wizard). + SetStyle( ControlStyles.SupportsTransparentBackColor | + ControlStyles.Opaque | + ControlStyles.ResizeRedraw | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.CacheText | // We gain about 2% in painting by avoiding extra GetWindowText calls + ControlStyles.StandardClick, + true); + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetStyle(ControlStyles.UserMouse | + ControlStyles.UserPaint, OwnerDraw); + SetFlag(FlagUseMnemonic, true); + SetFlag(FlagShowToolTip, false); + } + + /// + /// This property controls the activation handling of bleedover for the text that + /// extends beyond the width of the button. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + SRDescription(SR.ButtonAutoEllipsisDescr) + ] + public bool AutoEllipsis { + get { + return GetFlag(FlagAutoEllipsis); + } + + set { + if (AutoEllipsis != value) { + SetFlag(FlagAutoEllipsis, value); + if (value) { + if (textToolTip == null) { + textToolTip = new ToolTip(); + } + } + Invalidate(); + } + } + } + + /// + /// Indicates whether the control is automatically resized to fit its contents + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + base.AutoSize = value; + //don't show ellipsis if the control is autosized + if (value) { + AutoEllipsis = false; + } + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlBackColorDescr) + ] + public override Color BackColor { + get { + return base.BackColor; + } + set { + if (DesignMode) { + if (value != Color.Empty) { + PropertyDescriptor pd = TypeDescriptor.GetProperties(this)["UseVisualStyleBackColor"]; + Debug.Assert(pd != null); + if (pd != null) { + pd.SetValue(this, false); + } + } + } + else { + UseVisualStyleBackColor = false; + } + + base.BackColor = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(75, 23); + } + } + + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + if (!OwnerDraw) { + cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the BS_XXXX alignment styles + + cp.Style |= NativeMethods.BS_MULTILINE; + + if (IsDefault) { + cp.Style |= NativeMethods.BS_DEFPUSHBUTTON; + } + + ContentAlignment align = RtlTranslateContent(TextAlign); + + if ((int)(align & WindowsFormsUtils.AnyLeftAlign) != 0) { + cp.Style |= NativeMethods.BS_LEFT; + } + else if ((int)(align & WindowsFormsUtils.AnyRightAlign) != 0) { + cp.Style |= NativeMethods.BS_RIGHT; + } + else { + cp.Style |= NativeMethods.BS_CENTER; + + } + if ((int)(align & WindowsFormsUtils.AnyTopAlign) != 0) { + cp.Style |= NativeMethods.BS_TOP; + } + else if ((int)(align & WindowsFormsUtils.AnyBottomAlign) != 0) { + cp.Style |= NativeMethods.BS_BOTTOM; + } + else { + cp.Style |= NativeMethods.BS_VCENTER; + } + } + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// [To be supplied.] + /// + protected internal bool IsDefault { + get { + return GetFlag(FlagIsDefault); + } + set { + if (GetFlag(FlagIsDefault) != value) { + SetFlag(FlagIsDefault, value); + if (IsHandleCreated) { + if (OwnerDraw) { + Invalidate(); + } + else { + UpdateStyles(); + } + } + } + } + } + + /// + /// + /// + /// Gets or + /// sets + /// the flat style appearance of the button control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Standard), + Localizable(true), + SRDescription(SR.ButtonFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { + return flatStyle; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + flatStyle = value; + LayoutTransaction.DoLayoutIf(AutoSize,ParentInternal, this, PropertyNames.FlatStyle); + Invalidate(); + UpdateOwnerDraw(); + } + } + + /// + /// + /// + [ + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonFlatAppearance), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + ] + public FlatButtonAppearance FlatAppearance { + get { + if (flatAppearance == null) { + flatAppearance = new FlatButtonAppearance(this); + } + + return flatAppearance; + } + } + + /// + /// + /// + /// Gets or sets the image + /// that is displayed on a button control. + /// + /// + [ + SRDescription(SR.ButtonImageDescr), + Localizable(true), + SRCategory(SR.CatAppearance) + ] + public Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (image == null && imageList != null) { + int actualIndex = imageIndex.ActualIndex; + + // Pre-whidbey we used to use ImageIndex rather than ImageIndexer.ActualIndex. + // ImageIndex clamps to the length of the image list. We need to replicate + // this logic here for backwards compatibility. (VSWhidbey #95780) + // Per Microsoft we do not bake this into ImageIndexer because different controls + // treat this scenario differently. + if(actualIndex >= imageList.Images.Count) { + actualIndex = imageList.Images.Count - 1; + } + + if (actualIndex >= 0) { + return imageList.Images[actualIndex]; + } + Debug.Assert(image == null, "We expect to be returning null."); + } + return image; + } + set { + if (Image != value) { + StopAnimate(); + + image = value; + if (image != null) { + ImageIndex = -1; + ImageList = null; + } + + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.Image); + Animate(); + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the alignment of the image on the button control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRDescription(SR.ButtonImageAlignDescr), + SRCategory(SR.CatAppearance) + ] + public ContentAlignment ImageAlign { + get { + return imageAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if(value != imageAlign) { + imageAlign = value; + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.ImageAlign); + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the image list index value of the image + /// displayed on the button control. + /// + /// + [ + TypeConverterAttribute(typeof(ImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable(true), + DefaultValue(-1), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ButtonImageIndexDescr), + SRCategory(SR.CatAppearance) + ] + public int ImageIndex { + get { + if (imageIndex.Index != -1 && imageList != null && imageIndex.Index >= imageList.Images.Count) { + return imageList.Images.Count - 1; + } + return imageIndex.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", (value).ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + if (imageIndex.Index != value) { + if (value != -1) { + // Image.set calls ImageIndex = -1 + image = null; + } + + // If they were previously using keys - this should clear out the image key field. + imageIndex.Index = value; + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the image list index key of the image + /// displayed on the button control. Note - setting this unsets the ImageIndex + /// + /// + [ + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable(true), + DefaultValue(""), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ButtonImageIndexDescr), + SRCategory(SR.CatAppearance) + ] + public string ImageKey { + get { + return imageIndex.Key; + } + set { + if (imageIndex.Key != value) { + if (value != null) { + // Image.set calls ImageIndex = -1 + image = null; + } + + // If they were previously using indexes - this should clear out the image index field. + imageIndex.Key = value; + Invalidate(); + } + } + } + + + /// + /// + /// + /// Gets or sets the that contains the displayed on a button control. + /// + /// + [ + DefaultValue(null), + SRDescription(SR.ButtonImageListDescr), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatAppearance) + ] + public ImageList ImageList { + get { + return imageList; + } + set { + if (imageList != value) { + EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + // Detach old event handlers + // + if (imageList != null) { + imageList.RecreateHandle -= recreateHandler; + imageList.Disposed -= disposedHandler; + } + + // Make sure we don't have an Image as well as an ImageList + // + if (value != null) { + image = null; // Image.set calls ImageList = null + } + + imageList = value; + imageIndex.ImageList = value; + + // Wire up new event handlers + // + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + + + Invalidate(); + } + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal override bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + /// + /// + /// The area of the button encompassing any changes between the button's + /// resting appearance and its appearance when the mouse is over it. + /// + /// + /// Consider overriding this property if you override any painting methods, + /// or your button may not paint correctly or may have flicker. Returning + /// ClientRectangle is safe for correct painting but may still cause flicker. + /// + /// + internal virtual Rectangle OverChangeRectangle { + get { + if (FlatStyle == FlatStyle.Standard) { + // this Rectangle will cause no Invalidation + // can't use Rectangle.Empty because it will cause Invalidate(ClientRectangle) + return new Rectangle(-1, -1, 1, 1); + } + else { + return ClientRectangle; + } + } + } + + internal bool OwnerDraw { + get { + return FlatStyle != FlatStyle.System; + } + } + + /// + /// + /// The area of the button encompassing any changes between the button's + /// appearance when the mouse is over it but not pressed and when it is pressed. + /// + /// + /// Consider overriding this property if you override any painting methods, + /// or your button may not paint correctly or may have flicker. Returning + /// ClientRectangle is safe for correct painting but may still cause flicker. + /// + /// + internal virtual Rectangle DownChangeRectangle { + get { + return ClientRectangle; + } + } + + internal bool MouseIsPressed { + get { + return GetFlag(FlagMousePressed); + } + } + + // a "smart" version of mouseDown for Appearance.Button CheckBoxes & RadioButtons + // for these, instead of being based on the actual mouse state, it's based on the appropriate button state + internal bool MouseIsDown { + get { + return GetFlag(FlagMouseDown); + } + } + + // a "smart" version of mouseOver for Appearance.Button CheckBoxes & RadioButtons + // for these, instead of being based on the actual mouse state, it's based on the appropriate button state + internal bool MouseIsOver { + get { + return GetFlag(FlagMouseOver); + } + } + + + /// + /// Indicates whether the tooltip should be shown + /// + internal bool ShowToolTip { + get { + return GetFlag(FlagShowToolTip); + } + set { + SetFlag(FlagShowToolTip, value); + } + } + + [ + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SettingsBindable(true) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + /// + /// Gets or sets the alignment of the text on the button control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRDescription(SR.ButtonTextAlignDescr), + SRCategory(SR.CatAppearance) + ] + public virtual ContentAlignment TextAlign { + get { + return textAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if(value != textAlign) { + textAlign = value; + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.TextAlign); + if (OwnerDraw) { + Invalidate(); + } + else { + UpdateStyles(); + } + } + } + } + + /// + [DefaultValue(TextImageRelation.Overlay), + Localizable(true), + SRDescription(SR.ButtonTextImageRelationDescr), + SRCategory(SR.CatAppearance)] + public TextImageRelation TextImageRelation { + get { + return textImageRelation; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidTextImageRelation(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(TextImageRelation)); + } + if(value != TextImageRelation) { + textImageRelation = value; + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.TextImageRelation); + Invalidate(); + } + } + } + + /// + /// Gets or sets a value indicating whether an ampersand (&) included in the text of + /// the control. + /// + [ + SRDescription(SR.ButtonUseMnemonicDescr), + DefaultValue(true), + SRCategory(SR.CatAppearance) + ] + public bool UseMnemonic { + get { + return GetFlag(FlagUseMnemonic); + } + + set { + SetFlag(FlagUseMnemonic, value); + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.Text); + Invalidate(); + } + } + + private void Animate() { + Animate(!DesignMode && Visible && Enabled && ParentInternal != null); + } + + private void StopAnimate() { + Animate(false); + } + + private void Animate(bool animate) { + if (animate != GetFlag(FlagCurrentlyAnimating)) { + if (animate) { + if (this.image != null) { + ImageAnimator.Animate(this.image, new EventHandler(this.OnFrameChanged)); + SetFlag(FlagCurrentlyAnimating, animate); + } + } + else { + if (this.image != null) { + ImageAnimator.StopAnimate(this.image, new EventHandler(this.OnFrameChanged)); + SetFlag(FlagCurrentlyAnimating, animate); + } + } + } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ButtonBaseAccessibleObject(this); + } + + private void DetachImageList(object sender, EventArgs e) { + ImageList = null; + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + StopAnimate(); + if (imageList != null) { + imageList.Disposed -= new EventHandler(this.DetachImageList); + } + //Dipose the tooltip if one present.. + if (textToolTip != null) { + textToolTip.Dispose(); + textToolTip = null; + } + } + base.Dispose(disposing); + } + + private bool GetFlag(int flag) { + return ((state & flag) == flag); + } + + private void ImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + Invalidate(); + } + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnGotFocus(EventArgs e) { + base.OnGotFocus(e); + Invalidate(); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + + // Hitting tab while holding down the space key. See ASURT 38669. + SetFlag(FlagMouseDown, false); + CaptureInternal = false; + + Invalidate(); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseEnter(EventArgs eventargs) { + Debug.Assert(Enabled, "ButtonBase.OnMouseEnter should not be called if the button is disabled"); + SetFlag(FlagMouseOver, true); + Invalidate(); + if (!DesignMode && AutoEllipsis && ShowToolTip && textToolTip != null) { + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + textToolTip.Show(WindowsFormsUtils.TextWithoutMnemonics(Text), this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnMouseEnter(eventargs); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseLeave(EventArgs eventargs) { + SetFlag(FlagMouseOver, false); + if (textToolTip != null) { + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + textToolTip.Hide(this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + Invalidate(); + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnMouseLeave(eventargs); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseMove(MouseEventArgs mevent) { + Debug.Assert(Enabled, "ButtonBase.OnMouseMove should not be called if the button is disabled"); + if (mevent.Button != MouseButtons.None && GetFlag(FlagMousePressed)) { + Rectangle r = ClientRectangle; + if (!r.Contains(mevent.X, mevent.Y)) { + if (GetFlag(FlagMouseDown)) { + SetFlag(FlagMouseDown, false); + Invalidate(DownChangeRectangle); + } + } + else { + if (!GetFlag(FlagMouseDown)) { + SetFlag(FlagMouseDown, true); + Invalidate(DownChangeRectangle); + } + } + } + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnMouseMove(mevent); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseDown(MouseEventArgs mevent) { + Debug.Assert(Enabled, "ButtonBase.OnMouseDown should not be called if the button is disabled"); + if (mevent.Button == MouseButtons.Left) { + SetFlag(FlagMouseDown, true); + SetFlag(FlagMousePressed, true); + Invalidate(DownChangeRectangle); + } + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnMouseDown(mevent); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + base.OnMouseUp(mevent); + } + + /// + /// + /// + /// + /// Used for quick re-painting of the button after the pressed state. + /// + /// + protected void ResetFlagsandPaint() { + SetFlag(FlagMousePressed, false); + SetFlag(FlagMouseDown, false); + Invalidate(DownChangeRectangle); + Update(); + } + + /// + /// Central paint dispatcher to one of the three styles of painting. + /// + private void PaintControl(PaintEventArgs pevent) { + Debug.Assert(GetStyle(ControlStyles.UserPaint), "Shouldn't be in PaintControl when control is not UserPaint style"); + Adapter.Paint(pevent); + } + + public override Size GetPreferredSize( Size proposedSize ) { + // TableLayoutPanel passes width = 1 to get the minimum autosize width, since Buttons word-break text + // that width would be the size of the widest caracter in the text. We need to make the proposed size + // unbounded. + // This is the same as what Label does. + if( proposedSize.Width == 1 ) { + proposedSize.Width = 0; + } + if( proposedSize.Height == 1 ) { + proposedSize.Height = 0; + } + return base.GetPreferredSize( proposedSize ); + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + Size prefSize = Adapter.GetPreferredSizeCore(proposedConstraints); + return LayoutUtils.UnionSizes(prefSize + Padding.Size, MinimumSize); + } + + private ButtonBaseAdapter _adapter = null; + private FlatStyle _cachedAdapterType; + + internal ButtonBaseAdapter Adapter { + get { + if(_adapter == null || FlatStyle != _cachedAdapterType) { + switch (FlatStyle) { + case FlatStyle.Standard: + _adapter = CreateStandardAdapter(); + break; + case FlatStyle.Popup: + _adapter = CreatePopupAdapter(); + break; + case FlatStyle.Flat: + _adapter = CreateFlatAdapter();; + break; + default: + Debug.Fail("Unsupported FlatStyle: '" + FlatStyle + '"'); + break; + } + _cachedAdapterType = FlatStyle; + } + return _adapter; + } + } + + internal virtual ButtonBaseAdapter CreateFlatAdapter() { + Debug.Fail("Derived classes need to provide a meaningful implementation."); + return null; + } + + internal virtual ButtonBaseAdapter CreatePopupAdapter() { + Debug.Fail("Derived classes need to provide a meaningful implementation."); + return null; + } + + internal virtual ButtonBaseAdapter CreateStandardAdapter() { + Debug.Fail("Derived classes need to provide a meaningful implementation."); + return null; + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal virtual StringFormat CreateStringFormat() { + if( Adapter == null ) { + Debug.Fail("Adapter not expected to be null at this point"); + return new StringFormat(); + } + return Adapter.CreateStringFormat(); + } + + internal virtual TextFormatFlags CreateTextFormatFlags() { + if( Adapter == null ) { + Debug.Fail( "Adapter not expected to be null at this point" ); + return TextFormatFlags.Default; + } + return Adapter.CreateTextFormatFlags(); + } + + private void OnFrameChanged(object o, EventArgs e) { + if (Disposing || IsDisposed) { + return; + } + if (IsHandleCreated && InvokeRequired) { + BeginInvoke(new EventHandler(this.OnFrameChanged), new object[]{o,e}); + return; + } + + Invalidate(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + Animate(); + if (!Enabled) { + // disabled button is always "up" + SetFlag(FlagMouseDown, false); + SetFlag(FlagMouseOver, false); + Invalidate(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnTextChanged(EventArgs e) { + using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { + base.OnTextChanged(e); + Invalidate(); + } + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnKeyDown(KeyEventArgs kevent) { + Debug.Assert(Enabled, "ButtonBase.OnKeyDown should not be called if the button is disabled"); + if (kevent.KeyData == Keys.Space) { + if (!GetFlag(FlagMouseDown)) { + SetFlag(FlagMouseDown, true); + // It looks like none of the "SPACE" key downs generate the BM_SETSTATE. + // This causes to not draw the focus rectangle inside the button and also + // not paint the button as "un-depressed". + // + if(!OwnerDraw) { + SendMessage(NativeMethods.BM_SETSTATE, 1, 0); + } + Invalidate(DownChangeRectangle); + } + kevent.Handled = true; + } + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnKeyDown(kevent); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnKeyUp(KeyEventArgs kevent) { + if (GetFlag(FlagMouseDown) && !ValidationCancelled) { + if (OwnerDraw) { + ResetFlagsandPaint(); + } + else { + SetFlag(FlagMousePressed, false); + SetFlag(FlagMouseDown, false); + SendMessage(NativeMethods.BM_SETSTATE, 0, 0); + } + // VSWhidbey 498398: Breaking change: specifically filter out Keys.Enter and Keys.Space as the only + // two keystrokes to execute OnClick. + if (kevent.KeyCode == Keys.Enter || kevent.KeyCode == Keys.Space) { + OnClick(EventArgs.Empty); + } + kevent.Handled = true; + } + // call base last, so if it invokes any listeners that disable the button, we + // don't have to recheck + base.OnKeyUp(kevent); + + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnPaint(PaintEventArgs pevent) { + if( AutoEllipsis ){ + Size preferredSize = PreferredSize; + ShowToolTip = (this.ClientRectangle.Width < preferredSize.Width || this.ClientRectangle.Height < preferredSize.Height); + } + else { + ShowToolTip = false; + } + + if (GetStyle(ControlStyles.UserPaint)) { + Animate(); + ImageAnimator.UpdateFrames(this.Image); + + PaintControl(pevent); + } + base.OnPaint(pevent); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + Animate(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + Animate(); + } + + private void ResetImage() { + Image = null; + } + + private void SetFlag(int flag, bool value) { + bool oldValue = ((state & flag) != 0); + + if (value) { + state |= flag; + } + else { + state &= ~flag; + } + + if (OwnerDraw && (flag & FlagMouseDown) != 0 && value != oldValue) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + } + } + + private bool ShouldSerializeImage() { + return image != null; + } + + private void UpdateOwnerDraw() { + if (OwnerDraw != GetStyle(ControlStyles.UserPaint)) { + SetStyle(ControlStyles.UserMouse | ControlStyles.UserPaint, OwnerDraw); + RecreateHandle(); + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public bool UseCompatibleTextRendering { + get{ + return base.UseCompatibleTextRenderingInt; + } + set{ + base.UseCompatibleTextRenderingInt = value; + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same + /// value if the child control supports it. + /// + internal override bool SupportsUseCompatibleTextRendering { + get { + return true; + } + } + + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonUseVisualStyleBackColorDescr) + ] + public bool UseVisualStyleBackColor { + get { + if (isEnableVisualStyleBackgroundSet || ((RawBackColor.IsEmpty) && (BackColor == SystemColors.Control))) + { + return enableVisualStyleBackground; + } + else + { + return false; + } + + } + set { + isEnableVisualStyleBackgroundSet = true; + enableVisualStyleBackground = value; + this.Invalidate(); + } + } + + private void ResetUseVisualStyleBackColor() { + isEnableVisualStyleBackgroundSet = false; + enableVisualStyleBackground = true; + this.Invalidate(); + } + + private bool ShouldSerializeUseVisualStyleBackColor() { + return isEnableVisualStyleBackgroundSet; + } + + + /// + /// + /// [To be supplied.] + /// + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + switch (m.Msg) { + // NDPWhidbey 28043 -- we don't respect this because the code below eats BM_SETSTATE. + // so we just invoke the click. + // + case NativeMethods.BM_CLICK: + if (this is IButtonControl) { + ((IButtonControl)this).PerformClick(); + } + else { + OnClick(EventArgs.Empty); + } + return; + } + + if (OwnerDraw) { + switch (m.Msg) { + case NativeMethods.BM_SETSTATE: + // Ignore BM_SETSTATE -- Windows gets confused and paints + // things, even though we are ownerdraw. See ASURT 38669. + break; + + case NativeMethods.WM_KILLFOCUS: + case NativeMethods.WM_CANCELMODE: + case NativeMethods.WM_CAPTURECHANGED: + if (!GetFlag(FlagInButtonUp) && GetFlag(FlagMousePressed)) { + SetFlag(FlagMousePressed, false); + + if (GetFlag(FlagMouseDown)) { + SetFlag(FlagMouseDown, false); + Invalidate(DownChangeRectangle); + } + } + base.WndProc(ref m); + break; + + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_MBUTTONUP: + case NativeMethods.WM_RBUTTONUP: + try { + SetFlag(FlagInButtonUp, true); + base.WndProc(ref m); + } + finally { + SetFlag(FlagInButtonUp, false); + } + break; + + default: + base.WndProc(ref m); + break; + } + } + else { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.BN_CLICKED && !ValidationCancelled) { + OnClick(EventArgs.Empty); + } + break; + default: + base.WndProc(ref m); + break; + } + } + } + + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ButtonBaseAccessibleObject : ControlAccessibleObject { + + /// + public ButtonBaseAccessibleObject(Control owner) : base(owner) { + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ((ButtonBase)Owner).OnClick(EventArgs.Empty); + } + + /// + public override AccessibleStates State { + get { + AccessibleStates state = base.State; + + ButtonBase owner = (ButtonBase) Owner; + if (owner.OwnerDraw && owner.MouseIsDown) { + state |= AccessibleStates.Pressed; + } + + return state; + } + } + + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ButtonBorderStyle.cs b/WindowsForms/Managed/System/WinForms/ButtonBorderStyle.cs new file mode 100644 index 000000000..3362e12d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonBorderStyle.cs @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies + /// the border style for a button control. + /// + /// + public enum ButtonBorderStyle { + /// + /// + /// + /// No border. + /// + /// + None, + /// + /// + /// + /// A dotted-line border. + /// + /// + Dotted, + /// + /// + /// + /// A dashed border. + /// + /// + Dashed, + /// + /// + /// + /// A solid border. + /// + /// + Solid, + /// + /// + /// + /// A sunken border. + /// + /// + Inset, + /// + /// + /// + /// A raised border. + /// + /// + Outset, + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonBaseAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonBaseAdapter.cs new file mode 100644 index 000000000..95e144324 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonBaseAdapter.cs @@ -0,0 +1,1610 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Collections.Specialized; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Runtime.Versioning; + using Runtime.CompilerServices; + + /// + /// PLEASE READ + /// ----------- + /// This class is used for more than just Button: + /// it's used for things that derive from ButtonBase, + /// parts of ToolStripItem, and parts of the DataGridView. + /// + internal abstract class ButtonBaseAdapter { + private ButtonBase control; + + // SystemInformation.Border3DSize + 2 pixels for focus rect + protected static int buttonBorderSize = 4; + + internal ButtonBaseAdapter(ButtonBase control) { + this.control = control; + } + + protected ButtonBase Control { + get { return this.control; } + } + + internal void Paint(PaintEventArgs pevent) { + if (Control.MouseIsDown) { + PaintDown(pevent, CheckState.Unchecked); + } + else if (Control.MouseIsOver) { + PaintOver(pevent, CheckState.Unchecked); + } + else { + PaintUp(pevent, CheckState.Unchecked); + } + } + + internal virtual Size GetPreferredSizeCore(Size proposedSize) { + // this is a shared cached graphics, therefore it does not require dispose. + using (Graphics measurementGraphics = WindowsFormsUtils.CreateMeasurementGraphics()) { + using (PaintEventArgs pe = new PaintEventArgs(measurementGraphics, new Rectangle())) { + LayoutOptions options = Layout(pe); + return options.GetPreferredSizeCore(proposedSize); + } + } + } + + protected abstract LayoutOptions Layout(PaintEventArgs e); + + internal abstract void PaintUp(PaintEventArgs e, CheckState state); + + internal abstract void PaintDown(PaintEventArgs e, CheckState state); + + internal abstract void PaintOver(PaintEventArgs e, CheckState state); + + #region Accessibility Helpers + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected bool IsHighContrastHighlighted() { + return AccessibilityImprovements.Level1 && IsHighContrastHighlightedInternal(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected bool IsHighContrastHighlighted2() { + return AccessibilityImprovements.Level2 && IsHighContrastHighlightedInternal(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsHighContrastHighlightedInternal() { + return SystemInformation.HighContrast && Application.RenderWithVisualStyles && + (Control.Focused || Control.MouseIsOver || (Control.IsDefault && Control.Enabled)); + } + +#endregion + + #region Drawing Helpers + + internal static Color MixedColor(Color color1, Color color2) { + byte a1 = color1.A; + byte r1 = color1.R; + byte g1 = color1.G; + byte b1 = color1.B; + + byte a2 = color2.A; + byte r2 = color2.R; + byte g2 = color2.G; + byte b2 = color2.B; + + int a3 = (a1 + a2) / 2; + int r3 = (r1 + r2) / 2; + int g3 = (g1 + g2) / 2; + int b3 = (b1 + b2) / 2; + + return Color.FromArgb(a3, r3, g3, b3); + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Machine)] + internal static Brush CreateDitherBrush(Color color1, Color color2) { + + // Note: Don't dispose the bitmap here. The texture brush will take ownership + // of the bitmap. So the bitmap will get disposed by the brush's Dispose(). + + using (Bitmap b = new Bitmap(2, 2)) { + b.SetPixel(0, 0, color1); + b.SetPixel(0, 1, color2); + b.SetPixel(1, 1, color1); + b.SetPixel(1, 0, color2); + + return new TextureBrush(b); + } + } + + /// + /// Get StringFormat object for rendering text using GDI+ (Graphics). + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal virtual StringFormat CreateStringFormat() { + return ControlPaint.CreateStringFormat( Control, Control.TextAlign, Control.ShowToolTip, Control.UseMnemonic ); + } + + /// + /// Get TextFormatFlags flags for rendering text using GDI (TextRenderer). + /// + internal virtual TextFormatFlags CreateTextFormatFlags(){ + return ControlPaint.CreateTextFormatFlags( Control, Control.TextAlign, Control.ShowToolTip, Control.UseMnemonic ); + } + + internal static void DrawDitheredFill(Graphics g, Color color1, Color color2, Rectangle bounds) { + using (Brush brush = CreateDitherBrush(color1, color2)) { + g.FillRectangle(brush, bounds); + } + } + + protected void Draw3DBorder(Graphics g, Rectangle bounds, ColorData colors, bool raised) { + if (Control.BackColor != SystemColors.Control && SystemInformation.HighContrast) { + if (raised) { + Draw3DBorderHighContrastRaised(g, ref bounds, colors); + } + else { + ControlPaint.DrawBorder(g, bounds, ControlPaint.Dark(Control.BackColor), ButtonBorderStyle.Solid); + } + } + else { + if (raised) { + Draw3DBorderRaised(g, ref bounds, colors); + } + else { + Draw3DBorderNormal(g, ref bounds, colors); + } + } + } + + + private void Draw3DBorderHighContrastRaised(Graphics g, ref Rectangle bounds, ColorData colors) { + bool stockColor = colors.buttonFace.ToKnownColor() == SystemColors.Control.ToKnownColor(); + bool disabledHighContrast = (!Control.Enabled) && SystemInformation.HighContrast && AccessibilityImprovements.Level1; + + using ( WindowsGraphics wg = WindowsGraphics.FromGraphics(g) ) { + + // Draw counter-clock-wise. + Point p1 = new Point(bounds.X + bounds.Width - 1, bounds.Y ); // upper inner right. + Point p2 = new Point(bounds.X , bounds.Y ); // upper left. + Point p3 = new Point(bounds.X , bounds.Y + bounds.Height - 1 ); // bottom inner left. + Point p4 = new Point(bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1 ); // inner bottom right. + + WindowsPen penTopLeft = null; + WindowsPen penBottomRight = null; + WindowsPen insetPen = null; + WindowsPen bottomRightInsetPen = null; + + try { + // top + left + if (disabledHighContrast) { + penTopLeft = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else { + penTopLeft = stockColor ? new WindowsPen(wg.DeviceContext, SystemColors.ControlLightLight) : new WindowsPen(wg.DeviceContext, colors.highlight); + } + wg.DrawLine(penTopLeft, p1, p2); // top (right-left) + wg.DrawLine(penTopLeft, p2, p3); // left (up-down) + + // bottom + right + if (disabledHighContrast) { + penBottomRight = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else { + penBottomRight = stockColor ? new WindowsPen(wg.DeviceContext, SystemColors.ControlDarkDark) : new WindowsPen(wg.DeviceContext, colors.buttonShadowDark); + } + p1.Offset(0,-1); // need to paint last pixel too. + wg.DrawLine(penBottomRight, p3, p4); // bottom (left-right) + wg.DrawLine(penBottomRight, p4, p1); // right (bottom-up ) + + // Draw inset using the background color to make the top and left lines thinner + if (stockColor) { + if (SystemInformation.HighContrast) { + insetPen = new WindowsPen(wg.DeviceContext, SystemColors.ControlLight); + } + else { + insetPen = new WindowsPen(wg.DeviceContext, SystemColors.Control); + } + } + else { + if (SystemInformation.HighContrast) { + insetPen = new WindowsPen(wg.DeviceContext, colors.highlight); + } + else { + insetPen = new WindowsPen(wg.DeviceContext, colors.buttonFace); + } + } + + p1.Offset(-1, 2); + p2.Offset( 1, 1); + p3.Offset( 1,-1); + p4.Offset(-1,-1); + + // top + left inset + wg.DrawLine(insetPen, p1, p2); // top (right-left) + wg.DrawLine(insetPen, p2, p3); // left( up-down) + + // Bottom + right inset + if (disabledHighContrast) { + bottomRightInsetPen = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else { + bottomRightInsetPen = stockColor ? new WindowsPen(wg.DeviceContext, SystemColors.ControlDark) : new WindowsPen(wg.DeviceContext, colors.buttonShadow); + } + p1.Offset(0,-1); // need to paint last pixel too. + wg.DrawLine(bottomRightInsetPen, p3, p4); // bottom (left-right) + wg.DrawLine(bottomRightInsetPen, p4, p1); // right (bottom-up) + } + finally { + if (penTopLeft != null) { + penTopLeft.Dispose(); + } + + if (penBottomRight != null) { + penBottomRight.Dispose(); + } + + if (insetPen != null) { + insetPen.Dispose(); + } + + if (bottomRightInsetPen != null) { + bottomRightInsetPen.Dispose(); + } + } + } + } + + private void Draw3DBorderNormal(Graphics g, ref Rectangle bounds, ColorData colors) { + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(g) ) { + + // Draw counter-clock-wise. + Point p1 = new Point(bounds.X + bounds.Width - 1, bounds.Y ); // upper inner right. + Point p2 = new Point(bounds.X , bounds.Y ); // upper left. + Point p3 = new Point(bounds.X , bounds.Y + bounds.Height - 1 ); // bottom inner left. + Point p4 = new Point(bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1 ); // inner bottom right. + + // top + left + WindowsPen pen = new WindowsPen(wg.DeviceContext, colors.buttonShadowDark); + try { + wg.DrawLine(pen, p1, p2); // top (right-left) + wg.DrawLine(pen, p2, p3); // left(up-down) + } + finally { + pen.Dispose(); + } + + // bottom + right + pen = new WindowsPen(wg.DeviceContext, colors.highlight); + try { + p1.Offset(0,-1); // need to paint last pixel too. + wg.DrawLine(pen, p3, p4); // bottom(left-right) + wg.DrawLine(pen, p4, p1); // right (bottom-up) + } + finally { + pen.Dispose(); + } + + // Draw inset + + pen = new WindowsPen(wg.DeviceContext, colors.buttonFace); + + p1.Offset(-1, 2); + p2.Offset( 1, 1); + p3.Offset( 1,-1); + p4.Offset(-1,-1); + + // top + left inset + try { + wg.DrawLine(pen, p1, p2); // top (right-left) + wg.DrawLine(pen, p2, p3); // left(up-down) + } + finally { + pen.Dispose(); + } + + // bottom + right inset + if (colors.buttonFace.ToKnownColor() == SystemColors.Control.ToKnownColor()) { + pen = new WindowsPen(wg.DeviceContext, SystemColors.ControlLight); + } + else { + pen = new WindowsPen(wg.DeviceContext, colors.buttonFace); + } + + try { + p1.Offset(0,-1); // need to paint last pixel too. + wg.DrawLine(pen, p3, p4); // bottom(left-right) + wg.DrawLine(pen, p4, p1); // right (bottom-up) + } + finally { + pen.Dispose(); + } + } + } + + private void Draw3DBorderRaised(Graphics g, ref Rectangle bounds, ColorData colors) { + bool stockColor = colors.buttonFace.ToKnownColor() == SystemColors.Control.ToKnownColor(); + bool disabledHighContrast = (!Control.Enabled) && SystemInformation.HighContrast && AccessibilityImprovements.Level1; + + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(g) ) { + + // Draw counter-clock-wise. + Point p1 = new Point(bounds.X + bounds.Width - 1, bounds.Y ); // upper inner right. + Point p2 = new Point(bounds.X , bounds.Y ); // upper left. + Point p3 = new Point(bounds.X , bounds.Y + bounds.Height - 1 ); // bottom inner left. + Point p4 = new Point(bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1 ); // inner bottom right. + + // Draw counter-clock-wise. + + // top + left + WindowsPen pen; + if (disabledHighContrast) { + pen = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else if (stockColor) { + pen = new WindowsPen(wg.DeviceContext, SystemColors.ControlLightLight); + } + else { + pen = new WindowsPen(wg.DeviceContext, colors.highlight); + } + + try { + wg.DrawLine(pen, p1, p2); // top (right-left) + wg.DrawLine(pen, p2, p3); // left(up-down) + } + finally { + pen.Dispose(); + } + + // bottom + right + if (disabledHighContrast) { + pen = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else if (stockColor) { + pen = new WindowsPen(wg.DeviceContext, SystemColors.ControlDarkDark); + } + else { + pen = new WindowsPen(wg.DeviceContext, colors.buttonShadowDark); + } + + try { + p1.Offset(0, -1); // need to paint last pixel too. + wg.DrawLine(pen, p3, p4); // bottom(left-right) + wg.DrawLine(pen, p4, p1); // right (bottom-up) + } + finally { + pen.Dispose(); + } + + // Draw inset - use the back ground color here to have a thinner border + p1.Offset(-1, 2); + p2.Offset(1, 1); + p3.Offset(1, -1); + p4.Offset(-1, -1); + + if (stockColor) { + if (SystemInformation.HighContrast) { + pen = new WindowsPen(wg.DeviceContext, SystemColors.ControlLight); + } + else { + pen = new WindowsPen(wg.DeviceContext, SystemColors.Control); + } + } + else { + pen = new WindowsPen(wg.DeviceContext, colors.buttonFace); + } + + // top + left inset + try { + wg.DrawLine(pen, p1, p2); // top (right-left) + wg.DrawLine(pen, p2, p3); // left(up-down) + } + finally { + pen.Dispose(); + } + + // Bottom + right inset + if (disabledHighContrast) { + pen = new WindowsPen(wg.DeviceContext, colors.windowDisabled); + } + else if (stockColor) { + pen = new WindowsPen(wg.DeviceContext, SystemColors.ControlDark); + } + else { + pen = new WindowsPen(wg.DeviceContext, colors.buttonShadow); + } + + try { + p1.Offset(0, -1); // need to paint last pixel too. + wg.DrawLine(pen, p3, p4); // bottom(left-right) + wg.DrawLine(pen, p4, p1); // right (bottom-up) + } + finally { + pen.Dispose(); + } + } + } + + /// + /// Draws a border for the in the 3D style of the popup button. + /// + protected internal static void Draw3DLiteBorder(Graphics g, Rectangle r, ColorData colors, bool up) { + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(g) ) { + + // Draw counter-clock-wise. + Point p1 = new Point(r.Right - 1, r.Top ); // upper inner right. + Point p2 = new Point(r.Left , r.Top ); // upper left. + Point p3 = new Point(r.Left , r.Bottom - 1); // bottom inner left. + Point p4 = new Point(r.Right - 1, r.Bottom - 1); // inner bottom right. + + + // top, left + WindowsPen pen = up ? new WindowsPen(wg.DeviceContext, colors.highlight) : new WindowsPen(wg.DeviceContext, colors.buttonShadow); + + try { + wg.DrawLine(pen, p1, p2); // top (right-left) + wg.DrawLine(pen, p2, p3); // left (top-down) + } + finally { + pen.Dispose(); + } + + // bottom, right + pen = up ? new WindowsPen(wg.DeviceContext, colors.buttonShadow) : new WindowsPen(wg.DeviceContext, colors.highlight); + + try { + p1.Offset(0,-1); // need to paint last pixel too. + wg.DrawLine(pen, p3, p4); // bottom (left-right) + wg.DrawLine(pen, p4, p1); // right(bottom-up) + } + finally { + pen.Dispose(); + } + } + } + + internal static void DrawFlatBorder(Graphics g, Rectangle r, Color c) { + ControlPaint.DrawBorder(g, r, c, ButtonBorderStyle.Solid); + } + + /// + /// + /// + /// Draws the flat border with specified bordersize. + /// This function gets called only for Flatstyle == Flatstyle.Flat. + /// + /// + internal static void DrawFlatBorderWithSize(Graphics g, Rectangle r, Color c, int size) { + bool stockBorder = c.IsSystemColor; + SolidBrush brush = null; + + if (size > 1) { + brush = new SolidBrush(c); + } + else { + if (stockBorder) { + brush = (SolidBrush)SystemBrushes.FromSystemColor(c); + } + else { + brush = new SolidBrush(c); + } + } + + try { + size = System.Math.Min(size, System.Math.Min(r.Width, r.Height)); + // ...truncate pen width to button size, to avoid overflow if border size is huge! + + //Left Border + g.FillRectangle(brush, r.X, r.Y, size, r.Height); + + //Right Border + g.FillRectangle(brush, (r.X + r.Width - size), r.Y, size, r.Height); + + //Top Border + g.FillRectangle(brush, (r.X + size), r.Y, (r.Width - size * 2), size); + + //Bottom Border + g.FillRectangle(brush, (r.X + size), (r.Y + r.Height - size), (r.Width - size * 2), size); + } + finally { + if (!stockBorder && brush != null) { + brush.Dispose(); + } + } + } + + internal static void DrawFlatFocus(Graphics g, Rectangle r, Color c) { + using(WindowsGraphics wg = WindowsGraphics.FromGraphics(g)) { + using (WindowsPen focus = new WindowsPen(wg.DeviceContext, c)) { + wg.DrawRectangle(focus, r); + } + } + } + + /// + /// + /// Draws the focus rectangle if the control has focus. + /// + /// + /// + void DrawFocus(Graphics g, Rectangle r) { + if (Control.Focused && Control.ShowFocusCues) { + ControlPaint.DrawFocusRectangle(g, r, Control.ForeColor, Control.BackColor); + } + } + + /// + /// Draws the button's image. + /// + void DrawImage(Graphics graphics, LayoutData layout) { + if (Control.Image != null) { + //setup new clip region & draw + DrawImageCore(graphics, Control.Image, layout.imageBounds, layout.imageStart, layout); + } + } + + // here for DropDownButton + internal virtual void DrawImageCore(Graphics graphics, Image image, Rectangle imageBounds, Point imageStart, LayoutData layout) { + Region oldClip = graphics.Clip; + + if (!layout.options.everettButtonCompat) { // FOR EVERETT COMPATIBILITY - DO NOT CHANGE + Rectangle bounds = new Rectangle(buttonBorderSize, buttonBorderSize, this.Control.Width - (2 * buttonBorderSize), this.Control.Height - (2 * buttonBorderSize)); + + Region newClip = oldClip.Clone(); + newClip.Intersect(bounds); + + // If we don't do this, DrawImageUnscaled will happily draw the entire image, even though imageBounds + // is smaller than the image size. + newClip.Intersect(imageBounds); + graphics.Clip = newClip; + } + else { + // FOR EVERETT COMPATIBILITY - DO NOT CHANGE + imageBounds.Width += 1; + imageBounds.Height +=1; + imageBounds.X = imageStart.X + 1; + imageBounds.Y = imageStart.Y + 1; + } + + + try { + if (!Control.Enabled) + // need to specify width and height + ControlPaint.DrawImageDisabled(graphics, image, imageBounds, Control.BackColor, true /* unscaled image*/); + else { + graphics.DrawImage(image, imageBounds.X, imageBounds.Y, image.Width, image.Height); + } + } + + finally { + if (!layout.options.everettButtonCompat) {// FOR EVERETT COMPATIBILITY - DO NOT CHANGE + graphics.Clip = oldClip; + } + } + } + + internal static void DrawDefaultBorder(Graphics g, Rectangle r, Color c, bool isDefault) { + if (isDefault) { + r.Inflate(1, 1); + + Pen pen; + if (c.IsSystemColor) { + pen = SystemPens.FromSystemColor(c); + } + else { + pen = new Pen(c); + } + g.DrawRectangle(pen, r.X, r.Y, r.Width - 1, r.Height - 1); + if (!c.IsSystemColor) { + pen.Dispose(); + } + } + } + + /// + /// Draws the button's text. Color c is the foreground color set with enabled/disabled state in mind. + /// + void DrawText(Graphics g, LayoutData layout, Color c, ColorData colors) + { + Rectangle r = layout.textBounds; + bool disabledText3D = layout.options.shadowedText; + + if (Control.UseCompatibleTextRendering) { // Draw text using GDI+ + using (StringFormat stringFormat = CreateStringFormat()) { + // DrawString doesn't seem to draw where it says it does + if ((Control.TextAlign & LayoutUtils.AnyCenter) == 0) { + r.X -= 1; + } + r.Width += 1; + if (disabledText3D && !Control.Enabled && + (!AccessibilityImprovements.Level1 || (!colors.options.highContrast && AccessibilityImprovements.Level1))) { + using (SolidBrush brush = new SolidBrush(colors.highlight)) { + r.Offset(1, 1); + g.DrawString(Control.Text, Control.Font, brush, r, stringFormat); + + r.Offset(-1, -1); + brush.Color = colors.buttonShadow; + g.DrawString(Control.Text, Control.Font, brush, r, stringFormat); + } + } + else { + Brush brush; + + if (c.IsSystemColor) { + brush = SystemBrushes.FromSystemColor(c); + } + else { + brush = new SolidBrush(c); + } + g.DrawString(Control.Text, Control.Font, brush, r, stringFormat); + + if (!c.IsSystemColor) { + brush.Dispose(); + } + } + } + } + else { // Draw text using GDI (Whidbey+ feature). + TextFormatFlags formatFlags = CreateTextFormatFlags(); + if (disabledText3D && !Control.Enabled && (!AccessibilityImprovements.Level1 || (!colors.options.highContrast && AccessibilityImprovements.Level1))) { + if (Application.RenderWithVisualStyles) { + //don't draw chiseled text if themed as win32 app does. + TextRenderer.DrawText(g, Control.Text, Control.Font, r, colors.buttonShadow, formatFlags); + } + else { + r.Offset(1, 1); + TextRenderer.DrawText(g, Control.Text, Control.Font, r, colors.highlight, formatFlags); + + r.Offset(-1, -1); + TextRenderer.DrawText(g, Control.Text, Control.Font, r, colors.buttonShadow, formatFlags); + } + } + else { + TextRenderer.DrawText(g, Control.Text, Control.Font, r, c, formatFlags); + } + } + } + +#endregion Drawing Helpers + +#region Draw Content Helpers + + // the DataGridViewButtonCell uses this method + internal static void PaintButtonBackground(WindowsGraphics wg, Rectangle bounds, WindowsBrush background) { + wg.FillRectangle(background, bounds); + } + + internal void PaintButtonBackground(PaintEventArgs e, Rectangle bounds, Brush background) { + if (background == null) { + Control.PaintBackground(e, bounds); + } + else { + e.Graphics.FillRectangle(background, bounds); + } + } + + internal void PaintField(PaintEventArgs e, + LayoutData layout, + ColorData colors, + Color foreColor, + bool drawFocus) { + + Graphics g = e.Graphics; + + Rectangle maxFocus = layout.focus; + + DrawText(g, layout, foreColor, colors); + + if (drawFocus) { + DrawFocus(g, maxFocus); + } + } + + internal void PaintImage(PaintEventArgs e, LayoutData layout) { + Graphics g = e.Graphics; + + DrawImage(g, layout); + } + +#endregion + +#region Color + + internal class ColorOptions { + internal Color backColor; + internal Color foreColor; + internal bool enabled; + internal bool highContrast; + internal Graphics graphics; + + internal ColorOptions(Graphics graphics, Color foreColor, Color backColor) { + this.graphics = graphics; + this.backColor = backColor; + this.foreColor = foreColor; + highContrast = SystemInformation.HighContrast; + } + + internal static int Adjust255(float percentage, int value) { + int v = (int)(percentage * value); + if (v > 255) { + return 255; + } + return v; + } + + internal ColorData Calculate() { + ColorData colors = new ColorData(this); + + colors.buttonFace = backColor; + + if (backColor == SystemColors.Control) { + colors.buttonShadow = SystemColors.ControlDark; + colors.buttonShadowDark = SystemColors.ControlDarkDark; + colors.highlight = SystemColors.ControlLightLight; + } + else { + if (!highContrast) { + colors.buttonShadow = ControlPaint.Dark(backColor); + colors.buttonShadowDark = ControlPaint.DarkDark(backColor); + colors.highlight = ControlPaint.LightLight(backColor); + } + else { + colors.buttonShadow = ControlPaint.Dark(backColor); + colors.buttonShadowDark = ControlPaint.LightLight(backColor); + colors.highlight = ControlPaint.LightLight(backColor); + } + } + colors.windowDisabled = (highContrast && AccessibilityImprovements.Level1) ? SystemColors.GrayText : colors.buttonShadow; + + const float lowlight = .1f; + float adjust = 1 - lowlight; + + if (colors.buttonFace.GetBrightness() < .5) { + adjust = 1 + lowlight * 2; + } + colors.lowButtonFace = Color.FromArgb(Adjust255(adjust, colors.buttonFace.R), + Adjust255(adjust, colors.buttonFace.G), + Adjust255(adjust, colors.buttonFace.B)); + + adjust = 1 - lowlight; + if (colors.highlight.GetBrightness() < .5) { + adjust = 1 + lowlight * 2; + } + colors.lowHighlight = Color.FromArgb(Adjust255(adjust, colors.highlight.R), + Adjust255(adjust, colors.highlight.G), + Adjust255(adjust, colors.highlight.B)); + + if (highContrast && backColor != SystemColors.Control) { + colors.highlight = colors.lowHighlight; + } + + colors.windowFrame = foreColor; + + + /* debug * / + colors.buttonFace = Color.Yellow; + colors.buttonShadow = Color.Blue; + colors.highlight = Color.Brown; + colors.lowButtonFace = Color.Beige; + colors.lowHighlight = Color.Cyan; + colors.windowFrame = Color.Red; + colors.windowText = Color.Green; + / * debug */ + + + if (colors.buttonFace.GetBrightness() < .5) { + colors.constrastButtonShadow = colors.lowHighlight; + } + else { + colors.constrastButtonShadow = colors.buttonShadow; + } + + if (!enabled) { + colors.windowText = colors.windowDisabled; + if (highContrast && AccessibilityImprovements.Level1) { + colors.windowFrame = colors.windowDisabled; + colors.buttonShadow = colors.windowDisabled; + } + } + else { + colors.windowText = colors.windowFrame; + } + + IntPtr hdc = this.graphics.GetHdc(); + + try + { + using (WindowsGraphics wg = WindowsGraphics.FromHdc(hdc)) { + colors.buttonFace = wg.GetNearestColor(colors.buttonFace); + colors.buttonShadow = wg.GetNearestColor(colors.buttonShadow); + colors.buttonShadowDark = wg.GetNearestColor(colors.buttonShadowDark); + colors.constrastButtonShadow = wg.GetNearestColor(colors.constrastButtonShadow); + colors.windowText = wg.GetNearestColor(colors.windowText); + colors.highlight = wg.GetNearestColor(colors.highlight); + colors.lowHighlight = wg.GetNearestColor(colors.lowHighlight); + colors.lowButtonFace = wg.GetNearestColor(colors.lowButtonFace); + colors.windowFrame = wg.GetNearestColor(colors.windowFrame); + colors.windowDisabled = wg.GetNearestColor(colors.windowDisabled); + } + } + finally + { + this.graphics.ReleaseHdc(); + } + + return colors; + } + } + + internal class ColorData { + internal Color buttonFace; + internal Color buttonShadow; + internal Color buttonShadowDark; + internal Color constrastButtonShadow; + internal Color windowText; + internal Color windowDisabled; + internal Color highlight; + internal Color lowHighlight; + internal Color lowButtonFace; + internal Color windowFrame; + + internal ColorOptions options; + + internal ColorData(ColorOptions options) { + this.options = options; + } + } + +#endregion + +#region Layout + + internal class LayoutOptions { + internal Rectangle client; + internal bool growBorderBy1PxWhenDefault; + internal bool isDefault; + internal int borderSize; + internal int paddingSize; + internal bool maxFocus; + internal bool focusOddEvenFixup; + internal Font font; + internal string text; + internal Size imageSize; + internal int checkSize; + internal int checkPaddingSize; + internal ContentAlignment checkAlign; + internal ContentAlignment imageAlign; + internal ContentAlignment textAlign; + internal TextImageRelation textImageRelation; + internal bool hintTextUp; + internal bool textOffset; + internal bool shadowedText; + internal bool layoutRTL; + internal bool verticalText = false; + internal bool useCompatibleTextRendering = false; + internal bool everettButtonCompat = true; + internal TextFormatFlags gdiTextFormatFlags = TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl; + internal StringFormatFlags gdipFormatFlags; + internal StringTrimming gdipTrimming; + internal HotkeyPrefix gdipHotkeyPrefix; + internal StringAlignment gdipAlignment; // horizontal alignment. + internal StringAlignment gdipLineAlignment; // vertical alignment. + private bool disableWordWrapping; + + /// + /// We don't cache the StringFormat itself because we don't have a deterministic way of disposing it, instead + /// we cache the flags that make it up and create it on demand so it can be disposed by calling code. + /// + public StringFormat StringFormat { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + StringFormat format = new StringFormat(); + + format.FormatFlags = this.gdipFormatFlags; + format.Trimming = this.gdipTrimming; + format.HotkeyPrefix = this.gdipHotkeyPrefix; + format.Alignment = this.gdipAlignment; + format.LineAlignment = this.gdipLineAlignment; + + if (disableWordWrapping) { + format.FormatFlags |= StringFormatFlags.NoWrap; + } + + return format; + } + set { + this.gdipFormatFlags = value.FormatFlags; + this.gdipTrimming = value.Trimming; + this.gdipHotkeyPrefix = value.HotkeyPrefix; + this.gdipAlignment = value.Alignment; + this.gdipLineAlignment = value.LineAlignment; + } + } + + /// + /// + public TextFormatFlags TextFormatFlags { + get { + if (disableWordWrapping) { + return gdiTextFormatFlags & ~TextFormatFlags.WordBreak; + } + + return gdiTextFormatFlags; + } + //set { + // this.gdiTextFormatFlags = value; + //} + } + + // textImageInset compensates for two factors: 3d text when the button is disabled, + // and moving text on 3d-look buttons. These factors make the text require a couple + // more pixels of space. We inset image by the same amount so they line up. + internal int textImageInset = 2; + + internal Padding padding; + + #region PreferredSize + private static readonly int combineCheck = BitVector32.CreateMask(); + private static readonly int combineImageText = BitVector32.CreateMask(combineCheck); + + private enum Composition { + NoneCombined = 0x00, + CheckCombined = 0x01, + TextImageCombined = 0x02, + AllCombined = 0x03 + } + + // Uses checkAlign, imageAlign, and textAlign to figure out how to compose + // checkSize, imageSize, and textSize into the preferredSize. + private Size Compose(Size checkSize, Size imageSize, Size textSize) { + Composition hComposition = GetHorizontalComposition(); + Composition vComposition = GetVerticalComposition(); + return new Size( + xCompose(hComposition, checkSize.Width, imageSize.Width, textSize.Width), + xCompose(vComposition, checkSize.Height, imageSize.Height, textSize.Height) + ); + } + + private int xCompose(Composition composition, int checkSize, int imageSize, int textSize) { + switch(composition) { + case Composition.NoneCombined: + return checkSize + imageSize + textSize; + case Composition.CheckCombined: + return Math.Max(checkSize, imageSize + textSize); + case Composition.TextImageCombined: + return Math.Max(imageSize, textSize) + checkSize; + case Composition.AllCombined: + return Math.Max(Math.Max(checkSize, imageSize), textSize); + default: + Debug.Fail(SR.GetString(SR.InvalidArgument, "composition", composition.ToString())); + return -7107; + } + } + + // Uses checkAlign, imageAlign, and textAlign to figure out how to decompose + // proposedSize into just the space left over for text. + private Size Decompose(Size checkSize, Size imageSize, Size proposedSize) { + Composition hComposition = GetHorizontalComposition(); + Composition vComposition = GetVerticalComposition(); + return new Size( + xDecompose(hComposition, checkSize.Width, imageSize.Width, proposedSize.Width), + xDecompose(vComposition, checkSize.Height, imageSize.Height, proposedSize.Height) + ); + } + + private int xDecompose(Composition composition, int checkSize, int imageSize, int proposedSize) { + switch(composition) { + case Composition.NoneCombined: + return proposedSize - (checkSize + imageSize); + case Composition.CheckCombined: + return proposedSize - imageSize; + case Composition.TextImageCombined: + return proposedSize - checkSize; + case Composition.AllCombined: + return proposedSize; + default: + Debug.Fail(SR.GetString(SR.InvalidArgument, "composition", composition.ToString())); + return -7109; + } + } + + private Composition GetHorizontalComposition() { + BitVector32 action = new BitVector32(); + + // Checks reserve space horizontally if possible, so only AnyLeft/AnyRight prevents combination. + action[combineCheck] = checkAlign == ContentAlignment.MiddleCenter || !LayoutUtils.IsHorizontalAlignment(checkAlign); + action[combineImageText] = !LayoutUtils.IsHorizontalRelation(textImageRelation); + return (Composition) action.Data; + } + + internal Size GetPreferredSizeCore(Size proposedSize) { + // Get space required for border and padding + // + int linearBorderAndPadding = borderSize*2 + paddingSize*2; + if(growBorderBy1PxWhenDefault) { + linearBorderAndPadding += 2; + } + Size bordersAndPadding = new Size(linearBorderAndPadding, linearBorderAndPadding); + proposedSize -= bordersAndPadding; + + // Get space required for Check + // + int checkSizeLinear = FullCheckSize; + Size checkSize = checkSizeLinear > 0 ? new Size(checkSizeLinear + 1, checkSizeLinear) : Size.Empty; + + // Get space required for Image - textImageInset compensated for by expanding image. + // + Size textImageInsetSize = new Size(textImageInset * 2, textImageInset * 2); + Size requiredImageSize = (imageSize != Size.Empty) ? imageSize + textImageInsetSize : Size.Empty; + + // Pack Text into remaning space + // + proposedSize -= textImageInsetSize; + proposedSize = Decompose(checkSize, requiredImageSize, proposedSize); + + Size textSize = Size.Empty; + + if (!string.IsNullOrEmpty(text)) { + // When Button.AutoSizeMode is set to GrowOnly TableLayoutPanel expects buttons not to automatically wrap on word break. If + // there's enough room for the text to word-wrap then it will happen but the layout would not be adjusted to allow text wrapping. + // If someone has a carriage return in the text we'll honor that for preferred size, but we wont wrap based on constraints. + // See VSW#542448,537840,515227. + try { + this.disableWordWrapping = true; + textSize = GetTextSize(proposedSize) + textImageInsetSize; + } + finally { + this.disableWordWrapping = false; + } + } + + // Combine pieces to get final preferred size + // + Size requiredSize = Compose(checkSize, imageSize, textSize); + requiredSize += bordersAndPadding; + + return requiredSize; + } + + private Composition GetVerticalComposition() { + BitVector32 action = new BitVector32(); + + // Checks reserve space horizontally if possible, so only Top/Bottom prevents combination. + action[combineCheck] = checkAlign == ContentAlignment.MiddleCenter || !LayoutUtils.IsVerticalAlignment(checkAlign); + action[combineImageText] = !LayoutUtils.IsVerticalRelation(textImageRelation); + return (Composition) action.Data; + } + #endregion PreferredSize + + private int FullBorderSize { + get { + int result = borderSize; + if (OnePixExtraBorder) { + borderSize++; + } + return borderSize; + } + } + + private bool OnePixExtraBorder { + get { return growBorderBy1PxWhenDefault && isDefault; } + } + + internal LayoutData Layout() { + LayoutData layout = new LayoutData(this); + layout.client = this.client; + + // subtract border size from layout area + int fullBorderSize = FullBorderSize; + layout.face = Rectangle.Inflate(layout.client, -fullBorderSize, -fullBorderSize); + + // checkBounds, checkArea, field + // + CalcCheckmarkRectangle(layout); + + // imageBounds, imageLocation, textBounds + LayoutTextAndImage(layout); + + // focus + // + if (maxFocus) { + layout.focus = layout.field; + layout.focus.Inflate(-1, -1); + + // Adjust for padding. VSWhidbey #387208 + layout.focus = LayoutUtils.InflateRect(layout.focus, this.padding); + } + else { + Rectangle textAdjusted = new Rectangle(layout.textBounds.X - 1, layout.textBounds.Y - 1, + layout.textBounds.Width + 2, layout.textBounds.Height + 3); + if (imageSize != Size.Empty) { + layout.focus = Rectangle.Union(textAdjusted, layout.imageBounds); + } + else { + layout.focus = textAdjusted; + } + } + if (focusOddEvenFixup) { + if (layout.focus.Height % 2 == 0) { + layout.focus.Y++; + layout.focus.Height--; + } + if (layout.focus.Width % 2 == 0) { + layout.focus.X++; + layout.focus.Width--; + } + } + + + return layout; + } + + TextImageRelation RtlTranslateRelation(TextImageRelation relation) { + // If RTL, we swap ImageBeforeText and TextBeforeImage + if (layoutRTL) { + switch(relation) { + case TextImageRelation.ImageBeforeText: + return TextImageRelation.TextBeforeImage; + case TextImageRelation.TextBeforeImage: + return TextImageRelation.ImageBeforeText; + } + } + return relation; + } + + internal ContentAlignment RtlTranslateContent(ContentAlignment align) { + + if (layoutRTL) { + ContentAlignment[][] mapping = new ContentAlignment[3][]; + mapping[0] = new ContentAlignment[2] { ContentAlignment.TopLeft, ContentAlignment.TopRight }; + mapping[1] = new ContentAlignment[2] { ContentAlignment.MiddleLeft, ContentAlignment.MiddleRight }; + mapping[2] = new ContentAlignment[2] { ContentAlignment.BottomLeft, ContentAlignment.BottomRight }; + + for(int i=0; i < 3; ++i) { + if (mapping[i][0] == align) { + return mapping[i][1]; + } + else if (mapping[i][1] == align) { + return mapping[i][0]; + } + } + } + return align; + } + + private int FullCheckSize { + get { + return checkSize + checkPaddingSize; + } + } + + void CalcCheckmarkRectangle(LayoutData layout) { + int checkSizeFull = FullCheckSize; + layout.checkBounds = new Rectangle(client.X, client.Y, checkSizeFull, checkSizeFull); + + // Translate checkAlign for Rtl applications + ContentAlignment align = RtlTranslateContent(checkAlign); + + Rectangle field = Rectangle.Inflate(layout.face, -paddingSize, -paddingSize); + + layout.field = field; + + if (checkSizeFull > 0) { + if ((align & LayoutUtils.AnyRight) != 0) { + layout.checkBounds.X = (field.X+field.Width) - layout.checkBounds.Width; + } + else if ((align & LayoutUtils.AnyCenter) != 0) { + layout.checkBounds.X = field.X + (field.Width - layout.checkBounds.Width)/2; + } + + if ((align & LayoutUtils.AnyBottom) != 0) { + layout.checkBounds.Y = (field.Y+field.Height)-layout.checkBounds.Height; + } + else if ((align & LayoutUtils.AnyTop) != 0) { + layout.checkBounds.Y = field.Y + 2; // + 2: this needs to be aligned to the text (bug 87483) + } + else { + layout.checkBounds.Y = field.Y + (field.Height - layout.checkBounds.Height)/2; + } + + switch (align) { + case ContentAlignment.TopLeft: + case ContentAlignment.MiddleLeft: + case ContentAlignment.BottomLeft: + layout.checkArea.X = field.X; + layout.checkArea.Width = checkSizeFull + 1; + + layout.checkArea.Y = field.Y; + layout.checkArea.Height = field.Height; + + layout.field.X += checkSizeFull + 1; + layout.field.Width -= checkSizeFull + 1; + break; + case ContentAlignment.TopRight: + case ContentAlignment.MiddleRight: + case ContentAlignment.BottomRight: + layout.checkArea.X = field.X + field.Width - checkSizeFull; + layout.checkArea.Width = checkSizeFull + 1; + + layout.checkArea.Y = field.Y; + layout.checkArea.Height = field.Height; + + layout.field.Width -= checkSizeFull + 1; + break; + case ContentAlignment.TopCenter: + layout.checkArea.X = field.X; + layout.checkArea.Width = field.Width; + + layout.checkArea.Y = field.Y; + layout.checkArea.Height = checkSizeFull; + + layout.field.Y += checkSizeFull; + layout.field.Height -= checkSizeFull; + break; + + case ContentAlignment.BottomCenter: + layout.checkArea.X = field.X; + layout.checkArea.Width = field.Width; + + layout.checkArea.Y = field.Y + field.Height - checkSizeFull; + layout.checkArea.Height = checkSizeFull; + + layout.field.Height -= checkSizeFull; + break; + + case ContentAlignment.MiddleCenter: + layout.checkArea = layout.checkBounds; + break; + } + + layout.checkBounds.Width -= checkPaddingSize; + layout.checkBounds.Height -= checkPaddingSize; + } + } + + // Maps an image align to the set of TextImageRelations that represent the same edge. + // For example, imageAlign = TopLeft maps to TextImageRelations ImageAboveText (top) + // and ImageBeforeText (left). + private static readonly TextImageRelation[] _imageAlignToRelation = new TextImageRelation[] { + /* TopLeft = */ TextImageRelation.ImageAboveText | TextImageRelation.ImageBeforeText, + /* TopCenter = */ TextImageRelation.ImageAboveText, + /* TopRight = */ TextImageRelation.ImageAboveText | TextImageRelation.TextBeforeImage, + /* Invalid */ 0, + /* MiddleLeft = */ TextImageRelation.ImageBeforeText, + /* MiddleCenter = */ 0, + /* MiddleRight = */ TextImageRelation.TextBeforeImage, + /* Invalid */ 0, + /* BottomLeft = */ TextImageRelation.TextAboveImage | TextImageRelation.ImageBeforeText, + /* BottomCenter = */ TextImageRelation.TextAboveImage, + /* BottomRight = */ TextImageRelation.TextAboveImage | TextImageRelation.TextBeforeImage + }; + + private static TextImageRelation ImageAlignToRelation(ContentAlignment alignment) { + return _imageAlignToRelation[LayoutUtils.ContentAlignmentToIndex(alignment)]; + } + + private static TextImageRelation TextAlignToRelation(ContentAlignment alignment) { + return LayoutUtils.GetOppositeTextImageRelation(ImageAlignToRelation(alignment)); + } + + internal void LayoutTextAndImage(LayoutData layout) { + // Translate for Rtl applications. This intentially shadows the member variables. + ContentAlignment imageAlign = RtlTranslateContent(this.imageAlign); + ContentAlignment textAlign = RtlTranslateContent(this.textAlign); + TextImageRelation textImageRelation = RtlTranslateRelation(this.textImageRelation); + + // Figure out the maximum bounds for text & image + Rectangle maxBounds = Rectangle.Inflate(layout.field, -textImageInset, -textImageInset); + if(OnePixExtraBorder) { + maxBounds.Inflate(1, 1); + } + + // Compute the final image and text bounds. + if(imageSize == Size.Empty || text == null || text.Length == 0 || textImageRelation == TextImageRelation.Overlay) { + // Do not worry about text/image overlaying + Size textSize = GetTextSize(maxBounds.Size); + + // FOR EVERETT COMPATIBILITY - DO NOT CHANGE + Size size = imageSize; + if (layout.options.everettButtonCompat && imageSize != Size.Empty) { + size = new Size(size.Width + 1, size.Height + 1); + } + + layout.imageBounds = LayoutUtils.Align(size, maxBounds, imageAlign); + layout.textBounds = LayoutUtils.Align(textSize, maxBounds, textAlign); + + } else { + // Rearrage text/image to prevent overlay. Pack text into maxBounds - space reserved for image + Size maxTextSize = LayoutUtils.SubAlignedRegion(maxBounds.Size, imageSize, textImageRelation); + Size textSize = GetTextSize(maxTextSize); + Rectangle maxCombinedBounds = maxBounds; + + // Combine text & image into one rectangle that we center within maxBounds. + Size combinedSize = LayoutUtils.AddAlignedRegion(textSize, imageSize, textImageRelation); + maxCombinedBounds.Size = LayoutUtils.UnionSizes(maxCombinedBounds.Size, combinedSize); + Rectangle combinedBounds = LayoutUtils.Align(combinedSize, maxCombinedBounds, ContentAlignment.MiddleCenter); + + // imageEdge indicates whether the combination of imageAlign and textImageRelation place + // the image along the edge of the control. If so, we can increase the space for text. + bool imageEdge = (AnchorStyles)(ImageAlignToRelation(imageAlign) & textImageRelation) != AnchorStyles.None; + + // textEdge indicates whether the combination of textAlign and textImageRelation place + // the text along the edge of the control. If so, we can increase the space for image. + bool textEdge = (AnchorStyles)(TextAlignToRelation(textAlign) & textImageRelation) != AnchorStyles.None; + + if(imageEdge) { + // If imageEdge, just split imageSize off of maxCombinedBounds. + LayoutUtils.SplitRegion(maxCombinedBounds, imageSize, (AnchorStyles) textImageRelation, out layout.imageBounds, out layout.textBounds); + } else if(textEdge) { + // Else if textEdge, just split textSize off of maxCombinedBounds. + LayoutUtils.SplitRegion(maxCombinedBounds, textSize, (AnchorStyles) LayoutUtils.GetOppositeTextImageRelation(textImageRelation), out layout.textBounds, out layout.imageBounds); + } else { + // Expand the adjacent regions to maxCombinedBounds (centered) and split the rectangle into imageBounds and textBounds. + LayoutUtils.SplitRegion(combinedBounds, imageSize, (AnchorStyles) textImageRelation, out layout.imageBounds, out layout.textBounds); + LayoutUtils.ExpandRegionsToFillBounds(maxCombinedBounds, (AnchorStyles) textImageRelation, ref layout.imageBounds, ref layout.textBounds); + } + + // align text/image within their regions. + layout.imageBounds = LayoutUtils.Align(imageSize, layout.imageBounds, imageAlign); + layout.textBounds = LayoutUtils.Align(textSize, layout.textBounds, textAlign); + } + + //Don't call "layout.imageBounds = Rectangle.Intersect(layout.imageBounds, maxBounds);" + // because that is a breaking change that causes images to be scaled to the dimensions of the control. + //adjust textBounds so that the text is still visible even if the image is larger than the button's size + //fixes Whidbey 234985 + //why do we intersect with layout.field for textBounds while we intersect with maxBounds for imageBounds? + //this is because there are some legacy code which squeezes the button so small that text will get clipped + //if we intersect with maxBounds. Have to do this for backward compatibility. + //See Whidbey 341480 + if (textImageRelation == TextImageRelation.TextBeforeImage || textImageRelation == TextImageRelation.ImageBeforeText) { + //adjust the vertical position of textBounds so that the text doesn't fall off the boundary of the button + int textBottom = Math.Min(layout.textBounds.Bottom, layout.field.Bottom); + layout.textBounds.Y = Math.Max(Math.Min(layout.textBounds.Y, layout.field.Y + (layout.field.Height - layout.textBounds.Height)/2), layout.field.Y); + layout.textBounds.Height = textBottom - layout.textBounds.Y; + } + if (textImageRelation == TextImageRelation.TextAboveImage || textImageRelation == TextImageRelation.ImageAboveText) { + //adjust the horizontal position of textBounds so that the text doesn't fall off the boundary of the button + int textRight = Math.Min(layout.textBounds.Right, layout.field.Right); + layout.textBounds.X = Math.Max(Math.Min(layout.textBounds.X, layout.field.X + (layout.field.Width - layout.textBounds.Width)/2), layout.field.X); + layout.textBounds.Width = textRight - layout.textBounds.X; + } + if (textImageRelation == TextImageRelation.ImageBeforeText && layout.imageBounds.Size.Width != 0) { + //squeezes imageBounds.Width so that text is visible + layout.imageBounds.Width = Math.Max(0, Math.Min(maxBounds.Width - layout.textBounds.Width, layout.imageBounds.Width)); + layout.textBounds.X = layout.imageBounds.X + layout.imageBounds.Width; + } + if (textImageRelation == TextImageRelation.ImageAboveText && layout.imageBounds.Size.Height != 0) { + //squeezes imageBounds.Height so that the text is visible + layout.imageBounds.Height = Math.Max(0, Math.Min(maxBounds.Height - layout.textBounds.Height, layout.imageBounds.Height)); + layout.textBounds.Y = layout.imageBounds.Y + layout.imageBounds.Height; + } + //make sure that textBound is contained in layout.field + layout.textBounds = Rectangle.Intersect(layout.textBounds, layout.field); + if (hintTextUp) { + layout.textBounds.Y--; + } + if (textOffset) { + layout.textBounds.Offset(1, 1); + } + + // FOR EVERETT COMPATIBILITY - DO NOT CHANGE + if (layout.options.everettButtonCompat) { + layout.imageStart = layout.imageBounds.Location; + layout.imageBounds = Rectangle.Intersect(layout.imageBounds, layout.field); + } + else if (!Application.RenderWithVisualStyles) { + // Not sure why this is here, but we can't remove it, since it might break + // ToolStrips on non-themed machines + layout.textBounds.X++; + } + + // clip + // + int bottom; + // If we are using GDI to measure text, then we can get into a situation, where + // the proposed height is ignore. In this case, we want to clip it against + // maxbounds. VSWhidbey #480670 + if (!useCompatibleTextRendering) { + bottom = Math.Min(layout.textBounds.Bottom, maxBounds.Bottom); + layout.textBounds.Y = Math.Max(layout.textBounds.Y, maxBounds.Y); + } + else { + // If we are using GDI+ (like Everett), then use the old Everett code + // This ensures that we have pixel-level rendering compatibility + bottom = Math.Min(layout.textBounds.Bottom, layout.field.Bottom); + layout.textBounds.Y = Math.Max(layout.textBounds.Y, layout.field.Y); + } + layout.textBounds.Height = bottom - layout.textBounds.Y; + + //This causes a breaking change because images get shrunk to the new clipped size instead of clipped. + //********** bottom = Math.Min(layout.imageBounds.Bottom, maxBounds.Bottom); + //********** layout.imageBounds.Y = Math.Max(layout.imageBounds.Y, maxBounds.Y); + //********** layout.imageBounds.Height = bottom - layout.imageBounds.Y; + + } + + protected virtual Size GetTextSize(Size proposedSize) { + //set the Prefix field of TextFormatFlags + proposedSize = LayoutUtils.FlipSizeIf(verticalText, proposedSize); + Size textSize = Size.Empty; + + if (useCompatibleTextRendering) { // GDI+ text rendering. + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) { + using ( StringFormat gdipStringFormat = this.StringFormat ) { + textSize = Size.Ceiling(g.MeasureString(text, font, new SizeF(proposedSize.Width, proposedSize.Height), gdipStringFormat)); + } + } + } + else if (!string.IsNullOrEmpty(text)) { // GDI text rendering (Whidbey feature). + textSize = TextRenderer.MeasureText(text, font, proposedSize, this.TextFormatFlags); + } + //else skip calling MeasureText, it should return 0,0 + + return LayoutUtils.FlipSizeIf(verticalText, textSize); + + } + +#if DEBUG + public override string ToString() { + return + "{ client = " + client + "\n" + + "OnePixExtraBorder = " + OnePixExtraBorder + "\n" + + "borderSize = " + borderSize + "\n" + + "paddingSize = " + paddingSize + "\n" + + "maxFocus = " + maxFocus + "\n" + + "font = " + font + "\n" + + "text = " + text + "\n" + + "imageSize = " + imageSize + "\n" + + "checkSize = " + checkSize + "\n" + + "checkPaddingSize = " + checkPaddingSize + "\n" + + "checkAlign = " + checkAlign + "\n" + + "imageAlign = " + imageAlign + "\n" + + "textAlign = " + textAlign + "\n" + + "textOffset = " + textOffset + "\n" + + "shadowedText = " + shadowedText + "\n" + + "textImageRelation = " + textImageRelation + "\n" + + "layoutRTL = " + layoutRTL + " }"; + } +#endif + } + + internal class LayoutData { + internal Rectangle client; + internal Rectangle face; + internal Rectangle checkArea; + internal Rectangle checkBounds; + internal Rectangle textBounds; + internal Rectangle field; + internal Rectangle focus; + internal Rectangle imageBounds; + internal Point imageStart; // FOR EVERETT COMPATIBILITY - DO NOT CHANGE + internal LayoutOptions options; + + internal LayoutData(LayoutOptions options) { + Debug.Assert(options != null, "must have options"); + this.options = options; + } + } + +#endregion + +#region Layout + + // used by the DataGridViewButtonCell + internal static LayoutOptions CommonLayout(Rectangle clientRectangle, Padding padding, bool isDefault, Font font, string text, bool enabled, ContentAlignment textAlign, RightToLeft rtl) + { + LayoutOptions layout = new LayoutOptions(); + layout.client = LayoutUtils.DeflateRect(clientRectangle, padding); + layout.padding = padding; + layout.growBorderBy1PxWhenDefault = true; + layout.isDefault = isDefault; + layout.borderSize = 2; + layout.paddingSize = 0; + layout.maxFocus = true; + layout.focusOddEvenFixup = false; + layout.font = font; + layout.text = text; + layout.imageSize = Size.Empty; + layout.checkSize = 0; + layout.checkPaddingSize = 0; + layout.checkAlign = ContentAlignment.TopLeft; + layout.imageAlign = ContentAlignment.MiddleCenter; + layout.textAlign = textAlign; + layout.hintTextUp = false; + layout.shadowedText = !enabled; + layout.layoutRTL = RightToLeft.Yes == rtl; + layout.textImageRelation = TextImageRelation.Overlay; + layout.useCompatibleTextRendering = false; + return layout; + } + + internal virtual LayoutOptions CommonLayout() { + LayoutOptions layout = new LayoutOptions(); + layout.client = LayoutUtils.DeflateRect(Control.ClientRectangle, Control.Padding); + layout.padding = Control.Padding; + layout.growBorderBy1PxWhenDefault = true; + layout.isDefault = Control.IsDefault; + layout.borderSize = 2; + layout.paddingSize = 0; + layout.maxFocus = true; + layout.focusOddEvenFixup = false; + layout.font = Control.Font; + layout.text = Control.Text; + layout.imageSize = (Control.Image == null) ? Size.Empty : Control.Image.Size; + layout.checkSize = 0; + layout.checkPaddingSize = 0; + layout.checkAlign = ContentAlignment.TopLeft; + layout.imageAlign = Control.ImageAlign; + layout.textAlign = Control.TextAlign; + layout.hintTextUp = false; + layout.shadowedText = !Control.Enabled; + layout.layoutRTL = RightToLeft.Yes == Control.RightToLeft; + layout.textImageRelation = Control.TextImageRelation; + layout.useCompatibleTextRendering = Control.UseCompatibleTextRendering; + + if( Control.FlatStyle != FlatStyle.System ) { + if( layout.useCompatibleTextRendering ) { + using( StringFormat format = Control.CreateStringFormat() ) { + layout.StringFormat = format; + } + } + else { + layout.gdiTextFormatFlags = Control.CreateTextFormatFlags(); + } + } + + return layout; + } + + // used by the DataGridViewButtonCell + static ColorOptions CommonRender(Graphics g, Color foreColor, Color backColor, bool enabled) { + ColorOptions colors = new ColorOptions(g, foreColor, backColor); + colors.enabled = enabled; + return colors; + } + + ColorOptions CommonRender(Graphics g) { + ColorOptions colors = new ColorOptions(g, Control.ForeColor, Control.BackColor); + colors.enabled = Control.Enabled; + return colors; + } + + protected ColorOptions PaintRender(Graphics g) { + return CommonRender(g); + } + + // used by the DataGridViewButtonCell + internal static ColorOptions PaintFlatRender(Graphics g, Color foreColor, Color backColor, bool enabled) { + return CommonRender(g, foreColor, backColor, enabled); + } + + protected ColorOptions PaintFlatRender(Graphics g) { + return CommonRender(g); + } + + // used by the DataGridViewButtonCell + internal static ColorOptions PaintPopupRender(Graphics g, Color foreColor, Color backColor, bool enabled) { + return CommonRender(g, foreColor, backColor, enabled); + } + + protected ColorOptions PaintPopupRender(Graphics g) { + return CommonRender(g); + } + +#endregion + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonFlatAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonFlatAdapter.cs new file mode 100644 index 000000000..913784900 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonFlatAdapter.cs @@ -0,0 +1,315 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Diagnostics.CodeAnalysis; + + internal class ButtonFlatAdapter : ButtonBaseAdapter { + + private const int BORDERSIZE = 1; + + internal ButtonFlatAdapter(ButtonBase control) : base(control) {} + + private void PaintBackground(PaintEventArgs e, Rectangle r, Color backColor) { + Rectangle rect = r; + rect.Inflate(-Control.FlatAppearance.BorderSize,-Control.FlatAppearance.BorderSize); + Control.PaintBackground(e, rect, backColor, rect.Location); + } + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + bool hasCustomBorder = (Control.FlatAppearance.BorderSize != BORDERSIZE || !Control.FlatAppearance.BorderColor.IsEmpty); + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + LayoutData layout = PaintFlatLayout(e, + !Control.FlatAppearance.CheckedBackColor.IsEmpty || (SystemInformation.HighContrast ? state != CheckState.Indeterminate : state == CheckState.Unchecked), + !hasCustomBorder && SystemInformation.HighContrast && state == CheckState.Checked, + Control.FlatAppearance.BorderSize).Layout(); + + //Paint with the BorderColor if Set. + /// + if (!Control.FlatAppearance.BorderColor.IsEmpty) { + colors.windowFrame = Control.FlatAppearance.BorderColor; + } + + Graphics g = e.Graphics; + //Region original = g.Clip; + + Rectangle r = Control.ClientRectangle; + + Color backColor = Control.BackColor; + + if (!Control.FlatAppearance.CheckedBackColor.IsEmpty) { + switch (state) { + case CheckState.Checked: + backColor = Control.FlatAppearance.CheckedBackColor; + break; + case CheckState.Indeterminate: + backColor = MixedColor(Control.FlatAppearance.CheckedBackColor, colors.buttonFace); + break; + } + } + else { + switch (state) { + case CheckState.Checked: + backColor = colors.highlight; + break; + case CheckState.Indeterminate: + backColor = MixedColor(colors.highlight, colors.buttonFace); + break; + } + } + + PaintBackground(e, r, IsHighContrastHighlighted2() ? SystemColors.Highlight : backColor); + + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + PaintImage(e, layout); + PaintField(e, layout, colors, IsHighContrastHighlighted2() ? SystemColors.HighlightText : colors.windowText, false); + + if (Control.Focused && Control.ShowFocusCues) { + DrawFlatFocus(g, layout.focus, colors.options.highContrast ? colors.windowText : colors.constrastButtonShadow); + } + + + if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0))) { + DrawDefaultBorder(g, r, colors.windowFrame, this.Control.IsDefault); + } + + //Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user. + if (hasCustomBorder) { + if (Control.FlatAppearance.BorderSize != BORDERSIZE) { + DrawFlatBorderWithSize(g, r, colors.windowFrame, Control.FlatAppearance.BorderSize); + } + else { + DrawFlatBorder(g, r, colors.windowFrame); + } + } + else if (state == CheckState.Checked && SystemInformation.HighContrast) { + DrawFlatBorder(g, r, colors.windowFrame); + DrawFlatBorder(g, r, colors.buttonShadow); + } + else if (state == CheckState.Indeterminate) { + Draw3DLiteBorder(g, r, colors, false); + } + else { + DrawFlatBorder(g, r, colors.windowFrame); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + bool hasCustomBorder = (Control.FlatAppearance.BorderSize != BORDERSIZE || !Control.FlatAppearance.BorderColor.IsEmpty); + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + LayoutData layout = PaintFlatLayout(e, + !Control.FlatAppearance.CheckedBackColor.IsEmpty || (SystemInformation.HighContrast ? state != CheckState.Indeterminate : state == CheckState.Unchecked), + !hasCustomBorder && SystemInformation.HighContrast && state == CheckState.Checked, + Control.FlatAppearance.BorderSize).Layout(); + + //Paint with the BorderColor if Set. + if (!Control.FlatAppearance.BorderColor.IsEmpty) { + colors.windowFrame = Control.FlatAppearance.BorderColor; + } + + Graphics g = e.Graphics; + //Region original = g.Clip; + + Rectangle r = Control.ClientRectangle; + + Color backColor = Control.BackColor; + + if (!Control.FlatAppearance.MouseDownBackColor.IsEmpty) { + backColor = Control.FlatAppearance.MouseDownBackColor; + } + else { + switch (state) { + case CheckState.Unchecked: + case CheckState.Checked: + backColor = colors.options.highContrast ? colors.buttonShadow : colors.lowHighlight; + break; + case CheckState.Indeterminate: + backColor = MixedColor(colors.options.highContrast ? colors.buttonShadow : colors.lowHighlight, colors.buttonFace); + break; + } + } + + PaintBackground(e, r, backColor); + + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + PaintImage(e, layout); + PaintField(e, layout, colors, colors.windowText, false); + + if (Control.Focused && Control.ShowFocusCues) { + DrawFlatFocus(g, layout.focus, colors.options.highContrast ? colors.windowText : colors.constrastButtonShadow); + } + + if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0))) { + DrawDefaultBorder(g, r, colors.windowFrame, this.Control.IsDefault); + } + + //Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user. + if (hasCustomBorder) { + if (Control.FlatAppearance.BorderSize != BORDERSIZE) { + DrawFlatBorderWithSize(g, r, colors.windowFrame, Control.FlatAppearance.BorderSize); + } + else { + DrawFlatBorder(g, r, colors.windowFrame); + } + } + else if (state == CheckState.Checked && SystemInformation.HighContrast) { + DrawFlatBorder(g, r, colors.windowFrame); + DrawFlatBorder(g, r, colors.buttonShadow); + } + else if (state == CheckState.Indeterminate) { + Draw3DLiteBorder(g, r, colors, false); + } + else { + DrawFlatBorder(g, r, colors.windowFrame); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + if (SystemInformation.HighContrast) { + PaintUp(e, state); + } + else { + bool hasCustomBorder = (Control.FlatAppearance.BorderSize != BORDERSIZE || !Control.FlatAppearance.BorderColor.IsEmpty); + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + LayoutData layout = PaintFlatLayout(e, + !Control.FlatAppearance.CheckedBackColor.IsEmpty || state == CheckState.Unchecked, + false, + Control.FlatAppearance.BorderSize).Layout(); + + //Paint with the BorderColor if Set. + /// + if (!Control.FlatAppearance.BorderColor.IsEmpty) { + colors.windowFrame = Control.FlatAppearance.BorderColor; + } + + Graphics g = e.Graphics; + //Region original = g.Clip; + + Rectangle r = Control.ClientRectangle; + + Color backColor = Control.BackColor; + + if (!Control.FlatAppearance.MouseOverBackColor.IsEmpty) { + backColor = Control.FlatAppearance.MouseOverBackColor; + } + else if (!Control.FlatAppearance.CheckedBackColor.IsEmpty) { + if (state == CheckState.Checked || state == CheckState.Indeterminate) { + backColor = MixedColor(Control.FlatAppearance.CheckedBackColor, colors.lowButtonFace); + } + else { + backColor = colors.lowButtonFace; + } + } + else { + if (state == CheckState.Indeterminate) { + backColor = MixedColor(colors.buttonFace, colors.lowButtonFace); + } + else { + backColor = colors.lowButtonFace; + } + } + + PaintBackground(e, r, IsHighContrastHighlighted2() ? SystemColors.Highlight : backColor); + + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + PaintImage(e, layout); + PaintField(e, layout, colors, IsHighContrastHighlighted2() ? SystemColors.HighlightText : colors.windowText, false); + + if (Control.Focused && Control.ShowFocusCues) { + DrawFlatFocus(g, layout.focus, colors.constrastButtonShadow); + } + + if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0))) { + DrawDefaultBorder(g, r, colors.windowFrame, this.Control.IsDefault); + } + + //Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user. + if (hasCustomBorder) { + if (Control.FlatAppearance.BorderSize != BORDERSIZE) { + DrawFlatBorderWithSize(g, r, colors.windowFrame, Control.FlatAppearance.BorderSize); + } + else { + DrawFlatBorder(g, r, colors.windowFrame); + } + } + else if (state == CheckState.Unchecked) { + DrawFlatBorder(g, r, colors.windowFrame); + } + else { + Draw3DLiteBorder(g, r, colors, false); + } + } + } + + + #region LayoutData + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = PaintFlatLayout(e, /* up = */ false, /* check = */ true, Control.FlatAppearance.BorderSize); +#if DEBUG + Size prefSize = layout.GetPreferredSizeCore(LayoutUtils.MaxSize); + Debug.Assert( + prefSize == PaintFlatLayout(e, /* up = */ false, /* check = */ false, Control.FlatAppearance.BorderSize).GetPreferredSizeCore(LayoutUtils.MaxSize) && + prefSize == PaintFlatLayout(e, /* up = */ true, /* check = */ false, Control.FlatAppearance.BorderSize).GetPreferredSizeCore(LayoutUtils.MaxSize) && + prefSize == PaintFlatLayout(e, /* up = */ true, /* check = */ true, Control.FlatAppearance.BorderSize).GetPreferredSizeCore(LayoutUtils.MaxSize), + "The state of up and check should not effect PreferredSize"); +#endif + return layout; + } + + + // used by DataGridViewButtonCell + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + internal static LayoutOptions PaintFlatLayout(Graphics g, bool up, bool check, int borderSize, Rectangle clientRectangle, Padding padding, + bool isDefault, Font font, string text, bool enabled, ContentAlignment textAlign, RightToLeft rtl) + { + LayoutOptions layout = CommonLayout(clientRectangle, padding, isDefault, font, text, enabled, textAlign, rtl); + layout.borderSize = borderSize + (check ? 1 : 0); + layout.paddingSize = check ? 1 : 2; + layout.focusOddEvenFixup = false; + layout.textOffset = !up; + layout.shadowedText = SystemInformation.HighContrast; + + return layout; + } + + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + private LayoutOptions PaintFlatLayout(PaintEventArgs e, bool up, bool check, int borderSize) { + LayoutOptions layout = CommonLayout(); + layout.borderSize = borderSize + (check ? 1 : 0); + layout.paddingSize = check ? 1 : 2; + layout.focusOddEvenFixup = false; + layout.textOffset = !up; + layout.shadowedText = SystemInformation.HighContrast; + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonPopupAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonPopupAdapter.cs new file mode 100644 index 000000000..3050f4cda --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonPopupAdapter.cs @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Diagnostics.CodeAnalysis; + + + internal class ButtonPopupAdapter : ButtonBaseAdapter { + + internal ButtonPopupAdapter(ButtonBase control) : base(control) {} + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, state == CheckState.Unchecked, 1).Layout(); + + Graphics g = e.Graphics; + + Rectangle r = Control.ClientRectangle; + + if (state == CheckState.Indeterminate) { + Brush backbrush = CreateDitherBrush(colors.highlight, colors.buttonFace); + try { + PaintButtonBackground(e, r, backbrush); + } + finally { + backbrush.Dispose(); + backbrush = null; + } + } + else { + Control.PaintBackground(e, r, IsHighContrastHighlighted2() ? SystemColors.Highlight : Control.BackColor, r.Location); + } + + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + PaintImage(e, layout); + PaintField(e, layout, colors, state != CheckState.Indeterminate && IsHighContrastHighlighted2() ? SystemColors.HighlightText : colors.windowText, true); + + DrawDefaultBorder(g, r, colors.options.highContrast ? colors.windowText : colors.buttonShadow, this.Control.IsDefault); + + if (state == CheckState.Unchecked) { + DrawFlatBorder(g, r, colors.options.highContrast ? colors.windowText : colors.buttonShadow); + } + else { + Draw3DLiteBorder(g, r, colors, false); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, state == CheckState.Unchecked, SystemInformation.HighContrast ? 2 : 1).Layout(); + + Graphics g = e.Graphics; + //Region original = g.Clip; + + Rectangle r = Control.ClientRectangle; + + if (state == CheckState.Indeterminate) { + Brush backbrush = CreateDitherBrush(colors.highlight, colors.buttonFace); + try { + PaintButtonBackground(e, r, backbrush); + } + finally { + backbrush.Dispose(); + backbrush = null; + } + } + else { + Control.PaintBackground(e, r, IsHighContrastHighlighted2() ? SystemColors.Highlight : Control.BackColor, r.Location); + } + + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + PaintImage(e, layout); + PaintField(e, layout, colors, IsHighContrastHighlighted2() ? SystemColors.HighlightText : colors.windowText, true); + + DrawDefaultBorder(g, r, colors.options.highContrast ? colors.windowText : colors.buttonShadow, this.Control.IsDefault); + + if (SystemInformation.HighContrast) { + using (Pen windowFrame = new Pen(colors.windowFrame), + highlight = new Pen(colors.highlight), + buttonShadow = new Pen(colors.buttonShadow)) { + + // top, left white + g.DrawLine(windowFrame, r.Left + 1, r.Top + 1, r.Right - 2, r.Top + 1); + g.DrawLine(windowFrame, r.Left + 1, r.Top + 1, r.Left + 1, r.Bottom - 2); + + // bottom, right white + g.DrawLine(windowFrame, r.Left, r.Bottom - 1, r.Right, r.Bottom - 1); + g.DrawLine(windowFrame, r.Right - 1, r.Top, r.Right - 1, r.Bottom); + + // top, left gray + g.DrawLine(highlight, r.Left, r.Top, r.Right, r.Top); + g.DrawLine(highlight, r.Left, r.Top, r.Left, r.Bottom); + + // bottom, right gray + g.DrawLine(buttonShadow, r.Left + 1, r.Bottom - 2, r.Right - 2, r.Bottom - 2); + g.DrawLine(buttonShadow, r.Right - 2, r.Top + 1, r.Right - 2, r.Bottom - 2); + } + + r.Inflate(-2, -2); + } + else { + Draw3DLiteBorder(g, r, colors, true); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, false, SystemInformation.HighContrast ? 2 : 1).Layout(); + + Graphics g = e.Graphics; + //Region original = g.Clip; + + Rectangle r = Control.ClientRectangle; + PaintButtonBackground(e, r, null); + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + r.Inflate(-1, -1); + + PaintImage(e, layout); + PaintField(e, layout, colors, colors.windowText, true); + + r.Inflate(1, 1); + DrawDefaultBorder(g, r, colors.options.highContrast ? colors.windowText : colors.windowFrame, this.Control.IsDefault); + ControlPaint.DrawBorder(g, r, colors.options.highContrast ? colors.windowText : colors.buttonShadow, ButtonBorderStyle.Solid); + } + + #region Layout + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = PaintPopupLayout(e, /* up = */ false, 0); + Debug.Assert(layout.GetPreferredSizeCore(LayoutUtils.MaxSize) + == PaintPopupLayout(e, /* up = */ true, 2).GetPreferredSizeCore(LayoutUtils.MaxSize), + "The state of up should not effect PreferredSize"); + return layout; + } + + + // used by the DataGridViewButtonCell + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + internal static LayoutOptions PaintPopupLayout(Graphics g, bool up, int paintedBorder, Rectangle clientRectangle, Padding padding, + bool isDefault, Font font, string text, bool enabled, ContentAlignment textAlign, RightToLeft rtl) + { + LayoutOptions layout = CommonLayout(clientRectangle, padding, isDefault, font, text, enabled, textAlign, rtl); + layout.borderSize = paintedBorder; + layout.paddingSize = 2 - paintedBorder; + layout.hintTextUp = false; + layout.textOffset = !up; + layout.shadowedText = SystemInformation.HighContrast; + + Debug.Assert(layout.borderSize + layout.paddingSize == 2, + "It is assemed borderSize + paddingSize will always be 2. Bad value for paintedBorder?"); + + return layout; + } + + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + private LayoutOptions PaintPopupLayout(PaintEventArgs e, bool up, int paintedBorder) { + + LayoutOptions layout = CommonLayout(); + layout.borderSize = paintedBorder; + layout.paddingSize = 2 - paintedBorder;//3 - paintedBorder - (Control.IsDefault ? 1 : 0); + layout.hintTextUp = false; + layout.textOffset = !up; + layout.shadowedText = SystemInformation.HighContrast; + + Debug.Assert(layout.borderSize + layout.paddingSize == 2, + "borderSize + paddingSize will always be 2. Bad value for paintedBorder?"); + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonStandardAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonStandardAdapter.cs new file mode 100644 index 000000000..379ac3d2f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/ButtonStandardAdapter.cs @@ -0,0 +1,241 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Windows.Forms.VisualStyles; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + + + internal class ButtonStandardAdapter : ButtonBaseAdapter { + + private const int borderWidth = 2; + + internal ButtonStandardAdapter(ButtonBase control) : base(control) {} + + protected bool IsFilledWithHighlightColor { get; private set; } + + private PushButtonState DetermineState(bool up) { + PushButtonState state = PushButtonState.Normal; + + if (!up) { + state = PushButtonState.Pressed; + } + else if (Control.MouseIsOver) { + state = PushButtonState.Hot; + } + else if (!Control.Enabled) { + state = PushButtonState.Disabled; + } + else if (Control.Focused || Control.IsDefault) { + state = PushButtonState.Default; + } + + return state; + } + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + PaintWorker(e, true, state); + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + PaintWorker(e, false, state); + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + PaintUp(e, state); + } + + private void PaintThemedButtonBackground(PaintEventArgs e, Rectangle bounds, bool up) { + PushButtonState pbState = DetermineState(up); + + // First handle transparent case + + if (ButtonRenderer.IsBackgroundPartiallyTransparent(pbState)) { + ButtonRenderer.DrawParentBackground(e.Graphics, bounds, Control); + } + + // Now draw the actual themed background + if (!DpiHelper.EnableDpiChangedHighDpiImprovements) { + ButtonRenderer.DrawButton(e.Graphics, Control.ClientRectangle, false, pbState); + } + else { + ButtonRenderer.DrawButtonForHandle(e.Graphics, Control.ClientRectangle, false, pbState, this.Control.HandleInternal); + } + + // Now overlay the background image or backcolor (the former overrides the latter), leaving a + // margin. We hardcode this margin for now since GetThemeMargins returns 0 all the + // time. + //HACK We need to see what's best here. changing this to HACK because GetThemeMargins simply does not + // work in some cases. + bounds.Inflate(-buttonBorderSize, -buttonBorderSize); + + + //only paint if the user said not to use the themed backcolor. + if (!Control.UseVisualStyleBackColor) { + bool painted = false; + bool isHighContrastHighlighted = up && IsHighContrastHighlighted(); + Color color = isHighContrastHighlighted ? SystemColors.Highlight : Control.BackColor; + + // Note: PaintEvent.HDC == 0 if GDI+ has used the HDC -- it wouldn't be safe for us + // to use it without enough bookkeeping to negate any performance gain of using GDI. + if (color.A == 255 && e.HDC != IntPtr.Zero) { + + if (DisplayInformation.BitsPerPixel > 8) { + NativeMethods.RECT r = new NativeMethods.RECT(bounds.X, bounds.Y, bounds.Right, bounds.Bottom); + // SysColorBrush does not have to be deleted. + SafeNativeMethods.FillRect(new HandleRef(e, e.HDC), ref r, new HandleRef(this, + isHighContrastHighlighted ? SafeNativeMethods.GetSysColorBrush(ColorTranslator.ToOle(color) & 0xFF) : Control.BackColorBrush)); + painted = true; + } + } + + if (!painted) { + // don't paint anything from 100% transparent background + // + if (color.A > 0) { + if (color.A == 255) { + color = e.Graphics.GetNearestColor(color); + } + + // Color has some transparency or we have no HDC, so we must + // fall back to using GDI+. + // + using (Brush brush = new SolidBrush(color)) { + e.Graphics.FillRectangle(brush, bounds); + IsFilledWithHighlightColor = (color.ToArgb() == SystemColors.Highlight.ToArgb()); + } + } + } + } + + //This code is mostly taken from the non-themed rendering code path. + if (Control.BackgroundImage != null && !DisplayInformation.HighContrast) { + ControlPaint.DrawBackgroundImage(e.Graphics, Control.BackgroundImage, Color.Transparent, Control.BackgroundImageLayout, Control.ClientRectangle, bounds, Control.DisplayRectangle.Location, Control.RightToLeft); + } + } + + void PaintWorker(PaintEventArgs e, bool up, CheckState state) { + up = up && state == CheckState.Unchecked; + + IsFilledWithHighlightColor = false; + + ColorData colors = PaintRender(e.Graphics).Calculate(); + LayoutData layout; + if (Application.RenderWithVisualStyles) { + //don't have the text-pressed-down effect when we use themed painting + //this is for consistency with win32 app. + layout = PaintLayout(e, true).Layout(); + } + else { + layout = PaintLayout(e, up).Layout(); + } + + Graphics g = e.Graphics; + + Button thisbutton = this.Control as Button; + if (Application.RenderWithVisualStyles) { + PaintThemedButtonBackground(e, Control.ClientRectangle, up); + } + else { + Brush backbrush = null; + if (state == CheckState.Indeterminate) { + backbrush = CreateDitherBrush(colors.highlight, colors.buttonFace); + } + + try { + Rectangle bounds = Control.ClientRectangle; + if (up) { + // We are going to draw a 2 pixel border + bounds.Inflate(-borderWidth, -borderWidth); // VS Whidbey #459900 + } + else { + // We are going to draw a 1 pixel border. + bounds.Inflate(-1, -1); // VS Whidbey #503487 + } + + PaintButtonBackground(e, bounds, backbrush); + } + finally { + if (backbrush != null) { + backbrush.Dispose(); + backbrush = null; + } + } + } + + PaintImage(e, layout); + //inflate the focus rectangle to be consistent with the behavior of Win32 app + if (Application.RenderWithVisualStyles) { + layout.focus.Inflate(1, 1); + } + + if (up & IsHighContrastHighlighted2()) { + var highlightTextColor = SystemColors.HighlightText; + PaintField(e, layout, colors, highlightTextColor, false); + + if (Control.Focused && Control.ShowFocusCues) { + // drawing focus rectangle of HighlightText color + ControlPaint.DrawHighContrastFocusRectangle(g, layout.focus, highlightTextColor); + } + } + else if (up & IsHighContrastHighlighted()) { + PaintField(e, layout, colors, SystemColors.HighlightText, true); + } + else { + PaintField(e, layout, colors, colors.windowText, true); + } + + if (!Application.RenderWithVisualStyles) { + Rectangle r = Control.ClientRectangle; + if (Control.IsDefault) { + r.Inflate(-1, -1); + } + + DrawDefaultBorder(g, r, colors.windowFrame, this.Control.IsDefault); + + if (up) { + Draw3DBorder(g, r, colors, up); + } + else { + // contrary to popular belief, not Draw3DBorder(..., false); + // + ControlPaint.DrawBorder(g, r, colors.buttonShadow, ButtonBorderStyle.Solid); + } + } + } + + #region Layout + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = PaintLayout(e, /* up = */ false); + Debug.Assert(layout.GetPreferredSizeCore(LayoutUtils.MaxSize) == PaintLayout(e, /* up = */ true).GetPreferredSizeCore(LayoutUtils.MaxSize), + "The state of up should not effect PreferredSize"); + return layout; + } + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + private LayoutOptions PaintLayout(PaintEventArgs e, bool up) { + LayoutOptions layout = CommonLayout(); + layout.textOffset = !up; + layout.everettButtonCompat = !Application.RenderWithVisualStyles; + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxBaseAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxBaseAdapter.cs new file mode 100644 index 000000000..dabdfc858 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxBaseAdapter.cs @@ -0,0 +1,297 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Runtime.InteropServices; + + internal abstract class CheckBoxBaseAdapter : CheckableControlBaseAdapter { + + protected const int flatCheckSize = 11; + + [ThreadStatic] + private static Bitmap checkImageChecked = null; + [ThreadStatic] + private static Color checkImageCheckedBackColor = Color.Empty; + + [ThreadStatic] + private static Bitmap checkImageIndeterminate = null; + [ThreadStatic] + private static Color checkImageIndeterminateBackColor = Color.Empty; + + internal CheckBoxBaseAdapter(ButtonBase control) : base(control) {} + + protected new CheckBox Control { + get { + return ((CheckBox)base.Control); + } + } + + #region Drawing Helpers + + protected void DrawCheckFlat(PaintEventArgs e, LayoutData layout, Color checkColor, Color checkBackground, Color checkBorder, ColorData colors) { + Rectangle bounds = layout.checkBounds; + // Removed subtracting one for Width and Height. In Everett we needed to do this, + // since we were using GDI+ to draw the border. Now that we are using GDI, + // we should not do before drawing the border. + + if (!layout.options.everettButtonCompat) { + bounds.Width--; + bounds.Height--; + } + using (WindowsGraphics wg = WindowsGraphics.FromGraphics( e.Graphics )) { + using(WindowsPen pen = new WindowsPen(wg.DeviceContext, checkBorder)){ + wg.DrawRectangle(pen, bounds); + } + + // Now subtract, since the rest of the code is like Everett. + if (layout.options.everettButtonCompat) { + bounds.Width--; + bounds.Height--; + } + bounds.Inflate(-1, -1); + } + if (Control.CheckState == CheckState.Indeterminate) { + bounds.Width++; + bounds.Height++; + DrawDitheredFill(e.Graphics, colors.buttonFace, checkBackground, bounds); + } + else { + using( WindowsGraphics wg = WindowsGraphics.FromGraphics( e.Graphics )) { + using (WindowsBrush brush = new WindowsSolidBrush(wg.DeviceContext, checkBackground)) { + // Even though we are using GDI here as opposed to GDI+ in Everett, we still need to add 1. + bounds.Width++; + bounds.Height++; + wg.FillRectangle(brush, bounds); + } + } + } + DrawCheckOnly(e, layout, colors, checkColor, checkBackground); + + } + + // used by DataGridViewCheckBoxCell + internal static void DrawCheckBackground(bool controlEnabled, CheckState controlCheckState, Graphics g, Rectangle bounds, Color checkColor, Color checkBackground, bool disabledColors, ColorData colors) + { + using ( WindowsGraphics wg = WindowsGraphics.FromGraphics( g )) { + WindowsBrush brush; + if (!controlEnabled && disabledColors) { + brush = new WindowsSolidBrush(wg.DeviceContext, SystemColors.Control); + } + else if (controlCheckState == CheckState.Indeterminate && checkBackground == SystemColors.Window && disabledColors) { + Color comboColor = SystemInformation.HighContrast ? SystemColors.ControlDark : + SystemColors.Control; + byte R = (byte)((comboColor.R + SystemColors.Window.R) / 2); + byte G = (byte)((comboColor.G + SystemColors.Window.G) / 2); + byte B = (byte)((comboColor.B + SystemColors.Window.B) / 2); + brush = new WindowsSolidBrush(wg.DeviceContext, Color.FromArgb(R, G, B)); + } + else { + brush = new WindowsSolidBrush(wg.DeviceContext, checkBackground); + } + + try { + wg.FillRectangle(brush, bounds); + } + finally { + if (brush != null) { + brush.Dispose(); + } + } + } + } + + protected void DrawCheckBackground(PaintEventArgs e, Rectangle bounds, Color checkColor, Color checkBackground, bool disabledColors, ColorData colors) { + // area behind check + // + if (Control.CheckState == CheckState.Indeterminate) { + DrawDitheredFill(e.Graphics, colors.buttonFace, checkBackground, bounds); + } + else { + DrawCheckBackground(Control.Enabled, Control.CheckState, e.Graphics, bounds, checkColor, checkBackground, disabledColors, colors); + } + } + + protected void DrawCheckOnly(PaintEventArgs e, LayoutData layout, ColorData colors, Color checkColor, Color checkBackground) { + DrawCheckOnly(flatCheckSize, Control.Checked, Control.Enabled, Control.CheckState, e.Graphics, layout, colors, checkColor, checkBackground); + } + + // used by DataGridViewCheckBoxCell + internal static void DrawCheckOnly(int checkSize, bool controlChecked, bool controlEnabled, CheckState controlCheckState, Graphics g, LayoutData layout, ColorData colors, Color checkColor, Color checkBackground) { + + // check + // + if (controlChecked) { + if (!controlEnabled) { + checkColor = colors.buttonShadow; + } + else if (controlCheckState == CheckState.Indeterminate) { + checkColor = SystemInformation.HighContrast ? colors.highlight : + colors.buttonShadow; + } + + Rectangle fullSize = layout.checkBounds; + + if (fullSize.Width == checkSize) { + fullSize.Width++; + fullSize.Height++; + } + + fullSize.Width++; + + fullSize.Height++; + Bitmap checkImage = null; + if (controlCheckState == CheckState.Checked) { + checkImage = GetCheckBoxImage(checkColor, fullSize, ref checkImageCheckedBackColor, ref checkImageChecked); + } else { + Debug.Assert(controlCheckState == CheckState.Indeterminate, "we want to paint the check box only if the item is checked or indeterminate"); + checkImage = GetCheckBoxImage(checkColor, fullSize, ref checkImageIndeterminateBackColor, ref checkImageIndeterminate); + } + + if (layout.options.everettButtonCompat) { + fullSize.Y -= 1; + } + else { + fullSize.Y -= 2; + } + + ControlPaint.DrawImageColorized(g, checkImage, fullSize, checkColor); + } + } + + internal static Rectangle DrawPopupBorder(Graphics g, Rectangle r, ColorData colors) { + using (WindowsGraphics wg = WindowsGraphics.FromGraphics( g )) { + + using( WindowsPen high = new WindowsPen(wg.DeviceContext, colors.highlight), + shadow = new WindowsPen(wg.DeviceContext, colors.buttonShadow), + face = new WindowsPen(wg.DeviceContext, colors.buttonFace)) { + + wg.DrawLine(high, r.Right-1 , r.Top, r.Right-1, r.Bottom); + wg.DrawLine(high, r.Left, r.Bottom-1, r.Right, r.Bottom-1); + + wg.DrawLine(shadow, r.Left, r.Top, r.Left , r.Bottom); + wg.DrawLine(shadow, r.Left, r.Top, r.Right- 1, r.Top); + + wg.DrawLine(face, r.Right - 2, r.Top + 1, r.Right - 2, r.Bottom - 1); + wg.DrawLine(face, r.Left + 1, r.Bottom - 2, r.Right - 1, r.Bottom - 2); + } + } + r.Inflate(-1, -1); + return r; + } + + protected ButtonState GetState() { + ButtonState style = (ButtonState)0; + + if (Control.CheckState == CheckState.Unchecked) { + style |= ButtonState.Normal; + } + else { + style |= ButtonState.Checked; + } + + if (!Control.Enabled) { + style |= ButtonState.Inactive; + } + + if (Control.MouseIsDown) { + style |= ButtonState.Pushed; + } + + return style; + } + + protected void DrawCheckBox(PaintEventArgs e, LayoutData layout) { + Graphics g = e.Graphics; + + ButtonState style = GetState(); + + if (Control.CheckState == CheckState.Indeterminate) { + if (Application.RenderWithVisualStyles) { + CheckBoxRenderer.DrawCheckBox(g, new Point(layout.checkBounds.Left, layout.checkBounds.Top), CheckBoxRenderer.ConvertFromButtonState(style, true, Control.MouseIsOver), Control.HandleInternal); + } + else { + ControlPaint.DrawMixedCheckBox(g, layout.checkBounds, style); + } + } + else { + if (Application.RenderWithVisualStyles) { + CheckBoxRenderer.DrawCheckBox(g, new Point(layout.checkBounds.Left, layout.checkBounds.Top), CheckBoxRenderer.ConvertFromButtonState(style, false, Control.MouseIsOver), Control.HandleInternal); + } + else { + ControlPaint.DrawCheckBox(g, layout.checkBounds, style); + } + } + } + + #endregion + + private static Bitmap GetCheckBoxImage(Color checkColor, Rectangle fullSize, ref Color cacheCheckColor, ref Bitmap cacheCheckImage) + { + if (cacheCheckImage != null && + cacheCheckColor.Equals(checkColor) && + cacheCheckImage.Width == fullSize.Width && + cacheCheckImage.Height == fullSize.Height) { + return cacheCheckImage; + } + + if (cacheCheckImage != null) + { + cacheCheckImage.Dispose(); + cacheCheckImage = null; + } + + // We draw the checkmark slightly off center to eliminate 3-D border artifacts, + // and compensate below + NativeMethods.RECT rcCheck = NativeMethods.RECT.FromXYWH(0, 0, fullSize.Width, fullSize.Height); + Bitmap bitmap = new Bitmap(fullSize.Width, fullSize.Height); + Graphics offscreen = Graphics.FromImage(bitmap); + offscreen.Clear(Color.Transparent); + IntPtr dc = offscreen.GetHdc(); + try { + SafeNativeMethods.DrawFrameControl(new HandleRef(offscreen, dc), ref rcCheck, + NativeMethods.DFC_MENU, NativeMethods.DFCS_MENUCHECK); + } finally { + offscreen.ReleaseHdcInternal(dc); + offscreen.Dispose(); + } + + bitmap.MakeTransparent(); + cacheCheckImage = bitmap; + cacheCheckColor = checkColor; + + return cacheCheckImage; + } + + protected void AdjustFocusRectangle(LayoutData layout) { + if (AccessibilityImprovements.Level2 && String.IsNullOrEmpty(Control.Text)) { + // When a CheckBox has no text, AutoSize sets the size to zero + // and thus there's no place around which to draw the focus rectangle. + // So, when AutoSize == true we want the focus rectangle to be rendered inside the box. + // Otherwise, it should encircle all the available space next to the box (like it's done in WPF and ComCtl32). + layout.focus = Control.AutoSize ? Rectangle.Inflate(layout.checkBounds, -2, -2) : layout.field; + } + } + + internal override LayoutOptions CommonLayout() { + LayoutOptions layout = base.CommonLayout(); + layout.checkAlign = Control.CheckAlign; + layout.textOffset = false; + layout.shadowedText = !Control.Enabled; + layout.layoutRTL = RightToLeft.Yes == Control.RightToLeft; + + return layout; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxFlatAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxFlatAdapter.cs new file mode 100644 index 000000000..8ae34a6ad --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxFlatAdapter.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + + internal class CheckBoxFlatAdapter : CheckBoxBaseAdapter { + + internal CheckBoxFlatAdapter(ButtonBase control) : base(control) {} + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintDown(e, Control.CheckState); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.highlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintOver(e, Control.CheckState); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.lowHighlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintUp(e, Control.CheckState); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.highlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + private void PaintFlatWorker(PaintEventArgs e, Color checkColor, Color checkBackground, Color checkBorder, ColorData colors) { + System.Drawing.Graphics g = e.Graphics; + LayoutData layout = Layout(e).Layout(); + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + DrawCheckFlat(e, layout, checkColor, colors.options.highContrast ? colors.buttonFace : checkBackground, checkBorder, colors); + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, checkColor, true); + } + + #region Layout + + private new ButtonFlatAdapter ButtonAdapter { + get { + return ((ButtonFlatAdapter)base.ButtonAdapter); + } + } + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonFlatAdapter(Control); + } + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = CommonLayout(); + layout.checkSize = (int)(flatCheckSize * GetDpiScaleRatio(e.Graphics)); + layout.shadowedText = false; + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxPopupAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxPopupAdapter.cs new file mode 100644 index 000000000..9511c026e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxPopupAdapter.cs @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Diagnostics.CodeAnalysis; + + + internal class CheckBoxPopupAdapter : CheckBoxBaseAdapter { + + internal CheckBoxPopupAdapter(ButtonBase control) : base(control) {} + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintUp(e, Control.CheckState); + } + else { + System.Drawing.Graphics g = e.Graphics; + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, false).Layout(); + + Region original = e.Graphics.Clip; + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + DrawCheckBackground(e, layout.checkBounds, colors.windowText, colors.options.highContrast ? colors.buttonFace : colors.highlight, true, colors); + DrawFlatBorder(e.Graphics, layout.checkBounds, + (colors.options.highContrast && !Control.Enabled && AccessibilityImprovements.Level1) ? colors.windowFrame : colors.buttonShadow); + DrawCheckOnly(e, layout, colors, colors.windowText, colors.highlight); + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + System.Drawing.Graphics g = e.Graphics; + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintOver(e, Control.CheckState); + } + else { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, true).Layout(); + + Region original = e.Graphics.Clip; + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + DrawCheckBackground(e, layout.checkBounds, colors.windowText, colors.options.highContrast ? colors.buttonFace : colors.highlight, true, colors); + DrawPopupBorder(g, layout.checkBounds, colors); + DrawCheckOnly(e, layout, colors, colors.windowText, colors.highlight); + + if (!AccessibilityImprovements.Level2 || !String.IsNullOrEmpty(Control.Text)) { + e.Graphics.Clip = original; + e.Graphics.ExcludeClip(layout.checkArea); + } + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintDown(e, Control.CheckState); + } + else { + System.Drawing.Graphics g = e.Graphics; + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = PaintPopupLayout(e, true).Layout(); + + Region original = e.Graphics.Clip; + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + DrawCheckBackground(e, layout.checkBounds, colors.windowText, colors.buttonFace, true, colors); + DrawPopupBorder(g, layout.checkBounds, colors); + DrawCheckOnly(e, layout, colors, colors.windowText, colors.buttonFace); + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + #region Layout + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonPopupAdapter(Control); + } + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = PaintPopupLayout(e, /* up = */ true); + Debug.Assert(layout.GetPreferredSizeCore(LayoutUtils.MaxSize) + == PaintPopupLayout(e, /* up = */ false).GetPreferredSizeCore(LayoutUtils.MaxSize), + "The state of show3D should not effect PreferredSize"); + return layout; + } + + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + internal static LayoutOptions PaintPopupLayout(Graphics g, bool show3D, int checkSize, Rectangle clientRectangle, Padding padding, + bool isDefault, Font font, string text, bool enabled, ContentAlignment textAlign, RightToLeft rtl, + Control control = null) + { + LayoutOptions layout = CommonLayout(clientRectangle, padding, isDefault, font, text, enabled, textAlign, rtl); + layout.shadowedText = false; + if (show3D) { + layout.checkSize = (int)(checkSize * GetDpiScaleRatio(g, control) + 1); + } + else { + layout.checkSize = (int)(checkSize * GetDpiScaleRatio(g, control)); + layout.checkPaddingSize = 1; + } + return layout; + } + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // removed graphics, may have to put it back + private LayoutOptions PaintPopupLayout(PaintEventArgs e, bool show3D) { + LayoutOptions layout = CommonLayout(); + layout.shadowedText = false; + if (show3D) { + layout.checkSize = (int)(flatCheckSize * GetDpiScaleRatio(e.Graphics) + 1); + } + else { + layout.checkSize = (int)(flatCheckSize * GetDpiScaleRatio(e.Graphics)); + layout.checkPaddingSize = 1; + } + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxStandardAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxStandardAdapter.cs new file mode 100644 index 000000000..98f2744af --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckBoxStandardAdapter.cs @@ -0,0 +1,134 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + + internal sealed class CheckBoxStandardAdapter : CheckBoxBaseAdapter { + + internal CheckBoxStandardAdapter(ButtonBase control) : base(control) {} + + internal override void PaintUp(PaintEventArgs e, CheckState state) + { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintUp(e, Control.CheckState); + } + else { + ColorData colors = PaintRender(e.Graphics).Calculate(); + LayoutData layout = Layout(e).Layout(); + PaintButtonBackground(e, Control.ClientRectangle, null); + + if (!layout.options.everettButtonCompat) { + layout.textBounds.Offset(-1, -1); + } + layout.imageBounds.Offset(-1, -1); + + AdjustFocusRectangle(layout); + + if (!AccessibilityImprovements.Level2 || !String.IsNullOrEmpty(Control.Text)) { + //minor adjustment to make sure the appearance is exactly the same as Win32 app. + int focusRectFixup = layout.focus.X & 0x1; // if it's odd, subtract one pixel for fixup. + if (!Application.RenderWithVisualStyles) { + focusRectFixup = 1 - focusRectFixup; + } + layout.focus.Offset(-(focusRectFixup + 1), -2); + layout.focus.Width = layout.textBounds.Width + layout.imageBounds.Width - 1; + layout.focus.Intersect(layout.textBounds); + + if (layout.options.textAlign != LayoutUtils.AnyLeft && layout.options.useCompatibleTextRendering && layout.options.font.Italic) { + // fixup for GDI+ text rendering. VSW#515164 + layout.focus.Width += 2; + } + } + + PaintImage(e, layout); + DrawCheckBox(e, layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintDown(e, Control.CheckState); + } + else { + PaintUp(e, state); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintOver(e, Control.CheckState); + } + else { + PaintUp(e, state); + } + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + if (Control.Appearance == Appearance.Button) { + ButtonStandardAdapter adapter = new ButtonStandardAdapter(Control); + return adapter.GetPreferredSizeCore(proposedSize); + } + else { + using (Graphics measurementGraphics = WindowsFormsUtils.CreateMeasurementGraphics()) { + using (PaintEventArgs pe = new PaintEventArgs(measurementGraphics, new Rectangle())) { + LayoutOptions options = Layout(pe); + + return options.GetPreferredSizeCore(proposedSize); + } + } + } + } + + #region Layout + + private new ButtonStandardAdapter ButtonAdapter { + get { + return ((ButtonStandardAdapter)base.ButtonAdapter); + } + } + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonStandardAdapter(Control); + } + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = CommonLayout(); + layout.checkPaddingSize = 1; + layout.everettButtonCompat = !Application.RenderWithVisualStyles; + + // VSWhidbey 420870 + if (Application.RenderWithVisualStyles) { + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) { + layout.checkSize = CheckBoxRenderer.GetGlyphSize(g, CheckBoxRenderer.ConvertFromButtonState(GetState(), true, Control.MouseIsOver), Control.HandleInternal).Width; + } + } + // Dev10 bug 525537 + else { + if (DpiHelper.EnableDpiChangedMessageHandling) { + layout.checkSize = Control.LogicalToDeviceUnits(layout.checkSize); + } + else { + layout.checkSize = (int)(layout.checkSize * GetDpiScaleRatio(e.Graphics)); + } + } + + return layout; + } + + #endregion + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckableControlBaseAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckableControlBaseAdapter.cs new file mode 100644 index 000000000..24f90e4c1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/CheckableControlBaseAdapter.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + + /// + /// Common class for RadioButtonBaseAdapter and CheckBoxBaseAdapter + /// + internal abstract class CheckableControlBaseAdapter : ButtonBaseAdapter { + private const int standardCheckSize = 13; + private ButtonBaseAdapter buttonAdapter; + + internal CheckableControlBaseAdapter(ButtonBase control) : base(control) {} + + protected ButtonBaseAdapter ButtonAdapter { + get { + if (buttonAdapter == null) { + buttonAdapter = CreateButtonAdapter(); + } + return buttonAdapter; + } + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + if (Appearance == Appearance.Button) { + return ButtonAdapter.GetPreferredSizeCore(proposedSize); + } + + using (Graphics measurementGraphics = WindowsFormsUtils.CreateMeasurementGraphics()) { + using (PaintEventArgs pe = new PaintEventArgs(measurementGraphics, new Rectangle())) { + LayoutOptions options = Layout(pe); + return options.GetPreferredSizeCore(proposedSize); + } + } + } + + protected abstract ButtonBaseAdapter CreateButtonAdapter(); + + private Appearance Appearance { + get { + CheckBox checkBox = Control as CheckBox; + if(checkBox != null) { + return checkBox.Appearance; + } + + RadioButton radioButton = Control as RadioButton; + if(radioButton != null) { + return radioButton.Appearance; + } + + Debug.Fail("Unexpected control type '" + Control.GetType().FullName + "'"); + return Appearance.Normal; + } + } + + internal override LayoutOptions CommonLayout() { + LayoutOptions layout = base.CommonLayout(); + layout.growBorderBy1PxWhenDefault = false; + layout.borderSize = 0; + layout.paddingSize = 0; + layout.maxFocus = false; + layout.focusOddEvenFixup = true; + layout.checkSize = standardCheckSize; + return layout; + } + + internal double GetDpiScaleRatio(Graphics g) { + return GetDpiScaleRatio(g, Control); + } + + internal static double GetDpiScaleRatio(Graphics g, Control control) { + + if (DpiHelper.EnableDpiChangedMessageHandling + && control != null && control.IsHandleCreated) { + + return control.deviceDpi / DpiHelper.LogicalDpi; + } + + if (g == null) + return 1.0F; + return g.DpiX / 96; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonBaseAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonBaseAdapter.cs new file mode 100644 index 000000000..19b67b69e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonBaseAdapter.cs @@ -0,0 +1,215 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + + internal abstract class RadioButtonBaseAdapter : CheckableControlBaseAdapter { + + internal RadioButtonBaseAdapter(ButtonBase control) : base(control) {} + + protected new RadioButton Control { + get { + return ((RadioButton)base.Control); + } + } + + #region Drawing helpers + protected void DrawCheckFlat(PaintEventArgs e, LayoutData layout, Color checkColor, Color checkBackground, Color checkBorder) { + DrawCheckBackgroundFlat(e, layout.checkBounds, checkBorder, checkBackground); + DrawCheckOnly(e, layout, checkColor, checkBackground, true); + } + + protected void DrawCheckBackground3DLite(PaintEventArgs e, Rectangle bounds, Color checkColor, Color checkBackground, ColorData colors, bool disabledColors) { + Graphics g = e.Graphics; + + Color field = checkBackground; + if (!Control.Enabled && disabledColors) { + field = SystemColors.Control; + } + + using (Brush fieldBrush = new SolidBrush(field)) { + using (Pen dark = new Pen(colors.buttonShadow), + light = new Pen(colors.buttonFace), + lightlight = new Pen(colors.highlight)) { + + bounds.Width--; + bounds.Height--; + // fall a little short of SW, NW, NE, SE because corners come out nasty + g.DrawPie(dark, bounds, (float)(135 + 1), (float)(90 - 2)); + g.DrawPie(dark, bounds, (float)(225 + 1), (float)(90 - 2)); + g.DrawPie(lightlight, bounds, (float)(315 + 1), (float)(90 - 2)); + g.DrawPie(lightlight, bounds, (float)(45 + 1), (float)(90 - 2)); + bounds.Inflate(-1, -1); + g.FillEllipse(fieldBrush, bounds); + g.DrawEllipse(light, bounds); + } + } + } + + protected void DrawCheckBackgroundFlat(PaintEventArgs e, Rectangle bounds, Color borderColor, Color checkBackground) { + Color field = checkBackground; + Color border = borderColor; + + if (!Control.Enabled) { + // if we are not in HighContrast mode OR we opted into the legacy behavior + if (!SystemInformation.HighContrast || !AccessibilityImprovements.Level1) { + border = ControlPaint.ContrastControlDark; + } + // otherwise we are in HighContrast mode + field = SystemColors.Control; + } + + double scale = GetDpiScaleRatio(e.Graphics); + + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(e.Graphics) ) { + using( WindowsPen borderPen = new WindowsPen(wg.DeviceContext, border) ) { + using( WindowsBrush fieldBrush = new WindowsSolidBrush(wg.DeviceContext, field) ) { + // for Dev10 525537, in high DPI mode when we draw ellipse as three rectantles, + // the quality of ellipse is poor. Draw it directly as ellipse + if(scale > 1.1) { + bounds.Width--; + bounds.Height--; + wg.DrawAndFillEllipse(borderPen, fieldBrush, bounds); + bounds.Inflate(-1, -1); + } + else { + DrawAndFillEllipse(wg, borderPen, fieldBrush, bounds); + } + } + } + } + } + + // Helper method to overcome the poor GDI ellipse drawing routine + // VSWhidbey #334097 + private static void DrawAndFillEllipse(WindowsGraphics wg, WindowsPen borderPen, WindowsBrush fieldBrush, Rectangle bounds) + { + Debug.Assert(wg != null,"Calling DrawAndFillEllipse with null wg"); + if (wg == null) { + return; + } + + wg.FillRectangle(fieldBrush, new Rectangle(bounds.X + 2, bounds.Y + 2, 8, 8)); + wg.FillRectangle(fieldBrush, new Rectangle(bounds.X + 4, bounds.Y + 1, 4, 10)); + wg.FillRectangle(fieldBrush, new Rectangle(bounds.X + 1, bounds.Y + 4, 10, 4)); + + wg.DrawLine(borderPen, new Point(bounds.X + 4, bounds.Y + 0), new Point(bounds.X + 8, bounds.Y + 0)); + wg.DrawLine(borderPen, new Point(bounds.X + 4, bounds.Y + 11), new Point(bounds.X + 8, bounds.Y + 11)); + + wg.DrawLine(borderPen, new Point(bounds.X + 2, bounds.Y + 1), new Point(bounds.X + 4, bounds.Y + 1)); + wg.DrawLine(borderPen, new Point(bounds.X + 8, bounds.Y + 1), new Point(bounds.X + 10, bounds.Y + 1)); + + wg.DrawLine(borderPen, new Point(bounds.X + 2, bounds.Y + 10), new Point(bounds.X + 4, bounds.Y + 10)); + wg.DrawLine(borderPen, new Point(bounds.X + 8, bounds.Y + 10), new Point(bounds.X + 10, bounds.Y + 10)); + + wg.DrawLine(borderPen, new Point(bounds.X + 0, bounds.Y + 4), new Point(bounds.X + 0, bounds.Y + 8)); + wg.DrawLine(borderPen, new Point(bounds.X + 11, bounds.Y + 4), new Point(bounds.X + 11, bounds.Y + 8)); + + wg.DrawLine(borderPen, new Point(bounds.X + 1, bounds.Y + 2), new Point(bounds.X + 1, bounds.Y + 4)); + wg.DrawLine(borderPen, new Point(bounds.X + 1, bounds.Y + 8), new Point(bounds.X + 1, bounds.Y + 10)); + + wg.DrawLine(borderPen, new Point(bounds.X + 10, bounds.Y + 2), new Point(bounds.X + 10, bounds.Y + 4)); + wg.DrawLine(borderPen, new Point(bounds.X + 10, bounds.Y + 8), new Point(bounds.X + 10, bounds.Y + 10)); + } + + private static int GetScaledNumber(int n, double scale) + { + return (int)(n * scale); + } + + protected void DrawCheckOnly(PaintEventArgs e, LayoutData layout, Color checkColor, Color checkBackground, bool disabledColors) { + + // check + // + if (Control.Checked) { + if (!Control.Enabled && disabledColors) { + checkColor = SystemColors.ControlDark; + } + + double scale = GetDpiScaleRatio(e.Graphics); + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(e.Graphics) ) { + using (WindowsBrush brush = new WindowsSolidBrush(wg.DeviceContext, checkColor)) { + // circle drawing doesn't work at this size + int offset = 5; + Rectangle vCross = new Rectangle (layout.checkBounds.X + GetScaledNumber(offset, scale), layout.checkBounds.Y + GetScaledNumber(offset - 1, scale), GetScaledNumber(2, scale), GetScaledNumber(4, scale)); + wg.FillRectangle(brush, vCross); + Rectangle hCross = new Rectangle (layout.checkBounds.X + GetScaledNumber(offset - 1, scale), layout.checkBounds.Y + GetScaledNumber(offset, scale), GetScaledNumber(4, scale), GetScaledNumber(2, scale)); + wg.FillRectangle(brush, hCross); + } + } + } + } + + protected ButtonState GetState() { + ButtonState style = (ButtonState)0; + + if (Control.Checked) { + style |= ButtonState.Checked; + } + else { + style |= ButtonState.Normal; + } + + if (!Control.Enabled) { + style |= ButtonState.Inactive; + } + + if (Control.MouseIsDown) { + style |= ButtonState.Pushed; + } + + return style; + + } + + protected void DrawCheckBox(PaintEventArgs e, LayoutData layout) { + Graphics g = e.Graphics; + + Rectangle check = layout.checkBounds; + if (!Application.RenderWithVisualStyles) { + check.X--; // compensate for Windows drawing slightly offset to right + } + + ButtonState style = GetState(); + + if (Application.RenderWithVisualStyles) { + RadioButtonRenderer.DrawRadioButton(g, new Point(check.Left, check.Top), RadioButtonRenderer.ConvertFromButtonState(style, Control.MouseIsOver), Control.HandleInternal); + } + else { + ControlPaint.DrawRadioButton(g, check, style); + } + } + + #endregion + + protected void AdjustFocusRectangle(LayoutData layout) { + if (AccessibilityImprovements.Level2 && String.IsNullOrEmpty(Control.Text)) { + // When a RadioButton has no text, AutoSize sets the size to zero + // and thus there's no place around which to draw the focus rectangle. + // So, when AutoSize == true we want the focus rectangle to be rendered around the circle area. + // Otherwise, it should encircle all the available space next to the box (like it's done in WPF and ComCtl32). + layout.focus = Control.AutoSize ? layout.checkBounds : layout.field; + } + } + + internal override LayoutOptions CommonLayout() { + LayoutOptions layout = base.CommonLayout(); + layout.checkAlign = Control.CheckAlign; + + return layout; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonFlatAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonFlatAdapter.cs new file mode 100644 index 000000000..4fb5fe414 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonFlatAdapter.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + + internal class RadioButtonFlatAdapter : RadioButtonBaseAdapter { + + protected const int flatCheckSize = 12; + + internal RadioButtonFlatAdapter(ButtonBase control) : base(control) {} + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonFlatAdapter adapter = new ButtonFlatAdapter(Control); + adapter.PaintDown(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.highlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonFlatAdapter adapter = new ButtonFlatAdapter(Control); + adapter.PaintOver(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.lowHighlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonFlatAdapter adapter = new ButtonFlatAdapter(Control); + adapter.PaintUp(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + return; + } + + ColorData colors = PaintFlatRender(e.Graphics).Calculate(); + if (Control.Enabled) { + PaintFlatWorker(e, colors.windowText, colors.highlight, colors.windowFrame, colors); + } + else { + PaintFlatWorker(e, colors.buttonShadow, colors.buttonFace, colors.buttonShadow, colors); + } + } + + void PaintFlatWorker(PaintEventArgs e, Color checkColor, Color checkBackground, Color checkBorder, ColorData colors) { + System.Drawing.Graphics g = e.Graphics; + LayoutData layout = Layout(e).Layout(); + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + DrawCheckFlat(e, layout, checkColor, colors.options.highContrast ? colors.buttonFace : checkBackground, checkBorder); + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, checkColor, true); + } + + #region Layout + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonFlatAdapter(Control); + } + + // RadioButtonPopupLayout also uses this layout for down and over + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = CommonLayout(); + layout.checkSize = (int)(flatCheckSize * GetDpiScaleRatio(e.Graphics)); + layout.shadowedText = false; + + return layout; + } + + #endregion + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonPopupAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonPopupAdapter.cs new file mode 100644 index 000000000..4a6c4ed55 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonPopupAdapter.cs @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + + internal class RadioButtonPopupAdapter : RadioButtonFlatAdapter { + + internal RadioButtonPopupAdapter(ButtonBase control) : base(control) {} + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + System.Drawing.Graphics g = e.Graphics; + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintUp(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = Layout(e).Layout(); + + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + DrawCheckBackgroundFlat(e, layout.checkBounds, colors.buttonShadow, colors.options.highContrast ? colors.buttonFace : colors.highlight); + DrawCheckOnly(e, layout, colors.windowText, colors.highlight, true); + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + System.Drawing.Graphics g = e.Graphics; + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintOver(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = Layout(e).Layout(); + + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + Color checkBackgroundColor = (colors.options.highContrast && AccessibilityImprovements.Level1) ? colors.buttonFace : colors.highlight; + DrawCheckBackground3DLite(e, layout.checkBounds, colors.windowText, checkBackgroundColor, colors, true); + DrawCheckOnly(e, layout, colors.windowText, colors.highlight, true); + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + System.Drawing.Graphics g = e.Graphics; + if (Control.Appearance == Appearance.Button) { + ButtonPopupAdapter adapter = new ButtonPopupAdapter(Control); + adapter.PaintDown(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + ColorData colors = PaintPopupRender(e.Graphics).Calculate(); + LayoutData layout = Layout(e).Layout(); + + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + + DrawCheckBackground3DLite(e, layout.checkBounds, colors.windowText, colors.highlight, colors, true); + DrawCheckOnly(e, layout, colors.buttonShadow, colors.highlight, true); + + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + #region Layout + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonPopupAdapter(Control); + } + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = base.Layout(e); + + if (!Control.MouseIsDown && !Control.MouseIsOver) { + layout.shadowedText = true; + } + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonStandardAdapter.cs b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonStandardAdapter.cs new file mode 100644 index 000000000..aab44113b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonInternal/RadioButtonStandardAdapter.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ButtonInternal { + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.Drawing.Text; + using System.Windows.Forms; + + internal class RadioButtonStandardAdapter : RadioButtonBaseAdapter { + + internal RadioButtonStandardAdapter(ButtonBase control) : base(control) {} + + internal override void PaintUp(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintUp(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + ColorData colors = PaintRender(e.Graphics).Calculate(); + LayoutData layout = Layout(e).Layout(); + PaintButtonBackground(e, Control.ClientRectangle, null); + + PaintImage(e, layout); + DrawCheckBox(e, layout); + AdjustFocusRectangle(layout); + PaintField(e, layout, colors, colors.windowText, true); + } + } + + internal override void PaintDown(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintDown(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + PaintUp(e, state); + } + } + + internal override void PaintOver(PaintEventArgs e, CheckState state) { + if (Control.Appearance == Appearance.Button) { + ButtonAdapter.PaintOver(e, Control.Checked ? CheckState.Checked : CheckState.Unchecked); + } + else { + PaintUp(e, state); + } + } + + private new ButtonStandardAdapter ButtonAdapter { + get { + return ((ButtonStandardAdapter)base.ButtonAdapter); + } + } + + protected override ButtonBaseAdapter CreateButtonAdapter() { + return new ButtonStandardAdapter(Control); + } + + #region Temp + + protected override LayoutOptions Layout(PaintEventArgs e) { + LayoutOptions layout = CommonLayout(); + layout.hintTextUp = false; + layout.everettButtonCompat = !Application.RenderWithVisualStyles; + + // VSWhidbey 420870 + if (Application.RenderWithVisualStyles) { + ButtonBase b = Control; + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) { + layout.checkSize = RadioButtonRenderer.GetGlyphSize(g, RadioButtonRenderer.ConvertFromButtonState(GetState(), b.MouseIsOver), b.HandleInternal).Width; + } + } + // Dev10 bug 525537 + else { + layout.checkSize = (int)(layout.checkSize * GetDpiScaleRatio(e.Graphics)); + } + + return layout; + } + + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonRenderer.cs b/WindowsForms/Managed/System/WinForms/ButtonRenderer.cs new file mode 100644 index 000000000..9be49ccc9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonRenderer.cs @@ -0,0 +1,279 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + + using System; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Internal; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the Button control. It works downlevel too (obviously + /// without visual styles applied.) + /// + /// + public sealed class ButtonRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement ButtonElement = VisualStyleElement.Button.PushButton.Normal; + private static bool renderMatchingApplicationState = true; + + //cannot instantiate + private ButtonRenderer() { + } + + /// + /// + /// + /// If this property is true, then the renderer will use the setting from Application.RenderWithVisualStyles to + /// determine how to render. + /// If this property is false, the renderer will always render with visualstyles. + /// + /// + public static bool RenderMatchingApplicationState { + get { + return renderMatchingApplicationState; + } + set { + renderMatchingApplicationState = value; + } + } + + private static bool RenderWithVisualStyles { + get { + return (!renderMatchingApplicationState || Application.RenderWithVisualStyles); + } + } + + /// + /// + /// + /// Returns true if the background corresponding to the given state is partially transparent, else false. + /// + /// + public static bool IsBackgroundPartiallyTransparent(PushButtonState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + return visualStyleRenderer.IsBackgroundPartiallyTransparent(); + } + else { + return false; //for downlevel, this is false + } + } + + /// + /// + /// + /// This is just a convenience wrapper for VisualStyleRenderer.DrawThemeParentBackground. For downlevel, + /// this isn't required and does nothing. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static void DrawParentBackground(Graphics g, Rectangle bounds, Control childControl) { + if (RenderWithVisualStyles) { + InitializeRenderer(0); + visualStyleRenderer.DrawParentBackground(g, bounds, childControl); + } + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, PushButtonState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + else { + ControlPaint.DrawButton(g, bounds, ConvertToButtonState(state)); + } + } + + /// + /// Method to draw visualstyle themes in case of per-monitor scenarios where Hwnd is necessary + /// + /// graphics object + /// button bounds + /// is focused? + /// state + /// handle to the control + internal static void DrawButtonForHandle(Graphics g, Rectangle bounds, bool focused, PushButtonState state, IntPtr handle) { + Rectangle contentBounds; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, bounds, handle); + contentBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + } + else { + ControlPaint.DrawButton(g, bounds, ConvertToButtonState(state)); + contentBounds = Rectangle.Inflate(bounds, -3, -3); + } + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, bool focused, PushButtonState state) { + DrawButtonForHandle(g, bounds, focused, state, IntPtr.Zero); + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, string buttonText, Font font, bool focused, PushButtonState state) { + DrawButton(g, bounds, buttonText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + focused, state); + } + + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, string buttonText, Font font, TextFormatFlags flags, bool focused, PushButtonState state) { + Rectangle contentBounds; + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + contentBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + ControlPaint.DrawButton(g, bounds, ConvertToButtonState(state)); + contentBounds = Rectangle.Inflate(bounds, -3, -3); + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, buttonText, font, contentBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, Image image, Rectangle imageBounds, bool focused, PushButtonState state) { + Rectangle contentBounds; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + visualStyleRenderer.DrawImage(g, imageBounds, image); + contentBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + } + else { + ControlPaint.DrawButton(g, bounds, ConvertToButtonState(state)); + g.DrawImage(image, imageBounds); + contentBounds = Rectangle.Inflate(bounds, -3, -3); + } + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, string buttonText, Font font, Image image, Rectangle imageBounds, bool focused, PushButtonState state) { + DrawButton(g, bounds, buttonText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + image, imageBounds, focused, state); + } + + /// + /// + /// + /// Renders a Button control. + /// + /// + public static void DrawButton(Graphics g, Rectangle bounds, string buttonText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, PushButtonState state) { + Rectangle contentBounds; + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + visualStyleRenderer.DrawImage(g, imageBounds, image); + contentBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + ControlPaint.DrawButton(g, bounds, ConvertToButtonState(state)); + g.DrawImage(image, imageBounds); + contentBounds = Rectangle.Inflate(bounds, -3, -3); + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, buttonText, font, contentBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + internal static ButtonState ConvertToButtonState(PushButtonState state) { + switch (state) { + + case PushButtonState.Pressed: + return ButtonState.Pushed; + case PushButtonState.Disabled: + return ButtonState.Inactive; + + default: + return ButtonState.Normal; + } + } + + private static void InitializeRenderer(int state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(ButtonElement.ClassName, ButtonElement.Part, state); + } + else { + visualStyleRenderer.SetParameters(ButtonElement.ClassName, ButtonElement.Part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ButtonState.cs b/WindowsForms/Managed/System/WinForms/ButtonState.cs new file mode 100644 index 000000000..ba0609839 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ButtonState.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the appearance of a button. + /// + /// + /// + [Flags] + public enum ButtonState { + + /// + /// + /// + /// The button has a + /// checked or latched appearance. Use + /// this appearance to show that a toggle button has been pressed. + /// + /// + /// + Checked = NativeMethods.DFCS_CHECKED, + + /// + /// + /// + /// The button has a flat, two-dimensional appearance. + /// + /// + /// + Flat = NativeMethods.DFCS_FLAT, + + /// + /// + /// + /// The button is inactive (grayed). + /// + /// + /// + Inactive = NativeMethods.DFCS_INACTIVE, + + /// + /// + /// + /// + /// The button has its normal appearance + /// (three-dimensional and not pressed). + /// + /// + /// + Normal = 0, + + /// + /// + /// + /// + /// The button is currently pressed. + /// + /// + /// + Pushed = NativeMethods.DFCS_PUSHED, + + /// + /// + /// + /// All viable + /// flags in the bit mask are used. + /// + /// + /// + All = Flat | Checked | Pushed | Inactive, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEvent.cs b/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEvent.cs new file mode 100644 index 000000000..b43731148 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEvent.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +/* + */ +using System; +namespace System.Windows.Forms { + /// + public class CacheVirtualItemsEventArgs : EventArgs { + private int startIndex; + private int endIndex; + /// + public CacheVirtualItemsEventArgs(int startIndex, int endIndex) { + this.startIndex = startIndex; + this.endIndex = endIndex; + } + /// + public int StartIndex { + get { + return startIndex; + } + } + /// + public int EndIndex { + get { + return endIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEventHandler.cs b/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEventHandler.cs new file mode 100644 index 000000000..42691b6d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CacheVirtualItemsEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the CacheVirtualItems event of a ListView. + /// + /// + public delegate void CacheVirtualItemsEventHandler(object sender, CacheVirtualItemsEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/CaptionButton.cs b/WindowsForms/Managed/System/WinForms/CaptionButton.cs new file mode 100644 index 000000000..337118587 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CaptionButton.cs @@ -0,0 +1,68 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies the type + /// of caption button to display. + /// + /// + public enum CaptionButton { + + /// + /// + /// + /// A Close button. + /// + /// + /// + Close = NativeMethods.DFCS_CAPTIONCLOSE, + + /// + /// + /// + /// A Help button. + /// + /// + Help = NativeMethods.DFCS_CAPTIONHELP, + + /// + /// + /// + /// A Maximize button. + /// + /// + Maximize = NativeMethods.DFCS_CAPTIONMAX, + + /// + /// + /// + /// A Minimize button. + /// + /// + Minimize = NativeMethods.DFCS_CAPTIONMIN, + + /// + /// + /// + /// A Restore button. + /// + /// + Restore = NativeMethods.DFCS_CAPTIONRESTORE, + } +} diff --git a/WindowsForms/Managed/System/WinForms/CharacterCasing.cs b/WindowsForms/Managed/System/WinForms/CharacterCasing.cs new file mode 100644 index 000000000..422c35765 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CharacterCasing.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies the case of characters in a Textbox control. + /// + /// + public enum CharacterCasing { + + /// + /// + /// + /// The case of + /// characters is left unchanged. + /// + /// + Normal = 0, + + /// + /// + /// + /// Converts all characters to uppercase. + /// + /// + Upper = 1, + + /// + /// + /// + /// Converts all characters to lowercase. + /// + /// + Lower = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/CheckBox.cs b/WindowsForms/Managed/System/WinForms/CheckBox.cs new file mode 100644 index 000000000..3a886223b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CheckBox.cs @@ -0,0 +1,800 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Windows.Forms.ButtonInternal; + + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms.Layout; + + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// Represents a Windows + /// check box. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Checked"), + DefaultEvent("CheckedChanged"), + DefaultBindingProperty("CheckState"), + ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionCheckBox) + ] + public class CheckBox : ButtonBase { + private static readonly object EVENT_CHECKEDCHANGED = new object(); + private static readonly object EVENT_CHECKSTATECHANGED = new object(); + private static readonly object EVENT_APPEARANCECHANGED = new object(); + static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight; + + private bool autoCheck; + private bool threeState; + private bool accObjDoDefaultAction = false; + + private ContentAlignment checkAlign = ContentAlignment.MiddleLeft; + private CheckState checkState; + private Appearance appearance; + + private const int FlatSystemStylePaddingWidth = 25; + private const int FlatSystemStyleMinimumHeight = 13; + + internal int flatSystemStylePaddingWidth = FlatSystemStylePaddingWidth; + internal int flatSystemStyleMinimumHeight = FlatSystemStyleMinimumHeight; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public CheckBox() + : base() { + + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth); + flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight); + } + + // Checkboxes shouldn't respond to right clicks, so we need to do all our own click logic + SetStyle(ControlStyles.StandardClick | + ControlStyles.StandardDoubleClick, false); + + SetAutoSizeMode(AutoSizeMode.GrowAndShrink); + + autoCheck = true; + TextAlign = ContentAlignment.MiddleLeft; + + } + + private bool AccObjDoDefaultAction { + get { + return this.accObjDoDefaultAction; + } + set { + this.accObjDoDefaultAction = value; + } + } + + /// + /// + /// Gets + /// or sets the value that determines the appearance of a + /// check box control. + /// + [ + DefaultValue(Appearance.Normal), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.CheckBoxAppearanceDescr) + ] + public Appearance Appearance { + get { + return appearance; + } + + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)Appearance.Normal, (int)Appearance.Button)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(Appearance)); + } + + if (appearance != value) { + using (LayoutTransaction.CreateTransactionIf(AutoSize, this.ParentInternal, this, PropertyNames.Appearance)) { + appearance = value; + if (OwnerDraw) { + Refresh(); + } + else { + UpdateStyles(); + } + OnAppearanceChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.CheckBoxOnAppearanceChangedDescr)] + public event EventHandler AppearanceChanged { + add { + Events.AddHandler(EVENT_APPEARANCECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_APPEARANCECHANGED, value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the or + /// value and the check box's appearance are automatically + /// changed when it is clicked. + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.CheckBoxAutoCheckDescr) + ] + public bool AutoCheck { + get { + return autoCheck; + } + + set { + autoCheck = value; + } + } + + /// + /// + /// + /// Gets or sets + /// the horizontal and vertical alignment of a check box on a check box + /// control. + /// + /// + /// + [ + Bindable(true), + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(ContentAlignment.MiddleLeft), + SRDescription(SR.CheckBoxCheckAlignDescr) + ] + public ContentAlignment CheckAlign { + get { + return checkAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + + if (checkAlign != value) { + checkAlign = value; + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.CheckAlign); + if (OwnerDraw) { + Invalidate(); + } + else { + UpdateStyles(); + } + } + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating whether the + /// check box + /// is checked. + /// + /// + [ + Bindable(true), + SettingsBindable(true), + DefaultValue(false), + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.CheckBoxCheckedDescr) + ] + public bool Checked { + get { + return checkState != CheckState.Unchecked; + } + + set { + if (value != Checked) { + CheckState = value ? CheckState.Checked : CheckState.Unchecked; + } + } + } + + /// + /// + /// Gets + /// or sets a value indicating whether the check box is checked. + /// + [ + Bindable(true), + SRCategory(SR.CatAppearance), + DefaultValue(CheckState.Unchecked), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.CheckBoxCheckStateDescr) + ] + public CheckState CheckState { + get { + return checkState; + } + + set { + // valid values are 0-2 inclusive. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)CheckState.Unchecked, (int)CheckState.Indeterminate)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(CheckState)); + } + + if (checkState != value) { + + bool oldChecked = Checked; + + checkState = value; + + if (IsHandleCreated) { + SendMessage(NativeMethods.BM_SETCHECK, (int)checkState, 0); + } + + if (oldChecked != Checked) { + OnCheckedChanged(EventArgs.Empty); + } + OnCheckStateChanged(EventArgs.Empty); + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + /// + /// + /// Gets the information used to create the handle for the + /// + /// control. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "BUTTON"; + if (OwnerDraw) { + cp.Style |= NativeMethods.BS_OWNERDRAW; + } + else { + cp.Style |= NativeMethods.BS_3STATE; + if (Appearance == Appearance.Button) { + cp.Style |= NativeMethods.BS_PUSHLIKE; + } + + // Determine the alignment of the check box + // + ContentAlignment align = RtlTranslateContent(CheckAlign); + if ((int)(align & anyRight) != 0) { + cp.Style |= NativeMethods.BS_RIGHTBUTTON; + } + + } + + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(104, 24); + } + } + + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// For CheckBox controls, scale the width of the system-style padding, and height of the box. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on. + /// + /// Old DPI value + /// New DPI value + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth); + flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight); + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + if (Appearance == Appearance.Button) { + ButtonStandardAdapter adapter = new ButtonStandardAdapter(this); + return adapter.GetPreferredSizeCore(proposedConstraints); + } + + if(FlatStyle != FlatStyle.System) { + return base.GetPreferredSizeCore(proposedConstraints); + } + + Size textSize = TextRenderer.MeasureText(this.Text, this.Font); + Size size = SizeFromClientSize(textSize); + size.Width += flatSystemStylePaddingWidth; + size.Height = DpiHelper.EnableDpiChangedHighDpiImprovements ? Math.Max(size.Height + 5, flatSystemStyleMinimumHeight) : size.Height + 5; // ensure minimum height to avoid truncation of check-box or text + return size + Padding.Size; + } + + /// + /// + /// + /// + internal override Rectangle OverChangeRectangle { + get { + if (Appearance == Appearance.Button) { + return base.OverChangeRectangle; + } + else { + if (FlatStyle == FlatStyle.Standard) { + // this Rectangle will cause no Invalidation + // can't use Rectangle.Empty because it will cause Invalidate(ClientRectangle) + return new Rectangle(-1, -1, 1, 1); + } + else { + // Popup mouseover rectangle is actually bigger than GetCheckmarkRectangle + return Adapter.CommonLayout().Layout().checkBounds; + } + } + } + } + + /// + /// + /// + /// + internal override Rectangle DownChangeRectangle { + get { + if (Appearance == Appearance.Button || FlatStyle == FlatStyle.System) { + return base.DownChangeRectangle; + } + else { + // Popup mouseover rectangle is actually bigger than GetCheckmarkRectangle() + return Adapter.CommonLayout().Layout().checkBounds; + } + } + } + + /// + /// + /// + /// + /// Gets or sets a value indicating the alignment of the + /// text on the checkbox control. + /// + /// + /// + [ + Localizable(true), + DefaultValue(ContentAlignment.MiddleLeft) + ] + public override ContentAlignment TextAlign { + get { + return base.TextAlign; + } + set { + base.TextAlign = value; + } + } + + /// + /// + /// Gets or sets a value indicating + /// whether the check box will allow three check states rather than two. + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.CheckBoxThreeStateDescr) + ] + public bool ThreeState { + get { + return threeState; + } + set { + threeState = value; + } + } + + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckedChangedDescr)] + public event EventHandler CheckedChanged { + add { + Events.AddHandler(EVENT_CHECKEDCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CHECKEDCHANGED, value); + } + } + + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckStateChangedDescr)] + public event EventHandler CheckStateChanged { + add { + Events.AddHandler(EVENT_CHECKSTATECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CHECKSTATECHANGED, value); + } + } + + /// + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new CheckBoxAccessibleObject(this); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnAppearanceChanged(EventArgs e) { + EventHandler eh = Events[EVENT_APPEARANCECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnCheckedChanged(EventArgs e) { + // accessibility stuff + if (this.FlatStyle == FlatStyle.System) { + AccessibilityNotifyClients(AccessibleEvents.SystemCaptureStart, -1); + } + + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + + if (this.FlatStyle == FlatStyle.System) { + AccessibilityNotifyClients(AccessibleEvents.SystemCaptureEnd, -1); + } + + EventHandler handler = (EventHandler)Events[EVENT_CHECKEDCHANGED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnCheckStateChanged(EventArgs e) { + if (OwnerDraw) { + Refresh(); + } + + EventHandler handler = (EventHandler)Events[EVENT_CHECKSTATECHANGED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// + /// + /// Fires the event indicating that the control has been clicked. + /// Inheriting controls should use this in favour of actually listening to + /// the event, but should not forget to call base.onClicked() to + /// ensure that the event is still fired for external listeners. + /// + /// + /// + protected override void OnClick(EventArgs e) { + if (autoCheck) { + switch (CheckState) { + case CheckState.Unchecked: + CheckState = CheckState.Checked; + break; + case CheckState.Checked: + if (threeState) { + CheckState = CheckState.Indeterminate; + + // If the check box is clicked as a result of AccObj::DoDefaultAction + // then the native check box does not fire OBJ_STATE_CHANGE event when going to Indeterminate state. + // So the Microsoft layer fires the OBJ_STATE_CHANGE event. + // vsw 543351. + if (this.AccObjDoDefaultAction) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + } + } + else { + CheckState = CheckState.Unchecked; + } + break; + default: + CheckState = CheckState.Unchecked; + break; + } + } + base.OnClick(e); + } + + /// + /// + /// We override this to ensure that the control's click values are set up + /// correctly. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + // Since this is a protected override... + // this can be directly called in by a overriden class.. + // and the Handle need not be created... + // So Check for the handle + if (IsHandleCreated) { + SendMessage(NativeMethods.BM_SETCHECK, (int)checkState, 0); + } + } + + /// + /// We override this to ensure that press '+' or '=' checks the box, + /// while pressing '-' unchecks the box + /// + /// + protected override void OnKeyDown(KeyEventArgs e) { + //this fixes bug 235019, but it will be a breaking change from Everett + //see the comments attached to bug 235019 + /* + if (Enabled) { + if (e.KeyCode == Keys.Oemplus || e.KeyCode == Keys.Add) { + CheckState = CheckState.Checked; + } + if (e.KeyCode == Keys.OemMinus || e.KeyCode == Keys.Subtract) { + CheckState = CheckState.Unchecked; + } + } + */ + base.OnKeyDown(e); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + if (mevent.Button == MouseButtons.Left && MouseIsPressed) { + // It's best not to have the mouse captured while running Click events + if (base.MouseIsDown) { + Point pt = PointToScreen(new Point(mevent.X, mevent.Y)); + if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + //Paint in raised state... + ResetFlagsandPaint(); + if (!ValidationCancelled) { + if (this.Capture) { + OnClick(mevent); + } + OnMouseClick(mevent); + } + } + } + } + base.OnMouseUp(mevent); + } + + internal override ButtonBaseAdapter CreateFlatAdapter() { + return new CheckBoxFlatAdapter(this); + } + + internal override ButtonBaseAdapter CreatePopupAdapter() { + return new CheckBoxPopupAdapter(this); + } + + internal override ButtonBaseAdapter CreateStandardAdapter() { + return new CheckBoxStandardAdapter(this); + } + + /// + /// + /// Overridden to handle mnemonics properly. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (UseMnemonic && IsMnemonic(charCode, Text) && CanSelect) { + if (FocusInternal()) { + //Paint in raised state... + // + ResetFlagsandPaint(); + if (!ValidationCancelled) { + OnClick(EventArgs.Empty); + } + + } + return true; + } + return false; + } + + /// + /// + /// Provides some interesting information for the CheckBox control in + /// String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + // C#R cpb: 14744 (Microsoft) We shouldn't need to convert the enum to int -- EE M10 workitem. + int checkState = (int)CheckState; + return s + ", CheckState: " + checkState.ToString(CultureInfo.InvariantCulture); + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class CheckBoxAccessibleObject : ButtonBaseAccessibleObject { + + /// + /// + /// [To be supplied.] + /// + public CheckBoxAccessibleObject(Control owner) : base(owner) { + } + + /// + /// + /// [To be supplied.] + /// + public override string DefaultAction { + get { + string defaultAction = Owner.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + + if (((CheckBox)Owner).Checked) { + return SR.GetString(SR.AccessibleActionUncheck); + } + else { + return SR.GetString(SR.AccessibleActionCheck); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.CheckButton; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleStates State { + get { + switch (((CheckBox)Owner).CheckState) { + case CheckState.Checked: + return AccessibleStates.Checked | base.State; + case CheckState.Indeterminate: + return AccessibleStates.Indeterminate | base.State; + } + + return base.State; + } + } + + /// + /// + /// [To be supplied.] + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + CheckBox cb = this.Owner as CheckBox; + + if (cb != null) { + cb.AccObjDoDefaultAction = true; + } + + try { + base.DoDefaultAction(); + } finally { + if (cb != null) { + cb.AccObjDoDefaultAction = false; + } + } + + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/CheckBoxRenderer.cs b/WindowsForms/Managed/System/WinForms/CheckBoxRenderer.cs new file mode 100644 index 000000000..2bd78eccb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CheckBoxRenderer.cs @@ -0,0 +1,359 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.Internal; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + + /// + /// + /// + /// This is a rendering class for the CheckBox control. It works downlevel too (obviously + /// without visual styles applied.) + /// + /// + public sealed class CheckBoxRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement CheckBoxElement = VisualStyleElement.Button.CheckBox.UncheckedNormal; + private static bool renderMatchingApplicationState = true; + + //cannot instantiate + private CheckBoxRenderer() { + } + + /// + /// + /// + /// If this property is true, then the renderer will use the setting from Application.RenderWithVisualStyles to + /// determine how to render. + /// If this property is false, the renderer will always render with visualstyles. + /// + /// + public static bool RenderMatchingApplicationState { + get { + return renderMatchingApplicationState; + } + set { + renderMatchingApplicationState = value; + } + } + + private static bool RenderWithVisualStyles { + get { + return (!renderMatchingApplicationState || Application.RenderWithVisualStyles); + } + } + + /// + /// + /// + /// Returns true if the background corresponding to the given state is partially transparent, else false. + /// + /// + public static bool IsBackgroundPartiallyTransparent(CheckBoxState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + return visualStyleRenderer.IsBackgroundPartiallyTransparent(); + } + else { + return false; //for downlevel, this is false + } + } + + /// + /// + /// + /// This is just a convenience wrapper for VisualStyleRenderer.DrawThemeParentBackground. For downlevel, + /// this isn't required and does nothing. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawParentBackground(Graphics g, Rectangle bounds, Control childControl) { + if (RenderWithVisualStyles) { + InitializeRenderer(0); + + visualStyleRenderer.DrawParentBackground(g, bounds, childControl); + } + } + + /// + /// + /// + /// Renders a CheckBox control. + /// + /// + public static void DrawCheckBox(Graphics g, Point glyphLocation, CheckBoxState state) { + DrawCheckBox(g, glyphLocation, state, IntPtr.Zero); + } + + internal static void DrawCheckBox(Graphics g, Point glyphLocation, CheckBoxState state, IntPtr hWnd) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state, hWnd)); + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, glyphBounds, hWnd); + } + else { + if (IsMixed(state)) { + ControlPaint.DrawMixedCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + else { + ControlPaint.DrawCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + } + + } + + /// + /// + /// + /// Renders a CheckBox control. + /// + /// + public static void DrawCheckBox(Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, bool focused, CheckBoxState state) { + DrawCheckBox(g, glyphLocation, textBounds, checkBoxText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + focused, state); + } + + /// + /// + /// + /// Renders a CheckBox control. + /// + /// + public static void DrawCheckBox(Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, TextFormatFlags flags, bool focused, CheckBoxState state) { + DrawCheckBox(g, glyphLocation, textBounds, checkBoxText, font, flags, focused, state, IntPtr.Zero); + } + + internal static void DrawCheckBox(Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, TextFormatFlags flags, bool focused, CheckBoxState state, IntPtr hWnd) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state, hWnd)); + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, glyphBounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + if (IsMixed(state)) { + ControlPaint.DrawMixedCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + else { + ControlPaint.DrawCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, checkBoxText, font, textBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, textBounds); + } + } + + /// + /// + /// + /// Renders a CheckBox control. + /// + /// + public static void DrawCheckBox(Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, Image image, Rectangle imageBounds, bool focused, CheckBoxState state) { + DrawCheckBox(g, glyphLocation, textBounds, checkBoxText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + image, imageBounds, focused, state); + } + + /// + /// + /// + /// Renders a CheckBox control. + /// + /// + public static void DrawCheckBox(Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, CheckBoxState state) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state)); + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + //Keep this drawing order! It matches default drawing order. + visualStyleRenderer.DrawImage(g, imageBounds, image); + visualStyleRenderer.DrawBackground(g, glyphBounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + g.DrawImage(image, imageBounds); + if (IsMixed(state)) { + ControlPaint.DrawMixedCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + else { + ControlPaint.DrawCheckBox(g, glyphBounds, ConvertToButtonState(state)); + } + + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, checkBoxText, font, textBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, textBounds); + } + } + + /// + /// + /// + /// Returns the size of the CheckBox glyph. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static Size GetGlyphSize(Graphics g, CheckBoxState state) { + return GetGlyphSize(g, state, IntPtr.Zero); + } + + internal static Size GetGlyphSize(Graphics g, CheckBoxState state, IntPtr hWnd) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + return visualStyleRenderer.GetPartSize(g, ThemeSizeType.Draw, hWnd); + } + + return new Size(13, 13); + } + + internal static ButtonState ConvertToButtonState(CheckBoxState state) { + switch (state) { + + case CheckBoxState.CheckedNormal: + case CheckBoxState.CheckedHot: + return ButtonState.Checked; + case CheckBoxState.CheckedPressed: + return (ButtonState.Checked | ButtonState.Pushed); + case CheckBoxState.CheckedDisabled: + return (ButtonState.Checked | ButtonState.Inactive); + + case CheckBoxState.UncheckedPressed: + return ButtonState.Pushed; + case CheckBoxState.UncheckedDisabled: + return ButtonState.Inactive; + + //Downlevel mixed drawing works only if ButtonState.Checked is set + case CheckBoxState.MixedNormal: + case CheckBoxState.MixedHot: + return ButtonState.Checked; + case CheckBoxState.MixedPressed: + return (ButtonState.Checked | ButtonState.Pushed); + case CheckBoxState.MixedDisabled: + return (ButtonState.Checked | ButtonState.Inactive); + + default: + return ButtonState.Normal; + } + } + + internal static CheckBoxState ConvertFromButtonState(ButtonState state, bool isMixed, bool isHot) { + if (isMixed) { + if ((state & ButtonState.Pushed) == ButtonState.Pushed) { + return CheckBoxState.MixedPressed; + } + else if ((state & ButtonState.Inactive) == ButtonState.Inactive) { + return CheckBoxState.MixedDisabled; + } + else if (isHot) { + return CheckBoxState.MixedHot; + } + + return CheckBoxState.MixedNormal; + } + else if ((state & ButtonState.Checked) == ButtonState.Checked) { + if ((state & ButtonState.Pushed) == ButtonState.Pushed) { + return CheckBoxState.CheckedPressed; + } + else if ((state & ButtonState.Inactive) == ButtonState.Inactive) { + return CheckBoxState.CheckedDisabled; + } + else if (isHot) { + return CheckBoxState.CheckedHot; + } + + return CheckBoxState.CheckedNormal; + } + else { //unchecked + if ((state & ButtonState.Pushed) == ButtonState.Pushed) { + return CheckBoxState.UncheckedPressed; + } + else if ((state & ButtonState.Inactive) == ButtonState.Inactive) { + return CheckBoxState.UncheckedDisabled; + } + else if (isHot) { + return CheckBoxState.UncheckedHot; + } + + return CheckBoxState.UncheckedNormal; + } + } + + private static bool IsMixed(CheckBoxState state) { + switch (state) { + + case CheckBoxState.MixedNormal: + case CheckBoxState.MixedHot: + case CheckBoxState.MixedPressed: + case CheckBoxState.MixedDisabled: + return true; + + default: + return false; + } + } + + private static bool IsDisabled(CheckBoxState state) { + switch (state) { + case CheckBoxState.CheckedDisabled: + case CheckBoxState.UncheckedDisabled: + case CheckBoxState.MixedDisabled: + return true; + + default: + return false; + } + } + + private static void InitializeRenderer(int state) { + int part = CheckBoxElement.Part; + if (AccessibilityImprovements.Level2 + && SystemInformation.HighContrast + && IsDisabled((CheckBoxState)state) + && VisualStyleRenderer.IsCombinationDefined(CheckBoxElement.ClassName, VisualStyleElement.Button.CheckBox.HighContrastDisabledPart)) { + part = VisualStyleElement.Button.CheckBox.HighContrastDisabledPart; + } + + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(CheckBoxElement.ClassName, part, state); + } + else { + visualStyleRenderer.SetParameters(CheckBoxElement.ClassName, part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CheckState.cs b/WindowsForms/Managed/System/WinForms/CheckState.cs new file mode 100644 index 000000000..f6b709fa1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CheckState.cs @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the state of a control, + /// such as + /// a check + /// box, that can be checked, unchecked, or + /// set to an indeterminate state. + /// + /// + public enum CheckState { + + /// + /// + /// + /// The control is unchecked. + /// + /// + /// + Unchecked = 0, + + /// + /// + /// + /// The control is checked. + /// + /// + /// + Checked = 1, + + /// + /// + /// + /// The control + /// is indeterminate. An indeterminate control generally has a shaded appearance. + /// + /// + /// + Indeterminate = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/CheckedListBox.cs b/WindowsForms/Managed/System/WinForms/CheckedListBox.cs new file mode 100644 index 000000000..49bd46674 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CheckedListBox.cs @@ -0,0 +1,1801 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + + using System.Text; + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.ComponentModel; + + using System.Drawing; + using System.ComponentModel; + using System.Runtime.InteropServices; + + using Hashtable = System.Collections.Hashtable; + using Microsoft.Win32; + + using System.Drawing.Design; + using System.Globalization; + using System.Drawing.Text; + + + /// + /// + /// + /// + /// Displays a list with a checkbox to the left + /// + /// of each item. + /// + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + LookupBindingProperties(), // ...overrides equivalent attribute in ListControl + SRDescription(SR.DescriptionCheckedListBox) + ] + public class CheckedListBox : ListBox { + + private int idealCheckSize = 13; + + private const int LB_CHECKED = 1; + private const int LB_UNCHECKED = 0; + private const int LB_ERROR = -1; + private const int BORDER_SIZE = 1; + + + /// + /// + /// Decides whether or not to ignore the next LBN_SELCHANGE + /// message - used to prevent cursor keys from toggling checkboxes + /// + private bool killnextselect = false; + + /// + /// + /// Current listener of the onItemCheck event. + /// + private ItemCheckEventHandler onItemCheck; + + /// + /// + /// Indicates whether or not we should toggle check state on the first + /// click on an item, or whether we should wait for the user to click + /// again. + /// + private bool checkOnClick = false; + + /// + /// + /// Should we use 3d checkboxes or flat ones? + /// + private bool flat = true; + + /// + /// + /// Indicates which item was last selected. We want to keep track + /// of this so we can be a little less aggressive about checking/ + /// unchecking the items as the user moves around. + /// + private int lastSelected = -1; + + /// + /// + /// The collection of checked items in the CheckedListBox. + /// + private CheckedItemCollection checkedItemCollection = null; + private CheckedIndexCollection checkedIndexCollection = null; + + private static int LBC_GETCHECKSTATE; + private static int LBC_SETCHECKSTATE; + + static CheckedListBox() { + LBC_GETCHECKSTATE = SafeNativeMethods.RegisterWindowMessage("LBC_GETCHECKSTATE"); + LBC_SETCHECKSTATE = SafeNativeMethods.RegisterWindowMessage("LBC_SETCHECKSTATE"); + } + + /// + /// + /// Creates a new CheckedListBox for the user. + /// + public CheckedListBox() : base() { + // If we eat WM_ERASEBKGRND messages, the background will be + // painted sometimes but not others. See ASURT 28545. + // SetStyle(ControlStyles.Opaque, true); + + // If a long item is drawn with ellipsis, we must redraw the ellipsed part + // as well as the newly uncovered region. + SetStyle(ControlStyles.ResizeRedraw, true); + + } + + /// + /// + /// Indicates whether or not the checkbox should be toggled whenever an + /// item is selected. The default behaviour is to just change the + /// selection, and then make the user click again to check it. However, + /// some may prefer checking the item as soon as it is clicked. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.CheckedListBoxCheckOnClickDescr) + ] + public bool CheckOnClick { + get { + return checkOnClick; + } + + set { + checkOnClick = value; + } + } + + /// + /// + /// Collection of checked indices in this CheckedListBox. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public CheckedIndexCollection CheckedIndices { + get { + if (checkedIndexCollection == null) { + checkedIndexCollection = new CheckedIndexCollection(this); + } + return checkedIndexCollection; + } + } + + /// + /// + /// Collection of checked items in this CheckedListBox. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public CheckedItemCollection CheckedItems { + get { + if (checkedItemCollection == null) { + checkedItemCollection = new CheckedItemCollection(this); + } + return checkedItemCollection; + } + } + + /// + /// + /// This is called when creating a window. Inheriting classes can ovveride + /// this to add extra functionality, but should not forget to first call + /// base.CreateParams() to make sure the control continues to work + /// correctly. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.Style |= NativeMethods.LBS_OWNERDRAWFIXED | NativeMethods.LBS_WANTKEYBOARDINPUT; + return cp; + } + } + + + /// + /// + /// CheckedListBox DataSource. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new object DataSource { + get { + return base.DataSource; + } + set { + base.DataSource = value; + } + } + + /// + /// + /// CheckedListBox DisplayMember. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new string DisplayMember { + get { + return base.DisplayMember ; + } + set { + base.DisplayMember = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override DrawMode DrawMode { + get { + return DrawMode.Normal; + } + set { + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override int ItemHeight { + get { + // this should take FontHeight + buffer into Consideration. + return Font.Height + scaledListItemBordersHeight; + } + set { + } + } + + /// + /// + /// Collection of items in this listbox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ListBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + new public CheckedListBox.ObjectCollection Items { + get { + return(CheckedListBox.ObjectCollection)base.Items; + } + } + + // Computes the maximum width of all items in the ListBox + // + internal override int MaxItemWidth { + get { + // Overridden to include the size of the checkbox + // Allows for one pixel either side of the checkbox, plus another 1 pixel buffer = 3 pixels + // + return base.MaxItemWidth + idealCheckSize + scaledListItemPaddingBuffer; + } + } + + /// + /// + /// For CheckedListBoxes, multi-selection is not supported. You can set + /// selection to be able to select one item or no items. + /// + public override SelectionMode SelectionMode { + get { + return base.SelectionMode; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SelectionMode.None, (int)SelectionMode.MultiExtended)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(SelectionMode)); + } + if (value != SelectionMode.One + && value != SelectionMode.None) { + throw new ArgumentException(SR.GetString(SR.CheckedListBoxInvalidSelectionMode)); + } + + if (value != SelectionMode) { + base.SelectionMode = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates if the CheckBoxes should show up as flat or 3D in appearance. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.CheckedListBoxThreeDCheckBoxesDescr) + ] + public bool ThreeDCheckBoxes { + get { + return !flat; + } + set { + // change the style and repaint. + // + if (flat == value) { + flat = !value; + + // see if we have some items, and only invalidate if we do. + CheckedListBox.ObjectCollection items = (CheckedListBox.ObjectCollection) Items; + if ((items != null) && (items.Count > 0)) { + this.Invalidate(); + } + } + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public bool UseCompatibleTextRendering { + get{ + return base.UseCompatibleTextRenderingInt; + } + set{ + base.UseCompatibleTextRenderingInt = value; + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same + /// value if the child control supports it. + /// + internal override bool SupportsUseCompatibleTextRendering { + get { + return true; + } + } + + /// + /// + /// CheckedListBox ValueMember. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new string ValueMember { + get { + return base.ValueMember; + } + set { + base.ValueMember = value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DataSourceChanged { + add { + base.DataSourceChanged += value; + } + remove { + base.DataSourceChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DisplayMemberChanged { + add { + base.DisplayMemberChanged += value; + } + remove { + base.DisplayMemberChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.CheckedListBoxItemCheckDescr)] + public event ItemCheckEventHandler ItemCheck { + add { + onItemCheck += value; + } + remove { + onItemCheck -= value; + } + } + + /// + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event DrawItemEventHandler DrawItem { + add { + base.DrawItem += value; + } + remove { + base.DrawItem -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MeasureItemEventHandler MeasureItem { + add { + base.MeasureItem += value; + } + remove { + base.MeasureItem -= value; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ValueMemberChanged { + add { + base.ValueMemberChanged += value; + } + remove { + base.ValueMemberChanged -= value; + } + } + + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new CheckedListBoxAccessibleObject(this); + } + + /// + /// + /// [To be supplied.] + /// + protected override ListBox.ObjectCollection CreateItemCollection() { + return new ObjectCollection(this); + } + + /// + /// + /// Gets the check value of the current item. This value will be from the + /// System.Windows.Forms.CheckState enumeration. + /// + public CheckState GetItemCheckState(int index) { + + if (index < 0 || index >= Items.Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + return CheckedItems.GetCheckedState(index); + } + + /// + /// + /// Indicates if the given item is, in any way, shape, or form, checked. + /// This will return true if the item is fully or indeterminately checked. + /// + public bool GetItemChecked(int index) { + return(GetItemCheckState(index) != CheckState.Unchecked); + } + + /// + /// + /// Invalidates the given item in the listbox + /// + /// + private void InvalidateItem(int index) { + if (IsHandleCreated) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + SendMessage(NativeMethods.LB_GETITEMRECT, index, ref rect); + SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), ref rect, false); + } + } + + /// + /// + /// A redirected LBN_SELCHANGE message notification. + /// + /// + private void LbnSelChange() { + + // prepare to change the selection. we'll fire an event for + // this. Note that we'll only change the selection when the + // user clicks again on a currently selected item, or when the + // user has CheckOnClick set to true. Otherwise + // just using the up and down arrows selects or unselects + // every item around town ... + // + + // Get the index of the item to check/uncheck + int index = SelectedIndex; + + // make sure we have a valid index, otherwise we're going to + // fail ahead... + if (index < 0 || index >= Items.Count) + return; + + // Send an accessibility notification + // + AccessibilityNotifyClients(AccessibleEvents.Focus, index); + AccessibilityNotifyClients(AccessibleEvents.Selection, index); + + + //# VS7 86 + if (!killnextselect && (index == lastSelected || checkOnClick == true)) { + CheckState currentValue = CheckedItems.GetCheckedState(index); + CheckState newValue = (currentValue != CheckState.Unchecked) + ? CheckState.Unchecked + : CheckState.Checked; + + ItemCheckEventArgs itemCheckEvent = new ItemCheckEventArgs(index, newValue, currentValue); + OnItemCheck(itemCheckEvent); + + // take whatever value the user set, and set that as the value. + // + CheckedItems.SetCheckedState(index, itemCheckEvent.NewValue); + + // Send accessibility notifications for state change + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, index); + AccessibilityNotifyClients(AccessibleEvents.NameChange, index); + } + } + + lastSelected = index; + InvalidateItem(index); + } + + /// + /// + /// Ensures that mouse clicks can toggle... + /// + /// + protected override void OnClick(EventArgs e) { + killnextselect = false; + base.OnClick(e); + } + + + /// + /// + /// When the handle is created we can dump any cached item-check pairs. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); + + } + + /// + /// + /// Actually goes and fires the drawItem event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler yourself for this event]. They should, + /// however, remember to call base.OnDrawItem(e); to ensure the event is + /// still fired to external listeners + /// + protected override void OnDrawItem(DrawItemEventArgs e) { + object item; + + if (Font.Height < 0) + { + this.Font = Control.DefaultFont; + } + + if (e.Index >= 0) { + if (e.Index < Items.Count) { + item = Items[e.Index]; + } + else { + // If the item is not part of our collection, we will just + // get the string for it and display it. + // + item = NativeGetItemText(e.Index); + } + + Rectangle bounds = e.Bounds; + int height = this.ItemHeight; + + // set up the appearance of the checkbox + // + ButtonState state = ButtonState.Normal; + if (flat) { + state |= ButtonState.Flat; + } + if (e.Index < Items.Count) { + switch (CheckedItems.GetCheckedState(e.Index)) { + case CheckState.Checked: + state |= ButtonState.Checked; + break; + case CheckState.Indeterminate: + state |= ButtonState.Checked | ButtonState.Inactive; + break; + } + } + + // If we are drawing themed CheckBox .. get the size from renderer.. + // the Renderer might return a different size in different DPI modes.. + if (Application.RenderWithVisualStyles) { + VisualStyles.CheckBoxState cbState = CheckBoxRenderer.ConvertFromButtonState(state, false, ((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)); + idealCheckSize = (int)(CheckBoxRenderer.GetGlyphSize(e.Graphics, cbState, HandleInternal)).Width; + } + + // Determine bounds for the checkbox + // + int centeringFactor = Math.Max((height - idealCheckSize) / 2, 0); + + // Keep the checkbox within the item's upper and lower bounds + if (centeringFactor + idealCheckSize > bounds.Height) { + centeringFactor = bounds.Height - idealCheckSize; + } + + Rectangle box = new Rectangle(bounds.X + scaledListItemStartPosition, + bounds.Y + centeringFactor, + idealCheckSize, + idealCheckSize); + + if (RightToLeft == RightToLeft.Yes) { + // For a RightToLeft checked list box, we want the checkbox + // to be drawn at the right. + // So we override the X position. + box.X = bounds.X + bounds.Width - idealCheckSize - scaledListItemStartPosition; + } + + + + // Draw the checkbox. + // + if (Application.RenderWithVisualStyles) { + VisualStyles.CheckBoxState cbState = CheckBoxRenderer.ConvertFromButtonState(state, false, ((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)); + CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(box.X, box.Y), cbState, HandleInternal); + } + else { + ControlPaint.DrawCheckBox(e.Graphics, box, state); + } + + // Determine bounds for the text portion of the item + // + Rectangle textBounds = new Rectangle( + bounds.X + idealCheckSize + (scaledListItemStartPosition * 2), + bounds.Y, + bounds.Width - (idealCheckSize + (scaledListItemStartPosition * 2)), + bounds.Height); + if (RightToLeft == RightToLeft.Yes) { + // For a RightToLeft checked list box, we want the text + // to be drawn at the left. + // So we override the X position. + textBounds.X = bounds.X; + } + + // Setup text font, color, and text + // + string text = ""; + Color backColor = (SelectionMode != SelectionMode.None) ? e.BackColor : BackColor; + Color foreColor = (SelectionMode != SelectionMode.None) ? e.ForeColor : ForeColor; + if (!Enabled) { + foreColor = SystemColors.GrayText; + } + Font font = Font; + + text = GetItemText(item); + + if (SelectionMode != SelectionMode.None && (e.State & DrawItemState.Selected) == DrawItemState.Selected) { + if (Enabled) + { + backColor = SystemColors.Highlight; + foreColor = SystemColors.HighlightText; + } + else + { + backColor = SystemColors.InactiveBorder; + foreColor = SystemColors.GrayText; + } + } + + // Draw the text + // + + // Due to some sort of unpredictable painting optimization in the Windows ListBox control, + // we need to always paint the background rectangle for the current line. + using (Brush b = new SolidBrush(backColor)) { + e.Graphics.FillRectangle(b, textBounds); + } + + Rectangle stringBounds = new Rectangle( + textBounds.X + BORDER_SIZE, + textBounds.Y, + textBounds.Width - BORDER_SIZE, + textBounds.Height - 2 * BORDER_SIZE); // minus borders + + if( UseCompatibleTextRendering ){ + using (StringFormat format = new StringFormat()) { + if (UseTabStops) { + // Set tab stops so it looks similar to a ListBox, at least with the default font size. + float tabDistance = 3.6f * Font.Height; // about 7 characters + float[] tabStops = new float[15]; + float tabOffset = -(idealCheckSize + (scaledListItemStartPosition * 2)); + for (int i = 1; i < tabStops.Length; i++) + tabStops[i] = tabDistance; + + //(bug 111825) + if (Math.Abs(tabOffset) < tabDistance) { + tabStops[0] = tabDistance +tabOffset; + } + else { + tabStops[0] = tabDistance; + } + format.SetTabStops(0, tabStops); + } + else if (UseCustomTabOffsets) { + //Set TabStops to userDefined values + int wpar = CustomTabOffsets.Count; + float[] tabStops = new float[wpar]; + CustomTabOffsets.CopyTo(tabStops, 0); + format.SetTabStops(0, tabStops); + } + + // Adjust string format for Rtl controls + if (RightToLeft == RightToLeft.Yes) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + } + + // ListBox doesn't word-wrap its items, so neither should CheckedListBox + // + format.FormatFlags |= StringFormatFlags.NoWrap; + + // VSWhidbey 95774: Set Trimming to None to prevent DrawString() from whacking the entire + // string when there is only one character per tab included in the string. + format.Trimming = StringTrimming.None; + + // Do actual drawing + using (SolidBrush brush = new SolidBrush(foreColor)) { + e.Graphics.DrawString(text, font, brush, stringBounds, format); + } + } + } + else{ + TextFormatFlags flags = TextFormatFlags.Default; + flags |= TextFormatFlags.NoPrefix; + + if (UseTabStops || UseCustomTabOffsets) { + flags |= TextFormatFlags.ExpandTabs; + } + + // Adjust string format for Rtl controls + if (RightToLeft == RightToLeft.Yes) { + flags |= TextFormatFlags.RightToLeft; + flags |= TextFormatFlags.Right; + } + + // Do actual drawing + TextRenderer.DrawText(e.Graphics, text, font, stringBounds, foreColor, flags ); + } + + // Draw the focus rect if required + // + if ((e.State & DrawItemState.Focus) == DrawItemState.Focus && + (e.State & DrawItemState.NoFocusRect) != DrawItemState.NoFocusRect) { + ControlPaint.DrawFocusRectangle(e.Graphics, textBounds, foreColor, backColor); + } + } + + if (Items.Count == 0 && AccessibilityImprovements.Level3 && + e.Bounds.Width > 2 * BORDER_SIZE && e.Bounds.Height > 2 * BORDER_SIZE) { + Color backColor = (SelectionMode != SelectionMode.None) ? e.BackColor : BackColor; + Rectangle bounds = e.Bounds; + Rectangle emptyRectangle = new Rectangle( + bounds.X + BORDER_SIZE, + bounds.Y, + bounds.Width - BORDER_SIZE, + bounds.Height - 2 * BORDER_SIZE); // Upper and lower borders. + if (Focused) { + // Draw focus rectangle for virtual first item in the list if there are no items in the list. + Color foreColor = (SelectionMode != SelectionMode.None) ? e.ForeColor : ForeColor; + if (!Enabled) { + foreColor = SystemColors.GrayText; + } + + ControlPaint.DrawFocusRectangle(e.Graphics, emptyRectangle, foreColor, backColor); + } + else if (!Application.RenderWithVisualStyles) { + // If VisualStyles are off, rectangle needs to be explicitly erased, when focus is lost. + // This is because of persisting empty focus rectangle when VisualStyles are off. + using (Brush brush = new SolidBrush(backColor)) { + e.Graphics.FillRectangle(brush, emptyRectangle); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + + if (IsHandleCreated) { + SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), null, true); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + + // Update the item height + // + if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); + } + + // The base OnFontChanged will adjust the height of the CheckedListBox accordingly + // + base.OnFontChanged(e); + } + + /// + /// + /// This is the code that actually fires the "keyPress" event. The Checked + /// ListBox overrides this to look for space characters, since we + /// want to use those to check or uncheck items periodically. Don't + /// forget to call base.OnKeyPress() to ensure that KeyPrese events + /// are correctly fired for all other keys. + /// + /// + protected override void OnKeyPress(KeyPressEventArgs e) { + if (e.KeyChar == ' ' && SelectionMode != SelectionMode.None) { + LbnSelChange(); + } + if (FormattingEnabled) //We want to fire KeyPress only when FormattingEnabled (this is a whidbey property) + { + base.OnKeyPress(e); + } + } + + /// + /// + /// This is the code that actually fires the itemCheck event. Don't + /// forget to call base.onItemCheck() to ensure that itemCheck vents + /// are correctly fired for all other keys. + /// + /// + protected virtual void OnItemCheck(ItemCheckEventArgs ice) { + if (onItemCheck != null) onItemCheck(this, ice); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnMeasureItem(MeasureItemEventArgs e) { + base.OnMeasureItem(e); + + // we'll use the ideal checkbox size plus enough for padding on the top + // and bottom + // + if (e.ItemHeight < idealCheckSize + 2) { + e.ItemHeight = idealCheckSize + 2; + } + } + + /// + /// + /// Actually goes and fires the selectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected override void OnSelectedIndexChanged(EventArgs e) { + + base.OnSelectedIndexChanged(e); + lastSelected = SelectedIndex; + + } + + + + /// + /// + /// Reparses the objects, getting new text strings for them. + /// + /// + protected override void RefreshItems() { + Hashtable savedcheckedItems = new Hashtable(); + for (int i =0; i < Items.Count ; i ++) + { + savedcheckedItems[i] = CheckedItems.GetCheckedState(i); + } + + //call the base + base.RefreshItems(); + // restore the checkedItems... + + for (int j =0; j < Items.Count; j++) + { + CheckedItems.SetCheckedState(j, (CheckState)savedcheckedItems[j]); + } + } + + /// + /// + /// Sets the checked value of the given item. This value should be from + /// the System.Windows.Forms.CheckState enumeration. + /// + public void SetItemCheckState(int index, CheckState value) { + if (index < 0 || index >= Items.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + // valid values are 0-2 inclusive. + if (!ClientUtils.IsEnumValid(value,(int)value, (int)CheckState.Unchecked, (int)CheckState.Indeterminate)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(CheckState)); + } + CheckState currentValue = CheckedItems.GetCheckedState(index); + + if (value != currentValue) { + ItemCheckEventArgs itemCheckEvent = new ItemCheckEventArgs(index, value, currentValue); + OnItemCheck(itemCheckEvent); + + if (itemCheckEvent.NewValue != currentValue) { + CheckedItems.SetCheckedState(index, itemCheckEvent.NewValue); + InvalidateItem(index); + } + } + } + + /// + /// + /// Sets the checked value of the given item. This value should be a + /// boolean. + /// + public void SetItemChecked(int index, bool value) { + SetItemCheckState(index, value ? CheckState.Checked : CheckState.Unchecked); + } + + /// + /// + /// We need to get LBN_SELCHANGE notifications + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected override void WmReflectCommand(ref Message m) { + switch (NativeMethods.Util.HIWORD(m.WParam)) { + case NativeMethods.LBN_SELCHANGE: + LbnSelChange(); + // finally, fire the OnSelectionChange event. + base.WmReflectCommand(ref m); + break; + + case NativeMethods.LBN_DBLCLK: + // We want double-clicks to change the checkstate on each click - just like the CheckBox control + // + LbnSelChange(); + base.WmReflectCommand(ref m); + break; + + default: + base.WmReflectCommand(ref m); + break; + } + } + + /// + /// + /// Handle keyboard input to prevent arrow keys from toggling + /// checkboxes in CheckOnClick mode. + /// + /// + private void WmReflectVKeyToItem(ref Message m) { + int keycode = NativeMethods.Util.LOWORD(m.WParam); + switch ((Keys)keycode) { + case Keys.Up: + case Keys.Down: + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + case Keys.Left: + case Keys.Right: + killnextselect = true; + break; + default: + killnextselect = false; + break; + } + m.Result = NativeMethods.InvalidIntPtr; + } + + /// + /// + /// The listbox's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the button continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_CHARTOITEM: + m.Result = NativeMethods.InvalidIntPtr; + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_VKEYTOITEM: + WmReflectVKeyToItem(ref m); + break; + default: + if (m.Msg == LBC_GETCHECKSTATE) { + int item = unchecked( (int) (long)m.WParam); + if (item < 0 || item >= Items.Count) { + m.Result = (IntPtr)LB_ERROR; + } + else { + m.Result = (IntPtr)(GetItemChecked(item) ? LB_CHECKED : LB_UNCHECKED); + } + } + else if (m.Msg == LBC_SETCHECKSTATE) { + int item = unchecked( (int) (long)m.WParam); + int state = unchecked( (int) (long)m.LParam); + if (item < 0 || item >= Items.Count || (state != LB_CHECKED && state != LB_UNCHECKED)) { + m.Result = IntPtr.Zero; + } + else { + SetItemChecked(item, (state == LB_CHECKED)); + m.Result = (IntPtr)1; + } + } + else { + base.WndProc(ref m); + } + break; + } + } + + /// + /// + /// [To be supplied.] + /// + new public class ObjectCollection : ListBox.ObjectCollection { + private CheckedListBox owner; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(CheckedListBox owner) + : base(owner) { + this.owner = owner; + } + + /// + /// + /// Lets the user add an item to the listbox with the given initial value + /// for the Checked portion of the item. + /// + public int Add(object item, bool isChecked) { + return Add(item, isChecked ? CheckState.Checked : CheckState.Unchecked); + } + + /// + /// + /// Lets the user add an item to the listbox with the given initial value + /// for the Checked portion of the item. + /// + public int Add(object item, CheckState check) { + + //validate the enum that's passed in here + // + // Valid values are 0-2 inclusive. + if (!ClientUtils.IsEnumValid(check, (int)check, (int)CheckState.Unchecked, (int)CheckState.Indeterminate)){ + throw new InvalidEnumArgumentException("value", (int)check, typeof(CheckState)); + } + + int index = base.Add(item); + owner.SetItemCheckState(index, check); + + return index; + } + } + + /// + /// + /// [To be supplied.] + /// + public class CheckedIndexCollection : IList { + private CheckedListBox owner; + + internal CheckedIndexCollection(CheckedListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current checked items. + /// + public int Count { + get { + return owner.CheckedItems.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// Retrieves the specified checked item. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int this[int index] { + get { + object identifier = InnerArray.GetEntryObject(index, CheckedItemCollection.AnyMask); + return InnerArray.IndexOfIdentifier(identifier, 0); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedIndexCollectionIsReadOnly)); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int index) { + return (IndexOf(index) != -1); + } + + /// + /// + bool IList.Contains(object index) { + if (index is Int32) { + return Contains((int)index); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + int cnt = owner.CheckedItems.Count; + for (int i = 0; i < cnt; i++) { + dest.SetValue(this[i], i + index); + } + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + return ((ObjectCollection)owner.Items).InnerArray; + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + int[] indices = new int[this.Count]; + CopyTo(indices, 0); + return indices.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int index) { + if (index >= 0 && index < owner.Items.Count) { + object value = InnerArray.GetEntryObject(index, 0); + return owner.CheckedItems.IndexOfIdentifier(value); + } + return -1; + } + + /// + /// + int IList.IndexOf(object index) { + if (index is Int32) { + return IndexOf((int)index); + } + else { + return -1; + } + } + + } + + /// + /// + /// [To be supplied.] + /// + public class CheckedItemCollection : IList { + + internal static int CheckedItemMask = ItemArray.CreateMask(); + internal static int IndeterminateItemMask = ItemArray.CreateMask(); + internal static int AnyMask = CheckedItemMask | IndeterminateItemMask; + + private CheckedListBox owner; + + internal CheckedItemCollection(CheckedListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current checked items. + /// + public int Count { + get { + return InnerArray.GetCount(AnyMask); + } + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + return ((ListBox.ObjectCollection)owner.Items).InnerArray; + } + } + + /// + /// + /// Retrieves the specified checked item. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object this[int index] { + get { + return InnerArray.GetItem(index, AnyMask); + } + set { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object item) { + return IndexOf(item) != -1; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object item) { + return InnerArray.IndexOf(item, AnyMask); + } + + internal int IndexOfIdentifier(object item) { + return InnerArray.IndexOfIdentifier(item, AnyMask); + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.CheckedListBoxCheckedItemCollectionIsReadOnly)); + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + int cnt = InnerArray.GetCount(AnyMask); + for (int i = 0; i < cnt; i++) { + dest.SetValue(InnerArray.GetItem(i, AnyMask), i + index); + } + } + + /// + /// This method returns if the actual item index is checked. The index is the index to the MAIN + /// collection, not this one. + /// + internal CheckState GetCheckedState(int index) { + bool isChecked = InnerArray.GetState(index, CheckedItemMask); + bool isIndeterminate = InnerArray.GetState(index, IndeterminateItemMask); + Debug.Assert(!isChecked || !isIndeterminate, "Can't be both checked and indeterminate. Somebody broke our state."); + if (isIndeterminate) { + return CheckState.Indeterminate; + } + else if (isChecked) { + return CheckState.Checked; + } + + return CheckState.Unchecked; + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return InnerArray.GetEnumerator(AnyMask, true); + } + + /// + /// Same thing for GetChecked. + /// + internal void SetCheckedState(int index, CheckState value) { + bool isChecked; + bool isIndeterminate; + + switch(value) { + case CheckState.Checked: + isChecked = true; + isIndeterminate = false; + break; + + case CheckState.Indeterminate: + isChecked = false; + isIndeterminate = true; + break; + + default: + isChecked = false; + isIndeterminate = false; + break; + } + + bool wasChecked = InnerArray.GetState(index, CheckedItemMask); + bool wasIndeterminate = InnerArray.GetState(index, IndeterminateItemMask); + + InnerArray.SetState(index, CheckedItemMask, isChecked); + InnerArray.SetState(index, IndeterminateItemMask, isIndeterminate); + + if (wasChecked != isChecked || wasIndeterminate != isIndeterminate) { + // Raise a notify event that this item has changed. + owner.AccessibilityNotifyClients(AccessibleEvents.StateChange, index); + } + } + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class CheckedListBoxAccessibleObject : ControlAccessibleObject { + + /// + /// + /// + public CheckedListBoxAccessibleObject(CheckedListBox owner) : base(owner) { + } + + private CheckedListBox CheckedListBox { + get { + return (CheckedListBox)Owner; + } + } + + /// + /// + /// + public override AccessibleObject GetChild(int index) { + if (index >= 0 && index < CheckedListBox.Items.Count) { + return new CheckedListBoxItemAccessibleObject(this.CheckedListBox.GetItemText(CheckedListBox.Items[index]), index, this); + } + else { + return null; + } + } + + /// + /// + /// + public override int GetChildCount() { + return CheckedListBox.Items.Count; + } + + public override AccessibleObject GetFocused() { + int index = CheckedListBox.FocusedIndex; + if (index >= 0) { + return GetChild(index); + } + + return null; + } + + public override AccessibleObject GetSelected() { + int index = CheckedListBox.SelectedIndex; + if (index >= 0) { + return GetChild(index); + } + + return null; + } + + public override AccessibleObject HitTest(int x, int y) { + + // Within a child element? + // + int count = GetChildCount(); + for(int index=0; index < count; ++index) { + AccessibleObject child = GetChild(index); + if (child.Bounds.Contains(x, y)) { + return child; + } + } + + // Within the CheckedListBox bounds? + // + if (this.Bounds.Contains(x, y)) { + return this; + } + + return null; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation direction) { + if (GetChildCount() > 0) { + if (direction == AccessibleNavigation.FirstChild) { + return GetChild(0); + } + if (direction == AccessibleNavigation.LastChild) { + return GetChild(GetChildCount() - 1); + } + } + return base.Navigate(direction); + } + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class CheckedListBoxItemAccessibleObject : AccessibleObject { + + private string name; + private int index; + private CheckedListBoxAccessibleObject parent; + + public CheckedListBoxItemAccessibleObject(string name, int index, CheckedListBoxAccessibleObject parent) : base() { + this.name = name; + this.parent = parent; + this.index = index; + } + + public override Rectangle Bounds { + get { + Rectangle rect = ParentCheckedListBox.GetItemRectangle(index); + + // Translate rect to screen coordinates + // + NativeMethods.POINT pt = new NativeMethods.POINT(rect.X, rect.Y); + UnsafeNativeMethods.ClientToScreen(new HandleRef(ParentCheckedListBox, ParentCheckedListBox.Handle), pt); + + return new Rectangle(pt.x, pt.y, rect.Width, rect.Height); + } + } + + public override string DefaultAction { + get { + if (ParentCheckedListBox.GetItemChecked(index)) { + return SR.GetString(SR.AccessibleActionUncheck); + } + else { + return SR.GetString(SR.AccessibleActionCheck); + } + } + } + + private CheckedListBox ParentCheckedListBox { + get { + return(CheckedListBox)parent.Owner; + } + } + + public override string Name { + get { + return name; + } + set { + name = value; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return parent; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.CheckButton; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable; + + // Checked state + // + switch (ParentCheckedListBox.GetItemCheckState(index)) { + case CheckState.Checked: + state |= AccessibleStates.Checked; + break; + case CheckState.Indeterminate: + state |= AccessibleStates.Indeterminate; + break; + case CheckState.Unchecked: + // No accessible state corresponding to unchecked + break; + } + + // Selected state + // + if (ParentCheckedListBox.SelectedIndex == index) { + state |= AccessibleStates.Selected | AccessibleStates.Focused; + } + + if (AccessibilityImprovements.Level3 && ParentCheckedListBox.Focused && ParentCheckedListBox.SelectedIndex == -1) { + state |= AccessibleStates.Focused; + } + + return state; + + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return ParentCheckedListBox.GetItemChecked(index).ToString(); + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ParentCheckedListBox.SetItemChecked(index, !ParentCheckedListBox.GetItemChecked(index)); + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation direction) { + // Down/Next + // + if (direction == AccessibleNavigation.Down || + direction == AccessibleNavigation.Next) { + if (index < parent.GetChildCount() - 1) { + return parent.GetChild(index + 1); + } + } + + // Up/Previous + // + if (direction == AccessibleNavigation.Up || + direction == AccessibleNavigation.Previous) { + if (index > 0) { + return parent.GetChild(index - 1); + } + } + + return base.Navigate(direction); + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + try { + ParentCheckedListBox.AccessibilityObject.GetSystemIAccessibleInternal().accSelect((int) flags, index + 1); + } catch (ArgumentException) { + // In Everett, the CheckedListBox accessible children did not have any selection capability. + // In Whidbey, they delegate the selection capability to OLEACC. + // However, OLEACC does not deal w/ several Selection flags: ExtendSelection, AddSelection, RemoveSelection. + // OLEACC instead throws an ArgumentException. + // Since Whidbey API's should not throw an exception in places where Everett API's did not, we catch + // the ArgumentException and fail silently. + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Clipboard.cs b/WindowsForms/Managed/System/WinForms/Clipboard.cs new file mode 100644 index 000000000..73ccbfbd0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Clipboard.cs @@ -0,0 +1,609 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.IO; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Security; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using System.Globalization; + using System.Security.Permissions; + using System.Collections; + + /// + /// + /// Provides methods to place data on and retrieve data from the system clipboard. This class cannot be inherited. + /// + public sealed class Clipboard { + + // not creatable... + // + private Clipboard() { + } + + // + // Checks the validity of format while setting data into ClipBoard + // + private static bool IsFormatValid(DataObject data) { + return IsFormatValid(data.GetFormats()); + } + + internal static bool IsFormatValid(string[] formats) { + + Debug.Assert(formats != null, "Null returned from GetFormats"); + if (formats != null) { + if (formats.Length <= 4) { + for (int i = 0; i < formats.Length; i++) { + switch (formats[i]) { + case "Text": + case "UnicodeText": + case "System.String": + case "Csv": + break; + default: + return false; + + } + } + return true; + } + } + return false; + } + + internal static bool IsFormatValid(FORMATETC[] formats) { + + Debug.Assert(formats != null, "Null returned from GetFormats"); + if (formats != null) { + if (formats.Length <= 4) { + for (int i = 0; i < formats.Length; i++) { + short format = formats[i].cfFormat; + if (format != NativeMethods.CF_TEXT && + format != NativeMethods.CF_UNICODETEXT && + format != DataFormats.GetFormat("System.String").Id && + format != DataFormats.GetFormat("Csv").Id) { + return false; + } + } + return true; + } + } + return false; + } + + /// + /// + /// Places nonpersistent data on the system . + /// + public static void SetDataObject(object data) { + SetDataObject(data, false); + } + + /// + /// + /// Overload that uses default values for retryTimes and retryDelay. + /// + public static void SetDataObject(object data, bool copy) { + SetDataObject(data, copy, 10 /*retryTimes*/, 100 /*retryDelay*/); + } + + /// + /// + /// Places data on the system and uses copy to specify whether the data + /// should remain on the + /// after the application exits. + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + public static void SetDataObject(object data, bool copy, int retryTimes, int retryDelay) { + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new System.Threading.ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + + if (data == null) { + throw new ArgumentNullException("data"); + } + + if (retryTimes < 0) { + throw new ArgumentOutOfRangeException("retryTimes", SR.GetString(SR.InvalidLowBoundArgumentEx, "retryTimes", retryTimes.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (retryDelay < 0) { + throw new ArgumentOutOfRangeException("retryDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "retryDelay", retryDelay.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + + DataObject dataObject = null; + if (!(data is IComDataObject)) { + dataObject = new DataObject(data); + } + + bool restrictedFormats = false; + + // If the caller doesnt have AllClipBoard permission then we allow only restricted formats. + // These formats are TEXT, UNICODETEXT,String and CSV + try + { + IntSecurity.ClipboardRead.Demand(); + } + catch (SecurityException) + { + // We dont have allClipBoard so we can set only data in the following formats + // TEXT, UNICODETEXT, and CSV. + restrictedFormats = true; + } + + // Compute the format of the "data" passed in iff setText == true; + + if (restrictedFormats) + { + if (dataObject == null) + { + dataObject = data as DataObject; + } + if (!IsFormatValid(dataObject)) + { + throw new SecurityException(SR.GetString(SR.ClipboardSecurityException)); + } + } + + if (dataObject != null) { + dataObject.RestrictedFormats = restrictedFormats; + } + int hr, retry = retryTimes; + + IntSecurity.UnmanagedCode.Assert(); + + try + { + do { + if (data is IComDataObject) { + hr = UnsafeNativeMethods.OleSetClipboard((IComDataObject)data); + } + else { + hr = UnsafeNativeMethods.OleSetClipboard(dataObject); + } + if (hr != 0) { + if (retry == 0) { + ThrowIfFailed(hr); + } + retry--; + System.Threading.Thread.Sleep(retryDelay /*ms*/); + } + } + while (hr != 0); + + if (copy) { + retry = retryTimes; + do { + hr = UnsafeNativeMethods.OleFlushClipboard(); + if (hr != 0) { + if (retry == 0) { + ThrowIfFailed(hr); + } + retry--; + System.Threading.Thread.Sleep(retryDelay /*ms*/); + } + } + while (hr != 0); + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// Retrieves the data that is currently on the system + /// . + /// + public static IDataObject GetDataObject() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); + IntSecurity.ClipboardRead.Demand(); + + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + + // only throw if a message loop was started. This makes the case of trying + // to query the clipboard from your finalizer or non-ui MTA thread + // silently fail, instead of making your app die. + // + // however, if you are trying to write a normal windows forms app and + // forget to set the STAThread attribute, we will correctly report + // an error to aid in debugging. + // + if (Application.MessageLoop) { + throw new System.Threading.ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + else { + return null; + } + } + // Vswhidbey :: 476911. We need to retry the GetDataObject() since the clipBaord is busy sometimes and hence the GetDataObject would fail with ClipBoardException. + return GetDataObject(10 /*retryTimes*/, 100 /*retryDelay*/); + } + + // Private method to help accessing clipBoard for know retries before failing. + private static IDataObject GetDataObject(int retryTimes, int retryDelay) + { + IComDataObject dataObject = null; + int hr, retry = retryTimes; + do { + hr = UnsafeNativeMethods.OleGetClipboard(ref dataObject); + if (hr != 0) { + if (retry == 0) { + ThrowIfFailed(hr); + } + retry--; + System.Threading.Thread.Sleep(retryDelay /*ms*/); + } + } + while (hr != 0); + + if (dataObject != null) { + if (dataObject is IDataObject && !Marshal.IsComObject(dataObject)) { + return (IDataObject)dataObject; + } + else { + return new DataObject(dataObject); + } + } + return null; + } + + // <-- WHIDBEY ADDITIONS + + /// + /// + /// [To be supplied.] + /// + public static void Clear() { + Clipboard.SetDataObject(new DataObject()); + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsAudio() { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetDataPresent(DataFormats.WaveAudio, false); + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsData(string format) { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetDataPresent(format, false); + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsFileDropList() { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetDataPresent(DataFormats.FileDrop, true); + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsImage() { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetDataPresent(DataFormats.Bitmap, true); + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsText() { + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) + { + return ContainsText(TextDataFormat.Text); + } + else { + return ContainsText(TextDataFormat.UnicodeText); + } + } + + /// + /// + /// [To be supplied.] + /// + public static bool ContainsText(TextDataFormat format) { + // valid values are 0x0-0x4 inclusive + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)){ + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetDataPresent(ConvertToDataFormats(format), false); + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + public static Stream GetAudioStream() { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetData(DataFormats.WaveAudio, false) as Stream; + } + + return null; + } + + /// + /// + /// [To be supplied.] + /// + public static object GetData(string format) { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetData(format); + } + + return null; + } + + /// + /// + /// [To be supplied.] + /// + public static StringCollection GetFileDropList() { + IDataObject dataObject = Clipboard.GetDataObject(); + StringCollection retVal = new StringCollection(); + + if (dataObject != null) { + string[] strings = dataObject.GetData(DataFormats.FileDrop, true) as string[]; + if (strings != null) { + retVal.AddRange(strings); + } + } + + return retVal; + } + + /// + /// + /// [To be supplied.] + /// + public static Image GetImage() { + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + return dataObject.GetData(DataFormats.Bitmap, true) as Image; + } + + return null; + } + + /// + /// + /// [To be supplied.] + /// + public static string GetText() { + // Pass in Text format for Win98... + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) + { + return GetText(TextDataFormat.Text); + } + else { + return GetText(TextDataFormat.UnicodeText); + } + } + + /// + /// + /// [To be supplied.] + /// + public static string GetText(TextDataFormat format) { + // valid values are 0x0 to 0x4 inclusive + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)) + { + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) { + string text = dataObject.GetData(ConvertToDataFormats(format), false) as string; + if (text != null) { + return text; + } + } + + return String.Empty; + } + + /// + /// + /// [To be supplied.] + /// + public static void SetAudio(byte[] audioBytes) { + if (audioBytes == null) { + throw new ArgumentNullException("audioBytes"); + } + SetAudio(new MemoryStream(audioBytes)); + } + + /// + /// + /// [To be supplied.] + /// + public static void SetAudio(Stream audioStream) { + if (audioStream == null) { + throw new ArgumentNullException("audioStream"); + } + IDataObject dataObject = new DataObject(); + dataObject.SetData(DataFormats.WaveAudio, false, audioStream); + Clipboard.SetDataObject(dataObject, true); + } + + /// + /// + /// [To be supplied.] + /// + public static void SetData(string format, object data) { + //Note: We delegate argument checking to IDataObject.SetData, if it wants to do so. + IDataObject dataObject = new DataObject(); + dataObject.SetData(format, data); + Clipboard.SetDataObject(dataObject, true); + } + + /// + /// + /// [To be supplied.] + /// + public static void SetFileDropList(StringCollection filePaths) { + if (filePaths == null) { + throw new ArgumentNullException("filePaths"); + } + // VsWhidbey:432618 throw Argument exception for zero-length filepath collection. + if (filePaths.Count == 0) + { + throw new ArgumentException(SR.GetString(SR.CollectionEmptyException)); + } + + //VSWhidbey #163538 - Validate the paths to make sure they don't contain invalid characters + foreach (string path in filePaths) { + try { + string temp = Path.GetFullPath(path); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + throw new ArgumentException(SR.GetString(SR.Clipboard_InvalidPath, path, "filePaths"), e); + } + } + + if (filePaths.Count > 0) { + IDataObject dataObject = new DataObject(); + string[] strings = new string[filePaths.Count]; + filePaths.CopyTo(strings, 0); + dataObject.SetData(DataFormats.FileDrop, true, strings); + Clipboard.SetDataObject(dataObject, true); + } + } + + /// + /// + /// [To be supplied.] + /// + public static void SetImage(Image image) { + if (image == null) { + throw new ArgumentNullException("image"); + } + IDataObject dataObject = new DataObject(); + dataObject.SetData(DataFormats.Bitmap, true, image); + Clipboard.SetDataObject(dataObject, true); + } + + /// + /// + /// [To be supplied.] + /// + public static void SetText(string text) { + // Pass in Text format for Win98... + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) + { + SetText(text, TextDataFormat.Text); + } + else { + SetText(text, TextDataFormat.UnicodeText); + } + } + + /// + /// + /// [To be supplied.] + /// + public static void SetText(string text, TextDataFormat format) { + if (String.IsNullOrEmpty(text)) { + throw new ArgumentNullException("text"); + } + + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)) + { + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + IDataObject dataObject = new DataObject(); + dataObject.SetData(ConvertToDataFormats(format), false, text); + Clipboard.SetDataObject(dataObject, true); + } + + private static string ConvertToDataFormats(TextDataFormat format) { + switch (format) { + case TextDataFormat.Text: + return DataFormats.Text; + + case TextDataFormat.UnicodeText: + return DataFormats.UnicodeText; + + case TextDataFormat.Rtf: + return DataFormats.Rtf; + + case TextDataFormat.Html: + return DataFormats.Html; + + case TextDataFormat.CommaSeparatedValue: + return DataFormats.CommaSeparatedValue; + } + + return DataFormats.UnicodeText; + } + + // END - WHIDBEY ADDITIONS --> + + private static void ThrowIfFailed(int hr) { + // + if (hr != 0) { + ExternalException e = new ExternalException(SR.GetString(SR.ClipboardOperationFailed), hr); + throw e; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CloseReason.cs b/WindowsForms/Managed/System/WinForms/CloseReason.cs new file mode 100644 index 000000000..c44491f19 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CloseReason.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Specifies the reason for the Form Closing. + /// + /// + public enum CloseReason { + + /// + /// + /// + /// No reason for closure of the Form. + /// + /// + None = 0, + + /// + /// + /// + /// In the process of shutting down, Windows has closed the application. + /// + /// + // Verb: Shut down, not noun Shutdown. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + WindowsShutDown = 1, + + /// + /// + /// + /// The parent form of this MDI form is closing. + /// + /// + MdiFormClosing = 2, + + /// + /// + /// + /// The user has clicked the close button on the form window, selected Close from the window's control menu or + /// hit Alt + F4. + /// + /// + UserClosing = 3, + + /// + /// + /// + /// The Microsoft Windows Task Manager is closing the application. + /// + /// + TaskManagerClosing = 4, + + /// + /// + /// + /// A form is closing because its owner is closing. + /// + /// + FormOwnerClosing = 5, + + /// + /// + /// + /// A form is closing because Application.Exit() was called. + /// + /// + ApplicationExitCall = 6 + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ColorDepth.cs b/WindowsForms/Managed/System/WinForms/ColorDepth.cs new file mode 100644 index 000000000..299bd9392 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColorDepth.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + // Used with ImageList. + /// + /// + /// [To be supplied.] + /// + public enum ColorDepth { + /// + /// + /// [To be supplied.] + /// + Depth4Bit = 4, + /// + /// + /// [To be supplied.] + /// + Depth8Bit = 8, + /// + /// + /// [To be supplied.] + /// + Depth16Bit = 16, + /// + /// + /// [To be supplied.] + /// + Depth24Bit = 24, + /// + /// + /// [To be supplied.] + /// + Depth32Bit = 32, + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColorDialog.cs b/WindowsForms/Managed/System/WinForms/ColorDialog.cs new file mode 100644 index 000000000..63c783d5d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColorDialog.cs @@ -0,0 +1,344 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Drawing; + + using System.ComponentModel; + using System.Windows.Forms; + + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// + /// Represents a common dialog box that displays available colors along with + /// controls that allow the user to define custom colors. + /// + /// + [DefaultProperty("Color")] + [SRDescription(SR.DescriptionColorDialog)] + // The only event this dialog has is HelpRequest, which isn't very useful + public class ColorDialog : CommonDialog { + + private int options; + private int[] customColors; + + /// + /// + /// + /// + private Color color; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not call Reset + // it would be a breaking change. + ] + public ColorDialog() { + customColors = new int[16]; + Reset(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the user can use the dialog box + /// to define custom colors. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.CDallowFullOpenDescr) + ] + public virtual bool AllowFullOpen { + get { + return !GetOption(NativeMethods.CC_PREVENTFULLOPEN); + } + + set { + SetOption(NativeMethods.CC_PREVENTFULLOPEN, !value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box displays all available colors in + /// the set of basic colors. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.CDanyColorDescr) + ] + public virtual bool AnyColor { + get { + return GetOption(NativeMethods.CC_ANYCOLOR); + } + + set { + SetOption(NativeMethods.CC_ANYCOLOR, value); + } + } + + /// + /// + /// + /// Gets or sets the color selected by the user. + /// + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.CDcolorDescr) + ] + public Color Color { + get { + return color; + } + set { + if (!value.IsEmpty) { + color = value; + } + else { + color = Color.Black; + } + } + } + + /// + /// + /// + /// Gets or sets the set of + /// custom colors shown in the dialog box. + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.CDcustomColorsDescr) + ] + public int[] CustomColors { + get { return(int[]) customColors.Clone();} + set { + int length = value == null? 0: Math.Min(value.Length, 16); + if (length > 0) Array.Copy(value, 0, customColors, 0, length); + for (int i = length; i < 16; i++) customColors[i] = 0x00FFFFFF; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the controls used to create custom + /// colors are visible when the dialog box is opened + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.CDfullOpenDescr) + ] + public virtual bool FullOpen { + get { + return GetOption(NativeMethods.CC_FULLOPEN); + } + + set { + SetOption(NativeMethods.CC_FULLOPEN, value); + } + } + + /// + /// + /// + /// + /// Our HINSTANCE from Windows. + /// + /// + protected virtual IntPtr Instance { + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { return UnsafeNativeMethods.GetModuleHandle(null);} + } + + /// + /// + /// Returns our CHOOSECOLOR options. + /// + /// + protected virtual int Options { + get { + return options; + } + } + + + /// + /// + /// + /// Gets or sets a value indicating whether a Help button appears + /// in the color dialog box. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.CDshowHelpDescr) + ] + public virtual bool ShowHelp { + get { + return GetOption(NativeMethods.CC_SHOWHELP); + } + set { + SetOption(NativeMethods.CC_SHOWHELP, value); + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating + /// whether the dialog + /// box will restrict users to selecting solid colors only. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.CDsolidColorOnlyDescr) + ] + public virtual bool SolidColorOnly { + get { + return GetOption(NativeMethods.CC_SOLIDCOLOR); + } + set { + SetOption(NativeMethods.CC_SOLIDCOLOR, value); + } + } + + /// + /// + /// Lets us control the CHOOSECOLOR options. + /// + /// + private bool GetOption(int option) { + return(options & option) != 0; + } + + /// + /// + /// + /// Resets + /// all options to their + /// default values, the last selected color to black, and the custom + /// colors to their default values. + /// + /// + public override void Reset() { + options = 0; + color = Color.Black; + CustomColors = null; + } + + private void ResetColor() { + Color = Color.Black; + } + + /// + /// + /// + /// + protected override bool RunDialog(IntPtr hwndOwner) { + + NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); + NativeMethods.CHOOSECOLOR cc = new NativeMethods.CHOOSECOLOR(); + IntPtr custColorPtr = Marshal.AllocCoTaskMem(64); + try { + Marshal.Copy(customColors, 0, custColorPtr, 16); + cc.hwndOwner = hwndOwner; + cc.hInstance = Instance; + cc.rgbResult = ColorTranslator.ToWin32(color); + cc.lpCustColors = custColorPtr; + + int flags = Options | (NativeMethods.CC_RGBINIT | NativeMethods.CC_ENABLEHOOK); + // Our docs say AllowFullOpen takes precedence over FullOpen; ChooseColor implements the opposite + if (!AllowFullOpen) + flags &= ~NativeMethods.CC_FULLOPEN; + cc.Flags = flags; + + cc.lpfnHook = hookProcPtr; + if (!SafeNativeMethods.ChooseColor(cc)) return false; + if (cc.rgbResult != ColorTranslator.ToWin32(color)) color = ColorTranslator.FromOle(cc.rgbResult); + Marshal.Copy(custColorPtr, customColors, 0, 16); + return true; + } + finally { + Marshal.FreeCoTaskMem(custColorPtr); + } + } + + /// + /// + /// Allows us to manipulate the CHOOSECOLOR options + /// + /// + private void SetOption(int option, bool value) { + if (value) { + options |= option; + } + else { + options &= ~option; + } + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + private bool ShouldSerializeColor() { + return !Color.Equals(Color.Black); + } + + /// + /// + /// + /// + /// Provides a string version of this object. + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", Color: " + Color.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnClickEvent.cs b/WindowsForms/Managed/System/WinForms/ColumnClickEvent.cs new file mode 100644 index 000000000..9a639238c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnClickEvent.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + /// + public class ColumnClickEventArgs : EventArgs { + readonly int column; + + /// + /// + /// [To be supplied.] + /// + public ColumnClickEventArgs(int column) { + this.column = column; + } + + /// + /// + /// [To be supplied.] + /// + public int Column { + get { + return column; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnClickEventHandler.cs b/WindowsForms/Managed/System/WinForms/ColumnClickEventHandler.cs new file mode 100644 index 000000000..697106c83 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnClickEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// the event of a + /// . + /// + /// + /// + public delegate void ColumnClickEventHandler(object sender, ColumnClickEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnHeader.cs b/WindowsForms/Managed/System/WinForms/ColumnHeader.cs new file mode 100644 index 000000000..a5201f2c4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnHeader.cs @@ -0,0 +1,522 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Drawing.Design; + using System.Diagnostics; + using System.Drawing; + using System.IO; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Windows.Forms; + using System.Globalization; + + /// + /// + /// + /// Displays a single column header in a + /// control. + /// + /// + /// + [ + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Text"), + TypeConverterAttribute(typeof(ColumnHeaderConverter)) + ] + public class ColumnHeader : Component, ICloneable { + +// disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal int index = -1; +#pragma warning restore 0414 + internal string text = null; + internal string name = null; + internal int width = 60; + // Use TextAlign property instead of this member variable, always + private HorizontalAlignment textAlign = HorizontalAlignment.Left; + private bool textAlignInitialized = false; + private int displayIndexInternal = -1; + private ColumnHeaderImageListIndexer imageIndexer = null; + + object userData; + + private ListView listview; + // We need to send some messages to ListView when it gets initialized. + internal ListView OwnerListview + { + get + { + return listview; + } + set + { + int width = this.Width; + + listview = value; + + // The below properties are set into the listview. + this.Width = width; + } + } + + /// + /// + /// Creates a new ColumnHeader object + /// + public ColumnHeader() { + imageIndexer = new ColumnHeaderImageListIndexer(this); + } + + /// + /// + /// Creates a new ColumnHeader object + /// + public ColumnHeader(int imageIndex) : this () { + this.ImageIndex = imageIndex; + } + + /// + /// + /// Creates a new ColumnHeader object + /// + public ColumnHeader(string imageKey) : this () { + this.ImageKey = imageKey; + } + + internal int ActualImageIndex_Internal { + get { + int imgIndex = this.imageIndexer.ActualIndex; + if (this.ImageList == null || this.ImageList.Images == null || imgIndex >= this.ImageList.Images.Count) { + // the ImageIndex equivalent of a ImageKey that does not exist in the ImageList + return -1; + } else { + return imgIndex; + } + } + } + + + /// + [ + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatBehavior), + SRDescription(SR.ColumnHeaderDisplayIndexDescr) + ] + public int DisplayIndex { + get { + return this.DisplayIndexInternal; + } + + set { + + // When the list is being deserialized we need + // to take the display index as is. ListView + // does correctly synchronize the indices. + if (this.listview == null) { + this.DisplayIndexInternal = value; + return; + } + + if (value < 0 || value> (this.listview.Columns.Count - 1)) { + throw new ArgumentOutOfRangeException("DisplayIndex", SR.GetString(SR.ColumnHeaderBadDisplayIndex)); + } + + int lowDI = Math.Min(this.DisplayIndexInternal, value); + int hiDI = Math.Max(this.DisplayIndexInternal, value); + int[] colsOrder = new int[this.listview.Columns.Count]; + + // set the display indices. This is not an expensive operation because + // we only set an integer in the column header class + bool hdrMovedForward = value > this.DisplayIndexInternal; + ColumnHeader movedHdr = null; + for (int i = 0; i < this.listview.Columns.Count; i ++) { + + ColumnHeader hdr = this.listview.Columns[i]; + if (hdr.DisplayIndex == this.DisplayIndexInternal) { + movedHdr = hdr; + } else if (hdr.DisplayIndex >= lowDI && hdr.DisplayIndex <= hiDI) { + hdr.DisplayIndexInternal -= hdrMovedForward ? 1 : -1; + } + if (i != this.Index) { + colsOrder[ hdr.DisplayIndexInternal ] = i; + } + } + + movedHdr.DisplayIndexInternal = value; + colsOrder[ movedHdr.DisplayIndexInternal ] = movedHdr.Index; + SetDisplayIndices( colsOrder ); + } + } + + internal int DisplayIndexInternal { + get { + return this.displayIndexInternal; + } + set { + this.displayIndexInternal = value; + } + } + + /// + /// + /// The index of this column. This index does not necessarily correspond + /// to the current visual position of the column in the ListView, because the + /// user may orerder columns if the allowColumnReorder property is true. + /// + [ Browsable(false)] + public int Index { + get { + if (listview != null) + return listview.GetColumnIndex(this); + return -1; + } + } + + /// + [ + DefaultValue(-1), + TypeConverterAttribute(typeof(ImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int ImageIndex { + get { + if (imageIndexer.Index != -1 && ImageList != null && imageIndexer.Index >= ImageList.Images.Count) { + return ImageList.Images.Count - 1; + } + return imageIndexer.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", (value).ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + if (imageIndexer.Index != value) { + imageIndexer.Index = value; + + if (ListView != null && ListView.IsHandleCreated) { + ListView.SetColumnInfo(NativeMethods.LVCF_IMAGE, this); + } + } + } + } + + /// + [Browsable(false)] + public ImageList ImageList { + // we added the ImageList property so that the ImageIndexConverter can find our image list + get { + return this.imageIndexer.ImageList; + } + } + + /// + [ + DefaultValue(""), + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public string ImageKey { + get { + return imageIndexer.Key; + } + set { + if (value != imageIndexer.Key) { + imageIndexer.Key = value; + + if (ListView != null && ListView.IsHandleCreated) { + ListView.SetColumnInfo(NativeMethods.LVCF_IMAGE, this); + } + } + } + } + + /// + /// + /// Returns the ListView control that this column is displayed in. May be null + /// + [ Browsable(false) ] + public ListView ListView { + get { + return this.listview; + } + } + + /// + /// + /// The Name of the column header + /// + [ + Browsable(false), + SRDescription(SR.ColumnHeaderNameDescr) + ] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this,name); + } + set { + if (value == null) { + this.name = ""; + } + else { + this.name = value; + } + if(Site != null) { + Site.Name = value; + } + } + + } + + /// + /// + /// The text displayed in the column header + /// + [ + Localizable(true), + SRDescription(SR.ColumnCaption) + ] + public string Text { + get { + return(text != null ? text : "ColumnHeader"); + } + set { + if (value == null) { + this.text = ""; + } + else { + this.text = value; + } + if (listview != null) { + listview.SetColumnInfo(NativeMethods.LVCF_TEXT, this); + } + } + + } + + /// + /// + /// The horizontal alignment of the text contained in this column + /// + [ + SRDescription(SR.ColumnAlignment), + Localizable(true), + DefaultValue(HorizontalAlignment.Left) + ] + public HorizontalAlignment TextAlign { + get { + if (!textAlignInitialized && (listview != null)) + { + textAlignInitialized = true; + // See below for an explanation of (Index != 0) + //Added !IsMirrored for VSWhidbey # 365437 + if ((Index != 0) && (listview.RightToLeft == RightToLeft.Yes) && !listview.IsMirrored) + { + this.textAlign = HorizontalAlignment.Right; + } + } + return this.textAlign; + } + set { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + + this.textAlign = value; + + // The first column must be left-aligned + if (Index == 0 && this.textAlign != HorizontalAlignment.Left) { + this.textAlign = HorizontalAlignment.Left; + } + + if (listview != null) { + listview.SetColumnInfo(NativeMethods.LVCF_FMT, this); + listview.Invalidate(); + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + internal int WidthInternal { + get { + return width; + } + } + /// + /// + /// The width of the column in pixels. + /// + [ + SRDescription(SR.ColumnWidth), + Localizable(true), + DefaultValue(60) + ] + public int Width { + get { + // Since we can't keep our private width in sync with the real width because + // we don't get notified when the user changes it, we need to get this info + // from the underlying control every time we're asked. + // The underlying control will only report the correct width if it's in Report view + if (listview != null && listview.IsHandleCreated && !listview.Disposing && listview.View == View.Details) { + + // Make sure this column has already been added to the ListView, else just return width + // + IntPtr hwndHdr = UnsafeNativeMethods.SendMessage(new HandleRef(listview, listview.Handle), NativeMethods.LVM_GETHEADER, 0, 0); + if (hwndHdr != IntPtr.Zero) { + int nativeColumnCount = (int)UnsafeNativeMethods.SendMessage(new HandleRef(listview, hwndHdr), NativeMethods.HDM_GETITEMCOUNT, 0, 0); + if (Index < nativeColumnCount) { + width = (int)UnsafeNativeMethods.SendMessage(new HandleRef(listview, listview.Handle), NativeMethods.LVM_GETCOLUMNWIDTH, Index, 0); + } + } + } + + return width; + } + set { + this.width = value; + if (listview != null) + listview.SetColumnWidth(Index, ColumnHeaderAutoResizeStyle.None); + } + } + + /// + public void AutoResize(ColumnHeaderAutoResizeStyle headerAutoResize) { + + if (headerAutoResize < ColumnHeaderAutoResizeStyle.None || headerAutoResize > ColumnHeaderAutoResizeStyle.ColumnContent) { + throw new InvalidEnumArgumentException("headerAutoResize", (int)headerAutoResize, typeof(ColumnHeaderAutoResizeStyle)); + } + + if (this.listview != null) { + this.listview.AutoResizeColumn(this.Index, headerAutoResize); + } + } + + + /// + /// + /// Creates an identical ColumnHeader, unattached to any ListView + /// + public object Clone() { + Type clonedType = this.GetType(); + ColumnHeader columnHeader = null; + + if (clonedType == typeof(ColumnHeader)) { + columnHeader = new ColumnHeader(); + } + else { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + columnHeader = (ColumnHeader)Activator.CreateInstance(clonedType); + } + + columnHeader.text = text; + columnHeader.Width = width; + columnHeader.textAlign = TextAlign; + return columnHeader; + } + + /// + /// + /// [To be supplied.] + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (listview != null) { + int index = Index; + if (index != -1) { + listview.Columns.RemoveAt(index); + } + } + } + base.Dispose(disposing); + } + + private void ResetText() { + Text = null; + } + + // Set the display indices of the listview columns + private void SetDisplayIndices(int[] cols) { + + if (this.listview.IsHandleCreated && !this.listview.Disposing) { + UnsafeNativeMethods.SendMessage(new HandleRef(this.listview, this.listview.Handle), NativeMethods.LVM_SETCOLUMNORDERARRAY, cols.Length, cols); + } + } + + private bool ShouldSerializeName() { + return !string.IsNullOrEmpty(this.name); + } + + private bool ShouldSerializeDisplayIndex() { + return this.DisplayIndex != this.Index; + } + + /// + /// + /// [To be supplied.] + /// + internal bool ShouldSerializeText() { + return(text != null); + } + + /// + /// + /// Returns a string representation of this column header + /// + public override string ToString() { + return "ColumnHeader: Text: " + Text; + } + + internal class ColumnHeaderImageListIndexer : ImageList.Indexer { + private ColumnHeader owner = null; + public ColumnHeaderImageListIndexer(ColumnHeader ch) { + owner = ch; + } + + public override ImageList ImageList { + get { + if (owner != null && owner.ListView != null) { + return owner.ListView.SmallImageList; + } + return null; + } + set { + Debug.Assert(false, "We should never set the image list"); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnHeaderAutoResizeStyle.cs b/WindowsForms/Managed/System/WinForms/ColumnHeaderAutoResizeStyle.cs new file mode 100644 index 000000000..01e89ee42 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnHeaderAutoResizeStyle.cs @@ -0,0 +1,28 @@ +using System; + +namespace System.Windows.Forms { + /// + /// + /// + /// Specifies how the column headers are autoresized in + /// a control. + /// + /// + public enum ColumnHeaderAutoResizeStyle { + /// + /// + /// Do not auto resize the column headers. + /// + None, + /// + /// + /// Autoresize the column headers based on the width of just the column header. + /// + HeaderSize, + /// + /// + /// Autoresize the column headers based on the width of the largest subitem in the column. + /// + ColumnContent, + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnHeaderConverter.cs b/WindowsForms/Managed/System/WinForms/ColumnHeaderConverter.cs new file mode 100644 index 000000000..ff386023f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnHeaderConverter.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// + public class ColumnHeaderConverter : ExpandableObjectConverter { + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is ColumnHeader) { + ColumnHeader col = (ColumnHeader) value; + ConstructorInfo ctor; + + Type t = TypeDescriptor.GetReflectionType(value); + InstanceDescriptor id = null; + + if (col.ImageIndex != -1) { + ctor = t.GetConstructor(new Type[]{typeof(int)}); + if (ctor != null) { + id = new InstanceDescriptor(ctor, new object[]{col.ImageIndex}, false); + } + + } + + if (id == null && !String.IsNullOrEmpty(col.ImageKey)) { + ctor = t.GetConstructor(new Type[]{typeof(string)}); + if (ctor != null) { + id = new InstanceDescriptor(ctor, new object[]{col.ImageKey}, false); + } + } + + if (id == null) { + ctor = t.GetConstructor(new Type[0]); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[0], false); + } + else { + throw new ArgumentException(SR.GetString(SR.NoDefaultConstructor, t.FullName)); + } + } + return id; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ColumnHeaderStyle.cs b/WindowsForms/Managed/System/WinForms/ColumnHeaderStyle.cs new file mode 100644 index 000000000..fa6726fb5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnHeaderStyle.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies how column headers behave. + /// + /// + public enum ColumnHeaderStyle { + + /// + /// + /// + /// No visible column header. + /// + /// + None = 0, + /// + /// + /// + /// Visible column header that does not respond to clicking. + /// + /// + Nonclickable = 1, + /// + /// + /// + /// Visible column header that responds to clicking. + /// + /// + Clickable = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnReorderedEventArgs.cs b/WindowsForms/Managed/System/WinForms/ColumnReorderedEventArgs.cs new file mode 100644 index 000000000..80aaac402 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnReorderedEventArgs.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel; + +namespace System.Windows.Forms { + /// + public class ColumnReorderedEventArgs : CancelEventArgs { + private int oldDisplayIndex; + private int newDisplayIndex; + private ColumnHeader header; + + /// + public ColumnReorderedEventArgs(int oldDisplayIndex, int newDisplayIndex, ColumnHeader header) : base() { + this.oldDisplayIndex = oldDisplayIndex; + this.newDisplayIndex = newDisplayIndex; + this.header = header; + } + + /// + public int OldDisplayIndex { + get { + return oldDisplayIndex; + } + } + + /// + public int NewDisplayIndex { + get { + return newDisplayIndex; + } + } + + /// + public ColumnHeader Header { + get { + return header; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnReorderedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ColumnReorderedEventHandler.cs new file mode 100644 index 000000000..fc1643b29 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnReorderedEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// + /// + public delegate void ColumnReorderedEventHandler(object sender, ColumnReorderedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEvent.cs b/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEvent.cs new file mode 100644 index 000000000..7cd9a621e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEvent.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + public class ColumnWidthChangedEventArgs : EventArgs { + readonly int columnIndex; + + /// + /// + /// [To be supplied.] + /// + public ColumnWidthChangedEventArgs(int columnIndex) { + this.columnIndex = columnIndex; + } + + /// + /// + /// [To be supplied.] + /// + public int ColumnIndex { + get { + return columnIndex; + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEventHandler.cs new file mode 100644 index 000000000..f47d36fde --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnWidthChangedEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void ColumnWidthChangedEventHandler(object sender, ColumnWidthChangedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEvent.cs b/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEvent.cs new file mode 100644 index 000000000..469cf9548 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEvent.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { +using System; +using System.ComponentModel; + + /// + public class ColumnWidthChangingEventArgs : CancelEventArgs { + int columnIndex; + int newWidth; + + /// + /// + /// Creates a new ColumnWidthChanging event + /// + public ColumnWidthChangingEventArgs(int columnIndex, int newWidth, bool cancel) : base (cancel) { + this.columnIndex = columnIndex; + this.newWidth = newWidth; + } + + /// + /// + /// Creates a new ColumnWidthChanging event + /// + public ColumnWidthChangingEventArgs(int columnIndex, int newWidth) : base() { + this.columnIndex = columnIndex; + this.newWidth = newWidth; + } + + /// + /// + /// Returns the index of the column header whose width is changing + /// + public int ColumnIndex { + get { + return this.columnIndex; + } + } + + /// + /// + /// Returns the new width for the column header who is changing + /// + public int NewWidth { + get { + return this.newWidth; + } + set { + this.newWidth = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEventHandler.cs b/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEventHandler.cs new file mode 100644 index 000000000..87ea4bd54 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ColumnWidthChangingEventHandler.cs @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { +using System; + /// + public delegate void ColumnWidthChangingEventHandler(object sender, ColumnWidthChangingEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/ComboBox.cs b/WindowsForms/Managed/System/WinForms/ComboBox.cs new file mode 100644 index 000000000..9cc9c274a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComboBox.cs @@ -0,0 +1,6140 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Runtime.Versioning; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + using System.Windows.Forms.ComponentModel; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using Microsoft.Win32; + using System.Reflection; + using System.Collections.Specialized; + using System.Text; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Displays an editing field and a list, allowing the user to select from the + /// list or to enter new text. Displays only the editing field until the user + /// explicitly displays the list. + /// + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("SelectedIndexChanged"), + DefaultProperty("Items"), + DefaultBindingProperty("Text"), + Designer("System.Windows.Forms.Design.ComboBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionComboBox) + ] + public class ComboBox : ListControl { + + private static readonly object EVENT_DROPDOWN = new object(); + private static readonly object EVENT_DRAWITEM = new object(); + private static readonly object EVENT_MEASUREITEM = new object(); + private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); + private static readonly object EVENT_SELECTIONCHANGECOMMITTED = new object(); + private static readonly object EVENT_SELECTEDITEMCHANGED = new object(); + private static readonly object EVENT_DROPDOWNSTYLE = new object(); + private static readonly object EVENT_TEXTUPDATE = new object(); + private static readonly object EVENT_DROPDOWNCLOSED = new object(); + + private static readonly int PropMaxLength = PropertyStore.CreateKey(); + private static readonly int PropItemHeight = PropertyStore.CreateKey(); + private static readonly int PropDropDownWidth = PropertyStore.CreateKey(); + private static readonly int PropDropDownHeight = PropertyStore.CreateKey(); + private static readonly int PropStyle = PropertyStore.CreateKey(); + private static readonly int PropDrawMode = PropertyStore.CreateKey(); + private static readonly int PropMatchingText = PropertyStore.CreateKey(); + private static readonly int PropFlatComboAdapter = PropertyStore.CreateKey(); + + private const int DefaultSimpleStyleHeight = 150; + private const int DefaultDropDownHeight = 106; + private const int AutoCompleteTimeout = 10000000; // 1 second timeout for resetting the MatchingText + private bool autoCompleteDroppedDown = false; + + private FlatStyle flatStyle = FlatStyle.Standard; + private int updateCount; + + //Timestamp of the last keystroke. Used for auto-completion + // in DropDownList style. + private long autoCompleteTimeStamp; + + private int selectedIndex = -1; // used when we don't have a handle. + private bool allowCommit = true; + + // When the style is "simple", the requested height is used + // for the actual height of the control. + // When the style is non-simple, the height of the control + // is determined by the OS. + // This fixes bug #20966 + private int requestedHeight; + + private ComboBoxChildNativeWindow childDropDown; + private ComboBoxChildNativeWindow childEdit; + private ComboBoxChildNativeWindow childListBox; + + private IntPtr dropDownHandle; + private ObjectCollection itemsCollection; + private short prefHeightCache = -1; + private short maxDropDownItems = 8; + private bool integralHeight = true; + private bool mousePressed; + private bool mouseEvents; + private bool mouseInEdit; + + private bool sorted; + private bool fireSetFocus = true; + private bool fireLostFocus = true; + private bool mouseOver; + private bool suppressNextWindosPos; + private bool canFireLostFocus; + + // When the user types a letter and drops the dropdown... + // the combobox itself auto-searches the matching item... + // and selects the item in the edit... + // thus changing the windowText... + // hence we should Fire the TextChanged event in such a scenario.. + // The string below is used for checking the window Text before and after the dropdown. + private string currentText = ""; + private string lastTextChangedValue; + private bool dropDown; + private AutoCompleteDropDownFinder finder = new AutoCompleteDropDownFinder(); + + private bool selectedValueChangedFired; + + /// + /// + /// This stores the value for the autocomplete mode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// + private AutoCompleteMode autoCompleteMode = AutoCompleteMode.None; + + /// + /// + /// This stores the value for the autoCompleteSource mode which can be one of the values + /// from AutoCompleteSource enum. + /// + private AutoCompleteSource autoCompleteSource = AutoCompleteSource.None; + + /// + /// + /// This stores the custom StringCollection required for the autoCompleteSource when its set to CustomSource. + /// + private AutoCompleteStringCollection autoCompleteCustomSource; + private StringSource stringSource; + private bool fromHandleCreate = false; + + private ComboBoxChildListUiaProvider childListAccessibleObject; + private ComboBoxChildEditUiaProvider childEditAccessibleObject; + private ComboBoxChildTextUiaProvider childTextAccessibleObject; + + // Indicates whether the dropdown list will be closed after + // selection (on getting CBN_SELENDOK notification) to prevent + // focusing on the list item after hiding the list. + private bool dropDownWillBeClosed = false; + + /// + /// + /// Creates a new ComboBox control. The default style for the combo is + /// a regular DropDown Combo. + /// + public ComboBox() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.UseTextForAccessibility | + ControlStyles.StandardClick, false); + + requestedHeight = DefaultSimpleStyleHeight; + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + } + + /// + /// + /// This is the AutoCompleteMode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// This property in conjunction with AutoCompleteSource enables the AutoComplete feature for ComboBox. + /// + [ + DefaultValue(AutoCompleteMode.None), + SRDescription(SR.ComboBoxAutoCompleteModeDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteMode AutoCompleteMode { + get { + return autoCompleteMode; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoCompleteMode.None, (int)AutoCompleteMode.SuggestAppend)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteMode)); + } + if (this.DropDownStyle == ComboBoxStyle.DropDownList && + this.AutoCompleteSource != AutoCompleteSource.ListItems && + value != AutoCompleteMode.None) { + throw new NotSupportedException(SR.GetString(SR.ComboBoxAutoCompleteModeOnlyNoneAllowed)); + } + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + bool resetAutoComplete = false; + if (autoCompleteMode != AutoCompleteMode.None && value == AutoCompleteMode.None) { + resetAutoComplete = true; + } + autoCompleteMode = value; + SetAutoComplete(resetAutoComplete, true); + } + } + + /// + /// + /// This is the AutoCompleteSource which can be one of the + /// values from AutoCompleteSource enumeration. + /// This property in conjunction with AutoCompleteMode enables the AutoComplete feature for ComboBox. + /// + [ + DefaultValue(AutoCompleteSource.None), + SRDescription(SR.ComboBoxAutoCompleteSourceDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteSource AutoCompleteSource { + get { + return autoCompleteSource; + } + set { + // FxCop: Avoid usage of Enum.IsDefined - this looks like an enum that could grow + if (!ClientUtils.IsEnumValid_NotSequential(value, (int)value, + (int)AutoCompleteSource.None, + (int)AutoCompleteSource.AllSystemSources, + (int)AutoCompleteSource.AllUrl, + (int)AutoCompleteSource.CustomSource, + (int)AutoCompleteSource.FileSystem, + (int)AutoCompleteSource.FileSystemDirectories, + (int)AutoCompleteSource.HistoryList, + (int)AutoCompleteSource.ListItems, + (int)AutoCompleteSource.RecentlyUsedList)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteSource)); + } + + if (this.DropDownStyle == ComboBoxStyle.DropDownList && + this.AutoCompleteMode != AutoCompleteMode.None && + value != AutoCompleteSource.ListItems) { + throw new NotSupportedException(SR.GetString(SR.ComboBoxAutoCompleteSourceOnlyListItemsAllowed)); + } + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + + // VSWhidbey 466300 + if (value != AutoCompleteSource.None && + value != AutoCompleteSource.CustomSource && + value != AutoCompleteSource.ListItems) + { + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Demand(); + } + + autoCompleteSource = value; + SetAutoComplete(false, true); + } + } + + /// + /// + /// This is the AutoCompleteCustomSource which is custom StringCollection used when the + /// AutoCompleteSource is CustomSource. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxAutoCompleteCustomSourceDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteStringCollection AutoCompleteCustomSource { + get { + if (autoCompleteCustomSource == null) { + autoCompleteCustomSource = new AutoCompleteStringCollection(); + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + return autoCompleteCustomSource; + } + set { + if (autoCompleteCustomSource != value) { + + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + + autoCompleteCustomSource = value; + + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + SetAutoComplete(false, true); + } + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + internal ChildAccessibleObject ChildEditAccessibleObject { + get { + if (childEditAccessibleObject == null) { + childEditAccessibleObject = new ComboBoxChildEditUiaProvider(this, childEdit.Handle); + } + + return childEditAccessibleObject; + } + } + + internal ChildAccessibleObject ChildListAccessibleObject { + get { + if (childListAccessibleObject == null) { + childListAccessibleObject = + new ComboBoxChildListUiaProvider(this, DropDownStyle == ComboBoxStyle.Simple ? childListBox.Handle : dropDownHandle); + } + + return childListAccessibleObject; + } + } + + internal AccessibleObject ChildTextAccessibleObject { + get { + if (childTextAccessibleObject == null) { + childTextAccessibleObject = new ComboBoxChildTextUiaProvider(this); + } + + return childTextAccessibleObject; + } + } + + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.CreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "COMBOBOX"; + cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.CBS_HASSTRINGS | NativeMethods.CBS_AUTOHSCROLL; + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + if (!integralHeight) cp.Style |= NativeMethods.CBS_NOINTEGRALHEIGHT; + + switch (DropDownStyle) { + case ComboBoxStyle.Simple: + cp.Style |= NativeMethods.CBS_SIMPLE; + break; + case ComboBoxStyle.DropDown: + cp.Style |= NativeMethods.CBS_DROPDOWN; + // Make sure we put the height back or we won't be able to size the dropdown! + cp.Height = PreferredHeight; + break; + case ComboBoxStyle.DropDownList: + cp.Style |= NativeMethods.CBS_DROPDOWNLIST; + // Comment above... + cp.Height = PreferredHeight; + break; + } + switch (DrawMode) { + + case DrawMode.OwnerDrawFixed: + cp.Style |= NativeMethods.CBS_OWNERDRAWFIXED; + break; + case DrawMode.OwnerDrawVariable: + cp.Style |= NativeMethods.CBS_OWNERDRAWVARIABLE; + break; + } + + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(121, PreferredHeight); + } + } + + /// + /// + /// The ListSource to consume as this ListBox's source of data. + /// When set, a user can not modify the Items collection. + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.ListControlDataSourceDescr) + ] + public new object DataSource { + get { + return base.DataSource; + } + set { + base.DataSource = value; + } + } + + + /// + /// + /// Retrieves the value of the DrawMode property. The DrawMode property + /// controls whether the control is drawn by Windows or by the user. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DrawMode.Normal), + SRDescription(SR.ComboBoxDrawModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public DrawMode DrawMode { + get { + bool found; + int drawMode = Properties.GetInteger(PropDrawMode, out found); + if (found) { + return (DrawMode)drawMode; + } + + return DrawMode.Normal; + } + set { + if (DrawMode != value) { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); + } + ResetHeightCache(); + Properties.SetInteger(PropDrawMode, (int)value); + RecreateHandle(); + } + } + } + + /// + /// + /// Returns the width of the drop down box in a combo box. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownWidthDescr) + ] + public int DropDownWidth { + get { + bool found; + int dropDownWidth = Properties.GetInteger(PropDropDownWidth, out found); + + if (found) { + return dropDownWidth; + } + else { + return Width; + } + } + + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("DropDownWidth", SR.GetString(SR.InvalidArgument, "DropDownWidth", (value).ToString(CultureInfo.CurrentCulture))); + } + if (Properties.GetInteger(PropDropDownWidth) != value) { + Properties.SetInteger(PropDropDownWidth, value); + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETDROPPEDWIDTH, value, 0); + } + + } + } + } + + /// + /// + /// Sets the Height of the drop down box in a combo box. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownHeightDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(106) + ] + public int DropDownHeight { + get { + bool found; + int dropDownHeight = Properties.GetInteger(PropDropDownHeight, out found); + if (found) { + return dropDownHeight; + } + else { + return DefaultDropDownHeight; + } + } + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("DropDownHeight", SR.GetString(SR.InvalidArgument, "DropDownHeight", (value).ToString(CultureInfo.CurrentCulture))); + } + if (Properties.GetInteger(PropDropDownHeight) != value) { + Properties.SetInteger(PropDropDownHeight, value); + + // The dropDownHeight is not reflected unless the + // combobox integralHeight == false.. + IntegralHeight = false; + } + } + } + + /// + /// + /// Indicates whether the DropDown of the combo is currently dropped down. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxDroppedDownDescr) + ] + public bool DroppedDown { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.CB_GETDROPPEDSTATE, 0, 0) ) != 0; + } + else { + return false; + } + } + + set { + + if (!IsHandleCreated) { + CreateHandle(); + } + + SendMessage(NativeMethods.CB_SHOWDROPDOWN, value ? -1 : 0, 0); + } + } + + /// + /// + /// + /// Gets or + /// sets + /// the flat style appearance of the button control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Standard), + Localizable(true), + SRDescription(SR.ComboBoxFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { + return flatStyle; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + flatStyle = value; + Invalidate(); + } + } + + /// + /// + /// Returns true if this control has focus. + /// + public override bool Focused { + get { + if (base.Focused) return true; + IntPtr focus = UnsafeNativeMethods.GetFocus(); + return focus != IntPtr.Zero && ((childEdit != null && focus == childEdit.Handle) || (childListBox != null && focus == childListBox.Handle)); + } + } + + /// + /// + /// + /// Gets or sets the foreground color of the control. + /// + /// + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// Indicates if the combo should avoid showing partial Items. If so, + /// then only full items will be displayed, and the list portion will be resized + /// to prevent partial items from being shown. Otherwise, they will be + /// shown + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ComboBoxIntegralHeightDescr) + ] + public bool IntegralHeight { + get { + return integralHeight; + } + + set { + if (integralHeight != value) { + integralHeight = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Returns the height of an item in the combo box. When drawMode is Normal + /// or OwnerDrawFixed, all items have the same height. When drawMode is + /// OwnerDrawVariable, this method returns the height that will be given + /// to new items added to the combo box. To determine the actual height of + /// an item, use the GetItemHeight() method with an integer parameter. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ComboBoxItemHeightDescr) + ] + public int ItemHeight { + get { + + DrawMode drawMode = DrawMode; + if (drawMode == DrawMode.OwnerDrawFixed || + drawMode == DrawMode.OwnerDrawVariable || + !IsHandleCreated) { + + bool found; + int itemHeight = Properties.GetInteger(PropItemHeight, out found); + if (found) { + return itemHeight; + } + else { + return FontHeight + 2; // bug (90774)+2 for the 1 pixel gap on each side (up and Bottom) of the Text. + } + } + + // Note that the above if clause deals with the case when the handle has not yet been created + Debug.Assert(IsHandleCreated, "Handle should be created at this point"); + + int h = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, 0, 0) ); + if (h == -1) { + throw new Win32Exception(); + } + return h; + } + + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture))); + } + + ResetHeightCache(); + + if (Properties.GetInteger(PropItemHeight) != value) { + Properties.SetInteger(PropItemHeight, value); + if (DrawMode != DrawMode.Normal) { + UpdateItemHeight(); + } + } + } + } + + /// + /// + /// Collection of the items contained in this ComboBox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + MergableProperty(false) + ] + public ObjectCollection Items { + get { + if (itemsCollection == null) { + itemsCollection = new ObjectCollection(this); + } + return itemsCollection; + } + } + + // Text used to match an item in the list when auto-completion + // is used in DropDownList style. + private string MatchingText { + get { + string matchingText = (string)Properties.GetObject(PropMatchingText); + return (matchingText == null) ? string.Empty : matchingText; + } + set { + if (value != null || this.Properties.ContainsObject(PropMatchingText)) { + Properties.SetObject(PropMatchingText, value); + } + } + } + + /// + /// + /// The maximum number of items to be shown in the dropdown portion + /// of the ComboBox. This number can be between 1 and 100. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(8), + Localizable(true), + SRDescription(SR.ComboBoxMaxDropDownItemsDescr) + ] + public int MaxDropDownItems { + get { + return maxDropDownItems; + } + set { + if (value < 1 || value > 100) { + throw new ArgumentOutOfRangeException("MaxDropDownItems", SR.GetString(SR.InvalidBoundArgument, "MaxDropDownItems", (value).ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture), (100).ToString(CultureInfo.CurrentCulture))); + } + maxDropDownItems = (short)value; + } + } + + public override Size MaximumSize { + get { return base.MaximumSize; } + set { + base.MaximumSize = new Size(value.Width, 0); + } + } + + public override Size MinimumSize { + get { return base.MinimumSize; } + set { + base.MinimumSize = new Size(value.Width, 0); + } + } + + /// + /// + /// The maximum length of the text the user may type into the edit control + /// of a combo box. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.ComboBoxMaxLengthDescr) + ] + public int MaxLength { + get { + return Properties.GetInteger(PropMaxLength); + } + set { + if (value < 0) value = 0; + if (MaxLength != value) { + Properties.SetInteger(PropMaxLength, value); + if (IsHandleCreated) SendMessage(NativeMethods.CB_LIMITTEXT, value, 0); + } + } + } + + /// + /// + /// If the mouse is over the combobox, draw selection rect. + /// + internal bool MouseIsOver { + get { return mouseOver; } + set { + if (mouseOver != value) { + mouseOver = value; + // Nothing to see here... Just keep on walking... VSWhidbey 504477. + // Turns out that with Theming off, we don't get quite the same messages as with theming on, so + // our drawing gets a little messed up. So in case theming is off, force a draw here. + if ((!ContainsFocus || !Application.RenderWithVisualStyles) && this.FlatStyle == FlatStyle.Popup) { + Invalidate(); + Update(); + } + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + + /// + /// + /// ApplySizeConstraints calls into this method when DropDownStyles is DropDown and DropDownList. + /// This causes PreferredSize to be bounded by PreferredHeight in these two cases only. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxPreferredHeightDescr) + ] + public int PreferredHeight { + get { + if (!FormattingEnabled) { + //see #465057 for why I did this + + //do preferred height the old broken way for everett apps + //we need this for compat reasons because (get this) + // (a) everett preferredheight was always wrong. + // (b) so, when combobox1.Size = actualdefaultsize was called, it would enter setboundscore + // (c) this updated requestedheight + // (d) if the user then changed the combo to simple style, the height did not change. + // We simply cannot match this behavior if preferredheight is corrected so that (b) never + // occurs. We simply do not know when Size was set. + + // So in whidbey, the behavior will be: + // (1) user uses default size = setting dropdownstyle=simple will revert to simple height + // (2) user uses nondefault size = setting dropdownstyle=simple will not change height from this value + + //In everett + // if the user manually sets Size = (121, 20) in code (usually height gets forced to 21), then he will see Whidey.(1) above + // user usually uses nondefault size and will experience whidbey.(2) above + + Size textSize = TextRenderer.MeasureText(LayoutUtils.TestString, this.Font, new Size(Int16.MaxValue, (int)(FontHeight * 1.25)), TextFormatFlags.SingleLine); + prefHeightCache = (short)(textSize.Height + SystemInformation.BorderSize.Height * 8 + Padding.Size.Height); + + return prefHeightCache ; + } + else { + // Normally we do this sort of calculation in GetPreferredSizeCore which has builtin + // caching, but in this case we can not because PreferredHeight is used in ApplySizeConstraints + // which is used by GetPreferredSize (infinite loop). + if (prefHeightCache < 0) { + Size textSize = TextRenderer.MeasureText(LayoutUtils.TestString, this.Font, new Size(Int16.MaxValue, (int)(FontHeight * 1.25)), TextFormatFlags.SingleLine); + + // For a "simple" style combobox, the preferred height depends on the + // number of items in the combobox. + if (DropDownStyle == ComboBoxStyle.Simple) { + int itemCount = Items.Count + 1; + prefHeightCache = (short)(textSize.Height * itemCount + SystemInformation.BorderSize.Height * 16 + Padding.Size.Height); + } + else { + // We do this old school rather than use SizeFromClientSize because CreateParams calls this + // method and SizeFromClientSize calls CreateParams (another infinite loop.) + prefHeightCache = (short)GetComboHeight(); + } + } + return prefHeightCache; + } + } + } + + + // VSWhidbey 194386 - ComboBox.PreferredHeight returns incorrect values + // This is translated from windows implementation. Since we cannot control the size + // of the combo box, we need to use the same calculation they do. + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // "0" is what Windows uses in all languages. + private int GetComboHeight() { + + int cyCombo = 0; + // Add on CYEDGE just for some extra space in the edit field/static item. + // It's really only for static text items, but we want static & editable + // controls to be the same height. + Size textExtent = Size.Empty; + + using (WindowsFont font = WindowsFont.FromFont(this.Font)) { + // this is the character that Windows uses to determine the extent + textExtent = WindowsGraphicsCacheManager.MeasurementGraphics.GetTextExtent("0", font); + } + + int dyEdit = textExtent.Height + SystemInformation.Border3DSize.Height; + + if (DrawMode != DrawMode.Normal) { + // This is an ownerdraw combo. Have the owner tell us how tall this + // item is. + dyEdit = ItemHeight; + } + + // Set the initial width to be the combo box rect. Later we will shorten it + // if there is a dropdown button. + Size fixedFrameBoderSize = SystemInformation.FixedFrameBorderSize; + cyCombo = 2 * fixedFrameBoderSize.Height + dyEdit; + return cyCombo; + } + + private string[] GetStringsForAutoComplete(IList collection) + { + if (collection is AutoCompleteStringCollection) + { + string[] strings = new string[AutoCompleteCustomSource.Count]; + for (int i = 0; i < AutoCompleteCustomSource.Count; i++) { + strings[i] = AutoCompleteCustomSource[i]; + } + return strings; + + } + else if (collection is ObjectCollection) + { + string[] strings = new string[itemsCollection.Count]; + for (int i = 0; i < itemsCollection.Count; i++) { + strings[i] = GetItemText(itemsCollection[i]); + } + return strings; + + } + return new string[0]; + } + + + /// + /// + /// The [zero based] index of the currently selected item in the combos list. + /// Note If the value of index is -1, then the ComboBox is + /// set to have no selection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedIndexDescr) + ] + public override int SelectedIndex { + get { + if (IsHandleCreated) { + + return unchecked( (int) (long)SendMessage(NativeMethods.CB_GETCURSEL, 0, 0) ); + } + else { + return selectedIndex; + } + } + set { + if (SelectedIndex != value) { + int itemCount = 0; + if (itemsCollection != null) { + itemCount = itemsCollection.Count; + } + + if (value < -1 || value >= itemCount) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETCURSEL, value, 0); + + } + else { + selectedIndex = value; + } + + UpdateText(); + + if (IsHandleCreated) { + OnTextChanged(EventArgs.Empty); + } + + OnSelectedItemChanged(EventArgs.Empty); + OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// The handle to the object that is currently selected in the + /// combos list. + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedItemDescr) + ] + public object SelectedItem { + get { + int index = SelectedIndex; + return (index == -1) ? null : Items[index]; + } + set { + int x = -1; + + if (itemsCollection != null) { + //bug (82115) + if (value != null) + x = itemsCollection.IndexOf(value); + else + SelectedIndex = -1; + } + + if (x != -1) { + SelectedIndex = x; + } + } + } + + /// + /// + /// The selected text in the edit component of the ComboBox. If the + /// ComboBox has ComboBoxStyle.DROPDOWNLIST, the return is an empty + /// string (""). + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedTextDescr) + ] + public string SelectedText { + get { + if (DropDownStyle == ComboBoxStyle.DropDownList) return ""; + return Text.Substring(SelectionStart, SelectionLength); + } + set { + if (DropDownStyle != ComboBoxStyle.DropDownList) { + //guard against null string, since otherwise we will throw an + //AccessViolation exception, which is bad + string str = (value == null ? "" : value); + CreateControl(); + if (IsHandleCreated) { + Debug.Assert(childEdit != null); + if (childEdit != null) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_REPLACESEL, NativeMethods.InvalidIntPtr, str); + } + } + } + } + } + + /// + /// + /// The length, in characters, of the selection in the editbox. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionLengthDescr) + ] + public int SelectionLength { + get { + int[] end = new int[] { 0 }; + int[] start = new int[] { 0 }; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETEDITSEL, start, end); + return end[0] - start[0]; + } + set { + // SelectionLength can be negtive... + Select(SelectionStart, value); + } + } + + /// + /// + /// The [zero-based] index of the first character in the current text selection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionStartDescr) + ] + public int SelectionStart { + get { + int[] value = new int[] { 0 }; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETEDITSEL, value, (int[])null); + return value[0]; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidArgument, "SelectionStart", value.ToString(CultureInfo.CurrentCulture))); + } + Select(value, SelectionLength); + } + } + + /// + /// + /// Indicates if the Combos list is sorted or not. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ComboBoxSortedDescr) + ] + public bool Sorted { + get { + return sorted; + } + set { + if (sorted != value) { + if (this.DataSource != null && value) { + throw new ArgumentException(SR.GetString(SR.ComboBoxSortWithDataSource)); + } + + sorted = value; + RefreshItems(); + SelectedIndex = -1; + } + } + } + + /// + /// + /// The type of combo that we are right now. The value would come + /// from the System.Windows.Forms.ComboBoxStyle enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ComboBoxStyle.DropDown), + SRDescription(SR.ComboBoxStyleDescr), + RefreshPropertiesAttribute(RefreshProperties.Repaint) + ] + public ComboBoxStyle DropDownStyle { + get { + bool found; + int style = Properties.GetInteger(PropStyle, out found); + if (found) { + return (ComboBoxStyle)style; + } + + return ComboBoxStyle.DropDown; + } + set { + if (DropDownStyle != value) { + + // verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ComboBoxStyle.Simple, (int)ComboBoxStyle.DropDownList)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ComboBoxStyle)); + } + + if (value == ComboBoxStyle.DropDownList && + this.AutoCompleteSource != AutoCompleteSource.ListItems && + this.AutoCompleteMode != AutoCompleteMode.None) { + this.AutoCompleteMode = AutoCompleteMode.None; + } + + // reset preferred height. + ResetHeightCache(); + + Properties.SetInteger(PropStyle, (int)value); + + if (IsHandleCreated) { + RecreateHandle(); + } + + OnDropDownStyleChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + Bindable(true) + ] + public override string Text { + get { + if (SelectedItem != null && !BindingFieldEmpty) { + + //preserve everett behavior if "formatting enabled == false" -- just return selecteditem text. + if (FormattingEnabled) { + string candidate = GetItemText(SelectedItem); + if (!String.IsNullOrEmpty(candidate)) { + if (String.Compare(candidate, base.Text, true, CultureInfo.CurrentCulture) == 0) { + return candidate; //for whidbey, if we only differ by case -- return the candidate; + } + } + } + else { + return FilterItemOnProperty(SelectedItem).ToString(); //heinous. + } + } + return base.Text; + } + set { + if (DropDownStyle == ComboBoxStyle.DropDownList && !IsHandleCreated && !String.IsNullOrEmpty(value) && FindStringExact(value) == -1) + return; + + base.Text = value; + object selectedItem = null; + + selectedItem = SelectedItem; + + if (!DesignMode) { + //bug <70650> Subhag removed 'value.Length == 0' check to handle String.Empty. + // + if (value == null) { + SelectedIndex = -1; + } + else if (value != null && + (selectedItem == null || (String.Compare(value, GetItemText(selectedItem), false, CultureInfo.CurrentCulture) != 0))) { + + int index = FindStringIgnoreCase(value); + + //we cannot set the index to -1 unless we want to do a hack and save/restore text + //because the native control will erase the text when we change the index to -1 + if (index != -1) { + SelectedIndex = index; + } + } + } + } + } + + + private int FindStringIgnoreCase(string value) { + //look for an exact match and then a case insensitive match if that fails. + int index = FindStringExact(value, -1, false); + + if (index == -1) { + index = FindStringExact(value, -1, true); + } + + return index; + } + + // Special AutoComplete notification handling + // If the text changed, this will fire TextChanged + // If it matches an item in the list, this will fire SIC and SVC + private void NotifyAutoComplete() { + NotifyAutoComplete(true); + } + + // Special AutoComplete notification handling + // If the text changed, this will fire TextChanged + // If it matches an item in the list, this will fire SIC and SVC + private void NotifyAutoComplete(bool setSelectedIndex) { + string text = this.Text; + bool textChanged = (text != this.lastTextChangedValue); + bool selectedIndexSet = false; + + if (setSelectedIndex) { + // Process return key. This is sent by the AutoComplete DropDown when a + // selection is made from the DropDown + // Check to see if the Text Changed. If so, at least fire a TextChanged + int index = FindStringIgnoreCase(text); + + if ((index != -1) && (index != SelectedIndex)) { + // We found a match, do the full monty + SelectedIndex = index; + + // Put the cursor at the end + SelectionStart = 0; + SelectionLength = text.Length; + + selectedIndexSet = true; + } + } + + //don't fire textch if we had set the selectedindex -- because it was already fired if so. + if (textChanged && !selectedIndexSet) { + // No match, just fire a TextChagned + OnTextChanged(EventArgs.Empty); + } + + // Save the new value + this.lastTextChangedValue = text; + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + // Returns true if using System AutoComplete + private bool SystemAutoCompleteEnabled { + get { + return ((this.autoCompleteMode != AutoCompleteMode.None) && (this.DropDownStyle != ComboBoxStyle.DropDownList)); + } + } + + // VSWhidbey 95691: Prevent this event from being displayed in the Property Grid. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_DRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWITEM, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownDescr)] + public event EventHandler DropDown { + add { + Events.AddHandler(EVENT_DROPDOWN, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWN, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] + public event MeasureItemEventHandler MeasureItem { + add { + Events.AddHandler(EVENT_MEASUREITEM, value); + UpdateItemHeight(); + } + remove { + Events.RemoveHandler(EVENT_MEASUREITEM, value); + UpdateItemHeight(); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectionChangeCommittedEventDescr)] + public event EventHandler SelectionChangeCommitted { + add { + Events.AddHandler(EVENT_SELECTIONCHANGECOMMITTED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTIONCHANGECOMMITTED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxDropDownStyleChangedDescr)] + public event EventHandler DropDownStyleChanged { + add { + Events.AddHandler(EVENT_DROPDOWNSTYLE, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWNSTYLE, value); + } + } + + /// + /// + /// ComboBox Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// This will fire the TextUpdate Event on the ComboBox. This events fires when the Combobox gets the + /// CBN_EDITUPDATE notification. + // + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnTextUpdateDescr)] + public event EventHandler TextUpdate { + add { + Events.AddHandler(EVENT_TEXTUPDATE, value); + } + remove { + Events.RemoveHandler(EVENT_TEXTUPDATE, value); + } + } + + + /// + /// + /// This will fire the DropDownClosed Event on the ComboBox. This events fires when the Combobox gets the + /// CBN_CLOSEUP notification. This happens when the DropDown closes. + // + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownClosedDescr)] + public event EventHandler DropDownClosed { + add { + Events.AddHandler(EVENT_DROPDOWNCLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWNCLOSED, value); + } + } + + /// + /// + /// Performs the work of adding the specified items to the combobox + /// + [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] + protected virtual void AddItemsCore(object[] value) { + int count = value == null ? 0 : value.Length; + if (count == 0) { + return; + } + + BeginUpdate(); + try { + Items.AddRangeInternal(value); + } + finally { + EndUpdate(); + } + } + + /// + /// + /// Disables redrawing of the combo box. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// combo box is deferred until the call to endUpdate(). + /// + public void BeginUpdate() { + updateCount++; + BeginUpdateInternal(); + } + + private void CheckNoDataSource() { + if (DataSource != null) { + throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); + } + } + + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ComboBoxUiaProvider(this); + } + else if (AccessibilityImprovements.Level1) { + return new ComboBoxExAccessibleObject(this); + } + else { + return new ComboBoxAccessibleObject(this); + } + } + + /// + /// + internal bool UpdateNeeded() + { + return (updateCount == 0); + } + + + /// + /// This procedure takes in the message, converts the Edit handle coordinates into Combo Box Coordinates + /// + /// + internal Point EditToComboboxMapping(Message m) { + if (childEdit == null) { + return new Point(0, 0); + } + // Get the Combox Rect ... + // + NativeMethods.RECT comboRectMid = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref comboRectMid); + // + //Get the Edit Rectangle... + // + NativeMethods.RECT editRectMid = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, childEdit.Handle), ref editRectMid); + + //get the delta + int comboXMid = NativeMethods.Util.SignedLOWORD(m.LParam) + (editRectMid.left - comboRectMid.left); + int comboYMid = NativeMethods.Util.SignedHIWORD(m.LParam) + (editRectMid.top - comboRectMid.top); + + return (new Point(comboXMid, comboYMid)); + + } + + + /// + /// Subclassed window procedure for the edit and list child controls of the + /// combo box. + /// + /// + private void ChildWndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_CHAR: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + case NativeMethods.WM_SYSCHAR: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyEventArgs(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + if (SystemAutoCompleteEnabled && !ACNativeWindow.AutoCompleteActive) { + finder.FindDropDowns(false); + } + + if (AutoCompleteMode != AutoCompleteMode.None) { + char keyChar = unchecked((char)(long)m.WParam); + if (keyChar == (char)(int)Keys.Escape) { + this.DroppedDown = false; + } + else if (keyChar == (char)(int)Keys.Return && this.DroppedDown) { + UpdateText(); + OnSelectionChangeCommittedInternal(EventArgs.Empty); + this.DroppedDown = false; + } + } + + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + + case NativeMethods.WM_INPUTLANGCHANGE: + DefChildWndProc( ref m ); + break; + + case NativeMethods.WM_KEYUP: + case NativeMethods.WM_SYSKEYUP: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + if (SystemAutoCompleteEnabled && !ACNativeWindow.AutoCompleteActive) { + finder.FindDropDowns(); + } + + break; + case NativeMethods.WM_KILLFOCUS: + // Consider - If we dont' have a childwndproc, then we don't get here, so we don't + // update the cache. Do we need to? This happens when we have a DropDownList. + if (!DesignMode) { + OnImeContextStatusChanged( m.HWnd ); + } + + DefChildWndProc(ref m); + // We don't want to fire the focus events twice - + // once in the combobox and once here. + if (fireLostFocus) { + this.InvokeLostFocus(this, EventArgs.Empty); + } + + if (FlatStyle == FlatStyle.Popup) { + this.Invalidate(); + } + + break; + case NativeMethods.WM_SETFOCUS: + + // Consider - If we dont' have a childwndproc, then we don't get here, so we don't + // set the status. Do we need to? This happens when we have a DropDownList. + if (!DesignMode) { + ImeContext.SetImeStatus(CachedImeMode, m.HWnd); + } + + if (!HostedInWin32DialogManager) { + IContainerControl c = GetContainerControlInternal(); + if (c != null) { + ContainerControl container = c as ContainerControl; + if (container != null) { + if (!container.ActivateControlInternal(this, false)) { + return; + } + } + } + } + + DefChildWndProc(ref m); + + // We don't want to fire the focus events twice - + // once in the combobox and once here. + if (fireSetFocus) { + if (!DesignMode && (childEdit != null && m.HWnd == childEdit.Handle) && !LocalAppContextSwitches.EnableLegacyIMEFocusInComboBox) { + WmImeSetFocus(); + } + this.InvokeGotFocus(this, EventArgs.Empty); + } + + if (FlatStyle == FlatStyle.Popup) { + this.Invalidate(); + } + break; + + case NativeMethods.WM_SETFONT: + DefChildWndProc(ref m); + if (childEdit != null && m.HWnd == childEdit.Handle) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_SETMARGINS, + NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); + } + break; + case NativeMethods.WM_LBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptlc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, Ptlc.X, Ptlc.Y, 0)); + break; + + case NativeMethods.WM_MBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptmc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Middle, 1, Ptmc.X, Ptmc.Y, 0)); + break; + + case NativeMethods.WM_RBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptrc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Right, 1, Ptrc.X, Ptrc.Y, 0)); + break; + + case NativeMethods.WM_LBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptl = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, Ptl.X, Ptl.Y, 0)); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + Rectangle ClientRect = new Rectangle(r.left, r.top, r.right - r.left, r.bottom - r.top); + // Get the mouse location + // + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x, y); + pt = PointToScreen(pt); + // combo box gets a WM_LBUTTONUP for focus change ... + // So check MouseEvents.... + if (mouseEvents && !ValidationCancelled) { + mouseEvents = false; + if (mousePressed) { + if (ClientRect.Contains(pt)) { + mousePressed = false; + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + else { + mousePressed = false; + mouseInEdit = false; + OnMouseLeave(EventArgs.Empty); + } + } + } + DefChildWndProc(ref m); + CaptureInternal = false; + + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + pt = EditToComboboxMapping(m); + + OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, pt.X, pt.Y, 0)); + break; + case NativeMethods.WM_MBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point P = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Middle, 1, P.X, P.Y, 0)); + break; + case NativeMethods.WM_RBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + + //set the mouse capture .. this is the Child Wndproc.. + // Bug# 112108: If I set the capture=true here, the + // DefWndProc() never fires the WM_CONTEXTMENU that would show + // the default context menu. + // + if (this.ContextMenu != null || this.ContextMenuStrip != null) + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Pt = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Right, 1, Pt.X, Pt.Y, 0)); + break; + case NativeMethods.WM_MBUTTONUP: + mousePressed = false; + mouseEvents = false; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = false; + DefChildWndProc(ref m); + OnMouseUp(new MouseEventArgs(MouseButtons.Middle, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + break; + case NativeMethods.WM_RBUTTONUP: + mousePressed = false; + mouseEvents = false; + //set the mouse capture .. this is the Child Wndproc.. + // + if (this.ContextMenu != null) + CaptureInternal = false; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point ptRBtnUp = EditToComboboxMapping(m); + + OnMouseUp(new MouseEventArgs(MouseButtons.Right, 1, ptRBtnUp.X, ptRBtnUp.Y, 0)); + break; + + case NativeMethods.WM_CONTEXTMENU: + // Forward context menu messages to the parent control + if (this.ContextMenu != null || this.ContextMenuStrip != null) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_CONTEXTMENU, m.WParam, m.LParam); + } + else { + DefChildWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSEMOVE: + Point point = EditToComboboxMapping(m); + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + OnMouseEnterInternal(EventArgs.Empty); + OnMouseMove(new MouseEventArgs(MouseButtons, 0, point.X, point.Y, 0)); + break; + + case NativeMethods.WM_SETCURSOR: + if (Cursor != DefaultCursor && childEdit != null && m.HWnd == childEdit.Handle && NativeMethods.Util.LOWORD(m.LParam) == NativeMethods.HTCLIENT) { + Cursor.CurrentInternal = Cursor; + } + else { + DefChildWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSELEAVE: + DefChildWndProc(ref m); + OnMouseLeaveInternal(EventArgs.Empty); + break; + + default: + DefChildWndProc(ref m); + break; + } + } + + /// + /// Helper to handle MouseEnter. + /// + /// + private void OnMouseEnterInternal(EventArgs args) { + if (!mouseInEdit) { + OnMouseEnter(args); + mouseInEdit = true; + } + } + + /// + /// Helper to handle mouseleave + /// + /// + private void OnMouseLeaveInternal(EventArgs args) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref rect); + Rectangle Rect = new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + Point p = MousePosition; + if (!Rect.Contains(p)) { + OnMouseLeave(args); + mouseInEdit = false; + } + } + + /// + /// + /// + private void DefChildWndProc(ref Message m) { + if (childEdit != null) { + NativeWindow childWindow; + if (m.HWnd == childEdit.Handle) { + childWindow = childEdit; + } else if (AccessibilityImprovements.Level3 && m.HWnd == dropDownHandle) { + childWindow = childDropDown; + } + else { + childWindow = childListBox; + } + + //childwindow could be null if the handle was recreated while within a message handler + // and then whoever recreated the handle allowed the message to continue to be processed + //we cannot really be sure the new child will properly handle this window message, so we eat it. + if (childWindow != null) { + childWindow.DefWndProc(ref m); + } + } + } + + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + } + base.Dispose(disposing); + } + + + + + /// + /// + /// Reenables redrawing of the combo box. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// combo box is deferred until the call to endUpdate(). + /// + public void EndUpdate() { + updateCount--; + if (updateCount == 0 && AutoCompleteSource == AutoCompleteSource.ListItems) + { + SetAutoComplete(false, false); + } + if (EndUpdateInternal()) { + if (childEdit != null && childEdit.Handle != IntPtr.Zero) { + SafeNativeMethods.InvalidateRect(new HandleRef(this, childEdit.Handle), null, false); + } + if (childListBox != null && childListBox.Handle != IntPtr.Zero) { + SafeNativeMethods.InvalidateRect(new HandleRef(this, childListBox.Handle), null, false); + } + } + } + + /// + /// + /// Finds the first item in the combo box that starts with the given string. + /// The search is not case sensitive. + /// + public int FindString(string s) { + return FindString(s, -1); + } + + /// + /// + /// Finds the first item after the given index which starts with the given + /// string. The search is not case sensitive. + /// + public int FindString(string s, int startIndex) { + if (s == null) { + return -1; + } + + if (itemsCollection == null || itemsCollection.Count == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of CB_FINDSTRING. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, false); + } + + /// + /// + /// Finds the first item in the combo box that matches the given string. + /// The strings must match exactly, except for differences in casing. + /// + public int FindStringExact(string s) { + return FindStringExact(s, -1, true); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match exactly, except for differences in + /// casing. + /// + public int FindStringExact(string s, int startIndex) { + return FindStringExact(s, startIndex, true); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match exactly, except for differences in + /// casing. + /// + internal int FindStringExact(string s, int startIndex, bool ignorecase) { + if (s == null) return -1; + + if (itemsCollection == null || itemsCollection.Count == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of CB_FINDSTRINGEXACT. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, true, ignorecase); + } + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + if (DropDownStyle == ComboBoxStyle.DropDown + || DropDownStyle == ComboBoxStyle.DropDownList) { + + proposedHeight = PreferredHeight; + } + + return base.ApplyBoundsConstraints(suggestedX,suggestedY, proposedWidth, proposedHeight); + } + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + if (factor.Width != 1F && factor.Height != 1F) { + // VSWhidbey 440275 + // we get called on scale before we get a notification that our font has changed. + // in this case, we need to reset our height cache. + ResetHeightCache(); + } + base.ScaleControl(factor, specified); + } + + /// + /// + /// Returns the height of the given item in an OwnerDrawVariable style + /// combo box. This method should not be used for Normal or OwnerDrawFixed + /// style combo boxes. + /// + public int GetItemHeight(int index) { + + // This function is only relevant for OwnerDrawVariable + if (DrawMode != DrawMode.OwnerDrawVariable) { + return ItemHeight; + } + + if (index < 0 || itemsCollection == null || index >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (IsHandleCreated) { + + int h = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, index, 0) ); + if (h == -1) { + throw new Win32Exception(); + } + return h; + } + + return ItemHeight; + } + + internal IntPtr GetListHandle() { + return DropDownStyle == ComboBoxStyle.Simple ? childListBox.Handle : dropDownHandle; + } + + internal NativeWindow GetListNativeWindow() { + return DropDownStyle == ComboBoxStyle.Simple ? childListBox : childDropDown; + } + + internal int GetListNativeWindowRuntimeIdPart() { + var listNativeWindow = GetListNativeWindow(); + return listNativeWindow != null ? listNativeWindow.GetHashCode() : 0; + } + + internal override IntPtr InitializeDCForWmCtlColor(IntPtr dc, int msg) { + if ((msg == NativeMethods.WM_CTLCOLORSTATIC) && !ShouldSerializeBackColor()) { + // Let the Win32 Edit control handle background colors itself. + // This is necessary because a disabled edit control will display a different + // BackColor than when enabled. + return IntPtr.Zero; + } + else if ((msg == NativeMethods.WM_CTLCOLORLISTBOX) && GetStyle(ControlStyles.UserPaint)) { + // VSWhidbey#93929. Base class returns hollow brush when UserPaint style is set, to avoid flicker in + // main control. But when returning colors for child dropdown list, return normal ForeColor/BackColor, + // since hollow brush leaves the list background unpainted. + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), ColorTranslator.ToWin32(ForeColor)); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(BackColor)); + return BackColorBrush; + } + else { + return base.InitializeDCForWmCtlColor(dc, msg); + } + } + + // Returns true when the key processing needs to be intercepted to allow + // auto-completion in DropDownList style. + private bool InterceptAutoCompleteKeystroke(Message m) { + if (m.Msg == NativeMethods.WM_KEYDOWN) { + Debug.Assert((ModifierKeys & Keys.Alt) == 0); + // Keys.Delete only triggers a WM_KEYDOWN and WM_KEYUP, and no WM_CHAR. That's why it's treated separately. + if ((Keys)unchecked( (int) (long)m.WParam) == Keys.Delete) { + // Reset matching text and remove any selection + this.MatchingText = ""; + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + if (this.Items.Count > 0) { + SelectedIndex = 0; + } + return false; + } + } + else if (m.Msg == NativeMethods.WM_CHAR) { + Debug.Assert((ModifierKeys & Keys.Alt) == 0); + char keyChar = unchecked((char)(long)m.WParam); + if (keyChar == (char)Keys.Back) { + if (DateTime.Now.Ticks - this.autoCompleteTimeStamp > AutoCompleteTimeout || + this.MatchingText.Length <= 1) { + // Reset matching text and remove any selection + this.MatchingText = ""; + if (this.Items.Count > 0) { + SelectedIndex = 0; + } + } + else { + // Remove one character from matching text and rematch + this.MatchingText = this.MatchingText.Remove(this.MatchingText.Length - 1); + this.SelectedIndex = FindString(this.MatchingText); + } + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return false; + } + else if (keyChar == (char)Keys.Escape) { + this.MatchingText = ""; + } + + string newMatchingText; + if (keyChar != (char)Keys.Escape && keyChar != (char)Keys.Return && !DroppedDown + && AutoCompleteMode != AutoCompleteMode.Append) { + DroppedDown = true; + } + if (DateTime.Now.Ticks - this.autoCompleteTimeStamp > AutoCompleteTimeout) { + newMatchingText = new string(keyChar, 1); + if (FindString(newMatchingText) != -1) { + this.MatchingText = newMatchingText; + // Select the found item + } + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return false; + } + else { + newMatchingText = this.MatchingText + keyChar; + int itemFound = FindString(newMatchingText); + if (itemFound != -1) { + this.MatchingText = newMatchingText; + if (itemFound != this.SelectedIndex) { + this.SelectedIndex = itemFound; + } + } + // Do not change the selection + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return true; + } + } + return false; + } + + // Invalidate the entire control, including child HWNDs and non-client areas + private void InvalidateEverything() { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), + null, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_FRAME | // Control.Invalidate(true) doesn't invalidate the non-client region + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + + /// + /// + /// Determines if keyData is in input key that the control wants. + /// Overridden to return true for RETURN and ESCAPE when the combo box is + /// dropped down. + /// + /// + protected override bool IsInputKey(Keys keyData) { + + Keys keyCode = keyData & (Keys.KeyCode | Keys.Alt); + if (keyCode == Keys.Return || keyCode == Keys.Escape) { + if (this.DroppedDown || autoCompleteDroppedDown) { + //old behavior + return true; + } + else if (SystemAutoCompleteEnabled && ACNativeWindow.AutoCompleteActive) { + autoCompleteDroppedDown = true; + return true; + } + } + + return base.IsInputKey(keyData); + } + + /// + /// Adds the given item to the native combo box. This asserts if the handle hasn't been + /// created. + /// + private int NativeAdd(object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.CB_ADDSTRING, 0, GetItemText(item))); + if (insertIndex < 0) { + throw new OutOfMemoryException(SR.GetString(SR.ComboBoxItemOverflow)); + } + return insertIndex; + } + + /// + /// Clears the contents of the combo box. + /// + private void NativeClear() { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + string saved = null; + if (DropDownStyle != ComboBoxStyle.DropDownList) { + saved = WindowText; + } + SendMessage(NativeMethods.CB_RESETCONTENT, 0, 0); + if (saved != null) { + WindowText = saved; + } + } + + /// + /// Get the text stored by the native control for the specified list item. + /// + private string NativeGetItemText(int index) { + int len = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETLBTEXTLEN, index, 0)); + StringBuilder sb = new StringBuilder(len + 1); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETLBTEXT, index, sb); + return sb.ToString(); + } + + /// + /// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been + /// created or if the resulting insert index doesn't match the passed in index. + /// + private int NativeInsert(int index, object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.CB_INSERTSTRING, index, GetItemText(item))); + if (insertIndex < 0) { + throw new OutOfMemoryException(SR.GetString(SR.ComboBoxItemOverflow)); + } + Debug.Assert(insertIndex == index, "NativeComboBox inserted at " + insertIndex + " not the requested index of " + index); + return insertIndex; + } + + /// + /// Removes the native item from the given index. + /// + private void NativeRemoveAt(int index) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + + // Windows combo does not invalidate the selected region if you remove the + // currently selected item. Test for this and invalidate. Note that because + // invalidate will lazy-paint we can actually invalidate before we send the + // delete message. + // + if (DropDownStyle == ComboBoxStyle.DropDownList && SelectedIndex == index) { + Invalidate(); + } + + SendMessage(NativeMethods.CB_DELETESTRING, index, 0); + } + + internal override void RecreateHandleCore() { + + string oldText = WindowText; + base.RecreateHandleCore(); + if (!String.IsNullOrEmpty(oldText) && String.IsNullOrEmpty(WindowText)) { + WindowText = oldText; //restore the window text + } + } + /// + /// + /// Overridden to avoid multiple layouts during handle creation due to combobox size change + /// see bug 458948 + /// + /// + protected override void CreateHandle() + { + using (new LayoutTransaction(ParentInternal, this, PropertyNames.Bounds)) + { + base.CreateHandle(); + } + } + + /// + /// + /// Overridden to make sure all the items and styles get set up correctly. + /// Inheriting classes should not forget to call + /// base.OnHandleCreated() + /// + /// + protected override void OnHandleCreated(EventArgs e) { + + base.OnHandleCreated(e); + + if (MaxLength > 0) { + SendMessage(NativeMethods.CB_LIMITTEXT, MaxLength, 0); + } + + // Get the handles and wndprocs of the ComboBox's child windows + // + Debug.Assert(childEdit == null, "Child edit window already attached"); + Debug.Assert(childListBox == null, "Child listbox window already attached"); + + bool ok = childEdit == null && childListBox == null; + + if (ok && DropDownStyle != ComboBoxStyle.DropDownList) { + IntPtr hwnd = UnsafeNativeMethods.GetWindow(new HandleRef(this, Handle), NativeMethods.GW_CHILD); + if (hwnd != IntPtr.Zero) { + + // if it's a simple dropdown list, the first HWND is the list box. + // + if (DropDownStyle == ComboBoxStyle.Simple) { + childListBox = new ComboBoxChildNativeWindow(this, ChildWindowType.ListBox); + childListBox.AssignHandle(hwnd); + + // get the edits hwnd... + // + hwnd = UnsafeNativeMethods.GetWindow(new HandleRef(this, hwnd), NativeMethods.GW_HWNDNEXT); + } + + childEdit = new ComboBoxChildNativeWindow(this, ChildWindowType.Edit); + childEdit.AssignHandle(hwnd); + + // set the initial margin for combobox to be zero (this is also done whenever the font is changed). + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_SETMARGINS, + NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); + } + } + + bool found; + int dropDownWidth = Properties.GetInteger(PropDropDownWidth, out found); + if (found) { + SendMessage(NativeMethods.CB_SETDROPPEDWIDTH, dropDownWidth, 0); + } + + found = false; + int itemHeight = Properties.GetInteger(PropItemHeight, out found); + if (found) { + // someone has set the item height - update it + UpdateItemHeight(); + } + // Resize a simple style combobox on handle creation + // to respect the requested height. + // + if (DropDownStyle == ComboBoxStyle.Simple) { + Height = requestedHeight; + } + + //If HandleCreated set the AutoComplete... + //this function checks if the correct properties are set to enable AutoComplete feature on combobox. + try + { + fromHandleCreate = true; + SetAutoComplete(false, false); + } + finally + { + fromHandleCreate = false; + } + + + if (itemsCollection != null) { + foreach (object item in itemsCollection) { + NativeAdd(item); + } + + // Now udpate the current selection. + // + if (selectedIndex >= 0) { + SendMessage(NativeMethods.CB_SETCURSEL, selectedIndex, 0); + UpdateText(); + selectedIndex = -1; + } + } + // NOTE: Setting SelectedIndex must be the last thing we do. See ASURT 73949. + + } + + /// + /// + /// We need to un-subclasses everything here. Inheriting classes should + /// not forget to call base.OnHandleDestroyed() + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + dropDownHandle = IntPtr.Zero; + if (Disposing) { + itemsCollection = null; + selectedIndex = -1; + } + else { + selectedIndex = SelectedIndex; + } + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// + /// This is the code that actually fires the drawItem event. Don't + /// forget to call base.onDrawItem() to ensure that drawItem events + /// are correctly fired at all other times. + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This is the code that actually fires the dropDown event. Don't + /// forget to call base.onDropDown() to ensure that dropDown events + /// are correctly fired at all other times. + /// + protected virtual void OnDropDown(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWN]; + if (handler != null) handler(this, e); + + if (AccessibilityImprovements.Level3) { + // Notify collapsed/expanded property change. + AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Collapsed, + UnsafeNativeMethods.ExpandCollapseState.Expanded); + + var accessibleObject = AccessibilityObject as ComboBoxUiaProvider; + if (accessibleObject != null) { + accessibleObject.SetComboBoxItemFocus(); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyDown(KeyEventArgs e) { + // Do Return/ESC handling + if (SystemAutoCompleteEnabled) { + if (e.KeyCode == Keys.Return) { + // Set SelectedIndex + NotifyAutoComplete(true); + } + else if ((e.KeyCode == Keys.Escape) && autoCompleteDroppedDown) { + // Fire TextChanged Only + NotifyAutoComplete(false); + } + autoCompleteDroppedDown = false; + } + + // Base Handling + base.OnKeyDown(e); + } + + /// + /// + /// Key press event handler. Overridden to close up the combo box when the + /// user presses RETURN or ESCAPE. + /// + /// + protected override void OnKeyPress(KeyPressEventArgs e) { + + base.OnKeyPress(e); + + //return when dropped down already fires commit. + if (!e.Handled && (e.KeyChar == (char)(int)Keys.Return || e.KeyChar == (char)(int)Keys.Escape) + && DroppedDown) { + dropDown = false; + if (FormattingEnabled) { + //Set the Text which would Compare the WindowText with the TEXT and change SelectedIndex. + Text = WindowText; + SelectAll(); + e.Handled = false; + } + else { + DroppedDown = false; + e.Handled = true; + } + } + } + + /// + /// + /// This is the code that actually fires the measuereItem event. Don't + /// forget to call base.onMeasureItem() to ensure that measureItem + /// events are correctly fired at all other times. + /// + protected virtual void OnMeasureItem(MeasureItemEventArgs e) { + MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; + if (handler != null) handler(this, e); + } + + /// + /// + /// If we have the style set to popup show mouse over + /// + protected override void OnMouseEnter(EventArgs e) { + base.OnMouseEnter(e); + MouseIsOver = true; + } + /// + /// + /// If we have the style set to popup show mouse over + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + MouseIsOver = false; + } + + /// + /// This internal helper allows us to call the committed function multiple times without worrying about double firing. + /// + private void OnSelectionChangeCommittedInternal(EventArgs e) { + //There are cases where if we disable the combo while in this event handler, it sends the message again. + //This is a recursion gaurd to ensure we only send one commit per user action. + if (allowCommit) { + try { + allowCommit = false; + OnSelectionChangeCommitted(e); + } + finally { + allowCommit = true; + } + } + } + + /// + /// + /// This is the code that actually fires the SelectionChangeCommitted event. + /// Don't forget to call base.OnSelectionChangeCommitted() to ensure + /// that SelectionChangeCommitted events are correctly fired at all other times. + /// + protected virtual void OnSelectionChangeCommitted(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELECTIONCHANGECOMMITTED]; + if (handler != null) handler(this, e); + + // The user selects a list item or selects an item and then closes the list. + // It indicates that the user's selection is to be processed but should not + // be focused after closing the list. + if (dropDown) { + dropDownWillBeClosed = true; + } + } + + /// + /// + /// This is the code that actually fires the selectedIndexChanged event. + /// Don't forget to call base.onSelectedIndexChanged() to ensure + /// that selectedIndexChanged events are correctly fired at all other times. + /// + protected override void OnSelectedIndexChanged(EventArgs e) { + base.OnSelectedIndexChanged(e); + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; + if (handler != null) handler(this, e); + + if (dropDownWillBeClosed) { + // This is after-closing selection - do not focus on the list item + // and reset the state to announce the selections later. + dropDownWillBeClosed = false; + } + else if (AccessibilityImprovements.Level3) { + var accessibleObject = AccessibilityObject as ComboBoxUiaProvider; + if (accessibleObject != null) { + + // Announce DropDown- and DropDownList-styled ComboBox item selection using keyboard + // in case when Level 3 is enabled and DropDown is not in expanded state. Simple-styled + // ComboBox selection is announced by TextProvider. + if (DropDownStyle == ComboBoxStyle.DropDownList || DropDownStyle == ComboBoxStyle.DropDown) { + if (dropDown) { + accessibleObject.SetComboBoxItemFocus(); + } + + accessibleObject.SetComboBoxItemSelection(); + } + } + } + + // set the position in the dataSource, if there is any + // we will only set the position in the currencyManager if it is different + // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) + // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls + // into the backEnd. We do not need to do that. + // + // don't change the position if SelectedIndex is -1 because this indicates a selection not from the list. + if (this.DataManager != null && DataManager.Position != SelectedIndex) { + //read this as "if everett or (whidbey and selindex is valid)" + if (!FormattingEnabled || SelectedIndex != -1) { + this.DataManager.Position = this.SelectedIndex; + } + } + } + + /// + protected override void OnSelectedValueChanged(EventArgs e) { + base.OnSelectedValueChanged(e); + selectedValueChangedFired = true; + } + + /// + /// + /// This is the code that actually fires the selectedItemChanged event. + /// Don't forget to call base.onSelectedItemChanged() to ensure + /// that selectedItemChanged events are correctly fired at all other times. + /// + protected virtual void OnSelectedItemChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDITEMCHANGED]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This is the code that actually fires the DropDownStyleChanged event. + /// + protected virtual void OnDropDownStyleChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWNSTYLE]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This method is called by the parent control when any property + /// changes on the parent. This can be overriden by inheriting + /// classes, however they must call base.OnParentPropertyChanged. + /// + protected override void OnParentBackColorChanged(EventArgs e) { + base.OnParentBackColorChanged(e); + if (DropDownStyle == ComboBoxStyle.Simple) Invalidate(); + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + ResetHeightCache(); + + if (this.AutoCompleteMode == AutoCompleteMode.None) { + UpdateControl(true); + } + else { + //we always will recreate the handle when autocomplete mode is on + RecreateHandle(); + } + CommonProperties.xClearPreferredSizeCache(this); + } + + + private void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) { + if (AutoCompleteSource == AutoCompleteSource.CustomSource) + { + if (AutoCompleteCustomSource.Count == 0) + { + SetAutoComplete(true, true /*recreate handle*/); + } + else + { + SetAutoComplete(true, false); + } + + } + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + UpdateControl(false); + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnForeColorChanged(EventArgs e) { + base.OnForeColorChanged(e); + UpdateControl(false); + } + + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnGotFocus(EventArgs e) { + if (!canFireLostFocus) { + base.OnGotFocus(e); + canFireLostFocus = true; + } + } + + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLostFocus(EventArgs e) { + if (canFireLostFocus) { + if (this.AutoCompleteMode != AutoCompleteMode.None && + this.AutoCompleteSource == AutoCompleteSource.ListItems && + this.DropDownStyle == ComboBoxStyle.DropDownList) { + this.MatchingText = ""; + } + base.OnLostFocus(e); + canFireLostFocus = false; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnTextChanged(EventArgs e) { + if (SystemAutoCompleteEnabled) { + string text = this.Text; + + // Prevent multiple TextChanges... + if (text != this.lastTextChangedValue) { + // Need to still fire a TextChanged + base.OnTextChanged(e); + + // Save the new value + this.lastTextChangedValue = text; + } + } + else { + // Call the base + base.OnTextChanged(e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnValidating(CancelEventArgs e) { + if (SystemAutoCompleteEnabled) { + // Handle AutoComplete notification + NotifyAutoComplete(); + } + + // Call base + base.OnValidating(e); + } + + + private void UpdateControl(bool recreate) { + //clear the pref height cache + ResetHeightCache(); + + if (IsHandleCreated) { + if (DropDownStyle == ComboBoxStyle.Simple && recreate) { + // Control forgets to add a scrollbar. See ASURT 19113. + RecreateHandle(); + } + else { + UpdateItemHeight(); + // Force everything to repaint. See ASURT 19113. + InvalidateEverything(); + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (DropDownStyle == ComboBoxStyle.Simple) { + // simple style combo boxes have more painting problems than you can shake a stick at (#19113) + InvalidateEverything(); + } + } + + /// + protected override void OnDataSourceChanged(EventArgs e) { + if (Sorted) { + if (DataSource != null && Created) { + // we will only throw the exception when the control is already on the form. + Debug.Assert(DisplayMember.Equals(String.Empty), "if this list is sorted it means that dataSource was null when Sorted first became true. at that point DisplayMember had to be String.Empty"); + DataSource = null; + throw new InvalidOperationException(SR.GetString(SR.ComboBoxDataSourceWithSort)); + } + } + if (DataSource == null) { + BeginUpdate(); + SelectedIndex = -1; + Items.ClearInternal(); + EndUpdate(); + } + if (!Sorted && Created) + base.OnDataSourceChanged(e); + RefreshItems(); + } + + /// + protected override void OnDisplayMemberChanged(EventArgs e) { + base.OnDisplayMemberChanged(e); + + // bug 63005: when we change the displayMember, we need to refresh the + // items collection + // + RefreshItems(); + } + + /// + /// + /// This event is fired when the dropdown portion of the combobox is hidden. + /// + protected virtual void OnDropDownClosed(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWNCLOSED]; + if (handler != null) handler(this, e); + + if (AccessibilityImprovements.Level3) { + // Need to announce the focus on combo-box with new selected value on drop-down close. + // If do not do this focus in Level 3 stays on list item of unvisible list. + // This is necessary for DropDown style as edit should not take focus. + if (DropDownStyle == ComboBoxStyle.DropDown) { + AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + + // Notify Collapsed/expanded property change. + AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Expanded, + UnsafeNativeMethods.ExpandCollapseState.Collapsed); + + // Collapsing the DropDown, so reset the flag. + dropDownWillBeClosed = false; + } + } + + + /// + /// + /// This event is fired when the edit portion of a combobox is about to display altered text. + /// This event is NOT fired if the TEXT property is programatically changed. + /// + protected virtual void OnTextUpdate(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_TEXTUPDATE]; + if (handler != null) handler(this, e); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) { + if (this.AutoCompleteMode != AutoCompleteMode.None && + this.AutoCompleteSource == AutoCompleteSource.ListItems && + this.DropDownStyle == ComboBoxStyle.DropDownList && + InterceptAutoCompleteKeystroke(m)) { + return true; + } + else { + return base.ProcessKeyEventArgs(ref m); + } + } + + private void ResetHeightCache() { + prefHeightCache = -1; + } + /// + /// + /// Reparses the objects, getting new text strings for them. + /// + /// + protected override void RefreshItems() { + + // Save off the selection and the current collection. + // + int selectedIndex = SelectedIndex; + ObjectCollection savedItems = itemsCollection; + + itemsCollection = null; + + + object[] newItems = null; + + // if we have a dataSource and a DisplayMember, then use it + // to populate the Items collection + // + if (this.DataManager != null && this.DataManager.Count != -1) { + newItems = new object[this.DataManager.Count]; + for (int i = 0; i < newItems.Length; i++) { + newItems[i] = this.DataManager[i]; + } + } + else if (savedItems != null) { + newItems = new object[savedItems.Count]; + savedItems.CopyTo(newItems, 0); + } + BeginUpdate(); + try + { + // Clear the items. + // + if (IsHandleCreated) + { + NativeClear(); + } + // Store the current list of items + // + if (newItems != null) + { + Items.AddRangeInternal(newItems); + } + if (this.DataManager != null) + { + // put the selectedIndex in sync w/ the position in the dataManager + this.SelectedIndex = this.DataManager.Position; + } + else + { + SelectedIndex = selectedIndex; + } + } + finally + { + EndUpdate(); + } + } + + /// + /// + /// Reparses the object at the given index, getting new text string for it. + /// + /// + protected override void RefreshItem(int index) { + Items.SetItemInternal(index, Items[index]); + } + + /// + /// Release the ChildWindow object by un-subclassing the child edit and + /// list controls and freeing the root of the ChildWindow object. + /// + private void ReleaseChildWindow() { + + if (childEdit != null) { + + // We do not use UI Automation provider for child edit, so do not need to release providers. + childEdit.ReleaseHandle(); + childEdit = null; + } + + if (childListBox != null) { + + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + if (AccessibilityImprovements.Level3) { + ReleaseUiaProvider(childListBox.Handle); + } + + childListBox.ReleaseHandle(); + childListBox = null; + } + + if (childDropDown != null) { + + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + if (AccessibilityImprovements.Level3) { + ReleaseUiaProvider(childDropDown.Handle); + } + + childDropDown.ReleaseHandle(); + childDropDown = null; + } + } + + internal override void ReleaseUiaProvider(IntPtr handle) { + base.ReleaseUiaProvider(handle); + + var uiaProvider = AccessibilityObject as ComboBoxUiaProvider; + uiaProvider?.ResetListItemAccessibleObjects(); + } + + private void ResetAutoCompleteCustomSource() { + AutoCompleteCustomSource = null; + } + + private void ResetDropDownWidth() { + Properties.RemoveInteger(PropDropDownWidth); + } + + private void ResetItemHeight() { + Properties.RemoveInteger(PropItemHeight); + } + + public override void ResetText() { + base.ResetText(); + } + + /// + /// + /// Enables the AutoComplete feature for combobox depending on the properties set. + /// These properties are namely AutoCompleteMode, AutoCompleteSource and AutoCompleteCustomSource. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private void SetAutoComplete(bool reset, bool recreate) { + if (!IsHandleCreated || childEdit == null) { + return; + } + + if (AutoCompleteMode != AutoCompleteMode.None) { + if (!fromHandleCreate && recreate && IsHandleCreated) + { + //RecreateHandle to avoid Leak. + // notice the use of member variable to avoid re-entrancy + AutoCompleteMode backUpMode = this.AutoCompleteMode; + autoCompleteMode = AutoCompleteMode.None; + RecreateHandle(); + autoCompleteMode = backUpMode; + } + + if (AutoCompleteSource == AutoCompleteSource.CustomSource) { + if (AutoCompleteCustomSource != null) { + if (AutoCompleteCustomSource.Count == 0) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + else { + + if (stringSource == null) + { + stringSource = new StringSource(GetStringsForAutoComplete(AutoCompleteCustomSource)); + if (!stringSource.Bind(new HandleRef(this, childEdit.Handle), (int)AutoCompleteMode)) { + throw new ArgumentException(SR.GetString(SR.AutoCompleteFailure)); + } + } + else + { + stringSource.RefreshList(GetStringsForAutoComplete(AutoCompleteCustomSource)); + } + + } + } + } + else if (AutoCompleteSource == AutoCompleteSource.ListItems) { + if (DropDownStyle != ComboBoxStyle.DropDownList) { + if (itemsCollection != null) { + if (itemsCollection.Count == 0) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + else { + + if (stringSource == null) + { + stringSource = new StringSource(GetStringsForAutoComplete(Items)); + if (!stringSource.Bind(new HandleRef(this, childEdit.Handle), (int)AutoCompleteMode)) { + throw new ArgumentException(SR.GetString(SR.AutoCompleteFailureListItems)); + } + } + else + { + stringSource.RefreshList(GetStringsForAutoComplete(Items)); + } + + } + } + } + else { + // Drop Down List special handling + Debug.Assert(DropDownStyle == ComboBoxStyle.DropDownList); + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + } + else { + try { + int mode = 0; + + if (AutoCompleteMode == AutoCompleteMode.Suggest) { + mode |= NativeMethods.AUTOSUGGEST | NativeMethods.AUTOAPPEND_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.Append) { + mode |= NativeMethods.AUTOAPPEND | NativeMethods.AUTOSUGGEST_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.SuggestAppend) { + mode |= NativeMethods.AUTOSUGGEST; + mode |= NativeMethods.AUTOAPPEND; + } + int ret = SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), (int)AutoCompleteSource | mode); + } + catch (SecurityException) { + // If we don't have full trust, degrade gracefully. Allow the control to + // function without auto-complete. Allow the app to continue running. + } + } + } + else if (reset) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + } + + /// + /// + /// Selects the text in the editable portion of the ComboBox at the + /// from the given start index to the given end index. + /// + public void Select(int start, int length) { + if (start < 0) { + throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidArgument, "start", start.ToString(CultureInfo.CurrentCulture))); + } + // the Length can be negative to support Selecting in the "reverse" direction.. + int end = start + length; + + // but end cannot be negative... this means Length is far negative... + if (end < 0) { + throw new ArgumentOutOfRangeException("length", SR.GetString(SR.InvalidArgument, "length", length.ToString(CultureInfo.CurrentCulture))); + } + + SendMessage(NativeMethods.CB_SETEDITSEL, 0, NativeMethods.Util.MAKELPARAM(start, end)); + } + + /// + /// + /// Selects all the text in the editable portion of the ComboBox. + /// + public void SelectAll() { + Select(0, Int32.MaxValue); + } + + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + // If we are changing height, store the requested height. + // Requested height is used if the style is changed to simple. + // (Bug fix #20966) + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) { + requestedHeight = height; + } + + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Performs the work of setting the specified items to the combobox + /// + protected override void SetItemsCore(IList value) { + BeginUpdate(); + Items.ClearInternal(); + Items.AddRangeInternal(value); + + // if the list changed, we want to keep the same selected index + // CurrencyManager will provide the PositionChanged event + // it will be provided before changing the list though... + if (this.DataManager != null) { + if (this.DataSource is ICurrencyManagerProvider) { + // Everett ListControl's had a bug where they would not fire + // OnSelectedValueChanged if their list of items were refreshed. + // We fix this post-Everett. + // However, for APPCOMPAT reasons, we only want to fix it when binding to + // Whidbey components. + // vsw 547279. + this.selectedValueChangedFired = false; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETCURSEL, DataManager.Position, 0); + } + else { + selectedIndex = DataManager.Position; + } + + // if set_SelectedIndexChanged did not fire OnSelectedValueChanged + // then we have to fire it ourselves, cos the list changed anyway + if (!selectedValueChangedFired) { + OnSelectedValueChanged(EventArgs.Empty); + selectedValueChangedFired = false; + } + } + + EndUpdate(); + } + + /// + protected override void SetItemCore(int index, object value) { + Items.SetItemInternal(index, value); + } + + private bool ShouldSerializeAutoCompleteCustomSource() { + return autoCompleteCustomSource != null && autoCompleteCustomSource.Count > 0; + } + + internal bool ShouldSerializeDropDownWidth() { + return (Properties.ContainsInteger(PropDropDownWidth)); + } + + /// + /// Indicates whether the itemHeight property should be persisted. + /// + internal bool ShouldSerializeItemHeight() { + return (Properties.ContainsInteger(PropItemHeight)); + } + + /// + /// + /// Determines if the Text property needs to be persisted. + /// + internal override bool ShouldSerializeText() { + return SelectedIndex == -1 && base.ShouldSerializeText(); + } + + /// + /// + /// Provides some interesting info about this control in String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Items.Count: " + ((itemsCollection == null) ? (0).ToString(CultureInfo.CurrentCulture) : itemsCollection.Count.ToString(CultureInfo.CurrentCulture)); + } + + /// + /// + /// + private void UpdateDropDownHeight() { + if (dropDownHandle != IntPtr.Zero) { + //Now use the DropDownHeight property instead of calculating the Height... + int height = DropDownHeight; + if (height == DefaultDropDownHeight) { + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + int count = Math.Min(Math.Max(itemCount, 1), maxDropDownItems); + height = (ItemHeight * count + 2); + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, dropDownHandle), NativeMethods.NullHandleRef, 0, 0, DropDownWidth, height, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER); + } + } + + /// + /// Manufactures a MeasureItemEventArgs for each item in the list to simulate + /// the combobox requesting the info. This gives the effect of allowing the + /// measureitem info to be updated at anytime. + /// + /// + private void UpdateItemHeight() { + if (!IsHandleCreated) { + // VSWhidbey 156992: if we don't create control here we report item heights incorrectly later on. + CreateControl(); + } + if (DrawMode == DrawMode.OwnerDrawFixed) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, -1, ItemHeight); + SendMessage(NativeMethods.CB_SETITEMHEIGHT, 0, ItemHeight); + } + else if (DrawMode == DrawMode.OwnerDrawVariable) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, -1, ItemHeight); + Graphics graphics = CreateGraphicsInternal(); + for (int i = 0; i < Items.Count; i++) { + int original = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, i, 0)); + MeasureItemEventArgs mievent = new MeasureItemEventArgs(graphics, i, original); + OnMeasureItem(mievent); + if (mievent.ItemHeight != original) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, i, mievent.ItemHeight); + } + } + graphics.Dispose(); + } + } + + /// + /// Forces the text to be updated based on the current selection. + /// + /// + private void UpdateText() { + // V#45724 - Fire text changed for dropdown combos when the selection + // changes, since the text really does change. We've got + // to do this asynchronously because the actual edit text + // isn't updated until a bit later (V#51240). + // + + // QFE 2471: + // v1.0 - ComboBox::set_Text compared items w/ "value" and set the SelectedIndex accordingly + // v1.0 - null values can't correspond to String.Empty + // v1.0 - SelectedIndex == -1 corresponds to Text == String.Emtpy + // + // v1.1 - ComboBox::set_Text compares FilterItemOnProperty(item) w/ "value" and set the SelectedIndex accordingly + // v1.1 - null values correspond to String.Empty + // v1.1 - SelectedIndex == -1 corresponds to Text == null + string s = null; + + if (SelectedIndex != -1) { + object item = Items[SelectedIndex]; + if (item != null) { + s = GetItemText(item); + } + } + + Text = s; + + if (DropDownStyle == ComboBoxStyle.DropDown) { + if (childEdit != null && childEdit.Handle != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.WM_SETTEXT, IntPtr.Zero, s); + } + } + } + + /// + /// + /// + private void WmEraseBkgnd(ref Message m) { + if ((DropDownStyle == ComboBoxStyle.Simple) && ParentInternal != null) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + SafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rect); + Control p = ParentInternal; + Graphics graphics = Graphics.FromHdcInternal(m.WParam); + if (p != null) { + Brush brush = new SolidBrush(p.BackColor); + graphics.FillRectangle(brush, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + brush.Dispose(); + } + else { + graphics.FillRectangle(SystemBrushes.Control, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + } + graphics.Dispose(); + m.Result = (IntPtr)1; + return; + } + base.WndProc(ref m); + } + + /// + /// + /// + private void WmParentNotify(ref Message m) { + base.WndProc(ref m); + if (unchecked((int)(long)m.WParam) == (NativeMethods.WM_CREATE | 1000 << 16)) { + dropDownHandle = m.LParam; + + if (AccessibilityImprovements.Level3) { + // By some reason WmParentNotify with WM_DESTROY is not called before recreation. + // So release the old references here. + if (childDropDown != null) { + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + ReleaseUiaProvider(childListBox.Handle); + + childDropDown.ReleaseHandle(); + } + + childDropDown = new ComboBoxChildNativeWindow(this, ChildWindowType.DropDownList); + childDropDown.AssignHandle(dropDownHandle); + + // Reset the child list accessible object in case the the DDL is recreated. + // For instance when dialog window containging the ComboBox is reopened. + childListAccessibleObject = null; + } + } + } + + /// + /// Text change behavior. + /// Here are the window messages corresponding to each user event. + /// + /// DropDown (free text window): + /// Type in Text Window: + /// CBN_EDITUPDATE + /// CBN_EDITCHANGE + /// Down/Up Arrow + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// with text set to beginning of a valid item -- drop and close dropdown (selects that item) + /// CBN_DROPDOWN + /// CBN_CLOSEUP + /// Drop List, up/down arrow to select item, click away + /// CBN_DROPDOWN + /// CBN_SELCHANGE + /// CBN_CLOSEUP + /// Drop List, click on item + /// CBN_DROPDOWN + /// CBN_SELENDOK + /// CBN_CLOSEUP + /// CBN_SELCHANGE (text changes here via selected item) + /// + /// DropDownList (limited text window): + /// + /// Type text and arrow up/down: + /// CBN_SELENDOK (text already changed) + /// CBN_SELCHANGE + /// Drop List, up/down arrow to select item, click away + /// CBN_DROPDOWN + /// CBN_SELCHANGE + /// CBN_CLOSEUP + /// Drop List, click on item + /// CBN_DROPDOWN + /// CBN_SELENDOK + /// CBN_CLOSEUP + /// CBN_SELCHANGE + /// + /// Simple (listbox visible): + /// Type in Text Window: + /// CBN_EDITUPDATE + /// CBN_EDITCHANGE + /// Down/Up Arrow + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// Click on item + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// + /// + /// What we do is fire textchange events in these messages: + /// CBN_SELCHANGE + /// CBN_EDITCHANGE + /// CBN_CLOSEUP + /// + /// and we only actually call the real event if the Text is different than currentText. + /// currentText is never changed outside this method. + /// This internal version can be called from anywhere we might suspect text has changed + /// it's fairly safe to call anywhere. + + /// + /// + /// + private void WmReflectCommand(ref Message m) { + switch (NativeMethods.Util.HIWORD(m.WParam)) { + case NativeMethods.CBN_DBLCLK: + //OnDoubleClick(EventArgs.Empty); + break; + case NativeMethods.CBN_EDITUPDATE: + OnTextUpdate(EventArgs.Empty); + break; + case NativeMethods.CBN_CLOSEUP: + + OnDropDownClosed(EventArgs.Empty); + if (FormattingEnabled && Text != currentText && dropDown) { + OnTextChanged(EventArgs.Empty); + } + dropDown = false; + break; + case NativeMethods.CBN_DROPDOWN: + currentText = Text; + dropDown = true; + OnDropDown(EventArgs.Empty); + UpdateDropDownHeight(); + + break; + case NativeMethods.CBN_EDITCHANGE: + OnTextChanged(EventArgs.Empty); + break; + case NativeMethods.CBN_SELCHANGE: + UpdateText(); + OnSelectedIndexChanged(EventArgs.Empty); + break; + case NativeMethods.CBN_SELENDOK: + OnSelectionChangeCommittedInternal(EventArgs.Empty); + break; + } + } + + /// + /// + /// + private void WmReflectDrawItem(ref Message m) { + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + IntPtr oldPal = SetUpPalette(dis.hDC, false /*force*/, false /*realize*/); + try { + Graphics g = Graphics.FromHdcInternal(dis.hDC); + + try { + OnDrawItem(new DrawItemEventArgs(g, Font, Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom), + dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); + } + finally { + g.Dispose(); + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(this, dis.hDC), new HandleRef(null, oldPal), 0); + } + } + m.Result = (IntPtr)1; + } + + /// + /// + /// + private void WmReflectMeasureItem(ref Message m) { + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + // Determine if message was sent by a combo item or the combo edit field + if (DrawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { + Graphics graphics = CreateGraphicsInternal(); + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); + OnMeasureItem(mie); + mis.itemHeight = mie.ItemHeight; + graphics.Dispose(); + } + else { + // Message was sent by the combo edit field + mis.itemHeight = ItemHeight; + } + Marshal.StructureToPtr(mis, m.LParam, false); + m.Result = (IntPtr)1; + } + + /// + /// + /// The comboboxs window procedure. Inheritng classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the combo continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + // We don't want to fire the focus events twice - + // once in the combobox and once in the ChildWndProc. + case NativeMethods.WM_SETFOCUS: + try { + fireSetFocus = false; + base.WndProc(ref m); + } + + finally { + fireSetFocus = true; + } + break; + case NativeMethods.WM_KILLFOCUS: + try { + fireLostFocus = false; + base.WndProc(ref m); + // Nothing to see here... Just keep on walking... VSWhidbey 504477. + // Turns out that with Theming off, we don't get quite the same messages as with theming on. + + // With theming on we get a WM_MOUSELEAVE after a WM_KILLFOCUS even if you use the Tab key + // to move focus. Our response to WM_MOUSELEAVE causes us to repaint everything correctly. + + // With theming off, we do not get a WM_MOUSELEAVE after a WM_KILLFOCUS, and since we don't have a childwndproc + // when we are a Flat DropDownList, we need to force a repaint. The easiest way to do this is to send a + // WM_MOUSELEAVE to ourselves, since that also sets up the right state. Or... at least the state is the same + // as with Theming on. + + // This is such a @#$(*&#@$ hack. + + if (!Application.RenderWithVisualStyles && GetStyle(ControlStyles.UserPaint) == false && this.DropDownStyle == ComboBoxStyle.DropDownList && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup)) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_MOUSELEAVE, 0, 0); + } + } + + finally { + fireLostFocus = true; + } + break; + case NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_CTLCOLORLISTBOX: + m.Result = InitializeDCForWmCtlColor(m.WParam, m.Msg); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + case NativeMethods.WM_PARENTNOTIFY: + WmParentNotify(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmReflectDrawItem(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: + WmReflectMeasureItem(ref m); + break; + case NativeMethods.WM_LBUTTONDOWN: + mouseEvents = true; + base.WndProc(ref m); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + Rectangle ClientRect = new Rectangle(r.left, r.top, r.right - r.left, r.bottom - r.top); + + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x, y); + pt = PointToScreen(pt); + //mouseEvents is used to keep the check that we get the WM_LBUTTONUP after + //WM_LBUTTONDOWN or WM_LBUTTONDBLBCLK + // combo box gets a WM_LBUTTONUP for focus change ... + // + if (mouseEvents && !ValidationCancelled) { + mouseEvents = false; + bool captured = Capture; + if (captured && ClientRect.Contains(pt)) { + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + base.WndProc(ref m); + } + else { + CaptureInternal = false; + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSELEAVE: + DefWndProc(ref m); + OnMouseLeaveInternal(EventArgs.Empty); + break; + + case NativeMethods.WM_PAINT: + if (GetStyle(ControlStyles.UserPaint) == false && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup)) { + + using (WindowsRegion dr = new WindowsRegion(FlatComboBoxAdapter.dropDownRect)) { + using (WindowsRegion wr = new WindowsRegion(this.Bounds)) { + + // Stash off the region we have to update (the base is going to clear this off in BeginPaint) + NativeMethods.RegionFlags updateRegionFlags = (NativeMethods.RegionFlags)SafeNativeMethods.GetUpdateRgn(new HandleRef(this, this.Handle), new HandleRef(this, wr.HRegion), true); + + dr.CombineRegion(wr, dr, RegionCombineMode.DIFF); + + Rectangle updateRegionBoundingRect = wr.ToRectangle(); + FlatComboBoxAdapter.ValidateOwnerDrawRegions(this, updateRegionBoundingRect); + // Call the base class to do its painting (with a clipped DC). + + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + IntPtr dc; + bool disposeDc = false; + if (m.WParam == IntPtr.Zero) { + dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, Handle), ref ps); + disposeDc = true; + } + else { + dc = m.WParam; + } + + using (DeviceContext mDC = DeviceContext.FromHdc(dc)) { + using (WindowsGraphics wg = new WindowsGraphics(mDC)) { + if (updateRegionFlags != NativeMethods.RegionFlags.ERROR) { + wg.DeviceContext.SetClip(dr); + } + m.WParam = dc; + DefWndProc(ref m); + if (updateRegionFlags != NativeMethods.RegionFlags.ERROR) { + wg.DeviceContext.SetClip(wr); + } + using (Graphics g = Graphics.FromHdcInternal(dc)) { + FlatComboBoxAdapter.DrawFlatCombo(this, g); + } + } + } + + if (disposeDc) { + UnsafeNativeMethods.EndPaint(new HandleRef(this, Handle), ref ps); + } + + } + return; + } + } + + base.WndProc(ref m); + break; + case NativeMethods.WM_PRINTCLIENT: + // all the fancy stuff we do in OnPaint has to happen again in OnPrint. + if (GetStyle(ControlStyles.UserPaint) == false && FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup) { + DefWndProc(ref m); + + if ((unchecked( (int) (long)m.LParam) & NativeMethods.PRF_CLIENT) == NativeMethods.PRF_CLIENT) { + if (GetStyle(ControlStyles.UserPaint) == false && FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup) { + using (Graphics g = Graphics.FromHdcInternal(m.WParam)) { + FlatComboBoxAdapter.DrawFlatCombo(this,g); + } + } + return; + } + } + base.WndProc(ref m); + return; + case NativeMethods.WM_SETCURSOR: + base.WndProc(ref m); + break; + + case NativeMethods.WM_SETFONT: + //(bug 119265) + if (Width == 0) { + suppressNextWindosPos = true; + } + base.WndProc(ref m); + break; + + + case NativeMethods.WM_WINDOWPOSCHANGED: + if (!suppressNextWindosPos) { + base.WndProc(ref m); + } + suppressNextWindosPos = false; + break; + + case NativeMethods.WM_NCDESTROY: + base.WndProc(ref m); + ReleaseChildWindow(); + break; + + default: + if (m.Msg == NativeMethods.WM_MOUSEENTER) { + DefWndProc(ref m); + OnMouseEnterInternal(EventArgs.Empty); + break; + } + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + [ComVisible(true)] + internal class ComboBoxChildNativeWindow : NativeWindow { + private ComboBox _owner; + private InternalAccessibleObject _accessibilityObject; + private ChildWindowType _childWindowType; + + public ComboBoxChildNativeWindow(ComboBox comboBox, ChildWindowType childWindowType) { + _owner = comboBox; + _childWindowType = childWindowType; + } + + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_GETOBJECT: + WmGetObject(ref m); + return; + case NativeMethods.WM_MOUSEMOVE: + if (_childWindowType == ChildWindowType.DropDownList) { + + // Need to track the selection change via mouse over to + // raise focus changed event for the items. Monitoring + // item change in setters does not guarantee that focus + // is properly announced. + object before = _owner.SelectedItem; + DefWndProc(ref m); + object after = _owner.SelectedItem; + if (before != after) { + (_owner.AccessibilityObject as ComboBoxUiaProvider).SetComboBoxItemFocus(); + } + } else { + _owner.ChildWndProc(ref m); + } + break; + default: + if (_childWindowType == ChildWindowType.DropDownList) { + DefWndProc(ref m); // Drop Down window should behave by its own. + } else { + _owner.ChildWndProc(ref m); + } + break; + } + } + + private ChildAccessibleObject GetChildAccessibleObject(ChildWindowType childWindowType) { + if (childWindowType == ChildWindowType.Edit) { + return _owner.ChildEditAccessibleObject; + } + else if (childWindowType == ChildWindowType.ListBox || childWindowType == ChildWindowType.DropDownList) { + return _owner.ChildListAccessibleObject; + } + + return new ChildAccessibleObject(_owner, Handle); + } + + private void WmGetObject(ref Message m) { + if (AccessibilityImprovements.Level3 && m.LParam == (IntPtr)NativeMethods.UiaRootObjectId && + // Managed UIAutomation providers are supplied for child list windows but not for the child edit window. + // Child list accessibility object provides all necessary patterns and UIAutomation notifications, + // so there is no need to native provider supplement. + // Child edit accessibility object has only partial support of edit box accessibility, most of the patterns + // and notifications for child edit window are supplied by native providers, so here is no need to + // override root UIA object for child edit window. + (_childWindowType == ChildWindowType.ListBox || _childWindowType == ChildWindowType.DropDownList)) { + AccessibleObject uiaProvider = GetChildAccessibleObject(_childWindowType); + + // If the requested object identifier is UiaRootObjectId, + // we should return an UI Automation provider using the UiaReturnRawElementProvider function. + InternalAccessibleObject internalAccessibleObject; + IntSecurity.UnmanagedCode.Assert(); + try { + internalAccessibleObject = new InternalAccessibleObject(uiaProvider); + } + finally { + CodeAccessPermission.RevertAssert(); + } + m.Result = UnsafeNativeMethods.UiaReturnRawElementProvider( + new HandleRef(this, Handle), + m.WParam, + m.LParam, + internalAccessibleObject); + + return; + } + + // See "How to Handle WM_GETOBJECT" in MSDN + // + if (NativeMethods.OBJID_CLIENT == unchecked((int)(long)m.LParam)) { + + // Get the IAccessible GUID + // + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + + // Get an Lresult for the accessibility Object for this control + // + IntPtr punkAcc; + try { + AccessibleObject wfAccessibleObject = null; + UnsafeNativeMethods.IAccessibleInternal iacc = null; + + if (_accessibilityObject == null) { + IntSecurity.UnmanagedCode.Assert(); + try { + wfAccessibleObject = AccessibilityImprovements.Level3 + ? GetChildAccessibleObject(_childWindowType) + : new ChildAccessibleObject(_owner, Handle); + _accessibilityObject = new InternalAccessibleObject(wfAccessibleObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + iacc = (UnsafeNativeMethods.IAccessibleInternal)_accessibilityObject; + + // Obtain the Lresult + // + punkAcc = Marshal.GetIUnknownForObject(iacc); + + IntSecurity.UnmanagedCode.Assert(); + try { + m.Result = UnsafeNativeMethods.LresultFromObject(ref IID_IAccessible, m.WParam, new HandleRef(this, punkAcc)); + } + finally { + CodeAccessPermission.RevertAssert(); + Marshal.Release(punkAcc); + } + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.RichControlLresult), e); + } + } + else { // m.lparam != OBJID_CLIENT, so do default message processing + DefWndProc(ref m); + } + } + } + + private sealed class ItemComparer : System.Collections.IComparer { + private ComboBox comboBox; + + public ItemComparer(ComboBox comboBox) { + this.comboBox = comboBox; + } + + public int Compare(object item1, object item2) { + if (item1 == null) { + if (item2 == null) + return 0; //both null, then they are equal + + return -1; //item1 is null, but item2 is valid (greater) + } + if (item2 == null) + return 1; //item2 is null, so item 1 is greater + + String itemName1 = comboBox.GetItemText(item1); + String itemName2 = comboBox.GetItemText(item2); + + CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; + return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); + } + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class ObjectCollection : IList { + + private ComboBox owner; + private ArrayList innerList; + private IComparer comparer; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(ComboBox owner) { + this.owner = owner; + } + + private IComparer Comparer { + get { + if (comparer == null) { + comparer = new ItemComparer(owner); + } + return comparer; + } + } + + private ArrayList InnerList { + get { + if (innerList == null) { + innerList = new ArrayList(); + } + return innerList; + } + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count { + get { + return InnerList.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + + public int Add(object item) + { + owner.CheckNoDataSource(); + int index = AddInternal(item); + if (owner.UpdateNeeded() && owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + return index; + } + + + private int AddInternal(object item) { + + if (item == null) { + throw new ArgumentNullException("item"); + } + int index = -1; + if (!owner.sorted) + { + InnerList.Add(item); + } + else + { + index = InnerList.BinarySearch(item, Comparer); + if (index < 0) + { + index = ~index; // getting the index of the first element that is larger than the search value + } + + Debug.Assert(index>=0 && index <= InnerList.Count, "Wrong index for insert"); + InnerList.Insert(index, item); + } + bool successful = false; + + try { + if (owner.sorted) { + if (owner.IsHandleCreated) { + owner.NativeInsert(index, item); + } + } + else { + index = InnerList.Count-1; + if (owner.IsHandleCreated) { + owner.NativeAdd(item); + } + } + successful = true; + } + finally { + if (!successful) { + InnerList.Remove(item); + } + } + + return index; + } + + /// + /// + int IList.Add(object item) { + return Add(item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(object[] items) { + owner.CheckNoDataSource(); + owner.BeginUpdate(); + try + { + AddRangeInternal(items); + } + finally + { + owner.EndUpdate(); + } + } + + internal void AddRangeInternal(IList items) { + + if (items == null) + { + throw new ArgumentNullException("items"); + } + foreach (object item in items) { + // adding items one-by-one for performance (especially for sorted combobox) + // we can not rely on ArrayList.Sort since its worst case complexity is n*n + // AddInternal is based on BinarySearch and ensures n*log(n) complexity + AddInternal(item); + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + + /// + /// + /// Retrieves the item with the specified index. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual object this[int index] { + get { + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + return InnerList[index]; + } + set { + owner.CheckNoDataSource(); + SetItemInternal(index, value); + } + } + + /// + /// + /// Removes all items from the ComboBox. + /// + public void Clear() { + owner.CheckNoDataSource(); + ClearInternal(); + } + + internal void ClearInternal() { + + if (owner.IsHandleCreated) { + owner.NativeClear(); + } + + InnerList.Clear(); + owner.selectedIndex = -1; + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, true /*recreateHandle*/); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object value) { + return IndexOf(value) != -1; + } + + /// + /// + /// Copies the ComboBox Items collection to a destination array. + /// + public void CopyTo(object[] destination, int arrayIndex) { + InnerList.CopyTo(destination, arrayIndex); + } + + /// + /// + void ICollection.CopyTo(Array destination, int index) { + InnerList.CopyTo(destination, index); + } + + /// + /// + /// Returns an enumerator for the ComboBox Items collection. + /// + public IEnumerator GetEnumerator() { + return InnerList.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerList.IndexOf(value); + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public void Insert(int index, object item) { + owner.CheckNoDataSource(); + + if (item == null) { + throw new ArgumentNullException("item"); + } + + if (index < 0 || index > InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + // If the combo box is sorted, then nust treat this like an add + // because we are going to twiddle the index anyway. + // + if (owner.sorted) { + Add(item); + } + else { + InnerList.Insert(index, item); + if (owner.IsHandleCreated) { + + bool successful = false; + + try { + owner.NativeInsert(index, item); + successful = true; + } + finally { + if (successful) { + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + else { + InnerList.RemoveAt(index); + } + } + } + } + } + + /// + /// + /// Removes an item from the ComboBox at the given index. + /// + public void RemoveAt(int index) { + owner.CheckNoDataSource(); + + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.IsHandleCreated) { + owner.NativeRemoveAt(index); + } + + InnerList.RemoveAt(index); + if (!owner.IsHandleCreated && index < owner.selectedIndex) { + owner.selectedIndex--; + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + + /// + /// + /// Removes the given item from the ComboBox, provided that it is + /// actually in the list. + /// + public void Remove(object value) { + + int index = InnerList.IndexOf(value); + + if (index != -1) { + RemoveAt(index); + } + } + + internal void SetItemInternal(int index, object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + InnerList[index] = value; + + // If the native control has been created, and the display text of the new list item object + // is different to the current text in the native list item, recreate the native list item... + if (owner.IsHandleCreated) { + bool selected = (index == owner.SelectedIndex); + + if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { + owner.NativeRemoveAt(index); + owner.NativeInsert(index, value); + if (selected) { + owner.SelectedIndex = index; + owner.UpdateText(); + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + else { + // NEW - FOR COMPATIBILITY REASONS + // Minimum compatibility fix for VSWhidbey 377287/444903 + if (selected) { + owner.OnSelectedItemChanged(EventArgs.Empty); //we do this because set_SelectedIndex does this. (for consistency) + owner.OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + } + + } // end ObjectCollection + + /// + /// + [ComVisible(true)] + public class ChildAccessibleObject : AccessibleObject { + + ComboBox owner; + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public ChildAccessibleObject(ComboBox owner, IntPtr handle) { + Debug.Assert(owner != null && owner.Handle != IntPtr.Zero, "ComboBox's handle hasn't been created"); + + this.owner = owner; + UseStdAccessibleObjects(handle); + } + + /// + public override string Name { + get { + return owner.AccessibilityObject.Name; + } + } + } + + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ComboBoxAccessibleObject : ControlAccessibleObject { + + private const int COMBOBOX_ACC_ITEM_INDEX = 1; + + public ComboBoxAccessibleObject(Control ownerControl) + : base(ownerControl) { + } + + internal override string get_accNameInternal(object childID) { + this.ValidateChildID(ref childID); + + if (childID != null && ((int)childID) == COMBOBOX_ACC_ITEM_INDEX) { + return this.Name; + } + else { + return base.get_accNameInternal(childID); + } + } + + internal override string get_accKeyboardShortcutInternal(object childID) { + this.ValidateChildID(ref childID); + if (childID != null && ((int)childID) == COMBOBOX_ACC_ITEM_INDEX) { + return this.KeyboardShortcut; + } else { + return base.get_accKeyboardShortcutInternal(childID); + } + } + } + + /// + /// + [ComVisible(true)] + internal class ComboBoxExAccessibleObject : ComboBoxAccessibleObject { + + private ComboBox ownerItem = null; + + private void ComboBoxDefaultAction(bool expand) { + if (ownerItem.DroppedDown != expand) { + ownerItem.DroppedDown = expand; + } + } + + public ComboBoxExAccessibleObject(ComboBox ownerControl) + : base(ownerControl) { + ownerItem = ownerControl; + } + + internal override bool IsIAccessibleExSupported() { + if (ownerItem != null) { + return true; + } + return base.IsIAccessibleExSupported(); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + if (ownerItem.DropDownStyle == ComboBoxStyle.Simple) { + return false; + } + return true; + } + else { + if (patternId == NativeMethods.UIA_ValuePatternId) { + if (ownerItem.DropDownStyle == ComboBoxStyle.DropDownList) { + // NOTE: Initially the Value pattern is disabled for DropDownList in managed code, + // but despite of this MSAA provides true (when Level < 3). When UIA mode is enabled, + // Value pattern becomes disabled for the DropDownList and brings inconsistency. + // At this time keeping 'return false;' commented out and preserving Value pattern + // enabled in all cases: Level < 3 (by MSAA) and Level3 (by UIA). + // return false; + return AccessibilityImprovements.Level3; + } + return true; + } + } + return base.IsPatternSupported(patternId); + } + + internal override int[] RuntimeId { + get { + if (ownerItem != null) { + // we need to provide a unique ID + // others are implementing this in the same manner + // first item is static - 0x2a (RuntimeIDFirstItem) + // second item can be anything, but here it is a hash + + var runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)ownerItem.Handle; + runtimeId[2] = ownerItem.GetHashCode(); + return runtimeId; + } + + return base.RuntimeId; + } + } + + internal override object GetPropertyValue(int propertyID) { + + switch (propertyID) { + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId: + return (object)this.IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + return (object)this.IsPatternSupported(NativeMethods.UIA_ValuePatternId); + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override void Expand() { + ComboBoxDefaultAction(true); + } + + internal override void Collapse() { + ComboBoxDefaultAction(false); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return ownerItem.DroppedDown == true ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + } + + /// + /// Represents the ComboBox item accessible object. + /// + [ComVisible(true)] + internal class ComboBoxItemAccessibleObject : AccessibleObject { + + private ComboBox _owningComboBox; + private object _owningItem; + private IAccessible _systemIAccessible; + + /// + /// Initializes new instance of ComboBox item accessible object. + /// + /// The owning ComboBox. + /// The owning ComboBox item. + public ComboBoxItemAccessibleObject(ComboBox owningComboBox, object owningItem) { + _owningComboBox = owningComboBox; + _owningItem = owningItem; + + _systemIAccessible = _owningComboBox.ChildListAccessibleObject.GetSystemIAccessibleInternal(); + } + + /// + /// Gets the ComboBox Item bounds. + /// + public override Rectangle Bounds { + get { + var listAccessibleObject = _owningComboBox.ChildListAccessibleObject; + int currentIndex = GetCurrentIndex(); + + var parentRect = listAccessibleObject.BoundingRectangle; + int left = parentRect.Left; + int top = parentRect.Top + _owningComboBox.ItemHeight * currentIndex; + int width = parentRect.Width; + int height = _owningComboBox.ItemHeight; + + return new Rectangle(left, top, width, height); + } + } + + /// + /// Gets the ComboBox item default action. + /// + public override string DefaultAction { + get { + return _systemIAccessible.accDefaultAction[GetChildId()]; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return _owningComboBox.ChildListAccessibleObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + int currentIndex = GetCurrentIndex(); + var comboBoxChildListUiaProvider = _owningComboBox.ChildListAccessibleObject as ComboBoxChildListUiaProvider; + if (comboBoxChildListUiaProvider != null) { + int itemsCount = comboBoxChildListUiaProvider.GetChildFragmentCount(); + int nextItemIndex = currentIndex + 1; + if (itemsCount > nextItemIndex) { + return comboBoxChildListUiaProvider.GetChildFragment(nextItemIndex); + } + } + break; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + currentIndex = GetCurrentIndex(); + comboBoxChildListUiaProvider = _owningComboBox.ChildListAccessibleObject as ComboBoxChildListUiaProvider; + if (comboBoxChildListUiaProvider != null) { + var itemsCount = comboBoxChildListUiaProvider.GetChildFragmentCount(); + int previousItemIndex = currentIndex - 1; + if (previousItemIndex >= 0) { + return comboBoxChildListUiaProvider.GetChildFragment(previousItemIndex); + } + } + + break; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningComboBox.AccessibilityObject; + } + } + + private int GetCurrentIndex() { + return _owningComboBox.Items.IndexOf(_owningItem); + } + + internal override int GetChildId() { + return GetCurrentIndex() + 1; // Index is zero-based, Child ID is 1-based. + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return BoundingRectangle; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ListItemControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut ?? string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owningComboBox.Focused && _owningComboBox.SelectedIndex == GetCurrentIndex(); + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owningComboBox.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsControlElementPropertyId: + return true; + case NativeMethods.UIA_IsContentElementPropertyId: + return true; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_IsSelectionItemPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_SelectionItemIsSelectedPropertyId: + return (State & AccessibleStates.Selected) != 0; + case NativeMethods.UIA_SelectionItemSelectionContainerPropertyId: + return _owningComboBox.ChildListAccessibleObject; + + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the help text. + /// + public override string Help { + get { + return _systemIAccessible.accHelp[GetChildId()]; + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_InvokePatternId || + patternId == NativeMethods.UIA_SelectionItemPatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets or sets the accessible name. + /// + public override string Name { + get { + if (_owningComboBox != null) { + return _owningItem.ToString(); + } + + return base.Name; + } + + set { + base.Name = value; + } + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + return (AccessibleRole)_systemIAccessible.get_accRole(GetChildId()); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[4]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owningComboBox.Handle; + runtimeId[2] = _owningComboBox.GetListNativeWindowRuntimeIdPart(); + runtimeId[3] = _owningItem.GetHashCode(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + return (AccessibleStates)_systemIAccessible.get_accState(GetChildId()); + } + } + + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + + internal override void SelectItem() { + _owningComboBox.SelectedIndex = GetCurrentIndex(); + + SafeNativeMethods.InvalidateRect(new HandleRef(this, _owningComboBox.GetListHandle()), null, false); + } + + internal override void AddToSelection() { + SelectItem(); + } + + internal override void RemoveFromSelection() { + // Do nothing, C++ implementation returns UIA_E_INVALIDOPERATION 0x80131509 + } + + internal override bool IsItemSelected { + get { + return (State & AccessibleStates.Selected) != 0; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple ItemSelectionContainer { + get { + return _owningComboBox.ChildListAccessibleObject; + } + } + } + + internal class ComboBoxItemAccessibleObjectCollection : Hashtable { + + private ComboBox _owningComboBoxBox; + + public ComboBoxItemAccessibleObjectCollection(ComboBox owningComboBoxBox) { + _owningComboBoxBox = owningComboBoxBox; + } + + public override object this[object key] { + get { + if (!ContainsKey(key)) { + var itemAccessibleObject = new ComboBoxItemAccessibleObject(_owningComboBoxBox, key); + base[key] = itemAccessibleObject; + } + + return base[key]; + } + + set { + base[key] = value; + } + } + } + + /// + /// ComboBox control accessible object for AccessibilityImprovements of Level 3 with UI Automation provider functionality. + /// This inherits from the base ComboBoxExAccessibleObject and ComboBoxAccessibleObject to have all base functionality. + /// + [ComVisible(true)] + internal class ComboBoxUiaProvider : ComboBoxExAccessibleObject { + private ComboBoxChildDropDownButtonUiaProvider _dropDownButtonUiaProvider; + private ComboBoxItemAccessibleObjectCollection _itemAccessibleObjects; + private ComboBox _owningComboBox; + + /// + /// Initializes new instance of ComboBoxUiaProvider. + /// + /// The owning ComboBox control. + public ComboBoxUiaProvider(ComboBox owningComboBox) : base(owningComboBox) { + _owningComboBox = owningComboBox; + _itemAccessibleObjects = new ComboBoxItemAccessibleObjectCollection(owningComboBox); + } + + /// + /// Gets the collection of item accessible objects. + /// + public ComboBoxItemAccessibleObjectCollection ItemAccessibleObjects { + get { + return _itemAccessibleObjects; + } + } + + /// + /// Gets the DropDown button accessible object. (UI Automation provider) + /// + public ComboBoxChildDropDownButtonUiaProvider DropDownButtonUiaProvider { + get { + return (_dropDownButtonUiaProvider ?? new ComboBoxChildDropDownButtonUiaProvider(_owningComboBox, _owningComboBox.Handle)); + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild) { + return GetChildFragment(0); + } + else if (direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd) { + if (hwnd == _owningComboBox.childEdit.Handle) { + return _owningComboBox.ChildEditAccessibleObject; + } + else if ( + hwnd == _owningComboBox.childListBox.Handle || + hwnd == _owningComboBox.dropDownHandle) { + return _owningComboBox.ChildListAccessibleObject; + } + + return null; + } + + /// + /// Gets the accessible child corresponding to the specified index. + /// + /// The child index. + /// The accessible child. + /// + /// GetChild method should be unchanged to not break the MSAA scenarios. + /// + internal AccessibleObject GetChildFragment(int index) { + if (_owningComboBox.DropDownStyle == ComboBoxStyle.DropDownList) { + if (index == 0) { + return _owningComboBox.ChildTextAccessibleObject; + } + + index--; + } + + if (index == 0 && _owningComboBox.DropDownStyle != ComboBoxStyle.Simple) { + return DropDownButtonUiaProvider; + } + + return null; + } + + /// + /// Gets the number of children belonging to an accessible object. + /// + /// The number of children. + /// + /// GetChildCount method should be unchanged to not break the MSAA scenarios. + /// + internal int GetChildFragmentCount() { + int childFragmentCount = 0; + + if (_owningComboBox.DropDownStyle == ComboBoxStyle.DropDownList) { + childFragmentCount++; // Text instead of edit for style is DropDownList but not DropDown. + } + + if (_owningComboBox.DropDownStyle != ComboBoxStyle.Simple) { + childFragmentCount++; // DropDown button. + } + + return childFragmentCount; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ComboBoxControlTypeId; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owningComboBox.Focused; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _owningComboBox.Handle; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal void ResetListItemAccessibleObjects() { + _itemAccessibleObjects.Clear(); + } + + internal void SetComboBoxItemFocus() { + var selectedItem = _owningComboBox.SelectedItem; + if (selectedItem == null) { + return; + } + + var itemAccessibleObject = ItemAccessibleObjects[selectedItem] as ComboBoxItemAccessibleObject; + if (itemAccessibleObject != null) { + itemAccessibleObject.SetFocus(); + } + } + + internal void SetComboBoxItemSelection() { + var selectedItem = _owningComboBox.SelectedItem; + if (selectedItem == null) { + return; + } + + var itemAccessibleObject = ItemAccessibleObjects[selectedItem] as ComboBoxItemAccessibleObject; + if (itemAccessibleObject != null) { + itemAccessibleObject.RaiseAutomationEvent(NativeMethods.UIA_SelectionItem_ElementSelectedEventId); + } + } + + internal override void SetFocus() { + base.SetFocus(); + + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + + /// + /// Represents the ComboBox's child (inner) edit native window control accessible object with UI Automation provider functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildEditUiaProvider : ChildAccessibleObject { + private const string COMBO_BOX_EDIT_AUTOMATION_ID = "1001"; + + private ComboBox _owner; + private IntPtr _handle; + + /// + /// Initializes new instance of ComboBoxChildEditUiaProvider. + /// + /// The ComboBox owning control. + /// The child edit native window handle. + public ComboBoxChildEditUiaProvider(ComboBox owner, IntPtr childEditControlhandle) : base(owner, childEditControlhandle) { + _owner = owner; + _handle = childEditControlhandle; + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + Debug.WriteLine("Edit parent " + _owner.AccessibilityObject.GetPropertyValue(NativeMethods.UIA_ControlTypePropertyId)); + return _owner.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + if (_owner.DropDownStyle == ComboBoxStyle.Simple) { + return null; + } + + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // DropDown button is next; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + var firstComboBoxChildFragment = comboBoxUiaProvider.GetChildFragment(0); + if (RuntimeId != firstComboBoxChildFragment.RuntimeId) { + return firstComboBoxChildFragment; + } + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_EditControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_AutomationIdPropertyId: + return COMBO_BOX_EDIT_AUTOMATION_ID; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _handle; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, _handle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override int ProviderOptions { + get { + return (int)UnsafeNativeMethods.ProviderOptions.ClientSideProvider; + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = GetHashCode(); + + return runtimeId; + } + } + } + + /// + /// Represents the ComboBox's child (inner) list native window control accessible object with UI Automation provider functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildListUiaProvider : ChildAccessibleObject { + private const string COMBO_BOX_LIST_AUTOMATION_ID = "1000"; + + private ComboBox _owningComboBox; + private IntPtr _childListControlhandle; + + /// + /// Initializes new instance of ComboBoxChildListUiaProvider. + /// + /// + /// + public ComboBoxChildListUiaProvider(ComboBox owningComboBox, IntPtr childListControlhandle) : base(owningComboBox, childListControlhandle) { + _owningComboBox = owningComboBox; + _childListControlhandle = childListControlhandle; + } + + /// + /// Return the child object at the given screen coordinates. + /// + /// X coordinate. + /// Y coordinate. + /// The accessible object of corresponding element in the provided coordinates. + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + if (AccessibilityImprovements.Level3) { + var systemIAccessible = GetSystemIAccessibleInternal(); + if (systemIAccessible != null) { + object result = systemIAccessible.accHitTest((int)x, (int)y); + if (result is int) { + int childId = (int)result; + return GetChildFragment(childId - 1); + } + else { + return null; + } + } + } + + return base.ElementProviderFromPoint(x, y); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetChildFragment(0); + case UnsafeNativeMethods.NavigateDirection.LastChild: + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningComboBox.AccessibilityObject; + } + } + + public AccessibleObject GetChildFragment(int index) { + if (index < 0 || index >= _owningComboBox.Items.Count) { + return null; + } + + var item = _owningComboBox.Items[index]; + var comboBoxUiaProvider = _owningComboBox.AccessibilityObject as ComboBoxUiaProvider; + return comboBoxUiaProvider.ItemAccessibleObjects[item] as AccessibleObject; + } + + public int GetChildFragmentCount() { + return _owningComboBox.Items.Count; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ListControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return false; // Narrator should keep the keyboard focus on th ComboBox itself but not on the DropDown. + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owningComboBox.Enabled; + case NativeMethods.UIA_AutomationIdPropertyId: + return COMBO_BOX_LIST_AUTOMATION_ID; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _childListControlhandle; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_IsSelectionPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_SelectionCanSelectMultiplePropertyId: + return CanSelectMultiple; + case NativeMethods.UIA_SelectionIsSelectionRequiredPropertyId: + return IsSelectionRequired; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return GetFocused(); + } + + public override AccessibleObject GetFocused() { + int selectedIndex = _owningComboBox.SelectedIndex; + return GetChildFragment(selectedIndex); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetSelection() { + int selectedIndex = _owningComboBox.SelectedIndex; + + AccessibleObject itemAccessibleObject = GetChildFragment(selectedIndex); + if (itemAccessibleObject != null) { + return new UnsafeNativeMethods.IRawElementProviderSimple[] { + itemAccessibleObject + }; + } + + return new UnsafeNativeMethods.IRawElementProviderSimple[0]; + } + + internal override bool CanSelectMultiple { + get { + return false; + } + } + + internal override bool IsSelectionRequired { + get { + return true; + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_SelectionPatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, _childListControlhandle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owningComboBox.Handle; + runtimeId[2] = _owningComboBox.GetListNativeWindowRuntimeIdPart(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Focusable; + if (_owningComboBox.Focused) { + state |= AccessibleStates.Focused; + } + + return state; + } + } + } + + /// + /// Represents the ComboBox's child text (is used instead of inner Edit when style is DropDownList but not DropDown) accessible object. + /// + [ComVisible(true)] + internal class ComboBoxChildTextUiaProvider : AccessibleObject { + + private const int COMBOBOX_TEXT_ACC_ITEM_INDEX = 1; + + private ComboBox _owner; + + /// + /// Initializes new instance of ComboBoxChildTextUiaProvider. + /// + /// The owning ComboBox control. + public ComboBoxChildTextUiaProvider(ComboBox owner) { + _owner = owner; + } + + /// + /// Gets the bounds. + /// + public override Rectangle Bounds { + get { + return _owner.AccessibilityObject.Bounds; + } + } + + /// + /// Gets the child ID. + /// + /// The child ID. + internal override int GetChildId() { + return COMBOBOX_TEXT_ACC_ITEM_INDEX; + } + + /// + /// Gets or sets the accessible Name of ComboBox's child text element. + /// + public override string Name { + get { + return _owner.AccessibilityObject.Name ?? string.Empty; + } + set { + // Do nothing. + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return _owner.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // DropDown button is next; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + var firstComboBoxChildFragment = comboBoxUiaProvider.GetChildFragment(0); + if (RuntimeId != firstComboBoxChildFragment.RuntimeId) { + return firstComboBoxChildFragment; + } + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_TextControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[5]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owner.Handle; + runtimeId[2] = _owner.GetHashCode(); + runtimeId[3] = GetHashCode(); + runtimeId[4] = GetChildId(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Focusable; + if (_owner.Focused) { + state |= AccessibleStates.Focused; + } + + return state; + } + } + } + + /// + /// Represents the ComboBox child (inner) DropDown button accessible object with UI Automation functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildDropDownButtonUiaProvider : AccessibleObject { + + private const int COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX = 2; + private ComboBox _owner; + + /// + /// Initializes new instance of ComboBoxChildDropDownButtonUiaProvider. + /// + /// The owning ComboBox control. + /// The owning ComboBox control's handle. + public ComboBoxChildDropDownButtonUiaProvider(ComboBox owner, IntPtr comboBoxControlhandle) { + _owner = owner; + UseStdAccessibleObjects(comboBoxControlhandle); + } + + /// + /// Gets or sets the accessible Name of ComboBox's child DropDown button. ("Open" or "Close" depending on stat of the DropDown) + /// + public override string Name { + get { + return get_accNameInternal(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + set { + var systemIAccessible = GetSystemIAccessibleInternal(); + systemIAccessible.set_accName(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX, value); + } + } + + /// + /// Gets the DropDown button bounds. + /// + public override Rectangle Bounds { + get { + int left; + int top; + int width; + int height; + var systemIAccessible = GetSystemIAccessibleInternal(); + systemIAccessible.accLocation(out left, out top, out width, out height, COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + return new Rectangle(left, top, width, height); + } + } + + /// + /// Gets the DropDown button default action. + /// + public override string DefaultAction { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accDefaultAction[COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX]; + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent) { + return _owner.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.PreviousSibling) { + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // Text or edit is previous; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the child accessible object ID. + /// + /// The child accessible object ID. + internal override int GetChildId() { + return COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return BoundingRectangle; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ButtonControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the help text. + /// + public override string Help { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accHelp[COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX]; + } + } + + /// + /// Gets the keyboard shortcut. + /// + public override string KeyboardShortcut { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.get_accKeyboardShortcut(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_InvokePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleRole)systemIAccessible.get_accRole(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[5]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owner.Handle; + runtimeId[2] = _owner.GetHashCode(); + + // Made up constant from MSAA proxy. When MSAA proxy is used as an accessibility provider, + // the similar Runtime ID is returned (for consistency purpose) + const int generatedRuntimeId = 61453; + runtimeId[3] = generatedRuntimeId; + runtimeId[4] = COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX; + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleStates)systemIAccessible.get_accState(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + } + + /// + /// This subclasses an autocomplete window so that we can determine if control is inside the AC wndproc. + /// + private sealed class ACNativeWindow : NativeWindow { + static internal int inWndProcCnt; + //this hashtable can contain null for those ACWindows we find, but are sure are not ours. + static private Hashtable ACWindows = new Hashtable(); + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") + ] + internal ACNativeWindow(IntPtr acHandle) { + Debug.Assert(!ACWindows.ContainsKey(acHandle)); + this.AssignHandle(acHandle); + ACWindows.Add(acHandle, this); + UnsafeNativeMethods.EnumChildWindows(new HandleRef(this, acHandle), + new NativeMethods.EnumChildrenCallback(ACNativeWindow.RegisterACWindowRecursive), + NativeMethods.NullHandleRef); + } + + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + private static bool RegisterACWindowRecursive(IntPtr handle, IntPtr lparam) { + if (!ACWindows.ContainsKey(handle)) { + ACNativeWindow newAC = new ACNativeWindow(handle); + } + return true; + } + + internal bool Visible { + get { + return SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle)); + } + } + + static internal bool AutoCompleteActive { + get { + if (inWndProcCnt>0) { + return true; + } + foreach(object o in ACWindows.Values) { + ACNativeWindow window = o as ACNativeWindow; + if (window != null && window.Visible) { + return true; + } + } + return false; + } + } + + protected override void WndProc(ref Message m) { + inWndProcCnt++; + try { + base.WndProc(ref m); + } + finally { + inWndProcCnt--; + } + + if (m.Msg == NativeMethods.WM_NCDESTROY) { + Debug.Assert(ACWindows.ContainsKey(this.Handle)); + ACWindows.Remove(this.Handle); //so we do not leak ac windows. + } + } + + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + internal static void RegisterACWindow(IntPtr acHandle, bool subclass) { + if (subclass && ACWindows.ContainsKey(acHandle)) { + if (ACWindows[acHandle] == null) { + ACWindows.Remove(acHandle); //if an external handle got destroyed, dont let it stop us. + } + } + + if (!ACWindows.ContainsKey(acHandle)) { + if (subclass) { + ACNativeWindow newAC = new ACNativeWindow(acHandle); + } + else { + ACWindows.Add(acHandle, null); + } + } + } + + /// + /// This method clears out null entries so we get a clean BEFORE and AFTER snapshot + /// null entries are ACWindows that belong to someone else. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] // Perfectly acceptible when dealing with PropertyDescriptors + internal static void ClearNullACWindows() { + ArrayList nulllist = new ArrayList(); + foreach (DictionaryEntry e in ACWindows) { + if (e.Value == null) { + nulllist.Add(e.Key); + } + } + foreach (IntPtr handle in nulllist) { + ACWindows.Remove(handle); + } + } + } + + /// + /// This finds all autcomplete windows that belong to the active thread. + /// + private class AutoCompleteDropDownFinder { + private const int MaxClassName = 256; + private const string AutoCompleteClassName = "Auto-Suggest Dropdown"; + bool shouldSubClass = false; //nonstatic + + internal void FindDropDowns() { + FindDropDowns(true); + } + + internal void FindDropDowns(bool subclass) { + if (!subclass) { + //generating a before snapshot -- lets lose the null handles + ACNativeWindow.ClearNullACWindows(); + } + // Look for a popped up dropdown + shouldSubClass = subclass; + UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), new NativeMethods.EnumThreadWindowsCallback(this.Callback), new HandleRef(null, IntPtr.Zero)); + } + + private bool Callback(IntPtr hWnd, IntPtr lParam) { + HandleRef hRef = new HandleRef(null, hWnd); + + // Check class name and see if it's visible + if (GetClassName(hRef) == AutoCompleteClassName) { + ACNativeWindow.RegisterACWindow(hRef.Handle, shouldSubClass); + } + + return true; + } + + static string GetClassName(HandleRef hRef) { + StringBuilder sb = new StringBuilder(MaxClassName); + UnsafeNativeMethods.GetClassName(hRef, sb, MaxClassName); + return sb.ToString(); + } + } + + + private FlatComboAdapter FlatComboBoxAdapter { + get { + FlatComboAdapter comboAdapter = Properties.GetObject(PropFlatComboAdapter) as FlatComboAdapter; + if (comboAdapter == null || !comboAdapter.IsValid(this)) { + comboAdapter = CreateFlatComboAdapterInstance(); + Properties.SetObject(PropFlatComboAdapter, comboAdapter); + } + return comboAdapter; + } + } + + internal virtual FlatComboAdapter CreateFlatComboAdapterInstance() { + return new FlatComboAdapter(this,/*smallButton=*/false); + } + + + internal class FlatComboAdapter { + Rectangle outerBorder; + Rectangle innerBorder; + Rectangle innerInnerBorder; + internal Rectangle dropDownRect; + Rectangle whiteFillRect; + Rectangle clientRect; + + RightToLeft origRightToLeft; // The combo box's RTL value when we were created + + private const int WhiteFillRectWidth = 5; // used for making the button look smaller than it is + + private static bool isScalingInitialized = false; + private static int OFFSET_2PIXELS = 2; + protected static int Offset2Pixels = OFFSET_2PIXELS; + + public FlatComboAdapter(ComboBox comboBox, bool smallButton) { + // adapter is re-created when combobox is resized, see IsValid method, thus we don't need to handle DPI changed explicitly + if ((!isScalingInitialized && DpiHelper.IsScalingRequired) || DpiHelper.EnableDpiChangedMessageHandling) { + Offset2Pixels = comboBox.LogicalToDeviceUnits(OFFSET_2PIXELS); + isScalingInitialized = true; + } + + clientRect = comboBox.ClientRectangle; + int dropDownButtonWidth = SystemInformation.GetHorizontalScrollBarArrowWidthForDpi(comboBox.deviceDpi); + outerBorder = new Rectangle(clientRect.Location, new Size(clientRect.Width - 1, clientRect.Height - 1)); + innerBorder = new Rectangle(outerBorder.X + 1, outerBorder.Y + 1, outerBorder.Width - dropDownButtonWidth - 2, outerBorder.Height - 2); + innerInnerBorder = new Rectangle(innerBorder.X + 1, innerBorder.Y + 1, innerBorder.Width - 2, innerBorder.Height - 2); + dropDownRect = new Rectangle(innerBorder.Right + 1, innerBorder.Y, dropDownButtonWidth, innerBorder.Height+1); + + + // fill in several pixels of the dropdown rect with white so that it looks like the combo button is thinner. + if (smallButton) { + whiteFillRect = dropDownRect; + whiteFillRect.Width = WhiteFillRectWidth; + dropDownRect.X += WhiteFillRectWidth; + dropDownRect.Width -= WhiteFillRectWidth; + } + + origRightToLeft = comboBox.RightToLeft; + + + if (origRightToLeft == RightToLeft.Yes) { + innerBorder.X = clientRect.Width - innerBorder.Right; + innerInnerBorder.X = clientRect.Width - innerInnerBorder.Right; + dropDownRect.X = clientRect.Width - dropDownRect.Right; + whiteFillRect.X = clientRect.Width - whiteFillRect.Right + 1; // since we're filling, we need to move over to the next px. + } + + } + + public bool IsValid(ComboBox combo) { + return (combo.ClientRectangle == clientRect && combo.RightToLeft == origRightToLeft); + } + + /// + /// + /// Paints over the edges of the combo box to make it appear flat. + /// + public virtual void DrawFlatCombo(ComboBox comboBox, Graphics g) { + if (comboBox.DropDownStyle == ComboBoxStyle.Simple){ + return; + } + + Color outerBorderColor = GetOuterBorderColor(comboBox); + Color innerBorderColor = GetInnerBorderColor(comboBox); + bool rightToLeft = comboBox.RightToLeft == RightToLeft.Yes; + + // draw the drop down + DrawFlatComboDropDown(comboBox, g, dropDownRect); + + // when we are disabled there is one line of color that seems to eek through if backcolor is set + // so lets erase it. + if (!LayoutUtils.IsZeroWidthOrHeight(whiteFillRect)) { + // fill in two more pixels with white so it looks smaller. + using (Brush b = new SolidBrush(innerBorderColor)) { + g.FillRectangle(b, whiteFillRect); + } + } + + + + // Draw the outer border + if (outerBorderColor.IsSystemColor) { + Pen outerBorderPen = SystemPens.FromSystemColor(outerBorderColor); + g.DrawRectangle(outerBorderPen, outerBorder); + if (rightToLeft) { + g.DrawRectangle(outerBorderPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(outerBorderPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + } + else { + using (Pen outerBorderPen = new Pen(outerBorderColor)) { + g.DrawRectangle(outerBorderPen, outerBorder); + if (rightToLeft) { + g.DrawRectangle(outerBorderPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(outerBorderPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + } + } + + // Draw the inner border + if (innerBorderColor.IsSystemColor) { + Pen innerBorderPen = SystemPens.FromSystemColor(innerBorderColor); + g.DrawRectangle(innerBorderPen, innerBorder); + g.DrawRectangle(innerBorderPen, innerInnerBorder); + } + else { + using (Pen innerBorderPen = new Pen(innerBorderColor)) { + g.DrawRectangle(innerBorderPen, innerBorder); + g.DrawRectangle(innerBorderPen, innerInnerBorder); + } + } + + + + // Draw a dark border around everything if we're in popup mode + if ((!comboBox.Enabled) ||(comboBox.FlatStyle == FlatStyle.Popup)) { + bool focused = comboBox.ContainsFocus || comboBox.MouseIsOver; + Color borderPenColor = GetPopupOuterBorderColor(comboBox, focused); + + using (Pen borderPen = new Pen(borderPenColor)) { + + Pen innerPen = (comboBox.Enabled) ? borderPen : SystemPens.Control; + + // around the dropdown + if (rightToLeft) { + g.DrawRectangle(innerPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(innerPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + + // around the whole combobox. + g.DrawRectangle(borderPen, outerBorder); + + } + } + + } + + /// + /// + /// Paints over the edges of the combo box to make it appear flat. + /// + + protected virtual void DrawFlatComboDropDown(ComboBox comboBox, Graphics g, Rectangle dropDownRect) { + + g.FillRectangle(SystemBrushes.Control, dropDownRect); + + Brush brush = (comboBox.Enabled) ? SystemBrushes.ControlText : SystemBrushes.ControlDark; + + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + if (origRightToLeft == RightToLeft.Yes) { + // if the width is odd - favor pushing it over one pixel left. + middle.X -= (dropDownRect.Width % 2); + } + else { + // if the width is odd - favor pushing it over one pixel right. + middle.X += (dropDownRect.Width % 2); + } + + g.FillPolygon(brush, new Point[] { + new Point(middle.X - Offset2Pixels, middle.Y - 1), + new Point(middle.X + Offset2Pixels + 1, middle.Y - 1), + new Point(middle.X, middle.Y + Offset2Pixels) + }); + } + + protected virtual Color GetOuterBorderColor(ComboBox comboBox) { + return (comboBox.Enabled) ? SystemColors.Window : SystemColors.ControlDark; + } + + protected virtual Color GetPopupOuterBorderColor(ComboBox comboBox, bool focused) { + if (!comboBox.Enabled) { + return SystemColors.ControlDark; + } + return (focused) ? SystemColors.ControlDark : SystemColors.Window; + } + + protected virtual Color GetInnerBorderColor(ComboBox comboBox) { + return (comboBox.Enabled) ? comboBox.BackColor : SystemColors.Control; + } + + // this eliminates flicker by removing the pieces we're going to paint ourselves from + // the update region. Note the UpdateRegionBox is the bounding box of the actual update region. + // this is just here so we can quickly eliminate rectangles that arent in the update region. + public void ValidateOwnerDrawRegions(ComboBox comboBox, Rectangle updateRegionBox) { + NativeMethods.RECT validRect; + if (comboBox != null) { return; } + Rectangle topOwnerDrawArea = new Rectangle(0,0,comboBox.Width, innerBorder.Top); + Rectangle bottomOwnerDrawArea = new Rectangle(0,innerBorder.Bottom,comboBox.Width, comboBox.Height-innerBorder.Bottom); + Rectangle leftOwnerDrawArea = new Rectangle(0,0,innerBorder.Left, comboBox.Height); + Rectangle rightOwnerDrawArea = new Rectangle(innerBorder.Right,0,comboBox.Width - innerBorder.Right,comboBox.Height); + + if (topOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(topOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (bottomOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(bottomOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (leftOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(leftOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (rightOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(rightOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + } + } + + /// + /// Represents the ComboBox child native window type. + /// + internal enum ChildWindowType { + ListBox, + Edit, + DropDownList + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComboBox.cs.back b/WindowsForms/Managed/System/WinForms/ComboBox.cs.back new file mode 100644 index 000000000..9cc9c274a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComboBox.cs.back @@ -0,0 +1,6140 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Runtime.Versioning; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + using System.Windows.Forms.ComponentModel; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using Microsoft.Win32; + using System.Reflection; + using System.Collections.Specialized; + using System.Text; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Displays an editing field and a list, allowing the user to select from the + /// list or to enter new text. Displays only the editing field until the user + /// explicitly displays the list. + /// + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("SelectedIndexChanged"), + DefaultProperty("Items"), + DefaultBindingProperty("Text"), + Designer("System.Windows.Forms.Design.ComboBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionComboBox) + ] + public class ComboBox : ListControl { + + private static readonly object EVENT_DROPDOWN = new object(); + private static readonly object EVENT_DRAWITEM = new object(); + private static readonly object EVENT_MEASUREITEM = new object(); + private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); + private static readonly object EVENT_SELECTIONCHANGECOMMITTED = new object(); + private static readonly object EVENT_SELECTEDITEMCHANGED = new object(); + private static readonly object EVENT_DROPDOWNSTYLE = new object(); + private static readonly object EVENT_TEXTUPDATE = new object(); + private static readonly object EVENT_DROPDOWNCLOSED = new object(); + + private static readonly int PropMaxLength = PropertyStore.CreateKey(); + private static readonly int PropItemHeight = PropertyStore.CreateKey(); + private static readonly int PropDropDownWidth = PropertyStore.CreateKey(); + private static readonly int PropDropDownHeight = PropertyStore.CreateKey(); + private static readonly int PropStyle = PropertyStore.CreateKey(); + private static readonly int PropDrawMode = PropertyStore.CreateKey(); + private static readonly int PropMatchingText = PropertyStore.CreateKey(); + private static readonly int PropFlatComboAdapter = PropertyStore.CreateKey(); + + private const int DefaultSimpleStyleHeight = 150; + private const int DefaultDropDownHeight = 106; + private const int AutoCompleteTimeout = 10000000; // 1 second timeout for resetting the MatchingText + private bool autoCompleteDroppedDown = false; + + private FlatStyle flatStyle = FlatStyle.Standard; + private int updateCount; + + //Timestamp of the last keystroke. Used for auto-completion + // in DropDownList style. + private long autoCompleteTimeStamp; + + private int selectedIndex = -1; // used when we don't have a handle. + private bool allowCommit = true; + + // When the style is "simple", the requested height is used + // for the actual height of the control. + // When the style is non-simple, the height of the control + // is determined by the OS. + // This fixes bug #20966 + private int requestedHeight; + + private ComboBoxChildNativeWindow childDropDown; + private ComboBoxChildNativeWindow childEdit; + private ComboBoxChildNativeWindow childListBox; + + private IntPtr dropDownHandle; + private ObjectCollection itemsCollection; + private short prefHeightCache = -1; + private short maxDropDownItems = 8; + private bool integralHeight = true; + private bool mousePressed; + private bool mouseEvents; + private bool mouseInEdit; + + private bool sorted; + private bool fireSetFocus = true; + private bool fireLostFocus = true; + private bool mouseOver; + private bool suppressNextWindosPos; + private bool canFireLostFocus; + + // When the user types a letter and drops the dropdown... + // the combobox itself auto-searches the matching item... + // and selects the item in the edit... + // thus changing the windowText... + // hence we should Fire the TextChanged event in such a scenario.. + // The string below is used for checking the window Text before and after the dropdown. + private string currentText = ""; + private string lastTextChangedValue; + private bool dropDown; + private AutoCompleteDropDownFinder finder = new AutoCompleteDropDownFinder(); + + private bool selectedValueChangedFired; + + /// + /// + /// This stores the value for the autocomplete mode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// + private AutoCompleteMode autoCompleteMode = AutoCompleteMode.None; + + /// + /// + /// This stores the value for the autoCompleteSource mode which can be one of the values + /// from AutoCompleteSource enum. + /// + private AutoCompleteSource autoCompleteSource = AutoCompleteSource.None; + + /// + /// + /// This stores the custom StringCollection required for the autoCompleteSource when its set to CustomSource. + /// + private AutoCompleteStringCollection autoCompleteCustomSource; + private StringSource stringSource; + private bool fromHandleCreate = false; + + private ComboBoxChildListUiaProvider childListAccessibleObject; + private ComboBoxChildEditUiaProvider childEditAccessibleObject; + private ComboBoxChildTextUiaProvider childTextAccessibleObject; + + // Indicates whether the dropdown list will be closed after + // selection (on getting CBN_SELENDOK notification) to prevent + // focusing on the list item after hiding the list. + private bool dropDownWillBeClosed = false; + + /// + /// + /// Creates a new ComboBox control. The default style for the combo is + /// a regular DropDown Combo. + /// + public ComboBox() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.UseTextForAccessibility | + ControlStyles.StandardClick, false); + + requestedHeight = DefaultSimpleStyleHeight; + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + } + + /// + /// + /// This is the AutoCompleteMode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// This property in conjunction with AutoCompleteSource enables the AutoComplete feature for ComboBox. + /// + [ + DefaultValue(AutoCompleteMode.None), + SRDescription(SR.ComboBoxAutoCompleteModeDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteMode AutoCompleteMode { + get { + return autoCompleteMode; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoCompleteMode.None, (int)AutoCompleteMode.SuggestAppend)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteMode)); + } + if (this.DropDownStyle == ComboBoxStyle.DropDownList && + this.AutoCompleteSource != AutoCompleteSource.ListItems && + value != AutoCompleteMode.None) { + throw new NotSupportedException(SR.GetString(SR.ComboBoxAutoCompleteModeOnlyNoneAllowed)); + } + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + bool resetAutoComplete = false; + if (autoCompleteMode != AutoCompleteMode.None && value == AutoCompleteMode.None) { + resetAutoComplete = true; + } + autoCompleteMode = value; + SetAutoComplete(resetAutoComplete, true); + } + } + + /// + /// + /// This is the AutoCompleteSource which can be one of the + /// values from AutoCompleteSource enumeration. + /// This property in conjunction with AutoCompleteMode enables the AutoComplete feature for ComboBox. + /// + [ + DefaultValue(AutoCompleteSource.None), + SRDescription(SR.ComboBoxAutoCompleteSourceDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteSource AutoCompleteSource { + get { + return autoCompleteSource; + } + set { + // FxCop: Avoid usage of Enum.IsDefined - this looks like an enum that could grow + if (!ClientUtils.IsEnumValid_NotSequential(value, (int)value, + (int)AutoCompleteSource.None, + (int)AutoCompleteSource.AllSystemSources, + (int)AutoCompleteSource.AllUrl, + (int)AutoCompleteSource.CustomSource, + (int)AutoCompleteSource.FileSystem, + (int)AutoCompleteSource.FileSystemDirectories, + (int)AutoCompleteSource.HistoryList, + (int)AutoCompleteSource.ListItems, + (int)AutoCompleteSource.RecentlyUsedList)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteSource)); + } + + if (this.DropDownStyle == ComboBoxStyle.DropDownList && + this.AutoCompleteMode != AutoCompleteMode.None && + value != AutoCompleteSource.ListItems) { + throw new NotSupportedException(SR.GetString(SR.ComboBoxAutoCompleteSourceOnlyListItemsAllowed)); + } + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + + // VSWhidbey 466300 + if (value != AutoCompleteSource.None && + value != AutoCompleteSource.CustomSource && + value != AutoCompleteSource.ListItems) + { + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Demand(); + } + + autoCompleteSource = value; + SetAutoComplete(false, true); + } + } + + /// + /// + /// This is the AutoCompleteCustomSource which is custom StringCollection used when the + /// AutoCompleteSource is CustomSource. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxAutoCompleteCustomSourceDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteStringCollection AutoCompleteCustomSource { + get { + if (autoCompleteCustomSource == null) { + autoCompleteCustomSource = new AutoCompleteStringCollection(); + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + return autoCompleteCustomSource; + } + set { + if (autoCompleteCustomSource != value) { + + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + + autoCompleteCustomSource = value; + + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + SetAutoComplete(false, true); + } + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + internal ChildAccessibleObject ChildEditAccessibleObject { + get { + if (childEditAccessibleObject == null) { + childEditAccessibleObject = new ComboBoxChildEditUiaProvider(this, childEdit.Handle); + } + + return childEditAccessibleObject; + } + } + + internal ChildAccessibleObject ChildListAccessibleObject { + get { + if (childListAccessibleObject == null) { + childListAccessibleObject = + new ComboBoxChildListUiaProvider(this, DropDownStyle == ComboBoxStyle.Simple ? childListBox.Handle : dropDownHandle); + } + + return childListAccessibleObject; + } + } + + internal AccessibleObject ChildTextAccessibleObject { + get { + if (childTextAccessibleObject == null) { + childTextAccessibleObject = new ComboBoxChildTextUiaProvider(this); + } + + return childTextAccessibleObject; + } + } + + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.CreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "COMBOBOX"; + cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.CBS_HASSTRINGS | NativeMethods.CBS_AUTOHSCROLL; + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + if (!integralHeight) cp.Style |= NativeMethods.CBS_NOINTEGRALHEIGHT; + + switch (DropDownStyle) { + case ComboBoxStyle.Simple: + cp.Style |= NativeMethods.CBS_SIMPLE; + break; + case ComboBoxStyle.DropDown: + cp.Style |= NativeMethods.CBS_DROPDOWN; + // Make sure we put the height back or we won't be able to size the dropdown! + cp.Height = PreferredHeight; + break; + case ComboBoxStyle.DropDownList: + cp.Style |= NativeMethods.CBS_DROPDOWNLIST; + // Comment above... + cp.Height = PreferredHeight; + break; + } + switch (DrawMode) { + + case DrawMode.OwnerDrawFixed: + cp.Style |= NativeMethods.CBS_OWNERDRAWFIXED; + break; + case DrawMode.OwnerDrawVariable: + cp.Style |= NativeMethods.CBS_OWNERDRAWVARIABLE; + break; + } + + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(121, PreferredHeight); + } + } + + /// + /// + /// The ListSource to consume as this ListBox's source of data. + /// When set, a user can not modify the Items collection. + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.ListControlDataSourceDescr) + ] + public new object DataSource { + get { + return base.DataSource; + } + set { + base.DataSource = value; + } + } + + + /// + /// + /// Retrieves the value of the DrawMode property. The DrawMode property + /// controls whether the control is drawn by Windows or by the user. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DrawMode.Normal), + SRDescription(SR.ComboBoxDrawModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public DrawMode DrawMode { + get { + bool found; + int drawMode = Properties.GetInteger(PropDrawMode, out found); + if (found) { + return (DrawMode)drawMode; + } + + return DrawMode.Normal; + } + set { + if (DrawMode != value) { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); + } + ResetHeightCache(); + Properties.SetInteger(PropDrawMode, (int)value); + RecreateHandle(); + } + } + } + + /// + /// + /// Returns the width of the drop down box in a combo box. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownWidthDescr) + ] + public int DropDownWidth { + get { + bool found; + int dropDownWidth = Properties.GetInteger(PropDropDownWidth, out found); + + if (found) { + return dropDownWidth; + } + else { + return Width; + } + } + + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("DropDownWidth", SR.GetString(SR.InvalidArgument, "DropDownWidth", (value).ToString(CultureInfo.CurrentCulture))); + } + if (Properties.GetInteger(PropDropDownWidth) != value) { + Properties.SetInteger(PropDropDownWidth, value); + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETDROPPEDWIDTH, value, 0); + } + + } + } + } + + /// + /// + /// Sets the Height of the drop down box in a combo box. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownHeightDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(106) + ] + public int DropDownHeight { + get { + bool found; + int dropDownHeight = Properties.GetInteger(PropDropDownHeight, out found); + if (found) { + return dropDownHeight; + } + else { + return DefaultDropDownHeight; + } + } + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("DropDownHeight", SR.GetString(SR.InvalidArgument, "DropDownHeight", (value).ToString(CultureInfo.CurrentCulture))); + } + if (Properties.GetInteger(PropDropDownHeight) != value) { + Properties.SetInteger(PropDropDownHeight, value); + + // The dropDownHeight is not reflected unless the + // combobox integralHeight == false.. + IntegralHeight = false; + } + } + } + + /// + /// + /// Indicates whether the DropDown of the combo is currently dropped down. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxDroppedDownDescr) + ] + public bool DroppedDown { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.CB_GETDROPPEDSTATE, 0, 0) ) != 0; + } + else { + return false; + } + } + + set { + + if (!IsHandleCreated) { + CreateHandle(); + } + + SendMessage(NativeMethods.CB_SHOWDROPDOWN, value ? -1 : 0, 0); + } + } + + /// + /// + /// + /// Gets or + /// sets + /// the flat style appearance of the button control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Standard), + Localizable(true), + SRDescription(SR.ComboBoxFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { + return flatStyle; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + flatStyle = value; + Invalidate(); + } + } + + /// + /// + /// Returns true if this control has focus. + /// + public override bool Focused { + get { + if (base.Focused) return true; + IntPtr focus = UnsafeNativeMethods.GetFocus(); + return focus != IntPtr.Zero && ((childEdit != null && focus == childEdit.Handle) || (childListBox != null && focus == childListBox.Handle)); + } + } + + /// + /// + /// + /// Gets or sets the foreground color of the control. + /// + /// + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// Indicates if the combo should avoid showing partial Items. If so, + /// then only full items will be displayed, and the list portion will be resized + /// to prevent partial items from being shown. Otherwise, they will be + /// shown + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ComboBoxIntegralHeightDescr) + ] + public bool IntegralHeight { + get { + return integralHeight; + } + + set { + if (integralHeight != value) { + integralHeight = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Returns the height of an item in the combo box. When drawMode is Normal + /// or OwnerDrawFixed, all items have the same height. When drawMode is + /// OwnerDrawVariable, this method returns the height that will be given + /// to new items added to the combo box. To determine the actual height of + /// an item, use the GetItemHeight() method with an integer parameter. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ComboBoxItemHeightDescr) + ] + public int ItemHeight { + get { + + DrawMode drawMode = DrawMode; + if (drawMode == DrawMode.OwnerDrawFixed || + drawMode == DrawMode.OwnerDrawVariable || + !IsHandleCreated) { + + bool found; + int itemHeight = Properties.GetInteger(PropItemHeight, out found); + if (found) { + return itemHeight; + } + else { + return FontHeight + 2; // bug (90774)+2 for the 1 pixel gap on each side (up and Bottom) of the Text. + } + } + + // Note that the above if clause deals with the case when the handle has not yet been created + Debug.Assert(IsHandleCreated, "Handle should be created at this point"); + + int h = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, 0, 0) ); + if (h == -1) { + throw new Win32Exception(); + } + return h; + } + + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture))); + } + + ResetHeightCache(); + + if (Properties.GetInteger(PropItemHeight) != value) { + Properties.SetInteger(PropItemHeight, value); + if (DrawMode != DrawMode.Normal) { + UpdateItemHeight(); + } + } + } + } + + /// + /// + /// Collection of the items contained in this ComboBox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + MergableProperty(false) + ] + public ObjectCollection Items { + get { + if (itemsCollection == null) { + itemsCollection = new ObjectCollection(this); + } + return itemsCollection; + } + } + + // Text used to match an item in the list when auto-completion + // is used in DropDownList style. + private string MatchingText { + get { + string matchingText = (string)Properties.GetObject(PropMatchingText); + return (matchingText == null) ? string.Empty : matchingText; + } + set { + if (value != null || this.Properties.ContainsObject(PropMatchingText)) { + Properties.SetObject(PropMatchingText, value); + } + } + } + + /// + /// + /// The maximum number of items to be shown in the dropdown portion + /// of the ComboBox. This number can be between 1 and 100. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(8), + Localizable(true), + SRDescription(SR.ComboBoxMaxDropDownItemsDescr) + ] + public int MaxDropDownItems { + get { + return maxDropDownItems; + } + set { + if (value < 1 || value > 100) { + throw new ArgumentOutOfRangeException("MaxDropDownItems", SR.GetString(SR.InvalidBoundArgument, "MaxDropDownItems", (value).ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture), (100).ToString(CultureInfo.CurrentCulture))); + } + maxDropDownItems = (short)value; + } + } + + public override Size MaximumSize { + get { return base.MaximumSize; } + set { + base.MaximumSize = new Size(value.Width, 0); + } + } + + public override Size MinimumSize { + get { return base.MinimumSize; } + set { + base.MinimumSize = new Size(value.Width, 0); + } + } + + /// + /// + /// The maximum length of the text the user may type into the edit control + /// of a combo box. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.ComboBoxMaxLengthDescr) + ] + public int MaxLength { + get { + return Properties.GetInteger(PropMaxLength); + } + set { + if (value < 0) value = 0; + if (MaxLength != value) { + Properties.SetInteger(PropMaxLength, value); + if (IsHandleCreated) SendMessage(NativeMethods.CB_LIMITTEXT, value, 0); + } + } + } + + /// + /// + /// If the mouse is over the combobox, draw selection rect. + /// + internal bool MouseIsOver { + get { return mouseOver; } + set { + if (mouseOver != value) { + mouseOver = value; + // Nothing to see here... Just keep on walking... VSWhidbey 504477. + // Turns out that with Theming off, we don't get quite the same messages as with theming on, so + // our drawing gets a little messed up. So in case theming is off, force a draw here. + if ((!ContainsFocus || !Application.RenderWithVisualStyles) && this.FlatStyle == FlatStyle.Popup) { + Invalidate(); + Update(); + } + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + + /// + /// + /// ApplySizeConstraints calls into this method when DropDownStyles is DropDown and DropDownList. + /// This causes PreferredSize to be bounded by PreferredHeight in these two cases only. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxPreferredHeightDescr) + ] + public int PreferredHeight { + get { + if (!FormattingEnabled) { + //see #465057 for why I did this + + //do preferred height the old broken way for everett apps + //we need this for compat reasons because (get this) + // (a) everett preferredheight was always wrong. + // (b) so, when combobox1.Size = actualdefaultsize was called, it would enter setboundscore + // (c) this updated requestedheight + // (d) if the user then changed the combo to simple style, the height did not change. + // We simply cannot match this behavior if preferredheight is corrected so that (b) never + // occurs. We simply do not know when Size was set. + + // So in whidbey, the behavior will be: + // (1) user uses default size = setting dropdownstyle=simple will revert to simple height + // (2) user uses nondefault size = setting dropdownstyle=simple will not change height from this value + + //In everett + // if the user manually sets Size = (121, 20) in code (usually height gets forced to 21), then he will see Whidey.(1) above + // user usually uses nondefault size and will experience whidbey.(2) above + + Size textSize = TextRenderer.MeasureText(LayoutUtils.TestString, this.Font, new Size(Int16.MaxValue, (int)(FontHeight * 1.25)), TextFormatFlags.SingleLine); + prefHeightCache = (short)(textSize.Height + SystemInformation.BorderSize.Height * 8 + Padding.Size.Height); + + return prefHeightCache ; + } + else { + // Normally we do this sort of calculation in GetPreferredSizeCore which has builtin + // caching, but in this case we can not because PreferredHeight is used in ApplySizeConstraints + // which is used by GetPreferredSize (infinite loop). + if (prefHeightCache < 0) { + Size textSize = TextRenderer.MeasureText(LayoutUtils.TestString, this.Font, new Size(Int16.MaxValue, (int)(FontHeight * 1.25)), TextFormatFlags.SingleLine); + + // For a "simple" style combobox, the preferred height depends on the + // number of items in the combobox. + if (DropDownStyle == ComboBoxStyle.Simple) { + int itemCount = Items.Count + 1; + prefHeightCache = (short)(textSize.Height * itemCount + SystemInformation.BorderSize.Height * 16 + Padding.Size.Height); + } + else { + // We do this old school rather than use SizeFromClientSize because CreateParams calls this + // method and SizeFromClientSize calls CreateParams (another infinite loop.) + prefHeightCache = (short)GetComboHeight(); + } + } + return prefHeightCache; + } + } + } + + + // VSWhidbey 194386 - ComboBox.PreferredHeight returns incorrect values + // This is translated from windows implementation. Since we cannot control the size + // of the combo box, we need to use the same calculation they do. + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // "0" is what Windows uses in all languages. + private int GetComboHeight() { + + int cyCombo = 0; + // Add on CYEDGE just for some extra space in the edit field/static item. + // It's really only for static text items, but we want static & editable + // controls to be the same height. + Size textExtent = Size.Empty; + + using (WindowsFont font = WindowsFont.FromFont(this.Font)) { + // this is the character that Windows uses to determine the extent + textExtent = WindowsGraphicsCacheManager.MeasurementGraphics.GetTextExtent("0", font); + } + + int dyEdit = textExtent.Height + SystemInformation.Border3DSize.Height; + + if (DrawMode != DrawMode.Normal) { + // This is an ownerdraw combo. Have the owner tell us how tall this + // item is. + dyEdit = ItemHeight; + } + + // Set the initial width to be the combo box rect. Later we will shorten it + // if there is a dropdown button. + Size fixedFrameBoderSize = SystemInformation.FixedFrameBorderSize; + cyCombo = 2 * fixedFrameBoderSize.Height + dyEdit; + return cyCombo; + } + + private string[] GetStringsForAutoComplete(IList collection) + { + if (collection is AutoCompleteStringCollection) + { + string[] strings = new string[AutoCompleteCustomSource.Count]; + for (int i = 0; i < AutoCompleteCustomSource.Count; i++) { + strings[i] = AutoCompleteCustomSource[i]; + } + return strings; + + } + else if (collection is ObjectCollection) + { + string[] strings = new string[itemsCollection.Count]; + for (int i = 0; i < itemsCollection.Count; i++) { + strings[i] = GetItemText(itemsCollection[i]); + } + return strings; + + } + return new string[0]; + } + + + /// + /// + /// The [zero based] index of the currently selected item in the combos list. + /// Note If the value of index is -1, then the ComboBox is + /// set to have no selection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedIndexDescr) + ] + public override int SelectedIndex { + get { + if (IsHandleCreated) { + + return unchecked( (int) (long)SendMessage(NativeMethods.CB_GETCURSEL, 0, 0) ); + } + else { + return selectedIndex; + } + } + set { + if (SelectedIndex != value) { + int itemCount = 0; + if (itemsCollection != null) { + itemCount = itemsCollection.Count; + } + + if (value < -1 || value >= itemCount) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETCURSEL, value, 0); + + } + else { + selectedIndex = value; + } + + UpdateText(); + + if (IsHandleCreated) { + OnTextChanged(EventArgs.Empty); + } + + OnSelectedItemChanged(EventArgs.Empty); + OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// The handle to the object that is currently selected in the + /// combos list. + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedItemDescr) + ] + public object SelectedItem { + get { + int index = SelectedIndex; + return (index == -1) ? null : Items[index]; + } + set { + int x = -1; + + if (itemsCollection != null) { + //bug (82115) + if (value != null) + x = itemsCollection.IndexOf(value); + else + SelectedIndex = -1; + } + + if (x != -1) { + SelectedIndex = x; + } + } + } + + /// + /// + /// The selected text in the edit component of the ComboBox. If the + /// ComboBox has ComboBoxStyle.DROPDOWNLIST, the return is an empty + /// string (""). + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedTextDescr) + ] + public string SelectedText { + get { + if (DropDownStyle == ComboBoxStyle.DropDownList) return ""; + return Text.Substring(SelectionStart, SelectionLength); + } + set { + if (DropDownStyle != ComboBoxStyle.DropDownList) { + //guard against null string, since otherwise we will throw an + //AccessViolation exception, which is bad + string str = (value == null ? "" : value); + CreateControl(); + if (IsHandleCreated) { + Debug.Assert(childEdit != null); + if (childEdit != null) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_REPLACESEL, NativeMethods.InvalidIntPtr, str); + } + } + } + } + } + + /// + /// + /// The length, in characters, of the selection in the editbox. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionLengthDescr) + ] + public int SelectionLength { + get { + int[] end = new int[] { 0 }; + int[] start = new int[] { 0 }; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETEDITSEL, start, end); + return end[0] - start[0]; + } + set { + // SelectionLength can be negtive... + Select(SelectionStart, value); + } + } + + /// + /// + /// The [zero-based] index of the first character in the current text selection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionStartDescr) + ] + public int SelectionStart { + get { + int[] value = new int[] { 0 }; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETEDITSEL, value, (int[])null); + return value[0]; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidArgument, "SelectionStart", value.ToString(CultureInfo.CurrentCulture))); + } + Select(value, SelectionLength); + } + } + + /// + /// + /// Indicates if the Combos list is sorted or not. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ComboBoxSortedDescr) + ] + public bool Sorted { + get { + return sorted; + } + set { + if (sorted != value) { + if (this.DataSource != null && value) { + throw new ArgumentException(SR.GetString(SR.ComboBoxSortWithDataSource)); + } + + sorted = value; + RefreshItems(); + SelectedIndex = -1; + } + } + } + + /// + /// + /// The type of combo that we are right now. The value would come + /// from the System.Windows.Forms.ComboBoxStyle enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ComboBoxStyle.DropDown), + SRDescription(SR.ComboBoxStyleDescr), + RefreshPropertiesAttribute(RefreshProperties.Repaint) + ] + public ComboBoxStyle DropDownStyle { + get { + bool found; + int style = Properties.GetInteger(PropStyle, out found); + if (found) { + return (ComboBoxStyle)style; + } + + return ComboBoxStyle.DropDown; + } + set { + if (DropDownStyle != value) { + + // verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ComboBoxStyle.Simple, (int)ComboBoxStyle.DropDownList)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ComboBoxStyle)); + } + + if (value == ComboBoxStyle.DropDownList && + this.AutoCompleteSource != AutoCompleteSource.ListItems && + this.AutoCompleteMode != AutoCompleteMode.None) { + this.AutoCompleteMode = AutoCompleteMode.None; + } + + // reset preferred height. + ResetHeightCache(); + + Properties.SetInteger(PropStyle, (int)value); + + if (IsHandleCreated) { + RecreateHandle(); + } + + OnDropDownStyleChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + Bindable(true) + ] + public override string Text { + get { + if (SelectedItem != null && !BindingFieldEmpty) { + + //preserve everett behavior if "formatting enabled == false" -- just return selecteditem text. + if (FormattingEnabled) { + string candidate = GetItemText(SelectedItem); + if (!String.IsNullOrEmpty(candidate)) { + if (String.Compare(candidate, base.Text, true, CultureInfo.CurrentCulture) == 0) { + return candidate; //for whidbey, if we only differ by case -- return the candidate; + } + } + } + else { + return FilterItemOnProperty(SelectedItem).ToString(); //heinous. + } + } + return base.Text; + } + set { + if (DropDownStyle == ComboBoxStyle.DropDownList && !IsHandleCreated && !String.IsNullOrEmpty(value) && FindStringExact(value) == -1) + return; + + base.Text = value; + object selectedItem = null; + + selectedItem = SelectedItem; + + if (!DesignMode) { + //bug <70650> Subhag removed 'value.Length == 0' check to handle String.Empty. + // + if (value == null) { + SelectedIndex = -1; + } + else if (value != null && + (selectedItem == null || (String.Compare(value, GetItemText(selectedItem), false, CultureInfo.CurrentCulture) != 0))) { + + int index = FindStringIgnoreCase(value); + + //we cannot set the index to -1 unless we want to do a hack and save/restore text + //because the native control will erase the text when we change the index to -1 + if (index != -1) { + SelectedIndex = index; + } + } + } + } + } + + + private int FindStringIgnoreCase(string value) { + //look for an exact match and then a case insensitive match if that fails. + int index = FindStringExact(value, -1, false); + + if (index == -1) { + index = FindStringExact(value, -1, true); + } + + return index; + } + + // Special AutoComplete notification handling + // If the text changed, this will fire TextChanged + // If it matches an item in the list, this will fire SIC and SVC + private void NotifyAutoComplete() { + NotifyAutoComplete(true); + } + + // Special AutoComplete notification handling + // If the text changed, this will fire TextChanged + // If it matches an item in the list, this will fire SIC and SVC + private void NotifyAutoComplete(bool setSelectedIndex) { + string text = this.Text; + bool textChanged = (text != this.lastTextChangedValue); + bool selectedIndexSet = false; + + if (setSelectedIndex) { + // Process return key. This is sent by the AutoComplete DropDown when a + // selection is made from the DropDown + // Check to see if the Text Changed. If so, at least fire a TextChanged + int index = FindStringIgnoreCase(text); + + if ((index != -1) && (index != SelectedIndex)) { + // We found a match, do the full monty + SelectedIndex = index; + + // Put the cursor at the end + SelectionStart = 0; + SelectionLength = text.Length; + + selectedIndexSet = true; + } + } + + //don't fire textch if we had set the selectedindex -- because it was already fired if so. + if (textChanged && !selectedIndexSet) { + // No match, just fire a TextChagned + OnTextChanged(EventArgs.Empty); + } + + // Save the new value + this.lastTextChangedValue = text; + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + // Returns true if using System AutoComplete + private bool SystemAutoCompleteEnabled { + get { + return ((this.autoCompleteMode != AutoCompleteMode.None) && (this.DropDownStyle != ComboBoxStyle.DropDownList)); + } + } + + // VSWhidbey 95691: Prevent this event from being displayed in the Property Grid. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_DRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWITEM, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownDescr)] + public event EventHandler DropDown { + add { + Events.AddHandler(EVENT_DROPDOWN, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWN, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] + public event MeasureItemEventHandler MeasureItem { + add { + Events.AddHandler(EVENT_MEASUREITEM, value); + UpdateItemHeight(); + } + remove { + Events.RemoveHandler(EVENT_MEASUREITEM, value); + UpdateItemHeight(); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectionChangeCommittedEventDescr)] + public event EventHandler SelectionChangeCommitted { + add { + Events.AddHandler(EVENT_SELECTIONCHANGECOMMITTED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTIONCHANGECOMMITTED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxDropDownStyleChangedDescr)] + public event EventHandler DropDownStyleChanged { + add { + Events.AddHandler(EVENT_DROPDOWNSTYLE, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWNSTYLE, value); + } + } + + /// + /// + /// ComboBox Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// This will fire the TextUpdate Event on the ComboBox. This events fires when the Combobox gets the + /// CBN_EDITUPDATE notification. + // + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnTextUpdateDescr)] + public event EventHandler TextUpdate { + add { + Events.AddHandler(EVENT_TEXTUPDATE, value); + } + remove { + Events.RemoveHandler(EVENT_TEXTUPDATE, value); + } + } + + + /// + /// + /// This will fire the DropDownClosed Event on the ComboBox. This events fires when the Combobox gets the + /// CBN_CLOSEUP notification. This happens when the DropDown closes. + // + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownClosedDescr)] + public event EventHandler DropDownClosed { + add { + Events.AddHandler(EVENT_DROPDOWNCLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_DROPDOWNCLOSED, value); + } + } + + /// + /// + /// Performs the work of adding the specified items to the combobox + /// + [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] + protected virtual void AddItemsCore(object[] value) { + int count = value == null ? 0 : value.Length; + if (count == 0) { + return; + } + + BeginUpdate(); + try { + Items.AddRangeInternal(value); + } + finally { + EndUpdate(); + } + } + + /// + /// + /// Disables redrawing of the combo box. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// combo box is deferred until the call to endUpdate(). + /// + public void BeginUpdate() { + updateCount++; + BeginUpdateInternal(); + } + + private void CheckNoDataSource() { + if (DataSource != null) { + throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); + } + } + + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ComboBoxUiaProvider(this); + } + else if (AccessibilityImprovements.Level1) { + return new ComboBoxExAccessibleObject(this); + } + else { + return new ComboBoxAccessibleObject(this); + } + } + + /// + /// + internal bool UpdateNeeded() + { + return (updateCount == 0); + } + + + /// + /// This procedure takes in the message, converts the Edit handle coordinates into Combo Box Coordinates + /// + /// + internal Point EditToComboboxMapping(Message m) { + if (childEdit == null) { + return new Point(0, 0); + } + // Get the Combox Rect ... + // + NativeMethods.RECT comboRectMid = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref comboRectMid); + // + //Get the Edit Rectangle... + // + NativeMethods.RECT editRectMid = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, childEdit.Handle), ref editRectMid); + + //get the delta + int comboXMid = NativeMethods.Util.SignedLOWORD(m.LParam) + (editRectMid.left - comboRectMid.left); + int comboYMid = NativeMethods.Util.SignedHIWORD(m.LParam) + (editRectMid.top - comboRectMid.top); + + return (new Point(comboXMid, comboYMid)); + + } + + + /// + /// Subclassed window procedure for the edit and list child controls of the + /// combo box. + /// + /// + private void ChildWndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_CHAR: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + case NativeMethods.WM_SYSCHAR: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyEventArgs(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + if (SystemAutoCompleteEnabled && !ACNativeWindow.AutoCompleteActive) { + finder.FindDropDowns(false); + } + + if (AutoCompleteMode != AutoCompleteMode.None) { + char keyChar = unchecked((char)(long)m.WParam); + if (keyChar == (char)(int)Keys.Escape) { + this.DroppedDown = false; + } + else if (keyChar == (char)(int)Keys.Return && this.DroppedDown) { + UpdateText(); + OnSelectionChangeCommittedInternal(EventArgs.Empty); + this.DroppedDown = false; + } + } + + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + break; + + case NativeMethods.WM_INPUTLANGCHANGE: + DefChildWndProc( ref m ); + break; + + case NativeMethods.WM_KEYUP: + case NativeMethods.WM_SYSKEYUP: + if (DropDownStyle == ComboBoxStyle.Simple && m.HWnd == childListBox.Handle) { + DefChildWndProc(ref m); + } + else { + if (PreProcessControlMessage(ref m) != PreProcessControlState.MessageProcessed) { + if (ProcessKeyMessage(ref m)) { + return; + } + DefChildWndProc(ref m); + } + } + if (SystemAutoCompleteEnabled && !ACNativeWindow.AutoCompleteActive) { + finder.FindDropDowns(); + } + + break; + case NativeMethods.WM_KILLFOCUS: + // Consider - If we dont' have a childwndproc, then we don't get here, so we don't + // update the cache. Do we need to? This happens when we have a DropDownList. + if (!DesignMode) { + OnImeContextStatusChanged( m.HWnd ); + } + + DefChildWndProc(ref m); + // We don't want to fire the focus events twice - + // once in the combobox and once here. + if (fireLostFocus) { + this.InvokeLostFocus(this, EventArgs.Empty); + } + + if (FlatStyle == FlatStyle.Popup) { + this.Invalidate(); + } + + break; + case NativeMethods.WM_SETFOCUS: + + // Consider - If we dont' have a childwndproc, then we don't get here, so we don't + // set the status. Do we need to? This happens when we have a DropDownList. + if (!DesignMode) { + ImeContext.SetImeStatus(CachedImeMode, m.HWnd); + } + + if (!HostedInWin32DialogManager) { + IContainerControl c = GetContainerControlInternal(); + if (c != null) { + ContainerControl container = c as ContainerControl; + if (container != null) { + if (!container.ActivateControlInternal(this, false)) { + return; + } + } + } + } + + DefChildWndProc(ref m); + + // We don't want to fire the focus events twice - + // once in the combobox and once here. + if (fireSetFocus) { + if (!DesignMode && (childEdit != null && m.HWnd == childEdit.Handle) && !LocalAppContextSwitches.EnableLegacyIMEFocusInComboBox) { + WmImeSetFocus(); + } + this.InvokeGotFocus(this, EventArgs.Empty); + } + + if (FlatStyle == FlatStyle.Popup) { + this.Invalidate(); + } + break; + + case NativeMethods.WM_SETFONT: + DefChildWndProc(ref m); + if (childEdit != null && m.HWnd == childEdit.Handle) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_SETMARGINS, + NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); + } + break; + case NativeMethods.WM_LBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptlc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, Ptlc.X, Ptlc.Y, 0)); + break; + + case NativeMethods.WM_MBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptmc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Middle, 1, Ptmc.X, Ptmc.Y, 0)); + break; + + case NativeMethods.WM_RBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //Set MouseEvents... + mousePressed = true; + mouseEvents = true; + CaptureInternal = true; + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptrc = EditToComboboxMapping(m); + OnMouseDown(new MouseEventArgs(MouseButtons.Right, 1, Ptrc.X, Ptrc.Y, 0)); + break; + + case NativeMethods.WM_LBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Ptl = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, Ptl.X, Ptl.Y, 0)); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + Rectangle ClientRect = new Rectangle(r.left, r.top, r.right - r.left, r.bottom - r.top); + // Get the mouse location + // + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x, y); + pt = PointToScreen(pt); + // combo box gets a WM_LBUTTONUP for focus change ... + // So check MouseEvents.... + if (mouseEvents && !ValidationCancelled) { + mouseEvents = false; + if (mousePressed) { + if (ClientRect.Contains(pt)) { + mousePressed = false; + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + else { + mousePressed = false; + mouseInEdit = false; + OnMouseLeave(EventArgs.Empty); + } + } + } + DefChildWndProc(ref m); + CaptureInternal = false; + + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + pt = EditToComboboxMapping(m); + + OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, pt.X, pt.Y, 0)); + break; + case NativeMethods.WM_MBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point P = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Middle, 1, P.X, P.Y, 0)); + break; + case NativeMethods.WM_RBUTTONDOWN: + mousePressed = true; + mouseEvents = true; + + //set the mouse capture .. this is the Child Wndproc.. + // Bug# 112108: If I set the capture=true here, the + // DefWndProc() never fires the WM_CONTEXTMENU that would show + // the default context menu. + // + if (this.ContextMenu != null || this.ContextMenuStrip != null) + CaptureInternal = true; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point Pt = EditToComboboxMapping(m); + + OnMouseDown(new MouseEventArgs(MouseButtons.Right, 1, Pt.X, Pt.Y, 0)); + break; + case NativeMethods.WM_MBUTTONUP: + mousePressed = false; + mouseEvents = false; + //set the mouse capture .. this is the Child Wndproc.. + // + CaptureInternal = false; + DefChildWndProc(ref m); + OnMouseUp(new MouseEventArgs(MouseButtons.Middle, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + break; + case NativeMethods.WM_RBUTTONUP: + mousePressed = false; + mouseEvents = false; + //set the mouse capture .. this is the Child Wndproc.. + // + if (this.ContextMenu != null) + CaptureInternal = false; + DefChildWndProc(ref m); + //the up gets fired from "Combo-box's WndPrc --- So Convert these Coordinates to Combobox coordianate... + // + Point ptRBtnUp = EditToComboboxMapping(m); + + OnMouseUp(new MouseEventArgs(MouseButtons.Right, 1, ptRBtnUp.X, ptRBtnUp.Y, 0)); + break; + + case NativeMethods.WM_CONTEXTMENU: + // Forward context menu messages to the parent control + if (this.ContextMenu != null || this.ContextMenuStrip != null) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_CONTEXTMENU, m.WParam, m.LParam); + } + else { + DefChildWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSEMOVE: + Point point = EditToComboboxMapping(m); + //Call the DefWndProc() so that mousemove messages get to the windows edit(112079) + // + DefChildWndProc(ref m); + OnMouseEnterInternal(EventArgs.Empty); + OnMouseMove(new MouseEventArgs(MouseButtons, 0, point.X, point.Y, 0)); + break; + + case NativeMethods.WM_SETCURSOR: + if (Cursor != DefaultCursor && childEdit != null && m.HWnd == childEdit.Handle && NativeMethods.Util.LOWORD(m.LParam) == NativeMethods.HTCLIENT) { + Cursor.CurrentInternal = Cursor; + } + else { + DefChildWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSELEAVE: + DefChildWndProc(ref m); + OnMouseLeaveInternal(EventArgs.Empty); + break; + + default: + DefChildWndProc(ref m); + break; + } + } + + /// + /// Helper to handle MouseEnter. + /// + /// + private void OnMouseEnterInternal(EventArgs args) { + if (!mouseInEdit) { + OnMouseEnter(args); + mouseInEdit = true; + } + } + + /// + /// Helper to handle mouseleave + /// + /// + private void OnMouseLeaveInternal(EventArgs args) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref rect); + Rectangle Rect = new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + Point p = MousePosition; + if (!Rect.Contains(p)) { + OnMouseLeave(args); + mouseInEdit = false; + } + } + + /// + /// + /// + private void DefChildWndProc(ref Message m) { + if (childEdit != null) { + NativeWindow childWindow; + if (m.HWnd == childEdit.Handle) { + childWindow = childEdit; + } else if (AccessibilityImprovements.Level3 && m.HWnd == dropDownHandle) { + childWindow = childDropDown; + } + else { + childWindow = childListBox; + } + + //childwindow could be null if the handle was recreated while within a message handler + // and then whoever recreated the handle allowed the message to continue to be processed + //we cannot really be sure the new child will properly handle this window message, so we eat it. + if (childWindow != null) { + childWindow.DefWndProc(ref m); + } + } + } + + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + } + base.Dispose(disposing); + } + + + + + /// + /// + /// Reenables redrawing of the combo box. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// combo box is deferred until the call to endUpdate(). + /// + public void EndUpdate() { + updateCount--; + if (updateCount == 0 && AutoCompleteSource == AutoCompleteSource.ListItems) + { + SetAutoComplete(false, false); + } + if (EndUpdateInternal()) { + if (childEdit != null && childEdit.Handle != IntPtr.Zero) { + SafeNativeMethods.InvalidateRect(new HandleRef(this, childEdit.Handle), null, false); + } + if (childListBox != null && childListBox.Handle != IntPtr.Zero) { + SafeNativeMethods.InvalidateRect(new HandleRef(this, childListBox.Handle), null, false); + } + } + } + + /// + /// + /// Finds the first item in the combo box that starts with the given string. + /// The search is not case sensitive. + /// + public int FindString(string s) { + return FindString(s, -1); + } + + /// + /// + /// Finds the first item after the given index which starts with the given + /// string. The search is not case sensitive. + /// + public int FindString(string s, int startIndex) { + if (s == null) { + return -1; + } + + if (itemsCollection == null || itemsCollection.Count == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of CB_FINDSTRING. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, false); + } + + /// + /// + /// Finds the first item in the combo box that matches the given string. + /// The strings must match exactly, except for differences in casing. + /// + public int FindStringExact(string s) { + return FindStringExact(s, -1, true); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match exactly, except for differences in + /// casing. + /// + public int FindStringExact(string s, int startIndex) { + return FindStringExact(s, startIndex, true); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match exactly, except for differences in + /// casing. + /// + internal int FindStringExact(string s, int startIndex, bool ignorecase) { + if (s == null) return -1; + + if (itemsCollection == null || itemsCollection.Count == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of CB_FINDSTRINGEXACT. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, true, ignorecase); + } + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + if (DropDownStyle == ComboBoxStyle.DropDown + || DropDownStyle == ComboBoxStyle.DropDownList) { + + proposedHeight = PreferredHeight; + } + + return base.ApplyBoundsConstraints(suggestedX,suggestedY, proposedWidth, proposedHeight); + } + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + if (factor.Width != 1F && factor.Height != 1F) { + // VSWhidbey 440275 + // we get called on scale before we get a notification that our font has changed. + // in this case, we need to reset our height cache. + ResetHeightCache(); + } + base.ScaleControl(factor, specified); + } + + /// + /// + /// Returns the height of the given item in an OwnerDrawVariable style + /// combo box. This method should not be used for Normal or OwnerDrawFixed + /// style combo boxes. + /// + public int GetItemHeight(int index) { + + // This function is only relevant for OwnerDrawVariable + if (DrawMode != DrawMode.OwnerDrawVariable) { + return ItemHeight; + } + + if (index < 0 || itemsCollection == null || index >= itemsCollection.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (IsHandleCreated) { + + int h = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, index, 0) ); + if (h == -1) { + throw new Win32Exception(); + } + return h; + } + + return ItemHeight; + } + + internal IntPtr GetListHandle() { + return DropDownStyle == ComboBoxStyle.Simple ? childListBox.Handle : dropDownHandle; + } + + internal NativeWindow GetListNativeWindow() { + return DropDownStyle == ComboBoxStyle.Simple ? childListBox : childDropDown; + } + + internal int GetListNativeWindowRuntimeIdPart() { + var listNativeWindow = GetListNativeWindow(); + return listNativeWindow != null ? listNativeWindow.GetHashCode() : 0; + } + + internal override IntPtr InitializeDCForWmCtlColor(IntPtr dc, int msg) { + if ((msg == NativeMethods.WM_CTLCOLORSTATIC) && !ShouldSerializeBackColor()) { + // Let the Win32 Edit control handle background colors itself. + // This is necessary because a disabled edit control will display a different + // BackColor than when enabled. + return IntPtr.Zero; + } + else if ((msg == NativeMethods.WM_CTLCOLORLISTBOX) && GetStyle(ControlStyles.UserPaint)) { + // VSWhidbey#93929. Base class returns hollow brush when UserPaint style is set, to avoid flicker in + // main control. But when returning colors for child dropdown list, return normal ForeColor/BackColor, + // since hollow brush leaves the list background unpainted. + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), ColorTranslator.ToWin32(ForeColor)); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(BackColor)); + return BackColorBrush; + } + else { + return base.InitializeDCForWmCtlColor(dc, msg); + } + } + + // Returns true when the key processing needs to be intercepted to allow + // auto-completion in DropDownList style. + private bool InterceptAutoCompleteKeystroke(Message m) { + if (m.Msg == NativeMethods.WM_KEYDOWN) { + Debug.Assert((ModifierKeys & Keys.Alt) == 0); + // Keys.Delete only triggers a WM_KEYDOWN and WM_KEYUP, and no WM_CHAR. That's why it's treated separately. + if ((Keys)unchecked( (int) (long)m.WParam) == Keys.Delete) { + // Reset matching text and remove any selection + this.MatchingText = ""; + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + if (this.Items.Count > 0) { + SelectedIndex = 0; + } + return false; + } + } + else if (m.Msg == NativeMethods.WM_CHAR) { + Debug.Assert((ModifierKeys & Keys.Alt) == 0); + char keyChar = unchecked((char)(long)m.WParam); + if (keyChar == (char)Keys.Back) { + if (DateTime.Now.Ticks - this.autoCompleteTimeStamp > AutoCompleteTimeout || + this.MatchingText.Length <= 1) { + // Reset matching text and remove any selection + this.MatchingText = ""; + if (this.Items.Count > 0) { + SelectedIndex = 0; + } + } + else { + // Remove one character from matching text and rematch + this.MatchingText = this.MatchingText.Remove(this.MatchingText.Length - 1); + this.SelectedIndex = FindString(this.MatchingText); + } + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return false; + } + else if (keyChar == (char)Keys.Escape) { + this.MatchingText = ""; + } + + string newMatchingText; + if (keyChar != (char)Keys.Escape && keyChar != (char)Keys.Return && !DroppedDown + && AutoCompleteMode != AutoCompleteMode.Append) { + DroppedDown = true; + } + if (DateTime.Now.Ticks - this.autoCompleteTimeStamp > AutoCompleteTimeout) { + newMatchingText = new string(keyChar, 1); + if (FindString(newMatchingText) != -1) { + this.MatchingText = newMatchingText; + // Select the found item + } + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return false; + } + else { + newMatchingText = this.MatchingText + keyChar; + int itemFound = FindString(newMatchingText); + if (itemFound != -1) { + this.MatchingText = newMatchingText; + if (itemFound != this.SelectedIndex) { + this.SelectedIndex = itemFound; + } + } + // Do not change the selection + this.autoCompleteTimeStamp = DateTime.Now.Ticks; + return true; + } + } + return false; + } + + // Invalidate the entire control, including child HWNDs and non-client areas + private void InvalidateEverything() { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), + null, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_FRAME | // Control.Invalidate(true) doesn't invalidate the non-client region + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + + /// + /// + /// Determines if keyData is in input key that the control wants. + /// Overridden to return true for RETURN and ESCAPE when the combo box is + /// dropped down. + /// + /// + protected override bool IsInputKey(Keys keyData) { + + Keys keyCode = keyData & (Keys.KeyCode | Keys.Alt); + if (keyCode == Keys.Return || keyCode == Keys.Escape) { + if (this.DroppedDown || autoCompleteDroppedDown) { + //old behavior + return true; + } + else if (SystemAutoCompleteEnabled && ACNativeWindow.AutoCompleteActive) { + autoCompleteDroppedDown = true; + return true; + } + } + + return base.IsInputKey(keyData); + } + + /// + /// Adds the given item to the native combo box. This asserts if the handle hasn't been + /// created. + /// + private int NativeAdd(object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.CB_ADDSTRING, 0, GetItemText(item))); + if (insertIndex < 0) { + throw new OutOfMemoryException(SR.GetString(SR.ComboBoxItemOverflow)); + } + return insertIndex; + } + + /// + /// Clears the contents of the combo box. + /// + private void NativeClear() { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + string saved = null; + if (DropDownStyle != ComboBoxStyle.DropDownList) { + saved = WindowText; + } + SendMessage(NativeMethods.CB_RESETCONTENT, 0, 0); + if (saved != null) { + WindowText = saved; + } + } + + /// + /// Get the text stored by the native control for the specified list item. + /// + private string NativeGetItemText(int index) { + int len = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETLBTEXTLEN, index, 0)); + StringBuilder sb = new StringBuilder(len + 1); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.CB_GETLBTEXT, index, sb); + return sb.ToString(); + } + + /// + /// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been + /// created or if the resulting insert index doesn't match the passed in index. + /// + private int NativeInsert(int index, object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.CB_INSERTSTRING, index, GetItemText(item))); + if (insertIndex < 0) { + throw new OutOfMemoryException(SR.GetString(SR.ComboBoxItemOverflow)); + } + Debug.Assert(insertIndex == index, "NativeComboBox inserted at " + insertIndex + " not the requested index of " + index); + return insertIndex; + } + + /// + /// Removes the native item from the given index. + /// + private void NativeRemoveAt(int index) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + + // Windows combo does not invalidate the selected region if you remove the + // currently selected item. Test for this and invalidate. Note that because + // invalidate will lazy-paint we can actually invalidate before we send the + // delete message. + // + if (DropDownStyle == ComboBoxStyle.DropDownList && SelectedIndex == index) { + Invalidate(); + } + + SendMessage(NativeMethods.CB_DELETESTRING, index, 0); + } + + internal override void RecreateHandleCore() { + + string oldText = WindowText; + base.RecreateHandleCore(); + if (!String.IsNullOrEmpty(oldText) && String.IsNullOrEmpty(WindowText)) { + WindowText = oldText; //restore the window text + } + } + /// + /// + /// Overridden to avoid multiple layouts during handle creation due to combobox size change + /// see bug 458948 + /// + /// + protected override void CreateHandle() + { + using (new LayoutTransaction(ParentInternal, this, PropertyNames.Bounds)) + { + base.CreateHandle(); + } + } + + /// + /// + /// Overridden to make sure all the items and styles get set up correctly. + /// Inheriting classes should not forget to call + /// base.OnHandleCreated() + /// + /// + protected override void OnHandleCreated(EventArgs e) { + + base.OnHandleCreated(e); + + if (MaxLength > 0) { + SendMessage(NativeMethods.CB_LIMITTEXT, MaxLength, 0); + } + + // Get the handles and wndprocs of the ComboBox's child windows + // + Debug.Assert(childEdit == null, "Child edit window already attached"); + Debug.Assert(childListBox == null, "Child listbox window already attached"); + + bool ok = childEdit == null && childListBox == null; + + if (ok && DropDownStyle != ComboBoxStyle.DropDownList) { + IntPtr hwnd = UnsafeNativeMethods.GetWindow(new HandleRef(this, Handle), NativeMethods.GW_CHILD); + if (hwnd != IntPtr.Zero) { + + // if it's a simple dropdown list, the first HWND is the list box. + // + if (DropDownStyle == ComboBoxStyle.Simple) { + childListBox = new ComboBoxChildNativeWindow(this, ChildWindowType.ListBox); + childListBox.AssignHandle(hwnd); + + // get the edits hwnd... + // + hwnd = UnsafeNativeMethods.GetWindow(new HandleRef(this, hwnd), NativeMethods.GW_HWNDNEXT); + } + + childEdit = new ComboBoxChildNativeWindow(this, ChildWindowType.Edit); + childEdit.AssignHandle(hwnd); + + // set the initial margin for combobox to be zero (this is also done whenever the font is changed). + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.EM_SETMARGINS, + NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); + } + } + + bool found; + int dropDownWidth = Properties.GetInteger(PropDropDownWidth, out found); + if (found) { + SendMessage(NativeMethods.CB_SETDROPPEDWIDTH, dropDownWidth, 0); + } + + found = false; + int itemHeight = Properties.GetInteger(PropItemHeight, out found); + if (found) { + // someone has set the item height - update it + UpdateItemHeight(); + } + // Resize a simple style combobox on handle creation + // to respect the requested height. + // + if (DropDownStyle == ComboBoxStyle.Simple) { + Height = requestedHeight; + } + + //If HandleCreated set the AutoComplete... + //this function checks if the correct properties are set to enable AutoComplete feature on combobox. + try + { + fromHandleCreate = true; + SetAutoComplete(false, false); + } + finally + { + fromHandleCreate = false; + } + + + if (itemsCollection != null) { + foreach (object item in itemsCollection) { + NativeAdd(item); + } + + // Now udpate the current selection. + // + if (selectedIndex >= 0) { + SendMessage(NativeMethods.CB_SETCURSEL, selectedIndex, 0); + UpdateText(); + selectedIndex = -1; + } + } + // NOTE: Setting SelectedIndex must be the last thing we do. See ASURT 73949. + + } + + /// + /// + /// We need to un-subclasses everything here. Inheriting classes should + /// not forget to call base.OnHandleDestroyed() + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + dropDownHandle = IntPtr.Zero; + if (Disposing) { + itemsCollection = null; + selectedIndex = -1; + } + else { + selectedIndex = SelectedIndex; + } + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// + /// This is the code that actually fires the drawItem event. Don't + /// forget to call base.onDrawItem() to ensure that drawItem events + /// are correctly fired at all other times. + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This is the code that actually fires the dropDown event. Don't + /// forget to call base.onDropDown() to ensure that dropDown events + /// are correctly fired at all other times. + /// + protected virtual void OnDropDown(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWN]; + if (handler != null) handler(this, e); + + if (AccessibilityImprovements.Level3) { + // Notify collapsed/expanded property change. + AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Collapsed, + UnsafeNativeMethods.ExpandCollapseState.Expanded); + + var accessibleObject = AccessibilityObject as ComboBoxUiaProvider; + if (accessibleObject != null) { + accessibleObject.SetComboBoxItemFocus(); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyDown(KeyEventArgs e) { + // Do Return/ESC handling + if (SystemAutoCompleteEnabled) { + if (e.KeyCode == Keys.Return) { + // Set SelectedIndex + NotifyAutoComplete(true); + } + else if ((e.KeyCode == Keys.Escape) && autoCompleteDroppedDown) { + // Fire TextChanged Only + NotifyAutoComplete(false); + } + autoCompleteDroppedDown = false; + } + + // Base Handling + base.OnKeyDown(e); + } + + /// + /// + /// Key press event handler. Overridden to close up the combo box when the + /// user presses RETURN or ESCAPE. + /// + /// + protected override void OnKeyPress(KeyPressEventArgs e) { + + base.OnKeyPress(e); + + //return when dropped down already fires commit. + if (!e.Handled && (e.KeyChar == (char)(int)Keys.Return || e.KeyChar == (char)(int)Keys.Escape) + && DroppedDown) { + dropDown = false; + if (FormattingEnabled) { + //Set the Text which would Compare the WindowText with the TEXT and change SelectedIndex. + Text = WindowText; + SelectAll(); + e.Handled = false; + } + else { + DroppedDown = false; + e.Handled = true; + } + } + } + + /// + /// + /// This is the code that actually fires the measuereItem event. Don't + /// forget to call base.onMeasureItem() to ensure that measureItem + /// events are correctly fired at all other times. + /// + protected virtual void OnMeasureItem(MeasureItemEventArgs e) { + MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; + if (handler != null) handler(this, e); + } + + /// + /// + /// If we have the style set to popup show mouse over + /// + protected override void OnMouseEnter(EventArgs e) { + base.OnMouseEnter(e); + MouseIsOver = true; + } + /// + /// + /// If we have the style set to popup show mouse over + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + MouseIsOver = false; + } + + /// + /// This internal helper allows us to call the committed function multiple times without worrying about double firing. + /// + private void OnSelectionChangeCommittedInternal(EventArgs e) { + //There are cases where if we disable the combo while in this event handler, it sends the message again. + //This is a recursion gaurd to ensure we only send one commit per user action. + if (allowCommit) { + try { + allowCommit = false; + OnSelectionChangeCommitted(e); + } + finally { + allowCommit = true; + } + } + } + + /// + /// + /// This is the code that actually fires the SelectionChangeCommitted event. + /// Don't forget to call base.OnSelectionChangeCommitted() to ensure + /// that SelectionChangeCommitted events are correctly fired at all other times. + /// + protected virtual void OnSelectionChangeCommitted(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELECTIONCHANGECOMMITTED]; + if (handler != null) handler(this, e); + + // The user selects a list item or selects an item and then closes the list. + // It indicates that the user's selection is to be processed but should not + // be focused after closing the list. + if (dropDown) { + dropDownWillBeClosed = true; + } + } + + /// + /// + /// This is the code that actually fires the selectedIndexChanged event. + /// Don't forget to call base.onSelectedIndexChanged() to ensure + /// that selectedIndexChanged events are correctly fired at all other times. + /// + protected override void OnSelectedIndexChanged(EventArgs e) { + base.OnSelectedIndexChanged(e); + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; + if (handler != null) handler(this, e); + + if (dropDownWillBeClosed) { + // This is after-closing selection - do not focus on the list item + // and reset the state to announce the selections later. + dropDownWillBeClosed = false; + } + else if (AccessibilityImprovements.Level3) { + var accessibleObject = AccessibilityObject as ComboBoxUiaProvider; + if (accessibleObject != null) { + + // Announce DropDown- and DropDownList-styled ComboBox item selection using keyboard + // in case when Level 3 is enabled and DropDown is not in expanded state. Simple-styled + // ComboBox selection is announced by TextProvider. + if (DropDownStyle == ComboBoxStyle.DropDownList || DropDownStyle == ComboBoxStyle.DropDown) { + if (dropDown) { + accessibleObject.SetComboBoxItemFocus(); + } + + accessibleObject.SetComboBoxItemSelection(); + } + } + } + + // set the position in the dataSource, if there is any + // we will only set the position in the currencyManager if it is different + // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) + // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls + // into the backEnd. We do not need to do that. + // + // don't change the position if SelectedIndex is -1 because this indicates a selection not from the list. + if (this.DataManager != null && DataManager.Position != SelectedIndex) { + //read this as "if everett or (whidbey and selindex is valid)" + if (!FormattingEnabled || SelectedIndex != -1) { + this.DataManager.Position = this.SelectedIndex; + } + } + } + + /// + protected override void OnSelectedValueChanged(EventArgs e) { + base.OnSelectedValueChanged(e); + selectedValueChangedFired = true; + } + + /// + /// + /// This is the code that actually fires the selectedItemChanged event. + /// Don't forget to call base.onSelectedItemChanged() to ensure + /// that selectedItemChanged events are correctly fired at all other times. + /// + protected virtual void OnSelectedItemChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDITEMCHANGED]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This is the code that actually fires the DropDownStyleChanged event. + /// + protected virtual void OnDropDownStyleChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWNSTYLE]; + if (handler != null) handler(this, e); + } + + /// + /// + /// This method is called by the parent control when any property + /// changes on the parent. This can be overriden by inheriting + /// classes, however they must call base.OnParentPropertyChanged. + /// + protected override void OnParentBackColorChanged(EventArgs e) { + base.OnParentBackColorChanged(e); + if (DropDownStyle == ComboBoxStyle.Simple) Invalidate(); + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + ResetHeightCache(); + + if (this.AutoCompleteMode == AutoCompleteMode.None) { + UpdateControl(true); + } + else { + //we always will recreate the handle when autocomplete mode is on + RecreateHandle(); + } + CommonProperties.xClearPreferredSizeCache(this); + } + + + private void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) { + if (AutoCompleteSource == AutoCompleteSource.CustomSource) + { + if (AutoCompleteCustomSource.Count == 0) + { + SetAutoComplete(true, true /*recreate handle*/); + } + else + { + SetAutoComplete(true, false); + } + + } + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + UpdateControl(false); + } + + /// + /// + /// Indicates that a critical property, such as color or font has + /// changed. + /// + /// + protected override void OnForeColorChanged(EventArgs e) { + base.OnForeColorChanged(e); + UpdateControl(false); + } + + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnGotFocus(EventArgs e) { + if (!canFireLostFocus) { + base.OnGotFocus(e); + canFireLostFocus = true; + } + } + + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLostFocus(EventArgs e) { + if (canFireLostFocus) { + if (this.AutoCompleteMode != AutoCompleteMode.None && + this.AutoCompleteSource == AutoCompleteSource.ListItems && + this.DropDownStyle == ComboBoxStyle.DropDownList) { + this.MatchingText = ""; + } + base.OnLostFocus(e); + canFireLostFocus = false; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnTextChanged(EventArgs e) { + if (SystemAutoCompleteEnabled) { + string text = this.Text; + + // Prevent multiple TextChanges... + if (text != this.lastTextChangedValue) { + // Need to still fire a TextChanged + base.OnTextChanged(e); + + // Save the new value + this.lastTextChangedValue = text; + } + } + else { + // Call the base + base.OnTextChanged(e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnValidating(CancelEventArgs e) { + if (SystemAutoCompleteEnabled) { + // Handle AutoComplete notification + NotifyAutoComplete(); + } + + // Call base + base.OnValidating(e); + } + + + private void UpdateControl(bool recreate) { + //clear the pref height cache + ResetHeightCache(); + + if (IsHandleCreated) { + if (DropDownStyle == ComboBoxStyle.Simple && recreate) { + // Control forgets to add a scrollbar. See ASURT 19113. + RecreateHandle(); + } + else { + UpdateItemHeight(); + // Force everything to repaint. See ASURT 19113. + InvalidateEverything(); + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (DropDownStyle == ComboBoxStyle.Simple) { + // simple style combo boxes have more painting problems than you can shake a stick at (#19113) + InvalidateEverything(); + } + } + + /// + protected override void OnDataSourceChanged(EventArgs e) { + if (Sorted) { + if (DataSource != null && Created) { + // we will only throw the exception when the control is already on the form. + Debug.Assert(DisplayMember.Equals(String.Empty), "if this list is sorted it means that dataSource was null when Sorted first became true. at that point DisplayMember had to be String.Empty"); + DataSource = null; + throw new InvalidOperationException(SR.GetString(SR.ComboBoxDataSourceWithSort)); + } + } + if (DataSource == null) { + BeginUpdate(); + SelectedIndex = -1; + Items.ClearInternal(); + EndUpdate(); + } + if (!Sorted && Created) + base.OnDataSourceChanged(e); + RefreshItems(); + } + + /// + protected override void OnDisplayMemberChanged(EventArgs e) { + base.OnDisplayMemberChanged(e); + + // bug 63005: when we change the displayMember, we need to refresh the + // items collection + // + RefreshItems(); + } + + /// + /// + /// This event is fired when the dropdown portion of the combobox is hidden. + /// + protected virtual void OnDropDownClosed(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DROPDOWNCLOSED]; + if (handler != null) handler(this, e); + + if (AccessibilityImprovements.Level3) { + // Need to announce the focus on combo-box with new selected value on drop-down close. + // If do not do this focus in Level 3 stays on list item of unvisible list. + // This is necessary for DropDown style as edit should not take focus. + if (DropDownStyle == ComboBoxStyle.DropDown) { + AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + + // Notify Collapsed/expanded property change. + AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Expanded, + UnsafeNativeMethods.ExpandCollapseState.Collapsed); + + // Collapsing the DropDown, so reset the flag. + dropDownWillBeClosed = false; + } + } + + + /// + /// + /// This event is fired when the edit portion of a combobox is about to display altered text. + /// This event is NOT fired if the TEXT property is programatically changed. + /// + protected virtual void OnTextUpdate(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_TEXTUPDATE]; + if (handler != null) handler(this, e); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) { + if (this.AutoCompleteMode != AutoCompleteMode.None && + this.AutoCompleteSource == AutoCompleteSource.ListItems && + this.DropDownStyle == ComboBoxStyle.DropDownList && + InterceptAutoCompleteKeystroke(m)) { + return true; + } + else { + return base.ProcessKeyEventArgs(ref m); + } + } + + private void ResetHeightCache() { + prefHeightCache = -1; + } + /// + /// + /// Reparses the objects, getting new text strings for them. + /// + /// + protected override void RefreshItems() { + + // Save off the selection and the current collection. + // + int selectedIndex = SelectedIndex; + ObjectCollection savedItems = itemsCollection; + + itemsCollection = null; + + + object[] newItems = null; + + // if we have a dataSource and a DisplayMember, then use it + // to populate the Items collection + // + if (this.DataManager != null && this.DataManager.Count != -1) { + newItems = new object[this.DataManager.Count]; + for (int i = 0; i < newItems.Length; i++) { + newItems[i] = this.DataManager[i]; + } + } + else if (savedItems != null) { + newItems = new object[savedItems.Count]; + savedItems.CopyTo(newItems, 0); + } + BeginUpdate(); + try + { + // Clear the items. + // + if (IsHandleCreated) + { + NativeClear(); + } + // Store the current list of items + // + if (newItems != null) + { + Items.AddRangeInternal(newItems); + } + if (this.DataManager != null) + { + // put the selectedIndex in sync w/ the position in the dataManager + this.SelectedIndex = this.DataManager.Position; + } + else + { + SelectedIndex = selectedIndex; + } + } + finally + { + EndUpdate(); + } + } + + /// + /// + /// Reparses the object at the given index, getting new text string for it. + /// + /// + protected override void RefreshItem(int index) { + Items.SetItemInternal(index, Items[index]); + } + + /// + /// Release the ChildWindow object by un-subclassing the child edit and + /// list controls and freeing the root of the ChildWindow object. + /// + private void ReleaseChildWindow() { + + if (childEdit != null) { + + // We do not use UI Automation provider for child edit, so do not need to release providers. + childEdit.ReleaseHandle(); + childEdit = null; + } + + if (childListBox != null) { + + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + if (AccessibilityImprovements.Level3) { + ReleaseUiaProvider(childListBox.Handle); + } + + childListBox.ReleaseHandle(); + childListBox = null; + } + + if (childDropDown != null) { + + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + if (AccessibilityImprovements.Level3) { + ReleaseUiaProvider(childDropDown.Handle); + } + + childDropDown.ReleaseHandle(); + childDropDown = null; + } + } + + internal override void ReleaseUiaProvider(IntPtr handle) { + base.ReleaseUiaProvider(handle); + + var uiaProvider = AccessibilityObject as ComboBoxUiaProvider; + uiaProvider?.ResetListItemAccessibleObjects(); + } + + private void ResetAutoCompleteCustomSource() { + AutoCompleteCustomSource = null; + } + + private void ResetDropDownWidth() { + Properties.RemoveInteger(PropDropDownWidth); + } + + private void ResetItemHeight() { + Properties.RemoveInteger(PropItemHeight); + } + + public override void ResetText() { + base.ResetText(); + } + + /// + /// + /// Enables the AutoComplete feature for combobox depending on the properties set. + /// These properties are namely AutoCompleteMode, AutoCompleteSource and AutoCompleteCustomSource. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private void SetAutoComplete(bool reset, bool recreate) { + if (!IsHandleCreated || childEdit == null) { + return; + } + + if (AutoCompleteMode != AutoCompleteMode.None) { + if (!fromHandleCreate && recreate && IsHandleCreated) + { + //RecreateHandle to avoid Leak. + // notice the use of member variable to avoid re-entrancy + AutoCompleteMode backUpMode = this.AutoCompleteMode; + autoCompleteMode = AutoCompleteMode.None; + RecreateHandle(); + autoCompleteMode = backUpMode; + } + + if (AutoCompleteSource == AutoCompleteSource.CustomSource) { + if (AutoCompleteCustomSource != null) { + if (AutoCompleteCustomSource.Count == 0) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + else { + + if (stringSource == null) + { + stringSource = new StringSource(GetStringsForAutoComplete(AutoCompleteCustomSource)); + if (!stringSource.Bind(new HandleRef(this, childEdit.Handle), (int)AutoCompleteMode)) { + throw new ArgumentException(SR.GetString(SR.AutoCompleteFailure)); + } + } + else + { + stringSource.RefreshList(GetStringsForAutoComplete(AutoCompleteCustomSource)); + } + + } + } + } + else if (AutoCompleteSource == AutoCompleteSource.ListItems) { + if (DropDownStyle != ComboBoxStyle.DropDownList) { + if (itemsCollection != null) { + if (itemsCollection.Count == 0) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + else { + + if (stringSource == null) + { + stringSource = new StringSource(GetStringsForAutoComplete(Items)); + if (!stringSource.Bind(new HandleRef(this, childEdit.Handle), (int)AutoCompleteMode)) { + throw new ArgumentException(SR.GetString(SR.AutoCompleteFailureListItems)); + } + } + else + { + stringSource.RefreshList(GetStringsForAutoComplete(Items)); + } + + } + } + } + else { + // Drop Down List special handling + Debug.Assert(DropDownStyle == ComboBoxStyle.DropDownList); + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + } + else { + try { + int mode = 0; + + if (AutoCompleteMode == AutoCompleteMode.Suggest) { + mode |= NativeMethods.AUTOSUGGEST | NativeMethods.AUTOAPPEND_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.Append) { + mode |= NativeMethods.AUTOAPPEND | NativeMethods.AUTOSUGGEST_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.SuggestAppend) { + mode |= NativeMethods.AUTOSUGGEST; + mode |= NativeMethods.AUTOAPPEND; + } + int ret = SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), (int)AutoCompleteSource | mode); + } + catch (SecurityException) { + // If we don't have full trust, degrade gracefully. Allow the control to + // function without auto-complete. Allow the app to continue running. + } + } + } + else if (reset) { + int mode = NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, childEdit.Handle), mode); + } + } + + /// + /// + /// Selects the text in the editable portion of the ComboBox at the + /// from the given start index to the given end index. + /// + public void Select(int start, int length) { + if (start < 0) { + throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidArgument, "start", start.ToString(CultureInfo.CurrentCulture))); + } + // the Length can be negative to support Selecting in the "reverse" direction.. + int end = start + length; + + // but end cannot be negative... this means Length is far negative... + if (end < 0) { + throw new ArgumentOutOfRangeException("length", SR.GetString(SR.InvalidArgument, "length", length.ToString(CultureInfo.CurrentCulture))); + } + + SendMessage(NativeMethods.CB_SETEDITSEL, 0, NativeMethods.Util.MAKELPARAM(start, end)); + } + + /// + /// + /// Selects all the text in the editable portion of the ComboBox. + /// + public void SelectAll() { + Select(0, Int32.MaxValue); + } + + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + // If we are changing height, store the requested height. + // Requested height is used if the style is changed to simple. + // (Bug fix #20966) + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) { + requestedHeight = height; + } + + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Performs the work of setting the specified items to the combobox + /// + protected override void SetItemsCore(IList value) { + BeginUpdate(); + Items.ClearInternal(); + Items.AddRangeInternal(value); + + // if the list changed, we want to keep the same selected index + // CurrencyManager will provide the PositionChanged event + // it will be provided before changing the list though... + if (this.DataManager != null) { + if (this.DataSource is ICurrencyManagerProvider) { + // Everett ListControl's had a bug where they would not fire + // OnSelectedValueChanged if their list of items were refreshed. + // We fix this post-Everett. + // However, for APPCOMPAT reasons, we only want to fix it when binding to + // Whidbey components. + // vsw 547279. + this.selectedValueChangedFired = false; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.CB_SETCURSEL, DataManager.Position, 0); + } + else { + selectedIndex = DataManager.Position; + } + + // if set_SelectedIndexChanged did not fire OnSelectedValueChanged + // then we have to fire it ourselves, cos the list changed anyway + if (!selectedValueChangedFired) { + OnSelectedValueChanged(EventArgs.Empty); + selectedValueChangedFired = false; + } + } + + EndUpdate(); + } + + /// + protected override void SetItemCore(int index, object value) { + Items.SetItemInternal(index, value); + } + + private bool ShouldSerializeAutoCompleteCustomSource() { + return autoCompleteCustomSource != null && autoCompleteCustomSource.Count > 0; + } + + internal bool ShouldSerializeDropDownWidth() { + return (Properties.ContainsInteger(PropDropDownWidth)); + } + + /// + /// Indicates whether the itemHeight property should be persisted. + /// + internal bool ShouldSerializeItemHeight() { + return (Properties.ContainsInteger(PropItemHeight)); + } + + /// + /// + /// Determines if the Text property needs to be persisted. + /// + internal override bool ShouldSerializeText() { + return SelectedIndex == -1 && base.ShouldSerializeText(); + } + + /// + /// + /// Provides some interesting info about this control in String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Items.Count: " + ((itemsCollection == null) ? (0).ToString(CultureInfo.CurrentCulture) : itemsCollection.Count.ToString(CultureInfo.CurrentCulture)); + } + + /// + /// + /// + private void UpdateDropDownHeight() { + if (dropDownHandle != IntPtr.Zero) { + //Now use the DropDownHeight property instead of calculating the Height... + int height = DropDownHeight; + if (height == DefaultDropDownHeight) { + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + int count = Math.Min(Math.Max(itemCount, 1), maxDropDownItems); + height = (ItemHeight * count + 2); + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, dropDownHandle), NativeMethods.NullHandleRef, 0, 0, DropDownWidth, height, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER); + } + } + + /// + /// Manufactures a MeasureItemEventArgs for each item in the list to simulate + /// the combobox requesting the info. This gives the effect of allowing the + /// measureitem info to be updated at anytime. + /// + /// + private void UpdateItemHeight() { + if (!IsHandleCreated) { + // VSWhidbey 156992: if we don't create control here we report item heights incorrectly later on. + CreateControl(); + } + if (DrawMode == DrawMode.OwnerDrawFixed) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, -1, ItemHeight); + SendMessage(NativeMethods.CB_SETITEMHEIGHT, 0, ItemHeight); + } + else if (DrawMode == DrawMode.OwnerDrawVariable) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, -1, ItemHeight); + Graphics graphics = CreateGraphicsInternal(); + for (int i = 0; i < Items.Count; i++) { + int original = unchecked( (int) (long)SendMessage(NativeMethods.CB_GETITEMHEIGHT, i, 0)); + MeasureItemEventArgs mievent = new MeasureItemEventArgs(graphics, i, original); + OnMeasureItem(mievent); + if (mievent.ItemHeight != original) { + SendMessage(NativeMethods.CB_SETITEMHEIGHT, i, mievent.ItemHeight); + } + } + graphics.Dispose(); + } + } + + /// + /// Forces the text to be updated based on the current selection. + /// + /// + private void UpdateText() { + // V#45724 - Fire text changed for dropdown combos when the selection + // changes, since the text really does change. We've got + // to do this asynchronously because the actual edit text + // isn't updated until a bit later (V#51240). + // + + // QFE 2471: + // v1.0 - ComboBox::set_Text compared items w/ "value" and set the SelectedIndex accordingly + // v1.0 - null values can't correspond to String.Empty + // v1.0 - SelectedIndex == -1 corresponds to Text == String.Emtpy + // + // v1.1 - ComboBox::set_Text compares FilterItemOnProperty(item) w/ "value" and set the SelectedIndex accordingly + // v1.1 - null values correspond to String.Empty + // v1.1 - SelectedIndex == -1 corresponds to Text == null + string s = null; + + if (SelectedIndex != -1) { + object item = Items[SelectedIndex]; + if (item != null) { + s = GetItemText(item); + } + } + + Text = s; + + if (DropDownStyle == ComboBoxStyle.DropDown) { + if (childEdit != null && childEdit.Handle != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, childEdit.Handle), NativeMethods.WM_SETTEXT, IntPtr.Zero, s); + } + } + } + + /// + /// + /// + private void WmEraseBkgnd(ref Message m) { + if ((DropDownStyle == ComboBoxStyle.Simple) && ParentInternal != null) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + SafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rect); + Control p = ParentInternal; + Graphics graphics = Graphics.FromHdcInternal(m.WParam); + if (p != null) { + Brush brush = new SolidBrush(p.BackColor); + graphics.FillRectangle(brush, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + brush.Dispose(); + } + else { + graphics.FillRectangle(SystemBrushes.Control, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + } + graphics.Dispose(); + m.Result = (IntPtr)1; + return; + } + base.WndProc(ref m); + } + + /// + /// + /// + private void WmParentNotify(ref Message m) { + base.WndProc(ref m); + if (unchecked((int)(long)m.WParam) == (NativeMethods.WM_CREATE | 1000 << 16)) { + dropDownHandle = m.LParam; + + if (AccessibilityImprovements.Level3) { + // By some reason WmParentNotify with WM_DESTROY is not called before recreation. + // So release the old references here. + if (childDropDown != null) { + // Need to notify UI Automation that it can safely remove all map entries that refer to the specified window. + ReleaseUiaProvider(childListBox.Handle); + + childDropDown.ReleaseHandle(); + } + + childDropDown = new ComboBoxChildNativeWindow(this, ChildWindowType.DropDownList); + childDropDown.AssignHandle(dropDownHandle); + + // Reset the child list accessible object in case the the DDL is recreated. + // For instance when dialog window containging the ComboBox is reopened. + childListAccessibleObject = null; + } + } + } + + /// + /// Text change behavior. + /// Here are the window messages corresponding to each user event. + /// + /// DropDown (free text window): + /// Type in Text Window: + /// CBN_EDITUPDATE + /// CBN_EDITCHANGE + /// Down/Up Arrow + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// with text set to beginning of a valid item -- drop and close dropdown (selects that item) + /// CBN_DROPDOWN + /// CBN_CLOSEUP + /// Drop List, up/down arrow to select item, click away + /// CBN_DROPDOWN + /// CBN_SELCHANGE + /// CBN_CLOSEUP + /// Drop List, click on item + /// CBN_DROPDOWN + /// CBN_SELENDOK + /// CBN_CLOSEUP + /// CBN_SELCHANGE (text changes here via selected item) + /// + /// DropDownList (limited text window): + /// + /// Type text and arrow up/down: + /// CBN_SELENDOK (text already changed) + /// CBN_SELCHANGE + /// Drop List, up/down arrow to select item, click away + /// CBN_DROPDOWN + /// CBN_SELCHANGE + /// CBN_CLOSEUP + /// Drop List, click on item + /// CBN_DROPDOWN + /// CBN_SELENDOK + /// CBN_CLOSEUP + /// CBN_SELCHANGE + /// + /// Simple (listbox visible): + /// Type in Text Window: + /// CBN_EDITUPDATE + /// CBN_EDITCHANGE + /// Down/Up Arrow + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// Click on item + /// CBN_SELENDOK (text not changed yet) + /// CBN_SELCHANGE + /// + /// + /// What we do is fire textchange events in these messages: + /// CBN_SELCHANGE + /// CBN_EDITCHANGE + /// CBN_CLOSEUP + /// + /// and we only actually call the real event if the Text is different than currentText. + /// currentText is never changed outside this method. + /// This internal version can be called from anywhere we might suspect text has changed + /// it's fairly safe to call anywhere. + + /// + /// + /// + private void WmReflectCommand(ref Message m) { + switch (NativeMethods.Util.HIWORD(m.WParam)) { + case NativeMethods.CBN_DBLCLK: + //OnDoubleClick(EventArgs.Empty); + break; + case NativeMethods.CBN_EDITUPDATE: + OnTextUpdate(EventArgs.Empty); + break; + case NativeMethods.CBN_CLOSEUP: + + OnDropDownClosed(EventArgs.Empty); + if (FormattingEnabled && Text != currentText && dropDown) { + OnTextChanged(EventArgs.Empty); + } + dropDown = false; + break; + case NativeMethods.CBN_DROPDOWN: + currentText = Text; + dropDown = true; + OnDropDown(EventArgs.Empty); + UpdateDropDownHeight(); + + break; + case NativeMethods.CBN_EDITCHANGE: + OnTextChanged(EventArgs.Empty); + break; + case NativeMethods.CBN_SELCHANGE: + UpdateText(); + OnSelectedIndexChanged(EventArgs.Empty); + break; + case NativeMethods.CBN_SELENDOK: + OnSelectionChangeCommittedInternal(EventArgs.Empty); + break; + } + } + + /// + /// + /// + private void WmReflectDrawItem(ref Message m) { + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + IntPtr oldPal = SetUpPalette(dis.hDC, false /*force*/, false /*realize*/); + try { + Graphics g = Graphics.FromHdcInternal(dis.hDC); + + try { + OnDrawItem(new DrawItemEventArgs(g, Font, Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom), + dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); + } + finally { + g.Dispose(); + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(this, dis.hDC), new HandleRef(null, oldPal), 0); + } + } + m.Result = (IntPtr)1; + } + + /// + /// + /// + private void WmReflectMeasureItem(ref Message m) { + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + // Determine if message was sent by a combo item or the combo edit field + if (DrawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { + Graphics graphics = CreateGraphicsInternal(); + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); + OnMeasureItem(mie); + mis.itemHeight = mie.ItemHeight; + graphics.Dispose(); + } + else { + // Message was sent by the combo edit field + mis.itemHeight = ItemHeight; + } + Marshal.StructureToPtr(mis, m.LParam, false); + m.Result = (IntPtr)1; + } + + /// + /// + /// The comboboxs window procedure. Inheritng classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the combo continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + // We don't want to fire the focus events twice - + // once in the combobox and once in the ChildWndProc. + case NativeMethods.WM_SETFOCUS: + try { + fireSetFocus = false; + base.WndProc(ref m); + } + + finally { + fireSetFocus = true; + } + break; + case NativeMethods.WM_KILLFOCUS: + try { + fireLostFocus = false; + base.WndProc(ref m); + // Nothing to see here... Just keep on walking... VSWhidbey 504477. + // Turns out that with Theming off, we don't get quite the same messages as with theming on. + + // With theming on we get a WM_MOUSELEAVE after a WM_KILLFOCUS even if you use the Tab key + // to move focus. Our response to WM_MOUSELEAVE causes us to repaint everything correctly. + + // With theming off, we do not get a WM_MOUSELEAVE after a WM_KILLFOCUS, and since we don't have a childwndproc + // when we are a Flat DropDownList, we need to force a repaint. The easiest way to do this is to send a + // WM_MOUSELEAVE to ourselves, since that also sets up the right state. Or... at least the state is the same + // as with Theming on. + + // This is such a @#$(*&#@$ hack. + + if (!Application.RenderWithVisualStyles && GetStyle(ControlStyles.UserPaint) == false && this.DropDownStyle == ComboBoxStyle.DropDownList && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup)) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_MOUSELEAVE, 0, 0); + } + } + + finally { + fireLostFocus = true; + } + break; + case NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_CTLCOLORLISTBOX: + m.Result = InitializeDCForWmCtlColor(m.WParam, m.Msg); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + case NativeMethods.WM_PARENTNOTIFY: + WmParentNotify(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmReflectDrawItem(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: + WmReflectMeasureItem(ref m); + break; + case NativeMethods.WM_LBUTTONDOWN: + mouseEvents = true; + base.WndProc(ref m); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + Rectangle ClientRect = new Rectangle(r.left, r.top, r.right - r.left, r.bottom - r.top); + + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x, y); + pt = PointToScreen(pt); + //mouseEvents is used to keep the check that we get the WM_LBUTTONUP after + //WM_LBUTTONDOWN or WM_LBUTTONDBLBCLK + // combo box gets a WM_LBUTTONUP for focus change ... + // + if (mouseEvents && !ValidationCancelled) { + mouseEvents = false; + bool captured = Capture; + if (captured && ClientRect.Contains(pt)) { + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + base.WndProc(ref m); + } + else { + CaptureInternal = false; + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_MOUSELEAVE: + DefWndProc(ref m); + OnMouseLeaveInternal(EventArgs.Empty); + break; + + case NativeMethods.WM_PAINT: + if (GetStyle(ControlStyles.UserPaint) == false && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup)) { + + using (WindowsRegion dr = new WindowsRegion(FlatComboBoxAdapter.dropDownRect)) { + using (WindowsRegion wr = new WindowsRegion(this.Bounds)) { + + // Stash off the region we have to update (the base is going to clear this off in BeginPaint) + NativeMethods.RegionFlags updateRegionFlags = (NativeMethods.RegionFlags)SafeNativeMethods.GetUpdateRgn(new HandleRef(this, this.Handle), new HandleRef(this, wr.HRegion), true); + + dr.CombineRegion(wr, dr, RegionCombineMode.DIFF); + + Rectangle updateRegionBoundingRect = wr.ToRectangle(); + FlatComboBoxAdapter.ValidateOwnerDrawRegions(this, updateRegionBoundingRect); + // Call the base class to do its painting (with a clipped DC). + + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + IntPtr dc; + bool disposeDc = false; + if (m.WParam == IntPtr.Zero) { + dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, Handle), ref ps); + disposeDc = true; + } + else { + dc = m.WParam; + } + + using (DeviceContext mDC = DeviceContext.FromHdc(dc)) { + using (WindowsGraphics wg = new WindowsGraphics(mDC)) { + if (updateRegionFlags != NativeMethods.RegionFlags.ERROR) { + wg.DeviceContext.SetClip(dr); + } + m.WParam = dc; + DefWndProc(ref m); + if (updateRegionFlags != NativeMethods.RegionFlags.ERROR) { + wg.DeviceContext.SetClip(wr); + } + using (Graphics g = Graphics.FromHdcInternal(dc)) { + FlatComboBoxAdapter.DrawFlatCombo(this, g); + } + } + } + + if (disposeDc) { + UnsafeNativeMethods.EndPaint(new HandleRef(this, Handle), ref ps); + } + + } + return; + } + } + + base.WndProc(ref m); + break; + case NativeMethods.WM_PRINTCLIENT: + // all the fancy stuff we do in OnPaint has to happen again in OnPrint. + if (GetStyle(ControlStyles.UserPaint) == false && FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup) { + DefWndProc(ref m); + + if ((unchecked( (int) (long)m.LParam) & NativeMethods.PRF_CLIENT) == NativeMethods.PRF_CLIENT) { + if (GetStyle(ControlStyles.UserPaint) == false && FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup) { + using (Graphics g = Graphics.FromHdcInternal(m.WParam)) { + FlatComboBoxAdapter.DrawFlatCombo(this,g); + } + } + return; + } + } + base.WndProc(ref m); + return; + case NativeMethods.WM_SETCURSOR: + base.WndProc(ref m); + break; + + case NativeMethods.WM_SETFONT: + //(bug 119265) + if (Width == 0) { + suppressNextWindosPos = true; + } + base.WndProc(ref m); + break; + + + case NativeMethods.WM_WINDOWPOSCHANGED: + if (!suppressNextWindosPos) { + base.WndProc(ref m); + } + suppressNextWindosPos = false; + break; + + case NativeMethods.WM_NCDESTROY: + base.WndProc(ref m); + ReleaseChildWindow(); + break; + + default: + if (m.Msg == NativeMethods.WM_MOUSEENTER) { + DefWndProc(ref m); + OnMouseEnterInternal(EventArgs.Empty); + break; + } + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + [ComVisible(true)] + internal class ComboBoxChildNativeWindow : NativeWindow { + private ComboBox _owner; + private InternalAccessibleObject _accessibilityObject; + private ChildWindowType _childWindowType; + + public ComboBoxChildNativeWindow(ComboBox comboBox, ChildWindowType childWindowType) { + _owner = comboBox; + _childWindowType = childWindowType; + } + + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_GETOBJECT: + WmGetObject(ref m); + return; + case NativeMethods.WM_MOUSEMOVE: + if (_childWindowType == ChildWindowType.DropDownList) { + + // Need to track the selection change via mouse over to + // raise focus changed event for the items. Monitoring + // item change in setters does not guarantee that focus + // is properly announced. + object before = _owner.SelectedItem; + DefWndProc(ref m); + object after = _owner.SelectedItem; + if (before != after) { + (_owner.AccessibilityObject as ComboBoxUiaProvider).SetComboBoxItemFocus(); + } + } else { + _owner.ChildWndProc(ref m); + } + break; + default: + if (_childWindowType == ChildWindowType.DropDownList) { + DefWndProc(ref m); // Drop Down window should behave by its own. + } else { + _owner.ChildWndProc(ref m); + } + break; + } + } + + private ChildAccessibleObject GetChildAccessibleObject(ChildWindowType childWindowType) { + if (childWindowType == ChildWindowType.Edit) { + return _owner.ChildEditAccessibleObject; + } + else if (childWindowType == ChildWindowType.ListBox || childWindowType == ChildWindowType.DropDownList) { + return _owner.ChildListAccessibleObject; + } + + return new ChildAccessibleObject(_owner, Handle); + } + + private void WmGetObject(ref Message m) { + if (AccessibilityImprovements.Level3 && m.LParam == (IntPtr)NativeMethods.UiaRootObjectId && + // Managed UIAutomation providers are supplied for child list windows but not for the child edit window. + // Child list accessibility object provides all necessary patterns and UIAutomation notifications, + // so there is no need to native provider supplement. + // Child edit accessibility object has only partial support of edit box accessibility, most of the patterns + // and notifications for child edit window are supplied by native providers, so here is no need to + // override root UIA object for child edit window. + (_childWindowType == ChildWindowType.ListBox || _childWindowType == ChildWindowType.DropDownList)) { + AccessibleObject uiaProvider = GetChildAccessibleObject(_childWindowType); + + // If the requested object identifier is UiaRootObjectId, + // we should return an UI Automation provider using the UiaReturnRawElementProvider function. + InternalAccessibleObject internalAccessibleObject; + IntSecurity.UnmanagedCode.Assert(); + try { + internalAccessibleObject = new InternalAccessibleObject(uiaProvider); + } + finally { + CodeAccessPermission.RevertAssert(); + } + m.Result = UnsafeNativeMethods.UiaReturnRawElementProvider( + new HandleRef(this, Handle), + m.WParam, + m.LParam, + internalAccessibleObject); + + return; + } + + // See "How to Handle WM_GETOBJECT" in MSDN + // + if (NativeMethods.OBJID_CLIENT == unchecked((int)(long)m.LParam)) { + + // Get the IAccessible GUID + // + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + + // Get an Lresult for the accessibility Object for this control + // + IntPtr punkAcc; + try { + AccessibleObject wfAccessibleObject = null; + UnsafeNativeMethods.IAccessibleInternal iacc = null; + + if (_accessibilityObject == null) { + IntSecurity.UnmanagedCode.Assert(); + try { + wfAccessibleObject = AccessibilityImprovements.Level3 + ? GetChildAccessibleObject(_childWindowType) + : new ChildAccessibleObject(_owner, Handle); + _accessibilityObject = new InternalAccessibleObject(wfAccessibleObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + iacc = (UnsafeNativeMethods.IAccessibleInternal)_accessibilityObject; + + // Obtain the Lresult + // + punkAcc = Marshal.GetIUnknownForObject(iacc); + + IntSecurity.UnmanagedCode.Assert(); + try { + m.Result = UnsafeNativeMethods.LresultFromObject(ref IID_IAccessible, m.WParam, new HandleRef(this, punkAcc)); + } + finally { + CodeAccessPermission.RevertAssert(); + Marshal.Release(punkAcc); + } + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.RichControlLresult), e); + } + } + else { // m.lparam != OBJID_CLIENT, so do default message processing + DefWndProc(ref m); + } + } + } + + private sealed class ItemComparer : System.Collections.IComparer { + private ComboBox comboBox; + + public ItemComparer(ComboBox comboBox) { + this.comboBox = comboBox; + } + + public int Compare(object item1, object item2) { + if (item1 == null) { + if (item2 == null) + return 0; //both null, then they are equal + + return -1; //item1 is null, but item2 is valid (greater) + } + if (item2 == null) + return 1; //item2 is null, so item 1 is greater + + String itemName1 = comboBox.GetItemText(item1); + String itemName2 = comboBox.GetItemText(item2); + + CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; + return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); + } + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class ObjectCollection : IList { + + private ComboBox owner; + private ArrayList innerList; + private IComparer comparer; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(ComboBox owner) { + this.owner = owner; + } + + private IComparer Comparer { + get { + if (comparer == null) { + comparer = new ItemComparer(owner); + } + return comparer; + } + } + + private ArrayList InnerList { + get { + if (innerList == null) { + innerList = new ArrayList(); + } + return innerList; + } + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count { + get { + return InnerList.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + + public int Add(object item) + { + owner.CheckNoDataSource(); + int index = AddInternal(item); + if (owner.UpdateNeeded() && owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + return index; + } + + + private int AddInternal(object item) { + + if (item == null) { + throw new ArgumentNullException("item"); + } + int index = -1; + if (!owner.sorted) + { + InnerList.Add(item); + } + else + { + index = InnerList.BinarySearch(item, Comparer); + if (index < 0) + { + index = ~index; // getting the index of the first element that is larger than the search value + } + + Debug.Assert(index>=0 && index <= InnerList.Count, "Wrong index for insert"); + InnerList.Insert(index, item); + } + bool successful = false; + + try { + if (owner.sorted) { + if (owner.IsHandleCreated) { + owner.NativeInsert(index, item); + } + } + else { + index = InnerList.Count-1; + if (owner.IsHandleCreated) { + owner.NativeAdd(item); + } + } + successful = true; + } + finally { + if (!successful) { + InnerList.Remove(item); + } + } + + return index; + } + + /// + /// + int IList.Add(object item) { + return Add(item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(object[] items) { + owner.CheckNoDataSource(); + owner.BeginUpdate(); + try + { + AddRangeInternal(items); + } + finally + { + owner.EndUpdate(); + } + } + + internal void AddRangeInternal(IList items) { + + if (items == null) + { + throw new ArgumentNullException("items"); + } + foreach (object item in items) { + // adding items one-by-one for performance (especially for sorted combobox) + // we can not rely on ArrayList.Sort since its worst case complexity is n*n + // AddInternal is based on BinarySearch and ensures n*log(n) complexity + AddInternal(item); + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + + /// + /// + /// Retrieves the item with the specified index. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual object this[int index] { + get { + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + return InnerList[index]; + } + set { + owner.CheckNoDataSource(); + SetItemInternal(index, value); + } + } + + /// + /// + /// Removes all items from the ComboBox. + /// + public void Clear() { + owner.CheckNoDataSource(); + ClearInternal(); + } + + internal void ClearInternal() { + + if (owner.IsHandleCreated) { + owner.NativeClear(); + } + + InnerList.Clear(); + owner.selectedIndex = -1; + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, true /*recreateHandle*/); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object value) { + return IndexOf(value) != -1; + } + + /// + /// + /// Copies the ComboBox Items collection to a destination array. + /// + public void CopyTo(object[] destination, int arrayIndex) { + InnerList.CopyTo(destination, arrayIndex); + } + + /// + /// + void ICollection.CopyTo(Array destination, int index) { + InnerList.CopyTo(destination, index); + } + + /// + /// + /// Returns an enumerator for the ComboBox Items collection. + /// + public IEnumerator GetEnumerator() { + return InnerList.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerList.IndexOf(value); + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public void Insert(int index, object item) { + owner.CheckNoDataSource(); + + if (item == null) { + throw new ArgumentNullException("item"); + } + + if (index < 0 || index > InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + // If the combo box is sorted, then nust treat this like an add + // because we are going to twiddle the index anyway. + // + if (owner.sorted) { + Add(item); + } + else { + InnerList.Insert(index, item); + if (owner.IsHandleCreated) { + + bool successful = false; + + try { + owner.NativeInsert(index, item); + successful = true; + } + finally { + if (successful) { + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + else { + InnerList.RemoveAt(index); + } + } + } + } + } + + /// + /// + /// Removes an item from the ComboBox at the given index. + /// + public void RemoveAt(int index) { + owner.CheckNoDataSource(); + + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.IsHandleCreated) { + owner.NativeRemoveAt(index); + } + + InnerList.RemoveAt(index); + if (!owner.IsHandleCreated && index < owner.selectedIndex) { + owner.selectedIndex--; + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + + /// + /// + /// Removes the given item from the ComboBox, provided that it is + /// actually in the list. + /// + public void Remove(object value) { + + int index = InnerList.IndexOf(value); + + if (index != -1) { + RemoveAt(index); + } + } + + internal void SetItemInternal(int index, object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + InnerList[index] = value; + + // If the native control has been created, and the display text of the new list item object + // is different to the current text in the native list item, recreate the native list item... + if (owner.IsHandleCreated) { + bool selected = (index == owner.SelectedIndex); + + if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { + owner.NativeRemoveAt(index); + owner.NativeInsert(index, value); + if (selected) { + owner.SelectedIndex = index; + owner.UpdateText(); + } + if (owner.AutoCompleteSource == AutoCompleteSource.ListItems) + { + owner.SetAutoComplete(false, false); + } + } + else { + // NEW - FOR COMPATIBILITY REASONS + // Minimum compatibility fix for VSWhidbey 377287/444903 + if (selected) { + owner.OnSelectedItemChanged(EventArgs.Empty); //we do this because set_SelectedIndex does this. (for consistency) + owner.OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + } + + } // end ObjectCollection + + /// + /// + [ComVisible(true)] + public class ChildAccessibleObject : AccessibleObject { + + ComboBox owner; + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public ChildAccessibleObject(ComboBox owner, IntPtr handle) { + Debug.Assert(owner != null && owner.Handle != IntPtr.Zero, "ComboBox's handle hasn't been created"); + + this.owner = owner; + UseStdAccessibleObjects(handle); + } + + /// + public override string Name { + get { + return owner.AccessibilityObject.Name; + } + } + } + + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ComboBoxAccessibleObject : ControlAccessibleObject { + + private const int COMBOBOX_ACC_ITEM_INDEX = 1; + + public ComboBoxAccessibleObject(Control ownerControl) + : base(ownerControl) { + } + + internal override string get_accNameInternal(object childID) { + this.ValidateChildID(ref childID); + + if (childID != null && ((int)childID) == COMBOBOX_ACC_ITEM_INDEX) { + return this.Name; + } + else { + return base.get_accNameInternal(childID); + } + } + + internal override string get_accKeyboardShortcutInternal(object childID) { + this.ValidateChildID(ref childID); + if (childID != null && ((int)childID) == COMBOBOX_ACC_ITEM_INDEX) { + return this.KeyboardShortcut; + } else { + return base.get_accKeyboardShortcutInternal(childID); + } + } + } + + /// + /// + [ComVisible(true)] + internal class ComboBoxExAccessibleObject : ComboBoxAccessibleObject { + + private ComboBox ownerItem = null; + + private void ComboBoxDefaultAction(bool expand) { + if (ownerItem.DroppedDown != expand) { + ownerItem.DroppedDown = expand; + } + } + + public ComboBoxExAccessibleObject(ComboBox ownerControl) + : base(ownerControl) { + ownerItem = ownerControl; + } + + internal override bool IsIAccessibleExSupported() { + if (ownerItem != null) { + return true; + } + return base.IsIAccessibleExSupported(); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + if (ownerItem.DropDownStyle == ComboBoxStyle.Simple) { + return false; + } + return true; + } + else { + if (patternId == NativeMethods.UIA_ValuePatternId) { + if (ownerItem.DropDownStyle == ComboBoxStyle.DropDownList) { + // NOTE: Initially the Value pattern is disabled for DropDownList in managed code, + // but despite of this MSAA provides true (when Level < 3). When UIA mode is enabled, + // Value pattern becomes disabled for the DropDownList and brings inconsistency. + // At this time keeping 'return false;' commented out and preserving Value pattern + // enabled in all cases: Level < 3 (by MSAA) and Level3 (by UIA). + // return false; + return AccessibilityImprovements.Level3; + } + return true; + } + } + return base.IsPatternSupported(patternId); + } + + internal override int[] RuntimeId { + get { + if (ownerItem != null) { + // we need to provide a unique ID + // others are implementing this in the same manner + // first item is static - 0x2a (RuntimeIDFirstItem) + // second item can be anything, but here it is a hash + + var runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)ownerItem.Handle; + runtimeId[2] = ownerItem.GetHashCode(); + return runtimeId; + } + + return base.RuntimeId; + } + } + + internal override object GetPropertyValue(int propertyID) { + + switch (propertyID) { + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId: + return (object)this.IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + return (object)this.IsPatternSupported(NativeMethods.UIA_ValuePatternId); + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override void Expand() { + ComboBoxDefaultAction(true); + } + + internal override void Collapse() { + ComboBoxDefaultAction(false); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return ownerItem.DroppedDown == true ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + } + + /// + /// Represents the ComboBox item accessible object. + /// + [ComVisible(true)] + internal class ComboBoxItemAccessibleObject : AccessibleObject { + + private ComboBox _owningComboBox; + private object _owningItem; + private IAccessible _systemIAccessible; + + /// + /// Initializes new instance of ComboBox item accessible object. + /// + /// The owning ComboBox. + /// The owning ComboBox item. + public ComboBoxItemAccessibleObject(ComboBox owningComboBox, object owningItem) { + _owningComboBox = owningComboBox; + _owningItem = owningItem; + + _systemIAccessible = _owningComboBox.ChildListAccessibleObject.GetSystemIAccessibleInternal(); + } + + /// + /// Gets the ComboBox Item bounds. + /// + public override Rectangle Bounds { + get { + var listAccessibleObject = _owningComboBox.ChildListAccessibleObject; + int currentIndex = GetCurrentIndex(); + + var parentRect = listAccessibleObject.BoundingRectangle; + int left = parentRect.Left; + int top = parentRect.Top + _owningComboBox.ItemHeight * currentIndex; + int width = parentRect.Width; + int height = _owningComboBox.ItemHeight; + + return new Rectangle(left, top, width, height); + } + } + + /// + /// Gets the ComboBox item default action. + /// + public override string DefaultAction { + get { + return _systemIAccessible.accDefaultAction[GetChildId()]; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return _owningComboBox.ChildListAccessibleObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + int currentIndex = GetCurrentIndex(); + var comboBoxChildListUiaProvider = _owningComboBox.ChildListAccessibleObject as ComboBoxChildListUiaProvider; + if (comboBoxChildListUiaProvider != null) { + int itemsCount = comboBoxChildListUiaProvider.GetChildFragmentCount(); + int nextItemIndex = currentIndex + 1; + if (itemsCount > nextItemIndex) { + return comboBoxChildListUiaProvider.GetChildFragment(nextItemIndex); + } + } + break; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + currentIndex = GetCurrentIndex(); + comboBoxChildListUiaProvider = _owningComboBox.ChildListAccessibleObject as ComboBoxChildListUiaProvider; + if (comboBoxChildListUiaProvider != null) { + var itemsCount = comboBoxChildListUiaProvider.GetChildFragmentCount(); + int previousItemIndex = currentIndex - 1; + if (previousItemIndex >= 0) { + return comboBoxChildListUiaProvider.GetChildFragment(previousItemIndex); + } + } + + break; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningComboBox.AccessibilityObject; + } + } + + private int GetCurrentIndex() { + return _owningComboBox.Items.IndexOf(_owningItem); + } + + internal override int GetChildId() { + return GetCurrentIndex() + 1; // Index is zero-based, Child ID is 1-based. + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return BoundingRectangle; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ListItemControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut ?? string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owningComboBox.Focused && _owningComboBox.SelectedIndex == GetCurrentIndex(); + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owningComboBox.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsControlElementPropertyId: + return true; + case NativeMethods.UIA_IsContentElementPropertyId: + return true; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_IsSelectionItemPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_SelectionItemIsSelectedPropertyId: + return (State & AccessibleStates.Selected) != 0; + case NativeMethods.UIA_SelectionItemSelectionContainerPropertyId: + return _owningComboBox.ChildListAccessibleObject; + + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the help text. + /// + public override string Help { + get { + return _systemIAccessible.accHelp[GetChildId()]; + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_InvokePatternId || + patternId == NativeMethods.UIA_SelectionItemPatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets or sets the accessible name. + /// + public override string Name { + get { + if (_owningComboBox != null) { + return _owningItem.ToString(); + } + + return base.Name; + } + + set { + base.Name = value; + } + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + return (AccessibleRole)_systemIAccessible.get_accRole(GetChildId()); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[4]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owningComboBox.Handle; + runtimeId[2] = _owningComboBox.GetListNativeWindowRuntimeIdPart(); + runtimeId[3] = _owningItem.GetHashCode(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + return (AccessibleStates)_systemIAccessible.get_accState(GetChildId()); + } + } + + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + + internal override void SelectItem() { + _owningComboBox.SelectedIndex = GetCurrentIndex(); + + SafeNativeMethods.InvalidateRect(new HandleRef(this, _owningComboBox.GetListHandle()), null, false); + } + + internal override void AddToSelection() { + SelectItem(); + } + + internal override void RemoveFromSelection() { + // Do nothing, C++ implementation returns UIA_E_INVALIDOPERATION 0x80131509 + } + + internal override bool IsItemSelected { + get { + return (State & AccessibleStates.Selected) != 0; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple ItemSelectionContainer { + get { + return _owningComboBox.ChildListAccessibleObject; + } + } + } + + internal class ComboBoxItemAccessibleObjectCollection : Hashtable { + + private ComboBox _owningComboBoxBox; + + public ComboBoxItemAccessibleObjectCollection(ComboBox owningComboBoxBox) { + _owningComboBoxBox = owningComboBoxBox; + } + + public override object this[object key] { + get { + if (!ContainsKey(key)) { + var itemAccessibleObject = new ComboBoxItemAccessibleObject(_owningComboBoxBox, key); + base[key] = itemAccessibleObject; + } + + return base[key]; + } + + set { + base[key] = value; + } + } + } + + /// + /// ComboBox control accessible object for AccessibilityImprovements of Level 3 with UI Automation provider functionality. + /// This inherits from the base ComboBoxExAccessibleObject and ComboBoxAccessibleObject to have all base functionality. + /// + [ComVisible(true)] + internal class ComboBoxUiaProvider : ComboBoxExAccessibleObject { + private ComboBoxChildDropDownButtonUiaProvider _dropDownButtonUiaProvider; + private ComboBoxItemAccessibleObjectCollection _itemAccessibleObjects; + private ComboBox _owningComboBox; + + /// + /// Initializes new instance of ComboBoxUiaProvider. + /// + /// The owning ComboBox control. + public ComboBoxUiaProvider(ComboBox owningComboBox) : base(owningComboBox) { + _owningComboBox = owningComboBox; + _itemAccessibleObjects = new ComboBoxItemAccessibleObjectCollection(owningComboBox); + } + + /// + /// Gets the collection of item accessible objects. + /// + public ComboBoxItemAccessibleObjectCollection ItemAccessibleObjects { + get { + return _itemAccessibleObjects; + } + } + + /// + /// Gets the DropDown button accessible object. (UI Automation provider) + /// + public ComboBoxChildDropDownButtonUiaProvider DropDownButtonUiaProvider { + get { + return (_dropDownButtonUiaProvider ?? new ComboBoxChildDropDownButtonUiaProvider(_owningComboBox, _owningComboBox.Handle)); + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild) { + return GetChildFragment(0); + } + else if (direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd) { + if (hwnd == _owningComboBox.childEdit.Handle) { + return _owningComboBox.ChildEditAccessibleObject; + } + else if ( + hwnd == _owningComboBox.childListBox.Handle || + hwnd == _owningComboBox.dropDownHandle) { + return _owningComboBox.ChildListAccessibleObject; + } + + return null; + } + + /// + /// Gets the accessible child corresponding to the specified index. + /// + /// The child index. + /// The accessible child. + /// + /// GetChild method should be unchanged to not break the MSAA scenarios. + /// + internal AccessibleObject GetChildFragment(int index) { + if (_owningComboBox.DropDownStyle == ComboBoxStyle.DropDownList) { + if (index == 0) { + return _owningComboBox.ChildTextAccessibleObject; + } + + index--; + } + + if (index == 0 && _owningComboBox.DropDownStyle != ComboBoxStyle.Simple) { + return DropDownButtonUiaProvider; + } + + return null; + } + + /// + /// Gets the number of children belonging to an accessible object. + /// + /// The number of children. + /// + /// GetChildCount method should be unchanged to not break the MSAA scenarios. + /// + internal int GetChildFragmentCount() { + int childFragmentCount = 0; + + if (_owningComboBox.DropDownStyle == ComboBoxStyle.DropDownList) { + childFragmentCount++; // Text instead of edit for style is DropDownList but not DropDown. + } + + if (_owningComboBox.DropDownStyle != ComboBoxStyle.Simple) { + childFragmentCount++; // DropDown button. + } + + return childFragmentCount; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ComboBoxControlTypeId; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owningComboBox.Focused; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _owningComboBox.Handle; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal void ResetListItemAccessibleObjects() { + _itemAccessibleObjects.Clear(); + } + + internal void SetComboBoxItemFocus() { + var selectedItem = _owningComboBox.SelectedItem; + if (selectedItem == null) { + return; + } + + var itemAccessibleObject = ItemAccessibleObjects[selectedItem] as ComboBoxItemAccessibleObject; + if (itemAccessibleObject != null) { + itemAccessibleObject.SetFocus(); + } + } + + internal void SetComboBoxItemSelection() { + var selectedItem = _owningComboBox.SelectedItem; + if (selectedItem == null) { + return; + } + + var itemAccessibleObject = ItemAccessibleObjects[selectedItem] as ComboBoxItemAccessibleObject; + if (itemAccessibleObject != null) { + itemAccessibleObject.RaiseAutomationEvent(NativeMethods.UIA_SelectionItem_ElementSelectedEventId); + } + } + + internal override void SetFocus() { + base.SetFocus(); + + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + + /// + /// Represents the ComboBox's child (inner) edit native window control accessible object with UI Automation provider functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildEditUiaProvider : ChildAccessibleObject { + private const string COMBO_BOX_EDIT_AUTOMATION_ID = "1001"; + + private ComboBox _owner; + private IntPtr _handle; + + /// + /// Initializes new instance of ComboBoxChildEditUiaProvider. + /// + /// The ComboBox owning control. + /// The child edit native window handle. + public ComboBoxChildEditUiaProvider(ComboBox owner, IntPtr childEditControlhandle) : base(owner, childEditControlhandle) { + _owner = owner; + _handle = childEditControlhandle; + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + Debug.WriteLine("Edit parent " + _owner.AccessibilityObject.GetPropertyValue(NativeMethods.UIA_ControlTypePropertyId)); + return _owner.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + if (_owner.DropDownStyle == ComboBoxStyle.Simple) { + return null; + } + + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // DropDown button is next; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + var firstComboBoxChildFragment = comboBoxUiaProvider.GetChildFragment(0); + if (RuntimeId != firstComboBoxChildFragment.RuntimeId) { + return firstComboBoxChildFragment; + } + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_EditControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_AutomationIdPropertyId: + return COMBO_BOX_EDIT_AUTOMATION_ID; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _handle; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, _handle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override int ProviderOptions { + get { + return (int)UnsafeNativeMethods.ProviderOptions.ClientSideProvider; + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = GetHashCode(); + + return runtimeId; + } + } + } + + /// + /// Represents the ComboBox's child (inner) list native window control accessible object with UI Automation provider functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildListUiaProvider : ChildAccessibleObject { + private const string COMBO_BOX_LIST_AUTOMATION_ID = "1000"; + + private ComboBox _owningComboBox; + private IntPtr _childListControlhandle; + + /// + /// Initializes new instance of ComboBoxChildListUiaProvider. + /// + /// + /// + public ComboBoxChildListUiaProvider(ComboBox owningComboBox, IntPtr childListControlhandle) : base(owningComboBox, childListControlhandle) { + _owningComboBox = owningComboBox; + _childListControlhandle = childListControlhandle; + } + + /// + /// Return the child object at the given screen coordinates. + /// + /// X coordinate. + /// Y coordinate. + /// The accessible object of corresponding element in the provided coordinates. + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + if (AccessibilityImprovements.Level3) { + var systemIAccessible = GetSystemIAccessibleInternal(); + if (systemIAccessible != null) { + object result = systemIAccessible.accHitTest((int)x, (int)y); + if (result is int) { + int childId = (int)result; + return GetChildFragment(childId - 1); + } + else { + return null; + } + } + } + + return base.ElementProviderFromPoint(x, y); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetChildFragment(0); + case UnsafeNativeMethods.NavigateDirection.LastChild: + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningComboBox.AccessibilityObject; + } + } + + public AccessibleObject GetChildFragment(int index) { + if (index < 0 || index >= _owningComboBox.Items.Count) { + return null; + } + + var item = _owningComboBox.Items[index]; + var comboBoxUiaProvider = _owningComboBox.AccessibilityObject as ComboBoxUiaProvider; + return comboBoxUiaProvider.ItemAccessibleObjects[item] as AccessibleObject; + } + + public int GetChildFragmentCount() { + return _owningComboBox.Items.Count; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ListControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return false; // Narrator should keep the keyboard focus on th ComboBox itself but not on the DropDown. + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owningComboBox.Enabled; + case NativeMethods.UIA_AutomationIdPropertyId: + return COMBO_BOX_LIST_AUTOMATION_ID; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_NativeWindowHandlePropertyId: + return _childListControlhandle; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_IsSelectionPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_SelectionCanSelectMultiplePropertyId: + return CanSelectMultiple; + case NativeMethods.UIA_SelectionIsSelectionRequiredPropertyId: + return IsSelectionRequired; + + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return GetFocused(); + } + + public override AccessibleObject GetFocused() { + int selectedIndex = _owningComboBox.SelectedIndex; + return GetChildFragment(selectedIndex); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetSelection() { + int selectedIndex = _owningComboBox.SelectedIndex; + + AccessibleObject itemAccessibleObject = GetChildFragment(selectedIndex); + if (itemAccessibleObject != null) { + return new UnsafeNativeMethods.IRawElementProviderSimple[] { + itemAccessibleObject + }; + } + + return new UnsafeNativeMethods.IRawElementProviderSimple[0]; + } + + internal override bool CanSelectMultiple { + get { + return false; + } + } + + internal override bool IsSelectionRequired { + get { + return true; + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_SelectionPatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, _childListControlhandle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owningComboBox.Handle; + runtimeId[2] = _owningComboBox.GetListNativeWindowRuntimeIdPart(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Focusable; + if (_owningComboBox.Focused) { + state |= AccessibleStates.Focused; + } + + return state; + } + } + } + + /// + /// Represents the ComboBox's child text (is used instead of inner Edit when style is DropDownList but not DropDown) accessible object. + /// + [ComVisible(true)] + internal class ComboBoxChildTextUiaProvider : AccessibleObject { + + private const int COMBOBOX_TEXT_ACC_ITEM_INDEX = 1; + + private ComboBox _owner; + + /// + /// Initializes new instance of ComboBoxChildTextUiaProvider. + /// + /// The owning ComboBox control. + public ComboBoxChildTextUiaProvider(ComboBox owner) { + _owner = owner; + } + + /// + /// Gets the bounds. + /// + public override Rectangle Bounds { + get { + return _owner.AccessibilityObject.Bounds; + } + } + + /// + /// Gets the child ID. + /// + /// The child ID. + internal override int GetChildId() { + return COMBOBOX_TEXT_ACC_ITEM_INDEX; + } + + /// + /// Gets or sets the accessible Name of ComboBox's child text element. + /// + public override string Name { + get { + return _owner.AccessibilityObject.Name ?? string.Empty; + } + set { + // Do nothing. + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return _owner.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // DropDown button is next; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + var firstComboBoxChildFragment = comboBoxUiaProvider.GetChildFragment(0); + if (RuntimeId != firstComboBoxChildFragment.RuntimeId) { + return firstComboBoxChildFragment; + } + } + + return null; + default: + return base.FragmentNavigate(direction); + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return Bounds; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_TextControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[5]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owner.Handle; + runtimeId[2] = _owner.GetHashCode(); + runtimeId[3] = GetHashCode(); + runtimeId[4] = GetChildId(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Focusable; + if (_owner.Focused) { + state |= AccessibleStates.Focused; + } + + return state; + } + } + } + + /// + /// Represents the ComboBox child (inner) DropDown button accessible object with UI Automation functionality. + /// + [ComVisible(true)] + internal class ComboBoxChildDropDownButtonUiaProvider : AccessibleObject { + + private const int COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX = 2; + private ComboBox _owner; + + /// + /// Initializes new instance of ComboBoxChildDropDownButtonUiaProvider. + /// + /// The owning ComboBox control. + /// The owning ComboBox control's handle. + public ComboBoxChildDropDownButtonUiaProvider(ComboBox owner, IntPtr comboBoxControlhandle) { + _owner = owner; + UseStdAccessibleObjects(comboBoxControlhandle); + } + + /// + /// Gets or sets the accessible Name of ComboBox's child DropDown button. ("Open" or "Close" depending on stat of the DropDown) + /// + public override string Name { + get { + return get_accNameInternal(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + set { + var systemIAccessible = GetSystemIAccessibleInternal(); + systemIAccessible.set_accName(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX, value); + } + } + + /// + /// Gets the DropDown button bounds. + /// + public override Rectangle Bounds { + get { + int left; + int top; + int width; + int height; + var systemIAccessible = GetSystemIAccessibleInternal(); + systemIAccessible.accLocation(out left, out top, out width, out height, COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + return new Rectangle(left, top, width, height); + } + } + + /// + /// Gets the DropDown button default action. + /// + public override string DefaultAction { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accDefaultAction[COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX]; + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent) { + return _owner.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.PreviousSibling) { + var comboBoxUiaProvider = _owner.AccessibilityObject as ComboBoxUiaProvider; + if (comboBoxUiaProvider != null) { + int comboBoxChildFragmentCount = comboBoxUiaProvider.GetChildFragmentCount(); + if (comboBoxChildFragmentCount > 1) { // Text or edit is previous; + return comboBoxUiaProvider.GetChildFragment(comboBoxChildFragmentCount - 1); + } + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owner.AccessibilityObject; + } + } + + /// + /// Gets the child accessible object ID. + /// + /// The child accessible object ID. + internal override int GetChildId() { + return COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX; + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return BoundingRectangle; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ButtonControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owner.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owner.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the help text. + /// + public override string Help { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accHelp[COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX]; + } + } + + /// + /// Gets the keyboard shortcut. + /// + public override string KeyboardShortcut { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.get_accKeyboardShortcut(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_InvokePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleRole)systemIAccessible.get_accRole(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[5]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owner.Handle; + runtimeId[2] = _owner.GetHashCode(); + + // Made up constant from MSAA proxy. When MSAA proxy is used as an accessibility provider, + // the similar Runtime ID is returned (for consistency purpose) + const int generatedRuntimeId = 61453; + runtimeId[3] = generatedRuntimeId; + runtimeId[4] = COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX; + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleStates)systemIAccessible.get_accState(COMBOBOX_DROPDOWN_BUTTON_ACC_ITEM_INDEX); + } + } + } + + /// + /// This subclasses an autocomplete window so that we can determine if control is inside the AC wndproc. + /// + private sealed class ACNativeWindow : NativeWindow { + static internal int inWndProcCnt; + //this hashtable can contain null for those ACWindows we find, but are sure are not ours. + static private Hashtable ACWindows = new Hashtable(); + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") + ] + internal ACNativeWindow(IntPtr acHandle) { + Debug.Assert(!ACWindows.ContainsKey(acHandle)); + this.AssignHandle(acHandle); + ACWindows.Add(acHandle, this); + UnsafeNativeMethods.EnumChildWindows(new HandleRef(this, acHandle), + new NativeMethods.EnumChildrenCallback(ACNativeWindow.RegisterACWindowRecursive), + NativeMethods.NullHandleRef); + } + + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + private static bool RegisterACWindowRecursive(IntPtr handle, IntPtr lparam) { + if (!ACWindows.ContainsKey(handle)) { + ACNativeWindow newAC = new ACNativeWindow(handle); + } + return true; + } + + internal bool Visible { + get { + return SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle)); + } + } + + static internal bool AutoCompleteActive { + get { + if (inWndProcCnt>0) { + return true; + } + foreach(object o in ACWindows.Values) { + ACNativeWindow window = o as ACNativeWindow; + if (window != null && window.Visible) { + return true; + } + } + return false; + } + } + + protected override void WndProc(ref Message m) { + inWndProcCnt++; + try { + base.WndProc(ref m); + } + finally { + inWndProcCnt--; + } + + if (m.Msg == NativeMethods.WM_NCDESTROY) { + Debug.Assert(ACWindows.ContainsKey(this.Handle)); + ACWindows.Remove(this.Handle); //so we do not leak ac windows. + } + } + + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + internal static void RegisterACWindow(IntPtr acHandle, bool subclass) { + if (subclass && ACWindows.ContainsKey(acHandle)) { + if (ACWindows[acHandle] == null) { + ACWindows.Remove(acHandle); //if an external handle got destroyed, dont let it stop us. + } + } + + if (!ACWindows.ContainsKey(acHandle)) { + if (subclass) { + ACNativeWindow newAC = new ACNativeWindow(acHandle); + } + else { + ACWindows.Add(acHandle, null); + } + } + } + + /// + /// This method clears out null entries so we get a clean BEFORE and AFTER snapshot + /// null entries are ACWindows that belong to someone else. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] // Perfectly acceptible when dealing with PropertyDescriptors + internal static void ClearNullACWindows() { + ArrayList nulllist = new ArrayList(); + foreach (DictionaryEntry e in ACWindows) { + if (e.Value == null) { + nulllist.Add(e.Key); + } + } + foreach (IntPtr handle in nulllist) { + ACWindows.Remove(handle); + } + } + } + + /// + /// This finds all autcomplete windows that belong to the active thread. + /// + private class AutoCompleteDropDownFinder { + private const int MaxClassName = 256; + private const string AutoCompleteClassName = "Auto-Suggest Dropdown"; + bool shouldSubClass = false; //nonstatic + + internal void FindDropDowns() { + FindDropDowns(true); + } + + internal void FindDropDowns(bool subclass) { + if (!subclass) { + //generating a before snapshot -- lets lose the null handles + ACNativeWindow.ClearNullACWindows(); + } + // Look for a popped up dropdown + shouldSubClass = subclass; + UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), new NativeMethods.EnumThreadWindowsCallback(this.Callback), new HandleRef(null, IntPtr.Zero)); + } + + private bool Callback(IntPtr hWnd, IntPtr lParam) { + HandleRef hRef = new HandleRef(null, hWnd); + + // Check class name and see if it's visible + if (GetClassName(hRef) == AutoCompleteClassName) { + ACNativeWindow.RegisterACWindow(hRef.Handle, shouldSubClass); + } + + return true; + } + + static string GetClassName(HandleRef hRef) { + StringBuilder sb = new StringBuilder(MaxClassName); + UnsafeNativeMethods.GetClassName(hRef, sb, MaxClassName); + return sb.ToString(); + } + } + + + private FlatComboAdapter FlatComboBoxAdapter { + get { + FlatComboAdapter comboAdapter = Properties.GetObject(PropFlatComboAdapter) as FlatComboAdapter; + if (comboAdapter == null || !comboAdapter.IsValid(this)) { + comboAdapter = CreateFlatComboAdapterInstance(); + Properties.SetObject(PropFlatComboAdapter, comboAdapter); + } + return comboAdapter; + } + } + + internal virtual FlatComboAdapter CreateFlatComboAdapterInstance() { + return new FlatComboAdapter(this,/*smallButton=*/false); + } + + + internal class FlatComboAdapter { + Rectangle outerBorder; + Rectangle innerBorder; + Rectangle innerInnerBorder; + internal Rectangle dropDownRect; + Rectangle whiteFillRect; + Rectangle clientRect; + + RightToLeft origRightToLeft; // The combo box's RTL value when we were created + + private const int WhiteFillRectWidth = 5; // used for making the button look smaller than it is + + private static bool isScalingInitialized = false; + private static int OFFSET_2PIXELS = 2; + protected static int Offset2Pixels = OFFSET_2PIXELS; + + public FlatComboAdapter(ComboBox comboBox, bool smallButton) { + // adapter is re-created when combobox is resized, see IsValid method, thus we don't need to handle DPI changed explicitly + if ((!isScalingInitialized && DpiHelper.IsScalingRequired) || DpiHelper.EnableDpiChangedMessageHandling) { + Offset2Pixels = comboBox.LogicalToDeviceUnits(OFFSET_2PIXELS); + isScalingInitialized = true; + } + + clientRect = comboBox.ClientRectangle; + int dropDownButtonWidth = SystemInformation.GetHorizontalScrollBarArrowWidthForDpi(comboBox.deviceDpi); + outerBorder = new Rectangle(clientRect.Location, new Size(clientRect.Width - 1, clientRect.Height - 1)); + innerBorder = new Rectangle(outerBorder.X + 1, outerBorder.Y + 1, outerBorder.Width - dropDownButtonWidth - 2, outerBorder.Height - 2); + innerInnerBorder = new Rectangle(innerBorder.X + 1, innerBorder.Y + 1, innerBorder.Width - 2, innerBorder.Height - 2); + dropDownRect = new Rectangle(innerBorder.Right + 1, innerBorder.Y, dropDownButtonWidth, innerBorder.Height+1); + + + // fill in several pixels of the dropdown rect with white so that it looks like the combo button is thinner. + if (smallButton) { + whiteFillRect = dropDownRect; + whiteFillRect.Width = WhiteFillRectWidth; + dropDownRect.X += WhiteFillRectWidth; + dropDownRect.Width -= WhiteFillRectWidth; + } + + origRightToLeft = comboBox.RightToLeft; + + + if (origRightToLeft == RightToLeft.Yes) { + innerBorder.X = clientRect.Width - innerBorder.Right; + innerInnerBorder.X = clientRect.Width - innerInnerBorder.Right; + dropDownRect.X = clientRect.Width - dropDownRect.Right; + whiteFillRect.X = clientRect.Width - whiteFillRect.Right + 1; // since we're filling, we need to move over to the next px. + } + + } + + public bool IsValid(ComboBox combo) { + return (combo.ClientRectangle == clientRect && combo.RightToLeft == origRightToLeft); + } + + /// + /// + /// Paints over the edges of the combo box to make it appear flat. + /// + public virtual void DrawFlatCombo(ComboBox comboBox, Graphics g) { + if (comboBox.DropDownStyle == ComboBoxStyle.Simple){ + return; + } + + Color outerBorderColor = GetOuterBorderColor(comboBox); + Color innerBorderColor = GetInnerBorderColor(comboBox); + bool rightToLeft = comboBox.RightToLeft == RightToLeft.Yes; + + // draw the drop down + DrawFlatComboDropDown(comboBox, g, dropDownRect); + + // when we are disabled there is one line of color that seems to eek through if backcolor is set + // so lets erase it. + if (!LayoutUtils.IsZeroWidthOrHeight(whiteFillRect)) { + // fill in two more pixels with white so it looks smaller. + using (Brush b = new SolidBrush(innerBorderColor)) { + g.FillRectangle(b, whiteFillRect); + } + } + + + + // Draw the outer border + if (outerBorderColor.IsSystemColor) { + Pen outerBorderPen = SystemPens.FromSystemColor(outerBorderColor); + g.DrawRectangle(outerBorderPen, outerBorder); + if (rightToLeft) { + g.DrawRectangle(outerBorderPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(outerBorderPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + } + else { + using (Pen outerBorderPen = new Pen(outerBorderColor)) { + g.DrawRectangle(outerBorderPen, outerBorder); + if (rightToLeft) { + g.DrawRectangle(outerBorderPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(outerBorderPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + } + } + + // Draw the inner border + if (innerBorderColor.IsSystemColor) { + Pen innerBorderPen = SystemPens.FromSystemColor(innerBorderColor); + g.DrawRectangle(innerBorderPen, innerBorder); + g.DrawRectangle(innerBorderPen, innerInnerBorder); + } + else { + using (Pen innerBorderPen = new Pen(innerBorderColor)) { + g.DrawRectangle(innerBorderPen, innerBorder); + g.DrawRectangle(innerBorderPen, innerInnerBorder); + } + } + + + + // Draw a dark border around everything if we're in popup mode + if ((!comboBox.Enabled) ||(comboBox.FlatStyle == FlatStyle.Popup)) { + bool focused = comboBox.ContainsFocus || comboBox.MouseIsOver; + Color borderPenColor = GetPopupOuterBorderColor(comboBox, focused); + + using (Pen borderPen = new Pen(borderPenColor)) { + + Pen innerPen = (comboBox.Enabled) ? borderPen : SystemPens.Control; + + // around the dropdown + if (rightToLeft) { + g.DrawRectangle(innerPen, new Rectangle(outerBorder.X, outerBorder.Y, dropDownRect.Width + 1, outerBorder.Height)); + } + else { + g.DrawRectangle(innerPen, new Rectangle(dropDownRect.X, outerBorder.Y, outerBorder.Right - dropDownRect.X, outerBorder.Height)); + } + + // around the whole combobox. + g.DrawRectangle(borderPen, outerBorder); + + } + } + + } + + /// + /// + /// Paints over the edges of the combo box to make it appear flat. + /// + + protected virtual void DrawFlatComboDropDown(ComboBox comboBox, Graphics g, Rectangle dropDownRect) { + + g.FillRectangle(SystemBrushes.Control, dropDownRect); + + Brush brush = (comboBox.Enabled) ? SystemBrushes.ControlText : SystemBrushes.ControlDark; + + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + if (origRightToLeft == RightToLeft.Yes) { + // if the width is odd - favor pushing it over one pixel left. + middle.X -= (dropDownRect.Width % 2); + } + else { + // if the width is odd - favor pushing it over one pixel right. + middle.X += (dropDownRect.Width % 2); + } + + g.FillPolygon(brush, new Point[] { + new Point(middle.X - Offset2Pixels, middle.Y - 1), + new Point(middle.X + Offset2Pixels + 1, middle.Y - 1), + new Point(middle.X, middle.Y + Offset2Pixels) + }); + } + + protected virtual Color GetOuterBorderColor(ComboBox comboBox) { + return (comboBox.Enabled) ? SystemColors.Window : SystemColors.ControlDark; + } + + protected virtual Color GetPopupOuterBorderColor(ComboBox comboBox, bool focused) { + if (!comboBox.Enabled) { + return SystemColors.ControlDark; + } + return (focused) ? SystemColors.ControlDark : SystemColors.Window; + } + + protected virtual Color GetInnerBorderColor(ComboBox comboBox) { + return (comboBox.Enabled) ? comboBox.BackColor : SystemColors.Control; + } + + // this eliminates flicker by removing the pieces we're going to paint ourselves from + // the update region. Note the UpdateRegionBox is the bounding box of the actual update region. + // this is just here so we can quickly eliminate rectangles that arent in the update region. + public void ValidateOwnerDrawRegions(ComboBox comboBox, Rectangle updateRegionBox) { + NativeMethods.RECT validRect; + if (comboBox != null) { return; } + Rectangle topOwnerDrawArea = new Rectangle(0,0,comboBox.Width, innerBorder.Top); + Rectangle bottomOwnerDrawArea = new Rectangle(0,innerBorder.Bottom,comboBox.Width, comboBox.Height-innerBorder.Bottom); + Rectangle leftOwnerDrawArea = new Rectangle(0,0,innerBorder.Left, comboBox.Height); + Rectangle rightOwnerDrawArea = new Rectangle(innerBorder.Right,0,comboBox.Width - innerBorder.Right,comboBox.Height); + + if (topOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(topOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (bottomOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(bottomOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (leftOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(leftOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + if (rightOwnerDrawArea.IntersectsWith(updateRegionBox)) { + validRect = new NativeMethods.RECT(rightOwnerDrawArea); + SafeNativeMethods.ValidateRect(new HandleRef(comboBox, comboBox.Handle), ref validRect); + } + + } + } + + /// + /// Represents the ComboBox child native window type. + /// + internal enum ChildWindowType { + ListBox, + Edit, + DropDownList + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComboBoxRenderer.cs b/WindowsForms/Managed/System/WinForms/ComboBoxRenderer.cs new file mode 100644 index 000000000..3f18d688c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComboBoxRenderer.cs @@ -0,0 +1,177 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + + using System; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Internal; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + + + /// + /// + /// + /// This is a rendering class for the ComboBox control. + /// + /// + public sealed class ComboBoxRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement ComboBoxElement = VisualStyleElement.ComboBox.DropDownButton.Normal; + private static readonly VisualStyleElement TextBoxElement = VisualStyleElement.TextBox.TextEdit.Normal; + + //cannot instantiate + private ComboBoxRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + private static void DrawBackground(Graphics g, Rectangle bounds, ComboBoxState state) { + visualStyleRenderer.DrawBackground(g, bounds); + //for disabled comboboxes, comctl does not use the window backcolor, so + // we don't refill here in that case. + if (state != ComboBoxState.Disabled) { + Color windowColor = visualStyleRenderer.GetColor(ColorProperty.FillColor); + if (windowColor != SystemColors.Window) { + Rectangle fillRect = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + fillRect.Inflate(-2, -2); + //then we need to re-fill the background. + g.FillRectangle(SystemBrushes.Window, fillRect); + } + } + } + + /// + /// + /// + /// Renders the textbox part of a ComboBox control. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTextBox(Graphics g, Rectangle bounds, ComboBoxState state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + else { + visualStyleRenderer.SetParameters(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + + DrawBackground(g, bounds, state); + } + + /// + /// + /// + /// Renders the textbox part of a ComboBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string comboBoxText, Font font, ComboBoxState state) { + DrawTextBox(g, bounds, comboBoxText, font, TextFormatFlags.TextBoxControl, state); + } + + /// + /// + /// + /// Renders the textbox part of a ComboBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string comboBoxText, Font font, Rectangle textBounds, ComboBoxState state) { + DrawTextBox(g, bounds, comboBoxText, font, textBounds, TextFormatFlags.TextBoxControl, state); + } + + /// + /// + /// + /// Renders the textbox part of a ComboBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string comboBoxText, Font font, TextFormatFlags flags, ComboBoxState state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + else { + visualStyleRenderer.SetParameters(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + + Rectangle textBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + textBounds.Inflate(-2,-2); + DrawTextBox(g, bounds, comboBoxText, font, textBounds, flags, state); + } + + /// + /// + /// + /// Renders the textbox part of a ComboBox control. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTextBox(Graphics g, Rectangle bounds, string comboBoxText, Font font, Rectangle textBounds, TextFormatFlags flags, ComboBoxState state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + else { + visualStyleRenderer.SetParameters(TextBoxElement.ClassName, TextBoxElement.Part, (int)state); + } + + DrawBackground(g, bounds, state); + Color textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + TextRenderer.DrawText(g, comboBoxText, font, textBounds, textColor, flags); + } + + /// + /// + /// + /// Renders a ComboBox drop-down button. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxState state) { + DrawDropDownButtonForHandle(g, bounds, state, IntPtr.Zero); + } + + /// + /// Renders a ComboBox drop-down button in per-monitor scenario. + /// + /// graphics object + /// dropdown button bounds + /// state + /// handle of the control + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + internal static void DrawDropDownButtonForHandle(Graphics g, Rectangle bounds, ComboBoxState state, IntPtr handle) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxElement.ClassName, ComboBoxElement.Part, (int)state); + } + else { + visualStyleRenderer.SetParameters(ComboBoxElement.ClassName, ComboBoxElement.Part, (int)state); + } + + visualStyleRenderer.DrawBackground(g, bounds, handle); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComboBoxStyle.cs b/WindowsForms/Managed/System/WinForms/ComboBoxStyle.cs new file mode 100644 index 000000000..150c66a37 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComboBoxStyle.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the + /// style. + /// + /// + /// + public enum ComboBoxStyle { + + /// + /// + /// + /// The text portion is editable. The list portion is + /// always visible. + /// + /// + /// + Simple = 0, + + /// + /// + /// + /// + /// The text portion is editable. The user must click the arrow button to display + /// the list portion. + /// + /// + /// + DropDown = 1, + + /// + /// + /// + /// The + /// user cannot directly edit the text portion. The user must click the arrow button to + /// display the list portion. + /// + /// + /// + DropDownList = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Command.cs b/WindowsForms/Managed/System/WinForms/Command.cs new file mode 100644 index 000000000..93353045e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Command.cs @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.ComponentModel; + using System.Windows.Forms; + using System.Drawing; + + using Microsoft.Win32; + + /// + /// + internal class Command : WeakReference { + + private static Command[] cmds; + private static int icmdTry; + private static object internalSyncObject = new object(); + private const int idMin = 0x00100; + private const int idLim = 0x10000; + + internal int id; + + public Command(ICommandExecutor target) + : base(target, false) { + AssignID(this); + } + + public virtual int ID { + get { + return id; + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] + protected static void AssignID(Command cmd) { + lock(internalSyncObject) { + int icmd; + + if (null == cmds) { + cmds = new Command[20]; + icmd = 0; + } + else { + Debug.Assert(cmds.Length > 0, "why is cmds.Length zero?"); + Debug.Assert(icmdTry >= 0, "why is icmdTry negative?"); + + int icmdLim = cmds.Length; + + if (icmdTry >= icmdLim) + icmdTry = 0; + + // First look for an empty slot (starting at icmdTry). + for (icmd = icmdTry; icmd < icmdLim; icmd++) + if (null == cmds[icmd]) goto FindSlotComplete; + for (icmd = 0; icmd < icmdTry; icmd++) + if (null == cmds[icmd]) goto FindSlotComplete; + + // All slots have Command objects in them. Look for a command + // with a null referent. + for (icmd = 0; icmd < icmdLim; icmd++) + if (null == cmds[icmd].Target) goto FindSlotComplete; + + // Grow the array. + icmd = cmds.Length; + icmdLim = Math.Min(idLim - idMin, 2 * icmd); + + if (icmdLim <= icmd) { + // Already at maximal size. Do a garbage collect and look again. + GC.Collect(); + for (icmd = 0; icmd < icmdLim; icmd++) { + if (null == cmds[icmd] || null == cmds[icmd].Target) + goto FindSlotComplete; + } + throw new ArgumentException(SR.GetString(SR.CommandIdNotAllocated)); + } + else { + Command[] newCmds = new Command[icmdLim]; + Array.Copy(cmds, 0, newCmds, 0, icmd); + cmds = newCmds; + } + } + +FindSlotComplete: + + cmd.id = icmd + idMin; + Debug.Assert(cmd.id >= idMin && cmd.id < idLim, "generated command id out of range"); + + cmds[icmd] = cmd; + icmdTry = icmd + 1; + } + } + + public static bool DispatchID(int id) { + Command cmd = GetCommandFromID(id); + if (null == cmd) + return false; + return cmd.Invoke(); + } + + protected static void Dispose(Command cmd) { + lock (internalSyncObject) + { + if (cmd.id >= idMin) { + cmd.Target = null; + if (cmds[cmd.id - idMin] == cmd) + cmds[cmd.id - idMin] = null; + cmd.id = 0; + } + } + } + + public virtual void Dispose() { + if (id >= idMin) + Dispose(this); + } + + public static Command GetCommandFromID(int id) { + lock (internalSyncObject) + { + if (null == cmds) + return null; + int i = id - idMin; + if (i < 0 || i >= cmds.Length) + return null; + return cmds[i]; + } + } + + public virtual bool Invoke() { + object target = Target; + if (!(target is ICommandExecutor)) + return false; + ((ICommandExecutor)target).Execute(); + return true; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CommonDialog.cs b/WindowsForms/Managed/System/WinForms/CommonDialog.cs new file mode 100644 index 000000000..6f3ee303a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CommonDialog.cs @@ -0,0 +1,340 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// + /// Specifies the base class used for displaying + /// dialog boxes on the screen. + /// + /// + [ + ToolboxItemFilter("System.Windows.Forms"), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett + ] + public abstract class CommonDialog : Component { + private static readonly object EventHelpRequest = new object(); + private const int CDM_SETDEFAULTFOCUS = NativeMethods.WM_USER + 0x51; + private static int helpMsg; + + private IntPtr defOwnerWndProc; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private IntPtr hookedWndProc; + + private IntPtr defaultControlHwnd; + + object userData; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public CommonDialog() { + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// + /// Occurs when the user clicks the Help button on a common + /// dialog box. + /// + /// + [SRDescription(SR.CommonDialogHelpRequested)] + public event EventHandler HelpRequest { + add { + Events.AddHandler(EventHelpRequest, value); + } + remove { + Events.RemoveHandler(EventHelpRequest, value); + } + } + + // Generate meaningful result from Windows.CommDlgExtendedError() + + /* Only used in PageSetupDialog.cs in a line that is commented out. + Commenting out until we need it again. + + internal static string CommonDialogErrorToString(int error) { + switch (error) { + case NativeMethods.CDERR_DIALOGFAILURE: return "dialogfailure"; + case NativeMethods.CDERR_FINDRESFAILURE: return "findresfailure"; + case NativeMethods.CDERR_INITIALIZATION: return "initialization"; + case NativeMethods.CDERR_LOADRESFAILURE: return "loadresfailure"; + case NativeMethods.CDERR_LOADSTRFAILURE: return "loadstrfailure"; + case NativeMethods.CDERR_LOCKRESFAILURE: return "lockresfailure"; + case NativeMethods.CDERR_MEMALLOCFAILURE: return "memallocfailure"; + case NativeMethods.CDERR_MEMLOCKFAILURE: return "memlockfailure"; + case NativeMethods.CDERR_NOHINSTANCE: return "nohinstance"; + case NativeMethods.CDERR_NOHOOK: return "nohook"; + case NativeMethods.CDERR_NOTEMPLATE: return "notemplate"; + case NativeMethods.CDERR_REGISTERMSGFAIL: return "registermsgfail"; + case NativeMethods.CDERR_STRUCTSIZE: return "structsize"; + case NativeMethods.PDERR_CREATEICFAILURE: return "createicfailure"; + case NativeMethods.PDERR_DEFAULTDIFFERENT: return "defaultdifferent"; + case NativeMethods.PDERR_DNDMMISMATCH: return "dndmmismatch"; + case NativeMethods.PDERR_GETDEVMODEFAIL: return "getdevmodefail"; + case NativeMethods.PDERR_INITFAILURE: return "initfailure"; + case NativeMethods.PDERR_LOADDRVFAILURE: return "loaddrvfailure"; + case NativeMethods.PDERR_NODEFAULTPRN: return "nodefaultprn"; + case NativeMethods.PDERR_NODEVICES: return "nodevices"; + case NativeMethods.PDERR_PARSEFAILURE: return "parsefailure"; + case NativeMethods.PDERR_PRINTERNOTFOUND: return "printernotfound"; + case NativeMethods.PDERR_RETDEFFAILURE: return "retdeffailure"; + case NativeMethods.PDERR_SETUPFAILURE: return "setupfailure"; + case NativeMethods.CFERR_MAXLESSTHANMIN: return "maxlessthanmin"; + case NativeMethods.CFERR_NOFONTS: return "nofonts"; + case NativeMethods.FNERR_BUFFERTOOSMALL: return "buffertoosmall"; + case NativeMethods.FNERR_INVALIDFILENAME: return "invalidfilename"; + case NativeMethods.FNERR_SUBCLASSFAILURE: return "subclassfailure"; + case NativeMethods.FRERR_BUFFERLENGTHZERO : return "bufferlengthzero"; + default: return "unknown error"; + } + } + + */ + + /// + /// + /// + /// Defines the common dialog box hook + /// procedure that is overridden to add specific functionality to a common dialog + /// box. + /// + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected virtual IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + if (msg == NativeMethods.WM_INITDIALOG) { + MoveToScreenCenter(hWnd); + + // Under some circumstances, the dialog + // does not initially focus on any control. We fix that by explicitly + // setting focus ourselves. See ASURT 39435. + // + this.defaultControlHwnd = wparam; + UnsafeNativeMethods.SetFocus(new HandleRef(null, wparam)); + } + else if (msg == NativeMethods.WM_SETFOCUS) { + UnsafeNativeMethods.PostMessage(new HandleRef(null, hWnd), CDM_SETDEFAULTFOCUS, 0, 0); + } + else if (msg == CDM_SETDEFAULTFOCUS) { + // If the dialog box gets focus, bounce it to the default control. + // so we post a message back to ourselves to wait for the focus change then push it to the default + // control. See ASURT 84016. + // + UnsafeNativeMethods.SetFocus(new HandleRef(this, defaultControlHwnd)); + } + return IntPtr.Zero; + } + + /// + /// + /// Centers the given window on the screen. This method is used by the default + /// common dialog hook procedure to center the dialog on the screen before it + /// is shown. + /// + internal static void MoveToScreenCenter(IntPtr hWnd) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(null, hWnd), ref r); + Rectangle screen = Screen.GetWorkingArea(Control.MousePosition); + int x = screen.X + (screen.Width - r.right + r.left) / 2; + int y = screen.Y + (screen.Height - r.bottom + r.top) / 3; + SafeNativeMethods.SetWindowPos(new HandleRef(null, hWnd), NativeMethods.NullHandleRef, x, y, 0, 0, NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnHelpRequest(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventHelpRequest]; + if (handler != null) handler(this, e); + } + + /// + /// + /// + /// Defines the owner window procedure that is + /// overridden to add specific functionality to a common dialog box. + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual IntPtr OwnerWndProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + if (msg == helpMsg) { + if (NativeWindow.WndProcShouldBeDebuggable) { + OnHelpRequest(EventArgs.Empty); + } + else { + try { + OnHelpRequest(EventArgs.Empty); + } + catch (Exception e) { + Application.OnThreadException(e); + } + } + return IntPtr.Zero; + } + return UnsafeNativeMethods.CallWindowProc(defOwnerWndProc, hWnd, msg, wparam, lparam); + } + + /// + /// + /// + /// When overridden in a derived class, + /// resets the properties of a common dialog to their default + /// values. + /// + /// + [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public abstract void Reset(); + + /// + /// + /// + /// When overridden in a derived class, + /// specifies a common dialog box. + /// + /// + [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + protected abstract bool RunDialog(IntPtr hwndOwner); + + /// + /// + /// + /// Runs a common dialog box. + /// + /// + public DialogResult ShowDialog() { + return ShowDialog(null); + } + + /// + /// + /// + /// Runs a common dialog box, parented to the given IWin32Window. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] + public DialogResult ShowDialog( IWin32Window owner ) { + + IntSecurity.SafeSubWindows.Demand(); + + if (!SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + + NativeWindow native = null;//This will be used if there is no owner or active window (declared here so it can be kept alive) + + IntPtr hwndOwner = IntPtr.Zero; + DialogResult result = DialogResult.Cancel; + try { + if (owner != null) { + hwndOwner = Control.GetSafeHandle(owner); + } + + if (hwndOwner == IntPtr.Zero) { + hwndOwner = UnsafeNativeMethods.GetActiveWindow(); + } + + if (hwndOwner == IntPtr.Zero) { + //We will have to create our own Window + native = new NativeWindow(); + native.CreateHandle(new CreateParams()); + hwndOwner = native.Handle; + } + + if (helpMsg == 0) { + helpMsg = SafeNativeMethods.RegisterWindowMessage("commdlg_help"); + } + + NativeMethods.WndProc ownerProc = new NativeMethods.WndProc(this.OwnerWndProc); + hookedWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(ownerProc); + System.Diagnostics.Debug.Assert(IntPtr.Zero == defOwnerWndProc, "The previous subclass wasn't properly cleaned up"); + + IntPtr userCookie = IntPtr.Zero; + try { + //UnsafeNativeMethods.[Get|Set]WindowLong is smart enough to call SetWindowLongPtr on 64-bit OS + defOwnerWndProc = UnsafeNativeMethods.SetWindowLong(new HandleRef(this, hwndOwner), NativeMethods.GWL_WNDPROC, ownerProc); + + if (Application.UseVisualStyles) { + userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + } + + Application.BeginModalMessageLoop(); + try { + result = RunDialog(hwndOwner) ? DialogResult.OK : DialogResult.Cancel; + } + finally { + Application.EndModalMessageLoop(); + } + } + finally { + IntPtr currentSubClass = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hwndOwner), NativeMethods.GWL_WNDPROC); + if ( IntPtr.Zero != defOwnerWndProc || currentSubClass != hookedWndProc) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, hwndOwner), NativeMethods.GWL_WNDPROC, new HandleRef(this, defOwnerWndProc)); + } + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + + defOwnerWndProc = IntPtr.Zero; + hookedWndProc = IntPtr.Zero; + //Ensure that the subclass delegate will not be GC collected until after it has been subclassed + GC.KeepAlive(ownerProc); + } + } + finally { + if (null != native) { + native.DestroyHandle(); + } + } + + return result; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentManagerBroker.cs b/WindowsForms/Managed/System/WinForms/ComponentManagerBroker.cs new file mode 100644 index 000000000..54553a49d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentManagerBroker.cs @@ -0,0 +1,634 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting.Lifetime; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Versioning; + using System.Threading; + using System.Globalization; + + /// + /// Ok, this class needs some explanation. We share message loops with other applications through + /// an interface called IMsoComponentManager. A "component' is fairly coarse here: Windows Forms + /// is a single component. The component manager is the application that owns and runs the message + /// loop. And, consequently, an IMsoComponent is a component that plugs into that message loop + /// to listen in on Windows messages. So far, so good. + /// + /// Because message loops are per-thread, IMsoComponentManager is also per-thread, which means + /// we will register a new IMsoComponent for each thread that is running a message loop. + /// + /// In a purely managed application, we satisfy both halves of the equation: Windows Forms + /// implements both the IMsoComponentManager and the IMsoComponent. Things start + /// to get complicated when the IMsoComponentManager comes from the COM world. + /// + /// There's a wrinkle with this design, however: It is illegal to call IMsoComponentManager on a + /// different thread than it expects. In fact, it will throw an exception. That's a probolem for key + /// events that we receive during shutdown, like app domain unload and process exit. These + /// events occur on a thread pool thread, and because as soon as we return from that thread the + /// domain will typically be torn down, we don't have much of a chance to marshal the call to + /// the right thread. + /// + /// That's where this set of classes comes in. We actually maintain a single process-wide + /// application domain, and within this app domain is where we keep all of our precious + /// IMsoComponent objects. These objects can marshal to other domains and that is how + /// all other user-created Windows Forms app domains talke to the component manager. + /// When one of these user-created domains is shut down, it notifies a proxied + /// IMsoComponent, which simply decrements a ref count. When the ref count reaches zero, + /// the component waits until a new message comes into it from the component manager. + /// At that point it knows that it is on the right thread, and it unregisters itself from the + /// component manager. + /// + /// If all this sounds expensive and complex, you should get a gold star. It is. But, we take + /// some care to only do it if we absolutely have to. For example, If we only need the additional + /// app domain if there is no executing assembly (so there is no managed entry point) and if + /// the component manager we get is a native COM object. + /// + /// So, if you're with me so far you probably want to know how it all works, probably due to some + /// nasty bug I introduced. Sorry about that. + /// + /// There are two main classes here: ComponentManagerBroker and ComponentManagerProxy. + /// + /// ComponentManagerBroker: + /// This class has a static API that can be used to retrieve a component manager proxy. + /// The API uses managed remoting to attempt to communicate with our secondary domain. + /// It will create the domain if it doesn't exist. It communicates with an instance of itself + /// on the other side of the domain. That instance maintains a ComponentManagerProxy + /// object for each thread that comes in with a request. + /// + /// ComponentManagerProxy: + /// This class implements both IMsoComponentManager and IMsoComponent. It implements + /// IMsoComponent so it can register with with the real IMsoComponentManager that was + /// passed into this method. After registering itself it will return an instance of itself + /// as IMsoComponentManager. After that the component manager broker stays + /// out of the picture. Here's a diagram to help: + /// + /// UCM <-> CProxy / CMProxy <-> AC + /// + /// UCM: Unmanaged component manager + /// CProxy: IMsoComponent half of ComponentManagerProxy + /// CMProxy: IMsoComponentManager half of ComponentManagerProxy + /// AC: Application's IMsoComponent implementation + /// + internal sealed class ComponentManagerBroker : MarshalByRefObject { + + // These are constants per process and are initialized in + // a class cctor below. + private static object _syncObject; + private static string _remoteObjectName; + + // We keep a static instance of ourself. It will really be + // per-domain, but we only have one domain. The purpose + // of this is to keep us alive. Note that this variable + // is re-used in two different domains -- in our special + // domain, it keeps us alive. In other domains it acts as + // cache. + + private static ComponentManagerBroker _broker; + + // Per-instance state + [ThreadStatic] + private ComponentManagerProxy _proxy; + + /// + /// Static ctor. We just set up a few per-process globals here + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + static ComponentManagerBroker() { + int pid = SafeNativeMethods.GetCurrentProcessId(); + _syncObject = new object(); + _remoteObjectName = string.Format(CultureInfo.CurrentCulture, "ComponentManagerBroker.{0}.{1:X}", Application.WindowsFormsVersion, pid); + } + + /// + /// Ctor. Quite a bit happens here. Here, we register a channel so + /// we can be found and we publish ouru object by calling Marshal. + /// + /// Note that we have a single static _broker field. We assign this + /// If it is already assigned that means that someone in the default + /// app domain needed a component manger broker and craeted it directly, + /// passing in false for "remoteObject". Therefore, all we need to do + /// is remote the existing broker. This makes the instance of + /// ComponentManagerBroker we are creating in this ctor a temporary + /// object, because the calling code will use the Singleton property + /// to extract the actual _broker value. + /// NOTE: ctor must be public here for remoting to grab hold. + /// + public ComponentManagerBroker() { + + // Note that we only ever configure a single broker object. + // We could be extremely transient here. + if (_broker == null) { + _broker = this; + } + } + + /// + /// Called during creation to account for an existing component manager + /// broker that was never remoted. We try not to remote the broker + /// until we need to because it is very expensive. + /// + internal ComponentManagerBroker Singleton { + get { + return _broker; + } + } + + /// + /// + internal void ClearComponentManager() { + _proxy = null; + } + + /// + /// Keep the object alive forever. + /// + public override object InitializeLifetimeService() { + return null; + } + + #region Instance API only callable from a proxied object + + /// + /// + public UnsafeNativeMethods.IMsoComponentManager GetProxy(long pCM) { + if (_proxy == null) { + UnsafeNativeMethods.IMsoComponentManager original = (UnsafeNativeMethods.IMsoComponentManager)Marshal.GetObjectForIUnknown((IntPtr)pCM); + _proxy = new ComponentManagerProxy(this, original); + } + + return _proxy; + } + + #endregion + + #region Static API callable from any domain + + /// + /// This method locates our per-process app domain and connects to a running + /// instance of ComponentManagerBroker. That instance then demand- + /// creates an instance of ComponentManagerProxy for the calling thread + /// and returns it. + /// + internal static UnsafeNativeMethods.IMsoComponentManager GetComponentManager(IntPtr pOriginal) { + + lock(_syncObject) { + + if (_broker == null) { + + // We need the default domain for the process. That's the domain we will use + // for all component managers. There is no managed way to get this domain, however, + // so we use ICorRuntimeHost. + UnsafeNativeMethods.ICorRuntimeHost host = (UnsafeNativeMethods.ICorRuntimeHost) RuntimeEnvironment.GetRuntimeInterfaceAsObject(typeof(UnsafeNativeMethods.CorRuntimeHost).GUID, typeof(UnsafeNativeMethods.ICorRuntimeHost).GUID); + object domainObj; + int hr = host.GetDefaultDomain(out domainObj); + Debug.Assert(NativeMethods.Succeeded(hr), "ICorRuntimeHost failed to return the default domain. The only way that should happen is if it hasn't been started yet, but if it hasn't been started how are we running managed code?"); + AppDomain domain = domainObj as AppDomain; + + if (domain == null) { + Debug.Assert(NativeMethods.Failed(hr) || domain != null, "ICorRuntimeHost::GetDefaultDomain succeeded but didn't retrn us an app domain."); + domain = AppDomain.CurrentDomain; + } + + // Ok, we have a domain. Next, check to see if it is our current domain. + // If it is, we bypass the CreateInstanceAndUnwrap logic because we + // can directly go with the broker. In this case we will create a broker + // and NOT remote it. We will defer the remoting until we have a different + // domain. To detect this, the _broker static variable will be assigned + // a broker in the primary app domain. The CreateInstance code looks at this + // and if it is aready set, simply remotes that broker. That is why there + // is a "Singleton" property on the broker -- just in case we had to create + // a temporary broker during CreateInstanceAndUnwrap. + + if (domain == AppDomain.CurrentDomain) { + _broker = new ComponentManagerBroker(); + } + else { + _broker = GetRemotedComponentManagerBroker(domain); + } + } + } + + // However we got here, we got here. What's important is that we have a proxied instance to the broker object + // and we can now call on it. + // + return _broker.GetProxy((long)pOriginal); + } + + /// + /// This method is factored out of GetComponentManager so we can prevent System.Runtime.Remoting from being + /// loaded into the process if we are using a single domain. + /// + private static ComponentManagerBroker GetRemotedComponentManagerBroker(AppDomain domain) { + Type ourType = typeof(ComponentManagerBroker); + ComponentManagerBroker broker = (ComponentManagerBroker)domain.CreateInstanceAndUnwrap(ourType.Assembly.FullName, ourType.FullName); + return broker.Singleton; + } + #endregion + } + + #region ComponentManagerProxy Class + /// + /// The proxy object. This acts as, well, a proxy between the unmanaged IMsoComponentManager and zero or more + /// managed components. + /// + internal class ComponentManagerProxy : MarshalByRefObject, UnsafeNativeMethods.IMsoComponentManager, UnsafeNativeMethods.IMsoComponent { + + private ComponentManagerBroker _broker; + private UnsafeNativeMethods.IMsoComponentManager _original; + private int _refCount; + private int _creationThread; + private IntPtr _componentId; + private int _nextComponentId; + private Dictionary _components; + private UnsafeNativeMethods.IMsoComponent _activeComponent; + private int _activeComponentId; + private UnsafeNativeMethods.IMsoComponent _trackingComponent; + private int _trackingComponentId; + + internal ComponentManagerProxy(ComponentManagerBroker broker, UnsafeNativeMethods.IMsoComponentManager original) { + _broker = broker; + _original = original; + _creationThread = SafeNativeMethods.GetCurrentThreadId(); + _refCount = 0; + } + + private void Dispose() { + if (_original != null) { + Marshal.ReleaseComObject(_original); + _original = null; + _components = null; + _componentId = (IntPtr)0; + _refCount = 0; + _broker.ClearComponentManager(); + } + } + + /// + /// Keep the object alive forever. + /// + public override object InitializeLifetimeService() { + return null; + } + + private bool RevokeComponent() { + return _original.FRevokeComponent(_componentId); + } + + private UnsafeNativeMethods.IMsoComponent Component { + get { + if (_trackingComponent != null) return _trackingComponent; + if (_activeComponent != null) return _activeComponent; + return null; + } + } + + #region IMsoComponent Implementation + bool UnsafeNativeMethods.IMsoComponent.FDebugMessage(IntPtr hInst, int msg, IntPtr wparam, IntPtr lparam) { + UnsafeNativeMethods.IMsoComponent c = Component; + + if (c != null) { + return c.FDebugMessage(hInst, msg, wparam, lparam); + } + + return false; + } + + bool UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(ref NativeMethods.MSG msg) { + UnsafeNativeMethods.IMsoComponent c = Component; + + if (c != null) { + return c.FPreTranslateMessage(ref msg); + } + + return false; + } + + void UnsafeNativeMethods.IMsoComponent.OnEnterState(int uStateID, bool fEnter) { + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent c in _components.Values) { + c.OnEnterState(uStateID, fEnter); + } + } + } + + void UnsafeNativeMethods.IMsoComponent.OnAppActivate(bool fActive, int dwOtherThreadID) { + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent c in _components.Values) { + c.OnAppActivate(fActive, dwOtherThreadID); + } + } + } + + void UnsafeNativeMethods.IMsoComponent.OnLoseActivation() { + if (_activeComponent != null) { + _activeComponent.OnLoseActivation(); + } + } + + void UnsafeNativeMethods.IMsoComponent.OnActivationChange(UnsafeNativeMethods.IMsoComponent component, bool fSameComponent, int pcrinfo, bool fHostIsActivating, int pchostinfo, int dwReserved) { + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent c in _components.Values) { + c.OnActivationChange(component, fSameComponent, pcrinfo, fHostIsActivating, pchostinfo, dwReserved); + } + } + } + + bool UnsafeNativeMethods.IMsoComponent.FDoIdle(int grfidlef) { + bool cont = false; + + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent c in _components.Values) { + cont |= c.FDoIdle(grfidlef); + } + } + + return cont; + } + + bool UnsafeNativeMethods.IMsoComponent.FContinueMessageLoop(int reason, int pvLoopData, NativeMethods.MSG[] msgPeeked) { + bool cont = false; + + if (_refCount == 0 && _componentId != (IntPtr)0) { + if (RevokeComponent()) { + _components.Clear(); + _componentId = (IntPtr)0; + } + } + + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent c in _components.Values) { + cont |= c.FContinueMessageLoop(reason, pvLoopData, msgPeeked); + } + } + + return cont; + } + + bool UnsafeNativeMethods.IMsoComponent.FQueryTerminate(bool fPromptUser) { + return true; + } + + void UnsafeNativeMethods.IMsoComponent.Terminate() { + if (_components != null && _components.Values.Count>0) { + UnsafeNativeMethods.IMsoComponent[] components = new UnsafeNativeMethods.IMsoComponent[_components.Values.Count]; + _components.Values.CopyTo(components, 0); + foreach (UnsafeNativeMethods.IMsoComponent c in components) { + c.Terminate(); + } + } + + if (_original != null) { + RevokeComponent(); + } + + Dispose(); + } + + IntPtr UnsafeNativeMethods.IMsoComponent.HwndGetWindow(int dwWhich, int dwReserved) { + UnsafeNativeMethods.IMsoComponent c = Component; + + if (c != null) { + return c.HwndGetWindow(dwWhich, dwReserved); + } + + return IntPtr.Zero; + } + #endregion + + #region IMsoComponentManager Implementation + int UnsafeNativeMethods.IMsoComponentManager.QueryService(ref Guid guidService, ref Guid iid, out object ppvObj) { + return _original.QueryService(ref guidService, ref iid, out ppvObj); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FDebugMessage(IntPtr hInst, int msg, IntPtr wparam, IntPtr lparam) { + return _original.FDebugMessage(hInst, msg, wparam, lparam); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FRegisterComponent(UnsafeNativeMethods.IMsoComponent component, NativeMethods.MSOCRINFOSTRUCT pcrinfo, out IntPtr dwComponentID) { + if (component == null) { + throw new ArgumentNullException("component"); + } + + dwComponentID = (IntPtr)0; + + if (_refCount == 0) { + // Our first time hooking up to the real component manager + if (!_original.FRegisterComponent(this, pcrinfo, out _componentId)) { + return false; + } + } + + _refCount++; + + if (_components == null) { + _components = new Dictionary(); + } + + _nextComponentId++; + if (_nextComponentId == int.MaxValue) { + _nextComponentId = 1; + } + + bool outofMemory = false; + //just in case we wrap, lets search for a free ID + while (_components.ContainsKey(_nextComponentId)) { + _nextComponentId++; + if (_nextComponentId == int.MaxValue) { + if (outofMemory) { + throw new InvalidOperationException(SR.GetString(SR.ComponentManagerProxyOutOfMemory)); + } + outofMemory = true; + _nextComponentId = 1; + } + } + + _components.Add(_nextComponentId, component); + dwComponentID = (IntPtr)_nextComponentId; + + return true; + } + + bool UnsafeNativeMethods.IMsoComponentManager.FRevokeComponent(IntPtr dwComponentID) { + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + if (_original == null) return false; + if (_components == null || dwLocalComponentID <= 0 || !_components.ContainsKey(dwLocalComponentID)) { + return false; + } + + if (_refCount == 1 && SafeNativeMethods.GetCurrentThreadId() == _creationThread) { + if (!RevokeComponent()) { + return false; + } + } + + _refCount--; + _components.Remove(dwLocalComponentID); + + Debug.Assert(_refCount >= 0, "underflow on ref count"); + if (_refCount <= 0) { + Dispose(); + } + + if (dwLocalComponentID == _activeComponentId) { + _activeComponent = null; + _activeComponentId = 0; + } + if (dwLocalComponentID == _trackingComponentId) { + _trackingComponent = null; + _trackingComponentId = 0; + } + + return true; + } + + bool UnsafeNativeMethods.IMsoComponentManager.FUpdateComponentRegistration(IntPtr dwComponentID, NativeMethods.MSOCRINFOSTRUCT info) { + if (_original == null) return false; + // We assume that all Microsoft domains use the same registration. + return _original.FUpdateComponentRegistration(_componentId, info); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FOnComponentActivate(IntPtr dwComponentID) { + + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + if (_original == null) return false; + // Activation requres us to store the currently active component. We will send data to it + if (_components == null || dwLocalComponentID <= 0 || !_components.ContainsKey(dwLocalComponentID)) { + return false; + } + + if (!_original.FOnComponentActivate(_componentId)) { + return false; + } + + _activeComponent = _components[dwLocalComponentID]; + _activeComponentId = dwLocalComponentID; + return true; + } + + bool UnsafeNativeMethods.IMsoComponentManager.FSetTrackingComponent(IntPtr dwComponentID, bool fTrack) { + // Tracking requres us to store the current tracking component. We will send data to it + int dwLocalComponentID = unchecked((int)(long)dwComponentID); + + if (_original == null) { + return false; + } + + if (_components == null || dwLocalComponentID <= 0 || !_components.ContainsKey(dwLocalComponentID)) { + return false; + } + + if (!_original.FSetTrackingComponent(_componentId, fTrack)) { + return false; + } + + if (fTrack) { + _trackingComponent = _components[dwLocalComponentID]; + _trackingComponentId = dwLocalComponentID; + } + else { + _trackingComponent = null; + _trackingComponentId = 0; + } + + return true; + } + + void UnsafeNativeMethods.IMsoComponentManager.OnComponentEnterState(IntPtr dwComponentID, int uStateID, int uContext, int cpicmExclude, int rgpicmExclude, int dwReserved) { + if (_original == null) return; + if (uContext == NativeMethods.MSOCM.msoccontextAll || uContext == NativeMethods.MSOCM.msoccontextMine) { + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent comp in _components.Values) { + comp.OnEnterState(uStateID, true); + } + } + } + + _original.OnComponentEnterState(_componentId, uStateID, uContext, cpicmExclude, rgpicmExclude, dwReserved); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FOnComponentExitState(IntPtr dwComponentID, int uStateID, int uContext, int cpicmExclude, int rgpicmExclude ) { + if (_original == null) return false; + if (uContext == NativeMethods.MSOCM.msoccontextAll || uContext == NativeMethods.MSOCM.msoccontextMine) { + if (_components != null) { + foreach (UnsafeNativeMethods.IMsoComponent comp in _components.Values) { + comp.OnEnterState(uStateID, false); + } + } + } + + return _original.FOnComponentExitState(_componentId, uStateID, uContext, cpicmExclude, rgpicmExclude); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FInState(int uStateID, IntPtr pvoid) { + if (_original == null) return false; + return _original.FInState(uStateID, pvoid); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FContinueIdle() { + if (_original == null) return false; + return _original.FContinueIdle(); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, int reason, int pvLoopData) { + if (_original == null) return false; + return _original.FPushMessageLoop(_componentId, reason, pvLoopData); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FCreateSubComponentManager(object punkOuter, object punkServProv, ref Guid riid, out IntPtr ppvObj) { + if (_original == null) { ppvObj = IntPtr.Zero; return false; } + return _original.FCreateSubComponentManager(punkOuter, punkServProv, ref riid, out ppvObj); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FGetParentComponentManager(out UnsafeNativeMethods.IMsoComponentManager ppicm) { + if (_original == null) { ppicm = null; return false; } + return _original.FGetParentComponentManager(out ppicm); + } + + bool UnsafeNativeMethods.IMsoComponentManager.FGetActiveComponent(int dwgac, UnsafeNativeMethods.IMsoComponent[] ppic, NativeMethods.MSOCRINFOSTRUCT info, int dwReserved) { + if (_original == null) return false; + if (_original.FGetActiveComponent(dwgac, ppic, info, dwReserved)) { + // We got a component. See if it's our proxy, and if it is, + // return what we think is currently active. We need only + // check for "this", because we only have one of these + // doo jabbers per process. + if (ppic[0] == this) { + if (dwgac == NativeMethods.MSOCM.msogacActive) { + ppic[0] = _activeComponent; + } + else if (dwgac == NativeMethods.MSOCM.msogacTracking) { + ppic[0] = _trackingComponent; + } + else if (dwgac == NativeMethods.MSOCM.msogacTrackingOrActive) { + if (_trackingComponent != null) { + ppic[0] = _trackingComponent; + } + } + } + + return ppic[0] != null; + } + else { + return false; + } + } + #endregion + + } + #endregion +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/BaseCAMarshaler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/BaseCAMarshaler.cs new file mode 100644 index 000000000..b5650213f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/BaseCAMarshaler.cs @@ -0,0 +1,140 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using Microsoft.Win32; + using System.Globalization; + + + /// + /// + /// This class performs basic operation for marshaling data passed + /// in from native in one of the CA*** structs (CADWORD, CAUUID, etc), + /// which are structs in which the first word is the number of elements + /// and the second is a pointer to an array of such elements. + /// + /// + internal abstract class BaseCAMarshaler { + + private static TraceSwitch CAMarshalSwitch = new TraceSwitch("CAMarshal", "BaseCAMarshaler: Debug CA* struct marshaling"); + + private IntPtr caArrayAddress; + private int count; + private object[] itemArray; + + /// + /// + /// Base ctor + /// + protected BaseCAMarshaler(NativeMethods.CA_STRUCT caStruct) : base() { + if (caStruct == null) { + count = 0; + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "BaseCAMarshaler: null passed in!"); + } + + // first 4 bytes is the count + count = caStruct.cElems; + caArrayAddress = caStruct.pElems; + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "Marshaling " + count.ToString(CultureInfo.InvariantCulture) + " items of type " + ItemType.Name); + } + + ~BaseCAMarshaler() { + try { + if (itemArray == null && caArrayAddress != IntPtr.Zero) { + object[] items = Items; + } + } + catch { + } + } + + protected abstract Array CreateArray(); + + /// + /// + /// Returns the type of item this marshaler will + /// return in the items array. + /// + public abstract Type ItemType { + get; + } + + + /// + /// + /// Returns the count of items that will be or have been + /// marshaled. + /// + public int Count { + get { + return count; + } + } + + + + /// + /// + /// The marshaled items. + /// + public virtual object[] Items { + get { + try { + if (itemArray == null) { + itemArray = Get_Items(); + } + } + catch (Exception ex) { + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "Marshaling failed: " + ex.GetType().Name + ", " + ex.Message); + } +#if DEBUG + if (itemArray != null) { + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "Marshaled: " + itemArray.Length.ToString(CultureInfo.InvariantCulture) + " items, array type=" + itemArray.GetType().Name); + } +#endif + return itemArray; + } + } + + + /// + /// + /// Override this member to perform marshalling of a single item + /// given it's native address. + /// + protected abstract object GetItemFromAddress(IntPtr addr); + + // Retrieve the items + private object[] Get_Items() { + // cycle through the addresses and get an item for each addr + IntPtr addr; + Array items = new object[Count]; //cpb vs38262 System.Array.CreateInstance(this.ItemType,count); + object curItem; + for (int i = 0; i < count; i++) { + try { + addr = Marshal.ReadIntPtr(caArrayAddress, i * IntPtr.Size); + curItem = GetItemFromAddress(addr); + if (curItem != null && ItemType.IsInstanceOfType(curItem)) { + items.SetValue(curItem, i); + } + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "Marshaled " + ItemType.Name + " item, value=" + (curItem == null ? "(null)" : curItem.ToString())); + } + catch (Exception ex) { + Debug.WriteLineIf(CAMarshalSwitch.TraceVerbose, "Failed to marshal " + ItemType.Name + " item, exception=" + ex.GetType().Name +", " +ex.Message); + } + } + // free the array + Marshal.FreeCoTaskMem(caArrayAddress); + caArrayAddress = IntPtr.Zero; + return(object[])items; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2AboutBoxPropertyDescriptor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2AboutBoxPropertyDescriptor.cs new file mode 100644 index 000000000..78f74f7eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2AboutBoxPropertyDescriptor.cs @@ -0,0 +1,190 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel.Design; + using Microsoft.Win32; + using System.Collections; + using System.Drawing.Design; + + internal class Com2AboutBoxPropertyDescriptor : Com2PropertyDescriptor { + private TypeConverter converter; + private UITypeEditor editor; + + public Com2AboutBoxPropertyDescriptor() : base(NativeMethods.ActiveX.DISPID_ABOUTBOX, "About", new Attribute[]{new DispIdAttribute(NativeMethods.ActiveX.DISPID_ABOUTBOX), + DesignerSerializationVisibilityAttribute.Hidden, + new DescriptionAttribute(SR.GetString(SR.AboutBoxDesc)), + new ParenthesizePropertyNameAttribute(true)}, true, typeof(string), null, false) { + } + + /// + /// + /// Retrieves the type of the component this PropertyDescriptor is bound to. + /// + public override Type ComponentType { + get { + return typeof(UnsafeNativeMethods.IDispatch); + } + } + + + /// + /// + /// Retrieves the type converter for this property. + /// + public override TypeConverter Converter { + get { + if (converter == null) { + converter = new TypeConverter(); + } + return converter; + } + } + /// + /// + /// Indicates whether this property is read only. + /// + public override bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// Retrieves the type of the property. + /// + public override Type PropertyType { + get { + return typeof(string); + } + } + + /// + /// + /// Indicates whether reset will change the value of the component. If there + /// is a DefaultValueAttribute, then this will return true if getValue returns + /// something different than the default value. If there is a reset method and + /// a shouldPersist method, this will return what shouldPersist returns. + /// If there is just a reset method, this always returns true. If none of these + /// cases apply, this returns false. + /// + public override bool CanResetValue(object component) { + return false; + } + + /// + /// + /// Retrieves an editor of the requested type. + /// + public override object GetEditor(Type editorBaseType) { + if (editorBaseType == typeof(UITypeEditor)) { + if (editor == null) { + editor = new AboutBoxUITypeEditor(); + } + } + + return editor; + } + + /// + /// + /// Retrieves the current value of the property on component, + /// invoking the getXXX method. An exception in the getXXX + /// method will pass through. + /// + public override object GetValue(object component) { + return ""; + } + + /// + /// + /// Will reset the default value for this property on the component. If + /// there was a default value passed in as a DefaultValueAttribute, that + /// value will be set as the value of the property on the component. If + /// there was no default value passed in, a ResetXXX method will be looked + /// for. If one is found, it will be invoked. If one is not found, this + /// is a nop. + /// + public override void ResetValue(object component){ + } + + /// + /// + /// This will set value to be the new value of this property on the + /// component by invoking the setXXX method on the component. If the + /// value specified is invalid, the component should throw an exception + /// which will be passed up. The component designer should design the + /// property so that getXXX following a setXXX should return the value + /// passed in if no exception was thrown in the setXXX call. + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public override void SetValue(object component, object value) { + throw new ArgumentException(); + } + + /// + /// + /// Indicates whether the value of this property needs to be persisted. In + /// other words, it indicates whether the state of the property is distinct + /// from when the component is first instantiated. If there is a default + /// value specified in this PropertyDescriptor, it will be compared against the + /// property's current value to determine this. If there is't, the + /// shouldPersistXXX method is looked for and invoked if found. If both + /// these routes fail, true will be returned. + /// + /// If this returns false, a tool should not persist this property's value. + /// + public override bool ShouldSerializeValue(object component) { + return false; + } + + public class AboutBoxUITypeEditor : UITypeEditor { + /// + /// + /// Edits the given object value using the editor style provided by + /// GetEditorStyle. A service provider is provided so that any + /// required editing services can be obtained. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + object component = context.Instance; + + if (Marshal.IsComObject(component) && component is UnsafeNativeMethods.IDispatch) { + UnsafeNativeMethods.IDispatch pDisp = (UnsafeNativeMethods.IDispatch)component; + NativeMethods.tagEXCEPINFO pExcepInfo = new NativeMethods.tagEXCEPINFO(); + Guid g = Guid.Empty; + + int hr = pDisp.Invoke(NativeMethods.ActiveX.DISPID_ABOUTBOX, + ref g, + SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_METHOD, + new NativeMethods.tagDISPPARAMS(), + null, + pExcepInfo, null); + + Debug.Assert(NativeMethods.Succeeded(hr), "Failed to launch about box."); + } + return value; + } + + /// + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ColorConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ColorConverter.cs new file mode 100644 index 000000000..8bdf3c899 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ColorConverter.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Diagnostics; + using System; + + using System.Drawing; + using System.Collections; + using Microsoft.Win32; + + /// + /// + /// This class maps an OLE_COLOR to a managed Color editor. + /// + internal class Com2ColorConverter : Com2DataTypeToManagedDataTypeConverter{ + + /// + /// + /// Returns the managed type that this editor maps the property type to. + /// + public override Type ManagedType{ + get{ + return typeof(Color); + } + } + + /// + /// + /// Converts the native value into a managed value + /// + public override object ConvertNativeToManaged(object nativeValue, Com2PropertyDescriptor pd){ + object baseValue = nativeValue; + int intVal = 0; + + // get the integer value out of the native... + // + if (nativeValue is UInt32){ + intVal = (int)(UInt32)nativeValue; + } + else if (nativeValue is Int32){ + intVal = (int)nativeValue; + } + + return ColorTranslator.FromOle(intVal); + } + + /// + /// + /// Converts the managed value into a native value + /// + public override object ConvertManagedToNative(object managedValue, Com2PropertyDescriptor pd, ref bool cancelSet){ + // don't cancel the set + cancelSet = false; + + // we default to black. + // + if (managedValue == null){ + managedValue = Color.Black; + } + + if (managedValue is Color){ + return ColorTranslator.ToOle(((Color)managedValue)); + + } + Debug.Fail("Don't know how to set type:" + managedValue.GetType().Name); + return 0; + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ComponentEditor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ComponentEditor.cs new file mode 100644 index 000000000..4fccb5842 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ComponentEditor.cs @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using Microsoft.Win32; + using System.Windows.Forms.Design; + + internal class Com2ComponentEditor : WindowsFormsComponentEditor { + + public static bool NeedsComponentEditor(object obj) { + if (obj is NativeMethods.IPerPropertyBrowsing) { + // check for a property page + Guid guid = Guid.Empty; + int hr = ((NativeMethods.IPerPropertyBrowsing)obj).MapPropertyToPage(NativeMethods.MEMBERID_NIL, out guid); + if ((hr == NativeMethods.S_OK) && !guid.Equals(Guid.Empty)) { + return true; + } + } + + if (obj is NativeMethods.ISpecifyPropertyPages) { + try { + NativeMethods.tagCAUUID uuids = new NativeMethods.tagCAUUID(); + try { + ((NativeMethods.ISpecifyPropertyPages)obj).GetPages(uuids); + if (uuids.cElems > 0) { + return true; + } + } + finally { + if (uuids.pElems != IntPtr.Zero) { + Marshal.FreeCoTaskMem(uuids.pElems); + } + } + } + catch { + } + + return false; + } + return false; + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // This was shipped in Everett. + ] + public override bool EditComponent(ITypeDescriptorContext context, object obj, IWin32Window parent) { + + IntPtr handle = (parent == null ? IntPtr.Zero : parent.Handle); + + // try to get the page guid + if (obj is NativeMethods.IPerPropertyBrowsing) { + // check for a property page + Guid guid = Guid.Empty; + int hr = ((NativeMethods.IPerPropertyBrowsing)obj).MapPropertyToPage(NativeMethods.MEMBERID_NIL, out guid); + if (hr == NativeMethods.S_OK) { + if (!guid.Equals(Guid.Empty)) { + object o = obj; + SafeNativeMethods.OleCreatePropertyFrame(new HandleRef(parent, handle), 0, 0, "PropertyPages", 1, ref o, 1, new Guid[]{guid}, Application.CurrentCulture.LCID, 0, IntPtr.Zero); + return true; + } + } + } + + if (obj is NativeMethods.ISpecifyPropertyPages) { + bool failed = false; + Exception failureException; + + try { + NativeMethods.tagCAUUID uuids = new NativeMethods.tagCAUUID(); + try { + ((NativeMethods.ISpecifyPropertyPages)obj).GetPages(uuids); + if (uuids.cElems <= 0) { + return false; + } + } + catch { + return false; + } + try { + object o = obj; + SafeNativeMethods.OleCreatePropertyFrame(new HandleRef(parent, handle), 0, 0, "PropertyPages", 1, ref o, uuids.cElems, new HandleRef(uuids, uuids.pElems), Application.CurrentCulture.LCID, 0, IntPtr.Zero); + return true; + } + finally { + if (uuids.pElems != IntPtr.Zero) { + Marshal.FreeCoTaskMem(uuids.pElems); + } + } + + } + catch (Exception ex1) { + failed = true; + failureException = ex1; + } + + if (failed) { + String errString = SR.GetString(SR.ErrorPropertyPageFailed); + + IUIService uiSvc = (context != null) ? ((IUIService) context.GetService(typeof(IUIService))) : null; + + if (uiSvc == null) { + RTLAwareMessageBox.Show(null, errString, SR.GetString(SR.PropertyGridTitle), + MessageBoxButtons.OK, MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1, 0); + } + else if (failureException != null) { + uiSvc.ShowError(failureException, errString); + } + else { + uiSvc.ShowError(errString); + } + } + } + return false; + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2DataTypeToManagedDataTypeConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2DataTypeToManagedDataTypeConverter.cs new file mode 100644 index 000000000..39c9e9175 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2DataTypeToManagedDataTypeConverter.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.ComponentModel.Design; + using System.Collections; + using Microsoft.Win32; + + /// + /// + /// This base class maps an ole defined data type (OLE_COLOR, IFont, etc.), + /// + /// + internal abstract class Com2DataTypeToManagedDataTypeConverter{ + + + public virtual bool AllowExpand { + get { + return false; + } + } + + /// + /// + /// Returns the managed type that this editor maps the property type to. + /// + public abstract Type ManagedType{ + get; + } + + /// + /// + /// Converts the native value into a managed value + /// + public abstract object ConvertNativeToManaged(object nativeValue, Com2PropertyDescriptor pd); + + /// + /// + /// Converts the managed value into a native value + /// + public abstract object ConvertManagedToNative(object managedValue, Com2PropertyDescriptor pd, ref bool cancelSet); + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Enum.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Enum.cs new file mode 100644 index 000000000..fd712c6d0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Enum.cs @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// This class mimics a clr enum that we can create at runtime. + /// It associates an array of names with an array of values and converts + /// between them. + /// + /// A note here: we compare string values when looking for the value of an item. + /// Typically these aren't large lists and the perf is worth it. The reason stems + /// from IPerPropertyBrowsing, which supplies a list of names and a list of + /// variants to mimic enum functionality. If the actual property value is a DWORD, + /// which translates to VT_UI4, and they specify their values as VT_I4 (which is a common + /// mistake), they won't compare properly and values can't be updated. + /// By comparing strings, we avoid this problem and add flexiblity to the system. + /// + internal class Com2Enum { + + /// + /// + /// Our array of value string names + /// + private string[] names; + + + /// + /// + /// Our values + /// + private object[] values; + + + + /// + /// + /// Our cached array of value.ToString()'s + /// + private string[] stringValues; + + /// + /// + /// Should we allow values besides what's in the listbox? + /// + private bool allowUnknownValues; + + /// + /// + /// Our one and only ctor + /// + public Com2Enum(string[] names, object[] values, bool allowUnknownValues) { + + this.allowUnknownValues = allowUnknownValues; + + // these have to be null and the same length + if (names == null || + values == null || + names.Length != values.Length) { + throw new ArgumentException(SR.GetString(SR.COM2NamesAndValuesNotEqual)); + } + + PopulateArrays(names, values); + } + + /// + /// + /// Can this enum be values other than the strict enum? + /// + public bool IsStrictEnum { + get { + return !this.allowUnknownValues; + } + } + + /// + /// + /// Retrieve a copy of the value array + /// + public virtual object[] Values { + get { + return(object[])this.values.Clone(); + } + } + + /// + /// + /// Retrieve a copy of the nme array. + /// + public virtual string[] Names { + get { + return(string[])this.names.Clone(); + } + } + + /// + /// + /// Associate a string to the appropriate value. + /// + public virtual object FromString(string s) { + int bestMatch = -1; + + for (int i = 0; i < stringValues.Length; i++) { + if (String.Compare(names[i], s, true, CultureInfo.InvariantCulture) == 0 || + String.Compare(stringValues[i], s, true, CultureInfo.InvariantCulture) == 0) { + return values[i]; + } + + if (bestMatch == -1 && 0 == String.Compare(names[i], s, true, CultureInfo.InvariantCulture)) { + bestMatch = i; + } + } + + if (bestMatch != -1) { + return values[bestMatch]; + } + + return allowUnknownValues ? s : null; + } + + protected virtual void PopulateArrays(string[] names, object[] values) { + // setup our values...since we have to walk through + // them anyway to do the ToString, we just copy them here. + this.names = new string[names.Length]; + this.stringValues = new string[names.Length]; + this.values = new object[names.Length]; + for (int i = 0; i < names.Length; i++) { + //Debug.WriteLine(names[i] + ": item " + i.ToString() + ",type=" + values[i].GetType().Name + ", value=" + values[i].ToString()); + this.names[i] = names[i]; + this.values[i] = values[i]; + if (values[i] != null) { + this.stringValues[i] = values[i].ToString(); + } + } + } + + /// + /// + /// Retrieves the string name of a given value. + /// + public virtual string ToString(object v) { + if (v != null) { + + // in case this is a real enum...try to convert it. + // + if (values.Length > 0 && v.GetType() != values[0].GetType()) { + try { + v = Convert.ChangeType(v, values[0].GetType(), CultureInfo.InvariantCulture); + } + catch{ + } + } + + // we have to do this do compensate for small discrpencies + // in a lot of objects in COM2 (DWORD -> VT_IU4, value we get is VT_I4, which + // convert to Int32, UInt32 respectively + string strVal = v.ToString(); + for (int i = 0; i < values.Length; i++) { + if (String.Compare(stringValues[i], strVal, true, CultureInfo.InvariantCulture) == 0) { + return names[i]; + } + } + if (allowUnknownValues) { + return strVal; + } + } + return ""; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2EnumConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2EnumConverter.cs new file mode 100644 index 000000000..f5393661d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2EnumConverter.cs @@ -0,0 +1,138 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + */ +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using System.Collections; + using Microsoft.Win32; + using System.Globalization; + + internal class Com2EnumConverter : TypeConverter { + + internal readonly Com2Enum com2Enum; + private StandardValuesCollection values; + + public Com2EnumConverter(Com2Enum enumObj) { + com2Enum = enumObj; + } + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destType) { + if (base.CanConvertTo(context, destType)) { + return true; + } + return destType.IsEnum; + } + + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is string) { + return com2Enum.FromString((string)value); + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string)) { + if (value != null) { + string str = com2Enum.ToString(value); + return (str == null ? "" : str); + } + } + + if (destinationType.IsEnum) { + return Enum.ToObject(destinationType, value); + } + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (values == null) { + object[] objValues = com2Enum.Values; + if (objValues != null) { + values = new StandardValuesCollection(objValues); + } + } + return values; + } + + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + return com2Enum.IsStrictEnum; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Determines if the given object value is valid for this type. + /// + public override bool IsValid(ITypeDescriptorContext context, object value) { + string strValue = com2Enum.ToString(value); + return strValue != null && strValue.Length > 0; + } + + public void RefreshValues() { + this.values = null; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedBrowsingHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedBrowsingHandler.cs new file mode 100644 index 000000000..285fbf48c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedBrowsingHandler.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + + using System.Diagnostics; + using System; + + /// + /// + /// This is the base class for handlers for COM2 extended browsing interface + /// such as IPerPropertyBrowsing, etc. + /// + /// These handlers should be stateless. That is, they should keep no refs to object + /// and should only work on a give object and dispid. That way all objects that + /// support a give interface can share a handler. + /// + /// See COM2Properties for the array of handler classes to interface classes + /// where handlers should be registered. + /// + internal abstract class Com2ExtendedBrowsingHandler{ + + /// + /// + /// The interface that this handler managers + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + public abstract Type Interface{ + get; + } + + /// + /// + /// Called to setup the property handlers on a given property + /// In this method, the handler will add listeners to the events that + /// the COM2PropertyDescriptor surfaces that it cares about. + /// + public virtual void SetupPropertyHandlers(Com2PropertyDescriptor propDesc){ + SetupPropertyHandlers(new Com2PropertyDescriptor[]{propDesc}); + } + + /// + /// + /// Called to setup the property handlers on a given properties + /// In this method, the handler will add listeners to the events that + /// the Com2PropertyDescriptor surfaces that it cares about. + /// + public abstract void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedTypeConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedTypeConverter.cs new file mode 100644 index 000000000..eee8fe011 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedTypeConverter.cs @@ -0,0 +1,216 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + using System.Globalization; + + using System.Collections; + + /// + /// + /// Base class for value editors that extend basic functionality. + /// calls will be delegated to the "base value editor". + /// + internal class Com2ExtendedTypeConverter : TypeConverter { + private TypeConverter innerConverter; + + public Com2ExtendedTypeConverter(TypeConverter innerConverter) { + this.innerConverter = innerConverter; + } + + public Com2ExtendedTypeConverter(Type baseType) { + this.innerConverter = TypeDescriptor.GetConverter(baseType); + } + + public TypeConverter InnerConverter { + get { + return innerConverter; + } + } + + public TypeConverter GetWrappedConverter(Type t) { + + TypeConverter converter = innerConverter; + + while (converter != null) { + if (t.IsInstanceOfType(converter)) { + return converter; + } + + if (converter is Com2ExtendedTypeConverter) { + converter = ((Com2ExtendedTypeConverter)converter).InnerConverter; + } + else { + break; + } + } + return null; + } + + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (innerConverter != null) { + return innerConverter.CanConvertFrom(context, sourceType); + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Determines if this converter can convert an object to the given destination + /// type. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (innerConverter != null) { + return innerConverter.CanConvertTo(context, destinationType); + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (innerConverter != null) { + return innerConverter.ConvertFrom(context, culture, value); + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (innerConverter != null) { + return innerConverter.ConvertTo(context, culture, value, destinationType); + } + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + if (innerConverter != null) { + return innerConverter.CreateInstance(context, propertyValues); + } + return base.CreateInstance(context, propertyValues); + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + if (innerConverter != null) { + return innerConverter.GetCreateInstanceSupported(context); + } + return base.GetCreateInstanceSupported(context); + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + if (innerConverter != null) { + return innerConverter.GetProperties(context, value, attributes); + } + return base.GetProperties(context, value, attributes); + } + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + if (innerConverter != null) { + return innerConverter.GetPropertiesSupported(context); + } + return base.GetPropertiesSupported(context); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (innerConverter != null) { + return innerConverter.GetStandardValues(context); + } + return base.GetStandardValues(context); + } + + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + if (innerConverter != null) { + return innerConverter.GetStandardValuesExclusive(context); + } + return base.GetStandardValuesExclusive(context); + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + if (innerConverter != null) { + return innerConverter.GetStandardValuesSupported(context); + } + return base.GetStandardValuesSupported(context); + } + + /// + /// + /// Determines if the given object value is valid for this type. + /// + public override bool IsValid(ITypeDescriptorContext context, object value) { + if (innerConverter != null) { + return innerConverter.IsValid(context, value); + } + return base.IsValid(context, value); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedUITypeEditor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedUITypeEditor.cs new file mode 100644 index 000000000..74d628f50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ExtendedUITypeEditor.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Drawing.Design.Com2ExtendedUITypeEditor..ctor(System.Type)")] + +/* + */ +namespace System.Drawing.Design { + + using System.Diagnostics; + + using System.Collections; + using Microsoft.Win32; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing; + using System.Drawing.Design; + + /// + /// + /// + /// Provides an editor that provides a way to visually edit the values of a COM2 + /// type. + /// + internal class Com2ExtendedUITypeEditor : UITypeEditor { + + private UITypeEditor innerEditor; + + public Com2ExtendedUITypeEditor(UITypeEditor baseTypeEditor) { + this.innerEditor = baseTypeEditor; + } + + public Com2ExtendedUITypeEditor(Type baseType) { + this.innerEditor = (UITypeEditor)TypeDescriptor.GetEditor(baseType, typeof(UITypeEditor)); + } + + public UITypeEditor InnerEditor { + get { + return innerEditor; + } + } + + /// + /// + /// Edits the given object value using the editor style provided by + /// GetEditorStyle. A service provider is provided so that any + /// required editing services can be obtained. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + if (innerEditor != null) { + return innerEditor.EditValue(context, provider, value); + } + else { + return base.EditValue(context, provider, value); + } + } + + /// + /// + /// Determines if this editor supports the painting of a representation + /// of an object's value. + /// + public override bool GetPaintValueSupported(ITypeDescriptorContext context) { + if (innerEditor != null) { + return innerEditor.GetPaintValueSupported(context); + } + return base.GetPaintValueSupported(context); + } + + /// + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + if (innerEditor != null) { + return innerEditor.GetEditStyle(context); + } + return base.GetEditStyle(context); + } + + /// + /// + /// Paints a representative value of the given object to the provided + /// canvas. Painting should be done within the boundaries of the + /// provided rectangle. + /// + public override void PaintValue(PaintValueEventArgs e) { + if (innerEditor != null) { + innerEditor.PaintValue(e); + } + base.PaintValue(e); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2FontConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2FontConverter.cs new file mode 100644 index 000000000..e0bce4e65 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2FontConverter.cs @@ -0,0 +1,132 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Collections; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// This class maps an OLE_COLOR to a managed Color editor. + /// + internal class Com2FontConverter : Com2DataTypeToManagedDataTypeConverter { + + private IntPtr lastHandle = IntPtr.Zero; + private Font lastFont = null; + + public override bool AllowExpand { + get { + return true; + } + } + + /// + /// + /// Returns the managed type that this editor maps the property type to. + /// + public override Type ManagedType { + get { + return typeof(Font); + } + } + + + /// + /// + /// Converts the native value into a managed value + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public override object ConvertNativeToManaged(object nativeValue, Com2PropertyDescriptor pd) { + // we're getting an IFont thing here + UnsafeNativeMethods.IFont nativeFont = nativeValue as UnsafeNativeMethods.IFont; + + if (nativeFont == null) { + lastHandle = IntPtr.Zero; + lastFont = Control.DefaultFont; + return lastFont; + } + + IntPtr fontHandle = nativeFont.GetHFont(); + + // see if we have this guy cached + if (fontHandle == lastHandle && lastFont != null) { + return lastFont; + } + + lastHandle = fontHandle; + + try { + // this wasn't working because it was converting everything to + // world units. + // + Font font = Font.FromHfont(lastHandle); + try { + lastFont = ControlPaint.FontInPoints(font); + } + finally { + font.Dispose(); + } + } + catch(ArgumentException) { + // we will fail on non-truetype fonts, so + // just use the default font. + lastFont = Control.DefaultFont; + } + + return lastFont; + } + + /// + /// + /// Converts the managed value into a native value + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public override object ConvertManagedToNative(object managedValue, Com2PropertyDescriptor pd, ref bool cancelSet) { + + // we default to black. + // + if (managedValue == null) { + managedValue = Control.DefaultFont; + } + + cancelSet = true; + + if (lastFont != null && lastFont.Equals(managedValue)) { + // don't do anything here. + return null; + } + + + lastFont = (Font)managedValue; + UnsafeNativeMethods.IFont nativeFont = (UnsafeNativeMethods.IFont)pd.GetNativeValue(pd.TargetObject); + + // now, push all the values into the native side + if (nativeFont != null) { + bool changed = ControlPaint.FontToIFont(lastFont, nativeFont); + + if (changed) { + // here, we want to pick up a new font from the handle + lastFont = null; + ConvertNativeToManaged(nativeFont, pd); + + } + } + return null; + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ICategorizePropertiesHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ICategorizePropertiesHandler.cs new file mode 100644 index 000000000..5f7e6f2a7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ICategorizePropertiesHandler.cs @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Globalization; + using Microsoft.Win32; + + [System.Security.SuppressUnmanagedCodeSecurityAttribute()] + internal class Com2ICategorizePropertiesHandler : Com2ExtendedBrowsingHandler { + + public override Type Interface { + get { + return typeof(NativeMethods.ICategorizeProperties); + } + } + + private string GetCategoryFromObject(object obj, int dispid) { + if (obj == null) { + return null; + } + + if (obj is NativeMethods.ICategorizeProperties) { + NativeMethods.ICategorizeProperties catObj = (NativeMethods.ICategorizeProperties)obj; + + try { + int categoryID = 0; + + if (NativeMethods.S_OK == catObj.MapPropertyToCategory(dispid, ref categoryID)) { + string categoryName = null; + + switch (categoryID) { + case NativeMethods.ActiveX.PROPCAT_Nil: + return ""; + case NativeMethods.ActiveX.PROPCAT_Misc: + return SR.GetString(SR.PropertyCategoryMisc); + case NativeMethods.ActiveX.PROPCAT_Font: + return SR.GetString(SR.PropertyCategoryFont); + case NativeMethods.ActiveX.PROPCAT_Position: + return SR.GetString(SR.PropertyCategoryPosition); + case NativeMethods.ActiveX.PROPCAT_Appearance: + return SR.GetString(SR.PropertyCategoryAppearance); + case NativeMethods.ActiveX.PROPCAT_Behavior: + return SR.GetString(SR.PropertyCategoryBehavior); + case NativeMethods.ActiveX.PROPCAT_Data: + return SR.GetString(SR.PropertyCategoryData); + case NativeMethods.ActiveX.PROPCAT_List: + return SR.GetString(SR.PropertyCategoryList); + case NativeMethods.ActiveX.PROPCAT_Text: + return SR.GetString(SR.PropertyCategoryText); + case NativeMethods.ActiveX.PROPCAT_Scale: + return SR.GetString(SR.PropertyCategoryScale); + case NativeMethods.ActiveX.PROPCAT_DDE: + return SR.GetString(SR.PropertyCategoryDDE); + } + + if (NativeMethods.S_OK == catObj.GetCategoryName(categoryID, CultureInfo.CurrentCulture.LCID, out categoryName)) { + return categoryName; + } + } + } + catch { + } + } + return null; + } + + public override void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc) { + if (propDesc == null) { + return; + } + for (int i = 0; i < propDesc.Length; i++) { + propDesc[i].QueryGetBaseAttributes += new GetAttributesEventHandler(this.OnGetAttributes); + } + } + + private void OnGetAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent) { + + string cat = GetCategoryFromObject(sender.TargetObject, sender.DISPID); + + if (cat != null && cat.Length > 0) { + attrEvent.Add(new CategoryAttribute(cat)); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IDispatchConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IDispatchConverter.cs new file mode 100644 index 000000000..db501032f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IDispatchConverter.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using System.Diagnostics; + using System; + + using System.ComponentModel; + using System.Collections; + using Microsoft.Win32; + using System.Globalization; + + internal class Com2IDispatchConverter : Com2ExtendedTypeConverter{ + Com2PropertyDescriptor propDesc; + + /// + /// + /// What we return textually for null. + /// + protected static readonly string none = SR.GetString(SR.toStringNone); + + private bool allowExpand; + + + public Com2IDispatchConverter(Com2PropertyDescriptor propDesc, bool allowExpand, TypeConverter baseConverter) : base(baseConverter){ + this.propDesc = propDesc; + this.allowExpand = allowExpand; + } + + + public Com2IDispatchConverter(Com2PropertyDescriptor propDesc, bool allowExpand) : base(propDesc.PropertyType){ + this.propDesc = propDesc; + this.allowExpand = allowExpand; + } + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + return false; + } + + /// + /// + /// Determines if this converter can convert an object to the given destination + /// type. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + return destinationType == typeof(string); + } + + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == typeof(string)){ + if (value == null){ + return none; + } + + string text = ComNativeDescriptor.Instance.GetName(value); + + if (text == null || text.Length == 0){ + text = ComNativeDescriptor.Instance.GetClassName(value); + } + + if (text == null){ + return "(Object)"; + } + return text; + } + return base.ConvertTo(context, culture, value, destinationType); + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + return TypeDescriptor.GetProperties(value, attributes); + } + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return this.allowExpand; + } + + // no dropdown, please! + // + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return false; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs new file mode 100644 index 000000000..757386836 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs @@ -0,0 +1,263 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Reflection; + using Microsoft.Win32; + using System.Collections; + using System.Globalization; + + [System.Security.SuppressUnmanagedCodeSecurityAttribute()] + internal class Com2IManagedPerPropertyBrowsingHandler : Com2ExtendedBrowsingHandler { + + public override Type Interface { + get { + return typeof(NativeMethods.IManagedPerPropertyBrowsing); + } + } + + public override void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc) { + if (propDesc == null) { + return; + } + for (int i = 0; i < propDesc.Length; i++) { + propDesc[i].QueryGetDynamicAttributes += new GetAttributesEventHandler(this.OnGetAttributes); + } + } + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo and IVsPerPropertyBrowsing. HideProperty + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + private void OnGetAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent) { + Object target = sender.TargetObject; + + if (target is NativeMethods.IManagedPerPropertyBrowsing) { + Attribute[] attrs = GetComponentAttributes((NativeMethods.IManagedPerPropertyBrowsing)target,sender.DISPID); + if (attrs != null) { + for (int i = 0; i < attrs.Length; i++) { + attrEvent.Add(attrs[i]); + } + } + } + } + + internal static Attribute[] GetComponentAttributes(NativeMethods.IManagedPerPropertyBrowsing target, int dispid) { + int cItems = 0; + IntPtr pbstrs = IntPtr.Zero; + IntPtr pvars = IntPtr.Zero; + + int hr = target.GetPropertyAttributes(dispid, ref cItems, ref pbstrs, ref pvars); + + if (hr != NativeMethods.S_OK || cItems == 0) { + return new Attribute[0]; + } + + ArrayList attrs = new ArrayList(); + + string[] attrTypeNames = GetStringsFromPtr(pbstrs, cItems); + Object[] varParams = GetVariantsFromPtr(pvars, cItems); + + Debug.Assert(attrTypeNames.Length == varParams.Length, "Mismatched parameter and attribute name length"); + if (attrTypeNames.Length != varParams.Length) { + return new Attribute[0]; + } + + // get the types + Type[] types = new Type[attrTypeNames.Length]; + for (int i = 0; i < attrTypeNames.Length; i++) { + + string attrName = attrTypeNames[i]; + + // try the name first + Type t = Type.GetType(attrName); + Assembly a = null; + + if (t != null) { + a = t.Assembly; + } + + if (t == null) { + + + // check for an assembly name. + // + string assemblyName = ""; + + int comma = attrName.LastIndexOf(','); + + if (comma != -1) { + assemblyName = attrName.Substring(comma); + attrName = attrName.Substring(0, comma); + } + + string fieldName; + int lastDot = attrName.LastIndexOf('.'); + if (lastDot != -1) { + fieldName = attrName.Substring(lastDot + 1); + } + else { + // somethings odd + Debug.Fail("No dot in class name?"); + continue; + } + + // try to get the field value + if (a == null) { + t = Type.GetType(attrName.Substring(0,lastDot) + assemblyName); + } + else { + t = a.GetType(attrName.Substring(0,lastDot) + assemblyName); + } + + if (t == null){ + Debug.Fail("Failed load attribute '" + attrName + assemblyName + "'. It's Type could not be found."); + continue; + } + + Debug.Assert(typeof(Attribute).IsAssignableFrom(t), "Attribute type " + t.FullName + " does not derive from Attribute"); + if (!typeof(Attribute).IsAssignableFrom(t)) { + continue; + } + + if (t != null) { + FieldInfo fi = t.GetField(fieldName); + + // only if it's static + if (fi != null && fi.IsStatic) { + Object fieldValue = fi.GetValue(null); + if (fieldValue is Attribute) { + // add it to the list + attrs.Add(fieldValue); + continue; + } + } + else { + Debug.Fail("Couldn't load field '" + fieldName + "' from type '" + attrName.Substring(0,lastDot) + "'. It does not exist or is not static"); + } + } + } + + + Debug.Assert(typeof(Attribute).IsAssignableFrom(t), "Attribute type " + t.FullName + " does not derive from Attribute"); + if (!typeof(Attribute).IsAssignableFrom(t)) { + continue; + } + + Attribute attr = null; + + // okay, if we got here, we need to build the attribute... + // get the initalizer value if we've got a one item ctor + + if (!Convert.IsDBNull(varParams[i]) && varParams[i] != null) { + ConstructorInfo[] ctors = t.GetConstructors(); + for (int c=0; c < ctors.Length; c++) { + ParameterInfo[] pis = ctors[c].GetParameters(); + if (pis.Length == 1 && pis[0].ParameterType.IsAssignableFrom(varParams[i].GetType())) { + // found a one-parameter ctor, use it + // try to construct a default one + try { + attr = (Attribute)Activator.CreateInstance(t, new Object[]{varParams[i]}); + attrs.Add(attr); + } + catch { + // bummer, nevermind + Debug.Fail("Attribute " + t.FullName + " did not have a initalizer specified and has no default constructor"); + continue; + } + } + } + } + else { + // try to construct a default one + try { + attr = (Attribute)Activator.CreateInstance(t); + attrs.Add(attr); + } + catch { + // bummer, nevermind + Debug.Fail("Attribute " + t.FullName + " did not have a initalizer specified and has no default constructor"); + continue; + } + } + } + + Attribute[] temp = new Attribute[attrs.Count]; + attrs.CopyTo(temp, 0); + return temp; + } + + private static string[] GetStringsFromPtr(IntPtr ptr, int cStrings) { + if (ptr != IntPtr.Zero) { + string[] strs = new string[cStrings]; + IntPtr bstr; + for (int i = 0; i < cStrings; i++) { + try{ + bstr = Marshal.ReadIntPtr(ptr, i*4); + if (bstr != IntPtr.Zero) { + strs[i] = Marshal.PtrToStringUni(bstr); + SafeNativeMethods.SysFreeString(new HandleRef(null, bstr)); + } + else { + strs[i] = ""; + } + } + catch (Exception ex) { + Debug.Fail("Failed to marshal component attribute BSTR " + i.ToString(CultureInfo.InvariantCulture), ex.ToString()); + } + } + try{ + Marshal.FreeCoTaskMem(ptr); + } + catch (Exception ex) { + Debug.Fail("Failed to free BSTR array memory", ex.ToString()); + } + return strs; + } + else { + return new string[0]; + } + } + + private static Object[] GetVariantsFromPtr(IntPtr ptr, int cVariants) { + if (ptr != IntPtr.Zero) { + Object[] objects = new Object[cVariants]; + IntPtr curVariant; + + for (int i = 0; i < cVariants; i++) { + try{ + curVariant = (IntPtr)((long)ptr + (i* 16 /*sizeof(VARIANT)*/)); + if (curVariant != IntPtr.Zero) { + objects[i] = Marshal.GetObjectForNativeVariant(curVariant); + SafeNativeMethods.VariantClear(new HandleRef(null, curVariant)); + } + else { + objects[i] = Convert.DBNull; + } + } + catch (Exception ex) { + Debug.Fail("Failed to marshal component attribute VARIANT " + i.ToString(CultureInfo.InvariantCulture), ex.ToString()); + } + } + try{ + Marshal.FreeCoTaskMem(ptr); + } + catch (Exception ex) { + Debug.Fail("Failed to free VARIANT array memory", ex.ToString()); + } + return objects; + } + else { + return new Object[cVariants]; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IPerPropertyBrowsingHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IPerPropertyBrowsingHandler.cs new file mode 100644 index 000000000..c0db876ad --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IPerPropertyBrowsingHandler.cs @@ -0,0 +1,371 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using Microsoft.Win32; + using System.Globalization; + using System.Drawing.Design; + + [System.Security.SuppressUnmanagedCodeSecurityAttribute()] + internal class Com2IPerPropertyBrowsingHandler : Com2ExtendedBrowsingHandler { + public override Type Interface { + get { + return typeof(NativeMethods.IPerPropertyBrowsing); + } + } + + public override void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc) { + if (propDesc == null) { + return; + } + for (int i = 0; i < propDesc.Length; i++) { + propDesc[i].QueryGetBaseAttributes += new GetAttributesEventHandler(this.OnGetBaseAttributes); + propDesc[i].QueryGetDisplayValue += new GetNameItemEventHandler(this.OnGetDisplayValue); + + propDesc[i].QueryGetTypeConverterAndTypeEditor += new GetTypeConverterAndTypeEditorEventHandler(this.OnGetTypeConverterAndTypeEditor); + } + } + + private Guid GetPropertyPageGuid(NativeMethods.IPerPropertyBrowsing target, int dispid) { + // check for a property page + Guid guid; + int hr = target.MapPropertyToPage(dispid, out guid); + if (hr == NativeMethods.S_OK) { + return guid; + } + return Guid.Empty; + } + + internal static string GetDisplayString(NativeMethods.IPerPropertyBrowsing ppb, int dispid, ref bool success) { + string[] strVal = new string[1]; + int hr = ppb.GetDisplayString(dispid, strVal); + if (hr == NativeMethods.S_OK) { + success = (strVal[0] != null); + //Debug.Assert(success, "IPerPropertyBrowsing::GetDisplayString returned NULL and S_OK -- this is not a valid state. This component does not property implement IPerPropertyBrowsing. (component class=" + TypeDescriptor.GetClassName(ppb) + ")"); + return strVal[0]; + } + else { + success = false; + } + return null; + } + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo and IVsPerPropertyBrowsing. HideProperty + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + private void OnGetBaseAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent) { + NativeMethods.IPerPropertyBrowsing target = sender.TargetObject as NativeMethods.IPerPropertyBrowsing; + if (target != null) { + // we hide IDispatch props by default, we we need to force showing them here + + bool validPropPage = !Guid.Empty.Equals(GetPropertyPageGuid(target, sender.DISPID)); + + if (sender.CanShow && validPropPage) { + if (typeof(UnsafeNativeMethods.IDispatch).IsAssignableFrom(sender.PropertyType)) { + attrEvent.Add(BrowsableAttribute.Yes); + } + } + } + } + + private void OnGetDisplayValue(Com2PropertyDescriptor sender, GetNameItemEvent gnievent) { + try { + if (sender.TargetObject is NativeMethods.IPerPropertyBrowsing) { + + // if we are using the dropdown, don't convert the value + // or the values will change when we select them and call back + // for the display value. + if (sender.Converter is Com2IPerPropertyEnumConverter || sender.ConvertingNativeType) { + return; + } + + bool success = true; + + string displayString = GetDisplayString((NativeMethods.IPerPropertyBrowsing)sender.TargetObject, sender.DISPID, ref success); + + if (success) { + gnievent.Name = displayString; + } + } + } + catch { + } + } + + private void OnGetTypeConverterAndTypeEditor(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent gveevent) { + if (sender.TargetObject is NativeMethods.IPerPropertyBrowsing) { + NativeMethods.IPerPropertyBrowsing ppb = (NativeMethods.IPerPropertyBrowsing)sender.TargetObject; + + bool hasStrings = false; + + // check for enums + NativeMethods.CA_STRUCT caStrings = new NativeMethods.CA_STRUCT(); + NativeMethods.CA_STRUCT caCookies = new NativeMethods.CA_STRUCT(); + + int hr = NativeMethods.S_OK; + + try { + hr = ppb.GetPredefinedStrings(sender.DISPID, caStrings, caCookies); + } + catch(ExternalException ex){ + hr = ex.ErrorCode; + Debug.Fail("An exception occurred inside IPerPropertyBrowsing::GetPredefinedStrings(dispid=" + sender.DISPID + "), object type=" + new ComNativeDescriptor().GetClassName(ppb) + ". This is caused by an exception (usually an AV) inside the object being browsed, and is not a problem in the properties window."); + } + + // Terminate the existing editor if we created the current one + // so if the items have disappeared, we don't hold onto the old + // items. + if (gveevent.TypeConverter is Com2IPerPropertyEnumConverter) { + gveevent.TypeConverter = null; + } + + if (hr != NativeMethods.S_OK) { + hasStrings = false; + } + else { + hasStrings = true; + } + + if (hasStrings) { + + OleStrCAMarshaler stringMarshaler = new OleStrCAMarshaler(caStrings); + Int32CAMarshaler intMarshaler = new Int32CAMarshaler(caCookies); + + + if (stringMarshaler.Count > 0 && intMarshaler.Count > 0) { + gveevent.TypeConverter = new Com2IPerPropertyEnumConverter(new Com2IPerPropertyBrowsingEnum(sender, this, stringMarshaler, intMarshaler, true)); + } + else { + //hasStrings = false; + } + } + + // if we didn't get any strings, try the proppage edtior + // + if (!hasStrings){ + + // this is a _bit_ of a backwards-compat work around... + // many older ActiveX controls will show a property page + // for all properties since the old grid would only put up the + // [...] button for "(Custom)". If we have a conversion editor, + // don't allow this to override it... + // + if (sender.ConvertingNativeType){ + return; + } + + Guid g = GetPropertyPageGuid(ppb, sender.DISPID); + + if (!Guid.Empty.Equals(g)){ + gveevent.TypeEditor = new Com2PropertyPageUITypeEditor(sender, g, (UITypeEditor)gveevent.TypeEditor); + } + } + } + } + + + // this is just here so we can identify the enums that we added + private class Com2IPerPropertyEnumConverter : Com2EnumConverter { + private Com2IPerPropertyBrowsingEnum itemsEnum; + public Com2IPerPropertyEnumConverter(Com2IPerPropertyBrowsingEnum items) : base(items) { + this.itemsEnum = items; + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType) { + + if (destType == typeof(string) && !itemsEnum.arraysFetched) { + object curValue = itemsEnum.target.GetValue(itemsEnum.target.TargetObject); + if (curValue == value || (curValue != null && curValue.Equals(value))) { + bool success = false; + string val = GetDisplayString((NativeMethods.IPerPropertyBrowsing)itemsEnum.target.TargetObject, itemsEnum.target.DISPID, ref success); + if (success) { + return val; + } + } + } + return base.ConvertTo(context, culture, value, destType); + } + } + + + // This exists for perf reasons. We delay doing this until we + // are actually asked for the array of values. + // + private class Com2IPerPropertyBrowsingEnum : Com2Enum { + + internal Com2PropertyDescriptor target; + private Com2IPerPropertyBrowsingHandler handler; + private OleStrCAMarshaler nameMarshaller; + private Int32CAMarshaler valueMarshaller; + internal bool arraysFetched; + //private bool standardValuesQueried; + + public Com2IPerPropertyBrowsingEnum(Com2PropertyDescriptor targetObject, Com2IPerPropertyBrowsingHandler handler, OleStrCAMarshaler names, Int32CAMarshaler values, bool allowUnknowns) : base(new string[0], new object[0], allowUnknowns) { + this.target = targetObject; + this.nameMarshaller = names; + this.valueMarshaller = values; + this.handler = handler; + this.arraysFetched = false; + } + + /// + /// + /// Retrieve a copy of the value array + /// + public override object[] Values { + get { + EnsureArrays(); + return base.Values; + } + } + + /// + /// + /// Retrieve a copy of the nme array. + /// + public override string[] Names { + get { + EnsureArrays(); + return base.Names; + } + } + + /*internal bool StandardValuesQueried { + get { + this.standardValuesQueried = value; + } + } */ + + + // ensure that we have processed the caStructs into arrays + // of values and strings + // + private void EnsureArrays() { + if (this.arraysFetched) { + return; + } + + this.arraysFetched = true; + + try { + // marshal the items. + object[] nameItems = nameMarshaller.Items; + object[] cookieItems= valueMarshaller.Items; + + NativeMethods.IPerPropertyBrowsing ppb = (NativeMethods.IPerPropertyBrowsing)target.TargetObject; + int itemCount = 0; + + Debug.Assert(cookieItems != null && nameItems != null, "An item array is null"); + + + if (nameItems.Length > 0){ + + object[] valueItems = new object[cookieItems.Length]; + NativeMethods.VARIANT var = new NativeMethods.VARIANT(); + int cookie; + + Debug.Assert(cookieItems.Length == nameItems.Length, "Got uneven names and cookies"); + + // for each name item, we ask the object for it's corresponding value. + // + Type targetType = target.PropertyType; + for (int i = nameItems.Length - 1; i >= 0; i--) { + cookie = (int)cookieItems[i]; + if (nameItems[i] == null || !(nameItems[i] is string)) { + Debug.Fail("Bad IPerPropertyBrowsing item [" + i.ToString(CultureInfo.InvariantCulture) + "], name=" + (nameItems == null ? "(unknown)" : nameItems[i].ToString())); + continue; + } + var.vt = (short)NativeMethods.tagVT.VT_EMPTY; + int hr = ppb.GetPredefinedValue(target.DISPID, cookie, var); + if (hr == NativeMethods.S_OK && var.vt != (short)NativeMethods.tagVT.VT_EMPTY) { + valueItems[i] = var.ToObject(); + if (valueItems[i].GetType() != targetType) { + if (targetType.IsEnum) { + valueItems[i] = Enum.ToObject(targetType, valueItems[i]); + } + else { + try { + valueItems[i] = Convert.ChangeType(valueItems[i], targetType, CultureInfo.InvariantCulture); + } + catch { + // oh well... + } + } + } + } + + var.Clear(); + if (hr == NativeMethods.S_OK) { + itemCount++; + continue; + } + else if (itemCount > 0){ + // shorten the arrays to ignore the failed ones. this isn't terribly + // efficient but shouldn't happen very often. It's rare for these to fail. + // + Array.Copy(nameItems, i, nameItems, i+1, itemCount); + Array.Copy(valueItems, i, valueItems, i+1, itemCount); + } + + } + + // pass this data down to the base Com2Enum object... + string[] strings = new string[itemCount]; + Array.Copy(nameItems, 0, strings, 0, itemCount); + base.PopulateArrays(strings, valueItems); + + } + } + catch (Exception ex) { + base.PopulateArrays(new string[0], new object[0]); + Debug.Fail("Failed to build IPerPropertyBrowsing editor. " + ex.GetType().Name + ", " + ex.Message); + } + } + + protected override void PopulateArrays(string[] names, object[] values) { + // we call base.PopulateArrays directly when we actually want to do this. + } + + public override object FromString(string s) { + EnsureArrays(); + return base.FromString(s); + } + + public override string ToString(object v) { + + // If the value is the object's current value, then + // ask GetDisplay string first. This is a perf improvement + // because this way we don't populate the arrays when an object is selected, only + // when the dropdown is actually opened. + // + if (target.IsCurrentValue(v)) { + + bool success = false; + + string displayString = Com2IPerPropertyBrowsingHandler.GetDisplayString((NativeMethods.IPerPropertyBrowsing)target.TargetObject, target.DISPID, ref success); + + if (success) { + return displayString; + } + } + + // couldn't get a display string...do the normal thing. + // + EnsureArrays(); + return base.ToString(v); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IProvidePropertyBuilderHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IProvidePropertyBuilderHandler.cs new file mode 100644 index 000000000..534a14d54 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IProvidePropertyBuilderHandler.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using Microsoft.Win32; + using System.Drawing.Design; + + [System.Security.SuppressUnmanagedCodeSecurityAttribute()] + internal class Com2IProvidePropertyBuilderHandler : Com2ExtendedBrowsingHandler { + + public override Type Interface { + get { + return typeof(NativeMethods.IProvidePropertyBuilder); + } + } + + private bool GetBuilderGuidString(NativeMethods.IProvidePropertyBuilder target, int dispid, ref string strGuidBldr, int[] bldrType) { + bool valid = false; + string[] pGuidBldr = new string[1]; + if (NativeMethods.Failed(target.MapPropertyToBuilder(dispid, bldrType, pGuidBldr, ref valid))) { + valid = false; + } + + if (valid && (bldrType[0] & _CTLBLDTYPE.CTLBLDTYPE_FINTERNALBUILDER) == 0) { + valid = false; + Debug.Fail("Property Browser doesn't support standard builders -- NYI"); + } + + if (!valid) { + return false; + } + + if (pGuidBldr[0] == null) { + strGuidBldr = Guid.Empty.ToString(); + } + else { + strGuidBldr = pGuidBldr[0]; + } + return true; + } + + public override void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc) { + if (propDesc == null) { + return; + } + for (int i = 0; i < propDesc.Length; i++) { + propDesc[i].QueryGetBaseAttributes += new GetAttributesEventHandler(this.OnGetBaseAttributes); + + propDesc[i].QueryGetTypeConverterAndTypeEditor += new GetTypeConverterAndTypeEditorEventHandler(this.OnGetTypeConverterAndTypeEditor); + + } + } + + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo and IVsPerPropertyBrowsing. HideProperty + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + private void OnGetBaseAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent) { + NativeMethods.IProvidePropertyBuilder target = sender.TargetObject as NativeMethods.IProvidePropertyBuilder; + + if (target != null ) { + string s = null; + bool builderValid = GetBuilderGuidString(target, sender.DISPID, ref s, new int[1]); + // we hide IDispatch props by default, we we need to force showing them here + if (sender.CanShow && builderValid) { + if (typeof(UnsafeNativeMethods.IDispatch).IsAssignableFrom(sender.PropertyType)) { + attrEvent.Add(BrowsableAttribute.Yes); + } + } + } + } + + private void OnGetTypeConverterAndTypeEditor(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent gveevent) { + object target = sender.TargetObject; + + if (target is NativeMethods.IProvidePropertyBuilder) { + NativeMethods.IProvidePropertyBuilder propBuilder = (NativeMethods.IProvidePropertyBuilder)target; + int[] pctlBldType = new int[1]; + string guidString = null; + + if (GetBuilderGuidString(propBuilder, sender.DISPID, ref guidString, pctlBldType)) { + gveevent.TypeEditor = new Com2PropertyBuilderUITypeEditor(sender, guidString, pctlBldType[0], (UITypeEditor)gveevent.TypeEditor); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IVsPerPropertyBrowsingHandler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IVsPerPropertyBrowsingHandler.cs new file mode 100644 index 000000000..15997e2c1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2IVsPerPropertyBrowsingHandler.cs @@ -0,0 +1,235 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Globalization; + using Microsoft.Win32; + + /// + /// + /// This is the base class for handlers for Com2 extended browsing interface + /// such as IPerPropertyBrowsing, etc. + /// + /// These handlers should be stateless. That is, they should keep no refs to object + /// and should only work on a give object and dispid. That way all objects that + /// support a give interface can share a handler. + /// + /// See Com2Properties for the array of handler classes to interface classes + /// where handlers should be registered. + /// + [System.Security.SuppressUnmanagedCodeSecurityAttribute()] + internal class Com2IVsPerPropertyBrowsingHandler: Com2ExtendedBrowsingHandler { + + /// + /// + /// The interface that this handler managers + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + public override Type Interface { + get { + return typeof(NativeMethods.IVsPerPropertyBrowsing); + } + } + + public static bool AllowChildProperties(Com2PropertyDescriptor propDesc) { + if (propDesc.TargetObject is NativeMethods.IVsPerPropertyBrowsing) { + bool pfHide = false; + int hr = ((NativeMethods.IVsPerPropertyBrowsing)propDesc.TargetObject).DisplayChildProperties(propDesc.DISPID, ref pfHide); + return (hr == NativeMethods.S_OK && pfHide); + } + return false; + } + + /// + /// + /// Called to setup the property handlers on a given properties + /// In this method, the handler will add listeners to the events that + /// the Com2PropertyDescriptor surfaces that it cares about. + /// + public override void SetupPropertyHandlers(Com2PropertyDescriptor[] propDesc){ + if (propDesc == null){ + return; + } + for (int i = 0; i < propDesc.Length; i++){ + propDesc[i].QueryGetDynamicAttributes += new GetAttributesEventHandler(this.OnGetDynamicAttributes); + propDesc[i].QueryGetBaseAttributes += new GetAttributesEventHandler(this.OnGetBaseAttributes); + propDesc[i].QueryGetDisplayName += new GetNameItemEventHandler(this.OnGetDisplayName); + propDesc[i].QueryGetIsReadOnly += new GetBoolValueEventHandler(this.OnGetIsReadOnly); + + propDesc[i].QueryShouldSerializeValue += new GetBoolValueEventHandler(this.OnShouldSerializeValue); + propDesc[i].QueryCanResetValue += new GetBoolValueEventHandler(this.OnCanResetPropertyValue); + propDesc[i].QueryResetValue += new Com2EventHandler(this.OnResetPropertyValue); + + propDesc[i].QueryGetTypeConverterAndTypeEditor += new GetTypeConverterAndTypeEditorEventHandler(this.OnGetTypeConverterAndTypeEditor); + + } + } + + private void OnGetBaseAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent){ + NativeMethods.IVsPerPropertyBrowsing vsObj = sender.TargetObject as NativeMethods.IVsPerPropertyBrowsing; + + if (vsObj == null) { + return; + } + + // should we localize this? + string[] pHelpString = new string[1]; + int hr = vsObj.GetLocalizedPropertyInfo(sender.DISPID, CultureInfo.CurrentCulture.LCID, null, pHelpString); + if (hr == NativeMethods.S_OK && pHelpString[0] != null){ + attrEvent.Add(new DescriptionAttribute(pHelpString[0])); + } + } + + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo and IVsPerPropertyBrowsing. HideProperty + /// such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. + /// + private void OnGetDynamicAttributes(Com2PropertyDescriptor sender, GetAttributesEvent attrEvent){ + + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing){ + NativeMethods.IVsPerPropertyBrowsing vsObj = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + + int hr = NativeMethods.S_OK; + + // we want to avoid allowing clients to force a bad property to be browsable, + // so we don't allow things that are marked as non browsable to become browsable, + // only the other way around. + // + if (sender.CanShow) { + // should we hide this? + bool pfHide = sender.Attributes[typeof(BrowsableAttribute)].Equals(BrowsableAttribute.No); + + hr = vsObj.HideProperty(sender.DISPID, ref pfHide); + if (hr == NativeMethods.S_OK){ + attrEvent.Add(pfHide ? BrowsableAttribute.No : BrowsableAttribute.Yes); + } + } + + // should we show this + if (typeof(UnsafeNativeMethods.IDispatch).IsAssignableFrom(sender.PropertyType) && sender.CanShow){ + bool pfDisplay = false; + hr = vsObj.DisplayChildProperties(sender.DISPID, ref pfDisplay); + if (hr == NativeMethods.S_OK && pfDisplay){ + attrEvent.Add(BrowsableAttribute.Yes); + } + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + + private void OnCanResetPropertyValue(Com2PropertyDescriptor sender, GetBoolValueEvent boolEvent) { + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing) { + NativeMethods.IVsPerPropertyBrowsing target = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + bool canReset = boolEvent.Value; + int hr = target.CanResetPropertyValue(sender.DISPID, ref canReset); + + if (NativeMethods.Succeeded(hr)){ + boolEvent.Value = canReset; + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo (part 2) + /// + private void OnGetDisplayName(Com2PropertyDescriptor sender, GetNameItemEvent nameItem){ + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing){ + NativeMethods.IVsPerPropertyBrowsing vsObj = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + + // get the localized name, if applicable + string[] pNameString = new string[1]; + int hr = vsObj.GetLocalizedPropertyInfo(sender.DISPID, CultureInfo.CurrentCulture.LCID, pNameString, null); + if (hr == NativeMethods.S_OK && pNameString[0] != null){ + nameItem.Name = pNameString[0]; + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.IsPropertyReadOnly + /// + private void OnGetIsReadOnly(Com2PropertyDescriptor sender, GetBoolValueEvent gbvevent){ + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing){ + NativeMethods.IVsPerPropertyBrowsing vsObj = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + + // should we make this read only? + bool pfResult = false; + int hr = vsObj.IsPropertyReadOnly(sender.DISPID, ref pfResult); + + if (hr == NativeMethods.S_OK){ + gbvevent.Value = pfResult; + } + } + } + + /// + /// + /// Here is where we handle IVsPerPropertyBrowsing.DisplayChildProperties + /// + private void OnGetTypeConverterAndTypeEditor(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent gveevent) { + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing){ + + // we only do this for IDispatch types + if (sender.CanShow && typeof(UnsafeNativeMethods.IDispatch).IsAssignableFrom(sender.PropertyType)){ + NativeMethods.IVsPerPropertyBrowsing vsObj = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + + // should we make this read only? + bool pfResult = false; + int hr = vsObj.DisplayChildProperties(sender.DISPID, ref pfResult); + + if (gveevent.TypeConverter is Com2IDispatchConverter){ + gveevent.TypeConverter = new Com2IDispatchConverter(sender, (hr == NativeMethods.S_OK && pfResult)); + } + else{ + gveevent.TypeConverter = new Com2IDispatchConverter(sender, (hr == NativeMethods.S_OK && pfResult), gveevent.TypeConverter); + } + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + + private void OnResetPropertyValue(Com2PropertyDescriptor sender, EventArgs e) { + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing) { + + + NativeMethods.IVsPerPropertyBrowsing target = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + int dispid = sender.DISPID; + bool canReset = false; + int hr = target.CanResetPropertyValue(dispid, ref canReset); + + if (NativeMethods.Succeeded(hr)){ + target.ResetPropertyValue(dispid); + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + + private void OnShouldSerializeValue(Com2PropertyDescriptor sender, GetBoolValueEvent gbvevent){ + if (sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing){ + NativeMethods.IVsPerPropertyBrowsing vsObj = (NativeMethods.IVsPerPropertyBrowsing)sender.TargetObject; + + // by default we say it's default + bool pfResult = true; + int hr = vsObj.HasDefaultValue(sender.DISPID,ref pfResult); + + if (hr == NativeMethods.S_OK && !pfResult){ + // specify a default value editor + gbvevent.Value = true; + } + } + Debug.Assert(sender.TargetObject == null || sender.TargetObject is NativeMethods.IVsPerPropertyBrowsing, "Object is not " + Interface.Name + "!"); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PictureConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PictureConverter.cs new file mode 100644 index 000000000..c3a399f9b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PictureConverter.cs @@ -0,0 +1,140 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Collections; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// This class maps an IPicture to a System.Drawing.Image. + /// + internal class Com2PictureConverter : Com2DataTypeToManagedDataTypeConverter { + + object lastManaged; + IntPtr lastNativeHandle; + WeakReference pictureRef; + IntPtr lastPalette = IntPtr.Zero; + + Type pictureType = typeof(Bitmap); + + public Com2PictureConverter(Com2PropertyDescriptor pd) { + if (pd.DISPID == NativeMethods.ActiveX.DISPID_MOUSEICON || pd.Name.IndexOf("Icon") != -1) { + pictureType = typeof(Icon); + } + } + + /// + /// + /// Returns the managed type that this editor maps the property type to. + /// + public override Type ManagedType { + get { + return pictureType; + } + } + + /// + /// + /// Converts the native value into a managed value + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public override object ConvertNativeToManaged(object nativeValue, Com2PropertyDescriptor pd) { + + if (nativeValue == null) { + return null; + } + + Debug.Assert(nativeValue is UnsafeNativeMethods.IPicture, "nativevalue is not IPicture"); + + UnsafeNativeMethods.IPicture nativePicture = (UnsafeNativeMethods.IPicture)nativeValue; + IntPtr handle = nativePicture.GetHandle(); + + if (lastManaged != null && handle == lastNativeHandle) { + return lastManaged; + } + + lastNativeHandle = handle; + //lastPalette = nativePicture.GetHPal(); + if (handle != IntPtr.Zero) { + switch (nativePicture.GetPictureType()) { + case NativeMethods.Ole.PICTYPE_ICON: + pictureType = typeof(Icon); + lastManaged = Icon.FromHandle(handle); + break; + case NativeMethods.Ole.PICTYPE_BITMAP: + pictureType = typeof(Bitmap); + lastManaged = Image.FromHbitmap(handle); + break; + default: + Debug.Fail("Unknown picture type"); + break; + } + pictureRef = new WeakReference(nativePicture); + } + else { + lastManaged = null; + pictureRef = null; + } + return lastManaged; + } + + /// + /// + /// Converts the managed value into a native value + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public override object ConvertManagedToNative(object managedValue, Com2PropertyDescriptor pd, ref bool cancelSet) { + // don't cancel the set + cancelSet = false; + + if (lastManaged != null && lastManaged.Equals(managedValue) && pictureRef != null && pictureRef.IsAlive) { + return pictureRef.Target; + } + + // we have to build an IPicture + lastManaged = managedValue; + + if (managedValue != null) { + Guid g = typeof(UnsafeNativeMethods.IPicture).GUID; + NativeMethods.PICTDESC pictdesc = null; + bool own = false; + + if (lastManaged is Icon) { + pictdesc = NativeMethods.PICTDESC.CreateIconPICTDESC(((Icon)lastManaged).Handle); + } + else if (lastManaged is Bitmap) { + pictdesc = NativeMethods.PICTDESC.CreateBitmapPICTDESC(((Bitmap)lastManaged).GetHbitmap(), lastPalette); + own = true; + } + else { + Debug.Fail("Unknown Image type: " + managedValue.GetType().Name); + } + + UnsafeNativeMethods.IPicture pict = UnsafeNativeMethods.OleCreatePictureIndirect(pictdesc, ref g, own); + lastNativeHandle = pict.GetHandle(); + pictureRef = new WeakReference(pict); + return pict; + } + else { + lastManaged = null; + lastNativeHandle = lastPalette = IntPtr.Zero; + pictureRef = null; + return null; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Properties.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Properties.cs new file mode 100644 index 000000000..ea0131fae --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2Properties.cs @@ -0,0 +1,524 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using System.Collections.Generic; + using Microsoft.Win32; + + + /// + /// + /// This class is responsible for managing a set or properties for a native object. It determines + /// when the properties need to be refreshed, and owns the extended handlers for those properties. + /// + internal class Com2Properties { + + private static TraceSwitch DbgCom2PropertiesSwitch = new TraceSwitch("DbgCom2Properties", "Com2Properties: debug Com2 properties manager"); + + + + /// + /// + /// This is the interval that we'll hold props for. If someone doesn't touch an object + /// for this amount of time, we'll dump the properties from our cache. + /// + /// 5 minutes -- ticks are 1/10,000,000th of a second + /// + private static long AGE_THRESHHOLD = (long)(10000000L * 60L * 5L); + + + /// + /// + /// This is the object that gave us the properties. We hold a WeakRef so we don't addref the object. + /// + internal WeakReference weakObjRef; + + /// + /// + /// This is our list of properties. + /// + private Com2PropertyDescriptor[] props; + + /// + /// + /// The index of the default property + /// + private int defaultIndex = -1; + + + /// + /// + /// The timestamp of the last operation on this property manager, usually + /// when the property list was fetched. + /// + private long touchedTime; + + /// + /// For non-IProvideMultipleClassInfo ITypeInfos, this is the version number on the last + /// ITypeInfo we looked at. If this changes, we know we need to dump the cache. + /// + private long[] typeInfoVersions; + + +#if DEBUG + private string dbgObjName; + private string dbgObjClass; +#endif + + private int alwaysValid = 0; + + /// + /// + /// These are the interfaces we recognize for extended browsing. + /// + private static Type[] extendedInterfaces = new Type[]{ + typeof(NativeMethods.ICategorizeProperties), + typeof(NativeMethods.IProvidePropertyBuilder), + typeof(NativeMethods.IPerPropertyBrowsing), + typeof(NativeMethods.IVsPerPropertyBrowsing), + typeof(NativeMethods.IManagedPerPropertyBrowsing)}; + + /// + /// + /// These are the classes of handlers corresponding to the extended + /// interfaces above. + /// + private static Type[] extendedInterfaceHandlerTypes = new Type[]{ + typeof(Com2ICategorizePropertiesHandler), + typeof(Com2IProvidePropertyBuilderHandler), + typeof(Com2IPerPropertyBrowsingHandler), + typeof(Com2IVsPerPropertyBrowsingHandler), + typeof(Com2IManagedPerPropertyBrowsingHandler)}; + + + + public event EventHandler Disposed; + + + /// + /// + /// Default ctor. + /// + public Com2Properties(object obj, Com2PropertyDescriptor[] props, int defaultIndex) { +#if DEBUG + ComNativeDescriptor cnd = new ComNativeDescriptor(); + this.dbgObjName = cnd.GetName(obj); + if (this.dbgObjName == null) { + this.dbgObjName = "(null)"; + } + this.dbgObjClass = cnd.GetClassName(obj); + if (this.dbgObjClass == null) { + this.dbgObjClass = "(null)"; + } + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("Creating Com2Properties for object " + dbgObjName + ", class=" + dbgObjClass); +#endif + + // set up our variables + SetProps(props); + weakObjRef = new WeakReference(obj); + + this.defaultIndex = defaultIndex; + + typeInfoVersions = GetTypeInfoVersions(obj); + + touchedTime = DateTime.Now.Ticks; + + } + + internal bool AlwaysValid { + get { + return this.alwaysValid > 0; + } + set { + if (value) { + if (alwaysValid == 0 && !CheckValid()) { + return; + } + this.alwaysValid++; + } + else { + if (alwaysValid > 0) { + this.alwaysValid--; + } + } + } + } + + /// + /// + /// Retrieve the default property. + /// + public Com2PropertyDescriptor DefaultProperty{ + get{ + if (!CheckValid(true)) { + return null; + } + if (defaultIndex == -1) { + if (props.Length > 0) { + return props[0]; + } + else { + return null; + } + } + Debug.Assert(defaultIndex < props.Length, "Whoops! default index is > props.Length"); + return props[defaultIndex]; + } + } + + /// + /// + /// The object that created the list of properties. This will + /// return null if the timeout has passed or the ref has died. + /// + public object TargetObject{ + get{ + if (!CheckValid(false) || touchedTime == 0) { +#if DEBUG + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("CheckValid called on dead object!"); +#endif + return null; + } + return weakObjRef.Target; + } + } + + /// + /// + /// How long since these props have been queried. + /// + public long TicksSinceTouched{ + get{ + if (touchedTime == 0) { + return 0; + } + return DateTime.Now.Ticks - touchedTime; + } + } + + /// + /// + /// Returns the list of properties + /// + public Com2PropertyDescriptor[] Properties{ + get{ + CheckValid(true); + if (touchedTime == 0 || props == null) { + return null; + } + touchedTime = DateTime.Now.Ticks; + + // refresh everyone! + for (int i = 0; i < props.Length; i++) { + props[i].SetNeedsRefresh(Com2PropertyDescriptorRefresh.All, true); + } + +#if DEBUG + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("Returning prop array for object " + dbgObjName + ", class=" + dbgObjClass); +#endif + return props; + } + } + + /// + /// + /// Should this guy be refreshed because of old age? + /// + public bool TooOld{ + get{ + // check if the property is valid but don't dispose it if it's not + CheckValid(false, false); + if (touchedTime == 0) { + return false; + } + return TicksSinceTouched > AGE_THRESHHOLD; + } + } + + /// + /// + /// Checks the source object for eache extended browsing inteface + /// listed in extendedInterfaces and creates a handler from extendedInterfaceHandlerTypes + /// to handle it. + /// + public void AddExtendedBrowsingHandlers(Hashtable handlers) { + + object target = this.TargetObject; + if (target == null) { + return; + } + + + // process all our registered types + Type t; + for (int i = 0; i < extendedInterfaces.Length; i++) { + t = extendedInterfaces[i]; + + // is this object an implementor of the interface? + // + if (t.IsInstanceOfType(target)) { + + // since handlers must be stateless, check to see if we've already + // created one of this type + // + Com2ExtendedBrowsingHandler handler = (Com2ExtendedBrowsingHandler)handlers[t]; + if (handler == null) { + handler = (Com2ExtendedBrowsingHandler)Activator.CreateInstance(extendedInterfaceHandlerTypes[i]); + handlers[t] = handler; + } + + // make sure we got the right one + // + if (t.IsAssignableFrom(handler.Interface)) { +#if DEBUG + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("Adding browsing handler type " + handler.Interface.Name + " to object " + dbgObjName + ", class=" + dbgObjClass); +#endif + // allow the handler to attach itself to the appropriate properties + // + handler.SetupPropertyHandlers(props); + } + else { + throw new ArgumentException(SR.GetString(SR.COM2BadHandlerType, t.Name, handler.Interface.Name)); + } + } + } + } + + + public void Dispose() { +#if DEBUG + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("Disposing property manager for " + dbgObjName + ", class=" + dbgObjClass); +#endif + + if (props != null) { + + if (Disposed != null) { + Disposed(this, EventArgs.Empty); + } + + weakObjRef = null; + props = null; + touchedTime = 0; + } + } + + public bool CheckValid() { + return CheckValid(false); + } + + /// + /// + /// Make sure this property list is still valid. + /// + /// 1) WeakRef is still alive + /// 2) Our timeout hasn't passed + /// + public bool CheckValid(bool checkVersions) { + return CheckValid(checkVersions, true); + } + + + /// + /// Gets a list of version longs for each type info in the COM object + /// representing hte current version stamp, function and variable count. + /// If any of these things change, we'll re-fetch the properties. + /// + private long[] GetTypeInfoVersions(object comObject) { + + // get type infos + // + UnsafeNativeMethods.ITypeInfo[] pTypeInfos = Com2TypeInfoProcessor.FindTypeInfos(comObject, false); + + // build up the info. + // + long[] versions = new long[pTypeInfos.Length]; + for (int i = 0; i < pTypeInfos.Length; i++) { + versions [i] = GetTypeInfoVersion(pTypeInfos[i]); + } + return versions; + } + + private static int countOffset = -1; + private static int versionOffset = -1; + + // we define a struct here so we can use unsafe code to marshal it + // as a blob of memory. This is here as a reference to how big and where the members are. + // + /*private struct tagTYPEATTR { + public Guid guid; //16 + public int lcid; // 4 + public int dwReserved; // 4 + public int memidConstructor; // 4 + public int memidDestructor; // 4 + public IntPtr lpstrSchema; // platform + public int cbSizeInstance; // 4 + public int typekind; // 4 + public short cFuncs; // 2 + public short cVars; // 2 + public short cImplTypes; // 2 + public short cbSizeVft; // 2 + public short cbAlignment; // 2 + public short wTypeFlags; // 2 + public short wMajorVerNum; // 2 + public short wMinorVerNum; // 2 + + public int tdescAlias_unionMember; + public short tdescAlias_vt; + public int idldescType_dwReserved; + public short idldescType_wIDLFlags; + + }*/ + + + // the offset of the cFunc member in the TYPEATTR structure. + // + private static int CountMemberOffset { + + get { + if (countOffset == -1) { + countOffset = Marshal.SizeOf(typeof(Guid)) + IntPtr.Size + 24; + } + return countOffset; + } + } + + // the offset of the cMajorVerNum member in the TYPEATTR structure. + // + private static int VersionOffset { + get { + if (versionOffset == -1) { + versionOffset = CountMemberOffset + 12; + } + return versionOffset; + } + + } + + private unsafe long GetTypeInfoVersion(UnsafeNativeMethods.ITypeInfo pTypeInfo) { + + + IntPtr pTypeAttr = IntPtr.Zero; + int hr = pTypeInfo.GetTypeAttr(ref pTypeAttr); + if (!NativeMethods.Succeeded(hr)) { + return 0; + } + + System.Runtime.InteropServices.ComTypes.TYPEATTR pTAStruct; + try { + try { + // just access directly...no marshalling needed! + // + pTAStruct = *(System.Runtime.InteropServices.ComTypes.TYPEATTR*)pTypeAttr; + } + catch { + + return 0; + } + + long result = 0; + + // we pull two things out of the struct: the + // number of functions and variables, and the version. + // since they are next to each other, we just pull the memory directly. + // + // the cFuncs and cVars are both shorts, so we read them as one block of ints. + // + // + int* pResult = (int*)&result; + byte* pbStruct = (byte*)&pTAStruct; + + // in the low byte, pull the number of props. + // + *pResult = *(int*)(pbStruct + CountMemberOffset); + + // move up to the high word of the long. + // + pResult++; + + // now pull out the version info. + // + *pResult = *(int*)(pbStruct + VersionOffset); + + // return that composite long. + // + return result; + } + finally { + pTypeInfo.ReleaseTypeAttr(pTypeAttr); + } + } + + internal bool CheckValid(bool checkVersions, bool callDispose) { + + if (this.AlwaysValid) { + return true; + } + + bool valid = weakObjRef != null && weakObjRef.IsAlive; + + // check the version information for each ITypeInfo the object exposes. + // + if (valid && checkVersions) { + + // + long[] newTypeInfoVersions = GetTypeInfoVersions(weakObjRef.Target); + + if (newTypeInfoVersions.Length != typeInfoVersions.Length) { + valid = false; + } else { + // compare each version number to the old one. + // + for (int i = 0; i < newTypeInfoVersions.Length; i++) { + + if (newTypeInfoVersions[i] != typeInfoVersions[i]) { + valid = false; + break; + } + } + } + + if (!valid) { + + // update to the new version list we have. + // + typeInfoVersions = newTypeInfoVersions; + } + } + + if (!valid && callDispose) { + // weak ref has died, so remove this from the hash table + // +#if DEBUG + if (DbgCom2PropertiesSwitch.TraceVerbose) Debug.WriteLine("Disposing reference to object " + dbgObjName + ", class=" + dbgObjClass + " (weakRef " + (weakObjRef == null ? "null" : "dead") + ")"); +#endif + + Dispose(); + } + + return valid; + } + + /// + /// + /// Set the props for this object, and notify each property + /// that we are now it's manager + /// + internal void SetProps(Com2PropertyDescriptor[] props) { + this.props = props; + if (props != null) { + for (int i = 0; i < props.Length; i++) { + props[i].PropertyManager = this; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyBuilderUITypeEditor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyBuilderUITypeEditor.cs new file mode 100644 index 000000000..122a1a67c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyBuilderUITypeEditor.cs @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Runtime.InteropServices; + using System.Windows.Forms.Design; + using System.Globalization; + + /// + /// + /// + internal class Com2PropertyBuilderUITypeEditor : Com2ExtendedUITypeEditor { + + private Com2PropertyDescriptor propDesc; + string guidString; + int bldrType; + + public Com2PropertyBuilderUITypeEditor(Com2PropertyDescriptor pd, string guidString, int type, UITypeEditor baseEditor) : base(baseEditor) { + propDesc = pd; + this.guidString = guidString; + this.bldrType = type; + } + + /// + /// + /// Takes the value returned from valueAccess.getValue() and modifies or replaces + /// the value, passing the result into valueAccess.setValue(). This is where + /// an editor can launch a modal dialog or create a drop down editor to allow + /// the user to modify the value. Host assistance in presenting UI to the user + /// can be found through the valueAccess.getService function. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + + IntPtr parentHandle = (IntPtr)UnsafeNativeMethods.GetFocus(); + + IUIService uiSvc = (IUIService)provider.GetService(typeof(IUIService)); + if (uiSvc != null) { + IWin32Window parent = uiSvc.GetDialogOwnerWindow(); + if (parent != null) { + parentHandle = parent.Handle; + } + } + + bool useValue = false; + //VARIANT pValue = null; + object pValue = value; + + try{ + object obj = propDesc.TargetObject; + + + if (obj is ICustomTypeDescriptor) { + obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(propDesc); + } + + + Debug.Assert(obj is NativeMethods.IProvidePropertyBuilder, "object is not IProvidePropertyBuilder"); + NativeMethods.IProvidePropertyBuilder propBuilder = (NativeMethods.IProvidePropertyBuilder)obj; + + if (NativeMethods.Failed(propBuilder.ExecuteBuilder(propDesc.DISPID, + guidString, + null, + new HandleRef(null, parentHandle), + ref pValue, ref useValue))){ + useValue = false; + } + + }catch(ExternalException ex) { + Debug.Fail("Failed to show property frame: " + ex.ErrorCode.ToString(CultureInfo.InvariantCulture)); + } + + if (useValue && (bldrType & _CTLBLDTYPE.CTLBLDTYPE_FEDITSOBJDIRECTLY) == 0){ + + return pValue;//pValue.ToVariant(); + } + return value; + } + + /// + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs new file mode 100644 index 000000000..1b267295d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs @@ -0,0 +1,1529 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Reflection; + using System.ComponentModel.Design; + using Microsoft.Win32; + using System.Collections; + using System.Text; + using System.Drawing.Design; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// This class wraps a com native property in a property descriptor. + /// It maintains all information relative to the basic (e.g. ITypeInfo) + /// information about the member dispid function, and converts that info + /// to meaningful managed code information. + /// + /// It also allows other objects to register listeners to add extended + /// information at runtime such as attributes of TypeConverters. + /// + internal class Com2PropertyDescriptor : PropertyDescriptor, ICloneable{ + private EventHandlerList events; + + /// + /// + /// Is this guy read only? + /// + private bool baseReadOnly; + private bool readOnly; + + /// + /// + /// The resoved native type -> clr type + /// + private Type propertyType; + + /// + /// + /// The dispid. This is also in a DispIDAttiribute, but we + /// need it a lot. + /// + private int dispid; + + private TypeConverter converter; + private object editor; + + /// + /// + /// The current display name to show for this property + /// + private string displayName; + + /// + /// + /// This is any extra data needed. For IDispatch types, it's the GUID of + /// the interface, etc. + /// + private object typeData; + + + /// + /// + /// Keeps track of which data members need to be refreshed. + /// + private int refreshState; + + /// + /// + /// Should we bother asking if refresh is needed? + /// + private bool queryRefresh = false; + + /// + /// + /// Our properties manager + /// + private Com2Properties com2props; + + + /// + /// + /// Our original baseline properties + /// + private Attribute[] baseAttrs; + + /// + /// + /// Our cached last value -- this is only + /// for checking if we should ask for a display value + /// + private Object lastValue; + + /// + /// + /// For Object and dispatch types, we hide them by default. + /// + private bool typeHide; + + /// + /// + /// Set if the metadata causes this property to always be hidden + /// + private bool canShow; + + /// + /// + /// This property is hidden because its get didn't return S_OK + /// + private bool hrHidden; + + /// + /// + /// Set if we are in the process of asking handlers for attributes + /// + private bool inAttrQuery; + + /// + /// + /// Our event signitures. + /// + private static readonly Object EventGetBaseAttributes = new Object(); + private static readonly Object EventGetDynamicAttributes = new Object(); + private static readonly Object EventShouldRefresh = new Object(); + private static readonly Object EventGetDisplayName = new Object(); + private static readonly Object EventGetDisplayValue = new Object(); + private static readonly Object EventGetIsReadOnly = new Object(); + + + private static readonly Object EventGetTypeConverterAndTypeEditor = new Object(); + + private static readonly Object EventShouldSerializeValue = new Object(); + private static readonly Object EventCanResetValue = new Object(); + private static readonly Object EventResetValue = new Object(); + + private static readonly Guid GUID_COLOR = new Guid("{66504301-BE0F-101A-8BBB-00AA00300CAB}"); + + + /// + /// + /// Our map of native types that we can map to managed types for editors + /// + private static IDictionary oleConverters; + + static Com2PropertyDescriptor() { + oleConverters = new SortedList(); + oleConverters[GUID_COLOR] = typeof(Com2ColorConverter); + oleConverters[typeof(SafeNativeMethods.IFontDisp).GUID] = typeof(Com2FontConverter); + oleConverters[typeof(UnsafeNativeMethods.IFont).GUID] = typeof(Com2FontConverter); + oleConverters[typeof(UnsafeNativeMethods.IPictureDisp).GUID] = typeof(Com2PictureConverter); + oleConverters[typeof(UnsafeNativeMethods.IPicture).GUID] = typeof(Com2PictureConverter); + } + + /// + /// + /// Should we convert our type? + /// + private Com2DataTypeToManagedDataTypeConverter valueConverter; + + + /// + /// + /// Ctor. + /// + public Com2PropertyDescriptor(int dispid, string name, Attribute[] attrs, bool readOnly, Type propType, Object typeData, bool hrHidden) + : base(name, attrs) { + this.baseReadOnly = readOnly; + this.readOnly = readOnly; + + this.baseAttrs = attrs; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.BaseAttributes, true); + + this.hrHidden = hrHidden; + + // readonly to begin with are always read only + SetNeedsRefresh(Com2PropertyDescriptorRefresh.ReadOnly, readOnly); + + this.propertyType = propType; + + this.dispid = dispid; + + if (typeData != null) { + this.typeData = typeData; + if (typeData is Com2Enum) { + converter = new Com2EnumConverter((Com2Enum)typeData); + } + else if (typeData is Guid) { + valueConverter = CreateOleTypeConverter((Type)oleConverters[(Guid)typeData]); + } + } + + // check if this thing is hidden from metadata + this.canShow = true; + + if (attrs != null) { + for (int i = 0; i < attrs.Length; i++) { + if (attrs[i].Equals(BrowsableAttribute.No) && !hrHidden) { + this.canShow = false; + break; + } + } + } + + if (this.canShow && (propType == typeof(Object) || (valueConverter == null && propType == typeof(UnsafeNativeMethods.IDispatch)))) { + this.typeHide = true; + } + } + + protected Attribute[] BaseAttributes { + get { + + if (GetNeedsRefresh(Com2PropertyDescriptorRefresh.BaseAttributes)) { + SetNeedsRefresh(Com2PropertyDescriptorRefresh.BaseAttributes, false); + + int baseCount = baseAttrs == null ? 0 : baseAttrs.Length; + + ArrayList attrList = new ArrayList(); + + if (baseCount != 0) { + attrList.AddRange(baseAttrs); + } + + OnGetBaseAttributes(new GetAttributesEvent(attrList)); + + if (attrList.Count != baseCount) { + this.baseAttrs = new Attribute[attrList.Count]; + } + + if (baseAttrs != null) { + attrList.CopyTo(this.baseAttrs, 0); + } + else { + baseAttrs = new Attribute[0]; + } + } + + return baseAttrs; + } + set { + baseAttrs = value; + } + } + + /// + /// + /// Attributes + /// + public override AttributeCollection Attributes { + get { + if (this.AttributesValid || this.InAttrQuery) { + return base.Attributes; + } + + // restore our base attributes + this.AttributeArray = this.BaseAttributes; + + ArrayList newAttributes = null; + + // if we are forcing a hide + if (typeHide && canShow) { + if (newAttributes == null) { + newAttributes = new ArrayList(AttributeArray); + } + newAttributes.Add(new BrowsableAttribute(false)); + } + else if (hrHidden) { + // check to see if the get still fails + Object target = this.TargetObject; + if (target != null) { + int hr = new ComNativeDescriptor().GetPropertyValue(target, this.dispid, new object[1]); + + // if not, go ahead and make this a browsable item + if (NativeMethods.Succeeded(hr)) { + // make it browsable + if (newAttributes == null) { + newAttributes = new ArrayList(AttributeArray); + } + newAttributes.Add(new BrowsableAttribute(true)); + hrHidden = false; + } + } + } + + this.inAttrQuery = true; + try { + + // demand get any extended attributes + ArrayList attrList = new ArrayList(); + + OnGetDynamicAttributes(new GetAttributesEvent(attrList)); + + Attribute ma; + + if (attrList.Count != 0 && newAttributes == null) { + newAttributes = new ArrayList(AttributeArray); + } + + // push any new attributes into the base type + for (int i=0; i < attrList.Count; i++) { + ma = (Attribute)attrList[i]; + newAttributes.Add(ma); + } + } + finally { + this.inAttrQuery = false; + } + + // these are now valid. + SetNeedsRefresh(Com2PropertyDescriptorRefresh.Attributes, false); + + // If we reconfigured attributes, then poke the new set back in. + // + if (newAttributes != null) { + Attribute[] temp = new Attribute[newAttributes.Count]; + newAttributes.CopyTo(temp, 0); + AttributeArray = temp; + } + + return base.Attributes; + } + + } + + /// + /// + /// Checks if the attributes are valid. Asks any clients if they + /// would like attributes requeried. + /// + protected bool AttributesValid{ + get{ + bool currentRefresh = !GetNeedsRefresh(Com2PropertyDescriptorRefresh.Attributes); + if (queryRefresh) { + GetRefreshStateEvent rse = new GetRefreshStateEvent(Com2ShouldRefreshTypes.Attributes, !currentRefresh); + OnShouldRefresh(rse); + currentRefresh = !rse.Value; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.Attributes, rse.Value); + } + return currentRefresh; + } + } + + /// + /// + /// Checks if this item can be shown. + /// + public bool CanShow{ + get{ + return this.canShow; + } + } + + + /// + /// + /// Retrieves the type of the component this PropertyDescriptor is bound to. + /// + public override Type ComponentType { + get { + return typeof(UnsafeNativeMethods.IDispatch); + } + } + + /// + /// + /// Retrieves the type converter for this property. + /// + public override TypeConverter Converter { + get { + if (TypeConverterValid) { + return converter; + } + + Object typeEd = null; + + GetTypeConverterAndTypeEditor(ref converter, typeof(UITypeEditor), ref typeEd); + + if (!TypeEditorValid) { + this.editor = typeEd; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeEditor, false); + } + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeConverter, false); + + return converter; + } + } + + /// + /// + /// Retrieves whether this component is applying a type conversion... + /// + public bool ConvertingNativeType { + get { + return(valueConverter != null); + } + } + + /// + /// + /// Retrieves the default value for this property. + /// + protected virtual Object DefaultValue { + get { + return null; + } + } + + /// + /// + /// Retrieves the DISPID for this item + /// + public int DISPID{ + get{ + return this.dispid; + } + } + + /// + /// + /// Gets the friendly name that should be displayed to the user in a window like + /// the Property Browser. + /// + public override string DisplayName { + get { + if (!this.DisplayNameValid) { + GetNameItemEvent gni = new GetNameItemEvent(base.DisplayName); + OnGetDisplayName(gni); + this.displayName = gni.NameString; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.DisplayName, false); + } + return this.displayName; + } + } + + /// + /// + /// Checks if the property display name is valid + /// asks clients if they would like display name requeried. + /// + protected bool DisplayNameValid{ + get{ + bool currentRefresh = !(displayName == null || GetNeedsRefresh(Com2PropertyDescriptorRefresh.DisplayName)); + if (queryRefresh) { + GetRefreshStateEvent rse = new GetRefreshStateEvent(Com2ShouldRefreshTypes.DisplayName, !currentRefresh); + OnShouldRefresh(rse); + SetNeedsRefresh(Com2PropertyDescriptorRefresh.DisplayName, rse.Value); + currentRefresh = !rse.Value; + } + return currentRefresh; + } + } + + protected EventHandlerList Events { + get { + if (events == null) { + events = new EventHandlerList(); + } + return events; + } + } + + protected bool InAttrQuery{ + get{ + return this.inAttrQuery; + } + } + + /// + /// + /// Indicates whether this property is read only. + /// + public override bool IsReadOnly { + get { + if (!this.ReadOnlyValid) { + this.readOnly |= (this.Attributes[typeof(ReadOnlyAttribute)].Equals(ReadOnlyAttribute.Yes)); + GetBoolValueEvent gbv = new GetBoolValueEvent(this.readOnly); + OnGetIsReadOnly(gbv); + this.readOnly = gbv.Value; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.ReadOnly, false); + } + return this.readOnly; + } + } + + internal Com2Properties PropertyManager{ + set{ + this.com2props = value; + } + get{ + return this.com2props; + } + } + + /// + /// + /// Retrieves the type of the property. + /// + public override Type PropertyType { + get { + // replace the type with the mapped converter type + if (valueConverter != null) { + return valueConverter.ManagedType; + } + else { + return propertyType; + } + } + } + + /// + /// + /// Checks if the read only state is valid. + /// Asks clients if they would like read-only requeried. + /// + protected bool ReadOnlyValid{ + get{ + if (baseReadOnly) { + return true; + } + + bool currentRefresh = !GetNeedsRefresh(Com2PropertyDescriptorRefresh.ReadOnly); + + if (queryRefresh) { + GetRefreshStateEvent rse = new GetRefreshStateEvent(Com2ShouldRefreshTypes.ReadOnly, !currentRefresh); + OnShouldRefresh(rse); + SetNeedsRefresh(Com2PropertyDescriptorRefresh.ReadOnly, rse.Value); + currentRefresh = !rse.Value; + } + return currentRefresh; + } + } + + /// + /// + /// Gets the Object that this descriptor was created for. + /// May be null if the Object's ref has died. + /// + public virtual Object TargetObject{ + get{ + if (com2props != null) { + return com2props.TargetObject; + } + return null; + } + } + + protected bool TypeConverterValid { + get { + bool currentRefresh =!(converter == null || GetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeConverter)); + if (queryRefresh) { + GetRefreshStateEvent rse = new GetRefreshStateEvent(Com2ShouldRefreshTypes.TypeConverter, !currentRefresh); + OnShouldRefresh(rse); + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeConverter, rse.Value); + currentRefresh = !rse.Value; + } + return currentRefresh; + } + } + + protected bool TypeEditorValid { + get { + bool currentRefresh = !(editor == null || GetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeEditor)); + + if (queryRefresh) { + GetRefreshStateEvent rse = new GetRefreshStateEvent(Com2ShouldRefreshTypes.TypeEditor, !currentRefresh); + OnShouldRefresh(rse); + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeEditor, rse.Value); + currentRefresh = !rse.Value; + } + return currentRefresh; + } + } + + + public event GetBoolValueEventHandler QueryCanResetValue { + add { + Events.AddHandler(EventCanResetValue, value); + } + remove { + Events.RemoveHandler(EventCanResetValue, value); + } + } + + public event GetAttributesEventHandler QueryGetBaseAttributes { + add { + Events.AddHandler(EventGetBaseAttributes, value); + } + remove { + Events.RemoveHandler(EventGetBaseAttributes, value); + } + } + + public event GetAttributesEventHandler QueryGetDynamicAttributes { + add { + Events.AddHandler(EventGetDynamicAttributes, value); + } + remove { + Events.RemoveHandler(EventGetDynamicAttributes, value); + } + } + + + public event GetNameItemEventHandler QueryGetDisplayName { + add { + Events.AddHandler(EventGetDisplayName, value); + } + remove { + Events.RemoveHandler(EventGetDisplayName, value); + } + } + + + public event GetNameItemEventHandler QueryGetDisplayValue { + add { + Events.AddHandler(EventGetDisplayValue, value); + } + remove { + Events.RemoveHandler(EventGetDisplayValue, value); + } + } + + + public event GetBoolValueEventHandler QueryGetIsReadOnly { + add { + Events.AddHandler(EventGetIsReadOnly, value); + } + remove { + Events.RemoveHandler(EventGetIsReadOnly, value); + } + } + + + public event GetTypeConverterAndTypeEditorEventHandler QueryGetTypeConverterAndTypeEditor { + add { + Events.AddHandler(EventGetTypeConverterAndTypeEditor, value); + } + remove { + Events.RemoveHandler(EventGetTypeConverterAndTypeEditor, value); + } + } + + + public event Com2EventHandler QueryResetValue { + add { + Events.AddHandler(EventResetValue, value); + } + remove { + Events.RemoveHandler(EventResetValue, value); + } + } + + + public event GetBoolValueEventHandler QueryShouldSerializeValue { + add { + Events.AddHandler(EventShouldSerializeValue, value); + } + remove { + Events.RemoveHandler(EventShouldSerializeValue, value); + } + } + + + + /// + /// + /// Indicates whether reset will change the value of the component. If there + /// is a DefaultValueAttribute, then this will return true if getValue returns + /// something different than the default value. If there is a reset method and + /// a shouldPersist method, this will return what shouldPersist returns. + /// If there is just a reset method, this always returns true. If none of these + /// cases apply, this returns false. + /// + public override bool CanResetValue(Object component) { + + if (component is ICustomTypeDescriptor) { + component = ((ICustomTypeDescriptor)component).GetPropertyOwner(this); + } + + if (component == this.TargetObject) { + GetBoolValueEvent gbv = new GetBoolValueEvent(false); + OnCanResetValue(gbv); + return gbv.Value; + } + return false; + } + + public object Clone() { + return new Com2PropertyDescriptor(this.dispid, this.Name, (Attribute[])this.baseAttrs.Clone(), this.readOnly, this.propertyType, this.typeData, this.hrHidden); + } + + /// + /// + /// Creates a converter Object, first by looking for a ctor with a Com2ProeprtyDescriptor + /// parameter, then using the default ctor if it is not found. + /// + private Com2DataTypeToManagedDataTypeConverter CreateOleTypeConverter(Type t) { + + if (t == null) { + return null; + } + + ConstructorInfo ctor = t.GetConstructor(new Type[]{typeof(Com2PropertyDescriptor)}); + Com2DataTypeToManagedDataTypeConverter converter; + if (ctor != null) { + converter = (Com2DataTypeToManagedDataTypeConverter)ctor.Invoke(new Object[]{this}); + } + else { + converter = (Com2DataTypeToManagedDataTypeConverter)Activator.CreateInstance(t); + } + return converter; + } + + /// + /// + /// Creates an instance of the member attribute collection. This can + /// be overriden by subclasses to return a subclass of AttributeCollection. + /// + protected override AttributeCollection CreateAttributeCollection() { + return new AttributeCollection(AttributeArray); + } + + private TypeConverter GetBaseTypeConverter() { + + if (PropertyType == null) { + return new TypeConverter(); + } + + TypeConverter localConverter = null; + + TypeConverterAttribute attr = (TypeConverterAttribute)Attributes[typeof(TypeConverterAttribute)]; + if (attr != null) { + string converterTypeName = attr.ConverterTypeName; + if (converterTypeName != null && converterTypeName.Length > 0) { + Type converterType = Type.GetType(converterTypeName); + if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) { + try { + localConverter = (TypeConverter)Activator.CreateInstance(converterType); + if (localConverter != null) { + refreshState |= Com2PropertyDescriptorRefresh.TypeConverterAttr; + } + } + catch (Exception ex) { + Debug.Fail("Failed to create TypeConverter of type '" + attr.ConverterTypeName + "' from Attribute", ex.ToString()); + } + } + } + } + + // if we didn't get one from the attribute, ask the type descriptor + if (localConverter == null) { + // we don't want to create the value editor for the IDispatch props because + // that will create the reference editor. We don't want that guy! + // + if (!typeof(UnsafeNativeMethods.IDispatch).IsAssignableFrom(this.PropertyType)) { + localConverter = base.Converter; + } + else { + localConverter = new Com2IDispatchConverter(this, false); + } + } + + if (localConverter == null) { + localConverter = new TypeConverter(); + } + return localConverter; + } + + private Object GetBaseTypeEditor(Type editorBaseType) { + + if (PropertyType == null) { + return null; + } + + Object localEditor = null; + EditorAttribute attr = (EditorAttribute)Attributes[typeof(EditorAttribute)]; + if (attr != null) { + string editorTypeName = attr.EditorBaseTypeName; + + if (editorTypeName != null && editorTypeName.Length > 0) { + Type attrEditorBaseType = Type.GetType(editorTypeName); + if (attrEditorBaseType != null && attrEditorBaseType == editorBaseType) { + Type type = Type.GetType(attr.EditorTypeName); + if (type != null) { + try { + localEditor = Activator.CreateInstance(type); + if (localEditor != null) { + refreshState |= Com2PropertyDescriptorRefresh.TypeEditorAttr; + } + } + catch (Exception ex) { + Debug.Fail("Failed to create edtior of type '" + attr.EditorTypeName + "' from Attribute", ex.ToString()); + } + } + } + } + } + if (localEditor == null) { + localEditor = base.GetEditor(editorBaseType); + } + return localEditor; + } + + /// + /// + /// Gets the value that should be displayed to the user, such as in + /// the Property Browser. + /// + public virtual string GetDisplayValue(string defaultValue) { + + GetNameItemEvent nie = new GetNameItemEvent(defaultValue); + OnGetDisplayValue(nie); + + string str = (nie.Name == null ? null : nie.Name.ToString()); + return str; + } + + /// + /// + /// Retrieves an editor of the requested type. + /// + public override Object GetEditor(Type editorBaseType) { + if (TypeEditorValid) { + return editor; + } + + if (PropertyType == null) { + return null; + } + + if (editorBaseType == typeof(UITypeEditor)) { + TypeConverter c = null; + GetTypeConverterAndTypeEditor(ref c, editorBaseType, ref editor); + + if (!TypeConverterValid) { + this.converter = c; + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeConverter, false); + } + SetNeedsRefresh(Com2PropertyDescriptorRefresh.TypeEditor, false); + } + else { + editor = base.GetEditor(editorBaseType); + } + return editor; + + } + + /// + /// + /// Retrieves the current native value of the property on component, + /// invoking the getXXX method. An exception in the getXXX + /// method will pass through. + /// + public Object GetNativeValue(Object component){ + if (component == null) + return null; + + if (component is ICustomTypeDescriptor) { + component = ((ICustomTypeDescriptor)component).GetPropertyOwner(this); + } + + if (component == null || !Marshal.IsComObject(component) || !(component is UnsafeNativeMethods.IDispatch)) + return null; + + UnsafeNativeMethods.IDispatch pDisp = (UnsafeNativeMethods.IDispatch)component; + Object[] pVarResult = new Object[1]; + NativeMethods.tagEXCEPINFO pExcepInfo = new NativeMethods.tagEXCEPINFO(); + Guid g = Guid.Empty; + + int hr = pDisp.Invoke(this.dispid, + ref g, + SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_PROPERTYGET, + new NativeMethods.tagDISPPARAMS(), + pVarResult, + pExcepInfo, null); + + switch (hr) { + case NativeMethods.S_OK: + case NativeMethods.S_FALSE: + + if (pVarResult[0] == null || Convert.IsDBNull(pVarResult[0])) { + lastValue = null; + } + else { + lastValue = pVarResult[0]; + } + return lastValue; + case NativeMethods.DISP_E_EXCEPTION: + //PrintExceptionInfo(pExcepInfo); + return null; + default: + throw new ExternalException(SR.GetString(SR.DispInvokeFailed, "GetValue" , hr), hr); + } + } + + /// + /// + /// Checks whether the particular item(s) need refreshing. + /// + private bool GetNeedsRefresh(int mask){ + return(refreshState & mask) != 0; + } + + + /// + /// + /// Retrieves the current value of the property on component, + /// invoking the getXXX method. An exception in the getXXX + /// method will pass through. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public override Object GetValue(Object component) { + lastValue = GetNativeValue(component); + // do we need to convert the type? + if (this.ConvertingNativeType && lastValue != null) { + lastValue = valueConverter.ConvertNativeToManaged(lastValue, this); + } + else if (lastValue != null && propertyType != null && propertyType.IsEnum && lastValue.GetType().IsPrimitive) { + // we've got to convert the value here -- we built the enum but the native object returns + // us values as integers + // + try { + lastValue = Enum.ToObject(propertyType, lastValue); + } + catch { + } + } + return lastValue; + } + + /// + /// + /// Retrieves the value editor for the property. If a value editor is passed + /// in as a TypeConverterAttribute, that value editor will be instantiated. + /// If no such attribute was found, a system value editor will be looked for. + /// See TypeConverter for a description of how system value editors are found. + /// If there is no system value editor, null is returned. If the value editor found + /// takes an IEditorSite in its constructor, the parameter will be passed in. + /// + public void GetTypeConverterAndTypeEditor(ref TypeConverter typeConverter, Type editorBaseType, ref Object typeEditor) { + + // get the base editor and converter, attributes first + TypeConverter localConverter = typeConverter; + Object localEditor = typeEditor; + + if (localConverter == null) { + localConverter = GetBaseTypeConverter(); + } + + if (localEditor == null) { + localEditor = GetBaseTypeEditor(editorBaseType); + } + + // if this is a object, get the value and attempt to create the correct value editor based on that value. + // we don't do this if the state came from an attribute + // + if (0 == (refreshState & Com2PropertyDescriptorRefresh.TypeConverterAttr) && this.PropertyType == typeof(Com2Variant)) { + Type editorType = PropertyType; + Object value = GetValue(TargetObject); + if (value != null) { + editorType = value.GetType(); + } + ComNativeDescriptor.ResolveVariantTypeConverterAndTypeEditor(value, ref localConverter, editorBaseType, ref localEditor); + } + + // now see if someone else would like to serve up a value editor + // + + // unwrap the editor if it's one of ours. + if (localConverter is Com2PropDescMainConverter) { + localConverter = ((Com2PropDescMainConverter)localConverter).InnerConverter; + } + + GetTypeConverterAndTypeEditorEvent e = new GetTypeConverterAndTypeEditorEvent(localConverter, localEditor); + OnGetTypeConverterAndTypeEditor(e); + localConverter = e.TypeConverter; + localEditor = e.TypeEditor; + + // just in case one of the handlers removed our editor... + // + if (localConverter == null) { + localConverter = GetBaseTypeConverter(); + } + + if (localEditor == null) { + localEditor = GetBaseTypeEditor(editorBaseType); + } + + // wrap the value editor in our main value editor, but only if it isn't "TypeConverter" or already a Com2PropDescMainTypeConverter + // + Type localConverterType = localConverter.GetType(); + if (localConverterType != typeof(TypeConverter) && localConverterType != (typeof(Com2PropDescMainConverter))) { + localConverter = new Com2PropDescMainConverter(this, localConverter); + } + + // save the values back to the variables. + // + typeConverter = localConverter; + typeEditor = localEditor; + } + + /// + /// + /// Is the given value equal to the last known value for this object? + /// + public bool IsCurrentValue(object value) { + return (value == lastValue || (lastValue != null && lastValue.Equals(value))); + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnCanResetValue(GetBoolValueEvent gvbe) { + RaiseGetBoolValueEvent(EventCanResetValue, gvbe); + } + + protected void OnGetBaseAttributes(GetAttributesEvent e) { + + try { + com2props.AlwaysValid = com2props.CheckValid(); + + GetAttributesEventHandler handler = (GetAttributesEventHandler)Events[EventGetBaseAttributes]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnGetDisplayName(GetNameItemEvent gnie) { + RaiseGetNameItemEvent(EventGetDisplayName, gnie); + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnGetDisplayValue(GetNameItemEvent gnie) { + RaiseGetNameItemEvent(EventGetDisplayValue, gnie); + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnGetDynamicAttributes(GetAttributesEvent e) { + + try { + com2props.AlwaysValid = com2props.CheckValid(); + GetAttributesEventHandler handler = (GetAttributesEventHandler)Events[EventGetDynamicAttributes]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + + + /// + /// + /// Raises the appropriate event + /// + protected void OnGetIsReadOnly(GetBoolValueEvent gvbe) { + RaiseGetBoolValueEvent(EventGetIsReadOnly, gvbe); + } + + protected void OnGetTypeConverterAndTypeEditor(GetTypeConverterAndTypeEditorEvent e) { + try { + com2props.AlwaysValid = com2props.CheckValid(); + GetTypeConverterAndTypeEditorEventHandler handler = (GetTypeConverterAndTypeEditorEventHandler)Events[EventGetTypeConverterAndTypeEditor]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnResetValue(EventArgs e) { + RaiseCom2Event(EventResetValue, e); + } + + /// + /// + /// Raises the appropriate event + /// + protected void OnShouldSerializeValue(GetBoolValueEvent gvbe) { + RaiseGetBoolValueEvent(EventShouldSerializeValue, gvbe); + } + + + /// + /// + /// Raises the appropriate event + /// + protected void OnShouldRefresh(GetRefreshStateEvent gvbe) { + RaiseGetBoolValueEvent(EventShouldRefresh, gvbe); + } + + /// + /// + /// Raises the appropriate event + /// + private void RaiseGetBoolValueEvent(Object key, GetBoolValueEvent e) { + try { + com2props.AlwaysValid = com2props.CheckValid(); + GetBoolValueEventHandler handler = (GetBoolValueEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + /// + /// + /// Raises the appropriate event + /// + private void RaiseCom2Event(Object key, EventArgs e) { + try { + com2props.AlwaysValid = com2props.CheckValid(); + Com2EventHandler handler = (Com2EventHandler)Events[key]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + /// + /// + /// Raises the appropriate event + /// + private void RaiseGetNameItemEvent(Object key, GetNameItemEvent e) { + try { + com2props.AlwaysValid = com2props.CheckValid(); + GetNameItemEventHandler handler = (GetNameItemEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + finally { + com2props.AlwaysValid = false; + } + } + + /// + /// + /// Will reset the default value for this property on the component. If + /// there was a default value passed in as a DefaultValueAttribute, that + /// value will be set as the value of the property on the component. If + /// there was no default value passed in, a ResetXXX method will be looked + /// for. If one is found, it will be invoked. If one is not found, this + /// is a nop. + /// + public override void ResetValue(Object component) { + if (component is ICustomTypeDescriptor) { + component = ((ICustomTypeDescriptor)component).GetPropertyOwner(this); + } + + if (component == this.TargetObject) { + OnResetValue(EventArgs.Empty); + } + } + + /// + /// + /// Sets whether the particular item(s) need refreshing. + /// + internal void SetNeedsRefresh(int mask, bool value){ + if (value) { + refreshState |= mask; + } + else { + refreshState &= ~mask; + } + } + + /// + /// + /// This will set value to be the new value of this property on the + /// component by invoking the setXXX method on the component. If the + /// value specified is invalid, the component should throw an exception + /// which will be passed up. The component designer should design the + /// property so that getXXX following a setXXX should return the value + /// passed in if no exception was thrown in the setXXX call. + /// + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + public override void SetValue(Object component, Object value) { + + if (this.readOnly) { + throw new NotSupportedException(SR.GetString(SR.COM2ReadonlyProperty, this.Name )); + } + + object owner = component; + if (owner is ICustomTypeDescriptor) { + owner = ((ICustomTypeDescriptor)owner).GetPropertyOwner(this); + } + + if (owner == null || !Marshal.IsComObject(owner) || !(owner is UnsafeNativeMethods.IDispatch)) { + return; + } + + // do we need to convert the type? + if (valueConverter != null) { + bool cancel = false; + value = valueConverter.ConvertManagedToNative(value, this, ref cancel); + if (cancel) { + return; + } + } + + UnsafeNativeMethods.IDispatch pDisp = (UnsafeNativeMethods.IDispatch)owner; + + NativeMethods.tagDISPPARAMS dp = new NativeMethods.tagDISPPARAMS(); + NativeMethods.tagEXCEPINFO excepInfo = new NativeMethods.tagEXCEPINFO(); + dp.cArgs = 1; + dp.cNamedArgs = 1; + int[] namedArgs = new int[]{NativeMethods.DISPID_PROPERTYPUT}; + GCHandle gcHandle = GCHandle.Alloc(namedArgs, GCHandleType.Pinned); + + try { + dp.rgdispidNamedArgs = Marshal.UnsafeAddrOfPinnedArrayElement(namedArgs, 0); + IntPtr mem = Marshal.AllocCoTaskMem( 16 /*Marshal.SizeOf(typeof(VARIANT)) */); + SafeNativeMethods.VariantInit(new HandleRef(null, mem)); + Marshal.GetNativeVariantForObject(value, mem); + dp.rgvarg = mem; + try { + + Guid g = Guid.Empty; + int hr = pDisp.Invoke(this.dispid, + ref g, + SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_PROPERTYPUT, + dp, + null, + excepInfo, new IntPtr[1]); + + + string errorInfo = null; + if (hr == NativeMethods.DISP_E_EXCEPTION && excepInfo.scode != 0) { + hr = excepInfo.scode; + errorInfo = excepInfo.bstrDescription; + } + + switch (hr) { + case NativeMethods.E_ABORT: + case NativeMethods.OLE_E_PROMPTSAVECANCELLED: + // cancelled checkout, etc. + return; + case NativeMethods.S_OK: + case NativeMethods.S_FALSE: + OnValueChanged(component, EventArgs.Empty); + lastValue = value; + return; + default: + + //Debug.Fail(String.Format("IDispatch::Invoke(INVOKE_PROPPUT) returned hr=0x{0:X}", hr)); + + if (pDisp is UnsafeNativeMethods.ISupportErrorInfo) { + g = typeof(UnsafeNativeMethods.IDispatch).GUID; + if (NativeMethods.Succeeded(((UnsafeNativeMethods.ISupportErrorInfo)pDisp).InterfaceSupportsErrorInfo(ref g))) { + UnsafeNativeMethods.IErrorInfo pErrorInfo = null; + UnsafeNativeMethods.GetErrorInfo(0, ref pErrorInfo); + string info= null; + if (pErrorInfo != null) { + if (NativeMethods.Succeeded(pErrorInfo.GetDescription(ref info))) { + errorInfo = info; + } + } + + } + } + else if (errorInfo == null) { + StringBuilder strMessage = new StringBuilder(256); + + int result = SafeNativeMethods.FormatMessage(NativeMethods.FORMAT_MESSAGE_FROM_SYSTEM | + NativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS, + NativeMethods.NullHandleRef, + hr, + CultureInfo.CurrentCulture.LCID, + strMessage, + 255, + NativeMethods.NullHandleRef); + + + if (result == 0) { + errorInfo = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DispInvokeFailed, "SetValue", hr)); + } + else { + errorInfo = strMessage.ToString(); + // strip of any trailing cr/lf + while (errorInfo.Length > 0 && + errorInfo[errorInfo.Length -1] == '\n' || + errorInfo[errorInfo.Length -1] == '\r') { + errorInfo = errorInfo.Substring(0, errorInfo.Length-1); + } + } + } + throw new ExternalException(errorInfo, hr); + } + } + finally { + SafeNativeMethods.VariantClear(new HandleRef(null, mem)); + Marshal.FreeCoTaskMem(mem); + } + } + finally { + gcHandle.Free(); + } + } + + /// + /// + /// Indicates whether the value of this property needs to be persisted. In + /// other words, it indicates whether the state of the property is distinct + /// from when the component is first instantiated. If there is a default + /// value specified in this PropertyDescriptor, it will be compared against the + /// property's current value to determine this. If there is't, the + /// shouldPersistXXX method is looked for and invoked if found. If both + /// these routes fail, true will be returned. + /// + /// If this returns false, a tool should not persist this property's value. + /// + public override bool ShouldSerializeValue(Object component) { + GetBoolValueEvent gbv = new GetBoolValueEvent(false); + OnShouldSerializeValue(gbv); + return gbv.Value; + } + /// + /// + /// we wrap all value editors in this one so we can intercept + /// the GetTextFromValue calls for objects that would like + /// to modify the display name + /// + private class Com2PropDescMainConverter : Com2ExtendedTypeConverter { + Com2PropertyDescriptor pd; + + private const int CheckSubprops = 0; + private const int AllowSubprops = 1; + private const int SupressSubprops = 2; + + + private int subprops = CheckSubprops; + + public Com2PropDescMainConverter(Com2PropertyDescriptor pd, TypeConverter baseConverter) : base(baseConverter) { + this.pd = pd; + } + + public override Object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType) { + Object baseConversion = base.ConvertTo(context, culture, value, destinationType); + if (destinationType == typeof(string)) { + // if this is our current value, ask if it should be changed for display, + // otherwise we'll ask for our enum drop downs, which we don't wanna do! + // + if (pd.IsCurrentValue(value)) { + // don't ever do this for enum types + if (!pd.PropertyType.IsEnum) { + Com2EnumConverter baseConverter = (Com2EnumConverter)GetWrappedConverter(typeof(Com2EnumConverter)); + if (baseConverter == null) { + return pd.GetDisplayValue((string)baseConversion); + } + else { + return baseConverter.ConvertTo(value, destinationType); + } + } + } + } + return baseConversion; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(value, attributes); + + if (props != null && props.Count > 0) { + // Return sorted read-only collection (can't sort original because its read-only) + props = props.Sort(); + PropertyDescriptor[] descs = new PropertyDescriptor[props.Count]; + props.CopyTo(descs, 0); + props = new PropertyDescriptorCollection(descs, true); + } + + return props; + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + if (subprops == CheckSubprops) { + if (!base.GetPropertiesSupported(context)){ + subprops = SupressSubprops; + } + else { + // special case the font converter here. + // + if ((pd.valueConverter != null && pd.valueConverter.AllowExpand) || Com2IVsPerPropertyBrowsingHandler.AllowChildProperties(this.pd)) { + subprops = AllowSubprops; + } + } + } + return (subprops == AllowSubprops); + } + } + } + + internal class GetAttributesEvent : EventArgs { + private ArrayList attrList; + + public GetAttributesEvent(ArrayList attrList) { + this.attrList = attrList; + + } + + public void Add(Attribute attribute) { + attrList.Add(attribute); + } + } + + internal delegate void Com2EventHandler(Com2PropertyDescriptor sender, EventArgs e); + + internal delegate void GetAttributesEventHandler(Com2PropertyDescriptor sender, GetAttributesEvent gaevent); + + internal class GetNameItemEvent : EventArgs { + private Object nameItem; + + public GetNameItemEvent(Object defName) { + this.nameItem = defName; + + } + + public Object Name{ + get{ + return nameItem; + } + set{ + nameItem = value; + } + } + + public string NameString{ + get{ + if (nameItem != null) { + return nameItem.ToString(); + } + return ""; + } + } + } + + + internal delegate void GetNameItemEventHandler(Com2PropertyDescriptor sender, GetNameItemEvent gnievent); + + + internal class GetBoolValueEvent : EventArgs { + private bool value; + + public GetBoolValueEvent(bool defValue) { + this.value= defValue; + + } + + public bool Value{ + get{ + return value; + } + set{ + this.value = value; + } + } + } + + internal delegate void GetBoolValueEventHandler(Com2PropertyDescriptor sender, GetBoolValueEvent gbeevent); + + internal class GetRefreshStateEvent : GetBoolValueEvent { + + Com2ShouldRefreshTypes item; + + public GetRefreshStateEvent(Com2ShouldRefreshTypes item, bool defValue) : base(defValue) { + this.item = item; + } + } + + internal delegate void GetTypeConverterAndTypeEditorEventHandler(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent e); + + internal class GetTypeConverterAndTypeEditorEvent : EventArgs { + private TypeConverter typeConverter; + private Object typeEditor; + + public GetTypeConverterAndTypeEditorEvent(TypeConverter typeConverter, Object typeEditor) { + this.typeEditor = typeEditor; + this.typeConverter = typeConverter; + } + + public TypeConverter TypeConverter{ + get{ + return typeConverter; + } + set{ + typeConverter = value; + } + } + + public Object TypeEditor{ + get{ + return typeEditor; + } + set{ + typeEditor = value; + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptorRefresh.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptorRefresh.cs new file mode 100644 index 000000000..038ced132 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyDescriptorRefresh.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class Com2PropertyDescriptorRefresh{ + public const int Attributes = 0x0001; + public const int DisplayName = 0x0002; + public const int ReadOnly = 0x0004; + public const int TypeConverter = 0x0020; + public const int TypeEditor = 0x0040; + + public const int All = 0x00FF; + + public const int TypeConverterAttr = 0x2000; + public const int TypeEditorAttr = 0x4000; + public const int BaseAttributes = 0x8000; + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyPageUITypeConverter.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyPageUITypeConverter.cs new file mode 100644 index 000000000..3f90d779f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2PropertyPageUITypeConverter.cs @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Runtime.InteropServices; + using System.Windows.Forms.Design; + + /// + /// + /// + internal class Com2PropertyPageUITypeEditor : Com2ExtendedUITypeEditor, ICom2PropertyPageDisplayService { + + private Com2PropertyDescriptor propDesc; + private Guid guid; + + public Com2PropertyPageUITypeEditor(Com2PropertyDescriptor pd, Guid guid, UITypeEditor baseEditor) : base(baseEditor){ + propDesc = pd; + this.guid = guid; + } + + /// + /// + /// Takes the value returned from valueAccess.getValue() and modifies or replaces + /// the value, passing the result into valueAccess.setValue(). This is where + /// an editor can launch a modal dialog or create a drop down editor to allow + /// the user to modify the value. Host assistance in presenting UI to the user + /// can be found through the valueAccess.getService function. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + + IntPtr hWndParent = UnsafeNativeMethods.GetFocus(); // Windows.GetForegroundWindow + + try { + + + ICom2PropertyPageDisplayService propPageSvc = (ICom2PropertyPageDisplayService)provider.GetService(typeof(ICom2PropertyPageDisplayService)); + + if (propPageSvc == null) { + propPageSvc = this; + } + + object instance = context.Instance; + + if (!instance.GetType().IsArray) { + instance = propDesc.TargetObject; + if (instance is ICustomTypeDescriptor) { + instance = ((ICustomTypeDescriptor)instance).GetPropertyOwner(propDesc); + } + } + + propPageSvc.ShowPropertyPage(propDesc.Name, instance, propDesc.DISPID, this.guid, hWndParent); + + } + catch (Exception ex1) { + if (provider != null) { + IUIService uiSvc = (IUIService)provider.GetService(typeof(IUIService)); + if (uiSvc != null){ + uiSvc.ShowError(ex1, SR.GetString(SR.ErrorTypeConverterFailed)); + } + } + } + return value; + } + + /// + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + + public unsafe void ShowPropertyPage(string title, object component, int dispid, Guid pageGuid, IntPtr parentHandle){ + Guid[] guids = new Guid[]{pageGuid}; + IntPtr guidsAddr = Marshal.UnsafeAddrOfPinnedArrayElement(guids, 0); + + object[] objs = component.GetType().IsArray ? (object[])component : new object[]{component}; + + int nObjs = objs.Length; + IntPtr[] objAddrs = new IntPtr[nObjs]; + + try { + for (int i=0; i < nObjs; i++) { + objAddrs[i] = Marshal.GetIUnknownForObject(objs[i]); + } + + fixed (IntPtr* pAddrs = objAddrs) { + SafeNativeMethods.OleCreatePropertyFrame(new HandleRef(null, parentHandle), + 0, 0, + title, + nObjs, + new HandleRef(null, (IntPtr)(long)pAddrs), + 1, + new HandleRef(null, guidsAddr), + SafeNativeMethods.GetThreadLCID(), + 0, IntPtr.Zero ); + } + } finally { + for (int i=0; i < nObjs; i++) { + if (objAddrs[i] != IntPtr.Zero) { + Marshal.Release(objAddrs[i]); + } + } + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ShouldRefreshTypes.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ShouldRefreshTypes.cs new file mode 100644 index 000000000..2908f9d26 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2ShouldRefreshTypes.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + + using System.Diagnostics; + + internal enum Com2ShouldRefreshTypes{ + Attributes, + DisplayName, + ReadOnly, + TypeConverter, + TypeEditor + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2TypeInfoProcessor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2TypeInfoProcessor.cs new file mode 100644 index 000000000..04e7c28ab --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/COM2TypeInfoProcessor.cs @@ -0,0 +1,1220 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Windows.Forms; + using System.ComponentModel.Design; + using Microsoft.Win32; + using System.Collections; + using Hashtable = System.Collections.Hashtable; + + using System.Reflection.Emit; + using System.Reflection; + using System.Threading; + using System.Globalization; + + + /// + /// + /// This is the main worker class of Com2 property interop. It takes an IDispatch Object + /// and translates it's ITypeInfo into Com2PropertyDescriptor objects that are understandable + /// by managed code. + /// + /// This class only knows how to process things that are natively in the typeinfo. Other property + /// information such as IPerPropertyBrowsing is handled elsewhere. + /// + internal class Com2TypeInfoProcessor { + + #if DEBUG + private static TraceSwitch DbgTypeInfoProcessorSwitch = new TraceSwitch("DbgTypeInfoProcessor", "Com2TypeInfoProcessor: debug Com2 type info processing"); + #else + private static TraceSwitch DbgTypeInfoProcessorSwitch; + #endif + + private Com2TypeInfoProcessor() { + } + + private static ModuleBuilder moduleBuilder = null; + + private static ModuleBuilder ModuleBuilder { + get { + if (moduleBuilder == null) { + AppDomain currentDomain = Thread.GetDomain(); + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = "COM2InteropEmit"; + AssemblyBuilder aBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + moduleBuilder = aBuilder.DefineDynamicModule("COM2Interop.Emit"); + } + return moduleBuilder; + } + } + + private static Hashtable builtEnums; + private static Hashtable processedLibraries; + + + /// + /// + /// Given an Object, this attempts to locate its type ifo + /// + public static UnsafeNativeMethods.ITypeInfo FindTypeInfo(Object obj, bool wantCoClass) { + UnsafeNativeMethods.ITypeInfo pTypeInfo = null; + + // this is kind of odd. What's going on here is that + // if we want the CoClass (e.g. for the interface name), + // we need to look for IProvideClassInfo first, then + // look for the typeinfo from the IDispatch. + // In the case of many OleAut32 operations, the CoClass + // doesn't have the interface members on it, although + // in the shell it usually does, so + // we need to re-order the lookup if we _actually_ want + // the CoClass if it's available. + // + + for (int i = 0; pTypeInfo == null && i < 2; i++) { + + if (wantCoClass == (i == 0)){ + if (obj is NativeMethods.IProvideClassInfo) { + NativeMethods.IProvideClassInfo pProvideClassInfo = (NativeMethods.IProvideClassInfo)obj; + try { + pTypeInfo = pProvideClassInfo.GetClassInfo(); + } + catch { + } + } + } + else { + if (obj is UnsafeNativeMethods.IDispatch) { + UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)obj; + try { + pTypeInfo = iDispatch.GetTypeInfo(0, SafeNativeMethods.GetThreadLCID()); + } + catch { + } + } + } + + } + return pTypeInfo; + } + + + /// + /// + /// Given an Object, this attempts to locate its type info. If it implementes IProvideMultipleClassInfo + /// all available type infos will be returned, otherwise the primary one will be alled. + /// + public static UnsafeNativeMethods.ITypeInfo[] FindTypeInfos(Object obj, bool wantCoClass){ + + UnsafeNativeMethods.ITypeInfo[] typeInfos = null; + int n = 0; + UnsafeNativeMethods.ITypeInfo temp = null; + + if (obj is NativeMethods.IProvideMultipleClassInfo) { + NativeMethods.IProvideMultipleClassInfo pCI = (NativeMethods.IProvideMultipleClassInfo)obj; + if (!NativeMethods.Succeeded(pCI.GetMultiTypeInfoCount(ref n)) || n == 0) { + n = 0; + } + + if (n > 0){ + typeInfos = new UnsafeNativeMethods.ITypeInfo[n]; + + for (int i = 0; i < n; i++){ + if (NativeMethods.Failed(pCI.GetInfoOfIndex(i, 1 /*MULTICLASSINFO_GETTYPEINFO*/, ref temp, 0, 0, IntPtr.Zero, IntPtr.Zero))){ + continue; + } + Debug.Assert(temp != null, "IProvideMultipleClassInfo::GetInfoOfIndex returned S_OK for ITypeInfo index " + i + ", this is a issue in the object that's being browsed, NOT the property browser."); + typeInfos[i] = temp; + } + } + } + + if (typeInfos == null || typeInfos.Length == 0){ + temp = FindTypeInfo(obj, wantCoClass); + if (temp != null) { + typeInfos = new UnsafeNativeMethods.ITypeInfo[]{temp}; + } + } + + return typeInfos; + } + + /// + /// + /// Retrieve the dispid of the property that we are to use as the name + /// member. In this case, the grid will put parens around the name. + /// + public static int GetNameDispId(UnsafeNativeMethods.IDispatch obj){ + int dispid = NativeMethods.DISPID_UNKNOWN; + string[] names = null; + + ComNativeDescriptor cnd = ComNativeDescriptor.Instance; + bool succeeded = false; + + // first try to find one with a valid value + cnd.GetPropertyValue(obj, "__id", ref succeeded); + + if (succeeded) { + names = new string[]{"__id"}; + } + else { + cnd.GetPropertyValue(obj, NativeMethods.ActiveX.DISPID_Name, ref succeeded); + if (succeeded) { + dispid = NativeMethods.ActiveX.DISPID_Name; + } + else { + cnd.GetPropertyValue(obj, "Name", ref succeeded); + if (succeeded) { + names = new string[]{"Name"}; + } + } + } + + // now get the dispid of the one that worked... + if (names != null) { + int[] pDispid = new int[]{NativeMethods.DISPID_UNKNOWN}; + Guid g = Guid.Empty; + int hr = obj.GetIDsOfNames(ref g, names, 1, SafeNativeMethods.GetThreadLCID(), pDispid); + if (NativeMethods.Succeeded(hr)){ + + dispid = pDispid[0]; + } + } + + return dispid; + } + + + /// + /// + /// Gets the properties for a given Com2 Object. The returned Com2Properties + /// Object contains the properties and relevant data about them. + /// + public static Com2Properties GetProperties(Object obj) { + + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties"); + + if (obj == null || !Marshal.IsComObject(obj)) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties returning null: Object is not a com Object"); + return null; + } + + UnsafeNativeMethods.ITypeInfo[] typeInfos = FindTypeInfos(obj, false); + + // oops, looks like this guy doesn't surface any type info + // this is okay, so we just say it has no props + if (typeInfos == null || typeInfos.Length == 0) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties :: Didn't get typeinfo"); + return null; + } + + + int defaultProp = -1; + int temp = -1; + ArrayList propList = new ArrayList(); + Guid[] typeGuids = new Guid[typeInfos.Length]; + + for (int i = 0; i < typeInfos.Length; i++) { + UnsafeNativeMethods.ITypeInfo ti = typeInfos[i]; + + if (ti == null) { + continue; + } + + int[] versions = new int[2]; + Guid typeGuid = GetGuidForTypeInfo(ti, null, versions); + PropertyDescriptor[] props = null; + bool dontProcess = typeGuid != Guid.Empty && processedLibraries != null && processedLibraries.Contains(typeGuid); + + if (dontProcess) { + CachedProperties cp = (CachedProperties)processedLibraries[typeGuid]; + + if (versions[0] == cp.MajorVersion && versions[1] == cp.MinorVersion) { + props = cp.Properties; + if (i == 0 && cp.DefaultIndex != -1) { + defaultProp = cp.DefaultIndex; + } + } + else { + dontProcess = false; + } + } + + if (!dontProcess) { + props = InternalGetProperties(obj, ti, NativeMethods.MEMBERID_NIL, ref temp); + + // only save the default property from the first type Info + if (i == 0 && temp != -1) { + defaultProp = temp; + } + + if (processedLibraries == null) { + processedLibraries = new Hashtable(); + } + + if (typeGuid != Guid.Empty) { + processedLibraries[typeGuid] = new CachedProperties(props, i == 0 ? defaultProp : -1, versions[0], versions[1]); + } + } + + if (props != null){ + propList.AddRange(props); + } + } + + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Com2TypeInfoProcessor.GetProperties : returning " + propList.Count.ToString(CultureInfo.InvariantCulture) + " properties"); + + // done! + Com2PropertyDescriptor[] temp2 = new Com2PropertyDescriptor[propList.Count]; + propList.CopyTo(temp2, 0); + + return new Com2Properties(obj, temp2, defaultProp); + } + + private static Guid GetGuidForTypeInfo(UnsafeNativeMethods.ITypeInfo typeInfo, StructCache structCache, int[] versions) { + IntPtr pTypeAttr = IntPtr.Zero; + int hr = typeInfo.GetTypeAttr(ref pTypeAttr); + if (!NativeMethods.Succeeded(hr)) { + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); + } + + Guid g = Guid.Empty; + NativeMethods.tagTYPEATTR typeAttr = null; + try { + + + if (structCache == null) { + typeAttr = new NativeMethods.tagTYPEATTR(); + } + else { + typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR)); + } + UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); + g = typeAttr.guid; + if (versions != null) { + versions[0] = typeAttr.wMajorVerNum; + versions[1] = typeAttr.wMinorVerNum; + } + } + finally { + typeInfo.ReleaseTypeAttr(pTypeAttr); + if (structCache != null && typeAttr != null) { + structCache.ReleaseStruct(typeAttr); + } + } + + return g; + } + + + /// + /// + /// Resolves a value type for a property from a TYPEDESC. Value types can be + /// user defined, which and may be aliased into other type infos. This function + /// will recusively walk the ITypeInfos to resolve the type to a clr Type. + /// + private static Type GetValueTypeFromTypeDesc(NativeMethods.tagTYPEDESC typeDesc, UnsafeNativeMethods.ITypeInfo typeInfo, Object[] typeData, StructCache structCache) { + IntPtr hreftype; + int hr = 0; + + switch ((NativeMethods.tagVT)typeDesc.vt) { + default: + return VTToType((NativeMethods.tagVT)typeDesc.vt); + + case NativeMethods.tagVT.VT_UNKNOWN: + case NativeMethods.tagVT.VT_DISPATCH: + // get the guid + typeData[0] = GetGuidForTypeInfo(typeInfo, structCache, null); + + // return the type + return VTToType((NativeMethods.tagVT)typeDesc.vt); + + case NativeMethods.tagVT.VT_USERDEFINED: + // we'll need to recurse into a user defined reference typeinfo + Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an hreftype!"); + hreftype = typeDesc.unionMember; + break; + + case NativeMethods.tagVT.VT_PTR: + // we'll need to recurse into a user defined reference typeinfo + Debug.Assert(typeDesc.unionMember != IntPtr.Zero, "typeDesc doesn't contain an refTypeDesc!"); + NativeMethods.tagTYPEDESC refTypeDesc = (NativeMethods.tagTYPEDESC)structCache.GetStruct(typeof(NativeMethods.tagTYPEDESC)); + + try { + + try { + //(tagTYPEDESC)Marshal.PtrToStructure(typeDesc.unionMember, typeof(tagTYPEDESC)); + UnsafeNativeMethods.PtrToStructure(typeDesc.unionMember, refTypeDesc); + } + catch { + // above is failing, why? + refTypeDesc = new NativeMethods.tagTYPEDESC(); + refTypeDesc.unionMember = (IntPtr)Marshal.ReadInt32(typeDesc.unionMember); + refTypeDesc.vt = Marshal.ReadInt16(typeDesc.unionMember, 4); + } + + if (refTypeDesc.vt == (int)NativeMethods.tagVT.VT_VARIANT) { + return VTToType((NativeMethods.tagVT)refTypeDesc.vt); + } + hreftype = refTypeDesc.unionMember; + } + finally { + structCache.ReleaseStruct(refTypeDesc); + } + break; + } + + // get the reference type info + UnsafeNativeMethods.ITypeInfo refTypeInfo = null; + + hr = typeInfo.GetRefTypeInfo(hreftype, ref refTypeInfo); + if (!NativeMethods.Succeeded(hr)) { + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetRefTypeInfoFailed, hr), hr); + } + + try { + // here is where we look at the next level type info. + // if we get an enum, process it, otherwise we will recurse + // or get a dispatch. + // + if (refTypeInfo != null) { + IntPtr pRefTypeAttr = IntPtr.Zero; + hr = refTypeInfo.GetTypeAttr(ref pRefTypeAttr); + + if (!NativeMethods.Succeeded(hr)) { + + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); + } + + NativeMethods.tagTYPEATTR refTypeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pRefTypeAttr, typeof(tagTYPEATTR)); + UnsafeNativeMethods.PtrToStructure(pRefTypeAttr, refTypeAttr); + try { + Guid g = refTypeAttr.guid; + + // save the guid if we've got one here + if (!Guid.Empty.Equals(g)){ + typeData[0] = g; + } + + switch ((NativeMethods.tagTYPEKIND)refTypeAttr.typekind) { + + case NativeMethods.tagTYPEKIND.TKIND_ENUM: + return ProcessTypeInfoEnum(refTypeInfo, structCache); + //return VTToType(tagVT.VT_I4); + case NativeMethods.tagTYPEKIND.TKIND_ALIAS: + // recurse here + return GetValueTypeFromTypeDesc(refTypeAttr.Get_tdescAlias(), refTypeInfo, typeData, structCache); + case NativeMethods.tagTYPEKIND.TKIND_DISPATCH: + return VTToType(NativeMethods.tagVT.VT_DISPATCH); + case NativeMethods.tagTYPEKIND.TKIND_INTERFACE: + case NativeMethods.tagTYPEKIND.TKIND_COCLASS: + return VTToType(NativeMethods.tagVT.VT_UNKNOWN); + default: + return null; + } + } + finally { + refTypeInfo.ReleaseTypeAttr(pRefTypeAttr); + structCache.ReleaseStruct(refTypeAttr); + } + } + } + finally { + refTypeInfo = null; + } + return null; + } + + private static PropertyDescriptor[] InternalGetProperties(Object obj, UnsafeNativeMethods.ITypeInfo typeInfo, int dispidToGet, ref int defaultIndex) { + + if (typeInfo == null) { + return null; + } + + Hashtable propInfos = new Hashtable(); + + int nameDispID = GetNameDispId((UnsafeNativeMethods.IDispatch)obj); + bool addAboutBox = false; + + StructCache structCache = new StructCache(); + + // properties can live as functions with get_ and put_ or + // as variables, so we do two steps here. + try { + // DO FUNCDESC things + ProcessFunctions(typeInfo, propInfos, dispidToGet, nameDispID, ref addAboutBox, structCache); + } + catch (ExternalException ex) { + Debug.Fail("ProcessFunctions failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString()); + } + + try { + // DO VARDESC things. + ProcessVariables(typeInfo, propInfos, dispidToGet, nameDispID, structCache); + } + catch (ExternalException ex) { + Debug.Fail("ProcessVariables failed with hr=" + ex.ErrorCode.ToString(CultureInfo.InvariantCulture) + ", message=" + ex.ToString()); + } + + typeInfo = null; + + + // now we take the propertyInfo structures we built up + // and use them to create the actual descriptors. + int cProps = propInfos.Count; + + if (addAboutBox) { + cProps++; + } + + PropertyDescriptor[] props = new PropertyDescriptor[cProps]; + int defaultProp = -1; + + int hr = NativeMethods.S_OK; + Object[] pvar = new Object[1]; + ComNativeDescriptor cnd = ComNativeDescriptor.Instance; + + // for each item in uur list, create the descriptor an check + // if it's the default one. + foreach (PropInfo pi in propInfos.Values){ + if (!pi.NonBrowsable) { + // finally, for each property, make sure we can get the value + // if we can't then we should mark it non-browsable + + try { + hr = cnd.GetPropertyValue(obj, pi.DispId, pvar); + } + catch (ExternalException ex) { + hr = ex.ErrorCode; + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "IDispatch::Invoke(PROPGET, " + pi.Name + ") threw an exception :" + ex.ToString()); + } + if (!NativeMethods.Succeeded(hr)) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Adding Browsable(false) to property '" + pi.Name + "' because Invoke(dispid=0x{0:X} ,DISPATCH_PROPERTYGET) returned hr=0x{1:X}. Properties that do not return S_OK are hidden by default.", pi.DispId, hr)); + pi.Attributes.Add(new BrowsableAttribute(false)); + pi.NonBrowsable = true; + } + } + else { + hr = NativeMethods.S_OK; + } + + Attribute[] temp = new Attribute[pi.Attributes.Count]; + pi.Attributes.CopyTo(temp, 0); + //Debug.Assert(pi.nonbrowsable || pi.valueType != null, "Browsable property '" + pi.name + "' has a null type"); + props[pi.Index] = new Com2PropertyDescriptor(pi.DispId, pi.Name, temp, pi.ReadOnly != PropInfo.ReadOnlyFalse, pi.ValueType, pi.TypeData, !NativeMethods.Succeeded(hr)); + if (pi.IsDefault) { + defaultProp = pi.Index; + } + } + + if (addAboutBox) { + props[props.Length-1] = new Com2AboutBoxPropertyDescriptor(); + } + return props; + } + + + private static PropInfo ProcessDataCore(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispid, int nameDispID, NativeMethods.tagTYPEDESC typeDesc, int flags, StructCache structCache) { + string pPropName = null; + string pPropDesc = null; + + + // get the name and the helpstring + int hr = typeInfo.GetDocumentation(dispid, ref pPropName, ref pPropDesc, null, null); + + ComNativeDescriptor cnd = ComNativeDescriptor.Instance; + + + if (!NativeMethods.Succeeded(hr)) { + throw new COMException(SR.GetString(SR.TYPEINFOPROCESSORGetDocumentationFailed, dispid, hr, cnd.GetClassName(typeInfo)), hr); + } + + if (pPropName == null){ + Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ITypeInfo::GetDocumentation didn't return a name for DISPID 0x{0:X} but returned SUCEEDED(hr), Component=" + cnd.GetClassName(typeInfo), dispid)); + return null; + } + + // now we can create our struct... make sure we don't already have one + PropInfo pi = (PropInfo)propInfoList[pPropName]; + + if (pi == null) { + pi = new PropInfo(); + pi.Index = propInfoList.Count; + propInfoList[pPropName] = pi; + pi.Name = pPropName; + pi.DispId = dispid; + pi.Attributes.Add(new DispIdAttribute(pi.DispId)); + } + + if (pPropDesc != null) { + pi.Attributes.Add(new DescriptionAttribute(pPropDesc)); + } + + // figure out the value type + if (pi.ValueType == null) { + Object[] pTypeData = new Object[1]; + try { + pi.ValueType = GetValueTypeFromTypeDesc(typeDesc, typeInfo, pTypeData, structCache); + } + catch (Exception ex) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "Hiding property " + pi.Name + " because value Type could not be resolved: " + ex.ToString()); + } + + // if we can't resolve the type, mark the property as nonbrowsable + // from the browser + // + if (pi.ValueType == null) { + pi.NonBrowsable = true; + } + + if (pi.NonBrowsable) { + flags |= (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE; + } + + if (pTypeData[0] != null) { + pi.TypeData = pTypeData[0]; + } + } + + // check the flags + if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FREADONLY) != 0) { + pi.ReadOnly = PropInfo.ReadOnlyTrue; + } + + if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FHIDDEN) != 0 || + (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FNONBROWSABLE) != 0 || + pi.Name[0] == '_' || + dispid == NativeMethods.ActiveX.DISPID_HWND) { + pi.Attributes.Add(new BrowsableAttribute(false)); + pi.NonBrowsable = true; + } + + if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FUIDEFAULT) != 0) { + pi.IsDefault = true; + } + + if ((flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FBINDABLE) != 0 && + (flags & (int)NativeMethods.tagVARFLAGS.VARFLAG_FDISPLAYBIND) != 0) { + pi.Attributes.Add(new BindableAttribute(true)); + } + + // lastly, if it's DISPID_Name, add the ParenthesizeNameAttribute + if (dispid == nameDispID){ + pi.Attributes.Add(new ParenthesizePropertyNameAttribute(true)); + + // don't allow merges on the name + pi.Attributes.Add(new MergablePropertyAttribute(false)); + } + + return pi; + } + + private static void ProcessFunctions(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, ref bool addAboutBox, StructCache structCache) { + IntPtr pTypeAttr = IntPtr.Zero; + int hr = typeInfo.GetTypeAttr(ref pTypeAttr); + + if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) { + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); + } + + NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); + UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); + if (typeAttr == null) { + return; + } + NativeMethods.tagFUNCDESC funcDesc = null; + NativeMethods.tagELEMDESC ed = null; + try { + + funcDesc = (NativeMethods.tagFUNCDESC)structCache.GetStruct(typeof(NativeMethods.tagFUNCDESC)); + ed = (NativeMethods.tagELEMDESC)structCache.GetStruct(typeof(NativeMethods.tagELEMDESC)); + + bool isPropGet; + PropInfo pi; + + for (int i = 0; i < typeAttr.cFuncs; i++) { + IntPtr pFuncDesc = IntPtr.Zero; + hr = typeInfo.GetFuncDesc(i, ref pFuncDesc); + + if (!NativeMethods.Succeeded(hr) || pFuncDesc == IntPtr.Zero) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring function item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", i, hr)); + continue; + } + + //funcDesc = (tagFUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(tagFUNCDESC)); + UnsafeNativeMethods.PtrToStructure(pFuncDesc, funcDesc); + try { + if (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_FUNC || + (dispidToGet != NativeMethods.MEMBERID_NIL && funcDesc.memid != dispidToGet)) { + + if (funcDesc.memid == NativeMethods.ActiveX.DISPID_ABOUTBOX) { + addAboutBox = true; + } + continue; + } + + NativeMethods.tagTYPEDESC typeDesc; + + // is this a get or a put? + isPropGet = (funcDesc.invkind == (int)NativeMethods.tagINVOKEKIND.INVOKE_PROPERTYGET); + + if (isPropGet) { + + if (funcDesc.cParams != 0) { + + continue; + } + + typeDesc = funcDesc.elemdescFunc.tdesc; + } + else { + Debug.Assert(funcDesc.lprgelemdescParam != IntPtr.Zero, "ELEMDESC param is null!"); + if (funcDesc.lprgelemdescParam == IntPtr.Zero || funcDesc.cParams != 1) { + + continue; + } + Marshal.PtrToStructure(funcDesc.lprgelemdescParam, ed); + typeDesc = ed.tdesc; + } + pi = ProcessDataCore(typeInfo, propInfoList, funcDesc.memid, nameDispID, typeDesc, funcDesc.wFuncFlags, structCache); + + // if we got a setmethod, it's not readonly + if (pi != null && !isPropGet) { + pi.ReadOnly = PropInfo.ReadOnlyFalse; + } + } + finally { + typeInfo.ReleaseFuncDesc(pFuncDesc); + } + } + } + finally { + if (funcDesc != null) { + structCache.ReleaseStruct(funcDesc); + } + if (ed != null) { + structCache.ReleaseStruct(ed); + } + typeInfo.ReleaseTypeAttr(pTypeAttr); + structCache.ReleaseStruct(typeAttr); + } + } + + /// + /// + /// This converts a type info that describes a IDL defined enum + /// into one we can use + /// + private static Type ProcessTypeInfoEnum(UnsafeNativeMethods.ITypeInfo enumTypeInfo, StructCache structCache) { + + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum entered"); + + if (enumTypeInfo == null) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got a NULL enumTypeInfo"); + return null; + } + + try { + IntPtr pTypeAttr = IntPtr.Zero; + int hr = enumTypeInfo.GetTypeAttr(ref pTypeAttr); + + if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) { + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); + } + + NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); + UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); + + if (pTypeAttr == IntPtr.Zero) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: failed to get a typeAttr"); + return null; + } + + try { + + int nItems = typeAttr.cVars; + + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: processing " + nItems.ToString(CultureInfo.InvariantCulture) + " variables"); + + ArrayList strs = new ArrayList(); + ArrayList vars = new ArrayList(); + + NativeMethods.tagVARDESC varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC)); + Object varValue = null; + string enumName = null; + string name = null; + string helpstr = null; + + enumTypeInfo.GetDocumentation(NativeMethods.MEMBERID_NIL, ref enumName, ref helpstr, null, null); + + // For each item in the enum type info, + // we just need it's name and value, and helpstring if it's there. + // + for (int i = 0; i < nItems; i++) { + IntPtr pVarDesc = IntPtr.Zero; + hr = enumTypeInfo.GetVarDesc(i, ref pVarDesc); + + if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetVarDesc returned hr=0x{1:X} or NULL", hr)); + continue; + } + + try { + //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC)); + UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc); + + if (varDesc == null || + varDesc.varkind != (int)NativeMethods.tagVARKIND.VAR_CONST || + varDesc.unionMember == IntPtr.Zero) { + continue; + } + + name = helpstr = null; + varValue = null; + + // get the name and the helpstring + + hr = enumTypeInfo.GetDocumentation(varDesc.memid, ref name, ref helpstr, null, null); + + + if (!NativeMethods.Succeeded(hr)) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring item 0x{0:X} because ITypeInfo::GetDocumentation returned hr=0x{1:X} or NULL", hr)); + continue; + } + + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum got name=" + (name == null ? "(null)" : name) + ", helpstring=" + (helpstr == null ? "(null)" : helpstr)); + + // get the value + try { + //varValue = (VARIANT)Marshal.PtrToStructure(varDesc.unionMember, typeof(VARIANT)); + varValue = Marshal.GetObjectForNativeVariant(varDesc.unionMember); + } + catch (Exception ex) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: PtrtoStructFailed " + ex.GetType().Name + "," + ex.Message); + } + + /*if (varValue == null) { + Debug.Fail("Couldn't get VARIANT from VARIANTDESC"); + continue; + }*/ + + //variant v = varValue.ToVariant(); + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding variable value=" + Convert.ToString(varValue, CultureInfo.InvariantCulture)); + vars.Add(varValue); + + // if we have a helpstring, use it, otherwise use name + string nameString; + if (helpstr != null) { + nameString = helpstr; + } + else { + Debug.Assert(name != null, "No name for VARDESC member, but GetDocumentation returned S_OK!"); + nameString = name; + } + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: adding name value=" + nameString); + strs.Add(nameString); + } + finally { + if (pVarDesc != IntPtr.Zero) { + enumTypeInfo.ReleaseVarDesc(pVarDesc); + } + } + } + structCache.ReleaseStruct(varDesc); + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, "ProcessTypeInfoEnum: returning enum with " + strs.Count.ToString(CultureInfo.InvariantCulture) + " items"); + + // just build our enumerator + if (strs.Count > 0) { + + // get the IUnknown value of the ITypeInfo + IntPtr pTypeInfoUnk = Marshal.GetIUnknownForObject(enumTypeInfo); + + try { + enumName = pTypeInfoUnk.ToString() + "_" + enumName; + + if (builtEnums == null) { + builtEnums = new Hashtable(); + } + else if (builtEnums.ContainsKey(enumName)) { + return (Type)builtEnums[enumName]; + } + + Type enumType = typeof(int); + + if (vars.Count > 0 && vars[0] != null) { + enumType = vars[0].GetType(); + } + + EnumBuilder enumBuilder = ModuleBuilder.DefineEnum(enumName, TypeAttributes.Public, enumType); + for (int i = 0; i < strs.Count; i++) { + enumBuilder.DefineLiteral((string)strs[i], vars[i]); + } + Type t = enumBuilder.CreateType(); + builtEnums[enumName] = t; + return t; + } + finally { + if (pTypeInfoUnk != IntPtr.Zero) { + Marshal.Release(pTypeInfoUnk); + } + } + } + + } + finally { + enumTypeInfo.ReleaseTypeAttr(pTypeAttr); + structCache.ReleaseStruct(typeAttr); + } + } + catch { + } + return null; + } + + + private static void ProcessVariables(UnsafeNativeMethods.ITypeInfo typeInfo, IDictionary propInfoList, int dispidToGet, int nameDispID, StructCache structCache) { + IntPtr pTypeAttr = IntPtr.Zero; + int hr = typeInfo.GetTypeAttr(ref pTypeAttr); + + if (!NativeMethods.Succeeded(hr) || pTypeAttr == IntPtr.Zero) { + throw new ExternalException(SR.GetString(SR.TYPEINFOPROCESSORGetTypeAttrFailed, hr), hr); + } + + NativeMethods.tagTYPEATTR typeAttr = (NativeMethods.tagTYPEATTR)structCache.GetStruct(typeof(NativeMethods.tagTYPEATTR));//(tagTYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(tagTYPEATTR)); + UnsafeNativeMethods.PtrToStructure(pTypeAttr, typeAttr); + + try { + if (typeAttr == null) { + return; + } + NativeMethods.tagVARDESC varDesc = (NativeMethods.tagVARDESC)structCache.GetStruct(typeof(NativeMethods.tagVARDESC)); + + for (int i = 0; i < typeAttr.cVars; i++) { + IntPtr pVarDesc = IntPtr.Zero; + + hr = typeInfo.GetVarDesc(i, ref pVarDesc); + if (!NativeMethods.Succeeded(hr) || pVarDesc == IntPtr.Zero) { + Debug.WriteLineIf(DbgTypeInfoProcessorSwitch.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "ProcessTypeInfoEnum: ignoring variable item 0x{0:X} because ITypeInfo::GetFuncDesc returned hr=0x{1:X} or NULL", hr)); + continue; + } + + //varDesc = (tagVARDESC)Marshal.PtrToStructure(pVarDesc, typeof(tagVARDESC)); + UnsafeNativeMethods.PtrToStructure(pVarDesc, varDesc); + + try { + + if (varDesc.varkind == (int)NativeMethods.tagVARKIND.VAR_CONST || + (dispidToGet != NativeMethods.MEMBERID_NIL && varDesc.memid != dispidToGet)) { + continue; + } + + + PropInfo pi = ProcessDataCore(typeInfo, propInfoList, varDesc.memid, nameDispID, varDesc.elemdescVar.tdesc, varDesc.wVarFlags, structCache); + if (pi.ReadOnly != PropInfo.ReadOnlyTrue) { + pi.ReadOnly = PropInfo.ReadOnlyFalse; + } + } + finally { + if (pVarDesc != IntPtr.Zero) { + typeInfo.ReleaseVarDesc(pVarDesc); + } + } + } + structCache.ReleaseStruct(varDesc); + } + finally { + typeInfo.ReleaseTypeAttr(pTypeAttr); + structCache.ReleaseStruct(typeAttr); + } + } + + private static Type VTToType(NativeMethods.tagVT vt) { + switch (vt) { + case NativeMethods.tagVT.VT_EMPTY: + case NativeMethods.tagVT.VT_NULL: + return null; + case NativeMethods.tagVT.VT_I1: + return typeof(SByte); + case NativeMethods.tagVT.VT_UI1: + return typeof(Byte); + + case NativeMethods.tagVT.VT_I2: + return typeof(Int16); + case NativeMethods.tagVT.VT_UI2: + return typeof(UInt16); + + + case NativeMethods.tagVT.VT_I4: + case NativeMethods.tagVT.VT_INT: + return typeof(Int32); + + case NativeMethods.tagVT.VT_UI4: + case NativeMethods.tagVT.VT_UINT: + return typeof(UInt32); + + case NativeMethods.tagVT.VT_I8: + return typeof(Int64); + case NativeMethods.tagVT.VT_UI8: + return typeof(UInt64); + + case NativeMethods.tagVT.VT_R4: + return typeof(float); + + case NativeMethods.tagVT.VT_R8: + return typeof(double); + + case NativeMethods.tagVT.VT_CY: + return typeof(Decimal); + case NativeMethods.tagVT.VT_DATE: + return typeof(DateTime); + case NativeMethods.tagVT.VT_BSTR: + case NativeMethods.tagVT.VT_LPSTR: + case NativeMethods.tagVT.VT_LPWSTR: + return typeof(string); + + case NativeMethods.tagVT.VT_DISPATCH: + return typeof(UnsafeNativeMethods.IDispatch); + case NativeMethods.tagVT.VT_UNKNOWN: + return typeof(Object); + + case NativeMethods.tagVT.VT_ERROR: + case NativeMethods.tagVT.VT_HRESULT: + return typeof(int); + + case NativeMethods.tagVT.VT_BOOL: + return typeof(bool); + + case NativeMethods.tagVT.VT_VARIANT: + return typeof(Com2Variant); + case NativeMethods.tagVT.VT_CLSID: + return typeof(Guid); + + case NativeMethods.tagVT.VT_FILETIME: + return typeof(NativeMethods.FILETIME); + + case NativeMethods.tagVT.VT_USERDEFINED: + throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, "VT_USERDEFINED")); + + /*case VT_ENUM: + if (enumNames != null || null != pPropertyInfo.GetEnum()) { + return typeof(IEnum); + } + goto default;*/ + case NativeMethods.tagVT.VT_VOID: + case NativeMethods.tagVT.VT_PTR: + case NativeMethods.tagVT.VT_SAFEARRAY: + case NativeMethods.tagVT.VT_CARRAY: + + case NativeMethods.tagVT.VT_RECORD: + case NativeMethods.tagVT.VT_BLOB: + case NativeMethods.tagVT.VT_STREAM: + case NativeMethods.tagVT.VT_STORAGE: + case NativeMethods.tagVT.VT_STREAMED_OBJECT: + case NativeMethods.tagVT.VT_STORED_OBJECT: + case NativeMethods.tagVT.VT_BLOB_OBJECT: + case NativeMethods.tagVT.VT_CF: + case NativeMethods.tagVT.VT_BSTR_BLOB: + case NativeMethods.tagVT.VT_VECTOR: + case NativeMethods.tagVT.VT_ARRAY: + case NativeMethods.tagVT.VT_BYREF: + case NativeMethods.tagVT.VT_RESERVED: + default: + throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, ((int)vt).ToString(CultureInfo.InvariantCulture))); + } + } + + internal class CachedProperties { + + private PropertyDescriptor[] props; + + public readonly int MajorVersion; + public readonly int MinorVersion; + private int defaultIndex; + + internal CachedProperties(PropertyDescriptor[] props, int defIndex, int majVersion, int minVersion) { + this.props = ClonePropertyDescriptors(props); + this.MajorVersion = majVersion; + this.MinorVersion = minVersion; + this.defaultIndex = defIndex; + } + + public PropertyDescriptor[] Properties { + get { + return ClonePropertyDescriptors(props); + } + } + + public int DefaultIndex { + get { + return defaultIndex; + } + } + + private PropertyDescriptor[] ClonePropertyDescriptors(PropertyDescriptor[] props) { + PropertyDescriptor[] retProps = new PropertyDescriptor[props.Length]; + for (int i = 0; i < props.Length; i++) { + if (props[i] is ICloneable) { + retProps[i] = (PropertyDescriptor)((ICloneable)props[i]).Clone();; + } + else { + retProps[i] = props[i]; + } + } + return retProps; + } + } + + /// + /// + /// This class manages a cache of structures that we can use + /// for passing into native so we don't have to create them every time. + /// for many objects, these can be used thousands of times. + /// + public class StructCache { + + private Hashtable queuedTypes = new Hashtable(); + +#if DEBUG + private Hashtable releaseCheck = new Hashtable(); + + ~StructCache() { + IEnumerator enumRelease = releaseCheck.Keys.GetEnumerator(); + + while (enumRelease.MoveNext()) { + Type t = (Type)enumRelease.Current; + if ((int)releaseCheck[t] != 0) { + Debug.Assert(false, "Failed to release struct of type " + t.Name); + } + } + } + +#endif + + private Queue GetQueue(Type t, bool create) { + Object queue = queuedTypes[t]; + + if (queue == null && create){ + queue = new Queue(); + queuedTypes[t] = queue; + #if DEBUG + releaseCheck[t] = 0; + #endif + } + + return (Queue)queue; + } + + public Object GetStruct(Type t) { + Queue queue = GetQueue(t, true); + + Object str = null; + + if (queue.Count == 0) { + str = Activator.CreateInstance(t); + } + else { + str = queue.Dequeue(); + } + + #if DEBUG + int count = (int)releaseCheck[t]; + releaseCheck[t] = ++count; + #endif + + return str; + } + + public void ReleaseStruct(Object str) { + Type t = str.GetType(); + Queue queue = GetQueue(t, false); + + if (queue != null) { + queue.Enqueue(str); + + #if DEBUG + int count = (int)releaseCheck[t]; + releaseCheck[t] = --count; + #endif + } + } + + } + + private class PropInfo { + + public const int ReadOnlyUnknown = 0; + public const int ReadOnlyTrue = 1; + public const int ReadOnlyFalse = 2; + + string name = null; + int dispid = -1; + Type valueType = null; + readonly ArrayList attributes = new ArrayList(); + int readOnly = ReadOnlyUnknown; + bool isDefault; + Object typeData; + bool nonbrowsable = false; + int index; + + public string Name { + get { return name; } + set { name = value; } + } + public int DispId { + get { return dispid; } + set { dispid = value; } + } + public Type ValueType { + get { return valueType; } + set { valueType = value; } + } + public ArrayList Attributes { + get { return attributes; } + } + public int ReadOnly { + get { return readOnly; } + set { readOnly = value; } + } + public bool IsDefault { + get { return isDefault; } + set { isDefault = value; } + } + public object TypeData { + get { return typeData; } + set { typeData = value; } + } + public bool NonBrowsable { + get { return nonbrowsable; } + set { nonbrowsable = value; } + } + public int Index{ + get {return index;} + set {index = value;} + } + + + public override int GetHashCode() { + if (name != null) { + return name.GetHashCode(); + } + return base.GetHashCode(); + } + } + } + + + // just so we can recognize a variant properly... + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class Com2Variant { + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ComNativeDescriptor.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ComNativeDescriptor.cs new file mode 100644 index 000000000..adb5525d7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ComNativeDescriptor.cs @@ -0,0 +1,573 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel.Design; + using Microsoft.Win32; + + /// + /// + /// Top level mapping layer between COM Object and TypeDescriptor. + /// + /// + internal class ComNativeDescriptor : TypeDescriptionProvider { + + private static ComNativeDescriptor handler = null; + + private AttributeCollection staticAttrs = new AttributeCollection(new Attribute[]{BrowsableAttribute.Yes, DesignTimeVisibleAttribute.No}); + + /// + /// + /// Our collection of Object managers (Com2Properties) for native properties + /// + private WeakHashtable nativeProps = new WeakHashtable(); + + /// + /// + /// Our collection of browsing handlers, which are stateless and shared across objects. + /// + private Hashtable extendedBrowsingHandlers = new Hashtable(); + + /// + /// + /// We increment this every time we look at an Object, at specified + /// intervals, we run through the properies list to see if we should + /// delete any. + /// + private int clearCount = 0; + private const int CLEAR_INTERVAL = 25; + + internal static ComNativeDescriptor Instance { + get { + if (handler == null) { + handler = new ComNativeDescriptor(); + } + return handler; + } + } + + + [ + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode") + ] + // called via reflection for AutomationExtender stuff. Don't delete! + // + public static object GetNativePropertyValue(object component, string propertyName, ref bool succeeded) + { + return Instance.GetPropertyValue(component, propertyName, ref succeeded); + } + + + /// + /// This method returns a custom type descriptor for the given type / object. + /// The objectType parameter is always valid, but the instance parameter may + /// be null if no instance was passed to TypeDescriptor. The method should + /// return a custom type descriptor for the object. If the method is not + /// interested in providing type information for the object it should + /// return null. + /// + /// This method is prototyped as virtual, and by default returns null + /// if no parent provider was passed. If a parent provider was passed, + /// this method will invoke the parent provider's GetTypeDescriptor + /// method. + /// + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + return new ComTypeDescriptor(this, instance); + } + + internal string GetClassName(Object component) { + + string name = null; + + // does IVsPerPropretyBrowsing supply us a name? + if (component is NativeMethods.IVsPerPropertyBrowsing) { + int hr = ((NativeMethods.IVsPerPropertyBrowsing)component).GetClassName(ref name); + if (NativeMethods.Succeeded(hr) && name != null) { + return name; + } + // otherwise fall through... + } + + UnsafeNativeMethods.ITypeInfo pTypeInfo = Com2TypeInfoProcessor.FindTypeInfo(component, true); + + if (pTypeInfo == null) { + //Debug.Fail("The current component failed to return an ITypeInfo"); + return ""; + } + + if (pTypeInfo != null) { + string desc = null; + try { + pTypeInfo.GetDocumentation(NativeMethods.MEMBERID_NIL, ref name, ref desc, null, null); + + // strip the leading underscores + while (name != null && name.Length > 0 && name[0] == '_') { + name = name.Substring(1); + } + return name; + } + catch { + } + } + return ""; + } + + internal TypeConverter GetConverter(Object component) { + return TypeDescriptor.GetConverter(typeof(IComponent)); + } + + internal Object GetEditor(Object component, Type baseEditorType) { + return TypeDescriptor.GetEditor(component.GetType(), baseEditorType); + } + + internal string GetName(Object component) { + + if (!(component is UnsafeNativeMethods.IDispatch)) { + return ""; + } + + int dispid = Com2TypeInfoProcessor.GetNameDispId((UnsafeNativeMethods.IDispatch)component); + if (dispid != NativeMethods.MEMBERID_NIL) { + bool success = false; + object value = GetPropertyValue(component, dispid, ref success); + + if (success && value != null) { + return value.ToString(); + } + } + return ""; + } + + internal Object GetPropertyValue(Object component, string propertyName, ref bool succeeded) { + + if (!(component is UnsafeNativeMethods.IDispatch)) { + return null; + } + + UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)component; + string[] names = new string[]{propertyName}; + int[] dispid = new int[1]; + dispid[0] = NativeMethods.DISPID_UNKNOWN; + Guid g = Guid.Empty; + try { + int hr = iDispatch.GetIDsOfNames(ref g, names, 1, SafeNativeMethods.GetThreadLCID(), dispid); + + if (dispid[0] == NativeMethods.DISPID_UNKNOWN || NativeMethods.Failed(hr)) { + return null; + } + } + catch { + return null; + } + return GetPropertyValue(component, dispid[0], ref succeeded); + } + + internal Object GetPropertyValue(Object component, int dispid, ref bool succeeded) { + if (!(component is UnsafeNativeMethods.IDispatch)) { + return null; + } + Object[] pVarResult = new Object[1]; + if (GetPropertyValue(component, dispid, pVarResult) == NativeMethods.S_OK) { + succeeded = true; + return pVarResult[0]; + } + else { + succeeded = false; + return null; + } + } + + internal int GetPropertyValue(Object component, int dispid, Object[] retval) { + if (!(component is UnsafeNativeMethods.IDispatch)) { + return NativeMethods.E_NOINTERFACE; + } + UnsafeNativeMethods.IDispatch iDispatch = (UnsafeNativeMethods.IDispatch)component; + try { + Guid g = Guid.Empty; + NativeMethods.tagEXCEPINFO pExcepInfo = new NativeMethods.tagEXCEPINFO(); + int hr; + + try{ + + hr = iDispatch.Invoke(dispid, + ref g, + SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_PROPERTYGET, + new NativeMethods.tagDISPPARAMS(), + retval, + pExcepInfo, null); + + /*if (hr != NativeMethods.S_OK){ + Com2PropertyDescriptor.PrintExceptionInfo(pExcepInfo); + + } */ + if (hr == NativeMethods.DISP_E_EXCEPTION) { + hr = pExcepInfo.scode; + } + + } + catch (ExternalException ex){ + hr = ex.ErrorCode; + } + return hr; + } + catch { + //Debug.Fail(e.ToString() + " " + component.GetType().GUID.ToString() + " " + component.ToString()); + } + return NativeMethods.E_FAIL; + } + + /// + /// + /// Checks if the given dispid matches the dispid that the Object would like to specify + /// as its identification proeprty (Name, ID, etc). + /// + internal bool IsNameDispId(Object obj, int dispid) { + if (obj == null || !obj.GetType().IsCOMObject) { + return false; + } + return dispid == Com2TypeInfoProcessor.GetNameDispId((UnsafeNativeMethods.IDispatch)obj); + } + + /// + /// + /// Checks all our property manages to see if any have become invalid. + /// + private void CheckClear(Object component) { + + // walk the list every so many calls + if ((++clearCount % CLEAR_INTERVAL) == 0) { + + lock(nativeProps) { + clearCount = 0; + + List disposeList = null; + Com2Properties entry; + + // first walk the list looking for items that need to be + // cleaned out. + // + foreach(DictionaryEntry de in nativeProps) { + entry = de.Value as Com2Properties; + + if (entry != null && entry.TooOld) { + if (disposeList == null) { + disposeList = new List(3); + } + disposeList.Add(de.Key); + } + } + + // now run through the ones that are dead and dispose them. + // there's going to be a very small number of these. + // + if (disposeList != null) { + object oldKey; + for (int i = disposeList.Count - 1; i >= 0; i--) { + oldKey = disposeList[i]; + entry = nativeProps[oldKey] as Com2Properties; + + if (entry != null) { + entry.Disposed -= new EventHandler(OnPropsInfoDisposed); + entry.Dispose(); + nativeProps.Remove(oldKey); + } + } + } + } + } + } + + /// + /// + /// Gets the properties manager for an Object. + /// + private Com2Properties GetPropsInfo(Object component) { + // check caches if necessary + // + CheckClear(component); + + // Get the property info Object + // + Com2Properties propsInfo = (Com2Properties)nativeProps[component]; + + // if we dont' have one, create one and set it up + // + if (propsInfo == null || !propsInfo.CheckValid()) { + propsInfo = Com2TypeInfoProcessor.GetProperties(component); + if (propsInfo != null) { + propsInfo.Disposed += new EventHandler(OnPropsInfoDisposed); + nativeProps.SetWeak(component, propsInfo); + propsInfo.AddExtendedBrowsingHandlers(extendedBrowsingHandlers); + } + } + return propsInfo; + } + + /// + /// + /// Got attributes? + /// + internal AttributeCollection GetAttributes(Object component) { + + ArrayList attrs = new ArrayList(); + + if (component is NativeMethods.IManagedPerPropertyBrowsing) { + Object[] temp = Com2IManagedPerPropertyBrowsingHandler.GetComponentAttributes((NativeMethods.IManagedPerPropertyBrowsing)component, NativeMethods.MEMBERID_NIL); + for (int i = 0; i < temp.Length; ++i) { + attrs.Add(temp[i]); + } + } + + if (Com2ComponentEditor.NeedsComponentEditor(component)) { + EditorAttribute a = new EditorAttribute(typeof(Com2ComponentEditor), typeof(ComponentEditor)); + attrs.Add(a); + } + + if (attrs == null || attrs.Count == 0) { + return staticAttrs; + } + else { + Attribute[] temp = new Attribute[attrs.Count]; + attrs.CopyTo(temp, 0); + return new AttributeCollection(temp); + } + } + + /// + /// + /// Default Property, please + /// + internal PropertyDescriptor GetDefaultProperty(Object component) { + CheckClear(component); + + Com2Properties propsInfo = GetPropsInfo(component); + if (propsInfo != null) { + return propsInfo.DefaultProperty; + } + return null; + } + + internal EventDescriptorCollection GetEvents(Object component) { + return new EventDescriptorCollection(null); + } + + internal EventDescriptorCollection GetEvents(Object component, Attribute[] attributes) { + return new EventDescriptorCollection(null); + } + + internal EventDescriptor GetDefaultEvent(Object component) { + return null; + } + + /// + /// Props! + /// + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + internal PropertyDescriptorCollection GetProperties(Object component, Attribute[] attributes) { + + Com2Properties propsInfo = GetPropsInfo(component); + + if (propsInfo == null) { + return PropertyDescriptorCollection.Empty; + } + + try { + propsInfo.AlwaysValid = true; + PropertyDescriptor[] props = propsInfo.Properties; + + //Debug.Assert(propDescList.Count > 0, "Didn't add any properties! (propInfos=0)"); + return new PropertyDescriptorCollection(props); + } + finally { + propsInfo.AlwaysValid = false; + } + } + + /// + /// Fired when the property info gets disposed. + /// + private void OnPropsInfoDisposed(object sender, EventArgs e) { + Com2Properties propsInfo = sender as Com2Properties; + + if (propsInfo != null) { + propsInfo.Disposed -= new EventHandler(OnPropsInfoDisposed); + + lock(nativeProps) { + + // find the key + object key = propsInfo.TargetObject; + + if (key == null && nativeProps.ContainsValue(propsInfo)) { + // need to find it - the target object has probably been cleaned out + // of the Com2Properties object already, so we run through the + // hashtable looking for the value, so we know what key to remove. + // + foreach (DictionaryEntry de in nativeProps) { + if (de.Value == propsInfo) { + key = de.Key; + break; + } + } + + if (key == null) { + Debug.Fail("Failed to find Com2 properties key on dispose."); + return; + } + } + + nativeProps.Remove(key); + } + } + } + + /// + /// + /// Looks at at value's type and creates an editor based on that. We use this to decide which editor to use + /// for a generic variant. + /// + internal static void ResolveVariantTypeConverterAndTypeEditor(Object propertyValue, ref TypeConverter currentConverter, Type editorType, ref Object currentEditor) { + + Object curValue = propertyValue; + if (curValue != null && curValue != null && !Convert.IsDBNull(curValue)){ + Type t = curValue.GetType(); + TypeConverter subConverter = TypeDescriptor.GetConverter(t); + if (subConverter != null && subConverter.GetType() != typeof(TypeConverter)){ + currentConverter = subConverter; + } + Object subEditor = TypeDescriptor.GetEditor(t, editorType); + if (subEditor != null) { + currentEditor = subEditor; + } + } + } + + /// + /// This type descriptor sits on top of a ComNativeDescriptor + /// + private sealed class ComTypeDescriptor : ICustomTypeDescriptor + { + private ComNativeDescriptor _handler; + private object _instance; + + /// + /// Creates a new WalkingTypeDescriptor. + /// + internal ComTypeDescriptor(ComNativeDescriptor handler, object instance) + { + _handler = handler; + _instance = instance; + } + + /// + /// ICustomTypeDescriptor implementation. + /// + AttributeCollection ICustomTypeDescriptor.GetAttributes() + { + return _handler.GetAttributes(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + string ICustomTypeDescriptor.GetClassName() + { + return _handler.GetClassName(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + string ICustomTypeDescriptor.GetComponentName() + { + return _handler.GetName(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + TypeConverter ICustomTypeDescriptor.GetConverter() + { + return _handler.GetConverter(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + { + return _handler.GetDefaultEvent(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + { + return _handler.GetDefaultProperty(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + { + return _handler.GetEditor(_instance, editorBaseType); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() + { + return _handler.GetEvents(_instance); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + { + return _handler.GetEvents(_instance, attributes); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() + { + return _handler.GetProperties(_instance, null); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + { + return _handler.GetProperties(_instance, attributes); + } + + /// + /// ICustomTypeDescriptor implementation. + /// + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + { + return _instance; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ICOM2PropertyPageDisplayService.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ICOM2PropertyPageDisplayService.cs new file mode 100644 index 000000000..cac18e731 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/ICOM2PropertyPageDisplayService.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using Microsoft.Win32; + + /// + /// + /// [To be supplied.] + /// + public interface ICom2PropertyPageDisplayService { + /// + /// + /// [To be supplied.] + /// + void ShowPropertyPage(string title, object component, int dispid, Guid pageGuid, IntPtr parentHandle); + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/IComPropertyBrowser.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/IComPropertyBrowser.cs new file mode 100644 index 000000000..29a13215b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/IComPropertyBrowser.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using System.ComponentModel.Design; + using Microsoft.Win32; + + /// + /// + public interface IComPropertyBrowser { + + /// + /// + /// [To be supplied.] + /// + void DropDownDone(); + + /// + /// + /// [To be supplied.] + /// + + bool InPropertySet{get;} + /// + /// + /// [To be supplied.] + /// + + event ComponentRenameEventHandler ComComponentNameChanged; + /// + /// + /// [To be supplied.] + /// + bool EnsurePendingChangesCommitted(); + /// + /// + /// [To be supplied.] + /// + void HandleF4(); + /// + /// + /// [To be supplied.] + /// + void LoadState(RegistryKey key); + /// + /// + /// [To be supplied.] + /// + void SaveState(RegistryKey key); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/Int32CAMarshaler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/Int32CAMarshaler.cs new file mode 100644 index 000000000..fcaa98392 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/Int32CAMarshaler.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + + + /// + /// + /// This class performs marshaling on a CADWORD struct given + /// from native code. + /// + internal class Int32CAMarshaler : BaseCAMarshaler { + public Int32CAMarshaler(NativeMethods.CA_STRUCT caStruct) : base(caStruct) { + } + + + /// + /// + /// Returns the type of item this marshaler will + /// return in the items array. In this case, the type is int. + /// + public override Type ItemType { + get { + return typeof(int); + } + } + + protected override Array CreateArray() { + return new int[Count]; + } + + /// + /// + /// Override this member to perform marshalling of a single item + /// given it's native address. + /// + protected override object GetItemFromAddress(IntPtr addr) { + return addr.ToInt32(); + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/OleStrCAMarshaler.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/OleStrCAMarshaler.cs new file mode 100644 index 000000000..a4e132977 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/OleStrCAMarshaler.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + + + /// + /// + /// This class performs marshaling on a CALPOLESTR struct given + /// from native code. + /// + internal class OleStrCAMarshaler: BaseCAMarshaler { + public OleStrCAMarshaler(NativeMethods.CA_STRUCT caAddr) : base(caAddr) { + } + + /// + /// + /// Returns the type of item this marshaler will + /// return in the items array. In this case, the type is string. + /// + public override Type ItemType { + get { + return typeof(string); + } + } + + protected override Array CreateArray() { + return new string[Count]; + } + + /// + /// + /// Override this member to perform marshalling of a single item + /// given it's native address. + /// + protected override object GetItemFromAddress(IntPtr addr) { + string item = Marshal.PtrToStringUni(addr); + // free the memory + Marshal.FreeCoTaskMem(addr); + return item; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/_CTLBLDTYPE.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/_CTLBLDTYPE.cs new file mode 100644 index 000000000..6a1b9ccf2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/COM2Interop/_CTLBLDTYPE.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.ComponentModel.Com2Interop { + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + + [CLSCompliant(false)] + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class _CTLBLDTYPE { + public const int CTLBLDTYPE_FSTDPROPBUILDER = 0x00000001; + public const int CTLBLDTYPE_FINTERNALBUILDER = 0x00000002; + public const int CTLBLDTYPE_FEDITSOBJDIRECTLY = 0x00000004; + } +} diff --git a/WindowsForms/Managed/System/WinForms/ComponentModel/IComponentEditorPageSite.cs b/WindowsForms/Managed/System/WinForms/ComponentModel/IComponentEditorPageSite.cs new file mode 100644 index 000000000..53d8178a6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ComponentModel/IComponentEditorPageSite.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using System.ComponentModel; + + using System.Diagnostics; + + using System; + using System.Windows.Forms; + using Microsoft.Win32; + + /// + /// + /// + /// The site for a ComponentEditorPage. + /// + public interface IComponentEditorPageSite { + + /// + /// + /// Returns the parent control for the page window. + /// + Control GetControl(); + + /// + /// + /// Notifies the site that the editor is in dirty state. + /// + void SetDirty(); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ContainerControl.cs b/WindowsForms/Managed/System/WinForms/ContainerControl.cs new file mode 100644 index 000000000..e2a6e3bb1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ContainerControl.cs @@ -0,0 +1,1951 @@ +//---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Drawing; + using System.ComponentModel.Design; + using System.Windows.Forms.Layout; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// Defines a + /// base class for + /// controls that can parent other controls. + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + public class ContainerControl : ScrollableControl, IContainerControl { + private Control activeControl; // current active control + private Control focusedControl; // Current focused control. Do not directly edit this value. + private Control unvalidatedControl; // The last control that requires validation. Do not directly edit this value. + private AutoValidate autoValidate = AutoValidate.Inherit; // Indicates whether automatic validation is turned on. + private EventHandler autoValidateChanged; // Event fired whenever the AutoValidate property changes. + + // Auto scaling property values + private SizeF autoScaleDimensions = SizeF.Empty; + private SizeF currentAutoScaleDimensions = SizeF.Empty; + private AutoScaleMode autoScaleMode = AutoScaleMode.Inherit; + private BitVector32 state = new BitVector32(); + + private static readonly int stateScalingNeededOnLayout = BitVector32.CreateMask(); // True if we need to perform scaling when layout resumes + private static readonly int stateValidating = BitVector32.CreateMask(stateScalingNeededOnLayout); // Indicates whether we're currently state[stateValidating]. + private static readonly int stateProcessingMnemonic = BitVector32.CreateMask(stateValidating); // Indicates whether we or one of our children is currently processing a mnemonic. + private static readonly int stateScalingChild = BitVector32.CreateMask(stateProcessingMnemonic); // True while we are scaling a child control + private static readonly int stateParentChanged = BitVector32.CreateMask(stateScalingChild); // Flagged when a parent changes so we can adpat our scaling logic to match + + private static readonly int PropAxContainer = PropertyStore.CreateKey(); + private const string fontMeasureString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /// + /// + /// Initializes a new instance of the + /// class. + /// + public ContainerControl() : base() { + SetStyle(ControlStyles.AllPaintingInWmPaint, false); + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + } + + /// + /// + /// AutoScaleDimensions represents the DPI or Font setting that the control has been scaled + /// to or designed at. Specifically, at design time this property will be set by the + /// designer to the value that the developer is designing at. Then, at runtime, when the + /// form loads if the CurrentAutoScaleDimensions are different from the AutoScaleDimensions, + /// PerformAutoScale will be called and AutoScaleDimensions will be set to the new value to + /// match the CurrentAutoScaleDimensions by PerformAutoScale. + /// + [Localizable(true)] + [Browsable(false)] + [SRCategory(SR.CatLayout)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public SizeF AutoScaleDimensions { + get { + return autoScaleDimensions; + } + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // value is the name of the param passed in. + // So we don't have to localize it. + ] + set { + if (value.Width < 0 || value.Height < 0) { + throw new ArgumentOutOfRangeException(SR.GetString(SR.ContainerControlInvalidAutoScaleDimensions), "value"); + } + autoScaleDimensions = value; + if (!autoScaleDimensions.IsEmpty) { + LayoutScalingNeeded(); + } + } + } + + /// + /// + /// AutoScaleFactor represents the scaling factor difference between + /// CurrentAutoScaleDimensions and AutoScaleDimensions. This value is + /// calculated on the fly. Eg: If CurrentAutoScaleDimensions is 192, 192 + /// and AutoScaleDimensions is 96, 96 then the AutoScaleFactor is 2.0, 2.0 + /// + protected SizeF AutoScaleFactor { + get { + SizeF current = CurrentAutoScaleDimensions; + SizeF saved = AutoScaleDimensions; + + // If no one has configured auto scale dimensions yet, the scaling factor + // is unity. + if (saved.IsEmpty) { + return new SizeF(1.0F, 1.0F); + } + + return new SizeF(current.Width / saved.Width, current.Height / saved.Height); + } + } + + /// + /// + /// Determines the scaling mode of this control. The default is no scaling. + /// + /// Scaling by Font is useful if you wish to have a control + /// or form stretch or shrink according to the size of the fonts in the system, and should + /// be used when the control or form's size itself does not matter. + /// + /// Scaling by DPI is useful when you wish to keep a control or form a specific size + /// independent of font. for example, a control displaying a chart or other graphic + /// may want to use DPI scaling to increase in size to account for higher DPI monitors. + /// + [SRCategory(SR.CatLayout)] + [SRDescription(SR.ContainerControlAutoScaleModeDescr)] + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public AutoScaleMode AutoScaleMode { + get { + return autoScaleMode; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoScaleMode.None, (int)AutoScaleMode.Inherit)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoScaleMode)); + } + + bool scalingNeeded = false; + + if (value != autoScaleMode) { + + // Invalidate any current scaling factors. If we + // are changing AutoScaleMode to anything other than + // its default, we should clear out autoScaleDimensions as it is + // nonsensical. + if (autoScaleMode != AutoScaleMode.Inherit) { + autoScaleDimensions = SizeF.Empty; + } + + currentAutoScaleDimensions = SizeF.Empty; + autoScaleMode = value; + scalingNeeded = true; + } + + OnAutoScaleModeChanged(); + + if (scalingNeeded) { + LayoutScalingNeeded(); + } + } + } + + /// + /// + /// Indicates whether controls in this container will be automatically validated when the focus changes. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + AmbientValue(AutoValidate.Inherit), + SRCategory(SR.CatBehavior), + SRDescription(SR.ContainerControlAutoValidate), + ] + public virtual AutoValidate AutoValidate { + get { + if (autoValidate == AutoValidate.Inherit) { + return GetAutoValidateForControl(this); + } + else { + return autoValidate; + } + } + set { + // PERF/FXCop: dont use Enum.IsDefined. + switch (value) { + case AutoValidate.Disable: + case AutoValidate.EnablePreventFocusChange: + case AutoValidate.EnableAllowFocusChange: + case AutoValidate.Inherit: + break; + default: + throw new InvalidEnumArgumentException("AutoValidate", (int) value, typeof(AutoValidate)); + } + + if (autoValidate != value) { + autoValidate = value; + OnAutoValidateChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ContainerControlOnAutoValidateChangedDescr), + ] + public event EventHandler AutoValidateChanged { + add { + this.autoValidateChanged += value; + } + remove { + this.autoValidateChanged -= value; + } + } + + /// + /// + /// + /// + /// The binding manager for the container control. + /// + /// + [ + Browsable(false), + SRDescription(SR.ContainerControlBindingContextDescr) + ] + public override BindingContext BindingContext { + get { + BindingContext bm = base.BindingContext; + if (bm == null) { + bm = new BindingContext(); + BindingContext = bm; + } + return bm; + } + set { + base.BindingContext = value; + } + } + + /// + /// Container controls support ImeMode only to allow child controls to inherit it from their parents. + /// + protected override bool CanEnableIme { + get { + // Note: If overriding this property make sure to copy the Debug code and call this method. + + Debug.Indent(); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CanEnableIme(), value = false" + ", this = " + this ); + Debug.Unindent(); + + return false; + } + } + + /// + /// + /// Indicates the current active control on the container control. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ContainerControlActiveControlDescr) + ] + public Control ActiveControl { + get { + return activeControl; + } + + set { + SetActiveControl(value); + } + } + + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + return cp; + } + } + + /// + /// + /// CurrentAutoScaleDimensions represent the actual DPI or Font settings + /// of the display at runtime. If the AutoScaleMode is set to ‘None’ then + /// the CurrentAutoScaleDimensions is equal to the ActualScaleDimensions + /// + [Browsable(false)] + [SRCategory(SR.CatLayout)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public SizeF CurrentAutoScaleDimensions { + get { + if (currentAutoScaleDimensions.IsEmpty) { + switch (AutoScaleMode) { + case AutoScaleMode.Font: + currentAutoScaleDimensions = GetFontAutoScaleDimensions(); + break; + + case AutoScaleMode.Dpi: + // Screen Dpi + if (DpiHelper.EnableDpiChangedMessageHandling) { + currentAutoScaleDimensions = new SizeF((float)deviceDpi, (float)deviceDpi); + } + else { + // this DPI value comes from the primary monitor. + currentAutoScaleDimensions = WindowsGraphicsCacheManager.MeasurementGraphics.DeviceContext.Dpi; + } + break; + + default: + currentAutoScaleDimensions = AutoScaleDimensions; + break; + } + } + + return currentAutoScaleDimensions; + } + } + + /// + /// + /// Indicates the form that the scrollable control is assigned to. This property is read-only. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ContainerControlParentFormDescr) + ] + public Form ParentForm { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + return ParentFormInternal; + } + } + + internal Form ParentFormInternal { + get { + if (ParentInternal != null) { + return ParentInternal.FindFormInternal(); + } + else { + if (this is Form) { + return null; + } + + return FindFormInternal(); + } + } + } + + // Package scope for Control + /// + /// + /// + /// Activates the specified control. + /// + bool IContainerControl.ActivateControl(Control control) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + return ActivateControlInternal(control, true); + } + + internal bool ActivateControlInternal(Control control) { + return ActivateControlInternal(control, true); + } + + internal bool ActivateControlInternal(Control control, bool originator) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::ActivateControlInternal(" + (control == null ? "null" : control.Name) + "," + originator.ToString() + ") - " + this.Name); + // Recursive function that makes sure that the chain of active controls + // is coherent. + bool ret = true; + bool updateContainerActiveControl = false; + ContainerControl cc = null; + Control parent = this.ParentInternal; + if (parent != null) + { + cc = (parent.GetContainerControlInternal()) as ContainerControl; + if (cc != null) + { + updateContainerActiveControl = (cc.ActiveControl != this); + } + } + if (control != activeControl || updateContainerActiveControl) + { + if (updateContainerActiveControl) + { + if (!cc.ActivateControlInternal(this, false)) + { + return false; + } + } + ret = AssignActiveControlInternal((control == this) ? null : control); + } + + if (originator) { + ScrollActiveControlIntoView(); + } + return ret; + } + + /// + /// + /// Used for UserControls - checks if the control + /// has a focusable control inside or not + /// + internal bool HasFocusableChild() + { + Control ctl = null; + do { + ctl = GetNextControl(ctl, true); + if (ctl != null && + ctl.CanSelect && + ctl.TabStop) + { + break; + } + } while (ctl != null); + return ctl != null; + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void AdjustFormScrollbars(bool displayScrollbars) { + base.AdjustFormScrollbars(displayScrollbars); + + if (!GetScrollState(ScrollStateUserHasScrolled)) { + ScrollActiveControlIntoView(); + } + } + + /// + /// Cleans up form state after a control has been removed. + /// Package scope for Control + /// + /// + internal virtual void AfterControlRemoved(Control control, Control oldParent) { + ContainerControl cc; + Debug.Assert(control != null); + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::AfterControlRemoved(" + control.Name + ") - " + this.Name); + if (control == activeControl || control.Contains(activeControl)) { + bool selected; + // SECREVIEW : Note that a function overriding "protected virtual void Control::Select(bool directed, bool forward)" + // called by SelectNextControl will be able to set the focus to any control. + // This is also enabled by the ModifyFocus.Assert inside Control::SelectNextIfFocused. + IntSecurity.ModifyFocus.Assert (); + try + { + selected = SelectNextControl(control, true, true, true, true); + } + finally + { + CodeAccessPermission.RevertAssert (); + } + if (selected && this.activeControl != control) + { + // Bug 847648. + // Add the check. If it is set to true, do not call into FocusActiveControlInternal(). + // The TOP MDI window could be gone and CreateHandle method will fail + // because it try to create a parking window Parent for the MDI children + if (!this.activeControl.Parent.IsTopMdiWindowClosing) + { + FocusActiveControlInternal(); + } + } + else + { + SetActiveControlInternal(null); + } + } + else if (activeControl == null && ParentInternal != null) + { + // The last control of an active container was removed. Focus needs to be given to the next + // control in the Form. + cc = ParentInternal.GetContainerControlInternal() as ContainerControl; + if (cc != null && cc.ActiveControl == this) + { + Form f = FindFormInternal(); + if (f != null) + { + // SECREVIEW : Same comment as above. + IntSecurity.ModifyFocus.Assert (); + try + { + f.SelectNextControl(this, true, true, true, true); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + } + } + + // VSWhidbey#262686: Two controls in UserControls that don't take focus via UI can have bad behavior if ... + // VSWhidbey#537131: + // When a control is removed from a container, not only do we need to clear the unvalidatedControl of that + // container potentially, but the unvalidatedControl of all its container parents, up the chain, needs to + // now point to the old parent of the disappearing control. + cc = this; + while (cc != null) { + Control parent = cc.ParentInternal; + if (parent == null) { + break; + } + else { + cc = parent.GetContainerControlInternal() as ContainerControl; + } + if (cc != null && + cc.unvalidatedControl != null && + (cc.unvalidatedControl == control || control.Contains(cc.unvalidatedControl))) + { + cc.unvalidatedControl = oldParent; + } + } + + if (control == unvalidatedControl || control.Contains(unvalidatedControl)) { + unvalidatedControl = null; + } + } + + private bool AssignActiveControlInternal(Control value) { +#if DEBUG + if (value == null || (value != null && value.ParentInternal != null && !value.ParentInternal.IsContainerControl)) + { + Debug.Assert(value == null || (value.ParentInternal != null && this == value.ParentInternal.GetContainerControlInternal())); + } +#endif + + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::AssignActiveControlInternal(" + (value == null ? "null" : value.Name) + ") - " + this.Name); + if (activeControl != value) { + // cpb: #7318 +#if FALSE + if (activeControl != null) { + AxHost.Container cont = FindAxContainer(); + if (cont != null) { + cont.OnOldActiveControl(activeControl, value); + } + } +#endif + try { + if (value != null) { + value.BecomingActiveControl = true; + } + activeControl = value; + UpdateFocusedControl(); + } + finally { + if (value != null) { + value.BecomingActiveControl = false; + } + } + if (activeControl == value) { + // cpb: #7318 +#if FALSE + AxHost.Container cont = FindAxContainer(); + if (cont != null) { + cont.OnNewActiveControl(value); + } +#endif + Form form = FindFormInternal(); + if (form != null) + { + form.UpdateDefaultButton(); + } + } + } + else { + focusedControl = activeControl; + } + return(activeControl == value); + } + + /// + /// Used to notify the AxContainer that the form + /// has been created. This should only be called if + /// there is an AX container. + /// + private void AxContainerFormCreated() { + ((AxHost.AxContainer)Properties.GetObject(PropAxContainer)).FormCreated(); + } + + /// + /// Specifies whether this control can process the mnemonic or not. + /// + internal override bool CanProcessMnemonic() { +#if DEBUG + TraceCanProcessMnemonic(); +#endif + if( this.state[stateProcessingMnemonic]){ + return true; + } + + return base.CanProcessMnemonic(); + } + + internal AxHost.AxContainer CreateAxContainer() { + object aXContainer = Properties.GetObject(PropAxContainer); + if (aXContainer == null) { + aXContainer = new AxHost.AxContainer(this); + Properties.SetObject(PropAxContainer, aXContainer); + } + return(AxHost.AxContainer)aXContainer; + } + + /// + /// + /// Disposes of the resources (other than memory) used by + /// the + /// . + /// + protected override void Dispose(bool disposing) { + if (disposing) { + activeControl = null; + } + + base.Dispose(disposing); + + focusedControl = null; + unvalidatedControl = null; + } + + /// + /// Recursively enables required scaling from the given control + /// + private void EnableRequiredScaling(Control start, bool enable) { + start.RequiredScalingEnabled = enable; + foreach(Control c in start.Controls) { + EnableRequiredScaling(c, enable); + } + } + + /// + /// Assigns focus to the activeControl. If there is no activeControl then + /// focus is given to the form. + /// package scope for Form + /// + internal void FocusActiveControlInternal() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::FocusActiveControlInternal() - " + this.Name); +#if DEBUG + // Things really get ugly if you try to pop up an assert dialog here + if (activeControl != null && !this.Contains(activeControl)) + Debug.WriteLine("ActiveControl is not a child of this ContainerControl"); +#endif + + if (activeControl != null && activeControl.Visible) { + + // Avoid focus loops, especially with ComboBoxes, on Win98/ME. + // + IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); + if (focusHandle == IntPtr.Zero || Control.FromChildHandleInternal(focusHandle) != activeControl) { + UnsafeNativeMethods.SetFocus(new HandleRef(activeControl, activeControl.Handle)); + } + } + else { + // Determine and focus closest visible parent + ContainerControl cc = this; + while (cc != null && !cc.Visible) + { + Control parent = cc.ParentInternal; + if (parent != null) + { + cc = parent.GetContainerControlInternal() as ContainerControl; + } + else { + break; + } + } + if (cc != null && cc.Visible) + { + UnsafeNativeMethods.SetFocus(new HandleRef(cc, cc.Handle)); + } + } + } + + + internal override Size GetPreferredSizeCore(Size proposedSize) { + // Translating 0,0 from ClientSize to actual Size tells us how much space + // is required for the borders. + Size borderSize = SizeFromClientSize(Size.Empty); + Size totalPadding = borderSize + Padding.Size; + return LayoutEngine.GetPreferredSize(this, proposedSize - totalPadding) + totalPadding; + } + + internal override Rectangle GetToolNativeScreenRectangle() { + if (this.GetTopLevel()) { + // Get window's client rectangle (i.e. without chrome) expressed in screen coordinates + NativeMethods.RECT clientRectangle = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(this, this.Handle), ref clientRectangle); + NativeMethods.POINT topLeftPoint = new NativeMethods.POINT(0, 0); + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, this.Handle), topLeftPoint); + return new Rectangle(topLeftPoint.x, topLeftPoint.y, clientRectangle.right, clientRectangle.bottom); + } + else { + return base.GetToolNativeScreenRectangle(); + } + } + + /// + /// This method calcuates the auto scale dimensions based on the + /// control's current font. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // Refers to 'fontMeasureString'. + [SuppressMessage("Microsoft.Usage", "CA2204:LiteralsShouldBeSpelledCorrectly")] + private SizeF GetFontAutoScaleDimensions() + { + SizeF retval = SizeF.Empty; + + // Windows uses CreateCompatibleDC(NULL) to get a + // memory DC for the monitor the application is currently + // on. + IntPtr dc = UnsafeNativeMethods.CreateCompatibleDC(NativeMethods.NullHandleRef); + if (dc == IntPtr.Zero) { + throw new Win32Exception(); + } + + HandleRef hdc = new HandleRef(this, dc); + + try { + // We clone the Windows scaling function here as closely as + // possible. They use textmetric for height, and textmetric + // for width of fixed width fonts. For variable width fonts + // they use GetTextExtentPoint32 and pass in a long a-Z string. + // We must do the same here if our dialogs are to scale in a + // similar fashion. + + HandleRef hfont = new HandleRef(this, FontHandle); + HandleRef hfontOld = new HandleRef(this, SafeNativeMethods.SelectObject(hdc, hfont)); + + try { + NativeMethods.TEXTMETRIC tm = new NativeMethods.TEXTMETRIC(); + SafeNativeMethods.GetTextMetrics(hdc, ref tm); + + retval.Height = tm.tmHeight; + + if ((tm.tmPitchAndFamily & NativeMethods.TMPF_FIXED_PITCH) != 0) { + IntNativeMethods.SIZE size = new IntNativeMethods.SIZE(); + IntUnsafeNativeMethods.GetTextExtentPoint32(hdc, fontMeasureString, size); + // Note: intentional integer round off here for Win32 compat + //retval.Width = (float)(((size.cx / 26) + 1) / 2); + retval.Width = (int)Math.Round(((float)size.cx) / ((float)fontMeasureString.Length)); + } + else { + retval.Width = tm.tmAveCharWidth; + } + } + finally { + SafeNativeMethods.SelectObject(hdc, hfontOld); + } + } + finally { + UnsafeNativeMethods.DeleteCompatibleDC(hdc); + } + + return retval; + } + + /// + /// This method is called when one of the auto scale properties changes, indicating + /// that we should scale controls on the next layout. + /// + private void LayoutScalingNeeded() { + + EnableRequiredScaling(this, true); + state[stateScalingNeededOnLayout] = true; + + // If layout is not currently suspended, then perform a layout now, + // as otherwise we don't know when one will happen. + if (!IsLayoutSuspended) { + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + } + } + + /// + /// to maintain backwards compat with AutoScale on form, we need to keep the + /// two models from conflicting. This method is only here for Form to override + /// it and update its AutoScale property appropriately. + /// + internal virtual void OnAutoScaleModeChanged() { + } + + /// + /// + /// Raises the AutoValidateChanged event. + /// + protected virtual void OnAutoValidateChanged(EventArgs e) { + if (autoValidateChanged != null) { + autoValidateChanged(this, e); + } + } + + // Refer VsWhidbey : 515910 & 269769 + internal override void OnFrameWindowActivate(bool fActivate) { + if (fActivate) { + IntSecurity.ModifyFocus.Assert(); + try { + if (ActiveControl == null) { + SelectNextControl(null, true, true, true, false); + } + InnerMostActiveContainerControl.FocusActiveControlInternal(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// + /// Called when a child is about to resume its layout. The default implementation + /// calls OnChildLayoutResuming on the parent. + /// + internal override void OnChildLayoutResuming(Control child, bool performLayout) { + base.OnChildLayoutResuming(child, performLayout); + + // do not scale children if AutoScaleMode is set to Dpi + if (DpiHelper.EnableSinglePassScalingOfDpiForms && (AutoScaleMode == AutoScaleMode.Dpi)) { + return; + } + + // We need to scale children before their layout engines get to them. + // We don't have a lot of opportunity for that because the code + // generator always generates a PerformLayout() right after a + // ResumeLayout(false). This seems to be the most oppportune place + // for thiis, although it is unfortunate. + if (!state[stateScalingChild] && !performLayout && AutoScaleMode != AutoScaleMode.None && AutoScaleMode != AutoScaleMode.Inherit && state[stateScalingNeededOnLayout]) { + state[stateScalingChild] = true; + try { + child.Scale(AutoScaleFactor, SizeF.Empty, this); + } + finally { + state[stateScalingChild] = false; + } + } + } + + /// + /// + /// Raises the CreateControl event. + /// + protected override void OnCreateControl() { + base.OnCreateControl(); + + if (Properties.GetObject(PropAxContainer) != null) { + AxContainerFormCreated(); + } + OnBindingContextChanged(EventArgs.Empty); + } + + /// + /// + /// We override this to clear the current autoscale cache. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnFontChanged(EventArgs e) { + if (AutoScaleMode == AutoScaleMode.Font) { + currentAutoScaleDimensions = SizeF.Empty; + + // If the font changes and we are going to autoscale + // as a result, do it now, and wrap the entire + // transaction in a suspend layout to prevent + // the layout engines from conflicting with our + // work. + SuspendAllLayout(this); + + try { + PerformAutoScale(!RequiredScalingEnabled, true); + } + finally { + ResumeAllLayout(this, false); + } + } + + base.OnFontChanged(e); + } + + /// + /// + /// This is called by the top level form to clear the current autoscale cache. + /// + internal void FormDpiChanged(float factor) { + Debug.Assert(this is Form); + + currentAutoScaleDimensions = SizeF.Empty; + + SuspendAllLayout(this); + SizeF factorSize = new SizeF(factor, factor); + try { + ScaleChildControls(factorSize, factorSize, this, true); + } + finally { + ResumeAllLayout(this, false); + } + } + + /// + /// Container controls scale during layout. + /// + protected override void OnLayout(LayoutEventArgs e) { + PerformNeededAutoScaleOnLayout(); + base.OnLayout(e); + } + + /// + /// + /// Called when the last resume layout call is made. If performLayout is true + /// a layout will occur as soon as this call returns. Layout is + /// still suspended when this call is made. The default implementation + /// calls OnChildLayoutResuming on the parent, if it exists. + /// + internal override void OnLayoutResuming(bool performLayout) { + PerformNeededAutoScaleOnLayout(); + base.OnLayoutResuming(performLayout); + } + + /// + /// + /// Called when the parent changes. Container controls prefer to have their parents + /// scale themselves, but when a parent is first changed, and as a result the font + /// changes as well, a container control should scale itself. We save off this state + /// so a later font change can trigger a scale of us. We only set this state if + /// required scaling is disabled: if it is enabled we are still initializing and + /// parent changes are normal. + /// + protected override void OnParentChanged(EventArgs e) { + state[stateParentChanged] = !RequiredScalingEnabled; + base.OnParentChanged(e); + } + + /// + /// + /// Performs scaling of this control. Scaling works by scaling all children of this control. + /// Those children that are ContainerControls will have their PerformAutoScale method called + /// so they can scale their children. + /// + public void PerformAutoScale() { + PerformAutoScale(true, true); + } + + /// + /// + /// Performs scaling of this control. Scaling works by scaling all children of this control. + /// + /// If includedBounds is true those controls whose bounds have changed since + /// they were last scaled will be auto scaled. If excludedBounds is true those + /// controls whose bounds have not changed since they were last scaled will be + /// auto scaled. + /// + /// PerformAutoScale is automatically called during OnLayout. The parameters to + /// PerformAutoScale are passed as follows: + /// + /// 1. If AutoScaleDimensions are set, includedBounds is set to true. + /// 2. If a font change occurred, excludedBounds is set to true. + /// + private void PerformAutoScale(bool includedBounds, bool excludedBounds) { + + bool suspended = false; + + try { + if (AutoScaleMode != AutoScaleMode.None && AutoScaleMode != AutoScaleMode.Inherit) { + SuspendAllLayout(this); + suspended = true; + + // Walk each control recursively and scale. We search the control + // for its own set of scaling data; if we don't find it, we use the current + // container control's scaling data. Once we scale a control, we set + // its scaling factors to unity. As we walk out of a container control, + // we set its scaling factor to unity too. + SizeF included = SizeF.Empty; + SizeF excluded = SizeF.Empty; + + if (includedBounds) included = AutoScaleFactor; + if (excludedBounds) excluded = AutoScaleFactor; + + Scale(included, excluded, this); + autoScaleDimensions = CurrentAutoScaleDimensions; + } + } + finally { + if (includedBounds) { + state[stateScalingNeededOnLayout] = false; + EnableRequiredScaling(this, false); + } + state[stateParentChanged] = false; + + if (suspended) { + ResumeAllLayout(this, false); + } + } + } + + /// + /// Checks to see if we need to perform an autoscale in + /// response to a layout. + /// + private void PerformNeededAutoScaleOnLayout() { + if (state[stateScalingNeededOnLayout]) { + PerformAutoScale(state[stateScalingNeededOnLayout], false); + } + } + + /// + /// Recursively resumes all layout. + /// + internal void ResumeAllLayout(Control start, bool performLayout) { + + ControlCollection controlsCollection = start.Controls; + // This may have changed the sizes of our children. + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + ResumeAllLayout(controlsCollection[i], performLayout); + } + + start.ResumeLayout(performLayout); + } + + /// + /// Recursively suspends all layout. + /// + internal void SuspendAllLayout(Control start) { + start.SuspendLayout(); + CommonProperties.xClearPreferredSizeCache(start); + + ControlCollection controlsCollection = start.Controls; + // This may have changed the sizes of our children. + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + SuspendAllLayout(controlsCollection[i]); + } + } + + /// + /// + /// Overrides the default scaling mechanism to account for autoscaling. This + /// override behaves as follows: any unchanged controls are always scaled + /// according to the container control's AutoScaleFactor. Any changed controls are + /// scaled according to the provided scaling factor. + /// + internal override void Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) { + + // If we're inhieriting our scaling from our parent, Scale is really easy: just do the + // base class implementation. + if (AutoScaleMode == AutoScaleMode.Inherit) { + base.Scale(includedFactor, excludedFactor, requestingControl); + } + else { + // We scale our controls based on our own auto scaling + // factor, not the one provided to us. We only do this for + // controls that are not required to be scaled (excluded controls). + SizeF ourExcludedFactor = excludedFactor; + SizeF childIncludedFactor = includedFactor; + + if (!ourExcludedFactor.IsEmpty) { + ourExcludedFactor = AutoScaleFactor; + } + + // If we're not supposed to be scaling, don't scale the internal + // ones either. + if (AutoScaleMode == AutoScaleMode.None) { + childIncludedFactor = AutoScaleFactor; + } + + // When we scale, we are establishing new baselines for the + // positions of all controls. Therefore, we should resume(false). + using (new LayoutTransaction(this, this, PropertyNames.Bounds, false)) { + + // Our own container control poses a problem. We want + // an outer control to be responsible for scaling it, + // because the outer control knows the container's dimensions. + // We detect this by checking who is requesting that the + // scaling occur. + SizeF ourExternalContainerFactor = ourExcludedFactor; + + if (!excludedFactor.IsEmpty && ParentInternal != null) { + ourExternalContainerFactor = SizeF.Empty; + + bool scaleUs = (requestingControl != this || state[stateParentChanged]); + + // Hack for design time support: we may be parented within another form + // that is not part of the designer. + if (!scaleUs) { + bool dt = false; + bool parentDt = false; + ISite site = Site; + ISite parentSite = ParentInternal.Site; + + if (site != null) dt = site.DesignMode; + if (parentSite != null) parentDt = parentSite.DesignMode; + + if (dt && !parentDt) { + scaleUs = true; + } + } + + if (scaleUs) { + ourExternalContainerFactor = excludedFactor; + } + } + + ScaleControl(includedFactor, ourExternalContainerFactor, requestingControl); + ScaleChildControls(childIncludedFactor, ourExcludedFactor, requestingControl); + } + } + } + + /// + /// Process an arrowKey press by selecting the next control in the group + /// that the activeControl belongs to. + /// + /// + private bool ProcessArrowKey(bool forward) { + Control group = this; + if (activeControl != null) { + group = activeControl.ParentInternal; + } + return group.SelectNextControl(activeControl, forward, false, false, true); + } + + /// + /// + /// + /// Processes a dialog character. Overrides Control.processDialogChar(). + /// This method calls the processMnemonic() method to check if the character + /// is a mnemonic for one of the controls on the form. If processMnemonic() + /// does not consume the character, then base.processDialogChar() is + /// called. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogChar(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessDialogChar [" + charCode.ToString() + "]"); +#endif + // If we're the top-level form or control, we need to do the mnemonic handling + // + ContainerControl parent = GetContainerControlInternal() as ContainerControl; + if (parent != null && charCode != ' ' && ProcessMnemonic(charCode)) return true; + return base.ProcessDialogChar(charCode); + } + + /// + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN + /// keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. For the TAB key, the method selects the next control + /// on the form. For the arrow keys, + /// !!! + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessDialogKey [" + keyData.ToString() + "]"); +#endif + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Tab: + if (ProcessTabKey((keyData & Keys.Shift) == Keys.None)) return true; + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + if (ProcessArrowKey(keyCode == Keys.Right || + keyCode == Keys.Down)) return true; + break; + } + } + return base.ProcessDialogKey(keyData); + } + + + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessCmdKey " + msg.ToString()); + + if (base.ProcessCmdKey(ref msg, keyData)) { + return true; + } + if (ParentInternal == null) { + // unfortunately, we have to stick this here for the case where we're hosted without + // a form in the chain. This would be something like a context menu strip with shortcuts + // hosted within Office, VS or IE. + // + // this is an optimized search O(number of ToolStrips in thread) + // that happens only if the key routing makes it to the top. + return ToolStripManager.ProcessCmdKey(ref msg, keyData); + } + return false; + } + + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessMnemonic [" + charCode.ToString() + "]"); + Debug.Indent(); + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "this == " + ToString()); +#endif + if( !CanProcessMnemonic() ){ + return false; + } + + if (Controls.Count == 0) { + Debug.Unindent(); + return false; + } + + // Start with the active control. + // + Control start = ActiveControl; + +#if DEBUG + int count = 0; +#endif //DEBUG + + // Set the processing mnemonic flag so child controls don't check for it when checking if they + // can process the mnemonic. + this.state[stateProcessingMnemonic] = true; + + bool processed = false; + + try { + // safety flag to avoid infinite loop when testing controls in a container. + bool wrapped = false; + + Control ctl = start; + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Check starting at '" + ((start != null) ? start.ToString() : "") + "'"); + + do { + // Loop through the controls starting at the control next to the current Active control in the Tab order + // till we find someone willing to process this mnemonic. + // We don't start the search on the Active control to allow controls in the same container with the same + // mnemonic (bad UI design but supported) to be processed sequentially (see VSWhidbey#428029). +#if DEBUG + count++; + if (count > 9999) { + Debug.Fail("Infinite loop trying to find controls which can ProcessMnemonic()!!!"); + } +#endif //DEBUG + ctl = GetNextControl(ctl, true); + + if ( ctl != null ) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...checking for mnemonics on " + ctl.ToString()); + // Control.TraceMnemonicProcessing.Enabled disables CanProcessMnemonic consistency check. + bool canProcess = Control.TraceMnemonicProcessing.Enabled ? true : ctl.CanProcessMnemonic(); // Processing the mnemonic can change the value of CanProcessMnemonic. See ASURT 39583. +#endif + // Processing the mnemonic can change the value of CanProcessMnemonic. See ASURT 39583. + if (ctl.ProcessMnemonic(charCode)) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...mnemonics found"); + Debug.Assert((Control.TraceMnemonicProcessing.Enabled || canProcess), "ProcessMnemonic returned true, even though CanProcessMnemonic() is false. Someone probably overrode ProcessMnemonic and forgot to test CanSelect or CanProcessMnemonic()."); + Debug.Unindent(); +#endif + processed = true; + break; + } + } + else { // ctl is null + if( wrapped ){ + break; // This avoids infinite loops + } + + wrapped = true; + } + } while (ctl != start); + } + finally { + this.state[stateProcessingMnemonic] = false; + } + + Debug.Unindent(); + return processed; + } + + /// + /// + /// Selects the next available control and makes it the active control. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool ProcessTabKey(bool forward) { + if (SelectNextControl(activeControl, forward, true, true, false)) return true; + return false; + } + + private ScrollableControl FindScrollableParent(Control ctl) { + Control current = ctl.ParentInternal; + while (current != null && !(current is ScrollableControl)) { + current = current.ParentInternal; + } + if (current != null) { + return(ScrollableControl)current; + } + return null; + } + + private void ScrollActiveControlIntoView() { + Control last = activeControl; + if (last != null) { + ScrollableControl scrollParent = FindScrollableParent(last); + + while (scrollParent != null) { + scrollParent.ScrollControlIntoView(activeControl); + last = scrollParent; + scrollParent = FindScrollableParent(scrollParent); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void Select(bool directed, bool forward) { + bool correctParentActiveControl = true; + if (ParentInternal != null) + { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + if (c != null) + { + c.ActiveControl = this; + correctParentActiveControl = (c.ActiveControl == this); + } + } + if (directed && correctParentActiveControl) + { + SelectNextControl(null, forward, true, true, false); + } + } + + /// + /// Implements ActiveControl property setter. + /// + private void SetActiveControl(Control ctl) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::SetActiveControl(" + (ctl == null ? "null" : ctl.Name) + ") - " + this.Name); + + SetActiveControlInternal(ctl); + } + + /// + /// Unsafe version of SetActiveControl - Use with caution! + /// + internal void SetActiveControlInternal(Control value) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::SetActiveControlInternal(" + (value == null ? "null" : value.Name) + ") - " + this.Name); + if (activeControl != value || (value != null && !value.Focused)) { + if (value != null && !Contains(value)) { + throw new ArgumentException(SR.GetString(SR.CannotActivateControl)); + } + + bool ret; + ContainerControl cc = this; + + if (value != null && value.ParentInternal != null) + { + cc = (value.ParentInternal.GetContainerControlInternal()) as ContainerControl; + } + if (cc != null) + { + // Call to the recursive function that corrects the chain + // of active controls + ret = cc.ActivateControlInternal(value, false); + } + else + { + ret = AssignActiveControlInternal(value); + } + + if (cc != null && ret) + { + ContainerControl ccAncestor = this; + while (ccAncestor.ParentInternal != null && + ccAncestor.ParentInternal.GetContainerControlInternal() is ContainerControl) + { + ccAncestor = ccAncestor.ParentInternal.GetContainerControlInternal() as ContainerControl; + Debug.Assert(ccAncestor != null); + } + + if (ccAncestor.ContainsFocus && + (value == null || + !(value is UserControl) || + (value is UserControl && !((UserControl)value).HasFocusableChild()))) + { + cc.FocusActiveControlInternal(); + } + } + } + } + + internal ContainerControl InnerMostActiveContainerControl + { + get + { + ContainerControl ret = this; + while (ret.ActiveControl is ContainerControl) + { + ret = (ContainerControl) ret.ActiveControl; + } + return ret; + } + } + + internal ContainerControl InnerMostFocusedContainerControl + { + get + { + ContainerControl ret = this; + while (ret.focusedControl is ContainerControl) + { + ret = (ContainerControl) ret.focusedControl; + } + return ret; + } + } + + /// + /// + /// Updates the default button based on current selection, and the + /// acceptButton property. + /// + /// + protected virtual void UpdateDefaultButton() { + // hook for form + } + + /// + /// Updates the focusedControl variable by walking towards the + /// activeControl variable, firing enter and leave events and validation + /// as necessary. + /// + /// + internal void UpdateFocusedControl() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::UpdateFocusedControl() - " + this.Name); + + // Capture the current focusedControl as the unvalidatedControl if we don't have one/are not validating. + EnsureUnvalidatedControl(focusedControl); + Control pathControl = focusedControl; + + while (activeControl != pathControl) { + if (pathControl == null || pathControl.IsDescendant(activeControl)) { + // heading down. find next control on path. + Control nextControlDown = activeControl; + while (true) { + Control parent = nextControlDown.ParentInternal; + if (parent == this || parent == pathControl) + break; + nextControlDown = nextControlDown.ParentInternal; + } + + Control priorFocusedControl = focusedControl = pathControl; + EnterValidation(nextControlDown); + // If validation changed position, then jump back to the loop. + if (focusedControl != priorFocusedControl) { + pathControl = focusedControl; + continue; + } + + pathControl = nextControlDown; + if (NativeWindow.WndProcShouldBeDebuggable) { + pathControl.NotifyEnter(); + } + else { + try { + pathControl.NotifyEnter(); + } + catch (Exception e) { + Application.OnThreadException(e); + } + } + } + else { + // heading up. + ContainerControl innerMostFCC = InnerMostFocusedContainerControl; + Control stopControl = null; + + if (innerMostFCC.focusedControl != null) + { + pathControl = innerMostFCC.focusedControl; + stopControl = innerMostFCC; + + if (innerMostFCC != this) + { + innerMostFCC.focusedControl = null; + if (!(innerMostFCC.ParentInternal != null && innerMostFCC.ParentInternal is MdiClient)) + { + // Don't reset the active control of a MDIChild that loses the focus + innerMostFCC.activeControl = null; + } + } + } + else + { + pathControl = innerMostFCC; + // innerMostFCC.ParentInternal can be null when the ActiveControl is deleted. + if (innerMostFCC.ParentInternal != null) + { + ContainerControl cc = (innerMostFCC.ParentInternal.GetContainerControlInternal()) as ContainerControl; + stopControl = cc; + if (cc != null && cc != this) + { + cc.focusedControl = null; + cc.activeControl = null; + } + } + } + + do + { + Control leaveControl = pathControl; + + if (pathControl != null) + { + pathControl = pathControl.ParentInternal; + } + + if (pathControl == this) + { + pathControl = null; + } + + if (leaveControl != null) + { + if (NativeWindow.WndProcShouldBeDebuggable) { + leaveControl.NotifyLeave(); + } + else { + try { + leaveControl.NotifyLeave(); + } + catch (Exception e) { + Application.OnThreadException(e); + } + } + } + } + while (pathControl != null && + pathControl != stopControl && + !pathControl.IsDescendant(activeControl)); + } + } + +#if DEBUG + if (activeControl == null || (activeControl != null && activeControl.ParentInternal != null && !activeControl.ParentInternal.IsContainerControl)) + { + Debug.Assert(activeControl == null || activeControl.ParentInternal.GetContainerControlInternal() == this); + } +#endif + focusedControl = activeControl; + if (activeControl != null) { + EnterValidation(activeControl); + } + } + + /// + /// Make sure we have a valid choice of last unvalidated control if at all possible. + /// + /// + private void EnsureUnvalidatedControl(Control candidate) { + // Don't change the unvalidated control while in the middle of validation (re-entrancy) + if (state[stateValidating]) { + return; + } + + // Don't change the existing unvalidated control + if (unvalidatedControl != null) { + return; + } + + // No new choice of unvalidated control was specified - leave unvalidated control blank + if (candidate == null) { + return; + } + + // Specified control has auto-validation disabled - leave unvalidated control blank + if (!candidate.ShouldAutoValidate) { + return; + } + + // Go ahead and make specified control the current unvalidated control for this container + unvalidatedControl = candidate; + + // In the case of nested container controls, try to pick the deepest possible unvalidated + // control. For a container with no unvalidated control, use the active control instead. + // Stop as soon as we encounter any control that has auto-validation turned off. + while (unvalidatedControl is ContainerControl) { + ContainerControl container = unvalidatedControl as ContainerControl; + + if (container.unvalidatedControl != null && container.unvalidatedControl.ShouldAutoValidate) { + unvalidatedControl = container.unvalidatedControl; + } + else if (container.activeControl != null && container.activeControl.ShouldAutoValidate) { + unvalidatedControl = container.activeControl; + } + else { + break; + } + } + } + + /// + /// Validates the last unvalidated control and its ancestors (up through the ancestor in common + /// with enterControl) if enterControl causes validation. + /// + /// + private void EnterValidation(Control enterControl) { + // No unvalidated control to validate - stop now + if (unvalidatedControl == null) { + return; + } + + // Entered control does not trigger validation - stop now + if (!enterControl.CausesValidation) { + return; + } + + // Get the effective AutoValidate mode for this control (based on its container control) + AutoValidate autoValidateMode = Control.GetAutoValidateForControl(unvalidatedControl); + + // Auto-validate has been turned off in container of unvalidated control - stop now + if (autoValidateMode == AutoValidate.Disable) { + return; + } + + // Find common ancestor of entered control and unvalidated control + Control commonAncestor = enterControl; + while (commonAncestor != null && !commonAncestor.IsDescendant(unvalidatedControl)) { + commonAncestor = commonAncestor.ParentInternal; + } + + // Should we force focus to stay on same control if there is a validation error? + bool preventFocusChangeOnError = (autoValidateMode == AutoValidate.EnablePreventFocusChange); + + // Validate control and its ancestors, up to (but not including) the common ancestor + ValidateThroughAncestor(commonAncestor, preventFocusChangeOnError); + } + + /// + /// + /// + /// Validates the last unvalidated control and its ancestors up through, but not including the current control. + /// + /// This version always performs validation, regardless of the AutoValidate setting of the control's parent. + /// + /// + // + // ------------------------------- + // INTERNAL NOTE FOR Microsoft DEVS: This version is intended for user code that wants to force validation, even + // while auto-validation is turned off. When adding any explicit Validate() calls to our code, consider using + // Validate(true) rather than Validate(), so that you will be sensitive to the current auto-validation setting. + // ------------------------------- + // + public bool Validate() { + return Validate(false); + } + + /// + /// + /// + /// Validates the last unvalidated control and its ancestors up through, but not including the current control. + /// + /// This version will skip validation if checkAutoValidate is true and the effective AutoValidate setting, as + /// determined by the control's parent, is AutoValidate.Disable. + /// + /// + public bool Validate(bool checkAutoValidate) { + bool validatedControlAllowsFocusChange; + return ValidateInternal(checkAutoValidate, out validatedControlAllowsFocusChange); + } + + internal bool ValidateInternal(bool checkAutoValidate, out bool validatedControlAllowsFocusChange) { + validatedControlAllowsFocusChange = false; + + if (this.AutoValidate == AutoValidate.EnablePreventFocusChange || + (activeControl != null && activeControl.CausesValidation)) { + if (unvalidatedControl == null) { + if (focusedControl is ContainerControl && focusedControl.CausesValidation) { + ContainerControl c = (ContainerControl)focusedControl; + if( !c.ValidateInternal(checkAutoValidate, out validatedControlAllowsFocusChange) ){ + return false; + } + } + else { + unvalidatedControl = focusedControl; + } + } + + // Should we force focus to stay on same control if there is a validation error? + bool preventFocusChangeOnError = true; + + Control controlToValidate = unvalidatedControl != null ? unvalidatedControl : focusedControl; + + if (controlToValidate != null) + { + // Get the effective AutoValidate mode for unvalidated control (based on its container control) + AutoValidate autoValidateMode = Control.GetAutoValidateForControl(controlToValidate); + + // Auto-validate has been turned off in container of unvalidated control - stop now + if (checkAutoValidate && autoValidateMode == AutoValidate.Disable) + { + return true; + } + preventFocusChangeOnError = (autoValidateMode == AutoValidate.EnablePreventFocusChange); + validatedControlAllowsFocusChange = (autoValidateMode == AutoValidate.EnableAllowFocusChange); + } + + return ValidateThroughAncestor(null, preventFocusChangeOnError); + } + return true; + } + + /// + /// + /// Validates all selectable child controls in the container, including descendants. This is + /// equivalent to calling ValidateChildren(ValidationConstraints.Selectable). See + /// for details of exactly which child controls will be validated. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual bool ValidateChildren() { + return ValidateChildren(ValidationConstraints.Selectable); + } + + /// + /// + /// Validates all the child controls in the container. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public virtual bool ValidateChildren(ValidationConstraints validationConstraints) { + // validationConstraints must be a combination of + // None = 0x00, Selectable = 0x01, Enabled = 0x02, Visible = 0x04, TabStop = 0x08, ImmediateChildren = 0x10 + // Not using ClientUtils.IsValidEnum here because this is a flags enum and everything is valid between 0x00 and 0x1F. + if ((int)validationConstraints < 0x00 || (int)validationConstraints > 0x1F) + { + throw new InvalidEnumArgumentException("validationConstraints", (int)validationConstraints, typeof(ValidationConstraints)); + } + return !PerformContainerValidation(validationConstraints); + } + + private bool ValidateThroughAncestor(Control ancestorControl, bool preventFocusChangeOnError) { + if (ancestorControl == null) + ancestorControl = this; + if (state[stateValidating]) + return false; + if (unvalidatedControl == null) + unvalidatedControl = focusedControl; + //return true for a Container Control with no controls to validate.... + // + if (unvalidatedControl == null) + return true; + if (!ancestorControl.IsDescendant(unvalidatedControl)) + return false; + + this.state[stateValidating] = true; + bool cancel = false; + + Control currentActiveControl = activeControl; + Control currentValidatingControl = unvalidatedControl; + if (currentActiveControl != null) { + currentActiveControl.ValidationCancelled = false; + if (currentActiveControl is ContainerControl) { + ContainerControl currentActiveContainerControl = currentActiveControl as ContainerControl; + + currentActiveContainerControl.ResetValidationFlag(); + } + } + try { + while (currentValidatingControl != null && currentValidatingControl != ancestorControl) { + try { + cancel = currentValidatingControl.PerformControlValidation(false); + } + catch { + cancel = true; + throw; + } + + if (cancel) { + break; + } + + currentValidatingControl = currentValidatingControl.ParentInternal; + } + + if (cancel && preventFocusChangeOnError) { + if (unvalidatedControl == null && currentValidatingControl != null && + ancestorControl.IsDescendant(currentValidatingControl)) + { + unvalidatedControl = currentValidatingControl; + } + // This bit 'marks' the control that was going to get the focus, so that it will ignore any pending + // mouse or key events. Otherwise it would still perform its default 'click' action or whatever. + if (currentActiveControl == activeControl) { + if (currentActiveControl != null) { + CancelEventArgs ev = new CancelEventArgs(); + ev.Cancel = true; + currentActiveControl.NotifyValidationResult(currentValidatingControl, ev); + if (currentActiveControl is ContainerControl) { + ContainerControl currentActiveContainerControl = currentActiveControl as ContainerControl; + if (currentActiveContainerControl.focusedControl != null) { + currentActiveContainerControl.focusedControl.ValidationCancelled = true; + } + currentActiveContainerControl.ResetActiveAndFocusedControlsRecursive(); + } + } + } + // This bit forces the focus to move back to the invalid control + SetActiveControlInternal(unvalidatedControl); + } + } + finally { + unvalidatedControl = null; + state[stateValidating] = false; + } + + return !cancel; + } + + private void ResetValidationFlag() { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + Control.ControlCollection children = this.Controls; + int count = children.Count; + for(int i = 0; i < count; i++) { + children[i].ValidationCancelled = false; + } + } + + internal void ResetActiveAndFocusedControlsRecursive() + { + if (activeControl is ContainerControl) + { + ((ContainerControl) activeControl).ResetActiveAndFocusedControlsRecursive(); + } + activeControl = null; + focusedControl = null; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeAutoValidate() { + return autoValidate != AutoValidate.Inherit; + } + + /// + /// WM_SETFOCUS handler + /// + /// + private void WmSetFocus(ref Message m) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "ContainerControl::WmSetFocus() - " + this.Name); + if (!HostedInWin32DialogManager) { + if (ActiveControl != null) { + WmImeSetFocus(); + // Microsoft: Do not raise GotFocus event since the focus + // is given to the visible ActiveControl + if (!ActiveControl.Visible) { + this.InvokeGotFocus(this, EventArgs.Empty); + } + FocusActiveControlInternal(); + } + else { + if (ParentInternal != null) { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + if (c != null) { + bool succeeded = false; + + ContainerControl knowncontainer = c as ContainerControl; + if (knowncontainer != null) { + succeeded = knowncontainer.ActivateControlInternal(this); + } + else { + + // SECREVIEW : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + succeeded = c.ActivateControl(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + if (!succeeded) { + return; + } + } + } + base.WndProc(ref m); + } + } + else { + base.WndProc(ref m); + } + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_SETFOCUS: + WmSetFocus(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ContextMenu.cs b/WindowsForms/Managed/System/WinForms/ContextMenu.cs new file mode 100644 index 000000000..1458f0312 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ContextMenu.cs @@ -0,0 +1,240 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// This class is used to put context menus on your form and show them for + /// controls at runtime. It basically acts like a regular Menu control, + /// but can be set for the ContextMenu property that most controls have. + /// + [ + DefaultEvent("Popup"), + ] + public class ContextMenu : Menu { + + private EventHandler onPopup; + private EventHandler onCollapse; + internal Control sourceControl; + + private RightToLeft rightToLeft = System.Windows.Forms.RightToLeft.Inherit; + + /// + /// + /// Creates a new ContextMenu object with no items in it by default. + /// + public ContextMenu() + : base(null) { + } + + /// + /// + /// Creates a ContextMenu object with the given MenuItems. + /// + public ContextMenu(MenuItem[] menuItems) + : base(menuItems) { + } + + /// + /// + /// The last control that was acted upon that resulted in this context + /// menu being displayed. + /// VSWHIDBEY 426099 - add demand for AllWindows. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ContextMenuSourceControlDescr) + ] + public Control SourceControl { + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + get { + return sourceControl; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRDescription(SR.MenuItemOnInitDescr)] + public event EventHandler Popup { + add { + onPopup += value; + } + remove { + onPopup -= value; + } + } + + /// + /// + /// Fires when the context menu collapses. + /// + [SRDescription(SR.ContextMenuCollapseDescr)] + public event EventHandler Collapse { + add { + onCollapse += value; + } + remove { + onCollapse -= value; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// text alignment and reading order will be from right to left. + /// + // VSWhidbey 164244: Add a DefaultValue attribute so that the Reset context menu becomes + // available in the Property Grid but the default value remains No. + [ + Localizable(true), + DefaultValue(RightToLeft.No), + SRDescription(SR.MenuRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + if (System.Windows.Forms.RightToLeft.Inherit == rightToLeft) { + if (sourceControl != null) { + return ((Control)sourceControl).RightToLeft; + } + else { + return RightToLeft.No; + } + } + else { + return rightToLeft; + } + } + set { + + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)){ + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + if (RightToLeft != value) { + rightToLeft = value; + UpdateRtl((value == System.Windows.Forms.RightToLeft.Yes)); + } + + } + } + + internal override bool RenderIsRightToLeft { + get { + return (rightToLeft == System.Windows.Forms.RightToLeft.Yes); + } + } + /// + /// + /// Fires the popup event + /// + protected internal virtual void OnPopup(EventArgs e) { + if (onPopup != null) { + onPopup(this, e); + } + } + + /// + /// + /// Fires the collapse event + /// + protected internal virtual void OnCollapse(EventArgs e) { + if (onCollapse != null) { + onCollapse(this, e); + } + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessCmdKey(ref Message msg, Keys keyData, Control control) { + sourceControl = control; + return ProcessCmdKey(ref msg, keyData); + } + + private void ResetRightToLeft() { + RightToLeft = RightToLeft.No; + } + + /// + /// + /// Returns true if the RightToLeft should be persisted in code gen. + /// + internal virtual bool ShouldSerializeRightToLeft() { + if (System.Windows.Forms.RightToLeft.Inherit == rightToLeft) { + return false; + } + return true; + } + + /// + /// + /// Displays the context menu at the specified position. This method + /// doesn't return until the menu is dismissed. + /// + public void Show(Control control, Point pos) { + Show(control, pos, NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON); + } + + /// + /// + /// Displays the context menu at the specified position. This method + /// doesn't return until the menu is dismissed. + /// + public void Show(Control control, Point pos, LeftRightAlignment alignment) { + + // This code below looks wrong but it's correct. + // Microsoft Left alignment means we want the menu to show up left of the point it is invoked from. + // We specify TPM_RIGHTALIGN which tells win32 to align the right side of this + // menu with the point (which aligns it Left visually) + if (alignment == LeftRightAlignment.Left) { + Show(control, pos, NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON | NativeMethods.TPM_RIGHTALIGN); + } + else { + Show(control, pos, NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON | NativeMethods.TPM_LEFTALIGN); + } + } + + private void Show(Control control, Point pos, int flags) { + if (control == null) + throw new ArgumentNullException("control"); + + if (!control.IsHandleCreated || !control.Visible) + throw new ArgumentException(SR.GetString(SR.ContextMenuInvalidParent), "control"); + + sourceControl = control; + + OnPopup(EventArgs.Empty); + pos = control.PointToScreen(pos); + SafeNativeMethods.TrackPopupMenuEx(new HandleRef(this, Handle), + flags, + pos.X, + pos.Y, + new HandleRef(control, control.Handle), + null); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ContextMenuStrip.cs b/WindowsForms/Managed/System/WinForms/ContextMenuStrip.cs new file mode 100644 index 000000000..7f9d8514d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ContextMenuStrip.cs @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + /// this class is just a conceptual wrapper around ToolStripDropDownMenu. + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("Opening"), + SRDescription(SR.DescriptionContextMenuStrip) + ] + public class ContextMenuStrip : ToolStripDropDownMenu { + + /// + /// Summary of ContextMenuStrip. + /// + public ContextMenuStrip(IContainer container) : base() { + // this constructor ensures ContextMenuStrip is disposed properly since its not parented to the form. + if (container == null) { + throw new ArgumentNullException("container"); + } + container.Add(this); + } + + public ContextMenuStrip(){ + } + + protected override void Dispose(bool disposing) { + base.Dispose(disposing); + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ContextMenuStripSourceControlDescr) + ] + public Control SourceControl { + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + get { + return SourceControlInternal; + } + } + + // minimal Clone implementation for DGV support only. + internal ContextMenuStrip Clone() { + + // VERY limited support for cloning. + + ContextMenuStrip contextMenuStrip = new ContextMenuStrip(); + + // copy over events + contextMenuStrip.Events.AddHandlers(this.Events); + + contextMenuStrip.AutoClose = AutoClose; + contextMenuStrip.AutoSize = AutoSize; + contextMenuStrip.Bounds = Bounds; + contextMenuStrip.ImageList = ImageList; + contextMenuStrip.ShowCheckMargin = ShowCheckMargin; + contextMenuStrip.ShowImageMargin = ShowImageMargin; + + + // copy over relevant properties + + for (int i = 0; i < Items.Count; i++) { + ToolStripItem item = Items[i]; + + if (item is ToolStripSeparator) { + contextMenuStrip.Items.Add(new ToolStripSeparator()); + } + else if (item is ToolStripMenuItem) { + ToolStripMenuItem menuItem = item as ToolStripMenuItem; + contextMenuStrip.Items.Add(menuItem.Clone()); + } + + } + return contextMenuStrip; + } + + // internal overload so we know whether or not to show mnemonics. + internal void ShowInternal(Control source, Point location, bool isKeyboardActivated) { + Show(source, location); + + // if we were activated by keyboard - show mnemonics. + if (isKeyboardActivated) { + ToolStripManager.ModalMenuFilter.Instance.ShowUnderlines = true; + } + } + + internal void ShowInTaskbar(int x, int y) { + // we need to make ourselves a topmost window + WorkingAreaConstrained = false; + Rectangle bounds = CalculateDropDownLocation(new Point(x,y), ToolStripDropDownDirection.AboveLeft); + Rectangle screenBounds = Screen.FromRectangle(bounds).Bounds; + if (bounds.Y < screenBounds.Y) { + bounds = CalculateDropDownLocation(new Point(x,y), ToolStripDropDownDirection.BelowLeft); + } + else if (bounds.X < screenBounds.X) { + bounds = CalculateDropDownLocation(new Point(x,y), ToolStripDropDownDirection.AboveRight); + } + bounds = WindowsFormsUtils.ConstrainToBounds(screenBounds, bounds); + + Show(bounds.X, bounds.Y); + } + + protected override void SetVisibleCore(bool visible) { + if (!visible) { + WorkingAreaConstrained = true; + } + base.SetVisibleCore(visible); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Control.Ime.cs b/WindowsForms/Managed/System/WinForms/Control.Ime.cs new file mode 100644 index 000000000..85dd5a88d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Control.Ime.cs @@ -0,0 +1,1462 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Configuration.Assemblies; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Globalization; + using System.Security.Permissions; + using System.Security; + using System.IO; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.Remoting; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Serialization.Formatters.Binary; + using System.Text; + using System.Threading; + using System.Windows.Forms.Design; + using System.Windows.Forms.Internal; + using Encoding = System.Text.Encoding; + using System.Drawing.Imaging; + using System.Windows.Forms.Layout; + + /// + /// Control's IME feature. + /// + public partial class Control : + Component, + UnsafeNativeMethods.IOleControl, + UnsafeNativeMethods.IOleObject, + UnsafeNativeMethods.IOleInPlaceObject, + UnsafeNativeMethods.IOleInPlaceActiveObject, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.IViewObject, + UnsafeNativeMethods.IViewObject2, + UnsafeNativeMethods.IPersist, + UnsafeNativeMethods.IPersistStreamInit, + UnsafeNativeMethods.IPersistPropertyBag, + UnsafeNativeMethods.IPersistStorage, + UnsafeNativeMethods.IQuickActivate, + ISupportOleDropSource, + IDropTarget, + ISynchronizeInvoke, + IWin32Window, + IArrangedElement, + IBindableComponent { + + // See IME feature spec at http://dotnetclient/Whidbey/dev/General%20Documentation/Microsoft.Ime.doc + + /// + /// Constants starting/ending the WM_CHAR messages to ignore count. See ImeWmCharsToIgnore property. + /// + private const int ImeCharsToIgnoreDisabled = -1; + private const int ImeCharsToIgnoreEnabled = 0; + + /// + /// The ImeMode value for controls with ImeMode = ImeMode.NoControl. See PropagatingImeMode property. + /// + private static ImeMode propagatingImeMode = ImeMode.Inherit; // Inherit means uninitialized. + + /// + /// This flag prevents resetting ImeMode value of the focused control. See IgnoreWmImeNotify property. + /// + private static bool ignoreWmImeNotify; + + /// + /// This flag works around an Issue with the Chinese IME sending IMENotify messages prior to WmInputLangChange + /// which would cause this code to use OnHalf as the default mode overriding .ImeMode property. See WmImeNotify + /// + private static bool lastLanguageChinese = false; + + /// + /// The ImeMode in the property store. + /// + internal ImeMode CachedImeMode { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CachedImeMode(), this = " + this ); + Debug.Indent(); + + // Get the ImeMode from the property store + // + bool found; + ImeMode cachedImeMode = (ImeMode) Properties.GetInteger( PropImeMode, out found ); + if( !found ) { + cachedImeMode = DefaultImeMode; + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "using DefaultImeMode == " + cachedImeMode ); + } + else { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "using property store ImeMode == " + cachedImeMode ); + } + + // If inherited, get the mode from this control's parent + // + if( cachedImeMode == ImeMode.Inherit ) { + Control parent = ParentInternal; + if( parent != null ) { + cachedImeMode = parent.CachedImeMode; + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "inherited from parent = " + parent.GetType() ); + } + else { + cachedImeMode = ImeMode.NoControl; + } + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "returning CachedImeMode == " + cachedImeMode ); + Debug.Unindent(); + + return cachedImeMode; + } + + set { + // WARNING: When the control is in restricted mode (!CanEnableIme) the CachedImeMode should be changed only programatically, + // calls generated by user interaction should be wrapped with a check for CanEnableIme. + // + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_CachedImeMode(), this = " + this ); + Debug.Indent(); + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, "Setting cached Ime == " + value ); + Properties.SetInteger( PropImeMode, (int) value ); + + Debug.Unindent(); + } + } + + /// + /// Specifies whether the ImeMode property value can be changed to an active value. + /// Added to support Password & ReadOnly (and maybe other) properties, which when set, should force disabling + /// the IME if using one. + /// + protected virtual bool CanEnableIme { + get { + // Note: If overriding this property make sure to add the Debug tracing code and call this method (base.CanEnableIme). + + Debug.Indent(); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format(CultureInfo.CurrentCulture, "Inside get_CanEnableIme(), value = {0}, this = {1}", ImeSupported, this ) ); + Debug.Unindent(); + + return ImeSupported; + } + } + + /// + /// Gets the current IME context mode. If no IME associated, ImeMode.Inherit is returned. + /// + internal ImeMode CurrentImeContextMode { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CurrentImeContextMode(), this = " + this ); + + if( this.IsHandleCreated ) { + return ImeContext.GetImeMode( this.Handle ); + } + else { + // window is not yet created hence no IME associated yet. + return ImeMode.Inherit; + } + } + } + + /// + /// + protected virtual ImeMode DefaultImeMode { + get { return ImeMode.Inherit; } + } + + /// + /// Flag used to avoid re-entrancy during WM_IME_NOTFIY message processing - see WmImeNotify(). + /// Also to avoid raising the ImeModeChanged event more than once during the process of changing the ImeMode. + /// + internal int DisableImeModeChangedCount { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_DisableImeModeChangedCount()" ); + Debug.Indent(); + + bool dummy; + int val = (int) Properties.GetInteger( PropDisableImeModeChangedCount, out dummy ); + + Debug.Assert( val >= 0, "Counter underflow." ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + val ); + Debug.Unindent(); + + return val; + } + set { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_DisableImeModeChangedCount(): " + value ); + Properties.SetInteger(PropDisableImeModeChangedCount, value); + } + } + + /// + /// Flag used to prevent setting ImeMode in focused control when losing focus and hosted in a non-Form shell. + /// See WmImeKillFocus() for more info. + /// + private static bool IgnoreWmImeNotify { + get { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_IgnoreWmImeNotify()"); + Debug.Indent(); + + bool val = Control.ignoreWmImeNotify; + + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + val); + Debug.Unindent(); + + return val; + } + set { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_IgnoreWmImeNotify(): " + value); + Control.ignoreWmImeNotify = value; + } + } + + /// + /// + /// Specifies a value that determines the IME (Input Method Editor) status of the + /// object when that object is selected. + /// + [ + SRCategory( SR.CatBehavior ), + Localizable( true ), + AmbientValue( ImeMode.Inherit ), + SRDescription( SR.ControlIMEModeDescr ) + ] + public ImeMode ImeMode { + get { + ImeMode imeMode = ImeModeBase; + + if (imeMode == ImeMode.OnHalf) // This is for compatibility. See QFE#4448. + { + imeMode = ImeMode.On; + } + + return imeMode; + } + set { + ImeModeBase = value; + } + } + + /// + /// Internal version of ImeMode property. This is provided for controls that override CanEnableIme and that + /// return ImeMode.Disable for the ImeMode property when CanEnableIme is false - See TextBoxBase controls. + /// + protected virtual ImeMode ImeModeBase { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_ImeModeBase(), this = " + this ); + Debug.Indent(); + + ImeMode imeMode = CachedImeMode; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + imeMode ); + Debug.Unindent(); + + return imeMode; + } + set { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside set_ImeModeBase({0}), this = {1}", value, this ) ); + Debug.Indent(); + + //valid values are -1 to 0xb + if( !ClientUtils.IsEnumValid( value, (int) value, (int) ImeMode.Inherit, (int) ImeMode.OnHalf ) ) { + throw new InvalidEnumArgumentException( "ImeMode", (int) value, typeof( ImeMode ) ); + } + + ImeMode oldImeMode = CachedImeMode; + CachedImeMode = value; + + if( oldImeMode != value ) { + // Cache current value to determine whether we need to raise the ImeModeChanged. + Control ctl = null; + + if( !DesignMode && ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { + // Set the context to the new value if control is focused. + if( Focused ) { + ctl = this; + } + else if( ContainsFocus ) { + ctl = FromChildHandleInternal( UnsafeNativeMethods.GetFocus() ); + } + + if( ctl != null && ctl.CanEnableIme ) { + // Block ImeModeChanged since we are checking for it below. + DisableImeModeChangedCount++; + + try { + ctl.UpdateImeContextMode(); + } + finally { + DisableImeModeChangedCount--; + } + } + } + + VerifyImeModeChanged( oldImeMode, CachedImeMode ); + } + + ImeContext.TraceImeStatus( this ); + Debug.Unindent(); + } + } + + /// + /// Determines whether the Control supports IME handling by default. + /// + private bool ImeSupported { + get { + return DefaultImeMode != ImeMode.Disable; + } + } + + /// + /// + /// [To be supplied.] + /// + [WinCategory( "Behavior" ), SRDescription( SR.ControlOnImeModeChangedDescr )] + public event EventHandler ImeModeChanged { + add { + Events.AddHandler( EventImeModeChanged, value ); + } + remove { + Events.RemoveHandler( EventImeModeChanged, value ); + } + } + + /// + /// Returns the current number of WM_CHAR messages to ignore after processing corresponding WM_IME_CHAR msgs. + /// + internal int ImeWmCharsToIgnore { + // The IME sends WM_IME_CHAR messages for each character in the composition string, and then + // after all messages are sent, corresponding WM_CHAR messages are also sent. (in non-unicode + // windows two WM_CHAR messages are sent per char in the IME). We need to keep a counter + // not to process each character twice or more. + // Marshal.SystemDefaultCharSize represents the default character size on the system; the default + // is 2 for Unicode systems and 1 for ANSI systems. This is how it is implemented in Control. + get { + return Properties.GetInteger( PropImeWmCharsToIgnore ); + } + set { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside set_ImeWmCharToIgnore(value={0}), this = {1}", value, this ) ); + Debug.Indent(); + + // WM_CHAR is not send after WM_IME_CHAR when the composition has been closed by either, changing the conversion mode or + // dissasociating the IME (for instance when loosing focus and conversion is forced to complete). + if( ImeWmCharsToIgnore != ImeCharsToIgnoreDisabled ) { + Properties.SetInteger( PropImeWmCharsToIgnore, value ); + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImeWmCharsToIgnore on leaving setter: " + ImeWmCharsToIgnore ); + Debug.Unindent(); + } + } + + /// + /// Gets the last value CanEnableIme property when it was last checked for ensuring IME context restriction mode. + /// This is used by controls that implement some sort of IME restriction mode (like TextBox on Password/ReadOnly mode). + /// See the VerifyImeRestrictedModeChanged() method. + /// + private bool LastCanEnableIme { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_LastCanEnableIme()" ); + Debug.Indent(); + + bool valueFound; + int val = (int) Properties.GetInteger( PropLastCanEnableIme, out valueFound ); + + if( valueFound ) { + valueFound = val == 1; + } + else { + valueFound = true; + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + valueFound ); + Debug.Unindent(); + + return valueFound; + } + set { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_LastCanEnableIme(): " + value ); + Properties.SetInteger( PropLastCanEnableIme, value ? 1 : 0 ); + } + } + + /// + /// Represents the internal ImeMode value for controls with ImeMode = ImeMode.NoControl. This property is changed + /// only by user interaction and is required to set the IME context appropriately while keeping the ImeMode property + /// unchanged. + /// + protected static ImeMode PropagatingImeMode { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_PropagatingImeMode()" ); + Debug.Indent(); + + if( Control.propagatingImeMode == ImeMode.Inherit ) { + // Initialize the propagating IME mode to the value the IME associated to the focused window currently has, + // this enables propagating the IME mode from/to unmanaged applications hosting Microsoft controls. + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Initializing PropagatingImeMode" ); + + ImeMode imeMode = ImeMode.Inherit; + IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); + + if( focusHandle != IntPtr.Zero ) { + imeMode = ImeContext.GetImeMode(focusHandle); + + // If focused control is disabled we won't be able to get the app ime context mode, try the top window. + // this is the case of a disabled Microsoft control hosted in a non-Form shell. + if( imeMode == ImeMode.Disable ) { + focusHandle = UnsafeNativeMethods.GetAncestor(new HandleRef(null, focusHandle), NativeMethods.GA_ROOT); + + if( focusHandle != IntPtr.Zero ) { + imeMode = ImeContext.GetImeMode(focusHandle); + } + } + } + + // If IME is disabled the PropagatingImeMode will not be initialized, see property setter below. + PropagatingImeMode = imeMode; + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Value: " + Control.propagatingImeMode ); + Debug.Unindent(); + + return Control.propagatingImeMode; + } + private set { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_PropagatingImeMode()" ); + Debug.Indent(); + + if (Control.propagatingImeMode != value) { + switch( value ) { + case ImeMode.NoControl: + case ImeMode.Disable: + // Cannot set propagating ImeMode to one of these values. + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Cannot change PropagatingImeMode to " + value); + return; + + default: + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, string.Format( CultureInfo.CurrentCulture, "Setting PropagatingImeMode: Current value = {0}, New value = {1}", propagatingImeMode, value ) ); + Control.propagatingImeMode = value; + break; + } + } + + Debug.Unindent(); + } + } + + /// + /// Sets the IME context to the appropriate ImeMode according to the control's ImeMode state. + /// This method is commonly used when attaching the IME to the control's window. + /// + internal void UpdateImeContextMode() { + ImeMode[] inputLanguageTable = ImeModeConversion.InputLanguageTable; + if (!DesignMode && (inputLanguageTable != ImeModeConversion.UnsupportedTable) && Focused) { + // Note: CHN IME won't send WM_IME_NOTIFY msg when getting associated, setting the IME context mode + // forces the message to be sent as a side effect. + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside UpdateImeContextMode(), this = " + this ); + Debug.Indent(); + + // If the value is not supported by the Ime, it will be mapped to a corresponding one, we need + // to update the cached ImeMode to the actual value. + + ImeMode newImeContextMode = ImeMode.Disable; + ImeMode currentImeMode = CachedImeMode; + + if( ImeSupported && CanEnableIme ) { + newImeContextMode = currentImeMode == ImeMode.NoControl ? PropagatingImeMode : currentImeMode; + } + + // If PropagatingImeMode has not been initialized it will return ImeMode.Inherit above, need to check newImeContextMode for this. + if (CurrentImeContextMode != newImeContextMode && newImeContextMode != ImeMode.Inherit) { + // If the context changes the window will receive one or more WM_IME_NOTIFY messages and as part of its + // processing it will raise the ImeModeChanged event if needed. We need to prevent the event from been + // raised here from here. + DisableImeModeChangedCount++; + + // Setting IME status to Disable will first close the IME and then disable it. For CHN IME, the first action will + // update the PropagatingImeMode to ImeMode.Close which is incorrect. We need to save the PropagatingImeMode in + // this case and restore it after the context has been changed. See VSW#530471 + // Also this call here is very important since it will initialize the PropagatingImeMode if not already initialized + // before setting the IME context to the control's ImeMode value which could be different from the propagating value. + ImeMode savedPropagatingImeMode = PropagatingImeMode; + + try { + ImeContext.SetImeStatus( newImeContextMode, this.Handle ); + } + finally { + DisableImeModeChangedCount--; + + if (newImeContextMode == ImeMode.Disable && inputLanguageTable == ImeModeConversion.ChineseTable) { + // Restore saved propagating mode. + PropagatingImeMode = savedPropagatingImeMode; + } + } + + // Get mapped value from the context. + if( currentImeMode == ImeMode.NoControl ) { + if( CanEnableIme ) { + PropagatingImeMode = CurrentImeContextMode; + } + } + else { + if( CanEnableIme ) { + CachedImeMode = CurrentImeContextMode; + } + + // Need to raise the ImeModeChanged event? + VerifyImeModeChanged( newImeContextMode, CachedImeMode ); + } + } + + ImeContext.TraceImeStatus( this ); + Debug.Unindent(); + } + } + + /// + /// Checks if specified ImeMode values are different and raise the event if true. + /// + private void VerifyImeModeChanged( ImeMode oldMode, ImeMode newMode ) { + if( ImeSupported && (DisableImeModeChangedCount == 0) && (newMode != ImeMode.NoControl) ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside VerifyImeModeChanged(oldMode={0}, newMode={1}), this = {2}", oldMode, newMode, this ) ); + Debug.Indent(); + + if( oldMode != newMode ) { + OnImeModeChanged( EventArgs.Empty ); + } + + Debug.Unindent(); + } + } + + /// + /// Verifies whether the IME context mode is correct based on the control's Ime restriction mode (CanEnableIme) + /// and updates the IME context if needed. + /// + internal void VerifyImeRestrictedModeChanged() { + Debug.Assert( ImeSupported, "This method should not be called from controls that don't support IME input." ); + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside VerifyImeRestrictedModeChanged(), this = " + this ); + Debug.Indent(); + + bool currentCanEnableIme = this.CanEnableIme; + + if( LastCanEnableIme != currentCanEnableIme ) { + if( Focused ) { + // Disable ImeModeChanged from the following call since we'll raise it here if needed. + DisableImeModeChangedCount++; + try { + UpdateImeContextMode(); + } + finally { + DisableImeModeChangedCount--; + } + } + + // Assume for a moment the control is getting restricted; + ImeMode oldImeMode = CachedImeMode; + ImeMode newImeMode = ImeMode.Disable; + + if( currentCanEnableIme ) { + // Control is actually getting unrestricted, swap values. + newImeMode = oldImeMode; + oldImeMode = ImeMode.Disable; + } + + // Do we need to raise the ImeModeChanged event? + VerifyImeModeChanged( oldImeMode, newImeMode ); + + // Finally update the saved CanEnableIme value. + LastCanEnableIme = currentCanEnableIme; + } + + Debug.Unindent(); + } + + /// + /// Update internal ImeMode properties (PropagatingImeMode/CachedImeMode) with actual IME context mode if needed. + /// This method can be used with a child control when the IME mode is more relevant to it than to the control itself, + /// for instance ComboBox and its native ListBox/Edit controls. + /// + internal void OnImeContextStatusChanged( IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside OnImeContextStatusChanged(), this = " + this ); + Debug.Indent(); + + Debug.Assert( ImeSupported, "WARNING: Attempting to update ImeMode properties on IME-Unaware control!" ); + Debug.Assert( !DesignMode, "Shouldn't be updating cached ime mode at design-time" ); + + ImeMode fromContext = ImeContext.GetImeMode( handle ); + + if( fromContext != ImeMode.Inherit ) { + ImeMode oldImeMode = CachedImeMode; + + if( CanEnableIme ) { // Cache or Propagating ImeMode should not be updated by interaction when the control is in restricted mode. + if( oldImeMode != ImeMode.NoControl ) { + CachedImeMode = fromContext; // This could end up in the same value due to ImeMode language mapping. + + // ImeMode may be changing by user interaction. + VerifyImeModeChanged( oldImeMode, CachedImeMode ); + } + else { + PropagatingImeMode = fromContext; + } + } + } + + Debug.Unindent(); + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnImeModeChanged( EventArgs e ) { + Debug.Assert( ImeSupported, "ImeModeChanged should not be raised on an Ime-Unaware control." ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside OnImeModeChanged(), this = " + this ); + EventHandler handler = (EventHandler) Events[EventImeModeChanged]; + if( handler != null ) handler( this, e ); + } + + /// + /// Resets the Ime mode. + /// + [EditorBrowsable( EditorBrowsableState.Never )] + public void ResetImeMode() { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ResetImeMode(), this = " + this ); + ImeMode = DefaultImeMode; + } + + /// + /// Returns true if the ImeMode should be persisted in code gen. + /// + [EditorBrowsable( EditorBrowsableState.Never )] + internal virtual bool ShouldSerializeImeMode() { + // This method is for designer support. If the ImeMode has not been changed or it is the same as the + // default value it should not be serialized. + bool found; + int imeMode = Properties.GetInteger( PropImeMode, out found ); + + return ( found && imeMode != (int) DefaultImeMode ); + } + + /// + /// Handles the WM_INPUTLANGCHANGE message + /// + /// + private void WmInputLangChange( ref Message m ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmInputLangChange(), this = " + this ); + Debug.Indent(); + + // Make sure the IME context is associated with the correct (mapped) mode. + UpdateImeContextMode(); + + // If detaching IME (setting to English) reset propagating IME mode so when reattaching the IME is set to direct input again. + if( ImeModeConversion.InputLanguageTable == ImeModeConversion.UnsupportedTable ) { + PropagatingImeMode = ImeMode.Off; + } + + if( LocalAppContextSwitches.EnableLegacyChineseIMEIndicator && ImeModeConversion.InputLanguageTable == ImeModeConversion.ChineseTable ) { + IgnoreWmImeNotify = false; + } + + Form form = FindFormInternal(); + + if( form != null ) { + InputLanguageChangedEventArgs e = InputLanguage.CreateInputLanguageChangedEventArgs( m ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Culture=" + e.Culture ); + form.PerformOnInputLanguageChanged( e ); + } + + DefWndProc( ref m ); + + ImeContext.TraceImeStatus( this ); + Debug.Unindent(); + } + + + /// + /// Handles the WM_INPUTLANGCHANGEREQUEST message + /// + /// + private void WmInputLangChangeRequest( ref Message m ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmInputLangChangeRequest(), this=" + this ); + Debug.Indent(); + + InputLanguageChangingEventArgs e = InputLanguage.CreateInputLanguageChangingEventArgs( m ); + Form form = FindFormInternal(); + + if( form != null ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Culture=" + e.Culture ); + form.PerformOnInputLanguageChanging( e ); + } + + if( !e.Cancel ) { + DefWndProc( ref m ); + } + else { + m.Result = IntPtr.Zero; + } + + Debug.Unindent(); + } + + /// + /// Handles the WM_IME_CHAR message + /// + private void WmImeChar( ref Message m ) { + if( ProcessKeyEventArgs( ref m ) ) { + return; + } + DefWndProc( ref m ); + } + + /// + /// Handles the WM_IME_ENDCOMPOSITION message + /// + private void WmImeEndComposition( ref Message m ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeEndComposition() - Disabling ImeWmCharToIgnore, this=" + this ); + this.ImeWmCharsToIgnore = ImeCharsToIgnoreDisabled; + DefWndProc( ref m ); + } + + /// + /// Handles the WM_IME_NOTIFY message + /// + private void WmImeNotify( ref Message m ) { + + ImeMode[] inputLanguageTable = ImeModeConversion.InputLanguageTable; + + // During a change to the Chinese language with Focus already set, the Chinese IME will send several WmImeNotify messages + // before ever sending a WmInputLangChange event. Also, the IME will report an IME input context during this time that we + // interpret as On = 'OnHalf'. The combination of these causes us to update the default Cached ImeMode to OnHalf, overriding + // the control's ImeMode property -- unwanted behavior. We workaround this by skipping our mode synchronization during these + // IMENotify messages until we get a WmInputLangChange event. + // + // If this is the first time here after conversion to chinese language, wait for WmInputLanguageChange + // before listening to WmImeNotifys. + if ( LocalAppContextSwitches.EnableLegacyChineseIMEIndicator && (inputLanguageTable == ImeModeConversion.ChineseTable) && !lastLanguageChinese) { + IgnoreWmImeNotify = true; + } + + lastLanguageChinese = (inputLanguageTable == ImeModeConversion.ChineseTable); + + if( ImeSupported && inputLanguageTable != ImeModeConversion.UnsupportedTable && !IgnoreWmImeNotify) { + int wparam = (int) m.WParam; + + // The WM_IME_NOTIFY message is not consistent across the different IMEs, particularly the notification type + // we care about (IMN_SETCONVERSIONMODE & IMN_SETOPENSTATUS). + // The IMN_SETOPENSTATUS command is sent when the open status of the input context is updated. + // The IMN_SETCONVERSIONMODE command is sent when the conversion mode of the input context is updated. + // - The Korean IME sents both msg notifications when changing the conversion mode (From/To Hangul/Alpha). + // - The Chinese IMEs sends the IMN_SETCONVERSIONMODE when changing mode (On/Close, Full Shape/Half Shape) + // and IMN_SETOPENSTATUS when getting disabled/enabled or closing/opening as well, but it does not send any + // WM_IME_NOTIFY when associating an IME to the app for the first time; setting the IME mode to direct input + // during WM_INPUTLANGCHANGED forces the IMN_SETOPENSTATUS message to be sent. + // - The Japanese IME sends IMN_SETCONVERSIONMODE when changing from Off to one of the active modes (Katakana..) + // and IMN_SETOPENSTATUS when changing beteween the active modes or when enabling/disabling the IME. + // In any case we update the cache. + // Warning: + // Attempting to change the IME mode from here will cause re-entrancy - WM_IME_NOTIFY is resent. + // We guard against re-entrancy since the ImeModeChanged event can be raised and any changes from the handler could + // lead to another WM_IME_NOTIFY loop. + + if( wparam == NativeMethods.IMN_SETCONVERSIONMODE || wparam == NativeMethods.IMN_SETOPENSTATUS ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside WmImeNotify(m.wparam=[{0}]), this={1}", m.WParam, this ) ); + Debug.Indent(); + + // Synchronize internal properties with the IME context mode. + OnImeContextStatusChanged( this.Handle ); + + Debug.Unindent(); + } + } + + DefWndProc( ref m ); + } + + /// + /// Handles the WM_SETFOCUS message for IME related stuff. + /// + internal void WmImeSetFocus() { + if (ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable) { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeSetFocus(), this=" + this); + Debug.Indent(); + + // Make sure the IME context is set to the correct value. + // Consider - Perf improvement: ContainerControl controls should update the IME context only when they don't contain + // a focusable control since it will be updated by that control. + UpdateImeContextMode(); + + Debug.Unindent(); + } + } + + /// + /// Handles the WM_IME_STARTCOMPOSITION message + /// + private void WmImeStartComposition( ref Message m ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeStartComposition() - Enabling ImeWmCharToIgnore, this=" + this ); + + // Need to call the property store directly because the WmImeCharsToIgnore property is locked when ImeCharsToIgnoreDisabled. + Properties.SetInteger( PropImeWmCharsToIgnore, ImeCharsToIgnoreEnabled ); + DefWndProc( ref m ); + } + + /// + /// Handles the WM_KILLFOCUS message + /// + /// + private void WmImeKillFocus() { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeKillFocus(), this=" + this); + Debug.Indent(); + + Control topMostWinformsParent = TopMostParent; + Form appForm = topMostWinformsParent as Form; + + if( (appForm == null || appForm.Modal) && !topMostWinformsParent.ContainsFocus ) { + // This means the Microsoft component container is not a Microsoft host and it is no longer focused. + // Or it is not the main app host. See VSW#531520,604741 (VSU QFE#5055), DDB#95889, TFS VSTSDEVDIV463760 + + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Unfocused TopMostParent = " + topMostWinformsParent); + + // We need to reset the PropagatingImeMode to force reinitialization when the Microsoft component gets focused again; + // this enables inheritting the propagating mode from an unmanaged application hosting a Microsoft component. + // But before leaving the Microsoft container we need to set the IME to the propagating IME mode since the focused control + // may not support IME which would leave the IME disabled. + // See the PropagatingImeMode property and VSW#531520. + + // Note: We need to check the static field here directly to avoid initialization of the property. + if (Control.propagatingImeMode != ImeMode.Inherit) { + // Setting the ime context of the top window will generate a WM_IME_NOTIFY on the focused control which will + // update its ImeMode, we need to prevent this temporarily. + IgnoreWmImeNotify = true; + + try { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Setting IME context to PropagatingImeMode (leaving Microsoft container). this = " + this); + ImeContext.SetImeStatus(PropagatingImeMode, topMostWinformsParent.Handle); + + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Resetting PropagatingImeMode. this = " + this); + PropagatingImeMode = ImeMode.Inherit; + } + finally { + IgnoreWmImeNotify = false; + } + } + } + + + Debug.Unindent(); + } + } // end class Control + + + ///////////////////////////////////////////////////////// ImeContext class ///////////////////////////////////////////////////////// + + /// + /// Represents the native IME context. + /// + public static class ImeContext { + /// + /// The IME context handle obtained when first associating an IME. + /// + private static IntPtr originalImeContext; + + /// + /// Disable the IME + /// + public static void Disable( IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.Disable(" + handle + ")" ); + Debug.Indent(); + + if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { + // Close the IME if necessary + // + if( ImeContext.IsOpen( handle ) ) { + ImeContext.SetOpenStatus( false, handle ); + } + + // Disable the IME by disassociating the context from the window. + // + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext(" + handle + ", null)" ); + IntPtr oldContext = UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), NativeMethods.NullHandleRef ); + if( oldContext != IntPtr.Zero ) { + originalImeContext = oldContext; + } + } + + ImeContext.TraceImeStatus( handle ); + Debug.Unindent(); + } + + /// + /// Enable the IME + /// + public static void Enable( IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.Enable(" + handle + ")" ); + Debug.Indent(); + + if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); + IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); + + // Enable IME by associating the IME context to the window. + if( inputContext == IntPtr.Zero ) { + if( originalImeContext == IntPtr.Zero ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmCreateContext()" ); + inputContext = UnsafeNativeMethods.ImmCreateContext(); + if( inputContext != IntPtr.Zero ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext(" + handle + ", " + inputContext + ")" ); + UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + } + } + else { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext()" ); + UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), new HandleRef( null, originalImeContext ) ); + } + } + else { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); + UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + } + + // Make sure the IME is opened. + if( !ImeContext.IsOpen( handle ) ) { + ImeContext.SetOpenStatus( true, handle ); + } + } + + ImeContext.TraceImeStatus( handle ); + Debug.Unindent(); + } + + /// + /// Gets the ImeMode that corresponds to ImeMode.Disable based on the current input language ImeMode table. + /// + public static ImeMode GetImeMode( IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Insise ImeContext.GetImeMode(" + handle + ")" ); + Debug.Indent(); + + IntPtr inputContext = IntPtr.Zero; + ImeMode retval = ImeMode.NoControl; + + // Get the right table for the current keyboard layout + // + ImeMode[] countryTable = ImeModeConversion.InputLanguageTable; + if( countryTable == ImeModeConversion.UnsupportedTable ) { + // No IME associated with current culture. + retval = ImeMode.Inherit; + goto cleanup; + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); + inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); + + if( inputContext == IntPtr.Zero ) { + // No IME context attached - The Ime has been disabled. + retval = ImeMode.Disable; + goto cleanup; + } + + if( !ImeContext.IsOpen( handle ) ) { + // There's an IME associated with the window but is closed - the input is taken from the keyboard as is (English). + retval = countryTable[ImeModeConversion.ImeClosed]; + goto cleanup; + } + + // Determine the IME mode from the conversion status + // + + int conversion = 0; // Combination of conversion mode values + int sentence = 0; // Sentence mode value + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetConversionStatus(" + inputContext + ", conversion, sentence)" ); + UnsafeNativeMethods.ImmGetConversionStatus( new HandleRef( null, inputContext ), ref conversion, ref sentence ); + + Debug.Assert( countryTable != null, "countryTable is null" ); + + if( ( conversion & NativeMethods.IME_CMODE_NATIVE ) != 0 ) { + if( ( conversion & NativeMethods.IME_CMODE_KATAKANA ) != 0 ) { + retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) + ? countryTable[ImeModeConversion.ImeNativeFullKatakana] + : countryTable[ImeModeConversion.ImeNativeHalfKatakana]; + goto cleanup; + } + else { // ! Katakana + retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) + ? countryTable[ImeModeConversion.ImeNativeFullHiragana] + : countryTable[ImeModeConversion.ImeNativeHalfHiragana]; + goto cleanup; + } + } + else { // ! IME_CMODE_NATIVE + retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) + ? countryTable[ImeModeConversion.ImeAlphaFull] + : countryTable[ImeModeConversion.ImeAlphaHalf]; + } + + cleanup: + if( inputContext != IntPtr.Zero ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); + UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + } + + ImeContext.TraceImeStatus( handle ); + + Debug.Unindent(); + return retval; + } + + /// + /// Get the actual IME status - This method is for debugging purposes only. + /// + [Conditional( "DEBUG" )] + internal static void TraceImeStatus( Control ctl ) { +#if DEBUG + if ( ctl.IsHandleCreated ){ + TraceImeStatus( ctl.Handle ); + } +#endif + } + + [Conditional( "DEBUG" )] + private static void TraceImeStatus( IntPtr handle ) { +#if DEBUG + if (CompModSwitches.ImeMode.Level >= TraceLevel.Info) { + string status = "?"; + IntPtr inputContext = IntPtr.Zero; + ImeMode[] countryTable = ImeModeConversion.InputLanguageTable; + + if (countryTable == ImeModeConversion.UnsupportedTable) { + status = "IME not supported in current language."; + goto cleanup; + } + + inputContext = UnsafeNativeMethods.ImmGetContext(new HandleRef(null, handle)); + + if (inputContext == IntPtr.Zero) { + status = string.Format(CultureInfo.CurrentCulture, "No ime context for handle=[{0}]", handle); + goto cleanup; + } + + if (!UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(null, inputContext))) { + status = string.Format(CultureInfo.CurrentCulture, "Ime closed for handle=[{0}]", handle); + goto cleanup; + } + + int conversion = 0; // Combination of conversion mode values + int sentence = 0; // Sentence mode value + + UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(null, inputContext), ref conversion, ref sentence); + ImeMode retval; + + if ((conversion & NativeMethods.IME_CMODE_NATIVE) != 0) { + if ((conversion & NativeMethods.IME_CMODE_KATAKANA) != 0) { + retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) + ? countryTable[ImeModeConversion.ImeNativeFullKatakana] + : countryTable[ImeModeConversion.ImeNativeHalfKatakana]; + } + else { // ! Katakana + retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) + ? countryTable[ImeModeConversion.ImeNativeFullHiragana] + : countryTable[ImeModeConversion.ImeNativeHalfHiragana]; + } + } + else { // ! IME_CMODE_NATIVE + retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) + ? countryTable[ImeModeConversion.ImeAlphaFull] + : countryTable[ImeModeConversion.ImeAlphaHalf]; + } + + status = string.Format(CultureInfo.CurrentCulture, "Ime conversion status mode for handle=[{0}]: {1}", handle, retval); + + cleanup: + if (inputContext != IntPtr.Zero) { + UnsafeNativeMethods.ImmReleaseContext(new HandleRef(null, handle), new HandleRef(null, inputContext)); + } + + Debug.WriteLine(status); + } +#endif + } + + /// + /// Returns true if the IME is currently open + /// + public static bool IsOpen( IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.IsOpen(" + handle + ")" ); + Debug.Indent(); + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); + IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); + + bool retval = false; + + if( inputContext != IntPtr.Zero ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetOpenStatus(" + inputContext + ")" ); + retval = UnsafeNativeMethods.ImmGetOpenStatus( new HandleRef( null, inputContext ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); + UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, " IsOpen = " + retval ); + Debug.Unindent(); + + return retval; + } + + /// + /// Sets the actual IME context value. + /// + public static void SetImeStatus( ImeMode imeMode, IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside ImeContext.SetImeStatus(ImeMode=[{0}, handle=[{1}])", imeMode, handle ) ); + Debug.Indent(); + + Debug.Assert( imeMode != ImeMode.Inherit, "ImeMode.Inherit is an invalid argument to ImeContext.SetImeStatus" ); + + if( imeMode == ImeMode.Inherit || imeMode == ImeMode.NoControl ) { + goto cleanup; // No action required + } + + ImeMode[] inputLanguageTable = ImeModeConversion.InputLanguageTable; + + if (inputLanguageTable == ImeModeConversion.UnsupportedTable) { + goto cleanup; // We only support Japanese, Korean and Chinese IME. + } + + int conversion = 0; // Combination of conversion mode values + int sentence = 0; // Sentence mode value + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, "\tSetting IME context to " + imeMode ); + + if( imeMode == ImeMode.Disable ) { + ImeContext.Disable( handle ); + } + else { + // This will make sure the IME is opened. + ImeContext.Enable( handle ); + } + + switch( imeMode ) { + case ImeMode.NoControl: + case ImeMode.Disable: + break; // No action required + + case ImeMode.On: + // IME active mode (CHN = On, JPN = Hiragana, KOR = Hangul). + // Setting ImeMode to Hiragana (or any other active value) will force the IME to get to an active value + // independent on the language. + imeMode = ImeMode.Hiragana; + goto default; + + case ImeMode.Off: + // IME direct input (CHN = Off, JPN = Off, KOR = Alpha). + if (inputLanguageTable == ImeModeConversion.JapaneseTable) { + // Japanese IME interprets Close as Off. + goto case ImeMode.Close; + } + + // CHN: to differentiate between Close and Off we set the ImeMode to Alpha. + imeMode = ImeMode.Alpha; + goto default; + + case ImeMode.Close: + if (inputLanguageTable == ImeModeConversion.KoreanTable) { + // Korean IME has no idea what Close means. + imeMode = ImeMode.Alpha; + goto default; + } + + ImeContext.SetOpenStatus( false, handle ); + break; + + default: + if( ImeModeConversion.ImeModeConversionBits.ContainsKey( imeMode ) ) { + + // Update the conversion status + // + ImeModeConversion conversionEntry = (ImeModeConversion) ImeModeConversion.ImeModeConversionBits[imeMode]; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); + IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetConversionStatus(" + inputContext + ", conversion, sentence)" ); + UnsafeNativeMethods.ImmGetConversionStatus( new HandleRef( null, inputContext ), ref conversion, ref sentence ); + + conversion |= conversionEntry.setBits; + conversion &= ~conversionEntry.clearBits; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmSetConversionStatus(" + inputContext + ", conversion, sentence)" ); + bool retval = UnsafeNativeMethods.ImmSetConversionStatus( new HandleRef( null, inputContext ), conversion, sentence ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); + UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + } + break; + } + + cleanup: + ImeContext.TraceImeStatus( handle ); + Debug.Unindent(); + } + + /// + /// Opens or closes the IME context. + /// + public static void SetOpenStatus( bool open, IntPtr handle ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside SetOpenStatus(open=[{0}], handle=[{1}])", open, handle ) ); + Debug.Indent(); + + if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); + IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); + + if( inputContext != IntPtr.Zero ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmSetOpenStatus(" + inputContext + ", " + open + ")" ); + bool succeeded = UnsafeNativeMethods.ImmSetOpenStatus( new HandleRef( null, inputContext ), open ); + Debug.Assert( succeeded, "Could not set the IME open status." ); + + if( succeeded ) { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); + succeeded = UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); + Debug.Assert( succeeded, "Could not release IME context." ); + } + } + } + + ImeContext.TraceImeStatus( handle ); + Debug.Unindent(); + } + }// end ImeContext class + + + ///////////////////////////////////////////////////////// ImeModeConversion structure ///////////////////////////////////////////////////////// + + /// + /// Helper class that provides information about IME convertion mode. Convertion mode refers to how IME interprets input like + /// ALPHANUMERIC or HIRAGANA and depending on its value the IME enables/disables the IME convertion window appropriately. + /// + [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // this class has no public instance memebers. + public struct ImeModeConversion + { + private static Dictionary imeModeConversionBits; + + internal int setBits; + internal int clearBits; + + // Tables of conversions from IME context bits to IME mode + // + //internal const int ImeNotAvailable = 0; + internal const int ImeDisabled = 1; + internal const int ImeDirectInput = 2; + internal const int ImeClosed = 3; + internal const int ImeNativeInput = 4; + internal const int ImeNativeFullHiragana = 4; // Index of Native Input Mode. + internal const int ImeNativeHalfHiragana = 5; + internal const int ImeNativeFullKatakana = 6; + internal const int ImeNativeHalfKatakana = 7; + internal const int ImeAlphaFull = 8; + internal const int ImeAlphaHalf = 9; + + /// + /// Supported input language ImeMode tables. + /// WARNING: Do not try to map 'active' IME modes from one table to another since they can have a different + /// meaning depending on the language; for instance ImeMode.Off means 'disable' or 'alpha' to Chinese + /// but to Japanese it is 'alpha' and to Korean it has no meaning. + /// + private static ImeMode[] japaneseTable = { + ImeMode.Inherit, + ImeMode.Disable, + ImeMode.Off, + ImeMode.Off, + ImeMode.Hiragana, + ImeMode.Hiragana, + ImeMode.Katakana, + ImeMode.KatakanaHalf, + ImeMode.AlphaFull, + ImeMode.Alpha + }; + + private static ImeMode[] koreanTable = { + ImeMode.Inherit, + ImeMode.Disable, + ImeMode.Alpha, + ImeMode.Alpha, + ImeMode.HangulFull, + ImeMode.Hangul, + ImeMode.HangulFull, + ImeMode.Hangul, + ImeMode.AlphaFull, + ImeMode.Alpha + }; + + private static ImeMode[] chineseTable = { + ImeMode.Inherit, + ImeMode.Disable, + ImeMode.Off, + ImeMode.Close, + ImeMode.On, + ImeMode.OnHalf, + ImeMode.On, + ImeMode.OnHalf, + ImeMode.Off, + ImeMode.Off + }; + + private static ImeMode[] unsupportedTable = { + }; + + + internal static ImeMode[] ChineseTable { + get { + return chineseTable; + } + } + + internal static ImeMode[] JapaneseTable { + get { + return japaneseTable; + } + } + + internal static ImeMode[] KoreanTable { + get { + return koreanTable; + } + } + + internal static ImeMode[] UnsupportedTable { + get { + return unsupportedTable; + } + } + + /// + /// Gets the ImeMode table of the current input language. + /// Although this property is per-thread based we cannot cache it and share it among controls running in the same thread + /// for two main reasons: we still have some controls that don't handle IME properly (TabControl, ComboBox, TreeView...) + /// and would render it invalid and since the IME API is not public third party controls would not have a way to update + /// the cached value. See VSW#541650/541728. + /// + internal static ImeMode[] InputLanguageTable { + get { + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_InputLanguageTable(), Input Language = " + InputLanguage.CurrentInputLanguage.Culture.DisplayName + ", Thread = " + System.Threading.Thread.CurrentThread.ManagedThreadId); + InputLanguage inputLanguage = InputLanguage.CurrentInputLanguage; + + int lcid = (int)((long)inputLanguage.Handle & (long)0xFFFF); + + switch (lcid) { + case 0x0404: // Chinese (----) + case 0x0804: // Chinese (PRC) + case 0x0c04: // Chinese (Hong Kong SAR, PRC) + case 0x1004: // Chinese (Singapore) + case 0x1404: // Chinese (Macau SAR) + return chineseTable; + + case 0x0412: // Korean + case 0x0812: // Korean (Johab) + return koreanTable; + + case 0x0411: // Japanese + return japaneseTable; + + default: + return unsupportedTable; + } + } + } + + /// + /// Dictionary of ImeMode and corresponding convertion flags. + /// + public static Dictionary ImeModeConversionBits { + get { + if( imeModeConversionBits == null ) { + + // Create ImeModeConversionBits dictionary + imeModeConversionBits = new Dictionary( 7 ); + ImeModeConversion conversion; + + // Hiragana, On + // + conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA; + imeModeConversionBits.Add( ImeMode.Hiragana, conversion ); + + // Katakana + // + conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = 0; + imeModeConversionBits.Add( ImeMode.Katakana, conversion ); + + // KatakanaHalf + // + conversion.setBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE; + imeModeConversionBits.Add( ImeMode.KatakanaHalf, conversion ); + + // AlphaFull + // + conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE; + conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; + imeModeConversionBits.Add( ImeMode.AlphaFull, conversion ); + + // Alpha + // + conversion.setBits = 0; + conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; + imeModeConversionBits.Add( ImeMode.Alpha, conversion ); + + // HangulFull + // + conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = 0; + imeModeConversionBits.Add( ImeMode.HangulFull, conversion ); + + // Hangul + // + conversion.setBits = NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE; + imeModeConversionBits.Add( ImeMode.Hangul, conversion ); + + // OnHalf + // + conversion.setBits = NativeMethods.IME_CMODE_NATIVE; + conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_FULLSHAPE; + imeModeConversionBits.Add(ImeMode.OnHalf, conversion); + } + + return imeModeConversionBits; + } + } + + public static bool IsCurrentConversionTableSupported{ + get { + return InputLanguageTable != UnsupportedTable; + } + } + }// end ImeModeConversion struct. +} // end namespace System.Windows.Forms diff --git a/WindowsForms/Managed/System/WinForms/Control.cs b/WindowsForms/Managed/System/WinForms/Control.cs new file mode 100644 index 000000000..4af6aea07 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Control.cs @@ -0,0 +1,20350 @@ +#define DEBUG_PREFERREDSIZE + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.Control+ActiveXFontMarshaler..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control, System.Windows.Forms.Message):System.Windows.Forms.PreProcessControlState")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.OnPreviewKeyDown(System.Windows.Forms.PreviewKeyDownEventArgs):System.Void")] + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Configuration.Assemblies; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Globalization; + using System.Security.Permissions; + using System.Security; + using System.IO; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.Remoting; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Serialization.Formatters.Binary; + using System.Text; + using System.Threading; + using System.Windows.Forms.Design; + using System.Windows.Forms.Internal; + using Encoding = System.Text.Encoding; + using System.Drawing.Imaging; + using System.Windows.Forms.Layout; + using System.Runtime.Versioning; + using Automation; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using Collections.Generic; + + /// + /// + /// Defines the base class for controls, which are components + /// with visual representation. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Text"), + DefaultEvent("Click"), + Designer("System.Windows.Forms.Design.ControlDesigner, " + AssemblyRef.SystemDesign), + DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign), + ToolboxItemFilter("System.Windows.Forms") + ] + public partial class Control : + Component, + UnsafeNativeMethods.IOleControl, + UnsafeNativeMethods.IOleObject, + UnsafeNativeMethods.IOleInPlaceObject, + UnsafeNativeMethods.IOleInPlaceActiveObject, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.IViewObject, + UnsafeNativeMethods.IViewObject2, + UnsafeNativeMethods.IPersist, + UnsafeNativeMethods.IPersistStreamInit, + UnsafeNativeMethods.IPersistPropertyBag, + UnsafeNativeMethods.IPersistStorage, + UnsafeNativeMethods.IQuickActivate, + ISupportOleDropSource, + IDropTarget, + ISynchronizeInvoke, + IWin32Window, + IArrangedElement, + IBindableComponent, + IKeyboardToolTip { + +#if FINALIZATION_WATCH + static readonly TraceSwitch ControlFinalization = new TraceSwitch("ControlFinalization", "Tracks the creation and destruction of finalization"); + internal static string GetAllocationStack() { + if (ControlFinalization.TraceVerbose) { + // SECREVIEW: This assert is safe, the operation is safe (data obtained from the CLR). This code is for debugging purposes only. + // Tracked by VSWhidbey #426446. + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + return Environment.StackTrace; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + else { + return "Enable 'ControlFinalization' switch to see stack of allocation"; + } + + } + private string allocationSite = Control.GetAllocationStack(); +#endif + +#if DEBUG + internal static readonly TraceSwitch PaletteTracing = new TraceSwitch("PaletteTracing", "Debug Palette code"); + internal static readonly TraceSwitch ControlKeyboardRouting = new TraceSwitch("ControlKeyboardRouting", "Debug Keyboard routing for controls"); + internal static readonly TraceSwitch FocusTracing = new TraceSwitch("FocusTracing", "Debug focus/active control/enter/leave"); + private static readonly BooleanSwitch AssertOnControlCreateSwitch = new BooleanSwitch("AssertOnControlCreate", "Assert when anything directly deriving from control is created."); + internal static readonly BooleanSwitch TraceMnemonicProcessing = new BooleanSwitch("TraceCanProcessMnemonic", "Trace mnemonic processing calls to assure right child-parent call ordering."); + + internal void TraceCanProcessMnemonic(){ + if( TraceMnemonicProcessing.Enabled ){ + string str; + try { + str = string.Format( CultureInfo.CurrentCulture, "{0}<{1}>", this.GetType().Name, this.Text ); + int maxFrameCount = new StackTrace().FrameCount; + if (maxFrameCount > 5) { + maxFrameCount = 5; + } + int frameIndex = 1; + while( frameIndex < maxFrameCount) { + StackFrame sf = new StackFrame(frameIndex); + if( frameIndex == 2 && sf.GetMethod().Name.Equals("CanProcessMnemonic") ){ // log immediate call if in a virtual/recursive call. + break; + } + str += new StackTrace(sf).ToString().TrimEnd(); + frameIndex++; + } + if( frameIndex > 2 ){ // new CanProcessMnemonic virtual/recursive call stack. + str = "\r\n" + str; + } + } + catch (Exception ex) { + str = ex.ToString(); + } + Debug.WriteLine( str ); + } + } +#else + internal static readonly TraceSwitch ControlKeyboardRouting; + internal static readonly TraceSwitch PaletteTracing; + internal static readonly TraceSwitch FocusTracing; +#endif + +#if DEBUG + internal static readonly BooleanSwitch BufferPinkRect = new BooleanSwitch("BufferPinkRect", "Renders a pink rectangle with painting double buffered controls"); + internal static readonly BooleanSwitch BufferDisabled = new BooleanSwitch("BufferDisabled", "Makes double buffered controls non-double buffered"); +#else + internal static readonly BooleanSwitch BufferPinkRect; +#endif + + private static int WM_GETCONTROLNAME; + private static int WM_GETCONTROLTYPE; + + static Control() { + WM_GETCONTROLNAME = SafeNativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME"); + WM_GETCONTROLTYPE = SafeNativeMethods.RegisterWindowMessage("WM_GETCONTROLTYPE"); + + } + + internal const int STATE_CREATED = 0x00000001; + internal const int STATE_VISIBLE = 0x00000002; + internal const int STATE_ENABLED = 0x00000004; + internal const int STATE_TABSTOP = 0x00000008; + internal const int STATE_RECREATE = 0x00000010; + internal const int STATE_MODAL = 0x00000020; + internal const int STATE_ALLOWDROP = 0x00000040; + internal const int STATE_DROPTARGET = 0x00000080; + internal const int STATE_NOZORDER = 0x00000100; + internal const int STATE_LAYOUTDEFERRED = 0x00000200; + internal const int STATE_USEWAITCURSOR = 0x00000400; + internal const int STATE_DISPOSED = 0x00000800; + internal const int STATE_DISPOSING = 0x00001000; + internal const int STATE_MOUSEENTERPENDING = 0x00002000; + internal const int STATE_TRACKINGMOUSEEVENT = 0x00004000; + internal const int STATE_THREADMARSHALLPENDING = 0x00008000; + internal const int STATE_SIZELOCKEDBYOS = 0x00010000; + internal const int STATE_CAUSESVALIDATION = 0x00020000; + internal const int STATE_CREATINGHANDLE = 0x00040000; + internal const int STATE_TOPLEVEL = 0x00080000; + internal const int STATE_ISACCESSIBLE = 0x00100000; + internal const int STATE_OWNCTLBRUSH = 0x00200000; + internal const int STATE_EXCEPTIONWHILEPAINTING = 0x00400000; + internal const int STATE_LAYOUTISDIRTY = 0x00800000; + internal const int STATE_CHECKEDHOST = 0x01000000; + internal const int STATE_HOSTEDINDIALOG = 0x02000000; + internal const int STATE_DOUBLECLICKFIRED = 0x04000000; + internal const int STATE_MOUSEPRESSED = 0x08000000; + internal const int STATE_VALIDATIONCANCELLED = 0x10000000; + internal const int STATE_PARENTRECREATING = 0x20000000; + internal const int STATE_MIRRORED = 0x40000000; + + // HACK HACK HACK - when we change RightToLeft, we need to change the scrollbar thumb. + // We can't do that until after the control has been created, and all the items added + // back. This is because the system control won't know the nMin and nMax of the scroll + // bar until the items are added. So in RightToLeftChanged, we set a flag that indicates + // that we want to set the scroll position. In OnHandleCreated we check this flag, + // and if set, we BeginInvoke. We have to BeginInvoke since we have to wait until the items + // are added. We only want to do this when RightToLeft changes thus the flags + // STATE2_HAVEINVOKED and STATE2_SETSCROLLPOS. Otherwise we would do this on each HandleCreated. + private const int STATE2_HAVEINVOKED = 0x00000001; + private const int STATE2_SETSCROLLPOS = 0x00000002; + private const int STATE2_LISTENINGTOUSERPREFERENCECHANGED = 0x00000004; // set when the control is listening to SystemEvents.UserPreferenceChanged. + internal const int STATE2_INTERESTEDINUSERPREFERENCECHANGED = 0x00000008; // if set, the control will listen to SystemEvents.UserPreferenceChanged when TopLevel is true and handle is created. + internal const int STATE2_MAINTAINSOWNCAPTUREMODE = 0x00000010; // if set, the control DOES NOT necessarily take capture on MouseDown + private const int STATE2_BECOMINGACTIVECONTROL = 0x00000020; // set to true by ContainerControl when this control is becoming its active control + + private const int STATE2_CLEARLAYOUTARGS = 0x00000040; // if set, the next time PerformLayout is called, cachedLayoutEventArg will be cleared. + private const int STATE2_INPUTKEY = 0x00000080; + private const int STATE2_INPUTCHAR = 0x00000100; + private const int STATE2_UICUES = 0x00000200; + private const int STATE2_ISACTIVEX = 0x00000400; + internal const int STATE2_USEPREFERREDSIZECACHE = 0x00000800; + internal const int STATE2_TOPMDIWINDOWCLOSING = 0x00001000; + internal const int STATE2_CURRENTLYBEINGSCALED = 0x00002000; // if set, the control is being scaled, currently + + private static readonly object EventAutoSizeChanged = new object(); + private static readonly object EventKeyDown = new object(); + private static readonly object EventKeyPress = new object(); + private static readonly object EventKeyUp = new object(); + private static readonly object EventMouseDown = new object(); + private static readonly object EventMouseEnter = new object(); + private static readonly object EventMouseLeave = new object(); + private static readonly object EventDpiChangedBeforeParent = new object(); + private static readonly object EventDpiChangedAfterParent = new object(); + private static readonly object EventMouseHover = new object(); + private static readonly object EventMouseMove = new object(); + private static readonly object EventMouseUp = new object(); + private static readonly object EventMouseWheel = new object(); + private static readonly object EventClick = new object(); + private static readonly object EventClientSize = new object(); + private static readonly object EventDoubleClick = new object(); + private static readonly object EventMouseClick = new object(); + private static readonly object EventMouseDoubleClick = new object(); + private static readonly object EventMouseCaptureChanged = new object(); + private static readonly object EventMove = new object(); + private static readonly object EventResize = new object(); + private static readonly object EventLayout = new object(); + private static readonly object EventGotFocus = new object(); + private static readonly object EventLostFocus = new object(); + private static readonly object EventEnabledChanged = new object(); + private static readonly object EventEnter = new object(); + private static readonly object EventLeave = new object(); + private static readonly object EventHandleCreated = new object(); + private static readonly object EventHandleDestroyed = new object(); + private static readonly object EventVisibleChanged = new object(); + private static readonly object EventControlAdded = new object(); + private static readonly object EventControlRemoved = new object(); + private static readonly object EventChangeUICues = new object(); + private static readonly object EventSystemColorsChanged = new object(); + private static readonly object EventValidating = new object(); + private static readonly object EventValidated = new object(); + private static readonly object EventStyleChanged = new object(); + private static readonly object EventImeModeChanged = new object(); + private static readonly object EventHelpRequested = new object(); + private static readonly object EventPaint = new object(); + private static readonly object EventInvalidated = new object(); + private static readonly object EventQueryContinueDrag = new object(); + private static readonly object EventGiveFeedback = new object(); + private static readonly object EventDragEnter = new object(); + private static readonly object EventDragLeave = new object(); + private static readonly object EventDragOver = new object(); + private static readonly object EventDragDrop = new object(); + private static readonly object EventQueryAccessibilityHelp = new object(); + private static readonly object EventBackgroundImage = new object(); + private static readonly object EventBackgroundImageLayout = new object(); + private static readonly object EventBindingContext = new object(); + private static readonly object EventBackColor = new object(); + private static readonly object EventParent = new object(); + private static readonly object EventVisible = new object(); + private static readonly object EventText = new object(); + private static readonly object EventTabStop = new object(); + private static readonly object EventTabIndex = new object(); + private static readonly object EventSize = new object(); + private static readonly object EventRightToLeft = new object(); + private static readonly object EventLocation = new object(); + private static readonly object EventForeColor = new object(); + private static readonly object EventFont = new object(); + private static readonly object EventEnabled = new object(); + private static readonly object EventDock = new object(); + private static readonly object EventCursor = new object(); + private static readonly object EventContextMenu = new object(); + private static readonly object EventContextMenuStrip = new object(); + private static readonly object EventCausesValidation = new object(); + private static readonly object EventRegionChanged = new object(); + private static readonly object EventMarginChanged = new object(); + internal static readonly object EventPaddingChanged = new object(); + private static readonly object EventPreviewKeyDown = new object(); + + #if WIN95_SUPPORT + private static int mouseWheelMessage = NativeMethods.WM_MOUSEWHEEL; + private static bool mouseWheelRoutingNeeded; + private static bool mouseWheelInit; + #endif + + private static int threadCallbackMessage; + + // Initially check for illegal multithreading based on whether the + // debugger is attached. + [ResourceExposure(ResourceScope.Process)] + private static bool checkForIllegalCrossThreadCalls = Debugger.IsAttached; + private static ContextCallback invokeMarshaledCallbackHelperDelegate; + + [ ThreadStatic ] + private static bool inCrossThreadSafeCall = false; + +// disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + [ ThreadStatic ] + internal static HelpInfo currentHelpInfo = null; +#pragma warning restore 0414 + + private static FontHandleWrapper defaultFontHandleWrapper; + + private const short PaintLayerBackground = 1; + private const short PaintLayerForeground = 2; + + private const byte RequiredScalingEnabledMask = 0x10; + private const byte RequiredScalingMask = 0x0F; + + private const byte HighOrderBitMask = 0x80; + + private static Font defaultFont; + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + // + private static readonly int PropName = PropertyStore.CreateKey(); + private static readonly int PropBackBrush = PropertyStore.CreateKey(); + private static readonly int PropFontHeight = PropertyStore.CreateKey(); + private static readonly int PropCurrentAmbientFont = PropertyStore.CreateKey(); + + private static readonly int PropControlsCollection = PropertyStore.CreateKey(); + private static readonly int PropBackColor = PropertyStore.CreateKey(); + private static readonly int PropForeColor = PropertyStore.CreateKey(); + private static readonly int PropFont = PropertyStore.CreateKey(); + + private static readonly int PropBackgroundImage = PropertyStore.CreateKey(); + private static readonly int PropFontHandleWrapper = PropertyStore.CreateKey(); + private static readonly int PropUserData = PropertyStore.CreateKey(); + private static readonly int PropContextMenu = PropertyStore.CreateKey(); + + private static readonly int PropCursor = PropertyStore.CreateKey(); + private static readonly int PropRegion = PropertyStore.CreateKey(); + private static readonly int PropRightToLeft = PropertyStore.CreateKey(); + + private static readonly int PropBindings = PropertyStore.CreateKey(); + private static readonly int PropBindingManager = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDefaultActionDescription = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDescription = PropertyStore.CreateKey(); + + private static readonly int PropAccessibility = PropertyStore.CreateKey(); + private static readonly int PropNcAccessibility = PropertyStore.CreateKey(); + private static readonly int PropAccessibleName = PropertyStore.CreateKey(); + private static readonly int PropAccessibleRole = PropertyStore.CreateKey(); + + private static readonly int PropPaintingException = PropertyStore.CreateKey(); + private static readonly int PropActiveXImpl = PropertyStore.CreateKey(); + private static readonly int PropControlVersionInfo = PropertyStore.CreateKey(); + private static readonly int PropBackgroundImageLayout = PropertyStore.CreateKey(); + + private static readonly int PropAccessibleHelpProvider = PropertyStore.CreateKey(); + private static readonly int PropContextMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropAutoScrollOffset = PropertyStore.CreateKey(); + private static readonly int PropUseCompatibleTextRendering = PropertyStore.CreateKey(); + + private static readonly int PropImeWmCharsToIgnore = PropertyStore.CreateKey(); + private static readonly int PropImeMode = PropertyStore.CreateKey(); + private static readonly int PropDisableImeModeChangedCount = PropertyStore.CreateKey(); + private static readonly int PropLastCanEnableIme = PropertyStore.CreateKey(); + + private static readonly int PropCacheTextCount = PropertyStore.CreateKey(); + private static readonly int PropCacheTextField = PropertyStore.CreateKey(); + private static readonly int PropAmbientPropertiesService = PropertyStore.CreateKey(); + + private static bool needToLoadComCtl = true; + + // This switch determines the default text rendering engine to use by some controls that support switching rendering engine. + // CheckedListBox, PropertyGrid, GroupBox, Label and LinkLabel, and ButtonBase controls. + // True means use GDI+, false means use GDI (TextRenderer). + internal static bool UseCompatibleTextRenderingDefault = true; + + /////////////////////////////////////////////////////////////////////// + // Control per instance members + // + // Note: Do not add anything to this list unless absolutely neccessary. + // Every control on a form has the overhead of all of these + // variables! + // + // Begin Members { + + // List of properties that are generally set, so we keep them directly on + // control. + // + + // Resist the temptation to make this variable 'internal' rather than + // private. Handle access should be tightly controlled, and is in this + // file. Making it 'internal' makes controlling it quite difficult. + private ControlNativeWindow window; + + private Control parent; + private Control reflectParent; + private CreateParams createParams; + private int x; // CONSIDER: changing this to short + private int y; + private int width; + private int height; + private int clientWidth; + private int clientHeight; + private int state; // See STATE_ constants above + private int state2; // See STATE2_ constants above + private ControlStyles controlStyle; // User supplied control style + private int tabIndex; + private string text; // See ControlStyles.CacheText for usage notes + private byte layoutSuspendCount; + private byte requiredScaling; // bits 0-4: BoundsSpecified stored in RequiredScaling property. Bit 5: RequiredScalingEnabled property. + private PropertyStore propertyStore; // Contains all properties that are not always set. + private NativeMethods.TRACKMOUSEEVENT trackMouseEvent; + private short updateCount; + private LayoutEventArgs cachedLayoutEventArgs; + private Queue threadCallbackList; + internal int deviceDpi; + + + // for keeping track of our ui state for focus and keyboard cues. using a member variable + // here because we hit this a lot + // + private int uiCuesState; + + private const int UISTATE_FOCUS_CUES_MASK = 0x000F; + private const int UISTATE_FOCUS_CUES_HIDDEN = 0x0001; + private const int UISTATE_FOCUS_CUES_SHOW = 0x0002; + private const int UISTATE_KEYBOARD_CUES_MASK = 0x00F0; + private const int UISTATE_KEYBOARD_CUES_HIDDEN = 0x0010; + private const int UISTATE_KEYBOARD_CUES_SHOW = 0x0020; + + [ThreadStatic] + private static byte[] tempKeyboardStateArray; + + // } End Members + /////////////////////////////////////////////////////////////////////// + +#if DEBUG + internal int LayoutSuspendCount { + get { return layoutSuspendCount; } + } + + internal void AssertLayoutSuspendCount(int value) { + Debug.Assert(value == layoutSuspendCount, "Suspend/Resume layout mismatch!"); + } + +/* +example usage + +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif +*/ + +#endif + + /// + /// + /// Initializes a new instance of the class. + /// + public Control() : this(true) { + } + + /// + /// + internal Control(bool autoInstallSyncContext) : base() { +#if DEBUG + if (AssertOnControlCreateSwitch.Enabled) { + Debug.Assert(this.GetType().BaseType != typeof(Control), "Direct derivative of Control Created: " + this.GetType().FullName); + Debug.Assert(this.GetType() != typeof(Control), "Control Created!"); + } +#endif + propertyStore = new PropertyStore(); + + DpiHelper.InitializeDpiHelperForWinforms(); + // Initialize DPI to the value on the primary screen, we will have the correct value when the Handle is created. + deviceDpi = DpiHelper.DeviceDpi; + + window = new ControlNativeWindow(this); + RequiredScalingEnabled = true; + RequiredScaling = BoundsSpecified.All; + tabIndex = -1; + + state = STATE_VISIBLE | STATE_ENABLED | STATE_TABSTOP | STATE_CAUSESVALIDATION; + state2 = STATE2_INTERESTEDINUSERPREFERENCECHANGED; + SetStyle(ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint | + ControlStyles.StandardClick | + ControlStyles.StandardDoubleClick | + ControlStyles.UseTextForAccessibility | + ControlStyles.Selectable,true); + +#if WIN95_SUPPORT + InitMouseWheelSupport(); +#endif + + // We baked the "default default" margin and min size into CommonProperties + // so that in the common case the PropertyStore would be empty. If, however, + // someone overrides these Default* methads, we need to write the default + // value into the PropertyStore in the ctor. + // + if(DefaultMargin != CommonProperties.DefaultMargin) { + Margin = DefaultMargin; + } + if(DefaultMinimumSize != CommonProperties.DefaultMinimumSize) { + MinimumSize = DefaultMinimumSize; + } + if(DefaultMaximumSize != CommonProperties.DefaultMaximumSize) { + MaximumSize = DefaultMaximumSize; + } + + // Compute our default size. + // + Size defaultSize = DefaultSize; + width = defaultSize.Width; + height = defaultSize.Height; + + // DefaultSize may have hit GetPreferredSize causing a PreferredSize to be cached. The + // PreferredSize may change as a result of the current size. Since a SetBoundsCore did + // not happen, so we need to clear the preferredSize cache manually. + CommonProperties.xClearPreferredSizeCache(this); + + if (width != 0 && height != 0) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + rect.left = rect.right = rect.top = rect.bottom = 0; + + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle); + clientWidth = width - (rect.right - rect.left); + clientHeight = height - (rect.bottom - rect.top); + } + + + // Set up for async operations on this thread. + if (autoInstallSyncContext) { + WindowsFormsSynchronizationContext.InstallIfNeeded(); + } + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( string text ) : this( (Control) null, text ) { + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( string text, int left, int top, int width, int height ) : + this( (Control) null, text, left, top, width, height ) { + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( Control parent, string text ) : this() { + this.Parent = parent; + this.Text = text; + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( Control parent, string text, int left, int top, int width, int height ) : this( parent, text ) { + this.Location = new Point( left, top ); + this.Size = new Size( width, height ); + } + + /// + /// gets or sets control Dpi awareness context value. + /// + internal DpiAwarenessContext DpiAwarenessContext { + get { + return window.DpiAwarenessContext; + } + } + + /// + /// + /// The Accessibility Object for this Control + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAccessibilityObjectDescr) + ] + public AccessibleObject AccessibilityObject { + get { + AccessibleObject accessibleObject = (AccessibleObject)Properties.GetObject(PropAccessibility); + if (accessibleObject == null) { + accessibleObject = CreateAccessibilityInstance(); + // this is a security check. we want to enforce that we only return + // ControlAccessibleObject and not some other derived class + if(!(accessibleObject is ControlAccessibleObject)) { + Debug.Fail("Accessible objects for controls must be derived from ControlAccessibleObject."); + return null; + } + Properties.SetObject(PropAccessibility, accessibleObject); + } + + Debug.Assert(accessibleObject != null, "Failed to create accessibility object"); + return accessibleObject; + } + } + + /// + /// Private accessibility object for control, used to wrap the object that + /// OLEACC.DLL creates to represent the control's non-client (NC) region. + /// + private AccessibleObject NcAccessibilityObject { + get { + AccessibleObject ncAccessibleObject = (AccessibleObject)Properties.GetObject(PropNcAccessibility); + if (ncAccessibleObject == null) { + ncAccessibleObject = new ControlAccessibleObject(this, NativeMethods.OBJID_WINDOW); + Properties.SetObject(PropNcAccessibility, ncAccessibleObject); + } + + Debug.Assert(ncAccessibleObject != null, "Failed to create NON-CLIENT accessibility object"); + return ncAccessibleObject; + } + } + + /// + /// Returns a specific AccessibleObject associated with this + /// control, based on standard "accessibile object id". + /// + /// + private AccessibleObject GetAccessibilityObject(int accObjId) { + AccessibleObject accessibleObject; + + switch (accObjId) { + case NativeMethods.OBJID_CLIENT: + accessibleObject = this.AccessibilityObject; + break; + case NativeMethods.OBJID_WINDOW: + accessibleObject = this.NcAccessibilityObject; + break; + default: + if (accObjId > 0) { + accessibleObject = this.GetAccessibilityObjectById(accObjId); + } else { + accessibleObject = null; + } + break; + } + + return accessibleObject; + } + + /// + /// + /// Returns a specific AccessibleObbject associated w/ the objectID + /// + protected virtual AccessibleObject GetAccessibilityObjectById(int objectId) { + if (AccessibilityImprovements.Level3 && this is IAutomationLiveRegion) { + return this.AccessibilityObject; + } + + return null; + } + + /// + /// + /// The default action description of the control + /// + [ + SRCategory(SR.CatAccessibility), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAccessibleDefaultActionDescr) + ] + public string AccessibleDefaultActionDescription { + get { + return (string)Properties.GetObject(PropAccessibleDefaultActionDescription); + } + set { + Properties.SetObject(PropAccessibleDefaultActionDescription, value); + } + } + + /// + /// + /// The accessible description of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlAccessibleDescriptionDescr) + ] + public string AccessibleDescription { + get { + return (string)Properties.GetObject(PropAccessibleDescription); + } + set { + Properties.SetObject(PropAccessibleDescription, value); + } + } + + /// + /// + /// The accessible name of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlAccessibleNameDescr) + ] + public string AccessibleName { + get { + return (string)Properties.GetObject(PropAccessibleName); + } + + set { + Properties.SetObject(PropAccessibleName, value); + } + } + + /// + /// + /// The accessible role of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AccessibleRole.Default), + SRDescription(SR.ControlAccessibleRoleDescr) + ] + public AccessibleRole AccessibleRole { + + get { + bool found; + int role = Properties.GetInteger(PropAccessibleRole, out found); + if (found) { + return (AccessibleRole)role; + } + else { + return AccessibleRole.Default; + } + } + + set { + //valid values are -1 to 0x40 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AccessibleRole.Default, (int)AccessibleRole.OutlineButton)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AccessibleRole)); + } + Properties.SetInteger(PropAccessibleRole, (int)value); + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Color ActiveXAmbientBackColor { + get { + return ActiveXInstance.AmbientBackColor; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Color ActiveXAmbientForeColor { + get { + return ActiveXInstance.AmbientForeColor; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Font ActiveXAmbientFont { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + return ActiveXInstance.AmbientFont; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private bool ActiveXEventsFrozen { + get { + return ActiveXInstance.EventsFrozen; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private IntPtr ActiveXHWNDParent { + get { + return ActiveXInstance.HWNDParent; + } + } + + /// + /// Retrieves the ActiveX control implementation for + /// this control. This will demand create the implementation + /// if it does not already exist. + /// + private ActiveXImpl ActiveXInstance { + get { + ActiveXImpl activeXImpl = (ActiveXImpl)Properties.GetObject(PropActiveXImpl); + if (activeXImpl == null) { + + // Don't allow top level objects to be hosted + // as activeX controls. + // + if (GetState(STATE_TOPLEVEL)) { + throw new NotSupportedException(SR.GetString(SR.AXTopLevelSource)); + } + + activeXImpl = new ActiveXImpl(this); + + // PERF: IsActiveX is called quite a bit - checked everywhere from sizing to event raising. Using a state + // bit to track PropActiveXImpl instead of fetching from the property store. + SetState2(STATE2_ISACTIVEX, true); + Properties.SetObject(PropActiveXImpl, activeXImpl); + } + + return activeXImpl; + } + } + + /// + /// + /// The AllowDrop property. If AllowDrop is set to true then + /// this control will allow drag and drop operations and events to be used. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ControlAllowDropDescr) + ] + public virtual bool AllowDrop { + get { + return GetState(STATE_ALLOWDROP); + } + + set { + if (GetState(STATE_ALLOWDROP) != value) { + // Since we won't call SetAcceptDrops without a handle, + // we do the security demand here. Without this demand + // we are still safe, but you get the exception at an + // odd time. This gives a better experience. + // + if (value && !IsHandleCreated) { + IntSecurity.ClipboardRead.Demand(); + } + + SetState(STATE_ALLOWDROP, value); + + if (IsHandleCreated) { + try { + SetAcceptDrops(value); + } + catch { + // If there is an error, back out the AllowDrop state... + // + SetState(STATE_ALLOWDROP, !value); + throw; + } + } + } + } + } + + // Queries the Site for AmbientProperties. May return null. + // Do not confuse with inheritedProperties -- the service is turned to + // after we've exhausted inheritedProperties. + private AmbientProperties AmbientPropertiesService { + get { + bool contains; + AmbientProperties props = (AmbientProperties)Properties.GetObject(PropAmbientPropertiesService, out contains); + if (!contains) { + if (Site != null) { + props = (AmbientProperties)Site.GetService(typeof(AmbientProperties)); + } + else { + props = (AmbientProperties)GetService(typeof(AmbientProperties)); + } + + if (props != null) { + Properties.SetObject(PropAmbientPropertiesService, props); + } + } + return props; + } + } + + /// + /// + /// The current value of the anchor property. The anchor property + /// determines which edges of the control are anchored to the container's + /// edges. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + DefaultValue(CommonProperties.DefaultAnchor), + SRDescription(SR.ControlAnchorDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual AnchorStyles Anchor { + get { + return DefaultLayout.GetAnchor(this); + } + set { + DefaultLayout.SetAnchor(ParentInternal, this, value); + } + } + + /// + [SRCategory(SR.CatLayout)] + [RefreshProperties(RefreshProperties.All)] + [Localizable(true)] + [DefaultValue(CommonProperties.DefaultAutoSize)] + [SRDescription(SR.ControlAutoSizeDescr)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool AutoSize { + get { return CommonProperties.GetAutoSize(this); } + set { + if(value != AutoSize) { + CommonProperties.SetAutoSize(this, value); + if(ParentInternal != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(value && ParentInternal.LayoutEngine == DefaultLayout.Instance) { + ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize); + } + + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler AutoSizeChanged { + add { + Events.AddHandler(EventAutoSizeChanged, value); + } + remove { + Events.RemoveHandler(EventAutoSizeChanged, value); + } + } + + /// + /// Controls the location of where this control is scrolled to in ScrollableControl.ScrollControlIntoView. + /// Default is the upper left hand corner of the control. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(typeof(Point), "0, 0") + ] + public virtual Point AutoScrollOffset { + get { + if (Properties.ContainsObject(PropAutoScrollOffset)) { + return (Point)Properties.GetObject(PropAutoScrollOffset); + } + return Point.Empty; + } + set { + if (AutoScrollOffset != value) { + Properties.SetObject(PropAutoScrollOffset, value); + } + } + } + + protected void SetAutoSizeMode(AutoSizeMode mode) { + CommonProperties.SetAutoSizeMode(this, mode); + } + + protected AutoSizeMode GetAutoSizeMode() { + return CommonProperties.GetAutoSizeMode(this); + } + + /// + // Public because this is interesting for ControlDesigners. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual LayoutEngine LayoutEngine { + get { return DefaultLayout.Instance; } + } + + /// + /// The GDI brush for our background color. + /// Whidbey Note: Made this internal, since we need to use this in ButtonStandardAdapter. Also, renamed + /// from BackBrush to BackColorBrush due to a naming conflict with DataGrid's BackBrush. + /// + internal IntPtr BackColorBrush { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + object customBackBrush = Properties.GetObject(PropBackBrush); + if (customBackBrush != null) { + + // We already have a valid brush. Unbox, and return. + // + return (IntPtr)customBackBrush; + } + + if (!Properties.ContainsObject(PropBackColor)) { + + // No custom back color. See if we can get to our parent. + // The color check here is to account for parents and children who + // override the BackColor property. + // + if (parent != null && parent.BackColor == BackColor) { + return parent.BackColorBrush; + } + } + + // No parent, or we have a custom back color. Either way, we need to + // create our own. + // + Color color = BackColor; + IntPtr backBrush; + + if (ColorTranslator.ToOle(color) < 0) { + backBrush = SafeNativeMethods.GetSysColorBrush(ColorTranslator.ToOle(color) & 0xFF); + SetState(STATE_OWNCTLBRUSH, false); + } + else { + backBrush = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(color)); + SetState(STATE_OWNCTLBRUSH, true); + } + + Debug.Assert(backBrush != IntPtr.Zero, "Failed to create brushHandle"); + Properties.SetObject(PropBackBrush, backBrush); + + return backBrush; + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_BACKCOLOR), + SRDescription(SR.ControlBackColorDescr) + ] + public virtual Color BackColor { + get { + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) { + return c; + } + + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + c = p.BackColor; + if (IsValidBackColor(c)) + { + return c; + } + + } + + if (IsActiveX) { + c = ActiveXAmbientBackColor; + } + + if (c.IsEmpty) { + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null) + c = ambient.BackColor; + } + + if (!c.IsEmpty && IsValidBackColor(c)) + return c; + else + return DefaultBackColor; + } + + set { + if (!value.Equals(Color.Empty) && !GetStyle(ControlStyles.SupportsTransparentBackColor) && value.A < 255) + throw new ArgumentException(SR.GetString(SR.TransparentBackColorNotAllowed)); + + Color c = BackColor; + if (!value.IsEmpty || Properties.ContainsObject(PropBackColor)) { + Properties.SetColor(PropBackColor, value); + } + + if (!c.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackColorChangedDescr)] + public event EventHandler BackColorChanged { + add { + Events.AddHandler(EventBackColor, value); + } + remove { + Events.RemoveHandler(EventBackColor, value); + } + } + + /// + /// + /// The background image of the control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlBackgroundImageDescr) + ] + public virtual Image BackgroundImage { + get { + return (Image)Properties.GetObject(PropBackgroundImage); + } + set { + if (BackgroundImage != value) { + Properties.SetObject(PropBackgroundImage, value); + OnBackgroundImageChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackgroundImageChangedDescr)] + public event EventHandler BackgroundImageChanged { + add { + Events.AddHandler(EventBackgroundImage, value); + } + remove { + Events.RemoveHandler(EventBackgroundImage, value); + } + } + + /// + /// + /// The BackgroundImageLayout of the control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public virtual ImageLayout BackgroundImageLayout { + get { + bool found = Properties.ContainsObject(PropBackgroundImageLayout); + if (!found) { + return ImageLayout.Tile; + } + else { + return ((ImageLayout)Properties.GetObject(PropBackgroundImageLayout)); + } + } + set { + if (BackgroundImageLayout != value) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ImageLayout.None, (int)ImageLayout.Zoom)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ImageLayout)); + } + // Check if the value is either center, strech or zoom; + if (value == ImageLayout.Center || value == ImageLayout.Zoom || value == ImageLayout.Stretch) { + SetStyle(ControlStyles.ResizeRedraw, true); + // Only for images that support transparency. + if (ControlPaint.IsImageTransparent(BackgroundImage)) + { + DoubleBuffered = true; + } + } + Properties.SetObject(PropBackgroundImageLayout, value); + OnBackgroundImageLayoutChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackgroundImageLayoutChangedDescr)] + public event EventHandler BackgroundImageLayoutChanged { + add { + Events.AddHandler(EventBackgroundImageLayout, value); + } + remove { + Events.RemoveHandler(EventBackgroundImageLayout, value); + } + } + + // Set/reset by ContainerControl.AssignActiveControlInternal + internal bool BecomingActiveControl + { + get + { + return GetState2(STATE2_BECOMINGACTIVECONTROL); + } + set + { + if (value != this.BecomingActiveControl) + { + Application.ThreadContext.FromCurrent().ActivatingControl = (value) ? this : null; + SetState2(STATE2_BECOMINGACTIVECONTROL, value); + } + } + } + + private bool ShouldSerializeAccessibleName() { + string accName = this.AccessibleName; + return accName != null && accName.Length > 0; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetBindings() { + ControlBindingsCollection bindings = (ControlBindingsCollection)Properties.GetObject(PropBindings); + if (bindings != null) { + bindings.Clear(); + } + } + + /// + /// + /// BindingContextInternal provides a mechanism so that controls like SplitContainer that inherit from the + /// ContainerControl can bypass the "containerControls" bindingContext property and do what the other simple controls + /// do. + /// + internal BindingContext BindingContextInternal + { + get + { + // See if we have locally overridden the binding manager. + // + BindingContext context = (BindingContext) Properties.GetObject(PropBindingManager); + if (context != null) { + return context; + } + + // Otherwise, see if the parent has one for us. + // + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + return p.BindingContext; + } + + // Otherwise, we have no binding manager available. + // + return null; + } + set + { + BindingContext oldContext = (BindingContext) Properties.GetObject(PropBindingManager); + BindingContext newContext = value; + + if (oldContext != newContext) { + Properties.SetObject(PropBindingManager, newContext); + + // the property change will wire up the bindings. + // + OnBindingContextChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBindingContextDescr) + ] + public virtual BindingContext BindingContext { + get { + return BindingContextInternal; + } + set { + BindingContextInternal = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBindingContextChangedDescr)] + public event EventHandler BindingContextChanged { + add { + Events.AddHandler(EventBindingContext, value); + } + remove { + Events.RemoveHandler(EventBindingContext, value); + } + } + + /// + /// + /// The bottom coordinate of this control. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBottomDescr), + SRCategory(SR.CatLayout) + ] + public int Bottom { + get { + return y + height; + } + } + + /// + /// + /// The bounds of this control. This is the window coordinates of the + /// control in parent client coordinates. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBoundsDescr), + SRCategory(SR.CatLayout) + ] + public Rectangle Bounds { + get { + return new Rectangle(x, y, width, height); + } + + set { + SetBounds(value.X, value.Y, value.Width, value.Height, BoundsSpecified.All); + } + } + + /// + /// + /// + internal virtual bool CanAccessProperties { + get { + return true; + } + } + + /// + /// + /// Indicates whether the control can receive focus. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCanFocusDescr) + ] + public bool CanFocus { + get { + if (!IsHandleCreated) { + return false; + } + bool visible = SafeNativeMethods.IsWindowVisible(new HandleRef(window, Handle)); + bool enabled = SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle)); + return (visible && enabled); + } + } + + /// + /// + /// Determines if events can be fired on the control. If this control is being + /// hosted as an ActiveX control, this property will return false if the ActiveX + /// control has its events frozen. + /// + protected override bool CanRaiseEvents { + get { + if (IsActiveX) { + return !ActiveXEventsFrozen; + } + + return true; + } + } + + /// + /// + /// + /// Indicates whether the control can be selected. This property + /// is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCanSelectDescr) + ] + public bool CanSelect { + // We implement this to allow only AxHost to override canSelectCore, but still + // expose the method publicly + // + get { + return CanSelectCore(); + } + } + + /// + /// + /// Indicates whether the control has captured the mouse. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCaptureDescr) + ] + public bool Capture { + get { + return CaptureInternal; + } + + set { + if (value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetCapture Demanded"); + IntSecurity.GetCapture.Demand(); + } + CaptureInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal bool CaptureInternal { + get { + return IsHandleCreated && UnsafeNativeMethods.GetCapture() == Handle; + } + set { + if (CaptureInternal != value) { + if (value) { + UnsafeNativeMethods.SetCapture(new HandleRef(this, Handle)); + } + else { + SafeNativeMethods.ReleaseCapture(); + } + } + } + } + + /// + /// + /// + /// Indicates whether entering the control causes validation on the controls requiring validation. + /// + [ + SRCategory(SR.CatFocus), + DefaultValue(true), + SRDescription(SR.ControlCausesValidationDescr) + ] + public bool CausesValidation { + get { + return GetState(STATE_CAUSESVALIDATION); + } + set { + if (value != this.CausesValidation) { + SetState(STATE_CAUSESVALIDATION, value); + OnCausesValidationChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnCausesValidationChangedDescr)] + public event EventHandler CausesValidationChanged { + add { + Events.AddHandler(EventCausesValidation, value); + } + remove { + Events.RemoveHandler(EventCausesValidation, value); + } + } + + + /// This is for perf. Turn this property on to temporarily enable text caching. This is good for + /// operations such as layout or painting where we don't expect the text to change (we will update the + /// cache if it does) but prevents us from sending a ton of messages turing layout. See the PaintWithErrorHandling + /// function. + /// + internal bool CacheTextInternal { + get { + + // check if we're caching text. + // + bool found; + int cacheTextCounter = Properties.GetInteger(PropCacheTextCount, out found); + + return cacheTextCounter > 0 || GetStyle(ControlStyles.CacheText); + } + set { + + // if this control always cachest text or the handle hasn't been created, + // just bail. + // + if (GetStyle(ControlStyles.CacheText) || !IsHandleCreated) { + return; + } + + // otherwise, get the state and update the cache if necessary. + // + bool found; + int cacheTextCounter = Properties.GetInteger(PropCacheTextCount, out found); + + if (value) { + if (cacheTextCounter == 0) { + Properties.SetObject(PropCacheTextField, text); + if (text == null) { + text = WindowText; + } + } + cacheTextCounter++; + } + else { + cacheTextCounter--; + if (cacheTextCounter == 0) { + text = (string)Properties.GetObject(PropCacheTextField, out found); + } + } + Properties.SetInteger(PropCacheTextCount, cacheTextCounter); + } + } + + /// + [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlCheckForIllegalCrossThreadCalls), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public static bool CheckForIllegalCrossThreadCalls { + get { return checkForIllegalCrossThreadCalls; } + set { checkForIllegalCrossThreadCalls = value; } + } + + /// + /// + /// The client rect of the control. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatLayout), + SRDescription(SR.ControlClientRectangleDescr) + ] + public Rectangle ClientRectangle { + get { + return new Rectangle(0, 0, clientWidth, clientHeight); + } + } + + /// + /// + /// The size of the clientRect. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlClientSizeDescr) + ] + public Size ClientSize { + get { + return new Size(clientWidth, clientHeight); + } + + set { + SetClientSizeCore(value.Width, value.Height); + } + } + + /// + /// + /// Fired when ClientSize changes. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnClientSizeChangedDescr)] + public event EventHandler ClientSizeChanged { + add { + Events.AddHandler(EventClientSize, value); + } + remove { + Events.RemoveHandler(EventClientSize, value); + } + } + + /// + /// + /// Retrieves the company name of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DescriptionAttribute(SR.ControlCompanyNameDescr) + ] + public string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.CompanyName; + } + } + + /// + /// + /// Indicates whether the control or one of its children currently has the system + /// focus. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlContainsFocusDescr) + ] + public bool ContainsFocus { + get { + if (!IsHandleCreated) { + return false; + } + + IntPtr focusHwnd = UnsafeNativeMethods.GetFocus(); + + if (focusHwnd == IntPtr.Zero) { + return false; + } + + if (focusHwnd == this.Handle) { + return true; + } + + if (UnsafeNativeMethods.IsChild(new HandleRef(this, this.Handle), new HandleRef(this, focusHwnd))) { + return true; + } + + return false; + } + } + + /// + /// + /// The contextMenu associated with this control. The contextMenu + /// will be shown when the user right clicks the mouse on the control. + /// + /// Whidbey: ContextMenu is browsable false. In all cases where both a context menu + /// and a context menu strip are assigned, context menu will be shown instead of context menu strip. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr), + Browsable(false) + ] + public virtual ContextMenu ContextMenu { + get { + return (ContextMenu)Properties.GetObject(PropContextMenu); + } + set { + ContextMenu oldValue = (ContextMenu)Properties.GetObject(PropContextMenu); + + if (oldValue != value) { + EventHandler disposedHandler = new EventHandler(DetachContextMenu); + + if (oldValue != null) { + oldValue.Disposed -= disposedHandler; + } + + Properties.SetObject(PropContextMenu, value); + + if (value != null) { + value.Disposed += disposedHandler; + } + + OnContextMenuChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ControlOnContextMenuChangedDescr), + Browsable(false) + ] + public event EventHandler ContextMenuChanged { + add { + Events.AddHandler(EventContextMenu, value); + } + remove { + Events.RemoveHandler(EventContextMenu, value); + } + } + + + /// + /// + /// The contextMenuStrip associated with this control. The contextMenuStrip + /// will be shown when the user right clicks the mouse on the control. + /// Note: if a context menu is also assigned, it will take precidence over this property. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr) + ] + public virtual ContextMenuStrip ContextMenuStrip { + get { + return (ContextMenuStrip)Properties.GetObject(PropContextMenuStrip); + } + set { + ContextMenuStrip oldValue = Properties.GetObject(PropContextMenuStrip) as ContextMenuStrip; + + if (oldValue != value) { + EventHandler disposedHandler = new EventHandler(DetachContextMenuStrip); + + if (oldValue != null) { + oldValue.Disposed -= disposedHandler; + } + + Properties.SetObject(PropContextMenuStrip, value); + + if (value != null) { + value.Disposed += disposedHandler; + } + + OnContextMenuStripChanged(EventArgs.Empty); + } + } + + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlContextMenuStripChangedDescr)] + public event EventHandler ContextMenuStripChanged { + add { + Events.AddHandler(EventContextMenuStrip, value); + } + remove { + Events.RemoveHandler(EventContextMenuStrip, value); + } + } + + /// + /// + /// Collection of child controls. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.ControlControlsDescr) + ] + public ControlCollection Controls { + get { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection == null) { + controlsCollection = CreateControlsInstance(); + Properties.SetObject(PropControlsCollection, controlsCollection); + } + return controlsCollection; + } + } + + /// + /// + /// Indicates whether the control has been created. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlCreatedDescr) + ] + public bool Created { + get { + return(state & STATE_CREATED) != 0; + } + } + + /// + /// + /// Returns the CreateParams used to create the handle for this control. + /// Inheriting classes should call base.CreateParams in the manor + /// below: + /// + protected virtual CreateParams CreateParams { + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + + // CLR4.0 or later, comctl32.dll needs to be loaded explicitly. + if (needToLoadComCtl) { + if ((UnsafeNativeMethods.GetModuleHandle(ExternDll.Comctl32) != IntPtr.Zero) + || (UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(ExternDll.Comctl32) != IntPtr.Zero)) { + needToLoadComCtl = false; + } else { + int lastWin32Error = Marshal.GetLastWin32Error(); + throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError, ExternDll.Comctl32)); + } + } + + // In a typical control this is accessed ten times to create and show a control. + // It is a net memory savings, then, to maintain a copy on control. + // + if (createParams == null) { + createParams = new CreateParams(); + } + + CreateParams cp = createParams; + cp.Style = 0; + cp.ExStyle = 0; + cp.ClassStyle = 0; + cp.Caption = text; + + cp.X = x; + cp.Y = y; + cp.Width = width; + cp.Height = height; + + cp.Style = NativeMethods.WS_CLIPCHILDREN; + if (GetStyle(ControlStyles.ContainerControl)) { + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + } + cp.ClassStyle = NativeMethods.CS_DBLCLKS; + + if ((state & STATE_TOPLEVEL) == 0) { + // When the window is actually created, we will parent WS_CHILD windows to the + // parking form if cp.parent == 0. + // + cp.Parent = parent == null ? IntPtr.Zero : parent.InternalHandle; + cp.Style |= NativeMethods.WS_CHILD | NativeMethods.WS_CLIPSIBLINGS; + } + else { + cp.Parent = IntPtr.Zero; + } + + if ((state & STATE_TABSTOP) != 0) cp.Style |= NativeMethods.WS_TABSTOP; + if ((state & STATE_VISIBLE) != 0) cp.Style |= NativeMethods.WS_VISIBLE; + + // Unlike Visible, Windows doesn't correctly inherit disabledness from its parent -- an enabled child + // of a disabled parent will look enabled but not get mouse events + if (!Enabled) cp.Style |= NativeMethods.WS_DISABLED; + + // If we are being hosted as an Ax control, try to prevent the parking window + // from being created by pre-filling the window handle here. + // + if (cp.Parent == IntPtr.Zero && IsActiveX) { + cp.Parent = ActiveXHWNDParent; + } + + // Set Rtl bits + if (RightToLeft == RightToLeft.Yes) { + cp.ExStyle |= NativeMethods.WS_EX_RTLREADING; + cp.ExStyle |= NativeMethods.WS_EX_RIGHT; + cp.ExStyle |= NativeMethods.WS_EX_LEFTSCROLLBAR; + } + + return cp; + } + } + + internal virtual void NotifyValidationResult(object sender, CancelEventArgs ev) { + this.ValidationCancelled = ev.Cancel ; + } + + /// + /// Helper method... + /// + /// Triggers validation on the active control, and returns bool indicating whether that control was valid. + /// + /// The correct way to do this is to find the common ancestor of the active control and this control, + /// then request validation to be performed by that common container control. + /// + /// Used by controls that don't participate in the normal enter/leave/validation process, but which + /// want to force form-level validation to occur before they attempt some important action. + /// + internal bool ValidateActiveControl(out bool validatedControlAllowsFocusChange) { + bool valid = true; + validatedControlAllowsFocusChange = false; + IContainerControl c = GetContainerControlInternal(); + if (c != null && this.CausesValidation) { + ContainerControl container = c as ContainerControl; + if (container != null) { + while (container.ActiveControl == null) { + ContainerControl cc; + Control parent = container.ParentInternal; + if (parent != null) { + cc = parent.GetContainerControlInternal() as ContainerControl; + if (cc != null) { + container = cc; + } + else { + break; + } + } + else { + break; + } + } + + valid = container.ValidateInternal(true, out validatedControlAllowsFocusChange); + } + } + + return valid; + } + + internal bool ValidationCancelled { + set { + SetState(STATE_VALIDATIONCANCELLED, value); + } + get { + if (GetState(STATE_VALIDATIONCANCELLED)) { + return true; + } + else { + Control parent = this.ParentInternal; + if (parent != null) { + return parent.ValidationCancelled; + } + + return false; + } + } + } + + /// + /// returns bool indicating whether the Top MDI Window is closing. + /// This property is set in the MDI children in WmClose method in form.cs when the top window is closing. + /// This property will be used in ActiveControl to determine if we want to skip set focus and window handle re-creation for the control. + /// + /// + internal bool IsTopMdiWindowClosing { + set { + SetState2(STATE2_TOPMDIWINDOWCLOSING, value); + } + get { + return GetState2(STATE2_TOPMDIWINDOWCLOSING); + } + } + + /// + /// returns bool indicating whether the control is currently being scaled. + /// This property is set in ScaleControl method to allow method being called to condition code that should not run for scaling. + /// + /// + internal bool IsCurrentlyBeingScaled { + private set { + SetState2(STATE2_CURRENTLYBEINGSCALED, value); + } + get { + return GetState2(STATE2_CURRENTLYBEINGSCALED); + } + } + + /// + /// Retrieves the Win32 thread ID of the thread that created the + /// handle for this control. If the control's handle hasn't been + /// created yet, this method will return the current thread's ID. + /// + /// + internal int CreateThreadId { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + if (IsHandleCreated) { + int pid; + return SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, Handle), out pid); + } + else { + return SafeNativeMethods.GetCurrentThreadId(); + } + } + } + + /// + /// + /// Retrieves the cursor that will be displayed when the mouse is over this + /// control. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlCursorDescr), + AmbientValue(null) + ] + public virtual Cursor Cursor { + get { + if (GetState(STATE_USEWAITCURSOR)) + { + return Cursors.WaitCursor; + } + + Cursor cursor = (Cursor)Properties.GetObject(PropCursor); + if (cursor != null) { + return cursor; + } + + // We only do ambients for things with "Cursors.Default" + // as their default. + // + Cursor localDefault = DefaultCursor; + if (localDefault != Cursors.Default) { + return localDefault; + } + + Control p = ParentInternal; + if (p != null) { + return p.Cursor; + } + + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null && ambient.Cursor != null) + return ambient.Cursor; + + return localDefault; + } + set { + + Cursor localCursor = (Cursor)Properties.GetObject(PropCursor); + Cursor resolvedCursor = Cursor; + if (localCursor != value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyCursor Demanded"); + IntSecurity.ModifyCursor.Demand(); + + Properties.SetObject(PropCursor, value); + } + + // Other things can change the cursor... we + // really want to force the correct cursor always... + // + if (IsHandleCreated) { + // We want to instantly change the cursor if the mouse is within our bounds. + // This includes the case where the mouse is over one of our children + NativeMethods.POINT p = new NativeMethods.POINT(); + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetCursorPos(p); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + + if ((r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom) || UnsafeNativeMethods.GetCapture() == Handle) + SendMessage(NativeMethods.WM_SETCURSOR, Handle, (IntPtr)NativeMethods.HTCLIENT); + } + + if (!resolvedCursor.Equals(value)) { + OnCursorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnCursorChangedDescr)] + public event EventHandler CursorChanged { + add { + Events.AddHandler(EventCursor, value); + } + remove { + Events.RemoveHandler(EventCursor, value); + } + } + + /// + /// + /// Retrieves the bindings for this control. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ControlBindingsDescr), + RefreshProperties(RefreshProperties.All), + ParenthesizePropertyName(true) + ] + public ControlBindingsCollection DataBindings + { + get + { + ControlBindingsCollection bindings = (ControlBindingsCollection)Properties.GetObject(PropBindings); + if (bindings == null) + { + bindings = new ControlBindingsCollection(this); + Properties.SetObject(PropBindings, bindings); + } + return bindings; + } + } + + /// + /// + /// The default BackColor of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Color DefaultBackColor { + get { return SystemColors.Control;} + } + + /// + /// + /// Deriving classes can override this to configure a default cursor for their control. + /// This is more efficient than setting the cursor in the control's constructor, + /// and gives automatic support for ShouldSerialize and Reset in the designer. + /// + protected virtual Cursor DefaultCursor { + get { + return Cursors.Default; + } + } + + /// + /// + /// The default Font of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Font DefaultFont { + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + if (defaultFont == null) { + defaultFont = SystemFonts.DefaultFont; + Debug.Assert(defaultFont != null, "defaultFont wasn't set!"); + } + + return defaultFont; + } + } + + /// + /// + /// The default ForeColor of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Color DefaultForeColor { + get { return SystemColors.ControlText;} + } + + /// + protected virtual Padding DefaultMargin { + get { return CommonProperties.DefaultMargin; } + } + + /// + protected virtual Size DefaultMaximumSize { + get { return CommonProperties.DefaultMaximumSize; } + } + + /// + protected virtual Size DefaultMinimumSize { + get { return CommonProperties.DefaultMinimumSize; } + } + + /// + protected virtual Padding DefaultPadding { + get { return Padding.Empty; } + } + + private RightToLeft DefaultRightToLeft { + get { return RightToLeft.No; } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Size DefaultSize { + get { return Size.Empty; } + } + + private void DetachContextMenu(object sender, EventArgs e) { + ContextMenu = null; + } + + + private void DetachContextMenuStrip(object sender, EventArgs e) { + ContextMenuStrip = null; + } + + /// + /// + /// DPI value either for the primary screen or for the monitor where the top-level parent is displayed when + /// EnableDpiChangedMessageHandling option is on and the application is per-monitor V2 DPI-aware (rs2+) + /// + [ + Browsable(false), // don't show in property browser + EditorBrowsable(EditorBrowsableState.Always), // do show in the intellisense + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) // do not serialize + ] + public int DeviceDpi { + get { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return deviceDpi; + } + return DpiHelper.DeviceDpi; + } + } + + // The color to use when drawing disabled text. Normally we use BackColor, + // but that obviously won't work if we're transparent. + internal Color DisabledColor { + get { + Color color = BackColor; + if (color.A == 0) { + Control control = ParentInternal; + while (color.A == 0) { + if (control == null) { + // Don't know what to do, this seems good as anything + color = SystemColors.Control; + break; + } + color = control.BackColor; + control = control.ParentInternal; + } + } + return color; + } + } + + /// + /// + /// Returns the client rect of the display area of the control. + /// For the base control class, this is identical to getClientRect. + /// However, inheriting controls may want to change this if their client + /// area differs from their display area. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisplayRectangleDescr) + ] + public virtual Rectangle DisplayRectangle { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + return new Rectangle(0, 0, clientWidth, clientHeight); + } + } + + /// + /// + /// Indicates whether the control has been disposed. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisposedDescr) + ] + public bool IsDisposed { + get { + return GetState(STATE_DISPOSED); + } + } + + /// + /// Disposes of the currently selected font handle (if cached). + /// + private void DisposeFontHandle() { + if (Properties.ContainsObject(PropFontHandleWrapper)) { + FontHandleWrapper fontHandle = Properties.GetObject(PropFontHandleWrapper) as FontHandleWrapper; + if( fontHandle != null ){ + fontHandle.Dispose(); + } + Properties.SetObject(PropFontHandleWrapper, null); + } + } + + /// + /// + /// Indicates whether the control is in the process of being disposed. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisposingDescr) + ] + public bool Disposing { + get { + return GetState(STATE_DISPOSING); + } + } + + /// + /// + /// The dock property. The dock property controls to which edge + /// of the container this control is docked to. For example, when docked to + /// the top of the container, the control will be displayed flush at the + /// top of the container, extending the length of the container. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(CommonProperties.DefaultDock), + SRDescription(SR.ControlDockDescr) + ] + public virtual DockStyle Dock { + get { + return DefaultLayout.GetDock(this); + } + set { + if(value != Dock) { // NDPWhidbey 29434 +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + DefaultLayout.SetDock(this, value); + OnDockChanged(EventArgs.Empty); + } finally { + ResumeLayout(); + } +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnDockChangedDescr)] + public event EventHandler DockChanged { + add { + Events.AddHandler(EventDock, value); + } + remove { + Events.RemoveHandler(EventDock, value); + } + } + + /// + /// + /// This will enable or disable double buffering. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ControlDoubleBufferedDescr) + ] + protected virtual bool DoubleBuffered { + get { + return GetStyle(ControlStyles.OptimizedDoubleBuffer); + } + set { + if (value != DoubleBuffered) { + + if (value) { + SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, value); + } + else { + SetStyle(ControlStyles.OptimizedDoubleBuffer, value); + } + } + } + } + + private bool DoubleBufferingEnabled { + get { +#pragma warning disable 618 + return GetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint); +#pragma warning restore 618 + } + } + + /// + /// + /// Indicates whether the control is currently enabled. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DispId(NativeMethods.ActiveX.DISPID_ENABLED), + SRDescription(SR.ControlEnabledDescr) + ] + public bool Enabled { + get { + // We are only enabled if our parent is enabled + if (!GetState(STATE_ENABLED)) + return false; + else if (ParentInternal == null) + return true; + else + return ParentInternal.Enabled; + } + + set { + bool oldValue = Enabled; + SetState(STATE_ENABLED, value); + + if (oldValue != value) { + if (!value) { + SelectNextIfFocused(); + } + + OnEnabledChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// Occurs when the control is enabled. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnEnabledChangedDescr)] + public event EventHandler EnabledChanged { + add { + Events.AddHandler(EventEnabled, value); + } + remove { + Events.RemoveHandler(EventEnabled, value); + } + } + + /// + /// + /// Indicates whether the control has focus. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlFocusedDescr) + ] + public virtual bool Focused { + get { + return IsHandleCreated && UnsafeNativeMethods.GetFocus() == Handle; + } + } + + /// + /// + /// Retrieves the current font for this control. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DispId(NativeMethods.ActiveX.DISPID_FONT), + AmbientValue(null), + SRDescription(SR.ControlFontDescr) + ] + public virtual Font Font { + [return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ActiveXFontMarshaler))] + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return font; + } + + Font f = GetParentFont(); + if (f != null) { + return f; + } + + if (IsActiveX) { + f = ActiveXAmbientFont; + if (f != null) { + return f; + } + } + + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null && ambient.Font != null) { + return ambient.Font; + } + + return DefaultFont; + } + [param : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ActiveXFontMarshaler))] + set { + Font local = (Font)Properties.GetObject(PropFont); + Font resolved = Font; + + bool localChanged = false; + if (value == null) { + if (local != null) { + localChanged = true; + } + } + else { + if (local == null) { + localChanged = true; + } + else { + localChanged = !value.Equals(local); + } + } + + if (localChanged) { + // Store new local value + // + Properties.SetObject(PropFont, value); + + // We only fire the Changed event if the "resolved" value + // changed, however we must update the font if the local + // value changed... + // + if (!resolved.Equals(value)) { + // Cleanup any font handle wrapper... + // + DisposeFontHandle(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, (value == null) ? -1 : value.Height); + } + + // Font is an ambient property. We need to layout our parent because Font may + // change our size. We need to layout ourselves because our children may change + // size by inheriting the new value. + // + using(new LayoutTransaction(ParentInternal, this, PropertyNames.Font)) { + OnFontChanged(EventArgs.Empty); + } + } + else { + if (IsHandleCreated && !GetStyle(ControlStyles.UserPaint)) { + DisposeFontHandle(); + SetWindowFont(); + } + } + } + } + } + + internal void ScaleFont(float factor) { + Font local = (Font)Properties.GetObject(PropFont); + Font resolved = Font; + Font newFont = DpiHelper.EnableDpiChangedHighDpiImprovements ? + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style, this.Font.Unit, this.Font.GdiCharSet, this.Font.GdiVerticalFont) : + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style); + + if ((local == null) || !local.Equals(newFont)) { + Properties.SetObject(PropFont, newFont); + + if (!resolved.Equals(newFont)) { + DisposeFontHandle(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, newFont.Height); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnFontChangedDescr)] + public event EventHandler FontChanged { + add { + Events.AddHandler(EventFont, value); + } + remove { + Events.RemoveHandler(EventFont, value); + } + } + + internal IntPtr FontHandle { + get { + Font font = (Font)Properties.GetObject(PropFont); + + if (font != null) { + FontHandleWrapper fontHandle = (FontHandleWrapper)Properties.GetObject(PropFontHandleWrapper); + if (fontHandle == null) { + fontHandle = new FontHandleWrapper(font); + + Properties.SetObject(PropFontHandleWrapper, fontHandle); + } + + return fontHandle.Handle; + } + + if (parent != null) { + return parent.FontHandle; + } + + AmbientProperties ambient = AmbientPropertiesService; + + if (ambient != null && ambient.Font != null) { + + FontHandleWrapper fontHandle = null; + + Font currentAmbient = (Font)Properties.GetObject(PropCurrentAmbientFont); + + if (currentAmbient != null && currentAmbient == ambient.Font) { + fontHandle = (FontHandleWrapper)Properties.GetObject(PropFontHandleWrapper); + } + else { + Properties.SetObject(PropCurrentAmbientFont, ambient.Font); + } + + if (fontHandle == null) { + font = ambient.Font; + fontHandle = new FontHandleWrapper(font); + + Properties.SetObject(PropFontHandleWrapper, fontHandle); + } + + return fontHandle.Handle; + } + + return GetDefaultFontHandleWrapper().Handle; + } + } + + /// + /// + /// [To be supplied.] + /// + protected int FontHeight { + get { + bool found; + int fontHeight = Properties.GetInteger(PropFontHeight, out found); + if (found && fontHeight != -1) { + return fontHeight; + } + else { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + fontHeight = font.Height; + Properties.SetInteger(PropFontHeight, fontHeight); + return fontHeight; + } + } + + //ask the parent if it has the font height + int localFontHeight = -1; + + if (ParentInternal != null && ParentInternal.CanAccessProperties) { + localFontHeight = ParentInternal.FontHeight; + } + + //if we still have a bad value, then get the actual font height + if (localFontHeight == -1) { + localFontHeight = Font.Height; + Properties.SetInteger(PropFontHeight, localFontHeight); + } + + return localFontHeight; + } + set { + Properties.SetInteger(PropFontHeight, value); + } + } + + /// + /// + /// The foreground color of the control. + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_FORECOLOR), + SRDescription(SR.ControlForeColorDescr) + ] + public virtual Color ForeColor { + get { + Color color = Properties.GetColor(PropForeColor); + if (!color.IsEmpty) { + return color; + } + + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + return p.ForeColor; + } + + Color c = Color.Empty; + + if (IsActiveX) { + c = ActiveXAmbientForeColor; + } + + if (c.IsEmpty) { + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null) + c = ambient.ForeColor; + } + + if (!c.IsEmpty) + return c; + else + return DefaultForeColor; + } + + set { + Color c = ForeColor; + if (!value.IsEmpty || Properties.ContainsObject(PropForeColor)) { + Properties.SetColor(PropForeColor, value); + } + if (!c.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnForeColorChangedDescr)] + public event EventHandler ForeColorChanged { + add { + Events.AddHandler(EventForeColor, value); + } + remove { + Events.RemoveHandler(EventForeColor, value); + } + } + + private Font GetParentFont() { + if (ParentInternal != null && ParentInternal.CanAccessProperties) + return ParentInternal.Font; + else + return null; + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + public virtual Size GetPreferredSize(Size proposedSize) { + Size prefSize; + + if (GetState(STATE_DISPOSING| STATE_DISPOSED)) { + // if someone's asking when we're disposing just return what we last had. + prefSize = CommonProperties.xGetPreferredSizeCache(this); + } + else { + // Switch Size.Empty to maximum possible values + proposedSize = LayoutUtils.ConvertZeroToUnbounded(proposedSize); + + // Force proposedSize to be within the elements constraints. (This applies + // minimumSize, maximumSize, etc.) + proposedSize = ApplySizeConstraints(proposedSize); + if (GetState2(STATE2_USEPREFERREDSIZECACHE)) { + Size cachedSize = CommonProperties.xGetPreferredSizeCache(this); + + // If the "default" preferred size is being requested, and we have a cached value for it, return it. + // + if(!cachedSize.IsEmpty && (proposedSize == LayoutUtils.MaxSize)) { + +#if DEBUG +#if DEBUG_PREFERREDSIZE + Size newPreferredSize = ApplySizeConstraints(GetPreferredSizeCore(proposedSize)); + bool cacheHitCorrect = (cachedSize == newPreferredSize); + if (!cacheHitCorrect && !GetAnyDisposingInHierarchy()) { + Debug.Fail( + "Cached PreferredSize " + cachedSize.ToString() + + " did not match computed: " +newPreferredSize.ToString() + +". Did we forget to invalidate the cache?\r\n\r\nControl Information: " + WindowsFormsUtils.AssertControlInformation(cacheHitCorrect, this) + + "\r\nChanged Properties\r\n " + CommonProperties.Debug_GetChangedProperties(this)); + } +#endif +#endif + return cachedSize; + } + } + + + + CacheTextInternal = true; + try { + prefSize = GetPreferredSizeCore(proposedSize); + } + finally { + CacheTextInternal = false; + } + + // There is no guarantee that GetPreferredSizeCore() return something within + // proposedSize, so we apply the element's constraints again. + prefSize = ApplySizeConstraints(prefSize); + + // If the "default" preferred size was requested, cache the computed value. + // + if(GetState2(STATE2_USEPREFERREDSIZECACHE) && proposedSize == LayoutUtils.MaxSize) { + CommonProperties.xSetPreferredSizeCache(this, prefSize); + } + } + return prefSize; + } + + // Overriding this method allows us to get the caching and clamping the proposedSize/output to + // MinimumSize / MaximumSize from GetPreferredSize for free. + internal virtual Size GetPreferredSizeCore(Size proposedSize) { + return CommonProperties.GetSpecifiedBounds(this).Size; + } + + /// + /// + /// The HWND handle that this control is bound to. If the handle + /// has not yet been created, this will force handle creation. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DispId(NativeMethods.ActiveX.DISPID_HWND), + SRDescription(SR.ControlHandleDescr) + ] + public IntPtr Handle { + get { + if (checkForIllegalCrossThreadCalls && + !inCrossThreadSafeCall && + InvokeRequired) { + throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall, + Name)); + } + + if (!IsHandleCreated) + { + CreateHandle(); + } + + return HandleInternal; + } + } + + internal IntPtr HandleInternal { + get { + return window.Handle; + } + } + + /// + /// + /// True if this control has child controls in its collection. This + /// is more efficient than checking for Controls.Count > 0, but has the + /// same effect. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHasChildrenDescr) + ] + public bool HasChildren { + get { + ControlCollection controls = (ControlCollection)Properties.GetObject(PropControlsCollection); + return controls != null && controls.Count > 0; + } + } + + internal virtual bool HasMenu { + get { + return false; + } + } + + /// + /// + /// The height of this control + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHeightDescr) + ] + public int Height { + get { + return height; + } + set{ + SetBounds(x, y, width, value, BoundsSpecified.Height); + } + } + + internal bool HostedInWin32DialogManager { + get { + if (!GetState(STATE_CHECKEDHOST)) { + Control topMost = TopMostParent; + if (this != topMost) { + SetState(STATE_HOSTEDINDIALOG, topMost.HostedInWin32DialogManager); + } + else { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(this, Handle)); + IntPtr lastParentHandle = parentHandle; + + StringBuilder sb = new StringBuilder(32); + + SetState(STATE_HOSTEDINDIALOG, false); + + while (parentHandle != IntPtr.Zero) { + int len = UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), null, 0); + if (len > sb.Capacity) { + sb.Capacity = len + 5; + } + UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), sb, sb.Capacity); + + if (sb.ToString() == "#32770") { + SetState(STATE_HOSTEDINDIALOG, true); + break; + } + + lastParentHandle = parentHandle; + parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(null, parentHandle)); + } + } + + SetState(STATE_CHECKEDHOST, true); + + } + + return GetState(STATE_HOSTEDINDIALOG); + } + } + + /// + /// + /// Whether or not this control has a handle associated with it. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHandleCreatedDescr) + ] + public bool IsHandleCreated { + get { return window.Handle != IntPtr.Zero; } + } + + /// + /// Determines if layout is currently suspended. + /// + internal bool IsLayoutSuspended { + get { + return layoutSuspendCount > 0; + } + } + + internal bool IsWindowObscured { + get { + if (!IsHandleCreated || !Visible) { + return false; + } + + bool emptyRegion = false; + + NativeMethods.RECT temp = new NativeMethods.RECT(); + Region working; + Control parent = ParentInternal; + if (parent != null) { + while (parent.ParentInternal != null ) { + parent = parent.ParentInternal; + } + } + + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref temp); + working = new Region(Rectangle.FromLTRB(temp.left, temp.top, temp.right, temp.bottom)); + + try { + IntPtr prev; + IntPtr next; + IntPtr start; + if (parent != null) { + start = parent.Handle; + } + else { + start = Handle; + } + + for (prev = start; + (next = UnsafeNativeMethods.GetWindow(new HandleRef(null, prev), NativeMethods.GW_HWNDPREV)) != IntPtr.Zero; + prev = next) { + + UnsafeNativeMethods.GetWindowRect(new HandleRef(null, next), ref temp); + Rectangle current = Rectangle.FromLTRB(temp.left, temp.top, temp.right, temp.bottom); + + if (SafeNativeMethods.IsWindowVisible(new HandleRef(null, next))) { + working.Exclude(current); + } + } + + + Graphics g = CreateGraphics(); + try { + emptyRegion = working.IsEmpty(g); + } + finally { + g.Dispose(); + } + } + finally { + working.Dispose(); + } + + return emptyRegion; + } + } + + /// + /// Returns the current value of the handle. This may be zero if the handle + /// has not been created. + /// + internal IntPtr InternalHandle { + get { + if (!IsHandleCreated) + { + return IntPtr.Zero; + } + else + { + return Handle; + } + } + } + + /// + /// + /// Determines if the caller must call invoke when making method + /// calls to this control. Controls in windows forms are bound to a specific thread, + /// and are not thread safe. Therefore, if you are calling a control's method + /// from a different thread, you must use the control's invoke method + /// to marshal the call to the proper thread. This function can be used to + /// determine if you must call invoke, which can be handy if you don't know + /// what thread owns a control. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and + /// CreateGraphics. For all other method calls, you should use one of the + /// invoke methods. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlInvokeRequiredDescr) + ] + public bool InvokeRequired { + get { + + using (new MultithreadSafeCallScope()) + { + HandleRef hwnd; + if (IsHandleCreated) { + hwnd = new HandleRef(this, Handle); + } + else { + Control marshalingControl = FindMarshalingControl(); + + if (!marshalingControl.IsHandleCreated) { + return false; + } + + hwnd = new HandleRef(marshalingControl, marshalingControl.Handle); + } + + int pid; + int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(hwnd, out pid); + int currentThread = SafeNativeMethods.GetCurrentThreadId(); + return(hwndThread != currentThread); + } + } + } + + /// + /// + /// Indicates whether or not this control is an accessible control + /// i.e. whether it should be visible to accessibility applications. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlIsAccessibleDescr) + ] + public bool IsAccessible { + get { + return GetState(STATE_ISACCESSIBLE); + } + set { + SetState(STATE_ISACCESSIBLE, value); + } + } + + /// + /// Used to tell if this control is being hosted as an ActiveX control. + /// + internal bool IsActiveX { + get { + return GetState2(STATE2_ISACTIVEX); + } + } + + // VsWhidbey 434959 : If the control on which GetContainerControl( ) is called is a ContainerControl, then we dont return the parent + // but return the same control. This is Everett behavior so we cannot change this since this would be a breaking change. + // Hence we have a new internal property IsContainerControl which returns false for all Everett control, but + // this property is overidden in SplitContainer to return true so that we skip the SplitContainer + // and the correct Parent ContainerControl is returned by GetContainerControl(). + internal virtual bool IsContainerControl + { + get + { + return false; + } + } + + /// + /// Used to tell if this control is being hosted in IE. + /// + internal bool IsIEParent { + get { + return IsActiveX ? ActiveXInstance.IsIE : false; + } + } + + /// + /// Used to tell if the control is mirrored + /// Don't call this from CreateParams. Will lead to nasty problems + /// since we might call CreateParams here - you dig! + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.IsMirroredDescr) + ] + public bool IsMirrored { + get { + if (!IsHandleCreated) { + CreateParams cp = CreateParams; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + } + return GetState(STATE_MIRRORED); + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal virtual bool IsMnemonicsListenerAxSourced + { + get + { + return false; + } + } + + /// + /// Used to tell if this BackColor is Supported + /// + private bool IsValidBackColor (Color c){ + if (!c.IsEmpty && !GetStyle(ControlStyles.SupportsTransparentBackColor) && c.A < 255) + { + return false; + } + return true; + + } + + /// + /// + /// The left coordinate of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlLeftDescr) + ] + public int Left { + get { + return x; + } + set { + SetBounds(value, y, width, height, BoundsSpecified.X); + } + } + + /// + /// + /// The location of this control. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ControlLocationDescr) + ] + public Point Location { + get { + return new Point(x, y); + } + set { + SetBounds(value.X, value.Y, width, height, BoundsSpecified.Location); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnLocationChangedDescr)] + public event EventHandler LocationChanged { + add { + Events.AddHandler(EventLocation, value); + } + remove { + Events.RemoveHandler(EventLocation, value); + } + } + + /// + [ + SRDescription(SR.ControlMarginDescr), + SRCategory(SR.CatLayout), + Localizable(true) + ] + public Padding Margin + { + get { return CommonProperties.GetMargin(this); } + set + { + // This should be done here rather than in the property store as + // some IArrangedElements actually support negative padding. + value = LayoutUtils.ClampNegativePaddingToZero(value); + + // SetMargin causes a layout as a side effect. + if (value != Margin) + { + CommonProperties.SetMargin(this, value); + OnMarginChanged(EventArgs.Empty); + } + Debug.Assert(Margin == value, "Error detected while setting Margin."); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnMarginChangedDescr)] + public event EventHandler MarginChanged + { + add + { + Events.AddHandler(EventMarginChanged, value); + } + remove + { + Events.RemoveHandler(EventMarginChanged, value); + } + } + + /// + [SRCategory(SR.CatLayout)] + [Localizable(true)] + [SRDescription(SR.ControlMaximumSizeDescr)] + [AmbientValue(typeof(Size), "0, 0")] + public virtual Size MaximumSize { + get { return CommonProperties.GetMaximumSize(this, DefaultMaximumSize); } + set { + if (value == Size.Empty) { + CommonProperties.ClearMaximumSize(this); + Debug.Assert(MaximumSize == DefaultMaximumSize, "Error detected while resetting MaximumSize."); + } + else if (value != MaximumSize) { + // SetMaximumSize causes a layout as a side effect. + CommonProperties.SetMaximumSize(this, value); + Debug.Assert(MaximumSize == value, "Error detected while setting MaximumSize."); + } + + } + } + + /// + [SRCategory(SR.CatLayout)] + [Localizable(true)] + [SRDescription(SR.ControlMinimumSizeDescr)] + public virtual Size MinimumSize { + get { return CommonProperties.GetMinimumSize(this, DefaultMinimumSize); } + set { + if (value != MinimumSize) { + // SetMinimumSize causes a layout as a side effect. + CommonProperties.SetMinimumSize(this, value); + } + Debug.Assert(MinimumSize == value, "Error detected while setting MinimumSize."); + } + } + + + /// + /// + /// Retrieves the current state of the modifier keys. This will check the + /// current state of the shift, control, and alt keys. + /// + public static Keys ModifierKeys { + get { + Keys modifiers = 0; + // SECURITYNOTE : only let state of Shift-Control-Alt out... + // + if (UnsafeNativeMethods.GetKeyState((int)Keys.ShiftKey) < 0) modifiers |= Keys.Shift; + if (UnsafeNativeMethods.GetKeyState((int)Keys.ControlKey) < 0) modifiers |= Keys.Control; + if (UnsafeNativeMethods.GetKeyState((int)Keys.Menu) < 0) modifiers |= Keys.Alt; + return modifiers; + } + } + + /// + /// + /// The current state of the mouse buttons. This will check the + /// current state of the left, right, and middle mouse buttons. + /// + public static MouseButtons MouseButtons { + get { + MouseButtons buttons = (MouseButtons)0; + // SECURITYNOTE : only let state of MouseButtons out... + // + if (UnsafeNativeMethods.GetKeyState((int)Keys.LButton) < 0) buttons |= MouseButtons.Left; + if (UnsafeNativeMethods.GetKeyState((int)Keys.RButton) < 0) buttons |= MouseButtons.Right; + if (UnsafeNativeMethods.GetKeyState((int)Keys.MButton) < 0) buttons |= MouseButtons.Middle; + if (UnsafeNativeMethods.GetKeyState((int)Keys.XButton1) < 0) buttons |= MouseButtons.XButton1; + if (UnsafeNativeMethods.GetKeyState((int)Keys.XButton2) < 0) buttons |= MouseButtons.XButton2; + return buttons; + } + } + + /// + /// + /// The current position of the mouse in screen coordinates. + /// + public static Point MousePosition { + get { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(pt); + return new Point(pt.x, pt.y); + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control. The name can be + /// used as a key into the ControlCollection. + /// + [Browsable(false)] + public string Name { + get { + string name = (string)Properties.GetObject(PropName); + if (String.IsNullOrEmpty(name)) { + if (Site != null) { + name = Site.Name; + } + + if (name == null) { + name = ""; + } + } + + return name; + } + set { + if (String.IsNullOrEmpty(value)) { + Properties.SetObject(PropName, null); + } + else { + Properties.SetObject(PropName, value); + } + } + } + + /// + /// + /// The parent of this control. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlParentDescr) + ] + public Control Parent { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + return ParentInternal; + } + set { + ParentInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal virtual Control ParentInternal { + get { + return parent; + } + set { + if (parent != value) { + if (value != null) { + value.Controls.Add(this); + } + else { + parent.Controls.Remove(this); + } + } + } + } + + /// + /// + /// Retrieves the product name of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlProductNameDescr) + ] + public string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.ProductName; + } + } + + /// + /// + /// Retrieves the product version of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlProductVersionDescr) + ] + public string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.ProductVersion; + } + } + + /// + /// Retrieves our internal property storage object. If you have a property + /// whose value is not always set, you should store it in here to save + /// space. + /// + internal PropertyStore Properties { + get { + return propertyStore; + } + } + + // Returns the value of the backColor field -- no asking the parent with its color is, etc. + internal Color RawBackColor { + get { + return Properties.GetColor(PropBackColor); + } + } + + /// + /// + /// Indicates whether the control is currently recreating its handle. This + /// property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRecreatingHandleDescr) + ] + public bool RecreatingHandle { + get { + return(state & STATE_RECREATE) != 0; + } + } + + internal virtual void AddReflectChild() { + } + + internal virtual void RemoveReflectChild() { + } + + private Control ReflectParent { + get { + return reflectParent; + } + set { + if (value != null) { + value.AddReflectChild(); + } + + Control c = ReflectParent as Control; + reflectParent = value; + if (c != null) { + c.RemoveReflectChild(); + } + } + } + + /// + /// + /// The Region associated with this control. (defines the + /// outline/silhouette/boundary of control) + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRegionDescr) + ] + public Region Region { + get { + return (Region)Properties.GetObject(PropRegion); + } + set { + if (GetState(STATE_TOPLEVEL)) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ChangeWindowRegion Demanded"); + IntSecurity.ChangeWindowRegionForTopLevel.Demand(); + } + + Region oldRegion = Region; + if (oldRegion != value) { + Properties.SetObject(PropRegion, value); + + if (oldRegion != null) { + oldRegion.Dispose(); + } + + if (IsHandleCreated) { + IntPtr regionHandle = IntPtr.Zero; + + try { + if (value != null) { + regionHandle = GetHRgn(value); + } + + if (IsActiveX) { + regionHandle = ActiveXMergeRegion(regionHandle); + } + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(this, regionHandle), SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle))) != 0) { + //The Hwnd owns the region. + regionHandle = IntPtr.Zero; + } + } + finally { + if (regionHandle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, regionHandle)); + } + } + } + + OnRegionChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// Event fired when the value of Region property is changed on Control + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlRegionChangedDescr)] + public event EventHandler RegionChanged { + add { + Events.AddHandler(EventRegionChanged, value); + } + remove { + Events.RemoveHandler(EventRegionChanged, value); + } + } + + // Helper function for Rtl + /// + /// + /// + /// [To be supplied.] + /// + [Obsolete("This property has been deprecated. Please use RightToLeft instead. http://go.microsoft.com/fwlink/?linkid=14202")] + protected internal bool RenderRightToLeft + { + get { + return true; + } + } + + /// + /// Determines if the parent's background will be rendered on the label control. + /// + internal bool RenderTransparent { + get { + return GetStyle(ControlStyles.SupportsTransparentBackColor) && this.BackColor.A < 255; + } + } + + /// + /// + private bool RenderColorTransparent(Color c) { + return GetStyle(ControlStyles.SupportsTransparentBackColor) && c.A < 255; + } + + + /// + /// Refer VsWhidbey : 440669: This property is required by certain controls (TabPage) to render its transparency using theming API. + /// We dont want all controls (that are have transparent BackColor) to use theming API to render its background because it has HUGE PERF cost. + /// + /// + internal virtual bool RenderTransparencyWithVisualStyles { + get { + return false; + } + } + + /// + /// Represents the bounds of the control that need to be scaled. Control bounds + /// need to be scaled until ScaleControl is called. They need to be scaled again + /// if their bounds change after ScaleControl is called. + /// + internal BoundsSpecified RequiredScaling { + get { + if ((requiredScaling & RequiredScalingEnabledMask) != 0) { + return (BoundsSpecified)(requiredScaling & RequiredScalingMask); + } + return BoundsSpecified.None; + } + set { + byte enableBit = (byte)(requiredScaling & RequiredScalingEnabledMask); + requiredScaling = (byte)(((int)value & RequiredScalingMask) | enableBit); + } + } + + /// + /// Determines if the required scaling property is enabled. If not, + /// RequiredScaling always returns None. + /// + internal bool RequiredScalingEnabled { + get { + return (requiredScaling & RequiredScalingEnabledMask) != 0; + } + set { + byte scaling = (byte)(requiredScaling & RequiredScalingMask); + requiredScaling = scaling; + if (value) requiredScaling |= RequiredScalingEnabledMask; + } + } + + /// + /// + /// Indicates whether the control should redraw itself when resized. + /// + [ + SRDescription(SR.ControlResizeRedrawDescr) + ] + protected bool ResizeRedraw { + get { + return GetStyle( ControlStyles.ResizeRedraw ); + } + set { + SetStyle( ControlStyles.ResizeRedraw, value ); + } + } + + /// + /// + /// The right coordinate of the control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRightDescr) + ] + public int Right { + get { + return x + width; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(RightToLeft.Inherit), + SRDescription(SR.ControlRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + bool found; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + + if (((RightToLeft)rightToLeft) == RightToLeft.Inherit) { + Control parent = ParentInternal; + if (parent != null) { + rightToLeft = (int)parent.RightToLeft; + } + else { + rightToLeft = (int)DefaultRightToLeft; + } + } + return (RightToLeft)rightToLeft; + } + + set { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)){ + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + + RightToLeft oldValue = RightToLeft; + + if (Properties.ContainsInteger(PropRightToLeft) || value != RightToLeft.Inherit) { + Properties.SetInteger(PropRightToLeft, (int)value); + } + + if (oldValue != RightToLeft) { + // Setting RTL on a container does not cause the container to change size. + // Only the children need to have thier layout updated. + using(new LayoutTransaction(this, this, PropertyNames.RightToLeft)) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + } + + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftChangedDescr)] + public event EventHandler RightToLeftChanged { + add { + Events.AddHandler(EventRightToLeft, value); + } + remove { + Events.RemoveHandler(EventRightToLeft, value); + } + } + + + /// + /// + /// This property controls the scaling of child controls. If true child controls + /// will be scaled when the Scale method on this control is called. If false, + /// child controls will not be scaled. The default is true, and you must override + /// this property to provide a different value. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual bool ScaleChildren { + get { + return true; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override ISite Site { + get { + return base.Site; + } + set { + AmbientProperties oldAmbients = AmbientPropertiesService; + AmbientProperties newAmbients = null; + + if (value != null) { + newAmbients = (AmbientProperties)value.GetService(typeof(AmbientProperties)); + } + + + // If the ambients changed, compare each property. + // + if (oldAmbients != newAmbients) { + bool checkFont = !Properties.ContainsObject(PropFont); + bool checkBackColor = !Properties.ContainsObject(PropBackColor); + bool checkForeColor = !Properties.ContainsObject(PropForeColor); + bool checkCursor = !Properties.ContainsObject(PropCursor); + + Font oldFont = null; + Color oldBackColor = Color.Empty; + Color oldForeColor = Color.Empty; + Cursor oldCursor = null; + + if (checkFont) { + oldFont = Font; + } + + if (checkBackColor) { + oldBackColor = BackColor; + } + + if (checkForeColor) { + oldForeColor = ForeColor; + } + + if (checkCursor) { + oldCursor = Cursor; + } + + Properties.SetObject(PropAmbientPropertiesService, newAmbients); + base.Site = value; + + if (checkFont && !oldFont.Equals(Font)) { + OnFontChanged(EventArgs.Empty); + } + if (checkForeColor && !oldForeColor.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + if (checkBackColor && !oldBackColor.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + if (checkCursor && oldCursor.Equals(Cursor)) { + OnCursorChanged(EventArgs.Empty); + } + } + else { + // If the ambients haven't changed, we just set a new site. + // + base.Site = value; + } + } + } + + /// + /// + /// The size of the control. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ControlSizeDescr) + ] + public Size Size { + get { + return new Size(width, height); + } + set { + SetBounds(x, y, value.Width, value.Height, BoundsSpecified.Size); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnSizeChangedDescr)] + public event EventHandler SizeChanged { + add { + Events.AddHandler(EventSize, value); + } + remove { + Events.RemoveHandler(EventSize, value); + } + } + + /// + /// + /// + /// The tab index of + /// this control. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + MergableProperty(false), + SRDescription(SR.ControlTabIndexDescr) + ] + public int TabIndex { + get { + return tabIndex == -1 ? 0 : tabIndex; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("TabIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "TabIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (tabIndex != value) { + tabIndex = value; + OnTabIndexChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTabIndexChangedDescr)] + public event EventHandler TabIndexChanged { + add { + Events.AddHandler(EventTabIndex, value); + } + remove { + Events.RemoveHandler(EventTabIndex, value); + } + } + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public bool TabStop { + get { + return TabStopInternal; + } + set { + if (TabStop != value) { + TabStopInternal = value; + if (IsHandleCreated) SetWindowStyle(NativeMethods.WS_TABSTOP, value); + OnTabStopChanged(EventArgs.Empty); + } + } + } + + // Grab out the logical of setting TABSTOP state, so that derived class could use this. + internal bool TabStopInternal { + get { + return (state & STATE_TABSTOP) != 0; + } + set { + if (TabStopInternal != value) { + SetState(STATE_TABSTOP, value); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTabStopChangedDescr)] + public event EventHandler TabStopChanged { + add { + Events.AddHandler(EventTabStop, value); + } + remove { + Events.RemoveHandler(EventTabStop, value); + } + } + + + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return Properties.GetObject(PropUserData); + } + set { + Properties.SetObject(PropUserData, value); + } + } + + /// + /// + /// The current text associated with this control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + Bindable(true), + DispId(NativeMethods.ActiveX.DISPID_TEXT), + SRDescription(SR.ControlTextDescr) + ] + public virtual string Text { + get { + if (CacheTextInternal) { + return(text == null) ? "" : text; + } + else { + return WindowText; + } + } + + set { + if (value == null) { + value = ""; + } + + if (value == Text) { + return; + } + + if (CacheTextInternal) { + text = value; + } + WindowText = value; + OnTextChanged(EventArgs.Empty); + + if( this.IsMnemonicsListenerAxSourced ){ + for( Control ctl = this; ctl != null; ctl = ctl.ParentInternal ) { + ActiveXImpl activeXImpl = (ActiveXImpl)ctl.Properties.GetObject(PropActiveXImpl); + if( activeXImpl != null ) { + activeXImpl.UpdateAccelTable(); + break; + } + } + } + + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTextChangedDescr)] + public event EventHandler TextChanged { + add { + Events.AddHandler(EventText, value); + } + remove { + Events.RemoveHandler(EventText, value); + } + } + + /// + /// + /// Top coordinate of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlTopDescr) + ] + public int Top { + get { + return y; + } + set { + SetBounds(x, value, width, height, BoundsSpecified.Y); + } + } + + /// + /// + /// The top level control that contains this control. This doesn't + /// have to be the same as the value returned from getForm since forms + /// can be parented to other controls. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlTopLevelControlDescr) + ] + public Control TopLevelControl { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + return TopLevelControlInternal; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Control TopLevelControlInternal { + get { + Control control = this; + while (control != null && !control.GetTopLevel()) { + control = control.ParentInternal; + } + return control; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Control TopMostParent { + get { + Control control = this; + while (control.ParentInternal != null) { + control = control.ParentInternal; + } + return control; + } + } + + + private BufferedGraphicsContext BufferContext { + get { + //This auto upgraged v1 client to per-process doublebuffering logic + // + return BufferedGraphicsManager.Current; + } + } + + /// + /// + /// Indicates whether the user interface is in a state to show or hide keyboard + /// accelerators. This property is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected internal virtual bool ShowKeyboardCues { + get { + // Controls in design mode always draw their accellerators. + if (!IsHandleCreated || DesignMode) { + return true; + // would be nice to query SystemParametersInfo, but have trouble + // getting this to work and this should not really be called before + // handle created anyway + } + + // How this all works + + // uiCuesState contains this control's cached state of whether or not it thinks + // accelerators/focus cues are turned on. the first 16 bits represent focus cues + // the second represent keyboard cues. "F" is the UISTATE_FOCUS_CUES_MASK, + // "F0" is the UISTATE_KEYBOARD_CUES_MASK + + // We check here if we have cached state. If we dont, we need to initialize ourself. + // We do this by checking "MenuAccessKeysUnderlined" - we show if this returns true. + + // If MenuAccessKeysUnderlined returns false, we have to manually call CHANGEUISTATE on the topmost control + // Why? Well the way the API seems to work is that it stores in a bit flag for the the hidden + // state. + + // Details from the Menu keydown to changed value of uiCuesState... + + // When someone does press the ALT (Menu)/F10 key we will + // Call ProcessUICues on the control that had focus at the time + // ProcessUICues will check the current state of the control using WM_QUERYUISTATE + // If WM_QUERYUISTATE indicates that the accelerators are hidden we will + // either call WM_UPDATEUISTATE or WM_CHANGEUISTATE depending on whether we're hosted or not. + // All controls in the heirarchy will be individually called back on WM_UPDATEUISTATE, which will go into WmUpdateUIState. + // In WmUpdateUIState, we will update our uiCuesState cached value, which + // changes the public value of what we return here for ShowKeyboardCues/ShowFocusCues. + + if ((uiCuesState & UISTATE_KEYBOARD_CUES_MASK) == 0) { + + // VSWhidbey 362408 -- if we get in here that means this is the windows bug where the first top + // level window doesn't get notified with WM_UPDATEUISTATE + // + + if (SystemInformation.MenuAccessKeysUnderlined) { + uiCuesState |= UISTATE_KEYBOARD_CUES_SHOW; + } + else { + // if we're in the hidden state, we need to manufacture an update message so everyone knows it. + // + int actionMask = (NativeMethods.UISF_HIDEACCEL | + (AccessibilityImprovements.Level1 ? 0 : NativeMethods.UISF_HIDEFOCUS)) << 16; + uiCuesState |= UISTATE_KEYBOARD_CUES_HIDDEN; + + // The side effect of this initial state is that adding new controls may clear the accelerator + // state (has been this way forever) + UnsafeNativeMethods.SendMessage(new HandleRef(TopMostParent, TopMostParent.Handle), + NativeMethods.WM_CHANGEUISTATE, + (IntPtr)(actionMask | NativeMethods.UIS_SET), + IntPtr.Zero); + } + } + return (uiCuesState & UISTATE_KEYBOARD_CUES_MASK) == UISTATE_KEYBOARD_CUES_SHOW; + } + } + + /// + /// + /// Indicates whether the user interface is in a state to show or hide focus + /// rectangles. This property is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected internal virtual bool ShowFocusCues { + get { + if (!IsHandleCreated) { + return true; + // would be nice to query SystemParametersInfo, but have trouble + // getting this to work and this should not really be called before + // handle created anyway + } + + // See "How this all works" in ShowKeyboardCues + + if ((uiCuesState & UISTATE_FOCUS_CUES_MASK) == 0) { + // VSWhidbey 362408 -- if we get in here that means this is the windows bug where the first top + // level window doesn't get notified with WM_UPDATEUISTATE + // + if (SystemInformation.MenuAccessKeysUnderlined) { + uiCuesState |= UISTATE_FOCUS_CUES_SHOW; + } + else { + uiCuesState |= UISTATE_FOCUS_CUES_HIDDEN; + + // if we're in the hidden state, we need to manufacture an update message so everyone knows it. + // + int actionMask = (NativeMethods.UISF_HIDEACCEL | NativeMethods.UISF_HIDEFOCUS) << 16; + + // The side effect of this initial state is that adding new controls may clear the focus cue state + // state (has been this way forever) + UnsafeNativeMethods.SendMessage(new HandleRef(TopMostParent, TopMostParent.Handle), + NativeMethods.WM_CHANGEUISTATE, + (IntPtr)(actionMask | NativeMethods.UIS_SET), + IntPtr.Zero); + + } + } + return (uiCuesState & UISTATE_FOCUS_CUES_MASK) == UISTATE_FOCUS_CUES_SHOW; + } + } + + // The parameter used in the call to ShowWindow for this control + // + internal virtual int ShowParams { + get { + return NativeMethods.SW_SHOW; + } + } + + + /// + /// + /// When this property in true the Cursor Property is set to WaitCursor as well as the Cursor Property + /// of all the child controls. + /// + [ + DefaultValue(false), + EditorBrowsable(EditorBrowsableState.Always), + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlUseWaitCursorDescr), + ] + public bool UseWaitCursor { + get { return GetState(STATE_USEWAITCURSOR); } + set { + if (GetState(STATE_USEWAITCURSOR) != value) { + SetState(STATE_USEWAITCURSOR, value); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].UseWaitCursor = value; + } + } + } + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// This property overwrites the UseCompatibleTextRenderingDefault switch when set programmatically. + /// Exposed publicly only by controls that support GDI text rendering (Label, LinkLabel and some others). + /// Observe that this property is NOT virtual (to allow for caching the property value - see LinkLabel) + /// and should be used by controls that support it only (see SupportsUseCompatibleTextRendering). + /// + internal bool UseCompatibleTextRenderingInt { + get{ + if (Properties.ContainsInteger(PropUseCompatibleTextRendering)){ + bool found; + int value = Properties.GetInteger(PropUseCompatibleTextRendering, out found); + if( found ){ + return value == 1; + } + } + + return Control.UseCompatibleTextRenderingDefault; + } + set{ + if( SupportsUseCompatibleTextRendering && UseCompatibleTextRenderingInt != value ){ + Properties.SetInteger(PropUseCompatibleTextRendering, value ? 1 : 0); + // Update the preferred size cache since we will be rendering text using a different engine. + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.UseCompatibleTextRendering); + Invalidate(); + } + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls (PropertyGrid) to iterate through its children to set + /// UseCompatibleTextRendering to the same value if the child control supports it. + /// + internal virtual bool SupportsUseCompatibleTextRendering { + get { + return false; + } + } + + private ControlVersionInfo VersionInfo { + get { + ControlVersionInfo info = (ControlVersionInfo)Properties.GetObject(PropControlVersionInfo); + if (info == null) { + info = new ControlVersionInfo(this); + Properties.SetObject(PropControlVersionInfo, info); + } + return info; + } + } + + /// + /// + /// Indicates whether the control is visible. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ControlVisibleDescr) + ] + public bool Visible { + get { + return GetVisibleCore(); + } + set { + SetVisibleCore(value); + } + } + + /// + /// + /// Occurs when the control becomes visible. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnVisibleChangedDescr)] + public event EventHandler VisibleChanged { + add { + Events.AddHandler(EventVisible, value); + } + remove { + Events.RemoveHandler(EventVisible, value); + } + } + + /// + /// + /// Wait for the wait handle to receive a signal: throw an exception if the thread is no longer with us. + /// + private void WaitForWaitHandle(WaitHandle waitHandle) { + int threadId = CreateThreadId; + Application.ThreadContext ctx = Application.ThreadContext.FromId(threadId); + if (ctx == null) { + // Couldn't find the thread context, so we don't know the state. We shouldn't throw. + return; + } + IntPtr threadHandle = ctx.GetHandle(); + bool processed = false; + // setting default exitcode to 0, though it won't be accessed in current code below due to short-circuit logic in condition (returnValue will be false when exitCode is undefined) + uint exitCode = 0; + bool returnValue = false; + while (!processed) { + //Get the thread's exit code, if we found the thread as expected + if (threadHandle != null) { + returnValue = UnsafeNativeMethods.GetExitCodeThread(threadHandle, out exitCode); + } + //If we didn't find the thread, or if GetExitCodeThread failed, we don't know the thread's state: + //if we don't know, we shouldn't throw. + if ((returnValue && exitCode != NativeMethods.STILL_ACTIVE) || + (!returnValue && Marshal.GetLastWin32Error() == NativeMethods.ERROR_INVALID_HANDLE) || + AppDomain.CurrentDomain.IsFinalizingForUnload()) { + if (waitHandle.WaitOne(1, false)) { + break; + } + throw new InvalidAsynchronousStateException(SR.GetString(SR.ThreadNoLongerValid)); + } + + //Dev10 Bug 600316, 905126: Because Control.Invoke() is not fully thread safe, so it is possible that + //a ThreadMethodEntry can be sent to a control after it is disposed. In this case, we need to check + //if there is any ThreadMethodEntry in the queue. If so, we need "complete" them. + if (IsDisposed && threadCallbackList != null && threadCallbackList.Count > 0) { + lock (threadCallbackList) { + Exception ex = new System.ObjectDisposedException(GetType().Name); + while (threadCallbackList.Count > 0) { + ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue(); + entry.exception = ex; + entry.Complete(); + } + } + } + + processed = waitHandle.WaitOne(1000, false); + } + } + + + /// + /// + /// The width of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWidthDescr) + ] + public int Width { + get { + return width; + } + set { + SetBounds(x, y, value, height, BoundsSpecified.Width); + } + } + + /// + /// The current exStyle of the hWnd + /// + /// + private int WindowExStyle { + get { + return unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE)); + } + set { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE, new HandleRef(null, (IntPtr)value)); + } + } + + /// + /// The current style of the hWnd + /// + /// + internal int WindowStyle { + get { + return unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE)); + } + set { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)value)); + } + } + + /// + /// + /// The target of Win32 window messages. + /// + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWindowTargetDescr) + ] + public IWindowTarget WindowTarget { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + return window.WindowTarget; + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + set { + window.WindowTarget = value; + } + } + + /// + /// The current text of the Window; if the window has not yet been created, stores it in the control. + /// If the window has been created, stores the text in the underlying win32 control. + /// This property should be used whenever you want to get at the win32 control's text. For all other cases, + /// use the Text property - but note that this is overridable, and any of your code that uses it will use + /// the overridden version in controls that subclass your own. + /// + internal virtual string WindowText { + get { + + if (!IsHandleCreated) { + if (text == null) { + return ""; + } + else { + return text; + } + } + + using (new MultithreadSafeCallScope()) { + + // it's okay to call GetWindowText cross-thread. + // + + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(window, Handle)); + + // Check to see if the system supports DBCS character + // if so, double the length of the buffer. + if (SystemInformation.DbcsEnabled) { + textLen = (textLen * 2) + 1; + } + StringBuilder sb = new StringBuilder(textLen + 1); + UnsafeNativeMethods.GetWindowText(new HandleRef(window, Handle), sb, sb.Capacity); + return sb.ToString(); + } + } + set { + if (value == null) value = ""; + if (!WindowText.Equals(value)) { + if (IsHandleCreated) { + UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value); + } + else { + if (value.Length == 0) { + text = null; + } + else { + text = value; + } + } + } + } + } + + + + /// + /// + /// Occurs when the control is clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnClickDescr)] + public event EventHandler Click { + add { + Events.AddHandler(EventClick, value); + } + remove { + Events.RemoveHandler(EventClick, value); + } + } + + + + /// + /// + /// Occurs when a new control is added. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlOnControlAddedDescr) + ] + public event ControlEventHandler ControlAdded { + add { + Events.AddHandler(EventControlAdded, value); + } + remove { + Events.RemoveHandler(EventControlAdded, value); + } + } + + + /// + /// + /// Occurs when a control is removed. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlOnControlRemovedDescr) + ] + public event ControlEventHandler ControlRemoved { + add { + Events.AddHandler(EventControlRemoved, value); + } + remove { + Events.RemoveHandler(EventControlRemoved, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragDropDescr)] + public event DragEventHandler DragDrop { + add { + Events.AddHandler(EventDragDrop, value); + } + remove { + Events.RemoveHandler(EventDragDrop, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragEnterDescr)] + public event DragEventHandler DragEnter { + add { + Events.AddHandler(EventDragEnter, value); + } + remove { + Events.RemoveHandler(EventDragEnter, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragOverDescr)] + public event DragEventHandler DragOver { + add { + Events.AddHandler(EventDragOver, value); + } + remove { + Events.RemoveHandler(EventDragOver, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragLeaveDescr)] + public event EventHandler DragLeave { + add { + Events.AddHandler(EventDragLeave, value); + } + remove { + Events.RemoveHandler(EventDragLeave, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnGiveFeedbackDescr)] + public event GiveFeedbackEventHandler GiveFeedback { + add { + Events.AddHandler(EventGiveFeedback, value); + } + remove { + Events.RemoveHandler(EventGiveFeedback, value); + } + } + + + /// + /// + /// Occurs when a handle is created for the control. + /// + [SRCategory(SR.CatPrivate), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnCreateHandleDescr)] + public event EventHandler HandleCreated { + add { + Events.AddHandler(EventHandleCreated, value); + } + remove { + Events.RemoveHandler(EventHandleCreated, value); + } + } + + + /// + /// + /// Occurs when the control's handle is destroyed. + /// + [SRCategory(SR.CatPrivate), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnDestroyHandleDescr)] + public event EventHandler HandleDestroyed { + add { + Events.AddHandler(EventHandleDestroyed, value); + } + remove { + Events.RemoveHandler(EventHandleDestroyed, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnHelpDescr)] + public event HelpEventHandler HelpRequested { + add { + Events.AddHandler(EventHelpRequested, value); + } + remove { + Events.RemoveHandler(EventHelpRequested, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnInvalidateDescr)] + public event InvalidateEventHandler Invalidated { + add { + Events.AddHandler(EventInvalidated, value); + } + remove { + Events.RemoveHandler(EventInvalidated, value); + } + } + + /// + [Browsable(false)] + public Size PreferredSize { + get { return GetPreferredSize(Size.Empty); } + } + + /// + [ + SRDescription(SR.ControlPaddingDescr), + SRCategory(SR.CatLayout), + Localizable(true) + ] + public Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + if (value != Padding) { + CommonProperties.SetPadding(this, value); + // Ideally we are being laid out by a LayoutEngine that cares about our preferred size. + // We set our LAYOUTISDIRTY bit and ask our parent to refresh us. + SetState(STATE_LAYOUTISDIRTY, true); + using (new LayoutTransaction(ParentInternal, this, PropertyNames.Padding)) { + OnPaddingChanged(EventArgs.Empty); + } + + if(GetState(STATE_LAYOUTISDIRTY)) { + // The above did not cause our layout to be refreshed. We explicitly refresh our + // layout to ensure that any children are repositioned to account for the change + // in padding. + LayoutTransaction.DoLayout(this, this, PropertyNames.Padding); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr)] + public event EventHandler PaddingChanged { + add { + Events.AddHandler(EventPaddingChanged, value); + } + remove { + Events.RemoveHandler(EventPaddingChanged, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ControlOnPaintDescr)] + public event PaintEventHandler Paint { + add { + Events.AddHandler(EventPaint, value); + } + remove { + Events.RemoveHandler(EventPaint, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnQueryContinueDragDescr)] + public event QueryContinueDragEventHandler QueryContinueDrag { + add { + Events.AddHandler(EventQueryContinueDrag, value); + } + remove { + Events.RemoveHandler(EventQueryContinueDrag, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnQueryAccessibilityHelpDescr)] + public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + Events.AddHandler(EventQueryAccessibilityHelp, value); + } + remove { + Events.RemoveHandler(EventQueryAccessibilityHelp, value); + } + } + + /// + /// + /// Occurs when the control is double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnDoubleClickDescr)] + public event EventHandler DoubleClick { + add { + Events.AddHandler(EventDoubleClick, value); + } + remove { + Events.RemoveHandler(EventDoubleClick, value); + } + } + + /// + /// + /// Occurs when the control is entered. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnEnterDescr)] + public event EventHandler Enter { + add { + Events.AddHandler(EventEnter, value); + } + remove { + Events.RemoveHandler(EventEnter, value); + } + } + + /// + /// + /// Occurs when the control receives focus. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnGotFocusDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler GotFocus { + add { + Events.AddHandler(EventGotFocus, value); + } + remove { + Events.RemoveHandler(EventGotFocus, value); + } + } + + /// + /// + /// Occurs when a key is pressed down while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyDownDescr)] + public event KeyEventHandler KeyDown { + add { + Events.AddHandler(EventKeyDown, value); + } + remove { + Events.RemoveHandler(EventKeyDown, value); + } + } + + + /// + /// + /// Occurs when a key is pressed while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyPressDescr)] + public event KeyPressEventHandler KeyPress { + add { + Events.AddHandler(EventKeyPress, value); + } + remove { + Events.RemoveHandler(EventKeyPress, value); + } + } + + + /// + /// + /// Occurs when a key is released while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyUpDescr)] + public event KeyEventHandler KeyUp { + add { + Events.AddHandler(EventKeyUp, value); + } + remove { + Events.RemoveHandler(EventKeyUp, value); + } + } + + + /// + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnLayoutDescr)] + public event LayoutEventHandler Layout { + add { + Events.AddHandler(EventLayout, value); + } + remove { + Events.RemoveHandler(EventLayout, value); + } + } + + + /// + /// + /// Occurs when the control is left. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLeaveDescr)] + public event EventHandler Leave { + add { + Events.AddHandler(EventLeave, value); + } + remove { + Events.RemoveHandler(EventLeave, value); + } + } + + /// + /// + /// Occurs when the control loses focus. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLostFocusDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler LostFocus { + add { + Events.AddHandler(EventLostFocus, value); + } + remove { + Events.RemoveHandler(EventLostFocus, value); + } + } + + /// + /// + /// Occurs when the control is mouse clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseClickDescr)] + public event MouseEventHandler MouseClick { + add { + Events.AddHandler(EventMouseClick, value); + } + remove { + Events.RemoveHandler(EventMouseClick, value); + } + } + + + /// + /// + /// Occurs when the control is mouse double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseDoubleClickDescr)] + public event MouseEventHandler MouseDoubleClick { + add { + Events.AddHandler(EventMouseDoubleClick, value); + } + remove { + Events.RemoveHandler(EventMouseDoubleClick, value); + } + } + + + /// + /// + /// Occurs when the control loses mouse Capture. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseCaptureChangedDescr)] + public event EventHandler MouseCaptureChanged { + add { + Events.AddHandler(EventMouseCaptureChanged, value); + } + remove { + Events.RemoveHandler(EventMouseCaptureChanged, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is + /// pressed. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseDownDescr)] + public event MouseEventHandler MouseDown { + add { + Events.AddHandler(EventMouseDown, value); + } + remove { + Events.RemoveHandler(EventMouseDown, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer enters the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseEnterDescr)] + public event EventHandler MouseEnter { + add { + Events.AddHandler(EventMouseEnter, value); + } + remove { + Events.RemoveHandler(EventMouseEnter, value); + } + } + + /// + /// + /// Occurs when the mouse pointer leaves the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseLeaveDescr)] + public event EventHandler MouseLeave { + add { + Events.AddHandler(EventMouseLeave, value); + } + remove { + Events.RemoveHandler(EventMouseLeave, value); + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this control is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// This event is raised before the top level parent window recieves WM_DPICHANGED message. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnDpiChangedBeforeParentDescr)] + public event EventHandler DpiChangedBeforeParent { + add { + Events.AddHandler(EventDpiChangedBeforeParent, value); + } + remove { + Events.RemoveHandler(EventDpiChangedBeforeParent, value); + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this control is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// This message is received after the top levet parent window recieves WM_DPICHANGED message. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnDpiChangedAfterParentDescr)] + public event EventHandler DpiChangedAfterParent { + add { + Events.AddHandler(EventDpiChangedAfterParent, value); + } + remove { + Events.RemoveHandler(EventDpiChangedAfterParent, value); + } + } + + /// + /// + /// Occurs when the mouse pointer hovers over the contro. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseHoverDescr)] + public event EventHandler MouseHover { + add { + Events.AddHandler(EventMouseHover, value); + } + remove { + Events.RemoveHandler(EventMouseHover, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer is moved over the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseMoveDescr)] + public event MouseEventHandler MouseMove { + add { + Events.AddHandler(EventMouseMove, value); + } + remove { + Events.RemoveHandler(EventMouseMove, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is released. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseUpDescr)] + public event MouseEventHandler MouseUp { + add { + Events.AddHandler(EventMouseUp, value); + } + remove { + Events.RemoveHandler(EventMouseUp, value); + } + } + + + /// + /// + /// Occurs when the mouse wheel moves while the control has focus. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseWheelDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event MouseEventHandler MouseWheel { + add { + Events.AddHandler(EventMouseWheel, value); + } + remove { + Events.RemoveHandler(EventMouseWheel, value); + } + } + + + /// + /// + /// Occurs when the control is moved. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnMoveDescr)] + public event EventHandler Move { + add { + Events.AddHandler(EventMove, value); + } + remove { + Events.RemoveHandler(EventMove, value); + } + } + + /// + /// Raised to preview a key down event + /// + [SRCategory(SR.CatKey), SRDescription(SR.PreviewKeyDownDescr)] + public event PreviewKeyDownEventHandler PreviewKeyDown { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + add { + Events.AddHandler(EventPreviewKeyDown, value); + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + remove { + Events.RemoveHandler(EventPreviewKeyDown, value); + } + } + + + /// + /// + /// Occurs when the control is resized. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnResizeDescr), + EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler Resize { + add { + Events.AddHandler(EventResize, value); + } + remove { + Events.RemoveHandler(EventResize, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnChangeUICuesDescr)] + public event UICuesEventHandler ChangeUICues { + add { + Events.AddHandler(EventChangeUICues, value); + } + remove { + Events.RemoveHandler(EventChangeUICues, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnStyleChangedDescr)] + public event EventHandler StyleChanged { + add { + Events.AddHandler(EventStyleChanged, value); + } + remove { + Events.RemoveHandler(EventStyleChanged, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnSystemColorsChangedDescr)] + public event EventHandler SystemColorsChanged { + add { + Events.AddHandler(EventSystemColorsChanged, value); + } + remove { + Events.RemoveHandler(EventSystemColorsChanged, value); + } + } + + /// + /// + /// Occurs when the control is validating. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatingDescr)] + public event CancelEventHandler Validating { + add { + Events.AddHandler(EventValidating, value); + } + remove { + Events.RemoveHandler(EventValidating, value); + } + } + + + /// + /// + /// Occurs when the control is done validating. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatedDescr)] + public event EventHandler Validated { + add { + Events.AddHandler(EventValidated, value); + } + remove { + Events.RemoveHandler(EventValidated, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal void AccessibilityNotifyClients(AccessibleEvents accEvent, int childID) { + AccessibilityNotifyClients(accEvent, NativeMethods.OBJID_CLIENT, childID); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void AccessibilityNotifyClients(AccessibleEvents accEvent, int objectID, int childID) { + if (IsHandleCreated) { + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), objectID, childID + 1); + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + private IntPtr ActiveXMergeRegion(IntPtr region) { + return ActiveXInstance.MergeRegion(region); + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXOnFocus(bool focus) { + ActiveXInstance.OnFocus(focus); + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXViewChanged() { + ActiveXInstance.ViewChangedInternal(); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// + /// This is called by regasm to register a control as an ActiveX control. + /// + [ComRegisterFunction()] + protected static void ActiveXRegister(Type type) { + + if (type == null) { + throw new ArgumentNullException("type"); + } + + // If the user is not registering an AX control, then + // bail now. + // + if (!typeof(Control).IsAssignableFrom(type)) { + return; + } + + // Add the flags that define us as a control to the rest of the + // world. + // + RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID", true).CreateSubKey("{" + type.GUID.ToString() + "}"); + RegistryKey key = clsidKey.CreateSubKey("Control"); + key.Close(); + + key = clsidKey.CreateSubKey("Implemented Categories"); + RegistryKey subKey = key.CreateSubKey("{40FC6ED4-2438-11CF-A3DB-080036F12502}"); // CATID_Control + subKey.Close(); + key.Close(); + + // Calculate MiscStatus bits. Note that this is a static version + // of GetMiscStatus in ActiveXImpl below. Keep them in sync! + // + int miscStatus = NativeMethods.OLEMISC_ACTIVATEWHENVISIBLE | + NativeMethods.OLEMISC_INSIDEOUT | + NativeMethods.OLEMISC_SETCLIENTSITEFIRST | + NativeMethods.OLEMISC_RECOMPOSEONRESIZE; + if (typeof(IButtonControl).IsAssignableFrom(type)) { + miscStatus |= NativeMethods.OLEMISC_ACTSLIKEBUTTON; + } + + key = clsidKey.CreateSubKey("MiscStatus"); + key.SetValue("", miscStatus.ToString()); + key.Close(); + + // Now add the typelib information. Many containers don't + // host Active X controls without a typelib in the registry + // (visual basic won't even show it in the control dialog). + // + Guid typeLibGuid = Marshal.GetTypeLibGuidForAssembly(type.Assembly); + Version assemblyVer = type.Assembly.GetName().Version; + + if (typeLibGuid != Guid.Empty) { + key = clsidKey.CreateSubKey("TypeLib"); + key.SetValue("", "{" + typeLibGuid.ToString() + "}"); + key.Close(); + key = clsidKey.CreateSubKey("Version"); + key.SetValue("", assemblyVer.Major.ToString() + "." + assemblyVer.Minor.ToString()); + key.Close(); + } + + clsidKey.Close(); + } + #endif + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXUpdateBounds(ref int x, ref int y, ref int width, ref int height, int flags) { + ActiveXInstance.UpdateBounds(ref x, ref y, ref width, ref height, flags); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// + /// This is called by regasm to un-register a control as an ActiveX control. + /// + [ComUnregisterFunction()] + protected static void ActiveXUnregister(Type type) { + + if (type == null) { + throw new ArgumentNullException("type"); + } + + // If the user is not unregistering an AX control, then + // bail now. + // + if (!typeof(Control).IsAssignableFrom(type)) { + return; + } + + RegistryKey clsidKey = null; + + // Unregistration should be very robust and unregister what it can. We eat all exceptions here. + // + try { + clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID", true).CreateSubKey("{" + type.GUID.ToString() + "}"); + + try { + clsidKey.DeleteSubKeyTree("Control"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("Implemented Categories"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("MiscStatus"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("TypeLib"); + } + catch { + } + } + finally { + if (clsidKey != null) { + clsidKey.Close(); + } + } + } + #endif + + /// + /// Assigns a new parent control. Sends out the appropriate property change + /// notifications for properties that are affected by the change of parent. + /// + internal virtual void AssignParent(Control value) { + + // Adopt the parent's required scaling bits + if (value != null) { + RequiredScalingEnabled = value.RequiredScalingEnabled; + } + + if (CanAccessProperties) { + // Store the old values for these properties + // + Font oldFont = Font; + Color oldForeColor = ForeColor; + Color oldBackColor = BackColor; + RightToLeft oldRtl = RightToLeft; + bool oldEnabled = Enabled; + bool oldVisible = Visible; + + // Update the parent + // + parent = value; + OnParentChanged(EventArgs.Empty); + + if (GetAnyDisposingInHierarchy()) { + return; + } + + // Compare property values with new parent to old values + // + if (oldEnabled != Enabled) { + OnEnabledChanged(EventArgs.Empty); + } + + // VSWhidbey 320131 + // When a control seems to be going from invisible -> visible, + // yet its parent is being set to null and it's not top level, do not raise OnVisibleChanged. + bool newVisible = Visible; + + if (oldVisible != newVisible && !(!oldVisible && newVisible && parent == null && !GetTopLevel())) { + OnVisibleChanged(EventArgs.Empty); + } + if (!oldFont.Equals(Font)) { + OnFontChanged(EventArgs.Empty); + } + if (!oldForeColor.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + if (!oldBackColor.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + if (oldRtl != RightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + if (Properties.GetObject(PropBindingManager) == null && this.Created) { + // We do not want to call our parent's BindingContext property here. + // We have no idea if us or any of our children are using data binding, + // and invoking the property would just create the binding manager, which + // we don't need. We just blindly notify that the binding manager has + // changed, and if anyone cares, they will do the comparison at that time. + // + OnBindingContextChanged(EventArgs.Empty); + } + } + else { + parent = value; + OnParentChanged(EventArgs.Empty); + } + + SetState(STATE_CHECKEDHOST, false); + if (ParentInternal != null) ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.All); + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnParentChangedDescr)] + public event EventHandler ParentChanged { + add { + Events.AddHandler(EventParent, value); + } + remove { + Events.RemoveHandler(EventParent, value); + } + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. The delegate is called asynchronously and this + /// method returns immediately. You may call this from any thread, even the + /// thread that owns the control's handle. If the control's handle doesn't + /// exist yet, this will follow up the control's parent chain until it finds a + /// control or form that does have a window handle. If no appropriate handle + /// can be found, BeginInvoke will throw an exception. Exceptions within the + /// delegate method are considered untrapped and will be sent to the + /// application's untrapped exception handler. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IAsyncResult BeginInvoke(Delegate method) { + return BeginInvoke(method, null); + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. The delegate is called asynchronously and this + /// method returns immediately. You may call this from any thread, even the + /// thread that owns the control's handle. If the control's handle doesn't + /// exist yet, this will follow up the control's parent chain until it finds a + /// control or form that does have a window handle. If no appropriate handle + /// can be found, BeginInvoke will throw an exception. Exceptions within the + /// delegate method are considered untrapped and will be sent to the + /// application's untrapped exception handler. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + public IAsyncResult BeginInvoke(Delegate method, params Object[] args) { + using (new MultithreadSafeCallScope()) { + Control marshaler = FindMarshalingControl(); + return(IAsyncResult)marshaler.MarshaledInvoke(this, method, args, false); + } + } + + internal void BeginUpdateInternal() { + if (!IsHandleCreated) { + return; + } + if (updateCount == 0) SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); + updateCount++; + } + + /// + /// + /// Brings this control to the front of the zorder. + /// + public void BringToFront() { + if (parent != null) { + parent.Controls.SetChildIndex(this, 0); + } + else if (IsHandleCreated && GetTopLevel() && SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle))) { + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.HWND_TOP, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + } + + /// + /// Specifies whether this control can process the mnemonic or not. A condition to process a mnemonic is that + /// all controls in the parent chain can do it too, but since the semantics for this function can be overriden, + /// we need to call the method on the parent 'recursively' (not exactly since it is not necessarily the same method). + /// + internal virtual bool CanProcessMnemonic() { +#if DEBUG + TraceCanProcessMnemonic(); +#endif + if( !this.Enabled || !this.Visible) { + return false; + } + + if( this.parent != null ){ + return this.parent.CanProcessMnemonic(); + } + + return true; + } + + // Package scope to allow AxHost to override + // + internal virtual bool CanSelectCore() { + if ((controlStyle & ControlStyles.Selectable) != ControlStyles.Selectable) { + return false; + } + + for (Control ctl = this; ctl != null; ctl = ctl.parent) { + if (!ctl.Enabled || !ctl.Visible) { + return false; + } + } + + return true; + } + + /// + /// Searches the parent/owner tree for bottom to find any instance + /// of toFind in the parent/owner tree. + /// + internal static void CheckParentingCycle(Control bottom, Control toFind) { + Form lastOwner = null; + Control lastParent = null; + + for (Control ctl = bottom; ctl != null; ctl = ctl.ParentInternal) { + lastParent = ctl; + if (ctl == toFind) { + throw new ArgumentException(SR.GetString(SR.CircularOwner)); + } + } + + if (lastParent != null) { + if (lastParent is Form) { + Form f = (Form)lastParent; + for (Form form = f; form != null; form = form.OwnerInternal) { + lastOwner = form; + if (form == toFind) { + throw new ArgumentException(SR.GetString(SR.CircularOwner)); + } + } + } + } + + if (lastOwner != null) { + if (lastOwner.ParentInternal != null) { + CheckParentingCycle(lastOwner.ParentInternal, toFind); + } + } + } + /// + /// + /// + private void ChildGotFocus(Control child) { + if (IsActiveX) { + ActiveXOnFocus(true); + } + if (parent != null) { + parent.ChildGotFocus(child); + } + } + + /// + /// + /// Verifies if a control is a child of this control. + /// + public bool Contains(Control ctl) { + while (ctl != null) { + ctl = ctl.ParentInternal; + if (ctl == null) { + return false; + } + if (ctl == this) { + return true; + } + } + return false; + } + + /// + /// + /// constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual AccessibleObject CreateAccessibilityInstance() { + return new ControlAccessibleObject(this); + } + + /// + /// + /// Constructs the new instance of the Controls collection objects. Subclasses + /// should not call base.CreateControlsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual ControlCollection CreateControlsInstance() { + return new System.Windows.Forms.Control.ControlCollection(this); + } + + /// + /// + /// Creates a Graphics for this control. The control's brush, font, foreground + /// color and background color become the default values for the Graphics. + /// The returned Graphics must be disposed through a call to its dispose() + /// method when it is no longer needed. The Graphics Object is only valid for + /// the duration of the current window's message. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public System.Drawing.Graphics CreateGraphics() { + using (new MultithreadSafeCallScope()) + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "CreateGraphicsForControl Demanded"); + IntSecurity.CreateGraphicsForControl.Demand(); + return CreateGraphicsInternal(); + } + } + + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal System.Drawing.Graphics CreateGraphicsInternal() { + return Graphics.FromHwndInternal(this.Handle); + } + + /// + /// + /// Creates a handle for this control. This method is called by the .NET Framework, this should + /// not be called. Inheriting classes should always call base.createHandle when + /// overriding this method. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows) + ] + protected virtual void CreateHandle() { + IntPtr userCookie = IntPtr.Zero; + + if (GetState(STATE_DISPOSED)) { + throw new System.ObjectDisposedException(GetType().Name); + } + + if (GetState(STATE_CREATINGHANDLE)) { + return; + } + + Rectangle originalBounds; + + try { + SetState(STATE_CREATINGHANDLE, true); + + originalBounds = this.Bounds; + + if (Application.UseVisualStyles) { + // Activate theming scope to get theming for controls at design time and when hosted in browser. + // NOTE: If a theming context is already active, this call is very fast, so shouldn't be a perf issue. + userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + } + + CreateParams cp = CreateParams; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + + // Adjust for scrolling of parent... + // + if (parent != null) { + Rectangle parentClient = parent.ClientRectangle; + + if (!parentClient.IsEmpty) { + if (cp.X != NativeMethods.CW_USEDEFAULT) { + cp.X -= parentClient.X; + } + if (cp.Y != NativeMethods.CW_USEDEFAULT) { + cp.Y -= parentClient.Y; + } + } + } + + // And if we are WS_CHILD, ensure we have a parent handle. + // + if (cp.Parent == IntPtr.Zero && (cp.Style & NativeMethods.WS_CHILD) != 0) { + Debug.Assert((cp.ExStyle & NativeMethods.WS_EX_MDICHILD) == 0, "Can't put MDI child forms on the parking form"); + Application.ParkHandle(cp, this.DpiAwarenessContext); + } + + window.CreateHandle(cp); + + UpdateReflectParent(true); + + } + finally { + SetState(STATE_CREATINGHANDLE, false); + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + // VSWhidbey #121382 - For certain controls (e.g., ComboBox) CreateWindowEx + // may cause the control to resize. WM_SETWINDOWPOSCHANGED takes care of + // the control being resized, but our layout container may need a refresh as well. + if (this.Bounds != originalBounds) { + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + } + + /// + /// + /// Forces the creation of the control. This includes the creation of the handle, + /// and any child controls. + /// + public void CreateControl() { + bool controlIsAlreadyCreated = this.Created; + CreateControl(false); + + if (Properties.GetObject(PropBindingManager) == null && ParentInternal != null && !controlIsAlreadyCreated) { + // We do not want to call our parent's BindingContext property here. + // We have no idea if us or any of our children are using data binding, + // and invoking the property would just create the binding manager, which + // we don't need. We just blindly notify that the binding manager has + // changed, and if anyone cares, they will do the comparison at that time. + OnBindingContextChanged(EventArgs.Empty); + } + } + + /// + /// Forces the creation of the control. This includes the creation of the handle, + /// and any child controls. + /// + /// Determines whether we should create the handle after checking the Visible + /// property of the control or not. + /// + /// + internal void CreateControl(bool fIgnoreVisible) { + bool ready = (state & STATE_CREATED) == 0; + + // PERF: Only "create" the control if it is + // : visible. This has the effect of delayed handle creation of + // : hidden controls. + // + ready = ready && Visible; + + if (ready || fIgnoreVisible) { + state |= STATE_CREATED; + bool createdOK = false; + try { + if (!IsHandleCreated) CreateHandle(); + + // 58041 - must snapshot this array because + // z-order updates from Windows may rearrange it! + // + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + Control[] controlSnapshot = new Control[controlsCollection.Count]; + controlsCollection.CopyTo(controlSnapshot, 0); + + foreach(Control ctl in controlSnapshot) { + if (ctl.IsHandleCreated) { + ctl.SetParentHandle(Handle); + } + ctl.CreateControl(fIgnoreVisible); + } + } + + createdOK = true; + } + finally { + if (!createdOK) + state &= (~STATE_CREATED); + } + OnCreateControl(); + } + } + + + /// + /// + /// Sends the message to the default window proc. + /// + /* Primarily here for Form to override */ + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void DefWndProc(ref Message m) { + window.DefWndProc(ref m); + } + + + /// + /// + /// Destroys the handle associated with this control. Inheriting classes should + /// always call base.destroyHandle. + /// + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void DestroyHandle() { + + if (RecreatingHandle) { + if (threadCallbackList != null) { + // See if we have a thread marshaling request pending. If so, we will need to + // re-post it after recreating the handle. + // + lock (threadCallbackList) { + if (threadCallbackMessage != 0) { + NativeMethods.MSG msg = new NativeMethods.MSG(); + if (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, Handle), threadCallbackMessage, + threadCallbackMessage, NativeMethods.PM_NOREMOVE)) { + + SetState(STATE_THREADMARSHALLPENDING, true); + } + } + } + } + } + + // If we're not recreating the handle, then any items in the thread callback list will + // be orphaned. An orphaned item is bad, because it will cause the thread to never + // wake up. So, we put exceptions into all these items and wake up all threads. + // If we are recreating the handle, then we're fine because recreation will re-post + // the thread callback message to the new handle for us. + // + if (!RecreatingHandle) { + if (threadCallbackList != null) { + lock (threadCallbackList) { + Exception ex = new System.ObjectDisposedException(GetType().Name); + + while (threadCallbackList.Count > 0) { + ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue(); + entry.exception = ex; + entry.Complete(); + } + } + } + } + + if (0 != (NativeMethods.WS_EX_MDICHILD & (int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(window, InternalHandle), NativeMethods.GWL_EXSTYLE))) { + UnsafeNativeMethods.DefMDIChildProc(InternalHandle, NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + } + else { + window.DestroyHandle(); + } + + trackMouseEvent = null; + } + + /// + /// + /// Disposes of the resources (other than memory) used by the + /// + /// . + /// + protected override void Dispose(bool disposing) { + if (GetState(STATE_OWNCTLBRUSH)) { + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + Properties.SetObject(PropBackBrush, null); + } + } + + //set reflectparent = null regardless of whether we are in the finalizer thread or not. + UpdateReflectParent(false); + if (disposing) { + if (GetState(STATE_DISPOSING)) { + return; + } + + if (GetState(STATE_CREATINGHANDLE)) { + throw new InvalidOperationException(SR.GetString(SR.ClosingWhileCreatingHandle, "Dispose")); + // I imagine most subclasses will get themselves in a half disposed state + // if this exception is thrown, but things will be equally broken if we ignore this error, + // and this way at least the user knows what they did wrong. + } + + SetState(STATE_DISPOSING, true); + this.SuspendLayout(); + try { + DisposeAxControls(); + + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + contextMenu.Disposed -= new EventHandler(DetachContextMenu); + } + + ResetBindings(); + + if (IsHandleCreated) DestroyHandle(); + + if (parent != null) { + parent.Controls.Remove(this); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + ctl.parent = null; + ctl.Dispose(); + } + Properties.SetObject(PropControlsCollection, null); + } + + base.Dispose(disposing); + } + finally { + this.ResumeLayout(false); + SetState(STATE_DISPOSING, false); + SetState(STATE_DISPOSED, true); + } + } + else { + +#if FINALIZATION_WATCH + if (!GetState(STATE_DISPOSED)) { + Debug.Fail("Control of type '" + GetType().FullName +"' is being finalized that wasn't disposed\n" + allocationSite); + } +#endif + // This same post is done in NativeWindow's finalize method, so if you change + // it, change it there too. + // + if (window != null) { + window.ForceExitMessageLoop(); + } + base.Dispose(disposing); + } + } + + // Package scope to allow AxHost to override. + // + internal virtual void DisposeAxControls() { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].DisposeAxControls(); + } + } + } + + /// + /// + /// Begins a drag operation. The allowedEffects determine which + /// drag operations can occur. If the drag operation needs to interop + /// with applications in another process, data should either be + /// a base managed class (String, Bitmap, or Metafile) or some Object + /// that implements System.Runtime.Serialization.ISerializable. data can also be any Object that + /// implements System.Windows.Forms.IDataObject. + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + public DragDropEffects DoDragDrop(Object data, DragDropEffects allowedEffects) { + int[] finalEffect = new int[] {(int)DragDropEffects.None}; + UnsafeNativeMethods.IOleDropSource dropSource = new DropSource( this ); + IComDataObject dataObject = null; + + if (data is IComDataObject) { + dataObject = (IComDataObject)data; + } + else { + + DataObject iwdata = null; + if (data is IDataObject) { + iwdata = new DataObject((IDataObject)data); + } + else { + iwdata = new DataObject(); + iwdata.SetData(data); + } + dataObject = (IComDataObject)iwdata; + } + + try { + SafeNativeMethods.DoDragDrop(dataObject, dropSource, (int)allowedEffects, finalEffect); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + + return(DragDropEffects)finalEffect[0]; + } + + /// + /// + // Trinity are currently calling IViewObject::Draw in order to render controls on Word & Excel + // before they are in place active. However this method is private and they need a public way to do this. + // This means that we will need to add a public method to control that supports rendering to a Bitmap: + // public virtual void DrawToBitmap(Bitmap bmp, RectangleF targetBounds) + // where target bounds is the bounds within which the control should render. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly"), + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), // Using Bitmap instead of Image intentionally + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // targetBounds is the name of the param passed in. + // So we don't have to localize it. + UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows) + ] + public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) { + + if (bitmap == null) { + throw new ArgumentNullException("bitmap"); + } + + if (targetBounds.Width <= 0 || targetBounds.Height <= 0 + || targetBounds.X < 0 || targetBounds.Y < 0) { + throw new ArgumentException("targetBounds"); + } + + if (!IsHandleCreated) { + CreateHandle(); + } + + + int width = Math.Min(this.Width, targetBounds.Width); + int height = Math.Min(this.Height, targetBounds.Height); + + using (Bitmap image = new Bitmap(width, height, bitmap.PixelFormat)) { + using (Graphics g = Graphics.FromImage(image)) { + IntPtr hDc = g.GetHdc(); + //send the actual wm_print message + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.WM_PRINT, (IntPtr)hDc, + (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT)); + + //now BLT the result to the destination bitmap. + using (Graphics destGraphics = Graphics.FromImage(bitmap)) { + IntPtr desthDC = destGraphics.GetHdc(); + SafeNativeMethods.BitBlt(new HandleRef(destGraphics, desthDC), targetBounds.X, targetBounds.Y, width, height, + new HandleRef(g, hDc), 0, 0, 0xcc0020); + destGraphics.ReleaseHdcInternal(desthDC); + } + + g.ReleaseHdcInternal(hDc); + } + } + } + + /// + /// + /// Retrieves the return value of the asynchronous operation + /// represented by the IAsyncResult interface passed. If the + /// async operation has not been completed, this function will + /// block until the result is available. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public Object EndInvoke(IAsyncResult asyncResult) { + using (new MultithreadSafeCallScope()) + { + if (asyncResult == null) { + throw new ArgumentNullException("asyncResult"); + } + + ThreadMethodEntry entry = asyncResult as ThreadMethodEntry; + if (entry == null) { + throw new ArgumentException(SR.GetString(SR.ControlBadAsyncResult),"asyncResult"); + } + Debug.Assert(this == entry.caller, "Called BeginInvoke on one control, and the corresponding EndInvoke on a different control"); + + if (!asyncResult.IsCompleted) { + int pid; // ignored + Control marshaler = FindMarshalingControl(); + if (SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(marshaler, marshaler.Handle), out pid) == SafeNativeMethods.GetCurrentThreadId()) { + marshaler.InvokeMarshaledCallbacks(); + } + else { + marshaler = entry.marshaler; + marshaler.WaitForWaitHandle(asyncResult.AsyncWaitHandle); + } + } + + Debug.Assert(asyncResult.IsCompleted, "Why isn't this asyncResult done yet?"); + if (entry.exception != null) { + throw entry.exception; + } + return entry.retVal; + } + } + + internal bool EndUpdateInternal() { + return EndUpdateInternal(true); + } + + internal bool EndUpdateInternal(bool invalidate) { + if (updateCount > 0) { + Debug.Assert(IsHandleCreated, "Handle should be created by now"); + updateCount--; + if (updateCount == 0) { + SendMessage(NativeMethods.WM_SETREDRAW, -1, 0); + if (invalidate) { + Invalidate(); + } + + } + return true; + } + else { + return false; + } + } + + /// + /// + /// Retrieves the form that this control is on. The control's parent + /// may not be the same as the form. + /// + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + public Form FindForm() { + return FindFormInternal(); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal Form FindFormInternal() { + Control cur = this; + while (cur != null && !(cur is Form) ) { + cur = cur.ParentInternal; + } + return (Form)cur; + } + + /// + /// Attempts to find a control Object that we can use to marshal + /// calls. We must marshal calls to a control with a window + /// handle, so we traverse up the parent chain until we find one. + /// Failing that, we just return ouselves. + /// + /// + private Control FindMarshalingControl() { + // + lock(this) { + Control c = this; + + while (c != null && !c.IsHandleCreated) { + Control p = c.ParentInternal; + c = p; + } + + if (c == null) { + // No control with a created handle. We + // just use our own control. MarshaledInvoke + // will throw an exception because there + // is no handle. + // + c = this; + } + else { + Debug.Assert(c.IsHandleCreated, "FindMarshalingControl chose a bad control."); + } + + return(Control)c; + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool GetTopLevel() { + return(state & STATE_TOPLEVEL) != 0; + } + + /// + /// Used by AxHost to fire the CreateHandle event. + /// + /// + internal void RaiseCreateHandleEvent(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventHandleCreated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseKeyEvent(object key, KeyEventArgs e) { + KeyEventHandler handler = (KeyEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseMouseEvent(object key, MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Sets focus to the control. + /// Attempts to set focus to this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public bool Focus() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::Focus - " + this.Name); + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + //here, we call our internal method (which form overrides) + //see comments in FocusInternal + // + return FocusInternal(); + } + + /// + /// Internal method for setting focus to the control. + /// Form overrides this method - because MDI child forms + /// need to be focused by calling the MDIACTIVATE message. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal virtual bool FocusInternal() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::FocusInternal - " + this.Name); + if (CanFocus){ + UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle)); + } + if (Focused && this.ParentInternal != null) { + IContainerControl c = this.ParentInternal.GetContainerControlInternal(); + + if (c != null) { + if (c is ContainerControl) { + ((ContainerControl)c).SetActiveControlInternal(this); + } + else { + c.ActiveControl = this; + } + } + } + + + return Focused; + } + + /// + /// + /// Returns the control that is currently associated with handle. + /// This method will search up the HWND parent chain until it finds some + /// handle that is associated with with a control. This method is more + /// robust that fromHandle because it will correctly return controls + /// that own more than one handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Control FromChildHandle(IntPtr handle) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + return FromChildHandleInternal(handle); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static Control FromChildHandleInternal(IntPtr handle) { + while (handle != IntPtr.Zero) { + Control ctl = FromHandleInternal(handle); + if (ctl != null) return ctl; + handle = UnsafeNativeMethods.GetAncestor(new HandleRef(null, handle), NativeMethods.GA_PARENT); + } + return null; + } + + /// + /// + /// Returns the control that is currently associated with handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Control FromHandle(IntPtr handle) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + return FromHandleInternal(handle); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static Control FromHandleInternal(IntPtr handle) { + NativeWindow w = NativeWindow.FromHandle(handle); + while (w != null && !(w is ControlNativeWindow)) { + w = w.PreviousWindow; + } + + if (w is ControlNativeWindow) { + return((ControlNativeWindow)w).GetControl(); + } + return null; + } + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal Size ApplySizeConstraints(int width, int height) { + return ApplyBoundsConstraints(0,0,width, height).Size; + } + + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal Size ApplySizeConstraints(Size proposedSize) { + return ApplyBoundsConstraints(0,0,proposedSize.Width,proposedSize.Height).Size; + } + + internal virtual Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + + // COMPAT: 529991 - in Everett we would allow you to set negative values in pre-handle mode + // in Whidbey, if you've set Min/Max size we will constrain you to 0,0. Everett apps didnt + // have min/max size on control, which is why this works. + if (MaximumSize != Size.Empty || MinimumSize != Size.Empty) { + Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(MaximumSize); + Rectangle newBounds = new Rectangle(suggestedX, suggestedY, 0,0); + + // Clip the size to maximum and inflate it to minimum as necessary. + newBounds.Size = LayoutUtils.IntersectSizes(new Size(proposedWidth,proposedHeight), maximumSize); + newBounds.Size = LayoutUtils.UnionSizes(newBounds.Size, MinimumSize); + + return newBounds; + } + + return new Rectangle(suggestedX, suggestedY, proposedWidth, proposedHeight); + } + + + + /// + /// + /// Retrieves the child control that is located at the specified client + /// coordinates. + /// + public Control GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue) { + int value = (int)skipValue; + // Since this is a Flags Enumeration... the only way to validate skipValue is by checking if its within the range. + if(value < 0 || value > 7) + { + throw new InvalidEnumArgumentException("skipValue", value, typeof(GetChildAtPointSkip)); + } + + IntPtr hwnd = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, Handle), pt.X, pt.Y, value); + + // Security Reviewed + // While doing a security review it was noticed that GetChildAtPoint + // does work to ensure that you can only gain access to children of your own control, + // but the methods it uses to determine the children demand all window permission first, + // negating the extra check. + // It is OK to return child windows for children within your own control for semitrust. + + // Hence calling the Internal methods to ByPass the Security Demand... + // for IntSecurity.ControlFromHandleOrLocation == ALLWindows. + + Control ctl = FromChildHandleInternal(hwnd); + if (ctl != null && !IsDescendant(ctl)) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + } + + return(ctl == this) ? null : ctl; + } + + + /// + /// + /// Retrieves the child control that is located at the specified client + /// coordinates. + /// + public Control GetChildAtPoint(Point pt) { + return GetChildAtPoint(pt, GetChildAtPointSkip.None); + } + + /// + /// + /// Returns the closest ContainerControl in the control's chain of + /// parent controls and forms. + /// + public IContainerControl GetContainerControl() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + return GetContainerControlInternal(); + } + + private static bool IsFocusManagingContainerControl(Control ctl) { + return ((ctl.controlStyle & ControlStyles.ContainerControl) == ControlStyles.ContainerControl && ctl is IContainerControl); + } + + /// + /// This new Internal method checks the updateCount to signify that the control is within the "BeginUpdate" and "EndUpdate" cycle. + /// Check out Vs whidbey : 310110 : for usage of this. The Treeview tries to ForceUpdate the scrollbars by calling "WM_SETREDRAW" + /// even if the control in "Begin - End" update cycle. Using thie Function we can guard against repetitively redrawing the control. + /// + internal bool IsUpdating() + { + return (updateCount > 0); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal IContainerControl GetContainerControlInternal() { + Control c = this; + + // VsWhidbey 434959 : Refer to IsContainerControl property for more details. + if (c != null && IsContainerControl) + { + c = c.ParentInternal; + } + while (c != null && (!IsFocusManagingContainerControl(c))) { + c = c.ParentInternal; + } + return (IContainerControl)c; + } + + // Essentially an Hfont; see inner class for details. + private static FontHandleWrapper GetDefaultFontHandleWrapper() { + if (defaultFontHandleWrapper == null) { + defaultFontHandleWrapper = new FontHandleWrapper(DefaultFont); + } + + return defaultFontHandleWrapper; + } + + internal IntPtr GetHRgn(Region region) { + Graphics graphics = CreateGraphicsInternal(); + IntPtr handle = region.GetHrgn(graphics); + System.Internal.HandleCollector.Add(handle, NativeMethods.CommonHandles.GDI); + graphics.Dispose(); + return handle; + } + + /// + /// + /// This is a helper method that is called by ScaleControl to retrieve the bounds + /// that the control should be scaled by. You may override this method if you + /// wish to reuse ScaleControl's scaling logic but you need to supply your own + /// bounds. The default implementation returns scaled bounds that take into + /// account the BoundsSpecified, whether the control is top level, and whether + /// the control is fixed width or auto size, and any adornments the control may have. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + + // We should not include the window adornments in our calculation, + // because windows scales them for us. + NativeMethods.RECT adornments = new NativeMethods.RECT(0, 0, 0, 0); + CreateParams cp = CreateParams; + AdjustWindowRectEx(ref adornments, cp.Style, HasMenu, cp.ExStyle); + + float dx = factor.Width; + float dy = factor.Height; + + int sx = bounds.X; + int sy = bounds.Y; + + // Don't reposition top level controls. Also, if we're in + // design mode, don't reposition the root component. + bool scaleLoc = !GetState(STATE_TOPLEVEL); + if (scaleLoc) { + ISite site = Site; + if (site != null && site.DesignMode) { + IDesignerHost host = site.GetService(typeof(IDesignerHost)) as IDesignerHost; + if (host != null && host.RootComponent == this) { + scaleLoc = false; + } + } + } + + if (scaleLoc) { + if ((specified & BoundsSpecified.X) != 0) { + sx = (int)Math.Round(bounds.X * dx); + } + + if ((specified & BoundsSpecified.Y) != 0) { + sy = (int)Math.Round(bounds.Y * dy); + } + } + + int sw = bounds.Width; + int sh = bounds.Height; + + // Do this even for auto sized controls. They'll "snap back", but it is important to size them in case + // they are anchored. + if ((controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth && (specified & BoundsSpecified.Width) != 0) { + int adornmentWidth = (adornments.right - adornments.left); + int localWidth = bounds.Width - adornmentWidth; + sw = (int)Math.Round(localWidth * dx) + adornmentWidth; + } + if ((controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight && (specified & BoundsSpecified.Height) != 0) { + int adornmentHeight = (adornments.bottom - adornments.top); + int localHeight = bounds.Height - adornmentHeight; + sh = (int)Math.Round(localHeight * dy) + adornmentHeight; + } + + return new Rectangle(sx, sy, sw, sh); + } + + private MouseButtons GetXButton(int wparam) { + switch (wparam) { + case NativeMethods.XBUTTON1: + return MouseButtons.XButton1; + case NativeMethods.XBUTTON2: + return MouseButtons.XButton2; + } + Debug.Fail("Unknown XButton: " + wparam); + return MouseButtons.None; + } + + internal virtual bool GetVisibleCore() { + // We are only visible if our parent is visible + if (!GetState(STATE_VISIBLE)) + return false; + else if (ParentInternal == null) + return true; + else + return ParentInternal.GetVisibleCore(); + } + + internal bool GetAnyDisposingInHierarchy() { + Control up = this; + bool isDisposing = false; + while (up != null) { + if (up.Disposing) { + isDisposing = true; + break; + } + up = up.parent; + } + return isDisposing; + } + + private MenuItem GetMenuItemFromHandleId(IntPtr hmenu, int item) { + MenuItem mi = null; + int id = UnsafeNativeMethods.GetMenuItemID(new HandleRef(null, hmenu), item); + if (id == unchecked((int)0xFFFFFFFF)) { + IntPtr childMenu = IntPtr.Zero; + childMenu = UnsafeNativeMethods.GetSubMenu(new HandleRef(null, hmenu), item); + int count = UnsafeNativeMethods.GetMenuItemCount(new HandleRef(null, childMenu)); + MenuItem found = null; + for (int i=0; i + /// - Returns child controls sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Returns a TabIndex sorted array of ControlTabOrderHolder objects. + /// + private ArrayList GetChildControlsTabOrderList(bool handleCreatedOnly) { + ArrayList holders = new ArrayList(); + + foreach (Control c in Controls) { + if (!handleCreatedOnly || c.IsHandleCreated) { + holders.Add(new ControlTabOrderHolder(holders.Count, c.TabIndex, c)); + } + } + + holders.Sort(new ControlTabOrderComparer()); + + return holders; + } + + /// + /// - Returns native child windows sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Child windows with no corresponding Control objects (and therefore no discernable TabIndex) + /// are sorted to the front of the list (but remain in relative z-order to one another). + /// - This version returns a sorted array of integers, representing the original z-order + /// based indexes of the native child windows. + /// + private int[] GetChildWindowsInTabOrder() { + ArrayList holders = GetChildWindowsTabOrderList(); + + int[] indexes = new int[holders.Count]; + + for (int i = 0; i < holders.Count; i++) { + indexes[i] = ((ControlTabOrderHolder) holders[i]).oldOrder; + } + + return indexes; + } + + /// + /// - Returns child controls sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - This version returns a sorted array of control references. + /// + internal Control[] GetChildControlsInTabOrder(bool handleCreatedOnly) { + ArrayList holders = GetChildControlsTabOrderList(handleCreatedOnly); + + Control[] ctls = new Control[holders.Count]; + + for (int i = 0; i < holders.Count; i++) { + ctls[i] = ((ControlTabOrderHolder) holders[i]).control; + } + + return ctls; + } + + /// + /// This class contains a control and associates it with a z-order. + /// This is used when sorting controls based on tab index first, + /// z-order second. + /// + private class ControlTabOrderHolder { + internal readonly int oldOrder; + internal readonly int newOrder; + internal readonly Control control; + + internal ControlTabOrderHolder(int oldOrder, int newOrder, Control control) { + this.oldOrder = oldOrder; + this.newOrder = newOrder; + this.control = control; + } + } + + /// + /// Used to sort controls based on tab index and z-order. + /// + private class ControlTabOrderComparer : IComparer { + int IComparer.Compare(Object x, Object y) { + ControlTabOrderHolder hx = (ControlTabOrderHolder) x; + ControlTabOrderHolder hy = (ControlTabOrderHolder) y; + + int delta = hx.newOrder - hy.newOrder; + if (delta == 0) + delta = hx.oldOrder - hy.oldOrder; + + return delta; + } + } + + /// + /// Given a native window handle, returns array of handles to window's children (in z-order). + /// + private static ArrayList GetChildWindows(IntPtr hWndParent) { + ArrayList windows = new ArrayList(); + + for (IntPtr hWndChild = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWndParent), NativeMethods.GW_CHILD); + hWndChild != IntPtr.Zero; + hWndChild = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWndChild), NativeMethods.GW_HWNDNEXT)) { + windows.Add(hWndChild); + } + + return windows; + } + + /// + /// - Returns native child windows sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Child windows with no corresponding Control objects (and therefore no discernable TabIndex) + /// are sorted to the front of the list (but remain in relative z-order to one another). + /// - Returns a TabIndex sorted array of ControlTabOrderHolder objects. + /// + private ArrayList GetChildWindowsTabOrderList() { + ArrayList holders = new ArrayList(); + ArrayList windows = GetChildWindows(this.Handle); + + foreach (IntPtr hWnd in windows) { + Control ctl = FromHandleInternal(hWnd); + int tabIndex = (ctl == null) ? -1 : ctl.TabIndex; + holders.Add(new ControlTabOrderHolder(holders.Count, tabIndex, ctl)); + } + + holders.Sort(new ControlTabOrderComparer()); + + return holders; + } + + // + + internal virtual Control GetFirstChildControlInTabOrder(bool forward) { + + ControlCollection ctlControls = (ControlCollection)this.Properties.GetObject(PropControlsCollection); + + Control found = null; + if (ctlControls != null) { + if (forward) { + for (int c = 0; c < ctlControls.Count; c++) { + if (found == null || found.tabIndex > ctlControls[c].tabIndex) { + found = ctlControls[c]; + } + } + } + else { + + // Cycle through the controls in reverse z-order looking for the one with the highest + // tab index. + // + for (int c = ctlControls.Count - 1; c >= 0; c--) { + if (found == null || found.tabIndex < ctlControls[c].tabIndex) { + found = ctlControls[c]; + } + } + } + } + return found; + + } + + /// + /// + /// Retrieves the next control in the tab order of child controls. + /// + public Control GetNextControl(Control ctl, bool forward) { + if (!Contains(ctl)) { + ctl = this; + } + + if (forward) { + ControlCollection ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + + if (ctlControls != null && ctlControls.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl))) { + Control found = ctl.GetFirstChildControlInTabOrder(/*forward=*/true); + if (found != null) { + return found; + } + } + + while (ctl != this) { + int targetIndex = ctl.tabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.parent; + + // Cycle through the controls in z-order looking for the one with the next highest + // tab index. Because there can be dups, we have to start with the existing tab index and + // remember to exclude the current control. + // + int parentControlCount = 0; + + ControlCollection parentControls = (ControlCollection)p.Properties.GetObject(PropControlsCollection); + + if (parentControls != null) { + parentControlCount = parentControls.Count; + } + + for (int c = 0; c < parentControlCount; c++) { + + // The logic for this is a bit lengthy, so I have broken it into separate + // caluses: + + // We are not interested in ourself. + // + if (parentControls[c] != ctl) { + + // We are interested in controls with >= tab indexes to ctl. We must include those + // controls with equal indexes to account for duplicate indexes. + // + if (parentControls[c].tabIndex >= targetIndex) { + + // Check to see if this control replaces the "best match" we've already + // found. + // + if (found == null || found.tabIndex > parentControls[c].tabIndex) { + + // Finally, check to make sure that if this tab index is the same as ctl, + // that we've already encountered ctl in the z-order. If it isn't the same, + // than we're more than happy with it. + // + if (parentControls[c].tabIndex != targetIndex || hitCtl) { + found = parentControls[c]; + } + } + } + } + else { + // We track when we have encountered "ctl". We never want to select ctl again, but + // we want to know when we've seen it in case we find another control with the same tab index. + // + hitCtl = true; + } + } + + if (found != null) { + return found; + } + + ctl = ctl.parent; + } + } + else { + if (ctl != this) { + + int targetIndex = ctl.tabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.parent; + + // Cycle through the controls in reverse z-order looking for the next lowest tab index. We must + // start with the same tab index as ctl, because there can be dups. + // + int parentControlCount = 0; + + ControlCollection parentControls = (ControlCollection)p.Properties.GetObject(PropControlsCollection); + + if (parentControls != null) { + parentControlCount = parentControls.Count; + } + + for (int c = parentControlCount - 1; c >= 0; c--) { + + // The logic for this is a bit lengthy, so I have broken it into separate + // caluses: + + // We are not interested in ourself. + // + if (parentControls[c] != ctl) { + + // We are interested in controls with <= tab indexes to ctl. We must include those + // controls with equal indexes to account for duplicate indexes. + // + if (parentControls[c].tabIndex <= targetIndex) { + + // Check to see if this control replaces the "best match" we've already + // found. + // + if (found == null || found.tabIndex < parentControls[c].tabIndex) { + + // Finally, check to make sure that if this tab index is the same as ctl, + // that we've already encountered ctl in the z-order. If it isn't the same, + // than we're more than happy with it. + // + if (parentControls[c].tabIndex != targetIndex || hitCtl) { + found = parentControls[c]; + } + } + } + } + else { + // We track when we have encountered "ctl". We never want to select ctl again, but + // we want to know when we've seen it in case we find another control with the same tab index. + // + hitCtl = true; + } + } + + // If we were unable to find a control we should return the control's parent. However, if that parent is us, return + // NULL. + // + if (found != null) { + ctl = found; + } + else { + if (p == this) { + return null; + } + else { + return p; + } + } + } + + // We found a control. Walk into this control to find the proper sub control within it to select. + // + ControlCollection ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + + while (ctlControls != null && ctlControls.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl))) { + Control found = ctl.GetFirstChildControlInTabOrder(/*forward=*/false); + if (found != null) { + ctl = found; + ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + } + else { + break; + } + } + } + + return ctl == this? null: ctl; + } + + /// + /// Return ((Control) window).Handle if window is a Control. + /// Otherwise, demands permission for AllWindows and returns window.Handle + /// + internal static IntPtr GetSafeHandle(IWin32Window window) + { + Debug.Assert(window != null, "window is null in Control.GetSafeHandle"); + IntPtr hWnd = IntPtr.Zero; + Control control = window as Control; + if (control != null) { + hWnd = control.Handle; + Debug.Assert(hWnd == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))); + return hWnd; + } + else { + IntSecurity.AllWindows.Demand(); + hWnd = window.Handle; + if (hWnd == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))) { + return hWnd; + } + else { + throw new Win32Exception(NativeMethods.ERROR_INVALID_HANDLE); + } + } + } + + /// + /// Retrieves the current value of the specified bit in the control's state. + /// + internal bool GetState(int flag) { + return(state & flag) != 0; + } + + /// + /// Retrieves the current value of the specified bit in the control's state2. + /// + private bool GetState2(int flag) + { + return (state2 & flag) != 0; + } + + /// + /// + /// Retrieves the current value of the specified bit in the control's style. + /// NOTE: This is control style, not the Win32 style of the hWnd. + /// + protected bool GetStyle(ControlStyles flag) { + return (controlStyle & flag) == flag; + } + + /// + /// + /// Hides the control by setting the visible property to false; + /// + public void Hide() { + Visible = false; + } + + + /// + /// Sets up the TrackMouseEvent for listening for the + /// mouse leave event. + /// + /// + private void HookMouseEvent() { + if (!GetState(STATE_TRACKINGMOUSEEVENT)) { + SetState(STATE_TRACKINGMOUSEEVENT, true); + + if (trackMouseEvent == null) { + trackMouseEvent = new NativeMethods.TRACKMOUSEEVENT(); + trackMouseEvent.dwFlags = NativeMethods.TME_LEAVE | NativeMethods.TME_HOVER; + trackMouseEvent.hwndTrack = Handle; + } + + SafeNativeMethods.TrackMouseEvent(trackMouseEvent); + } + } + + /// + /// + /// Called after the control has been added to another container. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void InitLayout() { + LayoutEngine.InitLayout(this, BoundsSpecified.All); + } + + /// + /// This method initializes the scaling bits for this control based on + /// the bounds. + /// + private void InitScaling(BoundsSpecified specified) { + requiredScaling |= (byte)((int)specified & RequiredScalingMask); + } + + // Sets the text and background colors of the DC, and returns the background HBRUSH. + // This gets called from some variation on WM_CTLCOLOR... + // Virtual because Scrollbar insists on being different. + // + // NOTE: this message may not have originally been sent to this HWND. + // + + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + internal virtual IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) { + if (!GetStyle(ControlStyles.UserPaint)) { + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), ColorTranslator.ToWin32(ForeColor)); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(BackColor)); + return BackColorBrush; + } + else { + return UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH); + } + } + + #if WIN95_SUPPORT + /// + /// Initializes mouse wheel support. This may involve registering some windows + /// messages on older operating systems. + /// + /// + private void InitMouseWheelSupport() { + if (!mouseWheelInit) { + // If we are running on less than NT4 or less that Win98 then we must use + // the manual mousewheel stuff... + // + mouseWheelRoutingNeeded = !SystemInformation.NativeMouseWheelSupport; + + if (mouseWheelRoutingNeeded) { + IntPtr hwndMouseWheel = IntPtr.Zero; + + // Check for the MouseZ "service". This is a little app that generated the + // MSH_MOUSEWHEEL messages by monitoring the hardware. If this app isn't + // found, then there is no support for MouseWheels on the system. + // + hwndMouseWheel = UnsafeNativeMethods.FindWindow(NativeMethods.MOUSEZ_CLASSNAME, NativeMethods.MOUSEZ_TITLE); + + if (hwndMouseWheel != IntPtr.Zero) { + + // Register the MSH_MOUSEWHEEL message... we look for this in the + // wndProc, and treat it just like WM_MOUSEWHEEL. + // + int message = SafeNativeMethods.RegisterWindowMessage(NativeMethods.MSH_MOUSEWHEEL); + + if (message != 0) { + mouseWheelMessage = message; + } + } + } + mouseWheelInit = true; + } + } + #endif + + /// + /// + /// Invalidates a region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Region region) { + Invalidate(region, false); + } + + /// + /// + /// Invalidates a region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Region region, bool invalidateChildren) { + if (region == null) { + Invalidate(invalidateChildren); + } + else if (IsHandleCreated) { + IntPtr regionHandle = GetHRgn(region); + + try { + Debug.Assert(regionHandle != IntPtr.Zero, "Region wasn't null but HRGN is?"); + if (invalidateChildren) { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), + null, new HandleRef(region, regionHandle), + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + // It's safe to invoke InvalidateRgn from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRgn(new HandleRef(this, Handle), + new HandleRef(region, regionHandle), + !GetStyle(ControlStyles.Opaque)); + } + } + } + finally { + SafeNativeMethods.DeleteObject(new HandleRef(region, regionHandle)); + } + + Rectangle bounds = Rectangle.Empty; + + // gpr: We shouldn't have to create a Graphics for this... + using (Graphics graphics = CreateGraphicsInternal()) { + bounds = Rectangle.Ceiling(region.GetBounds(graphics)); + } + + OnInvalidated(new InvalidateEventArgs(bounds)); + } + } + + + /// + /// + /// Invalidates the control and causes a paint message to be sent to the control. + /// This will not force a synchronous paint to occur, calling update after + /// invalidate will force a synchronous paint. + /// + public void Invalidate() { + Invalidate(false); + } + + /// + /// + /// Invalidates the control and causes a paint message to be sent to the control. + /// This will not force a synchronous paint to occur, calling update after + /// invalidate will force a synchronous paint. + /// + public void Invalidate(bool invalidateChildren) { + if (IsHandleCreated) { + if (invalidateChildren) { + SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle), + null, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + // It's safe to invoke InvalidateRect from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle), + null, + (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque); + } + } + + NotifyInvalidate(this.ClientRectangle); + } + } + + /// + /// + /// Invalidates a rectangular region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Rectangle rc) { + Invalidate(rc, false); + } + + /// + /// + /// Invalidates a rectangular region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Rectangle rc, bool invalidateChildren) { + if (rc.IsEmpty) { + Invalidate(invalidateChildren); + } + else if (IsHandleCreated) { + if (invalidateChildren) { + NativeMethods.RECT rcArea = NativeMethods.RECT.FromXYWH(rc.X, rc.Y, rc.Width, rc.Height); + SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle), + ref rcArea, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + NativeMethods.RECT rcArea = + NativeMethods.RECT.FromXYWH(rc.X, rc.Y, rc.Width, + rc.Height); + + // It's safe to invoke InvalidateRect from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle), + ref rcArea, + (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque); + } + } + NotifyInvalidate(rc); + } + } + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. It is an error to call this on the same thread that + /// the control belongs to. If the control's handle doesn't exist yet, this will + /// follow up the control's parent chain until it finds a control or form that does + /// have a window handle. If no appropriate handle can be found, invoke will throw + /// an exception. Exceptions that are raised during the call will be + /// propapgated back to the caller. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + public Object Invoke(Delegate method) { + return Invoke(method, null); + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. It is an error to call this on the same thread that + /// the control belongs to. If the control's handle doesn't exist yet, this will + /// follow up the control's parent chain until it finds a control or form that does + /// have a window handle. If no appropriate handle can be found, invoke will throw + /// an exception. Exceptions that are raised during the call will be + /// propapgated back to the caller. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + public Object Invoke(Delegate method, params Object[] args) { + using (new MultithreadSafeCallScope()) { + Control marshaler = FindMarshalingControl(); + return marshaler.MarshaledInvoke(this, method, args, true); + } + } + + /// + /// Perform the callback of a particular ThreadMethodEntry - called by InvokeMarshaledCallbacks below. + /// + /// If the invoke request originated from another thread, we should have already captured the ExecutionContext + /// of that thread. The callback is then invoked using that ExecutionContext (which includes info like the + /// compressed security stack). + /// + /// NOTE: The one part of the ExecutionContext that we DONT want applied to the callback is its SyncContext, + /// since this is the SyncContext of the other thread. So we grab the SyncContext of OUR thread, and pass + /// this through to the callback to use instead. + /// + /// When the invoke request comes from this thread, there won't be an ExecutionContext so we just invoke + /// the callback as is. + /// + private void InvokeMarshaledCallback(ThreadMethodEntry tme){ + if (tme.executionContext != null) { + if (invokeMarshaledCallbackHelperDelegate == null) { + invokeMarshaledCallbackHelperDelegate = new ContextCallback(InvokeMarshaledCallbackHelper); + } + // If there's no ExecutionContext, make sure we have a SynchronizationContext. There's no + // direct check for ExecutionContext: this is as close as we can get. + if (SynchronizationContext.Current == null) { + WindowsFormsSynchronizationContext.InstallIfNeeded(); + } + + tme.syncContext = SynchronizationContext.Current; + ExecutionContext.Run(tme.executionContext, invokeMarshaledCallbackHelperDelegate, tme); + } + else { + InvokeMarshaledCallbackHelper(tme); + } + } + + /// + /// Worker for invoking marshaled callbacks. + /// + private static void InvokeMarshaledCallbackHelper(object obj) + { + ThreadMethodEntry tme = (ThreadMethodEntry) obj; + + if (tme.syncContext != null) { + SynchronizationContext oldContext = SynchronizationContext.Current; + + try { + SynchronizationContext.SetSynchronizationContext(tme.syncContext); + + InvokeMarshaledCallbackDo(tme); + } + finally { + SynchronizationContext.SetSynchronizationContext(oldContext); + } + } + else { + InvokeMarshaledCallbackDo(tme); + } + } + + /// + /// + private static void InvokeMarshaledCallbackDo(ThreadMethodEntry tme) + { + // We short-circuit a couple of common cases for speed. + // + if (tme.method is EventHandler) { + if (tme.args == null || tme.args.Length < 1) { + ((EventHandler)tme.method)(tme.caller, EventArgs.Empty); + } + else if (tme.args.Length < 2) { + ((EventHandler)tme.method)(tme.args[0], EventArgs.Empty); + } + else { + ((EventHandler)tme.method)(tme.args[0], (EventArgs)tme.args[1]); + } + } + else if (tme.method is MethodInvoker) { + ((MethodInvoker)tme.method)(); + } + else if (tme.method is WaitCallback) + { + Debug.Assert(tme.args.Length == 1, + "Arguments are wrong for WaitCallback"); + ((WaitCallback)tme.method)(tme.args[0]); + } + else { + tme.retVal = tme.method.DynamicInvoke(tme.args); + } + } + + /// + /// Called on the control's owning thread to perform the actual callback. + /// This empties this control's callback queue, propagating any exceptions + /// back as needed. + /// + private void InvokeMarshaledCallbacks() { + ThreadMethodEntry current = null; + lock(threadCallbackList) { + if (threadCallbackList.Count > 0) { + current = (ThreadMethodEntry)threadCallbackList.Dequeue(); + } + } + + // Now invoke on all the queued items. + // + while (current != null) { + if (current.method != null) { + + try { + // If we are running under the debugger, don't wrap asynchronous + // calls in a try catch. It is much better to throw here than pop up + // a thread exception dialog below. + if (NativeWindow.WndProcShouldBeDebuggable && !current.synchronous) { + InvokeMarshaledCallback(current); + } + else { + try { + InvokeMarshaledCallback(current); + } + catch (Exception t) { + current.exception = t.GetBaseException(); + } + } + } + finally { + current.Complete(); + + // This code matches the behavior above. Basically, if we're debugging, don't + // do this because the exception would have been handled above. If we're + // not debugging, raise the exception here. + if (!NativeWindow.WndProcShouldBeDebuggable && + current.exception != null && !current.synchronous) { + Application.OnThreadException(current.exception); + } + } + } + + lock (threadCallbackList) { + if (threadCallbackList.Count > 0) { + current = (ThreadMethodEntry)threadCallbackList.Dequeue(); + } + else { + current = null; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected void InvokePaint(Control c, PaintEventArgs e) { + c.OnPaint(e); + } + + /// + /// + /// [To be supplied.] + /// + protected void InvokePaintBackground(Control c, PaintEventArgs e) { + c.OnPaintBackground(e); + } + + /// + /// determines whether the font is set + /// + /// + internal bool IsFontSet() { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return true; + } + return false; + } + + /// + /// WARNING! The meaning of this method is not what it appears. + /// The method returns true if "descendant" (the argument) is a descendant + /// of "this". I'd expect it to be the other way around, but oh well too late. + /// + /// + internal bool IsDescendant(Control descendant) { + Control control = descendant; + while (control != null) { + if (control == this) + return true; + control = control.ParentInternal; + } + return false; + } + + /// + /// + /// This Function will return a Boolean as to whether the Key value passed in is Locked... + /// + public static bool IsKeyLocked(Keys keyVal) { + if (keyVal == Keys.Insert || keyVal == Keys.NumLock || keyVal == Keys.CapsLock || keyVal == Keys.Scroll) { + int result = UnsafeNativeMethods.GetKeyState((int)keyVal); + + // If the high-order bit is 1, the key is down; otherwise, it is up. + // If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, + // is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. + // A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, + // and off when the key is untoggled. + + // Toggle keys (only low bit is of interest). + if( keyVal == Keys.Insert || keyVal == Keys.CapsLock ) + { + return (result & 0x1) != 0x0; + } + + return (result & 0x8001) != 0x0; + } + + // else - it's an un-lockable key. + // VSWhidbey 95660: Actually get the exception string from the system resource. + throw new NotSupportedException(SR.GetString(SR.ControlIsKeyLockedNumCapsScrollLockKeysSupportedOnly)); + } + + /// + /// + /// Determines if charCode is an input character that the control + /// wants. This method is called during window message pre-processing to + /// determine whether the given input character should be pre-processed or + /// sent directly to the control. If isInputChar returns true, the + /// given character is sent directly to the control. If isInputChar + /// returns false, the character is pre-processed and only sent to the + /// control if it is not consumed by the pre-processing phase. The + /// pre-processing of a character includes checking whether the character + /// is a mnemonic of another control. + /// + + // SECREVIEW: + // NOT LinkDemanding here because these functions do not have a processing side effect + // by directly calling them. + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool IsInputChar(char charCode) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.IsInputChar 0x" + ((int)charCode).ToString("X", CultureInfo.InvariantCulture)); + + int mask = 0; + if (charCode == (char)(int)Keys.Tab) { + mask = NativeMethods.DLGC_WANTCHARS | NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB; + } + else { + mask = NativeMethods.DLGC_WANTCHARS | NativeMethods.DLGC_WANTALLKEYS; + } + return(unchecked( (int) (long)SendMessage(NativeMethods.WM_GETDLGCODE, 0, 0)) & mask) != 0; + } + + /// + /// + /// Determines if keyData is an input key that the control wants. + /// This method is called during window message pre-processing to determine + /// whether the given input key should be pre-processed or sent directly to + /// the control. If isInputKey returns true, the given key is sent + /// directly to the control. If isInputKey returns false, the key is + /// pre-processed and only sent to the control if it is not consumed by the + /// pre-processing phase. Keys that are pre-processed include TAB, RETURN, + /// ESCAPE, and arrow keys. + /// + + // SECREVIEW: + // NOT LinkDemanding here because these functions do not have a processing side effect + // by directly calling them. + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool IsInputKey(Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.IsInputKey " + keyData.ToString()); + + if ((keyData & Keys.Alt) == Keys.Alt) return false; + int mask = NativeMethods.DLGC_WANTALLKEYS; + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + mask = NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB; + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + mask = NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTARROWS; + break; + } + + if (IsHandleCreated) { + return(unchecked( (int) (long)SendMessage(NativeMethods.WM_GETDLGCODE, 0, 0)) & mask) != 0; + } + else { + return false; + } + } + + /// + /// + /// Determines if charCode is the mnemonic character in text. + /// The mnemonic character is the character imediately following the first + /// instance of "&" in text + /// + public static bool IsMnemonic(char charCode, string text) { +#if DEBUG + if (ControlKeyboardRouting.TraceVerbose) { + Debug.Write("Control.IsMnemonic(" + charCode.ToString() + ", "); + + if (text != null) { + Debug.Write(text); + } + else { + Debug.Write("null"); + } + Debug.WriteLine(")"); + } +#endif + + //Special case handling: + if (charCode=='&') { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning false"); + return false; + } //if + + + if (text != null) { + int pos = -1; // start with -1 to handle double &'s + char c2 = Char.ToUpper(charCode, CultureInfo.CurrentCulture); + for (;;) { + if (pos + 1 >= text.Length) + break; + pos = text.IndexOf('&', pos + 1) + 1; + if (pos <= 0 || pos >= text.Length) + break; + char c1 = Char.ToUpper(text[pos], CultureInfo.CurrentCulture); + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...& found... char=" + c1.ToString()); + if (c1 == c2 || Char.ToLower(c1, CultureInfo.CurrentCulture) == Char.ToLower(c2, CultureInfo.CurrentCulture)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning true"); + return true; + } + } + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose && pos == 0, " ...no & found"); + } + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning false"); + return false; + } + + private void ListenToUserPreferenceChanged(bool listen) { + if (GetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED)) { + if (!listen) { + SetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED, false); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + } + } + else if (listen) { + SetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED, true); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + } + } + + /// + /// + /// Transforms an integer coordinate from logical to device units + /// by scaling it for the current DPI and rounding down to the nearest integer value. + /// + public int LogicalToDeviceUnits(int value) { + return DpiHelper.LogicalToDeviceUnits(value, DeviceDpi); + } + + /// + /// Transforms size from logical to device units + /// by scaling it for the current DPI and rounding down to the nearest integer value for width and height. + /// + /// size to be scaled + /// scaled size + public Size LogicalToDeviceUnits(Size value) { + return DpiHelper.LogicalToDeviceUnits(value, DeviceDpi); + } + + /// + /// Create a new bitmap scaled for the device units. + /// When displayed on the device, the scaled image will have same size as the original image would have when displayed at 96dpi. + /// + /// The image to scale from logical units to device units + public void ScaleBitmapLogicalToDevice(ref Bitmap logicalBitmap) { + DpiHelper.ScaleBitmapLogicalToDevice(ref logicalBitmap, DeviceDpi); + } + + internal void AdjustWindowRectEx(ref NativeMethods.RECT rect, int style, bool bMenu, int exStyle) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + SafeNativeMethods.AdjustWindowRectExForDpi(ref rect, style, bMenu, exStyle, (uint)deviceDpi); + } else { + SafeNativeMethods.AdjustWindowRectEx(ref rect, style, bMenu, exStyle); + } + } + + private Object MarshaledInvoke(Control caller, Delegate method, Object[] args, bool synchronous) { + + // Marshaling an invoke occurs in three steps: + // + // 1. Create a ThreadMethodEntry that contains the packet of information + // about this invoke. This TME is placed on a linked list of entries because + // we have a gap between the time we PostMessage and the time it actually + // gets processed, and this gap may allow other invokes to come in. Access + // to this linked list is always synchronized. + // + // 2. Post ourselves a message. Our caller has already determined the + // best control to call us on, and we should almost always have a handle. + // + // 3. If we're synchronous, wait for the message to get processed. We don't do + // a SendMessage here so we're compatible with OLE, which will abort many + // types of calls if we're within a SendMessage. + // + + if (!IsHandleCreated) { + throw new InvalidOperationException(SR.GetString(SR.ErrorNoMarshalingThread)); + } + + // We have to demand unmanaged code permission here for the control hosted in + // the browser case. Without this check, we will expose a security hole, because + // ActiveXImpl.OnMessage() will assert unmanaged code for everyone as part of + // its implementation. + // The right fix is to remove the Assert() on top of the ActiveXImpl class, and + // visit each method to see if it needs unmanaged code permission, and if so, add + // the permission just to that method(s). + // + ActiveXImpl activeXImpl = (ActiveXImpl)Properties.GetObject(PropActiveXImpl); + if (activeXImpl != null) { + IntSecurity.UnmanagedCode.Demand(); + } + + // We don't want to wait if we're on the same thread, or else we'll deadlock. + // It is important that syncSameThread always be false for asynchronous calls. + // + bool syncSameThread = false; + int pid; // ignored + if (SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, Handle), out pid) == SafeNativeMethods.GetCurrentThreadId()) { + if (synchronous) + syncSameThread = true; + } + + // Store the compressed stack information from the thread that is calling the Invoke() + // so we can assign the same security context to the thread that will actually execute + // the delegate being passed. + // + ExecutionContext executionContext = null; + if (!syncSameThread) { + executionContext = ExecutionContext.Capture(); + } + ThreadMethodEntry tme = new ThreadMethodEntry(caller, this, method, args, synchronous, executionContext); + + lock (this) { + if (threadCallbackList == null) { + threadCallbackList = new Queue(); + } + } + + lock (threadCallbackList) { + if (threadCallbackMessage == 0) { + threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage"); + } + threadCallbackList.Enqueue(tme); + } + + if (syncSameThread) { + InvokeMarshaledCallbacks(); + } else { + // + + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); + } + + if (synchronous) { + if (!tme.IsCompleted) { + WaitForWaitHandle(tme.AsyncWaitHandle); + } + if (tme.exception != null) { + throw tme.exception; + } + return tme.retVal; + } + else { + return(IAsyncResult)tme; + } + } + + /// + /// This method is used by WM_GETCONTROLNAME and WM_GETCONTROLTYPE + /// to marshal a string to a message structure. It handles + /// two cases: if no buffer was passed it returns the size of + /// buffer needed. If a buffer was passed, it fills the buffer. + /// If the passed buffer is not long enough it will return -1. + /// + private void MarshalStringToMessage(string value, ref Message m) { + if (m.LParam == IntPtr.Zero) { + m.Result = (IntPtr)((value.Length + 1) * Marshal.SystemDefaultCharSize); + return; + } + + if (unchecked((int)(long)m.WParam) < value.Length + 1) { + m.Result = (IntPtr)(-1); + return; + } + + // Copy the name into the given IntPtr + // + char[] nullChar = new char[] {(char)0}; + byte[] nullBytes; + byte[] bytes; + + if (Marshal.SystemDefaultCharSize == 1) { + bytes = Encoding.Default.GetBytes(value); + nullBytes = Encoding.Default.GetBytes(nullChar); + } + else { + bytes = Encoding.Unicode.GetBytes(value); + nullBytes = Encoding.Unicode.GetBytes(nullChar); + } + + Marshal.Copy(bytes, 0, m.LParam, bytes.Length); + Marshal.Copy(nullBytes, 0, unchecked((IntPtr)((long)m.LParam + (long)bytes.Length)), nullBytes.Length); + + m.Result = (IntPtr)((bytes.Length + nullBytes.Length)/Marshal.SystemDefaultCharSize); + } + + // Used by form to notify the control that it has been "entered" + // + internal void NotifyEnter() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::NotifyEnter() - " + this.Name); + OnEnter(EventArgs.Empty); + } + + // Used by form to notify the control that it has been "left" + // + internal void NotifyLeave() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::NotifyLeave() - " + this.Name); + OnLeave(EventArgs.Empty); + } + + + /// + /// + /// + /// Propagates the invalidation event, notifying the control that + /// some part of it is being invalidated and will subsequently need + /// to repaint. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void NotifyInvalidate(Rectangle invalidatedArea) { + OnInvalidated(new InvalidateEventArgs(invalidatedArea)); + } + + // Used by form to notify the control that it is validating. + // + private bool NotifyValidating() { + CancelEventArgs ev = new CancelEventArgs(); + OnValidating(ev); + return ev.Cancel; + } + + // Used by form to notify the control that it has been validated. + // + private void NotifyValidated() { + OnValidated(EventArgs.Empty); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeOnClick(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnClick(e); + } + } + + /// + protected virtual void OnAutoSizeChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventAutoSizeChanged] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackColorChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + if (GetState(STATE_OWNCTLBRUSH)) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + } + Properties.SetObject(PropBackBrush, null); + } + + Invalidate(); + + EventHandler eh = Events[EventBackColor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBackColorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackgroundImageChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventBackgroundImage] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBackgroundImageChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackgroundImageLayoutChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventBackgroundImageLayout] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBindingContextChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropBindings) != null) { + UpdateBindings(); + } + + EventHandler eh = Events[EventBindingContext] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBindingContextChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCausesValidationChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventCausesValidation] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Called when a child is about to resume its layout. The default implementation + /// calls OnChildLayoutResuming on the parent. + /// + internal virtual void OnChildLayoutResuming(Control child, bool performLayout) { + if (ParentInternal != null) { + ParentInternal.OnChildLayoutResuming(child, performLayout); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnContextMenuChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventContextMenu] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnContextMenuStripChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventContextMenuStrip] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCursorChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventCursor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentCursorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDockChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventDock] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.OnEnabled to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnEnabledChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (IsHandleCreated) { + SafeNativeMethods.EnableWindow(new HandleRef(this, Handle), Enabled); + + // User-paint controls should repaint when their enabled state changes + // + if (GetStyle(ControlStyles.UserPaint)) { + Invalidate(); + Update(); + } + } + + EventHandler eh = Events[EventEnabled] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentEnabledChanged(e); + } + } + } + + // Refer VsWhidbey : 515910 & 269769 + internal virtual void OnFrameWindowActivate(bool fActivate) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFontChanged(EventArgs e) { + Contract.Requires(e != null); + // bail if disposing + // + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, -1); + } + + + // Cleanup any font handle wrapper... + // + DisposeFontHandle(); + + if (IsHandleCreated && !GetStyle(ControlStyles.UserPaint)) { + SetWindowFont(); + } + + EventHandler eh = Events[EventFont] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + using(new LayoutTransaction(this, this, PropertyNames.Font, false)) { + if (controlsCollection != null) { + // This may have changed the sizes of our children. + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentFontChanged(e); + } + } + } + + LayoutTransaction.DoLayout(this,this,PropertyNames.Font); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnForeColorChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventForeColor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentForeColorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + // update the scroll position when the handle has been created + // MUST SET THIS BEFORE CALLING RecreateHandle!!! + SetState2(STATE2_SETSCROLLPOS, true); + + RecreateHandle(); + + EventHandler eh = Events[EventRightToLeft] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentRightToLeftChanged(e); + } + } + } + + /// + /// + /// OnNotifyMessage is called if the ControlStyles.EnableNotifyMessage + /// bit is set. This allows for semi-trusted controls to listen to + /// window messages, without allowing them to actually modify the + /// message. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnNotifyMessage(Message m) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackColorChanged(EventArgs e) { + Contract.Requires(e != null); + Color backColor = Properties.GetColor(PropBackColor); + if (backColor.IsEmpty) { + OnBackColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackgroundImageChanged(EventArgs e) { + Contract.Requires(e != null); + OnBackgroundImageChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBindingContextChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropBindingManager) == null) { + OnBindingContextChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentCursorChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropCursor) == null) { + OnCursorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentEnabledChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetState(STATE_ENABLED)) { + OnEnabledChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentFontChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropFont) == null) { + OnFontChanged(e); + } + } + + + // occurs when the parent of this control has recreated + // its handle. + // presently internal as we feel this will be rare to + // require override + internal virtual void OnParentHandleRecreated() { + // restore ourselves over to the original control. + + // use SetParent directly so as to not raise ParentChanged events + Control parent = ParentInternal; + if (parent != null) { + if (IsHandleCreated) { + UnsafeNativeMethods.SetParent(new HandleRef(this, this.Handle), new HandleRef(parent, parent.Handle)); + UpdateZOrder(); + } + } + SetState(STATE_PARENTRECREATING, false); + + // if our parent was initially the the parent who's handle just got recreated, we need + // to recreate ourselves so that we get notification. See UpdateReflectParent for more details. + if (ReflectParent == ParentInternal) { + RecreateHandle(); + } + + } + + + // occurs when the parent of this control is recreating + // its handle. + // presently internal as we feel this will be rare to + // require override + internal virtual void OnParentHandleRecreating() { + SetState(STATE_PARENTRECREATING, true); + // swoop this control over to the parking window. + + // if we left it parented to the parent control, the DestroyWindow + // would force us to destroy our handle as well... hopefully + // parenting us over to the parking window and restoring ourselves + // should help improve recreate perf. + + // use SetParent directly so as to not raise ParentChanged events + if (IsHandleCreated) { + Application.ParkHandle(new HandleRef(this, this.Handle), this.DpiAwarenessContext); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentForeColorChanged(EventArgs e) { + Contract.Requires(e != null); + Color foreColor = Properties.GetColor(PropForeColor); + if (foreColor.IsEmpty) { + OnForeColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentRightToLeftChanged(EventArgs e) { + Contract.Requires(e != null); + if (!Properties.ContainsInteger(PropRightToLeft) || ((RightToLeft)Properties.GetInteger(PropRightToLeft)) == RightToLeft.Inherit) { + OnRightToLeftChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentVisibleChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetState(STATE_VISIBLE)) { + OnVisibleChanged(e); + } + } + + // Used to work around VSWhidbey 527459. OnVisibleChanged/OnParentVisibleChanged is not called when a parent becomes invisible + internal virtual void OnParentBecameInvisible() { + if (GetState(STATE_VISIBLE)) { + // This control became invisible too - notify its children + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + ctl.OnParentBecameInvisible(); + } + } + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPrint(PaintEventArgs e) { + if (e == null) { + throw new ArgumentNullException("e"); + } + Contract.EndContractBlock(); + + if (GetStyle(ControlStyles.UserPaint)) { + // Theme support on Windows XP requires that we paint the background + // and foreground to support semi-transparent children + // + PaintWithErrorHandling(e, PaintLayerBackground); + e.ResetGraphics(); + PaintWithErrorHandling(e, PaintLayerForeground); + } + else { + PrintPaintEventArgs ppev = e as PrintPaintEventArgs; + Message m; + bool releaseDC = false; + IntPtr hdc = IntPtr.Zero; + + if (ppev == null) { + IntPtr flags = (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT); + hdc = e.HDC; + + if (hdc == IntPtr.Zero) { + // a manually created paintevent args + hdc = e.Graphics.GetHdc(); + releaseDC = true; + } + m = Message.Create(this.Handle, NativeMethods.WM_PRINTCLIENT, hdc, flags); + } + else { + m = ppev.Message; + } + + try { + DefWndProc(ref m); + } + finally { + if (releaseDC) { + e.Graphics.ReleaseHdcInternal(hdc); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTabIndexChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventTabIndex] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTabStopChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventTabStop] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTextChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventText] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.OnVisible to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnVisibleChanged(EventArgs e) { + Contract.Requires(e != null); + bool visible = this.Visible; + if (visible) { + UnhookMouseEvent(); + trackMouseEvent = null; + } + if (parent != null && visible && !Created) { + bool isDisposing = GetAnyDisposingInHierarchy(); + if (!isDisposing) { + // Usually the control is created by now, but in a few corner cases + // exercised by the PropertyGrid dropdowns, it isn't + CreateControl(); + } + } + + EventHandler eh = Events[EventVisible] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + if (ctl.Visible) { + ctl.OnParentVisibleChanged(e); + } + if (!visible) { + ctl.OnParentBecameInvisible(); + } + } + } + } + + /// + internal virtual void OnTopMostActiveXParentChanged(EventArgs e) { + Contract.Requires(e != null); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnTopMostActiveXParentChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventParent] as EventHandler; + if (eh != null) { + eh(this, e); + } + + // + // Inform the control that the topmost control is now an ActiveX control + if (TopMostParent.IsActiveX) { + OnTopMostActiveXParentChanged(EventArgs.Empty); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClick(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClientSizeChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventClientSize] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnControlAdded(ControlEventArgs e) { + Contract.Requires(e != null); + ControlEventHandler handler = (ControlEventHandler)Events[EventControlAdded]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnControlRemoved(ControlEventArgs e) { + Contract.Requires(e != null); + ControlEventHandler handler = (ControlEventHandler)Events[EventControlRemoved]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Called when the control is first created. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCreateControl() { + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. + /// Call base.OnHandleCreated first. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHandleCreated(EventArgs e) { + Contract.Requires(e != null); + if (this.IsHandleCreated) { + + // Setting fonts is for some reason incredibly expensive. + // (Even if you exclude font handle creation) + if (!GetStyle(ControlStyles.UserPaint)){ + SetWindowFont(); + } + + if (DpiHelper.EnableDpiChangedMessageHandling && !(typeof(Form).IsAssignableFrom(this.GetType()))) { + int old = deviceDpi; + deviceDpi = (int)UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + if (old != deviceDpi) { + RescaleConstantsForDpi(old, deviceDpi); + } + } + + // Restore dragdrop status. Ole Initialize happens + // when the ThreadContext in Application is created + // + SetAcceptDrops(AllowDrop); + + Region region = (Region)Properties.GetObject(PropRegion); + if (region != null) { + IntPtr regionHandle = GetHRgn(region); + + try { + if (IsActiveX) { + regionHandle = ActiveXMergeRegion(regionHandle); + } + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(this, regionHandle), SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle))) != 0) { + //The HWnd owns the region. + regionHandle = IntPtr.Zero; + } + } + finally { + if (regionHandle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, regionHandle)); + } + } + } + + // Update accessbility information. + // + ControlAccessibleObject accObj = Properties.GetObject(PropAccessibility) as ControlAccessibleObject; + ControlAccessibleObject ncAccObj = Properties.GetObject(PropNcAccessibility) as ControlAccessibleObject; + + // Cache Handle in a local before asserting so we minimize code running under the Assert. + IntPtr handle = Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + + try { + if (accObj != null) { + accObj.Handle = handle; + } + if (ncAccObj != null) { + ncAccObj.Handle = handle; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + // Set the window text from the Text property. + // + if (text != null && text.Length != 0) { + UnsafeNativeMethods.SetWindowText(new HandleRef(this, Handle), text); + } + + if (!(this is ScrollableControl) && !IsMirrored && GetState2(STATE2_SETSCROLLPOS) && !GetState2(STATE2_HAVEINVOKED)) { + BeginInvoke(new EventHandler(this.OnSetScrollPosition)); + SetState2(STATE2_HAVEINVOKED, true); + SetState2(STATE2_SETSCROLLPOS, false); + } + + // Listen to UserPreferenceChanged if the control is top level and interested in the notification. + if (GetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED)) { + ListenToUserPreferenceChanged(GetTopLevel()); + } + } + + EventHandler handler = (EventHandler)Events[EventHandleCreated]; + if (handler != null) handler(this, e); + + if (this.IsHandleCreated) + { + // Now, repost the thread callback message if we found it. We should do + // this last, so we're as close to the same state as when the message + // was placed. + // + if (GetState(STATE_THREADMARSHALLPENDING)) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); + SetState(STATE_THREADMARSHALLPENDING, false); + } + } + } + + private void OnSetScrollPosition(object sender, EventArgs e) { + SetState2(STATE2_HAVEINVOKED, false); + OnInvokedSetScrollPosition(sender, e); + } + + internal virtual void OnInvokedSetScrollPosition(object sender, EventArgs e) { + if (!(this is ScrollableControl) && !IsMirrored) { + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_RANGE; + if (UnsafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ,si) != false) { + si.nPos = (RightToLeft == RightToLeft.Yes) ? si.nMax : si.nMin; + SendMessage(NativeMethods.WM_HSCROLL, NativeMethods.Util.MAKELPARAM(NativeMethods.SB_THUMBPOSITION, si.nPos), 0); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLocationChanged(EventArgs e) { + Contract.Requires(e != null); + OnMove(EventArgs.Empty); + + EventHandler eh = Events[EventLocation] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle is about to be destroyed. + /// Call base.OnHandleDestroyed last. + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHandleDestroyed(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventHandleDestroyed]; + if (handler != null) handler(this, e); + + UpdateReflectParent(false); + + if (!RecreatingHandle) { + if (GetState(STATE_OWNCTLBRUSH)) { + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + Properties.SetObject(PropBackBrush, null); + } + } + ListenToUserPreferenceChanged(false /*listen*/); + } + + // this code is important -- it is critical that we stash away + // the value of the text for controls such as edit, button, + // label, etc. Without this processing, any time you change a + // property that forces handle recreation, you lose your text! + // See the above code in wmCreate + // + + try { + if (!GetAnyDisposingInHierarchy()) { + text = Text; + if (text != null && text.Length == 0) text = null; + } + SetAcceptDrops(false); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + + // Some ActiveX controls throw exceptions when + // you ask for the text property after you have destroyed their handle. We + // don't want those exceptions to bubble all the way to the top, since + // we leave our state in a mess. See ASURT 49990. + // + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDoubleClick(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventDoubleClick]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.onEnter to send this event to any registered event listeners. + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragEnter to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragEnter(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragEnter]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragOver to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragOver(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragOver]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragLeave to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventDragLeave]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragDrop to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragDrop(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragDrop]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onGiveFeedback to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + protected virtual void OnGiveFeedback(GiveFeedbackEventArgs gfbevent) { + Contract.Requires(gfbevent != null); + GiveFeedbackEventHandler handler = (GiveFeedbackEventHandler)Events[EventGiveFeedback]; + if (handler != null) handler(this,gfbevent); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnEnter(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventEnter]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeGotFocus(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnGotFocus(e); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(toInvoke); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnGotFocus(EventArgs e) { + Contract.Requires(e != null); + if (IsActiveX) { + ActiveXOnFocus(true); + } + + if (parent != null) { + parent.ChildGotFocus(this); + } + + EventHandler handler = (EventHandler)Events[EventGotFocus]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onHelp to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHelpRequested(HelpEventArgs hevent) { + Contract.Requires(hevent != null); + HelpEventHandler handler = (HelpEventHandler)Events[EventHelpRequested]; + if (handler != null) { + handler(this,hevent); + // VSWhidbey 95281: Set this to true so that multiple events aren't raised to the Form. + hevent.Handled = true; + } + + if (!hevent.Handled) { + if (ParentInternal != null) { + ParentInternal.OnHelpRequested(hevent); + } + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.OnInvalidate to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInvalidated(InvalidateEventArgs e) { + Contract.Requires(e != null); + // Ask the site to change the view... + if (IsActiveX) { + ActiveXViewChanged(); + } + + // Transparent control support + ControlCollection controls = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controls != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controls.Count; i++) { + controls[i].OnParentInvalidated(e); + } + } + + InvalidateEventHandler handler = (InvalidateEventHandler)Events[EventInvalidated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyDown(KeyEventArgs e) { + Contract.Requires(e != null); + KeyEventHandler handler = (KeyEventHandler)Events[EventKeyDown]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyPress(KeyPressEventArgs e) { + Contract.Requires(e != null); + KeyPressEventHandler handler = (KeyPressEventHandler)Events[EventKeyPress]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyUp(KeyEventArgs e) { + Contract.Requires(e != null); + KeyEventHandler handler = (KeyEventHandler)Events[EventKeyUp]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Core layout logic. Inheriting controls should override this function + /// to do any custom layout logic. It is not neccessary to call + /// base.layoutCore, however for normal docking and anchoring + /// functions to work, base.layoutCore must be called. + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLayout(LayoutEventArgs levent) { + Contract.Requires(levent != null); + // Ask the site to change the view... + if (IsActiveX) { + ActiveXViewChanged(); + } + LayoutEventHandler handler = (LayoutEventHandler)Events[EventLayout]; + if (handler != null) handler(this, levent); + + bool parentRequiresLayout = LayoutEngine.Layout(this, levent); + + // + + + if(parentRequiresLayout && ParentInternal != null) { + // LayoutEngine.Layout can return true to request that our parent resize us because + // we did not have enough room for our contents. We can not just call PerformLayout + // because this container is currently suspended. PerformLayout will check this state + // flag and PerformLayout on our parent. + ParentInternal.SetState(STATE_LAYOUTISDIRTY, true); + } + } + + /// + /// + /// Called when the last resume layout call is made. If performLayout is true + /// a layout will occur as soon as this call returns. Layout is + /// still suspended when this call is made. The default implementation + /// calls OnChildLayoutResuming on the parent, if it exists. + /// + internal virtual void OnLayoutResuming(bool performLayout) { + if (ParentInternal != null) { + ParentInternal.OnChildLayoutResuming(this, performLayout); + } + } + + internal virtual void OnLayoutSuspended() { + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventLeave]; + if (handler != null) handler(this, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeLostFocus(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnLostFocus(e); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(toInvoke); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLostFocus(EventArgs e) { + Contract.Requires(e != null); + if (IsActiveX) { + ActiveXOnFocus(false); + } + + EventHandler handler = (EventHandler)Events[EventLostFocus]; + if (handler != null) handler(this, e); + } + + protected virtual void OnMarginChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMarginChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseDoubleClick(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseDoubleClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseClick(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseCaptureChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseCaptureChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseDown(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseDown]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseEnter(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseEnter]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseLeave]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// + /// Raises the event. + /// Occurs when the form is moved to a monitor with a different resolution (number of dots per inch), + /// or when scaling level is changed in the windows setting by the user. + /// This message is not sent to the top level windows. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + protected virtual void OnDpiChangedBeforeParent(EventArgs e) { + Contract.Requires(e != null); + ((EventHandler)Events[EventDpiChangedBeforeParent])?.Invoke(this, e); + } + + /// + /// + /// + /// Raises the event. + /// Occurs when the form is moved to a monitor with a different resolution (number of dots per inch), + /// or when scaling level is changed in windows setting by the user. + /// This message is not sent to the top level windows. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + protected virtual void OnDpiChangedAfterParent(EventArgs e) { + Contract.Requires(e != null); + ((EventHandler)Events[EventDpiChangedAfterParent])?.Invoke(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseHover(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseHover]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseMove(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseMove]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseUp(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseUp]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseWheel(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseWheel]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMove(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMove]; + if (handler != null) handler(this, e); + if (RenderTransparent) + Invalidate(); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onPaint to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPaint(PaintEventArgs e) { + Contract.Requires(e != null); + PaintEventHandler handler = (PaintEventHandler)Events[EventPaint]; + if (handler != null) handler(this, e); + } + + protected virtual void OnPaddingChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetStyle(ControlStyles.ResizeRedraw)) { + Invalidate(); + } + EventHandler handler = (EventHandler)Events[EventPaddingChanged]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// Inheriting classes should override this method to handle the erase + /// background request from windows. It is not necessary to call + /// base.onPaintBackground, however if you do not want the default + /// Windows behavior you must set event.handled to true. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPaintBackground(PaintEventArgs pevent) { + Contract.Requires(pevent != null); + // We need the true client rectangle as clip rectangle causes + // problems on "Windows Classic" theme. + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(window, InternalHandle), ref rect); + + PaintBackground(pevent, new Rectangle(rect.left, rect.top, rect.right, rect.bottom)); + } + + // Transparent control support + private void OnParentInvalidated(InvalidateEventArgs e) { + Contract.Requires(e != null); + if (!RenderTransparent) return; + + if (this.IsHandleCreated) { + // move invalid rect into child space + Rectangle cliprect = e.InvalidRect; + Point offs = this.Location; + cliprect.Offset(-offs.X,-offs.Y); + cliprect = Rectangle.Intersect(this.ClientRectangle, cliprect); + + // if we don't intersect at all, do nothing + if (cliprect.IsEmpty) return; + this.Invalidate(cliprect); + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onQueryContinueDrag to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + protected virtual void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent) { + Contract.Requires(qcdevent != null); + QueryContinueDragEventHandler handler = (QueryContinueDragEventHandler)Events[EventQueryContinueDrag]; + if (handler != null) handler(this, qcdevent); + } + + /// + /// + /// Raises the event when the Region property has changed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRegionChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventRegionChanged] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResize(EventArgs e) { + Contract.Requires(e != null); + if ((controlStyle & ControlStyles.ResizeRedraw) == ControlStyles.ResizeRedraw + || GetState(STATE_EXCEPTIONWHILEPAINTING)) { + Invalidate(); + } + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + EventHandler handler = (EventHandler)Events[EventResize]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers") + ] + protected virtual void OnPreviewKeyDown(PreviewKeyDownEventArgs e) { + Contract.Requires(e != null); + PreviewKeyDownEventHandler handler = (PreviewKeyDownEventHandler)Events[EventPreviewKeyDown]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnSizeChanged(EventArgs e) { + Contract.Requires(e != null); + OnResize(EventArgs.Empty); + + EventHandler eh = Events[EventSize] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnChangeUICues(UICuesEventArgs e) { + Contract.Requires(e != null); + UICuesEventHandler handler = (UICuesEventHandler)Events[EventChangeUICues]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnStyleChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventStyleChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnSystemColorsChanged(EventArgs e) { + Contract.Requires(e != null); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnSystemColorsChanged(EventArgs.Empty); + } + } + Invalidate(); + + EventHandler handler = (EventHandler)Events[EventSystemColorsChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnValidating(CancelEventArgs e) { + Contract.Requires(e != null); + CancelEventHandler handler = (CancelEventHandler)Events[EventValidating]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnValidated(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventValidated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Is invoked when the control handle is created or right before the top level parent receives WM_DPICHANGED message. + /// This method is an opportunity to rescale any constant sizes, glyphs or bitmaps before re-painting. + /// The derived class can choose to not call the base class implementation. + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + } + + // This is basically OnPaintBackground, put in a separate method for ButtonBase, + // which does all painting under OnPaint, and tries very hard to avoid double-painting the border pixels. + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle) { + PaintBackground(e, rectangle, this.BackColor, Point.Empty); + } + + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor) { + PaintBackground(e, rectangle, backColor, Point.Empty); + } + + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor, Point scrollOffset) { + if (RenderColorTransparent(backColor)) { + PaintTransparentBackground(e, rectangle); + } + + //If the form or mdiclient is mirrored then we do not render the background image due to GDI+ issues. + bool formRTL = ((this is Form || this is MdiClient) && this.IsMirrored); + + // The rest of this won't do much if BackColor is transparent and there is no BackgroundImage, + // but we need to call it in the partial alpha case. + + // + + if (BackgroundImage != null && !DisplayInformation.HighContrast && !formRTL) { + + if (BackgroundImageLayout == ImageLayout.Tile) { + if (ControlPaint.IsImageTransparent(BackgroundImage)) { + PaintTransparentBackground(e, rectangle); + } + } + + // + + Point scrollLocation = scrollOffset; + ScrollableControl scrollControl = this as ScrollableControl; + if (scrollControl != null && scrollLocation != Point.Empty) { + scrollLocation = ((ScrollableControl)this).AutoScrollPosition; + } + + if (ControlPaint.IsImageTransparent(BackgroundImage)) { + PaintBackColor(e, rectangle, backColor); + } + + ControlPaint.DrawBackgroundImage(e.Graphics, BackgroundImage, backColor, BackgroundImageLayout, ClientRectangle, rectangle, scrollLocation, RightToLeft); + } + else { + PaintBackColor(e, rectangle, backColor); + } + } + private static void PaintBackColor(PaintEventArgs e, Rectangle rectangle, Color backColor) { + // Common case of just painting the background. For this, we + // use GDI because it is faster for simple things than creating + // a graphics object, brush, etc. Also, we may be able to + // use a system brush, avoiding the brush create altogether. + // + Color color = backColor; + + // Note: PaintEvent.HDC == 0 if GDI+ has used the HDC -- it wouldn't be safe for us + // to use it without enough bookkeeping to negate any performance gain of using GDI. + if (color.A == 255) { + using (WindowsGraphics wg = (e.HDC != IntPtr.Zero && DisplayInformation.BitsPerPixel > 8) ? + WindowsGraphics.FromHdc(e.HDC) : WindowsGraphics.FromGraphics(e.Graphics)) { + color = wg.GetNearestColor(color); + + using (WindowsBrush brush = new WindowsSolidBrush(wg.DeviceContext, color)) { + wg.FillRectangle(brush, rectangle); + } + } + } + else { + // don't paint anything from 100% transparent background + // + if (color.A > 0) { + // Color has some transparency or we have no HDC, so we must + // fall back to using GDI+. + // + using (Brush brush = new SolidBrush(color)) { + e.Graphics.FillRectangle(brush, rectangle); + } + } + } + } + + // Paints a red rectangle with a red X, painted on a white background + private void PaintException(PaintEventArgs e) { +#if false + StringFormat stringFormat = ControlPaint.StringFormatForAlignment(ContentAlignment.TopLeft); + string exceptionText = Properties.GetObject(PropPaintingException).ToString(); + stringFormat.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, exceptionText.Length)}); + + // rendering calculations... + // + int penThickness = 2; + Size glyphSize = SystemInformation.IconSize; + int marginX = penThickness * 2; + int marginY = penThickness * 2; + + Rectangle clientRectangle = ClientRectangle; + + Rectangle borderRectangle = ClientRectangle; + borderRectangle.X++; + borderRectangle.Y++; + borderRectangle.Width -=2; + borderRectangle.Height-=2; + + Rectangle imageRect = new Rectangle(marginX, marginY, glyphSize.Width, glyphSize.Height); + + Rectangle textRect = clientRectangle; + textRect.X = imageRect.X + imageRect.Width + 2 * marginX; + textRect.Y = imageRect.Y; + textRect.Width -= (textRect.X + marginX + penThickness); + textRect.Height -= (textRect.Y + marginY + penThickness); + + using (Font errorFont = new Font(Font.FontFamily, Math.Max(SystemInformation.ToolWindowCaptionHeight - SystemInformation.BorderSize.Height - 2, Font.Height), GraphicsUnit.Pixel)) { + + using(Region textRegion = e.Graphics.MeasureCharacterRanges(exceptionText, errorFont, textRect, stringFormat)[0]) { + // paint contents... clipping optimizations for less flicker... + // + Region originalClip = e.Graphics.Clip; + + e.Graphics.ExcludeClip(textRegion); + e.Graphics.ExcludeClip(imageRect); + try { + e.Graphics.FillRectangle(Brushes.White, clientRectangle); + } + finally { + e.Graphics.Clip = originalClip; + } + + using (Pen pen = new Pen(Color.Red, penThickness)) { + e.Graphics.DrawRectangle(pen, borderRectangle); + } + + Icon err = SystemIcons.Error; + + e.Graphics.FillRectangle(Brushes.White, imageRect); + e.Graphics.DrawIcon(err, imageRect.X, imageRect.Y); + + textRect.X++; + e.Graphics.IntersectClip(textRegion); + try { + e.Graphics.FillRectangle(Brushes.White, textRect); + e.Graphics.DrawString(exceptionText, errorFont, new SolidBrush(ForeColor), textRect, stringFormat); + } + finally { + e.Graphics.Clip = originalClip; + } + } + } +#else + int penThickness = 2; + using (Pen pen = new Pen(Color.Red, penThickness)) { + Rectangle clientRectangle = ClientRectangle; + Rectangle rectangle = clientRectangle; + rectangle.X++; + rectangle.Y++; + rectangle.Width--; + rectangle.Height--; + + e.Graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1); + rectangle.Inflate(-1, -1); + e.Graphics.FillRectangle(Brushes.White, rectangle); + e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Top, + clientRectangle.Right, clientRectangle.Bottom); + e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Bottom, + clientRectangle.Right, clientRectangle.Top); + } + +#endif + } + + internal void PaintTransparentBackground(PaintEventArgs e, Rectangle rectangle) { + PaintTransparentBackground(e,rectangle,null); + } + // Trick our parent into painting our background for us, or paint some default + // color if that doesn't work. + // + // This method is the hardest part of implementing transparent controls; + // call this in your OnPaintBackground method, and away you go. + // + // If you only want a region of the control to be transparent, pass in a region into the + // last parameter. A null region implies that you want the entire rectangle to be transparent. + internal void PaintTransparentBackground(PaintEventArgs e, Rectangle rectangle, Region transparentRegion) { + Graphics g = e.Graphics; + Control parent = this.ParentInternal; + + if (parent != null) { + // Refer to VSWhidbey : 440669. + // We need to use themeing painting for certain controls (like TabPage) when they parent other controls. + // But we dont want to to this always as this causes serious preformance (at Runtime and DesignTime) + // so checking for RenderTransparencyWithVisualStyles which is TRUE for TabPage and false by default. + if (Application.RenderWithVisualStyles && parent.RenderTransparencyWithVisualStyles) { + // When we are rendering with visual styles, we can use the cool DrawThemeParentBackground function + // that UxTheme provides to render the parent's background. This function is control agnostic, so + // we use the wrapper in ButtonRenderer - this should do the right thing for all controls, + // not just Buttons. + + GraphicsState graphicsState = null; + if (transparentRegion != null) { + graphicsState = g.Save(); + } + try { + if (transparentRegion != null) { + g.Clip = transparentRegion; + } + ButtonRenderer.DrawParentBackground(g, rectangle, this); + } + finally { + if (graphicsState != null) { + g.Restore(graphicsState); + } + } + + + } + else { + // how to move the rendering area and setup it's size + // (we want to translate it to the parent's origin) + Rectangle shift = new Rectangle(-this.Left,-this.Top, parent.Width, parent.Height); + + // moving the clipping rectangle to the parent coordinate system + Rectangle newClipRect = new Rectangle(rectangle.Left + this.Left, rectangle.Top + this.Top, rectangle.Width, rectangle.Height); + + using (WindowsGraphics wg = WindowsGraphics.FromGraphics(g)) { + wg.DeviceContext.TranslateTransform(-this.Left, -this.Top); + using (PaintEventArgs np = new PaintEventArgs(wg.GetHdc(), newClipRect)) { + if (transparentRegion != null) { + np.Graphics.Clip = transparentRegion; + np.Graphics.TranslateClip(-shift.X, -shift.Y); + } + try { + InvokePaintBackground(parent, np); + InvokePaint(parent, np); + } + finally { + if (transparentRegion != null) { + // restore region back to original state. + np.Graphics.TranslateClip(shift.X, shift.Y); + } + } + } + } + + } + + } + else { + // For whatever reason, our parent can't paint our background, but we need some kind of background + // since we're transparent. + g.FillRectangle(SystemBrushes.Control, rectangle); + } + } + + // Exceptions during painting are nasty, because paint events happen so often. + // So if user painting code ----s up, we make sure never to call it again, + // so as not to spam the end-user with exception dialogs. + // + private void PaintWithErrorHandling(PaintEventArgs e, short layer) { + try { + CacheTextInternal = true; + if (GetState(STATE_EXCEPTIONWHILEPAINTING)) { + if (layer == PaintLayerBackground) + PaintException(e); + } + else { + bool exceptionThrown = true; + try { + switch (layer) { + case PaintLayerForeground: + OnPaint(e); + break; + case PaintLayerBackground: + if (!GetStyle(ControlStyles.Opaque)) { + OnPaintBackground(e); + } + break; + default: + Debug.Fail("Unknown PaintLayer " + layer); + break; + } + exceptionThrown = false; + } + finally { + if (exceptionThrown) { + SetState(STATE_EXCEPTIONWHILEPAINTING, true); + Invalidate(); + } + } + } + } + finally { + CacheTextInternal = false; + } + } + + /// + /// + /// Find ContainerControl that is the container of this control. + /// + /// + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal ContainerControl ParentContainerControl { + get { + for (Control c = this.ParentInternal; c != null; c = c.ParentInternal) { + if (c is ContainerControl) { + return c as ContainerControl; + } + } + + return null; + } + } + + /// + /// + /// Forces the control to apply layout logic to all of the child controls. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void PerformLayout() { + if (cachedLayoutEventArgs != null) { + PerformLayout(cachedLayoutEventArgs); + cachedLayoutEventArgs = null; + + // VSWhidbey 464817 - we need to be careful + // about which LayoutEventArgs are used in + // SuspendLayout, PerformLayout, ResumeLayout() sequences. + // See bug for more info. + SetState2(STATE2_CLEARLAYOUTARGS, false); + } + else { + PerformLayout(null, null); + } + } + + /// + /// + /// Forces the control to apply layout logic to all of the child controls. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void PerformLayout(Control affectedControl, string affectedProperty) { + PerformLayout(new LayoutEventArgs(affectedControl, affectedProperty)); + } + + internal void PerformLayout(LayoutEventArgs args) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (layoutSuspendCount > 0) { + SetState(STATE_LAYOUTDEFERRED, true); + if (cachedLayoutEventArgs == null || (GetState2(STATE2_CLEARLAYOUTARGS) && args != null)) { + cachedLayoutEventArgs = args; + if (GetState2(STATE2_CLEARLAYOUTARGS)) { + SetState2(STATE2_CLEARLAYOUTARGS, false); + } + } + + LayoutEngine.ProcessSuspendedLayoutEventArgs(this, args); + + return; + } + + // (Essentially the same as suspending layout while we layout, but we clear differently below.) + layoutSuspendCount = 1; + + try { + CacheTextInternal = true; + OnLayout(args); + +#if DEBUG +#if DEBUG_PREFERREDSIZE + // DO NOT REMOVE - See http://wiki/default.aspx/Microsoft.Projects.DotNetClient.PreferredSize + // PreferredSize has an assert that will verify that the current cached size matches + // the computed size. The calls to PreferredSize below help to catch a mismatch earlier. + if (!this.GetState(STATE_DISPOSING | STATE_DISPOSED)) { + if (GetState2(STATE2_USEPREFERREDSIZECACHE)) { + this.PreferredSize.ToString(); + } + + if (args.AffectedControl != null && args.AffectedControl != this && args.AffectedControl.GetState2(STATE2_USEPREFERREDSIZECACHE)) { + args.AffectedControl.PreferredSize.ToString(); + } + } +#endif +#endif + + + } + finally { + CacheTextInternal = false; + // Rather than resume layout (which will could allow a deferred layout to layout the + // the container we just finished laying out) we set layoutSuspendCount back to zero + // and clear the deferred and dirty flags. + SetState(STATE_LAYOUTDEFERRED | STATE_LAYOUTISDIRTY, false); + layoutSuspendCount = 0; + + // LayoutEngine.Layout can return true to request that our parent resize us because + // we did not have enough room for our contents. Now that we are unsuspended, + // see if this happened and layout parent if necessary. (See also OnLayout) + if(ParentInternal != null && ParentInternal.GetState(STATE_LAYOUTISDIRTY)) { + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.PreferredSize); + } + } + } + + /// + /// Peforms data validation (not paint validation!) on a single control. + /// + /// Returns whether validation failed: + /// False = Validation succeeded, control is valid, accept its new value + /// True = Validation was cancelled, control is invalid, reject its new value + /// + /// NOTE: This is the lowest possible level of validation. It does not account + /// for the context in which the validation is occuring, eg. change of focus + /// between controls in a container. Stuff like that is handled by the caller. + /// + /// + internal bool PerformControlValidation(bool bulkValidation) { + + // Skip validation for controls that don't support it + if (!this.CausesValidation) { + return false; + } + + // Raise the 'Validating' event. Stop now if handler cancels (ie. control is invalid). + // NOTE: Handler may throw an exception here, but we must not attempt to catch it. + if (this.NotifyValidating()) { + return true; + } + + // Raise the 'Validated' event. Handlers may throw exceptions here too - but + // convert these to ThreadException events, unless the app is being debugged, + // or the control is being validated as part of a bulk validation operation. + if (bulkValidation || NativeWindow.WndProcShouldBeDebuggable) { + this.NotifyValidated(); + } + else { + try { + this.NotifyValidated(); + } + catch (Exception e) { + Application.OnThreadException(e); + } + } + + return false; + } + + /// + /// Validates all the child controls in a container control. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// Return value indicates whether validation failed for any of the controls validated. + /// Calling function is responsible for checking the correctness of the validationConstraints argument. + /// + /// + internal bool PerformContainerValidation(ValidationConstraints validationConstraints) { + bool failed = false; + + // For every child control of this container control... + foreach (Control c in Controls) { + + // First, if the control is a container, recurse into its descendants. + if ((validationConstraints & ValidationConstraints.ImmediateChildren) != ValidationConstraints.ImmediateChildren && + c.ShouldPerformContainerValidation() && + c.PerformContainerValidation(validationConstraints)) { + failed = true; + } + + // Next, use input flags to decide whether to validate the control itself + if ((validationConstraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle(ControlStyles.Selectable) || + (validationConstraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled || + (validationConstraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible || + (validationConstraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop) { + continue; + } + + // Finally, perform validation on the control itself + if (c.PerformControlValidation(true)) { + failed = true; + } + } + + return failed; + } + + /// + /// + /// Computes the location of the screen point p in client coords. + /// + public Point PointToClient(Point p) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + return PointToClientInternal(p); + } + internal Point PointToClientInternal(Point p) { + // ASURT 42367. + // Win9x reports incorrect values if you go outside the 16-bit range. + // We're not going to do anything about it, though -- it's esoteric, it clutters up the code, + // and potentially causes problems on systems that do support 32-bit coordinates. + + NativeMethods.POINT point = new NativeMethods.POINT(p.X, p.Y); + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(this, Handle), point, 1); + return new Point(point.x, point.y); + } + + /// + /// + /// Computes the location of the client point p in screen coords. + /// + public Point PointToScreen(Point p) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.POINT point = new NativeMethods.POINT(p.X, p.Y); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, Handle), NativeMethods.NullHandleRef, point, 1); + return new Point(point.x, point.y); + } + + /// + /// + /// + /// This method is called by the application's message loop to pre-process + /// input messages before they are dispatched. Possible values for the + /// msg.message field are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR. + /// If this method processes the message it must return true, in which case + /// the message loop will not dispatch the message. + /// + /// + /// For WM_KEYDOWN and WM_SYSKEYDOWN messages, preProcessMessage() first + /// calls processCmdKey() to check for command keys such as accelerators and + /// menu shortcuts. If processCmdKey() doesn't process the message, then + /// isInputKey() is called to check whether the key message represents an + /// input key for the control. Finally, if isInputKey() indicates that the + /// control isn't interested in the key message, then processDialogKey() is + /// called to check for dialog keys such as TAB, arrow keys, and mnemonics. + /// + /// + /// For WM_CHAR messages, preProcessMessage() first calls isInputChar() to + /// check whether the character message represents an input character for + /// the control. If isInputChar() indicates that the control isn't interested + /// in the character message, then processDialogChar() is called to check for + /// dialog characters such as mnemonics. + /// + /// + /// For WM_SYSCHAR messages, preProcessMessage() calls processDialogChar() + /// to check for dialog characters such as mnemonics. + /// + /// + /// When overriding preProcessMessage(), a control should return true to + /// indicate that it has processed the message. For messages that aren't + /// processed by the control, the result of "base.preProcessMessage()" + /// should be returned. Controls will typically override one of the more + /// specialized methods (isInputChar(), isInputKey(), processCmdKey(), + /// processDialogChar(), or processDialogKey()) instead of overriding + /// preProcessMessage(). + /// + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public virtual bool PreProcessMessage(ref Message msg) { + // Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessMessage " + msg.ToString()); + + bool ret; + + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + if (!GetState2(STATE2_UICUES)) { + ProcessUICues(ref msg); + } + + Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys); + if (ProcessCmdKey(ref msg, keyData)) { + ret = true; + } + else if (IsInputKey(keyData)) { + SetState2(STATE2_INPUTKEY, true); + ret = false; + } + else { + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = ProcessDialogKey(keyData); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) { + if (msg.Msg == NativeMethods.WM_CHAR && IsInputChar((char)msg.WParam)) { + SetState2(STATE2_INPUTCHAR, true); + ret = false; + } + else { + ret = ProcessDialogChar((char)msg.WParam); + } + } + else { + ret = false; + } + + return ret; + } + + [EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly"), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters"), + SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] // using ref is OK. + public PreProcessControlState PreProcessControlMessage(ref Message msg) { + return PreProcessControlMessageInternal(null, ref msg); + } + + /// + /// This method is similar to PreProcessMessage, but instead of indicating + /// that the message was either processed or it wasn't, it has three return + /// values: + /// + /// MessageProcessed - PreProcessMessage() returns true, and the message + /// needs no further processing + /// + /// MessageNeeded - PreProcessMessage() returns false, but IsInputKey/Char + /// return true. This means the message wasn't processed, + /// but the control is interested in it. + /// + /// MessageNotNeeded - PreProcessMessage() returns false, and IsInputKey/Char + /// return false. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + internal static PreProcessControlState PreProcessControlMessageInternal(Control target, ref Message msg) { + if (target == null) { + target = Control.FromChildHandleInternal(msg.HWnd); + } + + if (target == null) { + return PreProcessControlState.MessageNotNeeded; + } + + // reset state that is used to make sure IsInputChar, IsInputKey and + // ProcessUICues are not called multiple times. + // ISSUE: Which control should these state bits be set on? probably the target. + target.SetState2(STATE2_INPUTKEY, false); + target.SetState2(STATE2_INPUTCHAR, false); + target.SetState2(STATE2_UICUES, true); + + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessControlMessageInternal " + msg.ToString()); + + try { + Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys); + + // Allow control to preview key down message. + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + target.ProcessUICues(ref msg); + + PreviewKeyDownEventArgs args = new PreviewKeyDownEventArgs(keyData); + target.OnPreviewKeyDown(args); + + if (args.IsInputKey) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "PreviewKeyDown indicated this is an input key."); + // Control wants this message - indicate it should be dispatched. + return PreProcessControlState.MessageNeeded; + } + } + + PreProcessControlState state = PreProcessControlState.MessageNotNeeded; + + if (!target.PreProcessMessage(ref msg)) { + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + + // check if IsInputKey has already procssed this message + // or if it is safe to call - we only want it to be called once. + if (target.GetState2(STATE2_INPUTKEY) || target.IsInputKey(keyData)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched"); + state = PreProcessControlState.MessageNeeded; + } + } + else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) { + + // check if IsInputChar has already procssed this message + // or if it is safe to call - we only want it to be called once. + if (target.GetState2(STATE2_INPUTCHAR) || target.IsInputChar((char)msg.WParam)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched"); + state = PreProcessControlState.MessageNeeded; + } + } + } + else { + state = PreProcessControlState.MessageProcessed; + } + + return state; + } + finally { + target.SetState2(STATE2_UICUES, false); + } + } + + /// + /// + /// + /// Processes a command key. This method is called during message + /// pre-processing to handle command keys. Command keys are keys that always + /// take precedence over regular input keys. Examples of command keys + /// include accelerators and menu shortcuts. The method must return true to + /// indicate that it has processed the command key, or false to indicate + /// that the key is not a command key. + /// + /// + /// processCmdKey() first checks if the control has a context menu, and if + /// so calls the menu's processCmdKey() to check for menu shortcuts. If the + /// command key isn't a menu shortcut, and if the control has a parent, the + /// key is passed to the parent's processCmdKey() method. The net effect is + /// that command keys are "bubbled" up the control hierarchy. + /// + /// + /// When overriding processCmdKey(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed by + /// the control, the result of "base.processCmdKey()" should be returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessCmdKey(ref Message msg, Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessCmdKey " + msg.ToString()); + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null && contextMenu.ProcessCmdKey(ref msg, keyData, this)) { + return true; + } + + if (parent != null) { + return parent.ProcessCmdKey(ref msg, keyData); + } + return false; + + } + + private void PrintToMetaFile(HandleRef hDC, IntPtr lParam) { + Debug.Assert(UnsafeNativeMethods.GetObjectType(hDC) == NativeMethods.OBJ_ENHMETADC, + "PrintToMetaFile() called with a non-Enhanced MetaFile DC."); + Debug.Assert(((long) lParam & NativeMethods.PRF_CHILDREN) != 0, + "PrintToMetaFile() called without PRF_CHILDREN."); + + // Strip the PRF_CHILDREN flag. We will manually walk our children and print them. + lParam = (IntPtr)((long) lParam & ~NativeMethods.PRF_CHILDREN); + + // We're the root contol, so we need to set up our clipping region. Retrieve the + // x-coordinates and y-coordinates of the viewport origin for the specified device context. + NativeMethods.POINT viewportOrg = new NativeMethods.POINT(); + bool success = SafeNativeMethods.GetViewportOrgEx(hDC, viewportOrg); + Debug.Assert(success, "GetViewportOrgEx() failed."); + + HandleRef hClippingRegion = new HandleRef(null, SafeNativeMethods.CreateRectRgn(viewportOrg.x,viewportOrg.y, viewportOrg.x + this.Width, viewportOrg.y + this.Height)); + Debug.Assert(hClippingRegion.Handle != IntPtr.Zero, "CreateRectRgn() failed."); + + try { + // Select the new clipping region; make sure it's a SIMPLEREGION or NULLREGION + NativeMethods.RegionFlags selectResult = (NativeMethods.RegionFlags)SafeNativeMethods.SelectClipRgn(hDC, hClippingRegion); + Debug.Assert((selectResult == NativeMethods.RegionFlags.SIMPLEREGION || + selectResult == NativeMethods.RegionFlags.NULLREGION), + "SIMPLEREGION or NULLLREGION expected."); + + PrintToMetaFileRecursive(hDC, lParam, new Rectangle(Point.Empty, this.Size)); + } + finally { + success = SafeNativeMethods.DeleteObject(hClippingRegion); + Debug.Assert(success, "DeleteObject() failed."); + } + } + + internal virtual void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + // We assume the target does not want us to offset the root control in the metafile. + + using (WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, bounds)) { + // print the non-client area + PrintToMetaFile_SendPrintMessage(hDC, (IntPtr)((long)lParam & ~NativeMethods.PRF_CLIENT)); + + // figure out mapping for the client area + NativeMethods.RECT windowRect = new NativeMethods.RECT(); + bool success = UnsafeNativeMethods.GetWindowRect(new HandleRef(null, this.Handle), ref windowRect); + Debug.Assert(success, "GetWindowRect() failed."); + Point clientOffset = PointToScreen(Point.Empty); + clientOffset = new Point(clientOffset.X - windowRect.left, clientOffset.Y - windowRect.top); + Rectangle clientBounds = new Rectangle(clientOffset, ClientSize); + + using (WindowsFormsUtils.DCMapping clientMapping = new WindowsFormsUtils.DCMapping(hDC, clientBounds)) { + // print the client area + PrintToMetaFile_SendPrintMessage(hDC, (IntPtr)((long)lParam & ~NativeMethods.PRF_NONCLIENT)); + + // Paint children in reverse Z-Order + int count = Controls.Count; + for (int i = count - 1; i >= 0; i--) { + Control child = Controls[i]; + if(child.Visible) { + child.PrintToMetaFileRecursive(hDC, lParam, child.Bounds); + } + } + } + } + } + + private void PrintToMetaFile_SendPrintMessage(HandleRef hDC, IntPtr lParam) { + if(GetStyle(ControlStyles.UserPaint)) { + // We let user paint controls paint directly into the metafile + SendMessage(NativeMethods.WM_PRINT, hDC.Handle, lParam); + } + else { + // If a system control has no children in the Controls collection we + // restore the the PRF_CHILDREN flag because it may internally + // have nested children we do not know about. ComboBox is a + // good example. + if(Controls.Count == 0) { + lParam = (IntPtr)((long) lParam | NativeMethods.PRF_CHILDREN); + } + + // System controls must be painted into a temporary bitmap + // which is then copied into the metafile. (Old GDI line drawing + // is 1px thin, which causes borders to disappear, etc.) + using (MetafileDCWrapper dcWrapper = new MetafileDCWrapper(hDC, this.Size)) { + SendMessage(NativeMethods.WM_PRINT, dcWrapper.HDC, lParam); + } + } + } + + /// + /// + /// + /// Processes a dialog character. This method is called during message + /// pre-processing to handle dialog characters, such as control mnemonics. + /// This method is called only if the isInputChar() method indicates that + /// the control isn't interested in the character. + /// + /// + /// processDialogChar() simply sends the character to the parent's + /// processDialogChar() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog characters. + /// + /// + /// When overriding processDialogChar(), a control should return true to + /// indicate that it has processed the character. For characters that aren't + /// processed by the control, the result of "base.processDialogChar()" + /// should be returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool ProcessDialogChar(char charCode) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessDialogChar [" + charCode.ToString() + "]"); + return parent == null? false: parent.ProcessDialogChar(charCode); + } + + /// + /// + /// + /// Processes a dialog key. This method is called during message + /// pre-processing to handle dialog characters, such as TAB, RETURN, ESCAPE, + /// and arrow keys. This method is called only if the isInputKey() method + /// indicates that the control isn't interested in the key. + /// + /// + /// processDialogKey() simply sends the character to the parent's + /// processDialogKey() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog keys. + /// + /// + /// When overriding processDialogKey(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processDialogKey(...)" should be + /// returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool ProcessDialogKey(Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessDialogKey " + keyData.ToString()); + return parent == null? false: parent.ProcessDialogKey(keyData); + } + + /// + /// + /// + /// Processes a key message. This method is called when a control receives a + /// keyboard message. The method is responsible for generating the appropriate + /// key events for the message by calling OnKeyPress(), onKeyDown(), or + /// onKeyUp(). The m parameter contains the window message that must + /// be processed. Possible values for the m.msg field are WM_CHAR, + /// WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, and WM_IMECHAR. + /// + /// + /// When overriding processKeyEventArgs(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processKeyEventArgs()" should be + /// returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessKeyEventArgs(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyEventArgs " + m.ToString()); + KeyEventArgs ke = null; + KeyPressEventArgs kpe = null; + IntPtr newWParam = IntPtr.Zero; + + if (m.Msg == NativeMethods.WM_CHAR || m.Msg == NativeMethods.WM_SYSCHAR) { + int charsToIgnore = this.ImeWmCharsToIgnore; + + if (charsToIgnore > 0) { + charsToIgnore--; + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "charsToIgnore decreased, new val = " + charsToIgnore + ", this=" + this); + + this.ImeWmCharsToIgnore = charsToIgnore; + return false; + } + else { + kpe = new KeyPressEventArgs(unchecked((char)(long)m.WParam)); + OnKeyPress(kpe); + newWParam = (IntPtr) kpe.KeyChar; + } + } + else if (m.Msg == NativeMethods.WM_IME_CHAR) { + int charsToIgnore = this.ImeWmCharsToIgnore; + + if (Marshal.SystemDefaultCharSize == 1) { + // On Win9X we get either an SBCS or an MBCS value. We must convert it to + // UNICODE to use in the KeyPressEventArg. + // + // VSWhidbey 145962: Convert the character in this message to UNICODE + // for use in the KeyPress event, but then convert it back from UNICODE + // to its source value for processing in the DefWindowProc, which will expect a + // non-UNICODE character. + char unicodeChar = (char)0; + byte[] b = new byte[] {(byte)(unchecked((int)(long)m.WParam) >> 8), unchecked((byte)(long)m.WParam)}; + char[] unicodeCharArray = new char[1]; + int stringLength = UnsafeNativeMethods.MultiByteToWideChar(0 /*CP_ACP*/, UnsafeNativeMethods.MB_PRECOMPOSED, b, b.Length, unicodeCharArray, 0); + if (stringLength > 0) { + unicodeCharArray = new char[stringLength]; + UnsafeNativeMethods.MultiByteToWideChar(0 /*CP_ACP*/, UnsafeNativeMethods.MB_PRECOMPOSED, b, b.Length, unicodeCharArray, unicodeCharArray.Length); + + // Per the docs for WM_IME_CHAR, the processing of this message by DefWindowProc + // will produce two WM_CHAR messages if the character value is a DBCS character. + // Otherwise, only one WM_CHAR message will be generated. Therefore, only ignore + // one WM_CHAR message for SBCS characters and two WM_CHAR messages for DBCS + // characters. + + // What type of character were we passed? + if (unicodeCharArray[0] != 0) { + // This was an MBCS character, so pass along the first character + // from the resultant array. + unicodeChar = unicodeCharArray[0]; + + charsToIgnore += 2; + } + else if (unicodeCharArray[0] == 0 && unicodeCharArray.Length >= 2) { + // This was an SBCS character, so pass along the second character + // from the resultant array since the first character in the array is NULL. + unicodeChar = unicodeCharArray[1]; + + charsToIgnore += 1; + } + } + else { + //MultiByteToWideChar failed + throw new Win32Exception(); + } + + this.ImeWmCharsToIgnore = charsToIgnore; + + // + kpe = new KeyPressEventArgs(unicodeChar); + } + else { + charsToIgnore += (3 - Marshal.SystemDefaultCharSize); + this.ImeWmCharsToIgnore = charsToIgnore; + + kpe = new KeyPressEventArgs(unchecked((char)(long)m.WParam)); + } + + char preEventCharacter = kpe.KeyChar; + OnKeyPress(kpe); + + //If the character wasn't changed, just use the original value rather than round tripping. + if (kpe.KeyChar == preEventCharacter) { + newWParam = m.WParam; + } + else { + if (Marshal.SystemDefaultCharSize == 1) { + // VSWhidbey 145962: On Win9X we work with either an SBCS or an MBCS value. Since we + // already converted it to UNICODE to send it to the KeyPress event, we must now convert + // it back to either MBCS or SBCS for processing by the DefWindowProc. + // + string keyChar = new string(new char[] { kpe.KeyChar }); + byte[] mbcsBytes = null; + int bytesNeeded = UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, keyChar, keyChar.Length, null, 0, IntPtr.Zero, IntPtr.Zero); + // GB18030 defines 4 byte characters: we shouldn't assume that the length is capped at 2. + if (bytesNeeded >= 2) { + // This is an MBCS character. + mbcsBytes = new byte[bytesNeeded]; + UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, keyChar, keyChar.Length, mbcsBytes, mbcsBytes.Length, IntPtr.Zero, IntPtr.Zero); + + int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr)); + if (bytesNeeded > sizeOfIntPtr) { + bytesNeeded = sizeOfIntPtr; //Same again: we wouldn't be able to stuff anything larger into a WParam + } + long wParam = 0; + for (int i = 0; i < bytesNeeded; i++) { + wParam <<= 8; + wParam |= (long)mbcsBytes[i]; + } + newWParam = (IntPtr)wParam; + } + else if (bytesNeeded == 1) { + // This is an SBCS character. + mbcsBytes = new byte[bytesNeeded]; + UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, + 0, + keyChar, + keyChar.Length, + mbcsBytes, + mbcsBytes.Length, + IntPtr.Zero, + IntPtr.Zero); + newWParam = (IntPtr)((int)mbcsBytes[0]); + } + else { + //We don't know what's going on: WideCharToMultiByte failed. We can't deal with that. + newWParam = m.WParam; + } + } + else { + newWParam = (IntPtr)kpe.KeyChar; + } + } + } + else { + ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) { + OnKeyDown(ke); + } + else { + OnKeyUp(ke); + } + } + + if (kpe != null) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " processkeyeventarg returning: " + kpe.Handled); + m.WParam = newWParam; + return kpe.Handled; + } + else { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " processkeyeventarg returning: " + ke.Handled); + if (ke.SuppressKeyPress) { + RemovePendingMessages(NativeMethods.WM_CHAR, NativeMethods.WM_CHAR); + RemovePendingMessages(NativeMethods.WM_SYSCHAR, NativeMethods.WM_SYSCHAR); + RemovePendingMessages(NativeMethods.WM_IME_CHAR, NativeMethods.WM_IME_CHAR); + } + return ke.Handled; + } + } + + /// + /// + /// Processes a key message. This method is called when a control receives a + /// keyboard message. The method first checks if the control has a parent, + /// and if so calls the parent's processKeyPreview() method. If the parent's + /// processKeyPreview() method doesn't consume the message then + /// processKeyEventArgs() is called to generate the appropriate keyboard events. + /// The m parameter contains the window message that must be + /// processed. Possible values for the m.msg field are WM_CHAR, + /// WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and WM_SYSKEYUP. + /// When overriding processKeyMessage(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processKeyMessage()" should be + /// returned. + /// Controls will seldom, if ever, need to override this method. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessKeyMessage(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyMessage " + m.ToString()); + if (parent != null && parent.ProcessKeyPreview(ref m)) return true; + return ProcessKeyEventArgs(ref m); + } + + /// + /// + /// + /// Previews a keyboard message. This method is called by a child control + /// when the child control receives a keyboard message. The child control + /// calls this method before generating any keyboard events for the message. + /// If this method returns true, the child control considers the message + /// consumed and does not generate any keyboard events. The m + /// parameter contains the window message to preview. Possible values for + /// the m.msg field are WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, + /// and WM_SYSKEYUP. + /// + /// + /// processKeyPreview() simply sends the character to the parent's + /// processKeyPreview() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog keys. + /// + /// + /// When overriding processKeyPreview(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.ProcessKeyPreview(...)" should be + /// returned. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessKeyPreview(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyPreview " + m.ToString()); + return parent == null? false: parent.ProcessKeyPreview(ref m); + } + + /// + /// + /// + /// Processes a mnemonic character. This method is called to give a control + /// the opportunity to process a mnemonic character. The method should check + /// if the control is in a state to process mnemonics and if the given + /// character represents a mnemonic. If so, the method should perform the + /// action associated with the mnemonic and return true. If not, the method + /// should return false. + /// + /// + /// Implementations of this method often use the isMnemonic() method to + /// check if the given character matches a mnemonic in the control's text, + /// for example: + /// + /// if (canSelect() && isMnemonic(charCode, getText()) { + /// // perform action associated with mnemonic + /// } + /// + /// + /// + /// This default implementation of processMnemonic() simply returns false + /// to indicate that the control has no mnemonic. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal virtual bool ProcessMnemonic(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessMnemonic [0x" + ((int)charCode).ToString("X", CultureInfo.InvariantCulture) + "]"); +#endif + return false; + } + + /// + /// Preprocess keys which affect focus indicators and keyboard cues. + /// + internal void ProcessUICues(ref Message msg) { + Keys keyCode = (Keys)((int)msg.WParam) & Keys.KeyCode; + + if (keyCode != Keys.F10 && keyCode != Keys.Menu && keyCode != Keys.Tab) { + return; // PERF: dont WM_QUERYUISTATE if we dont have to. + } + + Control topMostParent = null; + int current = unchecked( (int) (long)SendMessage(NativeMethods.WM_QUERYUISTATE, 0, 0)); + + // VSWhidbey 536248 - dont trust when a control says the accelerators are showing. + // make sure the topmost parent agrees with this as we could be in a mismatched state. + if (current == 0 /*accelerator and focus cues are showing*/) { + topMostParent = this.TopMostParent; + current = (int)topMostParent.SendMessage(NativeMethods.WM_QUERYUISTATE, 0, 0); + } + int toClear = 0; + + // if we are here, a key or tab has been pressed on this control. + // now that we know the state of accelerators, check to see if we need + // to show them. NOTE: due to the strangeness of the API we OR in + // the opposite of what we want to do. So if we want to show accelerators, + // we OR in UISF_HIDEACCEL, then call UIS_CLEAR to clear the "hidden" state. + + if (keyCode == Keys.F10 || keyCode == Keys.Menu) { + if ((current & NativeMethods.UISF_HIDEACCEL) != 0) { + // Keyboard accelerators are hidden, they need to be shown + toClear |= NativeMethods.UISF_HIDEACCEL; + } + } + + if (keyCode == Keys.Tab) { + if ((current & NativeMethods.UISF_HIDEFOCUS) != 0) { + // Focus indicators are hidden, they need to be shown + toClear |= NativeMethods.UISF_HIDEFOCUS; + } + } + + if (toClear != 0) { + // We've detected some state we need to unset, usually clearing the hidden state of + // the accelerators. We need to get the topmost parent and call CHANGEUISTATE so + // that the entire tree of controls is + if (topMostParent == null) { + topMostParent = this.TopMostParent; + } + + // VSWhidbey 434901,362408 + // A) if we're parented to a native dialog - REFRESH our child states ONLY + // Then we've got to send a WM_UPDATEUISTATE to the topmost managed control (which will be non-toplevel) + // (we assume here the root native window has changed UI state, and we're not to manage the UI state for it) + // + // B) if we're totally managed - CHANGE the root window state AND REFRESH our child states. + // Then we've got to send a WM_CHANGEUISTATE to the topmost managed control (which will be toplevel) + // According to MSDN, WM_CHANGEUISTATE will generate WM_UPDATEUISTATE messages for all immediate children (via DefWndProc) + // (we're in charge here, we've got to change the state of the root window) + UnsafeNativeMethods.SendMessage( + new HandleRef(topMostParent, topMostParent.Handle), + UnsafeNativeMethods.GetParent(new HandleRef(null, topMostParent.Handle)) == IntPtr.Zero ? NativeMethods.WM_CHANGEUISTATE : NativeMethods.WM_UPDATEUISTATE, + (IntPtr)(NativeMethods.UIS_CLEAR | (toClear << 16)), + IntPtr.Zero); + } + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseDragEvent(Object key, DragEventArgs e) { + DragEventHandler handler = (DragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaisePaintEvent(Object key, PaintEventArgs e) { + PaintEventHandler handler = (PaintEventHandler)Events[EventPaint]; + if (handler != null) handler(this, e); + } + + private void RemovePendingMessages(int msgMin, int msgMax) { + if (!this.IsDisposed) { + NativeMethods.MSG msg = new NativeMethods.MSG(); + IntPtr hwnd = this.Handle; + while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, hwnd), + msgMin, msgMax, + NativeMethods.PM_REMOVE)) { + ; // NULL loop + } + } + } + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetBackColor() { + BackColor = Color.Empty; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetCursor() { + Cursor = null; + } + + private void ResetEnabled() { + Enabled = true; + } + + /// + /// + /// Resets the font to be based on the parent's font. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetFont() { + Font = null; + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetForeColor() { + ForeColor = Color.Empty; + } + + private void ResetLocation() { + Location = new Point(0,0); + } + + private void ResetMargin() { + Margin = DefaultMargin; + } + + private void ResetMinimumSize() { + MinimumSize = DefaultMinimumSize; + } + + private void ResetPadding() { + CommonProperties.ResetPadding(this); + } + + private void ResetSize() { + Size = DefaultSize; + } + + /// + /// + /// Resets the RightToLeft to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetRightToLeft() { + RightToLeft = RightToLeft.Inherit; + } + + /// + /// + /// Forces the recreation of the handle for this control. Inheriting controls + /// must call base.RecreateHandle. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected void RecreateHandle() { + RecreateHandleCore(); + } + + internal virtual void RecreateHandleCore() { + // + lock(this) { + if (IsHandleCreated) { + + bool focused = ContainsFocus; + +#if DEBUG + if (CoreSwitches.PerfTrack.Enabled) { + Debug.Write("RecreateHandle: "); + Debug.Write(GetType().FullName); + Debug.Write(" [Text="); + Debug.Write(Text); + Debug.Write("]"); + Debug.WriteLine(""); + } +#endif + bool created = (state & STATE_CREATED) != 0; + if (GetState(STATE_TRACKINGMOUSEEVENT)) { + SetState(STATE_MOUSEENTERPENDING, true); + UnhookMouseEvent(); + } + + HandleRef parentHandle = new HandleRef(this, UnsafeNativeMethods.GetParent(new HandleRef(this, this.Handle))); + + try { + Control [] controlSnapshot = null; + state |= STATE_RECREATE; + + try { + + + // VSWhidbey 228656 RecreateHandle - destroy window destroys all children + // Inform child controls that their parent is recreating handle. + + // The default behavior is to now SetParent to parking window, then + // SetParent back after the parent's handle has been recreated. + // This behavior can be overridden in OnParentHandleRecreat* and is in ListView. + + //fish out control collection w/o demand creating one. + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null && controlsCollection.Count > 0) { + controlSnapshot = new Control[controlsCollection.Count]; + for (int i = 0; i < controlsCollection.Count; i++) { + Control childControl = controlsCollection[i]; + if (childControl != null && childControl.IsHandleCreated) { + // SetParent to parking window + childControl.OnParentHandleRecreating(); + + // if we were successful, remember this control + // so we can raise OnParentHandleRecreated + controlSnapshot[i] = childControl; + } + else { + // put in a null slot which we'll skip over later. + controlSnapshot[i] = null; + } + } + } + + // do the main work of recreating the handle + DestroyHandle(); + CreateHandle(); + } + finally { + state &= ~STATE_RECREATE; + + // inform children that their parent's handle has recreated + if (controlSnapshot != null) { + for (int i = 0; i < controlSnapshot.Length; i++) { + Control childControl = controlSnapshot[i]; + if (childControl != null && childControl.IsHandleCreated) { + // SetParent back to the new Parent handle + childControl.OnParentHandleRecreated(); + } + } + } + } + if (created) { + CreateControl(); + } + } + finally { + if (parentHandle.Handle != IntPtr.Zero // the parent was not null + && (Control.FromHandleInternal(parentHandle.Handle) == null || this.parent == null) // but wasnt a windows forms window + && UnsafeNativeMethods.IsWindow(parentHandle)) { // and still is a window + // correctly parent back up to where we were before. + // if we were parented to a proper windows forms control, CreateControl would have properly parented + // us back. + UnsafeNativeMethods.SetParent(new HandleRef(this, this.Handle), parentHandle); + } + } + + + // Restore control focus + if (focused) { + FocusInternal(); + } + } + } + } + + /// + /// + /// Computes the location of the screen rectangle r in client coords. + /// + public Rectangle RectangleToClient(Rectangle r) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(this, Handle), ref rect, 2); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// Computes the location of the client rectangle r in screen coords. + /// + public Rectangle RectangleToScreen(Rectangle r) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, Handle), NativeMethods.NullHandleRef, ref rect, 2); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// Reflects the specified message to the control that is bound to hWnd. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static bool ReflectMessage(IntPtr hWnd, ref Message m) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SendMessages Demanded"); + IntSecurity.SendMessages.Demand(); + return ReflectMessageInternal(hWnd, ref m); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static bool ReflectMessageInternal(IntPtr hWnd, ref Message m) { + Control control = Control.FromHandleInternal(hWnd); + if (control == null) return false; + m.Result = control.SendMessage(NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + return true; + } + + /// + /// + /// Forces the control to invalidate and immediately + /// repaint itself and any children. + /// + public virtual void Refresh() { + Invalidate(true); + Update(); + } + + /// + /// /Releases UI Automation provinder for specified window. + /// + /// The window handle. + internal virtual void ReleaseUiaProvider(IntPtr handle) { + // When a window that previously returned providers has been destroyed, + // you should notify UI Automation by calling the UiaReturnRawElementProvider + // as follows: UiaReturnRawElementProvider(hwnd, 0, 0, NULL). This call tells + // UI Automation that it can safely remove all map entries that refer to the specified window. + UnsafeNativeMethods.UiaReturnRawElementProvider(new HandleRef(this, handle), new IntPtr(0), new IntPtr(0), null); + } + + /// + /// + /// Resets the mouse leave listeners. + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void ResetMouseEventArgs() { + if (GetState(STATE_TRACKINGMOUSEEVENT)) { + UnhookMouseEvent(); + HookMouseEvent(); + } + } + + /// + /// + /// Resets the text to it's default value. + /// + public virtual void ResetText() { + Text = String.Empty; + } + + private void ResetVisible() { + Visible = true; + } + + /// + /// + /// Resumes normal layout logic. This will force a layout immediately + /// if there are any pending layout requests. + /// + public void ResumeLayout() { + ResumeLayout(true); + } + + /// + /// + /// Resumes normal layout logic. If performLayout is set to true then + /// this will force a layout immediately if there are any pending layout requests. + /// + public void ResumeLayout(bool performLayout) { +#if DEBUG + if (CompModSwitches.LayoutSuspendResume.TraceInfo) { + Debug.WriteLine(GetType().Name + "::ResumeLayout( preformLayout = " + performLayout + ", newCount = " + Math.Max(0, layoutSuspendCount - 1) + ")"); + } +#endif + Debug.Assert(layoutSuspendCount > 0, "Unbalanance suspend/resume layout."); + + bool performedLayout = false; + + if (layoutSuspendCount > 0) { + + if (layoutSuspendCount == 1) { + layoutSuspendCount++; + try { + OnLayoutResuming(performLayout); + } + finally { + layoutSuspendCount--; + } + } + + layoutSuspendCount--; + if (layoutSuspendCount == 0 + && GetState(STATE_LAYOUTDEFERRED) + && performLayout) { + PerformLayout(); + performedLayout = true; + } + } + + + // VSWhidbey 464817 - we need to be careful + // about which LayoutEventArgs are used in + // SuspendLayout, PerformLayout, ResumeLayout() sequences. + // See bug for more info. + if (!performedLayout) { + SetState2(STATE2_CLEARLAYOUTARGS, true); + } + + + /* + + We've had this since Everett,but it seems wrong, redundant and a performance hit. The + correct layout calls are already made when bounds or parenting changes, which is all + we care about. We may want to call this at layout suspend count == 0, but certainaly + not for all resumes. I tried removing it, and doing it only when suspendCount == 0, + but we break things at every step. + + */ + if (!performLayout) { + + CommonProperties.xClearPreferredSizeCache(this); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + if (controlsCollection != null) { + for (int i = 0; i < controlsCollection.Count; i++) { + LayoutEngine.InitLayout(controlsCollection[i], BoundsSpecified.All); + CommonProperties.xClearPreferredSizeCache(controlsCollection[i]); + } + } + } + + } + + /// + /// Used to actually register the control as a drop target. + /// + /// + internal void SetAcceptDrops(bool accept) { + if (accept != GetState(STATE_DROPTARGET) && IsHandleCreated) { + try { + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + if (accept) { + IntSecurity.ClipboardRead.Demand(); + + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Registering as drop target: " + Handle.ToString()); + // Register + int n = UnsafeNativeMethods.RegisterDragDrop(new HandleRef(this, Handle), (UnsafeNativeMethods.IOleDropTarget)(new DropTarget(this))); + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.CurrentCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_ALREADYREGISTERED) { + throw new Win32Exception(n); + } + } + else { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Revoking drop target: " + Handle.ToString()); + // Revoke + int n = UnsafeNativeMethods.RevokeDragDrop(new HandleRef(this, Handle)); + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.InvariantCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_NOTREGISTERED) { + throw new Win32Exception(n); + } + } + SetState(STATE_DROPTARGET, accept); + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); + } + } + } + + /// + /// + /// Scales to entire control and any child controls. + /// + [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void Scale(float ratio) { + ScaleCore(ratio, ratio); + } + + /// + /// + /// Scales the entire control and any child controls. + /// + [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void Scale(float dx, float dy) { +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + ScaleCore(dx, dy); + } + finally { + ResumeLayout(); +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// + /// Scales a control and its children given a scaling factor. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void Scale(SizeF factor) { + + // VSWhidbey 501184: + // manually call ScaleControl recursively instead of the internal scale method + // when someone calls this method, they really do want to do some sort of + // zooming feature, as opposed to AutoScale. + using (new LayoutTransaction(this, this, PropertyNames.Bounds, false)) { + ScaleControl(factor, factor, this); + if (ScaleChildren) { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { + Control c = controlsCollection[i]; + c.Scale(factor); + } + } + } + } + + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + + } + + /// + /// + /// Scales a control and its children given a pair of scaling factors. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + internal virtual void Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) { + // When we scale, we are establishing new baselines for the + // positions of all controls. Therefore, we should resume(false). + using (new LayoutTransaction(this, this, PropertyNames.Bounds, false)) { + ScaleControl(includedFactor, excludedFactor, requestingControl); + ScaleChildControls(includedFactor, excludedFactor, requestingControl); + } + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + } + + /// + /// + /// Scales the children of this control. The default implementation recursively + /// walks children and calls ScaleControl on each one. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + /// The updateWindowFontIfNeeded parameter indicates if we need to update Window + /// font for controls that need it, i.e. controls using default or inherited font, + /// that are also not user-painted. + /// + internal void ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, bool updateWindowFontIfNeeded = false) { + + if (ScaleChildren) { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { + Control c = controlsCollection[i]; + + // Update window font before scaling, as controls often use font metrics during scaling. + if (updateWindowFontIfNeeded) { + c.UpdateWindowFontIfNeeded(); + } + + c.Scale(includedFactor, excludedFactor, requestingControl); + } + } + } + } + + /// + /// Calls SetWindowFont if DpiHelper.EnableDpiChangedHighDpiImprovements is true, + /// control uses default or inherited font and is not user-painted. + /// + internal void UpdateWindowFontIfNeeded() { + if (DpiHelper.EnableDpiChangedHighDpiImprovements && !GetStyle(ControlStyles.UserPaint) && (Properties.GetObject(PropFont) == null)) { + SetWindowFont(); + } + } + + /// + /// Scales the children of this control. The default implementation walks the controls + /// collection for the control and calls Scale on each control. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + internal void ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) { + try { + IsCurrentlyBeingScaled = true; + + BoundsSpecified includedSpecified = BoundsSpecified.None; + BoundsSpecified excludedSpecified = BoundsSpecified.None; + + if (!includedFactor.IsEmpty) { + includedSpecified = RequiredScaling; + } + + if (!excludedFactor.IsEmpty) { + excludedSpecified |= (~RequiredScaling & BoundsSpecified.All); + } +#if DEBUG + if (CompModSwitches.RichLayout.TraceInfo) { + Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Scaling {0} Included: {1}, Excluded: {2}", + this, includedFactor, excludedFactor)); + } +#endif + + if (includedSpecified != BoundsSpecified.None) { + ScaleControl(includedFactor, includedSpecified); + } + + if (excludedSpecified != BoundsSpecified.None) { + ScaleControl(excludedFactor, excludedSpecified); + } + + if (!includedFactor.IsEmpty) { + RequiredScaling = BoundsSpecified.None; + } + } + finally { + IsCurrentlyBeingScaled = false; + } + } + + /// + /// + /// Scales an individual control's location, size, padding and margin. + /// If the control is top level, this will not scale the control's location. + /// This does not scale children or the size of auto sized controls. You can + /// omit scaling in any direction by changing BoundsSpecified. + /// + /// After the control is scaled the RequiredScaling property is set to + /// BoundsSpecified.None. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void ScaleControl(SizeF factor, BoundsSpecified specified) { + + CreateParams cp = CreateParams; + NativeMethods.RECT adornments = new NativeMethods.RECT(0, 0, 0, 0); + AdjustWindowRectEx(ref adornments, cp.Style, HasMenu, cp.ExStyle); + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + + // VSWhidbey 412836 + // clear out min and max size, otherwise this could affect the scaling logic. + MinimumSize = Size.Empty; + MaximumSize = Size.Empty; + + // this is raw because Min/Max size have been cleared at this point. + Rectangle rawScaledBounds = GetScaledBounds(Bounds, factor, specified); + + // + // Scale Padding and Margin + // + float dx = factor.Width; + float dy = factor.Height; + + Padding padding = Padding; + Padding margins = Margin; + + // Clear off specified bits for 1.0 scaling factors + if (dx == 1.0F) specified &= ~(BoundsSpecified.X | BoundsSpecified.Width); + if (dy == 1.0F) specified &= ~(BoundsSpecified.Y | BoundsSpecified.Height); + + if (dx != 1.0F) { + padding.Left = (int)Math.Round(padding.Left * dx); + padding.Right = (int)Math.Round(padding.Right * dx); + margins.Left = (int)Math.Round(margins.Left * dx); + margins.Right = (int)Math.Round(margins.Right * dx); + } + + if (dy != 1.0F) { + padding.Top = (int)Math.Round(padding.Top * dy); + padding.Bottom = (int)Math.Round(padding.Bottom * dy); + margins.Top = (int)Math.Round(margins.Top * dy); + margins.Bottom = (int)Math.Round(margins.Bottom * dy); + } + + // Apply padding and margins + Padding = padding; + Margin = margins; + + // + // Scale Min/Max size + // + + // VSWhidbey 412836 + // make sure we consider the andornments as fixed. rather than scaling the entire size, + // we should pull out the fixed things such as the border, scale the rest, then apply the fixed + // adornment size. + Size adornmentSize = adornments.Size; + if (!minSize.IsEmpty) { + minSize -= adornmentSize; + minSize = ScaleSize(LayoutUtils.UnionSizes(Size.Empty, minSize), // make sure we dont go below 0. + factor.Width, + factor.Height) + adornmentSize; + } + if (!maxSize.IsEmpty) { + maxSize -= adornmentSize; + maxSize = ScaleSize(LayoutUtils.UnionSizes(Size.Empty, maxSize), // make sure we dont go below 0. + factor.Width, + factor.Height) + adornmentSize; + } + + + // Apply the min/max size constraints - dont call ApplySizeConstraints + // as MinimumSize/MaximumSize are currently cleared out. + Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(maxSize); + Size scaledSize = LayoutUtils.IntersectSizes(rawScaledBounds.Size, maximumSize); + scaledSize = LayoutUtils.UnionSizes(scaledSize, minSize); + + if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (ParentInternal != null) && (ParentInternal.LayoutEngine == DefaultLayout.Instance)) { + // We need to scale AnchorInfo to update distances to container edges + DefaultLayout.ScaleAnchorInfo((IArrangedElement)this, factor); + } + + // Set in the scaled bounds as constrained by the newly scaled min/max size. + SetBoundsCore(rawScaledBounds.X, rawScaledBounds.Y, scaledSize.Width, scaledSize.Height, BoundsSpecified.All); + + MaximumSize = maxSize; + MinimumSize = minSize; + + + } + + + /// + /// + /// Performs the work of scaling the entire control and any child controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void ScaleCore(float dx, float dy) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ScaleCore(" + dx + ", " + dy + ")"); +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + int sx = (int)Math.Round(x * dx); + int sy = (int)Math.Round(y * dy); + + int sw = width; + if ((controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth) { + sw = (int)(Math.Round((x + width) * dx)) - sx; + } + int sh = height; + if ((controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight) { + sh = (int)(Math.Round((y + height) * dy)) - sy; + } + + SetBounds(sx, sy, sw, sh, BoundsSpecified.All); + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { +#pragma warning disable 618 + controlsCollection[i].Scale(dx, dy); +#pragma warning restore 618 + } + } + } + finally { + ResumeLayout(); +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// Scales a given size with the provided values. + /// + internal Size ScaleSize(Size startSize, float x, float y) { + Size size = startSize; + if (!GetStyle(ControlStyles.FixedWidth)) { + size.Width = (int)Math.Round((float)size.Width * x); + } + if (!GetStyle(ControlStyles.FixedHeight)) { + size.Height = (int)Math.Round((float)size.Height * y); + } + return size; + } + + /// + /// + /// Activates this control. + /// + public void Select() { + Select(false, false); + } + + // used by Form + /// + /// + /// [To be supplied.] + /// + protected virtual void Select(bool directed, bool forward) { + IContainerControl c = GetContainerControlInternal(); + + if (c != null) { + c.ActiveControl = this; + } + } + + /// + /// + /// Selects the next control following ctl. + /// + public bool SelectNextControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + Control nextSelectableControl = this.GetNextSelectableControl(ctl, forward, tabStopOnly, nested, wrap); + if (nextSelectableControl != null) { + nextSelectableControl.Select(true, forward); + return true; + } + else { + return false; + } + } + + private Control GetNextSelectableControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + if (!Contains(ctl) || !nested && ctl.parent != this) ctl = null; + + bool alreadyWrapped = false; + Control start = ctl; + do { + ctl = GetNextControl(ctl, forward); + if (ctl == null) { + if (!wrap) break; + if (alreadyWrapped) { + return null; //VSWhidbey 423098 prevent infinite wrapping. + } + alreadyWrapped = true; + } + else { + if (ctl.CanSelect + && (!tabStopOnly || ctl.TabStop) + && (nested || ctl.parent == this)) { + + if (AccessibilityImprovements.Level3 && ctl.parent is ToolStrip) { + continue; + } + return ctl; + } + } + } while (ctl != start); + return null; + } + + + /// + /// Unsafe internal version of SelectNextControl - Use with caution! + /// + internal bool SelectNextControlInternal(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + return SelectNextControl(ctl, forward, tabStopOnly, nested, wrap); + } + + /// + /// This is called recursively when visibility is changed for a control, this + /// forces focus to be moved to a visible control. + /// + private void SelectNextIfFocused() { + // V#32437 - We want to move focus away from hidden controls, so this + // function was added. + // + if (ContainsFocus && ParentInternal != null) { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + + if (c != null) { + // SECREVIEW : Control.SelectNextControl generates a call to ContainerControl.ActiveControl which demands + // ModifyFocus permission, the demand is to prevent DOS attacks but it doesn't expose a sec + // vulnerability indirectly. So it is safe to call the internal version of SelectNextControl here. + // + ((Control)c).SelectNextControlInternal(this, true, true, true, true); + } + } + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, ref int wparam, ref int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, ref wparam, ref lparam); + } + internal IntPtr SendMessage(int msg, int wparam, IntPtr lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, (IntPtr)wparam, lparam); + } + + internal IntPtr SendMessage(int msg, IntPtr wparam, IntPtr lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + internal IntPtr SendMessage(int msg, IntPtr wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, (IntPtr)lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, ref NativeMethods.RECT lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, ref lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, bool wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, string lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + /// + /// + /// sends this control to the back of the z-order + /// + public void SendToBack() { + if (parent != null) { + parent.Controls.SetChildIndex(this, -1); + } + else if (IsHandleCreated && GetTopLevel()) { + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.HWND_BOTTOM, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + + } + + /// + /// + /// Sets the bounds of the control. + /// + public void SetBounds(int x, int y, int width, int height) { + if (this.x != x || this.y != y || this.width != width || + this.height != height) { + SetBoundsCore(x, y, width, height, BoundsSpecified.All); + + // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will + // have refreshed the interior layout. We only need to layout the parent. + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + else { + // Still need to init scaling. + InitScaling(BoundsSpecified.All); + } + } + + /// + /// + /// Sets the bounds of the control. + /// + public void SetBounds(int x, int y, int width, int height, BoundsSpecified specified) { + if ((specified & BoundsSpecified.X) == BoundsSpecified.None) x = this.x; + if ((specified & BoundsSpecified.Y) == BoundsSpecified.None) y = this.y; + if ((specified & BoundsSpecified.Width) == BoundsSpecified.None) width = this.width; + if ((specified & BoundsSpecified.Height) == BoundsSpecified.None) height = this.height; + if (this.x != x || this.y != y || this.width != width || + this.height != height) { + SetBoundsCore(x, y, width, height, specified); + + // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will + // have refreshed the interior layout or the resized control. We only need to layout + // the parent. This happens after InitLayout has been invoked. + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + else { + // Still need to init scaling. + InitScaling(specified); + } + } + + /// + /// + /// Performs the work of setting the bounds of this control. Inheriting + /// classes can overide this function to add size restrictions. Inheriting + /// classes must call base.setBoundsCore to actually cause the bounds + /// of the control to change. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { +#if DEBUG + if (CompModSwitches.SetBounds.TraceInfo) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "{0}::SetBoundsCore(x={1} y={2} width={3} height={4} specified={5}", Name, x, y, width, height, specified)); + } +#endif + // SetWindowPos below sends a WmWindowPositionChanged (not posts) so we immediately + // end up in WmWindowPositionChanged which may cause the parent to layout. We need to + // suspend/resume to defer the parent from laying out until after InitLayout has been called + // to update the layout engine's state with the new control bounds. +#if DEBUG + int suspendCount = -44371; // Arbitrary negative prime to surface bugs. +#endif + if (ParentInternal != null) { +#if DEBUG + suspendCount = ParentInternal.LayoutSuspendCount; +#endif + ParentInternal.SuspendLayout(); + } + try { + if (this.x != x || this.y != y || this.width != width || this.height != height) { + CommonProperties.UpdateSpecifiedBounds(this, x, y, width, height, specified); + + // Provide control with an opportunity to apply self imposed constraints on its size. + Rectangle adjustedBounds = ApplyBoundsConstraints(x,y, width, height); + width = adjustedBounds.Width; + height = adjustedBounds.Height; + x = adjustedBounds.X; + y = adjustedBounds.Y; + + if (!IsHandleCreated) { + // Handle is not created, just record our new position and we're done. + UpdateBounds(x, y, width, height); + } else { + if (!GetState(STATE_SIZELOCKEDBYOS)) { + int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE; + + if (this.x == x && this.y == y) { + flags |= NativeMethods.SWP_NOMOVE; + } + if (this.width == width && this.height == height) { + flags |= NativeMethods.SWP_NOSIZE; + } + + // + // Give a chance for derived controls to do what they want, just before we resize. + OnBoundsUpdate(x, y, width, height); + + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.NullHandleRef, x, y, width, height, flags); + + // NOTE: SetWindowPos causes a WM_WINDOWPOSCHANGED which is processed + // synchonously so we effectively end up in UpdateBounds immediately following + // SetWindowPos. + // + //UpdateBounds(x, y, width, height); + } + } + } + } + finally { + + // Initialize the scaling engine. + InitScaling(specified); + + if (ParentInternal != null) { + // Some layout engines (DefaultLayout) base their PreferredSize on + // the bounds of their children. If we change change the child bounds, we + // need to clear their PreferredSize cache. The semantics of SetBoundsCore + // is that it does not cause a layout, so we just clear. + CommonProperties.xClearPreferredSizeCache(ParentInternal); + + // Cause the current control to initialize its layout (e.g., Anchored controls + // memorize their distance from their parent's edges). It is your parent's + // LayoutEngine which manages your layout, so we call into the parent's + // LayoutEngine. + ParentInternal.LayoutEngine.InitLayout(this, specified); + ParentInternal.ResumeLayout( /* performLayout = */ true ); +#if DEBUG + Debug.Assert(ParentInternal.LayoutSuspendCount == suspendCount, "Suspend/Resume layout mismatch!"); +#endif + } + } + } + + /// + /// + /// Performs the work of setting the size of the client area of the control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void SetClientSizeCore(int x, int y) { + Size = SizeFromClientSize(x, y); + clientWidth = x; + clientHeight = y; + OnClientSizeChanged(EventArgs.Empty); + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual Size SizeFromClientSize(Size clientSize) { + return SizeFromClientSize(clientSize.Width, clientSize.Height); + } + + internal Size SizeFromClientSize(int width, int height) { + NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, width, height); + + CreateParams cp = CreateParams; + AdjustWindowRectEx(ref rect, cp.Style, HasMenu, cp.ExStyle); + return rect.Size; + } + + private void SetHandle(IntPtr value) { + if (value == IntPtr.Zero) { + SetState(STATE_CREATED, false); + } + UpdateRoot(); + } + + private void SetParentHandle(IntPtr value) { + Debug.Assert(value != NativeMethods.InvalidIntPtr, "Outdated call to SetParentHandle"); + + if (IsHandleCreated) { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(window, Handle)); + bool topLevel = GetTopLevel(); + if (parentHandle != value || (parentHandle == IntPtr.Zero && !topLevel)) { + Debug.Assert(Handle != value, "Cycle created in SetParentHandle"); + + bool recreate = (parentHandle == IntPtr.Zero && !topLevel) + || (value == IntPtr.Zero && topLevel); + + if (recreate) { + // We will recreate later, when the MdiChild's visibility + // is set to true (see bug 124232) + Form f = this as Form; + if (f != null) { + if (!f.CanRecreateHandle()) { + recreate = false; + // we don't want to recreate - but our styles may have changed. + // before we unpark the window below we need to update + UpdateStyles(); + } + } + } + + if (recreate) { + RecreateHandle(); + } + if (!GetTopLevel()) { + if (value == IntPtr.Zero) { + Application.ParkHandle(new HandleRef(window, Handle), this.DpiAwarenessContext); + UpdateRoot(); + } + else { + UnsafeNativeMethods.SetParent(new HandleRef(window, Handle), new HandleRef(null, value)); + if (parent != null) { + parent.UpdateChildZOrder(this); + } + Application.UnparkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext); + } + } + } + else if (value == IntPtr.Zero && parentHandle == IntPtr.Zero && topLevel) { + // See VSWhidbey 531685: The handle was previously parented to the parking window. Its TopLevel property was + // then changed to true so the above call to GetParent returns null even though the parent of the control is + // not null. We need to explicitly set the parent to null. + UnsafeNativeMethods.SetParent(new HandleRef(window, Handle), new HandleRef(null, IntPtr.Zero)); + Application.UnparkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext); + } + } + } + + // Form, UserControl, AxHost usage + internal void SetState(int flag, bool value) { + state = value? state | flag: state & ~flag; + } + + // Application, SKWindow usage + internal void SetState2(int flag, bool value) { + state2 = value ? state2 | flag : state2 & ~flag; + } + + /// + /// + /// Sets the current value of the specified bit in the control's style. + /// NOTE: This is control style, not the Win32 style of the hWnd. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void SetStyle(ControlStyles flag, bool value) { + // WARNING: if we ever add argument checking to "flag", we will need + // to move private styles like Layered to State. + // ASURT: 151576 Calling SetStyle(ControlStyles.EnableNotifyMessage,...) should require UnmanagedCode + if ((flag & ControlStyles.EnableNotifyMessage) != 0 && value) + { + // demand security permission for this condition. + // this will throw security exception in semi-trust. + IntSecurity.UnmanagedCode.Demand(); + } + controlStyle = value? controlStyle | flag: controlStyle & ~flag; + } + + internal static IntPtr SetUpPalette(IntPtr dc, bool force, bool realizePalette) { + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, "SetUpPalette(force:=" + force + ", ralizePalette:=" + realizePalette + ")"); + + IntPtr halftonePalette = Graphics.GetHalftonePalette(); + + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, "select palette " + !force); + IntPtr result = SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, halftonePalette), (force ? 0 : 1)); + + if (result != IntPtr.Zero && realizePalette) { + SafeNativeMethods.RealizePalette(new HandleRef(null, dc)); + } + + return result; + } + + /// + /// + /// [To be supplied.] + /// + protected void SetTopLevel(bool value) { + if (value && IsActiveX) { + throw new InvalidOperationException(SR.GetString(SR.TopLevelNotAllowedIfActiveX)); + } + else { + if (value) { + if (this is Form) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "TopLevelWindow Demanded"); + IntSecurity.TopLevelWindow.Demand(); + } + else { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "UnrestrictedWindows Demanded"); + IntSecurity.UnrestrictedWindows.Demand(); + } + } + + SetTopLevelInternal(value); + } + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal void SetTopLevelInternal(bool value) { + + if (GetTopLevel() != value) { + if (parent != null) { + throw new ArgumentException(SR.GetString(SR.TopLevelParentedControl), "value"); + } + SetState(STATE_TOPLEVEL, value); + // VSWhidbey 476889: make sure the handle is created before hooking, otherwise a toplevel control that never + // creates its handle will leak. + if (IsHandleCreated && GetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED)) { + ListenToUserPreferenceChanged(value); + } + UpdateStyles(); + SetParentHandle(IntPtr.Zero); + if (value && Visible) { + CreateControl(); + } + UpdateRoot(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void SetVisibleCore(bool value) { + try { + System.Internal.HandleCollector.SuspendCollect(); + + if (GetVisibleCore() != value) { + if (!value) { + SelectNextIfFocused(); + } + + bool fireChange = false; + + if (GetTopLevel()) { + + // The processing of WmShowWindow will set the visibility + // bit and call CreateControl() + // + if (IsHandleCreated || value) { + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), value ? ShowParams : NativeMethods.SW_HIDE); + } + } + else if (IsHandleCreated || value && parent != null && parent.Created) { + + // We want to mark the control as visible so that CreateControl + // knows that we are going to be displayed... however in case + // an exception is thrown, we need to back the change out. + // + SetState(STATE_VISIBLE, value); + fireChange = true; + try { + if (value) CreateControl(); + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), + NativeMethods.NullHandleRef, + 0, 0, 0, 0, + NativeMethods.SWP_NOSIZE + | NativeMethods.SWP_NOMOVE + | NativeMethods.SWP_NOZORDER + | NativeMethods.SWP_NOACTIVATE + | (value ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW)); + } + catch { + SetState(STATE_VISIBLE, !value); + throw; + } + } + if (GetVisibleCore() != value) { + SetState(STATE_VISIBLE, value); + fireChange = true; + } + + if (fireChange) { + // We do not do this in the OnPropertyChanged event for visible + // Lots of things could cause us to become visible, including a + // parent window. We do not want to indescriminiately layout + // due to this, but we do want to layout if the user changed + // our visibility. + // + + using (new LayoutTransaction(parent, this, PropertyNames.Visible)) { + OnVisibleChanged(EventArgs.Empty); + } + } + UpdateRoot(); + } + else { // value of Visible property not changed, but raw bit may have + + if (!GetState(STATE_VISIBLE) && !value && IsHandleCreated) { + // PERF - setting Visible=false twice can get us into this else block + // which makes us process WM_WINDOWPOS* messages - make sure we've already + // visible=false - if not, make it so. + if (!SafeNativeMethods.IsWindowVisible(new HandleRef(this,this.Handle))) { + // we're already invisible - bail. + return; + } + } + + SetState(STATE_VISIBLE, value); + + // If the handle is already created, we need to update the window style. + // This situation occurs when the parent control is not currently visible, + // but the child control has already been created. + // + if (IsHandleCreated) { + + SafeNativeMethods.SetWindowPos( + new HandleRef(window, Handle), NativeMethods.NullHandleRef, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE | + (value ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW)); + } + } + } + finally { + System.Internal.HandleCollector.ResumeCollect(); + } + } + + /// + /// + /// Determine effective auto-validation setting for a given control, based on the AutoValidate property + /// of its containing control. Defaults to 'EnablePreventFocusChange' if there is no containing control + /// (eg. because this control is a top-level container). + /// + /// + internal static AutoValidate GetAutoValidateForControl(Control control) { + ContainerControl parent = control.ParentContainerControl; + return (parent != null) ? parent.AutoValidate : AutoValidate.EnablePreventFocusChange; + } + + /// + /// + /// Is auto-validation currently in effect for this control? + /// Depends on the AutoValidate property of the containing control. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldAutoValidate { + get { + return GetAutoValidateForControl(this) != AutoValidate.Disable; + } + } + + // Refer vsW: 543074: This method is called in PerformContainerValidation to check if this control supports containerValidation. + // TabControl overrides this method to return true. + internal virtual bool ShouldPerformContainerValidation() { + return GetStyle(ControlStyles.ContainerControl); + } + + /// + /// + /// Returns true if the backColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeBackColor() { + Color backColor = Properties.GetColor(PropBackColor); + return !backColor.IsEmpty; + } + + /// + /// + /// Returns true if the cursor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeCursor() { + bool found; + object cursor = Properties.GetObject(PropCursor, out found); + return (found && cursor != null); + } + + /// + /// Returns true if the enabled property should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeEnabled() { + return (!GetState(STATE_ENABLED)); + } + + /// + /// + /// Returns true if the foreColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeForeColor() { + Color foreColor = Properties.GetColor(PropForeColor); + return !foreColor.IsEmpty; + } + + /// + /// + /// Returns true if the font should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeFont() { + bool found; + object font = Properties.GetObject(PropFont, out found); + return (found && font != null); + } + + /// + /// + /// Returns true if the RightToLeft should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeRightToLeft() { + bool found; + int rtl = Properties.GetInteger(PropRightToLeft, out found); + return (found && rtl != (int)RightToLeft.Inherit); + } + + /// + /// Returns true if the visible property should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeVisible() { + return (!GetState(STATE_VISIBLE)); + } + + // Helper function - translates text alignment for Rtl controls + // Read TextAlign as Left == Near, Right == Far + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected HorizontalAlignment RtlTranslateAlignment(HorizontalAlignment align) { + return RtlTranslateHorizontal(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected LeftRightAlignment RtlTranslateAlignment(LeftRightAlignment align) { + return RtlTranslateLeftRight(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected ContentAlignment RtlTranslateAlignment(ContentAlignment align) { + return RtlTranslateContent(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected HorizontalAlignment RtlTranslateHorizontal(HorizontalAlignment align) { + + if (RightToLeft.Yes == RightToLeft) { + if (HorizontalAlignment.Left == align) { + return HorizontalAlignment.Right; + } + else if (HorizontalAlignment.Right == align) { + return HorizontalAlignment.Left; + } + } + + return align; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected LeftRightAlignment RtlTranslateLeftRight(LeftRightAlignment align) { + + if (RightToLeft.Yes == RightToLeft) { + if (LeftRightAlignment.Left == align) { + return LeftRightAlignment.Right; + } + else if (LeftRightAlignment.Right == align) { + return LeftRightAlignment.Left; + } + } + + return align; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal ContentAlignment RtlTranslateContent(ContentAlignment align) { + if (RightToLeft.Yes == RightToLeft) { + + if ((align & WindowsFormsUtils.AnyTopAlign) != 0) { + switch (align) { + case ContentAlignment.TopLeft: + return ContentAlignment.TopRight; + case ContentAlignment.TopRight: + return ContentAlignment.TopLeft; + } + } + if ((align & WindowsFormsUtils.AnyMiddleAlign) != 0 ) { + switch (align) { + case ContentAlignment.MiddleLeft: + return ContentAlignment.MiddleRight; + case ContentAlignment.MiddleRight: + return ContentAlignment.MiddleLeft; + } + } + + if ((align & WindowsFormsUtils.AnyBottomAlign) != 0 ) { + switch (align) { + case ContentAlignment.BottomLeft: + return ContentAlignment.BottomRight; + case ContentAlignment.BottomRight: + return ContentAlignment.BottomLeft; + } + } + } + return align; + } + + private void SetWindowFont(){ + SendMessage(NativeMethods.WM_SETFONT, FontHandle, 0 /*redraw = false*/); + } + + private void SetWindowStyle(int flag, bool value) { + int styleFlags = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)(value? styleFlags | flag: styleFlags & ~flag))); + } + + + /// + /// + /// Makes the control display by setting the visible property to true + /// + public void Show() { + Visible = true; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializeMargin() { + return !Margin.Equals(DefaultMargin); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeMaximumSize() { + return MaximumSize != DefaultMaximumSize; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeMinimumSize() { + return MinimumSize != DefaultMinimumSize; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializePadding() { + return !Padding.Equals(DefaultPadding); + } + + /// + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeSize() { + // VSWhidbey 379287: In Whidbey the ControlDesigner class will always serialize size as it replaces the Size + // property descriptor with its own. This is here for compat. + Size s = DefaultSize; + return width != s.Width || height != s.Height; + } + + /// + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeText() { + return Text.Length != 0; + } + + /// + /// + /// Suspends the layout logic for the control. + /// + public void SuspendLayout() { + + + layoutSuspendCount++; + if (layoutSuspendCount == 1) { + OnLayoutSuspended(); + } + +#if DEBUG + Debug.Assert(layoutSuspendCount > 0, "SuspendLayout: layoutSuspendCount overflowed."); + if (CompModSwitches.LayoutSuspendResume.TraceInfo) { + Debug.WriteLine(GetType().Name + "::SuspendLayout( newCount = " + layoutSuspendCount + ")"); + } +#endif + } + + /// + /// Stops listening for the mouse leave event. + /// + /// + private void UnhookMouseEvent() { + SetState(STATE_TRACKINGMOUSEEVENT, false); + } + + /// + /// + /// Forces the control to paint any currently invalid areas. + /// + public void Update() { + SafeNativeMethods.UpdateWindow(new HandleRef(window, InternalHandle)); + } + + /// + /// + /// Updates the bounds of the control based on the handle the control is + /// bound to. + /// + // Internal for ScrollableControl + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal void UpdateBounds() { + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(window, InternalHandle), ref rect); + int clientWidth = rect.right; + int clientHeight = rect.bottom; + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, InternalHandle), ref rect); + if (!GetTopLevel()) { + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(null, UnsafeNativeMethods.GetParent(new HandleRef(window, InternalHandle))), ref rect, 2); + } + + UpdateBounds(rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, clientWidth, clientHeight); + } + + /// + /// + /// Updates the bounds of the control based on the bounds passed in. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateBounds(int x, int y, int width, int height) { + Debug.Assert(!IsHandleCreated, "Don't call this method when handle is created!!"); + + // reverse-engineer the AdjustWindowRectEx call to figure out + // the appropriate clientWidth and clientHeight + NativeMethods.RECT rect = new NativeMethods.RECT(); + rect.left = rect.right = rect.top = rect.bottom = 0; + + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle); + int clientWidth = width - (rect.right - rect.left); + int clientHeight = height - (rect.bottom - rect.top); + UpdateBounds(x, y, width, height, clientWidth, clientHeight); + } + + /// + /// + /// Updates the bounds of the control based on the bounds passed in. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateBounds(int x, int y, int width, int height, int clientWidth, int clientHeight) { +#if DEBUG + if (CompModSwitches.SetBounds.TraceVerbose){ + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "{0}::UpdateBounds(", Name)); + Debug.Indent(); + Debug.WriteLine(String.Format( + CultureInfo.CurrentCulture, "oldBounds={{x={0} y={1} width={2} height={3} clientWidth={4} clientHeight={5}}}", + this.x, this.y, this.width, this.height, this.clientWidth, this.clientHeight)); + } +#endif // DEBUG + + + bool newLocation = this.x != x || this.y != y; + bool newSize = this.Width != width || this.Height != height || + this.clientWidth != clientWidth || this.clientHeight != clientHeight; + + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.clientWidth = clientWidth; + this.clientHeight = clientHeight; + + if (newLocation) { +#if DEBUG + Rectangle originalBounds = this.Bounds; +#endif + OnLocationChanged(EventArgs.Empty); +#if DEBUG + if (this.Bounds != originalBounds && CompModSwitches.SetBounds.TraceWarning) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "WARNING: Bounds changed during OnLocationChanged()\r\nbefore={0} after={1}", originalBounds, this.Bounds)); + } +#endif + } + if (newSize) { +#if DEBUG + Rectangle originalBounds = this.Bounds; +#endif + OnSizeChanged(EventArgs.Empty); + OnClientSizeChanged(EventArgs.Empty); + + // Clear PreferredSize cache for this control + CommonProperties.xClearPreferredSizeCache(this); + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + +#if DEBUG + if (this.Bounds != originalBounds && CompModSwitches.SetBounds.TraceWarning) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "WARNING: Bounds changed during OnSizeChanged()\r\nbefore={0} after={1}", originalBounds, this.Bounds)); + } +#endif + } + + +#if DEBUG + if (CompModSwitches.SetBounds.TraceVerbose) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "newBounds={{x={0} y={1} width={2} height={3} clientWidth={4} clientHeight={5}}}",x, y, width, height, clientWidth, clientHeight)); + Debug.Unindent(); + } +#endif + } + + /// + /// Updates the binding manager bindings when the binding proeprty changes. + /// We have the code here, rather than in PropertyChagned, so we don't pull + /// in the data assembly if it's not used. + /// + private void UpdateBindings() { + for (int i = 0; i < DataBindings.Count; i++) { + BindingContext.UpdateBinding(BindingContext, DataBindings[i]); + } + } + + /// + /// Updates the child control's position in the control array to correctly + /// reflect it's index. + /// + private void UpdateChildControlIndex(Control ctl) { + // VSO 411856 + // Don't reorder the child control array for tab controls. Implemented as a special case + // in order to keep the method private. + if (!LocalAppContextSwitches.AllowUpdateChildControlIndexForTabControls) { + if (this.GetType().IsAssignableFrom(typeof(TabControl))) { + return; + } + } + + int newIndex = 0; + int curIndex = this.Controls.GetChildIndex(ctl); + IntPtr hWnd = ctl.InternalHandle; + while ((hWnd = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWnd), NativeMethods.GW_HWNDPREV)) != IntPtr.Zero) { + Control c = FromHandleInternal(hWnd); + if (c != null) { + newIndex = this.Controls.GetChildIndex(c, false) + 1; + break; + } + } + if (newIndex > curIndex) { + newIndex--; + } + if (newIndex != curIndex) + { + this.Controls.SetChildIndex(ctl, newIndex); + } + } + + + // whenever we create our handle, we need to get ahold of the HWND of our parent, + // and track if that thing is destroyed, because any messages sent to our parent (e.g. WM_NOTIFY, WM_DRAWITEM, etc) + // will continue to be sent to that window even after we do a SetParent call until the handle is recreated. + // So here we keep track of that, and if that window ever gets destroyed, we'll recreate our handle. + // + // + // Scenario is when you've got a control in one parent, you move it to another, then destroy the first parent. It'll stop + // getting any reflected messages because Windows will send them to the original parent. + // + private void UpdateReflectParent(bool findNewParent) { + if (!Disposing && findNewParent && IsHandleCreated) { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(this, Handle)); + if (parentHandle != IntPtr.Zero) { + ReflectParent = Control.FromHandleInternal(parentHandle); + return; + } + } + ReflectParent = null; + } + + /// + /// + /// Updates this control in it's parent's zorder. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateZOrder() { + if (parent != null) { + parent.UpdateChildZOrder(this); + } + } + + /// + /// Syncs the ZOrder of child control to the index we want it to be. + /// + private void UpdateChildZOrder(Control ctl) { + if (!IsHandleCreated || !ctl.IsHandleCreated || ctl.parent != this) return; + IntPtr prevHandle = (IntPtr)NativeMethods.HWND_TOP; + for (int i = this.Controls.GetChildIndex(ctl); --i >= 0;) { + Control c = Controls[i]; + if (c.IsHandleCreated && c.parent == this) { + prevHandle = c.Handle; + break; + } + } + if (UnsafeNativeMethods.GetWindow(new HandleRef(ctl.window, ctl.Handle), NativeMethods.GW_HWNDPREV) != prevHandle) { + state |= STATE_NOZORDER; + try { + SafeNativeMethods.SetWindowPos(new HandleRef(ctl.window, ctl.Handle), new HandleRef(null, prevHandle), 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + finally { + state &= ~STATE_NOZORDER; + } + } + } + + /// + /// Updates the rootReference in the bound window. + /// (Used to prevent visible top-level controls from being garbage collected) + /// + private void UpdateRoot() { + window.LockReference(GetTopLevel() && Visible); + } + + /// + /// + /// Forces styles to be reapplied to the handle. This function will call + /// CreateParams to get the styles to apply. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateStyles() { + UpdateStylesCore(); + + OnStyleChanged(EventArgs.Empty); + } + + internal virtual void UpdateStylesCore() { + if (IsHandleCreated) { + CreateParams cp = CreateParams; + int winStyle = WindowStyle; + int exStyle = WindowExStyle; + + // resolve the Form's lazy visibility. + if ((state & STATE_VISIBLE) != 0){ + cp.Style |= NativeMethods.WS_VISIBLE; + } + if (winStyle != cp.Style) { + WindowStyle = cp.Style; + } + if (exStyle != cp.ExStyle) { + WindowExStyle = cp.ExStyle; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + } + + SafeNativeMethods.SetWindowPos( + new HandleRef(this, Handle), NativeMethods.NullHandleRef, 0, 0, 0, 0, + NativeMethods.SWP_DRAWFRAME | NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOMOVE + | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER); + + Invalidate(true); + } + } + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + if (pref.Category == UserPreferenceCategory.Color) { + defaultFont = null; + OnSystemColorsChanged(EventArgs.Empty); + } + } + + // + // Give a chance for derived controls to do what they want, just before we resize. + internal virtual void OnBoundsUpdate(int x, int y, int width, int height) + { + } + + // These Window* methods allow us to keep access to the "window" + // property private, which is important for restricting access to the + // handle. + internal void WindowAssignHandle(IntPtr handle, bool value) + { + window.AssignHandle(handle, value); + } + + internal void WindowReleaseHandle() + { + window.ReleaseHandle(); + } + + private void WmClose(ref Message m) { + + // More generic fix for KB article Q125644... + // + if (ParentInternal != null) { + IntPtr parentHandle = Handle; + IntPtr lastParentHandle = parentHandle; + + while (parentHandle != IntPtr.Zero) { + lastParentHandle = parentHandle; + parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(null, parentHandle)); + + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(null, lastParentHandle), NativeMethods.GWL_STYLE))); + + if ((style & NativeMethods.WS_CHILD) == 0) { + break; + } + + } + + if (lastParentHandle != IntPtr.Zero) { + UnsafeNativeMethods.PostMessage(new HandleRef(null, lastParentHandle), NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + } + } + + DefWndProc(ref m); + } + + + /// + /// Handles the WM_CAPTURECHANGED message + /// + /// + private void WmCaptureChanged(ref Message m) { + OnMouseCaptureChanged(EventArgs.Empty); + DefWndProc(ref m); + + } + /// + /// Handles the WM_COMMAND message + /// + /// + private void WmCommand(ref Message m) { + if (IntPtr.Zero == m.LParam) { + if (Command.DispatchID(NativeMethods.Util.LOWORD(m.WParam))) return; + } + else { + if (ReflectMessageInternal(m.LParam, ref m)) { + return; + } + } + DefWndProc(ref m); + } + + // overridable so nested controls can provide a different source control. + internal virtual void WmContextMenu(ref Message m) { + WmContextMenu(ref m, this); + } + /// + /// Handles the WM_CONTEXTMENU message + /// + /// + internal void WmContextMenu(ref Message m, Control sourceControl) { + ContextMenu contextMenu = Properties.GetObject(PropContextMenu) as ContextMenu; + ContextMenuStrip contextMenuStrip = (contextMenu != null) ? null /*save ourselves a property fetch*/ + : Properties.GetObject(PropContextMenuStrip) as ContextMenuStrip; + + if (contextMenu != null || contextMenuStrip != null) { + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point client; + bool keyboardActivated = false; + // lparam will be exactly -1 when the user invokes the context menu + // with the keyboard. + // + if (unchecked((int)(long)m.LParam) == -1) { + keyboardActivated = true; + client = new Point(Width/2, Height/2); + } + else { + client = PointToClientInternal(new Point(x, y)); + } + + // + + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (ClientRectangle.Contains( client )) { + if (contextMenu != null) { + contextMenu.Show(sourceControl, client); + } + else if (contextMenuStrip != null) { + contextMenuStrip.ShowInternal(sourceControl, client, keyboardActivated); + } + else { + Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?"); + DefWndProc( ref m ); + } + } + else { + DefWndProc( ref m ); + } + } + else { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_CTLCOLOR message + /// + /// + private void WmCtlColorControl(ref Message m) { + // We could simply reflect the message, but it's faster to handle it here if possible. + Control control = Control.FromHandleInternal(m.LParam); + if (control != null) { + m.Result = control.InitializeDCForWmCtlColor(m.WParam, m.Msg); + if (m.Result != IntPtr.Zero) { + return; + } + } + + DefWndProc(ref m); + } + + private void WmDisplayChange(ref Message m) { + BufferedGraphicsManager.Current.Invalidate(); + DefWndProc(ref m); + } + + /// + /// WM_DRAWITEM handler + /// + /// + private void WmDrawItem(ref Message m) { + + // If the wparam is zero, then the message was sent by a menu. + // See WM_DRAWITEM in MSDN. + if (m.WParam == IntPtr.Zero) { + WmDrawItemMenuItem(ref m); + } + else { + WmOwnerDraw(ref m); + } + } + + private void WmDrawItemMenuItem(ref Message m) { + // Obtain the menu item object + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + + // A pointer to the correct MenuItem is stored in the draw item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(dis.itemData); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmDrawItem(ref m); + } + } + + /// + /// Handles the WM_ERASEBKGND message + /// + /// + private void WmEraseBkgnd(ref Message m) { + if (GetStyle(ControlStyles.UserPaint)) { + // When possible, it's best to do all painting directly from WM_PAINT. + // OptimizedDoubleBuffer is the "same" as turning on AllPaintingInWMPaint + if (!(GetStyle(ControlStyles.AllPaintingInWmPaint))) { + IntPtr dc = m.WParam; + if (dc == IntPtr.Zero) { // This happens under extreme stress conditions + m.Result = (IntPtr)0; + return; + } + NativeMethods.RECT rc = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rc); + using (PaintEventArgs pevent = new PaintEventArgs(dc, Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom))) { + PaintWithErrorHandling(pevent, PaintLayerBackground); + } + } + m.Result = (IntPtr)1; + } + else { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_EXITMENULOOP message. If this control has a context menu, its + /// Collapse event is raised. + /// + /// + private void WmExitMenuLoop(ref Message m) { + bool isContextMenu = (unchecked((int)(long)m.WParam) == 0) ? false : true; + + if (isContextMenu) { + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + contextMenu.OnCollapse(EventArgs.Empty); + } + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_GETCONTROLNAME message. Returns the name of the control. + /// + /// + private void WmGetControlName(ref Message m) { + string name; + + if (this.Site != null) { + name = this.Site.Name; + } + else { + name = this.Name; + } + + if (name == null) + name = ""; + + + MarshalStringToMessage(name, ref m); + } + + /// + /// Handles the WM_GETCONTROLTYPE message. Returns the name of the control. + /// + /// + private void WmGetControlType(ref Message m) { + string type = GetType().AssemblyQualifiedName; + MarshalStringToMessage(type, ref m); + } + + /// + /// Handles the WM_GETOBJECT message. Used for accessibility. + /// + /// + private void WmGetObject(ref Message m) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "In WmGetObject, this = " + this.GetType().FullName + ", lParam = " + m.LParam.ToString()); + + InternalAccessibleObject intAccessibleObject = null; + + if (m.Msg == NativeMethods.WM_GETOBJECT && m.LParam == (IntPtr)NativeMethods.UiaRootObjectId && this.SupportsUiaProviders) { + // If the requested object identifier is UiaRootObjectId, + // we should return an UI Automation provider using the UiaReturnRawElementProvider function. + IntSecurity.UnmanagedCode.Assert(); + try { + intAccessibleObject = new InternalAccessibleObject(this.AccessibilityObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + m.Result = UnsafeNativeMethods.UiaReturnRawElementProvider( + new HandleRef(this, Handle), + m.WParam, + m.LParam, + intAccessibleObject); + + return; + } + + AccessibleObject ctrlAccessibleObject = GetAccessibilityObject(unchecked((int)(long)m.LParam)); + + if (ctrlAccessibleObject != null) { + // SECREVIEW : Asserting here is safe, the interface is used to get the LRESULT from the accessible object + // into the Message.Result. + // + IntSecurity.UnmanagedCode.Assert(); + try { + intAccessibleObject = new InternalAccessibleObject(ctrlAccessibleObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + // See "How to Handle WM_GETOBJECT" in MSDN + if (intAccessibleObject != null) { + + // Get the IAccessible GUID + // + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + + // Get an Lresult for the accessibility Object for this control + // + IntPtr punkAcc; + try { + // Sanity check: For security reasons, it is + + + + object tempObject = intAccessibleObject; + IAccessible iAccCheck = tempObject as IAccessible; + if (iAccCheck != null) { + throw new InvalidOperationException(SR.GetString(SR.ControlAccessibileObjectInvalid)); + } + + // Check that we have an IAccessibleInternal implementation and return this + UnsafeNativeMethods.IAccessibleInternal iacc = (UnsafeNativeMethods.IAccessibleInternal) intAccessibleObject; + + if (iacc == null) { + // Accessibility is not supported on this control + // + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibilityObject returned null"); + m.Result = (IntPtr)0; + } + else { + // Obtain the Lresult + // + punkAcc = Marshal.GetIUnknownForObject(iacc); + + // SECREVIEW : The following assert is required because the interop call below will call into an interface (punkAcc) + // which demands UnmanagedCode permission. We make sure we are calling the right into the right interface + // (see Sanity-Check comment above) and the call is safe since we are just getting the LRESULT from the + // Acc interface. So this assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + m.Result = UnsafeNativeMethods.LresultFromObject(ref IID_IAccessible, m.WParam, new HandleRef(ctrlAccessibleObject, punkAcc)); + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "LresultFromObject returned " + m.Result.ToString()); + } + finally { + CodeAccessPermission.RevertAssert(); + Marshal.Release(punkAcc); + } + } + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.RichControlLresult), e); + } + } + else { // some accessible object requested that we don't care about, so do default message processing + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_HELP message + /// + /// + private void WmHelp(ref Message m) { + + // if there's currently a message box open - grab the help info from it. + HelpInfo hpi = MessageBox.HelpInfo; + if (hpi != null) { + switch (hpi.Option) { + case NativeMethods.HLP_FILE : Help.ShowHelp (this, hpi.HelpFilePath); + break; + case NativeMethods.HLP_KEYWORD : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Keyword); + break; + case NativeMethods.HLP_NAVIGATOR : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Navigator); + break; + case NativeMethods.HLP_OBJECT : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Navigator, hpi.Param); + break; + } + } + + // Note: info.hItemHandle is the handle of the window that sent the help message. + NativeMethods.HELPINFO info = (NativeMethods.HELPINFO)m.GetLParam(typeof(NativeMethods.HELPINFO)); + + HelpEventArgs hevent = new HelpEventArgs(new Point(info.MousePos.x, info.MousePos.y)); + OnHelpRequested(hevent); + if (!hevent.Handled) { + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_INITMENUPOPUP message + /// + /// + private void WmInitMenuPopup(ref Message m) { + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + + if (contextMenu.ProcessInitMenuPopup(m.WParam)) + return; + } + DefWndProc(ref m); + } + + /// + /// WM_MEASUREITEM handler + /// + /// + private void WmMeasureItem(ref Message m) { + + // If the wparam is zero, then the message was sent by a menu. + // See WM_MEASUREITEM in MSDN. + if (m.WParam == IntPtr.Zero) { + + // Obtain the menu item object + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + Debug.Assert(m.LParam != IntPtr.Zero, "m.lparam is null"); + + // A pointer to the correct MenuItem is stored in the measure item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(mis.itemData); + Debug.Assert(menuItem != null, "UniqueID is not associated with a menu item"); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmMeasureItem(ref m); + } + } + else { + WmOwnerDraw(ref m); + } + } + + /// + /// Handles the WM_MENUCHAR message + /// + /// + private void WmMenuChar(ref Message m) { + Menu menu = ContextMenu; + if (menu != null) { + menu.WmMenuChar(ref m); + if (m.Result != IntPtr.Zero) { + // This char is a mnemonic on our menu. + return; + } + } + } + + /// + /// Handles the WM_MENUSELECT message + /// + /// + private void WmMenuSelect(ref Message m) { + int item = NativeMethods.Util.LOWORD(m.WParam); + int flags = NativeMethods.Util.HIWORD(m.WParam); + IntPtr hmenu = m.LParam; + MenuItem mi = null; + + if ((flags & NativeMethods.MF_SYSMENU) != 0) { + // nothing + } + else if ((flags & NativeMethods.MF_POPUP) == 0) { + Command cmd = Command.GetCommandFromID(item); + if (cmd != null) { + Object reference = cmd.Target; + if (reference != null && reference is MenuItem.MenuItemData) { + mi = ((MenuItem.MenuItemData)reference).baseItem; + } + } + } + else { + mi = GetMenuItemFromHandleId(hmenu, item); + } + + if (mi != null) { + mi.PerformSelect(); + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_CREATE message + /// + /// + private void WmCreate(ref Message m) { + + DefWndProc(ref m); + + if (parent != null) { + parent.UpdateChildZOrder(this); + } + UpdateBounds(); + + // Let any interested sites know that we've now created a handle + // + OnHandleCreated(EventArgs.Empty); + + // this code is important -- it is critical that we stash away + // the value of the text for controls such as edit, button, + // label, etc. Without this processing, any time you change a + // property that forces handle recreation, you lose your text! + // See the below code in wmDestroy + // + if (!GetStyle(ControlStyles.CacheText)) { + text = null; + } + } + + /// + /// Handles the WM_DESTROY message + /// + /// + private void WmDestroy(ref Message m) { + // Let any interested sites know that we're destroying our handle + // + + // Disabling the assert for M2 VSWhidbey 122139 + //Debug.Assert(IsHandleCreated, "Need to have the handle here"); + + if (!RecreatingHandle && !Disposing && !IsDisposed && GetState(STATE_TRACKINGMOUSEEVENT)) + { + // VSWhidbey 339087. Raise the MouseLeave event for the control below the mouse + // when a modal dialog is discarded. + OnMouseLeave(EventArgs.Empty); + UnhookMouseEvent(); + } + + if (SupportsUiaProviders) + { + ReleaseUiaProvider(Handle); + } + + OnHandleDestroyed(EventArgs.Empty); + + if (!Disposing) { + // If we are not recreating the handle, set our created state + // back to false so we can be rebuilt if we need to be. + // + if (!RecreatingHandle) { + SetState(STATE_CREATED, false); + } + } + else { + SetState(STATE_VISIBLE, false); + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and + /// WM_SYSKEYUP messages. + /// + /// + private void WmKeyChar(ref Message m) { + if (ProcessKeyMessage(ref m)) return; + DefWndProc(ref m); + } + + /// + /// Handles the WM_KILLFOCUS message + /// + /// + private void WmKillFocus(ref Message m) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::WmKillFocus - " + this.Name); + WmImeKillFocus(); + DefWndProc(ref m); + this.InvokeLostFocus(this, EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSEDOWN message + /// + /// + private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { + // If this is a "real" mouse event (not just WM_LBUTTONDOWN, etc) then + // we need to see if something happens during processing of + // user code that changed the state of the buttons (i.e. bringing up + // a dialog) to keep the control in a consistent state... + // + MouseButtons realState = MouseButtons; + SetState(STATE_MOUSEPRESSED, true); + + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + // we might had re-entered the message loop and processed a WM_CLOSE message + if (IsDisposed) { + return; + } + } + else { + // DefWndProc would normally set the focus to this control, but + // since we're skipping DefWndProc, we need to do it ourselves. + if (button == MouseButtons.Left && GetStyle(ControlStyles.Selectable)) { + FocusInternal(); + } + } + + if (realState != MouseButtons) { + return; + } + + if (!GetState2(STATE2_MAINTAINSOWNCAPTUREMODE)) { + //CaptureInternal is set usually in MouseDown (ToolStrip main exception) + CaptureInternal = true; + } + + if (realState != MouseButtons) { + return; + } + + // control should be enabled when this method is entered, but may have become + // disabled during its lifetime (e.g. through a Click or Focus listener) + if (Enabled) { + OnMouseDown(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + } + + /// + /// Handles the WM_MOUSEENTER message + /// + /// + private void WmMouseEnter(ref Message m) { + DefWndProc(ref m); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutMouseEnter(this); + } + OnMouseEnter(EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSELEAVE message + /// + /// + private void WmMouseLeave(ref Message m) { + DefWndProc(ref m); + OnMouseLeave(EventArgs.Empty); + } + + /// + /// Handles the WM_DPICHANGED_BEFOREPARENT message. This message is not sent to top level windows. + /// + private void WmDpiChangedBeforeParent(ref Message m) { + DefWndProc(ref m); + + if (IsHandleCreated) { + int deviceDpiOld = deviceDpi; + deviceDpi = (int)UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + + // Controls are by default font scaled. + // Dpi change requires font to be recalculated inorder to get controls scaled with right dpi. + if (deviceDpiOld != deviceDpi) { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + // Checking if font was inherited from parent. Font inherited from parent will receive OnParentFontChanged() events to scale those controls. + Font local = (Font)Properties.GetObject(PropFont); + if (local != null) { + var factor = (float)deviceDpi / deviceDpiOld; + this.Font = new Font(local.FontFamily, local.Size * factor, local.Style, local.Unit, local.GdiCharSet, local.GdiVerticalFont); + } + } + + RescaleConstantsForDpi(deviceDpiOld, deviceDpi); + } + } + + OnDpiChangedBeforeParent(EventArgs.Empty); + } + + /// + /// Handles the WM_DPICHANGED_AFTERPARENT message + /// + private void WmDpiChangedAfterParent(ref Message m) { + DefWndProc(ref m); + + uint dpi = UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + + OnDpiChangedAfterParent(EventArgs.Empty); + } + + /// + /// Handles the "WM_MOUSEHOVER" message... until we get actuall OS support + /// for this, it is implemented as a custom message. + /// + /// + private void WmMouseHover(ref Message m) { + DefWndProc(ref m); + OnMouseHover(EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSEMOVE message + /// + /// + private void WmMouseMove(ref Message m) { + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + } + OnMouseMove(new MouseEventArgs(MouseButtons, 0, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + + /// + /// Handles the WM_MOUSEUP message + /// + /// + private void WmMouseUp(ref Message m, MouseButtons button, int clicks) { + // Get the mouse location + // + try { + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x,y); + pt = PointToScreen(pt); + + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + } + else { + // DefWndProc would normally trigger a context menu here + // (for a right button click), but since we're skipping DefWndProc + // we have to do it ourselves. + if (button == MouseButtons.Right) { + SendMessage(NativeMethods.WM_CONTEXTMENU, this.Handle, NativeMethods.Util.MAKELPARAM(pt.X, pt.Y)); + } + } + + bool fireClick = false; + + if ((controlStyle & ControlStyles.StandardClick) == ControlStyles.StandardClick) { + if (GetState(STATE_MOUSEPRESSED) && !IsDisposed && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + fireClick = true; + } + } + + if (fireClick && !ValidationCancelled) { + if (!GetState(STATE_DOUBLECLICKFIRED)) { + //OnClick(EventArgs.Empty); + //In Whidbey .. if the click in by MOUSE then pass the MouseEventArgs... + OnClick(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + + else { + //OnDoubleClick(EventArgs.Empty); + OnDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + + } + //call the MouseUp Finally... + OnMouseUp(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + finally { + //Always Reset the STATE_DOUBLECLICKFIRED in UP.. Since we get UP - DOWN - DBLCLK - UP sequqnce + //The Flag is set in L_BUTTONDBLCLK in the controls WndProc() ... + // + SetState(STATE_DOUBLECLICKFIRED, false); + SetState(STATE_MOUSEPRESSED, false); + SetState(STATE_VALIDATIONCANCELLED, false); + //CaptureInternal is Resetted while exiting the MouseUp + CaptureInternal = false; + } + } + + /// + /// Handles the WM_MOUSEWHEEL message + /// + /// + private void WmMouseWheel(ref Message m) { + Point p = new Point(NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam)); + p = PointToClient(p); + HandledMouseEventArgs e = new HandledMouseEventArgs(MouseButtons.None, + 0, + p.X, + p.Y, + NativeMethods.Util.SignedHIWORD(m.WParam)); + OnMouseWheel(e); + m.Result = (IntPtr) (e.Handled ? 0 : 1); + if (!e.Handled) + { + // Forwarding the message to the parent window + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_MOVE message. We must do this in + /// addition to WM_WINDOWPOSCHANGED because windows may + /// send WM_MOVE directly. + /// + /// + private void WmMove(ref Message m) { + DefWndProc(ref m); + UpdateBounds(); + } + + /// + /// Handles the WM_NOTIFY message + /// + /// + private unsafe void WmNotify(ref Message m) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + if (!ReflectMessageInternal(nmhdr->hwndFrom,ref m)) { + if(nmhdr->code == NativeMethods.TTN_SHOW) { + m.Result = UnsafeNativeMethods.SendMessage(new HandleRef(null, nmhdr->hwndFrom), NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + return; + } + if(nmhdr->code == NativeMethods.TTN_POP) { + UnsafeNativeMethods.SendMessage(new HandleRef(null, nmhdr->hwndFrom), NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + } + + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_NOTIFYFORMAT message + /// + /// + private void WmNotifyFormat(ref Message m) { + if (!ReflectMessageInternal(m.WParam, ref m)) { + DefWndProc(ref m); + } + } + + + /// + /// Handles the WM_DRAWITEM\WM_MEASUREITEM messages for controls other than menus + /// + /// + private void WmOwnerDraw(ref Message m) { + bool reflectCalled = false; + + int ctrlId = unchecked((int)(long)m.WParam); + IntPtr p = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, m.HWnd), ctrlId); + if (p == IntPtr.Zero) { + // On 64-bit platforms wParam is already 64 bit but the control ID stored in it is only 32-bit + // Empirically, we have observed that the 64 bit HWND is just a sign extension of the 32-bit ctrl ID + // Since WParam is already 64-bit, we need to discard the high dword first and then re-extend the 32-bit value + // treating it as signed + p = (IntPtr)(long)ctrlId; + } + if (!ReflectMessageInternal(p, ref m)) { + //Additional Check For Control .... TabControl truncates the Hwnd value... + IntPtr handle = window.GetHandleFromID((short)NativeMethods.Util.LOWORD(m.WParam)); + if (handle != IntPtr.Zero) { + Control control = Control.FromHandleInternal(handle); + if (control != null) { + m.Result = control.SendMessage(NativeMethods.WM_REFLECT + m.Msg, handle, m.LParam); + reflectCalled = true; + } + } + } + else { + reflectCalled = true; + } + + + if (!reflectCalled) { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_PAINT messages. This should only be called + /// for userpaint controls. + /// + /// + private void WmPaint(ref Message m) { + bool doubleBuffered = DoubleBuffered || (GetStyle(ControlStyles.AllPaintingInWmPaint) && DoubleBufferingEnabled); +#if DEBUG + if (BufferDisabled.Enabled) { + doubleBuffered = false; + } +#endif + IntPtr hWnd = IntPtr.Zero; + IntPtr dc; + Rectangle clip; + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + bool needDisposeDC = false; + + try { + if (m.WParam == IntPtr.Zero) { + // Cache Handle not only for perf but to avoid object disposed exception in case the window + // is destroyed in an event handler (VSW#261657). + hWnd = this.Handle; + dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, hWnd), ref ps); + if (dc == IntPtr.Zero) { + return; + } + needDisposeDC = true; + clip = new Rectangle(ps.rcPaint_left, ps.rcPaint_top, + ps.rcPaint_right - ps.rcPaint_left, + ps.rcPaint_bottom - ps.rcPaint_top); + } + else { + dc = m.WParam; + clip = ClientRectangle; + } + + // Consider: Why don't check the clip condition when non-doubleBuffered? + // we should probably get rid of the !doubleBuffered condition. + if (!doubleBuffered || (clip.Width > 0 && clip.Height > 0)) { + IntPtr oldPal = IntPtr.Zero; + BufferedGraphics bufferedGraphics = null; + PaintEventArgs pevent = null; + GraphicsState state = null; + + try { + if (doubleBuffered || m.WParam == IntPtr.Zero) { + oldPal = SetUpPalette(dc, false, false); + } + + if (doubleBuffered) { + try { + bufferedGraphics = BufferContext.Allocate(dc, ClientRectangle); +#if DEBUG + if (BufferPinkRect.Enabled) { + Rectangle band = ClientRectangle; + using( BufferedGraphics bufferedGraphics2 = BufferContext.Allocate( dc, band ) ) { + bufferedGraphics2.Graphics.FillRectangle( new SolidBrush( Color.Red ), band ); + bufferedGraphics2.Render(); + } + Thread.Sleep(50); + } +#endif + } + catch (Exception ex) { + // BufferContext.Allocate will throw out of memory exceptions + // when it fails to create a device dependent bitmap while trying to + // get information about the device we are painting on. + // That is not the same as a system running out of memory and there is a + // very good chance that we can continue to paint successfully. We cannot + // check whether double buffering is supported in this case, and we will disable it. + // We could set a specific string when throwing the exception and check for it here + // to distinguish between that case and real out of memory exceptions but we + // see no reasons justifying the additional complexity. + if (ClientUtils.IsCriticalException(ex) && !(ex is OutOfMemoryException)) { + throw; + } +#if DEBUG + if( BufferPinkRect.Enabled ) { + Debug.WriteLine("Could not create buffered graphics, will paint in the surface directly" ); + } +#endif + doubleBuffered = false; // paint directly on the window DC. + } + } + + if (bufferedGraphics != null) { + bufferedGraphics.Graphics.SetClip(clip); + pevent = new PaintEventArgs(bufferedGraphics.Graphics, clip); + state = pevent.Graphics.Save(); + } + else { + pevent = new PaintEventArgs(dc, clip); + } + + using (pevent) { + try { + if ((m.WParam == IntPtr.Zero) && GetStyle(ControlStyles.AllPaintingInWmPaint) || doubleBuffered) { + PaintWithErrorHandling(pevent, PaintLayerBackground); + // Consider: This condition could be elimiated, + // do we have to save/restore the state of the buffered graphics? + + } + } + finally { + if (state != null) { + pevent.Graphics.Restore(state); + } + else { + pevent.ResetGraphics(); + } + } + PaintWithErrorHandling(pevent, PaintLayerForeground); + + if (bufferedGraphics != null) { + bufferedGraphics.Render(); + } + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); + } + if (bufferedGraphics != null) { + bufferedGraphics.Dispose(); + } + + } + } + } + finally { + if (needDisposeDC) { + UnsafeNativeMethods.EndPaint(new HandleRef(this, hWnd), ref ps); + } + } + } + + /// + /// Handles the WM_PRINTCLIENT messages. + /// + /// + private void WmPrintClient(ref Message m) { + using (PaintEventArgs e = new PrintPaintEventArgs(m, m.WParam, ClientRectangle)) { + OnPrint(e); + } + } + + private void WmQueryNewPalette(ref Message m) { + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, Handle + ": WM_QUERYNEWPALETTE"); + IntPtr dc = UnsafeNativeMethods.GetDC(new HandleRef(this, Handle)); + try { + SetUpPalette(dc, true /*force*/, true/*realize*/); + } + finally { + // Let WmPaletteChanged do any necessary invalidation + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, Handle), new HandleRef(null, dc)); + } + Invalidate(true); + m.Result = (IntPtr)1; + DefWndProc(ref m); + } + + + /// + /// Handles the WM_SETCURSOR message + /// + /// + private void WmSetCursor(ref Message m) { + + // Accessing through the Handle property has side effects that break this + // logic. You must use InternalHandle. + // + if (m.WParam == InternalHandle && NativeMethods.Util.LOWORD(m.LParam) == NativeMethods.HTCLIENT) { + Cursor.CurrentInternal = Cursor; + } + else { + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_WINDOWPOSCHANGING message + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + + // We let this fall through to defwndproc unless we are being surfaced as + // an ActiveX control. In that case, we must let the ActiveX side of things + // manipulate our bounds here. + // + if (IsActiveX) { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + // Only call UpdateBounds if the new bounds are different. + // + bool different = false; + + if ((wp->flags & NativeMethods.SWP_NOMOVE) == 0 && (wp->x != Left || wp->y != Top)) { + different = true; + } + if ((wp->flags & NativeMethods.SWP_NOSIZE) == 0 && (wp->cx != Width || wp->cy != Height)) { + different = true; + } + + if (different) { + ActiveXUpdateBounds(ref wp->x, ref wp->y, ref wp->cx, ref wp->cy, wp->flags); + } + } + + DefWndProc(ref m); + } + + + /// + /// Handles the WM_PARENTNOTIFY message + /// + /// + private void WmParentNotify(ref Message m) { + int msg = NativeMethods.Util.LOWORD(m.WParam); + IntPtr hWnd = IntPtr.Zero; + switch (msg) { + case NativeMethods.WM_CREATE: + hWnd = m.LParam; + break; + case NativeMethods.WM_DESTROY: + break; + default: + hWnd = UnsafeNativeMethods.GetDlgItem(new HandleRef(this, Handle), NativeMethods.Util.HIWORD(m.WParam)); + break; + } + if (hWnd == IntPtr.Zero || !ReflectMessageInternal(hWnd, ref m)) { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_SETFOCUS message + /// + /// + private void WmSetFocus(ref Message m) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::WmSetFocus - " + this.Name); + WmImeSetFocus(); + + if (!HostedInWin32DialogManager) { + IContainerControl c = GetContainerControlInternal(); + if (c != null) { + bool activateSucceed; + + ContainerControl knowncontainer = c as ContainerControl; + if (knowncontainer != null) { + activateSucceed = knowncontainer.ActivateControlInternal(this); + } + else { + // Reviewed : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + activateSucceed = c.ActivateControl(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + if (!activateSucceed) { + return; + } + } + } + + DefWndProc(ref m); + this.InvokeGotFocus(this, EventArgs.Empty); + } + + /// + /// Handles the WM_SHOWWINDOW message + /// + /// + private void WmShowWindow(ref Message m) { + // We get this message for each control, even if their parent is not visible. + + DefWndProc(ref m); + + if ((state & STATE_RECREATE) == 0) { + + bool visible = m.WParam != IntPtr.Zero; + bool oldVisibleProperty = Visible; + + if (visible) { + bool oldVisibleBit = GetState(STATE_VISIBLE); + SetState(STATE_VISIBLE, true); + bool executedOk = false; + try { + CreateControl(); + executedOk = true; + } + + finally { + if (!executedOk) { + // We do it this way instead of a try/catch because catching and rethrowing + // an exception loses call stack information + SetState(STATE_VISIBLE, oldVisibleBit); + } + } + } + else { // not visible + // If Windows tells us it's visible, that's pretty unambiguous. + // But if it tells us it's not visible, there's more than one explanation -- + // maybe the container control became invisible. So we look at the parent + // and take a guess at the reason. + + // We do not want to update state if we are on the parking window. + bool parentVisible = GetTopLevel(); + if (ParentInternal != null) { + parentVisible = ParentInternal.Visible; + } + + if (parentVisible) { + SetState(STATE_VISIBLE, false); + } + } + + if (!GetState(STATE_PARENTRECREATING) && (oldVisibleProperty != visible)) { + OnVisibleChanged(EventArgs.Empty); + } + } + } + + /// + /// Handles the WM_UPDATEUISTATE message + /// + /// + private void WmUpdateUIState(ref Message m) { + + // See "How this all works" in ShowKeyboardCues + + bool keyboard = false; + bool focus = false; + + // check the cached values in uiCuesState to see if we've ever set in the UI state + bool keyboardInitialized = (uiCuesState & UISTATE_KEYBOARD_CUES_MASK) != 0; + bool focusInitialized = (uiCuesState & UISTATE_FOCUS_CUES_MASK) != 0; + + if (keyboardInitialized) { + keyboard = ShowKeyboardCues; + } + + if (focusInitialized) { + focus = ShowFocusCues; + } + + DefWndProc(ref m); + + int cmd = NativeMethods.Util.LOWORD(m.WParam); + + // if we're initializing, dont bother updating the uiCuesState/Firing the event. + + if (cmd == NativeMethods.UIS_INITIALIZE) { + return; + } + + // Set in the cached value for uiCuesState... + + // Windows stores the opposite of what you would think, it has bit + // flags for the "Hidden" state, the presence of this flag means its + // hidden, the absence thereof means it's shown. + // + // When we're called here with a UIS_CLEAR and the hidden state is set + // that means we want to show the accelerator. + UICues UIcues = UICues.None; + if ((NativeMethods.Util.HIWORD(m.WParam) & NativeMethods.UISF_HIDEACCEL) != 0) { + + // yes, clear means show. nice api, guys. + // + bool showKeyboard = (cmd == NativeMethods.UIS_CLEAR); + + if (showKeyboard != keyboard || !keyboardInitialized ) { + + UIcues |= UICues.ChangeKeyboard; + + // clear the old state. + // + uiCuesState &= ~UISTATE_KEYBOARD_CUES_MASK; + uiCuesState |= (showKeyboard ? UISTATE_KEYBOARD_CUES_SHOW : UISTATE_KEYBOARD_CUES_HIDDEN) ; + } + + if (showKeyboard) { + UIcues |= UICues.ShowKeyboard; + } + } + + // Same deal for the Focus cues as the keyboard cues. + if ((NativeMethods.Util.HIWORD(m.WParam) & NativeMethods.UISF_HIDEFOCUS) != 0) { + + // yes, clear means show. nice api, guys. + // + bool showFocus = (cmd == NativeMethods.UIS_CLEAR); + + if (showFocus != focus || !focusInitialized) { + UIcues |= UICues.ChangeFocus; + + // clear the old state. + // + uiCuesState &= ~UISTATE_FOCUS_CUES_MASK; + uiCuesState |= (showFocus ? UISTATE_FOCUS_CUES_SHOW : UISTATE_FOCUS_CUES_HIDDEN) ; + } + + if (showFocus) { + UIcues |= UICues.ShowFocus; + } + } + + + // fire the UI cues state changed event. + if ((UIcues & UICues.Changed) != 0) { + OnChangeUICues(new UICuesEventArgs(UIcues)); + Invalidate(true); + } + } + + /// + /// Handles the WM_WINDOWPOSCHANGED message + /// + /// + private unsafe void WmWindowPosChanged(ref Message m) { + DefWndProc(ref m); + // Update new size / position + UpdateBounds(); + if (parent != null && UnsafeNativeMethods.GetParent(new HandleRef(window, InternalHandle)) == parent.InternalHandle && + (state & STATE_NOZORDER) == 0) { + + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + if ((wp->flags & NativeMethods.SWP_NOZORDER) == 0) { + parent.UpdateChildControlIndex(this); + } + } + } + + /// + /// + /// Base wndProc. All messages are sent to wndProc after getting filtered + /// through the preProcessMessage function. Inheriting controls should + /// call base.wndProc for any messages that they don't handle. + /// + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual void WndProc(ref Message m) { + // + // + + + + + /* + if( GetState(STATE_DISPOSED)) + { + Debug.Fail("Attempting to process a windows message in a disposed control. This may be OK if the app domain is being unloaded."); + DefWndProc(ref m); + return; + } + */ + + // inlined code from GetStyle(...) to ensure no perf hit + // for a method call... + // + if ((controlStyle & ControlStyles.EnableNotifyMessage) == ControlStyles.EnableNotifyMessage) { + // pass message *by value* to avoid the possibility + // of the OnNotifyMessage modifying the message. + // + OnNotifyMessage(m); + } + + /* + * If you add any new messages below (or change the message handling code for any messages) + * please make sure that you also modify AxHost.wndProc to do the right thing and intercept + * messages which the Ocx would own before passing them onto Control.wndProc. + */ + switch (m.Msg) { + case NativeMethods.WM_CAPTURECHANGED: + WmCaptureChanged(ref m); + break; + + case NativeMethods.WM_GETOBJECT: + WmGetObject(ref m); + break; + + case NativeMethods.WM_COMMAND: + WmCommand(ref m); + break; + case NativeMethods.WM_CLOSE: + WmClose(ref m); + break; + case NativeMethods.WM_CONTEXTMENU: + WmContextMenu(ref m); + break; + case NativeMethods.WM_DISPLAYCHANGE: + WmDisplayChange(ref m); + break; + case NativeMethods.WM_DRAWITEM: + WmDrawItem(ref m); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + + case NativeMethods.WM_EXITMENULOOP: + WmExitMenuLoop(ref m); + break; + + case NativeMethods.WM_HELP: + WmHelp(ref m); + break; + + case NativeMethods.WM_PAINT: + if (GetStyle(ControlStyles.UserPaint)) { + WmPaint(ref m); + } + else { + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_PRINTCLIENT: + if (GetStyle(ControlStyles.UserPaint)) { + WmPrintClient(ref m); + } + else { + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_INITMENUPOPUP: + WmInitMenuPopup(ref m); + break; + + case NativeMethods.WM_SYSCOMMAND: + if ((unchecked((int)(long)m.WParam) & 0xFFF0) == NativeMethods.SC_KEYMENU) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.WndProc processing " + m.ToString()); + + if (ToolStripManager.ProcessMenuKey(ref m)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.WndProc ToolStripManager.ProcessMenuKey returned true" + m.ToString()); + m.Result = IntPtr.Zero; + return; + } + } + DefWndProc(ref m); + break; + case NativeMethods.WM_INPUTLANGCHANGE: + WmInputLangChange(ref m); + break; + + case NativeMethods.WM_INPUTLANGCHANGEREQUEST: + WmInputLangChangeRequest(ref m); + break; + + case NativeMethods.WM_MEASUREITEM: + WmMeasureItem(ref m); + break; + case NativeMethods.WM_MENUCHAR: + WmMenuChar(ref m); + break; + + case NativeMethods.WM_MENUSELECT: + WmMenuSelect(ref m); + break; + + case NativeMethods.WM_SETCURSOR: + WmSetCursor(ref m); + break; + + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref m); + break; + + case NativeMethods.WM_CHAR: + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + case NativeMethods.WM_KEYUP: + case NativeMethods.WM_SYSKEYUP: + WmKeyChar(ref m); + break; + case NativeMethods.WM_CREATE: + WmCreate(ref m); + break; + case NativeMethods.WM_DESTROY: + WmDestroy(ref m); + break; + + + case NativeMethods.WM_CTLCOLOR: + case NativeMethods.WM_CTLCOLORBTN: + case NativeMethods.WM_CTLCOLORDLG: + case NativeMethods.WM_CTLCOLORMSGBOX: + case NativeMethods.WM_CTLCOLORSCROLLBAR: + case NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_CTLCOLORLISTBOX: + case NativeMethods.WM_CTLCOLORSTATIC: + + // VSWhidbey 165168 -- this is for the trinity guys. The case is if you've got a windows + // forms edit or something hosted as an AX control somewhere, there isn't anyone to reflect + // these back. If they went ahead and just sent them back, some controls don't like that + // and end up recursing. Our code handles it fine because we just pick the HWND out of the LPARAM. + // + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLOR: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORBTN: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORDLG: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORMSGBOX: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORSCROLLBAR: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORLISTBOX: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORSTATIC: + WmCtlColorControl(ref m); + break; + case NativeMethods.WM_HSCROLL: + case NativeMethods.WM_VSCROLL: + case NativeMethods.WM_DELETEITEM: + case NativeMethods.WM_VKEYTOITEM: + case NativeMethods.WM_CHARTOITEM: + case NativeMethods.WM_COMPAREITEM: + if (!ReflectMessageInternal(m.LParam, ref m)) { + DefWndProc(ref m); + } + break; + case NativeMethods.WM_IME_CHAR: + WmImeChar(ref m); + break; + case NativeMethods.WM_IME_STARTCOMPOSITION: + WmImeStartComposition(ref m); + break; + case NativeMethods.WM_IME_ENDCOMPOSITION: + WmImeEndComposition(ref m); + break; + case NativeMethods.WM_IME_NOTIFY: + WmImeNotify(ref m); + break; + case NativeMethods.WM_KILLFOCUS: + WmKillFocus(ref m); + break; + case NativeMethods.WM_LBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Left, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_LBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Left, 1); + break; + case NativeMethods.WM_LBUTTONUP: + WmMouseUp(ref m, MouseButtons.Left, 1); + break; + case NativeMethods.WM_MBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Middle, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_MBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Middle, 1); + break; + case NativeMethods.WM_MBUTTONUP: + WmMouseUp(ref m, MouseButtons.Middle, 1); + break; + case NativeMethods.WM_XBUTTONDOWN: + WmMouseDown(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 1); + break; + case NativeMethods.WM_XBUTTONUP: + WmMouseUp(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 1); + break; + case NativeMethods.WM_XBUTTONDBLCLK: + WmMouseDown(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_MOUSELEAVE: + WmMouseLeave(ref m); + break; + case NativeMethods.WM_DPICHANGED_BEFOREPARENT: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChangedBeforeParent(ref m); + m.Result = IntPtr.Zero; + } + break; + case NativeMethods.WM_DPICHANGED_AFTERPARENT: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChangedAfterParent(ref m); + m.Result = IntPtr.Zero; + } + break; + case NativeMethods.WM_MOUSEMOVE: + WmMouseMove(ref m); + break; + case NativeMethods.WM_MOUSEWHEEL: + WmMouseWheel(ref m); + break; + case NativeMethods.WM_MOVE: + WmMove(ref m); + break; + case NativeMethods.WM_NOTIFY: + WmNotify(ref m); + break; + case NativeMethods.WM_NOTIFYFORMAT: + WmNotifyFormat(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFYFORMAT: + m.Result = (IntPtr)(Marshal.SystemDefaultCharSize == 1 ? NativeMethods.NFR_ANSI : NativeMethods.NFR_UNICODE); + break; + case NativeMethods.WM_SHOWWINDOW: + WmShowWindow(ref m); + break; + case NativeMethods.WM_RBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Right, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_RBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Right, 1); + break; + case NativeMethods.WM_RBUTTONUP: + WmMouseUp(ref m, MouseButtons.Right, 1); + break; + case NativeMethods.WM_SETFOCUS: + WmSetFocus(ref m); + break; + case NativeMethods.WM_MOUSEHOVER: + WmMouseHover(ref m); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + WmWindowPosChanged(ref m); + break; + case NativeMethods.WM_QUERYNEWPALETTE: + WmQueryNewPalette(ref m); + break; + case NativeMethods.WM_UPDATEUISTATE: + WmUpdateUIState(ref m); + break; + case NativeMethods.WM_PARENTNOTIFY: + WmParentNotify(ref m); + break; + default: + // If we received a thread execute message, then execute it. + // + if (m.Msg == threadCallbackMessage && m.Msg != 0) { + InvokeMarshaledCallbacks(); + return; + } + else if (m.Msg == Control.WM_GETCONTROLNAME) { + WmGetControlName(ref m); + return; + } + else if (m.Msg == Control.WM_GETCONTROLTYPE) { + WmGetControlType(ref m); + return; + } + + #if WIN95_SUPPORT + // If we have to route the mousewheel messages, do it (this logic was taken + // from the MFC sources...) + // + if (mouseWheelRoutingNeeded) { + if (m.Msg == mouseWheelMessage) { + Keys keyState = Keys.None; + keyState |= (Keys)((UnsafeNativeMethods.GetKeyState((int)Keys.ControlKey) < 0) ? NativeMethods.MK_CONTROL : 0); + keyState |= (Keys)((UnsafeNativeMethods.GetKeyState((int)Keys.ShiftKey) < 0) ? NativeMethods.MK_SHIFT : 0); + + IntPtr hwndFocus = UnsafeNativeMethods.GetFocus(); + + if (hwndFocus == IntPtr.Zero) { + SendMessage(m.Msg, (IntPtr)((unchecked((int)(long)m.WParam) << 16) | (int)keyState), m.LParam); + } + else { + IntPtr result = IntPtr.Zero; + IntPtr hwndDesktop = UnsafeNativeMethods.GetDesktopWindow(); + + while (result == IntPtr.Zero && hwndFocus != IntPtr.Zero && hwndFocus != hwndDesktop) { + result = UnsafeNativeMethods.SendMessage(new HandleRef(null, hwndFocus), + NativeMethods.WM_MOUSEWHEEL, + (unchecked((int)(long)m.WParam) << 16) | (int)keyState, + m.LParam); + hwndFocus = UnsafeNativeMethods.GetParent(new HandleRef(null, hwndFocus)); + } + } + } + } + #endif + + if (m.Msg == NativeMethods.WM_MOUSEENTER) { + WmMouseEnter(ref m); + break; + } + + DefWndProc(ref m); + break; + } + + } + + /// + /// Called when an exception occurs in dispatching messages through + /// the main window procedure. + /// + private void WndProcException(Exception e) { + Application.OnThreadException(e); + } + + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection == null) { + return ArrangedElementCollection.Empty; + } + return controlsCollection; + } + } + + /// + /// + IArrangedElement IArrangedElement.Container { + get { + // This is safe because the IArrangedElement interface is internal + return ParentInternal; + } + } + + /// + /// + bool IArrangedElement.ParticipatesInLayout { + get { return GetState(STATE_VISIBLE); } + } + + /// + /// + void IArrangedElement.PerformLayout(IArrangedElement affectedElement, string affectedProperty) { + PerformLayout(new LayoutEventArgs(affectedElement, affectedProperty)); + } + + /// + /// + PropertyStore IArrangedElement.Properties { + get { return Properties; } + } + + // CAREFUL: This really calls SetBoundsCore, not SetBounds. + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + ISite site = Site; + IComponentChangeService ccs = null; + PropertyDescriptor sizeProperty = null; + PropertyDescriptor locationProperty = null; + bool sizeChanged = false; + bool locationChanged = false; + + if (site != null && site.DesignMode) { + ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if(ccs != null) { + sizeProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Size]; + locationProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Location]; + Debug.Assert(sizeProperty != null && locationProperty != null, "Error retrieving Size/Location properties on Control."); + + try { + if (sizeProperty != null && !sizeProperty.IsReadOnly && (bounds.Width != this.Width || bounds.Height != this.Height)) { + if (!(site is INestedSite)) + { + ccs.OnComponentChanging(this, sizeProperty); + } + sizeChanged = true; + } + if (locationProperty != null && !locationProperty.IsReadOnly && (bounds.X != this.x || bounds.Y != this.y)) { + if (!(site is INestedSite)) + { + ccs.OnComponentChanging(this, locationProperty); + } + locationChanged = true; + } + } + catch (InvalidOperationException) { + // The component change events can throw InvalidOperationException if a change is + // currently not allowed (typically because the doc data in VS is locked). + // When this happens, we just eat the exception and proceed with the change. + } + } + } + + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + + if (site != null && ccs != null) { + try { + if (sizeChanged) ccs.OnComponentChanged(this, sizeProperty, null, null); + if (locationChanged) ccs.OnComponentChanged(this, locationProperty, null, null); + } + catch (InvalidOperationException) { + // The component change events can throw InvalidOperationException if a change is + // currently not allowed (typically because the doc data in VS is locked). + // When this happens, we just eat the exception and proceed with the change. + } + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces + /// + internal virtual bool SupportsUiaProviders { + get { + return false; + } + } + + /// + /// + internal sealed class ControlNativeWindow : NativeWindow, IWindowTarget { + private Control control; + private GCHandle rootRef; // We will root the control when we do not want to be elligible for garbage collection. + internal IWindowTarget target; + + internal ControlNativeWindow(Control control) { + this.control = control; + target = this; + } + + internal Control GetControl() { + return control; + } + + protected override void OnHandleChange() { + target.OnHandleChange(this.Handle); + } + + // IWindowTarget method + public void OnHandleChange(IntPtr newHandle) { + control.SetHandle(newHandle); + } + + internal void LockReference(bool locked) { + if (locked) { + if (!rootRef.IsAllocated) { + rootRef = GCHandle.Alloc(GetControl(), GCHandleType.Normal); + } + } + else { + if (rootRef.IsAllocated) { + rootRef.Free(); + } + } + } + + protected override void OnThreadException(Exception e) { + control.WndProcException(e); + } + + // IWindowTarget method + public void OnMessage(ref Message m) { + control.WndProc(ref m); + } + + internal IWindowTarget WindowTarget { + get { + return target; + } + set { + target = value; + } + } + + #if DEBUG + // We override ToString so in debug asserts that fire for + // non-released controls will show what control wasn't released. + // + public override string ToString() { + if (control != null) { + return control.GetType().FullName; + } + return base.ToString(); + } + #endif + + protected override void WndProc(ref Message m) { + // There are certain messages that we want to process + // regardless of what window target we are using. These + // messages cause other messages or state transitions + // to occur within control. + // + switch (m.Msg) { + case NativeMethods.WM_MOUSELEAVE: + control.UnhookMouseEvent(); + break; + + case NativeMethods.WM_MOUSEMOVE: + if (!control.GetState(Control.STATE_TRACKINGMOUSEEVENT)) { + control.HookMouseEvent(); + if (!control.GetState(Control.STATE_MOUSEENTERPENDING)) { + control.SendMessage(NativeMethods.WM_MOUSEENTER, 0, 0); + } + else { + control.SetState(Control.STATE_MOUSEENTERPENDING, false); + } + } + break; + + case NativeMethods.WM_MOUSEWHEEL: + // TrackMouseEvent's mousehover implementation doesn't watch the wheel + // correctly... + // + control.ResetMouseEventArgs(); + break; + } + + target.OnMessage(ref m); + } + } + + /// + /// Explicit support of DropTarget + /// + /// + /// + void IDropTarget.OnDragEnter(DragEventArgs drgEvent) { + OnDragEnter(drgEvent); + } + /// + /// + void IDropTarget.OnDragOver(DragEventArgs drgEvent) { + OnDragOver(drgEvent); + } + /// + /// + void IDropTarget.OnDragLeave(EventArgs e) { + OnDragLeave(e); + } + /// + /// + void IDropTarget.OnDragDrop(DragEventArgs drgEvent) { + OnDragDrop(drgEvent); + } + + /// + /// Explicit support of DropSource + /// + /// + /// + void ISupportOleDropSource.OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEventArgs) { + OnGiveFeedback(giveFeedbackEventArgs); + } + /// + /// + void ISupportOleDropSource.OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEventArgs) { + OnQueryContinueDrag(queryContinueDragEventArgs); + } + + /// + /// + /// Collection of controls... + /// + [ + ListBindable(false), ComVisible(false) + ] + public class ControlCollection : ArrangedElementCollection, IList, ICloneable { + + private Control owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways. + private int lastAccessedIndex = -1; + + + /// + /// + /// [To be supplied.] + /// + public ControlCollection(Control owner) { + this.owner = owner; + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// Adds a child control to this control. The control becomes the last control in + /// the child control list. If the control is already a child of another control it + /// is first removed from that control. + /// + public virtual void Add(Control value) { + if (value == null) + return; + if (value.GetTopLevel()) { + throw new ArgumentException(SR.GetString(SR.TopLevelControlAdd)); + } + + // Verify that the control being added is on the same thread as + // us...or our parent chain. + // + if (owner.CreateThreadId != value.CreateThreadId) { + throw new ArgumentException(SR.GetString(SR.AddDifferentThreads)); + } + + CheckParentingCycle(owner, value); + + if (value.parent == owner) { + value.SendToBack(); + return; + } + + // Remove the new control from its old parent (if any) + // + if (value.parent != null) { + value.parent.Controls.Remove(value); + } + + // Add the control + // + InnerList.Add(value); + + if (value.tabIndex == -1) { + + // Find the next highest tab index + // + int nextTabIndex = 0; + for (int c = 0; c < (Count - 1); c++) { + int t = this[c].TabIndex; + if (nextTabIndex <= t) { + nextTabIndex = t + 1; + } + } + value.tabIndex = nextTabIndex; + } + + // if we don't suspend layout, AssignParent will indirectly trigger a layout event + // before we're ready (AssignParent will fire a PropertyChangedEvent("Visible"), which calls PerformLayout) + // +#if DEBUG + int dbgLayoutCheck = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + + try { + Control oldParent = value.parent; + try { + // AssignParent calls into user code - this could throw, which + // would make us short-circuit the rest of the reparenting logic. + // you could end up with a control half reparented. + value.AssignParent(owner); + } + finally { + if (oldParent != value.parent && (owner.state & STATE_CREATED) != 0) { + value.SetParentHandle(owner.InternalHandle); + if (value.Visible) { + value.CreateControl(); + } + } + } + + value.InitLayout(); + } + finally { + owner.ResumeLayout(false); +#if DEBUG + owner.AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + + } + + // Not putting in the finally block, as it would eat the original + // exception thrown from AssignParent if the following throws an exception. + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + owner.OnControlAdded(new ControlEventArgs(value)); + + + } + + /// + /// + int IList.Add(object control) { + if (control is Control) { + Add((Control)control); + return IndexOf((Control)control); + } + else { + throw new ArgumentException(SR.GetString(SR.ControlBadControl), "control"); + } + } + + /// + /// + /// [To be supplied.] + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual void AddRange(Control[] controls) { + if (controls == null) { + throw new ArgumentNullException("controls"); + } + if (controls.Length > 0) { +#if DEBUG + int dbgLayoutCheck = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + try { + for(int i=0;i < controls.Length; ++i) { + Add(controls[i]); + } + } + finally { + owner.ResumeLayout(true); + } +#if DEBUG + owner.AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// + object ICloneable.Clone() { + // Use CreateControlInstance so we get the same type of ControlCollection, but whack the + // owner so adding controls to this new collection does not affect the control we cloned from. + ControlCollection ccOther = owner.CreateControlsInstance(); + + // We add using InnerList to prevent unnecessary parent cycle checks, etc. + ccOther.InnerList.AddRange(this); + return ccOther; + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(Control control) { + return InnerList.Contains(control); + } + + + /// + /// + /// Searches for Controls by their Name property, builds up an array + /// of all the controls that match. + /// + /// + public Control[] Find(string key, bool searchAllChildren) { + if (String.IsNullOrEmpty(key)) { + throw new System.ArgumentNullException("key", SR.GetString(SR.FindKeyMayNotBeEmptyOrNull)); + } + + ArrayList foundControls = FindInternal(key, searchAllChildren, this, new ArrayList()); + + // Make this a stongly typed collection. + Control[] stronglyTypedFoundControls = new Control[foundControls.Count]; + foundControls.CopyTo(stronglyTypedFoundControls, 0); + + return stronglyTypedFoundControls; + } + + /// + /// + /// Searches for Controls by their Name property, builds up an array list + /// of all the controls that match. + /// + /// + /// + private ArrayList FindInternal(string key, bool searchAllChildren, ControlCollection controlsToLookIn, ArrayList foundControls) { + if ((controlsToLookIn == null) || (foundControls == null)) { + return null; + } + + try { + // Perform breadth first search - as it's likely people will want controls belonging + // to the same parent close to each other. + + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null){ + continue; + } + + if (WindowsFormsUtils.SafeCompareStrings(controlsToLookIn[i].Name, key, /* ignoreCase = */ true)) { + foundControls.Add(controlsToLookIn[i]); + } + } + + // Optional recurive search for controls in child collections. + + if (searchAllChildren){ + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null){ + continue; + } + if ((controlsToLookIn[i].Controls != null) && controlsToLookIn[i].Controls.Count > 0){ + // if it has a valid child collecion, append those results to our collection + foundControls = FindInternal(key, searchAllChildren, controlsToLookIn[i].Controls, foundControls); + } + } + } + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + return foundControls; + } + + public override IEnumerator GetEnumerator() { + return new ControlCollectionEnumerator(this); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(Control control) { + return InnerList.IndexOf(control); + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (String.IsNullOrEmpty(key)) { + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// Who owns this control collection. + /// + /// + public Control Owner { + get { + return owner; + } + } + + /// + /// + /// Removes control from this control. Inheriting controls should call + /// base.remove to ensure that the control is removed. + /// + public virtual void Remove(Control value) { + + // Sanity check parameter + // + if (value == null) { + return; // Don't do anything + } + + if (value.ParentInternal == owner) { + Debug.Assert(owner != null); + + value.SetParentHandle(IntPtr.Zero); + + // Remove the control from the internal control array + // + InnerList.Remove(value); + value.AssignParent(null); + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + owner.OnControlRemoved(new ControlEventArgs(value)); + + // ContainerControl needs to see it needs to find a new ActiveControl. + ContainerControl cc = owner.GetContainerControlInternal() as ContainerControl; + if (cc != null) + { + cc.AfterControlRemoved(value, owner); + } + } + } + + /// + /// + void IList.Remove(object control) { + if (control is Control) { + Remove((Control)control); + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + Remove(this[index]); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// Retrieves the child control with the specified index. + /// + public new virtual Control this[int index] { + get { + //do some bounds checking here... + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); + } + + Control control = (Control) InnerList[index]; + Debug.Assert(control != null, "Why are we returning null controls from a valid index?"); + return control; + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual Control this[string key] { + get { + // We do not support null and empty string as valid keys. + if (String.IsNullOrEmpty(key)) { + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { +#if DEBUG + int layoutSuspendCount = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + // clear all preferred size caches in the tree - + // inherited fonts could go away, etc. + CommonProperties.xClearAllPreferredSizeCaches(owner); + + try { + while (Count != 0) + RemoveAt(Count - 1); + } finally { + owner.ResumeLayout(); +#if DEBUG + Debug.Assert(owner.LayoutSuspendCount == layoutSuspendCount, "Suspend/Resume layout mismatch!"); +#endif + } + } + + /// + /// + /// Retrieves the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public int GetChildIndex(Control child) { + return GetChildIndex(child, true); + } + + /// + /// + /// Retrieves the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public virtual int GetChildIndex(Control child, bool throwException) { + int index = IndexOf(child); + if (index == -1 && throwException) { + throw new ArgumentException(SR.GetString(SR.ControlNotChild)); + } + return index; + } + + /// + /// This is internal virtual method so that "Readonly Collections" can override this and throw as they should not allow changing + /// the child control indices. + /// + /// + internal virtual void SetChildIndexInternal(Control child, int newIndex) + { + // Sanity check parameters + // + if (child == null) { + throw new ArgumentNullException("child"); + } + + int currentIndex = GetChildIndex(child); + + if (currentIndex == newIndex) { + return; + } + + if (newIndex >= Count || newIndex == -1) { + newIndex = Count - 1; + } + + MoveElement(child, currentIndex, newIndex); + child.UpdateZOrder(); + + LayoutTransaction.DoLayout(owner, child, PropertyNames.ChildIndex); + + } + + /// + /// + /// Sets the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public virtual void SetChildIndex(Control child, int newIndex) { + SetChildIndexInternal(child, newIndex); + } + + // COMPAT: VSWhidbey 448276 + // This is the same as WinformsUtils.ArraySubsetEnumerator + // however since we're no longer an array, we've gotta employ a + // special version of this. + private class ControlCollectionEnumerator : IEnumerator { + private ControlCollection controls; + private int current; + private int originalCount; + + public ControlCollectionEnumerator(ControlCollection controls) { + this.controls = controls; + this.originalCount = controls.Count; + current = -1; + } + + public bool MoveNext() { + // VSWhidbey 448276 + // We have to use Controls.Count here because someone could have deleted + // an item from the array. + // + // this can happen if someone does: + // foreach (Control c in Controls) { c.Dispose(); } + // + // We also dont want to iterate past the original size of the collection + // + // this can happen if someone does + // foreach (Control c in Controls) { c.Controls.Add(new Label()); } + + if (current < controls.Count - 1 && current < originalCount - 1) { + current++; + return true; + } + else { + return false; + } + } + + public void Reset() { + current = -1; + } + + public object Current { + get { + if (current == -1) { + return null; + } + else { + return controls[current]; + } + } + } + } + + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.GetControlInfo(NativeMethods.tagCONTROLINFO pCI) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetControlInfo"); + + pCI.cb = Marshal.SizeOf(typeof(NativeMethods.tagCONTROLINFO)); + pCI.hAccel = IntPtr.Zero; + pCI.cAccel = 0; + pCI.dwFlags = 0; + + if (IsInputKey(Keys.Return)) { + pCI.dwFlags |= NativeMethods.CTRLINFO_EATS_RETURN; + } + if (IsInputKey(Keys.Escape)) { + pCI.dwFlags |= NativeMethods.CTRLINFO_EATS_ESCAPE; + } + + ActiveXInstance.GetControlInfo(pCI); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.OnMnemonic(ref NativeMethods.MSG pMsg) { + + // If we got a mnemonic here, then the appropriate control will focus itself which + // will cause us to become UI active. + // + bool processed = ProcessMnemonic((char)pMsg.wParam); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnMnemonic processed: " + processed.ToString()); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.OnAmbientPropertyChange(int dispID) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnAmbientPropertyChange. Dispid: " + dispID); + Debug.Indent(); + ActiveXInstance.OnAmbientPropertyChange(dispID); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.FreezeEvents(int bFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:FreezeEvents. Freeze: " + bFreeze); + ActiveXInstance.EventsFrozen = (bFreeze != 0); + Debug.Assert(ActiveXInstance.EventsFrozen == (bFreeze != 0), "Failed to set EventsFrozen correctly"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceActiveObject.GetWindow(out IntPtr hwnd) { + return((UnsafeNativeMethods.IOleInPlaceObject)this).GetWindow(out hwnd); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.ContextSensitiveHelp(int fEnterMode) { + ((UnsafeNativeMethods.IOleInPlaceObject)this).ContextSensitiveHelp(fEnterMode); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceActiveObject.TranslateAccelerator(ref NativeMethods.MSG lpmsg) { + return ActiveXInstance.TranslateAccelerator(ref lpmsg); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.OnFrameWindowActivate(bool fActivate) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnFrameWindowActivate"); + OnFrameWindowActivate(fActivate); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.OnDocWindowActivate(int fActivate) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnDocWindowActivate. Activate: " + fActivate.ToString(CultureInfo.InvariantCulture)); + Debug.Indent(); + ActiveXInstance.OnDocWindowActivate(fActivate); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.ResizeBorder(NativeMethods.COMRECT prcBorder, UnsafeNativeMethods.IOleInPlaceUIWindow pUIWindow, bool fFrameWindow) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ResizesBorder"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.EnableModeless(int fEnable) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnableModeless"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceObject.GetWindow(out IntPtr hwnd) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetWindow"); + int hr = ActiveXInstance.GetWindow(out hwnd); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "\twin == " + hwnd); + return hr; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.ContextSensitiveHelp(int fEnterMode) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ContextSensitiveHelp. Mode: " + fEnterMode.ToString(CultureInfo.InvariantCulture)); + if (fEnterMode != 0) { + OnHelpRequested(new HelpEventArgs(Control.MousePosition)); + } + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.InPlaceDeactivate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:InPlaceDeactivate"); + Debug.Indent(); + ActiveXInstance.InPlaceDeactivate(); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceObject.UIDeactivate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:UIDeactivate"); + return ActiveXInstance.UIDeactivate(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.SetObjectRects(NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetObjectRects(" + lprcClipRect.left + ", " + lprcClipRect.top + ", " + lprcClipRect.right + ", " + lprcClipRect.bottom + ")"); + Debug.Indent(); + ActiveXInstance.SetObjectRects(lprcPosRect, lprcClipRect); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.ReactivateAndUndo() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ReactivateAndUndo"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetClientSite(UnsafeNativeMethods.IOleClientSite pClientSite) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetClientSite"); + ActiveXInstance.SetClientSite(pClientSite); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + UnsafeNativeMethods.IOleClientSite UnsafeNativeMethods.IOleObject.GetClientSite() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetClientSite"); + return ActiveXInstance.GetClientSite(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetHostNames(string szContainerApp, string szContainerObj) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetHostNames"); + // Since ActiveX controls never "open" for editing, we shouldn't need + // to store these. + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Close(int dwSaveOption) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Close. Save option: " + dwSaveOption); + ActiveXInstance.Close(dwSaveOption); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetMoniker(int dwWhichMoniker, Object pmk) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetMoniker"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetMoniker(int dwAssign, int dwWhichMoniker, out Object moniker) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMoniker"); + moniker = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.InitFromData(IComDataObject pDataObject, int fCreation, int dwReserved) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:InitFromData"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetClipboardData(int dwReserved, out IComDataObject data) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetClipboardData"); + data = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.DoVerb(int iVerb, IntPtr lpmsg, UnsafeNativeMethods.IOleClientSite pActiveSite, int lindex, IntPtr hwndParent, NativeMethods.COMRECT lprcPosRect) { + + // In Office they are internally casting an iverb to a short and not + // doing the proper sign extension. So, we do it here. + // + short sVerb = unchecked((short)iVerb); + iVerb = (int)sVerb; + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("AxSource:DoVerb {"); + Debug.WriteLine(" verb: " + iVerb); + Debug.WriteLine(" msg: " + lpmsg); + Debug.WriteLine(" activeSite: " + pActiveSite); + Debug.WriteLine(" index: " + lindex); + Debug.WriteLine(" hwndParent: " + hwndParent); + Debug.WriteLine(" posRect: " + lprcPosRect); + } +#endif + Debug.Indent(); + try { + ActiveXInstance.DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect); + } + catch (Exception e) { + Debug.Fail("Exception occurred during DoVerb(" + iVerb + ") " + e.ToString()); + throw; + } + finally { + Debug.Unindent(); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "}"); + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnumVerbs"); + return ActiveXImpl.EnumVerbs(out e); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.OleUpdate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OleUpdate"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.IsUpToDate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IsUpToDate"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetUserClassID(ref Guid pClsid) { + pClsid = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetUserClassID. ClassID: " + pClsid.ToString()); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetUserType(int dwFormOfType, out string userType) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetUserType"); + if (dwFormOfType == NativeMethods.USERCLASSTYPE_FULL) { + userType = GetType().FullName; + } + else { + userType = GetType().Name; + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetExtent(" + pSizel.cx + ", " + pSizel.cy + ")"); + Debug.Indent(); + ActiveXInstance.SetExtent(dwDrawAspect, pSizel); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetExtent. Aspect: " + dwDrawAspect.ToString(CultureInfo.InvariantCulture)); + Debug.Indent(); + ActiveXInstance.GetExtent(dwDrawAspect, pSizel); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "value: " + pSizel.cx + ", " + pSizel.cy); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Advise(IAdviseSink pAdvSink, out int cookie) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Advise"); + cookie = ActiveXInstance.Advise(pAdvSink); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Unadvise(int dwConnection) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unadvise"); + Debug.Indent(); + ActiveXInstance.Unadvise(dwConnection); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.EnumAdvise(out IEnumSTATDATA e) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnumAdvise"); + e = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetMiscStatus(int dwAspect, out int cookie) { + if ((dwAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + int status = NativeMethods.OLEMISC_ACTIVATEWHENVISIBLE | NativeMethods.OLEMISC_INSIDEOUT | NativeMethods.OLEMISC_SETCLIENTSITEFIRST; + + if (GetStyle(ControlStyles.ResizeRedraw)) { + status |= NativeMethods.OLEMISC_RECOMPOSEONRESIZE; + } + + if (this is IButtonControl) { + status |= NativeMethods.OLEMISC_ACTSLIKEBUTTON; + } + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMiscStatus. Status: " + status.ToString(CultureInfo.InvariantCulture)); + cookie = status; + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMiscStatus. Status: ERROR, wrong aspect."); + cookie = 0; + return NativeMethods.DV_E_DVASPECT; + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetColorScheme(NativeMethods.tagLOGPALETTE pLogpal) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetColorScheme"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + return((UnsafeNativeMethods.IOleInPlaceObject)this).GetWindow(out hwnd); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + ((UnsafeNativeMethods.IOleInPlaceObject)this).ContextSensitiveHelp(fEnterMode); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersist.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersist.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.InitNew() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistPropertyBag.InitNew"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistPropertyBag.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.Load(UnsafeNativeMethods.IPropertyBag pPropBag, UnsafeNativeMethods.IErrorLog pErrorLog) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Load (IPersistPropertyBag)"); + Debug.Indent(); + ActiveXInstance.Load(pPropBag, pErrorLog); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.Save(UnsafeNativeMethods.IPropertyBag pPropBag, bool fClearDirty, bool fSaveAllProperties) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Save (IPersistPropertyBag)"); + Debug.Indent(); + ActiveXInstance.Save(pPropBag, fClearDirty, fSaveAllProperties); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStorage.IsDirty() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.IsDirty"); + return ActiveXInstance.IsDirty(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.InitNew(UnsafeNativeMethods.IStorage pstg) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.InitNew"); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStorage.Load(UnsafeNativeMethods.IStorage pstg) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.Load"); + Debug.Indent(); + ActiveXInstance.Load(pstg); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.Save(UnsafeNativeMethods.IStorage pstg, bool fSameAsLoad) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.Save"); + Debug.Indent(); + ActiveXInstance.Save(pstg, fSameAsLoad); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.SaveCompleted(UnsafeNativeMethods.IStorage pStgNew) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.SaveCompleted"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.HandsOffStorage() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.HandsOffStorage"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStreamInit.IsDirty() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.IsDirty"); + return ActiveXInstance.IsDirty(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.Load(UnsafeNativeMethods.IStream pstm) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.Load"); + Debug.Indent(); + ActiveXInstance.Load(pstm); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.Save(UnsafeNativeMethods.IStream pstm, bool fClearDirty) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.Save"); + Debug.Indent(); + ActiveXInstance.Save(pstm, fClearDirty); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.GetSizeMax(long pcbSize) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetSizeMax"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.InitNew() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.InitNew"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:QuickActivate"); + Debug.Indent(); + ActiveXInstance.QuickActivate(pQaContainer, pQaControl); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.SetContentExtent(NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetContentExtent"); + Debug.Indent(); + ActiveXInstance.SetExtent(NativeMethods.DVASPECT_CONTENT, pSizel); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.GetContentExtent(NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetContentExtent"); + Debug.Indent(); + ActiveXInstance.GetExtent(NativeMethods.DVASPECT_CONTENT, pSizel); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT lprcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Draw"); + + + Debug.Indent(); + try { + ActiveXInstance.Draw(dwDrawAspect, lindex, pvAspect, ptd, hdcTargetDev, + hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue); + + } + catch(ExternalException ex) { + return ex.ErrorCode; + } + finally { + Debug.Unindent(); + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.GetColorSet(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hicTargetDev, NativeMethods.tagLOGPALETTE ppColorSet) { + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetColorSet"); + + // GDI+ doesn't do palettes. + // + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Freeze(int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr pdwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Freezes"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Unfreeze(int dwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unfreeze"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject.SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetAdvise"); + ActiveXInstance.SetAdvise(aspects, advf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject.GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAdvise"); + ActiveXInstance.GetAdvise(paspects, padvf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT lprcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Draw"); + Debug.Indent(); + ActiveXInstance.Draw(dwDrawAspect, lindex, pvAspect, ptd, hdcTargetDev, + hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.GetColorSet(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hicTargetDev, NativeMethods.tagLOGPALETTE ppColorSet) { + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetColorSet"); + + // GDI+ doesn't do palettes. + // + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.Freeze(int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr pdwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Freezes"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.Unfreeze(int dwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unfreeze"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetAdvise"); + ActiveXInstance.SetAdvise(aspects, advf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAdvise"); + ActiveXInstance.GetAdvise(paspects, padvf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.GetExtent(int dwDrawAspect, int lindex, NativeMethods.tagDVTARGETDEVICE ptd, NativeMethods.tagSIZEL lpsizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetExtent (IViewObject2)"); + // we already have an implementation of this [from IOleObject] + // + ((UnsafeNativeMethods.IOleObject)this).GetExtent(dwDrawAspect, lpsizel); + } + + #region IKeyboardToolTip implementation + + bool IKeyboardToolTip.CanShowToolTipsNow() { + IKeyboardToolTip host = this.ToolStripControlHost; + return this.IsHandleCreated && this.Visible && (host == null || host.CanShowToolTipsNow()); + } + + Rectangle IKeyboardToolTip.GetNativeScreenRectangle() { + return this.GetToolNativeScreenRectangle(); + } + + IList IKeyboardToolTip.GetNeighboringToolsRectangles() { + IKeyboardToolTip host = this.ToolStripControlHost; + if (host == null) { + return this.GetOwnNeighboringToolsRectangles(); + } + else { + return host.GetNeighboringToolsRectangles(); + } + } + + bool IKeyboardToolTip.IsHoveredWithMouse() { + return this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition)); + } + + bool IKeyboardToolTip.HasRtlModeEnabled() { + Control topLevelControl = TopLevelControlInternal; + return topLevelControl != null && topLevelControl.RightToLeft == RightToLeft.Yes && !this.IsMirrored; + } + + bool IKeyboardToolTip.AllowsToolTip() { + IKeyboardToolTip host = this.ToolStripControlHost; + return (host == null || host.AllowsToolTip()) && this.AllowsKeyboardToolTip(); + } + + IWin32Window IKeyboardToolTip.GetOwnerWindow() { + return this; + } + + void IKeyboardToolTip.OnHooked(ToolTip toolTip) { + this.OnKeyboardToolTipHook(toolTip); + } + + void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) { + this.OnKeyboardToolTipUnhook(toolTip); + } + + string IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) { + IKeyboardToolTip host = this.ToolStripControlHost; + if (host == null) { + return toolTip.GetCaptionForTool(this); + } + else { + return host.GetCaptionForTool(toolTip); + } + } + + bool IKeyboardToolTip.ShowsOwnToolTip() { + IKeyboardToolTip host = this.ToolStripControlHost; + return (host == null || host.ShowsOwnToolTip()) && this.ShowsOwnKeyboardToolTip(); + } + + bool IKeyboardToolTip.IsBeingTabbedTo() { + return Control.AreCommonNavigationalKeysDown(); + } + + bool IKeyboardToolTip.AllowsChildrenToShowToolTips() { + return this.AllowsChildrenToShowToolTips(); + } + + #endregion + + private IList GetOwnNeighboringToolsRectangles() { + Control controlParent = this.ParentInternal; + if (controlParent != null) { + Control[] neighboringControls = new Control[4] { + // Next and previous control which are accessible with Tab and Shift+Tab + controlParent.GetNextSelectableControl(this, true, true, true, false), + controlParent.GetNextSelectableControl(this, false, true, true, false), + // Next and previous control which are accessible with arrow keys + controlParent.GetNextSelectableControl(this, true, false, false, true), + controlParent.GetNextSelectableControl(this, false, false, false, true) + }; + + List neighboringControlsRectangles = new List(4); + foreach (Control neighboringControl in neighboringControls) { + if (neighboringControl != null && neighboringControl.IsHandleCreated) { + neighboringControlsRectangles.Add(((IKeyboardToolTip)neighboringControl).GetNativeScreenRectangle()); + } + } + + return neighboringControlsRectangles; + } + else { + return new Rectangle[0]; + } + } + + internal virtual bool ShowsOwnKeyboardToolTip() { + return true; + } + + internal virtual void OnKeyboardToolTipHook(ToolTip toolTip) { + } + + internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip) { + } + + internal virtual Rectangle GetToolNativeScreenRectangle() { + NativeMethods.RECT rectangle = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, this.Handle), ref rectangle); + return Rectangle.FromLTRB(rectangle.left, rectangle.top, rectangle.right, rectangle.bottom); + } + + internal virtual bool AllowsKeyboardToolTip() { + // This internal method enables keyboard ToolTips for all controls including the foreign descendants of Control unless this method is overridden in a child class belonging to this assembly. + // ElementHost is one such control which is located in a different assembly. + // This control doesn't show a mouse ToolTip when hovered and thus should not have a keyboard ToolTip as well. + // We are not going to fix it now since it seems unlikely that someone would set ToolTip on such special container control as ElementHost. + return true; + } + + private static bool IsKeyDown(Keys key) { + return (tempKeyboardStateArray[(int)key] & Control.HighOrderBitMask) != 0; + } + + internal static bool AreCommonNavigationalKeysDown() { + if(tempKeyboardStateArray == null) { + tempKeyboardStateArray = new byte[256]; + } + UnsafeNativeMethods.GetKeyboardState(tempKeyboardStateArray); + return IsKeyDown(Keys.Tab) + || IsKeyDown(Keys.Up) + || IsKeyDown(Keys.Down) + || IsKeyDown(Keys.Left) + || IsKeyDown(Keys.Right) + // receiving focus from the ToolStrip + || IsKeyDown(Keys.Menu) + || IsKeyDown(Keys.F10) + || IsKeyDown(Keys.Escape); + } + + private readonly WeakReference toolStripControlHostReference = new WeakReference(null); + + internal ToolStripControlHost ToolStripControlHost { + get { + ToolStripControlHost value; + this.toolStripControlHostReference.TryGetTarget(out value); + return value; + } + set { + this.toolStripControlHostReference.SetTarget(value); + } + } + + internal virtual bool AllowsChildrenToShowToolTips() { + return true; + } + + /// + /// This class holds all of the state data for an ActiveX control and + /// supplies the implementation for many of the non-trivial methods. + /// + /// + private class ActiveXImpl : MarshalByRefObject, IWindowTarget { + // SECUNDONE : This call is private and is only used to expose a WinForm control as + // : an ActiveX control. This class needs more review. + // + + private static readonly int hiMetricPerInch = 2540; + private static readonly int viewAdviseOnlyOnce = BitVector32.CreateMask(); + private static readonly int viewAdvisePrimeFirst = BitVector32.CreateMask(viewAdviseOnlyOnce); + private static readonly int eventsFrozen = BitVector32.CreateMask(viewAdvisePrimeFirst); + private static readonly int changingExtents = BitVector32.CreateMask(eventsFrozen); + private static readonly int saving = BitVector32.CreateMask(changingExtents); + private static readonly int isDirty = BitVector32.CreateMask(saving); + private static readonly int inPlaceActive = BitVector32.CreateMask(isDirty); + private static readonly int inPlaceVisible = BitVector32.CreateMask(inPlaceActive); + private static readonly int uiActive = BitVector32.CreateMask(inPlaceVisible); + private static readonly int uiDead = BitVector32.CreateMask(uiActive); + private static readonly int adjustingRect = BitVector32.CreateMask(uiDead); + + private static Point logPixels = Point.Empty; + private static NativeMethods.tagOLEVERB[] axVerbs; + + private static int globalActiveXCount = 0; + private static bool checkedIE; + private static bool isIE; + + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + private static ActiveXPropPage propPage; + + #endif + + private Control control; + private IWindowTarget controlWindowTarget; + private IntPtr clipRegion; + private UnsafeNativeMethods.IOleClientSite clientSite; + private UnsafeNativeMethods.IOleInPlaceUIWindow inPlaceUiWindow; + private UnsafeNativeMethods.IOleInPlaceFrame inPlaceFrame; + private ArrayList adviseList; + private IAdviseSink viewAdviseSink; + private BitVector32 activeXState; + private AmbientProperty[] ambientProperties; + private IntPtr hwndParent; + private IntPtr accelTable; + private short accelCount = -1; + private NativeMethods.COMRECT adjustRect; // temporary rect used during OnPosRectChange && SetObjectRects + + /// + /// + /// Creates a new ActiveXImpl. + /// + internal ActiveXImpl(Control control) { + this.control = control; + + // We replace the control's window target with our own. We + // do this so we can handle the UI Dead ambient property. + // + controlWindowTarget = control.WindowTarget; + control.WindowTarget = this; + + adviseList = new ArrayList(); + activeXState = new BitVector32(); + ambientProperties = new AmbientProperty[] { + new AmbientProperty("Font", NativeMethods.ActiveX.DISPID_AMBIENT_FONT), + new AmbientProperty("BackColor", NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR), + new AmbientProperty("ForeColor", NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR) + }; + } + + /// + /// + /// Retrieves the ambient back color for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Color AmbientBackColor { + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR, ref obj)) { + if (obj != null) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object color type=" + obj.GetType().FullName); + prop.Value = ColorTranslator.FromOle(Convert.ToInt32(obj, CultureInfo.InvariantCulture)); + } + catch (Exception e) { + Debug.Fail("Failed to massage ambient back color to a Color", e.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + } + } + } + + if (prop.Value == null) { + return Color.Empty; + } + else { + return(Color)prop.Value; + } + } + } + + /// + /// Retrieves the ambient font for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Font AmbientFont { + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_FONT, ref obj)) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object font type=" + obj.GetType().FullName); + Debug.Assert(obj != null, "GetAmbientProperty failed"); + IntPtr hfont = IntPtr.Zero; + + + UnsafeNativeMethods.IFont ifont = (UnsafeNativeMethods.IFont)obj; + IntSecurity.ObjectFromWin32Handle.Assert(); + Font font = null; + try { + hfont = ifont.GetHFont(); + font = Font.FromHfont(hfont); + } + finally { + CodeAccessPermission.RevertAssert(); + } + prop.Value = font; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Do NULL, so we just defer to the default font + prop.Value = null; + } + } + } + + return(Font)prop.Value; + } + } + + /// + /// Retrieves the ambient back color for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Color AmbientForeColor { + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR, ref obj)) { + if (obj != null) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object color type=" + obj.GetType().FullName); + prop.Value = ColorTranslator.FromOle(Convert.ToInt32(obj, CultureInfo.InvariantCulture)); + } + catch (Exception e) { + Debug.Fail("Failed to massage ambient fore color to a Color", e.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + } + } + } + + if (prop.Value == null) { + return Color.Empty; + } + else { + return(Color)prop.Value; + } + } + } + + /// + /// + /// Determines if events should be frozen. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal bool EventsFrozen { + get { + return activeXState[eventsFrozen]; + } + set { + activeXState[eventsFrozen] = value; + } + } + + /// + /// Provides access to the parent window handle + /// when we are UI active + /// + internal IntPtr HWNDParent { + get { + return hwndParent; + } + } + + /// + /// Returns true if this app domain is running inside of IE. The + /// control must be sited for this to succeed (it will assert and + /// return false if the control is not sited). + /// + internal bool IsIE { + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + if (!checkedIE) { + if (clientSite == null) { + Debug.Fail("Do not reference IsIE property unless control is sited."); + return false; + } + + // First, is this a managed EXE? If so, it will correctly shut down + // the runtime. + if (Assembly.GetEntryAssembly() == null) { + // Now check for IHTMLDocument2 + UnsafeNativeMethods.IOleContainer container; + + if (NativeMethods.Succeeded(clientSite.GetContainer(out container)) && container is NativeMethods.IHTMLDocument) { + isIE = true; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "AxSource:IsIE running under IE"); + } + + if (container != null && UnsafeNativeMethods.IsComObject(container)) { + UnsafeNativeMethods.ReleaseComObject(container); + } + } + + checkedIE = true; + } + + return isIE; + } + } + + /// + /// Retrieves the number of logical pixels per inch on the + /// primary monitor. + /// + private Point LogPixels { + get { + if (logPixels.IsEmpty) { + logPixels = new Point(); + IntPtr dc = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + logPixels.X = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX); + logPixels.Y = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); + } + return logPixels; + } + } + + /// + /// + /// Implements IOleObject::Advise + /// + internal int Advise(IAdviseSink pAdvSink) { + adviseList.Add(pAdvSink); + return adviseList.Count; + } + + /// + /// + /// Implements IOleObject::Close + /// + internal void Close(int dwSaveOption) { + if (activeXState[inPlaceActive]) { + InPlaceDeactivate(); + } + + if ((dwSaveOption == NativeMethods.OLECLOSE_SAVEIFDIRTY || + dwSaveOption == NativeMethods.OLECLOSE_PROMPTSAVE) && + activeXState[isDirty]) { + + if (clientSite != null) { + clientSite.SaveObject(); + } + SendOnSave(); + } + } + + /// + /// + /// Implements IOleObject::DoVerb + /// + internal void DoVerb(int iVerb, IntPtr lpmsg, UnsafeNativeMethods.IOleClientSite pActiveSite, int lindex, IntPtr hwndParent, NativeMethods.COMRECT lprcPosRect) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "AxSource:ActiveXImpl:DoVerb(" + iVerb + ")"); + switch (iVerb) { + case NativeMethods.OLEIVERB_SHOW: + case NativeMethods.OLEIVERB_INPLACEACTIVATE: + case NativeMethods.OLEIVERB_UIACTIVATE: + case NativeMethods.OLEIVERB_PRIMARY: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Show, InPlaceActivate, UIActivate"); + InPlaceActivate(iVerb); + + // Now that we're active, send the lpmsg to the control if it + // is valid. + // + if (lpmsg != IntPtr.Zero) { + NativeMethods.MSG msg = (NativeMethods.MSG)UnsafeNativeMethods.PtrToStructure(lpmsg, typeof(NativeMethods.MSG)); + Control target = control; + + if (msg.hwnd != control.Handle && msg.message >= NativeMethods.WM_MOUSEFIRST && msg.message <= NativeMethods.WM_MOUSELAST) { + + // Must translate message coordniates over to our HWND. We first try + // + IntPtr hwndMap = msg.hwnd == IntPtr.Zero ? hwndParent : msg.hwnd; + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = NativeMethods.Util.LOWORD(msg.lParam); + pt.y = NativeMethods.Util.HIWORD(msg.lParam); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(null, hwndMap), new HandleRef(control, control.Handle), pt, 1); + + // check to see if this message should really go to a child + // control, and if so, map the point into that child's window + // coordinates + // + Control realTarget = target.GetChildAtPoint(new Point(pt.x, pt.y)); + if (realTarget != null && realTarget != target) { + UnsafeNativeMethods.MapWindowPoints(new HandleRef(target, target.Handle), new HandleRef(realTarget, realTarget.Handle), pt, 1); + target = realTarget; + } + + msg.lParam = NativeMethods.Util.MAKELPARAM(pt.x, pt.y); + } + +#if DEBUG + if (CompModSwitches.ActiveX.TraceVerbose) { + Message m = Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam); + Debug.WriteLine("Valid message pointer passed, sending to control: " + m.ToString()); + } +#endif // DEBUG + + if (msg.message == NativeMethods.WM_KEYDOWN && msg.wParam == (IntPtr)NativeMethods.VK_TAB) { + target.SelectNextControl(null, Control.ModifierKeys != Keys.Shift, true, true, true); + } + else { + target.SendMessage(msg.message, msg.wParam, msg.lParam); + } + } + break; + + // These affect our visibility + // + case NativeMethods.OLEIVERB_HIDE: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Hide"); + UIDeactivate(); + InPlaceDeactivate(); + if (activeXState[inPlaceVisible]) { + SetInPlaceVisible(false); + } + break; + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + // Show our property pages + // + case NativeMethods.OLEIVERB_PROPERTIES: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Primary, Properties"); + ShowProperties(); + break; + + #endif + + // All other verbs are notimpl. + // + default: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Other"); + ThrowHr(NativeMethods.E_NOTIMPL); + break; + } + } + + /// + /// + /// Implements IViewObject2::Draw. + /// + internal void Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT prcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + + // support the aspects required for multi-pass drawing + // + switch (dwDrawAspect) { + case NativeMethods.DVASPECT_CONTENT: + case NativeMethods.DVASPECT_OPAQUE: + case NativeMethods.DVASPECT_TRANSPARENT: + break; + default: + ThrowHr(NativeMethods.DV_E_DVASPECT); + break; + } + + // We can paint to an enhanced metafile, but not all GDI / GDI+ is + // supported on classic metafiles. We throw VIEW_E_DRAW in the hope that + // the caller figures it out and sends us a different DC. + // + int hdcType = UnsafeNativeMethods.GetObjectType(new HandleRef(null, hdcDraw)); + if (hdcType == NativeMethods.OBJ_METADC) { + ThrowHr(NativeMethods.VIEW_E_DRAW); + } + + NativeMethods.RECT rc; + NativeMethods.POINT pVp = new NativeMethods.POINT(); + NativeMethods.POINT pW = new NativeMethods.POINT(); + NativeMethods.SIZE sWindowExt = new NativeMethods.SIZE(); + NativeMethods.SIZE sViewportExt = new NativeMethods.SIZE(); + int iMode = NativeMethods.MM_TEXT; + + if (!control.IsHandleCreated) { + control.CreateHandle(); + } + + // if they didn't give us a rectangle, just copy over ours + // + if (prcBounds != null) { + rc = new NativeMethods.RECT(prcBounds.left, prcBounds.top, prcBounds.right, prcBounds.bottom); + + // To draw to a given rect, we scale the DC in such a way as to + // make the values it takes match our own happy MM_TEXT. Then, + // we back-convert prcBounds so that we convert it to this coordinate + // system. This puts us in the most similar coordinates as we currently + // use. + // + SafeNativeMethods.LPtoDP(new HandleRef(null, hdcDraw), ref rc, 2); + + iMode = SafeNativeMethods.SetMapMode(new HandleRef(null, hdcDraw), NativeMethods.MM_ANISOTROPIC); + SafeNativeMethods.SetWindowOrgEx(new HandleRef(null, hdcDraw), 0, 0, pW); + SafeNativeMethods.SetWindowExtEx(new HandleRef(null, hdcDraw), control.Width, control.Height, sWindowExt); + SafeNativeMethods.SetViewportOrgEx(new HandleRef(null, hdcDraw), rc.left, rc.top, pVp); + SafeNativeMethods.SetViewportExtEx(new HandleRef(null, hdcDraw), rc.right - rc.left, rc.bottom - rc.top, sViewportExt); + } + + // Now do the actual drawing. We must ask all of our children to draw as well. + try { + IntPtr flags = (IntPtr)(NativeMethods.PRF_CHILDREN + | NativeMethods.PRF_CLIENT + | NativeMethods.PRF_ERASEBKGND + | NativeMethods.PRF_NONCLIENT); + //| NativeMethods.PRF_CHECKVISIBLE + //| NativeMethods.PRF_OWNED)); + + if(hdcType != NativeMethods.OBJ_ENHMETADC) { + control.SendMessage(NativeMethods.WM_PRINT, hdcDraw, flags); + } else { + control.PrintToMetaFile(new HandleRef(null, hdcDraw), flags); + } + } + finally { + // And clean up the DC + // + if (prcBounds != null) { + SafeNativeMethods.SetWindowOrgEx(new HandleRef(null, hdcDraw), pW.x, pW.y, null); + SafeNativeMethods.SetWindowExtEx(new HandleRef(null, hdcDraw), sWindowExt.cx, sWindowExt.cy, null); + SafeNativeMethods.SetViewportOrgEx(new HandleRef(null, hdcDraw), pVp.x, pVp.y, null); + SafeNativeMethods.SetViewportExtEx(new HandleRef(null, hdcDraw), sViewportExt.cx, sViewportExt.cy, null); + SafeNativeMethods.SetMapMode(new HandleRef(null, hdcDraw), iMode); + } + } + } + + /// + /// + /// Returns a new verb enumerator. + /// + internal static int EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e) { + if (axVerbs == null) { + NativeMethods.tagOLEVERB verbShow = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbInplaceActivate = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbUIActivate = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbHide = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbPrimary = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbProperties = new NativeMethods.tagOLEVERB(); + + verbShow.lVerb = NativeMethods.OLEIVERB_SHOW; + verbInplaceActivate.lVerb = NativeMethods.OLEIVERB_INPLACEACTIVATE; + verbUIActivate.lVerb = NativeMethods.OLEIVERB_UIACTIVATE; + verbHide.lVerb = NativeMethods.OLEIVERB_HIDE; + verbPrimary.lVerb = NativeMethods.OLEIVERB_PRIMARY; + verbProperties.lVerb = NativeMethods.OLEIVERB_PROPERTIES; + verbProperties.lpszVerbName = SR.GetString(SR.AXProperties); + verbProperties.grfAttribs = NativeMethods.ActiveX.OLEVERBATTRIB_ONCONTAINERMENU; + + axVerbs = new NativeMethods.tagOLEVERB[] { + verbShow, + verbInplaceActivate, + verbUIActivate, + verbHide, + verbPrimary + #if ACTIVEX_SOURCING + ,verbProperties + #endif + }; + } + + + e = new ActiveXVerbEnum(axVerbs); + return NativeMethods.S_OK; + } + + /// + /// Converts the given string to a byte array. + /// + private static byte[] FromBase64WrappedString(string text) { + if (text.IndexOfAny(new char[] {' ', '\r', '\n'}) != -1) { + StringBuilder sb = new StringBuilder(text.Length); + for (int i=0; i + /// Implements IViewObject2::GetAdvise. + /// + internal void GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + // if they want it, give it to them + // + if (paspects != null) { + paspects[0] = NativeMethods.DVASPECT_CONTENT; + } + + if (padvf != null) { + padvf[0] = 0; + + if (activeXState[viewAdviseOnlyOnce]) padvf[0] |= NativeMethods.ADVF_ONLYONCE; + if (activeXState[viewAdvisePrimeFirst]) padvf[0] |= NativeMethods.ADVF_PRIMEFIRST; + } + + if (pAdvSink != null) { + pAdvSink[0] = viewAdviseSink; + } + } + + /// + /// Helper function to retrieve an ambient property. Returns false if the + /// property wasn't found. + /// + private bool GetAmbientProperty(int dispid, ref Object obj) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAmbientProperty"); + Debug.Indent(); + + if (clientSite is UnsafeNativeMethods.IDispatch) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "clientSite implements IDispatch"); + + UnsafeNativeMethods.IDispatch disp = (UnsafeNativeMethods.IDispatch)clientSite; + Object[] pvt = new Object[1]; + Guid g = Guid.Empty; + int hr = NativeMethods.E_FAIL; + + IntSecurity.UnmanagedCode.Assert(); + try { + hr = disp.Invoke(dispid, ref g, NativeMethods.LOCALE_USER_DEFAULT, + NativeMethods.DISPATCH_PROPERTYGET, new NativeMethods.tagDISPPARAMS(), + pvt, null, null); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (NativeMethods.Succeeded(hr)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "IDispatch::Invoke succeeded. VT=" + pvt[0].GetType().FullName); + obj = pvt[0]; + Debug.Unindent(); + return true; + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "IDispatch::Invoke failed. HR: 0x" + String.Format(CultureInfo.CurrentCulture, "{0:X}", hr)); + } + } + + Debug.Unindent(); + return false; + } + + /// + /// Implements IOleObject::GetClientSite. + /// + internal UnsafeNativeMethods.IOleClientSite GetClientSite() { + return clientSite; + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal int GetControlInfo(NativeMethods.tagCONTROLINFO pCI) { + if (accelCount == -1) { + ArrayList mnemonicList = new ArrayList(); + GetMnemonicList(control, mnemonicList); + + accelCount = (short)mnemonicList.Count; + + if (accelCount > 0) { + int accelSize = UnsafeNativeMethods.SizeOf(typeof(NativeMethods.ACCEL)); + + // In the worst case we may have two accelerators per mnemonic: one lower case and + // one upper case, hence the * 2 below. + // + IntPtr accelBlob = Marshal.AllocHGlobal(accelSize * accelCount * 2); + + try { + NativeMethods.ACCEL accel = new NativeMethods.ACCEL(); + accel.cmd = 0; + + Debug.Indent(); + + accelCount = 0; + + foreach(char ch in mnemonicList) { + IntPtr structAddr = (IntPtr)((long)accelBlob + accelCount * accelSize); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Mnemonic: " + ch.ToString()); + + if (ch >= 'A' && ch <= 'Z') { + + // Lower case letter + // + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY; + accel.key = (short)(UnsafeNativeMethods.VkKeyScan(ch) & 0x00FF); + Marshal.StructureToPtr(accel, structAddr, false); + + // Upper case letter + // + accelCount++; + structAddr = (IntPtr)((long)structAddr + accelSize); + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY | NativeMethods.FSHIFT; + Marshal.StructureToPtr(accel, structAddr, false); + } + else { + // Some non-printable character. + // + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY; + short scan = (short)(UnsafeNativeMethods.VkKeyScan(ch)); + if ((scan & 0x0100) != 0) { + accel.fVirt |= NativeMethods.FSHIFT; + } + accel.key = (short)(scan & 0x00FF); + Marshal.StructureToPtr(accel, structAddr, false); + } + + accel.cmd++; + accelCount++; + } + + Debug.Unindent(); + + // Now create an accelerator table and then free our memory. + // + + // DevDiv Bugs 170945 + if (accelTable != IntPtr.Zero) { + UnsafeNativeMethods.DestroyAcceleratorTable(new HandleRef(this, accelTable)); + accelTable = IntPtr.Zero; + } + + accelTable = UnsafeNativeMethods.CreateAcceleratorTable(new HandleRef(null, accelBlob), accelCount); + } + finally { + if (accelBlob != IntPtr.Zero) { + Marshal.FreeHGlobal(accelBlob); + } + } + } + } + + pCI.cAccel = accelCount; + pCI.hAccel = accelTable; + return NativeMethods.S_OK; + } + + /// + /// Implements IOleObject::GetExtent. + /// + internal void GetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + if ((dwDrawAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + Size size = control.Size; + + Point pt = PixelToHiMetric(size.Width, size.Height); + pSizel.cx = pt.X; + pSizel.cy = pt.Y; + } + else { + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + } + + /// + /// Searches the control hierarchy of the given control and adds + /// the mnemonics for each control to mnemonicList. Each mnemonic + /// is added as a char to the list. + /// + private void GetMnemonicList(Control control, ArrayList mnemonicList) { + // Get the mnemonic for our control + // + char mnemonic = WindowsFormsUtils.GetMnemonic(control.Text, true); + if (mnemonic != 0) { + mnemonicList.Add(mnemonic); + } + + // And recurse for our children. + // + foreach(Control c in control.Controls) { + if (c != null) GetMnemonicList(c, mnemonicList); + } + } + + /// + /// Name to use for a stream: use the control's type name (max 31 chars, use the end chars + /// if it's longer than that) + /// + private string GetStreamName() + { + string streamName = control.GetType().FullName; + int len = streamName.Length; + if (len > 31) + { // The max allowed length of the stream name is 31. + streamName = streamName.Substring(len - 31); + } + return streamName; + } + + /// + /// Implements IOleWindow::GetWindow + /// + internal int GetWindow(out IntPtr hwnd) { + if (!activeXState[inPlaceActive]) { + hwnd = IntPtr.Zero; + return NativeMethods.E_FAIL; + } + hwnd = control.Handle; + return NativeMethods.S_OK; + } + + /// + /// Converts coordinates in HiMetric to pixels. Used for ActiveX sourcing. + /// + /// + private Point HiMetricToPixel(int x, int y) { + Point pt = new Point(); + pt.X = (LogPixels.X * x + hiMetricPerInch / 2) / hiMetricPerInch; + pt.Y = (LogPixels.Y * y + hiMetricPerInch / 2) / hiMetricPerInch; + return pt; + } + + /// + /// + /// In place activates this Object. + /// + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal void InPlaceActivate(int verb) + { + // If we don't have a client site, then there's not much to do. + // We also punt if this isn't an in-place site, since we can't + // go active then. + // + UnsafeNativeMethods.IOleInPlaceSite inPlaceSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (inPlaceSite == null) + { + return; + } + + // If we're not already active, go and do it. + // + if (!activeXState[inPlaceActive]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> inplaceactive"); + + int hr = inPlaceSite.CanInPlaceActivate(); + + if (hr != NativeMethods.S_OK) { + if (NativeMethods.Succeeded(hr)) { + hr = NativeMethods.E_FAIL; + } + ThrowHr(hr); + } + + inPlaceSite.OnInPlaceActivate(); + + activeXState[inPlaceActive] = true; + } + + + // And if we're not visible, do that too. + // + if (!activeXState[inPlaceVisible]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> inplacevisible"); + NativeMethods.tagOIFI inPlaceFrameInfo = new NativeMethods.tagOIFI(); + inPlaceFrameInfo.cb = UnsafeNativeMethods.SizeOf(typeof(NativeMethods.tagOIFI)); + IntPtr hwndParent = IntPtr.Zero; + + // We are entering a secure context here. + // + hwndParent = inPlaceSite.GetWindow(); + + UnsafeNativeMethods.IOleInPlaceFrame pFrame; + UnsafeNativeMethods.IOleInPlaceUIWindow pWindow; + NativeMethods.COMRECT posRect = new NativeMethods.COMRECT(); + NativeMethods.COMRECT clipRect = new NativeMethods.COMRECT(); + + if (inPlaceUiWindow != null && UnsafeNativeMethods.IsComObject(inPlaceUiWindow)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceUiWindow); + inPlaceUiWindow = null; + } + + if (inPlaceFrame != null && UnsafeNativeMethods.IsComObject(inPlaceFrame)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceFrame); + inPlaceFrame = null; + } + + inPlaceSite.GetWindowContext(out pFrame, out pWindow, posRect, clipRect, inPlaceFrameInfo); + + SetObjectRects(posRect, clipRect); + + inPlaceFrame = pFrame; + inPlaceUiWindow = pWindow; + + // We are parenting ourselves + // directly to the host window. The host must + // implement the ambient property + // DISPID_AMBIENT_MESSAGEREFLECT. + // If it doesn't, that means that the host + // won't reflect messages back to us. + // + this.hwndParent = hwndParent; + UnsafeNativeMethods.SetParent(new HandleRef(control, control.Handle), new HandleRef(null, hwndParent)); + + // Now create our handle if it hasn't already been done. Note that because + // this raises events to the user that it CANNOT be done with a security assertion + // in place! + // + control.CreateControl(); + + clientSite.ShowObject(); + + SetInPlaceVisible(true); + Debug.Assert(activeXState[inPlaceVisible], "Failed to set inplacevisible"); + } + + // if we weren't asked to UIActivate, then we're done. + // + if (verb != NativeMethods.OLEIVERB_PRIMARY && verb != NativeMethods.OLEIVERB_UIACTIVATE) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> not becoming UIActive"); + return; + } + + // if we're not already UI active, do sow now. + // + if (!activeXState[uiActive]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> uiactive"); + activeXState[uiActive] = true; + + // inform the container of our intent + // + inPlaceSite.OnUIActivate(); + + // take the focus [which is what UI Activation is all about !] + // + if (!control.ContainsFocus) control.FocusInternal(); + + // set ourselves up in the host. + // + Debug.Assert(inPlaceFrame != null, "Setting us to visible should have created the in place frame"); + inPlaceFrame.SetActiveObject(control, null); + if (inPlaceUiWindow != null) { + inPlaceUiWindow.SetActiveObject(control, null); + } + + // we have to explicitly say we don't wany any border space. + // + int hr = inPlaceFrame.SetBorderSpace(null); + if (NativeMethods.Failed(hr) && hr != NativeMethods.OLE_E_INVALIDRECT && + hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + + if (inPlaceUiWindow != null) { + hr = inPlaceFrame.SetBorderSpace(null); + if (NativeMethods.Failed(hr) && hr != NativeMethods.OLE_E_INVALIDRECT && + hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + } + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> already uiactive"); + } + } + + /// + /// + /// Implements IOleInPlaceObject::InPlaceDeactivate. + /// + internal void InPlaceDeactivate() { + + // Only do this if we're already in place active. + // + if (!activeXState[inPlaceActive]) { + return; + } + + // Deactivate us if we're UI active + // + if (activeXState[uiActive]) { + UIDeactivate(); + } + + // Some containers may call into us to save, and if we're still + // active we will try to deactivate and recurse back into the container. + // So, set the state bits here first. + // + activeXState[inPlaceActive] = false; + activeXState[inPlaceVisible] = false; + + // Notify our site of our deactivation. + // + UnsafeNativeMethods.IOleInPlaceSite oleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (oleClientSite != null) { + IntSecurity.UnmanagedCode.Assert(); + try { + oleClientSite.OnInPlaceDeactivate(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + control.Visible = false; + hwndParent = IntPtr.Zero; + + if (inPlaceUiWindow != null && UnsafeNativeMethods.IsComObject(inPlaceUiWindow)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceUiWindow); + inPlaceUiWindow = null; + } + + if (inPlaceFrame != null && UnsafeNativeMethods.IsComObject(inPlaceFrame)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceFrame); + inPlaceFrame = null; + } + } + + /// + /// Implements IPersistStreamInit::IsDirty. + /// + internal int IsDirty() { + if (activeXState[isDirty]) { + return NativeMethods.S_OK; + } + else { + return NativeMethods.S_FALSE; + } + } + + /// + /// Looks at the property to see if it should be loaded / saved as a resource or + /// through a type converter. + /// + private bool IsResourceProp(PropertyDescriptor prop) { + TypeConverter converter = prop.Converter; + Type[] convertTypes = new Type[] { + typeof(string), + typeof(byte[]) + }; + + foreach (Type t in convertTypes) { + if (converter.CanConvertTo(t) && converter.CanConvertFrom(t)) { + return false; + } + } + + // Finally, if the property can be serialized, it is a resource property. + // + return (prop.GetValue(control) is ISerializable); + } + + /// + /// + /// Implements IPersistStorage::Load + /// + internal void Load(UnsafeNativeMethods.IStorage stg) { + UnsafeNativeMethods.IStream stream = null; + + IntSecurity.UnmanagedCode.Assert(); + try { + stream = stg.OpenStream(this.GetStreamName(), IntPtr.Zero, NativeMethods.STGM_READ | NativeMethods.STGM_SHARE_EXCLUSIVE, 0); + } + catch (COMException e) { + if (e.ErrorCode == NativeMethods.STG_E_FILENOTFOUND) { + // For backward compatibility: We were earlier using GetType().FullName + // as the stream name in v1. Lets see if a stream by that name exists. + stream = stg.OpenStream(GetType().FullName, IntPtr.Zero, NativeMethods.STGM_READ | NativeMethods.STGM_SHARE_EXCLUSIVE, 0); + } + else { + throw; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + Load(stream); + stream = null; + if (UnsafeNativeMethods.IsComObject(stg)) { + UnsafeNativeMethods.ReleaseComObject(stg); + } + } + + /// + /// + /// Implements IPersistStreamInit::Load + /// + internal void Load(UnsafeNativeMethods.IStream stream) { + // We do everything through property bags because we support full fidelity + // in them. So, load through that method. + // + PropertyBagStream bag = new PropertyBagStream(); + bag.Read(stream); + Load(bag, null); + + if (UnsafeNativeMethods.IsComObject(stream)) { + UnsafeNativeMethods.ReleaseComObject(stream); + } + } + + /// + /// Implements IPersistPropertyBag::Load + /// + internal void Load(UnsafeNativeMethods.IPropertyBag pPropBag, UnsafeNativeMethods.IErrorLog pErrorLog) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(control, + new Attribute[] {DesignerSerializationVisibilityAttribute.Visible}); + + for (int i = 0; i < props.Count; i++) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Loading property " + props[i].Name); + + try { + object obj = null; + int hr = NativeMethods.E_FAIL; + + IntSecurity.UnmanagedCode.Assert(); + try { + hr = pPropBag.Read(props[i].Name, ref obj, pErrorLog); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (NativeMethods.Succeeded(hr) && obj != null) { + Debug.Indent(); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Property was in bag"); + + String errorString = null; + int errorCode = 0; + + try { + if (obj.GetType() != typeof(string)) { + Debug.Fail("Expected property " + props[i].Name + " to be stored in IPropertyBag as a string. Attempting to coerce"); + obj = Convert.ToString(obj, CultureInfo.InvariantCulture); + } + + // Determine if this is a resource property or was persisted via a type converter. + // + if (IsResourceProp(props[i])) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "It's a resource property"); + + // Resource property. We encode these as base 64 strings. To load them, we convert + // to a binary blob and then de-serialize. + // + byte[] bytes = Convert.FromBase64String(obj.ToString()); + MemoryStream stream = new MemoryStream(bytes); + BinaryFormatter formatter = new BinaryFormatter(); + props[i].SetValue(control, formatter.Deserialize(stream)); + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "It's a standard property"); + + // Not a resource property. Use TypeConverters to convert the string back to the data type. We do + // not check for CanConvertFrom here -- we the conversion fails the type converter will throw, + // and we will log it into the COM error log. + // + TypeConverter converter = props[i].Converter; + Debug.Assert(converter != null, "No type converter for property '" + props[i].Name + "' on class " + control.GetType().FullName); + + // Check to see if the type converter can convert from a string. If it can,. + // use that as it is the best format for IPropertyBag. Otherwise, check to see + // if it can convert from a byte array. If it can, get the string, decode it + // to a byte array, and then set the value. + // + object value = null; + + if (converter.CanConvertFrom(typeof(string))) { + value = converter.ConvertFromInvariantString(obj.ToString()); + } + else if (converter.CanConvertFrom(typeof(byte[]))) { + string objString = obj.ToString(); + value = converter.ConvertFrom(null, CultureInfo.InvariantCulture, FromBase64WrappedString(objString)); + } + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Converter returned " + value); + props[i].SetValue(control, value); + } + } + catch (Exception e) { + errorString = e.ToString(); + if (e is ExternalException) { + errorCode = ((ExternalException) e).ErrorCode; + } + else { + errorCode = NativeMethods.E_FAIL; + } + } + if (errorString != null) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Exception converting property: " + errorString); + if (pErrorLog != null) { + NativeMethods.tagEXCEPINFO err = new NativeMethods.tagEXCEPINFO(); + err.bstrSource = control.GetType().FullName; + err.bstrDescription = errorString; + err.scode = errorCode; + pErrorLog.AddError(props[i].Name, err); + } + } + Debug.Unindent(); + } + } + catch (Exception ex) { + Debug.Fail("Unexpected failure reading property", ex.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + if (UnsafeNativeMethods.IsComObject(pPropBag)) { + UnsafeNativeMethods.ReleaseComObject(pPropBag); + } + } + + /// + /// Simple lookup to find the AmbientProperty corresponding to the given + /// dispid. + /// + private AmbientProperty LookupAmbient(int dispid) { + for (int i = 0; i < ambientProperties.Length; i++) { + if (ambientProperties[i].DispID == dispid) { + return ambientProperties[i]; + } + } + Debug.Fail("No ambient property for dispid " + dispid.ToString(CultureInfo.InvariantCulture)); + return ambientProperties[0]; + } + + /// + /// Merges the input region with the current clipping region. + /// The output is always a region that can be fed directly + /// to SetWindowRgn. The region does not have to be destroyed. + /// The original region is destroyed if a new region is returned. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal IntPtr MergeRegion(IntPtr region) { + if (clipRegion == IntPtr.Zero) { + return region; + } + + if (region == IntPtr.Zero) { + return clipRegion; + } + + try { + // + + IntPtr newRegion = SafeNativeMethods.CreateRectRgn(0, 0, 0, 0); + try { + SafeNativeMethods.CombineRgn(new HandleRef(null, newRegion), new HandleRef(null, region), new HandleRef(this, clipRegion), NativeMethods.RGN_DIFF); + SafeNativeMethods.DeleteObject(new HandleRef(null, region)); + } + catch { + SafeNativeMethods.DeleteObject(new HandleRef(null, newRegion)); + throw; + } + return newRegion; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // If something goes wrong, use the original region. + return region; + } + } + + private void CallParentPropertyChanged(Control control, string propName) { + switch (propName) { + case "BackColor" : + control.OnParentBackColorChanged(EventArgs.Empty); + break; + + case "BackgroundImage" : + control.OnParentBackgroundImageChanged(EventArgs.Empty); + break; + + case "BindingContext" : + control.OnParentBindingContextChanged(EventArgs.Empty); + break; + + case "Enabled" : + control.OnParentEnabledChanged(EventArgs.Empty); + break; + + case "Font" : + control.OnParentFontChanged(EventArgs.Empty); + break; + + case "ForeColor" : + control.OnParentForeColorChanged(EventArgs.Empty); + break; + + case "RightToLeft" : + control.OnParentRightToLeftChanged(EventArgs.Empty); + break; + + case "Visible" : + control.OnParentVisibleChanged(EventArgs.Empty); + break; + + default: + Debug.Fail("There is no property change notification for: " + propName + " on Control."); + break; + } + } + + /// + /// + /// Implements IOleControl::OnAmbientPropertyChanged + /// + internal void OnAmbientPropertyChange(int dispID) { + if (dispID != NativeMethods.ActiveX.DISPID_UNKNOWN) { + + // Look for a specific property that has changed. + // + for (int i = 0; i < ambientProperties.Length; i++) { + if (ambientProperties[i].DispID == dispID) { + ambientProperties[i].ResetValue(); + CallParentPropertyChanged(control, ambientProperties[i].Name); + return; + } + } + + // Special properties that we care about + // + Object obj = new Object(); + + switch (dispID) { + case NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD: + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + activeXState[uiDead] = (bool)obj; + } + break; + + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT: + IButtonControl ibuttonControl = control as IButtonControl; + if (ibuttonControl != null && GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT, ref obj)) { + ibuttonControl.NotifyDefault((bool)obj); + } + break; + } + } + else { + // Invalidate all properties. Ideally we should be checking each one, but + // that's pretty expensive too. + // + for (int i = 0; i < ambientProperties.Length; i++) { + ambientProperties[i].ResetValue(); + CallParentPropertyChanged(control, ambientProperties[i].Name); + } + } + } + + /// + /// + /// Implements IOleInPlaceActiveObject::OnDocWindowActivate. + /// + internal void OnDocWindowActivate(int fActivate) { + if (activeXState[uiActive] && fActivate != 0 && inPlaceFrame != null) { + // we have to explicitly say we don't wany any border space. + // + int hr; + IntSecurity.UnmanagedCode.Assert(); + try { + hr = inPlaceFrame.SetBorderSpace(null); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (NativeMethods.Failed(hr) && hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + } + } + + /// + /// Called by Control when it gets the focus. + /// + internal void OnFocus(bool focus) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AXSource: SetFocus: " + focus.ToString()); + if (activeXState[inPlaceActive] && clientSite is UnsafeNativeMethods.IOleControlSite) { + IntSecurity.UnmanagedCode.Assert(); + try { + ((UnsafeNativeMethods.IOleControlSite)clientSite).OnFocus(focus ? 1 : 0); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + if (focus && activeXState[inPlaceActive] && !activeXState[uiActive]) { + InPlaceActivate(NativeMethods.OLEIVERB_UIACTIVATE); + } + } + + /// + /// Converts coordinates in pixels to HiMetric. + /// + /// + private Point PixelToHiMetric(int x, int y) { + Point pt = new Point(); + pt.X = (hiMetricPerInch * x + (LogPixels.X >> 1)) / LogPixels.X; + pt.Y = (hiMetricPerInch * y + (LogPixels.Y >> 1)) / LogPixels.Y; + return pt; + } + + /// + /// + /// Our implementation of IQuickActivate::QuickActivate + /// + internal void QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl) { + // Hookup our ambient colors + // + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + prop.Value = ColorTranslator.FromOle(unchecked((int)pQaContainer.colorBack)); + + prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + prop.Value = ColorTranslator.FromOle(unchecked((int)pQaContainer.colorFore)); + + // And our ambient font + // + if (pQaContainer.pFont != null) { + + prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + + IntSecurity.UnmanagedCode.Assert(); + try + { + IntPtr hfont = IntPtr.Zero; + object objfont = pQaContainer.pFont; + UnsafeNativeMethods.IFont ifont = (UnsafeNativeMethods.IFont)objfont; + hfont = ifont.GetHFont(); + Font font = Font.FromHfont(hfont); + prop.Value = font; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Do NULL, so we just defer to the default font + prop.Value = null; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + // Now use the rest of the goo that we got passed in. + // + int status; + + pQaControl.cbSize = UnsafeNativeMethods.SizeOf(typeof(UnsafeNativeMethods.tagQACONTROL)); + + SetClientSite(pQaContainer.pClientSite); + + if (pQaContainer.pAdviseSink != null) { + SetAdvise(NativeMethods.DVASPECT_CONTENT, 0, (IAdviseSink)pQaContainer.pAdviseSink); + } + + IntSecurity.UnmanagedCode.Assert(); + try { + ((UnsafeNativeMethods.IOleObject)control).GetMiscStatus(NativeMethods.DVASPECT_CONTENT, out status); + } + finally { + CodeAccessPermission.RevertAssert(); + } + pQaControl.dwMiscStatus = status; + + // VSWhidbey 591430. Advise the event sink so VB6 can catch events raised from UserControls. + // VB6 expects the control to do this during IQuickActivate, otherwise it will not hook events at runtime. + // We will do this if all of the following are true: + // 1. The container (e.g., vb6) has supplied an event sink + // 2. The control is a UserControl (this is only to limit the scope of the changed behavior) + // 3. The UserControl has indicated it wants to expose events to COM via the ComSourceInterfacesAttribute + // Note that the AdviseHelper handles some non-standard COM interop that is required in order to access + // the events on the CLR-supplied CCW (COM-callable Wrapper. + + if ((pQaContainer.pUnkEventSink != null) && (control is UserControl)) { + // Check if this control exposes events to COM. + Type eventInterface = GetDefaultEventsInterface(control.GetType()); + + if (eventInterface != null) { + + IntSecurity.UnmanagedCode.Assert(); + try { + // For the default source interface, call IConnectionPoint.Advise with the supplied event sink. + // This is easier said than done. See notes in AdviseHelper.AdviseConnectionPoint. + AdviseHelper.AdviseConnectionPoint(control, pQaContainer.pUnkEventSink, eventInterface, out pQaControl.dwEventCookie); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + if (pQaContainer.pPropertyNotifySink != null && UnsafeNativeMethods.IsComObject(pQaContainer.pPropertyNotifySink)) { + UnsafeNativeMethods.ReleaseComObject(pQaContainer.pPropertyNotifySink); + } + + if (pQaContainer.pUnkEventSink != null && UnsafeNativeMethods.IsComObject(pQaContainer.pUnkEventSink)) { + UnsafeNativeMethods.ReleaseComObject(pQaContainer.pUnkEventSink); + } + } + + /// + /// Helper class. Calls IConnectionPoint.Advise to hook up a native COM event sink + /// to a manage .NET event interface. + /// The events are exposed to COM by the CLR-supplied COM-callable Wrapper (CCW). + /// + internal static class AdviseHelper { + /// + /// Get the COM connection point container from the CLR's CCW and advise for the given event id. + /// + public static bool AdviseConnectionPoint(object connectionPoint, object sink, Type eventInterface, out int cookie) { + + // Note that we cannot simply cast the connectionPoint object to + // System.Runtime.InteropServices.ComTypes.IConnectionPointContainer because the .NET + // object doesn't implement it directly. When the object is exposed to COM, the CLR + // implements IConnectionPointContainer on the proxy object called the CCW or COM-callable wrapper. + // We use the helper class ComConnectionPointContainer to get to the CCW directly + // to to call the interface. + // It is critical to call Dispose to ensure that the IUnknown is released. + + using (ComConnectionPointContainer cpc = new ComConnectionPointContainer(connectionPoint, true)) { + return AdviseConnectionPoint(cpc, sink, eventInterface, out cookie); + } + } + + /// + /// Find the COM connection point and call Advise for the given event id. + /// + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")] + internal static bool AdviseConnectionPoint(ComConnectionPointContainer cpc, object sink, Type eventInterface, out int cookie) { + + // Note that we cannot simply cast the returned IConnectionPoint to + // System.Runtime.InteropServices.ComTypes.IConnectionPoint because the .NET + // object doesn't implement it directly. When the object is exposed to COM, the CLR + // implements IConnectionPoint for the proxy object via the CCW or COM-callable wrapper. + // We use the helper class ComConnectionPoint to get to the CCW directly to to call the interface. + // It is critical to call Dispose to ensure that the IUnknown is released. + + using (ComConnectionPoint cp = cpc.FindConnectionPoint(eventInterface)) { + using (SafeIUnknown punkEventsSink = new SafeIUnknown(sink, true)) { + // Finally...we can call IConnectionPoint.Advise to hook up a native COM event sink + // to a managed .NET event interface. + return cp.Advise(punkEventsSink.DangerousGetHandle(), out cookie); + } + } + } + + /// + /// Wraps a native IUnknown in a SafeHandle. + /// See similar implementaton in the class. + /// + internal class SafeIUnknown : SafeHandle { + + /// + /// Wrap an incomoing unknown or get the unknown for the CCW (COM-callable wrapper). + /// + public SafeIUnknown(object obj, bool addRefIntPtr) + : this(obj, addRefIntPtr, Guid.Empty) { + } + + /// + /// Wrap an incomoing unknown or get the unknown for the CCW (COM-callable wrapper). + /// If an iid is supplied, QI for the interface and wrap that unknonwn instead. + /// + public SafeIUnknown(object obj, bool addRefIntPtr, Guid iid) + : base(IntPtr.Zero, true) { + + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); + try { + + // Set this.handle in a finally block to ensure the com ptr is set in the SafeHandle + // even if the runtime throws a exception (such as ThreadAbortException) during the call. + // This ensures that the finalizer will clean up the COM reference. + } + finally { + + // Get a raw IUnknown for this object. + // We are responsible for releasing the IUnknown ourselves. + IntPtr unknown; + + if (obj is IntPtr) { + unknown = (IntPtr)obj; + + // The incoming IntPtr may already be reference counted or not, depending on + // where it came from. The caller needs to tell us whether to add-ref or not. + if (addRefIntPtr) { + Marshal.AddRef(unknown); + } + + } else { + // GetIUnknownForObject will return a reference-counted object + unknown = Marshal.GetIUnknownForObject(obj); + } + + // Attempt QueryInterface if an iid is specified. + if (iid != Guid.Empty) { + IntPtr oldUnknown = unknown; + try { + unknown = InternalQueryInterface(unknown, ref iid); + } + finally { + // It is critical to release the original unknown if + // InternalQueryInterface throws out so we don't leak ref counts. + Marshal.Release(oldUnknown); + } + } + + // Preserve the com ptr in the SafeHandle. + this.handle = unknown; + } + } + + /// + /// Helper function for QueryInterface. + /// + private static IntPtr InternalQueryInterface(IntPtr pUnk, ref Guid iid) { + IntPtr ppv; + int hresult = Marshal.QueryInterface(pUnk, ref iid, out ppv); + if (hresult != 0 || ppv == IntPtr.Zero) { + throw new InvalidCastException(SR.GetString(SR.AxInterfaceNotSupported)); + } + return ppv; + } + + /// + /// Return whether the handle is invalid. + /// + public sealed override bool IsInvalid { + get { + if (!base.IsClosed) { + return (IntPtr.Zero == base.handle); + } + return true; + } + } + + /// + /// Release the IUnknown. + /// + protected sealed override bool ReleaseHandle() { + IntPtr ptr1 = base.handle; + base.handle = IntPtr.Zero; + if (IntPtr.Zero != ptr1) { + Marshal.Release(ptr1); + } + return true; + } + + /// + /// Helper function to load a COM v-table from a com object pointer. + /// + protected V LoadVtable() { + IntPtr vtblptr = Marshal.ReadIntPtr(this.handle, 0); + return (V)Marshal.PtrToStructure(vtblptr, typeof(V)); + } + } + + /// + /// Helper class to access IConnectionPointContainer from a .NET COM-callable wrapper. + /// The IConnectionPointContainer COM pointer is wrapped in a SafeHandle. + /// + internal sealed class ComConnectionPointContainer + : SafeIUnknown { + + public ComConnectionPointContainer(object obj, bool addRefIntPtr) + : base(obj, addRefIntPtr, typeof(System.Runtime.InteropServices.ComTypes.IConnectionPointContainer).GUID) { + this.vtbl = base.LoadVtable(); + } + + private VTABLE vtbl; + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + [StructLayout(LayoutKind.Sequential)] + private class VTABLE { + public IntPtr QueryInterfacePtr; + public IntPtr AddRefPtr; + public IntPtr ReleasePtr; + public IntPtr EnumConnectionPointsPtr; + public IntPtr FindConnectionPointPtr; + } + + /// + /// Call IConnectionPointContainer.FindConnectionPoint using Delegate.Invoke on the v-table slot. + /// + public ComConnectionPoint FindConnectionPoint(Type eventInterface) { + FindConnectionPointD findConnectionPoint = (FindConnectionPointD)Marshal.GetDelegateForFunctionPointer(this.vtbl.FindConnectionPointPtr, typeof(FindConnectionPointD)); + IntPtr result = IntPtr.Zero; + + Guid iid = eventInterface.GUID; + int hresult = findConnectionPoint.Invoke(this.handle, ref iid, out result); + if (hresult != 0 || result == IntPtr.Zero) { + throw new ArgumentException(SR.GetString(SR.AXNoConnectionPoint, eventInterface.Name)); + } + + return new ComConnectionPoint(result, false); // result is already ref-counted as an out-param so pass in false + } + + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] + private delegate int FindConnectionPointD(IntPtr This, ref Guid iid, out IntPtr ppv); + } + + /// + /// Helper class to access IConnectionPoint from a .NET COM-callable wrapper. + /// The IConnectionPoint COM pointer is wrapped in a SafeHandle. + /// + internal sealed class ComConnectionPoint + : SafeIUnknown { + + public ComConnectionPoint(object obj, bool addRefIntPtr) + : base(obj, addRefIntPtr, typeof(System.Runtime.InteropServices.ComTypes.IConnectionPoint).GUID) { + this.vtbl = this.LoadVtable(); + } + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + [StructLayout(LayoutKind.Sequential)] + private class VTABLE { + public IntPtr QueryInterfacePtr; + public IntPtr AddRefPtr; + public IntPtr ReleasePtr; + public IntPtr GetConnectionInterfacePtr; + public IntPtr GetConnectionPointContainterPtr; + public IntPtr AdvisePtr; + public IntPtr UnadvisePtr; + public IntPtr EnumConnectionsPtr; + } + + private VTABLE vtbl; + + /// + /// Call IConnectioinPoint.Advise using Delegate.Invoke on the v-table slot. + /// + public bool Advise(IntPtr punkEventSink, out int cookie) { + AdviseD advise = (AdviseD)Marshal.GetDelegateForFunctionPointer(this.vtbl.AdvisePtr, typeof(AdviseD)); + if (advise.Invoke(this.handle, punkEventSink, out cookie) == 0) { + return true; + } + return false; + } + + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] + private delegate int AdviseD(IntPtr This, IntPtr punkEventSink, out int cookie); + } + + } + + /// + /// Return the default COM events interface declared on a .NET class. + /// This looks for the ComSourceInterfacesAttribute and returns the .NET + /// interface type of the first interface declared. + /// + private static Type GetDefaultEventsInterface(Type controlType) { + + Type eventInterface = null; + object[] custom = controlType.GetCustomAttributes(typeof(ComSourceInterfacesAttribute), false); + + if (custom.Length > 0) { + ComSourceInterfacesAttribute coms = (ComSourceInterfacesAttribute)custom[0]; + string eventName = coms.Value.Split(new char[] { '\0' })[0]; + eventInterface = controlType.Module.Assembly.GetType(eventName, false); + if (eventInterface == null) { + eventInterface = Type.GetType(eventName, false); + } + } + + return eventInterface; + } + + + /// + /// + /// Implements IPersistStorage::Save + /// + internal void Save(UnsafeNativeMethods.IStorage stg, bool fSameAsLoad) { + UnsafeNativeMethods.IStream stream = null; + + IntSecurity.UnmanagedCode.Assert(); + try { + stream = stg.CreateStream(this.GetStreamName(), NativeMethods.STGM_WRITE | NativeMethods.STGM_SHARE_EXCLUSIVE | NativeMethods.STGM_CREATE, 0, 0); + } + finally { + CodeAccessPermission.RevertAssert(); + } + Debug.Assert(stream != null, "Stream should be non-null, or an exception should have been thrown."); + Save(stream, true); + UnsafeNativeMethods.ReleaseComObject(stream); + } + + /// + /// + /// Implements IPersistStreamInit::Save + /// + internal void Save(UnsafeNativeMethods.IStream stream, bool fClearDirty) { + // We do everything through property bags because we support full fidelity + // in them. So, save through that method. + // + PropertyBagStream bag = new PropertyBagStream(); + Save(bag, fClearDirty, false); + IntSecurity.UnmanagedCode.Assert(); + try { + bag.Write(stream); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (UnsafeNativeMethods.IsComObject(stream)) { + UnsafeNativeMethods.ReleaseComObject(stream); + } + } + + /// + /// Implements IPersistPropertyBag::Save + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void Save(UnsafeNativeMethods.IPropertyBag pPropBag, bool fClearDirty, bool fSaveAllProperties) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(control, + new Attribute[] {DesignerSerializationVisibilityAttribute.Visible}); + + for (int i = 0; i < props.Count; i++) { + if (fSaveAllProperties || props[i].ShouldSerializeValue(control)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Saving property " + props[i].Name); + + object propValue; + + if (IsResourceProp(props[i])) { + + // Resource property. Save this to the bag as a 64bit encoded string. + // + MemoryStream stream = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, props[i].GetValue(control)); + byte[] bytes = new byte[(int)stream.Length]; + stream.Position = 0; + stream.Read(bytes, 0, bytes.Length); + propValue = Convert.ToBase64String(bytes); + pPropBag.Write(props[i].Name, ref propValue); + } + else { + + // Not a resource property. Persist this using standard type converters. + // + TypeConverter converter = props[i].Converter; + Debug.Assert(converter != null, "No type converter for property '" + props[i].Name + "' on class " + control.GetType().FullName); + + if (converter.CanConvertFrom(typeof(string))) { + propValue = converter.ConvertToInvariantString(props[i].GetValue(control)); + pPropBag.Write(props[i].Name, ref propValue); + } + else if (converter.CanConvertFrom(typeof(byte[]))) { + byte[] data = (byte[])converter.ConvertTo(null, CultureInfo.InvariantCulture, props[i].GetValue(control), typeof(byte[])); + propValue = Convert.ToBase64String(data); + pPropBag.Write(props[i].Name, ref propValue); + } + } + } + } + + if (UnsafeNativeMethods.IsComObject(pPropBag)) { + UnsafeNativeMethods.ReleaseComObject(pPropBag); + } + + if (fClearDirty) { + activeXState[isDirty] = false; + } + } + + /// + /// Fires the OnSave event to all of our IAdviseSink + /// listeners. Used for ActiveXSourcing. + /// + /// + private void SendOnSave() { + int cnt = adviseList.Count; + IntSecurity.UnmanagedCode.Assert(); + for (int i = 0; i < cnt; i++) { + IAdviseSink s = (IAdviseSink)adviseList[i]; + Debug.Assert(s != null, "NULL in our advise list"); + s.OnSave(); + } + } + + /// + /// + /// Implements IViewObject2::SetAdvise. + /// + internal void SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + // if it's not a content aspect, we don't support it. + // + if ((aspects & NativeMethods.DVASPECT_CONTENT) == 0) { + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + + // set up some flags [we gotta stash for GetAdvise ...] + // + activeXState[viewAdvisePrimeFirst] = (advf & NativeMethods.ADVF_PRIMEFIRST) != 0 ? true : false; + activeXState[viewAdviseOnlyOnce] = (advf & NativeMethods.ADVF_ONLYONCE) != 0 ? true : false; + + if (viewAdviseSink != null && UnsafeNativeMethods.IsComObject(viewAdviseSink)) { + UnsafeNativeMethods.ReleaseComObject(viewAdviseSink); + } + + viewAdviseSink = pAdvSink; + + // prime them if they want it [we need to store this so they can get flags later] + // + if (activeXState[viewAdvisePrimeFirst]) { + ViewChanged(); + } + } + + /// + /// + /// Implements IOleObject::SetClientSite. + /// + internal void SetClientSite(UnsafeNativeMethods.IOleClientSite value) { + if (clientSite != null) { + + if (value == null) { + globalActiveXCount--; + + if (globalActiveXCount == 0 && IsIE) { + + // This the last ActiveX control and we are + // being hosted in IE. Use private reflection + // to ask SystemEvents to shutdown. This is to + // prevent a crash (ASURT 139932). + + // SECREVIEW : The SystemEvents class has a LinkDemand for FullTrust permission but it becomes a full demand + // on a method called using private reflection. We need to assert here. + // SystemEvents.Shutdown is a private method intended to be called using private reflection (See commnets + // on the method definition); it sends a WM_QUIT message to the app broadcast window. + // So it is safe to assert here. + // + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + MethodInfo method = typeof(SystemEvents).GetMethod("Shutdown", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, + null, new Type[0], new ParameterModifier[0]); + Debug.Assert(method != null, "No Shutdown method on SystemEvents"); + if (method != null) { + method.Invoke(null, null); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + if (UnsafeNativeMethods.IsComObject(clientSite)) { + IntSecurity.UnmanagedCode.Assert(); + try { + Marshal.FinalReleaseComObject(clientSite); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + clientSite = value; + + if (clientSite != null) { + control.Site = new AxSourcingSite(control, clientSite, "ControlAxSourcingSite"); + } + else { + control.Site = null; + } + + // Get the ambient properties that effect us... + // + Object obj = new Object(); + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + activeXState[uiDead] = (bool)obj; + } + + if (control is IButtonControl && GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + ((IButtonControl)control).NotifyDefault((bool)obj); + } + + if (clientSite == null) { + if (accelTable != IntPtr.Zero) { + UnsafeNativeMethods.DestroyAcceleratorTable(new HandleRef(this, accelTable)); + accelTable = IntPtr.Zero; + accelCount = -1; + } + + if (IsIE) { + this.control.Dispose(); + } + } + else { + globalActiveXCount++; + + if (globalActiveXCount == 1 && IsIE) { + + // This the first ActiveX control and we are + // being hosted in IE. Use private reflection + // to ask SystemEvents to start. Startup will only + // restart system events if we previously shut it down. + // This is to prevent a crash (ASURT 139932). + // + // SECREVIEW : The SystemEvents class has a LinkDemand for FullTrust permission but it becomes a full demand + // on a method called using private reflection. We need to assert here. + // SystemEvents.Shutdown is a private method intended to be called using private reflection (See commnets + // on the method definition); it sends a WM_QUIT message to the app broadcast window. + // So it is safe to assert here. + // + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + MethodInfo method = typeof(SystemEvents).GetMethod("Startup", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, + null, new Type[0], new ParameterModifier[0]); + Debug.Assert(method != null, "No Startup method on SystemEvents"); + if (method != null) { + method.Invoke(null, null); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + control.OnTopMostActiveXParentChanged(EventArgs.Empty); + } + + /// + /// Implements IOleObject::SetExtent + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void SetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + if ((dwDrawAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + + if (activeXState[changingExtents]) { + return; + } + + activeXState[changingExtents] = true; + + try { + Size size = new Size(HiMetricToPixel(pSizel.cx, pSizel.cy)); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : new size:" + size.ToString()); + + // If we're in place active, let the in place site set our bounds. + // Otherwise, just set it on our control directly. + // + if (activeXState[inPlaceActive]) { + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + Rectangle bounds = control.Bounds; + bounds.Location = new Point(bounds.X, bounds.Y); + Size adjusted = new Size(size.Width, size.Height); + bounds.Width = adjusted.Width; + bounds.Height = adjusted.Height; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Announcing to in place site that our rect has changed."); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, " Announcing rect = " + bounds); + Debug.Assert(clientSite != null, "How can we setextent before we are sited??"); + + ioleClientSite.OnPosRectChange(NativeMethods.COMRECT.FromXYWH(bounds.X, bounds.Y, bounds.Width, bounds.Height)); + } + } + + control.Size = size; + + // Check to see if the control overwrote our size with + // its own values. + // + if (!control.Size.Equals(size)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Control has changed size. Setting dirty bit"); + activeXState[isDirty] = true; + + // If we're not inplace active, then anounce that the view changed. + // + if (!activeXState[inPlaceActive]) { + ViewChanged(); + } + + // We need to call RequestNewObjectLayout + // here so we visually display our new extents. + // + if (!activeXState[inPlaceActive] && clientSite != null) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Requesting new Object layout."); + clientSite.RequestNewObjectLayout(); + } + } + } + finally { + activeXState[changingExtents] = false; + } + } + else { + // We don't support any other aspects + // + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + } + + /// + /// Marks our state as in place visible. + /// + private void SetInPlaceVisible(bool visible) { + activeXState[inPlaceVisible] = visible; + control.Visible = visible; + } + + /// + /// Implements IOleInPlaceObject::SetObjectRects. + /// + internal void SetObjectRects(NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect) { + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("SetObjectRects:"); + Debug.Indent(); + + if (lprcPosRect != null) { + Debug.WriteLine("PosLeft: " + lprcPosRect.left.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosTop: " + lprcPosRect.top.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosRight: " + lprcPosRect.right.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosBottom: " + lprcPosRect.bottom.ToString(CultureInfo.InvariantCulture)); + } + + if (lprcClipRect != null) { + Debug.WriteLine("ClipLeft: " + lprcClipRect.left.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipTop: " + lprcClipRect.top.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipRight: " + lprcClipRect.right.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipBottom: " + lprcClipRect.bottom.ToString(CultureInfo.InvariantCulture)); + } + Debug.Unindent(); + } +#endif + + Rectangle posRect = Rectangle.FromLTRB(lprcPosRect.left, lprcPosRect.top, lprcPosRect.right, lprcPosRect.bottom); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Set control bounds: " + posRect.ToString()); + + // ActiveX expects to be notified when a control's bounds change, and also + // intends to notify us through SetObjectRects when we report that the + // bounds are about to change. We implement this all on a control's Bounds + // property, which doesn't use this callback mechanism. The adjustRect + // member handles this. If it is non-null, then we are being called in + // response to an OnPosRectChange call. In this case we do not + // set the control bounds but set the bounds on the adjustRect. When + // this returns from the container and comes back to our OnPosRectChange + // implementation, these new bounds will be handed back to the control + // for the actual window change. + // + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Old Control Bounds: " + control.Bounds); + if (activeXState[adjustingRect]) { + adjustRect.left = posRect.X; + adjustRect.top = posRect.Y; + adjustRect.right = posRect.Width + posRect.X; + adjustRect.bottom = posRect.Height + posRect.Y; + } + else { + activeXState[adjustingRect] = true; + try { + control.Bounds = posRect; + } + finally { + activeXState[adjustingRect] = false; + } + } + + bool setRegion = false; + + if (clipRegion != IntPtr.Zero) { + // Bad -- after calling SetWindowReg, windows owns the region. + //SafeNativeMethods.DeleteObject(clipRegion); + clipRegion = IntPtr.Zero; + setRegion = true; + } + + if (lprcClipRect != null) { + + // The container wants us to clip, so figure out if we really + // need to. + // + Rectangle clipRect = Rectangle.FromLTRB(lprcClipRect.left, lprcClipRect.top, lprcClipRect.right, lprcClipRect.bottom); + + Rectangle intersect; + + // Trident always sends an empty ClipRect... and so, we check for that and not do an + // intersect in that case. + // + if (!clipRect.IsEmpty) + intersect = Rectangle.Intersect(posRect, clipRect); + else + intersect = posRect; + + if (!intersect.Equals(posRect)) { + + // Offset the rectangle back to client coordinates + // + NativeMethods.RECT rcIntersect = NativeMethods.RECT.FromXYWH(intersect.X, intersect.Y, intersect.Width, intersect.Height); + IntPtr hWndParent = UnsafeNativeMethods.GetParent(new HandleRef(control, control.Handle)); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Old Intersect: " + new Rectangle(rcIntersect.left, rcIntersect.top, rcIntersect.right-rcIntersect.left, rcIntersect.bottom-rcIntersect.top)); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "New Control Bounds: " + posRect); + + UnsafeNativeMethods.MapWindowPoints(new HandleRef(null, hWndParent), new HandleRef(control, control.Handle), ref rcIntersect, 2); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "New Intersect: " + new Rectangle(rcIntersect.left, rcIntersect.top, rcIntersect.right-rcIntersect.left, rcIntersect.bottom-rcIntersect.top)); + + // Create a Win32 region for it + // + clipRegion = SafeNativeMethods.CreateRectRgn(rcIntersect.left, rcIntersect.top, + rcIntersect.right, rcIntersect.bottom); + setRegion = true; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Created clipping region"); + } + } + + // If our region has changed, set the new value. We only do this if + // the handle has been created, since otherwise the control will + // merge our region automatically. + // + if (setRegion && control.IsHandleCreated) { + IntPtr finalClipRegion = clipRegion; + + Region controlRegion = control.Region; + if (controlRegion != null) { + IntPtr rgn = control.GetHRgn(controlRegion); + finalClipRegion = MergeRegion(rgn); + } + + UnsafeNativeMethods.SetWindowRgn(new HandleRef(control, control.Handle), new HandleRef(this, finalClipRegion), SafeNativeMethods.IsWindowVisible(new HandleRef(control, control.Handle))); + } + + // Yuck. Forms^3 uses transparent overlay windows that appear to cause + // painting artifacts. Flicker like a banshee. + // + control.Invalidate(); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// Shows a property page dialog. + /// + private void ShowProperties() { + if (propPage == null) { + propPage = new ActiveXPropPage(control); + } + + if (inPlaceFrame != null) { + inPlaceFrame.EnableModeless(0); + } + try { + propPage.Edit(control); + } + finally { + if (inPlaceFrame != null) { + inPlaceFrame.EnableModeless(1); + } + } + } + #endif + + /// + /// + /// Throws the given hresult. This is used by ActiveX sourcing. + /// + internal static void ThrowHr(int hr) { + ExternalException e = new ExternalException(SR.GetString(SR.ExternalException), hr); + throw e; + } + + /// + /// + /// Handles IOleControl::TranslateAccelerator + /// + internal int TranslateAccelerator(ref NativeMethods.MSG lpmsg) { + + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + if (!control.IsHandleCreated) { + Debug.WriteLine("AxSource: TranslateAccelerator before handle creation"); + } + else { + Message m = Message.Create(lpmsg.hwnd, lpmsg.message, lpmsg.wParam, lpmsg.lParam); + Debug.WriteLine("AxSource: TranslateAccelerator : " + m.ToString()); + } + } +#endif // DEBUG + + bool needPreProcess = false; + + switch (lpmsg.message) { + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + case NativeMethods.WM_CHAR: + case NativeMethods.WM_SYSCHAR: + needPreProcess = true; + break; + } + + Message msg = Message.Create(lpmsg.hwnd, lpmsg.message, lpmsg.wParam, lpmsg.lParam); + + if (needPreProcess) { + Control target = Control.FromChildHandleInternal(lpmsg.hwnd); + if (target != null && (control == target || control.Contains(target))) { + PreProcessControlState messageState = PreProcessControlMessageInternal(target, ref msg); + switch (messageState) { + case PreProcessControlState.MessageProcessed: + // someone returned true from PreProcessMessage + // no need to dispatch the message, its already been coped with. + lpmsg.message = msg.Msg; + lpmsg.wParam = msg.WParam; + lpmsg.lParam = msg.LParam; + return NativeMethods.S_OK; + case PreProcessControlState.MessageNeeded: + // VSWhidbey 515519: Here we need to dispatch the message ourselves + // otherwise the host may never send the key to our wndproc. + + // Someone returned true from IsInputKey or IsInputChar + UnsafeNativeMethods.TranslateMessage(ref lpmsg); + if (SafeNativeMethods.IsWindowUnicode(new HandleRef(null, lpmsg.hwnd))) { + UnsafeNativeMethods.DispatchMessageW(ref lpmsg); + } + else { + UnsafeNativeMethods.DispatchMessageA(ref lpmsg); + } + return NativeMethods.S_OK; + case PreProcessControlState.MessageNotNeeded: + // in this case we'll check the site to see if it wants the message. + break; + } + } + } + + + // SITE processing. We're not interested in the message, but the site may be. + + int hr = NativeMethods.S_FALSE; + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource: Control did not process accelerator, handing to site"); + + UnsafeNativeMethods.IOleControlSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleControlSite; + if (ioleClientSite != null) { + int keyState = 0; + + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_SHIFT) < 0) keyState |= 1; + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL) < 0) keyState |= 2; + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU) < 0) keyState |= 4; + IntSecurity.UnmanagedCode.Assert(); + try { + hr = ioleClientSite.TranslateAccelerator(ref lpmsg, keyState); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + return hr; + } + + /// + /// + /// Implements IOleInPlaceObject::UIDeactivate. + /// + internal int UIDeactivate() { + // Only do this if we're UI active + // + if (!activeXState[uiActive]) { + return NativeMethods.S_OK; + } + + activeXState[uiActive] = false; + + // Notify frame windows, if appropriate, that we're no longer ui-active. + // + if (inPlaceUiWindow != null) { + inPlaceUiWindow.SetActiveObject(null, null); + } + + //May need this for SetActiveObject & OnUIDeactivate, so leave until function return + IntSecurity.UnmanagedCode.Assert(); + Debug.Assert(inPlaceFrame != null, "No inplace frame -- how dod we go UI active?"); + inPlaceFrame.SetActiveObject(null, null); + + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + ioleClientSite.OnUIDeactivate(0); + } + + return NativeMethods.S_OK; + } + + /// + /// + /// Implements IOleObject::Unadvise + /// + internal void Unadvise(int dwConnection) { + if (dwConnection > adviseList.Count || adviseList[dwConnection - 1] == null) { + ThrowHr(NativeMethods.OLE_E_NOCONNECTION); + } + + IAdviseSink sink = (IAdviseSink)adviseList[dwConnection - 1]; + adviseList.RemoveAt(dwConnection - 1); + if (sink != null && UnsafeNativeMethods.IsComObject(sink)) { + UnsafeNativeMethods.ReleaseComObject(sink); + } + } + + /// + /// Notifies our site that we have changed our size and location. + /// + /// + internal void UpdateBounds(ref int x, ref int y, ref int width, ref int height, int flags) { + if (!activeXState[adjustingRect] && activeXState[inPlaceVisible]) { + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + NativeMethods.COMRECT rc = new NativeMethods.COMRECT(); + + if ((flags & NativeMethods.SWP_NOMOVE) != 0) { + rc.left = control.Left; + rc.top = control.Top; + } + else { + rc.left = x; + rc.top = y; + } + + if ((flags & NativeMethods.SWP_NOSIZE) != 0) { + rc.right = rc.left + control.Width; + rc.bottom = rc.top + control.Height; + } + else { + rc.right = rc.left + width; + rc.bottom = rc.top + height; + } + + // This member variable may be modified by SetObjectRects by the container. + adjustRect = rc; + activeXState[adjustingRect] = true; + + IntSecurity.UnmanagedCode.Assert(); + try { + ioleClientSite.OnPosRectChange(rc); + } + finally { + CodeAccessPermission.RevertAssert(); + adjustRect = null; + activeXState[adjustingRect] = false; + } + + // On output, the new bounds will be reflected in rc + if ((flags & NativeMethods.SWP_NOMOVE) == 0) { + x = rc.left; + y = rc.top; + } + if ((flags & NativeMethods.SWP_NOSIZE) == 0) { + width = rc.right - rc.left; + height = rc.bottom - rc.top; + } + } + } + } + + /// + /// Notifies that the accelerator table needs to be updated due to a change in a control mnemonic. + /// + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + internal void UpdateAccelTable(){ + // Setting the count to -1 will recreate the table on demand (when GetControlInfo is called). + this.accelCount = -1; + + UnsafeNativeMethods.IOleControlSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleControlSite; + if (ioleClientSite != null) { + IntSecurity.UnmanagedCode.Assert(); + ioleClientSite.OnControlInfoChanged(); + } + } + + // Since this method is used by Reflection .. dont change the "signature" + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + internal void ViewChangedInternal() + { + ViewChanged(); + } + + /// + /// Notifies our view advise sink (if it exists) that the view has + /// changed. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + private void ViewChanged() { + // send the view change notification to anybody listening. + // + // Note: Word2000 won't resize components correctly if an OnViewChange notification + // is sent while the component is persisting it's state. The !m_fSaving check + // is to make sure we don't call OnViewChange in this case. + // + if (viewAdviseSink != null && !activeXState[saving]) { + IntSecurity.UnmanagedCode.Assert(); + try { + viewAdviseSink.OnViewChange(NativeMethods.DVASPECT_CONTENT, -1); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (activeXState[viewAdviseOnlyOnce]) { + if (UnsafeNativeMethods.IsComObject(viewAdviseSink)) { + UnsafeNativeMethods.ReleaseComObject(viewAdviseSink); + } + viewAdviseSink = null; + } + } + } + + /// + /// Called when the window handle of the control has changed. + /// + void IWindowTarget.OnHandleChange(IntPtr newHandle) { + controlWindowTarget.OnHandleChange(newHandle); + } + + /// + /// Called to do control-specific processing for this window. + /// + void IWindowTarget.OnMessage(ref Message m) { + if (activeXState[uiDead]) { + if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST) { + return; + } + if (m.Msg >= NativeMethods.WM_NCLBUTTONDOWN && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK) { + return; + } + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + return; + } + } + + IntSecurity.UnmanagedCode.Assert(); + controlWindowTarget.OnMessage(ref m); + } + + /// + /// This is a property bag implementation that sits on a stream. It can + /// read and write the bag to the stream. + /// + private class PropertyBagStream : UnsafeNativeMethods.IPropertyBag { + private Hashtable bag = new Hashtable(); + + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void Read(UnsafeNativeMethods.IStream istream) { + // visual basic's memory streams don't support seeking, so we have to + // work around this limitation here. We do this by copying + // the contents of the stream into a MemoryStream object. + // + Stream stream = new DataStreamFromComStream(istream); + const int PAGE_SIZE = 0x1000; // one page (4096b) + byte[] streamData = new byte[PAGE_SIZE]; + int offset = 0; + + int count = stream.Read(streamData, offset, PAGE_SIZE); + int totalCount = count; + + while (count == PAGE_SIZE) { + byte[] newChunk = new byte[streamData.Length + PAGE_SIZE]; + Array.Copy(streamData, newChunk, streamData.Length); + streamData = newChunk; + + offset += PAGE_SIZE; + count = stream.Read(streamData, offset, PAGE_SIZE); + totalCount += count; + } + + stream = new MemoryStream(streamData); + + BinaryFormatter formatter = new BinaryFormatter(); + try { + bag = (Hashtable)formatter.Deserialize(stream); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Error reading. Just init an empty hashtable. + bag = new Hashtable(); + } + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IPropertyBag.Read(string pszPropName, ref object pVar, UnsafeNativeMethods.IErrorLog pErrorLog) + { + if (!bag.Contains(pszPropName)) + return NativeMethods.E_INVALIDARG; + + pVar = bag[pszPropName]; + return NativeMethods.S_OK; + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IPropertyBag.Write(string pszPropName, ref object pVar) + { + bag[pszPropName] = pVar; + return NativeMethods.S_OK; + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal void Write(UnsafeNativeMethods.IStream istream) + { + Stream stream = new DataStreamFromComStream(istream); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, bag); + } + } + } + + private class AxSourcingSite : ISite { + private IComponent component; + private UnsafeNativeMethods.IOleClientSite clientSite; + private string name; + private HtmlShimManager shimManager; + + + internal AxSourcingSite(IComponent component, UnsafeNativeMethods.IOleClientSite clientSite, string name) { + this.component = component; + this.clientSite = clientSite; + this.name = name; + } + + /** The component sited by this component site. */ + public IComponent Component { + get { + return component; + } + } + + /** The container in which the component is sited. */ + public IContainer Container { + get { + return null; + } + } + + public Object GetService(Type service) { + object retVal = null; + + if (service == typeof(HtmlDocument)) { + + UnsafeNativeMethods.IOleContainer iOlecontainer; + int hr; + try { + // SECREVIEW : UnsafeNativeMethods.IOleClientSite demands UnmnagedCode permission like most of our COM interfaces + // so we need to assert here. This assert is safe, we are checking for specific services we support + // to return it to the user (COM interface). + // + IntSecurity.UnmanagedCode.Assert(); + hr = clientSite.GetContainer(out iOlecontainer); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + + if (NativeMethods.Succeeded(hr) + && (iOlecontainer is UnsafeNativeMethods.IHTMLDocument)) { + if (shimManager == null) { + shimManager = new HtmlShimManager(); + } + + // Security note: There is a demand for FullTrust on HtmlDocument that should apply + // to this call. So we don't need to demand here. + retVal = new HtmlDocument(shimManager, iOlecontainer as UnsafeNativeMethods.IHTMLDocument); + } + + } + else if (clientSite.GetType().IsAssignableFrom(service)) { + IntSecurity.UnmanagedCode.Demand(); + retVal = clientSite; + } + + return retVal; + } + + + /** Indicates whether the component is in design mode. */ + public bool DesignMode { + get { + return false; + } + } + + /** + * The name of the component. + */ + public String Name { + get { return name;} + set { + if (value == null || name == null) { + name = value; + } + } + } + } + + /// + /// This is a marshaler object that knows how to marshal IFont to Font + /// and back. + /// + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + private class ActiveXFontMarshaler : ICustomMarshaler { + + private static ActiveXFontMarshaler instance; + + public void CleanUpManagedData(object obj) { + } + + public void CleanUpNativeData(IntPtr pObj) { + Marshal.Release(pObj); + } + + internal static ICustomMarshaler GetInstance(string cookie) { + if (instance == null) { + instance = new ActiveXFontMarshaler(); + } + return instance; + } + public int GetNativeDataSize() { + return -1; // not a value type, so use -1 + } + + public IntPtr MarshalManagedToNative(object obj) { + Font font = (Font)obj; + NativeMethods.tagFONTDESC fontDesc = new NativeMethods.tagFONTDESC(); + NativeMethods.LOGFONT logFont = new NativeMethods.LOGFONT(); + + // SECREVIEW : The Font.ToLogFont method is marked with the 'unsafe' modifier cause it needs to write bytes into the logFont struct, + // here we are passing that struct so the assert is safe. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font.ToLogFont(logFont); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + fontDesc.lpstrName = font.Name; + fontDesc.cySize = (long)(font.SizeInPoints * 10000); + fontDesc.sWeight = (short)logFont.lfWeight; + fontDesc.sCharset = logFont.lfCharSet; + fontDesc.fItalic = font.Italic; + fontDesc.fUnderline = font.Underline; + fontDesc.fStrikethrough = font.Strikeout; + + Guid iid = typeof(UnsafeNativeMethods.IFont).GUID; + + UnsafeNativeMethods.IFont oleFont = UnsafeNativeMethods.OleCreateFontIndirect(fontDesc, ref iid); + IntPtr pFont = Marshal.GetIUnknownForObject(oleFont); + IntPtr pIFont; + + int hr = Marshal.QueryInterface(pFont, ref iid, out pIFont); + + Marshal.Release(pFont); + + if (NativeMethods.Failed(hr)) { + Marshal.ThrowExceptionForHR(hr); + } + + return pIFont; + } + + public object MarshalNativeToManaged(IntPtr pObj) { + UnsafeNativeMethods.IFont nativeFont = (UnsafeNativeMethods.IFont)Marshal.GetObjectForIUnknown(pObj); + IntPtr hfont = IntPtr.Zero; + + IntSecurity.UnmanagedCode.Assert(); + try { + hfont = nativeFont.GetHFont(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + Font font = null; + + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font = Font.FromHfont(hfont); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + font = Control.DefaultFont; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return font; + } + } + + /// + /// Simple verb enumerator. + /// + private class ActiveXVerbEnum : UnsafeNativeMethods.IEnumOLEVERB { + private NativeMethods.tagOLEVERB[] verbs; + private int current; + + internal ActiveXVerbEnum(NativeMethods.tagOLEVERB[] verbs) { + this.verbs = verbs; + current = 0; + } + + public int Next(int celt, NativeMethods.tagOLEVERB rgelt, int[] pceltFetched) { + int fetched = 0; + + if (celt != 1) { + Debug.Fail("Caller of IEnumOLEVERB requested celt > 1, but clr marshalling does not support this."); + celt = 1; + } + + while (celt > 0 && current < verbs.Length) { + rgelt.lVerb = verbs[current].lVerb; + rgelt.lpszVerbName = verbs[current].lpszVerbName; + rgelt.fuFlags = verbs[current].fuFlags; + rgelt.grfAttribs = verbs[current].grfAttribs; + celt--; + current++; + fetched++; + } + + if (pceltFetched != null) { + pceltFetched[0] = fetched; + } + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("AxSource:IEnumOLEVERB::Next returning " + fetched.ToString(CultureInfo.InvariantCulture) + " verbs:"); + Debug.Indent(); + for (int i = current - fetched; i < current; i++) { + Debug.WriteLine(i.ToString(CultureInfo.InvariantCulture) + ": " + verbs[i].lVerb + " " + (verbs[i].lpszVerbName == null ? string.Empty : verbs[i].lpszVerbName)); + } + Debug.Unindent(); + } +#endif + return(celt == 0 ? NativeMethods.S_OK : NativeMethods.S_FALSE); + } + + public int Skip(int celt) { + if (current + celt < verbs.Length) { + current += celt; + return NativeMethods.S_OK; + } + else { + current = verbs.Length; + return NativeMethods.S_FALSE; + } + } + + public void Reset() { + current = 0; + } + + public void Clone(out UnsafeNativeMethods.IEnumOLEVERB ppenum) { + ppenum = new ActiveXVerbEnum(verbs); + } + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// The properties window we display. + /// + private class ActiveXPropPage { + private Form form; + private PropertyGrid grid; + private ComponentEditor compEditor; + + internal ActiveXPropPage(object component) { + compEditor = (ComponentEditor)TypeDescriptor.GetEditor(component, typeof(ComponentEditor)); + + if (compEditor == null) { + form = new Form(); + grid = new PropertyGrid(); + + form.Text = SR.GetString(SR.AXProperties); + form.StartPosition = FormStartPosition.CenterParent; + form.Size = new Size(300, 350); + form.FormBorderStyle = FormBorderStyle.Sizable; + form.MinimizeBox = false; + form.MaximizeBox = false; + form.ControlBox = true; + form.SizeGripStyle = SizeGripStyle.Show; + form.DockPadding.Bottom = 16; // size grip size + + Bitmap bitmap = new Bitmap(grid.GetType(), "PropertyGrid.bmp"); + bitmap.MakeTransparent(); + form.Icon = Icon.FromHandle(bitmap.GetHicon()); + + grid.Dock = DockStyle.Fill; + + form.Controls.Add(grid); + } + } + + internal void Edit(object editingObject) { + if (compEditor != null) { + compEditor.EditComponent(null, editingObject); + } + else { + grid.SelectedObject = editingObject; + form.ShowDialog(); + } + } + } + + #endif + + /// + /// Contains a single ambient property, including DISPID, name and value. + /// + private class AmbientProperty { + private string name; + private int dispID; + private Object value; + private bool empty; + + /// + /// + /// Creates a new, empty ambient property. + /// + internal AmbientProperty(string name, int dispID) { + this.name = name; + this.dispID = dispID; + this.value = null; + this.empty = true; + } + + /// + /// + /// The windows forms property name. + /// + internal string Name { + get { + return name; + } + } + + /// + /// + /// The DispID for the property. + /// + internal int DispID { + get { + return dispID; + } + } + + /// + /// + /// Returns true if this property has not been set. + /// + internal bool Empty { + get { + return empty; + } + } + + /// + /// + /// The current value of the property. + /// + internal Object Value { + get { + return value; + } + set { + this.value = value; + empty = false; + } + } + + /// + /// + /// Resets the property. + /// + internal void ResetValue() { + empty = true; + value = null; + } + } + + /// + /// MetafileDCWrapper is used to wrap a metafile DC so that subsequent + /// paint operations are rendered to a temporary bitmap. When the + /// wrapper is disposed, it copies the bitmap back to the metafile DC. + /// + /// Example: + /// + /// using(MetafileDCWrapper dcWrapper = new MetafileDCWrapper(hDC, size) { + /// // ...use dcWrapper.HDC to do painting + /// } + /// + /// + private class MetafileDCWrapper : IDisposable { + + HandleRef hBitmapDC = NativeMethods.NullHandleRef; + HandleRef hBitmap = NativeMethods.NullHandleRef; + HandleRef hOriginalBmp = NativeMethods.NullHandleRef; + HandleRef hMetafileDC = NativeMethods.NullHandleRef; + NativeMethods.RECT destRect; + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Process | ResourceScope.Machine)] + internal MetafileDCWrapper(HandleRef hOriginalDC, Size size) { + Debug.Assert(UnsafeNativeMethods.GetObjectType(hOriginalDC) == NativeMethods.OBJ_ENHMETADC, + "Why wrap a non-Enhanced MetaFile DC?"); + + // Security fix: make sure the size has non-negative width and height. + if (size.Width < 0 || size.Height < 0) { + throw new ArgumentException("size", SR.GetString(SR.ControlMetaFileDCWrapperSizeInvalid)); + } + + hMetafileDC = hOriginalDC; + destRect = new NativeMethods.RECT(0, 0, size.Width, size.Height); + hBitmapDC = new HandleRef(this, UnsafeNativeMethods.CreateCompatibleDC(NativeMethods.NullHandleRef)); + + int planes = UnsafeNativeMethods.GetDeviceCaps(hBitmapDC, NativeMethods.PLANES); + int bitsPixel = UnsafeNativeMethods.GetDeviceCaps(hBitmapDC, NativeMethods.BITSPIXEL); + hBitmap = new HandleRef(this, SafeNativeMethods.CreateBitmap(size.Width, size.Height, planes, bitsPixel, IntPtr.Zero)); + + hOriginalBmp = new HandleRef(this, SafeNativeMethods.SelectObject(hBitmapDC, hBitmap)); + } + + ~MetafileDCWrapper() { + ((IDisposable) this).Dispose(); + } + + void IDisposable.Dispose() { + if (hBitmapDC.Handle == IntPtr.Zero || hMetafileDC.Handle == IntPtr.Zero || hBitmap.Handle == IntPtr.Zero) { + return; + } + + bool success; + + try { + success = DICopy(hMetafileDC, hBitmapDC, destRect, true); + Debug.Assert(success, "DICopy() failed."); + SafeNativeMethods.SelectObject(hBitmapDC, hOriginalBmp); + success = SafeNativeMethods.DeleteObject(hBitmap); + Debug.Assert(success, "DeleteObject() failed."); + success = UnsafeNativeMethods.DeleteCompatibleDC(hBitmapDC); + Debug.Assert(success, "DeleteObject() failed."); + } + finally { + + // Dispose is done. Set all the handles to IntPtr.Zero so this way the Dispose method executes only once. + hBitmapDC = NativeMethods.NullHandleRef; + hBitmap = NativeMethods.NullHandleRef; + hOriginalBmp = NativeMethods.NullHandleRef; + + GC.SuppressFinalize(this); + } + } + + internal IntPtr HDC { + get { return hBitmapDC.Handle; } + } + + // ported form VB6 (Ctls\PortUtil\StdCtl.cpp:6176) + private unsafe bool DICopy(HandleRef hdcDest, HandleRef hdcSrc, NativeMethods.RECT rect, bool bStretch) { + long i; + + bool fSuccess = false; // Assume failure + + // + // Get the bitmap from the DC by selecting in a 1x1 pixel temp bitmap + HandleRef hNullBitmap = new HandleRef(this, SafeNativeMethods.CreateBitmap(1, 1, 1, 1, IntPtr.Zero)); + if (hNullBitmap.Handle == IntPtr.Zero) return fSuccess; + + try { + HandleRef hBitmap = new HandleRef(this, SafeNativeMethods.SelectObject(hdcSrc, hNullBitmap)); + if (hBitmap.Handle == IntPtr.Zero) return fSuccess; + + // + // Restore original bitmap + SafeNativeMethods.SelectObject(hdcSrc, hBitmap); + + + NativeMethods.BITMAP bmp = new NativeMethods.BITMAP(); + if (UnsafeNativeMethods.GetObject(hBitmap, Marshal.SizeOf(bmp), bmp) == 0) { + return fSuccess; + } + + NativeMethods.BITMAPINFO_FLAT lpbmi = new NativeMethods.BITMAPINFO_FLAT(); + lpbmi.bmiHeader_biSize = Marshal.SizeOf(typeof(NativeMethods.BITMAPINFOHEADER)); + lpbmi.bmiHeader_biWidth = bmp.bmWidth; + lpbmi.bmiHeader_biHeight = bmp.bmHeight; + lpbmi.bmiHeader_biPlanes = 1; + lpbmi.bmiHeader_biBitCount = bmp.bmBitsPixel; + lpbmi.bmiHeader_biCompression = NativeMethods.BI_RGB; + lpbmi.bmiHeader_biSizeImage = 0; //Not needed since using BI_RGB + lpbmi.bmiHeader_biXPelsPerMeter = 0; + lpbmi.bmiHeader_biYPelsPerMeter = 0; + lpbmi.bmiHeader_biClrUsed = 0; + lpbmi.bmiHeader_biClrImportant = 0; + lpbmi.bmiColors = new byte[NativeMethods.BITMAPINFO_MAX_COLORSIZE*4]; + + // + // Include the palette for 256 color bitmaps + long iColors = 1 << (bmp.bmBitsPixel * bmp.bmPlanes); + if (iColors <= 256) { + byte[] aj = new byte[Marshal.SizeOf(typeof(NativeMethods.PALETTEENTRY)) * 256]; + SafeNativeMethods.GetSystemPaletteEntries(hdcSrc, 0, (int)iColors, aj); + + fixed (byte* pcolors = lpbmi.bmiColors) { + fixed (byte* ppal = aj) { + NativeMethods.RGBQUAD* prgb = (NativeMethods.RGBQUAD*)pcolors; + NativeMethods.PALETTEENTRY* lppe = (NativeMethods.PALETTEENTRY*)ppal; + // + // Convert the palette entries to RGB quad entries + for (i = 0; i < (int)iColors; i++) { + prgb[i].rgbRed = lppe[i].peRed; + prgb[i].rgbBlue = lppe[i].peBlue; + prgb[i].rgbGreen = lppe[i].peGreen; + } + } + } + } + + // + // Allocate memory to hold the bitmap bits + long bitsPerScanLine = bmp.bmBitsPixel * (long)bmp.bmWidth; + long bytesPerScanLine = (bitsPerScanLine + 7) / 8; + long totalBytesReqd = bytesPerScanLine * bmp.bmHeight; + byte[] lpBits = new byte[totalBytesReqd]; + + // + // Get the bitmap bits + int diRet = SafeNativeMethods.GetDIBits(hdcSrc, hBitmap, 0, bmp.bmHeight, lpBits, + ref lpbmi, NativeMethods.DIB_RGB_COLORS); + if (diRet == 0) return fSuccess; + + // + // Set the destination coordiates depending on whether stretch-to-fit was chosen + int xDest, yDest, cxDest, cyDest; + if (bStretch) + { + xDest = rect.left; + yDest = rect.top; + cxDest = rect.right - rect.left; + cyDest = rect.bottom - rect.top; + } + else + { + xDest = rect.left; + yDest = rect.top; + cxDest = bmp.bmWidth; + cyDest = bmp.bmHeight; + } + + // Paint the bitmap + int iRet = SafeNativeMethods.StretchDIBits(hdcDest, + xDest, yDest, cxDest, cyDest, 0, 0, bmp.bmWidth, bmp.bmHeight, + lpBits, ref lpbmi, NativeMethods.DIB_RGB_COLORS, NativeMethods.SRCCOPY); + if (iRet == NativeMethods.GDI_ERROR) return fSuccess; + + fSuccess = true; + } + finally { + SafeNativeMethods.DeleteObject(hNullBitmap); + } + return fSuccess; + } + } + + + /// + /// + /// An implementation of AccessibleChild for use with Controls + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ControlAccessibleObject : AccessibleObject { + + private static IntPtr oleAccAvailable = NativeMethods.InvalidIntPtr; + + // Member variables + + private IntPtr handle = IntPtr.Zero; // Associated window handle (if any) + private Control ownerControl = null; // The associated Control for this AccessibleChild (if any) + private int[] runtimeId = null; // Used by UIAutomation + + // constructors + + /// + /// + /// [To be supplied.] + /// + public ControlAccessibleObject(Control ownerControl) { + + Debug.Assert(ownerControl != null, "Cannot construct a ControlAccessibleObject with a null ownerControl"); + if (ownerControl == null) { + throw new ArgumentNullException("ownerControl"); + } + + this.ownerControl = ownerControl; + + // call get_Handle outside the UnmanagedCode permission because if the handle is not created yet, + // we will invoke 3rd party HandleCreated event handlers + IntPtr handle = ownerControl.Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + this.Handle = handle; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// [To be supplied.] + /// + internal ControlAccessibleObject(Control ownerControl, int accObjId) { + + Debug.Assert(ownerControl != null, "Cannot construct a ControlAccessibleObject with a null ownerControl"); + if (ownerControl == null) { + throw new ArgumentNullException("ownerControl"); + } + + this.AccessibleObjectId = accObjId; // ...must set this *before* setting the Handle property + + this.ownerControl = ownerControl; + IntPtr handle = ownerControl.Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + this.Handle = handle; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// For container controls only, return array of child controls sorted into + /// tab index order. This gets applies to the list of child accessible objects + /// as returned by the system, so that we can present a meaningful order to + /// the user. The system defaults to z-order, which is bad for us because + /// that is usually the reverse of tab order! + /// + internal override int[] GetSysChildOrder() { + if (ownerControl.GetStyle(ControlStyles.ContainerControl)) + return ownerControl.GetChildWindowsInTabOrder(); + + return base.GetSysChildOrder(); + } + + /// + /// Perform custom navigation between parent/child/sibling accessible objects, + /// using tab index order as the guide, rather than letting the system default + /// behavior do navigation based on z-order. + /// + /// For a container control and its child controls, the accessible object tree + /// looks like this... + /// + /// [client area of container] + /// [non-client area of child #1] + /// [random non-client elements] + /// [client area of child #1] + /// [random non-client elements] + /// [non-client area of child #2] + /// [random non-client elements] + /// [client area of child #2] + /// [random non-client elements] + /// [non-client area of child #3] + /// [random non-client elements] + /// [client area of child #3] + /// [random non-client elements] + /// + /// We need to intercept first-child / last-child navigation from the container's + /// client object, and next-sibling / previous-sibling navigation from each child's + /// non-client object. All other navigation operations must be allowed to fall back + /// on the system's deafult behavior (provided by OLEACC.DLL). + /// + /// When combined with the re-ordering behavior of GetSysChildOrder() above, this + /// allows us to present the end user with the illusion of accessible objects in + /// tab index order, even though the system behavior only supports z-order. + /// + internal override bool GetSysChild(AccessibleNavigation navdir, out AccessibleObject accessibleObject) { + + // Clear the out parameter + accessibleObject = null; + + // Get the owning control's parent, if it has one + Control parentControl = ownerControl.ParentInternal; + + // ctrls[index] will indicate the control at the destination of this navigation operation + int index = -1; + Control[] ctrls = null; + + // Now handle any 'appropriate' navigation requests... + switch (navdir) { + + case AccessibleNavigation.FirstChild: + if (IsClientObject) { + ctrls = ownerControl.GetChildControlsInTabOrder(true); + index = 0; + } + break; + + case AccessibleNavigation.LastChild: + if (IsClientObject) { + ctrls = ownerControl.GetChildControlsInTabOrder(true); + index = ctrls.Length - 1; + } + break; + + case AccessibleNavigation.Previous: + if (IsNonClientObject && parentControl != null) { + ctrls = parentControl.GetChildControlsInTabOrder(true); + index = Array.IndexOf(ctrls, ownerControl); + if (index != -1) + --index; + } + break; + + case AccessibleNavigation.Next: + if (IsNonClientObject && parentControl != null) { + ctrls = parentControl.GetChildControlsInTabOrder(true); + index = Array.IndexOf(ctrls, ownerControl); + if (index != -1) + ++index; + } + break; + } + + // Unsupported navigation operation for this object, or unexpected error. + // Return false to force fall back on default system navigation behavior. + if (ctrls == null || ctrls.Length == 0) + return false; + + // If ctrls[index] is a valid control, return its non-client accessible object. + // If index is invalid, return null pointer meaning "end of list reached". + if (index >= 0 && index < ctrls.Length) + accessibleObject = ctrls[index].NcAccessibilityObject; + + // Return true to use the found accessible object and block default system behavior + return true; + } + + /// + public override string DefaultAction { + get { + string defaultAction = ownerControl.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + else { + return base.DefaultAction; + } + } + } + + // This is used only if control supports IAccessibleEx + internal override int[] RuntimeId { + get { + if (runtimeId == null) { + runtimeId = new int[2]; + runtimeId[0] = 0x2a; + runtimeId[1] = (int)(long)this.Handle; + } + + return runtimeId; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Description { + get { + string description = ownerControl.AccessibleDescription; + if (description != null) { + return description; + } + else { + return base.Description; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public IntPtr Handle { + + get { + return handle; + } + + set { + // Demand security permission for this! + IntSecurity.UnmanagedCode.Demand(); + + if (handle != value) { + handle = value; + + if (oleAccAvailable == IntPtr.Zero) { + return; + } + + bool freeLib = false; + + if (oleAccAvailable == NativeMethods.InvalidIntPtr) { + oleAccAvailable = UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable("oleacc.dll"); + freeLib = (oleAccAvailable != IntPtr.Zero); + } + + // Update systemIAccessible + // We need to store internally the system provided + // IAccessible, because some windows forms controls use it + // as the default IAccessible implementation. + if (handle != IntPtr.Zero && oleAccAvailable != IntPtr.Zero) { + UseStdAccessibleObjects(handle); + } + + if (freeLib) { + UnsafeNativeMethods.FreeLibrary(new HandleRef(null, oleAccAvailable)); + } + + } + } // end Handle.Set + + } // end Handle + + /// + /// + /// [To be supplied.] + /// + public override string Help { + get { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + return args.HelpString; + } + else { + return base.Help; + } + } + } + + /// + public override string KeyboardShortcut { + get { + // For controls, the default keyboard shortcut comes directly from the accessible + // name property. This matches the default behavior of OLEACC.DLL exactly. + char mnemonic = WindowsFormsUtils.GetMnemonic(this.TextLabel, false); + return (mnemonic == (char) 0) ? null : ("Alt+" + mnemonic); + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + // Special case: If an explicit name has been set in the AccessibleName property, use that. + // Note: Any non-null value in AccessibleName overrides the default accessible name logic, + // even an empty string (this is the only way to *force* the accessible name to be blank). + string name = ownerControl.AccessibleName; + if (name != null) { + return name; + } + + // Otherwise just return the default label string, minus any mnemonics + return WindowsFormsUtils.TextWithoutMnemonics(TextLabel); + } + + set { + // If anyone tries to set the accessible name, just cache the value in the control's + // AccessibleName property. This value will then end up overriding the normal accessible + // name logic, until such time as AccessibleName is set back to null. + ownerControl.AccessibleName = value; + } + } + + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return base.Parent; + } + } + + // Determine the text that should be used to 'label' this control for accessibility purposes. + // + // Prior to Whidbey, we just called 'base.Name' to determine the accessible name. This would end up calling + // OLEACC.DLL to determine the name. The rules used by OLEACC.DLL are the same as what we now have below, + // except that OLEACC searches for preceding labels using z-order, and we want to search for labels using + // TabIndex order. + // + internal string TextLabel { + get { + // If owning control allows, use its Text property - but only if that Text is not empty + if (ownerControl.GetStyle(ControlStyles.UseTextForAccessibility)) { + string text = ownerControl.Text; + if (!string.IsNullOrEmpty(text)) { + return text; + } + } + + // Otherwise use the text of the preceding Label control, if there is one + Label previousLabel = PreviousLabel; + if (previousLabel != null) { + string text = previousLabel.Text; + if (!string.IsNullOrEmpty(text)) { + return text; + } + } + + // This control has no discernable MSAA name - return an empty string to indiciate 'nameless'. + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public Control Owner { + get { + return ownerControl; + } + } + + // Look for a label immediately preceeding this control in + // the tab order, and use its name for the accessible name. + // + // This method aims to emulate the equivalent behavior in OLEACC.DLL, + // but walking controls in TabIndex order rather than native z-order. + // + internal Label PreviousLabel { + get { + // Try to get to the parent of this control. + Control parent = Owner.ParentInternal; + + if (parent == null) { + return null; + } + + // Find this control's containing control + ContainerControl container = parent.GetContainerControlInternal() as ContainerControl; + if (container == null) { + return null; + } + + // Walk backwards through peer controls... + for (Control previous = container.GetNextControl(Owner, false); + previous != null; + previous = container.GetNextControl(previous, false)) { + + // Stop when we hit a Label (whether visible or invisible) + if (previous is Label) { + return previous as Label; + } + + // Stop at any *visible* tab stop + if (previous.Visible && previous.TabStop) { + break; + } + } + + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = ownerControl.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return base.Role; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public override int GetHelpTopic(out string fileName) { + int topic = 0; + + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + + fileName = args.HelpNamespace; + if (!String.IsNullOrEmpty(fileName)) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + + try { + topic = Int32.Parse(args.HelpKeyword, CultureInfo.InvariantCulture); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + + return topic; + } + else { + return base.GetHelpTopic(out fileName); + } + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + ", accEvent = " + accEvent.ToString() + ", childID = self"); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), NativeMethods.OBJID_CLIENT, 0); + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent, int childID) { + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + + ", accEvent = " + accEvent.ToString() + + ", childID = " + childID.ToString(CultureInfo.InvariantCulture)); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), NativeMethods.OBJID_CLIENT, childID + 1); + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent, int objectID, int childID) { + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + + ", accEvent = " + accEvent.ToString() + + ", childID = " + childID.ToString(CultureInfo.InvariantCulture)); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), objectID, childID + 1); + } + + /// + /// Raises the LiveRegionChanged UIA event. + /// To make this method effective, the control must implement System.Windows.Forms.Automation.IAutomationLiveRegion interface + /// and its LiveSetting property must return either AutomationLiveSetting.Polite or AutomationLiveSetting.Assertive value. + /// In addition, the applications must be recompiled to target .NET Framework 4.7.3 or opt in into this feature using compatibility switches. + /// + /// True if operation succeeds, False otherwise. + public override bool RaiseLiveRegionChanged() { + if (!(this.Owner is IAutomationLiveRegion)) { + throw new InvalidOperationException(SR.GetString(SR.OwnerControlIsNotALiveRegion)); + } + + return RaiseAutomationEvent(NativeMethods.UIA_LiveRegionChangedEventId); + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3 && this.Owner is IAutomationLiveRegion) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_LiveSettingPropertyId && Owner is IAutomationLiveRegion) { + return ((IAutomationLiveRegion)Owner).LiveSetting; + } + + if (Owner.SupportsUiaProviders) { + switch (propertyID) { + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return Owner.CanSelect; + case NativeMethods.UIA_IsOffscreenPropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HelpTextPropertyId: + var help = Help; + return AccessibilityImprovements.Level3 ? (help ?? string.Empty) : help; + } + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, Handle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + if (Owner != null) { + return "ControlAccessibleObject: Owner = " + Owner.ToString(); + } + else { + return "ControlAccessibleObject: Owner = null"; + } + } + } + + + // Fonts can be a pain to track, so we wrap Hfonts in this class to get a Finalize method. + // + internal sealed class FontHandleWrapper : MarshalByRefObject, IDisposable { +#if DEBUG + private string stackOnCreate = null; + private string stackOnDispose = null; + private bool finalizing = false; +#endif + private IntPtr handle; + + internal FontHandleWrapper(Font font) { +#if DEBUG + if (CompModSwitches.LifetimeTracing.Enabled) stackOnCreate = new System.Diagnostics.StackTrace().ToString(); +#endif + handle = font.ToHfont(); + System.Internal.HandleCollector.Add(handle, NativeMethods.CommonHandles.GDI); + } + + internal IntPtr Handle { + get { + Debug.Assert(handle != IntPtr.Zero, "FontHandleWrapper disposed, but still being accessed"); + return handle; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) { + +#if DEBUG + Debug.Assert(finalizing || this != defaultFontHandleWrapper, "Don't dispose the defaultFontHandleWrapper"); +#endif + + if (handle != IntPtr.Zero) { +#if DEBUG + if (CompModSwitches.LifetimeTracing.Enabled) stackOnDispose = new System.Diagnostics.StackTrace().ToString(); +#endif + SafeNativeMethods.DeleteObject(new HandleRef(this, handle)); + handle = IntPtr.Zero; + } + + } + + ~FontHandleWrapper() { +#if DEBUG + finalizing = true; +#endif + Dispose(false); + } + } + + /// + /// Used with BeginInvoke/EndInvoke + /// + private class ThreadMethodEntry : IAsyncResult { + internal Control caller; + internal Control marshaler; + internal Delegate method; + internal Object[] args; + internal Object retVal; + internal Exception exception; + internal bool synchronous; + private bool isCompleted; + private ManualResetEvent resetEvent; + private object invokeSyncObject = new object(); + + // Store the execution context associated with the caller thread, and + // information about which thread actually got the stack applied to it. + // + internal ExecutionContext executionContext; + + // Optionally store the synchronization context associated with the callee thread. + // This overrides the sync context in the execution context of the caller thread. + // + internal SynchronizationContext syncContext = null; + + internal ThreadMethodEntry(Control caller, Control marshaler, Delegate method, Object[] args, bool synchronous, ExecutionContext executionContext) { + this.caller = caller; + this.marshaler = marshaler; + this.method = method; + this.args = args; + this.exception = null; + this.retVal = null; + this.synchronous = synchronous; + this.isCompleted = false; + this.resetEvent = null; + this.executionContext = executionContext; + } + + ~ThreadMethodEntry() + { + if (this.resetEvent != null) + { + this.resetEvent.Close(); + } + } + + public Object AsyncState { + get { + return null; + } + } + + public WaitHandle AsyncWaitHandle { + get { + if (this.resetEvent == null) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock (invokeSyncObject) { + // VSWhidbey 314175 BeginInvoke hangs on Multi-proc system: + // taking the lock prevents a race condition between IsCompleted + // boolean flag and resetEvent mutex in multiproc scenarios. + if (this.resetEvent == null) { + this.resetEvent = new ManualResetEvent(false); + if (this.isCompleted) { + this.resetEvent.Set(); + } + } + } + } + return(WaitHandle)this.resetEvent; + } + } + + public bool CompletedSynchronously { + get { + if (this.isCompleted && this.synchronous) + return true; + + return false; + } + } + + public bool IsCompleted { + get { + return this.isCompleted; + } + } + + internal void Complete() { + lock (invokeSyncObject) { + this.isCompleted = true; + if (this.resetEvent != null) { + this.resetEvent.Set(); + } + } + } + } + + private class ControlVersionInfo { + private string companyName = null; + private string productName = null; + private string productVersion = null; + private FileVersionInfo versionInfo = null; + private Control owner; + + internal ControlVersionInfo(Control owner) { + this.owner = owner; + } + + /// + /// + /// The company name associated with the component. + /// + internal string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (companyName == null) { + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attrs != null && attrs.Length > 0) { + companyName = ((AssemblyCompanyAttribute)attrs[0]).Company; + } + + if (companyName == null || companyName.Length == 0) { + companyName = GetFileVersionInfo().CompanyName; + if (companyName != null) { + companyName = companyName.Trim(); + } + } + + if (companyName == null || companyName.Length == 0) { + string ns = owner.GetType().Namespace; + + if (ns == null) { + ns = String.Empty; + } + + int firstDot = ns.IndexOf("/"); + if (firstDot != -1) { + companyName = ns.Substring(0, firstDot); + } + else { + companyName = ns; + } + } + } + return companyName; + } + } + + + /// + /// + /// The product name associated with this component. + /// + internal string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (productName == null) { + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attrs != null && attrs.Length > 0) { + productName = ((AssemblyProductAttribute)attrs[0]).Product; + } + + if (productName == null || productName.Length == 0) { + productName = GetFileVersionInfo().ProductName; + if (productName != null) { + productName = productName.Trim(); + } + } + + if (productName == null || productName.Length == 0) { + string ns = owner.GetType().Namespace; + + if (ns == null) { + ns = String.Empty; + } + int firstDot = ns.IndexOf("."); + if (firstDot != -1) { + productName = ns.Substring(firstDot+1); + } + else { + productName = ns; + } + } + } + + return productName; + } + } + + + /// + /// The product version associated with this component. + /// + internal string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (productVersion == null) { + // custom attribute + // + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); + if (attrs != null && attrs.Length > 0) { + productVersion = ((AssemblyInformationalVersionAttribute)attrs[0]).InformationalVersion; + } + + // win32 version info + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = GetFileVersionInfo().ProductVersion; + if (productVersion != null) { + productVersion = productVersion.Trim(); + } + } + + // fake it + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = "1.0.0.0"; + } + } + return productVersion; + } + } + + + /// + /// Retrieves the FileVersionInfo associated with the main module for + /// the component. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private FileVersionInfo GetFileVersionInfo() { + if (versionInfo == null) { + string path; + + // SECREVIEW : sec assert is safe, the module name is obtained from the system, no user input involded. + // + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + try { + path = owner.GetType().Module.FullyQualifiedName; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + // SECREVIEW : sec assert is safe, the file version is obtained from the system, no user input involded. + // + new FileIOPermission(FileIOPermissionAccess.Read, path).Assert(); + try { + versionInfo = FileVersionInfo.GetVersionInfo(path); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + return versionInfo; + + } + + } + + private sealed class MultithreadSafeCallScope : IDisposable + { + // Use local stack variable rather than a refcount since we're + // guaranteed that these 'scopes' are properly nested. + private bool resultedInSet; + + internal MultithreadSafeCallScope() + { + // Only access the thread-local stuff if we're going to be + // checking for illegal thread calling (no need to incur the + // expense otherwise). + if (checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall) + { + inCrossThreadSafeCall = true; + resultedInSet = true; + } + else + { + resultedInSet = false; + } + } + + void IDisposable.Dispose() + { + if (resultedInSet) + { + inCrossThreadSafeCall = false; + } + } + } + + private sealed class PrintPaintEventArgs : PaintEventArgs { + Message m; + + internal PrintPaintEventArgs(Message m, IntPtr dc, Rectangle clipRect) + : base(dc, clipRect) { + this.m = m; + } + + internal Message Message { + get { + return m; + } + } + } + + } // end class Control + +} // end namespace System.Windows.Forms + + + + + diff --git a/WindowsForms/Managed/System/WinForms/Control.cs.back b/WindowsForms/Managed/System/WinForms/Control.cs.back new file mode 100644 index 000000000..860a3f376 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Control.cs.back @@ -0,0 +1,20350 @@ +#define DEBUG_PREFERREDSIZE + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.Control+ActiveXFontMarshaler..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control, System.Windows.Forms.Message):System.Windows.Forms.PreProcessControlState")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.OnPreviewKeyDown(System.Windows.Forms.PreviewKeyDownEventArgs):System.Void")] + +/* + */ +namespace System.Windows.Forms { + using Accessibility; + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Configuration.Assemblies; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Globalization; + using System.Security.Permissions; + using System.Security; + using System.IO; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.Remoting; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Serialization.Formatters.Binary; + using System.Text; + using System.Threading; + using System.Windows.Forms.Design; + using System.Windows.Forms.Internal; + using Encoding = System.Text.Encoding; + using System.Drawing.Imaging; + using System.Windows.Forms.Layout; + using System.Runtime.Versioning; + using Automation; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using Collections.Generic; + + /// + /// + /// Defines the base class for controls, which are components + /// with visual representation. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Text"), + DefaultEvent("Click"), + Designer("System.Windows.Forms.Design.ControlDesigner, " + AssemblyRef.SystemDesign), + DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign), + ToolboxItemFilter("System.Windows.Forms") + ] + public partial class Control : + Component, + UnsafeNativeMethods.IOleControl, + UnsafeNativeMethods.IOleObject, + UnsafeNativeMethods.IOleInPlaceObject, + UnsafeNativeMethods.IOleInPlaceActiveObject, + UnsafeNativeMethods.IOleWindow, + UnsafeNativeMethods.IViewObject, + UnsafeNativeMethods.IViewObject2, + UnsafeNativeMethods.IPersist, + UnsafeNativeMethods.IPersistStreamInit, + UnsafeNativeMethods.IPersistPropertyBag, + UnsafeNativeMethods.IPersistStorage, + UnsafeNativeMethods.IQuickActivate, + ISupportOleDropSource, + IDropTarget, + ISynchronizeInvoke, + IWin32Window, + IArrangedElement, + IBindableComponent, + IKeyboardToolTip { + +#if FINALIZATION_WATCH + static readonly TraceSwitch ControlFinalization = new TraceSwitch("ControlFinalization", "Tracks the creation and destruction of finalization"); + internal static string GetAllocationStack() { + if (ControlFinalization.TraceVerbose) { + // SECREVIEW: This assert is safe, the operation is safe (data obtained from the CLR). This code is for debugging purposes only. + // Tracked by VSWhidbey #426446. + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + return Environment.StackTrace; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + else { + return "Enable 'ControlFinalization' switch to see stack of allocation"; + } + + } + private string allocationSite = Control.GetAllocationStack(); +#endif + +#if DEBUG + internal static readonly TraceSwitch PaletteTracing = new TraceSwitch("PaletteTracing", "Debug Palette code"); + internal static readonly TraceSwitch ControlKeyboardRouting = new TraceSwitch("ControlKeyboardRouting", "Debug Keyboard routing for controls"); + internal static readonly TraceSwitch FocusTracing = new TraceSwitch("FocusTracing", "Debug focus/active control/enter/leave"); + private static readonly BooleanSwitch AssertOnControlCreateSwitch = new BooleanSwitch("AssertOnControlCreate", "Assert when anything directly deriving from control is created."); + internal static readonly BooleanSwitch TraceMnemonicProcessing = new BooleanSwitch("TraceCanProcessMnemonic", "Trace mnemonic processing calls to assure right child-parent call ordering."); + + internal void TraceCanProcessMnemonic(){ + if( TraceMnemonicProcessing.Enabled ){ + string str; + try { + str = string.Format( CultureInfo.CurrentCulture, "{0}<{1}>", this.GetType().Name, this.Text ); + int maxFrameCount = new StackTrace().FrameCount; + if (maxFrameCount > 5) { + maxFrameCount = 5; + } + int frameIndex = 1; + while( frameIndex < maxFrameCount) { + StackFrame sf = new StackFrame(frameIndex); + if( frameIndex == 2 && sf.GetMethod().Name.Equals("CanProcessMnemonic") ){ // log immediate call if in a virtual/recursive call. + break; + } + str += new StackTrace(sf).ToString().TrimEnd(); + frameIndex++; + } + if( frameIndex > 2 ){ // new CanProcessMnemonic virtual/recursive call stack. + str = "\r\n" + str; + } + } + catch (Exception ex) { + str = ex.ToString(); + } + Debug.WriteLine( str ); + } + } +#else + internal static readonly TraceSwitch ControlKeyboardRouting; + internal static readonly TraceSwitch PaletteTracing; + internal static readonly TraceSwitch FocusTracing; +#endif + +#if DEBUG + internal static readonly BooleanSwitch BufferPinkRect = new BooleanSwitch("BufferPinkRect", "Renders a pink rectangle with painting double buffered controls"); + internal static readonly BooleanSwitch BufferDisabled = new BooleanSwitch("BufferDisabled", "Makes double buffered controls non-double buffered"); +#else + internal static readonly BooleanSwitch BufferPinkRect; +#endif + + private static int WM_GETCONTROLNAME; + private static int WM_GETCONTROLTYPE; + + static Control() { + WM_GETCONTROLNAME = SafeNativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME"); + WM_GETCONTROLTYPE = SafeNativeMethods.RegisterWindowMessage("WM_GETCONTROLTYPE"); + + } + + internal const int STATE_CREATED = 0x00000001; + internal const int STATE_VISIBLE = 0x00000002; + internal const int STATE_ENABLED = 0x00000004; + internal const int STATE_TABSTOP = 0x00000008; + internal const int STATE_RECREATE = 0x00000010; + internal const int STATE_MODAL = 0x00000020; + internal const int STATE_ALLOWDROP = 0x00000040; + internal const int STATE_DROPTARGET = 0x00000080; + internal const int STATE_NOZORDER = 0x00000100; + internal const int STATE_LAYOUTDEFERRED = 0x00000200; + internal const int STATE_USEWAITCURSOR = 0x00000400; + internal const int STATE_DISPOSED = 0x00000800; + internal const int STATE_DISPOSING = 0x00001000; + internal const int STATE_MOUSEENTERPENDING = 0x00002000; + internal const int STATE_TRACKINGMOUSEEVENT = 0x00004000; + internal const int STATE_THREADMARSHALLPENDING = 0x00008000; + internal const int STATE_SIZELOCKEDBYOS = 0x00010000; + internal const int STATE_CAUSESVALIDATION = 0x00020000; + internal const int STATE_CREATINGHANDLE = 0x00040000; + internal const int STATE_TOPLEVEL = 0x00080000; + internal const int STATE_ISACCESSIBLE = 0x00100000; + internal const int STATE_OWNCTLBRUSH = 0x00200000; + internal const int STATE_EXCEPTIONWHILEPAINTING = 0x00400000; + internal const int STATE_LAYOUTISDIRTY = 0x00800000; + internal const int STATE_CHECKEDHOST = 0x01000000; + internal const int STATE_HOSTEDINDIALOG = 0x02000000; + internal const int STATE_DOUBLECLICKFIRED = 0x04000000; + internal const int STATE_MOUSEPRESSED = 0x08000000; + internal const int STATE_VALIDATIONCANCELLED = 0x10000000; + internal const int STATE_PARENTRECREATING = 0x20000000; + internal const int STATE_MIRRORED = 0x40000000; + + // HACK HACK HACK - when we change RightToLeft, we need to change the scrollbar thumb. + // We can't do that until after the control has been created, and all the items added + // back. This is because the system control won't know the nMin and nMax of the scroll + // bar until the items are added. So in RightToLeftChanged, we set a flag that indicates + // that we want to set the scroll position. In OnHandleCreated we check this flag, + // and if set, we BeginInvoke. We have to BeginInvoke since we have to wait until the items + // are added. We only want to do this when RightToLeft changes thus the flags + // STATE2_HAVEINVOKED and STATE2_SETSCROLLPOS. Otherwise we would do this on each HandleCreated. + private const int STATE2_HAVEINVOKED = 0x00000001; + private const int STATE2_SETSCROLLPOS = 0x00000002; + private const int STATE2_LISTENINGTOUSERPREFERENCECHANGED = 0x00000004; // set when the control is listening to SystemEvents.UserPreferenceChanged. + internal const int STATE2_INTERESTEDINUSERPREFERENCECHANGED = 0x00000008; // if set, the control will listen to SystemEvents.UserPreferenceChanged when TopLevel is true and handle is created. + internal const int STATE2_MAINTAINSOWNCAPTUREMODE = 0x00000010; // if set, the control DOES NOT necessarily take capture on MouseDown + private const int STATE2_BECOMINGACTIVECONTROL = 0x00000020; // set to true by ContainerControl when this control is becoming its active control + + private const int STATE2_CLEARLAYOUTARGS = 0x00000040; // if set, the next time PerformLayout is called, cachedLayoutEventArg will be cleared. + private const int STATE2_INPUTKEY = 0x00000080; + private const int STATE2_INPUTCHAR = 0x00000100; + private const int STATE2_UICUES = 0x00000200; + private const int STATE2_ISACTIVEX = 0x00000400; + internal const int STATE2_USEPREFERREDSIZECACHE = 0x00000800; + internal const int STATE2_TOPMDIWINDOWCLOSING = 0x00001000; + internal const int STATE2_CURRENTLYBEINGSCALED = 0x00002000; // if set, the control is being scaled, currently + + private static readonly object EventAutoSizeChanged = new object(); + private static readonly object EventKeyDown = new object(); + private static readonly object EventKeyPress = new object(); + private static readonly object EventKeyUp = new object(); + private static readonly object EventMouseDown = new object(); + private static readonly object EventMouseEnter = new object(); + private static readonly object EventMouseLeave = new object(); + private static readonly object EventDpiChangedBeforeParent = new object(); + private static readonly object EventDpiChangedAfterParent = new object(); + private static readonly object EventMouseHover = new object(); + private static readonly object EventMouseMove = new object(); + private static readonly object EventMouseUp = new object(); + private static readonly object EventMouseWheel = new object(); + private static readonly object EventClick = new object(); + private static readonly object EventClientSize = new object(); + private static readonly object EventDoubleClick = new object(); + private static readonly object EventMouseClick = new object(); + private static readonly object EventMouseDoubleClick = new object(); + private static readonly object EventMouseCaptureChanged = new object(); + private static readonly object EventMove = new object(); + private static readonly object EventResize = new object(); + private static readonly object EventLayout = new object(); + private static readonly object EventGotFocus = new object(); + private static readonly object EventLostFocus = new object(); + private static readonly object EventEnabledChanged = new object(); + private static readonly object EventEnter = new object(); + private static readonly object EventLeave = new object(); + private static readonly object EventHandleCreated = new object(); + private static readonly object EventHandleDestroyed = new object(); + private static readonly object EventVisibleChanged = new object(); + private static readonly object EventControlAdded = new object(); + private static readonly object EventControlRemoved = new object(); + private static readonly object EventChangeUICues = new object(); + private static readonly object EventSystemColorsChanged = new object(); + private static readonly object EventValidating = new object(); + private static readonly object EventValidated = new object(); + private static readonly object EventStyleChanged = new object(); + private static readonly object EventImeModeChanged = new object(); + private static readonly object EventHelpRequested = new object(); + private static readonly object EventPaint = new object(); + private static readonly object EventInvalidated = new object(); + private static readonly object EventQueryContinueDrag = new object(); + private static readonly object EventGiveFeedback = new object(); + private static readonly object EventDragEnter = new object(); + private static readonly object EventDragLeave = new object(); + private static readonly object EventDragOver = new object(); + private static readonly object EventDragDrop = new object(); + private static readonly object EventQueryAccessibilityHelp = new object(); + private static readonly object EventBackgroundImage = new object(); + private static readonly object EventBackgroundImageLayout = new object(); + private static readonly object EventBindingContext = new object(); + private static readonly object EventBackColor = new object(); + private static readonly object EventParent = new object(); + private static readonly object EventVisible = new object(); + private static readonly object EventText = new object(); + private static readonly object EventTabStop = new object(); + private static readonly object EventTabIndex = new object(); + private static readonly object EventSize = new object(); + private static readonly object EventRightToLeft = new object(); + private static readonly object EventLocation = new object(); + private static readonly object EventForeColor = new object(); + private static readonly object EventFont = new object(); + private static readonly object EventEnabled = new object(); + private static readonly object EventDock = new object(); + private static readonly object EventCursor = new object(); + private static readonly object EventContextMenu = new object(); + private static readonly object EventContextMenuStrip = new object(); + private static readonly object EventCausesValidation = new object(); + private static readonly object EventRegionChanged = new object(); + private static readonly object EventMarginChanged = new object(); + internal static readonly object EventPaddingChanged = new object(); + private static readonly object EventPreviewKeyDown = new object(); + + #if WIN95_SUPPORT + private static int mouseWheelMessage = NativeMethods.WM_MOUSEWHEEL; + private static bool mouseWheelRoutingNeeded; + private static bool mouseWheelInit; + #endif + + private static int threadCallbackMessage; + + // Initially check for illegal multithreading based on whether the + // debugger is attached. + [ResourceExposure(ResourceScope.Process)] + private static bool checkForIllegalCrossThreadCalls = Debugger.IsAttached; + private static ContextCallback invokeMarshaledCallbackHelperDelegate; + + [ ThreadStatic ] + private static bool inCrossThreadSafeCall = false; + +// disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + [ ThreadStatic ] + internal static HelpInfo currentHelpInfo = null; +#pragma warning restore 0414 + + private static FontHandleWrapper defaultFontHandleWrapper; + + private const short PaintLayerBackground = 1; + private const short PaintLayerForeground = 2; + + private const byte RequiredScalingEnabledMask = 0x10; + private const byte RequiredScalingMask = 0x0F; + + private const byte HighOrderBitMask = 0x80; + + private static Font defaultFont; + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + // + private static readonly int PropName = PropertyStore.CreateKey(); + private static readonly int PropBackBrush = PropertyStore.CreateKey(); + private static readonly int PropFontHeight = PropertyStore.CreateKey(); + private static readonly int PropCurrentAmbientFont = PropertyStore.CreateKey(); + + private static readonly int PropControlsCollection = PropertyStore.CreateKey(); + private static readonly int PropBackColor = PropertyStore.CreateKey(); + private static readonly int PropForeColor = PropertyStore.CreateKey(); + private static readonly int PropFont = PropertyStore.CreateKey(); + + private static readonly int PropBackgroundImage = PropertyStore.CreateKey(); + private static readonly int PropFontHandleWrapper = PropertyStore.CreateKey(); + private static readonly int PropUserData = PropertyStore.CreateKey(); + private static readonly int PropContextMenu = PropertyStore.CreateKey(); + + private static readonly int PropCursor = PropertyStore.CreateKey(); + private static readonly int PropRegion = PropertyStore.CreateKey(); + private static readonly int PropRightToLeft = PropertyStore.CreateKey(); + + private static readonly int PropBindings = PropertyStore.CreateKey(); + private static readonly int PropBindingManager = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDefaultActionDescription = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDescription = PropertyStore.CreateKey(); + + private static readonly int PropAccessibility = PropertyStore.CreateKey(); + private static readonly int PropNcAccessibility = PropertyStore.CreateKey(); + private static readonly int PropAccessibleName = PropertyStore.CreateKey(); + private static readonly int PropAccessibleRole = PropertyStore.CreateKey(); + + private static readonly int PropPaintingException = PropertyStore.CreateKey(); + private static readonly int PropActiveXImpl = PropertyStore.CreateKey(); + private static readonly int PropControlVersionInfo = PropertyStore.CreateKey(); + private static readonly int PropBackgroundImageLayout = PropertyStore.CreateKey(); + + private static readonly int PropAccessibleHelpProvider = PropertyStore.CreateKey(); + private static readonly int PropContextMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropAutoScrollOffset = PropertyStore.CreateKey(); + private static readonly int PropUseCompatibleTextRendering = PropertyStore.CreateKey(); + + private static readonly int PropImeWmCharsToIgnore = PropertyStore.CreateKey(); + private static readonly int PropImeMode = PropertyStore.CreateKey(); + private static readonly int PropDisableImeModeChangedCount = PropertyStore.CreateKey(); + private static readonly int PropLastCanEnableIme = PropertyStore.CreateKey(); + + private static readonly int PropCacheTextCount = PropertyStore.CreateKey(); + private static readonly int PropCacheTextField = PropertyStore.CreateKey(); + private static readonly int PropAmbientPropertiesService = PropertyStore.CreateKey(); + + private static bool needToLoadComCtl = true; + + // This switch determines the default text rendering engine to use by some controls that support switching rendering engine. + // CheckedListBox, PropertyGrid, GroupBox, Label and LinkLabel, and ButtonBase controls. + // True means use GDI+, false means use GDI (TextRenderer). + internal static bool UseCompatibleTextRenderingDefault = true; + + /////////////////////////////////////////////////////////////////////// + // Control per instance members + // + // Note: Do not add anything to this list unless absolutely neccessary. + // Every control on a form has the overhead of all of these + // variables! + // + // Begin Members { + + // List of properties that are generally set, so we keep them directly on + // control. + // + + // Resist the temptation to make this variable 'internal' rather than + // private. Handle access should be tightly controlled, and is in this + // file. Making it 'internal' makes controlling it quite difficult. + private ControlNativeWindow window; + + private Control parent; + private Control reflectParent; + private CreateParams createParams; + private int x; // + private int y; + private int width; + private int height; + private int clientWidth; + private int clientHeight; + private int state; // See STATE_ constants above + private int state2; // See STATE2_ constants above + private ControlStyles controlStyle; // User supplied control style + private int tabIndex; + private string text; // See ControlStyles.CacheText for usage notes + private byte layoutSuspendCount; + private byte requiredScaling; // bits 0-4: BoundsSpecified stored in RequiredScaling property. Bit 5: RequiredScalingEnabled property. + private PropertyStore propertyStore; // Contains all properties that are not always set. + private NativeMethods.TRACKMOUSEEVENT trackMouseEvent; + private short updateCount; + private LayoutEventArgs cachedLayoutEventArgs; + private Queue threadCallbackList; + internal int deviceDpi; + + + // for keeping track of our ui state for focus and keyboard cues. using a member variable + // here because we hit this a lot + // + private int uiCuesState; + + private const int UISTATE_FOCUS_CUES_MASK = 0x000F; + private const int UISTATE_FOCUS_CUES_HIDDEN = 0x0001; + private const int UISTATE_FOCUS_CUES_SHOW = 0x0002; + private const int UISTATE_KEYBOARD_CUES_MASK = 0x00F0; + private const int UISTATE_KEYBOARD_CUES_HIDDEN = 0x0010; + private const int UISTATE_KEYBOARD_CUES_SHOW = 0x0020; + + [ThreadStatic] + private static byte[] tempKeyboardStateArray; + + // } End Members + /////////////////////////////////////////////////////////////////////// + +#if DEBUG + internal int LayoutSuspendCount { + get { return layoutSuspendCount; } + } + + internal void AssertLayoutSuspendCount(int value) { + Debug.Assert(value == layoutSuspendCount, "Suspend/Resume layout mismatch!"); + } + +/* +example usage + +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif +*/ + +#endif + + /// + /// + /// Initializes a new instance of the class. + /// + public Control() : this(true) { + } + + /// + /// + internal Control(bool autoInstallSyncContext) : base() { +#if DEBUG + if (AssertOnControlCreateSwitch.Enabled) { + Debug.Assert(this.GetType().BaseType != typeof(Control), "Direct derivative of Control Created: " + this.GetType().FullName); + Debug.Assert(this.GetType() != typeof(Control), "Control Created!"); + } +#endif + propertyStore = new PropertyStore(); + + DpiHelper.InitializeDpiHelperForWinforms(); + // Initialize DPI to the value on the primary screen, we will have the correct value when the Handle is created. + deviceDpi = DpiHelper.DeviceDpi; + + window = new ControlNativeWindow(this); + RequiredScalingEnabled = true; + RequiredScaling = BoundsSpecified.All; + tabIndex = -1; + + state = STATE_VISIBLE | STATE_ENABLED | STATE_TABSTOP | STATE_CAUSESVALIDATION; + state2 = STATE2_INTERESTEDINUSERPREFERENCECHANGED; + SetStyle(ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint | + ControlStyles.StandardClick | + ControlStyles.StandardDoubleClick | + ControlStyles.UseTextForAccessibility | + ControlStyles.Selectable,true); + +#if WIN95_SUPPORT + InitMouseWheelSupport(); +#endif + + // We baked the "default default" margin and min size into CommonProperties + // so that in the common case the PropertyStore would be empty. If, however, + // someone overrides these Default* methads, we need to write the default + // value into the PropertyStore in the ctor. + // + if(DefaultMargin != CommonProperties.DefaultMargin) { + Margin = DefaultMargin; + } + if(DefaultMinimumSize != CommonProperties.DefaultMinimumSize) { + MinimumSize = DefaultMinimumSize; + } + if(DefaultMaximumSize != CommonProperties.DefaultMaximumSize) { + MaximumSize = DefaultMaximumSize; + } + + // Compute our default size. + // + Size defaultSize = DefaultSize; + width = defaultSize.Width; + height = defaultSize.Height; + + // DefaultSize may have hit GetPreferredSize causing a PreferredSize to be cached. The + // PreferredSize may change as a result of the current size. Since a SetBoundsCore did + // not happen, so we need to clear the preferredSize cache manually. + CommonProperties.xClearPreferredSizeCache(this); + + if (width != 0 && height != 0) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + rect.left = rect.right = rect.top = rect.bottom = 0; + + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle); + clientWidth = width - (rect.right - rect.left); + clientHeight = height - (rect.bottom - rect.top); + } + + + // Set up for async operations on this thread. + if (autoInstallSyncContext) { + WindowsFormsSynchronizationContext.InstallIfNeeded(); + } + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( string text ) : this( (Control) null, text ) { + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( string text, int left, int top, int width, int height ) : + this( (Control) null, text, left, top, width, height ) { + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( Control parent, string text ) : this() { + this.Parent = parent; + this.Text = text; + } + + /// + /// + /// Initializes a new instance of the class. + /// + public Control( Control parent, string text, int left, int top, int width, int height ) : this( parent, text ) { + this.Location = new Point( left, top ); + this.Size = new Size( width, height ); + } + + /// + /// gets or sets control Dpi awareness context value. + /// + internal DpiAwarenessContext DpiAwarenessContext { + get { + return window.DpiAwarenessContext; + } + } + + /// + /// + /// The Accessibility Object for this Control + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAccessibilityObjectDescr) + ] + public AccessibleObject AccessibilityObject { + get { + AccessibleObject accessibleObject = (AccessibleObject)Properties.GetObject(PropAccessibility); + if (accessibleObject == null) { + accessibleObject = CreateAccessibilityInstance(); + // this is a security check. we want to enforce that we only return + // ControlAccessibleObject and not some other derived class + if(!(accessibleObject is ControlAccessibleObject)) { + Debug.Fail("Accessible objects for controls must be derived from ControlAccessibleObject."); + return null; + } + Properties.SetObject(PropAccessibility, accessibleObject); + } + + Debug.Assert(accessibleObject != null, "Failed to create accessibility object"); + return accessibleObject; + } + } + + /// + /// Private accessibility object for control, used to wrap the object that + /// OLEACC.DLL creates to represent the control's non-client (NC) region. + /// + private AccessibleObject NcAccessibilityObject { + get { + AccessibleObject ncAccessibleObject = (AccessibleObject)Properties.GetObject(PropNcAccessibility); + if (ncAccessibleObject == null) { + ncAccessibleObject = new ControlAccessibleObject(this, NativeMethods.OBJID_WINDOW); + Properties.SetObject(PropNcAccessibility, ncAccessibleObject); + } + + Debug.Assert(ncAccessibleObject != null, "Failed to create NON-CLIENT accessibility object"); + return ncAccessibleObject; + } + } + + /// + /// Returns a specific AccessibleObject associated with this + /// control, based on standard "accessibile object id". + /// + /// + private AccessibleObject GetAccessibilityObject(int accObjId) { + AccessibleObject accessibleObject; + + switch (accObjId) { + case NativeMethods.OBJID_CLIENT: + accessibleObject = this.AccessibilityObject; + break; + case NativeMethods.OBJID_WINDOW: + accessibleObject = this.NcAccessibilityObject; + break; + default: + if (accObjId > 0) { + accessibleObject = this.GetAccessibilityObjectById(accObjId); + } else { + accessibleObject = null; + } + break; + } + + return accessibleObject; + } + + /// + /// + /// Returns a specific AccessibleObbject associated w/ the objectID + /// + protected virtual AccessibleObject GetAccessibilityObjectById(int objectId) { + if (AccessibilityImprovements.Level3 && this is IAutomationLiveRegion) { + return this.AccessibilityObject; + } + + return null; + } + + /// + /// + /// The default action description of the control + /// + [ + SRCategory(SR.CatAccessibility), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAccessibleDefaultActionDescr) + ] + public string AccessibleDefaultActionDescription { + get { + return (string)Properties.GetObject(PropAccessibleDefaultActionDescription); + } + set { + Properties.SetObject(PropAccessibleDefaultActionDescription, value); + } + } + + /// + /// + /// The accessible description of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlAccessibleDescriptionDescr) + ] + public string AccessibleDescription { + get { + return (string)Properties.GetObject(PropAccessibleDescription); + } + set { + Properties.SetObject(PropAccessibleDescription, value); + } + } + + /// + /// + /// The accessible name of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlAccessibleNameDescr) + ] + public string AccessibleName { + get { + return (string)Properties.GetObject(PropAccessibleName); + } + + set { + Properties.SetObject(PropAccessibleName, value); + } + } + + /// + /// + /// The accessible role of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AccessibleRole.Default), + SRDescription(SR.ControlAccessibleRoleDescr) + ] + public AccessibleRole AccessibleRole { + + get { + bool found; + int role = Properties.GetInteger(PropAccessibleRole, out found); + if (found) { + return (AccessibleRole)role; + } + else { + return AccessibleRole.Default; + } + } + + set { + //valid values are -1 to 0x40 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AccessibleRole.Default, (int)AccessibleRole.OutlineButton)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AccessibleRole)); + } + Properties.SetInteger(PropAccessibleRole, (int)value); + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Color ActiveXAmbientBackColor { + get { + return ActiveXInstance.AmbientBackColor; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Color ActiveXAmbientForeColor { + get { + return ActiveXInstance.AmbientForeColor; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private Font ActiveXAmbientFont { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + return ActiveXInstance.AmbientFont; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private bool ActiveXEventsFrozen { + get { + return ActiveXInstance.EventsFrozen; + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private IntPtr ActiveXHWNDParent { + get { + return ActiveXInstance.HWNDParent; + } + } + + /// + /// Retrieves the ActiveX control implementation for + /// this control. This will demand create the implementation + /// if it does not already exist. + /// + private ActiveXImpl ActiveXInstance { + get { + ActiveXImpl activeXImpl = (ActiveXImpl)Properties.GetObject(PropActiveXImpl); + if (activeXImpl == null) { + + // Don't allow top level objects to be hosted + // as activeX controls. + // + if (GetState(STATE_TOPLEVEL)) { + throw new NotSupportedException(SR.GetString(SR.AXTopLevelSource)); + } + + activeXImpl = new ActiveXImpl(this); + + // PERF: IsActiveX is called quite a bit - checked everywhere from sizing to event raising. Using a state + // bit to track PropActiveXImpl instead of fetching from the property store. + SetState2(STATE2_ISACTIVEX, true); + Properties.SetObject(PropActiveXImpl, activeXImpl); + } + + return activeXImpl; + } + } + + /// + /// + /// The AllowDrop property. If AllowDrop is set to true then + /// this control will allow drag and drop operations and events to be used. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ControlAllowDropDescr) + ] + public virtual bool AllowDrop { + get { + return GetState(STATE_ALLOWDROP); + } + + set { + if (GetState(STATE_ALLOWDROP) != value) { + // Since we won't call SetAcceptDrops without a handle, + // we do the security demand here. Without this demand + // we are still safe, but you get the exception at an + // odd time. This gives a better experience. + // + if (value && !IsHandleCreated) { + IntSecurity.ClipboardRead.Demand(); + } + + SetState(STATE_ALLOWDROP, value); + + if (IsHandleCreated) { + try { + SetAcceptDrops(value); + } + catch { + // If there is an error, back out the AllowDrop state... + // + SetState(STATE_ALLOWDROP, !value); + throw; + } + } + } + } + } + + // Queries the Site for AmbientProperties. May return null. + // Do not confuse with inheritedProperties -- the service is turned to + // after we've exhausted inheritedProperties. + private AmbientProperties AmbientPropertiesService { + get { + bool contains; + AmbientProperties props = (AmbientProperties)Properties.GetObject(PropAmbientPropertiesService, out contains); + if (!contains) { + if (Site != null) { + props = (AmbientProperties)Site.GetService(typeof(AmbientProperties)); + } + else { + props = (AmbientProperties)GetService(typeof(AmbientProperties)); + } + + if (props != null) { + Properties.SetObject(PropAmbientPropertiesService, props); + } + } + return props; + } + } + + /// + /// + /// The current value of the anchor property. The anchor property + /// determines which edges of the control are anchored to the container's + /// edges. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + DefaultValue(CommonProperties.DefaultAnchor), + SRDescription(SR.ControlAnchorDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual AnchorStyles Anchor { + get { + return DefaultLayout.GetAnchor(this); + } + set { + DefaultLayout.SetAnchor(ParentInternal, this, value); + } + } + + /// + [SRCategory(SR.CatLayout)] + [RefreshProperties(RefreshProperties.All)] + [Localizable(true)] + [DefaultValue(CommonProperties.DefaultAutoSize)] + [SRDescription(SR.ControlAutoSizeDescr)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool AutoSize { + get { return CommonProperties.GetAutoSize(this); } + set { + if(value != AutoSize) { + CommonProperties.SetAutoSize(this, value); + if(ParentInternal != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(value && ParentInternal.LayoutEngine == DefaultLayout.Instance) { + ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize); + } + + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler AutoSizeChanged { + add { + Events.AddHandler(EventAutoSizeChanged, value); + } + remove { + Events.RemoveHandler(EventAutoSizeChanged, value); + } + } + + /// + /// Controls the location of where this control is scrolled to in ScrollableControl.ScrollControlIntoView. + /// Default is the upper left hand corner of the control. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(typeof(Point), "0, 0") + ] + public virtual Point AutoScrollOffset { + get { + if (Properties.ContainsObject(PropAutoScrollOffset)) { + return (Point)Properties.GetObject(PropAutoScrollOffset); + } + return Point.Empty; + } + set { + if (AutoScrollOffset != value) { + Properties.SetObject(PropAutoScrollOffset, value); + } + } + } + + protected void SetAutoSizeMode(AutoSizeMode mode) { + CommonProperties.SetAutoSizeMode(this, mode); + } + + protected AutoSizeMode GetAutoSizeMode() { + return CommonProperties.GetAutoSizeMode(this); + } + + /// + // Public because this is interesting for ControlDesigners. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual LayoutEngine LayoutEngine { + get { return DefaultLayout.Instance; } + } + + /// + /// The GDI brush for our background color. + /// Whidbey Note: Made this internal, since we need to use this in ButtonStandardAdapter. Also, renamed + /// from BackBrush to BackColorBrush due to a naming conflict with DataGrid's BackBrush. + /// + internal IntPtr BackColorBrush { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + object customBackBrush = Properties.GetObject(PropBackBrush); + if (customBackBrush != null) { + + // We already have a valid brush. Unbox, and return. + // + return (IntPtr)customBackBrush; + } + + if (!Properties.ContainsObject(PropBackColor)) { + + // No custom back color. See if we can get to our parent. + // The color check here is to account for parents and children who + // override the BackColor property. + // + if (parent != null && parent.BackColor == BackColor) { + return parent.BackColorBrush; + } + } + + // No parent, or we have a custom back color. Either way, we need to + // create our own. + // + Color color = BackColor; + IntPtr backBrush; + + if (ColorTranslator.ToOle(color) < 0) { + backBrush = SafeNativeMethods.GetSysColorBrush(ColorTranslator.ToOle(color) & 0xFF); + SetState(STATE_OWNCTLBRUSH, false); + } + else { + backBrush = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(color)); + SetState(STATE_OWNCTLBRUSH, true); + } + + Debug.Assert(backBrush != IntPtr.Zero, "Failed to create brushHandle"); + Properties.SetObject(PropBackBrush, backBrush); + + return backBrush; + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_BACKCOLOR), + SRDescription(SR.ControlBackColorDescr) + ] + public virtual Color BackColor { + get { + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) { + return c; + } + + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + c = p.BackColor; + if (IsValidBackColor(c)) + { + return c; + } + + } + + if (IsActiveX) { + c = ActiveXAmbientBackColor; + } + + if (c.IsEmpty) { + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null) + c = ambient.BackColor; + } + + if (!c.IsEmpty && IsValidBackColor(c)) + return c; + else + return DefaultBackColor; + } + + set { + if (!value.Equals(Color.Empty) && !GetStyle(ControlStyles.SupportsTransparentBackColor) && value.A < 255) + throw new ArgumentException(SR.GetString(SR.TransparentBackColorNotAllowed)); + + Color c = BackColor; + if (!value.IsEmpty || Properties.ContainsObject(PropBackColor)) { + Properties.SetColor(PropBackColor, value); + } + + if (!c.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackColorChangedDescr)] + public event EventHandler BackColorChanged { + add { + Events.AddHandler(EventBackColor, value); + } + remove { + Events.RemoveHandler(EventBackColor, value); + } + } + + /// + /// + /// The background image of the control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ControlBackgroundImageDescr) + ] + public virtual Image BackgroundImage { + get { + return (Image)Properties.GetObject(PropBackgroundImage); + } + set { + if (BackgroundImage != value) { + Properties.SetObject(PropBackgroundImage, value); + OnBackgroundImageChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackgroundImageChangedDescr)] + public event EventHandler BackgroundImageChanged { + add { + Events.AddHandler(EventBackgroundImage, value); + } + remove { + Events.RemoveHandler(EventBackgroundImage, value); + } + } + + /// + /// + /// The BackgroundImageLayout of the control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public virtual ImageLayout BackgroundImageLayout { + get { + bool found = Properties.ContainsObject(PropBackgroundImageLayout); + if (!found) { + return ImageLayout.Tile; + } + else { + return ((ImageLayout)Properties.GetObject(PropBackgroundImageLayout)); + } + } + set { + if (BackgroundImageLayout != value) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ImageLayout.None, (int)ImageLayout.Zoom)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ImageLayout)); + } + // Check if the value is either center, strech or zoom; + if (value == ImageLayout.Center || value == ImageLayout.Zoom || value == ImageLayout.Stretch) { + SetStyle(ControlStyles.ResizeRedraw, true); + // Only for images that support transparency. + if (ControlPaint.IsImageTransparent(BackgroundImage)) + { + DoubleBuffered = true; + } + } + Properties.SetObject(PropBackgroundImageLayout, value); + OnBackgroundImageLayoutChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBackgroundImageLayoutChangedDescr)] + public event EventHandler BackgroundImageLayoutChanged { + add { + Events.AddHandler(EventBackgroundImageLayout, value); + } + remove { + Events.RemoveHandler(EventBackgroundImageLayout, value); + } + } + + // Set/reset by ContainerControl.AssignActiveControlInternal + internal bool BecomingActiveControl + { + get + { + return GetState2(STATE2_BECOMINGACTIVECONTROL); + } + set + { + if (value != this.BecomingActiveControl) + { + Application.ThreadContext.FromCurrent().ActivatingControl = (value) ? this : null; + SetState2(STATE2_BECOMINGACTIVECONTROL, value); + } + } + } + + private bool ShouldSerializeAccessibleName() { + string accName = this.AccessibleName; + return accName != null && accName.Length > 0; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetBindings() { + ControlBindingsCollection bindings = (ControlBindingsCollection)Properties.GetObject(PropBindings); + if (bindings != null) { + bindings.Clear(); + } + } + + /// + /// + /// BindingContextInternal provides a mechanism so that controls like SplitContainer that inherit from the + /// ContainerControl can bypass the "containerControls" bindingContext property and do what the other simple controls + /// do. + /// + internal BindingContext BindingContextInternal + { + get + { + // See if we have locally overridden the binding manager. + // + BindingContext context = (BindingContext) Properties.GetObject(PropBindingManager); + if (context != null) { + return context; + } + + // Otherwise, see if the parent has one for us. + // + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + return p.BindingContext; + } + + // Otherwise, we have no binding manager available. + // + return null; + } + set + { + BindingContext oldContext = (BindingContext) Properties.GetObject(PropBindingManager); + BindingContext newContext = value; + + if (oldContext != newContext) { + Properties.SetObject(PropBindingManager, newContext); + + // the property change will wire up the bindings. + // + OnBindingContextChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBindingContextDescr) + ] + public virtual BindingContext BindingContext { + get { + return BindingContextInternal; + } + set { + BindingContextInternal = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnBindingContextChangedDescr)] + public event EventHandler BindingContextChanged { + add { + Events.AddHandler(EventBindingContext, value); + } + remove { + Events.RemoveHandler(EventBindingContext, value); + } + } + + /// + /// + /// The bottom coordinate of this control. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBottomDescr), + SRCategory(SR.CatLayout) + ] + public int Bottom { + get { + return y + height; + } + } + + /// + /// + /// The bounds of this control. This is the window coordinates of the + /// control in parent client coordinates. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlBoundsDescr), + SRCategory(SR.CatLayout) + ] + public Rectangle Bounds { + get { + return new Rectangle(x, y, width, height); + } + + set { + SetBounds(value.X, value.Y, value.Width, value.Height, BoundsSpecified.All); + } + } + + /// + /// + /// + internal virtual bool CanAccessProperties { + get { + return true; + } + } + + /// + /// + /// Indicates whether the control can receive focus. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCanFocusDescr) + ] + public bool CanFocus { + get { + if (!IsHandleCreated) { + return false; + } + bool visible = SafeNativeMethods.IsWindowVisible(new HandleRef(window, Handle)); + bool enabled = SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle)); + return (visible && enabled); + } + } + + /// + /// + /// Determines if events can be fired on the control. If this control is being + /// hosted as an ActiveX control, this property will return false if the ActiveX + /// control has its events frozen. + /// + protected override bool CanRaiseEvents { + get { + if (IsActiveX) { + return !ActiveXEventsFrozen; + } + + return true; + } + } + + /// + /// + /// + /// Indicates whether the control can be selected. This property + /// is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCanSelectDescr) + ] + public bool CanSelect { + // We implement this to allow only AxHost to override canSelectCore, but still + // expose the method publicly + // + get { + return CanSelectCore(); + } + } + + /// + /// + /// Indicates whether the control has captured the mouse. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatFocus), + SRDescription(SR.ControlCaptureDescr) + ] + public bool Capture { + get { + return CaptureInternal; + } + + set { + if (value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetCapture Demanded"); + IntSecurity.GetCapture.Demand(); + } + CaptureInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal bool CaptureInternal { + get { + return IsHandleCreated && UnsafeNativeMethods.GetCapture() == Handle; + } + set { + if (CaptureInternal != value) { + if (value) { + UnsafeNativeMethods.SetCapture(new HandleRef(this, Handle)); + } + else { + SafeNativeMethods.ReleaseCapture(); + } + } + } + } + + /// + /// + /// + /// Indicates whether entering the control causes validation on the controls requiring validation. + /// + [ + SRCategory(SR.CatFocus), + DefaultValue(true), + SRDescription(SR.ControlCausesValidationDescr) + ] + public bool CausesValidation { + get { + return GetState(STATE_CAUSESVALIDATION); + } + set { + if (value != this.CausesValidation) { + SetState(STATE_CAUSESVALIDATION, value); + OnCausesValidationChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnCausesValidationChangedDescr)] + public event EventHandler CausesValidationChanged { + add { + Events.AddHandler(EventCausesValidation, value); + } + remove { + Events.RemoveHandler(EventCausesValidation, value); + } + } + + + /// This is for perf. Turn this property on to temporarily enable text caching. This is good for + /// operations such as layout or painting where we don't expect the text to change (we will update the + /// cache if it does) but prevents us from sending a ton of messages turing layout. See the PaintWithErrorHandling + /// function. + /// + internal bool CacheTextInternal { + get { + + // check if we're caching text. + // + bool found; + int cacheTextCounter = Properties.GetInteger(PropCacheTextCount, out found); + + return cacheTextCounter > 0 || GetStyle(ControlStyles.CacheText); + } + set { + + // if this control always cachest text or the handle hasn't been created, + // just bail. + // + if (GetStyle(ControlStyles.CacheText) || !IsHandleCreated) { + return; + } + + // otherwise, get the state and update the cache if necessary. + // + bool found; + int cacheTextCounter = Properties.GetInteger(PropCacheTextCount, out found); + + if (value) { + if (cacheTextCounter == 0) { + Properties.SetObject(PropCacheTextField, text); + if (text == null) { + text = WindowText; + } + } + cacheTextCounter++; + } + else { + cacheTextCounter--; + if (cacheTextCounter == 0) { + text = (string)Properties.GetObject(PropCacheTextField, out found); + } + } + Properties.SetInteger(PropCacheTextCount, cacheTextCounter); + } + } + + /// + [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlCheckForIllegalCrossThreadCalls), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public static bool CheckForIllegalCrossThreadCalls { + get { return checkForIllegalCrossThreadCalls; } + set { checkForIllegalCrossThreadCalls = value; } + } + + /// + /// + /// The client rect of the control. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatLayout), + SRDescription(SR.ControlClientRectangleDescr) + ] + public Rectangle ClientRectangle { + get { + return new Rectangle(0, 0, clientWidth, clientHeight); + } + } + + /// + /// + /// The size of the clientRect. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlClientSizeDescr) + ] + public Size ClientSize { + get { + return new Size(clientWidth, clientHeight); + } + + set { + SetClientSizeCore(value.Width, value.Height); + } + } + + /// + /// + /// Fired when ClientSize changes. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnClientSizeChangedDescr)] + public event EventHandler ClientSizeChanged { + add { + Events.AddHandler(EventClientSize, value); + } + remove { + Events.RemoveHandler(EventClientSize, value); + } + } + + /// + /// + /// Retrieves the company name of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DescriptionAttribute(SR.ControlCompanyNameDescr) + ] + public string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.CompanyName; + } + } + + /// + /// + /// Indicates whether the control or one of its children currently has the system + /// focus. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlContainsFocusDescr) + ] + public bool ContainsFocus { + get { + if (!IsHandleCreated) { + return false; + } + + IntPtr focusHwnd = UnsafeNativeMethods.GetFocus(); + + if (focusHwnd == IntPtr.Zero) { + return false; + } + + if (focusHwnd == this.Handle) { + return true; + } + + if (UnsafeNativeMethods.IsChild(new HandleRef(this, this.Handle), new HandleRef(this, focusHwnd))) { + return true; + } + + return false; + } + } + + /// + /// + /// The contextMenu associated with this control. The contextMenu + /// will be shown when the user right clicks the mouse on the control. + /// + /// Whidbey: ContextMenu is browsable false. In all cases where both a context menu + /// and a context menu strip are assigned, context menu will be shown instead of context menu strip. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr), + Browsable(false) + ] + public virtual ContextMenu ContextMenu { + get { + return (ContextMenu)Properties.GetObject(PropContextMenu); + } + set { + ContextMenu oldValue = (ContextMenu)Properties.GetObject(PropContextMenu); + + if (oldValue != value) { + EventHandler disposedHandler = new EventHandler(DetachContextMenu); + + if (oldValue != null) { + oldValue.Disposed -= disposedHandler; + } + + Properties.SetObject(PropContextMenu, value); + + if (value != null) { + value.Disposed += disposedHandler; + } + + OnContextMenuChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ControlOnContextMenuChangedDescr), + Browsable(false) + ] + public event EventHandler ContextMenuChanged { + add { + Events.AddHandler(EventContextMenu, value); + } + remove { + Events.RemoveHandler(EventContextMenu, value); + } + } + + + /// + /// + /// The contextMenuStrip associated with this control. The contextMenuStrip + /// will be shown when the user right clicks the mouse on the control. + /// Note: if a context menu is also assigned, it will take precidence over this property. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr) + ] + public virtual ContextMenuStrip ContextMenuStrip { + get { + return (ContextMenuStrip)Properties.GetObject(PropContextMenuStrip); + } + set { + ContextMenuStrip oldValue = Properties.GetObject(PropContextMenuStrip) as ContextMenuStrip; + + if (oldValue != value) { + EventHandler disposedHandler = new EventHandler(DetachContextMenuStrip); + + if (oldValue != null) { + oldValue.Disposed -= disposedHandler; + } + + Properties.SetObject(PropContextMenuStrip, value); + + if (value != null) { + value.Disposed += disposedHandler; + } + + OnContextMenuStripChanged(EventArgs.Empty); + } + } + + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlContextMenuStripChangedDescr)] + public event EventHandler ContextMenuStripChanged { + add { + Events.AddHandler(EventContextMenuStrip, value); + } + remove { + Events.RemoveHandler(EventContextMenuStrip, value); + } + } + + /// + /// + /// Collection of child controls. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.ControlControlsDescr) + ] + public ControlCollection Controls { + get { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection == null) { + controlsCollection = CreateControlsInstance(); + Properties.SetObject(PropControlsCollection, controlsCollection); + } + return controlsCollection; + } + } + + /// + /// + /// Indicates whether the control has been created. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlCreatedDescr) + ] + public bool Created { + get { + return(state & STATE_CREATED) != 0; + } + } + + /// + /// + /// Returns the CreateParams used to create the handle for this control. + /// Inheriting classes should call base.CreateParams in the manor + /// below: + /// + protected virtual CreateParams CreateParams { + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + + // CLR4.0 or later, comctl32.dll needs to be loaded explicitly. + if (needToLoadComCtl) { + if ((UnsafeNativeMethods.GetModuleHandle(ExternDll.Comctl32) != IntPtr.Zero) + || (UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(ExternDll.Comctl32) != IntPtr.Zero)) { + needToLoadComCtl = false; + } else { + int lastWin32Error = Marshal.GetLastWin32Error(); + throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError, ExternDll.Comctl32)); + } + } + + // In a typical control this is accessed ten times to create and show a control. + // It is a net memory savings, then, to maintain a copy on control. + // + if (createParams == null) { + createParams = new CreateParams(); + } + + CreateParams cp = createParams; + cp.Style = 0; + cp.ExStyle = 0; + cp.ClassStyle = 0; + cp.Caption = text; + + cp.X = x; + cp.Y = y; + cp.Width = width; + cp.Height = height; + + cp.Style = NativeMethods.WS_CLIPCHILDREN; + if (GetStyle(ControlStyles.ContainerControl)) { + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + } + cp.ClassStyle = NativeMethods.CS_DBLCLKS; + + if ((state & STATE_TOPLEVEL) == 0) { + // When the window is actually created, we will parent WS_CHILD windows to the + // parking form if cp.parent == 0. + // + cp.Parent = parent == null ? IntPtr.Zero : parent.InternalHandle; + cp.Style |= NativeMethods.WS_CHILD | NativeMethods.WS_CLIPSIBLINGS; + } + else { + cp.Parent = IntPtr.Zero; + } + + if ((state & STATE_TABSTOP) != 0) cp.Style |= NativeMethods.WS_TABSTOP; + if ((state & STATE_VISIBLE) != 0) cp.Style |= NativeMethods.WS_VISIBLE; + + // Unlike Visible, Windows doesn't correctly inherit disabledness from its parent -- an enabled child + // of a disabled parent will look enabled but not get mouse events + if (!Enabled) cp.Style |= NativeMethods.WS_DISABLED; + + // If we are being hosted as an Ax control, try to prevent the parking window + // from being created by pre-filling the window handle here. + // + if (cp.Parent == IntPtr.Zero && IsActiveX) { + cp.Parent = ActiveXHWNDParent; + } + + // Set Rtl bits + if (RightToLeft == RightToLeft.Yes) { + cp.ExStyle |= NativeMethods.WS_EX_RTLREADING; + cp.ExStyle |= NativeMethods.WS_EX_RIGHT; + cp.ExStyle |= NativeMethods.WS_EX_LEFTSCROLLBAR; + } + + return cp; + } + } + + internal virtual void NotifyValidationResult(object sender, CancelEventArgs ev) { + this.ValidationCancelled = ev.Cancel ; + } + + /// + /// Helper method... + /// + /// Triggers validation on the active control, and returns bool indicating whether that control was valid. + /// + /// The correct way to do this is to find the common ancestor of the active control and this control, + /// then request validation to be performed by that common container control. + /// + /// Used by controls that don't participate in the normal enter/leave/validation process, but which + /// want to force form-level validation to occur before they attempt some important action. + /// + internal bool ValidateActiveControl(out bool validatedControlAllowsFocusChange) { + bool valid = true; + validatedControlAllowsFocusChange = false; + IContainerControl c = GetContainerControlInternal(); + if (c != null && this.CausesValidation) { + ContainerControl container = c as ContainerControl; + if (container != null) { + while (container.ActiveControl == null) { + ContainerControl cc; + Control parent = container.ParentInternal; + if (parent != null) { + cc = parent.GetContainerControlInternal() as ContainerControl; + if (cc != null) { + container = cc; + } + else { + break; + } + } + else { + break; + } + } + + valid = container.ValidateInternal(true, out validatedControlAllowsFocusChange); + } + } + + return valid; + } + + internal bool ValidationCancelled { + set { + SetState(STATE_VALIDATIONCANCELLED, value); + } + get { + if (GetState(STATE_VALIDATIONCANCELLED)) { + return true; + } + else { + Control parent = this.ParentInternal; + if (parent != null) { + return parent.ValidationCancelled; + } + + return false; + } + } + } + + /// + /// returns bool indicating whether the Top MDI Window is closing. + /// This property is set in the MDI children in WmClose method in form.cs when the top window is closing. + /// This property will be used in ActiveControl to determine if we want to skip set focus and window handle re-creation for the control. + /// + /// + internal bool IsTopMdiWindowClosing { + set { + SetState2(STATE2_TOPMDIWINDOWCLOSING, value); + } + get { + return GetState2(STATE2_TOPMDIWINDOWCLOSING); + } + } + + /// + /// returns bool indicating whether the control is currently being scaled. + /// This property is set in ScaleControl method to allow method being called to condition code that should not run for scaling. + /// + /// + internal bool IsCurrentlyBeingScaled { + private set { + SetState2(STATE2_CURRENTLYBEINGSCALED, value); + } + get { + return GetState2(STATE2_CURRENTLYBEINGSCALED); + } + } + + /// + /// Retrieves the Win32 thread ID of the thread that created the + /// handle for this control. If the control's handle hasn't been + /// created yet, this method will return the current thread's ID. + /// + /// + internal int CreateThreadId { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + if (IsHandleCreated) { + int pid; + return SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, Handle), out pid); + } + else { + return SafeNativeMethods.GetCurrentThreadId(); + } + } + } + + /// + /// + /// Retrieves the cursor that will be displayed when the mouse is over this + /// control. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlCursorDescr), + AmbientValue(null) + ] + public virtual Cursor Cursor { + get { + if (GetState(STATE_USEWAITCURSOR)) + { + return Cursors.WaitCursor; + } + + Cursor cursor = (Cursor)Properties.GetObject(PropCursor); + if (cursor != null) { + return cursor; + } + + // We only do ambients for things with "Cursors.Default" + // as their default. + // + Cursor localDefault = DefaultCursor; + if (localDefault != Cursors.Default) { + return localDefault; + } + + Control p = ParentInternal; + if (p != null) { + return p.Cursor; + } + + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null && ambient.Cursor != null) + return ambient.Cursor; + + return localDefault; + } + set { + + Cursor localCursor = (Cursor)Properties.GetObject(PropCursor); + Cursor resolvedCursor = Cursor; + if (localCursor != value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyCursor Demanded"); + IntSecurity.ModifyCursor.Demand(); + + Properties.SetObject(PropCursor, value); + } + + // Other things can change the cursor... we + // really want to force the correct cursor always... + // + if (IsHandleCreated) { + // We want to instantly change the cursor if the mouse is within our bounds. + // This includes the case where the mouse is over one of our children + NativeMethods.POINT p = new NativeMethods.POINT(); + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetCursorPos(p); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + + if ((r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom) || UnsafeNativeMethods.GetCapture() == Handle) + SendMessage(NativeMethods.WM_SETCURSOR, Handle, (IntPtr)NativeMethods.HTCLIENT); + } + + if (!resolvedCursor.Equals(value)) { + OnCursorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnCursorChangedDescr)] + public event EventHandler CursorChanged { + add { + Events.AddHandler(EventCursor, value); + } + remove { + Events.RemoveHandler(EventCursor, value); + } + } + + /// + /// + /// Retrieves the bindings for this control. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ControlBindingsDescr), + RefreshProperties(RefreshProperties.All), + ParenthesizePropertyName(true) + ] + public ControlBindingsCollection DataBindings + { + get + { + ControlBindingsCollection bindings = (ControlBindingsCollection)Properties.GetObject(PropBindings); + if (bindings == null) + { + bindings = new ControlBindingsCollection(this); + Properties.SetObject(PropBindings, bindings); + } + return bindings; + } + } + + /// + /// + /// The default BackColor of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Color DefaultBackColor { + get { return SystemColors.Control;} + } + + /// + /// + /// Deriving classes can override this to configure a default cursor for their control. + /// This is more efficient than setting the cursor in the control's constructor, + /// and gives automatic support for ShouldSerialize and Reset in the designer. + /// + protected virtual Cursor DefaultCursor { + get { + return Cursors.Default; + } + } + + /// + /// + /// The default Font of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Font DefaultFont { + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + if (defaultFont == null) { + defaultFont = SystemFonts.DefaultFont; + Debug.Assert(defaultFont != null, "defaultFont wasn't set!"); + } + + return defaultFont; + } + } + + /// + /// + /// The default ForeColor of a generic top-level Control. Subclasses may have + /// different defaults. + /// + public static Color DefaultForeColor { + get { return SystemColors.ControlText;} + } + + /// + protected virtual Padding DefaultMargin { + get { return CommonProperties.DefaultMargin; } + } + + /// + protected virtual Size DefaultMaximumSize { + get { return CommonProperties.DefaultMaximumSize; } + } + + /// + protected virtual Size DefaultMinimumSize { + get { return CommonProperties.DefaultMinimumSize; } + } + + /// + protected virtual Padding DefaultPadding { + get { return Padding.Empty; } + } + + private RightToLeft DefaultRightToLeft { + get { return RightToLeft.No; } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Size DefaultSize { + get { return Size.Empty; } + } + + private void DetachContextMenu(object sender, EventArgs e) { + ContextMenu = null; + } + + + private void DetachContextMenuStrip(object sender, EventArgs e) { + ContextMenuStrip = null; + } + + /// + /// + /// DPI value either for the primary screen or for the monitor where the top-level parent is displayed when + /// EnableDpiChangedMessageHandling option is on and the application is per-monitor V2 DPI-aware (rs2+) + /// + [ + Browsable(false), // don't show in property browser + EditorBrowsable(EditorBrowsableState.Always), // do show in the intellisense + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) // do not serialize + ] + public int DeviceDpi { + get { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return deviceDpi; + } + return DpiHelper.DeviceDpi; + } + } + + // The color to use when drawing disabled text. Normally we use BackColor, + // but that obviously won't work if we're transparent. + internal Color DisabledColor { + get { + Color color = BackColor; + if (color.A == 0) { + Control control = ParentInternal; + while (color.A == 0) { + if (control == null) { + // Don't know what to do, this seems good as anything + color = SystemColors.Control; + break; + } + color = control.BackColor; + control = control.ParentInternal; + } + } + return color; + } + } + + /// + /// + /// Returns the client rect of the display area of the control. + /// For the base control class, this is identical to getClientRect. + /// However, inheriting controls may want to change this if their client + /// area differs from their display area. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisplayRectangleDescr) + ] + public virtual Rectangle DisplayRectangle { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + return new Rectangle(0, 0, clientWidth, clientHeight); + } + } + + /// + /// + /// Indicates whether the control has been disposed. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisposedDescr) + ] + public bool IsDisposed { + get { + return GetState(STATE_DISPOSED); + } + } + + /// + /// Disposes of the currently selected font handle (if cached). + /// + private void DisposeFontHandle() { + if (Properties.ContainsObject(PropFontHandleWrapper)) { + FontHandleWrapper fontHandle = Properties.GetObject(PropFontHandleWrapper) as FontHandleWrapper; + if( fontHandle != null ){ + fontHandle.Dispose(); + } + Properties.SetObject(PropFontHandleWrapper, null); + } + } + + /// + /// + /// Indicates whether the control is in the process of being disposed. This + /// property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlDisposingDescr) + ] + public bool Disposing { + get { + return GetState(STATE_DISPOSING); + } + } + + /// + /// + /// The dock property. The dock property controls to which edge + /// of the container this control is docked to. For example, when docked to + /// the top of the container, the control will be displayed flush at the + /// top of the container, extending the length of the container. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(CommonProperties.DefaultDock), + SRDescription(SR.ControlDockDescr) + ] + public virtual DockStyle Dock { + get { + return DefaultLayout.GetDock(this); + } + set { + if(value != Dock) { // NDPWhidbey 29434 +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + DefaultLayout.SetDock(this, value); + OnDockChanged(EventArgs.Empty); + } finally { + ResumeLayout(); + } +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnDockChangedDescr)] + public event EventHandler DockChanged { + add { + Events.AddHandler(EventDock, value); + } + remove { + Events.RemoveHandler(EventDock, value); + } + } + + /// + /// + /// This will enable or disable double buffering. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ControlDoubleBufferedDescr) + ] + protected virtual bool DoubleBuffered { + get { + return GetStyle(ControlStyles.OptimizedDoubleBuffer); + } + set { + if (value != DoubleBuffered) { + + if (value) { + SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, value); + } + else { + SetStyle(ControlStyles.OptimizedDoubleBuffer, value); + } + } + } + } + + private bool DoubleBufferingEnabled { + get { +#pragma warning disable 618 + return GetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint); +#pragma warning restore 618 + } + } + + /// + /// + /// Indicates whether the control is currently enabled. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DispId(NativeMethods.ActiveX.DISPID_ENABLED), + SRDescription(SR.ControlEnabledDescr) + ] + public bool Enabled { + get { + // We are only enabled if our parent is enabled + if (!GetState(STATE_ENABLED)) + return false; + else if (ParentInternal == null) + return true; + else + return ParentInternal.Enabled; + } + + set { + bool oldValue = Enabled; + SetState(STATE_ENABLED, value); + + if (oldValue != value) { + if (!value) { + SelectNextIfFocused(); + } + + OnEnabledChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// Occurs when the control is enabled. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnEnabledChangedDescr)] + public event EventHandler EnabledChanged { + add { + Events.AddHandler(EventEnabled, value); + } + remove { + Events.RemoveHandler(EventEnabled, value); + } + } + + /// + /// + /// Indicates whether the control has focus. This property is read-only. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlFocusedDescr) + ] + public virtual bool Focused { + get { + return IsHandleCreated && UnsafeNativeMethods.GetFocus() == Handle; + } + } + + /// + /// + /// Retrieves the current font for this control. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DispId(NativeMethods.ActiveX.DISPID_FONT), + AmbientValue(null), + SRDescription(SR.ControlFontDescr) + ] + public virtual Font Font { + [return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ActiveXFontMarshaler))] + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return font; + } + + Font f = GetParentFont(); + if (f != null) { + return f; + } + + if (IsActiveX) { + f = ActiveXAmbientFont; + if (f != null) { + return f; + } + } + + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null && ambient.Font != null) { + return ambient.Font; + } + + return DefaultFont; + } + [param : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ActiveXFontMarshaler))] + set { + Font local = (Font)Properties.GetObject(PropFont); + Font resolved = Font; + + bool localChanged = false; + if (value == null) { + if (local != null) { + localChanged = true; + } + } + else { + if (local == null) { + localChanged = true; + } + else { + localChanged = !value.Equals(local); + } + } + + if (localChanged) { + // Store new local value + // + Properties.SetObject(PropFont, value); + + // We only fire the Changed event if the "resolved" value + // changed, however we must update the font if the local + // value changed... + // + if (!resolved.Equals(value)) { + // Cleanup any font handle wrapper... + // + DisposeFontHandle(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, (value == null) ? -1 : value.Height); + } + + // Font is an ambient property. We need to layout our parent because Font may + // change our size. We need to layout ourselves because our children may change + // size by inheriting the new value. + // + using(new LayoutTransaction(ParentInternal, this, PropertyNames.Font)) { + OnFontChanged(EventArgs.Empty); + } + } + else { + if (IsHandleCreated && !GetStyle(ControlStyles.UserPaint)) { + DisposeFontHandle(); + SetWindowFont(); + } + } + } + } + } + + internal void ScaleFont(float factor) { + Font local = (Font)Properties.GetObject(PropFont); + Font resolved = Font; + Font newFont = DpiHelper.EnableDpiChangedHighDpiImprovements ? + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style, this.Font.Unit, this.Font.GdiCharSet, this.Font.GdiVerticalFont) : + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style); + + if ((local == null) || !local.Equals(newFont)) { + Properties.SetObject(PropFont, newFont); + + if (!resolved.Equals(newFont)) { + DisposeFontHandle(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, newFont.Height); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnFontChangedDescr)] + public event EventHandler FontChanged { + add { + Events.AddHandler(EventFont, value); + } + remove { + Events.RemoveHandler(EventFont, value); + } + } + + internal IntPtr FontHandle { + get { + Font font = (Font)Properties.GetObject(PropFont); + + if (font != null) { + FontHandleWrapper fontHandle = (FontHandleWrapper)Properties.GetObject(PropFontHandleWrapper); + if (fontHandle == null) { + fontHandle = new FontHandleWrapper(font); + + Properties.SetObject(PropFontHandleWrapper, fontHandle); + } + + return fontHandle.Handle; + } + + if (parent != null) { + return parent.FontHandle; + } + + AmbientProperties ambient = AmbientPropertiesService; + + if (ambient != null && ambient.Font != null) { + + FontHandleWrapper fontHandle = null; + + Font currentAmbient = (Font)Properties.GetObject(PropCurrentAmbientFont); + + if (currentAmbient != null && currentAmbient == ambient.Font) { + fontHandle = (FontHandleWrapper)Properties.GetObject(PropFontHandleWrapper); + } + else { + Properties.SetObject(PropCurrentAmbientFont, ambient.Font); + } + + if (fontHandle == null) { + font = ambient.Font; + fontHandle = new FontHandleWrapper(font); + + Properties.SetObject(PropFontHandleWrapper, fontHandle); + } + + return fontHandle.Handle; + } + + return GetDefaultFontHandleWrapper().Handle; + } + } + + /// + /// + /// [To be supplied.] + /// + protected int FontHeight { + get { + bool found; + int fontHeight = Properties.GetInteger(PropFontHeight, out found); + if (found && fontHeight != -1) { + return fontHeight; + } + else { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + fontHeight = font.Height; + Properties.SetInteger(PropFontHeight, fontHeight); + return fontHeight; + } + } + + //ask the parent if it has the font height + int localFontHeight = -1; + + if (ParentInternal != null && ParentInternal.CanAccessProperties) { + localFontHeight = ParentInternal.FontHeight; + } + + //if we still have a bad value, then get the actual font height + if (localFontHeight == -1) { + localFontHeight = Font.Height; + Properties.SetInteger(PropFontHeight, localFontHeight); + } + + return localFontHeight; + } + set { + Properties.SetInteger(PropFontHeight, value); + } + } + + /// + /// + /// The foreground color of the control. + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_FORECOLOR), + SRDescription(SR.ControlForeColorDescr) + ] + public virtual Color ForeColor { + get { + Color color = Properties.GetColor(PropForeColor); + if (!color.IsEmpty) { + return color; + } + + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) { + return p.ForeColor; + } + + Color c = Color.Empty; + + if (IsActiveX) { + c = ActiveXAmbientForeColor; + } + + if (c.IsEmpty) { + AmbientProperties ambient = AmbientPropertiesService; + if (ambient != null) + c = ambient.ForeColor; + } + + if (!c.IsEmpty) + return c; + else + return DefaultForeColor; + } + + set { + Color c = ForeColor; + if (!value.IsEmpty || Properties.ContainsObject(PropForeColor)) { + Properties.SetColor(PropForeColor, value); + } + if (!c.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnForeColorChangedDescr)] + public event EventHandler ForeColorChanged { + add { + Events.AddHandler(EventForeColor, value); + } + remove { + Events.RemoveHandler(EventForeColor, value); + } + } + + private Font GetParentFont() { + if (ParentInternal != null && ParentInternal.CanAccessProperties) + return ParentInternal.Font; + else + return null; + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + public virtual Size GetPreferredSize(Size proposedSize) { + Size prefSize; + + if (GetState(STATE_DISPOSING| STATE_DISPOSED)) { + // if someone's asking when we're disposing just return what we last had. + prefSize = CommonProperties.xGetPreferredSizeCache(this); + } + else { + // Switch Size.Empty to maximum possible values + proposedSize = LayoutUtils.ConvertZeroToUnbounded(proposedSize); + + // Force proposedSize to be within the elements constraints. (This applies + // minimumSize, maximumSize, etc.) + proposedSize = ApplySizeConstraints(proposedSize); + if (GetState2(STATE2_USEPREFERREDSIZECACHE)) { + Size cachedSize = CommonProperties.xGetPreferredSizeCache(this); + + // If the "default" preferred size is being requested, and we have a cached value for it, return it. + // + if(!cachedSize.IsEmpty && (proposedSize == LayoutUtils.MaxSize)) { + +#if DEBUG +#if DEBUG_PREFERREDSIZE + Size newPreferredSize = ApplySizeConstraints(GetPreferredSizeCore(proposedSize)); + bool cacheHitCorrect = (cachedSize == newPreferredSize); + if (!cacheHitCorrect && !GetAnyDisposingInHierarchy()) { + Debug.Fail( + "Cached PreferredSize " + cachedSize.ToString() + + " did not match computed: " +newPreferredSize.ToString() + +". Did we forget to invalidate the cache?\r\n\r\nControl Information: " + WindowsFormsUtils.AssertControlInformation(cacheHitCorrect, this) + + "\r\nChanged Properties\r\n " + CommonProperties.Debug_GetChangedProperties(this)); + } +#endif +#endif + return cachedSize; + } + } + + + + CacheTextInternal = true; + try { + prefSize = GetPreferredSizeCore(proposedSize); + } + finally { + CacheTextInternal = false; + } + + // There is no guarantee that GetPreferredSizeCore() return something within + // proposedSize, so we apply the element's constraints again. + prefSize = ApplySizeConstraints(prefSize); + + // If the "default" preferred size was requested, cache the computed value. + // + if(GetState2(STATE2_USEPREFERREDSIZECACHE) && proposedSize == LayoutUtils.MaxSize) { + CommonProperties.xSetPreferredSizeCache(this, prefSize); + } + } + return prefSize; + } + + // Overriding this method allows us to get the caching and clamping the proposedSize/output to + // MinimumSize / MaximumSize from GetPreferredSize for free. + internal virtual Size GetPreferredSizeCore(Size proposedSize) { + return CommonProperties.GetSpecifiedBounds(this).Size; + } + + /// + /// + /// The HWND handle that this control is bound to. If the handle + /// has not yet been created, this will force handle creation. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DispId(NativeMethods.ActiveX.DISPID_HWND), + SRDescription(SR.ControlHandleDescr) + ] + public IntPtr Handle { + get { + if (checkForIllegalCrossThreadCalls && + !inCrossThreadSafeCall && + InvokeRequired) { + throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall, + Name)); + } + + if (!IsHandleCreated) + { + CreateHandle(); + } + + return HandleInternal; + } + } + + internal IntPtr HandleInternal { + get { + return window.Handle; + } + } + + /// + /// + /// True if this control has child controls in its collection. This + /// is more efficient than checking for Controls.Count > 0, but has the + /// same effect. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHasChildrenDescr) + ] + public bool HasChildren { + get { + ControlCollection controls = (ControlCollection)Properties.GetObject(PropControlsCollection); + return controls != null && controls.Count > 0; + } + } + + internal virtual bool HasMenu { + get { + return false; + } + } + + /// + /// + /// The height of this control + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHeightDescr) + ] + public int Height { + get { + return height; + } + set{ + SetBounds(x, y, width, value, BoundsSpecified.Height); + } + } + + internal bool HostedInWin32DialogManager { + get { + if (!GetState(STATE_CHECKEDHOST)) { + Control topMost = TopMostParent; + if (this != topMost) { + SetState(STATE_HOSTEDINDIALOG, topMost.HostedInWin32DialogManager); + } + else { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(this, Handle)); + IntPtr lastParentHandle = parentHandle; + + StringBuilder sb = new StringBuilder(32); + + SetState(STATE_HOSTEDINDIALOG, false); + + while (parentHandle != IntPtr.Zero) { + int len = UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), null, 0); + if (len > sb.Capacity) { + sb.Capacity = len + 5; + } + UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), sb, sb.Capacity); + + if (sb.ToString() == "#32770") { + SetState(STATE_HOSTEDINDIALOG, true); + break; + } + + lastParentHandle = parentHandle; + parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(null, parentHandle)); + } + } + + SetState(STATE_CHECKEDHOST, true); + + } + + return GetState(STATE_HOSTEDINDIALOG); + } + } + + /// + /// + /// Whether or not this control has a handle associated with it. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHandleCreatedDescr) + ] + public bool IsHandleCreated { + get { return window.Handle != IntPtr.Zero; } + } + + /// + /// Determines if layout is currently suspended. + /// + internal bool IsLayoutSuspended { + get { + return layoutSuspendCount > 0; + } + } + + internal bool IsWindowObscured { + get { + if (!IsHandleCreated || !Visible) { + return false; + } + + bool emptyRegion = false; + + NativeMethods.RECT temp = new NativeMethods.RECT(); + Region working; + Control parent = ParentInternal; + if (parent != null) { + while (parent.ParentInternal != null ) { + parent = parent.ParentInternal; + } + } + + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref temp); + working = new Region(Rectangle.FromLTRB(temp.left, temp.top, temp.right, temp.bottom)); + + try { + IntPtr prev; + IntPtr next; + IntPtr start; + if (parent != null) { + start = parent.Handle; + } + else { + start = Handle; + } + + for (prev = start; + (next = UnsafeNativeMethods.GetWindow(new HandleRef(null, prev), NativeMethods.GW_HWNDPREV)) != IntPtr.Zero; + prev = next) { + + UnsafeNativeMethods.GetWindowRect(new HandleRef(null, next), ref temp); + Rectangle current = Rectangle.FromLTRB(temp.left, temp.top, temp.right, temp.bottom); + + if (SafeNativeMethods.IsWindowVisible(new HandleRef(null, next))) { + working.Exclude(current); + } + } + + + Graphics g = CreateGraphics(); + try { + emptyRegion = working.IsEmpty(g); + } + finally { + g.Dispose(); + } + } + finally { + working.Dispose(); + } + + return emptyRegion; + } + } + + /// + /// Returns the current value of the handle. This may be zero if the handle + /// has not been created. + /// + internal IntPtr InternalHandle { + get { + if (!IsHandleCreated) + { + return IntPtr.Zero; + } + else + { + return Handle; + } + } + } + + /// + /// + /// Determines if the caller must call invoke when making method + /// calls to this control. Controls in windows forms are bound to a specific thread, + /// and are not thread safe. Therefore, if you are calling a control's method + /// from a different thread, you must use the control's invoke method + /// to marshal the call to the proper thread. This function can be used to + /// determine if you must call invoke, which can be handy if you don't know + /// what thread owns a control. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and + /// CreateGraphics. For all other method calls, you should use one of the + /// invoke methods. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlInvokeRequiredDescr) + ] + public bool InvokeRequired { + get { + + using (new MultithreadSafeCallScope()) + { + HandleRef hwnd; + if (IsHandleCreated) { + hwnd = new HandleRef(this, Handle); + } + else { + Control marshalingControl = FindMarshalingControl(); + + if (!marshalingControl.IsHandleCreated) { + return false; + } + + hwnd = new HandleRef(marshalingControl, marshalingControl.Handle); + } + + int pid; + int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(hwnd, out pid); + int currentThread = SafeNativeMethods.GetCurrentThreadId(); + return(hwndThread != currentThread); + } + } + } + + /// + /// + /// Indicates whether or not this control is an accessible control + /// i.e. whether it should be visible to accessibility applications. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlIsAccessibleDescr) + ] + public bool IsAccessible { + get { + return GetState(STATE_ISACCESSIBLE); + } + set { + SetState(STATE_ISACCESSIBLE, value); + } + } + + /// + /// Used to tell if this control is being hosted as an ActiveX control. + /// + internal bool IsActiveX { + get { + return GetState2(STATE2_ISACTIVEX); + } + } + + // VsWhidbey 434959 : If the control on which GetContainerControl( ) is called is a ContainerControl, then we dont return the parent + // but return the same control. This is Everett behavior so we cannot change this since this would be a breaking change. + // Hence we have a new internal property IsContainerControl which returns false for all Everett control, but + // this property is overidden in SplitContainer to return true so that we skip the SplitContainer + // and the correct Parent ContainerControl is returned by GetContainerControl(). + internal virtual bool IsContainerControl + { + get + { + return false; + } + } + + /// + /// Used to tell if this control is being hosted in IE. + /// + internal bool IsIEParent { + get { + return IsActiveX ? ActiveXInstance.IsIE : false; + } + } + + /// + /// Used to tell if the control is mirrored + /// Don't call this from CreateParams. Will lead to nasty problems + /// since we might call CreateParams here - you dig! + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.IsMirroredDescr) + ] + public bool IsMirrored { + get { + if (!IsHandleCreated) { + CreateParams cp = CreateParams; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + } + return GetState(STATE_MIRRORED); + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal virtual bool IsMnemonicsListenerAxSourced + { + get + { + return false; + } + } + + /// + /// Used to tell if this BackColor is Supported + /// + private bool IsValidBackColor (Color c){ + if (!c.IsEmpty && !GetStyle(ControlStyles.SupportsTransparentBackColor) && c.A < 255) + { + return false; + } + return true; + + } + + /// + /// + /// The left coordinate of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlLeftDescr) + ] + public int Left { + get { + return x; + } + set { + SetBounds(value, y, width, height, BoundsSpecified.X); + } + } + + /// + /// + /// The location of this control. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ControlLocationDescr) + ] + public Point Location { + get { + return new Point(x, y); + } + set { + SetBounds(value.X, value.Y, width, height, BoundsSpecified.Location); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnLocationChangedDescr)] + public event EventHandler LocationChanged { + add { + Events.AddHandler(EventLocation, value); + } + remove { + Events.RemoveHandler(EventLocation, value); + } + } + + /// + [ + SRDescription(SR.ControlMarginDescr), + SRCategory(SR.CatLayout), + Localizable(true) + ] + public Padding Margin + { + get { return CommonProperties.GetMargin(this); } + set + { + // This should be done here rather than in the property store as + // some IArrangedElements actually support negative padding. + value = LayoutUtils.ClampNegativePaddingToZero(value); + + // SetMargin causes a layout as a side effect. + if (value != Margin) + { + CommonProperties.SetMargin(this, value); + OnMarginChanged(EventArgs.Empty); + } + Debug.Assert(Margin == value, "Error detected while setting Margin."); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnMarginChangedDescr)] + public event EventHandler MarginChanged + { + add + { + Events.AddHandler(EventMarginChanged, value); + } + remove + { + Events.RemoveHandler(EventMarginChanged, value); + } + } + + /// + [SRCategory(SR.CatLayout)] + [Localizable(true)] + [SRDescription(SR.ControlMaximumSizeDescr)] + [AmbientValue(typeof(Size), "0, 0")] + public virtual Size MaximumSize { + get { return CommonProperties.GetMaximumSize(this, DefaultMaximumSize); } + set { + if (value == Size.Empty) { + CommonProperties.ClearMaximumSize(this); + Debug.Assert(MaximumSize == DefaultMaximumSize, "Error detected while resetting MaximumSize."); + } + else if (value != MaximumSize) { + // SetMaximumSize causes a layout as a side effect. + CommonProperties.SetMaximumSize(this, value); + Debug.Assert(MaximumSize == value, "Error detected while setting MaximumSize."); + } + + } + } + + /// + [SRCategory(SR.CatLayout)] + [Localizable(true)] + [SRDescription(SR.ControlMinimumSizeDescr)] + public virtual Size MinimumSize { + get { return CommonProperties.GetMinimumSize(this, DefaultMinimumSize); } + set { + if (value != MinimumSize) { + // SetMinimumSize causes a layout as a side effect. + CommonProperties.SetMinimumSize(this, value); + } + Debug.Assert(MinimumSize == value, "Error detected while setting MinimumSize."); + } + } + + + /// + /// + /// Retrieves the current state of the modifier keys. This will check the + /// current state of the shift, control, and alt keys. + /// + public static Keys ModifierKeys { + get { + Keys modifiers = 0; + // SECURITYNOTE : only let state of Shift-Control-Alt out... + // + if (UnsafeNativeMethods.GetKeyState((int)Keys.ShiftKey) < 0) modifiers |= Keys.Shift; + if (UnsafeNativeMethods.GetKeyState((int)Keys.ControlKey) < 0) modifiers |= Keys.Control; + if (UnsafeNativeMethods.GetKeyState((int)Keys.Menu) < 0) modifiers |= Keys.Alt; + return modifiers; + } + } + + /// + /// + /// The current state of the mouse buttons. This will check the + /// current state of the left, right, and middle mouse buttons. + /// + public static MouseButtons MouseButtons { + get { + MouseButtons buttons = (MouseButtons)0; + // SECURITYNOTE : only let state of MouseButtons out... + // + if (UnsafeNativeMethods.GetKeyState((int)Keys.LButton) < 0) buttons |= MouseButtons.Left; + if (UnsafeNativeMethods.GetKeyState((int)Keys.RButton) < 0) buttons |= MouseButtons.Right; + if (UnsafeNativeMethods.GetKeyState((int)Keys.MButton) < 0) buttons |= MouseButtons.Middle; + if (UnsafeNativeMethods.GetKeyState((int)Keys.XButton1) < 0) buttons |= MouseButtons.XButton1; + if (UnsafeNativeMethods.GetKeyState((int)Keys.XButton2) < 0) buttons |= MouseButtons.XButton2; + return buttons; + } + } + + /// + /// + /// The current position of the mouse in screen coordinates. + /// + public static Point MousePosition { + get { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(pt); + return new Point(pt.x, pt.y); + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control. The name can be + /// used as a key into the ControlCollection. + /// + [Browsable(false)] + public string Name { + get { + string name = (string)Properties.GetObject(PropName); + if (String.IsNullOrEmpty(name)) { + if (Site != null) { + name = Site.Name; + } + + if (name == null) { + name = ""; + } + } + + return name; + } + set { + if (String.IsNullOrEmpty(value)) { + Properties.SetObject(PropName, null); + } + else { + Properties.SetObject(PropName, value); + } + } + } + + /// + /// + /// The parent of this control. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlParentDescr) + ] + public Control Parent { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + return ParentInternal; + } + set { + ParentInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal virtual Control ParentInternal { + get { + return parent; + } + set { + if (parent != value) { + if (value != null) { + value.Controls.Add(this); + } + else { + parent.Controls.Remove(this); + } + } + } + } + + /// + /// + /// Retrieves the product name of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlProductNameDescr) + ] + public string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.ProductName; + } + } + + /// + /// + /// Retrieves the product version of this specific component. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlProductVersionDescr) + ] + public string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return VersionInfo.ProductVersion; + } + } + + /// + /// Retrieves our internal property storage object. If you have a property + /// whose value is not always set, you should store it in here to save + /// space. + /// + internal PropertyStore Properties { + get { + return propertyStore; + } + } + + // Returns the value of the backColor field -- no asking the parent with its color is, etc. + internal Color RawBackColor { + get { + return Properties.GetColor(PropBackColor); + } + } + + /// + /// + /// Indicates whether the control is currently recreating its handle. This + /// property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRecreatingHandleDescr) + ] + public bool RecreatingHandle { + get { + return(state & STATE_RECREATE) != 0; + } + } + + internal virtual void AddReflectChild() { + } + + internal virtual void RemoveReflectChild() { + } + + private Control ReflectParent { + get { + return reflectParent; + } + set { + if (value != null) { + value.AddReflectChild(); + } + + Control c = ReflectParent as Control; + reflectParent = value; + if (c != null) { + c.RemoveReflectChild(); + } + } + } + + /// + /// + /// The Region associated with this control. (defines the + /// outline/silhouette/boundary of control) + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRegionDescr) + ] + public Region Region { + get { + return (Region)Properties.GetObject(PropRegion); + } + set { + if (GetState(STATE_TOPLEVEL)) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ChangeWindowRegion Demanded"); + IntSecurity.ChangeWindowRegionForTopLevel.Demand(); + } + + Region oldRegion = Region; + if (oldRegion != value) { + Properties.SetObject(PropRegion, value); + + if (oldRegion != null) { + oldRegion.Dispose(); + } + + if (IsHandleCreated) { + IntPtr regionHandle = IntPtr.Zero; + + try { + if (value != null) { + regionHandle = GetHRgn(value); + } + + if (IsActiveX) { + regionHandle = ActiveXMergeRegion(regionHandle); + } + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(this, regionHandle), SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle))) != 0) { + //The Hwnd owns the region. + regionHandle = IntPtr.Zero; + } + } + finally { + if (regionHandle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, regionHandle)); + } + } + } + + OnRegionChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// Event fired when the value of Region property is changed on Control + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlRegionChangedDescr)] + public event EventHandler RegionChanged { + add { + Events.AddHandler(EventRegionChanged, value); + } + remove { + Events.RemoveHandler(EventRegionChanged, value); + } + } + + // Helper function for Rtl + /// + /// + /// + /// [To be supplied.] + /// + [Obsolete("This property has been deprecated. Please use RightToLeft instead. http://go.microsoft.com/fwlink/?linkid=14202")] + protected internal bool RenderRightToLeft + { + get { + return true; + } + } + + /// + /// Determines if the parent's background will be rendered on the label control. + /// + internal bool RenderTransparent { + get { + return GetStyle(ControlStyles.SupportsTransparentBackColor) && this.BackColor.A < 255; + } + } + + /// + /// + private bool RenderColorTransparent(Color c) { + return GetStyle(ControlStyles.SupportsTransparentBackColor) && c.A < 255; + } + + + /// + /// Refer VsWhidbey : 440669: This property is required by certain controls (TabPage) to render its transparency using theming API. + /// We dont want all controls (that are have transparent BackColor) to use theming API to render its background because it has HUGE PERF cost. + /// + /// + internal virtual bool RenderTransparencyWithVisualStyles { + get { + return false; + } + } + + /// + /// Represents the bounds of the control that need to be scaled. Control bounds + /// need to be scaled until ScaleControl is called. They need to be scaled again + /// if their bounds change after ScaleControl is called. + /// + internal BoundsSpecified RequiredScaling { + get { + if ((requiredScaling & RequiredScalingEnabledMask) != 0) { + return (BoundsSpecified)(requiredScaling & RequiredScalingMask); + } + return BoundsSpecified.None; + } + set { + byte enableBit = (byte)(requiredScaling & RequiredScalingEnabledMask); + requiredScaling = (byte)(((int)value & RequiredScalingMask) | enableBit); + } + } + + /// + /// Determines if the required scaling property is enabled. If not, + /// RequiredScaling always returns None. + /// + internal bool RequiredScalingEnabled { + get { + return (requiredScaling & RequiredScalingEnabledMask) != 0; + } + set { + byte scaling = (byte)(requiredScaling & RequiredScalingMask); + requiredScaling = scaling; + if (value) requiredScaling |= RequiredScalingEnabledMask; + } + } + + /// + /// + /// Indicates whether the control should redraw itself when resized. + /// + [ + SRDescription(SR.ControlResizeRedrawDescr) + ] + protected bool ResizeRedraw { + get { + return GetStyle( ControlStyles.ResizeRedraw ); + } + set { + SetStyle( ControlStyles.ResizeRedraw, value ); + } + } + + /// + /// + /// The right coordinate of the control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlRightDescr) + ] + public int Right { + get { + return x + width; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(RightToLeft.Inherit), + SRDescription(SR.ControlRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + bool found; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + + if (((RightToLeft)rightToLeft) == RightToLeft.Inherit) { + Control parent = ParentInternal; + if (parent != null) { + rightToLeft = (int)parent.RightToLeft; + } + else { + rightToLeft = (int)DefaultRightToLeft; + } + } + return (RightToLeft)rightToLeft; + } + + set { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)){ + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + + RightToLeft oldValue = RightToLeft; + + if (Properties.ContainsInteger(PropRightToLeft) || value != RightToLeft.Inherit) { + Properties.SetInteger(PropRightToLeft, (int)value); + } + + if (oldValue != RightToLeft) { + // Setting RTL on a container does not cause the container to change size. + // Only the children need to have thier layout updated. + using(new LayoutTransaction(this, this, PropertyNames.RightToLeft)) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + } + + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftChangedDescr)] + public event EventHandler RightToLeftChanged { + add { + Events.AddHandler(EventRightToLeft, value); + } + remove { + Events.RemoveHandler(EventRightToLeft, value); + } + } + + + /// + /// + /// This property controls the scaling of child controls. If true child controls + /// will be scaled when the Scale method on this control is called. If false, + /// child controls will not be scaled. The default is true, and you must override + /// this property to provide a different value. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual bool ScaleChildren { + get { + return true; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override ISite Site { + get { + return base.Site; + } + set { + AmbientProperties oldAmbients = AmbientPropertiesService; + AmbientProperties newAmbients = null; + + if (value != null) { + newAmbients = (AmbientProperties)value.GetService(typeof(AmbientProperties)); + } + + + // If the ambients changed, compare each property. + // + if (oldAmbients != newAmbients) { + bool checkFont = !Properties.ContainsObject(PropFont); + bool checkBackColor = !Properties.ContainsObject(PropBackColor); + bool checkForeColor = !Properties.ContainsObject(PropForeColor); + bool checkCursor = !Properties.ContainsObject(PropCursor); + + Font oldFont = null; + Color oldBackColor = Color.Empty; + Color oldForeColor = Color.Empty; + Cursor oldCursor = null; + + if (checkFont) { + oldFont = Font; + } + + if (checkBackColor) { + oldBackColor = BackColor; + } + + if (checkForeColor) { + oldForeColor = ForeColor; + } + + if (checkCursor) { + oldCursor = Cursor; + } + + Properties.SetObject(PropAmbientPropertiesService, newAmbients); + base.Site = value; + + if (checkFont && !oldFont.Equals(Font)) { + OnFontChanged(EventArgs.Empty); + } + if (checkForeColor && !oldForeColor.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + if (checkBackColor && !oldBackColor.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + if (checkCursor && oldCursor.Equals(Cursor)) { + OnCursorChanged(EventArgs.Empty); + } + } + else { + // If the ambients haven't changed, we just set a new site. + // + base.Site = value; + } + } + } + + /// + /// + /// The size of the control. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ControlSizeDescr) + ] + public Size Size { + get { + return new Size(width, height); + } + set { + SetBounds(x, y, value.Width, value.Height, BoundsSpecified.Size); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnSizeChangedDescr)] + public event EventHandler SizeChanged { + add { + Events.AddHandler(EventSize, value); + } + remove { + Events.RemoveHandler(EventSize, value); + } + } + + /// + /// + /// + /// The tab index of + /// this control. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + MergableProperty(false), + SRDescription(SR.ControlTabIndexDescr) + ] + public int TabIndex { + get { + return tabIndex == -1 ? 0 : tabIndex; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("TabIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "TabIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (tabIndex != value) { + tabIndex = value; + OnTabIndexChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTabIndexChangedDescr)] + public event EventHandler TabIndexChanged { + add { + Events.AddHandler(EventTabIndex, value); + } + remove { + Events.RemoveHandler(EventTabIndex, value); + } + } + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public bool TabStop { + get { + return TabStopInternal; + } + set { + if (TabStop != value) { + TabStopInternal = value; + if (IsHandleCreated) SetWindowStyle(NativeMethods.WS_TABSTOP, value); + OnTabStopChanged(EventArgs.Empty); + } + } + } + + // Grab out the logical of setting TABSTOP state, so that derived class could use this. + internal bool TabStopInternal { + get { + return (state & STATE_TABSTOP) != 0; + } + set { + if (TabStopInternal != value) { + SetState(STATE_TABSTOP, value); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTabStopChangedDescr)] + public event EventHandler TabStopChanged { + add { + Events.AddHandler(EventTabStop, value); + } + remove { + Events.RemoveHandler(EventTabStop, value); + } + } + + + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return Properties.GetObject(PropUserData); + } + set { + Properties.SetObject(PropUserData, value); + } + } + + /// + /// + /// The current text associated with this control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + Bindable(true), + DispId(NativeMethods.ActiveX.DISPID_TEXT), + SRDescription(SR.ControlTextDescr) + ] + public virtual string Text { + get { + if (CacheTextInternal) { + return(text == null) ? "" : text; + } + else { + return WindowText; + } + } + + set { + if (value == null) { + value = ""; + } + + if (value == Text) { + return; + } + + if (CacheTextInternal) { + text = value; + } + WindowText = value; + OnTextChanged(EventArgs.Empty); + + if( this.IsMnemonicsListenerAxSourced ){ + for( Control ctl = this; ctl != null; ctl = ctl.ParentInternal ) { + ActiveXImpl activeXImpl = (ActiveXImpl)ctl.Properties.GetObject(PropActiveXImpl); + if( activeXImpl != null ) { + activeXImpl.UpdateAccelTable(); + break; + } + } + } + + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnTextChangedDescr)] + public event EventHandler TextChanged { + add { + Events.AddHandler(EventText, value); + } + remove { + Events.RemoveHandler(EventText, value); + } + } + + /// + /// + /// Top coordinate of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlTopDescr) + ] + public int Top { + get { + return y; + } + set { + SetBounds(x, value, width, height, BoundsSpecified.Y); + } + } + + /// + /// + /// The top level control that contains this control. This doesn't + /// have to be the same as the value returned from getForm since forms + /// can be parented to other controls. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlTopLevelControlDescr) + ] + public Control TopLevelControl { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + return TopLevelControlInternal; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Control TopLevelControlInternal { + get { + Control control = this; + while (control != null && !control.GetTopLevel()) { + control = control.ParentInternal; + } + return control; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Control TopMostParent { + get { + Control control = this; + while (control.ParentInternal != null) { + control = control.ParentInternal; + } + return control; + } + } + + + private BufferedGraphicsContext BufferContext { + get { + //This auto upgraged v1 client to per-process doublebuffering logic + // + return BufferedGraphicsManager.Current; + } + } + + /// + /// + /// Indicates whether the user interface is in a state to show or hide keyboard + /// accelerators. This property is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected internal virtual bool ShowKeyboardCues { + get { + // Controls in design mode always draw their accellerators. + if (!IsHandleCreated || DesignMode) { + return true; + // would be nice to query SystemParametersInfo, but have trouble + // getting this to work and this should not really be called before + // handle created anyway + } + + // How this all works + + // uiCuesState contains this control's cached state of whether or not it thinks + // accelerators/focus cues are turned on. the first 16 bits represent focus cues + // the second represent keyboard cues. "F" is the UISTATE_FOCUS_CUES_MASK, + // "F0" is the UISTATE_KEYBOARD_CUES_MASK + + // We check here if we have cached state. If we dont, we need to initialize ourself. + // We do this by checking "MenuAccessKeysUnderlined" - we show if this returns true. + + // If MenuAccessKeysUnderlined returns false, we have to manually call CHANGEUISTATE on the topmost control + // Why? Well the way the API seems to work is that it stores in a bit flag for the the hidden + // state. + + // Details from the Menu keydown to changed value of uiCuesState... + + // When someone does press the ALT (Menu)/F10 key we will + // Call ProcessUICues on the control that had focus at the time + // ProcessUICues will check the current state of the control using WM_QUERYUISTATE + // If WM_QUERYUISTATE indicates that the accelerators are hidden we will + // either call WM_UPDATEUISTATE or WM_CHANGEUISTATE depending on whether we're hosted or not. + // All controls in the heirarchy will be individually called back on WM_UPDATEUISTATE, which will go into WmUpdateUIState. + // In WmUpdateUIState, we will update our uiCuesState cached value, which + // changes the public value of what we return here for ShowKeyboardCues/ShowFocusCues. + + if ((uiCuesState & UISTATE_KEYBOARD_CUES_MASK) == 0) { + + // VSWhidbey 362408 -- if we get in here that means this is the windows bug where the first top + // level window doesn't get notified with WM_UPDATEUISTATE + // + + if (SystemInformation.MenuAccessKeysUnderlined) { + uiCuesState |= UISTATE_KEYBOARD_CUES_SHOW; + } + else { + // if we're in the hidden state, we need to manufacture an update message so everyone knows it. + // + int actionMask = (NativeMethods.UISF_HIDEACCEL | + (AccessibilityImprovements.Level1 ? 0 : NativeMethods.UISF_HIDEFOCUS)) << 16; + uiCuesState |= UISTATE_KEYBOARD_CUES_HIDDEN; + + // The side effect of this initial state is that adding new controls may clear the accelerator + // state (has been this way forever) + UnsafeNativeMethods.SendMessage(new HandleRef(TopMostParent, TopMostParent.Handle), + NativeMethods.WM_CHANGEUISTATE, + (IntPtr)(actionMask | NativeMethods.UIS_SET), + IntPtr.Zero); + } + } + return (uiCuesState & UISTATE_KEYBOARD_CUES_MASK) == UISTATE_KEYBOARD_CUES_SHOW; + } + } + + /// + /// + /// Indicates whether the user interface is in a state to show or hide focus + /// rectangles. This property is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected internal virtual bool ShowFocusCues { + get { + if (!IsHandleCreated) { + return true; + // would be nice to query SystemParametersInfo, but have trouble + // getting this to work and this should not really be called before + // handle created anyway + } + + // See "How this all works" in ShowKeyboardCues + + if ((uiCuesState & UISTATE_FOCUS_CUES_MASK) == 0) { + // VSWhidbey 362408 -- if we get in here that means this is the windows bug where the first top + // level window doesn't get notified with WM_UPDATEUISTATE + // + if (SystemInformation.MenuAccessKeysUnderlined) { + uiCuesState |= UISTATE_FOCUS_CUES_SHOW; + } + else { + uiCuesState |= UISTATE_FOCUS_CUES_HIDDEN; + + // if we're in the hidden state, we need to manufacture an update message so everyone knows it. + // + int actionMask = (NativeMethods.UISF_HIDEACCEL | NativeMethods.UISF_HIDEFOCUS) << 16; + + // The side effect of this initial state is that adding new controls may clear the focus cue state + // state (has been this way forever) + UnsafeNativeMethods.SendMessage(new HandleRef(TopMostParent, TopMostParent.Handle), + NativeMethods.WM_CHANGEUISTATE, + (IntPtr)(actionMask | NativeMethods.UIS_SET), + IntPtr.Zero); + + } + } + return (uiCuesState & UISTATE_FOCUS_CUES_MASK) == UISTATE_FOCUS_CUES_SHOW; + } + } + + // The parameter used in the call to ShowWindow for this control + // + internal virtual int ShowParams { + get { + return NativeMethods.SW_SHOW; + } + } + + + /// + /// + /// When this property in true the Cursor Property is set to WaitCursor as well as the Cursor Property + /// of all the child controls. + /// + [ + DefaultValue(false), + EditorBrowsable(EditorBrowsableState.Always), + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlUseWaitCursorDescr), + ] + public bool UseWaitCursor { + get { return GetState(STATE_USEWAITCURSOR); } + set { + if (GetState(STATE_USEWAITCURSOR) != value) { + SetState(STATE_USEWAITCURSOR, value); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].UseWaitCursor = value; + } + } + } + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// This property overwrites the UseCompatibleTextRenderingDefault switch when set programmatically. + /// Exposed publicly only by controls that support GDI text rendering (Label, LinkLabel and some others). + /// Observe that this property is NOT virtual (to allow for caching the property value - see LinkLabel) + /// and should be used by controls that support it only (see SupportsUseCompatibleTextRendering). + /// + internal bool UseCompatibleTextRenderingInt { + get{ + if (Properties.ContainsInteger(PropUseCompatibleTextRendering)){ + bool found; + int value = Properties.GetInteger(PropUseCompatibleTextRendering, out found); + if( found ){ + return value == 1; + } + } + + return Control.UseCompatibleTextRenderingDefault; + } + set{ + if( SupportsUseCompatibleTextRendering && UseCompatibleTextRenderingInt != value ){ + Properties.SetInteger(PropUseCompatibleTextRendering, value ? 1 : 0); + // Update the preferred size cache since we will be rendering text using a different engine. + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.UseCompatibleTextRendering); + Invalidate(); + } + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls (PropertyGrid) to iterate through its children to set + /// UseCompatibleTextRendering to the same value if the child control supports it. + /// + internal virtual bool SupportsUseCompatibleTextRendering { + get { + return false; + } + } + + private ControlVersionInfo VersionInfo { + get { + ControlVersionInfo info = (ControlVersionInfo)Properties.GetObject(PropControlVersionInfo); + if (info == null) { + info = new ControlVersionInfo(this); + Properties.SetObject(PropControlVersionInfo, info); + } + return info; + } + } + + /// + /// + /// Indicates whether the control is visible. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ControlVisibleDescr) + ] + public bool Visible { + get { + return GetVisibleCore(); + } + set { + SetVisibleCore(value); + } + } + + /// + /// + /// Occurs when the control becomes visible. + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnVisibleChangedDescr)] + public event EventHandler VisibleChanged { + add { + Events.AddHandler(EventVisible, value); + } + remove { + Events.RemoveHandler(EventVisible, value); + } + } + + /// + /// + /// Wait for the wait handle to receive a signal: throw an exception if the thread is no longer with us. + /// + private void WaitForWaitHandle(WaitHandle waitHandle) { + int threadId = CreateThreadId; + Application.ThreadContext ctx = Application.ThreadContext.FromId(threadId); + if (ctx == null) { + // Couldn't find the thread context, so we don't know the state. We shouldn't throw. + return; + } + IntPtr threadHandle = ctx.GetHandle(); + bool processed = false; + // setting default exitcode to 0, though it won't be accessed in current code below due to short-circuit logic in condition (returnValue will be false when exitCode is undefined) + uint exitCode = 0; + bool returnValue = false; + while (!processed) { + //Get the thread's exit code, if we found the thread as expected + if (threadHandle != null) { + returnValue = UnsafeNativeMethods.GetExitCodeThread(threadHandle, out exitCode); + } + //If we didn't find the thread, or if GetExitCodeThread failed, we don't know the thread's state: + //if we don't know, we shouldn't throw. + if ((returnValue && exitCode != NativeMethods.STILL_ACTIVE) || + (!returnValue && Marshal.GetLastWin32Error() == NativeMethods.ERROR_INVALID_HANDLE) || + AppDomain.CurrentDomain.IsFinalizingForUnload()) { + if (waitHandle.WaitOne(1, false)) { + break; + } + throw new InvalidAsynchronousStateException(SR.GetString(SR.ThreadNoLongerValid)); + } + + //Dev10 Bug 600316, 905126: Because Control.Invoke() is not fully thread safe, so it is possible that + //a ThreadMethodEntry can be sent to a control after it is disposed. In this case, we need to check + //if there is any ThreadMethodEntry in the queue. If so, we need "complete" them. + if (IsDisposed && threadCallbackList != null && threadCallbackList.Count > 0) { + lock (threadCallbackList) { + Exception ex = new System.ObjectDisposedException(GetType().Name); + while (threadCallbackList.Count > 0) { + ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue(); + entry.exception = ex; + entry.Complete(); + } + } + } + + processed = waitHandle.WaitOne(1000, false); + } + } + + + /// + /// + /// The width of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWidthDescr) + ] + public int Width { + get { + return width; + } + set { + SetBounds(x, y, value, height, BoundsSpecified.Width); + } + } + + /// + /// The current exStyle of the hWnd + /// + /// + private int WindowExStyle { + get { + return unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE)); + } + set { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE, new HandleRef(null, (IntPtr)value)); + } + } + + /// + /// The current style of the hWnd + /// + /// + internal int WindowStyle { + get { + return unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE)); + } + set { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)value)); + } + } + + /// + /// + /// The target of Win32 window messages. + /// + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWindowTargetDescr) + ] + public IWindowTarget WindowTarget { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + return window.WindowTarget; + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + set { + window.WindowTarget = value; + } + } + + /// + /// The current text of the Window; if the window has not yet been created, stores it in the control. + /// If the window has been created, stores the text in the underlying win32 control. + /// This property should be used whenever you want to get at the win32 control's text. For all other cases, + /// use the Text property - but note that this is overridable, and any of your code that uses it will use + /// the overridden version in controls that subclass your own. + /// + internal virtual string WindowText { + get { + + if (!IsHandleCreated) { + if (text == null) { + return ""; + } + else { + return text; + } + } + + using (new MultithreadSafeCallScope()) { + + // it's okay to call GetWindowText cross-thread. + // + + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(window, Handle)); + + // Check to see if the system supports DBCS character + // if so, double the length of the buffer. + if (SystemInformation.DbcsEnabled) { + textLen = (textLen * 2) + 1; + } + StringBuilder sb = new StringBuilder(textLen + 1); + UnsafeNativeMethods.GetWindowText(new HandleRef(window, Handle), sb, sb.Capacity); + return sb.ToString(); + } + } + set { + if (value == null) value = ""; + if (!WindowText.Equals(value)) { + if (IsHandleCreated) { + UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value); + } + else { + if (value.Length == 0) { + text = null; + } + else { + text = value; + } + } + } + } + } + + + + /// + /// + /// Occurs when the control is clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnClickDescr)] + public event EventHandler Click { + add { + Events.AddHandler(EventClick, value); + } + remove { + Events.RemoveHandler(EventClick, value); + } + } + + + + /// + /// + /// Occurs when a new control is added. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlOnControlAddedDescr) + ] + public event ControlEventHandler ControlAdded { + add { + Events.AddHandler(EventControlAdded, value); + } + remove { + Events.RemoveHandler(EventControlAdded, value); + } + } + + + /// + /// + /// Occurs when a control is removed. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ControlOnControlRemovedDescr) + ] + public event ControlEventHandler ControlRemoved { + add { + Events.AddHandler(EventControlRemoved, value); + } + remove { + Events.RemoveHandler(EventControlRemoved, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragDropDescr)] + public event DragEventHandler DragDrop { + add { + Events.AddHandler(EventDragDrop, value); + } + remove { + Events.RemoveHandler(EventDragDrop, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragEnterDescr)] + public event DragEventHandler DragEnter { + add { + Events.AddHandler(EventDragEnter, value); + } + remove { + Events.RemoveHandler(EventDragEnter, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragOverDescr)] + public event DragEventHandler DragOver { + add { + Events.AddHandler(EventDragOver, value); + } + remove { + Events.RemoveHandler(EventDragOver, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnDragLeaveDescr)] + public event EventHandler DragLeave { + add { + Events.AddHandler(EventDragLeave, value); + } + remove { + Events.RemoveHandler(EventDragLeave, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnGiveFeedbackDescr)] + public event GiveFeedbackEventHandler GiveFeedback { + add { + Events.AddHandler(EventGiveFeedback, value); + } + remove { + Events.RemoveHandler(EventGiveFeedback, value); + } + } + + + /// + /// + /// Occurs when a handle is created for the control. + /// + [SRCategory(SR.CatPrivate), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnCreateHandleDescr)] + public event EventHandler HandleCreated { + add { + Events.AddHandler(EventHandleCreated, value); + } + remove { + Events.RemoveHandler(EventHandleCreated, value); + } + } + + + /// + /// + /// Occurs when the control's handle is destroyed. + /// + [SRCategory(SR.CatPrivate), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnDestroyHandleDescr)] + public event EventHandler HandleDestroyed { + add { + Events.AddHandler(EventHandleDestroyed, value); + } + remove { + Events.RemoveHandler(EventHandleDestroyed, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnHelpDescr)] + public event HelpEventHandler HelpRequested { + add { + Events.AddHandler(EventHelpRequested, value); + } + remove { + Events.RemoveHandler(EventHelpRequested, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), SRDescription(SR.ControlOnInvalidateDescr)] + public event InvalidateEventHandler Invalidated { + add { + Events.AddHandler(EventInvalidated, value); + } + remove { + Events.RemoveHandler(EventInvalidated, value); + } + } + + /// + [Browsable(false)] + public Size PreferredSize { + get { return GetPreferredSize(Size.Empty); } + } + + /// + [ + SRDescription(SR.ControlPaddingDescr), + SRCategory(SR.CatLayout), + Localizable(true) + ] + public Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + if (value != Padding) { + CommonProperties.SetPadding(this, value); + // Ideally we are being laid out by a LayoutEngine that cares about our preferred size. + // We set our LAYOUTISDIRTY bit and ask our parent to refresh us. + SetState(STATE_LAYOUTISDIRTY, true); + using (new LayoutTransaction(ParentInternal, this, PropertyNames.Padding)) { + OnPaddingChanged(EventArgs.Empty); + } + + if(GetState(STATE_LAYOUTISDIRTY)) { + // The above did not cause our layout to be refreshed. We explicitly refresh our + // layout to ensure that any children are repositioned to account for the change + // in padding. + LayoutTransaction.DoLayout(this, this, PropertyNames.Padding); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr)] + public event EventHandler PaddingChanged { + add { + Events.AddHandler(EventPaddingChanged, value); + } + remove { + Events.RemoveHandler(EventPaddingChanged, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ControlOnPaintDescr)] + public event PaintEventHandler Paint { + add { + Events.AddHandler(EventPaint, value); + } + remove { + Events.RemoveHandler(EventPaint, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatDragDrop), SRDescription(SR.ControlOnQueryContinueDragDescr)] + public event QueryContinueDragEventHandler QueryContinueDrag { + add { + Events.AddHandler(EventQueryContinueDrag, value); + } + remove { + Events.RemoveHandler(EventQueryContinueDrag, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnQueryAccessibilityHelpDescr)] + public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + Events.AddHandler(EventQueryAccessibilityHelp, value); + } + remove { + Events.RemoveHandler(EventQueryAccessibilityHelp, value); + } + } + + /// + /// + /// Occurs when the control is double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnDoubleClickDescr)] + public event EventHandler DoubleClick { + add { + Events.AddHandler(EventDoubleClick, value); + } + remove { + Events.RemoveHandler(EventDoubleClick, value); + } + } + + /// + /// + /// Occurs when the control is entered. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnEnterDescr)] + public event EventHandler Enter { + add { + Events.AddHandler(EventEnter, value); + } + remove { + Events.RemoveHandler(EventEnter, value); + } + } + + /// + /// + /// Occurs when the control receives focus. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnGotFocusDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler GotFocus { + add { + Events.AddHandler(EventGotFocus, value); + } + remove { + Events.RemoveHandler(EventGotFocus, value); + } + } + + /// + /// + /// Occurs when a key is pressed down while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyDownDescr)] + public event KeyEventHandler KeyDown { + add { + Events.AddHandler(EventKeyDown, value); + } + remove { + Events.RemoveHandler(EventKeyDown, value); + } + } + + + /// + /// + /// Occurs when a key is pressed while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyPressDescr)] + public event KeyPressEventHandler KeyPress { + add { + Events.AddHandler(EventKeyPress, value); + } + remove { + Events.RemoveHandler(EventKeyPress, value); + } + } + + + /// + /// + /// Occurs when a key is released while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyUpDescr)] + public event KeyEventHandler KeyUp { + add { + Events.AddHandler(EventKeyUp, value); + } + remove { + Events.RemoveHandler(EventKeyUp, value); + } + } + + + /// + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnLayoutDescr)] + public event LayoutEventHandler Layout { + add { + Events.AddHandler(EventLayout, value); + } + remove { + Events.RemoveHandler(EventLayout, value); + } + } + + + /// + /// + /// Occurs when the control is left. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLeaveDescr)] + public event EventHandler Leave { + add { + Events.AddHandler(EventLeave, value); + } + remove { + Events.RemoveHandler(EventLeave, value); + } + } + + /// + /// + /// Occurs when the control loses focus. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLostFocusDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler LostFocus { + add { + Events.AddHandler(EventLostFocus, value); + } + remove { + Events.RemoveHandler(EventLostFocus, value); + } + } + + /// + /// + /// Occurs when the control is mouse clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseClickDescr)] + public event MouseEventHandler MouseClick { + add { + Events.AddHandler(EventMouseClick, value); + } + remove { + Events.RemoveHandler(EventMouseClick, value); + } + } + + + /// + /// + /// Occurs when the control is mouse double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseDoubleClickDescr)] + public event MouseEventHandler MouseDoubleClick { + add { + Events.AddHandler(EventMouseDoubleClick, value); + } + remove { + Events.RemoveHandler(EventMouseDoubleClick, value); + } + } + + + /// + /// + /// Occurs when the control loses mouse Capture. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnMouseCaptureChangedDescr)] + public event EventHandler MouseCaptureChanged { + add { + Events.AddHandler(EventMouseCaptureChanged, value); + } + remove { + Events.RemoveHandler(EventMouseCaptureChanged, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is + /// pressed. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseDownDescr)] + public event MouseEventHandler MouseDown { + add { + Events.AddHandler(EventMouseDown, value); + } + remove { + Events.RemoveHandler(EventMouseDown, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer enters the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseEnterDescr)] + public event EventHandler MouseEnter { + add { + Events.AddHandler(EventMouseEnter, value); + } + remove { + Events.RemoveHandler(EventMouseEnter, value); + } + } + + /// + /// + /// Occurs when the mouse pointer leaves the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseLeaveDescr)] + public event EventHandler MouseLeave { + add { + Events.AddHandler(EventMouseLeave, value); + } + remove { + Events.RemoveHandler(EventMouseLeave, value); + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this control is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// This event is raised before the top level parent window recieves WM_DPICHANGED message. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnDpiChangedBeforeParentDescr)] + public event EventHandler DpiChangedBeforeParent { + add { + Events.AddHandler(EventDpiChangedBeforeParent, value); + } + remove { + Events.RemoveHandler(EventDpiChangedBeforeParent, value); + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this control is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// This message is received after the top levet parent window recieves WM_DPICHANGED message. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnDpiChangedAfterParentDescr)] + public event EventHandler DpiChangedAfterParent { + add { + Events.AddHandler(EventDpiChangedAfterParent, value); + } + remove { + Events.RemoveHandler(EventDpiChangedAfterParent, value); + } + } + + /// + /// + /// Occurs when the mouse pointer hovers over the contro. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseHoverDescr)] + public event EventHandler MouseHover { + add { + Events.AddHandler(EventMouseHover, value); + } + remove { + Events.RemoveHandler(EventMouseHover, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer is moved over the control. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseMoveDescr)] + public event MouseEventHandler MouseMove { + add { + Events.AddHandler(EventMouseMove, value); + } + remove { + Events.RemoveHandler(EventMouseMove, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is released. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseUpDescr)] + public event MouseEventHandler MouseUp { + add { + Events.AddHandler(EventMouseUp, value); + } + remove { + Events.RemoveHandler(EventMouseUp, value); + } + } + + + /// + /// + /// Occurs when the mouse wheel moves while the control has focus. + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseWheelDescr), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event MouseEventHandler MouseWheel { + add { + Events.AddHandler(EventMouseWheel, value); + } + remove { + Events.RemoveHandler(EventMouseWheel, value); + } + } + + + /// + /// + /// Occurs when the control is moved. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnMoveDescr)] + public event EventHandler Move { + add { + Events.AddHandler(EventMove, value); + } + remove { + Events.RemoveHandler(EventMove, value); + } + } + + /// + /// Raised to preview a key down event + /// + [SRCategory(SR.CatKey), SRDescription(SR.PreviewKeyDownDescr)] + public event PreviewKeyDownEventHandler PreviewKeyDown { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + add { + Events.AddHandler(EventPreviewKeyDown, value); + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + remove { + Events.RemoveHandler(EventPreviewKeyDown, value); + } + } + + + /// + /// + /// Occurs when the control is resized. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ControlOnResizeDescr), + EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler Resize { + add { + Events.AddHandler(EventResize, value); + } + remove { + Events.RemoveHandler(EventResize, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnChangeUICuesDescr)] + public event UICuesEventHandler ChangeUICues { + add { + Events.AddHandler(EventChangeUICues, value); + } + remove { + Events.RemoveHandler(EventChangeUICues, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnStyleChangedDescr)] + public event EventHandler StyleChanged { + add { + Events.AddHandler(EventStyleChanged, value); + } + remove { + Events.RemoveHandler(EventStyleChanged, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ControlOnSystemColorsChangedDescr)] + public event EventHandler SystemColorsChanged { + add { + Events.AddHandler(EventSystemColorsChanged, value); + } + remove { + Events.RemoveHandler(EventSystemColorsChanged, value); + } + } + + /// + /// + /// Occurs when the control is validating. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatingDescr)] + public event CancelEventHandler Validating { + add { + Events.AddHandler(EventValidating, value); + } + remove { + Events.RemoveHandler(EventValidating, value); + } + } + + + /// + /// + /// Occurs when the control is done validating. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatedDescr)] + public event EventHandler Validated { + add { + Events.AddHandler(EventValidated, value); + } + remove { + Events.RemoveHandler(EventValidated, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal void AccessibilityNotifyClients(AccessibleEvents accEvent, int childID) { + AccessibilityNotifyClients(accEvent, NativeMethods.OBJID_CLIENT, childID); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void AccessibilityNotifyClients(AccessibleEvents accEvent, int objectID, int childID) { + if (IsHandleCreated) { + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), objectID, childID + 1); + } + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + private IntPtr ActiveXMergeRegion(IntPtr region) { + return ActiveXInstance.MergeRegion(region); + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXOnFocus(bool focus) { + ActiveXInstance.OnFocus(focus); + } + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXViewChanged() { + ActiveXInstance.ViewChangedInternal(); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// + /// This is called by regasm to register a control as an ActiveX control. + /// + [ComRegisterFunction()] + protected static void ActiveXRegister(Type type) { + + if (type == null) { + throw new ArgumentNullException("type"); + } + + // If the user is not registering an AX control, then + // bail now. + // + if (!typeof(Control).IsAssignableFrom(type)) { + return; + } + + // Add the flags that define us as a control to the rest of the + // world. + // + RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID", true).CreateSubKey("{" + type.GUID.ToString() + "}"); + RegistryKey key = clsidKey.CreateSubKey("Control"); + key.Close(); + + key = clsidKey.CreateSubKey("Implemented Categories"); + RegistryKey subKey = key.CreateSubKey("{40FC6ED4-2438-11CF-A3DB-080036F12502}"); // CATID_Control + subKey.Close(); + key.Close(); + + // Calculate MiscStatus bits. Note that this is a static version + // of GetMiscStatus in ActiveXImpl below. Keep them in sync! + // + int miscStatus = NativeMethods.OLEMISC_ACTIVATEWHENVISIBLE | + NativeMethods.OLEMISC_INSIDEOUT | + NativeMethods.OLEMISC_SETCLIENTSITEFIRST | + NativeMethods.OLEMISC_RECOMPOSEONRESIZE; + if (typeof(IButtonControl).IsAssignableFrom(type)) { + miscStatus |= NativeMethods.OLEMISC_ACTSLIKEBUTTON; + } + + key = clsidKey.CreateSubKey("MiscStatus"); + key.SetValue("", miscStatus.ToString()); + key.Close(); + + // Now add the typelib information. Many containers don't + // host Active X controls without a typelib in the registry + // (visual basic won't even show it in the control dialog). + // + Guid typeLibGuid = Marshal.GetTypeLibGuidForAssembly(type.Assembly); + Version assemblyVer = type.Assembly.GetName().Version; + + if (typeLibGuid != Guid.Empty) { + key = clsidKey.CreateSubKey("TypeLib"); + key.SetValue("", "{" + typeLibGuid.ToString() + "}"); + key.Close(); + key = clsidKey.CreateSubKey("Version"); + key.SetValue("", assemblyVer.Major.ToString() + "." + assemblyVer.Minor.ToString()); + key.Close(); + } + + clsidKey.Close(); + } + #endif + + /// + /// Helper method for retrieving an ActiveX property. We abstract these + /// to another method so we do not force JIT the ActiveX codebase. + /// + private void ActiveXUpdateBounds(ref int x, ref int y, ref int width, ref int height, int flags) { + ActiveXInstance.UpdateBounds(ref x, ref y, ref width, ref height, flags); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// + /// This is called by regasm to un-register a control as an ActiveX control. + /// + [ComUnregisterFunction()] + protected static void ActiveXUnregister(Type type) { + + if (type == null) { + throw new ArgumentNullException("type"); + } + + // If the user is not unregistering an AX control, then + // bail now. + // + if (!typeof(Control).IsAssignableFrom(type)) { + return; + } + + RegistryKey clsidKey = null; + + // Unregistration should be very robust and unregister what it can. We eat all exceptions here. + // + try { + clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID", true).CreateSubKey("{" + type.GUID.ToString() + "}"); + + try { + clsidKey.DeleteSubKeyTree("Control"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("Implemented Categories"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("MiscStatus"); + } + catch { + } + try { + clsidKey.DeleteSubKeyTree("TypeLib"); + } + catch { + } + } + finally { + if (clsidKey != null) { + clsidKey.Close(); + } + } + } + #endif + + /// + /// Assigns a new parent control. Sends out the appropriate property change + /// notifications for properties that are affected by the change of parent. + /// + internal virtual void AssignParent(Control value) { + + // Adopt the parent's required scaling bits + if (value != null) { + RequiredScalingEnabled = value.RequiredScalingEnabled; + } + + if (CanAccessProperties) { + // Store the old values for these properties + // + Font oldFont = Font; + Color oldForeColor = ForeColor; + Color oldBackColor = BackColor; + RightToLeft oldRtl = RightToLeft; + bool oldEnabled = Enabled; + bool oldVisible = Visible; + + // Update the parent + // + parent = value; + OnParentChanged(EventArgs.Empty); + + if (GetAnyDisposingInHierarchy()) { + return; + } + + // Compare property values with new parent to old values + // + if (oldEnabled != Enabled) { + OnEnabledChanged(EventArgs.Empty); + } + + // VSWhidbey 320131 + // When a control seems to be going from invisible -> visible, + // yet its parent is being set to null and it's not top level, do not raise OnVisibleChanged. + bool newVisible = Visible; + + if (oldVisible != newVisible && !(!oldVisible && newVisible && parent == null && !GetTopLevel())) { + OnVisibleChanged(EventArgs.Empty); + } + if (!oldFont.Equals(Font)) { + OnFontChanged(EventArgs.Empty); + } + if (!oldForeColor.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + if (!oldBackColor.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + if (oldRtl != RightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + if (Properties.GetObject(PropBindingManager) == null && this.Created) { + // We do not want to call our parent's BindingContext property here. + // We have no idea if us or any of our children are using data binding, + // and invoking the property would just create the binding manager, which + // we don't need. We just blindly notify that the binding manager has + // changed, and if anyone cares, they will do the comparison at that time. + // + OnBindingContextChanged(EventArgs.Empty); + } + } + else { + parent = value; + OnParentChanged(EventArgs.Empty); + } + + SetState(STATE_CHECKEDHOST, false); + if (ParentInternal != null) ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.All); + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnParentChangedDescr)] + public event EventHandler ParentChanged { + add { + Events.AddHandler(EventParent, value); + } + remove { + Events.RemoveHandler(EventParent, value); + } + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. The delegate is called asynchronously and this + /// method returns immediately. You may call this from any thread, even the + /// thread that owns the control's handle. If the control's handle doesn't + /// exist yet, this will follow up the control's parent chain until it finds a + /// control or form that does have a window handle. If no appropriate handle + /// can be found, BeginInvoke will throw an exception. Exceptions within the + /// delegate method are considered untrapped and will be sent to the + /// application's untrapped exception handler. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IAsyncResult BeginInvoke(Delegate method) { + return BeginInvoke(method, null); + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. The delegate is called asynchronously and this + /// method returns immediately. You may call this from any thread, even the + /// thread that owns the control's handle. If the control's handle doesn't + /// exist yet, this will follow up the control's parent chain until it finds a + /// control or form that does have a window handle. If no appropriate handle + /// can be found, BeginInvoke will throw an exception. Exceptions within the + /// delegate method are considered untrapped and will be sent to the + /// application's untrapped exception handler. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + public IAsyncResult BeginInvoke(Delegate method, params Object[] args) { + using (new MultithreadSafeCallScope()) { + Control marshaler = FindMarshalingControl(); + return(IAsyncResult)marshaler.MarshaledInvoke(this, method, args, false); + } + } + + internal void BeginUpdateInternal() { + if (!IsHandleCreated) { + return; + } + if (updateCount == 0) SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); + updateCount++; + } + + /// + /// + /// Brings this control to the front of the zorder. + /// + public void BringToFront() { + if (parent != null) { + parent.Controls.SetChildIndex(this, 0); + } + else if (IsHandleCreated && GetTopLevel() && SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle))) { + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.HWND_TOP, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + } + + /// + /// Specifies whether this control can process the mnemonic or not. A condition to process a mnemonic is that + /// all controls in the parent chain can do it too, but since the semantics for this function can be overriden, + /// we need to call the method on the parent 'recursively' (not exactly since it is not necessarily the same method). + /// + internal virtual bool CanProcessMnemonic() { +#if DEBUG + TraceCanProcessMnemonic(); +#endif + if( !this.Enabled || !this.Visible) { + return false; + } + + if( this.parent != null ){ + return this.parent.CanProcessMnemonic(); + } + + return true; + } + + // Package scope to allow AxHost to override + // + internal virtual bool CanSelectCore() { + if ((controlStyle & ControlStyles.Selectable) != ControlStyles.Selectable) { + return false; + } + + for (Control ctl = this; ctl != null; ctl = ctl.parent) { + if (!ctl.Enabled || !ctl.Visible) { + return false; + } + } + + return true; + } + + /// + /// Searches the parent/owner tree for bottom to find any instance + /// of toFind in the parent/owner tree. + /// + internal static void CheckParentingCycle(Control bottom, Control toFind) { + Form lastOwner = null; + Control lastParent = null; + + for (Control ctl = bottom; ctl != null; ctl = ctl.ParentInternal) { + lastParent = ctl; + if (ctl == toFind) { + throw new ArgumentException(SR.GetString(SR.CircularOwner)); + } + } + + if (lastParent != null) { + if (lastParent is Form) { + Form f = (Form)lastParent; + for (Form form = f; form != null; form = form.OwnerInternal) { + lastOwner = form; + if (form == toFind) { + throw new ArgumentException(SR.GetString(SR.CircularOwner)); + } + } + } + } + + if (lastOwner != null) { + if (lastOwner.ParentInternal != null) { + CheckParentingCycle(lastOwner.ParentInternal, toFind); + } + } + } + /// + /// + /// + private void ChildGotFocus(Control child) { + if (IsActiveX) { + ActiveXOnFocus(true); + } + if (parent != null) { + parent.ChildGotFocus(child); + } + } + + /// + /// + /// Verifies if a control is a child of this control. + /// + public bool Contains(Control ctl) { + while (ctl != null) { + ctl = ctl.ParentInternal; + if (ctl == null) { + return false; + } + if (ctl == this) { + return true; + } + } + return false; + } + + /// + /// + /// constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual AccessibleObject CreateAccessibilityInstance() { + return new ControlAccessibleObject(this); + } + + /// + /// + /// Constructs the new instance of the Controls collection objects. Subclasses + /// should not call base.CreateControlsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual ControlCollection CreateControlsInstance() { + return new System.Windows.Forms.Control.ControlCollection(this); + } + + /// + /// + /// Creates a Graphics for this control. The control's brush, font, foreground + /// color and background color become the default values for the Graphics. + /// The returned Graphics must be disposed through a call to its dispose() + /// method when it is no longer needed. The Graphics Object is only valid for + /// the duration of the current window's message. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public System.Drawing.Graphics CreateGraphics() { + using (new MultithreadSafeCallScope()) + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "CreateGraphicsForControl Demanded"); + IntSecurity.CreateGraphicsForControl.Demand(); + return CreateGraphicsInternal(); + } + } + + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal System.Drawing.Graphics CreateGraphicsInternal() { + return Graphics.FromHwndInternal(this.Handle); + } + + /// + /// + /// Creates a handle for this control. This method is called by the .NET Framework, this should + /// not be called. Inheriting classes should always call base.createHandle when + /// overriding this method. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows) + ] + protected virtual void CreateHandle() { + IntPtr userCookie = IntPtr.Zero; + + if (GetState(STATE_DISPOSED)) { + throw new System.ObjectDisposedException(GetType().Name); + } + + if (GetState(STATE_CREATINGHANDLE)) { + return; + } + + Rectangle originalBounds; + + try { + SetState(STATE_CREATINGHANDLE, true); + + originalBounds = this.Bounds; + + if (Application.UseVisualStyles) { + // Activate theming scope to get theming for controls at design time and when hosted in browser. + // NOTE: If a theming context is already active, this call is very fast, so shouldn't be a perf issue. + userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + } + + CreateParams cp = CreateParams; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + + // Adjust for scrolling of parent... + // + if (parent != null) { + Rectangle parentClient = parent.ClientRectangle; + + if (!parentClient.IsEmpty) { + if (cp.X != NativeMethods.CW_USEDEFAULT) { + cp.X -= parentClient.X; + } + if (cp.Y != NativeMethods.CW_USEDEFAULT) { + cp.Y -= parentClient.Y; + } + } + } + + // And if we are WS_CHILD, ensure we have a parent handle. + // + if (cp.Parent == IntPtr.Zero && (cp.Style & NativeMethods.WS_CHILD) != 0) { + Debug.Assert((cp.ExStyle & NativeMethods.WS_EX_MDICHILD) == 0, "Can't put MDI child forms on the parking form"); + Application.ParkHandle(cp, this.DpiAwarenessContext); + } + + window.CreateHandle(cp); + + UpdateReflectParent(true); + + } + finally { + SetState(STATE_CREATINGHANDLE, false); + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + // VSWhidbey #121382 - For certain controls (e.g., ComboBox) CreateWindowEx + // may cause the control to resize. WM_SETWINDOWPOSCHANGED takes care of + // the control being resized, but our layout container may need a refresh as well. + if (this.Bounds != originalBounds) { + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + } + + /// + /// + /// Forces the creation of the control. This includes the creation of the handle, + /// and any child controls. + /// + public void CreateControl() { + bool controlIsAlreadyCreated = this.Created; + CreateControl(false); + + if (Properties.GetObject(PropBindingManager) == null && ParentInternal != null && !controlIsAlreadyCreated) { + // We do not want to call our parent's BindingContext property here. + // We have no idea if us or any of our children are using data binding, + // and invoking the property would just create the binding manager, which + // we don't need. We just blindly notify that the binding manager has + // changed, and if anyone cares, they will do the comparison at that time. + OnBindingContextChanged(EventArgs.Empty); + } + } + + /// + /// Forces the creation of the control. This includes the creation of the handle, + /// and any child controls. + /// + /// Determines whether we should create the handle after checking the Visible + /// property of the control or not. + /// + /// + internal void CreateControl(bool fIgnoreVisible) { + bool ready = (state & STATE_CREATED) == 0; + + // PERF: Only "create" the control if it is + // : visible. This has the effect of delayed handle creation of + // : hidden controls. + // + ready = ready && Visible; + + if (ready || fIgnoreVisible) { + state |= STATE_CREATED; + bool createdOK = false; + try { + if (!IsHandleCreated) CreateHandle(); + + // 58041 - must snapshot this array because + // z-order updates from Windows may rearrange it! + // + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + Control[] controlSnapshot = new Control[controlsCollection.Count]; + controlsCollection.CopyTo(controlSnapshot, 0); + + foreach(Control ctl in controlSnapshot) { + if (ctl.IsHandleCreated) { + ctl.SetParentHandle(Handle); + } + ctl.CreateControl(fIgnoreVisible); + } + } + + createdOK = true; + } + finally { + if (!createdOK) + state &= (~STATE_CREATED); + } + OnCreateControl(); + } + } + + + /// + /// + /// Sends the message to the default window proc. + /// + /* Primarily here for Form to override */ + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void DefWndProc(ref Message m) { + window.DefWndProc(ref m); + } + + + /// + /// + /// Destroys the handle associated with this control. Inheriting classes should + /// always call base.destroyHandle. + /// + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void DestroyHandle() { + + if (RecreatingHandle) { + if (threadCallbackList != null) { + // See if we have a thread marshaling request pending. If so, we will need to + // re-post it after recreating the handle. + // + lock (threadCallbackList) { + if (threadCallbackMessage != 0) { + NativeMethods.MSG msg = new NativeMethods.MSG(); + if (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, Handle), threadCallbackMessage, + threadCallbackMessage, NativeMethods.PM_NOREMOVE)) { + + SetState(STATE_THREADMARSHALLPENDING, true); + } + } + } + } + } + + // If we're not recreating the handle, then any items in the thread callback list will + // be orphaned. An orphaned item is bad, because it will cause the thread to never + // wake up. So, we put exceptions into all these items and wake up all threads. + // If we are recreating the handle, then we're fine because recreation will re-post + // the thread callback message to the new handle for us. + // + if (!RecreatingHandle) { + if (threadCallbackList != null) { + lock (threadCallbackList) { + Exception ex = new System.ObjectDisposedException(GetType().Name); + + while (threadCallbackList.Count > 0) { + ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue(); + entry.exception = ex; + entry.Complete(); + } + } + } + } + + if (0 != (NativeMethods.WS_EX_MDICHILD & (int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(window, InternalHandle), NativeMethods.GWL_EXSTYLE))) { + UnsafeNativeMethods.DefMDIChildProc(InternalHandle, NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + } + else { + window.DestroyHandle(); + } + + trackMouseEvent = null; + } + + /// + /// + /// Disposes of the resources (other than memory) used by the + /// + /// . + /// + protected override void Dispose(bool disposing) { + if (GetState(STATE_OWNCTLBRUSH)) { + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + Properties.SetObject(PropBackBrush, null); + } + } + + //set reflectparent = null regardless of whether we are in the finalizer thread or not. + UpdateReflectParent(false); + if (disposing) { + if (GetState(STATE_DISPOSING)) { + return; + } + + if (GetState(STATE_CREATINGHANDLE)) { + throw new InvalidOperationException(SR.GetString(SR.ClosingWhileCreatingHandle, "Dispose")); + // I imagine most subclasses will get themselves in a half disposed state + // if this exception is thrown, but things will be equally broken if we ignore this error, + // and this way at least the user knows what they did wrong. + } + + SetState(STATE_DISPOSING, true); + this.SuspendLayout(); + try { + DisposeAxControls(); + + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + contextMenu.Disposed -= new EventHandler(DetachContextMenu); + } + + ResetBindings(); + + if (IsHandleCreated) DestroyHandle(); + + if (parent != null) { + parent.Controls.Remove(this); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + ctl.parent = null; + ctl.Dispose(); + } + Properties.SetObject(PropControlsCollection, null); + } + + base.Dispose(disposing); + } + finally { + this.ResumeLayout(false); + SetState(STATE_DISPOSING, false); + SetState(STATE_DISPOSED, true); + } + } + else { + +#if FINALIZATION_WATCH + if (!GetState(STATE_DISPOSED)) { + Debug.Fail("Control of type '" + GetType().FullName +"' is being finalized that wasn't disposed\n" + allocationSite); + } +#endif + // This same post is done in NativeWindow's finalize method, so if you change + // it, change it there too. + // + if (window != null) { + window.ForceExitMessageLoop(); + } + base.Dispose(disposing); + } + } + + // Package scope to allow AxHost to override. + // + internal virtual void DisposeAxControls() { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].DisposeAxControls(); + } + } + } + + /// + /// + /// Begins a drag operation. The allowedEffects determine which + /// drag operations can occur. If the drag operation needs to interop + /// with applications in another process, data should either be + /// a base managed class (String, Bitmap, or Metafile) or some Object + /// that implements System.Runtime.Serialization.ISerializable. data can also be any Object that + /// implements System.Windows.Forms.IDataObject. + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + public DragDropEffects DoDragDrop(Object data, DragDropEffects allowedEffects) { + int[] finalEffect = new int[] {(int)DragDropEffects.None}; + UnsafeNativeMethods.IOleDropSource dropSource = new DropSource( this ); + IComDataObject dataObject = null; + + if (data is IComDataObject) { + dataObject = (IComDataObject)data; + } + else { + + DataObject iwdata = null; + if (data is IDataObject) { + iwdata = new DataObject((IDataObject)data); + } + else { + iwdata = new DataObject(); + iwdata.SetData(data); + } + dataObject = (IComDataObject)iwdata; + } + + try { + SafeNativeMethods.DoDragDrop(dataObject, dropSource, (int)allowedEffects, finalEffect); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + + return(DragDropEffects)finalEffect[0]; + } + + /// + /// + // Trinity are currently calling IViewObject::Draw in order to render controls on Word & Excel + // before they are in place active. However this method is private and they need a public way to do this. + // This means that we will need to add a public method to control that supports rendering to a Bitmap: + // public virtual void DrawToBitmap(Bitmap bmp, RectangleF targetBounds) + // where target bounds is the bounds within which the control should render. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly"), + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), // Using Bitmap instead of Image intentionally + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // targetBounds is the name of the param passed in. + // So we don't have to localize it. + UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows) + ] + public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) { + + if (bitmap == null) { + throw new ArgumentNullException("bitmap"); + } + + if (targetBounds.Width <= 0 || targetBounds.Height <= 0 + || targetBounds.X < 0 || targetBounds.Y < 0) { + throw new ArgumentException("targetBounds"); + } + + if (!IsHandleCreated) { + CreateHandle(); + } + + + int width = Math.Min(this.Width, targetBounds.Width); + int height = Math.Min(this.Height, targetBounds.Height); + + using (Bitmap image = new Bitmap(width, height, bitmap.PixelFormat)) { + using (Graphics g = Graphics.FromImage(image)) { + IntPtr hDc = g.GetHdc(); + //send the actual wm_print message + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.WM_PRINT, (IntPtr)hDc, + (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT)); + + //now BLT the result to the destination bitmap. + using (Graphics destGraphics = Graphics.FromImage(bitmap)) { + IntPtr desthDC = destGraphics.GetHdc(); + SafeNativeMethods.BitBlt(new HandleRef(destGraphics, desthDC), targetBounds.X, targetBounds.Y, width, height, + new HandleRef(g, hDc), 0, 0, 0xcc0020); + destGraphics.ReleaseHdcInternal(desthDC); + } + + g.ReleaseHdcInternal(hDc); + } + } + } + + /// + /// + /// Retrieves the return value of the asynchronous operation + /// represented by the IAsyncResult interface passed. If the + /// async operation has not been completed, this function will + /// block until the result is available. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public Object EndInvoke(IAsyncResult asyncResult) { + using (new MultithreadSafeCallScope()) + { + if (asyncResult == null) { + throw new ArgumentNullException("asyncResult"); + } + + ThreadMethodEntry entry = asyncResult as ThreadMethodEntry; + if (entry == null) { + throw new ArgumentException(SR.GetString(SR.ControlBadAsyncResult),"asyncResult"); + } + Debug.Assert(this == entry.caller, "Called BeginInvoke on one control, and the corresponding EndInvoke on a different control"); + + if (!asyncResult.IsCompleted) { + int pid; // ignored + Control marshaler = FindMarshalingControl(); + if (SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(marshaler, marshaler.Handle), out pid) == SafeNativeMethods.GetCurrentThreadId()) { + marshaler.InvokeMarshaledCallbacks(); + } + else { + marshaler = entry.marshaler; + marshaler.WaitForWaitHandle(asyncResult.AsyncWaitHandle); + } + } + + Debug.Assert(asyncResult.IsCompleted, "Why isn't this asyncResult done yet?"); + if (entry.exception != null) { + throw entry.exception; + } + return entry.retVal; + } + } + + internal bool EndUpdateInternal() { + return EndUpdateInternal(true); + } + + internal bool EndUpdateInternal(bool invalidate) { + if (updateCount > 0) { + Debug.Assert(IsHandleCreated, "Handle should be created by now"); + updateCount--; + if (updateCount == 0) { + SendMessage(NativeMethods.WM_SETREDRAW, -1, 0); + if (invalidate) { + Invalidate(); + } + + } + return true; + } + else { + return false; + } + } + + /// + /// + /// Retrieves the form that this control is on. The control's parent + /// may not be the same as the form. + /// + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + public Form FindForm() { + return FindFormInternal(); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal Form FindFormInternal() { + Control cur = this; + while (cur != null && !(cur is Form) ) { + cur = cur.ParentInternal; + } + return (Form)cur; + } + + /// + /// Attempts to find a control Object that we can use to marshal + /// calls. We must marshal calls to a control with a window + /// handle, so we traverse up the parent chain until we find one. + /// Failing that, we just return ouselves. + /// + /// + private Control FindMarshalingControl() { + // + lock(this) { + Control c = this; + + while (c != null && !c.IsHandleCreated) { + Control p = c.ParentInternal; + c = p; + } + + if (c == null) { + // No control with a created handle. We + // just use our own control. MarshaledInvoke + // will throw an exception because there + // is no handle. + // + c = this; + } + else { + Debug.Assert(c.IsHandleCreated, "FindMarshalingControl chose a bad control."); + } + + return(Control)c; + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool GetTopLevel() { + return(state & STATE_TOPLEVEL) != 0; + } + + /// + /// Used by AxHost to fire the CreateHandle event. + /// + /// + internal void RaiseCreateHandleEvent(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventHandleCreated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseKeyEvent(object key, KeyEventArgs e) { + KeyEventHandler handler = (KeyEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseMouseEvent(object key, MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Sets focus to the control. + /// Attempts to set focus to this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public bool Focus() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::Focus - " + this.Name); + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + //here, we call our internal method (which form overrides) + //see comments in FocusInternal + // + return FocusInternal(); + } + + /// + /// Internal method for setting focus to the control. + /// Form overrides this method - because MDI child forms + /// need to be focused by calling the MDIACTIVATE message. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal virtual bool FocusInternal() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::FocusInternal - " + this.Name); + if (CanFocus){ + UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle)); + } + if (Focused && this.ParentInternal != null) { + IContainerControl c = this.ParentInternal.GetContainerControlInternal(); + + if (c != null) { + if (c is ContainerControl) { + ((ContainerControl)c).SetActiveControlInternal(this); + } + else { + c.ActiveControl = this; + } + } + } + + + return Focused; + } + + /// + /// + /// Returns the control that is currently associated with handle. + /// This method will search up the HWND parent chain until it finds some + /// handle that is associated with with a control. This method is more + /// robust that fromHandle because it will correctly return controls + /// that own more than one handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Control FromChildHandle(IntPtr handle) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + return FromChildHandleInternal(handle); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static Control FromChildHandleInternal(IntPtr handle) { + while (handle != IntPtr.Zero) { + Control ctl = FromHandleInternal(handle); + if (ctl != null) return ctl; + handle = UnsafeNativeMethods.GetAncestor(new HandleRef(null, handle), NativeMethods.GA_PARENT); + } + return null; + } + + /// + /// + /// Returns the control that is currently associated with handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Control FromHandle(IntPtr handle) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + return FromHandleInternal(handle); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static Control FromHandleInternal(IntPtr handle) { + NativeWindow w = NativeWindow.FromHandle(handle); + while (w != null && !(w is ControlNativeWindow)) { + w = w.PreviousWindow; + } + + if (w is ControlNativeWindow) { + return((ControlNativeWindow)w).GetControl(); + } + return null; + } + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal Size ApplySizeConstraints(int width, int height) { + return ApplyBoundsConstraints(0,0,width, height).Size; + } + + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal Size ApplySizeConstraints(Size proposedSize) { + return ApplyBoundsConstraints(0,0,proposedSize.Width,proposedSize.Height).Size; + } + + internal virtual Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + + // COMPAT: 529991 - in Everett we would allow you to set negative values in pre-handle mode + // in Whidbey, if you've set Min/Max size we will constrain you to 0,0. Everett apps didnt + // have min/max size on control, which is why this works. + if (MaximumSize != Size.Empty || MinimumSize != Size.Empty) { + Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(MaximumSize); + Rectangle newBounds = new Rectangle(suggestedX, suggestedY, 0,0); + + // Clip the size to maximum and inflate it to minimum as necessary. + newBounds.Size = LayoutUtils.IntersectSizes(new Size(proposedWidth,proposedHeight), maximumSize); + newBounds.Size = LayoutUtils.UnionSizes(newBounds.Size, MinimumSize); + + return newBounds; + } + + return new Rectangle(suggestedX, suggestedY, proposedWidth, proposedHeight); + } + + + + /// + /// + /// Retrieves the child control that is located at the specified client + /// coordinates. + /// + public Control GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue) { + int value = (int)skipValue; + // Since this is a Flags Enumeration... the only way to validate skipValue is by checking if its within the range. + if(value < 0 || value > 7) + { + throw new InvalidEnumArgumentException("skipValue", value, typeof(GetChildAtPointSkip)); + } + + IntPtr hwnd = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, Handle), pt.X, pt.Y, value); + + // Security Reviewed + // While doing a security review it was noticed that GetChildAtPoint + // does work to ensure that you can only gain access to children of your own control, + // but the methods it uses to determine the children demand all window permission first, + // negating the extra check. + // It is OK to return child windows for children within your own control for semitrust. + + // Hence calling the Internal methods to ByPass the Security Demand... + // for IntSecurity.ControlFromHandleOrLocation == ALLWindows. + + Control ctl = FromChildHandleInternal(hwnd); + if (ctl != null && !IsDescendant(ctl)) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + } + + return(ctl == this) ? null : ctl; + } + + + /// + /// + /// Retrieves the child control that is located at the specified client + /// coordinates. + /// + public Control GetChildAtPoint(Point pt) { + return GetChildAtPoint(pt, GetChildAtPointSkip.None); + } + + /// + /// + /// Returns the closest ContainerControl in the control's chain of + /// parent controls and forms. + /// + public IContainerControl GetContainerControl() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + return GetContainerControlInternal(); + } + + private static bool IsFocusManagingContainerControl(Control ctl) { + return ((ctl.controlStyle & ControlStyles.ContainerControl) == ControlStyles.ContainerControl && ctl is IContainerControl); + } + + /// + /// This new Internal method checks the updateCount to signify that the control is within the "BeginUpdate" and "EndUpdate" cycle. + /// Check out Vs whidbey : 310110 : for usage of this. The Treeview tries to ForceUpdate the scrollbars by calling "WM_SETREDRAW" + /// even if the control in "Begin - End" update cycle. Using thie Function we can guard against repetitively redrawing the control. + /// + internal bool IsUpdating() + { + return (updateCount > 0); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal IContainerControl GetContainerControlInternal() { + Control c = this; + + // VsWhidbey 434959 : Refer to IsContainerControl property for more details. + if (c != null && IsContainerControl) + { + c = c.ParentInternal; + } + while (c != null && (!IsFocusManagingContainerControl(c))) { + c = c.ParentInternal; + } + return (IContainerControl)c; + } + + // Essentially an Hfont; see inner class for details. + private static FontHandleWrapper GetDefaultFontHandleWrapper() { + if (defaultFontHandleWrapper == null) { + defaultFontHandleWrapper = new FontHandleWrapper(DefaultFont); + } + + return defaultFontHandleWrapper; + } + + internal IntPtr GetHRgn(Region region) { + Graphics graphics = CreateGraphicsInternal(); + IntPtr handle = region.GetHrgn(graphics); + System.Internal.HandleCollector.Add(handle, NativeMethods.CommonHandles.GDI); + graphics.Dispose(); + return handle; + } + + /// + /// + /// This is a helper method that is called by ScaleControl to retrieve the bounds + /// that the control should be scaled by. You may override this method if you + /// wish to reuse ScaleControl's scaling logic but you need to supply your own + /// bounds. The default implementation returns scaled bounds that take into + /// account the BoundsSpecified, whether the control is top level, and whether + /// the control is fixed width or auto size, and any adornments the control may have. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + + // We should not include the window adornments in our calculation, + // because windows scales them for us. + NativeMethods.RECT adornments = new NativeMethods.RECT(0, 0, 0, 0); + CreateParams cp = CreateParams; + AdjustWindowRectEx(ref adornments, cp.Style, HasMenu, cp.ExStyle); + + float dx = factor.Width; + float dy = factor.Height; + + int sx = bounds.X; + int sy = bounds.Y; + + // Don't reposition top level controls. Also, if we're in + // design mode, don't reposition the root component. + bool scaleLoc = !GetState(STATE_TOPLEVEL); + if (scaleLoc) { + ISite site = Site; + if (site != null && site.DesignMode) { + IDesignerHost host = site.GetService(typeof(IDesignerHost)) as IDesignerHost; + if (host != null && host.RootComponent == this) { + scaleLoc = false; + } + } + } + + if (scaleLoc) { + if ((specified & BoundsSpecified.X) != 0) { + sx = (int)Math.Round(bounds.X * dx); + } + + if ((specified & BoundsSpecified.Y) != 0) { + sy = (int)Math.Round(bounds.Y * dy); + } + } + + int sw = bounds.Width; + int sh = bounds.Height; + + // Do this even for auto sized controls. They'll "snap back", but it is important to size them in case + // they are anchored. + if ((controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth && (specified & BoundsSpecified.Width) != 0) { + int adornmentWidth = (adornments.right - adornments.left); + int localWidth = bounds.Width - adornmentWidth; + sw = (int)Math.Round(localWidth * dx) + adornmentWidth; + } + if ((controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight && (specified & BoundsSpecified.Height) != 0) { + int adornmentHeight = (adornments.bottom - adornments.top); + int localHeight = bounds.Height - adornmentHeight; + sh = (int)Math.Round(localHeight * dy) + adornmentHeight; + } + + return new Rectangle(sx, sy, sw, sh); + } + + private MouseButtons GetXButton(int wparam) { + switch (wparam) { + case NativeMethods.XBUTTON1: + return MouseButtons.XButton1; + case NativeMethods.XBUTTON2: + return MouseButtons.XButton2; + } + Debug.Fail("Unknown XButton: " + wparam); + return MouseButtons.None; + } + + internal virtual bool GetVisibleCore() { + // We are only visible if our parent is visible + if (!GetState(STATE_VISIBLE)) + return false; + else if (ParentInternal == null) + return true; + else + return ParentInternal.GetVisibleCore(); + } + + internal bool GetAnyDisposingInHierarchy() { + Control up = this; + bool isDisposing = false; + while (up != null) { + if (up.Disposing) { + isDisposing = true; + break; + } + up = up.parent; + } + return isDisposing; + } + + private MenuItem GetMenuItemFromHandleId(IntPtr hmenu, int item) { + MenuItem mi = null; + int id = UnsafeNativeMethods.GetMenuItemID(new HandleRef(null, hmenu), item); + if (id == unchecked((int)0xFFFFFFFF)) { + IntPtr childMenu = IntPtr.Zero; + childMenu = UnsafeNativeMethods.GetSubMenu(new HandleRef(null, hmenu), item); + int count = UnsafeNativeMethods.GetMenuItemCount(new HandleRef(null, childMenu)); + MenuItem found = null; + for (int i=0; i + /// - Returns child controls sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Returns a TabIndex sorted array of ControlTabOrderHolder objects. + /// + private ArrayList GetChildControlsTabOrderList(bool handleCreatedOnly) { + ArrayList holders = new ArrayList(); + + foreach (Control c in Controls) { + if (!handleCreatedOnly || c.IsHandleCreated) { + holders.Add(new ControlTabOrderHolder(holders.Count, c.TabIndex, c)); + } + } + + holders.Sort(new ControlTabOrderComparer()); + + return holders; + } + + /// + /// - Returns native child windows sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Child windows with no corresponding Control objects (and therefore no discernable TabIndex) + /// are sorted to the front of the list (but remain in relative z-order to one another). + /// - This version returns a sorted array of integers, representing the original z-order + /// based indexes of the native child windows. + /// + private int[] GetChildWindowsInTabOrder() { + ArrayList holders = GetChildWindowsTabOrderList(); + + int[] indexes = new int[holders.Count]; + + for (int i = 0; i < holders.Count; i++) { + indexes[i] = ((ControlTabOrderHolder) holders[i]).oldOrder; + } + + return indexes; + } + + /// + /// - Returns child controls sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - This version returns a sorted array of control references. + /// + internal Control[] GetChildControlsInTabOrder(bool handleCreatedOnly) { + ArrayList holders = GetChildControlsTabOrderList(handleCreatedOnly); + + Control[] ctls = new Control[holders.Count]; + + for (int i = 0; i < holders.Count; i++) { + ctls[i] = ((ControlTabOrderHolder) holders[i]).control; + } + + return ctls; + } + + /// + /// This class contains a control and associates it with a z-order. + /// This is used when sorting controls based on tab index first, + /// z-order second. + /// + private class ControlTabOrderHolder { + internal readonly int oldOrder; + internal readonly int newOrder; + internal readonly Control control; + + internal ControlTabOrderHolder(int oldOrder, int newOrder, Control control) { + this.oldOrder = oldOrder; + this.newOrder = newOrder; + this.control = control; + } + } + + /// + /// Used to sort controls based on tab index and z-order. + /// + private class ControlTabOrderComparer : IComparer { + int IComparer.Compare(Object x, Object y) { + ControlTabOrderHolder hx = (ControlTabOrderHolder) x; + ControlTabOrderHolder hy = (ControlTabOrderHolder) y; + + int delta = hx.newOrder - hy.newOrder; + if (delta == 0) + delta = hx.oldOrder - hy.oldOrder; + + return delta; + } + } + + /// + /// Given a native window handle, returns array of handles to window's children (in z-order). + /// + private static ArrayList GetChildWindows(IntPtr hWndParent) { + ArrayList windows = new ArrayList(); + + for (IntPtr hWndChild = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWndParent), NativeMethods.GW_CHILD); + hWndChild != IntPtr.Zero; + hWndChild = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWndChild), NativeMethods.GW_HWNDNEXT)) { + windows.Add(hWndChild); + } + + return windows; + } + + /// + /// - Returns native child windows sorted according to their TabIndex property order. + /// - Controls with the same TabIndex remain in original relative child index order (= z-order). + /// - Child windows with no corresponding Control objects (and therefore no discernable TabIndex) + /// are sorted to the front of the list (but remain in relative z-order to one another). + /// - Returns a TabIndex sorted array of ControlTabOrderHolder objects. + /// + private ArrayList GetChildWindowsTabOrderList() { + ArrayList holders = new ArrayList(); + ArrayList windows = GetChildWindows(this.Handle); + + foreach (IntPtr hWnd in windows) { + Control ctl = FromHandleInternal(hWnd); + int tabIndex = (ctl == null) ? -1 : ctl.TabIndex; + holders.Add(new ControlTabOrderHolder(holders.Count, tabIndex, ctl)); + } + + holders.Sort(new ControlTabOrderComparer()); + + return holders; + } + + // + + internal virtual Control GetFirstChildControlInTabOrder(bool forward) { + + ControlCollection ctlControls = (ControlCollection)this.Properties.GetObject(PropControlsCollection); + + Control found = null; + if (ctlControls != null) { + if (forward) { + for (int c = 0; c < ctlControls.Count; c++) { + if (found == null || found.tabIndex > ctlControls[c].tabIndex) { + found = ctlControls[c]; + } + } + } + else { + + // Cycle through the controls in reverse z-order looking for the one with the highest + // tab index. + // + for (int c = ctlControls.Count - 1; c >= 0; c--) { + if (found == null || found.tabIndex < ctlControls[c].tabIndex) { + found = ctlControls[c]; + } + } + } + } + return found; + + } + + /// + /// + /// Retrieves the next control in the tab order of child controls. + /// + public Control GetNextControl(Control ctl, bool forward) { + if (!Contains(ctl)) { + ctl = this; + } + + if (forward) { + ControlCollection ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + + if (ctlControls != null && ctlControls.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl))) { + Control found = ctl.GetFirstChildControlInTabOrder(/*forward=*/true); + if (found != null) { + return found; + } + } + + while (ctl != this) { + int targetIndex = ctl.tabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.parent; + + // Cycle through the controls in z-order looking for the one with the next highest + // tab index. Because there can be dups, we have to start with the existing tab index and + // remember to exclude the current control. + // + int parentControlCount = 0; + + ControlCollection parentControls = (ControlCollection)p.Properties.GetObject(PropControlsCollection); + + if (parentControls != null) { + parentControlCount = parentControls.Count; + } + + for (int c = 0; c < parentControlCount; c++) { + + // The logic for this is a bit lengthy, so I have broken it into separate + // caluses: + + // We are not interested in ourself. + // + if (parentControls[c] != ctl) { + + // We are interested in controls with >= tab indexes to ctl. We must include those + // controls with equal indexes to account for duplicate indexes. + // + if (parentControls[c].tabIndex >= targetIndex) { + + // Check to see if this control replaces the "best match" we've already + // found. + // + if (found == null || found.tabIndex > parentControls[c].tabIndex) { + + // Finally, check to make sure that if this tab index is the same as ctl, + // that we've already encountered ctl in the z-order. If it isn't the same, + // than we're more than happy with it. + // + if (parentControls[c].tabIndex != targetIndex || hitCtl) { + found = parentControls[c]; + } + } + } + } + else { + // We track when we have encountered "ctl". We never want to select ctl again, but + // we want to know when we've seen it in case we find another control with the same tab index. + // + hitCtl = true; + } + } + + if (found != null) { + return found; + } + + ctl = ctl.parent; + } + } + else { + if (ctl != this) { + + int targetIndex = ctl.tabIndex; + bool hitCtl = false; + Control found = null; + Control p = ctl.parent; + + // Cycle through the controls in reverse z-order looking for the next lowest tab index. We must + // start with the same tab index as ctl, because there can be dups. + // + int parentControlCount = 0; + + ControlCollection parentControls = (ControlCollection)p.Properties.GetObject(PropControlsCollection); + + if (parentControls != null) { + parentControlCount = parentControls.Count; + } + + for (int c = parentControlCount - 1; c >= 0; c--) { + + // The logic for this is a bit lengthy, so I have broken it into separate + // caluses: + + // We are not interested in ourself. + // + if (parentControls[c] != ctl) { + + // We are interested in controls with <= tab indexes to ctl. We must include those + // controls with equal indexes to account for duplicate indexes. + // + if (parentControls[c].tabIndex <= targetIndex) { + + // Check to see if this control replaces the "best match" we've already + // found. + // + if (found == null || found.tabIndex < parentControls[c].tabIndex) { + + // Finally, check to make sure that if this tab index is the same as ctl, + // that we've already encountered ctl in the z-order. If it isn't the same, + // than we're more than happy with it. + // + if (parentControls[c].tabIndex != targetIndex || hitCtl) { + found = parentControls[c]; + } + } + } + } + else { + // We track when we have encountered "ctl". We never want to select ctl again, but + // we want to know when we've seen it in case we find another control with the same tab index. + // + hitCtl = true; + } + } + + // If we were unable to find a control we should return the control's parent. However, if that parent is us, return + // NULL. + // + if (found != null) { + ctl = found; + } + else { + if (p == this) { + return null; + } + else { + return p; + } + } + } + + // We found a control. Walk into this control to find the proper sub control within it to select. + // + ControlCollection ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + + while (ctlControls != null && ctlControls.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl))) { + Control found = ctl.GetFirstChildControlInTabOrder(/*forward=*/false); + if (found != null) { + ctl = found; + ctlControls = (ControlCollection)ctl.Properties.GetObject(PropControlsCollection); + } + else { + break; + } + } + } + + return ctl == this? null: ctl; + } + + /// + /// Return ((Control) window).Handle if window is a Control. + /// Otherwise, demands permission for AllWindows and returns window.Handle + /// + internal static IntPtr GetSafeHandle(IWin32Window window) + { + Debug.Assert(window != null, "window is null in Control.GetSafeHandle"); + IntPtr hWnd = IntPtr.Zero; + Control control = window as Control; + if (control != null) { + hWnd = control.Handle; + Debug.Assert(hWnd == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))); + return hWnd; + } + else { + IntSecurity.AllWindows.Demand(); + hWnd = window.Handle; + if (hWnd == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(null, hWnd))) { + return hWnd; + } + else { + throw new Win32Exception(NativeMethods.ERROR_INVALID_HANDLE); + } + } + } + + /// + /// Retrieves the current value of the specified bit in the control's state. + /// + internal bool GetState(int flag) { + return(state & flag) != 0; + } + + /// + /// Retrieves the current value of the specified bit in the control's state2. + /// + private bool GetState2(int flag) + { + return (state2 & flag) != 0; + } + + /// + /// + /// Retrieves the current value of the specified bit in the control's style. + /// NOTE: This is control style, not the Win32 style of the hWnd. + /// + protected bool GetStyle(ControlStyles flag) { + return (controlStyle & flag) == flag; + } + + /// + /// + /// Hides the control by setting the visible property to false; + /// + public void Hide() { + Visible = false; + } + + + /// + /// Sets up the TrackMouseEvent for listening for the + /// mouse leave event. + /// + /// + private void HookMouseEvent() { + if (!GetState(STATE_TRACKINGMOUSEEVENT)) { + SetState(STATE_TRACKINGMOUSEEVENT, true); + + if (trackMouseEvent == null) { + trackMouseEvent = new NativeMethods.TRACKMOUSEEVENT(); + trackMouseEvent.dwFlags = NativeMethods.TME_LEAVE | NativeMethods.TME_HOVER; + trackMouseEvent.hwndTrack = Handle; + } + + SafeNativeMethods.TrackMouseEvent(trackMouseEvent); + } + } + + /// + /// + /// Called after the control has been added to another container. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void InitLayout() { + LayoutEngine.InitLayout(this, BoundsSpecified.All); + } + + /// + /// This method initializes the scaling bits for this control based on + /// the bounds. + /// + private void InitScaling(BoundsSpecified specified) { + requiredScaling |= (byte)((int)specified & RequiredScalingMask); + } + + // Sets the text and background colors of the DC, and returns the background HBRUSH. + // This gets called from some variation on WM_CTLCOLOR... + // Virtual because Scrollbar insists on being different. + // + // NOTE: this message may not have originally been sent to this HWND. + // + + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + internal virtual IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) { + if (!GetStyle(ControlStyles.UserPaint)) { + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), ColorTranslator.ToWin32(ForeColor)); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(BackColor)); + return BackColorBrush; + } + else { + return UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH); + } + } + + #if WIN95_SUPPORT + /// + /// Initializes mouse wheel support. This may involve registering some windows + /// messages on older operating systems. + /// + /// + private void InitMouseWheelSupport() { + if (!mouseWheelInit) { + // If we are running on less than NT4 or less that Win98 then we must use + // the manual mousewheel stuff... + // + mouseWheelRoutingNeeded = !SystemInformation.NativeMouseWheelSupport; + + if (mouseWheelRoutingNeeded) { + IntPtr hwndMouseWheel = IntPtr.Zero; + + // Check for the MouseZ "service". This is a little app that generated the + // MSH_MOUSEWHEEL messages by monitoring the hardware. If this app isn't + // found, then there is no support for MouseWheels on the system. + // + hwndMouseWheel = UnsafeNativeMethods.FindWindow(NativeMethods.MOUSEZ_CLASSNAME, NativeMethods.MOUSEZ_TITLE); + + if (hwndMouseWheel != IntPtr.Zero) { + + // Register the MSH_MOUSEWHEEL message... we look for this in the + // wndProc, and treat it just like WM_MOUSEWHEEL. + // + int message = SafeNativeMethods.RegisterWindowMessage(NativeMethods.MSH_MOUSEWHEEL); + + if (message != 0) { + mouseWheelMessage = message; + } + } + } + mouseWheelInit = true; + } + } + #endif + + /// + /// + /// Invalidates a region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Region region) { + Invalidate(region, false); + } + + /// + /// + /// Invalidates a region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Region region, bool invalidateChildren) { + if (region == null) { + Invalidate(invalidateChildren); + } + else if (IsHandleCreated) { + IntPtr regionHandle = GetHRgn(region); + + try { + Debug.Assert(regionHandle != IntPtr.Zero, "Region wasn't null but HRGN is?"); + if (invalidateChildren) { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), + null, new HandleRef(region, regionHandle), + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + // It's safe to invoke InvalidateRgn from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRgn(new HandleRef(this, Handle), + new HandleRef(region, regionHandle), + !GetStyle(ControlStyles.Opaque)); + } + } + } + finally { + SafeNativeMethods.DeleteObject(new HandleRef(region, regionHandle)); + } + + Rectangle bounds = Rectangle.Empty; + + // gpr: We shouldn't have to create a Graphics for this... + using (Graphics graphics = CreateGraphicsInternal()) { + bounds = Rectangle.Ceiling(region.GetBounds(graphics)); + } + + OnInvalidated(new InvalidateEventArgs(bounds)); + } + } + + + /// + /// + /// Invalidates the control and causes a paint message to be sent to the control. + /// This will not force a synchronous paint to occur, calling update after + /// invalidate will force a synchronous paint. + /// + public void Invalidate() { + Invalidate(false); + } + + /// + /// + /// Invalidates the control and causes a paint message to be sent to the control. + /// This will not force a synchronous paint to occur, calling update after + /// invalidate will force a synchronous paint. + /// + public void Invalidate(bool invalidateChildren) { + if (IsHandleCreated) { + if (invalidateChildren) { + SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle), + null, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + // It's safe to invoke InvalidateRect from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle), + null, + (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque); + } + } + + NotifyInvalidate(this.ClientRectangle); + } + } + + /// + /// + /// Invalidates a rectangular region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Rectangle rc) { + Invalidate(rc, false); + } + + /// + /// + /// Invalidates a rectangular region of the control and causes a paint message + /// to be sent to the control. This will not force a synchronous paint to + /// occur, calling update after invalidate will force a + /// synchronous paint. + /// + public void Invalidate(Rectangle rc, bool invalidateChildren) { + if (rc.IsEmpty) { + Invalidate(invalidateChildren); + } + else if (IsHandleCreated) { + if (invalidateChildren) { + NativeMethods.RECT rcArea = NativeMethods.RECT.FromXYWH(rc.X, rc.Y, rc.Width, rc.Height); + SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle), + ref rcArea, NativeMethods.NullHandleRef, + NativeMethods.RDW_INVALIDATE | + NativeMethods.RDW_ERASE | + NativeMethods.RDW_ALLCHILDREN); + } + else { + NativeMethods.RECT rcArea = + NativeMethods.RECT.FromXYWH(rc.X, rc.Y, rc.Width, + rc.Height); + + // It's safe to invoke InvalidateRect from a separate thread. + using (new MultithreadSafeCallScope()) + { + SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle), + ref rcArea, + (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque); + } + } + NotifyInvalidate(rc); + } + } + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. It is an error to call this on the same thread that + /// the control belongs to. If the control's handle doesn't exist yet, this will + /// follow up the control's parent chain until it finds a control or form that does + /// have a window handle. If no appropriate handle can be found, invoke will throw + /// an exception. Exceptions that are raised during the call will be + /// propapgated back to the caller. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + public Object Invoke(Delegate method) { + return Invoke(method, null); + } + + /// + /// + /// Executes the given delegate on the thread that owns this Control's + /// underlying window handle. It is an error to call this on the same thread that + /// the control belongs to. If the control's handle doesn't exist yet, this will + /// follow up the control's parent chain until it finds a control or form that does + /// have a window handle. If no appropriate handle can be found, invoke will throw + /// an exception. Exceptions that are raised during the call will be + /// propapgated back to the caller. + /// + /// There are five functions on a control that are safe to call from any + /// thread: GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. + /// For all other method calls, you should use one of the invoke methods to marshal + /// the call to the control's thread. + /// + public Object Invoke(Delegate method, params Object[] args) { + using (new MultithreadSafeCallScope()) { + Control marshaler = FindMarshalingControl(); + return marshaler.MarshaledInvoke(this, method, args, true); + } + } + + /// + /// Perform the callback of a particular ThreadMethodEntry - called by InvokeMarshaledCallbacks below. + /// + /// If the invoke request originated from another thread, we should have already captured the ExecutionContext + /// of that thread. The callback is then invoked using that ExecutionContext (which includes info like the + /// compressed security stack). + /// + /// NOTE: The one part of the ExecutionContext that we DONT want applied to the callback is its SyncContext, + /// since this is the SyncContext of the other thread. So we grab the SyncContext of OUR thread, and pass + /// this through to the callback to use instead. + /// + /// When the invoke request comes from this thread, there won't be an ExecutionContext so we just invoke + /// the callback as is. + /// + private void InvokeMarshaledCallback(ThreadMethodEntry tme){ + if (tme.executionContext != null) { + if (invokeMarshaledCallbackHelperDelegate == null) { + invokeMarshaledCallbackHelperDelegate = new ContextCallback(InvokeMarshaledCallbackHelper); + } + // If there's no ExecutionContext, make sure we have a SynchronizationContext. There's no + // direct check for ExecutionContext: this is as close as we can get. + if (SynchronizationContext.Current == null) { + WindowsFormsSynchronizationContext.InstallIfNeeded(); + } + + tme.syncContext = SynchronizationContext.Current; + ExecutionContext.Run(tme.executionContext, invokeMarshaledCallbackHelperDelegate, tme); + } + else { + InvokeMarshaledCallbackHelper(tme); + } + } + + /// + /// Worker for invoking marshaled callbacks. + /// + private static void InvokeMarshaledCallbackHelper(object obj) + { + ThreadMethodEntry tme = (ThreadMethodEntry) obj; + + if (tme.syncContext != null) { + SynchronizationContext oldContext = SynchronizationContext.Current; + + try { + SynchronizationContext.SetSynchronizationContext(tme.syncContext); + + InvokeMarshaledCallbackDo(tme); + } + finally { + SynchronizationContext.SetSynchronizationContext(oldContext); + } + } + else { + InvokeMarshaledCallbackDo(tme); + } + } + + /// + /// + private static void InvokeMarshaledCallbackDo(ThreadMethodEntry tme) + { + // We short-circuit a couple of common cases for speed. + // + if (tme.method is EventHandler) { + if (tme.args == null || tme.args.Length < 1) { + ((EventHandler)tme.method)(tme.caller, EventArgs.Empty); + } + else if (tme.args.Length < 2) { + ((EventHandler)tme.method)(tme.args[0], EventArgs.Empty); + } + else { + ((EventHandler)tme.method)(tme.args[0], (EventArgs)tme.args[1]); + } + } + else if (tme.method is MethodInvoker) { + ((MethodInvoker)tme.method)(); + } + else if (tme.method is WaitCallback) + { + Debug.Assert(tme.args.Length == 1, + "Arguments are wrong for WaitCallback"); + ((WaitCallback)tme.method)(tme.args[0]); + } + else { + tme.retVal = tme.method.DynamicInvoke(tme.args); + } + } + + /// + /// Called on the control's owning thread to perform the actual callback. + /// This empties this control's callback queue, propagating any exceptions + /// back as needed. + /// + private void InvokeMarshaledCallbacks() { + ThreadMethodEntry current = null; + lock(threadCallbackList) { + if (threadCallbackList.Count > 0) { + current = (ThreadMethodEntry)threadCallbackList.Dequeue(); + } + } + + // Now invoke on all the queued items. + // + while (current != null) { + if (current.method != null) { + + try { + // If we are running under the debugger, don't wrap asynchronous + // calls in a try catch. It is much better to throw here than pop up + // a thread exception dialog below. + if (NativeWindow.WndProcShouldBeDebuggable && !current.synchronous) { + InvokeMarshaledCallback(current); + } + else { + try { + InvokeMarshaledCallback(current); + } + catch (Exception t) { + current.exception = t.GetBaseException(); + } + } + } + finally { + current.Complete(); + + // This code matches the behavior above. Basically, if we're debugging, don't + // do this because the exception would have been handled above. If we're + // not debugging, raise the exception here. + if (!NativeWindow.WndProcShouldBeDebuggable && + current.exception != null && !current.synchronous) { + Application.OnThreadException(current.exception); + } + } + } + + lock (threadCallbackList) { + if (threadCallbackList.Count > 0) { + current = (ThreadMethodEntry)threadCallbackList.Dequeue(); + } + else { + current = null; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected void InvokePaint(Control c, PaintEventArgs e) { + c.OnPaint(e); + } + + /// + /// + /// [To be supplied.] + /// + protected void InvokePaintBackground(Control c, PaintEventArgs e) { + c.OnPaintBackground(e); + } + + /// + /// determines whether the font is set + /// + /// + internal bool IsFontSet() { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return true; + } + return false; + } + + /// + /// WARNING! The meaning of this method is not what it appears. + /// The method returns true if "descendant" (the argument) is a descendant + /// of "this". I'd expect it to be the other way around, but oh well too late. + /// + /// + internal bool IsDescendant(Control descendant) { + Control control = descendant; + while (control != null) { + if (control == this) + return true; + control = control.ParentInternal; + } + return false; + } + + /// + /// + /// This Function will return a Boolean as to whether the Key value passed in is Locked... + /// + public static bool IsKeyLocked(Keys keyVal) { + if (keyVal == Keys.Insert || keyVal == Keys.NumLock || keyVal == Keys.CapsLock || keyVal == Keys.Scroll) { + int result = UnsafeNativeMethods.GetKeyState((int)keyVal); + + // If the high-order bit is 1, the key is down; otherwise, it is up. + // If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, + // is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. + // A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, + // and off when the key is untoggled. + + // Toggle keys (only low bit is of interest). + if( keyVal == Keys.Insert || keyVal == Keys.CapsLock ) + { + return (result & 0x1) != 0x0; + } + + return (result & 0x8001) != 0x0; + } + + // else - it's an un-lockable key. + // VSWhidbey 95660: Actually get the exception string from the system resource. + throw new NotSupportedException(SR.GetString(SR.ControlIsKeyLockedNumCapsScrollLockKeysSupportedOnly)); + } + + /// + /// + /// Determines if charCode is an input character that the control + /// wants. This method is called during window message pre-processing to + /// determine whether the given input character should be pre-processed or + /// sent directly to the control. If isInputChar returns true, the + /// given character is sent directly to the control. If isInputChar + /// returns false, the character is pre-processed and only sent to the + /// control if it is not consumed by the pre-processing phase. The + /// pre-processing of a character includes checking whether the character + /// is a mnemonic of another control. + /// + + // SECREVIEW: + // NOT LinkDemanding here because these functions do not have a processing side effect + // by directly calling them. + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool IsInputChar(char charCode) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.IsInputChar 0x" + ((int)charCode).ToString("X", CultureInfo.InvariantCulture)); + + int mask = 0; + if (charCode == (char)(int)Keys.Tab) { + mask = NativeMethods.DLGC_WANTCHARS | NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB; + } + else { + mask = NativeMethods.DLGC_WANTCHARS | NativeMethods.DLGC_WANTALLKEYS; + } + return(unchecked( (int) (long)SendMessage(NativeMethods.WM_GETDLGCODE, 0, 0)) & mask) != 0; + } + + /// + /// + /// Determines if keyData is an input key that the control wants. + /// This method is called during window message pre-processing to determine + /// whether the given input key should be pre-processed or sent directly to + /// the control. If isInputKey returns true, the given key is sent + /// directly to the control. If isInputKey returns false, the key is + /// pre-processed and only sent to the control if it is not consumed by the + /// pre-processing phase. Keys that are pre-processed include TAB, RETURN, + /// ESCAPE, and arrow keys. + /// + + // SECREVIEW: + // NOT LinkDemanding here because these functions do not have a processing side effect + // by directly calling them. + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool IsInputKey(Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.IsInputKey " + keyData.ToString()); + + if ((keyData & Keys.Alt) == Keys.Alt) return false; + int mask = NativeMethods.DLGC_WANTALLKEYS; + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + mask = NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB; + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + mask = NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTARROWS; + break; + } + + if (IsHandleCreated) { + return(unchecked( (int) (long)SendMessage(NativeMethods.WM_GETDLGCODE, 0, 0)) & mask) != 0; + } + else { + return false; + } + } + + /// + /// + /// Determines if charCode is the mnemonic character in text. + /// The mnemonic character is the character imediately following the first + /// instance of "&" in text + /// + public static bool IsMnemonic(char charCode, string text) { +#if DEBUG + if (ControlKeyboardRouting.TraceVerbose) { + Debug.Write("Control.IsMnemonic(" + charCode.ToString() + ", "); + + if (text != null) { + Debug.Write(text); + } + else { + Debug.Write("null"); + } + Debug.WriteLine(")"); + } +#endif + + //Special case handling: + if (charCode=='&') { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning false"); + return false; + } //if + + + if (text != null) { + int pos = -1; // start with -1 to handle double &'s + char c2 = Char.ToUpper(charCode, CultureInfo.CurrentCulture); + for (;;) { + if (pos + 1 >= text.Length) + break; + pos = text.IndexOf('&', pos + 1) + 1; + if (pos <= 0 || pos >= text.Length) + break; + char c1 = Char.ToUpper(text[pos], CultureInfo.CurrentCulture); + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...& found... char=" + c1.ToString()); + if (c1 == c2 || Char.ToLower(c1, CultureInfo.CurrentCulture) == Char.ToLower(c2, CultureInfo.CurrentCulture)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning true"); + return true; + } + } + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose && pos == 0, " ...no & found"); + } + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " ...returning false"); + return false; + } + + private void ListenToUserPreferenceChanged(bool listen) { + if (GetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED)) { + if (!listen) { + SetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED, false); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + } + } + else if (listen) { + SetState2(STATE2_LISTENINGTOUSERPREFERENCECHANGED, true); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + } + } + + /// + /// + /// Transforms an integer coordinate from logical to device units + /// by scaling it for the current DPI and rounding down to the nearest integer value. + /// + public int LogicalToDeviceUnits(int value) { + return DpiHelper.LogicalToDeviceUnits(value, DeviceDpi); + } + + /// + /// Transforms size from logical to device units + /// by scaling it for the current DPI and rounding down to the nearest integer value for width and height. + /// + /// size to be scaled + /// scaled size + public Size LogicalToDeviceUnits(Size value) { + return DpiHelper.LogicalToDeviceUnits(value, DeviceDpi); + } + + /// + /// Create a new bitmap scaled for the device units. + /// When displayed on the device, the scaled image will have same size as the original image would have when displayed at 96dpi. + /// + /// The image to scale from logical units to device units + public void ScaleBitmapLogicalToDevice(ref Bitmap logicalBitmap) { + DpiHelper.ScaleBitmapLogicalToDevice(ref logicalBitmap, DeviceDpi); + } + + internal void AdjustWindowRectEx(ref NativeMethods.RECT rect, int style, bool bMenu, int exStyle) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + SafeNativeMethods.AdjustWindowRectExForDpi(ref rect, style, bMenu, exStyle, (uint)deviceDpi); + } else { + SafeNativeMethods.AdjustWindowRectEx(ref rect, style, bMenu, exStyle); + } + } + + private Object MarshaledInvoke(Control caller, Delegate method, Object[] args, bool synchronous) { + + // Marshaling an invoke occurs in three steps: + // + // 1. Create a ThreadMethodEntry that contains the packet of information + // about this invoke. This TME is placed on a linked list of entries because + // we have a gap between the time we PostMessage and the time it actually + // gets processed, and this gap may allow other invokes to come in. Access + // to this linked list is always synchronized. + // + // 2. Post ourselves a message. Our caller has already determined the + // best control to call us on, and we should almost always have a handle. + // + // 3. If we're synchronous, wait for the message to get processed. We don't do + // a SendMessage here so we're compatible with OLE, which will abort many + // types of calls if we're within a SendMessage. + // + + if (!IsHandleCreated) { + throw new InvalidOperationException(SR.GetString(SR.ErrorNoMarshalingThread)); + } + + // We have to demand unmanaged code permission here for the control hosted in + // the browser case. Without this check, we will expose a security hole, because + // ActiveXImpl.OnMessage() will assert unmanaged code for everyone as part of + // its implementation. + // The right fix is to remove the Assert() on top of the ActiveXImpl class, and + // visit each method to see if it needs unmanaged code permission, and if so, add + // the permission just to that method(s). + // + ActiveXImpl activeXImpl = (ActiveXImpl)Properties.GetObject(PropActiveXImpl); + if (activeXImpl != null) { + IntSecurity.UnmanagedCode.Demand(); + } + + // We don't want to wait if we're on the same thread, or else we'll deadlock. + // It is important that syncSameThread always be false for asynchronous calls. + // + bool syncSameThread = false; + int pid; // ignored + if (SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, Handle), out pid) == SafeNativeMethods.GetCurrentThreadId()) { + if (synchronous) + syncSameThread = true; + } + + // Store the compressed stack information from the thread that is calling the Invoke() + // so we can assign the same security context to the thread that will actually execute + // the delegate being passed. + // + ExecutionContext executionContext = null; + if (!syncSameThread) { + executionContext = ExecutionContext.Capture(); + } + ThreadMethodEntry tme = new ThreadMethodEntry(caller, this, method, args, synchronous, executionContext); + + lock (this) { + if (threadCallbackList == null) { + threadCallbackList = new Queue(); + } + } + + lock (threadCallbackList) { + if (threadCallbackMessage == 0) { + threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage"); + } + threadCallbackList.Enqueue(tme); + } + + if (syncSameThread) { + InvokeMarshaledCallbacks(); + } else { + // + + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); + } + + if (synchronous) { + if (!tme.IsCompleted) { + WaitForWaitHandle(tme.AsyncWaitHandle); + } + if (tme.exception != null) { + throw tme.exception; + } + return tme.retVal; + } + else { + return(IAsyncResult)tme; + } + } + + /// + /// This method is used by WM_GETCONTROLNAME and WM_GETCONTROLTYPE + /// to marshal a string to a message structure. It handles + /// two cases: if no buffer was passed it returns the size of + /// buffer needed. If a buffer was passed, it fills the buffer. + /// If the passed buffer is not long enough it will return -1. + /// + private void MarshalStringToMessage(string value, ref Message m) { + if (m.LParam == IntPtr.Zero) { + m.Result = (IntPtr)((value.Length + 1) * Marshal.SystemDefaultCharSize); + return; + } + + if (unchecked((int)(long)m.WParam) < value.Length + 1) { + m.Result = (IntPtr)(-1); + return; + } + + // Copy the name into the given IntPtr + // + char[] nullChar = new char[] {(char)0}; + byte[] nullBytes; + byte[] bytes; + + if (Marshal.SystemDefaultCharSize == 1) { + bytes = Encoding.Default.GetBytes(value); + nullBytes = Encoding.Default.GetBytes(nullChar); + } + else { + bytes = Encoding.Unicode.GetBytes(value); + nullBytes = Encoding.Unicode.GetBytes(nullChar); + } + + Marshal.Copy(bytes, 0, m.LParam, bytes.Length); + Marshal.Copy(nullBytes, 0, unchecked((IntPtr)((long)m.LParam + (long)bytes.Length)), nullBytes.Length); + + m.Result = (IntPtr)((bytes.Length + nullBytes.Length)/Marshal.SystemDefaultCharSize); + } + + // Used by form to notify the control that it has been "entered" + // + internal void NotifyEnter() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::NotifyEnter() - " + this.Name); + OnEnter(EventArgs.Empty); + } + + // Used by form to notify the control that it has been "left" + // + internal void NotifyLeave() { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::NotifyLeave() - " + this.Name); + OnLeave(EventArgs.Empty); + } + + + /// + /// + /// + /// Propagates the invalidation event, notifying the control that + /// some part of it is being invalidated and will subsequently need + /// to repaint. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void NotifyInvalidate(Rectangle invalidatedArea) { + OnInvalidated(new InvalidateEventArgs(invalidatedArea)); + } + + // Used by form to notify the control that it is validating. + // + private bool NotifyValidating() { + CancelEventArgs ev = new CancelEventArgs(); + OnValidating(ev); + return ev.Cancel; + } + + // Used by form to notify the control that it has been validated. + // + private void NotifyValidated() { + OnValidated(EventArgs.Empty); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeOnClick(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnClick(e); + } + } + + /// + protected virtual void OnAutoSizeChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventAutoSizeChanged] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackColorChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + if (GetState(STATE_OWNCTLBRUSH)) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + } + Properties.SetObject(PropBackBrush, null); + } + + Invalidate(); + + EventHandler eh = Events[EventBackColor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBackColorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackgroundImageChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventBackgroundImage] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBackgroundImageChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackgroundImageLayoutChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventBackgroundImageLayout] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBindingContextChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropBindings) != null) { + UpdateBindings(); + } + + EventHandler eh = Events[EventBindingContext] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentBindingContextChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCausesValidationChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventCausesValidation] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Called when a child is about to resume its layout. The default implementation + /// calls OnChildLayoutResuming on the parent. + /// + internal virtual void OnChildLayoutResuming(Control child, bool performLayout) { + if (ParentInternal != null) { + ParentInternal.OnChildLayoutResuming(child, performLayout); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnContextMenuChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventContextMenu] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnContextMenuStripChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventContextMenuStrip] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCursorChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventCursor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentCursorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDockChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventDock] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.OnEnabled to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnEnabledChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (IsHandleCreated) { + SafeNativeMethods.EnableWindow(new HandleRef(this, Handle), Enabled); + + // User-paint controls should repaint when their enabled state changes + // + if (GetStyle(ControlStyles.UserPaint)) { + Invalidate(); + Update(); + } + } + + EventHandler eh = Events[EventEnabled] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentEnabledChanged(e); + } + } + } + + // Refer VsWhidbey : 515910 & 269769 + internal virtual void OnFrameWindowActivate(bool fActivate) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFontChanged(EventArgs e) { + Contract.Requires(e != null); + // bail if disposing + // + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + if (Properties.ContainsInteger(PropFontHeight)) { + Properties.SetInteger(PropFontHeight, -1); + } + + + // Cleanup any font handle wrapper... + // + DisposeFontHandle(); + + if (IsHandleCreated && !GetStyle(ControlStyles.UserPaint)) { + SetWindowFont(); + } + + EventHandler eh = Events[EventFont] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + using(new LayoutTransaction(this, this, PropertyNames.Font, false)) { + if (controlsCollection != null) { + // This may have changed the sizes of our children. + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentFontChanged(e); + } + } + } + + LayoutTransaction.DoLayout(this,this,PropertyNames.Font); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnForeColorChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + Invalidate(); + + EventHandler eh = Events[EventForeColor] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentForeColorChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + // update the scroll position when the handle has been created + // MUST SET THIS BEFORE CALLING RecreateHandle!!! + SetState2(STATE2_SETSCROLLPOS, true); + + RecreateHandle(); + + EventHandler eh = Events[EventRightToLeft] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnParentRightToLeftChanged(e); + } + } + } + + /// + /// + /// OnNotifyMessage is called if the ControlStyles.EnableNotifyMessage + /// bit is set. This allows for semi-trusted controls to listen to + /// window messages, without allowing them to actually modify the + /// message. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnNotifyMessage(Message m) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackColorChanged(EventArgs e) { + Contract.Requires(e != null); + Color backColor = Properties.GetColor(PropBackColor); + if (backColor.IsEmpty) { + OnBackColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackgroundImageChanged(EventArgs e) { + Contract.Requires(e != null); + OnBackgroundImageChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBindingContextChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropBindingManager) == null) { + OnBindingContextChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentCursorChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropCursor) == null) { + OnCursorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentEnabledChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetState(STATE_ENABLED)) { + OnEnabledChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentFontChanged(EventArgs e) { + Contract.Requires(e != null); + if (Properties.GetObject(PropFont) == null) { + OnFontChanged(e); + } + } + + + // occurs when the parent of this control has recreated + // its handle. + // presently internal as we feel this will be rare to + // require override + internal virtual void OnParentHandleRecreated() { + // restore ourselves over to the original control. + + // use SetParent directly so as to not raise ParentChanged events + Control parent = ParentInternal; + if (parent != null) { + if (IsHandleCreated) { + UnsafeNativeMethods.SetParent(new HandleRef(this, this.Handle), new HandleRef(parent, parent.Handle)); + UpdateZOrder(); + } + } + SetState(STATE_PARENTRECREATING, false); + + // if our parent was initially the the parent who's handle just got recreated, we need + // to recreate ourselves so that we get notification. See UpdateReflectParent for more details. + if (ReflectParent == ParentInternal) { + RecreateHandle(); + } + + } + + + // occurs when the parent of this control is recreating + // its handle. + // presently internal as we feel this will be rare to + // require override + internal virtual void OnParentHandleRecreating() { + SetState(STATE_PARENTRECREATING, true); + // swoop this control over to the parking window. + + // if we left it parented to the parent control, the DestroyWindow + // would force us to destroy our handle as well... hopefully + // parenting us over to the parking window and restoring ourselves + // should help improve recreate perf. + + // use SetParent directly so as to not raise ParentChanged events + if (IsHandleCreated) { + Application.ParkHandle(new HandleRef(this, this.Handle), this.DpiAwarenessContext); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentForeColorChanged(EventArgs e) { + Contract.Requires(e != null); + Color foreColor = Properties.GetColor(PropForeColor); + if (foreColor.IsEmpty) { + OnForeColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentRightToLeftChanged(EventArgs e) { + Contract.Requires(e != null); + if (!Properties.ContainsInteger(PropRightToLeft) || ((RightToLeft)Properties.GetInteger(PropRightToLeft)) == RightToLeft.Inherit) { + OnRightToLeftChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentVisibleChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetState(STATE_VISIBLE)) { + OnVisibleChanged(e); + } + } + + // Used to work around VSWhidbey 527459. OnVisibleChanged/OnParentVisibleChanged is not called when a parent becomes invisible + internal virtual void OnParentBecameInvisible() { + if (GetState(STATE_VISIBLE)) { + // This control became invisible too - notify its children + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + ctl.OnParentBecameInvisible(); + } + } + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPrint(PaintEventArgs e) { + if (e == null) { + throw new ArgumentNullException("e"); + } + Contract.EndContractBlock(); + + if (GetStyle(ControlStyles.UserPaint)) { + // Theme support on Windows XP requires that we paint the background + // and foreground to support semi-transparent children + // + PaintWithErrorHandling(e, PaintLayerBackground); + e.ResetGraphics(); + PaintWithErrorHandling(e, PaintLayerForeground); + } + else { + PrintPaintEventArgs ppev = e as PrintPaintEventArgs; + Message m; + bool releaseDC = false; + IntPtr hdc = IntPtr.Zero; + + if (ppev == null) { + IntPtr flags = (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT); + hdc = e.HDC; + + if (hdc == IntPtr.Zero) { + // a manually created paintevent args + hdc = e.Graphics.GetHdc(); + releaseDC = true; + } + m = Message.Create(this.Handle, NativeMethods.WM_PRINTCLIENT, hdc, flags); + } + else { + m = ppev.Message; + } + + try { + DefWndProc(ref m); + } + finally { + if (releaseDC) { + e.Graphics.ReleaseHdcInternal(hdc); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTabIndexChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventTabIndex] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTabStopChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventTabStop] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTextChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventText] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.OnVisible to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnVisibleChanged(EventArgs e) { + Contract.Requires(e != null); + bool visible = this.Visible; + if (visible) { + UnhookMouseEvent(); + trackMouseEvent = null; + } + if (parent != null && visible && !Created) { + bool isDisposing = GetAnyDisposingInHierarchy(); + if (!isDisposing) { + // Usually the control is created by now, but in a few corner cases + // exercised by the PropertyGrid dropdowns, it isn't + CreateControl(); + } + } + + EventHandler eh = Events[EventVisible] as EventHandler; + if (eh != null) { + eh(this, e); + } + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + Control ctl = controlsCollection[i]; + if (ctl.Visible) { + ctl.OnParentVisibleChanged(e); + } + if (!visible) { + ctl.OnParentBecameInvisible(); + } + } + } + } + + /// + internal virtual void OnTopMostActiveXParentChanged(EventArgs e) { + Contract.Requires(e != null); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnTopMostActiveXParentChanged(e); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventParent] as EventHandler; + if (eh != null) { + eh(this, e); + } + + // + // Inform the control that the topmost control is now an ActiveX control + if (TopMostParent.IsActiveX) { + OnTopMostActiveXParentChanged(EventArgs.Empty); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClick(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClientSizeChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventClientSize] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnControlAdded(ControlEventArgs e) { + Contract.Requires(e != null); + ControlEventHandler handler = (ControlEventHandler)Events[EventControlAdded]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnControlRemoved(ControlEventArgs e) { + Contract.Requires(e != null); + ControlEventHandler handler = (ControlEventHandler)Events[EventControlRemoved]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Called when the control is first created. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnCreateControl() { + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. + /// Call base.OnHandleCreated first. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHandleCreated(EventArgs e) { + Contract.Requires(e != null); + if (this.IsHandleCreated) { + + // Setting fonts is for some reason incredibly expensive. + // (Even if you exclude font handle creation) + if (!GetStyle(ControlStyles.UserPaint)){ + SetWindowFont(); + } + + if (DpiHelper.EnableDpiChangedMessageHandling && !(typeof(Form).IsAssignableFrom(this.GetType()))) { + int old = deviceDpi; + deviceDpi = (int)UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + if (old != deviceDpi) { + RescaleConstantsForDpi(old, deviceDpi); + } + } + + // Restore dragdrop status. Ole Initialize happens + // when the ThreadContext in Application is created + // + SetAcceptDrops(AllowDrop); + + Region region = (Region)Properties.GetObject(PropRegion); + if (region != null) { + IntPtr regionHandle = GetHRgn(region); + + try { + if (IsActiveX) { + regionHandle = ActiveXMergeRegion(regionHandle); + } + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(this, regionHandle), SafeNativeMethods.IsWindowVisible(new HandleRef(this, Handle))) != 0) { + //The HWnd owns the region. + regionHandle = IntPtr.Zero; + } + } + finally { + if (regionHandle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, regionHandle)); + } + } + } + + // Update accessbility information. + // + ControlAccessibleObject accObj = Properties.GetObject(PropAccessibility) as ControlAccessibleObject; + ControlAccessibleObject ncAccObj = Properties.GetObject(PropNcAccessibility) as ControlAccessibleObject; + + // Cache Handle in a local before asserting so we minimize code running under the Assert. + IntPtr handle = Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + + try { + if (accObj != null) { + accObj.Handle = handle; + } + if (ncAccObj != null) { + ncAccObj.Handle = handle; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + // Set the window text from the Text property. + // + if (text != null && text.Length != 0) { + UnsafeNativeMethods.SetWindowText(new HandleRef(this, Handle), text); + } + + if (!(this is ScrollableControl) && !IsMirrored && GetState2(STATE2_SETSCROLLPOS) && !GetState2(STATE2_HAVEINVOKED)) { + BeginInvoke(new EventHandler(this.OnSetScrollPosition)); + SetState2(STATE2_HAVEINVOKED, true); + SetState2(STATE2_SETSCROLLPOS, false); + } + + // Listen to UserPreferenceChanged if the control is top level and interested in the notification. + if (GetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED)) { + ListenToUserPreferenceChanged(GetTopLevel()); + } + } + + EventHandler handler = (EventHandler)Events[EventHandleCreated]; + if (handler != null) handler(this, e); + + if (this.IsHandleCreated) + { + // Now, repost the thread callback message if we found it. We should do + // this last, so we're as close to the same state as when the message + // was placed. + // + if (GetState(STATE_THREADMARSHALLPENDING)) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); + SetState(STATE_THREADMARSHALLPENDING, false); + } + } + } + + private void OnSetScrollPosition(object sender, EventArgs e) { + SetState2(STATE2_HAVEINVOKED, false); + OnInvokedSetScrollPosition(sender, e); + } + + internal virtual void OnInvokedSetScrollPosition(object sender, EventArgs e) { + if (!(this is ScrollableControl) && !IsMirrored) { + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_RANGE; + if (UnsafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ,si) != false) { + si.nPos = (RightToLeft == RightToLeft.Yes) ? si.nMax : si.nMin; + SendMessage(NativeMethods.WM_HSCROLL, NativeMethods.Util.MAKELPARAM(NativeMethods.SB_THUMBPOSITION, si.nPos), 0); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLocationChanged(EventArgs e) { + Contract.Requires(e != null); + OnMove(EventArgs.Empty); + + EventHandler eh = Events[EventLocation] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle is about to be destroyed. + /// Call base.OnHandleDestroyed last. + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHandleDestroyed(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventHandleDestroyed]; + if (handler != null) handler(this, e); + + UpdateReflectParent(false); + + if (!RecreatingHandle) { + if (GetState(STATE_OWNCTLBRUSH)) { + object backBrush = Properties.GetObject(PropBackBrush); + if (backBrush != null) { + IntPtr p = (IntPtr)backBrush; + if (p != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(this, p)); + } + Properties.SetObject(PropBackBrush, null); + } + } + ListenToUserPreferenceChanged(false /*listen*/); + } + + // this code is important -- it is critical that we stash away + // the value of the text for controls such as edit, button, + // label, etc. Without this processing, any time you change a + // property that forces handle recreation, you lose your text! + // See the above code in wmCreate + // + + try { + if (!GetAnyDisposingInHierarchy()) { + text = Text; + if (text != null && text.Length == 0) text = null; + } + SetAcceptDrops(false); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + + // Some ActiveX controls throw exceptions when + // you ask for the text property after you have destroyed their handle. We + // don't want those exceptions to bubble all the way to the top, since + // we leave our state in a mess. See ASURT 49990. + // + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDoubleClick(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventDoubleClick]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.onEnter to send this event to any registered event listeners. + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragEnter to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragEnter(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragEnter]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragOver to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragOver(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragOver]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragLeave to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventDragLeave]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragDrop to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragDrop(DragEventArgs drgevent) { + Contract.Requires(drgevent != null); + DragEventHandler handler = (DragEventHandler)Events[EventDragDrop]; + if (handler != null) handler(this,drgevent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onGiveFeedback to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + protected virtual void OnGiveFeedback(GiveFeedbackEventArgs gfbevent) { + Contract.Requires(gfbevent != null); + GiveFeedbackEventHandler handler = (GiveFeedbackEventHandler)Events[EventGiveFeedback]; + if (handler != null) handler(this,gfbevent); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnEnter(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventEnter]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeGotFocus(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnGotFocus(e); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(toInvoke); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnGotFocus(EventArgs e) { + Contract.Requires(e != null); + if (IsActiveX) { + ActiveXOnFocus(true); + } + + if (parent != null) { + parent.ChildGotFocus(this); + } + + EventHandler handler = (EventHandler)Events[EventGotFocus]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onHelp to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHelpRequested(HelpEventArgs hevent) { + Contract.Requires(hevent != null); + HelpEventHandler handler = (HelpEventHandler)Events[EventHelpRequested]; + if (handler != null) { + handler(this,hevent); + // VSWhidbey 95281: Set this to true so that multiple events aren't raised to the Form. + hevent.Handled = true; + } + + if (!hevent.Handled) { + if (ParentInternal != null) { + ParentInternal.OnHelpRequested(hevent); + } + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.OnInvalidate to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInvalidated(InvalidateEventArgs e) { + Contract.Requires(e != null); + // Ask the site to change the view... + if (IsActiveX) { + ActiveXViewChanged(); + } + + // Transparent control support + ControlCollection controls = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controls != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controls.Count; i++) { + controls[i].OnParentInvalidated(e); + } + } + + InvalidateEventHandler handler = (InvalidateEventHandler)Events[EventInvalidated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyDown(KeyEventArgs e) { + Contract.Requires(e != null); + KeyEventHandler handler = (KeyEventHandler)Events[EventKeyDown]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyPress(KeyPressEventArgs e) { + Contract.Requires(e != null); + KeyPressEventHandler handler = (KeyPressEventHandler)Events[EventKeyPress]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnKeyUp(KeyEventArgs e) { + Contract.Requires(e != null); + KeyEventHandler handler = (KeyEventHandler)Events[EventKeyUp]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Core layout logic. Inheriting controls should override this function + /// to do any custom layout logic. It is not neccessary to call + /// base.layoutCore, however for normal docking and anchoring + /// functions to work, base.layoutCore must be called. + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLayout(LayoutEventArgs levent) { + Contract.Requires(levent != null); + // Ask the site to change the view... + if (IsActiveX) { + ActiveXViewChanged(); + } + LayoutEventHandler handler = (LayoutEventHandler)Events[EventLayout]; + if (handler != null) handler(this, levent); + + bool parentRequiresLayout = LayoutEngine.Layout(this, levent); + + // + + + if(parentRequiresLayout && ParentInternal != null) { + // LayoutEngine.Layout can return true to request that our parent resize us because + // we did not have enough room for our contents. We can not just call PerformLayout + // because this container is currently suspended. PerformLayout will check this state + // flag and PerformLayout on our parent. + ParentInternal.SetState(STATE_LAYOUTISDIRTY, true); + } + } + + /// + /// + /// Called when the last resume layout call is made. If performLayout is true + /// a layout will occur as soon as this call returns. Layout is + /// still suspended when this call is made. The default implementation + /// calls OnChildLayoutResuming on the parent, if it exists. + /// + internal virtual void OnLayoutResuming(bool performLayout) { + if (ParentInternal != null) { + ParentInternal.OnChildLayoutResuming(this, performLayout); + } + } + + internal virtual void OnLayoutSuspended() { + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventLeave]; + if (handler != null) handler(this, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void InvokeLostFocus(Control toInvoke, EventArgs e) { + if (toInvoke != null) { + toInvoke.OnLostFocus(e); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(toInvoke); + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLostFocus(EventArgs e) { + Contract.Requires(e != null); + if (IsActiveX) { + ActiveXOnFocus(false); + } + + EventHandler handler = (EventHandler)Events[EventLostFocus]; + if (handler != null) handler(this, e); + } + + protected virtual void OnMarginChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMarginChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseDoubleClick(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseDoubleClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseClick(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseClick]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseCaptureChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseCaptureChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseDown(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseDown]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseEnter(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseEnter]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseLeave(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseLeave]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// + /// Raises the event. + /// Occurs when the form is moved to a monitor with a different resolution (number of dots per inch), + /// or when scaling level is changed in the windows setting by the user. + /// This message is not sent to the top level windows. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + protected virtual void OnDpiChangedBeforeParent(EventArgs e) { + Contract.Requires(e != null); + ((EventHandler)Events[EventDpiChangedBeforeParent])?.Invoke(this, e); + } + + /// + /// + /// + /// Raises the event. + /// Occurs when the form is moved to a monitor with a different resolution (number of dots per inch), + /// or when scaling level is changed in windows setting by the user. + /// This message is not sent to the top level windows. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + protected virtual void OnDpiChangedAfterParent(EventArgs e) { + Contract.Requires(e != null); + ((EventHandler)Events[EventDpiChangedAfterParent])?.Invoke(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseHover(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMouseHover]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseMove(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseMove]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseUp(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseUp]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMouseWheel(MouseEventArgs e) { + Contract.Requires(e != null); + MouseEventHandler handler = (MouseEventHandler)Events[EventMouseWheel]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMove(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventMove]; + if (handler != null) handler(this, e); + if (RenderTransparent) + Invalidate(); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onPaint to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPaint(PaintEventArgs e) { + Contract.Requires(e != null); + PaintEventHandler handler = (PaintEventHandler)Events[EventPaint]; + if (handler != null) handler(this, e); + } + + protected virtual void OnPaddingChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetStyle(ControlStyles.ResizeRedraw)) { + Invalidate(); + } + EventHandler handler = (EventHandler)Events[EventPaddingChanged]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// Inheriting classes should override this method to handle the erase + /// background request from windows. It is not necessary to call + /// base.onPaintBackground, however if you do not want the default + /// Windows behavior you must set event.handled to true. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnPaintBackground(PaintEventArgs pevent) { + Contract.Requires(pevent != null); + // We need the true client rectangle as clip rectangle causes + // problems on "Windows Classic" theme. + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(window, InternalHandle), ref rect); + + PaintBackground(pevent, new Rectangle(rect.left, rect.top, rect.right, rect.bottom)); + } + + // Transparent control support + private void OnParentInvalidated(InvalidateEventArgs e) { + Contract.Requires(e != null); + if (!RenderTransparent) return; + + if (this.IsHandleCreated) { + // move invalid rect into child space + Rectangle cliprect = e.InvalidRect; + Point offs = this.Location; + cliprect.Offset(-offs.X,-offs.Y); + cliprect = Rectangle.Intersect(this.ClientRectangle, cliprect); + + // if we don't intersect at all, do nothing + if (cliprect.IsEmpty) return; + this.Invalidate(cliprect); + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onQueryContinueDrag to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + protected virtual void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent) { + Contract.Requires(qcdevent != null); + QueryContinueDragEventHandler handler = (QueryContinueDragEventHandler)Events[EventQueryContinueDrag]; + if (handler != null) handler(this, qcdevent); + } + + /// + /// + /// Raises the event when the Region property has changed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRegionChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler eh = Events[EventRegionChanged] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResize(EventArgs e) { + Contract.Requires(e != null); + if ((controlStyle & ControlStyles.ResizeRedraw) == ControlStyles.ResizeRedraw + || GetState(STATE_EXCEPTIONWHILEPAINTING)) { + Invalidate(); + } + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + EventHandler handler = (EventHandler)Events[EventResize]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers") + ] + protected virtual void OnPreviewKeyDown(PreviewKeyDownEventArgs e) { + Contract.Requires(e != null); + PreviewKeyDownEventHandler handler = (PreviewKeyDownEventHandler)Events[EventPreviewKeyDown]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnSizeChanged(EventArgs e) { + Contract.Requires(e != null); + OnResize(EventArgs.Empty); + + EventHandler eh = Events[EventSize] as EventHandler; + if (eh != null) { + eh(this,e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnChangeUICues(UICuesEventArgs e) { + Contract.Requires(e != null); + UICuesEventHandler handler = (UICuesEventHandler)Events[EventChangeUICues]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnStyleChanged(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventStyleChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnSystemColorsChanged(EventArgs e) { + Contract.Requires(e != null); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + controlsCollection[i].OnSystemColorsChanged(EventArgs.Empty); + } + } + Invalidate(); + + EventHandler handler = (EventHandler)Events[EventSystemColorsChanged]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnValidating(CancelEventArgs e) { + Contract.Requires(e != null); + CancelEventHandler handler = (CancelEventHandler)Events[EventValidating]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnValidated(EventArgs e) { + Contract.Requires(e != null); + EventHandler handler = (EventHandler)Events[EventValidated]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Is invoked when the control handle is created or right before the top level parent receives WM_DPICHANGED message. + /// This method is an opportunity to rescale any constant sizes, glyphs or bitmaps before re-painting. + /// The derived class can choose to not call the base class implementation. + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + } + + // This is basically OnPaintBackground, put in a separate method for ButtonBase, + // which does all painting under OnPaint, and tries very hard to avoid double-painting the border pixels. + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle) { + PaintBackground(e, rectangle, this.BackColor, Point.Empty); + } + + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor) { + PaintBackground(e, rectangle, backColor, Point.Empty); + } + + internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor, Point scrollOffset) { + if (RenderColorTransparent(backColor)) { + PaintTransparentBackground(e, rectangle); + } + + //If the form or mdiclient is mirrored then we do not render the background image due to GDI+ issues. + bool formRTL = ((this is Form || this is MdiClient) && this.IsMirrored); + + // The rest of this won't do much if BackColor is transparent and there is no BackgroundImage, + // but we need to call it in the partial alpha case. + + // + + if (BackgroundImage != null && !DisplayInformation.HighContrast && !formRTL) { + + if (BackgroundImageLayout == ImageLayout.Tile) { + if (ControlPaint.IsImageTransparent(BackgroundImage)) { + PaintTransparentBackground(e, rectangle); + } + } + + // + + Point scrollLocation = scrollOffset; + ScrollableControl scrollControl = this as ScrollableControl; + if (scrollControl != null && scrollLocation != Point.Empty) { + scrollLocation = ((ScrollableControl)this).AutoScrollPosition; + } + + if (ControlPaint.IsImageTransparent(BackgroundImage)) { + PaintBackColor(e, rectangle, backColor); + } + + ControlPaint.DrawBackgroundImage(e.Graphics, BackgroundImage, backColor, BackgroundImageLayout, ClientRectangle, rectangle, scrollLocation, RightToLeft); + } + else { + PaintBackColor(e, rectangle, backColor); + } + } + private static void PaintBackColor(PaintEventArgs e, Rectangle rectangle, Color backColor) { + // Common case of just painting the background. For this, we + // use GDI because it is faster for simple things than creating + // a graphics object, brush, etc. Also, we may be able to + // use a system brush, avoiding the brush create altogether. + // + Color color = backColor; + + // Note: PaintEvent.HDC == 0 if GDI+ has used the HDC -- it wouldn't be safe for us + // to use it without enough bookkeeping to negate any performance gain of using GDI. + if (color.A == 255) { + using (WindowsGraphics wg = (e.HDC != IntPtr.Zero && DisplayInformation.BitsPerPixel > 8) ? + WindowsGraphics.FromHdc(e.HDC) : WindowsGraphics.FromGraphics(e.Graphics)) { + color = wg.GetNearestColor(color); + + using (WindowsBrush brush = new WindowsSolidBrush(wg.DeviceContext, color)) { + wg.FillRectangle(brush, rectangle); + } + } + } + else { + // don't paint anything from 100% transparent background + // + if (color.A > 0) { + // Color has some transparency or we have no HDC, so we must + // fall back to using GDI+. + // + using (Brush brush = new SolidBrush(color)) { + e.Graphics.FillRectangle(brush, rectangle); + } + } + } + } + + // Paints a red rectangle with a red X, painted on a white background + private void PaintException(PaintEventArgs e) { +#if false + StringFormat stringFormat = ControlPaint.StringFormatForAlignment(ContentAlignment.TopLeft); + string exceptionText = Properties.GetObject(PropPaintingException).ToString(); + stringFormat.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, exceptionText.Length)}); + + // rendering calculations... + // + int penThickness = 2; + Size glyphSize = SystemInformation.IconSize; + int marginX = penThickness * 2; + int marginY = penThickness * 2; + + Rectangle clientRectangle = ClientRectangle; + + Rectangle borderRectangle = ClientRectangle; + borderRectangle.X++; + borderRectangle.Y++; + borderRectangle.Width -=2; + borderRectangle.Height-=2; + + Rectangle imageRect = new Rectangle(marginX, marginY, glyphSize.Width, glyphSize.Height); + + Rectangle textRect = clientRectangle; + textRect.X = imageRect.X + imageRect.Width + 2 * marginX; + textRect.Y = imageRect.Y; + textRect.Width -= (textRect.X + marginX + penThickness); + textRect.Height -= (textRect.Y + marginY + penThickness); + + using (Font errorFont = new Font(Font.FontFamily, Math.Max(SystemInformation.ToolWindowCaptionHeight - SystemInformation.BorderSize.Height - 2, Font.Height), GraphicsUnit.Pixel)) { + + using(Region textRegion = e.Graphics.MeasureCharacterRanges(exceptionText, errorFont, textRect, stringFormat)[0]) { + // paint contents... clipping optimizations for less flicker... + // + Region originalClip = e.Graphics.Clip; + + e.Graphics.ExcludeClip(textRegion); + e.Graphics.ExcludeClip(imageRect); + try { + e.Graphics.FillRectangle(Brushes.White, clientRectangle); + } + finally { + e.Graphics.Clip = originalClip; + } + + using (Pen pen = new Pen(Color.Red, penThickness)) { + e.Graphics.DrawRectangle(pen, borderRectangle); + } + + Icon err = SystemIcons.Error; + + e.Graphics.FillRectangle(Brushes.White, imageRect); + e.Graphics.DrawIcon(err, imageRect.X, imageRect.Y); + + textRect.X++; + e.Graphics.IntersectClip(textRegion); + try { + e.Graphics.FillRectangle(Brushes.White, textRect); + e.Graphics.DrawString(exceptionText, errorFont, new SolidBrush(ForeColor), textRect, stringFormat); + } + finally { + e.Graphics.Clip = originalClip; + } + } + } +#else + int penThickness = 2; + using (Pen pen = new Pen(Color.Red, penThickness)) { + Rectangle clientRectangle = ClientRectangle; + Rectangle rectangle = clientRectangle; + rectangle.X++; + rectangle.Y++; + rectangle.Width--; + rectangle.Height--; + + e.Graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1); + rectangle.Inflate(-1, -1); + e.Graphics.FillRectangle(Brushes.White, rectangle); + e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Top, + clientRectangle.Right, clientRectangle.Bottom); + e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Bottom, + clientRectangle.Right, clientRectangle.Top); + } + +#endif + } + + internal void PaintTransparentBackground(PaintEventArgs e, Rectangle rectangle) { + PaintTransparentBackground(e,rectangle,null); + } + // Trick our parent into painting our background for us, or paint some default + // color if that doesn't work. + // + // This method is the hardest part of implementing transparent controls; + // call this in your OnPaintBackground method, and away you go. + // + // If you only want a region of the control to be transparent, pass in a region into the + // last parameter. A null region implies that you want the entire rectangle to be transparent. + internal void PaintTransparentBackground(PaintEventArgs e, Rectangle rectangle, Region transparentRegion) { + Graphics g = e.Graphics; + Control parent = this.ParentInternal; + + if (parent != null) { + // Refer to VSWhidbey : 440669. + // We need to use themeing painting for certain controls (like TabPage) when they parent other controls. + // But we dont want to to this always as this causes serious preformance (at Runtime and DesignTime) + // so checking for RenderTransparencyWithVisualStyles which is TRUE for TabPage and false by default. + if (Application.RenderWithVisualStyles && parent.RenderTransparencyWithVisualStyles) { + // When we are rendering with visual styles, we can use the cool DrawThemeParentBackground function + // that UxTheme provides to render the parent's background. This function is control agnostic, so + // we use the wrapper in ButtonRenderer - this should do the right thing for all controls, + // not just Buttons. + + GraphicsState graphicsState = null; + if (transparentRegion != null) { + graphicsState = g.Save(); + } + try { + if (transparentRegion != null) { + g.Clip = transparentRegion; + } + ButtonRenderer.DrawParentBackground(g, rectangle, this); + } + finally { + if (graphicsState != null) { + g.Restore(graphicsState); + } + } + + + } + else { + // how to move the rendering area and setup it's size + // (we want to translate it to the parent's origin) + Rectangle shift = new Rectangle(-this.Left,-this.Top, parent.Width, parent.Height); + + // moving the clipping rectangle to the parent coordinate system + Rectangle newClipRect = new Rectangle(rectangle.Left + this.Left, rectangle.Top + this.Top, rectangle.Width, rectangle.Height); + + using (WindowsGraphics wg = WindowsGraphics.FromGraphics(g)) { + wg.DeviceContext.TranslateTransform(-this.Left, -this.Top); + using (PaintEventArgs np = new PaintEventArgs(wg.GetHdc(), newClipRect)) { + if (transparentRegion != null) { + np.Graphics.Clip = transparentRegion; + np.Graphics.TranslateClip(-shift.X, -shift.Y); + } + try { + InvokePaintBackground(parent, np); + InvokePaint(parent, np); + } + finally { + if (transparentRegion != null) { + // restore region back to original state. + np.Graphics.TranslateClip(shift.X, shift.Y); + } + } + } + } + + } + + } + else { + // For whatever reason, our parent can't paint our background, but we need some kind of background + // since we're transparent. + g.FillRectangle(SystemBrushes.Control, rectangle); + } + } + + // Exceptions during painting are nasty, because paint events happen so often. + // So if user painting code ----s up, we make sure never to call it again, + // so as not to spam the end-user with exception dialogs. + // + private void PaintWithErrorHandling(PaintEventArgs e, short layer) { + try { + CacheTextInternal = true; + if (GetState(STATE_EXCEPTIONWHILEPAINTING)) { + if (layer == PaintLayerBackground) + PaintException(e); + } + else { + bool exceptionThrown = true; + try { + switch (layer) { + case PaintLayerForeground: + OnPaint(e); + break; + case PaintLayerBackground: + if (!GetStyle(ControlStyles.Opaque)) { + OnPaintBackground(e); + } + break; + default: + Debug.Fail("Unknown PaintLayer " + layer); + break; + } + exceptionThrown = false; + } + finally { + if (exceptionThrown) { + SetState(STATE_EXCEPTIONWHILEPAINTING, true); + Invalidate(); + } + } + } + } + finally { + CacheTextInternal = false; + } + } + + /// + /// + /// Find ContainerControl that is the container of this control. + /// + /// + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal ContainerControl ParentContainerControl { + get { + for (Control c = this.ParentInternal; c != null; c = c.ParentInternal) { + if (c is ContainerControl) { + return c as ContainerControl; + } + } + + return null; + } + } + + /// + /// + /// Forces the control to apply layout logic to all of the child controls. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void PerformLayout() { + if (cachedLayoutEventArgs != null) { + PerformLayout(cachedLayoutEventArgs); + cachedLayoutEventArgs = null; + + // VSWhidbey 464817 - we need to be careful + // about which LayoutEventArgs are used in + // SuspendLayout, PerformLayout, ResumeLayout() sequences. + // See bug for more info. + SetState2(STATE2_CLEARLAYOUTARGS, false); + } + else { + PerformLayout(null, null); + } + } + + /// + /// + /// Forces the control to apply layout logic to all of the child controls. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void PerformLayout(Control affectedControl, string affectedProperty) { + PerformLayout(new LayoutEventArgs(affectedControl, affectedProperty)); + } + + internal void PerformLayout(LayoutEventArgs args) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (layoutSuspendCount > 0) { + SetState(STATE_LAYOUTDEFERRED, true); + if (cachedLayoutEventArgs == null || (GetState2(STATE2_CLEARLAYOUTARGS) && args != null)) { + cachedLayoutEventArgs = args; + if (GetState2(STATE2_CLEARLAYOUTARGS)) { + SetState2(STATE2_CLEARLAYOUTARGS, false); + } + } + + LayoutEngine.ProcessSuspendedLayoutEventArgs(this, args); + + return; + } + + // (Essentially the same as suspending layout while we layout, but we clear differently below.) + layoutSuspendCount = 1; + + try { + CacheTextInternal = true; + OnLayout(args); + +#if DEBUG +#if DEBUG_PREFERREDSIZE + // DO NOT REMOVE - See http://wiki/default.aspx/Microsoft.Projects.DotNetClient.PreferredSize + // PreferredSize has an assert that will verify that the current cached size matches + // the computed size. The calls to PreferredSize below help to catch a mismatch earlier. + if (!this.GetState(STATE_DISPOSING | STATE_DISPOSED)) { + if (GetState2(STATE2_USEPREFERREDSIZECACHE)) { + this.PreferredSize.ToString(); + } + + if (args.AffectedControl != null && args.AffectedControl != this && args.AffectedControl.GetState2(STATE2_USEPREFERREDSIZECACHE)) { + args.AffectedControl.PreferredSize.ToString(); + } + } +#endif +#endif + + + } + finally { + CacheTextInternal = false; + // Rather than resume layout (which will could allow a deferred layout to layout the + // the container we just finished laying out) we set layoutSuspendCount back to zero + // and clear the deferred and dirty flags. + SetState(STATE_LAYOUTDEFERRED | STATE_LAYOUTISDIRTY, false); + layoutSuspendCount = 0; + + // LayoutEngine.Layout can return true to request that our parent resize us because + // we did not have enough room for our contents. Now that we are unsuspended, + // see if this happened and layout parent if necessary. (See also OnLayout) + if(ParentInternal != null && ParentInternal.GetState(STATE_LAYOUTISDIRTY)) { + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.PreferredSize); + } + } + } + + /// + /// Peforms data validation (not paint validation!) on a single control. + /// + /// Returns whether validation failed: + /// False = Validation succeeded, control is valid, accept its new value + /// True = Validation was cancelled, control is invalid, reject its new value + /// + /// NOTE: This is the lowest possible level of validation. It does not account + /// for the context in which the validation is occuring, eg. change of focus + /// between controls in a container. Stuff like that is handled by the caller. + /// + /// + internal bool PerformControlValidation(bool bulkValidation) { + + // Skip validation for controls that don't support it + if (!this.CausesValidation) { + return false; + } + + // Raise the 'Validating' event. Stop now if handler cancels (ie. control is invalid). + // NOTE: Handler may throw an exception here, but we must not attempt to catch it. + if (this.NotifyValidating()) { + return true; + } + + // Raise the 'Validated' event. Handlers may throw exceptions here too - but + // convert these to ThreadException events, unless the app is being debugged, + // or the control is being validated as part of a bulk validation operation. + if (bulkValidation || NativeWindow.WndProcShouldBeDebuggable) { + this.NotifyValidated(); + } + else { + try { + this.NotifyValidated(); + } + catch (Exception e) { + Application.OnThreadException(e); + } + } + + return false; + } + + /// + /// Validates all the child controls in a container control. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// Return value indicates whether validation failed for any of the controls validated. + /// Calling function is responsible for checking the correctness of the validationConstraints argument. + /// + /// + internal bool PerformContainerValidation(ValidationConstraints validationConstraints) { + bool failed = false; + + // For every child control of this container control... + foreach (Control c in Controls) { + + // First, if the control is a container, recurse into its descendants. + if ((validationConstraints & ValidationConstraints.ImmediateChildren) != ValidationConstraints.ImmediateChildren && + c.ShouldPerformContainerValidation() && + c.PerformContainerValidation(validationConstraints)) { + failed = true; + } + + // Next, use input flags to decide whether to validate the control itself + if ((validationConstraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle(ControlStyles.Selectable) || + (validationConstraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled || + (validationConstraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible || + (validationConstraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop) { + continue; + } + + // Finally, perform validation on the control itself + if (c.PerformControlValidation(true)) { + failed = true; + } + } + + return failed; + } + + /// + /// + /// Computes the location of the screen point p in client coords. + /// + public Point PointToClient(Point p) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + return PointToClientInternal(p); + } + internal Point PointToClientInternal(Point p) { + // ASURT 42367. + // Win9x reports incorrect values if you go outside the 16-bit range. + // We're not going to do anything about it, though -- it's esoteric, it clutters up the code, + // and potentially causes problems on systems that do support 32-bit coordinates. + + NativeMethods.POINT point = new NativeMethods.POINT(p.X, p.Y); + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(this, Handle), point, 1); + return new Point(point.x, point.y); + } + + /// + /// + /// Computes the location of the client point p in screen coords. + /// + public Point PointToScreen(Point p) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.POINT point = new NativeMethods.POINT(p.X, p.Y); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, Handle), NativeMethods.NullHandleRef, point, 1); + return new Point(point.x, point.y); + } + + /// + /// + /// + /// This method is called by the application's message loop to pre-process + /// input messages before they are dispatched. Possible values for the + /// msg.message field are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR. + /// If this method processes the message it must return true, in which case + /// the message loop will not dispatch the message. + /// + /// + /// For WM_KEYDOWN and WM_SYSKEYDOWN messages, preProcessMessage() first + /// calls processCmdKey() to check for command keys such as accelerators and + /// menu shortcuts. If processCmdKey() doesn't process the message, then + /// isInputKey() is called to check whether the key message represents an + /// input key for the control. Finally, if isInputKey() indicates that the + /// control isn't interested in the key message, then processDialogKey() is + /// called to check for dialog keys such as TAB, arrow keys, and mnemonics. + /// + /// + /// For WM_CHAR messages, preProcessMessage() first calls isInputChar() to + /// check whether the character message represents an input character for + /// the control. If isInputChar() indicates that the control isn't interested + /// in the character message, then processDialogChar() is called to check for + /// dialog characters such as mnemonics. + /// + /// + /// For WM_SYSCHAR messages, preProcessMessage() calls processDialogChar() + /// to check for dialog characters such as mnemonics. + /// + /// + /// When overriding preProcessMessage(), a control should return true to + /// indicate that it has processed the message. For messages that aren't + /// processed by the control, the result of "base.preProcessMessage()" + /// should be returned. Controls will typically override one of the more + /// specialized methods (isInputChar(), isInputKey(), processCmdKey(), + /// processDialogChar(), or processDialogKey()) instead of overriding + /// preProcessMessage(). + /// + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public virtual bool PreProcessMessage(ref Message msg) { + // Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessMessage " + msg.ToString()); + + bool ret; + + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + if (!GetState2(STATE2_UICUES)) { + ProcessUICues(ref msg); + } + + Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys); + if (ProcessCmdKey(ref msg, keyData)) { + ret = true; + } + else if (IsInputKey(keyData)) { + SetState2(STATE2_INPUTKEY, true); + ret = false; + } + else { + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = ProcessDialogKey(keyData); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) { + if (msg.Msg == NativeMethods.WM_CHAR && IsInputChar((char)msg.WParam)) { + SetState2(STATE2_INPUTCHAR, true); + ret = false; + } + else { + ret = ProcessDialogChar((char)msg.WParam); + } + } + else { + ret = false; + } + + return ret; + } + + [EditorBrowsable(EditorBrowsableState.Advanced), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly"), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters"), + SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] // using ref is OK. + public PreProcessControlState PreProcessControlMessage(ref Message msg) { + return PreProcessControlMessageInternal(null, ref msg); + } + + /// + /// This method is similar to PreProcessMessage, but instead of indicating + /// that the message was either processed or it wasn't, it has three return + /// values: + /// + /// MessageProcessed - PreProcessMessage() returns true, and the message + /// needs no further processing + /// + /// MessageNeeded - PreProcessMessage() returns false, but IsInputKey/Char + /// return true. This means the message wasn't processed, + /// but the control is interested in it. + /// + /// MessageNotNeeded - PreProcessMessage() returns false, and IsInputKey/Char + /// return false. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + internal static PreProcessControlState PreProcessControlMessageInternal(Control target, ref Message msg) { + if (target == null) { + target = Control.FromChildHandleInternal(msg.HWnd); + } + + if (target == null) { + return PreProcessControlState.MessageNotNeeded; + } + + // reset state that is used to make sure IsInputChar, IsInputKey and + // ProcessUICues are not called multiple times. + // ISSUE: Which control should these state bits be set on? probably the target. + target.SetState2(STATE2_INPUTKEY, false); + target.SetState2(STATE2_INPUTCHAR, false); + target.SetState2(STATE2_UICUES, true); + + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessControlMessageInternal " + msg.ToString()); + + try { + Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys); + + // Allow control to preview key down message. + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + target.ProcessUICues(ref msg); + + PreviewKeyDownEventArgs args = new PreviewKeyDownEventArgs(keyData); + target.OnPreviewKeyDown(args); + + if (args.IsInputKey) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "PreviewKeyDown indicated this is an input key."); + // Control wants this message - indicate it should be dispatched. + return PreProcessControlState.MessageNeeded; + } + } + + PreProcessControlState state = PreProcessControlState.MessageNotNeeded; + + if (!target.PreProcessMessage(ref msg)) { + if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) { + + // check if IsInputKey has already procssed this message + // or if it is safe to call - we only want it to be called once. + if (target.GetState2(STATE2_INPUTKEY) || target.IsInputKey(keyData)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched"); + state = PreProcessControlState.MessageNeeded; + } + } + else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) { + + // check if IsInputChar has already procssed this message + // or if it is safe to call - we only want it to be called once. + if (target.GetState2(STATE2_INPUTCHAR) || target.IsInputChar((char)msg.WParam)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched"); + state = PreProcessControlState.MessageNeeded; + } + } + } + else { + state = PreProcessControlState.MessageProcessed; + } + + return state; + } + finally { + target.SetState2(STATE2_UICUES, false); + } + } + + /// + /// + /// + /// Processes a command key. This method is called during message + /// pre-processing to handle command keys. Command keys are keys that always + /// take precedence over regular input keys. Examples of command keys + /// include accelerators and menu shortcuts. The method must return true to + /// indicate that it has processed the command key, or false to indicate + /// that the key is not a command key. + /// + /// + /// processCmdKey() first checks if the control has a context menu, and if + /// so calls the menu's processCmdKey() to check for menu shortcuts. If the + /// command key isn't a menu shortcut, and if the control has a parent, the + /// key is passed to the parent's processCmdKey() method. The net effect is + /// that command keys are "bubbled" up the control hierarchy. + /// + /// + /// When overriding processCmdKey(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed by + /// the control, the result of "base.processCmdKey()" should be returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessCmdKey(ref Message msg, Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessCmdKey " + msg.ToString()); + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null && contextMenu.ProcessCmdKey(ref msg, keyData, this)) { + return true; + } + + if (parent != null) { + return parent.ProcessCmdKey(ref msg, keyData); + } + return false; + + } + + private void PrintToMetaFile(HandleRef hDC, IntPtr lParam) { + Debug.Assert(UnsafeNativeMethods.GetObjectType(hDC) == NativeMethods.OBJ_ENHMETADC, + "PrintToMetaFile() called with a non-Enhanced MetaFile DC."); + Debug.Assert(((long) lParam & NativeMethods.PRF_CHILDREN) != 0, + "PrintToMetaFile() called without PRF_CHILDREN."); + + // Strip the PRF_CHILDREN flag. We will manually walk our children and print them. + lParam = (IntPtr)((long) lParam & ~NativeMethods.PRF_CHILDREN); + + // We're the root contol, so we need to set up our clipping region. Retrieve the + // x-coordinates and y-coordinates of the viewport origin for the specified device context. + NativeMethods.POINT viewportOrg = new NativeMethods.POINT(); + bool success = SafeNativeMethods.GetViewportOrgEx(hDC, viewportOrg); + Debug.Assert(success, "GetViewportOrgEx() failed."); + + HandleRef hClippingRegion = new HandleRef(null, SafeNativeMethods.CreateRectRgn(viewportOrg.x,viewportOrg.y, viewportOrg.x + this.Width, viewportOrg.y + this.Height)); + Debug.Assert(hClippingRegion.Handle != IntPtr.Zero, "CreateRectRgn() failed."); + + try { + // Select the new clipping region; make sure it's a SIMPLEREGION or NULLREGION + NativeMethods.RegionFlags selectResult = (NativeMethods.RegionFlags)SafeNativeMethods.SelectClipRgn(hDC, hClippingRegion); + Debug.Assert((selectResult == NativeMethods.RegionFlags.SIMPLEREGION || + selectResult == NativeMethods.RegionFlags.NULLREGION), + "SIMPLEREGION or NULLLREGION expected."); + + PrintToMetaFileRecursive(hDC, lParam, new Rectangle(Point.Empty, this.Size)); + } + finally { + success = SafeNativeMethods.DeleteObject(hClippingRegion); + Debug.Assert(success, "DeleteObject() failed."); + } + } + + internal virtual void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + // We assume the target does not want us to offset the root control in the metafile. + + using (WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, bounds)) { + // print the non-client area + PrintToMetaFile_SendPrintMessage(hDC, (IntPtr)((long)lParam & ~NativeMethods.PRF_CLIENT)); + + // figure out mapping for the client area + NativeMethods.RECT windowRect = new NativeMethods.RECT(); + bool success = UnsafeNativeMethods.GetWindowRect(new HandleRef(null, this.Handle), ref windowRect); + Debug.Assert(success, "GetWindowRect() failed."); + Point clientOffset = PointToScreen(Point.Empty); + clientOffset = new Point(clientOffset.X - windowRect.left, clientOffset.Y - windowRect.top); + Rectangle clientBounds = new Rectangle(clientOffset, ClientSize); + + using (WindowsFormsUtils.DCMapping clientMapping = new WindowsFormsUtils.DCMapping(hDC, clientBounds)) { + // print the client area + PrintToMetaFile_SendPrintMessage(hDC, (IntPtr)((long)lParam & ~NativeMethods.PRF_NONCLIENT)); + + // Paint children in reverse Z-Order + int count = Controls.Count; + for (int i = count - 1; i >= 0; i--) { + Control child = Controls[i]; + if(child.Visible) { + child.PrintToMetaFileRecursive(hDC, lParam, child.Bounds); + } + } + } + } + } + + private void PrintToMetaFile_SendPrintMessage(HandleRef hDC, IntPtr lParam) { + if(GetStyle(ControlStyles.UserPaint)) { + // We let user paint controls paint directly into the metafile + SendMessage(NativeMethods.WM_PRINT, hDC.Handle, lParam); + } + else { + // If a system control has no children in the Controls collection we + // restore the the PRF_CHILDREN flag because it may internally + // have nested children we do not know about. ComboBox is a + // good example. + if(Controls.Count == 0) { + lParam = (IntPtr)((long) lParam | NativeMethods.PRF_CHILDREN); + } + + // System controls must be painted into a temporary bitmap + // which is then copied into the metafile. (Old GDI line drawing + // is 1px thin, which causes borders to disappear, etc.) + using (MetafileDCWrapper dcWrapper = new MetafileDCWrapper(hDC, this.Size)) { + SendMessage(NativeMethods.WM_PRINT, dcWrapper.HDC, lParam); + } + } + } + + /// + /// + /// + /// Processes a dialog character. This method is called during message + /// pre-processing to handle dialog characters, such as control mnemonics. + /// This method is called only if the isInputChar() method indicates that + /// the control isn't interested in the character. + /// + /// + /// processDialogChar() simply sends the character to the parent's + /// processDialogChar() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog characters. + /// + /// + /// When overriding processDialogChar(), a control should return true to + /// indicate that it has processed the character. For characters that aren't + /// processed by the control, the result of "base.processDialogChar()" + /// should be returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool ProcessDialogChar(char charCode) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessDialogChar [" + charCode.ToString() + "]"); + return parent == null? false: parent.ProcessDialogChar(charCode); + } + + /// + /// + /// + /// Processes a dialog key. This method is called during message + /// pre-processing to handle dialog characters, such as TAB, RETURN, ESCAPE, + /// and arrow keys. This method is called only if the isInputKey() method + /// indicates that the control isn't interested in the key. + /// + /// + /// processDialogKey() simply sends the character to the parent's + /// processDialogKey() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog keys. + /// + /// + /// When overriding processDialogKey(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processDialogKey(...)" should be + /// returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected virtual bool ProcessDialogKey(Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessDialogKey " + keyData.ToString()); + return parent == null? false: parent.ProcessDialogKey(keyData); + } + + /// + /// + /// + /// Processes a key message. This method is called when a control receives a + /// keyboard message. The method is responsible for generating the appropriate + /// key events for the message by calling OnKeyPress(), onKeyDown(), or + /// onKeyUp(). The m parameter contains the window message that must + /// be processed. Possible values for the m.msg field are WM_CHAR, + /// WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, and WM_IMECHAR. + /// + /// + /// When overriding processKeyEventArgs(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processKeyEventArgs()" should be + /// returned. + /// + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessKeyEventArgs(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyEventArgs " + m.ToString()); + KeyEventArgs ke = null; + KeyPressEventArgs kpe = null; + IntPtr newWParam = IntPtr.Zero; + + if (m.Msg == NativeMethods.WM_CHAR || m.Msg == NativeMethods.WM_SYSCHAR) { + int charsToIgnore = this.ImeWmCharsToIgnore; + + if (charsToIgnore > 0) { + charsToIgnore--; + Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "charsToIgnore decreased, new val = " + charsToIgnore + ", this=" + this); + + this.ImeWmCharsToIgnore = charsToIgnore; + return false; + } + else { + kpe = new KeyPressEventArgs(unchecked((char)(long)m.WParam)); + OnKeyPress(kpe); + newWParam = (IntPtr) kpe.KeyChar; + } + } + else if (m.Msg == NativeMethods.WM_IME_CHAR) { + int charsToIgnore = this.ImeWmCharsToIgnore; + + if (Marshal.SystemDefaultCharSize == 1) { + // On Win9X we get either an SBCS or an MBCS value. We must convert it to + // UNICODE to use in the KeyPressEventArg. + // + // VSWhidbey 145962: Convert the character in this message to UNICODE + // for use in the KeyPress event, but then convert it back from UNICODE + // to its source value for processing in the DefWindowProc, which will expect a + // non-UNICODE character. + char unicodeChar = (char)0; + byte[] b = new byte[] {(byte)(unchecked((int)(long)m.WParam) >> 8), unchecked((byte)(long)m.WParam)}; + char[] unicodeCharArray = new char[1]; + int stringLength = UnsafeNativeMethods.MultiByteToWideChar(0 /*CP_ACP*/, UnsafeNativeMethods.MB_PRECOMPOSED, b, b.Length, unicodeCharArray, 0); + if (stringLength > 0) { + unicodeCharArray = new char[stringLength]; + UnsafeNativeMethods.MultiByteToWideChar(0 /*CP_ACP*/, UnsafeNativeMethods.MB_PRECOMPOSED, b, b.Length, unicodeCharArray, unicodeCharArray.Length); + + // Per the docs for WM_IME_CHAR, the processing of this message by DefWindowProc + // will produce two WM_CHAR messages if the character value is a DBCS character. + // Otherwise, only one WM_CHAR message will be generated. Therefore, only ignore + // one WM_CHAR message for SBCS characters and two WM_CHAR messages for DBCS + // characters. + + // What type of character were we passed? + if (unicodeCharArray[0] != 0) { + // This was an MBCS character, so pass along the first character + // from the resultant array. + unicodeChar = unicodeCharArray[0]; + + charsToIgnore += 2; + } + else if (unicodeCharArray[0] == 0 && unicodeCharArray.Length >= 2) { + // This was an SBCS character, so pass along the second character + // from the resultant array since the first character in the array is NULL. + unicodeChar = unicodeCharArray[1]; + + charsToIgnore += 1; + } + } + else { + //MultiByteToWideChar failed + throw new Win32Exception(); + } + + this.ImeWmCharsToIgnore = charsToIgnore; + + // + kpe = new KeyPressEventArgs(unicodeChar); + } + else { + charsToIgnore += (3 - Marshal.SystemDefaultCharSize); + this.ImeWmCharsToIgnore = charsToIgnore; + + kpe = new KeyPressEventArgs(unchecked((char)(long)m.WParam)); + } + + char preEventCharacter = kpe.KeyChar; + OnKeyPress(kpe); + + //If the character wasn't changed, just use the original value rather than round tripping. + if (kpe.KeyChar == preEventCharacter) { + newWParam = m.WParam; + } + else { + if (Marshal.SystemDefaultCharSize == 1) { + // VSWhidbey 145962: On Win9X we work with either an SBCS or an MBCS value. Since we + // already converted it to UNICODE to send it to the KeyPress event, we must now convert + // it back to either MBCS or SBCS for processing by the DefWindowProc. + // + string keyChar = new string(new char[] { kpe.KeyChar }); + byte[] mbcsBytes = null; + int bytesNeeded = UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, keyChar, keyChar.Length, null, 0, IntPtr.Zero, IntPtr.Zero); + // GB18030 defines 4 byte characters: we shouldn't assume that the length is capped at 2. + if (bytesNeeded >= 2) { + // This is an MBCS character. + mbcsBytes = new byte[bytesNeeded]; + UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, keyChar, keyChar.Length, mbcsBytes, mbcsBytes.Length, IntPtr.Zero, IntPtr.Zero); + + int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr)); + if (bytesNeeded > sizeOfIntPtr) { + bytesNeeded = sizeOfIntPtr; //Same again: we wouldn't be able to stuff anything larger into a WParam + } + long wParam = 0; + for (int i = 0; i < bytesNeeded; i++) { + wParam <<= 8; + wParam |= (long)mbcsBytes[i]; + } + newWParam = (IntPtr)wParam; + } + else if (bytesNeeded == 1) { + // This is an SBCS character. + mbcsBytes = new byte[bytesNeeded]; + UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, + 0, + keyChar, + keyChar.Length, + mbcsBytes, + mbcsBytes.Length, + IntPtr.Zero, + IntPtr.Zero); + newWParam = (IntPtr)((int)mbcsBytes[0]); + } + else { + //We don't know what's going on: WideCharToMultiByte failed. We can't deal with that. + newWParam = m.WParam; + } + } + else { + newWParam = (IntPtr)kpe.KeyChar; + } + } + } + else { + ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) { + OnKeyDown(ke); + } + else { + OnKeyUp(ke); + } + } + + if (kpe != null) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " processkeyeventarg returning: " + kpe.Handled); + m.WParam = newWParam; + return kpe.Handled; + } + else { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, " processkeyeventarg returning: " + ke.Handled); + if (ke.SuppressKeyPress) { + RemovePendingMessages(NativeMethods.WM_CHAR, NativeMethods.WM_CHAR); + RemovePendingMessages(NativeMethods.WM_SYSCHAR, NativeMethods.WM_SYSCHAR); + RemovePendingMessages(NativeMethods.WM_IME_CHAR, NativeMethods.WM_IME_CHAR); + } + return ke.Handled; + } + } + + /// + /// + /// Processes a key message. This method is called when a control receives a + /// keyboard message. The method first checks if the control has a parent, + /// and if so calls the parent's processKeyPreview() method. If the parent's + /// processKeyPreview() method doesn't consume the message then + /// processKeyEventArgs() is called to generate the appropriate keyboard events. + /// The m parameter contains the window message that must be + /// processed. Possible values for the m.msg field are WM_CHAR, + /// WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and WM_SYSKEYUP. + /// When overriding processKeyMessage(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.processKeyMessage()" should be + /// returned. + /// Controls will seldom, if ever, need to override this method. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessKeyMessage(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyMessage " + m.ToString()); + if (parent != null && parent.ProcessKeyPreview(ref m)) return true; + return ProcessKeyEventArgs(ref m); + } + + /// + /// + /// + /// Previews a keyboard message. This method is called by a child control + /// when the child control receives a keyboard message. The child control + /// calls this method before generating any keyboard events for the message. + /// If this method returns true, the child control considers the message + /// consumed and does not generate any keyboard events. The m + /// parameter contains the window message to preview. Possible values for + /// the m.msg field are WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, + /// and WM_SYSKEYUP. + /// + /// + /// processKeyPreview() simply sends the character to the parent's + /// processKeyPreview() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual + /// processing of dialog keys. + /// + /// + /// When overriding processKeyPreview(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed + /// by the control, the result of "base.ProcessKeyPreview(...)" should be + /// returned. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual bool ProcessKeyPreview(ref Message m) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessKeyPreview " + m.ToString()); + return parent == null? false: parent.ProcessKeyPreview(ref m); + } + + /// + /// + /// + /// Processes a mnemonic character. This method is called to give a control + /// the opportunity to process a mnemonic character. The method should check + /// if the control is in a state to process mnemonics and if the given + /// character represents a mnemonic. If so, the method should perform the + /// action associated with the mnemonic and return true. If not, the method + /// should return false. + /// + /// + /// Implementations of this method often use the isMnemonic() method to + /// check if the given character matches a mnemonic in the control's text, + /// for example: + /// + /// if (canSelect() && isMnemonic(charCode, getText()) { + /// // perform action associated with mnemonic + /// } + /// + /// + /// + /// This default implementation of processMnemonic() simply returns false + /// to indicate that the control has no mnemonic. + /// + /// + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal virtual bool ProcessMnemonic(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.ProcessMnemonic [0x" + ((int)charCode).ToString("X", CultureInfo.InvariantCulture) + "]"); +#endif + return false; + } + + /// + /// Preprocess keys which affect focus indicators and keyboard cues. + /// + internal void ProcessUICues(ref Message msg) { + Keys keyCode = (Keys)((int)msg.WParam) & Keys.KeyCode; + + if (keyCode != Keys.F10 && keyCode != Keys.Menu && keyCode != Keys.Tab) { + return; // PERF: dont WM_QUERYUISTATE if we dont have to. + } + + Control topMostParent = null; + int current = unchecked( (int) (long)SendMessage(NativeMethods.WM_QUERYUISTATE, 0, 0)); + + // VSWhidbey 536248 - dont trust when a control says the accelerators are showing. + // make sure the topmost parent agrees with this as we could be in a mismatched state. + if (current == 0 /*accelerator and focus cues are showing*/) { + topMostParent = this.TopMostParent; + current = (int)topMostParent.SendMessage(NativeMethods.WM_QUERYUISTATE, 0, 0); + } + int toClear = 0; + + // if we are here, a key or tab has been pressed on this control. + // now that we know the state of accelerators, check to see if we need + // to show them. NOTE: due to the strangeness of the API we OR in + // the opposite of what we want to do. So if we want to show accelerators, + // we OR in UISF_HIDEACCEL, then call UIS_CLEAR to clear the "hidden" state. + + if (keyCode == Keys.F10 || keyCode == Keys.Menu) { + if ((current & NativeMethods.UISF_HIDEACCEL) != 0) { + // Keyboard accelerators are hidden, they need to be shown + toClear |= NativeMethods.UISF_HIDEACCEL; + } + } + + if (keyCode == Keys.Tab) { + if ((current & NativeMethods.UISF_HIDEFOCUS) != 0) { + // Focus indicators are hidden, they need to be shown + toClear |= NativeMethods.UISF_HIDEFOCUS; + } + } + + if (toClear != 0) { + // We've detected some state we need to unset, usually clearing the hidden state of + // the accelerators. We need to get the topmost parent and call CHANGEUISTATE so + // that the entire tree of controls is + if (topMostParent == null) { + topMostParent = this.TopMostParent; + } + + // VSWhidbey 434901,362408 + // A) if we're parented to a native dialog - REFRESH our child states ONLY + // Then we've got to send a WM_UPDATEUISTATE to the topmost managed control (which will be non-toplevel) + // (we assume here the root native window has changed UI state, and we're not to manage the UI state for it) + // + // B) if we're totally managed - CHANGE the root window state AND REFRESH our child states. + // Then we've got to send a WM_CHANGEUISTATE to the topmost managed control (which will be toplevel) + // According to MSDN, WM_CHANGEUISTATE will generate WM_UPDATEUISTATE messages for all immediate children (via DefWndProc) + // (we're in charge here, we've got to change the state of the root window) + UnsafeNativeMethods.SendMessage( + new HandleRef(topMostParent, topMostParent.Handle), + UnsafeNativeMethods.GetParent(new HandleRef(null, topMostParent.Handle)) == IntPtr.Zero ? NativeMethods.WM_CHANGEUISTATE : NativeMethods.WM_UPDATEUISTATE, + (IntPtr)(NativeMethods.UIS_CLEAR | (toClear << 16)), + IntPtr.Zero); + } + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaiseDragEvent(Object key, DragEventArgs e) { + DragEventHandler handler = (DragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Raises the event associated with key with the event data of + /// e and a sender of this control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void RaisePaintEvent(Object key, PaintEventArgs e) { + PaintEventHandler handler = (PaintEventHandler)Events[EventPaint]; + if (handler != null) handler(this, e); + } + + private void RemovePendingMessages(int msgMin, int msgMax) { + if (!this.IsDisposed) { + NativeMethods.MSG msg = new NativeMethods.MSG(); + IntPtr hwnd = this.Handle; + while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, hwnd), + msgMin, msgMax, + NativeMethods.PM_REMOVE)) { + ; // NULL loop + } + } + } + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetBackColor() { + BackColor = Color.Empty; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetCursor() { + Cursor = null; + } + + private void ResetEnabled() { + Enabled = true; + } + + /// + /// + /// Resets the font to be based on the parent's font. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetFont() { + Font = null; + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetForeColor() { + ForeColor = Color.Empty; + } + + private void ResetLocation() { + Location = new Point(0,0); + } + + private void ResetMargin() { + Margin = DefaultMargin; + } + + private void ResetMinimumSize() { + MinimumSize = DefaultMinimumSize; + } + + private void ResetPadding() { + CommonProperties.ResetPadding(this); + } + + private void ResetSize() { + Size = DefaultSize; + } + + /// + /// + /// Resets the RightToLeft to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetRightToLeft() { + RightToLeft = RightToLeft.Inherit; + } + + /// + /// + /// Forces the recreation of the handle for this control. Inheriting controls + /// must call base.RecreateHandle. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected void RecreateHandle() { + RecreateHandleCore(); + } + + internal virtual void RecreateHandleCore() { + // + lock(this) { + if (IsHandleCreated) { + + bool focused = ContainsFocus; + +#if DEBUG + if (CoreSwitches.PerfTrack.Enabled) { + Debug.Write("RecreateHandle: "); + Debug.Write(GetType().FullName); + Debug.Write(" [Text="); + Debug.Write(Text); + Debug.Write("]"); + Debug.WriteLine(""); + } +#endif + bool created = (state & STATE_CREATED) != 0; + if (GetState(STATE_TRACKINGMOUSEEVENT)) { + SetState(STATE_MOUSEENTERPENDING, true); + UnhookMouseEvent(); + } + + HandleRef parentHandle = new HandleRef(this, UnsafeNativeMethods.GetParent(new HandleRef(this, this.Handle))); + + try { + Control [] controlSnapshot = null; + state |= STATE_RECREATE; + + try { + + + // VSWhidbey 228656 RecreateHandle - destroy window destroys all children + // Inform child controls that their parent is recreating handle. + + // The default behavior is to now SetParent to parking window, then + // SetParent back after the parent's handle has been recreated. + // This behavior can be overridden in OnParentHandleRecreat* and is in ListView. + + //fish out control collection w/o demand creating one. + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null && controlsCollection.Count > 0) { + controlSnapshot = new Control[controlsCollection.Count]; + for (int i = 0; i < controlsCollection.Count; i++) { + Control childControl = controlsCollection[i]; + if (childControl != null && childControl.IsHandleCreated) { + // SetParent to parking window + childControl.OnParentHandleRecreating(); + + // if we were successful, remember this control + // so we can raise OnParentHandleRecreated + controlSnapshot[i] = childControl; + } + else { + // put in a null slot which we'll skip over later. + controlSnapshot[i] = null; + } + } + } + + // do the main work of recreating the handle + DestroyHandle(); + CreateHandle(); + } + finally { + state &= ~STATE_RECREATE; + + // inform children that their parent's handle has recreated + if (controlSnapshot != null) { + for (int i = 0; i < controlSnapshot.Length; i++) { + Control childControl = controlSnapshot[i]; + if (childControl != null && childControl.IsHandleCreated) { + // SetParent back to the new Parent handle + childControl.OnParentHandleRecreated(); + } + } + } + } + if (created) { + CreateControl(); + } + } + finally { + if (parentHandle.Handle != IntPtr.Zero // the parent was not null + && (Control.FromHandleInternal(parentHandle.Handle) == null || this.parent == null) // but wasnt a windows forms window + && UnsafeNativeMethods.IsWindow(parentHandle)) { // and still is a window + // correctly parent back up to where we were before. + // if we were parented to a proper windows forms control, CreateControl would have properly parented + // us back. + UnsafeNativeMethods.SetParent(new HandleRef(this, this.Handle), parentHandle); + } + } + + + // Restore control focus + if (focused) { + FocusInternal(); + } + } + } + } + + /// + /// + /// Computes the location of the screen rectangle r in client coords. + /// + public Rectangle RectangleToClient(Rectangle r) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(this, Handle), ref rect, 2); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// Computes the location of the client rectangle r in screen coords. + /// + public Rectangle RectangleToScreen(Rectangle r) { + // Reviewed: The following demand has always been commented out since v1.0. In Whidbey, we have + // re-reviewed this method to see if it needs protection, but haven't found any threats, so are + // going to keep it this way. See VSWhidbey #427555 for details. + // + // Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ScreenLocationOfThings Demanded"); + // IntSecurity.ScreenLocationOfThings.Demand(); + + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, Handle), NativeMethods.NullHandleRef, ref rect, 2); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// Reflects the specified message to the control that is bound to hWnd. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected static bool ReflectMessage(IntPtr hWnd, ref Message m) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SendMessages Demanded"); + IntSecurity.SendMessages.Demand(); + return ReflectMessageInternal(hWnd, ref m); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal static bool ReflectMessageInternal(IntPtr hWnd, ref Message m) { + Control control = Control.FromHandleInternal(hWnd); + if (control == null) return false; + m.Result = control.SendMessage(NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + return true; + } + + /// + /// + /// Forces the control to invalidate and immediately + /// repaint itself and any children. + /// + public virtual void Refresh() { + Invalidate(true); + Update(); + } + + /// + /// /Releases UI Automation provinder for specified window. + /// + /// The window handle. + internal virtual void ReleaseUiaProvider(IntPtr handle) { + // When a window that previously returned providers has been destroyed, + // you should notify UI Automation by calling the UiaReturnRawElementProvider + // as follows: UiaReturnRawElementProvider(hwnd, 0, 0, NULL). This call tells + // UI Automation that it can safely remove all map entries that refer to the specified window. + UnsafeNativeMethods.UiaReturnRawElementProvider(new HandleRef(this, handle), new IntPtr(0), new IntPtr(0), null); + } + + /// + /// + /// Resets the mouse leave listeners. + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void ResetMouseEventArgs() { + if (GetState(STATE_TRACKINGMOUSEEVENT)) { + UnhookMouseEvent(); + HookMouseEvent(); + } + } + + /// + /// + /// Resets the text to it's default value. + /// + public virtual void ResetText() { + Text = String.Empty; + } + + private void ResetVisible() { + Visible = true; + } + + /// + /// + /// Resumes normal layout logic. This will force a layout immediately + /// if there are any pending layout requests. + /// + public void ResumeLayout() { + ResumeLayout(true); + } + + /// + /// + /// Resumes normal layout logic. If performLayout is set to true then + /// this will force a layout immediately if there are any pending layout requests. + /// + public void ResumeLayout(bool performLayout) { +#if DEBUG + if (CompModSwitches.LayoutSuspendResume.TraceInfo) { + Debug.WriteLine(GetType().Name + "::ResumeLayout( preformLayout = " + performLayout + ", newCount = " + Math.Max(0, layoutSuspendCount - 1) + ")"); + } +#endif + Debug.Assert(layoutSuspendCount > 0, "Unbalanance suspend/resume layout."); + + bool performedLayout = false; + + if (layoutSuspendCount > 0) { + + if (layoutSuspendCount == 1) { + layoutSuspendCount++; + try { + OnLayoutResuming(performLayout); + } + finally { + layoutSuspendCount--; + } + } + + layoutSuspendCount--; + if (layoutSuspendCount == 0 + && GetState(STATE_LAYOUTDEFERRED) + && performLayout) { + PerformLayout(); + performedLayout = true; + } + } + + + // VSWhidbey 464817 - we need to be careful + // about which LayoutEventArgs are used in + // SuspendLayout, PerformLayout, ResumeLayout() sequences. + // See bug for more info. + if (!performedLayout) { + SetState2(STATE2_CLEARLAYOUTARGS, true); + } + + + /* + + We've had this since Everett,but it seems wrong, redundant and a performance hit. The + correct layout calls are already made when bounds or parenting changes, which is all + we care about. We may want to call this at layout suspend count == 0, but certainaly + not for all resumes. I tried removing it, and doing it only when suspendCount == 0, + but we break things at every step. + + */ + if (!performLayout) { + + CommonProperties.xClearPreferredSizeCache(this); + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + if (controlsCollection != null) { + for (int i = 0; i < controlsCollection.Count; i++) { + LayoutEngine.InitLayout(controlsCollection[i], BoundsSpecified.All); + CommonProperties.xClearPreferredSizeCache(controlsCollection[i]); + } + } + } + + } + + /// + /// Used to actually register the control as a drop target. + /// + /// + internal void SetAcceptDrops(bool accept) { + if (accept != GetState(STATE_DROPTARGET) && IsHandleCreated) { + try { + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + if (accept) { + IntSecurity.ClipboardRead.Demand(); + + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Registering as drop target: " + Handle.ToString()); + // Register + int n = UnsafeNativeMethods.RegisterDragDrop(new HandleRef(this, Handle), (UnsafeNativeMethods.IOleDropTarget)(new DropTarget(this))); + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.CurrentCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_ALREADYREGISTERED) { + throw new Win32Exception(n); + } + } + else { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Revoking drop target: " + Handle.ToString()); + // Revoke + int n = UnsafeNativeMethods.RevokeDragDrop(new HandleRef(this, Handle)); + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.InvariantCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_NOTREGISTERED) { + throw new Win32Exception(n); + } + } + SetState(STATE_DROPTARGET, accept); + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); + } + } + } + + /// + /// + /// Scales to entire control and any child controls. + /// + [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void Scale(float ratio) { + ScaleCore(ratio, ratio); + } + + /// + /// + /// Scales the entire control and any child controls. + /// + [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void Scale(float dx, float dy) { +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + ScaleCore(dx, dy); + } + finally { + ResumeLayout(); +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// + /// Scales a control and its children given a scaling factor. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void Scale(SizeF factor) { + + // VSWhidbey 501184: + // manually call ScaleControl recursively instead of the internal scale method + // when someone calls this method, they really do want to do some sort of + // zooming feature, as opposed to AutoScale. + using (new LayoutTransaction(this, this, PropertyNames.Bounds, false)) { + ScaleControl(factor, factor, this); + if (ScaleChildren) { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { + Control c = controlsCollection[i]; + c.Scale(factor); + } + } + } + } + + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + + } + + /// + /// + /// Scales a control and its children given a pair of scaling factors. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + internal virtual void Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) { + // When we scale, we are establishing new baselines for the + // positions of all controls. Therefore, we should resume(false). + using (new LayoutTransaction(this, this, PropertyNames.Bounds, false)) { + ScaleControl(includedFactor, excludedFactor, requestingControl); + ScaleChildControls(includedFactor, excludedFactor, requestingControl); + } + LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds); + } + + /// + /// + /// Scales the children of this control. The default implementation recursively + /// walks children and calls ScaleControl on each one. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + /// The updateWindowFontIfNeeded parameter indicates if we need to update Window + /// font for controls that need it, i.e. controls using default or inherited font, + /// that are also not user-painted. + /// + internal void ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, bool updateWindowFontIfNeeded = false) { + + if (ScaleChildren) { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { + Control c = controlsCollection[i]; + + // Update window font before scaling, as controls often use font metrics during scaling. + if (updateWindowFontIfNeeded) { + c.UpdateWindowFontIfNeeded(); + } + + c.Scale(includedFactor, excludedFactor, requestingControl); + } + } + } + } + + /// + /// Calls SetWindowFont if DpiHelper.EnableDpiChangedHighDpiImprovements is true, + /// control uses default or inherited font and is not user-painted. + /// + internal void UpdateWindowFontIfNeeded() { + if (DpiHelper.EnableDpiChangedHighDpiImprovements && !GetStyle(ControlStyles.UserPaint) && (Properties.GetObject(PropFont) == null)) { + SetWindowFont(); + } + } + + /// + /// Scales the children of this control. The default implementation walks the controls + /// collection for the control and calls Scale on each control. + /// IncludedFactor will be applied to the dimensions of controls based on + /// their RequiredScaling property. For example, if a control's + /// RequiredScaling property returns Width, the width of the control will + /// be scaled according to the includedFactor value. + /// + /// The excludedFactor parameter is used to scale those control bounds who + /// are not included in RequiredScaling. + /// + /// If a factor is empty, it indicates that no scaling of those control + /// dimensions should be done. + /// + /// The requestingControl property indicates which control has requested + /// the scaling function. + /// + internal void ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl) { + try { + IsCurrentlyBeingScaled = true; + + BoundsSpecified includedSpecified = BoundsSpecified.None; + BoundsSpecified excludedSpecified = BoundsSpecified.None; + + if (!includedFactor.IsEmpty) { + includedSpecified = RequiredScaling; + } + + if (!excludedFactor.IsEmpty) { + excludedSpecified |= (~RequiredScaling & BoundsSpecified.All); + } +#if DEBUG + if (CompModSwitches.RichLayout.TraceInfo) { + Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Scaling {0} Included: {1}, Excluded: {2}", + this, includedFactor, excludedFactor)); + } +#endif + + if (includedSpecified != BoundsSpecified.None) { + ScaleControl(includedFactor, includedSpecified); + } + + if (excludedSpecified != BoundsSpecified.None) { + ScaleControl(excludedFactor, excludedSpecified); + } + + if (!includedFactor.IsEmpty) { + RequiredScaling = BoundsSpecified.None; + } + } + finally { + IsCurrentlyBeingScaled = false; + } + } + + /// + /// + /// Scales an individual control's location, size, padding and margin. + /// If the control is top level, this will not scale the control's location. + /// This does not scale children or the size of auto sized controls. You can + /// omit scaling in any direction by changing BoundsSpecified. + /// + /// After the control is scaled the RequiredScaling property is set to + /// BoundsSpecified.None. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void ScaleControl(SizeF factor, BoundsSpecified specified) { + + CreateParams cp = CreateParams; + NativeMethods.RECT adornments = new NativeMethods.RECT(0, 0, 0, 0); + AdjustWindowRectEx(ref adornments, cp.Style, HasMenu, cp.ExStyle); + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + + // VSWhidbey 412836 + // clear out min and max size, otherwise this could affect the scaling logic. + MinimumSize = Size.Empty; + MaximumSize = Size.Empty; + + // this is raw because Min/Max size have been cleared at this point. + Rectangle rawScaledBounds = GetScaledBounds(Bounds, factor, specified); + + // + // Scale Padding and Margin + // + float dx = factor.Width; + float dy = factor.Height; + + Padding padding = Padding; + Padding margins = Margin; + + // Clear off specified bits for 1.0 scaling factors + if (dx == 1.0F) specified &= ~(BoundsSpecified.X | BoundsSpecified.Width); + if (dy == 1.0F) specified &= ~(BoundsSpecified.Y | BoundsSpecified.Height); + + if (dx != 1.0F) { + padding.Left = (int)Math.Round(padding.Left * dx); + padding.Right = (int)Math.Round(padding.Right * dx); + margins.Left = (int)Math.Round(margins.Left * dx); + margins.Right = (int)Math.Round(margins.Right * dx); + } + + if (dy != 1.0F) { + padding.Top = (int)Math.Round(padding.Top * dy); + padding.Bottom = (int)Math.Round(padding.Bottom * dy); + margins.Top = (int)Math.Round(margins.Top * dy); + margins.Bottom = (int)Math.Round(margins.Bottom * dy); + } + + // Apply padding and margins + Padding = padding; + Margin = margins; + + // + // Scale Min/Max size + // + + // VSWhidbey 412836 + // make sure we consider the andornments as fixed. rather than scaling the entire size, + // we should pull out the fixed things such as the border, scale the rest, then apply the fixed + // adornment size. + Size adornmentSize = adornments.Size; + if (!minSize.IsEmpty) { + minSize -= adornmentSize; + minSize = ScaleSize(LayoutUtils.UnionSizes(Size.Empty, minSize), // make sure we dont go below 0. + factor.Width, + factor.Height) + adornmentSize; + } + if (!maxSize.IsEmpty) { + maxSize -= adornmentSize; + maxSize = ScaleSize(LayoutUtils.UnionSizes(Size.Empty, maxSize), // make sure we dont go below 0. + factor.Width, + factor.Height) + adornmentSize; + } + + + // Apply the min/max size constraints - dont call ApplySizeConstraints + // as MinimumSize/MaximumSize are currently cleared out. + Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(maxSize); + Size scaledSize = LayoutUtils.IntersectSizes(rawScaledBounds.Size, maximumSize); + scaledSize = LayoutUtils.UnionSizes(scaledSize, minSize); + + if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (ParentInternal != null) && (ParentInternal.LayoutEngine == DefaultLayout.Instance)) { + // We need to scale AnchorInfo to update distances to container edges + DefaultLayout.ScaleAnchorInfo((IArrangedElement)this, factor); + } + + // Set in the scaled bounds as constrained by the newly scaled min/max size. + SetBoundsCore(rawScaledBounds.X, rawScaledBounds.Y, scaledSize.Width, scaledSize.Height, BoundsSpecified.All); + + MaximumSize = maxSize; + MinimumSize = minSize; + + + } + + + /// + /// + /// Performs the work of scaling the entire control and any child controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void ScaleCore(float dx, float dy) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ScaleCore(" + dx + ", " + dy + ")"); +#if DEBUG + int dbgLayoutCheck = LayoutSuspendCount; +#endif + SuspendLayout(); + try { + int sx = (int)Math.Round(x * dx); + int sy = (int)Math.Round(y * dy); + + int sw = width; + if ((controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth) { + sw = (int)(Math.Round((x + width) * dx)) - sx; + } + int sh = height; + if ((controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight) { + sh = (int)(Math.Round((y + height) * dy)) - sy; + } + + SetBounds(sx, sy, sw, sh, BoundsSpecified.All); + + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection != null) { + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for (int i = 0; i < controlsCollection.Count; i++) { +#pragma warning disable 618 + controlsCollection[i].Scale(dx, dy); +#pragma warning restore 618 + } + } + } + finally { + ResumeLayout(); +#if DEBUG + AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// Scales a given size with the provided values. + /// + internal Size ScaleSize(Size startSize, float x, float y) { + Size size = startSize; + if (!GetStyle(ControlStyles.FixedWidth)) { + size.Width = (int)Math.Round((float)size.Width * x); + } + if (!GetStyle(ControlStyles.FixedHeight)) { + size.Height = (int)Math.Round((float)size.Height * y); + } + return size; + } + + /// + /// + /// Activates this control. + /// + public void Select() { + Select(false, false); + } + + // used by Form + /// + /// + /// [To be supplied.] + /// + protected virtual void Select(bool directed, bool forward) { + IContainerControl c = GetContainerControlInternal(); + + if (c != null) { + c.ActiveControl = this; + } + } + + /// + /// + /// Selects the next control following ctl. + /// + public bool SelectNextControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + Control nextSelectableControl = this.GetNextSelectableControl(ctl, forward, tabStopOnly, nested, wrap); + if (nextSelectableControl != null) { + nextSelectableControl.Select(true, forward); + return true; + } + else { + return false; + } + } + + private Control GetNextSelectableControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + if (!Contains(ctl) || !nested && ctl.parent != this) ctl = null; + + bool alreadyWrapped = false; + Control start = ctl; + do { + ctl = GetNextControl(ctl, forward); + if (ctl == null) { + if (!wrap) break; + if (alreadyWrapped) { + return null; //VSWhidbey 423098 prevent infinite wrapping. + } + alreadyWrapped = true; + } + else { + if (ctl.CanSelect + && (!tabStopOnly || ctl.TabStop) + && (nested || ctl.parent == this)) { + + if (AccessibilityImprovements.Level3 && ctl.parent is ToolStrip) { + continue; + } + return ctl; + } + } + } while (ctl != start); + return null; + } + + + /// + /// Unsafe internal version of SelectNextControl - Use with caution! + /// + internal bool SelectNextControlInternal(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) { + return SelectNextControl(ctl, forward, tabStopOnly, nested, wrap); + } + + /// + /// This is called recursively when visibility is changed for a control, this + /// forces focus to be moved to a visible control. + /// + private void SelectNextIfFocused() { + // V#32437 - We want to move focus away from hidden controls, so this + // function was added. + // + if (ContainsFocus && ParentInternal != null) { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + + if (c != null) { + // SECREVIEW : Control.SelectNextControl generates a call to ContainerControl.ActiveControl which demands + // ModifyFocus permission, the demand is to prevent DOS attacks but it doesn't expose a sec + // vulnerability indirectly. So it is safe to call the internal version of SelectNextControl here. + // + ((Control)c).SelectNextControlInternal(this, true, true, true, true); + } + } + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, ref int wparam, ref int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, ref wparam, ref lparam); + } + internal IntPtr SendMessage(int msg, int wparam, IntPtr lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, (IntPtr)wparam, lparam); + } + + internal IntPtr SendMessage(int msg, IntPtr wparam, IntPtr lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + internal IntPtr SendMessage(int msg, IntPtr wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, (IntPtr)lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, ref NativeMethods.RECT lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, ref lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, bool wparam, int lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + internal IntPtr SendMessage(int msg, int wparam, string lparam) { + Debug.Assert(IsHandleCreated, "Performance alert! Calling Control::SendMessage and forcing handle creation. Re-work control so handle creation is not required to set properties. If there is no work around, wrap the call in an IsHandleCreated check."); + return UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), msg, wparam, lparam); + } + /// + /// + /// sends this control to the back of the z-order + /// + public void SendToBack() { + if (parent != null) { + parent.Controls.SetChildIndex(this, -1); + } + else if (IsHandleCreated && GetTopLevel()) { + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.HWND_BOTTOM, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + + } + + /// + /// + /// Sets the bounds of the control. + /// + public void SetBounds(int x, int y, int width, int height) { + if (this.x != x || this.y != y || this.width != width || + this.height != height) { + SetBoundsCore(x, y, width, height, BoundsSpecified.All); + + // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will + // have refreshed the interior layout. We only need to layout the parent. + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + else { + // Still need to init scaling. + InitScaling(BoundsSpecified.All); + } + } + + /// + /// + /// Sets the bounds of the control. + /// + public void SetBounds(int x, int y, int width, int height, BoundsSpecified specified) { + if ((specified & BoundsSpecified.X) == BoundsSpecified.None) x = this.x; + if ((specified & BoundsSpecified.Y) == BoundsSpecified.None) y = this.y; + if ((specified & BoundsSpecified.Width) == BoundsSpecified.None) width = this.width; + if ((specified & BoundsSpecified.Height) == BoundsSpecified.None) height = this.height; + if (this.x != x || this.y != y || this.width != width || + this.height != height) { + SetBoundsCore(x, y, width, height, specified); + + // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will + // have refreshed the interior layout or the resized control. We only need to layout + // the parent. This happens after InitLayout has been invoked. + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + } + else { + // Still need to init scaling. + InitScaling(specified); + } + } + + /// + /// + /// Performs the work of setting the bounds of this control. Inheriting + /// classes can overide this function to add size restrictions. Inheriting + /// classes must call base.setBoundsCore to actually cause the bounds + /// of the control to change. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { +#if DEBUG + if (CompModSwitches.SetBounds.TraceInfo) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "{0}::SetBoundsCore(x={1} y={2} width={3} height={4} specified={5}", Name, x, y, width, height, specified)); + } +#endif + // SetWindowPos below sends a WmWindowPositionChanged (not posts) so we immediately + // end up in WmWindowPositionChanged which may cause the parent to layout. We need to + // suspend/resume to defer the parent from laying out until after InitLayout has been called + // to update the layout engine's state with the new control bounds. +#if DEBUG + int suspendCount = -44371; // Arbitrary negative prime to surface bugs. +#endif + if (ParentInternal != null) { +#if DEBUG + suspendCount = ParentInternal.LayoutSuspendCount; +#endif + ParentInternal.SuspendLayout(); + } + try { + if (this.x != x || this.y != y || this.width != width || this.height != height) { + CommonProperties.UpdateSpecifiedBounds(this, x, y, width, height, specified); + + // Provide control with an opportunity to apply self imposed constraints on its size. + Rectangle adjustedBounds = ApplyBoundsConstraints(x,y, width, height); + width = adjustedBounds.Width; + height = adjustedBounds.Height; + x = adjustedBounds.X; + y = adjustedBounds.Y; + + if (!IsHandleCreated) { + // Handle is not created, just record our new position and we're done. + UpdateBounds(x, y, width, height); + } else { + if (!GetState(STATE_SIZELOCKEDBYOS)) { + int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE; + + if (this.x == x && this.y == y) { + flags |= NativeMethods.SWP_NOMOVE; + } + if (this.width == width && this.height == height) { + flags |= NativeMethods.SWP_NOSIZE; + } + + // + // Give a chance for derived controls to do what they want, just before we resize. + OnBoundsUpdate(x, y, width, height); + + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.NullHandleRef, x, y, width, height, flags); + + // NOTE: SetWindowPos causes a WM_WINDOWPOSCHANGED which is processed + // synchonously so we effectively end up in UpdateBounds immediately following + // SetWindowPos. + // + //UpdateBounds(x, y, width, height); + } + } + } + } + finally { + + // Initialize the scaling engine. + InitScaling(specified); + + if (ParentInternal != null) { + // Some layout engines (DefaultLayout) base their PreferredSize on + // the bounds of their children. If we change change the child bounds, we + // need to clear their PreferredSize cache. The semantics of SetBoundsCore + // is that it does not cause a layout, so we just clear. + CommonProperties.xClearPreferredSizeCache(ParentInternal); + + // Cause the current control to initialize its layout (e.g., Anchored controls + // memorize their distance from their parent's edges). It is your parent's + // LayoutEngine which manages your layout, so we call into the parent's + // LayoutEngine. + ParentInternal.LayoutEngine.InitLayout(this, specified); + ParentInternal.ResumeLayout( /* performLayout = */ true ); +#if DEBUG + Debug.Assert(ParentInternal.LayoutSuspendCount == suspendCount, "Suspend/Resume layout mismatch!"); +#endif + } + } + } + + /// + /// + /// Performs the work of setting the size of the client area of the control. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void SetClientSizeCore(int x, int y) { + Size = SizeFromClientSize(x, y); + clientWidth = x; + clientHeight = y; + OnClientSizeChanged(EventArgs.Empty); + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual Size SizeFromClientSize(Size clientSize) { + return SizeFromClientSize(clientSize.Width, clientSize.Height); + } + + internal Size SizeFromClientSize(int width, int height) { + NativeMethods.RECT rect = new NativeMethods.RECT(0, 0, width, height); + + CreateParams cp = CreateParams; + AdjustWindowRectEx(ref rect, cp.Style, HasMenu, cp.ExStyle); + return rect.Size; + } + + private void SetHandle(IntPtr value) { + if (value == IntPtr.Zero) { + SetState(STATE_CREATED, false); + } + UpdateRoot(); + } + + private void SetParentHandle(IntPtr value) { + Debug.Assert(value != NativeMethods.InvalidIntPtr, "Outdated call to SetParentHandle"); + + if (IsHandleCreated) { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(window, Handle)); + bool topLevel = GetTopLevel(); + if (parentHandle != value || (parentHandle == IntPtr.Zero && !topLevel)) { + Debug.Assert(Handle != value, "Cycle created in SetParentHandle"); + + bool recreate = (parentHandle == IntPtr.Zero && !topLevel) + || (value == IntPtr.Zero && topLevel); + + if (recreate) { + // We will recreate later, when the MdiChild's visibility + // is set to true (see bug 124232) + Form f = this as Form; + if (f != null) { + if (!f.CanRecreateHandle()) { + recreate = false; + // we don't want to recreate - but our styles may have changed. + // before we unpark the window below we need to update + UpdateStyles(); + } + } + } + + if (recreate) { + RecreateHandle(); + } + if (!GetTopLevel()) { + if (value == IntPtr.Zero) { + Application.ParkHandle(new HandleRef(window, Handle), this.DpiAwarenessContext); + UpdateRoot(); + } + else { + UnsafeNativeMethods.SetParent(new HandleRef(window, Handle), new HandleRef(null, value)); + if (parent != null) { + parent.UpdateChildZOrder(this); + } + Application.UnparkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext); + } + } + } + else if (value == IntPtr.Zero && parentHandle == IntPtr.Zero && topLevel) { + // See VSWhidbey 531685: The handle was previously parented to the parking window. Its TopLevel property was + // then changed to true so the above call to GetParent returns null even though the parent of the control is + // not null. We need to explicitly set the parent to null. + UnsafeNativeMethods.SetParent(new HandleRef(window, Handle), new HandleRef(null, IntPtr.Zero)); + Application.UnparkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext); + } + } + } + + // Form, UserControl, AxHost usage + internal void SetState(int flag, bool value) { + state = value? state | flag: state & ~flag; + } + + // Application, SKWindow usage + internal void SetState2(int flag, bool value) { + state2 = value ? state2 | flag : state2 & ~flag; + } + + /// + /// + /// Sets the current value of the specified bit in the control's style. + /// NOTE: This is control style, not the Win32 style of the hWnd. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void SetStyle(ControlStyles flag, bool value) { + // WARNING: if we ever add argument checking to "flag", we will need + // to move private styles like Layered to State. + // ASURT: 151576 Calling SetStyle(ControlStyles.EnableNotifyMessage,...) should require UnmanagedCode + if ((flag & ControlStyles.EnableNotifyMessage) != 0 && value) + { + // demand security permission for this condition. + // this will throw security exception in semi-trust. + IntSecurity.UnmanagedCode.Demand(); + } + controlStyle = value? controlStyle | flag: controlStyle & ~flag; + } + + internal static IntPtr SetUpPalette(IntPtr dc, bool force, bool realizePalette) { + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, "SetUpPalette(force:=" + force + ", ralizePalette:=" + realizePalette + ")"); + + IntPtr halftonePalette = Graphics.GetHalftonePalette(); + + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, "select palette " + !force); + IntPtr result = SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, halftonePalette), (force ? 0 : 1)); + + if (result != IntPtr.Zero && realizePalette) { + SafeNativeMethods.RealizePalette(new HandleRef(null, dc)); + } + + return result; + } + + /// + /// + /// [To be supplied.] + /// + protected void SetTopLevel(bool value) { + if (value && IsActiveX) { + throw new InvalidOperationException(SR.GetString(SR.TopLevelNotAllowedIfActiveX)); + } + else { + if (value) { + if (this is Form) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "TopLevelWindow Demanded"); + IntSecurity.TopLevelWindow.Demand(); + } + else { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "UnrestrictedWindows Demanded"); + IntSecurity.UnrestrictedWindows.Demand(); + } + } + + SetTopLevelInternal(value); + } + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal void SetTopLevelInternal(bool value) { + + if (GetTopLevel() != value) { + if (parent != null) { + throw new ArgumentException(SR.GetString(SR.TopLevelParentedControl), "value"); + } + SetState(STATE_TOPLEVEL, value); + // VSWhidbey 476889: make sure the handle is created before hooking, otherwise a toplevel control that never + // creates its handle will leak. + if (IsHandleCreated && GetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED)) { + ListenToUserPreferenceChanged(value); + } + UpdateStyles(); + SetParentHandle(IntPtr.Zero); + if (value && Visible) { + CreateControl(); + } + UpdateRoot(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void SetVisibleCore(bool value) { + try { + System.Internal.HandleCollector.SuspendCollect(); + + if (GetVisibleCore() != value) { + if (!value) { + SelectNextIfFocused(); + } + + bool fireChange = false; + + if (GetTopLevel()) { + + // The processing of WmShowWindow will set the visibility + // bit and call CreateControl() + // + if (IsHandleCreated || value) { + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), value ? ShowParams : NativeMethods.SW_HIDE); + } + } + else if (IsHandleCreated || value && parent != null && parent.Created) { + + // We want to mark the control as visible so that CreateControl + // knows that we are going to be displayed... however in case + // an exception is thrown, we need to back the change out. + // + SetState(STATE_VISIBLE, value); + fireChange = true; + try { + if (value) CreateControl(); + SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), + NativeMethods.NullHandleRef, + 0, 0, 0, 0, + NativeMethods.SWP_NOSIZE + | NativeMethods.SWP_NOMOVE + | NativeMethods.SWP_NOZORDER + | NativeMethods.SWP_NOACTIVATE + | (value ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW)); + } + catch { + SetState(STATE_VISIBLE, !value); + throw; + } + } + if (GetVisibleCore() != value) { + SetState(STATE_VISIBLE, value); + fireChange = true; + } + + if (fireChange) { + // We do not do this in the OnPropertyChanged event for visible + // Lots of things could cause us to become visible, including a + // parent window. We do not want to indescriminiately layout + // due to this, but we do want to layout if the user changed + // our visibility. + // + + using (new LayoutTransaction(parent, this, PropertyNames.Visible)) { + OnVisibleChanged(EventArgs.Empty); + } + } + UpdateRoot(); + } + else { // value of Visible property not changed, but raw bit may have + + if (!GetState(STATE_VISIBLE) && !value && IsHandleCreated) { + // PERF - setting Visible=false twice can get us into this else block + // which makes us process WM_WINDOWPOS* messages - make sure we've already + // visible=false - if not, make it so. + if (!SafeNativeMethods.IsWindowVisible(new HandleRef(this,this.Handle))) { + // we're already invisible - bail. + return; + } + } + + SetState(STATE_VISIBLE, value); + + // If the handle is already created, we need to update the window style. + // This situation occurs when the parent control is not currently visible, + // but the child control has already been created. + // + if (IsHandleCreated) { + + SafeNativeMethods.SetWindowPos( + new HandleRef(window, Handle), NativeMethods.NullHandleRef, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE | + (value ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW)); + } + } + } + finally { + System.Internal.HandleCollector.ResumeCollect(); + } + } + + /// + /// + /// Determine effective auto-validation setting for a given control, based on the AutoValidate property + /// of its containing control. Defaults to 'EnablePreventFocusChange' if there is no containing control + /// (eg. because this control is a top-level container). + /// + /// + internal static AutoValidate GetAutoValidateForControl(Control control) { + ContainerControl parent = control.ParentContainerControl; + return (parent != null) ? parent.AutoValidate : AutoValidate.EnablePreventFocusChange; + } + + /// + /// + /// Is auto-validation currently in effect for this control? + /// Depends on the AutoValidate property of the containing control. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldAutoValidate { + get { + return GetAutoValidateForControl(this) != AutoValidate.Disable; + } + } + + // Refer vsW: 543074: This method is called in PerformContainerValidation to check if this control supports containerValidation. + // TabControl overrides this method to return true. + internal virtual bool ShouldPerformContainerValidation() { + return GetStyle(ControlStyles.ContainerControl); + } + + /// + /// + /// Returns true if the backColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeBackColor() { + Color backColor = Properties.GetColor(PropBackColor); + return !backColor.IsEmpty; + } + + /// + /// + /// Returns true if the cursor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeCursor() { + bool found; + object cursor = Properties.GetObject(PropCursor, out found); + return (found && cursor != null); + } + + /// + /// Returns true if the enabled property should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeEnabled() { + return (!GetState(STATE_ENABLED)); + } + + /// + /// + /// Returns true if the foreColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeForeColor() { + Color foreColor = Properties.GetColor(PropForeColor); + return !foreColor.IsEmpty; + } + + /// + /// + /// Returns true if the font should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeFont() { + bool found; + object font = Properties.GetObject(PropFont, out found); + return (found && font != null); + } + + /// + /// + /// Returns true if the RightToLeft should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeRightToLeft() { + bool found; + int rtl = Properties.GetInteger(PropRightToLeft, out found); + return (found && rtl != (int)RightToLeft.Inherit); + } + + /// + /// Returns true if the visible property should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeVisible() { + return (!GetState(STATE_VISIBLE)); + } + + // Helper function - translates text alignment for Rtl controls + // Read TextAlign as Left == Near, Right == Far + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected HorizontalAlignment RtlTranslateAlignment(HorizontalAlignment align) { + return RtlTranslateHorizontal(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected LeftRightAlignment RtlTranslateAlignment(LeftRightAlignment align) { + return RtlTranslateLeftRight(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected ContentAlignment RtlTranslateAlignment(ContentAlignment align) { + return RtlTranslateContent(align); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected HorizontalAlignment RtlTranslateHorizontal(HorizontalAlignment align) { + + if (RightToLeft.Yes == RightToLeft) { + if (HorizontalAlignment.Left == align) { + return HorizontalAlignment.Right; + } + else if (HorizontalAlignment.Right == align) { + return HorizontalAlignment.Left; + } + } + + return align; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected LeftRightAlignment RtlTranslateLeftRight(LeftRightAlignment align) { + + if (RightToLeft.Yes == RightToLeft) { + if (LeftRightAlignment.Left == align) { + return LeftRightAlignment.Right; + } + else if (LeftRightAlignment.Right == align) { + return LeftRightAlignment.Left; + } + } + + return align; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal ContentAlignment RtlTranslateContent(ContentAlignment align) { + if (RightToLeft.Yes == RightToLeft) { + + if ((align & WindowsFormsUtils.AnyTopAlign) != 0) { + switch (align) { + case ContentAlignment.TopLeft: + return ContentAlignment.TopRight; + case ContentAlignment.TopRight: + return ContentAlignment.TopLeft; + } + } + if ((align & WindowsFormsUtils.AnyMiddleAlign) != 0 ) { + switch (align) { + case ContentAlignment.MiddleLeft: + return ContentAlignment.MiddleRight; + case ContentAlignment.MiddleRight: + return ContentAlignment.MiddleLeft; + } + } + + if ((align & WindowsFormsUtils.AnyBottomAlign) != 0 ) { + switch (align) { + case ContentAlignment.BottomLeft: + return ContentAlignment.BottomRight; + case ContentAlignment.BottomRight: + return ContentAlignment.BottomLeft; + } + } + } + return align; + } + + private void SetWindowFont(){ + SendMessage(NativeMethods.WM_SETFONT, FontHandle, 0 /*redraw = false*/); + } + + private void SetWindowStyle(int flag, bool value) { + int styleFlags = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)(value? styleFlags | flag: styleFlags & ~flag))); + } + + + /// + /// + /// Makes the control display by setting the visible property to true + /// + public void Show() { + Visible = true; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializeMargin() { + return !Margin.Equals(DefaultMargin); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeMaximumSize() { + return MaximumSize != DefaultMaximumSize; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeMinimumSize() { + return MinimumSize != DefaultMinimumSize; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializePadding() { + return !Padding.Equals(DefaultPadding); + } + + /// + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeSize() { + // VSWhidbey 379287: In Whidbey the ControlDesigner class will always serialize size as it replaces the Size + // property descriptor with its own. This is here for compat. + Size s = DefaultSize; + return width != s.Width || height != s.Height; + } + + /// + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeText() { + return Text.Length != 0; + } + + /// + /// + /// Suspends the layout logic for the control. + /// + public void SuspendLayout() { + + + layoutSuspendCount++; + if (layoutSuspendCount == 1) { + OnLayoutSuspended(); + } + +#if DEBUG + Debug.Assert(layoutSuspendCount > 0, "SuspendLayout: layoutSuspendCount overflowed."); + if (CompModSwitches.LayoutSuspendResume.TraceInfo) { + Debug.WriteLine(GetType().Name + "::SuspendLayout( newCount = " + layoutSuspendCount + ")"); + } +#endif + } + + /// + /// Stops listening for the mouse leave event. + /// + /// + private void UnhookMouseEvent() { + SetState(STATE_TRACKINGMOUSEEVENT, false); + } + + /// + /// + /// Forces the control to paint any currently invalid areas. + /// + public void Update() { + SafeNativeMethods.UpdateWindow(new HandleRef(window, InternalHandle)); + } + + /// + /// + /// Updates the bounds of the control based on the handle the control is + /// bound to. + /// + // Internal for ScrollableControl + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal void UpdateBounds() { + NativeMethods.RECT rect = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(window, InternalHandle), ref rect); + int clientWidth = rect.right; + int clientHeight = rect.bottom; + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, InternalHandle), ref rect); + if (!GetTopLevel()) { + UnsafeNativeMethods.MapWindowPoints(NativeMethods.NullHandleRef, new HandleRef(null, UnsafeNativeMethods.GetParent(new HandleRef(window, InternalHandle))), ref rect, 2); + } + + UpdateBounds(rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, clientWidth, clientHeight); + } + + /// + /// + /// Updates the bounds of the control based on the bounds passed in. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateBounds(int x, int y, int width, int height) { + Debug.Assert(!IsHandleCreated, "Don't call this method when handle is created!!"); + + // reverse-engineer the AdjustWindowRectEx call to figure out + // the appropriate clientWidth and clientHeight + NativeMethods.RECT rect = new NativeMethods.RECT(); + rect.left = rect.right = rect.top = rect.bottom = 0; + + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle); + int clientWidth = width - (rect.right - rect.left); + int clientHeight = height - (rect.bottom - rect.top); + UpdateBounds(x, y, width, height, clientWidth, clientHeight); + } + + /// + /// + /// Updates the bounds of the control based on the bounds passed in. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateBounds(int x, int y, int width, int height, int clientWidth, int clientHeight) { +#if DEBUG + if (CompModSwitches.SetBounds.TraceVerbose){ + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "{0}::UpdateBounds(", Name)); + Debug.Indent(); + Debug.WriteLine(String.Format( + CultureInfo.CurrentCulture, "oldBounds={{x={0} y={1} width={2} height={3} clientWidth={4} clientHeight={5}}}", + this.x, this.y, this.width, this.height, this.clientWidth, this.clientHeight)); + } +#endif // DEBUG + + + bool newLocation = this.x != x || this.y != y; + bool newSize = this.Width != width || this.Height != height || + this.clientWidth != clientWidth || this.clientHeight != clientHeight; + + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.clientWidth = clientWidth; + this.clientHeight = clientHeight; + + if (newLocation) { +#if DEBUG + Rectangle originalBounds = this.Bounds; +#endif + OnLocationChanged(EventArgs.Empty); +#if DEBUG + if (this.Bounds != originalBounds && CompModSwitches.SetBounds.TraceWarning) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "WARNING: Bounds changed during OnLocationChanged()\r\nbefore={0} after={1}", originalBounds, this.Bounds)); + } +#endif + } + if (newSize) { +#if DEBUG + Rectangle originalBounds = this.Bounds; +#endif + OnSizeChanged(EventArgs.Empty); + OnClientSizeChanged(EventArgs.Empty); + + // Clear PreferredSize cache for this control + CommonProperties.xClearPreferredSizeCache(this); + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + +#if DEBUG + if (this.Bounds != originalBounds && CompModSwitches.SetBounds.TraceWarning) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "WARNING: Bounds changed during OnSizeChanged()\r\nbefore={0} after={1}", originalBounds, this.Bounds)); + } +#endif + } + + +#if DEBUG + if (CompModSwitches.SetBounds.TraceVerbose) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "newBounds={{x={0} y={1} width={2} height={3} clientWidth={4} clientHeight={5}}}",x, y, width, height, clientWidth, clientHeight)); + Debug.Unindent(); + } +#endif + } + + /// + /// Updates the binding manager bindings when the binding proeprty changes. + /// We have the code here, rather than in PropertyChagned, so we don't pull + /// in the data assembly if it's not used. + /// + private void UpdateBindings() { + for (int i = 0; i < DataBindings.Count; i++) { + BindingContext.UpdateBinding(BindingContext, DataBindings[i]); + } + } + + /// + /// Updates the child control's position in the control array to correctly + /// reflect it's index. + /// + private void UpdateChildControlIndex(Control ctl) { + // VSO 411856 + // Don't reorder the child control array for tab controls. Implemented as a special case + // in order to keep the method private. + if (!LocalAppContextSwitches.AllowUpdateChildControlIndexForTabControls) { + if (this.GetType().IsAssignableFrom(typeof(TabControl))) { + return; + } + } + + int newIndex = 0; + int curIndex = this.Controls.GetChildIndex(ctl); + IntPtr hWnd = ctl.InternalHandle; + while ((hWnd = UnsafeNativeMethods.GetWindow(new HandleRef(null, hWnd), NativeMethods.GW_HWNDPREV)) != IntPtr.Zero) { + Control c = FromHandleInternal(hWnd); + if (c != null) { + newIndex = this.Controls.GetChildIndex(c, false) + 1; + break; + } + } + if (newIndex > curIndex) { + newIndex--; + } + if (newIndex != curIndex) + { + this.Controls.SetChildIndex(ctl, newIndex); + } + } + + + // whenever we create our handle, we need to get ahold of the HWND of our parent, + // and track if that thing is destroyed, because any messages sent to our parent (e.g. WM_NOTIFY, WM_DRAWITEM, etc) + // will continue to be sent to that window even after we do a SetParent call until the handle is recreated. + // So here we keep track of that, and if that window ever gets destroyed, we'll recreate our handle. + // + // + // Scenario is when you've got a control in one parent, you move it to another, then destroy the first parent. It'll stop + // getting any reflected messages because Windows will send them to the original parent. + // + private void UpdateReflectParent(bool findNewParent) { + if (!Disposing && findNewParent && IsHandleCreated) { + IntPtr parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(this, Handle)); + if (parentHandle != IntPtr.Zero) { + ReflectParent = Control.FromHandleInternal(parentHandle); + return; + } + } + ReflectParent = null; + } + + /// + /// + /// Updates this control in it's parent's zorder. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateZOrder() { + if (parent != null) { + parent.UpdateChildZOrder(this); + } + } + + /// + /// Syncs the ZOrder of child control to the index we want it to be. + /// + private void UpdateChildZOrder(Control ctl) { + if (!IsHandleCreated || !ctl.IsHandleCreated || ctl.parent != this) return; + IntPtr prevHandle = (IntPtr)NativeMethods.HWND_TOP; + for (int i = this.Controls.GetChildIndex(ctl); --i >= 0;) { + Control c = Controls[i]; + if (c.IsHandleCreated && c.parent == this) { + prevHandle = c.Handle; + break; + } + } + if (UnsafeNativeMethods.GetWindow(new HandleRef(ctl.window, ctl.Handle), NativeMethods.GW_HWNDPREV) != prevHandle) { + state |= STATE_NOZORDER; + try { + SafeNativeMethods.SetWindowPos(new HandleRef(ctl.window, ctl.Handle), new HandleRef(null, prevHandle), 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + finally { + state &= ~STATE_NOZORDER; + } + } + } + + /// + /// Updates the rootReference in the bound window. + /// (Used to prevent visible top-level controls from being garbage collected) + /// + private void UpdateRoot() { + window.LockReference(GetTopLevel() && Visible); + } + + /// + /// + /// Forces styles to be reapplied to the handle. This function will call + /// CreateParams to get the styles to apply. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected void UpdateStyles() { + UpdateStylesCore(); + + OnStyleChanged(EventArgs.Empty); + } + + internal virtual void UpdateStylesCore() { + if (IsHandleCreated) { + CreateParams cp = CreateParams; + int winStyle = WindowStyle; + int exStyle = WindowExStyle; + + // resolve the Form's lazy visibility. + if ((state & STATE_VISIBLE) != 0){ + cp.Style |= NativeMethods.WS_VISIBLE; + } + if (winStyle != cp.Style) { + WindowStyle = cp.Style; + } + if (exStyle != cp.ExStyle) { + WindowExStyle = cp.ExStyle; + SetState(STATE_MIRRORED, (cp.ExStyle & NativeMethods.WS_EX_LAYOUTRTL) != 0); + } + + SafeNativeMethods.SetWindowPos( + new HandleRef(this, Handle), NativeMethods.NullHandleRef, 0, 0, 0, 0, + NativeMethods.SWP_DRAWFRAME | NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOMOVE + | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER); + + Invalidate(true); + } + } + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + if (pref.Category == UserPreferenceCategory.Color) { + defaultFont = null; + OnSystemColorsChanged(EventArgs.Empty); + } + } + + // + // Give a chance for derived controls to do what they want, just before we resize. + internal virtual void OnBoundsUpdate(int x, int y, int width, int height) + { + } + + // These Window* methods allow us to keep access to the "window" + // property private, which is important for restricting access to the + // handle. + internal void WindowAssignHandle(IntPtr handle, bool value) + { + window.AssignHandle(handle, value); + } + + internal void WindowReleaseHandle() + { + window.ReleaseHandle(); + } + + private void WmClose(ref Message m) { + + // More generic fix for KB article Q125644... + // + if (ParentInternal != null) { + IntPtr parentHandle = Handle; + IntPtr lastParentHandle = parentHandle; + + while (parentHandle != IntPtr.Zero) { + lastParentHandle = parentHandle; + parentHandle = UnsafeNativeMethods.GetParent(new HandleRef(null, parentHandle)); + + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(null, lastParentHandle), NativeMethods.GWL_STYLE))); + + if ((style & NativeMethods.WS_CHILD) == 0) { + break; + } + + } + + if (lastParentHandle != IntPtr.Zero) { + UnsafeNativeMethods.PostMessage(new HandleRef(null, lastParentHandle), NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + } + } + + DefWndProc(ref m); + } + + + /// + /// Handles the WM_CAPTURECHANGED message + /// + /// + private void WmCaptureChanged(ref Message m) { + OnMouseCaptureChanged(EventArgs.Empty); + DefWndProc(ref m); + + } + /// + /// Handles the WM_COMMAND message + /// + /// + private void WmCommand(ref Message m) { + if (IntPtr.Zero == m.LParam) { + if (Command.DispatchID(NativeMethods.Util.LOWORD(m.WParam))) return; + } + else { + if (ReflectMessageInternal(m.LParam, ref m)) { + return; + } + } + DefWndProc(ref m); + } + + // overridable so nested controls can provide a different source control. + internal virtual void WmContextMenu(ref Message m) { + WmContextMenu(ref m, this); + } + /// + /// Handles the WM_CONTEXTMENU message + /// + /// + internal void WmContextMenu(ref Message m, Control sourceControl) { + ContextMenu contextMenu = Properties.GetObject(PropContextMenu) as ContextMenu; + ContextMenuStrip contextMenuStrip = (contextMenu != null) ? null /*save ourselves a property fetch*/ + : Properties.GetObject(PropContextMenuStrip) as ContextMenuStrip; + + if (contextMenu != null || contextMenuStrip != null) { + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point client; + bool keyboardActivated = false; + // lparam will be exactly -1 when the user invokes the context menu + // with the keyboard. + // + if (unchecked((int)(long)m.LParam) == -1) { + keyboardActivated = true; + client = new Point(Width/2, Height/2); + } + else { + client = PointToClientInternal(new Point(x, y)); + } + + // + + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (ClientRectangle.Contains( client )) { + if (contextMenu != null) { + contextMenu.Show(sourceControl, client); + } + else if (contextMenuStrip != null) { + contextMenuStrip.ShowInternal(sourceControl, client, keyboardActivated); + } + else { + Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?"); + DefWndProc( ref m ); + } + } + else { + DefWndProc( ref m ); + } + } + else { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_CTLCOLOR message + /// + /// + private void WmCtlColorControl(ref Message m) { + // We could simply reflect the message, but it's faster to handle it here if possible. + Control control = Control.FromHandleInternal(m.LParam); + if (control != null) { + m.Result = control.InitializeDCForWmCtlColor(m.WParam, m.Msg); + if (m.Result != IntPtr.Zero) { + return; + } + } + + DefWndProc(ref m); + } + + private void WmDisplayChange(ref Message m) { + BufferedGraphicsManager.Current.Invalidate(); + DefWndProc(ref m); + } + + /// + /// WM_DRAWITEM handler + /// + /// + private void WmDrawItem(ref Message m) { + + // If the wparam is zero, then the message was sent by a menu. + // See WM_DRAWITEM in MSDN. + if (m.WParam == IntPtr.Zero) { + WmDrawItemMenuItem(ref m); + } + else { + WmOwnerDraw(ref m); + } + } + + private void WmDrawItemMenuItem(ref Message m) { + // Obtain the menu item object + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + + // A pointer to the correct MenuItem is stored in the draw item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(dis.itemData); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmDrawItem(ref m); + } + } + + /// + /// Handles the WM_ERASEBKGND message + /// + /// + private void WmEraseBkgnd(ref Message m) { + if (GetStyle(ControlStyles.UserPaint)) { + // When possible, it's best to do all painting directly from WM_PAINT. + // OptimizedDoubleBuffer is the "same" as turning on AllPaintingInWMPaint + if (!(GetStyle(ControlStyles.AllPaintingInWmPaint))) { + IntPtr dc = m.WParam; + if (dc == IntPtr.Zero) { // This happens under extreme stress conditions + m.Result = (IntPtr)0; + return; + } + NativeMethods.RECT rc = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rc); + using (PaintEventArgs pevent = new PaintEventArgs(dc, Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom))) { + PaintWithErrorHandling(pevent, PaintLayerBackground); + } + } + m.Result = (IntPtr)1; + } + else { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_EXITMENULOOP message. If this control has a context menu, its + /// Collapse event is raised. + /// + /// + private void WmExitMenuLoop(ref Message m) { + bool isContextMenu = (unchecked((int)(long)m.WParam) == 0) ? false : true; + + if (isContextMenu) { + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + contextMenu.OnCollapse(EventArgs.Empty); + } + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_GETCONTROLNAME message. Returns the name of the control. + /// + /// + private void WmGetControlName(ref Message m) { + string name; + + if (this.Site != null) { + name = this.Site.Name; + } + else { + name = this.Name; + } + + if (name == null) + name = ""; + + + MarshalStringToMessage(name, ref m); + } + + /// + /// Handles the WM_GETCONTROLTYPE message. Returns the name of the control. + /// + /// + private void WmGetControlType(ref Message m) { + string type = GetType().AssemblyQualifiedName; + MarshalStringToMessage(type, ref m); + } + + /// + /// Handles the WM_GETOBJECT message. Used for accessibility. + /// + /// + private void WmGetObject(ref Message m) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "In WmGetObject, this = " + this.GetType().FullName + ", lParam = " + m.LParam.ToString()); + + InternalAccessibleObject intAccessibleObject = null; + + if (m.Msg == NativeMethods.WM_GETOBJECT && m.LParam == (IntPtr)NativeMethods.UiaRootObjectId && this.SupportsUiaProviders) { + // If the requested object identifier is UiaRootObjectId, + // we should return an UI Automation provider using the UiaReturnRawElementProvider function. + IntSecurity.UnmanagedCode.Assert(); + try { + intAccessibleObject = new InternalAccessibleObject(this.AccessibilityObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + m.Result = UnsafeNativeMethods.UiaReturnRawElementProvider( + new HandleRef(this, Handle), + m.WParam, + m.LParam, + intAccessibleObject); + + return; + } + + AccessibleObject ctrlAccessibleObject = GetAccessibilityObject(unchecked((int)(long)m.LParam)); + + if (ctrlAccessibleObject != null) { + // SECREVIEW : Asserting here is safe, the interface is used to get the LRESULT from the accessible object + // into the Message.Result. + // + IntSecurity.UnmanagedCode.Assert(); + try { + intAccessibleObject = new InternalAccessibleObject(ctrlAccessibleObject); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + // See "How to Handle WM_GETOBJECT" in MSDN + if (intAccessibleObject != null) { + + // Get the IAccessible GUID + // + Guid IID_IAccessible = new Guid(NativeMethods.uuid_IAccessible); + + // Get an Lresult for the accessibility Object for this control + // + IntPtr punkAcc; + try { + // Sanity check: For security reasons, it is + + + + object tempObject = intAccessibleObject; + IAccessible iAccCheck = tempObject as IAccessible; + if (iAccCheck != null) { + throw new InvalidOperationException(SR.GetString(SR.ControlAccessibileObjectInvalid)); + } + + // Check that we have an IAccessibleInternal implementation and return this + UnsafeNativeMethods.IAccessibleInternal iacc = (UnsafeNativeMethods.IAccessibleInternal) intAccessibleObject; + + if (iacc == null) { + // Accessibility is not supported on this control + // + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "AccessibilityObject returned null"); + m.Result = (IntPtr)0; + } + else { + // Obtain the Lresult + // + punkAcc = Marshal.GetIUnknownForObject(iacc); + + // SECREVIEW : The following assert is required because the interop call below will call into an interface (punkAcc) + // which demands UnmanagedCode permission. We make sure we are calling the right into the right interface + // (see Sanity-Check comment above) and the call is safe since we are just getting the LRESULT from the + // Acc interface. So this assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + m.Result = UnsafeNativeMethods.LresultFromObject(ref IID_IAccessible, m.WParam, new HandleRef(ctrlAccessibleObject, punkAcc)); + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "LresultFromObject returned " + m.Result.ToString()); + } + finally { + CodeAccessPermission.RevertAssert(); + Marshal.Release(punkAcc); + } + } + } + catch (Exception e) { + throw new InvalidOperationException(SR.GetString(SR.RichControlLresult), e); + } + } + else { // some accessible object requested that we don't care about, so do default message processing + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_HELP message + /// + /// + private void WmHelp(ref Message m) { + + // if there's currently a message box open - grab the help info from it. + HelpInfo hpi = MessageBox.HelpInfo; + if (hpi != null) { + switch (hpi.Option) { + case NativeMethods.HLP_FILE : Help.ShowHelp (this, hpi.HelpFilePath); + break; + case NativeMethods.HLP_KEYWORD : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Keyword); + break; + case NativeMethods.HLP_NAVIGATOR : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Navigator); + break; + case NativeMethods.HLP_OBJECT : Help.ShowHelp (this, hpi.HelpFilePath, hpi.Navigator, hpi.Param); + break; + } + } + + // Note: info.hItemHandle is the handle of the window that sent the help message. + NativeMethods.HELPINFO info = (NativeMethods.HELPINFO)m.GetLParam(typeof(NativeMethods.HELPINFO)); + + HelpEventArgs hevent = new HelpEventArgs(new Point(info.MousePos.x, info.MousePos.y)); + OnHelpRequested(hevent); + if (!hevent.Handled) { + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_INITMENUPOPUP message + /// + /// + private void WmInitMenuPopup(ref Message m) { + ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); + if (contextMenu != null) { + + if (contextMenu.ProcessInitMenuPopup(m.WParam)) + return; + } + DefWndProc(ref m); + } + + /// + /// WM_MEASUREITEM handler + /// + /// + private void WmMeasureItem(ref Message m) { + + // If the wparam is zero, then the message was sent by a menu. + // See WM_MEASUREITEM in MSDN. + if (m.WParam == IntPtr.Zero) { + + // Obtain the menu item object + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + Debug.Assert(m.LParam != IntPtr.Zero, "m.lparam is null"); + + // A pointer to the correct MenuItem is stored in the measure item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(mis.itemData); + Debug.Assert(menuItem != null, "UniqueID is not associated with a menu item"); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmMeasureItem(ref m); + } + } + else { + WmOwnerDraw(ref m); + } + } + + /// + /// Handles the WM_MENUCHAR message + /// + /// + private void WmMenuChar(ref Message m) { + Menu menu = ContextMenu; + if (menu != null) { + menu.WmMenuChar(ref m); + if (m.Result != IntPtr.Zero) { + // This char is a mnemonic on our menu. + return; + } + } + } + + /// + /// Handles the WM_MENUSELECT message + /// + /// + private void WmMenuSelect(ref Message m) { + int item = NativeMethods.Util.LOWORD(m.WParam); + int flags = NativeMethods.Util.HIWORD(m.WParam); + IntPtr hmenu = m.LParam; + MenuItem mi = null; + + if ((flags & NativeMethods.MF_SYSMENU) != 0) { + // nothing + } + else if ((flags & NativeMethods.MF_POPUP) == 0) { + Command cmd = Command.GetCommandFromID(item); + if (cmd != null) { + Object reference = cmd.Target; + if (reference != null && reference is MenuItem.MenuItemData) { + mi = ((MenuItem.MenuItemData)reference).baseItem; + } + } + } + else { + mi = GetMenuItemFromHandleId(hmenu, item); + } + + if (mi != null) { + mi.PerformSelect(); + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_CREATE message + /// + /// + private void WmCreate(ref Message m) { + + DefWndProc(ref m); + + if (parent != null) { + parent.UpdateChildZOrder(this); + } + UpdateBounds(); + + // Let any interested sites know that we've now created a handle + // + OnHandleCreated(EventArgs.Empty); + + // this code is important -- it is critical that we stash away + // the value of the text for controls such as edit, button, + // label, etc. Without this processing, any time you change a + // property that forces handle recreation, you lose your text! + // See the below code in wmDestroy + // + if (!GetStyle(ControlStyles.CacheText)) { + text = null; + } + } + + /// + /// Handles the WM_DESTROY message + /// + /// + private void WmDestroy(ref Message m) { + // Let any interested sites know that we're destroying our handle + // + + // Disabling the assert for M2 VSWhidbey 122139 + //Debug.Assert(IsHandleCreated, "Need to have the handle here"); + + if (!RecreatingHandle && !Disposing && !IsDisposed && GetState(STATE_TRACKINGMOUSEEVENT)) + { + // VSWhidbey 339087. Raise the MouseLeave event for the control below the mouse + // when a modal dialog is discarded. + OnMouseLeave(EventArgs.Empty); + UnhookMouseEvent(); + } + + if (SupportsUiaProviders) + { + ReleaseUiaProvider(Handle); + } + + OnHandleDestroyed(EventArgs.Empty); + + if (!Disposing) { + // If we are not recreating the handle, set our created state + // back to false so we can be rebuilt if we need to be. + // + if (!RecreatingHandle) { + SetState(STATE_CREATED, false); + } + } + else { + SetState(STATE_VISIBLE, false); + } + + DefWndProc(ref m); + } + + /// + /// Handles the WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and + /// WM_SYSKEYUP messages. + /// + /// + private void WmKeyChar(ref Message m) { + if (ProcessKeyMessage(ref m)) return; + DefWndProc(ref m); + } + + /// + /// Handles the WM_KILLFOCUS message + /// + /// + private void WmKillFocus(ref Message m) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::WmKillFocus - " + this.Name); + WmImeKillFocus(); + DefWndProc(ref m); + this.InvokeLostFocus(this, EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSEDOWN message + /// + /// + private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { + // If this is a "real" mouse event (not just WM_LBUTTONDOWN, etc) then + // we need to see if something happens during processing of + // user code that changed the state of the buttons (i.e. bringing up + // a dialog) to keep the control in a consistent state... + // + MouseButtons realState = MouseButtons; + SetState(STATE_MOUSEPRESSED, true); + + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + // we might had re-entered the message loop and processed a WM_CLOSE message + if (IsDisposed) { + return; + } + } + else { + // DefWndProc would normally set the focus to this control, but + // since we're skipping DefWndProc, we need to do it ourselves. + if (button == MouseButtons.Left && GetStyle(ControlStyles.Selectable)) { + FocusInternal(); + } + } + + if (realState != MouseButtons) { + return; + } + + if (!GetState2(STATE2_MAINTAINSOWNCAPTUREMODE)) { + //CaptureInternal is set usually in MouseDown (ToolStrip main exception) + CaptureInternal = true; + } + + if (realState != MouseButtons) { + return; + } + + // control should be enabled when this method is entered, but may have become + // disabled during its lifetime (e.g. through a Click or Focus listener) + if (Enabled) { + OnMouseDown(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + } + + /// + /// Handles the WM_MOUSEENTER message + /// + /// + private void WmMouseEnter(ref Message m) { + DefWndProc(ref m); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutMouseEnter(this); + } + OnMouseEnter(EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSELEAVE message + /// + /// + private void WmMouseLeave(ref Message m) { + DefWndProc(ref m); + OnMouseLeave(EventArgs.Empty); + } + + /// + /// Handles the WM_DPICHANGED_BEFOREPARENT message. This message is not sent to top level windows. + /// + private void WmDpiChangedBeforeParent(ref Message m) { + DefWndProc(ref m); + + if (IsHandleCreated) { + int deviceDpiOld = deviceDpi; + deviceDpi = (int)UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + + // Controls are by default font scaled. + // Dpi change requires font to be recalculated inorder to get controls scaled with right dpi. + if (deviceDpiOld != deviceDpi) { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + // Checking if font was inherited from parent. Font inherited from parent will receive OnParentFontChanged() events to scale those controls. + Font local = (Font)Properties.GetObject(PropFont); + if (local != null) { + var factor = (float)deviceDpi / deviceDpiOld; + this.Font = new Font(local.FontFamily, local.Size * factor, local.Style, local.Unit, local.GdiCharSet, local.GdiVerticalFont); + } + } + + RescaleConstantsForDpi(deviceDpiOld, deviceDpi); + } + } + + OnDpiChangedBeforeParent(EventArgs.Empty); + } + + /// + /// Handles the WM_DPICHANGED_AFTERPARENT message + /// + private void WmDpiChangedAfterParent(ref Message m) { + DefWndProc(ref m); + + uint dpi = UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + + OnDpiChangedAfterParent(EventArgs.Empty); + } + + /// + /// Handles the "WM_MOUSEHOVER" message... until we get actuall OS support + /// for this, it is implemented as a custom message. + /// + /// + private void WmMouseHover(ref Message m) { + DefWndProc(ref m); + OnMouseHover(EventArgs.Empty); + } + + /// + /// Handles the WM_MOUSEMOVE message + /// + /// + private void WmMouseMove(ref Message m) { + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + } + OnMouseMove(new MouseEventArgs(MouseButtons, 0, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + + /// + /// Handles the WM_MOUSEUP message + /// + /// + private void WmMouseUp(ref Message m, MouseButtons button, int clicks) { + // Get the mouse location + // + try { + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x,y); + pt = PointToScreen(pt); + + // If the UserMouse style is set, the control does its own processing + // of mouse messages + // + if (!GetStyle(ControlStyles.UserMouse)) { + DefWndProc(ref m); + } + else { + // DefWndProc would normally trigger a context menu here + // (for a right button click), but since we're skipping DefWndProc + // we have to do it ourselves. + if (button == MouseButtons.Right) { + SendMessage(NativeMethods.WM_CONTEXTMENU, this.Handle, NativeMethods.Util.MAKELPARAM(pt.X, pt.Y)); + } + } + + bool fireClick = false; + + if ((controlStyle & ControlStyles.StandardClick) == ControlStyles.StandardClick) { + if (GetState(STATE_MOUSEPRESSED) && !IsDisposed && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + fireClick = true; + } + } + + if (fireClick && !ValidationCancelled) { + if (!GetState(STATE_DOUBLECLICKFIRED)) { + //OnClick(EventArgs.Empty); + //In Whidbey .. if the click in by MOUSE then pass the MouseEventArgs... + OnClick(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + + else { + //OnDoubleClick(EventArgs.Empty); + OnDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + + } + //call the MouseUp Finally... + OnMouseUp(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + finally { + //Always Reset the STATE_DOUBLECLICKFIRED in UP.. Since we get UP - DOWN - DBLCLK - UP sequqnce + //The Flag is set in L_BUTTONDBLCLK in the controls WndProc() ... + // + SetState(STATE_DOUBLECLICKFIRED, false); + SetState(STATE_MOUSEPRESSED, false); + SetState(STATE_VALIDATIONCANCELLED, false); + //CaptureInternal is Resetted while exiting the MouseUp + CaptureInternal = false; + } + } + + /// + /// Handles the WM_MOUSEWHEEL message + /// + /// + private void WmMouseWheel(ref Message m) { + Point p = new Point(NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam)); + p = PointToClient(p); + HandledMouseEventArgs e = new HandledMouseEventArgs(MouseButtons.None, + 0, + p.X, + p.Y, + NativeMethods.Util.SignedHIWORD(m.WParam)); + OnMouseWheel(e); + m.Result = (IntPtr) (e.Handled ? 0 : 1); + if (!e.Handled) + { + // Forwarding the message to the parent window + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_MOVE message. We must do this in + /// addition to WM_WINDOWPOSCHANGED because windows may + /// send WM_MOVE directly. + /// + /// + private void WmMove(ref Message m) { + DefWndProc(ref m); + UpdateBounds(); + } + + /// + /// Handles the WM_NOTIFY message + /// + /// + private unsafe void WmNotify(ref Message m) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + if (!ReflectMessageInternal(nmhdr->hwndFrom,ref m)) { + if(nmhdr->code == NativeMethods.TTN_SHOW) { + m.Result = UnsafeNativeMethods.SendMessage(new HandleRef(null, nmhdr->hwndFrom), NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + return; + } + if(nmhdr->code == NativeMethods.TTN_POP) { + UnsafeNativeMethods.SendMessage(new HandleRef(null, nmhdr->hwndFrom), NativeMethods.WM_REFLECT + m.Msg, m.WParam, m.LParam); + } + + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_NOTIFYFORMAT message + /// + /// + private void WmNotifyFormat(ref Message m) { + if (!ReflectMessageInternal(m.WParam, ref m)) { + DefWndProc(ref m); + } + } + + + /// + /// Handles the WM_DRAWITEM\WM_MEASUREITEM messages for controls other than menus + /// + /// + private void WmOwnerDraw(ref Message m) { + bool reflectCalled = false; + + int ctrlId = unchecked((int)(long)m.WParam); + IntPtr p = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, m.HWnd), ctrlId); + if (p == IntPtr.Zero) { + // On 64-bit platforms wParam is already 64 bit but the control ID stored in it is only 32-bit + // Empirically, we have observed that the 64 bit HWND is just a sign extension of the 32-bit ctrl ID + // Since WParam is already 64-bit, we need to discard the high dword first and then re-extend the 32-bit value + // treating it as signed + p = (IntPtr)(long)ctrlId; + } + if (!ReflectMessageInternal(p, ref m)) { + //Additional Check For Control .... TabControl truncates the Hwnd value... + IntPtr handle = window.GetHandleFromID((short)NativeMethods.Util.LOWORD(m.WParam)); + if (handle != IntPtr.Zero) { + Control control = Control.FromHandleInternal(handle); + if (control != null) { + m.Result = control.SendMessage(NativeMethods.WM_REFLECT + m.Msg, handle, m.LParam); + reflectCalled = true; + } + } + } + else { + reflectCalled = true; + } + + + if (!reflectCalled) { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_PAINT messages. This should only be called + /// for userpaint controls. + /// + /// + private void WmPaint(ref Message m) { + bool doubleBuffered = DoubleBuffered || (GetStyle(ControlStyles.AllPaintingInWmPaint) && DoubleBufferingEnabled); +#if DEBUG + if (BufferDisabled.Enabled) { + doubleBuffered = false; + } +#endif + IntPtr hWnd = IntPtr.Zero; + IntPtr dc; + Rectangle clip; + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + bool needDisposeDC = false; + + try { + if (m.WParam == IntPtr.Zero) { + // Cache Handle not only for perf but to avoid object disposed exception in case the window + // is destroyed in an event handler (VSW#261657). + hWnd = this.Handle; + dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, hWnd), ref ps); + if (dc == IntPtr.Zero) { + return; + } + needDisposeDC = true; + clip = new Rectangle(ps.rcPaint_left, ps.rcPaint_top, + ps.rcPaint_right - ps.rcPaint_left, + ps.rcPaint_bottom - ps.rcPaint_top); + } + else { + dc = m.WParam; + clip = ClientRectangle; + } + + // Consider: Why don't check the clip condition when non-doubleBuffered? + // we should probably get rid of the !doubleBuffered condition. + if (!doubleBuffered || (clip.Width > 0 && clip.Height > 0)) { + IntPtr oldPal = IntPtr.Zero; + BufferedGraphics bufferedGraphics = null; + PaintEventArgs pevent = null; + GraphicsState state = null; + + try { + if (doubleBuffered || m.WParam == IntPtr.Zero) { + oldPal = SetUpPalette(dc, false, false); + } + + if (doubleBuffered) { + try { + bufferedGraphics = BufferContext.Allocate(dc, ClientRectangle); +#if DEBUG + if (BufferPinkRect.Enabled) { + Rectangle band = ClientRectangle; + using( BufferedGraphics bufferedGraphics2 = BufferContext.Allocate( dc, band ) ) { + bufferedGraphics2.Graphics.FillRectangle( new SolidBrush( Color.Red ), band ); + bufferedGraphics2.Render(); + } + Thread.Sleep(50); + } +#endif + } + catch (Exception ex) { + // BufferContext.Allocate will throw out of memory exceptions + // when it fails to create a device dependent bitmap while trying to + // get information about the device we are painting on. + // That is not the same as a system running out of memory and there is a + // very good chance that we can continue to paint successfully. We cannot + // check whether double buffering is supported in this case, and we will disable it. + // We could set a specific string when throwing the exception and check for it here + // to distinguish between that case and real out of memory exceptions but we + // see no reasons justifying the additional complexity. + if (ClientUtils.IsCriticalException(ex) && !(ex is OutOfMemoryException)) { + throw; + } +#if DEBUG + if( BufferPinkRect.Enabled ) { + Debug.WriteLine("Could not create buffered graphics, will paint in the surface directly" ); + } +#endif + doubleBuffered = false; // paint directly on the window DC. + } + } + + if (bufferedGraphics != null) { + bufferedGraphics.Graphics.SetClip(clip); + pevent = new PaintEventArgs(bufferedGraphics.Graphics, clip); + state = pevent.Graphics.Save(); + } + else { + pevent = new PaintEventArgs(dc, clip); + } + + using (pevent) { + try { + if ((m.WParam == IntPtr.Zero) && GetStyle(ControlStyles.AllPaintingInWmPaint) || doubleBuffered) { + PaintWithErrorHandling(pevent, PaintLayerBackground); + // Consider: This condition could be elimiated, + // do we have to save/restore the state of the buffered graphics? + + } + } + finally { + if (state != null) { + pevent.Graphics.Restore(state); + } + else { + pevent.ResetGraphics(); + } + } + PaintWithErrorHandling(pevent, PaintLayerForeground); + + if (bufferedGraphics != null) { + bufferedGraphics.Render(); + } + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); + } + if (bufferedGraphics != null) { + bufferedGraphics.Dispose(); + } + + } + } + } + finally { + if (needDisposeDC) { + UnsafeNativeMethods.EndPaint(new HandleRef(this, hWnd), ref ps); + } + } + } + + /// + /// Handles the WM_PRINTCLIENT messages. + /// + /// + private void WmPrintClient(ref Message m) { + using (PaintEventArgs e = new PrintPaintEventArgs(m, m.WParam, ClientRectangle)) { + OnPrint(e); + } + } + + private void WmQueryNewPalette(ref Message m) { + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, Handle + ": WM_QUERYNEWPALETTE"); + IntPtr dc = UnsafeNativeMethods.GetDC(new HandleRef(this, Handle)); + try { + SetUpPalette(dc, true /*force*/, true/*realize*/); + } + finally { + // Let WmPaletteChanged do any necessary invalidation + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, Handle), new HandleRef(null, dc)); + } + Invalidate(true); + m.Result = (IntPtr)1; + DefWndProc(ref m); + } + + + /// + /// Handles the WM_SETCURSOR message + /// + /// + private void WmSetCursor(ref Message m) { + + // Accessing through the Handle property has side effects that break this + // logic. You must use InternalHandle. + // + if (m.WParam == InternalHandle && NativeMethods.Util.LOWORD(m.LParam) == NativeMethods.HTCLIENT) { + Cursor.CurrentInternal = Cursor; + } + else { + DefWndProc(ref m); + } + + } + + /// + /// Handles the WM_WINDOWPOSCHANGING message + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + + // We let this fall through to defwndproc unless we are being surfaced as + // an ActiveX control. In that case, we must let the ActiveX side of things + // manipulate our bounds here. + // + if (IsActiveX) { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + // Only call UpdateBounds if the new bounds are different. + // + bool different = false; + + if ((wp->flags & NativeMethods.SWP_NOMOVE) == 0 && (wp->x != Left || wp->y != Top)) { + different = true; + } + if ((wp->flags & NativeMethods.SWP_NOSIZE) == 0 && (wp->cx != Width || wp->cy != Height)) { + different = true; + } + + if (different) { + ActiveXUpdateBounds(ref wp->x, ref wp->y, ref wp->cx, ref wp->cy, wp->flags); + } + } + + DefWndProc(ref m); + } + + + /// + /// Handles the WM_PARENTNOTIFY message + /// + /// + private void WmParentNotify(ref Message m) { + int msg = NativeMethods.Util.LOWORD(m.WParam); + IntPtr hWnd = IntPtr.Zero; + switch (msg) { + case NativeMethods.WM_CREATE: + hWnd = m.LParam; + break; + case NativeMethods.WM_DESTROY: + break; + default: + hWnd = UnsafeNativeMethods.GetDlgItem(new HandleRef(this, Handle), NativeMethods.Util.HIWORD(m.WParam)); + break; + } + if (hWnd == IntPtr.Zero || !ReflectMessageInternal(hWnd, ref m)) { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_SETFOCUS message + /// + /// + private void WmSetFocus(ref Message m) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::WmSetFocus - " + this.Name); + WmImeSetFocus(); + + if (!HostedInWin32DialogManager) { + IContainerControl c = GetContainerControlInternal(); + if (c != null) { + bool activateSucceed; + + ContainerControl knowncontainer = c as ContainerControl; + if (knowncontainer != null) { + activateSucceed = knowncontainer.ActivateControlInternal(this); + } + else { + // Reviewed : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + activateSucceed = c.ActivateControl(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + if (!activateSucceed) { + return; + } + } + } + + DefWndProc(ref m); + this.InvokeGotFocus(this, EventArgs.Empty); + } + + /// + /// Handles the WM_SHOWWINDOW message + /// + /// + private void WmShowWindow(ref Message m) { + // We get this message for each control, even if their parent is not visible. + + DefWndProc(ref m); + + if ((state & STATE_RECREATE) == 0) { + + bool visible = m.WParam != IntPtr.Zero; + bool oldVisibleProperty = Visible; + + if (visible) { + bool oldVisibleBit = GetState(STATE_VISIBLE); + SetState(STATE_VISIBLE, true); + bool executedOk = false; + try { + CreateControl(); + executedOk = true; + } + + finally { + if (!executedOk) { + // We do it this way instead of a try/catch because catching and rethrowing + // an exception loses call stack information + SetState(STATE_VISIBLE, oldVisibleBit); + } + } + } + else { // not visible + // If Windows tells us it's visible, that's pretty unambiguous. + // But if it tells us it's not visible, there's more than one explanation -- + // maybe the container control became invisible. So we look at the parent + // and take a guess at the reason. + + // We do not want to update state if we are on the parking window. + bool parentVisible = GetTopLevel(); + if (ParentInternal != null) { + parentVisible = ParentInternal.Visible; + } + + if (parentVisible) { + SetState(STATE_VISIBLE, false); + } + } + + if (!GetState(STATE_PARENTRECREATING) && (oldVisibleProperty != visible)) { + OnVisibleChanged(EventArgs.Empty); + } + } + } + + /// + /// Handles the WM_UPDATEUISTATE message + /// + /// + private void WmUpdateUIState(ref Message m) { + + // See "How this all works" in ShowKeyboardCues + + bool keyboard = false; + bool focus = false; + + // check the cached values in uiCuesState to see if we've ever set in the UI state + bool keyboardInitialized = (uiCuesState & UISTATE_KEYBOARD_CUES_MASK) != 0; + bool focusInitialized = (uiCuesState & UISTATE_FOCUS_CUES_MASK) != 0; + + if (keyboardInitialized) { + keyboard = ShowKeyboardCues; + } + + if (focusInitialized) { + focus = ShowFocusCues; + } + + DefWndProc(ref m); + + int cmd = NativeMethods.Util.LOWORD(m.WParam); + + // if we're initializing, dont bother updating the uiCuesState/Firing the event. + + if (cmd == NativeMethods.UIS_INITIALIZE) { + return; + } + + // Set in the cached value for uiCuesState... + + // Windows stores the opposite of what you would think, it has bit + // flags for the "Hidden" state, the presence of this flag means its + // hidden, the absence thereof means it's shown. + // + // When we're called here with a UIS_CLEAR and the hidden state is set + // that means we want to show the accelerator. + UICues UIcues = UICues.None; + if ((NativeMethods.Util.HIWORD(m.WParam) & NativeMethods.UISF_HIDEACCEL) != 0) { + + // yes, clear means show. nice api, guys. + // + bool showKeyboard = (cmd == NativeMethods.UIS_CLEAR); + + if (showKeyboard != keyboard || !keyboardInitialized ) { + + UIcues |= UICues.ChangeKeyboard; + + // clear the old state. + // + uiCuesState &= ~UISTATE_KEYBOARD_CUES_MASK; + uiCuesState |= (showKeyboard ? UISTATE_KEYBOARD_CUES_SHOW : UISTATE_KEYBOARD_CUES_HIDDEN) ; + } + + if (showKeyboard) { + UIcues |= UICues.ShowKeyboard; + } + } + + // Same deal for the Focus cues as the keyboard cues. + if ((NativeMethods.Util.HIWORD(m.WParam) & NativeMethods.UISF_HIDEFOCUS) != 0) { + + // yes, clear means show. nice api, guys. + // + bool showFocus = (cmd == NativeMethods.UIS_CLEAR); + + if (showFocus != focus || !focusInitialized) { + UIcues |= UICues.ChangeFocus; + + // clear the old state. + // + uiCuesState &= ~UISTATE_FOCUS_CUES_MASK; + uiCuesState |= (showFocus ? UISTATE_FOCUS_CUES_SHOW : UISTATE_FOCUS_CUES_HIDDEN) ; + } + + if (showFocus) { + UIcues |= UICues.ShowFocus; + } + } + + + // fire the UI cues state changed event. + if ((UIcues & UICues.Changed) != 0) { + OnChangeUICues(new UICuesEventArgs(UIcues)); + Invalidate(true); + } + } + + /// + /// Handles the WM_WINDOWPOSCHANGED message + /// + /// + private unsafe void WmWindowPosChanged(ref Message m) { + DefWndProc(ref m); + // Update new size / position + UpdateBounds(); + if (parent != null && UnsafeNativeMethods.GetParent(new HandleRef(window, InternalHandle)) == parent.InternalHandle && + (state & STATE_NOZORDER) == 0) { + + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + if ((wp->flags & NativeMethods.SWP_NOZORDER) == 0) { + parent.UpdateChildControlIndex(this); + } + } + } + + /// + /// + /// Base wndProc. All messages are sent to wndProc after getting filtered + /// through the preProcessMessage function. Inheriting controls should + /// call base.wndProc for any messages that they don't handle. + /// + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual void WndProc(ref Message m) { + // + // + + + + + /* + if( GetState(STATE_DISPOSED)) + { + Debug.Fail("Attempting to process a windows message in a disposed control. This may be OK if the app domain is being unloaded."); + DefWndProc(ref m); + return; + } + */ + + // inlined code from GetStyle(...) to ensure no perf hit + // for a method call... + // + if ((controlStyle & ControlStyles.EnableNotifyMessage) == ControlStyles.EnableNotifyMessage) { + // pass message *by value* to avoid the possibility + // of the OnNotifyMessage modifying the message. + // + OnNotifyMessage(m); + } + + /* + * If you add any new messages below (or change the message handling code for any messages) + * please make sure that you also modify AxHost.wndProc to do the right thing and intercept + * messages which the Ocx would own before passing them onto Control.wndProc. + */ + switch (m.Msg) { + case NativeMethods.WM_CAPTURECHANGED: + WmCaptureChanged(ref m); + break; + + case NativeMethods.WM_GETOBJECT: + WmGetObject(ref m); + break; + + case NativeMethods.WM_COMMAND: + WmCommand(ref m); + break; + case NativeMethods.WM_CLOSE: + WmClose(ref m); + break; + case NativeMethods.WM_CONTEXTMENU: + WmContextMenu(ref m); + break; + case NativeMethods.WM_DISPLAYCHANGE: + WmDisplayChange(ref m); + break; + case NativeMethods.WM_DRAWITEM: + WmDrawItem(ref m); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + + case NativeMethods.WM_EXITMENULOOP: + WmExitMenuLoop(ref m); + break; + + case NativeMethods.WM_HELP: + WmHelp(ref m); + break; + + case NativeMethods.WM_PAINT: + if (GetStyle(ControlStyles.UserPaint)) { + WmPaint(ref m); + } + else { + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_PRINTCLIENT: + if (GetStyle(ControlStyles.UserPaint)) { + WmPrintClient(ref m); + } + else { + DefWndProc(ref m); + } + break; + + case NativeMethods.WM_INITMENUPOPUP: + WmInitMenuPopup(ref m); + break; + + case NativeMethods.WM_SYSCOMMAND: + if ((unchecked((int)(long)m.WParam) & 0xFFF0) == NativeMethods.SC_KEYMENU) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.WndProc processing " + m.ToString()); + + if (ToolStripManager.ProcessMenuKey(ref m)) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.WndProc ToolStripManager.ProcessMenuKey returned true" + m.ToString()); + m.Result = IntPtr.Zero; + return; + } + } + DefWndProc(ref m); + break; + case NativeMethods.WM_INPUTLANGCHANGE: + WmInputLangChange(ref m); + break; + + case NativeMethods.WM_INPUTLANGCHANGEREQUEST: + WmInputLangChangeRequest(ref m); + break; + + case NativeMethods.WM_MEASUREITEM: + WmMeasureItem(ref m); + break; + case NativeMethods.WM_MENUCHAR: + WmMenuChar(ref m); + break; + + case NativeMethods.WM_MENUSELECT: + WmMenuSelect(ref m); + break; + + case NativeMethods.WM_SETCURSOR: + WmSetCursor(ref m); + break; + + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref m); + break; + + case NativeMethods.WM_CHAR: + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + case NativeMethods.WM_KEYUP: + case NativeMethods.WM_SYSKEYUP: + WmKeyChar(ref m); + break; + case NativeMethods.WM_CREATE: + WmCreate(ref m); + break; + case NativeMethods.WM_DESTROY: + WmDestroy(ref m); + break; + + + case NativeMethods.WM_CTLCOLOR: + case NativeMethods.WM_CTLCOLORBTN: + case NativeMethods.WM_CTLCOLORDLG: + case NativeMethods.WM_CTLCOLORMSGBOX: + case NativeMethods.WM_CTLCOLORSCROLLBAR: + case NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_CTLCOLORLISTBOX: + case NativeMethods.WM_CTLCOLORSTATIC: + + // VSWhidbey 165168 -- this is for the trinity guys. The case is if you've got a windows + // forms edit or something hosted as an AX control somewhere, there isn't anyone to reflect + // these back. If they went ahead and just sent them back, some controls don't like that + // and end up recursing. Our code handles it fine because we just pick the HWND out of the LPARAM. + // + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLOR: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORBTN: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORDLG: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORMSGBOX: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORSCROLLBAR: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLOREDIT: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORLISTBOX: + case NativeMethods.WM_REFLECT + NativeMethods.WM_CTLCOLORSTATIC: + WmCtlColorControl(ref m); + break; + case NativeMethods.WM_HSCROLL: + case NativeMethods.WM_VSCROLL: + case NativeMethods.WM_DELETEITEM: + case NativeMethods.WM_VKEYTOITEM: + case NativeMethods.WM_CHARTOITEM: + case NativeMethods.WM_COMPAREITEM: + if (!ReflectMessageInternal(m.LParam, ref m)) { + DefWndProc(ref m); + } + break; + case NativeMethods.WM_IME_CHAR: + WmImeChar(ref m); + break; + case NativeMethods.WM_IME_STARTCOMPOSITION: + WmImeStartComposition(ref m); + break; + case NativeMethods.WM_IME_ENDCOMPOSITION: + WmImeEndComposition(ref m); + break; + case NativeMethods.WM_IME_NOTIFY: + WmImeNotify(ref m); + break; + case NativeMethods.WM_KILLFOCUS: + WmKillFocus(ref m); + break; + case NativeMethods.WM_LBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Left, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_LBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Left, 1); + break; + case NativeMethods.WM_LBUTTONUP: + WmMouseUp(ref m, MouseButtons.Left, 1); + break; + case NativeMethods.WM_MBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Middle, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_MBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Middle, 1); + break; + case NativeMethods.WM_MBUTTONUP: + WmMouseUp(ref m, MouseButtons.Middle, 1); + break; + case NativeMethods.WM_XBUTTONDOWN: + WmMouseDown(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 1); + break; + case NativeMethods.WM_XBUTTONUP: + WmMouseUp(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 1); + break; + case NativeMethods.WM_XBUTTONDBLCLK: + WmMouseDown(ref m, GetXButton(NativeMethods.Util.HIWORD(m.WParam)), 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_MOUSELEAVE: + WmMouseLeave(ref m); + break; + case NativeMethods.WM_DPICHANGED_BEFOREPARENT: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChangedBeforeParent(ref m); + m.Result = IntPtr.Zero; + } + break; + case NativeMethods.WM_DPICHANGED_AFTERPARENT: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChangedAfterParent(ref m); + m.Result = IntPtr.Zero; + } + break; + case NativeMethods.WM_MOUSEMOVE: + WmMouseMove(ref m); + break; + case NativeMethods.WM_MOUSEWHEEL: + WmMouseWheel(ref m); + break; + case NativeMethods.WM_MOVE: + WmMove(ref m); + break; + case NativeMethods.WM_NOTIFY: + WmNotify(ref m); + break; + case NativeMethods.WM_NOTIFYFORMAT: + WmNotifyFormat(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFYFORMAT: + m.Result = (IntPtr)(Marshal.SystemDefaultCharSize == 1 ? NativeMethods.NFR_ANSI : NativeMethods.NFR_UNICODE); + break; + case NativeMethods.WM_SHOWWINDOW: + WmShowWindow(ref m); + break; + case NativeMethods.WM_RBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Right, 2); + if (GetStyle(ControlStyles.StandardDoubleClick)) { + SetState(STATE_DOUBLECLICKFIRED, true); + } + break; + case NativeMethods.WM_RBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Right, 1); + break; + case NativeMethods.WM_RBUTTONUP: + WmMouseUp(ref m, MouseButtons.Right, 1); + break; + case NativeMethods.WM_SETFOCUS: + WmSetFocus(ref m); + break; + case NativeMethods.WM_MOUSEHOVER: + WmMouseHover(ref m); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + WmWindowPosChanged(ref m); + break; + case NativeMethods.WM_QUERYNEWPALETTE: + WmQueryNewPalette(ref m); + break; + case NativeMethods.WM_UPDATEUISTATE: + WmUpdateUIState(ref m); + break; + case NativeMethods.WM_PARENTNOTIFY: + WmParentNotify(ref m); + break; + default: + // If we received a thread execute message, then execute it. + // + if (m.Msg == threadCallbackMessage && m.Msg != 0) { + InvokeMarshaledCallbacks(); + return; + } + else if (m.Msg == Control.WM_GETCONTROLNAME) { + WmGetControlName(ref m); + return; + } + else if (m.Msg == Control.WM_GETCONTROLTYPE) { + WmGetControlType(ref m); + return; + } + + #if WIN95_SUPPORT + // If we have to route the mousewheel messages, do it (this logic was taken + // from the MFC sources...) + // + if (mouseWheelRoutingNeeded) { + if (m.Msg == mouseWheelMessage) { + Keys keyState = Keys.None; + keyState |= (Keys)((UnsafeNativeMethods.GetKeyState((int)Keys.ControlKey) < 0) ? NativeMethods.MK_CONTROL : 0); + keyState |= (Keys)((UnsafeNativeMethods.GetKeyState((int)Keys.ShiftKey) < 0) ? NativeMethods.MK_SHIFT : 0); + + IntPtr hwndFocus = UnsafeNativeMethods.GetFocus(); + + if (hwndFocus == IntPtr.Zero) { + SendMessage(m.Msg, (IntPtr)((unchecked((int)(long)m.WParam) << 16) | (int)keyState), m.LParam); + } + else { + IntPtr result = IntPtr.Zero; + IntPtr hwndDesktop = UnsafeNativeMethods.GetDesktopWindow(); + + while (result == IntPtr.Zero && hwndFocus != IntPtr.Zero && hwndFocus != hwndDesktop) { + result = UnsafeNativeMethods.SendMessage(new HandleRef(null, hwndFocus), + NativeMethods.WM_MOUSEWHEEL, + (unchecked((int)(long)m.WParam) << 16) | (int)keyState, + m.LParam); + hwndFocus = UnsafeNativeMethods.GetParent(new HandleRef(null, hwndFocus)); + } + } + } + } + #endif + + if (m.Msg == NativeMethods.WM_MOUSEENTER) { + WmMouseEnter(ref m); + break; + } + + DefWndProc(ref m); + break; + } + + } + + /// + /// Called when an exception occurs in dispatching messages through + /// the main window procedure. + /// + private void WndProcException(Exception e) { + Application.OnThreadException(e); + } + + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); + if (controlsCollection == null) { + return ArrangedElementCollection.Empty; + } + return controlsCollection; + } + } + + /// + /// + IArrangedElement IArrangedElement.Container { + get { + // This is safe because the IArrangedElement interface is internal + return ParentInternal; + } + } + + /// + /// + bool IArrangedElement.ParticipatesInLayout { + get { return GetState(STATE_VISIBLE); } + } + + /// + /// + void IArrangedElement.PerformLayout(IArrangedElement affectedElement, string affectedProperty) { + PerformLayout(new LayoutEventArgs(affectedElement, affectedProperty)); + } + + /// + /// + PropertyStore IArrangedElement.Properties { + get { return Properties; } + } + + // CAREFUL: This really calls SetBoundsCore, not SetBounds. + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + ISite site = Site; + IComponentChangeService ccs = null; + PropertyDescriptor sizeProperty = null; + PropertyDescriptor locationProperty = null; + bool sizeChanged = false; + bool locationChanged = false; + + if (site != null && site.DesignMode) { + ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if(ccs != null) { + sizeProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Size]; + locationProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Location]; + Debug.Assert(sizeProperty != null && locationProperty != null, "Error retrieving Size/Location properties on Control."); + + try { + if (sizeProperty != null && !sizeProperty.IsReadOnly && (bounds.Width != this.Width || bounds.Height != this.Height)) { + if (!(site is INestedSite)) + { + ccs.OnComponentChanging(this, sizeProperty); + } + sizeChanged = true; + } + if (locationProperty != null && !locationProperty.IsReadOnly && (bounds.X != this.x || bounds.Y != this.y)) { + if (!(site is INestedSite)) + { + ccs.OnComponentChanging(this, locationProperty); + } + locationChanged = true; + } + } + catch (InvalidOperationException) { + // The component change events can throw InvalidOperationException if a change is + // currently not allowed (typically because the doc data in VS is locked). + // When this happens, we just eat the exception and proceed with the change. + } + } + } + + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + + if (site != null && ccs != null) { + try { + if (sizeChanged) ccs.OnComponentChanged(this, sizeProperty, null, null); + if (locationChanged) ccs.OnComponentChanged(this, locationProperty, null, null); + } + catch (InvalidOperationException) { + // The component change events can throw InvalidOperationException if a change is + // currently not allowed (typically because the doc data in VS is locked). + // When this happens, we just eat the exception and proceed with the change. + } + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces + /// + internal virtual bool SupportsUiaProviders { + get { + return false; + } + } + + /// + /// + internal sealed class ControlNativeWindow : NativeWindow, IWindowTarget { + private Control control; + private GCHandle rootRef; // We will root the control when we do not want to be elligible for garbage collection. + internal IWindowTarget target; + + internal ControlNativeWindow(Control control) { + this.control = control; + target = this; + } + + internal Control GetControl() { + return control; + } + + protected override void OnHandleChange() { + target.OnHandleChange(this.Handle); + } + + // IWindowTarget method + public void OnHandleChange(IntPtr newHandle) { + control.SetHandle(newHandle); + } + + internal void LockReference(bool locked) { + if (locked) { + if (!rootRef.IsAllocated) { + rootRef = GCHandle.Alloc(GetControl(), GCHandleType.Normal); + } + } + else { + if (rootRef.IsAllocated) { + rootRef.Free(); + } + } + } + + protected override void OnThreadException(Exception e) { + control.WndProcException(e); + } + + // IWindowTarget method + public void OnMessage(ref Message m) { + control.WndProc(ref m); + } + + internal IWindowTarget WindowTarget { + get { + return target; + } + set { + target = value; + } + } + + #if DEBUG + // We override ToString so in debug asserts that fire for + // non-released controls will show what control wasn't released. + // + public override string ToString() { + if (control != null) { + return control.GetType().FullName; + } + return base.ToString(); + } + #endif + + protected override void WndProc(ref Message m) { + // There are certain messages that we want to process + // regardless of what window target we are using. These + // messages cause other messages or state transitions + // to occur within control. + // + switch (m.Msg) { + case NativeMethods.WM_MOUSELEAVE: + control.UnhookMouseEvent(); + break; + + case NativeMethods.WM_MOUSEMOVE: + if (!control.GetState(Control.STATE_TRACKINGMOUSEEVENT)) { + control.HookMouseEvent(); + if (!control.GetState(Control.STATE_MOUSEENTERPENDING)) { + control.SendMessage(NativeMethods.WM_MOUSEENTER, 0, 0); + } + else { + control.SetState(Control.STATE_MOUSEENTERPENDING, false); + } + } + break; + + case NativeMethods.WM_MOUSEWHEEL: + // TrackMouseEvent's mousehover implementation doesn't watch the wheel + // correctly... + // + control.ResetMouseEventArgs(); + break; + } + + target.OnMessage(ref m); + } + } + + /// + /// Explicit support of DropTarget + /// + /// + /// + void IDropTarget.OnDragEnter(DragEventArgs drgEvent) { + OnDragEnter(drgEvent); + } + /// + /// + void IDropTarget.OnDragOver(DragEventArgs drgEvent) { + OnDragOver(drgEvent); + } + /// + /// + void IDropTarget.OnDragLeave(EventArgs e) { + OnDragLeave(e); + } + /// + /// + void IDropTarget.OnDragDrop(DragEventArgs drgEvent) { + OnDragDrop(drgEvent); + } + + /// + /// Explicit support of DropSource + /// + /// + /// + void ISupportOleDropSource.OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEventArgs) { + OnGiveFeedback(giveFeedbackEventArgs); + } + /// + /// + void ISupportOleDropSource.OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEventArgs) { + OnQueryContinueDrag(queryContinueDragEventArgs); + } + + /// + /// + /// Collection of controls... + /// + [ + ListBindable(false), ComVisible(false) + ] + public class ControlCollection : ArrangedElementCollection, IList, ICloneable { + + private Control owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways. + private int lastAccessedIndex = -1; + + + /// + /// + /// [To be supplied.] + /// + public ControlCollection(Control owner) { + this.owner = owner; + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// Adds a child control to this control. The control becomes the last control in + /// the child control list. If the control is already a child of another control it + /// is first removed from that control. + /// + public virtual void Add(Control value) { + if (value == null) + return; + if (value.GetTopLevel()) { + throw new ArgumentException(SR.GetString(SR.TopLevelControlAdd)); + } + + // Verify that the control being added is on the same thread as + // us...or our parent chain. + // + if (owner.CreateThreadId != value.CreateThreadId) { + throw new ArgumentException(SR.GetString(SR.AddDifferentThreads)); + } + + CheckParentingCycle(owner, value); + + if (value.parent == owner) { + value.SendToBack(); + return; + } + + // Remove the new control from its old parent (if any) + // + if (value.parent != null) { + value.parent.Controls.Remove(value); + } + + // Add the control + // + InnerList.Add(value); + + if (value.tabIndex == -1) { + + // Find the next highest tab index + // + int nextTabIndex = 0; + for (int c = 0; c < (Count - 1); c++) { + int t = this[c].TabIndex; + if (nextTabIndex <= t) { + nextTabIndex = t + 1; + } + } + value.tabIndex = nextTabIndex; + } + + // if we don't suspend layout, AssignParent will indirectly trigger a layout event + // before we're ready (AssignParent will fire a PropertyChangedEvent("Visible"), which calls PerformLayout) + // +#if DEBUG + int dbgLayoutCheck = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + + try { + Control oldParent = value.parent; + try { + // AssignParent calls into user code - this could throw, which + // would make us short-circuit the rest of the reparenting logic. + // you could end up with a control half reparented. + value.AssignParent(owner); + } + finally { + if (oldParent != value.parent && (owner.state & STATE_CREATED) != 0) { + value.SetParentHandle(owner.InternalHandle); + if (value.Visible) { + value.CreateControl(); + } + } + } + + value.InitLayout(); + } + finally { + owner.ResumeLayout(false); +#if DEBUG + owner.AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + + } + + // Not putting in the finally block, as it would eat the original + // exception thrown from AssignParent if the following throws an exception. + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + owner.OnControlAdded(new ControlEventArgs(value)); + + + } + + /// + /// + int IList.Add(object control) { + if (control is Control) { + Add((Control)control); + return IndexOf((Control)control); + } + else { + throw new ArgumentException(SR.GetString(SR.ControlBadControl), "control"); + } + } + + /// + /// + /// [To be supplied.] + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual void AddRange(Control[] controls) { + if (controls == null) { + throw new ArgumentNullException("controls"); + } + if (controls.Length > 0) { +#if DEBUG + int dbgLayoutCheck = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + try { + for(int i=0;i < controls.Length; ++i) { + Add(controls[i]); + } + } + finally { + owner.ResumeLayout(true); + } +#if DEBUG + owner.AssertLayoutSuspendCount(dbgLayoutCheck); +#endif + } + } + + /// + /// + object ICloneable.Clone() { + // Use CreateControlInstance so we get the same type of ControlCollection, but whack the + // owner so adding controls to this new collection does not affect the control we cloned from. + ControlCollection ccOther = owner.CreateControlsInstance(); + + // We add using InnerList to prevent unnecessary parent cycle checks, etc. + ccOther.InnerList.AddRange(this); + return ccOther; + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(Control control) { + return InnerList.Contains(control); + } + + + /// + /// + /// Searches for Controls by their Name property, builds up an array + /// of all the controls that match. + /// + /// + public Control[] Find(string key, bool searchAllChildren) { + if (String.IsNullOrEmpty(key)) { + throw new System.ArgumentNullException("key", SR.GetString(SR.FindKeyMayNotBeEmptyOrNull)); + } + + ArrayList foundControls = FindInternal(key, searchAllChildren, this, new ArrayList()); + + // Make this a stongly typed collection. + Control[] stronglyTypedFoundControls = new Control[foundControls.Count]; + foundControls.CopyTo(stronglyTypedFoundControls, 0); + + return stronglyTypedFoundControls; + } + + /// + /// + /// Searches for Controls by their Name property, builds up an array list + /// of all the controls that match. + /// + /// + /// + private ArrayList FindInternal(string key, bool searchAllChildren, ControlCollection controlsToLookIn, ArrayList foundControls) { + if ((controlsToLookIn == null) || (foundControls == null)) { + return null; + } + + try { + // Perform breadth first search - as it's likely people will want controls belonging + // to the same parent close to each other. + + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null){ + continue; + } + + if (WindowsFormsUtils.SafeCompareStrings(controlsToLookIn[i].Name, key, /* ignoreCase = */ true)) { + foundControls.Add(controlsToLookIn[i]); + } + } + + // Optional recurive search for controls in child collections. + + if (searchAllChildren){ + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null){ + continue; + } + if ((controlsToLookIn[i].Controls != null) && controlsToLookIn[i].Controls.Count > 0){ + // if it has a valid child collecion, append those results to our collection + foundControls = FindInternal(key, searchAllChildren, controlsToLookIn[i].Controls, foundControls); + } + } + } + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + return foundControls; + } + + public override IEnumerator GetEnumerator() { + return new ControlCollectionEnumerator(this); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(Control control) { + return InnerList.IndexOf(control); + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (String.IsNullOrEmpty(key)) { + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// Who owns this control collection. + /// + /// + public Control Owner { + get { + return owner; + } + } + + /// + /// + /// Removes control from this control. Inheriting controls should call + /// base.remove to ensure that the control is removed. + /// + public virtual void Remove(Control value) { + + // Sanity check parameter + // + if (value == null) { + return; // Don't do anything + } + + if (value.ParentInternal == owner) { + Debug.Assert(owner != null); + + value.SetParentHandle(IntPtr.Zero); + + // Remove the control from the internal control array + // + InnerList.Remove(value); + value.AssignParent(null); + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + owner.OnControlRemoved(new ControlEventArgs(value)); + + // ContainerControl needs to see it needs to find a new ActiveControl. + ContainerControl cc = owner.GetContainerControlInternal() as ContainerControl; + if (cc != null) + { + cc.AfterControlRemoved(value, owner); + } + } + } + + /// + /// + void IList.Remove(object control) { + if (control is Control) { + Remove((Control)control); + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + Remove(this[index]); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// Retrieves the child control with the specified index. + /// + public new virtual Control this[int index] { + get { + //do some bounds checking here... + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); + } + + Control control = (Control) InnerList[index]; + Debug.Assert(control != null, "Why are we returning null controls from a valid index?"); + return control; + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual Control this[string key] { + get { + // We do not support null and empty string as valid keys. + if (String.IsNullOrEmpty(key)) { + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { +#if DEBUG + int layoutSuspendCount = owner.LayoutSuspendCount; +#endif + owner.SuspendLayout(); + // clear all preferred size caches in the tree - + // inherited fonts could go away, etc. + CommonProperties.xClearAllPreferredSizeCaches(owner); + + try { + while (Count != 0) + RemoveAt(Count - 1); + } finally { + owner.ResumeLayout(); +#if DEBUG + Debug.Assert(owner.LayoutSuspendCount == layoutSuspendCount, "Suspend/Resume layout mismatch!"); +#endif + } + } + + /// + /// + /// Retrieves the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public int GetChildIndex(Control child) { + return GetChildIndex(child, true); + } + + /// + /// + /// Retrieves the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public virtual int GetChildIndex(Control child, bool throwException) { + int index = IndexOf(child); + if (index == -1 && throwException) { + throw new ArgumentException(SR.GetString(SR.ControlNotChild)); + } + return index; + } + + /// + /// This is internal virtual method so that "Readonly Collections" can override this and throw as they should not allow changing + /// the child control indices. + /// + /// + internal virtual void SetChildIndexInternal(Control child, int newIndex) + { + // Sanity check parameters + // + if (child == null) { + throw new ArgumentNullException("child"); + } + + int currentIndex = GetChildIndex(child); + + if (currentIndex == newIndex) { + return; + } + + if (newIndex >= Count || newIndex == -1) { + newIndex = Count - 1; + } + + MoveElement(child, currentIndex, newIndex); + child.UpdateZOrder(); + + LayoutTransaction.DoLayout(owner, child, PropertyNames.ChildIndex); + + } + + /// + /// + /// Sets the index of the specified + /// child control in this array. An ArgumentException + /// is thrown if child is not parented to this + /// Control. + /// + public virtual void SetChildIndex(Control child, int newIndex) { + SetChildIndexInternal(child, newIndex); + } + + // COMPAT: VSWhidbey 448276 + // This is the same as WinformsUtils.ArraySubsetEnumerator + // however since we're no longer an array, we've gotta employ a + // special version of this. + private class ControlCollectionEnumerator : IEnumerator { + private ControlCollection controls; + private int current; + private int originalCount; + + public ControlCollectionEnumerator(ControlCollection controls) { + this.controls = controls; + this.originalCount = controls.Count; + current = -1; + } + + public bool MoveNext() { + // VSWhidbey 448276 + // We have to use Controls.Count here because someone could have deleted + // an item from the array. + // + // this can happen if someone does: + // foreach (Control c in Controls) { c.Dispose(); } + // + // We also dont want to iterate past the original size of the collection + // + // this can happen if someone does + // foreach (Control c in Controls) { c.Controls.Add(new Label()); } + + if (current < controls.Count - 1 && current < originalCount - 1) { + current++; + return true; + } + else { + return false; + } + } + + public void Reset() { + current = -1; + } + + public object Current { + get { + if (current == -1) { + return null; + } + else { + return controls[current]; + } + } + } + } + + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.GetControlInfo(NativeMethods.tagCONTROLINFO pCI) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetControlInfo"); + + pCI.cb = Marshal.SizeOf(typeof(NativeMethods.tagCONTROLINFO)); + pCI.hAccel = IntPtr.Zero; + pCI.cAccel = 0; + pCI.dwFlags = 0; + + if (IsInputKey(Keys.Return)) { + pCI.dwFlags |= NativeMethods.CTRLINFO_EATS_RETURN; + } + if (IsInputKey(Keys.Escape)) { + pCI.dwFlags |= NativeMethods.CTRLINFO_EATS_ESCAPE; + } + + ActiveXInstance.GetControlInfo(pCI); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.OnMnemonic(ref NativeMethods.MSG pMsg) { + + // If we got a mnemonic here, then the appropriate control will focus itself which + // will cause us to become UI active. + // + bool processed = ProcessMnemonic((char)pMsg.wParam); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnMnemonic processed: " + processed.ToString()); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.OnAmbientPropertyChange(int dispID) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnAmbientPropertyChange. Dispid: " + dispID); + Debug.Indent(); + ActiveXInstance.OnAmbientPropertyChange(dispID); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleControl.FreezeEvents(int bFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:FreezeEvents. Freeze: " + bFreeze); + ActiveXInstance.EventsFrozen = (bFreeze != 0); + Debug.Assert(ActiveXInstance.EventsFrozen == (bFreeze != 0), "Failed to set EventsFrozen correctly"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceActiveObject.GetWindow(out IntPtr hwnd) { + return((UnsafeNativeMethods.IOleInPlaceObject)this).GetWindow(out hwnd); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.ContextSensitiveHelp(int fEnterMode) { + ((UnsafeNativeMethods.IOleInPlaceObject)this).ContextSensitiveHelp(fEnterMode); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceActiveObject.TranslateAccelerator(ref NativeMethods.MSG lpmsg) { + return ActiveXInstance.TranslateAccelerator(ref lpmsg); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.OnFrameWindowActivate(bool fActivate) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnFrameWindowActivate"); + OnFrameWindowActivate(fActivate); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.OnDocWindowActivate(int fActivate) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OnDocWindowActivate. Activate: " + fActivate.ToString(CultureInfo.InvariantCulture)); + Debug.Indent(); + ActiveXInstance.OnDocWindowActivate(fActivate); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.ResizeBorder(NativeMethods.COMRECT prcBorder, UnsafeNativeMethods.IOleInPlaceUIWindow pUIWindow, bool fFrameWindow) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ResizesBorder"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceActiveObject.EnableModeless(int fEnable) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnableModeless"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceObject.GetWindow(out IntPtr hwnd) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetWindow"); + int hr = ActiveXInstance.GetWindow(out hwnd); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "\twin == " + hwnd); + return hr; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.ContextSensitiveHelp(int fEnterMode) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ContextSensitiveHelp. Mode: " + fEnterMode.ToString(CultureInfo.InvariantCulture)); + if (fEnterMode != 0) { + OnHelpRequested(new HelpEventArgs(Control.MousePosition)); + } + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.InPlaceDeactivate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:InPlaceDeactivate"); + Debug.Indent(); + ActiveXInstance.InPlaceDeactivate(); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleInPlaceObject.UIDeactivate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:UIDeactivate"); + return ActiveXInstance.UIDeactivate(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.SetObjectRects(NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetObjectRects(" + lprcClipRect.left + ", " + lprcClipRect.top + ", " + lprcClipRect.right + ", " + lprcClipRect.bottom + ")"); + Debug.Indent(); + ActiveXInstance.SetObjectRects(lprcPosRect, lprcClipRect); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleInPlaceObject.ReactivateAndUndo() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:ReactivateAndUndo"); + // return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetClientSite(UnsafeNativeMethods.IOleClientSite pClientSite) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetClientSite"); + ActiveXInstance.SetClientSite(pClientSite); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + UnsafeNativeMethods.IOleClientSite UnsafeNativeMethods.IOleObject.GetClientSite() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetClientSite"); + return ActiveXInstance.GetClientSite(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetHostNames(string szContainerApp, string szContainerObj) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetHostNames"); + // Since ActiveX controls never "open" for editing, we shouldn't need + // to store these. + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Close(int dwSaveOption) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Close. Save option: " + dwSaveOption); + ActiveXInstance.Close(dwSaveOption); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetMoniker(int dwWhichMoniker, Object pmk) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetMoniker"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetMoniker(int dwAssign, int dwWhichMoniker, out Object moniker) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMoniker"); + moniker = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.InitFromData(IComDataObject pDataObject, int fCreation, int dwReserved) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:InitFromData"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetClipboardData(int dwReserved, out IComDataObject data) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetClipboardData"); + data = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.DoVerb(int iVerb, IntPtr lpmsg, UnsafeNativeMethods.IOleClientSite pActiveSite, int lindex, IntPtr hwndParent, NativeMethods.COMRECT lprcPosRect) { + + // In Office they are internally casting an iverb to a short and not + // doing the proper sign extension. So, we do it here. + // + short sVerb = unchecked((short)iVerb); + iVerb = (int)sVerb; + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("AxSource:DoVerb {"); + Debug.WriteLine(" verb: " + iVerb); + Debug.WriteLine(" msg: " + lpmsg); + Debug.WriteLine(" activeSite: " + pActiveSite); + Debug.WriteLine(" index: " + lindex); + Debug.WriteLine(" hwndParent: " + hwndParent); + Debug.WriteLine(" posRect: " + lprcPosRect); + } +#endif + Debug.Indent(); + try { + ActiveXInstance.DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect); + } + catch (Exception e) { + Debug.Fail("Exception occurred during DoVerb(" + iVerb + ") " + e.ToString()); + throw; + } + finally { + Debug.Unindent(); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "}"); + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnumVerbs"); + return ActiveXImpl.EnumVerbs(out e); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.OleUpdate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:OleUpdate"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.IsUpToDate() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IsUpToDate"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetUserClassID(ref Guid pClsid) { + pClsid = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetUserClassID. ClassID: " + pClsid.ToString()); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetUserType(int dwFormOfType, out string userType) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetUserType"); + if (dwFormOfType == NativeMethods.USERCLASSTYPE_FULL) { + userType = GetType().FullName; + } + else { + userType = GetType().Name; + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetExtent(" + pSizel.cx + ", " + pSizel.cy + ")"); + Debug.Indent(); + ActiveXInstance.SetExtent(dwDrawAspect, pSizel); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetExtent. Aspect: " + dwDrawAspect.ToString(CultureInfo.InvariantCulture)); + Debug.Indent(); + ActiveXInstance.GetExtent(dwDrawAspect, pSizel); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "value: " + pSizel.cx + ", " + pSizel.cy); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Advise(IAdviseSink pAdvSink, out int cookie) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Advise"); + cookie = ActiveXInstance.Advise(pAdvSink); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.Unadvise(int dwConnection) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unadvise"); + Debug.Indent(); + ActiveXInstance.Unadvise(dwConnection); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.EnumAdvise(out IEnumSTATDATA e) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:EnumAdvise"); + e = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.GetMiscStatus(int dwAspect, out int cookie) { + if ((dwAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + int status = NativeMethods.OLEMISC_ACTIVATEWHENVISIBLE | NativeMethods.OLEMISC_INSIDEOUT | NativeMethods.OLEMISC_SETCLIENTSITEFIRST; + + if (GetStyle(ControlStyles.ResizeRedraw)) { + status |= NativeMethods.OLEMISC_RECOMPOSEONRESIZE; + } + + if (this is IButtonControl) { + status |= NativeMethods.OLEMISC_ACTSLIKEBUTTON; + } + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMiscStatus. Status: " + status.ToString(CultureInfo.InvariantCulture)); + cookie = status; + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetMiscStatus. Status: ERROR, wrong aspect."); + cookie = 0; + return NativeMethods.DV_E_DVASPECT; + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleObject.SetColorScheme(NativeMethods.tagLOGPALETTE pLogpal) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetColorScheme"); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IOleWindow.GetWindow(out IntPtr hwnd) { + return((UnsafeNativeMethods.IOleInPlaceObject)this).GetWindow(out hwnd); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IOleWindow.ContextSensitiveHelp(int fEnterMode) { + ((UnsafeNativeMethods.IOleInPlaceObject)this).ContextSensitiveHelp(fEnterMode); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersist.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersist.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.InitNew() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistPropertyBag.InitNew"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistPropertyBag.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.Load(UnsafeNativeMethods.IPropertyBag pPropBag, UnsafeNativeMethods.IErrorLog pErrorLog) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Load (IPersistPropertyBag)"); + Debug.Indent(); + ActiveXInstance.Load(pPropBag, pErrorLog); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistPropertyBag.Save(UnsafeNativeMethods.IPropertyBag pPropBag, bool fClearDirty, bool fSaveAllProperties) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Save (IPersistPropertyBag)"); + Debug.Indent(); + ActiveXInstance.Save(pPropBag, fClearDirty, fSaveAllProperties); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStorage.IsDirty() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.IsDirty"); + return ActiveXInstance.IsDirty(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.InitNew(UnsafeNativeMethods.IStorage pstg) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.InitNew"); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStorage.Load(UnsafeNativeMethods.IStorage pstg) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.Load"); + Debug.Indent(); + ActiveXInstance.Load(pstg); + Debug.Unindent(); + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.Save(UnsafeNativeMethods.IStorage pstg, bool fSameAsLoad) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.Save"); + Debug.Indent(); + ActiveXInstance.Save(pstg, fSameAsLoad); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.SaveCompleted(UnsafeNativeMethods.IStorage pStgNew) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.SaveCompleted"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStorage.HandsOffStorage() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStorage.HandsOffStorage"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.GetClassID(out Guid pClassID) { + pClassID = GetType().GUID; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.GetClassID. ClassID: " + pClassID.ToString()); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IPersistStreamInit.IsDirty() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.IsDirty"); + return ActiveXInstance.IsDirty(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.Load(UnsafeNativeMethods.IStream pstm) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.Load"); + Debug.Indent(); + ActiveXInstance.Load(pstm); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.Save(UnsafeNativeMethods.IStream pstm, bool fClearDirty) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.Save"); + Debug.Indent(); + ActiveXInstance.Save(pstm, fClearDirty); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.GetSizeMax(long pcbSize) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetSizeMax"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IPersistStreamInit.InitNew() { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:IPersistStreamInit.InitNew"); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:QuickActivate"); + Debug.Indent(); + ActiveXInstance.QuickActivate(pQaContainer, pQaControl); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.SetContentExtent(NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetContentExtent"); + Debug.Indent(); + ActiveXInstance.SetExtent(NativeMethods.DVASPECT_CONTENT, pSizel); + Debug.Unindent(); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IQuickActivate.GetContentExtent(NativeMethods.tagSIZEL pSizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetContentExtent"); + Debug.Indent(); + ActiveXInstance.GetExtent(NativeMethods.DVASPECT_CONTENT, pSizel); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT lprcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Draw"); + + + Debug.Indent(); + try { + ActiveXInstance.Draw(dwDrawAspect, lindex, pvAspect, ptd, hdcTargetDev, + hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue); + + } + catch(ExternalException ex) { + return ex.ErrorCode; + } + finally { + Debug.Unindent(); + } + return NativeMethods.S_OK; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.GetColorSet(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hicTargetDev, NativeMethods.tagLOGPALETTE ppColorSet) { + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetColorSet"); + + // GDI+ doesn't do palettes. + // + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Freeze(int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr pdwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Freezes"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject.Unfreeze(int dwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unfreeze"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject.SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetAdvise"); + ActiveXInstance.SetAdvise(aspects, advf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject.GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAdvise"); + ActiveXInstance.GetAdvise(paspects, padvf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT lprcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Draw"); + Debug.Indent(); + ActiveXInstance.Draw(dwDrawAspect, lindex, pvAspect, ptd, hdcTargetDev, + hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue); + Debug.Unindent(); + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.GetColorSet(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hicTargetDev, NativeMethods.tagLOGPALETTE ppColorSet) { + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetColorSet"); + + // GDI+ doesn't do palettes. + // + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.Freeze(int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr pdwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Freezes"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + int UnsafeNativeMethods.IViewObject2.Unfreeze(int dwFreeze) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:Unfreeze"); + return NativeMethods.E_NOTIMPL; + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:SetAdvise"); + ActiveXInstance.SetAdvise(aspects, advf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAdvise"); + ActiveXInstance.GetAdvise(paspects, padvf, pAdvSink); + } + + /// + /// + /// + /// + void UnsafeNativeMethods.IViewObject2.GetExtent(int dwDrawAspect, int lindex, NativeMethods.tagDVTARGETDEVICE ptd, NativeMethods.tagSIZEL lpsizel) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetExtent (IViewObject2)"); + // we already have an implementation of this [from IOleObject] + // + ((UnsafeNativeMethods.IOleObject)this).GetExtent(dwDrawAspect, lpsizel); + } + + #region IKeyboardToolTip implementation + + bool IKeyboardToolTip.CanShowToolTipsNow() { + IKeyboardToolTip host = this.ToolStripControlHost; + return this.IsHandleCreated && this.Visible && (host == null || host.CanShowToolTipsNow()); + } + + Rectangle IKeyboardToolTip.GetNativeScreenRectangle() { + return this.GetToolNativeScreenRectangle(); + } + + IList IKeyboardToolTip.GetNeighboringToolsRectangles() { + IKeyboardToolTip host = this.ToolStripControlHost; + if (host == null) { + return this.GetOwnNeighboringToolsRectangles(); + } + else { + return host.GetNeighboringToolsRectangles(); + } + } + + bool IKeyboardToolTip.IsHoveredWithMouse() { + return this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition)); + } + + bool IKeyboardToolTip.HasRtlModeEnabled() { + Control topLevelControl = TopLevelControlInternal; + return topLevelControl != null && topLevelControl.RightToLeft == RightToLeft.Yes && !this.IsMirrored; + } + + bool IKeyboardToolTip.AllowsToolTip() { + IKeyboardToolTip host = this.ToolStripControlHost; + return (host == null || host.AllowsToolTip()) && this.AllowsKeyboardToolTip(); + } + + IWin32Window IKeyboardToolTip.GetOwnerWindow() { + return this; + } + + void IKeyboardToolTip.OnHooked(ToolTip toolTip) { + this.OnKeyboardToolTipHook(toolTip); + } + + void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) { + this.OnKeyboardToolTipUnhook(toolTip); + } + + string IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) { + IKeyboardToolTip host = this.ToolStripControlHost; + if (host == null) { + return toolTip.GetCaptionForTool(this); + } + else { + return host.GetCaptionForTool(toolTip); + } + } + + bool IKeyboardToolTip.ShowsOwnToolTip() { + IKeyboardToolTip host = this.ToolStripControlHost; + return (host == null || host.ShowsOwnToolTip()) && this.ShowsOwnKeyboardToolTip(); + } + + bool IKeyboardToolTip.IsBeingTabbedTo() { + return Control.AreCommonNavigationalKeysDown(); + } + + bool IKeyboardToolTip.AllowsChildrenToShowToolTips() { + return this.AllowsChildrenToShowToolTips(); + } + + #endregion + + private IList GetOwnNeighboringToolsRectangles() { + Control controlParent = this.ParentInternal; + if (controlParent != null) { + Control[] neighboringControls = new Control[4] { + // Next and previous control which are accessible with Tab and Shift+Tab + controlParent.GetNextSelectableControl(this, true, true, true, false), + controlParent.GetNextSelectableControl(this, false, true, true, false), + // Next and previous control which are accessible with arrow keys + controlParent.GetNextSelectableControl(this, true, false, false, true), + controlParent.GetNextSelectableControl(this, false, false, false, true) + }; + + List neighboringControlsRectangles = new List(4); + foreach (Control neighboringControl in neighboringControls) { + if (neighboringControl != null && neighboringControl.IsHandleCreated) { + neighboringControlsRectangles.Add(((IKeyboardToolTip)neighboringControl).GetNativeScreenRectangle()); + } + } + + return neighboringControlsRectangles; + } + else { + return new Rectangle[0]; + } + } + + internal virtual bool ShowsOwnKeyboardToolTip() { + return true; + } + + internal virtual void OnKeyboardToolTipHook(ToolTip toolTip) { + } + + internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip) { + } + + internal virtual Rectangle GetToolNativeScreenRectangle() { + NativeMethods.RECT rectangle = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, this.Handle), ref rectangle); + return Rectangle.FromLTRB(rectangle.left, rectangle.top, rectangle.right, rectangle.bottom); + } + + internal virtual bool AllowsKeyboardToolTip() { + // This internal method enables keyboard ToolTips for all controls including the foreign descendants of Control unless this method is overridden in a child class belonging to this assembly. + // ElementHost is one such control which is located in a different assembly. + // This control doesn't show a mouse ToolTip when hovered and thus should not have a keyboard ToolTip as well. + // We are not going to fix it now since it seems unlikely that someone would set ToolTip on such special container control as ElementHost. + return true; + } + + private static bool IsKeyDown(Keys key) { + return (tempKeyboardStateArray[(int)key] & Control.HighOrderBitMask) != 0; + } + + internal static bool AreCommonNavigationalKeysDown() { + if(tempKeyboardStateArray == null) { + tempKeyboardStateArray = new byte[256]; + } + UnsafeNativeMethods.GetKeyboardState(tempKeyboardStateArray); + return IsKeyDown(Keys.Tab) + || IsKeyDown(Keys.Up) + || IsKeyDown(Keys.Down) + || IsKeyDown(Keys.Left) + || IsKeyDown(Keys.Right) + // receiving focus from the ToolStrip + || IsKeyDown(Keys.Menu) + || IsKeyDown(Keys.F10) + || IsKeyDown(Keys.Escape); + } + + private readonly WeakReference toolStripControlHostReference = new WeakReference(null); + + internal ToolStripControlHost ToolStripControlHost { + get { + ToolStripControlHost value; + this.toolStripControlHostReference.TryGetTarget(out value); + return value; + } + set { + this.toolStripControlHostReference.SetTarget(value); + } + } + + internal virtual bool AllowsChildrenToShowToolTips() { + return true; + } + + /// + /// This class holds all of the state data for an ActiveX control and + /// supplies the implementation for many of the non-trivial methods. + /// + /// + private class ActiveXImpl : MarshalByRefObject, IWindowTarget { + // SECUNDONE : This call is private and is only used to expose a WinForm control as + // : an ActiveX control. This class needs more review. + // + + private static readonly int hiMetricPerInch = 2540; + private static readonly int viewAdviseOnlyOnce = BitVector32.CreateMask(); + private static readonly int viewAdvisePrimeFirst = BitVector32.CreateMask(viewAdviseOnlyOnce); + private static readonly int eventsFrozen = BitVector32.CreateMask(viewAdvisePrimeFirst); + private static readonly int changingExtents = BitVector32.CreateMask(eventsFrozen); + private static readonly int saving = BitVector32.CreateMask(changingExtents); + private static readonly int isDirty = BitVector32.CreateMask(saving); + private static readonly int inPlaceActive = BitVector32.CreateMask(isDirty); + private static readonly int inPlaceVisible = BitVector32.CreateMask(inPlaceActive); + private static readonly int uiActive = BitVector32.CreateMask(inPlaceVisible); + private static readonly int uiDead = BitVector32.CreateMask(uiActive); + private static readonly int adjustingRect = BitVector32.CreateMask(uiDead); + + private static Point logPixels = Point.Empty; + private static NativeMethods.tagOLEVERB[] axVerbs; + + private static int globalActiveXCount = 0; + private static bool checkedIE; + private static bool isIE; + + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + private static ActiveXPropPage propPage; + + #endif + + private Control control; + private IWindowTarget controlWindowTarget; + private IntPtr clipRegion; + private UnsafeNativeMethods.IOleClientSite clientSite; + private UnsafeNativeMethods.IOleInPlaceUIWindow inPlaceUiWindow; + private UnsafeNativeMethods.IOleInPlaceFrame inPlaceFrame; + private ArrayList adviseList; + private IAdviseSink viewAdviseSink; + private BitVector32 activeXState; + private AmbientProperty[] ambientProperties; + private IntPtr hwndParent; + private IntPtr accelTable; + private short accelCount = -1; + private NativeMethods.COMRECT adjustRect; // temporary rect used during OnPosRectChange && SetObjectRects + + /// + /// + /// Creates a new ActiveXImpl. + /// + internal ActiveXImpl(Control control) { + this.control = control; + + // We replace the control's window target with our own. We + // do this so we can handle the UI Dead ambient property. + // + controlWindowTarget = control.WindowTarget; + control.WindowTarget = this; + + adviseList = new ArrayList(); + activeXState = new BitVector32(); + ambientProperties = new AmbientProperty[] { + new AmbientProperty("Font", NativeMethods.ActiveX.DISPID_AMBIENT_FONT), + new AmbientProperty("BackColor", NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR), + new AmbientProperty("ForeColor", NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR) + }; + } + + /// + /// + /// Retrieves the ambient back color for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Color AmbientBackColor { + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR, ref obj)) { + if (obj != null) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object color type=" + obj.GetType().FullName); + prop.Value = ColorTranslator.FromOle(Convert.ToInt32(obj, CultureInfo.InvariantCulture)); + } + catch (Exception e) { + Debug.Fail("Failed to massage ambient back color to a Color", e.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + } + } + } + + if (prop.Value == null) { + return Color.Empty; + } + else { + return(Color)prop.Value; + } + } + } + + /// + /// Retrieves the ambient font for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Font AmbientFont { + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_FONT, ref obj)) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object font type=" + obj.GetType().FullName); + Debug.Assert(obj != null, "GetAmbientProperty failed"); + IntPtr hfont = IntPtr.Zero; + + + UnsafeNativeMethods.IFont ifont = (UnsafeNativeMethods.IFont)obj; + IntSecurity.ObjectFromWin32Handle.Assert(); + Font font = null; + try { + hfont = ifont.GetHFont(); + font = Font.FromHfont(hfont); + } + finally { + CodeAccessPermission.RevertAssert(); + } + prop.Value = font; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Do NULL, so we just defer to the default font + prop.Value = null; + } + } + } + + return(Font)prop.Value; + } + } + + /// + /// Retrieves the ambient back color for the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal Color AmbientForeColor { + get { + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + + if (prop.Empty) { + Object obj = null; + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR, ref obj)) { + if (obj != null) { + try { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Object color type=" + obj.GetType().FullName); + prop.Value = ColorTranslator.FromOle(Convert.ToInt32(obj, CultureInfo.InvariantCulture)); + } + catch (Exception e) { + Debug.Fail("Failed to massage ambient fore color to a Color", e.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + } + } + } + + if (prop.Value == null) { + return Color.Empty; + } + else { + return(Color)prop.Value; + } + } + } + + /// + /// + /// Determines if events should be frozen. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal bool EventsFrozen { + get { + return activeXState[eventsFrozen]; + } + set { + activeXState[eventsFrozen] = value; + } + } + + /// + /// Provides access to the parent window handle + /// when we are UI active + /// + internal IntPtr HWNDParent { + get { + return hwndParent; + } + } + + /// + /// Returns true if this app domain is running inside of IE. The + /// control must be sited for this to succeed (it will assert and + /// return false if the control is not sited). + /// + internal bool IsIE { + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + if (!checkedIE) { + if (clientSite == null) { + Debug.Fail("Do not reference IsIE property unless control is sited."); + return false; + } + + // First, is this a managed EXE? If so, it will correctly shut down + // the runtime. + if (Assembly.GetEntryAssembly() == null) { + // Now check for IHTMLDocument2 + UnsafeNativeMethods.IOleContainer container; + + if (NativeMethods.Succeeded(clientSite.GetContainer(out container)) && container is NativeMethods.IHTMLDocument) { + isIE = true; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "AxSource:IsIE running under IE"); + } + + if (container != null && UnsafeNativeMethods.IsComObject(container)) { + UnsafeNativeMethods.ReleaseComObject(container); + } + } + + checkedIE = true; + } + + return isIE; + } + } + + /// + /// Retrieves the number of logical pixels per inch on the + /// primary monitor. + /// + private Point LogPixels { + get { + if (logPixels.IsEmpty) { + logPixels = new Point(); + IntPtr dc = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + logPixels.X = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX); + logPixels.Y = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); + } + return logPixels; + } + } + + /// + /// + /// Implements IOleObject::Advise + /// + internal int Advise(IAdviseSink pAdvSink) { + adviseList.Add(pAdvSink); + return adviseList.Count; + } + + /// + /// + /// Implements IOleObject::Close + /// + internal void Close(int dwSaveOption) { + if (activeXState[inPlaceActive]) { + InPlaceDeactivate(); + } + + if ((dwSaveOption == NativeMethods.OLECLOSE_SAVEIFDIRTY || + dwSaveOption == NativeMethods.OLECLOSE_PROMPTSAVE) && + activeXState[isDirty]) { + + if (clientSite != null) { + clientSite.SaveObject(); + } + SendOnSave(); + } + } + + /// + /// + /// Implements IOleObject::DoVerb + /// + internal void DoVerb(int iVerb, IntPtr lpmsg, UnsafeNativeMethods.IOleClientSite pActiveSite, int lindex, IntPtr hwndParent, NativeMethods.COMRECT lprcPosRect) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "AxSource:ActiveXImpl:DoVerb(" + iVerb + ")"); + switch (iVerb) { + case NativeMethods.OLEIVERB_SHOW: + case NativeMethods.OLEIVERB_INPLACEACTIVATE: + case NativeMethods.OLEIVERB_UIACTIVATE: + case NativeMethods.OLEIVERB_PRIMARY: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Show, InPlaceActivate, UIActivate"); + InPlaceActivate(iVerb); + + // Now that we're active, send the lpmsg to the control if it + // is valid. + // + if (lpmsg != IntPtr.Zero) { + NativeMethods.MSG msg = (NativeMethods.MSG)UnsafeNativeMethods.PtrToStructure(lpmsg, typeof(NativeMethods.MSG)); + Control target = control; + + if (msg.hwnd != control.Handle && msg.message >= NativeMethods.WM_MOUSEFIRST && msg.message <= NativeMethods.WM_MOUSELAST) { + + // Must translate message coordniates over to our HWND. We first try + // + IntPtr hwndMap = msg.hwnd == IntPtr.Zero ? hwndParent : msg.hwnd; + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = NativeMethods.Util.LOWORD(msg.lParam); + pt.y = NativeMethods.Util.HIWORD(msg.lParam); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(null, hwndMap), new HandleRef(control, control.Handle), pt, 1); + + // check to see if this message should really go to a child + // control, and if so, map the point into that child's window + // coordinates + // + Control realTarget = target.GetChildAtPoint(new Point(pt.x, pt.y)); + if (realTarget != null && realTarget != target) { + UnsafeNativeMethods.MapWindowPoints(new HandleRef(target, target.Handle), new HandleRef(realTarget, realTarget.Handle), pt, 1); + target = realTarget; + } + + msg.lParam = NativeMethods.Util.MAKELPARAM(pt.x, pt.y); + } + +#if DEBUG + if (CompModSwitches.ActiveX.TraceVerbose) { + Message m = Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam); + Debug.WriteLine("Valid message pointer passed, sending to control: " + m.ToString()); + } +#endif // DEBUG + + if (msg.message == NativeMethods.WM_KEYDOWN && msg.wParam == (IntPtr)NativeMethods.VK_TAB) { + target.SelectNextControl(null, Control.ModifierKeys != Keys.Shift, true, true, true); + } + else { + target.SendMessage(msg.message, msg.wParam, msg.lParam); + } + } + break; + + // These affect our visibility + // + case NativeMethods.OLEIVERB_HIDE: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Hide"); + UIDeactivate(); + InPlaceDeactivate(); + if (activeXState[inPlaceVisible]) { + SetInPlaceVisible(false); + } + break; + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + // Show our property pages + // + case NativeMethods.OLEIVERB_PROPERTIES: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Primary, Properties"); + ShowProperties(); + break; + + #endif + + // All other verbs are notimpl. + // + default: + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "DoVerb:Other"); + ThrowHr(NativeMethods.E_NOTIMPL); + break; + } + } + + /// + /// + /// Implements IViewObject2::Draw. + /// + internal void Draw(int dwDrawAspect, int lindex, IntPtr pvAspect, NativeMethods.tagDVTARGETDEVICE ptd, + IntPtr hdcTargetDev, IntPtr hdcDraw, NativeMethods.COMRECT prcBounds, NativeMethods.COMRECT lprcWBounds, + IntPtr pfnContinue, int dwContinue) { + + // support the aspects required for multi-pass drawing + // + switch (dwDrawAspect) { + case NativeMethods.DVASPECT_CONTENT: + case NativeMethods.DVASPECT_OPAQUE: + case NativeMethods.DVASPECT_TRANSPARENT: + break; + default: + ThrowHr(NativeMethods.DV_E_DVASPECT); + break; + } + + // We can paint to an enhanced metafile, but not all GDI / GDI+ is + // supported on classic metafiles. We throw VIEW_E_DRAW in the hope that + // the caller figures it out and sends us a different DC. + // + int hdcType = UnsafeNativeMethods.GetObjectType(new HandleRef(null, hdcDraw)); + if (hdcType == NativeMethods.OBJ_METADC) { + ThrowHr(NativeMethods.VIEW_E_DRAW); + } + + NativeMethods.RECT rc; + NativeMethods.POINT pVp = new NativeMethods.POINT(); + NativeMethods.POINT pW = new NativeMethods.POINT(); + NativeMethods.SIZE sWindowExt = new NativeMethods.SIZE(); + NativeMethods.SIZE sViewportExt = new NativeMethods.SIZE(); + int iMode = NativeMethods.MM_TEXT; + + if (!control.IsHandleCreated) { + control.CreateHandle(); + } + + // if they didn't give us a rectangle, just copy over ours + // + if (prcBounds != null) { + rc = new NativeMethods.RECT(prcBounds.left, prcBounds.top, prcBounds.right, prcBounds.bottom); + + // To draw to a given rect, we scale the DC in such a way as to + // make the values it takes match our own happy MM_TEXT. Then, + // we back-convert prcBounds so that we convert it to this coordinate + // system. This puts us in the most similar coordinates as we currently + // use. + // + SafeNativeMethods.LPtoDP(new HandleRef(null, hdcDraw), ref rc, 2); + + iMode = SafeNativeMethods.SetMapMode(new HandleRef(null, hdcDraw), NativeMethods.MM_ANISOTROPIC); + SafeNativeMethods.SetWindowOrgEx(new HandleRef(null, hdcDraw), 0, 0, pW); + SafeNativeMethods.SetWindowExtEx(new HandleRef(null, hdcDraw), control.Width, control.Height, sWindowExt); + SafeNativeMethods.SetViewportOrgEx(new HandleRef(null, hdcDraw), rc.left, rc.top, pVp); + SafeNativeMethods.SetViewportExtEx(new HandleRef(null, hdcDraw), rc.right - rc.left, rc.bottom - rc.top, sViewportExt); + } + + // Now do the actual drawing. We must ask all of our children to draw as well. + try { + IntPtr flags = (IntPtr)(NativeMethods.PRF_CHILDREN + | NativeMethods.PRF_CLIENT + | NativeMethods.PRF_ERASEBKGND + | NativeMethods.PRF_NONCLIENT); + //| NativeMethods.PRF_CHECKVISIBLE + //| NativeMethods.PRF_OWNED)); + + if(hdcType != NativeMethods.OBJ_ENHMETADC) { + control.SendMessage(NativeMethods.WM_PRINT, hdcDraw, flags); + } else { + control.PrintToMetaFile(new HandleRef(null, hdcDraw), flags); + } + } + finally { + // And clean up the DC + // + if (prcBounds != null) { + SafeNativeMethods.SetWindowOrgEx(new HandleRef(null, hdcDraw), pW.x, pW.y, null); + SafeNativeMethods.SetWindowExtEx(new HandleRef(null, hdcDraw), sWindowExt.cx, sWindowExt.cy, null); + SafeNativeMethods.SetViewportOrgEx(new HandleRef(null, hdcDraw), pVp.x, pVp.y, null); + SafeNativeMethods.SetViewportExtEx(new HandleRef(null, hdcDraw), sViewportExt.cx, sViewportExt.cy, null); + SafeNativeMethods.SetMapMode(new HandleRef(null, hdcDraw), iMode); + } + } + } + + /// + /// + /// Returns a new verb enumerator. + /// + internal static int EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e) { + if (axVerbs == null) { + NativeMethods.tagOLEVERB verbShow = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbInplaceActivate = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbUIActivate = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbHide = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbPrimary = new NativeMethods.tagOLEVERB(); + NativeMethods.tagOLEVERB verbProperties = new NativeMethods.tagOLEVERB(); + + verbShow.lVerb = NativeMethods.OLEIVERB_SHOW; + verbInplaceActivate.lVerb = NativeMethods.OLEIVERB_INPLACEACTIVATE; + verbUIActivate.lVerb = NativeMethods.OLEIVERB_UIACTIVATE; + verbHide.lVerb = NativeMethods.OLEIVERB_HIDE; + verbPrimary.lVerb = NativeMethods.OLEIVERB_PRIMARY; + verbProperties.lVerb = NativeMethods.OLEIVERB_PROPERTIES; + verbProperties.lpszVerbName = SR.GetString(SR.AXProperties); + verbProperties.grfAttribs = NativeMethods.ActiveX.OLEVERBATTRIB_ONCONTAINERMENU; + + axVerbs = new NativeMethods.tagOLEVERB[] { + verbShow, + verbInplaceActivate, + verbUIActivate, + verbHide, + verbPrimary + #if ACTIVEX_SOURCING + ,verbProperties + #endif + }; + } + + + e = new ActiveXVerbEnum(axVerbs); + return NativeMethods.S_OK; + } + + /// + /// Converts the given string to a byte array. + /// + private static byte[] FromBase64WrappedString(string text) { + if (text.IndexOfAny(new char[] {' ', '\r', '\n'}) != -1) { + StringBuilder sb = new StringBuilder(text.Length); + for (int i=0; i + /// Implements IViewObject2::GetAdvise. + /// + internal void GetAdvise(int[] paspects, int[] padvf, IAdviseSink[] pAdvSink) { + // if they want it, give it to them + // + if (paspects != null) { + paspects[0] = NativeMethods.DVASPECT_CONTENT; + } + + if (padvf != null) { + padvf[0] = 0; + + if (activeXState[viewAdviseOnlyOnce]) padvf[0] |= NativeMethods.ADVF_ONLYONCE; + if (activeXState[viewAdvisePrimeFirst]) padvf[0] |= NativeMethods.ADVF_PRIMEFIRST; + } + + if (pAdvSink != null) { + pAdvSink[0] = viewAdviseSink; + } + } + + /// + /// Helper function to retrieve an ambient property. Returns false if the + /// property wasn't found. + /// + private bool GetAmbientProperty(int dispid, ref Object obj) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource:GetAmbientProperty"); + Debug.Indent(); + + if (clientSite is UnsafeNativeMethods.IDispatch) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "clientSite implements IDispatch"); + + UnsafeNativeMethods.IDispatch disp = (UnsafeNativeMethods.IDispatch)clientSite; + Object[] pvt = new Object[1]; + Guid g = Guid.Empty; + int hr = NativeMethods.E_FAIL; + + IntSecurity.UnmanagedCode.Assert(); + try { + hr = disp.Invoke(dispid, ref g, NativeMethods.LOCALE_USER_DEFAULT, + NativeMethods.DISPATCH_PROPERTYGET, new NativeMethods.tagDISPPARAMS(), + pvt, null, null); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (NativeMethods.Succeeded(hr)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "IDispatch::Invoke succeeded. VT=" + pvt[0].GetType().FullName); + obj = pvt[0]; + Debug.Unindent(); + return true; + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "IDispatch::Invoke failed. HR: 0x" + String.Format(CultureInfo.CurrentCulture, "{0:X}", hr)); + } + } + + Debug.Unindent(); + return false; + } + + /// + /// Implements IOleObject::GetClientSite. + /// + internal UnsafeNativeMethods.IOleClientSite GetClientSite() { + return clientSite; + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal int GetControlInfo(NativeMethods.tagCONTROLINFO pCI) { + if (accelCount == -1) { + ArrayList mnemonicList = new ArrayList(); + GetMnemonicList(control, mnemonicList); + + accelCount = (short)mnemonicList.Count; + + if (accelCount > 0) { + int accelSize = UnsafeNativeMethods.SizeOf(typeof(NativeMethods.ACCEL)); + + // In the worst case we may have two accelerators per mnemonic: one lower case and + // one upper case, hence the * 2 below. + // + IntPtr accelBlob = Marshal.AllocHGlobal(accelSize * accelCount * 2); + + try { + NativeMethods.ACCEL accel = new NativeMethods.ACCEL(); + accel.cmd = 0; + + Debug.Indent(); + + accelCount = 0; + + foreach(char ch in mnemonicList) { + IntPtr structAddr = (IntPtr)((long)accelBlob + accelCount * accelSize); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Mnemonic: " + ch.ToString()); + + if (ch >= 'A' && ch <= 'Z') { + + // Lower case letter + // + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY; + accel.key = (short)(UnsafeNativeMethods.VkKeyScan(ch) & 0x00FF); + Marshal.StructureToPtr(accel, structAddr, false); + + // Upper case letter + // + accelCount++; + structAddr = (IntPtr)((long)structAddr + accelSize); + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY | NativeMethods.FSHIFT; + Marshal.StructureToPtr(accel, structAddr, false); + } + else { + // Some non-printable character. + // + accel.fVirt = NativeMethods.FALT | NativeMethods.FVIRTKEY; + short scan = (short)(UnsafeNativeMethods.VkKeyScan(ch)); + if ((scan & 0x0100) != 0) { + accel.fVirt |= NativeMethods.FSHIFT; + } + accel.key = (short)(scan & 0x00FF); + Marshal.StructureToPtr(accel, structAddr, false); + } + + accel.cmd++; + accelCount++; + } + + Debug.Unindent(); + + // Now create an accelerator table and then free our memory. + // + + // DevDiv Bugs 170945 + if (accelTable != IntPtr.Zero) { + UnsafeNativeMethods.DestroyAcceleratorTable(new HandleRef(this, accelTable)); + accelTable = IntPtr.Zero; + } + + accelTable = UnsafeNativeMethods.CreateAcceleratorTable(new HandleRef(null, accelBlob), accelCount); + } + finally { + if (accelBlob != IntPtr.Zero) { + Marshal.FreeHGlobal(accelBlob); + } + } + } + } + + pCI.cAccel = accelCount; + pCI.hAccel = accelTable; + return NativeMethods.S_OK; + } + + /// + /// Implements IOleObject::GetExtent. + /// + internal void GetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + if ((dwDrawAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + Size size = control.Size; + + Point pt = PixelToHiMetric(size.Width, size.Height); + pSizel.cx = pt.X; + pSizel.cy = pt.Y; + } + else { + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + } + + /// + /// Searches the control hierarchy of the given control and adds + /// the mnemonics for each control to mnemonicList. Each mnemonic + /// is added as a char to the list. + /// + private void GetMnemonicList(Control control, ArrayList mnemonicList) { + // Get the mnemonic for our control + // + char mnemonic = WindowsFormsUtils.GetMnemonic(control.Text, true); + if (mnemonic != 0) { + mnemonicList.Add(mnemonic); + } + + // And recurse for our children. + // + foreach(Control c in control.Controls) { + if (c != null) GetMnemonicList(c, mnemonicList); + } + } + + /// + /// Name to use for a stream: use the control's type name (max 31 chars, use the end chars + /// if it's longer than that) + /// + private string GetStreamName() + { + string streamName = control.GetType().FullName; + int len = streamName.Length; + if (len > 31) + { // The max allowed length of the stream name is 31. + streamName = streamName.Substring(len - 31); + } + return streamName; + } + + /// + /// Implements IOleWindow::GetWindow + /// + internal int GetWindow(out IntPtr hwnd) { + if (!activeXState[inPlaceActive]) { + hwnd = IntPtr.Zero; + return NativeMethods.E_FAIL; + } + hwnd = control.Handle; + return NativeMethods.S_OK; + } + + /// + /// Converts coordinates in HiMetric to pixels. Used for ActiveX sourcing. + /// + /// + private Point HiMetricToPixel(int x, int y) { + Point pt = new Point(); + pt.X = (LogPixels.X * x + hiMetricPerInch / 2) / hiMetricPerInch; + pt.Y = (LogPixels.Y * y + hiMetricPerInch / 2) / hiMetricPerInch; + return pt; + } + + /// + /// + /// In place activates this Object. + /// + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal void InPlaceActivate(int verb) + { + // If we don't have a client site, then there's not much to do. + // We also punt if this isn't an in-place site, since we can't + // go active then. + // + UnsafeNativeMethods.IOleInPlaceSite inPlaceSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (inPlaceSite == null) + { + return; + } + + // If we're not already active, go and do it. + // + if (!activeXState[inPlaceActive]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> inplaceactive"); + + int hr = inPlaceSite.CanInPlaceActivate(); + + if (hr != NativeMethods.S_OK) { + if (NativeMethods.Succeeded(hr)) { + hr = NativeMethods.E_FAIL; + } + ThrowHr(hr); + } + + inPlaceSite.OnInPlaceActivate(); + + activeXState[inPlaceActive] = true; + } + + + // And if we're not visible, do that too. + // + if (!activeXState[inPlaceVisible]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> inplacevisible"); + NativeMethods.tagOIFI inPlaceFrameInfo = new NativeMethods.tagOIFI(); + inPlaceFrameInfo.cb = UnsafeNativeMethods.SizeOf(typeof(NativeMethods.tagOIFI)); + IntPtr hwndParent = IntPtr.Zero; + + // We are entering a secure context here. + // + hwndParent = inPlaceSite.GetWindow(); + + UnsafeNativeMethods.IOleInPlaceFrame pFrame; + UnsafeNativeMethods.IOleInPlaceUIWindow pWindow; + NativeMethods.COMRECT posRect = new NativeMethods.COMRECT(); + NativeMethods.COMRECT clipRect = new NativeMethods.COMRECT(); + + if (inPlaceUiWindow != null && UnsafeNativeMethods.IsComObject(inPlaceUiWindow)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceUiWindow); + inPlaceUiWindow = null; + } + + if (inPlaceFrame != null && UnsafeNativeMethods.IsComObject(inPlaceFrame)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceFrame); + inPlaceFrame = null; + } + + inPlaceSite.GetWindowContext(out pFrame, out pWindow, posRect, clipRect, inPlaceFrameInfo); + + SetObjectRects(posRect, clipRect); + + inPlaceFrame = pFrame; + inPlaceUiWindow = pWindow; + + // We are parenting ourselves + // directly to the host window. The host must + // implement the ambient property + // DISPID_AMBIENT_MESSAGEREFLECT. + // If it doesn't, that means that the host + // won't reflect messages back to us. + // + this.hwndParent = hwndParent; + UnsafeNativeMethods.SetParent(new HandleRef(control, control.Handle), new HandleRef(null, hwndParent)); + + // Now create our handle if it hasn't already been done. Note that because + // this raises events to the user that it CANNOT be done with a security assertion + // in place! + // + control.CreateControl(); + + clientSite.ShowObject(); + + SetInPlaceVisible(true); + Debug.Assert(activeXState[inPlaceVisible], "Failed to set inplacevisible"); + } + + // if we weren't asked to UIActivate, then we're done. + // + if (verb != NativeMethods.OLEIVERB_PRIMARY && verb != NativeMethods.OLEIVERB_UIACTIVATE) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> not becoming UIActive"); + return; + } + + // if we're not already UI active, do sow now. + // + if (!activeXState[uiActive]) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> uiactive"); + activeXState[uiActive] = true; + + // inform the container of our intent + // + inPlaceSite.OnUIActivate(); + + // take the focus [which is what UI Activation is all about !] + // + if (!control.ContainsFocus) control.FocusInternal(); + + // set ourselves up in the host. + // + Debug.Assert(inPlaceFrame != null, "Setting us to visible should have created the in place frame"); + inPlaceFrame.SetActiveObject(control, null); + if (inPlaceUiWindow != null) { + inPlaceUiWindow.SetActiveObject(control, null); + } + + // we have to explicitly say we don't wany any border space. + // + int hr = inPlaceFrame.SetBorderSpace(null); + if (NativeMethods.Failed(hr) && hr != NativeMethods.OLE_E_INVALIDRECT && + hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + + if (inPlaceUiWindow != null) { + hr = inPlaceFrame.SetBorderSpace(null); + if (NativeMethods.Failed(hr) && hr != NativeMethods.OLE_E_INVALIDRECT && + hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + } + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceVerbose, "\tActiveXImpl:InPlaceActivate --> already uiactive"); + } + } + + /// + /// + /// Implements IOleInPlaceObject::InPlaceDeactivate. + /// + internal void InPlaceDeactivate() { + + // Only do this if we're already in place active. + // + if (!activeXState[inPlaceActive]) { + return; + } + + // Deactivate us if we're UI active + // + if (activeXState[uiActive]) { + UIDeactivate(); + } + + // Some containers may call into us to save, and if we're still + // active we will try to deactivate and recurse back into the container. + // So, set the state bits here first. + // + activeXState[inPlaceActive] = false; + activeXState[inPlaceVisible] = false; + + // Notify our site of our deactivation. + // + UnsafeNativeMethods.IOleInPlaceSite oleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (oleClientSite != null) { + IntSecurity.UnmanagedCode.Assert(); + try { + oleClientSite.OnInPlaceDeactivate(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + control.Visible = false; + hwndParent = IntPtr.Zero; + + if (inPlaceUiWindow != null && UnsafeNativeMethods.IsComObject(inPlaceUiWindow)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceUiWindow); + inPlaceUiWindow = null; + } + + if (inPlaceFrame != null && UnsafeNativeMethods.IsComObject(inPlaceFrame)) { + UnsafeNativeMethods.ReleaseComObject(inPlaceFrame); + inPlaceFrame = null; + } + } + + /// + /// Implements IPersistStreamInit::IsDirty. + /// + internal int IsDirty() { + if (activeXState[isDirty]) { + return NativeMethods.S_OK; + } + else { + return NativeMethods.S_FALSE; + } + } + + /// + /// Looks at the property to see if it should be loaded / saved as a resource or + /// through a type converter. + /// + private bool IsResourceProp(PropertyDescriptor prop) { + TypeConverter converter = prop.Converter; + Type[] convertTypes = new Type[] { + typeof(string), + typeof(byte[]) + }; + + foreach (Type t in convertTypes) { + if (converter.CanConvertTo(t) && converter.CanConvertFrom(t)) { + return false; + } + } + + // Finally, if the property can be serialized, it is a resource property. + // + return (prop.GetValue(control) is ISerializable); + } + + /// + /// + /// Implements IPersistStorage::Load + /// + internal void Load(UnsafeNativeMethods.IStorage stg) { + UnsafeNativeMethods.IStream stream = null; + + IntSecurity.UnmanagedCode.Assert(); + try { + stream = stg.OpenStream(this.GetStreamName(), IntPtr.Zero, NativeMethods.STGM_READ | NativeMethods.STGM_SHARE_EXCLUSIVE, 0); + } + catch (COMException e) { + if (e.ErrorCode == NativeMethods.STG_E_FILENOTFOUND) { + // For backward compatibility: We were earlier using GetType().FullName + // as the stream name in v1. Lets see if a stream by that name exists. + stream = stg.OpenStream(GetType().FullName, IntPtr.Zero, NativeMethods.STGM_READ | NativeMethods.STGM_SHARE_EXCLUSIVE, 0); + } + else { + throw; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + Load(stream); + stream = null; + if (UnsafeNativeMethods.IsComObject(stg)) { + UnsafeNativeMethods.ReleaseComObject(stg); + } + } + + /// + /// + /// Implements IPersistStreamInit::Load + /// + internal void Load(UnsafeNativeMethods.IStream stream) { + // We do everything through property bags because we support full fidelity + // in them. So, load through that method. + // + PropertyBagStream bag = new PropertyBagStream(); + bag.Read(stream); + Load(bag, null); + + if (UnsafeNativeMethods.IsComObject(stream)) { + UnsafeNativeMethods.ReleaseComObject(stream); + } + } + + /// + /// Implements IPersistPropertyBag::Load + /// + internal void Load(UnsafeNativeMethods.IPropertyBag pPropBag, UnsafeNativeMethods.IErrorLog pErrorLog) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(control, + new Attribute[] {DesignerSerializationVisibilityAttribute.Visible}); + + for (int i = 0; i < props.Count; i++) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Loading property " + props[i].Name); + + try { + object obj = null; + int hr = NativeMethods.E_FAIL; + + IntSecurity.UnmanagedCode.Assert(); + try { + hr = pPropBag.Read(props[i].Name, ref obj, pErrorLog); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (NativeMethods.Succeeded(hr) && obj != null) { + Debug.Indent(); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Property was in bag"); + + String errorString = null; + int errorCode = 0; + + try { + if (obj.GetType() != typeof(string)) { + Debug.Fail("Expected property " + props[i].Name + " to be stored in IPropertyBag as a string. Attempting to coerce"); + obj = Convert.ToString(obj, CultureInfo.InvariantCulture); + } + + // Determine if this is a resource property or was persisted via a type converter. + // + if (IsResourceProp(props[i])) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "It's a resource property"); + + // Resource property. We encode these as base 64 strings. To load them, we convert + // to a binary blob and then de-serialize. + // + byte[] bytes = Convert.FromBase64String(obj.ToString()); + MemoryStream stream = new MemoryStream(bytes); + BinaryFormatter formatter = new BinaryFormatter(); + props[i].SetValue(control, formatter.Deserialize(stream)); + } + else { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "It's a standard property"); + + // Not a resource property. Use TypeConverters to convert the string back to the data type. We do + // not check for CanConvertFrom here -- we the conversion fails the type converter will throw, + // and we will log it into the COM error log. + // + TypeConverter converter = props[i].Converter; + Debug.Assert(converter != null, "No type converter for property '" + props[i].Name + "' on class " + control.GetType().FullName); + + // Check to see if the type converter can convert from a string. If it can,. + // use that as it is the best format for IPropertyBag. Otherwise, check to see + // if it can convert from a byte array. If it can, get the string, decode it + // to a byte array, and then set the value. + // + object value = null; + + if (converter.CanConvertFrom(typeof(string))) { + value = converter.ConvertFromInvariantString(obj.ToString()); + } + else if (converter.CanConvertFrom(typeof(byte[]))) { + string objString = obj.ToString(); + value = converter.ConvertFrom(null, CultureInfo.InvariantCulture, FromBase64WrappedString(objString)); + } + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Converter returned " + value); + props[i].SetValue(control, value); + } + } + catch (Exception e) { + errorString = e.ToString(); + if (e is ExternalException) { + errorCode = ((ExternalException) e).ErrorCode; + } + else { + errorCode = NativeMethods.E_FAIL; + } + } + if (errorString != null) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Exception converting property: " + errorString); + if (pErrorLog != null) { + NativeMethods.tagEXCEPINFO err = new NativeMethods.tagEXCEPINFO(); + err.bstrSource = control.GetType().FullName; + err.bstrDescription = errorString; + err.scode = errorCode; + pErrorLog.AddError(props[i].Name, err); + } + } + Debug.Unindent(); + } + } + catch (Exception ex) { + Debug.Fail("Unexpected failure reading property", ex.ToString()); + + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + if (UnsafeNativeMethods.IsComObject(pPropBag)) { + UnsafeNativeMethods.ReleaseComObject(pPropBag); + } + } + + /// + /// Simple lookup to find the AmbientProperty corresponding to the given + /// dispid. + /// + private AmbientProperty LookupAmbient(int dispid) { + for (int i = 0; i < ambientProperties.Length; i++) { + if (ambientProperties[i].DispID == dispid) { + return ambientProperties[i]; + } + } + Debug.Fail("No ambient property for dispid " + dispid.ToString(CultureInfo.InvariantCulture)); + return ambientProperties[0]; + } + + /// + /// Merges the input region with the current clipping region. + /// The output is always a region that can be fed directly + /// to SetWindowRgn. The region does not have to be destroyed. + /// The original region is destroyed if a new region is returned. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal IntPtr MergeRegion(IntPtr region) { + if (clipRegion == IntPtr.Zero) { + return region; + } + + if (region == IntPtr.Zero) { + return clipRegion; + } + + try { + // + + IntPtr newRegion = SafeNativeMethods.CreateRectRgn(0, 0, 0, 0); + try { + SafeNativeMethods.CombineRgn(new HandleRef(null, newRegion), new HandleRef(null, region), new HandleRef(this, clipRegion), NativeMethods.RGN_DIFF); + SafeNativeMethods.DeleteObject(new HandleRef(null, region)); + } + catch { + SafeNativeMethods.DeleteObject(new HandleRef(null, newRegion)); + throw; + } + return newRegion; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // If something goes wrong, use the original region. + return region; + } + } + + private void CallParentPropertyChanged(Control control, string propName) { + switch (propName) { + case "BackColor" : + control.OnParentBackColorChanged(EventArgs.Empty); + break; + + case "BackgroundImage" : + control.OnParentBackgroundImageChanged(EventArgs.Empty); + break; + + case "BindingContext" : + control.OnParentBindingContextChanged(EventArgs.Empty); + break; + + case "Enabled" : + control.OnParentEnabledChanged(EventArgs.Empty); + break; + + case "Font" : + control.OnParentFontChanged(EventArgs.Empty); + break; + + case "ForeColor" : + control.OnParentForeColorChanged(EventArgs.Empty); + break; + + case "RightToLeft" : + control.OnParentRightToLeftChanged(EventArgs.Empty); + break; + + case "Visible" : + control.OnParentVisibleChanged(EventArgs.Empty); + break; + + default: + Debug.Fail("There is no property change notification for: " + propName + " on Control."); + break; + } + } + + /// + /// + /// Implements IOleControl::OnAmbientPropertyChanged + /// + internal void OnAmbientPropertyChange(int dispID) { + if (dispID != NativeMethods.ActiveX.DISPID_UNKNOWN) { + + // Look for a specific property that has changed. + // + for (int i = 0; i < ambientProperties.Length; i++) { + if (ambientProperties[i].DispID == dispID) { + ambientProperties[i].ResetValue(); + CallParentPropertyChanged(control, ambientProperties[i].Name); + return; + } + } + + // Special properties that we care about + // + Object obj = new Object(); + + switch (dispID) { + case NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD: + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + activeXState[uiDead] = (bool)obj; + } + break; + + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT: + IButtonControl ibuttonControl = control as IButtonControl; + if (ibuttonControl != null && GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT, ref obj)) { + ibuttonControl.NotifyDefault((bool)obj); + } + break; + } + } + else { + // Invalidate all properties. Ideally we should be checking each one, but + // that's pretty expensive too. + // + for (int i = 0; i < ambientProperties.Length; i++) { + ambientProperties[i].ResetValue(); + CallParentPropertyChanged(control, ambientProperties[i].Name); + } + } + } + + /// + /// + /// Implements IOleInPlaceActiveObject::OnDocWindowActivate. + /// + internal void OnDocWindowActivate(int fActivate) { + if (activeXState[uiActive] && fActivate != 0 && inPlaceFrame != null) { + // we have to explicitly say we don't wany any border space. + // + int hr; + IntSecurity.UnmanagedCode.Assert(); + try { + hr = inPlaceFrame.SetBorderSpace(null); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (NativeMethods.Failed(hr) && hr != NativeMethods.INPLACE_E_NOTOOLSPACE && hr != NativeMethods.E_NOTIMPL) { + UnsafeNativeMethods.ThrowExceptionForHR(hr); + } + } + } + + /// + /// Called by Control when it gets the focus. + /// + internal void OnFocus(bool focus) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AXSource: SetFocus: " + focus.ToString()); + if (activeXState[inPlaceActive] && clientSite is UnsafeNativeMethods.IOleControlSite) { + IntSecurity.UnmanagedCode.Assert(); + try { + ((UnsafeNativeMethods.IOleControlSite)clientSite).OnFocus(focus ? 1 : 0); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + if (focus && activeXState[inPlaceActive] && !activeXState[uiActive]) { + InPlaceActivate(NativeMethods.OLEIVERB_UIACTIVATE); + } + } + + /// + /// Converts coordinates in pixels to HiMetric. + /// + /// + private Point PixelToHiMetric(int x, int y) { + Point pt = new Point(); + pt.X = (hiMetricPerInch * x + (LogPixels.X >> 1)) / LogPixels.X; + pt.Y = (hiMetricPerInch * y + (LogPixels.Y >> 1)) / LogPixels.Y; + return pt; + } + + /// + /// + /// Our implementation of IQuickActivate::QuickActivate + /// + internal void QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl) { + // Hookup our ambient colors + // + AmbientProperty prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + prop.Value = ColorTranslator.FromOle(unchecked((int)pQaContainer.colorBack)); + + prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + prop.Value = ColorTranslator.FromOle(unchecked((int)pQaContainer.colorFore)); + + // And our ambient font + // + if (pQaContainer.pFont != null) { + + prop = LookupAmbient(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + + IntSecurity.UnmanagedCode.Assert(); + try + { + IntPtr hfont = IntPtr.Zero; + object objfont = pQaContainer.pFont; + UnsafeNativeMethods.IFont ifont = (UnsafeNativeMethods.IFont)objfont; + hfont = ifont.GetHFont(); + Font font = Font.FromHfont(hfont); + prop.Value = font; + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Do NULL, so we just defer to the default font + prop.Value = null; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + // Now use the rest of the goo that we got passed in. + // + int status; + + pQaControl.cbSize = UnsafeNativeMethods.SizeOf(typeof(UnsafeNativeMethods.tagQACONTROL)); + + SetClientSite(pQaContainer.pClientSite); + + if (pQaContainer.pAdviseSink != null) { + SetAdvise(NativeMethods.DVASPECT_CONTENT, 0, (IAdviseSink)pQaContainer.pAdviseSink); + } + + IntSecurity.UnmanagedCode.Assert(); + try { + ((UnsafeNativeMethods.IOleObject)control).GetMiscStatus(NativeMethods.DVASPECT_CONTENT, out status); + } + finally { + CodeAccessPermission.RevertAssert(); + } + pQaControl.dwMiscStatus = status; + + // VSWhidbey 591430. Advise the event sink so VB6 can catch events raised from UserControls. + // VB6 expects the control to do this during IQuickActivate, otherwise it will not hook events at runtime. + // We will do this if all of the following are true: + // 1. The container (e.g., vb6) has supplied an event sink + // 2. The control is a UserControl (this is only to limit the scope of the changed behavior) + // 3. The UserControl has indicated it wants to expose events to COM via the ComSourceInterfacesAttribute + // Note that the AdviseHelper handles some non-standard COM interop that is required in order to access + // the events on the CLR-supplied CCW (COM-callable Wrapper. + + if ((pQaContainer.pUnkEventSink != null) && (control is UserControl)) { + // Check if this control exposes events to COM. + Type eventInterface = GetDefaultEventsInterface(control.GetType()); + + if (eventInterface != null) { + + IntSecurity.UnmanagedCode.Assert(); + try { + // For the default source interface, call IConnectionPoint.Advise with the supplied event sink. + // This is easier said than done. See notes in AdviseHelper.AdviseConnectionPoint. + AdviseHelper.AdviseConnectionPoint(control, pQaContainer.pUnkEventSink, eventInterface, out pQaControl.dwEventCookie); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + if (pQaContainer.pPropertyNotifySink != null && UnsafeNativeMethods.IsComObject(pQaContainer.pPropertyNotifySink)) { + UnsafeNativeMethods.ReleaseComObject(pQaContainer.pPropertyNotifySink); + } + + if (pQaContainer.pUnkEventSink != null && UnsafeNativeMethods.IsComObject(pQaContainer.pUnkEventSink)) { + UnsafeNativeMethods.ReleaseComObject(pQaContainer.pUnkEventSink); + } + } + + /// + /// Helper class. Calls IConnectionPoint.Advise to hook up a native COM event sink + /// to a manage .NET event interface. + /// The events are exposed to COM by the CLR-supplied COM-callable Wrapper (CCW). + /// + internal static class AdviseHelper { + /// + /// Get the COM connection point container from the CLR's CCW and advise for the given event id. + /// + public static bool AdviseConnectionPoint(object connectionPoint, object sink, Type eventInterface, out int cookie) { + + // Note that we cannot simply cast the connectionPoint object to + // System.Runtime.InteropServices.ComTypes.IConnectionPointContainer because the .NET + // object doesn't implement it directly. When the object is exposed to COM, the CLR + // implements IConnectionPointContainer on the proxy object called the CCW or COM-callable wrapper. + // We use the helper class ComConnectionPointContainer to get to the CCW directly + // to to call the interface. + // It is critical to call Dispose to ensure that the IUnknown is released. + + using (ComConnectionPointContainer cpc = new ComConnectionPointContainer(connectionPoint, true)) { + return AdviseConnectionPoint(cpc, sink, eventInterface, out cookie); + } + } + + /// + /// Find the COM connection point and call Advise for the given event id. + /// + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")] + internal static bool AdviseConnectionPoint(ComConnectionPointContainer cpc, object sink, Type eventInterface, out int cookie) { + + // Note that we cannot simply cast the returned IConnectionPoint to + // System.Runtime.InteropServices.ComTypes.IConnectionPoint because the .NET + // object doesn't implement it directly. When the object is exposed to COM, the CLR + // implements IConnectionPoint for the proxy object via the CCW or COM-callable wrapper. + // We use the helper class ComConnectionPoint to get to the CCW directly to to call the interface. + // It is critical to call Dispose to ensure that the IUnknown is released. + + using (ComConnectionPoint cp = cpc.FindConnectionPoint(eventInterface)) { + using (SafeIUnknown punkEventsSink = new SafeIUnknown(sink, true)) { + // Finally...we can call IConnectionPoint.Advise to hook up a native COM event sink + // to a managed .NET event interface. + return cp.Advise(punkEventsSink.DangerousGetHandle(), out cookie); + } + } + } + + /// + /// Wraps a native IUnknown in a SafeHandle. + /// See similar implementaton in the class. + /// + internal class SafeIUnknown : SafeHandle { + + /// + /// Wrap an incomoing unknown or get the unknown for the CCW (COM-callable wrapper). + /// + public SafeIUnknown(object obj, bool addRefIntPtr) + : this(obj, addRefIntPtr, Guid.Empty) { + } + + /// + /// Wrap an incomoing unknown or get the unknown for the CCW (COM-callable wrapper). + /// If an iid is supplied, QI for the interface and wrap that unknonwn instead. + /// + public SafeIUnknown(object obj, bool addRefIntPtr, Guid iid) + : base(IntPtr.Zero, true) { + + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); + try { + + // Set this.handle in a finally block to ensure the com ptr is set in the SafeHandle + // even if the runtime throws a exception (such as ThreadAbortException) during the call. + // This ensures that the finalizer will clean up the COM reference. + } + finally { + + // Get a raw IUnknown for this object. + // We are responsible for releasing the IUnknown ourselves. + IntPtr unknown; + + if (obj is IntPtr) { + unknown = (IntPtr)obj; + + // The incoming IntPtr may already be reference counted or not, depending on + // where it came from. The caller needs to tell us whether to add-ref or not. + if (addRefIntPtr) { + Marshal.AddRef(unknown); + } + + } else { + // GetIUnknownForObject will return a reference-counted object + unknown = Marshal.GetIUnknownForObject(obj); + } + + // Attempt QueryInterface if an iid is specified. + if (iid != Guid.Empty) { + IntPtr oldUnknown = unknown; + try { + unknown = InternalQueryInterface(unknown, ref iid); + } + finally { + // It is critical to release the original unknown if + // InternalQueryInterface throws out so we don't leak ref counts. + Marshal.Release(oldUnknown); + } + } + + // Preserve the com ptr in the SafeHandle. + this.handle = unknown; + } + } + + /// + /// Helper function for QueryInterface. + /// + private static IntPtr InternalQueryInterface(IntPtr pUnk, ref Guid iid) { + IntPtr ppv; + int hresult = Marshal.QueryInterface(pUnk, ref iid, out ppv); + if (hresult != 0 || ppv == IntPtr.Zero) { + throw new InvalidCastException(SR.GetString(SR.AxInterfaceNotSupported)); + } + return ppv; + } + + /// + /// Return whether the handle is invalid. + /// + public sealed override bool IsInvalid { + get { + if (!base.IsClosed) { + return (IntPtr.Zero == base.handle); + } + return true; + } + } + + /// + /// Release the IUnknown. + /// + protected sealed override bool ReleaseHandle() { + IntPtr ptr1 = base.handle; + base.handle = IntPtr.Zero; + if (IntPtr.Zero != ptr1) { + Marshal.Release(ptr1); + } + return true; + } + + /// + /// Helper function to load a COM v-table from a com object pointer. + /// + protected V LoadVtable() { + IntPtr vtblptr = Marshal.ReadIntPtr(this.handle, 0); + return (V)Marshal.PtrToStructure(vtblptr, typeof(V)); + } + } + + /// + /// Helper class to access IConnectionPointContainer from a .NET COM-callable wrapper. + /// The IConnectionPointContainer COM pointer is wrapped in a SafeHandle. + /// + internal sealed class ComConnectionPointContainer + : SafeIUnknown { + + public ComConnectionPointContainer(object obj, bool addRefIntPtr) + : base(obj, addRefIntPtr, typeof(System.Runtime.InteropServices.ComTypes.IConnectionPointContainer).GUID) { + this.vtbl = base.LoadVtable(); + } + + private VTABLE vtbl; + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + [StructLayout(LayoutKind.Sequential)] + private class VTABLE { + public IntPtr QueryInterfacePtr; + public IntPtr AddRefPtr; + public IntPtr ReleasePtr; + public IntPtr EnumConnectionPointsPtr; + public IntPtr FindConnectionPointPtr; + } + + /// + /// Call IConnectionPointContainer.FindConnectionPoint using Delegate.Invoke on the v-table slot. + /// + public ComConnectionPoint FindConnectionPoint(Type eventInterface) { + FindConnectionPointD findConnectionPoint = (FindConnectionPointD)Marshal.GetDelegateForFunctionPointer(this.vtbl.FindConnectionPointPtr, typeof(FindConnectionPointD)); + IntPtr result = IntPtr.Zero; + + Guid iid = eventInterface.GUID; + int hresult = findConnectionPoint.Invoke(this.handle, ref iid, out result); + if (hresult != 0 || result == IntPtr.Zero) { + throw new ArgumentException(SR.GetString(SR.AXNoConnectionPoint, eventInterface.Name)); + } + + return new ComConnectionPoint(result, false); // result is already ref-counted as an out-param so pass in false + } + + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] + private delegate int FindConnectionPointD(IntPtr This, ref Guid iid, out IntPtr ppv); + } + + /// + /// Helper class to access IConnectionPoint from a .NET COM-callable wrapper. + /// The IConnectionPoint COM pointer is wrapped in a SafeHandle. + /// + internal sealed class ComConnectionPoint + : SafeIUnknown { + + public ComConnectionPoint(object obj, bool addRefIntPtr) + : base(obj, addRefIntPtr, typeof(System.Runtime.InteropServices.ComTypes.IConnectionPoint).GUID) { + this.vtbl = this.LoadVtable(); + } + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + [StructLayout(LayoutKind.Sequential)] + private class VTABLE { + public IntPtr QueryInterfacePtr; + public IntPtr AddRefPtr; + public IntPtr ReleasePtr; + public IntPtr GetConnectionInterfacePtr; + public IntPtr GetConnectionPointContainterPtr; + public IntPtr AdvisePtr; + public IntPtr UnadvisePtr; + public IntPtr EnumConnectionsPtr; + } + + private VTABLE vtbl; + + /// + /// Call IConnectioinPoint.Advise using Delegate.Invoke on the v-table slot. + /// + public bool Advise(IntPtr punkEventSink, out int cookie) { + AdviseD advise = (AdviseD)Marshal.GetDelegateForFunctionPointer(this.vtbl.AdvisePtr, typeof(AdviseD)); + if (advise.Invoke(this.handle, punkEventSink, out cookie) == 0) { + return true; + } + return false; + } + + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] + private delegate int AdviseD(IntPtr This, IntPtr punkEventSink, out int cookie); + } + + } + + /// + /// Return the default COM events interface declared on a .NET class. + /// This looks for the ComSourceInterfacesAttribute and returns the .NET + /// interface type of the first interface declared. + /// + private static Type GetDefaultEventsInterface(Type controlType) { + + Type eventInterface = null; + object[] custom = controlType.GetCustomAttributes(typeof(ComSourceInterfacesAttribute), false); + + if (custom.Length > 0) { + ComSourceInterfacesAttribute coms = (ComSourceInterfacesAttribute)custom[0]; + string eventName = coms.Value.Split(new char[] { '\0' })[0]; + eventInterface = controlType.Module.Assembly.GetType(eventName, false); + if (eventInterface == null) { + eventInterface = Type.GetType(eventName, false); + } + } + + return eventInterface; + } + + + /// + /// + /// Implements IPersistStorage::Save + /// + internal void Save(UnsafeNativeMethods.IStorage stg, bool fSameAsLoad) { + UnsafeNativeMethods.IStream stream = null; + + IntSecurity.UnmanagedCode.Assert(); + try { + stream = stg.CreateStream(this.GetStreamName(), NativeMethods.STGM_WRITE | NativeMethods.STGM_SHARE_EXCLUSIVE | NativeMethods.STGM_CREATE, 0, 0); + } + finally { + CodeAccessPermission.RevertAssert(); + } + Debug.Assert(stream != null, "Stream should be non-null, or an exception should have been thrown."); + Save(stream, true); + UnsafeNativeMethods.ReleaseComObject(stream); + } + + /// + /// + /// Implements IPersistStreamInit::Save + /// + internal void Save(UnsafeNativeMethods.IStream stream, bool fClearDirty) { + // We do everything through property bags because we support full fidelity + // in them. So, save through that method. + // + PropertyBagStream bag = new PropertyBagStream(); + Save(bag, fClearDirty, false); + IntSecurity.UnmanagedCode.Assert(); + try { + bag.Write(stream); + } + finally { + CodeAccessPermission.RevertAssert(); + } + if (UnsafeNativeMethods.IsComObject(stream)) { + UnsafeNativeMethods.ReleaseComObject(stream); + } + } + + /// + /// Implements IPersistPropertyBag::Save + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void Save(UnsafeNativeMethods.IPropertyBag pPropBag, bool fClearDirty, bool fSaveAllProperties) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(control, + new Attribute[] {DesignerSerializationVisibilityAttribute.Visible}); + + for (int i = 0; i < props.Count; i++) { + if (fSaveAllProperties || props[i].ShouldSerializeValue(control)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Saving property " + props[i].Name); + + object propValue; + + if (IsResourceProp(props[i])) { + + // Resource property. Save this to the bag as a 64bit encoded string. + // + MemoryStream stream = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, props[i].GetValue(control)); + byte[] bytes = new byte[(int)stream.Length]; + stream.Position = 0; + stream.Read(bytes, 0, bytes.Length); + propValue = Convert.ToBase64String(bytes); + pPropBag.Write(props[i].Name, ref propValue); + } + else { + + // Not a resource property. Persist this using standard type converters. + // + TypeConverter converter = props[i].Converter; + Debug.Assert(converter != null, "No type converter for property '" + props[i].Name + "' on class " + control.GetType().FullName); + + if (converter.CanConvertFrom(typeof(string))) { + propValue = converter.ConvertToInvariantString(props[i].GetValue(control)); + pPropBag.Write(props[i].Name, ref propValue); + } + else if (converter.CanConvertFrom(typeof(byte[]))) { + byte[] data = (byte[])converter.ConvertTo(null, CultureInfo.InvariantCulture, props[i].GetValue(control), typeof(byte[])); + propValue = Convert.ToBase64String(data); + pPropBag.Write(props[i].Name, ref propValue); + } + } + } + } + + if (UnsafeNativeMethods.IsComObject(pPropBag)) { + UnsafeNativeMethods.ReleaseComObject(pPropBag); + } + + if (fClearDirty) { + activeXState[isDirty] = false; + } + } + + /// + /// Fires the OnSave event to all of our IAdviseSink + /// listeners. Used for ActiveXSourcing. + /// + /// + private void SendOnSave() { + int cnt = adviseList.Count; + IntSecurity.UnmanagedCode.Assert(); + for (int i = 0; i < cnt; i++) { + IAdviseSink s = (IAdviseSink)adviseList[i]; + Debug.Assert(s != null, "NULL in our advise list"); + s.OnSave(); + } + } + + /// + /// + /// Implements IViewObject2::SetAdvise. + /// + internal void SetAdvise(int aspects, int advf, IAdviseSink pAdvSink) { + // if it's not a content aspect, we don't support it. + // + if ((aspects & NativeMethods.DVASPECT_CONTENT) == 0) { + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + + // set up some flags [we gotta stash for GetAdvise ...] + // + activeXState[viewAdvisePrimeFirst] = (advf & NativeMethods.ADVF_PRIMEFIRST) != 0 ? true : false; + activeXState[viewAdviseOnlyOnce] = (advf & NativeMethods.ADVF_ONLYONCE) != 0 ? true : false; + + if (viewAdviseSink != null && UnsafeNativeMethods.IsComObject(viewAdviseSink)) { + UnsafeNativeMethods.ReleaseComObject(viewAdviseSink); + } + + viewAdviseSink = pAdvSink; + + // prime them if they want it [we need to store this so they can get flags later] + // + if (activeXState[viewAdvisePrimeFirst]) { + ViewChanged(); + } + } + + /// + /// + /// Implements IOleObject::SetClientSite. + /// + internal void SetClientSite(UnsafeNativeMethods.IOleClientSite value) { + if (clientSite != null) { + + if (value == null) { + globalActiveXCount--; + + if (globalActiveXCount == 0 && IsIE) { + + // This the last ActiveX control and we are + // being hosted in IE. Use private reflection + // to ask SystemEvents to shutdown. This is to + // prevent a crash (ASURT 139932). + + // SECREVIEW : The SystemEvents class has a LinkDemand for FullTrust permission but it becomes a full demand + // on a method called using private reflection. We need to assert here. + // SystemEvents.Shutdown is a private method intended to be called using private reflection (See commnets + // on the method definition); it sends a WM_QUIT message to the app broadcast window. + // So it is safe to assert here. + // + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + MethodInfo method = typeof(SystemEvents).GetMethod("Shutdown", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, + null, new Type[0], new ParameterModifier[0]); + Debug.Assert(method != null, "No Shutdown method on SystemEvents"); + if (method != null) { + method.Invoke(null, null); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + if (UnsafeNativeMethods.IsComObject(clientSite)) { + IntSecurity.UnmanagedCode.Assert(); + try { + Marshal.FinalReleaseComObject(clientSite); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + clientSite = value; + + if (clientSite != null) { + control.Site = new AxSourcingSite(control, clientSite, "ControlAxSourcingSite"); + } + else { + control.Site = null; + } + + // Get the ambient properties that effect us... + // + Object obj = new Object(); + if (GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + activeXState[uiDead] = (bool)obj; + } + + if (control is IButtonControl && GetAmbientProperty(NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD, ref obj)) { + ((IButtonControl)control).NotifyDefault((bool)obj); + } + + if (clientSite == null) { + if (accelTable != IntPtr.Zero) { + UnsafeNativeMethods.DestroyAcceleratorTable(new HandleRef(this, accelTable)); + accelTable = IntPtr.Zero; + accelCount = -1; + } + + if (IsIE) { + this.control.Dispose(); + } + } + else { + globalActiveXCount++; + + if (globalActiveXCount == 1 && IsIE) { + + // This the first ActiveX control and we are + // being hosted in IE. Use private reflection + // to ask SystemEvents to start. Startup will only + // restart system events if we previously shut it down. + // This is to prevent a crash (ASURT 139932). + // + // SECREVIEW : The SystemEvents class has a LinkDemand for FullTrust permission but it becomes a full demand + // on a method called using private reflection. We need to assert here. + // SystemEvents.Shutdown is a private method intended to be called using private reflection (See commnets + // on the method definition); it sends a WM_QUIT message to the app broadcast window. + // So it is safe to assert here. + // + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + MethodInfo method = typeof(SystemEvents).GetMethod("Startup", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, + null, new Type[0], new ParameterModifier[0]); + Debug.Assert(method != null, "No Startup method on SystemEvents"); + if (method != null) { + method.Invoke(null, null); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + control.OnTopMostActiveXParentChanged(EventArgs.Empty); + } + + /// + /// Implements IOleObject::SetExtent + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void SetExtent(int dwDrawAspect, NativeMethods.tagSIZEL pSizel) { + if ((dwDrawAspect & NativeMethods.DVASPECT_CONTENT) != 0) { + + if (activeXState[changingExtents]) { + return; + } + + activeXState[changingExtents] = true; + + try { + Size size = new Size(HiMetricToPixel(pSizel.cx, pSizel.cy)); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : new size:" + size.ToString()); + + // If we're in place active, let the in place site set our bounds. + // Otherwise, just set it on our control directly. + // + if (activeXState[inPlaceActive]) { + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + Rectangle bounds = control.Bounds; + bounds.Location = new Point(bounds.X, bounds.Y); + Size adjusted = new Size(size.Width, size.Height); + bounds.Width = adjusted.Width; + bounds.Height = adjusted.Height; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Announcing to in place site that our rect has changed."); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, " Announcing rect = " + bounds); + Debug.Assert(clientSite != null, "How can we setextent before we are sited??"); + + ioleClientSite.OnPosRectChange(NativeMethods.COMRECT.FromXYWH(bounds.X, bounds.Y, bounds.Width, bounds.Height)); + } + } + + control.Size = size; + + // Check to see if the control overwrote our size with + // its own values. + // + if (!control.Size.Equals(size)) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Control has changed size. Setting dirty bit"); + activeXState[isDirty] = true; + + // If we're not inplace active, then anounce that the view changed. + // + if (!activeXState[inPlaceActive]) { + ViewChanged(); + } + + // We need to call RequestNewObjectLayout + // here so we visually display our new extents. + // + if (!activeXState[inPlaceActive] && clientSite != null) { + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "SetExtent : Requesting new Object layout."); + clientSite.RequestNewObjectLayout(); + } + } + } + finally { + activeXState[changingExtents] = false; + } + } + else { + // We don't support any other aspects + // + ThrowHr(NativeMethods.DV_E_DVASPECT); + } + } + + /// + /// Marks our state as in place visible. + /// + private void SetInPlaceVisible(bool visible) { + activeXState[inPlaceVisible] = visible; + control.Visible = visible; + } + + /// + /// Implements IOleInPlaceObject::SetObjectRects. + /// + internal void SetObjectRects(NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect) { + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("SetObjectRects:"); + Debug.Indent(); + + if (lprcPosRect != null) { + Debug.WriteLine("PosLeft: " + lprcPosRect.left.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosTop: " + lprcPosRect.top.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosRight: " + lprcPosRect.right.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("PosBottom: " + lprcPosRect.bottom.ToString(CultureInfo.InvariantCulture)); + } + + if (lprcClipRect != null) { + Debug.WriteLine("ClipLeft: " + lprcClipRect.left.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipTop: " + lprcClipRect.top.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipRight: " + lprcClipRect.right.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLine("ClipBottom: " + lprcClipRect.bottom.ToString(CultureInfo.InvariantCulture)); + } + Debug.Unindent(); + } +#endif + + Rectangle posRect = Rectangle.FromLTRB(lprcPosRect.left, lprcPosRect.top, lprcPosRect.right, lprcPosRect.bottom); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Set control bounds: " + posRect.ToString()); + + // ActiveX expects to be notified when a control's bounds change, and also + // intends to notify us through SetObjectRects when we report that the + // bounds are about to change. We implement this all on a control's Bounds + // property, which doesn't use this callback mechanism. The adjustRect + // member handles this. If it is non-null, then we are being called in + // response to an OnPosRectChange call. In this case we do not + // set the control bounds but set the bounds on the adjustRect. When + // this returns from the container and comes back to our OnPosRectChange + // implementation, these new bounds will be handed back to the control + // for the actual window change. + // + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Old Control Bounds: " + control.Bounds); + if (activeXState[adjustingRect]) { + adjustRect.left = posRect.X; + adjustRect.top = posRect.Y; + adjustRect.right = posRect.Width + posRect.X; + adjustRect.bottom = posRect.Height + posRect.Y; + } + else { + activeXState[adjustingRect] = true; + try { + control.Bounds = posRect; + } + finally { + activeXState[adjustingRect] = false; + } + } + + bool setRegion = false; + + if (clipRegion != IntPtr.Zero) { + // Bad -- after calling SetWindowReg, windows owns the region. + //SafeNativeMethods.DeleteObject(clipRegion); + clipRegion = IntPtr.Zero; + setRegion = true; + } + + if (lprcClipRect != null) { + + // The container wants us to clip, so figure out if we really + // need to. + // + Rectangle clipRect = Rectangle.FromLTRB(lprcClipRect.left, lprcClipRect.top, lprcClipRect.right, lprcClipRect.bottom); + + Rectangle intersect; + + // Trident always sends an empty ClipRect... and so, we check for that and not do an + // intersect in that case. + // + if (!clipRect.IsEmpty) + intersect = Rectangle.Intersect(posRect, clipRect); + else + intersect = posRect; + + if (!intersect.Equals(posRect)) { + + // Offset the rectangle back to client coordinates + // + NativeMethods.RECT rcIntersect = NativeMethods.RECT.FromXYWH(intersect.X, intersect.Y, intersect.Width, intersect.Height); + IntPtr hWndParent = UnsafeNativeMethods.GetParent(new HandleRef(control, control.Handle)); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Old Intersect: " + new Rectangle(rcIntersect.left, rcIntersect.top, rcIntersect.right-rcIntersect.left, rcIntersect.bottom-rcIntersect.top)); + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "New Control Bounds: " + posRect); + + UnsafeNativeMethods.MapWindowPoints(new HandleRef(null, hWndParent), new HandleRef(control, control.Handle), ref rcIntersect, 2); + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "New Intersect: " + new Rectangle(rcIntersect.left, rcIntersect.top, rcIntersect.right-rcIntersect.left, rcIntersect.bottom-rcIntersect.top)); + + // Create a Win32 region for it + // + clipRegion = SafeNativeMethods.CreateRectRgn(rcIntersect.left, rcIntersect.top, + rcIntersect.right, rcIntersect.bottom); + setRegion = true; + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "Created clipping region"); + } + } + + // If our region has changed, set the new value. We only do this if + // the handle has been created, since otherwise the control will + // merge our region automatically. + // + if (setRegion && control.IsHandleCreated) { + IntPtr finalClipRegion = clipRegion; + + Region controlRegion = control.Region; + if (controlRegion != null) { + IntPtr rgn = control.GetHRgn(controlRegion); + finalClipRegion = MergeRegion(rgn); + } + + UnsafeNativeMethods.SetWindowRgn(new HandleRef(control, control.Handle), new HandleRef(this, finalClipRegion), SafeNativeMethods.IsWindowVisible(new HandleRef(control, control.Handle))); + } + + // Yuck. Forms^3 uses transparent overlay windows that appear to cause + // painting artifacts. Flicker like a banshee. + // + control.Invalidate(); + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// Shows a property page dialog. + /// + private void ShowProperties() { + if (propPage == null) { + propPage = new ActiveXPropPage(control); + } + + if (inPlaceFrame != null) { + inPlaceFrame.EnableModeless(0); + } + try { + propPage.Edit(control); + } + finally { + if (inPlaceFrame != null) { + inPlaceFrame.EnableModeless(1); + } + } + } + #endif + + /// + /// + /// Throws the given hresult. This is used by ActiveX sourcing. + /// + internal static void ThrowHr(int hr) { + ExternalException e = new ExternalException(SR.GetString(SR.ExternalException), hr); + throw e; + } + + /// + /// + /// Handles IOleControl::TranslateAccelerator + /// + internal int TranslateAccelerator(ref NativeMethods.MSG lpmsg) { + + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + if (!control.IsHandleCreated) { + Debug.WriteLine("AxSource: TranslateAccelerator before handle creation"); + } + else { + Message m = Message.Create(lpmsg.hwnd, lpmsg.message, lpmsg.wParam, lpmsg.lParam); + Debug.WriteLine("AxSource: TranslateAccelerator : " + m.ToString()); + } + } +#endif // DEBUG + + bool needPreProcess = false; + + switch (lpmsg.message) { + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_SYSKEYDOWN: + case NativeMethods.WM_CHAR: + case NativeMethods.WM_SYSCHAR: + needPreProcess = true; + break; + } + + Message msg = Message.Create(lpmsg.hwnd, lpmsg.message, lpmsg.wParam, lpmsg.lParam); + + if (needPreProcess) { + Control target = Control.FromChildHandleInternal(lpmsg.hwnd); + if (target != null && (control == target || control.Contains(target))) { + PreProcessControlState messageState = PreProcessControlMessageInternal(target, ref msg); + switch (messageState) { + case PreProcessControlState.MessageProcessed: + // someone returned true from PreProcessMessage + // no need to dispatch the message, its already been coped with. + lpmsg.message = msg.Msg; + lpmsg.wParam = msg.WParam; + lpmsg.lParam = msg.LParam; + return NativeMethods.S_OK; + case PreProcessControlState.MessageNeeded: + // VSWhidbey 515519: Here we need to dispatch the message ourselves + // otherwise the host may never send the key to our wndproc. + + // Someone returned true from IsInputKey or IsInputChar + UnsafeNativeMethods.TranslateMessage(ref lpmsg); + if (SafeNativeMethods.IsWindowUnicode(new HandleRef(null, lpmsg.hwnd))) { + UnsafeNativeMethods.DispatchMessageW(ref lpmsg); + } + else { + UnsafeNativeMethods.DispatchMessageA(ref lpmsg); + } + return NativeMethods.S_OK; + case PreProcessControlState.MessageNotNeeded: + // in this case we'll check the site to see if it wants the message. + break; + } + } + } + + + // SITE processing. We're not interested in the message, but the site may be. + + int hr = NativeMethods.S_FALSE; + + Debug.WriteLineIf(CompModSwitches.ActiveX.TraceInfo, "AxSource: Control did not process accelerator, handing to site"); + + UnsafeNativeMethods.IOleControlSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleControlSite; + if (ioleClientSite != null) { + int keyState = 0; + + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_SHIFT) < 0) keyState |= 1; + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_CONTROL) < 0) keyState |= 2; + if (UnsafeNativeMethods.GetKeyState(NativeMethods.VK_MENU) < 0) keyState |= 4; + IntSecurity.UnmanagedCode.Assert(); + try { + hr = ioleClientSite.TranslateAccelerator(ref lpmsg, keyState); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + return hr; + } + + /// + /// + /// Implements IOleInPlaceObject::UIDeactivate. + /// + internal int UIDeactivate() { + // Only do this if we're UI active + // + if (!activeXState[uiActive]) { + return NativeMethods.S_OK; + } + + activeXState[uiActive] = false; + + // Notify frame windows, if appropriate, that we're no longer ui-active. + // + if (inPlaceUiWindow != null) { + inPlaceUiWindow.SetActiveObject(null, null); + } + + //May need this for SetActiveObject & OnUIDeactivate, so leave until function return + IntSecurity.UnmanagedCode.Assert(); + Debug.Assert(inPlaceFrame != null, "No inplace frame -- how dod we go UI active?"); + inPlaceFrame.SetActiveObject(null, null); + + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + ioleClientSite.OnUIDeactivate(0); + } + + return NativeMethods.S_OK; + } + + /// + /// + /// Implements IOleObject::Unadvise + /// + internal void Unadvise(int dwConnection) { + if (dwConnection > adviseList.Count || adviseList[dwConnection - 1] == null) { + ThrowHr(NativeMethods.OLE_E_NOCONNECTION); + } + + IAdviseSink sink = (IAdviseSink)adviseList[dwConnection - 1]; + adviseList.RemoveAt(dwConnection - 1); + if (sink != null && UnsafeNativeMethods.IsComObject(sink)) { + UnsafeNativeMethods.ReleaseComObject(sink); + } + } + + /// + /// Notifies our site that we have changed our size and location. + /// + /// + internal void UpdateBounds(ref int x, ref int y, ref int width, ref int height, int flags) { + if (!activeXState[adjustingRect] && activeXState[inPlaceVisible]) { + UnsafeNativeMethods.IOleInPlaceSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleInPlaceSite; + if (ioleClientSite != null) { + NativeMethods.COMRECT rc = new NativeMethods.COMRECT(); + + if ((flags & NativeMethods.SWP_NOMOVE) != 0) { + rc.left = control.Left; + rc.top = control.Top; + } + else { + rc.left = x; + rc.top = y; + } + + if ((flags & NativeMethods.SWP_NOSIZE) != 0) { + rc.right = rc.left + control.Width; + rc.bottom = rc.top + control.Height; + } + else { + rc.right = rc.left + width; + rc.bottom = rc.top + height; + } + + // This member variable may be modified by SetObjectRects by the container. + adjustRect = rc; + activeXState[adjustingRect] = true; + + IntSecurity.UnmanagedCode.Assert(); + try { + ioleClientSite.OnPosRectChange(rc); + } + finally { + CodeAccessPermission.RevertAssert(); + adjustRect = null; + activeXState[adjustingRect] = false; + } + + // On output, the new bounds will be reflected in rc + if ((flags & NativeMethods.SWP_NOMOVE) == 0) { + x = rc.left; + y = rc.top; + } + if ((flags & NativeMethods.SWP_NOSIZE) == 0) { + width = rc.right - rc.left; + height = rc.bottom - rc.top; + } + } + } + } + + /// + /// Notifies that the accelerator table needs to be updated due to a change in a control mnemonic. + /// + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + internal void UpdateAccelTable(){ + // Setting the count to -1 will recreate the table on demand (when GetControlInfo is called). + this.accelCount = -1; + + UnsafeNativeMethods.IOleControlSite ioleClientSite = clientSite as UnsafeNativeMethods.IOleControlSite; + if (ioleClientSite != null) { + IntSecurity.UnmanagedCode.Assert(); + ioleClientSite.OnControlInfoChanged(); + } + } + + // Since this method is used by Reflection .. dont change the "signature" + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + internal void ViewChangedInternal() + { + ViewChanged(); + } + + /// + /// Notifies our view advise sink (if it exists) that the view has + /// changed. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + private void ViewChanged() { + // send the view change notification to anybody listening. + // + // Note: Word2000 won't resize components correctly if an OnViewChange notification + // is sent while the component is persisting it's state. The !m_fSaving check + // is to make sure we don't call OnViewChange in this case. + // + if (viewAdviseSink != null && !activeXState[saving]) { + IntSecurity.UnmanagedCode.Assert(); + try { + viewAdviseSink.OnViewChange(NativeMethods.DVASPECT_CONTENT, -1); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (activeXState[viewAdviseOnlyOnce]) { + if (UnsafeNativeMethods.IsComObject(viewAdviseSink)) { + UnsafeNativeMethods.ReleaseComObject(viewAdviseSink); + } + viewAdviseSink = null; + } + } + } + + /// + /// Called when the window handle of the control has changed. + /// + void IWindowTarget.OnHandleChange(IntPtr newHandle) { + controlWindowTarget.OnHandleChange(newHandle); + } + + /// + /// Called to do control-specific processing for this window. + /// + void IWindowTarget.OnMessage(ref Message m) { + if (activeXState[uiDead]) { + if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST) { + return; + } + if (m.Msg >= NativeMethods.WM_NCLBUTTONDOWN && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK) { + return; + } + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + return; + } + } + + IntSecurity.UnmanagedCode.Assert(); + controlWindowTarget.OnMessage(ref m); + } + + /// + /// This is a property bag implementation that sits on a stream. It can + /// read and write the bag to the stream. + /// + private class PropertyBagStream : UnsafeNativeMethods.IPropertyBag { + private Hashtable bag = new Hashtable(); + + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal void Read(UnsafeNativeMethods.IStream istream) { + // visual basic's memory streams don't support seeking, so we have to + // work around this limitation here. We do this by copying + // the contents of the stream into a MemoryStream object. + // + Stream stream = new DataStreamFromComStream(istream); + const int PAGE_SIZE = 0x1000; // one page (4096b) + byte[] streamData = new byte[PAGE_SIZE]; + int offset = 0; + + int count = stream.Read(streamData, offset, PAGE_SIZE); + int totalCount = count; + + while (count == PAGE_SIZE) { + byte[] newChunk = new byte[streamData.Length + PAGE_SIZE]; + Array.Copy(streamData, newChunk, streamData.Length); + streamData = newChunk; + + offset += PAGE_SIZE; + count = stream.Read(streamData, offset, PAGE_SIZE); + totalCount += count; + } + + stream = new MemoryStream(streamData); + + BinaryFormatter formatter = new BinaryFormatter(); + try { + bag = (Hashtable)formatter.Deserialize(stream); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + // Error reading. Just init an empty hashtable. + bag = new Hashtable(); + } + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IPropertyBag.Read(string pszPropName, ref object pVar, UnsafeNativeMethods.IErrorLog pErrorLog) + { + if (!bag.Contains(pszPropName)) + return NativeMethods.E_INVALIDARG; + + pVar = bag[pszPropName]; + return NativeMethods.S_OK; + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + int UnsafeNativeMethods.IPropertyBag.Write(string pszPropName, ref object pVar) + { + bag[pszPropName] = pVar; + return NativeMethods.S_OK; + } + + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal void Write(UnsafeNativeMethods.IStream istream) + { + Stream stream = new DataStreamFromComStream(istream); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, bag); + } + } + } + + private class AxSourcingSite : ISite { + private IComponent component; + private UnsafeNativeMethods.IOleClientSite clientSite; + private string name; + private HtmlShimManager shimManager; + + + internal AxSourcingSite(IComponent component, UnsafeNativeMethods.IOleClientSite clientSite, string name) { + this.component = component; + this.clientSite = clientSite; + this.name = name; + } + + /** The component sited by this component site. */ + public IComponent Component { + get { + return component; + } + } + + /** The container in which the component is sited. */ + public IContainer Container { + get { + return null; + } + } + + public Object GetService(Type service) { + object retVal = null; + + if (service == typeof(HtmlDocument)) { + + UnsafeNativeMethods.IOleContainer iOlecontainer; + int hr; + try { + // SECREVIEW : UnsafeNativeMethods.IOleClientSite demands UnmnagedCode permission like most of our COM interfaces + // so we need to assert here. This assert is safe, we are checking for specific services we support + // to return it to the user (COM interface). + // + IntSecurity.UnmanagedCode.Assert(); + hr = clientSite.GetContainer(out iOlecontainer); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + + if (NativeMethods.Succeeded(hr) + && (iOlecontainer is UnsafeNativeMethods.IHTMLDocument)) { + if (shimManager == null) { + shimManager = new HtmlShimManager(); + } + + // Security note: There is a demand for FullTrust on HtmlDocument that should apply + // to this call. So we don't need to demand here. + retVal = new HtmlDocument(shimManager, iOlecontainer as UnsafeNativeMethods.IHTMLDocument); + } + + } + else if (clientSite.GetType().IsAssignableFrom(service)) { + IntSecurity.UnmanagedCode.Demand(); + retVal = clientSite; + } + + return retVal; + } + + + /** Indicates whether the component is in design mode. */ + public bool DesignMode { + get { + return false; + } + } + + /** + * The name of the component. + */ + public String Name { + get { return name;} + set { + if (value == null || name == null) { + name = value; + } + } + } + } + + /// + /// This is a marshaler object that knows how to marshal IFont to Font + /// and back. + /// + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + private class ActiveXFontMarshaler : ICustomMarshaler { + + private static ActiveXFontMarshaler instance; + + public void CleanUpManagedData(object obj) { + } + + public void CleanUpNativeData(IntPtr pObj) { + Marshal.Release(pObj); + } + + internal static ICustomMarshaler GetInstance(string cookie) { + if (instance == null) { + instance = new ActiveXFontMarshaler(); + } + return instance; + } + public int GetNativeDataSize() { + return -1; // not a value type, so use -1 + } + + public IntPtr MarshalManagedToNative(object obj) { + Font font = (Font)obj; + NativeMethods.tagFONTDESC fontDesc = new NativeMethods.tagFONTDESC(); + NativeMethods.LOGFONT logFont = new NativeMethods.LOGFONT(); + + // SECREVIEW : The Font.ToLogFont method is marked with the 'unsafe' modifier cause it needs to write bytes into the logFont struct, + // here we are passing that struct so the assert is safe. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font.ToLogFont(logFont); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + fontDesc.lpstrName = font.Name; + fontDesc.cySize = (long)(font.SizeInPoints * 10000); + fontDesc.sWeight = (short)logFont.lfWeight; + fontDesc.sCharset = logFont.lfCharSet; + fontDesc.fItalic = font.Italic; + fontDesc.fUnderline = font.Underline; + fontDesc.fStrikethrough = font.Strikeout; + + Guid iid = typeof(UnsafeNativeMethods.IFont).GUID; + + UnsafeNativeMethods.IFont oleFont = UnsafeNativeMethods.OleCreateFontIndirect(fontDesc, ref iid); + IntPtr pFont = Marshal.GetIUnknownForObject(oleFont); + IntPtr pIFont; + + int hr = Marshal.QueryInterface(pFont, ref iid, out pIFont); + + Marshal.Release(pFont); + + if (NativeMethods.Failed(hr)) { + Marshal.ThrowExceptionForHR(hr); + } + + return pIFont; + } + + public object MarshalNativeToManaged(IntPtr pObj) { + UnsafeNativeMethods.IFont nativeFont = (UnsafeNativeMethods.IFont)Marshal.GetObjectForIUnknown(pObj); + IntPtr hfont = IntPtr.Zero; + + IntSecurity.UnmanagedCode.Assert(); + try { + hfont = nativeFont.GetHFont(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + Font font = null; + + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font = Font.FromHfont(hfont); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + font = Control.DefaultFont; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return font; + } + } + + /// + /// Simple verb enumerator. + /// + private class ActiveXVerbEnum : UnsafeNativeMethods.IEnumOLEVERB { + private NativeMethods.tagOLEVERB[] verbs; + private int current; + + internal ActiveXVerbEnum(NativeMethods.tagOLEVERB[] verbs) { + this.verbs = verbs; + current = 0; + } + + public int Next(int celt, NativeMethods.tagOLEVERB rgelt, int[] pceltFetched) { + int fetched = 0; + + if (celt != 1) { + Debug.Fail("Caller of IEnumOLEVERB requested celt > 1, but clr marshalling does not support this."); + celt = 1; + } + + while (celt > 0 && current < verbs.Length) { + rgelt.lVerb = verbs[current].lVerb; + rgelt.lpszVerbName = verbs[current].lpszVerbName; + rgelt.fuFlags = verbs[current].fuFlags; + rgelt.grfAttribs = verbs[current].grfAttribs; + celt--; + current++; + fetched++; + } + + if (pceltFetched != null) { + pceltFetched[0] = fetched; + } + +#if DEBUG + if (CompModSwitches.ActiveX.TraceInfo) { + Debug.WriteLine("AxSource:IEnumOLEVERB::Next returning " + fetched.ToString(CultureInfo.InvariantCulture) + " verbs:"); + Debug.Indent(); + for (int i = current - fetched; i < current; i++) { + Debug.WriteLine(i.ToString(CultureInfo.InvariantCulture) + ": " + verbs[i].lVerb + " " + (verbs[i].lpszVerbName == null ? string.Empty : verbs[i].lpszVerbName)); + } + Debug.Unindent(); + } +#endif + return(celt == 0 ? NativeMethods.S_OK : NativeMethods.S_FALSE); + } + + public int Skip(int celt) { + if (current + celt < verbs.Length) { + current += celt; + return NativeMethods.S_OK; + } + else { + current = verbs.Length; + return NativeMethods.S_FALSE; + } + } + + public void Reset() { + current = 0; + } + + public void Clone(out UnsafeNativeMethods.IEnumOLEVERB ppenum) { + ppenum = new ActiveXVerbEnum(verbs); + } + } + + #if ACTIVEX_SOURCING + + // + // This has been cut from the product. + // + + /// + /// The properties window we display. + /// + private class ActiveXPropPage { + private Form form; + private PropertyGrid grid; + private ComponentEditor compEditor; + + internal ActiveXPropPage(object component) { + compEditor = (ComponentEditor)TypeDescriptor.GetEditor(component, typeof(ComponentEditor)); + + if (compEditor == null) { + form = new Form(); + grid = new PropertyGrid(); + + form.Text = SR.GetString(SR.AXProperties); + form.StartPosition = FormStartPosition.CenterParent; + form.Size = new Size(300, 350); + form.FormBorderStyle = FormBorderStyle.Sizable; + form.MinimizeBox = false; + form.MaximizeBox = false; + form.ControlBox = true; + form.SizeGripStyle = SizeGripStyle.Show; + form.DockPadding.Bottom = 16; // size grip size + + Bitmap bitmap = new Bitmap(grid.GetType(), "PropertyGrid.bmp"); + bitmap.MakeTransparent(); + form.Icon = Icon.FromHandle(bitmap.GetHicon()); + + grid.Dock = DockStyle.Fill; + + form.Controls.Add(grid); + } + } + + internal void Edit(object editingObject) { + if (compEditor != null) { + compEditor.EditComponent(null, editingObject); + } + else { + grid.SelectedObject = editingObject; + form.ShowDialog(); + } + } + } + + #endif + + /// + /// Contains a single ambient property, including DISPID, name and value. + /// + private class AmbientProperty { + private string name; + private int dispID; + private Object value; + private bool empty; + + /// + /// + /// Creates a new, empty ambient property. + /// + internal AmbientProperty(string name, int dispID) { + this.name = name; + this.dispID = dispID; + this.value = null; + this.empty = true; + } + + /// + /// + /// The windows forms property name. + /// + internal string Name { + get { + return name; + } + } + + /// + /// + /// The DispID for the property. + /// + internal int DispID { + get { + return dispID; + } + } + + /// + /// + /// Returns true if this property has not been set. + /// + internal bool Empty { + get { + return empty; + } + } + + /// + /// + /// The current value of the property. + /// + internal Object Value { + get { + return value; + } + set { + this.value = value; + empty = false; + } + } + + /// + /// + /// Resets the property. + /// + internal void ResetValue() { + empty = true; + value = null; + } + } + + /// + /// MetafileDCWrapper is used to wrap a metafile DC so that subsequent + /// paint operations are rendered to a temporary bitmap. When the + /// wrapper is disposed, it copies the bitmap back to the metafile DC. + /// + /// Example: + /// + /// using(MetafileDCWrapper dcWrapper = new MetafileDCWrapper(hDC, size) { + /// // ...use dcWrapper.HDC to do painting + /// } + /// + /// + private class MetafileDCWrapper : IDisposable { + + HandleRef hBitmapDC = NativeMethods.NullHandleRef; + HandleRef hBitmap = NativeMethods.NullHandleRef; + HandleRef hOriginalBmp = NativeMethods.NullHandleRef; + HandleRef hMetafileDC = NativeMethods.NullHandleRef; + NativeMethods.RECT destRect; + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Process | ResourceScope.Machine)] + internal MetafileDCWrapper(HandleRef hOriginalDC, Size size) { + Debug.Assert(UnsafeNativeMethods.GetObjectType(hOriginalDC) == NativeMethods.OBJ_ENHMETADC, + "Why wrap a non-Enhanced MetaFile DC?"); + + // Security fix: make sure the size has non-negative width and height. + if (size.Width < 0 || size.Height < 0) { + throw new ArgumentException("size", SR.GetString(SR.ControlMetaFileDCWrapperSizeInvalid)); + } + + hMetafileDC = hOriginalDC; + destRect = new NativeMethods.RECT(0, 0, size.Width, size.Height); + hBitmapDC = new HandleRef(this, UnsafeNativeMethods.CreateCompatibleDC(NativeMethods.NullHandleRef)); + + int planes = UnsafeNativeMethods.GetDeviceCaps(hBitmapDC, NativeMethods.PLANES); + int bitsPixel = UnsafeNativeMethods.GetDeviceCaps(hBitmapDC, NativeMethods.BITSPIXEL); + hBitmap = new HandleRef(this, SafeNativeMethods.CreateBitmap(size.Width, size.Height, planes, bitsPixel, IntPtr.Zero)); + + hOriginalBmp = new HandleRef(this, SafeNativeMethods.SelectObject(hBitmapDC, hBitmap)); + } + + ~MetafileDCWrapper() { + ((IDisposable) this).Dispose(); + } + + void IDisposable.Dispose() { + if (hBitmapDC.Handle == IntPtr.Zero || hMetafileDC.Handle == IntPtr.Zero || hBitmap.Handle == IntPtr.Zero) { + return; + } + + bool success; + + try { + success = DICopy(hMetafileDC, hBitmapDC, destRect, true); + Debug.Assert(success, "DICopy() failed."); + SafeNativeMethods.SelectObject(hBitmapDC, hOriginalBmp); + success = SafeNativeMethods.DeleteObject(hBitmap); + Debug.Assert(success, "DeleteObject() failed."); + success = UnsafeNativeMethods.DeleteCompatibleDC(hBitmapDC); + Debug.Assert(success, "DeleteObject() failed."); + } + finally { + + // Dispose is done. Set all the handles to IntPtr.Zero so this way the Dispose method executes only once. + hBitmapDC = NativeMethods.NullHandleRef; + hBitmap = NativeMethods.NullHandleRef; + hOriginalBmp = NativeMethods.NullHandleRef; + + GC.SuppressFinalize(this); + } + } + + internal IntPtr HDC { + get { return hBitmapDC.Handle; } + } + + // ported form VB6 (Ctls\PortUtil\StdCtl.cpp:6176) + private unsafe bool DICopy(HandleRef hdcDest, HandleRef hdcSrc, NativeMethods.RECT rect, bool bStretch) { + long i; + + bool fSuccess = false; // Assume failure + + // + // Get the bitmap from the DC by selecting in a 1x1 pixel temp bitmap + HandleRef hNullBitmap = new HandleRef(this, SafeNativeMethods.CreateBitmap(1, 1, 1, 1, IntPtr.Zero)); + if (hNullBitmap.Handle == IntPtr.Zero) return fSuccess; + + try { + HandleRef hBitmap = new HandleRef(this, SafeNativeMethods.SelectObject(hdcSrc, hNullBitmap)); + if (hBitmap.Handle == IntPtr.Zero) return fSuccess; + + // + // Restore original bitmap + SafeNativeMethods.SelectObject(hdcSrc, hBitmap); + + + NativeMethods.BITMAP bmp = new NativeMethods.BITMAP(); + if (UnsafeNativeMethods.GetObject(hBitmap, Marshal.SizeOf(bmp), bmp) == 0) { + return fSuccess; + } + + NativeMethods.BITMAPINFO_FLAT lpbmi = new NativeMethods.BITMAPINFO_FLAT(); + lpbmi.bmiHeader_biSize = Marshal.SizeOf(typeof(NativeMethods.BITMAPINFOHEADER)); + lpbmi.bmiHeader_biWidth = bmp.bmWidth; + lpbmi.bmiHeader_biHeight = bmp.bmHeight; + lpbmi.bmiHeader_biPlanes = 1; + lpbmi.bmiHeader_biBitCount = bmp.bmBitsPixel; + lpbmi.bmiHeader_biCompression = NativeMethods.BI_RGB; + lpbmi.bmiHeader_biSizeImage = 0; //Not needed since using BI_RGB + lpbmi.bmiHeader_biXPelsPerMeter = 0; + lpbmi.bmiHeader_biYPelsPerMeter = 0; + lpbmi.bmiHeader_biClrUsed = 0; + lpbmi.bmiHeader_biClrImportant = 0; + lpbmi.bmiColors = new byte[NativeMethods.BITMAPINFO_MAX_COLORSIZE*4]; + + // + // Include the palette for 256 color bitmaps + long iColors = 1 << (bmp.bmBitsPixel * bmp.bmPlanes); + if (iColors <= 256) { + byte[] aj = new byte[Marshal.SizeOf(typeof(NativeMethods.PALETTEENTRY)) * 256]; + SafeNativeMethods.GetSystemPaletteEntries(hdcSrc, 0, (int)iColors, aj); + + fixed (byte* pcolors = lpbmi.bmiColors) { + fixed (byte* ppal = aj) { + NativeMethods.RGBQUAD* prgb = (NativeMethods.RGBQUAD*)pcolors; + NativeMethods.PALETTEENTRY* lppe = (NativeMethods.PALETTEENTRY*)ppal; + // + // Convert the palette entries to RGB quad entries + for (i = 0; i < (int)iColors; i++) { + prgb[i].rgbRed = lppe[i].peRed; + prgb[i].rgbBlue = lppe[i].peBlue; + prgb[i].rgbGreen = lppe[i].peGreen; + } + } + } + } + + // + // Allocate memory to hold the bitmap bits + long bitsPerScanLine = bmp.bmBitsPixel * (long)bmp.bmWidth; + long bytesPerScanLine = (bitsPerScanLine + 7) / 8; + long totalBytesReqd = bytesPerScanLine * bmp.bmHeight; + byte[] lpBits = new byte[totalBytesReqd]; + + // + // Get the bitmap bits + int diRet = SafeNativeMethods.GetDIBits(hdcSrc, hBitmap, 0, bmp.bmHeight, lpBits, + ref lpbmi, NativeMethods.DIB_RGB_COLORS); + if (diRet == 0) return fSuccess; + + // + // Set the destination coordiates depending on whether stretch-to-fit was chosen + int xDest, yDest, cxDest, cyDest; + if (bStretch) + { + xDest = rect.left; + yDest = rect.top; + cxDest = rect.right - rect.left; + cyDest = rect.bottom - rect.top; + } + else + { + xDest = rect.left; + yDest = rect.top; + cxDest = bmp.bmWidth; + cyDest = bmp.bmHeight; + } + + // Paint the bitmap + int iRet = SafeNativeMethods.StretchDIBits(hdcDest, + xDest, yDest, cxDest, cyDest, 0, 0, bmp.bmWidth, bmp.bmHeight, + lpBits, ref lpbmi, NativeMethods.DIB_RGB_COLORS, NativeMethods.SRCCOPY); + if (iRet == NativeMethods.GDI_ERROR) return fSuccess; + + fSuccess = true; + } + finally { + SafeNativeMethods.DeleteObject(hNullBitmap); + } + return fSuccess; + } + } + + + /// + /// + /// An implementation of AccessibleChild for use with Controls + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ControlAccessibleObject : AccessibleObject { + + private static IntPtr oleAccAvailable = NativeMethods.InvalidIntPtr; + + // Member variables + + private IntPtr handle = IntPtr.Zero; // Associated window handle (if any) + private Control ownerControl = null; // The associated Control for this AccessibleChild (if any) + private int[] runtimeId = null; // Used by UIAutomation + + // constructors + + /// + /// + /// [To be supplied.] + /// + public ControlAccessibleObject(Control ownerControl) { + + Debug.Assert(ownerControl != null, "Cannot construct a ControlAccessibleObject with a null ownerControl"); + if (ownerControl == null) { + throw new ArgumentNullException("ownerControl"); + } + + this.ownerControl = ownerControl; + + // call get_Handle outside the UnmanagedCode permission because if the handle is not created yet, + // we will invoke 3rd party HandleCreated event handlers + IntPtr handle = ownerControl.Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + this.Handle = handle; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// + /// [To be supplied.] + /// + internal ControlAccessibleObject(Control ownerControl, int accObjId) { + + Debug.Assert(ownerControl != null, "Cannot construct a ControlAccessibleObject with a null ownerControl"); + if (ownerControl == null) { + throw new ArgumentNullException("ownerControl"); + } + + this.AccessibleObjectId = accObjId; // ...must set this *before* setting the Handle property + + this.ownerControl = ownerControl; + IntPtr handle = ownerControl.Handle; + + // Reviewed : ControlAccessibleObject.set_Handle demands UnmanagedCode permission for public use, it doesn't + // expose any security vulnerability indirectly. The sec Assert is safe. + // + IntSecurity.UnmanagedCode.Assert(); + try { + this.Handle = handle; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + /// + /// For container controls only, return array of child controls sorted into + /// tab index order. This gets applies to the list of child accessible objects + /// as returned by the system, so that we can present a meaningful order to + /// the user. The system defaults to z-order, which is bad for us because + /// that is usually the reverse of tab order! + /// + internal override int[] GetSysChildOrder() { + if (ownerControl.GetStyle(ControlStyles.ContainerControl)) + return ownerControl.GetChildWindowsInTabOrder(); + + return base.GetSysChildOrder(); + } + + /// + /// Perform custom navigation between parent/child/sibling accessible objects, + /// using tab index order as the guide, rather than letting the system default + /// behavior do navigation based on z-order. + /// + /// For a container control and its child controls, the accessible object tree + /// looks like this... + /// + /// [client area of container] + /// [non-client area of child #1] + /// [random non-client elements] + /// [client area of child #1] + /// [random non-client elements] + /// [non-client area of child #2] + /// [random non-client elements] + /// [client area of child #2] + /// [random non-client elements] + /// [non-client area of child #3] + /// [random non-client elements] + /// [client area of child #3] + /// [random non-client elements] + /// + /// We need to intercept first-child / last-child navigation from the container's + /// client object, and next-sibling / previous-sibling navigation from each child's + /// non-client object. All other navigation operations must be allowed to fall back + /// on the system's deafult behavior (provided by OLEACC.DLL). + /// + /// When combined with the re-ordering behavior of GetSysChildOrder() above, this + /// allows us to present the end user with the illusion of accessible objects in + /// tab index order, even though the system behavior only supports z-order. + /// + internal override bool GetSysChild(AccessibleNavigation navdir, out AccessibleObject accessibleObject) { + + // Clear the out parameter + accessibleObject = null; + + // Get the owning control's parent, if it has one + Control parentControl = ownerControl.ParentInternal; + + // ctrls[index] will indicate the control at the destination of this navigation operation + int index = -1; + Control[] ctrls = null; + + // Now handle any 'appropriate' navigation requests... + switch (navdir) { + + case AccessibleNavigation.FirstChild: + if (IsClientObject) { + ctrls = ownerControl.GetChildControlsInTabOrder(true); + index = 0; + } + break; + + case AccessibleNavigation.LastChild: + if (IsClientObject) { + ctrls = ownerControl.GetChildControlsInTabOrder(true); + index = ctrls.Length - 1; + } + break; + + case AccessibleNavigation.Previous: + if (IsNonClientObject && parentControl != null) { + ctrls = parentControl.GetChildControlsInTabOrder(true); + index = Array.IndexOf(ctrls, ownerControl); + if (index != -1) + --index; + } + break; + + case AccessibleNavigation.Next: + if (IsNonClientObject && parentControl != null) { + ctrls = parentControl.GetChildControlsInTabOrder(true); + index = Array.IndexOf(ctrls, ownerControl); + if (index != -1) + ++index; + } + break; + } + + // Unsupported navigation operation for this object, or unexpected error. + // Return false to force fall back on default system navigation behavior. + if (ctrls == null || ctrls.Length == 0) + return false; + + // If ctrls[index] is a valid control, return its non-client accessible object. + // If index is invalid, return null pointer meaning "end of list reached". + if (index >= 0 && index < ctrls.Length) + accessibleObject = ctrls[index].NcAccessibilityObject; + + // Return true to use the found accessible object and block default system behavior + return true; + } + + /// + public override string DefaultAction { + get { + string defaultAction = ownerControl.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + else { + return base.DefaultAction; + } + } + } + + // This is used only if control supports IAccessibleEx + internal override int[] RuntimeId { + get { + if (runtimeId == null) { + runtimeId = new int[2]; + runtimeId[0] = 0x2a; + runtimeId[1] = (int)(long)this.Handle; + } + + return runtimeId; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Description { + get { + string description = ownerControl.AccessibleDescription; + if (description != null) { + return description; + } + else { + return base.Description; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public IntPtr Handle { + + get { + return handle; + } + + set { + // Demand security permission for this! + IntSecurity.UnmanagedCode.Demand(); + + if (handle != value) { + handle = value; + + if (oleAccAvailable == IntPtr.Zero) { + return; + } + + bool freeLib = false; + + if (oleAccAvailable == NativeMethods.InvalidIntPtr) { + oleAccAvailable = UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable("oleacc.dll"); + freeLib = (oleAccAvailable != IntPtr.Zero); + } + + // Update systemIAccessible + // We need to store internally the system provided + // IAccessible, because some windows forms controls use it + // as the default IAccessible implementation. + if (handle != IntPtr.Zero && oleAccAvailable != IntPtr.Zero) { + UseStdAccessibleObjects(handle); + } + + if (freeLib) { + UnsafeNativeMethods.FreeLibrary(new HandleRef(null, oleAccAvailable)); + } + + } + } // end Handle.Set + + } // end Handle + + /// + /// + /// [To be supplied.] + /// + public override string Help { + get { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + return args.HelpString; + } + else { + return base.Help; + } + } + } + + /// + public override string KeyboardShortcut { + get { + // For controls, the default keyboard shortcut comes directly from the accessible + // name property. This matches the default behavior of OLEACC.DLL exactly. + char mnemonic = WindowsFormsUtils.GetMnemonic(this.TextLabel, false); + return (mnemonic == (char) 0) ? null : ("Alt+" + mnemonic); + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + // Special case: If an explicit name has been set in the AccessibleName property, use that. + // Note: Any non-null value in AccessibleName overrides the default accessible name logic, + // even an empty string (this is the only way to *force* the accessible name to be blank). + string name = ownerControl.AccessibleName; + if (name != null) { + return name; + } + + // Otherwise just return the default label string, minus any mnemonics + return WindowsFormsUtils.TextWithoutMnemonics(TextLabel); + } + + set { + // If anyone tries to set the accessible name, just cache the value in the control's + // AccessibleName property. This value will then end up overriding the normal accessible + // name logic, until such time as AccessibleName is set back to null. + ownerControl.AccessibleName = value; + } + } + + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return base.Parent; + } + } + + // Determine the text that should be used to 'label' this control for accessibility purposes. + // + // Prior to Whidbey, we just called 'base.Name' to determine the accessible name. This would end up calling + // OLEACC.DLL to determine the name. The rules used by OLEACC.DLL are the same as what we now have below, + // except that OLEACC searches for preceding labels using z-order, and we want to search for labels using + // TabIndex order. + // + internal string TextLabel { + get { + // If owning control allows, use its Text property - but only if that Text is not empty + if (ownerControl.GetStyle(ControlStyles.UseTextForAccessibility)) { + string text = ownerControl.Text; + if (!string.IsNullOrEmpty(text)) { + return text; + } + } + + // Otherwise use the text of the preceding Label control, if there is one + Label previousLabel = PreviousLabel; + if (previousLabel != null) { + string text = previousLabel.Text; + if (!string.IsNullOrEmpty(text)) { + return text; + } + } + + // This control has no discernable MSAA name - return an empty string to indiciate 'nameless'. + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public Control Owner { + get { + return ownerControl; + } + } + + // Look for a label immediately preceeding this control in + // the tab order, and use its name for the accessible name. + // + // This method aims to emulate the equivalent behavior in OLEACC.DLL, + // but walking controls in TabIndex order rather than native z-order. + // + internal Label PreviousLabel { + get { + // Try to get to the parent of this control. + Control parent = Owner.ParentInternal; + + if (parent == null) { + return null; + } + + // Find this control's containing control + ContainerControl container = parent.GetContainerControlInternal() as ContainerControl; + if (container == null) { + return null; + } + + // Walk backwards through peer controls... + for (Control previous = container.GetNextControl(Owner, false); + previous != null; + previous = container.GetNextControl(previous, false)) { + + // Stop when we hit a Label (whether visible or invisible) + if (previous is Label) { + return previous as Label; + } + + // Stop at any *visible* tab stop + if (previous.Visible && previous.TabStop) { + break; + } + } + + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = ownerControl.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return base.Role; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public override int GetHelpTopic(out string fileName) { + int topic = 0; + + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + + fileName = args.HelpNamespace; + if (!String.IsNullOrEmpty(fileName)) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + + try { + topic = Int32.Parse(args.HelpKeyword, CultureInfo.InvariantCulture); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + } + + return topic; + } + else { + return base.GetHelpTopic(out fileName); + } + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent) { + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + ", accEvent = " + accEvent.ToString() + ", childID = self"); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), NativeMethods.OBJID_CLIENT, 0); + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent, int childID) { + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + + ", accEvent = " + accEvent.ToString() + + ", childID = " + childID.ToString(CultureInfo.InvariantCulture)); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), NativeMethods.OBJID_CLIENT, childID + 1); + } + + /// + /// + /// [To be supplied.] + /// + public void NotifyClients(AccessibleEvents accEvent, int objectID, int childID) { + + Debug.WriteLineIf(CompModSwitches.MSAA.TraceInfo, "Control.NotifyClients: this = " + + this.ToString() + + ", accEvent = " + accEvent.ToString() + + ", childID = " + childID.ToString(CultureInfo.InvariantCulture)); + + UnsafeNativeMethods.NotifyWinEvent((int)accEvent, new HandleRef(this, Handle), objectID, childID + 1); + } + + /// + /// Raises the LiveRegionChanged UIA event. + /// To make this method effective, the control must implement System.Windows.Forms.Automation.IAutomationLiveRegion interface + /// and its LiveSetting property must return either AutomationLiveSetting.Polite or AutomationLiveSetting.Assertive value. + /// In addition, the applications must be recompiled to target .NET Framework 4.7.3 or opt in into this feature using compatibility switches. + /// + /// True if operation succeeds, False otherwise. + public override bool RaiseLiveRegionChanged() { + if (!(this.Owner is IAutomationLiveRegion)) { + throw new InvalidOperationException(SR.GetString(SR.OwnerControlIsNotALiveRegion)); + } + + return RaiseAutomationEvent(NativeMethods.UIA_LiveRegionChangedEventId); + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3 && this.Owner is IAutomationLiveRegion) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_LiveSettingPropertyId && Owner is IAutomationLiveRegion) { + return ((IAutomationLiveRegion)Owner).LiveSetting; + } + + if (Owner.SupportsUiaProviders) { + switch (propertyID) { + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return Owner.CanSelect; + case NativeMethods.UIA_IsOffscreenPropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HelpTextPropertyId: + var help = Help; + return AccessibilityImprovements.Level3 ? (help ?? string.Empty) : help; + } + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple HostRawElementProvider { + get { + if (AccessibilityImprovements.Level3) { + UnsafeNativeMethods.IRawElementProviderSimple provider; + UnsafeNativeMethods.UiaHostProviderFromHwnd(new HandleRef(this, Handle), out provider); + return provider; + } + + return base.HostRawElementProvider; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + if (Owner != null) { + return "ControlAccessibleObject: Owner = " + Owner.ToString(); + } + else { + return "ControlAccessibleObject: Owner = null"; + } + } + } + + + // Fonts can be a pain to track, so we wrap Hfonts in this class to get a Finalize method. + // + internal sealed class FontHandleWrapper : MarshalByRefObject, IDisposable { +#if DEBUG + private string stackOnCreate = null; + private string stackOnDispose = null; + private bool finalizing = false; +#endif + private IntPtr handle; + + internal FontHandleWrapper(Font font) { +#if DEBUG + if (CompModSwitches.LifetimeTracing.Enabled) stackOnCreate = new System.Diagnostics.StackTrace().ToString(); +#endif + handle = font.ToHfont(); + System.Internal.HandleCollector.Add(handle, NativeMethods.CommonHandles.GDI); + } + + internal IntPtr Handle { + get { + Debug.Assert(handle != IntPtr.Zero, "FontHandleWrapper disposed, but still being accessed"); + return handle; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) { + +#if DEBUG + Debug.Assert(finalizing || this != defaultFontHandleWrapper, "Don't dispose the defaultFontHandleWrapper"); +#endif + + if (handle != IntPtr.Zero) { +#if DEBUG + if (CompModSwitches.LifetimeTracing.Enabled) stackOnDispose = new System.Diagnostics.StackTrace().ToString(); +#endif + SafeNativeMethods.DeleteObject(new HandleRef(this, handle)); + handle = IntPtr.Zero; + } + + } + + ~FontHandleWrapper() { +#if DEBUG + finalizing = true; +#endif + Dispose(false); + } + } + + /// + /// Used with BeginInvoke/EndInvoke + /// + private class ThreadMethodEntry : IAsyncResult { + internal Control caller; + internal Control marshaler; + internal Delegate method; + internal Object[] args; + internal Object retVal; + internal Exception exception; + internal bool synchronous; + private bool isCompleted; + private ManualResetEvent resetEvent; + private object invokeSyncObject = new object(); + + // Store the execution context associated with the caller thread, and + // information about which thread actually got the stack applied to it. + // + internal ExecutionContext executionContext; + + // Optionally store the synchronization context associated with the callee thread. + // This overrides the sync context in the execution context of the caller thread. + // + internal SynchronizationContext syncContext = null; + + internal ThreadMethodEntry(Control caller, Control marshaler, Delegate method, Object[] args, bool synchronous, ExecutionContext executionContext) { + this.caller = caller; + this.marshaler = marshaler; + this.method = method; + this.args = args; + this.exception = null; + this.retVal = null; + this.synchronous = synchronous; + this.isCompleted = false; + this.resetEvent = null; + this.executionContext = executionContext; + } + + ~ThreadMethodEntry() + { + if (this.resetEvent != null) + { + this.resetEvent.Close(); + } + } + + public Object AsyncState { + get { + return null; + } + } + + public WaitHandle AsyncWaitHandle { + get { + if (this.resetEvent == null) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock (invokeSyncObject) { + // VSWhidbey 314175 BeginInvoke hangs on Multi-proc system: + // taking the lock prevents a race condition between IsCompleted + // boolean flag and resetEvent mutex in multiproc scenarios. + if (this.resetEvent == null) { + this.resetEvent = new ManualResetEvent(false); + if (this.isCompleted) { + this.resetEvent.Set(); + } + } + } + } + return(WaitHandle)this.resetEvent; + } + } + + public bool CompletedSynchronously { + get { + if (this.isCompleted && this.synchronous) + return true; + + return false; + } + } + + public bool IsCompleted { + get { + return this.isCompleted; + } + } + + internal void Complete() { + lock (invokeSyncObject) { + this.isCompleted = true; + if (this.resetEvent != null) { + this.resetEvent.Set(); + } + } + } + } + + private class ControlVersionInfo { + private string companyName = null; + private string productName = null; + private string productVersion = null; + private FileVersionInfo versionInfo = null; + private Control owner; + + internal ControlVersionInfo(Control owner) { + this.owner = owner; + } + + /// + /// + /// The company name associated with the component. + /// + internal string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (companyName == null) { + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attrs != null && attrs.Length > 0) { + companyName = ((AssemblyCompanyAttribute)attrs[0]).Company; + } + + if (companyName == null || companyName.Length == 0) { + companyName = GetFileVersionInfo().CompanyName; + if (companyName != null) { + companyName = companyName.Trim(); + } + } + + if (companyName == null || companyName.Length == 0) { + string ns = owner.GetType().Namespace; + + if (ns == null) { + ns = String.Empty; + } + + int firstDot = ns.IndexOf("/"); + if (firstDot != -1) { + companyName = ns.Substring(0, firstDot); + } + else { + companyName = ns; + } + } + } + return companyName; + } + } + + + /// + /// + /// The product name associated with this component. + /// + internal string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (productName == null) { + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attrs != null && attrs.Length > 0) { + productName = ((AssemblyProductAttribute)attrs[0]).Product; + } + + if (productName == null || productName.Length == 0) { + productName = GetFileVersionInfo().ProductName; + if (productName != null) { + productName = productName.Trim(); + } + } + + if (productName == null || productName.Length == 0) { + string ns = owner.GetType().Namespace; + + if (ns == null) { + ns = String.Empty; + } + int firstDot = ns.IndexOf("."); + if (firstDot != -1) { + productName = ns.Substring(firstDot+1); + } + else { + productName = ns; + } + } + } + + return productName; + } + } + + + /// + /// The product version associated with this component. + /// + internal string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (productVersion == null) { + // custom attribute + // + object[] attrs = owner.GetType().Module.Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); + if (attrs != null && attrs.Length > 0) { + productVersion = ((AssemblyInformationalVersionAttribute)attrs[0]).InformationalVersion; + } + + // win32 version info + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = GetFileVersionInfo().ProductVersion; + if (productVersion != null) { + productVersion = productVersion.Trim(); + } + } + + // fake it + // + if (productVersion == null || productVersion.Length == 0) { + productVersion = "1.0.0.0"; + } + } + return productVersion; + } + } + + + /// + /// Retrieves the FileVersionInfo associated with the main module for + /// the component. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private FileVersionInfo GetFileVersionInfo() { + if (versionInfo == null) { + string path; + + // SECREVIEW : sec assert is safe, the module name is obtained from the system, no user input involded. + // + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + try { + path = owner.GetType().Module.FullyQualifiedName; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + // SECREVIEW : sec assert is safe, the file version is obtained from the system, no user input involded. + // + new FileIOPermission(FileIOPermissionAccess.Read, path).Assert(); + try { + versionInfo = FileVersionInfo.GetVersionInfo(path); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + return versionInfo; + + } + + } + + private sealed class MultithreadSafeCallScope : IDisposable + { + // Use local stack variable rather than a refcount since we're + // guaranteed that these 'scopes' are properly nested. + private bool resultedInSet; + + internal MultithreadSafeCallScope() + { + // Only access the thread-local stuff if we're going to be + // checking for illegal thread calling (no need to incur the + // expense otherwise). + if (checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall) + { + inCrossThreadSafeCall = true; + resultedInSet = true; + } + else + { + resultedInSet = false; + } + } + + void IDisposable.Dispose() + { + if (resultedInSet) + { + inCrossThreadSafeCall = false; + } + } + } + + private sealed class PrintPaintEventArgs : PaintEventArgs { + Message m; + + internal PrintPaintEventArgs(Message m, IntPtr dc, Rectangle clipRect) + : base(dc, clipRect) { + this.m = m; + } + + internal Message Message { + get { + return m; + } + } + } + + } // end class Control + +} // end namespace System.Windows.Forms + + + + + diff --git a/WindowsForms/Managed/System/WinForms/ControlBindingsCollection.cs b/WindowsForms/Managed/System/WinForms/ControlBindingsCollection.cs new file mode 100644 index 000000000..f9d6ed009 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlBindingsCollection.cs @@ -0,0 +1,227 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.ComponentModel; + using System.Collections; + using System.Globalization; + + /// + /// + /// + /// Represents the collection of data bindings for a control. + /// + [DefaultEvent("CollectionChanged"), + Editor("System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing, typeof(System.Drawing.Design.UITypeEditor)), + TypeConverterAttribute("System.Windows.Forms.Design.ControlBindingsConverter, " + AssemblyRef.SystemDesign), + ] + public class ControlBindingsCollection : BindingsCollection { + + internal IBindableComponent control; + + private DataSourceUpdateMode defaultDataSourceUpdateMode = DataSourceUpdateMode.OnValidation; + + /// + public ControlBindingsCollection(IBindableComponent control) : base() { + Debug.Assert(control != null, "How could a controlbindingscollection not have a control associated with it!"); + this.control = control; + } + + /// + /// + /// [To be supplied.] + /// + public IBindableComponent BindableComponent { + get { + return this.control; + } + } + + /// + /// + /// [To be supplied.] + /// + public Control Control { + get { + return this.control as Control; + } + } + + /// + /// + /// [To be supplied.] + /// + public Binding this[string propertyName] { + get { + foreach (Binding binding in this) { + if (String.Equals(binding.PropertyName, propertyName, StringComparison.OrdinalIgnoreCase)) + { + return binding; + } + } + return null; + } + } + + /// + /// + /// Adds the binding to the collection. An ArgumentNullException is thrown if this binding + /// is null. An exception is thrown if a binding to the same target and Property as an existing binding or + /// if the binding's column isn't a valid column given this DataSource.Table's schema. + /// Fires the CollectionChangedEvent. + /// + public new void Add(Binding binding) { + base.Add(binding); + } + + /// + /// + /// Creates the binding and adds it to the collection. An InvalidBindingException is thrown + /// if this binding can't be constructed. An exception is thrown if a binding to the same target and Property as an existing binding or + /// if the binding's column isn't a valid column given this DataSource.Table's schema. + /// Fires the CollectionChangedEvent. + /// + public Binding Add(string propertyName, object dataSource, string dataMember) { + return Add(propertyName, dataSource, dataMember, false, this.DefaultDataSourceUpdateMode, null, String.Empty, null); + } + + /// + public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled) { + return Add(propertyName, dataSource, dataMember, formattingEnabled, this.DefaultDataSourceUpdateMode, null, String.Empty, null); + } + + /// + public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode) { + return Add(propertyName, dataSource, dataMember, formattingEnabled, updateMode, null, String.Empty, null); + } + + /// + public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue) { + return Add(propertyName, dataSource, dataMember, formattingEnabled, updateMode, nullValue, String.Empty, null); + } + + /// + public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue, string formatString) { + return Add(propertyName, dataSource, dataMember, formattingEnabled, updateMode, nullValue, formatString, null); + } + + /// + public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue, string formatString, IFormatProvider formatInfo) { + if (dataSource == null) + throw new ArgumentNullException("dataSource"); + Binding binding = new Binding(propertyName, dataSource, dataMember, formattingEnabled, updateMode, nullValue, formatString, formatInfo); + Add(binding); + return binding; + } + + /// + /// + /// Creates the binding and adds it to the collection. An InvalidBindingException is thrown + /// if this binding can't be constructed. An exception is thrown if a binding to the same target and Property as an existing binding or + /// if the binding's column isn't a valid column given this DataSource.Table's schema. + /// Fires the CollectionChangedEvent. + /// + protected override void AddCore(Binding dataBinding) { + if (dataBinding == null) + throw new ArgumentNullException("dataBinding"); + if (dataBinding.BindableComponent == control) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionAdd1)); + if (dataBinding.BindableComponent != null) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionAdd2)); + + // important to set prop first for error checking. + dataBinding.SetBindableComponent(control); + + base.AddCore(dataBinding); + } + + // internalonly + internal void CheckDuplicates(Binding binding) { + if (binding.PropertyName.Length == 0) { + return; + } + for (int i = 0; i < Count; i++) { + if (binding != this[i] && this[i].PropertyName.Length > 0 && + (String.Compare(binding.PropertyName, this[i].PropertyName, false, CultureInfo.InvariantCulture) == 0)) { + throw new ArgumentException(SR.GetString(SR.BindingsCollectionDup), "binding"); + } + } + } + + /// + /// + /// Clears the collection of any bindings. + /// Fires the CollectionChangedEvent. + /// + public new void Clear() { + base.Clear(); + } + + // internalonly + /// + /// + /// [To be supplied.] + /// + protected override void ClearCore() { + int numLinks = Count; + for (int i = 0; i < numLinks; i++) { + Binding dataBinding = this[i]; + dataBinding.SetBindableComponent(null); + } + base.ClearCore(); + } + + /// + /// + /// + public DataSourceUpdateMode DefaultDataSourceUpdateMode { + get { + return defaultDataSourceUpdateMode; + } + + set { + defaultDataSourceUpdateMode = value; + } + } + + /// + /// + /// Removes the given binding from the collection. + /// An ArgumentNullException is thrown if this binding is null. An ArgumentException is thrown + /// if this binding doesn't belong to this collection. + /// The CollectionChanged event is fired if it succeeds. + /// + public new void Remove(Binding binding) { + base.Remove(binding); + } + + /// + /// + /// Removes the given binding from the collection. + /// It throws an IndexOutOfRangeException if this doesn't have + /// a valid binding. + /// The CollectionChanged event is fired if it succeeds. + /// + public new void RemoveAt(int index) { + base.RemoveAt(index); + } + + /// + /// + /// [To be supplied.] + /// + protected override void RemoveCore(Binding dataBinding) { + if (dataBinding.BindableComponent != control) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionForeign)); + dataBinding.SetBindableComponent(null); + base.RemoveCore(dataBinding); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ControlEvent.cs b/WindowsForms/Managed/System/WinForms/ControlEvent.cs new file mode 100644 index 000000000..ebec7375d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlEvent.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + + + /// + /// + /// A ControlEventArgs is an event that has a control + /// as a property. + /// + public class ControlEventArgs : EventArgs { + private Control control; + + /// + /// + /// Retrieves the control object stored in this event. + /// + public Control Control { + get { + return control; + } + } + + /// + /// + /// Creates a new ControlEventArgs. + /// + public ControlEventArgs(Control control) { + this.control = control; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ControlEventHandler.cs b/WindowsForms/Managed/System/WinForms/ControlEventHandler.cs new file mode 100644 index 000000000..9dfe1324c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// Describes a delegate for an event that has a ControlEventArgs as + /// a parameter. + /// + public delegate void ControlEventHandler(object sender, ControlEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ControlPaint.cs b/WindowsForms/Managed/System/WinForms/ControlPaint.cs new file mode 100644 index 000000000..166cd38b0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlPaint.cs @@ -0,0 +1,3095 @@ +#define GRAYSCALE_DISABLED +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.ComponentModel; + + using System.Diagnostics; + using System; + using System.IO; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Drawing; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Drawing.Text; + using System.Drawing.Imaging; + using System.Drawing.Drawing2D; + using System.Runtime.InteropServices; + using System.Windows.Forms.Internal; + using System.Runtime.Versioning; + + /// + /// + /// The ControlPaint class provides a series of methods that can be used to + /// paint common Windows UI pieces. Many windows forms controls use this class to paint + /// their UI elements. + /// + public sealed class ControlPaint { + [ThreadStatic] + private static Bitmap checkImage; // image used to render checkmarks + [ThreadStatic] + private static Pen focusPen; // pen used to draw a focus rectangle + [ThreadStatic] + private static Pen focusPenInvert; // pen used to draw a focus rectangle + [ThreadStatic] + private static Color focusPenColor; // the last background color the focus pen was created with + [ThreadStatic] + private static bool hcFocusPen; // cached focus pen intended for high contrast mode + private static Pen grabPenPrimary; // pen used for primary grab handles + private static Pen grabPenSecondary; // pen used for secondary grab handles + private static Brush grabBrushPrimary; // brush used for primary grab handles + private static Brush grabBrushSecondary; // brush used for secondary grab handles + [ThreadStatic] + private static Brush frameBrushActive; // brush used for the active selection frame + private static Color frameColorActive; // color of active frame brush + [ThreadStatic] + private static Brush frameBrushSelected; // brush used for the inactive selection frame + private static Color frameColorSelected; // color of selected frame brush + [ThreadStatic] + private static Brush gridBrush; // brush used to draw a grid + private static Size gridSize; // the dimensions of the grid dots + private static bool gridInvert; // true if the grid color is inverted + [ThreadStatic] + private static ImageAttributes disabledImageAttr; // ImageAttributes used to render disabled images + + //use these value to signify ANY of the right, top, left, center, or bottom alignments with the ContentAlignment enum. + private static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight; + private static readonly ContentAlignment anyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight; + private static readonly ContentAlignment anyCenter = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter; + private static readonly ContentAlignment anyMiddle = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight; + + // not creatable... + // + private ControlPaint() { + } + + internal static Rectangle CalculateBackgroundImageRectangle(Rectangle bounds, Image backgroundImage, ImageLayout imageLayout) { + + Rectangle result = bounds; + + if (backgroundImage != null) { + switch (imageLayout) { + case ImageLayout.Stretch: + result.Size = bounds.Size; + break; + + case ImageLayout.None: + result.Size = backgroundImage.Size; + break; + + case ImageLayout.Center: + result.Size = backgroundImage.Size; + Size szCtl = bounds.Size; + + if (szCtl.Width > result.Width) { + result.X = (szCtl.Width - result.Width) / 2; + } + if (szCtl.Height > result.Height) { + result.Y = (szCtl.Height - result.Height) / 2; + } + break; + + case ImageLayout.Zoom: + Size imageSize = backgroundImage.Size; + float xRatio = (float)bounds.Width / (float)imageSize.Width; + float yRatio = (float)bounds.Height / (float)imageSize.Height; + if (xRatio < yRatio) { + //width should fill the entire bounds. + result.Width = bounds.Width; + // preserve the aspect ratio by multiplying the xRatio by the height + // adding .5 to round to the nearest pixel + result.Height = (int) ((imageSize.Height * xRatio) +.5); + if (bounds.Y >= 0) + { + result.Y = (bounds.Height - result.Height) /2; + } + } + else { + // width should fill the entire bounds + result.Height = bounds.Height; + // preserve the aspect ratio by multiplying the xRatio by the height + // adding .5 to round to the nearest pixel + result.Width = (int) ((imageSize.Width * yRatio) +.5); + if (bounds.X >= 0) + { + result.X = (bounds.Width - result.Width) /2; + } + } + + break; + } + } + return result; + } + + + // a color appropriate for certain elements that are ControlDark in normal color schemes, + // but for which ControlDark does not work in high contrast color schemes + /// + /// + /// [To be supplied.] + /// + public static Color ContrastControlDark { + get { + return SystemInformation.HighContrast ? SystemColors.WindowFrame : SystemColors.ControlDark; + } + } + + // Returns address of a BITMAPINFO for use by CreateHBITMAP16Bit. + // The caller is resposible for freeing the memory returned by this method. + // SECREVIEW : This method creates a BitmapInfo and copies its data to a raw memory block and returns a pointer + // to it. Its safe to use it privately. + // + private static IntPtr CreateBitmapInfo(Bitmap bitmap, IntPtr hdcS) { + NativeMethods.BITMAPINFOHEADER header = new NativeMethods.BITMAPINFOHEADER(); + header.biSize = Marshal.SizeOf(header); + header.biWidth = bitmap.Width; + header.biHeight = bitmap.Height; + header.biPlanes = 1; + header.biBitCount = 16; + header.biCompression = NativeMethods.BI_RGB; + // leave everything else 0 + + // Set up color table -- + int entryCount = 0; + IntPtr palette = SafeNativeMethods.CreateHalftonePalette(new HandleRef(null, hdcS)); + UnsafeNativeMethods.GetObject(new HandleRef(null, palette), 2, ref entryCount); + int[] entries = new int[entryCount]; + SafeNativeMethods.GetPaletteEntries(new HandleRef(null, palette), 0, entryCount, entries); + int[] colors = new int[entryCount]; + for (int i = 0; i < entryCount; i++) { + int entry = entries[i]; + colors[i] + = (entry & unchecked((int)0xff000000)) >> 6 // red + + (entry & 0x00ff0000) >> 4 // blue + + (entry & 0x0000ff00) >> 2; // green + } + SafeNativeMethods.DeleteObject(new HandleRef(null, palette)); + + IntPtr address = Marshal.AllocCoTaskMem(Marshal.SizeOf(header) + entryCount*4); + Marshal.StructureToPtr(header, address, false); + Marshal.Copy(colors, 0, (IntPtr)((long)address + Marshal.SizeOf(header)), entryCount); + return address; + } + + /// + /// + /// Creates a 16-bit color bitmap. + /// Sadly, this must be public for the designer to get at it. + /// From MSDN: + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr CreateHBitmap16Bit(Bitmap bitmap, Color background) { + IntPtr hBitmap; + Size size = bitmap.Size; + + using( DeviceContext screenDc = DeviceContext.ScreenDC ){ + IntPtr hdcS = screenDc.Hdc; + + using( DeviceContext compatDc = DeviceContext.FromCompatibleDC( hdcS ) ){ + IntPtr dc = compatDc.Hdc; + + byte[] enoughBits = new byte[bitmap.Width * bitmap.Height]; + IntPtr bitmapInfo = CreateBitmapInfo(bitmap, hdcS); + hBitmap = SafeNativeMethods.CreateDIBSection(new HandleRef(null, hdcS), new HandleRef(null, bitmapInfo), NativeMethods.DIB_RGB_COLORS, + enoughBits, IntPtr.Zero, 0); + + Marshal.FreeCoTaskMem(bitmapInfo); + + if (hBitmap == IntPtr.Zero) { + throw new Win32Exception(); + } + + try { + IntPtr previousBitmap = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, hBitmap)); + if (previousBitmap == IntPtr.Zero) { + throw new Win32Exception(); + } + + SafeNativeMethods.DeleteObject( new HandleRef( null, previousBitmap ) ); + + using( Graphics graphics = Graphics.FromHdcInternal(dc) ) { + using( Brush brush = new SolidBrush(background) ) { + graphics.FillRectangle(brush, 0, 0, size.Width, size.Height); + } + graphics.DrawImage(bitmap, 0, 0, size.Width, size.Height); + } + } + catch{ + SafeNativeMethods.DeleteObject( new HandleRef( null, hBitmap ) ); + throw; + } + } + } + + return hBitmap; + } + + /// + /// + /// Creates a Win32 HBITMAP out of the image. You are responsible for + /// de-allocating the HBITMAP with Windows.DeleteObject(handle). + /// If the image uses transparency, the background will be filled with the specified color. + /// From MSDN: + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr CreateHBitmapTransparencyMask(Bitmap bitmap) { + if (bitmap == null) { + throw new ArgumentNullException("bitmap"); + } + Size size = bitmap.Size; + int width = bitmap.Width; + int height = bitmap.Height; + + int monochromeStride = width / 8; + if ((width % 8) != 0) // wanted division to round up, not down + monochromeStride++; + // must be multiple of two -- i.e., bitmap + // scanlines must fall on double-byte boundaries + if ((monochromeStride % 2) != 0) + monochromeStride++; + + byte[] bits = new byte[monochromeStride * height]; + BitmapData data = bitmap.LockBits(new Rectangle(0,0, width, height), + ImageLockMode.ReadOnly, + PixelFormat.Format32bppArgb); + + Debug.Assert(data.Scan0 != IntPtr.Zero, "BitmapData.Scan0 is null; check marshalling"); + + for (int y = 0; y < height; y++) { + IntPtr scan = (IntPtr)((long)data.Scan0 + y * data.Stride); + for (int x = 0; x < width; x++) { + int color = Marshal.ReadInt32(scan, x*4); + if (color >> 24 == 0) { + // pixel is transparent; set bit to 1 + int index = monochromeStride * y + x / 8; + bits[index] |= (byte) (0x80 >> (x % 8)); + } + } + } + + bitmap.UnlockBits(data); + + IntPtr mask = SafeNativeMethods.CreateBitmap(size.Width, size.Height, 1, /* 1bpp */ 1, bits); + + return mask; + } + + /// + /// + /// Creates a Win32 HBITMAP out of the image. You are responsible for + /// de-allocating the HBITMAP with Windows.DeleteObject(handle). + /// If the image uses transparency, the background will be filled with the specified color. + /// From MSDN: + /// This member supports the .NET Framework infrastructure and is not intended to be used directly from your code. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr CreateHBitmapColorMask(Bitmap bitmap, IntPtr monochromeMask) { + Size size = bitmap.Size; + + IntPtr colorMask = bitmap.GetHbitmap(); + IntPtr hdcS = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + IntPtr source = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(null, hdcS)); + IntPtr target = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(null, hdcS)); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hdcS)); + IntPtr previousSourceBitmap = SafeNativeMethods.SelectObject(new HandleRef(null, source), new HandleRef(null, monochromeMask)); + IntPtr previousTargetBitmap = SafeNativeMethods.SelectObject(new HandleRef(null, target), new HandleRef(null, colorMask)); + + // Now the trick is to make colorBitmap black wherever the transparent + // color is located, but keep the original color everywhere else. + // We've already got the original bitmap, so all we need to do is + // to and with the inverse of the mask (ROP DSna). When going from + // monochrome to color, Windows sets all 1 bits to the background + // color, and all 0 bits to the foreground color. + // + SafeNativeMethods.SetBkColor(new HandleRef(null, target), 0x00ffffff); // white + SafeNativeMethods.SetTextColor(new HandleRef(null, target), 0); // black + SafeNativeMethods.BitBlt(new HandleRef(null, target), 0, 0, size.Width, size.Height, new HandleRef(null, source), + 0, 0, 0x220326); // RasterOp.SOURCE.Invert().AndWith(RasterOp.TARGET).GetRop()); + + SafeNativeMethods.SelectObject(new HandleRef(null, source), new HandleRef(null, previousSourceBitmap)); + SafeNativeMethods.SelectObject(new HandleRef(null, target), new HandleRef(null, previousTargetBitmap)); + UnsafeNativeMethods.DeleteCompatibleDC(new HandleRef(null, source)); + UnsafeNativeMethods.DeleteCompatibleDC(new HandleRef(null, target)); + + return System.Internal.HandleCollector.Add(colorMask, NativeMethods.CommonHandles.GDI);; + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.Process, ResourceScope.Machine)] + internal static IntPtr CreateHalftoneHBRUSH() { + short[] grayPattern = new short[8]; + for (int i = 0; i < 8; i++) + grayPattern[i] = (short)(0x5555 << (i & 1)); + IntPtr hBitmap = SafeNativeMethods.CreateBitmap(8, 8, 1, 1, grayPattern); + + NativeMethods.LOGBRUSH lb = new NativeMethods.LOGBRUSH(); + lb.lbColor = ColorTranslator.ToWin32(Color.Black); + lb.lbStyle = NativeMethods.BS_PATTERN; + lb.lbHatch = hBitmap; + IntPtr brush = SafeNativeMethods.CreateBrushIndirect(lb); + + SafeNativeMethods.DeleteObject(new HandleRef(null, hBitmap)); + return brush; + } + + // roughly the same code as in Graphics.cs + internal static void CopyPixels(IntPtr sourceHwnd, IDeviceContext targetDC, Point sourceLocation, Point destinationLocation, Size blockRegionSize, CopyPixelOperation copyPixelOperation) { + int destWidth = blockRegionSize.Width; + int destHeight = blockRegionSize.Height; + + DeviceContext dc = DeviceContext.FromHwnd(sourceHwnd); + HandleRef targetHDC = new HandleRef( null, targetDC.GetHdc()); + HandleRef screenHDC = new HandleRef( null, dc.Hdc ); + + try { + bool result = SafeNativeMethods.BitBlt(targetHDC, destinationLocation.X, destinationLocation.Y, destWidth, destHeight, + screenHDC, + sourceLocation.X, sourceLocation.Y, (int) copyPixelOperation); + + //a zero result indicates a win32 exception has been thrown + if (!result) { + throw new Win32Exception(); + } + } + finally { + targetDC.ReleaseHdc(); + dc.Dispose(); + } + } + + /// + /// + /// Draws a border of the specified style and color to the given graphics. + /// + private static DashStyle BorderStyleToDashStyle(ButtonBorderStyle borderStyle) { + switch (borderStyle) { + case ButtonBorderStyle.Dotted: return DashStyle.Dot; + case ButtonBorderStyle.Dashed: return DashStyle.Dash; + case ButtonBorderStyle.Solid: return DashStyle.Solid; + default: + Debug.Fail("border style has no corresponding dash style"); + return DashStyle.Solid; + } + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color Dark(Color baseColor, float percOfDarkDark) { + return new HLSColor(baseColor).Darker(percOfDarkDark); + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color Dark(Color baseColor) { + return new HLSColor(baseColor).Darker(0.5f); + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color DarkDark(Color baseColor) { + return new HLSColor(baseColor).Darker(1.0f); + } + + //returns true if the luminosity of c1 is less than c2. + internal static bool IsDarker(Color c1, Color c2) { + HLSColor hc1 = new HLSColor(c1); + HLSColor hc2 = new HLSColor(c2); + return (hc1.Luminosity < hc2.Luminosity); + } + + /// + /// Used by PrintToMetaFileRecursive overrides (Label, Panel) to manually + /// paint borders for UserPaint controls that were relying on + /// their window style to provide their borders. + /// + internal static void PrintBorder(Graphics graphics, Rectangle bounds, BorderStyle style, Border3DStyle b3dStyle) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + switch (style) { + case BorderStyle.FixedSingle: + ControlPaint.DrawBorder(graphics, bounds, Color.FromKnownColor(KnownColor.WindowFrame), ButtonBorderStyle.Solid); + break; + case BorderStyle.Fixed3D: + ControlPaint.DrawBorder3D(graphics, bounds, b3dStyle); + break; + case BorderStyle.None: + break; + default: + Debug.Fail("Unsupported border style."); + break; + } + } + internal static void DrawBackgroundImage(Graphics g, Image backgroundImage, Color backColor, ImageLayout backgroundImageLayout, Rectangle bounds, Rectangle clipRect) { + DrawBackgroundImage(g, backgroundImage, backColor, backgroundImageLayout, bounds, clipRect, Point.Empty, RightToLeft.No); + } + internal static void DrawBackgroundImage(Graphics g, Image backgroundImage, Color backColor, ImageLayout backgroundImageLayout, Rectangle bounds, Rectangle clipRect, Point scrollOffset) { + DrawBackgroundImage(g, backgroundImage, backColor, backgroundImageLayout, bounds, clipRect, scrollOffset, RightToLeft.No); + } + + internal static void DrawBackgroundImage(Graphics g, Image backgroundImage, Color backColor, ImageLayout backgroundImageLayout, Rectangle bounds, Rectangle clipRect, Point scrollOffset, RightToLeft rightToLeft) { + if (g == null) { + throw new ArgumentNullException("g"); + } + + if(backgroundImageLayout == ImageLayout.Tile) { + // tile + + using (TextureBrush textureBrush = new TextureBrush(backgroundImage,WrapMode.Tile)) { + // Make sure the brush origin matches the display rectangle, not the client rectangle, + // so the background image scrolls on AutoScroll forms. + if (scrollOffset != Point.Empty) { + Matrix transform = textureBrush.Transform; + transform.Translate(scrollOffset.X,scrollOffset.Y); + textureBrush.Transform = transform; + } + g.FillRectangle(textureBrush, clipRect); + } + } + + else { + // Center, Stretch, Zoom + + Rectangle imageRectangle = CalculateBackgroundImageRectangle(bounds, backgroundImage, backgroundImageLayout); + + //flip the coordinates only if we don't do any layout, since otherwise the image should be at the center of the + //displayRectangle anyway. + + if (rightToLeft == RightToLeft.Yes && backgroundImageLayout == ImageLayout.None) { + imageRectangle.X += clipRect.Width - imageRectangle.Width; + } + + // We fill the entire cliprect with the backcolor in case the image is transparent. + // Also, if gdi+ can't quite fill the rect with the image, they will interpolate the remaining + // pixels, and make them semi-transparent. This is another reason why we need to fill the entire rect. + // If we didn't where ever the image was transparent, we would get garbage. VS Whidbey #504388 + using (SolidBrush brush = new SolidBrush(backColor)) { + g.FillRectangle(brush, clipRect); + } + + if (!clipRect.Contains(imageRectangle)) { + if (backgroundImageLayout == ImageLayout.Stretch || backgroundImageLayout == ImageLayout.Zoom) { + imageRectangle.Intersect(clipRect); + g.DrawImage(backgroundImage, imageRectangle); + } + else if (backgroundImageLayout == ImageLayout.None) { + imageRectangle.Offset(clipRect.Location); + Rectangle imageRect = imageRectangle; + imageRect.Intersect(clipRect); + Rectangle partOfImageToDraw = new Rectangle(Point.Empty, imageRect.Size); + g.DrawImage(backgroundImage, imageRect, partOfImageToDraw.X, partOfImageToDraw.Y, partOfImageToDraw.Width, + partOfImageToDraw.Height, GraphicsUnit.Pixel); + } + else { + Rectangle imageRect = imageRectangle; + imageRect.Intersect(clipRect); + Rectangle partOfImageToDraw = new Rectangle(new Point(imageRect.X - imageRectangle.X, imageRect.Y - imageRectangle.Y) + , imageRect.Size); + + g.DrawImage(backgroundImage, imageRect, partOfImageToDraw.X, partOfImageToDraw.Y, partOfImageToDraw.Width, + partOfImageToDraw.Height, GraphicsUnit.Pixel); + } + } + else { + ImageAttributes imageAttrib = new ImageAttributes(); + imageAttrib.SetWrapMode(WrapMode.TileFlipXY); + g.DrawImage(backgroundImage, imageRectangle, 0, 0, backgroundImage.Width, backgroundImage.Height, GraphicsUnit.Pixel, imageAttrib); + imageAttrib.Dispose(); + + } + + } + + } + + + /// + /// + /// [To be supplied.] + /// + public static void DrawBorder(Graphics graphics, Rectangle bounds, Color color, ButtonBorderStyle style) { + // Optimized version + switch (style) { + case ButtonBorderStyle.None: + // nothing + break; + + case ButtonBorderStyle.Dotted: + case ButtonBorderStyle.Dashed: + case ButtonBorderStyle.Solid: + DrawBorderSimple(graphics, bounds, color, style); + break; + + case ButtonBorderStyle.Inset: + case ButtonBorderStyle.Outset: + DrawBorderComplex(graphics, bounds, color, style); + break; + + default: + Debug.Fail("Unknown border style"); + break; + } + } + + /// + /// + /// Draws a border of the specified style and color to the given graphics. + /// + public static void DrawBorder(Graphics graphics, Rectangle bounds, + Color leftColor, int leftWidth, ButtonBorderStyle leftStyle, + Color topColor, int topWidth, ButtonBorderStyle topStyle, + Color rightColor, int rightWidth, ButtonBorderStyle rightStyle, + Color bottomColor, int bottomWidth, ButtonBorderStyle bottomStyle) { + // Very general, and very slow + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + int[] topLineLefts = new int[topWidth]; + int[] topLineRights = new int[topWidth]; + int[] leftLineTops = new int[leftWidth]; + int[] leftLineBottoms = new int[leftWidth]; + int[] bottomLineLefts = new int[bottomWidth]; + int[] bottomLineRights = new int[bottomWidth]; + int[] rightLineTops = new int[rightWidth]; + int[] rightLineBottoms = new int[rightWidth]; + + float topToLeft = 0.0f; + float bottomToLeft = 0.0f; + if (leftWidth > 0) { + topToLeft = ((float)topWidth)/((float)leftWidth); + bottomToLeft = ((float)bottomWidth)/((float)leftWidth); + } + float topToRight = 0.0f; + float bottomToRight = 0.0f; + if (rightWidth > 0) { + topToRight = ((float)topWidth)/((float)rightWidth); + bottomToRight = ((float)bottomWidth)/((float)rightWidth); + } + + HLSColor topHLSColor = new HLSColor(topColor); + HLSColor leftHLSColor = new HLSColor(leftColor); + HLSColor bottomHLSColor = new HLSColor(bottomColor); + HLSColor rightHLSColor = new HLSColor(rightColor); + + if (topWidth > 0) { + int i=0; + for (; i 0) { + leftOffset = (int)(((float)i) / topToLeft); + } + int rightOffset = 0; + if (topToRight > 0) { + rightOffset = (int)(((float)i) / topToRight); + } + topLineLefts[i] = bounds.X + leftOffset; + topLineRights[i] = bounds.X + bounds.Width - rightOffset - 1; + if (leftWidth > 0) { + leftLineTops[leftOffset] = bounds.Y + i + 1; + } + if (rightWidth > 0) { + rightLineTops[rightOffset] = bounds.Y + i; + } + } + for (int j=i; j 0) { + int i=0; + for (; i 0) { + leftOffset = (int)(((float)i) / bottomToLeft); + } + int rightOffset = 0; + if (bottomToRight > 0) { + rightOffset = (int)(((float)i) / bottomToRight); + } + bottomLineLefts[i] = bounds.X + leftOffset; + bottomLineRights[i] = bounds.X + bounds.Width - rightOffset - 1; + if (leftWidth > 0) { + leftLineBottoms[leftOffset] = bounds.Y + bounds.Height - i - 1; + } + if (rightWidth > 0) { + rightLineBottoms[rightOffset] = bounds.Y + bounds.Height - i - 1; + } + } + for (int j=i; j + /// + /// Draws a 3D style border at the given rectangle. The default 3D style of + /// Etched is used. + /// + public static void DrawBorder3D(Graphics graphics, Rectangle rectangle) { + DrawBorder3D(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, Border3DStyle.Etched, + Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom); + } + + /// + /// + /// Draws a 3D style border at the given rectangle. You may specify the style + /// of the 3D appearance. + /// + public static void DrawBorder3D(Graphics graphics, Rectangle rectangle, Border3DStyle style) { + DrawBorder3D(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, style, + Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom); + } + + /// + /// + /// Draws a 3D style border at the given rectangle. You may specify the style + /// of the 3D appearance, and which sides of the 3D rectangle you wish to + /// draw. + /// + public static void DrawBorder3D(Graphics graphics, Rectangle rectangle, Border3DStyle style, Border3DSide sides) { + DrawBorder3D(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, style, sides); + } + + /// + /// + /// Draws a 3D style border at the given rectangle. The default 3D style of + /// ETCHED is used. + /// + public static void DrawBorder3D(Graphics graphics, int x, int y, int width, int height) { + DrawBorder3D(graphics, x, y, width, height, Border3DStyle.Etched, + Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom); + } + + /// + /// + /// Draws a 3D style border at the given rectangle. You may specify the style + /// of the 3D appearance. + /// + public static void DrawBorder3D(Graphics graphics, int x, int y, int width, int height, Border3DStyle style) { + DrawBorder3D(graphics, x, y, width, height, style, + Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom); + } + + /// + /// + /// Draws a 3D style border at the given rectangle. You may specify the style + /// of the 3D appearance, and which sides of the 3D rectangle you wish to + /// draw. + /// + public static void DrawBorder3D(Graphics graphics, int x, int y, int width, int height, Border3DStyle style, Border3DSide sides) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + int edge = ((int)style) & 0x0F; + int flags = ((int)sides) | (((int)style) & ~0x0F); + + NativeMethods.RECT rc = NativeMethods.RECT.FromXYWH(x, y, width, height); + + // Windows just draws the border to size, and then + // shrinks the rectangle so the user can paint the + // client area. We can't really do that, so we do + // the opposite: We precalculate the size of the border + // and enlarge the rectangle so the client size is + // preserved. + // + if ((flags & (int)Border3DStyle.Adjust) == (int)Border3DStyle.Adjust) { + Size sz = SystemInformation.Border3DSize; + rc.left -= sz.Width; + rc.right += sz.Width; + rc.top -= sz.Height; + rc.bottom += sz.Height; + flags &= ~((int)Border3DStyle.Adjust); + } + + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics)) { // Get Win32 dc with Graphics properties applied to it. + SafeNativeMethods.DrawEdge(new HandleRef(wg, wg.DeviceContext.Hdc), ref rc, edge, flags); + } + } + + /// + /// Helper function that draws a more complex border. This is used by DrawBorder for less common + /// rendering cases. We split DrawBorder into DrawBorderSimple and DrawBorderComplex so we maximize + /// the % of the function call. It is less performant to have large functions that do many things. + /// + private static void DrawBorderComplex(Graphics graphics, Rectangle bounds, Color color, ButtonBorderStyle style) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + if (style == ButtonBorderStyle.Inset) { // button being pushed + HLSColor hls = new HLSColor(color); + + // top + left + Pen pen = new Pen(hls.Darker(1.0f)); + graphics.DrawLine(pen, bounds.X, bounds.Y, + bounds.X + bounds.Width - 1, bounds.Y); + graphics.DrawLine(pen, bounds.X, bounds.Y, + bounds.X, bounds.Y + bounds.Height - 1); + + // bottom + right + pen.Color = hls.Lighter(1.0f); + graphics.DrawLine(pen, bounds.X, bounds.Y + bounds.Height - 1, + bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1); + graphics.DrawLine(pen, bounds.X + bounds.Width - 1, bounds.Y, + bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1); + + // Top + left inset + pen.Color = hls.Lighter(0.5f); + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + 1, + bounds.X + bounds.Width - 2, bounds.Y + 1); + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + 1, + bounds.X + 1, bounds.Y + bounds.Height - 2); + + // bottom + right inset + if (color.ToKnownColor() == SystemColors.Control.ToKnownColor()) { + pen.Color = SystemColors.ControlLight; + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + bounds.Height - 2, + bounds.X + bounds.Width - 2, bounds.Y + bounds.Height - 2); + graphics.DrawLine(pen, bounds.X + bounds.Width - 2, bounds.Y + 1, + bounds.X + bounds.Width - 2, bounds.Y + bounds.Height - 2); + } + + pen.Dispose(); + } + else { // Standard button + Debug.Assert(style == ButtonBorderStyle.Outset, "Caller should have known how to use us."); + + bool stockColor = color.ToKnownColor() == SystemColors.Control.ToKnownColor(); + HLSColor hls = new HLSColor(color); + + // top + left + Pen pen = stockColor ? SystemPens.ControlLightLight : new Pen(hls.Lighter(1.0f)); + graphics.DrawLine(pen, bounds.X, bounds.Y, + bounds.X + bounds.Width - 1, bounds.Y); + graphics.DrawLine(pen, bounds.X, bounds.Y, + bounds.X, bounds.Y + bounds.Height - 1); + // bottom + right + if (stockColor) { + pen = SystemPens.ControlDarkDark; + } + else { + pen.Color = hls.Darker(1.0f); + } + graphics.DrawLine(pen, bounds.X, bounds.Y + bounds.Height - 1, + bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1); + graphics.DrawLine(pen, bounds.X + bounds.Width - 1, bounds.Y, + bounds.X + bounds.Width - 1, bounds.Y + bounds.Height - 1); + // top + left inset + if (stockColor) { + if (SystemInformation.HighContrast) { + pen = SystemPens.ControlLight; + } + else { + pen = SystemPens.Control; + } + } + else { + pen.Color = color; + } + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + 1, + bounds.X + bounds.Width - 2, bounds.Y + 1); + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + 1, + bounds.X + 1, bounds.Y + bounds.Height - 2); + + // Bottom + right inset + if (stockColor) { + pen = SystemPens.ControlDark; + } + else { + pen.Color = hls.Darker(0.5f); + } + + graphics.DrawLine(pen, bounds.X + 1, bounds.Y + bounds.Height - 2, + bounds.X + bounds.Width - 2, bounds.Y + bounds.Height - 2); + graphics.DrawLine(pen, bounds.X + bounds.Width - 2, bounds.Y + 1, + bounds.X + bounds.Width - 2, bounds.Y + bounds.Height - 2); + + if (!stockColor) { + pen.Dispose(); + } + } + } + + /// + /// Helper function that draws a simple border. This is used by DrawBorder for the most common rendering cases. + /// + private static void DrawBorderSimple(Graphics graphics, Rectangle bounds, Color color, ButtonBorderStyle style) { + // Common case: system color with solid pen + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + bool stockBorder = (style == ButtonBorderStyle.Solid && color.IsSystemColor); + Pen pen; + if (stockBorder) { + pen = SystemPens.FromSystemColor(color); + } + else { + pen = new Pen(color); + if (style != ButtonBorderStyle.Solid) { + pen.DashStyle = BorderStyleToDashStyle(style); + } + } + + graphics.DrawRectangle(pen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + + if (!stockBorder) { + pen.Dispose(); + } + } + + /// + /// + /// Draws a Win32 button control in the given rectangle with the given state. + /// + public static void DrawButton(Graphics graphics, Rectangle rectangle, ButtonState state) { + DrawButton(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, state); + } + + /// + /// + /// Draws a Win32 button control in the given rectangle with the given state. + /// + public static void DrawButton(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_BUTTON, + NativeMethods.DFCS_BUTTONPUSH | (int) state, Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a Win32 window caption button in the given rectangle with the given state. + /// + public static void DrawCaptionButton(Graphics graphics, Rectangle rectangle, CaptionButton button, ButtonState state) { + DrawCaptionButton(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, button, state); + } + + /// + /// + /// Draws a Win32 window caption button in the given rectangle with the given state. + /// + public static void DrawCaptionButton(Graphics graphics, int x, int y, int width, int height, CaptionButton button, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_CAPTION, + (int) button | (int) state, Color.Empty, Color.Empty); + } + + + /// + /// + /// Draws a Win32 checkbox control in the given rectangle with the given state. + /// + public static void DrawCheckBox(Graphics graphics, Rectangle rectangle, ButtonState state) { + DrawCheckBox(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, state); + } + + /// + /// + /// Draws a Win32 checkbox control in the given rectangle with the given state. + /// + public static void DrawCheckBox(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + // Windows flat check box is too ugly for words. We draw our own here + if ((state & ButtonState.Flat) == ButtonState.Flat) { + DrawFlatCheckBox(graphics, new Rectangle(x, y, width, height), state); + } + else { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_BUTTON, + NativeMethods.DFCS_BUTTONCHECK | (int) state, Color.Empty, Color.Empty); + } + } + + /// + /// + /// Draws the drop down button of a Win32 combo box in the given rectangle with the given state. + /// + public static void DrawComboButton(Graphics graphics, Rectangle rectangle, ButtonState state) { + DrawComboButton(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, state); + } + + /// + /// + /// Draws the drop down button of a Win32 combo box in the given rectangle with the given state. + /// + public static void DrawComboButton(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_SCROLL, + NativeMethods.DFCS_SCROLLCOMBOBOX | (int) state, Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a container control grab handle glyph inside the given rectangle. + /// + public static void DrawContainerGrabHandle(Graphics graphics, Rectangle bounds) { + + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + Brush brush = Brushes.White; + Pen pen = Pens.Black; + + graphics.FillRectangle(brush, bounds.Left + 1, bounds.Top + 1, bounds.Width - 2, bounds.Height - 2); + + //draw the bounding rect w/o the four corners + graphics.DrawLine(pen, bounds.X + 1, bounds.Y, bounds.Right -2, bounds.Y); + graphics.DrawLine(pen, bounds.X + 1, bounds.Bottom - 1, bounds.Right -2, bounds.Bottom - 1); + graphics.DrawLine(pen, bounds.X, bounds.Y + 1, bounds.X, bounds.Bottom -2); + graphics.DrawLine(pen, bounds.Right - 1, bounds.Y + 1, bounds.Right - 1, bounds.Bottom -2); + + int midx = bounds.X + bounds.Width/2; + int midy = bounds.Y + bounds.Height/2; + + // vert line + graphics.DrawLine(pen, midx, bounds.Y, midx, bounds.Bottom - 2); + + // horiz line + graphics.DrawLine(pen, bounds.X, midy, bounds.Right - 2, midy); + + // top hash + graphics.DrawLine(pen, midx - 1, bounds.Y+2, midx+1, bounds.Y+2); + graphics.DrawLine(pen, midx - 2, bounds.Y+3, midx+2, bounds.Y+3); + + // left hash + graphics.DrawLine(pen, bounds.X+2, midy - 1, bounds.X + 2, midy + 1); + graphics.DrawLine(pen, bounds.X+3, midy - 2, bounds.X + 3, midy + 2); + + // right hash + graphics.DrawLine(pen, bounds.Right - 3, midy - 1, bounds.Right - 3, midy + 1); + graphics.DrawLine(pen, bounds.Right - 4, midy - 2, bounds.Right - 4, midy + 2); + + // bottom hash + graphics.DrawLine(pen, midx - 1, bounds.Bottom - 3, midx+1, bounds.Bottom - 3); + graphics.DrawLine(pen, midx - 2, bounds.Bottom - 4, midx+2, bounds.Bottom - 4); + } + + /// + /// + /// Draws a flat checkbox. + /// + private static void DrawFlatCheckBox(Graphics graphics, Rectangle rectangle, ButtonState state) { + // Background color of checkbox + // + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + Brush background = ((state & ButtonState.Inactive) == ButtonState.Inactive) ? + SystemBrushes.Control : + SystemBrushes.Window; + Color foreground = ((state & ButtonState.Inactive) == ButtonState.Inactive) ? + ((SystemInformation.HighContrast && AccessibilityImprovements.Level1) ? SystemColors.GrayText : SystemColors.ControlDark) : + SystemColors.ControlText; + DrawFlatCheckBox(graphics, rectangle, foreground, background, state); + } + + /// + /// + /// Draws a Win32 checkbox control in the given rectangle with the given state. This + /// draws a flat looking check box that is suitable for use in list boxes, etc. We + /// custom draw this because the windows version is soooo ugly. + /// + /// + private static void DrawFlatCheckBox(Graphics graphics, Rectangle rectangle, Color foreground, Brush background, ButtonState state) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + if (rectangle.Width < 0 || rectangle.Height < 0) { + throw new ArgumentOutOfRangeException("rectangle"); + } + + Rectangle offsetRectangle = new Rectangle(rectangle.X + 1, rectangle.Y + 1, + rectangle.Width - 2, rectangle.Height - 2); + graphics.FillRectangle(background, offsetRectangle); + + // Checkmark + // + if ((state & ButtonState.Checked) == ButtonState.Checked) { + if (checkImage == null || checkImage.Width != rectangle.Width || checkImage.Height != rectangle.Height) { + + if (checkImage != null) { + checkImage.Dispose(); + checkImage = null; + } + + // We draw the checkmark slightly off center to eliminate 3-D border artifacts, + // and compensate below + NativeMethods.RECT rcCheck = NativeMethods.RECT.FromXYWH(0, 0, rectangle.Width, rectangle.Height); + Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height); + using (Graphics g2 = Graphics.FromImage(bitmap)) { + g2.Clear(Color.Transparent); + IntPtr dc = g2.GetHdc(); + try { + SafeNativeMethods.DrawFrameControl(new HandleRef(null, dc), ref rcCheck, + NativeMethods.DFC_MENU, NativeMethods.DFCS_MENUCHECK); + } + finally { + g2.ReleaseHdcInternal(dc); + } + } + bitmap.MakeTransparent(); + checkImage = bitmap; + } + + rectangle.X += 1; + DrawImageColorized(graphics, checkImage, rectangle, foreground); + rectangle.X -= 1; + } + + // Surrounding border. We inset this by one pixel so we match how + // the 3D checkbox is drawn. + // + Pen pen = SystemPens.ControlDark; + graphics.DrawRectangle(pen, offsetRectangle.X, offsetRectangle.Y, offsetRectangle.Width - 1, offsetRectangle.Height - 1); + } + + /// + /// + /// Draws a focus rectangle. A focus rectangle is a dotted rectangle that Windows + /// uses to indicate what control has the current keyboard focus. + /// + public static void DrawFocusRectangle(Graphics graphics, Rectangle rectangle) { + DrawFocusRectangle(graphics, rectangle, SystemColors.ControlText, SystemColors.Control); + } + + /// + /// + /// Draws a focus rectangle. A focus rectangle is a dotted rectangle that Windows + /// uses to indicate what control has the current keyboard focus. + /// + public static void DrawFocusRectangle(Graphics graphics, Rectangle rectangle, Color foreColor, Color backColor) { + DrawFocusRectangle(graphics, rectangle, backColor, false); + } + + internal static void DrawHighContrastFocusRectangle(Graphics graphics, Rectangle rectangle, Color color) { + DrawFocusRectangle(graphics, rectangle, color, true); + } + + private static void DrawFocusRectangle(Graphics graphics, Rectangle rectangle, Color color, bool highContrast) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + rectangle.Width--; + rectangle.Height--; + graphics.DrawRectangle(GetFocusPen(color, + // we want the corner to be penned + // see GetFocusPen for more explanation + (rectangle.X + rectangle.Y) % 2 == 1, + highContrast), + rectangle); + } + + + /// + /// + /// Draws a win32 frame control. + /// + /// + private static void DrawFrameControl(Graphics graphics, int x, int y, int width, int height, + int kind, int state, Color foreColor, Color backColor) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + if (width < 0) { + throw new ArgumentOutOfRangeException("width"); + } + if (height < 0) { + throw new ArgumentOutOfRangeException("height"); + } + + NativeMethods.RECT rcFrame = NativeMethods.RECT.FromXYWH(0, 0, width, height); + using (Bitmap bitmap = new Bitmap(width, height)) { + using( Graphics g2 = Graphics.FromImage(bitmap) ) { + g2.Clear(Color.Transparent); + + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(g2) ){ // Get Win32 dc with Graphics properties applied to it. + SafeNativeMethods.DrawFrameControl(new HandleRef(wg, wg.DeviceContext.Hdc), ref rcFrame, kind, (int) state); + } + + if (foreColor == Color.Empty || backColor == Color.Empty) { + graphics.DrawImage(bitmap, x, y); + } + else { + // Replace black/white with foreColor/backColor. + ImageAttributes attrs = new ImageAttributes(); + ColorMap cm1 = new ColorMap(); + cm1.OldColor = Color.Black; + cm1.NewColor = foreColor; + ColorMap cm2 = new ColorMap(); + cm2.OldColor = Color.White; + cm2.NewColor = backColor; + attrs.SetRemapTable(new ColorMap[2] { cm1, cm2 }, ColorAdjustType.Bitmap); + graphics.DrawImage(bitmap, new Rectangle(x, y, width, height), 0, 0, width, height, GraphicsUnit.Pixel, attrs, null, IntPtr.Zero); + } + } + } + } + + /// + /// + /// Draws a standard selection grab handle with the given dimensions. Grab + /// handles are used by components to indicate to the user that they can + /// be directly maniupulated. + /// + public static void DrawGrabHandle(Graphics graphics, Rectangle rectangle, bool primary, bool enabled) { + Pen pen; + Brush brush; + + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + if (primary) { + if (null == grabPenPrimary) { + grabPenPrimary = Pens.Black; + } + pen = grabPenPrimary; + + if (enabled) { + if (null == grabBrushPrimary) { + grabBrushPrimary = Brushes.White; + } + brush = grabBrushPrimary; + } + else { + brush = SystemBrushes.Control; + } + } + else { + if (null == grabPenSecondary) { + grabPenSecondary = Pens.White; + } + pen = grabPenSecondary; + + if (enabled) { + if (null == grabBrushSecondary) { + grabBrushSecondary = Brushes.Black; + } + brush = grabBrushSecondary; + } + else { + brush = SystemBrushes.Control; + } + } + + Rectangle fillRect = new Rectangle(rectangle.X + 1, rectangle.Y + 1, rectangle.Width - 1, rectangle.Height - 1); + graphics.FillRectangle(brush, fillRect); + rectangle.Width --; + rectangle.Height--; + graphics.DrawRectangle(pen, rectangle); + } + + /// + /// + /// Draws a grid of one pixel dots in the given rectangle. + /// + public static void DrawGrid(Graphics graphics, Rectangle area, Size pixelsBetweenDots, Color backColor) { + + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + if (pixelsBetweenDots.Width <= 0 || pixelsBetweenDots.Height <= 0) { + throw new ArgumentOutOfRangeException("pixelsBetweenDots"); + } + + float intensity = backColor.GetBrightness(); + bool invert = (intensity < .5); + + if (gridBrush == null || gridSize.Width != pixelsBetweenDots.Width + || gridSize.Height != pixelsBetweenDots.Height || invert != gridInvert) { + + if (gridBrush != null) { + gridBrush.Dispose(); + gridBrush = null; + } + + gridSize = pixelsBetweenDots; + int idealSize = 16; + gridInvert = invert; + Color foreColor = (gridInvert) ? Color.White : Color.Black; + + // Round size to a multiple of pixelsBetweenDots + int width = ((idealSize / pixelsBetweenDots.Width) + 1) * pixelsBetweenDots.Width; + int height = ((idealSize / pixelsBetweenDots.Height) + 1) * pixelsBetweenDots.Height; + + Bitmap bitmap = new Bitmap(width, height); + + // draw the dots + for (int x = 0; x < width; x += pixelsBetweenDots.Width) + for (int y = 0; y < height; y += pixelsBetweenDots.Height) + bitmap.SetPixel(x, y, foreColor); + + gridBrush = new TextureBrush(bitmap); + bitmap.Dispose(); + } + + graphics.FillRectangle(gridBrush, area); + } + + /* Unused + // Takes a black and white image, and paints it in color + internal static void DrawImageColorized(Graphics graphics, Image image, Rectangle destination, + Color replaceBlack, Color replaceWhite) { + DrawImageColorized(graphics, image, destination, + RemapBlackAndWhiteAndTransparentMatrix(replaceBlack, replaceWhite)); + } + */ + + // Takes a black and transparent image, turns black pixels into some other color, and leaves transparent pixels alone + internal static void DrawImageColorized(Graphics graphics, Image image, Rectangle destination, + Color replaceBlack) { + DrawImageColorized(graphics, image, destination, + RemapBlackAndWhitePreserveTransparentMatrix(replaceBlack, Color.White)); + } + + internal static bool IsImageTransparent(Image backgroundImage) { + if (backgroundImage != null && (backgroundImage.Flags & (int)ImageFlags.HasAlpha) > 0) { + return true; + } + return false; + } + + // takes an image and replaces all the pixels of oldColor with newColor, drawing the new image into the rectangle on + // the supplied Graphics object. + internal static void DrawImageReplaceColor(Graphics g, Image image, Rectangle dest, Color oldColor, Color newColor) { + ImageAttributes attrs = new ImageAttributes(); + + ColorMap cm = new ColorMap(); + cm.OldColor = oldColor; + cm.NewColor = newColor; + + attrs.SetRemapTable(new ColorMap[]{cm}, ColorAdjustType.Bitmap); + + g.DrawImage(image, dest, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attrs, null, IntPtr.Zero); + attrs.Dispose(); + } + + // Takes a black and white image, and paints it in color + private static void DrawImageColorized(Graphics graphics, Image image, Rectangle destination, + ColorMatrix matrix) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + ImageAttributes attributes = new ImageAttributes(); + attributes.SetColorMatrix(matrix); + graphics.DrawImage(image, destination, 0,0, image.Width, image.Height, + GraphicsUnit.Pixel, attributes, null, IntPtr.Zero); + attributes.Dispose(); + } + + /// + /// + /// Draws an image and makes it look disabled. + /// + public static void DrawImageDisabled(Graphics graphics, Image image, int x, int y, Color background) { + DrawImageDisabled(graphics, image, new Rectangle(x, y, image.Width, image.Height), background, false); + } + + /// + /// Draws an image and makes it look disabled. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + internal static void DrawImageDisabled(Graphics graphics, Image image, Rectangle imageBounds, Color background, bool unscaledImage) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + if (image == null) { + throw new ArgumentNullException("image"); + } +#if GRAYSCALE_DISABLED + Size imageSize = image.Size; + + if (disabledImageAttr == null) { + // This is how I came up with this somewhat random ColorMatrix. + // Its set to resemble Office10 commandbars, but still be able to + // deal with hi-color (256+) icons and images. + // + // The idea is to scale everything down (more than just a grayscale does, + // therefore the small numbers in the scaling part of matrix) + // White -> some shade of gray & + // Black -> Black + // + // Second part of the matrix is to translate everything, so all colors are + // a bit brigher. + // Grays become lighter and washed out looking + // Black becomes a shade of gray as well. + // + // btw, if you do come up with something better let me know - Microsoft + + float[][] array = new float[5][]; + array[0] = new float[5] {0.2125f, 0.2125f, 0.2125f, 0, 0}; + array[1] = new float[5] {0.2577f, 0.2577f, 0.2577f, 0, 0}; + array[2] = new float[5] {0.0361f, 0.0361f, 0.0361f, 0, 0}; + array[3] = new float[5] {0, 0, 0, 1, 0}; + array[4] = new float[5] {0.38f, 0.38f, 0.38f, 0, 1}; + + ColorMatrix grayMatrix = new ColorMatrix(array); + + disabledImageAttr = new ImageAttributes(); + disabledImageAttr.ClearColorKey(); + disabledImageAttr.SetColorMatrix(grayMatrix); + } + + + if (unscaledImage) { + using (Bitmap bmp = new Bitmap(image.Width, image.Height)) { + using (Graphics g = Graphics.FromImage(bmp)) { + g.DrawImage(image, + new Rectangle(0, 0, imageSize.Width, imageSize.Height), + 0, 0, imageSize.Width, imageSize.Height, + GraphicsUnit.Pixel, + disabledImageAttr); + } + graphics.DrawImageUnscaled(bmp, imageBounds); + } + } + else { + graphics.DrawImage(image, + imageBounds, + 0, 0, imageSize.Width, imageSize.Height, + GraphicsUnit.Pixel, + disabledImageAttr); + } +#else + + + // This is remarkably simple -- make a monochrome version of the image, draw once + // with the button highlight color, then a second time offset by one pixel + // and in the button shadow color. + // Technique borrowed from comctl Toolbar. + + Bitmap bitmap; + bool disposeBitmap = false; + if (image is Bitmap) + bitmap = (Bitmap) image; + else { + // #37659 -- metafiles can have extremely high resolutions, + // so if we naively turn them into bitmaps, the performance will be very poor. + // bitmap = new Bitmap(image); + + GraphicsUnit units = GraphicsUnit.Display; + RectangleF bounds = image.GetBounds(ref units); + bitmap = new Bitmap((int) (bounds.Width * graphics.DpiX / image.HorizontalResolution), + (int) (bounds.Height * graphics.DpiY / image.VerticalResolution)); + + Graphics bitmapGraphics = Graphics.FromImage(bitmap); + bitmapGraphics.Clear(Color.Transparent); + bitmapGraphics.DrawImage(image, 0, 0, image.Size.Width, image.Size.Height); + bitmapGraphics.Dispose(); + + disposeBitmap = true; + } + + Color highlight = ControlPaint.LightLight(background); + Bitmap monochrome = MakeMonochrome(bitmap, highlight); + graphics.DrawImage(monochrome, new Rectangle(imageBounds.X + 1, imageBounds.Y + 1, imageBounds.Width, imageBounds.Height)); + monochrome.Dispose(); + + Color shadow = ControlPaint.Dark(background); + monochrome = MakeMonochrome(bitmap, shadow); + graphics.DrawImage(monochrome, imageBounds); + monochrome.Dispose(); + + if (disposeBitmap) + bitmap.Dispose(); +#endif + } + + /// + /// + /// Draws a locked selection frame around the given rectangle. + /// + public static void DrawLockedFrame(Graphics graphics, Rectangle rectangle, bool primary) { + Pen pen; + + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + if (primary) { + pen = Pens.White; + } + else { + pen = Pens.Black; + } + + graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1); + rectangle.Inflate(-1, -1); + graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1); + + if (primary) { + pen = Pens.Black; + } + else { + pen = Pens.White; + } + rectangle.Inflate(-1, -1); + graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1); + } + + /// + /// + /// Draws a menu glyph for a Win32 menu in the given rectangle with the given state. + /// + public static void DrawMenuGlyph(Graphics graphics, Rectangle rectangle, MenuGlyph glyph) { + DrawMenuGlyph(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, glyph); + } + + /// + /// + /// Draws a menu glyph for a Win32 menu in the given rectangle with the given state. + /// White color is replaced with backColor, Black is replaced with foreColor. + /// + public static void DrawMenuGlyph(Graphics graphics, Rectangle rectangle, MenuGlyph glyph, Color foreColor, Color backColor) + { + DrawMenuGlyph(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, glyph, foreColor, backColor); + } + + /// + /// + /// Draws a menu glyph for a Win32 menu in the given rectangle with the given state. + /// + public static void DrawMenuGlyph(Graphics graphics, int x, int y, int width, int height, MenuGlyph glyph) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_MENU, + (int) glyph, Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a menu glyph for a Win32 menu in the given rectangle with the given state. + /// White color is replaced with backColor, Black is replaced with foreColor. + /// + public static void DrawMenuGlyph(Graphics graphics, int x, int y, int width, int height, MenuGlyph glyph, Color foreColor, Color backColor) + { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_MENU, (int)glyph, foreColor, backColor); + } + + /// + /// + /// Draws a Win32 3-state checkbox control in the given rectangle with the given state. + /// + public static void DrawMixedCheckBox(Graphics graphics, Rectangle rectangle, ButtonState state) { + DrawMixedCheckBox(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, state); + } + + /// + /// + /// [To be supplied.] + /// + public static void DrawMixedCheckBox(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_BUTTON, + NativeMethods.DFCS_BUTTON3STATE | (int) state, Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a Win32 radio button in the given rectangle with the given state. + /// + public static void DrawRadioButton(Graphics graphics, Rectangle rectangle, ButtonState state) { + DrawRadioButton(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, state); + } + + /// + /// + /// Draws a Win32 radio button in the given rectangle with the given state. + /// + public static void DrawRadioButton(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_BUTTON, + NativeMethods.DFCS_BUTTONRADIO | ((int)state), Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a rectangular frame on the screen. The operation of this can be + /// "reversed" by drawing the same rectangle again. This is similar to + /// inverting a region of the screen except that it behaves better for + /// a wider variety of colors. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + public static void DrawReversibleFrame(Rectangle rectangle, Color backColor, FrameStyle style) { + int rop2; + Color graphicsColor; + + if (backColor.GetBrightness() < .5) { + rop2 = 0xA; // RasterOp.PEN.Invert().XorWith(RasterOp.TARGET); + graphicsColor = Color.White; + } + else { + rop2 = 0x7; // RasterOp.PEN.XorWith(RasterOp.TARGET); + graphicsColor = Color.Black; + } + + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(null, UnsafeNativeMethods.GetDesktopWindow()), NativeMethods.NullHandleRef, NativeMethods.DCX_WINDOW | NativeMethods.DCX_LOCKWINDOWUPDATE | NativeMethods.DCX_CACHE); + IntPtr pen; + + switch (style) { + case FrameStyle.Dashed: + pen = SafeNativeMethods.CreatePen(NativeMethods.PS_DOT, 1, ColorTranslator.ToWin32(backColor)); + break; + + case FrameStyle.Thick: + default: + pen = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 2, ColorTranslator.ToWin32(backColor)); + break; + } + + int prevRop2 = SafeNativeMethods.SetROP2(new HandleRef(null, dc), rop2); + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH))); + IntPtr oldPen = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, pen)); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(graphicsColor)); + SafeNativeMethods.Rectangle(new HandleRef(null, dc), rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom); + + SafeNativeMethods.SetROP2(new HandleRef(null, dc), prevRop2); + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, oldBrush)); + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, oldPen)); + + if (pen != IntPtr.Zero) + { + SafeNativeMethods.DeleteObject(new HandleRef(null, pen)); + } + + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); + } + + /// + /// + /// Draws a reversible line on the screen. A reversible line can + /// be erased by just drawing over it again. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + public static void DrawReversibleLine(Point start, Point end, Color backColor) { + int rop2 = GetColorRop(backColor, + 0xA, // RasterOp.PEN.Invert().XorWith(RasterOp.TARGET), + 0x7); //RasterOp.PEN.XorWith(RasterOp.TARGET)); + + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(null, UnsafeNativeMethods.GetDesktopWindow()), NativeMethods.NullHandleRef, NativeMethods.DCX_WINDOW | NativeMethods.DCX_LOCKWINDOWUPDATE | NativeMethods.DCX_CACHE); + IntPtr pen = SafeNativeMethods.CreatePen(NativeMethods.PS_SOLID, 1, ColorTranslator.ToWin32(backColor)); + + int prevRop2 = SafeNativeMethods.SetROP2(new HandleRef(null, dc), rop2); + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH))); + IntPtr oldPen = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, pen)); + + + SafeNativeMethods.MoveToEx(new HandleRef(null, dc), start.X, start.Y, null); + SafeNativeMethods.LineTo(new HandleRef(null, dc), end.X, end.Y); + + SafeNativeMethods.SetROP2(new HandleRef(null, dc), prevRop2); + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, oldBrush)); + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, oldPen)); + SafeNativeMethods.DeleteObject(new HandleRef(null, pen)); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); + } + + /// + /// + /// Draws a button for a Win32 scroll bar in the given rectangle with the given state. + /// + public static void DrawScrollButton(Graphics graphics, Rectangle rectangle, ScrollButton button, ButtonState state) { + DrawScrollButton(graphics, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, button, state); + } + + /// + /// + /// Draws a button for a Win32 scroll bar in the given rectangle with the given state. + /// + public static void DrawScrollButton(Graphics graphics, int x, int y, int width, int height, ScrollButton button, ButtonState state) { + DrawFrameControl(graphics, x, y, width, height, NativeMethods.DFC_SCROLL, + (int)button | (int)state, Color.Empty, Color.Empty); + } + + /// + /// + /// Draws a standard selection frame. A selection frame is a frame that is + /// drawn around a selected component at design time. + /// + public static void DrawSelectionFrame(Graphics graphics, bool active, Rectangle outsideRect, Rectangle insideRect, Color backColor) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + Brush frameBrush; + if (active) { + frameBrush = GetActiveBrush(backColor); + } + else { + frameBrush = GetSelectedBrush(backColor); + } + + Region clip = graphics.Clip; + graphics.ExcludeClip(insideRect); + graphics.FillRectangle(frameBrush, outsideRect); + graphics.Clip = clip; + } + + /// + /// + /// Draws a size grip at the given location. The color of the size grip is based + /// on the given background color. + /// + public static void DrawSizeGrip(Graphics graphics, Color backColor, Rectangle bounds) { + DrawSizeGrip(graphics, backColor, bounds.X, bounds.Y, bounds.Width, bounds.Height); + } + + /// + /// + /// Draws a size grip at the given location. The color of the size grip is based + /// on the given background color. + /// + public static void DrawSizeGrip(Graphics graphics, Color backColor, int x, int y, int width, int height) { + + // Note: We don't paint any background to facilitate transparency, background images, etc... + // + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + using( Pen bright = new Pen(LightLight(backColor)) ) { + using ( Pen dark = new Pen(Dark(backColor)) ) { + + int minDim = Math.Min(width, height); + int right = x+width-1; + int bottom = y+height-2; + + for (int i=0; i + /// + /// Draws a string in the style appropriate for disabled items. + /// + public static void DrawStringDisabled(Graphics graphics, string s, Font font, + Color color, RectangleF layoutRectangle, + StringFormat format) { + + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + + if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) { + // Ignore the foreground color argument and don't do shading in high contrast, + // as colors should match the OS-defined ones. + graphics.DrawString(s, font, SystemBrushes.GrayText, layoutRectangle, format); + } + else { + layoutRectangle.Offset(1, 1); + using (SolidBrush brush = new SolidBrush(LightLight(color))) { + graphics.DrawString(s, font, brush, layoutRectangle, format); + + layoutRectangle.Offset(-1, -1); + color = Dark(color); + brush.Color = color; + graphics.DrawString(s, font, brush, layoutRectangle, format); + } + } + } + + /// + /// Draws a string in the style appropriate for disabled items, using GDI-based TextRenderer. + /// + public static void DrawStringDisabled(IDeviceContext dc, string s, Font font, + Color color, Rectangle layoutRectangle, + TextFormatFlags format) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + + if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) { + TextRenderer.DrawText(dc, s, font, layoutRectangle, SystemColors.GrayText, format); + } + else { + layoutRectangle.Offset(1, 1); + Color paintcolor = LightLight(color); + + TextRenderer.DrawText(dc, s, font, layoutRectangle, paintcolor, format); + layoutRectangle.Offset(-1, -1); + paintcolor = Dark(color); + TextRenderer.DrawText(dc, s, font, layoutRectangle, paintcolor, format); + } + } + + + /// + /// + /// Draws a string in the style appropriate for disabled items. + /// + public static void DrawVisualStyleBorder(Graphics graphics, Rectangle bounds) { + if (graphics == null) { + throw new ArgumentNullException("graphics"); + } + using (Pen borderPen = new Pen(System.Windows.Forms.VisualStyles.VisualStyleInformation.TextControlBorder)) { + graphics.DrawRectangle(borderPen, bounds); + } + } + + /// + /// + /// Draws a filled rectangle on the screen. The operation of this can be + /// "reversed" by drawing the same rectangle again. This is similar to + /// inverting a region of the screen except that it behaves better for + /// a wider variety of colors. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + public static void FillReversibleRectangle(Rectangle rectangle, Color backColor) { + int rop3 = GetColorRop(backColor, + 0xa50065, // RasterOp.BRUSH.Invert().XorWith(RasterOp.TARGET), + 0x5a0049); // RasterOp.BRUSH.XorWith(RasterOp.TARGET)); + int rop2 = GetColorRop(backColor, + 0x6, // RasterOp.BRUSH.Invert().XorWith(RasterOp.TARGET), + 0x6); // RasterOp.BRUSH.XorWith(RasterOp.TARGET)); + + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(null, UnsafeNativeMethods.GetDesktopWindow()), NativeMethods.NullHandleRef, NativeMethods.DCX_WINDOW | NativeMethods.DCX_LOCKWINDOWUPDATE | NativeMethods.DCX_CACHE); + IntPtr brush = SafeNativeMethods.CreateSolidBrush(ColorTranslator.ToWin32(backColor)); + + int prevRop2 = SafeNativeMethods.SetROP2(new HandleRef(null, dc), rop2); + IntPtr oldBrush = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, brush)); + + // PatBlt must be the only Win32 function that wants height in width rather than x2,y2. + SafeNativeMethods.PatBlt(new HandleRef(null, dc), rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, rop3); + + SafeNativeMethods.SetROP2(new HandleRef(null, dc), prevRop2); + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, oldBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, brush)); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); + } + + // Converts the font into one where Font.Unit = Point. + // If the original font is in device-dependent units (and it usually is), + // we interpret the size relative to the screen. + // + // This is not really a general-purpose function -- when used on something + // not obtained from ChooseFont, it may round away some precision. + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal static Font FontInPoints(Font font) { + return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont); + } + + // Returns whether or not target was changed + internal static bool FontToIFont(Font source, UnsafeNativeMethods.IFont target) { + bool changed = false; + + // we need to go through all the pain of the diff here because + // it looks like setting them all has different results based on the + // order and each individual IFont implementor... + // + string fontName = target.GetName(); + if (!source.Name.Equals(fontName)) { + target.SetName(source.Name); + changed = true; + } + + // Microsoft, Review: this always seems to come back as + // the point size * 10000 (HIMETRIC?), regardless + // or ratio or mapping mode, and despite what + // the documentation says... + // + // Either figure out what's going on here or + // do the process that the windows forms FONT object does here + // or, worse case, just create another Font object + // from the handle, but that's pretty heavy... + // + float fontSize = (float)target.GetSize() / 10000; + + // size must be in points + float winformsSize = source.SizeInPoints; + if (winformsSize != fontSize) { + target.SetSize((long)(winformsSize * 10000)); + changed = true; + } + + NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); + + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + source.ToLogFont(logfont); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + short fontWeight = target.GetWeight(); + if (fontWeight != logfont.lfWeight) { + target.SetWeight((short)logfont.lfWeight); + changed = true; + } + + bool fontBold = target.GetBold(); + if (fontBold != (logfont.lfWeight >= 700)) { + target.SetBold(logfont.lfWeight >= 700); + changed = true; + } + + bool fontItalic = target.GetItalic(); + if (fontItalic != (0 != logfont.lfItalic)) { + target.SetItalic(0 != logfont.lfItalic); + changed = true; + } + + bool fontUnderline = target.GetUnderline(); + if (fontUnderline != (0 != logfont.lfUnderline)) { + target.SetUnderline(0 != logfont.lfUnderline); + changed = true; + } + + bool fontStrike = target.GetStrikethrough(); + if (fontStrike != (0 != logfont.lfStrikeOut)) { + target.SetStrikethrough(0 != logfont.lfStrikeOut); + changed = true; + } + + short fontCharset = target.GetCharset(); + if (fontCharset != logfont.lfCharSet) { + target.SetCharset(logfont.lfCharSet); + changed = true; + } + + return changed; + } + + /// + /// + /// This makes a choice from a set of raster op codes, based on the color given. If the + /// color is considered to be "dark", the raster op provided by dark will be returned. + /// + private static int GetColorRop(Color color, int darkROP, int lightROP) { + if (color.GetBrightness() < .5) { + return darkROP; + } + return lightROP; + } + + /// + /// + /// Retrieves the brush used to draw active objects. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Machine)] + private static Brush GetActiveBrush(Color backColor) { + Color brushColor; + + if (backColor.GetBrightness() <= .5) { + brushColor = SystemColors.ControlLight; + } + else { + brushColor = SystemColors.ControlDark; + } + + if (frameBrushActive == null || + !frameColorActive.Equals(brushColor)) { + + if (frameBrushActive != null) { + frameBrushActive.Dispose(); + frameBrushActive = null; + } + + frameColorActive = brushColor; + + int patternSize = 8; + + Bitmap bitmap = new Bitmap(patternSize, patternSize); + + // gpr : bitmap does not initialize itself to be zero? + // + for (int x = 0; x < patternSize; x++) { + for (int y = 0; y < patternSize; y++) { + bitmap.SetPixel(x, y, Color.Transparent); + } + } + + for (int y = 0; y < patternSize; y++) { + for (int x = -y; x < patternSize; x += 4) { + if (x >= 0) { + bitmap.SetPixel(x, y, brushColor); + } + } + } + + frameBrushActive = new TextureBrush(bitmap); + bitmap.Dispose(); + } + + return frameBrushActive; + } + + /// + /// + /// Retrieves the pen used to draw a focus rectangle around a control. The focus + /// rectangle is typically drawn when the control has keyboard focus. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Machine)] + private static Pen GetFocusPen(Color baseColor, bool odds, bool highContrast) { + if (focusPen == null || + (!highContrast && focusPenColor.GetBrightness() <= .5 && baseColor.GetBrightness() <= .5) || + focusPenColor.ToArgb() != baseColor.ToArgb() || + hcFocusPen != highContrast) { + + if (focusPen != null) { + focusPen.Dispose(); + focusPen = null; + focusPenInvert.Dispose(); + focusPenInvert = null; + } + + focusPenColor = baseColor; + hcFocusPen = highContrast; + + Bitmap b = new Bitmap(2,2); + Color color1 = Color.Transparent; + Color color2; + if (highContrast) { + // in highcontrast mode "baseColor" itself is used as the focus pen color + color2 = baseColor; + } + else { + // in non-highcontrast mode "baseColor" is used to calculate the focus pen colors + // in this mode "baseColor" is expected to contain background color of the control to do this calculation properly + color2 = Color.Black; + + if (baseColor.GetBrightness() <= .5) { + color1 = color2; + color2 = InvertColor(baseColor); + } + else if (baseColor == Color.Transparent) { + color1 = Color.White; + } + } + + b.SetPixel(1, 0, color2); + b.SetPixel(0, 1, color2); + b.SetPixel(0, 0, color1); + b.SetPixel(1, 1, color1); + + Brush brush = new TextureBrush(b); + focusPen = new Pen(brush, 1); + brush.Dispose(); // The Pen constructor copies what it needs from the brush + + b.SetPixel(1, 0, color1); + b.SetPixel(0, 1, color1); + b.SetPixel(0, 0, color2); + b.SetPixel(1, 1, color2); + + brush = new TextureBrush(b); + focusPenInvert = new Pen(brush, 1); + brush.Dispose(); + + b.Dispose(); + } + + return odds ? focusPen : focusPenInvert; + } + + /// + /// + /// Retrieves the brush used to draw selected objects. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Machine)] + private static Brush GetSelectedBrush(Color backColor) { + Color brushColor; + + if (backColor.GetBrightness() <= .5) { + brushColor = SystemColors.ControlLight; + } + else { + brushColor = SystemColors.ControlDark; + } + if (frameBrushSelected == null || + !frameColorSelected.Equals(brushColor)) { + + if (frameBrushSelected != null) { + frameBrushSelected.Dispose(); + frameBrushSelected = null; + } + + frameColorSelected = brushColor; + + int patternSize = 8; + + Bitmap bitmap = new Bitmap(patternSize, patternSize); + + // gpr : bitmap does not initialize itself to be zero? + // + for (int x = 0; x < patternSize; x++) { + for (int y = 0; y < patternSize; y++) { + bitmap.SetPixel(x, y, Color.Transparent); + } + } + + int start = 0; + + for (int x = 0; x < patternSize; x += 2) { + for (int y = start; y < patternSize; y += 2) { + bitmap.SetPixel(x, y, brushColor); + } + + start ^= 1; + } + + frameBrushSelected = new TextureBrush(bitmap); + bitmap.Dispose(); + } + + return frameBrushSelected; + } + + /// + /// + /// Converts an infinite value to "1". + /// + private static float InfinityToOne(float value) { + if (value == Single.NegativeInfinity || value == Single.PositiveInfinity) { + return 1.0f; + } + return value; + } + + /// + /// + /// Inverts the given color. + /// + private static Color InvertColor(Color color) { + return Color.FromArgb(color.A, (byte)~color.R, (byte)~color.G, (byte)~color.B); + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color Light(Color baseColor, float percOfLightLight) { + return new HLSColor(baseColor).Lighter(percOfLightLight); + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color Light(Color baseColor) { + return new HLSColor(baseColor).Lighter(0.5f); + } + + /// + /// + /// Creates a new color that is a object of the given color. + /// + public static Color LightLight(Color baseColor) { + return new HLSColor(baseColor).Lighter(1.0f); + } + +#if !GRAYSCALE_DISABLED + // Returns a monochrome bitmap based on the input. + private static Bitmap MakeMonochrome(Bitmap input, Color color) { + Bitmap output = new Bitmap(input.Width, input.Height); + output.SetResolution(input.HorizontalResolution, input.VerticalResolution); + Size size = input.Size; + int width = input.Width; + int height = input.Height; + + BitmapData inputData = input.LockBits(new Rectangle(0,0, width, height), + ImageLockMode.ReadOnly, + PixelFormat.Format32bppArgb); + BitmapData outputData = output.LockBits(new Rectangle(0,0, width, height), + ImageLockMode.WriteOnly, + PixelFormat.Format32bppArgb); + + Debug.Assert(inputData.Scan0 != IntPtr.Zero && outputData.Scan0 != IntPtr.Zero, "BitmapData.Scan0 is null; check marshalling"); + + int colorARGB = color.ToArgb(); + for (int y = 0; y < height; y++) { + IntPtr inputScan = (IntPtr)((long)inputData.Scan0 + y * inputData.Stride); + IntPtr outputScan = (IntPtr)((long)outputData.Scan0 + y * outputData.Stride); + for (int x = 0; x < width; x++) { + int pixel = Marshal.ReadInt32(inputScan,x*4); + if (pixel >> 24 == 0) + Marshal.WriteInt32(outputScan, x*4, 0); // transparent + else + Marshal.WriteInt32(outputScan, x*4, colorARGB); + } + } + input.UnlockBits(inputData); + output.UnlockBits(outputData); + + return output; + } +#endif + + internal static ColorMatrix MultiplyColorMatrix(float[][] matrix1, float[][] matrix2) { + int size = 5; // multiplies 2 5x5 matrices. + + // build up an empty 5x5 array for results + float[][] result = new float[size][]; + for (int row = 0; row < size; row++){ + result[row] = new float[size]; + } + + float[] column = new float[size]; + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + column[k] = matrix1[k][j]; + } + for (int i = 0; i < size; i++) { + float[] row = matrix2[i]; + float s = 0; + for (int k = 0; k < size; k++) { + s += row[k] * column[k]; + } + result[i][j] = s; + } + } + + return new ColorMatrix(result); + + } + //paint the border of the table + internal static void PaintTableControlBorder(TableLayoutPanelCellBorderStyle borderStyle, Graphics g, Rectangle bound) { + int x = bound.X; + int y = bound.Y; + int right = bound.Right; + int bottom = bound.Bottom; + //draw the outside bounding rectangle + switch(borderStyle) { + case TableLayoutPanelCellBorderStyle.None: + case TableLayoutPanelCellBorderStyle.Single: + break; + + case TableLayoutPanelCellBorderStyle.Inset: + case TableLayoutPanelCellBorderStyle.InsetDouble: + g.DrawLine(SystemPens.ControlDark, x, y, right - 1, y); + g.DrawLine(SystemPens.ControlDark, x, y, x, bottom - 1); + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, right - 1, y, right - 1, bottom - 1); + g.DrawLine(pen, x, bottom - 1, right - 1, bottom - 1); + } + break; + + case TableLayoutPanelCellBorderStyle.Outset: + case TableLayoutPanelCellBorderStyle.OutsetDouble: + case TableLayoutPanelCellBorderStyle.OutsetPartial: + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, x, y, right - 1, y); + g.DrawLine(pen, x, y, x, bottom - 1); + } + g.DrawLine(SystemPens.ControlDark, right - 1, y, right - 1, bottom - 1); + g.DrawLine(SystemPens.ControlDark, x, bottom - 1, right - 1, bottom - 1); + break; + } + } + + //paint individual cell of the table + internal static void PaintTableCellBorder(TableLayoutPanelCellBorderStyle borderStyle, Graphics g, Rectangle bound) { + + //next, paint the cell border + switch (borderStyle) { + case TableLayoutPanelCellBorderStyle.None : + break; + + case TableLayoutPanelCellBorderStyle.Single : + g.DrawRectangle(SystemPens.ControlDark, bound); + break; + + case TableLayoutPanelCellBorderStyle.Inset : + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, bound.X, bound.Y, bound.X + bound.Width - 1, bound.Y); + g.DrawLine(pen, bound.X, bound.Y, bound.X, bound.Y + bound.Height - 1); + } + + g.DrawLine(SystemPens.ControlDark, bound.X + bound.Width - 1, bound.Y, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y + bound.Height - 1, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + break; + + case TableLayoutPanelCellBorderStyle.InsetDouble : + g.DrawRectangle(SystemPens.Control, bound); + + //draw the shadow + bound = new Rectangle(bound.X + 1, bound.Y + 1, bound.Width - 1, bound.Height - 1); + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, bound.X, bound.Y, bound.X + bound.Width - 1, bound.Y); + g.DrawLine(pen, bound.X, bound.Y, bound.X, bound.Y + bound.Height - 1); + } + + g.DrawLine(SystemPens.ControlDark, bound.X + bound.Width - 1, bound.Y, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y + bound.Height - 1, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + break; + + case TableLayoutPanelCellBorderStyle.Outset : + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y, bound.X + bound.Width - 1, bound.Y); + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y, bound.X, bound.Y + bound.Height - 1); + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, bound.X + bound.Width - 1, bound.Y, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + g.DrawLine(pen, bound.X, bound.Y + bound.Height - 1, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + } + + break; + + case TableLayoutPanelCellBorderStyle.OutsetDouble : + case TableLayoutPanelCellBorderStyle.OutsetPartial : + g.DrawRectangle(SystemPens.Control, bound); + + //draw the shadow + bound = new Rectangle(bound.X + 1, bound.Y + 1, bound.Width - 1, bound.Height - 1); + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y, bound.X + bound.Width - 1, bound.Y); + g.DrawLine(SystemPens.ControlDark, bound.X, bound.Y, bound.X, bound.Y + bound.Height - 1); + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, bound.X + bound.Width - 1, bound.Y, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + g.DrawLine(pen, bound.X, bound.Y + bound.Height - 1, bound.X + bound.Width - 1, bound.Y + bound.Height - 1); + } + + break; + } + } + + /* Unused + // Takes a black and white image, and replaces those colors with the colors of your choice. + // The Alpha channel of the source bitmap will be ignored, meaning pixels with Color.Transparent + // (really transparent black) will be mapped to the replaceBlack color. + private static ColorMatrix RemapBlackAndWhiteAndTransparentMatrix(Color replaceBlack, Color replaceWhite) { + // Normalize the colors to 1.0. + + float normBlackRed = ((float)replaceBlack.R)/(float)255.0; + float normBlackGreen = ((float)replaceBlack.G)/(float)255.0; + float normBlackBlue = ((float)replaceBlack.B)/(float)255.0; + float normBlackAlpha = ((float)replaceBlack.A)/(float)255.0; + + float normWhiteRed = ((float)replaceWhite.R)/(float)255.0; + float normWhiteGreen = ((float)replaceWhite.G)/(float)255.0; + float normWhiteBlue = ((float)replaceWhite.B)/(float)255.0; + float normWhiteAlpha = ((float)replaceWhite.A)/(float)255.0; + + // Set up a matrix that will map white to replaceWhite and + // black and transparent black to replaceBlack. + // + // | -B -B -B -B 0 | + // | r g b a | + // | | + // | W W W W 0 | + // | r g b a | + // | | + // [ R G B A ] * | 0 0 0 0 0 | = [ R' G' B' A' ] + // | | + // | | + // | 0 0 0 0 0 | + // | | + // | | + // | B B B B 1 | + // | r g b a | + + ColorMatrix matrix = new ColorMatrix(); + + matrix.Matrix00 = -normBlackRed; + matrix.Matrix01 = -normBlackGreen; + matrix.Matrix02 = -normBlackBlue; + matrix.Matrix03 = -normBlackAlpha; + + matrix.Matrix10 = normWhiteRed; + matrix.Matrix11 = normWhiteGreen; + matrix.Matrix12 = normWhiteBlue; + matrix.Matrix13 = normWhiteAlpha; + + matrix.Matrix40 = normBlackRed; + matrix.Matrix41 = normBlackGreen; + matrix.Matrix42 = normBlackBlue; + matrix.Matrix43 = normBlackAlpha; + matrix.Matrix44 = 1.0f; + + return matrix; + } + */ + + // Takes a black and white image, and replaces those colors with the colors of your choice. + // The replaceBlack and replaceWhite colors must have alpha = 255, because the alpha value + // of the bitmap is preserved. + private static ColorMatrix RemapBlackAndWhitePreserveTransparentMatrix(Color replaceBlack, Color replaceWhite) { + Debug.Assert(replaceBlack.A == 255, "replaceBlack.Alpha is ignored, so please set it to 255 so I know you know what you're doing"); + Debug.Assert(replaceWhite.A == 255, "replaceWhite.Alpha is ignored, so please set it to 255 so I know you know what you're doing"); + + // Normalize the colors to 1.0. + + float normBlackRed = ((float)replaceBlack.R)/(float)255.0; + float normBlackGreen = ((float)replaceBlack.G)/(float)255.0; + float normBlackBlue = ((float)replaceBlack.B)/(float)255.0; + float normBlackAlpha = ((float)replaceBlack.A)/(float)255.0; + + float normWhiteRed = ((float)replaceWhite.R)/(float)255.0; + float normWhiteGreen = ((float)replaceWhite.G)/(float)255.0; + float normWhiteBlue = ((float)replaceWhite.B)/(float)255.0; + float normWhiteAlpha = ((float)replaceWhite.A)/(float)255.0; + + // Set up a matrix that will map white to replaceWhite and + // black to replaceBlack, using the source bitmap's alpha value for the output + // + // | -B -B -B 0 0 | + // | r g b | + // | | + // | W W W 0 0 | + // | r g b | + // | | + // [ R G B A ] * | 0 0 0 0 0 | = [ R' G' B' A ] + // | | + // | | + // | 0 0 0 1 0 | + // | | + // | | + // | B B B 0 1 | + // | r g b | + + ColorMatrix matrix = new ColorMatrix(); + + matrix.Matrix00 = -normBlackRed; + matrix.Matrix01 = -normBlackGreen; + matrix.Matrix02 = -normBlackBlue; + + matrix.Matrix10 = normWhiteRed; + matrix.Matrix11 = normWhiteGreen; + matrix.Matrix12 = normWhiteBlue; + + matrix.Matrix33 = 1.0f; + + matrix.Matrix40 = normBlackRed; + matrix.Matrix41 = normBlackGreen; + matrix.Matrix42 = normBlackBlue; + matrix.Matrix44 = 1.0f; + + return matrix; + } + + /* Unused + internal static StringAlignment TranslateAlignment(HorizontalAlignment align) { + StringAlignment result; + switch (align) { + case HorizontalAlignment.Right: + result = StringAlignment.Far; + break; + case HorizontalAlignment.Center: + result = StringAlignment.Center; + break; + case HorizontalAlignment.Left: + default: + result = StringAlignment.Near; + break; + } + + return result; + } + */ + + internal static TextFormatFlags TextFormatFlagsForAlignmentGDI(ContentAlignment align) { + TextFormatFlags output = new TextFormatFlags(); + output |= TranslateAlignmentForGDI(align); + output |= TranslateLineAlignmentForGDI(align); + return output; + } + + internal static StringAlignment TranslateAlignment(ContentAlignment align) { + StringAlignment result; + if ((align & anyRight) != 0) + result = StringAlignment.Far; + else if ((align & anyCenter) != 0) + result = StringAlignment.Center; + else + result = StringAlignment.Near; + return result; + } + + + internal static TextFormatFlags TranslateAlignmentForGDI(ContentAlignment align) { + TextFormatFlags result; + if ((align & anyBottom) != 0) + result = TextFormatFlags.Bottom; + else if ((align & anyMiddle) != 0) + result = TextFormatFlags.VerticalCenter; + else + result = TextFormatFlags.Top; + return result; + } + + + internal static StringAlignment TranslateLineAlignment(ContentAlignment align) { + StringAlignment result; + if ((align & anyBottom) != 0) { + result = StringAlignment.Far; + } + else if ((align & anyMiddle) != 0) { + result = StringAlignment.Center; + } + else { + result = StringAlignment.Near; + } + return result; + } + + internal static TextFormatFlags TranslateLineAlignmentForGDI(ContentAlignment align) { + TextFormatFlags result; + if ((align & anyRight) != 0) + result = TextFormatFlags.Right; + else if ((align & anyCenter) != 0) + result = TextFormatFlags.HorizontalCenter; + else + result = TextFormatFlags.Left; + return result; + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal static StringFormat StringFormatForAlignment(ContentAlignment align) { + StringFormat output = new StringFormat(); + output.Alignment = TranslateAlignment(align); + output.LineAlignment = TranslateLineAlignment(align); + return output; + } + + /* Unused + internal static StringFormat StringFormatForAlignment(HorizontalAlignment align) { + StringFormat output = new StringFormat(); + output.Alignment = TranslateAlignment(align); + return output; + } + */ + + /// + /// Get StringFormat object for rendering text using GDI+ (Graphics). + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal static StringFormat CreateStringFormat( Control ctl, ContentAlignment textAlign, bool showEllipsis, bool useMnemonic ) { + + StringFormat stringFormat = ControlPaint.StringFormatForAlignment( textAlign ); + + // make sure that the text is contained within the label + // + + + // Adjust string format for Rtl controls + if( ctl.RightToLeft == RightToLeft.Yes ) { + stringFormat.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + } + + if( showEllipsis ) { + stringFormat.Trimming = StringTrimming.EllipsisCharacter; + stringFormat.FormatFlags |= StringFormatFlags.LineLimit; + } + + if( !useMnemonic ) { + stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None; + } + else if( ctl.ShowKeyboardCues ) { + stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show; + } + else { + stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Hide; + } + + if( ctl.AutoSize ) { + stringFormat.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; + } + + return stringFormat; + } + + /// + /// Get TextFormatFlags flags for rendering text using GDI (TextRenderer). + /// + internal static TextFormatFlags CreateTextFormatFlags(Control ctl, ContentAlignment textAlign, bool showEllipsis, bool useMnemonic ) { + + textAlign = ctl.RtlTranslateContent( textAlign ); + TextFormatFlags flags = ControlPaint.TextFormatFlagsForAlignmentGDI( textAlign ); + + // The effect of the TextBoxControl flag is that in-word line breaking will occur if needed, this happens when AutoSize + // is false and a one-word line still doesn't fit the binding box (width). The other effect is that partially visible + // lines are clipped; this is how GDI+ works by default. + flags |= TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl; + + if( showEllipsis ) { + flags |= TextFormatFlags.EndEllipsis; + } + + // Adjust string format for Rtl controls + if( ctl.RightToLeft == RightToLeft.Yes ) { + flags |= TextFormatFlags.RightToLeft; + } + + //if we don't use mnemonic, set formatFlag to NoPrefix as this will show the ampersand + if( !useMnemonic ) { + flags |= TextFormatFlags.NoPrefix; + } + //else if we don't show keyboard cues, set formatFlag to HidePrefix as this will hide + //the ampersand if we don't press down the alt key + else if( !ctl.ShowKeyboardCues ) { + flags |= TextFormatFlags.HidePrefix; + } + + return flags; + } + + + /// + /// + /// Logic copied from Win2K sources to copy the lightening and + /// darkening of colors. + /// + private struct HLSColor { + private const int ShadowAdj = -333; + private const int HilightAdj = 500; + private const int WatermarkAdj = -50; + + private const int Range = 240; + private const int HLSMax = Range; + private const int RGBMax = 255; + private const int Undefined = HLSMax*2/3; + + private int hue; + private int saturation; + private int luminosity; + + private bool isSystemColors_Control; + + /// + /// + /// + public HLSColor(Color color) { + isSystemColors_Control = (color.ToKnownColor() == SystemColors.Control.ToKnownColor()); + int r = color.R; + int g = color.G; + int b = color.B; + int max, min; /* max and min RGB values */ + int sum, dif; + int Rdelta,Gdelta,Bdelta; /* intermediate value: % of spread from max */ + + /* calculate lightness */ + max = Math.Max( Math.Max(r,g), b); + min = Math.Min( Math.Min(r,g), b); + sum = max + min; + + luminosity = (((sum * HLSMax) + RGBMax)/(2*RGBMax)); + + dif = max - min; + if (dif == 0) { /* r=g=b --> achromatic case */ + saturation = 0; /* saturation */ + hue = Undefined; /* hue */ + } + else { /* chromatic case */ + /* saturation */ + if (luminosity <= (HLSMax/2)) + saturation = (int) (((dif * (int) HLSMax) + (sum / 2) ) / sum); + else + saturation = (int) ((int) ((dif * (int) HLSMax) + (int)((2*RGBMax-sum)/2) ) + / (2*RGBMax-sum)); + /* hue */ + Rdelta = (int) (( ((max-r)*(int)(HLSMax/6)) + (dif / 2) ) / dif); + Gdelta = (int) (( ((max-g)*(int)(HLSMax/6)) + (dif / 2) ) / dif); + Bdelta = (int) (( ((max-b)*(int)(HLSMax/6)) + (dif / 2) ) / dif); + + if ((int) r == max) + hue = Bdelta - Gdelta; + else if ((int)g == max) + hue = (HLSMax/3) + Rdelta - Bdelta; + else /* B == cMax */ + hue = ((2*HLSMax)/3) + Gdelta - Rdelta; + + if (hue < 0) + hue += HLSMax; + if (hue > HLSMax) + hue -= HLSMax; + } + } + + /* Unused + /// + /// + /// + public int Hue { + get { + return hue; + } + } + */ + + /// + /// + /// + public int Luminosity { + get { + return luminosity; + } + } + + /* Unused + /// + /// + /// + public int Saturation { + get { + return saturation; + } + } + */ + + /// + /// + /// + public Color Darker(float percDarker) { + if (isSystemColors_Control) { + // With the usual color scheme, ControlDark/DarkDark is not exactly + // what we would otherwise calculate + if (percDarker == 0.0f) { + return SystemColors.ControlDark; + } + else if (percDarker == 1.0f) { + return SystemColors.ControlDarkDark; + } + else { + Color dark = SystemColors.ControlDark; + Color darkDark = SystemColors.ControlDarkDark; + + int dr = dark.R - darkDark.R; + int dg = dark.G - darkDark.G; + int db = dark.B - darkDark.B; + + return Color.FromArgb((byte)(dark.R - (byte)(dr * percDarker)), + (byte)(dark.G - (byte)(dg * percDarker)), + (byte)(dark.B - (byte)(db * percDarker))); + } + } + else { + int oneLum = 0; + int zeroLum = NewLuma(ShadowAdj, true); + + /* + if (luminosity < 40) { + zeroLum = NewLuma(120, ShadowAdj, true); + } + else { + zeroLum = NewLuma(ShadowAdj, true); + } + */ + + return ColorFromHLS(hue, zeroLum - (int)((zeroLum - oneLum) * percDarker), saturation); + } + } + + public override bool Equals(object o) { + if (!(o is HLSColor)) { + return false; + } + + HLSColor c = (HLSColor)o; + return hue == c.hue && + saturation == c.saturation && + luminosity == c.luminosity && + isSystemColors_Control == c.isSystemColors_Control; + } + + public static bool operator ==(HLSColor a, HLSColor b) { + return a.Equals(b); + } + + public static bool operator !=(HLSColor a, HLSColor b) { + return !a.Equals(b); + } + + public override int GetHashCode() { + return hue << 6 | saturation << 2 | luminosity; + } + + /// + /// + /// + public Color Lighter(float percLighter) { + if (isSystemColors_Control) { + // With the usual color scheme, ControlLight/LightLight is not exactly + // what we would otherwise calculate + if (percLighter == 0.0f) { + return SystemColors.ControlLight; + } + else if (percLighter == 1.0f) { + return SystemColors.ControlLightLight; + } + else { + Color light = SystemColors.ControlLight; + Color lightLight = SystemColors.ControlLightLight; + + int dr = light.R - lightLight.R; + int dg = light.G - lightLight.G; + int db = light.B - lightLight.B; + + return Color.FromArgb((byte)(light.R - (byte)(dr * percLighter)), + (byte)(light.G - (byte)(dg * percLighter)), + (byte)(light.B - (byte)(db * percLighter))); + } + } + else { + int zeroLum = luminosity; + int oneLum = NewLuma(HilightAdj, true); + + /* + if (luminosity < 40) { + zeroLum = 120; + oneLum = NewLuma(120, HilightAdj, true); + } + else { + zeroLum = luminosity; + oneLum = NewLuma(HilightAdj, true); + } + */ + + return ColorFromHLS(hue, zeroLum + (int)((oneLum - zeroLum) * percLighter), saturation); + } + } + + /// + /// + /// + private int NewLuma(int n, bool scale) { + return NewLuma(luminosity, n, scale); + } + + /// + /// + /// + private int NewLuma(int luminosity, int n, bool scale) { + if (n == 0) + return luminosity; + + if (scale) { + if (n > 0) { + return(int)(((int)luminosity * (1000 - n) + (Range + 1L) * n) / 1000); + } + else { + return(int)(((int)luminosity * (n + 1000)) / 1000); + } + } + + int newLum = luminosity; + newLum += (int)((long)n * Range / 1000); + + if (newLum < 0) + newLum = 0; + if (newLum > HLSMax) + newLum = HLSMax; + + return newLum; + } + + /// + /// + /// + private Color ColorFromHLS(int hue, int luminosity, int saturation) { + byte r,g,b; /* RGB component values */ + int magic1,magic2; /* calculated magic numbers (really!) */ + + if (saturation == 0) { /* achromatic case */ + r = g = b = (byte)((luminosity * RGBMax) / HLSMax); + if (hue != Undefined) { + /* ERROR */ + } + } + else { /* chromatic case */ + /* set up magic numbers */ + if (luminosity <= (HLSMax/2)) + magic2 = (int)((luminosity * ((int)HLSMax + saturation) + (HLSMax/2))/HLSMax); + else + magic2 = luminosity + saturation - (int)(((luminosity*saturation) + (int)(HLSMax/2))/HLSMax); + magic1 = 2*luminosity-magic2; + + /* get RGB, change units from HLSMax to RGBMax */ + r = (byte)(((HueToRGB(magic1,magic2,(int)(hue+(int)(HLSMax/3)))*(int)RGBMax + (HLSMax/2))) / (int)HLSMax); + g = (byte)(((HueToRGB(magic1,magic2,hue)*(int)RGBMax + (HLSMax/2))) / HLSMax); + b = (byte)(((HueToRGB(magic1,magic2,(int)(hue-(int)(HLSMax/3)))*(int)RGBMax + (HLSMax/2))) / (int)HLSMax); + } + return Color.FromArgb(r,g,b); + } + + /// + /// + /// + private int HueToRGB(int n1, int n2, int hue) { + /* range check: note values passed add/subtract thirds of range */ + + /* The following is redundant for WORD (unsigned int) */ + if (hue < 0) + hue += HLSMax; + + if (hue > HLSMax) + hue -= HLSMax; + + /* return r,g, or b value from this tridrant */ + if (hue < (HLSMax/6)) + return( n1 + (((n2-n1)*hue+(HLSMax/12))/(HLSMax/6)) ); + if (hue < (HLSMax/2)) + return( n2 ); + if (hue < ((HLSMax*2)/3)) + return( n1 + (((n2-n1)*(((HLSMax*2)/3)-hue)+(HLSMax/12)) / (HLSMax/6)) ); + else + return( n1 ); + + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ControlStyles.cs b/WindowsForms/Managed/System/WinForms/ControlStyles.cs new file mode 100644 index 000000000..4790881d3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlStyles.cs @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.ComponentModel; + + using System.Diagnostics; + using System; + + /// + /// + /// + /// Specifies control functionality. + /// + /// + /// + [Flags] + public enum ControlStyles { + /// + /// + /// Indicates whether the control is a container-like control. + /// + ContainerControl= 0x00000001, + /// + /// + /// + /// The control paints itself; WM_PAINT and WM_ERASEBKGND messages are not passed + /// on to the underlying NativeWindow. + /// + /// + /// This style only applies to subclasses of Control. + /// + /// + UserPaint = 0x00000002, + /// + /// + /// + /// If specified, a PaintBackground event will not be raised, OnPaintBackground will not be called, + /// and Invalidate() will not invalidate the background of the HWND. + /// + /// + Opaque = 0x00000004, + /// + /// + /// + /// The control is completely redrawn when it is resized. + /// + /// + ResizeRedraw = 0x00000010, + /// + /// + /// + /// The control has a fixed width. + /// + /// + FixedWidth = 0x00000020, + /// + /// + /// + /// The control has a fixed height. + /// + /// + FixedHeight = 0x00000040, + /// + /// + /// + /// If set, windows forms calls OnClick and raises the Click event when the control is clicked + /// (unless it's the second click of a double-click and StandardDoubleClick is specified). + /// Regardless of this style, the control may call OnClick directly. + /// + /// + StandardClick = 0x00000100, + /// + /// + /// + /// The control can get the focus. + /// + /// + Selectable = 0x00000200, + /// + /// + /// + /// The control does its own mouse processing; WM_MOUSEDOWN, WM_MOUSEMOVE, and WM_MOUSEUP messages are not passed + /// on to the underlying NativeWindow. + /// + /// + UserMouse = 0x00000400, + /// + /// + /// + /// If the BackColor is set to a color whose alpha component is + /// less than 255 (i.e., BackColor.A < 255), OnPaintBackground will simulate transparency + /// by asking its parent control to paint our background. This is not true transparency -- + /// if there is another control between us and our parent, we will not show the control in the middle. + /// + /// + /// This style only applies to subclasses of Control. It only works if UserPaint is set, + /// and the parent control is a Control. + /// + /// + SupportsTransparentBackColor = 0x00000800, + /// + /// + /// + /// If set, windows forms calls OnDoubleClick and raises the DoubleClick event when the control is double clicked. + /// Regardless of whether it is set, the control may call OnDoubleClick directly. + /// This style is ignored if StandardClick is not set. + /// + /// + StandardDoubleClick = 0x00001000, + /// + /// + /// + /// If true, WM_ERASEBKGND is ignored, and both OnPaintBackground and OnPaint are called directly from + /// WM_PAINT. This generally reduces flicker, but can cause problems if other controls + /// send WM_ERASEBKGND messages to us. (This is sometimes done to achieve a pseudo-transparent effect similar to + /// ControlStyles.SupportsTransparentBackColor; for instance, ToolBar with flat appearance does this). + /// This style only makes sense if UserPaint is true. + /// + /// + AllPaintingInWmPaint = 0x00002000, + /// + /// + /// + /// If true, the control keeps a copy of the text rather than going to the hWnd for the + /// text every time. This improves performance but makes it difficult to keep the control + /// and hWnd's text synchronized. + /// This style defaults to false. + /// + /// + CacheText = 0x00004000, + /// + /// + /// + /// If true, the OnNotifyMessage method will be called for every message sent to + /// the control's WndProc. + /// This style defaults to false. + /// + /// + EnableNotifyMessage = 0x00008000, + /// + /// + /// + /// If set, all control painting will be double buffered. You must also + /// set the UserPaint and AllPaintingInWmPaint style. Note: This is obsolete, please + /// use OptimizedDoubleBuffer instead. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] // It is recommended that you use the DoubleBuffer property instead. See VSWhidbey 502873 + DoubleBuffer = 0x00010000, + /// + /// + /// + /// If set, all control painting will be double buffered. + /// + /// + OptimizedDoubleBuffer = 0x00020000, + /// + /// + /// + /// If this style is set, and there is a value in the control's Text property, that value will be + /// used to determine the control's default Active Accessibility name and shortcut key. Otherwise, + /// the text of the preceding Label control will be used instead. + /// + /// This style is set by default. Certain built-in control types such as TextBox and ComboBox + /// un-set this style, so that their current text will not be used by Active Accessibility. + /// + /// + UseTextForAccessibility = 0x00040000, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ControlUpdateMode.cs b/WindowsForms/Managed/System/WinForms/ControlUpdateMode.cs new file mode 100644 index 000000000..3c79c943d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ControlUpdateMode.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + /// + /// Determines when changes to a data source property get propagated up to the corresponding data-bound control property. + /// + public enum ControlUpdateMode { + + /// + /// + /// Control property is updated whenever the data source property changes, or the data source position changes. + /// This is the default update mode. + /// + OnPropertyChanged = 0, + + /// + /// + /// Control property is never updated. Binding is "write-only" with respect to the data source. + /// To force the control property to be updated, use the Binding.ReadValue method. + /// + Never = 1, + } +} diff --git a/WindowsForms/Managed/System/WinForms/ConvertEvent.cs b/WindowsForms/Managed/System/WinForms/ConvertEvent.cs new file mode 100644 index 000000000..913826472 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ConvertEvent.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// [To be supplied.] + /// + public class ConvertEventArgs : EventArgs { + + private object value; + private Type desiredType; + + /// + /// + /// [To be supplied.] + /// + public ConvertEventArgs(object value, Type desiredType) { + this.value = value; + this.desiredType = desiredType; + } + + /// + /// + /// [To be supplied.] + /// + public object Value { + get { + return value; + } + set { + this.value = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public Type DesiredType { + get { + return desiredType; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ConvertEventHandler.cs b/WindowsForms/Managed/System/WinForms/ConvertEventHandler.cs new file mode 100644 index 000000000..5964348c1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ConvertEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + /// + /// + /// [To be supplied.] + /// + public delegate void ConvertEventHandler(object sender, ConvertEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/CreateParams.cs b/WindowsForms/Managed/System/WinForms/CreateParams.cs new file mode 100644 index 000000000..8dbd7ad70 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CreateParams.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Text; + using System; + + /// + /// + /// [To be supplied.] + /// + public class CreateParams { + string className; + string caption; + int style; + int exStyle; + int classStyle; + int x; + int y; + int width; + int height; + IntPtr parent; + object param; + + /// + /// + /// Name of the window class to subclass. The default value for this field + /// is null, indicating that the window is not a subclass of an existing + /// window class. To subclass an existing window class, store the window + /// class name in this field. For example, to subclass the standard edit + /// control, set this field to "EDIT". + /// + public string ClassName { + get { return className; } + set { className = value; } + } + + /// + /// + /// The initial caption your control will have. + /// + public string Caption { + get { return caption; } + set { caption = value; } + } + + /// + /// + /// Window style bits. This must be a combination of WS_XXX style flags and + /// other control-specific style flags. + /// + public int Style { + get { return style; } + set { style = value; } + } + + /// + /// + /// Extended window style bits. This must be a combination of WS_EX_XXX + /// style flags. + /// + public int ExStyle { + get { return exStyle; } + set { exStyle = value; } + } + + /// + /// + /// Class style bits. This must be a combination of CS_XXX style flags. This + /// field is ignored if the className field is not null. + /// + public int ClassStyle { + get { return classStyle; } + set { classStyle = value; } + } + + /// + /// + /// The left portion of the initial proposed location. + /// + public int X { + get { return x; } + set { x = value; } + } + + /// + /// + /// The top portion of the initial proposed location. + /// + public int Y { + get { return y; } + set { y = value; } + } + + /// + /// + /// The initially proposed width. + /// + public int Width { + get { return width; } + set { width = value; } + } + + /// + /// + /// The initially proposed height. + /// + public int Height { + get { return height; } + set { height = value; } + } + + /// + /// + /// The controls parent. + /// + public IntPtr Parent { + get { return parent; } + set { parent = value; } + } + + /// + /// + /// Any extra information that the underlying handle might want. + /// + public object Param { + get { return param; } + set { param = value; } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + StringBuilder sb = new StringBuilder(64); + sb.Append("CreateParams {'"); + sb.Append(className); + sb.Append("', '"); + sb.Append(caption); + sb.Append("', 0x"); + sb.Append(Convert.ToString(style, 16)); + sb.Append(", 0x"); + sb.Append(Convert.ToString(exStyle, 16)); + sb.Append(", {"); + sb.Append(x); + sb.Append(", "); + sb.Append(y); + sb.Append(", "); + sb.Append(width); + sb.Append(", "); + sb.Append(height); + sb.Append("}"); + sb.Append("}"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CurrencyManager.cs b/WindowsForms/Managed/System/WinForms/CurrencyManager.cs new file mode 100644 index 000000000..f7fbe3840 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CurrencyManager.cs @@ -0,0 +1,1016 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using System.Collections; + using System.Reflection; + using System.Globalization; + + /// + /// + /// Manages the position and bindings of a + /// list. + /// + public class CurrencyManager : BindingManagerBase { + + private Object dataSource; + private IList list; + + private bool bound = false; + private bool shouldBind = true; + + /// + /// + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields") // We can't make CurrencyManager.listposition internal + // because it would be a breaking change. + ] + protected int listposition = -1; + + private int lastGoodKnownRow = -1; + private bool pullingData = false; + + private bool inChangeRecordState = false; + private bool suspendPushDataInCurrentChanged = false; + // private bool onItemChangedCalled = false; + // private EventHandler onCurrentChanged; + // private CurrentChangingEventHandler onCurrentChanging; + private ItemChangedEventHandler onItemChanged; + private ListChangedEventHandler onListChanged; + private ItemChangedEventArgs resetEvent = new ItemChangedEventArgs(-1); + private EventHandler onMetaDataChangedHandler; + + /// + /// + /// Gets the type of the list. + /// + protected Type finalType; + + /// + /// + /// Occurs when the + /// current item has been + /// altered. + /// + [SRCategory(SR.CatData)] + public event ItemChangedEventHandler ItemChanged { + add { + onItemChanged += value; + } + remove { + onItemChanged -= value; + } + } + + /// + public event ListChangedEventHandler ListChanged { + add { + onListChanged += value; + } + remove { + onListChanged -= value; + } + } + + /// + /// + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the dataSource + // it would be a breaking change. + ] + internal CurrencyManager(Object dataSource) { + SetDataSource(dataSource); + } + + /// + /// Gets a value indicating + /// whether items can be added to the list. + /// + internal bool AllowAdd { + get { + if (list is IBindingList) { + return ((IBindingList)list).AllowNew; + } + if (list == null) + return false; + return !list.IsReadOnly && !list.IsFixedSize; + } + } + + /// + /// Gets a value + /// indicating whether edits to the list are allowed. + /// + internal bool AllowEdit { + get { + if (list is IBindingList) { + return ((IBindingList)list).AllowEdit; + } + if (list == null) + return false; + return !list.IsReadOnly; + } + } + + /// + /// Gets a value indicating whether items can be removed from the list. + /// + internal bool AllowRemove { + get { + if (list is IBindingList) { + return ((IBindingList)list).AllowRemove; + } + if (list == null) + return false; + return !list.IsReadOnly && !list.IsFixedSize; + } + } + + /// + /// + /// Gets the number of items in the list. + /// + public override int Count { + get { + if (list == null) + return 0; + else + return list.Count; + } + } + + /// + /// + /// Gets the current item in the list. + /// + public override Object Current { + get { + return this[Position]; + } + } + + internal override Type BindType { + get { + return ListBindingHelper.GetListItemType(this.List); + } + } + + /// + /// Gets the data source of the list. + /// + internal override Object DataSource { + get { + return dataSource; + } + } + + internal override void SetDataSource(Object dataSource) { + if (this.dataSource != dataSource) { + Release(); + this.dataSource = dataSource; + this.list = null; + this.finalType = null; + + Object tempList = dataSource; + if (tempList is Array) { + finalType = tempList.GetType(); + tempList = (Array)tempList; + } + + if (tempList is IListSource) { + tempList = ((IListSource)tempList).GetList(); + } + + if (tempList is IList) { + if (finalType == null) { + finalType = tempList.GetType(); + } + this.list = (IList)tempList; + WireEvents(list); + if (list.Count > 0 ) + listposition = 0; + else + listposition = -1; + OnItemChanged(resetEvent); + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1, -1)); + UpdateIsBinding(); + } + else { + if (tempList == null) { + throw new ArgumentNullException("dataSource"); + } + throw new ArgumentException(SR.GetString(SR.ListManagerSetDataSource, tempList.GetType().FullName), "dataSource"); + } + + } + } + + /// + /// + /// + /// Gets a value indicating whether the list is bound to a data source. + /// + internal override bool IsBinding { + get { + return bound; + } + } + + // The DataGridView needs this. + internal bool ShouldBind { + get { + return shouldBind; + } + } + + /// + /// + /// Gets the list as an object. + /// + public IList List { + get { + // NOTE: do not change this to throw an exception if the list is not IBindingList. + // doing this will cause a major performance hit when wiring the + // dataGrid to listen for MetaDataChanged events from the IBindingList + // (basically we would have to wrap all calls to CurrencyManager::List with + // a try/catch block.) + // + return list; + } + } + + /// + /// + /// Gets or sets the position you are at within the list. + /// + public override int Position { + get { + return listposition; + } + set { + if (listposition == -1) + return; + + if (value < 0) + value = 0; + int count = list.Count; + if (value >= count) + value = count - 1; + + ChangeRecordState(value, listposition != value, true, true, false); // true for endCurrentEdit + // true for firingPositionChange notification + // data will be pulled from controls anyway. + } + } + + /// + /// Gets or sets the object at the specified index. + /// + internal Object this[int index] { + get { + if (index < 0 || index >= list.Count) { + throw new IndexOutOfRangeException(SR.GetString(SR.ListManagerNoValue, index.ToString(CultureInfo.CurrentCulture))); + } + return list[index]; + } + set { + if (index < 0 || index >= list.Count) { + throw new IndexOutOfRangeException(SR.GetString(SR.ListManagerNoValue, index.ToString(CultureInfo.CurrentCulture))); + } + list[index] = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override void AddNew() { + IBindingList ibl = list as IBindingList; + if (ibl != null) { + ibl.AddNew(); + } + else { + // If the list is not IBindingList, then throw an exception: + throw new NotSupportedException(SR.GetString(SR.CurrencyManagerCantAddNew)); + } + + ChangeRecordState(list.Count - 1, (Position != list.Count - 1), (Position != list.Count - 1), true, true); // true for firingPositionChangeNotification + // true for pulling data from the controls + } + + /// + /// + /// Cancels the current edit operation. + /// + public override void CancelCurrentEdit() { + if (Count > 0) { + Object item = (Position >= 0 && Position < list.Count) ? list[Position] : null; + + // onItemChangedCalled = false; + + IEditableObject iEditableItem = item as IEditableObject; + if (iEditableItem != null) { + iEditableItem.CancelEdit(); + } + + ICancelAddNew iListWithCancelAddNewSupport = list as ICancelAddNew; + if (iListWithCancelAddNewSupport != null) { + iListWithCancelAddNewSupport.CancelNew(this.Position); + } + + OnItemChanged(new ItemChangedEventArgs(Position)); + if (this.Position != -1) { + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, this.Position)); + } + } + } + + private void ChangeRecordState(int newPosition, bool validating, bool endCurrentEdit, bool firePositionChange, bool pullData) { + if (newPosition == -1 && list.Count == 0) { + if (listposition != -1) { + this.listposition = -1; + OnPositionChanged(EventArgs.Empty); + } + return; + } + + if ((newPosition < 0 || newPosition >= Count) && this.IsBinding) { + throw new IndexOutOfRangeException(SR.GetString(SR.ListManagerBadPosition)); + } + + // if PushData fails in the OnCurrentChanged and there was a lastGoodKnownRow + // then the position does not change, so we should not fire the OnPositionChanged + // event; + // this is why we have to cache the old position and compare that w/ the position that + // the user will want to navigate to + int oldPosition = listposition; + if (endCurrentEdit) { + // Do not PushData when pro. See ASURT 65095. + inChangeRecordState = true; + try { + EndCurrentEdit(); + } finally { + inChangeRecordState = false; + } + } + + // we pull the data from the controls only when the ListManager changes the list. when the backEnd changes the list we do not + // pull the data from the controls + if (validating && pullData) { + CurrencyManager_PullData(); + } + + // vsWhidbey 425961: EndCurrentEdit or PullData can cause the list managed by the CurrencyManager to shrink. + this.listposition = Math.Min(newPosition, Count - 1); + + if (validating) { + OnCurrentChanged(EventArgs.Empty); + } + + bool positionChanging = (oldPosition != listposition); + if (positionChanging && firePositionChange) { + OnPositionChanged(EventArgs.Empty); + } + } + + /// + /// + /// Throws an exception if there is no list. + /// + protected void CheckEmpty() { + if (dataSource == null || list == null || list.Count == 0) { + throw new InvalidOperationException(SR.GetString(SR.ListManagerEmptyList)); + } + } + + // will return true if this function changes the position in the list + private bool CurrencyManager_PushData() { + if (pullingData) + return false; + + int initialPosition = listposition; + if (lastGoodKnownRow == -1) { + try { + PushData(); + } + catch (Exception ex) { + OnDataError(ex); + + // get the first item in the list that is good to push data + // for now, we assume that there is a row in the backEnd + // that is good for all the bindings. + FindGoodRow(); + } + lastGoodKnownRow = listposition; + } else { + try { + PushData(); + } + catch (Exception ex) { + OnDataError(ex); + + listposition = lastGoodKnownRow; + PushData(); + } + lastGoodKnownRow = listposition; + } + + return initialPosition != listposition; + } + + private bool CurrencyManager_PullData() { + bool success = true; + pullingData = true; + + try { + PullData(out success); + } finally { + pullingData = false; + } + + return success; + } + + /// + /// + /// [To be supplied.] + /// + public override void RemoveAt(int index) { + list.RemoveAt(index); + } + + /// + /// + /// Ends the current edit operation. + /// + public override void EndCurrentEdit() { + if (Count > 0) { + bool success = CurrencyManager_PullData(); + + if (success) { + Object item = (Position >= 0 && Position < list.Count) ? list[Position] : null; + + IEditableObject iEditableItem = item as IEditableObject; + if (iEditableItem != null) { + iEditableItem.EndEdit(); + } + + ICancelAddNew iListWithCancelAddNewSupport = list as ICancelAddNew; + if (iListWithCancelAddNewSupport != null) { + iListWithCancelAddNewSupport.EndNew(this.Position); + } + } + } + } + + private void FindGoodRow() { + int rowCount = this.list.Count; + for (int i = 0; i < rowCount; i++) { + listposition = i; + try { + PushData(); + } + catch (Exception ex) { + OnDataError(ex); + continue; + } + listposition = i; + return; + } + // if we got here, the list did not contain any rows suitable for the bindings + // suspend binding and throw an exception + SuspendBinding(); + throw new InvalidOperationException(SR.GetString(SR.DataBindingPushDataException)); + } + + /// + /// Sets the column to sort by, and the direction of the sort. + /// + internal void SetSort(PropertyDescriptor property, ListSortDirection sortDirection) { + if (list is IBindingList && ((IBindingList)list).SupportsSorting) { + ((IBindingList)list).ApplySort(property, sortDirection); + } + } + + /// + /// Gets a for a CurrencyManager. + /// + internal PropertyDescriptor GetSortProperty() { + if ((list is IBindingList) && ((IBindingList)list).SupportsSorting) { + return ((IBindingList)list).SortProperty; + } + return null; + } + + /// + /// Gets the sort direction of a list. + /// + internal ListSortDirection GetSortDirection() { + if ((list is IBindingList) && ((IBindingList)list).SupportsSorting) { + return ((IBindingList)list).SortDirection; + } + return ListSortDirection.Ascending; + } + + /// + /// Find the position of a desired list item. + /// + internal int Find(PropertyDescriptor property, Object key, bool keepIndex) { + if (key == null) + throw new ArgumentNullException("key"); + + if (property != null && (list is IBindingList) && ((IBindingList)list).SupportsSearching) { + return ((IBindingList)list).Find(property, key); + } + + if (property != null) { + for (int i = 0; i < list.Count; i++) { + object value = property.GetValue(list[i]); + if (key.Equals(value)) { + return i; + } + } + } + + return -1; + } + + /// + /// Gets the name of the list. + /// + internal override string GetListName() { + if (list is ITypedList) { + return ((ITypedList)list).GetListName(null); + } + else { + return finalType.Name; + } + } + + /// + /// + /// Gets the name of the specified list. + /// + protected internal override string GetListName(ArrayList listAccessors) { + if (list is ITypedList) { + PropertyDescriptor[] properties = new PropertyDescriptor[listAccessors.Count]; + listAccessors.CopyTo(properties, 0); + return ((ITypedList)list).GetListName(properties); + } + return ""; + } + + /// + /// + internal override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { + return ListBindingHelper.GetListItemProperties(this.list, listAccessors); + } + + /// + /// + /// Gets the for + /// the list. + /// + public override PropertyDescriptorCollection GetItemProperties() { + return GetItemProperties(null); + } + + /// + /// Gets the for the specified list. + /// + private void List_ListChanged(Object sender, System.ComponentModel.ListChangedEventArgs e) { + // If you change the assert below, better change the + // code in the OnCurrentChanged that deals w/ firing the OnCurrentChanged event + Debug.Assert(lastGoodKnownRow == -1 || lastGoodKnownRow == listposition, "if we have a valid lastGoodKnownRow, then it should equal the position in the list"); + + // bug 139627: when the data view fires an ItemMoved event where the old position is negative, that really means + // that a row was added + // bug 156236: when the data view fires an ItemMoved event where the new position is negative, that really means + // that a row was deleted + // dbe is our DataBindingEvent + // + ListChangedEventArgs dbe; + + if (e.ListChangedType == ListChangedType.ItemMoved && e.OldIndex < 0) { + dbe = new ListChangedEventArgs(ListChangedType.ItemAdded, e.NewIndex, e.OldIndex); + } else if (e.ListChangedType == ListChangedType.ItemMoved && e.NewIndex < 0) { + dbe = new ListChangedEventArgs(ListChangedType.ItemDeleted, e.OldIndex, e.NewIndex); + } else { + dbe = e; + } + + int oldposition = listposition; + + UpdateLastGoodKnownRow(dbe); + UpdateIsBinding(); + + if (list.Count == 0) { + listposition = -1; + + if (oldposition != -1) { + // if we used to have a current row, but not any more, then report current as changed + OnPositionChanged(EventArgs.Empty); + OnCurrentChanged(EventArgs.Empty); + } + + if (dbe.ListChangedType == System.ComponentModel.ListChangedType.Reset && e.NewIndex == -1) { + // if the list is reset, then let our users know about it. + OnItemChanged(resetEvent); + } + + if (dbe.ListChangedType == System.ComponentModel.ListChangedType.ItemDeleted) { + // if the list is reset, then let our users know about it. + OnItemChanged(resetEvent); + } + + // we should still fire meta data change notification even when the list is empty + if (e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorAdded || + e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorDeleted || + e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorChanged) + OnMetaDataChanged(EventArgs.Empty); + + // + + + OnListChanged(dbe); + return; + } + + suspendPushDataInCurrentChanged = true; + try { + switch (dbe.ListChangedType) { + case System.ComponentModel.ListChangedType.Reset: + Debug.WriteLineIf(CompModSwitches.DataCursor.TraceVerbose, "System.ComponentModel.ListChangedType.Reset Position: " + Position + " Count: " + list.Count); + if (listposition == -1 && list.Count > 0) + ChangeRecordState(0, true, false, true, false); // last false: we don't pull the data from the control when DM changes + else + ChangeRecordState(Math.Min(listposition,list.Count - 1), true, false, true, false); + UpdateIsBinding(/*raiseItemChangedEvent:*/ false); + OnItemChanged(resetEvent); + break; + case System.ComponentModel.ListChangedType.ItemAdded: + Debug.WriteLineIf(CompModSwitches.DataCursor.TraceVerbose, "System.ComponentModel.ListChangedType.ItemAdded " + dbe.NewIndex.ToString(CultureInfo.InvariantCulture)); + if (dbe.NewIndex <= listposition && listposition < list.Count - 1) { + // this means the current row just moved down by one. + // the position changes, so end the current edit + ChangeRecordState(listposition + 1, true, true, listposition != list.Count - 2, false); + UpdateIsBinding(); + // 85426: refresh the list after we got the item added event + OnItemChanged(resetEvent); + // 84460: when we get the itemAdded, and the position was at the end + // of the list, do the right thing and notify the positionChanged after refreshing the list + if (listposition == list.Count - 1) + OnPositionChanged(EventArgs.Empty); + break; + } else if (dbe.NewIndex == this.listposition && this.listposition == list.Count - 1 && this.listposition != -1) { + // The CurrencyManager has a non-empty list. + // The position inside the currency manager is at the end of the list and the list still fired an ItemAdded event. + // This could be the second ItemAdded event that the DataView fires to signal that the AddNew operation was commited. + // We need to fire CurrentItemChanged event so that relatedCurrencyManagers update their lists. + OnCurrentItemChanged(System.EventArgs.Empty); + } + + if (listposition == -1) { + // do not call EndEdit on a row that was not there ( position == -1) + ChangeRecordState(0, false, false, true, false); + } + UpdateIsBinding(); + // put the call to OnItemChanged after setting the position, so the + // controls would use the actual position. + // if we have a control bound to a dataView, and then we add a row to a the dataView, + // then the control will use the old listposition to get the data. and this is bad. + // + OnItemChanged(resetEvent); + break; + case System.ComponentModel.ListChangedType.ItemDeleted: + Debug.WriteLineIf(CompModSwitches.DataCursor.TraceVerbose, "System.ComponentModel.ListChangedType.ItemDeleted " + dbe.NewIndex.ToString(CultureInfo.InvariantCulture)); + if (dbe.NewIndex == listposition) { + // this means that the current row got deleted. + // cannot end an edit on a row that does not exist anymore + ChangeRecordState(Math.Min(listposition, Count - 1), true, false, true, false); + // put the call to OnItemChanged after setting the position + // in the currencyManager, so controls will use the actual position + OnItemChanged(resetEvent); + break; + + } + if (dbe.NewIndex < listposition) { + // this means the current row just moved up by one. + // cannot end an edit on a row that does not exist anymore + ChangeRecordState(listposition - 1, true, false, true, false); + // put the call to OnItemChanged after setting the position + // in the currencyManager, so controls will use the actual position + OnItemChanged(resetEvent); + break; + } + OnItemChanged(resetEvent); + break; + case System.ComponentModel.ListChangedType.ItemChanged: + Debug.WriteLineIf(CompModSwitches.DataCursor.TraceVerbose, "System.ComponentModel.ListChangedType.ItemChanged " + dbe.NewIndex.ToString(CultureInfo.InvariantCulture)); + // the current item changed + if (dbe.NewIndex == this.listposition) { + OnCurrentItemChanged(EventArgs.Empty); + } + + OnItemChanged(new ItemChangedEventArgs(dbe.NewIndex)); + break; + case System.ComponentModel.ListChangedType.ItemMoved: + Debug.WriteLineIf(CompModSwitches.DataCursor.TraceVerbose, "System.ComponentModel.ListChangedType.ItemMoved " + dbe.NewIndex.ToString(CultureInfo.InvariantCulture)); + if (dbe.OldIndex == listposition) { // current got moved. + // the position changes, so end the current edit. Make sure there is something that we can end edit... + ChangeRecordState(dbe.NewIndex, true, this.Position > -1 && this.Position < list.Count, true, false); + } + else if (dbe.NewIndex == listposition) { // current was moved + // the position changes, so end the current edit. Make sure there is something that we can end edit + ChangeRecordState(dbe.OldIndex, true, this.Position > -1 && this.Position < list.Count, true, false); + } + OnItemChanged(resetEvent); + break; + case System.ComponentModel.ListChangedType.PropertyDescriptorAdded: + case System.ComponentModel.ListChangedType.PropertyDescriptorDeleted: + case System.ComponentModel.ListChangedType.PropertyDescriptorChanged: + // reset lastGoodKnownRow because it was computed against property descriptors which changed + this.lastGoodKnownRow = -1; + + // In Everett, metadata changes did not alter current list position. In Whidbey, this behavior + // preserved - except that we will now force the position to stay in valid range if necessary. + if (listposition == -1 && list.Count > 0) + ChangeRecordState(0, true, false, true, false); + else if (listposition > list.Count - 1) + ChangeRecordState(list.Count - 1, true, false, true, false); + + // fire the MetaDataChanged event + OnMetaDataChanged(EventArgs.Empty); + break; + } + // send the ListChanged notification after the position changed in the list + // + + + OnListChanged(dbe); + } finally { + suspendPushDataInCurrentChanged = false; + } + Debug.Assert(lastGoodKnownRow == -1 || listposition == lastGoodKnownRow, "how did they get out of sync?"); + } + + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] //Exists in Everett + [SRCategory(SR.CatData)] + public event EventHandler MetaDataChanged { + add { + onMetaDataChangedHandler += value; + } + remove { + onMetaDataChangedHandler -= value; + } + } + + /// + /// + /// Causes the CurrentChanged event to occur. + /// + internal protected override void OnCurrentChanged(EventArgs e) { + if (!inChangeRecordState) { + Debug.WriteLineIf(CompModSwitches.DataView.TraceVerbose, "OnCurrentChanged() " + e.ToString()); + int curLastGoodKnownRow = lastGoodKnownRow; + bool positionChanged = false; + if (!suspendPushDataInCurrentChanged) + positionChanged = CurrencyManager_PushData(); + if (Count > 0) { + Object item = list[Position]; + if (item is IEditableObject) { + ((IEditableObject)item).BeginEdit(); + } + } + try { + // if currencyManager changed position then we have two cases: + // 1. the previous lastGoodKnownRow was valid: in that case we fell back so do not fire onCurrentChanged + // 2. the previous lastGoodKnownRow was invalid: we have two cases: + // a. FindGoodRow actually found a good row, so it can't be the one before the user changed the position: fire the onCurrentChanged + // b. FindGoodRow did not find a good row: we should have gotten an exception so we should not even execute this code + if (!positionChanged ||(positionChanged && curLastGoodKnownRow != -1)) { + if (onCurrentChangedHandler != null) { + onCurrentChangedHandler(this, e); + } + + // we fire OnCurrentItemChanged event every time we fire the CurrentChanged + when a property of the Current item changed + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler(this, e); + } + } + } + catch (Exception ex) { + OnDataError(ex); + } + } + } + + // this method should only be called when the currency manager receives the ListChangedType.ItemChanged event + // and when the index of the ListChangedEventArgs == the position in the currency manager + /// + internal protected override void OnCurrentItemChanged(EventArgs e) { + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler(this, e); + } + } + + /// + /// + /// + protected virtual void OnItemChanged(ItemChangedEventArgs e) { + // It is possible that CurrencyManager_PushData will change the position + // in the list. in that case we have to fire OnPositionChanged event + bool positionChanged = false; + + // We should not push the data when we suspend the changeEvents. + // but we should still fire the OnItemChanged event that we get when processing the EndCurrentEdit method. + if ((e.Index == listposition || (e.Index == -1 && Position < Count)) && !inChangeRecordState) + positionChanged = CurrencyManager_PushData(); + Debug.WriteLineIf(CompModSwitches.DataView.TraceVerbose, "OnItemChanged(" + e.Index.ToString(CultureInfo.InvariantCulture) + ") " + e.ToString()); + try { + if (onItemChanged != null) + onItemChanged(this, e); + } + catch (Exception ex) { + OnDataError(ex); + } + + if (positionChanged) + OnPositionChanged(EventArgs.Empty); + // onItemChangedCalled = true; + } + + private void OnListChanged(ListChangedEventArgs e) { + if (onListChanged != null) + onListChanged(this, e); + } + + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] //Exists in Everett + internal protected void OnMetaDataChanged(EventArgs e) + { + if (onMetaDataChangedHandler != null) + onMetaDataChangedHandler(this,e); + } + + /// + /// + /// + protected virtual void OnPositionChanged(EventArgs e) { + // if (!inChangeRecordState) { + Debug.WriteLineIf(CompModSwitches.DataView.TraceVerbose, "OnPositionChanged(" + listposition.ToString(CultureInfo.InvariantCulture) + ") " + e.ToString()); + try { + if (onPositionChangedHandler != null) + onPositionChangedHandler(this, e); + } + catch (Exception ex) { + OnDataError(ex); + } + // } + } + + /// + /// + /// + /// Forces a repopulation of the CurrencyManager + /// + /// + public void Refresh() { + if (list.Count > 0 ) { + if (listposition >= list.Count) { + lastGoodKnownRow = -1; + listposition = 0; + } + } else { + listposition = -1; + } + List_ListChanged(list, new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.Reset, -1)); + } + + internal void Release() { + UnwireEvents(list); + } + + /// + /// + /// + /// Resumes binding of component properties to list items. + /// + public override void ResumeBinding() { + lastGoodKnownRow = -1; + try { + if (!shouldBind) { + shouldBind = true; + // we need to put the listPosition at the beginning of the list if the list is not empty + this.listposition = (this.list != null && this.list.Count != 0) ? 0:-1; + UpdateIsBinding(); + } + } + catch { + shouldBind = false; + UpdateIsBinding(); + throw; + } + } + + /// + /// + /// + /// Suspends binding. + /// + public override void SuspendBinding() { + lastGoodKnownRow = -1; + if (shouldBind) { + shouldBind = false; + UpdateIsBinding(); + } + } + + internal void UnwireEvents(IList list) { + if ((list is IBindingList) && ((IBindingList)list).SupportsChangeNotification) { + ((IBindingList)list).ListChanged -= new System.ComponentModel.ListChangedEventHandler(List_ListChanged); + /* + ILiveList liveList = (ILiveList) list; + liveList.TableChanged -= new TableChangedEventHandler(List_TableChanged); + */ + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void UpdateIsBinding() { + UpdateIsBinding(true); + } + + private void UpdateIsBinding(bool raiseItemChangedEvent) { + bool newBound = list != null && list.Count > 0 && shouldBind && listposition != -1; + if (list != null) + if (bound != newBound) { + // we will call end edit when moving from bound state to unbounded state + // + //bool endCurrentEdit = bound && !newBound; + bound = newBound; + int newPos = newBound ? 0 : -1; + ChangeRecordState(newPos, bound, (Position != newPos), true, false); + int numLinks = Bindings.Count; + for (int i = 0; i < numLinks; i++) { + Bindings[i].UpdateIsBinding(); + } + + if (raiseItemChangedEvent) { + OnItemChanged(resetEvent); + } + } + } + + private void UpdateLastGoodKnownRow(System.ComponentModel.ListChangedEventArgs e) { + switch (e.ListChangedType) { + case System.ComponentModel.ListChangedType.ItemDeleted: + if (e.NewIndex == lastGoodKnownRow) + lastGoodKnownRow = -1; + break; + case System.ComponentModel.ListChangedType.Reset: + lastGoodKnownRow = -1; + break; + case System.ComponentModel.ListChangedType.ItemAdded: + if (e.NewIndex <= lastGoodKnownRow && lastGoodKnownRow < this.List.Count - 1) + lastGoodKnownRow ++; + break; + case System.ComponentModel.ListChangedType.ItemMoved: + if (e.OldIndex == lastGoodKnownRow) + lastGoodKnownRow = e.NewIndex; + break; + case System.ComponentModel.ListChangedType.ItemChanged: + if (e.NewIndex == lastGoodKnownRow) + lastGoodKnownRow = -1; + break; + } + } + + internal void WireEvents(IList list) { + if ((list is IBindingList) && ((IBindingList)list).SupportsChangeNotification) { + ((IBindingList)list).ListChanged += new System.ComponentModel.ListChangedEventHandler(List_ListChanged); + /* + ILiveList liveList = (ILiveList) list; + liveList.TableChanged += new TableChangedEventHandler(List_TableChanged); + */ + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Cursor.cs b/WindowsForms/Managed/System/WinForms/Cursor.cs new file mode 100644 index 000000000..890c41eb8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Cursor.cs @@ -0,0 +1,750 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Drawing; + using System.Drawing.Design; + using CodeAccessPermission = System.Security.CodeAccessPermission; + using System.Security.Permissions; + using System.ComponentModel; + using System.IO; + using Microsoft.Win32; + using System.Runtime.Serialization; + using System.Globalization; + using System.Security; + + + /// + /// + /// + /// Represents the image used to paint the mouse pointer. + /// Different cursor shapes are used to inform the user what operation the mouse will + /// have. + /// + /// + // + [ + TypeConverterAttribute(typeof(CursorConverter)), + Serializable, + Editor("System.Drawing.Design.CursorEditor, " + AssemblyRef.SystemDrawingDesign, typeof(UITypeEditor)) + ] + public sealed class Cursor : IDisposable, ISerializable { + private static Size cursorSize = System.Drawing.Size.Empty; + + private byte[] cursorData; + private IntPtr handle = IntPtr.Zero; // handle to loaded image + private bool ownHandle = true; + private int resourceId = 0; + + private object userData; + + /** + * Constructor used in deserialization + */ + internal Cursor(SerializationInfo info, StreamingContext context) { + SerializationInfoEnumerator sie = info.GetEnumerator(); + if (sie == null) { + return; + } + for (; sie.MoveNext();) { + // Dont catch any exceptions while Deserialising objects from stream. + if (String.Equals(sie.Name, "CursorData", StringComparison.OrdinalIgnoreCase) ){ + cursorData = (byte[])sie.Value; + if (cursorData != null) { + LoadPicture(new UnsafeNativeMethods.ComStreamFromDataStream(new MemoryStream(cursorData))); + } + } + else if (String.Compare(sie.Name, "CursorResourceId", true, CultureInfo.InvariantCulture) == 0) { + LoadFromResourceId((int)sie.Value); + } + } + } + + /// + /// Private constructor. If you want a standard system cursor, use one of the + /// definitions in the Cursors class. + /// + // + internal Cursor(int nResourceId, int dummy) { + LoadFromResourceId(nResourceId); + } + + // Private constructor. We have a private constructor here for + // static cursors that are loaded through resources. The only reason + // to use the private constructor is so that we can assert, rather + // than throw, if the cursor couldn't be loaded. Why? Because + // throwing in is really rude and will prevent any of windows forms + // from initializing. This seems extreme just because we fail to + // load a cursor. + internal Cursor(string resource, int dummy) { + Stream stream = typeof(Cursor).Module.Assembly.GetManifestResourceStream(typeof(Cursor), resource); + Debug.Assert(stream != null, "couldn't get stream for resource " + resource); + cursorData = new byte[stream.Length]; + stream.Read(cursorData, 0, Convert.ToInt32(stream.Length)); // we assume that a cursor is less than 4gig big + LoadPicture(new UnsafeNativeMethods.ComStreamFromDataStream(new MemoryStream(cursorData))); + } + + /// + /// + /// + /// Initializes a new instance of the class with the specified handle. + /// + /// + public Cursor(IntPtr handle) { + IntSecurity.UnmanagedCode.Demand(); + if (handle == IntPtr.Zero) { + throw new ArgumentException(SR.GetString(SR.InvalidGDIHandle, (typeof(Cursor)).Name)); + } + + this.handle = handle; + this.ownHandle = false; + } + + /// + /// + /// + /// Initializes a new instance of the + /// class with + /// the specified filename. + /// + /// + public Cursor(string fileName) { + //Filestream demands the correct FILEIO access here + // + FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + try { + cursorData = new byte[f.Length]; + f.Read(cursorData, 0, Convert.ToInt32(f.Length)); // assume that a cursor is less than 4gig... + } + finally { + f.Close(); + } + LoadPicture(new UnsafeNativeMethods.ComStreamFromDataStream(new MemoryStream(cursorData))); + } + + /// + /// + /// + /// Initializes a new instance of the class from the specified resource. + /// + /// + public Cursor(Type type, string resource) : this(type.Module.Assembly.GetManifestResourceStream(type,resource)) { + } + + /// + /// + /// + /// Initializes a new instance of the class from the + /// specified data stream. + /// + /// + public Cursor(Stream stream) { + cursorData = new byte[stream.Length]; + stream.Read(cursorData, 0, Convert.ToInt32(stream.Length));// assume that a cursor is less than 4gig... + LoadPicture(new UnsafeNativeMethods.ComStreamFromDataStream(new MemoryStream(cursorData))); + } + + + /// + /// + /// + /// Gets or + /// sets a that represents the current clipping rectangle for this in + /// screen coordinates. + /// + /// + public static Rectangle Clip { + get { + return ClipInternal; + } + set { + if (!value.IsEmpty) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AdjustCursorClip Demanded"); + IntSecurity.AdjustCursorClip.Demand(); + } + ClipInternal = value; + } + } + + /// + /// Implemented separately to be used internally from safe places. + /// + internal static Rectangle ClipInternal + { + get { + NativeMethods.RECT r = new NativeMethods.RECT(); + SafeNativeMethods.GetClipCursor(ref r); + return Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom); + } + set { + if (value.IsEmpty) { + UnsafeNativeMethods.ClipCursor(null); + } + else { + NativeMethods.RECT rcClip = NativeMethods.RECT.FromXYWH(value.X, value.Y, value.Width, value.Height); + UnsafeNativeMethods.ClipCursor(ref rcClip); + } + } + } + + /// + /// + /// + /// Gets or + /// sets a that + /// represents the current mouse cursor. The value is NULL if the current mouse cursor is not visible. + /// + /// + public static Cursor Current { + get { + return CurrentInternal; + } + + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyCursor Demanded"); + IntSecurity.ModifyCursor.Demand(); + CurrentInternal = value; + } + } + + internal static Cursor CurrentInternal { + get { + IntPtr curHandle = SafeNativeMethods.GetCursor(); + + // SECREVIEW : This method is to be used internally from safe places. + // + IntSecurity.UnmanagedCode.Assert(); + return Cursors.KnownCursorFromHCursor( curHandle ); + // SECREVIEW RevertAssert automatically called on return from function. + } + set { + IntPtr handle = (value == null) ? IntPtr.Zero : value.handle; + UnsafeNativeMethods.SetCursor(new HandleRef(value, handle)); + } + } + + /// + /// + /// + /// Gets + /// the Win32 handle for this . + /// + /// + public IntPtr Handle { + get { + if (handle == IntPtr.Zero) { + throw new ObjectDisposedException(SR.GetString(SR.ObjectDisposed, GetType().Name)); + } + return handle; + } + } + + /// + /// + /// + /// returns the "hot" location of the cursor. + /// + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] //Minor, not worth breaking change + public Point HotSpot + { + get { + Point hotSpot = Point.Empty; + NativeMethods.ICONINFO info = new NativeMethods.ICONINFO(); + Icon currentIcon = null; + + // SECREVIEW : Safe to assert here, the handle used was created by us and the Icon created is not exposed. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try + { + currentIcon = Icon.FromHandle(this.Handle); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + try { + SafeNativeMethods.GetIconInfo(new HandleRef(this, currentIcon.Handle), info); + hotSpot = new Point(info.xHotspot, info.yHotspot); + } + finally { + // GetIconInfo creates bitmaps for the hbmMask and hbmColor members of ICONINFO. + // The calling application must manage these bitmaps and delete them when they are no longer necessary. + + if (info.hbmMask != IntPtr.Zero) { + // ExternalDelete to prevent Handle underflow + SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, info.hbmMask)); + info.hbmMask = IntPtr.Zero; + } + if (info.hbmColor != IntPtr.Zero) { + // ExternalDelete to prevent Handle underflow + SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, info.hbmColor)); + info.hbmColor = IntPtr.Zero; + } + currentIcon.Dispose(); + + } + return hotSpot; + } + } + + /// + /// + /// + /// Gets or sets a that specifies the current cursor + /// position in screen coordinates. + /// + /// + public static Point Position { + get { + NativeMethods.POINT p = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(p); + return new Point(p.x, p.y); + } + set { + IntSecurity.AdjustCursorPosition.Demand(); + UnsafeNativeMethods.SetCursorPos(value.X, value.Y); + } + } + + /// + /// + /// + /// Gets + /// the size of this object. + /// + /// + public Size Size { + get { + if (cursorSize.IsEmpty) { + cursorSize = new Size( + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXCURSOR), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYCURSOR) + ); + + } + return cursorSize; + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Duplicates this the Win32 handle of this . + /// + public IntPtr CopyHandle() { + Size sz = Size; + return SafeNativeMethods.CopyImage(new HandleRef(this, Handle), NativeMethods.IMAGE_CURSOR, sz.Width, sz.Height, 0); + } + + /// + /// Destroys the Win32 handle of this , if the + /// + /// owns the handle + /// + private void DestroyHandle() { + if (ownHandle) { + UnsafeNativeMethods.DestroyCursor(new HandleRef(this, handle)); + } + } + + /// + /// + /// Cleans up the resources allocated by this object. Once called, the cursor + /// object is no longer useful. + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) { + /*if (picture != null) { + picture = null; + + // If we have no message loop, OLE may block on this call. + // Let pent up SendMessage calls go through here. + // + NativeMethods.MSG msg = new NativeMethods.MSG(); + UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE | NativeMethods.PM_NOYIELD); + }*/ // do we still keep that? + + if (handle != IntPtr.Zero) { + DestroyHandle(); + handle = IntPtr.Zero; + } + } + + /// + /// Draws this image to a graphics object. The drawing command originates on the graphics + /// object, but a graphics object generally has no idea how to render a given image. So, + /// it passes the call to the actual image. This version crops the image to the given + /// dimensions and allows the user to specify a rectangle within the image to draw. + /// + // This method is way more powerful than what we expose, but I'll leave it in place. + private void DrawImageCore(Graphics graphics, Rectangle imageRect, Rectangle targetRect, bool stretch) { + // Support GDI+ Translate method + targetRect.X += (int) graphics.Transform.OffsetX; + targetRect.Y += (int) graphics.Transform.OffsetY; + + int rop = 0xcc0020; // RasterOp.SOURCE.GetRop(); + IntPtr dc = graphics.GetHdc(); + + try { // want finally clause to release dc + int imageX = 0; + int imageY = 0; + int imageWidth; + int imageHeight; + int targetX = 0; + int targetY = 0; + int targetWidth = 0; + int targetHeight = 0; + + Size cursorSize = Size; + + // compute the dimensions of the icon, if needed + // + if (!imageRect.IsEmpty) { + imageX = imageRect.X; + imageY = imageRect.Y; + imageWidth = imageRect.Width; + imageHeight = imageRect.Height; + } + else { + imageWidth = cursorSize.Width; + imageHeight = cursorSize.Height; + } + + if (!targetRect.IsEmpty) { + targetX = targetRect.X; + targetY = targetRect.Y; + targetWidth = targetRect.Width; + targetHeight = targetRect.Height; + } + else { + targetWidth = cursorSize.Width; + targetHeight = cursorSize.Height; + } + + int drawWidth, drawHeight; + int clipWidth, clipHeight; + + if (stretch) { + // Short circuit the simple case of blasting an icon to the + // screen + // + if (targetWidth == imageWidth && targetHeight == imageHeight + && imageX == 0 && imageY == 0 && rop == NativeMethods.SRCCOPY + && imageWidth == cursorSize.Width && imageHeight == cursorSize.Height) { + SafeNativeMethods.DrawIcon(new HandleRef(graphics, dc), targetX, targetY, new HandleRef(this, handle)); + return; + } + + drawWidth = cursorSize.Width * targetWidth / imageWidth; + drawHeight = cursorSize.Height * targetHeight / imageHeight; + clipWidth = targetWidth; + clipHeight = targetHeight; + } + else { + // Short circuit the simple case of blasting an icon to the + // screen + // + if (imageX == 0 && imageY == 0 && rop == NativeMethods.SRCCOPY + && cursorSize.Width <= targetWidth && cursorSize.Height <= targetHeight + && cursorSize.Width == imageWidth && cursorSize.Height == imageHeight) { + SafeNativeMethods.DrawIcon(new HandleRef(graphics, dc), targetX, targetY, new HandleRef(this, handle)); + return; + } + + drawWidth = cursorSize.Width; + drawHeight = cursorSize.Height; + clipWidth = targetWidth < imageWidth ? targetWidth : imageWidth; + clipHeight = targetHeight < imageHeight ? targetHeight : imageHeight; + } + + if (rop == NativeMethods.SRCCOPY) { + // The ROP is SRCCOPY, so we can be simple here and take + // advantage of clipping regions. Drawing the cursor + // is merely a matter of offsetting and clipping. + // + SafeNativeMethods.IntersectClipRect(new HandleRef(this, Handle), targetX, targetY, targetX+clipWidth, targetY+clipHeight); + SafeNativeMethods.DrawIconEx(new HandleRef(graphics, dc), targetX - imageX, targetY - imageY, + new HandleRef(this, handle), drawWidth, drawHeight, 0, NativeMethods.NullHandleRef, NativeMethods.DI_NORMAL); + // Let GDI+ restore clipping + return; + } + + Debug.Fail("Cursor.Draw does not support raster ops. How did you even pass one in?"); + } + finally { + graphics.ReleaseHdcInternal(dc); + } + } + + /// + /// + /// + /// Draws this to a . + /// + /// + public void Draw(Graphics g, Rectangle targetRect) { + DrawImageCore(g, Rectangle.Empty, targetRect, false); + } + + /// + /// + /// Draws this to a . + /// + public void DrawStretched(Graphics g, Rectangle targetRect) { + DrawImageCore(g, Rectangle.Empty, targetRect, true); + } + + /// + /// + /// Cleans up Windows resources for this object. + /// + ~Cursor() { + Dispose(false); + } + + /// + /// + /// ISerializable private implementation + /// + /// + [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + if (cursorData != null) { + si.AddValue("CursorData", cursorData, typeof(byte[])); + } + else if (resourceId != 0) { + si.AddValue("CursorResourceId", resourceId, typeof(int)); + } + else { + Debug.Fail("Why are we trying to serialize an empty cursor?"); + throw new SerializationException(SR.GetString(SR.CursorNonSerializableHandle)); + } + } + + /// + /// + /// + /// Hides the cursor. For every call to Cursor.hide() there must be a + /// balancing call to Cursor.show(). + /// + /// + public static void Hide() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AdjustCursorClip Demanded"); + IntSecurity.AdjustCursorClip.Demand(); + + UnsafeNativeMethods.ShowCursor(false); + } + + private void LoadFromResourceId(int nResourceId) { + ownHandle = false; // we don't delete stock cursors. + + // We assert here on exception -- this constructor is used during clinit, + // and it would be a shame if we failed to initialize all of windows forms just + // just because a cursor couldn't load. + // + try { + resourceId = nResourceId; + handle = SafeNativeMethods.LoadCursor(NativeMethods.NullHandleRef, nResourceId); + } + catch (Exception e) { + handle = IntPtr.Zero; + Debug.Fail(e.ToString()); + } + } + + // this code is adapted from Icon.GetIconSize please take this into account when changing this + private Size GetIconSize(IntPtr iconHandle) { + Size iconSize = Size; + + NativeMethods.ICONINFO info = new NativeMethods.ICONINFO(); + SafeNativeMethods.GetIconInfo(new HandleRef(this, iconHandle), info); + NativeMethods.BITMAP bmp = new NativeMethods.BITMAP(); + + if (info.hbmColor != IntPtr.Zero) { + UnsafeNativeMethods.GetObject(new HandleRef(null, info.hbmColor), Marshal.SizeOf(typeof(NativeMethods.BITMAP)), bmp); + SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmColor)); + iconSize = new Size(bmp.bmWidth, bmp.bmHeight); + } + else if (info.hbmMask != IntPtr.Zero) { + UnsafeNativeMethods.GetObject(new HandleRef(null, info.hbmMask), Marshal.SizeOf(typeof(NativeMethods.BITMAP)), bmp); + iconSize = new Size(bmp.bmWidth, bmp.bmHeight / 2); + } + + if (info.hbmMask != IntPtr.Zero) { + SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmMask)); + } + return iconSize; + } + + + + /// + /// Loads a picture from the requested stream. + /// + private void LoadPicture(UnsafeNativeMethods.IStream stream) { + + if (stream == null) { + throw new ArgumentNullException("stream"); + } + try { + Guid g = typeof(UnsafeNativeMethods.IPicture).GUID; + UnsafeNativeMethods.IPicture picture = null; + + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + + try { + picture = UnsafeNativeMethods.OleCreateIPictureIndirect(null, ref g, true); + UnsafeNativeMethods.IPersistStream ipictureAsIPersist = (UnsafeNativeMethods.IPersistStream)picture; + ipictureAsIPersist.Load(stream); + + if (picture != null && picture.GetPictureType() == NativeMethods.Ole.PICTYPE_ICON) { + IntPtr cursorHandle = picture.GetHandle(); + Size picSize = GetIconSize(cursorHandle); + if (DpiHelper.IsScalingRequired) { + picSize = DpiHelper.LogicalToDeviceUnits(picSize); + } + + handle = SafeNativeMethods.CopyImageAsCursor(new HandleRef(this, cursorHandle), NativeMethods.IMAGE_CURSOR, + picSize.Width, picSize.Height, 0); + ownHandle = true; + } + else { + throw new ArgumentException(SR.GetString(SR.InvalidPictureType, + "picture", + "Cursor"), "picture"); + } + } + finally { + CodeAccessPermission.RevertAssert(); + // destroy the picture... + if(picture != null) { + Marshal.ReleaseComObject(picture); + } + } + } + catch (COMException e) { + Debug.Fail(e.ToString()); + throw new ArgumentException(SR.GetString(SR.InvalidPictureFormat), "stream", e); + } + } + + /// + /// Saves a picture from the requested stream. + /// + internal void SavePicture(Stream stream) { + if (stream == null) { + throw new ArgumentNullException("stream"); + } + if(this.resourceId != 0) { + throw new FormatException(SR.GetString(SR.CursorCannotCovertToBytes)); + } + try { + stream.Write(cursorData, 0, cursorData.Length); + } + catch (SecurityException) { + // VSWhidbey 424904 - dont eat security exceptions. + throw; + } + catch (Exception e) { + Debug.Fail(e.ToString()); + throw new InvalidOperationException(SR.GetString(SR.InvalidPictureFormat)); + } + } + + /// + /// + /// + /// Displays the cursor. For every call to Cursor.show() there must have been + /// a previous call to Cursor.hide(). + /// + /// + public static void Show() { + UnsafeNativeMethods.ShowCursor(true); + } + + /// + /// + /// + /// Retrieves a human readable string representing this + /// + /// . + /// + /// + public override string ToString() { + string s = null; + + if (!this.ownHandle) + s = TypeDescriptor.GetConverter(typeof(Cursor)).ConvertToString(this); + else + s = base.ToString(); + + return "[Cursor: " + s + "]"; + } + + /// + public static bool operator ==(Cursor left, Cursor right) { + if (object.ReferenceEquals(left, null) != object.ReferenceEquals(right, null)) { + return false; + } + + if (!object.ReferenceEquals(left, null)) { + return (left.handle == right.handle); + } + else { + return true; + } + } + + /// + public static bool operator !=(Cursor left, Cursor right) { + return !(left == right); + } + + /// + public override int GetHashCode() { + // Handle is a 64-bit value in 64-bit machines, uncheck here to avoid overflow exceptions. + return unchecked((int)handle); + } + + /// + public override bool Equals(object obj) { + if (!(obj is Cursor)) { + return false; + } + return (this == (Cursor)obj); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/CursorConverter.cs b/WindowsForms/Managed/System/WinForms/CursorConverter.cs new file mode 100644 index 000000000..8db5b00eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/CursorConverter.cs @@ -0,0 +1,191 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Reflection; + using System.IO; + + /// + /// + /// CursorConverter is a class that can be used to convert + /// colors from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class CursorConverter : TypeConverter { + + private StandardValuesCollection values; + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string) || sourceType == typeof(byte[])) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(byte[])) { + return true; + } + + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + string text = ((string)value).Trim(); + + PropertyInfo[] props = GetProperties(); + for (int i = 0; i < props.Length; i++) { + PropertyInfo prop = props[i]; + if (string.Equals(prop.Name, text, StringComparison.OrdinalIgnoreCase) ){ + object[] tempIndex = null; + return prop.GetValue(null, tempIndex); + } + } + } + + if (value is byte[]) { + MemoryStream ms = new MemoryStream((byte[])value); + return new Cursor(ms); + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string) && value != null) { + PropertyInfo[] props = GetProperties(); + int bestMatch = -1; + + for (int i = 0; i < props.Length; i++) { + PropertyInfo prop = props[i]; + object[] tempIndex = null; + Cursor c = (Cursor)prop.GetValue(null, tempIndex); + if (c == (Cursor)value) { + if (Object.ReferenceEquals(c, value)) { + return prop.Name; + } + else { + bestMatch = i; + } + } + } + + if (bestMatch != -1) { + return props[bestMatch].Name; + } + + // We throw here because we cannot meaningfully convert a custom + // cursor into a string. In fact, the ResXResourceWriter will use + // this exception to indicate to itself that this object should + // be serialized through ISeriazable instead of a string. + // + throw new FormatException(SR.GetString(SR.CursorCannotCovertToString)); + } + + if (destinationType == typeof(InstanceDescriptor) && value is Cursor) { + PropertyInfo[] props = GetProperties(); + foreach(PropertyInfo prop in props) { + if (prop.GetValue(null, null) == value) { + return new InstanceDescriptor(prop, null); + } + } + } + + if (destinationType == typeof(byte[])) { + if (value != null) { + MemoryStream ms = new MemoryStream(); + Cursor cursor = (Cursor)value; + cursor.SavePicture(ms); + ms.Close(); + return ms.ToArray(); + } + else + return new byte[0]; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Retrieves the properties for the available cursors. + /// + private PropertyInfo[] GetProperties() { + return typeof(Cursors).GetProperties(BindingFlags.Static | BindingFlags.Public); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (values == null) { + ArrayList list = new ArrayList(); + PropertyInfo[] props = GetProperties(); + for (int i = 0; i < props.Length; i++) { + PropertyInfo prop = props[i]; + object[] tempIndex = null; + Debug.Assert(prop.GetValue(null, tempIndex) != null, "Property " + prop.Name + " returned NULL"); + list.Add(prop.GetValue(null, tempIndex)); + } + + values = new StandardValuesCollection(list.ToArray()); + } + + return values; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Cursors.cs b/WindowsForms/Managed/System/WinForms/Cursors.cs new file mode 100644 index 000000000..c89df1dbe --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Cursors.cs @@ -0,0 +1,431 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// Standard cursors + /// + public sealed class Cursors { + private static Cursor appStarting = null; + private static Cursor arrow = null; + private static Cursor cross = null; + private static Cursor defaultCursor = null; + private static Cursor iBeam = null; + private static Cursor no = null; + private static Cursor sizeAll = null; + private static Cursor sizeNESW = null; + private static Cursor sizeNS = null; + private static Cursor sizeNWSE = null; + private static Cursor sizeWE = null; + private static Cursor upArrow = null; + private static Cursor wait = null; + private static Cursor help = null; + private static Cursor hSplit = null; + private static Cursor vSplit = null; + private static Cursor noMove2D = null; + private static Cursor noMoveHoriz = null; + private static Cursor noMoveVert = null; + private static Cursor panEast = null; + private static Cursor panNE = null; + private static Cursor panNorth = null; + private static Cursor panNW = null; + private static Cursor panSE = null; + private static Cursor panSouth = null; + private static Cursor panSW = null; + private static Cursor panWest = null; + private static Cursor hand = null; + + private Cursors() { + } + + internal static Cursor KnownCursorFromHCursor( IntPtr handle ) { + if (handle == IntPtr.Zero) { + return null; + } + else { + return new Cursor(handle); + } + + // if (handle == Cursors.AppStarting.Handle) return Cursors.AppStarting; + // if (handle == Cursors.Arrow.Handle) return Cursors.Arrow; + // if (handle == Cursors.IBeam.Handle) return Cursors.IBeam; + // if (handle == Cursors.Cross.Handle) return Cursors.Cross; + // if (handle == Cursors.Default.Handle) return Cursors.Default; + // if (handle == Cursors.No.Handle) return Cursors.No; + // if (handle == Cursors.SizeAll.Handle) return Cursors.SizeAll; + // if (handle == Cursors.SizeNS.Handle) return Cursors.SizeNS; + // if (handle == Cursors.SizeWE.Handle) return Cursors.SizeWE; + // if (handle == Cursors.SizeNWSE.Handle) return Cursors.SizeNWSE; + // if (handle == Cursors.SizeNESW.Handle) return Cursors.SizeNESW; + // if (handle == Cursors.VSplit.Handle) return Cursors.VSplit; + // if (handle == Cursors.HSplit.Handle) return Cursors.HSplit; + // if (handle == Cursors.WaitCursor.Handle) return Cursors.WaitCursor; + // if (handle == Cursors.Help.Handle) return Cursors.Help; + // if (handle == IntPtr.Zero) return null; + + // appStarting = new Cursor(NativeMethods.IDC_APPSTARTING,0); + // arrow = new Cursor(NativeMethods.IDC_ARROW,0); + // cross = new Cursor(NativeMethods.IDC_CROSS,0); + // defaultCursor = new Cursor(NativeMethods.IDC_ARROW,0); + // iBeam = new Cursor(NativeMethods.IDC_IBEAM,0); + // no = new Cursor(NativeMethods.IDC_NO,0); + // sizeAll = new Cursor(NativeMethods.IDC_SIZEALL,0); + // sizeNESW = new Cursor(NativeMethods.IDC_SIZENESW,0); + // sizeNS = new Cursor(NativeMethods.IDC_SIZENS,0); + // sizeNWSE = new Cursor(NativeMethods.IDC_SIZENWSE,0); + // sizeWE = new Cursor(NativeMethods.IDC_SIZEWE,0); + // upArrow = new Cursor(NativeMethods.IDC_UPARROW,0); + // wait = new Cursor(NativeMethods.IDC_WAIT,0); + // help = new Cursor(NativeMethods.IDC_HELP,0); + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor AppStarting { + get { + if (appStarting == null) + appStarting = new Cursor(NativeMethods.IDC_APPSTARTING,0); + return appStarting; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor Arrow { + get { + if (arrow == null) + arrow = new Cursor(NativeMethods.IDC_ARROW,0); + return arrow; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor Cross { + get { + if (cross == null) + cross = new Cursor(NativeMethods.IDC_CROSS,0); + return cross; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor Default { + get { + if (defaultCursor == null) + defaultCursor = new Cursor(NativeMethods.IDC_ARROW,0); + return defaultCursor; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor IBeam { + get { + if (iBeam == null) + iBeam = new Cursor(NativeMethods.IDC_IBEAM,0); + return iBeam; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor No { + get { + if (no == null) + no = new Cursor(NativeMethods.IDC_NO,0); + return no; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor SizeAll { + get { + if (sizeAll == null) + sizeAll = new Cursor(NativeMethods.IDC_SIZEALL,0); + return sizeAll; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor SizeNESW { + get { + if (sizeNESW == null) + sizeNESW = new Cursor(NativeMethods.IDC_SIZENESW,0); + return sizeNESW; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor SizeNS { + get { + if (sizeNS == null) + sizeNS = new Cursor(NativeMethods.IDC_SIZENS,0); + return sizeNS; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor SizeNWSE { + get { + if (sizeNWSE == null) + sizeNWSE = new Cursor(NativeMethods.IDC_SIZENWSE,0); + return sizeNWSE; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor SizeWE { + get { + if (sizeWE == null) + sizeWE = new Cursor(NativeMethods.IDC_SIZEWE,0); + return sizeWE; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor UpArrow { + get { + if (upArrow == null) + upArrow = new Cursor(NativeMethods.IDC_UPARROW,0); + return upArrow; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor WaitCursor { + get { + if (wait == null) + wait = new Cursor(NativeMethods.IDC_WAIT,0); + return wait; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor Help { + get { + if (help == null) + help = new Cursor(NativeMethods.IDC_HELP,0); + return help; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor HSplit { + get { + if (hSplit == null) + hSplit = new Cursor("hsplit.cur", 0); + return hSplit; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor VSplit { + get { + if (vSplit == null) + vSplit = new Cursor("vsplit.cur", 0); + return vSplit; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor NoMove2D { + get { + if (noMove2D == null) + noMove2D = new Cursor("nomove2d.cur", 0); + return noMove2D; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor NoMoveHoriz { + get { + if (noMoveHoriz == null) + noMoveHoriz = new Cursor("nomoveh.cur", 0); + return noMoveHoriz; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor NoMoveVert { + get { + if (noMoveVert == null) + noMoveVert = new Cursor("nomovev.cur", 0); + return noMoveVert; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanEast { + get { + if (panEast == null) + panEast = new Cursor("east.cur", 0); + return panEast; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanNE { + get { + if (panNE == null) + panNE = new Cursor("ne.cur", 0); + return panNE; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanNorth { + get { + if (panNorth == null) + panNorth = new Cursor("north.cur", 0); + return panNorth; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanNW { + get { + if (panNW == null) + panNW = new Cursor("nw.cur", 0); + return panNW; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanSE { + get { + if (panSE == null) + panSE = new Cursor("se.cur", 0); + return panSE; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanSouth { + get { + if (panSouth == null) + panSouth = new Cursor("south.cur", 0); + return panSouth; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanSW { + get { + if (panSW == null) + panSW = new Cursor("sw.cur", 0); + return panSW; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor PanWest { + get { + if (panWest == null) + panWest = new Cursor("west.cur", 0); + return panWest; + } + } + + /// + /// + /// [To be supplied.] + /// + public static Cursor Hand { + get { + if (hand == null) + hand = new Cursor("hand.cur", 0); + return hand; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataFormats.cs b/WindowsForms/Managed/System/WinForms/DataFormats.cs new file mode 100644 index 000000000..feab5dd10 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataFormats.cs @@ -0,0 +1,397 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Text; + using System.Configuration.Assemblies; + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Runtime.InteropServices; + using System.Globalization; + + /// + /// + /// Translates + /// between Win Forms text-based + /// formats and 32-bit signed integer-based + /// clipboard formats. Provides methods to create new formats and add + /// them to the Windows Registry. + /// + public class DataFormats { + /// + /// + /// Specifies the standard ANSI text format. This + /// field is read-only. + /// + public static readonly string Text = "Text"; + + /// + /// + /// Specifies the standard Windows Unicode text format. This + /// + /// field is read-only. + /// + public static readonly string UnicodeText = "UnicodeText"; + + /// + /// + /// Specifies the Windows Device Independent Bitmap (DIB) + /// format. This + /// field is read-only. + /// + public static readonly string Dib = "DeviceIndependentBitmap"; + + /// + /// + /// Specifies a Windows bitmap format. This field is read-only. + /// + public static readonly string Bitmap = "Bitmap"; + + /// + /// + /// Specifies the Windows enhanced metafile format. This + /// field is read-only. + /// + public static readonly string EnhancedMetafile = "EnhancedMetafile"; + + /// + /// + /// Specifies the Windows metafile format, which Win Forms + /// does not directly use. This + /// field is read-only. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] // Would be a breaking change to rename this + public static readonly string MetafilePict = "MetaFilePict"; + + /// + /// + /// Specifies the Windows symbolic link format, which Win + /// Forms does not directly use. This + /// field is read-only. + /// + public static readonly string SymbolicLink = "SymbolicLink"; + + /// + /// + /// Specifies the Windows data interchange format, which Win + /// Forms does not directly use. This + /// field is read-only. + /// + public static readonly string Dif = "DataInterchangeFormat"; + + /// + /// + /// Specifies the Tagged Image File Format (TIFF), which Win + /// Forms does not directly use. This + /// field is read-only. + /// + public static readonly string Tiff = "TaggedImageFileFormat"; + + /// + /// + /// Specifies the standard Windows original equipment + /// manufacturer (OEM) text format. This field is read-only. + /// + public static readonly string OemText = "OEMText"; + /// + /// + /// Specifies the Windows palette format. This + /// field is read-only. + /// + public static readonly string Palette = "Palette"; + + /// + /// + /// Specifies the Windows pen data format, which consists of + /// pen strokes for handwriting software; Win Forms does not use this format. This + /// + /// field is read-only. + /// + public static readonly string PenData = "PenData"; + + /// + /// + /// Specifies the Resource Interchange File Format (RIFF) + /// audio format, which Win Forms does not directly use. This field is read-only. + /// + public static readonly string Riff = "RiffAudio"; + + /// + /// + /// Specifies the wave audio format, which Win Forms does not + /// directly use. This field is read-only. + /// + public static readonly string WaveAudio = "WaveAudio"; + + /// + /// + /// Specifies the Windows file drop format, which Win Forms + /// does not directly use. This + /// field is read-only. + /// + public static readonly string FileDrop = "FileDrop"; + + /// + /// + /// Specifies the Windows culture format, which Win Forms does + /// not directly use. This field is read-only. + /// + public static readonly string Locale = "Locale"; + + /// + /// + /// Specifies text consisting of HTML data. This + /// field is read-only. + /// + public static readonly string Html = "HTML Format"; + + /// + /// + /// Specifies text consisting of Rich Text Format (RTF) data. This + /// field is read-only. + /// + public static readonly string Rtf = "Rich Text Format"; + + /// + /// + /// Specifies a comma-separated value (CSV) format, which is a + /// common interchange format used by spreadsheets. This format is not used directly + /// by Win Forms. This + /// field is read-only. + /// + public static readonly string CommaSeparatedValue = "Csv"; + + /// + /// + /// Specifies the Win Forms string class format, which Win + /// Forms uses to store string objects. This + /// field is read-only. + /// + // I'm sure upper case "String" is a reserved word in some language that matters + public static readonly string StringFormat = typeof(string).FullName; + //C#r: public static readonly String CF_STRINGCLASS = typeof(String).Name; + + /// + /// + /// Specifies a format that encapsulates any type of Win Forms + /// object. This field is read-only. + /// + public static readonly string Serializable = Application.WindowsFormsVersion + "PersistentObject"; + + + private static Format[] formatList; + private static int formatCount = 0; + + private static object internalSyncObject = new object(); + + // not creatable... + // + private DataFormats() { + } + + /// + /// + /// Gets a with the Windows Clipboard numeric ID and name for the specified format. + /// + public static Format GetFormat(string format) { + lock(internalSyncObject) { + EnsurePredefined(); + + // It is much faster to do a case sensitive search here. So do + // the case sensitive compare first, then the expensive one. + // + for (int n = 0; n < formatCount; n++) { + if (formatList[n].Name.Equals(format)) + return formatList[n]; + } + + for (int n = 0; n < formatCount; n++) { + if (String.Equals(formatList[n].Name, format, StringComparison.OrdinalIgnoreCase)) + return formatList[n]; + } + + // need to add this format string + // + int formatId = SafeNativeMethods.RegisterClipboardFormat(format); + if (0 == formatId) { + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.GetString(SR.RegisterCFFailed)); + } + + + EnsureFormatSpace(1); + formatList[formatCount] = new Format(format, formatId); + return formatList[formatCount++]; + } + } + + /// + /// + /// Gets a with the Windows Clipboard numeric + /// ID and name for the specified ID. + /// + public static Format GetFormat(int id) { + // Win32 uses an unsigned 16 bit type as a format ID, thus stripping off the leading bits. + // Registered format IDs are in the range 0xC000 through 0xFFFF, thus it's important + // to represent format as an unsigned type. + return InternalGetFormat( null, (ushort)(id & 0xFFFF)); + } + + /// + /// + /// Allows a the new format name to be specified if the requested format is not + /// in the list + /// + /// + private static Format InternalGetFormat(string strName, ushort id) { + lock(internalSyncObject) { + EnsurePredefined(); + + for (int n = 0; n < formatCount; n++) { + if (formatList[n].Id == id) + return formatList[n]; + } + + StringBuilder s = new StringBuilder(128); + + // This can happen if windows adds a standard format that we don't know about, + // so we should play it safe. + // + if (0 == SafeNativeMethods.GetClipboardFormatName(id, s, s.Capacity)) { + s.Length = 0; + if (strName == null) { + s.Append( "Format" ).Append( id ); + } + else { + s.Append( strName ); + } + } + + EnsureFormatSpace(1); + formatList[formatCount] = new Format(s.ToString(), id); + + return formatList[formatCount++]; + } + } + + + /// + /// + /// ensures that we have enough room in our format list + /// + /// + private static void EnsureFormatSpace(int size) { + if (null == formatList || formatList.Length <= formatCount + size) { + int newSize = formatCount + 20; + + Format[] newList = new Format[newSize]; + + for (int n = 0; n < formatCount; n++) { + newList[n] = formatList[n]; + } + formatList = newList; + } + } + + /// + /// + /// ensures that the Win32 predefined formats are setup in our format list. This + /// is called anytime we need to search the list + /// + /// + private static void EnsurePredefined() { + + if (0 == formatCount) { + + /* not used + int standardText; + + // We must handle text differently for Win 95 and NT. We should put + // UnicodeText on the clipboard for NT, and Text for Win 95. + // So, we figure it out here theh first time someone asks for format info + // + if (1 == Marshal.SystemDefaultCharSize) { + standardText = NativeMethods.CF_TEXT; + } + else { + standardText = NativeMethods.CF_UNICODETEXT; + } + */ + + formatList = new Format [] { + // Text name Win32 format ID Data stored as a Win32 handle? + new Format(UnicodeText, NativeMethods.CF_UNICODETEXT), + new Format(Text, NativeMethods.CF_TEXT), + new Format(Bitmap, NativeMethods.CF_BITMAP), + new Format(MetafilePict, NativeMethods.CF_METAFILEPICT), + new Format(EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), + new Format(Dif, NativeMethods.CF_DIF), + new Format(Tiff, NativeMethods.CF_TIFF), + new Format(OemText, NativeMethods.CF_OEMTEXT), + new Format(Dib, NativeMethods.CF_DIB), + new Format(Palette, NativeMethods.CF_PALETTE), + new Format(PenData, NativeMethods.CF_PENDATA), + new Format(Riff, NativeMethods.CF_RIFF), + new Format(WaveAudio, NativeMethods.CF_WAVE), + new Format(SymbolicLink, NativeMethods.CF_SYLK), + new Format(FileDrop, NativeMethods.CF_HDROP), + new Format(Locale, NativeMethods.CF_LOCALE) + }; + + formatCount = formatList.Length; + } + } + + /// + /// + /// Represents a format type. + /// + public class Format { + readonly string name; + readonly int id; + + /// + /// + /// + /// Specifies the + /// name of this format. This field is read-only. + /// + /// + /// + public string Name { + get { + return name; + } + } + + /// + /// + /// + /// Specifies the ID + /// number for this format. This field is read-only. + /// + /// + public int Id { + get { + return id; + } + } + + /// + /// + /// Initializes a new instance of the class and specifies whether a + /// + /// handle is expected with this format. + /// + public Format(string name, int id) { + this.name = name; + this.id = id; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGrid.cs b/WindowsForms/Managed/System/WinForms/DataGrid.cs new file mode 100644 index 000000000..94aa63bc9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGrid.cs @@ -0,0 +1,9550 @@ + + //------------------------------------------------------------------------------ + // + // Copyright (c) Microsoft Corporation. All rights reserved. + // + //------------------------------------------------------------------------------ + + namespace System.Windows.Forms { + using System.Text; + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Security; + using System.Security.Permissions; + using System; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Drawing; + + using Microsoft.Win32; + using System.Windows.Forms.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + /// + /// Displays ADO.NET data in a scrollable + /// grid. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.DataGridDesigner, " + AssemblyRef.SystemDesign), + DefaultProperty("DataSource"), + DefaultEvent("Navigate"), + ComplexBindingProperties("DataSource", "DataMember"), + ] + public class DataGrid : Control, ISupportInitialize, IDataGridEditingService { + #if DEBUG + internal TraceSwitch DataGridAcc = new TraceSwitch("DataGridAcc", "Trace Windows Forms DataGrid Accessibility"); + #else + internal TraceSwitch DataGridAcc = null; + #endif + + private const int GRIDSTATE_allowSorting = 0x00000001; + private const int GRIDSTATE_columnHeadersVisible = 0x00000002; + private const int GRIDSTATE_rowHeadersVisible = 0x00000004; + private const int GRIDSTATE_trackColResize = 0x00000008; + private const int GRIDSTATE_trackRowResize = 0x00000010; + private const int GRIDSTATE_isLedgerStyle = 0x00000020; + private const int GRIDSTATE_isFlatMode = 0x00000040; + private const int GRIDSTATE_listHasErrors = 0x00000080; + private const int GRIDSTATE_dragging = 0x00000100; + private const int GRIDSTATE_inListAddNew = 0x00000200; + private const int GRIDSTATE_inDeleteRow = 0x00000400; + private const int GRIDSTATE_canFocus = 0x00000800; + private const int GRIDSTATE_readOnlyMode = 0x00001000; + private const int GRIDSTATE_allowNavigation = 0x00002000; + private const int GRIDSTATE_isNavigating = 0x00004000; + private const int GRIDSTATE_isEditing = 0x00008000; + private const int GRIDSTATE_editControlChanging = 0x00010000; + private const int GRIDSTATE_isScrolling = 0x00020000; + private const int GRIDSTATE_overCaption = 0x00040000; + private const int GRIDSTATE_childLinkFocused = 0x00080000; + private const int GRIDSTATE_inAddNewRow = 0x00100000; + private const int GRIDSTATE_inSetListManager = 0x00200000; + private const int GRIDSTATE_metaDataChanged = 0x00400000; + private const int GRIDSTATE_exceptionInPaint = 0x00800000; + private const int GRIDSTATE_layoutSuspended = 0x01000000; + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 gridState; // see GRIDSTATE_ consts above + + // for column widths + private const int NumRowsForAutoResize = 10; + + private const int errorRowBitmapWidth = 15; + + private const DataGridParentRowsLabelStyle defaultParentRowsLabelStyle = DataGridParentRowsLabelStyle.Both; + + private const BorderStyle defaultBorderStyle = BorderStyle.Fixed3D; + + private const bool defaultCaptionVisible = true; + + private const bool defaultParentRowsVisible = true; + + private DataGridTableStyle defaultTableStyle = new DataGridTableStyle(true); + + // private bool allowSorting = true; + + private SolidBrush alternatingBackBrush = DefaultAlternatingBackBrush; + + // private bool columnHeadersVisible = true; + + private SolidBrush gridLineBrush = DefaultGridLineBrush; + + private const DataGridLineStyle defaultGridLineStyle = DataGridLineStyle.Solid; + private DataGridLineStyle gridLineStyle = defaultGridLineStyle; + + private SolidBrush headerBackBrush = DefaultHeaderBackBrush; + + private Font headerFont = null; // this is ambient property to Font value + + private SolidBrush headerForeBrush = DefaultHeaderForeBrush; + private Pen headerForePen = DefaultHeaderForePen; + + private SolidBrush linkBrush = DefaultLinkBrush; + + private const int defaultPreferredColumnWidth = 75; + private int preferredColumnWidth = defaultPreferredColumnWidth; + + private static int defaultFontHeight = Control.DefaultFont.Height; + private int prefferedRowHeight = defaultFontHeight + 3; + + // private bool rowHeadersVisible = true; + private const int defaultRowHeaderWidth = 35; + private int rowHeaderWidth = defaultRowHeaderWidth; + private int minRowHeaderWidth; + + private SolidBrush selectionBackBrush = DefaultSelectionBackBrush; + private SolidBrush selectionForeBrush = DefaultSelectionForeBrush; + + // parent rows + // + private DataGridParentRows parentRows = null; + // Set_ListManager uses the originalState to determine + // if the grid should disconnect from all the MetaDataChangedEvents + // keep "originalState != null" when navigating back and forth in the grid + // and use Add/RemoveMetaDataChanged methods. + private DataGridState originalState = null; + + // ui state + // + // Don't use dataGridRows, use the accessor!!! + private DataGridRow[] dataGridRows = new DataGridRow[0]; + private int dataGridRowsLength = 0; + + // for toolTip + private int toolTipId = 0; + private DataGridToolTip toolTipProvider = null; + + private DataGridAddNewRow addNewRow = null; + private LayoutData layout = new LayoutData(); + private NativeMethods.RECT[] cachedScrollableRegion = null; + + // header namespace goo + // + + // these are actually get/set by ColumnBehavior + internal bool allowColumnResize = true; + + internal bool allowRowResize = true; + + internal DataGridParentRowsLabelStyle parentRowsLabels = defaultParentRowsLabelStyle; + + // information for col/row resizing + // private bool trackColResize = false; + private int trackColAnchor = 0; + private int trackColumn = 0; + // private bool trackRowResize = false; + private int trackRowAnchor = 0; + private int trackRow = 0; + private PropertyDescriptor trackColumnHeader = null; + private MouseEventArgs lastSplitBar = null; + + // private bool isLedgerStyle = true; + // private bool isFlatMode = false; + private Font linkFont = null; + + private SolidBrush backBrush = DefaultBackBrush; + private SolidBrush foreBrush = DefaultForeBrush; + private SolidBrush backgroundBrush = DefaultBackgroundBrush; + + // font cacheing info + private int fontHeight = -1; + private int linkFontHeight = -1; + private int captionFontHeight = -1; + private int headerFontHeight = -1; + + // the preffered height of the row. + + // if the list has items with errors + + // private bool listHasErrors = false; + + // caption + private DataGridCaption caption; + + // Border + // + private BorderStyle borderStyle; + + // data binding + // + private object dataSource = null; + private string dataMember = ""; + private CurrencyManager listManager = null; + + // currently focused control + // we want to unparent it either when rebinding the grid or when the grid is disposed + Control toBeDisposedEditingControl = null; + + // persistent data state + // + internal GridTableStylesCollection dataGridTables = null; + // SET myGridTable in SetDataGridTable ONLY + internal DataGridTableStyle myGridTable = null; + internal bool checkHierarchy = true; + internal bool inInit = false; + + // Selection + internal int currentRow = 0; + internal int currentCol = 0; + private int numSelectedRows = 0; + private int lastRowSelected = -1; + + // dragging: + // private bool dragging = false; + + // addNewRow + // private bool inAddNewRow = false; + // delete Row + // private bool inDeleteRow = false; + + // when we leave, we call CommitEdit + // if we leave, then do not focus the dataGrid. + // so we can't focus the grid at the following moments: + // 1. while processing the OnLayout event + // 2. while processing the OnLeave event + // private bool canFocus = true; + + // for CurrentCell + #if DEBUG + private bool inDataSource_PositionChanged = false; + #endif // DEBUG + + // Policy + // private bool readOnlyMode = false; + private Policy policy = new Policy(); + // private bool allowNavigation = true; + + // editing + // private bool isNavigating = false; + // private bool isEditing = false; + // private bool editControlChanging = false; + private DataGridColumnStyle editColumn = null; + private DataGridRow editRow = null; + + // scrolling + // + private ScrollBar horizScrollBar = new HScrollBar(); + private ScrollBar vertScrollBar = new VScrollBar(); + + // the sum of the widths of the columns preceding the firstVisibleColumn + // + private int horizontalOffset = 0; + + // the number of pixels of the firstVisibleColumn which are not visible + // + private int negOffset = 0; + + private int wheelDelta = 0; + // private bool isScrolling = false; + + // Visibility + // + internal int firstVisibleRow = 0; + internal int firstVisibleCol = 0; + private int numVisibleRows = 0; + // the number of columns which are visible + private int numVisibleCols = 0; + private int numTotallyVisibleRows = 0; + // lastTotallyVisibleCol == -1 means that the data grid does not show any column in its entirety + private int lastTotallyVisibleCol = 0; + + // mouse move hot-tracking + // + private int oldRow = -1; + // private bool overCaption = true; + + // child relationships focused + // + // private bool childLinkFocused = false; + + // private static readonly object EVENT_COLUMNHEADERCLICK = new object(); + private static readonly object EVENT_CURRENTCELLCHANGED = new object(); + // private static readonly object EVENT_COLUMNRESIZE = new object(); + // private static readonly object EVENT_LINKCLICKED = new object(); + private static readonly object EVENT_NODECLICKED = new object(); + // private static readonly object EVENT_ROWRESIZE = new object(); + private static readonly object EVENT_SCROLL = new object(); + private static readonly object EVENT_BACKBUTTONCLICK = new object(); + private static readonly object EVENT_DOWNBUTTONCLICK = new object(); + + + // event handlers + // + private ItemChangedEventHandler itemChangedHandler; + private EventHandler positionChangedHandler; + private EventHandler currentChangedHandler; + private EventHandler metaDataChangedHandler; + + // we have to know when the collection of dataGridTableStyles changes + private CollectionChangeEventHandler dataGridTableStylesCollectionChanged; + + private EventHandler backButtonHandler; + private EventHandler downButtonHandler; + + private NavigateEventHandler onNavigate; + + private EventHandler onRowHeaderClick; + + // forDebug + // + // private int forDebug = 0; + + // =----------------------------------------------------------------- + + + /// + /// + /// Initializes a new instance of the + /// class. + /// + public DataGrid() : base() { + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.Opaque, false); + SetStyle(ControlStyles.SupportsTransparentBackColor, false); + SetStyle(ControlStyles.UserMouse, true); + this.gridState = new System.Collections.Specialized.BitVector32(0x00042827); + + dataGridTables = new GridTableStylesCollection(this); + layout = CreateInitialLayoutState(); + parentRows = new DataGridParentRows(this); + + horizScrollBar.Top = ClientRectangle.Height - horizScrollBar.Height; + horizScrollBar.Left = 0; + horizScrollBar.Visible = false; + horizScrollBar.Scroll += new ScrollEventHandler(GridHScrolled); + Controls.Add(horizScrollBar); + + vertScrollBar.Top = 0; + vertScrollBar.Left = ClientRectangle.Width - vertScrollBar.Width; + vertScrollBar.Visible = false; + vertScrollBar.Scroll += new ScrollEventHandler(GridVScrolled); + Controls.Add(vertScrollBar); + + BackColor = DefaultBackBrush.Color; + ForeColor = DefaultForeBrush.Color; + borderStyle = defaultBorderStyle; + + // create the event handlers + // + currentChangedHandler = new EventHandler(DataSource_RowChanged); + positionChangedHandler = new EventHandler(DataSource_PositionChanged); + itemChangedHandler = new ItemChangedEventHandler(DataSource_ItemChanged); + metaDataChangedHandler = new EventHandler(DataSource_MetaDataChanged); + dataGridTableStylesCollectionChanged = new CollectionChangeEventHandler(TableStylesCollectionChanged); + this.dataGridTables.CollectionChanged += dataGridTableStylesCollectionChanged; + + SetDataGridTable(this.defaultTableStyle, true); + + backButtonHandler = new EventHandler(OnBackButtonClicked); + downButtonHandler = new EventHandler(OnShowParentDetailsButtonClicked); + + caption = new DataGridCaption(this); + caption.BackwardClicked += backButtonHandler; + caption.DownClicked += downButtonHandler; + + RecalculateFonts(); + Size = new Size(130, 80); + Invalidate(); + PerformLayout(); + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + /// + /// + /// Gets or sets a value indicating whether the grid can be resorted by clicking on + /// a column header. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.DataGridAllowSortingDescr) + ] + public bool AllowSorting { + get { + return gridState[GRIDSTATE_allowSorting]; + } + set { + if (AllowSorting != value) { + gridState[GRIDSTATE_allowSorting] = value; + if (!value && this.listManager != null) { + IList list = this.listManager.List; + if (list is IBindingList) + ((IBindingList) list).RemoveSort(); + } + } + } + } + + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridAlternatingBackColorDescr) + ] + public Color AlternatingBackColor { + get { + return alternatingBackBrush.Color; + } + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, + "AlternatingBackColor")); + } + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentAlternatingBackColorNotAllowed)); + if (!alternatingBackBrush.Color.Equals(value)) { + alternatingBackBrush = new SolidBrush(value); + InvalidateInside(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void ResetAlternatingBackColor() { + if (ShouldSerializeAlternatingBackColor()) { + AlternatingBackColor = DefaultAlternatingBackBrush.Color; + InvalidateInside(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeAlternatingBackColor() + { + return !AlternatingBackBrush.Equals(DefaultAlternatingBackBrush); + } + + internal Brush AlternatingBackBrush { + get { + return alternatingBackBrush; + } + } + + // overrode those properties just to move the BackColor and the ForeColor + // from the Appearance group onto the Color Group + /// + /// + /// Gets or sets the background color of the grid. + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.ControlBackColorDescr) + ] + public override Color BackColor { + // overrode those properties just to move the BackColor and the ForeColor + // from the Appearance group onto the Color Group + get { + return base.BackColor; + } + set { + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentBackColorNotAllowed)); + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override void ResetBackColor() { + if (!this.BackColor.Equals(DefaultBackBrush.Color)) { + this.BackColor = DefaultBackBrush.Color; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.ControlForeColorDescr) + ] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + /// + /// + /// [To be supplied.] + /// + public override void ResetForeColor() { + if (!this.ForeColor.Equals(DefaultForeBrush.Color)) { + this.ForeColor = DefaultForeBrush.Color; + } + } + + /// + /// + /// Gets a value + /// indicating whether the property should be + /// persisted. + /// + /// + internal SolidBrush BackBrush { + get { + return backBrush; + } + } + + internal SolidBrush ForeBrush { + get { + return foreBrush; + } + } + + /// + /// + /// + /// Gets or + /// sets the border style. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(defaultBorderStyle), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.DataGridBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + set { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + if (borderStyle != value) { + borderStyle = value; + PerformLayout(); + Invalidate(); + OnBorderStyleChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_BORDERSTYLECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnBorderStyleChangedDescr)] + public event EventHandler BorderStyleChanged { + add { + Events.AddHandler(EVENT_BORDERSTYLECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_BORDERSTYLECHANGED, value); + } + } + + private int BorderWidth { + get { + if (BorderStyle == BorderStyle.Fixed3D) { + return SystemInformation.Border3DSize.Width; + } + else if (BorderStyle == BorderStyle.FixedSingle) { + return 2; + } + else { + return 0; + } + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(130, 80); + } + } + + private static SolidBrush DefaultSelectionBackBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaption; + } + } + private static SolidBrush DefaultSelectionForeBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaptionText; + } + } + internal static SolidBrush DefaultBackBrush { + get { + return (SolidBrush)SystemBrushes.Window; + } + } + internal static SolidBrush DefaultForeBrush { + get { + return (SolidBrush)SystemBrushes.WindowText; + } + } + private static SolidBrush DefaultBackgroundBrush { + get { + return (SolidBrush)SystemBrushes.AppWorkspace; + } + } + internal static SolidBrush DefaultParentRowsForeBrush { + get { + return (SolidBrush)SystemBrushes.WindowText; + } + } + internal static SolidBrush DefaultParentRowsBackBrush { + get { + return (SolidBrush)SystemBrushes.Control; + } + } + internal static SolidBrush DefaultAlternatingBackBrush { + get { + return (SolidBrush)SystemBrushes.Window; + } + } + private static SolidBrush DefaultGridLineBrush { + get { + return (SolidBrush)SystemBrushes.Control; + } + } + private static SolidBrush DefaultHeaderBackBrush { + get { + return (SolidBrush)SystemBrushes.Control; + } + } + private static SolidBrush DefaultHeaderForeBrush { + get { + return (SolidBrush)SystemBrushes.ControlText; + } + } + private static Pen DefaultHeaderForePen { + get { + return new Pen(SystemColors.ControlText); + } + } + private static SolidBrush DefaultLinkBrush { + get { + return (SolidBrush)SystemBrushes.HotTrack; + } + } + + + private bool ListHasErrors { + get { + return gridState[GRIDSTATE_listHasErrors]; + } + set { + if (ListHasErrors != value) + { + gridState[GRIDSTATE_listHasErrors] = value; + ComputeMinimumRowHeaderWidth(); + if (!layout.RowHeadersVisible) + return; + if (value) { + if (myGridTable.IsDefault) + this.RowHeaderWidth += errorRowBitmapWidth; + else + this.myGridTable.RowHeaderWidth += errorRowBitmapWidth; + } + else { + if (myGridTable.IsDefault) + this.RowHeaderWidth -= errorRowBitmapWidth; + else + this.myGridTable.RowHeaderWidth -= errorRowBitmapWidth; + } + } + } + } + + private bool Bound { + get { + return !(listManager == null || myGridTable == null); + } + } + + internal DataGridCaption Caption { + get { + return caption; + } + } + + + /// + /// + /// + /// Gets or sets the background color of the caption area. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridCaptionBackColorDescr) + ] + public Color CaptionBackColor { + get { + return Caption.BackColor; + } + set { + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentCaptionBackColorNotAllowed)); + Caption.BackColor = value; + } + } + + private void ResetCaptionBackColor() { + Caption.ResetBackColor(); + } + + /// + /// + /// + /// Gets a value + /// indicating whether the property should be + /// persisted. + /// + /// + protected virtual bool ShouldSerializeCaptionBackColor() + { + return Caption.ShouldSerializeBackColor(); + } + + /// + /// + /// + /// Gets + /// or sets the foreground color + /// of the caption area. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridCaptionForeColorDescr) + ] + public Color CaptionForeColor { + get { + return Caption.ForeColor; + } + set { + Caption.ForeColor = value; + } + } + + private void ResetCaptionForeColor() { + Caption.ResetForeColor(); + } + + /// + /// + /// + /// Gets a value + /// indicating whether the property should be + /// persisted. + /// + /// + protected virtual bool ShouldSerializeCaptionForeColor() + { + return Caption.ShouldSerializeForeColor(); + } + + /// + /// + /// + /// Gets or sets the font of the grid's caption. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(null), + SRDescription(SR.DataGridCaptionFontDescr) + ] + public Font CaptionFont { + get { + return Caption.Font; + } + set { + Caption.Font = value; + } + } + + /// + /// + /// Gets a value indicating whether the + /// caption's font is persisted. + /// + /// + private bool ShouldSerializeCaptionFont() { + return Caption.ShouldSerializeFont(); + } + + private void ResetCaptionFont() { + Caption.ResetFont(); + } + + /// + /// + /// + /// Gets or sets the text of the grid's caption. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(""), + Localizable(true), + SRDescription(SR.DataGridCaptionTextDescr) + ] + public string CaptionText { + get { + return Caption.Text; + } + set { + Caption.Text = value; + } + } + + /// + /// + /// + /// Gets or sets a value that indicates + /// whether the grid's caption is visible. + /// + /// + [ + DefaultValue(true), + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridCaptionVisibleDescr) + ] + public bool CaptionVisible { + get { + return layout.CaptionVisible; + } + set { + if (layout.CaptionVisible != value) { + layout.CaptionVisible = value; + PerformLayout(); + Invalidate(); + OnCaptionVisibleChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_CAPTIONVISIBLECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnCaptionVisibleChangedDescr)] + public event EventHandler CaptionVisibleChanged { + add { + Events.AddHandler(EVENT_CAPTIONVISIBLECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CAPTIONVISIBLECHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets which cell has the focus. Not available at design time. + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.DataGridCurrentCellDescr) + ] + public DataGridCell CurrentCell { + get { + return new DataGridCell(currentRow, currentCol); + } + set { + // if the OnLayout event was not set in the grid, then we can't + // reliably set the currentCell on the grid. + if (layout.dirty) + throw new ArgumentException(SR.GetString(SR.DataGridSettingCurrentCellNotGood)); + if (value.RowNumber == currentRow && value.ColumnNumber == currentCol) + return; + + // should we throw an exception, maybe? + if (DataGridRowsLength == 0 || this.myGridTable.GridColumnStyles == null || this.myGridTable.GridColumnStyles.Count == 0) + return; + EnsureBound(); + + int currentRowSaved = currentRow; + int currentColSaved = currentCol; + bool wasEditing = gridState[GRIDSTATE_isEditing]; + bool cellChanged = false; + + // if the position in the listManager changed under the DataGrid, + // then do not edit after setting the current cell + bool doNotEdit = false; + + int newCol = value.ColumnNumber; + int newRow = value.RowNumber; + + String errorMessage = null; + + try { + int columnCount = myGridTable.GridColumnStyles.Count; + if (newCol < 0) + newCol = 0; + if (newCol >= columnCount) + newCol = columnCount - 1; + + int localGridRowsLength = DataGridRowsLength; + DataGridRow[] localGridRows = DataGridRows; + + if (newRow < 0) { + newRow = 0; + } + if (newRow >= localGridRowsLength) { + newRow = localGridRowsLength - 1; + } + + // Current Column changing + // + if (currentCol != newCol) { + cellChanged = true; + int currentListManagerPosition = this.ListManager.Position; + int currentListManagerCount = this.ListManager.List.Count; + + EndEdit(); + + if (this.ListManager.Position != currentListManagerPosition || + currentListManagerCount != this.ListManager.List.Count) { + // EndEdit changed the list. + // Reset the data grid rows and the current row inside the datagrid. + // And then exit the method. + RecreateDataGridRows(); + if (this.ListManager.List.Count > 0) { + this.currentRow = this.ListManager.Position; + Edit(); + } else { + this.currentRow = -1; + } + + return; + } + + currentCol = newCol; + InvalidateRow(currentRow); + } + + // Current Row changing + // + if (currentRow != newRow) { + cellChanged = true; + int currentListManagerPosition = this.ListManager.Position; + int currentListManagerCount = this.ListManager.List.Count; + + EndEdit(); + + if (this.ListManager.Position != currentListManagerPosition || + currentListManagerCount != this.ListManager.List.Count) { + // EndEdit changed the list. + // Reset the data grid rows and the current row inside the datagrid. + // And then exit the method. + RecreateDataGridRows(); + if (this.ListManager.List.Count > 0) { + this.currentRow = this.ListManager.Position; + Edit(); + } else { + this.currentRow = -1; + } + + return; + } + + if (currentRow < localGridRowsLength) + localGridRows[currentRow].OnRowLeave(); + localGridRows[newRow].OnRowEnter(); + currentRow = newRow; + if (currentRowSaved < localGridRowsLength) + InvalidateRow(currentRowSaved); + InvalidateRow(currentRow); + + if (currentRowSaved != listManager.Position) { + // not in sync + #if DEBUG + Debug.Assert(inDataSource_PositionChanged, "currentRow and listManager.Position can be out of sync only when the listManager changes its position under the DataGrid or when navigating back"); + Debug.Assert(ListManager.Position == currentRow || listManager.Position == -1, "DataSource_PositionChanged changes the position in the grid to the position in the listManager"); + #endif //DEBUG + doNotEdit = true; + if (gridState[GRIDSTATE_isEditing]) + AbortEdit(); + } else if (gridState[GRIDSTATE_inAddNewRow]) { + #if DEBUG + int currentRowCount = this.DataGridRowsLength; + #endif // debug + // cancelCurrentEdit will change the position in the list + // to the last element in the list. and the grid will get an on position changed + // event, and will set the current cell to the last element in the dataSource. + // so unhook the PositionChanged event from the listManager; + this.ListManager.PositionChanged -= positionChangedHandler; + this.ListManager.CancelCurrentEdit(); + this.ListManager.Position = this.currentRow; + this.ListManager.PositionChanged += positionChangedHandler; + #if DEBUG + + Debug.Assert(currentRowSaved > currentRow, "we can only go up when we are inAddNewRow"); + Debug.Assert(currentRowCount == this.DataGridRowsLength, "the number of rows in the dataGrid should not change"); + Debug.Assert(currentRowCount == this.ListManager.Count + 1, "the listManager should have one less record"); + #endif // debug + localGridRows[DataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength-1); + SetDataGridRows(localGridRows, DataGridRowsLength); + gridState[GRIDSTATE_inAddNewRow] = false; + } else { + ListManager.EndCurrentEdit(); + // some special care must be given when setting the + // position in the listManager. + // if EndCurrentEdit() deleted the current row + // ( because of some filtering problem, say ) + // then we cannot go over the last row + // + if (localGridRowsLength != DataGridRowsLength) + { + Debug.Assert(localGridRowsLength == DataGridRowsLength + 1, "this is the only change that could have happened"); + currentRow = (currentRow == localGridRowsLength - 1) ? DataGridRowsLength - 1 : currentRow; + } + + if (currentRow == dataGridRowsLength - 1 && policy.AllowAdd) { + // it may be case ( see previous comment ) + // that listManager.EndCurrentEdit changed the number of rows + // in the grid. in this case, we should not be using the old + // localGridRows in our assertion, cause they are outdated now + // + Debug.Assert(this.DataGridRows[currentRow] is DataGridAddNewRow, "the last row is the DataGridAddNewRow"); + this.AddNewRow(); + Debug.Assert(ListManager.Position == currentRow || listManager.Position == -1, "the listManager should be positioned at the last row"); + } else { + + ListManager.Position = currentRow; + } + } + + } + + } + catch (Exception e) { + errorMessage = e.Message; + } + + if (errorMessage != null) { + DialogResult result = RTLAwareMessageBox.Show(null, + SR.GetString(SR.DataGridPushedIncorrectValueIntoColumn, errorMessage), + SR.GetString(SR.DataGridErrorMessageBoxCaption), MessageBoxButtons.YesNo, + MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + + if (result == DialogResult.Yes) { + currentRow = currentRowSaved; + currentCol = currentColSaved; + Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager (" + ListManager.Position + ") is out of sync with the currentRow (" + currentRow + ")" + " and the exception is '" + errorMessage + "'"); + // this will make sure the newRow will not paint the + // row selector. + InvalidateRowHeader(newRow); + // also, make sure that we get the row selector on the currentrow, too + InvalidateRowHeader(currentRow); + if (wasEditing) + Edit(); + } else { + // if the user committed a row that used to be addNewRow and the backEnd rejects it, + // and then it tries to navigate down then we should stay in the addNewRow + // in this particular scenario, CancelCurrentEdit will cause the last row to be deleted, + // and this will ultimately call InvalidateRow w/ a row number larger than the number of rows + // so set the currentRow here: + if (currentRow == this.DataGridRowsLength - 1 && currentRowSaved == this.DataGridRowsLength - 2 && DataGridRows[currentRow] is DataGridAddNewRow) + newRow = currentRowSaved; + currentRow = newRow; + Debug.Assert(result == DialogResult.No, "we only put cancel and ok on the error message box"); + this.listManager.PositionChanged -= positionChangedHandler; + this.listManager.CancelCurrentEdit(); + this.listManager.Position = newRow; + this.listManager.PositionChanged += positionChangedHandler; + currentRow = newRow; + currentCol = newCol; + if (wasEditing) + Edit(); + } + } + + if (cellChanged) { + EnsureVisible(currentRow, currentCol); + OnCurrentCellChanged(EventArgs.Empty); + + // if the user changed the current cell using the UI, edit the new cell + // but if the user changed the current cell by changing the position in the + // listManager, then do not continue the edit + // + if (!doNotEdit) + { + #if DEBUG + Debug.Assert(!inDataSource_PositionChanged, "if the user changed the current cell using the UI, then do not edit"); + #endif // debug + Edit(); + } + + + AccessibilityNotifyClients(AccessibleEvents.Focus, CurrentCellAccIndex); + AccessibilityNotifyClients(AccessibleEvents.Selection, CurrentCellAccIndex); + } + + Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager is out of sync with the currentRow"); + } + } + + internal int CurrentCellAccIndex { + get { + int currentCellAccIndex = 0; + currentCellAccIndex ++; // ParentRowsAccessibleObject + currentCellAccIndex += this.myGridTable.GridColumnStyles.Count; // ColumnHeaderAccessibleObject + currentCellAccIndex += this.DataGridRows.Length; // DataGridRowAccessibleObject + if (this.horizScrollBar.Visible) // Horizontal Scroll Bar Accessible Object + currentCellAccIndex ++; + if (this.vertScrollBar.Visible) // Vertical Scroll Bar Accessible Object + currentCellAccIndex ++; + currentCellAccIndex += (this.currentRow * this.myGridTable.GridColumnStyles.Count) + this.currentCol; + return currentCellAccIndex; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnCurrentCellChangedDescr)] + public event EventHandler CurrentCellChanged { + add { + Events.AddHandler(EVENT_CURRENTCELLCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CURRENTCELLCHANGED, value); + } + } + + private int CurrentColumn { + get { + return CurrentCell.ColumnNumber; + } + set { + this.CurrentCell = new DataGridCell(currentRow, value); + } + } + + private int CurrentRow { + get { + return CurrentCell.RowNumber; + } + set { + CurrentCell = new DataGridCell(value, currentCol); + } + } + + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridSelectionBackColorDescr) + ] + public Color SelectionBackColor { + get { + return selectionBackBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionBackColor")); + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentSelectionBackColorNotAllowed)); + if (!value.Equals(selectionBackBrush.Color)) { + selectionBackBrush = new SolidBrush(value); + + InvalidateInside(); + } + } + } + + internal SolidBrush SelectionBackBrush { + get { + return this.selectionBackBrush; + } + } + + internal SolidBrush SelectionForeBrush { + get { + return this.selectionForeBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializeSelectionBackColor() + { + return !DefaultSelectionBackBrush.Equals(selectionBackBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetSelectionBackColor() { + if (ShouldSerializeSelectionBackColor()) + SelectionBackColor = DefaultSelectionBackBrush.Color; + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridSelectionForeColorDescr) + ] + public Color SelectionForeColor { + get { + return selectionForeBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionForeColor")); + if (!value.Equals(selectionForeBrush.Color)) { + selectionForeBrush = new SolidBrush(value); + + InvalidateInside(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeSelectionForeColor() + { + return !SelectionForeBrush.Equals(DefaultSelectionForeBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetSelectionForeColor() { + if (ShouldSerializeSelectionForeColor()) + SelectionForeColor = DefaultSelectionForeBrush.Color; + } + + /// + /// [To be supplied.] + /// + internal override bool ShouldSerializeForeColor() + { + return !DefaultForeBrush.Color.Equals(this.ForeColor); + } + + /// + /// Indicates whether the property should be + /// persisted. + /// + internal override bool ShouldSerializeBackColor() + { + return !DefaultBackBrush.Color.Equals(this.BackColor); + } + + // Don't use dataGridRows, use the accessor!!! + internal DataGridRow[] DataGridRows { + get { + if (dataGridRows == null) + CreateDataGridRows(); + return dataGridRows; + } + } + + // ToolTipping + internal DataGridToolTip ToolTipProvider { + get { + return toolTipProvider; + } + } + + internal int ToolTipId { + get { + return toolTipId; + } + set { + toolTipId = value; + } + } + + private void ResetToolTip() + { + // remove all the tool tips which are stored + for (int i = 0; i < ToolTipId; i ++) { + ToolTipProvider.RemoveToolTip(new IntPtr(i)); + } + + // add toolTips for the backButton and + // details button on the caption. + if (!this.parentRows.IsEmpty()) { + bool alignRight = this.isRightToLeft(); + int detailsButtonWidth = this.Caption.GetDetailsButtonWidth(); + Rectangle backButton = this.Caption.GetBackButtonRect(this.layout.Caption, alignRight, detailsButtonWidth); + Rectangle detailsButton = this.Caption.GetDetailsButtonRect(this.layout.Caption, alignRight); + + // mirror the buttons wrt RTL property + backButton.X = MirrorRectangle(backButton, layout.Inside, isRightToLeft()); + detailsButton.X = MirrorRectangle(detailsButton, layout.Inside, isRightToLeft()); + + ToolTipProvider.AddToolTip(SR.GetString(SR.DataGridCaptionBackButtonToolTip), new IntPtr(0), backButton); + ToolTipProvider.AddToolTip(SR.GetString(SR.DataGridCaptionDetailsButtonToolTip), new IntPtr(1), detailsButton); + ToolTipId = 2; + } else { + ToolTipId = 0; + } + } + + + + /// + /// Given a cursor, this will Create the right DataGridRows + /// + private void CreateDataGridRows() { + CurrencyManager listManager = ListManager; + DataGridTableStyle dgt = this.myGridTable; + InitializeColumnWidths(); + + if (listManager == null) { + SetDataGridRows(new DataGridRow[0], 0); + return; + } + + int nDataGridRows = listManager.Count; + if (policy.AllowAdd) + nDataGridRows++; + + DataGridRow[] rows = new DataGridRow[nDataGridRows]; + for (int r = 0; r < listManager.Count; r++) { + rows[r] = new DataGridRelationshipRow(this, dgt,r); + } + + if (policy.AllowAdd) { + this.addNewRow = new DataGridAddNewRow(this, dgt, nDataGridRows - 1); + rows[nDataGridRows - 1] = addNewRow; + } + else { + addNewRow = null; + } + // SetDataGridRows(rows, rows.Length); + SetDataGridRows(rows, nDataGridRows); + } + + private void RecreateDataGridRows() { + int nDataGridRows = 0; + CurrencyManager listManager = ListManager; + + if (listManager != null) { + nDataGridRows = listManager.Count; + if (policy.AllowAdd) { + nDataGridRows++; + } + } + SetDataGridRows(null, nDataGridRows); + } + + /// + /// Sets the array of DataGridRow objects used for + /// all row-related logic in the DataGrid. + /// + internal void SetDataGridRows(DataGridRow[] newRows, int newRowsLength) { + dataGridRows = newRows; + dataGridRowsLength = newRowsLength; + + // update the vertical scroll bar + vertScrollBar.Maximum = Math.Max(0, DataGridRowsLength - 1); + if (firstVisibleRow > newRowsLength) { + vertScrollBar.Value = 0; + firstVisibleRow = 0; + } + + ResetUIState(); + #if DEBUG + // sanity check: all the rows should have the same + // dataGridTable + if (newRows != null && newRowsLength > 0) { + DataGridTableStyle dgTable = newRows[0].DataGridTableStyle; + for (int i = 0; i < newRowsLength; i ++) { + Debug.Assert(dgTable == newRows[i].DataGridTableStyle, "how can two rows have different tableStyles?"); + + } + } + #endif // DEBUG + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: There are now " + DataGridRowsLength.ToString(CultureInfo.InvariantCulture) + " rows."); + } + + internal int DataGridRowsLength { + get { + return dataGridRowsLength; + } + } + + /// + /// + /// Gets or sets the data source that the grid is displaying data for. + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.DataGridDataSourceDescr) + ] + public object DataSource { + get { + return dataSource; + } + + set { + if (value != null && !(value is IList || value is IListSource)) + throw new ArgumentException(SR.GetString(SR.BadDataSourceForComplexBinding)); + if (dataSource != null && dataSource.Equals(value)) + { + return; + } + + // when the designer resets the dataSource to null, set the dataMember to null, too + if ((value == null || value == Convert.DBNull) && this.DataMember != null && this.DataMember.Length != 0 ) { + this.dataSource = null; + this.DataMember = ""; + return; + } + + // if we are setting the dataSource and the dataMember is not a part + // of the properties in the dataSource, then set the dataMember to "" + // + if (value != null) + EnforceValidDataMember(value); + + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource being set to " + ((value == null) ? "null" : value.ToString())); + + // when we change the dataSource, we need to clear the parent rows. + // the same goes for all the caption UI: reset it when the datasource changes. + // + ResetParentRows(); + Set_ListManager(value, this.DataMember, false); + } + } + + private static readonly object EVENT_DATASOURCECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnDataSourceChangedDescr)] + public event EventHandler DataSourceChanged { + add { + Events.AddHandler(EVENT_DATASOURCECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DATASOURCECHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets the specific table in a DataSource for the control. + /// + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.DataGridDataMemberDescr) + ] + public string DataMember { + get { + return dataMember; + } + set { + if (dataMember != null && dataMember.Equals(value)) + return; + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource being set to " + ((value == null) ? "null" : value.ToString())); + // when we change the dataMember, we need to clear the parent rows. + // the same goes for all the caption UI: reset it when the datamember changes. + // + ResetParentRows(); + Set_ListManager(this.DataSource, value, false); + } + } + + /// + /// + /// [To be supplied.] + /// + public void SetDataBinding(object dataSource, string dataMember) { + parentRows.Clear(); + originalState = null; + caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false; + caption.SetDownButtonDirection(!layout.ParentRowsVisible); + + Set_ListManager(dataSource, dataMember, false); + } + + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridListManagerDescr) + ] + internal protected CurrencyManager ListManager { + get { + //try to return something useful: + if (listManager == null && this.BindingContext != null && this.DataSource != null) + return (CurrencyManager) this.BindingContext[this.DataSource, this.DataMember]; + else + return listManager; + } + set { + throw new NotSupportedException(SR.GetString(SR.DataGridSetListManager)); + } + } + + internal void Set_ListManager(object newDataSource, string newDataMember, bool force) { + Set_ListManager(newDataSource, newDataMember, force, true); // true for forcing column creation + } + + // + // prerequisite: the dataMember and the dataSource should be set to the new values + // + // will do the following: + // call EndEdit on the current listManager, will unWire the listManager events, will set the listManager to the new + // reality, will wire the new listManager, will update the policy, will set the dataGridTable, will reset the ui state. + // + internal void Set_ListManager(object newDataSource, string newDataMember, bool force, bool forceColumnCreation) + { + bool dataSourceChanged = this.DataSource != newDataSource; + bool dataMemberChanged = this.DataMember != newDataMember; + + // if nothing happened, then why do any work? + if (!force && !dataSourceChanged && !dataMemberChanged && gridState[GRIDSTATE_inSetListManager]) + return; + + gridState[GRIDSTATE_inSetListManager] = true; + if (this.toBeDisposedEditingControl != null) { + Debug.Assert(this.Controls.Contains(this.toBeDisposedEditingControl)); + this.Controls.Remove(this.toBeDisposedEditingControl); + this.toBeDisposedEditingControl = null; + } + bool beginUpdateInternal = true; + try { + // will endEdit on the current listManager + UpdateListManager(); + + // unwire the events: + if (this.listManager != null) + UnWireDataSource(); + + CurrencyManager oldListManager = listManager; + bool listManagerChanged = false; + // set up the new listManager + // CAUTION: we need to set up the listManager in the grid before setting the dataSource/dataMember props + // in the grid. the reason is that if the BindingContext was not yet requested, and it is created in the BindingContext prop + // then the grid will call Set_ListManager again, and eventually that means that the dataGrid::listManager will + // be hooked up twice to all the events (PositionChanged, ItemChanged, CurrentChanged) + if (newDataSource != null && this.BindingContext != null && !(newDataSource == Convert.DBNull)) + this.listManager = (CurrencyManager) this.BindingContext[newDataSource, newDataMember]; + else + listManager = null; + + // update the dataSource and the dateMember + this.dataSource = newDataSource; + this.dataMember = newDataMember == null ? "" : newDataMember; + + listManagerChanged = (listManager != oldListManager); + + // wire the events + if (listManager != null) { + WireDataSource(); + // update the policy + policy.UpdatePolicy(this.listManager, this.ReadOnly); + } + + if (!Initializing) + { + if (listManager == null) + { + if (this.ContainsFocus && this.ParentInternal == null) { + Debug.Assert(this.toBeDisposedEditingControl == null, "we should have removed the toBeDisposedEditingControl already"); + // if we unparent the active control then the form won't close + for (int i = 0; i < this.Controls.Count; i++) { + if (this.Controls[i].Focused) { + this.toBeDisposedEditingControl = this.Controls[i]; + break; + } + } + + if (this.toBeDisposedEditingControl == this.horizScrollBar || this.toBeDisposedEditingControl == this.vertScrollBar) { + this.toBeDisposedEditingControl = null; + } + + #if DEBUG + else { + Debug.Assert(this.toBeDisposedEditingControl != null, "if the grid contains the focus, then the active control should be in the children of data grid control"); + Debug.Assert(this.editColumn != null, "if we have an editing control should be a control in the data grid column"); + if (this.editColumn is DataGridTextBoxColumn) { + Debug.Assert(((DataGridTextBoxColumn) this.editColumn).TextBox == this.toBeDisposedEditingControl, "if we have an editing control should be a control in the data grid column"); + } + } + #endif // debug; + + } + + SetDataGridRows(null, 0); + this.defaultTableStyle.GridColumnStyles.Clear(); + SetDataGridTable(this.defaultTableStyle, forceColumnCreation); + + if (this.toBeDisposedEditingControl != null) { + this.Controls.Add(this.toBeDisposedEditingControl); + } + } + } + + // PERF: if the listManager did not change, then do not: + // 1. create new rows + // 2. create new columns + // 3. compute the errors in the list + // + // when the metaDataChanges, we need to recreate + // the rows and the columns + // + if (listManagerChanged || gridState[GRIDSTATE_metaDataChanged]) { + if (this.Visible) BeginUpdateInternal(); + + if (listManager != null) + { + // get rid of the old gridColumns + // we need to clear the old column collection even when navigating to + // a list that has a table style associated w/ it. Why? because the + // old column collection will be used by the parent rows to paint + this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection(); + + DataGridTableStyle newGridTable = this.dataGridTables[listManager.GetListName()]; + if (newGridTable == null) { + SetDataGridTable(this.defaultTableStyle, forceColumnCreation); + } else { + SetDataGridTable(newGridTable, forceColumnCreation); + } + + // set the currentRow in ssync w/ the position in the listManager + currentRow = listManager.Position == -1 ? 0 : listManager.Position; + } + + // when we create the rows we need to use the current dataGridTable + // + RecreateDataGridRows(); + if (this.Visible) EndUpdateInternal(); + beginUpdateInternal = false; + + ComputeMinimumRowHeaderWidth(); + if (this.myGridTable.IsDefault) + this.RowHeaderWidth = Math.Max(this.minRowHeaderWidth, this.RowHeaderWidth); + else + this.myGridTable.RowHeaderWidth = Math.Max(this.minRowHeaderWidth, this.RowHeaderWidth); + + ListHasErrors = DataGridSourceHasErrors(); + + // build the list of columns and relationships + // wipe out the now invalid states + //ResetMouseState(); + + ResetUIState(); + + //layout.CaptionVisible = dataCursor == null ? false : true; + + OnDataSourceChanged(EventArgs.Empty); + } + + } finally { + gridState[GRIDSTATE_inSetListManager] = false; + // start painting again + if (beginUpdateInternal && this.Visible) EndUpdateInternal(); + } + } + + /// + /// + /// Gets or sets index of the selected row. + /// + // will set the position in the ListManager + // + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + SRDescription(SR.DataGridSelectedIndexDescr) + ] + public int CurrentRowIndex { + get { + if (originalState == null) + return this.listManager == null ? - 1 : this.listManager.Position; + else { + if (this.BindingContext == null) + return -1; + CurrencyManager originalListManager = (CurrencyManager) this.BindingContext[originalState.DataSource, originalState.DataMember]; + return originalListManager.Position; + } + } + set { + if (this.listManager == null) + throw new InvalidOperationException(SR.GetString(SR.DataGridSetSelectIndex)); + + if (originalState == null) { + this.listManager.Position = value; + currentRow = value; + return; + } + + // if we have a this.ListManager, then this.BindingManager cannot be null + // + CurrencyManager originalListManager = (CurrencyManager) this.BindingContext[originalState.DataSource, originalState.DataMember]; + originalListManager.Position = value; + + // this is for parent rows + originalState.LinkingRow = originalState.DataGridRows[value]; + + // Invalidate everything + Invalidate(); + } + } + + + /// + /// + /// Gets the collection of tables for the grid. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.DataGridGridTablesDescr) + ] + public GridTableStylesCollection TableStyles { + get { + return dataGridTables; + } + } + + internal new int FontHeight { + get { + return fontHeight; + } + } + + internal AccessibleObject ParentRowsAccessibleObject { + get { + return parentRows.AccessibleObject; + } + } + + internal Rectangle ParentRowsBounds { + get { + return layout.ParentRows; + } + } + + /// + /// + /// Gets or sets the color of the grid lines. + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridGridLineColorDescr) + ] + public Color GridLineColor { + get { + return gridLineBrush.Color; + } + set { + if (gridLineBrush.Color != value) { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "GridLineColor")); + gridLineBrush = new SolidBrush(value); + + Invalidate(layout.Data); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeGridLineColor() + { + return !GridLineBrush.Equals(DefaultGridLineBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetGridLineColor() { + if (ShouldSerializeGridLineColor()) { + GridLineColor = DefaultGridLineBrush.Color; + } + } + + internal SolidBrush GridLineBrush { + get { + return gridLineBrush; + } + } + + /// + /// + /// + /// Gets or sets the line style of the grid. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(defaultGridLineStyle), + SRDescription(SR.DataGridGridLineStyleDescr) + ] + public DataGridLineStyle GridLineStyle { + get { + return gridLineStyle; + } + set { + //valid values are 0x0 to 0x1. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridLineStyle.None, (int)DataGridLineStyle.Solid)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridLineStyle)); + } + if (gridLineStyle != value) { + gridLineStyle = value; + this.myGridTable.ResetRelationsUI(); + Invalidate(layout.Data); + } + } + } + + internal int GridLineWidth { + get { + Debug.Assert(this.GridLineStyle == DataGridLineStyle.Solid || this.GridLineStyle == DataGridLineStyle.None, "are there any other styles?"); + return GridLineStyle == DataGridLineStyle.Solid ? 1 : 0; + } + } + + /// + /// + /// + /// Gets or + /// sets the + /// way parent row labels are displayed. + /// + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DefaultValue(defaultParentRowsLabelStyle), + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridParentRowsLabelStyleDescr) + ] + public DataGridParentRowsLabelStyle ParentRowsLabelStyle { + get { + return parentRowsLabels; + } + + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridParentRowsLabelStyle.None, (int)DataGridParentRowsLabelStyle.Both)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridParentRowsLabelStyle)); + } + + if (parentRowsLabels != value) + { + parentRowsLabels = value; + Invalidate(layout.ParentRows); + OnParentRowsLabelStyleChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_PARENTROWSLABELSTYLECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnParentRowsLabelStyleChangedDescr)] + public event EventHandler ParentRowsLabelStyleChanged { + add { + Events.AddHandler(EVENT_PARENTROWSLABELSTYLECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_PARENTROWSLABELSTYLECHANGED, value); + } + } + + internal bool Initializing { + get { + return inInit; + } + } + + /// + /// + /// + /// Gets the index of the first visible column in a grid. + /// + /// + [ + Browsable(false), + SRDescription(SR.DataGridFirstVisibleColumnDescr) + ] + public int FirstVisibleColumn { + get { + return firstVisibleCol; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the grid displays in flat mode. + /// + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridFlatModeDescr) + ] + public bool FlatMode { + get { + return gridState[GRIDSTATE_isFlatMode]; + } + set { + if (value != FlatMode) { + gridState[GRIDSTATE_isFlatMode] = value; + Invalidate(layout.Inside); + OnFlatModeChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_FLATMODECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnFlatModeChangedDescr)] + public event EventHandler FlatModeChanged { + add { + Events.AddHandler(EVENT_FLATMODECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_FLATMODECHANGED, value); + } + } + + /// + /// + /// + /// Gets or + /// sets the background color of all row and column headers. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridHeaderBackColorDescr) + ] + public Color HeaderBackColor { + get { + return headerBackBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderBackColor")); + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentHeaderBackColorNotAllowed)); + if (!value.Equals(headerBackBrush.Color)) { + headerBackBrush = new SolidBrush(value); + + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + } + } + } + + internal SolidBrush HeaderBackBrush { + get { + return headerBackBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeHeaderBackColor() + { + return !HeaderBackBrush.Equals(DefaultHeaderBackBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderBackColor() { + if(ShouldSerializeHeaderBackColor()) { + HeaderBackColor = DefaultHeaderBackBrush.Color; + } + } + internal SolidBrush BackgroundBrush { + get { + return backgroundBrush; + } + } + + private void ResetBackgroundColor() { + if (backgroundBrush != null && BackgroundBrush != DefaultBackgroundBrush) { + backgroundBrush.Dispose(); + backgroundBrush = null; + } + backgroundBrush = DefaultBackgroundBrush; + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeBackgroundColor() + { + return !BackgroundBrush.Equals(DefaultBackgroundBrush); + } + + + // using this property, the user can set the backGround color + /// + /// + /// Gets or sets the background color of the grid. + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridBackgroundColorDescr) + ] + public Color BackgroundColor { + get { + return backgroundBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "BackgroundColor")); + if (!value.Equals(backgroundBrush.Color)) { + + if (backgroundBrush != null && BackgroundBrush != DefaultBackgroundBrush) { + backgroundBrush.Dispose(); + backgroundBrush = null; + } + backgroundBrush = new SolidBrush(value); + + Invalidate(layout.Inside); + OnBackgroundColorChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_BACKGROUNDCOLORCHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnBackgroundColorChangedDescr)] + public event EventHandler BackgroundColorChanged { + add { + Events.AddHandler(EVENT_BACKGROUNDCOLORCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_BACKGROUNDCOLORCHANGED, value); + } + } + + /// + /// + /// + /// Indicates whether the property should be persisted. + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridHeaderFontDescr) + ] + public Font HeaderFont { + get { + return(headerFont == null ? this.Font : headerFont); + } + set { + if (value == null) + throw new ArgumentNullException("HeaderFont"); + if (!value.Equals(headerFont)) { + headerFont = value; + RecalculateFonts(); + PerformLayout(); + Invalidate(layout.Inside); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializeHeaderFont() { + return(headerFont != null); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderFont() { + if (headerFont != null) { + headerFont = null; + RecalculateFonts(); + PerformLayout(); + Invalidate(layout.Inside); + } + } + + /// + /// + /// + /// Resets the property to its default value. + /// + /// + /// + /// + /// Gets or sets the foreground color of the grid's headers. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridHeaderForeColorDescr) + ] + public Color HeaderForeColor { + get { + return headerForePen.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderForeColor")); + if (!value.Equals(headerForePen.Color)) { + headerForePen = new Pen(value); + headerForeBrush = new SolidBrush(value); + + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeHeaderForeColor() + { + return !HeaderForePen.Equals(DefaultHeaderForePen); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderForeColor() { + if(ShouldSerializeHeaderForeColor()) { + HeaderForeColor = DefaultHeaderForeBrush.Color; + } + } + + internal SolidBrush HeaderForeBrush { + get { + return this.headerForeBrush; + } + } + + internal Pen HeaderForePen { + get { + return headerForePen; + } + } + private void ResetHorizontalOffset() { + horizontalOffset = 0; + negOffset = 0; + firstVisibleCol = 0; + numVisibleCols = 0; + lastTotallyVisibleCol = -1; + } + + internal int HorizontalOffset { + get { + return horizontalOffset; + } + set { + //if (CompModSwitches.DataGridScrolling.TraceVerbose) Debug.WriteLine("DataGridScrolling: Set_HorizontalOffset, value = " + value.ToString()); + if (value < 0) + value = 0; + + // + // if the dataGrid is not bound ( listManager == null || gridTable == null) + // then use ResetHorizontalOffset(); + // + + int totalWidth = GetColumnWidthSum(); + int widthNotVisible = totalWidth - layout.Data.Width; + if (value > widthNotVisible && widthNotVisible > 0) + value = widthNotVisible; + + if (value == horizontalOffset) + return; + + int change = horizontalOffset - value; + horizScrollBar.Value = value; + Rectangle scroll = layout.Data; + if (layout.ColumnHeadersVisible) + scroll = Rectangle.Union(scroll, layout.ColumnHeaders); + horizontalOffset = value; + + this.firstVisibleCol = ComputeFirstVisibleColumn(); + // update the lastTotallyVisibleCol + ComputeVisibleColumns(); + + if (gridState[GRIDSTATE_isScrolling]) + { + // if the user did not click on the grid yet, then do not put the edit + // control when scrolling + if (currentCol >= firstVisibleCol && currentCol < firstVisibleCol + numVisibleCols - 1 && (gridState[GRIDSTATE_isEditing] || gridState[GRIDSTATE_isNavigating])) + Edit(); + else + EndEdit(); + + // isScrolling is set to TRUE when the user scrolls. + // once we move the edit box, we finished processing the scroll event, so set isScrolling to FALSE + // to set isScrolling to TRUE, we need another scroll event. + gridState[GRIDSTATE_isScrolling] = false; + } + else + { + EndEdit(); + } + + NativeMethods.RECT[] rects = CreateScrollableRegion(scroll); + ScrollRectangles(rects, change); + OnScroll(EventArgs.Empty); + } + } + + private void ScrollRectangles(NativeMethods.RECT[] rects, int change) { + if (rects != null) { + NativeMethods.RECT scroll; + if (isRightToLeft()) change = -change; + for (int r = 0; r < rects.Length; r++) { + scroll = rects[r]; + SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), + change, + 0, + ref scroll, + ref scroll); + } + } + } + + /// + /// + /// + [ + SRDescription(SR.DataGridHorizScrollBarDescr) + ] + protected ScrollBar HorizScrollBar { + get { + return horizScrollBar; + } + } + + /// + /// + /// Retrieves a value indicating whether odd and even + /// rows are painted using a different background color. + /// + /// + // CUT by 53973 - Cleanup eventually to be static. + internal bool LedgerStyle { + get { + return gridState[GRIDSTATE_isLedgerStyle]; + } + /* + set { + if (isLedgerStyle != value) { + isLedgerStyle = value; + InvalidateInside(); + } + } + */ + } + + /// + /// + /// + /// Indicates whether the property should be persisted. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridLinkColorDescr) + ] + public Color LinkColor { + get { + return linkBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "LinkColor")); + if (!linkBrush.Color.Equals(value)) { + linkBrush = new SolidBrush(value); + Invalidate(layout.Data); + } + } + } + + /// + /// [To be supplied.] + /// + internal virtual bool ShouldSerializeLinkColor() + { + return !LinkBrush.Equals(DefaultLinkBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetLinkColor() { + if (ShouldSerializeLinkColor()) + LinkColor = DefaultLinkBrush.Color; + } + + internal Brush LinkBrush { + get { + return linkBrush; + } + } + + /// + /// + /// + /// Gets + /// or sets the color a link changes to when + /// the mouse pointer moves over it. + /// + /// + [ + SRDescription(SR.DataGridLinkHoverColorDescr), + SRCategory(SR.CatColors), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + + ] + public Color LinkHoverColor { + get { + return this.LinkColor; + } + set { + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeLinkHoverColor() + { + return false; + // return !LinkHoverBrush.Equals(defaultLinkHoverBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetLinkHoverColor() { + /* + if (ShouldSerializeLinkHoverColor()) + LinkHoverColor = defaultLinkHoverBrush.Color;*/ + } + + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + + internal Font LinkFont { + get { + return linkFont; + } + } + + internal int LinkFontHeight { + get { + return linkFontHeight; + } + } + + /// + /// + /// Gets or sets a value + /// that specifies which links are shown and in what context. + /// + [ + DefaultValue(true), + SRDescription(SR.DataGridNavigationModeDescr), + SRCategory(SR.CatBehavior) + ] + public bool AllowNavigation { + get { + return gridState[GRIDSTATE_allowNavigation]; + } + set { + if (AllowNavigation != value) { + gridState[GRIDSTATE_allowNavigation] = value; + // let the Caption know about this: + this.Caption.BackButtonActive = !parentRows.IsEmpty() && (value); + this.Caption.BackButtonVisible = this.Caption.BackButtonActive; + RecreateDataGridRows(); + + OnAllowNavigationChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_ALLOWNAVIGATIONCHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnNavigationModeChangedDescr)] + public event EventHandler AllowNavigationChanged { + add { + Events.AddHandler(EVENT_ALLOWNAVIGATIONCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_ALLOWNAVIGATIONCHANGED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public override Cursor Cursor { + // get the cursor out of the propertyGrid. + get { + return base.Cursor; + } + + set { + base.Cursor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CursorChanged { + add { + base.CursorChanged += value; + } + remove { + base.CursorChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public override Image BackgroundImage { + // get the BackgroundImage out of the propertyGrid. + get { + return base.BackgroundImage; + } + + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public override ImageLayout BackgroundImageLayout { + // get the BackgroundImage out of the propertyGrid. + get { + return base.BackgroundImageLayout; + } + + set { + base.BackgroundImageLayout = value; + } + } + + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the background color of parent rows. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridParentRowsBackColorDescr) + ] + public Color ParentRowsBackColor { + get { + return parentRows.BackColor; + } + set { + if (IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTransparentParentRowsBackColorNotAllowed)); + this.parentRows.BackColor = value; + } + } + + internal SolidBrush ParentRowsBackBrush { + get { + return parentRows.BackBrush; + } + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + protected virtual bool ShouldSerializeParentRowsBackColor() + { + return !ParentRowsBackBrush.Equals(DefaultParentRowsBackBrush); + } + + private void ResetParentRowsBackColor() { + if (ShouldSerializeParentRowsBackColor()) + parentRows.BackBrush = DefaultParentRowsBackBrush; + } + + /// + /// + /// + /// Gets or sets the foreground color of parent rows. + /// + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridParentRowsForeColorDescr) + ] + public Color ParentRowsForeColor { + get { + return parentRows.ForeColor; + } + set { + this.parentRows.ForeColor = value; + } + } + + internal SolidBrush ParentRowsForeBrush { + get { + return parentRows.ForeBrush; + } + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + protected virtual bool ShouldSerializeParentRowsForeColor() + { + return !ParentRowsForeBrush.Equals(DefaultParentRowsForeBrush); + } + + private void ResetParentRowsForeColor() { + if (ShouldSerializeParentRowsForeColor()) + parentRows.ForeBrush = DefaultParentRowsForeBrush; + } + + + /// + /// + /// + /// Gets + /// or sets the default width of the grid columns in + /// pixels. + /// + /// + [ + DefaultValue(defaultPreferredColumnWidth), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridPreferredColumnWidthDescr), + TypeConverter(typeof(DataGridPreferredColumnWidthTypeConverter)) + ] + public int PreferredColumnWidth { + get { + return preferredColumnWidth; + } + set { + if (value < 0) + throw new ArgumentException(SR.GetString(SR.DataGridColumnWidth), "PreferredColumnWidth"); + if (preferredColumnWidth != value) { + preferredColumnWidth = value; + } + } + } + + /// + /// + /// + /// Gets or sets the preferred row height for the control. + /// + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridPreferredRowHeightDescr) + ] + public int PreferredRowHeight { + get { + return prefferedRowHeight; + } + set { + if (value < 0) + throw new ArgumentException(SR.GetString(SR.DataGridRowRowHeight)); + prefferedRowHeight = value; + } + } + + private void ResetPreferredRowHeight() { + prefferedRowHeight = defaultFontHeight + 3; + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializePreferredRowHeight() + { + return prefferedRowHeight != defaultFontHeight + 3; + } + + /// + /// + /// Gets or sets a value indicating whether the grid + /// is in read-only mode. + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridReadOnlyDescr) + ] + public bool ReadOnly { + get { + return gridState[GRIDSTATE_readOnlyMode]; + } + set { + if (ReadOnly != value) { + bool recreateRows = false; + if (value) { + // AllowAdd happens to have the same boolean value as whether we need to recreate rows. + recreateRows = policy.AllowAdd; + + policy.AllowRemove = false; + policy.AllowEdit = false; + policy.AllowAdd = false; + } + else { + recreateRows |= policy.UpdatePolicy(this.listManager, value); + } + gridState[GRIDSTATE_readOnlyMode] = value; + DataGridRow[] dataGridRows = this.DataGridRows; + if (recreateRows) { + RecreateDataGridRows(); + + // keep the selected rows + DataGridRow[] currentDataGridRows = this.DataGridRows; + int rowCount = Math.Min(currentDataGridRows.Length, dataGridRows.Length); + for (int i = 0; i < rowCount; i++) { + if (dataGridRows[i].Selected) + currentDataGridRows[i].Selected = true; + } + } + + // the addnew row needs to be updated. + PerformLayout(); + InvalidateInside(); + OnReadOnlyChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_READONLYCHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnReadOnlyChangedDescr)] + public event EventHandler ReadOnlyChanged { + add { + Events.AddHandler(EVENT_READONLYCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_READONLYCHANGED, value); + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating if the grid's column headers are visible. + /// + /// + [ + SRCategory(SR.CatDisplay), + DefaultValue(true), + SRDescription(SR.DataGridColumnHeadersVisibleDescr) + ] + public bool ColumnHeadersVisible { + get { + return gridState[GRIDSTATE_columnHeadersVisible]; + } + set { + if (ColumnHeadersVisible != value) { + gridState[GRIDSTATE_columnHeadersVisible] = value; + layout.ColumnHeadersVisible = value; + PerformLayout(); + InvalidateInside(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the parent rows of a table are + /// visible. + /// + /// + [ + SRCategory(SR.CatDisplay), + DefaultValue(true), + SRDescription(SR.DataGridParentRowsVisibleDescr) + ] + public bool ParentRowsVisible { + get { + return layout.ParentRowsVisible; + } + set { + if (layout.ParentRowsVisible != value) { + SetParentRowsVisibility(value); + + // update the caption: parentDownVisible == false corresponds to DownButtonDown == true; + // + caption.SetDownButtonDirection(!value); + + OnParentRowsVisibleChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_PARENTROWSVISIBLECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnParentRowsVisibleChangedDescr)] + public event EventHandler ParentRowsVisibleChanged { + add { + Events.AddHandler(EVENT_PARENTROWSVISIBLECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_PARENTROWSVISIBLECHANGED, value); + } + } + + internal bool ParentRowsIsEmpty() { + return parentRows.IsEmpty(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the data grid's row headers are + /// visible. + /// + /// + [ + SRCategory(SR.CatDisplay), + DefaultValue(true), + SRDescription(SR.DataGridRowHeadersVisibleDescr) + ] + public bool RowHeadersVisible { + get { + return gridState[GRIDSTATE_rowHeadersVisible]; + } + set { + if (RowHeadersVisible != value) { + gridState[GRIDSTATE_rowHeadersVisible] = value; + PerformLayout(); + InvalidateInside(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(defaultRowHeaderWidth), + SRDescription(SR.DataGridRowHeaderWidthDescr) + ] + public int RowHeaderWidth { + get { + return rowHeaderWidth; + } + set { + value = Math.Max(this.minRowHeaderWidth, value); + if (rowHeaderWidth != value) + { + rowHeaderWidth = value; + if (layout.RowHeadersVisible) + { + PerformLayout(); + InvalidateInside(); + } + } + } + } + + /// + /// + /// + /// Gets or sets the width of headers. + /// + /// + + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Bindable(false) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Gets the vertical scroll bar of the control. + /// + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridVertScrollBarDescr) + ] + protected ScrollBar VertScrollBar { + get { + return vertScrollBar; + } + } + + /// + /// + /// + /// Gets the number of visible columns. + /// + /// + [ + Browsable(false), + SRDescription(SR.DataGridVisibleColumnCountDescr) + ] + public int VisibleColumnCount { + get { + return Math.Min(numVisibleCols, this.myGridTable == null ? 0 : this.myGridTable.GridColumnStyles.Count); + } + } + + /// + /// + /// + /// Gets the number of rows visible. + /// + /// + [ + Browsable(false), + SRDescription(SR.DataGridVisibleRowCountDescr) + ] + public int VisibleRowCount { + get { + return numVisibleRows; + } + } + + + /// + /// + /// Gets or sets the value of the cell at + /// the specified the row and column. + /// + public object this[int rowIndex, int columnIndex] { + get { + EnsureBound(); + if (rowIndex < 0 || rowIndex >= DataGridRowsLength) + throw new ArgumentOutOfRangeException("rowIndex"); + if (columnIndex < 0 || columnIndex >= myGridTable.GridColumnStyles.Count) + throw new ArgumentOutOfRangeException("columnIndex"); + CurrencyManager listManager = this.listManager; + DataGridColumnStyle column = myGridTable.GridColumnStyles[columnIndex]; + return column.GetColumnValueAtRow(listManager, rowIndex); + } + set { + EnsureBound(); + if (rowIndex < 0 || rowIndex >= DataGridRowsLength) + throw new ArgumentOutOfRangeException("rowIndex"); + if (columnIndex < 0 || columnIndex >= myGridTable.GridColumnStyles.Count) + throw new ArgumentOutOfRangeException("columnIndex"); + CurrencyManager listManager = this.listManager; + if (listManager.Position != rowIndex) + listManager.Position = rowIndex; + DataGridColumnStyle column = myGridTable.GridColumnStyles[columnIndex]; + column.SetColumnValueAtRow(listManager, rowIndex, value); + + // invalidate the bounds of the cell only if the cell is visible + if (columnIndex >= firstVisibleCol && columnIndex <= firstVisibleCol+ numVisibleCols - 1 && + rowIndex >= firstVisibleRow && rowIndex <= firstVisibleRow + numVisibleRows) { + Rectangle bounds = GetCellBounds(rowIndex, columnIndex); + this.Invalidate(bounds); + } + } + } + + /// + /// + /// Gets or sets the value of a specified . + /// + public object this[DataGridCell cell] { + get { + return this[cell.RowNumber, cell.ColumnNumber]; + } + set { + this[cell.RowNumber, cell.ColumnNumber] = value; + } + } + + private void WireTableStylePropChanged(DataGridTableStyle gridTable) { + gridTable.GridLineColorChanged += new EventHandler(GridLineColorChanged); + gridTable.GridLineStyleChanged += new EventHandler(GridLineStyleChanged); + gridTable.HeaderBackColorChanged += new EventHandler(HeaderBackColorChanged); + gridTable.HeaderFontChanged += new EventHandler(HeaderFontChanged); + gridTable.HeaderForeColorChanged += new EventHandler(HeaderForeColorChanged); + gridTable.LinkColorChanged += new EventHandler(LinkColorChanged); + gridTable.LinkHoverColorChanged += new EventHandler(LinkHoverColorChanged); + gridTable.PreferredColumnWidthChanged += new EventHandler(PreferredColumnWidthChanged); + gridTable.RowHeadersVisibleChanged += new EventHandler(RowHeadersVisibleChanged); + gridTable.ColumnHeadersVisibleChanged += new EventHandler(ColumnHeadersVisibleChanged); + gridTable.RowHeaderWidthChanged += new EventHandler(RowHeaderWidthChanged); + gridTable.AllowSortingChanged += new EventHandler(AllowSortingChanged); + } + + private void UnWireTableStylePropChanged(DataGridTableStyle gridTable) { + gridTable.GridLineColorChanged -= new EventHandler(GridLineColorChanged); + gridTable.GridLineStyleChanged -= new EventHandler(GridLineStyleChanged); + gridTable.HeaderBackColorChanged -= new EventHandler(HeaderBackColorChanged); + gridTable.HeaderFontChanged -= new EventHandler(HeaderFontChanged); + gridTable.HeaderForeColorChanged -= new EventHandler(HeaderForeColorChanged); + gridTable.LinkColorChanged -= new EventHandler(LinkColorChanged); + gridTable.LinkHoverColorChanged -= new EventHandler(LinkHoverColorChanged); + gridTable.PreferredColumnWidthChanged -= new EventHandler(PreferredColumnWidthChanged); + gridTable.RowHeadersVisibleChanged -= new EventHandler(RowHeadersVisibleChanged); + gridTable.ColumnHeadersVisibleChanged -= new EventHandler(ColumnHeadersVisibleChanged); + gridTable.RowHeaderWidthChanged -= new EventHandler(RowHeaderWidthChanged); + gridTable.AllowSortingChanged -= new EventHandler(AllowSortingChanged); + } + + /// + /// DataSource events are handled + /// + private void WireDataSource() { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: WireDataSource"); + Debug.Assert(listManager!= null, "Can't wire up to a null DataSource"); + listManager.CurrentChanged += currentChangedHandler; + listManager.PositionChanged += positionChangedHandler; + listManager.ItemChanged += itemChangedHandler; + listManager.MetaDataChanged += metaDataChangedHandler; + } + + private void UnWireDataSource() { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: UnWireDataSource"); + Debug.Assert(listManager != null, "Can't un wire from a null DataSource"); + listManager.CurrentChanged -= currentChangedHandler; + listManager.PositionChanged -= positionChangedHandler; + listManager.ItemChanged -= itemChangedHandler; + listManager.MetaDataChanged -= metaDataChangedHandler; + } + + // This is called after a row has been added. And I think whenever + // a row gets deleted, etc. + // We recreate our datagrid rows at this point. + private void DataSource_Changed(object sender, EventArgs ea) { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_Changed"); + + // the grid will receive the dataSource_Changed event when + // allowAdd changes on the dataView. + policy.UpdatePolicy(this.ListManager, this.ReadOnly); + if (gridState[GRIDSTATE_inListAddNew]) { + // we are adding a new row + // keep the old rows, w/ their height, expanded/collapsed information + // + Debug.Assert(policy.AllowAdd, "how can we add a new row if the policy does not allow this?"); + Debug.Assert(DataGridRowsLength == DataGridRows.Length, "how can this fail?"); + + DataGridRow[] gridRows = DataGridRows; + int currentRowCount = DataGridRowsLength; + // put the added row: + // + gridRows[currentRowCount - 1] = new DataGridRelationshipRow(this, this.myGridTable, currentRowCount - 1); + SetDataGridRows(gridRows, currentRowCount); + } else if (gridState[GRIDSTATE_inAddNewRow] && !gridState[GRIDSTATE_inDeleteRow]) { + // when the backEnd adds a row and we are still inAddNewRow + listManager.CancelCurrentEdit(); + gridState[GRIDSTATE_inAddNewRow] = false; + RecreateDataGridRows(); + } else if (!gridState[GRIDSTATE_inDeleteRow]) { + RecreateDataGridRows(); + currentRow = Math.Min(currentRow, this.listManager.Count); + } + + bool oldListHasErrors = ListHasErrors; + ListHasErrors = DataGridSourceHasErrors(); + // if we changed the ListHasErrors, then the grid is already invalidated + if (oldListHasErrors == ListHasErrors) + InvalidateInside(); + } + + private void GridLineColorChanged(object sender, EventArgs e) { + Invalidate(layout.Data); + } + private void GridLineStyleChanged(object sender, EventArgs e) { + this.myGridTable.ResetRelationsUI(); + Invalidate(layout.Data); + } + private void HeaderBackColorChanged(object sender, EventArgs e) { + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + } + private void HeaderFontChanged(object sender, EventArgs e) { + RecalculateFonts(); + PerformLayout(); + Invalidate(layout.Inside); + } + private void HeaderForeColorChanged(object sender, EventArgs e) { + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + } + private void LinkColorChanged(object sender, EventArgs e) { + Invalidate(layout.Data); + } + private void LinkHoverColorChanged(object sender, EventArgs e) { + Invalidate(layout.Data); + } + private void PreferredColumnWidthChanged(object sender, EventArgs e) { + // reset the dataGridRows + SetDataGridRows(null, this.DataGridRowsLength); + // layout the horizontal scroll bar + PerformLayout(); + // invalidate everything + Invalidate(); + } + private void RowHeadersVisibleChanged(object sender, EventArgs e) { + layout.RowHeadersVisible = this.myGridTable == null ? false : this.myGridTable.RowHeadersVisible; + PerformLayout(); + InvalidateInside(); + } + private void ColumnHeadersVisibleChanged(object sender, EventArgs e) { + layout.ColumnHeadersVisible = this.myGridTable == null ? false : this.myGridTable.ColumnHeadersVisible; + PerformLayout(); + InvalidateInside(); + } + private void RowHeaderWidthChanged(object sender, EventArgs e) { + if (layout.RowHeadersVisible) + { + PerformLayout(); + InvalidateInside(); + } + } + private void AllowSortingChanged(object sender, EventArgs e) { + if (!this.myGridTable.AllowSorting && this.listManager != null) { + IList list = this.listManager.List; + if (list is IBindingList) + ((IBindingList) list).RemoveSort(); + } + } + + private void DataSource_RowChanged(object sender, EventArgs ea) { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_RowChanged"); + // it may be the case that our cache was not updated + // to the latest changes in the list : CurrentChanged is fired before + // ListChanged. + // So invalidate the row if there is something to invalidate + DataGridRow[] rows = this.DataGridRows; + if (currentRow < this.DataGridRowsLength) { + InvalidateRow(currentRow); + } + } + + /// + /// + /// Fired by the DataSource when row position moves. + /// + /// + private void DataSource_PositionChanged(object sender, EventArgs ea) { + #if DEBUG + inDataSource_PositionChanged = true; + #endif + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_PositionChanged to " + listManager.Position.ToString(CultureInfo.InvariantCulture)); + // the grid will get the PositionChanged event + // before the OnItemChanged event when a row will be deleted in the backEnd; + // we still want to keep the old rows when the user deletes the rows using the grid + // and we do not want to do the same work twice when the user adds a row via the grid + if (this.DataGridRowsLength > this.listManager.Count + (policy.AllowAdd?1:0) && !gridState[GRIDSTATE_inDeleteRow]) { + Debug.Assert(!gridState[GRIDSTATE_inAddNewRow] && !gridState[GRIDSTATE_inListAddNew], "how can the list decrease when we are adding a row?"); + RecreateDataGridRows(); + } + if (this.ListManager.Position != currentRow) { + CurrentCell = new DataGridCell(listManager.Position, currentCol); + + } + #if DEBUG + inDataSource_PositionChanged = false; + #endif + } + + internal void DataSource_MetaDataChanged(object sender, EventArgs e) { + MetaDataChanged(); + } + + private bool DataGridSourceHasErrors() + { + if (this.listManager == null) + return false; + for (int i = 0; i < this.listManager.Count; i++) + { + object errObj = this.listManager[i]; + if (errObj is IDataErrorInfo) + { + string errString = ((IDataErrorInfo)errObj).Error; + if (errString != null && errString.Length != 0) + return true; + } + } + return false; + } + + private void TableStylesCollectionChanged(object sender, CollectionChangeEventArgs ccea) { + // if the users changed the collection of tableStyles + if (sender != this.dataGridTables) + return; + if (this.listManager == null) + return; + + if (ccea.Action == CollectionChangeAction.Add) { + DataGridTableStyle tableStyle = (DataGridTableStyle) ccea.Element; + if (this.listManager.GetListName().Equals(tableStyle.MappingName)) { + Debug.Assert(this.myGridTable.IsDefault, "if the table is not default, then it had a name. how can one add another table to the collection w/ the same name and not throw an exception"); + SetDataGridTable(tableStyle, true); // true for forcing column creation + SetDataGridRows(null, 0); + } + } else if (ccea.Action == CollectionChangeAction.Remove) { + DataGridTableStyle tableStyle = (DataGridTableStyle) ccea.Element; + if (this.myGridTable.MappingName.Equals(tableStyle.MappingName)) { + Debug.Assert(this.myGridTable.IsDefault, "if the table is not default, then it had a name. how can one add another table to the collection w/ the same name and not throw an exception"); + this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection(); + SetDataGridTable(this.defaultTableStyle, true); // true for forcing column creation + SetDataGridRows(null, 0); + } + } else { + Debug.Assert(ccea.Action == CollectionChangeAction.Refresh, "what else is possible?"); + // we have to search to see if the collection of table styles contains one + // w/ the same name as the list in the dataGrid + + DataGridTableStyle newGridTable = this.dataGridTables[listManager.GetListName()]; + if (newGridTable == null) { + if (!this.myGridTable.IsDefault) { + // get rid of the old gridColumns + this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection(); + SetDataGridTable(this.defaultTableStyle, true); // true for forcing column creation + SetDataGridRows(null, 0); + } + } else { + SetDataGridTable(newGridTable, true); // true for forcing column creation + SetDataGridRows(null, 0); + } + } + } + + private void DataSource_ItemChanged(object sender, ItemChangedEventArgs ea) { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_ItemChanged at index " + ea.Index.ToString(CultureInfo.InvariantCulture)); + + // if ea.Index == -1, then we invalidate all rows. + if (ea.Index == -1) + { + DataSource_Changed(sender, EventArgs.Empty); + /* + // if there are rows which are invisible, it is more efficient to invalidata layout.Data + if (numVisibleRows <= dataGridRowsLength) + Invalidate(layout.Data); + else + { + Debug.Assert(firstVisibleRow == 0, "if all rows are visible, then how come that first row is not visible?"); + for (int i = 0; i < numVisibleRows; i++) + InvalidateRow(firstVisibleRow + numVisibleRows); + } + */ + } + else + { + // let's see how we are doing w/ the errors + object errObj = this.listManager[ea.Index]; + bool oldListHasErrors = ListHasErrors; + if (errObj is IDataErrorInfo) + { + if (((IDataErrorInfo)errObj).Error.Length != 0) + ListHasErrors = true; + else if (ListHasErrors) + { + // maybe there was an error that now is fixed + ListHasErrors = DataGridSourceHasErrors(); + } + } + + // Invalidate the row only if we did not change the ListHasErrors + if (oldListHasErrors == ListHasErrors) + InvalidateRow(ea.Index); + + // we need to update the edit box: + // we update the text in the edit box only when the currentRow + // equals the ea.Index + if (editColumn != null && ea.Index == currentRow) + editColumn.UpdateUI(this.ListManager, ea.Index, null); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnBorderStyleChanged(EventArgs e) { + EventHandler eh = Events[EVENT_BORDERSTYLECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnCaptionVisibleChanged(EventArgs e) { + EventHandler eh = Events[EVENT_CAPTIONVISIBLECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnCurrentCellChanged(EventArgs e) { + EventHandler eh = Events[EVENT_CURRENTCELLCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /* + /// + /// + /// Raises the event. + /// + /// + /// + /// An that contains the event data. + /// + /// + /// + /// Raising an event invokes the event-handling method through a delegate. For an + /// overview, see . + /// + /// When overriding in an + /// derived class, be sure to call the base class's method. + /// + protected void OnColumnHeaderClick(EventArgs e) { + RaiseEvent(EVENT_COLUMNHEADERCLICK, e); + } + */ + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnFlatModeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_FLATMODECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnBackgroundColorChanged(EventArgs e) { + EventHandler eh = Events[EVENT_BACKGROUNDCOLORCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnAllowNavigationChanged(EventArgs e) { + EventHandler eh = Events[EVENT_ALLOWNAVIGATIONCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnParentRowsVisibleChanged(EventArgs e) { + EventHandler eh = Events[EVENT_PARENTROWSVISIBLECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnParentRowsLabelStyleChanged(EventArgs e) { + EventHandler eh = Events[EVENT_PARENTROWSLABELSTYLECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnReadOnlyChanged(EventArgs e) { + EventHandler eh = Events[EVENT_READONLYCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected void OnNavigate(NavigateEventArgs e) { + if (onNavigate != null) + onNavigate(this, e); + } + + /* + /// + /// + /// + /// Raises the event. + /// + /// + protected void OnColumnResize(EventArgs e) { + RaiseEvent(EVENT_COLUMNRESIZE, e); + } + + internal void OnLinkClick(EventArgs e) { + RaiseEvent(EVENT_LINKCLICKED, e); + } + */ + + internal void OnNodeClick(EventArgs e) { + // if we expanded/collapsed the RelationshipRow + // then we need to layout the vertical scroll bar + // + PerformLayout(); + + + // also, we need to let the hosted edit control that its + // boundaries possibly changed. do this with a call to Edit() + // do this only if the firstVisibleColumn is the editColumn + // + GridColumnStylesCollection columns = this.myGridTable.GridColumnStyles; + if (firstVisibleCol > -1 && firstVisibleCol < columns.Count && columns[firstVisibleCol] == editColumn) + Edit(); + + // Raise the event for the event listeners + EventHandler handler = (EventHandler)Events[EVENT_NODECLICKED]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// Raises the event. + /// + protected void OnRowHeaderClick(EventArgs e) { + if (onRowHeaderClick != null) + onRowHeaderClick(this, e); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected void OnScroll(EventArgs e) { + // reset the toolTip information + if (ToolTipProvider != null) + ResetToolTip(); + + EventHandler handler = (EventHandler)Events[EVENT_SCROLL]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// + /// Listens + /// for the horizontal scrollbar's scroll + /// event. + /// + /// + protected virtual void GridHScrolled(object sender, ScrollEventArgs se) { + if (!Enabled) + return; + if (DataSource == null) { + Debug.Fail("Horizontal Scrollbar should be disabled without a DataSource."); + return; + } + + gridState[GRIDSTATE_isScrolling] = true; + + #if DEBUG + + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: in GridHScrolled: the scroll event type:"); + switch (se.Type) + { + case ScrollEventType.SmallIncrement: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small increment"); + break; + case ScrollEventType.SmallDecrement: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small decrement"); + break; + case ScrollEventType.LargeIncrement: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Large decrement"); + break; + case ScrollEventType.LargeDecrement: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small decrement"); + break; + case ScrollEventType.ThumbPosition: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Thumb Position"); + break; + case ScrollEventType.ThumbTrack: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Thumb Track"); + break; + case ScrollEventType.First: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "First"); + break; + case ScrollEventType.Last: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Last"); + break; + case ScrollEventType.EndScroll: + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "EndScroll"); + break; + } + + #endif // DEBUG + + if (se.Type == ScrollEventType.SmallIncrement || + se.Type == ScrollEventType.SmallDecrement) + { + int dCols = (se.Type == ScrollEventType.SmallIncrement)? 1:-1; + if (se.Type == ScrollEventType.SmallDecrement && this.negOffset == 0) { + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + // if the column before the first visible column has width == 0 then skip it + for (int i = this.firstVisibleCol - 1; i >= 0 && cols[i].Width == 0; i --) { + dCols --; + } + } + + if (se.Type == ScrollEventType.SmallIncrement && this.negOffset == 0) { + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + for (int i = this.firstVisibleCol; i > -1 && i < cols.Count && cols[i].Width == 0; i ++) { + dCols ++; + } + } + + ScrollRight(dCols); + se.NewValue = HorizontalOffset; + } + else if (se.Type != ScrollEventType.EndScroll) + { + HorizontalOffset = se.NewValue; + } + + this.gridState[GRIDSTATE_isScrolling] = false; + } + + /// + /// + /// + /// Listens + /// for the vertical scrollbar's scroll event. + /// + /// + protected virtual void GridVScrolled(object sender, ScrollEventArgs se) { + if (!Enabled) + return; + if (DataSource == null) { + Debug.Fail("Vertical Scrollbar should be disabled without a DataSource."); + return; + } + + gridState[GRIDSTATE_isScrolling] = true; + + try { + se.NewValue = Math.Min(se.NewValue, DataGridRowsLength - numTotallyVisibleRows); + int dRows = se.NewValue - firstVisibleRow; + ScrollDown(dRows); + } finally { + gridState[GRIDSTATE_isScrolling] = false; + } + } + + private void HandleEndCurrentEdit() { + int currentRowSaved = currentRow; + int currentColSaved = currentCol; + + String errorMessage = null; + + try { + listManager.EndCurrentEdit(); + } + catch (Exception e) { + errorMessage = e.Message; + } + + if (errorMessage != null) { + DialogResult result = RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridPushedIncorrectValueIntoColumn, + errorMessage), SR.GetString(SR.DataGridErrorMessageBoxCaption), MessageBoxButtons.YesNo, + MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + + if (result == DialogResult.Yes) { + currentRow = currentRowSaved; + currentCol = currentColSaved; + Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager (" + ListManager.Position + ") is out of sync with the currentRow (" + currentRow + ")" + " and the exception is '" + errorMessage + "'"); + // also, make sure that we get the row selector on the currentrow, too + InvalidateRowHeader(currentRow); + Edit(); + } else { + // if the user committed a row that used to be addNewRow and the backEnd rejects it, + // and then it tries to navigate down then we should stay in the addNewRow + // in this particular scenario, CancelCurrentEdit will cause the last row to be deleted, + // and this will ultimately call InvalidateRow w/ a row number larger than the number of rows + // so set the currentRow here: + Debug.Assert(result == DialogResult.No, "we only put cancel and ok on the error message box"); + this.listManager.PositionChanged -= positionChangedHandler; + this.listManager.CancelCurrentEdit(); + this.listManager.Position = currentRow; + this.listManager.PositionChanged += positionChangedHandler; + } + } + } + + /// + /// + /// + /// Listens + /// for the caption's back button clicked event. + /// + /// + protected void OnBackButtonClicked(object sender, EventArgs e) { + NavigateBack(); + + EventHandler handler = (EventHandler)Events[EVENT_BACKBUTTONCLICK]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBackColorChanged(EventArgs e) { + backBrush = new SolidBrush(BackColor); + Invalidate(); + + base.OnBackColorChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBindingContextChanged(EventArgs e) { + if (this.DataSource != null && !gridState[GRIDSTATE_inSetListManager]) + try { + Set_ListManager(this.DataSource, this.DataMember, true, false); // we do not want to create columns + // if the columns are already created + // the grid should not rely on OnBindingContextChanged + // to create columns. + } catch { + // at runtime we will rethrow the exception + if (this.Site == null || !this.Site.DesignMode) + throw; + + RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridExceptionInPaint), null, + MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + + if (this.Visible) BeginUpdateInternal(); + + ResetParentRows(); + + Set_ListManager(null, String.Empty, true); + if (this.Visible) EndUpdateInternal(); + } + base.OnBindingContextChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnDataSourceChanged(EventArgs e) { + EventHandler eh = Events[EVENT_DATASOURCECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// + /// Listens for + /// the caption's down button clicked event. + /// + /// + protected void OnShowParentDetailsButtonClicked(object sender, EventArgs e) { + // we need to fire the ParentRowsVisibleChanged event + // and the ParentRowsVisible property just calls SetParentRowsVisibility and + // then fires the event. + this.ParentRowsVisible = !caption.ToggleDownButtonDirection(); + + EventHandler handler = (EventHandler)Events[EVENT_DOWNBUTTONCLICK]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnForeColorChanged(EventArgs e) { + foreBrush = new SolidBrush(ForeColor); + Invalidate(); + + base.OnForeColorChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + // let the caption know about the event changed + // + this.Caption.OnGridFontChanged(); + RecalculateFonts(); + RecreateDataGridRows(); + // get all the rows in the parentRows stack, and modify their height + if (originalState != null) { + Debug.Assert(!parentRows.IsEmpty(), "if the originalState is not null, then parentRows contains at least one row"); + Stack parentStack = new Stack(); + // this is a huge performance hit: + // everytime we get/put something from/to + // the parentRows, the buttons in the dataGridCaption + // are invalidated + while (!parentRows.IsEmpty()) { + DataGridState dgs = parentRows.PopTop(); + int rowCount = dgs.DataGridRowsLength; + + for(int i = 0; i < rowCount; i++) { + // performance hit: this will cause to invalidate a bunch of + // stuff + + dgs.DataGridRows[i].Height = dgs.DataGridRows[i].MinimumRowHeight(dgs.GridColumnStyles); + } + parentStack.Push(dgs); + } + + while(parentStack.Count != 0) { + parentRows.AddParent((DataGridState)parentStack.Pop()); + } + } + + base.OnFontChanged(e); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnPaintBackground(PaintEventArgs ebe) { + // null body + } + + /// + /// + /// + /// Raises the event which + /// repositions controls + /// and updates scroll bars. + /// + /// + protected override void OnLayout(LayoutEventArgs levent) { + // if we get a OnLayout event while the editControl changes, then just + // ignore it + // + if (gridState[GRIDSTATE_editControlChanging]) return; + + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnLayout"); + base.OnLayout(levent); + + if (gridState[GRIDSTATE_layoutSuspended]) return; + + gridState[GRIDSTATE_canFocus] = false; + try { + if (IsHandleCreated) { + if (layout.ParentRowsVisible) + parentRows.OnLayout(); + + // reset the toolTip information + if (ToolTipProvider != null) + ResetToolTip(); + + ComputeLayout(); + } + } finally { + gridState[GRIDSTATE_canFocus] = true; + } + + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + // toolTipping + toolTipProvider = new DataGridToolTip(this); + toolTipProvider.CreateToolTipHandle(); + toolTipId = 0; + + PerformLayout(); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnHandleDestroyed(EventArgs e) + { + base.OnHandleDestroyed(e); + + // toolTipping + if (toolTipProvider != null) { + toolTipProvider.Destroy(); + toolTipProvider = null; + } + toolTipId = 0; + } + + /// + /// + /// Raises the + /// event. + /// + protected override void OnEnter(EventArgs e) { + if (gridState[GRIDSTATE_canFocus] && !gridState[GRIDSTATE_editControlChanging]) { + if (Bound) + { + Edit(); + } + base.OnEnter(e); + } + } + + /// + /// + /// Raises the + /// event. + /// + protected override void OnLeave(EventArgs e) { + OnLeave_Grid(); + base.OnLeave(e); + } + + private void OnLeave_Grid() { + gridState[GRIDSTATE_canFocus] = false; + try { + EndEdit(); + if (this.listManager != null && !this.gridState[GRIDSTATE_editControlChanging]) { + if (gridState[GRIDSTATE_inAddNewRow]) { + // if the user did not type anything + // in the addNewRow, then cancel the currentedit + this.listManager.CancelCurrentEdit(); + // set the addNewRow back + DataGridRow[] localGridRows = this.DataGridRows; + localGridRows[DataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength-1); + SetDataGridRows(localGridRows, DataGridRowsLength); + } else { + // this.listManager.EndCurrentEdit(); + HandleEndCurrentEdit(); + } + } + } finally { + gridState[GRIDSTATE_canFocus] = true; + // inAddNewRow should be set to false if the control was + // not changing + if (!this.gridState[GRIDSTATE_editControlChanging]) + gridState[GRIDSTATE_inAddNewRow] = false; + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnKeyDown(KeyEventArgs ke) { + Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: OnKeyDown "); + base.OnKeyDown(ke); + ProcessGridKey(ke); + } + + /// + /// + /// Raises the event. + /// + protected override void OnKeyPress(KeyPressEventArgs kpe) { + Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: OnKeyPress " + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(kpe.KeyChar)); + + base.OnKeyPress(kpe); + GridColumnStylesCollection coll = this.myGridTable.GridColumnStyles; + if (coll != null && currentCol > 0 && currentCol < coll.Count) { + if (!coll[currentCol].ReadOnly) + if (kpe.KeyChar > 32) { + Edit(new string(new char [] { (char) kpe.KeyChar})); + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown(e); + + gridState[GRIDSTATE_childLinkFocused] = false; + gridState[GRIDSTATE_dragging] = false; + if (listManager == null) + return; + + HitTestInfo location = HitTest(e.X, e.Y); + Keys nModifier = ModifierKeys; + bool isControlDown = (nModifier & Keys.Control) == Keys.Control && (nModifier & Keys.Alt) == 0; + bool isShiftDown = (nModifier & Keys.Shift) == Keys.Shift; + + // Only left clicks for now + if (e.Button != MouseButtons.Left) + return; + + // Check column resize + if (location.type == HitTestType.ColumnResize) { + if (e.Clicks > 1) { + ColAutoResize(location.col); + } + else + ColResizeBegin(e, location.col); + return; + } + + // Check row resize + if (location.type == HitTestType.RowResize) + { + if (e.Clicks > 1) + { + RowAutoResize(location.row); + } + else + { + RowResizeBegin(e, location.row); + } + return; + } + + // Check column headers + if (location.type == HitTestType.ColumnHeader) { + trackColumnHeader = this.myGridTable.GridColumnStyles[location.col].PropertyDescriptor; + return; + } + + if (location.type == HitTestType.Caption) { + Rectangle captionRect = layout.Caption; + caption.MouseDown(e.X - captionRect.X, e.Y - captionRect.Y); + return; + } + + if (layout.Data.Contains(e.X, e.Y) || layout.RowHeaders.Contains(e.X, e.Y)) { + // Check with the row underneath the mouse + int row = GetRowFromY(e.Y); + if (row > -1) { + Point p = NormalizeToRow(e.X, e.Y, row); + DataGridRow[] localGridRows = DataGridRows; + if (localGridRows[row].OnMouseDown(p.X, p.Y, + layout.RowHeaders, + isRightToLeft())) { + CommitEdit(); + + // possibly this was the last row, so then the link may not + // be visible. make it visible, by making the row visible. + // how can we be sure that the user did not click + // on a relationship and navigated to the child rows? + // check if the row is expanded: if the row is expanded, then the user clicked + // on the node. when we navigate to child rows the rows are recreated + // and are initially collapsed + localGridRows = this.DataGridRows; + if (row < DataGridRowsLength && (localGridRows[row] is DataGridRelationshipRow) && ((DataGridRelationshipRow)localGridRows[row]).Expanded) + EnsureVisible(row, 0); + + // show the edit box + // + Edit(); + return; + } + } + } + + // Check row headers + // + if (location.type == HitTestType.RowHeader) { + EndEdit(); + if (!(DataGridRows[location.row] is DataGridAddNewRow)) { + int savedCurrentRow = this.currentRow; + CurrentCell = new DataGridCell(location.row, currentCol); + if (location.row != savedCurrentRow && + this.currentRow != location.row && + this.currentRow == savedCurrentRow) { + // The data grid was not able to move away from its previous current row. + // Be defensive and don't select the row. + return; + } + } + + if (isControlDown) { + if (IsSelected(location.row)) + UnSelect(location.row); + else + Select(location.row); + } + else { + if (lastRowSelected == -1 || !isShiftDown) + { + ResetSelection(); + Select(location.row); + } + else + { + int lowerRow = Math.Min(lastRowSelected, location.row); + int upperRow = Math.Max(lastRowSelected, location.row); + + // we need to reset the old SelectedRows. + // ResetSelection() will also reset lastRowSelected, so we + // need to save it + int saveLastRowSelected = lastRowSelected; + ResetSelection(); + lastRowSelected = saveLastRowSelected; + + DataGridRow[] rows = DataGridRows; + for (int i = lowerRow; i <= upperRow; i++) + { + rows[i].Selected = true; + numSelectedRows ++; + } + + // hide the edit box: + // + EndEdit(); + return; + } + } + + lastRowSelected = location.row; + // OnRowHeaderClick(EventArgs.Empty); + return; + } + + // Check parentRows + // + if (location.type == HitTestType.ParentRows) + { + EndEdit(); + parentRows.OnMouseDown(e.X, e.Y, isRightToLeft()); + } + + // Check cell clicks + // + if (location.type == HitTestType.Cell) { + if (this.myGridTable.GridColumnStyles[location.col].MouseDown(location.row, e.X, e.Y)) + return; + DataGridCell target = new DataGridCell(location.row, location.col); + if (policy.AllowEdit && CurrentCell.Equals(target)) { + ResetSelection(); + // + // what if only a part of the current cell is visible? + // + EnsureVisible(currentRow, currentCol); + Edit(); + } + else { + ResetSelection(); + CurrentCell = target; + } + } + } + + /// + /// + /// Creates the + /// event. + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + if (oldRow != -1) { + DataGridRow[] localGridRows = DataGridRows; + localGridRows[oldRow].OnMouseLeft(layout.RowHeaders, isRightToLeft()); + } + if (gridState[GRIDSTATE_overCaption]) { + caption.MouseLeft(); + } + // when the mouse leaves the grid control, reset the cursor to arrow + Cursor = null; + } + + internal void TextBoxOnMouseWheel(MouseEventArgs e) { + this.OnMouseWheel(e); + } + + /// + /// + /// Raises the + /// event. + /// + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + if (listManager == null) + return; + + HitTestInfo location = HitTest(e.X, e.Y); + + bool alignToRight = isRightToLeft(); + + // We need to give UI feedback when the user is resizing a column + if (gridState[GRIDSTATE_trackColResize]) { + ColResizeMove(e); + } + + if (gridState[GRIDSTATE_trackRowResize]) + { + RowResizeMove(e); + } + + if (gridState[GRIDSTATE_trackColResize] || location.type == HitTestType.ColumnResize) + { + Cursor = Cursors.SizeWE; + return; + } + else if ( gridState[GRIDSTATE_trackRowResize] || location.type == HitTestType.RowResize) + { + Cursor = Cursors.SizeNS; + return; + } + else + { + Cursor = null; + } + + if ((layout.Data.Contains(e.X, e.Y) + || (layout.RowHeadersVisible && layout.RowHeaders.Contains(e.X, e.Y)))) { + // && (isNavigating || isEditing)) { + DataGridRow[] localGridRows = DataGridRows; + // If we are over a row, let it know about the mouse move. + int rowOver = GetRowFromY(e.Y); + // set the dragging bit: + if (lastRowSelected != -1 && !gridState[GRIDSTATE_dragging]) { + int topRow = GetRowTop(lastRowSelected); + int bottomRow = topRow + localGridRows[lastRowSelected].Height; + int dragHeight = SystemInformation.DragSize.Height; + gridState[GRIDSTATE_dragging] = ((e.Y - topRow < dragHeight && topRow - e.Y < dragHeight) || (e.Y - bottomRow < dragHeight && bottomRow - e.Y < dragHeight)); + } + if (rowOver > -1) { + Point p = NormalizeToRow(e.X, e.Y, rowOver); + if (!localGridRows[rowOver].OnMouseMove(p.X, p.Y, layout.RowHeaders, alignToRight) && gridState[GRIDSTATE_dragging]) { + // if the row did not use this, see if selection can use it + MouseButtons mouse = MouseButtons; + if (lastRowSelected != -1 && (mouse & MouseButtons.Left) == MouseButtons.Left + && !(((Control.ModifierKeys & Keys.Control) == Keys.Control) && (Control.ModifierKeys & Keys.Alt) == 0)){ + // ResetSelection() will reset the lastRowSelected too + // + int saveLastRowSelected = lastRowSelected; + ResetSelection(); + lastRowSelected = saveLastRowSelected; + + int lowerRow = Math.Min(lastRowSelected, rowOver); + int upperRow = Math.Max(lastRowSelected, rowOver); + + DataGridRow[] rows = DataGridRows; + for (int i = lowerRow; i <= upperRow; i++) + { + rows[i].Selected = true; + numSelectedRows ++; + } + } + } + } + + if (oldRow != rowOver && oldRow != -1) { + localGridRows[oldRow].OnMouseLeft(layout.RowHeaders, alignToRight); + } + oldRow = rowOver; + } + + // check parentRows + // + if (location.type == HitTestType.ParentRows) + { + if (parentRows != null) + { + parentRows.OnMouseMove(e.X, e.Y); + } + } + + if (location.type == HitTestType.Caption) { + gridState[GRIDSTATE_overCaption] = true; + Rectangle captionRect = layout.Caption; + caption.MouseOver(e.X - captionRect.X, e.Y - captionRect.Y); + return; + } + else { + if (gridState[GRIDSTATE_overCaption]) { + gridState[GRIDSTATE_overCaption] = false; + caption.MouseLeft(); + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseUp(MouseEventArgs e) { + base.OnMouseUp(e); + gridState[GRIDSTATE_dragging] = false; + if (listManager == null || myGridTable == null) + return; + if (gridState[GRIDSTATE_trackColResize]) { + ColResizeEnd(e); + } + + if (gridState[GRIDSTATE_trackRowResize]) + { + RowResizeEnd(e); + } + + gridState[GRIDSTATE_trackColResize] = false; + gridState[GRIDSTATE_trackRowResize] = false; + + HitTestInfo ci = HitTest(e.X, e.Y); + if ((ci.type & HitTestType.Caption) == HitTestType.Caption) { + caption.MouseUp(e.X, e.Y); + } + + // Check column headers + if (ci.type == HitTestType.ColumnHeader) { + PropertyDescriptor prop = this.myGridTable.GridColumnStyles[ci.col].PropertyDescriptor; + if (prop == trackColumnHeader) { + ColumnHeaderClicked(trackColumnHeader); + } + } + + trackColumnHeader = null; + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseWheel(MouseEventArgs e) { + base.OnMouseWheel(e); + + if (e is HandledMouseEventArgs) { + if (((HandledMouseEventArgs) e).Handled) { + // The application event handler handled the scrolling - don't do anything more. + return; + } + ((HandledMouseEventArgs) e).Handled = true; + } + + bool wheelingDown = true; + if ((ModifierKeys & Keys.Control) != 0) + wheelingDown = false; + + if (listManager == null || myGridTable == null) + return; + ScrollBar sb = wheelingDown ? vertScrollBar : horizScrollBar; + if (!sb.Visible) + return; + + // so we scroll. we have to know this, cause otherwise we will call EndEdit + // and that would be wrong. + gridState[GRIDSTATE_isScrolling] = true; + wheelDelta += e.Delta; + float movePerc = (float)wheelDelta / (float)NativeMethods.WHEEL_DELTA; + int move = (int)((float)SystemInformation.MouseWheelScrollLines * movePerc); + if (move != 0) { + wheelDelta = 0; + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnMouseWheel move="+move.ToString(CultureInfo.InvariantCulture)); + if (wheelingDown) { + int newRow = firstVisibleRow - move; + newRow = Math.Max(0, Math.Min(newRow, DataGridRowsLength - numTotallyVisibleRows)); + ScrollDown(newRow - firstVisibleRow); + } + else { + int newValue = horizScrollBar.Value + (move < 0 ? 1 : -1) * horizScrollBar.LargeChange; + HorizontalOffset = newValue; + } + } + + gridState[GRIDSTATE_isScrolling] = false; + } + + /// + /// + /// Raises the + /// event. + /// + protected override void OnPaint(PaintEventArgs pe) { + try { + CheckHierarchyState(); + + if (this.layout.dirty) + ComputeLayout(); + + Graphics g = pe.Graphics; + + Region clipRegion = g.Clip; + if (layout.CaptionVisible) + caption.Paint(g, layout.Caption, isRightToLeft()); + + if (layout.ParentRowsVisible) { + Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Painting ParentRows " + layout.ParentRows.ToString()); + g.FillRectangle(SystemBrushes.AppWorkspace, layout.ParentRows); + parentRows.Paint(g, layout.ParentRows, isRightToLeft()); + } + + Rectangle gridRect = layout.Data; + if (layout.RowHeadersVisible) + gridRect = Rectangle.Union(gridRect, layout.RowHeaders); + if (layout.ColumnHeadersVisible) + gridRect = Rectangle.Union(gridRect, layout.ColumnHeaders); + + g.SetClip(gridRect); + PaintGrid(g, gridRect); + g.Clip = clipRegion; + clipRegion.Dispose(); + PaintBorder(g, layout.ClientRectangle); + + g.FillRectangle(DefaultHeaderBackBrush, layout.ResizeBoxRect); + + base.OnPaint(pe); // raise paint event + } + catch { + // at runtime we will rethrow the exception + if (this.Site == null || !this.Site.DesignMode) + throw; + gridState[GRIDSTATE_exceptionInPaint] = true; + try { + RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridExceptionInPaint), null, MessageBoxButtons.OK, + MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + + if (this.Visible) BeginUpdateInternal(); + + ResetParentRows(); + + Set_ListManager(null, String.Empty, true); + } finally { + gridState[GRIDSTATE_exceptionInPaint] = false; + if (this.Visible) EndUpdateInternal(); + } + } + } + + // Since Win32 only invalidates the area that gets uncovered, + // we have to manually invalidate the old border area + /// + /// + /// Raises the event. + /// + protected override void OnResize(EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnResize"); + + if (layout.CaptionVisible) + Invalidate(layout.Caption); + if (layout.ParentRowsVisible) + parentRows.OnResize(layout.ParentRows); + + int borderWidth = BorderWidth; + Rectangle right; + Rectangle bottom; + Rectangle oldClientRectangle = layout.ClientRectangle; + + right = new Rectangle(oldClientRectangle.X + oldClientRectangle.Width - borderWidth, + oldClientRectangle.Y, + borderWidth, + oldClientRectangle.Height); + bottom = new Rectangle(oldClientRectangle.X, + oldClientRectangle.Y + oldClientRectangle.Height - borderWidth, + oldClientRectangle.Width, + borderWidth); + + Rectangle newClientRectangle = this.ClientRectangle; + if (newClientRectangle.Width != oldClientRectangle.Width) { + Invalidate(right); + right = new Rectangle(newClientRectangle.X + newClientRectangle.Width - borderWidth, + newClientRectangle.Y, + borderWidth, + newClientRectangle.Height); + Invalidate(right); + } + if (newClientRectangle.Height != oldClientRectangle.Height) { + Invalidate(bottom); + bottom = new Rectangle(newClientRectangle.X, + newClientRectangle.Y + newClientRectangle.Height - borderWidth, + newClientRectangle.Width, + borderWidth); + Invalidate(bottom); + } + + //also, invalidate the ResizeBoxRect + if (!this.layout.ResizeBoxRect.IsEmpty) + Invalidate(layout.ResizeBoxRect); + + layout.ClientRectangle = newClientRectangle; + + int oldFirstVisibleRow = firstVisibleRow; + base.OnResize(e); + if (isRightToLeft() || oldFirstVisibleRow != firstVisibleRow) + Invalidate(); + } + + internal void OnRowHeightChanged(DataGridRow row) { + ClearRegionCache(); + int cy = GetRowTop(row.RowNumber); + if (cy > 0) { + Rectangle refresh = new Rectangle(); + refresh.Y = cy; + refresh.X = layout.Inside.X; + refresh.Width = layout.Inside.Width; + refresh.Height = layout.Inside.Bottom - cy; + Invalidate(refresh); + } + } + + internal void ParentRowsDataChanged() { + Debug.Assert(originalState != null, "how can we get a list changed event from another listmanager/list while not navigating"); + + // do the reset work that is done in SetDataBindings, set_DataSource, set_DataMember; + parentRows.Clear(); + caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false; + caption.SetDownButtonDirection(!layout.ParentRowsVisible); + object dSource = originalState.DataSource; + string dMember = originalState.DataMember; + // we don't need to set the GRIDSTATE_metaDataChanged bit, cause + // the listManager from the originalState should be different from the current listManager + // + // set the originalState to null so that Set_ListManager knows that + // it has to unhook the MetaDataChanged events + originalState = null; + Set_ListManager(dSource, dMember, true); + } + + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + private void AbortEdit() { + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: \t! AbortEdit"); + Debug.Assert(gridState[GRIDSTATE_isEditing], "Can't abort an edit that is not happening!"); + + // the same rules from editColumn.OnEdit + // while changing the editControl's visibility, do not + // PerformLayout on the entire DataGrid + gridState[GRIDSTATE_editControlChanging] = true; + + editColumn.Abort(editRow.RowNumber); + + // reset the editControl flag: + gridState[GRIDSTATE_editControlChanging] = false; + + gridState[GRIDSTATE_isEditing] = false; + editRow = null; + editColumn = null; + } + + /// + /// + /// Occurs when the user navigates to a new table. + /// + [SRCategory(SR.CatAction), SRDescription(SR.DataGridNavigateEventDescr)] + public event NavigateEventHandler Navigate { + add { + onNavigate += value; + } + remove { + onNavigate -= value; + } + } + + /// + /// + /// Occurs when a row header is clicked. + /// + protected event EventHandler RowHeaderClick { + add { + onRowHeaderClick += value; + } + remove { + onRowHeaderClick -= value; + } + } + + /// + /// + /// Adds an event handler for the 'System.Windows.Forms.DataGrid.OnNodeClick' + /// event. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.DataGridNodeClickEventDescr)] + internal event EventHandler NodeClick { + add { + Events.AddHandler(EVENT_NODECLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_NODECLICKED, value); + } + } + + /// + /// + /// + /// Occurs when the user scrolls the control. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.DataGridScrollEventDescr)] + public event EventHandler Scroll { + add { + Events.AddHandler(EVENT_SCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_SCROLL, value); + } + } + + /// + public override ISite Site { + get { + return base.Site; + } + set { + ISite temp = this.Site; + base.Site = value; + if (value != temp && !Disposing) { + // we should site the tables and the columns + // only when our site changes + SubObjectsSiteChange(false); + SubObjectsSiteChange(true); + } + } + } + + internal void AddNewRow() { + EnsureBound(); + ResetSelection(); + // EndEdit(); + UpdateListManager(); + gridState[GRIDSTATE_inListAddNew] = true; + gridState[GRIDSTATE_inAddNewRow] = true; + try { + this.ListManager.AddNew(); + } catch { + gridState[GRIDSTATE_inListAddNew] = false; + gridState[GRIDSTATE_inAddNewRow] = false; + PerformLayout(); + InvalidateInside(); + throw; + } + gridState[GRIDSTATE_inListAddNew] = false; + } + + /// + /// + /// Attempts to + /// put the grid into a state where editing is + /// allowed. + /// + public bool BeginEdit(DataGridColumnStyle gridColumn, int rowNumber) { + if (this.DataSource == null || this.myGridTable == null) + return false; + + // We deny edit requests if we are already editing a cell. + if (gridState[GRIDSTATE_isEditing]) + return false; + else { + int col = -1; + if ((col = this.myGridTable.GridColumnStyles.IndexOf(gridColumn)) < 0) + return false; + CurrentCell = new DataGridCell(rowNumber, col); + ResetSelection(); + Edit(); + return true; + } + } + + /// + /// + /// Specifies the beginning of the initialization code. + /// + public void BeginInit() { + if (inInit) + throw new InvalidOperationException(SR.GetString(SR.DataGridBeginInit)); + inInit = true; + } + + private Rectangle CalcRowResizeFeedbackRect(MouseEventArgs e) { + Rectangle inside = layout.Data; + Rectangle r = new Rectangle(inside.X, e.Y, inside.Width, 3); + r.Y = Math.Min(inside.Bottom - 3, r.Y); + r.Y = Math.Max(r.Y , 0); + return r; + } + + private Rectangle CalcColResizeFeedbackRect(MouseEventArgs e) { + Rectangle inside = layout.Data; + Rectangle r = new Rectangle(e.X, inside.Y, 3, inside.Height); + r.X = Math.Min(inside.Right - 3, r.X); + r.X = Math.Max(r.X , 0); + return r; + } + + private void CancelCursorUpdate() { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: Requesting CancelEdit()"); + if (listManager != null) { + EndEdit(); + listManager.CancelCurrentEdit(); + } + } + + private void CheckHierarchyState() { + if (checkHierarchy && this.listManager != null && this.myGridTable != null) { + if (myGridTable == null) + // there was nothing to check + return; + + for (int j = 0; j < myGridTable.GridColumnStyles.Count; j++) { + DataGridColumnStyle gridColumn = myGridTable.GridColumnStyles[j]; + } + + checkHierarchy = false; + } + } + + /// + /// The DataGrid caches an array of rectangular areas + /// which represent the area which scrolls left to right. + /// This method is invoked whenever the DataGrid's + /// scrollable regions change in such a way as to require + /// a re-recalculation. + /// + private void ClearRegionCache() { + cachedScrollableRegion = null; + } + + /// + /// Determines the best fit size for the given column. + /// + private void ColAutoResize(int col) { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColAutoResize"); + EndEdit(); + CurrencyManager listManager = this.listManager; + if (listManager == null) + return; + + int size; + Graphics g = CreateGraphicsInternal(); + try { + DataGridColumnStyle column = myGridTable.GridColumnStyles[col]; + string columnName = column.HeaderText; + + Font headerFont; + if (this.myGridTable.IsDefault) + headerFont = this.HeaderFont; + else + headerFont = this.myGridTable.HeaderFont; + size = (int) g.MeasureString(columnName, headerFont).Width + layout.ColumnHeaders.Height + 1; // This is not a bug, the sort triangle's width is equal to it's height. + int rowCount = listManager.Count; + for (int row = 0; row < rowCount; ++row) { + object value = column.GetColumnValueAtRow(listManager, row); + int width = column.GetPreferredSize(g, value).Width; + if (width > size) + size = width; + } + + if (column.Width != size) { + column.width = size; + + ComputeVisibleColumns(); + + bool lastColumnIsLastTotallyVisibleCol = true; + if (this.lastTotallyVisibleCol != -1) { + for (int i = this.lastTotallyVisibleCol + 1; i < this.myGridTable.GridColumnStyles.Count; i ++) { + if (this.myGridTable.GridColumnStyles[i].PropertyDescriptor != null) { + lastColumnIsLastTotallyVisibleCol = false; + break; + } + } + } else { + lastColumnIsLastTotallyVisibleCol = false; + } + + // if the column shrank and the last totally visible column was the last column + // then we need to recompute the horizontalOffset, firstVisibleCol, negOffset. + // lastTotallyVisibleCol remains the last column + if (lastColumnIsLastTotallyVisibleCol && + (this.negOffset != 0 || this.horizontalOffset != 0)) { + + // update the column width + column.width = size; + + int cx = 0; + int colCount = this.myGridTable.GridColumnStyles.Count; + int visibleWidth = layout.Data.Width; + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + + // assume everything fits + this.negOffset = 0; + this.horizontalOffset = 0; + this.firstVisibleCol= 0; + + for (int i = colCount - 1; i >= 0; i --) { + if (cols[i].PropertyDescriptor == null) { + continue; + } + + cx += cols[i].Width; + if (cx > visibleWidth) { + if (this.negOffset == 0) { + this.firstVisibleCol = i; + this.negOffset = cx - visibleWidth; + this.horizontalOffset = this.negOffset; + this.numVisibleCols ++; + } else { + this.horizontalOffset += cols[i].Width; + } + } else { + this.numVisibleCols ++; + } + } + + // refresh the horizontal scrollbar + PerformLayout(); + + // we need to invalidate the layout.Data and layout.ColumnHeaders + Invalidate(Rectangle.Union(layout.Data, layout.ColumnHeaders)); + } else { + // need to refresh the scroll bar + PerformLayout(); + + Rectangle rightArea = layout.Data; + if (layout.ColumnHeadersVisible) + rightArea = Rectangle.Union(rightArea, layout.ColumnHeaders); + + int left = GetColBeg(col); + + if (!isRightToLeft()) { + rightArea.Width -= left - rightArea.X; + rightArea.X = left; + } else { + rightArea.Width = rightArea.Width - left; + } + + Invalidate(rightArea); + } + } + } + finally { + g.Dispose(); + } + + if (this.horizScrollBar.Visible) { + this.horizScrollBar.Value = HorizontalOffset; + } + // OnColumnResize(EventArgs.Empty); + } + + /// + /// + /// + /// Collapses child relations, if any exist for all rows, or for a + /// specified row. + /// + /// + public void Collapse(int row) { + SetRowExpansionState(row, false); + } + + private void ColResizeBegin(MouseEventArgs e, int col) { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColResizeBegin"); + Debug.Assert(myGridTable != null, "Column resizing operations can't be called when myGridTable == null."); + + int x = e.X; + EndEdit(); + Rectangle clip = Rectangle.Union(layout.ColumnHeaders, layout.Data); + if (isRightToLeft()) + { + clip.Width = GetColBeg(col) - layout.Data.X - 2; + } + else + { + int leftEdge = GetColBeg(col); + clip.X = leftEdge + 3; + clip.Width = layout.Data.X + layout.Data.Width - leftEdge - 2; + } + + CaptureInternal = true; + Cursor.ClipInternal = RectangleToScreen(clip); + gridState[GRIDSTATE_trackColResize] = true; + trackColAnchor = x; + trackColumn = col; + + DrawColSplitBar(e); + lastSplitBar = e; + } + + private void ColResizeMove(MouseEventArgs e) { + if (lastSplitBar != null) { + DrawColSplitBar(lastSplitBar); + lastSplitBar = e; + } + DrawColSplitBar(e); + } + + private void ColResizeEnd(MouseEventArgs e) { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColResizeEnd"); + Debug.Assert(myGridTable != null, "Column resizing operations can't be called when myGridTable == null."); + + this.gridState[GRIDSTATE_layoutSuspended] = true; + try { + if (lastSplitBar != null) { + DrawColSplitBar(lastSplitBar); + lastSplitBar = null; + } + + bool rightToLeft = isRightToLeft(); + + int x = rightToLeft ? Math.Max(e.X, layout.Data.X) : Math.Min(e.X, layout.Data.Right + 1); + int delta = x - GetColEnd(trackColumn); + if (rightToLeft) delta = -delta; + + if (trackColAnchor != x && delta != 0) { + DataGridColumnStyle column = myGridTable.GridColumnStyles[trackColumn]; + int proposed = column.Width + delta; + proposed = Math.Max(proposed, 3); + column.Width = proposed; + + // refresh scrolling data: horizontalOffset, negOffset, firstVisibleCol, numVisibleCols, lastTotallyVisibleCol + ComputeVisibleColumns(); + + bool lastColumnIsLastTotallyVisibleCol = true; + for (int i = this.lastTotallyVisibleCol + 1; i < this.myGridTable.GridColumnStyles.Count; i ++) { + if (this.myGridTable.GridColumnStyles[i].PropertyDescriptor != null) { + lastColumnIsLastTotallyVisibleCol = false; + break; + } + } + + if (lastColumnIsLastTotallyVisibleCol && + (this.negOffset != 0 || this.horizontalOffset != 0)) { + + int cx = 0; + int colCount = this.myGridTable.GridColumnStyles.Count; + int visibleWidth = this.layout.Data.Width; + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + + // assume everything fits + this.negOffset = 0; + this.horizontalOffset = 0; + this.firstVisibleCol = 0; + + for (int i = colCount - 1; i > -1; i --) { + if (cols[i].PropertyDescriptor == null) { + continue; + } + + cx += cols[i].Width; + + if (cx > visibleWidth) { + if (this.negOffset == 0) { + this.negOffset = cx - visibleWidth; + this.firstVisibleCol = i; + this.horizontalOffset = negOffset; + this.numVisibleCols ++; + } else { + this.horizontalOffset += cols[i].Width; + } + } else { + this.numVisibleCols ++; + } + } + + // and invalidate pretty much everything + Invalidate(Rectangle.Union(this.layout.Data, this.layout.ColumnHeaders)); + } else { + + Rectangle rightArea = Rectangle.Union(layout.ColumnHeaders, layout.Data); + int left = GetColBeg(trackColumn); + rightArea.Width -= rightToLeft ? rightArea.Right - left : left - rightArea.X; + rightArea.X = rightToLeft ? layout.Data.X : left; + Invalidate(rightArea); + } + } + } + finally { + Cursor.ClipInternal = Rectangle.Empty; + CaptureInternal = false; + this.gridState[GRIDSTATE_layoutSuspended] = false; + } + + PerformLayout(); + + if (this.horizScrollBar.Visible) { + this.horizScrollBar.Value = HorizontalOffset; + } + // OnColumnResize(EventArgs.Empty); + } + + private void MetaDataChanged() { + // when we reset the Binding in the grid, we need to clear the parent rows. + // the same goes for all the caption UI: reset it when the datasource changes. + // + parentRows.Clear(); + caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false; + caption.SetDownButtonDirection(!layout.ParentRowsVisible); + + gridState[GRIDSTATE_metaDataChanged] = true; + try { + if (originalState != null) { + // set the originalState to null so that Set_ListManager knows that + // it has to unhook the MetaDataChanged events + Set_ListManager(originalState.DataSource, originalState.DataMember, true); + originalState = null; + } else { + Set_ListManager(this.DataSource, this.DataMember, true); + } + } finally { + gridState[GRIDSTATE_metaDataChanged] = false; + } + } + + + // =------------------------------------------------------------------ + // = Functions to resize rows + // =------------------------------------------------------------------ + + // will autoResize "row" + private void RowAutoResize(int row) + { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowAutoResize"); + + EndEdit(); + CurrencyManager listManager = this.ListManager; + if (listManager == null) + return; + + Graphics g = CreateGraphicsInternal(); + try + { + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + DataGridRow resizeRow = DataGridRows[row]; + int rowCount = listManager.Count; + int resizeHeight = 0; + + // compute the height that we should resize to: + int columnsCount = columns.Count; + for (int col = 0; col < columnsCount; col++) { + object value = columns[col].GetColumnValueAtRow(listManager, row); + resizeHeight = Math.Max(resizeHeight, columns[col].GetPreferredHeight(g, value)); + } + + if (resizeRow.Height != resizeHeight) + { + resizeRow.Height = resizeHeight; + + // needed to refresh scrollbar properties + PerformLayout(); + + Rectangle rightArea = layout.Data; + if (layout.RowHeadersVisible) + rightArea = Rectangle.Union(rightArea, layout.RowHeaders); + int top = GetRowTop(row); + rightArea.Height -= rightArea.Y - top; + rightArea.Y = top; + Invalidate(rightArea); + } + } + finally + { + g.Dispose(); + } + + // OnRowResize(EventArgs.Empty); + return; + } + + + + private void RowResizeBegin(MouseEventArgs e, int row) + { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowResizeBegin"); + Debug.Assert(myGridTable != null, "Row resizing operations can't be called when myGridTable == null."); + + int y = e.Y; + EndEdit(); + Rectangle clip = Rectangle.Union(layout.RowHeaders, layout.Data); + int topEdge = GetRowTop(row); + clip.Y = topEdge + 3; + clip.Height = layout.Data.Y + layout.Data.Height - topEdge - 2; + + CaptureInternal = true; + Cursor.ClipInternal = RectangleToScreen(clip); + gridState[GRIDSTATE_trackRowResize] = true; + trackRowAnchor = y; + trackRow = row; + + DrawRowSplitBar(e); + lastSplitBar = e; + } + + private void RowResizeMove(MouseEventArgs e) + { + if (lastSplitBar != null) { + DrawRowSplitBar(lastSplitBar); + lastSplitBar = e; + } + DrawRowSplitBar(e); + } + + private void RowResizeEnd(MouseEventArgs e) + { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowResizeEnd"); + Debug.Assert(myGridTable != null, "Row resizing operations can't be called when myGridTable == null."); + + try { + if (lastSplitBar != null) { + DrawRowSplitBar(lastSplitBar); + lastSplitBar = null; + } + + int y = Math.Min(e.Y, layout.Data.Y + layout.Data.Height + 1); + int delta = y - GetRowBottom(trackRow); + if (trackRowAnchor != y && delta != 0) { + DataGridRow row = DataGridRows[trackRow]; + int proposed = row.Height + delta; + proposed = Math.Max(proposed, 3); + row.Height = proposed; + + + // needed to refresh scrollbar properties + PerformLayout(); + + Rectangle rightArea = Rectangle.Union(layout.RowHeaders, layout.Data); + int top = GetRowTop(trackRow); + rightArea.Height -= rightArea.Y - top; + rightArea.Y = top; + Invalidate(rightArea); + } + } + finally { + Cursor.ClipInternal = Rectangle.Empty; + CaptureInternal = false; + } + // OnRowResize(EventArgs.Empty); + } + + /// + /// Fires the ColumnHeaderClicked event and handles column + /// sorting. + /// + private void ColumnHeaderClicked(PropertyDescriptor prop) { + if (!CommitEdit()) + return; + + // OnColumnHeaderClick(EventArgs.Empty); + bool allowSorting; + if (this.myGridTable.IsDefault) + allowSorting = this.AllowSorting; + else + allowSorting = myGridTable.AllowSorting; + + if (!allowSorting) + return; + + // if (CompModSwitches.DataGridCursor.OutputVerbose) Debug.WriteLine("DataGridCursor: We are about to sort column " + col.ToString()); + ListSortDirection direction = this.ListManager.GetSortDirection(); + PropertyDescriptor sortColumn = this.ListManager.GetSortProperty(); + if (sortColumn != null && sortColumn.Equals(prop)) + direction = (direction == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending; + else + // defaultSortDirection : ascending + direction = ListSortDirection.Ascending; + + if (listManager.Count == 0) + return; + + this.ListManager.SetSort(prop, direction); + ResetSelection(); + + InvalidateInside(); + } + + /// + /// Attempts to commit editing if a cell is being edited. + /// Return true if successfully commited editing. + /// Return false if editing can not be completed and the gird must + /// remain in our current Edit state. + /// + private bool CommitEdit() { + + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: \t CommitEdit " + (editRow == null ? "" : editRow.RowNumber.ToString(CultureInfo.InvariantCulture))); + + + // we want to commit the editing if + // 1. the user was editing or navigating around the data grid and + // 2. this is not the result of moving focus inside the data grid and + // 3. if the user was scrolling + if (!gridState[GRIDSTATE_isEditing] && !gridState[GRIDSTATE_isNavigating] || (gridState[GRIDSTATE_editControlChanging] && !gridState[GRIDSTATE_isScrolling])) + return true; + + // the same rules from editColumn.OnEdit + // flag that we are editing the Edit control, so if we get a OnLayout on the + // datagrid side of things while the edit control changes its visibility and bounds + // the datagrid does not perform a layout + gridState[GRIDSTATE_editControlChanging] = true; + + if (editColumn.ReadOnly || gridState[GRIDSTATE_inAddNewRow]) { + bool focusTheGrid = false; + if (this.ContainsFocus) { + focusTheGrid = true; + } + + if (focusTheGrid && gridState[GRIDSTATE_canFocus]) + this.FocusInternal(); + editColumn.ConcedeFocus(); + + // set the focus back to the grid + if (focusTheGrid && gridState[GRIDSTATE_canFocus] && CanFocus && !Focused) + this.FocusInternal(); + + // reset the editControl flag + gridState[GRIDSTATE_editControlChanging] = false; + return true; + } + + bool retVal = editColumn.Commit(ListManager, currentRow); + + // reset the editControl flag + gridState[GRIDSTATE_editControlChanging] = false; + + if (retVal) + gridState[GRIDSTATE_isEditing] = false; + + return retVal; + } + + /// + /// Figure out how many rows we need to scroll down + /// to move targetRow into visibility. + /// + private int ComputeDeltaRows(int targetRow) { + //Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeDeltaRows, firstVisibleRow = " + // + firstVisibleRow.ToString() + ", " + // + "targetRow = " + targetRow.ToString()); + + if (firstVisibleRow == targetRow) + return 0; + + int dRows = 0; + int firstVisibleRowLogicalTop = -1; + int targetRowLogicalTop = -1; + int nRows = DataGridRowsLength; + int cy = 0; + DataGridRow[] localGridRows = DataGridRows; + + for (int row = 0; row < nRows; ++row) { + if (row == firstVisibleRow) + firstVisibleRowLogicalTop = cy; + if (row == targetRow) + targetRowLogicalTop = cy; + if (targetRowLogicalTop != -1 && firstVisibleRowLogicalTop != -1) + break; + cy += localGridRows[row].Height; + } + + int targetRowLogicalBottom = targetRowLogicalTop + localGridRows[targetRow].Height; + int dataLogicalBottom = layout.Data.Height + firstVisibleRowLogicalTop; + if (targetRowLogicalBottom > dataLogicalBottom) { + // we need to move down. + int downDelta = targetRowLogicalBottom - dataLogicalBottom; + firstVisibleRowLogicalTop += downDelta; + } + else if (firstVisibleRowLogicalTop < targetRowLogicalTop) { + // we don't need to move + return 0; + } + else { + // we need to move up. + int upDelta = firstVisibleRowLogicalTop - targetRowLogicalTop; + firstVisibleRowLogicalTop -= upDelta; + } + int newFirstRow = ComputeFirstVisibleRow(firstVisibleRowLogicalTop); + dRows = (newFirstRow - firstVisibleRow); + //Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeDeltaRows returning " + dRows.ToString()); + return dRows; + } + + /// + /// Given the a logical vertical offset, figure out + /// which row number should be the first fully visible + /// row on or after the offset. + /// + private int ComputeFirstVisibleRow(int firstVisibleRowLogicalTop) { + int first; + int nRows = DataGridRowsLength; + int cy = 0; + DataGridRow[] localGridRows = DataGridRows; + for (first = 0; first < nRows; ++first) { + if (cy >= firstVisibleRowLogicalTop) + break; + cy += localGridRows[first].Height; + } + return first; + } + + /// + /// Constructs an updated Layout object. + /// + private void ComputeLayout() { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeLayout"); + + bool alignLeft = ! isRightToLeft(); + Rectangle oldResizeRect = this.layout.ResizeBoxRect; + + // hide the EditBox + EndEdit(); + + ClearRegionCache(); + + // NOTE : Since Rectangles are structs, then assignment is a + // : copy. Therefore, after saying "Rectangle inside = newLayout.Inside", + // : we must always assign back to newLayout.Inside. + // + + // Important since all of the visibility flags will move + // to the new layout here. + LayoutData newLayout = new LayoutData(layout); + + // Inside + newLayout.Inside = ClientRectangle; + Rectangle inside = newLayout.Inside; + int borderWidth = BorderWidth; + inside.Inflate(-borderWidth, -borderWidth); + + Rectangle insideLeft = inside; + + // Caption + if (layout.CaptionVisible) { + int captionHeight = captionFontHeight + 6; + Rectangle cap = newLayout.Caption; + cap = insideLeft; + cap.Height = captionHeight; + insideLeft.Y += captionHeight; + insideLeft.Height -= captionHeight; + + newLayout.Caption = cap; + } + else { + newLayout.Caption = Rectangle.Empty; + } + + // Parent Rows + if (layout.ParentRowsVisible) { + Rectangle parents = newLayout.ParentRows; + int parentHeight = parentRows.Height; + parents = insideLeft; + parents.Height = parentHeight; + insideLeft.Y += parentHeight; + insideLeft.Height -= parentHeight; + + newLayout.ParentRows = parents; + } + else { + newLayout.ParentRows = Rectangle.Empty; + } + + // Headers + // + int columnHeaderHeight = headerFontHeight + 6; + if (layout.ColumnHeadersVisible) { + Rectangle colHeaders = newLayout.ColumnHeaders; + colHeaders = insideLeft; + colHeaders.Height = columnHeaderHeight; + insideLeft.Y += columnHeaderHeight; + insideLeft.Height -= columnHeaderHeight; + + newLayout.ColumnHeaders = colHeaders; + } + else { + newLayout.ColumnHeaders = Rectangle.Empty; + } + + bool newRowHeadersVisible = this.myGridTable.IsDefault ? this.RowHeadersVisible : this.myGridTable.RowHeadersVisible; + int newRowHeaderWidth = this.myGridTable.IsDefault ? this.RowHeaderWidth : this.myGridTable.RowHeaderWidth; + newLayout.RowHeadersVisible = newRowHeadersVisible; + if (this.myGridTable != null && newRowHeadersVisible) { + Rectangle rowHeaders = newLayout.RowHeaders; + if (alignLeft) + { + rowHeaders = insideLeft; + rowHeaders.Width = newRowHeaderWidth; + insideLeft.X += newRowHeaderWidth; + insideLeft.Width -= newRowHeaderWidth; + } + else + { + rowHeaders = insideLeft; + rowHeaders.Width = newRowHeaderWidth; + rowHeaders.X = insideLeft.Right - newRowHeaderWidth; + insideLeft.Width -= newRowHeaderWidth; + } + newLayout.RowHeaders = rowHeaders; + + if (layout.ColumnHeadersVisible) { + Rectangle topLeft = newLayout.TopLeftHeader; + Rectangle colHeaders = newLayout.ColumnHeaders; + if (alignLeft) + { + topLeft = colHeaders; + topLeft.Width = newRowHeaderWidth; + colHeaders.Width -= newRowHeaderWidth; + colHeaders.X += newRowHeaderWidth; + } + else + { + topLeft = colHeaders; + topLeft.Width = newRowHeaderWidth; + topLeft.X = colHeaders.Right - newRowHeaderWidth; + colHeaders.Width -= newRowHeaderWidth; + } + + newLayout.TopLeftHeader = topLeft; + newLayout.ColumnHeaders = colHeaders; + } + else { + newLayout.TopLeftHeader = Rectangle.Empty; + } + } + else { + newLayout.RowHeaders = Rectangle.Empty; + newLayout.TopLeftHeader = Rectangle.Empty; + } + + // The Data region + newLayout.Data = insideLeft; + newLayout.Inside = inside; + + this.layout = newLayout; + + LayoutScrollBars(); + + // if the user shrank the grid client area, then OnResize invalidated the old + // resize area. however, we need to invalidate the left upper corner in the new ResizeArea + // note that we can't take the Invalidate call from the OnResize method, because if the + // user enlarges the form then the old area will not be invalidated. + // + if (!oldResizeRect.Equals(this.layout.ResizeBoxRect) && !this.layout.ResizeBoxRect.IsEmpty) + Invalidate(this.layout.ResizeBoxRect); + + this.layout.dirty = false; + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: " + layout.ToString()); + } + + /// + /// Computes the number of pixels to scroll to scroll from one + /// row to another. + /// + private int ComputeRowDelta(int from, int to) { + int first = from; + int last = to; + int sign = -1; + if (first > last) { + first = to; + last = from; + sign = 1; + } + DataGridRow[] localGridRows = DataGridRows; + int delta = 0; + for (int row = first; row < last; ++row) { + delta += localGridRows[row].Height; + } + return sign * delta; + } + + internal int MinimumRowHeaderWidth() { + return minRowHeaderWidth; + } + + internal void ComputeMinimumRowHeaderWidth() { + minRowHeaderWidth = errorRowBitmapWidth; // the size of the pencil, star and row selector images are the same as the image for the error bitmap + if (this.ListHasErrors) + minRowHeaderWidth += errorRowBitmapWidth; + if (this.myGridTable != null && this.myGridTable.RelationsList.Count != 0) + minRowHeaderWidth += 15; // the size of the plus/minus glyph and spacing around it + } + + /// + /// + /// Updates the internal variables with the number of columns visible + /// inside the Grid's client rectangle. + /// + private void ComputeVisibleColumns() { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeVisibleColumns"); + EnsureBound(); + + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + + int nGridCols = columns.Count; + int cx = - negOffset; + int visibleColumns = 0; + int visibleWidth = layout.Data.Width; + int curCol = firstVisibleCol; + + // the same problem with negative numbers: + // if the width passed in is negative, then return 0 + // + // added the check for the columns.Count == 0 ( Microsoft, November 14, 2000) + // + if (visibleWidth < 0 || columns.Count == 0) + { + numVisibleCols = firstVisibleCol = 0; + lastTotallyVisibleCol = -1; + return; + } + + while (cx < visibleWidth && curCol < nGridCols) + { + // if (columns.Visible && columns.PropertyDescriptor != null) + if (columns[curCol].PropertyDescriptor != null) + cx += columns[curCol].Width; + curCol++; + visibleColumns ++; + } + + numVisibleCols = visibleColumns; + + // if we inflate the data area + // then we paint columns to the left of firstVisibleColumn + if (cx < visibleWidth) + { + for (int i = firstVisibleCol -1; i > 0; i--) + { + if (cx + columns[i].Width > visibleWidth) + break; + // if (columns.Visible && columns.PropertyDescriptor != null) + if (columns[i].PropertyDescriptor != null) + cx += columns[i].Width; + visibleColumns ++; + firstVisibleCol --; + } + + if (numVisibleCols != visibleColumns) + { + Debug.Assert(numVisibleCols < visibleColumns, "the number of visible columns can only grow"); + // is there space for more columns than were visible? + // if so, then we need to repaint Data and ColumnHeaders + Invalidate(layout.Data); + Invalidate(layout.ColumnHeaders); + + // update the number of visible columns to the new reality + numVisibleCols = visibleColumns; + } + } + + lastTotallyVisibleCol = firstVisibleCol + numVisibleCols - 1; + if (cx > visibleWidth) { + if (numVisibleCols <= 1 || (numVisibleCols == 2 && this.negOffset != 0)) { + // no column is entirely visible + lastTotallyVisibleCol = -1; + } else { + lastTotallyVisibleCol--; + } + } + } + + /// + /// Determines which column is the first visible given + /// the object's horizontalOffset. + /// + private int ComputeFirstVisibleColumn() { + int first = 0; + if (this.horizontalOffset == 0) { + negOffset = 0; + return 0; + } + + // we will check to see if myGridTables.GridColumns.Count != 0 + // because when we reset the dataGridTable, we don't have any columns, and we still + // call HorizontalOffset = 0, and that will call ComputeFirstVisibleColumn with an empty collection of columns. + if (myGridTable != null && myGridTable.GridColumnStyles != null && myGridTable.GridColumnStyles.Count != 0) { + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + int cx = 0; + int nGridCols = columns.Count; + + if (columns[0].Width == -1) { + // the columns are not initialized yet + // + #if DEBUG + for (int i = 0; i < nGridCols; i++) { + Debug.Assert(columns[i].Width == -1, "the columns' widths should not be initialized"); + } + #endif // DEBUG + negOffset = 0; + return 0; + } + + for (first = 0; first < nGridCols; first++) { + // if (columns[first].Visible && columns[first].PropertyDescriptor != null); + if (columns[first].PropertyDescriptor != null) + cx += columns[first].Width; + if (cx > horizontalOffset) + break; + } + // first may actually be the number of columns + // in that case all the columns fit in the layout data + if (first == nGridCols) { + Debug.Assert(cx <= horizontalOffset, "look at the for loop before: we only exit that loop early if the cx is over the horizontal offset"); + negOffset = 0; + return 0; + } + negOffset = columns[first].Width - (cx - horizontalOffset); + //Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeFirstVisibleColumn, ret = " + first.ToString() + ", negOffset = " + negOffset.ToString()); + } + return first; + } + + /// + /// Updates the internal variables with the number of rows visible + /// in a given DataGrid Layout. + /// + private void ComputeVisibleRows() { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeVisibleRows"); + EnsureBound(); + + Rectangle data = layout.Data; + int visibleHeight = data.Height; + int cy = 0; + int visibleRows = 0; + DataGridRow[] localGridRows = DataGridRows; + int numRows = DataGridRowsLength; + + // when minimizing the dataGrid window, we will get negative values for the + // layout.Data.Width and layout.Data.Height ( is this a bug or not? if layout.Data.Height == 0 in that case, + // the old code would have worked ) + // + // if this is the case, then set numVisibleRows = numTotallyVisibleRows = 0; + // + if (visibleHeight < 0) + { + numVisibleRows = numTotallyVisibleRows = 0; + return; + } + + for (int i = firstVisibleRow; i < numRows; ++i) { + if (cy > visibleHeight) + break; + cy += localGridRows[i].Height; + visibleRows++; + } + + if (cy < visibleHeight) { + for (int i = firstVisibleRow-1; i >= 0; i--) { + int height = localGridRows[i].Height; + if (cy + height > visibleHeight) + break; + cy += height; + firstVisibleRow--; + visibleRows++; + } + } + + numVisibleRows = numTotallyVisibleRows = visibleRows; + if (cy > visibleHeight) + numTotallyVisibleRows--; + + Debug.Assert(numVisibleRows >= 0, "the number of visible rows can't be negative"); + Debug.Assert(numTotallyVisibleRows >= 0, "the number of totally visible rows can't be negative"); + } + + + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new DataGridAccessibleObject(this); + } + + + /// + /// + /// Creates a DataGridState representing the child table retrieved + /// from the passed DataRelation. + /// + private DataGridState CreateChildState(string relationName, DataGridRow source) { + DataGridState dgs = new DataGridState(); + + string newDataMember; + if (String.IsNullOrEmpty(this.DataMember)) + { + newDataMember = relationName; + } + else + { + newDataMember = this.DataMember + "." + relationName; + } + + CurrencyManager childLM = (CurrencyManager) this.BindingContext[this.DataSource, newDataMember]; + + dgs.DataSource = this.DataSource; + dgs.DataMember = newDataMember; + dgs.ListManager = childLM; + + dgs.DataGridRows = null; + dgs.DataGridRowsLength = childLM.Count + (policy.AllowAdd ? 1 : 0); + + return dgs; + } + + + /// + /// Constructs a Layout object containing the state + /// for a newly constructed DataGrid. + /// + private LayoutData CreateInitialLayoutState() { + Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: CreateInitialLayoutState"); + LayoutData newLayout = new LayoutData(); + newLayout.Inside = new Rectangle(); + newLayout.TopLeftHeader = new Rectangle(); + newLayout.ColumnHeaders = new Rectangle(); + newLayout.RowHeaders = new Rectangle(); + newLayout.Data = new Rectangle(); + newLayout.Caption = new Rectangle(); + newLayout.ParentRows = new Rectangle(); + newLayout.ResizeBoxRect = new Rectangle(); + newLayout.ColumnHeadersVisible = true; + newLayout.RowHeadersVisible = true; + newLayout.CaptionVisible = defaultCaptionVisible; + newLayout.ParentRowsVisible = defaultParentRowsVisible; + newLayout.ClientRectangle = ClientRectangle; + return newLayout; + } + + /// + /// The DataGrid caches an array of rectangular areas + /// which represent the area which scrolls left to right. + /// This method is invoked whenever the DataGrid needs + /// this scrollable region. + /// + private NativeMethods.RECT[] CreateScrollableRegion(Rectangle scroll) { + if (cachedScrollableRegion != null) { + return cachedScrollableRegion; + } + + bool alignToRight = isRightToLeft(); + + using(Region region = new Region(scroll)) { + int nRows = numVisibleRows; + int cy = layout.Data.Y; + int cx = layout.Data.X; + DataGridRow[] localGridRows = DataGridRows; + for (int r = firstVisibleRow; r < nRows; r++) { + int rowHeight = localGridRows[r].Height; + Rectangle rowExclude = localGridRows[r].GetNonScrollableArea(); + rowExclude.X += cx; + rowExclude.X = MirrorRectangle(rowExclude, layout.Data, alignToRight); + if (!rowExclude.IsEmpty) { + region.Exclude(new Rectangle(rowExclude.X, + rowExclude.Y + cy, + rowExclude.Width, + rowExclude.Height)); + } + cy += rowHeight; + } + + using (Graphics graphics = CreateGraphicsInternal()) { + IntPtr handle = region.GetHrgn(graphics); + if (handle != IntPtr.Zero) { + cachedScrollableRegion = UnsafeNativeMethods.GetRectsFromRegion(handle); + + // SECREVIEW : This assert is safe since we created the native region. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + region.ReleaseHrgn(handle); + } finally { + CodeAccessPermission.RevertAssert(); + } + } + } + } + + return cachedScrollableRegion; + } + + /// + /// + /// Disposes of the resources (other than memory) used + /// by the . + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (vertScrollBar != null) + vertScrollBar.Dispose(); + if (horizScrollBar != null) + horizScrollBar.Dispose(); + + if (this.toBeDisposedEditingControl != null) { + this.toBeDisposedEditingControl.Dispose(); + this.toBeDisposedEditingControl = null; + } + + GridTableStylesCollection tables = this.TableStyles; + if (tables != null) { + #if DEBUG + Debug.Assert(this.myGridTable == null || this.myGridTable.IsDefault || tables.Contains(this.myGridTable), "how come that the currentTable is not in the list of tables?"); + #endif // DEBUG + for (int i = 0; i < tables.Count; i++) + tables[i].Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// Draws an XOR region to give UI feedback for Column Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawColSplitBar(MouseEventArgs e) + { + Rectangle r = CalcColResizeFeedbackRect(e); + DrawSplitBar(r); + } + + /// + /// Draws an XOR region to give UI feedback for Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawRowSplitBar(MouseEventArgs e) + { + Rectangle r = CalcRowResizeFeedbackRect(e); + DrawSplitBar(r); + } + + /// + /// Draws an XOR region to give UI feedback for Column/Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawSplitBar(Rectangle r) + { + IntPtr parentHandle = Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT); + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc)); + } + + /// + /// Begin in-place editing of a cell. Any editing is commited + /// before the new edit takes place. + /// + /// This will always edit the currentCell + /// If you want to edit another cell than the current one, just reset CurrentCell + /// + private void Edit() { + Edit(null); + } + + private void Edit(string displayText) { + EnsureBound(); + + // we want to be able to edit a cell which is not visible, as in the case with editing and scrolling + // at the same time. So do not call Ensure Visible + // + // EnsureVisible(currentRow, currentCol); + + bool cellIsVisible = true; + + // whoever needs to call ResetSelection should not rely on + // Edit() to call it; + // + // ResetSelection(); + + EndEdit(); + + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: Edit, currentRow = " + currentRow.ToString(CultureInfo.InvariantCulture) + + ", currentCol = " + currentCol.ToString(CultureInfo.InvariantCulture) + (displayText != null ? ", displayText= " + displayText : "")); + + /* allow navigation even if the policy does not allow editing + if (!policy.AllowEdit) + return; + */ + + DataGridRow[] localGridRows = DataGridRows; + + // what do you want to edit when there are no rows? + if (DataGridRowsLength == 0) + return; + + localGridRows[currentRow].OnEdit(); + editRow = localGridRows[currentRow]; + + // if the list has no columns, then what good is an edit? + if (this.myGridTable.GridColumnStyles.Count == 0) + return; + + // what if the currentCol does not have a propDesc? + editColumn = myGridTable.GridColumnStyles[currentCol]; + if(editColumn.PropertyDescriptor == null) + return; + + + Rectangle cellBounds = Rectangle.Empty; + if (currentRow < firstVisibleRow || currentRow > firstVisibleRow + numVisibleRows || + currentCol < firstVisibleCol || currentCol > firstVisibleCol + numVisibleCols - 1 || + (currentCol == firstVisibleCol && negOffset != 0)) + { + cellIsVisible = false; + } + else + { + cellBounds = GetCellBounds(currentRow, currentCol); + } + + gridState[GRIDSTATE_isNavigating] = true; + gridState[GRIDSTATE_isEditing] = false; + + // once we call editColumn.Edit on a DataGridTextBoxColumn + // the edit control will become visible, and its bounds will get set. + // both actions cause Edit.Parent.OnLayout + // so we flag this change, cause we don't want to PerformLayout on the entire DataGrid + // everytime the edit column gets edited + gridState[GRIDSTATE_editControlChanging] = true; + + editColumn.Edit(this.ListManager, + currentRow, + cellBounds, + myGridTable.ReadOnly || this.ReadOnly || !policy.AllowEdit, + displayText, + cellIsVisible); + + // reset the editControlChanging to false + gridState[GRIDSTATE_editControlChanging] = false; + } + + /// + /// + /// Requests an end to an edit operation taking place on the + /// + /// control. + /// + public bool EndEdit(DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort) { + bool ret = false; + if (gridState[GRIDSTATE_isEditing]) { + if (gridColumn != editColumn) { + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit requested on a column we are not editing."); + } + if (rowNumber != editRow.RowNumber) { + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit requested on a row we are not editing."); + } + if (shouldAbort) { + AbortEdit(); + ret = true; + } + else + ret = CommitEdit(); + + } + return ret; + } + + /// + /// Ends any editing in progress by attempting to commit and then + /// aborting if not possible. + /// + private void EndEdit() { + Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit"); + + if (!gridState[GRIDSTATE_isEditing] && !gridState[GRIDSTATE_isNavigating]) + return; + + if (!CommitEdit()) { + AbortEdit(); + } + } + + // PERF: we attempt to create a ListManager for the DataSource/DateMember combination + // we do this in order to check for a valid DataMember + // if the check succeeds, then it means that we actully put the listManager in the BindingContext's + // list of BindingManagers. this is fine, cause if the check succeds, then Set_ListManager + // will be called, and this will get the listManager from the bindingManagerBase hashTable kept in the BindingContext + // + // this will work if the dataMember does not contain any dots ('.') + // if the dataMember contains dots, then it will be more complicated: maybe part of the binding path + // is valid w/ the new dataSource + // but we can leave w/ this, cause in the designer the dataMember will be only a column name. and the DataSource/DataMember + // properties are for use w/ the designer. + // + private void EnforceValidDataMember(object value) { + Debug.Assert(value != null, "we should not have a null dataSource when we want to check for a valid dataMember"); + if (this.DataMember == null || this.DataMember.Length == 0) + return; + if (this.BindingContext == null) + return; + // + try { + BindingManagerBase bm = this.BindingContext[value, this.dataMember]; + } + catch { + this.dataMember = ""; + } + } + + // will be used by the columns to tell the grid that + // editing is taken place (ie, the grid is no longer in the + // editOrNavigateMode) + // + // also, tell the current row to lose child focus + // + /// + internal protected virtual void ColumnStartedEditing(Rectangle bounds) { + Debug.Assert( currentRow >= firstVisibleRow && currentRow <= firstVisibleRow + numVisibleRows, "how can one edit a row which is invisible?"); + DataGridRow[] localGridRows = DataGridRows; + + if (bounds.IsEmpty && this.editColumn is DataGridTextBoxColumn && this.currentRow != -1 && this.currentCol != -1) { + // set the bounds on the control + // this will only work w/ our DataGridTexBox control + DataGridTextBoxColumn col = this.editColumn as DataGridTextBoxColumn; + Rectangle editBounds = this.GetCellBounds(this.currentRow, this.currentCol); + + gridState[GRIDSTATE_editControlChanging] = true; + try { + col.TextBox.Bounds = editBounds; + } finally { + gridState[GRIDSTATE_editControlChanging] = false; + } + } + + if (gridState[GRIDSTATE_inAddNewRow]) { + int currentRowCount = this.DataGridRowsLength; + DataGridRow[] newDataGridRows = new DataGridRow[currentRowCount + 1]; + for (int i = 0; i < currentRowCount; i++) { + newDataGridRows[i] = localGridRows[i]; + } + + + // put the AddNewRow + newDataGridRows[currentRowCount] = new DataGridAddNewRow(this, this.myGridTable, currentRowCount); + SetDataGridRows(newDataGridRows, currentRowCount + 1); + + Edit(); + // put this after the call to edit so that + // CommitEdit knows that the inAddNewRow is true; + gridState[GRIDSTATE_inAddNewRow] = false; + gridState[GRIDSTATE_isEditing] = true; + gridState[GRIDSTATE_isNavigating] = false; + return; + } + + gridState[GRIDSTATE_isEditing] = true; + gridState[GRIDSTATE_isNavigating] = false; + InvalidateRowHeader(this.currentRow); + + // tell the current row to lose the childFocuse + localGridRows[currentRow].LoseChildFocus(layout.RowHeaders, isRightToLeft()); + } + + /// + internal protected virtual void ColumnStartedEditing(Control editingControl) { + this.ColumnStartedEditing(editingControl.Bounds); + } + + /// + /// + /// + /// Displays child relations, if any exist, for all rows or a + /// specific row. + /// + /// + public void Expand(int row) { + SetRowExpansionState(row, true); + } + + /// + /// + /// + /// Creates a using the specified . + /// + /// + // protected and virtual because the SimpleDropdownDataGrid will override this + protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop, bool isDefault) { + return myGridTable == null ? null : myGridTable.CreateGridColumn(prop, isDefault); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop) + { + return myGridTable == null ? null : myGridTable.CreateGridColumn(prop); + } + + + #if PARENT_LINKS + + private ListManager ListManagerForChildColumn(ListManager childListManager, PropertyDescriptor prop) + { + /* + DataKey key; + RelationsCollection relCollection = dataColumn.Table.ParentRelations; + */ + + // this will give us the list of properties of the child + PropertyDescriptorCollection propCollection = childListManager.GetItemProperties(); + + int relCount = propCollection.Count; + for (int i=0;i + /// + /// Specifies the end of the initialization code. + /// + public void EndInit() { + inInit = false; + if (myGridTable == null && this.ListManager != null) { + SetDataGridTable(this.TableStyles[this.ListManager.GetListName()], true); // true for forcing column creation + } + if (myGridTable != null) + myGridTable.DataGrid = this; + } + + /// + /// Given a x coordinate, returns the column it is over. + /// + private int GetColFromX(int x) { + if (myGridTable == null) + return -1; + + Rectangle inside = layout.Data; + Debug.Assert(x >= inside.X && x < inside.Right, "x must be inside the horizontal bounds of layout.Data"); + + x = MirrorPoint(x, inside, isRightToLeft()); + + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + int columnCount = columns.Count; + + int cx = inside.X - negOffset; + int col = firstVisibleCol; + while (cx < inside.Width + inside.X && col < columnCount) { + // if (columns[col].Visible && columns[col].PropertyDescriptor != null) + if (columns[col].PropertyDescriptor != null) + cx += columns[col].Width; + if (cx > x) + return col; + ++col; + } + return -1; + } + + /// + /// Returns the coordinate of the left edge of the given column + /// Bi-Di: if the grid has the RightToLeft property set to RightToLeft.Yes, this will + /// return what appears as the right edge of the column + /// + internal int GetColBeg(int col) { + Debug.Assert(myGridTable != null, "GetColBeg can't be called when myGridTable == null."); + + int offset = layout.Data.X - negOffset; + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + + int lastCol = Math.Min(col, columns.Count); + for (int i = firstVisibleCol; i < lastCol; ++i) { + // if (columns[i].Visible && columns[i].PropertyDescriptor != null) + if (columns[i].PropertyDescriptor != null) + offset += columns[i].Width; + } + return MirrorPoint(offset, layout.Data, isRightToLeft()); + } + + /// + /// Returns the coordinate of the right edge of the given column + /// Bi-Di: if the grid has the RightToLeft property set to RightToLeft.Yes, this will + /// return what appears as the left edge of the column + /// + internal int GetColEnd(int col) { + // return MirrorPoint(GetColBeg(col) + myGridTable.GridColumnStyles[col].Width, layout.Data, isRightToLeft()); + int colBeg = GetColBeg(col); + Debug.Assert(myGridTable.GridColumnStyles[col].PropertyDescriptor != null, "why would we need the coordinate of a column that is not visible?"); + int width = myGridTable.GridColumnStyles[col].Width; + return isRightToLeft() ? colBeg - width : colBeg + width; + } + + private int GetColumnWidthSum() { + int sum = 0; + if (myGridTable != null && myGridTable.GridColumnStyles != null) { + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + + int columnsCount = columns.Count; + for (int i = 0; i < columnsCount; i++) + // if (columns[i].Visible && columns[i].PropertyDescriptor != null) + if (columns[i].PropertyDescriptor != null) + sum += columns[i].Width; + } + return sum; + } + + /// + /// Not all rows in the DataGrid are expandable, + /// this computes which ones are and returns an array + /// of references to them. + /// + private DataGridRelationshipRow[] GetExpandableRows() { + int nExpandableRows = DataGridRowsLength; + DataGridRow[] localGridRows = DataGridRows; + if (policy.AllowAdd) + nExpandableRows = Math.Max(nExpandableRows-1,0); + DataGridRelationshipRow[] expandableRows = new DataGridRelationshipRow[nExpandableRows]; + for (int i = 0; i < nExpandableRows; i++) + expandableRows[i] = (DataGridRelationshipRow)localGridRows[i]; + return expandableRows; + } + + /// + /// Returns the row number underneath the given y coordinate. + /// + /// + private int GetRowFromY(int y) { + Rectangle inside = layout.Data; + Debug.Assert(y >= inside.Y && y < inside.Bottom, "y must be inside the vertical bounds of the data"); + + int cy = inside.Y; + int row = firstVisibleRow; + int rowCount = DataGridRowsLength; + DataGridRow[] localGridRows = DataGridRows; + int bottom = inside.Bottom; + while (cy < bottom && row < rowCount) { + cy += localGridRows[row].Height; + if (cy > y) { + return row; + } + ++row; + } + return -1; + } + + internal Rectangle GetRowHeaderRect() { + return layout.RowHeaders; + } + + internal Rectangle GetColumnHeadersRect() { + return layout.ColumnHeaders; + } + + /// + /// Determines where on the control's ClientRectangle a given row is + /// painting to. + /// + private Rectangle GetRowRect(int rowNumber) { + Rectangle inside = layout.Data; + int cy = inside.Y; + DataGridRow[] localGridRows = DataGridRows; + for (int row = firstVisibleRow; row <= rowNumber; ++row) { + if (cy > inside.Bottom) { + break; + } + if (row == rowNumber) { + Rectangle rowRect = new Rectangle(inside.X, + cy, + inside.Width, + localGridRows[row].Height); + if (layout.RowHeadersVisible) { + rowRect.Width += layout.RowHeaders.Width; + rowRect.X -= isRightToLeft() ? 0 : layout.RowHeaders.Width; + } + return rowRect; + } + cy += localGridRows[row].Height; + } + return Rectangle.Empty; + } + + /// + /// Returns the coordinate of the top edge of the given row + /// + private int GetRowTop(int row) { + DataGridRow[] localGridRows = DataGridRows; + int offset = layout.Data.Y; + int lastRow = Math.Min(row, DataGridRowsLength); + for (int i = firstVisibleRow; i < lastRow; ++i) { + offset += localGridRows[i].Height; + } + for (int i=firstVisibleRow; i > lastRow; i--) { + offset -= localGridRows[i].Height; + } + return offset; + } + + /// + /// Returns the coordinate of the bottom edge of the given row + /// + private int GetRowBottom(int row) { + DataGridRow[] localGridRows = DataGridRows; + + return GetRowTop(row) + localGridRows[row].Height; + } + + /// + /// This method is called on methods that need the grid + /// to be bound to a DataTable to work. + /// + private void EnsureBound() { + if (!Bound) { + throw new InvalidOperationException(SR.GetString(SR.DataGridUnbound)); + } + } + + private void EnsureVisible(int row, int col) { + if (row < firstVisibleRow + || row >= firstVisibleRow + numTotallyVisibleRows) { + int dRows = ComputeDeltaRows(row); + ScrollDown(dRows); + } + + if (this.firstVisibleCol == 0 && this.numVisibleCols == 0 && this.lastTotallyVisibleCol == -1) { + // no columns are displayed whatsoever + // some sanity checks + Debug.Assert(this.negOffset == 0, " no columns are displayed so the negative offset should be 0"); + return; + } + + int previousFirstVisibleCol = this.firstVisibleCol; + int previousNegOffset = this.negOffset; + int previousLastTotallyVisibleCol = this.lastTotallyVisibleCol; + + while (col < firstVisibleCol + || col == firstVisibleCol && negOffset != 0 + || lastTotallyVisibleCol == -1 && col > firstVisibleCol + || lastTotallyVisibleCol > -1 && col > lastTotallyVisibleCol) { + + ScrollToColumn(col); + + if (previousFirstVisibleCol == this.firstVisibleCol && + previousNegOffset == this.negOffset && + previousLastTotallyVisibleCol == this.lastTotallyVisibleCol) { + // nothing changed since the last iteration + // don't get into an infinite loop + break; + } + previousFirstVisibleCol = firstVisibleCol; + previousNegOffset = this.negOffset; + previousLastTotallyVisibleCol = this.lastTotallyVisibleCol; + + // continue to scroll to the right until the scrollTo column is the totally last visible column or it is the first visible column + } + } + + /// + /// + /// Gets a + /// that specifies the four corners of the selected cell. + /// + public Rectangle GetCurrentCellBounds() { + DataGridCell current = this.CurrentCell; + return GetCellBounds(current.RowNumber, current.ColumnNumber); + } + + /// + /// + /// Gets the of the cell specified by row and column number. + /// + public Rectangle GetCellBounds(int row, int col) { + DataGridRow[] localGridRows = DataGridRows; + Rectangle cellBounds = localGridRows[row].GetCellBounds(col); + cellBounds.Y += GetRowTop(row); + cellBounds.X += layout.Data.X - negOffset; + cellBounds.X = MirrorRectangle(cellBounds, layout.Data, isRightToLeft()); + return cellBounds; + } + + /// + /// + /// Gets the of the cell specified by . + /// + public Rectangle GetCellBounds(DataGridCell dgc) { + return GetCellBounds(dgc.RowNumber, dgc.ColumnNumber); + } + + // + + + internal Rectangle GetRowBounds(DataGridRow row) { + Rectangle rowBounds = new Rectangle(); + rowBounds.Y = GetRowTop(row.RowNumber); + rowBounds.X = layout.Data.X; + rowBounds.Height = row.Height; + rowBounds.Width = layout.Data.Width; + return rowBounds; + } + + /// + /// + /// Gets information, such as row and column number of a + /// clicked point on + /// the grid, + /// using the x + /// and y coordinate passed to the method. + /// + public HitTestInfo HitTest(int x, int y) { + int topOfData = layout.Data.Y; + HitTestInfo ci = new HitTestInfo(); + + if (layout.CaptionVisible && layout.Caption.Contains(x,y)) { + ci.type = HitTestType.Caption; + return ci; + } + if (layout.ParentRowsVisible && layout.ParentRows.Contains(x,y)) { + ci.type = HitTestType.ParentRows; + return ci; + } + + if (!layout.Inside.Contains(x,y)) + return ci; + + if (layout.TopLeftHeader.Contains(x,y)) + return ci; + + // check for column resize + if (layout.ColumnHeaders.Contains(x,y)) { + ci.type = HitTestType.ColumnHeader; + ci.col = GetColFromX(x); + if (ci.col < 0) + return HitTestInfo.Nowhere; + int right = GetColBeg(ci.col + 1); + bool rightToLeft = isRightToLeft(); + if ((rightToLeft && x - right < 8) || (!rightToLeft && right - x < 8)) { + ci.type = HitTestType.ColumnResize; + } + return(allowColumnResize ? ci : HitTestInfo.Nowhere); + } + + //check for RowResize: + if (layout.RowHeaders.Contains(x,y)) { + ci.type = HitTestType.RowHeader; + ci.row = GetRowFromY(y); + if (ci.row < 0) + return HitTestInfo.Nowhere; + + // find out if the click was a RowResize click + DataGridRow[] localGridRows = DataGridRows; + int bottomBorder = GetRowTop(ci.row) + localGridRows[ci.row].Height; + if (bottomBorder - y - BorderWidth < 2 && !(localGridRows[ci.row] is DataGridAddNewRow)) { + ci.type = HitTestType.RowResize; + } + + return (allowRowResize ? ci : HitTestInfo.Nowhere); + } + + if (layout.Data.Contains(x,y)) { + ci.type = HitTestType.Cell; + ci.col = GetColFromX(x); + ci.row = GetRowFromY(y); + if (ci.col < 0 || ci.row < 0) return HitTestInfo.Nowhere; + return ci; + } + return ci; + } + + /// + /// + /// Gets information, such as row and column number of a + /// clicked point on the grid, about the + /// grid using a specific + /// . + /// + public HitTestInfo HitTest(Point position) { + return HitTest(position.X, position.Y); + } + + /// + /// + /// Initializes the values for column widths in the table. + /// + private void InitializeColumnWidths() { + if (myGridTable == null) + return; + + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + int numCols = columns.Count; + + // Resize the columns to a approximation of a best fit. + // We find the best fit width of NumRowsForAutoResize rows + // and use it for each column. + int preferredColumnWidth = this.myGridTable.IsDefault ? this.PreferredColumnWidth : this.myGridTable.PreferredColumnWidth; + // if we set the PreferredColumnWidth to something else than AutoColumnSize + // then use that value + // + for (int col = 0; col < numCols; col++) { + // if the column width is not -1, then this column was initialized already + if (columns[col].width != -1) continue; + + columns[col].width = preferredColumnWidth; + } + } + + /// + /// Invalidates the scrollable area of the DataGrid. + /// + internal void InvalidateInside() { + Invalidate(layout.Inside); + } + + /// + /// Invalidates the caption area of the DataGrid. + /// + internal void InvalidateCaption() { + if (layout.CaptionVisible) + Invalidate(layout.Caption); + } + + /// + /// Invalidates a rectangle normalized to the caption's + /// visual bounds. + /// + internal void InvalidateCaptionRect(Rectangle r) { + if (layout.CaptionVisible) { + Invalidate(r); + } + } + + /// + /// Invalidates the display region of a given DataGridColumn. + /// + internal void InvalidateColumn(int column) { + GridColumnStylesCollection gridColumns = this.myGridTable.GridColumnStyles; + if (column < 0 || gridColumns == null || gridColumns.Count <= column) + return; + + Debug.Assert(gridColumns[column].PropertyDescriptor != null, "how can we invalidate a column that is invisible?"); + // bail if the column is not visible. + if (column < firstVisibleCol || column > firstVisibleCol + numVisibleCols - 1) + return; + + Rectangle columnArea = new Rectangle(); + columnArea.Height = layout.Data.Height; + columnArea.Width = gridColumns[column].Width; + columnArea.Y = layout.Data.Y; + + int x = layout.Data.X - negOffset; + int gridColumnsCount = gridColumns.Count; + for (int i = firstVisibleCol; i < gridColumnsCount; ++i) { + if (i == column) + break; + x += gridColumns[i].Width; + } + columnArea.X = x; + columnArea.X = MirrorRectangle(columnArea, layout.Data, isRightToLeft()); + Invalidate(columnArea); + } + + /// + /// Invalidates the parent rows area of the DataGrid + /// + internal void InvalidateParentRows() { + if (layout.ParentRowsVisible) + Invalidate(layout.ParentRows); + } + + /// + /// Invalidates a rectangle normalized to the parent + /// rows area's visual bounds. + /// + internal void InvalidateParentRowsRect(Rectangle r) { + Rectangle parentRowsRect = layout.ParentRows; + Invalidate(r); + if (!parentRowsRect.IsEmpty) { + //Invalidate(new Rectangle(parentRowsRect.X + r.X, parentRowsRect.Y + r.Y, + // r.Width, r.Height)); + } + } + + /// + /// Invalidate the painting region for the row specified. + /// + internal void InvalidateRow(int rowNumber) { + Rectangle rowRect = GetRowRect(rowNumber); + if (!rowRect.IsEmpty) { + Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: Invalidating row " + rowNumber.ToString(CultureInfo.InvariantCulture)); + Invalidate(rowRect); + } + } + + private void InvalidateRowHeader(int rowNumber) { + if (rowNumber >= firstVisibleRow && rowNumber < firstVisibleRow + numVisibleRows) { + if (!layout.RowHeadersVisible) + return; + + Rectangle invalid = new Rectangle(); + invalid.Y = GetRowTop(rowNumber); + invalid.X = layout.RowHeaders.X; + invalid.Width = layout.RowHeaders.Width; + invalid.Height = this.DataGridRows[rowNumber].Height; + Invalidate(invalid); + } + } + + // NOTE: + // because of Rtl, we assume that the only place that calls InvalidateRowRect is + // the DataGridRelationshipRow + internal void InvalidateRowRect(int rowNumber, Rectangle r) { + Rectangle rowRect = GetRowRect(rowNumber); + if (!rowRect.IsEmpty) { + Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: Invalidating a rect in row " + rowNumber.ToString(CultureInfo.InvariantCulture)); + Rectangle inner = new Rectangle(rowRect.X + r.X, rowRect.Y + r.Y, r.Width, r.Height); + if (vertScrollBar.Visible && isRightToLeft()) + inner.X -= vertScrollBar.Width; + Invalidate(inner); + } + } + + /// + /// + /// Gets a value that indicates whether a specified row's node is expanded or collapsed. + /// + public bool IsExpanded(int rowNumber) { + if (rowNumber < 0 || rowNumber > DataGridRowsLength) + throw new ArgumentOutOfRangeException("rowNumber"); + DataGridRow[] localGridRows = DataGridRows; + + // + + + DataGridRow row = localGridRows[rowNumber]; + if (row is DataGridRelationshipRow) { + DataGridRelationshipRow relRow = (DataGridRelationshipRow)row; + return relRow.Expanded; + } + else + return false; + } + + /// + /// + /// + /// Gets a value indicating whether a + /// specified row is selected. + /// + /// + public bool IsSelected(int row) { + // SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change + DataGridRow[] localGridRows = DataGridRows; + return localGridRows[row].Selected; + } + + internal static bool IsTransparentColor(Color color) { + return color.A < 255; + } + + /// + /// Determines if Scrollbars should be visible, + /// updates their bounds and the bounds of all + /// other regions in the DataGrid's Layout. + /// + private void LayoutScrollBars() { + // if we set the dataSource to null, then take away the scrollbars. + if (listManager == null || myGridTable == null) { + horizScrollBar.Visible = false; + vertScrollBar.Visible = false; + return; + } + + // Scrollbars are a tricky issue. + // We need to see if we can cram our columns and rows + // in without scrollbars and if they don't fit, we make + // scrollbars visible and then fixup our regions for the + // data and headers. + bool needHorizScrollbar = false; + bool needVertScrollbar = false; + bool recountRows = false; + bool alignToRight = isRightToLeft(); + + int nGridCols = myGridTable.GridColumnStyles.Count; + + // if we call LayoutScrollBars before CreateDataGridRows + // then the columns will have their default width ( 100 ) + // CreateDataGridRows will possibly change the columns' width + // + // and anyway, ComputeVisibleRows will call the DataGridRows accessor + // + DataGridRow[] gridRows = this.DataGridRows; + + // at this stage, the data grid columns may have their width set to -1 ( ie, their width is uninitialized ) + // make sure that the totalWidth is at least 0 + int totalWidth = Math.Max(0, GetColumnWidthSum()); + + if (totalWidth > layout.Data.Width && !needHorizScrollbar) { + int horizHeight = horizScrollBar.Height; + layout.Data.Height -= horizHeight; + if (layout.RowHeadersVisible) + layout.RowHeaders.Height -= horizHeight; + needHorizScrollbar = true; + } + + int oldFirstVisibleRow = firstVisibleRow; + + ComputeVisibleRows(); + if (numTotallyVisibleRows != DataGridRowsLength && !needVertScrollbar) { + int vertWidth = vertScrollBar.Width; + layout.Data.Width -= vertWidth; + if (layout.ColumnHeadersVisible) + { + if (alignToRight) + layout.ColumnHeaders.X += vertWidth; + + layout.ColumnHeaders.Width -= vertWidth; + } + needVertScrollbar = true; + } + + this.firstVisibleCol = ComputeFirstVisibleColumn(); + // we compute the number of visible columns only after we set up the vertical scroll bar. + ComputeVisibleColumns(); + + if (needVertScrollbar && totalWidth > layout.Data.Width && !needHorizScrollbar) { + firstVisibleRow = oldFirstVisibleRow; + int horizHeight = horizScrollBar.Height; + layout.Data.Height -= horizHeight; + if (layout.RowHeadersVisible) + layout.RowHeaders.Height -= horizHeight; + needHorizScrollbar = true; + recountRows = true; + } + + if (recountRows) { + ComputeVisibleRows(); + if (numTotallyVisibleRows != DataGridRowsLength && !needVertScrollbar) { + int vertWidth = vertScrollBar.Width; + layout.Data.Width -= vertWidth; + if (layout.ColumnHeadersVisible) + { + if (alignToRight) + layout.ColumnHeaders.X += vertWidth; + + layout.ColumnHeaders.Width -= vertWidth; + } + needVertScrollbar = true; + } + } + + layout.ResizeBoxRect = new Rectangle(); + if (needVertScrollbar && needHorizScrollbar) { + Rectangle data = layout.Data; + layout.ResizeBoxRect = new Rectangle(alignToRight ? data.X : data.Right, + data.Bottom, + vertScrollBar.Width, + horizScrollBar.Height); + } + + + if (needHorizScrollbar && nGridCols > 0) { + //Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: foo"); + + int widthNotVisible = totalWidth - layout.Data.Width; + + horizScrollBar.Minimum = 0; + horizScrollBar.Maximum = totalWidth; + horizScrollBar.SmallChange = 1; + horizScrollBar.LargeChange = Math.Max(totalWidth - widthNotVisible, 0); + horizScrollBar.Enabled = this.Enabled; + horizScrollBar.RightToLeft = RightToLeft; + horizScrollBar.Bounds = new Rectangle(alignToRight ? layout.Inside.X + layout.ResizeBoxRect.Width : layout.Inside.X, + layout.Data.Bottom, + layout.Inside.Width - layout.ResizeBoxRect.Width, + horizScrollBar.Height); + horizScrollBar.Visible = true; + } + else { + HorizontalOffset = 0; + horizScrollBar.Visible = false; + } + + if (needVertScrollbar) { + int vertScrollBarTop = layout.Data.Y; + if (layout.ColumnHeadersVisible) + vertScrollBarTop = layout.ColumnHeaders.Y; + // if numTotallyVisibleRows == 0 ( the height of the row is bigger than the height of + // the grid ) then scroll in increments of 1. + vertScrollBar.LargeChange = numTotallyVisibleRows != 0 ? numTotallyVisibleRows : 1; + vertScrollBar.Bounds = new Rectangle(alignToRight ? layout.Data.X : layout.Data.Right, + vertScrollBarTop, + vertScrollBar.Width, + layout.Data.Height + layout.ColumnHeaders.Height); + vertScrollBar.Enabled = this.Enabled; + vertScrollBar.Visible = true; + if (alignToRight) + layout.Data.X += vertScrollBar.Width; + } + else { + vertScrollBar.Visible = false; + } + } + + /// + /// + /// Navigates back to the table previously displayed in the grid. + /// + public void NavigateBack() { + if (!CommitEdit() || parentRows.IsEmpty()) + return; + // when navigating back, if the grid is inAddNewRow state, cancel the currentEdit. + // we do not need to recreate the rows cause we are navigating back. + // the grid will catch any exception that happens. + if (gridState[GRIDSTATE_inAddNewRow]) { + gridState[GRIDSTATE_inAddNewRow] = false; + try { + listManager.CancelCurrentEdit(); + } catch { + } + } else { + UpdateListManager(); + } + + + DataGridState newState = parentRows.PopTop(); + + ResetMouseState(); + + newState.PullState(this, false); // we do not want to create columns when navigating back + + // we need to have originalState != null when we process + // Set_ListManager in the NavigateBack/NavigateTo methods. + // otherwise the DataSource_MetaDataChanged event will not get registered + // properly + if (parentRows.GetTopParent() == null) + originalState = null; + + DataGridRow[] localGridRows = this.DataGridRows; + // what if the user changed the ReadOnly property + // on the grid while the user was navigating to the child rows? + // + // what if the policy does not allow for allowAdd? + // + if ((this.ReadOnly || !policy.AllowAdd) == (localGridRows[DataGridRowsLength -1] is DataGridAddNewRow)) { + int newDataGridRowsLength = (ReadOnly || !policy.AllowAdd) ? DataGridRowsLength - 1 : DataGridRowsLength + 1; + DataGridRow[] newDataGridRows = new DataGridRow[newDataGridRowsLength]; + for (int i = 0; i < Math.Min(newDataGridRowsLength, DataGridRowsLength); i++) { + newDataGridRows[i] = DataGridRows[i]; + } + if(!this.ReadOnly && policy.AllowAdd) + newDataGridRows[newDataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, newDataGridRowsLength-1); + SetDataGridRows(newDataGridRows, newDataGridRowsLength); + } + + // when we navigate back from a child table, + // it may be the case that in between the user added a tableStyle that is different + // from the one that is currently in the grid + // in that case, we need to reset the dataGridTableStyle in the rows + localGridRows = this.DataGridRows; + if (localGridRows != null && localGridRows.Length != 0) { + DataGridTableStyle dgTable = localGridRows[0].DataGridTableStyle; + if (dgTable != this.myGridTable) { + for (int i = 0; i < localGridRows.Length; i ++) + localGridRows[i].DataGridTableStyle = this.myGridTable; + } + } + + // if we have the default table, when we navigate back + // we also have the default gridColumns, w/ width = -1 + // we need to set the width on the new gridColumns + // + if (this.myGridTable.GridColumnStyles.Count > 0 && this.myGridTable.GridColumnStyles[0].Width == -1) { + #if DEBUG + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + for (int i = 0; i < cols.Count; i++) { + Debug.Assert(cols[i].Width == -1, "Sanity check"); + } + Debug.Assert(this.myGridTable.IsDefault, "when we navigate to the parent rows and the columns have widths -1 we are using the default table"); + #endif // DEBUG + InitializeColumnWidths(); + } + + // reset the currentRow to the old position in the listmanager: + currentRow = this.ListManager.Position == -1 ? 0 : this.ListManager.Position; + + // if the AllowNavigation changed while the user was navigating the + // child tables, so that the new navigation mode does not allow childNavigation anymore + // then reset the rows + if (!AllowNavigation) { + RecreateDataGridRows(); + } + + caption.BackButtonActive = (parentRows.GetTopParent() != null) && AllowNavigation; + caption.BackButtonVisible = caption.BackButtonActive; + caption.DownButtonActive = (parentRows.GetTopParent() != null); + + PerformLayout(); + Invalidate(); + // reposition the scroll bar + if (vertScrollBar.Visible) + vertScrollBar.Value = firstVisibleRow; + if (horizScrollBar.Visible) + horizScrollBar.Value = HorizontalOffset + negOffset; + Edit(); + OnNavigate(new NavigateEventArgs(false)); + } + + /// + /// + /// + /// Navigates to the table specified by row and relation + /// name. + /// + /// + public void NavigateTo(int rowNumber, String relationName) { + // do not navigate if AllowNavigation is set to false + if (!AllowNavigation) + return; + DataGridRow[] localGridRows = DataGridRows; + if (rowNumber < 0 || rowNumber > DataGridRowsLength - (policy.AllowAdd ? 2:1)) { + throw new ArgumentOutOfRangeException("rowNumber"); + } + EnsureBound(); + + DataGridRow source = localGridRows[rowNumber]; + + NavigateTo(relationName, source, false); + } + + internal void NavigateTo(string relationName, DataGridRow source, bool fromRow) { + // do not navigate if AllowNavigation is set to false + if (!AllowNavigation) + return; + // Commit the edit if possible + if (!CommitEdit()) + return; + + DataGridState childState; + try { + childState = CreateChildState(relationName, source); + } + catch { + // if we get an error when creating the RelatedCurrencyManager + // then navigateBack and ignore the exception. + // + NavigateBack(); + return; + } + + // call EndCurrentEdit before navigating. + // if we get an exception, we do not navigate. + // + try { + this.listManager.EndCurrentEdit(); + } + catch { + return; + } + + // Preserve our current state + // we need to do this after the EndCurrentEdit, otherwise the + // DataGridState will get the listChanged event from the EndCurrentEdit + DataGridState dgs = new DataGridState(this); + dgs.LinkingRow = source; + + // we need to update the Position in the ListManager + // ( the RelatedListManager uses only the position in the parentManager + // to create the childRows + // + // before the code was calling CurrentCell = this and such + // we should only call EndCurrentEdit ( which the code was doing anyway ) + // and then set the position in the listManager to the new row. + // + if (source.RowNumber != CurrentRow) + this.listManager.Position = source.RowNumber; + + // We save our state if the parent rows stack is empty. + if (parentRows.GetTopParent() == null) { + originalState = dgs; + } + + parentRows.AddParent(dgs); + + NavigateTo(childState); + + OnNavigate(new NavigateEventArgs(true)); + if (fromRow) { + // OnLinkClick(EventArgs.Empty); + } + } + + private void NavigateTo(DataGridState childState) { + // we are navigating... better stop editing. + EndEdit(); + + // also, we are no longer in editOrNavigate mode either + gridState[GRIDSTATE_isNavigating] = false; + + // reset hot tracking + ResetMouseState(); + + // Retrieve the child state + childState.PullState(this, true); // true for creating columns when we navigate to child rows + + if (this.listManager.Position != this.currentRow) { + this.currentRow = listManager.Position == -1 ? 0 : listManager.Position; + } + + if (parentRows.GetTopParent() != null) { + caption.BackButtonActive = AllowNavigation; + caption.BackButtonVisible = caption.BackButtonActive; + caption.DownButtonActive = true; + } + + HorizontalOffset = 0; + PerformLayout(); + Invalidate(); + } + + + /// + /// Given a coordinate in the control this method returns + /// the equivalent point for a row. + /// + private Point NormalizeToRow(int x, int y, int row) { + Debug.Assert(row >= firstVisibleRow && row < firstVisibleRow + numVisibleRows, + "Row " + row.ToString(CultureInfo.InvariantCulture) + "is not visible! firstVisibleRow = " + + firstVisibleRow.ToString(CultureInfo.InvariantCulture) + ", numVisibleRows = " + + numVisibleRows.ToString(CultureInfo.InvariantCulture)); + Point origin = new Point(0, layout.Data.Y); + + DataGridRow[] localGridRows = DataGridRows; + for (int r = firstVisibleRow; r < row; ++r) { + origin.Y += localGridRows[r].Height; + } + // when hittesting for the PlusMinus, the code in the DataGridRelationshipRow + // will use real X coordinate ( the one from layout.RowHeaders ) to paint the glyph + // + return new Point(x, y - origin.Y); + } + + internal void OnColumnCollectionChanged(object sender, CollectionChangeEventArgs e) { + DataGridTableStyle table = (DataGridTableStyle)sender; + if (table.Equals(this.myGridTable)) { + // if we changed the column collection, then we need to set the property + // descriptors in the column collection. + // unless the user set the propertyDescriptor in the columnCollection + // + if (!this.myGridTable.IsDefault) { + // if the element in the collectionChangeEventArgs is not null + // and the action is refresh, then it means that the user + // set the propDesc. we do not want to override this. + if (e.Action != CollectionChangeAction.Refresh || e.Element == null) + PairTableStylesAndGridColumns(this.listManager, this.myGridTable, false); + } + Invalidate(); + PerformLayout(); + } + } + + /// + /// Paints column headers. + /// + private void PaintColumnHeaders(Graphics g) { + + bool alignToLeft = isRightToLeft(); + Rectangle boundingRect = layout.ColumnHeaders; + if (!alignToLeft) + boundingRect.X -= negOffset; + boundingRect.Width += negOffset; + + int columnHeaderWidth = PaintColumnHeaderText(g, boundingRect); + + if (alignToLeft) + boundingRect.X = boundingRect.Right - columnHeaderWidth; + + boundingRect.Width = columnHeaderWidth; + if (!FlatMode) { + ControlPaint.DrawBorder3D(g, boundingRect, Border3DStyle.RaisedInner); + boundingRect.Inflate(-1, -1); + // g.SetPen(OldSystemPens.Control); + // g.OldBrush = (OldSystemBrushes.Hollow); + boundingRect.Width --; + boundingRect.Height--; + g.DrawRectangle(SystemPens.Control, boundingRect); + } + } + + private int PaintColumnHeaderText(Graphics g, Rectangle boundingRect) { + int cx = 0; + Rectangle textBounds = boundingRect; + GridColumnStylesCollection gridColumns = this.myGridTable.GridColumnStyles; + bool alignRight = isRightToLeft(); + + + int nGridCols = gridColumns.Count; + // for sorting + PropertyDescriptor sortProperty = null; + sortProperty = this.ListManager.GetSortProperty(); + + // Now paint the column header text! + for (int col = firstVisibleCol; col < nGridCols; ++col) { + if (gridColumns[col].PropertyDescriptor == null) + continue; + + if (cx > boundingRect.Width) + break; + + bool columnSorted = sortProperty != null && sortProperty.Equals(gridColumns[col].PropertyDescriptor); + TriangleDirection whichWay = TriangleDirection.Up; + if (columnSorted) + { + ListSortDirection direction = this.ListManager.GetSortDirection(); + if (direction == ListSortDirection.Descending) + whichWay = TriangleDirection.Down; + } + + if (alignRight) + { + textBounds.Width = gridColumns[col].Width - + (columnSorted ? textBounds.Height : 0); + textBounds.X = boundingRect.Right - cx - textBounds.Width; + } + else + { + textBounds.X = boundingRect.X + cx; + textBounds.Width = gridColumns[col].Width - + (columnSorted ? textBounds.Height : 0); + } + + // at the moment we paint some pixels twice. + // we should not call FilLRectangle, once the real GDI+ is there, we will have no need to do that + + // if the user set the HeaderBackBrush property on the + // dataGrid, then use that property + Brush headerBrush; + if (this.myGridTable.IsDefault) + headerBrush = HeaderBackBrush; + else + headerBrush = this.myGridTable.HeaderBackBrush; + + g.FillRectangle(headerBrush, textBounds); + // granted, the code would be a lot cleaner if we were using a "new Rectangle" + // but like this will be faster + if (alignRight) + { + textBounds.X -= 2; textBounds.Y += 2; + } + else + { + textBounds.X += 2; textBounds.Y += 2; + } + + StringFormat format = new StringFormat(); + + // the columnHeaderText alignment should be the same as + // the alignment in the column + // + HorizontalAlignment colAlignment = gridColumns[col].Alignment; + format.Alignment = colAlignment == HorizontalAlignment.Right ? StringAlignment.Far : + colAlignment == HorizontalAlignment.Center ? StringAlignment.Center : + StringAlignment.Near; + + // part 1, section 1: the column headers should not wrap + format.FormatFlags |= StringFormatFlags.NoWrap; + + if (alignRight) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + format.Alignment = StringAlignment.Near; + } + g.DrawString(gridColumns[col].HeaderText, + this.myGridTable.IsDefault ? this.HeaderFont : this.myGridTable.HeaderFont, + this.myGridTable.IsDefault ? this.HeaderForeBrush : this.myGridTable.HeaderForeBrush, + textBounds, + format); + format.Dispose(); + + if (alignRight) + { + textBounds.X += 2; textBounds.Y -= 2; + } + else + { + textBounds.X -= 2; textBounds.Y -= 2; + } + + if (columnSorted) { + // + + Rectangle triBounds = new Rectangle(alignRight ? textBounds.X - textBounds.Height : textBounds.Right, + textBounds.Y, + textBounds.Height, + textBounds.Height); + + g.FillRectangle(headerBrush, triBounds); + int deflateValue = Math.Max(0,(textBounds.Height - 5) / 2); + triBounds.Inflate(-deflateValue, -deflateValue); + + Pen pen1 = new Pen(this.BackgroundBrush); + Pen pen2 = new Pen(this.myGridTable.BackBrush); + Triangle.Paint(g, triBounds, whichWay, headerBrush, pen1, pen2, pen1, true); + pen1.Dispose(); + pen2.Dispose(); + } + int paintedWidth = textBounds.Width + (columnSorted ? textBounds.Height : 0); + + if (!FlatMode) { + if (alignRight && columnSorted) + textBounds.X -= textBounds.Height; + textBounds.Width = paintedWidth; + + ControlPaint.DrawBorder3D(g, textBounds, Border3DStyle.RaisedInner); + } + cx += paintedWidth; + } + + // paint the possible exposed portion to the right ( or left, as the case may be) + if (cx < boundingRect.Width) { + textBounds = boundingRect; + + if (!alignRight) + textBounds.X += cx; + + textBounds.Width -= cx; + g.FillRectangle(backgroundBrush, textBounds); + } + return cx; + } + + + /// + /// Paints a border around the bouding rectangle given + /// + private void PaintBorder(Graphics g, Rectangle bounds) + { + if (BorderStyle == BorderStyle.None) + return; + if (BorderStyle == BorderStyle.Fixed3D) { + Border3DStyle style = Border3DStyle.Sunken; + ControlPaint.DrawBorder3D(g, bounds, style ); + } + else if (BorderStyle == BorderStyle.FixedSingle) { + Brush br; + + if (this.myGridTable.IsDefault) + br = this.HeaderForeBrush; + else + br = this.myGridTable.HeaderForeBrush; + g.FillRectangle(br, bounds.X, bounds.Y, bounds.Width + 2, 2); + g.FillRectangle(br, bounds.Right - 2, bounds.Y, 2, bounds.Height + 2); + g.FillRectangle(br, bounds.X, bounds.Bottom - 2, bounds.Width + 2, 2); + g.FillRectangle(br, bounds.X, bounds.Y, 2, bounds.Height + 2); + } + else { + Pen pen = SystemPens.WindowFrame; + bounds.Width --; + bounds.Height--; + g.DrawRectangle(pen, bounds); + } + } + + /// + /// Paints the grid in the bounding rectangle given. + /// This includes the column headers and each visible row. + /// + private void PaintGrid(Graphics g, Rectangle gridBounds) { + Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: PaintGrid on " + gridBounds.ToString()); + + Rectangle rc = gridBounds; + + if (this.listManager != null) { + if (layout.ColumnHeadersVisible) { + Region r = g.Clip; + g.SetClip(layout.ColumnHeaders); + PaintColumnHeaders(g); + g.Clip = r; + r.Dispose(); + int columnHeaderHeight = layout.ColumnHeaders.Height; + rc.Y += columnHeaderHeight; + rc.Height -= columnHeaderHeight; + } + + if (layout.TopLeftHeader.Width > 0) { + if (this.myGridTable.IsDefault) + g.FillRectangle(this.HeaderBackBrush, layout.TopLeftHeader); + else + g.FillRectangle(this.myGridTable.HeaderBackBrush, layout.TopLeftHeader); + + if (!FlatMode) { + ControlPaint.DrawBorder3D(g, layout.TopLeftHeader, Border3DStyle.RaisedInner); + } + } + + PaintRows(g, ref rc) ; + } + + // paint the possible exposed portion below + if (rc.Height > 0) { + g.FillRectangle(backgroundBrush, rc); + } + } + + private void DeleteDataGridRows(int deletedRows) { + if (deletedRows == 0) + return; + + int currentRowCount = DataGridRowsLength; + int newDataGridRowsLength = currentRowCount - deletedRows + (gridState[GRIDSTATE_inAddNewRow] ? 1:0); + DataGridRow[] newDataGridRows = new DataGridRow[newDataGridRowsLength]; + DataGridRow[] gridRows = DataGridRows; + + // the number of selected entries so far in the array + int selectedEntries = 0; + + for (int i = 0; i < currentRowCount; i++) { + if (gridRows[i].Selected) { + selectedEntries ++; + } else { + newDataGridRows[i - selectedEntries] = gridRows[i]; + newDataGridRows[i - selectedEntries].number = i - selectedEntries; + } + } + + if (gridState[GRIDSTATE_inAddNewRow]) { + newDataGridRows[currentRowCount - selectedEntries] = new DataGridAddNewRow(this, this.myGridTable, currentRowCount - selectedEntries); + gridState[GRIDSTATE_inAddNewRow] = false; + } + + Debug.Assert(selectedEntries == deletedRows, "all the rows that would have been deleted should have been selected: selectedGridEntries " + selectedEntries.ToString(CultureInfo.InvariantCulture) + " deletedRows " + deletedRows.ToString(CultureInfo.InvariantCulture)); + + SetDataGridRows(newDataGridRows, newDataGridRowsLength); + } + + /// + /// Paints the visible rows on the grid. + /// + private void PaintRows(Graphics g, ref Rectangle boundingRect) { + int cy = 0; + bool alignRight = isRightToLeft(); + Rectangle rowBounds = boundingRect; + Rectangle dataBounds = Rectangle.Empty; + bool paintRowHeaders = layout.RowHeadersVisible; + Rectangle headerBounds = Rectangle.Empty; + + + int numRows = DataGridRowsLength; + DataGridRow[] localGridRows = DataGridRows; + int numCols = myGridTable.GridColumnStyles.Count - firstVisibleCol; + + for (int row = firstVisibleRow; row < numRows; row++) { + if (cy > boundingRect.Height) + break; + + rowBounds = boundingRect; + rowBounds.Height = localGridRows[row].Height; + rowBounds.Y = boundingRect.Y + cy; + + // will add some errors + #if false + if (forDebug == 0 || forDebug == 1) + { + object dRowView = listManager[row]; + DataRow dRow= ((DataRowView) dRowView).Row; + // dRow.RowError = "Error " + forDebug.ToString(); + dRow.SetColumnError(forDebug, "another error " + forDebug.ToString()); + + /* + if (localGridRows[row].DataRow != null) + { + localGridRows[row].DataRow.RowError = "error " + forDebug.ToString(); + localGridRows[row].DataRow.SetColumnError(forDebug, "another error " + forDebug.ToString()); + } + */ + forDebug ++; + } + #endif // false + if (paintRowHeaders) { + headerBounds = rowBounds; + headerBounds.Width = layout.RowHeaders.Width; + + if (alignRight) + { + headerBounds.X = rowBounds.Right - headerBounds.Width; + } + + if (g.IsVisible(headerBounds)) { + localGridRows[row].PaintHeader(g, headerBounds, alignRight, gridState[GRIDSTATE_isEditing]); + g.ExcludeClip(headerBounds); + } + + if (!alignRight) + rowBounds.X += headerBounds.Width; + rowBounds.Width -= headerBounds.Width; + } + if (g.IsVisible(rowBounds)) { + dataBounds = rowBounds; + if (!alignRight) + dataBounds.X -= negOffset; + dataBounds.Width += negOffset; + + localGridRows[row].Paint(g, dataBounds, rowBounds, firstVisibleCol, numCols, alignRight); + } + cy += rowBounds.Height; + } + boundingRect.Y += cy; + boundingRect.Height -= cy; + } + + + /// + /// + /// + /// Gets or sets a value that indicates whether a key should be processed + /// further. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: ProcessDialogKey " + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(keyData)); + DataGridRow[] localGridRows = DataGridRows; + if (listManager != null && DataGridRowsLength > 0 && localGridRows[currentRow].OnKeyPress(keyData)) { + Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: Current Row ate the keystroke"); + return true; + } + + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + case Keys.Next: + case Keys.Prior: + case Keys.Enter: + case Keys.Escape: + case Keys.Oemplus: + case Keys.Add: + case Keys.OemMinus: + case Keys.Subtract: + case Keys.Space: + case Keys.Delete: + case Keys.A: + KeyEventArgs ke = new KeyEventArgs(keyData); + if (ProcessGridKey(ke)) + return true; + break; + + case Keys.C: + if ((keyData & Keys.Control) != 0 && (keyData & Keys.Alt) == 0) + { + // the user pressed Ctrl-C + if (!Bound) + break; + + // need to distinguish between selecting a set of rows, and + // selecting just one column. + if (numSelectedRows == 0) + { + // copy the data from one column only + if (currentRow < ListManager.Count) + { + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + if (currentCol >= 0 && currentCol < columns.Count) + { + DataGridColumnStyle column = columns[currentCol]; + string text = column.GetDisplayText(column.GetColumnValueAtRow(ListManager, currentRow)); + + // copy the data to the clipboard + Clipboard.SetDataObject(text); + return true; + } + } + } + else + { + // the user selected a set of rows to copy the data from + + int numRowsOutputted = 0; // the number of rows written to "text" + string text = ""; + + for (int i = 0; i < DataGridRowsLength; ++i) + { + if (localGridRows[i].Selected) + { + GridColumnStylesCollection columns = myGridTable.GridColumnStyles; + int numCols = columns.Count; + for (int j = 0; j < numCols; j++) + { + DataGridColumnStyle column = columns[j]; + text += column.GetDisplayText(column.GetColumnValueAtRow(ListManager, i)); + + // do not put the delimiter at the end of the last column + if ( j < numCols - 1) + { + text += GetOutputTextDelimiter(); + } + } + + // put the hard enter "\r\n" only if this is not the last selected row + if (numRowsOutputted < numSelectedRows - 1) + { + text += "\r\n"; + } + + numRowsOutputted ++; + } + } + + // copy the data to the clipboard + Clipboard.SetDataObject(text); + return true; + } + + } + break; + } + return base.ProcessDialogKey(keyData); + } + + private void DeleteRows(DataGridRow[] localGridRows) { + int rowsDeleted = 0; + + int currentRowsCount = this.listManager == null ? 0 : this.listManager.Count; + + if (this.Visible) BeginUpdateInternal(); + try { + if (this.ListManager != null) { + for (int i = 0; i < this.DataGridRowsLength; i++) { + if (localGridRows[i].Selected) { + if (localGridRows[i] is DataGridAddNewRow) { + Debug.Assert(i == DataGridRowsLength - 1, "the location of addNewRow is " + i.ToString(CultureInfo.InvariantCulture) + " and there are " + DataGridRowsLength.ToString(CultureInfo.InvariantCulture) + " rows "); + localGridRows[i].Selected = false; + } + else { + this.ListManager.RemoveAt(i - rowsDeleted); + rowsDeleted ++; + } + } + } + } + } + catch { + // if we got an exception from the back end + // when deleting the rows then we should reset + // our rows and re-throw the exception + // + RecreateDataGridRows(); + gridState[GRIDSTATE_inDeleteRow] = false; + if (this.Visible) EndUpdateInternal(); + throw; + } + // keep the copy of the old rows in place + // it may be the case that deleting one row could cause multiple rows to be deleted in the same list + // + if (this.listManager != null && currentRowsCount == this.listManager.Count + rowsDeleted) { + DeleteDataGridRows(rowsDeleted); + } else { + RecreateDataGridRows(); + } + + gridState[GRIDSTATE_inDeleteRow] = false; + if (this.Visible) EndUpdateInternal(); + + if (this.listManager != null && currentRowsCount != this.listManager.Count + rowsDeleted) { + Invalidate(); + } + } + + + // convention: + // if we return -1 it means that the user was going left and there were no visible columns to the left of the current one + // if we return cols.Count + 1 it means that the user was going right and there were no visible columns to the right of the currrent + private int MoveLeftRight(GridColumnStylesCollection cols, int startCol, bool goRight) { + int i; + if (goRight) { + for (i = startCol + 1; i < cols.Count; i++) { + // if (cols[i].Visible && cols[i].PropertyDescriptor != null) + if (cols[i].PropertyDescriptor != null) + return i; + } + return i; + } else { + for (i = startCol - 1; i>=0; i--) { + // if (cols[i].Visible && cols[i].PropertyDescriptor != null) + if (cols[i].PropertyDescriptor != null) + return i; + } + return i; + } + } + + /// + /// + /// + /// Processes keys for grid navigation. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected bool ProcessGridKey(KeyEventArgs ke) { + Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: ProcessGridKey "+ TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(ke.KeyCode)); + if (listManager == null || myGridTable == null) + return false; + + DataGridRow[] localGridRows = DataGridRows; + KeyEventArgs biDiKe = ke; + // check for Bi-Di + // + if (isRightToLeft()) + { + switch(ke.KeyCode) { + case Keys.Left: + biDiKe = new KeyEventArgs((Keys.Right | ke.Modifiers)); + break; + case Keys.Right: + biDiKe = new KeyEventArgs((Keys.Left | ke.Modifiers)); + break; + default: + break; + } + } + + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + int firstColumnMarkedVisible = 0; + int lastColumnMarkedVisible = cols.Count; + for (int i = 0; i < cols.Count; i++) { + if (cols[i].PropertyDescriptor != null) { + firstColumnMarkedVisible = i; + break; + } + } + + for (int i = cols.Count - 1 ; i >= 0; i--) { + if (cols[i].PropertyDescriptor != null) { + lastColumnMarkedVisible = i; + break; + } + } + + switch (biDiKe.KeyCode) { + case Keys.Tab: + return ProcessTabKey(biDiKe.KeyData); + case Keys.Up: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + if (biDiKe.Control && !biDiKe.Alt) + { + if (biDiKe.Shift) + { + DataGridRow [] gridRows = DataGridRows; + + int savedCurrentRow = currentRow; + CurrentRow = 0; + + ResetSelection(); + + for (int i = 0; i <= savedCurrentRow; i ++) + gridRows[i].Selected = true; + numSelectedRows = savedCurrentRow + 1; + // hide the edit box + // + EndEdit(); + return true; + } + // do not make the parentRowsVisible = false; + // ParentRowsVisible = false; + ResetSelection(); + CurrentRow = 0; + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + else if (biDiKe.Shift) + { + DataGridRow [] gridRows = DataGridRows; + // keep a continous selected region + if (gridRows[currentRow].Selected) { + if (currentRow >= 1) { + if (gridRows[currentRow - 1].Selected) { + if (currentRow >= DataGridRowsLength - 1 || !gridRows[currentRow+1].Selected) { + numSelectedRows --; + gridRows[currentRow].Selected = false; + } + } else { + numSelectedRows += gridRows[currentRow -1].Selected ? 0:1; + gridRows[currentRow - 1].Selected = true; + } + CurrentRow --; + } + } else { + numSelectedRows ++; + gridRows[currentRow].Selected = true; + if (currentRow >= 1) { + numSelectedRows += gridRows[currentRow-1].Selected ? 0:1; + gridRows[currentRow-1].Selected = true; + CurrentRow --; + } + } + + // hide the edit box: + // + EndEdit(); + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + else if (biDiKe.Alt) + { + // will need to collapse all child table links + // -1 is for all rows, and false is for collapsing the rows + SetRowExpansionState(-1, false); + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + ResetSelection(); + CurrentRow = CurrentRow - 1; + Edit(); + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + break; + case Keys.Down: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + if (biDiKe.Control && !biDiKe.Alt) + { + if (biDiKe.Shift) + { + int savedCurrentRow = currentRow; + CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2:1)); + DataGridRow [] gridRows = DataGridRows; + + ResetSelection(); + + for (int i = savedCurrentRow; i <= currentRow; i++) + gridRows[i].Selected = true; + + numSelectedRows = currentRow - savedCurrentRow + 1; + // hide the edit box + // + EndEdit(); + return true; + } + // do not make the parentRowsVisible = true; + // ParentRowsVisible = true; + ResetSelection(); + CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2 : 1)); + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + else if (biDiKe.Shift) + { + DataGridRow [] gridRows = DataGridRows; + + // keep a continous selected region + if (gridRows[currentRow].Selected) { + + // -1 because we index from 0 + if (currentRow < DataGridRowsLength - (policy.AllowAdd ? 1:0) - 1) { + if (gridRows[currentRow + 1].Selected) { + if (currentRow == 0 || !gridRows[currentRow - 1].Selected) { + numSelectedRows --; + gridRows[currentRow].Selected = false; + } + } else { + numSelectedRows += gridRows[currentRow + 1].Selected ? 0:1; + gridRows[currentRow + 1].Selected = true; + } + + CurrentRow ++; + } + } else { + numSelectedRows ++; + gridRows[currentRow].Selected = true; + // -1 because we index from 0, and -1 so this is not the last row + // so it adds to -2 + if (currentRow < DataGridRowsLength - (policy.AllowAdd ? 1:0) - 1) { + CurrentRow ++; + numSelectedRows += gridRows[currentRow].Selected ? 0:1; + gridRows[currentRow].Selected = true; + } + } + + // hide the edit box: + // + EndEdit(); + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + else if (biDiKe.Alt) + { + // will need to expande all child table links + // -1 is for all rows, and true is for expanding the rows + SetRowExpansionState(-1, true); + return true; + } + ResetSelection(); + Edit(); + CurrentRow = CurrentRow + 1; + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + break; + case Keys.OemMinus: + case Keys.Subtract: + gridState[GRIDSTATE_childLinkFocused] = false; + if (biDiKe.Control && !biDiKe.Alt) + { + SetRowExpansionState(-1, false); + return true; + } + return false; + case Keys.Oemplus: + case Keys.Add: + gridState[GRIDSTATE_childLinkFocused] = false; + if (biDiKe.Control) + { + SetRowExpansionState(-1, true); + // hide the edit box + // + EndEdit(); + return true; + } + return false; + case Keys.Space: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + if (biDiKe.Shift) + { + ResetSelection(); + EndEdit(); + DataGridRow [] gridRows = DataGridRows; + gridRows[currentRow].Selected = true; + numSelectedRows = 1; + + return true; + } + return false; + case Keys.Next: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + if (biDiKe.Shift) + { + int savedCurrentRow = currentRow; + CurrentRow = Math.Min(DataGridRowsLength - (policy.AllowAdd ? 2:1), currentRow + numTotallyVisibleRows); + + DataGridRow [] gridRows = DataGridRows; + for (int i = savedCurrentRow; i <= currentRow; i++) + { + if (!gridRows[i].Selected) + { + gridRows[i].Selected = true; + numSelectedRows ++; + } + } + // hide edit box + // + EndEdit(); + } else if (biDiKe.Control && !biDiKe.Alt) { + // map ctrl-pageDown to show the parentRows + ParentRowsVisible = true; + } + else + { + ResetSelection(); + CurrentRow = Math.Min(DataGridRowsLength - (policy.AllowAdd ? 2:1), + CurrentRow + numTotallyVisibleRows); + } + break; + case Keys.Prior: + if (this.dataGridRowsLength == 0) { + return true; + } + + gridState[GRIDSTATE_childLinkFocused] = false; + if (biDiKe.Shift) + { + int savedCurrentRow = currentRow; + CurrentRow = Math.Max(0, CurrentRow - numTotallyVisibleRows); + + DataGridRow [] gridRows = DataGridRows; + for (int i = savedCurrentRow; i >= currentRow; i--) + { + if ( !gridRows[i].Selected) + { + gridRows[i].Selected = true; + numSelectedRows ++; + } + } + + // hide the edit box + // + EndEdit(); + } else if (biDiKe.Control && !biDiKe.Alt) { + // map ctrl-pageUp to hide the parentRows + ParentRowsVisible = false; + } + else + { + ResetSelection(); + CurrentRow = Math.Max(0, + CurrentRow - numTotallyVisibleRows); + } + break; + case Keys.Left: + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + if ((biDiKe.Modifiers & Keys.Modifiers) == Keys.Alt) + { + if (Caption.BackButtonVisible) + NavigateBack(); + return true; + } + + if ((biDiKe.Modifiers & Keys.Control) == Keys.Control) + { + // we should navigate to the first visible column + CurrentColumn = firstColumnMarkedVisible; + break; + } + + if (currentCol == firstColumnMarkedVisible && currentRow != 0) + { + CurrentRow = CurrentRow - 1; + int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, this.myGridTable.GridColumnStyles.Count, false); + Debug.Assert(newCol != -1, "there should be at least a visible column, right?"); + CurrentColumn = newCol; + } + else { + int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, currentCol, false); + if (newCol == -1) { + if (currentRow == 0) + return true; + else { + // go to the previous row: + CurrentRow = CurrentRow - 1; + CurrentColumn = lastColumnMarkedVisible; + } + } else { + CurrentColumn = newCol; + } + } + break; + case Keys.Right: + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + if ((biDiKe.Modifiers & Keys.Control) == Keys.Control && !biDiKe.Alt) + { + // we should navigate to the last column that is marked as Visible + CurrentColumn = lastColumnMarkedVisible; + break; + } + + if (currentCol == lastColumnMarkedVisible && currentRow != DataGridRowsLength - 1) + { + CurrentRow = CurrentRow + 1; + // navigate to the first visible column + CurrentColumn = firstColumnMarkedVisible; + } + else { + int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, this.currentCol, true); + if (newCol == cols.Count + 1) { + // navigate to the first visible column + // and the next row + // + CurrentColumn = firstColumnMarkedVisible; + CurrentRow ++; + } + else + CurrentColumn = newCol; + } + break; + case Keys.F2: + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + Edit(); + break; + #if DEBUG + case Keys.F12: + gridState[GRIDSTATE_childLinkFocused] = false; + AddNewRow(); + break; + #endif + case Keys.Home: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + ResetSelection(); + CurrentColumn = 0; + if (biDiKe.Control && !biDiKe.Alt) { + int currentRowSaved = currentRow; + CurrentRow = 0; + + if (biDiKe.Shift) + { + // Ctrl-Shift-Home will select all the rows up to the first one + DataGridRow[] gridRows = DataGridRows; + for (int i = 0; i <= currentRowSaved; i++) + { + gridRows[i].Selected = true; + numSelectedRows ++; + } + // hide the edit box: + EndEdit(); + } + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + break; + case Keys.Delete: + gridState[GRIDSTATE_childLinkFocused] = false; + if (policy.AllowRemove && numSelectedRows > 0) { + #if DEBUG + // when the list is empty, then the position + // in the listManager is -1, and the currentPosition in the grid is 0 + if (ListManager != null && ListManager.Count > 0) { + Debug.Assert(ListManager.Position == this.currentRow, + "Current row out of sync with DataSource", + "The DataSource's Position property should be mirrored by the CurrentCell.RowNumber of the DataGrid."); + } + #endif // DEBUG + + gridState[GRIDSTATE_inDeleteRow] = true; + DeleteRows(localGridRows); + // set the currentRow to the position in the list + this.currentRow = this.listManager.Count == 0 ? 0 : this.listManager.Position; + numSelectedRows = 0; + } else { + // if we did not use the the Delete key, let the dataGridTextBox use it + return false; + } + break; + case Keys.End: + gridState[GRIDSTATE_childLinkFocused] = false; + if (this.dataGridRowsLength == 0) { + return true; + } + + ResetSelection(); + // go the the last visible column + CurrentColumn = lastColumnMarkedVisible; + + if (biDiKe.Control && !biDiKe.Alt) { + int savedCurrentRow = currentRow; + CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2:1)); + + if (biDiKe.Shift) + { + // Ctrl-Shift-Home will select all the rows up to the first one + DataGridRow[] gridRows = DataGridRows; + for (int i = savedCurrentRow; i <= currentRow; i++) + { + gridRows[i].Selected = true; + } + numSelectedRows = currentRow - savedCurrentRow + 1; + // hide the edit box + // + EndEdit(); + } + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + return true; + } + Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource"); + break; + case Keys.Enter: + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + + // yield the return key if there is no editing + if (!gridState[GRIDSTATE_isEditing]) + return false; + + // Ctrl-Enter will call EndCurrentEdit + if ((biDiKe.Modifiers & Keys.Control) != 0 && !biDiKe.Alt) + { + EndEdit(); + HandleEndCurrentEdit(); + Edit(); // put the edit box on the screen + } + else + { + // Do not commit the edit, cause reseting the + // current cell will do that + // + // CommitEdit(); + + CurrentRow = currentRow + 1; + } + + break; + case Keys.A: + gridState[GRIDSTATE_childLinkFocused] = false; + if (biDiKe.Control && !biDiKe.Alt) + { + DataGridRow [] gridRows = DataGridRows; + for (int i = 0; i < DataGridRowsLength; i++) + if (gridRows[i] is DataGridRelationshipRow) gridRows[i].Selected = true; + + numSelectedRows = DataGridRowsLength - (policy.AllowAdd ? 1 : 0); + // hide the edit box + // + EndEdit(); + return true; + } + return false; + case Keys.Escape: + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + if (gridState[GRIDSTATE_isEditing]) + { + // rollback + AbortEdit(); + + // we have to invalidate the row header ( make it display the row selector instead of the pencil ) + if (layout.RowHeadersVisible && this.currentRow > -1) { + Rectangle rowHdrRect = GetRowRect(this.currentRow); + rowHdrRect.Width = layout.RowHeaders.Width; + Invalidate(rowHdrRect); + } + + // now put the edit column back on the screen + Edit(); + } + else { + // add this protected virtual method for the XML designer team + CancelEditing(); + Edit(); + return false; + } + break; + } + return true; + } + + /// + /// + /// Previews a keyboard message and returns a value indicating if the key was + /// consumed. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) { + if (m.Msg == NativeMethods.WM_KEYDOWN) { + KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + switch (ke.KeyCode) { + case Keys.Up: + case Keys.Down: + case Keys.Prior: + case Keys.Next: + case Keys.Right: + case Keys.Left: + case Keys.Tab: + case Keys.Escape: + case Keys.Enter: + case Keys.OemMinus: + case Keys.Subtract: + case Keys.Oemplus: + case Keys.Add: + case Keys.Space: + case Keys.Home: + case Keys.End: + case Keys.F2: + case Keys.Delete: + case Keys.A: + return ProcessGridKey(ke); + } + // Ctrl-Tab will be sent as a tab paired w/ a control on the KeyUp message + // + } else if (m.Msg == NativeMethods.WM_KEYUP) { + KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + if (ke.KeyCode == Keys.Tab) + return ProcessGridKey(ke); + } + + return base.ProcessKeyPreview(ref m); + } + + /// + /// + /// + /// Gets a value indicating whether the Tab key should be processed. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected bool ProcessTabKey(Keys keyData) { + if (this.listManager == null || myGridTable == null) + return false; + bool wasEditing = false; + int columnCount = myGridTable.GridColumnStyles.Count; + bool biDi = isRightToLeft(); + ResetSelection(); + + // Try to commit changes to cell if we were editing + if (gridState[GRIDSTATE_isEditing]) { + wasEditing = true; + if (!CommitEdit()) { + //MessageBox.Show("Could not commit changes! Press Escape to abort edit"); + Edit(); // if we can't commit the value put the edit box so that the user sees where the focus is + return true; + } + } + + if ((keyData & Keys.Control) == Keys.Control) + { + // when the user hits ctrl-alt-tab just ignore it. + if ((keyData & Keys.Alt) == Keys.Alt) + return true; + + // navigate to the next control in the form + Keys ke = keyData & ~(Keys.Control); + EndEdit(); + + gridState[GRIDSTATE_editControlChanging] = true; + try { + FocusInternal(); + } finally { + gridState[GRIDSTATE_editControlChanging] = false; + } + + bool ret = false; + // SECREVIEW : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = base.ProcessDialogKey(ke); + } + finally { + CodeAccessPermission.RevertAssert(); + } + return ret; + } + + // see if the child relationships can use this TAB key + DataGridRow[] localRows = DataGridRows; + GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles; + + int lastColumnMarkedVisible = 0; + int firstColumnMarkedVisible = cols.Count-1; + // bug 70492: if we do not have any rows, then tab should move focus to the next control + // + if (localRows.Length == 0) { + EndEdit(); + + bool ret = false; + + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = base.ProcessDialogKey(keyData); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return ret; + } + + for (int i = 0; i < cols.Count; i ++) { + // if (cols[i].Visible && cols[i].PropertyDescriptor != null) { + if (cols[i].PropertyDescriptor != null) { + firstColumnMarkedVisible = i; + break; + } + } + for (int i = cols.Count - 1; i >= 0; i --) { + // if (cols[i].Visible && cols[i].PropertyDescriptor != null) { + if (cols[i].PropertyDescriptor != null) { + lastColumnMarkedVisible = i; + break; + } + } + + if (CurrentColumn == lastColumnMarkedVisible) + { + if (gridState[GRIDSTATE_childLinkFocused] || (!gridState[GRIDSTATE_childLinkFocused] && (keyData & Keys.Shift) != Keys.Shift)) { + if (localRows[CurrentRow].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft())) + { + if (cols.Count > 0) + cols[CurrentColumn].ConcedeFocus(); + gridState[GRIDSTATE_childLinkFocused] = true; + // let the grid regain focus + // introduced because of that BeginInvoke thing in the OnLeave method.... + if (gridState[GRIDSTATE_canFocus] && CanFocus && !Focused) + this.FocusInternal(); + return true; + } + } + + // actually, it turns out that we should leave the + // control if we are in the last row + if ((this.currentRow == this.DataGridRowsLength -1) && ((keyData & Keys.Shift) == 0)) { + + EndEdit(); + bool ret = false; + + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = base.ProcessDialogKey(keyData); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return ret; + } + } + + + if (CurrentColumn == firstColumnMarkedVisible) + { + // if the childLink is focused, then navigate within the relations + // in the row, otherwise expand the relations list for the row above + if (!gridState[GRIDSTATE_childLinkFocused]) { + if (CurrentRow != 0 && (keyData & Keys.Shift) == Keys.Shift) + { + if (localRows[CurrentRow - 1].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft())) + { + CurrentRow --; + if (cols.Count > 0) + cols[CurrentColumn].ConcedeFocus(); + gridState[GRIDSTATE_childLinkFocused] = true; + // let the grid regain focus + // introduced because of that BeginInvoke thing in the OnLeave method.... + if (gridState[GRIDSTATE_canFocus] && CanFocus && !Focused) + this.FocusInternal(); + return true; + } + } + } else { + if (localRows[CurrentRow].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft())) { + return true; + } else { + // we were on the firstColumn, previously the link was focused + // we have to navigate to the last column + gridState[GRIDSTATE_childLinkFocused] = false; + CurrentColumn = lastColumnMarkedVisible; + return true; + } + } + + // if we are on the first cell ( not on the addNewRow ) + // then shift - tab should move to the next control on the form + if (this.currentRow == 0 && ((keyData & Keys.Shift) == Keys.Shift)) { + EndEdit(); + bool ret = false; + + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try { + ret = base.ProcessDialogKey(keyData); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + return ret; + } + } + + // move + if ((keyData & Keys.Shift) != Keys.Shift) { + // forward + if (CurrentColumn == lastColumnMarkedVisible) { + if (CurrentRow != DataGridRowsLength - 1) + CurrentColumn = firstColumnMarkedVisible; + CurrentRow = CurrentRow + 1; + } + else { + int nextCol = MoveLeftRight(cols, currentCol, true); // true for going right; + Debug.Assert(nextCol < cols.Count, "we already checked that we are not at the lastColumnMarkedVisible"); + CurrentColumn = nextCol; + } + } + else { + // backward + if (CurrentColumn == firstColumnMarkedVisible) { + if (CurrentRow != 0) + { + CurrentColumn = lastColumnMarkedVisible; + } + if (!gridState[GRIDSTATE_childLinkFocused]) // bug 86803 + CurrentRow --; + } else if (gridState[GRIDSTATE_childLinkFocused] && CurrentColumn == lastColumnMarkedVisible) { + // part deux: when we hilite the childLink and then press shift-tab, we + // don't want to navigate at the second to last column + InvalidateRow(this.currentRow); + Edit(); + } else { + int prevCol = MoveLeftRight(cols, currentCol, false); // false for going left + Debug.Assert(prevCol != -1, "we already checked that we are not at the first columnMarked visible"); + CurrentColumn = prevCol; + } + } + + // if we got here, then invalidate childLinkFocused + // + gridState[GRIDSTATE_childLinkFocused] = false; + + // Begin another edit if we were editing before + if (wasEditing) { + ResetSelection(); + Edit(); + } + return true; + } + + /// + virtual protected void CancelEditing() { + CancelCursorUpdate(); + // yield the escape key if there is no editing + // make the last row a DataGridAddNewRow + if (gridState[GRIDSTATE_inAddNewRow]) { + gridState[GRIDSTATE_inAddNewRow] = false; + DataGridRow[] localGridRows = this.DataGridRows; + + localGridRows[DataGridRowsLength-1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength -1); + SetDataGridRows(localGridRows, DataGridRowsLength); + } + } + + internal void RecalculateFonts() { + try { + linkFont = new Font(Font, FontStyle.Underline); + } + catch { + } + fontHeight = Font.Height; + linkFontHeight = LinkFont.Height; + captionFontHeight = CaptionFont.Height; + + if (this.myGridTable == null || this.myGridTable.IsDefault) + headerFontHeight = this.HeaderFont.Height; + else + headerFontHeight = myGridTable.HeaderFont.Height; + } + + // the BackButtonClicked event: + // + /// + /// + /// Occurs when the BackButton is clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridBackButtonClickDescr) + ] + public event EventHandler BackButtonClick { + add { + Events.AddHandler(EVENT_BACKBUTTONCLICK, value); + } + remove { + Events.RemoveHandler(EVENT_BACKBUTTONCLICK, value); + } + } + + // the DownButtonClick event + // + /// + /// + /// Occurs when the Down button is clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridDownButtonClickDescr) + ] + public event EventHandler ShowParentDetailsButtonClick { + add { + Events.AddHandler(EVENT_DOWNBUTTONCLICK, value); + } + remove { + Events.RemoveHandler(EVENT_DOWNBUTTONCLICK, value); + } + } + + private void ResetMouseState() { + oldRow = -1; + gridState[GRIDSTATE_overCaption] = true; + } + + /// + /// + /// Turns off selection for all rows that are selected. + /// + protected void ResetSelection() { + if (numSelectedRows > 0) { + DataGridRow[] localGridRows = DataGridRows; + for (int i = 0; i < DataGridRowsLength; ++i) + if (localGridRows[i].Selected) + localGridRows[i].Selected = false; + } + numSelectedRows = 0; + lastRowSelected = -1; + } + + private void ResetParentRows() { + parentRows.Clear(); + originalState = null; + caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false; + caption.SetDownButtonDirection(!layout.ParentRowsVisible); + } + + /// + /// Re-initializes all UI related state. + /// + private void ResetUIState() { + gridState[GRIDSTATE_childLinkFocused] = false; + ResetSelection(); + ResetMouseState(); + PerformLayout(); + Invalidate(); // we want to invalidate after we set up the scrollbars + + // invalidate the horizontalscrollbar and the vertical scrollbar + // + if (horizScrollBar.Visible) + horizScrollBar.Invalidate(); + if (vertScrollBar.Visible) + vertScrollBar.Invalidate(); + } + + /// + /// Scrolls the datagrid down an arbritrary number of rows. + /// + private void ScrollDown(int rows) { + //Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ScrollDown, rows = " + rows.ToString()); + if (rows != 0) { + ClearRegionCache(); + + // we should put "dataGridRowsLength -1" + int newFirstRow = Math.Max(0, Math.Min(firstVisibleRow + rows, this.DataGridRowsLength -1)); + int oldFirstRow = firstVisibleRow; + firstVisibleRow = newFirstRow; + vertScrollBar.Value = newFirstRow; + bool wasEditing = this.gridState[GRIDSTATE_isEditing]; + ComputeVisibleRows(); + + if (gridState[GRIDSTATE_isScrolling]) { + Edit(); + // isScrolling is set to TRUE when the user scrolls. + // once we move the edit box, we finished processing the scroll event, so set isScrolling to FALSE + // to set isScrolling to TRUE, we need another scroll event. + gridState[GRIDSTATE_isScrolling] = false; + } else { + EndEdit(); + } + + int deltaY = ComputeRowDelta(oldFirstRow, newFirstRow); + Rectangle rowsRect = layout.Data; + if (layout.RowHeadersVisible) + rowsRect = Rectangle.Union(rowsRect, layout.RowHeaders); + NativeMethods.RECT scrollArea = NativeMethods.RECT.FromXYWH(rowsRect.X, rowsRect.Y, rowsRect.Width, rowsRect.Height); + SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), 0, deltaY, ref scrollArea, ref scrollArea); + OnScroll(EventArgs.Empty); + + if (wasEditing) { + // invalidate the rowHeader for the + InvalidateRowHeader(currentRow); + } + } + } + + /// + /// Scrolls the datagrid right an arbritrary number of columns. + /// + private void ScrollRight(int columns) { + Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ScrollRight, columns = " + columns.ToString(CultureInfo.InvariantCulture)); + int newCol = firstVisibleCol + columns; + + GridColumnStylesCollection gridColumns = myGridTable.GridColumnStyles; + int newColOffset = 0; + int nGridCols = gridColumns.Count; + int nVisibleCols = 0; + + // if we try to scroll past the last totally visible column, + // then the toolTips will dissapear + if (this.myGridTable.IsDefault) + nVisibleCols = nGridCols; + else + for (int i = 0; i < nGridCols; i++) + if (gridColumns[i].PropertyDescriptor != null) + nVisibleCols ++; + + if (this.lastTotallyVisibleCol == nVisibleCols - 1 && columns > 0 || + this.firstVisibleCol == 0 && columns < 0 && negOffset == 0) + return; + + newCol = Math.Min(newCol, nGridCols - 1); + + for (int i = 0; i < newCol; i++) + // if (gridColumns[i].Visible && gridColumns[i].PropertyDescriptor != null) + if (gridColumns[i].PropertyDescriptor != null) + newColOffset += gridColumns[i].Width; + + HorizontalOffset = newColOffset; + } + + /// + /// Scrolls a given column into visibility. + /// + private void ScrollToColumn(int targetCol) { + // do not flush the columns to the left + // so, scroll only as many columns as is necessary. + // + + int dCols = targetCol - firstVisibleCol; + + if (targetCol > lastTotallyVisibleCol && lastTotallyVisibleCol != -1) + dCols = targetCol - lastTotallyVisibleCol; + + // if only part of the currentCol is visible + // then we should still scroll + if (dCols != 0 || negOffset != 0) + ScrollRight(dCols); + } + + /// + /// + /// Selects a given row + /// + public void Select(int row) { + // SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change + Debug.WriteLineIf(CompModSwitches.DataGridSelection.TraceVerbose, "Selecting row " + row.ToString(CultureInfo.InvariantCulture)); + DataGridRow[] localGridRows = DataGridRows; + if (!localGridRows[row].Selected) { + localGridRows[row].Selected = true; + numSelectedRows++; + } + + // when selecting a row, hide the edit box + // + EndEdit(); + } + + // this function will pair the listManager w/ a table from the TableStylesCollection. + // and for each column in the TableStylesCollection will pair them w/ a propertyDescriptor + // from the listManager + // + // prerequisite: the current table is either the default table, or has the same name as the + // list in the listManager. + // + private void PairTableStylesAndGridColumns(CurrencyManager lm, DataGridTableStyle gridTable, bool forceColumnCreation) { + PropertyDescriptorCollection props = lm.GetItemProperties(); + GridColumnStylesCollection gridCols = gridTable.GridColumnStyles; + + // ]it is possible to have a dataTable w/ an empty string for a name. + if (!gridTable.IsDefault && String.Compare(lm.GetListName(), gridTable.MappingName, true, CultureInfo.InvariantCulture) == 0) { + // we will force column creation only at runtime + if (gridTable.GridColumnStyles.Count == 0 && !DesignMode) { + // we have to create some default columns for each of the propertyDescriptors + // + if (forceColumnCreation) + gridTable.SetGridColumnStylesCollection(lm); + else + gridTable.SetRelationsList(lm); + } else { + // it may the case that the user will have two lists w/ the same name. + // When switching binding between those different lists, we need to invalidate + // the propertyDescriptors from the current gridColumns + // + for (int i = 0; i < gridCols.Count; i ++) + gridCols[i].PropertyDescriptor = null; + + // pair the propertyDescriptor from each column to the actual property descriptor + // from the listManager + // + for (int i = 0; i < props.Count; i ++) { + DataGridColumnStyle col = gridCols.MapColumnStyleToPropertyName(props[i].Name); + if (col != null) { + col.PropertyDescriptor = props[i]; + } + } + // TableStyle::SetGridColumnStylesCollection will also set the + // relations list in the tableStyle. + gridTable.SetRelationsList(lm); + } + } else { + // we should put an assert, that this is the default Table Style + #if DEBUG + Debug.Assert(gridTable.IsDefault, "if we don't have a match, then the dataGRid should have the default table"); + #endif // DEBUG + gridTable.SetGridColumnStylesCollection(lm); + if (gridTable.GridColumnStyles.Count > 0 && gridTable.GridColumnStyles[0].Width == -1) { + #if DEBUG + GridColumnStylesCollection cols = gridTable.GridColumnStyles; + for (int i = 0; i < cols.Count; i++) { + Debug.Assert(cols[i].Width == -1, "if one column's width is not initialized, the same should be happening for the rest of the columns"); + } + #endif // DEBUG + InitializeColumnWidths(); + } + } + } + + /// + /// Sets the current GridTable for the DataGrid. + /// This GridTable is the table which is currently + /// being displayed on the grid. + /// + internal void SetDataGridTable(DataGridTableStyle newTable, bool forceColumnCreation) + { + // we have to listen to the dataGridTable for the propertyChangedEvent + if(this.myGridTable != null) { + // unwire the propertyChanged event + UnWireTableStylePropChanged(myGridTable); + + if (myGridTable.IsDefault) { + // reset the propertyDescriptors on the default table. + myGridTable.GridColumnStyles.ResetPropertyDescriptors(); + + // reset the relationship list from the default table + myGridTable.ResetRelationsList(); + } + } + + myGridTable = newTable; + + WireTableStylePropChanged(myGridTable); + + this.layout.RowHeadersVisible = newTable.IsDefault ? this.RowHeadersVisible : newTable.RowHeadersVisible; + + // we need to force the grid into the dataGridTableStyle + // this way the controls in the columns will be parented + // consider this scenario: when the user finished InitializeComponent, it added + // a bunch of tables. all of those tables will have the DataGrid property set to this + // grid. however, in InitializeComponent the tables will not have parented the + // edit controls w/ the grid. + // + // the code in DataGridTextBoxColumn already checks to see if the edits are parented + // before parenting them. + // + if (newTable != null) + newTable.DataGrid = this; + + // pair the tableStyles and GridColumns + // + if (this.listManager != null) + PairTableStylesAndGridColumns(this.listManager, this.myGridTable, forceColumnCreation); + + // reset the relations UI on the newTable + if (newTable != null) + newTable.ResetRelationsUI(); + + // set the isNavigating to false + gridState[GRIDSTATE_isNavigating] = false; + + horizScrollBar.Value = 0; + firstVisibleRow = 0; + currentCol = 0; + // if we add a tableStyle that mapps to the + // current listName, then we should set the currentRow to the + // position in the listManager + if (this.listManager == null) + currentRow = 0; + else + currentRow = this.listManager.Position == -1 ? 0 : listManager.Position; + ResetHorizontalOffset(); + negOffset = 0; + ResetUIState(); + + // check the hierarchy + checkHierarchy = true; + } + + /// + /// Scrolls the data area down to make room for the parent rows + /// and lays out the different regions of the DataGrid. + /// + internal void SetParentRowsVisibility(bool visible) { + Rectangle parentRowsRect = layout.ParentRows; + Rectangle underParentRows = layout.Data; + + if (layout.RowHeadersVisible) { + underParentRows.X -= isRightToLeft() ? 0 : layout.RowHeaders.Width; + underParentRows.Width += layout.RowHeaders.Width; + } + if (layout.ColumnHeadersVisible) { + underParentRows.Y -= layout.ColumnHeaders.Height; + underParentRows.Height += layout.ColumnHeaders.Height; + } + + // hide the Edit Box + EndEdit(); + + if (visible) { + /* + RECT scrollArea = RECT.FromXYWH(underParentRows.X, underParentRows.Y, underParentRows.Width, underParentRows.Height); + + Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Making parent rows visible."); + SafeNativeMethods.ScrollWindow(this.Handle, 0, parentRowsRect.Height, + ref scrollArea, ref scrollArea); + */ + + layout.ParentRowsVisible = true; + + PerformLayout(); + + Invalidate(); + + } + else { + // Rectangle scrollArea = Rectangle.Union(layout.ParentRows, underParentRows); + // RECT scrollRECT = RECT.FromXYWH(scrollArea.X, scrollArea.Y, scrollArea.Width, scrollArea.Height); + + NativeMethods.RECT scrollRECT = NativeMethods.RECT.FromXYWH(underParentRows.X, underParentRows.Y - layout.ParentRows.Height, underParentRows.Width, underParentRows.Height + layout.ParentRows.Height); + + SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), 0, -parentRowsRect.Height, ref scrollRECT, ref scrollRECT); + + // If the vertical scrollbar was visible before and not after + // the ScrollWindow call, then we will not get invalidated + // completely. We need to translate the visual bounds of + // the scrollbar's old location up and invalidate. + // + if (vertScrollBar.Visible) { + Rectangle fixupRect = vertScrollBar.Bounds; + fixupRect.Y -= parentRowsRect.Height; + fixupRect.Height += parentRowsRect.Height; + Invalidate(fixupRect); + } + + Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Making parent rows invisible."); + layout.ParentRowsVisible = false; + PerformLayout(); + } + } + + /// + /// Sets whether a row is expanded or not. + /// + private void SetRowExpansionState(int row, bool expanded) { + if (row < -1 || row > DataGridRowsLength - (policy.AllowAdd ? 2:1)) { + throw new ArgumentOutOfRangeException("row"); + } + + DataGridRow[] localGridRows = DataGridRows; + if (row == -1) { + DataGridRelationshipRow[] expandableRows = GetExpandableRows(); + bool repositionEditControl = false; + + for (int r = 0; r < expandableRows.Length; ++r) { + if (expandableRows[r].Expanded != expanded) + { + expandableRows[r].Expanded = expanded; + repositionEditControl = true; + } + } + if (repositionEditControl) + { + // we need to reposition the edit control + if (gridState[GRIDSTATE_isNavigating] || gridState[GRIDSTATE_isEditing]) + { + ResetSelection(); + Edit(); + } + } + } + else if (localGridRows[row] is DataGridRelationshipRow) { + DataGridRelationshipRow expandableRow = (DataGridRelationshipRow)localGridRows[row]; + if (expandableRow.Expanded != expanded) + { + // we need to reposition the edit control + if (gridState[GRIDSTATE_isNavigating] || gridState[GRIDSTATE_isEditing]) + { + ResetSelection(); + Edit(); + } + + expandableRow.Expanded = expanded; + } + } + } + + private void ObjectSiteChange(IContainer container, IComponent component, bool site) { + if (site) { + if (component.Site == null) { + container.Add(component); + } + } + else { + if (component.Site != null && component.Site.Container == container) { + container.Remove(component); + } + } + } + + /// + /// + public void SubObjectsSiteChange(bool site) { + DataGrid dgrid = this; + if (dgrid.DesignMode && dgrid.Site != null) { + IDesignerHost host = (IDesignerHost)dgrid.Site.GetService(typeof(IDesignerHost)); + if (host != null) { + DesignerTransaction trans = host.CreateTransaction(); + try { + IContainer container = dgrid.Site.Container; + + DataGridTableStyle[] tables = new DataGridTableStyle[dgrid.TableStyles.Count]; + dgrid.TableStyles.CopyTo(tables, 0); + + for (int i = 0; i < tables.Length; i++) { + DataGridTableStyle table = tables[i]; + ObjectSiteChange(container, table, site); + + DataGridColumnStyle[] columns = new DataGridColumnStyle[table.GridColumnStyles.Count]; + table.GridColumnStyles.CopyTo(columns, 0); + + for (int j = 0; j < columns.Length; j++) { + DataGridColumnStyle column = columns[j]; + ObjectSiteChange(container, column, site); + } + } + } + finally { + trans.Commit(); + } + } + } + } + + /// + /// + /// Unselects a given row + /// + public void UnSelect(int row) { + // SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change + Debug.WriteLineIf(CompModSwitches.DataGridSelection.TraceVerbose, "DataGridSelection: Unselecting row " + row.ToString(CultureInfo.InvariantCulture)); + DataGridRow[] localGridRows = DataGridRows; + if (localGridRows[row].Selected) { + localGridRows[row].Selected = false; + numSelectedRows--; + } + } + + /// + /// Asks the cursor to update. + /// + private void UpdateListManager() { + Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: Requesting EndEdit()"); + try { + if (this.listManager != null) { + EndEdit(); + this.listManager.EndCurrentEdit(); + } + } + catch { + } + } + + /// + /// + /// + /// Will return the string that will be used as a delimiter between columns + /// when copying rows contents to the Clipboard. + /// At the moment, return "\t" + /// + protected virtual string GetOutputTextDelimiter() { + return "\t"; + } + + /// + /// The accessible object class for a DataGrid. The child accessible objects + /// are accessible objects corresponding to the propertygrid entries. + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class DataGridAccessibleObject : ControlAccessibleObject { + /// + /// Construct a PropertyGridViewAccessibleObject + /// + public DataGridAccessibleObject(DataGrid owner) : base(owner) { + } + + internal DataGrid DataGrid { + get { + return (DataGrid)Owner; + } + } + + private int ColumnCountPrivate { + get { + return ((DataGrid)Owner).myGridTable.GridColumnStyles.Count; + } + } + + private int RowCountPrivate { + get { + return ((DataGrid)Owner).dataGridRows.Length; + } + } + + public override string Name { + get { + // Special case: If an explicit name has been set in the AccessibleName property, use that. + // Note: Any non-null value in AccessibleName overrides the default accessible name logic, + // even an empty string (this is the only way to *force* the accessible name to be blank). + string name = Owner.AccessibleName; + if (name != null) { + return name; + } + + // Otherwise just return the default label string, minus any mnemonics + return "DataGrid"; + } + + set { + // If anyone tries to set the accessible name, just cache the value in the control's + // AccessibleName property. This value will then end up overriding the normal accessible + // name logic, until such time as AccessibleName is set back to null. + Owner.AccessibleName = value; + } + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.Table; + } + } + public override AccessibleObject GetChild(int index) { + DataGrid dataGrid = (DataGrid)Owner; + + int cols = ColumnCountPrivate; + int rows = RowCountPrivate; + + if (dataGrid.dataGridRows == null) { + dataGrid.CreateDataGridRows(); + } + + if (index < 1) { + return dataGrid.ParentRowsAccessibleObject; + } + else { + index -= 1; + if (index < cols) { + return dataGrid.myGridTable.GridColumnStyles[index].HeaderAccessibleObject; + } + else { + index -= cols; + + if (index < rows) { + Debug.Assert(dataGrid.dataGridRows[index].RowNumber == index, "Row number is wrong!"); + return dataGrid.dataGridRows[index].AccessibleObject; + } + else { + index -= rows; + + if (dataGrid.horizScrollBar.Visible) { + if (index == 0) { + return dataGrid.horizScrollBar.AccessibilityObject; + } + index --; + } + + if (dataGrid.vertScrollBar.Visible) { + if (index == 0) { + return dataGrid.vertScrollBar.AccessibilityObject; + } + index --; + } + + int colCount = dataGrid.myGridTable.GridColumnStyles.Count; + int rowCount = dataGrid.dataGridRows.Length; + int currentRow = index / colCount; + int currentCol = index % colCount; + + if (currentRow < dataGrid.dataGridRows.Length && currentCol < dataGrid.myGridTable.GridColumnStyles.Count) { + return dataGrid.dataGridRows[currentRow].AccessibleObject.GetChild(currentCol); + } + } + } + } + + return null; + } + + public override int GetChildCount() { + int n = 1 + ColumnCountPrivate + ((DataGrid)Owner).DataGridRowsLength; + if (DataGrid.horizScrollBar.Visible) { + n++; + } + if (DataGrid.vertScrollBar.Visible) { + n++; + } + n += this.DataGrid.DataGridRows.Length * this.DataGrid.myGridTable.GridColumnStyles.Count; + return n; + } + + public override AccessibleObject GetFocused() { + if (DataGrid.Focused) { + return GetSelected(); + } + + return null; + } + + public override AccessibleObject GetSelected() { + if (this.DataGrid.DataGridRows.Length == 0 || this.DataGrid.myGridTable.GridColumnStyles.Count == 0) { + return null; + } + + DataGridCell cell = DataGrid.CurrentCell; + return GetChild(1 + ColumnCountPrivate + cell.RowNumber).GetChild(cell.ColumnNumber); + } + + public override AccessibleObject HitTest(int x, int y) { + Point client = DataGrid.PointToClient(new Point(x, y)); + HitTestInfo hti = DataGrid.HitTest(client.X, client.Y); + + switch (hti.Type) { + case HitTestType.RowHeader: + return GetChild(1 + ColumnCountPrivate + hti.Row); + case HitTestType.Cell: + return GetChild(1 + ColumnCountPrivate + hti.Row).GetChild(hti.Column); + case HitTestType.ColumnHeader: + return GetChild(1 + hti.Column); + case HitTestType.ParentRows: + return DataGrid.ParentRowsAccessibleObject; + case HitTestType.None: + case HitTestType.ColumnResize: + case HitTestType.RowResize: + case HitTestType.Caption: + break; + } + + return null; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + // We're only handling FirstChild and LastChild here + if (GetChildCount() > 0) { + switch(navdir) { + case AccessibleNavigation.FirstChild: + return GetChild(0); + case AccessibleNavigation.LastChild: + return GetChild(GetChildCount() - 1); + } + } + + return null; // Perform default behavior + } + } + + + + // + // This simple data structure holds all of the layout information + // for the DataGrid. + // + internal class LayoutData { + internal bool dirty = true; + // region inside the Control's borders. + public Rectangle Inside = Rectangle.Empty; + + public Rectangle RowHeaders = Rectangle.Empty; + + public Rectangle TopLeftHeader = Rectangle.Empty; + public Rectangle ColumnHeaders = Rectangle.Empty; + public Rectangle Data = Rectangle.Empty; + + public Rectangle Caption = Rectangle.Empty; + public Rectangle ParentRows = Rectangle.Empty; + + public Rectangle ResizeBoxRect = Rectangle.Empty; + + public bool ColumnHeadersVisible; + public bool RowHeadersVisible; + public bool CaptionVisible; + public bool ParentRowsVisible; + + // used for resizing. + public Rectangle ClientRectangle = Rectangle.Empty; + + public LayoutData() { + } + + public LayoutData(LayoutData src) { + GrabLayout(src); + } + + private void GrabLayout(LayoutData src) { + this.Inside = src.Inside; + this.TopLeftHeader = src.TopLeftHeader; + this.ColumnHeaders = src.ColumnHeaders; + this.RowHeaders = src.RowHeaders; + this.Data = src.Data; + this.Caption = src.Caption; + this.ParentRows = src.ParentRows; + this.ResizeBoxRect = src.ResizeBoxRect; + this.ColumnHeadersVisible = src.ColumnHeadersVisible; + this.RowHeadersVisible = src.RowHeadersVisible; + this.CaptionVisible = src.CaptionVisible; + this.ParentRowsVisible = src.ParentRowsVisible; + this.ClientRectangle = src.ClientRectangle; + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(200); + sb.Append(base.ToString()); + sb.Append(" { \n"); + sb.Append("Inside = "); + sb.Append(Inside.ToString()); + sb.Append('\n'); + sb.Append("TopLeftHeader = "); + sb.Append(TopLeftHeader.ToString()); + sb.Append('\n'); + sb.Append("ColumnHeaders = "); + sb.Append(ColumnHeaders.ToString()); + sb.Append('\n'); + sb.Append("RowHeaders = "); + sb.Append(RowHeaders.ToString()); + sb.Append('\n'); + sb.Append("Data = "); + sb.Append(Data.ToString()); + sb.Append('\n'); + sb.Append("Caption = "); + sb.Append(Caption.ToString()); + sb.Append('\n'); + sb.Append("ParentRows = "); + sb.Append(ParentRows.ToString()); + sb.Append('\n'); + sb.Append("ResizeBoxRect = "); + sb.Append(ResizeBoxRect.ToString()); + sb.Append('\n'); + sb.Append("ColumnHeadersVisible = "); + sb.Append(ColumnHeadersVisible.ToString()); + sb.Append('\n'); + sb.Append("RowHeadersVisible = "); + sb.Append(RowHeadersVisible.ToString()); + sb.Append('\n'); + sb.Append("CaptionVisible = "); + sb.Append(CaptionVisible.ToString()); + sb.Append('\n'); + sb.Append("ParentRowsVisible = "); + sb.Append(ParentRowsVisible.ToString()); + sb.Append('\n'); + sb.Append("ClientRectangle = "); + sb.Append(ClientRectangle.ToString()); + sb.Append(" } "); + return sb.ToString(); + } + } + + /// + /// + /// Contains information + /// about the part of the control the user + /// has clicked. This class cannot be inherited. + /// + public sealed class HitTestInfo { + internal HitTestType type = HitTestType.None; + + internal int row; + internal int col; + + /// + /// + /// Allows the object to inform you the + /// extent of the grid. + /// + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // DataGrid.HitTestInfo type is + // actually immutable. + ] + public static readonly HitTestInfo Nowhere = new HitTestInfo(); + + internal HitTestInfo() { + type = (HitTestType)0; + row = col = -1; + } + + internal HitTestInfo(HitTestType type) { + this.type = type; + row = col = -1; + } + + /// + /// + /// Gets the number of the clicked column. + /// + public int Column { + get { + return col; + } + } + + /// + /// + /// Gets the + /// number of the clicked row. + /// + public int Row { + get { + return row; + } + } + + /// + /// + /// Gets the part of the control, other than the row or column, that was + /// clicked. + /// + public HitTestType Type { + get { + return type; + } + } + + /// + /// + /// Indicates whether two objects are identical. + /// + public override bool Equals(object value) { + if (value is HitTestInfo) { + HitTestInfo ci =(HitTestInfo) value; + return(type == ci.type && + row == ci.row && + col == ci.col); + } + return false; + } + + /// + /// + /// Gets the hash code for the instance. + /// + public override int GetHashCode() { + return(int)type +(row << 8) +(col << 16); + } + + /// + /// + /// Gets the type, row number, and column number. + /// + public override string ToString() { + return "{ " + ((type).ToString()) + "," + row.ToString(CultureInfo.InvariantCulture) + "," + col.ToString(CultureInfo.InvariantCulture) + "}"; + } + } + + /// + /// + /// Specifies the part of the control the user has clicked. + /// + [Flags] + public enum HitTestType { + /// + None = 0x00000000, + + /// + Cell = 0x00000001, + + /// + ColumnHeader = 0x00000002, + + /// + RowHeader = 0x00000004, + + /// + ColumnResize = 0x00000008, + + /// + RowResize = 0x00000010, + + /// + Caption = 0x00000020, + + /// + ParentRows = 0x00000040 + } + + /// + /// Holds policy information for what the grid can and cannot do. + /// + private class Policy { + + private bool allowAdd = true; + private bool allowEdit = true; + private bool allowRemove = true; + + public Policy() { + } + + public bool AllowAdd { + get { + return allowAdd; + } + set { + if (allowAdd != value) { + allowAdd = value; + } + } + } + + public bool AllowEdit { + get { + return allowEdit; + } + set { + if (allowEdit != value) { + allowEdit = value; + } + } + } + + public bool AllowRemove { + get { + return allowRemove; + } + set { + if (allowRemove != value) { + allowRemove = value; + } + } + } + + // returns true if the UI needs to be updated (here because addnew has changed) + public bool UpdatePolicy(CurrencyManager listManager, bool gridReadOnly) + { + bool change = false; + // only IBindingList can have an AddNewRow + IBindingList bl = listManager == null ? null : listManager.List as IBindingList; + if (listManager == null) { + if (!allowAdd) + change = true; + allowAdd = allowEdit = allowRemove = true; + } + else { + if (AllowAdd != listManager.AllowAdd && !gridReadOnly) + change = true; + AllowAdd= listManager.AllowAdd && !gridReadOnly && bl != null && bl.SupportsChangeNotification; + AllowEdit= listManager.AllowEdit && !gridReadOnly; + AllowRemove = listManager.AllowRemove && !gridReadOnly && bl != null && bl.SupportsChangeNotification; // bug 86061 + } + return change; + } + } + + // + // Given the x coordinate and the Width of rectangle R1 inside rectangle rect, + // this function returns the x coordinate of the rectangle that + // corresponds to the Bi-Di transformation + // + private int MirrorRectangle(Rectangle R1, Rectangle rect, bool rightToLeft) + { + if (rightToLeft) + return rect.Right + rect.X - R1.Right; + else + return R1.X; + } + + // + // Given the x coordinate of a point inside rectangle rect, + // this function returns the x coordinate of the point that + // corresponds to the Bi-Di transformation + // + private int MirrorPoint(int x, Rectangle rect, bool rightToLeft) + { + if (rightToLeft) + return rect.Right + rect.X - x; + else + return x; + } + + // This function will return true if the RightToLeft property of the dataGrid is + // set to YES + private bool isRightToLeft() + { + return ( RightToLeft == RightToLeft.Yes); + } + } + } + diff --git a/WindowsForms/Managed/System/WinForms/DataGridAddNewRow.cs b/WindowsForms/Managed/System/WinForms/DataGridAddNewRow.cs new file mode 100644 index 000000000..96e4bc50e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridAddNewRow.cs @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// This class fully encapsulates the painting logic for an addnew row + /// appearing in a DataGrid. + /// + internal class DataGridAddNewRow : DataGridRow { + + private bool dataBound = false; + + public DataGridAddNewRow(DataGrid dGrid, DataGridTableStyle gridTable, int rowNum) + : base(dGrid, gridTable, rowNum) { + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + /// + /// Since the DataView does not return a valid DataRow for + /// a newly added row, the DataGrid sets this property to + /// true to signal that the AddNewRow can safely render + /// row contents and permit editing, etc because a DataRecord + /// exists in the cursor that created this row. + /// + public bool DataBound { + get { + return dataBound; + } + set { + dataBound = value; + } + } + + public override void OnEdit() { + if (!DataBound) { + DataGrid.AddNewRow(); + } + } + + public override void OnRowLeave() { + if (DataBound) + DataBound = false; + } + + // the addNewRow has nothing to do with losing focus + // + internal override void LoseChildFocus(Rectangle rowHeader, bool alignToRight) + { + } + + // the newDataRow has nothing to do with TAB keys + // + internal override bool ProcessTabKey(Keys keyData, Rectangle rowHeaders, bool alignToRight) + { + return false; + } + + /// + /// Paints the row. + /// + public override int Paint(Graphics g, Rectangle bounds, Rectangle trueRowBounds, int firstVisibleColumn, int columnCount) + { + return Paint(g, bounds, trueRowBounds, firstVisibleColumn, columnCount, false); + } + + public override int Paint(Graphics g, + Rectangle bounds, + Rectangle trueRowBounds, + int firstVisibleColumn, + int columnCount, + bool alignToRight) { + Rectangle dataBounds = bounds; + DataGridLineStyle gridStyle; + if (this.dgTable.IsDefault) + gridStyle = this.DataGrid.GridLineStyle; + else + gridStyle = this.dgTable.GridLineStyle; + int bWidth = this.DataGrid == null ? 0 : gridStyle == DataGridLineStyle.Solid ? 1 : 0; + dataBounds.Height -= bWidth; + int cx = base.PaintData(g, dataBounds, firstVisibleColumn, columnCount, alignToRight); + + if (bWidth > 0) + PaintBottomBorder(g, bounds, cx, bWidth, alignToRight); + return cx; + } + + protected override void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, + Brush backBr, Brush foreBrush, bool alignToRight) { + if (DataBound) { + CurrencyManager listManager = DataGrid.ListManager; + column.Paint(g, cellBounds, listManager, this.RowNumber, alignToRight); + } + else { + base.PaintCellContents(g, cellBounds, column, backBr, foreBrush, alignToRight); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridBoolColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridBoolColumn.cs new file mode 100644 index 000000000..06c5a0a9f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridBoolColumn.cs @@ -0,0 +1,508 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Specifies a column in + /// which each cell contains a check box for representing + /// a boolean value. + /// + public class DataGridBoolColumn : DataGridColumnStyle { + private static readonly int idealCheckSize = 14; + + private bool isEditing = false; + private bool isSelected = false; + private bool allowNull = true; + private int editingRow = -1; + private object currentValue = Convert.DBNull; + + private object trueValue = true; + private object falseValue = false; + private object nullValue = Convert.DBNull; + + private static readonly object EventTrueValue = new object(); + private static readonly object EventFalseValue = new object(); + private static readonly object EventAllowNull = new object(); + + /// + /// + /// Initializes a new instance of the class. + /// + public DataGridBoolColumn() : base() {} + + /// + /// + /// Initializes a new instance of a with the specified . + /// + public DataGridBoolColumn(PropertyDescriptor prop) + : base(prop) {} + + /// + /// + /// [To be supplied.] + /// + public DataGridBoolColumn(PropertyDescriptor prop, bool isDefault) + : base(prop, isDefault){} + + /// + /// + /// Gets or sets the actual value used when setting the + /// value of the column to . + /// + [TypeConverterAttribute(typeof(StringConverter)), + DefaultValue(true)] + public object TrueValue { + get { + return trueValue; + } + set { + if (!trueValue.Equals(value)) { + this.trueValue = value; + OnTrueValueChanged(EventArgs.Empty); + Invalidate(); + } + } + } + + /// + public event EventHandler TrueValueChanged { + add { + Events.AddHandler(EventTrueValue, value); + } + remove { + Events.RemoveHandler(EventTrueValue, value); + } + } + + /// + /// + /// Gets or sets the actual value used when setting the value of the column to + /// . + /// + [TypeConverterAttribute(typeof(StringConverter)), DefaultValue(false)] + public object FalseValue { + get { + return falseValue; + } + set { + if (!falseValue.Equals(value)) { + this.falseValue = value; + OnFalseValueChanged(EventArgs.Empty); + Invalidate(); + } + } + } + + /// + public event EventHandler FalseValueChanged { + add { + Events.AddHandler(EventFalseValue, value); + } + remove { + Events.RemoveHandler(EventFalseValue, value); + } + } + + /// + /// + /// Gets or sets the actual value used when setting the value of the column to + /// . + /// + [TypeConverterAttribute(typeof(StringConverter))] + public object NullValue { + get { + return nullValue; + } + set { + if (!nullValue.Equals(value)) { + this.nullValue = value; + OnFalseValueChanged(EventArgs.Empty); + Invalidate(); + } + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + // when the grid is in addNewRow it means that the user did not start typing + // so there is no data to be pushed back into the backEnd. + // make isEditing false so that in the Commit call we do not do any work. + // + /// + protected internal override void ConcedeFocus() { + base.ConcedeFocus(); + this.isSelected = false; + this.isEditing = false; + } + + private Rectangle GetCheckBoxBounds(Rectangle bounds, bool alignToRight) { + if (alignToRight) + return new Rectangle(bounds.X +((bounds.Width - idealCheckSize) /2), + bounds.Y +((bounds.Height - idealCheckSize) / 2), + bounds.Width < idealCheckSize ? bounds.Width : idealCheckSize, + idealCheckSize); + else + return new Rectangle(Math.Max(0,bounds.X +((bounds.Width - idealCheckSize) /2)), + Math.Max(0,bounds.Y +((bounds.Height - idealCheckSize) / 2)), + bounds.Width < idealCheckSize ? bounds.Width : idealCheckSize, + idealCheckSize); + } + + /// + /// + /// Gets the value at the specified row. + /// + protected internal override object GetColumnValueAtRow(CurrencyManager lm, int row) { + object baseValue = base.GetColumnValueAtRow(lm, row); + object value = Convert.DBNull; + if (baseValue.Equals(trueValue)) { + value = true; + } + else if (baseValue.Equals(falseValue)) { + value = false; + } + return value; + } + + private bool IsReadOnly() { + bool ret = this.ReadOnly; + if (this.DataGridTableStyle != null) { + ret = ret || this.DataGridTableStyle.ReadOnly; + if (this.DataGridTableStyle.DataGrid != null) + ret = ret || this.DataGridTableStyle.DataGrid.ReadOnly; + } + return ret; + } + + /// + /// + /// Sets the value a a specified row. + /// + protected internal override void SetColumnValueAtRow(CurrencyManager lm, int row, object value) { + object baseValue = null; + if (true.Equals(value)) { + baseValue = TrueValue; + } + else if (false.Equals(value)) { + baseValue = FalseValue; + } + else if (Convert.IsDBNull(value)) { + baseValue = NullValue; + } + currentValue = baseValue; + base.SetColumnValueAtRow(lm, row, baseValue); + } + + /// + /// + /// Gets the optimum width and height of a cell given + /// a specific value to contain. + /// + protected internal override Size GetPreferredSize(Graphics g, object value) { + return new Size(idealCheckSize+2, idealCheckSize+2); + } + + /// + /// + /// Gets + /// the height of a cell in a column. + /// + protected internal override int GetMinimumHeight() { + return idealCheckSize+2; + } + + /// + /// + /// + /// Gets the height used when resizing columns. + /// + /// + protected internal override int GetPreferredHeight(Graphics g, object value) + { + return idealCheckSize + 2; + } + + /// + /// + /// + /// Initiates a request to interrupt an edit procedure. + /// + /// + protected internal override void Abort(int rowNum) { + isSelected = false; + isEditing = false; + Invalidate(); + return; + } + + /// + /// + /// + /// Initiates a request to complete an editing procedure. + /// + /// + protected internal override bool Commit(CurrencyManager dataSource, int rowNum) { + isSelected = false; + // always invalidate + Invalidate(); + if (!isEditing) + return true; + + SetColumnValueAtRow(dataSource, rowNum, currentValue); + isEditing = false; + return true; + } + + /// + /// + /// + /// Prepares the cell for editing a value. + /// + /// + protected internal override void Edit(CurrencyManager source, + int rowNum, + Rectangle bounds, + bool readOnly, + string displayText, + bool cellIsVisible) + { + // toggle state right now... + isSelected = true; + + // move the focus away from the previous column and give it to the grid + // + DataGrid grid = this.DataGridTableStyle.DataGrid; + if (!grid.Focused) + grid.FocusInternal(); + + if (!readOnly && !IsReadOnly()) { + editingRow = rowNum; + currentValue = GetColumnValueAtRow(source, rowNum); + } + + base.Invalidate(); + } + + /// + /// + /// Provides a handler for determining which key was pressed, and whether to + /// process it. + /// + /// + internal override bool KeyPress(int rowNum, Keys keyData) { + if (isSelected && editingRow == rowNum && !IsReadOnly()) { + if ((keyData & Keys.KeyCode) == Keys.Space) { + ToggleValue(); + Invalidate(); + return true; + } + } + return base.KeyPress(rowNum, keyData); + } + + /// + /// + /// + /// Indicates whether the a mouse down event occurred at the specified row, at + /// the specified x and y coordinates. + /// + /// + internal override bool MouseDown(int rowNum, int x, int y) { + base.MouseDown(rowNum, x, y); + if (isSelected && editingRow == rowNum && !IsReadOnly()) { + ToggleValue(); + Invalidate(); + return true; + } + return false; + } + + private void OnTrueValueChanged(EventArgs e) { + EventHandler eh = this.Events[EventTrueValue] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnFalseValueChanged(EventArgs e) { + EventHandler eh = this.Events[EventFalseValue] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnAllowNullChanged(EventArgs e) { + EventHandler eh = this.Events[EventAllowNull] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + /// + /// + /// Draws the + /// with the given , + /// and row number. + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum) + { + Paint(g,bounds,source, rowNum, false); + } + + /// + /// + /// + /// Draws the + /// with the given , , + /// row number, and alignment settings. + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight) { + Paint(g,bounds,source, rowNum, this.DataGridTableStyle.BackBrush, this.DataGridTableStyle.ForeBrush, alignToRight); + } + + /// + /// + /// Draws the with the given , , + /// row number, , and . + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, + Brush backBrush, Brush foreBrush, + bool alignToRight) { + object value = (isEditing && editingRow == rowNum) ? currentValue : GetColumnValueAtRow(source, rowNum); + ButtonState checkedState = ButtonState.Inactive; + if (!Convert.IsDBNull(value)) { + checkedState = ((bool)value ? ButtonState.Checked : ButtonState.Normal); + } + + Rectangle box = GetCheckBoxBounds(bounds, alignToRight); + + Region r = g.Clip; + g.ExcludeClip(box); + + System.Drawing.Brush selectionBrush = this.DataGridTableStyle.IsDefault ? this.DataGridTableStyle.DataGrid.SelectionBackBrush : this.DataGridTableStyle.SelectionBackBrush; + if (isSelected && editingRow == rowNum && !IsReadOnly()) { + g.FillRectangle(selectionBrush, bounds); + } + else + g.FillRectangle(backBrush, bounds); + g.Clip = r; + + if (checkedState == ButtonState.Inactive) { + ControlPaint.DrawMixedCheckBox(g, box, ButtonState.Checked); + } else { + ControlPaint.DrawCheckBox(g, box, checkedState); + } + + // if the column is read only we should still show selection + if (IsReadOnly() && isSelected && source.Position == rowNum) { + bounds.Inflate(-1,-1); + System.Drawing.Pen pen = new System.Drawing.Pen(selectionBrush); + pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; + g.DrawRectangle(pen, bounds); + pen.Dispose(); + // restore the bounds rectangle + bounds.Inflate(1,1); + } + } + + /// + /// + /// Gets or sets a value indicating whether null values are allowed. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.DataGridBoolColumnAllowNullValue) + ] + public bool AllowNull { + get { + return allowNull; + } + set { + if (allowNull != value) + { + allowNull = value; + // if we do not allow null, and the gridColumn had + // a null in it, discard it + if (!value && Convert.IsDBNull(currentValue)) + { + currentValue = false; + Invalidate(); + } + OnAllowNullChanged(EventArgs.Empty); + } + } + } + + /// + public event EventHandler AllowNullChanged { + add { + Events.AddHandler(EventAllowNull, value); + } + remove { + Events.RemoveHandler(EventAllowNull, value); + } + } + + /// + /// + /// Enters a into the column. + /// + protected internal override void EnterNullValue() + { + // do not throw an exception when the column is marked as readOnly or + // does not allowNull + if (!this.AllowNull || IsReadOnly()) + return; + if (currentValue != Convert.DBNull) { + currentValue = Convert.DBNull; + Invalidate(); + } + } + + private void ResetNullValue() { + NullValue = Convert.DBNull; + } + + private bool ShouldSerializeNullValue() { + return nullValue != Convert.DBNull; + } + + private void ToggleValue() { + + if (currentValue is bool && ((bool)currentValue) == false) { + currentValue = true; + } + else { + if (AllowNull) { + if (Convert.IsDBNull(currentValue)) { + currentValue = false; + } + else { + currentValue = Convert.DBNull; + } + } + else { + currentValue = false; + } + } + // we started editing + isEditing = true; + // tell the dataGrid that things are changing + // we put Rectangle.Empty cause toggle will invalidate the row anyhow + this.DataGridTableStyle.DataGrid.ColumnStartedEditing(Rectangle.Empty); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridCaption.cs b/WindowsForms/Managed/System/WinForms/DataGridCaption.cs new file mode 100644 index 000000000..8be46b3ee --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridCaption.cs @@ -0,0 +1,758 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Imaging; + using System.Runtime.Versioning; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// Represents a caption in the DataGrid control. + /// + /// + internal class DataGridCaption { + + internal EventHandlerList events; + + private const int xOffset = 3; + private const int yOffset = 1; + private const int textPadding = 2; + private const int buttonToText = 4; + private static ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; + + + private static readonly Point minimumBounds = new Point(50, 30); + + private DataGrid dataGrid = null; + private bool backButtonVisible = false; + private bool downButtonVisible = false; + + private SolidBrush backBrush = DefaultBackBrush; + private SolidBrush foreBrush = DefaultForeBrush; + private Pen textBorderPen = DefaultTextBorderPen; + + private string text = ""; + private bool textBorderVisible = false; + private Font textFont = null; + + // use the datagridFont when the textFont is not set + // we cache this font ( cause we have to make it bold every time we paint the caption ) + // + private Font dataGridFont = null; + + private bool backActive = false; + private bool downActive = false; + private bool backPressed = false; + private bool downPressed = false; + + // if the downButton should point down or not + private bool downButtonDown = false; + + private static Bitmap leftButtonBitmap; + private static Bitmap leftButtonBitmap_bidi; + private static Bitmap magnifyingGlassBitmap; + + private Rectangle backButtonRect = new Rectangle(); + private Rectangle downButtonRect = new Rectangle(); + private Rectangle textRect = new Rectangle(); + + private CaptionLocation lastMouseLocation = CaptionLocation.Nowhere; + + private EventEntry eventList; + private static readonly object EVENT_BACKWARDCLICKED = new object(); + private static readonly object EVENT_DOWNCLICKED = new object(); + private static readonly object EVENT_CAPTIONCLICKED = new object(); + + internal DataGridCaption(DataGrid dataGrid) { + this.dataGrid = dataGrid; + this.downButtonVisible = dataGrid.ParentRowsVisible; + colorMap[0].OldColor = Color.White; + colorMap[0].NewColor = this.ForeColor; + OnGridFontChanged(); + } + + internal void OnGridFontChanged() { + if (dataGridFont == null || !dataGridFont.Equals(dataGrid.Font)) { + try { + this.dataGridFont = new Font(dataGrid.Font, FontStyle.Bold); + } + catch { + } + } + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + internal bool BackButtonActive { + get { + return backActive; + } + set { + if (backActive != value) { + backActive = value; + InvalidateCaptionRect(backButtonRect); + } + } + } + + internal bool DownButtonActive { + get { + return downActive; + } + set { + if (downActive != value) { + downActive = value; + InvalidateCaptionRect(downButtonRect); + } + } + } + + internal static SolidBrush DefaultBackBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaption; + } + } + internal static Pen DefaultTextBorderPen { + get { + return new Pen(SystemColors.ActiveCaptionText); + } + } + internal static SolidBrush DefaultForeBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaptionText; + } + } + internal Color BackColor { + get { + return backBrush.Color; + } + set { + if (!backBrush.Color.Equals(value)) { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Caption BackColor")); + backBrush = new SolidBrush(value); + Invalidate(); + } + } + } + + internal EventHandlerList Events { + get { + if (events == null) { + events = new EventHandlerList(); + } + return events; + } + } + + + internal Font Font { + get { + // use the dataGridFont only if the user + // did not set the CaptionFont + // + if (textFont == null) + return this.dataGridFont; + else + return textFont; + } + set { + if (textFont == null || !textFont.Equals(value)) { + textFont = value; + // this property gets called in the constructor before dataGrid has a caption + // and we don't need this special-handling then... + if (dataGrid.Caption != null) { + dataGrid.RecalculateFonts(); + dataGrid.PerformLayout(); + dataGrid.Invalidate(); // smaller invalidate rect? + } + } + } + } + + internal bool ShouldSerializeFont() { + return textFont != null && !textFont.Equals(this.dataGridFont); + } + + internal bool ShouldSerializeBackColor() { + return !backBrush.Equals(DefaultBackBrush); + } + + internal void ResetBackColor() { + if (ShouldSerializeBackColor()) { + backBrush = DefaultBackBrush; + Invalidate(); + } + } + + internal void ResetForeColor() { + if (ShouldSerializeForeColor()) { + foreBrush = DefaultForeBrush; + Invalidate(); + } + } + + internal bool ShouldSerializeForeColor() { + return !foreBrush.Equals(DefaultForeBrush); + } + + internal void ResetFont() { + textFont = null; + Invalidate(); + } + + internal string Text { + get { + return text; + } + set { + if (value == null) + text = ""; + else + text = value; + Invalidate(); + } + } + + internal bool TextBorderVisible { + get { + return textBorderVisible; + } + set { + textBorderVisible = value; + Invalidate(); + } + } + + internal Color ForeColor { + get { + return foreBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Caption ForeColor")); + foreBrush = new SolidBrush(value); + colorMap[0].NewColor = this.ForeColor; + Invalidate(); + } + } + + internal Point MinimumBounds { + get { + return minimumBounds; + } + } + + internal bool BackButtonVisible { + get { + return backButtonVisible; + } + set { + if (backButtonVisible != value) { + backButtonVisible = value; + Invalidate(); + } + } + } + + internal bool DownButtonVisible { + get { + return downButtonVisible; + } + set { + if (downButtonVisible != value) { + downButtonVisible = value; + Invalidate(); + } + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + protected virtual void AddEventHandler(object key, Delegate handler) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (handler == null) return; + for (EventEntry e = eventList; e != null; e = e.next) { + if (e.key == key) { + e.handler = Delegate.Combine(e.handler, handler); + return; + } + } + eventList = new EventEntry(eventList, key, handler); + } + } + + /// + /// Adds a listener for the BackwardClicked event. + /// + internal event EventHandler BackwardClicked { + add { + Events.AddHandler(EVENT_BACKWARDCLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_BACKWARDCLICKED, value); + } + } + + + /// + /// Adds a listener for the CaptionClicked event. + /// + internal event EventHandler CaptionClicked { + add { + Events.AddHandler(EVENT_CAPTIONCLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_CAPTIONCLICKED, value); + } + } + + internal event EventHandler DownClicked { + add { + Events.AddHandler(EVENT_DOWNCLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_DOWNCLICKED, value); + } + } + + private void Invalidate() { + if (dataGrid != null) + dataGrid.InvalidateCaption(); + } + + private void InvalidateCaptionRect(Rectangle r) { + if (dataGrid != null) + dataGrid.InvalidateCaptionRect(r); + } + + private void InvalidateLocation(CaptionLocation loc) { + Rectangle r; + switch (loc) { + case CaptionLocation.BackButton: + r = backButtonRect; + r.Inflate(1,1); + InvalidateCaptionRect(r); + break; + case CaptionLocation.DownButton: + r = downButtonRect; + r.Inflate(1,1); + InvalidateCaptionRect(r); + break; + } + } + + protected void OnBackwardClicked(EventArgs e) { + if (backActive) { + EventHandler handler = (EventHandler)Events[EVENT_BACKWARDCLICKED]; + if (handler != null) handler(this,e); + } + } + + protected void OnCaptionClicked(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_CAPTIONCLICKED]; + if (handler != null) handler(this,e); + } + + protected void OnDownClicked(EventArgs e) { + if (downActive && downButtonVisible) { + EventHandler handler = (EventHandler)Events[EVENT_DOWNCLICKED]; + if (handler != null) handler(this,e); + } + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetBitmap(string bitmapName) { + Bitmap b = null; + try { + b = new Bitmap(typeof(DataGridCaption), bitmapName); + b.MakeTransparent(); + } + catch (Exception e) { + Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString()); + } + return b; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetBackButtonBmp(bool alignRight) { + if (alignRight) { + if (leftButtonBitmap_bidi == null) + leftButtonBitmap_bidi = GetBitmap("DataGridCaption.backarrow_bidi.bmp"); + return leftButtonBitmap_bidi; + } else { + if (leftButtonBitmap == null) + leftButtonBitmap = GetBitmap("DataGridCaption.backarrow.bmp"); + return leftButtonBitmap; + } + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetDetailsBmp() { + if (magnifyingGlassBitmap == null) + magnifyingGlassBitmap = GetBitmap("DataGridCaption.Details.bmp"); + return magnifyingGlassBitmap; + } + + protected virtual Delegate GetEventHandler(object key) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + for (EventEntry e = eventList; e != null; e = e.next) { + if (e.key == key) return e.handler; + } + return null; + } + } + + internal Rectangle GetBackButtonRect(Rectangle bounds, bool alignRight, int downButtonWidth) { + Bitmap backButtonBmp = GetBackButtonBmp(false); + Size backButtonSize; + lock (backButtonBmp) { + backButtonSize = backButtonBmp.Size; + } + return new Rectangle( bounds.Right - xOffset * 4 - downButtonWidth - backButtonSize.Width, + bounds.Y + yOffset + textPadding, + backButtonSize.Width, + backButtonSize.Height); + } + + internal int GetDetailsButtonWidth() { + int width = 0; + Bitmap detailsBmp = GetDetailsBmp(); + lock (detailsBmp) { + width = detailsBmp.Size.Width; + } + return width; + } + + internal Rectangle GetDetailsButtonRect(Rectangle bounds, bool alignRight) { + Size downButtonSize; + Bitmap detailsBmp = GetDetailsBmp(); + lock (detailsBmp) { + downButtonSize = detailsBmp.Size; + } + int downButtonWidth = downButtonSize.Width; + return new Rectangle( bounds.Right - xOffset * 2 - downButtonWidth, + bounds.Y + yOffset + textPadding, + downButtonWidth, + downButtonSize.Height); + } + + /// + /// Called by the dataGrid when it needs the caption + /// to repaint. + /// + internal void Paint(Graphics g, Rectangle bounds, bool alignRight) { + Size textSize = new Size((int) g.MeasureString(text, this.Font).Width + 2, this.Font.Height + 2); + + downButtonRect = GetDetailsButtonRect(bounds, alignRight); + int downButtonWidth = GetDetailsButtonWidth(); + backButtonRect = GetBackButtonRect(bounds, alignRight, downButtonWidth); + + int backButtonArea = backButtonVisible ? backButtonRect.Width + xOffset + buttonToText : 0; + int downButtonArea = downButtonVisible && !dataGrid.ParentRowsIsEmpty() ? downButtonWidth + xOffset + buttonToText : 0; + + int textWidthLeft = bounds.Width - xOffset - backButtonArea - downButtonArea; + + + textRect = new Rectangle( + bounds.X, + bounds.Y + yOffset, + Math.Min(textWidthLeft, 2 * textPadding + textSize.Width), + 2 * textPadding + textSize.Height); + + // align the caption text box, downButton, and backButton + // if the RigthToLeft property is set to true + if (alignRight) { + textRect.X = bounds.Right - textRect.Width; + backButtonRect.X = bounds.X + xOffset * 4 + downButtonWidth; + downButtonRect.X = bounds.X + xOffset * 2; + } + + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "text size = " + textSize.ToString()); + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "downButtonWidth = " + downButtonWidth.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "textWidthLeft = " + textWidthLeft.ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "backButtonRect " + backButtonRect.ToString()); + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "textRect " + textRect.ToString()); + Debug.WriteLineIf(CompModSwitches.DGCaptionPaint.TraceVerbose, "downButtonRect " + downButtonRect.ToString()); + + // we should use the code that is commented out + // with today's code, there are pixels on the backButtonRect and the downButtonRect + // that are getting painted twice + // + g.FillRectangle(backBrush, bounds); + + if (backButtonVisible) { + PaintBackButton(g, backButtonRect, alignRight); + if (backActive) { + if (lastMouseLocation == CaptionLocation.BackButton) { + backButtonRect.Inflate(1,1); + ControlPaint.DrawBorder3D(g, backButtonRect, + backPressed ? Border3DStyle.SunkenInner : Border3DStyle.RaisedInner); + } + } + } + PaintText(g, textRect, alignRight); + + if (downButtonVisible && !dataGrid.ParentRowsIsEmpty()) { + PaintDownButton(g, downButtonRect); + // the rules have changed, yet again. + // now: if we show the parent rows and the mouse is + // not on top of this icon, then let the icon be depressed. + // if the mouse is pressed over the icon, then show the icon pressed + // if the mouse is over the icon and not pressed, then show the icon SunkenInner; + // + if (lastMouseLocation == CaptionLocation.DownButton) + { + downButtonRect.Inflate(1,1); + ControlPaint.DrawBorder3D(g, downButtonRect, + downPressed ? Border3DStyle.SunkenInner : Border3DStyle.RaisedInner); + } + } + } + + private void PaintIcon(Graphics g, Rectangle bounds, Bitmap b) { + ImageAttributes attr = new ImageAttributes(); + attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); + g.DrawImage(b, bounds, 0, 0, bounds.Width, bounds.Height,GraphicsUnit.Pixel, attr); + attr.Dispose(); + } + + private void PaintBackButton(Graphics g, Rectangle bounds, bool alignRight) { + Bitmap backButtonBmp = GetBackButtonBmp(alignRight); + lock (backButtonBmp) { + PaintIcon(g, bounds, backButtonBmp); + } + } + + private void PaintDownButton(Graphics g, Rectangle bounds) { + Bitmap detailsBmp = GetDetailsBmp(); + lock (detailsBmp) { + PaintIcon(g, bounds, detailsBmp); + } + } + + private void PaintText(Graphics g, Rectangle bounds, bool alignToRight) { + Rectangle textBounds = bounds; + + if (textBounds.Width <= 0 || textBounds.Height <= 0) + return; + + if (textBorderVisible) { + g.DrawRectangle(this.textBorderPen, textBounds.X, textBounds.Y, textBounds.Width - 1, textBounds.Height - 1); + textBounds.Inflate(-1,-1); + } + + if (textPadding > 0) { + Rectangle border = textBounds; + border.Height = textPadding; + g.FillRectangle(this.backBrush, border); + + border.Y = textBounds.Bottom - textPadding; + g.FillRectangle(this.backBrush, border); + + border = new Rectangle(textBounds.X, textBounds.Y + textPadding, + textPadding, textBounds.Height - 2*textPadding); + g.FillRectangle(this.backBrush, border); + + border.X = textBounds.Right - textPadding; + g.FillRectangle(this.backBrush, border); + textBounds.Inflate(-textPadding, -textPadding); + } + + g.FillRectangle(this.backBrush, textBounds); + + // Brush foreBrush = new SolidBrush(dataGrid.CaptionForeColor); + StringFormat format = new StringFormat(); + if (alignToRight) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + format.Alignment = StringAlignment.Far; + } + g.DrawString(text, this.Font, foreBrush, textBounds, format); + format.Dispose(); + // foreBrush.Dispose(); + } + + private CaptionLocation FindLocation(int x, int y) { + if (!backButtonRect.IsEmpty) { + if (backButtonRect.Contains(x,y)) + return CaptionLocation.BackButton; + } + if (!downButtonRect.IsEmpty) { + if (downButtonRect.Contains(x,y)) + return CaptionLocation.DownButton; + } + if (!textRect.IsEmpty) { + if (textRect.Contains(x,y)) + return CaptionLocation.Text; + } + return CaptionLocation.Nowhere; + } + + private bool DownButtonDown { + get { + return downButtonDown; + } + set { + if (downButtonDown != value) + { + downButtonDown = value; + InvalidateLocation(CaptionLocation.DownButton); + } + } + } + + internal bool GetDownButtonDirection() { + return DownButtonDown; + } + + /// + /// Called by the dataGrid when the mouse is pressed + /// inside the caption. + /// + internal void MouseDown(int x, int y) { + CaptionLocation loc = FindLocation(x, y); + switch (loc) { + case CaptionLocation.BackButton: + backPressed = true; + InvalidateLocation(loc); + break; + case CaptionLocation.DownButton: + downPressed = true; + InvalidateLocation(loc); + break; + case CaptionLocation.Text: + OnCaptionClicked(EventArgs.Empty); + break; + } + } + + /// + /// Called by the dataGrid when the mouse is released + /// inside the caption. + /// + internal void MouseUp(int x, int y) { + CaptionLocation loc = FindLocation(x, y); + switch (loc) { + case CaptionLocation.DownButton: + if (downPressed == true) { + downPressed = false; + OnDownClicked(EventArgs.Empty); + } + break; + case CaptionLocation.BackButton: + if (backPressed == true) { + backPressed = false; + OnBackwardClicked(EventArgs.Empty); + } + break; + } + } + + /// + /// Called by the dataGrid when the mouse leaves + /// the caption area. + /// + internal void MouseLeft() { + CaptionLocation oldLoc = lastMouseLocation; + lastMouseLocation = CaptionLocation.Nowhere; + InvalidateLocation(oldLoc); + } + + /// + /// Called by the dataGrid when the mouse is + /// inside the caption. + /// + internal void MouseOver(int x, int y) { + CaptionLocation newLoc = FindLocation(x, y); + + InvalidateLocation(lastMouseLocation); + InvalidateLocation(newLoc); + lastMouseLocation = newLoc; + } + + protected virtual void RaiseEvent(object key, EventArgs e) { + Delegate handler = GetEventHandler(key); + if (handler != null)((EventHandler)handler)(this, e); + } + + protected virtual void RemoveEventHandler(object key, Delegate handler) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (handler == null) return; + for (EventEntry e = eventList, prev = null; e != null; prev = e, e = e.next) { + if (e.key == key) { + e.handler = Delegate.Remove(e.handler, handler); + if (e.handler == null) { + if (prev == null) { + eventList = e.next; + } + else { + prev.next = e.next; + } + } + return; + } + } + } + } + + protected virtual void RemoveEventHandlers() { + eventList = null; + } + + internal void SetDownButtonDirection(bool pointDown) + { + DownButtonDown = pointDown; + } + + /// + /// Toggles the direction the "Down Button" is pointing. + /// + internal bool ToggleDownButtonDirection() { + DownButtonDown = !DownButtonDown; + return DownButtonDown; + } + internal enum CaptionLocation { + Nowhere, + BackButton, + DownButton, + Text + } + + private sealed class EventEntry { + internal EventEntry next; + internal object key; + internal Delegate handler; + + internal EventEntry(EventEntry next, object key, Delegate handler) { + this.next = next; + this.key = key; + this.handler = handler; + } + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridCell.cs b/WindowsForms/Managed/System/WinForms/DataGridCell.cs new file mode 100644 index 000000000..7933e0169 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridCell.cs @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Globalization; + + /// + /// + /// Identifies a cell in the grid. + /// + [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] + public struct DataGridCell { + private int rowNumber; + private int columnNumber; + + /// + /// + /// Gets or sets the number of a column in the control. + /// + public int ColumnNumber { + get { + return columnNumber; + } + set { + columnNumber = value; + } + } + + /// + /// + /// Gets or sets the number of a row in the control. + /// + public int RowNumber { + get { + return rowNumber; + } + set { + rowNumber = value; + } + } + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DataGridCell(int r, int c) { + this.rowNumber = r; + this.columnNumber = c; + } + + /// + /// + /// + /// Gets a value indicating whether the is identical to a second + /// . + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2231:OverrideOperatorEqualsOnOverridingValueTypeEquals")] + public override bool Equals(object o) { + if (o is DataGridCell) { + DataGridCell rhs = (DataGridCell)o; + return (rhs.RowNumber == RowNumber && rhs.ColumnNumber == ColumnNumber); + } + else + return false; + } + + /// + /// + /// + /// Gets + /// a hash value that uniquely identifies the cell. + /// + /// + public override int GetHashCode() { + return ((~rowNumber * (columnNumber+1)) & 0x00ffff00) >> 8; + } + + /// + /// + /// + /// Gets the row number and column number of the cell. + /// + /// + public override string ToString() { + return "DataGridCell {RowNumber = " + RowNumber.ToString(CultureInfo.CurrentCulture) + + ", ColumnNumber = " + ColumnNumber.ToString(CultureInfo.CurrentCulture) + "}"; + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridColumn.cs new file mode 100644 index 000000000..d07343f17 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridColumn.cs @@ -0,0 +1,1309 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms{ + using System.Security.Permissions; + using System.Runtime.Remoting; + using System.ComponentModel; + using System; + using System.Collections; + using System.Windows.Forms; + using System.Drawing; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using Microsoft.Win32; + using System.Runtime.InteropServices; + + /// + /// + /// [To be supplied.] + /// + public interface IDataGridColumnStyleEditingNotificationService { + /// + void ColumnStartedEditing(Control editingControl); + } + + /// + /// + /// Specifies the appearance and text formatting and behavior of + /// a control column. + /// + [ + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Header"), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett + ] + public abstract class DataGridColumnStyle : Component, IDataGridColumnStyleEditingNotificationService { + + private HorizontalAlignment alignment = HorizontalAlignment.Left; + // private SolidBrush alternatingBackBrush = null; + // private SolidBrush backBrush = null; + // SolidBrush foreBrush = null; + private PropertyDescriptor propertyDescriptor = null; + private DataGridTableStyle dataGridTableStyle = null; + private Font font = null; + internal int fontHeight = -1; + private string mappingName = ""; + private string headerName = ""; + private bool invalid = false; + private string nullText = SR.GetString(SR.DataGridNullText); + private bool readOnly = false; + private bool updating = false; + //private bool visible = true; + internal int width = -1; + private bool isDefault = false; + AccessibleObject headerAccessibleObject = null; + + private static readonly object EventAlignment = new object(); + private static readonly object EventPropertyDescriptor = new object(); + private static readonly object EventHeaderText = new object(); + private static readonly object EventMappingName = new object(); + private static readonly object EventNullText = new object(); + private static readonly object EventReadOnly = new object(); + private static readonly object EventWidth = new object(); + + /// + /// + /// In a derived class, + /// initializes a new instance of the class. + /// + public DataGridColumnStyle() { + } + + /// + /// + /// Initializes a new instance of the class with the specified . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] // Shipped like this in Everett. + public DataGridColumnStyle(PropertyDescriptor prop) : this() { + this.PropertyDescriptor = prop; + if (prop != null) + this.readOnly = prop.IsReadOnly; + } + + internal DataGridColumnStyle(PropertyDescriptor prop, bool isDefault) : this(prop) { + this.isDefault = isDefault; + if (isDefault) { + // take the header name from the property name + this.headerName = prop.Name; + this.mappingName = prop.Name; + } + } + +#if DEBUG + internal bool IsDefault { + get { + return this.isDefault; + } + } +#endif // debug + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + /// + /// + /// Gets or sets the alignment of text in a column. + /// + [SRCategory(SR.CatDisplay), + Localizable(true), + DefaultValue(HorizontalAlignment.Left)] + public virtual HorizontalAlignment Alignment { + get { + return alignment; + } + set { + //valid values are 0x0 to 0x2. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridLineStyle)); + } + if (alignment != value) { + alignment = value; + OnAlignmentChanged(EventArgs.Empty); + Invalidate(); + } + } + } + + /// + public event EventHandler AlignmentChanged { + add { + Events.AddHandler(EventAlignment, value); + } + remove { + Events.RemoveHandler(EventAlignment, value); + } + } + + /* + /// + /// Gets or sets the background color of alternating rows for a ledger + /// appearance. + /// + /// + /// A that represents the alternating background + /// color. The default is the of the + /// control. + /// + /// + /// Use this property to set a custom alternating color for each column displayed + /// in the control. + /// + /// + /// The following example sets the property of a specific + /// to yellow. + /// Private Sub SetColumnAlternatingBackColor() + /// ' Create a color object. + /// Dim c As System.Drawing.Color + /// c = System.Drawing.Color.Yellow + /// ' Declare an object variable for the DataGridColumnStyle. + /// Dim myGridColumn As DataGridColumnStyle + /// myGridColumn = DataGrid1.GridColumns(0) + /// ' Set the AlternatingBackColor to the color object. + /// myGridColumn.AlternatingBackColor = c + /// End Sub + /// + /// + /// + /// + [SRCategory(SR.CatColors)] + public virtual Color AlternatingBackColor { + get { + if (alternatingBackBrush != null) { + return alternatingBackBrush.Color; + } + DataGrid grid = DataGrid; + if (grid != null) { + return this.DataGridTableStyle.AlternatingBackColor; + } + return System.Windows.Forms.DataGridTableStyle.defaultAlternatingBackBrush.Color; + } + set { + if (value != Color.Empty && alternatingBackBrush != null && value.Equals(alternatingBackBrush.Color)) { + return; + } + this.alternatingBackBrush = new SolidBrush(value); + RaisePropertyChanged(EventArgs.Empty"AlternatingBackColor"); + Invalidate(); + } + } + */ + + /* + /// + /// Indicates whether the + /// property should be persisted. + /// + /// + /// if the property + /// value has been changed from its default; otherwise, + /// . + /// + /// + /// You typically use this method only if you are either + /// creating a designer for the , or creating your own control + /// incorporating the . + /// You can use the method to + /// determine whether the property value has changed from its default. + /// + /// + internal bool ShouldSerializeAlternatingBackColor() { + return alternatingBackBrush != null; + } + */ + + /* + /// + /// + /// Resets the + /// property to its default value. + /// + /// + /// + /// + /// You typically use this method only if you are either creating a designer for + /// the , or creating your own control incorporating the + /// . + /// + /// + /// You can use the + /// method to determine whether the property value has changed from its default. + /// + /// + /// The OnPropertyChanged + /// event occurs when the property value changes. + /// + /// + public void ResetAlternatingBackColor() { + if (alternatingBackBrush != null) { + this.alternatingBackBrush = null; + RaisePropertyChanged(EventArgs.Empty"AlternatingBackColor"); + Invalidate(); + } + } + */ + + /* + /// + /// + /// Gets either the or the of + /// a specified row. + /// + /// + /// + /// + /// A that represents the background color. + /// + /// + /// + /// + /// + /// + public Color GetBackColor(int rowNum) { + DataGrid grid = DataGrid; + if (rowNum % 2 == 1 && (grid != null && grid.LedgerStyle)) + return AlternatingBackColor; + else + return BackColor; + } + */ + + /// + /// + /// When overridden in a derived class, updates the value of a specified row with + /// the given text. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual void UpdateUI(CurrencyManager source, int rowNum, string displayText) + { + } + + /// + /// + /// + /// + /// [To Editor: I think this is going away.] + /// + /// + /// Gets or sets the background color of the column. + /// + /// + [Browsable(false)] + public AccessibleObject HeaderAccessibleObject { + get { + if (headerAccessibleObject == null) { + headerAccessibleObject = CreateHeaderAccessibleObject(); + } + return headerAccessibleObject; + } + } + + /// + /// + /// Gets or sets the that determines the + /// attributes of data displayed by the . + /// + [DefaultValue(null), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual PropertyDescriptor PropertyDescriptor { + get { + return propertyDescriptor; + } + set { + if (propertyDescriptor != value) { + propertyDescriptor = value; + OnPropertyDescriptorChanged(EventArgs.Empty); + /* + // + + +*/ + } + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler PropertyDescriptorChanged { + add { + Events.AddHandler(EventPropertyDescriptor, value); + } + remove { + Events.RemoveHandler(EventPropertyDescriptor, value); + } + } + + /// + /// + /// Gets the control that the belongs to. + /// + /* + protected virtual DataGrid DataGrid { + get { + DataGridTableStyle gridTable = DataGridTableStyle; + if (gridTable == null) + return null; + return gridTable.DataGrid; + } + } + */ + + protected virtual AccessibleObject CreateHeaderAccessibleObject() { + return new DataGridColumnHeaderAccessibleObject(this); + } + + /// + /// + /// When overridden in a derived class, sets the control that this column + /// belongs to. + /// + protected virtual void SetDataGrid(DataGrid value) + { + SetDataGridInColumn(value); + } + + /// + /// + /// + /// When overridden in a derived class, + /// sets the for the column. + /// + /// + protected virtual void SetDataGridInColumn(DataGrid value) { + // we need to set up the PropertyDescriptor + if (this.PropertyDescriptor == null && value != null) + { + CurrencyManager lm = value.ListManager; + if (lm == null) return; + PropertyDescriptorCollection propCollection = lm.GetItemProperties(); + int propCount = propCollection.Count; + for (int i = 0; i < propCollection.Count; i++) + { + PropertyDescriptor prop = propCollection[i]; + if (!typeof(IList).IsAssignableFrom(prop.PropertyType) && prop.Name.Equals(this.HeaderText)) + { + this.PropertyDescriptor = prop; + return; + } + } + } + } + + internal void SetDataGridInternalInColumn(DataGrid value) { + if (value == null || value.Initializing) + return; + SetDataGridInColumn(value); + } + + /// + /// + /// + /// Gets the System.Windows.Forms.DataGridTableStyle for the column. + /// + /// + [Browsable(false)] + public virtual DataGridTableStyle DataGridTableStyle { + get { + return dataGridTableStyle; + } + } + + internal void SetDataGridTableInColumn(DataGridTableStyle value, bool force) { + if (dataGridTableStyle != null && dataGridTableStyle.Equals(value) && !force) + return; + if (value != null) { + if (value.DataGrid != null && !value.DataGrid.Initializing) { + this.SetDataGridInColumn(value.DataGrid); + } + } + dataGridTableStyle = value; + } + + /// + /// + /// + /// Gets the height of the column's font. + /// + /// + protected int FontHeight { + get { + if (fontHeight != -1) { + return fontHeight; + } + else if (DataGridTableStyle!= null) { + return DataGridTableStyle.DataGrid.FontHeight; + } + else { + return DataGridTableStyle.defaultFontHeight; + } + } + } + + /// + /// + /// Indicates whether the Font property should be persisted. + /// + /// + private bool ShouldSerializeFont() { + return font != null; + } + + /// + public event EventHandler FontChanged { + add { + } + remove { + } + } + + + /* + /// + /// + /// Gets or sets the foreground color of the column. + /// + /// + /// + /// + /// A that represents the foreground color. The + /// default is the foreground color of the control. + /// + /// + /// + /// + /// The OnPropertyChanged event occurs when the property value + /// changes. + /// + /// + /// + /// + /// The following example sets the property of + /// a given . + /// + /// + /// Dim c As System.Drawing.Color + /// Dim dgCol As DataGridColumnStyle + /// c = System.Drawing.CadetBlue + /// Set dgCol = DataGrid1.GridColumns(0) + /// dgCol.ForeColor = c + /// + /// + /// + /// + /// + /// + public virtual Color ForeColor { + get { + if (foreBrush != null) { + return foreBrush.Color; + } + DataGrid grid = DataGrid; + if (grid != null) { + return grid.ForeColor; + } + return DataGrid.defaultForeBrush.Color; + } + set { + if (value != Color.Empty && foreBrush != null && value.Equals(foreBrush.Color)) + return; + this.foreBrush = new SolidBrush(value); + RaisePropertyChanged(EventArgs.Empty"ForeColor"); + Invalidate(); + } + } + + // used by the DataGridRow + internal SolidBrush ForeBrush { + get { + if (foreBrush != null) { + return foreBrush; + } + DataGrid grid = DataGrid; + if (grid != null) { + return grid.ForeBrush; + } + return DataGrid.defaultForeBrush; + } + } + */ + + /* + /// + /// + /// Indicates if the property should be + /// persisted. + /// + /// + /// + /// + /// if the property value has been changed from its + /// default; otherwise, . + /// + /// + /// + /// + /// You typically use this method only if you are either creating a designer for + /// the , or creating your own control incorporating the + /// . + /// + /// + internal bool ShouldSerializeForeColor() { + return foreBrush != null; + } + */ + + /* + /// + /// + /// Resets the property to its default value. + /// + /// + /// + /// + /// You typically use this method if you are either creating a designer for + /// the , or creating your own control incorporating the + /// . + /// + /// + /// You can use the method to + /// determine whether the property value has changed from its default. + /// + /// + /// The OnPropertyChanged event occurs when the property + /// value changes. + /// + /// + public void ResetForeColor() { + if (foreBrush != null) { + foreBrush = null; + RaisePropertyChanged(EventArgs.Empty"ForeColor"); + Invalidate(); + } + } + */ + + /// + /// + /// + /// Gets or sets + /// the text of the column header. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatDisplay) + ] + public virtual string HeaderText { + get { + return headerName; + } + set { + if (value == null) + value = ""; + + if (headerName.Equals(value)) + return; + + headerName = value; + OnHeaderTextChanged(EventArgs.Empty); + // we only invalidate columns that are visible ( ie, their propertyDescriptor is not null) + if (this.PropertyDescriptor != null) + Invalidate(); + } + } + + /// + public event EventHandler HeaderTextChanged { + add { + Events.AddHandler(EventHeaderText, value); + } + remove { + Events.RemoveHandler(EventHeaderText, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Editor("System.Windows.Forms.Design.DataGridColumnStyleMappingNameEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + Localizable(true), + DefaultValue("") + ] + public string MappingName { + get { + return mappingName; + } + set { + if (value == null) + value = ""; + if (mappingName.Equals(value)) + return; + string originalMappingName = mappingName; + // this may throw + mappingName = value; + try { + if (this.dataGridTableStyle != null) + this.dataGridTableStyle.GridColumnStyles.CheckForMappingNameDuplicates(this); + } catch { + mappingName = originalMappingName; + throw; + } + OnMappingNameChanged(EventArgs.Empty); + } + } + + /// + public event EventHandler MappingNameChanged { + add { + Events.AddHandler(EventMappingName, value); + } + remove { + Events.RemoveHandler(EventMappingName, value); + } + } + + /// + /// + /// Indicates whether the System.Windows.Forms.DataGridColumnStyle.Header property should be + /// persisted. + /// + /// + private bool ShouldSerializeHeaderText() { + return(headerName.Length != 0); + } + + /// + /// + /// + /// Resets the System.Windows.Forms.DataGridColumnStyle.Header to its default value + /// ( ). + /// + /// + public void ResetHeaderText() { + HeaderText = ""; + } + + /// + /// + /// Gets or sets the text that is displayed when the column contains a null + /// value. + /// + [ + Localizable(true), + SRCategory(SR.CatDisplay) + ] + public virtual string NullText { + get { + return nullText; + } + set { + if (nullText != null && nullText.Equals(value)) + return; + nullText = value; + OnNullTextChanged(EventArgs.Empty); + Invalidate(); + } + } + + /// + public event EventHandler NullTextChanged { + add { + Events.AddHandler(EventNullText, value); + } + remove { + Events.RemoveHandler(EventNullText, value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the data in the column cannot be edited. + /// + [DefaultValue(false)] + public virtual bool ReadOnly { + get { + return readOnly; + } + set { + if (readOnly != value) { + readOnly = value; + OnReadOnlyChanged(EventArgs.Empty); + } + } + } + + /// + public event EventHandler ReadOnlyChanged { + add { + Events.AddHandler(EventReadOnly, value); + } + remove { + Events.RemoveHandler(EventReadOnly, value); + } + } + +#if false + /// + /// + /// + /// Gets or sets a value indicating whether the column is visible. + /// + /// + [DefaultValue(true)] + public virtual bool Visible { + get { + return visible; + } + set { + if (visible == value) + return; + visible = value; + RaisePropertyChanged(EventArgs.Empty"Visible"); + Invalidate(); + } + } +#endif + + /// + /// + /// + /// Gets or sets the width of the column. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + DefaultValue(100) + ] + public virtual int Width { + get { + return width; + } + set { + if (width != value) { + width = value; + DataGrid grid = this.DataGridTableStyle == null ? null : DataGridTableStyle.DataGrid; + if (grid != null) { + // rearrange the scroll bars + grid.PerformLayout(); + + // force the grid to repaint + grid.InvalidateInside(); + } + OnWidthChanged(EventArgs.Empty); + } + } + } + + /// + public event EventHandler WidthChanged { + add { + Events.AddHandler(EventWidth, value); + } + remove { + Events.RemoveHandler(EventWidth, value); + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + /// + /// + /// + /// Suspends the painting of the column until the + /// method is called. + /// + /// + protected void BeginUpdate() { + updating = true; + } + + /// + /// + /// + /// Resumes the painting of columns suspended by calling the + /// + /// method. + /// + /// + protected void EndUpdate() { + updating = false; + if (invalid) { + invalid = false; + Invalidate(); + } + } + + internal virtual bool WantArrows { + get { + return false; + } + } + + + internal virtual string GetDisplayText(object value) { + return value.ToString(); + } + + private void ResetNullText() { + NullText = SR.GetString(SR.DataGridNullText); + } + + + private bool ShouldSerializeNullText() { + return (!SR.GetString(SR.DataGridNullText).Equals(nullText)); + } + + /// + /// + /// When overridden in a derived class, + /// gets the optimum width and height of the specified value. + /// + protected internal abstract Size GetPreferredSize(Graphics g, object value); + + /// + /// + /// Gets the minimum height of a row. + /// + protected internal abstract int GetMinimumHeight(); + + /// + /// + /// When + /// overridden in a derived class, gets the height to be used in for automatically resizing columns. + /// + protected internal abstract int GetPreferredHeight(Graphics g, object value); + + /// + /// + /// Gets the value in the specified row from the specified + /// System.Windows.Forms.ListManager. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual object GetColumnValueAtRow(CurrencyManager source, int rowNum) { + CheckValidDataSource(source); + if (PropertyDescriptor == null) { + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnNoPropertyDescriptor)); + } + object value = PropertyDescriptor.GetValue(source[rowNum]); + return value; + } + + /// + /// + /// Redraws the column and causes a paint + /// message to be sent to the control. + /// + protected virtual void Invalidate() { + if (updating) { + invalid = true; + return; + } + DataGridTableStyle table = this.DataGridTableStyle; + if (table != null) + table.InvalidateColumn(this); + } + + /// + /// + /// Checks if the specified DataView is valid. + /// + protected void CheckValidDataSource(CurrencyManager value) { + if (value == null) { + throw new ArgumentNullException("value", "DataGridColumnStyle.CheckValidDataSource(DataSource value), value == null"); + } + // the code may delete a gridColumn that was editing + // in that case, we still have to push the value into the backEnd + // and we only need the propertyDescriptor to push the value. + // (take a look at gridEditAndDeleteEditColumn) + // + // DataGridTableStyle myTable = this.DataGridTableStyle; + PropertyDescriptor myPropDesc = this.PropertyDescriptor; + if (myPropDesc == null) { + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnUnbound, HeaderText)); + } + +#if false + DataTable myDataTable = myTable.DataTable; + if (myDataColumn.Table != myDataTable) { + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnDataSourceMismatch, Header)); + } + + /* FOR DEMO: Microsoft: DataGridColumnStyle::CheckValidDataSource: make the check better */ + if (((DataView) value.DataSource).Table == null) { + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnNoDataTable, Header)); + } + else { + /* FOR DEMO: Microsoft: DataGridColumnStyle::CheckValidDataSource: make the check better */ + if (!myTable.DataTable.Equals(((DataView) value.DataSource).Table)) { + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnNoDataSource, Header, myTable.DataTable.TableName)); + } + } +#endif // false + } + + /// + /// + /// + /// When overridden in a derived class, initiates a + /// request to interrrupt an edit procedure. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal abstract void Abort(int rowNum); + + /// + /// + /// When overridden in a derived class, inititates a request to complete an + /// editing procedure. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal abstract bool Commit(CurrencyManager dataSource, int rowNum); + + /// + /// + /// When overridden in a deriving class, prepares a cell for editing. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual void Edit(CurrencyManager source, + int rowNum, + Rectangle bounds, + bool readOnly) { + Edit(source, rowNum, bounds, readOnly, null, true); + } + + /// + /// + /// Prepares the + /// cell for editing, passing the specified , row number, , argument + /// indicating whether the column is read-only, and the + /// text to display in the new control. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual void Edit(CurrencyManager source, + int rowNum, + Rectangle bounds, + bool readOnly, + string displayText) { + Edit(source, rowNum, bounds, readOnly, displayText, true); + } + + /// + /// + /// When overridden in a deriving class, prepares a cell for editing. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal abstract void Edit(CurrencyManager source, + int rowNum, + Rectangle bounds, + bool readOnly, + string displayText, + bool cellIsVisible); + + /// + /// + /// + /// Indicates whether the a mouse down event occurred at the specified row, at + /// the specified x and y coordinates. + /// + internal virtual bool MouseDown(int rowNum, int x, int y) { + return false; + } + + // this function mainly serves Alt0 functionality + /// + /// + /// When overriden in a derived class, enters a + /// into the column. + /// + protected internal virtual void EnterNullValue() { + } + + /// + /// + /// + /// Provides a handler for determining which key was pressed, + /// and whether to process it. + /// + /// + internal virtual bool KeyPress(int rowNum, Keys keyData) { + // if this is read only then do not do anything + if (this.ReadOnly || (this.DataGridTableStyle != null && this.DataGridTableStyle.DataGrid != null && this.DataGridTableStyle.DataGrid.ReadOnly)) + return false; + if (keyData == (Keys.Control | Keys.NumPad0) || keyData == (Keys.Control| Keys.D0)) { + EnterNullValue(); + return true; + } + return false; + } + + // will cause the edit control to become invisible when + // the user navigates to a focused relation child + /// + /// + /// When overridden in a derived class, directs the column to concede focus with an appropriate action. + /// + protected internal virtual void ConcedeFocus() { + } + + /// + /// + /// Paints the a with the specified , + /// , System.Windows.Forms.CurrencyManager, and row number. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal abstract void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum); + + /// + /// + /// When overridden in a derived class, + /// paints a with the specified , , see Rectangle, row number, and + /// alignment. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal abstract void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight); + + /// + /// + /// + /// Paints a with the specified , , see System.Data.DataView, row number, background color, foreground color, and alignment. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, + Brush backBrush, Brush foreBrush, bool alignToRight) { + Paint(g, bounds, source, rowNum, alignToRight); + } + + private void OnPropertyDescriptorChanged(EventArgs e) { + EventHandler eh = Events[EventPropertyDescriptor] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnAlignmentChanged(EventArgs e) { + EventHandler eh = Events[EventAlignment] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnHeaderTextChanged(EventArgs e) { + EventHandler eh = Events[EventHeaderText] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnMappingNameChanged(EventArgs e) { + EventHandler eh = Events[EventMappingName] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnReadOnlyChanged(EventArgs e) { + EventHandler eh = Events[EventReadOnly] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnNullTextChanged(EventArgs e) { + EventHandler eh = Events[EventNullText] as EventHandler; + if (eh != null) + eh(this, e); + } + private void OnWidthChanged(EventArgs e) { + EventHandler eh = Events[EventWidth] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + /// + /// Sets + /// the value in a specified row + /// with the value from a specified see DataView. + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected internal virtual void SetColumnValueAtRow(CurrencyManager source, int rowNum, object value) { + CheckValidDataSource(source); + + if (source.Position != rowNum) + throw new ArgumentException(SR.GetString(SR.DataGridColumnListManagerPosition), "rowNum"); + if (source[rowNum] is IEditableObject) + ((IEditableObject)source[rowNum]).BeginEdit(); + this.PropertyDescriptor.SetValue(source[rowNum], value); + } + + /// + internal protected virtual void ColumnStartedEditing(Control editingControl) { + this.DataGridTableStyle.DataGrid.ColumnStartedEditing(editingControl); + } + + /// + /// + void IDataGridColumnStyleEditingNotificationService.ColumnStartedEditing(Control editingControl) { + this.ColumnStartedEditing(editingControl); + } + + + /// + protected internal virtual void ReleaseHostedControl() { + } + + /// + /// + protected class CompModSwitches { + private static TraceSwitch dgEditColumnEditing; + + /// + public static TraceSwitch DGEditColumnEditing { + get { + if (dgEditColumnEditing == null) { + dgEditColumnEditing = new TraceSwitch("DGEditColumnEditing", "Editing related tracing"); + } + return dgEditColumnEditing; + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [System.Runtime.InteropServices.ComVisible(true)] + protected class DataGridColumnHeaderAccessibleObject : AccessibleObject { + DataGridColumnStyle owner = null; + + /// + /// + /// [To be supplied.] + /// + public DataGridColumnHeaderAccessibleObject(DataGridColumnStyle owner) : this() { + Debug.Assert(owner != null, "DataGridColumnHeaderAccessibleObject must have a valid owner DataGridColumn"); + this.owner = owner; + + } + + /// + /// + /// [To be supplied.] + /// + public DataGridColumnHeaderAccessibleObject() : base() { + } + + /// + /// + /// [To be supplied.] + /// + public override Rectangle Bounds { + get { + // we need to get the width and the X coordinate of this column on the screen + // we can't just cache this, cause the column may be moved in the collection + if (this.owner.PropertyDescriptor == null) + return Rectangle.Empty; + + DataGrid dg = this.DataGrid; + if (dg.DataGridRowsLength == 0) + return Rectangle.Empty; + + // we need to find this column's offset in the gridColumnCollection... + GridColumnStylesCollection cols = this.owner.dataGridTableStyle.GridColumnStyles; + int offset = -1; + for (int i = 0; i < cols.Count; i++) + if (cols[i] == this.owner) { + offset = i; + break; + } + Debug.Assert(offset >= 0, "this column must be in a collection, otherwise its bounds are useless"); + Rectangle rect = dg.GetCellBounds(0, offset); + // now add the Y coordinate of the datagrid.Layout.Data. it should be the same as + // dataGrid.Layout.ColumnHeaders + rect.Y = dg.GetColumnHeadersRect().Y; + return dg.RectangleToScreen(rect); + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + return Owner.headerName; + } + } + + /// + /// + /// [To be supplied.] + /// + protected DataGridColumnStyle Owner { + get { + return owner; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return DataGrid.AccessibilityObject; + } + } + + private DataGrid DataGrid { + get { + return owner.dataGridTableStyle.dataGrid; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + return AccessibleRole.ColumnHeader; + } + } + + /// + /// + /// [To be supplied.] + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + switch (navdir) { + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + case AccessibleNavigation.Down: + return Parent.GetChild(1 + Owner.dataGridTableStyle.GridColumnStyles.IndexOf(Owner) + 1); + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + return Parent.GetChild(1 + Owner.dataGridTableStyle.GridColumnStyles.IndexOf(Owner) - 1); + + } + + return null; + + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridColumnCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridColumnCollection.cs new file mode 100644 index 000000000..7c5a9dffc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridColumnCollection.cs @@ -0,0 +1,575 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Collections; + using System.Drawing.Design; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + + /// + /// + /// Represents a collection of System.Windows.Forms.DataGridColumnStyle objects in the + /// control. + /// + [ + Editor("System.Windows.Forms.Design.DataGridColumnCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + ListBindable(false) + ] + public class GridColumnStylesCollection : BaseCollection, IList { + CollectionChangeEventHandler onCollectionChanged; + ArrayList items = new ArrayList(); + DataGridTableStyle owner = null; + private bool isDefault = false; + + // we have to implement IList for the Collection editor to work + // + /// + /// + int IList.Add(object value) { + return this.Add((DataGridColumnStyle) value); + } + + /// + /// + void IList.Clear() { + this.Clear(); + } + + /// + /// + bool IList.Contains(object value) { + return items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) { + return items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object value) { + this.Remove((DataGridColumnStyle)value); + } + + /// + /// + void IList.RemoveAt(int index) { + this.RemoveAt(index); + } + + /// + /// + bool IList.IsFixedSize { + get {return false;} + } + + /// + /// + bool IList.IsReadOnly { + get {return false;} + } + + /// + /// + object IList.this[int index] { + get { return items[index]; } + set { throw new NotSupportedException(); } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count { + get {return this.items.Count;} + } + + /// + /// + bool ICollection.IsSynchronized { + get {return false;} + } + + /// + /// + object ICollection.SyncRoot { + get {return this;} + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() { + return items.GetEnumerator(); + } + + internal GridColumnStylesCollection(DataGridTableStyle table) { + owner = table; + } + + internal GridColumnStylesCollection(DataGridTableStyle table, bool isDefault) : this(table) { + this.isDefault = isDefault; + } + + /// + /// + /// Gets the list of items in the collection. + /// + protected override ArrayList List { + get { + return items; + } + } + + /* implemented in BaseCollection + /// + /// + /// Gets the number of System.Windows.Forms.DataGridColumnStyle objects in the collection. + /// + /// + /// + /// + /// The number of System.Windows.Forms.DataGridColumnStyle objects in the System.Windows.Forms.GridColumnsStyleCollection . + /// + /// + /// + /// + /// The following example uses the + /// property to determine how many System.Windows.Forms.DataGridColumnStyle objects are in a System.Windows.Forms.GridColumnsStyleCollection, and uses that number to iterate through the + /// collection. + /// + /// + /// Private Sub PrintGridColumns() + /// Dim colsCount As Integer + /// colsCount = DataGrid1.GridColumns.Count + /// Dim i As Integer + /// For i = 0 to colsCount - 1 + /// Debug.Print DataGrid1.GridColumns(i).GetType.ToString + /// Next i + /// End Sub + /// + /// + /// + /// + public override int Count { + get { + return items.Count; + } + } + */ + + /// + /// + /// Gets the System.Windows.Forms.DataGridColumnStyle at a specified index. + /// + public DataGridColumnStyle this[int index] { + get { + return (DataGridColumnStyle)items[index]; + } + } + + /// + /// + /// Gets the System.Windows.Forms.DataGridColumnStyle + /// with the specified name. + /// + public DataGridColumnStyle this[string columnName] { + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + get { + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridColumnStyle column = (DataGridColumnStyle)items[i]; + // NOTE: case-insensitive + if (String.Equals(column.MappingName, columnName, StringComparison.OrdinalIgnoreCase)) + return column; + } + return null; + } + } + + internal DataGridColumnStyle MapColumnStyleToPropertyName(string mappingName) { + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridColumnStyle column = (DataGridColumnStyle)items[i]; + // NOTE: case-insensitive + if (String.Equals(column.MappingName, mappingName, StringComparison.OrdinalIgnoreCase)) + return column; + } + return null; + } + + /// + /// + /// Gets the System.Windows.Forms.DataGridColumnStyle associated with the + /// specified . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")] + public DataGridColumnStyle this[PropertyDescriptor propertyDesciptor] { + [ + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // already shipped + ] + get { + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridColumnStyle column = (DataGridColumnStyle)items[i]; + if (propertyDesciptor.Equals(column.PropertyDescriptor)) + return column; + } + return null; + } + } + + internal DataGridTableStyle DataGridTableStyle { + get { + return this.owner; + } + } + + /// + /// Adds a System.Windows.Forms.DataGridColumnStyle to the System.Windows.Forms.GridColumnStylesCollection + /// + + internal void CheckForMappingNameDuplicates(DataGridColumnStyle column) { + if (String.IsNullOrEmpty(column.MappingName)) + return; + for (int i = 0; i < items.Count; i++) + if ( ((DataGridColumnStyle)items[i]).MappingName.Equals(column.MappingName) && column != items[i]) + throw new ArgumentException(SR.GetString(SR.DataGridColumnStyleDuplicateMappingName), "column"); + } + + private void ColumnStyleMappingNameChanged(object sender, EventArgs pcea) { + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + private void ColumnStylePropDescChanged(object sender, EventArgs pcea) { + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, (DataGridColumnStyle) sender)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual int Add(DataGridColumnStyle column) { + if (this.isDefault) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultColumnCollectionChanged)); + } + + CheckForMappingNameDuplicates(column); + + column.SetDataGridTableInColumn(owner, true); + column.MappingNameChanged += new EventHandler(ColumnStyleMappingNameChanged); + column.PropertyDescriptorChanged += new EventHandler(ColumnStylePropDescChanged); + + // columns which are not the default should have a default + // width of DataGrid.PreferredColumnWidth + if (this.DataGridTableStyle != null && column.Width == -1) + column.width = this.DataGridTableStyle.PreferredColumnWidth; +#if false + column.AddOnPropertyChanged(owner.OnColumnChanged); +#endif + int index = items.Add(column); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, column)); + return index; + } + + /// + public void AddRange(DataGridColumnStyle[] columns) { + if (columns == null) { + throw new ArgumentNullException("columns"); + } + for (int i = 0; i < columns.Length; i++) { + Add(columns[i]); + } + } + + // the dataGrid will need to add default columns to a default + // table when there is no match for the listName in the tableStyle + internal void AddDefaultColumn(DataGridColumnStyle column) { +#if DEBUG + Debug.Assert(this.isDefault, "we should be calling this function only for default tables"); + Debug.Assert(column.IsDefault, "we should be a default column"); +#endif // DEBUG + column.SetDataGridTableInColumn(owner, true); + this.items.Add(column); + } + + internal void ResetDefaultColumnCollection() { + Debug.Assert(this.isDefault, "we should be calling this function only for default tables"); + // unparent the edit controls + for (int i = 0; i < Count; i++) { + this[i].ReleaseHostedControl(); + } + + // get rid of the old list and get a new empty list + items.Clear(); + } + + /// + /// + /// Occurs when a change is made to the System.Windows.Forms.GridColumnStylesCollection. + /// + public event CollectionChangeEventHandler CollectionChanged { + add { + onCollectionChanged += value; + } + remove { + onCollectionChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + for (int i = 0; i < Count; i ++) { + this[i].ReleaseHostedControl(); + } + items.Clear(); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// + /// Gets a value indicating whether the System.Windows.Forms.GridColumnStylesCollection contains a System.Windows.Forms.DataGridColumnStyle associated with the + /// specified . + /// + /// + public bool Contains(PropertyDescriptor propertyDescriptor) { + return this[propertyDescriptor] != null; + } + + /// + /// + /// + /// Gets a value indicating whether the System.Windows.Forms.GridColumnsStyleCollection contains the specified System.Windows.Forms.DataGridColumnStyle. + /// + /// + public bool Contains(DataGridColumnStyle column) { + int index = items.IndexOf(column); + return index != -1; + } + + /// + /// + /// + /// Gets a value indicating whether the System.Windows.Forms.GridColumnsStyleCollection contains the System.Windows.Forms.DataGridColumnStyle with the specified name. + /// + /// + public bool Contains(string name) { + IEnumerator e = items.GetEnumerator(); + while (e.MoveNext()) { + DataGridColumnStyle column = (DataGridColumnStyle)e.Current; + // NOTE: case-insensitive + if (String.Compare(column.MappingName, name, true, CultureInfo.InvariantCulture) == 0) + return true; + } + return false; + } + + /* implemented at BaseCollection + /// + /// + /// Gets an enumerator for the System.Windows.Forms.GridColumnsStyleCollection. + /// + /// + /// + /// + /// Gets an enumerator for the System.Windows.Forms.GridColumnsStyleCollection. + /// + /// + /// + /// + /// An + /// that can be used to iterate through the collection. + /// + /// + /// + /// + /// The following example gets an that iterates through the System.Windows.Forms.GridColumnsStyleCollection. and prints the + /// of each + /// associated with the object. + /// + /// + /// Private Sub EnumerateThroughGridColumns() + /// Dim ie As System.Collections.IEnumerator + /// Dim dgCol As DataGridColumn + /// Set ie = DataGrid1.GridColumns.GetEnumerator + /// Do While ie.GetNext = True + /// Set dgCol = ie.GetObject + /// Debug.Print dgCol.DataColumn.Caption + /// Loop + /// End Sub + /// + /// + /// + /// + /// + /// + public override IEnumerator GetEnumerator() { + return items.GetEnumerator(); + } + + /// + /// + /// Gets an enumerator for the System.Windows.Forms.GridColumnsStyleCollection + /// . + /// + /// + /// + /// A value that indicates if the enumerator can remove elements. , if removals are allowed; otherwise, . The default is . + /// + /// + /// + /// An that can be used to iterate through the + /// collection. + /// + /// + /// + /// An attempt was made to remove the System.Windows.Forms.DataGridColumnStyle through the object's method. Use the System.Windows.Forms.GridColumnsStyleCollection object's method instead. + /// + /// + /// + /// Because this implementation doesn't support the removal + /// of System.Windows.Forms.DataGridColumnStyle objects through the + /// class's method, you must use the class's + /// method instead. + /// + /// + /// + /// + /// The following example gets an for that iterates through the System.Windows.Forms.GridColumnsStyleCollection. If a column in the collection is of type , it is deleted. + /// + /// + /// Private Sub RemoveBoolColumns() + /// Dim ie As System.Collections.IEnumerator + /// Dim dgCol As DataGridColumn + /// Set ie = DataGrid1.GridColumns.GetEnumerator(true) + /// Do While ie.GetNext + /// Set dgCol = ie.GetObject + /// + /// If dgCol.ToString = "DataGridBoolColumn" Then + /// DataGrid1.GridColumns.Remove dgCol + /// End If + /// Loop + /// End If + /// + /// + /// + /// + /// + /// + public override IEnumerator GetEnumerator(bool allowRemove) { + if (!allowRemove) + return GetEnumerator(); + else + throw new NotSupportedException(SR.GetString(SR.DataGridColumnCollectionGetEnumerator)); + } + */ + + /// + /// + /// Gets the index of a specified System.Windows.Forms.DataGridColumnStyle. + /// + public int IndexOf(DataGridColumnStyle element) { + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridColumnStyle column = (DataGridColumnStyle)items[i]; + if (element == column) + return i; + } + return -1; + } + + /// + /// + /// Raises the System.Windows.Forms.GridColumnsCollection.CollectionChanged event. + /// + protected void OnCollectionChanged(CollectionChangeEventArgs e) { + if (onCollectionChanged != null) + onCollectionChanged(this, e); + + DataGrid grid = owner.DataGrid; + if (grid != null) { + grid.checkHierarchy = true; + } + } + + /// + /// + /// Removes the specified System.Windows.Forms.DataGridColumnStyle from the System.Windows.Forms.GridColumnsStyleCollection. + /// + public void Remove(DataGridColumnStyle column) { + if (this.isDefault) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultColumnCollectionChanged)); + } + + int columnIndex = -1; + int itemsCount = items.Count; + for (int i = 0; i < itemsCount; ++i) + if (items[i] == column) { + columnIndex = i; + break; + } + if (columnIndex == -1) + throw new InvalidOperationException(SR.GetString(SR.DataGridColumnCollectionMissing)); + else + RemoveAt(columnIndex); + } + + /// + /// + /// Removes the System.Windows.Forms.DataGridColumnStyle with the specified index from the System.Windows.Forms.GridColumnsStyleCollection. + /// + public void RemoveAt(int index) { + if (this.isDefault) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultColumnCollectionChanged)); + } + + DataGridColumnStyle toRemove = (DataGridColumnStyle)items[index]; + toRemove.SetDataGridTableInColumn(null, true); + toRemove.MappingNameChanged -= new EventHandler(ColumnStyleMappingNameChanged); + toRemove.PropertyDescriptorChanged -= new EventHandler(ColumnStylePropDescChanged); +#if false + toRemove.RemoveOnPropertyChange(owner.OnColumnChanged); +#endif + items.RemoveAt(index); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, toRemove)); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetPropertyDescriptors() { + for (int i = 0; i < this.Count; i++) { + this[i].PropertyDescriptor = null; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridDefaultColumnWidthTypeConverter.cs b/WindowsForms/Managed/System/WinForms/DataGridDefaultColumnWidthTypeConverter.cs new file mode 100644 index 000000000..3bf8b74fd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridDefaultColumnWidthTypeConverter.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System; + using System.IO; + using System.ComponentModel; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// [To be supplied.] + /// + public class DataGridPreferredColumnWidthTypeConverter : TypeConverter { + /// + /// + /// [To be supplied.] + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string) || sourceType == typeof(int)) + return true; + else + return false; + } + + /// + /// + /// [To be supplied.] + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType == typeof(string)) + { + if (value.GetType() == typeof(int)) + { + int pulica = (int) value; + if (pulica == - 1) + return "AutoColumnResize (-1)"; + else + return pulica.ToString(CultureInfo.CurrentCulture); + } + else + { + return base.ConvertTo(context, culture, value, destinationType); + } + } + else + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// [To be supplied.] + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value.GetType() == typeof(string)) + { + string text = value.ToString(); + if (text.Equals("AutoColumnResize (-1)")) + return -1; + else + return Int32.Parse(text, CultureInfo.CurrentCulture); + } + else if (value.GetType() == typeof(int)) + { + return (int)value; + } + else + { + throw GetConvertFromException(value); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridLineStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridLineStyle.cs new file mode 100644 index 000000000..1f8e66056 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridLineStyle.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + /// + /// + /// + /// Specifies the style of gridlines in a . + /// + /// + public enum DataGridLineStyle { + /// + /// + /// + /// No gridlines between cells. + /// + /// + None, + /// + /// + /// + /// Solid gridlines between cells. + /// + /// + Solid + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridParentRows.cs b/WindowsForms/Managed/System/WinForms/DataGridParentRows.cs new file mode 100644 index 000000000..8b2e32d2f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridParentRows.cs @@ -0,0 +1,1215 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.Remoting; + + using System; + using System.Collections; + + using System.Windows.Forms; + using System.Security.Permissions; + + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Imaging; + using Microsoft.Win32; + + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Runtime.Versioning; + + internal class DataGridParentRows { + // siting + // + private DataGrid dataGrid; + + // ui + // + // private Color backColor = DataGrid.defaultParentRowsBackColor; + // private Color foreColor = DataGrid.defaultParentRowsForeColor; + + private SolidBrush backBrush = DataGrid.DefaultParentRowsBackBrush; + private SolidBrush foreBrush = DataGrid.DefaultParentRowsForeBrush; + + private int borderWidth = 1; + // private Color borderColor = SystemColors.WindowFrame; + private Brush borderBrush = new SolidBrush(SystemColors.WindowFrame); + + private static Bitmap rightArrow = null; + private static Bitmap leftArrow = null; + + private ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; + + // private bool gridLineDots = false; + // private Color gridLineColor = SystemColors.Control; + // private Brush gridLineBrush = SystemBrushes.Control; + private Pen gridLinePen = SystemPens.Control; + + private int totalHeight = 0; + private int textRegionHeight = 0; + + + // now that we have left and right arrows, we also have layout + private Layout layout = new Layout(); + + // mouse info + // + // private bool overLeftArrow = false; + // private bool overRightArrow = false; + private bool downLeftArrow = false; + private bool downRightArrow = false; + + // a horizOffset of 0 means that the layout for the parent + // rows is left aligned. + // a horizOffset of 1 means that the leftmost unit of information ( let it be a + // table name, a column name or a column value ) is not visible. + // a horizOffset of 2 means that the leftmost 2 units of information are not visible, and so on + // + private int horizOffset = 0; + + // storage for parent row states + // + private ArrayList parents = new ArrayList(); + private int parentsCount = 0; + private ArrayList rowHeights = new ArrayList(); + AccessibleObject accessibleObject; + + internal DataGridParentRows(DataGrid dataGrid) { + this.colorMap[0].OldColor = Color.Black; + this.dataGrid = dataGrid; + // UpdateGridLinePen(); + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + public AccessibleObject AccessibleObject { + get { + if (accessibleObject == null) { + accessibleObject = new DataGridParentRowsAccessibleObject(this); + } + return accessibleObject; + } + } + + internal Color BackColor { + get { + return backBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows BackColor")); + if (value != backBrush.Color) { + backBrush = new SolidBrush(value); + Invalidate(); + } + } + } + + internal SolidBrush BackBrush { + get { + return backBrush; + } + set { + if (value != backBrush) { + CheckNull(value, "BackBrush"); + backBrush = value; + Invalidate(); + } + } + } + + internal SolidBrush ForeBrush { + get { + return foreBrush; + } + set { + if (value != foreBrush) { + CheckNull(value, "BackBrush"); + foreBrush = value; + Invalidate(); + } + } + } + + // since the layout of the parentRows is computed on every paint message, + // we can actually return true ClientRectangle coordinates + internal Rectangle GetBoundsForDataGridStateAccesibility(DataGridState dgs) { + Rectangle ret = Rectangle.Empty; + int rectY = 0; + for (int i = 0; i < parentsCount; i++) { + int height = (int) rowHeights[i]; + if (parents[i] == dgs) { + ret.X = layout.leftArrow.IsEmpty ? layout.data.X : layout.leftArrow.Right; + ret.Height = height; + ret.Y = rectY; + ret.Width = layout.data.Width; + return ret; + } + rectY += height; + } + return ret; + } + + /* + internal Color BorderColor { + get { + return borderColor; + } + set { + if (value != borderColor) { + borderColor = value; + Invalidate(); + } + } + } + */ + + internal Brush BorderBrush { + get { + return borderBrush; + } + set { + if (value != borderBrush) { + borderBrush = value; + Invalidate(); + } + } + } + + /* + internal Brush GridLineBrush { + get { + return gridLineBrush; + } + set { + if (value != gridLineBrush) { + gridLineBrush = value; + UpdateGridLinePen(); + Invalidate(); + } + } + } + + internal bool GridLineDots { + get { + return gridLineDots; + } + set { + if (gridLineDots != value) { + gridLineDots = value; + UpdateGridLinePen(); + Invalidate(); + } + } + } + */ + + internal int Height { + get { + return totalHeight; + } + } + + internal Color ForeColor { + get { + return foreBrush.Color; + } + set { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows ForeColor")); + if (value != foreBrush.Color) { + foreBrush = new SolidBrush(value); + Invalidate(); + } + } + } + + internal bool Visible { + get { + return dataGrid.ParentRowsVisible; + } + set { + dataGrid.ParentRowsVisible = value; + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + /// + /// + /// Adds a DataGridState object to the top of the list of parents. + /// + internal void AddParent(DataGridState dgs) { + CurrencyManager childDataSource = (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember]; + parents.Add(dgs); + SetParentCount(parentsCount + 1); + Debug.Assert(GetTopParent() != null, "we should have a parent at least"); + } + + internal void Clear() { + for (int i = 0; i < parents.Count; i++) { + DataGridState dgs = parents[i] as DataGridState; + dgs.RemoveChangeNotification(); + } + parents.Clear(); + rowHeights.Clear(); + totalHeight = 0; + SetParentCount(0); + } + + internal void SetParentCount(int count) { + parentsCount = count; + dataGrid.Caption.BackButtonVisible = (parentsCount > 0) && (dataGrid.AllowNavigation); + } + + internal void CheckNull(object value, string propName) { + if (value == null) + throw new ArgumentNullException("propName"); + } + + internal void Dispose() { + gridLinePen.Dispose(); + } + + /// + /// + /// Retrieves the top most parent in the list of parents. + /// + internal DataGridState GetTopParent() { + if (parentsCount < 1) { + return null; + } + return(DataGridState)(((ICloneable)(parents[parentsCount-1])).Clone()); + } + + /// + /// + /// Determines if there are any parent rows contained in this object. + /// + internal bool IsEmpty() { + return parentsCount == 0; + } + + /// + /// + /// Similar to GetTopParent() but also removes it. + /// + internal DataGridState PopTop() { + if (parentsCount < 1) { + return null; + } + + SetParentCount(parentsCount - 1); + DataGridState ret = (DataGridState)parents[parentsCount]; + ret.RemoveChangeNotification(); + parents.RemoveAt(parentsCount); + return ret; + } + + internal void Invalidate() { + if (dataGrid != null) + dataGrid.InvalidateParentRows(); + } + + internal void InvalidateRect(Rectangle rect) + { + if (dataGrid != null) + { + Rectangle r = new Rectangle(rect.X, rect.Y, rect.Width + borderWidth, rect.Height + borderWidth); + dataGrid.InvalidateParentRowsRect(r); + } + } + + // called from DataGrid::OnLayout + internal void OnLayout() { + if (parentsCount == rowHeights.Count) + return; + + int height = 0; + if (totalHeight == 0) { + totalHeight += 2 * borderWidth; + } + + // figure out how tall each row's text will be + // + textRegionHeight = (int)dataGrid.Font.Height + 2; + + // make the height of the Column.Font count for the height + // of the parentRows; + // + // if the user wants to programatically + // navigate to a relation in the constructor of the form + // ( ie, when the form does not process PerformLayout ) + // the grid will receive an OnLayout message when there is more + // than one parent in the grid + if (parentsCount > rowHeights.Count) { + Debug.Assert(parentsCount == rowHeights.Count + 1 || rowHeights.Count == 0, "see bug 82808 for more info, or the comment above"); + int rowHeightsCount = this.rowHeights.Count; + for (int i = rowHeightsCount; i < parentsCount; i++) { + DataGridState dgs = (DataGridState) parents[i]; + GridColumnStylesCollection cols = dgs.GridColumnStyles; + + int colsHeight = 0; + + for (int j=0; j 0) { + ResetMouseInfo(); + horizOffset -= 1; + Invalidate(); + } + else + { + ResetMouseInfo(); + InvalidateRect(layout.leftArrow); + } + } + + private void RightArrowClick(int cellCount) { + if (horizOffset < cellCount - 1) { + ResetMouseInfo(); + horizOffset += 1; + Invalidate(); + } + else + { + ResetMouseInfo(); + InvalidateRect(layout.rightArrow); + } + } + + // the only mouse clicks that are handled are + // the mouse clicks on the LeftArrow and RightArrow + // + internal void OnMouseDown(int x, int y, bool alignToRight) { + if (layout.rightArrow.IsEmpty) { + Debug.Assert(layout.leftArrow.IsEmpty, "we can't have the leftArrow w/o the rightArrow"); + return; + } + + int cellCount = CellCount(); + + if (layout.rightArrow.Contains(x,y)) { + // draw a nice sunken border around the right arrow area + // we want to keep a cell on the screen + + downRightArrow = true; + + if (alignToRight) + LeftArrowClick(cellCount); + else + RightArrowClick(cellCount); + } + else if (layout.leftArrow.Contains(x,y)) { + downLeftArrow = true; + + if (alignToRight) + RightArrowClick(cellCount); + else + LeftArrowClick(cellCount); + } + else + { + if (downLeftArrow) + { + downLeftArrow = false; + InvalidateRect(layout.leftArrow); + } + if (downRightArrow) + { + downRightArrow = false; + InvalidateRect(layout.rightArrow); + } + } + } + + internal void OnMouseLeave() { + if (downLeftArrow) + { + downLeftArrow = false; + InvalidateRect(layout.leftArrow); + } + if (downRightArrow) + { + downRightArrow = false; + InvalidateRect(layout.rightArrow); + } + } + + internal void OnMouseMove(int x, int y) { + /* + if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) + { + ResetMouseInfo(); + overLeftArrow = true; + InvalidateRect(layout.leftArrow); + return; + } + if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) + { + ResetMouseInfo(); + overRightArrow = true; + InvalidateRect(layout.rightArrow); + return; + } + */ + + if (downLeftArrow) + { + downLeftArrow = false; + InvalidateRect(layout.leftArrow); + } + if (downRightArrow) + { + downRightArrow = false; + InvalidateRect(layout.rightArrow); + } + } + + internal void OnMouseUp(int x, int y) { + ResetMouseInfo(); + if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) + { + InvalidateRect(layout.rightArrow); + return; + } + if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) + { + InvalidateRect(layout.leftArrow); + return; + } + } + + internal void OnResize(Rectangle oldBounds) { + Invalidate(); + } + + /// + /// + /// Paints the parent rows + /// + internal void Paint(Graphics g, Rectangle visualbounds, bool alignRight) { + Rectangle bounds = visualbounds; + // Paint the border around our bounds + if (borderWidth > 0) { + PaintBorder(g, bounds); + bounds.Inflate(-borderWidth, -borderWidth); + } + + PaintParentRows(g, bounds, alignRight); + } + + private void PaintBorder(Graphics g, Rectangle bounds) { + Rectangle border = bounds; + + // top + border.Height = borderWidth; + g.FillRectangle(borderBrush, border); + + // bottom + border.Y = bounds.Bottom - borderWidth; + g.FillRectangle(borderBrush, border); + + // left + border = new Rectangle(bounds.X, bounds.Y + borderWidth, + borderWidth, bounds.Height - 2*borderWidth); + g.FillRectangle(borderBrush, border); + + // right + border.X = bounds.Right - borderWidth; + g.FillRectangle(borderBrush, border); + } + + // will return the width of the text box that will fit all the + // tables names + private int GetTableBoxWidth(Graphics g, Font font) { + // try to make the font BOLD + Font textFont = font; + try { + textFont = new Font(font, FontStyle.Bold); + } + catch { + } + int width = 0; + for (int row = 0; row < parentsCount; row ++) { + DataGridState dgs = (DataGridState) parents[row]; + // Graphics.MeasureString(...) returns different results for ": " than for " :" + // + string displayTableName = dgs.ListManager.GetListName() + " :"; + int size = (int) g.MeasureString(displayTableName, textFont).Width; + width = Math.Max(size, width); + } + + return width; + } + + // will return the width of the text box that will + // fit all the column names + private int GetColBoxWidth(Graphics g, Font font, int colNum) { + int width = 0; + + for (int row = 0; row < parentsCount; row ++) { + DataGridState dgs = (DataGridState) parents[row]; + GridColumnStylesCollection columns = dgs.GridColumnStyles; + if (colNum < columns.Count) { + // Graphics.MeasureString(...) returns different results for ": " than for " :" + // + string colName = columns[colNum].HeaderText + " :"; + int size = (int) g.MeasureString(colName, font).Width; + width = Math.Max(size, width); + } + } + + return width; + } + + + // will return the width of the best fit for the column + // + private int GetColDataBoxWidth(Graphics g, int colNum) { + int width = 0; + for (int row = 0; row < parentsCount; row ++) { + DataGridState dgs = (DataGridState) parents[row]; + GridColumnStylesCollection columns = dgs.GridColumnStyles; + if (colNum < columns.Count) { + object value = columns[colNum].GetColumnValueAtRow((CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], + dgs.LinkingRow.RowNumber); + int size = columns[colNum].GetPreferredSize(g, value).Width; + width = Math.Max(size, width); + } + } + return width; + } + + // will return the count of the table with the largest number of columns + private int ColsCount() { + int colNum = 0; + for (int row = 0; row < parentsCount; row ++) { + DataGridState dgs = (DataGridState) parents[row]; + colNum = Math.Max(colNum, dgs.GridColumnStyles.Count); + } + return colNum; + } + + // will return the total width required to paint the parentRows + private int TotalWidth(int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { + int totalWidth = 0; + totalWidth += tableNameBoxWidth; + Debug.Assert(colsNameWidths.Length == colsDataWidths.Length, "both arrays are as long as the largest column count in dgs"); + for (int i = 0; i < colsNameWidths.Length; i ++) { + totalWidth += colsNameWidths[i]; + totalWidth += colsDataWidths[i]; + } + + // let 3 pixels in between datacolumns + // see DonnaWa + totalWidth += 3 * (colsNameWidths.Length - 1); + return totalWidth; + } + + // computes the layout for the parent rows + // + private void ComputeLayout(Rectangle bounds, int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { + int totalWidth = TotalWidth(tableNameBoxWidth, colsNameWidths, colsDataWidths); + if (totalWidth > bounds.Width) { + layout.leftArrow = new Rectangle(bounds.X, bounds.Y, 15, bounds.Height); + layout.data = new Rectangle(layout.leftArrow.Right, bounds.Y, bounds.Width - 30, bounds.Height); + layout.rightArrow = new Rectangle(layout.data.Right, bounds.Y, 15, bounds.Height); + } + else { + layout.data = bounds; + layout.leftArrow = Rectangle.Empty; + layout.rightArrow = Rectangle.Empty; + } + } + + private void PaintParentRows(Graphics g, Rectangle bounds, bool alignToRight) { + // variables needed for aligning the table and column names + int tableNameBoxWidth = 0; + int numCols = ColsCount(); + int[] colsNameWidths = new int[numCols]; + int[] colsDataWidths = new int[numCols]; + + // compute the size of the box that will contain the tableName + // + if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || + dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { + tableNameBoxWidth = GetTableBoxWidth(g, dataGrid.Font); + } + + // initialiaze the arrays that contain the column names and the column size + // + for (int i = 0; i < numCols; i++) { + if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || + dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { + colsNameWidths[i] = GetColBoxWidth(g, dataGrid.Font, i); + } + else { + colsNameWidths[i] = 0; + } + colsDataWidths[i] = GetColDataBoxWidth(g, i); + } + + // compute the layout + // + ComputeLayout(bounds, tableNameBoxWidth, colsNameWidths, colsDataWidths); + + // paint the navigation arrows, if necessary + // + if (!layout.leftArrow.IsEmpty) + { + g.FillRectangle(BackBrush, layout.leftArrow); + PaintLeftArrow(g, layout.leftArrow, alignToRight); + } + + // paint the parent rows: + // + Rectangle rowBounds = layout.data; + for (int row = 0; row < parentsCount; ++row) { + rowBounds.Height = (int) rowHeights[row]; + if (rowBounds.Y > bounds.Bottom) + break; + int paintedWidth = PaintRow(g, rowBounds, row, dataGrid.Font, alignToRight, tableNameBoxWidth, colsNameWidths, colsDataWidths); + if (row == parentsCount-1) + break; + + // draw the grid line below + g.DrawLine(gridLinePen, rowBounds.X, rowBounds.Bottom, + rowBounds.X + paintedWidth, + rowBounds.Bottom); + rowBounds.Y += rowBounds.Height; + } + + if (!layout.rightArrow.IsEmpty) + { + g.FillRectangle(BackBrush, layout.rightArrow); + PaintRightArrow(g, layout.rightArrow, alignToRight); + } + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] + private Bitmap GetBitmap(string bitmapName, Color transparentColor) { + Bitmap b = null; + try { + b = new Bitmap(typeof(DataGridParentRows), bitmapName); + b.MakeTransparent(transparentColor); + } + catch (Exception e) { + Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString()); + } + return b; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetRightArrowBitmap() + { + if (rightArrow == null) + rightArrow = GetBitmap("DataGridParentRows.RightArrow.bmp", Color.White); + return rightArrow; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetLeftArrowBitmap() + { + if (leftArrow == null) + leftArrow = GetBitmap("DataGridParentRows.LeftArrow.bmp", Color.White); + return leftArrow; + } + + private void PaintBitmap(Graphics g, Bitmap b, Rectangle bounds) + { + // center the bitmap in the bounds: + int bmpX = bounds.X + (bounds.Width - b.Width) / 2; + int bmpY = bounds.Y + (bounds.Height - b.Height) / 2; + Rectangle bmpRect = new Rectangle(bmpX, bmpY, b.Width, b.Height); + + g.FillRectangle(BackBrush, bmpRect); + + // now draw the bitmap + ImageAttributes attr = new ImageAttributes(); + this.colorMap[0].NewColor = this.ForeColor; + attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); + g.DrawImage(b, bmpRect, 0, 0, bmpRect.Width, bmpRect.Height,GraphicsUnit.Pixel, attr); + attr.Dispose(); + } + + /* + private void PaintOverButton(Graphics g, Rectangle bounds) + { + } + */ + + private void PaintDownButton(Graphics g, Rectangle bounds) + { + g.DrawLine(Pens.Black, bounds.X, bounds.Y, bounds.X + bounds.Width, bounds.Y); // the top + g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y, bounds.X + bounds.Width, bounds.Y + bounds.Height); // the right side + g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y + bounds.Height, bounds.X, bounds.Y + bounds.Height); // the right side + g.DrawLine(Pens.Black, bounds.X, bounds.Y + bounds.Height, bounds.X, bounds.Y); // the left side + } + + private void PaintLeftArrow(Graphics g, Rectangle bounds, bool alignToRight) + { + Bitmap bmp = GetLeftArrowBitmap(); + // paint the border around this bitmap if this is the case + // + /* + if (overLeftArrow) + { + Debug.Assert(!downLeftArrow, "can both of those happen?"); + PaintOverButton(g, bounds); + layout.leftArrow.Inflate(-1,-1); + } + */ + if (downLeftArrow) + { + PaintDownButton(g, bounds); + layout.leftArrow.Inflate(-1,-1); + lock (bmp) { + PaintBitmap(g, bmp, bounds); + } + layout.leftArrow.Inflate(1,1); + } + else { + lock (bmp) { + PaintBitmap(g, bmp, bounds); + } + } + } + + private void PaintRightArrow(Graphics g, Rectangle bounds, bool alignToRight) + { + Bitmap bmp = GetRightArrowBitmap(); + // paint the border around this bitmap if this is the case + // + /* + if (overRightArrow) + { + Debug.Assert(!downRightArrow, "can both of those happen?"); + PaintOverButton(g, bounds); + layout.rightArrow.Inflate(-1,-1); + } + */ + if (downRightArrow) + { + PaintDownButton(g, bounds); + layout.rightArrow.Inflate(-1,-1); + lock (bmp) { + PaintBitmap(g, bmp, bounds); + } + layout.rightArrow.Inflate(1,1); + } + else { + lock (bmp) { + PaintBitmap(g, bmp, bounds); + } + } + } + + private int PaintRow(Graphics g, Rectangle bounds, int row, Font font, bool alignToRight, + int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { + DataGridState dgs = (DataGridState) parents[row]; + Rectangle paintBounds = bounds; + Rectangle rowBounds = bounds; + paintBounds.Height = (int) rowHeights[row]; + rowBounds.Height = (int) rowHeights[row]; + + int paintedWidth = 0; + // used for scrolling: when paiting, we will skip horizOffset cells in the dataGrid ParentRows + int skippedCells = 0; + + // paint the table name + if ( dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || + dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { + if (skippedCells < horizOffset) { + // skip this + skippedCells ++; + } + else { + paintBounds.Width = Math.Min(paintBounds.Width, tableNameBoxWidth); + paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); + string displayTableName = dgs.ListManager.GetListName() + ": "; + PaintText(g, paintBounds, displayTableName, font, true, alignToRight); // true is for painting bold + paintedWidth += paintBounds.Width; + } + } + + if (paintedWidth >= bounds.Width) + return bounds.Width; // we painted everything + + rowBounds.Width -= paintedWidth; + rowBounds.X += alignToRight ? 0 : paintedWidth; + paintedWidth += PaintColumns(g, rowBounds, dgs, font, alignToRight, colsNameWidths, colsDataWidths, skippedCells); + + // paint the possible space left after columns + if (paintedWidth < bounds.Width) { + paintBounds.X = bounds.X + paintedWidth; + paintBounds.Width = bounds.Width - paintedWidth; + paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); + g.FillRectangle(BackBrush, paintBounds); + } + return paintedWidth; + } + + private int PaintColumns(Graphics g, Rectangle bounds, DataGridState dgs, Font font, bool alignToRight, + int[] colsNameWidths, int[] colsDataWidths, int skippedCells) { + Rectangle paintBounds = bounds; + Rectangle rowBounds = bounds; + GridColumnStylesCollection cols = dgs.GridColumnStyles; + int cx = 0; + + for (int i = 0; i < cols.Count; i ++) { + if (cx >= bounds.Width) + break; + + // paint the column name, if we have to + if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || + dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { + if (skippedCells < horizOffset) { + // skip this column + } + else { + paintBounds.X = bounds.X + cx; + paintBounds.Width = Math.Min(bounds.Width - cx, colsNameWidths[i]); + paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); + + string colName = cols[i].HeaderText + ": "; + PaintText(g, paintBounds, colName, font, false, alignToRight); // false is for not painting bold + + cx += paintBounds.Width; + } + } + + if (cx >= bounds.Width) + break; + + if (skippedCells < horizOffset) { + // skip this cell + skippedCells ++; + } else { + // paint the cell contents + paintBounds.X = bounds.X + cx; + paintBounds.Width = Math.Min(bounds.Width - cx, colsDataWidths[i]); + paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); + + // when we paint the data grid parent rows, we want to paint the data at the position + // stored in the currency manager. + cols[i].Paint(g, paintBounds, (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], + dataGrid.BindingContext[dgs.DataSource, dgs.DataMember].Position, BackBrush, ForeBrush, alignToRight); + + cx += paintBounds.Width; + + // draw the line to the right (or left, according to alignRight) + // + g.DrawLine(new Pen(SystemColors.ControlDark), + alignToRight ? paintBounds.X : paintBounds.Right, + paintBounds.Y, + alignToRight ? paintBounds.X : paintBounds.Right, + paintBounds.Bottom); + + // this is how wide the line is.... + cx++; + + // put 3 pixels in between columns + // see DonnaWa + // + if ( i < cols.Count - 1) + { + paintBounds.X = bounds.X + cx; + paintBounds.Width = Math.Min(bounds.Width - cx, 3); + paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); + + g.FillRectangle(BackBrush, paintBounds); + cx += 3; + } + } + } + + return cx; + } + + /// + /// + /// Draws on the screen the text. It is used only to paint the Table Name and the column Names + /// Returns the width of bounding rectangle that was passed in + /// + private int PaintText(Graphics g, Rectangle textBounds, string text, Font font, bool bold, bool alignToRight) { + Font textFont = font; + if (bold) + try { + textFont = new Font(font, FontStyle.Bold); + } catch {} + else + textFont = font; + + // right now, we paint the entire box, cause it will be used anyway + g.FillRectangle(BackBrush, textBounds); + StringFormat format = new StringFormat(); + if (alignToRight) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + format.Alignment = StringAlignment.Far; + } + format.FormatFlags |= StringFormatFlags.NoWrap; + // part 1, section 3: put the table and the column name in the + // parent rows at the same height as the dataGridTextBoxColumn draws the string + // + textBounds.Offset(0, 2); + textBounds.Height -= 2; + g.DrawString(text, textFont, ForeBrush, textBounds, format); + format.Dispose(); + return textBounds.Width; + + } + + // will return the X coordinate of the containedRect mirrored within the surroundingRect + // according to the value of alignToRight + private int MirrorRect(Rectangle surroundingRect, Rectangle containedRect, bool alignToRight) { + Debug.Assert(containedRect.X >= surroundingRect.X && containedRect.Right <= surroundingRect.Right, "containedRect is not contained in surroundingRect"); + if (alignToRight) + return surroundingRect.Right - containedRect.Right + surroundingRect.X; + else + return containedRect.X; + } + + private class Layout { + public Rectangle data; + public Rectangle leftArrow; + public Rectangle rightArrow; + + public Layout() { + data = Rectangle.Empty; + leftArrow = Rectangle.Empty; + rightArrow = Rectangle.Empty; + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(200); + sb.Append("ParentRows Layout: \n"); + sb.Append("data = "); + sb.Append(data.ToString()); + sb.Append("\n leftArrow = "); + sb.Append(leftArrow.ToString()); + sb.Append("\n rightArrow = "); + sb.Append(rightArrow.ToString()); + sb.Append("\n"); + + return sb.ToString(); + } + } + + [ComVisible(true)] + protected internal class DataGridParentRowsAccessibleObject : AccessibleObject { + DataGridParentRows owner = null; + + public DataGridParentRowsAccessibleObject(DataGridParentRows owner) : base() { + Debug.Assert(owner != null, "DataGridParentRowsAccessibleObject must have a valid owner"); + this.owner = owner; + } + + internal DataGridParentRows Owner { + get { + return owner; + } + } + + public override Rectangle Bounds { + get { + return owner.dataGrid.RectangleToScreen(owner.dataGrid.ParentRowsBounds); + } + } + + public override string DefaultAction { + get { + return SR.GetString(SR.AccDGNavigateBack); + } + } + + public override string Name { + get { + return SR.GetString(SR.AccDGParentRows); + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return owner.dataGrid.AccessibilityObject; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.List; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.ReadOnly; + + if (owner.parentsCount == 0) { + state |= AccessibleStates.Invisible; + } + if (owner.dataGrid.ParentRowsVisible) { + state |= AccessibleStates.Expanded; + } + else { + state |= AccessibleStates.Collapsed; + } + + return state; + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return null; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + owner.dataGrid.NavigateBack(); + } + + public override AccessibleObject GetChild(int index) { + return ((DataGridState)owner.parents[index]).ParentRowAccessibleObject; + } + + public override int GetChildCount() { + return owner.parentsCount; + } + + /// + /// + /// Returns the currently focused child, if any. + /// Returns this if the object itself is focused. + /// + public override AccessibleObject GetFocused() { + return null; + } + + internal AccessibleObject GetNext(AccessibleObject child) { + int children = GetChildCount(); + bool hit = false; + + for (int i=0; i=0; i--) { + if (hit) { + return GetChild(i); + } + if (GetChild(i) == child) { + hit = true; + } + } + + return null; + } + + + /// + /// + /// Navigate to the next or previous grid entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + switch (navdir) { + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + case AccessibleNavigation.Down: + return Parent.GetChild(1); + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + return Parent.GetChild(GetChildCount() - 1); + case AccessibleNavigation.FirstChild: + if (GetChildCount() > 0) { + return GetChild(0); + } + break; + case AccessibleNavigation.LastChild: + if (GetChildCount() > 0) { + return GetChild(GetChildCount() - 1); + } + break; + + } + + return null; + + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridParentRowsLabel.cs b/WindowsForms/Managed/System/WinForms/DataGridParentRowsLabel.cs new file mode 100644 index 000000000..1a0500434 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridParentRowsLabel.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + /// + /// + /// + /// Specifies how parent row labels of a DataGrid + /// control are displayed. + /// + /// + public enum DataGridParentRowsLabelStyle { + /// + /// + /// + /// Display no parent row labels. + /// + /// + None = 0, + /// + /// + /// + /// Displaya the parent table name. + /// + /// + TableName = 1, + /// + /// + /// + /// Displaya the parent column name. + /// + /// + ColumnName = 2, + /// + /// + /// + /// Displays + /// both the parent table and column names. + /// + /// + Both = 3, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridRelationshipRow.cs b/WindowsForms/Managed/System/WinForms/DataGridRelationshipRow.cs new file mode 100644 index 000000000..04907c261 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridRelationshipRow.cs @@ -0,0 +1,1050 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System; + using System.Runtime.InteropServices; + using System.Security.Permissions; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + + using Microsoft.Win32; + using System.Diagnostics; + using System.Collections; + using System.Globalization; + + /// + /// + /// This class fully encapsulates the painting logic for a row + /// appearing in a DataGrid. + /// + internal class DataGridRelationshipRow : DataGridRow { + private const bool defaultOpen = false; + private const int expandoBoxWidth = 14; + private const int indentWidth = 20; + // private const int relationshipSpacing = 1; + private const int triangleSize = 5; + + private bool expanded = defaultOpen; + // private bool hasRelationships = false; + // private Font linkFont = null; + // private new DataGrid dataGrid; // Currently used only to obtain a Graphics object for measuring text + + // private Rectangle relationshipRect = Rectangle.Empty; + // private int relationshipHeight = 0; + + // relationships + // we should get this directly from the dgTable. + // private ArrayList relationships; + // private int focusedRelation = -1; + // private int focusedTextWidth; + + public DataGridRelationshipRow(DataGrid dataGrid, DataGridTableStyle dgTable, int rowNumber) + : base(dataGrid, dgTable, rowNumber) { + // this.dataGrid = dataGrid; + // linkFont = dataGrid.LinkFont; + // relationshipHeight = dataGrid.LinkFontHeight + this.dgTable.relationshipSpacing; + + // if (DataGrid.AllowNavigation) { + // hasRelationships = dgTable.RelationsList.Count > 0; + // } + } + + internal protected override int MinimumRowHeight(GridColumnStylesCollection cols) { + /* + if (DataGrid != null && DataGrid.LinkFontHeight + this.dgTable.relationshipSpacing != relationshipHeight) { + relationshipRect = Rectangle.Empty; + relationshipHeight = DataGrid.LinkFontHeight + this.dgTable.relationshipSpacing; + } + */ + + return base.MinimumRowHeight(cols) + (this.expanded ? GetRelationshipRect().Height : 0); + } + + internal protected override int MinimumRowHeight(DataGridTableStyle dgTable) { + /* + if (DataGrid != null && DataGrid.LinkFontHeight + this.dgTable.relationshipSpacing != relationshipHeight) { + relationshipRect = Rectangle.Empty; + relationshipHeight = DataGrid.LinkFontHeight + this.dgTable.relationshipSpacing; + } + */ + + return base.MinimumRowHeight(dgTable) + (this.expanded ? GetRelationshipRect().Height : 0); + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + public virtual bool Expanded { + get { + return expanded; + } + set { + if (expanded == value) + return; + if (expanded) + Collapse(); + else + Expand(); + } + } + + /* + private Color BorderColor { + get { + if (DataGrid == null) + return Color.Empty; + return DataGrid.GridLineColor; + } + } + */ + +#if FALSE + private int BorderWidth { + get { + DataGrid dataGrid = this.DataGrid; + if (dataGrid == null) + return 0; + // if the user set the GridLineStyle property on the dataGrid. + // then use the value of that property + DataGridLineStyle gridStyle; + int gridLineWidth; + if (this.dgTable.IsDefault) { + gridStyle = this.DataGrid.GridLineStyle; + gridLineWidth = this.DataGrid.GridLineWidth; + } else { + gridStyle = this.dgTable.GridLineStyle; + gridLineWidth = this.dgTable.GridLineWidth; + } + + if (gridStyle == DataGridLineStyle.None) + return 0; + + return gridLineWidth; + } + } +#endif //FALSE + + private int FocusedRelation { + get { + return this.dgTable.FocusedRelation; + } + set { + dgTable.FocusedRelation = value; + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + private void Collapse() { + Debug.Assert(this.dgTable.DataGrid.AllowNavigation, "how can the user collapse the relations if the grid does not allow navigation?"); + if (expanded) { + expanded = false; + // relationshipRect = Rectangle.Empty; + FocusedRelation = -1; + DataGrid.OnRowHeightChanged(this); + } + } + + protected override AccessibleObject CreateAccessibleObject() { + return new DataGridRelationshipRowAccessibleObject(this); + } + + + private void Expand() { + Debug.Assert(this.dgTable.DataGrid.AllowNavigation, "how can the user expand the relations if the grid does not allow navigation?"); + if (expanded == false + && DataGrid != null + && this.dgTable != null + && this.dgTable.RelationsList.Count > 0) { + expanded = true; + FocusedRelation = -1; + + // relationshipRect = Rectangle.Empty; + DataGrid.OnRowHeightChanged(this); + } + } + + public override int Height { + get { + int height = base.Height; + if (expanded) + return height + GetRelationshipRect().Height; + else + return height; + } + set { + // we should use the RelationshipRect only when the row is expanded + if (expanded) + base.Height = value - GetRelationshipRect().Height; + else + base.Height = value; + } + } + + // so the edit box will not paint under the + // grid line of the row + public override Rectangle GetCellBounds(int col) { + Rectangle cellBounds = base.GetCellBounds(col); + // decrement base.Height by 1, so the edit box will not + // paint over the bottom line. + cellBounds.Height = base.Height - 1; + return cellBounds; + } + + /// + /// + /// Given an origin, this procedure returns + /// a rectangle that describes the location of an outline box. + /// + /// + private Rectangle GetOutlineRect(int xOrigin, int yOrigin) { + Rectangle outline = new Rectangle(xOrigin + 2, + yOrigin + 2, + 9, + 9); + return outline; + } + + public override Rectangle GetNonScrollableArea() { + if (expanded) { + return GetRelationshipRect(); + } + else + return Rectangle.Empty; + } + + private Rectangle GetRelationshipRect() { + Debug.Assert(this.expanded, "we should need this rectangle only when the row is expanded"); + Rectangle ret = this.dgTable.RelationshipRect; + ret.Y = base.Height - this.dgTable.BorderWidth; + return ret; + } + +#if FALSE + private Rectangle GetRelationshipRect() { + if (relationshipRect.IsEmpty) { + Debug.WriteLineIf(CompModSwitches.DGRelationShpRowLayout.TraceVerbose, "GetRelationshipRect grinding away"); + if (!expanded) { + return(relationshipRect = new Rectangle(0,0,0,0)); + } + Graphics g = DataGrid.CreateGraphicsInternal(); + relationshipRect = new Rectangle(); + relationshipRect.X = 0; //indentWidth; + relationshipRect.Y = base.Height - this.dgTable.BorderWidth; + + // Determine the width of the widest relationship name + int longestRelationship = 0; + for (int r = 0; r < this.dgTable.RelationsList.Count; ++r) { + int rwidth = (int) Math.Ceiling(g.MeasureString(((string) this.dgTable.RelationsList[r]), this.DataGrid.LinkFont).Width); + if (rwidth > longestRelationship) + longestRelationship = rwidth; + } + + g.Dispose(); + + relationshipRect.Width = longestRelationship + 5; + relationshipRect.Width += 2; // relationshipRect border; + relationshipRect.Height = this.dgTable.BorderWidth + relationshipHeight * this.dgTable.RelationsList.Count; + relationshipRect.Height += 2; // relationship border + if (this.dgTable.RelationsList.Count > 0) + relationshipRect.Height += 2 * System.Windows.Forms.DataGridTableStyle.relationshipSpacing; + } + return relationshipRect; + } + +#endif// FALSE + + private Rectangle GetRelationshipRectWithMirroring() { + Rectangle relRect = GetRelationshipRect(); + bool rowHeadersVisible = this.dgTable.IsDefault ? this.DataGrid.RowHeadersVisible : this.dgTable.RowHeadersVisible; + if (rowHeadersVisible) { + int rowHeaderWidth = this.dgTable.IsDefault ? this.DataGrid.RowHeaderWidth : this.dgTable.RowHeaderWidth; + relRect.X += DataGrid.GetRowHeaderRect().X + rowHeaderWidth; + } + relRect.X = MirrorRelationshipRectangle(relRect, DataGrid.GetRowHeaderRect(), DataGrid.RightToLeft == RightToLeft.Yes); + return relRect; + } + + /// + /// + /// Called by the DataGrid when a click occurs in the row's client + /// area. The coordinates are normalized to the rectangle's top + /// left point. + /// + private bool PointOverPlusMinusGlyph(int x, int y, Rectangle rowHeaders, bool alignToRight) { + if (dgTable == null || dgTable.DataGrid == null || !dgTable.DataGrid.AllowNavigation) + return false; + Rectangle insideRowHeaders = rowHeaders; + if (!this.DataGrid.FlatMode) { + insideRowHeaders.Inflate(-1,-1); + } + + Rectangle outline = GetOutlineRect(insideRowHeaders.Right - expandoBoxWidth, 0); + + outline.X = MirrorRectangle(outline.X, outline.Width, insideRowHeaders, alignToRight); + + return outline.Contains(x,y); + } + + public override bool OnMouseDown(int x, int y, Rectangle rowHeaders, bool alignToRight) { + bool rowHeadersVisible = this.dgTable.IsDefault ? this.DataGrid.RowHeadersVisible : this.dgTable.RowHeadersVisible; + if (rowHeadersVisible) { + if (PointOverPlusMinusGlyph(x,y,rowHeaders, alignToRight)) { + if (this.dgTable.RelationsList.Count == 0) { + return false; + } + else if (expanded) { + Collapse(); + } + else { + Expand(); + } + DataGrid.OnNodeClick(EventArgs.Empty); + return true; + } + } + + if (!expanded) + return base.OnMouseDown(x, y, rowHeaders, alignToRight); + + // hit test for relationships + Rectangle relRect = GetRelationshipRectWithMirroring(); + + if (relRect.Contains(x, y)) { + int r = RelationFromY(y); + if (r != -1) { + // first, reset the FocusedRelation + FocusedRelation = -1; + DataGrid.NavigateTo(((string)this.dgTable.RelationsList[r]), this, true); + } + // DataGrid.OnLinkClick(EventArgs.Empty); + return true; + } + + return base.OnMouseDown(x, y, rowHeaders, alignToRight); + } + + public override bool OnMouseMove(int x, int y, Rectangle rowHeaders, bool alignToRight) { + if (!expanded) + return false; + + Rectangle relRect = GetRelationshipRectWithMirroring(); + + if (relRect.Contains(x, y)) { + this.DataGrid.Cursor = Cursors.Hand; + return true; + } + + this.DataGrid.Cursor = Cursors.Default; + return base.OnMouseMove(x, y, rowHeaders, alignToRight); + } + + // this function will not invalidate all of the + // row + public override void OnMouseLeft(Rectangle rowHeaders, bool alignToRight) { + if (!expanded) + return; + + Rectangle relRect = GetRelationshipRect(); + relRect.X += rowHeaders.X + this.dgTable.RowHeaderWidth; + relRect.X = MirrorRelationshipRectangle(relRect, rowHeaders, alignToRight); + + if (FocusedRelation != -1) { + InvalidateRowRect(relRect); + FocusedRelation = -1; + } + } + + public override void OnMouseLeft() { + if (!expanded) + return; + + if (FocusedRelation != -1) { + InvalidateRow(); + FocusedRelation = -1; + } + base.OnMouseLeft(); + } + + /// + /// + /// Called by the DataGrid when a keypress occurs on a row with "focus." + /// + public override bool OnKeyPress(Keys keyData) { + // ignore the shift key if it is not paired w/ the TAB key + if ((keyData & Keys.Modifiers) == Keys.Shift && (keyData & Keys.KeyCode) != Keys.Tab) + return false; + + switch (keyData & Keys.KeyCode) { + case Keys.F5: + if (dgTable == null || dgTable.DataGrid == null || !dgTable.DataGrid.AllowNavigation) + return false; + if (expanded) + Collapse(); + else + Expand(); + FocusedRelation = -1; + return true; + + // to make the gridTest run w/ the numLock key on + // + case Keys.NumLock: + if (FocusedRelation != -1) + return false; + else + return base.OnKeyPress(keyData); + case Keys.Enter: + if (FocusedRelation != -1) { + // somebody set the relation number up already + // navigate to the relation + DataGrid.NavigateTo(((string)this.dgTable.RelationsList[FocusedRelation]), this, true); + + // now reset the FocusedRelation + FocusedRelation = -1; + return true; + } + else + return false; + + case Keys.Tab: + return false; + + default: + FocusedRelation = -1; + return base.OnKeyPress(keyData); + } + } + + // will reset the FocusedRelation and will invalidate the + // rectangle so that the linkFont is no longer shown + internal override void LoseChildFocus(Rectangle rowHeaders, bool alignToRight) { + // we only invalidate stuff if the row is expanded. + if (FocusedRelation == -1 || !expanded) + return; + + FocusedRelation = -1; + Rectangle relRect = GetRelationshipRect(); + relRect.X += rowHeaders.X + this.dgTable.RowHeaderWidth; + relRect.X = MirrorRelationshipRectangle(relRect, rowHeaders, alignToRight); + InvalidateRowRect(relRect); + } + + // here is the logic for FOCUSED: + // + // first the dataGrid gets the KeyPress. + // the dataGrid passes it to the currentRow. if it is anything other + // than Enter or TAB, the currentRow resets the FocusedRelation variable. + // + // Then the dataGrid takes another look at the TAB key and if it is the case + // it passes it to the row. If the dataRelationshipRow can become focused, + // then it eats the TAB key, otherwise it will give it back to the dataGrid. + // + internal override bool ProcessTabKey(Keys keyData, Rectangle rowHeaders, bool alignToRight) { + Debug.Assert((keyData & Keys.Control) != Keys.Control, "the DataGridRelationshipRow only handles TAB and TAB-SHIFT"); + Debug.Assert((keyData & Keys.Alt) != Keys.Alt, "the DataGridRelationshipRow only handles TAB and TAB-SHIFT"); + + // if there are no relationships, this row can't do anything with the + // key + if (this.dgTable.RelationsList.Count == 0 || dgTable.DataGrid == null || !dgTable.DataGrid.AllowNavigation) + return false; + + // expand the relationship box + if (!expanded) + Expand(); + + if ((keyData & Keys.Shift) == Keys.Shift) { + if (FocusedRelation == 0) { + // if user hits TAB-SHIFT and the focus was on the first relationship then + // reset FocusedRelation and let the dataGrid use the key + // + // consider: Microsoft: if the relationships box is expanded, should we collapse it on leave? + FocusedRelation = -1; + return false; + } + + // we need to invalidate the relationshipRectangle, and cause the linkFont to move + // to the next relation + Rectangle relRect = GetRelationshipRect(); + relRect.X += rowHeaders.X + this.dgTable.RowHeaderWidth; + relRect.X = MirrorRelationshipRectangle(relRect, rowHeaders, alignToRight); + InvalidateRowRect(relRect); + + if (FocusedRelation == -1) + // is the first time that the user focuses on this + // set of relationships + FocusedRelation = this.dgTable.RelationsList.Count - 1; + else + FocusedRelation --; + return true; + } + else { + if (FocusedRelation == this.dgTable.RelationsList.Count - 1) { + // if the user hits TAB and the focus was on the last relationship then + // reset FocusedRelation and let the dataGrid use the key + // + // consider: Microsoft: if the relationships box is expanded, should we collapse it on leave? + FocusedRelation = -1; + return false; + } + + // we need to invalidate the relationshipRectangle, and cause the linkFont to move + // to the next relation + Rectangle relRect = GetRelationshipRect(); + relRect.X += rowHeaders.X + this.dgTable.RowHeaderWidth; + relRect.X = MirrorRelationshipRectangle(relRect, rowHeaders, alignToRight); + InvalidateRowRect(relRect); + + FocusedRelation ++; + return true; + } + } + + /// + /// + /// Paints the row. + /// + public override int Paint(Graphics g, Rectangle bounds, Rectangle trueRowBounds, int firstVisibleColumn, int numVisibleColumns) { + return Paint(g, bounds, trueRowBounds, firstVisibleColumn, numVisibleColumns, false); + } + + public override int Paint(Graphics g, + Rectangle bounds, // negative offsetted row bounds + Rectangle trueRowBounds, // real row bounds. + int firstVisibleColumn, + int numVisibleColumns, + bool alignToRight) { + if (CompModSwitches.DGRelationShpRowPaint.TraceVerbose) Debug.WriteLine("Painting row " + RowNumber.ToString(CultureInfo.InvariantCulture) + " with bounds " + bounds.ToString()); + int bWidth = this.dgTable.BorderWidth; + + // paint the data cells + Rectangle dataBounds = bounds; + dataBounds.Height = base.Height - bWidth; + int dataWidth = PaintData(g, dataBounds, firstVisibleColumn, numVisibleColumns, alignToRight); + int dataWidthOffsetted = dataWidth + bounds.X - trueRowBounds.X; + + dataBounds.Offset(0, bWidth); // use bWidth, not 1 + if (bWidth > 0) + PaintBottomBorder(g, dataBounds, dataWidth, bWidth, alignToRight); + + if (expanded && this.dgTable.RelationsList.Count > 0) { + // paint the relationships + Rectangle relationBounds = new Rectangle(trueRowBounds.X, + dataBounds.Bottom, + trueRowBounds.Width, + trueRowBounds.Height - dataBounds.Height - 2 * bWidth); + PaintRelations(g, relationBounds, trueRowBounds, dataWidthOffsetted, + firstVisibleColumn, numVisibleColumns, alignToRight); + relationBounds.Height += 1; + if (bWidth > 0) + PaintBottomBorder(g, relationBounds, dataWidthOffsetted, bWidth, alignToRight); + } + + return dataWidth; + } + + protected override void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, + Brush backBr, Brush foreBrush, bool alignToRight) { + CurrencyManager listManager = DataGrid.ListManager; + + // painting the error.. + // + string errString = String.Empty; + Rectangle bounds = cellBounds; + object errInfo = DataGrid.ListManager[this.number]; + if (errInfo is IDataErrorInfo) + errString = ((IDataErrorInfo) errInfo)[column.PropertyDescriptor.Name]; + + if (!String.IsNullOrEmpty(errString)) { + Bitmap bmp = GetErrorBitmap(); + Rectangle errRect; + lock (bmp) { + errRect = PaintIcon(g, bounds, true, alignToRight, bmp, backBr); + } + // paint the errors correctly when RTL = true + if (alignToRight) + bounds.Width -= errRect.Width + xOffset; + else + bounds.X += errRect.Width + xOffset; + DataGrid.ToolTipProvider.AddToolTip(errString, (IntPtr)(DataGrid.ToolTipId ++), errRect); + } + + column.Paint(g, bounds, listManager, this.RowNumber, backBr, foreBrush, alignToRight); + } + + public override void PaintHeader(Graphics g, Rectangle bounds, bool alignToRight, bool isDirty) { + + DataGrid grid = this.DataGrid; + + Rectangle insideBounds = bounds; + + if (!grid.FlatMode) { + ControlPaint.DrawBorder3D(g, insideBounds, Border3DStyle.RaisedInner); + insideBounds.Inflate(-1,-1); + } + + if (this.dgTable.IsDefault) + PaintHeaderInside(g, insideBounds, this.DataGrid.HeaderBackBrush, alignToRight, isDirty); + else + PaintHeaderInside(g, insideBounds, this.dgTable.HeaderBackBrush, alignToRight, isDirty); + } + + public void PaintHeaderInside(Graphics g, Rectangle bounds, Brush backBr, bool alignToRight, bool isDirty) { + // paint the row header + bool paintPlusMinus = dgTable.RelationsList.Count > 0 && dgTable.DataGrid.AllowNavigation; + int rowHeaderBoundsX = MirrorRectangle(bounds.X, + bounds.Width - (paintPlusMinus ? expandoBoxWidth : 0), + bounds, alignToRight); + + if (!alignToRight) Debug.Assert(bounds.X == rowHeaderBoundsX, "what's up doc?"); + + Rectangle rowHeaderBounds = new Rectangle(rowHeaderBoundsX, + bounds.Y, + bounds.Width - (paintPlusMinus ? expandoBoxWidth : 0), + bounds.Height); + + base.PaintHeader(g, rowHeaderBounds, alignToRight, isDirty); + + // Paint the expando on the right + int expandoBoxX = MirrorRectangle(bounds.X + rowHeaderBounds.Width, expandoBoxWidth, bounds, alignToRight); + + if (!alignToRight) Debug.Assert(rowHeaderBounds.Right == expandoBoxX, "what's up doc?"); + + Rectangle expandoBox = new Rectangle(expandoBoxX, + bounds.Y, + expandoBoxWidth, + bounds.Height); + if (paintPlusMinus) { + PaintPlusMinusGlyph(g, expandoBox, backBr, alignToRight); + } + + } + + /// + /// + /// Paints the relationships below the data area. + /// + private void PaintRelations(Graphics g, Rectangle bounds, Rectangle trueRowBounds, + int dataWidth, int firstCol, int nCols, bool alignToRight) { + + // Calculate the relationship rect. + // relationshipRect = Rectangle.Empty; + Rectangle relRect = GetRelationshipRect(); + //relRect.Offset(trueRowBounds.X, trueRowBounds.Y); + relRect.X = alignToRight ? bounds.Right - relRect.Width : bounds.X; + relRect.Y = bounds.Y; + int paintedWidth = Math.Max(dataWidth, relRect.Width); + + // Paint the stuff to the right , or left (Bi-Di) of the relationship rect. + Region r = g.Clip; + g.ExcludeClip(relRect); + + g.FillRectangle(GetBackBrush(), + alignToRight ? bounds.Right - dataWidth : bounds.X, + bounds.Y, + dataWidth, + bounds.Height); + + // Paint the relations' text + g.SetClip(bounds); + + relRect.Height -= this.dgTable.BorderWidth; // use bWidth not 1 + g.DrawRectangle(SystemPens.ControlText, relRect.X, relRect.Y, relRect.Width - 1, relRect.Height - 1); + relRect.Inflate(-1, -1); + + int cy = PaintRelationText(g, relRect, alignToRight); + + if (cy < relRect.Height) { + g.FillRectangle(GetBackBrush(), relRect.X, relRect.Y + cy, relRect.Width, relRect.Height - cy); + } + + g.Clip = r; + + // paint any exposed area to the right or to the left (BI-DI) + if (paintedWidth < bounds.Width) { + int bWidth; + if (this.dgTable.IsDefault) + bWidth = this.DataGrid.GridLineWidth; + else + bWidth = this.dgTable.GridLineWidth; + g.FillRectangle(DataGrid.BackgroundBrush, + alignToRight ? bounds.X : bounds.X + paintedWidth, + bounds.Y, + bounds.Width - paintedWidth - bWidth + 1, // + 1 cause the relationship rectangle was deflated + bounds.Height); + + // Paint the border to the right of each cell + if (bWidth > 0) { + Brush br; + // if the user changed the gridLineColor on the dataGrid + // from the defaultValue, then use that value; + if (this.dgTable.IsDefault) + br = this.DataGrid.GridLineBrush; + else + br = this.dgTable.GridLineBrush; + g.FillRectangle(br, + alignToRight ? bounds.Right - bWidth - paintedWidth : bounds.X + paintedWidth - bWidth, + bounds.Y, + bWidth, + bounds.Height); + } + } + + } + + private int PaintRelationText(Graphics g, Rectangle bounds, bool alignToRight) { + g.FillRectangle(GetBackBrush(), bounds.X, bounds.Y, bounds.Width, System.Windows.Forms.DataGridTableStyle.relationshipSpacing); + + int relationshipHeight = this.dgTable.RelationshipHeight; + Rectangle textBounds = new Rectangle(bounds.X, bounds.Y + System.Windows.Forms.DataGridTableStyle.relationshipSpacing, + bounds.Width, + relationshipHeight); + int cy = System.Windows.Forms.DataGridTableStyle.relationshipSpacing; + for (int r = 0; r < this.dgTable.RelationsList.Count; ++r) { + if (cy > bounds.Height) + break; + + Brush textBrush = this.dgTable.IsDefault ? this.DataGrid.LinkBrush : this.dgTable.LinkBrush; + + Font textFont = this.DataGrid.Font; + textBrush = this.dgTable.IsDefault ? this.DataGrid.LinkBrush : this.dgTable.LinkBrush; + textFont = this.DataGrid.LinkFont; + + g.FillRectangle(GetBackBrush(), textBounds); + + StringFormat format = new StringFormat(); + if (alignToRight) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + format.Alignment = StringAlignment.Far; + } + g.DrawString(((string)this.dgTable.RelationsList[r]), textFont, textBrush, textBounds, + format); + if (r == FocusedRelation && this.number == this.DataGrid.CurrentCell.RowNumber) { + textBounds.Width = this.dgTable.FocusedTextWidth; + ControlPaint.DrawFocusRectangle(g, textBounds, ((SolidBrush)textBrush).Color, ((SolidBrush)GetBackBrush()).Color); + textBounds.Width = bounds.Width; + } + format.Dispose(); + + textBounds.Y += relationshipHeight; + cy += textBounds.Height; + } + return cy; + } + + /// + /// + private void PaintPlusMinusGlyph(Graphics g, Rectangle bounds, Brush backBr, bool alignToRight) { + if (CompModSwitches.DGRelationShpRowPaint.TraceVerbose) Debug.WriteLine("PlusMinusGlyph painting in bounds -> " + bounds.ToString()); + Rectangle outline = GetOutlineRect(bounds.X, bounds.Y); + + outline = Rectangle.Intersect(bounds, outline); + if (outline.IsEmpty) + return; + + g.FillRectangle(backBr, bounds); + + if (CompModSwitches.DGRelationShpRowPaint.TraceVerbose) Debug.WriteLine("Painting PlusMinusGlyph with outline -> " + outline.ToString()); + // draw the +/- box + Pen drawPen = this.dgTable.IsDefault ? this.DataGrid.HeaderForePen : this.dgTable.HeaderForePen; + g.DrawRectangle(drawPen, outline.X, outline.Y, outline.Width - 1, outline.Height - 1); + + int indent = 2; + // draw the - + g.DrawLine(drawPen, + outline.X + indent, outline.Y + outline.Width / 2, + outline.Right - indent - 1, outline.Y + outline.Width/2); // -1 on the y coordinate + + if (!expanded) { + // draw the vertical line to make + + g.DrawLine(drawPen, + outline.X + outline.Height/2, outline.Y + indent, + outline.X + outline.Height/2, outline.Bottom - indent - 1); // -1... hinting + } + else { + Point[] points = new Point[3]; + points[0] = new Point(outline.X + outline.Height/2, outline.Bottom); + + points[1] = new Point(points[0].X, bounds.Y + 2*indent + base.Height); + + points[2] = new Point(alignToRight ? bounds.X : bounds.Right, + points[1].Y); + g.DrawLines(drawPen, points); + } + } + + private int RelationFromY(int y) { + int relation = -1; + int relationshipHeight = this.dgTable.RelationshipHeight; + Rectangle relRect = GetRelationshipRect(); + int cy = base.Height - this.dgTable.BorderWidth + System.Windows.Forms.DataGridTableStyle.relationshipSpacing; + while (cy < relRect.Bottom) { + if (cy > y) + break; + cy += relationshipHeight; + relation++; + } + if (relation >= this.dgTable.RelationsList.Count) + return -1; + return relation; + } + + // given the relRect and the rowHeader, this function will return the + // X coordinate of the relationship rectangle as it should appear on the screen + private int MirrorRelationshipRectangle(Rectangle relRect, Rectangle rowHeader, bool alignToRight) { + if (alignToRight) + return rowHeader.X - relRect.Width; + else + return relRect.X; + + } + + // given the X and Width of a rectangle R1 contained in rect, + // this will return the X coordinate of the rectangle that corresponds to R1 in Bi-Di transformation + private int MirrorRectangle(int x, int width, Rectangle rect, bool alignToRight) { + if (alignToRight) + return rect.Right + rect.X - width - x; + else + return x; + } + + [ComVisible(true)] + protected class DataGridRelationshipRowAccessibleObject : DataGridRowAccessibleObject { + public DataGridRelationshipRowAccessibleObject(DataGridRow owner) : base(owner) { + } + + + protected override void AddChildAccessibleObjects(IList children) { + base.AddChildAccessibleObjects(children); + DataGridRelationshipRow row = (DataGridRelationshipRow)Owner; + if (row.dgTable.RelationsList!= null) { + for (int i=0; i 0) { + if (RelationshipRow.Expanded) { + return SR.GetString(SR.AccDGCollapse); + } + else { + return SR.GetString(SR.AccDGExpand); + } + } + return null; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = base.State; + if (RelationshipRow.dgTable.RelationsList.Count > 0) { + if (((DataGridRelationshipRow)Owner).Expanded) { + state |= AccessibleStates.Expanded; + } + else { + state |= AccessibleStates.Collapsed; + } + } + return state; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + if (RelationshipRow.dgTable.RelationsList.Count > 0) { + ((DataGridRelationshipRow)Owner).Expanded = !((DataGridRelationshipRow)Owner).Expanded; + } + } + + public override AccessibleObject GetFocused() { + DataGridRelationshipRow row = (DataGridRelationshipRow)Owner; + int focusRel = row.dgTable.FocusedRelation; + if (focusRel == -1) { + return base.GetFocused(); + } + else { + return GetChild(GetChildCount() - row.dgTable.RelationsList.Count + focusRel); + } + } + } + + [ComVisible(true)] + protected class DataGridRelationshipAccessibleObject : AccessibleObject { + DataGridRelationshipRow owner = null; + int relationship; + + public DataGridRelationshipAccessibleObject(DataGridRelationshipRow owner, int relationship) : base() { + Debug.Assert(owner != null, "DataGridRelationshipAccessibleObject must have a valid owner DataGridRow"); + this.owner = owner; + this.relationship = relationship; + } + + public override Rectangle Bounds { + get { + Rectangle rowBounds = DataGrid.GetRowBounds(owner); + + Rectangle bounds = owner.Expanded ? owner.GetRelationshipRectWithMirroring() : Rectangle.Empty; + bounds.Y += owner.dgTable.RelationshipHeight * relationship; + bounds.Height = owner.Expanded ? owner.dgTable.RelationshipHeight : 0; // when the row is collapsed the height of the relationship object should be 0 + // GetRelationshipRectWithMirroring will use the row headers width + if (!owner.Expanded) + bounds.X += rowBounds.X; + bounds.Y += rowBounds.Y; + + return owner.DataGrid.RectangleToScreen(bounds); + } + } + + public override string Name { + get { + return (string)owner.dgTable.RelationsList[relationship]; + } + } + + protected DataGridRelationshipRow Owner { + get { + return owner; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return owner.AccessibleObject; + } + } + + protected DataGrid DataGrid { + get { + return owner.DataGrid; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.Link; + } + } + + public override AccessibleStates State { + get { + + DataGridRow[] dgRows = this.DataGrid.DataGridRows; + if (Array.IndexOf(dgRows, this.owner) == -1) { + return AccessibleStates.Unavailable; + } + + AccessibleStates state = AccessibleStates.Selectable + | AccessibleStates.Focusable + | AccessibleStates.Linked; + + if (!owner.Expanded) { + state |= AccessibleStates.Invisible; + } + + if (DataGrid.Focused && Owner.dgTable.FocusedRelation == relationship) { + state |= AccessibleStates.Focused; + } + + return state; + } + } + + public override string Value { + get { + DataGridRow[] dgRows = this.DataGrid.DataGridRows; + if (Array.IndexOf(dgRows, this.owner) == -1) { + return null; + } else { + return (string)owner.dgTable.RelationsList[relationship]; + } + } + set { + // not supported + } + } + + public override string DefaultAction { + get { + return SR.GetString(SR.AccDGNavigate); + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ((DataGridRelationshipRow)Owner).Expanded = true; + owner.FocusedRelation = -1; + DataGrid.NavigateTo((string)owner.dgTable.RelationsList[relationship], owner, true); + DataGrid.BeginInvoke(new MethodInvoker(this.ResetAccessibilityLayer)); + } + + private void ResetAccessibilityLayer() { + ((DataGrid.DataGridAccessibleObject) DataGrid.AccessibilityObject).NotifyClients(AccessibleEvents.Reorder, 0); + ((DataGrid.DataGridAccessibleObject) DataGrid.AccessibilityObject).NotifyClients(AccessibleEvents.Focus, DataGrid.CurrentCellAccIndex); + ((DataGrid.DataGridAccessibleObject) DataGrid.AccessibilityObject).NotifyClients(AccessibleEvents.Selection, DataGrid.CurrentCellAccIndex); + } + + /// + /// + /// Navigate to the next or previous grid entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + switch (navdir) { + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + case AccessibleNavigation.Down: + if (relationship + 1 < owner.dgTable.RelationsList.Count) { + return Parent.GetChild(Parent.GetChildCount() - owner.dgTable.RelationsList.Count + relationship + 1); + } + break; + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + if (relationship > 0) { + return Parent.GetChild(Parent.GetChildCount() - owner.dgTable.RelationsList.Count + relationship - 1); + } + break; + } + + return null; + + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + // Focus the PropertyGridView window + // + if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) { + DataGrid.Focus(); + } + + if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) { + Owner.FocusedRelation = relationship; + } + } + + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridRow.cs b/WindowsForms/Managed/System/WinForms/DataGridRow.cs new file mode 100644 index 000000000..a63f81553 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridRow.cs @@ -0,0 +1,1073 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.Runtime.Versioning; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Runtime.InteropServices; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Imaging; + using System.Security.Permissions; + using Microsoft.Win32; + using System.Collections; + + /// + /// + /// Encapsulates the painting logic for a new row added to a + /// + /// control. + /// + internal abstract class DataGridRow : MarshalByRefObject { + internal protected int number; // row number + private bool selected; + private int height; + // protected DataRow dataRow; + private IntPtr tooltipID = new IntPtr(-1); + private string tooltip = String.Empty; + AccessibleObject accessibleObject; + + // for accessibility... + // + // internal DataGrid dataGrid; + + // will need this for the painting information ( row header color ) + // + protected DataGridTableStyle dgTable; + + // we will be mapping only the black color to + // the HeaderForeColor + // + private static ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; + + // bitmaps + // + private static Bitmap rightArrow = null; + private static Bitmap leftArrow = null; + private static Bitmap errorBmp = null; + private static Bitmap pencilBmp = null; + private static Bitmap starBmp = null; + protected const int xOffset = 3; + protected const int yOffset = 2; + + + /// + /// + /// Initializes a new instance of a . + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // This class and its derived classes are internal. + // So this is not a security back door. + ] + public DataGridRow(DataGrid dataGrid, DataGridTableStyle dgTable, int rowNumber) { + if (dataGrid == null || dgTable.DataGrid == null) + throw new ArgumentNullException("dataGrid"); + if (rowNumber < 0) + throw new ArgumentException(SR.GetString(SR.DataGridRowRowNumber), "rowNumber"); + // this.dataGrid = dataGrid; + this.number = rowNumber; + + // map the black color in the pictures to the DataGrid's HeaderForeColor + // + colorMap[0].OldColor = Color.Black; + colorMap[0].NewColor = dgTable.HeaderForeColor; + + this.dgTable = dgTable; + height = MinimumRowHeight(dgTable); + } + + public AccessibleObject AccessibleObject { + get { + if (accessibleObject == null) { + accessibleObject = CreateAccessibleObject(); + } + return accessibleObject; + } + } + + protected virtual AccessibleObject CreateAccessibleObject() { + return new DataGridRowAccessibleObject(this); + } + + internal protected virtual int MinimumRowHeight(DataGridTableStyle dgTable) { + return MinimumRowHeight(dgTable.GridColumnStyles); + } + + internal protected virtual int MinimumRowHeight(GridColumnStylesCollection columns) { + int h = dgTable.IsDefault ? this.DataGrid.PreferredRowHeight : dgTable.PreferredRowHeight; + + try { + if (this.dgTable.DataGrid.DataSource != null) { + int nCols = columns.Count; + for (int i = 0; i < nCols; ++i) { + // if (columns[i].Visible && columns[i].PropertyDescriptor != null) + if (columns[i].PropertyDescriptor != null) + h = Math.Max(h, columns[i].GetMinimumHeight()); + } + } + } + catch { + } + return h; + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + /// + /// + /// Gets the control the row belongs to. + /// + public DataGrid DataGrid { + get { + return this.dgTable.DataGrid; + } + } + + internal DataGridTableStyle DataGridTableStyle { + get { + return this.dgTable; + } + set { + dgTable = value; + } + } + + /* + public DataGridTable DataGridTable { + get { + return dgTable; + } + } + */ + + /* + public DataRow DataRow { + get { + return dataRow; + } + } + */ + + /// + /// + /// Gets or sets the height of the row. + /// + public virtual int Height { + get { + return height; + } + set { + // the height of the row should be at least 0. + // this way, if the row has a relationship list and the user resizes the row such that + // the new height does not accomodate the height of the relationship list + // the row will at least show the relationship list ( and not paint on the portion of the row above this one ) + height = Math.Max(0, value); + // when we resize the row, or when we set the PreferredRowHeigth on the + // DataGridTableStyle, we change the height of the Row, which will cause to invalidate, + // then the grid itself will do another invalidate call. + this.dgTable.DataGrid.OnRowHeightChanged(this); + } + } + + /// + /// + /// Gets the row's number. + /// + public int RowNumber { + get { + return this.number; + } + } + + /// + /// + /// Gets or sets a value indicating whether the row is selected. + /// + public virtual bool Selected { + get { + return selected; + } + set { + selected = value; + InvalidateRow(); + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + /// + /// + /// Gets the bitmap associated with the row. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetBitmap(string bitmapName) { + Bitmap b = null; + try { + b = new Bitmap(typeof(DataGridCaption), bitmapName); + b.MakeTransparent(); + } + catch (Exception e) { + Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString()); + throw e; + } + return b; + } + + /// + /// + /// When overridden in a derived class, gets the + /// where a cell's contents gets painted. + /// + public virtual Rectangle GetCellBounds(int col) { + int firstVisibleCol = this.dgTable.DataGrid.FirstVisibleColumn; + int cx = 0; + Rectangle cellBounds = new Rectangle(); + GridColumnStylesCollection columns = this.dgTable.GridColumnStyles; + if (columns != null) { + for (int i = firstVisibleCol; i < col; i++) + if (columns[i].PropertyDescriptor != null) + cx += columns[i].Width; + + int borderWidth = this.dgTable.GridLineWidth; + cellBounds = new Rectangle(cx, + 0, + columns[col].Width - borderWidth, + Height - borderWidth); + } + return cellBounds; + } + + /// + /// + /// When overridden in a derived class, gets the of the non-scrollable area of + /// the row. + /// + public virtual Rectangle GetNonScrollableArea() { + return Rectangle.Empty; + } + + /// + /// + /// Gets or sets the bitmap displayed in the row header of a new row. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetStarBitmap() { + if (starBmp == null) + starBmp = GetBitmap("DataGridRow.star.bmp"); + return starBmp; + } + + /// + /// + /// Gets or sets the bitmap displayed in the row header that indicates a row can + /// be edited. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetPencilBitmap() { + if (pencilBmp == null) + pencilBmp = GetBitmap("DataGridRow.pencil.bmp"); + return pencilBmp; + } + + /// + /// + /// Gets or sets the bitmap displayed on a row with an error. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetErrorBitmap() { + if (errorBmp == null) + errorBmp = GetBitmap("DataGridRow.error.bmp"); + errorBmp.MakeTransparent(); + return errorBmp; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetLeftArrowBitmap() { + if (leftArrow == null) + leftArrow = GetBitmap("DataGridRow.left.bmp"); + return leftArrow; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected Bitmap GetRightArrowBitmap() { + if (rightArrow == null) + rightArrow = GetBitmap("DataGridRow.right.bmp"); + return rightArrow; + } + + public virtual void InvalidateRow() { + this.dgTable.DataGrid.InvalidateRow(number); + } + + public virtual void InvalidateRowRect(Rectangle r) { + this.dgTable.DataGrid.InvalidateRowRect(number, r); + } + + /// + /// + /// When overridden in a derived class, notifies the grid that an edit will + /// occur. + /// + public virtual void OnEdit() { + } + + /// + /// + /// When overridden in a derived class, called by the control when a key press occurs on a row with focus. + /// + public virtual bool OnKeyPress(Keys keyData) { + int currentColIndex = this.dgTable.DataGrid.CurrentCell.ColumnNumber; + GridColumnStylesCollection columns = this.dgTable.GridColumnStyles; + if (columns != null && currentColIndex >= 0 && currentColIndex < columns.Count) { + DataGridColumnStyle currentColumn = columns[currentColIndex]; + if (currentColumn.KeyPress(this.RowNumber, keyData)) { + return true; + } + } + return false; + } + + /// + /// + /// Called by the when a click occurs in the row's client area + /// specifed by the x and y coordinates and the specified + /// . + /// + public virtual bool OnMouseDown(int x, int y, Rectangle rowHeaders) + { + return OnMouseDown(x,y,rowHeaders, false); + } + + /// + /// + /// When overridden in a derived class, is called by the when a click occurs + /// in the row's + /// client area, specified by x and y coordinates. + /// + public virtual bool OnMouseDown(int x, int y, Rectangle rowHeaders, bool alignToRight) { + // if we call base.OnMouseDown, then the row could not use this + // mouse click at all. in that case LoseChildFocus, so the edit control + // will become visible + LoseChildFocus(rowHeaders, alignToRight); + + // we did not use this click at all. + return false; + } + + /// + /// + /// + public virtual bool OnMouseMove(int x, int y, Rectangle rowHeaders) { + return false; + } + + /// + /// + /// When overridden in a derived class, is called by the when + /// the mouse moves within the row's client area. + /// + public virtual bool OnMouseMove(int x, int y, Rectangle rowHeaders, bool alignToRight) { + return false; + } + + /// + /// + /// + public virtual void OnMouseLeft(Rectangle rowHeaders, bool alignToRight) { + } + + public virtual void OnMouseLeft() { + } + + /// + /// + /// When overridden in a derived class, causes the RowEnter event to occur. + /// + public virtual void OnRowEnter() {} + public virtual void OnRowLeave() {} + + // processes the Tab Key + // returns TRUE if the TAB key is processed + internal abstract bool ProcessTabKey(Keys keyData, Rectangle rowHeaders, bool alignToRight); + + // tells the dataGridRow that it lost the focus + internal abstract void LoseChildFocus(Rectangle rowHeaders, bool alignToRight); + + /// + /// + /// Paints the row. + /// + public abstract int Paint(Graphics g, + Rectangle dataBounds, + Rectangle rowBounds, + int firstVisibleColumn, + int numVisibleColumns); + + public abstract int Paint(Graphics g, + Rectangle dataBounds, + Rectangle rowBounds, + int firstVisibleColumn, + int numVisibleColumns, + bool alignToRight); + + /// + /// + /// Draws a border on the bottom DataGrid.GridLineWidth pixels + /// of the bounding rectangle passed in. + /// + protected virtual void PaintBottomBorder(Graphics g, Rectangle bounds, int dataWidth) + { + PaintBottomBorder(g, bounds, dataWidth, this.dgTable.GridLineWidth, false); + } + + protected virtual void PaintBottomBorder(Graphics g, Rectangle bounds, int dataWidth, int borderWidth, bool alignToRight) { + // paint bottom border + Rectangle bottomBorder = new Rectangle(alignToRight ? bounds.Right - dataWidth : bounds.X, + bounds.Bottom - borderWidth, + dataWidth, + borderWidth); + + g.FillRectangle(this.dgTable.IsDefault ? this.DataGrid.GridLineBrush : this.dgTable.GridLineBrush, bottomBorder); + + // paint any exposed region to the right + if (dataWidth < bounds.Width) { + g.FillRectangle(this.dgTable.DataGrid.BackgroundBrush, + alignToRight ? bounds.X: bottomBorder.Right, + bottomBorder.Y, + bounds.Width - bottomBorder.Width, + borderWidth); + } + } + + /// + /// + /// Paints the row. + /// + public virtual int PaintData(Graphics g, + Rectangle bounds, + int firstVisibleColumn, + int columnCount) + { + return PaintData(g, bounds, firstVisibleColumn, columnCount, false); + } + + public virtual int PaintData(Graphics g, + Rectangle bounds, + int firstVisibleColumn, + int columnCount, + bool alignToRight) { + Debug.WriteLineIf(CompModSwitches.DGRowPaint.TraceVerbose, "Painting DataGridAddNewRow: bounds = " + bounds.ToString()); + + Rectangle cellBounds = bounds; + int bWidth = this.dgTable.IsDefault ? this.DataGrid.GridLineWidth : this.dgTable.GridLineWidth; + int cx = 0; + + DataGridCell current = this.dgTable.DataGrid.CurrentCell; + + GridColumnStylesCollection columns = dgTable.GridColumnStyles; + int nCols = columns.Count; + for (int col = firstVisibleColumn; col < nCols; ++col) { + if (cx > bounds.Width) + break; + + // if (!columns[col].Visible || columns[col].PropertyDescriptor == null) + if (columns[col].PropertyDescriptor == null || columns[col].Width <= 0) + continue; + + cellBounds.Width = columns[col].Width - bWidth; + + if (alignToRight) + cellBounds.X = bounds.Right - cx - cellBounds.Width; + else + cellBounds.X = bounds.X + cx; + + // Paint the data with the the DataGridColumn + Brush backBr = BackBrushForDataPaint(ref current, columns[col], col); + Brush foreBrush = ForeBrushForDataPaint(ref current, columns[col], col); + + PaintCellContents(g, + cellBounds, + columns[col], + backBr, + foreBrush, + alignToRight); + + // Paint the border to the right of each cell + if (bWidth > 0) { + g.FillRectangle(this.dgTable.IsDefault ? this.DataGrid.GridLineBrush : this.dgTable.GridLineBrush, + alignToRight ? cellBounds.X - bWidth : cellBounds.Right, + cellBounds.Y, + bWidth, + cellBounds.Height); + } + cx += cellBounds.Width + bWidth; + } + + // Paint any exposed area to the right ( or left ) of the data cell area + if (cx < bounds.Width) { + g.FillRectangle(this.dgTable.DataGrid.BackgroundBrush, + alignToRight ? bounds.X : bounds.X + cx, + bounds.Y, + bounds.Width - cx, + bounds.Height); + } + return cx; + } + + protected virtual void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, + Brush backBr, Brush foreBrush) + { + PaintCellContents(g, cellBounds, column, backBr, foreBrush, false); + } + + protected virtual void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, + Brush backBr, Brush foreBrush, bool alignToRight) { + g.FillRectangle(backBr, cellBounds); + } + + // + // This function will do the following: if paintIcon is set to true, then + // will draw the image on the RowHeader. if paintIcon is set to false, + // then this function will fill the rectangle on which otherwise will + // have been drawn the image + // + // will return the rectangle that includes the Icon + // + protected Rectangle PaintIcon(Graphics g, Rectangle visualBounds, bool paintIcon, bool alignToRight, Bitmap bmp) { + return PaintIcon(g, visualBounds, paintIcon, alignToRight, bmp, + this.dgTable.IsDefault ? this.DataGrid.HeaderBackBrush : this.dgTable.HeaderBackBrush); + } + protected Rectangle PaintIcon(Graphics g, Rectangle visualBounds, bool paintIcon, bool alignToRight, Bitmap bmp, Brush backBrush) { + Size bmpSize = bmp.Size; + Rectangle bmpRect = new Rectangle(alignToRight ? visualBounds.Right - xOffset - bmpSize.Width : visualBounds.X + xOffset, + visualBounds.Y + yOffset, + bmpSize.Width, + bmpSize.Height); + g.FillRectangle(backBrush, visualBounds); + if (paintIcon) + { + colorMap[0].NewColor = this.dgTable.IsDefault ? this.DataGrid.HeaderForeColor : this.dgTable.HeaderForeColor; + colorMap[0].OldColor = Color.Black; + ImageAttributes attr = new ImageAttributes(); + attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); + g.DrawImage(bmp, bmpRect, 0, 0, bmpRect.Width, bmpRect.Height,GraphicsUnit.Pixel, attr); + // g.DrawImage(bmp, bmpRect); + attr.Dispose(); + } + + return bmpRect; + } + + // assume that the row is not aligned to right, and that the row is not dirty + public virtual void PaintHeader(Graphics g, Rectangle visualBounds) { + PaintHeader(g, visualBounds, false); + } + + // assume that the row is not dirty + public virtual void PaintHeader(Graphics g, Rectangle visualBounds, bool alignToRight) { + PaintHeader(g,visualBounds, alignToRight, false); + } + + public virtual void PaintHeader(Graphics g, Rectangle visualBounds, bool alignToRight, bool rowIsDirty) { + Rectangle bounds = visualBounds; + + // paint the first part of the row header: the Arror or Pencil/Star + Bitmap bmp; + if (this is DataGridAddNewRow) + { + bmp = GetStarBitmap(); + lock (bmp) { + bounds.X += PaintIcon(g, bounds, true, alignToRight, bmp).Width + xOffset; + } + return; + } + else if (rowIsDirty) + { + bmp = GetPencilBitmap(); + lock (bmp) { + bounds.X += PaintIcon(g, bounds, RowNumber == this.DataGrid.CurrentCell.RowNumber, alignToRight, bmp).Width + xOffset; + } + } + else + { + bmp = alignToRight ? GetLeftArrowBitmap() : GetRightArrowBitmap(); + lock (bmp) { + bounds.X += PaintIcon(g, bounds, RowNumber == this.DataGrid.CurrentCell.RowNumber, alignToRight, bmp).Width + xOffset; + } + } + + // Paint the error icon + // + object errorInfo = DataGrid.ListManager[this.number]; + if (!(errorInfo is IDataErrorInfo)) + return; + + string errString = ((IDataErrorInfo) errorInfo).Error; + if (errString == null) + errString = String.Empty; + + if (tooltip != errString) { + if (!String.IsNullOrEmpty(tooltip)) { + DataGrid.ToolTipProvider.RemoveToolTip(tooltipID); + tooltip = String.Empty; + tooltipID = new IntPtr(-1); + } + } + + if (String.IsNullOrEmpty(errString)) + return; + + // we now have an error string: paint the errorIcon and add the tooltip + Rectangle errRect; + bmp = GetErrorBitmap(); + lock (bmp) { + errRect = PaintIcon(g, bounds, true, alignToRight, bmp); + } + bounds.X += errRect.Width + xOffset; + + tooltip = errString; + tooltipID = (IntPtr)((int)DataGrid.ToolTipId++); + DataGrid.ToolTipProvider.AddToolTip(tooltip, tooltipID, errRect); + } + + protected Brush GetBackBrush() { + Brush br = this.dgTable.IsDefault ? DataGrid.BackBrush : this.dgTable.BackBrush; + if (DataGrid.LedgerStyle && (RowNumber % 2 == 1)) { + br = this.dgTable.IsDefault ? this.DataGrid.AlternatingBackBrush : this.dgTable.AlternatingBackBrush; + } + return br; + } + + /// + /// + /// Returns the BackColor and TextColor that the Graphics object should use + /// for the appropriate values for a given row and column when painting the data. + /// + /// + protected Brush BackBrushForDataPaint(ref DataGridCell current, DataGridColumnStyle gridColumn, int column) { + Brush backBr = this.GetBackBrush(); + + if (Selected) { + backBr = this.dgTable.IsDefault ? this.DataGrid.SelectionBackBrush : this.dgTable.SelectionBackBrush; + } + /* + if (RowNumber == current.RowNumber && column == current.ColumnNumber) { + backBr = grid.CurrentCellBackBrush; + } + */ + return backBr; + } + + protected Brush ForeBrushForDataPaint(ref DataGridCell current, DataGridColumnStyle gridColumn, int column) { + // Brush foreBrush = gridColumn.ForeBrush; + Brush foreBrush = this.dgTable.IsDefault ? this.DataGrid.ForeBrush : this.dgTable.ForeBrush; + + if (Selected) { + foreBrush = this.dgTable.IsDefault ? this.DataGrid.SelectionForeBrush : this.dgTable.SelectionForeBrush; + } + /* + if (RowNumber == current.RowNumber && column == current.ColumnNumber) { + foreColor = grid.CurrentCellForeColor; + } + */ + return foreBrush; + } + + [ComVisible(true)] + protected class DataGridRowAccessibleObject : AccessibleObject { + ArrayList cells; + + DataGridRow owner = null; + + internal static string CellToDisplayString(DataGrid grid, int row, int column) { + if (column < grid.myGridTable.GridColumnStyles.Count) { + return grid.myGridTable.GridColumnStyles[column].PropertyDescriptor.Converter.ConvertToString(grid[row, column]); + } + else { + return ""; + } + } + + internal static object DisplayStringToCell(DataGrid grid, int row, int column, string value) { + if (column < grid.myGridTable.GridColumnStyles.Count) { + return grid.myGridTable.GridColumnStyles[column].PropertyDescriptor.Converter.ConvertFromString(value); + } + // ignore... + // + return null; + } + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // This class and its derived classes are internal. + // So this is not a security back door. + ] + public DataGridRowAccessibleObject(DataGridRow owner) : base() { + Debug.Assert(owner != null, "DataGridRowAccessibleObject must have a valid owner DataGridRow"); + this.owner = owner; + DataGrid grid = DataGrid; + Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create row accessible object"); + + EnsureChildren(); + } + + private void EnsureChildren() { + if (cells == null) { + // default size... little extra for relationships... + // + cells = new ArrayList(DataGrid.myGridTable.GridColumnStyles.Count + 2); + AddChildAccessibleObjects(cells); + } + } + + protected virtual void AddChildAccessibleObjects(IList children) { + Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create row's accessible children"); + Debug.Indent(); + GridColumnStylesCollection cols = DataGrid.myGridTable.GridColumnStyles; + int len = cols.Count; + Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, len + " columns present"); + for (int i=0; i + /// + /// Returns the currently focused child, if any. + /// Returns this if the object itself is focused. + /// + public override AccessibleObject GetFocused() { + if (DataGrid.Focused) { + DataGridCell cell = DataGrid.CurrentCell; + if (cell.RowNumber == owner.RowNumber) { + return (AccessibleObject)cells[cell.ColumnNumber]; + } + } + + return null; + } + + + /// + /// + /// Navigate to the next or previous grid entry.entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + switch (navdir) { + case AccessibleNavigation.Down: + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1); + + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1); + + case AccessibleNavigation.FirstChild: + if (GetChildCount() > 0) { + return GetChild(0); + } + break; + case AccessibleNavigation.LastChild: + if (GetChildCount() > 0) { + return GetChild(GetChildCount() - 1); + } + break; + } + + return null; + + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + // Focus the PropertyGridView window + // + if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) { + DataGrid.Focus(); + } + + // Select the grid entry + // + if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) { + DataGrid.CurrentRowIndex = owner.RowNumber; + } + } + + } + + [ComVisible(true)] + protected class DataGridCellAccessibleObject : AccessibleObject { + DataGridRow owner = null; + int column; + + public DataGridCellAccessibleObject(DataGridRow owner, int column) : base() { + Debug.Assert(owner != null, "DataGridColumnAccessibleObject must have a valid owner DataGridRow"); + this.owner = owner; + this.column = column; + Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create cell accessible object"); + } + + public override Rectangle Bounds { + get { + return DataGrid.RectangleToScreen(DataGrid.GetCellBounds(new DataGridCell(owner.RowNumber, column))); + } + } + + public override string Name { + get { + return DataGrid.myGridTable.GridColumnStyles[column].HeaderText; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return owner.AccessibleObject; + } + } + + protected DataGrid DataGrid { + get { + return owner.DataGrid; + } + } + + public override string DefaultAction { + get { + return SR.GetString(SR.AccDGEdit); + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.Cell; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable; + + // Determine focus + // + if (DataGrid.CurrentCell.RowNumber == owner.RowNumber + && DataGrid.CurrentCell.ColumnNumber == column) { + if (DataGrid.Focused) { + state |= AccessibleStates.Focused; + } + state |= AccessibleStates.Selected; + } + + return state; + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (owner is DataGridAddNewRow) { + return null; + } + else { + return DataGridRowAccessibleObject.CellToDisplayString(DataGrid, owner.RowNumber, column); + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set { + if (!(owner is DataGridAddNewRow)) { + object realValue = DataGridRowAccessibleObject.DisplayStringToCell(DataGrid, owner.RowNumber, column, value); + DataGrid[owner.RowNumber, column] = realValue; + } + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + Select(AccessibleSelection.TakeFocus | AccessibleSelection.TakeSelection); + } + + /// + /// + /// Returns the currently focused child, if any. + /// Returns this if the object itself is focused. + /// + public override AccessibleObject GetFocused() { + // Datagrid always returns the cell as the focused thing... so do we! + // + return DataGrid.AccessibilityObject.GetFocused(); + } + + + /// + /// + /// Navigate to the next or previous grid entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + switch (navdir) { + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + if (column < owner.AccessibleObject.GetChildCount() - 1) { + return owner.AccessibleObject.GetChild(column + 1); + } + else { + AccessibleObject o = DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1); + if (o != null) { + return o.Navigate(AccessibleNavigation.FirstChild); + } + } + break; + case AccessibleNavigation.Down: + return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1).Navigate(AccessibleNavigation.FirstChild); + case AccessibleNavigation.Up: + return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1).Navigate(AccessibleNavigation.FirstChild); + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + if (column > 0) { + return owner.AccessibleObject.GetChild(column - 1); + } + else { + AccessibleObject o = DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1); + if (o != null) { + return o.Navigate(AccessibleNavigation.LastChild); + } + } + break; + + case AccessibleNavigation.FirstChild: + case AccessibleNavigation.LastChild: + + break; + } + + return null; + + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + // Focus the PropertyGridView window + // + if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) { + DataGrid.Focus(); + } + + // Select the grid entry + // + if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) { + DataGrid.CurrentCell = new DataGridCell(owner.RowNumber, column); + } + } + + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridState.cs b/WindowsForms/Managed/System/WinForms/DataGridState.cs new file mode 100644 index 000000000..33b068c13 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridState.cs @@ -0,0 +1,242 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Text; + using System.Runtime.InteropServices; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Security.Permissions; + + /// + /// + /// Encapsulates the state of a DataGrid that changes when the + /// user navigates back and forth through ADO.NET data relations. + /// + internal sealed class DataGridState : ICloneable { + // fields + // + public object DataSource = null; + public string DataMember = null; + public CurrencyManager ListManager = null; + public DataGridRow[] DataGridRows = new DataGridRow[0]; + public DataGrid DataGrid; + public int DataGridRowsLength = 0; + public GridColumnStylesCollection GridColumnStyles = null; + + public int FirstVisibleRow = 0; + public int FirstVisibleCol = 0; + + public int CurrentRow = 0; + public int CurrentCol = 0; + + public DataGridRow LinkingRow = null; + AccessibleObject parentRowAccessibleObject; + + public DataGridState() { + } + + public DataGridState(DataGrid dataGrid) { + PushState(dataGrid); + } + + internal AccessibleObject ParentRowAccessibleObject { + get { + if (parentRowAccessibleObject == null) { + parentRowAccessibleObject = new DataGridStateParentRowAccessibleObject(this); + } + return parentRowAccessibleObject; + } + } + + // methods + // + + public object Clone() { + DataGridState dgs = new DataGridState(); + dgs.DataGridRows = DataGridRows; + dgs.DataSource = DataSource; + dgs.DataMember = DataMember; + dgs.FirstVisibleRow = FirstVisibleRow; + dgs.FirstVisibleCol = FirstVisibleCol; + dgs.CurrentRow = CurrentRow; + dgs.CurrentCol = CurrentCol; + dgs.GridColumnStyles = GridColumnStyles; + dgs.ListManager = ListManager; + dgs.DataGrid = DataGrid; + return dgs; + } + + /// + /// + /// Called by a DataGrid when it wishes to preserve its + /// transient state in the current DataGridState object. + /// + public void PushState(DataGrid dataGrid) { + this.DataSource = dataGrid.DataSource; + this.DataMember = dataGrid.DataMember; + this.DataGrid = dataGrid; + this.DataGridRows = dataGrid.DataGridRows; + this.DataGridRowsLength = dataGrid.DataGridRowsLength; + this.FirstVisibleRow = dataGrid.firstVisibleRow; + this.FirstVisibleCol = dataGrid.firstVisibleCol; + this.CurrentRow = dataGrid.currentRow; + this.GridColumnStyles = new GridColumnStylesCollection(dataGrid.myGridTable); + + this.GridColumnStyles.Clear(); + foreach(DataGridColumnStyle style in dataGrid.myGridTable.GridColumnStyles) { + this.GridColumnStyles.Add(style); + } + + this.ListManager = dataGrid.ListManager; + this.ListManager.ItemChanged += new ItemChangedEventHandler(DataSource_Changed); + this.ListManager.MetaDataChanged += new EventHandler(DataSource_MetaDataChanged); + this.CurrentCol = dataGrid.currentCol; + } + + // this is needed so that the parent rows will remove notification from the list + // when the datagridstate is no longer needed; + public void RemoveChangeNotification() { + this.ListManager.ItemChanged -= new ItemChangedEventHandler(DataSource_Changed); + this.ListManager.MetaDataChanged -= new EventHandler(DataSource_MetaDataChanged); + } + + /// + /// + /// Called by a grid when it wishes to match its transient + /// state with the current DataGridState object. + /// + public void PullState(DataGrid dataGrid, bool createColumn) { + // dataGrid.DataSource = DataSource; + // dataGrid.DataMember = DataMember; + dataGrid.Set_ListManager(DataSource, DataMember, true, createColumn); // true for forcing new listManager, + + /* + if (DataSource.Table.ParentRelations.Count > 0) + dataGrid.PopulateColumns(); + */ + + dataGrid.firstVisibleRow = FirstVisibleRow; + dataGrid.firstVisibleCol = FirstVisibleCol; + dataGrid.currentRow = CurrentRow; + dataGrid.currentCol = CurrentCol; + dataGrid.SetDataGridRows(DataGridRows, DataGridRowsLength); + } + + private void DataSource_Changed(object sender, ItemChangedEventArgs e) { + if (this.DataGrid != null && this.ListManager.Position == e.Index) { + DataGrid.InvalidateParentRows(); + return; + } + + if (this.DataGrid != null) + DataGrid.ParentRowsDataChanged(); + } + + private void DataSource_MetaDataChanged(object sender, EventArgs e) { + if (this.DataGrid != null) + DataGrid.ParentRowsDataChanged(); + } + + + [ComVisible(true)] + internal class DataGridStateParentRowAccessibleObject : AccessibleObject { + DataGridState owner = null; + + public DataGridStateParentRowAccessibleObject(DataGridState owner) : base() { + Debug.Assert(owner != null, "DataGridRowAccessibleObject must have a valid owner DataGridRow"); + this.owner = owner; + } + + public override Rectangle Bounds { + get { + DataGridParentRows dataGridParentRows = ((DataGridParentRows.DataGridParentRowsAccessibleObject)this.Parent).Owner; + DataGrid g = owner.LinkingRow.DataGrid; + Rectangle r = dataGridParentRows.GetBoundsForDataGridStateAccesibility(owner); + r.Y += g.ParentRowsBounds.Y; + return g.RectangleToScreen(r); + } + } + + public override string Name { + get { + return SR.GetString(SR.AccDGParentRow); + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return owner.LinkingRow.DataGrid.ParentRowsAccessibleObject; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.ListItem; + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + StringBuilder sb = new StringBuilder(); + + CurrencyManager source = (CurrencyManager)owner.LinkingRow.DataGrid.BindingContext[owner.DataSource, owner.DataMember]; + + sb.Append(owner.ListManager.GetListName()); + sb.Append(": "); + + bool needComma = false; + foreach (DataGridColumnStyle col in owner.GridColumnStyles) { + if (needComma) { + sb.Append(", "); + } + + string colName = col.HeaderText; + string cellValue = col.PropertyDescriptor.Converter.ConvertToString(col.PropertyDescriptor.GetValue(source.Current)); + sb.Append(colName); + sb.Append(": "); + sb.Append(cellValue); + needComma = true; + } + + return sb.ToString(); + } + } + + /// + /// + /// Navigate to the next or previous grid entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + DataGridParentRows.DataGridParentRowsAccessibleObject parentAcc = (DataGridParentRows.DataGridParentRowsAccessibleObject)Parent; + + switch (navdir) { + case AccessibleNavigation.Down: + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + return parentAcc.GetNext(this); + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + return parentAcc.GetPrev(this); + } + + return null; + + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridTable.cs b/WindowsForms/Managed/System/WinForms/DataGridTable.cs new file mode 100644 index 000000000..e267d034b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridTable.cs @@ -0,0 +1,1909 @@ + +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Text; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.ComponentModel; + using System.Drawing; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + + /// + /// + /// Represents the table drawn by the control at run time. + /// + [ + ToolboxItem(false), + DesignTimeVisible(false), + //DefaultProperty("GridTableName") + ] + public class DataGridTableStyle : Component, IDataGridEditingService { + + // internal for DataGridColumn accessibility... + // + internal DataGrid dataGrid = null; + + // relationship UI + private int relationshipHeight = 0; + internal const int relationshipSpacing = 1; + private Rectangle relationshipRect = Rectangle.Empty; + private int focusedRelation = -1; + private int focusedTextWidth; + + // will contain a list of relationships that this table has + private ArrayList relationsList = new ArrayList(2); + + // the name of the table + private string mappingName = ""; + private GridColumnStylesCollection gridColumns = null; + private bool readOnly = false; + private bool isDefaultTableStyle = false; + + private static readonly object EventAllowSorting = new object(); + private static readonly object EventGridLineColor = new object(); + private static readonly object EventGridLineStyle = new object(); + private static readonly object EventHeaderBackColor = new object(); + private static readonly object EventHeaderForeColor = new object(); + private static readonly object EventHeaderFont = new object(); + private static readonly object EventLinkColor = new object(); + private static readonly object EventLinkHoverColor = new object(); + private static readonly object EventPreferredColumnWidth = new object(); + private static readonly object EventPreferredRowHeight = new object(); + private static readonly object EventColumnHeadersVisible = new object(); + private static readonly object EventRowHeaderWidth = new object(); + private static readonly object EventSelectionBackColor = new object(); + private static readonly object EventSelectionForeColor = new object(); + private static readonly object EventMappingName = new object(); + private static readonly object EventAlternatingBackColor = new object(); + private static readonly object EventBackColor = new object(); + private static readonly object EventForeColor = new object(); + private static readonly object EventReadOnly = new object(); + private static readonly object EventRowHeadersVisible = new object(); + + // add a bunch of properties, taken from the dataGrid + // + + // default values + // + private const bool defaultAllowSorting = true; + private const DataGridLineStyle defaultGridLineStyle = DataGridLineStyle.Solid; + private const int defaultPreferredColumnWidth = 75; + private const int defaultRowHeaderWidth = 35; + internal static readonly Font defaultFont = Control.DefaultFont; + internal static readonly int defaultFontHeight = defaultFont.Height; + + + // the actual place holders for properties + // + private bool allowSorting = defaultAllowSorting; + private SolidBrush alternatingBackBrush = DefaultAlternatingBackBrush; + private SolidBrush backBrush = DefaultBackBrush; + private SolidBrush foreBrush = DefaultForeBrush; + private SolidBrush gridLineBrush = DefaultGridLineBrush; + private DataGridLineStyle gridLineStyle = defaultGridLineStyle; + internal SolidBrush headerBackBrush = DefaultHeaderBackBrush; + internal Font headerFont = null; // this is ambient property to Font value. + internal SolidBrush headerForeBrush = DefaultHeaderForeBrush; + internal Pen headerForePen = DefaultHeaderForePen; + private SolidBrush linkBrush = DefaultLinkBrush; + internal int preferredColumnWidth = defaultPreferredColumnWidth; + private int prefferedRowHeight = defaultFontHeight + 3; + private SolidBrush selectionBackBrush = DefaultSelectionBackBrush; + private SolidBrush selectionForeBrush = DefaultSelectionForeBrush; + private int rowHeaderWidth = defaultRowHeaderWidth; + private bool rowHeadersVisible = true; + private bool columnHeadersVisible = true; + + // the dataGrid would need to know when the ColumnHeaderVisible, RowHeadersVisible, RowHeaderWidth + // and preferredColumnWidth, preferredRowHeight properties are changed in the current dataGridTableStyle + // also: for GridLineStyle, GridLineColor, ForeColor, BackColor, HeaderBackColor, HeaderFont, HeaderForeColor + // LinkColor, LinkHoverColor + // + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(defaultAllowSorting), + SRDescription(SR.DataGridAllowSortingDescr) + ] + public bool AllowSorting { + get { + return allowSorting; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "AllowSorting")); + } + + if (allowSorting != value) { + allowSorting = value; + OnAllowSortingChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied] + /// + public event EventHandler AllowSortingChanged { + add { + Events.AddHandler(EventAllowSorting, value); + } + remove { + Events.RemoveHandler(EventAllowSorting, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridAlternatingBackColorDescr) + ] + public Color AlternatingBackColor { + get { + return alternatingBackBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "AlternatingBackColor")); + } + + if (System.Windows.Forms.DataGrid.IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleTransparentAlternatingBackColorNotAllowed)); + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, + "AlternatingBackColor")); + } + if (!alternatingBackBrush.Color.Equals(value)) { + alternatingBackBrush = new SolidBrush(value); + InvalidateInside(); + OnAlternatingBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler AlternatingBackColorChanged { + add { + Events.AddHandler(EventAlternatingBackColor, value); + } + remove { + Events.RemoveHandler(EventAlternatingBackColor, value); + } + } + /// + /// + /// [To be supplied.] + /// + public void ResetAlternatingBackColor() { + if (ShouldSerializeAlternatingBackColor()) { + AlternatingBackColor = DefaultAlternatingBackBrush.Color; + InvalidateInside(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeAlternatingBackColor() + { + return !AlternatingBackBrush.Equals(DefaultAlternatingBackBrush); + } + + internal SolidBrush AlternatingBackBrush { + get { + return alternatingBackBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializeBackColor() { + return !System.Windows.Forms.DataGridTableStyle.DefaultBackBrush.Equals(this.backBrush); + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializeForeColor() { + return !System.Windows.Forms.DataGridTableStyle.DefaultForeBrush.Equals(this.foreBrush); + } + + internal SolidBrush BackBrush { + get { + return this.backBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.ControlBackColorDescr) + ] + public Color BackColor { + get { + return this.backBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "BackColor")); + } + + if (System.Windows.Forms.DataGrid.IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleTransparentBackColorNotAllowed)); + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, + "BackColor")); + } + if (!backBrush.Color.Equals(value)) { + this.backBrush = new SolidBrush(value); + InvalidateInside(); + OnBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler BackColorChanged { + add { + Events.AddHandler(EventBackColor, value); + } + remove { + Events.RemoveHandler(EventBackColor, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public void ResetBackColor() { + if (!this.backBrush.Equals(DefaultBackBrush)) { + this.BackColor = DefaultBackBrush.Color; + } + } + + internal int BorderWidth { + get { + DataGrid dataGrid = this.DataGrid; + if (dataGrid == null) + return 0; + // if the user set the GridLineStyle property on the dataGrid. + // then use the value of that property + DataGridLineStyle gridStyle; + int gridLineWidth; + if (this.IsDefault) { + gridStyle = this.DataGrid.GridLineStyle; + gridLineWidth = this.DataGrid.GridLineWidth; + } else { + gridStyle = this.GridLineStyle; + gridLineWidth = this.GridLineWidth; + } + + if (gridStyle == DataGridLineStyle.None) + return 0; + + return gridLineWidth; + } + } + + internal static SolidBrush DefaultAlternatingBackBrush { + get { + return (SolidBrush)SystemBrushes.Window; + } + } + internal static SolidBrush DefaultBackBrush { + get { + return (SolidBrush)SystemBrushes.Window; + } + } + internal static SolidBrush DefaultForeBrush { + get { + return (SolidBrush)SystemBrushes.WindowText; + } + } + private static SolidBrush DefaultGridLineBrush { + get { + return (SolidBrush)SystemBrushes.Control; + } + } + private static SolidBrush DefaultHeaderBackBrush { + get { + return (SolidBrush)SystemBrushes.Control; + } + } + private static SolidBrush DefaultHeaderForeBrush { + get { + return (SolidBrush)SystemBrushes.ControlText; + } + } + private static Pen DefaultHeaderForePen { + get { + return new Pen(SystemColors.ControlText); + } + } + private static SolidBrush DefaultLinkBrush { + get { + return (SolidBrush)SystemBrushes.HotTrack; + } + } + private static SolidBrush DefaultSelectionBackBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaption; + } + } + private static SolidBrush DefaultSelectionForeBrush { + get { + return (SolidBrush)SystemBrushes.ActiveCaptionText; + } + } + + internal int FocusedRelation { + get { + return focusedRelation; + } + set { + if (focusedRelation != value) { + focusedRelation = value; + if (focusedRelation == -1) { + focusedTextWidth = 0; + } else { + Graphics g = DataGrid.CreateGraphicsInternal(); + focusedTextWidth = (int) Math.Ceiling(g.MeasureString(((string) RelationsList[focusedRelation]), DataGrid.LinkFont).Width); + g.Dispose(); + } + } + } + } + + internal int FocusedTextWidth { + get { + return this.focusedTextWidth; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.ControlForeColorDescr) + ] + public Color ForeColor { + get { + return this.foreBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "ForeColor")); + } + + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, + "BackColor")); + } + if (!foreBrush.Color.Equals(value)) { + this.foreBrush = new SolidBrush(value); + InvalidateInside(); + OnForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler ForeColorChanged { + add { + Events.AddHandler(EventForeColor, value); + } + remove { + Events.RemoveHandler(EventForeColor, value); + } + } + + internal SolidBrush ForeBrush { + get { + return this.foreBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + public void ResetForeColor() { + if (!this.foreBrush.Equals(DefaultForeBrush)) { + this.ForeColor = DefaultForeBrush.Color; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridGridLineColorDescr) + ] + public Color GridLineColor { + get { + return gridLineBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "GridLineColor")); + } + + if (gridLineBrush.Color != value) { + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "GridLineColor")); + gridLineBrush = new SolidBrush(value); + + // Invalidate(layout.Data); + OnGridLineColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler GridLineColorChanged { + add { + Events.AddHandler(EventGridLineColor, value); + } + remove { + Events.RemoveHandler(EventGridLineColor, value); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeGridLineColor() + { + return !GridLineBrush.Equals(DefaultGridLineBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetGridLineColor() { + if (ShouldSerializeGridLineColor()) { + GridLineColor = DefaultGridLineBrush.Color; + } + } + + internal SolidBrush GridLineBrush { + get { + return gridLineBrush; + } + } + + internal int GridLineWidth { + get { + Debug.Assert(this.GridLineStyle == DataGridLineStyle.Solid || this.GridLineStyle == DataGridLineStyle.None, "are there any other styles?"); + return GridLineStyle == DataGridLineStyle.Solid ? 1 : 0; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(defaultGridLineStyle), + SRDescription(SR.DataGridGridLineStyleDescr) + ] + public DataGridLineStyle GridLineStyle { + get { + return gridLineStyle; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "GridLineStyle")); + } + + //valid values are 0x0 to 0x1. + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridLineStyle.None, (int)DataGridLineStyle.Solid)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridLineStyle)); + } + if (gridLineStyle != value) { + gridLineStyle = value; + // Invalidate(layout.Data); + OnGridLineStyleChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler GridLineStyleChanged { + add { + Events.AddHandler(EventGridLineStyle, value); + } + remove { + Events.RemoveHandler(EventGridLineStyle, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridHeaderBackColorDescr) + ] + public Color HeaderBackColor { + get { + return headerBackBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "HeaderBackColor")); + } + + if (System.Windows.Forms.DataGrid.IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleTransparentHeaderBackColorNotAllowed)); + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderBackColor")); + if (!value.Equals(headerBackBrush.Color)) { + headerBackBrush = new SolidBrush(value); + + /* + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + */ + OnHeaderBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler HeaderBackColorChanged { + add { + Events.AddHandler(EventHeaderBackColor, value); + } + remove { + Events.RemoveHandler(EventHeaderBackColor, value); + } + } + + internal SolidBrush HeaderBackBrush { + get { + return headerBackBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeHeaderBackColor() + { + return !HeaderBackBrush.Equals(DefaultHeaderBackBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderBackColor() { + if(ShouldSerializeHeaderBackColor()) { + HeaderBackColor = DefaultHeaderBackBrush.Color; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(null), + SRDescription(SR.DataGridHeaderFontDescr) + ] + public Font HeaderFont { + get { + return(headerFont == null ? (this.DataGrid == null ? Control.DefaultFont : this.DataGrid.Font) : headerFont); + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "HeaderFont")); + } + + if (value == null && headerFont != null || (value != null && !value.Equals(headerFont))) { + headerFont = value; + /* + RecalculateFonts(); + PerformLayout(); + Invalidate(layout.Inside); + */ + OnHeaderFontChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler HeaderFontChanged { + add { + Events.AddHandler(EventHeaderFont, value); + } + remove { + Events.RemoveHandler(EventHeaderFont, value); + } + } + + /// + /// + /// [To be supplied.] + /// + private bool ShouldSerializeHeaderFont() { + return(headerFont != null); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderFont() { + if (headerFont != null) { + headerFont = null; + /* + RecalculateFonts(); + PerformLayout(); + Invalidate(layout.Inside); + */ + OnHeaderFontChanged(EventArgs.Empty); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridHeaderForeColorDescr) + ] + public Color HeaderForeColor { + get { + return headerForePen.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "HeaderForeColor")); + } + + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderForeColor")); + if (!value.Equals(headerForePen.Color)) { + headerForePen = new Pen(value); + headerForeBrush = new SolidBrush(value); + + /* + if (layout.RowHeadersVisible) + Invalidate(layout.RowHeaders); + if (layout.ColumnHeadersVisible) + Invalidate(layout.ColumnHeaders); + Invalidate(layout.TopLeftHeader); + */ + OnHeaderForeColorChanged(EventArgs.Empty); + } + } + } + + /// + public event EventHandler HeaderForeColorChanged { + add { + Events.AddHandler(EventHeaderForeColor, value); + } + remove { + Events.RemoveHandler(EventHeaderForeColor, value); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeHeaderForeColor() + { + return !HeaderForePen.Equals(DefaultHeaderForePen); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetHeaderForeColor() { + if(ShouldSerializeHeaderForeColor()) { + HeaderForeColor = DefaultHeaderForeBrush.Color; + } + } + + internal SolidBrush HeaderForeBrush { + get { + return this.headerForeBrush; + } + } + + internal Pen HeaderForePen { + get { + return headerForePen; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridLinkColorDescr) + ] + public Color LinkColor { + get { + return linkBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "LinkColor")); + } + + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "LinkColor")); + if (!linkBrush.Color.Equals(value)) { + linkBrush = new SolidBrush(value); + // Invalidate(layout.Data); + OnLinkColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler LinkColorChanged { + add { + Events.AddHandler(EventLinkColor, value); + } + remove { + Events.RemoveHandler(EventLinkColor, value); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeLinkColor() + { + return !LinkBrush.Equals(DefaultLinkBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetLinkColor() { + if (ShouldSerializeLinkColor()) + LinkColor = DefaultLinkBrush.Color; + } + + internal Brush LinkBrush { + get { + return linkBrush; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + [ + SRDescription(SR.DataGridLinkHoverColorDescr), + SRCategory(SR.CatColors), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public Color LinkHoverColor { + get { + return this.LinkColor; + } + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + set { + } + } + + /// + /// + /// + /// [To be supplied.] + /// + public event EventHandler LinkHoverColorChanged { + add { + Events.AddHandler(EventLinkHoverColor, value); + } + remove { + Events.RemoveHandler(EventLinkHoverColor, value); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeLinkHoverColor() + { + return false; + // return !LinkHoverBrush.Equals(defaultLinkHoverBrush); + } + + internal Rectangle RelationshipRect { + get { + if (this.relationshipRect.IsEmpty) { + ComputeRelationshipRect(); + } + return this.relationshipRect; + } + } + + private Rectangle ComputeRelationshipRect() { + if (relationshipRect.IsEmpty && DataGrid.AllowNavigation) { + Debug.WriteLineIf(CompModSwitches.DGRelationShpRowLayout.TraceVerbose, "GetRelationshipRect grinding away"); + Graphics g = DataGrid.CreateGraphicsInternal(); + relationshipRect = new Rectangle(); + relationshipRect.X = 0; //indentWidth; + // relationshipRect.Y = base.Height - BorderWidth; + + // Determine the width of the widest relationship name + int longestRelationship = 0; + for (int r = 0; r < this.RelationsList.Count; ++r) { + int rwidth = (int) Math.Ceiling(g.MeasureString(((string) this.RelationsList[r]), this.DataGrid.LinkFont).Width) +; + if (rwidth > longestRelationship) + longestRelationship = rwidth; + } + + g.Dispose(); + + relationshipRect.Width = longestRelationship + 5; + relationshipRect.Width += 2; // relationshipRect border; + relationshipRect.Height = BorderWidth + relationshipHeight * this.RelationsList.Count; + relationshipRect.Height += 2; // relationship border + if (this.RelationsList.Count > 0) + relationshipRect.Height += 2 * relationshipSpacing; + } + return relationshipRect; + } + + internal void ResetRelationsUI() { + this.relationshipRect = Rectangle.Empty; + this.focusedRelation = -1; + this.relationshipHeight = this.dataGrid.LinkFontHeight + relationshipSpacing; + } + + internal int RelationshipHeight { + get { + return this.relationshipHeight; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + public void ResetLinkHoverColor() { + /*if (ShouldSerializeLinkHoverColor()) + LinkHoverColor = defaultLinkHoverBrush.Color;*/ + } + + /// + /// + /// [To be supplied.] + /// + [ + DefaultValue(defaultPreferredColumnWidth), + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.DataGridPreferredColumnWidthDescr), + TypeConverter(typeof(DataGridPreferredColumnWidthTypeConverter)) + ] + public int PreferredColumnWidth { + get { + return preferredColumnWidth; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "PreferredColumnWidth")); + } + + if (value < 0) + throw new ArgumentException(SR.GetString(SR.DataGridColumnWidth), "PreferredColumnWidth"); + if (preferredColumnWidth != value) { + preferredColumnWidth = value; + + /* + // reset the dataGridRows + SetDataGridRows(null, this.DataGridRowsLength); + // layout the horizontal scroll bar + PerformLayout(); + // invalidate everything + Invalidate(); + */ + + OnPreferredColumnWidthChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler PreferredColumnWidthChanged { + add { + Events.AddHandler(EventPreferredColumnWidth, value); + } + remove { + Events.RemoveHandler(EventPreferredColumnWidth, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.DataGridPreferredRowHeightDescr) + ] + public int PreferredRowHeight { + get { + return prefferedRowHeight; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "PrefferedRowHeight")); + } + + if (value < 0) + throw new ArgumentException(SR.GetString(SR.DataGridRowRowHeight)); + prefferedRowHeight = value; + + /* + bool needToRedraw = false; + DataGridRow[] rows = DataGridRows; + + for (int i = 0; i < DataGridRowsLength; i++) + { + if (rows[i].Height != value) needToRedraw = false; + rows[i].Height = value; + } + + // if all rows' height was equal to "value" before setting it, then + // there is no need to redraw. + if (!needToRedraw) + return; + + // need to layout the scroll bars: + PerformLayout(); + + // invalidate the entire area... + Rectangle rightArea = Rectangle.Union(layout.RowHeaders, layout.Data); + Invalidate(rightArea); + */ + OnPreferredRowHeightChanged(EventArgs.Empty); + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler PreferredRowHeightChanged { + add { + Events.AddHandler(EventPreferredRowHeight, value); + } + remove { + Events.RemoveHandler(EventPreferredRowHeight, value); + } + } + + private void ResetPreferredRowHeight() { + PreferredRowHeight = defaultFontHeight + 3; + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializePreferredRowHeight() + { + return prefferedRowHeight != defaultFontHeight + 3; + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDisplay), + DefaultValue(true), + SRDescription(SR.DataGridColumnHeadersVisibleDescr) + ] + public bool ColumnHeadersVisible { + get { + return columnHeadersVisible; + } + set { + if (columnHeadersVisible != value) { + columnHeadersVisible = value; + /* + PerformLayout(); + InvalidateInside(); + */ + OnColumnHeadersVisibleChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler ColumnHeadersVisibleChanged { + add { + Events.AddHandler(EventColumnHeadersVisible, value); + } + remove { + Events.RemoveHandler(EventColumnHeadersVisible, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDisplay), + DefaultValue(true), + SRDescription(SR.DataGridRowHeadersVisibleDescr) + ] + public bool RowHeadersVisible { + get { + return rowHeadersVisible; + } + set { + if (rowHeadersVisible != value) { + rowHeadersVisible = value; + /* + PerformLayout(); + InvalidateInside(); + */ + OnRowHeadersVisibleChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler RowHeadersVisibleChanged { + add { + Events.AddHandler(EventRowHeadersVisible, value); + } + remove { + Events.RemoveHandler(EventRowHeadersVisible, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(defaultRowHeaderWidth), + Localizable(true), + SRDescription(SR.DataGridRowHeaderWidthDescr) + ] + public int RowHeaderWidth { + get { + return rowHeaderWidth; + } + set { + if (this.DataGrid != null) + value = Math.Max(this.DataGrid.MinimumRowHeaderWidth(), value); + if (rowHeaderWidth != value) + { + rowHeaderWidth = value; + /* + if (layout.RowHeadersVisible) + { + PerformLayout(); + InvalidateInside(); + } + */ + OnRowHeaderWidthChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler RowHeaderWidthChanged { + add { + Events.AddHandler(EventRowHeaderWidth, value); + } + remove { + Events.RemoveHandler(EventRowHeaderWidth, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatColors), + SRDescription(SR.DataGridSelectionBackColorDescr) + ] + public Color SelectionBackColor { + get { + return selectionBackBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "SelectionBackColor")); + } + + if (System.Windows.Forms.DataGrid.IsTransparentColor(value)) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleTransparentSelectionBackColorNotAllowed)); + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionBackColor")); + if (!value.Equals(selectionBackBrush.Color)) { + selectionBackBrush = new SolidBrush(value); + + InvalidateInside(); + + OnSelectionBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler SelectionBackColorChanged { + add { + Events.AddHandler(EventSelectionBackColor, value); + } + remove { + Events.RemoveHandler(EventSelectionBackColor, value); + } + } + + internal SolidBrush SelectionBackBrush { + get { + return this.selectionBackBrush; + } + } + + internal SolidBrush SelectionForeBrush { + get { + return this.selectionForeBrush; + } + } + + /// + /// + /// [To be supplied.] + /// + protected bool ShouldSerializeSelectionBackColor() + { + return !DefaultSelectionBackBrush.Equals(selectionBackBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetSelectionBackColor() { + if (ShouldSerializeSelectionBackColor()) + SelectionBackColor = DefaultSelectionBackBrush.Color; + } + + /// + /// + /// [To be supplied.] + /// + [ + Description("The foreground color for the current data grid row"), + SRCategory(SR.CatColors), + SRDescription(SR.DataGridSelectionForeColorDescr) + ] + public Color SelectionForeColor { + get { + return selectionForeBrush.Color; + } + set { + if (this.isDefaultTableStyle) { + throw new ArgumentException(SR.GetString(SR.DataGridDefaultTableSet, "SelectionForeColor")); + } + + if (value.IsEmpty) + throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionForeColor")); + if (!value.Equals(selectionForeBrush.Color)) { + selectionForeBrush = new SolidBrush(value); + + InvalidateInside(); + + OnSelectionForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler SelectionForeColorChanged { + add { + Events.AddHandler(EventSelectionForeColor, value); + } + remove { + Events.RemoveHandler(EventSelectionForeColor, value); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual bool ShouldSerializeSelectionForeColor() + { + return !SelectionForeBrush.Equals(DefaultSelectionForeBrush); + } + + /// + /// + /// [To be supplied.] + /// + public void ResetSelectionForeColor() { + if (ShouldSerializeSelectionForeColor()) + SelectionForeColor = DefaultSelectionForeBrush.Color; + } + + // will need this function from the dataGrid + // + private void InvalidateInside() { + if (this.DataGrid != null) + this.DataGrid.InvalidateInside(); + } + + /// + /// + /// + /// [To be supplied.] + /// + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // This has already shipped so we can't change it. + ] + public static readonly DataGridTableStyle DefaultTableStyle = new DataGridTableStyle(true); + + + /// + /// + /// + /// Initializes a new instance of the class. + /// + public DataGridTableStyle(bool isDefaultTableStyle) { + gridColumns = new GridColumnStylesCollection(this, isDefaultTableStyle); + gridColumns.CollectionChanged += new CollectionChangeEventHandler(this.OnColumnCollectionChanged); + this.isDefaultTableStyle = isDefaultTableStyle; + } + + /// + /// + /// [To be supplied.] + /// + public DataGridTableStyle() : this(false) { + } + + /// + /// + /// Initializes a new instance of the class with the specified + /// . + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the GridColumnStyles + // it would be a breaking change. + ] + public DataGridTableStyle(CurrencyManager listManager) : this() { + Debug.Assert(listManager != null, "the DataGridTabel cannot use a null listManager"); + this.mappingName = listManager.GetListName(); + // set up the Relations and the columns + SetGridColumnStylesCollection(listManager); + } + + internal void SetRelationsList(CurrencyManager listManager) { + PropertyDescriptorCollection propCollection = listManager.GetItemProperties(); + Debug.Assert(!this.IsDefault, "the grid can set the relations only on a table that was manually added by the user"); + int propCount = propCollection.Count; + if (relationsList.Count > 0) + relationsList.Clear(); + for (int i = 0; i < propCount; i++) { + PropertyDescriptor prop = propCollection[i]; + Debug.Assert(prop != null, "prop is null: how that happened?"); + if (PropertyDescriptorIsARelation(prop)) { + // relation + relationsList.Add(prop.Name); + } + } + } + + internal void SetGridColumnStylesCollection(CurrencyManager listManager) { + // when we are setting the gridColumnStyles, do not handle any gridColumnCollectionChanged events + gridColumns.CollectionChanged -= new CollectionChangeEventHandler(this.OnColumnCollectionChanged); + + PropertyDescriptorCollection propCollection = listManager.GetItemProperties(); + + // we need to clear the relations list + if (relationsList.Count > 0) + relationsList.Clear(); + + Debug.Assert(propCollection != null, "propCollection is null: how that happened?"); + int propCount = propCollection.Count; + for (int i = 0; i < propCount; i++) { + PropertyDescriptor prop = propCollection[i]; + Debug.Assert(prop != null, "prop is null: how that happened?"); + // do not take into account the properties that are browsable. + if (!prop.IsBrowsable) + continue; + if (PropertyDescriptorIsARelation(prop)) { + // relation + relationsList.Add(prop.Name); + } + else + { + // column + DataGridColumnStyle col = this.CreateGridColumn(prop, this.isDefaultTableStyle); + if (this.isDefaultTableStyle) + gridColumns.AddDefaultColumn(col); + else { + col.MappingName = prop.Name; + col.HeaderText = prop.Name; + gridColumns.Add(col); + } + } + } + + // now we are able to handle the collectionChangeEvents + gridColumns.CollectionChanged += new CollectionChangeEventHandler(this.OnColumnCollectionChanged); + } + + private static bool PropertyDescriptorIsARelation(PropertyDescriptor prop) { + return typeof(IList).IsAssignableFrom(prop.PropertyType) && !typeof(Array).IsAssignableFrom(prop.PropertyType); + } + + /// + internal protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop) { + return this.CreateGridColumn(prop, false); + } + + /// + internal protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop, bool isDefault) { + DataGridColumnStyle ret = null; + Type dataType = prop.PropertyType; + + if (dataType.Equals(typeof(bool))) + ret = new DataGridBoolColumn(prop, isDefault); + else if (dataType.Equals(typeof(string))) + ret = new DataGridTextBoxColumn(prop, isDefault); + else if (dataType.Equals(typeof(DateTime))) + ret = new DataGridTextBoxColumn(prop, "d", isDefault); + + else if (dataType.Equals(typeof(Int16)) || + dataType.Equals(typeof(Int32)) || + dataType.Equals(typeof(Int64)) || + dataType.Equals(typeof(UInt16)) || + dataType.Equals(typeof(UInt32)) || + dataType.Equals(typeof(UInt64)) || + dataType.Equals(typeof(Decimal)) || + dataType.Equals(typeof(Double)) || + dataType.Equals(typeof(Single)) || + dataType.Equals(typeof(Byte)) || + dataType.Equals(typeof(SByte))) { + ret = new DataGridTextBoxColumn(prop, "G", isDefault); + } + else + { + ret = new DataGridTextBoxColumn(prop, isDefault); + } + return ret; + } + + internal void ResetRelationsList() { + if (this.isDefaultTableStyle) { + relationsList.Clear(); + } + } + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + /// + /// + /// Gets the name of this grid table. + /// + [Editor("System.Windows.Forms.Design.DataGridTableStyleMappingNameEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), DefaultValue("")] + public string MappingName { + get { + return mappingName; + } + set { + if (value == null) + value = ""; + if (value.Equals(mappingName)) + return; + string originalMappingName = this.MappingName; + mappingName = value; + + // this could throw + try { + if (this.DataGrid != null) + this.DataGrid.TableStyles.CheckForMappingNameDuplicates(this); + } catch { + this.mappingName = originalMappingName; + throw; + } + OnMappingNameChanged(EventArgs.Empty); + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler MappingNameChanged { + add { + Events.AddHandler(EventMappingName, value); + } + remove { + Events.RemoveHandler(EventMappingName, value); + } + } + + /// + /// + /// Gets the + /// list of relation objects for the grid table. + /// + internal ArrayList RelationsList { + get { + return relationsList; + } + } + + /// + /// + /// Gets the collection of columns drawn for this table. + /// + [ + Localizable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public virtual GridColumnStylesCollection GridColumnStyles { + get { + return gridColumns; + } + } + + /// + /// + /// + /// Gets or sets the + /// control displaying the table. + /// + /// + + internal void SetInternalDataGrid(DataGrid dG, bool force) + { + if (dataGrid != null && dataGrid.Equals(dG) && !force) + return; + else { + dataGrid = dG; + if (dG != null && dG.Initializing) + return; + int nCols = gridColumns.Count; + for (int i = 0; i < nCols; i++) + gridColumns[i].SetDataGridInternalInColumn(dG); + } + } + + /// + /// + /// Gets or sets the control for the drawn table. + /// + [Browsable(false)] + public virtual DataGrid DataGrid { + get { + return dataGrid; + } + set { + SetInternalDataGrid(value, true); + } + } + + /// + /// + /// Gets or sets a value indicating whether columns can be + /// edited. + /// + [DefaultValue(false)] + public virtual bool ReadOnly { + get { + return readOnly; + } + set { + if (readOnly != value) { + readOnly = value; + OnReadOnlyChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public event EventHandler ReadOnlyChanged { + add { + Events.AddHandler(EventReadOnly, value); + } + remove { + Events.RemoveHandler(EventReadOnly, value); + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + /// + /// + /// Requests an edit operation. + /// + public bool BeginEdit(DataGridColumnStyle gridColumn, int rowNumber) { + DataGrid grid = this.DataGrid; + if (grid == null) + return false; + else + return grid.BeginEdit(gridColumn, rowNumber); + } + + /// + /// + /// Requests an end to an edit + /// operation. + /// + public bool EndEdit(DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort) { + DataGrid grid = this.DataGrid; + if (grid == null) + return false; + else + return grid.EndEdit(gridColumn, rowNumber, shouldAbort); + } + + internal void InvalidateColumn(DataGridColumnStyle column) { + int index = GridColumnStyles.IndexOf(column); + if (index >= 0 && DataGrid != null) + DataGrid.InvalidateColumn(index); + } + + + private void OnColumnCollectionChanged(object sender, CollectionChangeEventArgs e) { + gridColumns.CollectionChanged -= new CollectionChangeEventHandler(this.OnColumnCollectionChanged); + + try { + DataGrid grid = this.DataGrid; + DataGridColumnStyle col = e.Element as DataGridColumnStyle; + if (e.Action == CollectionChangeAction.Add) { + if (col != null) + col.SetDataGridInternalInColumn(grid); + } else if (e.Action == CollectionChangeAction.Remove) { + if (col != null) + col.SetDataGridInternalInColumn(null); + } else { + // refresh + Debug.Assert(e.Action == CollectionChangeAction.Refresh, "there are only Add, Remove and Refresh in the CollectionChangeAction"); + // if we get a column in this collectionChangeEventArgs it means + // that the propertyDescriptor in that column changed. + if (e.Element != null) + for (int i = 0; i < gridColumns.Count; i++ ) + gridColumns[i].SetDataGridInternalInColumn(null); + } + + if (grid != null) + grid.OnColumnCollectionChanged(this, e); + } finally { + gridColumns.CollectionChanged += new CollectionChangeEventHandler(this.OnColumnCollectionChanged); + } + } + +#if false + /// + /// + /// The DataColumnCollection class actually wires up this + /// event handler to the PropertyChanged events of + /// a DataGridTable's columns. + /// + internal void OnColumnChanged(object sender, PropertyChangedEvent event) { + if (event.PropertyName.Equals("Visible")) + GenerateVisibleColumnsCache(); + } +#endif + /// + /// + /// [To be supplied.] + /// + protected virtual void OnReadOnlyChanged(EventArgs e) { + EventHandler eh = Events[EventReadOnly] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnMappingNameChanged(EventArgs e) { + EventHandler eh = Events[EventMappingName] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnAlternatingBackColorChanged(EventArgs e) { + EventHandler eh = Events[EventAlternatingBackColor] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnForeColorChanged(EventArgs e) { + EventHandler eh = Events[EventBackColor] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnBackColorChanged(EventArgs e) { + EventHandler eh = Events[EventForeColor] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnAllowSortingChanged(EventArgs e) { + EventHandler eh = Events[EventAllowSorting] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnGridLineColorChanged(EventArgs e) { + EventHandler eh = Events[EventGridLineColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnGridLineStyleChanged(EventArgs e) { + EventHandler eh = Events[EventGridLineStyle] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnHeaderBackColorChanged(EventArgs e) { + EventHandler eh = Events[EventHeaderBackColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnHeaderFontChanged(EventArgs e) { + EventHandler eh = Events[EventHeaderFont] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnHeaderForeColorChanged(EventArgs e) { + EventHandler eh = Events[EventHeaderForeColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnLinkColorChanged(EventArgs e) { + EventHandler eh = Events[EventLinkColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnLinkHoverColorChanged(EventArgs e) { + EventHandler eh = Events[EventLinkHoverColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnPreferredRowHeightChanged(EventArgs e) { + EventHandler eh = Events[EventPreferredRowHeight] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnPreferredColumnWidthChanged(EventArgs e) { + EventHandler eh = Events[EventPreferredColumnWidth] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnColumnHeadersVisibleChanged(EventArgs e) { + EventHandler eh = Events[EventColumnHeadersVisible] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnRowHeadersVisibleChanged(EventArgs e) { + EventHandler eh = Events[EventRowHeadersVisible] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnRowHeaderWidthChanged(EventArgs e) { + EventHandler eh = Events[EventRowHeaderWidth] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnSelectionForeColorChanged(EventArgs e) { + EventHandler eh = Events[EventSelectionForeColor] as EventHandler; + if (eh != null) + eh(this, e); + } + /// + /// + /// [To be supplied.] + /// + protected virtual void OnSelectionBackColorChanged(EventArgs e) { + EventHandler eh = Events[EventSelectionBackColor] as EventHandler; + if (eh != null) + eh(this, e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void Dispose(bool disposing) { + if (disposing) { + GridColumnStylesCollection cols = this.GridColumnStyles; + if (cols != null) { + for (int i = 0; i < cols.Count; i++) + cols[i].Dispose(); + } + } + base.Dispose(disposing); + } + + internal bool IsDefault { + get { + return this.isDefaultTableStyle; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridTableCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridTableCollection.cs new file mode 100644 index 000000000..f5e615b00 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridTableCollection.cs @@ -0,0 +1,345 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + + /// + /// + /// Represents a collection of objects in the + /// control. + /// + [ListBindable(false)] + public class GridTableStylesCollection : BaseCollection ,IList { + CollectionChangeEventHandler onCollectionChanged; + ArrayList items = new ArrayList(); + DataGrid owner = null; + + /// + /// + int IList.Add(object value) { + return this.Add((DataGridTableStyle) value); + } + + /// + /// + void IList.Clear() { + this.Clear(); + } + + /// + /// + bool IList.Contains(object value) { + return items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) { + return items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object value) { + this.Remove((DataGridTableStyle)value); + } + + /// + /// + void IList.RemoveAt(int index) { + this.RemoveAt(index); + } + + /// + /// + bool IList.IsFixedSize { + get {return false;} + } + + /// + /// + bool IList.IsReadOnly { + get {return false;} + } + + /// + /// + object IList.this[int index] { + get { return items[index]; } + set { throw new NotSupportedException(); } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count { + get {return this.items.Count;} + } + + /// + /// + bool ICollection.IsSynchronized { + get {return false;} + } + + /// + /// + object ICollection.SyncRoot { + get {return this;} + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() { + return items.GetEnumerator(); + } + + internal GridTableStylesCollection(DataGrid grid) { + owner = grid; + } + + /// + /// + /// [To be supplied.] + /// + protected override ArrayList List { + get { + return items; + } + } + + /* implemented in BaseCollection + /// + /// Retrieves the number of GridTables in the collection. + /// + /// + /// The number of GridTables in the collection. + /// + public override int Count { + get { + return items.Count; + } + } + */ + + /// + /// + /// Retrieves the DataGridTable with the specified index. + /// + public DataGridTableStyle this[int index] { + get { + return (DataGridTableStyle)items[index]; + } + } + + /// + /// + /// Retrieves the DataGridTable with the name provided. + /// + public DataGridTableStyle this[string tableName] { + get { + if (tableName == null) + throw new ArgumentNullException("tableName"); + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridTableStyle table = (DataGridTableStyle)items[i]; + // NOTE: case-insensitive + if (String.Equals(table.MappingName, tableName, StringComparison.OrdinalIgnoreCase)) + return table; + } + return null; + } + } + + internal void CheckForMappingNameDuplicates(DataGridTableStyle table) { + if (String.IsNullOrEmpty(table.MappingName)) + return; + for (int i = 0; i < items.Count; i++) + if ( ((DataGridTableStyle)items[i]).MappingName.Equals(table.MappingName) && table != items[i]) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleDuplicateMappingName), "table"); + } + + /// + /// + /// Adds a to this collection. + /// + public virtual int Add(DataGridTableStyle table) { + // set the rowHeaderWidth on the newly added table to at least the minimum value + // on its owner + if (this.owner != null && this.owner.MinimumRowHeaderWidth() > table.RowHeaderWidth) + table.RowHeaderWidth = this.owner.MinimumRowHeaderWidth(); + + if (table.DataGrid != owner && table.DataGrid != null) + throw new ArgumentException(SR.GetString(SR.DataGridTableStyleCollectionAddedParentedTableStyle), "table"); + table.DataGrid = owner; + CheckForMappingNameDuplicates(table); + table.MappingNameChanged += new EventHandler(TableStyleMappingNameChanged); + int index = items.Add(table); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, table)); + + return index; + } + + private void TableStyleMappingNameChanged(object sender, EventArgs pcea) { + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void AddRange(DataGridTableStyle[] tables) { + if (tables == null) { + throw new ArgumentNullException("tables"); + } + foreach(DataGridTableStyle table in tables) { + table.DataGrid = owner; + table.MappingNameChanged += new EventHandler(TableStyleMappingNameChanged); + items.Add(table); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// [To be supplied.] + /// + public event CollectionChangeEventHandler CollectionChanged { + add { + onCollectionChanged += value; + } + remove { + onCollectionChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + for (int i = 0; i < items.Count; i++) { + DataGridTableStyle element = (DataGridTableStyle)items[i]; + element.MappingNameChanged -= new EventHandler(TableStyleMappingNameChanged); + } + + items.Clear(); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// Checks to see if a DataGridTableStyle is contained in this collection. + /// + public bool Contains(DataGridTableStyle table) { + int index = items.IndexOf(table); + return index != -1; + } + + /// + /// + /// Checks to see if a with the given name + /// is contained in this collection. + /// + public bool Contains(string name) { + int itemCount = items.Count; + for (int i = 0; i < itemCount; ++i) { + DataGridTableStyle table = (DataGridTableStyle)items[i]; + // NOTE: case-insensitive + if (String.Compare(table.MappingName, name, true, CultureInfo.InvariantCulture) == 0) + return true; + } + return false; + } + + /* + public override IEnumerator GetEnumerator() { + return items.GetEnumerator(); + } + + public override IEnumerator GetEnumerator(bool allowRemove) { + if (!allowRemove) + return GetEnumerator(); + else + throw new NotSupportedException(SR.GetString(SR.DataGridTableCollectionGetEnumerator)); + } + */ + + /// + /// + /// [To be supplied.] + /// + protected void OnCollectionChanged(CollectionChangeEventArgs e) { + if (onCollectionChanged != null) + onCollectionChanged(this, e); + + DataGrid grid = owner; + if (grid != null) { + /* FOR DEMO: Microsoft: TableStylesCollection::OnCollectionChanged: set the datagridtble + DataView dataView = ((DataView) grid.DataSource); + if (dataView != null) { + DataTable dataTable = dataView.Table; + if (dataTable != null) { + if (Contains(dataTable)) { + grid.SetDataGridTable(this[dataTable]); + } + } + } + */ + grid.checkHierarchy = true; + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(DataGridTableStyle table) { + int tableIndex = -1; + int itemsCount = items.Count; + for (int i = 0; i < itemsCount; ++i) + if (items[i] == table) { + tableIndex = i; + break; + } + if (tableIndex == -1) + throw new ArgumentException(SR.GetString(SR.DataGridTableCollectionMissingTable), "table"); + else + RemoveAt(tableIndex); + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + DataGridTableStyle element = (DataGridTableStyle)items[index]; + element.MappingNameChanged -= new EventHandler(TableStyleMappingNameChanged); + items.RemoveAt(index); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, element)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridTablesFactory.cs b/WindowsForms/Managed/System/WinForms/DataGridTablesFactory.cs new file mode 100644 index 000000000..94e570c8c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridTablesFactory.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.Remoting; + + using System.Diagnostics; + using System; + using System.Collections; + using System.ComponentModel; + + using System.Windows.Forms; + using Microsoft.Win32; + + /// + /// + /// [To be supplied.] + /// + public sealed class GridTablesFactory { + // private static DataTableComparer dtComparer = new DataTableComparer(); + + // not creatable... + // + private GridTablesFactory() { + } + + + /// + /// + /// Takes a DataView and creates an intelligent mapping of + /// DataView storage types into available DataColumn types. + /// + public static DataGridTableStyle[] + CreateGridTables(DataGridTableStyle gridTable, object dataSource, string dataMember, BindingContext bindingManager) + { + return new DataGridTableStyle[] {gridTable}; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridTextBox.cs b/WindowsForms/Managed/System/WinForms/DataGridTextBox.cs new file mode 100644 index 000000000..31987118b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridTextBox.cs @@ -0,0 +1,264 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms{ + using System.Runtime.Remoting; + using System; + using System.Security.Permissions; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Runtime.InteropServices; + + /// + /// + /// Represents a control that is hosted in a + /// . + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("GridEditName") + ] + public class DataGridTextBox : TextBox { + + private bool isInEditOrNavigateMode = true; + + // only needed to signal the dataGrid that an edit + // takes place + private DataGrid dataGrid; + + /// + public DataGridTextBox() : base () { + TabStop = false; + } + /// + /// + /// Sets the to which this control belongs. + /// + public void SetDataGrid(DataGrid parentGrid) + { + dataGrid = parentGrid; + } + + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + // but what if we get a CtrlV? + // what about deleting from the menu? + if (m.Msg == NativeMethods.WM_PASTE || m.Msg == NativeMethods.WM_CUT || m.Msg == NativeMethods.WM_CLEAR) { + IsInEditOrNavigateMode = false; + dataGrid.ColumnStartedEditing(Bounds); + } + + base.WndProc(ref m); + + } + + /// + protected override void OnMouseWheel(MouseEventArgs e) { + dataGrid.TextBoxOnMouseWheel(e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + + // Shift-Space should not cause the grid to + // be put in edit mode + if (e.KeyChar == ' ' && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) + return; + + // if the edit box is in ReadOnly mode, then do not tell the DataGrid about the + // edit + if (this.ReadOnly) + return; + + // Ctrl-* should not put the grid in edit mode + if ((Control.ModifierKeys & Keys.Control) == Keys.Control && ((Control.ModifierKeys & Keys.Alt) == 0)) + return; + IsInEditOrNavigateMode = false; + + // let the DataGrid know about the edit + dataGrid.ColumnStartedEditing(Bounds); + } + + /// + /// + /// [To be supplied.] + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessKeyMessage(ref Message m) + { + Keys key = (Keys)unchecked((int)(long)m.WParam); + Keys modifierKeys = ModifierKeys; + + if ((key | modifierKeys) == Keys.Enter || (key | modifierKeys) == Keys.Escape || ((key | modifierKeys) == (Keys.Enter | Keys.Control)) + ) + { + // enter and escape keys are sent directly to the DataGrid + // for those keys, eat the WM_CHAR part of the KeyMessage + // + if (m.Msg == NativeMethods.WM_CHAR) + return true; + return ProcessKeyPreview(ref m); + } + + if (m.Msg == NativeMethods.WM_CHAR) { + if (key == Keys.LineFeed) // eat the LineFeed we get when the user presses Ctrl-Enter in a gridTextBox + return true; + return ProcessKeyEventArgs(ref m); + } + + // now the edit control will be always on top of the grid + // we only want to process the WM_KEYUP message ( the same way the grid was doing when the grid was getting all + // the keys ) + if (m.Msg == NativeMethods.WM_KEYUP) + return true; + + Keys keyData = key & Keys.KeyCode; + + switch (keyData) + { + case Keys.Right: + // here is the deal with Keys.Right: + // if the end of the selection is at the end of the string + // send this character to the dataGrid + // else, process the KeyEvent + // + if (SelectionStart + SelectionLength == Text.Length) + return ProcessKeyPreview(ref m); + return ProcessKeyEventArgs(ref m); + case Keys.Left: + // if the end of the selection is at the begining of the string + // or if the entire text is selected and we did not start editing + // send this character to the dataGrid + // else, process the KeyEvent + // + if (SelectionStart + SelectionLength == 0 || + (this.IsInEditOrNavigateMode && this.SelectionLength == Text.Length)) + return ProcessKeyPreview(ref m); + return ProcessKeyEventArgs(ref m); + case Keys.Down: + // if the end of the selection is on the last line of the text then + // send this character to the dataGrid + // else, process the KeyEvent + // + int end = SelectionStart + SelectionLength; + if (Text.IndexOf("\r\n", end) == -1) + return ProcessKeyPreview(ref m); + return ProcessKeyEventArgs(ref m); + case Keys.Up: + // if the end of the selection is on the first line of the text then + // send this character to the dataGrid + // else, process the KeyEvent + // + if ( Text.IndexOf("\r\n") < 0 || SelectionStart + SelectionLength < Text.IndexOf("\r\n")) + return ProcessKeyPreview(ref m); + return ProcessKeyEventArgs(ref m); + case Keys.Home: + case Keys.End: + if (SelectionLength == Text.Length) + return ProcessKeyPreview(ref m); + else + return ProcessKeyEventArgs(ref m); + case Keys.Prior: + case Keys.Next: + case Keys.Oemplus: + case Keys.Add: + case Keys.OemMinus: + case Keys.Subtract: + if (IsInEditOrNavigateMode) + { + // this will ultimately call parent's ProcessKeyPreview + // in our case, DataGrid's ProcessKeyPreview + return ProcessKeyPreview(ref m); + } + else + { + return ProcessKeyEventArgs(ref m); + } + case Keys.Space: + if (IsInEditOrNavigateMode && (Control.ModifierKeys & Keys.Shift) == Keys.Shift) + { + // when we get a SHIFT-SPACEBAR message, disregard the WM_CHAR part of the message + if (m.Msg == NativeMethods.WM_CHAR) return true; + + // if the user pressed the SHIFT key at the same time with + // the space key, send the key message to the DataGrid + return ProcessKeyPreview(ref m); + } + return ProcessKeyEventArgs(ref m); + case Keys.A: + if (IsInEditOrNavigateMode && (Control.ModifierKeys & Keys.Control) == Keys.Control) + { + // when we get a Control-A message, disregard the WM_CHAR part of the message + if (m.Msg == NativeMethods.WM_CHAR) return true; + + // if the user pressed the Control key at the same time with + // the space key, send the key message to the DataGrid + return ProcessKeyPreview(ref m); + } + return ProcessKeyEventArgs(ref m); + case Keys.F2: + IsInEditOrNavigateMode = false; + // do not select all the text, but + // position the caret at the end of the text + SelectionStart = Text.Length; + return true; + case Keys.Delete: + if (IsInEditOrNavigateMode) { + // pass the delete to the parent, in our case, the DataGrid + // if the dataGrid used the key, then we aren't gonne + // use it anymore, else we are + if (ProcessKeyPreview(ref m)) + return true; + else { + // the edit control will use the + // delete key: we are in Edit mode now: + IsInEditOrNavigateMode = false; + dataGrid.ColumnStartedEditing(Bounds); + + return ProcessKeyEventArgs(ref m); + } + } + else + return ProcessKeyEventArgs(ref m); + case Keys.Tab: + // the TextBox gets the Control-Tab messages, + // not the parent + if ((ModifierKeys & Keys.Control) == Keys.Control) + return ProcessKeyPreview(ref m); + else + return ProcessKeyEventArgs(ref m); + default: + return ProcessKeyEventArgs(ref m); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsInEditOrNavigateMode { + get { + return isInEditOrNavigateMode; + } + set { + isInEditOrNavigateMode = value; + if (value) + SelectAll(); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridTextBoxColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridTextBoxColumn.cs new file mode 100644 index 000000000..7dc6be888 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridTextBoxColumn.cs @@ -0,0 +1,633 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System; + + using System.Windows.Forms; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms.ComponentModel; + using System.Drawing; + + using Microsoft.Win32; + using System.Diagnostics; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Hosts a System.Windows.Forms.TextBox control in a cell of a System.Windows.Forms.DataGridColumnStyle for editing strings. + /// + public class DataGridTextBoxColumn : DataGridColumnStyle { + + // ui State + private int xMargin = 2; + private int yMargin = 1; + // private int fontHandle = 0; + private string format = null; + private TypeConverter typeConverter; + private IFormatProvider formatInfo = null; + private System.Reflection.MethodInfo parseMethod; + + // hosted control + private DataGridTextBox edit; + + // editing state + private string oldValue = null; + private int editRow = -1; + + /// + /// + /// Initializes a new instance of the System.Windows.Forms.DataGridTextBoxColumn + /// class. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format + // it would be a breaking change. + ] + public DataGridTextBoxColumn() : this(null, null) { + } + + /// + /// + /// Initializes a new instance of a System.Windows.Forms.DataGridTextBoxColumn with + /// a specified System.Data.DataColumn. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format + // it would be a breaking change. + ] + public DataGridTextBoxColumn(PropertyDescriptor prop) + : this(prop, null, false) { + } + + /// + /// + /// Initializes a new instance of a System.Windows.Forms.DataGridTextBoxColumn. with + /// the specified System.Data.DataColumn and System.Windows.Forms.ComponentModel.Format. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format + // it would be a breaking change. + ] + public DataGridTextBoxColumn(PropertyDescriptor prop, string format) : this(prop, format, false){} + + /// + /// + /// [To be supplied.] + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format + // it would be a breaking change. + ] + public DataGridTextBoxColumn(PropertyDescriptor prop, string format, bool isDefault) : base(prop, isDefault) { + edit = new DataGridTextBox(); + edit.BorderStyle = BorderStyle.None; + edit.Multiline = true; + edit.AcceptsReturn = true; + edit.Visible = false; + this.Format = format; + } + + /// + /// + /// [To be supplied.] + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set format + // it would be a breaking change. + ] + public DataGridTextBoxColumn(PropertyDescriptor prop, bool isDefault) : this(prop, null, isDefault) {} + + + // =------------------------------------------------------------------ + // = Properties + // =------------------------------------------------------------------ + + /// + /// + /// Gets the hosted System.Windows.Forms.TextBox control. + /// + [Browsable(false)] + public virtual TextBox TextBox { + get { + return edit; + } + } + + /// + internal override bool KeyPress(int rowNum, Keys keyData) { + if (edit.IsInEditOrNavigateMode) + return base.KeyPress(rowNum, keyData); + + // if the edit box is editing, then + // pass this keystroke to the edit box + // + return false; + } + + /// + /// + /// + /// Adds a System.Windows.Forms.TextBox control to the System.Windows.Forms.DataGrid control's System.Windows.Forms.Control.ControlCollection + /// . + /// + /// + protected override void SetDataGridInColumn(DataGrid value) { + base.SetDataGridInColumn(value); + if (edit.ParentInternal != null) { + edit.ParentInternal.Controls.Remove(edit); + } + if (value != null) { + value.Controls.Add(edit); + } + + // we have to tell the edit control about its dataGrid + edit.SetDataGrid(value); + } + + /* CUT as part of the new DataGridTableStyleSheet thing + public override Font Font { + set { + base.Font = value; + Font f = base.Font; + edit.Font = f; + // if (f != null) { + // fontHandle = f.Handle; + // } + } + } + */ + + /// + /// + /// Gets or sets the System.Windows.Forms.ComponentModel.Format for the System.Windows.Forms.DataGridTextBoxColumn + /// . + /// + [ + SRDescription(SR.FormatControlFormatDescr), + DefaultValue(null) + ] + public override PropertyDescriptor PropertyDescriptor { + set { + base.PropertyDescriptor = value; + if (this.PropertyDescriptor != null) { + if (this.PropertyDescriptor.PropertyType != typeof(object)) { + this.typeConverter = TypeDescriptor.GetConverter(this.PropertyDescriptor.PropertyType); + this.parseMethod = this.PropertyDescriptor.PropertyType.GetMethod("Parse", new Type[]{typeof(string), typeof(IFormatProvider)}); + } + } + } + } + + // add the corresponding value Editor: rip one from the valueEditor for the DisplayMember in the + // format object + /// + /// + /// [To be supplied.] + /// + [DefaultValue(null), Editor("System.Windows.Forms.Design.DataGridColumnStyleFormatEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor))] + public string Format { + get { + return format; + } + set { + if (value == null) + value = ""; + if (format == null || !format.Equals(value)) { + this.format = value; + + // if the associated typeConverter cannot convert from string, + // then we can't modify the column value. hence, make it readOnly + // + if (format.Length == 0) { + if (this.typeConverter != null && !typeConverter.CanConvertFrom(typeof(string))) + this.ReadOnly = true; + } + + Invalidate(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public IFormatProvider FormatInfo { + get { + return this.formatInfo; + } + set { + if (this.formatInfo == null || !this.formatInfo.Equals(value)) + this.formatInfo = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override bool ReadOnly { + get { + return base.ReadOnly; + } + set { + // if the gridColumn is can't convert the string to + // the backGround propertyDescriptor, then make the column ReadOnly + if (!value && (format == null || format.Length == 0)) { + if (this.typeConverter != null && !this.typeConverter.CanConvertFrom(typeof(string))) + return; + } + base.ReadOnly = value; + } + } + + // =------------------------------------------------------------------ + // = Methods + // =------------------------------------------------------------------ + + private void DebugOut(string s) { + Debug.WriteLineIf(CompModSwitches.DGEditColumnEditing.TraceVerbose, "DGEditColumnEditing: " + s); + } + + // will hide the edit control + /// + /// + /// Informs the column the focus is being conceded. + /// + protected internal override void ConcedeFocus() { + edit.Bounds = Rectangle.Empty; + // edit.Visible = false; + // HideEditBox(); + } + + /// + /// + /// + /// Hides the System.Windows.Forms.TextBox + /// control and moves the focus to the System.Windows.Forms.DataGrid + /// control. + /// + /// + protected void HideEditBox() { + bool wasFocused = edit.Focused; + edit.Visible = false; + + // it seems that edit.Visible = false will take away the focus from + // the edit control. And this means that we will not give the focus to the grid + // If all the columns would have an edit control this would not be bad + // ( or if the grid is the only control on the form ), + // but when we have a DataGridBoolColumn then the focus will be taken away + // by the next control in the form. + // + // if (edit.Focused && this.DataGridTableStyle.DataGrid.CanFocus) { + + // when the user deletes the current ( ie active ) column from the + // grid, the grid should still call EndEdit ( so that the changes that the user made + // before deleting the column will go to the backEnd) + // however, in that situation, we are left w/ the editColumn which is not parented. + // the grid will call Edit to reset the EditColumn + if (wasFocused && this.DataGridTableStyle != null && this.DataGridTableStyle.DataGrid != null && this.DataGridTableStyle.DataGrid.CanFocus) { + this.DataGridTableStyle.DataGrid.FocusInternal(); + Debug.Assert(!edit.Focused, "the edit control just conceeded focus to the dataGrid"); + } + } + + /// + /// + /// [To be supplied.] + /// + protected internal override void UpdateUI(CurrencyManager source, int rowNum, string displayText) + { + edit.Text = GetText(GetColumnValueAtRow(source, rowNum)); + if (!edit.ReadOnly && displayText != null) + edit.Text = displayText; + + } + + /// + /// + /// Ends an edit operation on the System.Windows.Forms.DataGridColumnStyle + /// . + /// + protected void EndEdit() { + edit.IsInEditOrNavigateMode = true; + DebugOut("Ending Edit"); + Invalidate(); + } + + /// + /// + /// Returns the optimum width and + /// height of the cell in a specified row relative + /// to the specified value. + /// + protected internal override Size GetPreferredSize(Graphics g, object value) { + Size extents = Size.Ceiling(g.MeasureString(GetText(value), DataGridTableStyle.DataGrid.Font)); + extents.Width += xMargin*2 + this.DataGridTableStyle.GridLineWidth; + extents.Height += yMargin; + return extents; + } + + /// + /// + /// + /// Gets the height of a cell in a System.Windows.Forms.DataGridColumnStyle + /// . + /// + /// + protected internal override int GetMinimumHeight() { + // why + 3? cause we have to give some way to the edit box. + return FontHeight + yMargin + 3; + } + + /// + /// + /// Gets the height to be used in for automatically resizing columns. + /// + protected internal override int GetPreferredHeight(Graphics g, object value) { + int newLineIndex = 0; + int newLines = 0; + string valueString = GetText(value); + while (newLineIndex != -1 && newLineIndex < valueString.Length) + { + newLineIndex = valueString.IndexOf("\r\n", newLineIndex + 1); + newLines ++; + } + + return FontHeight * newLines + yMargin; + } + + /// + /// + /// + /// Initiates a request to interrupt an edit procedure. + /// + /// + protected internal override void Abort(int rowNum) { + RollBack(); + HideEditBox(); + EndEdit(); + } + + // used for Alt0 functionality + /// + /// + /// + /// Enters a in the column. + /// + /// + protected internal override void EnterNullValue() { + if (this.ReadOnly) + return; + + // if the edit box is not visible, then + // do not put the edit text in it + if (!edit.Visible) + return; + + // if we are editing, then we should be able to enter alt-0 in a cell. + // + if (!edit.IsInEditOrNavigateMode) + return; + + edit.Text = NullText; + // edit.Visible = true; + edit.IsInEditOrNavigateMode = false; + // tell the dataGrid that there is an edit: + if (this.DataGridTableStyle != null && this.DataGridTableStyle.DataGrid != null) + this.DataGridTableStyle.DataGrid.ColumnStartedEditing(edit.Bounds); + } + + /// + /// + /// + /// Inititates a request to complete an editing procedure. + /// + /// + protected internal override bool Commit(CurrencyManager dataSource, int rowNum) { + // always hide the edit box + // HideEditBox(); + edit.Bounds = Rectangle.Empty; + + if (edit.IsInEditOrNavigateMode) + return true; + + try { + object value = edit.Text; + if (NullText.Equals(value)) { + value = Convert.DBNull; + edit.Text = NullText; + } else if (format != null && format.Length != 0 && this.parseMethod != null && this.FormatInfo != null) { + // use reflection to get the Parse method on the + // type of the propertyDescriptor. + value = (object) parseMethod.Invoke(null, new object[] {edit.Text, this.FormatInfo}); + if (value is IFormattable) { + edit.Text = ((IFormattable)value).ToString(format, formatInfo); + }else + edit.Text = value.ToString(); + } else if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string))) { + value = typeConverter.ConvertFromString(edit.Text); + edit.Text = typeConverter.ConvertToString(value); + } + + SetColumnValueAtRow(dataSource, rowNum, value); + } + catch { + // MessageBox.Show("There was an error caught setting field \"" + // + this.PropertyDescriptor.Name + "\" to the value \"" + edit.Text + "\"\n" + // + "The value is being rolled back to the original.\n" + // + "The error was a '" + e.Message + "' " + e.StackTrace + // , "Error commiting changes...", MessageBox.IconError); + // Debug.WriteLine(e.GetType().Name); + RollBack(); + return false; + } + DebugOut("OnCommit completed without Exception."); + EndEdit(); + return true; + } + + /// + /// + /// Prepares a cell for editing. + /// + protected internal override void Edit(CurrencyManager source, + int rowNum, + Rectangle bounds, + bool readOnly, + string displayText, + bool cellIsVisible) { + DebugOut("Begining Edit, rowNum :" + rowNum.ToString(CultureInfo.InvariantCulture)); + + Rectangle originalBounds = bounds; + + edit.ReadOnly = readOnly || ReadOnly || this.DataGridTableStyle.ReadOnly; + + edit.Text = GetText(GetColumnValueAtRow(source, rowNum)); + if (!edit.ReadOnly && displayText != null) { + // tell the grid that we are changing stuff + this.DataGridTableStyle.DataGrid.ColumnStartedEditing(bounds); + // tell the edit control that the user changed it + this.edit.IsInEditOrNavigateMode = false; + edit.Text = displayText; + } + + if (cellIsVisible) { + bounds.Offset(xMargin, 2 * yMargin); + bounds.Width -= xMargin; + bounds.Height -= 2 * yMargin; + DebugOut("edit bounds: " + bounds.ToString()); + edit.Bounds = bounds; + + edit.Visible = true; + + edit.TextAlign = this.Alignment; + } + else { + edit.Bounds = Rectangle.Empty; + // edit.Bounds = originalBounds; + // edit.Visible = false; + } + + edit.RightToLeft = this.DataGridTableStyle.DataGrid.RightToLeft; + + edit.FocusInternal(); + + editRow = rowNum; + + if (!edit.ReadOnly) { + oldValue = edit.Text; + } + + // select the text even if the text box is read only + // because the navigation code in the DataGridTextBox::ProcessKeyMessage + // uses the SelectedText property + if (displayText == null) + edit.SelectAll(); + else { + int end = edit.Text.Length; + edit.Select(end, 0); + } + + if (edit.Visible) + DataGridTableStyle.DataGrid.Invalidate(originalBounds); + } + + internal override string GetDisplayText(object value) { + return GetText(value); + } + + private string GetText(object value) { + if (value is System.DBNull) + return NullText; + else if (format != null && format.Length != 0 && (value is IFormattable)) { + try { + return ((IFormattable)value).ToString(format, this.formatInfo); + } + catch { + // + } + } else { + // use the typeConverter: + if (this.typeConverter != null && this.typeConverter.CanConvertTo(typeof(string))) + return (string)this.typeConverter.ConvertTo(value, typeof(string)); + } + return(value != null ? value.ToString() : ""); + } + + /// + /// + /// Paints the a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics, + /// System.Drawing.Rectangle, DataView.Rectangle, and row number. + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum) { + Paint(g, bounds, source, rowNum, false); + } + + /// + /// + /// + /// Paints a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics, System.Drawing.Rectangle, DataView, row number, and alignment. + /// + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight) { + string text = GetText(GetColumnValueAtRow(source, rowNum)); + PaintText(g, bounds, text, alignToRight); + } + + /// + /// + /// Paints a System.Windows.Forms.DataGridColumnStyle with the specified System.Drawing.Graphics, + /// System.Drawing.Rectangle, DataView.Rectangle, row number, background color, + /// and foreground color.. + /// + protected internal override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, + Brush backBrush, Brush foreBrush, bool alignToRight) { + string text = GetText(GetColumnValueAtRow(source, rowNum)); + PaintText(g, bounds, text, backBrush, foreBrush, alignToRight); + } + + /// + /// + /// Draws the text and + /// rectangle at the given location with the specified alignment. + /// + protected void PaintText(Graphics g, Rectangle bounds, string text, bool alignToRight) { + PaintText(g, bounds, text, this.DataGridTableStyle.BackBrush, this.DataGridTableStyle.ForeBrush, alignToRight); + } + + /// + /// + /// Draws the text and rectangle at the specified location with the + /// specified colors and alignment. + /// + protected void PaintText(Graphics g, Rectangle textBounds, string text, Brush backBrush, Brush foreBrush, bool alignToRight) { + /* + if (edit.Visible) + g.BackColor = BackColor; + */ + + Rectangle rect = textBounds; + + StringFormat format = new StringFormat(); + if (alignToRight) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + } + + format.Alignment = this.Alignment == HorizontalAlignment.Left ? StringAlignment.Near : this.Alignment == HorizontalAlignment.Center ? StringAlignment.Center : StringAlignment.Far; + + // do not wrap the text + // + format.FormatFlags |= StringFormatFlags.NoWrap; + + g.FillRectangle(backBrush, rect); + // by design, painting leaves a little padding around the rectangle. + // so do not deflate the rectangle. + rect.Offset(0,2 * yMargin); + rect.Height -= 2 * yMargin; + g.DrawString(text, this.DataGridTableStyle.DataGrid.Font, foreBrush, rect, format); + format.Dispose(); + } + + private void RollBack() { + Debug.Assert(!edit.IsInEditOrNavigateMode, "Must be editing to rollback changes..."); + edit.Text = oldValue; + } + + /// + protected internal override void ReleaseHostedControl() { + if (edit.ParentInternal != null) { + edit.ParentInternal.Controls.Remove(edit); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridToolTip.cs b/WindowsForms/Managed/System/WinForms/DataGridToolTip.cs new file mode 100644 index 000000000..8b39b8f16 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridToolTip.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Drawing; + + using System.Windows.Forms; + using Microsoft.Win32; + using System.Diagnostics; + using System.ComponentModel; + + // this class is basically a NativeWindow that does toolTipping + // should be one for the entire grid + internal class DataGridToolTip : MarshalByRefObject { + // the toolTip control + private NativeWindow tipWindow = null; + + // the dataGrid which contains this toolTip + private DataGrid dataGrid = null; + + // CONSTRUCTOR + public DataGridToolTip(DataGrid dataGrid) + { + Debug.Assert(dataGrid!= null, "can't attach a tool tip to a null grid"); + this.dataGrid = dataGrid; + } + + // will ensure that the toolTip window was created + public void CreateToolTipHandle() + { + if (tipWindow == null || tipWindow.Handle == IntPtr.Zero) + { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + icc.dwSize = Marshal.SizeOf(icc); + SafeNativeMethods.InitCommonControlsEx(icc); + CreateParams cparams = new CreateParams(); + cparams.Parent = dataGrid.Handle; + cparams.ClassName = NativeMethods.TOOLTIPS_CLASS; + cparams.Style = NativeMethods.TTS_ALWAYSTIP; + tipWindow = new NativeWindow(); + tipWindow.CreateHandle(cparams); + + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + SafeNativeMethods.SetWindowPos(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.HWND_NOTOPMOST, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOACTIVATE); + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETDELAYTIME, NativeMethods.TTDT_INITIAL, 0); + } + } + + // this function will add a toolTip to the + // windows system + public void AddToolTip(String toolTipString, IntPtr toolTipId, Rectangle iconBounds) + { + Debug.Assert(tipWindow != null && tipWindow.Handle != IntPtr.Zero, "the tipWindow was not initialized, bailing out"); + + if (toolTipString == null) + throw new ArgumentNullException("toolTipString"); + if (iconBounds.IsEmpty) + throw new ArgumentNullException("iconBounds", SR.GetString(SR.DataGridToolTipEmptyIcon)); + + NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(toolInfo); + toolInfo.hwnd = dataGrid.Handle; + toolInfo.uId = toolTipId; + toolInfo.lpszText = toolTipString; + toolInfo.rect = NativeMethods.RECT.FromXYWH(iconBounds.X, iconBounds.Y, iconBounds.Width, iconBounds.Height); + toolInfo.uFlags = NativeMethods.TTF_SUBCLASS; + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_ADDTOOL, 0, toolInfo); + } + + public void RemoveToolTip(IntPtr toolTipId) + { + NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(toolInfo); + toolInfo.hwnd = dataGrid.Handle; + toolInfo.uId = toolTipId; + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_DELTOOL, 0, toolInfo); + } + + // will destroy the tipWindow + public void Destroy() + { + Debug.Assert(tipWindow != null, "how can one destroy a null window"); + tipWindow.DestroyHandle(); + tipWindow = null; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridView.cs b/WindowsForms/Managed/System/WinForms/DataGridView.cs new file mode 100644 index 000000000..c37978113 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridView.cs @@ -0,0 +1,7087 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Drawing; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.Layout; + using System.Globalization; + using System.Diagnostics; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + using System.Collections.Specialized; + + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.DataGridViewDesigner, " + AssemblyRef.SystemDesign), + //DefaultProperty("DataSource"), + DefaultEvent("CellContentClick"), + ComplexBindingProperties("DataSource", "DataMember"), + Docking(DockingBehavior.Ask), + Editor("System.Windows.Forms.Design.DataGridViewComponentEditor, " + AssemblyRef.SystemDesign, typeof(ComponentEditor)), + SRDescription(SR.DescriptionDataGridView) + ] + public partial class DataGridView : Control, ISupportInitialize + { + private static readonly object EVENT_DATAGRIDVIEWALLOWUSERTOADDROWSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWALLOWUSERTODELETEROWSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWALLOWUSERTOORDERCOLUMNSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWALLOWUSERTORESIZECOLUMNSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWALLOWUSERTORESIZEROWSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWALTERNATINGROWSDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWAUTOGENERATECOLUMNSCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWAUTOSIZECOLUMNMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWAUTOSIZECOLUMNSMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWAUTOSIZEROWSMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWBACKGROUNDCOLORCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWBORDERSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCANCELROWEDIT = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLBEGINEDIT = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLBORDERSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLCLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLCONTENTCLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLCONTENTDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLENDEDIT = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLENTER = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLERRORTEXTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLERRORTEXTNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLFORMATTING = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLLEAVE = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEDOWN = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEENTER = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSELEAVE = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEMOVE = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEUP = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLPAINTING = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLPARSING = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLSTATECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLSTYLECONTENTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLVALIDATING = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLVALIDATED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLVALUECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLVALUENEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCELLVALUEPUSHED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNADDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNCONTEXTMENUSTRIPCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNDATAPROPERTYNAMECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNDISPLAYINDEXCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNDIVIDERWIDTHCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERCELLCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNDIVIDERDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSEDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERSBORDERSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERSDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTSIZEMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNMINIMUMWIDTHCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNNAMECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNREMOVED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNSORTMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNSTATECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNTOOLTIPTEXTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCOLUMNWIDTHCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCURRENTCELLCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWCURRENTCELLDIRTYSTATECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWDATABINDINGCOMPLETE = new object(); + private static readonly object EVENT_DATAGRIDVIEWDATAERROR = new object(); + private static readonly object EVENT_DATAGRIDVIEWDATAMEMBERCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWDATASOURCECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWDEFAULTVALUESNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWEDITINGCONTROLSHOWING = new object(); + private static readonly object EVENT_DATAGRIDVIEWEDITMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWGRIDCOLORCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWMULTISELECTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWNEWROWNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWREADONLYCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWDIRTYSTATENEEDED = new Object(); + private static readonly object EVENT_DATAGRIDVIEWROWDIVIDERHEIGHTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWENTER = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWERRORTEXTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWERRORTEXTNEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERCELLCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWDIVIDERDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERMOUSECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERMOUSEDOUBLECLICK = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERSBORDERSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERSDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERSWIDTHCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEADERSWIDTHSIZEMODECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEIGHTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEIGHTINFONEEDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWHEIGHTINFOPUSHED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWLEAVE = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWMINIMUMHEIGHTCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWPOSTPAINT = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWPREPAINT = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWSADDED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWSDEFAULTCELLSTYLECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWSREMOVED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWSTATECHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWUNSHARED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWVALIDATED = new object(); + private static readonly object EVENT_DATAGRIDVIEWROWVALIDATING = new object(); + private static readonly object EVENT_DATAGRIDVIEWSCROLL = new object(); + private static readonly object EVENT_DATAGRIDVIEWSELECTIONCHANGED = new object(); + private static readonly object EVENT_DATAGRIDVIEWSORTCOMPARE = new object(); + private static readonly object EVENT_DATAGRIDVIEWSORTED = new object(); + private static readonly object EVENT_DATAGRIDVIEWUSERADDEDROW = new object(); + private static readonly object EVENT_DATAGRIDVIEWUSERDELETEDROW = new object(); + private static readonly object EVENT_DATAGRIDVIEWUSERDELETINGROW = new object(); + + private const int DATAGRIDVIEWSTATE1_allowUserToAddRows = 0x00000001; + private const int DATAGRIDVIEWSTATE1_allowUserToDeleteRows = 0x00000002; + private const int DATAGRIDVIEWSTATE1_allowUserToOrderColumns = 0x00000004; + private const int DATAGRIDVIEWSTATE1_columnHeadersVisible = 0x00000008; + private const int DATAGRIDVIEWSTATE1_rowHeadersVisible = 0x00000010; + private const int DATAGRIDVIEWSTATE1_forwardCharMessage = 0x00000020; + private const int DATAGRIDVIEWSTATE1_leavingWithTabKey = 0x00000040; + private const int DATAGRIDVIEWSTATE1_multiSelect = 0x00000080; + private const int DATAGRIDVIEWSTATE1_ignoringEditingChanges = 0x00000200; + private const int DATAGRIDVIEWSTATE1_ambientForeColor = 0x00000400; + private const int DATAGRIDVIEWSTATE1_scrolledSinceMouseDown = 0x00000800; + private const int DATAGRIDVIEWSTATE1_editingControlHidden = 0x00001000; + private const int DATAGRIDVIEWSTATE1_standardTab = 0x00002000; + private const int DATAGRIDVIEWSTATE1_editingControlChanging = 0x00004000; + private const int DATAGRIDVIEWSTATE1_currentCellInEditMode = 0x00008000; + private const int DATAGRIDVIEWSTATE1_virtualMode = 0x00010000; + private const int DATAGRIDVIEWSTATE1_editedCellChanged = 0x00020000; + private const int DATAGRIDVIEWSTATE1_editedRowChanged = 0x00040000; + private const int DATAGRIDVIEWSTATE1_newRowEdited = 0x00080000; + private const int DATAGRIDVIEWSTATE1_readOnly = 0x00100000; + private const int DATAGRIDVIEWSTATE1_newRowCreatedByEditing = 0x00200000; + private const int DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell = 0x00400000; + private const int DATAGRIDVIEWSTATE1_autoGenerateColumns = 0x00800000; + private const int DATAGRIDVIEWSTATE1_customCursorSet = 0x01000000; + private const int DATAGRIDVIEWSTATE1_ambientFont = 0x02000000; + private const int DATAGRIDVIEWSTATE1_ambientColumnHeadersFont = 0x04000000; + private const int DATAGRIDVIEWSTATE1_ambientRowHeadersFont = 0x08000000; + private const int DATAGRIDVIEWSTATE1_isRestrictedChecked = 0x10000000; + private const int DATAGRIDVIEWSTATE1_isRestricted = 0x20000000; + private const int DATAGRIDVIEWSTATE1_isAutoSized = 0x40000000; + + // DATAGRIDVIEWSTATE2_ + private const int DATAGRIDVIEWSTATE2_showEditingIcon = 0x00000001; + private const int DATAGRIDVIEWSTATE2_allowUserToResizeColumns = 0x00000002; + private const int DATAGRIDVIEWSTATE2_allowUserToResizeRows = 0x00000004; + private const int DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl = 0x00000008; + private const int DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel = 0x00000010; + private const int DATAGRIDVIEWSTATE2_mouseEnterExpected = 0x00000020; + private const int DATAGRIDVIEWSTATE2_enableHeadersVisualStyles = 0x00000040; + private const int DATAGRIDVIEWSTATE2_showCellErrors = 0x00000080; + private const int DATAGRIDVIEWSTATE2_showCellToolTips = 0x00000100; + private const int DATAGRIDVIEWSTATE2_showRowErrors = 0x00000200; + private const int DATAGRIDVIEWSTATE2_showColumnRelocationInsertion = 0x00000400; + private const int DATAGRIDVIEWSTATE2_rightToLeftMode = 0x00000800; + private const int DATAGRIDVIEWSTATE2_rightToLeftValid = 0x00001000; + private const int DATAGRIDVIEWSTATE2_currentCellWantsInputKey = 0x00002000; + private const int DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll = 0x00004000; + private const int DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll = 0x00008000; + private const int DATAGRIDVIEWSTATE2_replacedCellSelected = 0x00010000; + private const int DATAGRIDVIEWSTATE2_replacedCellReadOnly = 0x00020000; + private const int DATAGRIDVIEWSTATE2_raiseSelectionChanged = 0x00040000; + private const int DATAGRIDVIEWSTATE2_initializing = 0x00080000; + private const int DATAGRIDVIEWSTATE2_autoSizedWithoutHandle = 0x00100000; + private const int DATAGRIDVIEWSTATE2_ignoreCursorChange = 0x00200000; + private const int DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell= 0x00400000; + private const int DATAGRIDVIEWSTATE2_nextMouseUpIsDouble = 0x00800000; + private const int DATAGRIDVIEWSTATE2_inBindingContextChanged = 0x01000000; + private const int DATAGRIDVIEWSTATE2_allowHorizontalScrollbar = 0x02000000; + private const int DATAGRIDVIEWSTATE2_usedFillWeightsDirty = 0x04000000; + private const int DATAGRIDVIEWSTATE2_messageFromEditingCtrls = 0x08000000; + private const int DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds = 0x10000000; + private const int DATAGRIDVIEWSTATE2_discardEditingControl = 0x20000000; + + // DATAGRIDVIEWOPER_ + private const int DATAGRIDVIEWOPER_trackColResize = 0x00000001; + private const int DATAGRIDVIEWOPER_trackRowResize = 0x00000002; + private const int DATAGRIDVIEWOPER_trackColSelect = 0x00000004; + private const int DATAGRIDVIEWOPER_trackRowSelect = 0x00000008; + private const int DATAGRIDVIEWOPER_trackCellSelect = 0x00000010; + private const int DATAGRIDVIEWOPER_trackColRelocation = 0x00000020; + private const int DATAGRIDVIEWOPER_inSort = 0x00000040; + private const int DATAGRIDVIEWOPER_trackColHeadersResize = 0x00000080; + private const int DATAGRIDVIEWOPER_trackRowHeadersResize = 0x00000100; + private const int DATAGRIDVIEWOPER_trackMouseMoves = 0x00000200; + private const int DATAGRIDVIEWOPER_inRefreshColumns = 0x00000400; + private const int DATAGRIDVIEWOPER_inDisplayIndexAdjustments = 0x00000800; + private const int DATAGRIDVIEWOPER_lastEditCtrlClickDoubled = 0x00001000; + private const int DATAGRIDVIEWOPER_inMouseDown = 0x00002000; + private const int DATAGRIDVIEWOPER_inReadOnlyChange = 0x00004000; + private const int DATAGRIDVIEWOPER_inCellValidating = 0x00008000; + private const int DATAGRIDVIEWOPER_inBorderStyleChange = 0x00010000; + private const int DATAGRIDVIEWOPER_inCurrentCellChange = 0x00020000; + private const int DATAGRIDVIEWOPER_inAdjustFillingColumns = 0x00040000; + private const int DATAGRIDVIEWOPER_inAdjustFillingColumn = 0x00080000; + private const int DATAGRIDVIEWOPER_inDispose = 0x00100000; + private const int DATAGRIDVIEWOPER_inBeginEdit = 0x00200000; + private const int DATAGRIDVIEWOPER_inEndEdit = 0x00400000; + private const int DATAGRIDVIEWOPER_resizingOperationAboutToStart = 0x00800000; + private const int DATAGRIDVIEWOPER_trackKeyboardColResize = 0x01000000; + private const int DATAGRIDVIEWOPER_mouseOperationMask = DATAGRIDVIEWOPER_trackColResize | DATAGRIDVIEWOPER_trackRowResize | + DATAGRIDVIEWOPER_trackColRelocation | DATAGRIDVIEWOPER_trackColHeadersResize | DATAGRIDVIEWOPER_trackRowHeadersResize; + private const int DATAGRIDVIEWOPER_keyboardOperationMask = DATAGRIDVIEWOPER_trackKeyboardColResize; + + private static Size DragSize = SystemInformation.DragSize; + + private const byte DATAGRIDVIEW_columnSizingHotZone = 6; + private const byte DATAGRIDVIEW_rowSizingHotZone = 5; + private const byte DATAGRIDVIEW_insertionBarWidth = 3; + private const byte DATAGRIDVIEW_bulkPaintThreshold = 8; + + private const string DATAGRIDVIEW_htmlPrefix = "Version:1.0\r\nStartHTML:00000097\r\nEndHTML:{0}\r\nStartFragment:00000133\r\nEndFragment:{1}\r\n"; + private const string DATAGRIDVIEW_htmlStartFragment = "\r\n\r\n"; + private const string DATAGRIDVIEW_htmlEndFragment = "\r\n\r\n\r\n"; + + private const int FOCUS_RECT_OFFSET = 2; + + private System.Collections.Specialized.BitVector32 dataGridViewState1; // see DATAGRIDVIEWSTATE1_ consts above + private System.Collections.Specialized.BitVector32 dataGridViewState2; // see DATAGRIDVIEWSTATE2_ consts above + private System.Collections.Specialized.BitVector32 dataGridViewOper; // see DATAGRIDVIEWOPER_ consts above + + private const BorderStyle defaultBorderStyle = BorderStyle.FixedSingle; + private const DataGridViewAdvancedCellBorderStyle defaultAdvancedCellBorderStyle = DataGridViewAdvancedCellBorderStyle.Single; + private const DataGridViewAdvancedCellBorderStyle defaultAdvancedRowHeadersBorderStyle = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + private const DataGridViewAdvancedCellBorderStyle defaultAdvancedColumnHeadersBorderStyle = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + + private const DataGridViewSelectionMode defaultSelectionMode = DataGridViewSelectionMode.RowHeaderSelect; + private const DataGridViewEditMode defaultEditMode = DataGridViewEditMode.EditOnKeystrokeOrF2; + + private const DataGridViewAutoSizeRowCriteriaInternal invalidDataGridViewAutoSizeRowCriteriaInternalMask = ~(DataGridViewAutoSizeRowCriteriaInternal.Header | DataGridViewAutoSizeRowCriteriaInternal.AllColumns); + + private SolidBrush backgroundBrush = DefaultBackgroundBrush; + private Pen gridPen; + private Cursor oldCursor; + + private HScrollBar horizScrollBar = new HScrollBar(); + private VScrollBar vertScrollBar = new VScrollBar(); + private DataGridViewHeaderCell topLeftHeaderCell; + + private DataGridViewRow rowTemplate; + private DataGridViewRowCollection dataGridViewRows; + private DataGridViewColumnCollection dataGridViewColumns; + + private DataGridViewCellStyle placeholderCellStyle; + private StringFormat placeholderStringFormat; + + private DataGridViewColumn sortedColumn; + private SortOrder sortOrder; + + private object uneditedFormattedValue; + private Control editingControl, latestEditingControl, cachedEditingControl; + private Panel editingPanel; + private DataGridViewEditingPanelAccessibleObject editingPanelAccessibleObject; + private Point ptCurrentCell, ptCurrentCellCache = Point.Empty, ptAnchorCell, ptMouseDownCell, ptMouseEnteredCell, ptToolTipCell, ptMouseDownGridCoord; + + private DataGridViewSelectionMode selectionMode; + private DataGridViewEditMode editMode; + + // Note that a cell can only be in one bag but not both at the same time. + private DataGridViewCellLinkedList individualSelectedCells; + private DataGridViewCellLinkedList individualReadOnlyCells; + private DataGridViewIntLinkedList selectedBandIndexes; + private DataGridViewIntLinkedList selectedBandSnapshotIndexes; + + private DataGridViewCellStyle defaultCellStyle, columnHeadersDefaultCellStyle, rowHeadersDefaultCellStyle; + private DataGridViewCellStyle rowsDefaultCellStyle, alternatingRowsDefaultCellStyle; + private ScrollBars scrollBars; + private LayoutData layout; + private DisplayedBandsData displayedBandsInfo; + private Rectangle normalClientRectangle; + private ArrayList lstRows; + private int availableWidthForFillColumns; + + private BorderStyle borderStyle; + private DataGridViewAdvancedBorderStyle advancedCellBorderStyle; + private DataGridViewAdvancedBorderStyle advancedRowHeadersBorderStyle; + private DataGridViewAdvancedBorderStyle advancedColumnHeadersBorderStyle; + + private DataGridViewClipboardCopyMode clipboardCopyMode; + + private const int minimumRowHeadersWidth = 4; + private const int minimumColumnHeadersHeight = 4; + private const int defaultRowHeadersWidth = 41; + private const int maxHeadersThickness = 32768; + private const int upperSize = 0x007FFFFF; + private int rowHeadersWidth = defaultRowHeadersWidth; + private int cachedRowHeadersWidth; + private const int defaultColumnHeadersHeight = 23; + private int columnHeadersHeight = defaultColumnHeadersHeight; + private int cachedColumnHeadersHeight; + private DataGridViewAutoSizeRowsMode autoSizeRowsMode; + private DataGridViewAutoSizeColumnsMode autoSizeColumnsMode; + private DataGridViewColumnHeadersHeightSizeMode columnHeadersHeightSizeMode; + private DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode; + + private DataGridViewCellStyleChangedEventArgs dgvcsce; + private DataGridViewCellPaintingEventArgs dgvcpe; + private DataGridViewCellValueEventArgs dgvcve; + private DataGridViewRowHeightInfoNeededEventArgs dgvrhine; + private DataGridViewRowPostPaintEventArgs dgvrpope; + private DataGridViewRowPrePaintEventArgs dgvrprpe; + + // the sum of the widths in pixels of the scrolling columns preceding + // the first visible scrolling column + private int horizontalOffset; + + // the sum of the heights in pixels of the scrolling rows preceding + // the first visible scrolling row + private int verticalOffset; + + // the number of pixels of the firstDisplayedScrollingCol which are not visible + private int negOffset; + + // the index of the potential 'new' row. -1 if there is no 'new' row. + private int newRowIndex = -1; + + // residual fraction of WHEEL_DELTA (120) for wheel scrolling + private int cumulativeVerticalWheelDelta; + private int cumulativeHorizontalWheelDelta; + + private int trackColAnchor; + private int trackColumn = -1; + private int trackColumnEdge = -1; + private int trackRowAnchor; + private int trackRow = -1; + private int trackRowEdge = -1; + private int lastHeaderShadow = -1; + private int currentColSplitBar = -1, lastColSplitBar = -1; + private int currentRowSplitBar = -1, lastRowSplitBar = -1; + private int mouseBarOffset; + private int noDimensionChangeCount; + private int noSelectionChangeCount; + private int noAutoSizeCount; + private int inBulkPaintCount; + private int inBulkLayoutCount; + private int inPerformLayoutCount; + + private int keyboardResizeStep; + private Rectangle resizeClipRectangle; + + private System.Windows.Forms.Timer vertScrollTimer, horizScrollTimer; + + private Hashtable converters; + private Hashtable pens; + private Hashtable brushes; + + private NativeMethods.RECT[] cachedScrollableRegion; + + // DataBinding + private DataGridViewDataConnection dataConnection; + + // ToolTip + private DataGridViewToolTip toolTipControl; + // the tool tip string we get from cells + private string toolTipCaption = String.Empty; + + private const int maxTTDISPINFOBufferLength = 80; + + // Last Mouse Click Info + private MouseClickInfo lastMouseClickInfo; + +#if DEBUG + // set to false when the grid is not in sync with the underlying data store + // in virtual mode, and OnCellValueNeeded cannot be called. + // disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal bool dataStoreAccessAllowed = true; +#pragma warning restore 0414 +#endif + /// + /// + /// Initializes a new instance of the class. + /// + public DataGridView() + { + SetStyle(ControlStyles.UserPaint | + ControlStyles.Opaque | + ControlStyles.UserMouse, true); + + SetStyle(ControlStyles.SupportsTransparentBackColor, false); + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + this.dataGridViewState1 = new System.Collections.Specialized.BitVector32(0x00000000); + this.dataGridViewState2 = new System.Collections.Specialized.BitVector32(0x00000000); + this.dataGridViewOper = new System.Collections.Specialized.BitVector32(0x00000000); + + this.dataGridViewState1[ DATAGRIDVIEWSTATE1_columnHeadersVisible + | DATAGRIDVIEWSTATE1_rowHeadersVisible + | DATAGRIDVIEWSTATE1_autoGenerateColumns + | DATAGRIDVIEWSTATE1_allowUserToAddRows + | DATAGRIDVIEWSTATE1_allowUserToDeleteRows ] = true; + + + + this.dataGridViewState2[ DATAGRIDVIEWSTATE2_showEditingIcon + | DATAGRIDVIEWSTATE2_enableHeadersVisualStyles + | DATAGRIDVIEWSTATE2_mouseEnterExpected + | DATAGRIDVIEWSTATE2_allowUserToResizeColumns + | DATAGRIDVIEWSTATE2_allowUserToResizeRows + | DATAGRIDVIEWSTATE2_showCellToolTips + | DATAGRIDVIEWSTATE2_showCellErrors + | DATAGRIDVIEWSTATE2_showRowErrors + | DATAGRIDVIEWSTATE2_allowHorizontalScrollbar + | DATAGRIDVIEWSTATE2_usedFillWeightsDirty ] = true; + + + this.displayedBandsInfo = new DisplayedBandsData(); + this.lstRows = new ArrayList(); + + this.converters = new Hashtable(8); + this.pens = new Hashtable(8); + this.brushes = new Hashtable(10); + this.gridPen = new Pen(DefaultGridColor); + + this.selectedBandIndexes = new DataGridViewIntLinkedList(); + this.individualSelectedCells = new DataGridViewCellLinkedList(); + this.individualReadOnlyCells = new DataGridViewCellLinkedList(); + + this.advancedCellBorderStyle = new DataGridViewAdvancedBorderStyle(this, + DataGridViewAdvancedCellBorderStyle.OutsetDouble, + DataGridViewAdvancedCellBorderStyle.OutsetPartial, + DataGridViewAdvancedCellBorderStyle.InsetDouble); + this.advancedRowHeadersBorderStyle = new DataGridViewAdvancedBorderStyle(this); + this.advancedColumnHeadersBorderStyle = new DataGridViewAdvancedBorderStyle(this); + this.advancedCellBorderStyle.All = defaultAdvancedCellBorderStyle; + this.advancedRowHeadersBorderStyle.All = defaultAdvancedRowHeadersBorderStyle; + this.advancedColumnHeadersBorderStyle.All = defaultAdvancedColumnHeadersBorderStyle; + this.borderStyle = defaultBorderStyle; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_multiSelect] = true; + this.selectionMode = defaultSelectionMode; + this.editMode = defaultEditMode; + this.autoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; + this.autoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; + this.columnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing; + this.rowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing; + + this.clipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithAutoHeaderText; + + this.layout = new LayoutData(); + this.layout.TopLeftHeader = Rectangle.Empty; + this.layout.ColumnHeaders = Rectangle.Empty; + this.layout.RowHeaders = Rectangle.Empty; + this.layout.ColumnHeadersVisible = true; + this.layout.RowHeadersVisible = true; + this.layout.ClientRectangle = this.ClientRectangle; + + this.scrollBars = ScrollBars.Both; + + this.horizScrollBar.RightToLeft = RightToLeft.Inherit; + this.horizScrollBar.AccessibleName = SR.GetString(SR.DataGridView_AccHorizontalScrollBarAccName); + this.horizScrollBar.Top = this.ClientRectangle.Height - horizScrollBar.Height; + this.horizScrollBar.Left = 0; + this.horizScrollBar.Visible = false; + this.horizScrollBar.Scroll += new ScrollEventHandler(DataGridViewHScrolled); + this.Controls.Add(this.horizScrollBar); + + this.vertScrollBar.Top = 0; + this.vertScrollBar.AccessibleName = SR.GetString(SR.DataGridView_AccVerticalScrollBarAccName); + this.vertScrollBar.Left = this.ClientRectangle.Width - vertScrollBar.Width; + this.vertScrollBar.Visible = false; + this.vertScrollBar.Scroll += new ScrollEventHandler(DataGridViewVScrolled); + this.Controls.Add(this.vertScrollBar); + + this.ptCurrentCell = new Point(-1, -1); + this.ptAnchorCell = new Point(-1, -1); + this.ptMouseDownCell = new Point(-2, -2); + this.ptMouseEnteredCell = new Point(-2, -2); + this.ptToolTipCell = new Point(-1, -1); + this.ptMouseDownGridCoord = new Point(-1, -1); + + this.sortOrder = SortOrder.None; + + this.lastMouseClickInfo.timeStamp = 0; + + WireScrollBarsEvents(); + PerformLayout(); + + this.toolTipControl = new DataGridViewToolTip(this); + this.rowHeadersWidth = ScaleToCurrentDpi(defaultRowHeadersWidth); + this.columnHeadersHeight = ScaleToCurrentDpi(defaultColumnHeadersHeight); + Invalidate(); + } + + /// + /// Scaling row header width and column header height. + /// + private int ScaleToCurrentDpi(int value) + { + return DpiHelper.EnableDataGridViewControlHighDpiImprovements ? LogicalToDeviceUnits(value) : value; + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual DataGridViewAdvancedBorderStyle AdjustedTopLeftHeaderBorderStyle + { + get + { + DataGridViewAdvancedBorderStyle dgvabs; + if (this.ApplyVisualStylesToHeaderCells) + { + switch (this.AdvancedColumnHeadersBorderStyle.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + dgvabs = new DataGridViewAdvancedBorderStyle(); + if (this.RightToLeftInternal) + { + dgvabs.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dgvabs.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + dgvabs.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dgvabs.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dgvabs.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + dgvabs = new DataGridViewAdvancedBorderStyle(); + if (this.RightToLeftInternal) + { + dgvabs.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + else + { + dgvabs.LeftInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + dgvabs.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dgvabs.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dgvabs.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + break; + + case DataGridViewAdvancedCellBorderStyle.NotSet: + // Since the row headers are visible, we should make sure + // that there is a left/right border for the TopLeftHeaderCell no matter what. + if ((!this.RightToLeftInternal && this.AdvancedColumnHeadersBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.None) || + (this.RightToLeftInternal && this.AdvancedColumnHeadersBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None)) + { + dgvabs = new DataGridViewAdvancedBorderStyle(); + if (this.RightToLeftInternal) + { + dgvabs.LeftInternal = this.AdvancedColumnHeadersBorderStyle.Left; + dgvabs.RightInternal = this.AdvancedRowHeadersBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble ? + DataGridViewAdvancedCellBorderStyle.Outset : this.AdvancedRowHeadersBorderStyle.Right; + } + else + { + dgvabs.LeftInternal = this.AdvancedRowHeadersBorderStyle.Left; + dgvabs.RightInternal = this.AdvancedColumnHeadersBorderStyle.Right; + } + dgvabs.TopInternal = this.AdvancedColumnHeadersBorderStyle.Top; + dgvabs.BottomInternal = this.AdvancedColumnHeadersBorderStyle.Bottom; + } + else + { + dgvabs = this.AdvancedColumnHeadersBorderStyle; + } + break; + + default: + dgvabs = this.AdvancedColumnHeadersBorderStyle; + break; + } + } + else + { + switch (this.AdvancedColumnHeadersBorderStyle.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + dgvabs = new DataGridViewAdvancedBorderStyle(); + dgvabs.LeftInternal = this.RightToLeftInternal ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dgvabs.RightInternal = this.RightToLeftInternal ? DataGridViewAdvancedCellBorderStyle.OutsetDouble : DataGridViewAdvancedCellBorderStyle.Outset; + dgvabs.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dgvabs.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + dgvabs = new DataGridViewAdvancedBorderStyle(); + dgvabs.LeftInternal = this.RightToLeftInternal ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + dgvabs.RightInternal = this.RightToLeftInternal ? DataGridViewAdvancedCellBorderStyle.InsetDouble : DataGridViewAdvancedCellBorderStyle.Inset; + dgvabs.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dgvabs.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + break; + + case DataGridViewAdvancedCellBorderStyle.NotSet: + // Since the row headers are visible, we should make sure + // that there is a left/right border for the TopLeftHeaderCell no matter what. + if ((!this.RightToLeftInternal && this.AdvancedColumnHeadersBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.None) || + (this.RightToLeftInternal && this.AdvancedColumnHeadersBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None)) + { + dgvabs = new DataGridViewAdvancedBorderStyle(); + if (this.RightToLeftInternal) + { + dgvabs.LeftInternal = this.AdvancedColumnHeadersBorderStyle.Left; + dgvabs.RightInternal = this.AdvancedRowHeadersBorderStyle.Right; + } + else + { + dgvabs.LeftInternal = this.AdvancedRowHeadersBorderStyle.Left; + dgvabs.RightInternal = this.AdvancedColumnHeadersBorderStyle.Right; + } + dgvabs.TopInternal = this.AdvancedColumnHeadersBorderStyle.Top; + dgvabs.BottomInternal = this.AdvancedColumnHeadersBorderStyle.Bottom; + } + else + { + dgvabs = this.AdvancedColumnHeadersBorderStyle; + } + break; + + default: + dgvabs = this.AdvancedColumnHeadersBorderStyle; + break; + } + } + return dgvabs; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public DataGridViewAdvancedBorderStyle AdvancedCellBorderStyle + { + get + { + return this.advancedCellBorderStyle; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public DataGridViewAdvancedBorderStyle AdvancedColumnHeadersBorderStyle + { + get + { + return this.advancedColumnHeadersBorderStyle; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public DataGridViewAdvancedBorderStyle AdvancedRowHeadersBorderStyle + { + get + { + return this.advancedRowHeadersBorderStyle; + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_AllowUserToAddRowsDescr) + ] + public bool AllowUserToAddRows + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToAddRows]; + } + set + { + if (this.AllowUserToAddRows != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToAddRows] = value; + if (this.DataSource != null) + { + this.dataConnection.ResetCachedAllowUserToAddRowsInternal(); + } + OnAllowUserToAddRowsChanged(EventArgs.Empty); + } + } + } + + internal bool AllowUserToAddRowsInternal + { + get + { + if (this.DataSource == null) + { + return this.AllowUserToAddRows; + } + else + { + return this.AllowUserToAddRows && this.dataConnection.AllowAdd; + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnAllowUserToAddRowsChangedDescr) + ] + public event EventHandler AllowUserToAddRowsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALLOWUSERTOADDROWSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALLOWUSERTOADDROWSCHANGED, value); + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_AllowUserToDeleteRowsDescr) + ] + public bool AllowUserToDeleteRows + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToDeleteRows]; + } + set + { + if (this.AllowUserToDeleteRows != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToDeleteRows] = value; + OnAllowUserToDeleteRowsChanged(EventArgs.Empty); + } + } + } + + internal bool AllowUserToDeleteRowsInternal + { + get + { + if (this.DataSource == null) + { + return this.AllowUserToDeleteRows; + } + else + { + return this.AllowUserToDeleteRows && this.dataConnection.AllowRemove; + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnAllowUserToDeleteRowsChangedDescr) + ] + public event EventHandler AllowUserToDeleteRowsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALLOWUSERTODELETEROWSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALLOWUSERTODELETEROWSCHANGED, value); + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_AllowUserToOrderColumnsDescr) + ] + public bool AllowUserToOrderColumns + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToOrderColumns]; + } + set + { + if (this.AllowUserToOrderColumns != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_allowUserToOrderColumns] = value; + OnAllowUserToOrderColumnsChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnAllowUserToOrderColumnsChangedDescr) + ] + public event EventHandler AllowUserToOrderColumnsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALLOWUSERTOORDERCOLUMNSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALLOWUSERTOORDERCOLUMNSCHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets a global value indicating if the dataGridView's columns are resizable with the mouse. + /// The resizable aspect of a column can be overridden by DataGridViewColumn.Resizable. + /// + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_AllowUserToResizeColumnsDescr) + ] + public bool AllowUserToResizeColumns + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowUserToResizeColumns]; + } + set + { + if (this.AllowUserToResizeColumns != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowUserToResizeColumns] = value; + OnAllowUserToResizeColumnsChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnAllowUserToResizeColumnsChangedDescr) + ] + public event EventHandler AllowUserToResizeColumnsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALLOWUSERTORESIZECOLUMNSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALLOWUSERTORESIZECOLUMNSCHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets a global value indicating if the dataGridView's rows are resizable with the mouse. + /// The resizable aspect of a row can be overridden by DataGridViewRow.Resizable. + /// + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_AllowUserToResizeRowsDescr) + ] + public bool AllowUserToResizeRows + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowUserToResizeRows]; + } + set + { + if (this.AllowUserToResizeRows != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowUserToResizeRows] = value; + OnAllowUserToResizeRowsChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnAllowUserToResizeRowsChangedDescr) + ] + public event EventHandler AllowUserToResizeRowsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALLOWUSERTORESIZEROWSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALLOWUSERTORESIZEROWSCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_AlternatingRowsDefaultCellStyleDescr) + ] + public DataGridViewCellStyle AlternatingRowsDefaultCellStyle + { + get + { + if (this.alternatingRowsDefaultCellStyle == null) + { + this.alternatingRowsDefaultCellStyle = new DataGridViewCellStyle(); + this.alternatingRowsDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.AlternatingRows); + } + return this.alternatingRowsDefaultCellStyle; + } + set + { + DataGridViewCellStyle cs = this.AlternatingRowsDefaultCellStyle; + cs.RemoveScope(DataGridViewCellStyleScopes.AlternatingRows); + this.alternatingRowsDefaultCellStyle = value; + if (value != null) + { + this.alternatingRowsDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.AlternatingRows); + } + DataGridViewCellStyleDifferences dgvcsc = cs.GetDifferencesFrom(this.AlternatingRowsDefaultCellStyle); + if (dgvcsc != DataGridViewCellStyleDifferences.None) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = (dgvcsc == DataGridViewCellStyleDifferences.AffectPreferredSize); + OnAlternatingRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewAlternatingRowsDefaultCellStyleChangedDescr) + ] + public event EventHandler AlternatingRowsDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWALTERNATINGROWSDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWALTERNATINGROWSDEFAULTCELLSTYLECHANGED, value); + } + } + + internal bool ApplyVisualStylesToInnerCells + { + get + { + return Application.RenderWithVisualStyles; + } + } + + internal bool ApplyVisualStylesToHeaderCells + { + get + { + return Application.RenderWithVisualStyles && this.EnableHeadersVisualStyles; + } + } + + /// + /// + /// + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(true) + ] + public bool AutoGenerateColumns + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_autoGenerateColumns]; + } + set{ + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_autoGenerateColumns] != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_autoGenerateColumns] = value; + OnAutoGenerateColumnsChanged(EventArgs.Empty); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler AutoGenerateColumnsChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWAUTOGENERATECOLUMNSCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWAUTOGENERATECOLUMNSCHANGED, value); + } + } + + /// + /// + /// Overriding base implementation for perf gains. + /// + public override bool AutoSize + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_isAutoSized]; + } + set + { + base.AutoSize = value; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isAutoSized] = value; + } + } + + /// + /// + /// Gets or sets the columns' autosizing mode. Standard inheritance model is used: + /// Columns with AutoSizeMode property set to NotSet will use this auto size mode. + /// + /// + [ + DefaultValue(DataGridViewAutoSizeColumnsMode.None), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_AutoSizeColumnsModeDescr) + ] + public DataGridViewAutoSizeColumnsMode AutoSizeColumnsMode + { + get + { + return this.autoSizeColumnsMode; + } + + set + { + switch (value) + { + case DataGridViewAutoSizeColumnsMode.None: + case DataGridViewAutoSizeColumnsMode.ColumnHeader: + case DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader: + case DataGridViewAutoSizeColumnsMode.AllCells: + case DataGridViewAutoSizeColumnsMode.DisplayedCellsExceptHeader: + case DataGridViewAutoSizeColumnsMode.DisplayedCells: + case DataGridViewAutoSizeColumnsMode.Fill: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAutoSizeColumnsMode)); + } + + + if (this.autoSizeColumnsMode != value) + { + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && dataGridViewColumn.Visible) + { + // Make sure there is no visible column which would have an inherited autosize mode based on the header only. + if (value == DataGridViewAutoSizeColumnsMode.ColumnHeader && !this.ColumnHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeColumnsInvisibleColumnHeaders)); + } + // Make sure there is no visible frozen column which would have a Fill inherited autosize mode. + if (value == DataGridViewAutoSizeColumnsMode.Fill && dataGridViewColumn.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoFillFrozenColumns)); + } + } + } + DataGridViewAutoSizeColumnMode[] previousModes = new DataGridViewAutoSizeColumnMode[this.Columns.Count]; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + /*DataGridViewAutoSizeColumnMode previousInheritedMode = dataGridViewColumn.InheritedAutoSizeMode; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet;*/ + previousModes[dataGridViewColumn.Index] = dataGridViewColumn.InheritedAutoSizeMode; + } + DataGridViewAutoSizeColumnsModeEventArgs dgvcasme = new DataGridViewAutoSizeColumnsModeEventArgs(previousModes); + this.autoSizeColumnsMode = value; + OnAutoSizeColumnsModeChanged(dgvcasme); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewAutoSizeColumnsModeChangedDescr) + ] + public event DataGridViewAutoSizeColumnsModeEventHandler AutoSizeColumnsModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWAUTOSIZECOLUMNSMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWAUTOSIZECOLUMNSMODECHANGED, value); + } + } + + /// + /// + /// Gets or sets the rows' autosizing mode. + /// + [ + DefaultValue(DataGridViewAutoSizeRowsMode.None), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_AutoSizeRowsModeDescr) + ] + public DataGridViewAutoSizeRowsMode AutoSizeRowsMode + { + get + { + return this.autoSizeRowsMode; + } + set + { + switch (value) + { + case DataGridViewAutoSizeRowsMode.None: + case DataGridViewAutoSizeRowsMode.AllHeaders: + case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.AllCells: + case DataGridViewAutoSizeRowsMode.DisplayedHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCells: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAutoSizeRowsMode)); + } + if ((value == DataGridViewAutoSizeRowsMode.AllHeaders || value == DataGridViewAutoSizeRowsMode.DisplayedHeaders) && + !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader)); + } + if (this.autoSizeRowsMode != value) + { + DataGridViewAutoSizeModeEventArgs dgvasme = new DataGridViewAutoSizeModeEventArgs(this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None); + this.autoSizeRowsMode = value; + OnAutoSizeRowsModeChanged(dgvasme); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewAutoSizeRowsModeChangedDescr) + ] + public event DataGridViewAutoSizeModeEventHandler AutoSizeRowsModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWAUTOSIZEROWSMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWAUTOSIZEROWSMODECHANGED, value); + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Color BackColor + { + get + { + return base.BackColor; + } + set + { + base.BackColor = value; + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler BackColorChanged + { + add + { + base.BackColorChanged += value; + } + remove + { + base.BackColorChanged -= value; + } + } + + internal SolidBrush BackgroundBrush + { + get + { + return this.backgroundBrush; + } + } + + /// + /// + /// Gets or sets the background color of the dataGridView. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridViewBackgroundColorDescr) + ] + public Color BackgroundColor + { + get + { + return this.backgroundBrush.Color; + } + set + { + if (value.IsEmpty) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_EmptyColor, "BackgroundColor")); + } + if (value.A < 255) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_TransparentColor, "BackgroundColor")); + } + if (!value.Equals(this.backgroundBrush.Color)) + { + this.backgroundBrush = new SolidBrush(value); + OnBackgroundColorChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewBackgroundColorChangedDescr) + ] + public event EventHandler BackgroundColorChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWBACKGROUNDCOLORCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWBACKGROUNDCOLORCHANGED, value); + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public override Image BackgroundImage + { + get + { + return base.BackgroundImage; + } + set + { + base.BackgroundImage = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public override ImageLayout BackgroundImageLayout + { + get + { + return base.BackgroundImageLayout; + } + set + { + base.BackgroundImageLayout = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler BackgroundImageChanged + { + add + { + base.BackgroundImageChanged += value; + } + remove + { + base.BackgroundImageChanged -= value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler BackgroundImageLayoutChanged + { + add + { + base.BackgroundImageLayoutChanged += value; + } + remove + { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + private bool ShouldSerializeBackgroundColor() + { + return !this.BackgroundColor.Equals(DefaultBackgroundBrush.Color); + } + + /// + [ + DefaultValue(BorderStyle.FixedSingle), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_BorderStyleDescr) + ] + public BorderStyle BorderStyle + { + get + { + return this.borderStyle; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + if (this.borderStyle != value) + { + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.BorderStyle)) + { + this.borderStyle = value; + if (!this.AutoSize) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + Invalidate(); + OnBorderStyleChanged(EventArgs.Empty); + } + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewBorderStyleChangedDescr) + ] + public event EventHandler BorderStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWBORDERSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWBORDERSTYLECHANGED, value); + } + } + + private int BorderWidth + { + get + { + if (this.BorderStyle == BorderStyle.Fixed3D) + { + return Application.RenderWithVisualStyles ? 1 : SystemInformation.Border3DSize.Width; + } + else if (this.BorderStyle == BorderStyle.FixedSingle) + { + return 1; + } + else + { + return 0; + } + } + } + + // Ime can be shown when there is a read-write current cell. + protected override bool CanEnableIme + { + get + { + bool canEnable = false; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CanEnableIme(), this = " + this ); + Debug.Indent(); + + if (this.ptCurrentCell.X != -1 /*&& !this.IsCurrentCellInEditMode*/ && ColumnEditable(this.ptCurrentCell.X)) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (!IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y)) + { + canEnable = base.CanEnableIme; + } + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + canEnable ); + Debug.Unindent(); + + return canEnable; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_CellBorderStyleDescr), + Browsable(true), + DefaultValue(DataGridViewCellBorderStyle.Single) + ] + public DataGridViewCellBorderStyle CellBorderStyle + { + get + { + switch (this.advancedCellBorderStyle.All) + { + case DataGridViewAdvancedCellBorderStyle.NotSet: + if (this.advancedCellBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None && + this.advancedCellBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.None) + { + if (this.RightToLeftInternal) + { + if (this.advancedCellBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None && + this.advancedCellBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.Single) + { + return DataGridViewCellBorderStyle.SingleVertical; + } + } + else + { + if (this.advancedCellBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.None && + this.advancedCellBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Single) + { + return DataGridViewCellBorderStyle.SingleVertical; + } + } + if (this.advancedCellBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Outset && + this.advancedCellBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.Outset) + { + return DataGridViewCellBorderStyle.RaisedVertical; + } + if (this.advancedCellBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Inset && + this.advancedCellBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.Inset) + { + return DataGridViewCellBorderStyle.SunkenVertical; + } + } + if (this.advancedCellBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.None && + this.advancedCellBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) + { + if (this.advancedCellBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None && + this.advancedCellBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.Single) + { + return DataGridViewCellBorderStyle.SingleHorizontal; + } + if (this.advancedCellBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.Outset && + this.advancedCellBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.Outset) + { + return DataGridViewCellBorderStyle.RaisedHorizontal; + } + if (this.advancedCellBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.Inset && + this.advancedCellBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.Inset) + { + return DataGridViewCellBorderStyle.SunkenHorizontal; + } + } + return DataGridViewCellBorderStyle.Custom; + + case DataGridViewAdvancedCellBorderStyle.None: + return DataGridViewCellBorderStyle.None; + + case DataGridViewAdvancedCellBorderStyle.Single: + return DataGridViewCellBorderStyle.Single; + + case DataGridViewAdvancedCellBorderStyle.Inset: + return DataGridViewCellBorderStyle.Sunken; + + case DataGridViewAdvancedCellBorderStyle.Outset: + return DataGridViewCellBorderStyle.Raised; + + default: + Debug.Fail("Unexpected this.advancedCellBorderStyle.All value in CellBorderStyle.get"); + return DataGridViewCellBorderStyle.Custom; + } + } + set + { + // Sequential enum. Valid values are 0x0 to 0xa + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewCellBorderStyle.Custom, (int)DataGridViewCellBorderStyle.SunkenHorizontal)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewCellBorderStyle)); + } + + if (value != this.CellBorderStyle) + { + if (value == DataGridViewCellBorderStyle.Custom) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CustomCellBorderStyleInvalid, "CellBorderStyle")); + } + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = true; + try + { + switch (value) + { + case DataGridViewCellBorderStyle.Single: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.Single; + break; + + case DataGridViewCellBorderStyle.Raised: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.Outset; + break; + + case DataGridViewCellBorderStyle.Sunken: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.Inset; + break; + + case DataGridViewCellBorderStyle.None: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + break; + + case DataGridViewCellBorderStyle.SingleVertical: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + if (this.RightToLeftInternal) + { + this.advancedCellBorderStyle.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + else + { + this.advancedCellBorderStyle.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + break; + + case DataGridViewCellBorderStyle.RaisedVertical: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + this.advancedCellBorderStyle.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + this.advancedCellBorderStyle.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + break; + + case DataGridViewCellBorderStyle.SunkenVertical: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + this.advancedCellBorderStyle.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + this.advancedCellBorderStyle.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + break; + + case DataGridViewCellBorderStyle.SingleHorizontal: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + this.advancedCellBorderStyle.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + break; + + case DataGridViewCellBorderStyle.RaisedHorizontal: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + this.advancedCellBorderStyle.TopInternal = DataGridViewAdvancedCellBorderStyle.Outset; + this.advancedCellBorderStyle.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + break; + + case DataGridViewCellBorderStyle.SunkenHorizontal: + this.advancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + this.advancedCellBorderStyle.TopInternal = DataGridViewAdvancedCellBorderStyle.Inset; + this.advancedCellBorderStyle.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + break; + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = false; + } + OnCellBorderStyleChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_CellBorderStyleChangedDescr) + ] + public event EventHandler CellBorderStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLBORDERSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLBORDERSTYLECHANGED, value); + } + } + + internal bool CellMouseDownInContentBounds + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds]; + } + set + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds] = value; + } + } + + internal DataGridViewCellPaintingEventArgs CellPaintingEventArgs + { + get + { + if (this.dgvcpe == null) + { + this.dgvcpe = new DataGridViewCellPaintingEventArgs(this); + } + return this.dgvcpe; + } + } + + private DataGridViewCellStyleChangedEventArgs CellStyleChangedEventArgs + { + get + { + if (this.dgvcsce == null) + { + this.dgvcsce = new DataGridViewCellStyleChangedEventArgs(); + } + return this.dgvcsce; + } + } + + internal DataGridViewCellValueEventArgs CellValueEventArgs + { + get + { + if (this.dgvcve == null) + { + this.dgvcve = new DataGridViewCellValueEventArgs(); + } + return this.dgvcve; + } + } + + /// + [ + Browsable(true), + DefaultValue(DataGridViewClipboardCopyMode.EnableWithAutoHeaderText), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ClipboardCopyModeDescr) + ] + public DataGridViewClipboardCopyMode ClipboardCopyMode + { + get + { + return this.clipboardCopyMode; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewClipboardCopyMode.Disable, (int)DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewClipboardCopyMode)); + } + this.clipboardCopyMode = value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DefaultValue(0), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public int ColumnCount + { + get + { + return this.Columns.Count; + } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("ColumnCount", SR.GetString(SR.InvalidLowBoundArgumentEx, "ColumnCount", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (this.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotSetColumnCountOnDataBoundDataGridView)); + } + if (value != this.Columns.Count) + { + if (value == 0) + { + // Total removal of the columns. This also clears the rows. + this.Columns.Clear(); + } + else if (value < this.Columns.Count) + { + // Some columns need to be removed, from the tail of the columns collection + while (value < this.Columns.Count) + { + int currentColumnCount = this.Columns.Count; + this.Columns.RemoveAt(currentColumnCount - 1); + if (this.Columns.Count >= currentColumnCount) + { + // Column removal failed. We stop the loop. + break; + } + } + } + else + { + // Some DataGridViewTextBoxColumn columns need to be appened. + while (value > this.Columns.Count) + { + int currentColumnCount = this.Columns.Count; + this.Columns.Add(null /*columnName*/, null /*headerText*/); + if (this.Columns.Count <= currentColumnCount) + { + // Column addition failed. We stop the loop. + break; + } + } + } + } + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnHeadersBorderStyleDescr), + Browsable(true), + DefaultValue(DataGridViewHeaderBorderStyle.Raised) + ] + public DataGridViewHeaderBorderStyle ColumnHeadersBorderStyle + { + get + { + switch (this.advancedColumnHeadersBorderStyle.All) + { + case DataGridViewAdvancedCellBorderStyle.NotSet: + return DataGridViewHeaderBorderStyle.Custom; + + case DataGridViewAdvancedCellBorderStyle.None: + return DataGridViewHeaderBorderStyle.None; + + case DataGridViewAdvancedCellBorderStyle.Single: + return DataGridViewHeaderBorderStyle.Single; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + return DataGridViewHeaderBorderStyle.Sunken; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + return DataGridViewHeaderBorderStyle.Raised; + + default: + return DataGridViewHeaderBorderStyle.Custom; + } + } + set + { + // Sequential enum. Valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewHeaderBorderStyle.Custom, (int)DataGridViewHeaderBorderStyle.None)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewHeaderBorderStyle)); + } + if (value != this.ColumnHeadersBorderStyle) + { + if (value == DataGridViewHeaderBorderStyle.Custom) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CustomCellBorderStyleInvalid, "ColumnHeadersBorderStyle")); + } + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = true; + try + { + switch (value) + { + case DataGridViewHeaderBorderStyle.Single: + this.advancedColumnHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.Single; + break; + + case DataGridViewHeaderBorderStyle.Raised: + this.advancedColumnHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + break; + + case DataGridViewHeaderBorderStyle.Sunken: + this.advancedColumnHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.InsetDouble; + break; + + case DataGridViewHeaderBorderStyle.None: + this.advancedColumnHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + break; + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = false; + } + OnColumnHeadersBorderStyleChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnHeadersBorderStyleChangedDescr) + ] + public event EventHandler ColumnHeadersBorderStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSBORDERSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSBORDERSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnHeadersDefaultCellStyleDescr), + AmbientValue(null) + ] + public DataGridViewCellStyle ColumnHeadersDefaultCellStyle + { + get + { + if (this.columnHeadersDefaultCellStyle == null) + { + this.columnHeadersDefaultCellStyle = this.DefaultColumnHeadersDefaultCellStyle; + } + return this.columnHeadersDefaultCellStyle; + } + set + { + DataGridViewCellStyle cs = this.ColumnHeadersDefaultCellStyle; + cs.RemoveScope(DataGridViewCellStyleScopes.ColumnHeaders); + this.columnHeadersDefaultCellStyle = value; + if (value != null) + { + this.columnHeadersDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.ColumnHeaders); + } + DataGridViewCellStyleDifferences dgvcsc = cs.GetDifferencesFrom(this.ColumnHeadersDefaultCellStyle); + if (dgvcsc != DataGridViewCellStyleDifferences.None) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = (dgvcsc == DataGridViewCellStyleDifferences.AffectPreferredSize); + OnColumnHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + } + + private DataGridViewCellStyle DefaultColumnHeadersDefaultCellStyle { + get + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + defaultStyle.BackColor = DefaultHeadersBackBrush.Color; + defaultStyle.ForeColor = DefaultForeBrush.Color; + defaultStyle.SelectionBackColor = DefaultSelectionBackBrush.Color; + defaultStyle.SelectionForeColor = DefaultSelectionForeBrush.Color; + defaultStyle.Font = base.Font; + defaultStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleLeft; + defaultStyle.WrapModeInternal = DataGridViewTriState.True; + defaultStyle.AddScope(this, DataGridViewCellStyleScopes.ColumnHeaders); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] = true; + + return defaultStyle; + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewColumnHeadersDefaultCellStyleChangedDescr) + ] + public event EventHandler ColumnHeadersDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSDEFAULTCELLSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.DataGridView_ColumnHeadersHeightDescr) + ] + public int ColumnHeadersHeight + { + get + { + return this.columnHeadersHeight; + } + set + { + if (value < minimumColumnHeadersHeight) + { + throw new ArgumentOutOfRangeException("ColumnHeadersHeight", SR.GetString(SR.InvalidLowBoundArgumentEx, "ColumnHeadersHeight", (value).ToString(CultureInfo.CurrentCulture), (minimumColumnHeadersHeight).ToString(CultureInfo.CurrentCulture))); + } + if (value > maxHeadersThickness) + { + throw new ArgumentOutOfRangeException("ColumnHeadersHeight", SR.GetString(SR.InvalidHighBoundArgumentEx, "ColumnHeadersHeight", (value).ToString(CultureInfo.CurrentCulture), (maxHeadersThickness).ToString(CultureInfo.CurrentCulture))); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + this.cachedColumnHeadersHeight = value; + } + else if (this.columnHeadersHeight != value) + { + SetColumnHeadersHeightInternal(value, true /*invalidInAdjustFillingColumns*/); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewColumnHeadersHeightChangedDescr) + ] + public event EventHandler ColumnHeadersHeightChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTCHANGED, value); + } + } + + private bool ShouldSerializeColumnHeadersHeight() + { + return this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize && defaultColumnHeadersHeight != this.ColumnHeadersHeight; + } + + /// + /// + /// + /// Gets or sets a value that determines the behavior for adjusting the column headers height. + /// + /// + [ + DefaultValue(DataGridViewColumnHeadersHeightSizeMode.EnableResizing), + RefreshProperties(RefreshProperties.All), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnHeadersHeightSizeModeDescr) + ] + public DataGridViewColumnHeadersHeightSizeMode ColumnHeadersHeightSizeMode + { + get + { + return this.columnHeadersHeightSizeMode; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewColumnHeadersHeightSizeMode.EnableResizing, (int)DataGridViewColumnHeadersHeightSizeMode.AutoSize)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewColumnHeadersHeightSizeMode)); + } + if (this.columnHeadersHeightSizeMode != value) + { + /*if (value == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !this.ColumnHeadersVisible) + { + We intentionally don't throw an error because of designer code spit order. + }*/ + DataGridViewAutoSizeModeEventArgs dgvasme = new DataGridViewAutoSizeModeEventArgs(this.columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize); + this.columnHeadersHeightSizeMode = value; + OnColumnHeadersHeightSizeModeChanged(dgvasme); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnHeadersHeightSizeModeChangedDescr) + ] + public event DataGridViewAutoSizeModeEventHandler ColumnHeadersHeightSizeModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTSIZEMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTSIZEMODECHANGED, value); + } + } + + /// + /// Indicates whether the ComboBox editing control was just detached. (focused out to another cell) + /// + internal bool ComboBoxControlWasDetached { get; set; } + + /// + /// Indicates whether the TextBox editing control was just detached. (focused out to another cell) + /// + internal bool TextBoxControlWasDetached { get; set; } + + /// + /// + /// + /// Gets + /// or sets a value indicating if the dataGridView's column headers are visible. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.DataGridViewColumnHeadersVisibleDescr) + ] + public bool ColumnHeadersVisible + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_columnHeadersVisible]; + } + set + { + if (this.ColumnHeadersVisible != value) + { + if (!value) + { + // Make sure that there is no visible column that only counts on the column headers to autosize + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnHeadersCannotBeInvisible)); + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.ColumnHeadersVisible)) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_columnHeadersVisible] = value; + this.layout.ColumnHeadersVisible = value; + this.displayedBandsInfo.EnsureDirtyState(); + if (!this.AutoSize) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + InvalidateInside(); + OnColumnHeadersGlobalAutoSize(); + } + } + } + } + + /// + [ + Editor("System.Windows.Forms.Design.DataGridViewColumnCollectionEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + MergableProperty(false) + ] + public DataGridViewColumnCollection Columns + { + get + { + if (this.dataGridViewColumns == null) + { + this.dataGridViewColumns = CreateColumnsInstance(); + } + return this.dataGridViewColumns; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewCell CurrentCell + { + get + { + if (this.ptCurrentCell.X == -1 && this.ptCurrentCell.Y == -1) + { + return null; + } + Debug.Assert(this.ptCurrentCell.X >= 0 && ptCurrentCell.Y >= 0); + Debug.Assert(this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y < this.Rows.Count); + DataGridViewRow dataGridViewRow = (DataGridViewRow) this.Rows[this.ptCurrentCell.Y]; // unsharing row + return dataGridViewRow.Cells[this.ptCurrentCell.X]; + } + set + { + if ((value != null && (value.RowIndex != this.ptCurrentCell.Y || value.ColumnIndex != this.ptCurrentCell.X)) || + (value == null && this.ptCurrentCell.X != -1)) + { + if (value == null) + { + ClearSelection(); + if (!SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, true /*validateCurrentCell*/, false /*throughMouseClick*/)) + { + // Edited value couldn't be committed or aborted + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + else + { + if (value.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CellDoesNotBelongToDataGridView)); + } + if (!this.Columns[value.ColumnIndex].Visible || + (this.Rows.GetRowState(value.RowIndex) & DataGridViewElementStates.Visible) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrentCellCannotBeInvisible)); + } + if (!ScrollIntoView(value.ColumnIndex, value.RowIndex, true)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + if (IsInnerCellOutOfBounds(value.ColumnIndex, value.RowIndex)) + { + return; + } + ClearSelection(value.ColumnIndex, value.RowIndex, true /*selectExceptionElement*/); + if (!SetCurrentCellAddressCore(value.ColumnIndex, value.RowIndex, true, false, false)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + } + } + } + + /// + [ + Browsable(false) + ] + public Point CurrentCellAddress + { + get + { + return this.ptCurrentCell; + } + } + + private DataGridViewCell CurrentCellInternal + { + get + { + Debug.Assert(this.ptCurrentCell.X >= 0 && this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y >= 0 && this.ptCurrentCell.Y < this.Rows.Count); + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(this.ptCurrentCell.Y); + Debug.Assert(dataGridViewRow != null); + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[this.ptCurrentCell.X]; + Debug.Assert(this.IsSharedCellVisible(dataGridViewCell, this.ptCurrentCell.Y)); + return dataGridViewCell; + } + } + + private bool CurrentCellIsFirstVisibleCell + { + get + { + if (this.ptCurrentCell.X == -1) + { + return false; + } + Debug.Assert(this.ptCurrentCell.Y != -1); + + bool previousVisibleColumnExists = (null != this.Columns.GetPreviousColumn(this.Columns[this.ptCurrentCell.X], DataGridViewElementStates.Visible, DataGridViewElementStates.None)); + bool previousVisibleRowExists = (-1 != this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible)); + + return !previousVisibleColumnExists && !previousVisibleRowExists; + } + } + + private bool CurrentCellIsLastVisibleCell + { + get + { + if (this.ptCurrentCell.X == -1) + { + return false; + } + + Debug.Assert(this.ptCurrentCell.Y != -1); + + bool nextVisibleColumnExists = (null != this.Columns.GetNextColumn(this.Columns[this.ptCurrentCell.X], DataGridViewElementStates.Visible, DataGridViewElementStates.None)); + bool nextVisibleRowExists = (-1 != this.Rows.GetNextRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible)); + + return !nextVisibleColumnExists && !nextVisibleRowExists; + } + } + + private bool CurrentCellIsEditedAndOnlySelectedCell + { + get + { + if (this.ptCurrentCell.X == -1) + { + return false; + } + + Debug.Assert(this.ptCurrentCell.Y != -1); + + return this.editingControl != null && + GetCellCount(DataGridViewElementStates.Selected) == 1 && + this.CurrentCellInternal.Selected; + } + } + + /// + [ + Browsable(false) + ] + public DataGridViewRow CurrentRow + { + get + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + + Debug.Assert(this.ptCurrentCell.Y >= 0); + Debug.Assert(this.ptCurrentCell.Y < this.Rows.Count); + + return this.Rows[this.ptCurrentCell.Y]; + } + } + + internal Cursor CursorInternal + { + set + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_ignoreCursorChange] = true; + try + { + this.Cursor = value; + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_ignoreCursorChange] = false; + } + } + } + + internal DataGridViewDataConnection DataConnection + { + get + { + return this.dataConnection; + } + } + + /// + [ + DefaultValue(""), + SRCategory(SR.CatData), + Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.DataGridViewDataMemberDescr) + ] + public string DataMember + { + get + { + if (this.dataConnection == null) + { + return String.Empty; + } + else + { + return this.dataConnection.DataMember; + } + } + set + { + if (value != this.DataMember) + { + this.CurrentCell = null; + if (this.dataConnection == null) + { + this.dataConnection = new DataGridViewDataConnection(this); + } + this.dataConnection.SetDataConnection(this.DataSource, value); + OnDataMemberChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewDataMemberChangedDescr) + ] + public event EventHandler DataMemberChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDATAMEMBERCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDATAMEMBERCHANGED, value); + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.DataGridViewDataSourceDescr) + ] + public object DataSource + { + get + { + if (this.dataConnection == null) + { + return null; + } + else + { + return this.dataConnection.DataSource; + } + } + set + { + if (value != this.DataSource) + { + this.CurrentCell = null; + if (this.dataConnection == null) + { + this.dataConnection = new DataGridViewDataConnection(this); + this.dataConnection.SetDataConnection(value, this.DataMember); + } + else + { + if (this.dataConnection.ShouldChangeDataMember(value)) + { + // we fire DataMemberChanged event + this.DataMember = ""; + } + this.dataConnection.SetDataConnection(value, this.DataMember); + if (value == null) + { + this.dataConnection = null; + } + } + OnDataSourceChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewDataSourceChangedDescr) + ] + public event EventHandler DataSourceChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDATASOURCECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDATASOURCECHANGED, value); + } + } + + private static SolidBrush DefaultBackBrush + { + get + { + return (SolidBrush) SystemBrushes.Window; + } + } + + private static SolidBrush DefaultBackgroundBrush + { + get + { + return (SolidBrush) SystemBrushes.AppWorkspace; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_DefaultCellStyleDescr), + AmbientValue(null) + ] + public DataGridViewCellStyle DefaultCellStyle + { + get + { + if (this.defaultCellStyle == null) + { + this.defaultCellStyle = this.DefaultDefaultCellStyle; + return this.defaultCellStyle; + } + else if (this.defaultCellStyle.BackColor == Color.Empty || + this.defaultCellStyle.ForeColor == Color.Empty || + this.defaultCellStyle.SelectionBackColor == Color.Empty || + this.defaultCellStyle.SelectionForeColor == Color.Empty || + this.defaultCellStyle.Font == null || + this.defaultCellStyle.Alignment == DataGridViewContentAlignment.NotSet || + this.defaultCellStyle.WrapMode == DataGridViewTriState.NotSet) + { + DataGridViewCellStyle defaultCellStyleTmp = new DataGridViewCellStyle(this.defaultCellStyle); + defaultCellStyleTmp.Scope = DataGridViewCellStyleScopes.None; + if (this.defaultCellStyle.BackColor == Color.Empty) + { + defaultCellStyleTmp.BackColor = DefaultBackBrush.Color; + } + if (this.defaultCellStyle.ForeColor == Color.Empty) + { + defaultCellStyleTmp.ForeColor = base.ForeColor; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = true; + } + if (this.defaultCellStyle.SelectionBackColor == Color.Empty) + { + defaultCellStyleTmp.SelectionBackColor = DefaultSelectionBackBrush.Color; + } + if (this.defaultCellStyle.SelectionForeColor == Color.Empty) + { + defaultCellStyleTmp.SelectionForeColor = DefaultSelectionForeBrush.Color; + } + if (this.defaultCellStyle.Font == null) + { + defaultCellStyleTmp.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = true; + } + if (this.defaultCellStyle.Alignment == DataGridViewContentAlignment.NotSet) + { + defaultCellStyleTmp.AlignmentInternal = DataGridViewContentAlignment.MiddleLeft; + } + if (this.defaultCellStyle.WrapMode == DataGridViewTriState.NotSet) + { + defaultCellStyleTmp.WrapModeInternal = DataGridViewTriState.False; + } + defaultCellStyleTmp.AddScope(this, DataGridViewCellStyleScopes.DataGridView); + return defaultCellStyleTmp; + } + else + { + return this.defaultCellStyle; + } + } + set + { + DataGridViewCellStyle cs = this.DefaultCellStyle; + cs.RemoveScope(DataGridViewCellStyleScopes.DataGridView); + this.defaultCellStyle = value; + if (value != null) + { + this.defaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.DataGridView); + } + DataGridViewCellStyleDifferences dgvcsc = cs.GetDifferencesFrom(this.DefaultCellStyle); + if (dgvcsc != DataGridViewCellStyleDifferences.None) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = (dgvcsc == DataGridViewCellStyleDifferences.AffectPreferredSize); + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + } + + private DataGridViewCellStyle DefaultDefaultCellStyle + { + get + { + DataGridViewCellStyle defaultCellStyle = new DataGridViewCellStyle(); + defaultCellStyle.BackColor = DefaultBackBrush.Color; + defaultCellStyle.ForeColor = base.ForeColor; + defaultCellStyle.SelectionBackColor = DefaultSelectionBackBrush.Color; + defaultCellStyle.SelectionForeColor = DefaultSelectionForeBrush.Color; + defaultCellStyle.Font = base.Font; + defaultCellStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleLeft; + defaultCellStyle.WrapModeInternal = DataGridViewTriState.False; + defaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.DataGridView); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = true; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = true; + + return defaultCellStyle; + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewDefaultCellStyleChangedDescr) + ] + public event EventHandler DefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDEFAULTCELLSTYLECHANGED, value); + } + } + + private static SolidBrush DefaultForeBrush + { + get + { + return (SolidBrush) SystemBrushes.WindowText; + } + } + + private static Color DefaultGridColor + { + get + { + return SystemColors.ControlDark; + } + } + + private static SolidBrush DefaultHeadersBackBrush + { + get + { + return (SolidBrush) SystemBrushes.Control; + } + } + + private DataGridViewCellStyle DefaultRowHeadersDefaultCellStyle + { + get + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + defaultStyle.BackColor = DefaultHeadersBackBrush.Color; + defaultStyle.ForeColor = DefaultForeBrush.Color; + defaultStyle.SelectionBackColor = DefaultSelectionBackBrush.Color; + defaultStyle.SelectionForeColor = DefaultSelectionForeBrush.Color; + defaultStyle.Font = base.Font; + defaultStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleLeft; + defaultStyle.WrapModeInternal = DataGridViewTriState.True; + defaultStyle.AddScope(this, DataGridViewCellStyleScopes.RowHeaders); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] = true; + + return defaultStyle; + } + } + + private static SolidBrush DefaultSelectionBackBrush + { + get + { + return (SolidBrush) SystemBrushes.Highlight; + } + } + + private static SolidBrush DefaultSelectionForeBrush + { + get + { + return (SolidBrush) SystemBrushes.HighlightText; + } + } + + /// + protected override Size DefaultSize + { + get + { + return new Size(240, 150); + } + } + + internal DisplayedBandsData DisplayedBandsInfo + { + get + { + return this.displayedBandsInfo; + } + } + + /// + /// + /// Returns the client rect of the display area of the control. + /// The DataGridView control return its client rectangle minus the potential scrollbars. + /// + public override Rectangle DisplayRectangle { + get { + Rectangle rectDisplay = this.ClientRectangle; + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + rectDisplay.Height -= this.horizScrollBar.Height; + } + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + rectDisplay.Width -= this.vertScrollBar.Width; + if (this.RightToLeftInternal) + { + rectDisplay.X = this.vertScrollBar.Width; + } + } + return rectDisplay; + } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DataGridViewEditMode.EditOnKeystrokeOrF2), + SRDescription(SR.DataGridView_EditModeDescr) + ] + public DataGridViewEditMode EditMode + { + get + { + return this.editMode; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewEditMode.EditOnEnter, (int)DataGridViewEditMode.EditProgrammatically)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewEditMode)); + } + if (this.editMode != value) + { + this.editMode = value; + OnEditModeChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_EditModeChangedDescr) + ] + public event EventHandler EditModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWEDITMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWEDITMODECHANGED, value); + } + } + + internal Point MouseEnteredCellAddress + { + get + { + return this.ptMouseEnteredCell; + } + } + + private bool MouseOverEditingControl + { + get + { + if (this.editingControl != null) + { + Point ptMouse = PointToClient(Control.MousePosition); + return this.editingControl.Bounds.Contains(ptMouse); + } + return false; + } + } + + private bool MouseOverEditingPanel + { + get + { + if (this.editingPanel != null) + { + Point ptMouse = PointToClient(Control.MousePosition); + return this.editingPanel.Bounds.Contains(ptMouse); + } + return false; + } + } + + private bool MouseOverScrollBar + { + get + { + Point ptMouse = PointToClient(Control.MousePosition); + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + if (this.vertScrollBar.Bounds.Contains(ptMouse)) + { + return true; + } + } + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + return this.horizScrollBar.Bounds.Contains(ptMouse); + } + return false; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public Control EditingControl + { + get + { + return this.editingControl; + } + } + + internal AccessibleObject EditingControlAccessibleObject + { + get + { + return EditingControl.AccessibilityObject; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public Panel EditingPanel + { + get + { + if (this.editingPanel == null) + { + this.editingPanel = AccessibilityImprovements.Level3 ? new DataGridViewEditingPanel(this) : new Panel(); + this.editingPanel.AccessibleName = SR.GetString(SR.DataGridView_AccEditingPanelAccName); + } + return this.editingPanel; + } + } + + internal DataGridViewEditingPanelAccessibleObject EditingPanelAccessibleObject + { + get + { + if (this.editingPanelAccessibleObject == null) + { + IntSecurity.UnmanagedCode.Assert(); + try + { + editingPanelAccessibleObject = new DataGridViewEditingPanelAccessibleObject(this, this.EditingPanel); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + + return editingPanelAccessibleObject; + } + } + + /// + /// + /// + /// Determines whether the DataGridView's header cells render using XP theming visual styles or not + /// when visual styles are enabled in the application. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.DataGridView_EnableHeadersVisualStylesDescr) + ] + public bool EnableHeadersVisualStyles + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_enableHeadersVisualStyles]; + } + set + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_enableHeadersVisualStyles] != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_enableHeadersVisualStyles] = value; + //OnEnableHeadersVisualStylesChanged(EventArgs.Empty); + // Some autosizing may have to be applied since the margins are potentially changed. + OnGlobalAutoSize(); // Put this into OnEnableHeadersVisualStylesChanged if created. + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewCell FirstDisplayedCell + { + get + { + Point firstDisplayedCellAddress = this.FirstDisplayedCellAddress; + if (firstDisplayedCellAddress.X >= 0) + { + return this.Rows[firstDisplayedCellAddress.Y].Cells[firstDisplayedCellAddress.X]; // unshares the row of first displayed cell + } + return null; + } + set + { + if (value != null) + { + DataGridViewCell firstDisplayedCell = value; + if (firstDisplayedCell.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CellDoesNotBelongToDataGridView)); + } + if (firstDisplayedCell.RowIndex == -1 || firstDisplayedCell.ColumnIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedCellCannotBeAHeaderOrSharedCell)); + } + + Debug.Assert(firstDisplayedCell.RowIndex >= 0 && + firstDisplayedCell.RowIndex < this.Rows.Count && + firstDisplayedCell.ColumnIndex >= 0 && + firstDisplayedCell.ColumnIndex < this.Columns.Count); + + if (!firstDisplayedCell.Visible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedCellCannotBeInvisible)); + } + + if (!firstDisplayedCell.Frozen) + { + if (!this.Rows[firstDisplayedCell.RowIndex].Frozen) + { + this.FirstDisplayedScrollingRowIndex = firstDisplayedCell.RowIndex; + } + + if (!this.Columns[firstDisplayedCell.ColumnIndex].Frozen) + { + this.FirstDisplayedScrollingColumnIndex = firstDisplayedCell.ColumnIndex; + } + } + } + } + } + + private Point FirstDisplayedCellAddress + { + get + { + Point ptFirstDisplayedCellAddress = new Point(-1, -1); + ptFirstDisplayedCellAddress.Y = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (ptFirstDisplayedCellAddress.Y == -1) + { + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == 0); + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + ptFirstDisplayedCellAddress.Y = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } +#if DEBUG + else + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow == -1); + Debug.Assert(this.displayedBandsInfo.NumDisplayedScrollingRows == 0); + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedScrollingRows == 0); + } +#endif + } + if (ptFirstDisplayedCellAddress.Y >= 0) + { + ptFirstDisplayedCellAddress.X = this.FirstDisplayedColumnIndex; + } + return ptFirstDisplayedCellAddress; + } + } + + internal int FirstDisplayedColumnIndex + { + get + { + if (!this.IsHandleCreated) + { + return -1; + } + + int firstDisplayedColumnIndex = -1; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null) + { + if (dataGridViewColumn.Frozen) + { + firstDisplayedColumnIndex = dataGridViewColumn.Index; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + firstDisplayedColumnIndex = this.displayedBandsInfo.FirstDisplayedScrollingCol; + } + } +#if DEBUG + DataGridViewColumn dataGridViewColumnDbg1 = this.Columns.GetFirstColumn(DataGridViewElementStates.Displayed); + int firstDisplayedColumnIndexDbg1 = (dataGridViewColumnDbg1 == null) ? -1 : dataGridViewColumnDbg1.Index; + + int firstDisplayedColumnIndexDbg2 = -1; + DataGridViewColumn dataGridViewColumnDbg = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (dataGridViewColumnDbg != null) + { + firstDisplayedColumnIndexDbg2 = dataGridViewColumnDbg.Index; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + firstDisplayedColumnIndexDbg2 = this.displayedBandsInfo.FirstDisplayedScrollingCol; + } + else + { + Debug.Assert(this.displayedBandsInfo.LastTotallyDisplayedScrollingCol == -1); + } + Debug.Assert(firstDisplayedColumnIndex == firstDisplayedColumnIndexDbg1 || !this.Visible || this.displayedBandsInfo.Dirty); + Debug.Assert(firstDisplayedColumnIndex == firstDisplayedColumnIndexDbg2 || this.displayedBandsInfo.Dirty); +#endif + return firstDisplayedColumnIndex; + } + } + + internal int FirstDisplayedRowIndex + { + get + { + if (!this.IsHandleCreated) + { + return -1; + } + + int firstDisplayedRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstDisplayedRowIndex != -1) + { + if ((this.Rows.GetRowState(firstDisplayedRowIndex) & DataGridViewElementStates.Frozen) == 0 && + this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + firstDisplayedRowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + } +#if FALSE //DEBUG + int firstDisplayedRowIndexDbg1 = this.Rows.GetFirstRow(DataGridViewElementStates.Displayed); + + int firstDisplayedRowIndexDbg2 = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (firstDisplayedRowIndexDbg2 == -1) + { + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + firstDisplayedRowIndexDbg2 = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + } + + Debug.Assert(firstDisplayedRowIndex == firstDisplayedRowIndexDbg1 || !this.Visible || this.displayedBandsInfo.Dirty, "firstDisplayedRowIndex =" + firstDisplayedRowIndex.ToString() + ", firstDisplayedRowIndexDbg1=" + firstDisplayedRowIndexDbg1.ToString()); + Debug.Assert(firstDisplayedRowIndex == firstDisplayedRowIndexDbg2 || this.displayedBandsInfo.Dirty, "firstDisplayedRowIndex =" + firstDisplayedRowIndex.ToString() + ", firstDisplayedRowIndexDbg2=" + firstDisplayedRowIndexDbg2.ToString()); +#endif + return firstDisplayedRowIndex; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int FirstDisplayedScrollingColumnHiddenWidth + { + get + { + return this.negOffset; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int FirstDisplayedScrollingColumnIndex + { + get + { + return this.displayedBandsInfo.FirstDisplayedScrollingCol; + } + set + { + if (value < 0 || value >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("value"); + } + if (!this.Columns[value].Visible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedScrollingColumnCannotBeInvisible)); + } + if (this.Columns[value].Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedScrollingColumnCannotBeFrozen)); + } + + if (!this.IsHandleCreated) + { + CreateHandle(); + } + + int displayWidth = this.layout.Data.Width; + if (displayWidth <= 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_NoRoomForDisplayedColumns)); + } + + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (totalVisibleFrozenWidth >= displayWidth) + { + Debug.Assert(totalVisibleFrozenWidth > 0); + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FrozenColumnsPreventFirstDisplayedScrollingColumn)); + } + + if (value == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + return; + } + + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value - return silently + // Microsoft: should we throw an error here? + return; + } + if (IsColumnOutOfBounds(value)) + { + return; + } + bool success = ScrollColumnIntoView(value, -1, /*committed*/ true, false /*forCurrentCellChange*/); + Debug.Assert(success); + + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol == value || + this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, value)); + int maxHorizontalOffset = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - displayWidth; + while (this.displayedBandsInfo.FirstDisplayedScrollingCol != value && + this.HorizontalOffset < maxHorizontalOffset) + { + ScrollColumns(1); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int FirstDisplayedScrollingRowIndex + { + get + { + return this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + set + { + if (value < 0 || value >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("value"); + } + if ((this.Rows.GetRowState(value) & DataGridViewElementStates.Visible) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedScrollingRowCannotBeInvisible)); + } + if ((this.Rows.GetRowState(value) & DataGridViewElementStates.Frozen) != 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FirstDisplayedScrollingRowCannotBeFrozen)); + } + + if (!this.IsHandleCreated) + { + CreateHandle(); + } + + int displayHeight = this.layout.Data.Height; + if (displayHeight <= 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_NoRoomForDisplayedRows)); + } + + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (totalVisibleFrozenHeight >= displayHeight) + { + Debug.Assert(totalVisibleFrozenHeight > 0); + throw new InvalidOperationException(SR.GetString(SR.DataGridView_FrozenRowsPreventFirstDisplayedScrollingRow)); + } + + if (value == this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + return; + } + + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value - return silently + // Microsoft: should we throw an error here? + return; + } + if (IsRowOutOfBounds(value)) + { + return; + } + + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + + if (value > this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + int rowsToScroll = this.Rows.GetRowCount(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, value); + Debug.Assert(rowsToScroll != 0); + ScrollRowsByCount(rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement); + } + else + { + bool success = ScrollRowIntoView(-1, value, /*committed*/ true, false /*forCurrentCellChange*/); + Debug.Assert(success); + } + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Color ForeColor + { + get + { + return base.ForeColor; + } + set + { + base.ForeColor = value; + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + new public event EventHandler ForeColorChanged + { + add + { + base.ForeColorChanged += value; + } + remove + { + base.ForeColorChanged -= value; + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public override Font Font + { + get + { + return base.Font; + } + set + { + base.Font = value; + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + new public event EventHandler FontChanged + { + add + { + base.FontChanged += value; + } + remove + { + base.FontChanged -= value; + } + } + + /// + /// + /// Gets or sets the grid color of the dataGridView (when Single mode is used). + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridViewGridColorDescr) + ] + public Color GridColor + { + get + { + return this.gridPen.Color; + } + set + { + if (value.IsEmpty) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_EmptyColor, "GridColor")); + } + if (value.A < 255) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_TransparentColor, "GridColor")); + } + if (!value.Equals(this.gridPen.Color)) + { + if (this.gridPen != null) + { + this.gridPen.Dispose(); + } + + this.gridPen = new Pen(value); + OnGridColorChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnGridColorChangedDescr) + ] + public event EventHandler GridColorChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWGRIDCOLORCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWGRIDCOLORCHANGED, value); + } + } + + /// + private bool ShouldSerializeGridColor() + { + return !this.GridPen.Color.Equals(DefaultGridColor); + } + + internal Pen GridPen + { + get + { + return this.gridPen; + } + } + + internal int HorizontalOffset + { + get + { + return this.horizontalOffset; + } + set + { + if (value < 0) + { + value = 0; + } + int widthNotVisible = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - this.layout.Data.Width; + if (value > widthNotVisible && widthNotVisible > 0) + { + value = widthNotVisible; + } + if (value == this.horizontalOffset) + { + return; + } + + ScrollEventType scrollEventType; + int oldFirstVisibleScrollingCol = this.displayedBandsInfo.FirstDisplayedScrollingCol; + int change = this.horizontalOffset - value; + if (this.horizScrollBar.Enabled) + { + this.horizScrollBar.Value = value; + } + this.horizontalOffset = value; + + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + Rectangle rectTmp = this.layout.Data; + if (this.layout.ColumnHeadersVisible) + { + // column headers must scroll as well + rectTmp = Rectangle.Union(rectTmp, this.layout.ColumnHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + rectTmp.X--; + } + rectTmp.Width++; + } + + if (this.SingleVerticalBorderAdded && + totalVisibleFrozenWidth > 0) + { + if (!this.RightToLeftInternal) + { + rectTmp.X++; + } + rectTmp.Width--; + } + + if (!this.RightToLeftInternal) + { + rectTmp.X += totalVisibleFrozenWidth; + } + rectTmp.Width -= totalVisibleFrozenWidth; + + this.displayedBandsInfo.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn(); + // update the lastTotallyDisplayedScrollingCol + ComputeVisibleColumns(); + + if (this.editingControl != null && + !this.Columns[this.ptCurrentCell.X].Frozen && + this.displayedBandsInfo.FirstDisplayedScrollingCol > -1) + { + PositionEditingControl(true /*setLocation*/, false /*setSize*/, false /*setFocus*/); + } + + // The mouse probably is not over the same cell after the scroll. + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + if (oldFirstVisibleScrollingCol == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + scrollEventType = change > 0 ? ScrollEventType.SmallIncrement : ScrollEventType.SmallDecrement; + } + else if (this.Columns.DisplayInOrder(oldFirstVisibleScrollingCol, this.displayedBandsInfo.FirstDisplayedScrollingCol)) + { + scrollEventType = this.Columns.GetColumnCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingCol, this.displayedBandsInfo.FirstDisplayedScrollingCol) > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement; + } + else + { + Debug.Assert(this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, oldFirstVisibleScrollingCol)); + scrollEventType = this.Columns.GetColumnCount(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingCol, oldFirstVisibleScrollingCol) > 1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement; + } + + NativeMethods.RECT[] rects = CreateScrollableRegion(rectTmp); + if (this.RightToLeftInternal) + { + change = -change; + } + ScrollRectangles(rects, change); + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll]) + { + OnScroll(scrollEventType, this.horizontalOffset + change, this.horizontalOffset, ScrollOrientation.HorizontalScroll); + } + FlushDisplayedChanged(); + } + } + + /// + protected ScrollBar HorizontalScrollBar + { + get + { + return this.horizScrollBar; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int HorizontalScrollingOffset + { + get + { + return this.horizontalOffset; + } + set + { + // int widthNotVisible = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - this.layout.Data.Width; + if (value < 0) + { + throw new ArgumentOutOfRangeException("HorizontalScrollingOffset", SR.GetString(SR.InvalidLowBoundArgumentEx, "HorizontalScrollingOffset", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + // Intentionally ignoring the out of range situation. + // else if (value > widthNotVisible && widthNotVisible > 0) + //{ + // throw new ArgumentOutOfRangeException(SR.GetString(SR.DataGridView_PropertyTooLarge, "HorizontalScrollingOffset", (widthNotVisible).ToString())); + //} + else if (value > 0 && (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - this.layout.Data.Width) <= 0) + { + // Intentionally ignoring the case where dev tries to set value while there is no horizontal scrolling possible. + // throw new ArgumentOutOfRangeException("HorizontalScrollingOffset", SR.GetString(SR.DataGridView_PropertyMustBeZero)); + Debug.Assert(this.horizontalOffset == 0); + return; + } + if (value == this.horizontalOffset) + { + return; + } + this.HorizontalOffset = value; + } + } + + private System.Windows.Forms.Timer HorizScrollTimer + { + get + { + if (this.horizScrollTimer == null) + { + this.horizScrollTimer = new System.Windows.Forms.Timer(); + this.horizScrollTimer.Tick += new System.EventHandler(HorizScrollTimer_Tick); + } + return this.horizScrollTimer; + } + } + + private bool InAdjustFillingColumns + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumn] || this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns]; + } + } + + internal bool InBeginEdit + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit]; + } + } + + internal bool InDisplayIndexAdjustments + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]; + } + set + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = value; + } + } + + internal bool InEndEdit + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit]; + } + } + + private DataGridViewCellStyle InheritedEditingCellStyle + { + get + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + + return this.CurrentCellInternal.GetInheritedStyleInternal(this.ptCurrentCell.Y); + } + } + + internal bool InInitialization + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_initializing]; + } + } + + internal bool InSortOperation + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_inSort]; + } + } + + /// + [Browsable(false)] + public bool IsCurrentCellDirty + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedCellChanged]; + } + } + + private bool IsCurrentCellDirtyInternal + { + set + { + if (value != this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedCellChanged]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedCellChanged] = value; + OnCurrentCellDirtyStateChanged(EventArgs.Empty); + } + } + } + + /// + [Browsable(false)] + public bool IsCurrentCellInEditMode + { + get + { + return this.editingControl != null || this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]; + } + } + + /// + // Only used in bound scenarios, when binding to a IEditableObject + [Browsable(false)] + public bool IsCurrentRowDirty + { + get + { + if (!this.VirtualMode) + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged] || this.IsCurrentCellDirty; + } + else + { + QuestionEventArgs qe = new QuestionEventArgs(this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged] || this.IsCurrentCellDirty); + OnRowDirtyStateNeeded(qe); + return qe.Response; + } + } + } + + internal bool IsCurrentRowDirtyInternal + { + set + { + if (value != this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged] = value; + if (this.RowHeadersVisible && this.ShowEditingIcon && this.ptCurrentCell.Y >= 0) + { + // Force the pencil to appear in the row header + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + } + } + } + + private bool IsEscapeKeyEffective + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] || + this.IsCurrentCellDirty || + ((this.VirtualMode || this.DataSource != null) && this.IsCurrentRowDirty) || + (this.EditMode != DataGridViewEditMode.EditOnEnter && this.editingControl != null || + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited]); + } + } + + private bool IsMinimized + { + get + { + Form parentForm = this.TopLevelControlInternal as Form; + return parentForm != null && parentForm.WindowState == FormWindowState.Minimized; + } + } + + internal bool IsRestricted + { + get + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestrictedChecked]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestricted] = false; + try + { + IntSecurity.AllWindows.Demand(); + } + catch (SecurityException) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestricted] = true; + } + catch + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestricted] = true; // To be on the safe side + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestrictedChecked] = true; + throw; + } + this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestrictedChecked] = true; + } + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_isRestricted]; + } + } + + private bool IsSharedCellReadOnly(DataGridViewCell dataGridViewCell, int rowIndex) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + return this.ReadOnly || + (rowState & DataGridViewElementStates.ReadOnly) != 0 || + (dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningColumn.ReadOnly) || + dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly); + } + + internal bool IsSharedCellSelected(DataGridViewCell dataGridViewCell, int rowIndex) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + return (rowState & DataGridViewElementStates.Selected) != 0 || + (dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningColumn.Selected) || + dataGridViewCell.StateIncludes(DataGridViewElementStates.Selected); + } + + internal bool IsSharedCellVisible(DataGridViewCell dataGridViewCell, int rowIndex) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + return (rowState & DataGridViewElementStates.Visible) != 0 && + (dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningColumn.Visible); + } + + internal LayoutData LayoutInfo + { + get + { + if (this.layout.dirty && this.IsHandleCreated) + { + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + return this.layout; + } + } + + internal Point MouseDownCellAddress + { + get + { + return this.ptMouseDownCell; + } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.DataGridView_MultiSelectDescr) + ] + public bool MultiSelect + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_multiSelect]; + } + set + { + if (this.MultiSelect != value) + { + ClearSelection(); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_multiSelect] = value; + OnMultiSelectChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnMultiSelectChangedDescr) + ] + public event EventHandler MultiSelectChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWMULTISELECTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWMULTISELECTCHANGED, value); + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int NewRowIndex + { + get + { + return this.newRowIndex; + } + } + + internal bool NoDimensionChangeAllowed + { + get + { + return this.noDimensionChangeCount > 0; + } + } + + private int NoSelectionChangeCount + { + get + { + return this.noSelectionChangeCount; + } + set + { + Debug.Assert(value >= 0); + this.noSelectionChangeCount = value; + if (value == 0) + { + FlushSelectionChanged(); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding + { + get + { + return base.Padding; + } + set + { + base.Padding = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler PaddingChanged + { + add + { + base.PaddingChanged += value; + } + remove + { + base.PaddingChanged -= value; + } + } + + internal DataGridViewCellStyle PlaceholderCellStyle + { + get + { + if (this.placeholderCellStyle == null) + { + this.placeholderCellStyle = new DataGridViewCellStyle(); + } + return this.placeholderCellStyle; + } + } + + /// + [ + Browsable(true), + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ReadOnlyDescr) + ] + public bool ReadOnly + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_readOnly]; + } + set + { + if (value != this.dataGridViewState1[DATAGRIDVIEWSTATE1_readOnly]) + { + if (value && + this.ptCurrentCell.X != -1 && + this.IsCurrentCellInEditMode) + { + // Current cell becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_readOnly] = value; + + if (value) + { + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = true; + for (int columnIndex = 0; columnIndex < this.Columns.Count; columnIndex++) + { + SetReadOnlyColumnCore(columnIndex, false); + } + int rowCount = this.Rows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + SetReadOnlyRowCore(rowIndex, false); + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = false; + } + } +#if DEBUG + else + { + Debug.Assert(this.individualReadOnlyCells.Count == 0); + for (int columnIndex = 0; columnIndex < this.Columns.Count; columnIndex++) + { + Debug.Assert(this.Columns[columnIndex].ReadOnly == false); + } + int rowCount = this.Rows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.ReadOnly) == 0); + } + } +#endif + OnReadOnlyChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewOnReadOnlyChangedDescr) + ] + public event EventHandler ReadOnlyChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWREADONLYCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWREADONLYCHANGED, value); + } + } + + private void ResetCurrentCell() + { + if (this.ptCurrentCell.X != -1 && + !SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, true /*validateCurrentCell*/, false /*throughMouseClick*/)) + { + // Edited value couldn't be committed or aborted + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + + internal bool ResizingOperationAboutToStart + { + get + { + return this.dataGridViewOper[DATAGRIDVIEWOPER_resizingOperationAboutToStart]; + } + } + + internal bool RightToLeftInternal + { + get + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftValid]) + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftMode]; + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftMode] = (this.RightToLeft == RightToLeft.Yes); + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftValid] = true; + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftMode]; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(0) + ] + public int RowCount + { + get + { + return this.Rows.Count; + } + set + { + if (this.AllowUserToAddRowsInternal) + { + if (value < 1) + { + throw new ArgumentOutOfRangeException("RowCount", SR.GetString(SR.InvalidLowBoundArgumentEx, "RowCount", value.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + } + else + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("RowCount", SR.GetString(SR.InvalidLowBoundArgumentEx, "RowCount", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + } + if (this.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotSetRowCountOnDataBoundDataGridView)); + } + if (value != this.Rows.Count) + { + if (value == 0) + { + // Total removal of the rows. + this.Rows.Clear(); + } + else if (value < this.Rows.Count) + { + // Some rows need to be removed, from the tail of the rows collection + while (value < this.Rows.Count) + { + int currentRowCount = this.Rows.Count; + this.Rows.RemoveAt(currentRowCount - (this.AllowUserToAddRowsInternal ? 2 : 1)); + if (this.Rows.Count >= currentRowCount) + { + // Row removal failed. We stop the loop. + break; + } + } + } + else + { + // Some rows need to be appened. + if (this.Columns.Count == 0) + { + // There are no columns yet, we simply create a single DataGridViewTextBoxColumn. + DataGridViewTextBoxColumn dataGridViewTextBoxColumn = new DataGridViewTextBoxColumn(); + this.Columns.Add(dataGridViewTextBoxColumn); + } + int rowsToAdd = value - this.Rows.Count; + if (rowsToAdd > 0) + { + this.Rows.Add(rowsToAdd); + } + } + } + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowHeadersBorderStyleDescr), + Browsable(true), + DefaultValue(DataGridViewHeaderBorderStyle.Raised) + ] + public DataGridViewHeaderBorderStyle RowHeadersBorderStyle + { + get + { + switch (this.advancedRowHeadersBorderStyle.All) + { + case DataGridViewAdvancedCellBorderStyle.NotSet: + return DataGridViewHeaderBorderStyle.Custom; + + case DataGridViewAdvancedCellBorderStyle.None: + return DataGridViewHeaderBorderStyle.None; + + case DataGridViewAdvancedCellBorderStyle.Single: + return DataGridViewHeaderBorderStyle.Single; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + return DataGridViewHeaderBorderStyle.Sunken; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + return DataGridViewHeaderBorderStyle.Raised; + + default: + return DataGridViewHeaderBorderStyle.Custom; + } + } + set + { + // Sequential enum. Valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewHeaderBorderStyle.Custom, (int)DataGridViewHeaderBorderStyle.None)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewHeaderBorderStyle)); + } + + if (value != this.RowHeadersBorderStyle) + { + if (value == DataGridViewHeaderBorderStyle.Custom) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CustomCellBorderStyleInvalid, "RowHeadersBorderStyle")); + } + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = true; + try + { + switch (value) + { + case DataGridViewHeaderBorderStyle.Single: + this.advancedRowHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.Single; + break; + + case DataGridViewHeaderBorderStyle.Raised: + this.advancedRowHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + break; + + case DataGridViewHeaderBorderStyle.Sunken: + this.advancedRowHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.InsetDouble; + break; + + case DataGridViewHeaderBorderStyle.None: + this.advancedRowHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None; + break; + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange] = false; + } + OnRowHeadersBorderStyleChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowHeadersBorderStyleChangedDescr) + ] + public event EventHandler RowHeadersBorderStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERSBORDERSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERSBORDERSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowHeadersDefaultCellStyleDescr), + AmbientValue(null) + ] + public DataGridViewCellStyle RowHeadersDefaultCellStyle + { + get + { + if (this.rowHeadersDefaultCellStyle == null) + { + this.rowHeadersDefaultCellStyle = this.DefaultRowHeadersDefaultCellStyle; + } + return this.rowHeadersDefaultCellStyle; + } + set + { + DataGridViewCellStyle cs = this.RowHeadersDefaultCellStyle; + cs.RemoveScope(DataGridViewCellStyleScopes.RowHeaders); + this.rowHeadersDefaultCellStyle = value; + if (value != null) + { + this.rowHeadersDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.RowHeaders); + } + DataGridViewCellStyleDifferences dgvcsc = cs.GetDifferencesFrom(this.RowHeadersDefaultCellStyle); + if (dgvcsc != DataGridViewCellStyleDifferences.None) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = (dgvcsc == DataGridViewCellStyleDifferences.AffectPreferredSize); + OnRowHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewRowHeadersDefaultCellStyleChangedDescr) + ] + public event EventHandler RowHeadersDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERSDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERSDEFAULTCELLSTYLECHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dataGridView's row headers are + /// visible. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.DataGridViewRowHeadersVisibleDescr) + ] + public bool RowHeadersVisible + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_rowHeadersVisible]; + } + set + { + if (this.RowHeadersVisible != value) + { + if (!value && + (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.AllHeaders || this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowHeadersCannotBeInvisible)); + } + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.RowHeadersVisible)) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_rowHeadersVisible] = value; + this.layout.RowHeadersVisible = value; + this.displayedBandsInfo.EnsureDirtyState(); + if (!this.AutoSize) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + InvalidateInside(); + OnRowHeadersGlobalAutoSize(value /*expandingRows*/); + } + } + } + } + + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.DataGridView_RowHeadersWidthDescr) + ] + public int RowHeadersWidth + { + get + { + return this.rowHeadersWidth; + } + set + { + if (value < minimumRowHeadersWidth) + { + throw new ArgumentOutOfRangeException("RowHeadersWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "RowHeadersWidth", (value).ToString(CultureInfo.CurrentCulture), (minimumRowHeadersWidth).ToString(CultureInfo.CurrentCulture))); + } + if (value > maxHeadersThickness) + { + throw new ArgumentOutOfRangeException("RowHeadersWidth", SR.GetString(SR.InvalidHighBoundArgumentEx, "RowHeadersWidth", (value).ToString(CultureInfo.CurrentCulture), (maxHeadersThickness).ToString(CultureInfo.CurrentCulture))); + } + if (this.RowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.RowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + this.cachedRowHeadersWidth = value; + } + else if (this.rowHeadersWidth != value) + { + this.RowHeadersWidthInternal = value; + } + } + } + + private int RowHeadersWidthInternal + { + set + { + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.RowHeadersWidth)) + { + Debug.Assert(this.rowHeadersWidth != value); + Debug.Assert(value >= minimumRowHeadersWidth); + this.rowHeadersWidth = value; + if (this.AutoSize) + { + InvalidateInside(); + } + else + { + if (this.layout.RowHeadersVisible) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + InvalidateInside(); + } + } + OnRowHeadersWidthChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewRowHeadersWidthChangedDescr) + ] + public event EventHandler RowHeadersWidthChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERSWIDTHCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERSWIDTHCHANGED, value); + } + } + + private bool ShouldSerializeRowHeadersWidth() + { + return (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) && + defaultRowHeadersWidth != this.RowHeadersWidth; + } + + /// + /// + /// + /// Gets or sets a value that determines the behavior for adjusting the row headers width. + /// + /// + [ + DefaultValue(DataGridViewRowHeadersWidthSizeMode.EnableResizing), + RefreshProperties(RefreshProperties.All), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_RowHeadersWidthSizeModeDescr) + ] + public DataGridViewRowHeadersWidthSizeMode RowHeadersWidthSizeMode + { + get + { + return this.rowHeadersWidthSizeMode; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewRowHeadersWidthSizeMode.EnableResizing, (int)DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewRowHeadersWidthSizeMode)); + } + if (this.rowHeadersWidthSizeMode != value) + { + /*if (value != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + * value != DataGridViewRowHeadersWidthSizeMode.DisableResizing && + * !this.RowHeadersVisible) + { + We intentionally don't throw an error because of designer code spit order. + }*/ + DataGridViewAutoSizeModeEventArgs dgvasme = new DataGridViewAutoSizeModeEventArgs(this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing); + this.rowHeadersWidthSizeMode = value; + OnRowHeadersWidthSizeModeChanged(dgvasme); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowHeadersWidthSizeModeChangedDescr) + ] + public event DataGridViewAutoSizeModeEventHandler RowHeadersWidthSizeModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERSWIDTHSIZEMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERSWIDTHSIZEMODECHANGED, value); + } + } + + /// + [ + Browsable(false) + ] + public DataGridViewRowCollection Rows + { + get + { + if (this.dataGridViewRows == null) + { + this.dataGridViewRows = CreateRowsInstance(); + } + return this.dataGridViewRows; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowsDefaultCellStyleDescr) + ] + public DataGridViewCellStyle RowsDefaultCellStyle + { + get + { + if (this.rowsDefaultCellStyle == null) + { + this.rowsDefaultCellStyle = new DataGridViewCellStyle(); + this.rowsDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.Rows); + } + return this.rowsDefaultCellStyle; + } + set + { + DataGridViewCellStyle cs = this.RowsDefaultCellStyle; + cs.RemoveScope(DataGridViewCellStyleScopes.Rows); + this.rowsDefaultCellStyle = value; + if (value != null) + { + this.rowsDefaultCellStyle.AddScope(this, DataGridViewCellStyleScopes.Rows); + } + DataGridViewCellStyleDifferences dgvcsc = cs.GetDifferencesFrom(this.RowsDefaultCellStyle); + if (dgvcsc != DataGridViewCellStyleDifferences.None) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = (dgvcsc == DataGridViewCellStyleDifferences.AffectPreferredSize); + OnRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewRowsDefaultCellStyleChangedDescr) + ] + public event EventHandler RowsDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWSDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWSDEFAULTCELLSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + Browsable(true), + SRDescription(SR.DataGridView_RowTemplateDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public DataGridViewRow RowTemplate + { + get + { + if (this.rowTemplate == null) + { + this.rowTemplate = new DataGridViewRow(); + } + return this.rowTemplate; + } + set + { + DataGridViewRow dataGridViewRow = value; + if (dataGridViewRow != null) + { + if (dataGridViewRow.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + //if (dataGridViewRow.Selected) + //{ + // throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowTemplateCannotBeSelected)); + //} + } + this.rowTemplate = dataGridViewRow; + } + } + + private bool ShouldSerializeRowTemplate() + { + return this.rowTemplate != null; + } + + internal DataGridViewRow RowTemplateClone + { + get + { + DataGridViewRow rowTemplateClone = (DataGridViewRow) this.RowTemplate.Clone(); + CompleteCellsCollection(rowTemplateClone); + return rowTemplateClone; + } + } + + /// + /// + /// Possible return values are given by the ScrollBars enumeration. + /// + [ + DefaultValue(ScrollBars.Both), + Localizable(true), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_ScrollBarsDescr) + ] + public ScrollBars ScrollBars + { + get + { + return this.scrollBars; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ScrollBars.None, (int)ScrollBars.Both)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ScrollBars)); + } + + if (this.scrollBars != value) + { + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.ScrollBars)) + { + // Before changing the value of this.scrollBars, we scroll to the top-left cell to + // avoid inconsitent state of scrollbars. + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + + if (dataGridViewColumn != null && firstVisibleRowIndex != -1) + { + if (!ScrollIntoView(dataGridViewColumn.Index, firstVisibleRowIndex, false)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + Debug.Assert(this.HorizontalOffset == 0); + Debug.Assert(this.VerticalOffset == 0); + + this.scrollBars = value; + + if (!this.AutoSize) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + Invalidate(); + } + } + } + } + + /// + [ + Browsable(false) + ] + public DataGridViewSelectedCellCollection SelectedCells + { + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops"), // not legitimate + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // consider using generics instead of DataGridViewIntLinkedList + ] + get + { + DataGridViewSelectedCellCollection stcc = new DataGridViewSelectedCellCollection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // Note: If we change the design and decide that SelectAll() should use band selection, + // we need to add those to the selected cells. + stcc.AddCellLinkedList(this.individualSelectedCells); + break; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + foreach (int columnIndex in this.selectedBandIndexes) + { + foreach (DataGridViewRow dataGridViewRow in this.Rows) // unshares all rows! + { + stcc.Add(dataGridViewRow.Cells[columnIndex]); + } + } + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + stcc.AddCellLinkedList(this.individualSelectedCells); + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + foreach (int rowIndex in this.selectedBandIndexes) + { + DataGridViewRow dataGridViewRow = (DataGridViewRow) this.Rows[rowIndex]; // unshares the selected row + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + stcc.Add(dataGridViewCell); + } + } + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + stcc.AddCellLinkedList(this.individualSelectedCells); + } + break; + } + } + return stcc; + } + } + + /// + [ + Browsable(false) + ] + public DataGridViewSelectedColumnCollection SelectedColumns + { + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // consider using generics instead of DataGridViewIntLinkedList + ] + get + { + DataGridViewSelectedColumnCollection strc = new DataGridViewSelectedColumnCollection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + break; + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + foreach (int columnIndex in this.selectedBandIndexes) + { + strc.Add(this.Columns[columnIndex]); + } + break; + } + return strc; + } + } + + /// + [ + Browsable(false), + ] + public DataGridViewSelectedRowCollection SelectedRows + { + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // using specialized DataGridViewIntLinkedList class instead of generics + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // not legitimate + ] + get + { + DataGridViewSelectedRowCollection strc = new DataGridViewSelectedRowCollection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + break; + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + foreach (int rowIndex in this.selectedBandIndexes) + { + strc.Add((DataGridViewRow) this.Rows[rowIndex]); // unshares the selected row + } + break; + } + return strc; + } + } + + /// + [ + Browsable(true), + SRCategory(SR.CatBehavior), + DefaultValue(DataGridViewSelectionMode.RowHeaderSelect), + SRDescription(SR.DataGridView_SelectionModeDescr) + ] + public DataGridViewSelectionMode SelectionMode + { + get + { + return this.selectionMode; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewSelectionMode.CellSelect, (int)DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewSelectionMode)); + } + + if (this.SelectionMode != value) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_initializing] && + (value == DataGridViewSelectionMode.FullColumnSelect || value == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_SelectionModeAndSortModeClash, (value).ToString())); + } + } + } + ClearSelection(); + this.selectionMode = value; + } + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ShowCellErrorsDescr) + ] + public bool ShowCellErrors + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_showCellErrors]; + } + set + { + if (this.ShowCellErrors != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showCellErrors] = value; + + // Put this into OnShowCellErrorsChanged if created. + if (this.IsHandleCreated && !this.DesignMode) + { + if (value && !this.ShowRowErrors && !this.ShowCellToolTips) + { + // the tool tip hasn't yet been activated + // activate it now + this.toolTipControl.Activate(!String.IsNullOrEmpty(this.toolTipCaption)); + } + + if (!value && !this.ShowRowErrors && !this.ShowCellToolTips) + { + // there is no reason to keep the tool tip activated + // deactivate it + this.toolTipCaption = String.Empty; + this.toolTipControl.Activate(false /*activate*/); + } + + if (!value && (this.ShowRowErrors || this.ShowCellToolTips)) + { + // reset the tool tip + this.toolTipControl.Activate(!String.IsNullOrEmpty(this.toolTipCaption)); + } + + // Some autosizing may have to be applied since the potential presence of error icons influences the preferred sizes. + OnGlobalAutoSize(); + } + + if (!this.layout.dirty && !this.DesignMode) + { + this.Invalidate(Rectangle.Union(this.layout.Data, this.layout.ColumnHeaders)); + this.Invalidate(this.layout.TopLeftHeader); + } + } + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ShowCellToolTipsDescr) + ] + public bool ShowCellToolTips + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_showCellToolTips]; + } + set + { + if (this.ShowCellToolTips != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showCellToolTips] = value; + + if (this.IsHandleCreated && !this.DesignMode) + { + if (value && !this.ShowRowErrors && !this.ShowCellErrors) + { + // the tool tip hasn't yet been activated + // activate it now + this.toolTipControl.Activate(!String.IsNullOrEmpty(this.toolTipCaption) /*activate*/); + } + + if (!value && !this.ShowRowErrors && !this.ShowCellErrors) + { + // there is no reason to keep the tool tip activated + // deactivate it + this.toolTipCaption = String.Empty; + this.toolTipControl.Activate(false /*activate*/); + } + + if (!value && (this.ShowRowErrors || this.ShowCellErrors)) + { + bool activate = !String.IsNullOrEmpty(this.toolTipCaption); + Point mouseCoord = System.Windows.Forms.Control.MousePosition; + activate &= this.ClientRectangle.Contains(PointToClient(mouseCoord)); + + // reset the tool tip + this.toolTipControl.Activate(activate); + } + } + + if (!this.layout.dirty && !this.DesignMode) + { + Invalidate(this.layout.Data); + } + } + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ShowEditingIconDescr) + ] + public bool ShowEditingIcon + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_showEditingIcon]; + } + set + { + if (this.ShowEditingIcon != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showEditingIcon] = value; + + // invalidate the row header to pick up the new ShowEditingIcon value + if (this.RowHeadersVisible) + { + if (this.VirtualMode || this.DataSource != null) + { + if (this.IsCurrentRowDirty) + { + Debug.Assert(this.ptCurrentCell.Y >= 0); + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + } + else + { + if (this.IsCurrentCellDirty) { + Debug.Assert(this.ptCurrentCell.Y >= 0); + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + } + } + } + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ShowRowErrorsDescr) + ] + public bool ShowRowErrors + { + get + { + return this.dataGridViewState2[DATAGRIDVIEWSTATE2_showRowErrors]; + } + set + { + if (this.ShowRowErrors != value) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showRowErrors] = value; + + if (this.IsHandleCreated && !this.DesignMode) + { + if (value && !this.ShowCellErrors && !this.ShowCellToolTips) + { + // the tool tip hasn't yet been activated + // activate it now + this.toolTipControl.Activate(!String.IsNullOrEmpty(this.toolTipCaption)); + } + + if (!value && !this.ShowCellErrors && !this.ShowCellToolTips) + { + // there is no reason to keep the tool tip activated + // deactivate it + this.toolTipCaption = String.Empty; + this.toolTipControl.Activate(false /*activate*/); + } + + if (!value && (this.ShowCellErrors || this.ShowCellToolTips)) + { + // reset the tool tip + this.toolTipControl.Activate(!String.IsNullOrEmpty(this.toolTipCaption)); + } + } + + if (!this.layout.dirty && !this.DesignMode) + { + Invalidate(this.layout.RowHeaders); + } + } + } + } + + internal bool SingleHorizontalBorderAdded + { + get + { + return !this.layout.ColumnHeadersVisible && + (this.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single || + this.CellBorderStyle == DataGridViewCellBorderStyle.SingleHorizontal); + } + } + + internal bool SingleVerticalBorderAdded + { + get + { + return !this.layout.RowHeadersVisible && + (this.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single || + this.CellBorderStyle == DataGridViewCellBorderStyle.SingleVertical); + } + } + + /// + [ + Browsable(false) + ] + public DataGridViewColumn SortedColumn + { + get + { + return this.sortedColumn; + } + } + + /// + [ + Browsable(false) + ] + public SortOrder SortOrder + { + get + { + return this.sortOrder; + } + } + + /// + /// + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_StandardTabDescr) + ] + public bool StandardTab + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_standardTab]; + } + set + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_standardTab] != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_standardTab] = value; + //OnStandardTabChanged(EventArgs.Empty); + } + } + } + + internal override bool SupportsUiaProviders + { + get + { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + Bindable(false) + ] + public override string Text + { + get + { + return base.Text; + } + set + { + base.Text = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler TextChanged + { + add + { + base.TextChanged += value; + } + remove + { + base.TextChanged -= value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SuppressMessage("Microsoft.Design", "CA1023:IndexersShouldNotBeMultidimensional") + ] + public DataGridViewCell this[int columnIndex, int rowIndex] + { + get + { + DataGridViewRow row = this.Rows[rowIndex]; + return row.Cells[columnIndex]; + } + set + { + DataGridViewRow row = this.Rows[rowIndex]; + row.Cells[columnIndex] = value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SuppressMessage("Microsoft.Design", "CA1023:IndexersShouldNotBeMultidimensional") + ] + public DataGridViewCell this[string columnName, int rowIndex] + { + get + { + DataGridViewRow row = this.Rows[rowIndex]; + return row.Cells[columnName]; + } + set + { + DataGridViewRow row = this.Rows[rowIndex]; + row.Cells[columnName] = value; + } + } + + private string ToolTipPrivate + { + get + { + return this.toolTipCaption; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewHeaderCell TopLeftHeaderCell + { + get + { + if (this.topLeftHeaderCell == null) + { + this.TopLeftHeaderCell = new DataGridViewTopLeftHeaderCell(); + } + return this.topLeftHeaderCell; + } + set + { + if (this.topLeftHeaderCell != value) + { + if (this.topLeftHeaderCell != null) + { + // Detach existing header cell + this.topLeftHeaderCell.DataGridViewInternal = null; + } + this.topLeftHeaderCell = value; + if (value != null) + { + this.topLeftHeaderCell.DataGridViewInternal = this; + } + if (this.ColumnHeadersVisible && this.RowHeadersVisible) + { + // If headers (rows or columns) are autosized, then this.RowHeadersWidth or this.ColumnHeadersHeight + // must be updated based on new cell preferred size + OnColumnHeadersGlobalAutoSize(); + // In all cases, the top left cell needs to repaint + Invalidate(new Rectangle(this.layout.Inside.X, this.layout.Inside.Y, this.RowHeadersWidth, this.ColumnHeadersHeight)); + } + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public Cursor UserSetCursor + { + get + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + return this.oldCursor; + } + else + { + return this.Cursor; + } + } + } + + internal int VerticalOffset + { + get + { + return this.verticalOffset; + } + set + { + if (value < 0) + { + value = 0; + } + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int fittingTrailingScrollingRowsHeight = ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight); + if (value > this.vertScrollBar.Maximum - fittingTrailingScrollingRowsHeight) + { + value = this.vertScrollBar.Maximum - fittingTrailingScrollingRowsHeight; + } + if (value == this.verticalOffset) + { + return; + } + + int change = value - this.verticalOffset; + if (this.vertScrollBar.Enabled) + { + this.vertScrollBar.Value = value; + } + ScrollRowsByHeight(change); // calculate how many rows need to be scrolled based on 'change' + } + } + + /// + protected ScrollBar VerticalScrollBar + { + get + { + return this.vertScrollBar; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int VerticalScrollingOffset + { + get + { + return this.verticalOffset; + } + } + + private System.Windows.Forms.Timer VertScrollTimer + { + get + { + if (this.vertScrollTimer == null) + { + this.vertScrollTimer = new System.Windows.Forms.Timer(); + this.vertScrollTimer.Tick += new System.EventHandler(VertScrollTimer_Tick); + } + return this.vertScrollTimer; + } + } + + /// + /// + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridViewVirtualModeDescr) + ] + public bool VirtualMode + { + get + { + return this.dataGridViewState1[DATAGRIDVIEWSTATE1_virtualMode]; + } + set + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_virtualMode] != value) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_virtualMode] = value; + InvalidateRowHeights(); + //OnVirtualModeChanged(EventArgs.Empty); + } + } + } + + private bool VisibleCellExists + { + get + { + if (null == this.Columns.GetFirstColumn(DataGridViewElementStates.Visible)) + { + return false; + } + return -1 != this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + } + } + + // Events start here + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridViewAutoSizeColumnModeChangedDescr) + ] + public event DataGridViewAutoSizeColumnModeEventHandler AutoSizeColumnModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWAUTOSIZECOLUMNMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWAUTOSIZECOLUMNMODECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_CancelRowEditDescr) + ] + public event QuestionEventHandler CancelRowEdit + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCANCELROWEDIT, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCANCELROWEDIT, value); + } + } + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_CellBeginEditDescr) + ] + public event DataGridViewCellCancelEventHandler CellBeginEdit + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLBEGINEDIT, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLBEGINEDIT, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellClickDescr) + ] + public event DataGridViewCellEventHandler CellClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellContentClick) + ] + public event DataGridViewCellEventHandler CellContentClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCONTENTCLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCONTENTCLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellContentDoubleClick) + ] + public event DataGridViewCellEventHandler CellContentDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCONTENTDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCONTENTDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_CellContextMenuStripChanged), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event DataGridViewCellEventHandler CellContextMenuStripChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_CellContextMenuStripNeeded), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event DataGridViewCellContextMenuStripNeededEventHandler CellContextMenuStripNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellDoubleClickDescr) + ] + public event DataGridViewCellEventHandler CellDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_CellEndEditDescr) + ] + public event DataGridViewCellEventHandler CellEndEdit + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLENDEDIT, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLENDEDIT, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_CellEnterDescr) + ] + public event DataGridViewCellEventHandler CellEnter + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLENTER, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLENTER, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_CellErrorTextChangedDescr) + ] + public event DataGridViewCellEventHandler CellErrorTextChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLERRORTEXTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLERRORTEXTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_CellErrorTextNeededDescr) + ] + public event DataGridViewCellErrorTextNeededEventHandler CellErrorTextNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLERRORTEXTNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLERRORTEXTNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridView_CellFormattingDescr) + ] + public event DataGridViewCellFormattingEventHandler CellFormatting + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLFORMATTING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLFORMATTING, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_CellLeaveDescr) + ] + public event DataGridViewCellEventHandler CellLeave + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLLEAVE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLLEAVE, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseClickDescr) + ] + public event DataGridViewCellMouseEventHandler CellMouseClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseDoubleClickDescr) + ] + public event DataGridViewCellMouseEventHandler CellMouseDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSEDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSEDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseDownDescr) + ] + public event DataGridViewCellMouseEventHandler CellMouseDown + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSEDOWN, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSEDOWN, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseEnterDescr) + ] + public event DataGridViewCellEventHandler CellMouseEnter + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSEENTER, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSEENTER, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseLeaveDescr) + ] + public event DataGridViewCellEventHandler CellMouseLeave + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSELEAVE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSELEAVE, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseMoveDescr) + ] + public event DataGridViewCellMouseEventHandler CellMouseMove + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSEMOVE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSEMOVE, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_CellMouseUpDescr) + ] + public event DataGridViewCellMouseEventHandler CellMouseUp + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLMOUSEUP, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLMOUSEUP, value); + } + } + + /// + [ + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridView_CellPaintingDescr) + ] + public event DataGridViewCellPaintingEventHandler CellPainting + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLPAINTING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLPAINTING, value); + } + } + + /// + [ + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridView_CellParsingDescr) + ] + public event DataGridViewCellParsingEventHandler CellParsing + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLPARSING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLPARSING, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_CellStateChangedDescr) + ] + public event DataGridViewCellStateChangedEventHandler CellStateChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLSTATECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLSTATECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_CellStyleChangedDescr) + ] + public event DataGridViewCellEventHandler CellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_CellStyleContentChangedDescr) + ] + public event DataGridViewCellStyleContentChangedEventHandler CellStyleContentChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLSTYLECONTENTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLSTYLECONTENTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_CellToolTipTextChangedDescr) + ] + public event DataGridViewCellEventHandler CellToolTipTextChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_CellToolTipTextNeededDescr), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event DataGridViewCellToolTipTextNeededEventHandler CellToolTipTextNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_CellValidatedDescr) + ] + public event DataGridViewCellEventHandler CellValidated + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALIDATED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALIDATED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_CellValidatingDescr) + ] + public event DataGridViewCellValidatingEventHandler CellValidating + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALIDATING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALIDATING, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_CellValueChangedDescr) + ] + public event DataGridViewCellEventHandler CellValueChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_CellValueNeededDescr) + ] + public event DataGridViewCellValueEventHandler CellValueNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUENEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUENEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_CellValuePushedDescr) + ] + public event DataGridViewCellValueEventHandler CellValuePushed + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUEPUSHED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUEPUSHED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_ColumnAddedDescr) + ] + public event DataGridViewColumnEventHandler ColumnAdded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNADDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNADDED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnContextMenuStripChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnContextMenuStripChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNCONTEXTMENUSTRIPCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNCONTEXTMENUSTRIPCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnDataPropertyNameChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnDataPropertyNameChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNDATAPROPERTYNAMECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNDATAPROPERTYNAMECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnDefaultCellStyleChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNDEFAULTCELLSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnDisplayIndexChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnDisplayIndexChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNDISPLAYINDEXCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNDISPLAYINDEXCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_ColumnDividerDoubleClickDescr) + ] + public event DataGridViewColumnDividerDoubleClickEventHandler ColumnDividerDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNDIVIDERDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNDIVIDERDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnDividerWidthChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnDividerWidthChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNDIVIDERWIDTHCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNDIVIDERWIDTHCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_ColumnHeaderMouseClickDescr) + ] + public event DataGridViewCellMouseEventHandler ColumnHeaderMouseClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_ColumnHeaderMouseDoubleClickDescr) + ] + public event DataGridViewCellMouseEventHandler ColumnHeaderMouseDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSEDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSEDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnHeaderCellChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnHeaderCellChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERCELLCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNHEADERCELLCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnMinimumWidthChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnMinimumWidthChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNMINIMUMWIDTHCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNMINIMUMWIDTHCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnNameChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnNameChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNNAMECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNNAMECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_ColumnRemovedDescr) + ] + public event DataGridViewColumnEventHandler ColumnRemoved + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNREMOVED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNREMOVED, value); + } + } + + /*/// + /// + public event EventHandler ColumnsDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNSDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNSDEFAULTCELLSTYLECHANGED, value); + } + }*/ + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridViewColumnSortModeChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnSortModeChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNSORTMODECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNSORTMODECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnStateChangedDescr) + ] + public event DataGridViewColumnStateChangedEventHandler ColumnStateChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNSTATECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNSTATECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_ColumnToolTipTextChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnToolTipTextChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNTOOLTIPTEXTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNTOOLTIPTEXTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_ColumnWidthChangedDescr) + ] + public event DataGridViewColumnEventHandler ColumnWidthChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCOLUMNWIDTHCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCOLUMNWIDTHCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_CurrentCellChangedDescr) + ] + public event EventHandler CurrentCellChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCURRENTCELLCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCURRENTCELLCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_CurrentCellDirtyStateChangedDescr) + ] + public event EventHandler CurrentCellDirtyStateChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWCURRENTCELLDIRTYSTATECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWCURRENTCELLDIRTYSTATECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_DataBindingCompleteDescr) + ] + public event DataGridViewBindingCompleteEventHandler DataBindingComplete + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDATABINDINGCOMPLETE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDATABINDINGCOMPLETE, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_DataErrorDescr) + ] + public event DataGridViewDataErrorEventHandler DataError + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDATAERROR, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDATAERROR, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_DefaultValuesNeededDescr) + ] + public event DataGridViewRowEventHandler DefaultValuesNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWDEFAULTVALUESNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWDEFAULTVALUESNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_EditingControlShowingDescr) + ] + public event DataGridViewEditingControlShowingEventHandler EditingControlShowing + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWEDITINGCONTROLSHOWING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWEDITINGCONTROLSHOWING, value); + } + } + + /* + /// + public event QuestionEventHandler KeepNewRow + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWKEEPNEWROW, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWKEEPNEWROW, value); + } + }*/ + + /*/// + /// + public event EventHandler NewRowDiscarded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWNEWROWDISCARDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWNEWROWDISCARDED, value); + } + }*/ + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_NewRowNeededDescr) + ] + public event DataGridViewRowEventHandler NewRowNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWNEWROWNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWNEWROWNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowContextMenuStripChangedDescr) + ] + public event DataGridViewRowEventHandler RowContextMenuStripChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowContextMenuStripNeededDescr) + ] + public event DataGridViewRowContextMenuStripNeededEventHandler RowContextMenuStripNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowDefaultCellStyleChangedDescr) + ] + public event DataGridViewRowEventHandler RowDefaultCellStyleChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWDEFAULTCELLSTYLECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWDEFAULTCELLSTYLECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowDirtyStateNeededDescr) + ] + public event QuestionEventHandler RowDirtyStateNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWDIRTYSTATENEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWDIRTYSTATENEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_RowDividerDoubleClickDescr) + ] + public event DataGridViewRowDividerDoubleClickEventHandler RowDividerDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWDIVIDERDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWDIVIDERDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowDividerHeightChangedDescr) + ] + public event DataGridViewRowEventHandler RowDividerHeightChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWDIVIDERHEIGHTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWDIVIDERHEIGHTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_RowEnterDescr) + ] + public event DataGridViewCellEventHandler RowEnter + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWENTER, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWENTER, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowErrorTextChangedDescr) + ] + public event DataGridViewRowEventHandler RowErrorTextChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWERRORTEXTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWERRORTEXTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowErrorTextNeededDescr) + ] + public event DataGridViewRowErrorTextNeededEventHandler RowErrorTextNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWERRORTEXTNEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWERRORTEXTNEEDED, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_RowHeaderMouseClickDescr) + ] + public event DataGridViewCellMouseEventHandler RowHeaderMouseClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERMOUSECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERMOUSECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.DataGridView_RowHeaderMouseDoubleClickDescr) + ] + public event DataGridViewCellMouseEventHandler RowHeaderMouseDoubleClick + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERMOUSEDOUBLECLICK, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERMOUSEDOUBLECLICK, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowHeaderCellChangedDescr) + ] + public event DataGridViewRowEventHandler RowHeaderCellChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEADERCELLCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEADERCELLCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowHeightChangedDescr) + ] + public event DataGridViewRowEventHandler RowHeightChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEIGHTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEIGHTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowHeightInfoNeededDescr) + ] + public event DataGridViewRowHeightInfoNeededEventHandler RowHeightInfoNeeded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEIGHTINFONEEDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEIGHTINFONEEDED, value); + } + } + + internal DataGridViewRowHeightInfoNeededEventArgs RowHeightInfoNeededEventArgs + { + get + { + if (this.dgvrhine == null) + { + this.dgvrhine = new DataGridViewRowHeightInfoNeededEventArgs(); + } + return this.dgvrhine; + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowHeightInfoPushedDescr) + ] + public event DataGridViewRowHeightInfoPushedEventHandler RowHeightInfoPushed + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWHEIGHTINFOPUSHED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWHEIGHTINFOPUSHED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_RowLeaveDescr) + ] + public event DataGridViewCellEventHandler RowLeave + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWLEAVE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWLEAVE, value); + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.DataGridView_RowMinimumHeightChangedDescr) + ] + public event DataGridViewRowEventHandler RowMinimumHeightChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWMINIMUMHEIGHTCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWMINIMUMHEIGHTCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridView_RowPostPaintDescr) + ] + public event DataGridViewRowPostPaintEventHandler RowPostPaint + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWPOSTPAINT, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWPOSTPAINT, value); + } + } + + internal DataGridViewRowPostPaintEventArgs RowPostPaintEventArgs + { + get + { + if (this.dgvrpope == null) + { + this.dgvrpope = new DataGridViewRowPostPaintEventArgs(this); + } + return this.dgvrpope; + } + } + + /// + [ + SRCategory(SR.CatDisplay), + SRDescription(SR.DataGridView_RowPrePaintDescr) + ] + public event DataGridViewRowPrePaintEventHandler RowPrePaint + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWPREPAINT, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWPREPAINT, value); + } + } + + internal DataGridViewRowPrePaintEventArgs RowPrePaintEventArgs + { + get + { + if (this.dgvrprpe == null) + { + this.dgvrprpe = new DataGridViewRowPrePaintEventArgs(this); + } + return this.dgvrprpe; + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_RowsAddedDescr) + ] + public event DataGridViewRowsAddedEventHandler RowsAdded + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWSADDED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWSADDED, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_RowsRemovedDescr) + ] + public event DataGridViewRowsRemovedEventHandler RowsRemoved + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWSREMOVED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWSREMOVED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_RowStateChangedDescr) + ] + public event DataGridViewRowStateChangedEventHandler RowStateChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWSTATECHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWSTATECHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatBehavior), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_RowUnsharedDescr) + ] + public event DataGridViewRowEventHandler RowUnshared + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWUNSHARED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWUNSHARED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_RowValidatedDescr) + ] + public event DataGridViewCellEventHandler RowValidated + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWVALIDATED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWVALIDATED, value); + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.DataGridView_RowValidatingDescr) + ] + public event DataGridViewCellCancelEventHandler RowValidating + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWROWVALIDATING, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWROWVALIDATING, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_ScrollDescr) + ] + public event ScrollEventHandler Scroll + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWSCROLL, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWSCROLL, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_SelectionChangedDescr) + ] + public event EventHandler SelectionChanged + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWSELECTIONCHANGED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWSELECTIONCHANGED, value); + } + } + + /// + [ + SRCategory(SR.CatData), + EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.DataGridView_SortCompareDescr) + ] + public event DataGridViewSortCompareEventHandler SortCompare + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWSORTCOMPARE, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWSORTCOMPARE, value); + } + } + + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_SortedDescr) + ] + public event EventHandler Sorted + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWSORTED, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWSORTED, value); + } + } + + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler StyleChanged + { + add + { + base.StyleChanged += value; + } + remove + { + base.StyleChanged -= value; + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_UserAddedRowDescr) + ] + public event DataGridViewRowEventHandler UserAddedRow + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWUSERADDEDROW, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWUSERADDEDROW, value); + } + } + + /*/// + /// + public event DataGridViewRowCancelEventHandler UserAddingRow + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWUSERADDINGROW, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWUSERADDINGROW, value); + } + }*/ + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_UserDeletedRowDescr) + ] + public event DataGridViewRowEventHandler UserDeletedRow + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWUSERDELETEDROW, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWUSERDELETEDROW, value); + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.DataGridView_UserDeletingRowDescr) + ] + public event DataGridViewRowCancelEventHandler UserDeletingRow + { + add + { + this.Events.AddHandler(EVENT_DATAGRIDVIEWUSERDELETINGROW, value); + } + remove + { + this.Events.RemoveHandler(EVENT_DATAGRIDVIEWUSERDELETINGROW, value); + } + } + + //////////////////////// + // // + // ISupportInitialize // + // // + //////////////////////// + [ + SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes") // VSWhidbey 405004 + ] + void ISupportInitialize.BeginInit() + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_initializing]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewBeginInit)); + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_initializing] = true; + } + + [ + SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes") // VSWhidbey 405004 + ] + void ISupportInitialize.EndInit() + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_initializing] = false; + + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.Frozen && + dataGridViewColumn.Visible && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + } + } + + DataGridViewSelectionMode selectionMode = this.SelectionMode; + if (selectionMode == DataGridViewSelectionMode.FullColumnSelect || selectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic) + { + // Resetting SelectionMode to its acceptable default value. We don't want the control to ever end up in an invalid state. + this.SelectionMode = defaultSelectionMode; // DataGridViewSelectionMode.RowHeaderSelect + throw new InvalidOperationException(SR.GetString(SR.DataGridView_SelectionModeReset, + SR.GetString(SR.DataGridView_SelectionModeAndSortModeClash, (selectionMode).ToString()), + (defaultSelectionMode).ToString())); + } + } + } + } + + /* INTERNAL ENUMERATIONS */ + + internal enum DataGridViewHitTestTypeInternal + { + None, + Cell, + ColumnHeader, + RowHeader, + ColumnResizeLeft, + ColumnResizeRight, + RowResizeTop, + RowResizeBottom, + FirstColumnHeaderLeft, + TopLeftHeader, + TopLeftHeaderResizeLeft, + TopLeftHeaderResizeRight, + TopLeftHeaderResizeTop, + TopLeftHeaderResizeBottom, + ColumnHeadersResizeBottom, + ColumnHeadersResizeTop, + RowHeadersResizeRight, + RowHeadersResizeLeft, + ColumnHeaderLeft, + ColumnHeaderRight + } + + internal enum DataGridViewValidateCellInternal + { + Never, + Always, + WhenChanged + } + + private enum DataGridViewMouseEvent + { + Click, + DoubleClick, + MouseClick, + MouseDoubleClick, + MouseDown, + MouseUp, + MouseMove + } + + private struct MouseClickInfo + { + public MouseButtons button; + public long timeStamp; + public int x; + public int y; + public int col; + public int row; + } + + internal class DataGridViewEditingPanel : Panel + { + private DataGridView owningDataGridView; + + public DataGridViewEditingPanel(DataGridView owningDataGridView) + { + this.owningDataGridView = owningDataGridView; + } + + internal override bool SupportsUiaProviders + { + get + { + return AccessibilityImprovements.Level3; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level3) + { + return new DataGridViewEditingPanelAccessibleObject(owningDataGridView, this); + } + + return base.CreateAccessibilityInstance(); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs new file mode 100644 index 000000000..9b6ac911b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs @@ -0,0 +1,599 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Security.Permissions; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + [ + System.Runtime.InteropServices.ComVisible(true) + ] + /// + protected class DataGridViewAccessibleObject : ControlAccessibleObject + { + private int[] runtimeId = null; // Used by UIAutomation + + DataGridView owner; + DataGridViewTopRowAccessibleObject topRowAccessibilityObject = null; + DataGridViewSelectedCellsAccessibleObject selectedCellsAccessibilityObject = null; + + /// + public DataGridViewAccessibleObject(DataGridView owner) + : base(owner) + { + this.owner = owner; + } + + /// + public override string Name + { + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // Don't localize string "DataGridView". + get + { + string name = this.Owner.AccessibleName; + if (!String.IsNullOrEmpty(name)) + { + return name; + } + else + { + // The default name should not be localized. + return "DataGridView"; + } + } + } + + /// + public override AccessibleRole Role + { + get + { + AccessibleRole role = owner.AccessibleRole; + if (role != AccessibleRole.Default) + { + return role; + } + + // the Default AccessibleRole is Table + return AccessibleRole.Table; + } + } + + private AccessibleObject TopRowAccessibilityObject + { + get + { + if (this.topRowAccessibilityObject == null) + { + this.topRowAccessibilityObject = new DataGridViewTopRowAccessibleObject(this.owner); + } + + return this.topRowAccessibilityObject; + } + } + + private AccessibleObject SelectedCellsAccessibilityObject + { + get + { + if (this.selectedCellsAccessibilityObject == null) + { + this.selectedCellsAccessibilityObject = new DataGridViewSelectedCellsAccessibleObject(this.owner); + } + + return this.selectedCellsAccessibilityObject; + } + } + + /// + public override AccessibleObject GetChild(int index) + { + if (this.owner.Columns.Count == 0) + { + System.Diagnostics.Debug.Assert(this.GetChildCount() == 0); + return null; + } + + if (index < 1 && this.owner.ColumnHeadersVisible) + { + return this.TopRowAccessibilityObject; + } + + if (this.owner.ColumnHeadersVisible) + { + index--; + } + + if (index < this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible)) + { + int actualRowIndex = this.owner.Rows.DisplayIndexToRowIndex(index); + return this.owner.Rows[actualRowIndex].AccessibilityObject; + } + + index -= this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible); + + if (this.owner.horizScrollBar.Visible) + { + if (index == 0) + { + return this.owner.horizScrollBar.AccessibilityObject; + } + else + { + index--; + } + } + + if (this.owner.vertScrollBar.Visible) + { + if (index == 0) + { + return this.owner.vertScrollBar.AccessibilityObject; + } + } + + return null; + } + + /// + public override int GetChildCount() + { + if (this.owner.Columns.Count == 0) + { + return 0; + } + + int childCount = this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible); + + // the column header collection Accessible Object + if (this.owner.ColumnHeadersVisible) + { + childCount++; + } + + if (this.owner.horizScrollBar.Visible) + { + childCount++; + } + + if (this.owner.vertScrollBar.Visible) + { + childCount++; + } + + return childCount; + } + + /// + public override AccessibleObject GetFocused() + { + if (this.owner.Focused && this.owner.CurrentCell != null) + { + return this.owner.CurrentCell.AccessibilityObject; + } + else + { + return null; + } + } + + /// + public override AccessibleObject GetSelected() + { + return this.SelectedCellsAccessibilityObject; + } + + /// + public override AccessibleObject HitTest(int x, int y) + { + Point pt = this.owner.PointToClient(new Point(x, y)); + HitTestInfo hti = this.owner.HitTest(pt.X, pt.Y); + + switch (hti.Type) + { + case DataGridViewHitTestType.Cell: + return this.owner.Rows[hti.RowIndex].Cells[hti.ColumnIndex].AccessibilityObject; + case DataGridViewHitTestType.ColumnHeader: + // map the column index to the actual display index + int actualDisplayIndex = this.owner.Columns.ColumnIndexToActualDisplayIndex(hti.ColumnIndex, DataGridViewElementStates.Visible); + if (this.owner.RowHeadersVisible) + { + // increment the childIndex because the first child in the TopRowAccessibleObject is the TopLeftHeaderCellAccObj + return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex + 1); + } + else + { + return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex); + } + case DataGridViewHitTestType.RowHeader: + return this.owner.Rows[hti.RowIndex].AccessibilityObject; + case DataGridViewHitTestType.TopLeftHeader: + return this.owner.TopLeftHeaderCell.AccessibilityObject; + case DataGridViewHitTestType.VerticalScrollBar: + return this.owner.VerticalScrollBar.AccessibilityObject; + case DataGridViewHitTestType.HorizontalScrollBar: + return this.owner.HorizontalScrollBar.AccessibilityObject; + default: + return null; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + switch (navigationDirection) + { + case AccessibleNavigation.FirstChild: + return this.GetChild(0); + case AccessibleNavigation.LastChild: + return this.GetChild(this.GetChildCount() - 1); + default: + return null; + } + } + + /* Microsoft: why is this method defined and not used? + // this method is called when the accessible object needs to be reset + // Example: when the user changes the display index on a column or when the user modifies the column collection + internal void Reset() + { + this.NotifyClients(AccessibleEvents.Reorder); + } + */ + + internal override int[] RuntimeId + { + get + { + if (runtimeId == null) + { + runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.GetHashCode(); + } + + return runtimeId; + } + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyID) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return false; // Only inner cell should be announced as focused by Narrator but not entire DGV. + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return owner.CanFocus; + case NativeMethods.UIA_IsEnabledPropertyId: + return owner.Enabled; + case NativeMethods.UIA_IsControlElementPropertyId: + return true; + case NativeMethods.UIA_IsTablePatternAvailablePropertyId: + case NativeMethods.UIA_IsGridPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_TableControlTypeId; + case NativeMethods.UIA_ItemStatusPropertyId: + // Whether the owner DataGridView can be sorted by some column. + // If so, provide not-sorted/sorted-by item status. + bool canSort = false; + for (int i = 0; i < owner.Columns.Count; i++) + { + if (owner.IsSortable(owner.Columns[i])) + { + canSort = true; + break; + } + } + + if (canSort) + { + switch (owner.SortOrder) + { + case SortOrder.None: + return SR.GetString(SR.NotSortedAccessibleStatus); + case SortOrder.Ascending: + return SR.GetString(SR.DataGridViewSortedAscendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText); + case SortOrder.Descending: + return SR.GetString(SR.DataGridViewSortedDescendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText); + } + } + + break; + } + } + + if (propertyID == NativeMethods.UIA_IsTablePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_TablePatternId); + } + else if (propertyID == NativeMethods.UIA_IsGridPatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_GridPatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_TablePatternId || + patternId == NativeMethods.UIA_GridPatternId) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders() + { + if (!this.owner.RowHeadersVisible) + { + return null; + } + + UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Rows.Count]; + for (int i = 0; i < this.owner.Rows.Count; i++) + { + result[i] = this.owner.Rows[i].HeaderCell.AccessibilityObject; + } + return result; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaders() + { + if (!this.owner.ColumnHeadersVisible) + { + return null; + } + + UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Columns.Count]; + for (int i = 0; i < this.owner.Columns.Count; i++) + { + result[i] = this.owner.Columns[i].HeaderCell.AccessibilityObject; + } + return result; + } + + internal override UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column) + { + if (row >= 0 && row < this.owner.Rows.Count && + column >= 0 && column < this.owner.Columns.Count) + { + return this.owner.Rows[row].Cells[column].AccessibilityObject; + } + + return null; + } + + internal override int RowCount + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.RowCount; + } + } + + internal override int ColumnCount + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.ColumnCount; + } + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return this.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildCount(); + if (childCount > 0) + { + return GetChild(0); + } + break; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildCount(); + if (childCount > 0) + { + int lastChildIndex = childCount - 1; + return GetChild(lastChildIndex); + } + break; + } + + return null; + } + + internal override void SetFocus() + { + if (owner.CanFocus) + { + owner.Focus(); + } + } + + #endregion + + #region IRawElementProviderFragmentRoot Implementation + + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) + { + return this.HitTest((int)x, (int)y); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() + { + return GetFocused(); + } + + #endregion + } + + internal class DataGridViewEditingPanelAccessibleObject : ControlAccessibleObject + { + private DataGridView dataGridView; + private Panel panel; + + public DataGridViewEditingPanelAccessibleObject(DataGridView dataGridView, Panel panel) : base(panel) + { + this.dataGridView = dataGridView; + this.panel = panel; + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return panel.AccessibilityObject.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return dataGridView.AccessibilityObject; + } + } + + internal override int[] RuntimeId + { + get + { + return panel.AccessibilityObject.RuntimeId; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var currentCell = dataGridView.CurrentCell; + if (currentCell != null && dataGridView.IsCurrentCellInEditMode) + { + return currentCell.AccessibilityObject; + } + break; + case UnsafeNativeMethods.NavigateDirection.FirstChild: + case UnsafeNativeMethods.NavigateDirection.LastChild: + return dataGridView.EditingControlAccessibleObject; + } + + return null; + } + + internal override void SetFocus() + { + if (panel.CanFocus) + { + panel.Focus(); + } + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override bool IsPatternSupported(int patternId) + { + return patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId); + } + + internal override object GetPropertyValue(int propertyId) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return SR.GetString(SR.DataGridView_AccEditingPanelAccName); + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_PaneControlTypeId; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return true; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return this.dataGridView.CurrentCell != null; + case NativeMethods.UIA_IsEnabledPropertyId: + return dataGridView.Enabled; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_IsControlElementPropertyId: + case NativeMethods.UIA_IsContentElementPropertyId: + return true; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return this.panel.AccessibilityObject.KeyboardShortcut; + case NativeMethods.UIA_HelpTextPropertyId: + return string.Empty; + case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId: + return true; + case NativeMethods.UIA_ProviderDescriptionPropertyId: + return SR.GetString(SR.DataGridViewEditingPanelUiaProviderDescription); + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs.back b/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs.back new file mode 100644 index 000000000..9b6ac911b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAccessibleObject.cs.back @@ -0,0 +1,599 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Security.Permissions; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + [ + System.Runtime.InteropServices.ComVisible(true) + ] + /// + protected class DataGridViewAccessibleObject : ControlAccessibleObject + { + private int[] runtimeId = null; // Used by UIAutomation + + DataGridView owner; + DataGridViewTopRowAccessibleObject topRowAccessibilityObject = null; + DataGridViewSelectedCellsAccessibleObject selectedCellsAccessibilityObject = null; + + /// + public DataGridViewAccessibleObject(DataGridView owner) + : base(owner) + { + this.owner = owner; + } + + /// + public override string Name + { + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // Don't localize string "DataGridView". + get + { + string name = this.Owner.AccessibleName; + if (!String.IsNullOrEmpty(name)) + { + return name; + } + else + { + // The default name should not be localized. + return "DataGridView"; + } + } + } + + /// + public override AccessibleRole Role + { + get + { + AccessibleRole role = owner.AccessibleRole; + if (role != AccessibleRole.Default) + { + return role; + } + + // the Default AccessibleRole is Table + return AccessibleRole.Table; + } + } + + private AccessibleObject TopRowAccessibilityObject + { + get + { + if (this.topRowAccessibilityObject == null) + { + this.topRowAccessibilityObject = new DataGridViewTopRowAccessibleObject(this.owner); + } + + return this.topRowAccessibilityObject; + } + } + + private AccessibleObject SelectedCellsAccessibilityObject + { + get + { + if (this.selectedCellsAccessibilityObject == null) + { + this.selectedCellsAccessibilityObject = new DataGridViewSelectedCellsAccessibleObject(this.owner); + } + + return this.selectedCellsAccessibilityObject; + } + } + + /// + public override AccessibleObject GetChild(int index) + { + if (this.owner.Columns.Count == 0) + { + System.Diagnostics.Debug.Assert(this.GetChildCount() == 0); + return null; + } + + if (index < 1 && this.owner.ColumnHeadersVisible) + { + return this.TopRowAccessibilityObject; + } + + if (this.owner.ColumnHeadersVisible) + { + index--; + } + + if (index < this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible)) + { + int actualRowIndex = this.owner.Rows.DisplayIndexToRowIndex(index); + return this.owner.Rows[actualRowIndex].AccessibilityObject; + } + + index -= this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible); + + if (this.owner.horizScrollBar.Visible) + { + if (index == 0) + { + return this.owner.horizScrollBar.AccessibilityObject; + } + else + { + index--; + } + } + + if (this.owner.vertScrollBar.Visible) + { + if (index == 0) + { + return this.owner.vertScrollBar.AccessibilityObject; + } + } + + return null; + } + + /// + public override int GetChildCount() + { + if (this.owner.Columns.Count == 0) + { + return 0; + } + + int childCount = this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible); + + // the column header collection Accessible Object + if (this.owner.ColumnHeadersVisible) + { + childCount++; + } + + if (this.owner.horizScrollBar.Visible) + { + childCount++; + } + + if (this.owner.vertScrollBar.Visible) + { + childCount++; + } + + return childCount; + } + + /// + public override AccessibleObject GetFocused() + { + if (this.owner.Focused && this.owner.CurrentCell != null) + { + return this.owner.CurrentCell.AccessibilityObject; + } + else + { + return null; + } + } + + /// + public override AccessibleObject GetSelected() + { + return this.SelectedCellsAccessibilityObject; + } + + /// + public override AccessibleObject HitTest(int x, int y) + { + Point pt = this.owner.PointToClient(new Point(x, y)); + HitTestInfo hti = this.owner.HitTest(pt.X, pt.Y); + + switch (hti.Type) + { + case DataGridViewHitTestType.Cell: + return this.owner.Rows[hti.RowIndex].Cells[hti.ColumnIndex].AccessibilityObject; + case DataGridViewHitTestType.ColumnHeader: + // map the column index to the actual display index + int actualDisplayIndex = this.owner.Columns.ColumnIndexToActualDisplayIndex(hti.ColumnIndex, DataGridViewElementStates.Visible); + if (this.owner.RowHeadersVisible) + { + // increment the childIndex because the first child in the TopRowAccessibleObject is the TopLeftHeaderCellAccObj + return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex + 1); + } + else + { + return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex); + } + case DataGridViewHitTestType.RowHeader: + return this.owner.Rows[hti.RowIndex].AccessibilityObject; + case DataGridViewHitTestType.TopLeftHeader: + return this.owner.TopLeftHeaderCell.AccessibilityObject; + case DataGridViewHitTestType.VerticalScrollBar: + return this.owner.VerticalScrollBar.AccessibilityObject; + case DataGridViewHitTestType.HorizontalScrollBar: + return this.owner.HorizontalScrollBar.AccessibilityObject; + default: + return null; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + switch (navigationDirection) + { + case AccessibleNavigation.FirstChild: + return this.GetChild(0); + case AccessibleNavigation.LastChild: + return this.GetChild(this.GetChildCount() - 1); + default: + return null; + } + } + + /* Microsoft: why is this method defined and not used? + // this method is called when the accessible object needs to be reset + // Example: when the user changes the display index on a column or when the user modifies the column collection + internal void Reset() + { + this.NotifyClients(AccessibleEvents.Reorder); + } + */ + + internal override int[] RuntimeId + { + get + { + if (runtimeId == null) + { + runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.GetHashCode(); + } + + return runtimeId; + } + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyID) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return false; // Only inner cell should be announced as focused by Narrator but not entire DGV. + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return owner.CanFocus; + case NativeMethods.UIA_IsEnabledPropertyId: + return owner.Enabled; + case NativeMethods.UIA_IsControlElementPropertyId: + return true; + case NativeMethods.UIA_IsTablePatternAvailablePropertyId: + case NativeMethods.UIA_IsGridPatternAvailablePropertyId: + return true; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_TableControlTypeId; + case NativeMethods.UIA_ItemStatusPropertyId: + // Whether the owner DataGridView can be sorted by some column. + // If so, provide not-sorted/sorted-by item status. + bool canSort = false; + for (int i = 0; i < owner.Columns.Count; i++) + { + if (owner.IsSortable(owner.Columns[i])) + { + canSort = true; + break; + } + } + + if (canSort) + { + switch (owner.SortOrder) + { + case SortOrder.None: + return SR.GetString(SR.NotSortedAccessibleStatus); + case SortOrder.Ascending: + return SR.GetString(SR.DataGridViewSortedAscendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText); + case SortOrder.Descending: + return SR.GetString(SR.DataGridViewSortedDescendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText); + } + } + + break; + } + } + + if (propertyID == NativeMethods.UIA_IsTablePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_TablePatternId); + } + else if (propertyID == NativeMethods.UIA_IsGridPatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_GridPatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_TablePatternId || + patternId == NativeMethods.UIA_GridPatternId) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders() + { + if (!this.owner.RowHeadersVisible) + { + return null; + } + + UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Rows.Count]; + for (int i = 0; i < this.owner.Rows.Count; i++) + { + result[i] = this.owner.Rows[i].HeaderCell.AccessibilityObject; + } + return result; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaders() + { + if (!this.owner.ColumnHeadersVisible) + { + return null; + } + + UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Columns.Count]; + for (int i = 0; i < this.owner.Columns.Count; i++) + { + result[i] = this.owner.Columns[i].HeaderCell.AccessibilityObject; + } + return result; + } + + internal override UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column) + { + if (row >= 0 && row < this.owner.Rows.Count && + column >= 0 && column < this.owner.Columns.Count) + { + return this.owner.Rows[row].Cells[column].AccessibilityObject; + } + + return null; + } + + internal override int RowCount + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.RowCount; + } + } + + internal override int ColumnCount + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.ColumnCount; + } + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return this.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildCount(); + if (childCount > 0) + { + return GetChild(0); + } + break; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildCount(); + if (childCount > 0) + { + int lastChildIndex = childCount - 1; + return GetChild(lastChildIndex); + } + break; + } + + return null; + } + + internal override void SetFocus() + { + if (owner.CanFocus) + { + owner.Focus(); + } + } + + #endregion + + #region IRawElementProviderFragmentRoot Implementation + + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) + { + return this.HitTest((int)x, (int)y); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() + { + return GetFocused(); + } + + #endregion + } + + internal class DataGridViewEditingPanelAccessibleObject : ControlAccessibleObject + { + private DataGridView dataGridView; + private Panel panel; + + public DataGridViewEditingPanelAccessibleObject(DataGridView dataGridView, Panel panel) : base(panel) + { + this.dataGridView = dataGridView; + this.panel = panel; + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return panel.AccessibilityObject.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return dataGridView.AccessibilityObject; + } + } + + internal override int[] RuntimeId + { + get + { + return panel.AccessibilityObject.RuntimeId; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var currentCell = dataGridView.CurrentCell; + if (currentCell != null && dataGridView.IsCurrentCellInEditMode) + { + return currentCell.AccessibilityObject; + } + break; + case UnsafeNativeMethods.NavigateDirection.FirstChild: + case UnsafeNativeMethods.NavigateDirection.LastChild: + return dataGridView.EditingControlAccessibleObject; + } + + return null; + } + + internal override void SetFocus() + { + if (panel.CanFocus) + { + panel.Focus(); + } + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override bool IsPatternSupported(int patternId) + { + return patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId); + } + + internal override object GetPropertyValue(int propertyId) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return SR.GetString(SR.DataGridView_AccEditingPanelAccName); + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_PaneControlTypeId; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return true; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return this.dataGridView.CurrentCell != null; + case NativeMethods.UIA_IsEnabledPropertyId: + return dataGridView.Enabled; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_IsControlElementPropertyId: + case NativeMethods.UIA_IsContentElementPropertyId: + return true; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return this.panel.AccessibilityObject.KeyboardShortcut; + case NativeMethods.UIA_HelpTextPropertyId: + return string.Empty; + case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId: + return true; + case NativeMethods.UIA_ProviderDescriptionPropertyId: + return SR.GetString(SR.DataGridViewEditingPanelUiaProviderDescription); + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedBorderStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedBorderStyle.cs new file mode 100644 index 000000000..992b5dcee --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedBorderStyle.cs @@ -0,0 +1,355 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + public sealed class DataGridViewAdvancedBorderStyle : ICloneable + { + private DataGridView owner; + private bool all = true; + private DataGridViewAdvancedCellBorderStyle banned1, banned2, banned3; + private DataGridViewAdvancedCellBorderStyle top = DataGridViewAdvancedCellBorderStyle.None; + private DataGridViewAdvancedCellBorderStyle left = DataGridViewAdvancedCellBorderStyle.None; + private DataGridViewAdvancedCellBorderStyle right = DataGridViewAdvancedCellBorderStyle.None; + private DataGridViewAdvancedCellBorderStyle bottom = DataGridViewAdvancedCellBorderStyle.None; + + /// + public DataGridViewAdvancedBorderStyle() : this(null, + DataGridViewAdvancedCellBorderStyle.NotSet, + DataGridViewAdvancedCellBorderStyle.NotSet, + DataGridViewAdvancedCellBorderStyle.NotSet) + { + } + + internal DataGridViewAdvancedBorderStyle(DataGridView owner) : this(owner, + DataGridViewAdvancedCellBorderStyle.NotSet, + DataGridViewAdvancedCellBorderStyle.NotSet, + DataGridViewAdvancedCellBorderStyle.NotSet) + { + } + + /// + /// Creates a new DataGridViewAdvancedBorderStyle. The specified owner will + /// be notified when the values are changed. + /// + internal DataGridViewAdvancedBorderStyle(DataGridView owner, + DataGridViewAdvancedCellBorderStyle banned1, + DataGridViewAdvancedCellBorderStyle banned2, + DataGridViewAdvancedCellBorderStyle banned3) + { + this.owner = owner; + this.banned1 = banned1; + this.banned2 = banned2; + this.banned3 = banned3; + } + + /// + public DataGridViewAdvancedCellBorderStyle All + { + get + { + return this.all ? this.top : DataGridViewAdvancedCellBorderStyle.NotSet; + } + set + { + + // Sequential enum. Valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewAdvancedCellBorderStyle.NotSet, (int)DataGridViewAdvancedCellBorderStyle.OutsetPartial)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAdvancedCellBorderStyle)); + } + if (value == DataGridViewAdvancedCellBorderStyle.NotSet || + value == this.banned1 || + value == this.banned2 || + value == this.banned3) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "All")); + } + if (!this.all || this.top != value) + { + this.all = true; + this.top = this.left = this.right = this.bottom = value; + if (this.owner != null) + { + this.owner.OnAdvancedBorderStyleChanged(this); + } + } + } + } + + /// + public DataGridViewAdvancedCellBorderStyle Bottom + { + get + { + if (this.all) + { + return this.top; + } + return this.bottom; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewAdvancedCellBorderStyle.NotSet, (int)DataGridViewAdvancedCellBorderStyle.OutsetPartial)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAdvancedCellBorderStyle)); + } + if (value == DataGridViewAdvancedCellBorderStyle.NotSet) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Bottom")); + } + this.BottomInternal = value; + } + } + + internal DataGridViewAdvancedCellBorderStyle BottomInternal + { + set + { + //Debug.Assert(Enum.IsDefined(typeof(DataGridViewAdvancedCellBorderStyle), value)); + //Debug.Assert(value != DataGridViewAdvancedCellBorderStyle.NotSet); + if ((this.all && this.top != value) || (!this.all && this.bottom != value)) + { + if (this.all) + { + if (this.right == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.right = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + this.all = false; + this.bottom = value; + if (this.owner != null) + { + this.owner.OnAdvancedBorderStyleChanged(this); + } + } + } + } + + /// + public DataGridViewAdvancedCellBorderStyle Left + { + get + { + if (this.all) + { + return this.top; + } + return this.left; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewAdvancedCellBorderStyle.NotSet, (int)DataGridViewAdvancedCellBorderStyle.OutsetPartial)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAdvancedCellBorderStyle)); + } + if (value == DataGridViewAdvancedCellBorderStyle.NotSet) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Left")); + } + this.LeftInternal = value; + } + } + + internal DataGridViewAdvancedCellBorderStyle LeftInternal + { + set + { + //Debug.Assert(Enum.IsDefined(typeof(DataGridViewAdvancedCellBorderStyle), value)); + //Debug.Assert(value != DataGridViewAdvancedCellBorderStyle.NotSet); + if ((this.all && this.top != value) || (!this.all && this.left != value)) + { + if ((this.owner != null && this.owner.RightToLeftInternal) && + (value == DataGridViewAdvancedCellBorderStyle.InsetDouble || value == DataGridViewAdvancedCellBorderStyle.OutsetDouble)) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Left")); + } + if (this.all) + { + if (this.right == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.right = DataGridViewAdvancedCellBorderStyle.Outset; + } + if (this.bottom == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.bottom = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + this.all = false; + this.left = value; + if (this.owner != null) + { + this.owner.OnAdvancedBorderStyleChanged(this); + } + } + } + } + + /// + public DataGridViewAdvancedCellBorderStyle Right + { + get + { + if (this.all) + { + return this.top; + } + return this.right; + } + set + { + + // Sequential enum. Valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewAdvancedCellBorderStyle.NotSet, (int)DataGridViewAdvancedCellBorderStyle.OutsetPartial)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAdvancedCellBorderStyle)); + } + if (value == DataGridViewAdvancedCellBorderStyle.NotSet) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Right")); + } + this.RightInternal = value; + } + } + + internal DataGridViewAdvancedCellBorderStyle RightInternal + { + set + { + //Debug.Assert(Enum.IsDefined(typeof(DataGridViewAdvancedCellBorderStyle), value)); + //Debug.Assert(value != DataGridViewAdvancedCellBorderStyle.NotSet); + if ((this.all && this.top != value) || (!this.all && this.right != value)) + { + if ((this.owner != null && !this.owner.RightToLeftInternal) && + (value == DataGridViewAdvancedCellBorderStyle.InsetDouble || value == DataGridViewAdvancedCellBorderStyle.OutsetDouble)) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Right")); + } + if (this.all) + { + if (this.bottom == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.bottom = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + this.all = false; + this.right = value; + if (this.owner != null) + { + this.owner.OnAdvancedBorderStyleChanged(this); + } + } + } + } + + /// + public DataGridViewAdvancedCellBorderStyle Top + { + get + { + return this.top; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewAdvancedCellBorderStyle.NotSet, (int)DataGridViewAdvancedCellBorderStyle.OutsetPartial)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAdvancedCellBorderStyle)); + } + if (value == DataGridViewAdvancedCellBorderStyle.NotSet) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_AdvancedCellBorderStyleInvalid, "Top")); + } + this.TopInternal = value; + } + } + + internal DataGridViewAdvancedCellBorderStyle TopInternal + { + set + { + //Debug.Assert(Enum.IsDefined(typeof(DataGridViewAdvancedCellBorderStyle), value)); + //Debug.Assert(value != DataGridViewAdvancedCellBorderStyle.NotSet); + if ((this.all && this.top != value) || (!this.all && this.top != value)) + { + if (this.all) + { + if (this.right == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.right = DataGridViewAdvancedCellBorderStyle.Outset; + } + if (this.bottom == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + this.bottom = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + this.all = false; + this.top = value; + if (this.owner != null) + { + this.owner.OnAdvancedBorderStyleChanged(this); + } + } + } + } + + /// + /// + public override bool Equals(object other) + { + DataGridViewAdvancedBorderStyle dgvabsOther = other as DataGridViewAdvancedBorderStyle; + + if (dgvabsOther == null) + { + return false; + } + + return dgvabsOther.all == this.all && + dgvabsOther.top == this.top && + dgvabsOther.left == this.left && + dgvabsOther.bottom == this.bottom && + dgvabsOther.right == this.right; + } + + /// + /// + public override int GetHashCode() + { + return WindowsFormsUtils.GetCombinedHashCodes((int) this.top, + (int) this.left, + (int) this.bottom, + (int) this.right); + } + + /// + /// + public override string ToString() + { + return "DataGridViewAdvancedBorderStyle { All=" + this.All.ToString() + ", Left=" + this.Left.ToString() + ", Right=" + this.Right.ToString() + ", Top=" + this.Top.ToString() + ", Bottom=" + this.Bottom.ToString() + " }"; + } + + /// + /// + object ICloneable.Clone() + { + DataGridViewAdvancedBorderStyle dgvabs = new DataGridViewAdvancedBorderStyle(this.owner, this.banned1, this.banned2, this.banned3); + dgvabs.all = this.all; + dgvabs.top = this.top; + dgvabs.right = this.right; + dgvabs.bottom = this.bottom; + dgvabs.left = this.left; + return dgvabs; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedCellBorderStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedCellBorderStyle.cs new file mode 100644 index 000000000..c33b07ff5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAdvancedCellBorderStyle.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + public enum DataGridViewAdvancedCellBorderStyle + { + /// + NotSet = 0, + + /// + None = 1, + + /// + Single = 2, + + /// + Inset = 3, + + /// + InsetDouble = 4, + + /// + Outset = 5, + + /// + OutsetDouble = 6, + + /// + OutsetPartial = 7 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnModeEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnModeEventArgs.cs new file mode 100644 index 000000000..ba9e5ac11 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnModeEventArgs.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewAutoSizeColumnModeEventArgs : EventArgs + { + private DataGridViewAutoSizeColumnMode previousMode; + private DataGridViewColumn dataGridViewColumn; + + /// + public DataGridViewAutoSizeColumnModeEventArgs(DataGridViewColumn dataGridViewColumn, DataGridViewAutoSizeColumnMode previousMode) + { + Debug.Assert(dataGridViewColumn != null); + this.dataGridViewColumn = dataGridViewColumn; + this.previousMode = previousMode; + } + + /// + public DataGridViewColumn Column + { + get + { + return this.dataGridViewColumn; + } + } + + /// + public DataGridViewAutoSizeColumnMode PreviousMode + { + get + { + return this.previousMode; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnsModeEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnsModeEventArgs.cs new file mode 100644 index 000000000..aed1ede8c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeColumnsModeEventArgs.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + /// + public class DataGridViewAutoSizeColumnsModeEventArgs : EventArgs + { + private DataGridViewAutoSizeColumnMode[] previousModes; + + /// + public DataGridViewAutoSizeColumnsModeEventArgs(DataGridViewAutoSizeColumnMode[] previousModes) + { + this.previousModes = previousModes; + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays") // Returning a collection would be overkill. + ] + public DataGridViewAutoSizeColumnMode[] PreviousModes + { + get + { + return this.previousModes; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeEnums.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeEnums.cs new file mode 100644 index 000000000..7f2afb758 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeEnums.cs @@ -0,0 +1,170 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewColumnHeadersHeightSizeMode + { + /// + EnableResizing = 0, + + /// + DisableResizing, + + /// + AutoSize + } + + /// + public enum DataGridViewRowHeadersWidthSizeMode + { + /// + EnableResizing = 0, + + /// + DisableResizing, + + /// + AutoSizeToAllHeaders, + + /// + AutoSizeToDisplayedHeaders, + + /// + AutoSizeToFirstHeader + } + + [Flags] + internal enum DataGridViewAutoSizeColumnCriteriaInternal + { + NotSet = 0x00, + None = 0x01, + Header = 0x02, + AllRows = 0x04, + DisplayedRows = 0x08, + Fill = 0x10 + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue"), // Intentionally has no 0 value because NotSet is used in DataGridViewAutoSizeColumnMode. + SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags") // values are not combinable + ] + public enum DataGridViewAutoSizeColumnsMode + { + /// + AllCells = DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows, + + /// + AllCellsExceptHeader = DataGridViewAutoSizeColumnCriteriaInternal.AllRows, + + /// + DisplayedCells = DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + + /// + DisplayedCellsExceptHeader = DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + + /// + None = DataGridViewAutoSizeColumnCriteriaInternal.None, + + /// + ColumnHeader = DataGridViewAutoSizeColumnCriteriaInternal.Header, + + /// + Fill = DataGridViewAutoSizeColumnCriteriaInternal.Fill + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags") // values are not combinable + ] + public enum DataGridViewAutoSizeColumnMode + { + /// + NotSet = DataGridViewAutoSizeColumnCriteriaInternal.NotSet, + + /// + None = DataGridViewAutoSizeColumnCriteriaInternal.None, + + /// + AllCells = DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows, + + /// + AllCellsExceptHeader = DataGridViewAutoSizeColumnCriteriaInternal.AllRows, + + /// + DisplayedCells = DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + + /// + DisplayedCellsExceptHeader = DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + + /// + ColumnHeader = DataGridViewAutoSizeColumnCriteriaInternal.Header, + + /// + Fill = DataGridViewAutoSizeColumnCriteriaInternal.Fill + } + + + [Flags] + internal enum DataGridViewAutoSizeRowsModeInternal + { + None = 0x00, + Header = 0x01, + AllColumns = 0x02, + AllRows = 0x04, + DisplayedRows = 0x08 + } + + /// + public enum DataGridViewAutoSizeRowsMode + { + /// + AllCells = DataGridViewAutoSizeRowsModeInternal.Header | DataGridViewAutoSizeRowsModeInternal.AllColumns | DataGridViewAutoSizeRowsModeInternal.AllRows, + + /// + AllCellsExceptHeaders = DataGridViewAutoSizeRowsModeInternal.AllColumns | DataGridViewAutoSizeRowsModeInternal.AllRows, + + /// + AllHeaders = DataGridViewAutoSizeRowsModeInternal.Header | DataGridViewAutoSizeRowsModeInternal.AllRows, + + /// + DisplayedCells = DataGridViewAutoSizeRowsModeInternal.Header | DataGridViewAutoSizeRowsModeInternal.AllColumns | DataGridViewAutoSizeRowsModeInternal.DisplayedRows, + + /// + DisplayedCellsExceptHeaders = DataGridViewAutoSizeRowsModeInternal.AllColumns | DataGridViewAutoSizeRowsModeInternal.DisplayedRows, + + /// + DisplayedHeaders = DataGridViewAutoSizeRowsModeInternal.Header | DataGridViewAutoSizeRowsModeInternal.DisplayedRows, + + /// + None = DataGridViewAutoSizeRowsModeInternal.None, + } + + [Flags] + internal enum DataGridViewAutoSizeRowCriteriaInternal + { + Header = 0x01, + AllColumns = 0x02 + } + + /// + [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] // Intentionally has no 0 value because values are combinations of DataGridViewAutoSizeRowCriteriaInternal. + public enum DataGridViewAutoSizeRowMode + { + /// + AllCells = DataGridViewAutoSizeRowCriteriaInternal.Header | DataGridViewAutoSizeRowCriteriaInternal.AllColumns, + + /// + AllCellsExceptHeader = DataGridViewAutoSizeRowCriteriaInternal.AllColumns, + + /// + RowHeader = DataGridViewAutoSizeRowCriteriaInternal.Header + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeModeEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeModeEventArgs.cs new file mode 100644 index 000000000..9560073bd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewAutoSizeModeEventArgs.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + /// + /// [To be supplied.] + /// + public class DataGridViewAutoSizeModeEventArgs : EventArgs + { + private bool previousModeAutoSized; + + /// + public DataGridViewAutoSizeModeEventArgs(bool previousModeAutoSized) + { + this.previousModeAutoSized = previousModeAutoSized; + } + + /// + /// + /// [To be supplied.] + /// + public bool PreviousModeAutoSized + { + get + { + return this.previousModeAutoSized; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewBand.cs b/WindowsForms/Managed/System/WinForms/DataGridViewBand.cs new file mode 100644 index 000000000..2060c328f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewBand.cs @@ -0,0 +1,1059 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Text; + using System; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// Identifies a band or column in the dataGridView. + /// + public class DataGridViewBand : DataGridViewElement, ICloneable, IDisposable + { + private static readonly int PropContextMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropDefaultCellStyle = PropertyStore.CreateKey(); + private static readonly int PropDefaultHeaderCellType = PropertyStore.CreateKey(); + private static readonly int PropDividerThickness = PropertyStore.CreateKey(); + private static readonly int PropHeaderCell = PropertyStore.CreateKey(); + private static readonly int PropUserData = PropertyStore.CreateKey(); + + internal const int minBandThickness = 2; + internal const int maxBandThickness = 65536; + + private PropertyStore propertyStore; // Contains all properties that are not always set. + private int thickness, cachedThickness; + private int minimumThickness; + private int bandIndex; + internal bool bandIsRow; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + internal DataGridViewBand() + { + this.propertyStore = new PropertyStore(); + this.bandIndex = -1; + } + + /// + ~DataGridViewBand() + { + Dispose(false); + } + + internal int CachedThickness + { + get + { + return this.cachedThickness; + } + set + { + this.cachedThickness = value; + } + } + + /// + [ + DefaultValue(null) + ] + public virtual ContextMenuStrip ContextMenuStrip + { + get + { + if (this.bandIsRow) + { + return ((DataGridViewRow) this).GetContextMenuStrip(this.Index); + } + return this.ContextMenuStripInternal; + } + set + { + this.ContextMenuStripInternal = value; + } + } + + internal ContextMenuStrip ContextMenuStripInternal + { + get + { + return (ContextMenuStrip)this.Properties.GetObject(PropContextMenuStrip); + } + set + { + ContextMenuStrip oldValue = (ContextMenuStrip)this.Properties.GetObject(PropContextMenuStrip); + if (oldValue != value) + { + EventHandler disposedHandler = new EventHandler(DetachContextMenuStrip); + if (oldValue != null) + { + oldValue.Disposed -= disposedHandler; + } + this.Properties.SetObject(PropContextMenuStrip, value); + if (value != null) + { + value.Disposed += disposedHandler; + } + if (this.DataGridView != null) + { + this.DataGridView.OnBandContextMenuStripChanged(this); + } + } + } + } + + /// + [ + Browsable(false) + ] + public virtual DataGridViewCellStyle DefaultCellStyle + { + get + { + DataGridViewCellStyle dgvcs = (DataGridViewCellStyle)this.Properties.GetObject(PropDefaultCellStyle); + if (dgvcs == null) + { + dgvcs = new DataGridViewCellStyle(); + dgvcs.AddScope(this.DataGridView, + this.bandIsRow ? DataGridViewCellStyleScopes.Row : DataGridViewCellStyleScopes.Column); + this.Properties.SetObject(PropDefaultCellStyle, dgvcs); + } + return dgvcs; + } + set + { + DataGridViewCellStyle dgvcs = null; + if (this.HasDefaultCellStyle) + { + dgvcs = this.DefaultCellStyle; + dgvcs.RemoveScope(this.bandIsRow ? DataGridViewCellStyleScopes.Row : DataGridViewCellStyleScopes.Column); + } + if (value != null || this.Properties.ContainsObject(PropDefaultCellStyle)) + { + if (value != null) + { + value.AddScope(this.DataGridView, + this.bandIsRow ? DataGridViewCellStyleScopes.Row : DataGridViewCellStyleScopes.Column); + } + this.Properties.SetObject(PropDefaultCellStyle, value); + } + if (((dgvcs != null && value == null) || + (dgvcs == null && value != null) || + (dgvcs != null && value != null && !dgvcs.Equals(this.DefaultCellStyle))) && this.DataGridView != null) + { + this.DataGridView.OnBandDefaultCellStyleChanged(this); + } + } + } + + /// + [ + Browsable(false) + ] + public Type DefaultHeaderCellType + { + get + { + Type dhct = (Type)this.Properties.GetObject(PropDefaultHeaderCellType); + if (dhct == null) + { + if (this.bandIsRow) + { + dhct = typeof(System.Windows.Forms.DataGridViewRowHeaderCell); + } + else + { + dhct = typeof(System.Windows.Forms.DataGridViewColumnHeaderCell); + } + } + return dhct; + } + set + { + if (value != null || this.Properties.ContainsObject(PropDefaultHeaderCellType)) + { + if (Type.GetType("System.Windows.Forms.DataGridViewHeaderCell").IsAssignableFrom(value)) + { + this.Properties.SetObject(PropDefaultHeaderCellType, value); + } + else + { + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongType, "DefaultHeaderCellType", "System.Windows.Forms.DataGridViewHeaderCell")); + } + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual bool Displayed + { + get + { + Debug.Assert(!this.bandIsRow); + bool displayed = (this.State & DataGridViewElementStates.Displayed) != 0; + // Only attached and visible columns can be displayed. + // Debug.Assert(!displayed || (this.DataGridView != null && this.DataGridView.Visible && this.Visible)); + return displayed; + } + } + + internal bool DisplayedInternal + { + set + { + Debug.Assert(value != this.Displayed); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.Displayed; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.Displayed; + } + if (this.DataGridView != null) + { + OnStateChanged(DataGridViewElementStates.Displayed); + } + } + } + + internal int DividerThickness + { + get + { + bool found; + int dividerThickness = this.Properties.GetInteger(PropDividerThickness, out found); + return found ? dividerThickness : 0; + } + set + { + if (value < 0) + { + if (this.bandIsRow) + { + throw new ArgumentOutOfRangeException("DividerHeight", SR.GetString(SR.InvalidLowBoundArgumentEx, "DividerHeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + else + { + throw new ArgumentOutOfRangeException("DividerWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "DividerWidth", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + } + if (value > maxBandThickness) + { + if (this.bandIsRow) + { + throw new ArgumentOutOfRangeException("DividerHeight", SR.GetString(SR.InvalidHighBoundArgumentEx, "DividerHeight", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture))); + } + else + { + throw new ArgumentOutOfRangeException("DividerWidth", SR.GetString(SR.InvalidHighBoundArgumentEx, "DividerWidth", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture))); + } + } + if (value != this.DividerThickness) + { + this.Properties.SetInteger(PropDividerThickness, (int)value); + if (this.DataGridView != null) + { + this.DataGridView.OnBandDividerThicknessChanged(this); + } + } + } + } + + /// + [ + DefaultValue(false), + ] + public virtual bool Frozen + { + get + { + Debug.Assert(!this.bandIsRow); + return (this.State & DataGridViewElementStates.Frozen) != 0; + } + set + { + if (((this.State & DataGridViewElementStates.Frozen) != 0) != value) + { + OnStateChanging(DataGridViewElementStates.Frozen); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.Frozen; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.Frozen; + } + OnStateChanged(DataGridViewElementStates.Frozen); + } + } + } + + /// + [ + Browsable(false) + ] + public bool HasDefaultCellStyle + { + get + { + return this.Properties.ContainsObject(PropDefaultCellStyle) && this.Properties.GetObject(PropDefaultCellStyle) != null; + } + } + + internal bool HasDefaultHeaderCellType + { + get + { + return this.Properties.ContainsObject(PropDefaultHeaderCellType) && this.Properties.GetObject(PropDefaultHeaderCellType) != null; + } + } + + internal bool HasHeaderCell + { + get + { + return this.Properties.ContainsObject(PropHeaderCell) && this.Properties.GetObject(PropHeaderCell) != null; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + protected DataGridViewHeaderCell HeaderCellCore + { + get + { + DataGridViewHeaderCell headerCell = (DataGridViewHeaderCell)this.Properties.GetObject(PropHeaderCell); + if (headerCell == null) + { + Type cellType = this.DefaultHeaderCellType; + + headerCell = (DataGridViewHeaderCell) SecurityUtils.SecureCreateInstance(cellType); + headerCell.DataGridViewInternal = this.DataGridView; + if (this.bandIsRow) + { + headerCell.OwningRowInternal = (DataGridViewRow)this; // may be a shared row + this.Properties.SetObject(PropHeaderCell, headerCell); + } + else + { + DataGridViewColumn dataGridViewColumn = this as DataGridViewColumn; + headerCell.OwningColumnInternal = dataGridViewColumn; + // Set the headerCell in the property store before setting the SortOrder. + // vsWhidbey 411787. + this.Properties.SetObject(PropHeaderCell, headerCell); + if (this.DataGridView != null && this.DataGridView.SortedColumn == dataGridViewColumn) + { + DataGridViewColumnHeaderCell dataGridViewColumnHeaderCell = headerCell as DataGridViewColumnHeaderCell; + Debug.Assert(dataGridViewColumnHeaderCell != null); + dataGridViewColumnHeaderCell.SortGlyphDirection = this.DataGridView.SortOrder; + } + } + } + return headerCell; + } + set + { + DataGridViewHeaderCell headerCell = (DataGridViewHeaderCell)this.Properties.GetObject(PropHeaderCell); + if (value != null || this.Properties.ContainsObject(PropHeaderCell)) + { + if (headerCell != null) + { + headerCell.DataGridViewInternal = null; + if (this.bandIsRow) + { + headerCell.OwningRowInternal = null; + } + else + { + headerCell.OwningColumnInternal = null; + ((DataGridViewColumnHeaderCell)headerCell).SortGlyphDirectionInternal = SortOrder.None; + } + } + + if (value != null) + { + if (this.bandIsRow) + { + if (!(value is DataGridViewRowHeaderCell)) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongType, "HeaderCell", "System.Windows.Forms.DataGridViewRowHeaderCell")); + } + // A HeaderCell can only be used by one band. + if (value.OwningRow != null) + { + value.OwningRow.HeaderCell = null; + } + Debug.Assert(value.OwningRow == null); + value.OwningRowInternal = (DataGridViewRow)this; // may be a shared row + } + else + { + DataGridViewColumnHeaderCell dataGridViewColumnHeaderCell = value as DataGridViewColumnHeaderCell; + if (dataGridViewColumnHeaderCell == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongType, "HeaderCell", "System.Windows.Forms.DataGridViewColumnHeaderCell")); + } + // A HeaderCell can only be used by one band. + if (value.OwningColumn != null) + { + value.OwningColumn.HeaderCell = null; + } + Debug.Assert(dataGridViewColumnHeaderCell.SortGlyphDirection == SortOrder.None); + Debug.Assert(value.OwningColumn == null); + value.OwningColumnInternal = (DataGridViewColumn)this; + } + Debug.Assert(value.DataGridView == null); + value.DataGridViewInternal = this.DataGridView; + } + + this.Properties.SetObject(PropHeaderCell, value); + } + if (((value == null && headerCell != null) || (value != null && headerCell == null) || (value != null && headerCell != null && !headerCell.Equals(value))) && this.DataGridView != null) + { + this.DataGridView.OnBandHeaderCellChanged(this); + } + } + } + + /// + /// + /// + /// + [ + Browsable(false) + ] + public int Index + { + get + { + return this.bandIndex; + } + } + + internal int IndexInternal + { + set + { + this.bandIndex = value; + } + } + + /// + [ + Browsable(false) + ] + public virtual DataGridViewCellStyle InheritedStyle + { + get + { + return null; + } + } + + /// + protected bool IsRow + { + get + { + return this.bandIsRow; + } + } + + internal int MinimumThickness + { + get + { + if (this.bandIsRow && this.bandIndex > -1) + { + int height, minimumHeight; + GetHeightInfo(this.bandIndex, out height, out minimumHeight); + return minimumHeight; + } + return this.minimumThickness; + } + set + { + if (this.minimumThickness != value) + { + if (value < minBandThickness) + { + if (this.bandIsRow) + { + throw new ArgumentOutOfRangeException("MinimumHeight", value, SR.GetString(SR.DataGridViewBand_MinimumHeightSmallerThanOne, (DataGridViewBand.minBandThickness).ToString(CultureInfo.CurrentCulture))); + } + else + { + throw new ArgumentOutOfRangeException("MinimumWidth", value, SR.GetString(SR.DataGridViewBand_MinimumWidthSmallerThanOne, (DataGridViewBand.minBandThickness).ToString(CultureInfo.CurrentCulture))); + } + } + if (this.Thickness < value) + { + // Force the new minimum width on potential auto fill column. + if (this.DataGridView != null && !this.bandIsRow) + { + this.DataGridView.OnColumnMinimumWidthChanging((DataGridViewColumn)this, value); + } + this.Thickness = value; + } + this.minimumThickness = value; + if (this.DataGridView != null) + { + this.DataGridView.OnBandMinimumThicknessChanged(this); + } + } + } + } + + internal PropertyStore Properties + { + get + { + return this.propertyStore; + } + } + + /// + [ + DefaultValue(false) + ] + public virtual bool ReadOnly + { + get + { + Debug.Assert(!this.bandIsRow); + return ((this.State & DataGridViewElementStates.ReadOnly) != 0 || + (this.DataGridView != null && this.DataGridView.ReadOnly)); + } + set + { + if (this.DataGridView != null) + { + if (this.DataGridView.ReadOnly) + { + // if (!value): Trying to make a band read-write when the whole grid is read-only. + // if (value): Trying to make a band read-only when the whole grid is read-only. + // Ignoring the request and returning. + return; + } + + // this may trigger a call to set_ReadOnlyInternal + if (this.bandIsRow) + { + if (this.bandIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "ReadOnly")); + } + OnStateChanging(DataGridViewElementStates.ReadOnly); + this.DataGridView.SetReadOnlyRowCore(this.bandIndex, value); + } + else + { + Debug.Assert(this.bandIndex >= 0); + OnStateChanging(DataGridViewElementStates.ReadOnly); + this.DataGridView.SetReadOnlyColumnCore(this.bandIndex, value); + } + } + else + { + if (((this.State & DataGridViewElementStates.ReadOnly) != 0) != value) + { + if (value) + { + if (this.bandIsRow) + { + foreach (DataGridViewCell dataGridViewCell in ((DataGridViewRow) this).Cells) + { + if (dataGridViewCell.ReadOnly) + { + dataGridViewCell.ReadOnlyInternal = false; + } + } + } + this.StateInternal = this.State | DataGridViewElementStates.ReadOnly; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.ReadOnly; + } + } + } + } + } + + internal bool ReadOnlyInternal + { + set + { + Debug.Assert(value != this.ReadOnly); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.ReadOnly; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.ReadOnly; + } + Debug.Assert(this.DataGridView != null); + OnStateChanged(DataGridViewElementStates.ReadOnly); + } + } + + /// + [ + Browsable(true) + ] + public virtual DataGridViewTriState Resizable + { + get + { + Debug.Assert(!this.bandIsRow); + if ((this.State & DataGridViewElementStates.ResizableSet) != 0) + { + return ((this.State & DataGridViewElementStates.Resizable) != 0) ? DataGridViewTriState.True : DataGridViewTriState.False; + } + if (this.DataGridView != null) + { + return this.DataGridView.AllowUserToResizeColumns ? DataGridViewTriState.True : DataGridViewTriState.False; + } + else + { + return DataGridViewTriState.NotSet; + } + } + set + { + DataGridViewTriState oldResizable = this.Resizable; + if (value == DataGridViewTriState.NotSet) + { + this.StateInternal = this.State & ~DataGridViewElementStates.ResizableSet; + } + else + { + this.StateInternal = this.State | DataGridViewElementStates.ResizableSet; + if (((this.State & DataGridViewElementStates.Resizable) != 0) != (value == DataGridViewTriState.True)) + { + if (value == DataGridViewTriState.True) + { + this.StateInternal = this.State | DataGridViewElementStates.Resizable; + } + else + { + Debug.Assert(value == DataGridViewTriState.False, "TriState only supports NotSet, True, False"); + this.StateInternal = this.State & ~DataGridViewElementStates.Resizable; + } + } + } + if (oldResizable != this.Resizable) + { + OnStateChanged(DataGridViewElementStates.Resizable); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual bool Selected + { + get + { + Debug.Assert(!this.bandIsRow); + return (this.State & DataGridViewElementStates.Selected) != 0; + } + set + { + if (this.DataGridView != null) + { + // this may trigger a call to set_SelectedInternal + if (this.bandIsRow) + { + if (this.bandIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "Selected")); + } + if (this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || this.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + this.DataGridView.SetSelectedRowCoreInternal(this.bandIndex, value); + } + } + else + { + Debug.Assert(this.bandIndex >= 0); + if (this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || this.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + this.DataGridView.SetSelectedColumnCoreInternal(this.bandIndex, value); + } + } + } + else if (value) + { + // We do not allow the selection of a band before it gets added to the dataGridView. + throw new InvalidOperationException(SR.GetString(SR.DataGridViewBand_CannotSelect)); + } + } + } + + internal bool SelectedInternal + { + set + { + Debug.Assert(value != this.Selected); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.Selected; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.Selected; + } + if (this.DataGridView != null) + { + OnStateChanged(DataGridViewElementStates.Selected); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public object Tag + { + get + { + return Properties.GetObject(PropUserData); + } + set + { + if (value != null || this.Properties.ContainsObject(PropUserData)) + { + Properties.SetObject(PropUserData, value); + } + } + } + + internal int Thickness + { + get + { + if (this.bandIsRow && this.bandIndex > -1) + { + int height, minimumHeight; + GetHeightInfo(this.bandIndex, out height, out minimumHeight); + return height; + } + return this.thickness; + } + set + { + int minimumThickness = this.MinimumThickness; + if (value < minimumThickness) + { + value = minimumThickness; + } + if (value > maxBandThickness) + { + if (this.bandIsRow) + { + throw new ArgumentOutOfRangeException("Height", SR.GetString(SR.InvalidHighBoundArgumentEx, "Height", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture))); + } + else + { + throw new ArgumentOutOfRangeException("Width", SR.GetString(SR.InvalidHighBoundArgumentEx, "Width", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture))); + } + } + bool setThickness = true; + if (this.bandIsRow) + { + if (this.DataGridView != null && this.DataGridView.AutoSizeRowsMode != DataGridViewAutoSizeRowsMode.None) + { + this.cachedThickness = value; + setThickness = false; + } + } + else + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this; + DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = dataGridViewColumn.InheritedAutoSizeMode; + if (inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.Fill && + inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.None && + inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.NotSet) + { + this.cachedThickness = value; + setThickness = false; + } + else if (inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && this.DataGridView != null) + { + if (dataGridViewColumn.Visible) + { + IntPtr handle = this.DataGridView.Handle; + this.DataGridView.AdjustFillingColumn(dataGridViewColumn, value); + setThickness = false; + } + } + } + + if (setThickness && this.thickness != value) + { + if (this.DataGridView != null) + { + this.DataGridView.OnBandThicknessChanging(); + } + this.ThicknessInternal = value; + } + } + } + + internal int ThicknessInternal + { + get + { + return this.thickness; + } + set + { + Debug.Assert(this.thickness != value); + Debug.Assert(value >= this.minimumThickness); + Debug.Assert(value <= maxBandThickness); + + this.thickness = value; + if (this.DataGridView != null) + { + this.DataGridView.OnBandThicknessChanged(this); + } + } + } + + /// + [ + DefaultValue(true), + ] + public virtual bool Visible + { + get + { + Debug.Assert(!this.bandIsRow); + return (this.State & DataGridViewElementStates.Visible) != 0; + } + set + { + if (((this.State & DataGridViewElementStates.Visible) != 0) != value) + { + if (this.DataGridView != null && + this.bandIsRow && + this.DataGridView.NewRowIndex != -1 && + this.DataGridView.NewRowIndex == this.bandIndex && + !value) + { + // the 'new' row cannot be made invisble. + throw new InvalidOperationException(SR.GetString(SR.DataGridViewBand_NewRowCannotBeInvisible)); + } + OnStateChanging(DataGridViewElementStates.Visible); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.Visible; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.Visible; + } + OnStateChanged(DataGridViewElementStates.Visible); + } + } + } + + /// + public virtual object Clone() + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + DataGridViewBand dataGridViewBand = (DataGridViewBand) System.Activator.CreateInstance(this.GetType()); + if (dataGridViewBand != null) + { + CloneInternal(dataGridViewBand); + } + return dataGridViewBand; + } + + internal void CloneInternal(DataGridViewBand dataGridViewBand) + { + dataGridViewBand.propertyStore = new PropertyStore(); + dataGridViewBand.bandIndex = -1; + dataGridViewBand.bandIsRow = this.bandIsRow; + if (!this.bandIsRow || this.bandIndex >= 0 || this.DataGridView == null) + { + dataGridViewBand.StateInternal = this.State & ~(DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed); + } + dataGridViewBand.thickness = this.Thickness; + dataGridViewBand.MinimumThickness = this.MinimumThickness; + dataGridViewBand.cachedThickness = this.CachedThickness; + dataGridViewBand.DividerThickness = this.DividerThickness; + dataGridViewBand.Tag = this.Tag; + if (this.HasDefaultCellStyle) + { + dataGridViewBand.DefaultCellStyle = new DataGridViewCellStyle(this.DefaultCellStyle); + } + if (this.HasDefaultHeaderCellType) + { + dataGridViewBand.DefaultHeaderCellType = this.DefaultHeaderCellType; + } + if (this.ContextMenuStripInternal != null) + { + dataGridViewBand.ContextMenuStrip = this.ContextMenuStripInternal.Clone(); + } + } + + private void DetachContextMenuStrip(object sender, EventArgs e) + { + this.ContextMenuStripInternal = null; + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ContextMenuStrip contextMenuStrip = (ContextMenuStrip)this.ContextMenuStripInternal; + if (contextMenuStrip != null) + { + contextMenuStrip.Disposed -= new EventHandler(DetachContextMenuStrip); + } + } + } + + internal void GetHeightInfo(int rowIndex, out int height, out int minimumHeight) + { + Debug.Assert(this.bandIsRow); + if (this.DataGridView != null && + (this.DataGridView.VirtualMode || this.DataGridView.DataSource != null) && + this.DataGridView.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + Debug.Assert(rowIndex > -1); + DataGridViewRowHeightInfoNeededEventArgs dgvrhine = this.DataGridView.OnRowHeightInfoNeeded(rowIndex, this.thickness, this.minimumThickness); + height = dgvrhine.Height; + minimumHeight = dgvrhine.MinimumHeight; + return; + } + height = this.thickness; + minimumHeight = this.minimumThickness; + } + + internal void OnStateChanged(DataGridViewElementStates elementState) + { + if (this.DataGridView != null) + { + // maybe move this code into OnDataGridViewElementStateChanged + if (this.bandIsRow) + { + // we could be smarter about what needs to be invalidated. + this.DataGridView.Rows.InvalidateCachedRowCount(elementState); + this.DataGridView.Rows.InvalidateCachedRowsHeight(elementState); + if (this.bandIndex != -1) + { + this.DataGridView.OnDataGridViewElementStateChanged(this, -1, elementState); + } + } + else + { + // we could be smarter about what needs to be invalidated. + this.DataGridView.Columns.InvalidateCachedColumnCount(elementState); + this.DataGridView.Columns.InvalidateCachedColumnsWidth(elementState); + this.DataGridView.OnDataGridViewElementStateChanged(this, -1, elementState); + } + } + } + + private void OnStateChanging(DataGridViewElementStates elementState) + { + if (this.DataGridView != null) + { + if (this.bandIsRow) + { + if (this.bandIndex != -1) + { + this.DataGridView.OnDataGridViewElementStateChanging(this, -1, elementState); + } + } + else + { + this.DataGridView.OnDataGridViewElementStateChanging(this, -1, elementState); + } + } + } + + /// + protected override void OnDataGridViewChanged() + { + if (this.HasDefaultCellStyle) + { + if (this.DataGridView == null) + { + this.DefaultCellStyle.RemoveScope(this.bandIsRow ? DataGridViewCellStyleScopes.Row : DataGridViewCellStyleScopes.Column); + } + else + { + this.DefaultCellStyle.AddScope(this.DataGridView, + this.bandIsRow ? DataGridViewCellStyleScopes.Row : DataGridViewCellStyleScopes.Column); + } + } + base.OnDataGridViewChanged(); + } + + private bool ShouldSerializeDefaultHeaderCellType() + { + Type dhct = (Type)this.Properties.GetObject(PropDefaultHeaderCellType); + return dhct != null; + } + + // internal because DataGridViewColumn needs to access it + internal bool ShouldSerializeResizable() + { + return (this.State & DataGridViewElementStates.ResizableSet) != 0; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(36); + sb.Append("DataGridViewBand { Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewBindingCompleteEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewBindingCompleteEventArgs.cs new file mode 100644 index 000000000..629c429d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewBindingCompleteEventArgs.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.ComponentModel; + + /// + public class DataGridViewBindingCompleteEventArgs : EventArgs + { + private ListChangedType listChangedType; + + /// + public DataGridViewBindingCompleteEventArgs(ListChangedType listChangedType) + { + this.listChangedType = listChangedType; + } + + /// + public ListChangedType ListChangedType + { + get + { + return this.listChangedType; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewButtonCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewButtonCell.cs new file mode 100644 index 000000000..60b9712a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewButtonCell.cs @@ -0,0 +1,1165 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.VisualStyles; + using System.Security.Permissions; + using System.Windows.Forms.ButtonInternal; + using System.Globalization; + + /// + /// + /// Identifies a button cell in the dataGridView. + /// + public class DataGridViewButtonCell : DataGridViewCell + { + private static readonly int PropButtonCellFlatStyle = PropertyStore.CreateKey(); + private static readonly int PropButtonCellState = PropertyStore.CreateKey(); + private static readonly int PropButtonCellUseColumnTextForButtonValue = PropertyStore.CreateKey(); + private static readonly VisualStyleElement ButtonElement = VisualStyleElement.Button.PushButton.Normal; + + private const byte DATAGRIDVIEWBUTTONCELL_themeMargin = 100; // used to calculate the margins required for XP theming rendering + private const byte DATAGRIDVIEWBUTTONCELL_horizontalTextMargin = 2; + private const byte DATAGRIDVIEWBUTTONCELL_verticalTextMargin = 1; + private const byte DATAGRIDVIEWBUTTONCELL_textPadding = 5; + + private static Rectangle rectThemeMargins = new Rectangle(-1, -1, 0, 0); + private static bool mouseInContentBounds = false; + + private static Type defaultFormattedValueType = typeof(System.String); + private static Type defaultValueType = typeof(System.Object); + private static Type cellType = typeof(DataGridViewButtonCell); + + /// + public DataGridViewButtonCell() + { + } + + private ButtonState ButtonState + { + get + { + bool found; + int buttonState = this.Properties.GetInteger(PropButtonCellState, out found); + if (found) + { + return (ButtonState)buttonState; + } + return ButtonState.Normal; + } + set + { + // ButtonState.Pushed is used for mouse interaction + // ButtonState.Checked is used for keyboard interaction + Debug.Assert((value & ~(ButtonState.Normal | ButtonState.Pushed | ButtonState.Checked)) == 0); + if (this.ButtonState != value) + { + this.Properties.SetInteger(PropButtonCellState, (int)value); + } + } + } + + /// + public override Type EditType + { + get + { + // Buttons can't switch to edit mode + return null; + } + } + + /// + [ + DefaultValue(FlatStyle.Standard) + ] + public FlatStyle FlatStyle + { + get + { + bool found; + int flatStyle = this.Properties.GetInteger(PropButtonCellFlatStyle, out found); + if (found) + { + return (FlatStyle)flatStyle; + } + return FlatStyle.Standard; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropButtonCellFlatStyle, (int)value); + OnCommonChange(); + } + } + } + + internal FlatStyle FlatStyleInternal + { + set + { + Debug.Assert(value >= FlatStyle.Flat && value <= FlatStyle.System); + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropButtonCellFlatStyle, (int)value); + } + } + } + + /// + public override Type FormattedValueType + { + get + { + // we return string for the formatted type + return defaultFormattedValueType; + } + } + + /// + [DefaultValue(false)] + public bool UseColumnTextForButtonValue + { + get + { + bool found; + int useColumnTextForButtonValue = this.Properties.GetInteger(PropButtonCellUseColumnTextForButtonValue, out found); + if (found) + { + return useColumnTextForButtonValue == 0 ? false : true; + } + return false; + } + set + { + if (value != this.UseColumnTextForButtonValue) + { + this.Properties.SetInteger(PropButtonCellUseColumnTextForButtonValue, value ? 1 : 0); + OnCommonChange(); + } + } + } + + internal bool UseColumnTextForButtonValueInternal + { + set + { + if (value != this.UseColumnTextForButtonValue) + { + this.Properties.SetInteger(PropButtonCellUseColumnTextForButtonValue, value ? 1 : 0); + } + } + } + + /// + public override Type ValueType + { + get + { + Type valueType = base.ValueType; + if (valueType != null) + { + return valueType; + } + return defaultValueType; + } + } + + /// + public override object Clone() + { + DataGridViewButtonCell dataGridViewCell; + Type thisType = this.GetType(); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewButtonCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewButtonCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.FlatStyleInternal = this.FlatStyle; + dataGridViewCell.UseColumnTextForButtonValueInternal = this.UseColumnTextForButtonValue; + return dataGridViewCell; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewButtonCellAccessibleObject(this); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle contentBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // contentBounds is independent of formattedValue + null /*errorText*/, // contentBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + object value = GetValue(rowIndex); + Rectangle contentBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting), + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(contentBoundsDebug.Equals(contentBounds)); +#endif + + return contentBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errorIconBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // errorIconBounds is independent of formattedValue + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + object value = GetValue(rowIndex); + Rectangle errorIconBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting), + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(errorIconBoundsDebug.Equals(errorIconBounds)); +#endif + + return errorIconBounds; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Size preferredSize; + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + int marginWidths, marginHeights; + string formattedString = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string; + if (string.IsNullOrEmpty(formattedString)) + { + formattedString = " "; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + + // Adding space for text padding. + if (this.DataGridView.ApplyVisualStylesToInnerCells) + { + Rectangle rectThemeMargins = DataGridViewButtonCell.GetThemeMargins(graphics); + marginWidths = rectThemeMargins.X + rectThemeMargins.Width; + marginHeights = rectThemeMargins.Y + rectThemeMargins.Height; + } + else + { + // Hardcoding 5 for the button borders for now. + marginWidths = marginHeights = DATAGRIDVIEWBUTTONCELL_textPadding; + } + + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + if (cellStyle.WrapMode == DataGridViewTriState.True && formattedString.Length > 1 && + constraintSize.Height - borderAndPaddingHeights - marginHeights - 2 * DATAGRIDVIEWBUTTONCELL_verticalTextMargin > 0) + { + preferredSize = new Size(DataGridViewCell.MeasureTextWidth(graphics, + formattedString, + cellStyle.Font, + constraintSize.Height - borderAndPaddingHeights - marginHeights - 2 * DATAGRIDVIEWBUTTONCELL_verticalTextMargin, + flags), + 0); + } + else + { + preferredSize = new Size(DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags).Width, + 0); + } + break; + } + case DataGridViewFreeDimension.Height: + { + if (cellStyle.WrapMode == DataGridViewTriState.True && formattedString.Length > 1 && + constraintSize.Width - borderAndPaddingWidths - marginWidths - 2 * DATAGRIDVIEWBUTTONCELL_horizontalTextMargin > 0) + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextHeight(graphics, + formattedString, + cellStyle.Font, + constraintSize.Width - borderAndPaddingWidths - marginWidths - 2 * DATAGRIDVIEWBUTTONCELL_horizontalTextMargin, + flags)); + } + else + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextSize(graphics, + formattedString, + cellStyle.Font, + flags).Height); + } + break; + } + default: + { + if (cellStyle.WrapMode == DataGridViewTriState.True && formattedString.Length > 1) + { + preferredSize = DataGridViewCell.MeasureTextPreferredSize(graphics, formattedString, cellStyle.Font, 5.0F, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags); + } + break; + } + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += borderAndPaddingWidths + marginWidths + 2 * DATAGRIDVIEWBUTTONCELL_horizontalTextMargin; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Width = Math.Max(preferredSize.Width, borderAndPaddingWidths + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height += borderAndPaddingHeights + marginHeights + 2 * DATAGRIDVIEWBUTTONCELL_verticalTextMargin; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Height = Math.Max(preferredSize.Height, borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + private static Rectangle GetThemeMargins(Graphics g) + { + if (rectThemeMargins.X == -1) + { + Rectangle rectCell = new Rectangle(0, 0, DATAGRIDVIEWBUTTONCELL_themeMargin, DATAGRIDVIEWBUTTONCELL_themeMargin); + Rectangle rectContent = DataGridViewButtonCellRenderer.DataGridViewButtonRenderer.GetBackgroundContentRectangle(g, rectCell); + rectThemeMargins.X = rectContent.X; + rectThemeMargins.Y = rectContent.Y; + rectThemeMargins.Width = DATAGRIDVIEWBUTTONCELL_themeMargin - rectContent.Right; + rectThemeMargins.Height = DATAGRIDVIEWBUTTONCELL_themeMargin - rectContent.Bottom; + } + return rectThemeMargins; + } + + /// + protected override object GetValue(int rowIndex) + { + if (this.UseColumnTextForButtonValue && + this.DataGridView != null && + this.DataGridView.NewRowIndex != rowIndex && + this.OwningColumn != null && + this.OwningColumn is DataGridViewButtonColumn) + { + return ((DataGridViewButtonColumn) this.OwningColumn).Text; + } + return base.GetValue(rowIndex); + } + + /// + protected override bool KeyDownUnsharesRow(KeyEventArgs e, int rowIndex) + { + return e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift; + } + + /// + protected override bool KeyUpUnsharesRow(KeyEventArgs e, int rowIndex) + { + return e.KeyCode == Keys.Space; + } + + /// + protected override bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left; + } + + /// + protected override bool MouseEnterUnsharesRow(int rowIndex) + { + return this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && rowIndex == this.DataGridView.MouseDownCellAddress.Y; + } + + /// + protected override bool MouseLeaveUnsharesRow(int rowIndex) + { + return (this.ButtonState & ButtonState.Pushed) != 0; + } + + /// + protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left; + } + + /// + protected override void OnKeyDown(KeyEventArgs e, int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift) + { + UpdateButtonState(this.ButtonState | ButtonState.Checked, rowIndex); + e.Handled = true; + } + } + + /// + protected override void OnKeyUp(KeyEventArgs e, int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (e.KeyCode == Keys.Space) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Checked, rowIndex); + if (!e.Alt && !e.Control && !e.Shift) + { + RaiseCellClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + if (this.DataGridView != null && + this.ColumnIndex < this.DataGridView.Columns.Count && + rowIndex < this.DataGridView.Rows.Count) + { + RaiseCellContentClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + } + e.Handled = true; + } + } + } + + /// + protected override void OnLeave(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.RowIndex >= 0); // Cell is not in a shared row. + UpdateButtonState(ButtonState.Normal, rowIndex); + } + } + + /// + protected override void OnMouseDown(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left && mouseInContentBounds) + { + Debug.Assert(this.DataGridView.CellMouseDownInContentBounds); + UpdateButtonState(this.ButtonState | ButtonState.Pushed, e.RowIndex); + } + } + + /// + protected override void OnMouseLeave(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + + if (mouseInContentBounds) + { + mouseInContentBounds = false; + if (this.ColumnIndex >= 0 && + rowIndex >= 0 && + (this.DataGridView.ApplyVisualStylesToInnerCells || this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup)) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + if ((this.ButtonState & ButtonState.Pushed) != 0 && + this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + rowIndex == this.DataGridView.MouseDownCellAddress.Y) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, rowIndex); + } + } + + /// + protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + + bool oldMouseInContentBounds = mouseInContentBounds; + mouseInContentBounds = GetContentBounds(e.RowIndex).Contains(e.X, e.Y); + if (oldMouseInContentBounds != mouseInContentBounds) + { + if (this.DataGridView.ApplyVisualStylesToInnerCells || this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + + if (e.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + e.RowIndex == this.DataGridView.MouseDownCellAddress.Y && + Control.MouseButtons == MouseButtons.Left) + { + if ((this.ButtonState & ButtonState.Pushed) == 0 && + mouseInContentBounds && + this.DataGridView.CellMouseDownInContentBounds) + { + UpdateButtonState(this.ButtonState | ButtonState.Pushed, e.RowIndex); + } + else if ((this.ButtonState & ButtonState.Pushed) != 0 && !mouseInContentBounds) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, e.RowIndex); + } + } + } + + base.OnMouseMove(e); + } + + /// + protected override void OnMouseUp(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, e.RowIndex); + } + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + elementState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + bool cellSelected = (elementState & DataGridViewElementStates.Selected) != 0; + bool cellCurrent = (ptCurrentCell.X == this.ColumnIndex && ptCurrentCell.Y == rowIndex); + + Rectangle resultBounds; + string formattedString = formattedValue as string; + + SolidBrush backBrush = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + SolidBrush foreBrush = this.DataGridView.GetCachedBrush(cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor); + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + if (valBounds.Height > 0 && valBounds.Width > 0) + { + if (paint && DataGridViewCell.PaintBackground(paintParts) && backBrush.Color.A == 255) + { + g.FillRectangle(backBrush, valBounds); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + Rectangle errorBounds = valBounds; + + if (valBounds.Height > 0 && valBounds.Width > 0 && (paint || computeContentBounds)) + { + if (this.FlatStyle == FlatStyle.Standard || this.FlatStyle == FlatStyle.System) + { + if (this.DataGridView.ApplyVisualStylesToInnerCells) + { + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + VisualStyles.PushButtonState pbState = VisualStyles.PushButtonState.Normal; + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0) + { + pbState = VisualStyles.PushButtonState.Pressed; + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds) + { + pbState = VisualStyles.PushButtonState.Hot; + } + if (DataGridViewCell.PaintFocus(paintParts) && + cellCurrent && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused) + { + pbState |= VisualStyles.PushButtonState.Default; + } + DataGridViewButtonCellRenderer.DrawButton(g, valBounds, (int)pbState); + } + resultBounds = valBounds; + valBounds = DataGridViewButtonCellRenderer.DataGridViewButtonRenderer.GetBackgroundContentRectangle(g, valBounds); + } + else + { + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + ControlPaint.DrawBorder(g, valBounds, SystemColors.Control, + (this.ButtonState == ButtonState.Normal) ? ButtonBorderStyle.Outset : ButtonBorderStyle.Inset); + } + resultBounds = valBounds; + valBounds.Inflate(-SystemInformation.Border3DSize.Width, -SystemInformation.Border3DSize.Height); + } + } + else if (this.FlatStyle == FlatStyle.Flat) + { + // ButtonBase::PaintFlatDown and ButtonBase::PaintFlatUp paint the border in the same way + valBounds.Inflate(-1, -1); + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + ButtonInternal.ButtonBaseAdapter.DrawDefaultBorder(g, valBounds, foreBrush.Color, true /*isDefault == true*/); + + if (backBrush.Color.A == 255) + { + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0) + { + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintFlatRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + + IntPtr hdc = g.GetHdc(); + try { + using( WindowsGraphics wg = WindowsGraphics.FromHdc(hdc)) { + + System.Windows.Forms.Internal.WindowsBrush windowsBrush; + if (colors.options.highContrast) + { + windowsBrush = new System.Windows.Forms.Internal.WindowsSolidBrush(wg.DeviceContext, colors.buttonShadow); + } + else + { + windowsBrush = new System.Windows.Forms.Internal.WindowsSolidBrush(wg.DeviceContext, colors.lowHighlight); + } + try + { + ButtonInternal.ButtonBaseAdapter.PaintButtonBackground(wg, valBounds, windowsBrush); + } + finally + { + windowsBrush.Dispose(); + } + } + } + finally { + g.ReleaseHdc(); + } + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds) + { + IntPtr hdc = g.GetHdc(); + try { + using( WindowsGraphics wg = WindowsGraphics.FromHdc(hdc)) { + Color mouseOverBackColor = SystemColors.ControlDark; + using (System.Windows.Forms.Internal.WindowsBrush windowBrush = new System.Windows.Forms.Internal.WindowsSolidBrush(wg.DeviceContext, mouseOverBackColor)) + { + ButtonInternal.ButtonBaseAdapter.PaintButtonBackground(wg, valBounds, windowBrush); + } + } + } + finally { + g.ReleaseHdc(); + } + } + } + } + resultBounds = valBounds; + } + else + { + Debug.Assert(this.FlatStyle == FlatStyle.Popup, "FlatStyle.Popup is the last flat style"); + valBounds.Inflate(-1, -1); + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0) + { + // paint down + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + ButtonBaseAdapter.DrawDefaultBorder(g, + valBounds, + colors.options.highContrast ? colors.windowText : colors.windowFrame, + true /*isDefault*/); + ControlPaint.DrawBorder(g, + valBounds, + colors.options.highContrast ? colors.windowText : colors.buttonShadow, + ButtonBorderStyle.Solid); + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds) + { + // paint over + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + ButtonBaseAdapter.DrawDefaultBorder(g, + valBounds, + colors.options.highContrast ? colors.windowText : colors.buttonShadow, + false /*isDefault*/); + ButtonBaseAdapter.Draw3DLiteBorder(g, valBounds, colors, true); + } + else + { + // paint up + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + ButtonBaseAdapter.DrawDefaultBorder(g, valBounds, colors.options.highContrast ? colors.windowText : colors.buttonShadow, false /*isDefault*/); + ButtonBaseAdapter.DrawFlatBorder(g, valBounds, colors.options.highContrast ? colors.windowText : colors.buttonShadow); + } + } + resultBounds = valBounds; + } + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + else + { + resultBounds = Rectangle.Empty; + } + } + else + { + Debug.Assert(valBounds.Height <= 0 || valBounds.Width <= 0); + resultBounds = Rectangle.Empty; + } + + if (paint && + DataGridViewCell.PaintFocus(paintParts) && + cellCurrent && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + valBounds.Width > 2 * SystemInformation.Border3DSize.Width + 1 && + valBounds.Height > 2 * SystemInformation.Border3DSize.Height + 1) + { + // Draw focus rectangle + if (this.FlatStyle == FlatStyle.System || this.FlatStyle == FlatStyle.Standard) + { + ControlPaint.DrawFocusRectangle(g, Rectangle.Inflate(valBounds, -1, -1), Color.Empty, SystemColors.Control); + } + else if (this.FlatStyle == FlatStyle.Flat) + { + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0 || + (this.DataGridView.CurrentCellAddress.Y == rowIndex && this.DataGridView.CurrentCellAddress.X == this.ColumnIndex)) + { + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintFlatRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + string text = (formattedString != null) ? formattedString : String.Empty; + + ButtonBaseAdapter.LayoutOptions options = ButtonInternal.ButtonFlatAdapter.PaintFlatLayout(g, + true, + SystemInformation.HighContrast, + 1, + valBounds, + Padding.Empty, + false, + cellStyle.Font, + text, + this.DataGridView.Enabled, + DataGridViewUtilities.ComputeDrawingContentAlignmentForCellStyleAlignment(cellStyle.Alignment), + this.DataGridView.RightToLeft); + options.everettButtonCompat = false; + ButtonBaseAdapter.LayoutData layout = options.Layout(); + + ButtonInternal.ButtonBaseAdapter.DrawFlatFocus(g, + layout.focus, + colors.options.highContrast ? colors.windowText : colors.constrastButtonShadow); + } + } + else + { + Debug.Assert(this.FlatStyle == FlatStyle.Popup, "FlatStyle.Popup is the last flat style"); + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0 || + (this.DataGridView.CurrentCellAddress.Y == rowIndex && this.DataGridView.CurrentCellAddress.X == this.ColumnIndex)) + { + // If we are painting the current cell, then paint the text up. + // If we are painting the current cell and the current cell is pressed down, then paint the text down. + bool paintUp = (this.ButtonState == ButtonState.Normal); + string text = (formattedString != null) ? formattedString : String.Empty; + ButtonBaseAdapter.LayoutOptions options = ButtonInternal.ButtonPopupAdapter.PaintPopupLayout(g, + paintUp, + SystemInformation.HighContrast ? 2 : 1, + valBounds, + Padding.Empty, + false, + cellStyle.Font, + text, + this.DataGridView.Enabled, + DataGridViewUtilities.ComputeDrawingContentAlignmentForCellStyleAlignment(cellStyle.Alignment), + this.DataGridView.RightToLeft); + options.everettButtonCompat = false; + ButtonBaseAdapter.LayoutData layout = options.Layout(); + + + ControlPaint.DrawFocusRectangle(g, + layout.focus, + cellStyle.ForeColor, + cellStyle.BackColor); + } + } + } + + if (formattedString != null && paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + // Font independent margins + valBounds.Offset(DATAGRIDVIEWBUTTONCELL_horizontalTextMargin, DATAGRIDVIEWBUTTONCELL_verticalTextMargin); + valBounds.Width -= 2 * DATAGRIDVIEWBUTTONCELL_horizontalTextMargin; + valBounds.Height -= 2 * DATAGRIDVIEWBUTTONCELL_verticalTextMargin; + + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0 && + this.FlatStyle != FlatStyle.Flat && this.FlatStyle != FlatStyle.Popup) + { + valBounds.Offset(1, 1); + valBounds.Width--; + valBounds.Height--; + } + + if (valBounds.Width > 0 && valBounds.Height > 0) + { + Color textColor; + if (this.DataGridView.ApplyVisualStylesToInnerCells && + (this.FlatStyle == FlatStyle.System || this.FlatStyle == FlatStyle.Standard)) + { + textColor = DataGridViewButtonCellRenderer.DataGridViewButtonRenderer.GetColor(ColorProperty.TextColor); + } + else + { + textColor = foreBrush.Color; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + TextRenderer.DrawText(g, + formattedString, + cellStyle.Font, + valBounds, + textColor, + flags); + } + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + } + else + { + resultBounds = Rectangle.Empty; + } + + return resultBounds; + } + + /// + public override string ToString() + { + return "DataGridViewButtonCell { ColumnIndex=" + ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private void UpdateButtonState(ButtonState newButtonState, int rowIndex) + { + if (this.ButtonState != newButtonState) + { + this.ButtonState = newButtonState; + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + private class DataGridViewButtonCellRenderer + { + private static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewButtonCellRenderer() + { + } + + public static VisualStyleRenderer DataGridViewButtonRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ButtonElement); + } + return visualStyleRenderer; + } + } + + public static void DrawButton(Graphics g, Rectangle bounds, int buttonState) + { + DataGridViewButtonRenderer.SetParameters(ButtonElement.ClassName, ButtonElement.Part, buttonState); + DataGridViewButtonRenderer.DrawBackground(g, bounds, Rectangle.Truncate(g.ClipBounds)); + } + } + + /// + protected class DataGridViewButtonCellAccessibleObject : DataGridViewCellAccessibleObject + { + /// + public DataGridViewButtonCellAccessibleObject(DataGridViewCell owner) : base (owner) + { + } + + /// + public override string DefaultAction + { + get + { + return SR.GetString(SR.DataGridView_AccButtonCellDefaultAction); + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + DataGridViewButtonCell dataGridViewCell = (DataGridViewButtonCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView != null && dataGridViewCell.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + + if (dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningRow != null) + { + dataGridView.OnCellClickInternal(new DataGridViewCellEventArgs(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex)); + dataGridView.OnCellContentClickInternal(new DataGridViewCellEventArgs(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex)); + } + } + + /// + public override int GetChildCount() + { + return 0; + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) + { + return NativeMethods.UIA_ButtonControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewButtonColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewButtonColumn.cs new file mode 100644 index 000000000..5c94f5174 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewButtonColumn.cs @@ -0,0 +1,260 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + [ToolboxBitmapAttribute(typeof(DataGridViewButtonColumn), "DataGridViewButtonColumn.bmp")] + public class DataGridViewButtonColumn : DataGridViewColumn + { + private static Type columnType = typeof(DataGridViewButtonColumn); + + private string text; + + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Can't think of a workaround. + ] + public DataGridViewButtonColumn() + : base(new DataGridViewButtonCell()) + { + DataGridViewCellStyle defaultCellStyle = new DataGridViewCellStyle(); + defaultCellStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleCenter; + this.DefaultCellStyle = defaultCellStyle; + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + if (value != null && !(value is System.Windows.Forms.DataGridViewButtonCell)) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewButtonCell")); + } + base.CellTemplate = value; + } + } + + /// + [ + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnDefaultCellStyleDescr) + ] + public override DataGridViewCellStyle DefaultCellStyle + { + get + { + return base.DefaultCellStyle; + } + set + { + base.DefaultCellStyle = value; + } + } + + /// + [ + DefaultValue(FlatStyle.Standard), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ButtonColumnFlatStyleDescr) + ] + public FlatStyle FlatStyle + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewButtonCell) this.CellTemplate).FlatStyle; + } + set + { + if (this.FlatStyle != value) + { + ((DataGridViewButtonCell)this.CellTemplate).FlatStyle = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewButtonCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewButtonCell; + if (dataGridViewCell != null) + { + dataGridViewCell.FlatStyleInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ButtonColumnTextDescr) + ] + public string Text + { + get + { + return this.text; + } + set + { + if (!string.Equals(value, this.text, StringComparison.Ordinal)) + { + this.text = value; + if (this.DataGridView != null) + { + if (this.UseColumnTextForButtonValue) + { + this.DataGridView.OnColumnCommonChange(this.Index); + } + else + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewButtonCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewButtonCell; + if (dataGridViewCell != null && dataGridViewCell.UseColumnTextForButtonValue) + { + this.DataGridView.OnColumnCommonChange(this.Index); + return; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ButtonColumnUseColumnTextForButtonValueDescr) + ] + public bool UseColumnTextForButtonValue + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewButtonCell)this.CellTemplate).UseColumnTextForButtonValue; + } + set + { + if (this.UseColumnTextForButtonValue != value) + { + ((DataGridViewButtonCell)this.CellTemplate).UseColumnTextForButtonValueInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewButtonCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewButtonCell; + if (dataGridViewCell != null) + { + dataGridViewCell.UseColumnTextForButtonValueInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + public override object Clone() + { + DataGridViewButtonColumn dataGridViewColumn; + Type thisType = this.GetType(); + + if (thisType == columnType) //performance improvement + { + dataGridViewColumn = new DataGridViewButtonColumn(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewColumn = (DataGridViewButtonColumn) System.Activator.CreateInstance(thisType); + } + if (dataGridViewColumn != null) + { + base.CloneInternal(dataGridViewColumn); + dataGridViewColumn.Text = this.text; + } + return dataGridViewColumn; + } + + private bool ShouldSerializeDefaultCellStyle() + { + if (!this.HasDefaultCellStyle) + { + return false; + } + + DataGridViewCellStyle defaultCellStyle = this.DefaultCellStyle; + + return (!defaultCellStyle.BackColor.IsEmpty || + !defaultCellStyle.ForeColor.IsEmpty || + !defaultCellStyle.SelectionBackColor.IsEmpty || + !defaultCellStyle.SelectionForeColor.IsEmpty || + defaultCellStyle.Font != null || + !defaultCellStyle.IsNullValueDefault || + !defaultCellStyle.IsDataSourceNullValueDefault || + !String.IsNullOrEmpty(defaultCellStyle.Format) || + !defaultCellStyle.FormatProvider.Equals(System.Globalization.CultureInfo.CurrentCulture) || + defaultCellStyle.Alignment != DataGridViewContentAlignment.MiddleCenter || + defaultCellStyle.WrapMode != DataGridViewTriState.NotSet || + defaultCellStyle.Tag != null || + !defaultCellStyle.Padding.Equals(Padding.Empty)); + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewButtonColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCell.cs new file mode 100644 index 000000000..bd3da77eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCell.cs @@ -0,0 +1,5640 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +// Code taken from ASP.NET file xsp\System\Web\httpserverutility.cs +// Don't entity encode high chars (160 to 256), to fix bugs VSWhidbey 85857/111927 +// ASP.NET team decided to postpone those bugs and leave behavior as is in Whidbey. +#define ENTITY_ENCODE_HIGH_ASCII_CHARS + +namespace System.Windows.Forms +{ + using System; + using System.IO; + using System.Text; + using System.Diagnostics; + using System.Drawing; + using System.Reflection; + using System.Globalization; + using System.ComponentModel; + using System.Windows.Forms.Internal; + using System.Security.Permissions; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + + /// + /// + /// Identifies a cell in the dataGridView. + /// + [ + TypeConverterAttribute(typeof(DataGridViewCellConverter)) + ] + public abstract class DataGridViewCell : DataGridViewElement, ICloneable, IDisposable + { + private const TextFormatFlags textFormatSupportedFlags = TextFormatFlags.SingleLine | /*TextFormatFlags.NoFullWidthCharacterBreak |*/ TextFormatFlags.WordBreak | TextFormatFlags.NoPrefix; + private const int DATAGRIDVIEWCELL_constrastThreshold = 1000; + private const int DATAGRIDVIEWCELL_highConstrastThreshold = 2000; + private const int DATAGRIDVIEWCELL_maxToolTipLength = 288; + private const int DATAGRIDVIEWCELL_maxToolTipCutOff = 256; + private const int DATAGRIDVIEWCELL_toolTipEllipsisLength = 3; + private const string DATAGRIDVIEWCELL_toolTipEllipsis = "..."; + private const byte DATAGRIDVIEWCELL_flagAreaNotSet = 0x00; + private const byte DATAGRIDVIEWCELL_flagDataArea = 0x01; + private const byte DATAGRIDVIEWCELL_flagErrorArea = 0x02; + internal const byte DATAGRIDVIEWCELL_iconMarginWidth = 4; // 4 pixels of margin on the left and right of icons + internal const byte DATAGRIDVIEWCELL_iconMarginHeight = 4; // 4 pixels of margin on the top and bottom of icons + private const byte DATAGRIDVIEWCELL_iconsWidth = 12; // all icons are 12 pixels wide - make sure that it stays that way + private const byte DATAGRIDVIEWCELL_iconsHeight = 11; // all icons are 11 pixels tall - make sure that it stays that way + + private static bool isScalingInitialized = false; + internal static byte iconsWidth = DATAGRIDVIEWCELL_iconsWidth; + internal static byte iconsHeight = DATAGRIDVIEWCELL_iconsHeight; + + internal static readonly int PropCellValue = PropertyStore.CreateKey(); + private static readonly int PropCellContextMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropCellErrorText = PropertyStore.CreateKey(); + private static readonly int PropCellStyle = PropertyStore.CreateKey(); + private static readonly int PropCellValueType = PropertyStore.CreateKey(); + private static readonly int PropCellTag = PropertyStore.CreateKey(); + private static readonly int PropCellToolTipText = PropertyStore.CreateKey(); + private static readonly int PropCellAccessibilityObject = PropertyStore.CreateKey(); + + private static Bitmap errorBmp = null; + + private PropertyStore propertyStore; // Contains all properties that are not always set. + private DataGridViewRow owningRow; + private DataGridViewColumn owningColumn; + + private static Type stringType = typeof(string); // cache the string type for performance + + private byte flags; // see DATAGRIDVIEWCELL_flag* consts above + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + protected DataGridViewCell() : base() + { + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + iconsWidth = (byte)DpiHelper.LogicalToDeviceUnitsX(DATAGRIDVIEWCELL_iconsWidth); + iconsHeight = (byte)DpiHelper.LogicalToDeviceUnitsY(DATAGRIDVIEWCELL_iconsHeight); + } + isScalingInitialized = true; + } + + this.propertyStore = new PropertyStore(); + this.StateInternal = DataGridViewElementStates.None; + } + + /// + ~DataGridViewCell() + { + Dispose(false); + } + + /// + [ + Browsable(false) + ] + public AccessibleObject AccessibilityObject + { + get + { + AccessibleObject result = (AccessibleObject) this.Properties.GetObject(PropCellAccessibilityObject); + if (result == null) + { + result = this.CreateAccessibilityInstance(); + this.Properties.SetObject(PropCellAccessibilityObject, result); + } + + return result; + } + } + + /// + /// + /// Gets or sets the Index of a column in the control. + /// + public int ColumnIndex + { + get + { + if (this.owningColumn == null) + { + return -1; + } + return this.owningColumn.Index; + } + } + + /// + [ + Browsable(false) + ] + public Rectangle ContentBounds + { + get + { + return GetContentBounds(this.RowIndex); + } + } + + /// + [ + DefaultValue(null) + ] + public virtual ContextMenuStrip ContextMenuStrip + { + get + { + return GetContextMenuStrip(this.RowIndex); + } + set + { + this.ContextMenuStripInternal = value; + } + } + + private ContextMenuStrip ContextMenuStripInternal + { + get + { + return (ContextMenuStrip)this.Properties.GetObject(PropCellContextMenuStrip); + } + set + { + ContextMenuStrip oldValue = (ContextMenuStrip)this.Properties.GetObject(PropCellContextMenuStrip); + if (oldValue != value) + { + EventHandler disposedHandler = new EventHandler(DetachContextMenuStrip); + if (oldValue != null) + { + oldValue.Disposed -= disposedHandler; + } + this.Properties.SetObject(PropCellContextMenuStrip, value); + if (value != null) + { + value.Disposed += disposedHandler; + } + if (this.DataGridView != null) + { + this.DataGridView.OnCellContextMenuStripChanged(this); + } + } + } + } + + private byte CurrentMouseLocation + { + get + { + return (byte) (this.flags & (DATAGRIDVIEWCELL_flagDataArea | DATAGRIDVIEWCELL_flagErrorArea)); + } + set + { + this.flags = (byte)(this.flags & ~(DATAGRIDVIEWCELL_flagDataArea | DATAGRIDVIEWCELL_flagErrorArea)); + this.flags |= value; + } + } + + /// + [ + Browsable(false) + ] + public virtual object DefaultNewRowValue + { + get + { + return null; + } + } + + /// + [ + Browsable(false) + ] + public virtual bool Displayed + { + get + { + Debug.Assert((this.State & DataGridViewElementStates.Displayed) == 0); + + if (this.DataGridView == null) + { + // No detached element is displayed. + return false; + } + + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.Displayed && this.owningRow.Displayed; + } + + return false; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public object EditedFormattedValue + { + get + { + if (this.DataGridView == null) + { + return null; + } + Debug.Assert(this.RowIndex >= -1); + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, this.RowIndex, false); + return GetEditedFormattedValue(GetValue(this.RowIndex), this.RowIndex, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual Type EditType + { + get + { + return typeof(System.Windows.Forms.DataGridViewTextBoxEditingControl); + } + } + + private static Bitmap ErrorBitmap + { + get + { + if (errorBmp == null) + { + errorBmp = GetBitmap("DataGridViewRow.error.bmp"); + } + return errorBmp; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods") // ErrorIconBounds/GetErrorIconBounds existence is intentional + ] + public Rectangle ErrorIconBounds + { + get + { + return GetErrorIconBounds(this.RowIndex); + } + } + + /// + [ + Browsable(false) + ] + public string ErrorText + { + get + { + return GetErrorText(this.RowIndex); + } + set + { + this.ErrorTextInternal = value; + } + } + + private string ErrorTextInternal + { + get + { + object errorText = this.Properties.GetObject(PropCellErrorText); + return (errorText == null) ? string.Empty : (string)errorText; + } + set + { + string errorText = this.ErrorTextInternal; + if (!string.IsNullOrEmpty(value) || this.Properties.ContainsObject(PropCellErrorText)) + { + this.Properties.SetObject(PropCellErrorText, value); + } + if (this.DataGridView != null && !errorText.Equals(this.ErrorTextInternal)) + { + this.DataGridView.OnCellErrorTextChanged(this); + } + } + } + + /// + [ + Browsable(false) + ] + public object FormattedValue + { + get + { + if (this.DataGridView == null) + { + return null; + } + Debug.Assert(this.RowIndex >= -1); + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, this.RowIndex, false); + return GetFormattedValue(this.RowIndex, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + } + } + + /// + [ + Browsable(false) + ] + public virtual Type FormattedValueType + { + get + { + return this.ValueType; + } + } + + private TypeConverter FormattedValueTypeConverter + { + get + { + TypeConverter formattedValueTypeConverter = null; + if (this.FormattedValueType != null) + { + if (this.DataGridView != null) + { + formattedValueTypeConverter = this.DataGridView.GetCachedTypeConverter(this.FormattedValueType); + } + else + { + formattedValueTypeConverter = TypeDescriptor.GetConverter(this.FormattedValueType); + } + } + return formattedValueTypeConverter; + } + } + + /// + [ + Browsable(false) + ] + public virtual bool Frozen + { + get + { + Debug.Assert((this.State & DataGridViewElementStates.Frozen) == 0); + + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.Frozen && this.owningRow.Frozen; + } + else if (this.owningRow != null && (this.owningRow.DataGridView == null || this.RowIndex >= 0)) + { + return this.owningRow.Frozen; + } + return false; + } + } + + internal bool HasErrorText + { + get + { + return this.Properties.ContainsObject(PropCellErrorText) && this.Properties.GetObject(PropCellErrorText) != null; + } + } + + /// + [ + Browsable(false) + ] + public bool HasStyle + { + get + { + return this.Properties.ContainsObject(PropCellStyle) && this.Properties.GetObject(PropCellStyle) != null; + } + } + + internal bool HasToolTipText + { + get + { + return this.Properties.ContainsObject(PropCellToolTipText) && this.Properties.GetObject(PropCellToolTipText) != null; + } + } + + internal bool HasValue + { + get + { + return this.Properties.ContainsObject(PropCellValue) && this.Properties.GetObject(PropCellValue) != null; + } + } + + internal virtual bool HasValueType + { + get + { + return this.Properties.ContainsObject(PropCellValueType) && this.Properties.GetObject(PropCellValueType) != null; + } + } + + /// + [ + Browsable(false), + SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods") // InheritedState/GetInheritedState existence is intentional + ] + public DataGridViewElementStates InheritedState + { + get + { + return GetInheritedState(this.RowIndex); + } + } + + /// + [ + Browsable(false), + SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods") // InheritedStyle/GetInheritedStyle existence is intentional + ] + public DataGridViewCellStyle InheritedStyle + { + get + { + // this.RowIndex could be -1 if: + // - the developer makes a mistake & calls dataGridView1.Rows.SharedRow(y).Cells(x).InheritedStyle. + // - the InheritedStyle of a ColumnHeaderCell is accessed. + return GetInheritedStyleInternal(this.RowIndex); + } + } + + /// + [ + Browsable(false) + ] + public bool IsInEditMode + { + get + { + if (this.DataGridView == null) + { + return false; + } + if (this.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + return ptCurrentCell.X != -1 && + ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == this.RowIndex && + this.DataGridView.IsCurrentCellInEditMode; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public DataGridViewColumn OwningColumn + { + get + { + return this.owningColumn; + } + } + + internal DataGridViewColumn OwningColumnInternal + { + set + { + this.owningColumn = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public DataGridViewRow OwningRow + { + get + { + return this.owningRow; + } + } + + internal DataGridViewRow OwningRowInternal + { + set + { + this.owningRow = value; + } + } + + /// + [ + Browsable(false) + ] + public Size PreferredSize + { + get + { + return GetPreferredSize(this.RowIndex); + } + } + + internal PropertyStore Properties + { + get + { + return this.propertyStore; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual bool ReadOnly + { + get + { + if ((this.State & DataGridViewElementStates.ReadOnly) != 0) + { + return true; + } + if (this.owningRow != null && (this.owningRow.DataGridView == null || this.RowIndex >= 0) && this.owningRow.ReadOnly) + { + return true; + } + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.ReadOnly; + } + return false; + } + set + { + if (this.DataGridView != null) + { + if (this.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + Debug.Assert(this.ColumnIndex >= 0); + // When the whole grid is read-only, we ignore the request. + if (value != this.ReadOnly && !this.DataGridView.ReadOnly) + { + this.DataGridView.OnDataGridViewElementStateChanging(this, -1, DataGridViewElementStates.ReadOnly); + this.DataGridView.SetReadOnlyCellCore(this.ColumnIndex, this.RowIndex, value); // this may trigger a call to set_ReadOnlyInternal + } + } + else + { + if (this.owningRow == null) + { + if (value != this.ReadOnly) + { + // We do not allow the read-only flag of a cell to be changed before it is added to a row. + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCell_CannotSetReadOnlyState)); + } + } + else + { + this.owningRow.SetReadOnlyCellCore(this, value); + } + } + } + } + + internal bool ReadOnlyInternal + { + set + { + Debug.Assert(value != this.ReadOnly); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.ReadOnly; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.ReadOnly; + } + if (this.DataGridView != null) + { + this.DataGridView.OnDataGridViewElementStateChanged(this, -1, DataGridViewElementStates.ReadOnly); + } + } + } + + /// + [ + Browsable(false) + ] + public virtual bool Resizable + { + get + { + Debug.Assert((this.State & DataGridViewElementStates.Resizable) == 0); + + if (this.owningRow != null && (this.owningRow.DataGridView == null || this.RowIndex >= 0) && this.owningRow.Resizable == DataGridViewTriState.True) + { + return true; + } + + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.Resizable == DataGridViewTriState.True; + } + + return false; + } + } + + /// + /// + /// Gets or sets the index of a row in the control. + /// + [ + Browsable(false) + ] + public int RowIndex + { + get + { + if (this.owningRow == null) + { + return -1; + } + return this.owningRow.Index; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual bool Selected + { + get + { + if ((this.State & DataGridViewElementStates.Selected) != 0) + { + return true; + } + + if (this.owningRow != null && (this.owningRow.DataGridView == null || this.RowIndex >= 0) && this.owningRow.Selected) + { + return true; + } + + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.Selected; + } + + return false; + } + set + { + if (this.DataGridView != null) + { + if (this.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + Debug.Assert(this.ColumnIndex >= 0); + this.DataGridView.SetSelectedCellCoreInternal(this.ColumnIndex, this.RowIndex, value); // this may trigger a call to set_SelectedInternal + } + else if (value) + { + // We do not allow the selection of a cell to be set before the row gets added to the dataGridView. + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCell_CannotSetSelectedState)); + } + } + } + + internal bool SelectedInternal + { + set + { + Debug.Assert(value != this.Selected); + if (value) + { + this.StateInternal = this.State | DataGridViewElementStates.Selected; + } + else + { + this.StateInternal = this.State & ~DataGridViewElementStates.Selected; + } + if (this.DataGridView != null) + { + this.DataGridView.OnDataGridViewElementStateChanged(this, -1, DataGridViewElementStates.Selected); + } + } + } + + /// + [ + Browsable(false), + SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods") // Size/GetSize existence is intentional + ] + public Size Size + { + get + { + return GetSize(this.RowIndex); + } + } + + internal Rectangle StdBorderWidths + { + get + { + if (this.DataGridView != null) + { + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + false /*singleVerticalBorderAdded*/, + false /*singleHorizontalBorderAdded*/, + false /*isFirstDisplayedColumn*/, + false /*isFirstDisplayedRow*/); + return BorderWidths(dgvabsEffective); + } + else + { + return Rectangle.Empty; + } + } + } + + /// + [ + Browsable(true) + ] + public DataGridViewCellStyle Style + { + get + { + DataGridViewCellStyle dgvcs = (DataGridViewCellStyle)this.Properties.GetObject(PropCellStyle); + if (dgvcs == null) + { + dgvcs = new DataGridViewCellStyle(); + dgvcs.AddScope(this.DataGridView, DataGridViewCellStyleScopes.Cell); + this.Properties.SetObject(PropCellStyle, dgvcs); + } + return dgvcs; + } + set + { + DataGridViewCellStyle dgvcs = null; + if (this.HasStyle) + { + dgvcs = this.Style; + dgvcs.RemoveScope(DataGridViewCellStyleScopes.Cell); + } + if (value != null || this.Properties.ContainsObject(PropCellStyle)) + { + if (value != null) + { + value.AddScope(this.DataGridView, DataGridViewCellStyleScopes.Cell); + } + this.Properties.SetObject(PropCellStyle, value); + } + if (((dgvcs != null && value == null) || + (dgvcs == null && value != null) || + (dgvcs != null && value != null && !dgvcs.Equals(this.Style))) && this.DataGridView != null) + { + this.DataGridView.OnCellStyleChanged(this); + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)) + ] + public object Tag + { + get + { + return this.Properties.GetObject(PropCellTag); + } + set + { + if (value != null || this.Properties.ContainsObject(PropCellTag)) + { + this.Properties.SetObject(PropCellTag, value); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public string ToolTipText + { + get + { + return GetToolTipText(this.RowIndex); + } + set + { + this.ToolTipTextInternal = value; + } + } + + private string ToolTipTextInternal + { + get + { + object toolTipText = this.Properties.GetObject(PropCellToolTipText); + return (toolTipText == null) ? string.Empty : (string)toolTipText; + } + set + { + string toolTipText = this.ToolTipTextInternal; + if (!String.IsNullOrEmpty(value) || this.Properties.ContainsObject(PropCellToolTipText)) + { + this.Properties.SetObject(PropCellToolTipText, value); + } + if (this.DataGridView != null && !toolTipText.Equals(this.ToolTipTextInternal)) + { + this.DataGridView.OnCellToolTipTextChanged(this); + } + } + } + + /// + [ + Browsable(false) + ] + public object Value + { + get + { + Debug.Assert(this.RowIndex >= -1); + return GetValue(this.RowIndex); + } + set + { + Debug.Assert(this.RowIndex >= -1); + SetValue(this.RowIndex, value); + } + } + + /// + [ + Browsable(false) + ] + public virtual Type ValueType + { + get + { + Type cellValueType = (Type) this.Properties.GetObject(PropCellValueType); + if (cellValueType == null && this.OwningColumn != null) + { + cellValueType = this.OwningColumn.ValueType; + } + + return cellValueType; + } + set + { + if (value != null || this.Properties.ContainsObject(PropCellValueType)) + { + this.Properties.SetObject(PropCellValueType, value); + } + } + } + + private TypeConverter ValueTypeConverter + { + get + { + TypeConverter valueTypeConverter = null; + if (this.OwningColumn != null) + { + valueTypeConverter = this.OwningColumn.BoundColumnConverter; + } + if (valueTypeConverter == null && this.ValueType != null) + { + if (this.DataGridView != null) + { + valueTypeConverter = this.DataGridView.GetCachedTypeConverter(this.ValueType); + } + else + { + valueTypeConverter = TypeDescriptor.GetConverter(this.ValueType); + } + } + return valueTypeConverter; + } + } + + /// + [ + Browsable(false) + ] + public virtual bool Visible + { + get + { + Debug.Assert((this.State & DataGridViewElementStates.Visible) == 0); + + if (this.DataGridView != null && this.RowIndex >= 0 && this.ColumnIndex >= 0) + { + Debug.Assert(this.DataGridView.Rows.GetRowState(this.RowIndex) == this.DataGridView.Rows.SharedRow(this.RowIndex).State); + return this.owningColumn.Visible && this.owningRow.Visible; + } + else if (this.owningRow != null && (this.owningRow.DataGridView == null || this.RowIndex >= 0)) + { + return this.owningRow.Visible; + } + return false; + } + } + + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual DataGridViewAdvancedBorderStyle AdjustCellBorderStyle(DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStyleInput, + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded, + bool isFirstDisplayedColumn, + bool isFirstDisplayedRow) + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + Debug.Fail("DataGridViewRow.AdjustCellBorderStyle - Unexpected DataGridViewAdvancedCellBorderStyle.OutsetPartial"); + break; + + case DataGridViewAdvancedCellBorderStyle.Single: + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = (isFirstDisplayedColumn && singleVerticalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = (isFirstDisplayedColumn && singleVerticalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = (isFirstDisplayedRow && singleHorizontalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.NotSet: + if (this.DataGridView != null && this.DataGridView.AdvancedCellBorderStyle == dataGridViewAdvancedBorderStyleInput) + { + switch (this.DataGridView.CellBorderStyle) + { + case DataGridViewCellBorderStyle.SingleVertical: + if (this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = (isFirstDisplayedColumn && singleVerticalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = (isFirstDisplayedColumn && singleVerticalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewCellBorderStyle.SingleHorizontal: + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = (isFirstDisplayedRow && singleHorizontalBorderAdded) ? DataGridViewAdvancedCellBorderStyle.Single : DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + return dataGridViewAdvancedBorderStylePlaceholder; + } + } + break; + } + return dataGridViewAdvancedBorderStyleInput; + } + + /// + protected virtual Rectangle BorderWidths(DataGridViewAdvancedBorderStyle advancedBorderStyle) + { + Rectangle rect = new Rectangle(); + + rect.X = (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.None) ? 0 : 1; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + rect.X++; + } + + rect.Y = (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) ? 0 : 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetDouble || advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + rect.Y++; + } + + rect.Width = (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) ? 0 : 1; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble || advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + rect.Width++; + } + + rect.Height = (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.None) ? 0 : 1; + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetDouble || advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + rect.Height++; + } + + if (this.owningColumn != null) + { + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + rect.X += this.owningColumn.DividerWidth; + } + else + { + rect.Width += this.owningColumn.DividerWidth; + } + } + if (this.owningRow != null) + { + rect.Height += this.owningRow.DividerHeight; + } + + return rect; + } + + // Called when the row that owns the editing control gets unshared. + // Too late in the product cycle to make this a public method. + internal virtual void CacheEditingControl() + { + } + + /* Unused at this point. + internal DataGridViewElementStates CellStateFromColumnRowStates() + { + Debug.Assert(this.DataGridView != null); + Debug.Assert(this.RowIndex >= 0); + return CellStateFromColumnRowStates(this.owningRow.State); + }*/ + + internal DataGridViewElementStates CellStateFromColumnRowStates(DataGridViewElementStates rowState) + { + Debug.Assert(this.DataGridView != null); + Debug.Assert(this.ColumnIndex >= 0); + DataGridViewElementStates orFlags = DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Resizable | DataGridViewElementStates.Selected; + DataGridViewElementStates andFlags = DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Visible; + DataGridViewElementStates cellState = (this.owningColumn.State & orFlags); + cellState |= (rowState & orFlags); + cellState |= ((this.owningColumn.State & andFlags) & (rowState & andFlags)); + return cellState; + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool ClickUnsharesRow(DataGridViewCellEventArgs e) + { + return false; + } + + internal bool ClickUnsharesRowInternal(DataGridViewCellEventArgs e) + { + return ClickUnsharesRow(e); + } + + internal void CloneInternal(DataGridViewCell dataGridViewCell) + { + if (this.HasValueType) + { + dataGridViewCell.ValueType = this.ValueType; + } + if (this.HasStyle) + { + dataGridViewCell.Style = new DataGridViewCellStyle(this.Style); + } + if (this.HasErrorText) + { + dataGridViewCell.ErrorText = this.ErrorTextInternal; + } + if (this.HasToolTipText) + { + dataGridViewCell.ToolTipText = this.ToolTipTextInternal; + } + if (this.ContextMenuStripInternal != null) + { + dataGridViewCell.ContextMenuStrip = this.ContextMenuStripInternal.Clone(); + } + dataGridViewCell.StateInternal = this.State & ~DataGridViewElementStates.Selected; + dataGridViewCell.Tag = this.Tag; + } + + /// + public virtual object Clone() + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + DataGridViewCell dataGridViewCell = (DataGridViewCell) System.Activator.CreateInstance(this.GetType()); + CloneInternal(dataGridViewCell); + return dataGridViewCell; + } + + internal static int ColorDistance(Color color1, Color color2) + { + int deltaR = color1.R - color2.R; + int deltaG = color1.G - color2.G; + int deltaB = color1.B - color2.B; + return deltaR * deltaR + deltaG * deltaG + deltaB * deltaB; + } + + internal void ComputeBorderStyleCellStateAndCellBounds(int rowIndex, + out DataGridViewAdvancedBorderStyle dgvabsEffective, + out DataGridViewElementStates cellState, + out Rectangle cellBounds) + { + Debug.Assert(this.DataGridView != null); + bool singleVerticalBorderAdded = !this.DataGridView.RowHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single; + bool singleHorizontalBorderAdded = !this.DataGridView.ColumnHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single; + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(); + + if (rowIndex > -1 && this.OwningColumn != null) + { + // Inner cell case + dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + this.ColumnIndex == this.DataGridView.FirstDisplayedColumnIndex /*isFirstDisplayedColumn*/, + rowIndex == this.DataGridView.FirstDisplayedRowIndex /*isFirstDisplayedRow*/); + DataGridViewElementStates rowState = this.DataGridView.Rows.GetRowState(rowIndex); + cellState = this.CellStateFromColumnRowStates(rowState); + cellState |= this.State; + } + else if (this.OwningColumn != null) + { + // Column header cell case + Debug.Assert(rowIndex == -1); + Debug.Assert(this is DataGridViewColumnHeaderCell, "if the row index == -1 and we have an owning column this should be a column header cell"); + DataGridViewColumn dataGridViewColumn = this.DataGridView.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + bool isLastVisibleColumn = (dataGridViewColumn != null && dataGridViewColumn.Index == this.ColumnIndex); + dgvabsEffective = this.DataGridView.AdjustColumnHeaderBorderStyle(this.DataGridView.AdvancedColumnHeadersBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + this.ColumnIndex == this.DataGridView.FirstDisplayedColumnIndex, + isLastVisibleColumn); + cellState = this.OwningColumn.State | this.State; + } + else if (this.OwningRow != null) + { + // Row header cell case + Debug.Assert(this is DataGridViewRowHeaderCell); + dgvabsEffective = this.OwningRow.AdjustRowHeaderBorderStyle(this.DataGridView.AdvancedRowHeadersBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + rowIndex == this.DataGridView.FirstDisplayedRowIndex /*isFirstDisplayedRow*/, + rowIndex == this.DataGridView.Rows.GetLastRow(DataGridViewElementStates.Visible) /*isLastVisibleRow*/); + cellState = this.OwningRow.GetState(rowIndex) | this.State; + } + else + { + Debug.Assert(this.OwningColumn == null); + Debug.Assert(this.OwningRow == null); + Debug.Assert(rowIndex == -1); + // TopLeft header cell case + dgvabsEffective = this.DataGridView.AdjustedTopLeftHeaderBorderStyle; + cellState = this.State; + } + + cellBounds = new Rectangle(new Point(0, 0), GetSize(rowIndex)); + } + + internal Rectangle ComputeErrorIconBounds(Rectangle cellValueBounds) + { + if (cellValueBounds.Width >= DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth && + cellValueBounds.Height >= DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight) + { + Rectangle bmpRect = new Rectangle(this.DataGridView.RightToLeftInternal ? + cellValueBounds.Left + DATAGRIDVIEWCELL_iconMarginWidth : + cellValueBounds.Right - DATAGRIDVIEWCELL_iconMarginWidth - iconsWidth, + cellValueBounds.Y + (cellValueBounds.Height - iconsHeight) / 2, + iconsWidth, + iconsHeight); + return bmpRect; + } + else + { + return Rectangle.Empty; + } + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool ContentClickUnsharesRow(DataGridViewCellEventArgs e) + { + return false; + } + + internal bool ContentClickUnsharesRowInternal(DataGridViewCellEventArgs e) + { + return ContentClickUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool ContentDoubleClickUnsharesRow(DataGridViewCellEventArgs e) + { + return false; + } + + internal bool ContentDoubleClickUnsharesRowInternal(DataGridViewCellEventArgs e) + { + return ContentDoubleClickUnsharesRow(e); + } + + /// + protected virtual AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewCellAccessibleObject(this); + } + + private void DetachContextMenuStrip(object sender, EventArgs e) + { + this.ContextMenuStripInternal = null; + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual void DetachEditingControl() + { + DataGridView dgv = this.DataGridView; + if (dgv == null || dgv.EditingControl == null) + { + throw new InvalidOperationException(); + } + if (dgv.EditingControl.ParentInternal != null) + { + if (dgv.EditingControl.ContainsFocus) + { + ContainerControl cc = dgv.GetContainerControlInternal() as ContainerControl; + if (cc != null && (dgv.EditingControl == cc.ActiveControl || dgv.EditingControl.Contains(cc.ActiveControl))) + { + dgv.FocusInternal(); + } + else + { + // VSWhidbey 481170. We don't want the grid to get the keyboard focus + // when the editing control gets parented to the parking window, + // because some other window is in the middle of receiving the focus. + UnsafeNativeMethods.SetFocus(new HandleRef(null, IntPtr.Zero)); + } + } + Debug.Assert(dgv.EditingControl.ParentInternal == dgv.EditingPanel); + Debug.Assert(dgv.EditingPanel.Controls.Contains(dgv.EditingControl)); + dgv.EditingPanel.Controls.Remove(dgv.EditingControl); + Debug.Assert(dgv.EditingControl.ParentInternal == null); + + if (AccessibilityImprovements.Level3) + { + if (dgv.EditingControl is DataGridViewTextBoxEditingControl) + { + dgv.TextBoxControlWasDetached = true; + } + + if (dgv.EditingControl is DataGridViewComboBoxEditingControl) + { + dgv.ComboBoxControlWasDetached = true; + } + + dgv.EditingControlAccessibleObject.SetParent(null); + AccessibilityObject.SetDetachableChild(null); + + AccessibilityObject.RaiseStructureChangedEvent(UnsafeNativeMethods.StructureChangeType.ChildRemoved, dgv.EditingControlAccessibleObject.RuntimeId); + } + } + if (dgv.EditingPanel.ParentInternal != null) + { + Debug.Assert(dgv.EditingPanel.ParentInternal == dgv); + Debug.Assert(dgv.Controls.Contains(dgv.EditingPanel)); + ((DataGridView.DataGridViewControlCollection)dgv.Controls).RemoveInternal(dgv.EditingPanel); + Debug.Assert(dgv.EditingPanel.ParentInternal == null); + } + + Debug.Assert(dgv.EditingControl.ParentInternal == null); + Debug.Assert(dgv.EditingPanel.ParentInternal == null); + Debug.Assert(dgv.EditingPanel.Controls.Count == 0); + + // Since the tooltip is removed when the editing control is shown, + // the CurrentMouseLocation is reset to DATAGRIDVIEWCELL_flagAreaNotSet + // so that the tooltip appears again on mousemove after the editing. + this.CurrentMouseLocation = DATAGRIDVIEWCELL_flagAreaNotSet; + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ContextMenuStrip contextMenuStrip = (ContextMenuStrip)this.ContextMenuStripInternal; + if (contextMenuStrip != null) + { + contextMenuStrip.Disposed -= new EventHandler(DetachContextMenuStrip); + } + } + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool DoubleClickUnsharesRow(DataGridViewCellEventArgs e) + { + return false; + } + + internal bool DoubleClickUnsharesRowInternal(DataGridViewCellEventArgs e) + { + return DoubleClickUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool EnterUnsharesRow(int rowIndex, bool throughMouseClick) + { + return false; + } + + internal bool EnterUnsharesRowInternal(int rowIndex, bool throughMouseClick) + { + return EnterUnsharesRow(rowIndex, throughMouseClick); + } + + internal static void FormatPlainText(string s, bool csv, TextWriter output, ref bool escapeApplied) + { + if (s == null) + { + return; + } + + int cb = s.Length; + for (int i = 0; i < cb; i++) + { + char ch = s[i]; + switch (ch) + { + case '"': + if (csv) + { + output.Write("\"\""); + escapeApplied = true; + } + else + { + output.Write('"'); + } + break; + case ',': + if (csv) + { + escapeApplied = true; + } + output.Write(','); + break; + case '\t': + if (!csv) + { + output.Write(' '); + } + else + { + output.Write('\t'); + } + break; + default: + output.Write(ch); + break; + } + } + if (escapeApplied) + { + output.Write('"'); // terminating double-quote. + // the caller is responsible for inserting the opening double-quote. + } + } + + // Code taken from ASP.NET file xsp\System\Web\httpserverutility.cs + internal static void FormatPlainTextAsHtml(string s, TextWriter output) + { + if (s == null) + { + return; + } + + int cb = s.Length; + char prevCh = '\0'; + + for (int i = 0; i < cb; i++) + { + char ch = s[i]; + switch (ch) + { + case '<': + output.Write("<"); + break; + case '>': + output.Write(">"); + break; + case '"': + output.Write("""); + break; + case '&': + output.Write("&"); + break; + case ' ': + if (prevCh == ' ') + { + output.Write(" "); + } + else + { + output.Write(ch); + } + break; + case '\r': + // Ignore \r, only handle \n + break; + case '\n': + output.Write("
"); + break; + // + default: + #if ENTITY_ENCODE_HIGH_ASCII_CHARS + // The seemingly arbitrary 160 comes from RFC + if (ch >= 160 && ch < 256) + { + output.Write("&#"); + output.Write(((int)ch).ToString(NumberFormatInfo.InvariantInfo)); + output.Write(';'); + break; + } + #endif // ENTITY_ENCODE_HIGH_ASCII_CHARS + output.Write(ch); + break; + } + prevCh = ch; + } + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static Bitmap GetBitmap(string bitmapName) + { + Bitmap b = new Bitmap(typeof(DataGridViewCell), bitmapName); + b.MakeTransparent(); + if (DpiHelper.IsScalingRequired) { + Bitmap scaledBitmap = DpiHelper.CreateResizedBitmap(b, new Size(iconsWidth, iconsHeight)); + if (scaledBitmap != null) { + b.Dispose(); + b = scaledBitmap; + } + } + return b; + } + + /// + protected virtual object GetClipboardContent(int rowIndex, + bool firstCell, + bool lastCell, + bool inFirstRow, + bool inLastRow, + string format) + { + if (this.DataGridView == null) + { + return null; + } + // Header Cell classes override this implementation - this implementation is only for inner cells + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + // Assuming (like in other places in this class) that the formatted value is independent of the style colors. + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + object formattedValue = null; + if (this.DataGridView.IsSharedCellSelected(this, rowIndex)) + { + formattedValue = GetEditedFormattedValue(GetValue(rowIndex), rowIndex, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.ClipboardContent); + } + + StringBuilder sb = new StringBuilder(64); + + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + if (firstCell) + { + if (inFirstRow) + { + sb.Append(""); + } + sb.Append(""); + } + sb.Append(""); + if (lastCell) + { + sb.Append(""); + if (inLastRow) + { + sb.Append("
"); + if (formattedValue != null) + { + FormatPlainTextAsHtml(formattedValue.ToString(), new StringWriter(sb, CultureInfo.CurrentCulture)); + } + else + { + sb.Append(" "); + } + sb.Append("
"); + } + } + return sb.ToString(); + } + else + { + bool csv = String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase); + if (csv || + String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) || + String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase)) + { + if (formattedValue != null) + { + if (firstCell && lastCell && inFirstRow && inLastRow) + { + sb.Append(formattedValue.ToString()); + } + else + { + bool escapeApplied = false; + int insertionPoint = sb.Length; + FormatPlainText(formattedValue.ToString(), csv, new StringWriter(sb, CultureInfo.CurrentCulture), ref escapeApplied); + if (escapeApplied) + { + Debug.Assert(csv); + sb.Insert(insertionPoint, '"'); + } + } + } + if (lastCell) + { + if (!inLastRow) + { + sb.Append((char)Keys.Return); + sb.Append((char)Keys.LineFeed); + } + } + else + { + sb.Append(csv ? ',' : (char)Keys.Tab); + } + return sb.ToString(); + } + else + { + return null; + } + } + } + + internal object GetClipboardContentInternal(int rowIndex, + bool firstCell, + bool lastCell, + bool inFirstRow, + bool inLastRow, + string format) + { + return GetClipboardContent(rowIndex, firstCell, lastCell, inFirstRow, inLastRow, format); + } + + internal ContextMenuStrip GetContextMenuStrip(int rowIndex) + { + ContextMenuStrip contextMenuStrip = this.ContextMenuStripInternal; + if (this.DataGridView != null && + (this.DataGridView.VirtualMode || this.DataGridView.DataSource != null)) + { + contextMenuStrip = this.DataGridView.OnCellContextMenuStripNeeded(this.ColumnIndex, rowIndex, contextMenuStrip); + } + return contextMenuStrip; + } + + internal void GetContrastedPens(Color baseline, ref Pen darkPen, ref Pen lightPen) + { + Debug.Assert(this.DataGridView != null); + + int darkDistance = ColorDistance(baseline, SystemColors.ControlDark); + int lightDistance = ColorDistance(baseline, SystemColors.ControlLightLight); + + if (SystemInformation.HighContrast) + { + if (darkDistance < DATAGRIDVIEWCELL_highConstrastThreshold) + { + darkPen = this.DataGridView.GetCachedPen(ControlPaint.DarkDark(baseline)); + } + else + { + darkPen = this.DataGridView.GetCachedPen(SystemColors.ControlDark); + } + if (lightDistance < DATAGRIDVIEWCELL_highConstrastThreshold) + { + lightPen = this.DataGridView.GetCachedPen(ControlPaint.LightLight(baseline)); + } + else + { + lightPen = this.DataGridView.GetCachedPen(SystemColors.ControlLightLight); + } + } + else + { + if (darkDistance < DATAGRIDVIEWCELL_constrastThreshold) + { + darkPen = this.DataGridView.GetCachedPen(ControlPaint.Dark(baseline)); + } + else + { + darkPen = this.DataGridView.GetCachedPen(SystemColors.ControlDark); + } + if (lightDistance < DATAGRIDVIEWCELL_constrastThreshold) + { + lightPen = this.DataGridView.GetCachedPen(ControlPaint.Light(baseline)); + } + else + { + lightPen = this.DataGridView.GetCachedPen(SystemColors.ControlLightLight); + } + } + } + +#if DGV_GDI + internal void GetContrastedWindowsPens(Color baseline, ref WindowsPen darkPen, ref WindowsPen lightPen) + { + Debug.Assert(this.DataGridView != null); + + int darkDistance = ColorDistance(baseline, SystemColors.ControlDark); + int lightDistance = ColorDistance(baseline, SystemColors.ControlLightLight); + + if (SystemInformation.HighContrast) + { + if (darkDistance < DATAGRIDVIEWCELL_highConstrastThreshold) + { + darkPen = this.DataGridView.GetCachedWindowsPen(ControlPaint.DarkDark(baseline)); + } + else + { + darkPen = this.DataGridView.GetCachedWindowsPen(SystemColors.ControlDark); + } + if (lightDistance < DATAGRIDVIEWCELL_highConstrastThreshold) + { + lightPen = this.DataGridView.GetCachedWindowsPen(ControlPaint.LightLight(baseline)); + } + else + { + lightPen = this.DataGridView.GetCachedWindowsPen(SystemColors.ControlLightLight); + } + } + else + { + if (darkDistance < DATAGRIDVIEWCELL_constrastThreshold) + { + darkPen = this.DataGridView.GetCachedWindowsPen(ControlPaint.Dark(baseline)); + } + else + { + darkPen = this.DataGridView.GetCachedWindowsPen(SystemColors.ControlDark); + } + if (lightDistance < DATAGRIDVIEWCELL_constrastThreshold) + { + lightPen = this.DataGridView.GetCachedWindowsPen(ControlPaint.Light(baseline)); + } + else + { + lightPen = this.DataGridView.GetCachedWindowsPen(SystemColors.ControlLightLight); + } + } + } +#endif // DGV_GDI + + /// + public Rectangle GetContentBounds(int rowIndex) + { + if (this.DataGridView == null) + { + return Rectangle.Empty; + } + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + return GetContentBounds(g, dataGridViewCellStyle, rowIndex); + } + } + + /// + protected virtual Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + return Rectangle.Empty; + } + + internal object GetEditedFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle dataGridViewCellStyle, DataGridViewDataErrorContexts context) + { + Debug.Assert(this.DataGridView != null); + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (this.ColumnIndex == ptCurrentCell.X && rowIndex == ptCurrentCell.Y) + { + IDataGridViewEditingControl dgvectl = (IDataGridViewEditingControl)this.DataGridView.EditingControl; + if (dgvectl != null) + { + return dgvectl.GetEditingControlFormattedValue(context); + } + IDataGridViewEditingCell dgvecell = this as IDataGridViewEditingCell; + if (dgvecell != null && this.DataGridView.IsCurrentCellInEditMode) + { + return dgvecell.GetEditingCellFormattedValue(context); + } + return GetFormattedValue(value, rowIndex, ref dataGridViewCellStyle, null, null, context); + } + return GetFormattedValue(value, rowIndex, ref dataGridViewCellStyle, null, null, context); + } + + /// + public object GetEditedFormattedValue(int rowIndex, DataGridViewDataErrorContexts context) + { + if (this.DataGridView == null) + { + return null; + } + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + return GetEditedFormattedValue(GetValue(rowIndex), rowIndex, ref dataGridViewCellStyle, context); + } + + internal Rectangle GetErrorIconBounds(int rowIndex) + { + Debug.Assert(this.DataGridView != null); + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + return GetErrorIconBounds(g, dataGridViewCellStyle, rowIndex); + } + } + + /// + protected virtual Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + return Rectangle.Empty; + } + + /// + protected internal virtual string GetErrorText(int rowIndex) + { + string errorText = string.Empty; + object objErrorText = this.Properties.GetObject(PropCellErrorText); + if (objErrorText != null) + { + errorText = (string) objErrorText; + } + else if (this.DataGridView != null && + rowIndex != -1 && + rowIndex != this.DataGridView.NewRowIndex && + this.OwningColumn != null && + this.OwningColumn.IsDataBound && + this.DataGridView.DataConnection != null) + { + errorText = this.DataGridView.DataConnection.GetError(this.OwningColumn.BoundColumnIndex, this.ColumnIndex, rowIndex); + } + + if (this.DataGridView != null && (this.DataGridView.VirtualMode || this.DataGridView.DataSource != null) && + this.ColumnIndex >= 0 && rowIndex >= 0) + { + errorText = this.DataGridView.OnCellErrorTextNeeded(this.ColumnIndex, rowIndex, errorText); + } + return errorText; + } + + internal object GetFormattedValue(int rowIndex, ref DataGridViewCellStyle cellStyle, DataGridViewDataErrorContexts context) + { + if (this.DataGridView == null) + { + return null; + } + else + { + return GetFormattedValue(GetValue(rowIndex), rowIndex, ref cellStyle, null, null, context); + } + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference") // using ref is OK. + ] + protected virtual object GetFormattedValue(object value, + int rowIndex, + ref DataGridViewCellStyle cellStyle, + TypeConverter valueTypeConverter, + TypeConverter formattedValueTypeConverter, + DataGridViewDataErrorContexts context) + { + if (this.DataGridView == null) + { + return null; + } + + DataGridViewCellFormattingEventArgs gdvcfe = this.DataGridView.OnCellFormatting(this.ColumnIndex, rowIndex, value, this.FormattedValueType, cellStyle); + cellStyle = gdvcfe.CellStyle; + bool formattingApplied = gdvcfe.FormattingApplied; + Object formattedValue = gdvcfe.Value; + bool checkFormattedValType = true; + + if (!formattingApplied && + this.FormattedValueType != null && + (formattedValue == null || !this.FormattedValueType.IsAssignableFrom(formattedValue.GetType()))) + { + try + { + formattedValue = Formatter.FormatObject(formattedValue, + this.FormattedValueType, + valueTypeConverter == null ? this.ValueTypeConverter : valueTypeConverter, /*sourceConverter*/ + formattedValueTypeConverter == null ? this.FormattedValueTypeConverter : formattedValueTypeConverter, /*targetConverter*/ + cellStyle.Format, + cellStyle.FormatProvider, + cellStyle.NullValue, + cellStyle.DataSourceNullValue); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + // Formatting failed, raise OnDataError event. + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + this.ColumnIndex, + rowIndex, + context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + checkFormattedValType = false; + } + } + + if (checkFormattedValType && + (formattedValue == null || this.FormattedValueType == null || !this.FormattedValueType.IsAssignableFrom(formattedValue.GetType()))) + { + if (formattedValue == null && + cellStyle.NullValue == null && + this.FormattedValueType != null && + !typeof(System.ValueType).IsAssignableFrom(this.FormattedValueType)) + { + // null is an acceptable formatted value + return null; + } + Exception exception = null; + if (this.FormattedValueType == null) + { + exception = new FormatException(SR.GetString(SR.DataGridViewCell_FormattedValueTypeNull)); + } + else + { + exception = new FormatException(SR.GetString(SR.DataGridViewCell_FormattedValueHasWrongType)); + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + this.ColumnIndex, + rowIndex, + context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + return formattedValue; + } + + static internal DataGridViewFreeDimension GetFreeDimensionFromConstraint(Size constraintSize) + { + if (constraintSize.Width < 0 || constraintSize.Height < 0) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "constraintSize", constraintSize.ToString())); + } + if (constraintSize.Width == 0) + { + if (constraintSize.Height == 0) + { + return DataGridViewFreeDimension.Both; + } + else + { + return DataGridViewFreeDimension.Width; + } + } + else + { + if (constraintSize.Height == 0) + { + return DataGridViewFreeDimension.Height; + } + else + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "constraintSize", constraintSize.ToString())); + } + } + } + + internal int GetHeight(int rowIndex) + { + if (this.DataGridView == null) + { + return -1; + } + Debug.Assert(this.owningRow != null); + return this.owningRow.GetHeight(rowIndex); + } + + /// + public virtual ContextMenuStrip GetInheritedContextMenuStrip(int rowIndex) + { + if (this.DataGridView != null) + { + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.ColumnIndex < 0) + { + throw new InvalidOperationException(); + } + Debug.Assert(this.ColumnIndex < this.DataGridView.Columns.Count); + } + + ContextMenuStrip contextMenuStrip = GetContextMenuStrip(rowIndex); + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + + if (this.owningRow != null) + { + contextMenuStrip = this.owningRow.GetContextMenuStrip(rowIndex); + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + } + + if (this.owningColumn != null) + { + contextMenuStrip = this.owningColumn.ContextMenuStrip; + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + } + + if (this.DataGridView != null) + { + return this.DataGridView.ContextMenuStrip; + } + else + { + return null; + } + } + + /// + public virtual DataGridViewElementStates GetInheritedState(int rowIndex) + { + DataGridViewElementStates state = this.State | DataGridViewElementStates.ResizableSet; + + if (this.DataGridView == null) + { + Debug.Assert(this.RowIndex == -1); + if (rowIndex != -1) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + if (this.owningRow != null) + { + state |= (this.owningRow.GetState(-1) & (DataGridViewElementStates.Frozen | DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)); + if (this.owningRow.GetResizable(rowIndex) == DataGridViewTriState.True) + { + state |= DataGridViewElementStates.Resizable; + } + } + return state; + } + + // Header Cell classes override this implementation - this implementation is only for inner cells + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + Debug.Assert(this.owningColumn != null); + Debug.Assert(this.owningRow != null); + Debug.Assert(this.ColumnIndex >= 0); + + if (this.DataGridView.Rows.SharedRow(rowIndex) != this.owningRow) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + + DataGridViewElementStates rowEffectiveState = this.DataGridView.Rows.GetRowState(rowIndex); + state |= (rowEffectiveState & (DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected)); + state |= (this.owningColumn.State & (DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected)); + + if (this.owningRow.GetResizable(rowIndex) == DataGridViewTriState.True || + this.owningColumn.Resizable == DataGridViewTriState.True) + { + state |= DataGridViewElementStates.Resizable; + } + if (this.owningColumn.Visible && this.owningRow.GetVisible(rowIndex)) + { + state |= DataGridViewElementStates.Visible; + if (this.owningColumn.Displayed && this.owningRow.GetDisplayed(rowIndex)) + { + state |= DataGridViewElementStates.Displayed; + } + } + if (this.owningColumn.Frozen && this.owningRow.GetFrozen(rowIndex)) + { + state |= DataGridViewElementStates.Frozen; + } + +#if DEBUG + DataGridViewElementStates stateDebug = DataGridViewElementStates.ResizableSet; + if (this.Displayed) + { + stateDebug |= DataGridViewElementStates.Displayed; + } + if (this.Frozen) + { + stateDebug |= DataGridViewElementStates.Frozen; + } + if (this.ReadOnly) + { + stateDebug |= DataGridViewElementStates.ReadOnly; + } + if (this.Resizable) + { + stateDebug |= DataGridViewElementStates.Resizable; + } + if (this.Selected) + { + stateDebug |= DataGridViewElementStates.Selected; + } + if (this.Visible) + { + stateDebug |= DataGridViewElementStates.Visible; + } + Debug.Assert(state == stateDebug || this.DataGridView.Rows.SharedRow(rowIndex).Index == -1); +#endif + return state; + } + + /// + public virtual DataGridViewCellStyle GetInheritedStyle(DataGridViewCellStyle inheritedCellStyle, int rowIndex, bool includeColors) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellNeedsDataGridViewForInheritedStyle)); + } + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.ColumnIndex < 0) + { + throw new InvalidOperationException(); + } + Debug.Assert(this.ColumnIndex < this.DataGridView.Columns.Count); + + DataGridViewCellStyle inheritedCellStyleTmp; + if (inheritedCellStyle == null) + { + inheritedCellStyleTmp = this.DataGridView.PlaceholderCellStyle; + if (!includeColors) + { + inheritedCellStyleTmp.BackColor = Color.Empty; + inheritedCellStyleTmp.ForeColor = Color.Empty; + inheritedCellStyleTmp.SelectionBackColor = Color.Empty; + inheritedCellStyleTmp.SelectionForeColor = Color.Empty; + } + } + else + { + inheritedCellStyleTmp = inheritedCellStyle; + } + + DataGridViewCellStyle cellStyle = null; + if (this.HasStyle) + { + cellStyle = this.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle rowStyle = null; + if (this.DataGridView.Rows.SharedRow(rowIndex).HasDefaultCellStyle) + { + rowStyle = this.DataGridView.Rows.SharedRow(rowIndex).DefaultCellStyle; + Debug.Assert(rowStyle != null); + } + + DataGridViewCellStyle columnStyle = null; + if (this.owningColumn.HasDefaultCellStyle) + { + columnStyle = this.owningColumn.DefaultCellStyle; + Debug.Assert(columnStyle != null); + } + + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (includeColors) + { + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = cellStyle.BackColor; + } + else if (rowStyle != null && !rowStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = rowStyle.BackColor; + } + else if (!this.DataGridView.RowsDefaultCellStyle.BackColor.IsEmpty && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.BackColor.IsEmpty)) + { + inheritedCellStyleTmp.BackColor = this.DataGridView.RowsDefaultCellStyle.BackColor; + } + else if (rowIndex % 2 == 1 && !this.DataGridView.AlternatingRowsDefaultCellStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = this.DataGridView.AlternatingRowsDefaultCellStyle.BackColor; + } + else if (columnStyle != null && !columnStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = columnStyle.BackColor; + } + else + { + inheritedCellStyleTmp.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = cellStyle.ForeColor; + } + else if (rowStyle != null && !rowStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = rowStyle.ForeColor; + } + else if (!this.DataGridView.RowsDefaultCellStyle.ForeColor.IsEmpty && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.ForeColor.IsEmpty)) + { + inheritedCellStyleTmp.ForeColor = this.DataGridView.RowsDefaultCellStyle.ForeColor; + } + else if (rowIndex % 2 == 1 && !this.DataGridView.AlternatingRowsDefaultCellStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = this.DataGridView.AlternatingRowsDefaultCellStyle.ForeColor; + } + else if (columnStyle != null && !columnStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = columnStyle.ForeColor; + } + else + { + inheritedCellStyleTmp.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (rowStyle != null && !rowStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = rowStyle.SelectionBackColor; + } + else if (!this.DataGridView.RowsDefaultCellStyle.SelectionBackColor.IsEmpty && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionBackColor.IsEmpty)) + { + inheritedCellStyleTmp.SelectionBackColor = this.DataGridView.RowsDefaultCellStyle.SelectionBackColor; + } + else if (rowIndex % 2 == 1 && !this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionBackColor; + } + else if (columnStyle != null && !columnStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = columnStyle.SelectionBackColor; + } + else + { + inheritedCellStyleTmp.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (rowStyle != null && !rowStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = rowStyle.SelectionForeColor; + } + else if (!this.DataGridView.RowsDefaultCellStyle.SelectionForeColor.IsEmpty && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionForeColor.IsEmpty)) + { + inheritedCellStyleTmp.SelectionForeColor = this.DataGridView.RowsDefaultCellStyle.SelectionForeColor; + } + else if (rowIndex % 2 == 1 && !this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = this.DataGridView.AlternatingRowsDefaultCellStyle.SelectionForeColor; + } + else if (columnStyle != null && !columnStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = columnStyle.SelectionForeColor; + } + else + { + inheritedCellStyleTmp.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyleTmp.Font = cellStyle.Font; + } + else if (rowStyle != null && rowStyle.Font != null) + { + inheritedCellStyleTmp.Font = rowStyle.Font; + } + else if (this.DataGridView.RowsDefaultCellStyle.Font != null && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.Font == null)) + { + inheritedCellStyleTmp.Font = this.DataGridView.RowsDefaultCellStyle.Font; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.Font != null) + { + inheritedCellStyleTmp.Font = this.DataGridView.AlternatingRowsDefaultCellStyle.Font; + } + else if (columnStyle != null && columnStyle.Font != null) + { + inheritedCellStyleTmp.Font = columnStyle.Font; + } + else + { + inheritedCellStyleTmp.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = cellStyle.NullValue; + } + else if (rowStyle != null && !rowStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = rowStyle.NullValue; + } + else if (!this.DataGridView.RowsDefaultCellStyle.IsNullValueDefault && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.IsNullValueDefault)) + { + inheritedCellStyleTmp.NullValue = this.DataGridView.RowsDefaultCellStyle.NullValue; + } + else if (rowIndex % 2 == 1 && + !this.DataGridView.AlternatingRowsDefaultCellStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = this.DataGridView.AlternatingRowsDefaultCellStyle.NullValue; + } + else if (columnStyle != null && !columnStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = columnStyle.NullValue; + } + else + { + inheritedCellStyleTmp.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (rowStyle != null && !rowStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = rowStyle.DataSourceNullValue; + } + else if (!this.DataGridView.RowsDefaultCellStyle.IsDataSourceNullValueDefault && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.IsDataSourceNullValueDefault)) + { + inheritedCellStyleTmp.DataSourceNullValue = this.DataGridView.RowsDefaultCellStyle.DataSourceNullValue; + } + else if (rowIndex % 2 == 1 && + !this.DataGridView.AlternatingRowsDefaultCellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = this.DataGridView.AlternatingRowsDefaultCellStyle.DataSourceNullValue; + } + else if (columnStyle != null && !columnStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = columnStyle.DataSourceNullValue; + } + else + { + inheritedCellStyleTmp.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = cellStyle.Format; + } + else if (rowStyle != null && rowStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = rowStyle.Format; + } + else if (this.DataGridView.RowsDefaultCellStyle.Format.Length != 0 && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.Format.Length == 0)) + { + inheritedCellStyleTmp.Format = this.DataGridView.RowsDefaultCellStyle.Format; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = this.DataGridView.AlternatingRowsDefaultCellStyle.Format; + } + else if (columnStyle != null && columnStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = columnStyle.Format; + } + else + { + inheritedCellStyleTmp.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = cellStyle.FormatProvider; + } + else if (rowStyle != null && !rowStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = rowStyle.FormatProvider; + } + else if (!this.DataGridView.RowsDefaultCellStyle.IsFormatProviderDefault && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.IsFormatProviderDefault)) + { + inheritedCellStyleTmp.FormatProvider = this.DataGridView.RowsDefaultCellStyle.FormatProvider; + } + else if (rowIndex % 2 == 1 && !this.DataGridView.AlternatingRowsDefaultCellStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = this.DataGridView.AlternatingRowsDefaultCellStyle.FormatProvider; + } + else if (columnStyle != null && !columnStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = columnStyle.FormatProvider; + } + else + { + inheritedCellStyleTmp.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = cellStyle.Alignment; + } + else if (rowStyle != null && rowStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = rowStyle.Alignment; + } + else if (this.DataGridView.RowsDefaultCellStyle.Alignment != DataGridViewContentAlignment.NotSet && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.Alignment == DataGridViewContentAlignment.NotSet)) + { + inheritedCellStyleTmp.AlignmentInternal = this.DataGridView.RowsDefaultCellStyle.Alignment; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = this.DataGridView.AlternatingRowsDefaultCellStyle.Alignment; + } + else if (columnStyle != null && columnStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = columnStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyleTmp.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = cellStyle.WrapMode; + } + else if (rowStyle != null && rowStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = rowStyle.WrapMode; + } + else if (this.DataGridView.RowsDefaultCellStyle.WrapMode != DataGridViewTriState.NotSet && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.WrapMode == DataGridViewTriState.NotSet)) + { + inheritedCellStyleTmp.WrapModeInternal = this.DataGridView.RowsDefaultCellStyle.WrapMode; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = this.DataGridView.AlternatingRowsDefaultCellStyle.WrapMode; + } + else if (columnStyle != null && columnStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = columnStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyleTmp.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = cellStyle.Tag; + } + else if (rowStyle != null && rowStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = rowStyle.Tag; + } + else if (this.DataGridView.RowsDefaultCellStyle.Tag != null && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.Tag == null)) + { + inheritedCellStyleTmp.Tag = this.DataGridView.RowsDefaultCellStyle.Tag; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = this.DataGridView.AlternatingRowsDefaultCellStyle.Tag; + } + else if (columnStyle != null && columnStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = columnStyle.Tag; + } + else + { + inheritedCellStyleTmp.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = cellStyle.Padding; + } + else if (rowStyle != null && rowStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = rowStyle.Padding; + } + else if (this.DataGridView.RowsDefaultCellStyle.Padding != Padding.Empty && + (rowIndex % 2 == 0 || this.DataGridView.AlternatingRowsDefaultCellStyle.Padding == Padding.Empty)) + { + inheritedCellStyleTmp.PaddingInternal = this.DataGridView.RowsDefaultCellStyle.Padding; + } + else if (rowIndex % 2 == 1 && this.DataGridView.AlternatingRowsDefaultCellStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = this.DataGridView.AlternatingRowsDefaultCellStyle.Padding; + } + else if (columnStyle != null && columnStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = columnStyle.Padding; + } + else + { + inheritedCellStyleTmp.PaddingInternal = dataGridViewStyle.Padding; + } + + return inheritedCellStyleTmp; + } + + internal DataGridViewCellStyle GetInheritedStyleInternal(int rowIndex) + { + return GetInheritedStyle(null, rowIndex, true /*includeColors*/); + } + + internal int GetPreferredHeight(int rowIndex, int width) + { + Debug.Assert(width > 0); + + if (this.DataGridView == null) + { + return -1; + } + + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + using( Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + return GetPreferredSize(g, dataGridViewCellStyle, rowIndex, new Size(width, 0)).Height; + } + } + + internal Size GetPreferredSize(int rowIndex) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + return GetPreferredSize(g, dataGridViewCellStyle, rowIndex, Size.Empty); + } + } + + /// + protected virtual Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + return new Size(-1, -1); + } + + internal static int GetPreferredTextHeight(Graphics g, + bool rightToLeft, + string text, + DataGridViewCellStyle cellStyle, + int maxWidth, + out bool widthTruncated) + { + Debug.Assert(maxWidth > 0); + + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(rightToLeft, cellStyle.Alignment, cellStyle.WrapMode); + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + return DataGridViewCell.MeasureTextHeight(g, text, cellStyle.Font, maxWidth, flags, out widthTruncated); + } + else + { + Size size = DataGridViewCell.MeasureTextSize(g, text, cellStyle.Font, flags); + widthTruncated = size.Width > maxWidth; + return size.Height; + } + } + + internal int GetPreferredWidth(int rowIndex, int height) + { + Debug.Assert(height > 0); + + if (this.DataGridView == null) + { + return -1; + } + + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + return GetPreferredSize(g, dataGridViewCellStyle, rowIndex, new Size(0, height)).Width; + } + } + + /// + protected virtual Size GetSize(int rowIndex) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + if (rowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedCell, "Size")); + } + Debug.Assert(this.owningColumn != null); + Debug.Assert(this.owningRow != null); + return new Size(this.owningColumn.Thickness, this.owningRow.GetHeight(rowIndex)); + } + + private string GetToolTipText(int rowIndex) + { + string toolTipText = this.ToolTipTextInternal; + if (this.DataGridView != null && + (this.DataGridView.VirtualMode || this.DataGridView.DataSource != null)) + { + toolTipText = this.DataGridView.OnCellToolTipTextNeeded(this.ColumnIndex, rowIndex, toolTipText); + } + return toolTipText; + } + + /// + protected virtual object GetValue(int rowIndex) + { + DataGridView dataGridView = this.DataGridView; + if (dataGridView != null) + { + if (rowIndex < 0 || rowIndex >= dataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.ColumnIndex < 0) + { + throw new InvalidOperationException(); + } + Debug.Assert(this.ColumnIndex < dataGridView.Columns.Count); + } + if (dataGridView == null || + (dataGridView.AllowUserToAddRowsInternal && rowIndex > -1 && rowIndex == dataGridView.NewRowIndex && rowIndex != dataGridView.CurrentCellAddress.Y) || + (!dataGridView.VirtualMode && this.OwningColumn != null && !this.OwningColumn.IsDataBound) || + rowIndex == -1 || + this.ColumnIndex == -1) + { + return this.Properties.GetObject(PropCellValue); + } + else if (this.OwningColumn != null && this.OwningColumn.IsDataBound) + { + DataGridView.DataGridViewDataConnection dataConnection = dataGridView.DataConnection; + if (dataConnection == null) + { + return null; + } + else if (dataConnection.CurrencyManager.Count <= rowIndex) + { + return this.Properties.GetObject(PropCellValue); + } + else + { + return dataConnection.GetValue(this.OwningColumn.BoundColumnIndex, this.ColumnIndex, rowIndex); + } + } + else + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(this.ColumnIndex >= 0); + return dataGridView.OnCellValueNeeded(this.ColumnIndex, rowIndex); + } + } + + internal object GetValueInternal(int rowIndex) + { + return GetValue(rowIndex); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) + { + DataGridView dgv = this.DataGridView; + if (dgv == null || dgv.EditingControl == null) + { + throw new InvalidOperationException(); + } + // Only add the control to the dataGridView's children if this hasn't been done yet since + // InitializeEditingControl can be called several times. + if (dgv.EditingControl.ParentInternal == null) + { + // Add editing control to the dataGridView hierarchy + dgv.EditingControl.CausesValidation = dgv.CausesValidation; + dgv.EditingPanel.CausesValidation = dgv.CausesValidation; + dgv.EditingControl.Visible = true; + Debug.Assert(!dgv.EditingPanel.ContainsFocus); + dgv.EditingPanel.Visible = false; + Debug.Assert(dgv.EditingPanel.ParentInternal == null); + dgv.Controls.Add(dgv.EditingPanel); + dgv.EditingPanel.Controls.Add(dgv.EditingControl); + Debug.Assert(dgv.IsSharedCellVisible(this, rowIndex)); + } + Debug.Assert(dgv.EditingControl.ParentInternal == dgv.EditingPanel); + Debug.Assert(dgv.EditingPanel.ParentInternal == dgv); + + if (AccessibilityImprovements.Level3) + { + if ((dgv.ComboBoxControlWasDetached && dgv.EditingControl is DataGridViewComboBoxEditingControl) || + (dgv.TextBoxControlWasDetached && dgv.EditingControl is DataGridViewTextBoxEditingControl)) + { + // Recreate control handle is necessary for cases when the same control was detached and then + // reattached to clear accessible hierarchy cache and not announce previous editing cell. + dgv.EditingControl.RecreateHandleCore(); + + dgv.ComboBoxControlWasDetached = false; + dgv.TextBoxControlWasDetached = false; + } + + dgv.EditingControlAccessibleObject.SetParent(AccessibilityObject); + AccessibilityObject.SetDetachableChild(dgv.EditingControl.AccessibilityObject); + AccessibilityObject.RaiseStructureChangedEvent(UnsafeNativeMethods.StructureChangeType.ChildAdded, dgv.EditingControlAccessibleObject.RuntimeId); + } + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool KeyDownUnsharesRow(KeyEventArgs e, int rowIndex) + { + return false; + } + + internal bool KeyDownUnsharesRowInternal(KeyEventArgs e, int rowIndex) + { + return KeyDownUnsharesRow(e, rowIndex); + } + + /// + public virtual bool KeyEntersEditMode(KeyEventArgs e) + { + return false; + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool KeyPressUnsharesRow(KeyPressEventArgs e, int rowIndex) + { + return false; + } + + internal bool KeyPressUnsharesRowInternal(KeyPressEventArgs e, int rowIndex) + { + return KeyPressUnsharesRow(e, rowIndex); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool KeyUpUnsharesRow(KeyEventArgs e, int rowIndex) + { + return false; + } + + internal bool KeyUpUnsharesRowInternal(KeyEventArgs e, int rowIndex) + { + return KeyUpUnsharesRow(e, rowIndex); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool LeaveUnsharesRow(int rowIndex, bool throughMouseClick) + { + return false; + } + + internal bool LeaveUnsharesRowInternal(int rowIndex, bool throughMouseClick) + { + return LeaveUnsharesRow(rowIndex, throughMouseClick); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public static int MeasureTextHeight(Graphics graphics, string text, Font font, int maxWidth, TextFormatFlags flags) + { + bool widthTruncated; + return DataGridViewCell.MeasureTextHeight(graphics, text, font, maxWidth, flags, out widthTruncated); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), // We don't want to use IDeviceContext here. + SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters") // out param OK here. + ] + public static int MeasureTextHeight(Graphics graphics, string text, Font font, int maxWidth, TextFormatFlags flags, out bool widthTruncated) + { + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + + if (font == null) + { + throw new ArgumentNullException("font"); + } + + if (maxWidth <= 0) + { + throw new ArgumentOutOfRangeException("maxWidth", SR.GetString(SR.InvalidLowBoundArgument, "maxWidth", (maxWidth).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (!DataGridViewUtilities.ValidTextFormatFlags(flags)) + { + throw new InvalidEnumArgumentException("flags", (int) flags, typeof(TextFormatFlags)); + } + + flags &= textFormatSupportedFlags; + // VSWhidbey 495922: dont use passed in graphics so we can optimze measurement + Size requiredSize = TextRenderer.MeasureText(text, font, new Size(maxWidth, System.Int32.MaxValue), flags); + widthTruncated = (requiredSize.Width > maxWidth); + return requiredSize.Height; + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public static Size MeasureTextPreferredSize(Graphics graphics, string text, Font font, float maxRatio, TextFormatFlags flags) + { + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + + if (font == null) + { + throw new ArgumentNullException("font"); + } + + if (maxRatio <= 0.0F) + { + throw new ArgumentOutOfRangeException("maxRatio", SR.GetString(SR.InvalidLowBoundArgument, "maxRatio", (maxRatio).ToString(CultureInfo.CurrentCulture), "0.0")); + } + + if (!DataGridViewUtilities.ValidTextFormatFlags(flags)) + { + throw new InvalidEnumArgumentException("flags", (int) flags, typeof(TextFormatFlags)); + } + + if (string.IsNullOrEmpty(text)) + { + return new Size(0, 0); + } + + Size textOneLineSize = DataGridViewCell.MeasureTextSize(graphics, text, font, flags); + if ((float)(textOneLineSize.Width / textOneLineSize.Height) <= maxRatio) + { + return textOneLineSize; + } + + flags &= textFormatSupportedFlags; + float maxWidth = (float) (textOneLineSize.Width * textOneLineSize.Width) / (float) textOneLineSize.Height / maxRatio * 1.1F; + Size textSize; + do + { + // VSWhidbey 495922: dont use passed in graphics so we can optimze measurement + textSize = TextRenderer.MeasureText(text, font, new Size((int)maxWidth, System.Int32.MaxValue), flags); + if ((float)(textSize.Width / textSize.Height) <= maxRatio || textSize.Width > (int)maxWidth) + { + return textSize; + } + maxWidth = (float)textSize.Width * 0.9F; + } + while (maxWidth > 1.0F); + return textSize; + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // We don't want to use IDeviceContext here. + ] + public static Size MeasureTextSize(Graphics graphics, string text, Font font, TextFormatFlags flags) + { + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + + if (font == null) + { + throw new ArgumentNullException("font"); + } + + if (!DataGridViewUtilities.ValidTextFormatFlags(flags)) + { + throw new InvalidEnumArgumentException("flags", (int) flags, typeof(TextFormatFlags)); + } + + flags &= textFormatSupportedFlags; + // VSWhidbey 495922: dont use passed in graphics so we can optimze measurement + return TextRenderer.MeasureText(text, font, new Size(System.Int32.MaxValue, System.Int32.MaxValue), flags); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public static int MeasureTextWidth(Graphics graphics, string text, Font font, int maxHeight, TextFormatFlags flags) + { + if (maxHeight <= 0) + { + throw new ArgumentOutOfRangeException("maxHeight", SR.GetString(SR.InvalidLowBoundArgument, "maxHeight", (maxHeight).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + Size oneLineSize = DataGridViewCell.MeasureTextSize(graphics, text, font, flags); + if (oneLineSize.Height >= maxHeight || (flags & TextFormatFlags.SingleLine) != 0) + { + return oneLineSize.Width; + } + else + { + flags &= textFormatSupportedFlags; + int lastFittingWidth = oneLineSize.Width; + float maxWidth = (float) lastFittingWidth * 0.9F; + Size textSize; + do + { + // VSWhidbey 495922: dont use passed in graphics so we can optimze measurement + textSize = TextRenderer.MeasureText(text, font, new Size((int)maxWidth, maxHeight), flags); + if (textSize.Height > maxHeight || textSize.Width > (int)maxWidth) + { + return lastFittingWidth; + } + else + { + lastFittingWidth = (int)maxWidth; + maxWidth = (float)textSize.Width * 0.9F; + } + } + while (maxWidth > 1.0F); + Debug.Assert(textSize.Height <= maxHeight); + return lastFittingWidth; + } + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseClickUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return false; + } + + internal bool MouseClickUnsharesRowInternal(DataGridViewCellMouseEventArgs e) + { + return MouseClickUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseDoubleClickUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return false; + } + + internal bool MouseDoubleClickUnsharesRowInternal(DataGridViewCellMouseEventArgs e) + { + return MouseDoubleClickUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return false; + } + + internal bool MouseDownUnsharesRowInternal(DataGridViewCellMouseEventArgs e) + { + return MouseDownUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseEnterUnsharesRow(int rowIndex) + { + return false; + } + + internal bool MouseEnterUnsharesRowInternal(int rowIndex) + { + return MouseEnterUnsharesRow(rowIndex); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseLeaveUnsharesRow(int rowIndex) + { + return false; + } + + internal bool MouseLeaveUnsharesRowInternal(int rowIndex) + { + return MouseLeaveUnsharesRow(rowIndex); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseMoveUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return false; + } + + internal bool MouseMoveUnsharesRowInternal(DataGridViewCellMouseEventArgs e) + { + return MouseMoveUnsharesRow(e); + } + + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Unshares is OK. + ] + protected virtual bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return false; + } + + internal bool MouseUpUnsharesRowInternal(DataGridViewCellMouseEventArgs e) + { + return MouseUpUnsharesRow(e); + } + + private void OnCellDataAreaMouseEnterInternal(int rowIndex) + { + Debug.Assert(this.DataGridView != null); + if (!this.DataGridView.ShowCellToolTips) + { + return; + } + + // Don't show a tooltip for edited cells with an editing control + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X != -1 && + ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == rowIndex && + this.DataGridView.EditingControl != null) + { + Debug.Assert(this.DataGridView.IsCurrentCellInEditMode); + return; + } + + // get the tool tip string + string toolTipText = GetToolTipText(rowIndex); + + if (String.IsNullOrEmpty(toolTipText)) + { + if (this.FormattedValueType == stringType) + { + if (rowIndex != -1 && this.OwningColumn != null) + { + int width = GetPreferredWidth(rowIndex, this.OwningRow.Height); + int height = GetPreferredHeight(rowIndex, this.OwningColumn.Width); + + if (this.OwningColumn.Width < width || this.OwningRow.Height < height) + { + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + string editedFormattedValue = GetEditedFormattedValue(GetValue(rowIndex), + rowIndex, + ref dataGridViewCellStyle, + DataGridViewDataErrorContexts.Display) as string; + if (!string.IsNullOrEmpty(editedFormattedValue)) + { + toolTipText = TruncateToolTipText(editedFormattedValue); + } + } + } + else if ((rowIndex != -1 && this.OwningRow != null && this.DataGridView.RowHeadersVisible && this.DataGridView.RowHeadersWidth > 0 && this.OwningColumn == null) || + rowIndex == -1) + { + // we are on a header cell. + Debug.Assert(this is DataGridViewHeaderCell); + string stringValue = GetValue(rowIndex) as string; + if (!string.IsNullOrEmpty(stringValue)) + { + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + Rectangle contentBounds = GetContentBounds(g, dataGridViewCellStyle, rowIndex); + + bool widthTruncated = false; + int preferredHeight = 0; + if (contentBounds.Width > 0) + { + preferredHeight = DataGridViewCell.GetPreferredTextHeight(g, + this.DataGridView.RightToLeftInternal, + stringValue, + dataGridViewCellStyle, + contentBounds.Width, + out widthTruncated); + } + else + { + widthTruncated = true; + } + if (preferredHeight > contentBounds.Height || widthTruncated) + { + toolTipText = TruncateToolTipText(stringValue); + } + } + } + } + } + } + else if (this.DataGridView.IsRestricted) + { + // In semi trust (demand for AllWindows fails), we truncate the tooltip at 256 if it exceeds 288 characters. + toolTipText = TruncateToolTipText(toolTipText); + } + + if (!String.IsNullOrEmpty(toolTipText)) + { + this.DataGridView.ActivateToolTip(true /*activate*/, toolTipText, this.ColumnIndex, rowIndex); + } + + // for debugging + // Console.WriteLine("OnCellDATA_AreaMouseENTER. ToolTipText : " + toolTipText); + } + + private void OnCellDataAreaMouseLeaveInternal() + { + if (this.DataGridView.IsDisposed) + { + return; + } + + this.DataGridView.ActivateToolTip(false /*activate*/, string.Empty, -1, -1); + // for debugging + // Console.WriteLine("OnCellDATA_AreaMouseLEAVE"); + } + + private void OnCellErrorAreaMouseEnterInternal(int rowIndex) + { + string errorText = GetErrorText(rowIndex); + Debug.Assert(!String.IsNullOrEmpty(errorText), "if we entered the cell error area then an error was painted, so we should have an error"); + this.DataGridView.ActivateToolTip(true /*activate*/, errorText, this.ColumnIndex, rowIndex); + + // for debugging + // Console.WriteLine("OnCellERROR_AreaMouseENTER. ErrorText : " + errorText); + } + + private void OnCellErrorAreaMouseLeaveInternal() + { + this.DataGridView.ActivateToolTip(false /*activate*/, string.Empty, -1, -1); + // for debugging + // Console.WriteLine("OnCellERROR_AreaMouseLEAVE"); + } + + /// + protected virtual void OnClick(DataGridViewCellEventArgs e) + { + } + + internal void OnClickInternal(DataGridViewCellEventArgs e) + { + OnClick(e); + } + + internal void OnCommonChange() + { + if (this.DataGridView != null && !this.DataGridView.IsDisposed && !this.DataGridView.Disposing) + { + if (this.RowIndex == -1) + { + // Invalidate and autosize column + this.DataGridView.OnColumnCommonChange(this.ColumnIndex); + } + else + { + // Invalidate and autosize cell + this.DataGridView.OnCellCommonChange(this.ColumnIndex, this.RowIndex); + } + } + } + + /// + protected virtual void OnContentClick(DataGridViewCellEventArgs e) + { + } + + internal void OnContentClickInternal(DataGridViewCellEventArgs e) + { + OnContentClick(e); + } + + /// + protected virtual void OnContentDoubleClick(DataGridViewCellEventArgs e) + { + } + + internal void OnContentDoubleClickInternal(DataGridViewCellEventArgs e) + { + OnContentDoubleClick(e); + } + + /// + protected virtual void OnDoubleClick(DataGridViewCellEventArgs e) + { + } + + internal void OnDoubleClickInternal(DataGridViewCellEventArgs e) + { + OnDoubleClick(e); + } + + /// + protected virtual void OnEnter(int rowIndex, bool throughMouseClick) + { + } + + internal void OnEnterInternal(int rowIndex, bool throughMouseClick) + { + OnEnter(rowIndex, throughMouseClick); + } + + internal void OnKeyDownInternal(KeyEventArgs e, int rowIndex) + { + OnKeyDown(e, rowIndex); + } + + /// + protected virtual void OnKeyDown(KeyEventArgs e, int rowIndex) + { + } + + internal void OnKeyPressInternal(KeyPressEventArgs e, int rowIndex) + { + OnKeyPress(e, rowIndex); + } + + /// + protected virtual void OnKeyPress(KeyPressEventArgs e, int rowIndex) + { + } + + /// + protected virtual void OnKeyUp(KeyEventArgs e, int rowIndex) + { + } + + /// + internal void OnKeyUpInternal(KeyEventArgs e, int rowIndex) + { + OnKeyUp(e, rowIndex); + } + + /// + protected virtual void OnLeave(int rowIndex, bool throughMouseClick) + { + } + + internal void OnLeaveInternal(int rowIndex, bool throughMouseClick) + { + OnLeave(rowIndex, throughMouseClick); + } + + /// + protected virtual void OnMouseClick(DataGridViewCellMouseEventArgs e) + { + } + + internal void OnMouseClickInternal(DataGridViewCellMouseEventArgs e) + { + OnMouseClick(e); + } + + /// + protected virtual void OnMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + } + + internal void OnMouseDoubleClickInternal(DataGridViewCellMouseEventArgs e) + { + OnMouseDoubleClick(e); + } + + /// + protected virtual void OnMouseDown(DataGridViewCellMouseEventArgs e) + { + } + + internal void OnMouseDownInternal(DataGridViewCellMouseEventArgs e) + { + this.DataGridView.CellMouseDownInContentBounds = GetContentBounds(e.RowIndex).Contains(e.X, e.Y); + + if (((this.ColumnIndex < 0 || e.RowIndex < 0) && this.DataGridView.ApplyVisualStylesToHeaderCells) || + ((this.ColumnIndex >= 0 && e.RowIndex >= 0) && this.DataGridView.ApplyVisualStylesToInnerCells)) + { + DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + OnMouseDown(e); + } + + /// + protected virtual void OnMouseEnter(int rowIndex) + { + } + + internal void OnMouseEnterInternal(int rowIndex) + { + OnMouseEnter(rowIndex); + } + + /// + protected virtual void OnMouseLeave(int rowIndex) + { + } + + internal void OnMouseLeaveInternal(int rowIndex) + { + switch (this.CurrentMouseLocation) + { + case DATAGRIDVIEWCELL_flagDataArea: + OnCellDataAreaMouseLeaveInternal(); + break; + case DATAGRIDVIEWCELL_flagErrorArea: + OnCellErrorAreaMouseLeaveInternal(); + break; + case DATAGRIDVIEWCELL_flagAreaNotSet: + // Microsoft: there seems to be a bug in setting the HorizontalOffset when scrolling + // Debug.Assert(false, "if we leave the cell we should already have set the CurrentMouseLocation to Data Area or Error Area"); + break; + default: + Debug.Assert(false, "there are only three possible choices for the CurrentMouseLocation"); + break; + } + + this.CurrentMouseLocation = DATAGRIDVIEWCELL_flagAreaNotSet; + OnMouseLeave(rowIndex); + } + + /// + protected virtual void OnMouseMove(DataGridViewCellMouseEventArgs e) + { + } + + internal void OnMouseMoveInternal(DataGridViewCellMouseEventArgs e) + { + byte mouseLocation = this.CurrentMouseLocation; + UpdateCurrentMouseLocation(e); + Debug.Assert(this.CurrentMouseLocation != DATAGRIDVIEWCELL_flagAreaNotSet); + switch (mouseLocation) + { + case DATAGRIDVIEWCELL_flagAreaNotSet: + if (this.CurrentMouseLocation == DATAGRIDVIEWCELL_flagDataArea) + { + OnCellDataAreaMouseEnterInternal(e.RowIndex); + } + else + { + OnCellErrorAreaMouseEnterInternal(e.RowIndex); + } + break; + case DATAGRIDVIEWCELL_flagDataArea: + if (this.CurrentMouseLocation == DATAGRIDVIEWCELL_flagErrorArea) + { + OnCellDataAreaMouseLeaveInternal(); + OnCellErrorAreaMouseEnterInternal(e.RowIndex); + } + break; + case DATAGRIDVIEWCELL_flagErrorArea: + if (this.CurrentMouseLocation == DATAGRIDVIEWCELL_flagDataArea) + { + OnCellErrorAreaMouseLeaveInternal(); + OnCellDataAreaMouseEnterInternal(e.RowIndex); + } + break; + default: + Debug.Fail("there are only three choices for CurrentMouseLocation"); + break; + } + OnMouseMove(e); + } + + /// + protected virtual void OnMouseUp(DataGridViewCellMouseEventArgs e) + { + } + + internal void OnMouseUpInternal(DataGridViewCellMouseEventArgs e) + { + int x = e.X; + int y = e.Y; + + if (((this.ColumnIndex < 0 || e.RowIndex < 0) && this.DataGridView.ApplyVisualStylesToHeaderCells) || + ((this.ColumnIndex >= 0 && e.RowIndex >= 0) && this.DataGridView.ApplyVisualStylesToInnerCells)) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + + if (e.Button == MouseButtons.Left && GetContentBounds(e.RowIndex).Contains(x, y)) + { + this.DataGridView.OnCommonCellContentClick(e.ColumnIndex, e.RowIndex, e.Clicks > 1); + } + + if (this.DataGridView != null && e.ColumnIndex < this.DataGridView.Columns.Count && e.RowIndex < this.DataGridView.Rows.Count) + { + OnMouseUp(e); + } + } + + /// + protected override void OnDataGridViewChanged() + { + if (this.HasStyle) + { + if (this.DataGridView == null) + { + this.Style.RemoveScope(DataGridViewCellStyleScopes.Cell); + } + else + { + this.Style.AddScope(this.DataGridView, DataGridViewCellStyleScopes.Cell); + } + } + base.OnDataGridViewChanged(); + } + + /// + protected virtual void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + } + + internal void PaintInternal(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + Paint(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + value, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts); + } + + internal static bool PaintBackground(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.Background) != 0; + } + + internal static bool PaintBorder(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.Border) != 0; + } + + /// + protected virtual void PaintBorder(Graphics graphics, + Rectangle clipBounds, + Rectangle bounds, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle) + { + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null) + { + return; + } + + // Using system colors for non-single grid colors for now + int y1, y2; + + Pen penControlDark = null, penControlLightLight = null; + Pen penBackColor = this.DataGridView.GetCachedPen(cellStyle.BackColor); + Pen penGridColor = this.DataGridView.GridPen; + + GetContrastedPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + + int dividerThickness = this.owningColumn == null ? 0 : this.owningColumn.DividerWidth; + if (dividerThickness != 0) + { + if (dividerThickness > bounds.Width) + { + dividerThickness = bounds.Width; + } + Color dividerWidthColor; + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.Single: + dividerWidthColor = this.DataGridView.GridPen.Color; + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + dividerWidthColor = SystemColors.ControlLightLight; + break; + + default: /* ie DataGridViewAdvancedCellBorderStyle.Outset, DataGridViewAdvancedCellBorderStyle.OutsetPartial, DataGridViewAdvancedCellBorderStyle.None */ + dividerWidthColor = SystemColors.ControlDark; + break; + } + graphics.FillRectangle(this.DataGridView.GetCachedBrush(dividerWidthColor), + this.DataGridView.RightToLeftInternal ? bounds.X : bounds.Right - dividerThickness, + bounds.Y, + dividerThickness, + bounds.Height); + if (this.DataGridView.RightToLeftInternal) + { + bounds.X += dividerThickness; + } + bounds.Width -= dividerThickness; + if (bounds.Width <= 0) + { + return; + } + } + + dividerThickness = this.owningRow == null ? 0 : this.owningRow.DividerHeight; + if (dividerThickness != 0) + { + if (dividerThickness > bounds.Height) + { + dividerThickness = bounds.Height; + } + Color dividerHeightColor; + switch (advancedBorderStyle.Bottom) + { + case DataGridViewAdvancedCellBorderStyle.Single: + dividerHeightColor = this.DataGridView.GridPen.Color; + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + dividerHeightColor = SystemColors.ControlLightLight; + break; + + default: /* ie DataGridViewAdvancedCellBorderStyle.Outset, DataGridViewAdvancedCellBorderStyle.OutsetPartial, DataGridViewAdvancedCellBorderStyle.None */ + dividerHeightColor = SystemColors.ControlDark; + break; + } + graphics.FillRectangle(this.DataGridView.GetCachedBrush(dividerHeightColor), bounds.X, bounds.Bottom - dividerThickness, bounds.Width, dividerThickness); + bounds.Height -= dividerThickness; + if (bounds.Height <= 0) + { + return; + } + } + + if (advancedBorderStyle.All == DataGridViewAdvancedCellBorderStyle.None) + { + return; + } + + switch (advancedBorderStyle.Left) + { + case DataGridViewAdvancedCellBorderStyle.Single: + graphics.DrawLine(penGridColor, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + graphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + y1 = bounds.Y + 2; + y2 = bounds.Bottom - 3; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + y1++; + } + else if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + graphics.DrawLine(penBackColor, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + graphics.DrawLine(penControlLightLight, bounds.X, y1, bounds.X, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + graphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + graphics.DrawLine(penControlLightLight, bounds.X + 1, y1, bounds.X + 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, bounds.X + 1, y1, bounds.X + 1, y2); + break; + } + + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.Single: + graphics.DrawLine(penGridColor, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + graphics.DrawLine(penControlLightLight, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + graphics.DrawLine(penControlDark, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + y1 = bounds.Y + 2; + y2 = bounds.Bottom - 3; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + y1++; + } + else if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + graphics.DrawLine(penBackColor, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + graphics.DrawLine(penControlDark, bounds.Right - 2, bounds.Y, bounds.Right - 2, bounds.Bottom - 1); + graphics.DrawLine(penControlLightLight, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.Inset) + { + y2++; + } + graphics.DrawLine(penControlLightLight, bounds.Right - 2, bounds.Y, bounds.Right - 2, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + } + + int x1, x2; + switch (advancedBorderStyle.Top) + { + case DataGridViewAdvancedCellBorderStyle.Single: + graphics.DrawLine(penGridColor, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Inset || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Outset) + { + x2--; + } + graphics.DrawLine(penControlDark, x1, bounds.Y, x2, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Inset || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Outset) + { + x2--; + } + graphics.DrawLine(penControlLightLight, x1, bounds.Y, x2, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x1++; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + } + if (advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x2--; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + } + graphics.DrawLine(penBackColor, x1, bounds.Y, x2, bounds.Y); + graphics.DrawLine(penControlLightLight, x1 + 1, bounds.Y, x2 - 1, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + x1 = bounds.X; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial && + advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None) + { + x1++; + } + x2 = bounds.Right - 2; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) + { + x2++; + } + graphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + graphics.DrawLine(penControlLightLight, x1, bounds.Y + 1, x2, bounds.Y + 1); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + x1 = bounds.X; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial && + advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None) + { + x1++; + } + x2 = bounds.Right - 2; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) + { + x2++; + } + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + graphics.DrawLine(penControlDark, x1, bounds.Y + 1, x2, bounds.Y + 1); + break; + } + + switch (advancedBorderStyle.Bottom) + { + case DataGridViewAdvancedCellBorderStyle.Single: + graphics.DrawLine(penGridColor, bounds.X, bounds.Bottom - 1, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + x2 = bounds.Right - 1; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Bottom - 1, x2, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + x2--; + } + graphics.DrawLine(penControlDark, x1, bounds.Bottom - 1, x2, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x1++; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + } + if (advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x2--; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + } + graphics.DrawLine(penBackColor, x1, bounds.Bottom - 1, x2, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, x1 + 1, bounds.Bottom - 1, x2 - 1, bounds.Bottom - 1); + break; + } + +#if DGV_GDI + // Using system colors for non-single grid colors for now + int y1, y2; + + WindowsPen penControlDark = null, penControlLightLight = null; + WindowsPen penBackColor = this.DataGridView.GetCachedWindowsPen(cellStyle.BackColor); + WindowsPen penGridColor = this.DataGridView.GetCachedWindowsPen(this.DataGridView.GridColor); + + GetContrastedWindowsPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + + using (WindowsGraphics windowsGraphics = WindowsGraphics.FromGraphics(graphics)) + { + int dividerThickness = this.owningColumn == null ? 0 : this.owningColumn.DividerWidth; + if (dividerThickness != 0) + { + if (dividerThickness > bounds.Width) + { + dividerThickness = bounds.Width; + } + Color dividerWidthColor; + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.Single: + dividerWidthColor = this.DataGridView.GridPen.Color; + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + dividerWidthColor = SystemColors.ControlLightLight; + break; + + default: /* ie DataGridViewAdvancedCellBorderStyle.Outset, DataGridViewAdvancedCellBorderStyle.OutsetPartial, DataGridViewAdvancedCellBorderStyle.None */ + dividerWidthColor = SystemColors.ControlDark; + break; + } + windowsGraphics.FillRectangle(this.DataGridView.GetCachedWindowsBrush(dividerWidthColor), + this.DataGridView.RightToLeftInternal ? bounds.X : bounds.Right - dividerThickness, + bounds.Y, + dividerThickness, + bounds.Height); + if (this.DataGridView.RightToLeftInternal) + { + bounds.X += dividerThickness; + } + bounds.Width -= dividerThickness; + if (bounds.Width <= 0) + { + return; + } + } + + dividerThickness = this.owningRow == null ? 0 : this.owningRow.DividerHeight; + if (dividerThickness != 0) + { + if (dividerThickness > bounds.Height) + { + dividerThickness = bounds.Height; + } + Color dividerHeightColor; + switch (advancedBorderStyle.Bottom) + { + case DataGridViewAdvancedCellBorderStyle.Single: + dividerHeightColor = this.DataGridView.GridPen.Color; + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + dividerHeightColor = SystemColors.ControlLightLight; + break; + + default: /* ie DataGridViewAdvancedCellBorderStyle.Outset, DataGridViewAdvancedCellBorderStyle.OutsetPartial, DataGridViewAdvancedCellBorderStyle.None */ + dividerHeightColor = SystemColors.ControlDark; + break; + } + windowsGraphics.FillRectangle(this.DataGridView.GetCachedWindowsBrush(dividerHeightColor), bounds.X, bounds.Bottom - dividerThickness, bounds.Width, dividerThickness); + bounds.Height -= dividerThickness; + if (bounds.Height <= 0) + { + return; + } + } + + if (advancedBorderStyle.All == DataGridViewAdvancedCellBorderStyle.None) + { + return; + } + + switch (advancedBorderStyle.Left) + { + case DataGridViewAdvancedCellBorderStyle.Single: + windowsGraphics.DrawLine(penGridColor, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + windowsGraphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + windowsGraphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + y1 = bounds.Y + 2; + y2 = bounds.Bottom - 3; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + y1++; + } + else if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + windowsGraphics.DrawLine(penBackColor, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlLightLight, bounds.X, y1, bounds.X, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + windowsGraphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlLightLight, bounds.X + 1, y1, bounds.X + 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + windowsGraphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlDark, bounds.X + 1, y1, bounds.X + 1, y2); + break; + } + + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.Single: + windowsGraphics.DrawLine(penGridColor, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + windowsGraphics.DrawLine(penControlLightLight, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + windowsGraphics.DrawLine(penControlDark, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + y1 = bounds.Y + 2; + y2 = bounds.Bottom - 3; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + y1++; + } + else if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + windowsGraphics.DrawLine(penBackColor, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlDark, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial) + { + y2++; + } + windowsGraphics.DrawLine(penControlDark, bounds.Right - 2, bounds.Y, bounds.Right - 2, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlLightLight, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + y1 = bounds.Y + 1; + y2 = bounds.Bottom - 1; + if (advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Top == DataGridViewAdvancedCellBorderStyle.None) + { + y1--; + } + if (advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Bottom == DataGridViewAdvancedCellBorderStyle.Inset) + { + y2++; + } + windowsGraphics.DrawLine(penControlLightLight, bounds.Right - 2, bounds.Y, bounds.Right - 2, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlDark, bounds.Right - 1, y1, bounds.Right - 1, y2); + break; + } + + int x1, x2; + switch (advancedBorderStyle.Top) + { + case DataGridViewAdvancedCellBorderStyle.Single: + windowsGraphics.DrawLine(penGridColor, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Inset || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Outset) + { + x2--; + } + windowsGraphics.DrawLine(penControlDark, x1, bounds.Y, x2, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Inset || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.Outset) + { + x2--; + } + windowsGraphics.DrawLine(penControlLightLight, x1, bounds.Y, x2, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x1++; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + } + if (advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x2--; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + } + windowsGraphics.DrawLine(penBackColor, x1, bounds.Y, x2, bounds.Y); + windowsGraphics.DrawLine(penControlLightLight, x1 + 1, bounds.Y, x2 - 1, bounds.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + x1 = bounds.X; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial && + advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None) + { + x1++; + } + x2 = bounds.Right - 2; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) + { + x2++; + } + windowsGraphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + windowsGraphics.DrawLine(penControlLightLight, x1, bounds.Y + 1, x2, bounds.Y + 1); + break; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + x1 = bounds.X; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial && + advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None) + { + x1++; + } + x2 = bounds.Right - 2; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetPartial || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.None) + { + x2++; + } + windowsGraphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + windowsGraphics.DrawLine(penControlDark, x1, bounds.Y + 1, x2, bounds.Y + 1); + break; + } + + switch (advancedBorderStyle.Bottom) + { + case DataGridViewAdvancedCellBorderStyle.Single: + windowsGraphics.DrawLine(penGridColor, bounds.X, bounds.Bottom - 1, bounds.Right - 1, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + x2 = bounds.Right - 1; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + windowsGraphics.DrawLine(penControlLightLight, bounds.X, bounds.Bottom - 1, x2, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble) + { + x2--; + } + windowsGraphics.DrawLine(penControlDark, x1, bounds.Bottom - 1, x2, bounds.Bottom - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + x1 = bounds.X; + x2 = bounds.Right - 1; + if (advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Left != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x1++; + if (advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Left == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x1++; + } + } + if (advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.None /* && advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetPartial*/) + { + x2--; + if (advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.OutsetDouble || + advancedBorderStyle.Right == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + x2--; + } + } + windowsGraphics.DrawLine(penBackColor, x1, bounds.Bottom - 1, x2, bounds.Bottom - 1); + windowsGraphics.DrawLine(penControlDark, x1 + 1, bounds.Bottom - 1, x2 - 1, bounds.Bottom - 1); + break; + } + } +#endif // DGV_GDI + } + + internal static bool PaintContentBackground(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.ContentBackground) != 0; + } + + internal static bool PaintContentForeground(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.ContentForeground) != 0; + } + + /// + protected virtual void PaintErrorIcon(Graphics graphics, Rectangle clipBounds, Rectangle cellValueBounds, string errorText) + { + if (!string.IsNullOrEmpty(errorText) && + cellValueBounds.Width >= DATAGRIDVIEWCELL_iconMarginWidth*2+iconsWidth && + cellValueBounds.Height >= DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight) + { + PaintErrorIcon(graphics, ComputeErrorIconBounds(cellValueBounds)); + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")] + private static void PaintErrorIcon(Graphics graphics, Rectangle iconBounds) + { + Bitmap bmp = DataGridViewCell.ErrorBitmap; + if (bmp != null) + { + lock (bmp) + { + graphics.DrawImage(bmp, iconBounds, 0, 0, iconsWidth, iconsHeight, GraphicsUnit.Pixel); + } + } + } + + internal void PaintErrorIcon(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Rectangle cellBounds, Rectangle cellValueBounds, string errorText) + { + if (!string.IsNullOrEmpty(errorText) && + cellValueBounds.Width >= DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth && + cellValueBounds.Height >= DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight) + { + Rectangle iconBounds = GetErrorIconBounds(graphics, cellStyle, rowIndex); + if (iconBounds.Width >= DATAGRIDVIEWCELL_iconMarginWidth && iconBounds.Height >= iconsHeight) + { + iconBounds.X += cellBounds.X; + iconBounds.Y += cellBounds.Y; + PaintErrorIcon(graphics, iconBounds); + } + } + } + + internal static bool PaintErrorIcon(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.ErrorIcon) != 0; + } + + internal static bool PaintFocus(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.Focus) != 0; + } + + static internal void PaintPadding(Graphics graphics, + Rectangle bounds, + DataGridViewCellStyle cellStyle, + Brush br, + bool rightToLeft) + { + Rectangle rectPadding; + if (rightToLeft) + { + rectPadding = new Rectangle(bounds.X, bounds.Y, cellStyle.Padding.Right, bounds.Height); + graphics.FillRectangle(br, rectPadding); + rectPadding.X = bounds.Right - cellStyle.Padding.Left; + rectPadding.Width = cellStyle.Padding.Left; + graphics.FillRectangle(br, rectPadding); + rectPadding.X = bounds.Left + cellStyle.Padding.Right; + } + else + { + rectPadding = new Rectangle(bounds.X, bounds.Y, cellStyle.Padding.Left, bounds.Height); + graphics.FillRectangle(br, rectPadding); + rectPadding.X = bounds.Right - cellStyle.Padding.Right; + rectPadding.Width = cellStyle.Padding.Right; + graphics.FillRectangle(br, rectPadding); + rectPadding.X = bounds.Left + cellStyle.Padding.Left; + } + rectPadding.Y = bounds.Y; + rectPadding.Width = bounds.Width - cellStyle.Padding.Horizontal; + rectPadding.Height = cellStyle.Padding.Top; + graphics.FillRectangle(br, rectPadding); + rectPadding.Y = bounds.Bottom - cellStyle.Padding.Bottom; + rectPadding.Height = cellStyle.Padding.Bottom; + graphics.FillRectangle(br, rectPadding); + } + + internal static bool PaintSelectionBackground(DataGridViewPaintParts paintParts) + { + return (paintParts & DataGridViewPaintParts.SelectionBackground) != 0; + } + + internal void PaintWork(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + Debug.Assert(this.DataGridView != null); + DataGridView dataGridView = this.DataGridView; + int columnIndex = this.ColumnIndex; + object formattedValue, value = GetValue(rowIndex); + string errorText = GetErrorText(rowIndex); + if (columnIndex > -1 && rowIndex > -1) + { + formattedValue = GetEditedFormattedValue(value, rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.Display); + } + else + { + // No formatting applied on header cells. + formattedValue = value; + } + + DataGridViewCellPaintingEventArgs dgvcpe = dataGridView.CellPaintingEventArgs; + dgvcpe.SetProperties(graphics, + clipBounds, + cellBounds, + rowIndex, + columnIndex, + cellState, + value, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts); + dataGridView.OnCellPainting(dgvcpe); + if (dgvcpe.Handled) + { + return; + } + + Paint(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + value, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts); + } + + /// + public virtual object ParseFormattedValue(object formattedValue, + DataGridViewCellStyle cellStyle, + TypeConverter formattedValueTypeConverter, + TypeConverter valueTypeConverter) + { + return ParseFormattedValueInternal(this.ValueType, formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter); + } + + internal object ParseFormattedValueInternal(Type valueType, + object formattedValue, + DataGridViewCellStyle cellStyle, + TypeConverter formattedValueTypeConverter, + TypeConverter valueTypeConverter) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + if (this.FormattedValueType == null) + { + throw new FormatException(SR.GetString(SR.DataGridViewCell_FormattedValueTypeNull)); + } + if (valueType == null) + { + throw new FormatException(SR.GetString(SR.DataGridViewCell_ValueTypeNull)); + } + if (formattedValue == null || + !this.FormattedValueType.IsAssignableFrom(formattedValue.GetType())) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewCell_FormattedValueHasWrongType), "formattedValue"); + } + return Formatter.ParseObject(formattedValue, + valueType, + this.FormattedValueType, + valueTypeConverter == null ? this.ValueTypeConverter : valueTypeConverter /*sourceConverter*/, + formattedValueTypeConverter == null ? this.FormattedValueTypeConverter : formattedValueTypeConverter /*targetConverter*/, + cellStyle.FormatProvider, + cellStyle.NullValue, + cellStyle.IsDataSourceNullValueDefault ? Formatter.GetDefaultDataSourceNullValue(valueType) : cellStyle.DataSourceNullValue); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual void PositionEditingControl( + bool setLocation, + bool setSize, + Rectangle cellBounds, + Rectangle cellClip, + DataGridViewCellStyle cellStyle, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded, + bool isFirstDisplayedColumn, + bool isFirstDisplayedRow) + { + Rectangle editingControlBounds = PositionEditingPanel(cellBounds, + cellClip, + cellStyle, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + isFirstDisplayedColumn, + isFirstDisplayedRow); + if (setLocation) + { + this.DataGridView.EditingControl.Location = new Point(editingControlBounds.X, editingControlBounds.Y); + } + if (setSize) + { + this.DataGridView.EditingControl.Size = new Size(editingControlBounds.Width, editingControlBounds.Height); + } + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters") // singleVerticalBorderAdded/singleHorizontalBorderAdded names are OK + ] + // Positions the editing panel and returns the normal bounds of the editing control, within the editing panel. + public virtual Rectangle PositionEditingPanel(Rectangle cellBounds, + Rectangle cellClip, + DataGridViewCellStyle cellStyle, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded, + bool isFirstDisplayedColumn, + bool isFirstDisplayedRow) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(); + } + + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + + dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + isFirstDisplayedColumn, + isFirstDisplayedRow); + + Rectangle borderAndPaddingWidths = BorderWidths(dgvabsEffective); + borderAndPaddingWidths.X += cellStyle.Padding.Left; + borderAndPaddingWidths.Y += cellStyle.Padding.Top; + borderAndPaddingWidths.Width += cellStyle.Padding.Right; + borderAndPaddingWidths.Height += cellStyle.Padding.Bottom; + int xEditingPanel, wEditingPanel = cellBounds.Width; + int xEditingControl, wEditingControl; + int yEditingPanel, hEditingPanel = cellBounds.Height; + int yEditingControl, hEditingControl; + + if (cellClip.X - cellBounds.X >= borderAndPaddingWidths.X) + { + xEditingPanel = cellClip.X; + wEditingPanel -= cellClip.X - cellBounds.X; + } + else + { + xEditingPanel = cellBounds.X + borderAndPaddingWidths.X; + wEditingPanel -= borderAndPaddingWidths.X; + } + + if (cellClip.Right <= cellBounds.Right - borderAndPaddingWidths.Width) + { + wEditingPanel -= cellBounds.Right - cellClip.Right; + } + else + { + wEditingPanel -= borderAndPaddingWidths.Width; + } + + xEditingControl = cellBounds.X - cellClip.X; + wEditingControl = cellBounds.Width - borderAndPaddingWidths.X - borderAndPaddingWidths.Width; + if (cellClip.Y - cellBounds.Y >= borderAndPaddingWidths.Y) + { + yEditingPanel = cellClip.Y; + hEditingPanel -= cellClip.Y - cellBounds.Y; + } + else + { + yEditingPanel = cellBounds.Y + borderAndPaddingWidths.Y; + hEditingPanel -= borderAndPaddingWidths.Y; + } + + if (cellClip.Bottom <= cellBounds.Bottom - borderAndPaddingWidths.Height) + { + hEditingPanel -= cellBounds.Bottom - cellClip.Bottom; + } + else + { + hEditingPanel -= borderAndPaddingWidths.Height; + } + + yEditingControl = cellBounds.Y - cellClip.Y; + hEditingControl = cellBounds.Height - borderAndPaddingWidths.Y - borderAndPaddingWidths.Height; + this.DataGridView.EditingPanel.Location = new Point(xEditingPanel, yEditingPanel); + this.DataGridView.EditingPanel.Size = new Size(wEditingPanel, hEditingPanel); + /* Code was added for VSWhidbey 274393 and removed for 461211. + if (this.DataGridView.RightToLeftInternal) + { + xEditingControl = wEditingPanel - xEditingControl - wEditingControl; + } + */ + return new Rectangle(xEditingControl, yEditingControl, wEditingControl, hEditingControl); + } + + /// + protected virtual bool SetValue(int rowIndex, object value) + { + object originalValue = null; + DataGridView dataGridView = this.DataGridView; + if (dataGridView != null && !dataGridView.InSortOperation) + { + originalValue = GetValue(rowIndex); + } + + if (dataGridView != null && this.OwningColumn != null && this.OwningColumn.IsDataBound) + { + DataGridView.DataGridViewDataConnection dataConnection = dataGridView.DataConnection; + if (dataConnection == null) + { + return false; + } + else if (dataConnection.CurrencyManager.Count <= rowIndex) + { + if (value != null || this.Properties.ContainsObject(PropCellValue)) + { + this.Properties.SetObject(PropCellValue, value); + } + } + else + { + if (dataConnection.PushValue(this.OwningColumn.BoundColumnIndex, this.ColumnIndex, rowIndex, value)) + { + if (this.DataGridView == null || this.OwningRow == null || this.OwningRow.DataGridView == null) + { + // As a result of pushing the value in the back end, the data grid view row and/or data grid view cell + // became disconnected from the DataGridView. + // Return true because the operation succeded. + // However, because the row which was edited became disconnected from the DataGridView, + // do not mark the current row in the data grid view as being dirty. + // And because the data grid view cell which was edited became disconnected from the data grid view + // do not fire CellValueChanged event. + // vsw 543015. + return true; + } + + if (this.OwningRow.Index == this.DataGridView.CurrentCellAddress.Y) + { + // The user programatically changed a value in the current row. + // The DataGridView already opened a transaction for the current row. + // All is left to do is to mark the current row in the DataGridView as being dirty. + this.DataGridView.IsCurrentRowDirtyInternal = true; + } + } + else + { + return false; + } + } + } + else if (dataGridView == null || + !dataGridView.VirtualMode || + rowIndex == -1 || + this.ColumnIndex == -1) + { + if (value != null || this.Properties.ContainsObject(PropCellValue)) + { + this.Properties.SetObject(PropCellValue, value); + } + } + else + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(this.ColumnIndex >= 0); + dataGridView.OnCellValuePushed(this.ColumnIndex, rowIndex, value); + } + + if (dataGridView != null && + !dataGridView.InSortOperation && + ((originalValue == null && value != null) || + (originalValue != null && value == null) || + (originalValue != null && !value.Equals(originalValue)))) + { + RaiseCellValueChanged(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + } + return true; + } + + internal bool SetValueInternal(int rowIndex, object value) + { + return SetValue(rowIndex, value); + } + + internal static bool TextFitsInBounds(Graphics graphics, string text, Font font, Size maxBounds, TextFormatFlags flags) + { + bool widthTruncated; + int requiredHeight = DataGridViewCell.MeasureTextHeight(graphics, text, font, maxBounds.Width, flags, out widthTruncated); + return requiredHeight <= maxBounds.Height && !widthTruncated; + } + + /// + /// + /// + /// Gets the row Index and column Index of the cell. + /// + /// + public override string ToString() + { + return "DataGridViewCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private static string TruncateToolTipText(string toolTipText) + { + if (toolTipText.Length > DATAGRIDVIEWCELL_maxToolTipLength) + { + StringBuilder sb = new StringBuilder(toolTipText.Substring(0, DATAGRIDVIEWCELL_maxToolTipCutOff), DATAGRIDVIEWCELL_maxToolTipCutOff + DATAGRIDVIEWCELL_toolTipEllipsisLength); + sb.Append(DATAGRIDVIEWCELL_toolTipEllipsis); + return sb.ToString(); + } + return toolTipText; + } + + private void UpdateCurrentMouseLocation(DataGridViewCellMouseEventArgs e) + { + if (GetErrorIconBounds(e.RowIndex).Contains(e.X, e.Y)) + { + this.CurrentMouseLocation = DATAGRIDVIEWCELL_flagErrorArea; + } + else + { + this.CurrentMouseLocation = DATAGRIDVIEWCELL_flagDataArea; + } + } + + /// + protected class DataGridViewCellAccessibleObject : AccessibleObject + { + private int[] runtimeId = null; // Used by UIAutomation + private AccessibleObject _child = null; + + DataGridViewCell owner; + + /// + public DataGridViewCellAccessibleObject() + { + } + + /// + public DataGridViewCellAccessibleObject(DataGridViewCell owner) + { + this.owner = owner; + } + + /// + public override Rectangle Bounds + { + get + { + return this.GetAccessibleObjectBounds(GetAccessibleObjectParent()); + } + } + + /// + public override string DefaultAction + { + get + { + if (this.Owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + if (!this.Owner.ReadOnly) + { + return SR.GetString(SR.DataGridView_AccCellDefaultAction); + } + else + { + return String.Empty; + } + } + } + + /// + public override string Help + { + get + { + if (AccessibilityImprovements.Level2) + { + return null; + } + + return this.owner.GetType().Name + "(" + owner.GetType().BaseType.Name + ")"; + } + } + + /// + public override string Name + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + if (this.owner.OwningColumn != null) + { + string name = SR.GetString(SR.DataGridView_AccDataGridViewCellName, this.owner.OwningColumn.HeaderText, this.owner.OwningRow.Index); + + if (AccessibilityImprovements.Level3 && owner.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable) + { + DataGridViewCell dataGridViewCell = this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridViewCell.OwningColumn != null && + dataGridViewCell.OwningColumn == dataGridView.SortedColumn) + { + name += ", " + (dataGridView.SortOrder == SortOrder.Ascending + ? SR.GetString(SR.SortedAscendingAccessibleStatus) + : SR.GetString(SR.SortedDescendingAccessibleStatus)); + } + else + { + name += ", " + SR.GetString(SR.NotSortedAccessibleStatus); + } + } + + return name; + } + else + { + return String.Empty; + } + } + } + + /// + public DataGridViewCell Owner + { + get + { + return this.owner; + } + set + { + if (this.owner != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerAlreadySet)); + } + this.owner = value; + } + } + + /// + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.ParentPrivate; + } + } + + private AccessibleObject ParentPrivate + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + if (this.owner.OwningRow == null) + { + return null; + } + else + { + return this.owner.OwningRow.AccessibilityObject; + } + } + } + + /// + public override AccessibleRole Role + { + get + { + return AccessibleRole.Cell; + } + } + + /// + public override AccessibleStates State + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable; + if (this.owner == this.owner.DataGridView.CurrentCell) + { + state |= AccessibleStates.Focused; + } + + if (this.owner.Selected) + { + state |= AccessibleStates.Selected; + } + + if (AccessibilityImprovements.Level1 && this.owner.ReadOnly) + { + state |= AccessibleStates.ReadOnly; + } + + Rectangle cellBounds; + if (this.owner.OwningColumn != null && this.owner.OwningRow != null) + { + cellBounds = this.owner.DataGridView.GetCellDisplayRectangle(this.owner.OwningColumn.Index, this.owner.OwningRow.Index, false /*cutOverflow*/); + } + else if (this.owner.OwningRow != null) + { + cellBounds = this.owner.DataGridView.GetCellDisplayRectangle(-1, this.owner.OwningRow.Index, false /*cutOverflow*/); + } + else if (this.owner.OwningColumn != null) + { + cellBounds = this.owner.DataGridView.GetCellDisplayRectangle(this.owner.OwningColumn.Index, -1, false /*cutOverflow*/); + } + else + { + cellBounds = this.owner.DataGridView.GetCellDisplayRectangle(-1, -1, false /*cutOverflow*/); + } + + if (!cellBounds.IntersectsWith(this.owner.DataGridView.ClientRectangle)) + { + state |= AccessibleStates.Offscreen; + } + + return state; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + object formattedValue = this.owner.FormattedValue; + string formattedValueAsString = formattedValue as string; + if (formattedValue == null || (formattedValueAsString != null && String.IsNullOrEmpty(formattedValueAsString))) + { + return SR.GetString(SR.DataGridView_AccNullValue); + } + else if (formattedValueAsString != null) + { + return formattedValueAsString; + } + else if (this.owner.OwningColumn != null) + { + TypeConverter converter = this.owner.FormattedValueTypeConverter; + if (converter != null && converter.CanConvertTo(typeof(string))) + { + return converter.ConvertToString(formattedValue); + } + else + { + return formattedValue.ToString(); + } + } + else + { + return String.Empty; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set + { + if (this.owner is DataGridViewHeaderCell) + { + return; + } + + if (this.owner.ReadOnly) + { + return; + } + + if (this.owner.OwningRow == null) + { + return; + } + + if (this.owner.DataGridView.IsCurrentCellInEditMode) + { + // EndEdit before setting the accessible object value. + // This way the value being edited is validated. + this.owner.DataGridView.EndEdit(); + } + + DataGridViewCellStyle dataGridViewCellStyle = this.owner.InheritedStyle; + + // Format string "True" to boolean True. + object formattedValue = this.owner.GetFormattedValue(value, + this.owner.OwningRow.Index, + ref dataGridViewCellStyle, + null /*formattedValueTypeConverter*/ , + null /*valueTypeConverter*/, + DataGridViewDataErrorContexts.Formatting); + // Parse the formatted value and push it into the back end. + this.owner.Value = owner.ParseFormattedValue(formattedValue, + dataGridViewCellStyle, + null /*formattedValueTypeConverter*/, + null /*valueTypeConverter*/); + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + DataGridViewCell dataGridViewCell = (DataGridViewCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridViewCell is DataGridViewHeaderCell) + { + return; + } + + if (dataGridView != null && dataGridViewCell.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + + Select(AccessibleSelection.TakeFocus | AccessibleSelection.TakeSelection); + Debug.Assert(dataGridView.CurrentCell == dataGridViewCell, "the result of selecting the cell should have made this cell the current cell"); + + if (dataGridViewCell.ReadOnly) + { + // don't edit if the cell is read only + return; + } + + if (dataGridViewCell.EditType != null) + { + if (dataGridView.InBeginEdit || dataGridView.InEndEdit) + { + // don't enter or exit editing mode if the control + // is in the middle of doing that already. + return; + } + if (dataGridView.IsCurrentCellInEditMode) + { + // stop editing + dataGridView.EndEdit(); + } + else if (dataGridView.EditMode != DataGridViewEditMode.EditProgrammatically) + { + // start editing + dataGridView.BeginEdit(true /*selectAll*/); + } + } + } + + internal Rectangle GetAccessibleObjectBounds(AccessibleObject parentAccObject) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + if (this.owner.OwningColumn == null) + { + return Rectangle.Empty; + } + + // use the accessibility bounds from the parent row acc obj + Rectangle rowRect = parentAccObject.Bounds; + Rectangle columnRect; + + int firstVisibleColumnIndex = this.owner.DataGridView.Columns.ColumnIndexToActualDisplayIndex(this.owner.DataGridView.FirstDisplayedScrollingColumnIndex, DataGridViewElementStates.Visible); + int visibleColumnIndex = this.owner.DataGridView.Columns.ColumnIndexToActualDisplayIndex(this.owner.ColumnIndex, DataGridViewElementStates.Visible); + + bool rowHeadersVisible = this.owner.DataGridView.RowHeadersVisible; + if (visibleColumnIndex < firstVisibleColumnIndex) + { + // Get the bounds for the cell to the RIGHT + columnRect = parentAccObject.GetChild(visibleColumnIndex + + 1 // + 1 for the next cell to the RIGHT + + (rowHeadersVisible ? 1 : 0)).Bounds; // + 1 but only if the row headers are visible + + // From the bounds of the cell to the RIGHT decrement the width of the owning column + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + columnRect.X -= this.owner.OwningColumn.Width; + } + else + { + columnRect.X = columnRect.Right; + } + columnRect.Width = this.owner.OwningColumn.Width; + } + else if (visibleColumnIndex == firstVisibleColumnIndex) + { + columnRect = this.owner.DataGridView.GetColumnDisplayRectangle(this.owner.ColumnIndex, false /*cutOverflow*/); + int negOffset = this.owner.DataGridView.FirstDisplayedScrollingColumnHiddenWidth; + + if (negOffset != 0) + { + if (this.owner.DataGridView.RightToLeft == RightToLeft.No) + { + columnRect.X -= negOffset; + } + columnRect.Width += negOffset; + } + columnRect = this.owner.DataGridView.RectangleToScreen(columnRect); + } + else + { + // Get the bounds for the cell to the LEFT + columnRect = parentAccObject.GetChild(visibleColumnIndex + - 1 // -1 because we want the previous cell to the LEFT + + (rowHeadersVisible ? 1 : 0)).Bounds; // +1 but only if the row headers are visible + + // From the bounds of the cell to the LEFT increment the width of the owning column + if (this.owner.DataGridView.RightToLeft == RightToLeft.No) + { + columnRect.X = columnRect.Right; + } + else + { + columnRect.X -= this.owner.OwningColumn.Width; + } + + columnRect.Width = this.owner.OwningColumn.Width; + } + + rowRect.X = columnRect.X; + rowRect.Width = columnRect.Width; + + return rowRect; + } + + private AccessibleObject GetAccessibleObjectParent() + { + // If this is one of our types, use the shortcut provided by ParentPrivate property. + // Otherwise, use the Parent property. + if (this.owner is DataGridViewButtonCell || + this.owner is DataGridViewCheckBoxCell || + this.owner is DataGridViewComboBoxCell || + this.owner is DataGridViewImageCell || + this.owner is DataGridViewLinkCell || + this.owner is DataGridViewTextBoxCell) + { + return this.ParentPrivate; + } + else + { + return this.Parent; + } + } + + /// + public override AccessibleObject GetChild(int index) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + if (this.owner.DataGridView.EditingControl != null && + this.owner.DataGridView.IsCurrentCellInEditMode && + this.owner.DataGridView.CurrentCell == this.owner && + index == 0) + { + return this.owner.DataGridView.EditingControl.AccessibilityObject; + } + else + { + return null; + } + } + + /// + public override int GetChildCount() + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + if (this.owner.DataGridView.EditingControl != null && + this.owner.DataGridView.IsCurrentCellInEditMode && + this.owner.DataGridView.CurrentCell == this.owner) + { + return 1; + } + else + { + return 0; + } + } + + /// + public override AccessibleObject GetFocused() + { + return null; + } + + /// + public override AccessibleObject GetSelected() + { + return null; + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + if (this.owner.OwningColumn == null || this.owner.OwningRow == null) + { + return null; + } + + switch (navigationDirection) + { + case AccessibleNavigation.Right: + if (this.owner.DataGridView.RightToLeft == RightToLeft.No) + { + return NavigateForward(true /*wrapAround*/); + } + else + { + return NavigateBackward(true /*wrapAround*/); + } + case AccessibleNavigation.Next: + return NavigateForward(false /*wrapAround*/); + case AccessibleNavigation.Left: + if (this.owner.DataGridView.RightToLeft == RightToLeft.No) + { + return NavigateBackward(true /*wrapAround*/); + } + else + { + return NavigateForward(true /*wrapAround*/); + } + case AccessibleNavigation.Previous: + return NavigateBackward(false /*wrapAround*/); + case AccessibleNavigation.Up: + if (this.owner.OwningRow.Index == this.owner.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible)) + { + if (this.owner.DataGridView.ColumnHeadersVisible) + { + // Return the column header accessible object. + return this.owner.OwningColumn.HeaderCell.AccessibilityObject; + } + else + { + return null; + } + } + else + { + int previousVisibleRow = this.owner.DataGridView.Rows.GetPreviousRow(this.owner.OwningRow.Index, DataGridViewElementStates.Visible); + return this.owner.DataGridView.Rows[previousVisibleRow].Cells[this.owner.OwningColumn.Index].AccessibilityObject; + } + case AccessibleNavigation.Down: + if (this.owner.OwningRow.Index == this.owner.DataGridView.Rows.GetLastRow(DataGridViewElementStates.Visible)) + { + return null; + } + else + { + int nextVisibleRow = this.owner.DataGridView.Rows.GetNextRow(this.owner.OwningRow.Index, DataGridViewElementStates.Visible); + return this.owner.DataGridView.Rows[nextVisibleRow].Cells[this.owner.OwningColumn.Index].AccessibilityObject; + } + default: + return null; + } + } + + private AccessibleObject NavigateBackward(bool wrapAround) + { + if (this.owner.OwningColumn == this.owner.DataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Visible)) + { + if (wrapAround) + { + // Return the last accessible object in the previous row + AccessibleObject previousRow = this.Owner.OwningRow.AccessibilityObject.Navigate(AccessibleNavigation.Previous); + if (previousRow != null && previousRow.GetChildCount() > 0) + { + return previousRow.GetChild(previousRow.GetChildCount() - 1); + } + else + { + return null; + } + } + else + { + // return the row header cell if the row headers are visible. + if (this.owner.DataGridView.RowHeadersVisible) + { + return this.owner.OwningRow.AccessibilityObject.GetChild(0); + } + else + { + return null; + } + } + } + else + { + int previousVisibleColumnIndex = this.owner.DataGridView.Columns.GetPreviousColumn(this.owner.OwningColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + return this.owner.OwningRow.Cells[previousVisibleColumnIndex].AccessibilityObject; + } + } + + private AccessibleObject NavigateForward(bool wrapAround) + { + if (this.owner.OwningColumn == this.owner.DataGridView.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + + if (wrapAround) + { + // Return the first cell in the next visible row. + // + AccessibleObject nextRow = this.Owner.OwningRow.AccessibilityObject.Navigate(AccessibleNavigation.Next); + if (nextRow != null && nextRow.GetChildCount() > 0) + { + if (this.Owner.DataGridView.RowHeadersVisible) + { + return nextRow.GetChild(1); + } + else + { + return nextRow.GetChild(0); + } + } + else + { + return null; + } + + } + else + { + return null; + } + } + else + { + int nextVisibleColumnIndex = this.owner.DataGridView.Columns.GetNextColumn(this.owner.OwningColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + return this.owner.OwningRow.Cells[nextVisibleColumnIndex].AccessibilityObject; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) + { + this.owner.DataGridView.FocusInternal(); + } + if ((flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) + { + this.owner.Selected = true; + this.owner.DataGridView.CurrentCell = this.owner; // Do not change old selection + } + if ((flags & AccessibleSelection.AddSelection) == AccessibleSelection.AddSelection) + { + // it seems that in any circumstances a cell can become selected + this.owner.Selected = true; + } + if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection && + (flags & (AccessibleSelection.AddSelection | AccessibleSelection.TakeSelection)) == 0) + { + this.owner.Selected = false; + } + } + + /// + /// Sets the detachable child accessible object which may be added or removed to/from hierachy nodes. + /// + /// The child accessible object. + internal override void SetDetachableChild(AccessibleObject child) + { + _child = child; + } + + internal override void SetFocus() + { + base.SetFocus(); + + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + + internal override int[] RuntimeId + { + get + { + if (runtimeId == null) + { + runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.GetHashCode(); + } + + return runtimeId; + } + } + + private string AutomationId + { + get + { + string automationId = string.Empty; + foreach (int runtimeIdPart in RuntimeId) + { + automationId += runtimeIdPart.ToString(); + } + + return automationId; + } + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return this.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return owner.DataGridView.AccessibilityObject; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + if (this.owner.OwningColumn == null || this.owner.OwningRow == null) + { + return null; + } + + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return this.owner.OwningRow.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + return NavigateForward(false); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return NavigateBackward(false); + case UnsafeNativeMethods.NavigateDirection.FirstChild: + case UnsafeNativeMethods.NavigateDirection.LastChild: + if (this.owner.DataGridView.CurrentCell == this.owner && + this.owner.DataGridView.IsCurrentCellInEditMode && + this.owner.DataGridView.EditingControl != null) + { + return _child; + } + break; + default: + return null; + } + + return null; + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override object GetPropertyValue(int propertyID) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyID) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return (State & AccessibleStates.Focused) == AccessibleStates.Focused; // Announce the cell when focusing. + case NativeMethods.UIA_IsEnabledPropertyId: + return owner.DataGridView.Enabled; + case NativeMethods.UIA_AutomationIdPropertyId: + return AutomationId; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_GridItemContainingGridPropertyId: + return Owner.DataGridView.AccessibilityObject; + } + } + + if (propertyID == NativeMethods.UIA_IsTableItemPatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_TableItemPatternId); + } + else if (propertyID == NativeMethods.UIA_IsGridItemPatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_GridItemPatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (AccessibilityImprovements.Level3 && + (patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId) || + patternId.Equals(NativeMethods.UIA_InvokePatternId) || + patternId.Equals(NativeMethods.UIA_ValuePatternId))) + { + return true; + } + + if ((patternId == NativeMethods.UIA_TableItemPatternId || + patternId == NativeMethods.UIA_GridItemPatternId) && + // We don't want to implement patterns for header cells + this.owner.ColumnIndex != -1 && this.owner.RowIndex != -1) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + #endregion + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaderItems() + { + if (this.owner.DataGridView.RowHeadersVisible && this.owner.OwningRow.HasHeaderCell) + { + return new UnsafeNativeMethods.IRawElementProviderSimple[1] { this.owner.OwningRow.HeaderCell.AccessibilityObject }; + } + + return null; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaderItems() + { + if (this.owner.DataGridView.ColumnHeadersVisible && this.owner.OwningColumn.HasHeaderCell) + { + return new UnsafeNativeMethods.IRawElementProviderSimple[1] { this.owner.OwningColumn.HeaderCell.AccessibilityObject }; + } + + return null; + } + + internal override int Row + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.OwningRow != null ? this.owner.OwningRow.Index : -1; + } + } + + internal override int Column + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.OwningColumn != null ? this.owner.OwningColumn.Index : -1; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple ContainingGrid + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.DataGridView.AccessibilityObject; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellBorderStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellBorderStyle.cs new file mode 100644 index 000000000..7fb0f2a58 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellBorderStyle.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewCellBorderStyle + { + /// + Custom = 0, + + /// + Single = 1, + + /// + Raised = 2, + + /// + Sunken = 3, + + /// + None = 4, + + /// + SingleVertical = 5, + + /// + RaisedVertical = 6, + + /// + SunkenVertical = 7, + + /// + SingleHorizontal = 8, + + /// + RaisedHorizontal = 9, + + /// + SunkenHorizontal = 10 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellCancelEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellCancelEventArgs.cs new file mode 100644 index 000000000..53fa6e044 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellCancelEventArgs.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + public class DataGridViewCellCancelEventArgs : CancelEventArgs + { + private int columnIndex; + private int rowIndex; + + internal DataGridViewCellCancelEventArgs(DataGridViewCell dataGridViewCell) : this(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex) + { + } + + /// + public DataGridViewCellCancelEventArgs(int columnIndex, int rowIndex) + { + if (columnIndex < -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellCollection.cs new file mode 100644 index 000000000..122eb29fd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellCollection.cs @@ -0,0 +1,487 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Represents a collection of objects in the + /// control. + /// + [ + ListBindable(false), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewCellCollection : BaseCollection, IList + { + CollectionChangeEventHandler onCollectionChanged; + ArrayList items = new ArrayList(); + DataGridViewRow owner = null; + + /// + /// + int IList.Add(object value) + { + return this.Add((DataGridViewCell) value); + } + + /// + /// + void IList.Clear() + { + this.Clear(); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + this.Insert(index, (DataGridViewCell) value); + } + + /// + /// + void IList.Remove(object value) + { + this.Remove((DataGridViewCell) value); + } + + /// + /// + void IList.RemoveAt(int index) + { + this.RemoveAt(index); + } + + /// + /// + bool IList.IsFixedSize + { + get {return false;} + } + + /// + /// + bool IList.IsReadOnly + { + get {return false;} + } + + /// + /// + object IList.this[int index] + { + get { return this[index]; } + set { this[index] = (DataGridViewCell) value; } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count + { + get {return this.items.Count;} + } + + /// + /// + bool ICollection.IsSynchronized + { + get {return false;} + } + + /// + /// + object ICollection.SyncRoot + { + get {return this;} + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + /// + public DataGridViewCellCollection(DataGridViewRow dataGridViewRow) + { + Debug.Assert(dataGridViewRow != null); + this.owner = dataGridViewRow; + } + + /// + /// + /// [To be supplied.] + /// + protected override ArrayList List + { + get + { + return this.items; + } + } + + /// + /// + /// Retrieves the DataGridViewCell with the specified index. + /// + public DataGridViewCell this[int index] + { + get + { + return (DataGridViewCell) this.items[index]; + } + set + { + DataGridViewCell dataGridViewCell = value; + if (dataGridViewCell == null) + { + throw new ArgumentNullException("value"); + } + if (dataGridViewCell.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CellAlreadyBelongsToDataGridView)); + } + if (dataGridViewCell.OwningRow != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CellAlreadyBelongsToDataGridViewRow)); + } + if (this.owner.DataGridView != null) + { + this.owner.DataGridView.OnReplacingCell(this.owner, index); + } + + DataGridViewCell oldDataGridViewCell = (DataGridViewCell) this.items[index]; + this.items[index] = dataGridViewCell; + dataGridViewCell.OwningRowInternal = this.owner; + dataGridViewCell.StateInternal = oldDataGridViewCell.State; + if (this.owner.DataGridView != null) + { + dataGridViewCell.DataGridViewInternal = this.owner.DataGridView; + dataGridViewCell.OwningColumnInternal = this.owner.DataGridView.Columns[index]; + this.owner.DataGridView.OnReplacedCell(this.owner, index); + } + + oldDataGridViewCell.DataGridViewInternal = null; + oldDataGridViewCell.OwningRowInternal = null; + oldDataGridViewCell.OwningColumnInternal = null; + if (oldDataGridViewCell.ReadOnly) + { + oldDataGridViewCell.ReadOnlyInternal = false; + } + if (oldDataGridViewCell.Selected) + { + oldDataGridViewCell.SelectedInternal = false; + } + } + } + + /// + /// + /// Retrieves the DataGridViewCell with the specified column name. + /// + public DataGridViewCell this[string columnName] + { + get + { + DataGridViewColumn dataGridViewColumn = null; + if (this.owner.DataGridView != null) + { + dataGridViewColumn = this.owner.DataGridView.Columns[columnName]; + } + if (dataGridViewColumn == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewColumnCollection_ColumnNotFound, columnName), "columnName"); + } + return (DataGridViewCell) this.items[dataGridViewColumn.Index]; + } + set + { + DataGridViewColumn dataGridViewColumn = null; + if (this.owner.DataGridView != null) + { + dataGridViewColumn = this.owner.DataGridView.Columns[columnName]; + } + if (dataGridViewColumn == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewColumnCollection_ColumnNotFound, columnName), "columnName"); + } + this[dataGridViewColumn.Index] = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public event CollectionChangeEventHandler CollectionChanged + { + add + { + this.onCollectionChanged += value; + } + remove + { + this.onCollectionChanged -= value; + } + } + + /// + /// + /// Adds a to this collection. + /// + public virtual int Add(DataGridViewCell dataGridViewCell) + { + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + if (dataGridViewCell.OwningRow != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CellAlreadyBelongsToDataGridViewRow)); + } + Debug.Assert(!dataGridViewCell.ReadOnly); + return AddInternal(dataGridViewCell); + } + + internal int AddInternal(DataGridViewCell dataGridViewCell) + { + Debug.Assert(!dataGridViewCell.Selected); + int index = this.items.Add(dataGridViewCell); + dataGridViewCell.OwningRowInternal = this.owner; + DataGridView dataGridView = this.owner.DataGridView; + if (dataGridView != null && dataGridView.Columns.Count > index) + { + dataGridViewCell.OwningColumnInternal = dataGridView.Columns[index]; + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewCell)); + return index; + } + + /// + /// + /// [To be supplied.] + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual void AddRange(params DataGridViewCell[] dataGridViewCells) + { + if (dataGridViewCells == null) + { + throw new ArgumentNullException("dataGridViewCells"); + } + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + foreach (DataGridViewCell dataGridViewCell in dataGridViewCells) + { + if (dataGridViewCell == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_AtLeastOneCellIsNull)); + } + + if (dataGridViewCell.OwningRow != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CellAlreadyBelongsToDataGridViewRow)); + } + } + + // Make sure no two cells are identical + int cellCount = dataGridViewCells.Length; + for (int cell1 = 0; cell1 < cellCount - 1; cell1++) + { + for (int cell2 = cell1 + 1; cell2 < cellCount; cell2++) + { + if (dataGridViewCells[cell1] == dataGridViewCells[cell2]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CannotAddIdenticalCells)); + } + } + } + + this.items.AddRange(dataGridViewCells); + foreach (DataGridViewCell dataGridViewCell in dataGridViewCells) + { + dataGridViewCell.OwningRowInternal = this.owner; + Debug.Assert(!dataGridViewCell.Selected); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() + { + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + foreach (DataGridViewCell dataGridViewCell in this.items) + { + dataGridViewCell.OwningRowInternal = null; + } + this.items.Clear(); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null)); + } + + /// + public void CopyTo(DataGridViewCell[] array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + /// Checks to see if a DataGridViewCell is contained in this collection. + /// + public virtual bool Contains(DataGridViewCell dataGridViewCell) + { + int index = this.items.IndexOf(dataGridViewCell); + return index != -1; + } + + /// + public int IndexOf(DataGridViewCell dataGridViewCell) + { + return this.items.IndexOf(dataGridViewCell); + } + + /// + public virtual void Insert(int index, DataGridViewCell dataGridViewCell) + { + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + if (dataGridViewCell.OwningRow != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_CellAlreadyBelongsToDataGridViewRow)); + } + Debug.Assert(!dataGridViewCell.ReadOnly); + Debug.Assert(!dataGridViewCell.Selected); + this.items.Insert(index, dataGridViewCell); + dataGridViewCell.OwningRowInternal = this.owner; + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewCell)); + } + + internal void InsertInternal(int index, DataGridViewCell dataGridViewCell) + { + Debug.Assert(!dataGridViewCell.Selected); + this.items.Insert(index, dataGridViewCell); + dataGridViewCell.OwningRowInternal = this.owner; + DataGridView dataGridView = this.owner.DataGridView; + if (dataGridView != null && dataGridView.Columns.Count > index) + { + dataGridViewCell.OwningColumnInternal = dataGridView.Columns[index]; + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewCell)); + } + + /// + /// + /// [To be supplied.] + /// + protected void OnCollectionChanged(CollectionChangeEventArgs e) + { + if (this.onCollectionChanged != null) + { + this.onCollectionChanged(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Remove(DataGridViewCell cell) + { + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + int cellIndex = -1; + int itemsCount = this.items.Count; + for (int i = 0; i < itemsCount; ++i) + { + if (this.items[i] == cell) + { + cellIndex = i; + break; + } + } + if (cellIndex == -1) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewCellCollection_CellNotFound)); + } + else + { + RemoveAt(cellIndex); + } + } + + /// + /// + /// [To be supplied.] + /// + public virtual void RemoveAt(int index) + { + if (this.owner.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellCollection_OwningRowAlreadyBelongsToDataGridView)); + } + RemoveAtInternal(index); + } + + internal void RemoveAtInternal(int index) + { + DataGridViewCell dataGridViewCell = (DataGridViewCell) this.items[index]; + this.items.RemoveAt(index); + dataGridViewCell.DataGridViewInternal = null; + dataGridViewCell.OwningRowInternal = null; + if (dataGridViewCell.ReadOnly) + { + dataGridViewCell.ReadOnlyInternal = false; + } + if (dataGridViewCell.Selected) + { + dataGridViewCell.SelectedInternal = false; + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataGridViewCell)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellContextMenuStripNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellContextMenuStripNeededEventArgs.cs new file mode 100644 index 000000000..eb4bf4ff9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellContextMenuStripNeededEventArgs.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewCellContextMenuStripNeededEventArgs : DataGridViewCellEventArgs + { + private ContextMenuStrip contextMenuStrip; + + /// + public DataGridViewCellContextMenuStripNeededEventArgs(int columnIndex, int rowIndex) : base(columnIndex, rowIndex) + { + } + + internal DataGridViewCellContextMenuStripNeededEventArgs( + int columnIndex, + int rowIndex, + ContextMenuStrip contextMenuStrip) : base(columnIndex, rowIndex) + { + this.contextMenuStrip = contextMenuStrip; + } + + /// + public ContextMenuStrip ContextMenuStrip + { + get + { + return this.contextMenuStrip; + } + set + { + this.contextMenuStrip = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellConverter.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellConverter.cs new file mode 100644 index 000000000..9ac106905 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellConverter.cs @@ -0,0 +1,44 @@ + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Windows.Forms; + using System.Globalization; + using System.ComponentModel; + using System.Reflection; + using System.Runtime.Serialization.Formatters; + using System.ComponentModel.Design.Serialization; + + // used by the designer to serialize the DataGridViewCell class + internal class DataGridViewCellConverter : ExpandableObjectConverter { + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + DataGridViewCell cell = value as DataGridViewCell; + if (destinationType == typeof(InstanceDescriptor) && cell != null) { + ConstructorInfo ctor = cell.GetType().GetConstructor(new Type[0]); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[0], false); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellErrorTextNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellErrorTextNeededEventArgs.cs new file mode 100644 index 000000000..92b85af13 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellErrorTextNeededEventArgs.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewCellErrorTextNeededEventArgs : DataGridViewCellEventArgs + { + private string errorText; + + internal DataGridViewCellErrorTextNeededEventArgs( + int columnIndex, + int rowIndex, + string errorText) : base(columnIndex, rowIndex) + { + this.errorText = errorText; + } + + /// + public string ErrorText + { + get + { + return this.errorText; + } + set + { + this.errorText = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellEventArgs.cs new file mode 100644 index 000000000..81f89814f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellEventArgs.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewCellEventArgs : EventArgs + { + private int columnIndex; + private int rowIndex; + + internal DataGridViewCellEventArgs(DataGridViewCell dataGridViewCell) : this(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex) + { + } + + /// + public DataGridViewCellEventArgs(int columnIndex, int rowIndex) + { + if (columnIndex < -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellFormattingEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellFormattingEventArgs.cs new file mode 100644 index 000000000..b025d4a5f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellFormattingEventArgs.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.ComponentModel; + + /// + public class DataGridViewCellFormattingEventArgs : ConvertEventArgs + { + private int columnIndex, rowIndex; + private DataGridViewCellStyle cellStyle; + private bool formattingApplied; + + /// + public DataGridViewCellFormattingEventArgs(int columnIndex, + int rowIndex, + object value, + Type desiredType, + DataGridViewCellStyle cellStyle) : base(value, desiredType) + { + if (columnIndex < -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + this.cellStyle = cellStyle; + } + + /// + public DataGridViewCellStyle CellStyle + { + get + { + return this.cellStyle; + } + set + { + this.cellStyle = value; + } + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public bool FormattingApplied + { + get + { + return this.formattingApplied; + } + set + { + this.formattingApplied = value; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellLinkedList.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellLinkedList.cs new file mode 100644 index 000000000..749d53812 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellLinkedList.cs @@ -0,0 +1,310 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + + /// + /// + /// Represents a linked list of objects + /// + internal class DataGridViewCellLinkedList : IEnumerable + { + private DataGridViewCellLinkedListElement lastAccessedElement; + private DataGridViewCellLinkedListElement headElement; + private int count, lastAccessedIndex; + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return new DataGridViewCellLinkedListEnumerator(this.headElement); + } + + /// + public DataGridViewCellLinkedList() + { + this.lastAccessedIndex = -1; + } + + /// + public DataGridViewCell this[int index] + { + get + { + Debug.Assert(index >= 0); + Debug.Assert(index < this.count); + if (this.lastAccessedIndex == -1 || index < this.lastAccessedIndex) + { + DataGridViewCellLinkedListElement tmp = this.headElement; + int tmpIndex = index; + while (tmpIndex > 0) + { + tmp = tmp.Next; + tmpIndex--; + } + this.lastAccessedElement = tmp; + this.lastAccessedIndex = index; + return tmp.DataGridViewCell; + } + else + { + while (this.lastAccessedIndex < index) + { + this.lastAccessedElement = this.lastAccessedElement.Next; + this.lastAccessedIndex++; + } + return this.lastAccessedElement.DataGridViewCell; + } + } + } + + /// + public int Count + { + get + { + return this.count; + } + } + + /// + public DataGridViewCell HeadCell + { + get + { + Debug.Assert(this.headElement != null); + return this.headElement.DataGridViewCell; + } + } + + /// + public void Add(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(dataGridViewCell.DataGridView.SelectionMode == DataGridViewSelectionMode.CellSelect || + dataGridViewCell.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect || + dataGridViewCell.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect); + DataGridViewCellLinkedListElement newHead = new DataGridViewCellLinkedListElement(dataGridViewCell); + if (this.headElement != null) + { + newHead.Next = this.headElement; + } + this.headElement = newHead; + this.count++; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + } + + /// + public void Clear() + { + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + this.headElement = null; + this.count = 0; + } + + /// + public bool Contains(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + int index = 0; + DataGridViewCellLinkedListElement tmp = this.headElement; + while (tmp != null) + { + if (tmp.DataGridViewCell == dataGridViewCell) + { + this.lastAccessedElement = tmp; + this.lastAccessedIndex = index; + return true; + } + tmp = tmp.Next; + index++; + } + return false; + } + + /// + public bool Remove(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + DataGridViewCellLinkedListElement tmp1 = null, tmp2 = this.headElement; + while (tmp2 != null) + { + if (tmp2.DataGridViewCell == dataGridViewCell) + { + break; + } + tmp1 = tmp2; + tmp2 = tmp2.Next; + } + if (tmp2.DataGridViewCell == dataGridViewCell) + { + DataGridViewCellLinkedListElement tmp3 = tmp2.Next; + if (tmp1 == null) + { + this.headElement = tmp3; + } + else + { + tmp1.Next = tmp3; + } + this.count--; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + return true; + } + return false; + } + + /* Unused for now + /// + public DataGridViewCell RemoveHead() + { + if (this.headElement == null) + { + return null; + } + DataGridViewCellLinkedListElement tmp = this.headElement; + this.headElement = tmp.Next; + this.count--; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + return tmp.DataGridViewCell; + } + */ + + /// + public int RemoveAllCellsAtBand(bool column, int bandIndex) + { + int removedCount = 0; + DataGridViewCellLinkedListElement tmp1 = null, tmp2 = this.headElement; + while (tmp2 != null) + { + if ((column && tmp2.DataGridViewCell.ColumnIndex == bandIndex) || + (!column && tmp2.DataGridViewCell.RowIndex == bandIndex)) + { + DataGridViewCellLinkedListElement tmp3 = tmp2.Next; + if (tmp1 == null) + { + this.headElement = tmp3; + } + else + { + tmp1.Next = tmp3; + } + tmp2 = tmp3; + this.count--; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + removedCount++; + } + else + { + tmp1 = tmp2; + tmp2 = tmp2.Next; + } + } + return removedCount; + } + } + + /// + /// + /// Represents an emunerator of elements in a linked list. + /// + internal class DataGridViewCellLinkedListEnumerator : IEnumerator + { + private DataGridViewCellLinkedListElement headElement; + private DataGridViewCellLinkedListElement current; + private bool reset; + + /// + public DataGridViewCellLinkedListEnumerator(DataGridViewCellLinkedListElement headElement) + { + this.headElement = headElement; + this.reset = true; + } + + /// + object IEnumerator.Current + { + get + { + Debug.Assert(this.current != null); // Since this is for internal use only. + return this.current.DataGridViewCell; + } + } + + /// + bool IEnumerator.MoveNext() + { + if (this.reset) + { + Debug.Assert(this.current == null); + this.current = this.headElement; + this.reset = false; + } + else + { + Debug.Assert(this.current != null); // Since this is for internal use only. + this.current = this.current.Next; + } + return (this.current != null); + } + + /// + void IEnumerator.Reset() + { + this.reset = true; + this.current = null; + } + } + + /// + /// + /// Represents an element in a linked list. + /// + internal class DataGridViewCellLinkedListElement + { + private DataGridViewCell dataGridViewCell; + private DataGridViewCellLinkedListElement next; + + /// + public DataGridViewCellLinkedListElement(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + this.dataGridViewCell = dataGridViewCell; + } + + /// + public DataGridViewCell DataGridViewCell + { + get + { + return this.dataGridViewCell; + } + } + + /// + public DataGridViewCellLinkedListElement Next + { + get + { + return this.next; + } + set + { + this.next = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellMouseEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellMouseEventArgs.cs new file mode 100644 index 000000000..0c3d75a5d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellMouseEventArgs.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// [To be supplied.] + /// + public class DataGridViewCellMouseEventArgs : MouseEventArgs + { + private int rowIndex, columnIndex; + + /// + public DataGridViewCellMouseEventArgs(int columnIndex, + int rowIndex, + int localX, + int localY, + MouseEventArgs e) : base(e.Button, e.Clicks, localX, localY, e.Delta) + { + if (columnIndex < -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellPaintingEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellPaintingEventArgs.cs new file mode 100644 index 000000000..d74d0b6d4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellPaintingEventArgs.cs @@ -0,0 +1,297 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Diagnostics; + using System.ComponentModel; + + /// + public class DataGridViewCellPaintingEventArgs : HandledEventArgs + { + private DataGridView dataGridView; + private Graphics graphics; + private Rectangle clipBounds; + private Rectangle cellBounds; + private int rowIndex, columnIndex; + private DataGridViewElementStates cellState; + private object value; + private object formattedValue; + private string errorText; + private DataGridViewCellStyle cellStyle; + private DataGridViewAdvancedBorderStyle advancedBorderStyle; + private DataGridViewPaintParts paintParts; + + /// + public DataGridViewCellPaintingEventArgs(DataGridView dataGridView, + Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + int columnIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (dataGridView == null) + { + throw new ArgumentNullException("dataGridView"); + } + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + if ((paintParts & ~DataGridViewPaintParts.All) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewPaintPartsCombination, "paintParts")); + } + this.graphics = graphics; + this.clipBounds = clipBounds; + this.cellBounds = cellBounds; + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.cellState = cellState; + this.value = value; + this.formattedValue = formattedValue; + this.errorText = errorText; + this.cellStyle = cellStyle; + this.advancedBorderStyle = advancedBorderStyle; + this.paintParts = paintParts; + } + + internal DataGridViewCellPaintingEventArgs(DataGridView dataGridView) + { + Debug.Assert(dataGridView != null); + this.dataGridView = dataGridView; + } + + /// + public DataGridViewAdvancedBorderStyle AdvancedBorderStyle + { + get + { + return this.advancedBorderStyle; + } + } + + /// + public Rectangle CellBounds + { + get + { + return this.cellBounds; + } + } + + /// + public DataGridViewCellStyle CellStyle + { + get + { + return this.cellStyle; + } + } + + /// + public Rectangle ClipBounds + { + get + { + return this.clipBounds; + } + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public string ErrorText + { + get + { + return this.errorText; + } + } + + /// + public object FormattedValue + { + get + { + return this.formattedValue; + } + } + + /// + public Graphics Graphics + { + get + { + return this.graphics; + } + } + + /// + public DataGridViewPaintParts PaintParts + { + get + { + return this.paintParts; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public DataGridViewElementStates State + { + get + { + return this.cellState; + } + } + + /// + public object Value + { + get + { + return this.value; + } + } + + /// + public void Paint(Rectangle clipBounds, DataGridViewPaintParts paintParts) + { + if (this.rowIndex < -1 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + if (this.columnIndex < -1 || this.columnIndex >= this.dataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_ColumnIndexOutOfRange)); + } + this.dataGridView.GetCellInternal(this.columnIndex, this.rowIndex).PaintInternal(this.graphics, + clipBounds, + this.cellBounds, + this.rowIndex, + this.cellState, + this.value, + this.formattedValue, + this.errorText, + this.cellStyle, + this.advancedBorderStyle, + paintParts); + } + + /// + public void PaintBackground(Rectangle clipBounds, bool cellsPaintSelectionBackground) + { + if (this.rowIndex < -1 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + if (this.columnIndex < -1 || this.columnIndex >= this.dataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_ColumnIndexOutOfRange)); + } + DataGridViewPaintParts paintParts = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border; + if (cellsPaintSelectionBackground) + { + paintParts |= DataGridViewPaintParts.SelectionBackground; + } + this.dataGridView.GetCellInternal(this.columnIndex, this.rowIndex).PaintInternal(this.graphics, + clipBounds, + this.cellBounds, + this.rowIndex, + this.cellState, + this.value, + this.formattedValue, + this.errorText, + this.cellStyle, + this.advancedBorderStyle, + paintParts); + } + + /// + public void PaintContent(Rectangle clipBounds) + { + if (this.rowIndex < -1 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + if (this.columnIndex < -1 || this.columnIndex >= this.dataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_ColumnIndexOutOfRange)); + } + this.dataGridView.GetCellInternal(this.columnIndex, this.rowIndex).PaintInternal(this.graphics, + clipBounds, + this.cellBounds, + this.rowIndex, + this.cellState, + this.value, + this.formattedValue, + this.errorText, + this.cellStyle, + this.advancedBorderStyle, + DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon); + } + + internal void SetProperties(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + int columnIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + Debug.Assert(graphics != null); + Debug.Assert(cellStyle != null); + + this.graphics = graphics; + this.clipBounds = clipBounds; + this.cellBounds = cellBounds; + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.cellState = cellState; + this.value = value; + this.formattedValue = formattedValue; + this.errorText = errorText; + this.cellStyle = cellStyle; + this.advancedBorderStyle = advancedBorderStyle; + this.paintParts = paintParts; + this.Handled = false; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellParsingEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellParsingEventArgs.cs new file mode 100644 index 000000000..2c271abc0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellParsingEventArgs.cs @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.ComponentModel; + + /// + public class DataGridViewCellParsingEventArgs : ConvertEventArgs + { + private int rowIndex, columnIndex; + private DataGridViewCellStyle inheritedCellStyle; + private bool parsingApplied; + + /// + public DataGridViewCellParsingEventArgs(int rowIndex, + int columnIndex, + object value, + Type desiredType, + DataGridViewCellStyle inheritedCellStyle) : base(value, desiredType) + { + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.inheritedCellStyle = inheritedCellStyle; + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public DataGridViewCellStyle InheritedCellStyle + { + get + { + return this.inheritedCellStyle; + } + set + { + this.inheritedCellStyle = value; + } + } + + /// + public bool ParsingApplied + { + get + { + return this.parsingApplied; + } + set + { + this.parsingApplied = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStateChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStateChangedEventArgs.cs new file mode 100644 index 000000000..aebe244f7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStateChangedEventArgs.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + public class DataGridViewCellStateChangedEventArgs : EventArgs + { + private DataGridViewCell dataGridViewCell; + private DataGridViewElementStates stateChanged; + + /// + public DataGridViewCellStateChangedEventArgs(DataGridViewCell dataGridViewCell, DataGridViewElementStates stateChanged) + { + if (dataGridViewCell == null) + { + throw new ArgumentNullException("dataGridViewCell"); + } + this.dataGridViewCell = dataGridViewCell; + this.stateChanged = stateChanged; + } + + /// + public DataGridViewCell Cell + { + get + { + return this.dataGridViewCell; + } + } + + /// + public DataGridViewElementStates StateChanged + { + get + { + return this.stateChanged; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyle.cs new file mode 100644 index 000000000..c3e5fd1eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyle.cs @@ -0,0 +1,941 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.Drawing; + using System.Diagnostics; + using System.ComponentModel; + using System.Windows.Forms.Design; + using System.Drawing.Design; + using System.Diagnostics.CodeAnalysis; + + /// + [ + TypeConverterAttribute(typeof(DataGridViewCellStyleConverter)), + EditorAttribute("System.Windows.Forms.Design.DataGridViewCellStyleEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public class DataGridViewCellStyle : ICloneable + { + private static readonly int PropAlignment = PropertyStore.CreateKey(); + private static readonly int PropBackColor = PropertyStore.CreateKey(); + private static readonly int PropDataSourceNullValue = PropertyStore.CreateKey(); + private static readonly int PropFont = PropertyStore.CreateKey(); + private static readonly int PropForeColor = PropertyStore.CreateKey(); + private static readonly int PropFormat = PropertyStore.CreateKey(); + private static readonly int PropFormatProvider = PropertyStore.CreateKey(); + private static readonly int PropNullValue = PropertyStore.CreateKey(); + private static readonly int PropPadding = PropertyStore.CreateKey(); + private static readonly int PropSelectionBackColor = PropertyStore.CreateKey(); + private static readonly int PropSelectionForeColor = PropertyStore.CreateKey(); + private static readonly int PropTag = PropertyStore.CreateKey(); + private static readonly int PropWrapMode = PropertyStore.CreateKey(); + + private const string DATAGRIDVIEWCELLSTYLE_nullText = ""; // default value of NullValue property + + private DataGridViewCellStyleScopes scope; + private PropertyStore propertyStore; // Contains all properties that are not always set. + private DataGridView dataGridView; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DataGridViewCellStyle() + { + this.propertyStore = new PropertyStore(); + this.scope = DataGridViewCellStyleScopes.None; + } + + /// + public DataGridViewCellStyle(DataGridViewCellStyle dataGridViewCellStyle) + { + if (dataGridViewCellStyle == null) + { + throw new ArgumentNullException("dataGridViewCellStyle"); + } + this.propertyStore = new PropertyStore(); + this.scope = DataGridViewCellStyleScopes.None; + this.BackColor = dataGridViewCellStyle.BackColor; + this.ForeColor = dataGridViewCellStyle.ForeColor; + this.SelectionBackColor = dataGridViewCellStyle.SelectionBackColor; + this.SelectionForeColor = dataGridViewCellStyle.SelectionForeColor; + this.Font = dataGridViewCellStyle.Font; + this.NullValue = dataGridViewCellStyle.NullValue; + this.DataSourceNullValue = dataGridViewCellStyle.DataSourceNullValue; + this.Format = dataGridViewCellStyle.Format; + if (!dataGridViewCellStyle.IsFormatProviderDefault) + { + this.FormatProvider = dataGridViewCellStyle.FormatProvider; + } + this.AlignmentInternal = dataGridViewCellStyle.Alignment; + this.WrapModeInternal = dataGridViewCellStyle.WrapMode; + this.Tag = dataGridViewCellStyle.Tag; + this.PaddingInternal = dataGridViewCellStyle.Padding; + } + + /// + [ + SRDescription(SR.DataGridViewCellStyleAlignmentDescr), + //Localizable(true), + DefaultValue(DataGridViewContentAlignment.NotSet), + SRCategory(SR.CatLayout) + ] + public DataGridViewContentAlignment Alignment + { + get + { + bool found; + int alignment = this.Properties.GetInteger(PropAlignment, out found); + if (found) + { + return (DataGridViewContentAlignment) alignment; + } + return DataGridViewContentAlignment.NotSet; + } + set + { + switch (value) + { + case DataGridViewContentAlignment.NotSet: + case DataGridViewContentAlignment.TopLeft: + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.TopRight: + case DataGridViewContentAlignment.MiddleLeft: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.MiddleRight: + case DataGridViewContentAlignment.BottomLeft: + case DataGridViewContentAlignment.BottomCenter: + case DataGridViewContentAlignment.BottomRight: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewContentAlignment)); + } + this.AlignmentInternal = value; + } + } + + internal DataGridViewContentAlignment AlignmentInternal + { + [ + SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible") // Enum.IsDefined is OK here. Debug only. + ] + set + { + Debug.Assert(Enum.IsDefined(typeof(DataGridViewContentAlignment), value)); + if (this.Alignment != value) + { + this.Properties.SetInteger(PropAlignment, (int) value); + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + } + + /// + [ + SRCategory(SR.CatAppearance) + ] + public Color BackColor + { + get + { + return this.Properties.GetColor(PropBackColor); + } + set + { + Color c = this.BackColor; + if (!value.IsEmpty || this.Properties.ContainsObject(PropBackColor)) + { + this.Properties.SetColor(PropBackColor, value); + } + if (!c.Equals(this.BackColor)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Color); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public object DataSourceNullValue + { + get + { + if (this.Properties.ContainsObject(PropDataSourceNullValue)) + { + return this.Properties.GetObject(PropDataSourceNullValue); + } + return System.DBNull.Value; + } + set + { + object oldDataSourceNullValue = this.DataSourceNullValue; + + if ((oldDataSourceNullValue == value) || + (oldDataSourceNullValue != null && oldDataSourceNullValue.Equals(value))) + { + return; + } + + if (value == System.DBNull.Value && + this.Properties.ContainsObject(PropDataSourceNullValue)) + { + this.Properties.RemoveObject(PropDataSourceNullValue); + } + else + { + this.Properties.SetObject(PropDataSourceNullValue, value); + } + + Debug.Assert((oldDataSourceNullValue == null && this.DataSourceNullValue != null) || + (oldDataSourceNullValue != null && this.DataSourceNullValue == null) || + (oldDataSourceNullValue != this.DataSourceNullValue && !oldDataSourceNullValue.Equals(this.DataSourceNullValue))); + + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + + /// + [ + SRCategory(SR.CatAppearance) + ] + public Font Font + { + get + { + return (Font) this.Properties.GetObject(PropFont); + } + set + { + Font f = this.Font; + if (value != null || this.Properties.ContainsObject(PropFont)) + { + this.Properties.SetObject(PropFont, value); + } + if ((f == null && value != null) || + (f != null && value == null) || + (f != null && value != null && !f.Equals(this.Font))) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Font); + } + } + } + + /// + [ + SRCategory(SR.CatAppearance) + ] + public Color ForeColor + { + get + { + return this.Properties.GetColor(PropForeColor); + } + set + { + Color c = this.ForeColor; + if (!value.IsEmpty || this.Properties.ContainsObject(PropForeColor)) + { + this.Properties.SetColor(PropForeColor, value); + } + if (!c.Equals(this.ForeColor)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.ForeColor); + } + } + } + + /// + [ + DefaultValue(""), + EditorAttribute("System.Windows.Forms.Design.FormatStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SRCategory(SR.CatBehavior), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public string Format + { + get + { + object format = this.Properties.GetObject(PropFormat); + if (format == null) + { + return string.Empty; + } + else + { + return (string) format; + } + } + set + { + string format = this.Format; + if ((value != null && value.Length > 0) || this.Properties.ContainsObject(PropFormat)) + { + this.Properties.SetObject(PropFormat, value); + } + if (!format.Equals(this.Format)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public IFormatProvider FormatProvider + { + get + { + object formatProvider = this.Properties.GetObject(PropFormatProvider); + if (formatProvider == null) + { + return System.Globalization.CultureInfo.CurrentCulture; + } + else + { + return (IFormatProvider) formatProvider; + } + } + set + { + object originalFormatProvider = this.Properties.GetObject(PropFormatProvider); + this.Properties.SetObject(PropFormatProvider, value); + if (value != originalFormatProvider) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public bool IsDataSourceNullValueDefault + { + get + { + if (!this.Properties.ContainsObject(PropDataSourceNullValue)) + { + return true; + } + return this.Properties.GetObject(PropDataSourceNullValue) == System.DBNull.Value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public bool IsFormatProviderDefault + { + get + { + return this.Properties.GetObject(PropFormatProvider) == null; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public bool IsNullValueDefault + { + get + { + if (!this.Properties.ContainsObject(PropNullValue)) + { + return true; + } + object nullValue = this.Properties.GetObject(PropNullValue); + return (nullValue is string && nullValue.Equals(DATAGRIDVIEWCELLSTYLE_nullText)); + } + } + + /// + [ + DefaultValue(DATAGRIDVIEWCELLSTYLE_nullText), + TypeConverter(typeof(StringConverter)), + SRCategory(SR.CatData) + ] + public object NullValue + { + get + { + if (this.Properties.ContainsObject(PropNullValue)) + { + return this.Properties.GetObject(PropNullValue); + } + return DATAGRIDVIEWCELLSTYLE_nullText; + } + set + { + object oldNullValue = this.NullValue; + + if ((oldNullValue == value) || + (oldNullValue != null && oldNullValue.Equals(value))) + { + return; + } + + if (value is string && + value.Equals(DATAGRIDVIEWCELLSTYLE_nullText) && + this.Properties.ContainsObject(PropNullValue)) + { + this.Properties.RemoveObject(PropNullValue); + } + else + { + this.Properties.SetObject(PropNullValue, value); + } + + Debug.Assert((oldNullValue == null && this.NullValue != null) || + (oldNullValue != null && this.NullValue == null) || + (oldNullValue != this.NullValue && !oldNullValue.Equals(this.NullValue))); + + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + + /// + [ + SRCategory(SR.CatLayout) + ] + public Padding Padding + { + get + { + return this.Properties.GetPadding(PropPadding); + } + set + { + if (value.Left < 0 || value.Right < 0 || value.Top < 0 || value.Bottom < 0) + { + if (value.All != -1) + { + Debug.Assert(value.All < -1); + value.All = 0; + } + else + { + value.Left = Math.Max(0, value.Left); + value.Right = Math.Max(0, value.Right); + value.Top = Math.Max(0, value.Top); + value.Bottom = Math.Max(0, value.Bottom); + } + } + this.PaddingInternal = value; + } + } + + internal Padding PaddingInternal + { + set + { + Debug.Assert(value.Left >= 0); + Debug.Assert(value.Right >= 0); + Debug.Assert(value.Top >= 0); + Debug.Assert(value.Bottom >= 0); + if (value != this.Padding) + { + this.Properties.SetPadding(PropPadding, value); + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + } + + internal PropertyStore Properties + { + get + { + return this.propertyStore; + } + } + + internal DataGridViewCellStyleScopes Scope + { + get + { + return this.scope; + } + set + { + this.scope = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance) + ] + public Color SelectionBackColor + { + get + { + return this.Properties.GetColor(PropSelectionBackColor); + } + set + { + Color c = this.SelectionBackColor; + if (!value.IsEmpty || this.Properties.ContainsObject(PropSelectionBackColor)) + { + this.Properties.SetColor(PropSelectionBackColor, value); + } + if (!c.Equals(this.SelectionBackColor)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Color); + } + } + } + + /// + [ + SRCategory(SR.CatAppearance) + ] + public Color SelectionForeColor + { + get + { + return this.Properties.GetColor(PropSelectionForeColor); + } + set + { + Color c = this.SelectionForeColor; + if (!value.IsEmpty || this.Properties.ContainsObject(PropSelectionForeColor)) + { + this.Properties.SetColor(PropSelectionForeColor, value); + } + if (!c.Equals(this.SelectionForeColor)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Color); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public object Tag + { + get + { + return Properties.GetObject(PropTag); + } + set + { + if (value != null || this.Properties.ContainsObject(PropTag)) + { + Properties.SetObject(PropTag, value); + } + } + } + + /// + [ + DefaultValue(DataGridViewTriState.NotSet), + SRCategory(SR.CatLayout) + + ] + public DataGridViewTriState WrapMode + { + get + { + bool found; + int wrap = this.Properties.GetInteger(PropWrapMode, out found); + if (found) + { + return (DataGridViewTriState) wrap; + } + return DataGridViewTriState.NotSet; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewTriState.NotSet, (int)DataGridViewTriState.False)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewTriState)); + } + this.WrapModeInternal = value; + } + } + + internal DataGridViewTriState WrapModeInternal + { + set + { + Debug.Assert(value >= DataGridViewTriState.NotSet && value <= DataGridViewTriState.False); + if (this.WrapMode != value) + { + this.Properties.SetInteger(PropWrapMode, (int) value); + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Other); + } + } + } + + internal void AddScope(DataGridView dataGridView, DataGridViewCellStyleScopes scope) + { + this.scope |= scope; + this.dataGridView = dataGridView; + } + + /// + public virtual void ApplyStyle(DataGridViewCellStyle dataGridViewCellStyle) + { + if (dataGridViewCellStyle == null) + { + throw new ArgumentNullException("dataGridViewCellStyle"); + } + if (!dataGridViewCellStyle.BackColor.IsEmpty) + { + this.BackColor = dataGridViewCellStyle.BackColor; + } + if (!dataGridViewCellStyle.ForeColor.IsEmpty) + { + this.ForeColor = dataGridViewCellStyle.ForeColor; + } + if (!dataGridViewCellStyle.SelectionBackColor.IsEmpty) + { + this.SelectionBackColor = dataGridViewCellStyle.SelectionBackColor; + } + if (!dataGridViewCellStyle.SelectionForeColor.IsEmpty) + { + this.SelectionForeColor = dataGridViewCellStyle.SelectionForeColor; + } + if (dataGridViewCellStyle.Font != null) + { + this.Font = dataGridViewCellStyle.Font; + } + if (!dataGridViewCellStyle.IsNullValueDefault) + { + this.NullValue = dataGridViewCellStyle.NullValue; + } + if (!dataGridViewCellStyle.IsDataSourceNullValueDefault) + { + this.DataSourceNullValue = dataGridViewCellStyle.DataSourceNullValue; + } + if (dataGridViewCellStyle.Format.Length != 0) + { + this.Format = dataGridViewCellStyle.Format; + } + if (!dataGridViewCellStyle.IsFormatProviderDefault) + { + this.FormatProvider = dataGridViewCellStyle.FormatProvider; + } + if (dataGridViewCellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + this.AlignmentInternal = dataGridViewCellStyle.Alignment; + } + if (dataGridViewCellStyle.WrapMode != DataGridViewTriState.NotSet) + { + this.WrapModeInternal = dataGridViewCellStyle.WrapMode; + } + if (dataGridViewCellStyle.Tag != null) + { + this.Tag = dataGridViewCellStyle.Tag; + } + if (dataGridViewCellStyle.Padding != Padding.Empty) + { + this.PaddingInternal = dataGridViewCellStyle.Padding; + } + } + + /// + public virtual DataGridViewCellStyle Clone() { + return new DataGridViewCellStyle(this); + } + + /// + public override bool Equals(object o) + { + DataGridViewCellStyle dgvcs = o as DataGridViewCellStyle; + if (dgvcs != null) + { + return GetDifferencesFrom(dgvcs) == DataGridViewCellStyleDifferences.None; + } + else + { + return false; + } + } + + internal DataGridViewCellStyleDifferences GetDifferencesFrom(DataGridViewCellStyle dgvcs) + { + Debug.Assert(dgvcs != null); + + bool preferredSizeAffectingPropDifferent = ( + dgvcs.Alignment != this.Alignment || + dgvcs.DataSourceNullValue != this.DataSourceNullValue || + dgvcs.Font != this.Font || + dgvcs.Format != this.Format || + dgvcs.FormatProvider != this.FormatProvider || + dgvcs.NullValue != this.NullValue || + dgvcs.Padding != this.Padding || + dgvcs.Tag != this.Tag || + dgvcs.WrapMode != this.WrapMode ); + + bool preferredSizeNonAffectingPropDifferent = ( + dgvcs.BackColor != this.BackColor || + dgvcs.ForeColor != this.ForeColor || + dgvcs.SelectionBackColor != this.SelectionBackColor || + dgvcs.SelectionForeColor != this.SelectionForeColor ); + + if (preferredSizeAffectingPropDifferent) + { + return DataGridViewCellStyleDifferences.AffectPreferredSize; + } + else if (preferredSizeNonAffectingPropDifferent) + { + return DataGridViewCellStyleDifferences.DoNotAffectPreferredSize; + } + else + { + return DataGridViewCellStyleDifferences.None; + } + } + + /// + public override int GetHashCode() + { + return WindowsFormsUtils.GetCombinedHashCodes((int) this.Alignment, + (int) this.WrapMode, + this.Padding.GetHashCode(), + this.Format.GetHashCode(), + this.BackColor.GetHashCode(), + this.ForeColor.GetHashCode(), + this.SelectionBackColor.GetHashCode(), + this.SelectionForeColor.GetHashCode(), + (this.Font == null ? 1 : this.Font.GetHashCode()), + (this.NullValue == null ? 1 : this.NullValue.GetHashCode()), + (this.DataSourceNullValue == null ? 1 : this.DataSourceNullValue.GetHashCode()), + (this.Tag == null ? 1 : this.Tag.GetHashCode())); + } + + private void OnPropertyChanged(DataGridViewCellStylePropertyInternal property) + { + if (this.dataGridView != null && this.scope != DataGridViewCellStyleScopes.None) + { + this.dataGridView.OnCellStyleContentChanged(this, property); + } + + /* + if ((this.scope & DataGridViewCellStyleScopeInternal.Cell) == DataGridViewCellStyleScopeInternal.Cell) + { + this.dataGridView.OnDataGridViewCellsStyleChanged(EventArgs.Empty); + } + + if ((this.scope & DataGridViewCellStyleScopeInternal.ColumnDefault) == DataGridViewCellStyleScopeInternal.ColumnDefault) + { + this.dataGridView.OnDataGridViewColumnsDefaultCellStyleChanged(EventArgs.Empty); + } + + if ((this.scope & DataGridViewCellStyleScopeInternal.RowDefault) == DataGridViewCellStyleScopeInternal.RowDefault) + { + this.dataGridView.OnDataGridViewRowsDefaultCellStyleChanged(EventArgs.Empty); + } + + if ((this.scope & DataGridViewCellStyleScopeInternal.DataGridViewDefault) == DataGridViewCellStyleScopeInternal.DataGridViewDefault) + { + this.dataGridView.OnDefaultCellStyleChanged(EventArgs.Empty); + } + + if ((this.scope & DataGridViewCellStyleScopeInternal.DataGridViewColumnHeadersDefault) == DataGridViewCellStyleScopeInternal.DataGridViewColumnHeadersDefault) + { + this.dataGridView.OnColumnHeadersDefaultCellStyleChanged(EventArgs.Empty); + } + + if ((this.scope & DataGridViewCellStyleScopeInternal.DataGridViewRowHeadersDefault) == DataGridViewCellStyleScopeInternal.DataGridViewRowHeadersDefault) + { + this.dataGridView.OnRowHeadersDefaultCellStyleChanged(EventArgs.Empty); + }*/ + } + + internal void RemoveScope(DataGridViewCellStyleScopes scope) + { + this.scope &= ~scope; + if (this.scope == DataGridViewCellStyleScopes.None) + { + this.dataGridView = null; + } + } + + private bool ShouldSerializeBackColor() { + bool found; + this.Properties.GetColor(PropBackColor, out found); + return found; + } + + private bool ShouldSerializeFont() { + return this.Properties.GetObject(PropFont) != null; + } + + private bool ShouldSerializeForeColor() { + bool found; + this.Properties.GetColor(PropForeColor, out found); + return found; + } + + private bool ShouldSerializeFormatProvider() { + return this.Properties.GetObject(PropFormatProvider) != null; + } + + private bool ShouldSerializePadding() { + return this.Padding != Padding.Empty; + } + + private bool ShouldSerializeSelectionBackColor() { + bool found; + this.Properties.GetObject(PropSelectionBackColor, out found); + return found; + } + + private bool ShouldSerializeSelectionForeColor() { + bool found; + this.Properties.GetColor(PropSelectionForeColor, out found); + return found; + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(128); + sb.Append("DataGridViewCellStyle {"); + bool firstPropAdded = true; + if (this.BackColor != Color.Empty) + { + sb.Append(" BackColor=" + this.BackColor.ToString()); + firstPropAdded = false; + } + if (this.ForeColor != Color.Empty) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" ForeColor=" + this.ForeColor.ToString()); + firstPropAdded = false; + } + if (this.SelectionBackColor != Color.Empty) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" SelectionBackColor=" + this.SelectionBackColor.ToString()); + firstPropAdded = false; + } + if (this.SelectionForeColor != Color.Empty) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" SelectionForeColor=" + this.SelectionForeColor.ToString()); + firstPropAdded = false; + } + if (this.Font != null) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" Font=" + this.Font.ToString()); + firstPropAdded = false; + } + if (!this.IsNullValueDefault && this.NullValue != null) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" NullValue=" + this.NullValue.ToString()); + firstPropAdded = false; + } + if (!this.IsDataSourceNullValueDefault && this.DataSourceNullValue != null) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" DataSourceNullValue=" + this.DataSourceNullValue.ToString()); + firstPropAdded = false; + } + if (!string.IsNullOrEmpty(this.Format)) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" Format=" + this.Format); + firstPropAdded = false; + } + if (this.WrapMode != DataGridViewTriState.NotSet) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" WrapMode=" + this.WrapMode.ToString()); + firstPropAdded = false; + } + if (this.Alignment != DataGridViewContentAlignment.NotSet) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" Alignment=" + this.Alignment.ToString()); + firstPropAdded = false; + } + if (this.Padding != Padding.Empty) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" Padding=" + this.Padding.ToString()); + firstPropAdded = false; + } + if (this.Tag != null) + { + if (!firstPropAdded) + { + sb.Append(","); + } + sb.Append(" Tag=" + this.Tag.ToString()); + firstPropAdded = false; + } + sb.Append(" }"); + return sb.ToString(); + } + + object ICloneable.Clone() { + return Clone(); + } + + internal enum DataGridViewCellStylePropertyInternal + { + Color, + Other, + Font, + ForeColor + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleChangedEventArgs.cs new file mode 100644 index 000000000..e7737a362 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleChangedEventArgs.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + internal class DataGridViewCellStyleChangedEventArgs : EventArgs + { + private bool changeAffectsPreferredSize; + + internal DataGridViewCellStyleChangedEventArgs() + { + } + + internal bool ChangeAffectsPreferredSize + { + get + { + return this.changeAffectsPreferredSize; + } + set + { + this.changeAffectsPreferredSize = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleContentChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleContentChangedEventArgs.cs new file mode 100644 index 000000000..b7f8dbf0c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleContentChangedEventArgs.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public class DataGridViewCellStyleContentChangedEventArgs : EventArgs + { + private DataGridViewCellStyle dataGridViewCellStyle; + private bool changeAffectsPreferredSize; + + internal DataGridViewCellStyleContentChangedEventArgs(DataGridViewCellStyle dataGridViewCellStyle, bool changeAffectsPreferredSize) + { + this.dataGridViewCellStyle = dataGridViewCellStyle; + this.changeAffectsPreferredSize = changeAffectsPreferredSize; + } + + /// + public DataGridViewCellStyle CellStyle + { + get + { + return this.dataGridViewCellStyle; + } + } + + /// + public DataGridViewCellStyleScopes CellStyleScope + { + get + { + return this.dataGridViewCellStyle.Scope; + } + } + + internal bool ChangeAffectsPreferredSize + { + get + { + return this.changeAffectsPreferredSize; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleConverter.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleConverter.cs new file mode 100644 index 000000000..da7988e37 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleConverter.cs @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// + public class DataGridViewCellStyleConverter : TypeConverter { + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is DataGridViewCellStyle) { + ConstructorInfo ctor = value.GetType().GetConstructor(new Type[0]); + return new InstanceDescriptor(ctor, new object[0], false); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleDifferences.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleDifferences.cs new file mode 100644 index 000000000..ee9683b1d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleDifferences.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + internal enum DataGridViewCellStyleDifferences + { + None = 0, + AffectPreferredSize, + DoNotAffectPreferredSize + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleScopes.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleScopes.cs new file mode 100644 index 000000000..378dbc67c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellStyleScopes.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + /// + [Flags] + public enum DataGridViewCellStyleScopes + { + /// + None = 0x00, + + /// + Cell = 0x01, + + /// + Column = 0x02, + + /// + Row = 0x04, + + /// + DataGridView = 0x08, + + /// + ColumnHeaders = 0x10, + + /// + RowHeaders = 0x20, + + /// + Rows = 0x40, + + /// + AlternatingRows = 0x80 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellToolTipTextNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellToolTipTextNeededEventArgs.cs new file mode 100644 index 000000000..167929f41 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellToolTipTextNeededEventArgs.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewCellToolTipTextNeededEventArgs : DataGridViewCellEventArgs + { + private string toolTipText; + + internal DataGridViewCellToolTipTextNeededEventArgs( + int columnIndex, + int rowIndex, + string toolTipText) : base(columnIndex, rowIndex) + { + this.toolTipText = toolTipText; + } + + /// + public string ToolTipText + { + get + { + return this.toolTipText; + } + set + { + this.toolTipText = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellValidatingEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellValidatingEventArgs.cs new file mode 100644 index 000000000..a8d108954 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellValidatingEventArgs.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// [To be supplied.] + /// + public class DataGridViewCellValidatingEventArgs : CancelEventArgs + { + private int rowIndex, columnIndex; + private object formattedValue; + + internal DataGridViewCellValidatingEventArgs(int columnIndex, int rowIndex, object formattedValue) + { + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.formattedValue = formattedValue; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public object FormattedValue + { + get + { + return this.formattedValue; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCellValueEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCellValueEventArgs.cs new file mode 100644 index 000000000..007347c5d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCellValueEventArgs.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewCellValueEventArgs : EventArgs + { + private int rowIndex, columnIndex; + private object val; + + internal DataGridViewCellValueEventArgs() + { + this.columnIndex = this.rowIndex = -1; + } + + /// + public DataGridViewCellValueEventArgs(int columnIndex, int rowIndex) + { + if (columnIndex < 0) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public object Value + { + get + { + return this.val; + } + set + { + this.val = value; + } + } + + internal void SetProperties(int columnIndex, int rowIndex, object value) + { + Debug.Assert(columnIndex >= -1); + Debug.Assert(rowIndex >= -1); + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + this.val = value; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxCell.cs new file mode 100644 index 000000000..109d271a5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxCell.cs @@ -0,0 +1,1990 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms.VisualStyles; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Internal; + using System.Windows.Forms.ButtonInternal; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + /// + /// Identifies a checkbox cell in the DataGridView. + /// + public class DataGridViewCheckBoxCell : DataGridViewCell, IDataGridViewEditingCell + { + private static readonly DataGridViewContentAlignment anyLeft = DataGridViewContentAlignment.TopLeft | DataGridViewContentAlignment.MiddleLeft | DataGridViewContentAlignment.BottomLeft; + private static readonly DataGridViewContentAlignment anyRight = DataGridViewContentAlignment.TopRight | DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.BottomRight; + private static readonly DataGridViewContentAlignment anyCenter = DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.MiddleCenter | DataGridViewContentAlignment.BottomCenter; + private static readonly DataGridViewContentAlignment anyBottom = DataGridViewContentAlignment.BottomRight | DataGridViewContentAlignment.BottomCenter | DataGridViewContentAlignment.BottomLeft; + private static readonly DataGridViewContentAlignment anyMiddle = DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.MiddleCenter | DataGridViewContentAlignment.MiddleLeft; + + private static readonly VisualStyleElement CheckBoxElement = VisualStyleElement.Button.CheckBox.UncheckedNormal; + private static readonly int PropButtonCellState = PropertyStore.CreateKey(); + private static readonly int PropTrueValue = PropertyStore.CreateKey(); + private static readonly int PropFalseValue = PropertyStore.CreateKey(); + private static readonly int PropFlatStyle = PropertyStore.CreateKey(); + private static readonly int PropIndeterminateValue = PropertyStore.CreateKey(); + private static Bitmap checkImage = null; + + private const byte DATAGRIDVIEWCHECKBOXCELL_threeState = 0x01; + private const byte DATAGRIDVIEWCHECKBOXCELL_valueChanged = 0x02; + private const byte DATAGRIDVIEWCHECKBOXCELL_checked = 0x10; + private const byte DATAGRIDVIEWCHECKBOXCELL_indeterminate = 0x20; + + private const byte DATAGRIDVIEWCHECKBOXCELL_margin = 2; // horizontal and vertical margins for preferred sizes + + private byte flags; // see DATAGRIDVIEWCHECKBOXCELL_ consts above + private static bool mouseInContentBounds = false; + private static Type defaultCheckStateType = typeof(System.Windows.Forms.CheckState); + private static Type defaultBooleanType = typeof(System.Boolean); + private static Type cellType = typeof(DataGridViewCheckBoxCell); + + /// + public DataGridViewCheckBoxCell() : this(false /*threeState*/) + { + } + + /// + public DataGridViewCheckBoxCell(bool threeState) + { + if (threeState) + { + this.flags = DATAGRIDVIEWCHECKBOXCELL_threeState; + } + } + + /// + public virtual object EditingCellFormattedValue + { + get + { + return GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting); + } + set + { + if (this.FormattedValueType == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewCell_FormattedValueTypeNull)); + } + if (value == null || !this.FormattedValueType.IsAssignableFrom(value.GetType())) + { + // Assigned formatted value may not be of the good type, in cases where the app + // is feeding wrong values to the cell in virtual / databound mode. + throw new ArgumentException(SR.GetString(SR.DataGridViewCheckBoxCell_InvalidValueType)); + } + if (value is System.Windows.Forms.CheckState) + { + if (((System.Windows.Forms.CheckState)value) == System.Windows.Forms.CheckState.Checked) + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_checked; + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_indeterminate); + } + else if (((System.Windows.Forms.CheckState)value) == System.Windows.Forms.CheckState.Indeterminate) + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_indeterminate; + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_checked); + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_checked); + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_indeterminate); + } + } + else if (value is System.Boolean) + { + if ((bool)value) + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_checked; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_checked); + } + + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_indeterminate); + } + else + { + throw new ArgumentException(SR.GetString(SR.DataGridViewCheckBoxCell_InvalidValueType)); + } + } + } + + /// + public virtual bool EditingCellValueChanged + { + get + { + return ((this.flags & DATAGRIDVIEWCHECKBOXCELL_valueChanged) != 0x00); + } + set + { + if (value) + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_valueChanged; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_valueChanged); + } + } + } + + /// + public virtual object GetEditingCellFormattedValue(DataGridViewDataErrorContexts context) + { + if (this.FormattedValueType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCell_FormattedValueTypeNull)); + } + if (this.FormattedValueType.IsAssignableFrom(defaultCheckStateType)) + { + if ((this.flags & DATAGRIDVIEWCHECKBOXCELL_checked) != 0x00) + { + if ((context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + return SR.GetString(SR.DataGridViewCheckBoxCell_ClipboardChecked); + } + return System.Windows.Forms.CheckState.Checked; + } + else if ((this.flags & DATAGRIDVIEWCHECKBOXCELL_indeterminate) != 0x00) + { + if ((context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + return SR.GetString(SR.DataGridViewCheckBoxCell_ClipboardIndeterminate); + } + return System.Windows.Forms.CheckState.Indeterminate; + } + else + { + if ((context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + return SR.GetString(SR.DataGridViewCheckBoxCell_ClipboardUnchecked); + } + return System.Windows.Forms.CheckState.Unchecked; + } + } + else if (this.FormattedValueType.IsAssignableFrom(defaultBooleanType)) + { + bool ret = (bool)((this.flags & DATAGRIDVIEWCHECKBOXCELL_checked) != 0x00); + if ((context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + return SR.GetString(ret ? SR.DataGridViewCheckBoxCell_ClipboardTrue : SR.DataGridViewCheckBoxCell_ClipboardFalse); + } + return ret; + } + else + { + return null; + } + } + + /// + public virtual void PrepareEditingCellForEdit(bool selectAll) + { + } + + private ButtonState ButtonState + { + get + { + bool found; + int buttonState = this.Properties.GetInteger(PropButtonCellState, out found); + if (found) + { + return (ButtonState)buttonState; + } + return ButtonState.Normal; + } + set + { + // ButtonState.Pushed is used for mouse interaction + // ButtonState.Checked is used for keyboard interaction + Debug.Assert((value & ~(ButtonState.Normal | ButtonState.Pushed | ButtonState.Checked)) == 0); + if (this.ButtonState != value) + { + this.Properties.SetInteger(PropButtonCellState, (int)value); + } + } + } + + /// + public override Type EditType + { + get + { + // Check boxes can't switch to edit mode + // This cell type must implement the IEditingCell interface + return null; + } + } + + /// + [DefaultValue(null)] + public object FalseValue + { + get + { + return this.Properties.GetObject(PropFalseValue); + } + set + { + if (value != null || this.Properties.ContainsObject(PropFalseValue)) + { + this.Properties.SetObject(PropFalseValue, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal object FalseValueInternal + { + set + { + if (value != null || this.Properties.ContainsObject(PropFalseValue)) + { + this.Properties.SetObject(PropFalseValue, value); + } + } + } + + /// + [DefaultValue(FlatStyle.Standard)] + public FlatStyle FlatStyle + { + get + { + bool found; + int flatStyle = this.Properties.GetInteger(PropFlatStyle, out found); + if (found) + { + return (FlatStyle)flatStyle; + } + return FlatStyle.Standard; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropFlatStyle, (int)value); + OnCommonChange(); + } + } + } + + internal FlatStyle FlatStyleInternal + { + set + { + Debug.Assert(value >= FlatStyle.Flat && value <= FlatStyle.System); + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropFlatStyle, (int)value); + } + } + } + + /// + public override Type FormattedValueType + { + get + { + if (this.ThreeState) + { + return defaultCheckStateType; + } + else + { + return defaultBooleanType; + } + } + } + + /// + [DefaultValue(null)] + public object IndeterminateValue + { + get + { + return this.Properties.GetObject(PropIndeterminateValue); + } + set + { + if (value != null || this.Properties.ContainsObject(PropIndeterminateValue)) + { + this.Properties.SetObject(PropIndeterminateValue, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal object IndeterminateValueInternal + { + set + { + if (value != null || this.Properties.ContainsObject(PropIndeterminateValue)) + { + this.Properties.SetObject(PropIndeterminateValue, value); + } + } + } + + /// + [DefaultValue(false)] + public bool ThreeState + { + get + { + return ((this.flags & DATAGRIDVIEWCHECKBOXCELL_threeState) != 0x00); + } + set + { + if (this.ThreeState != value) + { + this.ThreeStateInternal = value; + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal bool ThreeStateInternal + { + set + { + if (this.ThreeState != value) + { + if (value) + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_threeState; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCHECKBOXCELL_threeState); + } + } + } + } + + /// + [DefaultValue(null)] + public object TrueValue + { + get + { + return this.Properties.GetObject(PropTrueValue); + } + set + { + if (value != null || this.Properties.ContainsObject(PropTrueValue)) + { + this.Properties.SetObject(PropTrueValue, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal object TrueValueInternal + { + set + { + if (value != null || this.Properties.ContainsObject(PropTrueValue)) + { + this.Properties.SetObject(PropTrueValue, value); + } + } + } + + /// + public override Type ValueType + { + get + { + Type valueType = base.ValueType; + if (valueType != null) + { + return valueType; + } + + if (this.ThreeState) + { + return defaultCheckStateType; + } + else + { + return defaultBooleanType; + } + } + set + { + base.ValueType = value; + this.ThreeState = (value != null && defaultCheckStateType.IsAssignableFrom(value)); + } + } + + /// + public override object Clone() + { + DataGridViewCheckBoxCell dataGridViewCell; + Type thisType = this.GetType(); + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewCheckBoxCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewCheckBoxCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.ThreeStateInternal = this.ThreeState; + dataGridViewCell.TrueValueInternal = this.TrueValue; + dataGridViewCell.FalseValueInternal = this.FalseValue; + dataGridViewCell.IndeterminateValueInternal = this.IndeterminateValue; + dataGridViewCell.FlatStyleInternal = this.FlatStyle; + return dataGridViewCell; + } + + private bool CommonContentClickUnsharesRow(DataGridViewCellEventArgs e) + { + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + return ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == e.RowIndex && + this.DataGridView.IsCurrentCellInEditMode; + } + + /// + protected override bool ContentClickUnsharesRow(DataGridViewCellEventArgs e) + { + return CommonContentClickUnsharesRow(e); + } + + /// + protected override bool ContentDoubleClickUnsharesRow(DataGridViewCellEventArgs e) + { + return CommonContentClickUnsharesRow(e); + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewCheckBoxCellAccessibleObject(this); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle checkBoxBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // checkBoxBounds is independent of formattedValue + null /*errorText*/, // checkBoxBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + object value = GetValue(rowIndex); + Rectangle checkBoxBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + GetEditedFormattedValue(value, rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting), + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(checkBoxBoundsDebug.Equals(checkBoxBounds)); +#endif + + return checkBoxBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == rowIndex && this.DataGridView.IsCurrentCellInEditMode) + { + // PaintPrivate does not paint the error icon if this is the current cell. + // So don't set the ErrorIconBounds either. + return Rectangle.Empty; + } + + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errorIconBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // errorIconBounds is independent of formattedValue + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBound*/, + false /*paint*/); + + return errorIconBounds; + } + + /// + protected override object GetFormattedValue(object value, + int rowIndex, + ref DataGridViewCellStyle cellStyle, + TypeConverter valueTypeConverter, + TypeConverter formattedValueTypeConverter, + DataGridViewDataErrorContexts context) + { + if (value != null) + { + if (this.ThreeState) + { + if (value.Equals(this.TrueValue) || + (value is int && (int)value == (int)CheckState.Checked)) + { + value = CheckState.Checked; + } + else if (value.Equals(this.FalseValue) || + (value is int && (int)value == (int)CheckState.Unchecked)) + { + value = CheckState.Unchecked; + } + else if (value.Equals(this.IndeterminateValue) || + (value is int && (int)value == (int)CheckState.Indeterminate)) + { + value = CheckState.Indeterminate; + } + /* Commenting out because of bug VSWhidbey 300778 + else if (this.DataGridView != null && + this.FormattedValueType != null && + !(value is System.DBNull) && + !this.FormattedValueType.IsAssignableFrom(value.GetType())) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs( + new FormatException(SR.GetString(SR.DataGridViewCheckBoxCell_InvalidValueType)), this.ColumnIndex, + rowIndex, context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + */ + } + else + { + if (value.Equals(this.TrueValue) || + (value is int && (int)value != 0)) + { + value = true; + } + else if (value.Equals(this.FalseValue) || + (value is int && (int)value == 0)) + { + value = false; + } + /* Commenting out because of bug VSWhidbey 300778 + else if (this.DataGridView != null && + this.FormattedValueType != null && + !(value is System.DBNull) && + !this.FormattedValueType.IsAssignableFrom(value.GetType())) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs( + new FormatException(SR.GetString(SR.DataGridViewCheckBoxCell_InvalidValueType)), this.ColumnIndex, + rowIndex, context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + */ + } + } + + object ret = base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context); + if (ret != null && (context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + if (ret is bool) + { + bool retBool = (bool) ret; + if (retBool) + { + return SR.GetString(this.ThreeState ? SR.DataGridViewCheckBoxCell_ClipboardChecked : SR.DataGridViewCheckBoxCell_ClipboardTrue); + } + else + { + return SR.GetString(this.ThreeState ? SR.DataGridViewCheckBoxCell_ClipboardUnchecked : SR.DataGridViewCheckBoxCell_ClipboardFalse); + } + } + else if (ret is CheckState) + { + CheckState retCheckState = (CheckState) ret; + if (retCheckState == CheckState.Checked) + { + return SR.GetString(this.ThreeState ? SR.DataGridViewCheckBoxCell_ClipboardChecked : SR.DataGridViewCheckBoxCell_ClipboardTrue); + } + else if (retCheckState == CheckState.Unchecked) + { + return SR.GetString(this.ThreeState ? SR.DataGridViewCheckBoxCell_ClipboardUnchecked : SR.DataGridViewCheckBoxCell_ClipboardFalse); + } + else + { + Debug.Assert(retCheckState == CheckState.Indeterminate); + return SR.GetString(SR.DataGridViewCheckBoxCell_ClipboardIndeterminate); + } + } + } + return ret; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + Size preferredSize; + if (this.DataGridView.ApplyVisualStylesToInnerCells) + { + // Assuming here that all checkbox states use the same size. We should take the largest of the state specific sizes. + Size checkBoxSize = CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal); + switch (this.FlatStyle) + { + case FlatStyle.Standard: + case FlatStyle.System: + break; + case FlatStyle.Flat: + checkBoxSize.Width -= 3; + checkBoxSize.Height -= 3; + break; + case FlatStyle.Popup: + checkBoxSize.Width -= 2; + checkBoxSize.Height -= 2; + break; + } + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(checkBoxSize.Width + borderAndPaddingWidths + 2 * DATAGRIDVIEWCHECKBOXCELL_margin, 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, checkBoxSize.Height + borderAndPaddingHeights + 2 * DATAGRIDVIEWCHECKBOXCELL_margin); + break; + } + default: + { + preferredSize = new Size(checkBoxSize.Width + borderAndPaddingWidths + 2 * DATAGRIDVIEWCHECKBOXCELL_margin, + checkBoxSize.Height + borderAndPaddingHeights + 2 * DATAGRIDVIEWCHECKBOXCELL_margin); + break; + } + } + } + else + { + int checkBoxSize; + switch (this.FlatStyle) + { + case FlatStyle.Flat: + checkBoxSize = CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal).Width - 3; + break; + case FlatStyle.Popup: + checkBoxSize = CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal).Width - 2; + break; + default: // FlatStyle.Standard || FlatStyle.System + checkBoxSize = SystemInformation.Border3DSize.Width * 2 + 9 + 2 * DATAGRIDVIEWCHECKBOXCELL_margin; + break; + } + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(checkBoxSize + borderAndPaddingWidths, 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, checkBoxSize + borderAndPaddingHeights); + break; + } + default: + { + preferredSize = new Size(checkBoxSize + borderAndPaddingWidths, checkBoxSize + borderAndPaddingHeights); + break; + } + } + } + + // We should consider the border size when calculating the preferred size. + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + Rectangle borderWidths = BorderWidths(dgvabsEffective); + preferredSize.Width += borderWidths.X; + preferredSize.Height += borderWidths.Y; + + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width = Math.Max(preferredSize.Width, + borderAndPaddingWidths + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height = Math.Max(preferredSize.Height, + borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + /// + protected override bool KeyDownUnsharesRow(KeyEventArgs e, int rowIndex) + { + return e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift; + } + + /// + protected override bool KeyUpUnsharesRow(KeyEventArgs e, int rowIndex) + { + return e.KeyCode == Keys.Space; + } + + /// + protected override bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left; + } + + /// + protected override bool MouseEnterUnsharesRow(int rowIndex) + { + return this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && rowIndex == this.DataGridView.MouseDownCellAddress.Y; + } + + /// + protected override bool MouseLeaveUnsharesRow(int rowIndex) + { + return (this.ButtonState & ButtonState.Pushed) != 0; + } + + /// + protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left; + } + + private void NotifyDataGridViewOfValueChange() + { + this.flags |= (byte)DATAGRIDVIEWCHECKBOXCELL_valueChanged; + this.DataGridView.NotifyCurrentCellDirty(true); + } + + private void OnCommonContentClick(DataGridViewCellEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == e.RowIndex && + this.DataGridView.IsCurrentCellInEditMode) + { + if (SwitchFormattedValue()) + { + NotifyDataGridViewOfValueChange(); + } + } + } + + /// + protected override void OnContentClick(DataGridViewCellEventArgs e) + { + OnCommonContentClick(e); + } + + /// + protected override void OnContentDoubleClick(DataGridViewCellEventArgs e) + { + OnCommonContentClick(e); + } + + /// + protected override void OnKeyDown(KeyEventArgs e, int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift) + { + UpdateButtonState(this.ButtonState | ButtonState.Checked, rowIndex); + e.Handled = true; + } + } + + /// + protected override void OnKeyUp(KeyEventArgs e, int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (e.KeyCode == Keys.Space) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Checked, rowIndex); + if (!e.Alt && !e.Control && !e.Shift) + { + RaiseCellClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + if (this.DataGridView != null && + this.ColumnIndex < this.DataGridView.Columns.Count && + rowIndex < this.DataGridView.Rows.Count) + { + RaiseCellContentClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + } + e.Handled = true; + } + NotifyMASSClient(new Point(this.ColumnIndex, rowIndex)); + } + } + + /// + protected override void OnLeave(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.RowIndex >= 0); // Cell is not in a shared row. + UpdateButtonState(ButtonState.Normal, rowIndex); + } + } + + /// + protected override void OnMouseDown(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left && mouseInContentBounds) + { + Debug.Assert(this.DataGridView.CellMouseDownInContentBounds); + UpdateButtonState(this.ButtonState | ButtonState.Pushed, e.RowIndex); + } + } + + /// + protected override void OnMouseLeave(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + + if (mouseInContentBounds) + { + mouseInContentBounds = false; + if (this.ColumnIndex >= 0 && + rowIndex >= 0 && + (this.DataGridView.ApplyVisualStylesToInnerCells || this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup)) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + if ((this.ButtonState & ButtonState.Pushed) != 0 && + this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + rowIndex == this.DataGridView.MouseDownCellAddress.Y) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, rowIndex); + } + } + + /// + protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + + bool oldMouseInContentBounds = mouseInContentBounds; + mouseInContentBounds = GetContentBounds(e.RowIndex).Contains(e.X, e.Y); + if (oldMouseInContentBounds != mouseInContentBounds) + { + if (this.DataGridView.ApplyVisualStylesToInnerCells || this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + + if (e.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + e.RowIndex == this.DataGridView.MouseDownCellAddress.Y && + Control.MouseButtons == MouseButtons.Left) + { + if ((this.ButtonState & ButtonState.Pushed) == 0 && + mouseInContentBounds && + this.DataGridView.CellMouseDownInContentBounds) + { + UpdateButtonState(this.ButtonState | ButtonState.Pushed, e.RowIndex); + } + else if ((this.ButtonState & ButtonState.Pushed) != 0 && !mouseInContentBounds) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, e.RowIndex); + } + } + } + + base.OnMouseMove(e); + } + + /// + protected override void OnMouseUp(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left) + { + UpdateButtonState(this.ButtonState & ~ButtonState.Pushed, e.RowIndex); + NotifyMASSClient(new Point(e.ColumnIndex, e.RowIndex)); + } + } + + private void NotifyMASSClient(Point position) + { + Debug.Assert((position.X >= 0) && (position.X < this.DataGridView.Columns.Count)); + Debug.Assert((position.Y >= 0) && (position.Y < this.DataGridView.Rows.Count)); + + int visibleRowIndex = this.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, position.Y); + int visibleColumnIndex = this.DataGridView.Columns.ColumnIndexToActualDisplayIndex(position.X, DataGridViewElementStates.Visible); + + int topHeaderRowIncrement = this.DataGridView.ColumnHeadersVisible ? 1 : 0; + int rowHeaderIncrement = this.DataGridView.RowHeadersVisible ? 1 : 0; + + int objectID = visibleRowIndex + topHeaderRowIncrement // + 1 because the top header row acc obj is at index 0 + + 1; // + 1 because objectID's need to be positive and non-zero + + int childID = visibleColumnIndex + rowHeaderIncrement; // + 1 because the column header cell is at index 0 in top header row acc obj + // same thing for the row header cell in the data grid view row acc obj + + (this.DataGridView.AccessibilityObject as Control.ControlAccessibleObject).NotifyClients(AccessibleEvents.StateChange, objectID, childID); + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + elementState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + Rectangle resultBounds; + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + bool cellSelected = (elementState & DataGridViewElementStates.Selected) != 0; + bool drawAsMixedCheckBox = false, drawErrorText = true; + CheckState checkState; + ButtonState bs; + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == rowIndex && this.DataGridView.IsCurrentCellInEditMode) + { + drawErrorText = false; + } + + if (formattedValue != null && formattedValue is CheckState) + { + checkState = (CheckState)formattedValue; + bs = (checkState == CheckState.Unchecked) ? ButtonState.Normal : ButtonState.Checked; + drawAsMixedCheckBox = (checkState == CheckState.Indeterminate); + } + else if (formattedValue != null && formattedValue is bool) + { + if ((bool)formattedValue) + { + checkState = CheckState.Checked; + bs = ButtonState.Checked; + } + else + { + checkState = CheckState.Unchecked; + bs = ButtonState.Normal; + } + } + else + { + // The provided formatted value has a wrong type. We raised a DataError event while formatting. + bs = ButtonState.Normal; // Default rendering of the checkbox with wrong formatted value type. + checkState = CheckState.Unchecked; + } + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0) + { + bs |= ButtonState.Pushed; + } + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + if (paint && + DataGridViewCell.PaintFocus(paintParts) && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == rowIndex) + { + // Draw focus rectangle + ControlPaint.DrawFocusRectangle(g, valBounds, Color.Empty, br.Color); + } + + Rectangle errorBounds = valBounds; + + valBounds.Inflate(-DATAGRIDVIEWCHECKBOXCELL_margin, -DATAGRIDVIEWCHECKBOXCELL_margin); + + Size checkBoxSize; + + CheckBoxState themeCheckBoxState = CheckBoxState.UncheckedNormal; + + if (this.DataGridView.ApplyVisualStylesToInnerCells) + { + themeCheckBoxState = CheckBoxRenderer.ConvertFromButtonState(bs, drawAsMixedCheckBox, + this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds); + checkBoxSize = CheckBoxRenderer.GetGlyphSize(g, themeCheckBoxState); + switch (this.FlatStyle) + { + case FlatStyle.Standard: + case FlatStyle.System: + break; + case FlatStyle.Flat: + checkBoxSize.Width -= 3; + checkBoxSize.Height -= 3; + break; + case FlatStyle.Popup: + checkBoxSize.Width -= 2; + checkBoxSize.Height -= 2; + break; + } + } + else + { + switch (this.FlatStyle) + { + case FlatStyle.Flat: + checkBoxSize = CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.UncheckedNormal); + checkBoxSize.Width -= 3; + checkBoxSize.Height -= 3; + break; + case FlatStyle.Popup: + checkBoxSize = CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.UncheckedNormal); + checkBoxSize.Width -= 2; + checkBoxSize.Height -= 2; + break; + default: // FlatStyle.Standard || FlatStyle.System + checkBoxSize = new Size(SystemInformation.Border3DSize.Width * 2 + 9, SystemInformation.Border3DSize.Width * 2 + 9); + break; + } + } + + if (valBounds.Width >= checkBoxSize.Width && valBounds.Height >= checkBoxSize.Height && (paint || computeContentBounds)) + { + int checkBoxX = 0, checkBoxY = 0; + if ((!this.DataGridView.RightToLeftInternal && (cellStyle.Alignment & anyRight) != 0) || + (this.DataGridView.RightToLeftInternal && (cellStyle.Alignment & anyLeft) != 0)) + { + checkBoxX = valBounds.Right - checkBoxSize.Width; + } + else if ((cellStyle.Alignment & anyCenter) != 0) + { + checkBoxX = valBounds.Left + (valBounds.Width - checkBoxSize.Width) / 2; + } + else + { + checkBoxX = valBounds.Left; + } + + if ((cellStyle.Alignment & anyBottom) != 0) + { + checkBoxY = valBounds.Bottom - checkBoxSize.Height; + } + else if ((cellStyle.Alignment & anyMiddle) != 0) + { + checkBoxY = valBounds.Top + (valBounds.Height - checkBoxSize.Height) / 2; + } + else + { + checkBoxY = valBounds.Top; + } + + if (this.DataGridView.ApplyVisualStylesToInnerCells && this.FlatStyle != FlatStyle.Flat && this.FlatStyle != FlatStyle.Popup) + { + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + DataGridViewCheckBoxCellRenderer.DrawCheckBox(g, + new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height), + (int) themeCheckBoxState); + } + + resultBounds = new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height); + } + else + { + if (this.FlatStyle == FlatStyle.System || this.FlatStyle == FlatStyle.Standard) + { + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + if (drawAsMixedCheckBox) + { + ControlPaint.DrawMixedCheckBox(g, checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height, bs); + } + else + { + ControlPaint.DrawCheckBox(g, checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height, bs); + } + } + + resultBounds = new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height); + } + else if (this.FlatStyle == FlatStyle.Flat) + { + // CheckBox::Paint will only paint the check box differently when in FlatStyle.Flat + // this code is copied from CheckBox::DrawCheckFlat. it was a lot of trouble making this function static + + Rectangle checkBounds = new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height); + + SolidBrush foreBrush = null; + SolidBrush backBrush = null; + Color highlight = Color.Empty; + + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + foreBrush = this.DataGridView.GetCachedBrush(cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor); + backBrush = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + highlight = ControlPaint.LightLight(backBrush.Color); + + if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds) + { + const float lowlight = .1f; + float adjust = 1 - lowlight; + if (highlight.GetBrightness() < .5) + { + adjust = 1 + lowlight * 2; + } + highlight = Color.FromArgb(ButtonInternal.ButtonBaseAdapter.ColorOptions.Adjust255(adjust, highlight.R), + ButtonInternal.ButtonBaseAdapter.ColorOptions.Adjust255(adjust, highlight.G), + ButtonInternal.ButtonBaseAdapter.ColorOptions.Adjust255(adjust, highlight.B)); + } + highlight = g.GetNearestColor(highlight); + + using (Pen pen = new Pen(foreBrush.Color)) + { + g.DrawLine(pen, checkBounds.Left, checkBounds.Top, checkBounds.Right-1, checkBounds.Top); + g.DrawLine(pen, checkBounds.Left, checkBounds.Top, checkBounds.Left, checkBounds.Bottom-1); + } + } + + checkBounds.Inflate(-1, -1); + checkBounds.Width++; + checkBounds.Height++; + + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + if (checkState == CheckState.Indeterminate) + { + ButtonInternal.ButtonBaseAdapter.DrawDitheredFill(g, backBrush.Color, highlight, checkBounds); + } + else + { + using (SolidBrush highBrush = new SolidBrush(highlight)) + { + g.FillRectangle(highBrush, checkBounds); + } + } + + // draw the check box + if (checkState != CheckState.Unchecked) + { + Rectangle fullSize = new Rectangle(checkBoxX-1, checkBoxY-1, checkBoxSize.Width+3, checkBoxSize.Height+3); + fullSize.Width++; + fullSize.Height++; + + if (checkImage == null || checkImage.Width != fullSize.Width || checkImage.Height != fullSize.Height) + { + if (checkImage != null) + { + checkImage.Dispose(); + checkImage = null; + } + + // We draw the checkmark slightly off center to eliminate 3-D border artifacts, + // and compensate below + NativeMethods.RECT rcCheck = NativeMethods.RECT.FromXYWH(0, 0, fullSize.Width, fullSize.Height); + Bitmap bitmap = new Bitmap(fullSize.Width, fullSize.Height); + using (Graphics offscreen = Graphics.FromImage(bitmap)) + { + offscreen.Clear(Color.Transparent); + IntPtr dc = offscreen.GetHdc(); + try + { + SafeNativeMethods.DrawFrameControl(new HandleRef(offscreen, dc), ref rcCheck, + NativeMethods.DFC_MENU, NativeMethods.DFCS_MENUCHECK); + } + finally + { + offscreen.ReleaseHdcInternal(dc); + } + } + bitmap.MakeTransparent(); + checkImage = bitmap; + } + fullSize.Y--; + ControlPaint.DrawImageColorized(g, checkImage, fullSize, checkState == CheckState.Indeterminate ? ControlPaint.LightLight(foreBrush.Color) : foreBrush.Color); + } + } + + resultBounds = checkBounds; + } + else + { + Debug.Assert(this.FlatStyle == FlatStyle.Popup); + + Rectangle checkBounds = new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width - 1, checkBoxSize.Height - 1); + + // The CheckBoxAdapter code moves the check box down about 3 pixels so we have to take that into account + checkBounds.Y -= 3; + + if ((this.ButtonState & (ButtonState.Pushed | ButtonState.Checked)) != 0) + { + // paint down + ButtonBaseAdapter.LayoutOptions options = ButtonInternal.CheckBoxPopupAdapter.PaintPopupLayout(g, + true /*show3D*/, + checkBoxSize.Width, + checkBounds, + Padding.Empty, + false, + cellStyle.Font, + String.Empty, + this.DataGridView.Enabled, + DataGridViewUtilities.ComputeDrawingContentAlignmentForCellStyleAlignment(cellStyle.Alignment), + this.DataGridView.RightToLeft); + options.everettButtonCompat = false; + ButtonBaseAdapter.LayoutData layout = options.Layout(); + + + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + CheckBoxBaseAdapter.DrawCheckBackground(this.DataGridView.Enabled, + checkState, + g, + layout.checkBounds, + colors.windowText, + colors.buttonFace, + true /*disabledColors*/, + colors); + CheckBoxBaseAdapter.DrawPopupBorder(g, layout.checkBounds, colors); + CheckBoxBaseAdapter.DrawCheckOnly(checkBoxSize.Width, + checkState == CheckState.Checked || checkState == CheckState.Indeterminate, + this.DataGridView.Enabled, + checkState, + g, + layout, + colors, + colors.windowText, + colors.buttonFace); + } + resultBounds = layout.checkBounds; + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInContentBounds) + { + // paint over + + ButtonBaseAdapter.LayoutOptions options = ButtonInternal.CheckBoxPopupAdapter.PaintPopupLayout(g, + true /*show3D*/, + checkBoxSize.Width, + checkBounds, + Padding.Empty, + false, + cellStyle.Font, + String.Empty, + this.DataGridView.Enabled, + DataGridViewUtilities.ComputeDrawingContentAlignmentForCellStyleAlignment(cellStyle.Alignment), + this.DataGridView.RightToLeft); + options.everettButtonCompat = false; + ButtonBaseAdapter.LayoutData layout = options.Layout(); + + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + CheckBoxBaseAdapter.DrawCheckBackground(this.DataGridView.Enabled, + checkState, + g, + layout.checkBounds, + colors.windowText, + colors.options.highContrast ? colors.buttonFace : colors.highlight, + true /*disabledColors*/, + colors); + CheckBoxBaseAdapter.DrawPopupBorder(g, layout.checkBounds, colors); + CheckBoxBaseAdapter.DrawCheckOnly(checkBoxSize.Width, + checkState == CheckState.Checked || checkState == CheckState.Indeterminate, + this.DataGridView.Enabled, + checkState, + g, + layout, + colors, + colors.windowText, + colors.highlight); + } + resultBounds = layout.checkBounds; + } + else + { + // paint up + ButtonBaseAdapter.LayoutOptions options = ButtonInternal.CheckBoxPopupAdapter.PaintPopupLayout(g, + false /*show3D*/, + checkBoxSize.Width, + checkBounds, + Padding.Empty, + false, + cellStyle.Font, + String.Empty, + this.DataGridView.Enabled, + DataGridViewUtilities.ComputeDrawingContentAlignmentForCellStyleAlignment(cellStyle.Alignment), + this.DataGridView.RightToLeft); + + options.everettButtonCompat = false; + ButtonBaseAdapter.LayoutData layout = options.Layout(); + + if (paint && DataGridViewCell.PaintContentForeground(paintParts)) + { + ButtonBaseAdapter.ColorData colors = ButtonBaseAdapter.PaintPopupRender(g, + cellStyle.ForeColor, + cellStyle.BackColor, + this.DataGridView.Enabled).Calculate(); + CheckBoxBaseAdapter.DrawCheckBackground(this.DataGridView.Enabled, + checkState, + g, + layout.checkBounds, + colors.windowText, + colors.options.highContrast ? colors.buttonFace : colors.highlight, + true /*disabledColors*/, + colors); + ButtonBaseAdapter.DrawFlatBorder(g, layout.checkBounds, colors.buttonShadow); + CheckBoxBaseAdapter.DrawCheckOnly(checkBoxSize.Width, + checkState == CheckState.Checked || checkState == CheckState.Indeterminate, + this.DataGridView.Enabled, + checkState, + g, + layout, + colors, + colors.windowText, + colors.highlight); + } + resultBounds = layout.checkBounds; + } + } + } + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + else + { + resultBounds = Rectangle.Empty; + } + } + else + { + Debug.Assert(valBounds.Width < checkBoxSize.Width || valBounds.Height < checkBoxSize.Height, "the bounds are empty"); + resultBounds = Rectangle.Empty; + } + + if (paint && DataGridViewCell.PaintErrorIcon(paintParts) && drawErrorText && this.DataGridView.ShowCellErrors) + { + PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + + return resultBounds; + } + + /// + public override object ParseFormattedValue(object formattedValue, + DataGridViewCellStyle cellStyle, + TypeConverter formattedValueTypeConverter, + TypeConverter valueTypeConverter) + { + Debug.Assert(formattedValue == null || this.FormattedValueType == null || this.FormattedValueType.IsAssignableFrom(formattedValue.GetType())); + + if (formattedValue != null) + { + if (formattedValue is bool) + { + if ((bool) formattedValue) + { + if (this.TrueValue != null) + { + return this.TrueValue; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultBooleanType)) + { + return true; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultCheckStateType)) + { + return CheckState.Checked; + } + } + else + { + if (this.FalseValue != null) + { + return this.FalseValue; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultBooleanType)) + { + return false; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultCheckStateType)) + { + return CheckState.Unchecked; + } + } + } + else if (formattedValue is CheckState) + { + switch ((CheckState) formattedValue) + { + case CheckState.Checked: + if (this.TrueValue != null) + { + return this.TrueValue; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultBooleanType)) + { + return true; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultCheckStateType)) + { + return CheckState.Checked; + } + break; + case CheckState.Unchecked: + if (this.FalseValue != null) + { + return this.FalseValue; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultBooleanType)) + { + return false; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultCheckStateType)) + { + return CheckState.Unchecked; + } + break; + case CheckState.Indeterminate: + if (this.IndeterminateValue != null) + { + return this.IndeterminateValue; + } + else if (this.ValueType != null && this.ValueType.IsAssignableFrom(defaultCheckStateType)) + { + return CheckState.Indeterminate; + } + /* case where this.ValueType.IsAssignableFrom(defaultBooleanType) is treated in base.ParseFormattedValue */ + break; + } + } + } + return base.ParseFormattedValue(formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter); + } + + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // no much choice here. + ] + private bool SwitchFormattedValue() + { + if (this.FormattedValueType == null) + { + return false; + } + IDataGridViewEditingCell editingCell = (IDataGridViewEditingCell)this; + if (this.FormattedValueType.IsAssignableFrom(typeof(System.Windows.Forms.CheckState))) + { + if ((this.flags & DATAGRIDVIEWCHECKBOXCELL_checked) != 0x00) + { + editingCell.EditingCellFormattedValue = System.Windows.Forms.CheckState.Indeterminate; + } + else if ((this.flags & DATAGRIDVIEWCHECKBOXCELL_indeterminate) != 0x00) + { + editingCell.EditingCellFormattedValue = System.Windows.Forms.CheckState.Unchecked; + } + else + { + editingCell.EditingCellFormattedValue = System.Windows.Forms.CheckState.Checked; + } + } + else if (this.FormattedValueType.IsAssignableFrom(defaultBooleanType)) + { + editingCell.EditingCellFormattedValue = !((bool)editingCell.GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting)); + } + return true; + } + + /// + /// + /// + /// Gets the row Index and column Index of the cell. + /// + /// + public override string ToString() { + return "DataGridViewCheckBoxCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private void UpdateButtonState(ButtonState newButtonState, int rowIndex) + { + this.ButtonState = newButtonState; + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + + private class DataGridViewCheckBoxCellRenderer + { + static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewCheckBoxCellRenderer() + { + } + + public static VisualStyleRenderer CheckBoxRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(CheckBoxElement); + } + return visualStyleRenderer; + } + } + + public static void DrawCheckBox(Graphics g, Rectangle bounds, int state) + { + CheckBoxRenderer.SetParameters(CheckBoxElement.ClassName, CheckBoxElement.Part, (int) state); + CheckBoxRenderer.DrawBackground(g, bounds, Rectangle.Truncate(g.ClipBounds)); + } + } + + /// + protected class DataGridViewCheckBoxCellAccessibleObject : DataGridViewCellAccessibleObject + { + private int[] runtimeId = null; // Used by UIAutomation + + /// + public DataGridViewCheckBoxCellAccessibleObject(DataGridViewCell owner) : base (owner) + { + } + + /// + public override AccessibleStates State + { + get + { + if (((DataGridViewCheckBoxCell)Owner).EditedFormattedValue is CheckState) + { + CheckState state = (CheckState)(((DataGridViewCheckBoxCell)Owner).EditedFormattedValue); + switch (state) + { + case CheckState.Checked: + return AccessibleStates.Checked | base.State; + case CheckState.Indeterminate: + return AccessibleStates.Indeterminate | base.State; + } + } + else if (((DataGridViewCheckBoxCell)Owner).EditedFormattedValue is Boolean) + { + Boolean state = (Boolean)(((DataGridViewCheckBoxCell)Owner).EditedFormattedValue); + if (state) + { + return AccessibleStates.Checked | base.State; + } + } + return base.State; + } + } + + /// + public override string DefaultAction + { + get + { + if (!this.Owner.ReadOnly) + { + // determine if we switch to Checked/Unchecked value + // vsw 533813. + bool switchToCheckedState = true; + + object formattedValue = this.Owner.FormattedValue; + + if (formattedValue is System.Windows.Forms.CheckState) + { + switchToCheckedState = ((CheckState) formattedValue) == CheckState.Unchecked; + } + else if (formattedValue is bool) + { + switchToCheckedState = !((bool) formattedValue); + } + + if (switchToCheckedState) + { + return SR.GetString(SR.DataGridView_AccCheckBoxCellDefaultActionCheck); + } + else + { + return SR.GetString(SR.DataGridView_AccCheckBoxCellDefaultActionUncheck); + } + } + else + { + return String.Empty; + } + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + DataGridViewCheckBoxCell dataGridViewCell = (DataGridViewCheckBoxCell) this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView != null && dataGridViewCell.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + + if (!dataGridViewCell.ReadOnly && dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningRow != null) + { + dataGridView.CurrentCell = dataGridViewCell; + bool endEditMode = false; + if (!dataGridView.IsCurrentCellInEditMode) + { + endEditMode = true; + dataGridView.BeginEdit(false /*selectAll*/); + } + if (dataGridView.IsCurrentCellInEditMode) + { + if (dataGridViewCell.SwitchFormattedValue()) + { + dataGridViewCell.NotifyDataGridViewOfValueChange(); + dataGridView.InvalidateCell(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex); + + // notify MSAA clients that the default action changed + DataGridViewCheckBoxCell checkBoxCell = Owner as DataGridViewCheckBoxCell; + if (checkBoxCell != null) + { + checkBoxCell.NotifyMASSClient(new Point(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex)); + } + } + if (endEditMode) + { + dataGridView.EndEdit(); + } + } + } + } + + /// + public override int GetChildCount() + { + return 0; + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level1) { + return true; + } + else { + return false; + } + } + + internal override int[] RuntimeId { + get { + if (runtimeId == null) { + runtimeId = new int[2]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.GetHashCode(); + } + + return runtimeId; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_IsTogglePatternAvailablePropertyId) { + return (Object)IsPatternSupported(NativeMethods.UIA_TogglePatternId); + } + else if (propertyID == NativeMethods.UIA_ControlTypePropertyId && AccessibilityImprovements.Level2) + { + return NativeMethods.UIA_CheckBoxControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_TogglePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override void Toggle() { + DoDefaultAction(); + } + + internal override UnsafeNativeMethods.ToggleState ToggleState { + get { + bool toggledState = true; + object formattedValue = this.Owner.FormattedValue; + + if (formattedValue is System.Windows.Forms.CheckState) { + toggledState = ((CheckState)formattedValue) == CheckState.Checked; + } + else if (formattedValue is bool) { + toggledState = ((bool)formattedValue); + } + else { + return UnsafeNativeMethods.ToggleState.ToggleState_Indeterminate; + } + + return toggledState ? UnsafeNativeMethods.ToggleState.ToggleState_On : UnsafeNativeMethods.ToggleState.ToggleState_Off; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxColumn.cs new file mode 100644 index 000000000..a993b2b89 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewCheckBoxColumn.cs @@ -0,0 +1,365 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + [ToolboxBitmapAttribute(typeof(DataGridViewCheckBoxColumn), "DataGridViewCheckBoxColumn.bmp")] + public class DataGridViewCheckBoxColumn : DataGridViewColumn + { + /// + public DataGridViewCheckBoxColumn() : this(false) + { + } + + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Can't think of a workaround. + ] + public DataGridViewCheckBoxColumn(bool threeState) + : base(new DataGridViewCheckBoxCell(threeState)) + { + DataGridViewCellStyle defaultCellStyle = new DataGridViewCellStyle(); + defaultCellStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleCenter; + if (threeState) + { + defaultCellStyle.NullValue = CheckState.Indeterminate; + } + else + { + defaultCellStyle.NullValue = false; + } + this.DefaultCellStyle = defaultCellStyle; + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + if (value != null && !(value is System.Windows.Forms.DataGridViewCheckBoxCell)) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewCheckBoxCell")); + } + base.CellTemplate = value; + } + } + + private DataGridViewCheckBoxCell CheckBoxCellTemplate + { + get + { + return (DataGridViewCheckBoxCell) this.CellTemplate; + } + } + + /// + [ + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnDefaultCellStyleDescr) + ] + public override DataGridViewCellStyle DefaultCellStyle + { + get + { + return base.DefaultCellStyle; + } + set + { + base.DefaultCellStyle = value; + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_CheckBoxColumnFalseValueDescr), + TypeConverter(typeof(StringConverter)) + ] + public object FalseValue + { + get + { + if (this.CheckBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.CheckBoxCellTemplate.FalseValue; + } + set + { + if (this.FalseValue != value) + { + this.CheckBoxCellTemplate.FalseValueInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewCheckBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewCheckBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.FalseValueInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + /// + [ + DefaultValue(FlatStyle.Standard), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_CheckBoxColumnFlatStyleDescr) + ] + public FlatStyle FlatStyle + { + get + { + if (this.CheckBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.CheckBoxCellTemplate.FlatStyle; + } + set + { + if (this.FlatStyle != value) + { + this.CheckBoxCellTemplate.FlatStyle = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewCheckBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewCheckBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.FlatStyleInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_CheckBoxColumnIndeterminateValueDescr), + TypeConverter(typeof(StringConverter)) + ] + public object IndeterminateValue + { + get + { + if (this.CheckBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.CheckBoxCellTemplate.IndeterminateValue; + } + set + { + if (this.IndeterminateValue != value) + { + this.CheckBoxCellTemplate.IndeterminateValueInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewCheckBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewCheckBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.IndeterminateValueInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_CheckBoxColumnThreeStateDescr) + ] + public bool ThreeState + { + get + { + if (this.CheckBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.CheckBoxCellTemplate.ThreeState; + } + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // OK to cast a NullValue into a bool and CheckState + ] + set + { + if (this.ThreeState != value) + { + this.CheckBoxCellTemplate.ThreeStateInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewCheckBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewCheckBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.ThreeStateInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + + if (value && + this.DefaultCellStyle.NullValue is bool && + (bool) this.DefaultCellStyle.NullValue == false) + { + this.DefaultCellStyle.NullValue = CheckState.Indeterminate; + } + else if (!value && + this.DefaultCellStyle.NullValue is CheckState && + (CheckState) this.DefaultCellStyle.NullValue == CheckState.Indeterminate) + { + this.DefaultCellStyle.NullValue = false; + } + } + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_CheckBoxColumnTrueValueDescr), + TypeConverter(typeof(StringConverter)) + ] + public object TrueValue + { + get + { + if (this.CheckBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.CheckBoxCellTemplate.TrueValue; + } + set + { + if (this.TrueValue != value) + { + this.CheckBoxCellTemplate.TrueValueInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewCheckBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewCheckBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.TrueValueInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + private bool ShouldSerializeDefaultCellStyle() + { + Object defaultNullValue; + DataGridViewCheckBoxCell templateCell = this.CellTemplate as DataGridViewCheckBoxCell; + if (templateCell == null) + { + Debug.Fail("we can't compute the default cell style w/o a template cell"); + return true; + } + + if (templateCell.ThreeState) + { + defaultNullValue = CheckState.Indeterminate; + } + else + { + defaultNullValue = false; + } + + if (!this.HasDefaultCellStyle) + { + return false; + } + + DataGridViewCellStyle defaultCellStyle = this.DefaultCellStyle; + + return (!defaultCellStyle.BackColor.IsEmpty || + !defaultCellStyle.ForeColor.IsEmpty || + !defaultCellStyle.SelectionBackColor.IsEmpty || + !defaultCellStyle.SelectionForeColor.IsEmpty || + defaultCellStyle.Font != null || + !defaultCellStyle.NullValue.Equals(defaultNullValue) || + !defaultCellStyle.IsDataSourceNullValueDefault || + !String.IsNullOrEmpty(defaultCellStyle.Format) || + !defaultCellStyle.FormatProvider.Equals(System.Globalization.CultureInfo.CurrentCulture) || + defaultCellStyle.Alignment != DataGridViewContentAlignment.MiddleCenter || + defaultCellStyle.WrapMode != DataGridViewTriState.NotSet || + defaultCellStyle.Tag != null || + !defaultCellStyle.Padding.Equals(Padding.Empty)); + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewCheckBoxColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewClipboardCopyMode.cs b/WindowsForms/Managed/System/WinForms/DataGridViewClipboardCopyMode.cs new file mode 100644 index 000000000..5c88b8742 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewClipboardCopyMode.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewClipboardCopyMode + { + /// + Disable = 0, + + /// + EnableWithAutoHeaderText, + + /// + EnableWithoutHeaderText, + + /// + EnableAlwaysIncludeHeaderText + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumn.cs new file mode 100644 index 000000000..0f5916354 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumn.cs @@ -0,0 +1,1345 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Globalization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Base class for the columns in a data grid view. + /// + [ + Designer("System.Windows.Forms.Design.DataGridViewColumnDesigner, " + AssemblyRef.SystemDesign), + TypeConverterAttribute(typeof(DataGridViewColumnConverter)), + ToolboxItem(false), + DesignTimeVisible(false) + ] + public class DataGridViewColumn : DataGridViewBand, IComponent + { + private const float DATAGRIDVIEWCOLUMN_defaultFillWeight = 100F; + private const int DATAGRIDVIEWCOLUMN_defaultWidth = 100; + private const int DATAGRIDVIEWCOLUMN_defaultMinColumnThickness = 5; + + private const byte DATAGRIDVIEWCOLUMN_automaticSort = 0x01; + private const byte DATAGRIDVIEWCOLUMN_programmaticSort = 0x02; + private const byte DATAGRIDVIEWCOLUMN_isDataBound = 0x04; + private const byte DATAGRIDVIEWCOLUMN_isBrowsableInternal = 0x08; + private const byte DATAGRIDVIEWCOLUMN_displayIndexHasChangedInternal = 0x10; + + private byte flags; // see DATAGRIDVIEWCOLUMN_ consts above + private DataGridViewCell cellTemplate; + private string name; + private int displayIndex; + private int desiredFillWidth = 0; + private int desiredMinimumWidth = 0; + private float fillWeight, usedFillWeight; + private DataGridViewAutoSizeColumnMode autoSizeMode; + private int boundColumnIndex = -1; + private string dataPropertyName = String.Empty; + private TypeConverter boundColumnConverter = null; + + // needed for IComponent + private ISite site = null; + private EventHandler disposed = null; + + private static readonly int PropDataGridViewColumnValueType = PropertyStore.CreateKey(); + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DataGridViewColumn() : this((DataGridViewCell) null) + { + } + + /// + public DataGridViewColumn(DataGridViewCell cellTemplate) : base() + { + this.fillWeight = DATAGRIDVIEWCOLUMN_defaultFillWeight; + this.usedFillWeight = DATAGRIDVIEWCOLUMN_defaultFillWeight; + this.Thickness = ScaleToCurrentDpi(DATAGRIDVIEWCOLUMN_defaultWidth); + this.MinimumThickness = ScaleToCurrentDpi(DATAGRIDVIEWCOLUMN_defaultMinColumnThickness); + this.name = String.Empty; + this.bandIsRow = false; + this.displayIndex = -1; + this.cellTemplate = cellTemplate; + this.autoSizeMode = DataGridViewAutoSizeColumnMode.NotSet; + } + + /// + /// Scale to current device dpi settings + /// + /// initial value + /// scaled metric + private int ScaleToCurrentDpi(int value) + { + return DpiHelper.EnableDataGridViewControlHighDpiImprovements ? DpiHelper.LogicalToDeviceUnits(value) : value; + } + + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(DataGridViewAutoSizeColumnMode.NotSet), + SRDescription(SR.DataGridViewColumn_AutoSizeModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public DataGridViewAutoSizeColumnMode AutoSizeMode + { + get + { + return this.autoSizeMode; + } + set + { + switch (value) + { + case DataGridViewAutoSizeColumnMode.NotSet: + case DataGridViewAutoSizeColumnMode.None: + case DataGridViewAutoSizeColumnMode.ColumnHeader: + case DataGridViewAutoSizeColumnMode.AllCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.AllCells: + case DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.DisplayedCells: + case DataGridViewAutoSizeColumnMode.Fill: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewAutoSizeColumnMode)); + } + if (this.autoSizeMode != value) + { + if (this.Visible && this.DataGridView != null) + { + if (!this.DataGridView.ColumnHeadersVisible && + (value == DataGridViewAutoSizeColumnMode.ColumnHeader || + (value == DataGridViewAutoSizeColumnMode.NotSet && this.DataGridView.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_AutoSizeCriteriaCannotUseInvisibleHeaders)); + } + if (this.Frozen && + (value == DataGridViewAutoSizeColumnMode.Fill || + (value == DataGridViewAutoSizeColumnMode.NotSet && this.DataGridView.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + // Cannot set the inherited auto size mode to Fill when the column is frozen + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_FrozenColumnCannotAutoFill)); + } + } + DataGridViewAutoSizeColumnMode previousInheritedMode = this.InheritedAutoSizeMode; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet; + this.autoSizeMode = value; + if (this.DataGridView == null) + { + if (this.InheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.Fill && + this.InheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.None && + this.InheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.NotSet) + { + if (!previousInheritedModeAutoSized) + { + // Save current column width for later reuse + this.CachedThickness = this.Thickness; + } + } + else + { + if (this.Thickness != this.CachedThickness && previousInheritedModeAutoSized) + { + // Restoring cached column width + this.ThicknessInternal = this.CachedThickness; + } + } + } + else + { + this.DataGridView.OnAutoSizeColumnModeChanged(this, previousInheritedMode); + } + } + } + } + + // TypeConverter of the PropertyDescriptor attached to this column + // in databound cases. Null otherwise. + internal TypeConverter BoundColumnConverter + { + get + { + return this.boundColumnConverter; + } + set + { + this.boundColumnConverter = value; + } + } + + internal int BoundColumnIndex + { + get + { + return this.boundColumnIndex; + } + set + { + this.boundColumnIndex = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual DataGridViewCell CellTemplate + { + get + { + return this.cellTemplate; + } + set + { + this.cellTemplate = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + ] + public Type CellType + { + get + { + if (this.cellTemplate != null) + { + return this.cellTemplate.GetType(); + } + else + { + return null; + } + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnContextMenuStripDescr) + ] + public override ContextMenuStrip ContextMenuStrip + { + get + { + return base.ContextMenuStrip; + } + set + { + base.ContextMenuStrip = value; + } + } + + /// + [ + Browsable(true), + DefaultValue(""), + TypeConverterAttribute("System.Windows.Forms.Design.DataMemberFieldConverter, " + AssemblyRef.SystemDesign), + Editor("System.Windows.Forms.Design.DataGridViewColumnDataPropertyNameEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.DataGridView_ColumnDataPropertyNameDescr), + SRCategory(SR.CatData) + ] + public string DataPropertyName + { + get + { + return this.dataPropertyName; + } + set + { + if (value == null) + { + value = String.Empty; + } + if (value != this.dataPropertyName) + { + this.dataPropertyName = value; + if (this.DataGridView != null) + { + this.DataGridView.OnColumnDataPropertyNameChanged(this); + } + } + } + } + + /// + [ + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnDefaultCellStyleDescr) + ] + public override DataGridViewCellStyle DefaultCellStyle + { + get + { + return base.DefaultCellStyle; + } + set + { + base.DefaultCellStyle = value; + } + } + + private bool ShouldSerializeDefaultCellStyle() + { + + if (!this.HasDefaultCellStyle) + { + return false; + } + + DataGridViewCellStyle defaultCellStyle = this.DefaultCellStyle; + + return (!defaultCellStyle.BackColor.IsEmpty || + !defaultCellStyle.ForeColor.IsEmpty || + !defaultCellStyle.SelectionBackColor.IsEmpty || + !defaultCellStyle.SelectionForeColor.IsEmpty || + defaultCellStyle.Font != null || + !defaultCellStyle.IsNullValueDefault || + !defaultCellStyle.IsDataSourceNullValueDefault || + !String.IsNullOrEmpty(defaultCellStyle.Format) || + !defaultCellStyle.FormatProvider.Equals(System.Globalization.CultureInfo.CurrentCulture) || + defaultCellStyle.Alignment != DataGridViewContentAlignment.NotSet || + defaultCellStyle.WrapMode != DataGridViewTriState.NotSet || + defaultCellStyle.Tag != null || + !defaultCellStyle.Padding.Equals(Padding.Empty)); + } + + internal int DesiredFillWidth + { + get + { + return this.desiredFillWidth; + } + set + { + this.desiredFillWidth = value; + } + } + + internal int DesiredMinimumWidth + { + get + { + return this.desiredMinimumWidth; + } + set + { + this.desiredMinimumWidth = value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int DisplayIndex + { + get + { + return this.displayIndex; + } + set + { + if (this.displayIndex != value) + { + if (value == Int32.MaxValue) + { + throw new ArgumentOutOfRangeException("DisplayIndex", value, SR.GetString(SR.DataGridViewColumn_DisplayIndexTooLarge, Int32.MaxValue.ToString(CultureInfo.CurrentCulture))); + } + if (this.DataGridView != null) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("DisplayIndex", value, SR.GetString(SR.DataGridViewColumn_DisplayIndexNegative)); + } + if (value >= this.DataGridView.Columns.Count) + { + throw new ArgumentOutOfRangeException("DisplayIndex", value, SR.GetString(SR.DataGridViewColumn_DisplayIndexExceedsColumnCount)); + } + // Will throw an error if a visible frozen column is placed inside a non-frozen area or vice-versa. + this.DataGridView.OnColumnDisplayIndexChanging(this, value); + this.displayIndex = value; + try + { + this.DataGridView.InDisplayIndexAdjustments = true; + this.DataGridView.OnColumnDisplayIndexChanged_PreNotification(); + this.DataGridView.OnColumnDisplayIndexChanged(this); + this.DataGridView.OnColumnDisplayIndexChanged_PostNotification(); + } + finally + { + this.DataGridView.InDisplayIndexAdjustments = false; + } + } + else + { + if (value < -1) + { + throw new ArgumentOutOfRangeException("DisplayIndex", value, SR.GetString(SR.DataGridViewColumn_DisplayIndexTooNegative)); + } + this.displayIndex = value; + } + } + } + } + + internal bool DisplayIndexHasChanged + { + get + { + return (this.flags & DATAGRIDVIEWCOLUMN_displayIndexHasChangedInternal) != 0; + } + set + { + if (value) + { + this.flags |= (byte) DATAGRIDVIEWCOLUMN_displayIndexHasChangedInternal; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_displayIndexHasChangedInternal); + } + } + } + + internal int DisplayIndexInternal + { + set + { + Debug.Assert(value >= -1); + Debug.Assert(value < Int32.MaxValue); + + this.displayIndex = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler Disposed + { + add + { + this.disposed += value; + } + remove + { + this.disposed -= value; + } + } + + /// + [ + DefaultValue(0), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_ColumnDividerWidthDescr) + ] + public int DividerWidth + { + get + { + return this.DividerThickness; + } + set + { + this.DividerThickness = value; + } + } + + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(DATAGRIDVIEWCOLUMN_defaultFillWeight), + SRDescription(SR.DataGridViewColumn_FillWeightDescr), + ] + public float FillWeight + { + get + { + return this.fillWeight; + } + set + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException("FillWeight", SR.GetString(SR.InvalidLowBoundArgument, "FillWeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (value > (float)ushort.MaxValue) + { + throw new ArgumentOutOfRangeException("FillWeight", SR.GetString(SR.InvalidHighBoundArgumentEx, "FillWeight", (value).ToString(CultureInfo.CurrentCulture), (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + if (this.DataGridView != null) + { + this.DataGridView.OnColumnFillWeightChanging(this, value); + this.fillWeight = value; + this.DataGridView.OnColumnFillWeightChanged(this); + } + else + { + this.fillWeight = value; + } + } + } + + internal float FillWeightInternal + { + set + { + Debug.Assert(value > 0); + this.fillWeight = value; + } + } + + /// + [ + DefaultValue(false), + RefreshProperties(RefreshProperties.All), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_ColumnFrozenDescr) + ] + public override bool Frozen + { + get + { + return base.Frozen; + } + set + { + base.Frozen = value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewColumnHeaderCell HeaderCell + { + get + { + return (DataGridViewColumnHeaderCell) base.HeaderCellCore; + } + set + { + base.HeaderCellCore = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnHeaderTextDescr), + Localizable(true) + ] + public string HeaderText + { + get + { + if (this.HasHeaderCell) + { + string headerValue = this.HeaderCell.Value as string; + if (headerValue != null) + { + return headerValue; + } + else + { + return string.Empty; + } + } + else + { + return string.Empty; + } + } + set + { + if ((value != null || this.HasHeaderCell) && + this.HeaderCell.ValueType != null && + this.HeaderCell.ValueType.IsAssignableFrom(typeof(System.String))) + { + this.HeaderCell.Value = value; + } + } + } + + private bool ShouldSerializeHeaderText() + { + return this.HasHeaderCell && ((DataGridViewColumnHeaderCell) this.HeaderCell).ContainsLocalValue; + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewAutoSizeColumnMode InheritedAutoSizeMode + { + get + { + return GetInheritedAutoSizeMode(this.DataGridView); + } + } + + /// + [ + Browsable(false) + ] + public override DataGridViewCellStyle InheritedStyle + { + get + { + DataGridViewCellStyle columnStyle = null; + Debug.Assert(this.Index > -1); + if (this.HasDefaultCellStyle) + { + columnStyle = this.DefaultCellStyle; + Debug.Assert(columnStyle != null); + } + + if (this.DataGridView == null) + { + return columnStyle; + } + + DataGridViewCellStyle inheritedCellStyleTmp = new DataGridViewCellStyle(); + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (columnStyle != null && !columnStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = columnStyle.BackColor; + } + else + { + inheritedCellStyleTmp.BackColor = dataGridViewStyle.BackColor; + } + + if (columnStyle != null && !columnStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = columnStyle.ForeColor; + } + else + { + inheritedCellStyleTmp.ForeColor = dataGridViewStyle.ForeColor; + } + + if (columnStyle != null && !columnStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = columnStyle.SelectionBackColor; + } + else + { + inheritedCellStyleTmp.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (columnStyle != null && !columnStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = columnStyle.SelectionForeColor; + } + else + { + inheritedCellStyleTmp.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + + if (columnStyle != null && columnStyle.Font != null) + { + inheritedCellStyleTmp.Font = columnStyle.Font; + } + else + { + inheritedCellStyleTmp.Font = dataGridViewStyle.Font; + } + + if (columnStyle != null && !columnStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = columnStyle.NullValue; + } + else + { + inheritedCellStyleTmp.NullValue = dataGridViewStyle.NullValue; + } + + if (columnStyle != null && !columnStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = columnStyle.DataSourceNullValue; + } + else + { + inheritedCellStyleTmp.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (columnStyle != null && columnStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = columnStyle.Format; + } + else + { + inheritedCellStyleTmp.Format = dataGridViewStyle.Format; + } + + if (columnStyle != null && !columnStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = columnStyle.FormatProvider; + } + else + { + inheritedCellStyleTmp.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (columnStyle != null && columnStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = columnStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyleTmp.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (columnStyle != null && columnStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = columnStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyleTmp.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (columnStyle != null && columnStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = columnStyle.Tag; + } + else + { + inheritedCellStyleTmp.Tag = dataGridViewStyle.Tag; + } + + if (columnStyle != null && columnStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = columnStyle.Padding; + } + else + { + inheritedCellStyleTmp.PaddingInternal = dataGridViewStyle.Padding; + } + + return inheritedCellStyleTmp; + } + } + + internal bool IsBrowsableInternal + { + get + { + return (this.flags & DATAGRIDVIEWCOLUMN_isBrowsableInternal) != 0; + } + set + { + if (value) + { + this.flags |= (byte) DATAGRIDVIEWCOLUMN_isBrowsableInternal; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_isBrowsableInternal); + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool IsDataBound + { + get + { + return this.IsDataBoundInternal; + } + } + + internal bool IsDataBoundInternal + { + get + { + return (this.flags & DATAGRIDVIEWCOLUMN_isDataBound) != 0; + } + set + { + if (value) + { + this.flags |= (byte)DATAGRIDVIEWCOLUMN_isDataBound; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_isDataBound); + } + } + } + + /// + [ + DefaultValue(DATAGRIDVIEWCOLUMN_defaultMinColumnThickness), + Localizable(true), + SRCategory(SR.CatLayout), + SRDescription(SR.DataGridView_ColumnMinimumWidthDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int MinimumWidth + { + get + { + return this.MinimumThickness; + } + set + { + this.MinimumThickness = value; + } + } + + /// + [ + Browsable(false) + ] + public string Name + { + get + { + // + // Change needed to bring the design time and the runtime "Name" property together. + // The ExtenderProvider adds a "Name" property of its own. It does this for all IComponents. + // The "Name" property added by the ExtenderProvider interacts only w/ the Site property. + // The Control class' Name property can be changed only thru the "Name" property provided by the + // Extender Service. + // + // However, the user can change the DataGridView::Name property in the DataGridViewEditColumnDialog. + // So while the Control can fall back to Site.Name if the user did not explicitly set Control::Name, + // the DataGridViewColumn should always go first to the Site.Name to retrieve the name. + // + // NOTE: one side effect of bringing together the design time and the run time "Name" properties is that DataGridViewColumn::Name changes. + // However, DataGridView does not fire ColumnNameChanged event. + // We can't fix this because ISite does not provide Name change notification. So in effect + // DataGridViewColumn does not know when its name changed. + // I talked w/ MarkRi and he is perfectly fine w/ DataGridViewColumn::Name changing w/o ColumnNameChanged + // being fired. + // + if (this.Site != null && !String.IsNullOrEmpty(this.Site.Name)) + { + this.name = this.Site.Name; + } + + return name; + } + set + { + string oldName = this.name; + if (String.IsNullOrEmpty(value)) + { + this.name = string.Empty; + } + else + { + this.name = value; + } + + if (this.DataGridView != null && !string.Equals(this.name, oldName,StringComparison.Ordinal)) + { + this.DataGridView.OnColumnNameChanged(this); + } + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnReadOnlyDescr) + ] + public override bool ReadOnly + { + get + { + return base.ReadOnly; + } + set + { + if (this.IsDataBound && + this.DataGridView != null && + this.DataGridView.DataConnection != null && + this.boundColumnIndex != -1 && + this.DataGridView.DataConnection.DataFieldIsReadOnly(this.boundColumnIndex) && + !value) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnBoundToAReadOnlyFieldMustRemainReadOnly)); + } + base.ReadOnly = value; + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnResizableDescr) + ] + public override DataGridViewTriState Resizable + { + get + { + return base.Resizable; + } + set + { + base.Resizable = value; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ISite Site + { + get + { + return this.site; + } + set + { + this.site = value; + } + } + + /// + [ + DefaultValue(DataGridViewColumnSortMode.NotSortable), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ColumnSortModeDescr) + ] + public DataGridViewColumnSortMode SortMode + { + get + { + if ((this.flags & DATAGRIDVIEWCOLUMN_automaticSort) != 0x00) + { + return DataGridViewColumnSortMode.Automatic; + } + else if ((this.flags & DATAGRIDVIEWCOLUMN_programmaticSort) != 0x00) + { + return DataGridViewColumnSortMode.Programmatic; + } + else + { + return DataGridViewColumnSortMode.NotSortable; + } + } + set + { + if (value != this.SortMode) + { + if (value != DataGridViewColumnSortMode.NotSortable) + { + if (this.DataGridView != null && + !this.DataGridView.InInitialization && + value == DataGridViewColumnSortMode.Automatic && + (this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, (value).ToString(), this.DataGridView.SelectionMode.ToString())); + } + if (value == DataGridViewColumnSortMode.Automatic) + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_programmaticSort); + this.flags |= (byte)DATAGRIDVIEWCOLUMN_automaticSort; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_automaticSort); + this.flags |= (byte)DATAGRIDVIEWCOLUMN_programmaticSort; + } + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_automaticSort); + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOLUMN_programmaticSort); + } + if (this.DataGridView != null) + { + this.DataGridView.OnColumnSortModeChanged(this); + } + } + } + } + + /// + [ + DefaultValue(""), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnToolTipTextDescr) + ] + public string ToolTipText + { + get + { + return this.HeaderCell.ToolTipText; + } + set + { + if (String.Compare(this.ToolTipText, value, false /*ignore case*/, CultureInfo.InvariantCulture) != 0) + { + this.HeaderCell.ToolTipText = value; + + if (this.DataGridView != null) + { + this.DataGridView.OnColumnToolTipTextChanged(this); + } + } + } + } + + internal float UsedFillWeight + { + get + { + return this.usedFillWeight; + } + set + { + Debug.Assert(value > 0); + this.usedFillWeight = value; + } + } + + /// + [ + Browsable(false), + DefaultValue(null), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public Type ValueType + { + get + { + return (Type) this.Properties.GetObject(PropDataGridViewColumnValueType); + } + set + { + // what should we do when we modify the ValueType in the dataGridView column??? + this.Properties.SetObject(PropDataGridViewColumnValueType, value); + } + } + + /// + [ + DefaultValue(true), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnVisibleDescr) + ] + public override bool Visible + { + get + { + return base.Visible; + } + set + { + base.Visible = value; + } + } + + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.DataGridView_ColumnWidthDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int Width + { + get + { + return this.Thickness; + } + set + { + this.Thickness = value; + } + } + + /// + public override object Clone() + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) System.Activator.CreateInstance(this.GetType()); + if (dataGridViewColumn != null) + { + CloneInternal(dataGridViewColumn); + } + return dataGridViewColumn; + } + + internal void CloneInternal(DataGridViewColumn dataGridViewColumn) + { + base.CloneInternal(dataGridViewColumn); + + dataGridViewColumn.name = this.Name; + dataGridViewColumn.displayIndex = -1; + dataGridViewColumn.HeaderText = this.HeaderText; + dataGridViewColumn.DataPropertyName = this.DataPropertyName; + + // dataGridViewColumn.boundColumnConverter = columnTemplate.BoundColumnConverter; setting the DataPropertyName should also set the bound column converter later on. + if (dataGridViewColumn.CellTemplate != null) + { + dataGridViewColumn.cellTemplate = (DataGridViewCell)this.CellTemplate.Clone(); + } + else + { + dataGridViewColumn.cellTemplate = null; + } + + if (this.HasHeaderCell) + { + dataGridViewColumn.HeaderCell = (DataGridViewColumnHeaderCell) this.HeaderCell.Clone(); + } + + dataGridViewColumn.AutoSizeMode = this.AutoSizeMode; + dataGridViewColumn.SortMode = this.SortMode; + dataGridViewColumn.FillWeightInternal = this.FillWeight; + } + + /// + protected override void Dispose(bool disposing) { + try + { + if (disposing) + { + // + lock(this) + { + if (this.site != null && this.site.Container != null) + { + this.site.Container.Remove(this); + } + + if (this.disposed != null) + { + this.disposed(this, EventArgs.Empty); + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + internal DataGridViewAutoSizeColumnMode GetInheritedAutoSizeMode(DataGridView dataGridView) + { + if (dataGridView != null && this.autoSizeMode == DataGridViewAutoSizeColumnMode.NotSet) + { + switch (dataGridView.AutoSizeColumnsMode) + { + case DataGridViewAutoSizeColumnsMode.AllCells: + return DataGridViewAutoSizeColumnMode.AllCells; + + case DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader: + return DataGridViewAutoSizeColumnMode.AllCellsExceptHeader; + + case DataGridViewAutoSizeColumnsMode.DisplayedCells: + return DataGridViewAutoSizeColumnMode.DisplayedCells; + + case DataGridViewAutoSizeColumnsMode.DisplayedCellsExceptHeader: + return DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader; + + case DataGridViewAutoSizeColumnsMode.ColumnHeader: + return DataGridViewAutoSizeColumnMode.ColumnHeader; + + case DataGridViewAutoSizeColumnsMode.Fill: + return DataGridViewAutoSizeColumnMode.Fill; + + default: // None + return DataGridViewAutoSizeColumnMode.None; + } + } + return this.autoSizeMode; + } + + /// + public virtual int GetPreferredWidth(DataGridViewAutoSizeColumnMode autoSizeColumnMode, bool fixedHeight) + { + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.NotSet || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.None || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedColumnAutoSizingCriteria, "autoSizeColumnMode")); + } + switch (autoSizeColumnMode) { + case DataGridViewAutoSizeColumnMode.NotSet: + case DataGridViewAutoSizeColumnMode.None: + case DataGridViewAutoSizeColumnMode.ColumnHeader: + case DataGridViewAutoSizeColumnMode.AllCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.AllCells: + case DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.DisplayedCells: + case DataGridViewAutoSizeColumnMode.Fill: + break; + default: + throw new InvalidEnumArgumentException("value", (int) autoSizeColumnMode, typeof(DataGridViewAutoSizeColumnMode)); + } + + DataGridView dataGridView = this.DataGridView; + + Debug.Assert(dataGridView == null || this.Index > -1); + + if (dataGridView == null) + { + return -1; + } + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal) autoSizeColumnMode; + Debug.Assert(autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.Header || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.AllRows || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows) || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows)); + + int preferredColumnThickness = 0, preferredCellThickness, rowIndex; + DataGridViewRow dataGridViewRow; + Debug.Assert(dataGridView.ColumnHeadersVisible || autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Header); + + // take into account the preferred width of the header cell if displayed and cared about + if (dataGridView.ColumnHeadersVisible && + (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header) != 0) + { + if (fixedHeight) + { + preferredCellThickness = this.HeaderCell.GetPreferredWidth(-1, dataGridView.ColumnHeadersHeight); + } + else + { + preferredCellThickness = this.HeaderCell.GetPreferredSize(-1).Width; + } + if (preferredColumnThickness < preferredCellThickness) + { + preferredColumnThickness = preferredCellThickness; + } + } + if ((autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.AllRows) != 0) + { + for (rowIndex = dataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = dataGridView.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + dataGridViewRow = dataGridView.Rows.SharedRow(rowIndex); + if (fixedHeight) + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredWidth(rowIndex, dataGridViewRow.Thickness); + } + else + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredSize(rowIndex).Width; + } + if (preferredColumnThickness < preferredCellThickness) + { + preferredColumnThickness = preferredCellThickness; + } + } + } + else if ((autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows) != 0) + { + int displayHeight = dataGridView.LayoutInfo.Data.Height; + int cy = 0; + + rowIndex = dataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + dataGridViewRow = dataGridView.Rows.SharedRow(rowIndex); + if (fixedHeight) + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredWidth(rowIndex, dataGridViewRow.Thickness); + } + else + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredSize(rowIndex).Width; + } + if (preferredColumnThickness < preferredCellThickness) + { + preferredColumnThickness = preferredCellThickness; + } + cy += dataGridViewRow.Thickness; + rowIndex = dataGridView.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + if (cy < displayHeight) + { + rowIndex = dataGridView.DisplayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + dataGridViewRow = dataGridView.Rows.SharedRow(rowIndex); + if (fixedHeight) + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredWidth(rowIndex, dataGridViewRow.Thickness); + } + else + { + preferredCellThickness = dataGridViewRow.Cells[this.Index].GetPreferredSize(rowIndex).Width; + } + if (preferredColumnThickness < preferredCellThickness) + { + preferredColumnThickness = preferredCellThickness; + } + cy += dataGridViewRow.Thickness; + rowIndex = dataGridView.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + return preferredColumnThickness; + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnCollection.cs new file mode 100644 index 000000000..611597d66 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnCollection.cs @@ -0,0 +1,1303 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Drawing; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + + /// + /// + /// Represents a collection of objects in the + /// control. + /// + [ + ListBindable(false), + SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable"), // Columns are only disposed in the designer. + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewColumnCollection : BaseCollection, IList + { + private CollectionChangeEventHandler onCollectionChanged; + private ArrayList items = new ArrayList(); + private ArrayList itemsSorted; + private int lastAccessedSortedIndex = -1; + private int columnCountsVisible, columnCountsVisibleSelected; + private int columnsWidthVisible, columnsWidthVisibleFrozen; + private static ColumnOrderComparer columnOrderComparer = new ColumnOrderComparer(); + private DataGridView dataGridView; + + /* IList interface implementation */ + + /// + /// + bool IList.IsFixedSize + { + get {return false;} + } + + /// + /// + bool IList.IsReadOnly + { + get {return false;} + } + + /// + /// + object IList.this[int index] + { + get { return this[index]; } + set { throw new NotSupportedException(); } + } + + /// + /// + int IList.Add(object value) + { + return this.Add((DataGridViewColumn) value); + } + + /// + /// + void IList.Clear() + { + this.Clear(); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + this.Insert(index, (DataGridViewColumn) value); + } + + /// + /// + void IList.Remove(object value) + { + this.Remove((DataGridViewColumn) value); + } + + /// + /// + void IList.RemoveAt(int index) + { + this.RemoveAt(index); + } + + + /* ICollection interface implementation */ + + /// + /// + int ICollection.Count + { + get + { + return this.items.Count; + } + } + + /// + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + /// + object ICollection.SyncRoot + { + get + { + return this; + } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + + /* IEnumerable interface implementation */ + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + + /// + public DataGridViewColumnCollection(DataGridView dataGridView) + { + InvalidateCachedColumnCounts(); + InvalidateCachedColumnsWidths(); + this.dataGridView = dataGridView; + } + + internal static IComparer ColumnCollectionOrderComparer + { + get + { + return System.Windows.Forms.DataGridViewColumnCollection.columnOrderComparer; + } + } + + /// + protected override ArrayList List + { + get + { + return this.items; + } + } + + /// + protected DataGridView DataGridView + { + get + { + return this.dataGridView; + } + } + + /// + /// + /// Retrieves the DataGridViewColumn with the specified index. + /// + public DataGridViewColumn this[int index] + { + get + { + return (DataGridViewColumn) this.items[index]; + } + } + + /// + /// + /// Retrieves the DataGridViewColumn with the Name provided. + /// + public DataGridViewColumn this[string columnName] + { + get + { + if (columnName == null) + { + throw new ArgumentNullException("columnName"); + } + int itemCount = this.items.Count; + for (int i = 0; i < itemCount; ++i) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.items[i]; + // NOTE: case-insensitive + if (String.Equals(dataGridViewColumn.Name, columnName, StringComparison.OrdinalIgnoreCase)) + { + return dataGridViewColumn; + } + } + return null; + } + } + + /// + public event CollectionChangeEventHandler CollectionChanged + { + add + { + this.onCollectionChanged += value; + } + remove + { + this.onCollectionChanged -= value; + } + } + + internal int ActualDisplayIndexToColumnIndex(int actualDisplayIndex, DataGridViewElementStates includeFilter) + { + // Microsoft: is there a faster way to get the column index? + DataGridViewColumn dataGridViewColumn = GetFirstColumn(includeFilter); + for (int i = 0; i < actualDisplayIndex; i ++) + { + dataGridViewColumn = GetNextColumn(dataGridViewColumn, includeFilter, DataGridViewElementStates.None); + } + return dataGridViewColumn.Index; + } + + /// + /// + /// Adds a to this collection. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual int Add(string columnName, string headerText) + { + DataGridViewTextBoxColumn dataGridViewTextBoxColumn = new DataGridViewTextBoxColumn(); + dataGridViewTextBoxColumn.Name = columnName; + dataGridViewTextBoxColumn.HeaderText = headerText; + + return Add(dataGridViewTextBoxColumn); + } + + /// + /// + /// Adds a to this collection. + /// + public virtual int Add(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(this.DataGridView != null); + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + if (this.DataGridView.InDisplayIndexAdjustments) + { + // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + this.DataGridView.OnAddingColumn(dataGridViewColumn); // will throw an exception if the addition is illegal + + InvalidateCachedColumnsOrder(); + int index = this.items.Add(dataGridViewColumn); + dataGridViewColumn.IndexInternal = index; + dataGridViewColumn.DataGridViewInternal = dataGridView; + UpdateColumnCaches(dataGridViewColumn, true); + this.DataGridView.OnAddedColumn(dataGridViewColumn); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewColumn), false /*changeIsInsertion*/, new Point(-1, -1)); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + return index; + } + + /// + public virtual void AddRange(params DataGridViewColumn[] dataGridViewColumns) + { + if (dataGridViewColumns == null) + { + throw new ArgumentNullException("dataGridViewColumns"); + } + + Debug.Assert(this.DataGridView != null); + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + if (this.DataGridView.InDisplayIndexAdjustments) + { + // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + // Order the columns by ascending DisplayIndex so that their display indexes are not altered by the operation. + // The columns with DisplayIndex == -1 are left untouched relative to each other and put at the end of the array. + ArrayList initialColumns = new ArrayList(dataGridViewColumns.Length); + ArrayList sortedColumns = new ArrayList(dataGridViewColumns.Length); + + // All columns with DisplayIndex != -1 are put into the initialColumns array + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + if (dataGridViewColumn.DisplayIndex != -1) + { + initialColumns.Add(dataGridViewColumn); + } + } + + // Those columns are copied into the sortedColumns array in an N^2 sort algo that + // does not disrupt the order of columns with identical DisplayIndex values. + int smallestDisplayIndex, smallestIndex, index; + + while (initialColumns.Count > 0) + { + smallestDisplayIndex = Int32.MaxValue; + smallestIndex = -1; + for (index = 0; index < initialColumns.Count; index++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) initialColumns[index]; + if (dataGridViewColumn.DisplayIndex < smallestDisplayIndex) + { + smallestDisplayIndex = dataGridViewColumn.DisplayIndex; + smallestIndex = index; + } + } + Debug.Assert(smallestIndex >= 0); + sortedColumns.Add(initialColumns[smallestIndex]); + initialColumns.RemoveAt(smallestIndex); + } + + // The columns with DisplayIndex == -1 are append at the end of sortedColumns + // without disrupting their relative order. + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + if (dataGridViewColumn.DisplayIndex == -1) + { + sortedColumns.Add(dataGridViewColumn); + } + } + + // Finally the dataGridViewColumns is reconstructed using the sortedColumns. + index = 0; + foreach (DataGridViewColumn dataGridViewColumn in sortedColumns) + { + dataGridViewColumns[index] = dataGridViewColumn; + index++; + } + + this.DataGridView.OnAddingColumns(dataGridViewColumns); // will throw an exception if the addition is illegal + + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + InvalidateCachedColumnsOrder(); + index = this.items.Add(dataGridViewColumn); + dataGridViewColumn.IndexInternal = index; + dataGridViewColumn.DataGridViewInternal = dataGridView; + UpdateColumnCaches(dataGridViewColumn, true); + this.DataGridView.OnAddedColumn(dataGridViewColumn); + } + + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), false /*changeIsInsertion*/, new Point(-1, -1)); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + } + + /// + public virtual void Clear() + { + if (this.Count > 0) + { + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + if (this.DataGridView.InDisplayIndexAdjustments) + { + // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + for (int columnIndex = 0; columnIndex < this.Count; columnIndex++) + { + DataGridViewColumn dataGridViewColumn = this[columnIndex]; + // Detach the column... + dataGridViewColumn.DataGridViewInternal = null; + // ...and its potential header cell + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.DataGridViewInternal = null; + } + } + + DataGridViewColumn[] aColumns = new DataGridViewColumn[this.items.Count]; + CopyTo(aColumns, 0); + + this.DataGridView.OnClearingColumns(); + InvalidateCachedColumnsOrder(); + this.items.Clear(); + InvalidateCachedColumnCounts(); + InvalidateCachedColumnsWidths(); + foreach (DataGridViewColumn dataGridViewColumn in aColumns) + { + this.DataGridView.OnColumnRemoved(dataGridViewColumn); + this.DataGridView.OnColumnHidden(dataGridViewColumn); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), false /*changeIsInsertion*/, new Point(-1, -1)); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + } + } + + internal int ColumnIndexToActualDisplayIndex(int columnIndex, DataGridViewElementStates includeFilter) + { + // map the column index to the actual display index + DataGridViewColumn dataGridViewColumn = GetFirstColumn(includeFilter); + int actualDisplayIndex = 0; + while (dataGridViewColumn != null && dataGridViewColumn.Index != columnIndex) + { + dataGridViewColumn = GetNextColumn(dataGridViewColumn, includeFilter, DataGridViewElementStates.None); + actualDisplayIndex ++; + } + return actualDisplayIndex; + } + + /// + /// + /// Checks to see if a DataGridViewColumn is contained in this collection. + /// + public virtual bool Contains(DataGridViewColumn dataGridViewColumn) + { + return this.items.IndexOf(dataGridViewColumn) != -1; + } + + /// + public virtual bool Contains(string columnName) + { + if (columnName == null) + { + throw new ArgumentNullException("columnName"); + } + int itemCount = this.items.Count; + for (int i = 0; i < itemCount; ++i) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.items[i]; + // NOTE: case-insensitive + if (0 == String.Compare(dataGridViewColumn.Name, columnName, true, CultureInfo.InvariantCulture)) + { + return true; + } + } + return false; + } + + /// + public void CopyTo(DataGridViewColumn[] array, int index) + { + this.items.CopyTo(array, index); + } + + internal bool DisplayInOrder(int columnIndex1, int columnIndex2) + { + int displayIndex1 = ((DataGridViewColumn) this.items[columnIndex1]).DisplayIndex; + int displayIndex2 = ((DataGridViewColumn) this.items[columnIndex2]).DisplayIndex; + return displayIndex1 < displayIndex2; + } + + internal DataGridViewColumn GetColumnAtDisplayIndex(int displayIndex) + { + if (displayIndex < 0 || displayIndex >= this.items.Count) + { + return null; + } + DataGridViewColumn dataGridViewColumn = ((DataGridViewColumn) this.items[displayIndex]); + if (dataGridViewColumn.DisplayIndex == displayIndex) + { + // Performance gain if display indexes coincide with indexes. + return dataGridViewColumn; + } + for (int columnIndex = 0; columnIndex < this.items.Count; columnIndex++) + { + dataGridViewColumn = ((DataGridViewColumn) this.items[columnIndex]); + if (dataGridViewColumn.DisplayIndex == displayIndex) + { + return dataGridViewColumn; + } + } + Debug.Fail("no column found in GetColumnAtDisplayIndex"); + return null; + } + + /// + public int GetColumnCount(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + + // cache returned value and reuse it as long as none + // of the column's state has changed. + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.columnCountsVisible != -1) + { + return this.columnCountsVisible; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + if (this.columnCountsVisibleSelected != -1) + { + return this.columnCountsVisibleSelected; + } + break; + } + + int columnCount = 0; + if ((includeFilter & DataGridViewElementStates.Resizable) == 0) + { + for (int columnIndex = 0; columnIndex < this.items.Count; columnIndex++) + { + if (((DataGridViewColumn)this.items[columnIndex]).StateIncludes(includeFilter)) + { + columnCount++; + } + } + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + this.columnCountsVisible = columnCount; + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + this.columnCountsVisibleSelected = columnCount; + break; + } + } + else + { + DataGridViewElementStates correctedIncludeFilter = includeFilter & ~DataGridViewElementStates.Resizable; + for (int columnIndex = 0; columnIndex < this.items.Count; columnIndex++) + { + if (((DataGridViewColumn)this.items[columnIndex]).StateIncludes(correctedIncludeFilter) && + ((DataGridViewColumn)this.items[columnIndex]).Resizable == DataGridViewTriState.True) + { + columnCount++; + } + } + } + return columnCount; + } + + internal int GetColumnCount(DataGridViewElementStates includeFilter, int fromColumnIndex, int toColumnIndex) + { + Debug.Assert((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) == 0); + Debug.Assert((includeFilter & DataGridViewElementStates.Resizable) == 0); + Debug.Assert(DisplayInOrder(fromColumnIndex, toColumnIndex)); + Debug.Assert(((DataGridViewColumn) this.items[toColumnIndex]).StateIncludes(includeFilter)); + + int jumpColumns = 0; + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.items[fromColumnIndex]; + + while (dataGridViewColumn != (DataGridViewColumn) this.items[toColumnIndex]) + { + dataGridViewColumn = GetNextColumn(dataGridViewColumn, includeFilter, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn.StateIncludes(includeFilter)) + { + jumpColumns++; + } + } + return jumpColumns; + } + + private int GetColumnSortedIndex(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(this.itemsSorted != null); + Debug.Assert(this.lastAccessedSortedIndex == -1 || + this.lastAccessedSortedIndex < this.Count); + +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + if (this.lastAccessedSortedIndex != -1 && + this.itemsSorted[this.lastAccessedSortedIndex] == dataGridViewColumn) + { + return this.lastAccessedSortedIndex; + } + + int index = 0; + while (index < this.itemsSorted.Count) + { + if (dataGridViewColumn.Index == ((DataGridViewColumn) this.itemsSorted[index]).Index) + { + this.lastAccessedSortedIndex = index; + return index; + } + index++; + } + return -1; + } + + internal float GetColumnsFillWeight(DataGridViewElementStates includeFilter) + { + Debug.Assert((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) == 0); + + float weightSum = 0F; + for (int columnIndex = 0; columnIndex < this.items.Count; columnIndex++) + { + if (((DataGridViewColumn)this.items[columnIndex]).StateIncludes(includeFilter)) + { + weightSum += ((DataGridViewColumn)this.items[columnIndex]).FillWeight; + } + } + return weightSum; + } + + /// + public int GetColumnsWidth(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + + // cache returned value and reuse it as long as none + // of the column's state/thickness has changed. + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.columnsWidthVisible != -1) + { + return this.columnsWidthVisible; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.columnsWidthVisibleFrozen != -1) + { + return this.columnsWidthVisibleFrozen; + } + break; + } + + int columnsWidth = 0; + for(int columnIndex = 0; columnIndex < this.items.Count; columnIndex++) + { + if (((DataGridViewColumn) this.items[columnIndex]).StateIncludes(includeFilter)) + { + columnsWidth += ((DataGridViewColumn) this.items[columnIndex]).Thickness; + } + } + + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + this.columnsWidthVisible = columnsWidth; + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + this.columnsWidthVisibleFrozen = columnsWidth; + break; + } + return columnsWidth; + } + + /// + public DataGridViewColumn GetFirstColumn(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + + if (this.itemsSorted == null) + { + UpdateColumnOrderCache(); + } +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + int index = 0; + while (index < this.itemsSorted.Count) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.itemsSorted[index]; + if (dataGridViewColumn.StateIncludes(includeFilter)) + { + this.lastAccessedSortedIndex = index; + return dataGridViewColumn; + } + index++; + } + return null; + } + + /// + public DataGridViewColumn GetFirstColumn(DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (excludeFilter == DataGridViewElementStates.None) + { + return GetFirstColumn(includeFilter); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + + if (this.itemsSorted == null) + { + UpdateColumnOrderCache(); + } +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + int index = 0; + while (index < this.itemsSorted.Count) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.itemsSorted[index]; + if (dataGridViewColumn.StateIncludes(includeFilter) && + dataGridViewColumn.StateExcludes(excludeFilter)) + { + this.lastAccessedSortedIndex = index; + return dataGridViewColumn; + } + index++; + } + return null; + } + + /// + public DataGridViewColumn GetLastColumn(DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + + if (this.itemsSorted == null) + { + UpdateColumnOrderCache(); + } +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + int index = this.itemsSorted.Count - 1; + while (index >= 0) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.itemsSorted[index]; + if (dataGridViewColumn.StateIncludes(includeFilter) && + dataGridViewColumn.StateExcludes(excludeFilter)) + { + this.lastAccessedSortedIndex = index; + return dataGridViewColumn; + } + index--; + } + return null; + } + + /// + public DataGridViewColumn GetNextColumn(DataGridViewColumn dataGridViewColumnStart, + DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (dataGridViewColumnStart == null) + { + throw new ArgumentNullException("dataGridViewColumnStart"); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + + if (this.itemsSorted == null) + { + UpdateColumnOrderCache(); + } +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + int index = GetColumnSortedIndex(dataGridViewColumnStart); + if (index == -1) + { + bool columnFound = false; + int indexMin = Int32.MaxValue, displayIndexMin = Int32.MaxValue; + for (index = 0; index < this.items.Count; index++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.items[index]; + if (dataGridViewColumn.StateIncludes(includeFilter) && + dataGridViewColumn.StateExcludes(excludeFilter) && + (dataGridViewColumn.DisplayIndex > dataGridViewColumnStart.DisplayIndex || + (dataGridViewColumn.DisplayIndex == dataGridViewColumnStart.DisplayIndex && + dataGridViewColumn.Index > dataGridViewColumnStart.Index))) + { + if (dataGridViewColumn.DisplayIndex < displayIndexMin || + (dataGridViewColumn.DisplayIndex == displayIndexMin && + dataGridViewColumn.Index < indexMin)) + { + indexMin = index; + displayIndexMin = dataGridViewColumn.DisplayIndex; + columnFound = true; + } + } + } + return columnFound ? ((DataGridViewColumn) this.items[indexMin]) : null; + } + else + { + index++; + while (index < this.itemsSorted.Count) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.itemsSorted[index]; + + if (dataGridViewColumn.StateIncludes (includeFilter) && dataGridViewColumn.StateExcludes (excludeFilter)) + { + this.lastAccessedSortedIndex = index; + return dataGridViewColumn; + } + + index++; + } + } + return null; + } + + /// + public DataGridViewColumn GetPreviousColumn(DataGridViewColumn dataGridViewColumnStart, + DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (dataGridViewColumnStart == null) + { + throw new ArgumentNullException("dataGridViewColumnStart"); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + + if (this.itemsSorted == null) + { + UpdateColumnOrderCache(); + } +#if DEBUG + Debug.Assert(VerifyColumnOrderCache()); +#endif + int index = GetColumnSortedIndex(dataGridViewColumnStart); + if (index == -1) + { + bool columnFound = false; + int indexMax = -1, displayIndexMax = -1; + for (index = 0; index < this.items.Count; index++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.items[index]; + if (dataGridViewColumn.StateIncludes(includeFilter) && + dataGridViewColumn.StateExcludes(excludeFilter) && + (dataGridViewColumn.DisplayIndex < dataGridViewColumnStart.DisplayIndex || + (dataGridViewColumn.DisplayIndex == dataGridViewColumnStart.DisplayIndex && + dataGridViewColumn.Index < dataGridViewColumnStart.Index))) + { + if (dataGridViewColumn.DisplayIndex > displayIndexMax || + (dataGridViewColumn.DisplayIndex == displayIndexMax && + dataGridViewColumn.Index > indexMax)) + { + indexMax = index; + displayIndexMax = dataGridViewColumn.DisplayIndex; + columnFound = true; + } + } + } + return columnFound ? ((DataGridViewColumn) this.items[indexMax]) : null; + } + else + { + index--; + while (index >= 0) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.itemsSorted[index]; + if (dataGridViewColumn.StateIncludes(includeFilter) && + dataGridViewColumn.StateExcludes(excludeFilter)) + { + this.lastAccessedSortedIndex = index; + return dataGridViewColumn; + } + index--; + } + } + return null; + } + + /// + public int IndexOf(DataGridViewColumn dataGridViewColumn) + { + return this.items.IndexOf(dataGridViewColumn); + } + + /// + /// + /// Inserts a in this collection. + /// + public virtual void Insert(int columnIndex, DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(this.DataGridView != null); + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + if (this.DataGridView.InDisplayIndexAdjustments) + { + // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + int originalDisplayIndex = dataGridViewColumn.DisplayIndex; + if (originalDisplayIndex == -1) + { + dataGridViewColumn.DisplayIndex = columnIndex; + } + Point newCurrentCell; + try + { + this.DataGridView.OnInsertingColumn(columnIndex, dataGridViewColumn, out newCurrentCell); // will throw an exception if the insertion is illegal + } + finally + { + dataGridViewColumn.DisplayIndexInternal = originalDisplayIndex; + } + InvalidateCachedColumnsOrder(); + this.items.Insert(columnIndex, dataGridViewColumn); + dataGridViewColumn.IndexInternal = columnIndex; + dataGridViewColumn.DataGridViewInternal = dataGridView; + UpdateColumnCaches(dataGridViewColumn, true); + this.DataGridView.OnInsertedColumn_PreNotification(dataGridViewColumn); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewColumn), true /*changeIsInsertion*/, newCurrentCell); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + } + + internal void InvalidateCachedColumnCount(DataGridViewElementStates includeFilter) + { + Debug.Assert(includeFilter == DataGridViewElementStates.Displayed || + includeFilter == DataGridViewElementStates.Selected || + includeFilter == DataGridViewElementStates.ReadOnly || + includeFilter == DataGridViewElementStates.Resizable || + includeFilter == DataGridViewElementStates.Frozen || + includeFilter == DataGridViewElementStates.Visible); + + if (includeFilter == DataGridViewElementStates.Visible) + { + InvalidateCachedColumnCounts(); + } + else if (includeFilter == DataGridViewElementStates.Selected) + { + this.columnCountsVisibleSelected = -1; + } + } + + internal void InvalidateCachedColumnCounts() + { + this.columnCountsVisible = this.columnCountsVisibleSelected = -1; + } + + internal void InvalidateCachedColumnsOrder() + { + this.itemsSorted = null; + } + + internal void InvalidateCachedColumnsWidth(DataGridViewElementStates includeFilter) + { + Debug.Assert(includeFilter == DataGridViewElementStates.Displayed || + includeFilter == DataGridViewElementStates.Selected || + includeFilter == DataGridViewElementStates.ReadOnly || + includeFilter == DataGridViewElementStates.Resizable || + includeFilter == DataGridViewElementStates.Frozen || + includeFilter == DataGridViewElementStates.Visible); + + if (includeFilter == DataGridViewElementStates.Visible) + { + InvalidateCachedColumnsWidths(); + } + else if (includeFilter == DataGridViewElementStates.Frozen) + { + this.columnsWidthVisibleFrozen = -1; + } + } + + internal void InvalidateCachedColumnsWidths() + { + this.columnsWidthVisible = this.columnsWidthVisibleFrozen = -1; + } + + /// + protected virtual void OnCollectionChanged(CollectionChangeEventArgs e) + { + if (this.onCollectionChanged != null) + { + this.onCollectionChanged(this, e); + } + } + + private void OnCollectionChanged(CollectionChangeEventArgs ccea, bool changeIsInsertion, Point newCurrentCell) + { +#if DEBUG + Debug.Assert(VerifyColumnDisplayIndexes()); +#endif + OnCollectionChanged_PreNotification(ccea); + OnCollectionChanged(ccea); + OnCollectionChanged_PostNotification(ccea, changeIsInsertion, newCurrentCell); + } + + private void OnCollectionChanged_PreNotification(CollectionChangeEventArgs ccea) + { + Debug.Assert(this.DataGridView != null); + this.DataGridView.OnColumnCollectionChanged_PreNotification(ccea); + } + + private void OnCollectionChanged_PostNotification(CollectionChangeEventArgs ccea, bool changeIsInsertion, Point newCurrentCell) + { + Debug.Assert(this.DataGridView != null); + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)ccea.Element; + if (ccea.Action == CollectionChangeAction.Add && changeIsInsertion) + { + this.DataGridView.OnInsertedColumn_PostNotification(newCurrentCell); + } + else if (ccea.Action == CollectionChangeAction.Remove) + { + this.DataGridView.OnRemovedColumn_PostNotification(dataGridViewColumn, newCurrentCell); + } + + this.DataGridView.OnColumnCollectionChanged_PostNotification(dataGridViewColumn); + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // We don't want to use DataGridViewElement here. + ] + public virtual void Remove(DataGridViewColumn dataGridViewColumn) + { + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + + if (dataGridViewColumn.DataGridView != this.DataGridView) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView), "dataGridViewColumn"); + } + + int itemsCount = this.items.Count; + for (int i = 0; i < itemsCount; ++i) + { + if (this.items[i] == dataGridViewColumn) + { + RemoveAt(i); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + return; + } + } + + Debug.Fail("Column should have been found in DataGridViewColumnCollection.Remove"); + } + + /// + public virtual void Remove(string columnName) + { + if (columnName == null) + { + throw new ArgumentNullException("columnName"); + } + + int itemsCount = this.items.Count; + for (int i = 0; i < itemsCount; ++i) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this.items[i]; + // NOTE: case-insensitive + if (0 == String.Compare(dataGridViewColumn.Name, columnName, true, CultureInfo.InvariantCulture)) + { + RemoveAt(i); + return; + } + } + + throw new ArgumentException(SR.GetString(SR.DataGridViewColumnCollection_ColumnNotFound, columnName), "columnName"); + } + + /// + public virtual void RemoveAt(int index) + { + if (index < 0 || index >= this.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.InDisplayIndexAdjustments) + { + // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + RemoveAtInternal(index, false /*force*/); +#if DEBUG + Debug.Assert(this.itemsSorted == null || VerifyColumnOrderCache()); +#endif + } + + internal void RemoveAtInternal(int index, bool force) + { + // If force is true, the underlying data is gone and can't be accessed anymore. + + Debug.Assert(index >= 0 && index < this.Count); + Debug.Assert(this.DataGridView != null); + Debug.Assert(!this.DataGridView.NoDimensionChangeAllowed); + Debug.Assert(!this.DataGridView.InDisplayIndexAdjustments); + + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this.items[index]; + Point newCurrentCell; + this.DataGridView.OnRemovingColumn(dataGridViewColumn, out newCurrentCell, force); + InvalidateCachedColumnsOrder(); + this.items.RemoveAt(index); + dataGridViewColumn.DataGridViewInternal = null; + UpdateColumnCaches(dataGridViewColumn, false); + this.DataGridView.OnRemovedColumn_PreNotification(dataGridViewColumn); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataGridViewColumn), false /*changeIsInsertion*/, newCurrentCell); + } + + private void UpdateColumnCaches(DataGridViewColumn dataGridViewColumn, bool adding) + { + if (this.columnCountsVisible != -1 || this.columnCountsVisibleSelected != -1 || + this.columnsWidthVisible != -1 || this.columnsWidthVisibleFrozen != -1) + { + DataGridViewElementStates columnStates = dataGridViewColumn.State; + if ((columnStates & DataGridViewElementStates.Visible) != 0) + { + int columnCountIncrement = adding ? 1 : -1; + int columnWidthIncrement = 0; + if (this.columnsWidthVisible != -1 || + (this.columnsWidthVisibleFrozen != -1 && + ((columnStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)))) + { + columnWidthIncrement = adding ? dataGridViewColumn.Width : -dataGridViewColumn.Width; + } + + if (this.columnCountsVisible != -1) + { + this.columnCountsVisible += columnCountIncrement; + } + if (this.columnsWidthVisible != -1) + { + Debug.Assert(columnWidthIncrement != 0); + this.columnsWidthVisible += columnWidthIncrement; + } + + if ((columnStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) + { + if (this.columnsWidthVisibleFrozen != -1) + { + Debug.Assert(columnWidthIncrement != 0); + this.columnsWidthVisibleFrozen += columnWidthIncrement; + } + } + + if ((columnStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Selected)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Selected)) + { + if (this.columnCountsVisibleSelected != -1) + { + this.columnCountsVisibleSelected += columnCountIncrement; + } + } + } + } + } + + private void UpdateColumnOrderCache() + { + this.itemsSorted = (ArrayList) this.items.Clone(); + this.itemsSorted.Sort(columnOrderComparer); + this.lastAccessedSortedIndex = -1; + } + +#if DEBUG + internal bool VerifyColumnDisplayIndexes() + { + for (int columnDisplayIndex = 0; columnDisplayIndex < this.items.Count; columnDisplayIndex++) + { + if (GetColumnAtDisplayIndex(columnDisplayIndex) == null) + { + return false; + } + } + return true; + } + + private bool VerifyColumnOrderCache() + { + if (this.itemsSorted == null) return false; + if (this.itemsSorted.Count != this.items.Count) return false; + + int index = 0; + while (index < this.itemsSorted.Count-1) + { + if (((DataGridViewColumn) this.itemsSorted[index+1]).DisplayIndex != + ((DataGridViewColumn) this.itemsSorted[index]).DisplayIndex+1) return false; + index++; + } + return true; + } +#endif + + private class ColumnOrderComparer : IComparer + { + public ColumnOrderComparer() + { + } + + public Int32 Compare(Object x, Object y) + { + Debug.Assert(x != null); + Debug.Assert(y != null); + + DataGridViewColumn dataGridViewColumn1 = x as DataGridViewColumn; + DataGridViewColumn dataGridViewColumn2 = y as DataGridViewColumn; + + Debug.Assert(dataGridViewColumn1 != null); + Debug.Assert(dataGridViewColumn2 != null); + + return dataGridViewColumn1.DisplayIndex - dataGridViewColumn2.DisplayIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnConverter.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnConverter.cs new file mode 100644 index 000000000..6ad9640ad --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnConverter.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + internal class DataGridViewColumnConverter : ExpandableObjectConverter { + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + DataGridViewColumn dataGridViewColumn = value as DataGridViewColumn; + if (destinationType == typeof(InstanceDescriptor) && dataGridViewColumn != null) { + ConstructorInfo ctor; + + // public DataGridViewColumn(Type cellType) + // + if (dataGridViewColumn.CellType != null) { + ctor = dataGridViewColumn.GetType().GetConstructor(new Type[] { typeof(Type) }); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { dataGridViewColumn.CellType }, false); + } + } + + // public DataGridViewColumn() + // + ctor = dataGridViewColumn.GetType().GetConstructor(new Type[0]); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[0], false); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnDesignTimeVisibleAttribute.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnDesignTimeVisibleAttribute.cs new file mode 100644 index 000000000..b903501fd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnDesignTimeVisibleAttribute.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { +using System.Diagnostics.CodeAnalysis; + + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class DataGridViewColumnDesignTimeVisibleAttribute: Attribute { + private bool visible; + + /// + public DataGridViewColumnDesignTimeVisibleAttribute (bool visible) { + this.visible = visible; + } + + /// + public DataGridViewColumnDesignTimeVisibleAttribute () { + } + + /// + public bool Visible { + get { + return visible; + } + } + + /// + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // DataGridViewColumnDesignTimeVisibleAttribute + // actually immutable. + ] + public static readonly DataGridViewColumnDesignTimeVisibleAttribute Yes = new DataGridViewColumnDesignTimeVisibleAttribute(true); + + /// + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // DataGridViewColumnDesignTimeVisibleAttribute + // actually immutable. + ] + public static readonly DataGridViewColumnDesignTimeVisibleAttribute No = new DataGridViewColumnDesignTimeVisibleAttribute(false); + + /// + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // DataGridViewColumnDesignTimeVisibleAttribute + // actually immutable. + ] + public static readonly DataGridViewColumnDesignTimeVisibleAttribute Default = Yes; + + /// + public override bool Equals(object obj) { + if (obj == this) { + return true; + } + + DataGridViewColumnDesignTimeVisibleAttribute other = obj as DataGridViewColumnDesignTimeVisibleAttribute; + return other != null && other.Visible == visible; + } + + /// + public override int GetHashCode() { + return typeof(DataGridViewColumnDesignTimeVisibleAttribute).GetHashCode() ^ (visible ? -1 : 0); + } + + /// + public override bool IsDefaultAttribute() { + return (this.Visible == Default.Visible); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnDividerDoubleClickEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnDividerDoubleClickEventArgs.cs new file mode 100644 index 000000000..c06368622 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnDividerDoubleClickEventArgs.cs @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//--------------------------------------------------------------------------------------- + +namespace System.Windows.Forms +{ + /// + public class DataGridViewColumnDividerDoubleClickEventArgs : HandledMouseEventArgs + { + private int columnIndex; + + /// + public DataGridViewColumnDividerDoubleClickEventArgs(int columnIndex, HandledMouseEventArgs e) : base(e.Button, e.Clicks, e.X, e.Y, e.Delta, e.Handled) + { + if (columnIndex < -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + this.columnIndex = columnIndex; + } + + /// + public int ColumnIndex + { + get + { + return this.columnIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnEventArgs.cs new file mode 100644 index 000000000..3d01ba1db --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnEventArgs.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewColumnEventArgs : EventArgs + { + private DataGridViewColumn dataGridViewColumn; + + /// + public DataGridViewColumnEventArgs(DataGridViewColumn dataGridViewColumn) + { + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + Debug.Assert(dataGridViewColumn.Index >= -1); + this.dataGridViewColumn = dataGridViewColumn; + } + + /// + public DataGridViewColumn Column + { + get + { + return this.dataGridViewColumn; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnHeaderCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnHeaderCell.cs new file mode 100644 index 000000000..ac473985e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnHeaderCell.cs @@ -0,0 +1,1618 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.IO; + using System.Text; + using System.Diagnostics; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms.VisualStyles; + using System.Security.Permissions; + using System.Windows.Forms.Internal; + using System.Globalization; + + /// + public class DataGridViewColumnHeaderCell : DataGridViewHeaderCell + { + private static readonly VisualStyleElement HeaderElement = VisualStyleElement.Header.Item.Normal; + + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphSeparatorWidth = 2; // additional 2 pixels between caption and glyph + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHorizontalMargin = 4; // 4 pixels on left & right of glyph + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphWidth = 9; // glyph is 9 pixels wide by default + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHeight = 7; // glyph is 7 pixels high by default (includes 1 blank line on top and 1 at the bottom) + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft = 2; + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight = 2; + private const byte DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin = 1; // 1 pixel on top & bottom of glyph and text + + private static bool isScalingInitialized = false; + private static byte sortGlyphSeparatorWidth = DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphSeparatorWidth; + private static byte sortGlyphHorizontalMargin = DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHorizontalMargin; + private static byte sortGlyphWidth = DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphWidth; + private static byte sortGlyphHeight = DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHeight; + + private static Type cellType = typeof(DataGridViewColumnHeaderCell); + + private SortOrder sortGlyphDirection; + + /// + public DataGridViewColumnHeaderCell() + { + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + sortGlyphSeparatorWidth = (byte)DpiHelper.LogicalToDeviceUnitsX(DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphSeparatorWidth); + sortGlyphHorizontalMargin = (byte)DpiHelper.LogicalToDeviceUnitsX(DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHorizontalMargin); + sortGlyphWidth = (byte)DpiHelper.LogicalToDeviceUnitsX(DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphWidth); + // make sure that the width of the base of the arrow is odd, otherwise the tip of the arrow is one pixel off to the side + if ((sortGlyphWidth % 2) == 0) { + sortGlyphWidth++; + } + sortGlyphHeight = (byte)DpiHelper.LogicalToDeviceUnitsY(DATAGRIDVIEWCOLUMNHEADERCELL_sortGlyphHeight); + } + isScalingInitialized = true; + } + + this.sortGlyphDirection = SortOrder.None; + } + + internal bool ContainsLocalValue + { + get + { + return this.Properties.ContainsObject(PropCellValue); + } + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public SortOrder SortGlyphDirection + { + get + { + return this.sortGlyphDirection; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SortOrder.None, (int)SortOrder.Descending)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SortOrder)); + } + if (this.OwningColumn == null || this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellDoesNotYetBelongToDataGridView)); + } + if (value != this.sortGlyphDirection) + { + if (this.OwningColumn.SortMode == DataGridViewColumnSortMode.NotSortable && value != SortOrder.None) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumnHeaderCell_SortModeAndSortGlyphDirectionClash, (value).ToString())); + } + this.sortGlyphDirection = value; + this.DataGridView.OnSortGlyphDirectionChanged(this); + } + } + } + + internal SortOrder SortGlyphDirectionInternal + { + set + { + Debug.Assert(value >= SortOrder.None && value <= SortOrder.Descending); + this.sortGlyphDirection = value; + } + } + + /// + public override object Clone() + { + DataGridViewColumnHeaderCell dataGridViewCell; + Type thisType = this.GetType(); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewColumnHeaderCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewColumnHeaderCell) System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.Value = this.Value; + return dataGridViewCell; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewColumnHeaderCellAccessibleObject(this); + } + + /// + protected override object GetClipboardContent(int rowIndex, + bool firstCell, + bool lastCell, + bool inFirstRow, + bool inLastRow, + string format) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null) + { + return null; + } + + // Not using formatted values for header cells. + object val = GetValue(rowIndex); + StringBuilder sb = new StringBuilder(64); + + Debug.Assert(inFirstRow); + + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + if (firstCell) + { + sb.Append(""); + sb.Append(""); + } + sb.Append(""); + if (lastCell) + { + sb.Append(""); + if (inLastRow) + { + sb.Append("
"); + if (val != null) + { + FormatPlainTextAsHtml(val.ToString(), new StringWriter(sb, CultureInfo.CurrentCulture)); + } + else + { + sb.Append(" "); + } + sb.Append("
"); + } + } + return sb.ToString(); + } + else + { + bool csv = String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase); + if (csv || + String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) || + String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase)) + { + if (val != null) + { + bool escapeApplied = false; + int insertionPoint = sb.Length; + FormatPlainText(val.ToString(), csv, new StringWriter(sb, CultureInfo.CurrentCulture), ref escapeApplied); + if (escapeApplied) + { + Debug.Assert(csv); + sb.Insert(insertionPoint, '"'); + } + } + if (lastCell) + { + if (!inLastRow) + { + sb.Append((char) Keys.Return); + sb.Append((char) Keys.LineFeed); + } + } + else + { + sb.Append(csv ? ',' : (char) Keys.Tab); + } + return sb.ToString(); + } + else + { + return null; + } + } + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + // the content bounds are computed on demand + // we mimic a lot of the painting code + + // get the borders + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle contentBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*paint*/); + +#if DEBUG + Rectangle contentBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*paint*/); + Debug.Assert(contentBoundsDebug.Equals(contentBounds)); +#endif + + return contentBounds; + } + + /// + public override ContextMenuStrip GetInheritedContextMenuStrip(int rowIndex) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + ContextMenuStrip contextMenuStrip = GetContextMenuStrip(-1); + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + + if (this.DataGridView != null) + { + return this.DataGridView.ContextMenuStrip; + } + else + { + return null; + } + } + + /// + public override DataGridViewCellStyle GetInheritedStyle(DataGridViewCellStyle inheritedCellStyle, int rowIndex, bool includeColors) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + Debug.Assert(this.DataGridView != null); + + DataGridViewCellStyle inheritedCellStyleTmp = (inheritedCellStyle == null) ? new DataGridViewCellStyle() : inheritedCellStyle; + + DataGridViewCellStyle cellStyle = null; + if (this.HasStyle) + { + cellStyle = this.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle columnHeadersStyle = this.DataGridView.ColumnHeadersDefaultCellStyle; + Debug.Assert(columnHeadersStyle != null); + + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (includeColors) + { + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = cellStyle.BackColor; + } + else if (!columnHeadersStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = columnHeadersStyle.BackColor; + } + else + { + inheritedCellStyleTmp.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = cellStyle.ForeColor; + } + else if (!columnHeadersStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = columnHeadersStyle.ForeColor; + } + else + { + inheritedCellStyleTmp.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (!columnHeadersStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = columnHeadersStyle.SelectionBackColor; + } + else + { + inheritedCellStyleTmp.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (!columnHeadersStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = columnHeadersStyle.SelectionForeColor; + } + else + { + inheritedCellStyleTmp.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyleTmp.Font = cellStyle.Font; + } + else if (columnHeadersStyle.Font != null) + { + inheritedCellStyleTmp.Font = columnHeadersStyle.Font; + } + else + { + inheritedCellStyleTmp.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = cellStyle.NullValue; + } + else if (!columnHeadersStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = columnHeadersStyle.NullValue; + } + else + { + inheritedCellStyleTmp.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (!columnHeadersStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = columnHeadersStyle.DataSourceNullValue; + } + else + { + inheritedCellStyleTmp.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = cellStyle.Format; + } + else if (columnHeadersStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = columnHeadersStyle.Format; + } + else + { + inheritedCellStyleTmp.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = cellStyle.FormatProvider; + } + else if (!columnHeadersStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = columnHeadersStyle.FormatProvider; + } + else + { + inheritedCellStyleTmp.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = cellStyle.Alignment; + } + else if (columnHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = columnHeadersStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyleTmp.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = cellStyle.WrapMode; + } + else if (columnHeadersStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = columnHeadersStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyleTmp.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = cellStyle.Tag; + } + else if (columnHeadersStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = columnHeadersStyle.Tag; + } + else + { + inheritedCellStyleTmp.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = cellStyle.Padding; + } + else if (columnHeadersStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = columnHeadersStyle.Padding; + } + else + { + inheritedCellStyleTmp.PaddingInternal = dataGridViewStyle.Padding; + } + + return inheritedCellStyleTmp; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + DataGridViewAdvancedBorderStyle dgvabsPlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + dgvabsEffective = this.DataGridView.AdjustColumnHeaderBorderStyle(this.DataGridView.AdvancedColumnHeadersBorderStyle, + dgvabsPlaceholder, + false /*isFirstDisplayedColumn*/, + false /*isLastVisibleColumn*/); + Rectangle borderWidthsRect = BorderWidths(dgvabsEffective); + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + + Size preferredSize; + // approximate preferred sizes + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + string valStr = GetValue(rowIndex) as string; + + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(0, 0); + if (!string.IsNullOrEmpty(valStr)) + { + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + preferredSize = new Size(DataGridViewCell.MeasureTextWidth(graphics, + valStr, + cellStyle.Font, + Math.Max(1, constraintSize.Height - borderAndPaddingHeights - 2 * DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin), + flags), + 0); + } + else + { + preferredSize = new Size(DataGridViewCell.MeasureTextSize(graphics, + valStr, + cellStyle.Font, + flags).Width, + 0); + } + } + if (constraintSize.Height - borderAndPaddingHeights - 2 * DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin > sortGlyphHeight && + this.OwningColumn != null && + this.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable) + { + preferredSize.Width += sortGlyphWidth + + 2 * sortGlyphHorizontalMargin; + if (!string.IsNullOrEmpty(valStr)) + { + preferredSize.Width += sortGlyphSeparatorWidth; + } + } + preferredSize.Width = Math.Max(preferredSize.Width, 1); + break; + } + case DataGridViewFreeDimension.Height: + { + int allowedWidth = constraintSize.Width - borderAndPaddingWidths; + Size glyphSize; + preferredSize = new Size(0, 0); + + if (allowedWidth >= sortGlyphWidth + 2 * sortGlyphHorizontalMargin && + this.OwningColumn != null && + this.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable) + { + glyphSize = new Size(sortGlyphWidth + 2 * sortGlyphHorizontalMargin, + sortGlyphHeight); + } + else + { + glyphSize = Size.Empty; + } + + if (allowedWidth - DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight > 0 && + !string.IsNullOrEmpty(valStr)) + { + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + if (glyphSize.Width > 0 && + allowedWidth - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight - + sortGlyphSeparatorWidth - + glyphSize.Width > 0) + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextHeight(graphics, + valStr, + cellStyle.Font, + allowedWidth - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight - + sortGlyphSeparatorWidth - + glyphSize.Width, + flags)); + } + else + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextHeight(graphics, + valStr, + cellStyle.Font, + allowedWidth - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight, + flags)); + } + } + else + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextSize(graphics, + valStr, + cellStyle.Font, + flags).Height); + } + } + preferredSize.Height = Math.Max(preferredSize.Height, glyphSize.Height); + preferredSize.Height = Math.Max(preferredSize.Height, 1); + break; + } + default: + { + if (!string.IsNullOrEmpty(valStr)) + { + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + preferredSize = DataGridViewCell.MeasureTextPreferredSize(graphics, valStr, cellStyle.Font, 5.0F, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, valStr, cellStyle.Font, flags); + } + } + else + { + preferredSize = new Size(0, 0); + } + if (this.OwningColumn != null && + this.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable) + { + preferredSize.Width += sortGlyphWidth + + 2 * sortGlyphHorizontalMargin; + if (!string.IsNullOrEmpty(valStr)) + { + preferredSize.Width += sortGlyphSeparatorWidth; + } + preferredSize.Height = Math.Max(preferredSize.Height, sortGlyphHeight); + } + preferredSize.Width = Math.Max(preferredSize.Width, 1); + preferredSize.Height = Math.Max(preferredSize.Height, 1); + break; + } + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + if (!string.IsNullOrEmpty(valStr)) + { + preferredSize.Width += DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight; + } + preferredSize.Width += borderAndPaddingWidths; + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height += 2 * DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin + borderAndPaddingHeights; + } + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + Rectangle rectThemeMargins = DataGridViewHeaderCell.GetThemeMargins(graphics); + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += rectThemeMargins.X + rectThemeMargins.Width; + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height += rectThemeMargins.Y + rectThemeMargins.Height; + } + } + return preferredSize; + } + + /// + protected override object GetValue(int rowIndex) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.ContainsLocalValue) + { + return this.Properties.GetObject(PropCellValue); + } + else + { + if (this.OwningColumn != null) + { + return this.OwningColumn.Name; + } + else + { + return null; + } + } + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates dataGridViewElementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + dataGridViewElementState, + formattedValue, + cellStyle, + advancedBorderStyle, + paintParts, + true /*paint*/); + } + + + // PaintPrivate is used in two places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // PaintPrivate returns the content bounds; + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates dataGridViewElementState, + object formattedValue, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool paint) + { + Debug.Assert(cellStyle != null); + Rectangle contentBounds = Rectangle.Empty; + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + Rectangle backgroundBounds = valBounds; + + bool cellSelected = (dataGridViewElementState & DataGridViewElementStates.Selected) != 0; + SolidBrush br; + + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + if (cellStyle.Padding != Padding.Empty) + { + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + } + // XP Theming + if (paint && DataGridViewCell.PaintBackground(paintParts) && backgroundBounds.Width > 0 && backgroundBounds.Height > 0) + { + int state = (int) HeaderItemState.Normal; + + // Set the state to Pressed/Hot only if the column can be sorted or selected + // VSWhidbey 174824. + if (this.OwningColumn != null && this.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable || + this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.ButtonState == ButtonState.Pushed); + state = (int) HeaderItemState.Pressed; + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex) + { + state = (int) HeaderItemState.Hot; + } + else if (cellSelected) + { + state = (int)HeaderItemState.Pressed; + } + } + + if (IsHighlighted()) + { + state = (int)HeaderItemState.Pressed; + } + + // Microsoft: even though XP provides support for theming the sort glyph, + // we rely on our own implementation for painting the sort glyph + if (this.DataGridView.RightToLeftInternal) + { + // Flip the column header background + Bitmap bmFlipXPThemes = this.FlipXPThemesBitmap; + if (bmFlipXPThemes == null || + bmFlipXPThemes.Width < backgroundBounds.Width || bmFlipXPThemes.Width > 2 * backgroundBounds.Width || + bmFlipXPThemes.Height < backgroundBounds.Height || bmFlipXPThemes.Height > 2 * backgroundBounds.Height) + { + bmFlipXPThemes = this.FlipXPThemesBitmap = new Bitmap(backgroundBounds.Width, backgroundBounds.Height); + } + Graphics gFlip = Graphics.FromImage(bmFlipXPThemes); + DataGridViewColumnHeaderCellRenderer.DrawHeader(gFlip, new Rectangle(0, 0, backgroundBounds.Width, backgroundBounds.Height), state); + bmFlipXPThemes.RotateFlip(RotateFlipType.RotateNoneFlipX); + g.DrawImage(bmFlipXPThemes, backgroundBounds, new Rectangle(bmFlipXPThemes.Width - backgroundBounds.Width, 0, backgroundBounds.Width, backgroundBounds.Height), GraphicsUnit.Pixel); + } + else + { + DataGridViewColumnHeaderCellRenderer.DrawHeader(g, backgroundBounds, state); + } + } + // update the value bounds + Rectangle rectThemeMargins = DataGridViewHeaderCell.GetThemeMargins(g); + valBounds.Y += rectThemeMargins.Y; + valBounds.Height -= rectThemeMargins.Y + rectThemeMargins.Height; + if (this.DataGridView.RightToLeftInternal) + { + valBounds.X += rectThemeMargins.Width; + valBounds.Width -= rectThemeMargins.X + rectThemeMargins.Width; + } + else + { + valBounds.X += rectThemeMargins.X; + valBounds.Width -= rectThemeMargins.X + rectThemeMargins.Width; + } + } + else + { + if (paint && DataGridViewCell.PaintBackground(paintParts) && backgroundBounds.Width > 0 && backgroundBounds.Height > 0) + { + br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) || IsHighlighted() ? + cellStyle.SelectionBackColor : cellStyle.BackColor); + if (br.Color.A == 255) + { + g.FillRectangle(br, backgroundBounds); + } + } + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + } + + bool displaySortGlyph = false; + Point sortGlyphLocation = new Point(0, 0); + string formattedValueStr = formattedValue as string; + + // Font independent margins + valBounds.Y += DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin; + valBounds.Height -= 2 * DATAGRIDVIEWCOLUMNHEADERCELL_verticalMargin; + + if (valBounds.Width - DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight > 0 && + valBounds.Height > 0 && + !String.IsNullOrEmpty(formattedValueStr)) + { + valBounds.Offset(DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft, 0); + valBounds.Width -= DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight; + + Color textColor; + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + textColor = DataGridViewColumnHeaderCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + textColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + + if (this.OwningColumn != null && this.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable) + { + // Is there enough room to show the glyph? + int width = valBounds.Width - + sortGlyphSeparatorWidth - + sortGlyphWidth - + 2 * sortGlyphHorizontalMargin; + if (width > 0) + { + bool widthTruncated; + int preferredHeight = DataGridViewCell.GetPreferredTextHeight(g, this.DataGridView.RightToLeftInternal, formattedValueStr, cellStyle, width, out widthTruncated); + if (preferredHeight <= valBounds.Height && !widthTruncated) + { + displaySortGlyph = (this.SortGlyphDirection != SortOrder.None); + valBounds.Width -= sortGlyphSeparatorWidth + + sortGlyphWidth + + 2 * sortGlyphHorizontalMargin; + if (this.DataGridView.RightToLeftInternal) + { + valBounds.X += sortGlyphSeparatorWidth + + sortGlyphWidth + + 2 * sortGlyphHorizontalMargin; + sortGlyphLocation = new Point(valBounds.Left - + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginLeft - + sortGlyphSeparatorWidth - + sortGlyphHorizontalMargin - + sortGlyphWidth, + valBounds.Top + + (valBounds.Height-sortGlyphHeight)/2); + } + else + { + sortGlyphLocation = new Point(valBounds.Right + + DATAGRIDVIEWCOLUMNHEADERCELL_horizontalTextMarginRight + + sortGlyphSeparatorWidth + + sortGlyphHorizontalMargin, + valBounds.Top + + (valBounds.Height-sortGlyphHeight)/2); + } + } + } + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (paint) + { + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + TextRenderer.DrawText(g, + formattedValueStr, + cellStyle.Font, + valBounds, + textColor, + flags); + } + } + else + { + contentBounds = DataGridViewUtilities.GetTextBounds(valBounds, formattedValueStr, flags, cellStyle); + } + } + else + { + if (paint && this.SortGlyphDirection != SortOrder.None && + valBounds.Width >= sortGlyphWidth + 2 * sortGlyphHorizontalMargin && + valBounds.Height >= sortGlyphHeight) + { + displaySortGlyph = true; + sortGlyphLocation = new Point(valBounds.Left + (valBounds.Width-sortGlyphWidth)/2, + valBounds.Top + (valBounds.Height-sortGlyphHeight)/2); + } + } + + if (paint && displaySortGlyph && DataGridViewCell.PaintContentBackground(paintParts)) + { + Pen penControlDark = null, penControlLightLight = null; + GetContrastedPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + + if (this.SortGlyphDirection == SortOrder.Ascending) + { + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.Outset: + // Sunken look + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y + sortGlyphHeight-2, + sortGlyphLocation.X + sortGlyphWidth/2-1, + sortGlyphLocation.Y); + g.DrawLine(penControlDark, + sortGlyphLocation.X+1, + sortGlyphLocation.Y + sortGlyphHeight-2, + sortGlyphLocation.X + sortGlyphWidth/2-1, + sortGlyphLocation.Y); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + sortGlyphWidth/2, + sortGlyphLocation.Y, + sortGlyphLocation.X + sortGlyphWidth-2, + sortGlyphLocation.Y + sortGlyphHeight-2); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + sortGlyphWidth/2, + sortGlyphLocation.Y, + sortGlyphLocation.X + sortGlyphWidth-3, + sortGlyphLocation.Y + sortGlyphHeight-2); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y + sortGlyphHeight-1, + sortGlyphLocation.X + sortGlyphWidth-2, + sortGlyphLocation.Y + sortGlyphHeight-1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + // Raised look + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y + sortGlyphHeight - 2, + sortGlyphLocation.X + sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X+1, + sortGlyphLocation.Y + sortGlyphHeight - 2, + sortGlyphLocation.X + sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlDark, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + sortGlyphWidth - 2, + sortGlyphLocation.Y + sortGlyphHeight - 2); + g.DrawLine(penControlDark, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + sortGlyphWidth - 3, + sortGlyphLocation.Y + sortGlyphHeight - 2); + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y + sortGlyphHeight - 1, + sortGlyphLocation.X + sortGlyphWidth - 2, + sortGlyphLocation.Y + sortGlyphHeight - 1); + break; + + default: + // Flat look + for (int line = 0; line < sortGlyphWidth / 2; line++) + { + g.DrawLine(penControlDark, + sortGlyphLocation.X + line, + sortGlyphLocation.Y + sortGlyphHeight - line - 1, + sortGlyphLocation.X + sortGlyphWidth - line - 1, + sortGlyphLocation.Y + sortGlyphHeight - line - 1); + } + g.DrawLine(penControlDark, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y + sortGlyphHeight - sortGlyphWidth / 2 - 1, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y + sortGlyphHeight - sortGlyphWidth / 2); + break; + } + } + else + { + Debug.Assert(this.SortGlyphDirection == SortOrder.Descending); + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.Outset: + // Sunken look + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y+1, + sortGlyphLocation.X+sortGlyphWidth/2-1, + sortGlyphLocation.Y+sortGlyphHeight-1); + g.DrawLine(penControlDark, + sortGlyphLocation.X+1, + sortGlyphLocation.Y+1, + sortGlyphLocation.X+sortGlyphWidth/2-1, + sortGlyphLocation.Y+sortGlyphHeight-1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X+sortGlyphWidth/2, + sortGlyphLocation.Y+sortGlyphHeight-1, + sortGlyphLocation.X+sortGlyphWidth-2, + sortGlyphLocation.Y+1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X+sortGlyphWidth/2, + sortGlyphLocation.Y+sortGlyphHeight-1, + sortGlyphLocation.X+sortGlyphWidth-3, + sortGlyphLocation.Y+1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y, + sortGlyphLocation.X+sortGlyphWidth-2, + sortGlyphLocation.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + // Raised look + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y+1, + sortGlyphLocation.X+sortGlyphWidth/2-1, + sortGlyphLocation.Y+sortGlyphHeight-1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X+1, + sortGlyphLocation.Y+1, + sortGlyphLocation.X+sortGlyphWidth/2-1, + sortGlyphLocation.Y+sortGlyphHeight-1); + g.DrawLine(penControlDark, + sortGlyphLocation.X+sortGlyphWidth/2, + sortGlyphLocation.Y+sortGlyphHeight-1, + sortGlyphLocation.X+sortGlyphWidth-2, + sortGlyphLocation.Y+1); + g.DrawLine(penControlDark, + sortGlyphLocation.X+sortGlyphWidth/2, + sortGlyphLocation.Y+sortGlyphHeight-1, + sortGlyphLocation.X+sortGlyphWidth-3, + sortGlyphLocation.Y+1); + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y, + sortGlyphLocation.X+sortGlyphWidth-2, + sortGlyphLocation.Y); + break; + + default: + // Flat look + for (int line = 0; line < sortGlyphWidth / 2; line++) + { + g.DrawLine(penControlDark, + sortGlyphLocation.X + line, + sortGlyphLocation.Y + line + 2, + sortGlyphLocation.X + sortGlyphWidth - line - 1, + sortGlyphLocation.Y + line + 2); + } + g.DrawLine(penControlDark, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y + sortGlyphWidth / 2 + 1, + sortGlyphLocation.X + sortGlyphWidth / 2, + sortGlyphLocation.Y + sortGlyphWidth / 2 + 2); + break; + } + } + } + + return contentBounds; + } + + private bool IsHighlighted() + { + return this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect && + this.DataGridView.CurrentCell != null && this.DataGridView.CurrentCell.Selected && + this.DataGridView.CurrentCell.OwningColumn == this.OwningColumn && + AccessibilityImprovements.Level2; + } + + /// + protected override bool SetValue(int rowIndex, object value) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + object originalValue = GetValue(rowIndex); + this.Properties.SetObject(PropCellValue, value); + if (this.DataGridView != null && originalValue != value) + { + RaiseCellValueChanged(new DataGridViewCellEventArgs(this.ColumnIndex, -1)); + } + return true; + } + + /// + /// + /// + /// + public override string ToString() + { + return "DataGridViewColumnHeaderCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private class DataGridViewColumnHeaderCellRenderer + { + private static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewColumnHeaderCellRenderer() + { + } + + public static VisualStyleRenderer VisualStyleRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(HeaderElement); + } + return visualStyleRenderer; + } + } + + public static void DrawHeader(Graphics g, Rectangle bounds, int headerState) + { + Rectangle rectClip = Rectangle.Truncate(g.ClipBounds); + if ((int) HeaderItemState.Hot == headerState) + { + // Workaround for a bug in XP theming: no painting of corners around orange tab. + VisualStyleRenderer.SetParameters(HeaderElement); + Rectangle cornerClip = new Rectangle(bounds.Left, bounds.Bottom-2, 2, 2); + cornerClip.Intersect(rectClip); + VisualStyleRenderer.DrawBackground(g, bounds, cornerClip); + cornerClip = new Rectangle(bounds.Right-2, bounds.Bottom-2, 2, 2); + cornerClip.Intersect(rectClip); + VisualStyleRenderer.DrawBackground(g, bounds, cornerClip); + } + VisualStyleRenderer.SetParameters(HeaderElement.ClassName, HeaderElement.Part, headerState); + VisualStyleRenderer.DrawBackground(g, bounds, rectClip); + } + } + + /// + protected class DataGridViewColumnHeaderCellAccessibleObject : DataGridViewCellAccessibleObject + { + public DataGridViewColumnHeaderCellAccessibleObject(DataGridViewColumnHeaderCell owner) : base (owner) + { + } + + /// + public override Rectangle Bounds + { + get + { + return this.GetAccessibleObjectBounds(this.ParentPrivate); + } + } + + /// + public override string DefaultAction + { + get + { + if (this.Owner.OwningColumn != null) + { + if (this.Owner.OwningColumn.SortMode == DataGridViewColumnSortMode.Automatic) + { + return SR.GetString(SR.DataGridView_AccColumnHeaderCellDefaultAction); + } + else if (this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect|| + this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + return SR.GetString(SR.DataGridView_AccColumnHeaderCellSelectDefaultAction); + } + else + { + return String.Empty; + } + } + else + { + return String.Empty; + } + } + } + + /// + public override string Name + { + get + { + if (this.Owner.OwningColumn != null) + { + return this.Owner.OwningColumn.HeaderText; + } + else + { + return String.Empty; + } + } + } + + /// + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.ParentPrivate; + } + } + + private AccessibleObject ParentPrivate + { + get + { + // return the top header row accessible object + return this.Owner.DataGridView.AccessibilityObject.GetChild(0); + } + } + + /// + public override AccessibleRole Role + { + get + { + return AccessibleRole.ColumnHeader; + } + } + + /// + public override AccessibleStates State + { + get + { + AccessibleStates resultState = AccessibleStates.Selectable; + + // get the Offscreen state from the base method. + AccessibleStates state = base.State; + if ((state & AccessibleStates.Offscreen) == AccessibleStates.Offscreen) + { + resultState |= AccessibleStates.Offscreen; + } + + if (this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + if (this.Owner.OwningColumn != null && this.Owner.OwningColumn.Selected) + { + resultState |= AccessibleStates.Selected; + } + } + + return resultState; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.Name; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + DataGridViewColumnHeaderCell dataGridViewCell = (DataGridViewColumnHeaderCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridViewCell.OwningColumn != null) + { + if (dataGridViewCell.OwningColumn.SortMode == DataGridViewColumnSortMode.Automatic) + { + ListSortDirection listSortDirection = ListSortDirection.Ascending; + if (dataGridView.SortedColumn == dataGridViewCell.OwningColumn && dataGridView.SortOrder == SortOrder.Ascending) + { + listSortDirection = ListSortDirection.Descending; + } + dataGridView.Sort(dataGridViewCell.OwningColumn, listSortDirection); + } + else if (dataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + dataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + dataGridViewCell.OwningColumn.Selected = true; + } + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + if (this.Owner.OwningColumn == null) + { + return null; + } + + switch (navigationDirection) + { + case AccessibleNavigation.Right: + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + return NavigateForward(); + } + else + { + return NavigateBackward(); + } + case AccessibleNavigation.Next: + return NavigateForward(); + case AccessibleNavigation.Left: + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + return NavigateBackward(); + } + else + { + return NavigateForward(); + } + case AccessibleNavigation.Previous: + return NavigateBackward(); + case AccessibleNavigation.FirstChild: + // return the top left header cell accessible object + return this.Owner.DataGridView.AccessibilityObject.GetChild(0).GetChild(0); + case AccessibleNavigation.LastChild: + // return the last column header cell in the top row header accessible object + AccessibleObject topRowHeaderAccessibleObject = this.Owner.DataGridView.AccessibilityObject.GetChild(0); + return topRowHeaderAccessibleObject.GetChild(topRowHeaderAccessibleObject.GetChildCount() - 1); + default: + return null; + } + } + + private AccessibleObject NavigateBackward() + { + if (this.Owner.OwningColumn == this.Owner.DataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Visible)) + { + if (this.Owner.DataGridView.RowHeadersVisible) + { + // return the row header cell accessible object for the current row + return this.Parent.GetChild(0); + } + else + { + return null; + } + } + else + { + int previousVisibleColumnIndex = this.Owner.DataGridView.Columns.GetPreviousColumn(this.Owner.OwningColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + int actualDisplayIndex = this.Owner.DataGridView.Columns.ColumnIndexToActualDisplayIndex(previousVisibleColumnIndex, + DataGridViewElementStates.Visible); + if (this.Owner.DataGridView.RowHeadersVisible) + { + return this.Parent.GetChild(actualDisplayIndex + 1); + } + else + { + return this.Parent.GetChild(actualDisplayIndex); + } + } + } + + private AccessibleObject NavigateForward() + { + if (this.Owner.OwningColumn == this.Owner.DataGridView.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + return null; + } + else + { + int nextVisibleColumnIndex = this.Owner.DataGridView.Columns.GetNextColumn(this.Owner.OwningColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + int actualDisplayIndex = this.Owner.DataGridView.Columns.ColumnIndexToActualDisplayIndex(nextVisibleColumnIndex, + DataGridViewElementStates.Visible); + + if (this.Owner.DataGridView.RowHeadersVisible) + { + // + 1 because the top header row accessible object has the top left header cell accessible object at the beginning + return this.Parent.GetChild(actualDisplayIndex + 1); + } + else + { + return this.Parent.GetChild(actualDisplayIndex); + } + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) + { + if (this.Owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + DataGridViewColumnHeaderCell dataGridViewCell = (DataGridViewColumnHeaderCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView == null) + { + return; + } + if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) + { + dataGridView.FocusInternal(); + } + if (dataGridViewCell.OwningColumn != null && + (dataGridView.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + dataGridView.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + if ((flags & (AccessibleSelection.TakeSelection | AccessibleSelection.AddSelection)) != 0) + { + dataGridViewCell.OwningColumn.Selected = true; + } + else if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection) + { + dataGridViewCell.OwningColumn.Selected = false; + } + } + } + + #region IRawElementProviderFragment Implementation + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + if (this.Owner.OwningColumn == null) + { + return null; + } + + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + return NavigateForward(); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return NavigateBackward(); + default: + return null; + } + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override bool IsPatternSupported(int patternId) + { + return patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId) || + patternId.Equals(NativeMethods.UIA_InvokePatternId); + } + + internal override object GetPropertyValue(int propertyId) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_HeaderControlTypeId; + case NativeMethods.UIA_IsEnabledPropertyId: + return Owner.DataGridView.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return this.Help ?? string.Empty; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (this.State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (this.State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + } + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnSortMode.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnSortMode.cs new file mode 100644 index 000000000..9b2d8e1d4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnSortMode.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + public enum DataGridViewColumnSortMode + { + /// + NotSortable, + /// + Automatic, + /// + Programmatic + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewColumnStateChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewColumnStateChangedEventArgs.cs new file mode 100644 index 000000000..d6937c108 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewColumnStateChangedEventArgs.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + public class DataGridViewColumnStateChangedEventArgs : EventArgs + { + private DataGridViewColumn dataGridViewColumn; + private DataGridViewElementStates stateChanged; + + /// + public DataGridViewColumnStateChangedEventArgs(DataGridViewColumn dataGridViewColumn, DataGridViewElementStates stateChanged) + { + this.dataGridViewColumn = dataGridViewColumn; + this.stateChanged = stateChanged; + } + + /// + public DataGridViewColumn Column + { + get + { + return this.dataGridViewColumn; + } + } + + /// + public DataGridViewElementStates StateChanged + { + get + { + return this.stateChanged; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxCell.cs new file mode 100644 index 000000000..d8c42fe7f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxCell.cs @@ -0,0 +1,3276 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.ComponentModel; + using System.Collections; + using System.Globalization; + using System.Windows.Forms.VisualStyles; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + + /// + /// + /// + /// + public class DataGridViewComboBoxCell : DataGridViewCell + { + private static readonly int PropComboBoxCellDataSource = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDisplayMember = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellValueMember = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellItems = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDropDownWidth = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellMaxDropDownItems = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellEditingComboBox = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellValueMemberProp = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDisplayMemberProp = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDataManager = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellColumnTemplate = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellFlatStyle = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDisplayStyle = PropertyStore.CreateKey(); + private static readonly int PropComboBoxCellDisplayStyleForCurrentCellOnly = PropertyStore.CreateKey(); + + private const byte DATAGRIDVIEWCOMBOBOXCELL_margin = 3; + private const byte DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleHeight = 4; + private const byte DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleWidth = 7; + private const byte DATAGRIDVIEWCOMBOBOXCELL_horizontalTextMarginLeft = 0; + private const byte DATAGRIDVIEWCOMBOBOXCELL_verticalTextMarginTopWithWrapping = 0; + private const byte DATAGRIDVIEWCOMBOBOXCELL_verticalTextMarginTopWithoutWrapping = 1; + + private const byte DATAGRIDVIEWCOMBOBOXCELL_ignoreNextMouseClick = 0x01; + private const byte DATAGRIDVIEWCOMBOBOXCELL_sorted = 0x02; + private const byte DATAGRIDVIEWCOMBOBOXCELL_createItemsFromDataSource = 0x04; + private const byte DATAGRIDVIEWCOMBOBOXCELL_autoComplete = 0x08; + private const byte DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp = 0x10; + private const byte DATAGRIDVIEWCOMBOBOXCELL_dropDownHookedUp = 0x20; + + internal const int DATAGRIDVIEWCOMBOBOXCELL_defaultMaxDropDownItems = 8; + + private static Type defaultFormattedValueType = typeof(System.String); + private static Type defaultEditType = typeof(System.Windows.Forms.DataGridViewComboBoxEditingControl); + private static Type defaultValueType = typeof(System.Object); + private static Type cellType = typeof(DataGridViewComboBoxCell); + + private byte flags; // see DATAGRIDVIEWCOMBOBOXCELL_ consts above + private static bool mouseInDropDownButtonBounds = false; + private static int cachedDropDownWidth = -1; + + // Autosizing changed for VS Whidbey 489227 + // We need to make ItemFromComboBoxDataSource as fast as possible because ItemFromComboBoxDataSource is getting called a lot + // during AutoSize. To do that we keep a copy of the key and the value. + //private object keyUsedDuringAutoSize = null; + //private object valueUsedDuringAutoSize = null; + + private static bool isScalingInitialized = false; + private static int OFFSET_2PIXELS = 2; + private static int offset2X = OFFSET_2PIXELS; + private static int offset2Y = OFFSET_2PIXELS; + private static byte nonXPTriangleHeight = DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleHeight; + private static byte nonXPTriangleWidth = DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleWidth; + + /// + public DataGridViewComboBoxCell() + { + this.flags = DATAGRIDVIEWCOMBOBOXCELL_autoComplete; + if (!isScalingInitialized) + { + if (DpiHelper.IsScalingRequired) + { + offset2X = DpiHelper.LogicalToDeviceUnitsX(OFFSET_2PIXELS); + offset2Y = DpiHelper.LogicalToDeviceUnitsY(OFFSET_2PIXELS); + nonXPTriangleWidth = (byte)DpiHelper.LogicalToDeviceUnitsX(DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleWidth); + nonXPTriangleHeight = (byte)DpiHelper.LogicalToDeviceUnitsY(DATAGRIDVIEWCOMBOBOXCELL_nonXPTriangleHeight); + } + isScalingInitialized = true; + } + } + + /// + /// Creates a new AccessibleObject for this DataGridViewComboBoxCell instance. + /// The AccessibleObject instance returned by this method supports ControlType UIA property. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.7.2 or opt-in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this DataGridViewComboBoxCell instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level2) + { + return new DataGridViewComboBoxCellAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + /// + [DefaultValue(true)] + public virtual bool AutoComplete + { + get + { + return ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_autoComplete) != 0x00); + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + if (value != this.AutoComplete) + { + if (value) + { + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_autoComplete; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_autoComplete); + } + if (OwnsEditingComboBox(this.RowIndex)) + { + if (value) + { + this.EditingComboBox.AutoCompleteSource = AutoCompleteSource.ListItems; + this.EditingComboBox.AutoCompleteMode = AutoCompleteMode.Append; + } + else + { + this.EditingComboBox.AutoCompleteMode = AutoCompleteMode.None; + this.EditingComboBox.AutoCompleteSource = AutoCompleteSource.None; + } + } + } + } + } + + private CurrencyManager DataManager + { + get + { + return GetDataManager(this.DataGridView); + } + set + { + if (value != null || this.Properties.ContainsObject(PropComboBoxCellDataManager)) + { + this.Properties.SetObject(PropComboBoxCellDataManager, value); + } + } + } + + /// + public virtual object DataSource + { + get + { + return this.Properties.GetObject(PropComboBoxCellDataSource); + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + // Same check as for ListControl's DataSource + if (value != null && !(value is IList || value is IListSource)) + { + throw new ArgumentException(SR.GetString(SR.BadDataSourceForComplexBinding)); + } + if (this.DataSource != value) + { + // Invalidate the currency manager + this.DataManager = null; + + UnwireDataSource(); + + this.Properties.SetObject(PropComboBoxCellDataSource, value); + + WireDataSource(value); + + // Invalidate existing Items collection + this.CreateItemsFromDataSource = true; + cachedDropDownWidth = -1; + + try + { + InitializeDisplayMemberPropertyDescriptor(this.DisplayMember); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + Debug.Assert(this.DisplayMember != null && this.DisplayMember.Length > 0); + this.DisplayMemberInternal = null; + } + + try + { + InitializeValueMemberPropertyDescriptor(this.ValueMember); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + Debug.Assert(this.ValueMember != null && this.ValueMember.Length > 0); + this.ValueMemberInternal = null; + } + + if (value == null) + { + this.DisplayMemberInternal = null; + this.ValueMemberInternal = null; + } + + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.DataSource = value; + InitializeComboBoxText(); + } + else + { + OnCommonChange(); + } + } + } + } + + /// + [DefaultValue("")] + public virtual string DisplayMember + { + get + { + object displayMember = this.Properties.GetObject(PropComboBoxCellDisplayMember); + if (displayMember == null) + { + return String.Empty; + } + else + { + return (string)displayMember; + } + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + this.DisplayMemberInternal = value; + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.DisplayMember = value; + InitializeComboBoxText(); + } + else + { + OnCommonChange(); + } + } + } + + private string DisplayMemberInternal + { + set + { + InitializeDisplayMemberPropertyDescriptor(value); + if ((value != null && value.Length > 0) || this.Properties.ContainsObject(PropComboBoxCellDisplayMember)) + { + this.Properties.SetObject(PropComboBoxCellDisplayMember, value); + } + } + } + + private PropertyDescriptor DisplayMemberProperty + { + get + { + return (PropertyDescriptor)this.Properties.GetObject(PropComboBoxCellDisplayMemberProp); + } + set + { + if (value != null || this.Properties.ContainsObject(PropComboBoxCellDisplayMemberProp)) + { + this.Properties.SetObject(PropComboBoxCellDisplayMemberProp, value); + } + } + } + + /// + [DefaultValue(DataGridViewComboBoxDisplayStyle.DropDownButton)] + public DataGridViewComboBoxDisplayStyle DisplayStyle + { + get + { + bool found; + int displayStyle = this.Properties.GetInteger(PropComboBoxCellDisplayStyle, out found); + if (found) + { + return (DataGridViewComboBoxDisplayStyle)displayStyle; + } + return DataGridViewComboBoxDisplayStyle.DropDownButton; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewComboBoxDisplayStyle.ComboBox, (int)DataGridViewComboBoxDisplayStyle.Nothing)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewComboBoxDisplayStyle)); + } + if (value != this.DisplayStyle) + { + this.Properties.SetInteger(PropComboBoxCellDisplayStyle, (int)value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal DataGridViewComboBoxDisplayStyle DisplayStyleInternal + { + set + { + Debug.Assert(value >= DataGridViewComboBoxDisplayStyle.ComboBox && value <= DataGridViewComboBoxDisplayStyle.Nothing); + if (value != this.DisplayStyle) + { + this.Properties.SetInteger(PropComboBoxCellDisplayStyle, (int)value); + } + } + } + + /// + [DefaultValue(false)] + public bool DisplayStyleForCurrentCellOnly + { + get + { + bool found; + int displayStyleForCurrentCellOnly = this.Properties.GetInteger(PropComboBoxCellDisplayStyleForCurrentCellOnly, out found); + if (found) + { + return displayStyleForCurrentCellOnly == 0 ? false : true; + } + return false; + } + set + { + if (value != this.DisplayStyleForCurrentCellOnly) + { + this.Properties.SetInteger(PropComboBoxCellDisplayStyleForCurrentCellOnly, value ? 1 : 0); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal bool DisplayStyleForCurrentCellOnlyInternal + { + set + { + if (value != this.DisplayStyleForCurrentCellOnly) + { + this.Properties.SetInteger(PropComboBoxCellDisplayStyleForCurrentCellOnly, value ? 1 : 0); + } + } + } + + private Type DisplayType + { + get + { + if (this.DisplayMemberProperty != null) + { + return this.DisplayMemberProperty.PropertyType; + } + else if (this.ValueMemberProperty != null) + { + return this.ValueMemberProperty.PropertyType; + } + else + { + return defaultFormattedValueType; + } + } + } + + private TypeConverter DisplayTypeConverter + { + get + { + if (this.DataGridView != null) + { + return this.DataGridView.GetCachedTypeConverter(this.DisplayType); + } + else + { + return TypeDescriptor.GetConverter(this.DisplayType); + } + } + } + + /// + [DefaultValue(1)] + public virtual int DropDownWidth + { + get + { + bool found; + int dropDownWidth = this.Properties.GetInteger(PropComboBoxCellDropDownWidth, out found); + return found ? dropDownWidth : 1; + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + if (value < 1) + { + throw new ArgumentOutOfRangeException("DropDownWidth", value, SR.GetString(SR.DataGridViewComboBoxCell_DropDownWidthOutOfRange, (1).ToString(CultureInfo.CurrentCulture))); + } + this.Properties.SetInteger(PropComboBoxCellDropDownWidth, (int)value); + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.DropDownWidth = value; + } + } + } + + private DataGridViewComboBoxEditingControl EditingComboBox + { + get + { + return (DataGridViewComboBoxEditingControl)this.Properties.GetObject(PropComboBoxCellEditingComboBox); + } + set + { + if (value != null || this.Properties.ContainsObject(PropComboBoxCellEditingComboBox)) + { + this.Properties.SetObject(PropComboBoxCellEditingComboBox, value); + } + } + } + + /// + public override Type EditType + { + get + { + return defaultEditType; + } + } + + /// + [DefaultValue(FlatStyle.Standard)] + public FlatStyle FlatStyle + { + get + { + bool found; + int flatStyle = this.Properties.GetInteger(PropComboBoxCellFlatStyle, out found); + if (found) + { + return (FlatStyle)flatStyle; + } + return FlatStyle.Standard; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropComboBoxCellFlatStyle, (int)value); + OnCommonChange(); + } + } + } + + internal FlatStyle FlatStyleInternal + { + set + { + Debug.Assert(value >= FlatStyle.Flat && value <= FlatStyle.System); + if (value != this.FlatStyle) + { + this.Properties.SetInteger(PropComboBoxCellFlatStyle, (int)value); + } + } + } + + /// + public override Type FormattedValueType + { + get + { + return defaultFormattedValueType; + } + } + + internal bool HasItems + { + get + { + return this.Properties.ContainsObject(PropComboBoxCellItems) && this.Properties.GetObject(PropComboBoxCellItems) != null; + } + } + + /// + [Browsable(false)] + public virtual ObjectCollection Items + { + get + { + return GetItems(this.DataGridView); + } + } + + /// + [DefaultValue(DATAGRIDVIEWCOMBOBOXCELL_defaultMaxDropDownItems)] + public virtual int MaxDropDownItems + { + get + { + bool found; + int maxDropDownItems = this.Properties.GetInteger(PropComboBoxCellMaxDropDownItems, out found); + if (found) + { + return maxDropDownItems; + } + return DATAGRIDVIEWCOMBOBOXCELL_defaultMaxDropDownItems; + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + if (value < 1 || value > 100) + { + throw new ArgumentOutOfRangeException("MaxDropDownItems", value, SR.GetString(SR.DataGridViewComboBoxCell_MaxDropDownItemsOutOfRange, (1).ToString(CultureInfo.CurrentCulture), (100).ToString(CultureInfo.CurrentCulture))); + } + this.Properties.SetInteger(PropComboBoxCellMaxDropDownItems, (int)value); + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.MaxDropDownItems = value; + } + } + } + + private bool PaintXPThemes + { + get + { + bool paintFlat = this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup; + return !paintFlat && this.DataGridView.ApplyVisualStylesToInnerCells; + } + } + + private static bool PostXPThemesExist + { + get + { + return VisualStyleRenderer.IsElementDefined(VisualStyleElement.ComboBox.ReadOnlyButton.Normal); + } + } + + /// + [DefaultValue(false)] + public virtual bool Sorted + { + get + { + return ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_sorted) != 0x00); + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + if (value != this.Sorted) + { + if (value) + { + if (this.DataSource == null) + { + this.Items.SortInternal(); + } + else + { + throw new ArgumentException(SR.GetString(SR.ComboBoxSortWithDataSource)); + } + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_sorted; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_sorted); + } + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.Sorted = value; + } + } + } + } + + internal DataGridViewComboBoxColumn TemplateComboBoxColumn + { + get + { + return (DataGridViewComboBoxColumn) this.Properties.GetObject(PropComboBoxCellColumnTemplate); + } + set + { + this.Properties.SetObject(PropComboBoxCellColumnTemplate, value); + } + } + + /// + [DefaultValue("")] + public virtual string ValueMember + { + get + { + object valueMember = this.Properties.GetObject(PropComboBoxCellValueMember); + if (valueMember == null) + { + return String.Empty; + } + else + { + return (string)valueMember; + } + } + set + { + //CheckNoSharedCell(); VSWhidbey 515823 + this.ValueMemberInternal = value; + if (OwnsEditingComboBox(this.RowIndex)) + { + this.EditingComboBox.ValueMember = value; + InitializeComboBoxText(); + } + else + { + OnCommonChange(); + } + } + } + + private string ValueMemberInternal + { + set + { + InitializeValueMemberPropertyDescriptor(value); + if ((value != null && value.Length > 0) || this.Properties.ContainsObject(PropComboBoxCellValueMember)) + { + this.Properties.SetObject(PropComboBoxCellValueMember, value); + } + } + } + + private PropertyDescriptor ValueMemberProperty + { + get + { + return (PropertyDescriptor)this.Properties.GetObject(PropComboBoxCellValueMemberProp); + } + set + { + if (value != null || this.Properties.ContainsObject(PropComboBoxCellValueMemberProp)) + { + this.Properties.SetObject(PropComboBoxCellValueMemberProp, value); + } + } + } + + + /// + public override Type ValueType + { + get + { + if (this.ValueMemberProperty != null) + { + return this.ValueMemberProperty.PropertyType; + } + else if (this.DisplayMemberProperty != null) + { + return this.DisplayMemberProperty.PropertyType; + } + else + { + Type baseValueType = base.ValueType; + if (baseValueType != null) + { + return baseValueType; + } + return defaultValueType; + } + } + } + + // Called when the row that owns the editing control gets unshared. + internal override void CacheEditingControl() + { + this.EditingComboBox = this.DataGridView.EditingControl as DataGridViewComboBoxEditingControl; + } + + private void CheckDropDownList(int x, int y, int rowIndex) + { + Debug.Assert(this.EditingComboBox != null); + DataGridViewAdvancedBorderStyle dgvabsPlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle, + dgvabsPlaceholder, + false /*singleVerticalBorderAdded*/, + false /*singleHorizontalBorderAdded*/, + false /*isFirstDisplayedColumn*/, + false /*isFirstDisplayedRow*/); + DataGridViewCellStyle cellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + Rectangle borderAndPaddingWidths = BorderWidths(dgvabsEffective); + borderAndPaddingWidths.X += cellStyle.Padding.Left; + borderAndPaddingWidths.Y += cellStyle.Padding.Top; + borderAndPaddingWidths.Width += cellStyle.Padding.Right; + borderAndPaddingWidths.Height += cellStyle.Padding.Bottom; + Size size = GetSize(rowIndex); + Size adjustedSize = new Size(size.Width - borderAndPaddingWidths.X - borderAndPaddingWidths.Width, + size.Height - borderAndPaddingWidths.Y - borderAndPaddingWidths.Height); + + int dropHeight; + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + dropHeight = Math.Min(GetDropDownButtonHeight(g, cellStyle), adjustedSize.Height - 2); + } + + int dropWidth = Math.Min(SystemInformation.HorizontalScrollBarThumbWidth, adjustedSize.Width - 2 * DATAGRIDVIEWCOMBOBOXCELL_margin - 1); + + if (dropHeight > 0 && dropWidth > 0 && + y >= borderAndPaddingWidths.Y + 1 && + y <= borderAndPaddingWidths.Y + 1 + dropHeight) + { + if (this.DataGridView.RightToLeftInternal) + { + if (x >= borderAndPaddingWidths.X + 1 && + x <= borderAndPaddingWidths.X + dropWidth + 1) + { + this.EditingComboBox.DroppedDown = true; + } + } + else + { + if (x >= size.Width - borderAndPaddingWidths.Width - dropWidth - 1 && + x <= size.Width - borderAndPaddingWidths.Width - 1) + { + this.EditingComboBox.DroppedDown = true; + } + } + } + } + + private void CheckNoDataSource() + { + if (this.DataSource != null) + { + throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); + } + } + + //private void CheckNoSharedCell() + //{ + // if (this.DataGridView != null && this.RowIndex == -1) + // { + // throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + // } + //} + + private void ComboBox_DropDown(object sender, EventArgs e) + { + Debug.Assert(this.DataGridView != null); + Debug.Assert(this.EditingComboBox != null); + + ComboBox comboBox = this.EditingComboBox; + DataGridViewComboBoxColumn owningComboBoxColumn = this.OwningColumn as DataGridViewComboBoxColumn; + if (owningComboBoxColumn != null) + { + DataGridViewAutoSizeColumnMode autoSizeColumnMode = owningComboBoxColumn.GetInheritedAutoSizeMode(this.DataGridView); + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.ColumnHeader && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None) + { + if (this.DropDownWidth == 1) + { + // Owning combobox column is autosized based on inner cells. + // Resize the dropdown list based on the max width of the items. + if (cachedDropDownWidth == -1) + { + int maxPreferredWidth = -1; + if ((this.HasItems || this.CreateItemsFromDataSource) && this.Items.Count > 0) + { + foreach (object item in this.Items) + { + Size preferredSize = TextRenderer.MeasureText(comboBox.GetItemText(item), comboBox.Font); + if (preferredSize.Width > maxPreferredWidth) + { + maxPreferredWidth = preferredSize.Width; + } + } + } + cachedDropDownWidth = maxPreferredWidth + 2 + SystemInformation.VerticalScrollBarWidth; + } + Debug.Assert(cachedDropDownWidth >= 1); + UnsafeNativeMethods.SendMessage(new HandleRef(comboBox, comboBox.Handle), NativeMethods.CB_SETDROPPEDWIDTH, cachedDropDownWidth, 0); + } + } + else + { + // The dropdown width may have been previously adjusted to the items because of the owning column autosized. + // The dropdown width needs to be realigned to the DropDownWidth property value. + int dropDownWidth = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(comboBox, comboBox.Handle), NativeMethods.CB_GETDROPPEDWIDTH, 0, 0)); + if (dropDownWidth != this.DropDownWidth) + { + UnsafeNativeMethods.SendMessage(new HandleRef(comboBox, comboBox.Handle), NativeMethods.CB_SETDROPPEDWIDTH, this.DropDownWidth, 0); + } + } + } + } + + /// + public override object Clone() + { + DataGridViewComboBoxCell dataGridViewCell; + Type thisType = this.GetType(); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewComboBoxCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + dataGridViewCell = (DataGridViewComboBoxCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.DropDownWidth = this.DropDownWidth; + dataGridViewCell.MaxDropDownItems = this.MaxDropDownItems; + dataGridViewCell.CreateItemsFromDataSource = false; + dataGridViewCell.DataSource = this.DataSource; + dataGridViewCell.DisplayMember = this.DisplayMember; + dataGridViewCell.ValueMember = this.ValueMember; + if (this.HasItems && this.DataSource == null && this.Items.Count > 0) + { + dataGridViewCell.Items.AddRangeInternal(this.Items.InnerArray.ToArray()); + } + dataGridViewCell.AutoComplete = this.AutoComplete; + dataGridViewCell.Sorted = this.Sorted; + dataGridViewCell.FlatStyleInternal = this.FlatStyle; + dataGridViewCell.DisplayStyleInternal = this.DisplayStyle; + dataGridViewCell.DisplayStyleForCurrentCellOnlyInternal = this.DisplayStyleForCurrentCellOnly; + return dataGridViewCell; + } + + private bool CreateItemsFromDataSource + { + get + { + return ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_createItemsFromDataSource) != 0x00); + } + set + { + if (value) + { + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_createItemsFromDataSource; + } + else + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_createItemsFromDataSource); + } + } + } + + private void DataSource_Disposed(object sender, EventArgs e) + { + Debug.Assert(sender == this.DataSource, "How can we get dispose notification from anything other than our DataSource?"); + this.DataSource = null; + } + + private void DataSource_Initialized(object sender, EventArgs e) + { + Debug.Assert(sender == this.DataSource); + Debug.Assert(this.DataSource is ISupportInitializeNotification); + Debug.Assert((this.flags & DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp) != 0x00); + + ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification; + // Unhook the Initialized event. + if (dsInit != null) + { + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + } + + // The wait is over: DataSource is initialized. + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp); + + // Check the DisplayMember and ValueMember values - will throw if values don't match existing fields. + InitializeDisplayMemberPropertyDescriptor(this.DisplayMember); + InitializeValueMemberPropertyDescriptor(this.ValueMember); + } + + /// + public override void DetachEditingControl() + { + DataGridView dgv = this.DataGridView; + if (dgv == null || dgv.EditingControl == null) + { + throw new InvalidOperationException(); + } + + if (this.EditingComboBox != null && + (this.flags & DATAGRIDVIEWCOMBOBOXCELL_dropDownHookedUp) != 0x00) + { + this.EditingComboBox.DropDown -= new EventHandler(ComboBox_DropDown); + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_dropDownHookedUp); + } + + this.EditingComboBox = null; + base.DetachEditingControl(); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = GetEditedFormattedValue(value, rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle dropDownButtonRect; + Rectangle contentBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + null /*errorText*/, // contentBounds is independent of errorText + cellStyle, + dgvabsEffective, + out dropDownButtonRect, // not used + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*computeDropDownButtonRect*/, + false /*paint*/); + +#if DEBUG + Rectangle contentBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + out dropDownButtonRect, // not used + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*computeDropDownButtonRect*/, + false /*paint*/); + Debug.Assert(contentBoundsDebug.Equals(contentBounds)); +#endif + + return contentBounds; + } + + private CurrencyManager GetDataManager(DataGridView dataGridView) + { + CurrencyManager cm = (CurrencyManager)this.Properties.GetObject(PropComboBoxCellDataManager); + if (cm == null && this.DataSource != null && dataGridView != null && dataGridView.BindingContext != null && !(this.DataSource == Convert.DBNull)) + { + ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification; + if (dsInit != null && !dsInit.IsInitialized) + { + if ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp) == 0x00) + { + dsInit.Initialized += new EventHandler(DataSource_Initialized); + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp; + } + } + else + { + cm = (CurrencyManager)dataGridView.BindingContext[this.DataSource]; + this.DataManager = cm; + } + } + return cm; + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // Hard coded space is OK here. + ] + private int GetDropDownButtonHeight(Graphics graphics, DataGridViewCellStyle cellStyle) + { + int adjustment = 4; + if (this.PaintXPThemes) + { + if (PostXPThemesExist) + { + adjustment = 8; + } + else + { + adjustment = 6; + } + } + return DataGridViewCell.MeasureTextHeight(graphics, " ", cellStyle.Font, System.Int32.MaxValue, TextFormatFlags.Default) + adjustment; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = GetEditedFormattedValue(value, rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle dropDownButtonRect; + Rectangle errorIconBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + out dropDownButtonRect, // not used + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorBounds*/, + false /*computeDropDownButtonRect*/, + false /*paint*/); + +#if DEBUG + Rectangle errorIconBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + out dropDownButtonRect, // not used + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorBounds*/, + false /*computeDropDownButtonRect*/, + false /*paint*/); + Debug.Assert(errorIconBoundsDebug.Equals(errorIconBounds)); +#endif + + return errorIconBounds; + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily") // OK to cast value into String twice. + ] + protected override object GetFormattedValue(object value, + int rowIndex, + ref DataGridViewCellStyle cellStyle, + TypeConverter valueTypeConverter, + TypeConverter formattedValueTypeConverter, + DataGridViewDataErrorContexts context) + { + if (valueTypeConverter == null) + { + if (this.ValueMemberProperty != null) + { + valueTypeConverter = this.ValueMemberProperty.Converter; + } + else if (this.DisplayMemberProperty != null) + { + valueTypeConverter = this.DisplayMemberProperty.Converter; + } + } + + if (value == null || ((this.ValueType != null && !this.ValueType.IsAssignableFrom(value.GetType())) && value != System.DBNull.Value)) + { + // Do not raise the DataError event if the value is null and the row is the 'new row'. + // VS Whidbey bug 324054: In fact, unlike for other cell types, do not raise DataError event at all if value is null and therefore invalid. + // Using the DataGridViewComboBoxCell.DefaultNewRowValue property would be wrong. + if (value == null /* && ((this.DataGridView != null && rowIndex == this.DataGridView.NewRowIndex) || this.Items.Count == 0)*/) + { + // Debug.Assert(rowIndex != -1 || this.Items.Count == 0); + return base.GetFormattedValue(null, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context); + } + if (this.DataGridView != null) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs( + new FormatException(SR.GetString(SR.DataGridViewComboBoxCell_InvalidValue)), this.ColumnIndex, + rowIndex, context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context); + } + + String strValue = value as String; + if ((this.DataManager != null && (this.ValueMemberProperty != null || this.DisplayMemberProperty != null)) || + !string.IsNullOrEmpty(this.ValueMember) || !string.IsNullOrEmpty(this.DisplayMember)) + { + object displayValue; + if (!LookupDisplayValue(rowIndex, value, out displayValue)) + { + if (value == System.DBNull.Value) + { + displayValue = System.DBNull.Value; + } + else if (strValue != null && String.IsNullOrEmpty(strValue) && this.DisplayType == typeof(String)) + { + displayValue = String.Empty; + } + else if (this.DataGridView != null) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs( + new ArgumentException(SR.GetString(SR.DataGridViewComboBoxCell_InvalidValue)), this.ColumnIndex, + rowIndex, context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + + if (OwnsEditingComboBox(rowIndex)) + { + ((IDataGridViewEditingControl)this.EditingComboBox).EditingControlValueChanged = true; + this.DataGridView.NotifyCurrentCellDirty(true); + } + } + } + return base.GetFormattedValue(displayValue, rowIndex, ref cellStyle, this.DisplayTypeConverter, formattedValueTypeConverter, context); + } + else + { + if (!this.Items.Contains(value) && + value != System.DBNull.Value && + (!(value is String) || !String.IsNullOrEmpty(strValue))) + { + if (this.DataGridView != null) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs( + new ArgumentException(SR.GetString(SR.DataGridViewComboBoxCell_InvalidValue)), this.ColumnIndex, + rowIndex, context); + RaiseDataError(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + + if (this.Items.Count > 0) + { + value = this.Items[0]; + } + else + { + value = String.Empty; + } + } + return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context); + } + } + + internal string GetItemDisplayText(object item) + { + object displayValue = GetItemDisplayValue(item); + return (displayValue != null) ? Convert.ToString(displayValue, CultureInfo.CurrentCulture) : string.Empty; + } + + internal object GetItemDisplayValue(object item) + { + Debug.Assert(item != null); + bool displayValueSet = false; + object displayValue = null; + if (this.DisplayMemberProperty != null) + { + displayValue = this.DisplayMemberProperty.GetValue(item); + displayValueSet = true; + } + else if (this.ValueMemberProperty != null) + { + displayValue = this.ValueMemberProperty.GetValue(item); + displayValueSet = true; + } + else if (!string.IsNullOrEmpty(this.DisplayMember)) + { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.DisplayMember, true /*caseInsensitive*/); + if (propDesc != null) + { + displayValue = propDesc.GetValue(item); + displayValueSet = true; + } + } + else if (!string.IsNullOrEmpty(this.ValueMember)) + { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.ValueMember, true /*caseInsensitive*/); + if (propDesc != null) + { + displayValue = propDesc.GetValue(item); + displayValueSet = true; + } + } + if (!displayValueSet) + { + displayValue = item; + } + return displayValue; + } + + internal ObjectCollection GetItems(DataGridView dataGridView) + { + ObjectCollection items = (ObjectCollection)this.Properties.GetObject(PropComboBoxCellItems); + if (items == null) + { + items = new ObjectCollection(this); + this.Properties.SetObject(PropComboBoxCellItems, items); + } + if (this.CreateItemsFromDataSource) + { + items.ClearInternal(); + CurrencyManager dataManager = GetDataManager(dataGridView); + if (dataManager != null && dataManager.Count != -1) + { + object[] newItems = new object[dataManager.Count]; + for (int i = 0; i < newItems.Length; i++) + { + newItems[i] = dataManager[i]; + } + items.AddRangeInternal(newItems); + } + // Do not clear the CreateItemsFromDataSource flag when the data source has not been initialized yet + if (dataManager != null || (this.flags & DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp) == 0x00) + { + this.CreateItemsFromDataSource = false; + } + } + return items; + } + + internal object GetItemValue(object item) + { + bool valueSet = false; + object value = null; + if (this.ValueMemberProperty != null) + { + value = this.ValueMemberProperty.GetValue(item); + valueSet = true; + } + else if (this.DisplayMemberProperty != null) + { + value = this.DisplayMemberProperty.GetValue(item); + valueSet = true; + } + else if (!string.IsNullOrEmpty(this.ValueMember)) + { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.ValueMember, true /*caseInsensitive*/); + if (propDesc != null) + { + value = propDesc.GetValue(item); + valueSet = true; + } + } + if (!valueSet && !string.IsNullOrEmpty(this.DisplayMember)) + { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.DisplayMember, true /*caseInsensitive*/); + if (propDesc != null) + { + value = propDesc.GetValue(item); + valueSet = true; + } + } + if (!valueSet) + { + value = item; + } + return value; + } + + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // Hard coded space is OK here. + ] + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Size preferredSize = Size.Empty; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + + /* Changing design of DGVComboBoxCell.GetPreferredSize for performance reasons. See VS Whidbey 489227. + * Old design required looking through each combo item + string formattedValue; + if (freeDimension == DataGridViewFreeDimension.Height) + { + formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string; + if (formattedValue != null) + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextSize(graphics, formattedValue, cellStyle.Font, flags).Height); + } + else + { + preferredSize = new Size(DataGridViewCell.MeasureTextSize(graphics, " ", cellStyle.Font, flags).Height, + 0); + } + } + else + { + if ((this.HasItems || this.CreateItemsFromDataSource) && this.Items.Count > 0) + { + int maxPreferredWidth = -1; + try + { + foreach (object item in this.Items) + { + this.valueUsedDuringAutoSize = item; + this.keyUsedDuringAutoSize = GetItemValue(item); + formattedValue = GetFormattedValue(this.keyUsedDuringAutoSize, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string; + if (formattedValue != null) + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedValue, cellStyle.Font, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, " ", cellStyle.Font, flags); + } + if (preferredSize.Width > maxPreferredWidth) + { + maxPreferredWidth = preferredSize.Width; + } + } + } + finally + { + this.keyUsedDuringAutoSize = null; + this.valueUsedDuringAutoSize = null; + } + preferredSize.Width = maxPreferredWidth; + } + else + { + formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string; + if (formattedValue != null) + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedValue, cellStyle.Font, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, " ", cellStyle.Font, flags); + } + } + if (freeDimension == DataGridViewFreeDimension.Width) + { + preferredSize.Height = 0; + } + } + */ + + string formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string; + if (!string.IsNullOrEmpty(formattedValue)) + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedValue, cellStyle.Font, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, " ", cellStyle.Font, flags); + } + + if (freeDimension == DataGridViewFreeDimension.Height) + { + preferredSize.Width = 0; + } + else if (freeDimension == DataGridViewFreeDimension.Width) + { + preferredSize.Height = 0; + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += SystemInformation.HorizontalScrollBarThumbWidth + 1 + 2 * DATAGRIDVIEWCOMBOBOXCELL_margin + borderAndPaddingWidths; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Width = Math.Max(preferredSize.Width, borderAndPaddingWidths + SystemInformation.HorizontalScrollBarThumbWidth + 1 + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + if (this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup) + { + preferredSize.Height += 6; + } + else + { + preferredSize.Height += 8; + } + preferredSize.Height += borderAndPaddingHeights; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Height = Math.Max(preferredSize.Height, borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + private void InitializeComboBoxText() + { + Debug.Assert(this.EditingComboBox != null); + ((IDataGridViewEditingControl)this.EditingComboBox).EditingControlValueChanged = false; + int rowIndex = ((IDataGridViewEditingControl)this.EditingComboBox).EditingControlRowIndex; + Debug.Assert(rowIndex > -1); + DataGridViewCellStyle dataGridViewCellStyle = GetInheritedStyle(null, rowIndex, false); + this.EditingComboBox.Text = (string) GetFormattedValue(GetValue(rowIndex), rowIndex, ref dataGridViewCellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + } + + /// + public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) + { + Debug.Assert(this.DataGridView != null && + this.DataGridView.EditingPanel != null && + this.DataGridView.EditingControl != null); + Debug.Assert(!this.ReadOnly); + base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); + ComboBox comboBox = this.DataGridView.EditingControl as ComboBox; + if (comboBox != null) + { + // Use the selection backcolor for the editing panel when the cell is selected + if ((GetInheritedState(rowIndex) & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected) + { + this.DataGridView.EditingPanel.BackColor = dataGridViewCellStyle.SelectionBackColor; + } + + // We need the comboBox to be parented by a control which has a handle or else the native ComboBox ends up + // w/ its parentHwnd pointing to the WinFormsParkingWindow. + // See vsWhidbey 385025. + IntPtr h; + if (comboBox.ParentInternal != null) + { + h = comboBox.ParentInternal.Handle; + } + h = comboBox.Handle; // make sure that assigning the DataSource property does not assert. + comboBox.DropDownStyle = ComboBoxStyle.DropDownList; + comboBox.FormattingEnabled = true; + comboBox.MaxDropDownItems = this.MaxDropDownItems; + comboBox.DropDownWidth = this.DropDownWidth; + comboBox.DataSource = null; + comboBox.ValueMember = null; // VSWhidbey 424061 + comboBox.Items.Clear(); + + /* Don't set the position inside the currency manager blindly to 0 because it may be the case that + the DataGridView and the DataGridViewComboBoxCell share the same DataManager. + Then setting the position on the DataManager will also set the position on the DataGridView. + And this causes problems when changing position inside the DataGridView. See vsWhidbey 397071. + if (this.DataManager != null && this.DataManager.Count > 0) + { + this.DataManager.Position = 0; + } + */ + + comboBox.DataSource = this.DataSource; + comboBox.DisplayMember = this.DisplayMember; + comboBox.ValueMember = this.ValueMember; + if (this.HasItems && this.DataSource == null && this.Items.Count > 0) + { + comboBox.Items.AddRange(this.Items.InnerArray.ToArray()); + } + comboBox.Sorted = this.Sorted; + comboBox.FlatStyle = this.FlatStyle; + if (this.AutoComplete) + { + comboBox.AutoCompleteSource = AutoCompleteSource.ListItems; + comboBox.AutoCompleteMode = AutoCompleteMode.Append; + } + else + { + comboBox.AutoCompleteMode = AutoCompleteMode.None; + comboBox.AutoCompleteSource = AutoCompleteSource.None; + } + + string initialFormattedValueStr = initialFormattedValue as string; + if (initialFormattedValueStr == null) + { + initialFormattedValueStr = string.Empty; + } + comboBox.Text = initialFormattedValueStr; + + if ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_dropDownHookedUp) == 0x00) + { + comboBox.DropDown += new EventHandler(ComboBox_DropDown); + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_dropDownHookedUp; + } + cachedDropDownWidth = -1; + + this.EditingComboBox = this.DataGridView.EditingControl as DataGridViewComboBoxEditingControl; + if (GetHeight(rowIndex) > 21) + { + Rectangle rectBottomSection = this.DataGridView.GetCellDisplayRectangle(this.ColumnIndex, rowIndex, true); + rectBottomSection.Y += 21; + rectBottomSection.Height -= 21; + this.DataGridView.Invalidate(rectBottomSection); + } + } + } + + private void InitializeDisplayMemberPropertyDescriptor(string displayMember) + { + if (this.DataManager != null) + { + if (String.IsNullOrEmpty(displayMember)) + { + this.DisplayMemberProperty = null; + } + else + { + BindingMemberInfo displayBindingMember = new BindingMemberInfo(displayMember); + // make the DataManager point to the sublist inside this.DataSource + this.DataManager = this.DataGridView.BindingContext[this.DataSource, displayBindingMember.BindingPath] as CurrencyManager; + + PropertyDescriptorCollection props = this.DataManager.GetItemProperties(); + PropertyDescriptor displayMemberProperty = props.Find(displayBindingMember.BindingField, true); + if (displayMemberProperty == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewComboBoxCell_FieldNotFound, displayMember)); + } + else + { + this.DisplayMemberProperty = displayMemberProperty; + } + } + } + } + + private void InitializeValueMemberPropertyDescriptor(string valueMember) + { + if (this.DataManager != null) + { + if (String.IsNullOrEmpty(valueMember)) + { + this.ValueMemberProperty = null; + } + else + { + BindingMemberInfo valueBindingMember = new BindingMemberInfo(valueMember); + // make the DataManager point to the sublist inside this.DataSource + this.DataManager = this.DataGridView.BindingContext[this.DataSource, valueBindingMember.BindingPath] as CurrencyManager; + + PropertyDescriptorCollection props = this.DataManager.GetItemProperties(); + PropertyDescriptor valueMemberProperty = props.Find(valueBindingMember.BindingField, true); + if (valueMemberProperty == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewComboBoxCell_FieldNotFound, valueMember)); + } + else + { + this.ValueMemberProperty = valueMemberProperty; + } + } + } + } + + /// + /// Find the item in the ComboBox currency manager for the current cell + /// This can be horribly inefficient and it uses reflection which makes it expensive + /// - ripe for optimization + /// + private object ItemFromComboBoxDataSource(PropertyDescriptor property, object key) + { + if (key == null) + { + throw new ArgumentNullException("key"); + } + + // See VS Whidbey 489227 + //if (key == this.keyUsedDuringAutoSize) + //{ + // return this.valueUsedDuringAutoSize; + //} + + Debug.Assert(property != null); + Debug.Assert(this.DataManager != null); + object item = null; + + //If the data source is a bindinglist use that as it's probably more efficient + if ((this.DataManager.List is IBindingList) && ((IBindingList)this.DataManager.List).SupportsSearching) + { + int index = ((IBindingList)this.DataManager.List).Find(property, key); + if (index != -1) + { + item = this.DataManager.List[index]; + } + } + else + { + //Otherwise walk across the items looking for the item we want + for (int i = 0; i < this.DataManager.List.Count; i++) + { + object itemTmp = this.DataManager.List[i]; + object value = property.GetValue(itemTmp); + if (key.Equals(value)) + { + item = itemTmp; + break; + } + } + } + return item; + } + + private object ItemFromComboBoxItems(int rowIndex, string field, object key) + { + Debug.Assert(!string.IsNullOrEmpty(field)); + + object item = null; + if (OwnsEditingComboBox(rowIndex)) + { + // It is likely that the item looked for is the selected item. + item = this.EditingComboBox.SelectedItem; + object displayValue = null; + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(field, true /*caseInsensitive*/); + if (propDesc != null) + { + displayValue = propDesc.GetValue(item); + } + if (displayValue == null || !displayValue.Equals(key)) + { + // No, the selected item is not looked for. + item = null; // Need to loop through all the items + } + } + if (item == null) + { + foreach (object itemCandidate in this.Items) + { + object displayValue = null; + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(itemCandidate).Find(field, true /*caseInsensitive*/); + if (propDesc != null) + { + displayValue = propDesc.GetValue(itemCandidate); + } + if (displayValue != null && displayValue.Equals(key)) + { + // Found the item. + item = itemCandidate; + break; + } + } + } + if (item == null) + { + // The provided field could be wrong - try to match the key against an actual item + if (OwnsEditingComboBox(rowIndex)) + { + // It is likely that the item looked for is the selected item. + item = this.EditingComboBox.SelectedItem; + if (item == null || !item.Equals(key)) + { + item = null; + } + } + if (item == null && this.Items.Contains(key)) + { + item = key; + } + } + return item; + } + + /// + public override bool KeyEntersEditMode(KeyEventArgs e) + { + if (((char.IsLetterOrDigit((char)e.KeyCode) && !(e.KeyCode >= Keys.F1 && e.KeyCode <= Keys.F24)) || + (e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.Divide) || + (e.KeyCode >= Keys.OemSemicolon && e.KeyCode <= Keys.Oem102) || + (e.KeyCode == Keys.Space && !e.Shift) || + (e.KeyCode == Keys.F4) || + ((e.KeyCode == Keys.Down || e.KeyCode == Keys.Up) && e.Alt)) && + (!e.Alt || (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)) && + !e.Control) + { + return true; + } + return base.KeyEntersEditMode(e); + } + + /// + /// Lookup the display text for the given value. + /// + /// We use the value and ValueMember to look up the item in the + /// ComboBox datasource. We then use DisplayMember to get the + /// text to display. + /// + private bool LookupDisplayValue(int rowIndex, object value, out object displayValue) + { + Debug.Assert(value != null); + Debug.Assert(this.ValueMemberProperty != null || this.DisplayMemberProperty != null || + !string.IsNullOrEmpty(this.ValueMember) || !string.IsNullOrEmpty(this.DisplayMember)); + + object item = null; + if (this.DisplayMemberProperty != null || this.ValueMemberProperty != null) + { + //Now look up the item in the Combobox datasource - this can be horribly inefficient + //and it uses reflection which makes it expensive - ripe for optimization + item = this.ItemFromComboBoxDataSource(this.ValueMemberProperty != null ? this.ValueMemberProperty : this.DisplayMemberProperty, value); + } + else + { + //Find the item in the Items collection based on the provided ValueMember or DisplayMember + item = ItemFromComboBoxItems(rowIndex, string.IsNullOrEmpty(this.ValueMember) ? this.DisplayMember : this.ValueMember, value); + } + if (item == null) + { + displayValue = null; + return false; + } + + //Now we've got the item for the value - we can get the display text using the DisplayMember + + // DisplayMember & ValueMember may be null in which case we will use the item itself + displayValue = GetItemDisplayValue(item); + return true; + } + + /// + /// Lookup the value for the given display value. + /// + /// We use the display value and DisplayMember to look up the item in the + /// ComboBox datasource. We then use ValueMember to get the value. + /// + private bool LookupValue(object formattedValue, out object value) + { + if (formattedValue == null) + { + value = null; + return true; + } + + Debug.Assert(this.DisplayMemberProperty != null || this.ValueMemberProperty != null || + !string.IsNullOrEmpty(this.DisplayMember) || !string.IsNullOrEmpty(this.ValueMember)); + + object item = null; + if (this.DisplayMemberProperty != null || this.ValueMemberProperty != null) + { + //Now look up the item in the DataGridViewComboboxCell datasource - this can be horribly inefficient + //and it uses reflection which makes it expensive - ripe for optimization + item = ItemFromComboBoxDataSource(this.DisplayMemberProperty != null ? this.DisplayMemberProperty : this.ValueMemberProperty, formattedValue); + } + else + { + //Find the item in the Items collection based on the provided DisplayMember or ValueMember + item = ItemFromComboBoxItems(this.RowIndex, string.IsNullOrEmpty(this.DisplayMember) ? this.ValueMember : this.DisplayMember, formattedValue); + } + if (item == null) + { + value = null; + return false; + } + + //Now we've got the item for the value - we can get the value using the ValueMember + value = GetItemValue(item); + return true; + } + + /// + protected override void OnDataGridViewChanged() + { + if (this.DataGridView != null) + { + // Will throw an error if DataGridView is set and a member is invalid + InitializeDisplayMemberPropertyDescriptor(this.DisplayMember); + InitializeValueMemberPropertyDescriptor(this.ValueMember); + } + base.OnDataGridViewChanged(); + } + + /// + protected override void OnEnter(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + if (throughMouseClick && this.DataGridView.EditMode != DataGridViewEditMode.EditOnEnter) + { + this.flags |= (byte)DATAGRIDVIEWCOMBOBOXCELL_ignoreNextMouseClick; + } + } + + private void OnItemsCollectionChanged() + { + if (this.TemplateComboBoxColumn != null) + { + Debug.Assert(this.TemplateComboBoxColumn.CellTemplate == this); + this.TemplateComboBoxColumn.OnItemsCollectionChanged(); + } + cachedDropDownWidth = -1; + if (OwnsEditingComboBox(this.RowIndex)) + { + InitializeComboBoxText(); + } + else + { + OnCommonChange(); + } + } + + /// + protected override void OnLeave(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_ignoreNextMouseClick); + } + + /// + protected override void OnMouseClick(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + Debug.Assert(e.ColumnIndex == this.ColumnIndex); + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X == e.ColumnIndex && ptCurrentCell.Y == e.RowIndex) + { + if ((this.flags & DATAGRIDVIEWCOMBOBOXCELL_ignoreNextMouseClick) != 0x00) + { + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_ignoreNextMouseClick); + } + else if ((this.EditingComboBox == null || !this.EditingComboBox.DroppedDown) && + this.DataGridView.EditMode != DataGridViewEditMode.EditProgrammatically && + this.DataGridView.BeginEdit(true /*selectAll*/)) + { + if (this.EditingComboBox != null && this.DisplayStyle != DataGridViewComboBoxDisplayStyle.Nothing) + { + CheckDropDownList(e.X, e.Y, e.RowIndex); + } + } + } + } + + /// + protected override void OnMouseEnter(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + + if (this.DisplayStyle == DataGridViewComboBoxDisplayStyle.ComboBox && this.FlatStyle == FlatStyle.Popup) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + + base.OnMouseEnter(rowIndex); + } + + /// + protected override void OnMouseLeave(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + + if (mouseInDropDownButtonBounds) + { + mouseInDropDownButtonBounds = false; + if (this.ColumnIndex >= 0 && + rowIndex >= 0 && + (this.FlatStyle == FlatStyle.Standard || this.FlatStyle == FlatStyle.System) && + this.DataGridView.ApplyVisualStylesToInnerCells) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + if (this.DisplayStyle == DataGridViewComboBoxDisplayStyle.ComboBox && this.FlatStyle == FlatStyle.Popup) + { + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + + base.OnMouseEnter(rowIndex); + } + + /// + protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if ((this.FlatStyle == FlatStyle.Standard || this.FlatStyle == FlatStyle.System) && this.DataGridView.ApplyVisualStylesToInnerCells) + { + int rowIndex = e.RowIndex; + DataGridViewCellStyle cellStyle = GetInheritedStyle(null, rowIndex, false /*includeColors*/); + + // get the border style + bool singleVerticalBorderAdded = !this.DataGridView.RowHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single; + bool singleHorizontalBorderAdded = !this.DataGridView.ColumnHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single; + bool isFirstDisplayedRow = rowIndex == this.DataGridView.FirstDisplayedScrollingRowIndex; + bool isFirstDisplayedColumn = this.OwningColumn.Index == this.DataGridView.FirstDisplayedColumnIndex; + bool isFirstDisplayedScrollingColumn = this.OwningColumn.Index == this.DataGridView.FirstDisplayedScrollingColumnIndex; + DataGridViewAdvancedBorderStyle dgvabsEffective, dgvabsPlaceholder; + dgvabsPlaceholder = new DataGridViewAdvancedBorderStyle(); + dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle, dgvabsPlaceholder, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + isFirstDisplayedRow, + isFirstDisplayedColumn); + + Rectangle cellBounds = this.DataGridView.GetCellDisplayRectangle(this.OwningColumn.Index, rowIndex, false /*cutOverflow*/); + Rectangle cutoffCellBounds = cellBounds; + if (isFirstDisplayedScrollingColumn) + { + cellBounds.X -= this.DataGridView.FirstDisplayedScrollingColumnHiddenWidth; + cellBounds.Width += this.DataGridView.FirstDisplayedScrollingColumnHiddenWidth; + } + + DataGridViewElementStates rowState = this.DataGridView.Rows.GetRowState(rowIndex); + DataGridViewElementStates cellState = this.CellStateFromColumnRowStates(rowState); + cellState |= this.State; + + Rectangle dropDownButtonRect; + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + PaintPrivate(g, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // dropDownButtonRect is independent of formattedValue + null /*errorText*/, // dropDownButtonRect is independent of errorText + cellStyle, + dgvabsEffective, + out dropDownButtonRect, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*computeDropDownButtonRect*/, + false /*paint*/); + } + + bool newMouseInDropDownButtonBounds = dropDownButtonRect.Contains(this.DataGridView.PointToClient(Control.MousePosition)); + if (newMouseInDropDownButtonBounds != mouseInDropDownButtonBounds) + { + mouseInDropDownButtonBounds = newMouseInDropDownButtonBounds; + this.DataGridView.InvalidateCell(e.ColumnIndex, rowIndex); + } + } + base.OnMouseMove(e); + } + + private bool OwnsEditingComboBox(int rowIndex) + { + return rowIndex != -1 && this.EditingComboBox != null && rowIndex == ((IDataGridViewEditingControl)this.EditingComboBox).EditingControlRowIndex; + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Rectangle dropDownButtonRect; + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + elementState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + out dropDownButtonRect, // not used + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*computeDropDownButtonRect*/, + true /*paint*/); + } + + // PaintPrivate is used in four places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // 4. DataGridViewCell::OnMouseMove - to compute the dropDownButtonRect + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + // + // PaintPrivate uses the computeDropDownButtonRect to determine if it should compute the dropDownButtonRect + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + out Rectangle dropDownButtonRect, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool computeDropDownButtonRect, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds || computeDropDownButtonRect); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds || !computeDropDownButtonRect); + Debug.Assert(!paint || !computeContentBounds || !computeDropDownButtonRect || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint || !computeDropDownButtonRect); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !computeDropDownButtonRect || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds || !computeDropDownButtonRect); + Debug.Assert(!computeErrorIconBounds || !paint || !computeDropDownButtonRect || !computeContentBounds); + Debug.Assert(cellStyle != null); + + Rectangle resultBounds = Rectangle.Empty; + dropDownButtonRect = Rectangle.Empty; + + bool paintFlat = this.FlatStyle == FlatStyle.Flat || this.FlatStyle == FlatStyle.Popup; + bool paintPopup = this.FlatStyle == FlatStyle.Popup && + this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex; + + bool paintXPThemes = !paintFlat && this.DataGridView.ApplyVisualStylesToInnerCells; + bool paintPostXPThemes = paintXPThemes && PostXPThemesExist; + + ComboBoxState comboBoxState = ComboBoxState.Normal; + if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex && + mouseInDropDownButtonBounds) + { + comboBoxState = ComboBoxState.Hot; + } + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + Rectangle valBounds = cellBounds; + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + SolidBrush br; + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + bool cellCurrent = ptCurrentCell.X == this.ColumnIndex && ptCurrentCell.Y == rowIndex; + bool cellEdited = cellCurrent && this.DataGridView.EditingControl != null; + bool cellSelected = (elementState & DataGridViewElementStates.Selected) != 0; + bool drawComboBox = this.DisplayStyle == DataGridViewComboBoxDisplayStyle.ComboBox && + ((this.DisplayStyleForCurrentCellOnly && cellCurrent) || !this.DisplayStyleForCurrentCellOnly); + bool drawDropDownButton = this.DisplayStyle != DataGridViewComboBoxDisplayStyle.Nothing && + ((this.DisplayStyleForCurrentCellOnly && cellCurrent) || !this.DisplayStyleForCurrentCellOnly); + if (DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected && !cellEdited) + { + br = this.DataGridView.GetCachedBrush(cellStyle.SelectionBackColor); + } + else + { + br = this.DataGridView.GetCachedBrush(cellStyle.BackColor); + } + + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255 && valBounds.Width > 0 && valBounds.Height > 0) + { + DataGridViewCell.PaintPadding(g, valBounds, cellStyle, br, this.DataGridView.RightToLeftInternal); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + if (paint && valBounds.Width > 0 && valBounds.Height > 0) + { + if (paintXPThemes && drawComboBox) + { + if (paintPostXPThemes && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds.Left, valBounds.Top, valBounds.Width, valBounds.Height); + } + if (DataGridViewCell.PaintContentBackground(paintParts)) + { + if (paintPostXPThemes) + { + DataGridViewComboBoxCellRenderer.DrawBorder(g, valBounds); + } + else + { + DataGridViewComboBoxCellRenderer.DrawTextBox(g, valBounds, comboBoxState); + } + } + if (!paintPostXPThemes && + DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255 && valBounds.Width > 2 && valBounds.Height > 2) + { + g.FillRectangle(br, valBounds.Left + 1, valBounds.Top + 1, valBounds.Width - 2, valBounds.Height - 2); + } + } + else if (DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + if (paintPostXPThemes && drawDropDownButton && !drawComboBox) + { + g.DrawRectangle(SystemPens.ControlLightLight, new Rectangle(valBounds.X, valBounds.Y, valBounds.Width-1, valBounds.Height-1)); + } + else + { + g.FillRectangle(br, valBounds.Left, valBounds.Top, valBounds.Width, valBounds.Height); + } + } + } + + int dropWidth = Math.Min(SystemInformation.HorizontalScrollBarThumbWidth, valBounds.Width - 2 * DATAGRIDVIEWCOMBOBOXCELL_margin - 1); + + if (!cellEdited) + { + int dropHeight; + if (paintXPThemes || paintFlat) + { + dropHeight = Math.Min(GetDropDownButtonHeight(g, cellStyle), paintPostXPThemes ? valBounds.Height : valBounds.Height - 2); + } + else + { + dropHeight = Math.Min(GetDropDownButtonHeight(g, cellStyle), valBounds.Height - 4); + } + + if (dropWidth > 0 && dropHeight > 0) + { + Rectangle dropRect; + if (paintXPThemes || paintFlat) + { + if (paintPostXPThemes) + { + dropRect = new Rectangle(this.DataGridView.RightToLeftInternal ? valBounds.Left : valBounds.Right - dropWidth, + valBounds.Top, + dropWidth, + dropHeight); + } + else + { + dropRect = new Rectangle(this.DataGridView.RightToLeftInternal ? valBounds.Left + 1 : valBounds.Right - dropWidth - 1, + valBounds.Top + 1, + dropWidth, + dropHeight); + } + } + else + { + dropRect = new Rectangle(this.DataGridView.RightToLeftInternal ? valBounds.Left + 2 : valBounds.Right - dropWidth - 2, + valBounds.Top + 2, + dropWidth, + dropHeight); + } + + if (paintPostXPThemes && drawDropDownButton && !drawComboBox) + { + dropDownButtonRect = valBounds; + } + else + { + dropDownButtonRect = dropRect; + } + + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + if (drawDropDownButton) + { + if (paintFlat) + { + g.FillRectangle(SystemBrushes.Control, dropRect); + } + else if (paintXPThemes) + { + if (paintPostXPThemes) + { + if (drawComboBox) + { + DataGridViewComboBoxCellRenderer.DrawDropDownButton(g, dropRect, comboBoxState, this.DataGridView.RightToLeftInternal); + } + else + { + DataGridViewComboBoxCellRenderer.DrawReadOnlyButton(g, valBounds, comboBoxState); + DataGridViewComboBoxCellRenderer.DrawDropDownButton(g, dropRect, ComboBoxState.Normal); + } + + if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) + { + // In the case of ComboBox style, background is not filled in, + // in the case of DrawReadOnlyButton uses theming API to render CP_READONLY COMBOBOX part that renders the background, + // this API does not have "selected" state, thus always uses BackColor + br = this.DataGridView.GetCachedBrush(cellStyle.BackColor); + } + } + else + { + DataGridViewComboBoxCellRenderer.DrawDropDownButton(g, dropRect, comboBoxState); + } + } + else + { + g.FillRectangle(SystemBrushes.Control, dropRect); + } + } + if (!paintFlat && !paintXPThemes && (drawComboBox || drawDropDownButton)) + { + // border painting is ripped from button renderer + Color color= SystemColors.Control; + Color buttonShadow; + Color buttonShadowDark; + Color buttonFace = color; + Color highlight; + bool stockColor = color.ToKnownColor() == SystemColors.Control.ToKnownColor(); + bool highContrast = SystemInformation.HighContrast; + if (color == SystemColors.Control) + { + buttonShadow = SystemColors.ControlDark; + buttonShadowDark = SystemColors.ControlDarkDark; + highlight = SystemColors.ControlLightLight; + } + else + { + buttonShadow = ControlPaint.Dark(color); + highlight = ControlPaint.LightLight(color); + if (highContrast) + { + buttonShadowDark = ControlPaint.LightLight(color); + } + else + { + buttonShadowDark = ControlPaint.DarkDark(color); + } + } + + buttonShadow = g.GetNearestColor(buttonShadow); + buttonShadowDark = g.GetNearestColor(buttonShadowDark); + buttonFace = g.GetNearestColor(buttonFace); + highlight = g.GetNearestColor(highlight); + // top + left + Pen pen; + if (stockColor) { + if (SystemInformation.HighContrast) { + pen = SystemPens.ControlLight; + } + else { + pen = SystemPens.Control; + } + } + else { + pen= new Pen(highlight); + } + + if (drawDropDownButton) + { + g.DrawLine(pen, dropRect.X, dropRect.Y, + dropRect.X + dropRect.Width - 1, dropRect.Y); + g.DrawLine(pen, dropRect.X, dropRect.Y, + dropRect.X, dropRect.Y + dropRect.Height - 1); + } + // the bounds around the combobox control + if (drawComboBox) + { + g.DrawLine(pen, valBounds.X, valBounds.Y + valBounds.Height - 1, + valBounds.X + valBounds.Width - 1, valBounds.Y + valBounds.Height - 1); + g.DrawLine(pen, valBounds.X + valBounds.Width - 1, valBounds.Y, + valBounds.X + valBounds.Width - 1, valBounds.Y + valBounds.Height - 1); + } + // bottom + right + if (stockColor) { + pen = SystemPens.ControlDarkDark; + } + else { + pen.Color = buttonShadowDark; + } + if (drawDropDownButton) + { + g.DrawLine(pen, dropRect.X, dropRect.Y + dropRect.Height - 1, + dropRect.X + dropRect.Width - 1, dropRect.Y + dropRect.Height - 1); + g.DrawLine(pen, dropRect.X + dropRect.Width - 1, dropRect.Y, + dropRect.X + dropRect.Width - 1, dropRect.Y + dropRect.Height - 1); + } + // the bounds around the combobox control + if (drawComboBox) + { + g.DrawLine(pen, valBounds.X, valBounds.Y, + valBounds.X + valBounds.Width - 2, valBounds.Y); + g.DrawLine(pen, valBounds.X, valBounds.Y, + valBounds.X, valBounds.Y + valBounds.Height - 1); + } + // Top + Left inset + if (stockColor) + { + pen = SystemPens.ControlLightLight; + } + else + { + pen.Color = buttonFace; + } + if (drawDropDownButton) + { + g.DrawLine(pen, dropRect.X + 1, dropRect.Y + 1, + dropRect.X + dropRect.Width - 2, dropRect.Y + 1); + g.DrawLine(pen, dropRect.X + 1, dropRect.Y + 1, + dropRect.X + 1, dropRect.Y + dropRect.Height - 2); + } + // Bottom + Right inset + if (stockColor) { + pen = SystemPens.ControlDark; + } + else { + pen.Color = buttonShadow; + } + if (drawDropDownButton) + { + g.DrawLine(pen, dropRect.X + 1, dropRect.Y + dropRect.Height - 2, + dropRect.X + dropRect.Width - 2, dropRect.Y + dropRect.Height - 2); + g.DrawLine(pen, dropRect.X + dropRect.Width - 2, dropRect.Y + 1, + dropRect.X + dropRect.Width - 2, dropRect.Y + dropRect.Height - 2); + } + if (!stockColor) { + pen.Dispose(); + } + } + + if (dropWidth >= 5 && dropHeight >= 3 && drawDropDownButton) + { + if (paintFlat) + { + Point middle = new Point(dropRect.Left + dropRect.Width / 2, dropRect.Top + dropRect.Height / 2); + // if the width is odd - favor pushing it over one pixel right. + middle.X += (dropRect.Width % 2); + // if the height is odd - favor pushing it over one pixel down. + middle.Y += (dropRect.Height % 2); + + g.FillPolygon(SystemBrushes.ControlText, new Point[] + { + new Point(middle.X - offset2X, middle.Y - 1), + new Point(middle.X + offset2X + 1, middle.Y - 1), + new Point(middle.X, middle.Y + offset2Y) + }); + } + else if (!paintXPThemes) + { + // XPThemes already painted the drop down button + + // the down arrow looks better when it's fatten up by a pixel + dropRect.X--; + dropRect.Width++; + + Point middle = new Point(dropRect.Left + (dropRect.Width - 1) / 2, + dropRect.Top + (dropRect.Height + nonXPTriangleHeight) / 2); + // if the width is event - favor pushing it over one pixel right. + middle.X += ((dropRect.Width + 1) % 2); + // if the height is odd - favor pushing it over one pixel down. + middle.Y += (dropRect.Height % 2); + Point pt1 = new Point(middle.X - (nonXPTriangleWidth - 1) / 2, middle.Y - nonXPTriangleHeight); + Point pt2 = new Point(middle.X + (nonXPTriangleWidth - 1) / 2, middle.Y - nonXPTriangleHeight); + g.FillPolygon(SystemBrushes.ControlText, new Point[] { pt1, pt2, middle }); + // quirk in GDI+ : if we dont draw the line below then the top right most pixel of the DropDown triangle will not paint + // Would think that g.FillPolygon would have painted that... + g.DrawLine(SystemPens.ControlText, pt1.X, pt1.Y, pt2.X, pt2.Y); + + // slim down the drop rect + dropRect.X++; + dropRect.Width--; + } + } + + if (paintPopup && drawComboBox) + { + // draw a dark border around the dropdown rect if we are in popup mode + dropRect.Y--; + dropRect.Height++; + g.DrawRectangle(SystemPens.ControlDark, dropRect); + } + } + } + } + + Rectangle errorBounds = valBounds; + Rectangle textBounds = Rectangle.Inflate(valBounds, -2, -2); + + if (paintPostXPThemes) + { + if (!this.DataGridView.RightToLeftInternal) + { + textBounds.X--; + } + textBounds.Width++; + } + + if (drawDropDownButton) + { + if (paintXPThemes || paintFlat) + { + errorBounds.Width -= dropWidth; + textBounds.Width -= dropWidth; + if (this.DataGridView.RightToLeftInternal) + { + errorBounds.X += dropWidth; + textBounds.X += dropWidth; + } + } + else + { + errorBounds.Width -= dropWidth + 1; + textBounds.Width -= dropWidth + 1; + if (this.DataGridView.RightToLeftInternal) + { + errorBounds.X += dropWidth + 1; + textBounds.X += dropWidth + 1; + } + } + } + + if (textBounds.Width > 1 && textBounds.Height > 1) + { + if (cellCurrent && + !cellEdited && + DataGridViewCell.PaintFocus(paintParts) && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + paint) + { + // Draw focus rectangle + if (paintFlat) + { + Rectangle focusBounds = textBounds; + if (!this.DataGridView.RightToLeftInternal) + { + focusBounds.X--; + } + focusBounds.Width++; + focusBounds.Y--; + focusBounds.Height+=2; + ControlPaint.DrawFocusRectangle(g, focusBounds, Color.Empty, br.Color); + } + else if (paintPostXPThemes) + { + Rectangle focusBounds = textBounds; + focusBounds.X++; + focusBounds.Width -= 2; + focusBounds.Y++; + focusBounds.Height -= 2; + if (focusBounds.Width > 0 && focusBounds.Height > 0) + { + ControlPaint.DrawFocusRectangle(g, focusBounds, Color.Empty, br.Color); + } + } + else + { + ControlPaint.DrawFocusRectangle(g, textBounds, Color.Empty, br.Color); + } + } + + if (paintPopup) + { + valBounds.Width--; + valBounds.Height--; + if (!cellEdited && paint && DataGridViewCell.PaintContentBackground(paintParts) && drawComboBox) + { + g.DrawRectangle(SystemPens.ControlDark, valBounds); + } + } + + string formattedString = formattedValue as string; + + if (formattedString != null) + { + // Font independent margins + int verticalTextMarginTop = cellStyle.WrapMode == DataGridViewTriState.True ? DATAGRIDVIEWCOMBOBOXCELL_verticalTextMarginTopWithWrapping : DATAGRIDVIEWCOMBOBOXCELL_verticalTextMarginTopWithoutWrapping; + if (this.DataGridView.RightToLeftInternal) + { + textBounds.Offset(DATAGRIDVIEWCOMBOBOXCELL_horizontalTextMarginLeft, verticalTextMarginTop); + textBounds.Width += 2 - DATAGRIDVIEWCOMBOBOXCELL_horizontalTextMarginLeft; + } + else + { + textBounds.Offset(DATAGRIDVIEWCOMBOBOXCELL_horizontalTextMarginLeft - 1, verticalTextMarginTop); + textBounds.Width += 1 - DATAGRIDVIEWCOMBOBOXCELL_horizontalTextMarginLeft; + } + textBounds.Height -= verticalTextMarginTop; + + if (textBounds.Width > 0 && textBounds.Height > 0) + { + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (!cellEdited && paint) + { + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + Color textColor; + if (paintPostXPThemes && (drawDropDownButton || drawComboBox)) + { + textColor = DataGridViewComboBoxCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + textColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + TextRenderer.DrawText(g, + formattedString, + cellStyle.Font, + textBounds, + textColor, + flags); + } + } + else if (computeContentBounds) + { + resultBounds = DataGridViewUtilities.GetTextBounds(textBounds, formattedString, flags, cellStyle); + } + } + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + if (cellEdited) + { + return Rectangle.Empty; + } + } + } + + if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + else + { + resultBounds = Rectangle.Empty; + } + } + + return resultBounds; + } + + /// + public override object ParseFormattedValue(object formattedValue, + DataGridViewCellStyle cellStyle, + TypeConverter formattedValueTypeConverter, + TypeConverter valueTypeConverter) + { + if (valueTypeConverter == null) + { + if (this.ValueMemberProperty != null) + { + valueTypeConverter = this.ValueMemberProperty.Converter; + } + else if (this.DisplayMemberProperty != null) + { + valueTypeConverter = this.DisplayMemberProperty.Converter; + } + } + + // Find the item given its display value + if ((this.DataManager != null && + (this.DisplayMemberProperty != null || this.ValueMemberProperty != null)) || + !string.IsNullOrEmpty(this.DisplayMember) || !string.IsNullOrEmpty(this.ValueMember)) + { + object value = ParseFormattedValueInternal(this.DisplayType, formattedValue, cellStyle, + formattedValueTypeConverter, this.DisplayTypeConverter); + object originalValue = value; + if (!LookupValue(originalValue, out value)) + { + if (originalValue == System.DBNull.Value) + { + value = System.DBNull.Value; + } + else + { + throw new FormatException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Formatter_CantConvert), value, this.DisplayType)); + } + } + return value; + } + else + { + return ParseFormattedValueInternal(this.ValueType, formattedValue, cellStyle, + formattedValueTypeConverter, valueTypeConverter); + } + } + + /// + /// + /// + /// Gets the row Index and column Index of the cell. + /// + /// + public override string ToString() + { + return "DataGridViewComboBoxCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private void UnwireDataSource() + { + IComponent component = this.DataSource as IComponent; + if (component != null) + { + component.Disposed -= new EventHandler(DataSource_Disposed); + } + + ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification; + if (dsInit != null && (this.flags & DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp) != 0x00) + { + // If we previously hooked the data source's ISupportInitializeNotification + // Initialized event, then unhook it now (we don't always hook this event, + // only if we needed to because the data source was previously uninitialized) + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + this.flags = (byte)(this.flags & ~DATAGRIDVIEWCOMBOBOXCELL_dataSourceInitializedHookedUp); + } + } + + private void WireDataSource(object dataSource) + { + // If the source is a component, then hook the Disposed event, + // so we know when the component is deleted from the form + IComponent component = dataSource as IComponent; + if (component != null) + { + component.Disposed += new EventHandler(DataSource_Disposed); + } + } + + /// + /// + /// + /// A collection that stores objects. + /// + /// + [ListBindable(false)] + public class ObjectCollection : IList + { + private DataGridViewComboBoxCell owner; + private ArrayList items; + private IComparer comparer; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(DataGridViewComboBoxCell owner) + { + Debug.Assert(owner != null); + this.owner = owner; + } + + private IComparer Comparer + { + get + { + if (this.comparer == null) + { + this.comparer = new ItemComparer(this.owner); + } + return this.comparer; + } + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count + { + get + { + return this.InnerArray.Count; + } + } + + /// + /// Internal access to the actual data store. + /// + internal ArrayList InnerArray + { + get + { + if (this.items == null) + { + this.items = new ArrayList(); + } + return this.items; + } + } + + /// + /// + object ICollection.SyncRoot + { + get + { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + /// + bool IList.IsFixedSize + { + get + { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly + { + get + { + return false; + } + } + + /// + /// + /// Adds an item to the collection. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's ToString() method is called to obtain the string that is + /// displayed in the combo box. + /// + public int Add(object item) + { + //this.owner.CheckNoSharedCell(); //VSWhidbey 515823 + this.owner.CheckNoDataSource(); + + if (item == null) + { + throw new ArgumentNullException("item"); + } + + int index = this.InnerArray.Add(item); + + bool success = false; + if (this.owner.Sorted) + { + try + { + this.InnerArray.Sort(this.Comparer); + index = this.InnerArray.IndexOf(item); + success = true; + } + finally + { + if (!success) + { + this.InnerArray.Remove(item); + } + } + } + + this.owner.OnItemsCollectionChanged(); + return index; + } + + /// + /// + int IList.Add(object item) + { + return Add(item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(params object[] items) + { + //this.owner.CheckNoSharedCell(); //VSWhidbey 515823 + this.owner.CheckNoDataSource(); + AddRangeInternal((ICollection)items); + this.owner.OnItemsCollectionChanged(); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ObjectCollection value) + { + //this.owner.CheckNoSharedCell(); //VSWhidbey 515823 + this.owner.CheckNoDataSource(); + AddRangeInternal((ICollection) value); + this.owner.OnItemsCollectionChanged(); + } + + /// + /// Add range that bypasses the data source check. + /// + internal void AddRangeInternal(ICollection items) + { + if (items == null) + { + throw new ArgumentNullException("items"); + } + + foreach(object item in items) + { + if (item == null) + { + throw new InvalidOperationException(SR.GetString(SR.InvalidNullItemInCollection)); + } + } + + // Add everything to the collection first, then sort + this.InnerArray.AddRange(items); + if (this.owner.Sorted) + { + this.InnerArray.Sort(this.Comparer); + } + } + + internal void SortInternal() + { + this.InnerArray.Sort(this.Comparer); + } + + /// + /// + /// Retrieves the item with the specified index. + /// + public virtual object this[int index] + { + get + { + if (index < 0 || index >= this.InnerArray.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + return this.InnerArray[index]; + } + set + { + //this.owner.CheckNoSharedCell(); //VSWhidbey 515823 + this.owner.CheckNoDataSource(); + + if (value == null) + { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= this.InnerArray.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + this.InnerArray[index] = value; + this.owner.OnItemsCollectionChanged(); + } + } + + /// + /// + /// Removes all items from the collection. + /// + public void Clear() + { + if (this.InnerArray.Count > 0) + { + //this.owner.CheckNoSharedCell(); //VSWhidbey 515823 + this.owner.CheckNoDataSource(); + this.InnerArray.Clear(); + this.owner.OnItemsCollectionChanged(); + } + } + + internal void ClearInternal() + { + this.InnerArray.Clear(); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object value) + { + return IndexOf(value) != -1; + } + + /// + /// + /// Copies the DataGridViewComboBoxCell Items collection to a destination array. + /// + public void CopyTo(object[] destination, int arrayIndex) + { + int count = this.InnerArray.Count; + for(int i = 0; i < count; i++) + { + destination[i + arrayIndex] = this.InnerArray[i]; + } + } + + /// + /// + void ICollection.CopyTo(Array destination, int index) + { + int count = this.InnerArray.Count; + for(int i = 0; i < count; i++) + { + destination.SetValue(this.InnerArray[i], i + index); + } + } + + /// + /// + /// Returns an enumerator for the DataGridViewComboBoxCell Items collection. + /// + public IEnumerator GetEnumerator() + { + return this.InnerArray.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object value) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + return this.InnerArray.IndexOf(value); + } + + /// + /// + /// Adds an item to the collection. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// + public void Insert(int index, object item) + { + //this.owner.CheckNoSharedCell(); VSWhidbey 515823 + this.owner.CheckNoDataSource(); + + if (item == null) + { + throw new ArgumentNullException("item"); + } + + if (index < 0 || index > this.InnerArray.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + // If the combo box is sorted, then just treat this like an add + // because we are going to twiddle the index anyway. + if (this.owner.Sorted) + { + Add(item); + } + else + { + this.InnerArray.Insert(index, item); + this.owner.OnItemsCollectionChanged(); + } + } + + /// + /// + /// Removes the given item from the collection, provided that it is + /// actually in the list. + /// + public void Remove(object value) + { + int index = this.InnerArray.IndexOf(value); + + if (index != -1) + { + RemoveAt(index); + } + } + + /// + /// + /// Removes an item from the collection at the given index. + /// + public void RemoveAt(int index) + { + //this.owner.CheckNoSharedCell(); VSWhidbey 515823 + this.owner.CheckNoDataSource(); + + if (index < 0 || index >= this.InnerArray.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + this.InnerArray.RemoveAt(index); + this.owner.OnItemsCollectionChanged(); + } + } // end ObjectCollection + + private sealed class ItemComparer : System.Collections.IComparer + { + private DataGridViewComboBoxCell dataGridViewComboBoxCell; + + public ItemComparer(DataGridViewComboBoxCell dataGridViewComboBoxCell) + { + this.dataGridViewComboBoxCell = dataGridViewComboBoxCell; + } + + public int Compare(object item1, object item2) + { + if (item1 == null) + { + if (item2 == null) + { + return 0; //both null, then they are equal + } + return -1; //item1 is null, but item2 is valid (greater) + } + if (item2 == null) + { + return 1; //item2 is null, so item 1 is greater + } + String itemName1 = this.dataGridViewComboBoxCell.GetItemDisplayText(item1); + String itemName2 = this.dataGridViewComboBoxCell.GetItemDisplayText(item2); + + CompareInfo compInfo = Application.CurrentCulture.CompareInfo; + return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); + } + } + + private class DataGridViewComboBoxCellRenderer + { + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer; + private static readonly VisualStyleElement ComboBoxBorder = VisualStyleElement.ComboBox.Border.Normal; + private static readonly VisualStyleElement ComboBoxDropDownButtonRight = VisualStyleElement.ComboBox.DropDownButtonRight.Normal; + private static readonly VisualStyleElement ComboBoxDropDownButtonLeft = VisualStyleElement.ComboBox.DropDownButtonLeft.Normal; + private static readonly VisualStyleElement ComboBoxReadOnlyButton = VisualStyleElement.ComboBox.ReadOnlyButton.Normal; + + private DataGridViewComboBoxCellRenderer() + { + } + + public static VisualStyleRenderer VisualStyleRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxReadOnlyButton); + } + return visualStyleRenderer; + } + } + + public static void DrawTextBox(Graphics g, Rectangle bounds, ComboBoxState state) + { + ComboBoxRenderer.DrawTextBox(g, bounds, state); + } + + public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxState state) + { + ComboBoxRenderer.DrawDropDownButton(g, bounds, state); + } + + // Post XP theming functions + public static void DrawBorder(Graphics g, Rectangle bounds) + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxBorder); + } + else + { + visualStyleRenderer.SetParameters(ComboBoxBorder.ClassName, ComboBoxBorder.Part, ComboBoxBorder.State); + } + visualStyleRenderer.DrawBackground(g, bounds); + } + + public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxState state, bool rightToLeft) + { + if (rightToLeft) + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxDropDownButtonLeft.ClassName, ComboBoxDropDownButtonLeft.Part, (int)state); + } + else + { + visualStyleRenderer.SetParameters(ComboBoxDropDownButtonLeft.ClassName, ComboBoxDropDownButtonLeft.Part, (int)state); + } + } + else + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxDropDownButtonRight.ClassName, ComboBoxDropDownButtonRight.Part, (int)state); + } + else + { + visualStyleRenderer.SetParameters(ComboBoxDropDownButtonRight.ClassName, ComboBoxDropDownButtonRight.Part, (int)state); + } + } + visualStyleRenderer.DrawBackground(g, bounds); + } + + public static void DrawReadOnlyButton(Graphics g, Rectangle bounds, ComboBoxState state) + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(ComboBoxReadOnlyButton.ClassName, ComboBoxReadOnlyButton.Part, (int)state); + } + else + { + visualStyleRenderer.SetParameters(ComboBoxReadOnlyButton.ClassName, ComboBoxReadOnlyButton.Part, (int)state); + } + visualStyleRenderer.DrawBackground(g, bounds); + } + } + + /// + [ComVisible(true)] + protected class DataGridViewComboBoxCellAccessibleObject : DataGridViewCellAccessibleObject + { + + /// + public DataGridViewComboBoxCellAccessibleObject(DataGridViewCell owner) : base(owner) + { + } + + internal override bool IsIAccessibleExSupported() + { + return true; + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) + { + return NativeMethods.UIA_ComboBoxControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxColumn.cs new file mode 100644 index 000000000..f6e94ddf8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxColumn.cs @@ -0,0 +1,557 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + + /// + [ + Designer("System.Windows.Forms.Design.DataGridViewComboBoxColumnDesigner, " + AssemblyRef.SystemDesign), + ToolboxBitmapAttribute(typeof(DataGridViewComboBoxColumn), "DataGridViewComboBoxColumn.bmp") + ] + public class DataGridViewComboBoxColumn : DataGridViewColumn + { + private static Type columnType = typeof(DataGridViewComboBoxColumn); + + /// + public DataGridViewComboBoxColumn() : base(new DataGridViewComboBoxCell()) + { + ((DataGridViewComboBoxCell)base.CellTemplate).TemplateComboBoxColumn = this; + } + + /// + [ + Browsable(true), + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ComboBoxColumnAutoCompleteDescr) + ] + public bool AutoComplete + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.AutoComplete; + } + set + { + if (this.AutoComplete != value) + { + this.ComboBoxCellTemplate.AutoComplete = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.AutoComplete = value; + } + } + } + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + DataGridViewComboBoxCell dataGridViewComboBoxCell = value as DataGridViewComboBoxCell; + if (value != null && dataGridViewComboBoxCell == null) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewComboBoxCell")); + } + base.CellTemplate = value; + if (value != null) + { + dataGridViewComboBoxCell.TemplateComboBoxColumn = this; + } + } + } + + private DataGridViewComboBoxCell ComboBoxCellTemplate + { + get + { + return (DataGridViewComboBoxCell) this.CellTemplate; + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_ComboBoxColumnDataSourceDescr), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + ] + public object DataSource + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.DataSource; + } + set + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ComboBoxCellTemplate.DataSource = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.DataSource = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + + /// + [ + DefaultValue(""), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_ComboBoxColumnDisplayMemberDescr), + TypeConverterAttribute("System.Windows.Forms.Design.DataMemberFieldConverter, " + AssemblyRef.SystemDesign), + Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string DisplayMember + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.DisplayMember; + } + set + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ComboBoxCellTemplate.DisplayMember = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.DisplayMember = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + + /// + [ + DefaultValue(DataGridViewComboBoxDisplayStyle.DropDownButton), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ComboBoxColumnDisplayStyleDescr) + ] + public DataGridViewComboBoxDisplayStyle DisplayStyle + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.DisplayStyle; + } + set + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ComboBoxCellTemplate.DisplayStyle = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.DisplayStyleInternal = value; + } + } + // Calling InvalidateColumn instead of OnColumnCommonChange because DisplayStyle does not affect preferred size. + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ComboBoxColumnDisplayStyleForCurrentCellOnlyDescr) + ] + public bool DisplayStyleForCurrentCellOnly + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.DisplayStyleForCurrentCellOnly; + } + set + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ComboBoxCellTemplate.DisplayStyleForCurrentCellOnly = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.DisplayStyleForCurrentCellOnlyInternal = value; + } + } + // Calling InvalidateColumn instead of OnColumnCommonChange because DisplayStyleForCurrentCellOnly does not affect preferred size. + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + + /// + [ + DefaultValue(1), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ComboBoxColumnDropDownWidthDescr), + ] + public int DropDownWidth + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.DropDownWidth; + } + set + { + if (this.DropDownWidth != value) + { + this.ComboBoxCellTemplate.DropDownWidth = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.DropDownWidth = value; + } + } + } + } + } + } + + /// + [ + DefaultValue(FlatStyle.Standard), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ComboBoxColumnFlatStyleDescr), + ] + public FlatStyle FlatStyle + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewComboBoxCell) this.CellTemplate).FlatStyle; + } + set + { + if (this.FlatStyle != value) + { + ((DataGridViewComboBoxCell)this.CellTemplate).FlatStyle = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.FlatStyleInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + [ + Editor("System.Windows.Forms.Design.StringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_ComboBoxColumnItemsDescr) + ] + public DataGridViewComboBoxCell.ObjectCollection Items + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.GetItems(this.DataGridView); + } + } + + /// + [ + DefaultValue(""), + SRCategory(SR.CatData), + SRDescription(SR.DataGridView_ComboBoxColumnValueMemberDescr), + TypeConverterAttribute("System.Windows.Forms.Design.DataMemberFieldConverter, " + AssemblyRef.SystemDesign), + Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string ValueMember + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.ValueMember; + } + set + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ComboBoxCellTemplate.ValueMember = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.ValueMember = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + + /// + [ + DefaultValue(DataGridViewComboBoxCell.DATAGRIDVIEWCOMBOBOXCELL_defaultMaxDropDownItems), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ComboBoxColumnMaxDropDownItemsDescr) + ] + public int MaxDropDownItems + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.MaxDropDownItems; + } + set + { + if (this.MaxDropDownItems != value) + { + this.ComboBoxCellTemplate.MaxDropDownItems = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.MaxDropDownItems = value; + } + } + } + } + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_ComboBoxColumnSortedDescr) + ] + public bool Sorted + { + get + { + if (this.ComboBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ComboBoxCellTemplate.Sorted; + } + set + { + if (this.Sorted != value) + { + this.ComboBoxCellTemplate.Sorted = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.Sorted = value; + } + } + } + } + } + } + + /// + public override object Clone() + { + DataGridViewComboBoxColumn dataGridViewColumn; + Type thisType = this.GetType(); + + if (thisType == columnType) //performance improvement + { + dataGridViewColumn = new DataGridViewComboBoxColumn(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewColumn = (DataGridViewComboBoxColumn)System.Activator.CreateInstance(thisType); + } + if (dataGridViewColumn != null) + { + base.CloneInternal(dataGridViewColumn); + ((DataGridViewComboBoxCell) dataGridViewColumn.CellTemplate).TemplateComboBoxColumn = dataGridViewColumn; + } + return dataGridViewColumn; + } + + internal void OnItemsCollectionChanged() + { + // Items collection of the CellTemplate was changed. + // Update the items collection of each existing DataGridViewComboBoxCell in the column. + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + object[] items = ((DataGridViewComboBoxCell)this.CellTemplate).Items.InnerArray.ToArray(); + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewComboBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewComboBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.Items.ClearInternal(); + dataGridViewCell.Items.AddRangeInternal(items); + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewComboBoxColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxDisplayStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxDisplayStyle.cs new file mode 100644 index 000000000..cce542db3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxDisplayStyle.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewComboBoxDisplayStyle + { + /// + ComboBox, + + /// + DropDownButton, + + /// + Nothing + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs new file mode 100644 index 000000000..8ce205381 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs @@ -0,0 +1,281 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Runtime.InteropServices; + using Security.Permissions; + + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + public class DataGridViewComboBoxEditingControl : ComboBox, IDataGridViewEditingControl + { + private DataGridView dataGridView; + private bool valueChanged; + private int rowIndex; + + /// + public DataGridViewComboBoxEditingControl() : base() + { + this.TabStop = false; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level3) + { + return new DataGridViewComboBoxEditingControlAccessibleObject(this); + } + else if (AccessibilityImprovements.Level2) + { + return new DataGridViewEditingControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + // IDataGridViewEditingControl interface implementation + + /// + public virtual DataGridView EditingControlDataGridView + { + get + { + return this.dataGridView; + } + set + { + this.dataGridView = value; + } + } + + /// + public virtual object EditingControlFormattedValue + { + get + { + return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); + } + set + { + string valueStr = value as string; + if (valueStr != null) + { + this.Text = valueStr; + if (String.Compare(valueStr, this.Text, true, CultureInfo.CurrentCulture) != 0) + { + this.SelectedIndex = -1; + } + } + } + } + + /// + public virtual int EditingControlRowIndex + { + get + { + return this.rowIndex; + } + set + { + this.rowIndex = value; + } + } + + /// + public virtual bool EditingControlValueChanged + { + get + { + return this.valueChanged; + } + set + { + this.valueChanged = value; + } + } + + /// + public virtual Cursor EditingPanelCursor + { + get + { + return Cursors.Default; + } + } + + /// + public virtual bool RepositionEditingControlOnValueChange + { + get + { + return false; + } + } + + /// + public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) + { + this.Font = dataGridViewCellStyle.Font; + if (dataGridViewCellStyle.BackColor.A < 255) + { + // Our ComboBox does not support transparent back colors + Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor); + this.BackColor = opaqueBackColor; + this.dataGridView.EditingPanel.BackColor = opaqueBackColor; + } + else + { + this.BackColor = dataGridViewCellStyle.BackColor; + } + this.ForeColor = dataGridViewCellStyle.ForeColor; + } + + /// + public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) + { + if ((keyData & Keys.KeyCode) == Keys.Down || + (keyData & Keys.KeyCode) == Keys.Up || + (this.DroppedDown && ((keyData & Keys.KeyCode) == Keys.Escape) || (keyData & Keys.KeyCode) == Keys.Enter)) + { + return true; + } + return !dataGridViewWantsInputKey; + } + + /// + public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) + { + return this.Text; + } + + /// + public virtual void PrepareEditingControlForEdit(bool selectAll) + { + if (selectAll) + { + SelectAll(); + } + } + + private void NotifyDataGridViewOfValueChange() + { + this.valueChanged = true; + this.dataGridView.NotifyCurrentCellDirty(true); + } + + /// + protected override void OnSelectedIndexChanged(EventArgs e) + { + base.OnSelectedIndexChanged(e); + if (this.SelectedIndex != -1) + { + NotifyDataGridViewOfValueChange(); + } + } + } + + /// + /// Defines the DataGridView ComboBox EditingControl accessible object. + /// + /// + /// This accessible object is only available in AccessibilityImprovements of Level 3. + /// + internal class DataGridViewComboBoxEditingControlAccessibleObject : ComboBox.ComboBoxUiaProvider + { + private DataGridViewComboBoxEditingControl ownerControl; + + /// + /// The parent is changed when the editing control is attached to another editing cell. + /// + private AccessibleObject _parentAccessibleObject = null; + + public DataGridViewComboBoxEditingControlAccessibleObject(DataGridViewComboBoxEditingControl ownerControl) : base(ownerControl) + { + this.ownerControl = ownerControl; + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return _parentAccessibleObject; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var owner = Owner as IDataGridViewEditingControl; + if (owner != null && owner.EditingControlDataGridView.EditingControl == owner) + { + return _parentAccessibleObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.AccessibilityObject; + } + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) + { + return ownerControl.DropDownStyle != ComboBoxStyle.Simple; + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState + { + get + { + return ownerControl.DroppedDown ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal override void SetParent(AccessibleObject parent) + { + _parentAccessibleObject = parent; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs.back b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs.back new file mode 100644 index 000000000..8ce205381 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs.back @@ -0,0 +1,281 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Runtime.InteropServices; + using Security.Permissions; + + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + public class DataGridViewComboBoxEditingControl : ComboBox, IDataGridViewEditingControl + { + private DataGridView dataGridView; + private bool valueChanged; + private int rowIndex; + + /// + public DataGridViewComboBoxEditingControl() : base() + { + this.TabStop = false; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level3) + { + return new DataGridViewComboBoxEditingControlAccessibleObject(this); + } + else if (AccessibilityImprovements.Level2) + { + return new DataGridViewEditingControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + // IDataGridViewEditingControl interface implementation + + /// + public virtual DataGridView EditingControlDataGridView + { + get + { + return this.dataGridView; + } + set + { + this.dataGridView = value; + } + } + + /// + public virtual object EditingControlFormattedValue + { + get + { + return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); + } + set + { + string valueStr = value as string; + if (valueStr != null) + { + this.Text = valueStr; + if (String.Compare(valueStr, this.Text, true, CultureInfo.CurrentCulture) != 0) + { + this.SelectedIndex = -1; + } + } + } + } + + /// + public virtual int EditingControlRowIndex + { + get + { + return this.rowIndex; + } + set + { + this.rowIndex = value; + } + } + + /// + public virtual bool EditingControlValueChanged + { + get + { + return this.valueChanged; + } + set + { + this.valueChanged = value; + } + } + + /// + public virtual Cursor EditingPanelCursor + { + get + { + return Cursors.Default; + } + } + + /// + public virtual bool RepositionEditingControlOnValueChange + { + get + { + return false; + } + } + + /// + public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) + { + this.Font = dataGridViewCellStyle.Font; + if (dataGridViewCellStyle.BackColor.A < 255) + { + // Our ComboBox does not support transparent back colors + Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor); + this.BackColor = opaqueBackColor; + this.dataGridView.EditingPanel.BackColor = opaqueBackColor; + } + else + { + this.BackColor = dataGridViewCellStyle.BackColor; + } + this.ForeColor = dataGridViewCellStyle.ForeColor; + } + + /// + public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) + { + if ((keyData & Keys.KeyCode) == Keys.Down || + (keyData & Keys.KeyCode) == Keys.Up || + (this.DroppedDown && ((keyData & Keys.KeyCode) == Keys.Escape) || (keyData & Keys.KeyCode) == Keys.Enter)) + { + return true; + } + return !dataGridViewWantsInputKey; + } + + /// + public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) + { + return this.Text; + } + + /// + public virtual void PrepareEditingControlForEdit(bool selectAll) + { + if (selectAll) + { + SelectAll(); + } + } + + private void NotifyDataGridViewOfValueChange() + { + this.valueChanged = true; + this.dataGridView.NotifyCurrentCellDirty(true); + } + + /// + protected override void OnSelectedIndexChanged(EventArgs e) + { + base.OnSelectedIndexChanged(e); + if (this.SelectedIndex != -1) + { + NotifyDataGridViewOfValueChange(); + } + } + } + + /// + /// Defines the DataGridView ComboBox EditingControl accessible object. + /// + /// + /// This accessible object is only available in AccessibilityImprovements of Level 3. + /// + internal class DataGridViewComboBoxEditingControlAccessibleObject : ComboBox.ComboBoxUiaProvider + { + private DataGridViewComboBoxEditingControl ownerControl; + + /// + /// The parent is changed when the editing control is attached to another editing cell. + /// + private AccessibleObject _parentAccessibleObject = null; + + public DataGridViewComboBoxEditingControlAccessibleObject(DataGridViewComboBoxEditingControl ownerControl) : base(ownerControl) + { + this.ownerControl = ownerControl; + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return _parentAccessibleObject; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var owner = Owner as IDataGridViewEditingControl; + if (owner != null && owner.EditingControlDataGridView.EditingControl == owner) + { + return _parentAccessibleObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.AccessibilityObject; + } + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) + { + return ownerControl.DropDownStyle != ComboBoxStyle.Simple; + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState + { + get + { + return ownerControl.DroppedDown ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal override void SetParent(AccessibleObject parent) + { + _parentAccessibleObject = parent; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewContentAlignment.cs b/WindowsForms/Managed/System/WinForms/DataGridViewContentAlignment.cs new file mode 100644 index 000000000..8d19c6b43 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewContentAlignment.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + /// + public enum DataGridViewContentAlignment + { + /// + NotSet = 0x000, + + /// + /// + /// Content is vertically aligned at the top, and horizontally + /// aligned on the left. + /// + TopLeft = 0x001, + + /// + /// + /// + /// Content is vertically aligned at the top, and + /// horizontally aligned at the center. + /// + /// + TopCenter = 0x002, + + /// + /// + /// + /// Content is vertically aligned at the top, and + /// horizontally aligned on the right. + /// + /// + TopRight = 0x004, + + /// + /// + /// + /// Content is vertically aligned in the middle, and + /// horizontally aligned on the left. + /// + /// + MiddleLeft = 0x010, + + /// + /// + /// + /// Content is vertically aligned in the middle, and + /// horizontally aligned at the center. + /// + /// + MiddleCenter = 0x020, + + /// + /// + /// + /// Content is vertically aligned in the middle, and horizontally aligned on the + /// right. + /// + /// + MiddleRight = 0x040, + + /// + /// + /// + /// Content is vertically aligned at the bottom, and horizontally aligned on the + /// left. + /// + /// + BottomLeft = 0x100, + + /// + /// + /// + /// Content is vertically aligned at the bottom, and horizontally aligned at the + /// center. + /// + /// + BottomCenter = 0x200, + + /// + /// + /// + /// Content is vertically aligned at the bottom, and horizontally aligned on the + /// right. + /// + /// + BottomRight = 0x400, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewControlCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewControlCollection.cs new file mode 100644 index 000000000..1c975e839 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewControlCollection.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Collections; +using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + /// + [ + ComVisible(false), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewControlCollection : Control.ControlCollection + { + DataGridView owner; + + /// + public DataGridViewControlCollection(DataGridView owner) + : base(owner) + { + this.owner = owner; + } + + /// + public void CopyTo(Control[] array, int index) + { + base.CopyTo(array, index); + } + + /// + public void Insert(int index, Control value) + { + ((IList)this).Insert(index, (object)value); + } + + /// + public override void Remove(Control value) + { + if (value != owner.horizScrollBar && value != owner.vertScrollBar && value != this.owner.editingPanel) + { + base.Remove(value); + } + } + + internal void RemoveInternal(Control value) + { + base.Remove(value); + } + + /// + public override void Clear() + { + for (int i = 0; i < this.Count; i++) + { + if (this[i] == this.owner.horizScrollBar || this[i] == this.owner.vertScrollBar || this[i] == this.owner.editingPanel) + { + continue; + } + else + { + Remove(this[i]); + } + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewDataConnection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewDataConnection.cs new file mode 100644 index 000000000..6051e7109 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewDataConnection.cs @@ -0,0 +1,1678 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.ComponentModel; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Drawing; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + internal class DataGridViewDataConnection + { + DataGridView owner = null; + CurrencyManager currencyManager = null; + object dataSource = null; + string dataMember = String.Empty; + PropertyDescriptorCollection props = null; + int lastListCount = -1; + + // + // data connection state variables + // + private BitVector32 dataConnectionState; + private const int DATACONNECTIONSTATE_dataConnection_inSetDataConnection = 0x00000001; + private const int DATACONNECTIONSTATE_processingMetaDataChanges = 0x00000002; + + // AddNew + private const int DATACONNECTIONSTATE_finishedAddNew = 0x00000004; + + private const int DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl = 0x00000008; + // DataGridView::SetCurrentCellAddressCore makes the current row unavailable during the OnRowEnter event. + // we use the doNotChangePositionInTheCurrencyManager flag to go around this. + private const int DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager = 0x00000010; + + private const int DATACONNECTIONSTATE_interestedInRowEvents = 0x00000020; + private const int DATACONNECTIONSTATE_cancellingRowEdit = 0x00000040; + private const int DATACONNECTIONSTATE_restoreRow = 0x00000080; + private const int DATACONNECTIONSTATE_rowValidatingInAddNew = 0x00000100; + private const int DATACONNECTIONSTATE_inAddNew = 0x00000200; + private const int DATACONNECTIONSTATE_listWasReset = 0x00000400; + private const int DATACONNECTIONSTATE_positionChangingInCurrencyManager = 0x00000800; + + // + // The following three constants deal w/ the following situation: + // This is Master-Details schema. + // One DGV is bound to Master, another DGV is bound to Details. + // Master has 1 row. + // The user deletes the one and only row from Master + // + // Then the following sequence of Events happen: + // 1. DGV deletes the row from Master + // 2. The Child currency manager finds out that there are no rows in the Master table + // 3. The Child currency manager adds a row in the Master table - vsWhidbey 193802 which tracks removal of this feature was POSTPONED. + // 4. The DGV bound to the Master table receives the ItemAdded event. At this point, no rows have been deleted from the DGV. + // 5. The DGV bound to the Master table should not add a new DataGridViewRow to its Rows collection because it will be deleted later on. + // So the DGV marks _itemAddedInDeleteOperation to TRUE to know that the next event it expects is an ItemDeleted + // 6. The DGV bound to the Master table receives the ItemDeleted event. + // It goes ahead and deletes the item and resets _itemAddedInDeleteOperation + // + private const int DATACONNECTIONSTATE_inDeleteOperation = 0x00001000; + private const int DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView = 0x00002000; + private const int DATACONNECTIONSTATE_itemAddedInDeleteOperation = 0x00004000; + + // This constant is used to know if EndCurentEdit caused an item to be deleted from the back end + private const int DATACONNECTIONSTATE_inEndCurrentEdit = 0x00008000; + + // We need to cache the value of AllowUserToAddRowsInternal because it may change outside the DataGridView. + // When the DataGridView catches this change it will refresh its rows collection, no questions asked. + private const int DATACONNECTIONSTATE_cachedAllowUserToAddRowsInternal = 0x00010000; + + private const int DATACONNECTIONSTATE_processingListChangedEvent = 0x00020000; + + private const int DATACONNECTIONSTATE_dataSourceInitializedHookedUp = 0x00040000; + + public DataGridViewDataConnection(DataGridView owner) + { + this.owner = owner; + this.dataConnectionState = new BitVector32(DATACONNECTIONSTATE_finishedAddNew); + } + + public bool AllowAdd + { + get + { + if (this.currencyManager != null) + { + // we only allow to add new rows on an IBindingList + return (this.currencyManager.List is IBindingList) && this.currencyManager.AllowAdd && ((IBindingList)this.currencyManager.List).SupportsChangeNotification; + } + else + { + return false; + } + } + } + + public bool AllowEdit + { + get + { + if (this.currencyManager != null) + { + return this.currencyManager.AllowEdit; + } + else + { + return false; + } + } + } + + public bool AllowRemove + { + get + { + if (this.currencyManager != null) + { + // we only allow deletion on an IBindingList + return (this.currencyManager.List is IBindingList) && this.currencyManager.AllowRemove && ((IBindingList)this.currencyManager.List).SupportsChangeNotification; + } + else + { + return false; + } + } + } + + public bool CancellingRowEdit + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit]; + } + } + + public CurrencyManager CurrencyManager + { + get + { + return this.currencyManager; + } + } + + public string DataMember + { + get + { + return this.dataMember; + } + } + + public object DataSource + { + get + { + return this.dataSource; + } + } + + public bool DoNotChangePositionInTheCurrencyManager + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager]; + } + set + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager] = value; + } + } + + public bool InterestedInRowEvents + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_interestedInRowEvents]; + } + } + + public IList List + { + get + { + if (this.currencyManager != null) + { + return this.currencyManager.List; + } + else + { + return null; + } + } + } + + public bool ListWasReset + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_listWasReset]; + } + } + + public bool PositionChangingOutsideDataGridView + { + get + { + // DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl means that the data grid view control + // manages the position change + // so if DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl is true then the data grid view knows about the position change + return !this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] && + this.dataConnectionState[DATACONNECTIONSTATE_positionChangingInCurrencyManager]; + } + } + + public bool ProcessingListChangedEvent + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_processingListChangedEvent]; + } + } + + public bool ProcessingMetaDataChanges + { + get + { + return this.dataConnectionState[DATACONNECTIONSTATE_processingMetaDataChanges]; + } + } + + public bool RestoreRow + { + get + { + Debug.Assert(this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit]); + return this.dataConnectionState[DATACONNECTIONSTATE_restoreRow]; + } + } + + public void AddNew() + { + if (this.currencyManager != null) + { + // don't call AddNew on a suspended currency manager. + if (!this.currencyManager.ShouldBind) + { + return; + } + + Debug.Assert(this.currencyManager.AllowAdd, "why did we call AddNew on the currency manager when the currency manager does not allow new rows?"); + this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] = false; + + this.dataConnectionState[DATACONNECTIONSTATE_inEndCurrentEdit] = true; + try + { + this.currencyManager.EndCurrentEdit(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_inEndCurrentEdit] = false; + } + + this.dataConnectionState[DATACONNECTIONSTATE_inAddNew] = true; + + try + { + this.currencyManager.AddNew(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_inAddNew] = false; + } + } + } + + // + // This method pulls the information about which dataField is sorted on the IBindingList + // and applies it to the DataGridView. + // + // Here is how it does that (see vsWhidbey 230619 for reference): + // 1. Updating the DataGridView::SortedColumn property: + // When multiple columns are bound to a sorted column + // in the backend then the DataGridView::SortedColumn property should return the + // first column in index order that is sorted. For example, if the datasource is sorted on CustomerID and two + // CustomerID columns are in the grid at index 0 and 5, then SortedColumn should return the DGVColumn at index 0. + // 2. Changes to DataGridView::SortGlyphDirection. + // Go thru all the data bound columns on the back end and if they map to the sorted dataField + // set their SortGlyphDirection to the sort direction on the back end. + // + // Note: on IBindingList there is only one column that can be sorted. + // So if the back end is an IBindingView ( which supports sorting on multiple columns ) this code will not take into + // account the case that multiple columns are sorted. + // + public void ApplySortingInformationFromBackEnd() + { + if (this.currencyManager == null) + { + return; + } + + PropertyDescriptor sortField = null; + SortOrder sortOrder; + GetSortingInformationFromBackend(out sortField, out sortOrder); + + // If we are not bound to a sorted IBindingList then set the SortGlyphDirection to SortOrder.None + // on each dataBound DataGridViewColumn. + // This will have the side effect of setting DataGridView::SortedColumn to null and setting DataGridView::SortOrder to null. + if (sortField == null) + { + for (int i = 0; i < this.owner.Columns.Count; i++) + { + if (this.owner.Columns[i].IsDataBound) + { + this.owner.Columns[i].HeaderCell.SortGlyphDirection = SortOrder.None; + } + } + + this.owner.sortedColumn = null; + this.owner.sortOrder = SortOrder.None; + + // now return; + return; + } + + bool setSortedColumnYet = false; + for (int i = 0; i < this.owner.Columns.Count; i++) + { + DataGridViewColumn column = this.owner.Columns[i]; + if (!column.IsDataBound) + { + continue; + } + + if (column.SortMode == DataGridViewColumnSortMode.NotSortable) + { + continue; + } + + if (String.Equals(column.DataPropertyName, sortField.Name, StringComparison.OrdinalIgnoreCase)) + { + // Set the sorted column on the dataGridView only if the sorted Field is set outside the dataGridView. + // If the sortedField is set inside the dataGridView ( either by user clicking on a ColumnHeader or by user calling DGV.Sort(...) + // then we don't want to tamper w/ it. + if (!setSortedColumnYet && !this.owner.InSortOperation) + { + this.owner.sortedColumn = column; + this.owner.sortOrder = sortOrder; + setSortedColumnYet = true; + } + + // set the SortGlyphDirection on the data bound DataGridViewColumn + column.HeaderCell.SortGlyphDirection = sortOrder; + } + else + { + column.HeaderCell.SortGlyphDirection = SortOrder.None; + } + } + } + + public TypeConverter BoundColumnConverter(int boundColumnIndex) + { + Debug.Assert(this.props != null); + return this.props[boundColumnIndex].Converter; + } + + // given a data field name we get the bound index + public int BoundColumnIndex(string dataPropertyName) + { + if (this.props == null) + { + return -1; + } + + int ret = -1; + + for (int i = 0; i < this.props.Count; i++) + { + if (String.Compare(this.props[i].Name, dataPropertyName, true /*ignoreCase*/, CultureInfo.InvariantCulture) == 0) + { + ret = i; + break; + } + } + + return ret; + } + + public SortOrder BoundColumnSortOrder(int boundColumnIndex) + { + IBindingList ibl = this.currencyManager != null ? this.currencyManager.List as IBindingList : null; + IBindingListView iblv = ibl != null ? ibl as IBindingListView : null; + + if (ibl == null || !ibl.SupportsSorting || !ibl.IsSorted) + { + return SortOrder.None; + } + + PropertyDescriptor sortProperty; + SortOrder sortOrder; + + GetSortingInformationFromBackend(out sortProperty, out sortOrder); + + if (sortOrder == SortOrder.None) + { + Debug.Assert(sortProperty == null); + return SortOrder.None; + } + + if (String.Compare(this.props[boundColumnIndex].Name, sortProperty.Name, true /*ignoreCase*/, CultureInfo.InvariantCulture) == 0) + { + return sortOrder; + } + else + { + return SortOrder.None; + } + } + + public Type BoundColumnValueType(int boundColumnIndex) + { + Debug.Assert(this.props != null); + return this.props[boundColumnIndex].PropertyType; + } + +#if DEBUG + private void CheckRowCount(ListChangedEventArgs e) + { + if (e.ListChangedType != ListChangedType.Reset) + { + return; + } + + int dataGridViewRowsCount = this.owner.Rows.Count; + + Debug.Assert(DataBoundRowsCount() == this.currencyManager.List.Count || (this.owner.Columns.Count == 0 && dataGridViewRowsCount == 0), + "there should be the same number of rows in the dataGridView's Row Collection as in the back end list"); + } +#endif // DEBUG + + private void currencyManager_ListChanged(object sender, ListChangedEventArgs e) + { + Debug.Assert(sender == this.currencyManager, "did we forget to unregister our ListChanged event handler?"); + + this.dataConnectionState[DATACONNECTIONSTATE_processingListChangedEvent] = true; + try + { + ProcessListChanged(e); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_processingListChangedEvent] = false; + } + + this.owner.OnDataBindingComplete(e.ListChangedType); + + this.lastListCount = this.currencyManager.Count; + +#if DEBUG + CheckRowCount(e); +#endif // DEBUG + } + + private void ProcessListChanged(ListChangedEventArgs e) + { + if (e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorAdded || + e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorDeleted || + e.ListChangedType == System.ComponentModel.ListChangedType.PropertyDescriptorChanged) + { + this.dataConnectionState[DATACONNECTIONSTATE_processingMetaDataChanges] = true; + try + { + DataSourceMetaDataChanged(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_processingMetaDataChanges] = false; + } + return; + } + + Debug.Assert(!this.dataConnectionState[DATACONNECTIONSTATE_inAddNew] || !this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew], + "if inAddNew is true then finishedAddNew should be false"); + + // The value of AllowUserToAddRowsInternal changed under the DataGridView. + // Recreate the rows and return. + if (this.dataConnectionState[DATACONNECTIONSTATE_cachedAllowUserToAddRowsInternal] != this.owner.AllowUserToAddRowsInternal) + { + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = true; + try + { + this.owner.RefreshRows(!this.owner.InSortOperation /*scrollIntoView*/); + this.owner.PushAllowUserToAddRows(); + } + finally + { + // this will also set DATACONNECTIONSTATE_listWasReset to false + ResetDataConnectionState(); + } + return; + } + + // if the list changed the AddNew and we did not finish the AddNew operation then + // finish it now and return + if (!this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] && this.owner.newRowIndex == e.NewIndex) + { + Debug.Assert(this.owner.AllowUserToAddRowsInternal, "how did we start the add new transaction when the AllowUserToAddRowsInternal is false?"); + if (e.ListChangedType == ListChangedType.ItemAdded) + { + if (this.dataConnectionState[DATACONNECTIONSTATE_inAddNew]) + { + // still processing CurrencyManager::AddNew + // nothing to do + return; + } + + if (this.dataConnectionState[DATACONNECTIONSTATE_rowValidatingInAddNew]) + { + // DataGridView validation commited the AddNewRow to the back end + // DataGridView took care of newRowIndex, adding a new DataGridViewRow, etc + // we don't have to do anything + return; + } + + // We got a ListChangedType.ItemAdded event outside row validation and outside CurrencyManager::AddNew + if (this.owner.Columns.Count > 0) + { + // add rows until the back end and the DGV have the same number of bound rows. + do + { + // the new row becomes a regular row and a "new" new row is appended + this.owner.newRowIndex = -1; + this.owner.AddNewRow(false /* createdByEditing */); + } while (DataBoundRowsCount() < this.currencyManager.Count); + } + + this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] = true; + MatchCurrencyManagerPosition(true /*scrollIntoView*/, true /*clearSelection*/); + return; + } + else if (e.ListChangedType == ListChangedType.ItemDeleted) + { + if (this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit]) + { + // 'add new row' was discarded, bring back the new row default values. + this.owner.PopulateNewRowWithDefaultValues(); + } + else if (this.dataConnectionState[DATACONNECTIONSTATE_inEndCurrentEdit] || + this.dataConnectionState[DATACONNECTIONSTATE_inAddNew]) + { + // A row was deleted while the DataGridView control asked for a new row. + // Recreate the data grid view rows. + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = true; + try + { + this.owner.RefreshRows(!this.owner.InSortOperation /*scrollIntoView*/); + this.owner.PushAllowUserToAddRows(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = false; + } + } + else if (this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] && this.currencyManager.List.Count == 0) + { + // if System.Data.DataView was in AddNew transaction and we delete all the rows in the System.Data.DataView + // then System.Data.DataView will close the AddNew transaction under us + // start another AddNew transaction on the back end + this.AddNew(); + } + } + + return; + } + + Debug.Assert(DataBoundRowsCount() != -1, "the data bound data grid view rows count should be at least 0"); + + // we received an ListChangedType.ItemAdded and our list has exactly the same number of rows as the back-end. + // return. + if (e.ListChangedType == ListChangedType.ItemAdded && + this.currencyManager.List.Count == (this.owner.AllowUserToAddRowsInternal ? this.owner.Rows.Count - 1 : this.owner.Rows.Count)) + { + if (this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] && this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView]) + { + // we received a ListChangedType.ItemAdded while we were deleting rows from the back end + // and we stil haven't removed a row from the data grid view + // System.Data.DataView started an AddNew transaction as a result of deleting rows + // mark the state as itemAddedInDeleteOperation + this.dataConnectionState[DATACONNECTIONSTATE_itemAddedInDeleteOperation] = true; + + // The DGV gets in this situation when the user deletes the last row in a Master table. + // At this point, the Child table forces an AddNew on the Master Table. + // See comments where we declare _itemAddedInDeleteOperation"); + // + Debug.Assert(this.currencyManager.List.Count == 1); + + // if we were on an AddNew transaction then the MASTER table would have had more than 1 row. + // So the Child table should not have forcefully added a row on the MASTER table"); + // + Debug.Assert(this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew]); + } + + return; + } + + // this is the first ItemDeleted event we get after the ItemAdded event that we got while we were deleting rows from the data view + // don't do anything - this is the equivalent of removing the row that was added before + if (e.ListChangedType == ListChangedType.ItemDeleted) + { + if (this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] && + this.dataConnectionState[DATACONNECTIONSTATE_itemAddedInDeleteOperation] && + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView]) + { + // we removed the item that was added during the delete operation + this.dataConnectionState[DATACONNECTIONSTATE_itemAddedInDeleteOperation] = false; + Debug.Assert(this.currencyManager.List.Count == 0, "we deleted the row that the Child table forcefully added"); + } + else if (!this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] && + this.dataConnectionState[DATACONNECTIONSTATE_inEndCurrentEdit]) + { + // EndCurrentEdit caused an item to be deleted while in AddNew. + // Recreate the rows. + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = true; + try + { + this.owner.RefreshRows(!this.owner.InSortOperation /*scrollIntoView*/); + this.owner.PushAllowUserToAddRows(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = false; + } + return; + } + else if (this.currencyManager.List.Count == DataBoundRowsCount()) + { + return; + } + } + + // when we get the ListChanged notification the position in the currency manager already changed + // so do not change the position when we get the RowEnter event + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager] = true; + + try + { + switch (e.ListChangedType) + { + case ListChangedType.Reset: + this.dataConnectionState[DATACONNECTIONSTATE_listWasReset] = true; + bool startUpdateInternal = this.owner.Visible; + if (startUpdateInternal) + { + this.owner.BeginUpdateInternal(); + } + try + { + this.owner.RefreshRows(!this.owner.InSortOperation /*scrollIntoView*/); + this.owner.PushAllowUserToAddRows(); + + // ListChangedType.Reset can signal that the list became sorted or that the list is not sorted anymore. + this.ApplySortingInformationFromBackEnd(); + } + finally + { + // this will also set DATACONNECTIONSTATE_listWasReset to false + ResetDataConnectionState(); + if (startUpdateInternal) + { + this.owner.EndUpdateInternal(false); + this.owner.Invalidate(true); + } + } + break; + case ListChangedType.ItemAdded: + if (this.owner.NewRowIndex == -1 || e.NewIndex != this.owner.Rows.Count) + { + this.owner.Rows.InsertInternal(e.NewIndex, this.owner.RowTemplateClone, true /*force*/); + } + else + { + #if DEBUG + Debug.Fail("fail in debug builds so we can catch this situation in the check in suites"); + #endif // DEBUG + throw new InvalidOperationException(); + } + break; + case ListChangedType.ItemDeleted: + this.owner.Rows.RemoveAtInternal(e.NewIndex, true /*force*/); + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView] = false; + break; + case ListChangedType.ItemMoved: + // an ItemMoved event means that all the rows shifted up or down by 1 + // we have to invalidate all the rows in between + Debug.Assert(e.OldIndex > -1, "the currency manager should have taken care of this case"); + Debug.Assert(e.NewIndex > -1, "how can we move an item outside of the list?"); + int lo = Math.Min(e.OldIndex, e.NewIndex); + int hi = Math.Max(e.OldIndex, e.NewIndex); + this.owner.InvalidateRows(lo, hi); + break; + case ListChangedType.ItemChanged: + Debug.Assert(e.NewIndex != -1, "the item changed event does not cover changes to the entire list"); + string dataPropertyName = null; + if (e.PropertyDescriptor != null) + { + dataPropertyName = ((System.ComponentModel.MemberDescriptor)(e.PropertyDescriptor)).Name; + } + for (int columnIndex = 0; columnIndex < this.owner.Columns.Count; columnIndex++) + { + DataGridViewColumn dataGridViewColumn = this.owner.Columns[columnIndex]; + if (dataGridViewColumn.Visible && dataGridViewColumn.IsDataBound) + { + if (!string.IsNullOrEmpty(dataPropertyName)) + { + if (String.Compare(dataGridViewColumn.DataPropertyName, dataPropertyName, true /*ignoreCase*/, CultureInfo.InvariantCulture) == 0) + { + this.owner.OnCellCommonChange(columnIndex, e.NewIndex); + } + } + else + { + this.owner.OnCellCommonChange(columnIndex, e.NewIndex); + } + } + } + // VSWhidbey 533264. Repaint the row header cell to show potential error icon + this.owner.InvalidateCell(-1, e.NewIndex); + // update the editing control value if the data changed in the row the user was editing + if (this.owner.CurrentCellAddress.Y == e.NewIndex && this.owner.IsCurrentCellInEditMode) + { + this.owner.RefreshEdit(); + } + break; + default: + break; + } + // now put the position in the DataGridView control according to the position in the currency manager + if (this.owner.Rows.Count > 0 && + !this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] && + !this.owner.InSortOperation) + { + MatchCurrencyManagerPosition(false /*scrollIntoView*/, e.ListChangedType == ListChangedType.Reset /*clearSelection*/); + } + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager] = false; + } + } + + private void currencyManager_PositionChanged(object sender, EventArgs e) + { + Debug.Assert(sender == this.currencyManager, "did we forget to unregister our events?"); + if (this.owner.Columns.Count == 0) + { + Debug.Assert(this.owner.CurrentCellAddress.X == -1); + // No columns means we can't set the current cell. + // This happens when all columns where removed from the dataGridView, and all rows were cleared. + // Discuss this with Daniel/Mark. + // One solution: impose at least one visible column - all the time. + return; + } + + if (this.owner.Rows.Count == (owner.AllowUserToAddRowsInternal ? 1 : 0)) + { + // the dataGridView control has not yet been notified that the list is not empty + // don't do anything + return; + } + + if (this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl]) + { + return; + } + + // vsw 517818 and vsw 530726: when the back end is still inside an AddNew we get a PositionChanged event before + // we get the list changed event. So, we get the position changed event before we have a chance to refresh our + // row collection. + // It may be the case that the new position in the currency manager corresponds to the DataGridView::AddNew row position. + // And then DataGridView will enter its AddNew row and as a result of that will start another AddNew transaction - inside + // the current AddNew transaction. + // The solution is to not change the current cell if: + // 1. DataGridView::AllowUserToAddRowsInternal == true, and + // 2. DataGridView is not inside DataGridView::AddNew transaction, and + // 3. the new position inside the currency manager is not -1. + // 4. the new position corresponds to the DataGridView::NewRow position, and + // 5. the position inside the DataGridView is not on the new row index. + // 6. the count on the back end list is 1 more than the number of data bound data grid view rows. + // The DataGridView will change its current cell once the currency manager fires ListChanged event. + if (this.owner.AllowUserToAddRowsInternal && // condition 1. + this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] && // condition 2. + !this.dataConnectionState[DATACONNECTIONSTATE_inAddNew] && // condition 2. + this.currencyManager.Position > -1 && // condition 3. + this.currencyManager.Position == this.owner.NewRowIndex && // condition 4. + this.owner.CurrentCellAddress.Y != this.owner.NewRowIndex && // condition 5. + this.currencyManager.Count == DataBoundRowsCount() + 1) // condition 6. + { + return; + } + + + + this.dataConnectionState[DATACONNECTIONSTATE_positionChangingInCurrencyManager] = true; + try + { + if (!this.owner.InSortOperation) + { + bool scrollIntoView = true; + // VSWhidbey 492203. When an item is repositioned in a sorted column, while its + // row is being committed, don't scroll it into view. + if (this.dataConnectionState[DATACONNECTIONSTATE_rowValidatingInAddNew]) + { + IBindingList ibl = this.currencyManager.List as IBindingList; + if (ibl != null && ibl.SupportsSorting && ibl.IsSorted) + { + scrollIntoView = false; + } + } + + // If the user hit Escape while in AddNew then we clear the selection. + bool clearSelection = this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit] && !this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew]; + // Otherwise we clear the selection if the last list count is still uninitialized + // or if it is the same as the current list count. + clearSelection |= this.lastListCount == -1 || this.lastListCount == this.currencyManager.Count; + MatchCurrencyManagerPosition(scrollIntoView, clearSelection); + } + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_positionChangingInCurrencyManager] = false; + } + } + + // + // This function will return the number of rows inside the DataGridView which are data bound. + // For instance, the AddNewRow inside the DataGridView is not data bound so it should not be counted. + // + private int DataBoundRowsCount() + { + int result = this.owner.Rows.Count; + if (this.owner.AllowUserToAddRowsInternal && this.owner.Rows.Count > 0) + { + Debug.Assert(this.owner.NewRowIndex != -1, "the NewRowIndex is -1 only when AllowUserToAddRows is false"); + + // We have to check if the AddNew row is data bound or not. + // The AddNew row is data bound if the user is positioned in the AddNew row and the AddNew row is not dirty + if (this.owner.CurrentCellAddress.Y != this.owner.NewRowIndex || this.owner.IsCurrentRowDirty) + { + // The AddNew row in the DataGridView row collection is not data bound. + // Substract it from the row count; + result--; + } + } + + return result; + } + + private void DataSource_Initialized(object sender, EventArgs e) + { + Debug.Assert(sender == this.dataSource); + Debug.Assert(this.dataSource is ISupportInitializeNotification); + Debug.Assert(this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp]); + + ISupportInitializeNotification dsInit = this.dataSource as ISupportInitializeNotification; + // Unhook the Initialized event. + if (dsInit != null) + { + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + } + + // The wait is over: DataSource is initialized. + this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp] = false; + + // Update the data manager + SetDataConnection(this.dataSource, this.dataMember); + Debug.Assert(this.currencyManager != null); + this.owner.RefreshColumnsAndRows(); + this.owner.OnDataBindingComplete(ListChangedType.Reset); + } + + private void DataSourceMetaDataChanged() + { + Debug.Assert(this.currencyManager != null); + + // get the new meta data + this.props = this.currencyManager.GetItemProperties(); + + // when AutoGenerate == true: RefreshColumnsAndRows will delete the previously dataBound columns and create new dataBounds columns + // + // AutoGenerate == false : RefreshColumnsAndRows will refresh the property descriptors for the dataBound Columns. + // Some unBound columns may become dataBound, some dataBounds columns may become unBound + // + + this.owner.RefreshColumnsAndRows(); + } + + public void DeleteRow(int rowIndex) + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = true; + try + { + if (!this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew]) + { + Debug.Assert(this.owner.AllowUserToAddRowsInternal, "how did we start an add new row transaction if the dataGridView control has AllowUserToAddRows == false?"); + bool deleteAddNewRow = false; + if (this.owner.newRowIndex == this.currencyManager.List.Count) + { + // the user clicked on the 'add new row' and started typing + deleteAddNewRow = (rowIndex == this.owner.newRowIndex - 1); + } + else + { + // the user clicked on the 'add new row' but did not start typing + Debug.Assert(this.owner.newRowIndex == this.currencyManager.List.Count - 1); + deleteAddNewRow = (rowIndex == this.owner.newRowIndex); + } + + if (deleteAddNewRow) + { + // we finished the add new transaction + CancelRowEdit(false /*restoreRow*/, true /*addNewFinished*/); + } + else + { + // start the Delete operation + this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] = true; + // we did not delete any rows from the data grid view yet + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView] = true; + try + { + this.currencyManager.RemoveAt(rowIndex); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] = false; + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView] = false; + } + } + } + else + { + // start the Delete operation + this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] = true; + // we did not delete any rows from the data grid view yet + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView] = true; + try + { + this.currencyManager.RemoveAt(rowIndex); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_inDeleteOperation] = false; + this.dataConnectionState[DATACONNECTIONSTATE_didNotDeleteRowFromDataGridView] = false; + } + } + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = false; + } + } + + public bool DataFieldIsReadOnly(int boundColumnIndex) + { + if (this.props == null) + { + Debug.Fail("we only care about which data fields are read only when we are data bound"); + return false; + } + + return this.props[boundColumnIndex].IsReadOnly; + } + + // All we do in dispose is to unwire the data source. + public void Dispose() + { + UnWireEvents(); + + // Set the currency manager to null so if someone would want to resurect this data grid view data connection + // we would not unwire the events from the curency manager twice. + // (NOTE: resurecting a disposed data grid view data connection is not allowed.) + // + this.currencyManager = null; + } + + private static DataGridViewColumn GetDataGridViewColumnFromType(Type type) + { + DataGridViewColumn dataGridViewColumn; + TypeConverter imageTypeConverter = TypeDescriptor.GetConverter(typeof(Image)); + if (type.Equals(typeof(bool)) || type.Equals(typeof(CheckState))) + { + dataGridViewColumn = new DataGridViewCheckBoxColumn(type.Equals(typeof(CheckState))); + } + else if (typeof(System.Drawing.Image).IsAssignableFrom(type) || imageTypeConverter.CanConvertFrom(type)) + { + dataGridViewColumn = new DataGridViewImageColumn(); + } + else + { + dataGridViewColumn = new DataGridViewTextBoxColumn(); + } + return dataGridViewColumn; + } + + public DataGridViewColumn[] GetCollectionOfBoundDataGridViewColumns() + { + if (this.props == null) + { + return null; + } + ArrayList cols = new ArrayList(); + + for (int i = 0; i < this.props.Count; i++) + { + if (typeof(IList).IsAssignableFrom(this.props[i].PropertyType)) + { + // we have an IList. It could be a byte[] in which case we want to generate an Image column + // + TypeConverter imageTypeConverter = TypeDescriptor.GetConverter(typeof(Image)); + if (!imageTypeConverter.CanConvertFrom(this.props[i].PropertyType)) + { + continue; + } + } + + DataGridViewColumn dataGridViewColumn = GetDataGridViewColumnFromType(this.props[i].PropertyType); + dataGridViewColumn.IsDataBoundInternal = true; + dataGridViewColumn.BoundColumnIndex = i; + // we set the data property name + // if you plan on removing this, then you have to change the lookup into + // the GetCollectionOfBoundDataGridViewColumns + dataGridViewColumn.DataPropertyName = this.props[i].Name; + dataGridViewColumn.Name = this.props[i].Name; + dataGridViewColumn.BoundColumnConverter = this.props[i].Converter; + dataGridViewColumn.HeaderText = !String.IsNullOrEmpty(this.props[i].DisplayName) ? this.props[i].DisplayName : this.props[i].Name; + dataGridViewColumn.ValueType = this.props[i].PropertyType; + + dataGridViewColumn.IsBrowsableInternal = this.props[i].IsBrowsable; + + dataGridViewColumn.ReadOnly = props[i].IsReadOnly; + + cols.Add(dataGridViewColumn); + } + + DataGridViewColumn[] ret = new DataGridViewColumn[cols.Count]; + cols.CopyTo(ret); + return ret; + } + + private void GetSortingInformationFromBackend(out PropertyDescriptor sortProperty, out SortOrder sortOrder) + { + IBindingList ibl = this.currencyManager != null ? this.currencyManager.List as IBindingList : null; + IBindingListView iblv = ibl != null ? ibl as IBindingListView : null; + + if (ibl == null || !ibl.SupportsSorting || !ibl.IsSorted) + { + sortOrder = SortOrder.None; + sortProperty = null; + return; + } + + if (ibl.SortProperty != null) + { + sortProperty = ibl.SortProperty; + sortOrder = ibl.SortDirection == ListSortDirection.Ascending ? SortOrder.Ascending : SortOrder.Descending; + } + else if (iblv != null) + { + // Maybe the data view is sorted on multiple columns. + // Go thru the IBindingListView which offers the entire list of sorted columns + // and pick the first one as the SortedColumn. + ListSortDescriptionCollection sorts = iblv.SortDescriptions; + if (sorts != null && + sorts.Count > 0 && + sorts[0].PropertyDescriptor != null) + { + sortProperty = sorts[0].PropertyDescriptor; + sortOrder = sorts[0].SortDirection == ListSortDirection.Ascending ? SortOrder.Ascending : SortOrder.Descending; + } + else + { + // The IBindingListView did not have any sorting information. + sortProperty = null; + sortOrder = SortOrder.None; + } + } + else + { + // We could not get the sort order either from IBindingList nor from IBindingListView. + sortProperty = null; + sortOrder = SortOrder.None; + } + } + + public void ResetCachedAllowUserToAddRowsInternal() + { + this.dataConnectionState[DATACONNECTIONSTATE_cachedAllowUserToAddRowsInternal] = this.owner.AllowUserToAddRowsInternal; + } + + private void ResetDataConnectionState() + { + // Microsoft: I wish there would be a Reset method on BitVector32... + this.dataConnectionState = new BitVector32(DATACONNECTIONSTATE_finishedAddNew); + + if (this.currencyManager != null) + { + this.dataConnectionState[DATACONNECTIONSTATE_interestedInRowEvents] = true; + } + + ResetCachedAllowUserToAddRowsInternal(); + } + + public void SetDataConnection(object dataSource, string dataMember) + { + if (this.dataConnectionState[DATACONNECTIONSTATE_dataConnection_inSetDataConnection]) + { + return; + } + + ResetDataConnectionState(); + + if (dataMember == null) + { + dataMember = String.Empty; + } + + ISupportInitializeNotification dsInit = this.dataSource as ISupportInitializeNotification; + if (dsInit != null && this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp]) + { + // If we previously hooked the data source's ISupportInitializeNotification + // Initialized event, then unhook it now (we don't always hook this event, + // only if we needed to because the data source was previously uninitialized) + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp] = false; + } + + this.dataSource = dataSource; + this.dataMember = dataMember; + + if (this.owner.BindingContext == null) + { + return; + } + + this.dataConnectionState[DATACONNECTIONSTATE_dataConnection_inSetDataConnection] = true; + try + { + // unwire the events + UnWireEvents(); + + if (this.dataSource != null && this.owner.BindingContext != null && !(this.dataSource == Convert.DBNull)) + { + dsInit = this.dataSource as ISupportInitializeNotification; + if (dsInit != null && !dsInit.IsInitialized) + { + if (!this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp]) + { + dsInit.Initialized += new EventHandler(DataSource_Initialized); + this.dataConnectionState[DATACONNECTIONSTATE_dataSourceInitializedHookedUp] = true; + } + this.currencyManager = null; + } + else + { + this.currencyManager = this.owner.BindingContext[this.dataSource, this.dataMember] as CurrencyManager; + } + } + else + { + this.currencyManager = null; + } + + // wire the events + WireEvents(); + if (this.currencyManager != null) + { + this.props = this.currencyManager.GetItemProperties(); + } + else + { + this.props = null; + } + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_dataConnection_inSetDataConnection] = false; + } + + ResetCachedAllowUserToAddRowsInternal(); + + if (this.currencyManager != null) + { + this.lastListCount = this.currencyManager.Count; + } + else + { + this.lastListCount = -1; + } + } + + public string GetError(int rowIndex) + { + IDataErrorInfo errInfo = null; + try + { + errInfo = this.currencyManager[rowIndex] as IDataErrorInfo; + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception) && !(exception is IndexOutOfRangeException)) + { + throw; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, -1 /*columnIndex*/, rowIndex, + DataGridViewDataErrorContexts.Display); + this.owner.OnDataErrorInternal(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + + if (errInfo != null) + { + return errInfo.Error; + } + else + { + return String.Empty; + } + } + + public string GetError(int boundColumnIndex, int columnIndex, int rowIndex) + { + Debug.Assert(rowIndex >= 0); + + IDataErrorInfo errInfo = null; + try + { + errInfo = this.currencyManager[rowIndex] as IDataErrorInfo; + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception) && !(exception is IndexOutOfRangeException)) + { + throw; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, columnIndex, rowIndex, + DataGridViewDataErrorContexts.Display); + this.owner.OnDataErrorInternal(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + + if (errInfo != null) + { + return errInfo[this.props[boundColumnIndex].Name]; + } + else + { + return String.Empty; + } + } + + public object GetValue(int boundColumnIndex, int columnIndex, int rowIndex) + { + Debug.Assert(rowIndex >= 0); + object value = null; + try + { + value = this.props[boundColumnIndex].GetValue(this.currencyManager[rowIndex]); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception) && !(exception is IndexOutOfRangeException)) + { + throw; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, columnIndex, rowIndex, + DataGridViewDataErrorContexts.Display); + this.owner.OnDataErrorInternal(dgvdee); + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + return value; + } + + public void MatchCurrencyManagerPosition(bool scrollIntoView, bool clearSelection) + { + if (this.owner.Columns.Count == 0) + { +#if DEBUG + // all the properties in the currency manager should be either Browsable(false) or point to sub lists + if (this.props != null) + { + for (int i = 0; i < this.props.Count; i ++) + { + Debug.Assert(!props[i].IsBrowsable || typeof(IList).IsAssignableFrom(props[i].PropertyType), "if the DGV does not have any columns then the properties in the currency manager should be Browsable(false) or point to sub lists"); + } + } +#endif // DEBUG + + // nothing to do + return; + } + + int columnIndex = this.owner.CurrentCellAddress.X == -1 ? this.owner.FirstDisplayedColumnIndex : this.owner.CurrentCellAddress.X; + + // Treat case where columnIndex == -1. We change the visibility of the first column. + if (columnIndex == -1) + { + DataGridViewColumn dataGridViewColumn = this.owner.Columns.GetFirstColumn(DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + dataGridViewColumn.Visible = true; + columnIndex = dataGridViewColumn.Index; + } + + int rowIndex = this.currencyManager.Position; + + Debug.Assert(rowIndex >= -1); + + if (rowIndex == -1) + { + // Occurs when calling SuspendBinding() on the currency manager? + if (!this.owner.SetCurrentCellAddressCore(-1, -1, + false, /*setAnchorCellAddress*/ + false, /*validateCurrentCell*/ + false /*throughMouseClick*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + else if (rowIndex < this.owner.Rows.Count) + { + // vsWhidbey 419230: the currency manager sends the PositionChanged event before the ListChanged event. + // This means that it is possible for the data grid view to receive the position changed event + // before it had a chance to created its rows. + // So, if the position inside the currency manager is greater than the number of rows in the data grid view + // don't do anything. + // NOTE: because the currency manager will fire the list changed event after the position changed event + // the data grid view will actually get a second chance at matching the position inside the currency manager. + + // Do not allow to set the current cell to an invisible cell + if ((this.owner.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + // Make the target row visible. + this.owner.Rows[rowIndex].Visible = true; + } + + if (rowIndex == this.owner.CurrentCellAddress.Y && columnIndex == this.owner.CurrentCellAddress.X) + { + return; + } + + // Scroll target cell into view first. + if ((scrollIntoView && !this.owner.ScrollIntoView(columnIndex, rowIndex, true)) || + (columnIndex < this.owner.Columns.Count && rowIndex < this.owner.Rows.Count && + !this.owner.SetAndSelectCurrentCellAddress(columnIndex, rowIndex, + true, /*setAnchorCellAddress*/ + false, /*validateCurrentCell*/ + false, /*throughMouseClick*/ + clearSelection, + false /*forceCurrentCellSelection*/))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CellChangeCannotBeCommittedOrAborted)); + } + } + } + + public void CancelRowEdit(bool restoreRow, bool addNewFinished) + { + this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit] = true; + this.dataConnectionState[DATACONNECTIONSTATE_restoreRow] = restoreRow; + try + { + object currentItem = null; + if (this.currencyManager.Position >= 0 && this.currencyManager.Position < this.currencyManager.List.Count ) + { + currentItem = this.currencyManager.Current; + } + + this.currencyManager.CancelCurrentEdit(); + + // vsw 531871: CurrencyManager no longer starts a new transaction automatically + // when we call CurrencyManager::CancelCurrentEdit. + // So, if the current item inside the currency manager did not change, we have to start a new transaction. + // (If the current item inside the currency manager changed, then the currency manager would have already started a new transaction). + IEditableObject editableObject = null; + if (this.currencyManager.Position >= 0 && this.currencyManager.Position < this.currencyManager.List.Count ) + { + editableObject = this.currencyManager.Current as IEditableObject; + } + + if (editableObject != null && currentItem == editableObject) + { + editableObject.BeginEdit(); + } + + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit] = false; + } + + if (addNewFinished) + { + this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] = true; + } + } + + internal void OnNewRowNeeded() + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = true; + try + { + AddNew(); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = false; + } + } + + internal void OnRowEnter(DataGridViewCellEventArgs e) + { + // don't change position or start a transaction in the middle of a meta data change + if (this.dataConnectionState[DATACONNECTIONSTATE_processingMetaDataChanges]) + { + return; + } + + // don't start a transaction on a suspended currency manager. + if (!this.currencyManager.ShouldBind) + { + return; + } + + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = true; + try + { + if (e.RowIndex != this.owner.NewRowIndex && + !this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheCurrencyManager] && + this.currencyManager.Position != e.RowIndex) // don't automatically force an EndCurrentEdit on the currency manager + { + try + { + this.currencyManager.Position = e.RowIndex; + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + DataGridViewCellCancelEventArgs dgvce = new DataGridViewCellCancelEventArgs(e.ColumnIndex, e.RowIndex); + ProcessException(exception, dgvce, false /*beginEdit*/); + } + + IEditableObject iEditObj = this.currencyManager.Current as IEditableObject; + if (iEditObj != null) + { + iEditObj.BeginEdit(); + } + } + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = false; + } + } + + internal void OnRowValidating(DataGridViewCellCancelEventArgs e) + { + // don't end the transaction on a suspended currency manager. + if (!this.currencyManager.ShouldBind) + { + return; + } + + if (!this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] && !this.owner.IsCurrentRowDirty) + { + if (!this.dataConnectionState[DATACONNECTIONSTATE_cancellingRowEdit]) + { + Debug.Assert(DataBoundRowsCount() == this.currencyManager.List.Count, "if the back end was changed while in AddNew the DGV should have updated its rows collection"); + // Cancel the current AddNew transaction + // doNotChangePositionInTheDataGridViewControl because we will change position + // when we get notification from the back end that the cancel operation was completed + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = true; + try + { + CancelRowEdit(false /*restoreRow*/, false /*addNewFinished*/); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_doNotChangePositionInTheDataGridViewControl] = false; + } + } + } + else if (this.owner.IsCurrentRowDirty) + { + this.dataConnectionState[DATACONNECTIONSTATE_rowValidatingInAddNew] = true; + + try + { + this.currencyManager.EndCurrentEdit(); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + ProcessException(exception, e, true /*beginEdit*/); + } + finally + { + this.dataConnectionState[DATACONNECTIONSTATE_rowValidatingInAddNew] = false; + } + } + + // we moved away from the 'add new row', so the 'add new row' has been committed in the back-end + // or has been rejected from the back-end. In any case, the AddNew operation completed. + this.dataConnectionState[DATACONNECTIONSTATE_finishedAddNew] = true; + } + + public void ProcessException(Exception exception, DataGridViewCellCancelEventArgs e, bool beginEdit) + { + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, e.ColumnIndex, + e.RowIndex, + // null, + // null, + DataGridViewDataErrorContexts.Commit); + this.owner.OnDataErrorInternal(dgvdee); + + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + else if (dgvdee.Cancel) + { + e.Cancel = true; + if (beginEdit) + { + IEditableObject iEditObj = this.currencyManager.Current as IEditableObject; + if (iEditObj != null) + { + iEditObj.BeginEdit(); + } + } + } + else + { + CancelRowEdit(false /*restoreRow*/, false /*finishedAddNew*/); + // interrupt current operation + } + } + + public bool PushValue(int boundColumnIndex, int columnIndex, int rowIndex, object value) + { + try + { + if (value != null) + { + Type valueType = value.GetType(); + Type columnType = this.owner.Columns[columnIndex].ValueType; + if (!columnType.IsAssignableFrom(valueType)) + { + // value needs to be converted before being fed to the back-end. + TypeConverter boundColumnConverter = BoundColumnConverter(boundColumnIndex); + if (boundColumnConverter != null && boundColumnConverter.CanConvertFrom(valueType)) + { + value = boundColumnConverter.ConvertFrom(value); + } + else + { + TypeConverter valueConverter = this.owner.GetCachedTypeConverter(valueType); + if (valueConverter != null && valueConverter.CanConvertTo(columnType)) + { + value = valueConverter.ConvertTo(value, columnType); + } + } + } + } + this.props[boundColumnIndex].SetValue(this.currencyManager[rowIndex], value); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + DataGridViewCellCancelEventArgs e = new DataGridViewCellCancelEventArgs(columnIndex, rowIndex); + ProcessException(exception, e, false); + return !e.Cancel; + } + return true; + } + + public bool ShouldChangeDataMember(object newDataSource) + { + if (!this.owner.Created) + { + // if the owner is not created yet then data member can be valid + return false; + } + + if (this.owner.BindingContext == null) + { + // if we don't have the BindingContext then the data member can still be valid + return false; + } + + if (newDataSource == null) + { + // we have the binding context and the new data source is null + // we should change the data member to "" + return true; + } + + CurrencyManager cm = this.owner.BindingContext[newDataSource] as CurrencyManager; + if (cm == null) + { + // if we don't have a currency manager then the data member can be valid + return false; + } + + PropertyDescriptorCollection props = cm.GetItemProperties(); + if (this.dataMember.Length != 0 && props[this.dataMember] != null) + { + // the data member is valid. Don't change it + return false; + } + + return true; + } + + public void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction) + { + Debug.Assert(dataGridViewColumn.IsDataBound && dataGridViewColumn.BoundColumnIndex != -1, "we need a bound column index to perform the sort"); + Debug.Assert(this.List is IBindingList, "you should have checked by now that we are bound to an IBindingList"); + ((IBindingList)this.List).ApplySort(this.props[dataGridViewColumn.BoundColumnIndex], direction); + } + + private void UnWireEvents() + { + if (this.currencyManager != null) + { + this.currencyManager.PositionChanged -= new EventHandler(currencyManager_PositionChanged); + this.currencyManager.ListChanged -= new ListChangedEventHandler(currencyManager_ListChanged); + this.dataConnectionState[DATACONNECTIONSTATE_interestedInRowEvents] = false; + } + } + + private void WireEvents() + { + if (this.currencyManager != null) + { + this.currencyManager.PositionChanged += new EventHandler(currencyManager_PositionChanged); + this.currencyManager.ListChanged += new ListChangedEventHandler(currencyManager_ListChanged); + this.dataConnectionState[DATACONNECTIONSTATE_interestedInRowEvents] = true; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorContexts.cs b/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorContexts.cs new file mode 100644 index 000000000..5cd75bfac --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorContexts.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + /// + [Flags] + public enum DataGridViewDataErrorContexts + { + /// + Formatting = 0x0001, + + /// + Display = 0x0002, + + /// + PreferredSize = 0x0004, + + /// + RowDeletion = 0x0008, + + /// + Parsing = 0x0100, + + /// + Commit = 0x0200, + + /// + InitialValueRestoration = 0x0400, + + /// + LeaveControl = 0x0800, + + /// + CurrentCellChange = 0x1000, + + /// + Scroll = 0x2000, + + /// + ClipboardContent = 0x4000 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorEventArgs.cs new file mode 100644 index 000000000..87d055e53 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewDataErrorEventArgs.cs @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Diagnostics; + using System.ComponentModel; + + /// + public class DataGridViewDataErrorEventArgs : DataGridViewCellCancelEventArgs + { + private Exception exception; + private bool throwException; + private DataGridViewDataErrorContexts context; + + /// + public DataGridViewDataErrorEventArgs(Exception exception, + int columnIndex, + int rowIndex, + DataGridViewDataErrorContexts context) : base(columnIndex, rowIndex) + { + Debug.Assert(rowIndex > -1); + this.exception = exception; + this.context = context; + } + + /// + public DataGridViewDataErrorContexts Context + { + get + { + return this.context; + } + } + + /// + public Exception Exception + { + get + { + return this.exception; + } + } + + /// + public bool ThrowException + { + get + { + return this.throwException; + } + set + { + if (value && this.exception == null) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CannotThrowNullException)); + } + this.throwException = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEditMode.cs b/WindowsForms/Managed/System/WinForms/DataGridViewEditMode.cs new file mode 100644 index 000000000..350dac1b7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEditMode.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewEditMode + { + /// + EditOnEnter = 0, + + /// + EditOnKeystroke, + + /// + EditOnKeystrokeOrF2, + + /// + EditOnF2, + + /// + EditProgrammatically + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEditingCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewEditingCell.cs new file mode 100644 index 000000000..be0d1c801 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEditingCell.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics.CodeAnalysis; + + /// + public interface IDataGridViewEditingCell + { + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object EditingCellFormattedValue + { + get; + set; + } + + /// + bool EditingCellValueChanged + { + get; + set; + } + + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object GetEditingCellFormattedValue(DataGridViewDataErrorContexts context); + + /// + void PrepareEditingCellForEdit(bool selectAll); + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs new file mode 100644 index 000000000..a2add5556 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security.Permissions; + + /// + public interface IDataGridViewEditingControl + { + /// + DataGridView EditingControlDataGridView + { + get; + set; + } + + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object EditingControlFormattedValue + { + get; + set; + } + + /// + int EditingControlRowIndex + { + get; + set; + } + + /// + bool EditingControlValueChanged + { + get; + set; + } + + /// + Cursor EditingPanelCursor + { + get; + } + + /// + bool RepositionEditingControlOnValueChange + { + get; + } + + /// + void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle); + + /// + bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey); + + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context); + + /// + void PrepareEditingControlForEdit(bool selectAll); + } + + /// + /// + /// Implements a custom AccessibleObject that fixes editing control's accessibility ancestor chain. + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class DataGridViewEditingControlAccessibleObject : Control.ControlAccessibleObject + { + public DataGridViewEditingControlAccessibleObject(Control ownerControl) : base(ownerControl) + { + Debug.Assert(ownerControl is IDataGridViewEditingControl, "ownerControl must implement IDataGridViewEditingControl"); + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level3) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.CurrentCell?.AccessibilityObject; + } + } + + internal override bool IsPatternSupported(int patternId) + { + if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_ExpandCollapsePatternId) + { + ComboBox ownerComboBoxControl = Owner as ComboBox; + if (ownerComboBoxControl != null) + { + return ownerComboBoxControl.DropDownStyle != ComboBoxStyle.Simple; + } + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) + { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState + { + get + { + ComboBox ownerComboBoxControl = Owner as ComboBox; + if (ownerComboBoxControl != null) + { + return ownerComboBoxControl.DroppedDown == true ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + + return base.ExpandCollapseState; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs.back b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs.back new file mode 100644 index 000000000..a2add5556 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControl.cs.back @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security.Permissions; + + /// + public interface IDataGridViewEditingControl + { + /// + DataGridView EditingControlDataGridView + { + get; + set; + } + + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object EditingControlFormattedValue + { + get; + set; + } + + /// + int EditingControlRowIndex + { + get; + set; + } + + /// + bool EditingControlValueChanged + { + get; + set; + } + + /// + Cursor EditingPanelCursor + { + get; + } + + /// + bool RepositionEditingControlOnValueChange + { + get; + } + + /// + void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle); + + /// + bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey); + + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context); + + /// + void PrepareEditingControlForEdit(bool selectAll); + } + + /// + /// + /// Implements a custom AccessibleObject that fixes editing control's accessibility ancestor chain. + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class DataGridViewEditingControlAccessibleObject : Control.ControlAccessibleObject + { + public DataGridViewEditingControlAccessibleObject(Control ownerControl) : base(ownerControl) + { + Debug.Assert(ownerControl is IDataGridViewEditingControl, "ownerControl must implement IDataGridViewEditingControl"); + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level3) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.CurrentCell?.AccessibilityObject; + } + } + + internal override bool IsPatternSupported(int patternId) + { + if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_ExpandCollapsePatternId) + { + ComboBox ownerComboBoxControl = Owner as ComboBox; + if (ownerComboBoxControl != null) + { + return ownerComboBoxControl.DropDownStyle != ComboBoxStyle.Simple; + } + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) + { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) + { + return IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState + { + get + { + ComboBox ownerComboBoxControl = Owner as ComboBox; + if (ownerComboBoxControl != null) + { + return ownerComboBoxControl.DroppedDown == true ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + + return base.ExpandCollapseState; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEditingControlShowingEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControlShowingEventArgs.cs new file mode 100644 index 000000000..3427f3614 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEditingControlShowingEventArgs.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Windows.Forms; + using System; + + /// + public class DataGridViewEditingControlShowingEventArgs : EventArgs + { + Control control = null; + DataGridViewCellStyle cellStyle; + + /// + public DataGridViewEditingControlShowingEventArgs(Control control, DataGridViewCellStyle cellStyle) + { + this.control = control; + this.cellStyle = cellStyle; + } + + /// + public DataGridViewCellStyle CellStyle + { + get + { + return this.cellStyle; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + this.cellStyle = value; + } + } + + /// + public Control Control + { + get + { + return this.control; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewElement.cs b/WindowsForms/Managed/System/WinForms/DataGridViewElement.cs new file mode 100644 index 000000000..1b36d6264 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewElement.cs @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Diagnostics; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Identifies an element in the dataGridView (base class for TCell, TBand, TRow, TColumn. + /// + public class DataGridViewElement + { + private DataGridViewElementStates state; // enabled frozen readOnly resizable selected visible + private DataGridView dataGridView; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DataGridViewElement() + { + this.state = DataGridViewElementStates.Visible; + } + + internal DataGridViewElement(DataGridViewElement dgveTemplate) + { + // Selected and Displayed states are not inherited + this.state = dgveTemplate.State & (DataGridViewElementStates.Frozen | DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Resizable | DataGridViewElementStates.ResizableSet | DataGridViewElementStates.Visible); + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual DataGridViewElementStates State + { + get + { + return this.state; + } + } + + internal DataGridViewElementStates StateInternal + { + set + { + this.state = value; + } + } + + internal bool StateIncludes(DataGridViewElementStates elementState) + { + return (this.State & elementState) == elementState; + } + + internal bool StateExcludes(DataGridViewElementStates elementState) + { + return (this.State & elementState) == 0; + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridView DataGridView + { + get + { + return this.dataGridView; + } + } + + internal DataGridView DataGridViewInternal + { + set + { + if (this.DataGridView != value) + { + this.dataGridView = value; + OnDataGridViewChanged(); + } + } + } + + /// + protected virtual void OnDataGridViewChanged() + { + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseCellClick(DataGridViewCellEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnCellClickInternal(e); + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseCellContentClick(DataGridViewCellEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnCellContentClickInternal(e); + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseCellContentDoubleClick(DataGridViewCellEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnCellContentDoubleClickInternal(e); + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseCellValueChanged(DataGridViewCellEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnCellValueChangedInternal(e); + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseDataError(DataGridViewDataErrorEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnDataErrorInternal(e); + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // Method raises an event for the grid control + protected void RaiseMouseWheel(MouseEventArgs e) + { + if (this.dataGridView != null) + { + this.dataGridView.OnMouseWheelInternal(e); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewElementStates.cs b/WindowsForms/Managed/System/WinForms/DataGridViewElementStates.cs new file mode 100644 index 000000000..941c8d265 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewElementStates.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + [ + Flags, + System.Runtime.InteropServices.ComVisible(true) + ] + public enum DataGridViewElementStates + { + /// + None = 0x0000, + /// + Displayed = 0x0001, + /// + Frozen = 0x0002, + /// + ReadOnly = 0x0004, + /// + Resizable = 0x0008, + /// + ResizableSet = 0x0010, + /// + Selected = 0x0020, + /// + Visible = 0x0040 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewEventHandlers.cs b/WindowsForms/Managed/System/WinForms/DataGridViewEventHandlers.cs new file mode 100644 index 000000000..9c1b468b3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewEventHandlers.cs @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics.CodeAnalysis; + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewAutoSizeColumnModeEventHandler(object sender, DataGridViewAutoSizeColumnModeEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewAutoSizeColumnsModeEventHandler(object sender, DataGridViewAutoSizeColumnsModeEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewAutoSizeModeEventHandler(object sender, DataGridViewAutoSizeModeEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewBindingCompleteEventHandler(object sender, DataGridViewBindingCompleteEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellCancelEventHandler(object sender, DataGridViewCellCancelEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellContextMenuStripNeededEventHandler(object sender, DataGridViewCellContextMenuStripNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellErrorTextNeededEventHandler(object sender, DataGridViewCellErrorTextNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellEventHandler(object sender, DataGridViewCellEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellFormattingEventHandler(object sender, DataGridViewCellFormattingEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellMouseEventHandler(object sender, DataGridViewCellMouseEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellPaintingEventHandler(object sender, DataGridViewCellPaintingEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellParsingEventHandler(object sender, DataGridViewCellParsingEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellStateChangedEventHandler(object sender, DataGridViewCellStateChangedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellStyleContentChangedEventHandler(object sender, DataGridViewCellStyleContentChangedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellToolTipTextNeededEventHandler(object sender, DataGridViewCellToolTipTextNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellValidatingEventHandler(object sender, DataGridViewCellValidatingEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewCellValueEventHandler(object sender, DataGridViewCellValueEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewColumnDividerDoubleClickEventHandler(object sender, DataGridViewColumnDividerDoubleClickEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewColumnEventHandler(object sender, DataGridViewColumnEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewColumnStateChangedEventHandler(object sender, DataGridViewColumnStateChangedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewEditingControlShowingEventHandler(object sender, DataGridViewEditingControlShowingEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewDataErrorEventHandler(object sender, DataGridViewDataErrorEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowCancelEventHandler(object sender, DataGridViewRowCancelEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowContextMenuStripNeededEventHandler(object sender, DataGridViewRowContextMenuStripNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowDividerDoubleClickEventHandler(object sender, DataGridViewRowDividerDoubleClickEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowEventHandler(object sender, DataGridViewRowEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowErrorTextNeededEventHandler(object sender, DataGridViewRowErrorTextNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowHeightInfoNeededEventHandler(object sender, DataGridViewRowHeightInfoNeededEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowHeightInfoPushedEventHandler(object sender, DataGridViewRowHeightInfoPushedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowPostPaintEventHandler(object sender, DataGridViewRowPostPaintEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowPrePaintEventHandler(object sender, DataGridViewRowPrePaintEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowsAddedEventHandler(object sender, DataGridViewRowsAddedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowsRemovedEventHandler(object sender, DataGridViewRowsRemovedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewRowStateChangedEventHandler(object sender, DataGridViewRowStateChangedEventArgs e); + + /// + [SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")] + public delegate void DataGridViewSortCompareEventHandler(object sender, DataGridViewSortCompareEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewFreeDimension.cs b/WindowsForms/Managed/System/WinForms/DataGridViewFreeDimension.cs new file mode 100644 index 000000000..c541ded3e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewFreeDimension.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + internal enum DataGridViewFreeDimension + { + Both, + Height, + Width + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewHeaderBorderStyle.cs b/WindowsForms/Managed/System/WinForms/DataGridViewHeaderBorderStyle.cs new file mode 100644 index 000000000..80b28bce8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewHeaderBorderStyle.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewHeaderBorderStyle + { + /// + Custom = 0, + + /// + Single = 1, + + /// + Raised = 2, + + /// + Sunken = 3, + + /// + None = 4 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewHeaderCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewHeaderCell.cs new file mode 100644 index 000000000..3b8898e19 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewHeaderCell.cs @@ -0,0 +1,704 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.VisualStyles; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + /// + /// Identifies a cell in the dataGridView. + /// + /// + public class DataGridViewHeaderCell : DataGridViewCell + { + private const byte DATAGRIDVIEWHEADERCELL_themeMargin = 100; // used to calculate the margins required for XP theming rendering + + private static Type defaultFormattedValueType = typeof(System.String); + private static Type defaultValueType = typeof(System.Object); + private static Type cellType = typeof(DataGridViewHeaderCell); + private static Rectangle rectThemeMargins = new Rectangle(-1, -1, 0, 0); + private static readonly int PropValueType = PropertyStore.CreateKey(); + private static readonly int PropButtonState = PropertyStore.CreateKey(); + private static readonly int PropFlipXPThemesBitmap = PropertyStore.CreateKey(); + private const string AEROTHEMEFILENAME = "Aero.msstyles"; + + /// + public DataGridViewHeaderCell() + { + } + + /// + protected ButtonState ButtonState + { + get + { + bool found; + int buttonState = this.Properties.GetInteger(PropButtonState, out found); + if (found) + { + return (ButtonState) buttonState; + } + return ButtonState.Normal; + } + } + + private ButtonState ButtonStatePrivate + { + [ + SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible") // Enum.IsDefined is OK here. Only specific flag combinations are allowed, and it's debug only anyways. + ] + set + { + Debug.Assert(Enum.IsDefined(typeof(ButtonState), value)); + if (this.ButtonState != value) + { + this.Properties.SetInteger(PropButtonState, (int) value); + } + } + } + + protected override void Dispose(bool disposing) { + if (FlipXPThemesBitmap != null && disposing) { + FlipXPThemesBitmap.Dispose(); + } + base.Dispose(disposing); + + } + + /// + [ + Browsable(false) + ] + public override bool Displayed + { + get + { + if (this.DataGridView == null || !this.DataGridView.Visible) + { + // No detached or invisible element is displayed. + return false; + } + + if (this.OwningRow != null) + { + // row header cell + return this.DataGridView.RowHeadersVisible && this.OwningRow.Displayed; + } + + if (this.OwningColumn != null) + { + // column header cell + return this.DataGridView.ColumnHeadersVisible && this.OwningColumn.Displayed; + } + + // top left header cell + Debug.Assert(!this.DataGridView.LayoutInfo.dirty); + return this.DataGridView.LayoutInfo.TopLeftHeader != Rectangle.Empty; + } + } + + internal Bitmap FlipXPThemesBitmap + { + get + { + return (Bitmap)this.Properties.GetObject(PropFlipXPThemesBitmap); + } + set + { + if (value != null || this.Properties.ContainsObject(PropFlipXPThemesBitmap)) + { + this.Properties.SetObject(PropFlipXPThemesBitmap, value); + } + } + } + + /// + public override Type FormattedValueType + { + get + { + return defaultFormattedValueType; + } + } + + /// + [ + Browsable(false) + ] + public override bool Frozen + { + get + { + if (this.OwningRow != null) + { + // row header cell + return this.OwningRow.Frozen; + } + + if (this.OwningColumn != null) + { + // column header cell + return this.OwningColumn.Frozen; + } + + if (this.DataGridView != null) + { + // top left header cell + return true; + } + + // detached header cell + return false; + } + } + + internal override bool HasValueType + { + get + { + return this.Properties.ContainsObject(PropValueType) && this.Properties.GetObject(PropValueType) != null; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool ReadOnly + { + get + { + return true; + } + set + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_HeaderCellReadOnlyProperty, "ReadOnly")); + } + } + + /// + [ + Browsable(false) + ] + public override bool Resizable + { + get + { + if (this.OwningRow != null) + { + // must be a row header cell + return (this.OwningRow.Resizable == DataGridViewTriState.True) || (this.DataGridView != null && this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing); + } + + if (this.OwningColumn != null) + { + // must be a column header cell + return (this.OwningColumn.Resizable == DataGridViewTriState.True) || + (this.DataGridView != null && this.DataGridView.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing); + } + + // must be the top left header cell + return this.DataGridView != null && (this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || this.DataGridView.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing); + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool Selected + { + get + { + return false; + } + set + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_HeaderCellReadOnlyProperty, "Selected")); + } + } + + /// + public override Type ValueType + { + get + { + Type valueType = (Type) this.Properties.GetObject(PropValueType); + if (valueType != null) + { + return valueType; + } + return defaultValueType; + } + set + { + if (value != null || this.Properties.ContainsObject(PropValueType)) + { + this.Properties.SetObject(PropValueType, value); + } + } + } + + /// + [ + Browsable(false) + ] + public override bool Visible + { + get + { + if (this.OwningRow != null) + { + // row header cell + return this.OwningRow.Visible && + (this.DataGridView == null || this.DataGridView.RowHeadersVisible); + } + + if (this.OwningColumn != null) + { + // column header cell + return this.OwningColumn.Visible && + (this.DataGridView == null || this.DataGridView.ColumnHeadersVisible); + } + + if (this.DataGridView != null) + { + // top left header cell + return this.DataGridView.RowHeadersVisible && this.DataGridView.ColumnHeadersVisible; + } + + return false; + } + } + + /// + public override object Clone() + { + DataGridViewHeaderCell dataGridViewCell; + Type thisType = this.GetType(); + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewHeaderCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewHeaderCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.Value = this.Value; + return dataGridViewCell; + } + + /// + public override ContextMenuStrip GetInheritedContextMenuStrip(int rowIndex) + { + ContextMenuStrip contextMenuStrip = GetContextMenuStrip(rowIndex); + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + + if (this.DataGridView != null) + { + return this.DataGridView.ContextMenuStrip; + } + else + { + return null; + } + } + + /// + public override DataGridViewElementStates GetInheritedState(int rowIndex) + { + DataGridViewElementStates state = DataGridViewElementStates.ResizableSet | DataGridViewElementStates.ReadOnly; + + if (this.OwningRow != null) + { + // row header cell + if ((this.DataGridView == null && rowIndex != -1) || + (this.DataGridView != null && (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count))) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + if (this.DataGridView != null && this.DataGridView.Rows.SharedRow(rowIndex) != this.OwningRow) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + state |= (this.OwningRow.GetState(rowIndex) & DataGridViewElementStates.Frozen); + if (this.OwningRow.GetResizable(rowIndex) == DataGridViewTriState.True || (this.DataGridView != null && this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)) + { + state |= DataGridViewElementStates.Resizable; + } + if (this.OwningRow.GetVisible(rowIndex) && (this.DataGridView == null || this.DataGridView.RowHeadersVisible)) + { + state |= DataGridViewElementStates.Visible; + if (this.OwningRow.GetDisplayed(rowIndex)) + { + state |= DataGridViewElementStates.Displayed; + } + } + } + else if (this.OwningColumn != null) + { + // column header cell + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + state |= (this.OwningColumn.State & DataGridViewElementStates.Frozen); + if (this.OwningColumn.Resizable == DataGridViewTriState.True || + (this.DataGridView != null && this.DataGridView.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)) + { + state |= DataGridViewElementStates.Resizable; + } + if (this.OwningColumn.Visible && (this.DataGridView == null || this.DataGridView.ColumnHeadersVisible)) + { + state |= DataGridViewElementStates.Visible; + if (this.OwningColumn.Displayed) + { + state |= DataGridViewElementStates.Displayed; + } + } + } + else if (this.DataGridView != null) + { + // top left header cell + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + state |= DataGridViewElementStates.Frozen; + if (this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || this.DataGridView.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + state |= DataGridViewElementStates.Resizable; + } + if (this.DataGridView.RowHeadersVisible && this.DataGridView.ColumnHeadersVisible) + { + state |= DataGridViewElementStates.Visible; + if (this.DataGridView.LayoutInfo.TopLeftHeader != Rectangle.Empty) + { + state |= DataGridViewElementStates.Displayed; + } + } + } + +#if DEBUG + if (this.OwningRow == null || this.OwningRow.Index != -1) + { + DataGridViewElementStates stateDebug = DataGridViewElementStates.ResizableSet; + if (this.Displayed) + { + stateDebug |= DataGridViewElementStates.Displayed; + } + if (this.Frozen) + { + stateDebug |= DataGridViewElementStates.Frozen; + } + if (this.ReadOnly) + { + stateDebug |= DataGridViewElementStates.ReadOnly; + } + if (this.Resizable) + { + stateDebug |= DataGridViewElementStates.Resizable; + } + if (this.Selected) + { + stateDebug |= DataGridViewElementStates.Selected; + } + if (this.Visible) + { + stateDebug |= DataGridViewElementStates.Visible; + } + Debug.Assert(state == stateDebug); + } +#endif + + return state; + } + + /// + protected override Size GetSize(int rowIndex) + { + if (this.DataGridView == null) + { + // detached cell + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return new Size(-1, -1); + } + if (this.OwningColumn != null) + { + // must be a column header cell + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return new Size(this.OwningColumn.Thickness, this.DataGridView.ColumnHeadersHeight); + } + else if (this.OwningRow != null) + { + // must be a row header cell + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.DataGridView.Rows.SharedRow(rowIndex) != this.OwningRow) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + return new Size(this.DataGridView.RowHeadersWidth, this.OwningRow.GetHeight(rowIndex)); + } + else + { + // must be the top left header cell + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return new Size(this.DataGridView.RowHeadersWidth, this.DataGridView.ColumnHeadersHeight); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal static Rectangle GetThemeMargins(Graphics g) + { + if (rectThemeMargins.X == -1) + { + Rectangle rectCell = new Rectangle(0, 0, DATAGRIDVIEWHEADERCELL_themeMargin, DATAGRIDVIEWHEADERCELL_themeMargin); + Rectangle rectContent = DataGridViewHeaderCellRenderer.VisualStyleRenderer.GetBackgroundContentRectangle(g, rectCell); + rectThemeMargins.X = rectContent.X; + rectThemeMargins.Y = rectContent.Y; + rectThemeMargins.Width = DATAGRIDVIEWHEADERCELL_themeMargin - rectContent.Right; + rectThemeMargins.Height = DATAGRIDVIEWHEADERCELL_themeMargin - rectContent.Bottom; + // On WinXP, the theming margins for a header are unexpectedly (3, 0, 0, 0) when you'd expect something like (0, 0, 2, 3) + if (rectThemeMargins.X == 3 && + rectThemeMargins.Y + rectThemeMargins.Width + rectThemeMargins.Height == 0) + { + rectThemeMargins = new Rectangle(0, 0, 2, 3); + } + else + { + // On Vista, the theming margins for a header are unexpectedly (0, 0, 0, 0) when you'd expect something like (2, 1, 0, 2) + // Padding themePadding = DataGridViewHeaderCellRenderer.VisualStyleRenderer.GetMargins(g, MarginProperty.ContentMargins); /* or MarginProperty.SizingMargins */ + // does not work either at this time. It AVs -So we hard code the margins for now. + try + { + string themeFilename = System.IO.Path.GetFileName(System.Windows.Forms.VisualStyles.VisualStyleInformation.ThemeFilename); + if (String.Equals(themeFilename, AEROTHEMEFILENAME, StringComparison.OrdinalIgnoreCase)) + { + rectThemeMargins = new Rectangle(2, 1, 0, 2); + } + } + catch + { + } + } + } + return rectThemeMargins; + } + + /// + protected override object GetValue(int rowIndex) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return this.Properties.GetObject(PropCellValue); + } + + /// + protected override bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left && this.DataGridView.ApplyVisualStylesToHeaderCells; + } + + /// + protected override bool MouseEnterUnsharesRow(int rowIndex) + { + return this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + rowIndex == this.DataGridView.MouseDownCellAddress.Y && + this.DataGridView.ApplyVisualStylesToHeaderCells; + } + + /// + protected override bool MouseLeaveUnsharesRow(int rowIndex) + { + return this.ButtonState != ButtonState.Normal && this.DataGridView.ApplyVisualStylesToHeaderCells; + } + + /// + protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return e.Button == MouseButtons.Left && this.DataGridView.ApplyVisualStylesToHeaderCells; + } + + /// + protected override void OnMouseDown(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left && + this.DataGridView.ApplyVisualStylesToHeaderCells && + !this.DataGridView.ResizingOperationAboutToStart) + { + UpdateButtonState(ButtonState.Pushed, e.RowIndex); + } + } + + /// + protected override void OnMouseEnter(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + if (this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X && + rowIndex == this.DataGridView.MouseDownCellAddress.Y && + this.ButtonState == ButtonState.Normal && + Control.MouseButtons == MouseButtons.Left && + !this.DataGridView.ResizingOperationAboutToStart) + { + UpdateButtonState(ButtonState.Pushed, rowIndex); + } + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + /// + protected override void OnMouseLeave(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.ButtonState == ButtonState.Pushed); + Debug.Assert(this.ColumnIndex == this.DataGridView.MouseDownCellAddress.X); + Debug.Assert(rowIndex == this.DataGridView.MouseDownCellAddress.Y); + UpdateButtonState(ButtonState.Normal, rowIndex); + } + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + } + + /// + protected override void OnMouseUp(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (e.Button == MouseButtons.Left && this.DataGridView.ApplyVisualStylesToHeaderCells) + { + UpdateButtonState(ButtonState.Normal, e.RowIndex); + } + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates dataGridViewElementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + if (DataGridViewCell.PaintBackground(paintParts)) + { + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + bool cellSelected = (dataGridViewElementState & DataGridViewElementStates.Selected) != 0; + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + if (br.Color.A == 255) + { + graphics.FillRectangle(br, valBounds); + } + } + } + + /// + /// + /// + /// Gets the row Index and column Index of the cell. + /// + /// + public override string ToString() + { + return "DataGridViewHeaderCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private void UpdateButtonState(ButtonState newButtonState, int rowIndex) + { + Debug.Assert(this.DataGridView != null); + this.ButtonStatePrivate = newButtonState; + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + + private class DataGridViewHeaderCellRenderer + { + private static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewHeaderCellRenderer() + { + } + + public static VisualStyleRenderer VisualStyleRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Normal); + } + + return visualStyleRenderer; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewHitTestCloseEdge.cs b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestCloseEdge.cs new file mode 100644 index 000000000..16872c290 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestCloseEdge.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + internal enum DataGridViewHitTestTypeCloseEdge + { + /// + None = 0, + + /// + Left = 1, + + /// + Right = 2, + + /// + Top = 3, + + /// + Bottom = 4 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewHitTestInfo.cs b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestInfo.cs new file mode 100644 index 000000000..fadbaa1ee --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestInfo.cs @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Globalization; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + /// + public sealed class HitTestInfo + { + internal DataGridViewHitTestType type = DataGridViewHitTestType.None; + //internal DataGridViewHitTestTypeCloseEdge edge = DataGridViewHitTestTypeCloseEdge.None; + internal DataGridViewHitTestTypeInternal typeInternal = DataGridViewHitTestTypeInternal.None; + internal int row; + internal int col; + internal int adjacentRow; + internal int adjacentCol; + internal int mouseBarOffset; + internal int rowStart; + internal int colStart; + + /// + /// + /// Allows the object to inform you the + /// extent of the grid. + /// + public static readonly HitTestInfo Nowhere = new HitTestInfo(); + + internal HitTestInfo() + { + this.type = DataGridViewHitTestType.None; + this.typeInternal = DataGridViewHitTestTypeInternal.None; + //this.edge = DataGridViewHitTestTypeCloseEdge.None; + this.row = this.col = -1; + this.rowStart = this.colStart = -1; + this.adjacentRow = this.adjacentCol = -1; + } + + /// + /// + /// Gets the number of the clicked column. + /// + public int ColumnIndex + { + get + { + return this.col; + } + } + + /// + /// + /// Gets the + /// number of the clicked row. + /// + public int RowIndex + { + get + { + return this.row; + } + } + + /// + /// + /// Gets the left edge of the column. + /// + public int ColumnX + { + get + { + return this.colStart; + } + } + + /// + /// + /// Gets the top edge of the row. + /// + public int RowY + { + get + { + return this.rowStart; + } + } + + /// + /// + /// Gets the part of the control, other than the row or column, that was + /// clicked. + /// + public DataGridViewHitTestType Type + { + get + { + return this.type; + } + } + + /// + /// + /// Indicates whether two objects are identical. + /// + public override bool Equals(object value) + { + HitTestInfo hti = value as HitTestInfo; + if (hti != null) + { + return (this.type == hti.type && + this.row == hti.row && + this.col == hti.col); + } + return false; + } + + /// + /// + /// Gets the hash code for the instance. + /// + public override int GetHashCode() + { + return WindowsFormsUtils.GetCombinedHashCodes((int) this.type, + this.row, + this.col); + } + + /// + /// + /// Gets the type, column number and row number. + /// + public override string ToString() + { + return "{ Type:" + type.ToString() + ", Column:" + col.ToString(CultureInfo.CurrentCulture) + ", Row:" + row.ToString(CultureInfo.CurrentCulture) + " }"; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewHitTestType.cs b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestType.cs new file mode 100644 index 000000000..77841fdfb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewHitTestType.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// Specifies the part of the control where the mouse is. + /// + public enum DataGridViewHitTestType + { + /// + None = 0, + + /// + Cell = 1, + + /// + ColumnHeader = 2, + + /// + RowHeader = 3, + + /// + TopLeftHeader = 4, + + /// + HorizontalScrollBar = 5, + + /// + VerticalScrollBar = 6 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewImageCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewImageCell.cs new file mode 100644 index 000000000..a73200c39 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewImageCell.cs @@ -0,0 +1,1098 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Imaging; + using System.Drawing.Drawing2D; + using System.Diagnostics; + using System.Security.Permissions; + using System.Globalization; + using System.Runtime.Versioning; + + /// + public class DataGridViewImageCell : DataGridViewCell + { + private static ColorMap[] colorMap = new ColorMap[] { new ColorMap() }; + private static readonly int PropImageCellDescription = PropertyStore.CreateKey(); + private static readonly int PropImageCellLayout = PropertyStore.CreateKey(); + private static Type defaultTypeImage = typeof(System.Drawing.Image); + private static Type defaultTypeIcon = typeof(System.Drawing.Icon); + private static Type cellType = typeof(DataGridViewImageCell); + private static Bitmap errorBmp = null; + private static Icon errorIco = null; + + private const byte DATAGRIDVIEWIMAGECELL_valueIsIcon = 0x01; + + private byte flags; // see DATAGRIDVIEWIMAGECELL_ consts above + + /// + public DataGridViewImageCell() : this(false /*valueIsIcon*/) + { + } + + /// + public DataGridViewImageCell(bool valueIsIcon) + { + if (valueIsIcon) + { + this.flags = DATAGRIDVIEWIMAGECELL_valueIsIcon; + } + } + + /// + public override object DefaultNewRowValue + { + get + { + if (defaultTypeImage.IsAssignableFrom(this.ValueType)) + { + return ErrorBitmap; + } + else if (defaultTypeIcon.IsAssignableFrom(this.ValueType)) + { + return ErrorIcon; + } + else + { + return null; + } + } + } + + /// + [ + DefaultValue("") + ] + public string Description + { + get + { + object description = this.Properties.GetObject(PropImageCellDescription); + if (description != null) + { + return (string) description; + } + return string.Empty; + } + + set + { + if (!string.IsNullOrEmpty(value) || this.Properties.ContainsObject(PropImageCellDescription)) + { + this.Properties.SetObject(PropImageCellDescription, value); + } + } + } + + /// + public override Type EditType + { + get + { + // Image cells don't have an editor + return null; + } + } + + static internal Bitmap ErrorBitmap + { + get + { + if (errorBmp == null) + { + errorBmp = new Bitmap(typeof(DataGridView), "ImageInError.bmp"); + } + return errorBmp; + } + } + + static internal Icon ErrorIcon + { + get + { + if (errorIco == null) + { + errorIco = new Icon(typeof(DataGridView), "IconInError.ico"); + } + return errorIco; + } + } + + /// + public override Type FormattedValueType + { + get + { + if (this.ValueIsIcon) + { + return defaultTypeIcon; + } + else + { + return defaultTypeImage; + } + } + } + + /// + [ + DefaultValue(DataGridViewImageCellLayout.NotSet) + ] + public DataGridViewImageCellLayout ImageLayout + { + get + { + bool found; + int imageLayout = this.Properties.GetInteger(PropImageCellLayout, out found); + if (found) + { + return (DataGridViewImageCellLayout)imageLayout; + } + return DataGridViewImageCellLayout.Normal; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridViewImageCellLayout.NotSet, (int)DataGridViewImageCellLayout.Zoom)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridViewImageCellLayout)); + } + if (this.ImageLayout != value) + { + this.Properties.SetInteger(PropImageCellLayout, (int)value); + OnCommonChange(); + } + } + } + + internal DataGridViewImageCellLayout ImageLayoutInternal + { + set + { + Debug.Assert(value >= DataGridViewImageCellLayout.NotSet && value <= DataGridViewImageCellLayout.Zoom); + if (this.ImageLayout != value) + { + this.Properties.SetInteger(PropImageCellLayout, (int)value); + } + } + } + + /// + [ + DefaultValue(false) + ] + public bool ValueIsIcon + { + get + { + return ((this.flags & DATAGRIDVIEWIMAGECELL_valueIsIcon) != 0x00); + } + set + { + if (this.ValueIsIcon != value) + { + this.ValueIsIconInternal = value; + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal bool ValueIsIconInternal + { + set + { + if (this.ValueIsIcon != value) + { + if (value) + { + this.flags |= (byte) DATAGRIDVIEWIMAGECELL_valueIsIcon; + } + else + { + this.flags = (byte) (this.flags & ~DATAGRIDVIEWIMAGECELL_valueIsIcon); + } + if (this.DataGridView != null && + this.RowIndex != -1 && + this.DataGridView.NewRowIndex == this.RowIndex && + !this.DataGridView.VirtualMode) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + // We automatically update the content of the new row's cell based on the new ValueIsIcon value. + if ((value && this.Value == ErrorBitmap) || (!value && this.Value == ErrorIcon)) + { + this.Value = this.DefaultNewRowValue; + } + } + } + } + } + + /// + public override Type ValueType + { + get + { + Type baseValueType = base.ValueType; + + if (baseValueType != null) + { + return baseValueType; + } + + if (this.ValueIsIcon) + { + return defaultTypeIcon; + } + else + { + return defaultTypeImage; + } + } + set + { + base.ValueType = value; + this.ValueIsIcon = (value != null && defaultTypeIcon.IsAssignableFrom(value)); + } + } + + /// + public override object Clone() + { + DataGridViewImageCell dataGridViewCell; + Type thisType = this.GetType(); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewImageCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewImageCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.ValueIsIconInternal = this.ValueIsIcon; + dataGridViewCell.Description = this.Description; + dataGridViewCell.ImageLayoutInternal = this.ImageLayout; + return dataGridViewCell; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewImageCellAccessibleObject(this); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle imgBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + null /*errorText*/, // imgBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle imgBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(imgBoundsDebug.Equals(imgBounds)); +#endif + + return imgBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle errBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(errBoundsDebug.Equals(errBounds)); +#endif + + return errBounds; + } + + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + protected override object GetFormattedValue(object value, + int rowIndex, + ref DataGridViewCellStyle cellStyle, + TypeConverter valueTypeConverter, + TypeConverter formattedValueTypeConverter, + DataGridViewDataErrorContexts context) + { + if ((context & DataGridViewDataErrorContexts.ClipboardContent) != 0) + { + return this.Description; + } + + object formattedValue = base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context); + if (formattedValue == null && cellStyle.NullValue == null) + { + return null; + } + if (this.ValueIsIcon) + { + Icon ico = formattedValue as Icon; + if (ico == null) + { + ico = ErrorIcon; + } + return ico; + } + else + { + Image img = formattedValue as Image; + if (img == null) + { + img = ErrorBitmap; + } + return img; + } + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Size preferredSize; + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + object formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize); + Image img = formattedValue as Image; + Icon ico = null; + if (img == null) + { + ico = formattedValue as Icon; + } + + if (freeDimension == DataGridViewFreeDimension.Height && + this.ImageLayout == DataGridViewImageCellLayout.Zoom) + { + if (img != null || ico != null) + { + if (img != null) + { + int imgWidthAllowed = constraintSize.Width - borderAndPaddingWidths; + if (imgWidthAllowed <= 0 || img.Width == 0) + { + preferredSize = Size.Empty; + } + else + { + preferredSize = new Size(0, Math.Min(img.Height, Decimal.ToInt32((decimal)img.Height * imgWidthAllowed / img.Width))); + } + } + else + { + int icoWidthAllowed = constraintSize.Width - borderAndPaddingWidths; + if (icoWidthAllowed <= 0 || ico.Width == 0) + { + preferredSize = Size.Empty; + } + else + { + preferredSize = new Size(0, Math.Min(ico.Height, Decimal.ToInt32((decimal)ico.Height * icoWidthAllowed / ico.Width))); + } + } + } + else + { + preferredSize = new Size(0, 1); + } + } + else if (freeDimension == DataGridViewFreeDimension.Width && + this.ImageLayout == DataGridViewImageCellLayout.Zoom) + { + if (img != null || ico != null) + { + if (img != null) + { + int imgHeightAllowed = constraintSize.Height - borderAndPaddingHeights; + if (imgHeightAllowed <= 0 || img.Height == 0) + { + preferredSize = Size.Empty; + } + else + { + preferredSize = new Size(Math.Min(img.Width, Decimal.ToInt32((decimal)img.Width * imgHeightAllowed / img.Height)), 0); + } + } + else + { + int icoHeightAllowed = constraintSize.Height - borderAndPaddingHeights; + if (icoHeightAllowed <= 0 || ico.Height == 0) + { + preferredSize = Size.Empty; + } + else + { + preferredSize = new Size(Math.Min(ico.Width, Decimal.ToInt32((decimal)ico.Width * icoHeightAllowed / ico.Height)), 0); + } + } + } + else + { + preferredSize = new Size(1, 0); + } + } + else + { + if (img != null) + { + preferredSize = new Size(img.Width, img.Height); + } + else if (ico != null) + { + preferredSize = new Size(ico.Width, ico.Height); + } + else + { + preferredSize = new Size(1, 1); + } + if (freeDimension == DataGridViewFreeDimension.Height) + { + preferredSize.Width = 0; + } + else if (freeDimension == DataGridViewFreeDimension.Width) + { + preferredSize.Height = 0; + } + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += borderAndPaddingWidths; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Width = Math.Max(preferredSize.Width, borderAndPaddingWidths + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height += borderAndPaddingHeights; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Height = Math.Max(preferredSize.Height, borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + /// + protected override object GetValue(int rowIndex) + { + object valueBase = base.GetValue(rowIndex); + if (valueBase == null) + { + DataGridViewImageColumn owningImageColumn = this.OwningColumn as DataGridViewImageColumn; + if (owningImageColumn != null) + { + if (defaultTypeImage.IsAssignableFrom(this.ValueType)) + { + Image image = owningImageColumn.Image; + if (image != null) + { + return image; + } + } + else if (defaultTypeIcon.IsAssignableFrom(this.ValueType)) + { + Icon icon = owningImageColumn.Icon; + if (icon != null) + { + return icon; + } + } + } + } + return valueBase; + } + + private Rectangle ImgBounds(Rectangle bounds, int imgWidth, int imgHeight, DataGridViewImageCellLayout imageLayout, DataGridViewCellStyle cellStyle) + { + // when the imageLayout == stretch there is nothing to do + Debug.Assert(imageLayout != DataGridViewImageCellLayout.Stretch); + + Rectangle imgBounds = Rectangle.Empty; + + switch (imageLayout) + { + case DataGridViewImageCellLayout.Normal: + case DataGridViewImageCellLayout.NotSet: + imgBounds = new Rectangle(bounds.X, bounds.Y, imgWidth, imgHeight); + break; + case DataGridViewImageCellLayout.Zoom: + // we have to determine which side will be fully filled: the height or the width + if (imgWidth * bounds.Height < imgHeight * bounds.Width) + { + // we fill the height + imgBounds = new Rectangle(bounds.X, bounds.Y, Decimal.ToInt32((decimal)imgWidth * bounds.Height / imgHeight), bounds.Height); + } + else + { + // we fill the width + imgBounds = new Rectangle(bounds.X, bounds.Y, bounds.Width, Decimal.ToInt32((decimal)imgHeight * bounds.Width / imgWidth)); + } + break; + default: + break; + } + + // now use the alignment on the cellStyle to determine the final bounds + if (this.DataGridView.RightToLeftInternal) + { + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopRight: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.TopLeft: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + case DataGridViewContentAlignment.MiddleRight: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.MiddleLeft: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + case DataGridViewContentAlignment.BottomRight: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.BottomLeft: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + } + } + else + { + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopLeft: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.TopRight: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + case DataGridViewContentAlignment.MiddleLeft: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.MiddleRight: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + case DataGridViewContentAlignment.BottomLeft: + imgBounds.X = bounds.X; + break; + case DataGridViewContentAlignment.BottomRight: + imgBounds.X = bounds.Right - imgBounds.Width; + break; + } + } + + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.BottomCenter: + imgBounds.X = bounds.X + (bounds.Width - imgBounds.Width) / 2; + break; + } + + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopLeft: + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.TopRight: + imgBounds.Y = bounds.Y; + break; + + case DataGridViewContentAlignment.MiddleLeft: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.MiddleRight: + imgBounds.Y = bounds.Y + (bounds.Height - imgBounds.Height) / 2; + break; + + case DataGridViewContentAlignment.BottomLeft: + case DataGridViewContentAlignment.BottomCenter: + case DataGridViewContentAlignment.BottomRight: + imgBounds.Y = bounds.Bottom - imgBounds.Height; + break; + + default: + Debug.Assert(cellStyle.Alignment == DataGridViewContentAlignment.NotSet, "this is the only alignment left"); + break; + } + return imgBounds; + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + elementState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates elementState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle resultBounds; + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + if (valBounds.Width > 0 && valBounds.Height > 0 && (paint || computeContentBounds)) + { + Rectangle imgBounds = valBounds; + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + imgBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + imgBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + imgBounds.Width -= cellStyle.Padding.Horizontal; + imgBounds.Height -= cellStyle.Padding.Vertical; + } + + bool cellSelected = (elementState & DataGridViewElementStates.Selected) != 0; + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + + if (imgBounds.Width > 0 && imgBounds.Height > 0) + { + Image img = formattedValue as Image; + Icon ico = null; + if (img == null) + { + ico = formattedValue as Icon; + } + if (ico != null || img != null) + { + DataGridViewImageCellLayout imageLayout = this.ImageLayout; + if (imageLayout == DataGridViewImageCellLayout.NotSet) + { + if (this.OwningColumn is DataGridViewImageColumn) + { + imageLayout = ((DataGridViewImageColumn) this.OwningColumn).ImageLayout; + Debug.Assert(imageLayout != DataGridViewImageCellLayout.NotSet); + } + else + { + imageLayout = DataGridViewImageCellLayout.Normal; + } + } + + if (imageLayout == DataGridViewImageCellLayout.Stretch) + { + if (paint) + { + if (DataGridViewCell.PaintBackground(paintParts)) + { + DataGridViewCell.PaintPadding(g, valBounds, cellStyle, br, this.DataGridView.RightToLeftInternal); + } + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if (img != null) + { + // bug 21949: Graphics.DrawImage does not treat well scaled images + // we have to pass an ImageAttribute + ImageAttributes attr = new ImageAttributes(); + + attr.SetWrapMode(WrapMode.TileFlipXY); + g.DrawImage(img, imgBounds, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attr); + attr.Dispose(); + } + else + { + g.DrawIcon(ico, imgBounds); + } + } + } + + resultBounds = imgBounds; + } + else + { + Rectangle imgBounds2 = ImgBounds(imgBounds, (img == null) ? ico.Width : img.Width, (img == null) ? ico.Height : img.Height, imageLayout, cellStyle); + resultBounds = imgBounds2; + + if (paint) + { + if (DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds); + } + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + //paint the image + Region reg = g.Clip; + g.SetClip(Rectangle.Intersect(Rectangle.Intersect(imgBounds2, imgBounds), Rectangle.Truncate(g.VisibleClipBounds))); + if (img != null) + { + g.DrawImage(img, imgBounds2); + } + else + { + g.DrawIconUnstretched(ico, imgBounds2); + } + g.Clip = reg; + } + } + } + } + else + { + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds); + } + resultBounds = Rectangle.Empty; + } + } + else + { + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds); + } + resultBounds = Rectangle.Empty; + } + + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (paint && + DataGridViewCell.PaintFocus(paintParts) && + ptCurrentCell.X == this.ColumnIndex && + ptCurrentCell.Y == rowIndex && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused) + { + // Draw focus rectangle + ControlPaint.DrawFocusRectangle(g, valBounds, Color.Empty, br.Color); + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, valBounds, errorText); + } + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(valBounds); + } + else + { + resultBounds = Rectangle.Empty; + } + } + else + { + Debug.Assert(valBounds.Height <= 0 || valBounds.Width <= 0); + resultBounds = Rectangle.Empty; + } + + return resultBounds; + } + + /// + public override string ToString() + { + return "DataGridViewImageCell { ColumnIndex=" + ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + /// + protected class DataGridViewImageCellAccessibleObject : DataGridViewCellAccessibleObject + { + + /// + public DataGridViewImageCellAccessibleObject(DataGridViewCell owner) : base (owner) + { + } + + /// + public override string DefaultAction + { + get + { + return String.Empty; + } + } + + /// + public override string Description + { + get + { + DataGridViewImageCell imageCell = this.Owner as DataGridViewImageCell; + if (imageCell != null) + { + return imageCell.Description; + } + else + { + return null; + } + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return base.Value; + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set + { + // do nothing. + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + // do nothing if Level < 3 + + if (AccessibilityImprovements.Level3) + { + DataGridViewImageCell dataGridViewCell = (DataGridViewImageCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView != null && dataGridViewCell.RowIndex != -1 && + dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningRow != null) + { + dataGridView.OnCellContentClickInternal(new DataGridViewCellEventArgs(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex)); + } + } + } + + /// + public override int GetChildCount() + { + return 0; + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) + { + return NativeMethods.UIA_ImageControlTypeId; + } + + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsInvokePatternAvailablePropertyId) + { + return true; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (AccessibilityImprovements.Level3 && patternId == NativeMethods.UIA_InvokePatternId) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewImageCellLayout.cs b/WindowsForms/Managed/System/WinForms/DataGridViewImageCellLayout.cs new file mode 100644 index 000000000..beb7e1b0c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewImageCellLayout.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public enum DataGridViewImageCellLayout + { + /// + NotSet = 0, + + /// + Normal, + + /// + Stretch, + + /// + Zoom + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewImageColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewImageColumn.cs new file mode 100644 index 000000000..e3d7c212f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewImageColumn.cs @@ -0,0 +1,357 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.Drawing; + using System.Diagnostics; + using System.ComponentModel; + using System.Windows.Forms; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + [ToolboxBitmapAttribute(typeof(DataGridViewImageColumn), "DataGridViewImageColumn.bmp")] + public class DataGridViewImageColumn : DataGridViewColumn + { + private static Type columnType = typeof(DataGridViewImageColumn); + private Image image; + private Icon icon; + + /// + public DataGridViewImageColumn() : this(false /*valuesAreIcons*/) + { + } + + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Can't think of a workaround. + ] + public DataGridViewImageColumn(bool valuesAreIcons) + : base(new DataGridViewImageCell(valuesAreIcons)) + { + DataGridViewCellStyle defaultCellStyle = new DataGridViewCellStyle(); + defaultCellStyle.AlignmentInternal = DataGridViewContentAlignment.MiddleCenter; + if (valuesAreIcons) + { + defaultCellStyle.NullValue = DataGridViewImageCell.ErrorIcon; + } + else + { + defaultCellStyle.NullValue = DataGridViewImageCell.ErrorBitmap; + } + this.DefaultCellStyle = defaultCellStyle; + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + if (value != null && !(value is System.Windows.Forms.DataGridViewImageCell)) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewImageCell")); + } + base.CellTemplate = value; + } + } + + /// + [ + Browsable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_ColumnDefaultCellStyleDescr) + ] + public override DataGridViewCellStyle DefaultCellStyle + { + get + { + return base.DefaultCellStyle; + } + set + { + base.DefaultCellStyle = value; + } + } + + /// + [ + Browsable(true), + DefaultValue(""), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridViewImageColumn_DescriptionDescr) + ] + public string Description + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ImageCellTemplate.Description; + } + set + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + this.ImageCellTemplate.Description = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewImageCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewImageCell; + if (dataGridViewCell != null) + { + dataGridViewCell.Description = value; + } + } + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public Icon Icon + { + get + { + return this.icon; + } + set + { + this.icon = value; + if (this.DataGridView != null) + { + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridViewImageColumn_ImageDescr) + ] + public Image Image + { + get + { + return this.image; + } + set + { + this.image = value; + if (this.DataGridView != null) + { + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + + private DataGridViewImageCell ImageCellTemplate + { + get + { + return (DataGridViewImageCell) this.CellTemplate; + } + } + + /// + [ + DefaultValue(DataGridViewImageCellLayout.Normal), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridViewImageColumn_ImageLayoutDescr) + ] + public DataGridViewImageCellLayout ImageLayout + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + DataGridViewImageCellLayout imageLayout = this.ImageCellTemplate.ImageLayout; + if (imageLayout == DataGridViewImageCellLayout.NotSet) + { + imageLayout = DataGridViewImageCellLayout.Normal; + } + return imageLayout; + } + set + { + if (this.ImageLayout != value) + { + this.ImageCellTemplate.ImageLayout = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewImageCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewImageCell; + if (dataGridViewCell != null) + { + dataGridViewCell.ImageLayoutInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool ValuesAreIcons + { + get + { + if (this.ImageCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.ImageCellTemplate.ValueIsIcon; + } + set + { + if (this.ValuesAreIcons != value) + { + this.ImageCellTemplate.ValueIsIconInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewImageCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewImageCell; + if (dataGridViewCell != null) + { + dataGridViewCell.ValueIsIconInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + + if (value && + this.DefaultCellStyle.NullValue is Bitmap && + (Bitmap) this.DefaultCellStyle.NullValue == DataGridViewImageCell.ErrorBitmap) + { + this.DefaultCellStyle.NullValue = DataGridViewImageCell.ErrorIcon; + } + else if (!value && + this.DefaultCellStyle.NullValue is Icon && + (Icon) this.DefaultCellStyle.NullValue == DataGridViewImageCell.ErrorIcon) + { + this.DefaultCellStyle.NullValue = DataGridViewImageCell.ErrorBitmap; + } + } + } + } + + /// + public override object Clone() + { + DataGridViewImageColumn dataGridViewColumn; + Type thisType = this.GetType(); + + if (thisType == columnType) //performance improvement + { + dataGridViewColumn = new DataGridViewImageColumn(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewColumn = (DataGridViewImageColumn)System.Activator.CreateInstance(thisType); + } + if (dataGridViewColumn != null) + { + base.CloneInternal(dataGridViewColumn); + dataGridViewColumn.Icon = this.icon; + dataGridViewColumn.Image = this.image; + } + return dataGridViewColumn; + } + + private bool ShouldSerializeDefaultCellStyle() + { + DataGridViewImageCell templateCell = this.CellTemplate as DataGridViewImageCell; + if (templateCell == null) + { + Debug.Fail("we can't compute the default cell style w/o a template cell"); + return true; + } + + if (!this.HasDefaultCellStyle) + { + return false; + } + + object defaultNullValue; + if (templateCell.ValueIsIcon) + { + defaultNullValue = DataGridViewImageCell.ErrorIcon; + } + else + { + defaultNullValue = DataGridViewImageCell.ErrorBitmap; + } + + DataGridViewCellStyle defaultCellStyle = this.DefaultCellStyle; + + return (!defaultCellStyle.BackColor.IsEmpty || + !defaultCellStyle.ForeColor.IsEmpty || + !defaultCellStyle.SelectionBackColor.IsEmpty || + !defaultCellStyle.SelectionForeColor.IsEmpty || + defaultCellStyle.Font != null || + !defaultNullValue.Equals(defaultCellStyle.NullValue) || + !defaultCellStyle.IsDataSourceNullValueDefault || + !String.IsNullOrEmpty(defaultCellStyle.Format) || + !defaultCellStyle.FormatProvider.Equals(System.Globalization.CultureInfo.CurrentCulture) || + defaultCellStyle.Alignment != DataGridViewContentAlignment.MiddleCenter || + defaultCellStyle.WrapMode != DataGridViewTriState.NotSet || + defaultCellStyle.Tag != null || + !defaultCellStyle.Padding.Equals(Padding.Empty)); + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewImageColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewIntLinkedList.cs b/WindowsForms/Managed/System/WinForms/DataGridViewIntLinkedList.cs new file mode 100644 index 000000000..1aa444e90 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewIntLinkedList.cs @@ -0,0 +1,314 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + + /// + /// + /// Represents a linked list of integers + /// + internal class DataGridViewIntLinkedList : IEnumerable + { + private DataGridViewIntLinkedListElement lastAccessedElement; + private DataGridViewIntLinkedListElement headElement; + private int count, lastAccessedIndex; + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return new DataGridViewIntLinkedListEnumerator(this.headElement); + } + + /// + public DataGridViewIntLinkedList() + { + lastAccessedIndex = -1; + } + + /// + public DataGridViewIntLinkedList(DataGridViewIntLinkedList source) + { + Debug.Assert(source != null); + int elements = source.Count; + for (int element = 0; element < elements; element++) + { + Add(source[element]); + } + } + + /// + public int this[int index] + { + get + { + Debug.Assert(index >= 0); + Debug.Assert(index < this.count); + if (this.lastAccessedIndex == -1 || index < this.lastAccessedIndex) + { + DataGridViewIntLinkedListElement tmp = this.headElement; + int tmpIndex = index; + while (tmpIndex > 0) + { + tmp = tmp.Next; + tmpIndex--; + } + this.lastAccessedElement = tmp; + this.lastAccessedIndex = index; + return tmp.Int; + } + else + { + while (this.lastAccessedIndex < index) + { + this.lastAccessedElement = this.lastAccessedElement.Next; + this.lastAccessedIndex++; + } + return this.lastAccessedElement.Int; + } + } + set + { + Debug.Assert(index >= 0); + if (index != this.lastAccessedIndex) + { + int currentInt = this[index]; + Debug.Assert(index == this.lastAccessedIndex); + } + this.lastAccessedElement.Int = value; + } + } + + /// + public int Count + { + get + { + return this.count; + } + } + + /// + public int HeadInt + { + get + { + Debug.Assert(this.headElement != null); + return this.headElement.Int; + } + } + + /// + public void Add(int integer) + { + DataGridViewIntLinkedListElement newHead = new DataGridViewIntLinkedListElement(integer); + if (this.headElement != null) + { + newHead.Next = this.headElement; + } + this.headElement = newHead; + this.count++; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + } + + /// + public void Clear() + { + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + this.headElement = null; + this.count = 0; + } + + /// + public bool Contains(int integer) + { + int index = 0; + DataGridViewIntLinkedListElement tmp = this.headElement; + while (tmp != null) + { + if (tmp.Int == integer) + { + this.lastAccessedElement = tmp; + this.lastAccessedIndex = index; + return true; + } + tmp = tmp.Next; + index++; + } + return false; + } + + /// + public int IndexOf(int integer) + { + if (Contains(integer)) + { + return this.lastAccessedIndex; + } + else + { + return -1; + } + } + + /// + public bool Remove(int integer) + { + DataGridViewIntLinkedListElement tmp1 = null, tmp2 = this.headElement; + while (tmp2 != null) + { + if (tmp2.Int == integer) + { + break; + } + tmp1 = tmp2; + tmp2 = tmp2.Next; + } + if (tmp2.Int == integer) + { + DataGridViewIntLinkedListElement tmp3 = tmp2.Next; + if (tmp1 == null) + { + this.headElement = tmp3; + } + else + { + tmp1.Next = tmp3; + } + this.count--; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + return true; + } + return false; + } + + /// + public void RemoveAt(int index) + { + DataGridViewIntLinkedListElement tmp1 = null, tmp2 = this.headElement; + while (index > 0) + { + tmp1 = tmp2; + tmp2 = tmp2.Next; + index--; + } + DataGridViewIntLinkedListElement tmp3 = tmp2.Next; + if (tmp1 == null) + { + this.headElement = tmp3; + } + else + { + tmp1.Next = tmp3; + } + this.count--; + this.lastAccessedElement = null; + this.lastAccessedIndex = -1; + } + } + + /// + /// + /// Represents an emunerator of elements in a linked list. + /// + internal class DataGridViewIntLinkedListEnumerator : IEnumerator + { + private DataGridViewIntLinkedListElement headElement; + private DataGridViewIntLinkedListElement current; + private bool reset; + + /// + public DataGridViewIntLinkedListEnumerator(DataGridViewIntLinkedListElement headElement) + { + this.headElement = headElement; + this.reset = true; + } + + /// + object IEnumerator.Current + { + get + { + Debug.Assert(this.current != null); // Since this is for internal use only. + return this.current.Int; + } + } + + /// + bool IEnumerator.MoveNext() + { + if (this.reset) + { + Debug.Assert(this.current == null); + this.current = this.headElement; + this.reset = false; + } + else + { + Debug.Assert(this.current != null); // Since this is for internal use only. + this.current = this.current.Next; + } + return (this.current != null); + } + + /// + void IEnumerator.Reset() + { + this.reset = true; + this.current = null; + } + } + + /// + /// + /// Represents an element in a linked list. + /// + internal class DataGridViewIntLinkedListElement + { + private int integer; + private DataGridViewIntLinkedListElement next; + + /// + public DataGridViewIntLinkedListElement(int integer) + { + this.integer = integer; + } + + /// + public int Int + { + get + { + return this.integer; + } + set + { + this.integer = value; + } + } + + /// + public DataGridViewIntLinkedListElement Next + { + get + { + return this.next; + } + set + { + this.next = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewLayoutData.cs b/WindowsForms/Managed/System/WinForms/DataGridViewLayoutData.cs new file mode 100644 index 000000000..8459ebf99 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewLayoutData.cs @@ -0,0 +1,94 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System.Drawing; +using System.Text; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + internal class LayoutData + { + internal bool dirty = true; + + // used for resizing. + public Rectangle ClientRectangle = Rectangle.Empty; + + // region inside the dataGridView's borders. + public Rectangle Inside = Rectangle.Empty; + + // region occupied by row headers + public Rectangle RowHeaders = Rectangle.Empty; + + // region occupied by column headers + public Rectangle ColumnHeaders = Rectangle.Empty; + + // top left header cell + public Rectangle TopLeftHeader = Rectangle.Empty; + + // region for the cells + public Rectangle Data = Rectangle.Empty; + + // square connecting the two scrollbars + public Rectangle ResizeBoxRect = Rectangle.Empty; + + public bool ColumnHeadersVisible; + public bool RowHeadersVisible; + + public LayoutData() + { + } + + public LayoutData(LayoutData src) + { + this.ClientRectangle = src.ClientRectangle; + this.TopLeftHeader = src.TopLeftHeader; + this.ColumnHeaders = src.ColumnHeaders; + this.RowHeaders = src.RowHeaders; + this.Inside = src.Inside; + this.Data = src.Data; + this.ResizeBoxRect = src.ResizeBoxRect; + this.ColumnHeadersVisible = src.ColumnHeadersVisible; + this.RowHeadersVisible = src.RowHeadersVisible; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(100); + sb.Append(base.ToString()); + sb.Append(" { \n"); + sb.Append("ClientRectangle = "); + sb.Append(ClientRectangle.ToString()); + sb.Append('\n'); + sb.Append("Inside = "); + sb.Append(Inside.ToString()); + sb.Append('\n'); + sb.Append("TopLeftHeader = "); + sb.Append(TopLeftHeader.ToString()); + sb.Append('\n'); + sb.Append("ColumnHeaders = "); + sb.Append(ColumnHeaders.ToString()); + sb.Append('\n'); + sb.Append("RowHeaders = "); + sb.Append(RowHeaders.ToString()); + sb.Append('\n'); + sb.Append("Data = "); + sb.Append(Data.ToString()); + sb.Append('\n'); + sb.Append("ResizeBoxRect = "); + sb.Append(ResizeBoxRect.ToString()); + sb.Append('\n'); + sb.Append("ColumnHeadersVisible = "); + sb.Append(ColumnHeadersVisible.ToString()); + sb.Append('\n'); + sb.Append("RowHeadersVisible = "); + sb.Append(RowHeadersVisible.ToString()); + sb.Append(" }"); + return sb.ToString(); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewLinkCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewLinkCell.cs new file mode 100644 index 000000000..ddf53b3c7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewLinkCell.cs @@ -0,0 +1,1235 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.ComponentModel; + using System.Windows.Forms.Internal; + using System.Security.Permissions; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using Runtime.CompilerServices; + + /// + public class DataGridViewLinkCell : DataGridViewCell + { + private static readonly DataGridViewContentAlignment anyLeft = DataGridViewContentAlignment.TopLeft | DataGridViewContentAlignment.MiddleLeft | DataGridViewContentAlignment.BottomLeft; + private static readonly DataGridViewContentAlignment anyRight = DataGridViewContentAlignment.TopRight | DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.BottomRight; + private static readonly DataGridViewContentAlignment anyBottom = DataGridViewContentAlignment.BottomRight | DataGridViewContentAlignment.BottomCenter | DataGridViewContentAlignment.BottomLeft; + + private static Type defaultFormattedValueType = typeof(System.String); + private static Type defaultValueType = typeof(System.Object); + private static Type cellType = typeof(DataGridViewLinkCell); + + private static readonly int PropLinkCellActiveLinkColor = PropertyStore.CreateKey(); + private static readonly int PropLinkCellLinkBehavior = PropertyStore.CreateKey(); + private static readonly int PropLinkCellLinkColor = PropertyStore.CreateKey(); + private static readonly int PropLinkCellLinkState = PropertyStore.CreateKey(); + private static readonly int PropLinkCellTrackVisitedState = PropertyStore.CreateKey(); + private static readonly int PropLinkCellUseColumnTextForLinkValue = PropertyStore.CreateKey(); + private static readonly int PropLinkCellVisitedLinkColor = PropertyStore.CreateKey(); + + private const byte DATAGRIDVIEWLINKCELL_horizontalTextMarginLeft = 1; + private const byte DATAGRIDVIEWLINKCELL_horizontalTextMarginRight = 2; + private const byte DATAGRIDVIEWLINKCELL_verticalTextMarginTop = 1; + private const byte DATAGRIDVIEWLINKCELL_verticalTextMarginBottom = 1; + + // we cache LinkVisited because it will be set multiple times + private bool linkVisited = false; + private bool linkVisitedSet = false; + + private static Cursor dataGridViewCursor = null; + + /// + public DataGridViewLinkCell() + { + } + + /// + public Color ActiveLinkColor + { + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // unboxing OK here. + ] + get + { + if (this.Properties.ContainsObject(PropLinkCellActiveLinkColor)) + { + return (Color)this.Properties.GetObject(PropLinkCellActiveLinkColor); + } + else if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return this.HighContrastLinkColor; + } + else + { + // return the default IE Color + return LinkUtilities.IEActiveLinkColor; + } + } + set + { + if (!value.Equals(this.ActiveLinkColor)) + { + this.Properties.SetObject(PropLinkCellActiveLinkColor, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal Color ActiveLinkColorInternal + { + set + { + if (!value.Equals(this.ActiveLinkColor)) + { + this.Properties.SetObject(PropLinkCellActiveLinkColor, value); + } + } + } + + private bool ShouldSerializeActiveLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.ActiveLinkColor.Equals(SystemColors.HotTrack); + } + + return !this.ActiveLinkColor.Equals(LinkUtilities.IEActiveLinkColor); + } + + /// + public override Type EditType + { + get + { + // links can't switch to edit mode + return null; + } + } + + /// + public override Type FormattedValueType + { + get + { + return defaultFormattedValueType; + } + } + + /// + [DefaultValue(LinkBehavior.SystemDefault)] + public LinkBehavior LinkBehavior + { + get + { + bool found; + int linkBehavior = this.Properties.GetInteger(PropLinkCellLinkBehavior, out found); + if (found) + { + return (LinkBehavior)linkBehavior; + } + return LinkBehavior.SystemDefault; + } + set + { + // Sequential enum. Valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)LinkBehavior.SystemDefault, (int)LinkBehavior.NeverUnderline)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(LinkBehavior)); + } + if (value != this.LinkBehavior) + { + this.Properties.SetInteger(PropLinkCellLinkBehavior, (int)value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal LinkBehavior LinkBehaviorInternal + { + set + { + Debug.Assert(value >= LinkBehavior.SystemDefault && value <= LinkBehavior.NeverUnderline); + if (value != this.LinkBehavior) + { + this.Properties.SetInteger(PropLinkCellLinkBehavior, (int)value); + } + } + } + + /// + public Color LinkColor + { + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // unboxing OK here. + ] + get + { + if (this.Properties.ContainsObject(PropLinkCellLinkColor)) + { + return (Color)this.Properties.GetObject(PropLinkCellLinkColor); + } + else if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return this.HighContrastLinkColor; + } + else + { + // return the default IE Color + return LinkUtilities.IELinkColor; + } + } + set + { + if (!value.Equals(this.LinkColor)) + { + this.Properties.SetObject(PropLinkCellLinkColor, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal Color LinkColorInternal + { + set + { + if (!value.Equals(this.LinkColor)) + { + this.Properties.SetObject(PropLinkCellLinkColor, value); + } + } + } + + private bool ShouldSerializeLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.LinkColor.Equals(SystemColors.HotTrack); + } + + return !this.LinkColor.Equals(LinkUtilities.IELinkColor); + } + + private LinkState LinkState + { + get + { + bool found; + int linkState = this.Properties.GetInteger(PropLinkCellLinkState, out found); + if (found) + { + return (LinkState) linkState; + } + return LinkState.Normal; + } + set + { + if (this.LinkState != value) + { + this.Properties.SetInteger(PropLinkCellLinkState, (int) value); + } + } + } + + /// + public bool LinkVisited + { + get + { + if (this.linkVisitedSet) + { + return this.linkVisited; + } + + // the default is false + return false; + } + set + { + this.linkVisitedSet = true; + if (value != this.LinkVisited) + { + this.linkVisited = value; + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + private bool ShouldSerializeLinkVisited() + { + return this.linkVisitedSet = true; + } + + /// + [DefaultValue(true)] + public bool TrackVisitedState + { + get + { + bool found; + int trackVisitedState = this.Properties.GetInteger(PropLinkCellTrackVisitedState, out found); + if (found) + { + return trackVisitedState == 0 ? false : true; + } + return true; + } + set + { + if (value != this.TrackVisitedState) + { + this.Properties.SetInteger(PropLinkCellTrackVisitedState, value ? 1 : 0); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal bool TrackVisitedStateInternal + { + set + { + if (value != this.TrackVisitedState) + { + this.Properties.SetInteger(PropLinkCellTrackVisitedState, value ? 1 : 0); + } + } + } + + /// + [DefaultValue(false)] + public bool UseColumnTextForLinkValue + { + get + { + bool found; + int useColumnTextForLinkValue = this.Properties.GetInteger(PropLinkCellUseColumnTextForLinkValue, out found); + if (found) + { + return useColumnTextForLinkValue == 0 ? false : true; + } + return false; + } + set + { + if (value != this.UseColumnTextForLinkValue) + { + this.Properties.SetInteger(PropLinkCellUseColumnTextForLinkValue, value ? 1 : 0); + OnCommonChange(); + } + } + } + + internal bool UseColumnTextForLinkValueInternal + { + set + { + // Caller is responsible for invalidation + if (value != this.UseColumnTextForLinkValue) + { + this.Properties.SetInteger(PropLinkCellUseColumnTextForLinkValue, value ? 1 : 0); + } + } + } + + /// + public Color VisitedLinkColor + { + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // unboxing OK here. + ] + get + { + if (this.Properties.ContainsObject(PropLinkCellVisitedLinkColor)) + { + return (Color)this.Properties.GetObject(PropLinkCellVisitedLinkColor); + } + else if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return this.Selected ? SystemColors.HighlightText : LinkUtilities.GetVisitedLinkColor(); + } + else + { + // return the default IE Color + return LinkUtilities.IEVisitedLinkColor; + } + } + set + { + if (!value.Equals(this.VisitedLinkColor)) + { + this.Properties.SetObject(PropLinkCellVisitedLinkColor, value); + if (this.DataGridView != null) + { + if (this.RowIndex != -1) + { + this.DataGridView.InvalidateCell(this); + } + else + { + this.DataGridView.InvalidateColumnInternal(this.ColumnIndex); + } + } + } + } + } + + internal Color VisitedLinkColorInternal + { + set + { + if (!value.Equals(this.VisitedLinkColor)) + { + this.Properties.SetObject(PropLinkCellVisitedLinkColor, value); + } + } + } + + private bool ShouldSerializeVisitedLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.VisitedLinkColor.Equals(SystemColors.HotTrack); + } + + return !this.VisitedLinkColor.Equals(LinkUtilities.IEVisitedLinkColor); + } + + private Color HighContrastLinkColor + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // Selected cells have SystemColors.Highlight as a background. + // SystemColors.HighlightText is supposed to be in contrast with SystemColors.Highlight. + return this.Selected ? SystemColors.HighlightText : SystemColors.HotTrack; + } + } + + /// + public override Type ValueType + { + get + { + Type valueType = base.ValueType; + if (valueType != null) + { + return valueType; + } + return defaultValueType; + } + } + + /// + public override object Clone() + { + DataGridViewLinkCell dataGridViewCell; + Type thisType = this.GetType (); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewLinkCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewLinkCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + + if (this.Properties.ContainsObject(PropLinkCellActiveLinkColor)) + { + dataGridViewCell.ActiveLinkColorInternal = this.ActiveLinkColor; + } + + if (this.Properties.ContainsInteger(PropLinkCellUseColumnTextForLinkValue)) + { + dataGridViewCell.UseColumnTextForLinkValueInternal = this.UseColumnTextForLinkValue; + } + + if (this.Properties.ContainsInteger(PropLinkCellLinkBehavior)) + { + dataGridViewCell.LinkBehaviorInternal = this.LinkBehavior; + } + + if (this.Properties.ContainsObject(PropLinkCellLinkColor)) + { + dataGridViewCell.LinkColorInternal = this.LinkColor; + } + + if (this.Properties.ContainsInteger(PropLinkCellTrackVisitedState)) + { + dataGridViewCell.TrackVisitedStateInternal = this.TrackVisitedState; + } + + if (this.Properties.ContainsObject(PropLinkCellVisitedLinkColor)) + { + dataGridViewCell.VisitedLinkColorInternal = this.VisitedLinkColor; + } + + if (this.linkVisitedSet) + { + dataGridViewCell.LinkVisited = this.LinkVisited; + } + + return dataGridViewCell; + } + + private bool LinkBoundsContainPoint(int x, int y, int rowIndex) + { + Rectangle linkBounds = GetContentBounds(rowIndex); + + return linkBounds.Contains(x, y); + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewLinkCellAccessibleObject(this); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = this.GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle linkBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + null /*errorText*/, // linkBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle linkBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(linkBoundsDebug.Equals(linkBounds)); +#endif + + return linkBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = this.GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errorIconBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + + return errorIconBounds; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Size preferredSize; + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + object formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize); + string formattedString = formattedValue as string; + if (string.IsNullOrEmpty(formattedString)) + { + formattedString = " "; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (cellStyle.WrapMode == DataGridViewTriState.True && formattedString.Length > 1) + { + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + int maxHeight = constraintSize.Height - borderAndPaddingHeights - DATAGRIDVIEWLINKCELL_verticalTextMarginTop - DATAGRIDVIEWLINKCELL_verticalTextMarginBottom; + if ((cellStyle.Alignment & anyBottom) != 0) + { + maxHeight--; + } + preferredSize = new Size(DataGridViewCell.MeasureTextWidth(graphics, + formattedString, + cellStyle.Font, + Math.Max(1, maxHeight), + flags), + 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextHeight(graphics, + formattedString, + cellStyle.Font, + Math.Max(1, constraintSize.Width - borderAndPaddingWidths - DATAGRIDVIEWLINKCELL_horizontalTextMarginLeft - DATAGRIDVIEWLINKCELL_horizontalTextMarginRight), + flags)); + break; + } + default: + { + preferredSize = DataGridViewCell.MeasureTextPreferredSize(graphics, + formattedString, + cellStyle.Font, + 5.0F, + flags); + break; + } + } + } + else + { + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags).Width, + 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags).Height); + break; + } + default: + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags); + break; + } + } + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += DATAGRIDVIEWLINKCELL_horizontalTextMarginLeft + DATAGRIDVIEWLINKCELL_horizontalTextMarginRight + borderAndPaddingWidths; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Width = Math.Max(preferredSize.Width, borderAndPaddingWidths + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + preferredSize.Height += DATAGRIDVIEWLINKCELL_verticalTextMarginTop + DATAGRIDVIEWLINKCELL_verticalTextMarginBottom + borderAndPaddingHeights; + if ((cellStyle.Alignment & anyBottom) != 0) + { + preferredSize.Height += DATAGRIDVIEWLINKCELL_verticalTextMarginBottom; + } + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Height = Math.Max(preferredSize.Height, borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + /// + protected override object GetValue(int rowIndex) + { + if (this.UseColumnTextForLinkValue && + this.DataGridView != null && + this.DataGridView.NewRowIndex != rowIndex && + this.OwningColumn != null && + this.OwningColumn is DataGridViewLinkColumn) + { + return ((DataGridViewLinkColumn) this.OwningColumn).Text; + } + return base.GetValue(rowIndex); + } + + /// + protected override bool KeyUpUnsharesRow(KeyEventArgs e, int rowIndex) + { + if (e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift) + { + return this.TrackVisitedState && !this.LinkVisited; + } + else + { + return true; + } + } + + /// + protected override bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return LinkBoundsContainPoint(e.X, e.Y, e.RowIndex); + } + + /// + protected override bool MouseLeaveUnsharesRow(int rowIndex) + { + return this.LinkState != LinkState.Normal; + } + + /// + protected override bool MouseMoveUnsharesRow(DataGridViewCellMouseEventArgs e) + { + if (LinkBoundsContainPoint(e.X, e.Y, e.RowIndex)) + { + if ((this.LinkState & LinkState.Hover) == 0) + { + return true; + } + } + else + { + if ((this.LinkState & LinkState.Hover) != 0) + { + return true; + } + } + return false; + } + + /// + protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e) + { + return this.TrackVisitedState && LinkBoundsContainPoint(e.X, e.Y, e.RowIndex); + } + + /// + protected override void OnKeyUp(KeyEventArgs e, int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (e.KeyCode == Keys.Space && !e.Alt && !e.Control && !e.Shift) + { + RaiseCellClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + if (this.DataGridView != null && + this.ColumnIndex < this.DataGridView.Columns.Count && + rowIndex < this.DataGridView.Rows.Count) + { + RaiseCellContentClick(new DataGridViewCellEventArgs(this.ColumnIndex, rowIndex)); + if (this.TrackVisitedState) + { + this.LinkVisited = true; + } + } + e.Handled = true; + } + } + + /// + protected override void OnMouseDown(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (LinkBoundsContainPoint(e.X, e.Y, e.RowIndex)) + { + this.LinkState |= LinkState.Active; + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + + base.OnMouseDown(e); + } + + /// + protected override void OnMouseLeave(int rowIndex) + { + if (this.DataGridView == null) + { + return; + } + if (dataGridViewCursor != null) + { + this.DataGridView.Cursor = dataGridViewCursor; + dataGridViewCursor = null; + } + if (this.LinkState != LinkState.Normal) + { + this.LinkState = LinkState.Normal; + this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex); + } + + base.OnMouseLeave(rowIndex); + } + + /// + protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (LinkBoundsContainPoint(e.X, e.Y, e.RowIndex)) + { + if ((this.LinkState & LinkState.Hover) == 0) + { + this.LinkState |= LinkState.Hover; + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + + if (dataGridViewCursor == null) + { + dataGridViewCursor = this.DataGridView.UserSetCursor; + } + + if (this.DataGridView.Cursor != Cursors.Hand) + { + this.DataGridView.Cursor = Cursors.Hand; + } + } + else + { + if ((this.LinkState & LinkState.Hover) != 0) + { + this.LinkState &= ~LinkState.Hover; + this.DataGridView.Cursor = dataGridViewCursor; + this.DataGridView.InvalidateCell(this.ColumnIndex, e.RowIndex); + } + } + + base.OnMouseMove(e); + } + + /// + protected override void OnMouseUp(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + if (LinkBoundsContainPoint(e.X, e.Y, e.RowIndex) && this.TrackVisitedState) + { + this.LinkVisited = true; + } + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics g, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(g, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle resultBounds = Rectangle.Empty; + + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + Rectangle valBounds = cellBounds; + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + bool cellCurrent = ptCurrentCell.X == this.ColumnIndex && ptCurrentCell.Y == rowIndex; + bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0; + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + g.FillRectangle(br, valBounds); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + Rectangle errorBounds = valBounds; + string formattedValueStr = formattedValue as string; + + if (formattedValueStr != null && (paint || computeContentBounds)) + { + // Font independent margins + valBounds.Offset(DATAGRIDVIEWLINKCELL_horizontalTextMarginLeft, DATAGRIDVIEWLINKCELL_verticalTextMarginTop); + valBounds.Width -= DATAGRIDVIEWLINKCELL_horizontalTextMarginLeft + DATAGRIDVIEWLINKCELL_horizontalTextMarginRight; + valBounds.Height -= DATAGRIDVIEWLINKCELL_verticalTextMarginTop + DATAGRIDVIEWLINKCELL_verticalTextMarginBottom; + if ((cellStyle.Alignment & anyBottom) != 0) + { + valBounds.Height -= DATAGRIDVIEWLINKCELL_verticalTextMarginBottom; + } + + Font linkFont = null; + Font hoverFont = null; + LinkUtilities.EnsureLinkFonts(cellStyle.Font, this.LinkBehavior, ref linkFont, ref hoverFont); + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + // paint the focus rectangle around the link + if (paint) + { + if (valBounds.Width > 0 && valBounds.Height > 0) + { + if (cellCurrent && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + DataGridViewCell.PaintFocus(paintParts)) + { + Rectangle focusBounds = DataGridViewUtilities.GetTextBounds(valBounds, + formattedValueStr, + flags, + cellStyle, + this.LinkState == LinkState.Hover ? hoverFont : linkFont); + if ((cellStyle.Alignment & anyLeft) != 0) + { + focusBounds.X--; + focusBounds.Width++; + } + else if ((cellStyle.Alignment & anyRight) != 0) + { + focusBounds.X++; + focusBounds.Width++; + } + focusBounds.Height += 2; + ControlPaint.DrawFocusRectangle(g, focusBounds, Color.Empty, br.Color); + } + Color linkColor; + if ((this.LinkState & LinkState.Active) == LinkState.Active) + { + linkColor = this.ActiveLinkColor; + } + else if (this.LinkVisited) + { + linkColor = this.VisitedLinkColor; + } + else + { + linkColor = this.LinkColor; + } + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + TextRenderer.DrawText(g, + formattedValueStr, + this.LinkState == LinkState.Hover ? hoverFont : linkFont, + valBounds, + linkColor, + flags); + } + } + else if (cellCurrent && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + DataGridViewCell.PaintFocus(paintParts) && + errorBounds.Width > 0 && + errorBounds.Height > 0) + { + // Draw focus rectangle + ControlPaint.DrawFocusRectangle(g, errorBounds, Color.Empty, br.Color); + } + } + else + { + Debug.Assert(computeContentBounds); + resultBounds = DataGridViewUtilities.GetTextBounds(valBounds, + formattedValueStr, + flags, + cellStyle, + this.LinkState == LinkState.Hover ? hoverFont : linkFont); + } + linkFont.Dispose(); + hoverFont.Dispose(); + } + else if (paint || computeContentBounds) + { + if (cellCurrent && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + DataGridViewCell.PaintFocus(paintParts) && + paint && + valBounds.Width > 0 && + valBounds.Height > 0) + { + // Draw focus rectangle + ControlPaint.DrawFocusRectangle(g, valBounds, Color.Empty, br.Color); + } + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + + return resultBounds; + } + + /// + public override string ToString() + { + return "DataGridViewLinkCell { ColumnIndex=" + ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + /// + protected class DataGridViewLinkCellAccessibleObject : DataGridViewCellAccessibleObject + { + + /// + public DataGridViewLinkCellAccessibleObject(DataGridViewCell owner) : base (owner) + { + } + + /// + public override string DefaultAction + { + get + { + return SR.GetString(SR.DataGridView_AccLinkCellDefaultAction); + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + DataGridViewLinkCell dataGridViewCell = (DataGridViewLinkCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView != null && dataGridViewCell.RowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + } + + if (dataGridViewCell.OwningColumn != null && dataGridViewCell.OwningRow != null) + { + dataGridView.OnCellContentClickInternal(new DataGridViewCellEventArgs(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex)); + } + } + + /// + public override int GetChildCount() + { + return 0; + } + + internal override bool IsIAccessibleExSupported() + { + if (AccessibilityImprovements.Level2) + { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) + { + return NativeMethods.UIA_HyperlinkControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewLinkColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewLinkColumn.cs new file mode 100644 index 000000000..e203ec067 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewLinkColumn.cs @@ -0,0 +1,397 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms; + using System.Globalization; + + /// + [ToolboxBitmapAttribute(typeof(DataGridViewLinkColumn), "DataGridViewLinkColumn.bmp")] + public class DataGridViewLinkColumn : DataGridViewColumn + { + private static Type columnType = typeof(DataGridViewLinkColumn); + + private string text; + + /// + public DataGridViewLinkColumn() : base(new DataGridViewLinkCell()) + { + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_LinkColumnActiveLinkColorDescr) + ] + public Color ActiveLinkColor + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).ActiveLinkColor; + } + set + { + if (!this.ActiveLinkColor.Equals(value)) + { + ((DataGridViewLinkCell)this.CellTemplate).ActiveLinkColorInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.ActiveLinkColorInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + private bool ShouldSerializeActiveLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.ActiveLinkColor.Equals(SystemColors.HotTrack); + } + + return !this.ActiveLinkColor.Equals(LinkUtilities.IEActiveLinkColor); + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + if (value != null && !(value is System.Windows.Forms.DataGridViewLinkCell)) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewLinkCell")); + } + base.CellTemplate = value; + } + } + + /// + [ + DefaultValue(LinkBehavior.SystemDefault), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_LinkColumnLinkBehaviorDescr) + ] + public LinkBehavior LinkBehavior + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).LinkBehavior; + } + set + { + if (!this.LinkBehavior.Equals(value)) + { + ((DataGridViewLinkCell)this.CellTemplate).LinkBehavior = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.LinkBehaviorInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_LinkColumnLinkColorDescr) + ] + public Color LinkColor + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).LinkColor; + } + set + { + if (!this.LinkColor.Equals(value)) + { + ((DataGridViewLinkCell)this.CellTemplate).LinkColorInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.LinkColorInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + private bool ShouldSerializeLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.LinkColor.Equals(SystemColors.HotTrack); + } + + return !this.LinkColor.Equals(LinkUtilities.IELinkColor); + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_LinkColumnTextDescr) + ] + public string Text + { + get + { + return this.text; + } + set + { + if (!string.Equals(value, this.text, StringComparison.Ordinal)) + { + this.text = value; + if (this.DataGridView != null) + { + if (this.UseColumnTextForLinkValue) + { + this.DataGridView.OnColumnCommonChange(this.Index); + } + else + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null && dataGridViewCell.UseColumnTextForLinkValue) + { + this.DataGridView.OnColumnCommonChange(this.Index); + return; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + } + + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_LinkColumnTrackVisitedStateDescr) + ] + public bool TrackVisitedState + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).TrackVisitedState; + } + set + { + if (this.TrackVisitedState != value) + { + ((DataGridViewLinkCell)this.CellTemplate).TrackVisitedStateInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.TrackVisitedStateInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_LinkColumnUseColumnTextForLinkValueDescr) + ] + public bool UseColumnTextForLinkValue + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).UseColumnTextForLinkValue; + } + set + { + if (this.UseColumnTextForLinkValue != value) + { + ((DataGridViewLinkCell)this.CellTemplate).UseColumnTextForLinkValueInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.UseColumnTextForLinkValueInternal = value; + } + } + this.DataGridView.OnColumnCommonChange(this.Index); + } + } + } + } + + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_LinkColumnVisitedLinkColorDescr) + ] + public Color VisitedLinkColor + { + get + { + if (this.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return ((DataGridViewLinkCell)this.CellTemplate).VisitedLinkColor; + } + set + { + if (!this.VisitedLinkColor.Equals(value)) + { + ((DataGridViewLinkCell)this.CellTemplate).VisitedLinkColorInternal = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewLinkCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewLinkCell; + if (dataGridViewCell != null) + { + dataGridViewCell.VisitedLinkColorInternal = value; + } + } + this.DataGridView.InvalidateColumn(this.Index); + } + } + } + } + + private bool ShouldSerializeVisitedLinkColor() + { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level2) + { + return !this.VisitedLinkColor.Equals(SystemColors.HotTrack); + } + + return !this.VisitedLinkColor.Equals(LinkUtilities.IEVisitedLinkColor); + } + + /// + public override object Clone() + { + DataGridViewLinkColumn dataGridViewColumn; + Type thisType = this.GetType(); + + if (thisType == columnType) //performance improvement + { + dataGridViewColumn = new DataGridViewLinkColumn(); + } + else + { + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + dataGridViewColumn = (DataGridViewLinkColumn)System.Activator.CreateInstance(thisType); + } + if (dataGridViewColumn != null) + { + base.CloneInternal(dataGridViewColumn); + dataGridViewColumn.Text = this.text; + } + return dataGridViewColumn; + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewLinkColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs b/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs new file mode 100644 index 000000000..78c957f6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs @@ -0,0 +1,29998 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System.Collections; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Drawing; + using System.Windows.Forms.ComponentModel; + using System.Globalization; + using System.Diagnostics; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.Layout; + using Microsoft.Win32; + using System.Collections.Specialized; + using System.Windows.Forms.Internal; + using System.Runtime.Versioning; + using Runtime.CompilerServices; + + /// + public partial class DataGridView + { + /// + protected virtual void AccessibilityNotifyCurrentCellChanged(Point cellAddress) + { + if (cellAddress.X < 0 || cellAddress.X >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("cellAddress"); + } + + if (cellAddress.Y < 0 || cellAddress.Y >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("cellAddress"); + } + + int visibleRowIndex = this.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, cellAddress.Y); + int visibleColumnIndex = this.Columns.ColumnIndexToActualDisplayIndex(cellAddress.X, DataGridViewElementStates.Visible); + + int topHeaderRowIncrement = this.ColumnHeadersVisible ? 1 : 0; + int rowHeaderIncrement = this.RowHeadersVisible ? 1 : 0; + + int objectID = visibleRowIndex + topHeaderRowIncrement // + 1 because the top header row acc obj is at index 0 + + 1; // + 1 because objectID's need to be positive and non-zero + + int childID = visibleColumnIndex + rowHeaderIncrement; // + 1 because the column header cell is at index 0 in top header row acc obj + // same thing for the row header cell in the data grid view row acc obj + + if (this.ContainsFocus) + { + this.AccessibilityNotifyClients(AccessibleEvents.Focus, objectID, childID); + + if (AccessibilityImprovements.Level3) + { + CurrentCell?.AccessibilityObject.SetFocus(); + } + + } + this.AccessibilityNotifyClients(AccessibleEvents.Selection, objectID, childID); + } + + internal void ActivateToolTip(bool activate, string toolTipText, int columnIndex, int rowIndex) + { + this.toolTipCaption = toolTipText; + this.ptToolTipCell = new Point(columnIndex, rowIndex); + this.toolTipControl.Activate(activate); + } + + internal void AddNewRow(bool createdByEditing) + { + Debug.Assert(this.Columns.Count > 0); + Debug.Assert(this.newRowIndex == -1); + + this.Rows.AddInternal(true /*newRow*/, null /*values*/); + this.newRowIndex = this.Rows.Count - 1; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing] = createdByEditing; + + if (createdByEditing) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(this.Rows[this.newRowIndex]); + OnUserAddedRow(dgvre); + } + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual DataGridViewAdvancedBorderStyle AdjustColumnHeaderBorderStyle(DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStyleInput, + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder, + bool isFirstDisplayedColumn, + bool isLastVisibleColumn) + { + if (this.ApplyVisualStylesToHeaderCells) + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.Inset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + case DataGridViewAdvancedCellBorderStyle.Outset: + case DataGridViewAdvancedCellBorderStyle.Inset: + if (!isFirstDisplayedColumn || this.RowHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All; + return dataGridViewAdvancedBorderStylePlaceholder; + } + else + { + // isFirstDisplayedColumn == true && this.RowHeadersVisible == false + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = dataGridViewAdvancedBorderStyleInput.All; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All; + return dataGridViewAdvancedBorderStylePlaceholder; + } + } + } + else + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isLastVisibleColumn ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetPartial; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isLastVisibleColumn ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + if (!isFirstDisplayedColumn || this.RowHeadersVisible) + { + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + return dataGridViewAdvancedBorderStylePlaceholder; + } + break; + } + } + + return dataGridViewAdvancedBorderStyleInput; + } + + private bool AdjustExpandingColumn(DataGridViewColumn dataGridViewColumn, int rowIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(rowIndex > -1); + Debug.Assert(rowIndex < this.Rows.Count); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return false; + } + + bool ret = false; // No autosizing occurs by default. + try + { + this.noAutoSizeCount++; + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int preferredThickness = dataGridViewRow.Cells[dataGridViewColumn.Index].GetPreferredWidth(rowIndex, dataGridViewRow.GetHeight(rowIndex)); + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (dataGridViewColumn.Width < preferredThickness) + { + // Column needs to be expanded + dataGridViewColumn.ThicknessInternal = preferredThickness; + ret = true; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + + return ret; + } + + private bool AdjustExpandingColumns(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, int rowIndex) + { + Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0); + + bool ret = false; // No column autosizes by default + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria = (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = (inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter); + if (autoSizeColumnCriteriaFiltered != 0) + { + ret |= AdjustExpandingColumn(dataGridViewColumn, rowIndex); + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return ret; + } + + private int AdjustExpandingRow(int rowIndex, int columnIndex, bool fixedWidth) + { + Debug.Assert(columnIndex >= -1 && columnIndex < this.Columns.Count); + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders || + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders || + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedCells); + + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int width = 0; + DataGridViewCell dataGridViewCell; + if (columnIndex > -1 && (((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + if (fixedWidth) + { + width = this.Columns[columnIndex].Thickness; + } + } + else + { + Debug.Assert(columnIndex == -1); + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0); + dataGridViewCell = this.Rows.SharedRow(rowIndex).HeaderCell; + if (fixedWidth) + { + Debug.Assert(this.RowHeadersWidth > 0); + Debug.Assert(this.RowHeadersVisible); + width = this.RowHeadersWidth; + } + } + int preferredThickness; + if (fixedWidth) + { + preferredThickness = dataGridViewCell.GetPreferredHeight(rowIndex, width); + } + else + { + preferredThickness = dataGridViewCell.GetPreferredSize(rowIndex).Height; + } + int height, minimumHeight; + this.Rows.SharedRow(rowIndex).GetHeightInfo(rowIndex, out height, out minimumHeight); + if (preferredThickness < height) + { + preferredThickness = height; + } + Debug.Assert(preferredThickness >= minimumHeight); + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (height != preferredThickness) + { + Debug.Assert(this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None); + this.Rows[rowIndex].Thickness = preferredThickness; // unsharing the resized row + } + return preferredThickness; + } + + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] // Can't cache DataGridViewRow object because rowIndex is changing in loop. + private void AdjustExpandingRows(int columnIndex, bool fixedWidth) + { + if ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && this.RowHeadersVisible)) + { + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + // Very expensive processing - the developer should avoid this scenario. + // Switch to batch operation + this.inBulkPaintCount++; + try + { + if ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0) + { + this.inBulkLayoutCount++; + try + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + int width = 0; + DataGridViewCell dataGridViewCell; + if (columnIndex > -1 && (((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + if (fixedWidth) + { + width = this.Columns[columnIndex].Thickness; + } + } + else + { + //Debug.Assert(columnIndex == -1); + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0); + dataGridViewCell = this.Rows.SharedRow(rowIndex).HeaderCell; + if (fixedWidth) + { + Debug.Assert(this.RowHeadersWidth > 0); + Debug.Assert(this.RowHeadersVisible); + width = this.RowHeadersWidth; + } + } + int preferredHeight; + if (fixedWidth) + { + preferredHeight = dataGridViewCell.GetPreferredHeight(rowIndex, width); + } + else + { + preferredHeight = dataGridViewCell.GetPreferredSize(rowIndex).Height; + } + if (this.Rows.SharedRow(rowIndex).Height < preferredHeight) + { + this.Rows[rowIndex].Height = preferredHeight; // unsharing the row to be resized + } + } + } + finally + { + ExitBulkLayout(false /*invalidInAdjustFillingColumns*/); + } + } + else + { + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0); + int displayHeight = this.layout.Data.Height; + int cy = 0; + + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth); + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + if (cy < displayHeight) + { + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + } + finally + { + ExitBulkPaint(-1, -1); + } + } + } + + internal void AdjustFillingColumn(DataGridViewColumn dataGridViewColumn, int width) + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumn] = true; + + try + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Visible); + Debug.Assert(!dataGridViewColumn.Frozen); + Debug.Assert(dataGridViewColumn.MinimumWidth <= width); + Debug.Assert(dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill); + Debug.Assert(!this.layout.dirty); + + if (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) > this.layout.Data.Width) + { + // Columns are scrolling - this means that all columns have reached their minimum width. + // Do not affect their width or fill weight + Debug.Assert(dataGridViewColumn.MinimumWidth == dataGridViewColumn.Width); + return; + } + + int availableWidth = this.layout.Data.Width; // Width available for auto-filled columns + + // Check if the column is the first or last visible scrolling column + if (this.DesignMode || + dataGridViewColumn == this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) || + dataGridViewColumn == this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen)) + { + // Changing the width is equivalent to adjusting the FillWeight when: + // - the column is not scrolling and is the first non-frozen visible column + // - the column is not scrolling and is the last non-frozen visible column + + float weightSum = 0; // Weights of all auto filled visible columns. + int widthSum = 0; // Sum of widths of visible auto filled columns. + int imposedWidthSum = 0; // Minimum width required for all other columns. + bool otherFillingColumnExists = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible) + { + if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumnTmp.Frozen); + widthSum += dataGridViewColumnTmp.Width; + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index) + { + imposedWidthSum += dataGridViewColumnTmp.MinimumWidth; + otherFillingColumnExists = true; + } + weightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + imposedWidthSum += dataGridViewColumnTmp.Width; + availableWidth -= dataGridViewColumnTmp.Width; + } + } + } + + if (!otherFillingColumnExists) + { + // The resized column is the only one that is filling. This is a no-op operation. + // Neither the width nor the fill weight can change + return; + } + + int maximumPossibleWidth = this.layout.Data.Width - imposedWidthSum; + if (width > maximumPossibleWidth) + { + width = maximumPossibleWidth; + } + + // Determine fill weight equivalent to 'width' + float oldWeight = dataGridViewColumn.FillWeight; + float newWeight = (float)(width * weightSum) / (float)widthSum; + bool desiredWidthTooSmall = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.FillWeightInternal = (weightSum - newWeight) * dataGridViewColumnTmp.FillWeight / (weightSum - oldWeight); + + if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / (float)widthSum) + { + desiredWidthTooSmall = true; + dataGridViewColumnTmp.DesiredFillWidth = -1; + } + else + { + dataGridViewColumnTmp.DesiredFillWidth = 0; + } + } + } + + dataGridViewColumn.FillWeightInternal = newWeight; + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + float usedFillWeights = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + else if (dataGridViewColumnTmp.DesiredFillWidth == -1) + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + } + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumnTmp.DesiredFillWidth != -1) + { + dataGridViewColumnTmp.UsedFillWeight = Math.Max(dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal, + weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum); + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights; + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + } + } + #if DEBUG + float weightSumDbg = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + weightSumDbg += dataGridViewColumnTmp.UsedFillWeight; + } + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); + #endif + } + else + { + // This column is not the first nor the last visible non-frozen column + // Changing its width only affects the width and weight of the columns displayed after it + + // First figure out the maximum possible width + int imposedWidthSum = 0; // Minimum width required for all other columns + float weightSum = 0; // Weights of all auto filled visible columns. + float oldWeightSum = 0F; // Fill weights of the columns displayed after this resized column + bool otherFillingColumnExists = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible) + { + if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumnTmp.Frozen); + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index) + { + if (this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + imposedWidthSum += dataGridViewColumnTmp.MinimumWidth; // Column is allowed to shrink down to its minimum + oldWeightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + // Column is displayed before 'dataGridViewColumn', it is not allowed to shrink at all + imposedWidthSum += dataGridViewColumnTmp.Width; + } + otherFillingColumnExists = true; + } + weightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + imposedWidthSum += dataGridViewColumnTmp.Width; + availableWidth -= dataGridViewColumnTmp.Width; + } + } + } + + if (!otherFillingColumnExists) + { + // The resized column is the only one that is filling. This is a no-op operation. + // Neither the width nor the fill weight can change + return; + } + + int maximumPossibleWidth = this.layout.Data.Width - imposedWidthSum; + if (width > maximumPossibleWidth) + { + width = maximumPossibleWidth; + } + + // Then figure out the target fill weights + float oldWeight = dataGridViewColumn.FillWeight; + float newWeight = weightSum * width / availableWidth; + float newWeightSum = oldWeightSum + oldWeight - newWeight; + Debug.Assert(newWeightSum > 0); + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + dataGridViewColumnTmp.FillWeightInternal = dataGridViewColumnTmp.FillWeight * newWeightSum / oldWeightSum; + } + } + + dataGridViewColumn.FillWeightInternal = newWeight; + + bool desiredWidthTooSmall = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / (float)availableWidth) + { + desiredWidthTooSmall = true; + dataGridViewColumnTmp.DesiredFillWidth = -1; + } + else + { + dataGridViewColumnTmp.DesiredFillWidth = 0; + } + } + } + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + float usedFillWeights = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index || + this.Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index)) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + else + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.Width / availableWidth; + } + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + else if (dataGridViewColumnTmp.DesiredFillWidth == -1) + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + } + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index) && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumnTmp.DesiredFillWidth != -1) + { + dataGridViewColumnTmp.UsedFillWeight = Math.Max(dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal, + weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth); + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights; + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + } + } + #if DEBUG + float weightSumDbg = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + weightSumDbg += dataGridViewColumnTmp.UsedFillWeight; + } + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); + #endif + } + + // UsedFillWeight properties are up-to-date + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + // AdjustFillingColumns() will resize columns based on the UsedFillWeight values + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumn] = false; + } + } + + private bool AdjustFillingColumns() + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns]) + { + // No need to auto fill columns while we're already doing it. + return false; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns] = true; + + bool columnsAdjusted = false; + try + { + // Determine free space for filling columns. + int numVisibleFillColumns = 0; // number of visible columns that are auto filled. + int imposedWidthSum = 0; // total width of columns that don't have a flexible width. + int requiredWidthSum = 0; // total of the minimum widths of the visible auto filled columns. + float weightSum = 0F; // total of the weights of the visible auto filled columns. + ArrayList autoFillColumns = null; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.Visible) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumn.Frozen); + numVisibleFillColumns++; + requiredWidthSum += dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + weightSum += dataGridViewColumn.FillWeight; + if (autoFillColumns == null) + { + autoFillColumns = new ArrayList(this.Columns.Count); + } + autoFillColumns.Add(dataGridViewColumn); + } + else + { + imposedWidthSum += dataGridViewColumn.Width; + } + } + } + + if (numVisibleFillColumns > 0) + { + // Assuming no vertical scrollbar has been accounted for yet + Debug.Assert(this.layout.Data.Width == this.layout.Inside.Width - this.layout.RowHeaders.Width - (this.SingleVerticalBorderAdded ? 1 : 0)); + int availableWidth = this.layout.Data.Width - imposedWidthSum; + if ((this.scrollBars == ScrollBars.Both || this.scrollBars == ScrollBars.Vertical) /*&& + (availableWidth > requiredWidthSum || this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty])*/) + { + int totalVisibleRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible); + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + // Assuming there is no horizontal scrollbar, is a vertical scrollbar required? + ComputeVisibleRows(); // Make sure this.displayedBandsInfo.FirstDisplayedScrollingRow and other row count info variables have been set + + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + SystemInformation.VerticalScrollBarWidth <= this.layout.Data.Width) + { + // Vertical scrollbar is required, even when there is not horizontal one. + availableWidth -= SystemInformation.VerticalScrollBarWidth; + } + } + + int columnEntry; + + if (availableWidth <= requiredWidthSum) + { + // All auto filled columns need to take their minimum width. If (availableWidth < requiredWidthSum) a horizontal scrollbar appears. + availableWidth = 0; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + if (dataGridViewColumn.Thickness != minimumWidth) + { + columnsAdjusted = true; + dataGridViewColumn.ThicknessInternal = minimumWidth; + } + availableWidth += dataGridViewColumn.Thickness; + } + //if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + // Make sure the UsedFillWeight correspond to the actual column width + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.Width * weightSum / availableWidth; + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + } + return columnsAdjusted; + } + + // Auto filling columns can share some real estate. + + int usedWidth = 0; + + // Update the UsedFillWeight value if dirty + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + // Assign desired widths + Debug.Assert(weightSum > 0); + bool desiredWidthTooSmall = false; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (columnEntry == autoFillColumns.Count - 1) + { + dataGridViewColumn.DesiredFillWidth = availableWidth - usedWidth; + } + else + { + float desiredFillWidth = (dataGridViewColumn.FillWeight / weightSum) * availableWidth; + dataGridViewColumn.DesiredFillWidth = (int)Math.Round(desiredFillWidth, MidpointRounding.AwayFromZero); + usedWidth += dataGridViewColumn.DesiredFillWidth; + } + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + if (dataGridViewColumn.DesiredFillWidth < minimumWidth) + { + desiredWidthTooSmall = true; + dataGridViewColumn.DesiredFillWidth = -1; + } + } + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth == -1) + { + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + dataGridViewColumn.UsedFillWeight = weightSum * minimumWidth / availableWidth; + usedWeightSumNoneMinimal -= dataGridViewColumn.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumn.FillWeight; + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth != -1) + { + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal; + } + } + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight; + } + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + } + else if (availableWidth != this.availableWidthForFillColumns) + { + // The available width for auto-filled columns has changed - UsedFillWeight values need to be adjusted. + if (availableWidth > this.availableWidthForFillColumns) + { + // Available width increased + int widthGain = availableWidth - this.availableWidthForFillColumns; + // Allocate additional width according to UsedFillWeight and FillWeight values + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width; + } + float[] floatDesiredWidths = new float[autoFillColumns.Count]; + for (int gain = 0; gain < widthGain; gain++) + { + float fillWeightRatioSum = 0F; + bool minimumColumnExists = false; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + fillWeightRatioSum += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight; + if (dataGridViewColumn.DesiredFillWidth <= dataGridViewColumn.MinimumWidth) + { + minimumColumnExists = true; + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (gain == 0) + { + floatDesiredWidths[columnEntry] = this.availableWidthForFillColumns * dataGridViewColumn.UsedFillWeight / weightSum; + } + if (minimumColumnExists) + { + floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight / fillWeightRatioSum; + } + else + { + floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / weightSum; + } + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = weightSum / availableWidth * floatDesiredWidths[columnEntry]; + } + } + else + { + // availableWidth < this.availableWidthForFillColumns - Available width decreased + int totalWidthLoss = this.availableWidthForFillColumns - availableWidth; + int cumulatedWidthLoss = 0; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width; + } + // the width loss is accounted for in steps of 10% (see VSWhidbey 568343) + do + { + int stepDownAvailableWidthForFillColumns = this.availableWidthForFillColumns - cumulatedWidthLoss; + int widthLoss = Math.Min(stepDownAvailableWidthForFillColumns - availableWidth, Math.Max(1, (int)(stepDownAvailableWidthForFillColumns * 0.1F))); + cumulatedWidthLoss += widthLoss; + bool changeDone; + do + { + changeDone = false; + // Determine which column deserves to shrink the most + float biggestWeightDeficiency = 0F, fillWeightRatioSum = 0F, fillWeightRatio; + DataGridViewColumn mostDeservingDataGridViewColumn = null; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth > dataGridViewColumn.MinimumWidth) + { + fillWeightRatio = dataGridViewColumn.UsedFillWeight / dataGridViewColumn.FillWeight; + fillWeightRatioSum += fillWeightRatio; + if (fillWeightRatio > biggestWeightDeficiency) + { + mostDeservingDataGridViewColumn = dataGridViewColumn; + biggestWeightDeficiency = fillWeightRatio; + } + } + } + if (mostDeservingDataGridViewColumn != null) + { + float floatDesiredWidth = (stepDownAvailableWidthForFillColumns * mostDeservingDataGridViewColumn.UsedFillWeight / weightSum) - widthLoss * mostDeservingDataGridViewColumn.UsedFillWeight / mostDeservingDataGridViewColumn.FillWeight / fillWeightRatioSum; + if (floatDesiredWidth < (float)mostDeservingDataGridViewColumn.MinimumWidth) + { + floatDesiredWidth = (int)mostDeservingDataGridViewColumn.MinimumWidth; + } + int oldDesiredWidth = mostDeservingDataGridViewColumn.DesiredFillWidth; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Min(oldDesiredWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero)); + changeDone = (oldDesiredWidth != mostDeservingDataGridViewColumn.DesiredFillWidth); + if (!changeDone && widthLoss == 1 && oldDesiredWidth > mostDeservingDataGridViewColumn.MinimumWidth) + { + mostDeservingDataGridViewColumn.DesiredFillWidth--; + changeDone = true; + } + Debug.Assert(oldDesiredWidth >= mostDeservingDataGridViewColumn.DesiredFillWidth); + widthLoss -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + if (changeDone) + { + stepDownAvailableWidthForFillColumns -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = weightSum / stepDownAvailableWidthForFillColumns * dataGridViewColumn.DesiredFillWidth; + } + } + Debug.Assert(widthLoss >= 0); + } + } + while (changeDone && widthLoss > 0); + } + while (cumulatedWidthLoss < totalWidthLoss); + } + this.availableWidthForFillColumns = availableWidth; + } + +#if DEBUG + float weightSumDbg = 0F; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + weightSumDbg += dataGridViewColumn.UsedFillWeight; + + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); +#endif + + // Finally update the columns' width according the UsedFillWeight values. + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar] = false; + usedWidth = 0; + float carryover = 0F; + while (autoFillColumns.Count > 0) + { + DataGridViewColumn mostDeservingDataGridViewColumn = null; + if (autoFillColumns.Count == 1) + { + mostDeservingDataGridViewColumn = (DataGridViewColumn)autoFillColumns[0]; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(availableWidth - usedWidth, mostDeservingDataGridViewColumn.MinimumWidth); + autoFillColumns.Clear(); + } + else + { + float biggestWeightDiscrepancy = 0F, weightDiscrepancy; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + weightDiscrepancy = Math.Abs(dataGridViewColumn.UsedFillWeight - dataGridViewColumn.FillWeight) / dataGridViewColumn.FillWeight; + if (weightDiscrepancy > biggestWeightDiscrepancy || mostDeservingDataGridViewColumn == null) + { + mostDeservingDataGridViewColumn = dataGridViewColumn; + biggestWeightDiscrepancy = weightDiscrepancy; + } + } + float floatDesiredWidth = (mostDeservingDataGridViewColumn.UsedFillWeight * availableWidth / weightSum) + carryover; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(mostDeservingDataGridViewColumn.MinimumWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero)); + carryover = floatDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + usedWidth += mostDeservingDataGridViewColumn.DesiredFillWidth; + autoFillColumns.Remove(mostDeservingDataGridViewColumn); + } + if (mostDeservingDataGridViewColumn.DesiredFillWidth != mostDeservingDataGridViewColumn.Thickness) + { + columnsAdjusted = true; + mostDeservingDataGridViewColumn.ThicknessInternal = mostDeservingDataGridViewColumn.DesiredFillWidth; + } + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar] = true; + } + } +#if DEBUG + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(Math.Abs(dataGridViewColumnTmp.UsedFillWeight * this.availableWidthForFillColumns - weightSum * dataGridViewColumnTmp.Width) / weightSum / dataGridViewColumnTmp.Width <= 1.25F / dataGridViewColumnTmp.Width); + } + } + } + + bool nonMinColumnExists = false; + int widthSum = 0; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + widthSum += dataGridViewColumnTmp.Width; + if (dataGridViewColumnTmp.Width > dataGridViewColumnTmp.MinimumWidth) + { + nonMinColumnExists = true; + } + } + } + if (nonMinColumnExists) + { + Debug.Assert(widthSum == this.availableWidthForFillColumns); + } +#endif + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns] = false; + } + + return columnsAdjusted; + } + + private void AdjustShrinkingRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth, bool internalAutosizing) + { + if ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && this.RowHeadersVisible)) + { + // Switch to batch operation + this.inBulkPaintCount++; + try + { + if ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0) + { + // Very expensive processing - the developer should avoid this scenario. + this.inBulkLayoutCount++; + try + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + } + } + finally + { + ExitBulkLayout(false /*invalidInAdjustFillingColumns*/); + } + } + else + { + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0); + int displayHeight = this.layout.Data.Height; + int cy = 0; + + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + if (cy < displayHeight) + { + int cyFrozen = cy; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + rowIndex = oldFirstVisibleScrollingRow; + while (rowIndex != -1 && + cy < displayHeight && + oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + + do + { + oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (cy < displayHeight) + { + int rowAboveFirstVisibleScrollingRow = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowAboveFirstVisibleScrollingRow != -1) + { + AutoResizeRowInternal(rowAboveFirstVisibleScrollingRow, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + } + } + cy = cyFrozen; + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + while (oldFirstVisibleScrollingRow != this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + } + } + finally + { + ExitBulkPaint(-1, -1); + } + } + } + + /// + // Does not seem to be a valid fxcop violation report. Contacting fxcop team to double-check. + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] + public bool AreAllCellsSelected(bool includeInvisibleCells) + { + if (this.Columns.Count == 0 && this.Rows.Count == 0) + { + return true; + } + if (!includeInvisibleCells && + (this.Rows.GetFirstRow(DataGridViewElementStates.Visible) == -1 || + this.Columns.GetFirstColumn(DataGridViewElementStates.Visible) == null)) + { + return true; + } + + DataGridViewRow dataGridViewRow = null; + bool allCellsSelected; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + allCellsSelected = this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + return true; + } + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + allCellsSelected = this.selectedBandIndexes.Count * this.Rows.Count + this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!this.selectedBandIndexes.Contains(dataGridViewColumn.Index)) + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return true; + } + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + allCellsSelected = this.selectedBandIndexes.Count * this.Columns.Count + this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + return true; + } + } + } + Debug.Fail("DataGridView.AreAllCellsSelected - Shouldn't reach this code"); + return false; + } + + /// + /// Assigns a new parent control to the DataGridView. + /// + internal override void AssignParent(Control value) + { + if (this.toolTipControl.Activated) + { + this.toolTipControl.Activate(false /*activate*/); + } + base.AssignParent(value); + } + + private void AutoGenerateDataBoundColumns(DataGridViewColumn[] boundColumns) + { + DataGridViewColumnCollection dataGridViewCols = this.Columns; + DataGridViewColumn[] clonedColumns = new DataGridViewColumn[dataGridViewCols.Count]; + int clonedColumnsCount = 0; + int i; + + + // 1. Clone all the columns which are currently bound and will also be bound under the new DataSource/DataMember combination. + // 2. Delete all the currently bound columns. + // 3. Sort the cloned columns in order of their DisplayIndex. + // 4. Add the new data bound columns. Here are the rules: + // a. if the cloned collection contains (possible multiple) columns with DataProperyHame == current data field, + // add the first cloned column that maps to the data field. + // b. other wise add the new bound column. + // 5. Add the remaining cloned columns in the order of their DisplayIndex. + + // 1. Clone all the currently bound columns. + // + // We can't do 1. and 2. in the same loop because we need to save the DisplayIndex. + for (i = 0; i < dataGridViewCols.Count; i++) + { + if (this.DataSource != null && + !String.IsNullOrEmpty(dataGridViewCols[i].DataPropertyName) && + !dataGridViewCols[i].IsDataBound) + { + MapDataGridViewColumnToDataBoundField(dataGridViewCols[i]); + } + + if (dataGridViewCols[i].IsDataBound) + { + // We only clone columns which are data bound w/ the new DataSource/DataMember combination. + if (this.dataConnection != null && this.dataConnection.BoundColumnIndex(dataGridViewCols[i].DataPropertyName) != -1) + { + clonedColumns[clonedColumnsCount] = (DataGridViewColumn) dataGridViewCols[i].Clone(); + clonedColumns[clonedColumnsCount].DisplayIndex = dataGridViewCols[i].DisplayIndex; + clonedColumnsCount ++; + } + } + } + + i = 0; + // 2. Delete all the currently bound columns. + while (i < dataGridViewCols.Count) + { + if (dataGridViewCols[i].IsDataBound) + { + dataGridViewCols.RemoveAtInternal(i, true /*force*/); + } + else + { + i++; + } + } + + // 3. Sort the cloned columns in the order of their DisplayIndex. + + // Sort the cloned columns array by the display index. + // We need to copy the cloned columns into a possibly smaller array. + DataGridViewColumn[] finalClonedColumns; + if (clonedColumns.Length == clonedColumnsCount) + { + finalClonedColumns = clonedColumns; + } + else + { + finalClonedColumns = new DataGridViewColumn[clonedColumnsCount]; + Array.Copy(clonedColumns, finalClonedColumns, clonedColumnsCount); + } + + // Sort the array. + Array.Sort(finalClonedColumns, System.Windows.Forms.DataGridViewColumnCollection.ColumnCollectionOrderComparer); + + // 4. Add new columns for the Fields which were not data bound previously ( ie, for fields which do not have a clone ). + if (boundColumns != null) + { + for (int j = 0; j < boundColumns.Length; j ++) + { + if (boundColumns[j] != null && boundColumns[j].IsBrowsableInternal) + { + bool addNewColumn = true; + // Go thru the list of cloned columns and see if there is another column w/ the same data property name. + int clonedColIndex = 0; + for (; clonedColIndex < clonedColumnsCount; clonedColIndex ++) + { + if (finalClonedColumns[clonedColIndex] != null && + String.Compare(finalClonedColumns[clonedColIndex].DataPropertyName, + boundColumns[j].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + addNewColumn = false; + break; + } + } + + if (addNewColumn) + { + dataGridViewCols.Add(boundColumns[j]); + } + else + { + // add the cloned column. + dataGridViewCols.Add(finalClonedColumns[clonedColIndex]); + MapDataGridViewColumnToDataBoundField(finalClonedColumns[clonedColIndex]); + Debug.Assert(finalClonedColumns[clonedColIndex].IsDataBound); + finalClonedColumns[clonedColIndex] = null; + } + } + } + } + #if DEBUG + else + { + // If there are no data bound columns then there are no cloned columns either. + Debug.Assert(finalClonedColumns.Length == 0); + Debug.Assert(clonedColumnsCount == 0); + } + #endif // DEBUG + + // 5. Add remaining cloned columns. + if (clonedColumnsCount > 0) + { + for (int k = 0; k < finalClonedColumns.Length; k++) + { + if (finalClonedColumns[k] != null) + { + dataGridViewCols.Add(finalClonedColumns[k]); + MapDataGridViewColumnToDataBoundField(finalClonedColumns[k]); + Debug.Assert(finalClonedColumns[k].IsDataBound); + } + } + } + } + + private bool AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, bool fixedHeight) + { + Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0); + + bool ret = false; // No column autosizes by default + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = (inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter); + if (autoSizeColumnCriteriaFiltered != 0) + { + ret |= AutoResizeColumnInternal(dataGridViewColumn.Index, inheritedAutoSizeColumnCriteria, fixedHeight); + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return ret; + } + + /// + public void AutoResizeColumn(int columnIndex) + { + AutoResizeColumn(columnIndex, DataGridViewAutoSizeColumnMode.AllCells); + } + + /// + public void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode) + { + AutoResizeColumn(columnIndex, autoSizeColumnMode, true); + } + + /// + protected void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode, bool fixedHeight) + { + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.NotSet || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.None || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedColumnAutoSizingCriteria, "autoSizeColumnMode")); + } + + switch (autoSizeColumnMode) + { + case DataGridViewAutoSizeColumnMode.NotSet: + case DataGridViewAutoSizeColumnMode.None: + case DataGridViewAutoSizeColumnMode.ColumnHeader: + case DataGridViewAutoSizeColumnMode.AllCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.AllCells: + case DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.DisplayedCells: + case DataGridViewAutoSizeColumnMode.Fill: + break; + default: + throw new InvalidEnumArgumentException("autoSizeColumnMode", (int)autoSizeColumnMode, typeof(DataGridViewAutoSizeColumnMode)); + } + + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.ColumnHeader && !this.ColumnHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeInvisibleColumnHeader)); + } + + AutoResizeColumnInternal(columnIndex, (DataGridViewAutoSizeColumnCriteriaInternal) autoSizeColumnMode, fixedHeight); + } + + /// + public void AutoResizeColumnHeadersHeight() + { + AutoResizeColumnHeadersHeight(true, true); + } + + /// + public void AutoResizeColumnHeadersHeight(int columnIndex) + { + AutoResizeColumnHeadersHeight(columnIndex, true, true); + } + + /// + protected void AutoResizeColumnHeadersHeight(bool fixedRowHeadersWidth, bool fixedColumnsWidth) + { + if (!this.ColumnHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredHeight = 0; + if (this.layout.TopLeftHeader.Width > 0) + { + if (fixedRowHeadersWidth) + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredHeight(-1, this.layout.TopLeftHeader.Width); + } + else + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredSize(-1).Height; + } + } + int columnsCount = this.Columns.Count; + for (int columnIndex = 0; columnIndex < columnsCount; columnIndex++) + { + if (this.Columns[columnIndex].Visible) + { + if (fixedColumnsWidth) + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndex].HeaderCell.GetPreferredHeight(-1, this.Columns[columnIndex].Thickness)); + } + else + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndex].HeaderCell.GetPreferredSize(-1).Height); + } + } + } + if (preferredHeight < minimumColumnHeadersHeight) + { + preferredHeight = minimumColumnHeadersHeight; + } + if (preferredHeight > maxHeadersThickness) + { + preferredHeight = maxHeadersThickness; + } + if (preferredHeight != this.ColumnHeadersHeight) + { + SetColumnHeadersHeightInternal(preferredHeight, !fixedColumnsWidth /*invalidInAdjustFillingColumns*/); + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + protected void AutoResizeColumnHeadersHeight(int columnIndex, bool fixedRowHeadersWidth, bool fixedColumnWidth) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (!this.ColumnHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredHeight = 0; + if (this.layout.TopLeftHeader.Width > 0) + { + if (columnIndex != -1 || fixedRowHeadersWidth) + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredHeight(-1, this.layout.TopLeftHeader.Width); + } + else + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredSize(-1).Height; + } + } + int columnsCount = this.Columns.Count; + for (int columnIndexTmp = 0; columnIndexTmp < columnsCount; columnIndexTmp++) + { + if (this.Columns[columnIndexTmp].Visible) + { + if (columnIndex != columnIndexTmp || fixedColumnWidth) + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndexTmp].HeaderCell.GetPreferredHeight(-1, this.Columns[columnIndexTmp].Thickness)); + } + else + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndexTmp].HeaderCell.GetPreferredSize(-1).Height); + } + } + } + if (preferredHeight < minimumColumnHeadersHeight) + { + preferredHeight = minimumColumnHeadersHeight; + } + if (preferredHeight > maxHeadersThickness) + { + preferredHeight = maxHeadersThickness; + } + if (preferredHeight != this.ColumnHeadersHeight) + { + SetColumnHeadersHeightInternal(preferredHeight, !fixedColumnWidth /*invalidInAdjustFillingColumns*/); + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + private bool AutoResizeColumnInternal(int columnIndex, DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal, bool fixedHeight) + { + Debug.Assert(autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.Header || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.AllRows || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows) || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows)); + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Header || this.ColumnHeadersVisible); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return false; + } + + bool ret = false; // No autosizing occurs by default. + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns. + // Note: Even none-resizable column can programmatically be resized. + DataGridViewColumn dataGridViewColumn = this.Columns[columnIndex]; + int preferredColumnWidth = dataGridViewColumn.GetPreferredWidth((DataGridViewAutoSizeColumnMode) autoSizeColumnCriteriaInternal, fixedHeight); + if (preferredColumnWidth < dataGridViewColumn.MinimumThickness) + { + preferredColumnWidth = dataGridViewColumn.MinimumThickness; + } + if (preferredColumnWidth > DataGridViewBand.maxBandThickness) + { + preferredColumnWidth = DataGridViewBand.maxBandThickness; + } + if (preferredColumnWidth != dataGridViewColumn.Thickness) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + AdjustFillingColumn(dataGridViewColumn, preferredColumnWidth); + } + else + { + this.Columns[columnIndex].ThicknessInternal = preferredColumnWidth; + } + ret = true; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + return ret; + } + + /// + public void AutoResizeColumns() + { + AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); + } + + /// + public void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode) + { + AutoResizeColumns(autoSizeColumnsMode, true); + } + + /// + protected void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode, bool fixedHeight) + { + for (int columnIndex = 0; columnIndex < this.Columns.Count; columnIndex++) + { + AutoResizeColumn(columnIndex, (DataGridViewAutoSizeColumnMode)autoSizeColumnsMode, fixedHeight); + } + } + + /// + public void AutoResizeRow(int rowIndex) + { + AutoResizeRow(rowIndex, DataGridViewAutoSizeRowMode.AllCells); + } + + /// + public void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode) + { + AutoResizeRow(rowIndex, autoSizeRowMode, true /*fixedWidth*/); + } + + /// + protected void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + // not using ClientUtils here because it's a flags enum, masking instead. + if (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0) + { + throw new InvalidEnumArgumentException("autoSizeRowMode", (int) autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode)); + } + if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowInvisibleRowHeader)); + } + AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, false /*internalAutosizing*/); + } + + /// + // User can override this if there is a quicker way to determine preferred row headers width + public void AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode) + { + AutoResizeRowHeadersWidth(rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + + /// + // User can override this if there is a quicker way to determine preferred row headers width + protected void AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode, + bool fixedColumnHeadersHeight, + bool fixedRowsHeight) + { + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode")); + } + // custom range checking, not using ClientUtils. + if (rowHeadersWidthSizeMode < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || rowHeadersWidthSizeMode > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader) + { + throw new InvalidEnumArgumentException("rowHeadersWidthSizeMode", (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode)); + } + + + if (!this.RowHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredWidth = 0, rowIndex; + if (this.layout.TopLeftHeader.Width > 0) + { + if (fixedColumnHeadersHeight) + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredWidth(-1, this.layout.TopLeftHeader.Height); + } + else + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredSize(-1).Width; + } + } + switch (rowHeadersWidthSizeMode) + { + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader: + { + rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex != -1) + { + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, this.Rows.SharedRow(rowIndex).GetHeight(rowIndex))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders: + { + int displayHeight = this.layout.Data.Height, cy = 0; + rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex); + cy += dataGridViewRowHeight; + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width); + } + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + if (cy < displayHeight) + { + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex); + cy += dataGridViewRowHeight; + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width); + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders: + { + for (rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, this.Rows.SharedRow(rowIndex).GetHeight(rowIndex))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width); + } + } + break; + } + default: + { + Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth"); + break; + } + } + if (preferredWidth < minimumRowHeadersWidth) + { + preferredWidth = minimumRowHeadersWidth; + } + if (preferredWidth != this.RowHeadersWidth) + { + this.RowHeadersWidthInternal = preferredWidth; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + public void AutoResizeRowHeadersWidth(int rowIndex, DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode) + { + AutoResizeRowHeadersWidth(rowIndex, + rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowHeight*/); + } + + /// + protected void AutoResizeRowHeadersWidth(int rowIndex, + DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode, + bool fixedColumnHeadersHeight, + bool fixedRowHeight) + { + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode")); + } + if (rowHeadersWidthSizeMode < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || rowHeadersWidthSizeMode > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader) + { + throw new InvalidEnumArgumentException("rowHeadersWidthSizeMode", (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode)); + } + + if (!this.RowHeadersVisible) + { + return; + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader && + rowIndex != -1 && + rowIndex != this.Rows.GetFirstRow(DataGridViewElementStates.Visible)) + { + return; + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && + rowIndex != -1) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + if (!rowDisplayed) + { + return; + } + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredWidth = 0, rowIndexTmp; + if (this.layout.TopLeftHeader.Width > 0) + { + if (rowIndex != -1 || fixedColumnHeadersHeight) + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredWidth(-1, this.layout.TopLeftHeader.Height); + } + else + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredSize(-1).Width; + } + } + switch (rowHeadersWidthSizeMode) + { + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader: + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, this.Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders: + { + int displayHeight = this.layout.Data.Height, cy = 0; + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndexTmp != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndexTmp); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp); + cy += dataGridViewRowHeight; + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + if (cy < displayHeight) + { + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndexTmp != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndexTmp); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp); + cy += dataGridViewRowHeight; + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders: + { + for (rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndexTmp != -1; + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible)) + { + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, this.Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + } + break; + } + default: + { + Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth"); + break; + } + } + if (preferredWidth < minimumRowHeadersWidth) + { + preferredWidth = minimumRowHeadersWidth; + } + if (preferredWidth != this.RowHeadersWidth) + { + this.RowHeadersWidthInternal = preferredWidth; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + private void AutoResizeRowInternal(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth, bool internalAutosizing) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) == 0); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + // Note: Even none-resizable row can programmatically be resized. + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(rowIndex, out height, out minimumHeight); + int preferredThickness = dataGridViewRow.GetPreferredHeight(rowIndex, autoSizeRowMode, fixedWidth); + if (preferredThickness < minimumHeight) + { + preferredThickness = minimumHeight; + } + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (height != preferredThickness) + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + if (!OnRowHeightInfoPushed(rowIndex, preferredThickness, minimumHeight)) + { + this.Rows[rowIndex].ThicknessInternal = preferredThickness; // unsharing the resized row + } + } + else + { + if (internalAutosizing) + { + this.Rows[rowIndex].ThicknessInternal = preferredThickness; // unsharing the resized row + } + else + { + this.Rows[rowIndex].Thickness = preferredThickness; // unsharing the resized row + } + } + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + public void AutoResizeRows() + { + AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells); + } + + /// + public void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode) + { + AutoResizeRows(autoSizeRowsMode, true /*fixedWidth*/); + } + + /// + protected void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth) + { + switch (autoSizeRowsMode) + { + case DataGridViewAutoSizeRowsMode.None: + case DataGridViewAutoSizeRowsMode.AllHeaders: + case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.AllCells: + case DataGridViewAutoSizeRowsMode.DisplayedHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCells: + break; + default: + throw new InvalidEnumArgumentException("value", (int)autoSizeRowsMode, typeof(DataGridViewAutoSizeRowsMode)); + } + + if (autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "autoSizeRowsMode")); + } + + if ((autoSizeRowsMode == DataGridViewAutoSizeRowsMode.AllHeaders || autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders) && + !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader)); + } + + AdjustShrinkingRows(autoSizeRowsMode, fixedWidth, false /*internalAutosizing*/); + } + + /// + protected void AutoResizeRows(int rowIndexStart, int rowsCount, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth) + { + // not using ClientUtils.IsEnumValid here because DataGridViewAutoSizeRowCriteriaInternal is a flags enum. + if (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0) + { + throw new InvalidEnumArgumentException("autoSizeRowMode", (int) autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode)); + } + + if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader)); + } + + if (rowsCount < 0) + { + throw new ArgumentOutOfRangeException("rowsCount"); + } + + if (rowIndexStart < 0) + { + throw new ArgumentOutOfRangeException("rowIndexStart"); + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + this.inBulkPaintCount++; + this.inBulkLayoutCount++; + try + { + int rowIndex = this.Rows.GetNextRow(rowIndexStart - 1, DataGridViewElementStates.Visible); + int autoSizedCount = 0; + while (rowIndex != -1 && autoSizedCount < rowsCount) + { + AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, false /*internalAutosizing*/); + autoSizedCount++; + if (autoSizedCount < rowsCount) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + finally + { + ExitBulkLayout(true /*invalidInAdjustFillingColumns*/); + ExitBulkPaint(-1, -1); + } + } + + private void BeginColumnHeadersResize(int mouseY, int mouseBarOffset) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data); + if (this.layout.TopLeftHeader.Width > 0) + { + clip = Rectangle.Union(this.layout.TopLeftHeader, clip); + } + clip.Y += minimumColumnHeadersHeight - mouseBarOffset - 1; + // No need to limit the bottom edge of the cursor clip since maxHeadersThickness is very large. + CaptureMouse(clip); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] = true; + this.trackRowAnchor = mouseY; + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastRowSplitBar == -1); + this.currentRowSplitBar = mouseY; + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void BeginColumnRelocation(int mouseX, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle cursorClip = this.layout.ColumnHeaders; + int frozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int scrollingWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - frozenWidth; + if (this.Columns[index].Frozen) + { + // A frozen column cannot be relocated into an unfrozen area + if (this.RightToLeftInternal) + { + cursorClip.X += cursorClip.Width - frozenWidth; + } + cursorClip.Width = Math.Min(frozenWidth, this.layout.Data.Width); + } + else + { + // An unfrozen column cannot be relocated into a frozen area + if (!this.RightToLeftInternal) + { + cursorClip.X += frozenWidth; + } + else if (this.layout.Data.Width > frozenWidth + scrollingWidth) + { + cursorClip.X += this.layout.Data.Width - frozenWidth - scrollingWidth; + } + cursorClip.Width = Math.Min(scrollingWidth, this.layout.Data.Width); + } + CaptureMouse(cursorClip); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] = true; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumn = index; + this.trackColumnEdge = -1; + + this.mouseBarOffset = GetColumnXFromIndex(index) - mouseX; + this.lastHeaderShadow = mouseX; + Invalidate(this.layout.ColumnHeaders); + } + + private void BeginColumnResize(int x, int columnIndex) + { + this.trackColAnchor = x; + this.trackColumn = columnIndex; + + this.currentColSplitBar = x; + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + private void BeginMouseColumnResize(int mouseX, int mouseBarOffset, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] = true; + this.mouseBarOffset = mouseBarOffset; + this.resizeClipRectangle = GetResizeClipRectangle(index); + CaptureMouse(this.resizeClipRectangle); + + BeginColumnResize(mouseX, index); + } + + private void BeginKeyboardColumnResize(int columnIndex) + { + if (this.IsMouseOperationActive()) + { + return; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] = true; + this.mouseBarOffset = 0; + this.resizeClipRectangle = GetResizeClipRectangle(columnIndex); + this.keyboardResizeStep = this.ScaleToCurrentDpi(this.RightToLeftInternal ? -1 : 1); + int x = GetColumnXFromIndex(columnIndex); + x += this.RightToLeftInternal ? -this.Columns[columnIndex].Width : this.Columns[columnIndex].Width; + + BeginColumnResize(x, columnIndex); + } + + private Rectangle GetResizeClipRectangle(int columnIndex) + { + Rectangle clip = Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data); + int leftEdge = GetColumnXFromIndex(columnIndex); + if (this.RightToLeftInternal) + { + clip.X = this.layout.Data.X - this.mouseBarOffset - 1; + clip.Width = leftEdge - this.Columns[columnIndex].MinimumThickness - this.layout.Data.X + 3; + int overflowWidth = leftEdge - this.mouseBarOffset - clip.Left - DataGridViewBand.maxBandThickness + 1; + if (overflowWidth > 0) + { + clip.X += overflowWidth; + clip.Width -= overflowWidth; + } + } + else + { + clip.X = leftEdge + this.Columns[columnIndex].MinimumThickness - this.mouseBarOffset - 1; + clip.Width = this.layout.Data.Right - leftEdge - 1; + int overflowWidth = clip.Right + this.mouseBarOffset - leftEdge - DataGridViewBand.maxBandThickness; + if (overflowWidth > 0) + { + clip.Width -= overflowWidth; + } + } + + return clip; + } + + /// + public virtual bool BeginEdit(bool selectAll) + { + if (this.ptCurrentCell.X == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_NoCurrentCell)); + } + + if (this.IsCurrentCellInEditMode) + { + return true; + } + + return BeginEditInternal(selectAll); + } + + private bool BeginEditInternal(bool selectAll) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_BeginEditNotReentrant)); + } + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit] = true; + Debug.Assert(this.ptCurrentCell.X >= 0 && this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y >= 0 && this.ptCurrentCell.Y < this.Rows.Count); + Debug.Assert(!this.IsCurrentCellInEditMode); + + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) || + !ColumnEditable(this.ptCurrentCell.X)) + { + return false; + } + + Type editControlType = dataGridViewCell.EditType; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + Type editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + if (editingCellInterface == null) + { + return false; + } + } + + DataGridViewCellCancelEventArgs dgvcce = new DataGridViewCellCancelEventArgs(this.ptCurrentCell.X, this.ptCurrentCell.Y); + OnCellBeginEdit(dgvcce); + if (dgvcce.Cancel) + { + return false; + } + Debug.Assert(!this.IsCurrentCellInEditMode); + + if (this.ptCurrentCell.X > -1) + { + DataGridViewCell previousCurrentCell = dataGridViewCell; + dataGridViewCell = this.CurrentCellInternal; + if (previousCurrentCell != dataGridViewCell) + { + // VSWhidbey 555494. The new current cell can be a whole different cell. + // In that case, all tests previously done are no longer valid. + if (IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) || + !ColumnEditable(this.ptCurrentCell.X)) + { + return false; + } + + editControlType = dataGridViewCell.EditType; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + Type editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + if (editingCellInterface == null) + { + return false; + } + } + } + } + else + { + return false; + } + + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + + if (editControlType == null) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] = true; + InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell); + ((IDataGridViewEditingCell)dataGridViewCell).PrepareEditingCellForEdit(selectAll); + return true; + } + + Type editingCtrlInterface = editControlType.GetInterface("System.Windows.Forms.IDataGridViewEditingControl"); + if (!editControlType.IsSubclassOf(Type.GetType("System.Windows.Forms.Control")) || + editingCtrlInterface == null) + { + throw new InvalidCastException(SR.GetString(SR.DataGridView_InvalidEditingControl)); + } + if (this.latestEditingControl != null && + editControlType.IsInstanceOfType(this.latestEditingControl) && + !this.latestEditingControl.GetType().IsSubclassOf(editControlType)) + { + this.editingControl = this.latestEditingControl; + Debug.Assert(((IDataGridViewEditingControl)this.editingControl).EditingControlDataGridView == this); + } + else + { + Debug.Assert(this.editingControl == null); + this.editingControl = (Control)SecurityUtils.SecureCreateInstance(editControlType); + Debug.Assert(this.editingControl != null); + + ((IDataGridViewEditingControl)this.editingControl).EditingControlDataGridView = this; + if (this.latestEditingControl != null) + { + this.latestEditingControl.Dispose(); + this.latestEditingControl = null; + } + } + + Debug.Assert(this.editingControl != null); + if (String.IsNullOrEmpty(this.editingControl.AccessibleName)) + { + this.editingControl.AccessibleName = SR.GetString(SR.DataGridView_AccEditingControlAccName); + } + this.editingControl.ImeMode = this.ImeMode; + + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = this.ptCurrentCell.Y; + + InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell); + + WireEditingControlEvents(); + + Debug.Assert(this.editingControl != null); + Debug.Assert(this.editingPanel != null); + DataGridViewEditingControlShowingEventArgs dgvese = new DataGridViewEditingControlShowingEventArgs(this.editingControl, dataGridViewCellStyle); + OnEditingControlShowing(dgvese); + Debug.Assert(dgvese.CellStyle != null); + if (this.editingPanel == null || this.editingControl == null) + { + return false; + } + this.editingPanel.BackColor = dgvese.CellStyle.BackColor; + ((IDataGridViewEditingControl)this.editingControl).ApplyCellStyleToEditingControl(dgvese.CellStyle); + + // Get rid of the tooltip if it's showing for the current cell + if (this.toolTipControl.Activated && this.ptToolTipCell == this.ptCurrentCell) + { + this.toolTipControl.Activate(false /*activate*/); + } + + PositionEditingControl(true, true, true); + + // Guarding against bugs in customer code. + // For example setting the CurrentCell to null in DataGridView_OnLostFocus(...) causes this.editingControl + // to become null. + if (this.editingPanel == null || this.editingControl == null) + { + return false; + } + else + { + ((IDataGridViewEditingControl)this.editingControl).PrepareEditingControlForEdit(selectAll); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + return true; + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit] = false; + } + } + + private void BeginRowHeadersResize(int mouseX, int mouseBarOffset) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.RowHeaders, this.layout.Data); + if (this.layout.TopLeftHeader.Width > 0) + { + clip = Rectangle.Union(this.layout.TopLeftHeader, clip); + } + if (this.RightToLeftInternal) + { + clip.X -= mouseBarOffset + 1; + clip.Width -= minimumRowHeadersWidth - 1; + // No need to limit the left edge of the cursor clip since maxHeadersThickness is very large. + } + else + { + clip.X += minimumRowHeadersWidth - mouseBarOffset - 1; + // No need to limit the right edge of the cursor clip since maxHeadersThickness is very large. + } + CaptureMouse(clip); + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] = true; + this.trackColAnchor = mouseX; + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastColSplitBar == -1); + this.currentColSplitBar = mouseX; + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + private void BeginRowResize(int mouseY, int mouseBarOffset, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.RowHeaders, this.layout.Data); + int topEdge = GetRowYFromIndex(index); + clip.Y = topEdge + this.Rows.SharedRow(index).GetMinimumHeight(index) - mouseBarOffset - 1; + clip.Height = this.layout.Data.Y + this.layout.Data.Height - topEdge - 1; + CaptureMouse(clip); + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] = true; + this.trackRowAnchor = mouseY; + this.trackRow = index; + + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastRowSplitBar == -1); + this.currentRowSplitBar = mouseY; + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void BuildInheritedColumnHeaderCellStyle(DataGridViewCellStyle inheritedCellStyle, DataGridViewCell cell) + { + Debug.Assert(inheritedCellStyle != null); + + DataGridViewCellStyle cellStyle = null; + if (cell.HasStyle) + { + cellStyle = cell.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle columnHeadersStyle = this.ColumnHeadersDefaultCellStyle; + Debug.Assert(columnHeadersStyle != null); + + DataGridViewCellStyle dataGridViewStyle = this.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = cellStyle.BackColor; + } + else if (!columnHeadersStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = columnHeadersStyle.BackColor; + } + else + { + inheritedCellStyle.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = cellStyle.ForeColor; + } + else if (!columnHeadersStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = columnHeadersStyle.ForeColor; + } + else + { + inheritedCellStyle.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (!columnHeadersStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = columnHeadersStyle.SelectionBackColor; + } + else + { + inheritedCellStyle.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (!columnHeadersStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = columnHeadersStyle.SelectionForeColor; + } + else + { + inheritedCellStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyle.Font = cellStyle.Font; + } + else if (columnHeadersStyle.Font != null) + { + inheritedCellStyle.Font = columnHeadersStyle.Font; + } + else + { + inheritedCellStyle.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = cellStyle.NullValue; + } + else if (!columnHeadersStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = columnHeadersStyle.NullValue; + } + else + { + inheritedCellStyle.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (!columnHeadersStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = columnHeadersStyle.DataSourceNullValue; + } + else + { + inheritedCellStyle.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyle.Format = cellStyle.Format; + } + else if (columnHeadersStyle.Format.Length != 0) + { + inheritedCellStyle.Format = columnHeadersStyle.Format; + } + else + { + inheritedCellStyle.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = cellStyle.FormatProvider; + } + else if (!columnHeadersStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = columnHeadersStyle.FormatProvider; + } + else + { + inheritedCellStyle.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = cellStyle.Alignment; + } + else if (columnHeadersStyle != null && columnHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = columnHeadersStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyle.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = cellStyle.WrapMode; + } + else if (columnHeadersStyle != null && columnHeadersStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = columnHeadersStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyle.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyle.Tag = cellStyle.Tag; + } + else if (columnHeadersStyle.Tag != null) + { + inheritedCellStyle.Tag = columnHeadersStyle.Tag; + } + else + { + inheritedCellStyle.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = cellStyle.Padding; + } + else if (columnHeadersStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = columnHeadersStyle.Padding; + } + else + { + inheritedCellStyle.PaddingInternal = dataGridViewStyle.Padding; + } + } + + private Rectangle CalcColRelocationFeedbackRect(int mouseX) + { + Rectangle r, inside = this.layout.ColumnHeaders; + if (this.layout.TopLeftHeader.Width > 0) + { + inside = Rectangle.Union(this.layout.TopLeftHeader, inside); + } + if (this.RightToLeftInternal) + { + r = new Rectangle(mouseX + this.mouseBarOffset - this.Columns[this.trackColumn].Thickness + 1, + inside.Y, + this.Columns[this.trackColumn].Thickness, + inside.Height); + r.X = Math.Max(inside.Left, r.X); + r.X = Math.Min(r.X, inside.Right - r.Width); + } + else + { + r = new Rectangle(mouseX + this.mouseBarOffset - 1, inside.Y, this.Columns[this.trackColumn].Thickness, inside.Height); + r.X = Math.Min(inside.Right - r.Width, r.X); + r.X = Math.Max(r.X, inside.Left); + } + return r; + } + + private Rectangle CalcColResizeFeedbackRect(int mouseX) + { + Rectangle inside = this.layout.Data; + Rectangle r = new Rectangle(mouseX + this.mouseBarOffset - 1, inside.Y, 3, inside.Height); + if (this.RightToLeftInternal) + { + r.X = Math.Max(inside.Left, r.X); + } + else + { + r.X = Math.Min(inside.Right - 3, r.X); + r.X = Math.Max(r.X, 0); + } + return r; + } + + private Rectangle CalcRowResizeFeedbackRect(int mouseY) + { + Rectangle inside = this.layout.Data; + Rectangle r = new Rectangle(inside.X, mouseY + this.mouseBarOffset - 1, inside.Width, 3); + r.Y = Math.Min(inside.Bottom - 3, r.Y); + r.Y = Math.Max(r.Y, 0); + return r; + } + + /// + public bool CancelEdit() + { + return CancelEdit(false /*endEdit, DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration*/); + } + + private bool CancelEdit(bool endEdit /*, DataGridViewDataErrorContexts context*/) + { + if (this.ptCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + + int oldCurrentCellX = this.ptCurrentCell.X; + DataGridViewDataErrorEventArgs dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, context*/); + + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + + if (dgvdee.Cancel) + { + return false; + } + } + + if (this.IsCurrentCellInEditMode) + { + if (endEdit && this.EditMode != DataGridViewEditMode.EditOnEnter && this.editingControl != null) + { + bool success = EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration, + DataGridViewValidateCellInternal.Never /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell unused here*/, + true /*resetAnchorCell unused here*/); + Debug.Assert(success); + } + else + { + DataGridViewDataErrorEventArgs dgvdee2 = null; + IDataGridViewEditingCell dataGridViewEditingCell = null; + try + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlFormattedValue = this.uneditedFormattedValue; + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + dataGridViewEditingCell = this.CurrentCellInternal as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.EditingCellFormattedValue = this.uneditedFormattedValue; + dataGridViewEditingCell.EditingCellValueChanged = false; + } + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee2 = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee2 != null) + { + OnDataErrorInternal(dgvdee2); + if (dgvdee2.ThrowException) + { + throw dgvdee2.Exception; + } + } + + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + } + else + { + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + else if (this.ptCurrentCell.X == -1 && this.Focused) + { + Debug.Assert((this.AllowUserToAddRowsInternal && this.Rows.Count == 1) || + (!this.AllowUserToAddRowsInternal && this.Rows.Count == 0)); + if (this.Rows.Count > 0) + { + if (this.Columns.Count > oldCurrentCellX && this.Columns[oldCurrentCellX].Visible) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex != -1) + { + bool success = SetAndSelectCurrentCellAddress(oldCurrentCellX, + rowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + else + { + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + } + } + } + return true; + } + + private DataGridViewDataErrorEventArgs CancelEditPrivate(/*ref DataGridViewCell dataGridViewCurrentCell, DataGridViewDataErrorContexts context*/) + { + bool currentCellDirty = this.IsCurrentCellDirty; + bool currentRowDirty = this.IsCurrentRowDirty; + + if (this.IsCurrentCellInEditMode) + { + /* Do not push original value back into the cell - VS Whidbey bug 328624 + Exception exception; + if (!PushFormattedValue(ref dataGridViewCurrentCell, this.uneditedFormattedValue, out exception)) + { + Debug.Assert(dataGridViewCurrentCell.RowIndex > -1); + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + dataGridViewCurrentCell.ColumnIndex, + dataGridViewCurrentCell.RowIndex, + // dataGridViewCurrentCell.Value, + // this.uneditedFormattedValue, + context); + dgvdee.Cancel = true; + OnDataErrorInternal(dgvdee); + return dgvdee; + } + */ + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + ((IDataGridViewEditingCell)this.CurrentCellInternal).EditingCellValueChanged = false; + } + this.IsCurrentCellDirtyInternal = false; + } + + if (this.DataSource != null || this.VirtualMode) + { + if ((currentRowDirty && !currentCellDirty) || + (this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged])) + { + bool discardNewRow = this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited]; + this.IsCurrentRowDirtyInternal = false; + if (this.VirtualMode) + { + QuestionEventArgs qe = new QuestionEventArgs(discardNewRow); + OnCancelRowEdit(qe); + discardNewRow &= qe.Response; + } + if (this.DataSource != null) + { + int oldCurrentCellX = this.ptCurrentCell.X; + this.dataConnection.CancelRowEdit(true /*restoreRow*/, this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited]/*addNewFinished*/); + if (this.dataConnection.List.Count == 0) + { + // There are no rows left in the back end. + if (currentCellDirty || this.ptCurrentCell.Y == -1 || this.ptCurrentCell.X == -1) + { + if (!IsColumnOutOfBounds(oldCurrentCellX) && this.Columns[oldCurrentCellX].Visible) + { + Debug.Assert(0 == this.Rows.GetFirstRow(DataGridViewElementStates.Visible)); + // Setting the current cell to the current column in the first row + // will create the new row if the user was editing the cell. + SetAndSelectCurrentCellAddress(oldCurrentCellX, + 0, + true, /*setAnchorCellAddress*/ + false, /*validateCurrentCell*/ + false, /*throughMouseClick*/ + true /*clearSelecttion*/, + false /*forceCurrentCellSelection (unused)*/); + } + } + else + { + // Else, simply add a new row. + this.dataConnection.OnNewRowNeeded(); + } + } + + // CancelRowEdit discarded the new row if we were editing the new row. + discardNewRow = false; + } + if (this.ptCurrentCell.Y > -1) + { + InvalidateRowPrivate(this.ptCurrentCell.Y); + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + if (this.IsCurrentCellInEditMode) + { + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + if (this.editingControl != null) + { + InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell); + if (((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell); + } + } + } + if (discardNewRow && this.ptCurrentCell.Y == this.newRowIndex - 1) + { + DiscardNewRow(); + } + } + } + else + { + if (!this.IsCurrentRowDirty && + this.ptCurrentCell.Y == this.newRowIndex - 1 && + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing]) + { + DiscardNewRow(); + } + } + return null; + } + + internal bool CancelToolTipPopup(ToolTip toolTip) + { + if (this.toolTipControl.ToolTip == toolTip) + { + // Our own tool tip wants to show its text. + return false; + } + else + { + // This is an external tool tip control which wants to show a tool tip over the DataGridView. + // ToolTips from the data Grid view ( the error text, or the formatted text that does not fit in, or the tool tip text from the cell) + // and the ShowCellToolTips take precedence over the external tool tip. + return String.IsNullOrEmpty(this.toolTipCaption) && this.ShowCellToolTips; + } + } + + private bool CanSort(DataGridViewColumn dataGridViewColumn) + { + return dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && (!VirtualMode || dataGridViewColumn.IsDataBound); + } + + private bool IsSortable(DataGridViewColumn dataGridViewColumn) + { + return dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable && (!VirtualMode || dataGridViewColumn.IsDataBound); + } + + // determines if a data bound cell can be validated or not + private bool CanValidateDataBoundDataGridViewCell(DataGridViewCell dataGridViewCurrentCell) + { + if (dataGridViewCurrentCell == null) + { + if (this.ptCurrentCell.X > -1) + { + dataGridViewCurrentCell = this.CurrentCellInternal; + } + } + + if (dataGridViewCurrentCell == null) + { + return true; + } + + Debug.Assert(dataGridViewCurrentCell.OwningColumn != null); + + if (!dataGridViewCurrentCell.OwningColumn.IsDataBoundInternal) + { + // we are not data bound so it's not up to us to decide to stop validation + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + // Dispose is not the place to validate data. Also, chances are that the data source is also disposing itself. + return false; + } + + if (this.dataConnection == null) + { + // if there is no dataConnection then it is not up to this function to stop validation. + return true; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // // + // FROM HERE DOWN THE DATA CONNECTION DETERMINES IF THE DATAGRIDVIEW SHOULD VALIDATE THE CELL. // + // // + ///////////////////////////////////////////////////////////////////////////////////////////////// + + if (this.dataConnection.ProcessingMetaDataChanges) + { + // don't validate a cell in a data bound column while the property descriptors change under us + return false; + } + + if (this.dataConnection.CancellingRowEdit && !this.dataConnection.RestoreRow) + { + // don't validate a cell in a data bound column while we are cancelling a row edit and the old row is not restored + return false; + } + + if (this.dataConnection.CurrencyManager.Count <= this.ptCurrentCell.Y) + { + // don't validate a row beyond the last row in the back end list + return false; + } + + if (this.dataConnection.PositionChangingOutsideDataGridView) + { + // the position changed outside the data grid view and we haven't validated the data grid view cell already + // we can't validate it now because if the user cancels validation then we end up + // with a position different than the position in the currency manager + return false; + } + + if (this.dataConnection.ListWasReset) + { + // The list was reset outside data grid view. + // We can't validate it now because we would be pushing a value into a different object ( possibly located in a different list ). + return false; + } + + return true; + } + + private void CaptureMouse(Rectangle cursorClip) + { + this.CaptureInternal = true; + Cursor.ClipInternal = RectangleToScreen(cursorClip); + } + + private void ClearRegionCache() + { + this.cachedScrollableRegion = null; + } + + /// + public void ClearSelection() + { + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + + bool switchedToBulkPaint = false; + + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold || + this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + RemoveIndividuallySelectedCells(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and start using this.selectedBandIndexes in this SelectionMode, + // we'll have to clear those selections too. + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + while(this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + + // Force repainting of the current collumn's header cell to remove highlighting + if (this.ptCurrentCell.X != -1 && + this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && + AccessibilityImprovements.Level2) + { + InvalidateCellPrivate(this.ptCurrentCell.X, -1); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + while(this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + break; + } + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + /// + protected void ClearSelection(int columnIndexException, int rowIndexException, bool selectExceptionElement) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (columnIndexException < 0 || columnIndexException >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndexException"); + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (columnIndexException < -1 || columnIndexException >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndexException"); + } + break; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (rowIndexException < 0 || rowIndexException >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexException"); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (rowIndexException < -1 || rowIndexException >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexException"); + } + break; + } + } + + // Clears all selection except the row/column/cell specified as parameter + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + + bool switchedToBulkPaint = false; + + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold || + this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and start using this.selectedBandIndexes in this SelectionMode, + // we'll have to clear those selections too. + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != rowIndexException) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != columnIndexException) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + } + break; + } + } + if (selectExceptionElement) + { + SetSelectedElementCore(columnIndexException, rowIndexException, true); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private bool ColumnEditable(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count, "Invalid columnIndex: " + columnIndex ); + if (this.Columns[columnIndex].IsDataBound && + this.dataConnection != null && + !this.dataConnection.AllowEdit) + { + return false; + } + return true; + } + + private bool ColumnNeedsDisplayedState(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + + if (!dataGridViewColumn.Visible) + { + return false; + } + + if (dataGridViewColumn.Frozen) + { + DataGridViewColumn firstVisibleFrozenColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(firstVisibleFrozenColumn != null); + if (firstVisibleFrozenColumn.Index == dataGridViewColumn.Index) + { + return this.displayedBandsInfo.NumDisplayedFrozenCols > 0; + } + Debug.Assert(this.Columns.DisplayInOrder(firstVisibleFrozenColumn.Index, dataGridViewColumn.Index)); + return this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, firstVisibleFrozenColumn.Index, dataGridViewColumn.Index) < this.displayedBandsInfo.NumDisplayedFrozenCols; + } + else + { + int firstDisplayedScrollingColumnIndex = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (firstDisplayedScrollingColumnIndex != -1) + { + if (firstDisplayedScrollingColumnIndex == dataGridViewColumn.Index) + { + return this.displayedBandsInfo.NumDisplayedScrollingCols > 0; + } + if (this.Columns.DisplayInOrder(firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index)) + { + return this.Columns.GetColumnCount(DataGridViewElementStates.Visible, firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index) < this.displayedBandsInfo.NumDisplayedScrollingCols; + } + } + } + return false; + } + + private bool ColumnRelocationTarget(MouseEventArgs e, HitTestInfo hti, out int previousColumnIndex) + { + previousColumnIndex = -1; + if (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeader || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight) + { + Debug.Assert(hti.col != -1); + if (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeader) + { + int xColumnLeftEdge = GetColumnXFromIndex(hti.col); + int wColumn = this.Columns[hti.col].Width; + if ((this.RightToLeftInternal && e.X < xColumnLeftEdge - wColumn / 2) || + (!this.RightToLeftInternal && e.X > xColumnLeftEdge + wColumn / 2)) + { + // Insert column on the right of hti.col + previousColumnIndex = hti.col; + } + else + { + // Insert column on the left of hti.col + DataGridViewColumn dataGridViewColumnPrev = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnPrev != null) + { + previousColumnIndex = dataGridViewColumnPrev.Index; + } + } + } + else + { + previousColumnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight) ? + hti.col : hti.adjacentCol; + } + DataGridViewColumn dataGridViewColumnNext = null; + if (previousColumnIndex != -1) + { + dataGridViewColumnNext = this.Columns.GetNextColumn(this.Columns[previousColumnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + if (this.trackColumn != previousColumnIndex && + !(previousColumnIndex == -1 && hti.col == this.trackColumn) && + (dataGridViewColumnNext == null || this.trackColumn != dataGridViewColumnNext.Index)) + { + return true; + } + } + else if (hti.typeInternal == DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight) + { + Debug.Assert(hti.col != -1); + if (hti.col != this.trackColumn) + { + return true; + } + } + return false; + } + + private static bool ColumnsDisplayInOrder(int columnIndex1, + int columnDisplayIndex1, + int columnIndex2, + int columnDisplayIndex2) + { + return columnDisplayIndex1 < columnDisplayIndex2 || + (columnDisplayIndex1 == columnDisplayIndex2 && columnIndex1 < columnIndex2); + } + + /// + public bool CommitEdit(DataGridViewDataErrorContexts context) + { + if (this.IsCurrentCellInEditMode) + { + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + context, + DataGridViewValidateCellInternal.Never, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + return true; + } + + private DataGridViewDataErrorEventArgs CommitEdit(ref DataGridViewCell dataGridViewCurrentCell, + DataGridViewDataErrorContexts context, + DataGridViewValidateCellInternal validateCell, + bool fireCellLeave, + bool fireCellEnter, + bool fireRowLeave, + bool fireRowEnter, + bool fireLeave) + { + if (validateCell == DataGridViewValidateCellInternal.Always) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (fireCellLeave) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellLeave(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (fireRowLeave) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnRowLeave(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (fireLeave) + { + base.OnLeave(EventArgs.Empty); + + // Microsoft: can we be smarter about this? What if validating the current cell below forces a repaint on the cell? + // we would end up repainting the current cell twice. + // + // invalidate the current cell so the data grid view does not paint the focus rectangle any longer + if (this.ptCurrentCell.X > -1 && this.ptCurrentCell.Y > -1) + { + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + // OnCellValidating returns true if the dev cancelled the validation. + bool validateFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell); + if (validateFormattedValue) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + if (OnCellValidating(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, context)) + { + if (fireRowEnter) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnRowEnter(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + } + if (fireCellEnter) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellEnter(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(null, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + // null, + // null, + context); + dgvdee.Cancel = true; + return dgvdee; + } + + if (!this.IsCurrentCellInEditMode || !this.IsCurrentCellDirty) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellValidated(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + + if (this.ptCurrentCell.X == -1 || !this.IsCurrentCellInEditMode) + { + return null; + } + + Debug.Assert( + ( + (this.editingControl != null && ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged) || + (this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] && ((IDataGridViewEditingCell)this.CurrentCellInternal).EditingCellValueChanged) + ) == this.IsCurrentCellDirty || + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges]); + + if (this.IsCurrentCellDirty) + { + bool validateAndPushFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell); + if (validateAndPushFormattedValue) + { + if (validateCell == DataGridViewValidateCellInternal.WhenChanged) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (this.ptCurrentCell.X == -1) + { + return null; + } + if (OnCellValidating(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, context)) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(null, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + context); + dgvdee.Cancel = true; + return dgvdee; + } + } + + Exception exception; + object formattedValue; + + if (this.editingControl != null) + { + formattedValue = ((IDataGridViewEditingControl)this.editingControl).GetEditingControlFormattedValue(context); + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + formattedValue = ((IDataGridViewEditingCell)this.CurrentCellInternal).GetEditingCellFormattedValue(context); + } + + if (!PushFormattedValue(ref dataGridViewCurrentCell, formattedValue, out exception)) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + // dataGridViewCurrentCell.Value, + // formattedValue, + context); + dgvdee.Cancel = true; + OnDataErrorInternal(dgvdee); + return dgvdee; + } + if (!this.IsCurrentCellInEditMode) + { + return null; + } + this.uneditedFormattedValue = formattedValue; + } + + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + ((IDataGridViewEditingCell) this.CurrentCellInternal).EditingCellValueChanged = false; + } + this.IsCurrentCellDirtyInternal = false; + this.IsCurrentRowDirtyInternal = true; + + if (validateAndPushFormattedValue) + { + if (validateCell == DataGridViewValidateCellInternal.Always || + validateCell == DataGridViewValidateCellInternal.WhenChanged) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellValidated(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + return null; + } + + private bool CommitEdit(DataGridViewDataErrorContexts context, + bool forCurrentCellChange, + bool forCurrentRowChange) + { + // If we're already within a CellValidating event handler, don't try to commit the cell again. + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating]) + { + return false; + } + + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + context, + forCurrentCellChange ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.WhenChanged /*validateCell*/, + forCurrentCellChange /*fireCellLeave*/, + forCurrentCellChange /*fireCellEnter*/, + forCurrentRowChange /*fireRowLeave*/, + forCurrentRowChange /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, + DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll*/); // restore old value + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + + // See if we can leave the row + if (forCurrentRowChange && forCurrentCellChange) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (this.ptCurrentCell.X == -1) + { + return false; + } + int columnIndex = this.ptCurrentCell.X; + int rowIndex = this.ptCurrentCell.Y; + // OnRowValidating returns true when the row validation was cancelled. + if (OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex)) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnRowEnter(ref dataGridViewCurrentCell, columnIndex, rowIndex, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnCellEnter(ref dataGridViewCurrentCell, columnIndex, rowIndex); + return false; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex); + } + return true; + } + + private bool CommitEditForOperation(int columnIndex, int rowIndex, bool forCurrentCellChange) + { + if (forCurrentCellChange) + { + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + this.ptCurrentCell.Y != rowIndex /*fireRowLeave*/, + this.ptCurrentCell.Y != rowIndex /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (this.ptCurrentCell.Y != rowIndex && this.ptCurrentCell.Y != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + int columnIndexTmp = this.ptCurrentCell.X; + int rowIndexTmp = this.ptCurrentCell.Y; + if (OnRowValidating(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnRowEnter(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnCellEnter(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp); + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + + // Re-enter editing mode if needed + if (this.Focused && + (!this.IsCurrentCellInEditMode && (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)))) + { + BeginEditInternal(true /*selectAll*/); + } + + return false; + } + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnRowValidated(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp); + } + } + else + { + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + } + + // Row validation was not cancelled, but operation needs to be re-evaluated. + Debug.Assert(columnIndex < this.Columns.Count); + if (IsColumnOutOfBounds(columnIndex)) + { + return false; + } + if (rowIndex >= this.Rows.Count) + { + // CurrentCell was reset because the commit deleted row(s). + // Since the user wants to change the current cell, we don't + // want to end up with no CurrentCell. We pick the last visible + // row in the grid which may be the 'new row'. + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (forCurrentCellChange && + this.ptCurrentCell.X == -1 && + lastVisibleRowIndex != -1) + { + bool success = SetAndSelectCurrentCellAddress(columnIndex, lastVisibleRowIndex, true, false, false, false /*clearSelection*/, false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + // Interrupt operation because it has become invalid. + return false; + } + if (rowIndex > -1 && (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + // Interrupt operation because target row has become invisible. + return false; + } + return true; + } + + internal void CompleteCellsCollection(DataGridViewRow dataGridViewRow) + { + Debug.Assert(dataGridViewRow != null); + int cellsInCollection = dataGridViewRow.Cells.Count; + if (this.Columns.Count > cellsInCollection) + { + int cellCount = 0; + DataGridViewCell[] cells = new DataGridViewCell[this.Columns.Count - cellsInCollection]; + for (int columnIndex = cellsInCollection; columnIndex < this.Columns.Count; columnIndex++) + { + if (this.Columns[columnIndex].CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AColumnHasNoCellTemplate)); + } + DataGridViewCell dgvcNew = (DataGridViewCell) this.Columns[columnIndex].CellTemplate.Clone(); + cells[cellCount] = dgvcNew; + cellCount ++; + } + dataGridViewRow.Cells.AddRange(cells); + } + } + + /// + /// Determines which column is the first visible scrolling + /// column given the object's horizontalOffset. + /// + private int ComputeFirstVisibleScrollingColumn() + { + if (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) >= this.layout.Data.Width) + { + // Not enough room for scrolling columns. + this.negOffset = 0; + return -1; + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + + if (this.horizontalOffset == 0) + { + this.negOffset = 0; + return (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + } + + int cx = 0; + while (dataGridViewColumn != null) + { + cx += dataGridViewColumn.Thickness; + if (cx > this.horizontalOffset) + { + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + + if (dataGridViewColumn == null) + { + Debug.Assert(cx <= this.horizontalOffset); + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (dataGridViewColumn == null) + { + this.negOffset = 0; + return -1; + } + else + { + if (this.negOffset != this.horizontalOffset) + { + this.negOffset = 0; + } + return dataGridViewColumn.Index; + } + } + else + { + this.negOffset = dataGridViewColumn.Thickness - (cx - this.horizontalOffset); + return dataGridViewColumn.Index; + } + } + + private int ComputeHeightOfFittingTrailingScrollingRows(int totalVisibleFrozenHeight) + { + // + int displayHeight = this.layout.Data.Height - totalVisibleFrozenHeight; + int rowHeight = 0, rowHeights = 0; + int indexTmp = this.Rows.Count; + + if (indexTmp == 0 || displayHeight <= 0) + { + return 0; + } + else + { + indexTmp--; + } + + DataGridViewElementStates rowState = this.Rows.GetRowState(indexTmp); + if ((rowState & DataGridViewElementStates.Frozen) != 0) + { + return 0; + } + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + if (indexTmp != -1) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (rowHeight > displayHeight) + { + return rowHeight; + } + } + + while (indexTmp != -1 && rowHeights + rowHeight <= displayHeight) + { + rowHeights += rowHeight; + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (indexTmp != -1) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + } + } + + return rowHeights; + } + + private int ComputeHeightOfScrolledOffRows() + { + // + int height = 0; + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowIndex != -1) + { + while (rowIndex != this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + Debug.Assert(rowIndex < this.displayedBandsInfo.FirstDisplayedScrollingRow); + height += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + return height; + } + + private int ComputeHeightOfTrailingScrollingRows() + { + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + return this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, lastVisibleRowIndex) + + this.Rows.SharedRow(lastVisibleRowIndex).GetHeight(lastVisibleRowIndex); + } + return 0; + } + + private bool ComputeLayout() + { + ClearRegionCache(); + + LayoutData newLayout = new LayoutData(this.layout); + Rectangle oldResizeRect = this.layout.ResizeBoxRect; + + // Inside region + if (this.normalClientRectangle.Width > 0 || this.normalClientRectangle.Height > 0) + { + newLayout.Inside = this.normalClientRectangle; + } + else + { + newLayout.Inside = this.ClientRectangle; + } + Rectangle inside = newLayout.Inside; + int borderWidth = this.BorderWidth; + inside.Inflate(-borderWidth, -borderWidth); + if (inside.Height < 0) + { + inside.Height = 0; + } + if (inside.Width < 0) + { + inside.Width = 0; + } + + Rectangle insideLeft = inside; + + // Headers + if (this.layout.ColumnHeadersVisible) + { + Rectangle colHeaders = insideLeft; + colHeaders.Height = Math.Min(this.columnHeadersHeight, colHeaders.Height); + insideLeft.Y += colHeaders.Height; + insideLeft.Height -= colHeaders.Height; + Debug.Assert(insideLeft.Height >= 0); + newLayout.ColumnHeaders = colHeaders; + } + else + { + newLayout.ColumnHeaders = Rectangle.Empty; + } + + if (this.layout.RowHeadersVisible) + { + Rectangle rowHeaders = insideLeft; + rowHeaders.Width = Math.Min(this.rowHeadersWidth, rowHeaders.Width); + if (this.RightToLeftInternal) + { + rowHeaders.X += insideLeft.Width - rowHeaders.Width; + } + else + { + insideLeft.X += rowHeaders.Width; + } + insideLeft.Width -= rowHeaders.Width; + Debug.Assert(insideLeft.Width >= 0); + newLayout.RowHeaders = rowHeaders; + + if (this.layout.ColumnHeadersVisible) + { + Rectangle topLeft; + Rectangle colHeaders = newLayout.ColumnHeaders; + topLeft = colHeaders; + topLeft.Width = Math.Min(this.rowHeadersWidth, topLeft.Width); + colHeaders.Width -= topLeft.Width; + if (this.RightToLeftInternal) + { + topLeft.X += insideLeft.Width; + } + else + { + colHeaders.X += topLeft.Width; + } + Debug.Assert(colHeaders.Width >= 0); + newLayout.TopLeftHeader = topLeft; + newLayout.ColumnHeaders = colHeaders; + } + else + { + newLayout.TopLeftHeader = Rectangle.Empty; + } + } + else + { + newLayout.RowHeaders = Rectangle.Empty; + newLayout.TopLeftHeader = Rectangle.Empty; + } + + // Adjust insideLeft in case static top / left edge needs to be painted + if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + insideLeft.X++; + } + if (insideLeft.Width > 0) + { + insideLeft.Width--; + } + } + if (this.SingleHorizontalBorderAdded) + { + insideLeft.Y++; + if (insideLeft.Height > 0) + { + insideLeft.Height--; + } + } + + // Data region + newLayout.Data = insideLeft; + newLayout.Inside = inside; + + Debug.Assert(newLayout.Data.X >= 0); + Debug.Assert(newLayout.Data.Y >= 0); + Debug.Assert(newLayout.Data.Width >= 0); + Debug.Assert(newLayout.Data.Height >= 0); + + this.layout = newLayout; + this.layout.dirty = false; + + bool columnsAdjusted = AdjustFillingColumns(); + + this.layout = newLayout; + Debug.Assert(!this.layout.dirty); + LayoutScrollBars(); + + // if the user shrank the grid client area, then OnResize invalidated the old + // resize area. however, we need to invalidate the left upper corner in the new ResizeArea + // note that we can't take the Invalidate call from the OnResize method, because if the + // user enlarges the form then the old area will not be invalidated. + // + if (!oldResizeRect.Equals(this.layout.ResizeBoxRect) && !this.layout.ResizeBoxRect.IsEmpty) + { + Invalidate(this.layout.ResizeBoxRect); + } + + return columnsAdjusted; + } + + private void ComputeLayoutShortcut(bool computeVisibleRows) + { + // Called instead of ComputeLayout when a row is added, inserted or deleted beyond the limits of + // the layout.Data area. + // this.layout is unchanged - only the potential vertical scrollbar is affected. + + if (computeVisibleRows) + { + ComputeVisibleRows(); + } + #if DEBUG + else + { + int oldNumTotallyVisibleFrozenRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + int oldNumVisibleScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + int oldNumTotallyVisibleScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + ComputeVisibleRows(); + Debug.Assert(oldNumTotallyVisibleFrozenRows == this.displayedBandsInfo.NumTotallyDisplayedFrozenRows); + Debug.Assert(oldNumVisibleScrollingRows == this.displayedBandsInfo.NumDisplayedScrollingRows); + Debug.Assert(oldNumTotallyVisibleScrollingRows == this.displayedBandsInfo.NumTotallyDisplayedScrollingRows); + Debug.Assert(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + #endif + + #if DEBUG + int newFirstVisibleScrollingCol = ComputeFirstVisibleScrollingColumn(); + Debug.Assert(newFirstVisibleScrollingCol == this.displayedBandsInfo.FirstDisplayedScrollingCol); + + int oldLastTotallyVisibleScrollingCol = this.displayedBandsInfo.LastTotallyDisplayedScrollingCol; + int oldFirstVisibleScrollingCol = this.displayedBandsInfo.FirstDisplayedScrollingCol; + ComputeVisibleColumns(); + Debug.Assert(oldLastTotallyVisibleScrollingCol == this.displayedBandsInfo.LastTotallyDisplayedScrollingCol); + Debug.Assert(oldFirstVisibleScrollingCol == this.displayedBandsInfo.FirstDisplayedScrollingCol); + #endif + + if (this.vertScrollBar.Enabled) + { + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int oldVertScrollBarValue = this.vertScrollBar.Value; + int oldThumbHeight = Math.Max(((this.vertScrollBar.Height - 2*SystemInformation.VerticalScrollBarArrowHeight) * this.vertScrollBar.LargeChange) / this.vertScrollBar.Maximum, 8); + + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + Debug.Assert(this.vertScrollBar.Maximum > 0); + this.vertScrollBar.Value = ComputeHeightOfScrolledOffRows(); + this.vertScrollBar.LargeChange = this.layout.Data.Height - totalVisibleFrozenHeight; + this.verticalOffset = this.vertScrollBar.Value; + + if (this.vertScrollBar.Visible && + (oldVertScrollBarValue != this.verticalOffset || + oldThumbHeight != Math.Max(((this.vertScrollBar.Height - 2*SystemInformation.VerticalScrollBarArrowHeight) * this.vertScrollBar.LargeChange) / this.vertScrollBar.Maximum, 8))) + { + // Only update the vertical scroll bar is the thumb moved or resized. + this.vertScrollBar.Invalidate(); + } + Debug.Assert(this.verticalOffset == this.vertScrollBar.Value); + } + } + + /* Unused for now + private int ComputeScrolledOffRowCount(int scrolledOffRowsHeight) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowIndex == -1) + { + // No scrolling rows + return 0; + } + else + { + int height = 0; + int rowCount = 0; + while (rowIndex != -1 && height < scrolledOffRowsHeight) + { + height += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + if (height <= scrolledOffRowsHeight) + { + rowCount++; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + return rowCount; + } + } + */ + + private void ComputeVisibleColumns() + { + DataGridViewColumn dataGridViewColumn = null; + int numVisibleScrollingCols = 0, visibleScrollingColumnsTmp = 0; + int displayWidth = this.layout.Data.Width, cx = 0; + int numDisplayedFrozenCols = 0, firstDisplayedFrozenCol = -1, lastDisplayedFrozenCol = -1; + int firstDisplayedScrollingCol = this.displayedBandsInfo.FirstDisplayedScrollingCol; + + // the same problem with negative numbers: + // if the width passed in is negative, then return 0 + if (displayWidth <= 0 || this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = -1; + this.displayedBandsInfo.NumDisplayedFrozenCols = 0; + this.displayedBandsInfo.FirstDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + this.displayedBandsInfo.LastDisplayedFrozenCol = -1; + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + return; + } + + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.None); + while (dataGridViewColumn != null) + { + if (!dataGridViewColumn.Frozen && dataGridViewColumn.Visible) + { + break; + } + if (dataGridViewColumn.Visible) + { + if (firstDisplayedFrozenCol == -1) + { + firstDisplayedFrozenCol = dataGridViewColumn.Index; + } + cx += dataGridViewColumn.Width; + numDisplayedFrozenCols++; + lastDisplayedFrozenCol = dataGridViewColumn.Index; + if (cx >= displayWidth) + { + break; + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.None, DataGridViewElementStates.None); + } + + Debug.Assert(cx <= this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + + if (cx < displayWidth && firstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[firstDisplayedScrollingCol]; + if (dataGridViewColumn.Frozen) + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + this.negOffset = 0; + if (dataGridViewColumn == null) + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol; + this.displayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol; + this.displayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols; + this.displayedBandsInfo.FirstDisplayedScrollingCol = this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + return; + } + else + { + firstDisplayedScrollingCol = dataGridViewColumn.Index; + } + } + + cx -= this.negOffset; + while (cx < displayWidth && dataGridViewColumn != null) + { + cx += dataGridViewColumn.Thickness; + visibleScrollingColumnsTmp++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + numVisibleScrollingCols = visibleScrollingColumnsTmp; + + // if we inflate the data area then we paint columns to the left of firstDisplayedScrollingCol + if (cx < displayWidth) + { + bool invalidate = false; + Debug.Assert(firstDisplayedScrollingCol >= 0); + //first minimize value of this.negOffset + if (this.negOffset > 0) + { + invalidate = true; + if (displayWidth - cx > this.negOffset) + { + cx += this.negOffset; + this.horizontalOffset -= this.negOffset; + this.negOffset = 0; + } + else + { + this.horizontalOffset -= displayWidth - cx; + this.negOffset -= displayWidth - cx; + cx = displayWidth; + } + } + // second try to scroll entire columns + if (cx < displayWidth && this.horizontalOffset > 0) + { + Debug.Assert(this.negOffset == 0); + dataGridViewColumn = this.Columns.GetPreviousColumn((this.Columns[firstDisplayedScrollingCol]), + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && cx + dataGridViewColumn.Thickness <= displayWidth) + { + cx += dataGridViewColumn.Thickness; + visibleScrollingColumnsTmp++; + invalidate = true; + firstDisplayedScrollingCol = dataGridViewColumn.Index; + this.horizontalOffset -= dataGridViewColumn.Thickness; + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + // third try to partially scroll in first scrolled off column + if (cx < displayWidth && this.horizontalOffset > 0 && firstDisplayedScrollingCol != 0) + { + Debug.Assert(this.negOffset == 0); + dataGridViewColumn = this.Columns.GetPreviousColumn((this.Columns[firstDisplayedScrollingCol]), + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Thickness > displayWidth - cx); + firstDisplayedScrollingCol = dataGridViewColumn.Index; + this.negOffset = dataGridViewColumn.Thickness - displayWidth + cx; + this.horizontalOffset -= displayWidth - cx; + visibleScrollingColumnsTmp++; + invalidate = true; + cx = displayWidth; + Debug.Assert(this.negOffset == GetNegOffsetFromHorizontalOffset(this.horizontalOffset)); + } + + // update the number of visible columns to the new reality + Debug.Assert(numVisibleScrollingCols <= visibleScrollingColumnsTmp, "the number of displayed columns can only grow"); + numVisibleScrollingCols = visibleScrollingColumnsTmp; + + if (invalidate) + { + InvalidateData(); + Invalidate(this.layout.ColumnHeaders); + } + } + + int jumpFromFirstVisibleScrollingCol = numVisibleScrollingCols - 1; + if (cx > displayWidth) + { + jumpFromFirstVisibleScrollingCol--; + } + + Debug.Assert(jumpFromFirstVisibleScrollingCol >= -1); + + if (jumpFromFirstVisibleScrollingCol < 0) + { + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; // no totally visible scrolling column at all + } + else + { + Debug.Assert(firstDisplayedScrollingCol >= 0); + dataGridViewColumn = this.Columns[firstDisplayedScrollingCol]; + for (int jump = 0; jump < jumpFromFirstVisibleScrollingCol; jump++) + { + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + } + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = dataGridViewColumn.Index; + } + } + else + { + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + } + this.displayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol; + this.displayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol; + this.displayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols; + this.displayedBandsInfo.FirstDisplayedScrollingCol = firstDisplayedScrollingCol; + this.displayedBandsInfo.NumDisplayedScrollingCols = numVisibleScrollingCols; + Debug.Assert((this.displayedBandsInfo.NumDisplayedScrollingCols > 0 && this.displayedBandsInfo.FirstDisplayedScrollingCol != -1) || + (this.displayedBandsInfo.NumDisplayedScrollingCols == 0 && this.displayedBandsInfo.FirstDisplayedScrollingCol == -1)); + } + + private void ComputeVisibleRows() + { + int firstDisplayedFrozenRow = -1; + int firstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int lastDisplayedFrozenRow = -1, lastDisplayedScrollingRow = -1; + int numTotallyDisplayedFrozenRows = 0; + int displayHeight = this.layout.Data.Height; + int cy = 0; + int visibleScrollingRows = 0; + int nRows = this.Rows.Count; + int rowIndex; + + // when minimizing the dataGridView window, we will get negative values for the + // layout.Data.Width and layout.Data.Height ( is this a bug or not? if layout.Data.Height == 0 in that case, + // the old code would have worked ) + // + // if this is the case, set numTotallyDisplayedFrozenRows = numDisplayedScrollingRows = numTotallyDisplayedScrollingRows = 0; + // + if (displayHeight <= 0 || nRows == 0) + { + this.displayedBandsInfo.NumDisplayedFrozenRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = + this.displayedBandsInfo.NumDisplayedScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + this.displayedBandsInfo.FirstDisplayedFrozenRow = this.displayedBandsInfo.FirstDisplayedScrollingRow = + this.displayedBandsInfo.LastDisplayedFrozenRow = this.displayedBandsInfo.LastDisplayedScrollingRow = -1; + return; + } + + for (rowIndex = 0; rowIndex < nRows; rowIndex++) + { + Debug.Assert(cy < displayHeight); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Frozen) == 0 && + (rowState & DataGridViewElementStates.Visible) != 0) + { + break; + } + if ((rowState & DataGridViewElementStates.Visible) != 0) + { + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + if (firstDisplayedFrozenRow == -1) + { + firstDisplayedFrozenRow = rowIndex; + } + lastDisplayedFrozenRow = rowIndex; + if (cy <= displayHeight) + { + numTotallyDisplayedFrozenRows++; + } + if (cy >= displayHeight) + { + break; + } + } + } + + if (cy > displayHeight) + { + this.displayedBandsInfo.NumDisplayedFrozenRows = numTotallyDisplayedFrozenRows + 1; + } + else + { + this.displayedBandsInfo.NumDisplayedFrozenRows = numTotallyDisplayedFrozenRows; + } + + // loop exited when: + // - all rows are frozen and fit in displayHeight: rowIndex == nRows, cy <= displayHeight + // - rowIndex is not frozen: rowIndex < nRows, cy <= displayHeight + // - there are more frozen rows than can fit in displayHeight: rowIndex <= nRows, cy > displayHeight + + if (cy < displayHeight && rowIndex < nRows) + { + if (firstDisplayedScrollingRow == -1) + { + firstDisplayedScrollingRow = rowIndex; + } + + while (firstDisplayedScrollingRow < nRows && + ( + (this.Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Frozen) != 0 || + (this.Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Visible) == 0 + ) + ) + { + firstDisplayedScrollingRow++; + } + + for (int i = firstDisplayedScrollingRow; i < nRows; i++) + { + if ((this.Rows.GetRowState(i) & DataGridViewElementStates.Visible) != 0) + { + cy += this.Rows.SharedRow(i).GetHeight(i); + visibleScrollingRows++; + lastDisplayedScrollingRow = i; + } + if (cy >= displayHeight) + { + break; + } + } + + if (cy < displayHeight) + { + for (int i = firstDisplayedScrollingRow - 1; i >= numTotallyDisplayedFrozenRows; i--) + { + if ((this.Rows.GetRowState(i) & (DataGridViewElementStates.Frozen | DataGridViewElementStates.Visible)) == DataGridViewElementStates.Visible) + { + int height = this.Rows.SharedRow(i).GetHeight(i); + if (cy + height > displayHeight) + { + break; + } + cy += height; + firstDisplayedScrollingRow = i; + visibleScrollingRows++; + lastDisplayedScrollingRow = i; + } + } + } + + this.displayedBandsInfo.NumDisplayedScrollingRows = visibleScrollingRows; + if (cy > displayHeight) + { + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = visibleScrollingRows - 1; + } + else + { + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = visibleScrollingRows; + } + if (visibleScrollingRows == 0) + { + firstDisplayedScrollingRow = -1; + Debug.Assert(lastDisplayedScrollingRow == -1); + } + } + else + { + this.displayedBandsInfo.NumDisplayedScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + firstDisplayedScrollingRow = -1; + } + + Debug.Assert(firstDisplayedFrozenRow < nRows, "firstDisplayedFrozenRow larger than number of rows"); + Debug.Assert(lastDisplayedFrozenRow < nRows, "lastDisplayedFrozenRow larger than number of rows"); + Debug.Assert(lastDisplayedScrollingRow < nRows, "lastDisplayedScrollingRow larger than number of rows"); + + this.displayedBandsInfo.FirstDisplayedFrozenRow = firstDisplayedFrozenRow; + this.displayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRow; + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = numTotallyDisplayedFrozenRows; + this.displayedBandsInfo.LastDisplayedFrozenRow = lastDisplayedFrozenRow; + this.displayedBandsInfo.LastDisplayedScrollingRow = lastDisplayedScrollingRow; + + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedFrozenRows >= 0, "the number of visible frozen rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.NumDisplayedScrollingRows >= 0, "the number of visible scrolling rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedScrollingRows >= 0, "the number of totally visible scrolling rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow < nRows, "firstDisplayedScrollingRow larger than number of rows"); + } + + private Point ConvertCellToGridCoord(int columnIndex, int rowIndex, int x, int y) + { + int columnX, rowY; + if (columnIndex > -1) + { + columnX = GetColumnXFromIndex(columnIndex); + if (this.RightToLeftInternal) + { + columnX -= this.Columns[columnIndex].Width; + } + } + else + { + if (this.RightToLeftInternal) + { + columnX = this.layout.RowHeaders.Left - 1; + } + else + { + columnX = this.layout.RowHeaders.Left; + } + } + + if (rowIndex > -1) + { + rowY = GetRowYFromIndex(rowIndex); + } + else + { + rowY = this.layout.ColumnHeaders.Top; + } + + return new Point(columnX + x, rowY + y); + } + + private void CorrectColumnDisplayIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn) + { + // Column indexes have already been adjusted. + // This column has already been detached and has retained its old Index and DisplayIndex + + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == null); + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // All remaining columns with a DisplayIndex greater than dataGridViewColumn.DisplayIndex need to be decremented + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.DisplayIndex > dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + // Now raise all the OnColumnDisplayIndexChanged events + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + FlushDisplayIndexChanged(false /*raiseEvent*/); + } + } + + private void CorrectColumnDisplayIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == this); + // dataGridViewColumn.DisplayIndex has been set already. + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0); + Debug.Assert(dataGridViewColumn.DisplayIndex < this.Columns.Count); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // All other columns with a DisplayIndex equal or greater than dataGridViewColumn.DisplayIndex need to be incremented + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp != dataGridViewColumn && dataGridViewColumnTmp.DisplayIndex >= dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + // Now raise all the OnColumnDisplayIndexChanged events + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + FlushDisplayIndexChanged(false /*raiseEvent*/); + } + } + + private void CorrectColumnFrozenState(DataGridViewColumn dataGridViewColumn, int anticipatedColumnIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(anticipatedColumnIndex >= 0 && anticipatedColumnIndex <= this.Columns.Count); + + int anticipatedColumnDisplayIndex; + if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex > this.Columns.Count) + { + anticipatedColumnDisplayIndex = anticipatedColumnIndex; // By default, we pick the Index as the DisplayIndex. + } + else + { + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0 && dataGridViewColumn.DisplayIndex <= this.Columns.Count); + anticipatedColumnDisplayIndex = dataGridViewColumn.DisplayIndex; // The specified DisplayIndex is just fine. + } + + DataGridViewColumn dataGridViewColumnPrev; + int displayIndex = anticipatedColumnDisplayIndex-1; + do + { + dataGridViewColumnPrev = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex--; + } + while (displayIndex >= 0 && (dataGridViewColumnPrev == null || !dataGridViewColumnPrev.Visible)); + if (dataGridViewColumnPrev != null && !dataGridViewColumnPrev.Frozen && dataGridViewColumn.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenColumn)); + } + else + { + DataGridViewColumn dataGridViewColumnNext; + displayIndex = anticipatedColumnDisplayIndex; + do + { + dataGridViewColumnNext = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex++; + } + while (displayIndex < this.Columns.Count && (dataGridViewColumnNext == null || !dataGridViewColumnNext.Visible)); + if (dataGridViewColumnNext != null && dataGridViewColumnNext.Frozen && !dataGridViewColumn.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenColumn)); + } + } + } + + private void CorrectColumnFrozenStates(DataGridViewColumn[] dataGridViewColumns) + { + DataGridView dataGridViewTmp = new DataGridView(); + DataGridViewColumn dataGridViewColumnClone; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + dataGridViewColumnClone = (DataGridViewColumn) dataGridViewColumn.Clone(); + // DataGridViewColumn.Clone does not replicate the DisplayIndex value. + dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex; + dataGridViewTmp.Columns.Add(dataGridViewColumnClone); + } + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + dataGridViewColumnClone = (DataGridViewColumn) dataGridViewColumn.Clone(); + dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex; + dataGridViewTmp.Columns.Add(dataGridViewColumnClone); + } + } + + private void CorrectColumnFrozenStates(DataGridViewColumn dataGridViewColumn, bool frozenStateChanging) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumn dataGridViewColumnTmp; + if ((dataGridViewColumn.Frozen && !frozenStateChanging) || + (!dataGridViewColumn.Frozen && frozenStateChanging)) + { + // make sure the previous visible columns are frozen as well + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + if (dataGridViewColumnTmp == null) + { + DataGridViewColumn dataGridViewColumnFirst = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumnFirst != dataGridViewColumn) + { + dataGridViewColumnTmp = dataGridViewColumnFirst; + } + } + while (dataGridViewColumnTmp != null && this.Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index)) + { + dataGridViewColumnTmp.Frozen = true; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + else + { + // make sure the next visible columns are not frozen + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (dataGridViewColumnTmp == null) + { + DataGridViewColumn dataGridViewColumnLast = dataGridViewColumn; + do + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnLast, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnTmp != null) + { + dataGridViewColumnLast = dataGridViewColumnTmp; + } + } + while (dataGridViewColumnTmp != null); + if (dataGridViewColumnLast != dataGridViewColumn) + { + dataGridViewColumnTmp = dataGridViewColumnLast; + } + } + while (dataGridViewColumnTmp != null && this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + dataGridViewColumnTmp.Frozen = false; + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + } + + private void CorrectColumnFrozenStatesForMove(DataGridViewColumn dataGridViewColumn, int newDisplayIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex); + Debug.Assert(!this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); + + // No check necessary when: + // - column is invisible. + // - DisplayIndex decreases and column is frozen. + // - DisplayIndex increases and column is unfrozen. + + if (!dataGridViewColumn.Visible || + (newDisplayIndex < dataGridViewColumn.DisplayIndex && dataGridViewColumn.Frozen) || + (newDisplayIndex > dataGridViewColumn.DisplayIndex && !dataGridViewColumn.Frozen)) + { + return; + } + + int colCount = this.Columns.Count, displayIndex; + + if (newDisplayIndex < dataGridViewColumn.DisplayIndex) + { + // DisplayIndex decreases. + // Throw an exception if the visible unfrozen column is placed before a frozen column + // Get the closest visible column placed after the displaced column + DataGridViewColumn dataGridViewColumnNext; + displayIndex = newDisplayIndex; + do + { + dataGridViewColumnNext = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex++; + } + while (displayIndex < colCount && (dataGridViewColumnNext == null || dataGridViewColumnNext == dataGridViewColumn || !dataGridViewColumnNext.Visible)); + + if (dataGridViewColumnNext != null && dataGridViewColumnNext.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMoveNonFrozenColumn)); + } + } + else + { + // DisplayIndex increases. + // Throw an exception if the visible frozen column is placed after a non-frozen column + // Get the closest visible column placed before the displaced column + DataGridViewColumn dataGridViewColumnPrev; + displayIndex = newDisplayIndex; + do + { + dataGridViewColumnPrev = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex--; + } + while (displayIndex >= 0 && (dataGridViewColumnPrev == null || !dataGridViewColumnPrev.Visible)); + + if (dataGridViewColumnPrev != null && !dataGridViewColumnPrev.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMoveFrozenColumn)); + } + } + } + + private void CorrectColumnIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + for (int columnIndex = dataGridViewColumn.Index; columnIndex < this.Columns.Count; columnIndex++) + { + this.Columns[columnIndex].IndexInternal = this.Columns[columnIndex].Index - 1; + Debug.Assert(this.Columns[columnIndex].Index == columnIndex); + } + } + + private void CorrectColumnIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn, int insertionCount) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(insertionCount > 0); + for (int columnIndex = dataGridViewColumn.Index + insertionCount; columnIndex < this.Columns.Count; columnIndex++) + { + this.Columns[columnIndex].IndexInternal = columnIndex; + } + } + + private void CorrectFocus(bool onlyIfGridHasFocus) + { + if ((!onlyIfGridHasFocus || this.Focused) && this.editingControl != null) + { + Debug.Assert(this.CurrentCellInternal != null); + //Debug.Assert(this.editingControl.CanFocus); + this.editingControl.FocusInternal(); + } + } + + private void CorrectRowFrozenState(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, int anticipatedRowIndex) + { + Debug.Assert(dataGridViewRow != null); + Debug.Assert(anticipatedRowIndex >= 0 && anticipatedRowIndex <= this.Rows.Count); + + int previousRowIndex = this.Rows.GetPreviousRow(anticipatedRowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (previousRowIndex != -1 && + (this.Rows.GetRowState(previousRowIndex) & DataGridViewElementStates.Frozen) == 0 && + (rowState & DataGridViewElementStates.Frozen) != 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenRow)); + } + else + { + int nextRowIndex = this.Rows.GetNextRow((previousRowIndex == -1) ? anticipatedRowIndex - 1 : previousRowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (nextRowIndex != -1 && + (this.Rows.GetRowState(nextRowIndex) & DataGridViewElementStates.Frozen) != 0 && + (rowState & DataGridViewElementStates.Frozen) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenRow)); + } + } + } + + private void CorrectRowFrozenStates(DataGridViewRow[] dataGridViewRows, int rowIndexInserted) + { + bool nextVisibleRowPresent = false, previousRowFrozen = true, nextRowFrozen = false, currentRowFrozen; + + // Check if there is a visible row before the insertion point, and if it's frozen + int rowIndexTmp = this.Rows.GetPreviousRow(rowIndexInserted, DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + previousRowFrozen = (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + } + + // Check if there is a visible row at or after the insertion point, and if it's frozen + rowIndexTmp = this.Rows.GetNextRow(rowIndexInserted - 1, DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + nextVisibleRowPresent = true; + nextRowFrozen = (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + } + + for (int arrayIndex = 0; arrayIndex < dataGridViewRows.Length; arrayIndex++) + { + currentRowFrozen = ((DataGridViewRow)dataGridViewRows[arrayIndex]).Frozen; + if (!previousRowFrozen && currentRowFrozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenRow)); + } + previousRowFrozen = currentRowFrozen; + if (arrayIndex == dataGridViewRows.Length - 1 && + !currentRowFrozen && + nextVisibleRowPresent && + nextRowFrozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenRow)); + } + } + } + + private void CorrectRowFrozenStates(DataGridViewRow dataGridViewRow, int rowIndex, bool frozenStateChanging) + { + Debug.Assert(dataGridViewRow != null); + int rowIndexTmp; + if (((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) != 0 && !frozenStateChanging) || + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0 && frozenStateChanging)) + { + // make sure the previous visible rows are frozen as well + rowIndexTmp = this.Rows.GetPreviousRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (rowIndexTmp == -1) + { + int dataGridViewRowFirst = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (dataGridViewRowFirst != rowIndex) + { + rowIndexTmp = dataGridViewRowFirst; + } + } + while (rowIndexTmp != -1 && rowIndexTmp < rowIndex) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + else + { + // make sure the next visible rows are not frozen + rowIndexTmp = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (rowIndexTmp == -1) + { + int dataGridViewRowLast = rowIndex; + do + { + rowIndexTmp = this.Rows.GetNextRow(dataGridViewRowLast, + DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + dataGridViewRowLast = rowIndexTmp; + } + } + while (rowIndexTmp != -1); + if (dataGridViewRowLast != rowIndex) + { + rowIndexTmp = dataGridViewRowLast; + } + } + while (rowIndexTmp != -1 && rowIndexTmp > rowIndex) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, false); + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + } + } + + private void CorrectRowIndexesAfterDeletion(int rowIndexDeleted) + { + Debug.Assert(rowIndexDeleted >= 0); + int rowsCount = this.Rows.Count; + for (int rowIndex = rowIndexDeleted; rowIndex < rowsCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Index >= 0) + { + dataGridViewRow.IndexInternal = dataGridViewRow.Index - 1; + Debug.Assert(dataGridViewRow.Index == rowIndex); + } + } + + // Fix 'new' row index if existant + if (this.newRowIndex == rowIndexDeleted) + { + this.newRowIndex = -1; // No more 'new' row. + } + else if (this.newRowIndex != -1) + { + this.newRowIndex--; + } + } + + private void CorrectRowIndexesAfterInsertion(int rowIndexInserted, int insertionCount) + { + Debug.Assert(rowIndexInserted >= 0); + Debug.Assert(insertionCount > 0); + int rowsCount = this.Rows.Count; + for (int rowIndex = rowIndexInserted + insertionCount; rowIndex < rowsCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Index >= 0) + { + dataGridViewRow.IndexInternal = dataGridViewRow.Index + insertionCount; + Debug.Assert(dataGridViewRow.Index == rowIndex); + } + } + + // Lastly update the 'new' row index if needed. + if (this.newRowIndex != -1) + { + this.newRowIndex += insertionCount; + } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewAccessibleObject(this); + } + + /// + protected override Control.ControlCollection CreateControlsInstance() + { + return new DataGridViewControlCollection(this); + } + + /// + /// + /// Constructs the new instance of the Columns collection objects. Subclasses + /// should not call base.CreateColumnsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual DataGridViewColumnCollection CreateColumnsInstance() + { + return new DataGridViewColumnCollection(this); + } + + /// + /// + /// Constructs the new instance of the Rows collection objects. Subclasses + /// should not call base.CreateRowsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual DataGridViewRowCollection CreateRowsInstance() + { + return new DataGridViewRowCollection(this); + } + + private NativeMethods.RECT[] CreateScrollableRegion(Rectangle scroll) + { + if (this.cachedScrollableRegion != null) + { + return this.cachedScrollableRegion; + } + + using (Region region = new Region(scroll)) + { + IntPtr handle = IntPtr.Zero; + using (Graphics graphics = CreateGraphicsInternal()) + { + handle = region.GetHrgn(graphics); + } + if (handle != IntPtr.Zero) + { + this.cachedScrollableRegion = UnsafeNativeMethods.GetRectsFromRegion(handle); + + // SECREVIEW : This assert is safe since we created the native region. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try + { + region.ReleaseHrgn(handle); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + } + return this.cachedScrollableRegion; + } + + private void DiscardNewRow() + { + Debug.Assert(this.Rows.Count > 1); + Debug.Assert(this.newRowIndex != -1); + + DataGridViewRowCancelEventArgs dgvrce = new DataGridViewRowCancelEventArgs(this.Rows[this.newRowIndex]); + OnUserDeletingRow(dgvrce); + if (dgvrce.Cancel) + { + return; + } + + // Delete the 'new' row + Debug.Assert(this.newRowIndex == this.Rows.Count - 1); + DataGridViewRow dataGridViewRow = this.Rows[this.newRowIndex]; + this.Rows.RemoveAtInternal(this.newRowIndex, false /*force*/); + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + // CorrectRowIndexesAfterDeletion resets this.newRowIndex to -1. + Debug.Assert(this.newRowIndex == -1); + + if (this.AllowUserToAddRowsInternal) + { + this.newRowIndex = this.Rows.Count - 1; + Debug.Assert((this.Rows.GetRowState(this.newRowIndex) & DataGridViewElementStates.Visible) != 0); + Debug.Assert(this.ptCurrentCell.Y == this.newRowIndex); + + OnDefaultValuesNeeded(new DataGridViewRowEventArgs(this.Rows[this.newRowIndex])); + InvalidateRowPrivate(this.newRowIndex); + } + } + + private void DiscardZonesInScrollingArea(ref Rectangle rectScrollingArea, + int emptyBackgroundWidth, + int emptyBackgroundHeight, + int frozenVisibleRowsHeight, + bool discardFrozenColumns, + bool discardFrozenRows) + { + // Discard empty background + rectScrollingArea.Width -= emptyBackgroundWidth; + rectScrollingArea.Height -= emptyBackgroundHeight; + if (this.RightToLeftInternal) + { + rectScrollingArea.X += emptyBackgroundWidth; + } + + if (discardFrozenColumns) + { + // Discard frozen columns + int frozenVisibleColumnsWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (!this.RightToLeftInternal) + { + rectScrollingArea.X += frozenVisibleColumnsWidth; + } + rectScrollingArea.Width -= frozenVisibleColumnsWidth; + } + if (discardFrozenRows) + { + // Discard frozen rows + rectScrollingArea.Y += frozenVisibleRowsHeight; + rectScrollingArea.Height -= frozenVisibleRowsHeight; + } + } + + /// + public int DisplayedColumnCount(bool includePartialColumns) + { + int cxMax = this.layout.Data.Width, cx = 0; + int completeColumns = 0, partialColumns = 0; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && cx < cxMax) + { + partialColumns++; + cx += dataGridViewColumn.Thickness; + if (cx <= cxMax) + { + completeColumns++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + + if (cx < cxMax && this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + if (this.negOffset > 0) + { + cx -= this.negOffset; + completeColumns--; + } + dataGridViewColumn = (DataGridViewColumn)this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + while (dataGridViewColumn != null && cx < cxMax) + { + partialColumns++; + cx += dataGridViewColumn.Thickness; + if (cx <= cxMax) + { + completeColumns++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + + return includePartialColumns ? partialColumns : completeColumns; + } + + /// + public int DisplayedRowCount(bool includePartialRow) + { + return includePartialRow ? (this.displayedBandsInfo.NumDisplayedFrozenRows + this.displayedBandsInfo.NumDisplayedScrollingRows) : + (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows); + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] = true; + try + { + // Remove any Columns contained in this control + for (int i = 0; i < this.Columns.Count; i ++) + { + this.Columns[i].Dispose(); + } + + this.Columns.Clear(); + + UnwireScrollBarsEvents(); + if (this.vertScrollBar != null) + { + this.vertScrollBar.Dispose(); + this.vertScrollBar = null; + } + if (this.horizScrollBar != null) + { + this.horizScrollBar.Dispose(); + this.horizScrollBar = null; + } + + if (this.pens != null) + { + int nPenEntries = this.pens.Count; + if (nPenEntries > 0) + { + foreach (Pen pen in this.pens.Values) + { + pen.Dispose(); + } + this.pens.Clear(); + } + this.pens = null; + } + + if (this.brushes != null) + { + int nBrushEntries = this.brushes.Count; + if (nBrushEntries > 0) + { + foreach (SolidBrush brush in this.brushes.Values) + { + brush.Dispose(); + } + this.brushes.Clear(); + } + this.brushes = null; + } + + if (this.placeholderStringFormat != null) + { + this.placeholderStringFormat.Dispose(); + this.placeholderStringFormat = null; + } + + if (this.latestEditingControl != null) + { + this.latestEditingControl.Dispose(); + this.latestEditingControl = null; + } + if (this.editingControl != null) + { + this.editingControl.Dispose(); + this.editingControl = null; + } + if (this.editingPanel != null) + { + this.editingPanel.Dispose(); + this.editingPanel = null; + } + if (this.gridPen != null) + { + this.gridPen.Dispose(); + this.gridPen = null; + } + Debug.Assert(this.noSelectionChangeCount == 0); + + if (this.dataConnection != null) + { + this.dataConnection.Dispose(); + } + + // DGV should dispose the tool tip control before disposing itself. + // vsw 559812. + this.toolTipControl.Dispose(); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] = false; + } + } + base.Dispose(disposing); + } + + private void DrawColHeaderShadow(Graphics g, int mouseX) + { + Rectangle r = CalcColRelocationFeedbackRect(mouseX); + DrawShadowRect(r); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion]) + { + Rectangle rectInsertionBar = new Rectangle(0, this.layout.ColumnHeaders.Top, DATAGRIDVIEW_insertionBarWidth, this.layout.ColumnHeaders.Height); + // this.trackColumnEdge is the column preceeding the insertion point + if (this.trackColumnEdge == -1) + { + // Insert as first column + rectInsertionBar.X = GetColumnXFromIndex(this.Columns.GetFirstColumn(DataGridViewElementStates.Visible).Index); + if (this.RightToLeftInternal) + { + rectInsertionBar.X -= DATAGRIDVIEW_insertionBarWidth; + } + } + else + { + int offsetFromCenter = 0; + if (this.Columns.GetNextColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None) == null) + { + if (!this.RightToLeftInternal) + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth; + } + } + else + { + if (this.RightToLeftInternal) + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth / 2 - 1; + } + else + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth / 2 + 1; + } + } + if (this.RightToLeftInternal) + { + rectInsertionBar.X = Math.Max(this.layout.ColumnHeaders.X, + GetColumnXFromIndex(this.trackColumnEdge) - this.Columns[this.trackColumnEdge].Width - offsetFromCenter); + } + else + { + rectInsertionBar.X = Math.Min(GetColumnXFromIndex(this.trackColumnEdge) + this.Columns[this.trackColumnEdge].Width - offsetFromCenter, + this.layout.ColumnHeaders.Right - DATAGRIDVIEW_insertionBarWidth); + } + } + if (this.ApplyVisualStylesToHeaderCells) + { + g.FillRectangle(GetCachedBrush(SystemColors.HotTrack), rectInsertionBar); + } + else + { + ControlPaint.FillReversibleRectangle(RectangleToScreen(rectInsertionBar), Color.White); + } + } + } + + /// + /// Draws an XOR region to give UI feedback for Column Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawColSplitBar(int mouseX) + { + Rectangle r = CalcColResizeFeedbackRect(mouseX); + DrawSplitBar(r); + } + + /// + /// Draws an XOR region to give UI feedback for Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawRowSplitBar(int mouseY) + { + Rectangle r = CalcRowResizeFeedbackRect(mouseY); + DrawSplitBar(r); + } + + private void DrawShadowRect(Rectangle r) + { + const byte DATAGRIDVIEW_shadowEdgeThickness = 3; + + IntPtr parentHandle = this.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y + r.Height - DATAGRIDVIEW_shadowEdgeThickness, r.Width, DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X + r.Width - DATAGRIDVIEW_shadowEdgeThickness, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc)); + } + + /// + /// Draws an XOR region to give UI feedback for Column/Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawSplitBar(Rectangle r) + { + IntPtr parentHandle = this.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT); + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc)); + } + + private void EditingControls_CommonMouseEventHandler(object sender, MouseEventArgs e, DataGridViewMouseEvent dgvme) + { + Debug.Assert(this.ptCurrentCell.X != -1); + int adjustedX = this.editingPanel.Location.X + e.X; + int adjustedY = this.editingPanel.Location.Y + e.Y; + if (sender == this.editingControl) + { + adjustedX += this.editingControl.Location.X; + adjustedY += this.editingControl.Location.Y; + } + + if (dgvme == DataGridViewMouseEvent.MouseDown && e.Clicks == 1) + { + // Reset the flag that records single-click exposed as double-click. + this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled] = false; + } + + MouseEventArgs me = new MouseEventArgs(e.Button, + e.Clicks, + adjustedX, + adjustedY, + e.Delta); + + HitTestInfo hti = HitTest(me.X, me.Y); + int mouseX = me.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, me.Y - hti.RowY, me); + + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] = true; + // Check to see if this is a CellMouseDoubleClick situation + if ((dgvme == DataGridViewMouseEvent.MouseDown || + dgvme == DataGridViewMouseEvent.Click || + dgvme == DataGridViewMouseEvent.MouseClick) && + (DateTime.Now.Ticks - this.lastMouseClickInfo.timeStamp) / 10000 <= SystemInformation.DoubleClickTime && + e.Button == this.lastMouseClickInfo.button && + e.Clicks == 1 && + dgvcme.ColumnIndex == this.lastMouseClickInfo.col && + dgvcme.RowIndex == this.lastMouseClickInfo.row) + { + Size hotDoubleClickZone = SystemInformation.DoubleClickSize; + if (Math.Abs(dgvcme.X - this.lastMouseClickInfo.x) <= hotDoubleClickZone.Width / 2 && + Math.Abs(dgvcme.Y - this.lastMouseClickInfo.y) <= hotDoubleClickZone.Height / 2) + { + me = new MouseEventArgs(e.Button, + 2, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex, dgvcme.X, dgvcme.Y, me); + switch (dgvme) + { + case DataGridViewMouseEvent.MouseDown: + { + OnMouseDown(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDown(dgvcme); + } + break; + } + case DataGridViewMouseEvent.Click: + { + OnDoubleClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + } + case DataGridViewMouseEvent.MouseClick: + { + // Set the flag that prevents the triple-click to be exposed as a double-click + this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled] = true; + + OnMouseDoubleClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDoubleClick(dgvcme); + } + break; + } + } + return; + } + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled]) + { + // Make sure that the triple-click is exposed as a single-click and not a double-click. + if (e.Clicks == 2) + { + me = new MouseEventArgs(e.Button, + 1, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, me.Y - hti.RowY, me); + } + switch (dgvme) + { + case DataGridViewMouseEvent.DoubleClick: + dgvme = DataGridViewMouseEvent.Click; + break; + case DataGridViewMouseEvent.MouseDoubleClick: + dgvme = DataGridViewMouseEvent.MouseClick; + break; + } + } + + switch (dgvme) + { + case DataGridViewMouseEvent.Click: + OnClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + case DataGridViewMouseEvent.DoubleClick: + OnDoubleClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + case DataGridViewMouseEvent.MouseClick: + OnMouseClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseClick(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseDoubleClick: + OnMouseDoubleClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDoubleClick(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseDown: + OnMouseDown(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDown(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseUp: + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble]) + { + MouseEventArgs meTmp = new MouseEventArgs(e.Button, + 2, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex, dgvcme.X, dgvcme.Y, meTmp); + } + OnCellMouseUp(dgvcme); + OnMouseUp(me); + break; + case DataGridViewMouseEvent.MouseMove: + OnCellMouseMove(dgvcme); + break; + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] = false; + } + } + + private void EditingControls_Click(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + Debug.Assert(this.ptCurrentCell.X != -1); + System.Windows.Forms.MouseEventArgs me = e as System.Windows.Forms.MouseEventArgs; + if (me != null) + { + EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.Click); + } + } + + private void EditingControls_DoubleClick(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + Debug.Assert(this.ptCurrentCell.X != -1); + System.Windows.Forms.MouseEventArgs me = e as System.Windows.Forms.MouseEventArgs; + if (me != null) + { + EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.DoubleClick); + } + } + + private void EditingControls_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseClick); + } + + private void EditingControls_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDoubleClick); + } + + private void EditingControls_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDown); + } + + private void EditingControls_MouseEnter(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + if (sender == this.editingPanel) + { + Debug.Assert(this.editingControl != null); + Debug.Assert(!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + this.CursorInternal = ((IDataGridViewEditingControl)this.editingControl).EditingPanelCursor; + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected]) + { + OnMouseEnter(EventArgs.Empty); + } + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + private void EditingControls_MouseLeave(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + if (sender == this.editingPanel) + { + Debug.Assert(this.editingControl != null); + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + Point ptMouse = PointToClient(Control.MousePosition); + if (!this.ClientRectangle.Contains(ptMouse)) + { + OnMouseLeave(EventArgs.Empty); + } + } + + private void EditingControls_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseMove); + } + + private void EditingControls_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseUp); + } + + private void EndColumnHeadersResize(MouseEventArgs e) + { + try + { + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + + int y = Math.Min(e.Y + this.mouseBarOffset, this.layout.Data.Bottom - 1); + int delta = y - this.layout.ColumnHeaders.Y - this.ColumnHeadersHeight + 1; + if (this.trackRowAnchor != y && delta != 0) + { + this.ColumnHeadersHeight += delta; + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnRelocation(MouseEventArgs e, HitTestInfo hti) + { + try + { + if (this.lastHeaderShadow != -1) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumnEdge = -1; + this.lastHeaderShadow = -1; + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + + int previousColumnIndex; + if (ColumnRelocationTarget(e, hti, out previousColumnIndex)) + { + if (previousColumnIndex == -1) + { + this.Columns[this.trackColumn].DisplayIndex = 0; + } + else if (this.Columns[this.trackColumn].DisplayIndex > this.Columns[previousColumnIndex].DisplayIndex) + { + this.Columns[this.trackColumn].DisplayIndex = this.Columns[previousColumnIndex].DisplayIndex + 1; + } + else + { + this.Columns[this.trackColumn].DisplayIndex = this.Columns[previousColumnIndex].DisplayIndex; + } + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnResize(MouseEventArgs e) + { + try + { + EndColumnResize(e.X); + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnResize(int x) + { + int newX, delta; + if (this.RightToLeftInternal) + { + newX = Math.Max(x + this.mouseBarOffset, this.layout.Data.X); + delta = GetColumnXFromIndex(this.trackColumn) - this.Columns[this.trackColumn].Thickness - newX + 1; + } + else + { + newX = Math.Min(x + this.mouseBarOffset, this.layout.Data.Right - 1); + delta = newX - (GetColumnXFromIndex(this.trackColumn) + this.Columns[this.trackColumn].Thickness) + 1; + } + + if (this.trackColAnchor != newX && delta != 0) + { + int proposed = this.Columns[this.trackColumn].Thickness + delta; + Debug.Assert(proposed >= this.Columns[this.trackColumn].MinimumThickness); + Debug.Assert(proposed <= DataGridViewBand.maxBandThickness); + this.Columns[this.trackColumn].Thickness = proposed; + } + } + + /// + public bool EndEdit() + { + return EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit); + } + + /// + public bool EndEdit(DataGridViewDataErrorContexts context) + { + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + return CommitEdit(context); + } + else + { + return EndEdit(context, + DataGridViewValidateCellInternal.Never /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell*/, + true /*resetAnchorCell*/); + } + } + + private bool EndEdit(DataGridViewDataErrorContexts context, + DataGridViewValidateCellInternal validateCell, + bool fireCellLeave, + bool fireCellEnter, + bool fireRowLeave, + bool fireRowEnter, + bool fireLeave, + bool keepFocus, + bool resetCurrentCell, + bool resetAnchorCell) + { + if (this.ptCurrentCell.X == -1) + { + return true; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] = true; + try + { + int curRowIndex = this.ptCurrentCell.Y; + int curColIndex = this.ptCurrentCell.X; + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, context, validateCell, + fireCellLeave, fireCellEnter, fireRowLeave, fireRowEnter, fireLeave); + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, context*/); // restore old value + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + + if (!this.IsCurrentCellInEditMode) + { + return true; + } + + if (curRowIndex != this.ptCurrentCell.Y || curColIndex != this.ptCurrentCell.X) + { + return true; + } + + if (this.editingControl != null) + { + UnwireEditingControlEvents(); + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] = this.MouseOverEditingControl; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] = this.MouseOverEditingPanel; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + try + { + dataGridViewCurrentCell.DetachEditingControl(); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + } + + ImeMode editingControlImeMode = this.editingControl.CachedImeMode; // If in restricted mode, ImeMode will be Disable. + this.latestEditingControl = this.editingControl; + Debug.Assert(this.editingPanel == null || this.editingPanel.Controls.Count == 0); + this.editingControl = null; + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + if (resetCurrentCell) + { + bool success = SetCurrentCellAddressCore(-1, -1, resetAnchorCell, false, false); + Debug.Assert(success); + } + } + if (keepFocus) + { + // Debug.Assert(this.CanFocus || this.Focused); // Invalid assertion (VS Whidbey 325154) + FocusInternal(); + } + this.ImeMode = editingControlImeMode; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] = false; + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + if (!IsInnerCellOutOfBounds(curColIndex, curRowIndex)) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(curColIndex, curRowIndex); + OnCellEndEdit(dgvce); + } + + return true; + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] = false; + } + } + + private void EndRowHeadersResize(MouseEventArgs e) + { + try + { + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + + int x, delta; + if (this.RightToLeftInternal) + { + x = Math.Max(e.X + this.mouseBarOffset, this.layout.Data.Left - 1); + delta = this.layout.RowHeaders.Right - this.RowHeadersWidth - x - 1; + } + else + { + x = Math.Min(e.X + this.mouseBarOffset, this.layout.Data.Right - 1); + delta = x - this.layout.RowHeaders.X - this.RowHeadersWidth + 1; + } + if (this.trackColAnchor != x && delta != 0) + { + this.RowHeadersWidth += delta; + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndRowResize(MouseEventArgs e) + { + try + { + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(this.trackRow); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(this.trackRow, out height, out minimumHeight); + int y = Math.Min(e.Y + this.mouseBarOffset, this.layout.Data.Bottom - 1); + int delta = y - (GetRowYFromIndex(this.trackRow) + height) + 1; + if (this.trackRowAnchor != y && delta != 0) + { + int proposedHeight = height + delta; + proposedHeight = Math.Max(proposedHeight, minimumHeight); + if (!OnRowHeightInfoPushed(this.trackRow, proposedHeight, minimumHeight)) + { + if (dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[this.trackRow]; // Unsharing row + } + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None); + dataGridViewRow.ThicknessInternal = proposedHeight; + } + } + } + finally + { + RealeaseMouse(); + } + } + + private void ExitBulkLayout(bool invalidInAdjustFillingColumns) + { + if (this.inBulkLayoutCount > 0) + { + this.inBulkLayoutCount--; + if (this.inBulkLayoutCount == 0) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + } + } + + private void ExitBulkPaint(int columnIndex, int rowIndex) + { + if (this.inBulkPaintCount > 0) + { + this.inBulkPaintCount--; + if (this.inBulkPaintCount == 0) + { + if (columnIndex >= 0) + { + InvalidateColumnInternal(columnIndex); + } + else if (rowIndex >= 0) + { + InvalidateRowPrivate(rowIndex); + } + else + { + Invalidate(); + } + } + } + } + + private void FirstVisibleScrollingRowTempted(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(rowIndex < this.Rows.Count); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0); + + int displayHeight = this.layout.Data.Height; + if (displayHeight <= 0) + { + return; + } + + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (totalVisibleFrozenHeight < displayHeight) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = rowIndex; + } + } + + private void FlushDisplayedChanged() + { + if (this.displayedBandsInfo.Dirty && this.Visible) + { + // Handle the rows + + if (!this.RowHeadersVisible && this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + // All rows are hidden + UpdateRowsDisplayedState(false /*displayed*/); + } + else + { + Rectangle rectScreen = Screen.FromControl(this).WorkingArea; + int maxDisplayedRows = (int) (rectScreen.Height / DataGridViewBand.minBandThickness); + + // Make sure all displayed scrolling rows have the Displayed state. + int rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp != -1) + { + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + + int rowIndexTmp2 = rowIndexTmp; + + // Make sure all scrolling rows before FirstDisplayedScrollingRow have their Displayed state set to false + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow != -1); + rowIndexTmp = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + + // Make sure all rows below last displayed scrolling row have Displayed state set to false (next loop) + rowIndexTmp = rowIndexTmp2; + } + else + { + // No displayed scrolling rows. Make sure all non-frozen rows have their Displayed state set to false (next loop) + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + + // Make sure all displayed frozen rows have their Displayed state set to true + int numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + + // Make sure all non-displayed frozen rows have their Displayed state set to false + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + // Treat the cases where the old displayed rows are completely disjoint from the new displayed rows + int lastDisplayedFrozenRowIndex = -1; + int lastDisplayedScrollingRowIndex = -1; + + if (this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + int firstDisplayedFrozenRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(firstDisplayedFrozenRowIndex != -1); + if (this.displayedBandsInfo.NumDisplayedFrozenRows > 1) + { + lastDisplayedFrozenRowIndex = this.Rows.GetNextRow(firstDisplayedFrozenRowIndex, DataGridViewElementStates.Visible, this.displayedBandsInfo.NumDisplayedFrozenRows-2 /*skipRows*/); + } + else + { + lastDisplayedFrozenRowIndex = firstDisplayedFrozenRowIndex; + } + } + + if (this.displayedBandsInfo.FirstDisplayedScrollingRow != -1) + { + if (this.displayedBandsInfo.NumDisplayedScrollingRows > 1) + { + lastDisplayedScrollingRowIndex = this.Rows.GetNextRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, this.displayedBandsInfo.NumDisplayedScrollingRows - 2 /*skipRows*/); + } + else + { + lastDisplayedScrollingRowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + } + + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + while (rowIndexTmp != -1 && + rowIndexTmp < this.displayedBandsInfo.FirstDisplayedScrollingRow && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + break; + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + if (rowIndexTmp != -1 && + rowIndexTmp < this.Rows.Count && + (this.displayedBandsInfo.FirstDisplayedScrollingRow == -1 || this.displayedBandsInfo.FirstDisplayedScrollingRow < rowIndexTmp) && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + while (rowIndexTmp != -1) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + break; + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + } + + if (this.displayedBandsInfo.RowInsertionOccurred) + { + // Adjust the scrolling rows that were pushed down by the rows insertion + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + if (rowIndexTmp != -1) + { + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible, this.displayedBandsInfo.OldNumDisplayedScrollingRows - 1); + if (rowIndexTmp == -1) + { + rowIndexTmp = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + } + int rowCount = 0; + while (rowIndexTmp != -1 && + rowCount <= maxDisplayedRows && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + } + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible); + rowCount++; + } + } + + // Adjust the frozen rows that were pushed down by the rows insertion + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (rowIndexTmp != -1) + { + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, this.displayedBandsInfo.OldNumDisplayedFrozenRows - 1); + if (rowIndexTmp == -1) + { + rowIndexTmp = this.Rows.GetLastRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + int rowCount = 0; + while (rowIndexTmp != -1 && + rowCount <= maxDisplayedRows && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + } + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible); + rowCount++; + } + } + } + +#if DEBUG + for (rowIndexTmp = 0; rowIndexTmp < this.Rows.Count; rowIndexTmp++) + { + DataGridViewElementStates rowStateDbg = this.Rows.GetRowState(rowIndexTmp); + bool rowNeedsDisplayedState = RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex); + if (((rowStateDbg & DataGridViewElementStates.Displayed) != 0) != rowNeedsDisplayedState) + { + Debug.Fail("Unexpected Displayed state for row"); + } + } +#endif + } + + // Handle the columns + + if (!this.ColumnHeadersVisible && this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 0) + { + // All columns are hidden + UpdateColumnsDisplayedState(false /*displayed*/); + } + else + { + // Make sure all displayed scrolling columns have the Displayed state. + DataGridViewColumn dataGridViewColumnTmp; + int columnIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (columnIndexTmp != -1) + { + int numDisplayedScrollingCols = this.displayedBandsInfo.NumDisplayedScrollingCols; + Debug.Assert(numDisplayedScrollingCols > 0); + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (numDisplayedScrollingCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (!dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = true; + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + numDisplayedScrollingCols--; + } + + DataGridViewColumn dataGridViewColumnTmp2 = dataGridViewColumnTmp; + + // Make sure all scrolling columns before FirstDisplayedScrollingCol have their Displayed state set to false + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol != -1); + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + + // Make sure all columns after last displayed scrolling column have Displayed state set to false (next loop) + dataGridViewColumnTmp = dataGridViewColumnTmp2; + } + else + { + // No displayed scrolling columns. Make sure all non-frozen columns have their Displayed state set to false (next loop) + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + + // Make sure all displayed frozen columns have their Displayed state set to true + int numDisplayedFrozenCols = this.displayedBandsInfo.NumDisplayedFrozenCols; + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (!dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = true; + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + numDisplayedFrozenCols--; + } + + // Make sure all non-displayed frozen columns have their Displayed state set to false + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + } + + // Treat the cases where the old displayed columns are completely disjoint from the new displayed columns + + columnIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingCol; + while (columnIndexTmp != -1 && + columnIndexTmp < this.Columns.Count && + this.displayedBandsInfo.FirstDisplayedScrollingCol != -1 && + columnIndexTmp != this.displayedBandsInfo.FirstDisplayedScrollingCol && + this.Columns.DisplayInOrder(columnIndexTmp, this.displayedBandsInfo.FirstDisplayedScrollingCol) && + !ColumnNeedsDisplayedState(this.Columns[columnIndexTmp])) + { + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + if (!dataGridViewColumnTmp.Displayed) + { + break; + } + else + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + columnIndexTmp = (dataGridViewColumnTmp == null) ? -1 : dataGridViewColumnTmp.Index; + } + } + + columnIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingCol; + if (columnIndexTmp != -1 && + columnIndexTmp < this.Columns.Count && + (this.displayedBandsInfo.FirstDisplayedScrollingCol == -1 || (this.displayedBandsInfo.FirstDisplayedScrollingCol != columnIndexTmp && this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, columnIndexTmp))) && + !ColumnNeedsDisplayedState(this.Columns[columnIndexTmp])) + { + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (dataGridViewColumnTmp != null) + { + if (!dataGridViewColumnTmp.Displayed) + { + break; + } + else + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + } + + if (this.displayedBandsInfo.ColumnInsertionOccurred) + { + dataGridViewColumnTmp = this.Columns[this.Columns.Count - 1]; + while (dataGridViewColumnTmp != null && !ColumnNeedsDisplayedState(dataGridViewColumnTmp)) + { + if (dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + } + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + +#if DEBUG + for (columnIndexTmp = 0; columnIndexTmp < this.Columns.Count; columnIndexTmp++) + { + DataGridViewElementStates colStateDbg = this.Columns[columnIndexTmp].State; + bool columnNeedsDisplayedState = ColumnNeedsDisplayedState(this.Columns[columnIndexTmp]); + if (((colStateDbg & DataGridViewElementStates.Displayed) != 0) != columnNeedsDisplayedState) + { + Debug.Fail("Unexpected Displayed state for column"); + } + } +#endif + } + + this.displayedBandsInfo.Dirty = false; + } + } + + private void FlushDisplayIndexChanged(bool raiseEvent) + { + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.DisplayIndexHasChanged) + { + dataGridViewColumn.DisplayIndexHasChanged = false; + if (raiseEvent) + { + OnColumnDisplayIndexChanged(dataGridViewColumn); + } + } + } + } + + private void FlushSelectionChanged() + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged]) + { + OnSelectionChanged(EventArgs.Empty); + } + } + + /// + protected override AccessibleObject GetAccessibilityObjectById(int objectId) + { + // decrement the objectId because in our implementation of AccessibilityClient notitification objectId's are 1 - based. + // 0 == NativeMethods.CHILDID_SELF corresponds to the AccessibleObject itself + return this.AccessibilityObject.GetChild(objectId - 1); + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal SolidBrush GetCachedBrush(Color color) + { + SolidBrush brush = (SolidBrush) this.brushes[color]; + if (brush == null) + { + brush = new SolidBrush(color); + this.brushes.Add(color, brush); + } + return brush; + } + +#if DGV_GDI + internal WindowsSolidBrush GetCachedWindowsBrush(Color color) + { + WindowsSolidBrush brush = (WindowsSolidBrush)this.brushes[color]; + if (brush == null) + { + brush = new WindowsSolidBrush(color); + this.brushes.Add(color, brush); + } + return brush; + } +#endif // DGV_GDI + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal Pen GetCachedPen(Color color) + { + Pen pen = (Pen) this.pens[color]; + if (pen == null) + { + pen = new Pen(color); + this.pens.Add(color, pen); + } + return pen; + } + +#if DGV_GDI + internal WindowsPen GetCachedWindowsPen(Color color) + { + WindowsPen pen = (WindowsPen)this.pens[color]; + if (pen == null) + { + pen = new WindowsPen(color); + this.pens.Add(color, pen); + } + return pen; + } +#endif // DGV_GDI + + internal TypeConverter GetCachedTypeConverter(Type type) + { + if (this.converters.ContainsKey(type)) + { + return (TypeConverter)this.converters[type]; + } + + TypeConverter converter = TypeDescriptor.GetConverter(type); + this.converters.Add(type, converter); + return converter; + } + + internal Rectangle GetCellAdjustedDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow) + { + Rectangle rect = GetCellDisplayRectangle(columnIndex, rowIndex, cutOverflow); + if (!rect.IsEmpty) + { + if (this.SingleVerticalBorderAdded && columnIndex == this.FirstDisplayedColumnIndex) + { + if (!this.RightToLeftInternal) + { + rect.X--; + } + rect.Width++; + } + if (this.SingleHorizontalBorderAdded && rowIndex == this.FirstDisplayedRowIndex) + { + rect.Y--; + rect.Height++; + } + } + return rect; + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // using specialized DataGridViewCellLinkedList class instead of generics + ] + public int GetCellCount(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + + int cellCount = 0; + bool displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired; + + if ((includeFilter & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected) + { + if (includeFilter == DataGridViewElementStates.Selected) + { + cellCount = this.individualSelectedCells.Count; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and decide that SelectAll() should use band selection, + // we need to take the bands into account. + return cellCount; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + return cellCount + this.selectedBandIndexes.Count * this.Rows.Count; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + return cellCount + this.selectedBandIndexes.Count * this.Columns.Count; + } + } + } + + displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed; + frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly; + visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible; + + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, dataGridViewCell.RowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and decide that SelectAll() should use band selection, + // we need to take the bands into account. + return cellCount; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (int columnIndex in this.selectedBandIndexes) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + return cellCount; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + foreach (int rowIndex in this.selectedBandIndexes) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + return cellCount; + } + } + } + + if ((includeFilter == DataGridViewElementStates.ReadOnly && this.ReadOnly) || + includeFilter == DataGridViewElementStates.None) + { + return this.Rows.Count * this.Columns.Count; + } + + displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed; + frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly; + visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible; + + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (!visibleRequired || (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0) + { + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + } + return cellCount; + } + + private bool GetCellCount_CellIncluded(DataGridViewCell dataGridViewCell, + int rowIndex, + bool displayedRequired, + bool frozenRequired, + bool resizableRequired, + bool readOnlyRequired, + bool visibleRequired) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (displayedRequired) + { + bool cellDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0 && + dataGridViewCell.OwningColumn.Displayed; + if (!cellDisplayed) + { + return false; + } + } + if (frozenRequired) + { + bool cellFrozen = (rowState & DataGridViewElementStates.Frozen) != 0 || + dataGridViewCell.OwningColumn.Frozen || + dataGridViewCell.StateIncludes(DataGridViewElementStates.Frozen); + if (!cellFrozen) + { + return false; + } + } + if (resizableRequired) + { + if (!RowIsResizable(rowIndex) && dataGridViewCell.OwningColumn.Resizable != DataGridViewTriState.True) + { + return false; + } + } + if (readOnlyRequired) + { + bool cellReadOnly = this.ReadOnly || + (rowState & DataGridViewElementStates.ReadOnly) != 0 || + dataGridViewCell.OwningColumn.ReadOnly || + dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly); + if (!cellReadOnly) + { + return false; + } + } + if (visibleRequired) + { + bool cellVisible = (rowState & DataGridViewElementStates.Visible) != 0 && + dataGridViewCell.OwningColumn.Visible; + if (!cellVisible) + { + return false; + } + } + return true; + } + + /// + public Rectangle GetCellDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow) + { + Rectangle rowRect; + Rectangle columnRect; + + if (columnIndex >= 0) + { + if (columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + columnRect = GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow); + } + else + { + if (columnIndex != -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex >= 0) + { + columnRect = this.layout.RowHeaders; + } + else + { + columnRect = this.layout.TopLeftHeader; + } + } + + if (rowIndex >= 0) + { + if (rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + rowRect = GetRowDisplayRectanglePrivate(rowIndex, cutOverflow); + } + else + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (columnIndex >= 0) + { + rowRect = this.layout.ColumnHeaders; + } + else + { + rowRect = this.layout.TopLeftHeader; + } + } + + if (!cutOverflow) + { + int height = rowRect.Bottom - columnRect.Bottom; + if (height > 0) + { + columnRect.Height += height; + } + int width; + if (this.RightToLeftInternal) + { + width = rowRect.X - columnRect.X; + if (width > 0) + { + rowRect.Width += width; + rowRect.X -= width; + } + } + else + { + width = columnRect.Right - rowRect.Right; + if (width > 0) + { + rowRect.Width += width; + } + } + } + + rowRect.Intersect(columnRect); + + return rowRect; + } + + internal DataGridViewCell GetCellInternal(int columnIndex, int rowIndex) + { + Debug.Assert(columnIndex >= -1 && columnIndex < this.Columns.Count); + Debug.Assert(rowIndex >= -1 && rowIndex < this.Rows.Count); + if (rowIndex >= 0) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + Debug.Assert(dataGridViewRow != null); + if (columnIndex >= 0) + { + return dataGridViewRow.Cells[columnIndex]; + } + else + { + return dataGridViewRow.HeaderCell; + } + } + else + { + if (columnIndex >= 0) + { + return this.Columns[columnIndex].HeaderCell; + } + else + { + return this.TopLeftHeaderCell; + } + } + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // using specialized DataGridViewCellLinkedList class instead of generics + ] + public virtual DataObject GetClipboardContent() + { + if (this.ClipboardCopyMode == DataGridViewClipboardCopyMode.Disable) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_DisabledClipboardCopy)); + } + + if (this.CurrentCellIsEditedAndOnlySelectedCell) + { + return null; + } + + string[] formats = new string[] { DataFormats.Html, DataFormats.Text, DataFormats.UnicodeText, DataFormats.CommaSeparatedValue }; + DataObject dataObject = new DataObject(); + bool includeColumnHeaders = false, includeRowHeaders = false; + string cellContent = null; + StringBuilder sbContent = null; + DataGridViewColumn dataGridViewColumn, prevDataGridViewColumn, nextDataGridViewColumn; + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0) + { + return null; + } + + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + includeColumnHeaders = (this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) == -1); + includeRowHeaders = true; + } + else + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible columns in their reverse display order + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible columns in their display order + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + } + // Cycle through the visible selected rows. + bool firstRowIndex = true; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + Debug.Assert(rowIndex != -1); + int nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + while (rowIndex != -1) + { + if (this.RightToLeftInternal) + { + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + + // Cycle through the visible columns in their reverse display order + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible columns in their display order + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + rowIndex = nextRowIndex; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + } + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0) + { + return null; + } + + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + includeColumnHeaders = true; + includeRowHeaders = (this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) == null); + } + else + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible & selected columns in their display order + DataGridViewColumn lastDataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + dataGridViewColumn = lastDataGridViewColumn; + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + lastDataGridViewColumn == null /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible & selected columns in their display order + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + } + // Cycle through the visible rows. + bool firstRowIndex = true; + int rowIndex = firstVisibleRowIndex; + int nextRowIndex = -1; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + while (rowIndex != -1) + { + if (this.RightToLeftInternal) + { + DataGridViewColumn lastDataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + + // Cycle through the visible & selected columns in their reverse display order + dataGridViewColumn = lastDataGridViewColumn; + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + lastDataGridViewColumn == null /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible & selected columns in their display order + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + rowIndex = nextRowIndex; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + bool selectedVisibleCellExists = false; + bool selectedVisibleColumnExists = false; + bool selectedVisibleRowExists = false; + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + selectedVisibleRowExists = this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0; + selectedVisibleCellExists = selectedVisibleRowExists && this.Columns.GetColumnCount(DataGridViewElementStates.Visible) != 0; + } + else if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + selectedVisibleColumnExists = this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0; + selectedVisibleCellExists = selectedVisibleColumnExists && this.Rows.GetRowCount(DataGridViewElementStates.Visible) != 0; + } + if (!selectedVisibleCellExists && this.individualSelectedCells.Count > 0) + { + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (dataGridViewCell.Visible) + { + selectedVisibleCellExists = true; + break; + } + } + } + if (!selectedVisibleCellExists) + { + return null; + } + + // There is at least one selected visible cell. + if (this.SelectionMode == DataGridViewSelectionMode.CellSelect) + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + } + else + { + includeColumnHeaders = includeRowHeaders = false; + if (this.ColumnHeadersVisible) + { + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + if (selectedVisibleColumnExists) + { + includeColumnHeaders = true; + } + /* Use this code if column headers should be included when all cells are selected in a visible column. + else + { + // Include column headers if there is a column where all visible cells are selected + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + // Cycle through the visible rows, see if the cell in that column is selected + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + while (rowIndex != -1) + { + if (!this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].Selected) + { + break; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + if (rowIndex == -1) + { + // All visible cells in column are selected + includeColumnHeaders = true; + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + */ + } + else + { + includeColumnHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + } + + if (this.RowHeadersVisible) + { + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + if (selectedVisibleRowExists) + { + includeRowHeaders = true; + } + /* Use this code if row headers should be included when all cells are selected in a visible row. + else + { + // Include row headers if there is a row where all visible cells are selected + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowIndex != -1); + while (rowIndex != -1) + { + // Cycle through the visible columns, see if the cell in that row is selected + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (!this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].Selected) + { + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + if (dataGridViewColumn == null) + { + // All visible cells in row are selected + includeRowHeaders = true; + break; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + */ + } + else + { + includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + } + } + + // Get the four corners of the 'selected table' + int lRowIndex = int.MaxValue; + int uRowIndex = -1; + DataGridViewColumn lColumn = null, uColumn = null; + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + DataGridViewColumn firstVisibleColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + DataGridViewColumn lastVisibleColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + + Debug.Assert(firstVisibleColumn != null); + Debug.Assert(lastVisibleColumn != null); + foreach (int rowIndex in this.selectedBandIndexes) + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0) + { + if (rowIndex < lRowIndex) + { + lRowIndex = rowIndex; + } + if (rowIndex > uRowIndex) + { + uRowIndex = rowIndex; + } + lColumn = firstVisibleColumn; + uColumn = lastVisibleColumn; + } + } + } + else if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + Debug.Assert(firstVisibleRowIndex != -1); + Debug.Assert(lastVisibleRowIndex != -1); + foreach (int columnIndex in this.selectedBandIndexes) + { + if (this.Columns[columnIndex].Visible) + { + if (lColumn == null || this.Columns.DisplayInOrder(columnIndex, lColumn.Index)) + { + lColumn = this.Columns[columnIndex]; + } + if (uColumn == null || this.Columns.DisplayInOrder(uColumn.Index, columnIndex)) + { + uColumn = this.Columns[columnIndex]; + } + lRowIndex = firstVisibleRowIndex; + uRowIndex = lastVisibleRowIndex; + } + } + } + + // Go through the individually selected cells to potentially stretch the current 'selected table'. + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (dataGridViewCell.Visible) + { + if (dataGridViewCell.RowIndex < lRowIndex) + { + lRowIndex = dataGridViewCell.RowIndex; + } + if (dataGridViewCell.RowIndex > uRowIndex) + { + uRowIndex = dataGridViewCell.RowIndex; + } + if (lColumn == null || this.Columns.DisplayInOrder(dataGridViewCell.ColumnIndex, lColumn.Index)) + { + lColumn = dataGridViewCell.OwningColumn; + } + if (uColumn == null || this.Columns.DisplayInOrder(uColumn.Index, dataGridViewCell.ColumnIndex)) + { + uColumn = dataGridViewCell.OwningColumn; + } + } + } + + Debug.Assert(lRowIndex != -1); + Debug.Assert(uRowIndex != -1); + Debug.Assert(lColumn != null); + Debug.Assert(uColumn != null); + Debug.Assert(lColumn.Index == uColumn.Index || this.Columns.DisplayInOrder(lColumn.Index, uColumn.Index)); + Debug.Assert(lRowIndex <= uRowIndex); + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible columns from uColumn to lColumn + dataGridViewColumn = uColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != lColumn) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(prevDataGridViewColumn != null); + } + else + { + prevDataGridViewColumn = null; + } + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + dataGridViewColumn == uColumn /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = prevDataGridViewColumn; + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + false /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible columns from lColumn to uColumn + dataGridViewColumn = lColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != uColumn) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(nextDataGridViewColumn != null); + } + else + { + nextDataGridViewColumn = null; + } + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders && dataGridViewColumn == lColumn /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = nextDataGridViewColumn; + } + } + } + // Cycle through the visible rows from lRowIndex to uRowIndex. + bool firstRowIndex = true; + int rowIndex = lRowIndex; + int nextRowIndex = -1; + Debug.Assert(rowIndex != -1); + while (rowIndex != -1) + { + if (rowIndex != uRowIndex) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + Debug.Assert(nextRowIndex != -1); + } + else + { + nextRowIndex = -1; + } + + if (this.RightToLeftInternal) + { + // Cycle through the visible columns from uColumn to lColumn + dataGridViewColumn = uColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != lColumn) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(prevDataGridViewColumn != null); + } + else + { + prevDataGridViewColumn = null; + } + + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + dataGridViewColumn == uColumn /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = prevDataGridViewColumn; + } + + if (includeRowHeaders) + { + // Get the row header clipboard content + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + // Get the row header clipboard content + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + false /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible columns from lColumn to uColumn + dataGridViewColumn = lColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != uColumn) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(nextDataGridViewColumn != null); + } + else + { + nextDataGridViewColumn = null; + } + + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders && dataGridViewColumn == lColumn /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = nextDataGridViewColumn; + } + } + rowIndex = nextRowIndex; + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + } + return dataObject; + } + + private static void GetClipboardContentForHtml(StringBuilder sbContent, out System.IO.MemoryStream utf8Stream) + { + byte[] sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString()); + byte[] destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes); + + // Marshal.SystemDefaultCharSize is 2 on WinXP Pro - so the offsets seem to be in character counts instead of bytes. + // Test on JPN and Win9x machines. + int bytecountEndOfFragment = 135 + destinationBytes.Length; + int bytecountEndOfHtml = bytecountEndOfFragment + 36; + string prefix = string.Format(CultureInfo.InvariantCulture, DATAGRIDVIEW_htmlPrefix, bytecountEndOfHtml.ToString("00000000", CultureInfo.InvariantCulture), bytecountEndOfFragment.ToString("00000000", CultureInfo.InvariantCulture)) + DATAGRIDVIEW_htmlStartFragment; + sbContent.Insert(0, prefix); + sbContent.Append(DATAGRIDVIEW_htmlEndFragment); + + sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString()); + destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes); + + utf8Stream = new System.IO.MemoryStream(bytecountEndOfHtml + 1); + utf8Stream.Write(destinationBytes, 0, bytecountEndOfHtml); + utf8Stream.WriteByte((byte)0); + + #if DEBUG + Debug.Assert(destinationBytes[97] == '<'); + Debug.Assert(destinationBytes[bytecountEndOfHtml-1] == '>'); + Debug.Assert(destinationBytes[133] == '<'); + Debug.Assert(destinationBytes[bytecountEndOfFragment] == '<'); + #endif + } + + /// + // Rectangle returned includes the potential column header + public Rectangle GetColumnDisplayRectangle(int columnIndex, bool cutOverflow) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + return GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow); + } + + private Rectangle GetColumnDisplayRectanglePrivate(int columnIndex, bool cutOverflow) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (!this.Columns[columnIndex].Displayed) + { + return Rectangle.Empty; + } + + Rectangle data = this.layout.Data; + int cx; + bool columnFound = false; + DataGridViewColumn dataGridViewColumn; + if (this.RightToLeftInternal) + { + cx = data.Right; + } + else + { + cx = data.X; + } + for (dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + dataGridViewColumn != null && !columnFound; + ) + { + if ((this.RightToLeftInternal && cx < data.X) || + (!this.RightToLeftInternal && cx > data.Right)) + { + break; + } + if (dataGridViewColumn.Index == columnIndex) + { + columnFound = true; + } + else + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + + if (!columnFound && this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + for (dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + dataGridViewColumn != null && !columnFound; + ) + { + if ((this.RightToLeftInternal && cx < data.X) || + (!this.RightToLeftInternal && cx > data.Right)) + { + break; + } + if (dataGridViewColumn.Index == columnIndex) + { + columnFound = true; + } + else + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (dataGridViewColumn.Index == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + if (this.RightToLeftInternal) + { + cx += this.negOffset; + } + else + { + cx -= this.negOffset; + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + + if (columnFound) + { + Debug.Assert(dataGridViewColumn != null); + int displayWidth, viewedColumnWidth = dataGridViewColumn.Thickness; + if (dataGridViewColumn.Index == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + viewedColumnWidth -= this.negOffset; + } + if (cutOverflow && + ((!this.RightToLeftInternal && cx + viewedColumnWidth > data.Right) || + (this.RightToLeftInternal && cx - viewedColumnWidth < data.X))) + { + if (this.RightToLeftInternal) + { + displayWidth = cx - data.X; + } + else + { + displayWidth = data.Right - cx; + } + } + else + { + displayWidth = viewedColumnWidth; + } + + Rectangle columnRect; + if (this.RightToLeftInternal) + { + columnRect = new Rectangle(cx - displayWidth, data.Y, displayWidth, data.Height); + } + else + { + columnRect = new Rectangle(cx, data.Y, displayWidth, data.Height); + } + if (this.layout.ColumnHeadersVisible) + { + columnRect.Height += this.layout.ColumnHeaders.Height; + columnRect.Y -= this.layout.ColumnHeaders.Height; + } + return columnRect; + } + + return Rectangle.Empty; + } + + // xColumnLeftEdge returns the left edge of the column when RightToLeft is false. + // xColumnLeftEdge returns the right edge of the column when RightToLeft is true. + private int GetColumnIndexFromX(int x, out int xColumnLeftEdge) + { + Rectangle data = this.layout.Data; + Debug.Assert(this.RightToLeftInternal || (x >= data.X - 1 && x < data.Right), "x must be inside the horizontal bounds of this.layout.Data"); + Debug.Assert(!this.RightToLeftInternal || (x >= data.X && x <= data.Right), "x must be inside the horizontal bounds of this.layout.Data"); + + if (!this.RightToLeftInternal && x == data.X - 1) + { + x++; + } + else if (this.RightToLeftInternal && x == data.Right) + { + x--; + } + + int cx; + if (this.RightToLeftInternal) + { + cx = data.Right-1; + } + else + { + cx = data.X; + } + + // first try to match x against a frozen column + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && + ((!this.RightToLeftInternal && cx < data.Right) || (this.RightToLeftInternal && cx >= data.X))) + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (!this.RightToLeftInternal && cx > x) + { + xColumnLeftEdge = cx - dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + else if (this.RightToLeftInternal && cx < x) + { + xColumnLeftEdge = cx + dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + + if (this.RightToLeftInternal) + { + cx += this.negOffset; + } + else + { + cx -= this.negOffset; + } + + // second try to match x against a scrolling column + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + while (dataGridViewColumn != null && + ((!this.RightToLeftInternal && cx < data.Right) || (this.RightToLeftInternal && cx >= data.X))) + { + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (!this.RightToLeftInternal && cx > x) + { + xColumnLeftEdge = cx - dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + else if (this.RightToLeftInternal && cx < x) + { + xColumnLeftEdge = cx + dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + + xColumnLeftEdge = -1; + return -1; + } + + private static int GetColumnScrollRate(int xOffset) + { + Debug.Assert(xOffset > 0); + // Counting 20ms for executing the actual column scrolling + if (xOffset <= 10) + { + return 480; // Two columns per second + } + if (xOffset <= 15) + { + return 313; // Three columns per second + } + if (xOffset <= 25) + { + return 180; // Five columns per second + } + if (xOffset <= 35) + { + return 123; // Seven columns per second + } + return Math.Max(1, 4000 / xOffset); + } + + /// + /// Returns the coordinate of the left edge of the given column. Note that + /// the column does not need to be completely visible on the display area. + /// Value returned is not necessarily within layout.Data because of the + /// this.negOffset value, or because the column may start to the right of + /// data area, or behind the frozen area, or completely on the left of the control. + /// The right edge is returned in RightToLeft mode. + /// + internal int GetColumnXFromIndex(int index) + { + Debug.Assert(index < this.Columns.Count); + Debug.Assert(this.Columns[index].Visible); + + int x; + if (this.RightToLeftInternal) + { + x = this.layout.Data.Right-1; + } + else + { + x = this.layout.Data.X; + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + if (index == dataGridViewColumn.Index) + { + return x; + } + if (this.RightToLeftInternal) + { + x -= dataGridViewColumn.Thickness; + } + else + { + x += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + + if (this.RightToLeftInternal) + { + x += this.negOffset; + } + else + { + x -= this.negOffset; + } + + int xFirstVisibleScrollingCol = x; + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + while (dataGridViewColumn != null) + { + if (index == dataGridViewColumn.Index) + { + return x; + } + if (this.RightToLeftInternal) + { + x -= dataGridViewColumn.Thickness; + } + else + { + x += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + + // The column is completely hidden on the left/right of the dataGridView + x = xFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + if (this.RightToLeftInternal) + { + x += dataGridViewColumn.Thickness; + } + else + { + x -= dataGridViewColumn.Thickness; + } + if (index == dataGridViewColumn.Index) + { + return x; + } + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + Debug.Fail("Could not find column in GetColumnXFromIndex"); + return 0; + } + + private int GetNegOffsetFromHorizontalOffset(int horizontalOffset) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && dataGridViewColumn.Thickness <= horizontalOffset) + { + horizontalOffset -= dataGridViewColumn.Thickness; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + return horizontalOffset; + } + + private bool GetOutOfBoundCorrectedHitTestInfo(ref HitTestInfo hti, ref int mouseX, ref int mouseY, out int xOffset, out int yOffset) + { + xOffset = yOffset = 0; + Rectangle rectScrollingArea = this.layout.Data; + int visibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int frozenVisibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int fittingTrailingScrollingRowsHeight = ComputeHeightOfFittingTrailingScrollingRows(frozenVisibleRowsHeight); + int trailingScrollingRowsHeight = ComputeHeightOfTrailingScrollingRows(); + int emptyBackgroundWidth = Math.Max(0, this.layout.Data.Width - this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible)); + int emptyBackgroundHeight = Math.Max(0, this.layout.Data.Height - frozenVisibleRowsHeight - trailingScrollingRowsHeight); + + Debug.Assert(!this.vertScrollBar.Enabled || + !this.vertScrollBar.Visible || + this.vertScrollBar.Maximum == visibleRowsHeight - frozenVisibleRowsHeight); + //VSWhidbey 525671 + //Debug.Assert(!this.vertScrollBar.Enabled || + // !this.vertScrollBar.Visible || + // this.vertScrollBar.Value >= this.verticalOffset); + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect]) + { + if (this.layout.RowHeadersVisible) + { + // Include row headers + rectScrollingArea = Rectangle.Union(rectScrollingArea, this.layout.RowHeaders); + } + // Discard frozen rows + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + false /*discardFrozenColumns*/, true /*discardFrozenRows*/); + + if (mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom) + { + // Mouse's Y is in-bound -- correct X value + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right-1 : rectScrollingArea.Left, mouseY); + if (this.ptAnchorCell.Y != -1 && + (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) != 0 && + this.trackRowEdge != -1 && + (this.Rows.GetRowState(this.trackRowEdge) & DataGridViewElementStates.Frozen) != 0 && + hti.row >= 0 && + (this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Frozen) == 0) + { + // Anchor cell is in frozen row and target cell is in unfrozen row. Make sure no row is scrolled off. + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstUnfrozenRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + int firstColumnIndex; + if (hti.col >= 0) + { + firstColumnIndex = hti.col; + } + else + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + firstColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + } + if (firstColumnIndex >= 0 && firstUnfrozenRowIndex >= 0) + { + if (!ScrollIntoView(firstColumnIndex, firstUnfrozenRowIndex, false /*forCurrentCellChange*/)) + { + return false; + } + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY); + } + } + return true; + } + + // Mouse's Y is outside of scrolling bands + if (mouseY < rectScrollingArea.Top) + { + if (this.ptAnchorCell.Y != -1 && + ((this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0 || + (this.trackRowEdge != -1 && (this.Rows.GetRowState(this.trackRowEdge) & DataGridViewElementStates.Frozen) == 0)) && + this.verticalOffset != 0) + { + // Up scrolling is required because the anchor's row is unfrozen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + yOffset = mouseY - rectScrollingArea.Top; // yOffset strictly negative + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Right-1; + } + else + { + mouseX = rectScrollingArea.Left+1; + } + } + else + { + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY); + } + } + else + { + Debug.Assert(mouseY > rectScrollingArea.Bottom); + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (this.verticalOffset + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight) + { + // Down scrolling is required + yOffset = mouseY - rectScrollingArea.Bottom; // yOffset strictly positive + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Right-1; + } + else + { + mouseX = rectScrollingArea.Left+1; + } + } + } + } + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect]) + { + if (this.layout.ColumnHeadersVisible) + { + // Include column headers + rectScrollingArea = Rectangle.Union(rectScrollingArea, this.layout.ColumnHeaders); + } + + // Discard frozen columns + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + true /*discardFrozenColumns*/, false /*discardFrozenRows*/); + + if (mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right) + { + // Mouse's X is in-bound -- correct Y value + hti = HitTest(mouseX, rectScrollingArea.Top); + if (this.ptAnchorCell.X != -1 && + this.Columns[this.ptAnchorCell.X].Frozen && + this.trackColumnEdge != -1 && + this.Columns[this.trackColumnEdge].Frozen && + hti.col >= 0 && + !this.Columns[hti.col].Frozen) + { + // Anchor cell is in frozen column and target cell is in unfrozen column. Make sure no column is scrolled off. + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + int firstUnfrozenColumnIndex = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen).Index; + int firstRowIndex; + if (hti.row >= 0) + { + firstRowIndex = hti.row; + } + else + { + firstRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + } + if (firstRowIndex >= 0 && firstUnfrozenColumnIndex >= 0) + { + if (!ScrollIntoView(firstUnfrozenColumnIndex, firstRowIndex, false /*forCurrentCellChange*/)) + { + return false; + } + hti = HitTest(mouseX, rectScrollingArea.Top); + } + } + return true; + } + + // Mouse's X is outside of scrolling bands + if ((!this.RightToLeftInternal && mouseX < rectScrollingArea.Left) || + (this.RightToLeftInternal && mouseX > rectScrollingArea.Right)) + { + if (this.ptAnchorCell.X != -1 && + (!this.Columns[this.ptAnchorCell.X].Frozen || + (this.trackColumnEdge != -1 && !this.Columns[this.trackColumnEdge].Frozen)) && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.negOffset > 0 || + this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) != null)) + { + // xOffset strictly negative + if (this.RightToLeftInternal) + { + // Right scrolling is required + xOffset = rectScrollingArea.Right - mouseX; + } + else + { + // Left scrolling is required + xOffset = mouseX - rectScrollingArea.Left; + } + mouseY = rectScrollingArea.Top+1; + } + else + { + hti = HitTest(mouseX, rectScrollingArea.Top); + } + } + else + { + Debug.Assert((!this.RightToLeftInternal && mouseX > rectScrollingArea.Right) || (this.RightToLeftInternal && mouseX < rectScrollingArea.Left)); + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol != -1 && + this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.None) == null) + { + // No more columns to scroll + return true; + } + + DataGridViewColumn newFirstVisibleScrollingCol = this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + + if (this.HorizontalOffset != newColOffset) + { + // xOffset strictly positive + if (this.RightToLeftInternal) + { + // Left scrolling is required + xOffset = rectScrollingArea.Left - mouseX; + } + else + { + // Right scrolling is required + xOffset = mouseX - rectScrollingArea.Right; + } + mouseY = rectScrollingArea.Top+1; + } + } + } + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + bool recomputeHitTestInfo = false; + + // Discard frozen columns and rows + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + true /*discardFrozenColumns*/, true /*discardFrozenRows*/); + + if (mouseY < rectScrollingArea.Top) + { + // Mouse's Y is above scrolling bands + if ( + ( + (this.ptAnchorCell.Y != -1 && (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0) + || + (this.ptCurrentCell.Y != -1 && (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + ) + && + this.verticalOffset != 0 + ) + { + // Up scrolling is required - the anchor's row is unfrozen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + yOffset = mouseY - rectScrollingArea.Top; // yOffset strictly negative + } + else + { + // Correct mouse's Y - no scrolling can be performed + if (mouseY < this.layout.Data.Top) + { + mouseY = this.layout.Data.Top+1; + recomputeHitTestInfo = true; + } + } + } + else if (mouseY > rectScrollingArea.Bottom) + { + // Mouse's Y is below scrolling bands + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (this.verticalOffset + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight) + { + // Down scrolling is required + yOffset = mouseY - rectScrollingArea.Bottom; // yOffset strictly positive + } + else + { + // Correct mouse's Y - no scrolling can be performed + mouseY = rectScrollingArea.Bottom-1; + recomputeHitTestInfo = true; + } + } + else + { + // Correct mouse's Y - no scrolling can be performed + mouseY = rectScrollingArea.Bottom-1; + recomputeHitTestInfo = true; + } + } +#if DEBUG + else + { + // Mouse's Y is in-bound + Debug.Assert(mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom); + } +#endif + if ((!this.RightToLeftInternal && mouseX < rectScrollingArea.Left) || + (this.RightToLeftInternal && mouseX > rectScrollingArea.Right)) + { + // Mouse's X is on the left of scrolling bands (LTR) + if ( + ( + (this.ptAnchorCell.X != -1 && !this.Columns[this.ptAnchorCell.X].Frozen) + || + (this.ptCurrentCell.X != -1 && !this.Columns[this.ptCurrentCell.X].Frozen) + ) + && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.negOffset > 0 || + this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) != null) + ) + { + // xOffset strictly negative + if (this.RightToLeftInternal) + { + // Right scrolling is required - anchor's column is unfrozen + xOffset = rectScrollingArea.Right - mouseX; + } + else + { + // Left scrolling is required - anchor's column is unfrozen + xOffset = mouseX - rectScrollingArea.Left; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (!this.RightToLeftInternal && mouseX < this.layout.Data.Left) + { + mouseX = this.layout.Data.Left+1; + recomputeHitTestInfo = true; + } + else if (this.RightToLeftInternal && mouseX > this.layout.Data.Right) + { + mouseX = this.layout.Data.Right-1; + recomputeHitTestInfo = true; + } + } + } + else if ((!this.RightToLeftInternal && mouseX > rectScrollingArea.Right) || + (this.RightToLeftInternal && mouseX < rectScrollingArea.Left)) + { + // Mouse's X is on the right of scrolling bands (LTR) + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol == -1 || + this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.None) != null)) + { + DataGridViewColumn newFirstVisibleScrollingCol = this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + if (this.HorizontalOffset != newColOffset) + { + // xOffset strictly positive + if (this.RightToLeftInternal) + { + // Left scrolling is required + xOffset = rectScrollingArea.Left - mouseX; + } + else + { + // Right scrolling is required + xOffset = mouseX - rectScrollingArea.Right; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Left+1; + } + else + { + mouseX = rectScrollingArea.Right-1; + } + recomputeHitTestInfo = true; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Left+1; + } + else + { + mouseX = rectScrollingArea.Right-1; + } + recomputeHitTestInfo = true; + } + } +#if DEBUG + else + { + // Mouse's X is in-bound + Debug.Assert(mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right); + } +#endif + if (recomputeHitTestInfo) + { + hti = HitTest(mouseX, mouseY); + } + } + return true; + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + int bordersAndPaddingWidth = 2*(this.BorderWidth + this.Padding.Size.Width); + int bordersAndPaddingHeight = 2*(this.BorderWidth + this.Padding.Size.Height); + + bool allowHorizScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Horizontal); + bool allowVertScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Vertical); + + int minimumWidth = 16 + bordersAndPaddingWidth; + if (allowVertScrollbar) + { + minimumWidth += this.vertScrollBar.Width; + } + if (this.RowHeadersVisible) + { + minimumWidth += this.RowHeadersWidth; + } + int preferredWidth = Math.Min(minimumWidth + this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible), proposedConstraints.Width); + if (preferredWidth < minimumWidth) + { + preferredWidth = minimumWidth; + } + + int minimumHeight = 16 + bordersAndPaddingHeight; + if (allowHorizScrollbar) + { + minimumHeight += this.horizScrollBar.Height; + } + if (this.ColumnHeadersVisible) + { + minimumHeight += this.ColumnHeadersHeight; + } + int preferredHeight = Math.Min(minimumHeight + this.Rows.GetRowsHeight(DataGridViewElementStates.Visible), proposedConstraints.Height); + if (preferredHeight < minimumHeight) + { + preferredHeight = minimumHeight; + } + + return new Size(preferredWidth, preferredHeight); + } + + /// + // Rectangle returned includes the potential row header + public Rectangle GetRowDisplayRectangle(int rowIndex, bool cutOverflow) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return GetRowDisplayRectanglePrivate(rowIndex, cutOverflow); + } + + private Rectangle GetRowDisplayRectanglePrivate(int rowIndex, bool cutOverflow) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) == 0) + { + return Rectangle.Empty; + } + + Rectangle data = this.layout.Data; + int cy = data.Y; + bool rowFound = false; + int indexTmp; + for (indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + indexTmp != -1 && !rowFound; + ) + { + if (cy > data.Bottom) + { + break; + } + if (indexTmp == rowIndex) + { + rowFound = true; + } + else + { + cy += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + } + + if (!rowFound && this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + for (indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + indexTmp != -1 && !rowFound; + ) + { + if (cy > data.Bottom) + { + break; + } + if (indexTmp == rowIndex) + { + rowFound = true; + } + else + { + cy += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + } + } + } + + if (rowFound) + { + int displayHeight; + if (cutOverflow && cy + this.Rows.SharedRow(indexTmp).GetHeight(indexTmp) > data.Bottom) + { + displayHeight = data.Bottom - cy; + } + else + { + displayHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + } + + Rectangle rowRect = new Rectangle(data.X, + cy, + data.Width, + displayHeight); + if (this.layout.RowHeadersVisible) + { + rowRect.Width += this.layout.RowHeaders.Width; + if (!this.RightToLeftInternal) + { + rowRect.X -= this.layout.RowHeaders.Width; + } + } + return rowRect; + } + + return Rectangle.Empty; + } + + private int GetRowIndexFromY(int y, out int yRowTopEdge) + { + Rectangle data = this.layout.Data; + Debug.Assert(y >= data.Y-1 && y < data.Bottom, "y must be inside the vertical bounds of the data area."); + + if (y == data.Y-1) + { + y++; + } + + int rowHeight; + int cy = data.Y; + + // first try to match y against a frozen rows + int indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1 && cy < data.Bottom) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + cy += rowHeight; + if (cy > y) + { + yRowTopEdge = cy - rowHeight; + return indexTmp; + } + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + // second try to match y against a scrolling row + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0 && + (this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + + while (indexTmp != -1 && cy < data.Bottom) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + cy += rowHeight; + if (cy > y) + { + yRowTopEdge = cy - rowHeight; + return indexTmp; + } + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible); + } + } + + yRowTopEdge = -1; + return -1; + } + + private static int GetRowScrollRate(int yOffset) + { + Debug.Assert(yOffset > 0); + // Counting 10ms for executing the actual row scrolling + if (yOffset <= 10) + { + return 90; // Ten rows per second + } + if (yOffset <= 15) + { + return 57; // Fifteen rows per second + } + if (yOffset <= 25) + { + return 30; // Twenty-five rows per second + } + if (yOffset <= 35) + { + return 18; // Thirty-five rows per second + } + return Math.Max(1, 600 / yOffset); + } + + /// + /// Returns the coordinate of the upper edge of the given row. Note that + /// the row does not need to be completely visible on the display area. + /// Value returned is not necessarily within layout.Data because the row + /// may start below the data area. + /// + internal int GetRowYFromIndex(int index) + { + Debug.Assert(index >= 0 && index < this.Rows.Count); + Debug.Assert((this.Rows.GetRowState(index) & DataGridViewElementStates.Visible) != 0); + + int y = this.layout.Data.Y; + + int indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + if (index == indexTmp) + { + return y; + } + y += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + int yFirstVisibleScrollingRow = y; + + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (index >= this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + // index is part of the scrolling rows below the frozen rows + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + else + { + // index is part of the rows hidden behind the frozen rows or on top of the control + indexTmp = -1; + } + } + else + { + // frozen rows cover all the rows real-estate. Look for index starting at the first visible non-frozen row. + indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(indexTmp != -1); + } + + if (indexTmp != -1) + { + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0 && + (this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + + while (indexTmp != -1) + { + if (index == indexTmp) + { + return y; + } + y += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + } + } + + // Row is completely hidden behind frozen rows or on top of control + y = yFirstVisibleScrollingRow; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow != -1); + indexTmp = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + y -= this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (index == indexTmp) + { + return y; + } + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + Debug.Fail("Could not find row in GetRowYFromIndex"); + return 0; + } + + private bool GetTabKeyEffective(bool shift, bool ctrl) + { + if (this.StandardTab) + { + return ctrl && + !((!shift && (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell)) || + (shift && (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell))); + } + else + { + return !ctrl && + !((!shift && (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell)) || + (shift && (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell))); + } + } + + /// + public HitTestInfo HitTest(int x, int y) + { + HitTestInfo hti = new HitTestInfo(); + + if (!this.layout.Inside.Contains(x, y)) + { + return hti; + } + + if (this.horizScrollBar != null && this.horizScrollBar.Visible && this.horizScrollBar.Bounds.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.HorizontalScrollBar; + return hti; + } + + if (this.vertScrollBar != null && this.vertScrollBar.Visible && this.vertScrollBar.Bounds.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.VerticalScrollBar; + return hti; + } + + if (this.layout.TopLeftHeader.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.TopLeftHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeader; + if (this.RightToLeftInternal) + { + hti.colStart = this.layout.TopLeftHeader.Right-1; + } + else + { + hti.colStart = this.layout.TopLeftHeader.Left; + } + hti.rowStart = this.layout.TopLeftHeader.Top; + if ((!this.RightToLeftInternal && this.layout.TopLeftHeader.Right - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - this.layout.TopLeftHeader.Left < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = this.layout.TopLeftHeader.Left - x - 1; + } + else + { + hti.mouseBarOffset = this.layout.TopLeftHeader.Right - x - 1; + } + } + } + else if (this.layout.TopLeftHeader.Top + this.layout.TopLeftHeader.Height - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop; + hti.mouseBarOffset = this.layout.TopLeftHeader.Top + this.layout.TopLeftHeader.Height - y - 1; + } + } + return hti; + } + + // check for column resize / insertion + if (this.layout.ColumnHeaders.Contains(x, y)) + { + int xColumnLeftEdge; // this is actually the right edge in RTL mode + hti.col = GetColumnIndexFromX(x, out xColumnLeftEdge); + if (hti.col < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti.col)); + hti.type = DataGridViewHitTestType.ColumnHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeader; + hti.rowStart = this.layout.ColumnHeaders.Top; + hti.colStart = xColumnLeftEdge; + int columnWidth = this.Columns[hti.col].Thickness; + if ((!this.RightToLeftInternal && xColumnLeftEdge + columnWidth - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - xColumnLeftEdge + columnWidth < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - columnWidth - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge + columnWidth - x - 1; + } + DataGridViewColumn dataGridViewColumn = this.Columns[hti.col]; + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeRight; + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeaderRight; + } + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Left; + DataGridViewColumn dataGridViewColumn = null; + // VS Whidbey bug 317105 - Condition unnecessary + //if (hti.col != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + //{ + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + //} + if (dataGridViewColumn != null) + { + hti.adjacentCol = dataGridViewColumn.Index; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeLeft; + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeaderLeft; + } + } + else + { + if (this.RowHeadersVisible && this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft; + } + } + } + else if (this.layout.ColumnHeaders.Bottom - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + if (/*!this.RowHeadersVisible &&*/ this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom; + hti.mouseBarOffset = this.layout.ColumnHeaders.Bottom - y - 1; + } + } + } + + // check for row resize + if (this.layout.RowHeaders.Contains(x, y)) + { + int yRowTopEdge; + hti.row = GetRowIndexFromY(y, out yRowTopEdge); + if (hti.row < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti.row)); + hti.type = DataGridViewHitTestType.RowHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeader; + hti.rowStart = yRowTopEdge; + if (this.RightToLeftInternal) + { + hti.colStart = this.layout.RowHeaders.Right-1; + } + else + { + hti.colStart = this.layout.RowHeaders.Left; + } + int rowHeight = this.Rows.SharedRow(hti.row).GetHeight(hti.row); + if (yRowTopEdge + rowHeight - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + + if (RowIsResizable(hti.row) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom; + hti.mouseBarOffset = yRowTopEdge + rowHeight - y - 1; + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Top; + int indexTmp = -1; + if (hti.row != this.displayedBandsInfo.FirstDisplayedScrollingRow || this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + indexTmp = this.Rows.GetPreviousRow(hti.row, DataGridViewElementStates.Visible); + } + if (indexTmp != -1) + { + if (RowIsResizable(indexTmp) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop; + hti.adjacentRow = indexTmp; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + else + { + if (this.ColumnHeadersVisible && this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + else if ((!this.RightToLeftInternal && this.layout.RowHeaders.Right - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - this.layout.RowHeaders.Left < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeRight; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = this.layout.RowHeaders.Left - x - 1; + } + else + { + hti.mouseBarOffset = this.layout.RowHeaders.Right - x - 1; + } + } + } + } + + if (this.layout.Data.Contains(x, y)) + { + int xColumnLeftEdge, yRowTopEdge; + hti.col = GetColumnIndexFromX(x, out xColumnLeftEdge); + hti.row = GetRowIndexFromY(y, out yRowTopEdge); + if (hti.col < 0 || hti.row < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti.col)); + Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti.row)); + hti.type = DataGridViewHitTestType.Cell; + hti.typeInternal = DataGridViewHitTestTypeInternal.Cell; + hti.rowStart = yRowTopEdge; + hti.colStart = xColumnLeftEdge; + if (!this.ColumnHeadersVisible) + { + int columnWidth = this.Columns[hti.col].Thickness; + if ((!this.RightToLeftInternal && xColumnLeftEdge + columnWidth - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - xColumnLeftEdge + columnWidth < DATAGRIDVIEW_columnSizingHotZone)) + { + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - columnWidth - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge + columnWidth - x - 1; + } + DataGridViewColumn dataGridViewColumn = this.Columns[hti.col]; + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeRight; + } + return hti; + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + DataGridViewColumn dataGridViewColumn = null; + if (hti.col != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + if (dataGridViewColumn != null) + { + hti.adjacentCol = dataGridViewColumn.Index; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeLeft; + } + return hti; + } + else + { + if (this.RowHeadersVisible && this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + return hti; + } + } + } + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + if (hti.col == dataGridViewColumn.Index && + this.RowHeadersVisible && + this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + return hti; + } + } + + if (!this.RowHeadersVisible) + { + int rowHeight = this.Rows.SharedRow(hti.row).GetHeight(hti.row); + if (yRowTopEdge + rowHeight - y < DATAGRIDVIEW_rowSizingHotZone) + { + if (RowIsResizable(hti.row) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom; + hti.mouseBarOffset = yRowTopEdge + rowHeight - y - 1; + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + int indexTmp = -1; + if (hti.row != this.displayedBandsInfo.FirstDisplayedScrollingRow || this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + indexTmp = this.Rows.GetPreviousRow(hti.row, + DataGridViewElementStates.Visible); + } + if (indexTmp != -1) + { + if (RowIsResizable(indexTmp) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop; + hti.adjacentRow = indexTmp; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + else + { + if (this.ColumnHeadersVisible && this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowIndex >= 0); + if (hti.row == rowIndex && + this.ColumnHeadersVisible && + this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + + return hti; + } + + private void HorizScrollTimer_Tick(object sender, System.EventArgs e) + { + BeginInvoke(new MethodInvoker(HorizScrollTimerHandler)); + } + + private void HorizScrollTimerHandler() + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]); + + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y); + int xOffset, yOffset, mouseX = ptMouse.X, mouseY = ptMouse.Y; + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (xOffset != 0) + { + int absXOffset = Math.Abs(xOffset), normOffset = xOffset / absXOffset; + ScrollColumns(normOffset); + this.horizScrollTimer.Interval = GetColumnScrollRate(absXOffset); + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect]) + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), mouseY); + if (hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + if (yOffset != 0) + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), ptMouse.Y-yOffset-(yOffset/Math.Abs(yOffset))); + } + else + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), mouseY); + } + if (hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + this.HorizScrollTimer.Enabled = false; + } + } + } + + // Returns true for success, returns false when the OnDataError event cancels the operation. + private bool InitializeEditingCellValue(ref DataGridViewCellStyle dataGridViewCellStyle, ref DataGridViewCell dataGridViewCell) + { + DataGridViewDataErrorEventArgs dgvdee = null; + // Save unedited value so we can restore it later if parsing of new value fails + this.uneditedFormattedValue = dataGridViewCell.GetFormattedValue(this.ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + try + { + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + object currentFormattedValue = dataGridViewEditingCell.GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting); + if ((currentFormattedValue == null && this.uneditedFormattedValue != null) || + (currentFormattedValue != null && this.uneditedFormattedValue == null) || + (currentFormattedValue != null && !this.uneditedFormattedValue.Equals(currentFormattedValue))) + { + Debug.Assert(this.ptCurrentCell.X == dataGridViewCell.ColumnIndex); + dataGridViewCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unshare the edited cell + dataGridViewEditingCell = dataGridViewCell as IDataGridViewEditingCell; + dataGridViewEditingCell.EditingCellFormattedValue = this.uneditedFormattedValue; + dataGridViewEditingCell.EditingCellValueChanged = false; + } + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + OnDataErrorInternal(dgvdee); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + return !dgvdee.Cancel; + } + return true; + } + + // Returns true for success, returns false when the OnDataError event cancels the operation. + private bool InitializeEditingControlValue(ref DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(this.editingControl != null); + + DataGridViewDataErrorEventArgs dgvdee = null; + object initialFormattedValue = dataGridViewCell.GetFormattedValue(this.ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + try + { + dataGridViewCell.InitializeEditingControl(this.ptCurrentCell.Y, initialFormattedValue, dataGridViewCellStyle); + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + OnDataErrorInternal(dgvdee); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + return !dgvdee.Cancel; + } + + // Save unedited value so we can restore it later if parsing of new value fails + this.uneditedFormattedValue = initialFormattedValue; + return true; + } + + /// + public void InvalidateCell(DataGridViewCell dataGridViewCell) + { + if (dataGridViewCell == null) + { + throw new ArgumentNullException("dataGridViewCell"); + } + if (dataGridViewCell.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CellDoesNotBelongToDataGridView)); + } + InvalidateCellPrivate(dataGridViewCell); + } + + private void InvalidateCellPrivate(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(dataGridViewCell.DataGridView == this); + InvalidateCell(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex); + } + + /// + public void InvalidateCell(int columnIndex, int rowIndex) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + InvalidateCellPrivate(columnIndex, rowIndex); + } + + private void InvalidateCellPrivate(int columnIndex, int rowIndex) + { + if (this.IsHandleCreated) + { + Rectangle cellDisplayRect = GetCellAdjustedDisplayRectangle(columnIndex, rowIndex, true); + if (!cellDisplayRect.IsEmpty) + { + Invalidate(cellDisplayRect); + } + } + } + + /// + /// + /// Invalidate the painting region for the column specified. + /// + public void InvalidateColumn(int columnIndex) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + InvalidateColumnInternal(columnIndex); + } + + internal void InvalidateColumnInternal(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.IsHandleCreated) + { + Rectangle columnDisplayRect = GetColumnDisplayRectanglePrivate(columnIndex, true); + if (!columnDisplayRect.IsEmpty) + { + Invalidate(columnDisplayRect); + } + } + } + + private void InvalidateData() + { + if (this.IsHandleCreated) + { + Invalidate(this.layout.Data); + } + } + + /// + /// Invalidates the scrollable area of the DataGridView. + /// + private void InvalidateInside() + { + if (this.IsHandleCreated) + { + Invalidate(this.layout.Inside); + } + } + + /// + /// + /// Invalidate the painting region for the row specified. + /// + public void InvalidateRow(int rowIndex) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + InvalidateRowPrivate(rowIndex); + } + + private void InvalidateRowPrivate(int rowIndex) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if (this.IsHandleCreated) + { + Rectangle rowDisplayRect = GetRowDisplayRectanglePrivate(rowIndex, true); + if (!rowDisplayRect.IsEmpty) + { + Invalidate(rowDisplayRect); + } + } + } + + private void InvalidateRowHeights() + { + this.Rows.InvalidateCachedRowsHeights(); + if (this.IsHandleCreated) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + } + } + + private void InvalidateRows(int lo, int hi) + { + Debug.Assert(lo <= hi); + Debug.Assert(lo < this.Rows.Count); + Debug.Assert(hi < this.Rows.Count); + + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 0) + { + return; + } + + Rectangle rowDisplayRect, data; + int top, bottom; + + data = this.layout.Data; + + // If "lo" is not visible, then get the next visible row + if ((this.Rows.GetRowState(lo) & DataGridViewElementStates.Visible) == 0) + { + lo = this.Rows.GetNextRow(lo, DataGridViewElementStates.Visible); + } + + if (lo == -1) + { + // there are no visible rows below "lo" so there is nothing to invalidate. + return; + } + + // If "hi" is not visible, then get the previous visible row + if ((this.Rows.GetRowState(hi) & DataGridViewElementStates.Visible) == 0) + { + hi = this.Rows.GetPreviousRow(hi, DataGridViewElementStates.Visible); + } + + Debug.Assert(lo <= hi); + Debug.Assert(lo > -1); + + rowDisplayRect = this.GetRowDisplayRectangle(lo, true /*cutOverflow*/); + + if (rowDisplayRect.IsEmpty) + { + // The top row is offscreen + if ((this.Rows.GetRowState(lo) & DataGridViewElementStates.Frozen) != 0) + { + // "lo" is a frozen row which is offscreen. + // This means that "lo" and any other row below it are offscreen. + return; + } + else if (this.displayedBandsInfo.NumDisplayedScrollingRows == 0) + { + // "lo" and all the rows below are scrolling rows but no scrolling rows are displayed. + return; + } + else if (lo >= this.displayedBandsInfo.FirstDisplayedScrollingRow && + this.Rows.GetRowCount(DataGridViewElementStates.Visible, + this.displayedBandsInfo.FirstDisplayedScrollingRow, + lo) >= this.displayedBandsInfo.NumDisplayedScrollingRows) + { + // "lo" is a scrolling row whose coordinates are below the last visible row. + return; + } + else + { + // "lo" is a scrolling row "behind" frozen rows. + // Start invalidating at the top of the first displayed scrolling row. + top = this.GetRowDisplayRectangle(this.displayedBandsInfo.FirstDisplayedScrollingRow, true /*cutOverflow*/).Top; + } + } + else + { + top = rowDisplayRect.Top; + } + + rowDisplayRect = this.GetRowDisplayRectangle(hi, true /*cutOverflow*/); + + if (rowDisplayRect.IsEmpty) + { + // The bottom row is offscreen. + if ((this.Rows.GetRowState(hi) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen) + { + // "hi" is a frozen row offscreen and "lo" is a frozen row on screen. + // Invalidate all the way to the bottom + bottom = data.Bottom; + } + else if (hi > this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + // "hi" is a scrolling row offscreen which is beyond the firstDisplayedScrollingRow + // Invalidate all the way to the bottom again. + bottom = data.Bottom; + } + else if (this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) == 0) + { + // "hi" is a scrolling row above the first displayed scrolling row and there are no frozen rows. + // There is nothing to invalidate. + return; + } + else + { + // "hi" is a scrolling row which is "behind" the frozen rows. + // Invalidate all the way to the bottom of the frozen rows + // Compute the bottom of the last displayed frozen row. + // There may be invisible rows between the frozen rows. + bottom = 0; + for (int i = 0; i < this.displayedBandsInfo.NumDisplayedFrozenRows;) + { + if ((this.Rows.GetRowState(i) & DataGridViewElementStates.Visible) == 0) + { + continue; + } + + if (i == this.displayedBandsInfo.NumDisplayedFrozenRows - 1) + { + bottom = this.GetRowDisplayRectangle(i, true /*cutOverflow*/).Bottom; + break; + } + + i ++; + } + + if (bottom <= top) + { + // In this case both "lo" and "hi" are two scrolling rows behind the frozen rows. + // Nothing to invalidate. + return; + } + } + } + else + { + bottom = rowDisplayRect.Bottom; + } + + Invalidate(new Rectangle(data.X, top, data.Width, bottom - top)); + } + + private void InvalidateScrollBars() + { + // invalidate the horizontal and the vertical scrollbars + // note that the scrollbars can be null - this happens when + // the control has been disposed. + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + this.horizScrollBar.Invalidate(); + } + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + this.vertScrollBar.Invalidate(); + } + } + + private bool IsColumnOutOfBounds(int columnIndex) + { + return columnIndex >= this.Columns.Count || columnIndex == -1; + } + + private bool IsInnerCellOutOfBounds(int columnIndex, int rowIndex) + { + return columnIndex >= this.Columns.Count || rowIndex >= this.Rows.Count || columnIndex == -1 || rowIndex == -1; + } + + private bool IsRowOutOfBounds(int rowIndex) + { + return rowIndex >= this.Rows.Count || rowIndex == -1; + } + + /// + protected override bool IsInputChar(char charCode) + { + if (this.editingControl != null && + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage]) + { + // Do not process key press in ProcessDialogChar. + return true; + } + else + { + return base.IsInputChar(charCode); + } + } + + /// + protected override bool IsInputKey(Keys keyData) + { + if ((keyData & Keys.Alt) == Keys.Alt) + { + return false; + } + switch (keyData & Keys.KeyCode) + { + case Keys.Escape: + { + return this.IsEscapeKeyEffective; + } + + case Keys.Tab: + { + return GetTabKeyEffective((keyData & Keys.Shift) == Keys.Shift, (keyData & Keys.Control) == Keys.Control); + } + + case Keys.A: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control) + { + return true; + } + break; + } + + case Keys.C: + case Keys.Insert: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control) + { + return true; + } + break; + } + + case Keys.Space: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && + (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect || + this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) && + this.ptCurrentCell.X != -1) + { + return true; + } + break; + } + + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + case Keys.Home: + case Keys.End: + case Keys.Next: + case Keys.Prior: + case Keys.Enter: + case Keys.Delete: + case Keys.D0: + case Keys.NumPad0: + case Keys.F2: + case Keys.F3: + { + return true; + } + } + return base.IsInputKey(keyData); + } + + /// + /// Determines if Scrollbars should be visible, + /// updates their bounds and the bounds of all + /// other regions in the dataGridView's Layout. + /// + private void LayoutScrollBars() + { + SuspendLayout(); + try + { + // Scrollbars are a tricky issue. + // We need to see if we can cram our columns and rows + // in without scrollbars and if they don't fit, we make + // scrollbars visible and then fixup our regions for the + // data. + bool allowHorizScrollbar = ((this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Horizontal)) && + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar]; + bool allowVertScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Vertical); + bool needHorizScrollbarWithoutVertScrollbar = false; + bool needHorizScrollbar = false; + bool needVertScrollbar = false; + bool rightToLeftInternal = this.RightToLeftInternal; + int oldfirstDisplayedScrollingRow; + + int totalVisibleColCount = this.Columns.GetColumnCount(DataGridViewElementStates.Visible); + int totalVisibleRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible); + int totalVisibleWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible); + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + // Expensive call - dataGridView could have a mode where no row is resizable which would result in better perfs + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + int horizScrollBarHeight = this.horizScrollBar.Height = SystemInformation.HorizontalScrollBarHeight; + int vertScrollBarWidth = this.vertScrollBar.Width = SystemInformation.VerticalScrollBarWidth; + + if (allowHorizScrollbar && + totalVisibleWidth > this.layout.Data.Width && totalVisibleFrozenWidth < this.layout.Data.Width && + horizScrollBarHeight <= this.layout.Data.Height) + { + int oldDataHeight = this.layout.Data.Height; + this.layout.Data.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.Data.Height >= 0); + needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true; + if (totalVisibleWidth - this.layout.Data.Width <= vertScrollBarWidth || + this.layout.Data.Width - totalVisibleFrozenWidth <= vertScrollBarWidth) + { + // Would we still need a horizontal scrollbar if there were a vertical one? + oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + ComputeVisibleRows(); + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))) + { + needHorizScrollbar = (totalVisibleFrozenWidth < this.layout.Data.Width - vertScrollBarWidth); + } + this.displayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow; + } + + if (needHorizScrollbar) + { + if (this.layout.RowHeadersVisible) + { + this.layout.RowHeaders.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.RowHeaders.Height >= 0); + } + } + else + { + // Restore old data height because turns out a horizontal scroll bar wouldn't make sense + this.layout.Data.Height = oldDataHeight; + } + } + + oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + ComputeVisibleRows(); + if (allowVertScrollbar && + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + vertScrollBarWidth <= this.layout.Data.Width) + { + this.layout.Data.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.Data.Width >= 0); + if (rightToLeftInternal) + { + this.layout.Data.X += vertScrollBarWidth; + } + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.ColumnHeaders.Width >= 0); + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X += vertScrollBarWidth; + } + } + needVertScrollbar = true; + } + + this.displayedBandsInfo.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn(); + // we compute the number of visible columns only after we set up the vertical scroll bar. + ComputeVisibleColumns(); + + if (allowHorizScrollbar && + needVertScrollbar && !needHorizScrollbar && + totalVisibleWidth > this.layout.Data.Width && totalVisibleFrozenWidth < this.layout.Data.Width && + horizScrollBarHeight <= this.layout.Data.Height) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow; + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width += vertScrollBarWidth; + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X -= vertScrollBarWidth; + } + } + this.layout.Data.Width += vertScrollBarWidth; + if (rightToLeftInternal) + { + this.layout.Data.X -= vertScrollBarWidth; + } + this.layout.Data.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.Data.Height >= 0); + needVertScrollbar = false; + + ComputeVisibleRows(); + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + vertScrollBarWidth <= this.layout.Data.Width) + { + this.layout.Data.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.Data.Width >= 0); + if (rightToLeftInternal) + { + this.layout.Data.X += vertScrollBarWidth; + } + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.ColumnHeaders.Width >= 0); + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X += vertScrollBarWidth; + } + } + needVertScrollbar = true; + } + if (needVertScrollbar) + { + needHorizScrollbar = true; + } + else + { + needHorizScrollbar = needHorizScrollbarWithoutVertScrollbar; + } + } + + this.layout.ResizeBoxRect = new Rectangle(); + if (needVertScrollbar && needHorizScrollbar) + { + this.layout.ResizeBoxRect = new Rectangle( + rightToLeftInternal ? this.layout.Data.X - this.vertScrollBar.Width : this.layout.Data.Right, + this.layout.Data.Bottom, + this.vertScrollBar.Width, + this.horizScrollBar.Height); + } + + if (needHorizScrollbar && totalVisibleColCount > 0) + { + int widthNotVisible = totalVisibleWidth - this.layout.Data.Width; + + this.horizScrollBar.Minimum = 0; + this.horizScrollBar.Maximum = totalVisibleWidth - totalVisibleFrozenWidth; + Debug.Assert(this.horizScrollBar.Maximum > 0); + this.horizScrollBar.SmallChange = 1; + this.horizScrollBar.LargeChange = Math.Max(totalVisibleWidth - totalVisibleFrozenWidth - widthNotVisible, 0); + this.horizScrollBar.Enabled = this.Enabled; + this.horizScrollBar.Bounds = new Rectangle( + rightToLeftInternal ? this.layout.Inside.X + this.layout.ResizeBoxRect.Width : this.layout.Inside.X, + this.layout.Data.Bottom, + this.layout.Inside.Width - this.layout.ResizeBoxRect.Width, + this.horizScrollBar.Height); + this.horizScrollBar.Visible = true; + this.horizScrollBar.Invalidate(); + } + else + { + this.horizScrollBar.Visible = false; + this.HorizontalOffset = 0; + + this.horizScrollBar.Enabled = false; + this.horizScrollBar.Minimum = 0; + this.horizScrollBar.Maximum = 1; + this.horizScrollBar.SmallChange = 1; + this.horizScrollBar.LargeChange = 1; + this.horizScrollBar.Value = 0; + } + + if (needVertScrollbar) + { + int vertScrollBarTop = this.layout.Data.Y; + int vertScrollBarHeight = this.layout.Data.Height; + if (this.layout.ColumnHeadersVisible) + { + vertScrollBarTop = this.layout.ColumnHeaders.Y; + vertScrollBarHeight += this.layout.ColumnHeaders.Height; + } + else if (this.SingleHorizontalBorderAdded) + { + vertScrollBarTop--; + vertScrollBarHeight++; + } + + this.vertScrollBar.Minimum = 0; + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + Debug.Assert(this.vertScrollBar.Maximum > 0); + this.vertScrollBar.Value = ComputeHeightOfScrolledOffRows(); + this.vertScrollBar.LargeChange = this.layout.Data.Height - totalVisibleFrozenHeight; + this.vertScrollBar.Bounds = new Rectangle( + rightToLeftInternal ? this.layout.Data.X - this.vertScrollBar.Width : this.layout.Data.Right, + vertScrollBarTop, + this.vertScrollBar.Width, + vertScrollBarHeight); + this.vertScrollBar.Enabled = this.Enabled; + this.vertScrollBar.Visible = true; + this.vertScrollBar.Invalidate(); + + this.verticalOffset = this.vertScrollBar.Value; + } + else + { + this.vertScrollBar.Visible = false; + this.verticalOffset = ComputeHeightOfScrolledOffRows(); + + this.vertScrollBar.Enabled = false; + this.vertScrollBar.Minimum = 0; + this.vertScrollBar.Maximum = 1; + this.vertScrollBar.LargeChange = 1; + this.vertScrollBar.Value = 0; + } + } + finally + { + ResumeLayout(false); + } + } + + private void MakeFirstDisplayedCellCurrentCell(bool includeNewRow) + { + // No current cell - try to set the first displayed cell to be the current one. + Point firstDisplayedCellAddress = this.FirstDisplayedCellAddress; + if (firstDisplayedCellAddress.X != -1 && + (includeNewRow || + !this.AllowUserToAddRowsInternal || + firstDisplayedCellAddress.Y != this.Rows.Count - 1)) + { + bool success = SetAndSelectCurrentCellAddress(firstDisplayedCellAddress.X, + firstDisplayedCellAddress.Y, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + true /*clearSelection*/, + false /*forceCurrentCellSelection (unused)*/); + Debug.Assert(success); + } + } + + private static DataGridViewAutoSizeRowMode MapAutoSizeRowsModeToRowMode(DataGridViewAutoSizeRowsMode autoSizeRowsMode) + { + switch (autoSizeRowsMode) + { + case DataGridViewAutoSizeRowsMode.AllHeaders: + return DataGridViewAutoSizeRowMode.RowHeader; + case DataGridViewAutoSizeRowsMode.DisplayedHeaders: + return DataGridViewAutoSizeRowMode.RowHeader; + case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders: + return DataGridViewAutoSizeRowMode.AllCellsExceptHeader; + case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders: + return DataGridViewAutoSizeRowMode.AllCellsExceptHeader; + case DataGridViewAutoSizeRowsMode.AllCells: + return DataGridViewAutoSizeRowMode.AllCells; + case DataGridViewAutoSizeRowsMode.DisplayedCells: + return DataGridViewAutoSizeRowMode.AllCells; + default: + Debug.Fail("Unexpected autoSizeRowsMode value in MapAutoSizeRowsModeToRowMode"); + return DataGridViewAutoSizeRowMode.RowHeader; + } + } + + private void MoveColumnHeadersOrRowResize(MouseEventArgs e) + { + this.lastRowSplitBar = this.currentRowSplitBar; + this.currentRowSplitBar = e.Y; + Rectangle lastSplitBarRect = CalcRowResizeFeedbackRect(this.lastRowSplitBar); + if (this.editingControl != null && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] && + this.editingPanel.Bounds.IntersectsWith(lastSplitBarRect)) + { + this.editingPanel.Invalidate(); + this.editingPanel.Update(); + this.editingControl.Invalidate(); + this.editingControl.Update(); + } + Invalidate(lastSplitBarRect); + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void MapDataGridViewColumnToDataBoundField(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(this.DataSource != null, "this method should only be called when we have a data connection"); + Debug.Assert(dataGridViewColumn.DataPropertyName.Length != 0, "this method should be called only for columns which have DataPropertyName set"); + DataGridViewDataConnection conn = this.DataConnection; + + int boundColumnIndex = ((conn == null) ? -1 : conn.BoundColumnIndex(dataGridViewColumn.DataPropertyName)); + if (boundColumnIndex != -1) + { + dataGridViewColumn.IsDataBoundInternal = true; + dataGridViewColumn.BoundColumnIndex = boundColumnIndex; + dataGridViewColumn.BoundColumnConverter = conn.BoundColumnConverter(boundColumnIndex); + dataGridViewColumn.ValueType = conn.BoundColumnValueType(boundColumnIndex); + dataGridViewColumn.ReadOnly = conn.DataFieldIsReadOnly(dataGridViewColumn.BoundColumnIndex) || dataGridViewColumn.ReadOnly; + InvalidateColumnInternal(dataGridViewColumn.Index); + + // Set the Sorting information on the data grid view according to the new DataPropertyName. + // RefreshColumns() has its own routine for setting the Sorting information so don't do this step + // if we are in RefreshColumns(); + if (dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = conn.BoundColumnSortOrder(boundColumnIndex); + if (this.sortedColumn == null && dataGridViewColumn.HeaderCell.SortGlyphDirection != SortOrder.None) + { + this.sortedColumn = dataGridViewColumn; + this.sortOrder = dataGridViewColumn.HeaderCell.SortGlyphDirection; + // no need to sort because the back end is already sorted.... + } + } + } + else + { + dataGridViewColumn.IsDataBoundInternal = false; + dataGridViewColumn.BoundColumnIndex = -1; + dataGridViewColumn.BoundColumnConverter = null; + InvalidateColumnInternal(dataGridViewColumn.Index); + } + } + + private void MoveColumnRelocation(MouseEventArgs e, HitTestInfo hti) + { + this.lastHeaderShadow = e.X; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = ColumnRelocationTarget(e, hti, out this.trackColumnEdge); + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + + private void MoveRowHeadersOrColumnResize(int x) + { + this.lastColSplitBar = this.currentColSplitBar; + this.currentColSplitBar = x; + Rectangle lastSplitBarRect = CalcColResizeFeedbackRect(this.lastColSplitBar); + if (this.editingControl != null && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] && + this.editingPanel.Bounds.IntersectsWith(lastSplitBarRect)) + { + this.editingPanel.Invalidate(); + this.editingPanel.Update(); + this.editingControl.Invalidate(); + this.editingControl.Update(); + } + Invalidate(lastSplitBarRect); + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + /// + public virtual void NotifyCurrentCellDirty(bool dirty) + { + Debug.Assert(this.ptCurrentCell.X >= 0 && this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y >= 0 && this.ptCurrentCell.Y < this.Rows.Count); + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] == false) + { + // autosizing has no effect since edited value hasn't been committed + // and autosizing code only looks at committed values. + + this.IsCurrentCellDirtyInternal = dirty; + if (dirty && this.editingControl != null && ((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + internal void OnAddedColumn(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.Index < this.Columns.Count); + Debug.Assert(dataGridViewColumn.DataGridView == this); + + if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex >= this.Columns.Count) + { + // Developer did not assign a DisplayIndex or picked a large number. + // Choose the Index as the DisplayIndex. + dataGridViewColumn.DisplayIndexInternal = dataGridViewColumn.Index; + this.Columns.InvalidateCachedColumnsOrder(); + } + + CorrectColumnDisplayIndexesAfterInsertion(dataGridViewColumn); + + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.DataGridViewInternal = this; + } + + AdjustExpandingRows(dataGridViewColumn.Index, false /*fixedWidth*/); + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + bool fixedColumnWidth = autoSizeColumnMode == DataGridViewAutoSizeColumnMode.None || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + + if (!fixedColumnWidth) + { + // This is the first time the column autosizes. Save current column width for later reuse. + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, true /*fixedHeight*/); + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + + // Raise the ColumnAdded event + OnColumnAdded(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + internal void OnAddedRow_PreNotification(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + + if (this.AllowUserToAddRowsInternal && this.newRowIndex == -1) + { + // The added row is necessarily the 'new row' + // Set the this.newRowIndex variable as early as possible. + Debug.Assert(rowIndex == this.Rows.Count - 1); + this.newRowIndex = rowIndex; + } + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + +#if DEBUG + DataGridViewRow dataGridViewRowDebug = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRowDebug.Cells) + { + Debug.Assert(!dataGridViewCell.Selected); + Debug.Assert(dataGridViewRowDebug.Index != -1 || !dataGridViewCell.HasValue); + } +#endif + + // Update this.individualReadOnlyCells + if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (!dataGridViewCell.OwningColumn.ReadOnly && IsSharedCellReadOnly(dataGridViewCell, rowIndex)) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + } + } + } + } + + internal void OnAddedRow_PostNotification(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + + if ((rowState & DataGridViewElementStates.Visible) != 0) + { + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + + bool autoSizeRow = false; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None && + !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + this.Rows.SharedRow(rowIndex).CachedThickness = rowHeight; + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + bool columnAutoSized; + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible) > 1) + { + // Columns can only expand, and not collapse. + columnAutoSized = AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex); + } + else + { + columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + } + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool rowHeadersAutoSize = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + + if (!rowHeadersAutoSize && !columnAutoSized) + { + // No need to autosize the column headers when the row headers and columns don't change. + fixedColumnHeadersHeight = true; + } + + // Auto size row headers + if (rowHeadersAutoSize) + { + AutoResizeRowHeadersWidth(rowIndex, this.rowHeadersWidthSizeMode, fixedColumnHeadersHeight, true /*fixedRowsHeight*/); + } + + // Auto size column headers + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (rowHeadersAutoSize && !fixedColumnHeadersHeight) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(rowIndex, this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + } + } + + internal void OnAddedRows_PreNotification(DataGridViewRow[] dataGridViewRows) + { + // Note: no row can be added that breaks the frozen row packing on the top + + foreach(DataGridViewRow dataGridViewRow in dataGridViewRows) + { + OnAddedRow_PreNotification(dataGridViewRow.Index); + } + } + + internal void OnAddedRows_PostNotification(DataGridViewRow[] dataGridViewRows) + { + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + OnAddedRow_PostNotification(dataGridViewRow.Index); + } + } + + internal void OnAddingColumn(DataGridViewColumn dataGridViewColumn) + { + // throw an exception if the column to be added breaks the rules + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None) + dataGridViewColumn.FillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectColumnFrozenState(dataGridViewColumn, this.Columns.Count); + + // prepare the existing rows by appending cells of correct type + if (this.Rows.Count > 0) + { + // Only require a default cell type when there are rows to fill + if (dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + } + + int newColumnCount = this.Columns.Count + 1; + + try + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < newColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + dataGridViewRow.Cells.AddInternal(dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.SetValueInternal(rowIndex, dataGridViewCellNew.DefaultNewRowValue); + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + catch + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count == newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(newColumnCount - 1); + } + else + { + Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount); + break; + } + } + throw; + } + } + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // not legitimate + ] + internal void OnAddingColumns(DataGridViewColumn[] dataGridViewColumns) + { + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None); + Debug.Assert(weightSum <= (float)ushort.MaxValue); + + // throw an exception if any of the columns to be added breaks the rules + Debug.Assert(dataGridViewColumns != null); + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + if (dataGridViewColumn == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AtLeastOneColumnIsNull)); + } + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + // Only require a default cell type when there are rows to fill + if (this.Rows.Count > 0 && dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + weightSum += dataGridViewColumn.FillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + } + + Debug.Assert(weightSum <= (float)ushort.MaxValue); + + // make sure no two columns are identical + int columnCount = dataGridViewColumns.Length; + for (int column1 = 0; column1 < columnCount - 1; column1++) + { + for (int column2 = column1 + 1; column2 < columnCount; column2++) + { + if (dataGridViewColumns[column1] == dataGridViewColumns[column2]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddIdenticalColumns)); + } + } + } + + // check for correctness of frozen states - throws exception if any state is incorrect. + CorrectColumnFrozenStates(dataGridViewColumns); + + // prepare the existing rows by appending cells of correct type + if (this.Rows.Count > 0) + { + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + Debug.Assert(dataGridViewColumn.CellType != null); + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + break; + } + } + + int previousColumnCount = this.Columns.Count; + int addedColumnCount = 0; + + try + { + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + addedColumnCount++; + Debug.Assert(dataGridViewColumn.CellType != null); + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < previousColumnCount + addedColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + int indexCell = dataGridViewRow.Cells.AddInternal(dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue; + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + } + catch + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + while (dataGridViewRow.Cells.Count > previousColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(dataGridViewRow.Cells.Count - 1); + } + } + throw; + } + } + } + + internal void OnAddingRow(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, bool checkFrozenState) + { + // Note dataGridViewRow.DataGridView != null for duplication of shared rows. + + // throw an exception if the row to be added breaks the rules + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + + // !Do not check for dataGridViewRow.Selected flag. Caller does it instead! + // !Do not check for dataGridViewRow.DataGridView != null. Caller does it instead! + + if (checkFrozenState) + { + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectRowFrozenState(dataGridViewRow, rowState, this.Rows.Count); + } + + if (this.ReadOnly && dataGridViewRow.DataGridView == null && dataGridViewRow.ReadOnly) + { + // Clear the superfluous flag since the whole dataGridView is read-only + dataGridViewRow.ReadOnly = false; + } + + int columnIndex = 0; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if ((this.ReadOnly || dataGridViewColumn.ReadOnly) && dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly)) + { + // Clear superfluous flag since the whole dataGridView or column is ReadOnly + dataGridViewCell.ReadOnlyInternal = false; + } + columnIndex++; + } + } + + internal void OnAddingRows(DataGridViewRow[] dataGridViewRows, bool checkFrozenStates) + { + // throw an exception if any of the rows to be added breaks the rules + Debug.Assert(dataGridViewRows != null); + + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + if (dataGridViewRow == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AtLeastOneRowIsNull)); + } + + if (dataGridViewRow.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + + if (dataGridViewRow.Selected) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CannotAddOrInsertSelectedRow)); + } + + if (dataGridViewRow.Cells.Count > this.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_TooManyCells)); + } + } + + // make sure no two rows are identical + int rowCount = dataGridViewRows.Length; + for (int row1 = 0; row1 < rowCount - 1; row1++) + { + for (int row2 = row1 + 1; row2 < rowCount; row2++) + { + if (dataGridViewRows[row1] == dataGridViewRows[row2]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddIdenticalRows)); + } + } + } + + if (checkFrozenStates) + { + Debug.Assert(!this.AllowUserToAddRowsInternal); + CorrectRowFrozenStates(dataGridViewRows, this.Rows.Count /*rowIndexInserted*/); + } + + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + CompleteCellsCollection(dataGridViewRow); + OnAddingRow(dataGridViewRow, dataGridViewRow.State, false /*checkFrozenState*/); + } + } + + internal void OnAdvancedBorderStyleChanged(DataGridViewAdvancedBorderStyle dgvabs) + { + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange]) + { + if (dgvabs == this.advancedCellBorderStyle) + { + OnCellBorderStyleChanged(EventArgs.Empty); + } + else if (dgvabs == this.advancedColumnHeadersBorderStyle) + { + OnColumnHeadersBorderStyleChanged(EventArgs.Empty); + } + else if (dgvabs == this.advancedRowHeadersBorderStyle) + { + OnRowHeadersBorderStyleChanged(EventArgs.Empty); + } + } + } + + /// + protected virtual void OnAllowUserToAddRowsChanged(EventArgs e) + { + PushAllowUserToAddRows(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTOADDROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToDeleteRowsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTODELETEROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToOrderColumnsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTOORDERCOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToResizeColumnsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTORESIZECOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToResizeRowsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTORESIZEROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAlternatingRowsDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + InvalidateData(); + } + else + { + OnRowsGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALTERNATINGROWSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAutoGenerateColumnsChanged(EventArgs e) + { + if (this.AutoGenerateColumns && this.DataSource != null) + { + // refresh the list of columns and the rows + RefreshColumnsAndRows(); + } + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOGENERATECOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnAutoSizeColumnModeChanged(DataGridViewColumn dataGridViewColumn, DataGridViewAutoSizeColumnMode previousInheritedMode) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewAutoSizeColumnModeEventArgs dgvascme = new DataGridViewAutoSizeColumnModeEventArgs(dataGridViewColumn, previousInheritedMode); + OnAutoSizeColumnModeChanged(dgvascme); + } + + /// + protected virtual void OnAutoSizeColumnModeChanged(DataGridViewAutoSizeColumnModeEventArgs e) + { + DataGridViewColumn dataGridViewColumn = e.Column; + if (e.Column == null) + { + throw new InvalidOperationException(SR.GetString(SR.InvalidNullArgument, "e.Column")); + } + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + + DataGridViewAutoSizeColumnMode previousInheritedMode = e.PreviousMode; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet; + + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill || + previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None) + { + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + if (!previousInheritedModeAutoSized) + { + // Save current column width for later reuse + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + } + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal) autoSizeColumnMode, + fixedHeight); + } + } + else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized) + { + // Restoring cached column width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + + // Auto fill columns if needed + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + // Autosize rows and column headers if needed + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + + // Column gets autosized with 1 degree of freedom this time. + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + true /*fixedHeight*/); + } + } + + DataGridViewAutoSizeColumnModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZECOLUMNMODECHANGED] as DataGridViewAutoSizeColumnModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.PreviousModes is more precise than just e + protected virtual void OnAutoSizeColumnsModeChanged(DataGridViewAutoSizeColumnsModeEventArgs e) + { + DataGridViewAutoSizeColumnMode[] previousModes = e.PreviousModes; + if (previousModes == null) + { + throw new ArgumentNullException("e.PreviousModes"); + } + if (previousModes.Length != this.Columns.Count) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_PreviousModesHasWrongLength)); + } + + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.Visible) + { + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + DataGridViewAutoSizeColumnMode previousInheritedMode = previousModes[dataGridViewColumn.Index]; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet; + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill || + previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None) + { + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + if (!previousInheritedModeAutoSized) + { + // Save current column width for later reuse + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + } + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0 /*fixedHeight*/); + } + } + else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized) + { + // Restoring cached column width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + } + } + + // Auto fill columns if needed + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + // Autosize rows and column headers if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + + // Second pass of column autosizing with 1 degree of freedom + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + true /*fixedHeight*/); + } + } + } + + DataGridViewAutoSizeColumnsModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZECOLUMNSMODECHANGED] as DataGridViewAutoSizeColumnsModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAutoSizeRowsModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + // restore cached rows thickness + RestoreRowsCachedThickness(); + } + else + { + if (!e.PreviousModeAutoSized) + { + // Save the rows thickness for later reuse + // Note that only visible rows are affected, contrary to columns in OnAutoSizeColumnsModeChanged where all columns are affected. + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + this.Rows.SharedRow(rowIndex).CachedThickness = rowHeight; + } + } + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + DataGridViewAutoSizeModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZEROWSMODECHANGED] as DataGridViewAutoSizeModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnBackgroundColorChanged(EventArgs e) + { + InvalidateInside(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWBACKGROUNDCOLORCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnBandContextMenuStripChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnContextMenuStripChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowContextMenuStripChanged(dgvre); + } + } + + internal void OnBandDefaultCellStyleChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDefaultCellStyleChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowDefaultCellStyleChanged(dgvre); + } + } + + internal void OnBandDividerThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDividerWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowDividerHeightChanged(dgvre); + } + } + + internal void OnBandHeaderCellChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnHeaderCellChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowHeaderCellChanged(dgvre); + } + } + + internal void OnBandMinimumThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnMinimumWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowMinimumHeightChanged(dgvre); + } + } + + internal void OnBandThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowHeightChanged(dgvre); + } + } + + internal void OnBandThicknessChanging() + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + } + + /// + protected override void OnBindingContextChanged(EventArgs e) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged]) + { + return; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged] = true; + try + { + if (this.dataConnection != null) + { + this.CurrentCell = null; + try + { + this.dataConnection.SetDataConnection(this.DataSource, this.DataMember); + } + catch (ArgumentException) + { + if (this.DesignMode) + { + // This is the minimal fix for vsw 527646. + // If the DataMember became invalid at DesignTime then set it to String.Empty, + // regenerate the column collection and DO NOT send BindingContextChanged event. + this.DataMember = String.Empty; + RefreshColumnsAndRows(); + return; + } + else + { + throw; + } + } + RefreshColumnsAndRows(); + base.OnBindingContextChanged(e); + if (this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + else + { + base.OnBindingContextChanged(e); + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged] = false; + } + } + + /// + protected virtual void OnBorderStyleChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCancelRowEdit(QuestionEventArgs e) + { + QuestionEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCANCELROWEDIT] as QuestionEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellBeginEdit(DataGridViewCellCancelEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLBEGINEDIT] as DataGridViewCellCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCellBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellClickInternal(DataGridViewCellEventArgs e) + { + OnCellClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnClickInternal(e); + } + else + { + dataGridViewCell.OnClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellCommonChange(int columnIndex, int rowIndex) + { + if (columnIndex == -1) + { + // row or topleft header characteristic has changed + OnRowHeaderGlobalAutoSize(rowIndex); + } + else + { + if (rowIndex == -1) + { + // column header characteristic has changed + OnColumnHeaderGlobalAutoSize(columnIndex); + } + else + { + // regular cell characteristic changed + InvalidateCellPrivate(columnIndex, rowIndex); + + bool rowDisplayed = false; + if (rowIndex != -1) + { + rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + } + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + bool autoSizeColumn = (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.AllRows) != 0; + if (rowDisplayed) + { + autoSizeColumn |= (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows) != 0; + } + + bool autoSizeRow = (((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0; + if (autoSizeRow) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), !autoSizeColumn /*fixedWidth*/, true /*internalAutosizing*/); + } + if (autoSizeColumn) + { + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + if (autoSizeRow) + { + // Second round of row autosizing with 1 degree of freedom. + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + } + } + + internal void OnCellContentClickInternal(DataGridViewCellEventArgs e) + { + OnCellContentClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContentClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ContentClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentClickInternal(e); + } + else + { + dataGridViewCell.OnContentClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTENTCLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellContentDoubleClickInternal(DataGridViewCellEventArgs e) + { + OnCellContentDoubleClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContentDoubleClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ContentDoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnContentDoubleClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTENTDOUBLECLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellContextMenuStripChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellContextMenuStripChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContextMenuStripChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal ContextMenuStrip OnCellContextMenuStripNeeded(int columnIndex, int rowIndex, ContextMenuStrip contextMenuStrip) + { + DataGridViewCellContextMenuStripNeededEventArgs dgvccmsne = new DataGridViewCellContextMenuStripNeededEventArgs(columnIndex, rowIndex, contextMenuStrip); + OnCellContextMenuStripNeeded(dgvccmsne); + return dgvccmsne.ContextMenuStrip; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContextMenuStripNeeded(DataGridViewCellContextMenuStripNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellContextMenuStripNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPNEEDED] as DataGridViewCellContextMenuStripNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellDoubleClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.DoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnDoubleClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLDOUBLECLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellEndEdit(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLENDEDIT] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellEnter(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellEnter(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellEnter(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal void OnCellErrorTextChanged(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell.RowIndex >= -1); + Debug.Assert(dataGridViewCell.ColumnIndex >= -1); + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellErrorTextChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellErrorTextChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + UpdateCellErrorText(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLERRORTEXTCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnCellErrorTextNeeded(int columnIndex, int rowIndex, string errorText) + { + Debug.Assert(columnIndex >= 0); + Debug.Assert(rowIndex >= 0); + DataGridViewCellErrorTextNeededEventArgs dgvcetne = new DataGridViewCellErrorTextNeededEventArgs(columnIndex, rowIndex, errorText); + OnCellErrorTextNeeded(dgvcetne); + return dgvcetne.ErrorText; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellErrorTextNeeded(DataGridViewCellErrorTextNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellErrorTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLERRORTEXTNEEDED] as DataGridViewCellErrorTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal DataGridViewCellFormattingEventArgs OnCellFormatting(int columnIndex, int rowIndex, object val, Type formattedValueType, DataGridViewCellStyle cellStyle) + { + DataGridViewCellFormattingEventArgs dgvcfe = new DataGridViewCellFormattingEventArgs(columnIndex, + rowIndex, + val, + formattedValueType, + cellStyle); + OnCellFormatting(dgvcfe); + return dgvcfe; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellFormatting(DataGridViewCellFormattingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellFormattingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLFORMATTING] as DataGridViewCellFormattingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellLeave(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellLeave(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellLeave(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLLEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseClick(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseClickInternal(e); + } + else + { + dataGridViewCell.OnMouseClickInternal(e); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble] = false; + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseDoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnMouseDoubleClickInternal(e); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble] = true; + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseDown(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + + // Only left clicks for now + Keys nModifier = ModifierKeys; + bool isControlDown = (nModifier & Keys.Control) == Keys.Control && (nModifier & Keys.Alt) == 0; + bool isShiftDown = (nModifier & Keys.Shift) == Keys.Shift; + bool isAltDown = (nModifier & Keys.Alt) == Keys.Alt; + + Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y); + + HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && e.Button == MouseButtons.Left) + { + Debug.Assert(hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar); + Debug.Assert(this.ptMouseDownCell.X == hti.col); + Debug.Assert(this.ptMouseDownCell.Y == hti.row); + + switch (hti.typeInternal) + { + // Check for column/row (headers) resize + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + this.dataGridViewOper[DATAGRIDVIEWOPER_resizingOperationAboutToStart] = (e.Clicks == 1); + break; + } + } + } + + try + { + if (e.RowIndex >= 0 && dataGridViewCell.MouseDownUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDownInternal(e); + } + else + { + dataGridViewCell.OnMouseDownInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEDOWN] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && e.Button == MouseButtons.Left) + { + switch (hti.typeInternal) + { + // Check column resize + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + { + int columnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti.col : hti.adjacentCol; + Debug.Assert(this.Columns[columnIndex].Resizable == DataGridViewTriState.True); + if (e.Clicks == 1) + { + BeginMouseColumnResize(ptGridCoord.X, hti.mouseBarOffset, columnIndex); + } + break; + } + + // Check row resize + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + { + int rowIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti.row : hti.adjacentRow; + if (e.Clicks == 1) + { + BeginRowResize(ptGridCoord.Y, hti.mouseBarOffset, rowIndex); + } + break; + } + + // Check for column header mouse down + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + if (isAltDown && this.AllowUserToOrderColumns && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + BeginColumnRelocation(ptGridCoord.X, hti.col); + } + else + { + OnColumnHeaderMouseDown(hti, isShiftDown, isControlDown); + } + break; + } + + // Check for row header mouse down + case DataGridViewHitTestTypeInternal.RowHeader: + { + OnRowHeaderMouseDown(hti, isShiftDown, isControlDown); + break; + } + + // Check for cell mouse down + case DataGridViewHitTestTypeInternal.Cell: + { + OnCellMouseDown(hti, isShiftDown, isControlDown); + break; + } + + // Check for top/left header mouse down + case DataGridViewHitTestTypeInternal.TopLeftHeader: + { + OnTopLeftHeaderMouseDown(); + break; + } + + // Check for row headers resize + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + { + if (e.Clicks == 1) + { + BeginRowHeadersResize(ptGridCoord.X, hti.mouseBarOffset); + } + break; + } + + // Check for column headers resize + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + if (e.Clicks == 1) + { + BeginColumnHeadersResize(ptGridCoord.Y, hti.mouseBarOffset); + } + break; + } + } + // Make sure that there is a current cell after this mouse down event. + if (this.ptCurrentCell.X == -1) + { + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_resizingOperationAboutToStart] = false; + } + } + + private void OnCellMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.Cell); + // Only commit cell if the target cell is different from the current one. + if (this.ptCurrentCell.X >= 0 && + (this.ptCurrentCell.X != hti.col || this.ptCurrentCell.Y != hti.row)) + { + Point ptOriginalCurrentCell = this.ptCurrentCell; + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + this.ptCurrentCell.X != hti.col || this.ptCurrentCell.Y != hti.row /*forCurrentCellChange*/, + this.ptCurrentCell.Y != hti.row /*forCurrentRowChange*/)) + { + // Return silently if validating/commit/abort failed + return; + } + if (this.ptCurrentCell != ptOriginalCurrentCell) + { + // VSWhidbey 492203. Somehow the fact that the current cell was committed altered the current cell value. + // To avoid unintentional multi-selections, we act as if Shift and Control keys were up. + isShiftDown = isControlDown = false; + } + } + + if (hti.col >= this.Columns.Count) + { + DataGridViewColumn dataGridViewLastVisibleColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (this.ptCurrentCell.X == -1 && dataGridViewLastVisibleColumn != null) + { + // CurrentCell was reset because CommitEdit deleted column(s). + // Since the user clicked on a cell, we don't want to end up + // with no CurrentCell. We pick the last visible column in the grid. + hti.col = dataGridViewLastVisibleColumn.Index; + } + else + { + return; + } + } + if (hti.row >= this.Rows.Count) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (this.ptCurrentCell.X == -1 && lastVisibleRowIndex != -1) + { + // CurrentCell was reset because CommitEdit deleted row(s). + // Since the user clicked on a cell, we don't want to end up + // with no CurrentCell. We pick the last visible row in the + // grid which may be the 'new row'. + hti.row = lastVisibleRowIndex; + } + else + { + return; + } + } + + bool select = true; + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (isControlDown && + IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if ((!this.MultiSelect || !isControlDown) && !(this.MultiSelect && isShiftDown)) + { + Debug.Assert(this.MultiSelect || this.individualSelectedCells.Count <= 1); + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + if (this.MultiSelect) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + { + if (isControlDown && this.Columns[hti.col].Selected) + { + select = false; + } + if (select) + { + bool selectColumnRange = false; + this.trackColumn = hti.col; + this.trackColumnEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.X > -1 && + this.Columns[this.ptAnchorCell.X].Selected) + { + selectColumnRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.col) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = true; + } + if (selectColumnRange) + { + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, hti.col)) + { + SelectColumnRange(this.ptAnchorCell.X, hti.col, true); + } + else + { + SelectColumnRange(hti.col, this.ptAnchorCell.X, true); + } + } + else if (!this.selectedBandIndexes.Contains(hti.col)) + { + SetSelectedColumnCore(hti.col, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.col)); + SetSelectedColumnCore(hti.col, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (isControlDown && + (this.Columns[hti.col].Selected || IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row)) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(hti.col, hti.row, true); + } + else + { + // this.MultiSelect == true + if (!isControlDown && !isShiftDown) + { + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + { + if (isControlDown && + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)) + { + select = false; + } + if (select) + { + bool selectRowRange = false; + this.trackRow = hti.row; + this.trackRowEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.Y > -1 && (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0) + { + selectRowRange = true; + } + + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.row) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = true; + } + if (selectRowRange) + { + if (hti.row >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, hti.row, true); + } + else + { + SelectRowRange(hti.row, this.ptAnchorCell.Y, true); + } + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row) == + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(hti.row, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row)); + SetSelectedRowCore(hti.row, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (isControlDown && + (((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0) || + IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row)) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(hti.col, hti.row, true); + } + else + { + // this.MultiSelect == true + if (!isControlDown && !isShiftDown) + { + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseEnter(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + this.ptMouseEnteredCell.X = e.ColumnIndex; + this.ptMouseEnteredCell.Y = e.RowIndex; + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseEnterUnsharesRowInternal(e.RowIndex)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseEnterInternal(e.RowIndex); + } + else + { + dataGridViewCell.OnMouseEnterInternal(e.RowIndex); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseLeave(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + this.ptMouseEnteredCell.X = -2; + this.ptMouseEnteredCell.Y = -2; + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseLeaveUnsharesRowInternal(e.RowIndex)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseLeaveInternal(e.RowIndex); + } + else + { + dataGridViewCell.OnMouseLeaveInternal(e.RowIndex); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSELEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseMove(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseMoveUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseMoveInternal(e); + } + else + { + dataGridViewCell.OnMouseMoveInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEMOVE] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] && + !this.IsMouseOperationActive() && + this.AllowUserToOrderColumns && + this.SelectionMode != DataGridViewSelectionMode.FullColumnSelect && + this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect && + e.Button == MouseButtons.Left && + this.ptMouseDownCell.Y == -1 && + this.ptMouseDownCell.X >= 0 && + this.ptMouseDownCell.X < this.Columns.Count) + { + Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y); + + HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y); + + Debug.Assert(hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar); + + switch (hti.typeInternal) + { + // Check for column header mouse down + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]); + if (Math.Abs(this.ptMouseDownGridCoord.X - ptGridCoord.X) >= DataGridView.DragSize.Width || + Math.Abs(this.ptMouseDownGridCoord.Y - ptGridCoord.Y) >= DataGridView.DragSize.Height) + { + BeginColumnRelocation(this.ptMouseDownGridCoord.X, this.ptMouseDownCell.X); + } + break; + } + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseUp(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseUpUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseUpInternal(e); + } + else + { + dataGridViewCell.OnMouseUpInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEUP] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected internal virtual void OnCellPainting(DataGridViewCellPaintingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellPaintingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLPAINTING] as DataGridViewCellPaintingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal DataGridViewCellParsingEventArgs OnCellParsing(int rowIndex, int columnIndex, object formattedValue, Type valueType, DataGridViewCellStyle cellStyle) + { + DataGridViewCellParsingEventArgs dgvcpe = new DataGridViewCellParsingEventArgs(rowIndex, columnIndex, + formattedValue, + valueType, + cellStyle); + OnCellParsing(dgvcpe); + return dgvcpe; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellParsing(DataGridViewCellParsingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellParsingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLPARSING] as DataGridViewCellParsingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnCellSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(this.MultiSelect); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if ((hti.col != this.ptCurrentCell.X || hti.row != this.ptCurrentCell.Y) && + !CommitEditForOperation(hti.col, hti.row, true)) + { + // Return silently if validating/commit/abort failed + return; + } + this.noSelectionChangeCount++; + try + { + if (this.ptAnchorCell.X == -1 || IsInnerCellOutOfBounds(hti.col, hti.row)) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + if (hti.col != this.ptCurrentCell.X || hti.row != this.ptCurrentCell.Y) + { + bool success = SetCurrentCellAddressCore(hti.col, hti.row, false, false, false); + Debug.Assert(success); + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnCellStateChanged(DataGridViewCellStateChangedEventArgs e) + { + // At this point we assume that only the Selected state has an influence on the rendering of the cell. + // If there is a scenario where another state has an effect, then the dev will have to invalidate the cell by hand. + DataGridViewCell dataGridViewCell = e.Cell; + if (e.StateChanged == DataGridViewElementStates.Selected) + { + Debug.Assert(dataGridViewCell.RowIndex >= 0); + if (this.inBulkPaintCount == 0) + { + InvalidateCellPrivate(dataGridViewCell); + } + } + + DataGridViewCellStateChangedEventHandler eh = Events[EVENT_DATAGRIDVIEWCELLSTATECHANGED] as DataGridViewCellStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + this.ptCurrentCell.X == dataGridViewCell.ColumnIndex && + this.ptCurrentCell.Y == dataGridViewCell.RowIndex && + dataGridViewCell.RowIndex > -1 && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if (!dataGridViewCell.ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current cell becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnCellStyleChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellStyleChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellStyleChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + OnCellCommonChange(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLSTYLECHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellStyleContentChanged(DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCellStyle.DataGridViewCellStylePropertyInternal property) + { + Debug.Assert(dataGridViewCellStyle != null); + switch (property) + { + case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Font: + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = false; + } + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.ColumnHeaders) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] = false; + } + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.RowHeaders) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] = false; + } + break; + + case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor: + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = false; + } + break; + } + + DataGridViewCellStyleContentChangedEventArgs dgvcscce = new DataGridViewCellStyleContentChangedEventArgs(dataGridViewCellStyle, + property != DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Color && + property != DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor /*changeAffectsPreferredSize*/); + OnCellStyleContentChanged(dgvcscce); + } + + /// + protected virtual void OnCellStyleContentChanged(DataGridViewCellStyleContentChangedEventArgs e) + { + // We assume that when a color changes, it has no effect on the autosize of elements + bool repositionEditingControl = false; + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Cell) == DataGridViewCellStyleScopes.Cell && (e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == 0) + { + // Same processing as in OnDefaultCellStyleChanged + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + OnGlobalAutoSize(); + } + else + { + Invalidate(); + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Column) == DataGridViewCellStyleScopes.Column) + { + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + OnColumnsGlobalAutoSize(); + } + else + { + InvalidateData(); + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Row) == DataGridViewCellStyleScopes.Row && (e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == 0 && (e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == 0) + { + // Same processing as in OnRowsDefaultCellStyleChanged + InvalidateData(); + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + // Autosize rows if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + // Auto size columms also if needed + // Impossible to figure out if DisplayedRows filter should be added or not. Adding it to be on the conservative side. + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | + DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + true /*fixedHeight*/); + // Second round of rows autosizing + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == DataGridViewCellStyleScopes.DataGridView) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnDefaultCellStyleChanged will reposition the editing control. + } + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.ColumnHeaders) == DataGridViewCellStyleScopes.ColumnHeaders) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnColumnHeadersDefaultCellStyleChanged will reposition the editing control. + } + OnColumnHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.RowHeaders) == DataGridViewCellStyleScopes.RowHeaders) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnRowHeadersDefaultCellStyleChanged will reposition the editing control. + } + OnRowHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == DataGridViewCellStyleScopes.Rows) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnRowsDefaultCellStyleChanged will reposition the editing control. + } + OnRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == DataGridViewCellStyleScopes.AlternatingRows) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnAlternatingRowsDefaultCellStyleChanged will reposition the editing control. + } + OnAlternatingRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (repositionEditingControl && this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + + DataGridViewCellStyleContentChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLSTYLECONTENTCHANGED] as DataGridViewCellStyleContentChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellToolTipTextChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellToolTipTextChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellToolTipTextChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnCellToolTipTextNeeded(int columnIndex, int rowIndex, string toolTipText) + { + DataGridViewCellToolTipTextNeededEventArgs dgvctttne = new DataGridViewCellToolTipTextNeededEventArgs(columnIndex, rowIndex, toolTipText); + OnCellToolTipTextNeeded(dgvctttne); + return dgvctttne.ToolTipText; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellToolTipTextNeeded(DataGridViewCellToolTipTextNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellToolTipTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTNEEDED] as DataGridViewCellToolTipTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellValidated(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellValidated(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValidated(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALIDATED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal bool OnCellValidating(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex, DataGridViewDataErrorContexts context) + { + DataGridViewCell currentCell = (dataGridViewCell == null) ? this.CurrentCellInternal : dataGridViewCell; + DataGridViewCellStyle dataGridViewCellStyle = currentCell.GetInheritedStyle(null, rowIndex, false); + object val = currentCell.GetValueInternal(rowIndex); + object editedFormattedValue = currentCell.GetEditedFormattedValue(val, rowIndex, ref dataGridViewCellStyle, context); + DataGridViewCellValidatingEventArgs dgvcfvce = new DataGridViewCellValidatingEventArgs(columnIndex, rowIndex, editedFormattedValue); + OnCellValidating(dgvcfvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + return dgvcfvce.Cancel; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValidating(DataGridViewCellValidatingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating] = true; + + DataGridViewCellValidatingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALIDATING] as DataGridViewCellValidatingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating] = false; + } + } + + internal void OnCellValueChangedInternal(DataGridViewCellEventArgs e) + { + // For now, same effect as if the cell style had changed. + OnCellValueChanged(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValueChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + OnCellCommonChange(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUECHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal object OnCellValueNeeded(int columnIndex, int rowIndex) + { + DataGridViewCellValueEventArgs dgvcve = this.CellValueEventArgs; + dgvcve.SetProperties(columnIndex, rowIndex, null); + OnCellValueNeeded(dgvcve); + return dgvcve.Value; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValueNeeded(DataGridViewCellValueEventArgs e) + { + if (e.ColumnIndex < 0 || e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex < 0 || e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } +//#if DEBUG + // Some customer scenarios may result in accessing cell values while this.dataStoreAccessAllowed is false. This is bad practice, + // but since we're late in Whidbey, throwing an exception would be destabilizing our internal customers. + // Debug.Assert(this.dataStoreAccessAllowed); +//#endif + DataGridViewCellValueEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUENEEDED] as DataGridViewCellValueEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellValuePushed(int columnIndex, int rowIndex, object value) + { + DataGridViewCellValueEventArgs dgvcve = this.CellValueEventArgs; + dgvcve.SetProperties(columnIndex, rowIndex, value); + OnCellValuePushed(dgvcve); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValuePushed(DataGridViewCellValueEventArgs e) + { + if (e.ColumnIndex < 0 || e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex < 0 || e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellValueEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUEPUSHED] as DataGridViewCellValueEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnClearedRows() + { + // Raise the RowStateChanged(Displayed->false) events + foreach (DataGridViewRow dataGridViewRow in this.lstRows) + { + if (dataGridViewRow.Displayed) + { + dataGridViewRow.DisplayedInternal = false; + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, DataGridViewElementStates.Displayed); + OnRowStateChanged(-1 /*rowIndex*/, dgvrsce); + } + } + this.lstRows.Clear(); + } + + internal void OnClearingColumns() + { + this.CurrentCell = null; + + // Rows need to be cleared first. There cannot be rows without also having columns. + this.Rows.ClearInternal(false /*recreateNewRow*/); + + // Reset sort related variables. + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + + // selectedBandIndexes, individualSelectedCells & individualReadOnlyCells cleared in OnClearingRows. + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + internal void OnClearingRows() + { + // Build a list of displayed rows in order to be able to raise their RowStateChanged(Displayed->false) events later on + this.lstRows.Clear(); + int numDisplayedRows = this.displayedBandsInfo.NumDisplayedFrozenRows + this.displayedBandsInfo.NumDisplayedScrollingRows; + if (numDisplayedRows > 0) + { + this.lstRows.Capacity = numDisplayedRows; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Displayed); + while (numDisplayedRows > 0 && rowIndex != -1) + { + this.lstRows.Add(this.Rows[rowIndex]); + numDisplayedRows--; + if (numDisplayedRows > 0) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Displayed); + } + } + } + + this.CurrentCell = null; + + this.newRowIndex = -1; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = this.selectedBandIndexes.Count > 0 || + this.individualSelectedCells.Count > 0; + this.selectedBandIndexes.Clear(); + if (this.selectedBandSnapshotIndexes != null) + { + this.selectedBandSnapshotIndexes.Clear(); + } + this.individualSelectedCells.Clear(); + this.individualReadOnlyCells.Clear(); + } + + /// + protected virtual void OnColumnAdded(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNADDED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnCollectionChanged_PreNotification(CollectionChangeEventArgs ccea) + { + // we need to map columns w/ DataPropertyName to bound columns + if (this.DataSource != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + if (ccea.Action == CollectionChangeAction.Add) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)ccea.Element; + if (dataGridViewColumn.DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(dataGridViewColumn); + } + } + else if (ccea.Action == CollectionChangeAction.Refresh) + { + for (int i = 0; i < this.Columns.Count; i++) + { + if (this.Columns[i].DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(this.Columns[i]); + } + } + } + } + + ResetUIState(false /*useRowShortcut*/, false /*computeVisibleRows*/); + } + + internal void OnColumnCollectionChanged_PostNotification(DataGridViewColumn dataGridViewColumn) + { + if (this.Columns.Count != 0 && this.Rows.Count == 0) + { + if (this.DataSource != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + // this will create the 'add new row' when AllowUserToAddRowsInternal == true + RefreshRows(true /*scrollIntoView*/); + } + else if (this.AllowUserToAddRowsInternal) + { + AddNewRow(false); + } + } + if (this.AutoSize && (dataGridViewColumn == null || dataGridViewColumn.Visible)) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Columns); + } + } + + internal void OnColumnCommonChange(int columnIndex) + { + OnColumnGlobalAutoSize(columnIndex); + } + + /// + protected virtual void OnColumnContextMenuStripChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNCONTEXTMENUSTRIPCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDataPropertyNameChanged(DataGridViewColumn dataGridViewColumn) + { + OnColumnDataPropertyNameChanged(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnDataPropertyNameChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + // map the dataGridView column to some data field + if (this.DataSource != null && e.Column.DataPropertyName.Length != 0 && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + MapDataGridViewColumnToDataBoundField(e.Column); + } + else if (this.DataSource != null && e.Column.DataPropertyName.Length == 0) + { + if (e.Column.IsDataBound) + { + e.Column.IsDataBoundInternal = false; + e.Column.BoundColumnIndex = -1; + e.Column.BoundColumnConverter = null; + InvalidateColumnInternal(e.Column.Index); + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDATAPROPERTYNAMECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnDefaultCellStyleChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + OnColumnGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDEFAULTCELLSTYLECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDisplayIndexChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDisplayIndexChanged(dgvce); + } + + internal void OnColumnDisplayIndexChanging(DataGridViewColumn dataGridViewColumn, int newDisplayIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex); + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]) + { + // We are within columns display indexes adjustments. We do not allow changing display indexes while adjusting them. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + // Throws an exception if the requested move is illegal + CorrectColumnFrozenStatesForMove(dataGridViewColumn, newDisplayIndex); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // Move is legal - let's adjust the affected display indexes. + if (newDisplayIndex < dataGridViewColumn.DisplayIndex) + { + // DisplayIndex decreases. All columns with newDisplayIndex <= DisplayIndex < dataGridViewColumn.DisplayIndex + // get their DisplayIndex incremented. + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (newDisplayIndex <= dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex < dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + } + else + { + // DisplayIndex increases. All columns with dataGridViewColumn.DisplayIndex < DisplayIndex <= newDisplayIndex + // get their DisplayIndex incremented. + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumn.DisplayIndex < dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex <= newDisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + } + + // Note that displayIndex of moved column is updated by caller. + } + + /// + protected virtual void OnColumnDisplayIndexChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDISPLAYINDEXCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDisplayIndexChanged_PreNotification() + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); + + // column.DisplayIndex changed - this may require a complete re-layout of the control + this.Columns.InvalidateCachedColumnsOrder(); + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + if (this.editingControl != null) + { + PositionEditingControl(true, true, false); + } + Invalidate(Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data)); + } + + internal void OnColumnDisplayIndexChanged_PostNotification() + { + // Notifications for adjusted display indexes. + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + + /// + protected virtual void OnColumnDividerDoubleClick(DataGridViewColumnDividerDoubleClickEventArgs e) + { + DataGridViewColumnDividerDoubleClickEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDIVIDERDOUBLECLICK] as DataGridViewColumnDividerDoubleClickEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!e.Handled && e.Button == MouseButtons.Left && e.ColumnIndex < this.Columns.Count) + { + if (e.ColumnIndex == -1) + { + AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + else + { + DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = this.Columns[e.ColumnIndex].InheritedAutoSizeMode; + if (inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(e.ColumnIndex, DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows, true /*fixedHeight*/); + } + else + { + AutoResizeColumnInternal(e.ColumnIndex, (DataGridViewAutoSizeColumnCriteriaInternal)inheritedAutoSizeMode, true /*fixedHeight*/); + } + } + } + } + + /// + protected virtual void OnColumnDividerWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + OnColumnGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDIVIDERWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnFillWeightChanged(DataGridViewColumn dataGridViewColumn) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight properties need to be re-evaluated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + // Adjust filling columns based on new weight of this column + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + } + + internal void OnColumnFillWeightChanging(DataGridViewColumn dataGridViewColumn, float fillWeight) + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None) - dataGridViewColumn.FillWeight + fillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + } + + private void OnColumnGlobalAutoSize(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (!this.Columns[columnIndex].Visible) + { + return; + } + + InvalidateColumnInternal(columnIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + if (autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill) + { + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight); + } + + // Autosize rows and column headers if needed + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + if (!fixedHeight && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill) + { + // Second round of column autosizing with 1 degree of freedom + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + } + + /// + protected virtual void OnColumnHeaderCellChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + OnColumnHeaderGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERCELLCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeaderGlobalAutoSize(int columnIndex) + { + if (!this.ColumnHeadersVisible) + { + return; + } + + InvalidateCellPrivate(columnIndex, -1); + + if (this.noAutoSizeCount > 0) + { + return; + } + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0; + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + + if (!fixedColumnWidth) + { + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill); + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing with 1 degree of freedom + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing with 1 degree of freedom + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + /// + protected virtual void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e) + { + if (e.Button == MouseButtons.Left && + this.SelectionMode != DataGridViewSelectionMode.FullColumnSelect && + this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect) + { + DataGridViewColumn dataGridViewColumn = this.Columns[e.ColumnIndex]; + + if (CanSort(dataGridViewColumn)) + { + ListSortDirection direction = ListSortDirection.Ascending; + + if (this.sortedColumn == dataGridViewColumn) + { + Debug.Assert(this.sortOrder != SortOrder.None); + if (this.sortOrder == SortOrder.Ascending) + { + direction = ListSortDirection.Descending; + } + } + + if ((this.DataSource == null) || + (this.DataSource != null && + (this.dataConnection.List is IBindingList) && + ((IBindingList) this.dataConnection.List).SupportsSorting && + dataGridViewColumn.IsDataBound)) + { + Sort(dataGridViewColumn, direction); + } + } + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.ColumnHeader); + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + break; + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + bool select = true; + if (isControlDown && this.Columns[hti.col].Selected) + { + select = false; + } + if (select) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex > -1 && hti.col != this.ptCurrentCell.X) + { + // Make sure we will be able to scroll into view + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + rowIndex != this.ptCurrentCell.Y /*fireRowLeave*/, + rowIndex != this.ptCurrentCell.Y /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + true /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + return; + } + if (rowIndex != oldCurrentCellY && oldCurrentCellY != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + return; + } + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + if (IsColumnOutOfBounds(hti.col)) + { + return; + } + + bool selectColumnRange = false; + this.trackColumn = hti.col; + this.trackColumnEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.X > -1 && + this.Columns[this.ptAnchorCell.X].Selected) + { + selectColumnRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.col) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + RemoveIndividuallySelectedCells(); + } + else + { + Debug.Assert(this.individualSelectedCells.Count == 0); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = true; + } + if (selectColumnRange) + { + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, hti.col)) + { + SelectColumnRange(this.ptAnchorCell.X, hti.col, true); + } + else + { + SelectColumnRange(hti.col, this.ptAnchorCell.X, true); + } + } + else if (!this.selectedBandIndexes.Contains(hti.col)) + { + SetSelectedColumnCore(hti.col, true); + } + // set current cell to the top most visible cell in the column + if (rowIndex != -1) + { + if (hti.col != this.ptCurrentCell.X) + { + if (IsInnerCellOutOfBounds(hti.col, rowIndex)) + { + return; + } + bool success = ScrollIntoView(hti.col, rowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(hti.col, rowIndex)) + { + return; + } + success = SetCurrentCellAddressCore(hti.col, rowIndex, !isShiftDown, false, true); + Debug.Assert(success); + } + else if (-1 != this.ptCurrentCell.X) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false /*throughMouseClick*/); + Debug.Assert(success); + } + } + else + { + Debug.Assert(this.CurrentCellAddress == new Point(-1, -1)); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.col)); + SetSelectedColumnCore(hti.col, false); + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnColumnHeadersBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeadersDefaultCellStyleChanged(EventArgs e) + { + if (this.ColumnHeadersVisible) + { + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce == null || dgvcsce.ChangeAffectsPreferredSize) + { + OnColumnHeadersGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeadersGlobalAutoSize() + { + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, false /*fixedColumnsWidth*/); + } + + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, false /*fixedRowsHeight*/); + } + + // Autosize columns + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, false /*fixedHeight*/); + + if (!fixedRowHeadersWidth || columnAutoSized) + { + // Autosize rows + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (!fixedColumnHeadersHeight) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + + if (!fixedRowHeadersWidth) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + // Second round of columns autosizing + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, true /*fixedHeight*/); + } + + /// + protected virtual void OnColumnHeadersHeightChanged(EventArgs e) + { + if (this.editingControl != null) + { + PositionEditingControl(true, false, false); + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + OnColumnHeadersGlobalAutoSize(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeadersHeightSizeModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + if (!e.PreviousModeAutoSized) + { + // Save current column headers height for later reuse + this.cachedColumnHeadersHeight = this.ColumnHeadersHeight; + } + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + else if (e.PreviousModeAutoSized) + { + this.ColumnHeadersHeight = this.cachedColumnHeadersHeight; + } + + DataGridViewAutoSizeModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTSIZEMODECHANGED] as DataGridViewAutoSizeModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnHidden(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn.Displayed) + { + dataGridViewColumn.DisplayedInternal = false; + DataGridViewColumnStateChangedEventArgs dgvrsce = new DataGridViewColumnStateChangedEventArgs(dataGridViewColumn, DataGridViewElementStates.Displayed); + OnColumnStateChanged(dgvrsce); + } + } + + internal void OnColumnMinimumWidthChanging(DataGridViewColumn dataGridViewColumn, int minimumWidth) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && dataGridViewColumn.Width < minimumWidth) + { + // Force the filled column's width to be minimumWidth + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + dataGridViewColumn.DesiredMinimumWidth = minimumWidth; + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + finally + { + dataGridViewColumn.DesiredMinimumWidth = 0; + } + Debug.Assert(dataGridViewColumn.Width == minimumWidth); + } + } + + /// + protected virtual void OnColumnMinimumWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + if (e.Column.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // Column's width may adjust smaller + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNMINIMUMWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnNameChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnNameChanged(dgvce); + } + + /// + protected virtual void OnColumnNameChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + // Check if the column name is used as is in the column header + DataGridViewColumn dataGridViewColumn = e.Column; + + if (dataGridViewColumn.HasHeaderCell && dataGridViewColumn.HeaderCell.Value is string && + String.Compare((string)dataGridViewColumn.HeaderCell.Value, dataGridViewColumn.Name, false, CultureInfo.InvariantCulture) == 0) + { + InvalidateCellPrivate(dataGridViewColumn.Index, -1); + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !this.ColumnHeadersVisible; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + if (!fixedColumnWidth) + { + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNNAMECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnRemoved(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == null); + OnColumnRemoved(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnRemoved(DataGridViewColumnEventArgs e) + { + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNREMOVED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(hti.col >= 0); + Debug.Assert(this.MultiSelect); + + if (this.ptCurrentCell.X != -1 && + hti.col != this.ptCurrentCell.X && + !CommitEditForOperation(hti.col, this.ptCurrentCell.Y, true)) + { + // Return silently if validating/commit/abort failed + return; + } + if (IsColumnOutOfBounds(hti.col)) + { + return; + } + + this.noSelectionChangeCount++; + try + { + if (this.trackColumnEdge >= 0 && (this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) || this.trackColumnEdge == this.trackColumn) && this.Columns.DisplayInOrder(this.trackColumnEdge, hti.col)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) && this.Columns.DisplayInOrder(hti.col, this.trackColumnEdge) && (this.Columns.DisplayInOrder(this.trackColumn, hti.col) || hti.col == this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[hti.col], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, this.trackColumnEdge, false); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge == -1 && this.Columns.DisplayInOrder(this.trackColumn, hti.col)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && (this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn) || this.trackColumnEdge == this.trackColumn) && this.Columns.DisplayInOrder(hti.col, this.trackColumnEdge)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn) && this.Columns.DisplayInOrder(this.trackColumnEdge, hti.col) && (this.Columns.DisplayInOrder(hti.col, this.trackColumn) || hti.col == this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(this.trackColumnEdge, dataGridViewColumn.Index, false); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge == -1 && this.Columns.DisplayInOrder(hti.col, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) && this.Columns.DisplayInOrder(hti.col, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, this.trackColumnEdge, false); + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, hti.col) && this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(this.trackColumnEdge, dataGridViewColumn.Index, false); + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X != -1 && hti.col != this.ptCurrentCell.X) + { + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(hti.col)) + { + return; + } + bool success = SetCurrentCellAddressCore(hti.col, + this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + + private void OnColumnsGlobalAutoSize() + { + InvalidateData(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + // Auto-size columms if needed + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, fixedHeight); + // Autosize rows if needed + if (!fixedHeight) + { + if (columnAutoSized) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + // Second round of columns autosizing + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, true /*fixedHeight*/); + } + } + + internal void OnColumnSortModeChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnSortModeChanged(dgvce); + } + + /// + protected virtual void OnColumnSortModeChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumn dataGridViewColumn = e.Column; + + if (dataGridViewColumn.HasHeaderCell) + { + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.NotSortable || + (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Programmatic && this.SortedColumn == dataGridViewColumn)) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder.None; + // This call will trigger OnSortGlyphDirectionChanged which in turn does + // this.sortedColumn = null; and InvalidateCellPrivate(e.Column.Index, -1); + } + // Potential resizing of the column headers and/or affected column. + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !this.ColumnHeadersVisible; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + if (!fixedColumnWidth) + { + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNSORTMODECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnStateChanged(DataGridViewColumnStateChangedEventArgs e) + { + // column.Frozen | .Visible changed - this may require a complete re-layout of the control + DataGridViewColumn dataGridViewColumn = e.Column; + switch (e.StateChanged) + { + // At this point we assume that only the Selected state has an influence on the rendering of the column. + // If there is a customer scenario where another state has an influence, the dev must invalidate the column by hand. + // case DataGridViewElementStates.ReadOnly: + // case DataGridViewElementStates.Resizable: + + case DataGridViewElementStates.Selected: + if (dataGridViewColumn.Visible && this.inBulkPaintCount == 0) + { + InvalidateColumnInternal(dataGridViewColumn.Index); + } + break; + + case DataGridViewElementStates.Frozen: + if (dataGridViewColumn.Visible) + { + if (dataGridViewColumn.Frozen) + { + // visible column became frozen + if (this.horizontalOffset >= dataGridViewColumn.Thickness) + { + Debug.Assert(this.Columns.DisplayInOrder(dataGridViewColumn.Index, this.displayedBandsInfo.FirstDisplayedScrollingCol)); + this.horizontalOffset -= dataGridViewColumn.Thickness; + } + else + { + this.horizontalOffset = this.negOffset = 0; + } + } + else + { + // column was unfrozen - make it the first visible scrolling column if there is room + this.horizontalOffset = this.negOffset = 0; + } + if (this.horizScrollBar.Enabled) + { + this.horizScrollBar.Value = this.horizontalOffset; + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + } + break; + + case DataGridViewElementStates.Visible: + if (!dataGridViewColumn.Visible && dataGridViewColumn.Displayed) + { + // Displayed column becomes invisible. Turns off the Displayed state. + dataGridViewColumn.DisplayedInternal = false; + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + + bool autoSizeRows = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && + this.RowHeadersVisible); + bool autoSizeColumn = false; + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + // Column autosizes + int width = dataGridViewColumn.ThicknessInternal; + if (dataGridViewColumn.Visible) + { + // Cache column's width before potential autosizing occurs + dataGridViewColumn.CachedThickness = width; + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, !autoSizeRows /*fixedHeight*/); + autoSizeColumn = true; + } + else if (width != dataGridViewColumn.CachedThickness) + { + // Columns that are made invisible in the collection take their non-autosized width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + } + + if (autoSizeRows) + { + if (dataGridViewColumn.Visible) + { + AdjustExpandingRows(dataGridViewColumn.Index, true /*fixedWidth*/); + } + else + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (autoSizeColumn) + { + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, true /*fixedHeight*/); + } + } + else + { + Invalidate(); + } + break; + } + + DataGridViewColumnStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNSTATECHANGED] as DataGridViewColumnStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + dataGridViewColumn.Index == this.ptCurrentCell.X && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if (!dataGridViewColumn.ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.ReadOnly) == 0 && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current column becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnColumnToolTipTextChanged(DataGridViewColumn dataGridViewColumn) + { + OnColumnToolTipTextChanged(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnToolTipTextChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNTOOLTIPTEXTCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + this.Columns.InvalidateCachedColumnsWidths(); + + // don't do any layout logic if the handle was not created already + if (e.Column.Visible && this.IsHandleCreated) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + Rectangle rightArea = this.layout.Data; + if (this.layout.ColumnHeadersVisible) + { + rightArea = Rectangle.Union(rightArea, this.layout.ColumnHeaders); + } + else if (this.SingleHorizontalBorderAdded) + { + rightArea.Y--; + rightArea.Height++; + } + if (rightArea.Width > 0 && rightArea.Height > 0) + { + int leftEdge = GetColumnXFromIndex(e.Column.Index); + if (this.RightToLeftInternal) + { + rightArea.Width -= rightArea.Right - leftEdge; + } + else + { + rightArea.Width -= leftEdge - rightArea.X; + rightArea.X = leftEdge; + } + if (rightArea.Width > 0 && rightArea.Height > 0) + { + Invalidate(rightArea); + } + } + + if (this.editingControl != null) + { + PositionEditingControl(this.ptCurrentCell.X != e.Column.Index, true, false); + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Columns); + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.InAdjustFillingColumns) + { + // Autosize rows and column headers if needed + OnColumnGlobalAutoSize(e.Column.Index); + } + } + + internal void OnCommonCellContentClick(int columnIndex, int rowIndex, bool doubleClick) + { + if (this.ptMouseDownCell.X == -2 || + (this.dataGridViewState2[DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds] && + this.ptMouseDownCell.X == columnIndex && this.ptMouseDownCell.Y == rowIndex && + (this.ptMouseDownCell.X == -1 || this.ptMouseDownCell.Y == -1 || + (columnIndex == this.ptCurrentCell.X && rowIndex == this.ptCurrentCell.Y)))) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + if (doubleClick) + { + OnCellContentDoubleClick(dgvce); + } + else + { + OnCellContentClick(dgvce); + } + } + } + + /// + protected virtual void OnCurrentCellChanged(EventArgs e) + { + VerifyImeRestrictedModeChanged(); + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCURRENTCELLCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCurrentCellDirtyStateChanged(EventArgs e) + { + if (this.RowHeadersVisible && this.ShowEditingIcon) + { + // Force the pencil to appear in the row header + Debug.Assert(this.ptCurrentCell.Y >= 0); + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + if (this.IsCurrentCellDirty && this.newRowIndex == this.ptCurrentCell.Y) + { + Debug.Assert(this.newRowIndex != -1); + Debug.Assert(this.AllowUserToAddRowsInternal); + // First time the 'new' row gets edited. + // It becomes a regular row and a new 'new' row is appened. + this.newRowIndex = -1; + AddNewRow(true /*createdByEditing*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCURRENTCELLDIRTYSTATECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnCursorChanged(EventArgs e) + { + base.OnCursorChanged(e); + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_ignoreCursorChange]) + { + this.oldCursor = this.Cursor; + } + } + + internal void OnDataBindingComplete(ListChangedType listChangedType) + { + OnDataBindingComplete(new DataGridViewBindingCompleteEventArgs(listChangedType)); + } + + /// + protected virtual void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e) + { + DataGridViewBindingCompleteEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATABINDINGCOMPLETE] as DataGridViewBindingCompleteEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions") // Not using options when RightToLeft == false + ] + protected virtual void OnDataError(bool displayErrorDialogIfNoHandler, DataGridViewDataErrorEventArgs e) + { + DataGridViewDataErrorEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATAERROR] as DataGridViewDataErrorEventHandler; + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + if (eh == null) + { + if (displayErrorDialogIfNoHandler) + { + string errorText; + if (e.Exception == null) + { + errorText = SR.GetString(SR.DataGridView_ErrorMessageText_NoException); + } + else + { + errorText = SR.GetString(SR.DataGridView_ErrorMessageText_WithException, e.Exception); + } + if (this.RightToLeftInternal) + { + MessageBox.Show(errorText, SR.GetString(SR.DataGridView_ErrorMessageCaption), MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading); + } + else + { + MessageBox.Show(errorText, SR.GetString(SR.DataGridView_ErrorMessageCaption), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + else + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + } + + internal void OnDataErrorInternal(DataGridViewDataErrorEventArgs e) + { + OnDataError(!this.DesignMode /*displayErrorDialogIfNoHandler*/, e); + } + + internal void OnDataGridViewElementStateChanged(DataGridViewElement element, int index, DataGridViewElementStates elementState) + { + DataGridViewColumn dataGridViewColumn = element as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnStateChangedEventArgs dgvcsce = new DataGridViewColumnStateChangedEventArgs(dataGridViewColumn, elementState); + + OnColumnStateChanged(dgvcsce); + } + else + { + DataGridViewRow dataGridViewRow = element as DataGridViewRow; + if (dataGridViewRow != null) + { + DataGridViewRowStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSTATECHANGED] as DataGridViewRowStateChangedEventHandler; + + if (eh != null && dataGridViewRow.DataGridView != null && dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[index]; + } + + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, elementState); + + OnRowStateChanged(dataGridViewRow.Index == -1 ? index : dataGridViewRow.Index, dgvrsce); + } + else + { + DataGridViewCell dataGridViewCell = element as DataGridViewCell; + if (dataGridViewCell != null) + { + DataGridViewCellStateChangedEventArgs dgvcsce = new DataGridViewCellStateChangedEventArgs(dataGridViewCell, elementState); + + OnCellStateChanged(dgvcsce); + } + } + } + + if ((elementState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected) + { + if (this.noSelectionChangeCount > 0) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = true; + } + else + { + OnSelectionChanged(EventArgs.Empty); + } + } + } + + internal void OnDataGridViewElementStateChanging(DataGridViewElement element, int index, DataGridViewElementStates elementState) + { + DataGridViewColumn dataGridViewColumn = element as DataGridViewColumn; + if (dataGridViewColumn != null) + { + // column.Frozen | .Visible | .ReadOnly changing + switch (elementState) + { + case DataGridViewElementStates.Frozen: + case DataGridViewElementStates.Visible: + if (elementState == DataGridViewElementStates.Visible) + { + if (!dataGridViewColumn.Visible && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader && + !this.ColumnHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMakeAutoSizedColumnVisible)); + } + else if (!dataGridViewColumn.Visible && + dataGridViewColumn.Frozen && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // alternative: throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMakeAutoFillColumnVisible)); + // DataGridView_CannotMakeAutoFillColumnVisible=The column cannot be made visible because its autosizing mode is Fill and it is frozen. + // Removing the Fill auto size mode when frozen column becomes visible (instead of throwing an exception) + dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + } + else if (dataGridViewColumn.Visible && this.ptCurrentCell.X == dataGridViewColumn.Index) + { + // Column of the current cell is made invisible. Trying to reset the current cell. May throw an exception. + ResetCurrentCell(); + // Microsoft: Should the current cell be set to some cell after the operation? + } + } + if (elementState == DataGridViewElementStates.Frozen && + !dataGridViewColumn.Frozen && + dataGridViewColumn.Visible && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // Removing the Fill auto size mode when visible column becomes frozen (instead of throwing an exception) + dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + } + CorrectColumnFrozenStates(dataGridViewColumn, elementState == DataGridViewElementStates.Frozen); + break; + + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.X == dataGridViewColumn.Index && + this.IsCurrentCellInEditMode && + !dataGridViewColumn.ReadOnly && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + Debug.Assert(!this.ReadOnly); + // Column becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + DataGridViewRow dataGridViewRow = element as DataGridViewRow; + if (dataGridViewRow != null) + { + // row.Frozen | .Visible | .ReadOnly changing + int rowIndex = ((dataGridViewRow.Index > -1) ? dataGridViewRow.Index : index); + switch (elementState) + { + case DataGridViewElementStates.Frozen: + case DataGridViewElementStates.Visible: + if (elementState == DataGridViewElementStates.Visible && this.ptCurrentCell.Y == rowIndex) + { + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + // Row of the current cell is made invisible. + if (this.DataSource != null) + { + Debug.Assert(this.dataConnection != null); + Debug.Assert(this.dataConnection.CurrencyManager != null); + Debug.Assert(this.dataConnection.CurrencyManager.Position == this.ptCurrentCell.Y); + // the row associated with the currency manager's position cannot be made invisble. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrencyManagerRowCannotBeInvisible)); + } + // Trying to reset the current cell. May throw an exception. + ResetCurrentCell(); + // Microsoft: Should the current cell be set to some cell after the operation? + } + CorrectRowFrozenStates(dataGridViewRow, rowIndex, elementState == DataGridViewElementStates.Frozen); + break; + + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.Y == rowIndex && + (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly && + this.IsCurrentCellInEditMode && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + // Row becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + DataGridViewCell dataGridViewCell = element as DataGridViewCell; + if (dataGridViewCell != null) + { + // cell.ReadOnly changing + switch (elementState) + { + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.X == dataGridViewCell.ColumnIndex && + this.ptCurrentCell.Y == dataGridViewCell.RowIndex && + this.IsCurrentCellInEditMode && + !dataGridViewCell.ReadOnly && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + Debug.Assert(!this.Columns[dataGridViewCell.ColumnIndex].ReadOnly); + Debug.Assert(!this.Rows[dataGridViewCell.RowIndex].ReadOnly); + Debug.Assert(!this.ReadOnly); + // Current cell becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + Debug.Fail("Unexpected DataGridViewElement type in DataGridView.OnDataGridViewElementStateChanging"); + } + } + } + } + + /// + protected virtual void OnDataMemberChanged(EventArgs e) + { + RefreshColumnsAndRows(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATAMEMBERCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (this.dataConnection != null && this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + + /// + protected virtual void OnDataSourceChanged(EventArgs e) + { + RefreshColumnsAndRows(); + InvalidateRowHeights(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATASOURCECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (this.dataConnection != null && this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + + /// + protected virtual void OnDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + Invalidate(); + } + else + { + OnGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnDefaultValuesNeeded(DataGridViewRowEventArgs e) + { + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDEFAULTVALUESNEEDED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnDoubleClick(EventArgs e) + { + base.OnDoubleClick(e); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + MouseEventArgs me = e as MouseEventArgs; + if (me != null) + { + HitTestInfo hti = HitTest(me.X, me.Y); + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar && + me.Button == MouseButtons.Left) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(hti.col, hti.row)); + } + } + } + } + + /// + protected virtual void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e) + { + DataGridViewEditingControlShowingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWEDITINGCONTROLSHOWING] as DataGridViewEditingControlShowingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnEditModeChanged(EventArgs e) + { + if (this.Focused && + this.EditMode == DataGridViewEditMode.EditOnEnter && + this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode) + { + // New edit mode is EditOnEnter and there is an editable current cell, try to go to edit mode. + BeginEditInternal(true /*selectAll*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWEDITMODECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + if (GetAnyDisposingInHierarchy()) + { + return; + } + + if (this.IsHandleCreated && this.Enabled) + { + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + + this.vertScrollBar.Enabled = true; + } + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + this.horizScrollBar.Enabled = true; + } + } + } + + /// + protected override void OnEnter(EventArgs e) + { + if (this.editingControl != null && this.editingControl.ContainsFocus) + { + return; + } + + base.OnEnter(e); + + // vsw 453314: there are cases when a control is on the designer and it still receives the OnEnter event. + // Guard against this. + if (this.DesignMode) + { + return; + } + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] = false; + + if (this.ptCurrentCell.X > -1) + { + DataGridViewCell dataGridViewCell = null; + // We're re-entering a row we already entered earlier. The first time we entered the row, + // the DataGridView didn't have focus. This time it does. We don't want to recreate the new row a second time. + OnRowEnter(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*canCreateNewRow*/, false /*validationFailureOccurred*/); + if (this.ptCurrentCell.X == -1) + { + return; + } + OnCellEnter(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + + // Force repainting of the current collumn's header cell to highlight it + if (this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && AccessibilityImprovements.Level2) + { + InvalidateCellPrivate(this.ptCurrentCell.X, -1); + } + } + else if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // Focus is given to the DataGridView control via a the TAB key. + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + + if (this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode) + { + if (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)) + { + BeginEditInternal(true /*selectAll*/); + if (this.ptCurrentCell.X > -1 && this.CurrentCellInternal.EditType == null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // The current cell does not have an edit type so the data grid view did not put an edit control on top. + // We should invalidate the current cell so that the dataGridView repaints the focus around the current cell. + // But do that only if the dataGridView did not get the focus via mouse. + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + else if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // When the focus is given to the DataGridView control via mouse + // the dataGridView changes selection so it invalidates the current cell anyway + // + // In any other case Invalidate the current cell so the dataGridView repaints the focus around the current cell + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + + // Draw focus rectangle around the grid + if (this.IsGridFocusRectangleEnabled()) + { + this.InvalidateRectangleEdges(this.GetGridFocusRectangle()); + } + } + + /// + protected override void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + + if (GetAnyDisposingInHierarchy ()) + { + return; + } + + // Change may be due to an ambient font change. + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] && + this.ColumnHeadersDefaultCellStyle.Font != base.Font) + { + this.ColumnHeadersDefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnColumnHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] && + this.RowHeadersDefaultCellStyle.Font != base.Font) + { + this.RowHeadersDefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnRowHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] && + this.DefaultCellStyle.Font != base.Font) + { + this.DefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + + /// + protected override void OnForeColorChanged(EventArgs e) + { + base.OnForeColorChanged(e); + if (GetAnyDisposingInHierarchy()) + { + return; + } + + // Change may be due to an ambient forecolor change. + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] && this.DefaultCellStyle.ForeColor != base.ForeColor) + { + this.DefaultCellStyle.ForeColor = base.ForeColor; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = false; + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + + private void OnGlobalAutoSize() + { + Invalidate(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool autoSizeRowHeaders = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + if (autoSizeRowHeaders) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, + this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize /*fixedColumnHeadersHeight*/, + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None /*fixedRowsHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, false /*fixedColumnsWidth*/); + } + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, true /*fixedHeight*/); + + if (autoSizeRowHeaders && + (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize || this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None)) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None) + { + // Second round of rows autosizing + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + + /// + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + if (this.ptCurrentCell.X != -1) + { + InvalidateCell(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + // Inform Accessibility that our current cell contains the focus. + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] && + (!this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] || this.EditMode != DataGridViewEditMode.EditOnEnter) && + (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] || this.EditMode != DataGridViewEditMode.EditOnEnter) && + this.ptCurrentCell.X > -1) + { + // The name is misleading ( the current cell did not change ). + // However, AccessibilityNotifyCurrentCellChanged is now a public method so we can't change its name + // to better reflect its purpose. + AccessibilityNotifyCurrentCellChanged(this.ptCurrentCell); + } + } + + /// + protected virtual void OnGridColorChanged(EventArgs e) + { + InvalidateInside(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWGRIDCOLORCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + + if (this.layout.dirty) + { + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + if (this.ptCurrentCell.X == -1) + { + MakeFirstDisplayedCellCurrentCell(false /*includeNewRow*/); + } + else + { + ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*forCurrentCellChange*/); + } + + // do the AutoSize work that was skipped during initialization + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = false; + OnGlobalAutoSize(); + } + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + } + + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + internal void OnInsertedColumn_PreNotification(DataGridViewColumn dataGridViewColumn) + { + // Fix the OldFirstDisplayedScrollingCol + this.displayedBandsInfo.CorrectColumnIndexAfterInsertion(dataGridViewColumn.Index, 1); + + // Fix the Index of all following columns + CorrectColumnIndexesAfterInsertion(dataGridViewColumn, 1); + + // Same effect as appending a column + OnAddedColumn(dataGridViewColumn); + } + + internal void OnInsertedColumn_PostNotification(Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 1 /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertedRow_PreNotification(int rowIndex, int insertionCount) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(insertionCount > 0); + + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, insertionCount); + + // Fix the Index of all following rows + CorrectRowIndexesAfterInsertion(rowIndex, insertionCount); + + // Next, same effect as adding a row + OnAddedRow_PreNotification(rowIndex); + } + + internal void OnInsertedRow_PostNotification(int rowIndex, Point newCurrentCell, bool lastInsertion) + { + Debug.Assert(rowIndex >= 0); + + // Same effect as adding a row + OnAddedRow_PostNotification(rowIndex); + + // Update current cell if needed + if (lastInsertion && newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 1 /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertedRows_PreNotification(int rowIndex, DataGridViewRow[] dataGridViewRows) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(dataGridViewRows != null); + Debug.Assert(dataGridViewRows.Length > 0); + + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, dataGridViewRows.Length); + + // Fix the Index of all following rows + CorrectRowIndexesAfterInsertion(rowIndex, dataGridViewRows.Length); + + // Next, same effect as adding the rows + OnAddedRows_PreNotification(dataGridViewRows); + } + + internal void OnInsertedRows_PostNotification(DataGridViewRow[] dataGridViewRows, Point newCurrentCell) + { + Debug.Assert(dataGridViewRows != null); + Debug.Assert(dataGridViewRows.Length > 0); + + // Same effect as adding the rows + OnAddedRows_PostNotification(dataGridViewRows); + + // Update current cell if needed + if (newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, newCurrentCell.Y, true, false, false, false /*clearSelection*/, false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertingColumn(int columnIndexInserted, DataGridViewColumn dataGridViewColumn, out Point newCurrentCell) + { + Debug.Assert(dataGridViewColumn != null); + + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + } + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectColumnFrozenState(dataGridViewColumn, columnIndexInserted); + + // Reset current cell if there is one, no matter the relative position of the columns involved + if (this.ptCurrentCell.X != -1) + { + newCurrentCell = new Point(columnIndexInserted <= this.ptCurrentCell.X ? this.ptCurrentCell.X + 1 : this.ptCurrentCell.X, + this.ptCurrentCell.Y); + ResetCurrentCell(); + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // prepare the existing rows by inserting cells of correct type + if (this.Rows.Count > 0) + { + // Only require a default cell type when there are rows to fill + if (dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + } + + int newColumnCount = this.Columns.Count + 1; + + try + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < newColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + dataGridViewRow.Cells.InsertInternal(columnIndexInserted, dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue; + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + catch + { + // An error occurred while inserting the cells. Revert all the insertions. + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count == newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(columnIndexInserted); + } + else + { + Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount); + break; + } + } + throw; + } + } + + // Update the indexes of selected columns to compensate for the insertion of this column + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + int columnEntries = this.selectedBandIndexes.Count; + int columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndex = this.selectedBandIndexes[columnEntry]; + if (columnIndexInserted <= columnIndex) + { + this.selectedBandIndexes[columnEntry] = columnIndex + 1; + } + columnEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + columnEntries = this.selectedBandSnapshotIndexes.Count; + columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndex = this.selectedBandSnapshotIndexes[columnEntry]; + if (columnIndexInserted <= columnIndex) + { + this.selectedBandSnapshotIndexes[columnEntry] = columnIndex + 1; + } + columnEntry++; + } + } + break; + } + } + + internal void OnInsertingRow(int rowIndexInserted, + DataGridViewRow dataGridViewRow, + DataGridViewElementStates rowState, + ref Point newCurrentCell, + bool firstInsertion, + int insertionCount, + bool force) + { + // Reset the current cell's address if it's after the inserted row. + if (firstInsertion) + { + if (this.ptCurrentCell.Y != -1 && rowIndexInserted <= this.ptCurrentCell.Y) + { + newCurrentCell = new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y + insertionCount); + if (force) + { + // When force is true, the underlying data was already added, therefore we need to avoid accessing any back-end data + // since we might be off by 1 row. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + ResetCurrentCell(); + } + } + else + { + newCurrentCell = new Point(-1, -1); + } + } + else + { + if (newCurrentCell.Y != -1) + { + newCurrentCell.Y += insertionCount; + } + } + + // For now same checks as for OnAddingRow + OnAddingRow(dataGridViewRow, rowState, false /*checkFrozenState*/); + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectRowFrozenState(dataGridViewRow, rowState, rowIndexInserted); + + // Update the indexes of selected rows to compensate for the insertion of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex + insertionCount; + } + rowEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex + insertionCount; + } + rowEntry++; + } + } + break; + } + } + + internal void OnInsertingRows(int rowIndexInserted, DataGridViewRow[] dataGridViewRows, ref Point newCurrentCell) + { + Debug.Assert(dataGridViewRows != null); + + // Reset the current cell's address if it's after the inserted row. + if (this.ptCurrentCell.Y != -1 && rowIndexInserted <= this.ptCurrentCell.Y) + { + newCurrentCell = new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y + dataGridViewRows.Length); + ResetCurrentCell(); + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // For now almost same checks as for OnAddingRows + // OnAddingRows checks for Selected status of rows. + OnAddingRows(dataGridViewRows, false /*checkFrozenStates*/); + + // Check for Frozen state correctness + CorrectRowFrozenStates(dataGridViewRows, rowIndexInserted); + + // Update the indexes of selected rows to compensate for the insertion of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex + dataGridViewRows.Length; + } + rowEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex + dataGridViewRows.Length; + } + rowEntry++; + } + } + break; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if (e.Handled) + { + return; + } + + // Forward key down to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyDownUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyDownInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyDownInternal(e, this.ptCurrentCell.Y); + } + } + + if (!e.Handled) + { + switch (e.KeyData & Keys.KeyCode) + { + case Keys.A: + case Keys.C: + case Keys.D0: + case Keys.NumPad0: + case Keys.Delete: + case Keys.Down: + case Keys.F2: + case Keys.F3: + case Keys.End: + case Keys.Enter: + case Keys.Escape: + case Keys.Home: + case Keys.Insert: + case Keys.Left: + case Keys.Next: + case Keys.Prior: + case Keys.Right: + case Keys.Space: + case Keys.Tab: + case Keys.Up: + { + e.Handled = ProcessDataGridViewKey(e); + break; + } + } + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + if (e.Handled) + { + return; + } + + // Forward key press to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyPressUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyPressInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyPressInternal(e, this.ptCurrentCell.Y); + } + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if (e.Handled) + { + return; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] && (e.KeyData & Keys.Alt) != Keys.Alt && AccessibilityImprovements.Level2) + { + this.EndColumnResize(this.currentColSplitBar); + this.ResetKeyboardTrackingState(); + return; + } + + // Forward key up to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyUpUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyUpInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyUpInternal(e, this.ptCurrentCell.Y); + } + } + } + + /// + protected override void OnLayout(LayoutEventArgs e) + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging]) + { + return; + } + + base.OnLayout(e); + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + if (this.RightToLeftInternal) + { + Invalidate(); + } + if (this.editingControl != null) + { + PositionEditingControl(true, true, false); + } + } + + /// + protected override void OnLeave(EventArgs e) + { + if (this.ptCurrentCell.X > -1 && !this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + DataGridViewCell dataGridViewCell = null; + OnCellLeave(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (this.ptCurrentCell.X == -1) + { + return; + } + OnRowLeave(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + base.OnLeave(e); + + // invalidate the current cell so the data grid view does not paint the focus rectangle any longer + if (this.ptCurrentCell.X > -1 && this.ptCurrentCell.Y > -1) + { + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + + // Erase focus rectangle around the grid + if (this.IsGridFocusRectangleEnabled()) + { + this.InvalidateRectangleEdges(this.GetGridFocusRectangle()); + } + } + + /// + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + if (this.ptCurrentCell.X != -1) + { + InvalidateCell(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + + /// + protected override void OnMouseClick(MouseEventArgs e) + { + bool mouseClickRaised = false; + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + HitTestInfo hti = HitTest(e.X, e.Y); + + if (this.ptMouseDownCell.X == hti.col && + this.ptMouseDownCell.Y == hti.row && + (e.Button != MouseButtons.Left || + this.ptMouseDownCell.X == -1 || + this.ptMouseDownCell.Y == -1 || + (this.ptMouseDownCell.X == this.ptCurrentCell.X && + this.ptMouseDownCell.Y == this.ptCurrentCell.Y))) + { + DataGridViewCellMouseEventArgs dgvcme = null; + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + RecordCellMouseClick(dgvcme); + if (e.Button == MouseButtons.Left) + { + OnCellClick(new DataGridViewCellEventArgs(hti.col, hti.row)); + } + base.OnMouseClick(e); + mouseClickRaised = true; + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseClick(dgvcme); + } + } + else + { + base.OnMouseClick(e); + mouseClickRaised = true; + } + + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + switch (hti.typeInternal) + { + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnColumnHeaderMouseClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.RowHeader: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnRowHeaderMouseClick(dgvcme); + } + break; + } + } + } + } + } + if (!mouseClickRaised) + { + base.OnMouseClick(e); + } + } + + /// + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + HitTestInfo hti = HitTest(e.X, e.Y); + + if (this.ptMouseDownCell.X == hti.col && this.ptMouseDownCell.Y == hti.row) + { + DataGridViewCellMouseEventArgs dgvcme = null; + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + OnCellMouseDoubleClick(dgvcme); + } + + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + switch (hti.typeInternal) + { + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnColumnHeaderMouseDoubleClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + { + int columnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti.col : hti.adjacentCol; + if (columnIndex < this.Columns.Count) + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new DataGridViewColumnDividerDoubleClickEventArgs(columnIndex, hme); + Debug.Assert(this.Columns[columnIndex].Resizable == DataGridViewTriState.True); + OnColumnDividerDoubleClick(dgvcddce); + } + break; + } + + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new DataGridViewRowDividerDoubleClickEventArgs(-1, hme); + Debug.Assert(this.columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing); + OnRowDividerDoubleClick(dgvrddce); + break; + } + + case DataGridViewHitTestTypeInternal.RowHeader: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnRowHeaderMouseDoubleClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + { + int rowIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti.row : hti.adjacentRow; + if (rowIndex < this.Rows.Count) + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new DataGridViewRowDividerDoubleClickEventArgs(rowIndex, hme); + Debug.Assert(this.Rows[rowIndex].Resizable == DataGridViewTriState.True); + OnRowDividerDoubleClick(dgvrddce); + } + break; + } + + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new DataGridViewColumnDividerDoubleClickEventArgs(-1, hme); + Debug.Assert(this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing); + OnColumnDividerDoubleClick(dgvcddce); + break; + } + } + } + } + } + } + + /// + protected override void OnMouseDown(MouseEventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves] = true; + } + + base.OnMouseDown(e); + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + return; + } + + if (this.ptMouseDownCell.X != -2) + { + // Happens when user pushes the mouse wheel down while the left mouse button is already down + Debug.Assert(this.ptMouseDownCell.Y != -2); + return; + } + + HitTestInfo hti = HitTest(e.X, e.Y); + + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + this.ptMouseDownCell.X = hti.col; + this.ptMouseDownCell.Y = hti.row; + this.ptMouseDownGridCoord = new Point(e.X, e.Y); + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + OnCellMouseDown(dgvcme); + } + } + + /// + protected override void OnMouseEnter(EventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] && + !this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] && + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] && + !this.toolTipControl.Activated) + { + base.OnMouseEnter(e); + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] = false; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] = false; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] = false; + } + + /// + protected override void OnMouseLeave(EventArgs e) + { + // when the mouse leaves the dataGridView control, reset the cursor to the previously cached one + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + + bool mouseOverEditingControl = this.MouseOverEditingControl; + bool mouseOverEditingPanel = this.MouseOverEditingPanel; + bool mouseOverToolTipControl = this.toolTipControl.Activated && this.ClientRectangle.Contains(PointToClient(Control.MousePosition)); + + if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && this.ptMouseEnteredCell.X != -2) + { + if (this.ptMouseEnteredCell.X >= -1 && this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && this.ptMouseEnteredCell.Y < this.Rows.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + else + { + this.ptMouseEnteredCell.X = this.ptMouseEnteredCell.Y = -2; + } + } + + ResetTrackingState(); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves] = false; + + if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && !this.MouseOverScrollBar) + { + this.toolTipControl.Activate(false /*activate*/); + base.OnMouseLeave(e); + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] = true; + } + } + + /// + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + HitTestInfo hti = HitTest(e.X, e.Y); + + UpdateMouseEnteredCell(hti, e); + + // We need to give UI feedback when the user is resizing a column + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize]) + { + MoveRowHeadersOrColumnResize(e.X); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]) + { + MoveColumnHeadersOrRowResize(e); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + MoveColumnRelocation(e, hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize]) + { + MoveColumnHeadersOrRowResize(e); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + MoveRowHeadersOrColumnResize(e.X); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] || + ((hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeRight) && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect])) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.SizeWE; + return; + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] || + ((hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom) && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect])) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.SizeNS; + return; + } + /* Whidbey bug 156884 - no longer show hand cursor + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight)) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.Hand; + return; + }*/ + else if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + int xOffset, yOffset, mouseX = e.X, mouseY = e.Y; + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (xOffset == 0) + { + if (this.horizScrollTimer != null && this.horizScrollTimer.Enabled) + { + // Mouse's X came in-bound - need to stop the horizontal scroll timer + this.horizScrollTimer.Enabled = false; + } + } + else if (this.horizScrollTimer == null || !this.horizScrollTimer.Enabled) + { + // Need to start delayed horizontal scroll + this.HorizScrollTimer.Interval = GetColumnScrollRate(Math.Abs(xOffset)); + this.HorizScrollTimer.Enabled = true; + } + + if (yOffset == 0) + { + if (this.vertScrollTimer != null && this.vertScrollTimer.Enabled) + { + // Mouse's Y came in-bound - need to stop the vertical scroll timer + this.vertScrollTimer.Enabled = false; + } + } + else if (this.vertScrollTimer == null || !this.vertScrollTimer.Enabled) + { + // Need to start delayed vertical scroll + this.VertScrollTimer.Interval = GetRowScrollRate(Math.Abs(yOffset)); + this.VertScrollTimer.Enabled = true; + } + + if (this.HorizScrollTimer.Enabled || this.VertScrollTimer.Enabled) + { + return; + } + + if (/*!this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] && */ + hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + } +#if DEBUG + else + { + Debug.Assert(this.vertScrollTimer == null || !this.vertScrollTimer.Enabled); + Debug.Assert(this.horizScrollTimer == null || !this.horizScrollTimer.Enabled); + } +#endif + if (!this.toolTipControl.Activated) + { + // + // After processing the MouseMove event, the tool tip is still not activated. + // Reset the tool tip cell. + this.ptToolTipCell = new Point(-1, -1); + } + } + + /// + protected override void OnMouseUp(MouseEventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = false; + + HitTestInfo hti = HitTest(e.X, e.Y); + + if (!this.IsMouseOperationActive()) + { + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme; + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble]) + { + MouseEventArgs meTmp = new MouseEventArgs(e.Button, 2, e.X, e.Y, e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, meTmp); + } + else + { + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + } + if (hti.col >= 0 && this.ptMouseDownCell.X == hti.col && + hti.row >= 0 && this.ptMouseDownCell.Y == hti.row && + this.EditMode == DataGridViewEditMode.EditOnEnter && + this.editingControl != null) + { + OnClick(e); + OnMouseClick(e); + } + + CorrectFocus(true /*onlyIfGridHasFocus*/); + + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseUp(dgvcme); + } + } + else if (hti.Type == DataGridViewHitTestType.None) + { + // VS Whidbey bug 469429 + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize]) + { + EndColumnResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]) + { + EndRowResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + EndColumnRelocation(e, hti); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize]) + { + EndColumnHeadersResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + EndRowHeadersResize(e); + } + + // VS Whidbey bug 256893 + CorrectFocus(true /*onlyIfGridHasFocus*/); + + // Updating the hit test info since the EndXXX operation above may have invalidated the previously + // determined hti. + hti = HitTest(e.X, e.Y); + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + OnCellMouseUp(new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e)); + } + } + + ResetTrackingState(); + } + base.OnMouseUp(e); + } + + /// + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + HandledMouseEventArgs hme = e as HandledMouseEventArgs; + if (hme != null && hme.Handled) + { + // The application event handler handled the scrolling - don't do anything more. + return; + } + + if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) + { + return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down. + } + + bool verticalScroll = ((ModifierKeys & Keys.Control) == 0); + + ScrollBar sb = (verticalScroll ? (ScrollBar) this.vertScrollBar : (ScrollBar) this.horizScrollBar); + + if (!sb.Visible || !sb.Enabled) + { + return; // Do not scroll when the corresponding scrollbar is invisible or disabled + } + + if (hme != null) + { + hme.Handled = true; + } + + int wheelScrollLines = SystemInformation.MouseWheelScrollLines; + if (wheelScrollLines == 0) + { + return; // Do not scroll when the user system setting is 0 lines per notch + } + + Debug.Assert(this.cumulativeVerticalWheelDelta > -NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeVerticalWheelDelta < NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeHorizontalWheelDelta > -NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeHorizontalWheelDelta < NativeMethods.WHEEL_DELTA); + + float partialNotches; + + if (verticalScroll) + { + this.cumulativeVerticalWheelDelta += e.Delta; + partialNotches = (float) this.cumulativeVerticalWheelDelta / (float) NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeHorizontalWheelDelta += e.Delta; + partialNotches = (float) this.cumulativeHorizontalWheelDelta / (float) NativeMethods.WHEEL_DELTA; + } + + int fullNotches = (int) partialNotches; + + if (wheelScrollLines == -1) + { + // Equivalent to large change scrolls + if (fullNotches != 0) + { + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value + return; + } + if (verticalScroll) + { + int originalVerticalOffset = this.VerticalOffset; + this.VerticalOffset -= fullNotches * this.vertScrollBar.LargeChange; + if (Math.Abs(this.VerticalOffset - originalVerticalOffset) >= Math.Abs(fullNotches * this.vertScrollBar.LargeChange)) + { + this.cumulativeVerticalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeVerticalWheelDelta = 0; + } + } + else + { + int originalHorizontalOffset = this.HorizontalOffset; + this.HorizontalOffset -= fullNotches * this.horizScrollBar.LargeChange; + if (Math.Abs(this.HorizontalOffset - originalHorizontalOffset) >= Math.Abs(fullNotches * this.horizScrollBar.LargeChange)) + { + this.cumulativeHorizontalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeHorizontalWheelDelta = 0; + } + } + } + } + else + { + // Evaluate number of bands to scroll + int scrollBands = (int) ((float) wheelScrollLines * partialNotches); + if (scrollBands != 0) + { + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value + return; + } + int absScrollBands; + if (verticalScroll) + { + if (scrollBands > 0) + { + absScrollBands = scrollBands; + while (this.vertScrollBar.Value != this.vertScrollBar.Minimum && absScrollBands > 0) + { + ScrollRowsByCount(-1, ScrollEventType.SmallDecrement); + absScrollBands--; + } + if (this.vertScrollBar.Value == this.vertScrollBar.Minimum) + { + this.cumulativeVerticalWheelDelta = 0; + } + else + { + this.cumulativeVerticalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + else + { + absScrollBands = -scrollBands; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (absScrollBands > 0 && + this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + ScrollRowsByCount(1, ScrollEventType.SmallIncrement); + // Assuming totalVisibleFrozenHeight is unchanged by scrolling operation + Debug.Assert(totalVisibleFrozenHeight == this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + absScrollBands--; + } + if (this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) > + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + this.cumulativeVerticalWheelDelta = 0; + } + else + { + this.cumulativeVerticalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + } + else + { + int extremeScrollBarValue, scrollBand; + if (scrollBands > 0) + { + extremeScrollBarValue = this.horizScrollBar.Minimum; + scrollBand = -1; + } + else + { + extremeScrollBarValue = this.horizScrollBar.Maximum; + scrollBand = 1; + } + absScrollBands = Math.Abs(scrollBands); + while (this.horizScrollBar.Value != extremeScrollBarValue && absScrollBands > 0) + { + ScrollColumns(scrollBand); + absScrollBands--; + } + if (this.horizScrollBar.Value == extremeScrollBarValue) + { + this.cumulativeHorizontalWheelDelta = 0; + } + else + { + this.cumulativeHorizontalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + } + } + } + + internal void OnMouseWheelInternal(MouseEventArgs e) + { + // Notification forwarded from editing control + OnMouseWheel(e); + } + + /// + protected virtual void OnMultiSelectChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWMULTISELECTCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnNewRowNeeded(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWNEWROWNEEDED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnPaint(PaintEventArgs e) + { + try + { + // We can't paint if we are disposed. + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] || this.IsDisposed) + { + return; + } + + if (this.layout.dirty) + { + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + Graphics g = e.Graphics; + Rectangle clipRect = e.ClipRectangle; + Rectangle gridRect = this.GetGridRectangle(); + + if (this.currentRowSplitBar != -1) + { + clipRect = Rectangle.Union(clipRect, CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + else if (this.currentColSplitBar != -1) + { + clipRect = Rectangle.Union(clipRect, CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + if (clipRect.IntersectsWith(gridRect)) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(gridRect); + PaintBackground(g, clipRect, gridRect); + PaintGrid(g, gridRect, clipRect, this.SingleVerticalBorderAdded, this.SingleHorizontalBorderAdded); + g.Clip = clipRegion; + } + } + + PaintBorder(g, clipRect, this.layout.ClientRectangle); + if (clipRect.IntersectsWith(this.layout.ResizeBoxRect)) + { + g.FillRectangle(SystemBrushes.Control, this.layout.ResizeBoxRect); + } + + // Draw focus rectangle around the grid + if (this.Focused && this.IsGridFocusRectangleEnabled()) + { + if (SystemInformation.HighContrast) + { + ControlPaint.DrawHighContrastFocusRectangle(g, this.GetGridFocusRectangle(), SystemColors.ActiveCaptionText); + } + else + { + ControlPaint.DrawFocusRectangle(g, this.GetGridFocusRectangle()); + } + } + + base.OnPaint(e); // raise paint event + } + catch (Exception ex) + { + #if DEBUG + Debug.Fail("DataGridView.OnPaint exception: " + ex.Message + " stack trace " + ex.StackTrace); + #endif + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + } + + // Determines if a focus rectangle may be drawn along the perimeter of the DataGridView control + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsGridFocusRectangleEnabled() + { + return this.ShowFocusCues && this.CurrentCell == null && AccessibilityImprovements.Level2; + } + + // Creates a rectangle by merging row headers, column headers + // and cells rectangles (from layout data) + private Rectangle GetGridRectangle() + { + Rectangle gridRect = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + gridRect = Rectangle.Union(gridRect, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + gridRect.X--; + } + gridRect.Width++; + } + + if (this.layout.ColumnHeadersVisible) + { + gridRect = Rectangle.Union(gridRect, this.layout.ColumnHeaders); + } + else if (this.SingleHorizontalBorderAdded) + { + if (gridRect.Y == this.layout.Data.Y) + { + gridRect.Y--; + gridRect.Height++; + } + } + + return gridRect; + } + + // Creates a grid focus rectangle + private Rectangle GetGridFocusRectangle() + { + Rectangle focusRect = this.GetGridRectangle(); + focusRect.Inflate(1 - FOCUS_RECT_OFFSET, 1 - FOCUS_RECT_OFFSET); + return focusRect; + } + + private void InvalidateGridFocusOnScroll(int change, ScrollOrientation orientation) + { + if (change == 0) + { + return; + } + + Rectangle focusRect = GetGridFocusRectangle(); + + if (orientation == ScrollOrientation.HorizontalScroll) + { + // Scroll right + if (change > 0) + { + focusRect.Width -= change; + } + // Scroll left + else + { + focusRect.X -= change; + focusRect.Width += change; + } + } + else + { + // Scroll down + if (change > 0) + { + focusRect.Height -= change; + } + // Scroll up + else + { + focusRect.Y -= change; + focusRect.Height += change; + } + } + + this.InvalidateRectangleEdges(focusRect); + } + + private void InvalidateRectangleEdges(Rectangle rect) + { + // Left edge + Rectangle edge = rect; + edge.Width = 1; + this.Invalidate(edge); + + // Right edge + edge.X += rect.Width - 1; + this.Invalidate(edge); + + // Top edge + edge = rect; + edge.Height = 1; + this.Invalidate(edge); + + // Bottom edge + edge.Y += rect.Height - 1; + this.Invalidate(edge); + } + + // See VSWhidbey 527459 & 526373. + internal override void OnParentBecameInvisible() + { + base.OnParentBecameInvisible(); + if (GetState(STATE_VISIBLE)) + { + // This control became invisible too - Update the Displayed properties of the bands. + OnVisibleChangedPrivate(); + } + } + + /// + protected virtual void OnReadOnlyChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWREADONLYCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + VerifyImeRestrictedModeChanged(); + + if (!this.ReadOnly && + this.ptCurrentCell.X != -1 && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)) && + !this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X].ReadOnly) // Unshares the row + { + // Current cell becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + + internal void OnRemovedColumn_PreNotification(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.DataGridView == null); + + // Clear the potential header sort glyph + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.SortGlyphDirectionInternal = SortOrder.None; + } + // Intentionally keep the DisplayIndex intact after detaching the column. + CorrectColumnIndexesAfterDeletion(dataGridViewColumn); + + CorrectColumnDisplayIndexesAfterDeletion(dataGridViewColumn); + + // Fix the OldFirstDisplayedScrollingCol + this.displayedBandsInfo.CorrectRowIndexAfterDeletion(dataGridViewColumn.Index); + + // Raise the ColumnRemoved event + OnColumnRemoved(dataGridViewColumn); + } + + internal void OnRemovedColumn_PostNotification(DataGridViewColumn dataGridViewColumn, Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + + // Raise SelectionChanged event if needed + FlushSelectionChanged(); + + // Raise ColumnStateChanged event for Displayed state of deleted column + OnColumnHidden(dataGridViewColumn); + + // Columns that are removed from the collection take their non-autosized width + // Note that in some edge cases, the dev could have changed: + // - the grid's AutoSizeColumnsMode + // - the column's Width or AutoSizeMode + // in a ColumnRemoved event handler for example, in which case using the CachedThickness may be wrong. + // At least we make sure the column is not sized smaller than its minimum width. + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.GetInheritedAutoSizeMode(this); + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumn.ThicknessInternal != dataGridViewColumn.CachedThickness) + { + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + + // Autosize rows if needed + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + internal void OnRemovedRow_PreNotification(int rowIndexDeleted) + { + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterDeletion(rowIndexDeleted); + + CorrectRowIndexesAfterDeletion(rowIndexDeleted); + ComputeVisibleRows(); + } + + internal void OnRemovedRow_PostNotification(DataGridViewRow dataGridViewRow, Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + + // Raise SelectionChange event if needed + FlushSelectionChanged(); + + bool rowDisplayed = dataGridViewRow.DataGridView == null && dataGridViewRow.Displayed; + + // Raise RowStateChanged event for Displayed state of deleted row + if (rowDisplayed) + { + dataGridViewRow.DisplayedInternal = false; + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, DataGridViewElementStates.Displayed); + OnRowStateChanged(-1 /*rowIndex*/, dgvrsce); + } + + // Rows that are removed from the collection take their non-autosized height + // Note that in some edge cases, the dev could have changed: + // - the grid's AutoSizeRowsMode + // - the row's Height + // in a RowsRemoved event handler for example, in which case using the CachedThickness may be wrong. + // At least we make sure the row is not sized smaller than its minimum height. + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None && dataGridViewRow.ThicknessInternal != dataGridViewRow.CachedThickness) + { + dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness); + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + + if (fixedRowHeadersWidth && !columnAutoSized) + { + // No need to autosize the column headers when the row headers and columns don't change. + fixedColumnHeadersHeight = true; + } + + // Auto size column headers + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + if (!fixedColumnHeadersHeight && !fixedRowHeadersWidth) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + internal void OnRemovingColumn(DataGridViewColumn dataGridViewColumn, out Point newCurrentCell, bool force) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Index >= 0 && dataGridViewColumn.Index < this.Columns.Count); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + int columnIndex = dataGridViewColumn.Index; + + // Reset the current cell's address if there is one. + if (this.ptCurrentCell.X != -1) + { + int newX = this.ptCurrentCell.X; + if (columnIndex == this.ptCurrentCell.X) + { + DataGridViewColumn dataGridViewColumnNext = this.Columns.GetNextColumn( + this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnNext != null) + { + if (dataGridViewColumnNext.Index > columnIndex) + { + newX = dataGridViewColumnNext.Index - 1; + } + else + { + newX = dataGridViewColumnNext.Index; + } + } + else + { + DataGridViewColumn dataGridViewColumnPrevious = this.Columns.GetPreviousColumn( + this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnPrevious != null) + { + if (dataGridViewColumnPrevious.Index > columnIndex) + { + newX = dataGridViewColumnPrevious.Index - 1; + } + else + { + newX = dataGridViewColumnPrevious.Index; + } + } + else + { + newX = -1; + } + } + } + else if (columnIndex < this.ptCurrentCell.X) + { + newX = this.ptCurrentCell.X - 1; + } + newCurrentCell = new Point(newX, (newX == -1) ? -1 : this.ptCurrentCell.Y); + if (columnIndex == this.ptCurrentCell.X) + { + // Left cell is not validated since cancelling validation wouldn't have any effect anyways. + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else if (force) + { + // Underlying data of deleted column is gone. It cannot be accessed anymore. + // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + // Quit editing mode and set the current cell to its new location once everything is in sync again. + ResetCurrentCell(); + } + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // If the last column is removed, delete all the rows first. + if (this.Columns.Count == 1) + { + Debug.Assert(columnIndex == 0); + this.Rows.ClearInternal(false /*recreateNewRow*/); + } + + // Prepare the existing rows by removing cells at correct index + int newColumnCount = this.Columns.Count - 1; + + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count > newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(columnIndex); + } + } + + // Detach column header cell + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.DataGridViewInternal = null; + } + + // Reset sort related variables. + if (dataGridViewColumn == this.sortedColumn) + { + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + + if (dataGridViewColumn.IsDataBound) + { + // If the column being removed is the sorted column and it is also the dataBound column + // then see if there is another dataBound column which has the same property name as the sorted column. + // If so, then make that dataGridViewColumn the sorted column in the data grid view. + for (int i = 0; i < this.Columns.Count; i ++) + { + if (dataGridViewColumn != this.Columns[i] && + this.Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable && + String.Compare(dataGridViewColumn.DataPropertyName, + this.Columns[i].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + Debug.Assert(this.Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time"); + Debug.Assert(this.Columns[i].HeaderCell.SortGlyphDirection == dataGridViewColumn.HeaderCell.SortGlyphDirection, "DataBound columns should have the same SortGlyphDirection as the one on the DataGridView"); + this.sortedColumn = this.Columns[i]; + this.sortOrder = this.Columns[i].HeaderCell.SortGlyphDirection; + break; + } + } + } + } + + // Is deleted column scrolled off screen? + if (dataGridViewColumn.Visible && + !dataGridViewColumn.Frozen && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + // Deleted column is part of scrolling columns. + if (this.displayedBandsInfo.FirstDisplayedScrollingCol == dataGridViewColumn.Index) + { + // Deleted column is first scrolling column + this.horizontalOffset -= this.negOffset; + this.negOffset = 0; + } + else if (this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, dataGridViewColumn.Index)) + { + // Deleted column is displayed after first scrolling column + if (this.horizScrollBar.Enabled) + { + int newHorizontalOffset = this.horizScrollBar.Maximum - this.horizScrollBar.LargeChange - dataGridViewColumn.Thickness; + if (newHorizontalOffset >= 0 && newHorizontalOffset < this.horizScrollBar.Value) + { + this.horizontalOffset = newHorizontalOffset; + this.negOffset = GetNegOffsetFromHorizontalOffset(this.horizontalOffset); + } + } + else + { + this.horizontalOffset = this.negOffset = 0; + } + } + else + { + // Deleted column is displayed before first scrolling column + Debug.Assert(this.horizontalOffset >= dataGridViewColumn.Thickness); + this.horizontalOffset -= dataGridViewColumn.Thickness; + } + + if (this.horizScrollBar.Enabled) + { + this.horizScrollBar.Value = this.horizontalOffset; + } + } + + bool raiseSelectionChanged = false; + + // Update the indexes of selected columns or individual cells to compensate for the removal of this column + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + int columnEntries = this.selectedBandIndexes.Count; + int columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndexSelected = this.selectedBandIndexes[columnEntry]; + if (columnIndex == columnIndexSelected) + { + this.selectedBandIndexes.RemoveAt(columnEntry); + columnEntries--; + raiseSelectionChanged = true; + } + else + { + if (columnIndex < columnIndexSelected) + { + this.selectedBandIndexes[columnEntry] = columnIndexSelected - 1; + } + columnEntry++; + } + } + break; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] |= this.individualSelectedCells.RemoveAllCellsAtBand(true /*column*/, columnIndex) > 0 || + raiseSelectionChanged; + this.individualReadOnlyCells.RemoveAllCellsAtBand(true /*column*/, columnIndex); + } + + internal void OnRemovingRow(int rowIndexDeleted, out Point newCurrentCell, bool force) + { + // if force is true, the row needs to be deleted no matter what. The underlying data row was already deleted. + + Debug.Assert(rowIndexDeleted >= 0 && rowIndexDeleted < this.Rows.Count); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + newCurrentCell = new Point(-1, -1); + + // Reset the current cell's address if it's on the deleted row, or after it. + if (this.ptCurrentCell.Y != -1 && rowIndexDeleted <= this.ptCurrentCell.Y) + { + int newY; + if (rowIndexDeleted == this.ptCurrentCell.Y) + { + int rowIndexPrevious = this.Rows.GetPreviousRow(rowIndexDeleted, DataGridViewElementStates.Visible); + int rowIndexNext = this.Rows.GetNextRow(rowIndexDeleted, DataGridViewElementStates.Visible); + if (rowIndexPrevious > -1 && this.AllowUserToAddRowsInternal) + { + Debug.Assert(this.newRowIndex != -1); + Debug.Assert(this.newRowIndex == this.Rows.Count-1); + if (rowIndexNext > -1 && rowIndexNext < this.Rows.Count - 1) + { + newY = rowIndexNext - 1; + } + else + { + newY = rowIndexPrevious; + } + } + else + { + if (rowIndexNext > -1) + { + newY = rowIndexNext - 1; + } + else + { + newY = rowIndexPrevious; + } + } + // Since the current row is deleted, the dirty states need to be reset + this.IsCurrentCellDirtyInternal = false; + this.IsCurrentRowDirtyInternal = false; + } + else + { + Debug.Assert(rowIndexDeleted < this.ptCurrentCell.Y); + newY = this.ptCurrentCell.Y - 1; + } + newCurrentCell = new Point(this.ptCurrentCell.X, newY); + if (rowIndexDeleted == this.ptCurrentCell.Y) + { + // Left cell is not validated since cancelling validation wouldn't have any effect anyways. + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else if (force) + { + // Underlying data of deleted row is gone. It cannot be accessed anymore. + // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + // Quit editing mode and set the current cell to its new location once everything is in sync again. + ResetCurrentCell(); + } + } + + bool raiseSelectionChanged = false; + + // Update the indexes of selected rows to compensate for the removal of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexDeleted == rowIndex) + { + raiseSelectionChanged = true; + this.selectedBandIndexes.RemoveAt(rowEntry); + rowEntries--; + } + else + { + if (rowIndexDeleted < rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex - 1; + } + rowEntry++; + } + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexDeleted == rowIndex) + { + this.selectedBandSnapshotIndexes.RemoveAt(rowEntry); + rowEntries--; + } + else + { + if (rowIndexDeleted < rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex - 1; + } + rowEntry++; + } + } + } + break; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] |= this.individualSelectedCells.RemoveAllCellsAtBand(false /*column*/, rowIndexDeleted) > 0 || + raiseSelectionChanged; + this.individualReadOnlyCells.RemoveAllCellsAtBand(false /*column*/, rowIndexDeleted); + } + + internal void OnReplacedCell(DataGridViewRow dataGridViewRow, int columnIndex) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected]) + { + this.individualSelectedCells.Add(dataGridViewCell); + } + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly]) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + } + + // AutoSize column and row if needed + OnCellCommonChange(columnIndex, dataGridViewRow.Index); + + if (this.ptCurrentCellCache.X != -1) + { + if (!IsInnerCellOutOfBounds(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y)) + { + SetCurrentCellAddressCore(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y, false, false, false); + } + this.ptCurrentCellCache.X = -1; + this.ptCurrentCellCache.Y = -1; + } + } + + internal void OnReplacingCell(DataGridViewRow dataGridViewRow, int columnIndex) + { + if (this.ptCurrentCell.X == dataGridViewRow.Index && + this.ptCurrentCell.Y == columnIndex) + { + // Trying to replace the current cell. Exiting editing mode first (if needed). + // Remember to reset the current cell in OnReplacedCell notification + this.ptCurrentCellCache.X = this.ptCurrentCell.X; + this.ptCurrentCellCache.Y = this.ptCurrentCell.Y; + // This may fail and throw an exception + ResetCurrentCell(); + } + else + { + this.ptCurrentCellCache.X = -1; + this.ptCurrentCellCache.Y = -1; + } + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected] = this.individualSelectedCells.Contains(dataGridViewCell); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected]) + { + this.individualSelectedCells.Remove(dataGridViewCell); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly] = this.individualReadOnlyCells.Contains(dataGridViewCell); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly]) + { + this.individualReadOnlyCells.Remove(dataGridViewCell); + } + } + + /// + protected override void OnResize(EventArgs e) + { + int borderWidth = this.BorderWidth; + Rectangle right; + Rectangle bottom; + Rectangle oldClientRectangle = this.layout.ClientRectangle; + Rectangle oldGridFocusRectangle = this.GetGridFocusRectangle(); + + right = new Rectangle(oldClientRectangle.X + oldClientRectangle.Width - borderWidth, + oldClientRectangle.Y, + borderWidth, + oldClientRectangle.Height); + bottom = new Rectangle(oldClientRectangle.X, + oldClientRectangle.Y + oldClientRectangle.Height - borderWidth, + oldClientRectangle.Width, + borderWidth); + + if (!this.IsMinimized) + { + // When owning form is minimized, act as if it had a normal size + this.normalClientRectangle = this.ClientRectangle; + } + + Rectangle newClientRectangle = this.normalClientRectangle; + Rectangle newGridFocusRectangle = this.DisplayRectangle; + newGridFocusRectangle.Inflate(1 - borderWidth - FOCUS_RECT_OFFSET, 1 - borderWidth - FOCUS_RECT_OFFSET); + + if (newClientRectangle.Width != oldClientRectangle.Width) + { + Invalidate(right); + right = new Rectangle(newClientRectangle.X + newClientRectangle.Width - borderWidth, + newClientRectangle.Y, + borderWidth, + newClientRectangle.Height); + Invalidate(right); + } + if (newClientRectangle.Height != oldClientRectangle.Height) + { + Invalidate(bottom); + bottom = new Rectangle(newClientRectangle.X, + newClientRectangle.Y + newClientRectangle.Height - borderWidth, + newClientRectangle.Width, + borderWidth); + Invalidate(bottom); + } + + // Invalidate grid focus rectangle + if (this.Focused && this.IsGridFocusRectangleEnabled() && oldGridFocusRectangle != newGridFocusRectangle) + { + right = new Rectangle(oldGridFocusRectangle.X + oldGridFocusRectangle.Width - 1, + oldGridFocusRectangle.Y, + 1, + oldGridFocusRectangle.Height); + Invalidate(right); + + bottom = new Rectangle(oldGridFocusRectangle.X, + oldGridFocusRectangle.Y + oldGridFocusRectangle.Height - 1, + oldGridFocusRectangle.Width, + 1); + Invalidate(bottom); + + InvalidateRectangleEdges(newGridFocusRectangle); + } + + //also, invalidate the ResizeBoxRect + if (!this.layout.ResizeBoxRect.IsEmpty) + { + Invalidate(this.layout.ResizeBoxRect); + } + this.layout.ClientRectangle = newClientRectangle; + + int oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + base.OnResize(e); + if (oldfirstDisplayedScrollingRow != this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + Invalidate(); + } + } + + /// + protected override void OnRightToLeftChanged(EventArgs e) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftValid] = false; + base.OnRightToLeftChanged(e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + internal void OnRowCollectionChanged_PostNotification(bool recreateNewRow, + bool allowSettingCurrentCell, + CollectionChangeAction cca, + DataGridViewRow dataGridViewRow, + int rowIndex) + { + if (recreateNewRow && + cca == CollectionChangeAction.Refresh && + this.Columns.Count != 0 && + this.Rows.Count == 0 && + this.AllowUserToAddRowsInternal) + { + AddNewRow(false); + } + + if (cca == CollectionChangeAction.Refresh) + { + FlushSelectionChanged(); + } + + if ((cca == CollectionChangeAction.Refresh || cca == CollectionChangeAction.Add) && + this.ptCurrentCell.X == -1 && allowSettingCurrentCell && !this.InSortOperation) + { + MakeFirstDisplayedCellCurrentCell(false /*includeNewRow*/); + } + + if (this.AutoSize) + { + bool invalidatePreferredSizeCache = true; + switch (cca) + { + case CollectionChangeAction.Add: + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + invalidatePreferredSizeCache = ((rowState & DataGridViewElementStates.Visible) != 0); + break; + case CollectionChangeAction.Remove: + invalidatePreferredSizeCache = dataGridViewRow.DataGridView == null && dataGridViewRow.Visible; + break; + // case CollectionChangeAction.Refresh: invalidatePreferredSizeCache stays true + } + if (invalidatePreferredSizeCache) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + } + } + + /// + protected virtual void OnRowContextMenuStripChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal ContextMenuStrip OnRowContextMenuStripNeeded(int rowIndex, ContextMenuStrip contextMenuStrip) + { + DataGridViewRowContextMenuStripNeededEventArgs dgvrcmsne = new DataGridViewRowContextMenuStripNeededEventArgs(rowIndex, contextMenuStrip); + OnRowContextMenuStripNeeded(dgvrcmsne); + return dgvrcmsne.ContextMenuStrip; + } + + /// + protected virtual void OnRowContextMenuStripNeeded(DataGridViewRowContextMenuStripNeededEventArgs e) + { + DataGridViewRowContextMenuStripNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPNEEDED] as DataGridViewRowContextMenuStripNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDefaultCellStyleChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDEFAULTCELLSTYLECHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDirtyStateNeeded(QuestionEventArgs e) + { + QuestionEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIRTYSTATENEEDED] as QuestionEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDividerDoubleClick(DataGridViewRowDividerDoubleClickEventArgs e) + { + DataGridViewRowDividerDoubleClickEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIVIDERDOUBLECLICK] as DataGridViewRowDividerDoubleClickEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!e.Handled && e.Button == MouseButtons.Left && e.RowIndex < this.Rows.Count) + { + if (e.RowIndex == -1) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + else + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + AutoResizeRowInternal(e.RowIndex, DataGridViewAutoSizeRowMode.AllCells, true /*fixedWidth*/, true /*internalAutosizing*/); + } + else + { + AutoResizeRowInternal(e.RowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + } + + /// + protected virtual void OnRowDividerHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIVIDERHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowEnter(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex, bool canCreateNewRow, bool validationFailureOccurred) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0); + + if (!validationFailureOccurred) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] = false; + } + if (rowIndex < this.Rows.Count && + columnIndex < this.Columns.Count) + { + bool calledAddNewOnTheDataConnection = false; + if (!validationFailureOccurred && this.AllowUserToAddRowsInternal && this.newRowIndex == rowIndex) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] = true; + + if (canCreateNewRow) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(this.Rows[this.newRowIndex]); + if (this.VirtualMode || this.DataSource != null) + { + if (this.dataConnection != null && this.dataConnection.InterestedInRowEvents) + { + this.dataConnection.OnNewRowNeeded(); + calledAddNewOnTheDataConnection = true; + } + if (this.VirtualMode) + { + OnNewRowNeeded(dgvre); + } + } + + // vsWhidbey 329429: AllowUserToAddRowsInternal can become FALSE while adding a row. + // NOTE: we don't need to invalidate if AllowUserToAddRowsInternal changed to FALSE. + // + if (this.AllowUserToAddRowsInternal) + { + OnDefaultValuesNeeded(dgvre); + InvalidateRowPrivate(this.newRowIndex); + } + #if DEBUG + else + { + Debug.Assert(this.newRowIndex == -1, "newRowIndex and AllowUserToAddRowsInternal became out of sync"); + } + #endif // + } + } + + if (calledAddNewOnTheDataConnection && rowIndex > this.Rows.Count - 1) + { + // Calling AddNew on the DataConnection can result in the entire list being wiped out. + // + rowIndex = Math.Min(rowIndex, this.Rows.Count - 1); + } + + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowEnter(dgvce); + if (this.dataConnection != null && + this.dataConnection.InterestedInRowEvents && + !this.dataConnection.PositionChangingOutsideDataGridView && + !this.dataConnection.ListWasReset && + (!calledAddNewOnTheDataConnection || this.dataConnection.List.Count > 0)) + { + this.dataConnection.OnRowEnter(dgvce); + } + + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + } + + /// + protected virtual void OnRowEnter(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal void OnRowErrorTextChanged(DataGridViewRow dataGridViewRow) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnRowErrorTextChanged(dgvre); + } + + /// + protected virtual void OnRowErrorTextChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + UpdateRowErrorText(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWERRORTEXTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnRowErrorTextNeeded(int rowIndex, string errorText) + { + Debug.Assert(rowIndex >= 0); + DataGridViewRowErrorTextNeededEventArgs dgvretne = new DataGridViewRowErrorTextNeededEventArgs(rowIndex, errorText); + OnRowErrorTextNeeded(dgvretne); + return dgvretne.ErrorText; + } + + /// + protected virtual void OnRowErrorTextNeeded(DataGridViewRowErrorTextNeededEventArgs e) + { + DataGridViewRowErrorTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWERRORTEXTNEEDED] as DataGridViewRowErrorTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowGlobalAutoSize(int rowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + return; + } + + InvalidateRowPrivate(rowIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + bool autoSizeRow = false; + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None && + !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + // Auto size column headers + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + /// + protected virtual void OnRowHeaderCellChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowHeaderGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERCELLCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeaderGlobalAutoSize(int rowIndex) + { + if (!this.RowHeadersVisible) + { + return; + } + + InvalidateCellPrivate(-1, rowIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool rowDisplayed = false; + if (rowIndex != -1) + { + rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + } + + bool fixedColumnHeadersHeight = rowIndex != -1 || this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool fixedRowHeight = rowIndex == -1 || + ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0) || + ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && rowIndex != -1 && !rowDisplayed); + + bool autoSizeRowHeaders = false; + if (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || + (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && rowIndex != -1 && rowDisplayed) || + (this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing && rowIndex == -1) || + (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader && rowIndex != -1 && rowIndex == this.Rows.GetFirstRow(DataGridViewElementStates.Visible))) + { + AutoResizeRowHeadersWidth(rowIndex, + this.rowHeadersWidthSizeMode, + fixedColumnHeadersHeight, + fixedRowHeight); + autoSizeRowHeaders = true; + } + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(-1, true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + if (!fixedRowHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (autoSizeRowHeaders && (!fixedColumnHeadersHeight || !fixedRowHeight)) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(rowIndex, + this.rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowHeight*/); + } + } + + /// + protected virtual void OnRowHeaderMouseClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.RowHeader); + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + break; + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + bool select = true; + if (isControlDown && + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)) + { + select = false; + } + if (select) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null && hti.row != this.ptCurrentCell.Y) + { + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + // Make sure we will be able to scroll into view + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + hti.row != this.ptCurrentCell.Y /*fireRowLeave*/, + hti.row != this.ptCurrentCell.Y /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + true /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + return; + } + if (oldCurrentCellY != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + return; + } + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + + // Row validation was not cancelled, but operation needs to be re-evaluated. + if (hti.row >= this.Rows.Count) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (this.ptCurrentCell.X == -1 && lastVisibleRowIndex != -1) + { + // CurrentCell was reset because commit deleted row(s). + // Since the user wants to select a row, we don't want to + // end up with no CurrentCell. We pick the last visible + // row in the grid which may be the 'new row'. + if (IsColumnOutOfBounds(oldCurrentCellX)) + { + return; + } + bool success = SetAndSelectCurrentCellAddress(oldCurrentCellX, + lastVisibleRowIndex, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + return; + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Visible) == 0) + { + return; + } + } + } + bool selectRowRange = false; + this.trackRow = hti.row; + this.trackRowEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.Y > -1 && + (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0) + { + selectRowRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.row) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + RemoveIndividuallySelectedCells(); + } + else + { + Debug.Assert(this.individualSelectedCells.Count == 0); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = true; + } + if (selectRowRange) + { + if (hti.row >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, hti.row, true); + } + else + { + SelectRowRange(hti.row, this.ptAnchorCell.Y, true); + } + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row) == + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(hti.row, true); + } + if (dataGridViewColumn != null) + { + if (hti.row != this.ptCurrentCell.Y) + { + if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti.row)) + { + return; + } + // set current cell to the left most visible cell in the row + bool success = ScrollIntoView(dataGridViewColumn.Index, hti.row, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti.row)) + { + return; + } + success = SetCurrentCellAddressCore(dataGridViewColumn.Index, hti.row, !selectRowRange, false, true); + Debug.Assert(success); + } + else if (-1 != this.ptCurrentCell.Y) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + else + { + Debug.Assert(this.CurrentCellAddress == new Point(-1, -1)); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row)); + SetSelectedRowCore(hti.row, false); + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnRowHeadersBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeadersDefaultCellStyleChanged(EventArgs e) + { + if (this.RowHeadersVisible) + { + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.RowHeaders)); + + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce == null || dgvcsce.ChangeAffectsPreferredSize) + { + OnRowHeadersGlobalAutoSize(false /*expandingRows*/); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeadersGlobalAutoSize(bool expandingRows) + { + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedRowsHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0 || + !this.RowHeadersVisible; + bool autoSizeRowHeaders = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + if (autoSizeRowHeaders) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, fixedRowsHeight); + } + + if (!fixedRowsHeight) + { + if (expandingRows) + { + AdjustExpandingRows(-1, true /*fixedWidth*/); + } + else + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (autoSizeRowHeaders) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + } + } + + /// + protected virtual void OnRowHeadersWidthChanged(EventArgs e) + { + if (this.RowHeadersVisible) + { + if (this.editingControl != null) + { + PositionEditingControl(true, false, false); + } + + if (this.IsHandleCreated) + { + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + OnRowHeadersGlobalAutoSize(false /*expandingRows*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSWIDTHCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeadersWidthSizeModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + if (!e.PreviousModeAutoSized) + { + // Save current row headers width for later reuse + this.cachedRowHeadersWidth = this.RowHeadersWidth; + } + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + else if (e.PreviousModeAutoSized) + { + this.RowHeadersWidth = this.cachedRowHeadersWidth; + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSWIDTHSIZEMODECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + UpdateRowHeightInfoPrivate(e.Row.Index, false, false /*invalidInAdjustFillingColumns*/); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + OnRowGlobalAutoSize(e.Row.Index); + } + + internal DataGridViewRowHeightInfoNeededEventArgs OnRowHeightInfoNeeded(int rowIndex, int height, int minimumHeight) + { + DataGridViewRowHeightInfoNeededEventArgs dgvrhine = this.RowHeightInfoNeededEventArgs; + dgvrhine.SetProperties(rowIndex, height, minimumHeight); + OnRowHeightInfoNeeded(dgvrhine); + return dgvrhine; + } + + /// + protected virtual void OnRowHeightInfoNeeded(DataGridViewRowHeightInfoNeededEventArgs e) + { + DataGridViewRowHeightInfoNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTINFONEEDED] as DataGridViewRowHeightInfoNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private bool OnRowHeightInfoPushed(int rowIndex, int height, int minimumHeight) + { + Debug.Assert(rowIndex != -1); + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None); + if (this.VirtualMode || this.DataSource != null) + { + DataGridViewRowHeightInfoPushedEventArgs dgvrhipe = new DataGridViewRowHeightInfoPushedEventArgs(rowIndex, height, minimumHeight); + OnRowHeightInfoPushed(dgvrhipe); + if (dgvrhipe.Handled) + { + UpdateRowHeightInfoPrivate(rowIndex, false, true /*invalidInAdjustFillingColumns*/); + return true; + } + } + return false; + } + + /// + protected virtual void OnRowHeightInfoPushed(DataGridViewRowHeightInfoPushedEventArgs e) + { + DataGridViewRowHeightInfoPushedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTINFOPUSHED] as DataGridViewRowHeightInfoPushedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowLeave(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0); + if (rowIndex < this.Rows.Count && columnIndex < this.Columns.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowLeave(dgvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + } + + /// + protected virtual void OnRowLeave(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWLEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + /// + protected virtual void OnRowMinimumHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWMINIMUMHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected internal virtual void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e) + { + DataGridViewRowPostPaintEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWPOSTPAINT] as DataGridViewRowPostPaintEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected internal virtual void OnRowPrePaint(DataGridViewRowPrePaintEventArgs e) + { + DataGridViewRowPrePaintEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWPREPAINT] as DataGridViewRowPrePaintEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnRowsAddedInternal(int rowIndex, int rowCount) + { + OnRowsAdded(new DataGridViewRowsAddedEventArgs(rowIndex, rowCount)); + } + + /// + protected virtual void OnRowsAdded(DataGridViewRowsAddedEventArgs e) + { + DataGridViewRowsAddedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSADDED] as DataGridViewRowsAddedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowsDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + InvalidateData(); + } + else + { + OnRowsGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(hti.row >= 0); + Debug.Assert(this.MultiSelect); + + if (this.ptCurrentCell.Y != -1 && + hti.row != this.ptCurrentCell.Y && + !CommitEditForOperation(this.ptCurrentCell.X, hti.row, true)) + { + // Return silently if validating/commit/abort failed + return; + } + if (IsRowOutOfBounds(hti.row)) + { + return; + } + + this.noSelectionChangeCount++; + try + { + if (this.trackRowEdge >= this.trackRow && hti.row > this.trackRowEdge && this.trackRowEdge >= 0) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRowEdge, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge > this.trackRow && hti.row < this.trackRowEdge && hti.row >= this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.Rows.GetNextRow(hti.row, DataGridViewElementStates.Visible), + this.trackRowEdge, false); + this.trackRowEdge = hti.row; + } + else if (hti.row > this.trackRow && this.trackRowEdge == -1) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge <= this.trackRow && hti.row < this.trackRowEdge && this.trackRowEdge >= 0) + { + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRowEdge, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge < this.trackRow && hti.row > this.trackRowEdge && hti.row <= this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.trackRowEdge, + this.Rows.GetPreviousRow(hti.row, DataGridViewElementStates.Visible), + false); + this.trackRowEdge = hti.row; + } + else if (hti.row < this.trackRow && this.trackRowEdge == -1) + { + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge > this.trackRow && hti.row < this.trackRow) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + this.trackRowEdge, false); + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (hti.row > this.trackRow && this.trackRowEdge < this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.trackRowEdge, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + false); + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.Y != -1 && hti.row != this.ptCurrentCell.Y) + { + if (IsRowOutOfBounds(hti.row)) + { + return; + } + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, + hti.row, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + + private void OnRowsGlobalAutoSize() + { + InvalidateData(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + // Autosize rows if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + + // Auto size columms also if needed + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | + DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + true /*fixedHeight*/); + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + // Auto size column headers + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + // Second round of rows autosizing + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + // Second round of column headers autosizing + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + internal void OnRowsRemovedInternal(int rowIndex, int rowCount) + { + OnRowsRemoved(new DataGridViewRowsRemovedEventArgs(rowIndex, rowCount)); + } + + /// + protected virtual void OnRowsRemoved(DataGridViewRowsRemovedEventArgs e) + { + DataGridViewRowsRemovedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSREMOVED] as DataGridViewRowsRemovedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowStateChanged(int rowIndex, DataGridViewRowStateChangedEventArgs e) + { + Debug.Assert(rowIndex >= -1); + + // If row.Frozen changed, we need to update the vertical scroll bar + // A hidden row may become visible and vice-versa, we'd better repaint the whole control + DataGridViewRow dataGridViewRow = e.Row; + DataGridViewElementStates newState = DataGridViewElementStates.None; + bool rowVisible = false; + if (rowIndex >= 0) + { + newState = this.Rows.GetRowState(rowIndex); + rowVisible = ((newState & DataGridViewElementStates.Visible) != 0); + } + switch (e.StateChanged) + { + // At this point we assume that only the Selected state has an influence on the rendering of the row. + // If there is a customer scenario where another state has an influence, the dev will have to invalidate the row by hand. + // case DataGridViewElementStates.ReadOnly: + // case DataGridViewElementStates.Resizable: + + case DataGridViewElementStates.Selected: + if (rowVisible && this.inBulkPaintCount == 0) + { + InvalidateRowPrivate(rowIndex); + } + break; + + case DataGridViewElementStates.Frozen: + if (rowVisible) + { + if ((newState & DataGridViewElementStates.Frozen) == 0) + { + // row was unfrozen - make it the first visible scrolling row if there is room + FirstVisibleScrollingRowTempted(rowIndex); + } + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + } + break; + + case DataGridViewElementStates.Visible: + { + if (!rowVisible && (newState & DataGridViewElementStates.Displayed) != 0) + { + // Displayed row becomes invisible. Turns off the Displayed state. + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Displayed, false); + } + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + + bool rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + + bool autoSizeRow = false; + + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + int height = dataGridViewRow.ThicknessInternal; + if (rowVisible) + { + // Cache row's height before potential autosizing occurs + // Valid operation even for shared rows + dataGridViewRow.CachedThickness = height; + if (!((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + } + else if (height != dataGridViewRow.CachedThickness) + { + // Rows that are made invisible in the collection take their non-autosized height + // Not calling OnRowHeightInfoPushed(...) because rows are autosized + // Make sure the affected row is unshared + if (dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[rowIndex]; + } + dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness); + } + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + if (rowVisible && this.Rows.GetRowCount(DataGridViewElementStates.Visible) > 1) + { + // Columns can only expand, and not collapse. + AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex); + } + else + { + AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + break; + } + } + + DataGridViewRowStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSTATECHANGED] as DataGridViewRowStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + rowIndex == this.ptCurrentCell.Y && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if ((newState & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly && + !this.Columns[this.ptCurrentCell.X].ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current row becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnRowUnshared(DataGridViewRow dataGridViewRow) + { + if (-1 != this.ptCurrentCell.X && dataGridViewRow.Index == this.ptCurrentCell.Y && this.editingControl != null) + { + this.CurrentCellInternal.CacheEditingControl(); + } + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnRowUnshared(dgvre); + } + + /// + protected virtual void OnRowUnshared(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWUNSHARED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private bool OnRowValidating(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + DataGridViewCellCancelEventArgs dgvcce = new DataGridViewCellCancelEventArgs(columnIndex, rowIndex); + OnRowValidating(dgvcce); + if (!dgvcce.Cancel) + { + if (this.dataConnection != null && + this.dataConnection.InterestedInRowEvents && + !this.dataConnection.PositionChangingOutsideDataGridView && + !this.dataConnection.ListWasReset) + { + this.dataConnection.OnRowValidating(dgvcce); + } + } + if (dataGridViewCell != null && rowIndex < this.Rows.Count && columnIndex < this.Columns.Count) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + return dgvcce.Cancel; + } + + /// + protected virtual void OnRowValidating(DataGridViewCellCancelEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWVALIDATING] as DataGridViewCellCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + private void OnRowValidated(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + this.IsCurrentRowDirtyInternal = false; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing] = false; + if (rowIndex == this.newRowIndex) + { + // Stop displaying the default cell values on the 'new row'. + InvalidateRowPrivate(rowIndex); + } + + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowValidated(dgvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + protected virtual void OnRowValidated(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWVALIDATED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + private void RefreshByCurrentPos(int oldValue, int newValue) + { + Point pt = PointToScreen(Location); + int step = newValue - oldValue; + + // horizontal scrool left + if (pt.X < 0 && step < 0) + { + Invalidate(new Rectangle(new Point(-pt.X, ColumnHeadersHeight), + new Size(-step, ClientSize.Height))); + } + + pt.X += Width; + pt.Y += Height; + Rectangle rect = Screen.GetBounds(pt); + + // horizontal scrool right + if (pt.X > rect.Right && step > 0) + { + Invalidate(new Rectangle(new Point(ClientSize.Width - (pt.X - rect.Right) - step, ColumnHeadersHeight), + new Size(step, ClientSize.Height))); + } + + // vertical scrool up + if (pt.Y < 0 && step < 0) + { + Invalidate(new Rectangle(new Point(0, -pt.Y), + new Size(-step, ClientSize.Width))); + } + + // vertical scrool down + if (pt.Y > rect.Bottom && step > 0) + { + Invalidate(new Rectangle(new Point(0, ColumnHeadersHeight), + new Size(ClientSize.Width, ClientSize.Height - (pt.Y - rect.Bottom) - step))); + } + } + + private void OnScroll(ScrollEventType scrollEventType, int oldValue, int newValue, ScrollOrientation orientation) + { + ScrollEventArgs se = new ScrollEventArgs(scrollEventType, oldValue, newValue, orientation); + OnScroll(se); + RefreshByCurrentPos(oldValue, newValue); + if (this.Focused && this.IsGridFocusRectangleEnabled()) + { + InvalidateGridFocusOnScroll(newValue - oldValue, orientation); + } + if (ScrollOrientation.VerticalScroll == orientation) + { + if (se.NewValue != newValue) + { + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll] = true; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + int rowIndexPrevious = rowIndex; + newValue = se.NewValue; + while (rowIndex != -1 && newValue > 0) + { + rowIndexPrevious = rowIndex; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + newValue--; + } + if (rowIndex != -1) + { + rowIndexPrevious = rowIndex; + } + if (rowIndexPrevious != -1) + { + this.FirstDisplayedScrollingRowIndex = rowIndexPrevious; + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll] = false; + } + } + } + else + { + if (se.NewValue != newValue) + { + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll] = true; + this.HorizontalOffset = se.NewValue; + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll] = false; + } + } + } + } + + /// + protected virtual void OnScroll(ScrollEventArgs e) + { + ScrollEventHandler eh = this.Events[EVENT_DATAGRIDVIEWSCROLL] as ScrollEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnSelectionChanged(EventArgs e) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = false; + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWSELECTIONCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal bool OnSortCompare(DataGridViewColumn dataGridViewSortedColumn, object value1, object value2, int rowIndex1, int rowIndex2, out int sortResult) + { + DataGridViewSortCompareEventArgs dgvsce = new DataGridViewSortCompareEventArgs(dataGridViewSortedColumn, value1, value2, rowIndex1, rowIndex2); + OnSortCompare(dgvsce); + sortResult = dgvsce.SortResult; + return dgvsce.Handled; + } + + /// + protected virtual void OnSortCompare(DataGridViewSortCompareEventArgs e) + { + DataGridViewSortCompareEventHandler eh = this.Events[EVENT_DATAGRIDVIEWSORTCOMPARE] as DataGridViewSortCompareEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnSorted(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWSORTED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnSortGlyphDirectionChanged(DataGridViewColumnHeaderCell dataGridViewColumnHeaderCell) + { + Debug.Assert(dataGridViewColumnHeaderCell != null); + + if (dataGridViewColumnHeaderCell.OwningColumn == this.SortedColumn) + { + if (dataGridViewColumnHeaderCell.SortGlyphDirection == SortOrder.None) + { + this.sortedColumn = null; + DataGridViewColumn dataGridViewColumn = dataGridViewColumnHeaderCell.OwningColumn; + + if (dataGridViewColumn.IsDataBound) + { + // If the column whose SortGlyphChanges is the sorted column and it is also the dataBound column + // then see if there is another dataBound column which has the same property name as the sorted column. + // If so, then make that dataGridViewColumn the sorted column in the data grid view. + for (int i = 0; i < this.Columns.Count; i ++) + { + if (dataGridViewColumn != this.Columns[i] && + this.Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable && + String.Compare(dataGridViewColumn.DataPropertyName, + this.Columns[i].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + Debug.Assert(this.Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time"); + this.sortedColumn = this.Columns[i]; + break; + } + } + } + } + + this.sortOrder = this.sortedColumn != null ? this.sortedColumn.HeaderCell.SortGlyphDirection : SortOrder.None; + } + + InvalidateCellPrivate(dataGridViewColumnHeaderCell); + } + + private void OnTopLeftHeaderMouseDown() + { + if (this.MultiSelect) + { + SelectAll(); + if (-1 != this.ptCurrentCell.X) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + } + + /// + protected virtual void OnUserAddedRow(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERADDEDROW] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnUserDeletedRow(DataGridViewRowEventArgs e) + { + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERDELETEDROW] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnUserDeletingRow(DataGridViewRowCancelEventArgs e) + { + DataGridViewRowCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERDELETINGROW] as DataGridViewRowCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.Color || + e.Category == UserPreferenceCategory.Locale || + e.Category == UserPreferenceCategory.General || + e.Category == UserPreferenceCategory.Window || + e.Category == UserPreferenceCategory.VisualStyle) + { + OnGlobalAutoSize(); + if (e.Category == UserPreferenceCategory.Window) + { + this.cachedEditingControl = null; + if (this.editingControl != null) + { + // The editing control may not adapt well to the new system rendering, + // so instead of caching it into the this.cachedEditingControl variable + // next time editing mode is exited, simply discard the control. + this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl] = true; + } + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + } + } + + /// + protected override void OnValidating(CancelEventArgs e) + { + // VSWhidbey 481170. Avoid Cell/Row Validation events when the grid or its editing control gets the focus + if (!this.BecomingActiveControl && (this.editingControl == null || !this.editingControl.BecomingActiveControl)) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl, + DataGridViewValidateCellInternal.Always, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + false /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + e.Cancel = true; + return; + } + } + + if (this.ptCurrentCell.X >= 0) + { + DataGridViewCell dataGridViewCellTmp = null; + if (OnRowValidating(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y)) + { + // Row validation was cancelled + e.Cancel = true; + return; + } + if (this.ptCurrentCell.X == -1) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y); + // Row validation was not cancelled, but does operation need to be re-evaluated. + if (this.DataSource != null && + this.ptCurrentCell.X >= 0 && + this.AllowUserToAddRowsInternal && + this.newRowIndex == this.ptCurrentCell.Y) + { + // Current cell needs to be moved to the row just above the 'new row' if possible. + int rowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + if (rowIndex > -1) + { + bool success = SetAndSelectCurrentCellAddress(this.ptCurrentCell.X, rowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + else + { + bool success = SetCurrentCellAddressCore(-1, -1, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + } + } + base.OnValidating(e); + } + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + OnVisibleChangedPrivate(); + } + + private void OnVisibleChangedPrivate() + { + // Debug.Assert(!this.displayedBandsInfo.Dirty); Not valid because EnsureDirtyState can potentially be called + // for example when RowHeadersVisible is changed while the control is invisible. + int rowIndexTmp; + + if (this.Visible) + { + // Make sure all displayed bands get the Displayed state: 1 & 2 for rows + + // 1. Make sure all displayed frozen rows have their Displayed state set to true + int numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + if (numDisplayedFrozenRows > 0) + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { +#if DEBUG + int numDisplayedFrozenRowsDbg = numDisplayedFrozenRows; + while (numDisplayedFrozenRowsDbg > 0) + { + Debug.Assert(rowIndexTmp != -1); + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRowsDbg--; + } +#endif + return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job. + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + } + } + + // 2. Make sure all displayed scrolling rows have their Displayed state set to true + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp > -1) + { + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Visible) != 0); + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { +#if DEBUG + int numDisplayedScrollingRowsDbg = numDisplayedScrollingRows; + while (numDisplayedScrollingRowsDbg > 0) + { + Debug.Assert(rowIndexTmp != -1); + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRowsDbg--; + } +#endif + return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job. + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + } + } + } + else + { + // Make sure all displayed bands lose the Displayed state + UpdateRowsDisplayedState(false /*displayed*/); + } + + UpdateColumnsDisplayedState(this.Visible /*displayed*/); + } + + /// + protected virtual void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds) + { + // Paint potential block below rows + Rectangle rcBelowRows = gridBounds; + + // Fix Dev10 Bug473771 - Remaining cell images on DataGridView + int visibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Displayed); + if (this.layout.ColumnHeadersVisible) + { + rcBelowRows.Y += this.layout.ColumnHeaders.Height; + rcBelowRows.Height -= this.layout.ColumnHeaders.Height; + } + else if (this.SingleHorizontalBorderAdded && visibleRowsHeight > 0) + { + rcBelowRows.Y++; + rcBelowRows.Height--; + } + + rcBelowRows.Y += visibleRowsHeight; + rcBelowRows.Height -= visibleRowsHeight; + if (rcBelowRows.Width > 0 && rcBelowRows.Height > 0) + { + graphics.FillRectangle(this.backgroundBrush, rcBelowRows); + } + + // Paint potential block next to column headers and rows + // Fix Dev10 Bug473771 - Remaining cell images on DataGridView + int visibleColumnsWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Displayed); + Rectangle rcNextRows = gridBounds; + if (this.Columns.Count > 0) + { + if (this.layout.RowHeadersVisible) + { + if (!this.RightToLeftInternal) + { + rcNextRows.X += this.layout.RowHeaders.Width; + } + rcNextRows.Width -= this.layout.RowHeaders.Width; + } + else if (this.SingleVerticalBorderAdded && visibleColumnsWidth > 0) + { + if (!this.RightToLeftInternal) + { + rcNextRows.X++; + } + rcNextRows.Width--; + } + } + int rowsWidth = visibleColumnsWidth - this.horizontalOffset; + if (!this.RightToLeftInternal) + { + rcNextRows.X += rowsWidth; + } + rcNextRows.Width -= rowsWidth; + if (rcBelowRows.Height > 0) + { + rcNextRows.Height = gridBounds.Height - rcBelowRows.Height; + } + if (rcNextRows.Width > 0 && rcNextRows.Height > 0) + { + graphics.FillRectangle(this.backgroundBrush, rcNextRows); + } + } + + private void PaintBorder(Graphics g, Rectangle clipRect, Rectangle bounds) + { + Debug.Assert(bounds.Left == 0); + Debug.Assert(bounds.Top == 0); + if (this.BorderStyle == BorderStyle.None) + { + return; + } + bool paintingNeeded = false; + int borderWidth = this.BorderWidth; + // Does the clipRect intersect with the top edge? + Rectangle edge = new Rectangle(0, 0, bounds.Width, borderWidth); + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the bottom edge? + edge.Y = bounds.Height - borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the left edge? + edge.Y = 0; + edge.Height = bounds.Height; + edge.Width = borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the right edge? + edge.X = bounds.Width - borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + } + } + } + + if (paintingNeeded) + { + if (this.BorderStyle == BorderStyle.Fixed3D) + { + if (Application.RenderWithVisualStyles) + { + Pen pen = GetCachedPen(VisualStyleInformation.TextControlBorder); + g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1)); + } + else + { + ControlPaint.DrawBorder3D(g, bounds, Border3DStyle.Sunken); + } + } + else if (this.BorderStyle == BorderStyle.FixedSingle) + { + Pen pen = GetCachedPen(SystemColors.ControlText); + g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1)); + } + else + { + Debug.Fail("DataGridView.PaintBorder - Unexpected BorderStyle value."); + } + } + } + + private void PaintColumnHeaders(Graphics g, Rectangle clipBounds, bool singleBorderAdded) + { + if (g.IsVisible(this.layout.ColumnHeaders)) + { + Rectangle bandBounds, cellBounds; + bandBounds = cellBounds = this.layout.ColumnHeaders; + bandBounds.Height = cellBounds.Height = this.columnHeadersHeight; + int cx = 0; + bool isFirstDisplayedColumn = true, isLastVisibleColumn = false; + DataGridViewCell cell; + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + DataGridViewColumn dataGridViewColumnNext = null; + + // first paint the visible frozen columns + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + cell = dataGridViewColumn.HeaderCell; + cellBounds.Width = dataGridViewColumn.Thickness; + if (singleBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (this.RightToLeftInternal) + { + cellBounds.X = bandBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = bandBounds.X + cx; + } + + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + + dataGridViewColumnNext = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + if (dataGridViewColumnNext == null) + { + isLastVisibleColumn = (this.displayedBandsInfo.FirstDisplayedScrollingCol < 0); + } + + dgvabsEffective = AdjustColumnHeaderBorderStyle(this.AdvancedColumnHeadersBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + isFirstDisplayedColumn, isLastVisibleColumn); + + // Microsoft: should paintSelectionBackground be dev-settable? + cell.PaintWork(g, + clipBounds, + cellBounds, + -1, + dataGridViewColumn.State, + inheritedCellStyle, + dgvabsEffective, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + + cx += cellBounds.Width; + if (cx >= bandBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + // then paint the visible scrolling ones + Rectangle scrollingBounds = bandBounds; + if (!this.RightToLeftInternal) + { + scrollingBounds.X -= this.negOffset; + } + scrollingBounds.Width += this.negOffset; + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && cx < scrollingBounds.Width) + { + Region clipRegion = null; + if (this.negOffset > 0) + { + clipRegion = g.Clip; + Rectangle rowRect = bandBounds; + if (!this.RightToLeftInternal) + { + rowRect.X += cx; + } + rowRect.Width -= cx; + g.SetClip(rowRect); + } + + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + while (dataGridViewColumn != null) + { + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + cell = dataGridViewColumn.HeaderCell; + cellBounds.Width = dataGridViewColumn.Thickness; + if (singleBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (this.RightToLeftInternal) + { + cellBounds.X = scrollingBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = scrollingBounds.X + cx; + } + + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + + dataGridViewColumnNext = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + isLastVisibleColumn = (dataGridViewColumnNext == null); + + dgvabsEffective = AdjustColumnHeaderBorderStyle(this.AdvancedColumnHeadersBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + isFirstDisplayedColumn, isLastVisibleColumn); + + cell.PaintWork(g, + clipBounds, + cellBounds, + -1, + dataGridViewColumn.State, + inheritedCellStyle, + dgvabsEffective, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + + cx += cellBounds.Width; + if (cx >= scrollingBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + if (this.negOffset > 0) + { + Debug.Assert(clipRegion != null); + g.Clip = clipRegion; + clipRegion.Dispose(); + } + } + } + } + + private void PaintGrid(Graphics g, + Rectangle gridBounds, + Rectangle clipRect, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded) + { + Rectangle rc = gridBounds; + + if (this.layout.TopLeftHeader.Width > 0 && + (clipRect.IntersectsWith(this.layout.TopLeftHeader) || this.lastHeaderShadow != -1)) + { + if (this.Columns.Count > 0 || this.Rows.Count > 0) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(this.layout.TopLeftHeader); + PaintTopLeftHeaderCell(g); + g.Clip = clipRegion; + } + } + } + + if (this.layout.ColumnHeadersVisible) + { + Rectangle columnHeadersClip = new Rectangle(); + columnHeadersClip = this.layout.ColumnHeaders; + if (singleVerticalBorderAdded) + { + columnHeadersClip.Width++; + } + if (clipRect.IntersectsWith(columnHeadersClip) || this.lastHeaderShadow != -1) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(columnHeadersClip); + PaintColumnHeaders(g, columnHeadersClip, singleVerticalBorderAdded); + g.Clip = clipRegion; + } + } + int columnHeadersHeight = this.layout.ColumnHeaders.Height; + rc.Y += columnHeadersHeight; + rc.Height -= columnHeadersHeight; + + if (this.lastHeaderShadow != -1) + { + DrawColHeaderShadow(g, this.lastHeaderShadow); + } + } + + if (rc.Height > 0) + { + PaintRows(g, rc, clipRect, /*singleVerticalBorderAdded, */ singleHorizontalBorderAdded); + } + + if (this.currentRowSplitBar != -1) + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]); + DrawRowSplitBar(this.currentRowSplitBar); + } + else if (this.currentColSplitBar != -1) + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize]); + DrawColSplitBar(this.currentColSplitBar); + } + } + + private void PaintRows(Graphics g, + Rectangle boundingRect, + Rectangle clipRect, + /*bool singleVerticalBorderAdded,*/ + bool singleHorizontalBorderAdded) + { + int cy = 0; + Rectangle rowBounds; + DataGridViewRow dataGridViewRow; + bool isFirstDisplayedRow = true; + int indexTmp, indexTmpNext; + + // paint visible none-scrolling rows + indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + rowBounds = boundingRect; + + // Dev 10 Bug #434494 - DataGridView AutoSizeRowsMode does not work properly after column sort + // Should unshared the row and set the thickness to a perfect value + // every time user scroll to display the specific row. + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + this.Rows.SharedRow(indexTmp).CachedThickness = rowHeight; + AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + } + + rowBounds.Height = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + rowBounds.Height++; + } + rowBounds.Y = boundingRect.Y + cy; + + indexTmpNext = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + if (clipRect.IntersectsWith(rowBounds)) + { + dataGridViewRow = this.Rows.SharedRow(indexTmp); + dataGridViewRow.Paint(g, + clipRect, + rowBounds, + indexTmp, + this.Rows.GetRowState(indexTmp), + isFirstDisplayedRow, + (indexTmpNext == -1) && (this.displayedBandsInfo.FirstDisplayedScrollingRow == -1)); + } + cy += rowBounds.Height; + if (cy >= boundingRect.Height) + { + break; + } + indexTmp = indexTmpNext; + isFirstDisplayedRow = false; + } + + // paint scrolling rows + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0 && cy < boundingRect.Height) + { + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0); + while (indexTmp != -1) + { + rowBounds = boundingRect; + + // Dev 10 Bug #434494 - DataGridView AutoSizeRowsMode does not work properly after column sort + // Should unshared the row and set the thickness to a perfect value + // every time user scroll to display the specific row. + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + this.Rows.SharedRow(indexTmp).CachedThickness = rowHeight; + AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + } + + rowBounds.Height = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + rowBounds.Height++; + } + rowBounds.Y = boundingRect.Y + cy; + + indexTmpNext = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + + if (clipRect.IntersectsWith(rowBounds)) + { + dataGridViewRow = this.Rows.SharedRow(indexTmp); + dataGridViewRow.Paint(g, + clipRect, + rowBounds, + indexTmp, + this.Rows.GetRowState(indexTmp), + isFirstDisplayedRow, + indexTmpNext == -1); + } + cy += rowBounds.Height; + if (cy >= boundingRect.Height) + { + break; + } + indexTmp = indexTmpNext; + isFirstDisplayedRow = false; + } + } + } + + private void PaintTopLeftHeaderCell(Graphics g) + { + if (g.IsVisible(this.layout.TopLeftHeader)) + { + DataGridViewCell cell = this.TopLeftHeaderCell; + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + Rectangle cellBounds = this.layout.TopLeftHeader; + cellBounds.Width = this.rowHeadersWidth; + cellBounds.Height = this.columnHeadersHeight; + // Microsoft: Should paintSelectionBackground be dev-settable? + cell.PaintWork(g, + this.layout.TopLeftHeader, + cellBounds, + -1, + cell.State, + inheritedCellStyle, + this.AdjustedTopLeftHeaderBorderStyle, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + } + } + + private void PerformLayoutPrivate(bool useRowShortcut, + bool computeVisibleRows, + bool invalidInAdjustFillingColumns, + bool repositionEditingControl) + { + this.inPerformLayoutCount++; + try + { + if (invalidInAdjustFillingColumns && this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + if (this.IsHandleCreated) + { + bool columnsAdjusted = false; + if (useRowShortcut) + { + ComputeLayoutShortcut(computeVisibleRows); + } + else + { + columnsAdjusted = ComputeLayout(); + } + FlushDisplayedChanged(); + if (columnsAdjusted && this.inPerformLayoutCount < 3) + { + // Some columns were auto-filled, the rows and column headers may need to be autosized. + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + if (repositionEditingControl && this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, false /*setSize*/, false /*setFocus*/); + } + } + else + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = -1; + this.displayedBandsInfo.FirstDisplayedFrozenRow = -1; + this.displayedBandsInfo.FirstDisplayedScrollingRow = -1; + this.displayedBandsInfo.FirstDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedFrozenRows = 0; + this.displayedBandsInfo.NumDisplayedFrozenCols = 0; + this.displayedBandsInfo.NumDisplayedScrollingRows = 0; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = 0; + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + this.displayedBandsInfo.LastDisplayedScrollingRow = -1; + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + if (this.layout != null) + { + this.layout.dirty = true; + } + } + } + finally + { + this.inPerformLayoutCount--; + Debug.Assert(this.inPerformLayoutCount >= 0); + } + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + private void PopulateNewRowWithDefaultValues() + { + if (this.newRowIndex != -1) + { + DataGridViewRow newRow = this.Rows.SharedRow(this.newRowIndex); + DataGridViewCellCollection newRowCells = newRow.Cells; + foreach (DataGridViewCell dataGridViewCell in newRowCells) + { + if (dataGridViewCell.DefaultNewRowValue != null) + { + newRow = this.Rows[this.newRowIndex]; // unshare the 'new row'. + newRowCells = newRow.Cells; + break; + } + } + foreach (DataGridViewCell dataGridViewCell in newRowCells) + { + dataGridViewCell.SetValueInternal(this.newRowIndex, dataGridViewCell.DefaultNewRowValue); + } + } + } + + private void PositionEditingControl(bool setLocation, bool setSize, bool setFocus) + { + Debug.Assert(this.editingControl != null); + + if (!this.IsHandleCreated) + { + return; + } + + #if DEBUG + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + Debug.Assert(dataGridViewCell.ColumnIndex == this.ptCurrentCell.X); + Debug.Assert(dataGridViewCell.RowIndex == this.ptCurrentCell.Y || dataGridViewCell.RowIndex == -1); + #endif + + Rectangle editingZone = this.layout.Data; + if (editingZone.Width == 0 || editingZone.Height == 0) + { + return; + } + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + + try + { + int leftEdge = GetColumnXFromIndex(this.ptCurrentCell.X); + if (this.RightToLeftInternal) + { + leftEdge -= this.Columns[this.ptCurrentCell.X].Width-1; + } + Rectangle cellBounds = new Rectangle(leftEdge, GetRowYFromIndex(this.ptCurrentCell.Y), + this.Columns[this.ptCurrentCell.X].Width, this.Rows.SharedRow(this.ptCurrentCell.Y).GetHeight(this.ptCurrentCell.Y)); + Rectangle cellClip = cellBounds; + // Need to clip the zones of the frozen columns and rows and headers. + if (!this.Columns[this.ptCurrentCell.X].Frozen) + { + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (!this.RightToLeftInternal) + { + editingZone.X += totalVisibleFrozenWidth; + } + editingZone.Width = Math.Max(0, editingZone.Width - totalVisibleFrozenWidth); + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + { + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + editingZone.Y += totalVisibleFrozenHeight; + } + cellClip.Intersect(editingZone); + + if (cellClip.Width == 0 || cellClip.Height == 0) + { + // we cannot simply make the control invisible because we want it to keep the focus. + // (and Control::CanFocus returns false if the control is not visible). + // So we place the editing control to the right of the DataGridView. + Debug.Assert(this.editingControl != null); + this.editingPanel.Location = new Point(this.Width + 1, 0); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] = true; + } + else + { + bool singleVerticalBorderAdded = this.SingleVerticalBorderAdded; + bool singleHorizontalBorderAdded = this.SingleHorizontalBorderAdded; + bool isFirstDisplayedColumn = this.FirstDisplayedColumnIndex == this.ptCurrentCell.X; + bool isFirstDisplayedRow = this.FirstDisplayedRowIndex == this.ptCurrentCell.Y; + + if (singleVerticalBorderAdded && isFirstDisplayedColumn) + { + if (!this.RightToLeftInternal) + { + cellBounds.X--; + cellClip.X--; + } + cellBounds.Width++; + cellClip.Width++; + } + if (singleHorizontalBorderAdded && isFirstDisplayedRow) + { + cellBounds.Y--; + cellClip.Y--; + cellBounds.Height++; + cellClip.Height++; + } + + this.CurrentCellInternal.PositionEditingControl( + setLocation || this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden], + setSize || this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden], + cellBounds, cellClip, this.InheritedEditingCellStyle, + singleVerticalBorderAdded, singleHorizontalBorderAdded, + isFirstDisplayedColumn, isFirstDisplayedRow); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] = false; + } + this.editingPanel.Visible = true; + if (setFocus) + { + CorrectFocus(false /*onlyIfGridHasFocus*/); + } + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + } + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessAKey(Keys keyData) + { + if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control && + this.MultiSelect) + { + SelectAll(); + return true; + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessDeleteKey(Keys keyData) + { + if (this.AllowUserToDeleteRowsInternal) + { + if (this.editingControl != null) + { + // editing control gets a chance to handle the Delete key first + return false; + } + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int remainingSelectedRows = 0; + try + { + this.selectedBandSnapshotIndexes = new DataGridViewIntLinkedList(this.selectedBandIndexes); + while (this.selectedBandSnapshotIndexes.Count > remainingSelectedRows) + { + int rowIndex = this.selectedBandSnapshotIndexes[remainingSelectedRows]; + Debug.Assert(rowIndex >= 0); + if (rowIndex == this.newRowIndex || rowIndex >= this.Rows.Count) + { + remainingSelectedRows++; + } + else + { + DataGridViewRowCancelEventArgs dgvrce = new DataGridViewRowCancelEventArgs(this.Rows[rowIndex]); + OnUserDeletingRow(dgvrce); + if (!dgvrce.Cancel) + { + DataGridViewRow dataGridViewRow = this.Rows[rowIndex]; + if (this.DataSource != null) + { + int dataGridRowsCount = this.Rows.Count; +#if DEBUG + int dataGridViewRowsCount = this.Rows.Count; // the number of rows in the dataGridView row collection not counting the AddNewRow + int rowCount = this.dataConnection.CurrencyManager.List.Count; + if (this.AllowUserToAddRowsInternal ) + { + if (this.newRowIndex < rowCount) + { + // the user did not type inside the 'add new row' + Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync in AddNewTransaction when the user did not type in the 'add new row'"); + } + else + { + dataGridViewRowsCount --; + } + } + + Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync"); +#endif + DataGridViewDataErrorEventArgs dgvdee = null; + try + { + this.DataConnection.DeleteRow(rowIndex); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + // this is tricky. + // the back-end threw an exception. At that stage, we did not delete the dataGridView row + // from our collection of dataGridView rows. + // So all we do is to throw the exception if the user wants. Otherwise we don't do anything. + dgvdee = new DataGridViewDataErrorEventArgs(exception, + -1, + rowIndex, + // null, + // null, + DataGridViewDataErrorContexts.RowDeletion); + OnDataErrorInternal(dgvdee); + + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + else + { + remainingSelectedRows++; + } + } + + if (dataGridRowsCount != this.Rows.Count) + { + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + } + else if (dgvdee == null) + { + remainingSelectedRows++; + } + } + else + { + this.Rows.RemoveAtInternal(rowIndex, false /*force*/); + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + } + } + else + { + remainingSelectedRows++; + } + } + } + } + finally + { + this.selectedBandSnapshotIndexes = null; + } + return true; + } + } + return false; + } + + /// + /// + /// + /// Gets or sets a value that indicates whether a key should be processed + /// further. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) + { + Keys key = (keyData & Keys.KeyCode); + + if (key == Keys.Enter) + { + if (ProcessEnterKey(keyData)) + { + return true; + } + } + else if (key == Keys.Escape) + { + bool keyEffective = this.IsEscapeKeyEffective; + bool ret = base.ProcessDialogKey(keyData); + if (!keyEffective) + { + // This call may perform Click of Cancel button of form. + if (this.Focused) + { + // Make sure the current cell is in editing mode if needed. + if (this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + BeginEditInternal(true /*selectAll*/); + } + } + } + return ret; + } + else if (key == Keys.D0 || key == Keys.NumPad0) + { + if (ProcessZeroKey(keyData)) + { + return true; + } + } + else if (key == Keys.C || key == Keys.Insert) + { + if (ProcessInsertKey(keyData)) + { + return true; + } + } + else if (key == Keys.Tab) + { + IntSecurity.AllWindows.Demand(); + if (ProcessTabKey(keyData)) + { + return true; + } + else + { + if (this.editingControl != null) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] = true; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl, + DataGridViewValidateCellInternal.Always, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + true /*fireRowLeave*/, + true /*fireRowEnter*/, + true /*fireLeave*/, + false /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return true; + } + } + + keyData &= ~Keys.Control; + bool ret = false; + + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try + { + ret = base.ProcessDialogKey(keyData); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] && this.Focused) + { + // There was no other control to tab to. The CellLeave, RowLeave, Leave events were raised. + // Since the DataGridView control still has the focus, Enter, RowEnter, CellEnter events need to be raised. + OnEnter(EventArgs.Empty); + } + + return ret; + } + } + return base.ProcessDialogKey(keyData); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessDownKey(Keys keyData) + { + bool moved; + return ProcessDownKeyInternal(keyData, out moved); + } + + private bool ProcessDownKeyInternal(Keys keyData, out bool moved) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || lastVisibleRowIndex == -1) + { + moved = false; + return false; + } + int nextVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + nextVisibleRowIndex = this.Rows.GetNextRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + moved = true; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell + // the list under the DataGridView changes. + // In this case set moved to false so the users that call ProcessDownKey + // will commit the data. + // See vsWhidbey: 325296. + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + //SelectCellRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, lastVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptCurrentCell.X == -1 || this.ptAnchorCell.X == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, nextVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true /*forCurrentCellChange*/)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, + nextVisibleRowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.Y == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + if (nextVisibleRowIndex >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, nextVisibleRowIndex, true); + } + else + { + SelectRowRange(nextVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedRowCore(nextVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1 || this.ptCurrentCell.Y == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, lastVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + moved = false; + return true; + } + if (nextVisibleRowIndex >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, nextVisibleRowIndex, true); + } + else + { + SelectRowRange(nextVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextVisibleRowIndex, true); + } + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, nextVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(nextVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessEndKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int lastVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(this.ptCurrentCell.Y, lastVisibleRowIndex, true); + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + else + { + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.Y >= 0) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + } + else + { + SetSelectedRowCore(lastVisibleRowIndex, true); + } + + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessEnterKey(Keys keyData) + { + // commitRow is commented out for Dev10 bug 473789. + // When Enter is pressed, no matter Ctrl is also pressed or not, + // changes in a cell should be commited. + // Therefore, commitRow should be always true, and useless here. + bool moved = false, ret = true;//, commitRow = true; + if ((keyData & Keys.Control) == 0) + { + // Enter behaves like down arrow - it commits the potential editing and goes down one cell. + // commitRow = false; + keyData &= ~Keys.Shift; + ret = ProcessDownKeyInternal(keyData, out moved); + } + + if (!moved) + { + DataGridViewCell dataGridViewCurrentCell = null; + // Try to commit the potential editing + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + if (this.ptCurrentCell.X != -1) + { + dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.WhenChanged, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + } + } + else + { + EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.WhenChanged /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell unused here*/, + true /*resetAnchorCell unused here*/); + } + if (/*commitRow && */this.IsCurrentRowDirty) + { + dataGridViewCurrentCell = null; + int columnIndex = this.ptCurrentCell.X; + int rowIndex = this.ptCurrentCell.Y; + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return ret; + } + if (!OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex)) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return ret; + } + OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex); + } + } + } + return ret; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessEscapeKey(Keys keyData) + { + if (this.IsEscapeKeyEffective) + { + if (this.IsMouseOperationActive()) + { + ResetTrackingState(); + } + else + { + CancelEdit(true /*endEdit, DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration*/); + } + return true; + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessF2Key(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && + !this.IsCurrentCellInEditMode && + ModifierKeys == 0) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + Debug.Assert(this.CurrentCellInternal != null); + Debug.Assert(this.EditMode != DataGridViewEditMode.EditOnEnter || + (IsSharedCellReadOnly(this.CurrentCellInternal, this.ptCurrentCell.Y) || !ColumnEditable(this.ptCurrentCell.X))); + if (ColumnEditable(this.ptCurrentCell.X) && + !IsSharedCellReadOnly(this.CurrentCellInternal, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2 || + this.EditMode == DataGridViewEditMode.EditOnF2)) + { + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + BeginEditInternal(this.EditMode == DataGridViewEditMode.EditOnF2 /*selectAll*/); + return true; + } + } + return false; + } + + /// + /// + /// Sorts the current column. + /// 'UseLegacyAccessibilityFeatures2' accessibility switch + /// should be set to false to enable the feature. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected bool ProcessF3Key(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && AccessibilityImprovements.Level2) + { + DataGridViewColumn dataGridViewColumn = Columns[this.ptCurrentCell.X]; + if (dataGridViewColumn != null && CanSort(dataGridViewColumn)) + { + ListSortDirection listSortDirection = this.SortedColumn == dataGridViewColumn && this.SortOrder == SortOrder.Ascending ? + ListSortDirection.Descending : ListSortDirection.Ascending; + + this.Sort(dataGridViewColumn, listSortDirection); + return true; + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessHomeKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + else + { + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessInsertKey(Keys keyData) + { + if (((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control || + ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == (Keys.Control | Keys.Shift) && (keyData & Keys.KeyCode) == Keys.C)) && + this.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable) + { + DataObject dataObject = GetClipboardContent(); + if (dataObject != null) + { + Clipboard.SetDataObject(dataObject); + return true; + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) + { + if (m.Msg == NativeMethods.WM_SYSKEYDOWN || m.Msg == NativeMethods.WM_KEYDOWN) + { + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (!this.IsCurrentCellInEditMode && + ColumnEditable(this.ptCurrentCell.X) && + !IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystroke || this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2)) + { + KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + if (ke.KeyCode != Keys.ProcessKey || (int) m.LParam != 0x01) // Changing IME context does not trigger editing mode + { + Type editControlType = dataGridViewCell.EditType; + Type editingCellInterface = null; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + } + + if ((editControlType != null || editingCellInterface == null) && + dataGridViewCell.KeyEntersEditMode(ke)) + { + // Cell wants to go to edit mode + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + if (BeginEditInternal(!(ke.KeyCode == Keys.F2 && ModifierKeys == 0 && this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2) /*selectAll*/)) + { + // Forward the key message to the editing control if any + if (this.editingControl != null) + { + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] = true; + + return true; + } + } + } + } + } + } + } + else if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] && + (m.Msg == NativeMethods.WM_SYSCHAR || m.Msg == NativeMethods.WM_CHAR || m.Msg == NativeMethods.WM_IME_CHAR)) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] = false; + if (this.editingControl != null) + { + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + return true; + } + } + return base.ProcessKeyEventArgs(ref m); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) + { + bool dataGridViewWantsInputKey; + KeyEventArgs ke = new KeyEventArgs((Keys)((int)m.WParam) | ModifierKeys); + + // Refactor the special keys into two parts. + // 1. Escape and Space exist in both WM_CHAR and WM_KEYDOWN, WM_KEYUP. + // 2. Other special keys do not exist in WM_CHAR message, and character code of WM_CHAR may have overlapped + // w/ some of the key code. (Like character code of lowcase "q" is 0x71, it's overlapped w/ Keys.F2). This + // may introduce problem when handling them. + if (m.Msg == NativeMethods.WM_CHAR) + { + switch (ke.KeyCode) + { + case Keys.Escape: + case Keys.Space: + dataGridViewWantsInputKey = true; + break; + + default: + dataGridViewWantsInputKey = false; + break; + } + } + else + { + switch (ke.KeyCode) + { + case Keys.Delete: + case Keys.Down: + case Keys.End: + case Keys.Enter: + case Keys.Escape: + case Keys.F2: + case Keys.F3: + case Keys.Home: + case Keys.Left: + case Keys.Next: + case Keys.Prior: + case Keys.Right: + case Keys.Space: + case Keys.Tab: + case Keys.Up: + dataGridViewWantsInputKey = true; + break; + + default: + dataGridViewWantsInputKey = false; + break; + } + } + + if (this.editingControl != null && (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN)) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey] = ((IDataGridViewEditingControl)this.editingControl).EditingControlWantsInputKey(ke.KeyData, dataGridViewWantsInputKey); + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey]) + { + return base.ProcessKeyPreview(ref m); + } + + if (dataGridViewWantsInputKey) + { + if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) + { + if (ProcessDataGridViewKey(ke)) + { + return true; + // Ctrl-Tab will be sent as a tab paired w/ a control on the KeyUp message + } + else + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey] = true; + } + } + else + { + return true; + } + } + return base.ProcessKeyPreview(ref m); + } + + private bool? ProcessColumnResize(Keys keyData, int step) + { + if (AccessibilityImprovements.Level2 && (keyData & Keys.Alt) == Keys.Alt && this.AllowUserToResizeColumns && this.ptCurrentCell.X != -1) + { + if (this.currentColSplitBar == -1) + { + DataGridViewColumn dataGridViewColumn = Columns[this.ptCurrentCell.X]; + if (dataGridViewColumn != null && dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + BeginKeyboardColumnResize(this.ptCurrentCell.X); + return true; + } + return false; + } + else + { + int x = this.currentColSplitBar + step; + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] && this.resizeClipRectangle.Contains(x, this.resizeClipRectangle.Top)) + { + MoveRowHeadersOrColumnResize(x); + return true; + } + return false; + } + } + + return null; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessLeftKey(Keys keyData) + { + if (this.RightToLeftInternal) + { + return ProcessRightKeyPrivate(keyData); + } + else + { + return ProcessLeftKeyPrivate(keyData); + } + } + + private bool ProcessLeftKeyPrivate(Keys keyData) + { + bool? resizeResult = this.ProcessColumnResize(keyData, -this.keyboardResizeStep); + if (resizeResult.HasValue) + { + return resizeResult.Value; + } + + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int previousVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + previousVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + //SelectCellUnorderedRange(previousVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, previousVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, previousVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, previousVisibleColumnIndex, true); + } + else + { + SelectColumnRange(previousVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(previousVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, previousVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, previousVisibleColumnIndex, true); + } + else + { + SelectColumnRange(previousVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(previousVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, previousVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + // Ctrl Left <==> Home + // Shift Ctrl Left <==> Shift Home + private bool ProcessLeftMost(bool shift, int firstVisibleColumnIndex, int firstVisibleRowIndex) + { + bool success; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + //SelectCellRange(firstVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.Y == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + else + { + //ClearSelection(); + //SelectCellRange(firstVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || + IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessNextKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + if (firstVisibleColumnIndex == -1) + { + return false; + } + int nextScreenVisibleRowIndexTmp, nextScreenVisibleRowIndex = -1, jumpRows = 0; + if (this.ptCurrentCell.Y == -1) + { + nextScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (nextScreenVisibleRowIndex == -1) + { + return false; + } + } + else + { + nextScreenVisibleRowIndex = this.ptCurrentCell.Y; + } + + if ((this.Rows.GetRowState(nextScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > 0) + { + int firstDisplayedScrollingRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(firstDisplayedScrollingRowIndex != -1); + if (!ScrollIntoView(this.ptCurrentCell.X == -1 ? firstVisibleColumnIndex : this.ptCurrentCell.X, + firstDisplayedScrollingRowIndex, true)) + { + return true; + } + jumpRows = this.Rows.GetRowCount(DataGridViewElementStates.Visible, + this.ptCurrentCell.Y, + firstDisplayedScrollingRowIndex)-1; + } + else + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + } + } + jumpRows += this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + + nextScreenVisibleRowIndexTmp = nextScreenVisibleRowIndex; + Debug.Assert(nextScreenVisibleRowIndexTmp != -1); + if (jumpRows == 0) + { + jumpRows = 1; + } + while (jumpRows > 0 && nextScreenVisibleRowIndexTmp != -1) + { + nextScreenVisibleRowIndexTmp = this.Rows.GetNextRow(nextScreenVisibleRowIndex, DataGridViewElementStates.Visible); + if (nextScreenVisibleRowIndexTmp != -1) + { + nextScreenVisibleRowIndex = nextScreenVisibleRowIndexTmp; + jumpRows--; + } + } + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + SetSelectedCellCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullRowSelect: + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullColumnSelect: + SetSelectedColumnCore(firstVisibleColumnIndex, true); + break; + } + success = ScrollIntoView(firstVisibleColumnIndex, nextScreenVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, nextScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + return true; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.Y == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextScreenVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false); + Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y < nextScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, nextScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(nextScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.ptAnchorCell.Y < nextScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, nextScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(nextScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextScreenVisibleRowIndex); + } + } + else + { + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true, false, false); + // Debug.Assert(success); + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessPriorKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + if (firstVisibleColumnIndex == -1) + { + return false; + } + int previousScreenVisibleRowIndexTmp, previousScreenVisibleRowIndex = -1; + if (this.ptCurrentCell.Y == -1) + { + previousScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (previousScreenVisibleRowIndex == -1) + { + return false; + } + } + else + { + previousScreenVisibleRowIndex = this.ptCurrentCell.Y; + } + + int jumpRows; + if ((this.Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + } + else + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + } + if (jumpRows == 0) + { + jumpRows = 1; + } + previousScreenVisibleRowIndexTmp = previousScreenVisibleRowIndex; + Debug.Assert(previousScreenVisibleRowIndexTmp != -1); + while (jumpRows > 0 && previousScreenVisibleRowIndexTmp != -1) + { + previousScreenVisibleRowIndexTmp = this.Rows.GetPreviousRow(previousScreenVisibleRowIndex, DataGridViewElementStates.Visible); + if (previousScreenVisibleRowIndexTmp != -1) + { + previousScreenVisibleRowIndex = previousScreenVisibleRowIndexTmp; + } + jumpRows--; + } + + if ((this.Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + // Make sure the first scrolling row is visible if any + int firstDisplayedScrollingRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (firstDisplayedScrollingRowIndex != -1 && + !ScrollIntoView(this.ptCurrentCell.X == -1 ? firstVisibleColumnIndex : this.ptCurrentCell.X, + firstDisplayedScrollingRowIndex, true)) + { + return true; + } + // Also, first visible frozen row should become current one - there is no reason to jump to another frozen row. + previousScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + Debug.Assert(previousScreenVisibleRowIndex != -1); + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + SetSelectedCellCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullRowSelect: + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullColumnSelect: + SetSelectedColumnCore(firstVisibleColumnIndex, true); + break; + } + success = ScrollIntoView(firstVisibleColumnIndex, previousScreenVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, previousScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true, false, false); + // Debug.Assert(success); + return true; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousScreenVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y < previousScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, previousScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(previousScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.ptAnchorCell.Y < previousScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, previousScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(previousScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousScreenVisibleRowIndex); + } + } + else + { + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessRightKey(Keys keyData) + { + if (this.RightToLeftInternal) + { + return ProcessLeftKeyPrivate(keyData); + } + else + { + return ProcessRightKeyPrivate(keyData); + } + } + + private bool ProcessRightKeyPrivate(Keys keyData) + { + bool? resizeResult = this.ProcessColumnResize(keyData, this.keyboardResizeStep); + if (resizeResult.HasValue) + { + return resizeResult.Value; + } + + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int lastVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int nextVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + nextVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, nextVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, nextVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, nextVisibleColumnIndex, true); + } + else + { + SelectColumnRange(nextVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(nextVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, nextVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, nextVisibleColumnIndex, true); + } + else + { + SelectColumnRange(nextVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + } + else + { + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + //SelectCellUnorderedRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, nextVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + // Ctrl Right <==> End + // Shift Ctrl Right <==> Shift End + private bool ProcessRightMost(bool shift, int lastVisibleColumnIndex, int firstVisibleRowIndex) + { + bool success; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.ptAnchorCell.X == -1) + { + return true; + } + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (this.Columns[this.ptCurrentCell.X].Selected) + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessSpaceKey(Keys keyData) + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && + this.ptCurrentCell.X != -1) + { + this.noSelectionChangeCount++; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + // Same as clicking the column header cell + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != this.ptCurrentCell.X) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + RemoveIndividuallySelectedCells(); + if (!this.Columns[this.ptCurrentCell.X].Selected) + { + Debug.Assert(!this.selectedBandIndexes.Contains(this.ptCurrentCell.X)); + SetSelectedColumnCore(this.ptCurrentCell.X, true); + } + return true; + } + else if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + // Same as clicking the row header cell + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != this.ptCurrentCell.Y) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + RemoveIndividuallySelectedCells(); + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(!this.selectedBandIndexes.Contains(this.ptCurrentCell.Y)); + SetSelectedRowCore(this.ptCurrentCell.Y, true); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + return false; + } + + /// + /// + /// + /// Gets a value indicating whether the Tab key should be processed. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected bool ProcessTabKey(Keys keyData) + { + if (this.StandardTab) + { + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell) + { + // Goto previous control + return false; + } + else + { + // Goto previous cell + return TabToPreviousCell(); + } + } + else + { + if (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell) + { + // Goto next control + return false; + } + else + { + // Goto next cell + return TabToNextCell(); + } + } + } + else + { + /* + if ((keyData & Keys.Shift) == Keys.Shift) + { + // Goto previous control + } + else + { + // Goto next control + } + */ + return false; + } + } + else + { + if ((keyData & Keys.Control) == Keys.Control) + { + /* + if ((keyData & Keys.Shift) == Keys.Shift) + { + // Goto previous control + } + else + { + // Goto next control + } + */ + return false; + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell) + { + // Goto previous control + return false; + } + else + { + // Goto previous cell + return TabToPreviousCell(); + } + } + else + { + if (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell) + { + // Goto next control + return false; + } + else + { + // Goto next cell + return TabToNextCell(); + } + } + } + } + } + + /// + /// + /// + /// Processes keys for dataGridView navigation. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers") + ] + protected virtual bool ProcessDataGridViewKey(KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Tab: + { + return ProcessTabKey(e.KeyData); + } + case Keys.Up: + { + return ProcessUpKey(e.KeyData); + } + case Keys.Down: + { + return ProcessDownKey(e.KeyData); + } + case Keys.Next: + { + return ProcessNextKey(e.KeyData); + } + case Keys.Prior: + { + return ProcessPriorKey(e.KeyData); + } + case Keys.Left: + { + return ProcessLeftKey(e.KeyData); + } + case Keys.Right: + { + return ProcessRightKey(e.KeyData); + } + case Keys.F2: + { + return ProcessF2Key(e.KeyData); + } + case Keys.F3: + { + return ProcessF3Key(e.KeyData); + } + case Keys.Home: + { + return ProcessHomeKey(e.KeyData); + } + case Keys.D0: + case Keys.NumPad0: + { + return ProcessZeroKey(e.KeyData); + } + case Keys.Delete: + { + return ProcessDeleteKey(e.KeyData); + } + case Keys.End: + { + return ProcessEndKey(e.KeyData); + } + case Keys.Enter: + { + return ProcessEnterKey(e.KeyData); + } + case Keys.Escape: + { + return ProcessEscapeKey(e.KeyData); + } + case Keys.A: + { + return ProcessAKey(e.KeyData); + } + case Keys.C: + case Keys.Insert: + { + return ProcessInsertKey(e.KeyData); + } + case Keys.Space: + { + return ProcessSpaceKey(e.KeyData); + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessUpKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int previousVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + previousVisibleRowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + //SelectCellRange(this.ptCurrentCell.X, firstVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + //SelectCellUnorderedRange(this.ptCurrentCell.X, previousVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y >= previousVisibleRowIndex) + { + SelectRowRange(previousVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + SelectRowRange(this.ptAnchorCell.Y, previousVisibleRowIndex, true); + } + } + else + { + SetSelectedRowCore(previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(previousVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.Y >= 0); + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptCurrentCell.X, firstVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y >= previousVisibleRowIndex) + { + SelectRowRange(previousVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + SelectRowRange(this.ptAnchorCell.Y, previousVisibleRowIndex, true); + } + } + SetSelectedRowCore(previousVisibleRowIndex, true); + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, previousVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.Y == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(previousVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessZeroKey(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && !this.IsCurrentCellInEditMode && ColumnEditable(this.ptCurrentCell.X)) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (!IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystroke || this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2) && + dataGridViewCell.EditType != null) + { + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + if (!BeginEditInternal(false /*selectAll*/)) + { + return false; + } + } + } + + if ((keyData & (Keys.Alt | Keys.Shift | Keys.Control)) == Keys.Control && + this.IsCurrentCellInEditMode) + { + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCurrentCell != null); + object nullValue = dataGridViewCurrentCell.GetInheritedStyle(null, this.ptCurrentCell.Y, false).NullValue; + if (nullValue == null || + (dataGridViewCurrentCell.FormattedValueType != null && dataGridViewCurrentCell.FormattedValueType.IsAssignableFrom(nullValue.GetType()))) + { + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).EditingControlFormattedValue = nullValue; + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = true; + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.EditingCellFormattedValue = nullValue; + dataGridViewEditingCell.EditingCellValueChanged = true; + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + NotifyCurrentCellDirty(true); + return true; + } + return false; + } + return false; + } + + private void PushAllowUserToAddRows() + { + if (this.AllowUserToAddRowsInternal) + { + if (this.Columns.Count > 0 && this.newRowIndex == -1) + { + AddNewRow(false); + } + } + else if (this.newRowIndex != -1) + { + // Delete the 'new' row + Debug.Assert(this.newRowIndex == this.Rows.Count - 1); + this.Rows.RemoveAtInternal(this.newRowIndex, false /*force*/); + } + } + + private bool PushFormattedValue(ref DataGridViewCell dataGridViewCurrentCell, object formattedValue, out Exception exception) + { + Debug.Assert(this.IsCurrentCellInEditMode); + + exception = null; + + DataGridViewCellStyle cellStyle = this.InheritedEditingCellStyle; + + DataGridViewCellParsingEventArgs dgvcpe = OnCellParsing(this.ptCurrentCell.Y, + this.ptCurrentCell.X, + formattedValue, + dataGridViewCurrentCell.ValueType, + cellStyle); + if (dgvcpe.ParsingApplied && + dgvcpe.Value != null && + dataGridViewCurrentCell.ValueType != null && + dataGridViewCurrentCell.ValueType.IsAssignableFrom(dgvcpe.Value.GetType())) + { + if (dataGridViewCurrentCell.RowIndex == -1) + { + dataGridViewCurrentCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unsharing the row before pushing the new value + } + return dataGridViewCurrentCell.SetValueInternal(this.ptCurrentCell.Y, dgvcpe.Value); + } + + object val; + try + { + val = dataGridViewCurrentCell.ParseFormattedValue(formattedValue, dgvcpe.InheritedCellStyle, null, null); + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + exception = e; + return false; + } + if (dataGridViewCurrentCell.RowIndex == -1) + { + dataGridViewCurrentCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unsharing the row before pushing the new value + } + return dataGridViewCurrentCell.SetValueInternal(this.ptCurrentCell.Y, val); + } + + private void RecordCellMouseClick(DataGridViewCellMouseEventArgs dgvcme) + { + Debug.Assert(dgvcme.Clicks == 1); + this.lastMouseClickInfo.button = dgvcme.Button; + this.lastMouseClickInfo.timeStamp = DateTime.Now.Ticks; + this.lastMouseClickInfo.x = dgvcme.X; + this.lastMouseClickInfo.y = dgvcme.Y; + this.lastMouseClickInfo.col = dgvcme.ColumnIndex; + this.lastMouseClickInfo.row = dgvcme.RowIndex; + } + + private void RefreshColumnsAndRows() + { + this.Rows.ClearInternal(false /*recreateNewRow*/); + RefreshColumns(); + RefreshRows(true /*scrollIntoView*/); + } + + private void RefreshColumns() + { + // if AutoGenerateColumns == true then: + // 1. remove the columns which were previously bound + // 2. add new columns based on data source + // + + bool startUpdateInternal = this.Visible; + if (startUpdateInternal) + { + BeginUpdateInternal(); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns] = true; + try + { + DataGridViewColumnCollection dataGridViewCols = this.Columns; + DataGridViewColumn[] boundColumns = null; + + if (this.dataConnection != null) + { + boundColumns = this.dataConnection.GetCollectionOfBoundDataGridViewColumns(); + } + + if (this.AutoGenerateColumns) + { + AutoGenerateDataBoundColumns(boundColumns); + } + else + { + for (int j = 0; j < dataGridViewCols.Count; j ++) + { + // when we refresh the columns, always clear whatever binding information we had on the columns + dataGridViewCols[j].IsDataBoundInternal = false; + dataGridViewCols[j].BoundColumnIndex = -1; + dataGridViewCols[j].BoundColumnConverter = null; + // set up the columns which have DataPropertyName set to something + if (this.DataSource != null && dataGridViewCols[j].DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(dataGridViewCols[j]); + } + } + } + + if (this.DataSource != null) + { + this.dataConnection.ApplySortingInformationFromBackEnd(); + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns] = false; + + if (startUpdateInternal) + { + EndUpdateInternal(false); + Invalidate(true); + } + } + } + + /// + // Returns true for success, and false if an OnDataError event was raised and cancelled the operation (e.Cancel = true). + public bool RefreshEdit() + { + if (this.ptCurrentCell.X != -1 && this.IsCurrentCellInEditMode) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCurrentCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + if (this.editingControl != null) + { + if (InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCurrentCell)) + { + if (((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = false; + this.IsCurrentCellDirtyInternal = false; + return true; + } + return false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + if (InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCurrentCell)) + { + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + dataGridViewEditingCell.EditingCellValueChanged = false; + this.IsCurrentCellDirtyInternal = false; + return true; + } + return false; + } + } + return true; + } + + private void RefreshRows(bool scrollIntoView) + { + bool startBeginUpdate = this.Visible; + if (startBeginUpdate) + { + BeginUpdateInternal(); + } + + try + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = true; + } + + // Clear existing rows + this.Rows.ClearInternal(true /*recreateNewRow*/); + + // Add a row for each object in the data source + if (this.dataConnection != null && this.Columns.Count > 0) + { + IList list = this.dataConnection.List; + if (list != null && list.Count > 0) + { + int rowsCount = list.Count; + bool oldDoNotChangePositionInTheCurrencyManager = this.dataConnection.DoNotChangePositionInTheCurrencyManager; + + // scroll into view when we have a non-empty list + // and the layout is not dirty and we are not in a sort operation. + bool matchPositionInCurrencyManagerAfterRecreatingRows = !this.layout.dirty && !this.InSortOperation; + + if (matchPositionInCurrencyManagerAfterRecreatingRows) + { + // Prevent the data connection from changing position in the currency manager. + // But only if the data grid view will set the position in the currency manager after recreating the rows collection. + this.dataConnection.DoNotChangePositionInTheCurrencyManager = true; + } + try + { + this.Rows.AddInternal(this.RowTemplateClone); + Debug.Assert(list.Count == rowsCount); + if (rowsCount > 1) + { + this.Rows.AddCopiesInternal(0, rowsCount-1); + } + // Add selected Columns back in selectedBandIndexes + foreach(DataGridViewColumn column in Columns) + { + if (column.Selected && (!this.selectedBandIndexes.Contains(column.Index))) + { + this.selectedBandIndexes.Add(column.Index); + } + } + } + finally + { + this.dataConnection.DoNotChangePositionInTheCurrencyManager = oldDoNotChangePositionInTheCurrencyManager; + } + + if (matchPositionInCurrencyManagerAfterRecreatingRows) + { + dataConnection.MatchCurrencyManagerPosition(scrollIntoView, true /*clearSelection*/); + } + } + } + } + finally + { + if (startBeginUpdate) + { + EndUpdateInternal(false); + Invalidate(true); + } + } + } + + private void RealeaseMouse() + { + Cursor.ClipInternal = Rectangle.Empty; + this.CaptureInternal = false; + } + + private void RemoveIndividualReadOnlyCellsInColumn(int columnIndex) + { + int cellIndex = 0; + while (cellIndex < this.individualReadOnlyCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualReadOnlyCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + + private void RemoveIndividualReadOnlyCellsInRow(int rowIndex) + { + int cellIndex = 0; + while (cellIndex < this.individualReadOnlyCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualReadOnlyCells[cellIndex]; + if (dataGridViewCell.RowIndex == rowIndex) + { + SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, rowIndex, false); + } + else + { + cellIndex++; + } + } + } + + private void RemoveIndividuallySelectedCells() + { + Debug.Assert(this.noSelectionChangeCount > 0); + bool switchedToBulkPaint = false; + if (this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.individualSelectedCells.Count > 0) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void RemoveIndividuallySelectedCells(int columnIndexException, int rowIndexException) + { + Debug.Assert(this.noSelectionChangeCount > 0); + bool switchedToBulkPaint = false; + if (this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.individualSelectedCells.Count > 0) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + if (dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + while (this.individualSelectedCells.Count > 1) + { + dataGridViewCell = this.individualSelectedCells[1]; + Debug.Assert(dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException); + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + break; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void RemoveIndividuallySelectedCellsInColumn(int columnIndex) + { + Debug.Assert(this.noSelectionChangeCount > 0); + int cellIndex = 0; + int cellCountInColumn = 0; + bool switchToBulkOperation = false; + DataGridViewCell dataGridViewCell; + + while (cellIndex < this.individualSelectedCells.Count) + { + dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + cellCountInColumn++; + if (cellCountInColumn > DATAGRIDVIEW_bulkPaintThreshold) + { + switchToBulkOperation = true; + break; + } + } + else + { + cellIndex++; + } + } + + if (switchToBulkOperation) + { + this.inBulkPaintCount++; + try + { + while (cellIndex < this.individualSelectedCells.Count) + { + dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + finally + { + ExitBulkPaint(columnIndex, -1); + } + } + } + + private void RemoveIndividuallySelectedCellsInRow(int rowIndex) + { + Debug.Assert(this.noSelectionChangeCount > 0); + // Since there are typically not many columns in a row, we don't try to switch into a bulk operation + // as we do in RemoveIndividuallySelectedCellsInColumn. + int cellIndex = 0; + while (cellIndex < this.individualSelectedCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.RowIndex == rowIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + + // required by the Designer + private void ResetBackgroundColor() + { + this.BackgroundColor = DefaultBackgroundBrush.Color; + } + + // required by the Designer + private void ResetGridColor() + { + this.GridColor = DefaultGridColor; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetText() + { + base.ResetText(); + } + + /// + /// Re-initializes all tracking related state. + /// + private void ResetTrackingState() + { + if (this.IsKeyboardOperationActive()) + { + return; + } + if (this.horizScrollTimer != null && this.horizScrollTimer.Enabled) + { + this.horizScrollTimer.Enabled = false; + } + if (this.vertScrollTimer != null && this.vertScrollTimer.Enabled) + { + this.vertScrollTimer.Enabled = false; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_mouseOperationMask] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = false; + this.trackColumn = -1; + this.trackRow = -1; + this.ptMouseDownCell.X = -2; + this.ptMouseDownCell.Y = -2; + + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + if (this.lastHeaderShadow != -1) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumnEdge = -1; + this.lastHeaderShadow = -1; + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + RealeaseMouse(); + } + + // Re-initializes all state that is related to tracking keyboard operations + private void ResetKeyboardTrackingState() + { + if (this.IsMouseOperationActive()) + { + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_keyboardOperationMask] = false; + this.trackColumn = -1; + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsMouseOperationActive() + { + return (this.dataGridViewOper.Data & DATAGRIDVIEWOPER_mouseOperationMask) != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsKeyboardOperationActive() + { + return (this.dataGridViewOper.Data & DATAGRIDVIEWOPER_keyboardOperationMask) != 0; + } + + /// + /// Re-initializes all UI related state. + /// + internal void ResetUIState(bool useRowShortcut, bool computeVisibleRows) + { + PerformLayoutPrivate(useRowShortcut, computeVisibleRows, true /*invalidInAdjustFillingColumns*/, !useRowShortcut /*repositionEditingControl*/); + if (!useRowShortcut) + { + Invalidate(); + InvalidateScrollBars(); + } + } + + private void RestoreRowsCachedThickness() + { + // Switch to batch operation + this.inBulkPaintCount++; + this.inBulkLayoutCount++; + try + { + // Only height of visible rows are restored since invisible rows are not autosized. + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(rowIndex, out height, out minimumHeight); + if (height != dataGridViewRow.CachedThickness && + !OnRowHeightInfoPushed(rowIndex, dataGridViewRow.CachedThickness, minimumHeight)) + { + dataGridViewRow.ThicknessInternal = dataGridViewRow.CachedThickness; + } + } + } + finally + { + ExitBulkLayout(true /*invalidInAdjustFillingColumns*/); + ExitBulkPaint(-1, -1); + } + } + + // we need to access the GetRowState, otherwise we would unshare the row + private bool RowIsResizable(int rowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.ResizableSet) == DataGridViewElementStates.ResizableSet) + { + return (rowState & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + } + else + { + return this.AllowUserToResizeRows; + } + } + + private bool RowNeedsDisplayedState(int rowIndex, int lastDisplayedFrozenRowIndex, int lastDisplayedScrollingRowIndex) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(rowIndex < this.Rows.Count); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + return false; + } + + if ((rowState & DataGridViewElementStates.Frozen) != 0) + { + return rowIndex <= lastDisplayedFrozenRowIndex; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow != -1 && + rowIndex >= this.displayedBandsInfo.FirstDisplayedScrollingRow && + rowIndex <= lastDisplayedScrollingRowIndex) + { + return true; + } + return false; + } + + private void ScrollBar_MouseEnter(object sender, System.EventArgs e) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected]) + { + OnMouseEnter(EventArgs.Empty); + } + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + private void ScrollBar_MouseLeave(object sender, System.EventArgs e) + { + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + Point ptMouse = PointToClient(Control.MousePosition); + if (!this.ClientRectangle.Contains(ptMouse)) + { + OnMouseLeave(EventArgs.Empty); + } + } + + private bool ScrollColumnIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol != -1 && + !this.Columns[columnIndex].Frozen && + (columnIndex != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.negOffset > 0)) + { + int columnsToScroll; + if (this.Columns.DisplayInOrder(columnIndex, this.displayedBandsInfo.FirstDisplayedScrollingCol)) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + columnsToScroll = this.Columns.GetColumnCount(DataGridViewElementStates.Visible, columnIndex, this.displayedBandsInfo.FirstDisplayedScrollingCol); + if (this.negOffset > 0) + { + columnsToScroll++; + } + ScrollColumns(-columnsToScroll); + } + else if (columnIndex == this.displayedBandsInfo.FirstDisplayedScrollingCol && this.negOffset > 0) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + ScrollColumns(-1); + } + else if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol == -1 || + (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol != columnIndex && + this.Columns.DisplayInOrder(this.displayedBandsInfo.LastTotallyDisplayedScrollingCol, columnIndex))) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + columnsToScroll = 0; + int firstDisplayedScrollingColumn = this.displayedBandsInfo.FirstDisplayedScrollingCol; + int xColumnRightEdge; + + if (this.RightToLeftInternal) + { + xColumnRightEdge = GetColumnXFromIndex(columnIndex) - this.Columns[columnIndex].Width; + while (xColumnRightEdge < this.layout.Data.X && this.Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex)) + { + xColumnRightEdge += this.Columns[firstDisplayedScrollingColumn].Width; + if (firstDisplayedScrollingColumn == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + xColumnRightEdge -= this.negOffset; + } + columnsToScroll++; + if (xColumnRightEdge < this.layout.Data.X) + { + firstDisplayedScrollingColumn = this.Columns.GetNextColumn(this.Columns[firstDisplayedScrollingColumn], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + } + } + } + else + { + xColumnRightEdge = GetColumnXFromIndex(columnIndex) + this.Columns[columnIndex].Width; + while (xColumnRightEdge > this.layout.Data.Right && this.Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex)) + { + xColumnRightEdge -= this.Columns[firstDisplayedScrollingColumn].Width; + if (firstDisplayedScrollingColumn == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + xColumnRightEdge += this.negOffset; + } + columnsToScroll++; + if (xColumnRightEdge > this.layout.Data.Right) + { + firstDisplayedScrollingColumn = this.Columns.GetNextColumn(this.Columns[firstDisplayedScrollingColumn], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + } + } + } + if (columnsToScroll != 0) + { + ScrollColumns(columnsToScroll); + } + } + } + return true; + } + + private void ScrollColumns(int columns) + { + DataGridViewColumn newFirstVisibleScrollingCol = null; + DataGridViewColumn dataGridViewColumnTmp; + int colCount = 0; + //ScrollEventType scrollEventType; + if (columns > 0) + { + //scrollEventType = columns > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement; + if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + { + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol]; + while (colCount < columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + colCount++; + } + + if (dataGridViewColumnTmp == null) + { + // no more column to display on the right of the last totally seen column + return; + } + } + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + colCount = 0; + while (colCount < columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + colCount++; + } + newFirstVisibleScrollingCol = dataGridViewColumnTmp; + } + + if (columns < 0) + { + //scrollEventType = columns < -1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + if (this.negOffset > 0) + { + colCount++; + } + while (colCount < -columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + colCount++; + } + newFirstVisibleScrollingCol = dataGridViewColumnTmp; + if (newFirstVisibleScrollingCol == null) + { + if (this.negOffset == 0) + { + // no more column to display on the left of the first seen column + FlushDisplayedChanged(); + return; + } + else + { + newFirstVisibleScrollingCol = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + } + } + } + + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + + this.HorizontalOffset = newColOffset; + } + + private bool ScrollIntoView(int columnIndex, int rowIndex, bool forCurrentCellChange) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= -1 && this.displayedBandsInfo.FirstDisplayedScrollingCol < this.Columns.Count); + Debug.Assert(this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= -1 && this.displayedBandsInfo.LastTotallyDisplayedScrollingCol < this.Columns.Count); + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= -1 && this.displayedBandsInfo.FirstDisplayedScrollingRow < this.Rows.Count); + Debug.Assert(this.Columns[columnIndex].Visible); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + + bool committed = false; + if (this.ptCurrentCell.X >= 0 && + (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex)) + { + if (!CommitEditForOperation(columnIndex, rowIndex, forCurrentCellChange)) + { + return false; + } + committed = true; + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + } + + //scroll horizontally + if (!ScrollColumnIntoView(columnIndex, rowIndex, committed, forCurrentCellChange)) + { + return false; + } + + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + + //scroll vertically + return ScrollRowIntoView(columnIndex, rowIndex, committed, forCurrentCellChange); + } + + private void ScrollRectangles(NativeMethods.RECT[] rects, int change) + { + if (rects != null) + { + if (MouseButtons != MouseButtons.None) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = true; + } + + NativeMethods.RECT scroll; + for (int r = 0; r < rects.Length; r++) + { + scroll = rects[r]; + SafeNativeMethods.ScrollWindow(new HandleRef(this, this.Handle), + change, + 0, + ref scroll, + ref scroll); + } + } + } + + private bool ScrollRowIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0) + { + int rowsToScroll; + if (rowIndex < this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + rowsToScroll = this.Rows.GetRowCount(DataGridViewElementStates.Visible, rowIndex, this.displayedBandsInfo.FirstDisplayedScrollingRow); + ScrollRowsByCount(-rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement); + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0 && + rowIndex > this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + rowsToScroll = 0; + int firstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int yRowBottomEdge = GetRowYFromIndex(rowIndex) + this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + while (yRowBottomEdge > this.layout.Data.Bottom && rowIndex > firstDisplayedScrollingRow) + { + yRowBottomEdge -= this.Rows.SharedRow(firstDisplayedScrollingRow).GetHeight(firstDisplayedScrollingRow); + rowsToScroll++; + if (yRowBottomEdge > this.layout.Data.Bottom) + { + firstDisplayedScrollingRow = this.Rows.GetNextRow(firstDisplayedScrollingRow, DataGridViewElementStates.Visible); + Debug.Assert(firstDisplayedScrollingRow != -1); + } + } + if (rowsToScroll != 0) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + ScrollRowsByCount(rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement); + } + } + } + return true; + } + + private void ScrollRows(int rowCount, int deltaY, ScrollEventType scrollEventType) + { + bool invalidateTopOfRowHeaders = false; + Debug.Assert(rowCount != 0); + Debug.Assert(deltaY != 0); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + this.verticalOffset -= deltaY; + if (this.vertScrollBar.Enabled) + { + this.vertScrollBar.Value = this.verticalOffset; + } + ClearRegionCache(); + int frozenRowsThickness = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Rectangle rowsRect = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + rowsRect = Rectangle.Union(rowsRect, this.layout.RowHeaders); + if (this.SingleHorizontalBorderAdded) + { + rowsRect.Y++; + rowsRect.Height--; + invalidateTopOfRowHeaders = true; + } + } + else if (this.SingleVerticalBorderAdded) + { + Debug.Assert(rowsRect.X > 0); + rowsRect.X--; + rowsRect.Width++; + } + rowsRect.Y += frozenRowsThickness; + rowsRect.Height -= frozenRowsThickness; + Debug.Assert(rowsRect.Height >= 0); + + if (this.editingControl != null && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow > -1); + PositionEditingControl(true /*setLocation*/, false /*setSize*/, false /*setFocus*/); + } + + if (MouseButtons != MouseButtons.None) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = true; + } + + // The mouse probably is not over the same cell after the scroll. + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + NativeMethods.RECT scrollArea = NativeMethods.RECT.FromXYWH(rowsRect.X, rowsRect.Y, rowsRect.Width, rowsRect.Height); + SafeNativeMethods.ScrollWindow(new HandleRef(this, this.Handle), 0, deltaY, ref scrollArea, ref scrollArea); + if (invalidateTopOfRowHeaders) + { + rowsRect.X = this.layout.Inside.X; + rowsRect.Y = this.layout.Inside.Y; + rowsRect.Width = this.layout.RowHeaders.Width; + rowsRect.Height = 1; + Invalidate(rowsRect); + } + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll]) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstVisibleScrollingRow = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(firstVisibleScrollingRow != -1); + int newScrolledOffRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible, firstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + Debug.Assert(newScrolledOffRowCount >= rowCount); + OnScroll(scrollEventType, newScrolledOffRowCount - rowCount, newScrolledOffRowCount, ScrollOrientation.VerticalScroll); + } + } + + private void ScrollRowsByCount(int rows, ScrollEventType scrollEventType) + { + Debug.Assert(rows != 0); + Debug.Assert((rows > 0 && (scrollEventType == ScrollEventType.SmallIncrement || scrollEventType == ScrollEventType.LargeIncrement)) || + (rows < 0 && (scrollEventType == ScrollEventType.SmallDecrement || scrollEventType == ScrollEventType.LargeDecrement))); + + int deltaY = 0; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int newFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rows > 0) + { + for (int rowCount = rows; rowCount > 0; rowCount--) + { + deltaY -= this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + newFirstVisibleScrollingRow = this.Rows.GetNextRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible); + + Debug.Assert(newFirstVisibleScrollingRow != -1); + } + if (newFirstVisibleScrollingRow != -1) + { + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow. + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow + ComputeVisibleRows(); + // Compute the actual deltaY given the new this.displayedBandsInfo.FirstDisplayedScrollingRow + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow) + { + deltaY = -this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + rows = this.Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + else + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow == oldFirstVisibleScrollingRow); + rows = 0; + } + } + } + else + { + for (int rowCount = rows; rowCount < 0; rowCount++) + { + newFirstVisibleScrollingRow = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (newFirstVisibleScrollingRow != -1) + { + deltaY += this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + } + } + if (newFirstVisibleScrollingRow != -1) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + ComputeVisibleRows(); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow == newFirstVisibleScrollingRow); + } + } + + if (newFirstVisibleScrollingRow != -1 && rows != 0) + { + ScrollRows(rows, deltaY, scrollEventType); + } + + FlushDisplayedChanged(); + } + + private void ScrollRowsByHeight(int height) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int deltaY, scrollHeight = 0; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int newFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (height > 0) + { + deltaY = this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + while (deltaY <= height) + { + newFirstVisibleScrollingRow = this.Rows.GetNextRow(newFirstVisibleScrollingRow, DataGridViewElementStates.Visible); + if (newFirstVisibleScrollingRow == -1) + { + throw new InvalidOperationException(); // Occurs in case of VSWhidbey 533407 + } + else + { + deltaY += this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + } + } + } + else + { + newFirstVisibleScrollingRow = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + Debug.Assert(newFirstVisibleScrollingRow != -1); + deltaY = -this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + while (deltaY >= height) + { + int scrollingRowTmp = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (scrollingRowTmp != -1) + { + deltaY -= this.Rows.SharedRow(scrollingRowTmp).GetHeight(scrollingRowTmp); + if (deltaY >= height) + { + newFirstVisibleScrollingRow = scrollingRowTmp; + } + } + else + { + break; + } + } + } + + // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow. + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow + ComputeVisibleRows(); + ScrollEventType scrollEventType = ScrollEventType.EndScroll; + int rowCount = 0; + // Compute the scrollHeight given the new this.displayedBandsInfo.FirstDisplayedScrollingRow + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow) + { + scrollHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + rowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + scrollEventType = rowCount > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow < oldFirstVisibleScrollingRow) + { + scrollHeight = -this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow); + rowCount = -this.Rows.GetRowCount(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow); + scrollEventType = rowCount < -1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement; + } + if (scrollHeight != 0) + { + ScrollRows(rowCount, -scrollHeight, scrollEventType); + } + + FlushDisplayedChanged(); + } + + /// + // Does not seem to be a valid fxcop violation report. Contacting fxcop team to double-check. + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] + public void SelectAll() + { + if (!this.MultiSelect) + { + return; + } + this.inBulkPaintCount++; + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + try + { + DataGridViewRow dataGridViewRow = null; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // Bonjour the scalability issues! We select each cell, one at the time. + int maxColumnIndex = this.Columns.Count; + int rowIndex = 0, maxRowIndex = this.Rows.Count; + while (rowIndex < maxRowIndex) + { + dataGridViewRow = this.Rows[rowIndex]; //unsharing each row! + int columnIndex = 0; + while (columnIndex < maxColumnIndex) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + columnIndex++; + } + rowIndex++; + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int rowIndex = 0, maxRowIndex = this.Rows.Count; + while (rowIndex < maxRowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Selected) == 0) + { + SetSelectedRowCore(rowIndex, true); + } + rowIndex++; + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int columnIndex = 0, maxColumnIndex = this.Columns.Count; + while (columnIndex < maxColumnIndex) + { + if (!this.Columns[columnIndex].Selected) + { + SetSelectedColumnCore(columnIndex, true); + } + columnIndex++; + } + break; + } + } + } + finally + { + this.noDimensionChangeCount--; + this.noSelectionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + Debug.Assert(this.noSelectionChangeCount >= 0); + ExitBulkPaint(-1, -1); + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null && firstVisibleRowIndex != -1) + { + // This is the only place in the code outside of SetCurrentCellAddressCore where this.ptAnchorCell gets changed. + // There is no way in SetCurrentCellAddressCore to just change the anchor cell. + this.ptAnchorCell.X = dataGridViewColumn.Index; + this.ptAnchorCell.Y = firstVisibleRowIndex; + } + else + { + this.ptAnchorCell.X = -1; + this.ptAnchorCell.Y = -1; + } + + if (this.noSelectionChangeCount == 0) + { + FlushSelectionChanged(); + } + } + + private DataGridViewCell SelectedCell(int index) + { + Debug.Assert(index >= 0); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int selectedBand = 0, selectedBands = this.selectedBandIndexes.Count; + while (selectedBand < selectedBands && index >= 0) + { + if (index >= this.Rows.Count) + { + index -= this.Rows.Count; + selectedBand++; + } + else + { + int columnIndex = this.selectedBandIndexes[selectedBand]; + return this.Rows.SharedRow(index).Cells[columnIndex]; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect && + index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int selectedBand = 0, selectedBands = this.selectedBandIndexes.Count; + while (selectedBand < selectedBands && index >= 0) + { + if (index >= this.Columns.Count) + { + index -= this.Columns.Count; + selectedBand++; + } + else + { + int rowIndex = this.selectedBandIndexes[selectedBand]; + return this.Rows.SharedRow(rowIndex).Cells[index]; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect && + index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + } + return null; + } + + private void SetColumnHeadersHeightInternal(int columnHeadersHeight, bool invalidInAdjustFillingColumns) + { + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.ColumnHeadersHeight)) + { + Debug.Assert(this.columnHeadersHeight != columnHeadersHeight); + Debug.Assert(columnHeadersHeight >= minimumColumnHeadersHeight); + Debug.Assert(columnHeadersHeight <= maxHeadersThickness); + this.columnHeadersHeight = columnHeadersHeight; + if (this.AutoSize) + { + InvalidateInside(); + } + else + { + if (this.layout.ColumnHeadersVisible) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, true /*repositionEditingControl*/); + InvalidateInside(); + } + } + OnColumnHeadersHeightChanged(EventArgs.Empty); + } + } + + /// + protected virtual bool SetCurrentCellAddressCore(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick) + { + if (columnIndex < -1 || + (columnIndex >= 0 && rowIndex == -1) || + columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || + (columnIndex == -1 && rowIndex >= 0) || + rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex > -1 && + rowIndex > -1 && + !IsSharedCellVisible(this.Rows.SharedRow(rowIndex).Cells[columnIndex], rowIndex)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrentCellCannotBeInvisible)); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] && // Allow the code to be re-entrant only as a result of + (this.dataConnection == null || !this.dataConnection.ProcessingListChangedEvent)) // underlying data changing. + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_SetCurrentCellAddressCoreNotReentrant)); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] = true; + try + { + DataGridViewCell dataGridViewCellTmp = null; + + if (columnIndex > -1) + { + Debug.Assert(rowIndex >= 0 && + columnIndex < this.Columns.Count && + rowIndex < this.Rows.Count); + + if (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex) + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + this.ptCurrentCell.X = columnIndex; + this.ptCurrentCell.Y = rowIndex; + if (this.cachedEditingControl != null) + { + this.editingControl = this.cachedEditingControl; + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = rowIndex; + this.cachedEditingControl = null; + PositionEditingControl(true, true, false); + } + OnCurrentCellChanged(EventArgs.Empty); + return true; + } + + DataGridViewCell currentCell; + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (oldCurrentCellX >= 0) + { + currentCell = this.CurrentCellInternal; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never /*validateCell*/, + validateCurrentCell /*fireCellLeave*/, + false /*fireCellEnter*/, + validateCurrentCell && oldCurrentCellY != rowIndex /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell = this.Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX]; + if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick)) + { + currentCell = this.Rows[oldCurrentCellY].Cells[oldCurrentCellX]; // unsharing the current row + } + currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick); + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + if (oldCurrentCellY != rowIndex) + { + if (validateCurrentCell) + { + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + Debug.Assert(oldCurrentCellX == this.ptCurrentCell.X); + Debug.Assert(oldCurrentCellY == this.ptCurrentCell.Y); + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + } + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = false; + + try + { + if (oldCurrentCellY != rowIndex) + { + //Tentatively commenting out for bug #321924 + //this.ptCurrentCell.X = -1; + //this.ptCurrentCell.Y = -1; + //OnCurrentCellChanged(EventArgs.Empty); + if (!IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + OnRowEnter(ref dataGridViewCellTmp, columnIndex, rowIndex, true /*canCreateNewRow*/, false /*validationFailureOccurred*/); + } + } + + // Force repainting of the current and previous collumns` header cells to update highlighting + if (oldCurrentCellX != columnIndex && + this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && + AccessibilityImprovements.Level2) + { + if (oldCurrentCellX >= 0) + { + InvalidateCellPrivate(oldCurrentCellX, -1); + } + InvalidateCellPrivate(columnIndex, -1); + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell]) + { + // + // The rows collection was cleared while processing OnRowEnter. + // If the new list is too small for rowIndex, fail the call to SetCurrentCellAddressCore. + // + if (rowIndex >= this.Rows.Count) + { + return false; + } + // rowIndex = Math.Min(rowIndex, this.Rows.GetRowCount(DataGridViewElementStates.Visible) - 1); + } + + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + + this.ptCurrentCell.X = columnIndex; + this.ptCurrentCell.Y = rowIndex; + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = rowIndex; + } + OnCurrentCellChanged(EventArgs.Empty); + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = columnIndex; + this.ptAnchorCell.Y = rowIndex; + } + + #if FALSE + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell]) + { + // DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell bit will be cleared while executing the + // "finally" block. + return true; + } + #endif + + currentCell = this.CurrentCellInternal; + if (currentCell.EnterUnsharesRowInternal(rowIndex, throughMouseClick)) + { + currentCell = this.Rows[rowIndex].Cells[columnIndex]; // unsharing the row + } + currentCell.OnEnterInternal(rowIndex, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (oldCurrentCellX >= 0) + { + Debug.Assert(oldCurrentCellY >= 0); + if (oldCurrentCellX < this.Columns.Count && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY); + } + if (oldCurrentCellY != this.ptCurrentCell.Y && this.RowHeadersVisible && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(-1, oldCurrentCellY); + } + } + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (this.RowHeadersVisible && oldCurrentCellY != this.ptCurrentCell.Y) + { + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + if (this.Focused && + this.ptCurrentCell.X != -1 && + !this.IsCurrentCellInEditMode && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] && // don't edit if we're in the process of leaving the grid + !this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] && // don't edit if the rows collection changed + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && currentCell.EditType == null))) + { + BeginEditInternal(true /*selectAll*/); + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = false; + } + + // Accessibility + if (this.ptCurrentCell.X != -1) + { + AccessibilityNotifyCurrentCellChanged(new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y)); + } + } + else + { + // this.ptCurrentCell.X == columnIndex && this.ptCurrentCell.Y == rowIndex + // Not trying to change the current cell, but may need to edit it. + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = columnIndex; + this.ptAnchorCell.Y = rowIndex; + } + if (this.Focused && + (!this.IsCurrentCellInEditMode && (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)))) + { + BeginEditInternal(true /*selectAll*/); + } + else + { + CorrectFocus(false /*onlyIfGridHasFocus*/); + } + } + } + else + { + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (oldCurrentCellX >= 0 && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + DataGridViewCell currentCell = this.CurrentCellInternal; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never, + validateCurrentCell /*fireCellLeave*/, + false /*fireCellEnter*/, + validateCurrentCell /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell = this.Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX]; + if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick)) + { + currentCell = this.Rows[oldCurrentCellY].Cells[oldCurrentCellX]; // unsharing the current row + } + currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick); + } + if (validateCurrentCell) + { + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + Debug.Assert(oldCurrentCellX == this.ptCurrentCell.X); + Debug.Assert(oldCurrentCellY == this.ptCurrentCell.Y); + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + } + if (this.ptCurrentCell.X != -1) + { + this.ptCurrentCell.X = -1; + this.ptCurrentCell.Y = -1; + OnCurrentCellChanged(EventArgs.Empty); + } + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = -1; + this.ptAnchorCell.Y = -1; + } + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell]) + { + if (this.editingControl != null) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl] = false; + } + else + { + this.cachedEditingControl = this.editingControl; + } + this.editingControl = null; + } + } + else if (oldCurrentCellX >= 0 && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + Debug.Assert(oldCurrentCellY >= 0); + if (oldCurrentCellX < this.Columns.Count && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY); + } + if (this.RowHeadersVisible && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(-1, oldCurrentCellY); + } + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] = false; + } + return true; + } + + internal void SetCurrentCellAddressCoreInternal(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick) + { + SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick); + } + + private void SelectCellRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select) + { + Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0); + Debug.Assert((this.Columns[columnIndexFrom]).DisplayIndex <= (this.Columns[columnIndexTo]).DisplayIndex); + Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0); + Debug.Assert(rowIndexFrom <= rowIndexTo); + Debug.Assert(this.noSelectionChangeCount > 0); + + bool switchedToBulkPaint = false; + if (rowIndexTo - rowIndexFrom > DATAGRIDVIEW_bulkPaintThreshold) + { + // Switch to batch operation + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + // Selection and deselection are done in reverse order for perf. reasons. + if (select) + { + int columnIndex = columnIndexFrom; + do + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + } + if (columnIndex != columnIndexTo) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexTo); + + if (columnIndexFrom != columnIndexTo) + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + } + } + } + else + { + int columnIndex = columnIndexTo; + do + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + if (columnIndex != columnIndexFrom) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexFrom); + + if (columnIndexFrom != columnIndexTo) + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void SelectCellUnorderedRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select) + { + Debug.Assert(this.noSelectionChangeCount > 0); + int columnIndexFromTmp, rowIndexFromTmp, columnIndexToTmp, rowIndexToTmp; + + if (this.Columns.DisplayInOrder(columnIndexFrom, columnIndexTo)) + { + columnIndexFromTmp = columnIndexFrom; + columnIndexToTmp = columnIndexTo; + } + else + { + columnIndexFromTmp = columnIndexTo; + columnIndexToTmp = columnIndexFrom; + } + + if (rowIndexFrom < rowIndexTo) + { + rowIndexFromTmp = rowIndexFrom; + rowIndexToTmp = rowIndexTo; + } + else + { + rowIndexFromTmp = rowIndexTo; + rowIndexToTmp = rowIndexFrom; + } + + SelectCellRange(columnIndexFromTmp, rowIndexFromTmp, columnIndexToTmp, rowIndexToTmp, select); + } + + private void SelectColumnRange(int columnIndexFrom, int columnIndexTo, bool select) + { + Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0); + Debug.Assert((this.Columns[columnIndexFrom]).DisplayIndex <= (this.Columns[columnIndexTo]).DisplayIndex); + Debug.Assert(this.noSelectionChangeCount > 0); + + int columnIndex = columnIndexFrom; + do + { + if (select) + { + if (!this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + SetSelectedColumnCore(columnIndex, false); + } + if (columnIndex != columnIndexTo) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexTo); + + if (columnIndexFrom != columnIndexTo) + { + if (select) + { + if (!this.selectedBandIndexes.Contains(columnIndexTo)) + { + SetSelectedColumnCore(columnIndexTo, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndexTo)); + SetSelectedColumnCore(columnIndexTo, false); + } + } + } + + private void SelectRowRange(int rowIndexFrom, int rowIndexTo, bool select) + { + Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0); + Debug.Assert(rowIndexFrom <= rowIndexTo); + Debug.Assert(this.noSelectionChangeCount > 0); + + bool switchedToBulkPaint = false; + if (rowIndexTo - rowIndexFrom > DATAGRIDVIEW_bulkPaintThreshold) + { + // Switch to batch operation + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + // Selecting and deselecting rows in reverse order for perf. reasons + if (select) + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(!this.selectedBandIndexes.Contains(rowIndex)); + SetSelectedRowCore(rowIndex, true); + } + } + } + else + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + SetSelectedRowCore(rowIndex, false); + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private bool SetAndSelectCurrentCellAddress(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick, + bool clearSelection, + bool forceCurrentCellSelection) + { + if (!SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick)) + { + return false; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + if (clearSelection) + { + ClearSelection(columnIndex, rowIndex, true /*selectException*/); // we always select the new current cell when clearSelection is true + } + else + { + if (forceCurrentCellSelection) + { + SetSelectedElementCore(columnIndex, rowIndex, true); + } + else + { + if (this.MultiSelect && (this.individualSelectedCells.Count + this.selectedBandIndexes.Count) > 1) + { + return true; // Do not discard the multi-selection + } + if (this.individualSelectedCells.Count == 1) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + if (dataGridViewCell.ColumnIndex != columnIndex || dataGridViewCell.RowIndex != rowIndex) + { + return true; + } + } + else if (this.selectedBandIndexes.Count == 1) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (this.selectedBandIndexes.HeadInt != columnIndex) + { + return true; // Do not change a single selection that does not match the new current cell + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (this.selectedBandIndexes.HeadInt != rowIndex) + { + return true; // Do not change a single selection that does not match the new current cell + } + break; + } + } + } + SetSelectedElementCore(columnIndex, rowIndex, true); + } + } + return true; + } + + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) + { + if ((specified & BoundsSpecified.Width) == BoundsSpecified.Width && width > upperSize) { + throw new ArgumentOutOfRangeException("width", width, SR.GetString(SR.DataGridView_SizeTooLarge, (upperSize).ToString(CultureInfo.CurrentCulture))); + } + if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height && height > upperSize) { + throw new ArgumentOutOfRangeException("height", height, SR.GetString(SR.DataGridView_SizeTooLarge, (upperSize).ToString(CultureInfo.CurrentCulture))); + } + base.SetBoundsCore(x, y, width, height, specified); + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + internal void SetReadOnlyCellCore(int columnIndex, int rowIndex, bool readOnly) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0 && + columnIndex < this.Columns.Count && + rowIndex < this.Rows.Count); + + // cell's readonly state changes + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (IsSharedCellReadOnly(dataGridViewRow.Cells[columnIndex], rowIndex) != readOnly) + { + DataGridViewCell dataGridViewCell = this.Rows[rowIndex].Cells[columnIndex]; + if (readOnly) + { + if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && + !this.Columns[columnIndex].ReadOnly) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + dataGridViewCell.ReadOnlyInternal = true; + } + } + else + { + if (this.individualReadOnlyCells.Contains(dataGridViewCell)) + { + this.individualReadOnlyCells.Remove(dataGridViewCell); + } + else + { + DataGridViewCell dataGridViewCellTmp; + if (this.Columns[columnIndex].ReadOnly) + { + this.Columns[columnIndex].ReadOnlyInternal = false; + // Perf Issue: this unshares all rows! + for (int row = 0; row < rowIndex; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + for (int row = rowIndex+1; row < this.Rows.Count; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + } + if ((rowState & DataGridViewElementStates.ReadOnly) != 0) + { + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false); + for (int column = 0; column < columnIndex; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + for (int column = columnIndex+1; column < this.Columns.Count; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + } + } + if (dataGridViewCell.ReadOnly) + { + dataGridViewCell.ReadOnlyInternal = false; + } + } + } + } + + internal void SetReadOnlyColumnCore(int columnIndex, bool readOnly) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.Columns[columnIndex].ReadOnly != readOnly) + { + // ReadOnly state of entire column changes + if (readOnly) + { + // column is made read-only + // remove individual read-only cells of this column + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = true; + RemoveIndividualReadOnlyCellsInColumn(columnIndex); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = false; + } + this.Columns[columnIndex].ReadOnlyInternal = true; + } + else + { + // column is made read-write + this.Columns[columnIndex].ReadOnlyInternal = false; + } + } + else if (!readOnly) + { + // remove any potentially individual read-only cells in the column + RemoveIndividualReadOnlyCellsInColumn(columnIndex); + } + } + + internal void SetReadOnlyRowCore(int rowIndex, bool readOnly) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (((rowState & DataGridViewElementStates.ReadOnly) != 0) != readOnly) + { + // ReadOnly state of entire row changes + if (readOnly) + { + // row is made read-only + // first remove individual read-only cells of this row + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = true; + RemoveIndividualReadOnlyCellsInRow(rowIndex); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = false; + } + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, true); + } + else + { + // row is made read-write + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false); + } + } + else if (!readOnly) + { + // remove any potentially individual read-only cells in the row + RemoveIndividualReadOnlyCellsInRow(rowIndex); + } + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + protected virtual void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + // cell selection changes + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (IsSharedCellSelected(dataGridViewRow.Cells[columnIndex], rowIndex) != selected) + { + DataGridViewCell dataGridViewCell = this.Rows[rowIndex].Cells[columnIndex]; + if (selected) + { + if ((rowState & DataGridViewElementStates.Selected) == 0 && + !this.Columns[columnIndex].Selected) + { + this.individualSelectedCells.Add(dataGridViewCell); + dataGridViewCell.SelectedInternal = true; + } + } + else + { + if ((dataGridViewCell.State & DataGridViewElementStates.Selected) != 0) + { + Debug.Assert(this.individualSelectedCells.Contains(dataGridViewCell)); + this.individualSelectedCells.Remove(dataGridViewCell); + } + else + { + DataGridViewCell dataGridViewCellTmp; + bool switchedToBulkPaint = false; + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + if (this.Rows.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + this.selectedBandIndexes.Remove(columnIndex); + this.Columns[columnIndex].SelectedInternal = false; + // Perf Issue: this unshares all rows! + for (int row = 0; row < rowIndex; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + for (int row = rowIndex+1; row < this.Rows.Count; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(columnIndex, -1); + } + } + } + else if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + if (this.Columns.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Remove(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false); + for (int column = 0; column < columnIndex; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + for (int column = columnIndex+1; column < this.Columns.Count; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, rowIndex); + } + } + } + } + if (dataGridViewCell.Selected) + { + dataGridViewCell.SelectedInternal = false; + } + } + } + } + + internal void SetSelectedCellCoreInternal(int columnIndex, int rowIndex, bool selected) + { + if (selected && !this.MultiSelect) + { + if (!this.Columns[columnIndex].Visible || + (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrentCellCannotBeInvisible)); + } + + if (!ScrollIntoView(columnIndex, rowIndex, true)) + { + return; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (selected && !this.MultiSelect) + { + Debug.Assert(this.individualSelectedCells.Count <= 1); + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(columnIndex, rowIndex, selected); + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != columnIndex) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + if (!this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, true); + } + } + else + { + if (this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + } + SetSelectedCellCore(columnIndex, rowIndex, true); + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != rowIndex) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(rowIndex, true); + } + } + else + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(rowIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + } + SetSelectedCellCore(columnIndex, rowIndex, true); + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (selected && !this.MultiSelect) + { + bool success = SetCurrentCellAddressCore(columnIndex, rowIndex, true, false, true); + Debug.Assert(success); + } + } + + /// + protected virtual void SetSelectedColumnCore(int columnIndex, bool selected) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + this.noSelectionChangeCount++; + try + { + if (this.Columns[columnIndex].Selected != selected) + { + // selection of entire column changes + if (selected) + { + // column is selected + // remove individually selected cells of this column + RemoveIndividuallySelectedCellsInColumn(columnIndex); + this.Columns[columnIndex].SelectedInternal = true; + Debug.Assert(!this.selectedBandIndexes.Contains(columnIndex)); + this.selectedBandIndexes.Add(columnIndex); + } + else + { + // column is deselected + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + this.Columns[columnIndex].SelectedInternal = false; + this.selectedBandIndexes.Remove(columnIndex); + } + } + else if (!selected) + { + // remove any potentially individually selected cells in the column + RemoveIndividuallySelectedCellsInColumn(columnIndex); + } + } + finally + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + this.NoSelectionChangeCount--; + } + } + + internal void SetSelectedColumnCoreInternal(int columnIndex, bool selected) + { + this.noSelectionChangeCount++; + try + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + int columnIndexSelected = this.selectedBandIndexes.HeadInt; + if (columnIndexSelected != columnIndex) + { + SetSelectedColumnCore(columnIndexSelected, false); + } + } + } + SetSelectedColumnCore(columnIndex, selected); + } + finally + { + this.NoSelectionChangeCount--; + } + } + + private void SetSelectedElementCore(int columnIndex, int rowIndex, bool selected) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + break; + } + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (columnIndex == -1) + { + SetSelectedRowCore(rowIndex, selected); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + } + break; + } + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (rowIndex == -1) + { + SetSelectedColumnCore(columnIndex, selected); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + { + SetSelectedRowCore(rowIndex, selected); + break; + } + case DataGridViewSelectionMode.FullColumnSelect: + { + SetSelectedColumnCore(columnIndex, selected); + break; + } + } + } + + /// + protected virtual void SetSelectedRowCore(int rowIndex, bool selected) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + this.noSelectionChangeCount++; + try + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (((rowState & DataGridViewElementStates.Selected) != 0) != selected) + { + // selection of entire row changes + if (selected) + { + // row is selected + // first remove individually selected cells of this row + RemoveIndividuallySelectedCellsInRow(rowIndex); + Debug.Assert(!this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Add(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, true); + } + else + { + // row is deselected + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Remove(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false); + } + } + else if (!selected) + { + // remove any potentially individually selected cells in the row + RemoveIndividuallySelectedCellsInRow(rowIndex); + } + } + finally + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + this.NoSelectionChangeCount--; + } + } + + internal void SetSelectedRowCoreInternal(int rowIndex, bool selected) + { + this.noSelectionChangeCount++; + try + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + int rowIndexSelected = this.selectedBandIndexes.HeadInt; + if (rowIndexSelected != rowIndex) + { + SetSelectedRowCore(rowIndexSelected, false); + } + } + } + SetSelectedRowCore(rowIndex, selected); + } + finally + { + this.NoSelectionChangeCount--; + } + } + + private bool ShouldSerializeAlternatingRowsDefaultCellStyle() + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + return !this.AlternatingRowsDefaultCellStyle.Equals(defaultStyle); + } + + private bool ShouldSerializeColumnHeadersDefaultCellStyle() + { + return !this.ColumnHeadersDefaultCellStyle.Equals(this.DefaultColumnHeadersDefaultCellStyle); + } + + private bool ShouldSerializeDefaultCellStyle() + { + return !this.DefaultCellStyle.Equals(this.DefaultDefaultCellStyle); + } + + private bool ShouldSerializeRowHeadersDefaultCellStyle() + { + return !this.RowHeadersDefaultCellStyle.Equals(this.DefaultRowHeadersDefaultCellStyle); + } + + private bool ShouldSerializeRowsDefaultCellStyle() + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + return !this.RowsDefaultCellStyle.Equals(defaultStyle); + } + + /// + public virtual void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction) + { + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + + if (direction != ListSortDirection.Ascending && direction != ListSortDirection.Descending) + { + throw new InvalidEnumArgumentException("direction", (int)direction, typeof(ListSortDirection)); + } + + if (dataGridViewColumn.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + if (this.VirtualMode && !dataGridViewColumn.IsDataBound) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_OperationDisabledInVirtualMode)); + } + + SortInternal(null, dataGridViewColumn, direction); + } + + /// + public virtual void Sort(IComparer comparer) + { + if (comparer == null) + { + throw new ArgumentNullException("comparer"); + } + + if (this.VirtualMode) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_OperationDisabledInVirtualMode)); + } + + // can't sort a data bound dataGridView control using a comparer + if (this.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotUseAComparerToSortDataGridViewWhenDataBound)); + } + + SortInternal(comparer, null, ListSortDirection.Ascending); + } + + private void SortDataBoundDataGridView_PerformCheck(DataGridViewColumn dataGridViewColumn) + { + IBindingList ibl = this.dataConnection.List as IBindingList; + if (ibl == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotSortDataBoundDataGridViewBoundToNonIBindingList)); + } + + if (!ibl.SupportsSorting) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_IBindingListNeedsToSupportSorting)); + } + + if (!dataGridViewColumn.IsDataBound) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnNeedsToBeDataBoundWhenSortingDataBoundDataGridView), "dataGridViewColumn"); + } + } + + private void SortInternal(IComparer comparer, DataGridViewColumn dataGridViewColumn, ListSortDirection direction) + { + Debug.Assert(!(comparer != null && this.DataSource != null)); + Debug.Assert(direction == ListSortDirection.Ascending || direction == ListSortDirection.Descending); + + // Exit editing mode if needed + this.ptCurrentCellCache.X = this.ptCurrentCell.X; + this.ptCurrentCellCache.Y = this.ptCurrentCell.Y; + this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] = true; + try + { + if (!SetCurrentCellAddressCore(-1, -1, true, true, false)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + // 'finally' below resets this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] to false + return; + } + + int firstDisplayedScrollingRowCache = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int visibleFrozenRows = this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (visibleFrozenRows > 0 && this.DataSource == null) + { + int rowVFIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(rowVFIndex != -1); + this.Rows.SetRowState(rowVFIndex, DataGridViewElementStates.Frozen, false); + Debug.Assert(0 == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + } + + if (this.sortedColumn != null && + this.sortedColumn.SortMode == DataGridViewColumnSortMode.Automatic && + this.sortedColumn.HasHeaderCell) + { + this.sortedColumn.HeaderCell.SortGlyphDirection = SortOrder.None; + } + + if (comparer == null) + { + Debug.Assert(dataGridViewColumn != null); + this.sortedColumn = dataGridViewColumn; + this.sortOrder = (direction == ListSortDirection.Ascending) ? SortOrder.Ascending : SortOrder.Descending; + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = this.sortOrder; + } + } + else + { + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + } + + if (this.DataSource == null) + { + // Displayed rows may end up all spread out in the final layout. + // So we simply reset their displayed state before the sort. + UpdateRowsDisplayedState(false /*displayed*/); + this.Rows.Sort(comparer, direction == ListSortDirection.Ascending); + } + else + { + SortDataBoundDataGridView_PerformCheck(dataGridViewColumn); + // the check passed, do the sorting + this.dataConnection.Sort(dataGridViewColumn, direction); + } + + if (this.ptCurrentCellCache.X != -1) + { + if (!IsInnerCellOutOfBounds(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y)) + { + SetAndSelectCurrentCellAddress(this.ptCurrentCellCache.X, + this.ptCurrentCellCache.Y, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + } + } + if (visibleFrozenRows > 0) + { + int rowVIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowVIndex != -1); + while (visibleFrozenRows > 1) + { + rowVIndex = this.Rows.GetNextRow(rowVIndex, DataGridViewElementStates.Visible); + Debug.Assert(rowVIndex != -1); + visibleFrozenRows--; + } + this.Rows.SetRowState(rowVIndex, DataGridViewElementStates.Frozen, true); + } + this.displayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRowCache; + } + finally + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inSort]); + this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] = false; + } + // Same effect as changing a top level cell style + OnGlobalAutoSize(); + if (this.DataSource == null) + { + // VSWhidbey 500898. Ensure that the Displayed states get set properly because they were wiped out by UpdateRowsDisplayedState above. + this.displayedBandsInfo.EnsureDirtyState(); + } + ResetUIState(false /*useRowShortcut*/, false /*computeVisibleRows*/); + OnSorted(EventArgs.Empty); + + if (AccessibilityImprovements.Level3) + { + // RS4 narrator does not catch this event even though event is indeed raised. + AccessibilityNotifyClients(AccessibleEvents.Reorder, NativeMethods.OBJID_CLIENT, 0); + } + } + + internal void SwapSortedRows(int rowIndex1, int rowIndex2) + { + Debug.Assert(rowIndex1 != this.newRowIndex); + Debug.Assert(rowIndex2 != this.newRowIndex); + if (rowIndex1 == rowIndex2) + { + return; + } + + // Follow the move of the old current cell + if (rowIndex1 == this.ptCurrentCellCache.Y) + { + this.ptCurrentCellCache.Y = rowIndex2; + } + else if (rowIndex2 == this.ptCurrentCellCache.Y) + { + this.ptCurrentCellCache.Y = rowIndex1; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int row1Selected = this.selectedBandIndexes.IndexOf(rowIndex1); + int row2Selected = this.selectedBandIndexes.IndexOf(rowIndex2); + if (row1Selected != -1 && row2Selected == -1) + { + this.selectedBandIndexes[row1Selected] = rowIndex2; + } + else if (row1Selected == -1 && row2Selected != -1) + { + this.selectedBandIndexes[row2Selected] = rowIndex1; + } + if (this.selectedBandSnapshotIndexes != null) + { + row1Selected = this.selectedBandSnapshotIndexes.IndexOf(rowIndex1); + row2Selected = this.selectedBandSnapshotIndexes.IndexOf(rowIndex2); + if (row1Selected != -1 && row2Selected == -1) + { + this.selectedBandSnapshotIndexes[row1Selected] = rowIndex2; + } + else if (row1Selected == -1 && row2Selected != -1) + { + this.selectedBandSnapshotIndexes[row2Selected] = rowIndex1; + } + } + break; + } + } + + private void DataGridViewHScrolled(object sender, ScrollEventArgs se) + { + if (!this.Enabled) + { + return; + } + + /* VS Whidbey bug 262445 - no more commit on scroll + if (this.ptCurrentCell.X >= 0) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + se.NewValue = this.HorizontalOffset; + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + this.horizScrollBar.Invalidate(); + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange-/, false /*forCurrentRowChange-/)) + { + se.NewValue = this.HorizontalOffset; + if (se.Type == ScrollEventType.SmallIncrement || se.Type == ScrollEventType.SmallDecrement) + { + // Workaround for a pbm where the scrollbar ends up in a bad state after a validation failure dialog is displayed. + this.horizScrollBar.RecreateScrollBarHandle(); + } + else + { + this.horizScrollBar.Invalidate(); + } + return; + } + else + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = false; + } + } + */ + + if (se.Type == ScrollEventType.SmallIncrement || + se.Type == ScrollEventType.SmallDecrement) + { + int dCols = (se.Type == ScrollEventType.SmallIncrement) ? 1 : -1; + ScrollColumns(dCols); + se.NewValue = this.HorizontalOffset; + } + else if (se.Type != ScrollEventType.EndScroll) + { + this.HorizontalOffset = se.NewValue; + } + } + + private void DataGridViewVScrolled(object sender, ScrollEventArgs se) + { + if (!this.Enabled) + { + return; + } + + /* VS Whidbey bug 262445 - no more commit on scroll + if (this.ptCurrentCell.X >= 0) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + se.NewValue = this.VerticalOffset; + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + this.vertScrollBar.Invalidate(); + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange-/, false /*forCurrentRowChange-/)) + { + se.NewValue = this.VerticalOffset; + if (se.Type == ScrollEventType.SmallIncrement || se.Type == ScrollEventType.SmallDecrement) + { + // Workaround for a pbm where the scrollbar ends up in a bad state after a validation failure dialog is displayed. + this.vertScrollBar.RecreateScrollBarHandle(); + } + else + { + this.vertScrollBar.Invalidate(); + } + return; + } + else + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = false; + } + } + */ + + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + switch (se.Type) + { + case ScrollEventType.SmallIncrement: + { + // Making sure that when the last visible scrolling row is taller than the data area, it does not get scrolled off screen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + if (this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + ScrollRowsByCount(1, ScrollEventType.SmallIncrement); + } + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.SmallDecrement: + { + if (this.vertScrollBar.Value != this.vertScrollBar.Minimum) + { + ScrollRowsByCount(-1, ScrollEventType.SmallDecrement); + } + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.LargeIncrement: + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstDisplayedScrollingRowHeight = this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow); + this.VerticalOffset += Math.Max(firstDisplayedScrollingRowHeight, this.vertScrollBar.LargeChange); + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.LargeDecrement: + { + this.VerticalOffset -= this.vertScrollBar.LargeChange; + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.ThumbTrack: + case ScrollEventType.First: + case ScrollEventType.Last: + { + if (se.NewValue >= this.vertScrollBar.Maximum - this.vertScrollBar.LargeChange) + { + // Need to display the last scrolling row + this.VerticalOffset = this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight); + } + else + { + this.VerticalOffset = se.NewValue; + } + break; + } + } + } + + private bool TabToNextCell() + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int nextVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + nextVisibleColumnIndex = dataGridViewColumn.Index; + } + } + int nextVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + nextVisibleRowIndex = this.Rows.GetNextRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + int targetRowIndex = -1, targetColumnIndex = -1; + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + } + else + { + if (nextVisibleColumnIndex == -1) + { + targetRowIndex = (nextVisibleRowIndex == -1) ? firstVisibleRowIndex : nextVisibleRowIndex; + targetColumnIndex = firstVisibleColumnIndex; + } + else + { + targetRowIndex = this.ptCurrentCell.Y; + targetColumnIndex = nextVisibleColumnIndex; + } + if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true)) + { + return true; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(targetColumnIndex, targetRowIndex, true); + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (IsColumnOutOfBounds(targetColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(targetColumnIndex, true); + } + break; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + if (targetRowIndex != this.ptCurrentCell.Y || this.MultiSelect) + { + if (IsRowOutOfBounds(targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(targetRowIndex, true); + } + } + break; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X == -1) + { + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + } + + private bool TabToPreviousCell() + { + bool success; + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + int previousVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + previousVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + int previousVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + previousVisibleRowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + int lastVisibleColumnIndex = dataGridViewColumn.Index; + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + Debug.Assert(lastVisibleRowIndex != -1); + + int targetRowIndex = -1, targetColumnIndex = -1; + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + } + else + { + if (previousVisibleColumnIndex == -1) + { + targetRowIndex = (previousVisibleRowIndex == -1) ? lastVisibleRowIndex : previousVisibleRowIndex; + targetColumnIndex = lastVisibleColumnIndex; + } + else + { + targetRowIndex = this.ptCurrentCell.Y; + targetColumnIndex = previousVisibleColumnIndex; + } + if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true)) + { + return true; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(targetColumnIndex, targetRowIndex, true); + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (IsColumnOutOfBounds(targetColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(targetColumnIndex, true); + } + break; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + if (targetRowIndex != this.ptCurrentCell.Y || this.MultiSelect) + { + if (IsRowOutOfBounds(targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(targetRowIndex, true); + } + } + break; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X == -1) + { + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + + return true; + } + + private void UnwireEditingControlEvents() + { + Debug.Assert(this.editingPanel != null); + this.editingPanel.Click -= new System.EventHandler(EditingControls_Click); + this.editingPanel.DoubleClick -= new System.EventHandler(EditingControls_DoubleClick); + this.editingPanel.MouseClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingPanel.MouseDoubleClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingPanel.MouseDown -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingPanel.MouseEnter -= new System.EventHandler(EditingControls_MouseEnter); + this.editingPanel.MouseLeave -= new System.EventHandler(EditingControls_MouseLeave); + this.editingPanel.MouseMove -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingPanel.MouseUp -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + + Debug.Assert(this.editingControl != null); + this.editingControl.Click -= new System.EventHandler(EditingControls_Click); + this.editingControl.DoubleClick -= new System.EventHandler(EditingControls_DoubleClick); + this.editingControl.MouseClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingControl.MouseDoubleClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingControl.MouseDown -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingControl.MouseEnter -= new System.EventHandler(EditingControls_MouseEnter); + this.editingControl.MouseLeave -= new System.EventHandler(EditingControls_MouseLeave); + this.editingControl.MouseMove -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingControl.MouseUp -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + } + + private void UnwireScrollBarsEvents() + { + if (this.horizScrollBar != null) + { + this.horizScrollBar.MouseEnter -= new System.EventHandler(ScrollBar_MouseEnter); + this.horizScrollBar.MouseLeave -= new System.EventHandler(ScrollBar_MouseLeave); + } + if (this.vertScrollBar != null) + { + this.vertScrollBar.MouseEnter -= new System.EventHandler(ScrollBar_MouseEnter); + this.vertScrollBar.MouseLeave -= new System.EventHandler(ScrollBar_MouseLeave); + } + } + + /// + public void UpdateCellErrorText(int columnIndex, int rowIndex) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated) + { + InvalidateCellPrivate(columnIndex, rowIndex); + } + } + + /// + public void UpdateCellValue(int columnIndex, int rowIndex) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated) + { + OnCellCommonChange(columnIndex, rowIndex); + } + } + + private void UpdateColumnsDisplayedState(bool displayed) + { + // Make sure all displayed frozen columns have their Displayed state set to this.Visible + DataGridViewColumn dataGridViewColumnTmp; + int numDisplayedFrozenCols = this.displayedBandsInfo.NumDisplayedFrozenCols; + if (numDisplayedFrozenCols > 0) + { + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (dataGridViewColumnTmp.Displayed != displayed) + { + dataGridViewColumnTmp.DisplayedInternal = displayed; + Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp)); + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + numDisplayedFrozenCols--; + } + } + + // Make sure all displayed scrolling columns have the Displayed state set to this.Visible + int columnIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (columnIndexTmp != -1) + { + int numDisplayedScrollingCols = this.displayedBandsInfo.NumDisplayedScrollingCols; + Debug.Assert(numDisplayedScrollingCols > 0); + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (numDisplayedScrollingCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (dataGridViewColumnTmp.Displayed != displayed) + { + dataGridViewColumnTmp.DisplayedInternal = displayed; + Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp)); + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + numDisplayedScrollingCols--; + } + } + } + + /// + public void UpdateRowErrorText(int rowIndex) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated && this.layout.RowHeadersVisible) + { + InvalidateCellPrivate(-1, rowIndex); + } + } + + /// + public void UpdateRowErrorText(int rowIndexStart, int rowIndexEnd) + { + if (rowIndexStart < 0 || rowIndexStart >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexStart"); + } + if (rowIndexEnd < 0 || rowIndexEnd >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexEnd"); + } + if (rowIndexEnd < rowIndexStart) + { + throw new ArgumentOutOfRangeException("rowIndexEnd"); + } + if (this.IsHandleCreated && this.layout.RowHeadersVisible) + { + Rectangle rectUpper = GetCellAdjustedDisplayRectangle(-1, rowIndexStart, true); + Rectangle rectLower = GetCellAdjustedDisplayRectangle(-1, rowIndexEnd, true); + if (rectUpper.IsEmpty || rectLower.IsEmpty) + { + if (!rectUpper.IsEmpty || !rectLower.IsEmpty) + { + Invalidate(this.layout.RowHeaders); + } + } + else + { + Invalidate(Rectangle.Union(rectUpper, rectLower)); + } + } + } + + /// + public void UpdateRowHeightInfo(int rowIndex, bool updateToEnd) + { + UpdateRowHeightInfoPrivate(rowIndex, updateToEnd, true /*invalidInAdjustFillingColumns*/); + } + + private void UpdateRowHeightInfoPrivate(int rowIndex, bool updateToEnd, bool invalidInAdjustFillingColumns) + { + if ((updateToEnd && rowIndex < 0) || (!updateToEnd && rowIndex < -1) || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + this.Rows.InvalidateCachedRowsHeights(); + + bool rowVisible = (rowIndex >= 0 && (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + + // don't do any layout logic if the handled was not created already + if (this.IsHandleCreated && (rowIndex == -1 || rowVisible)) + { + if (updateToEnd) + { + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + + Rectangle bottomArea = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + bottomArea = Rectangle.Union(bottomArea, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + bottomArea.X--; + } + bottomArea.Width++; + } + + if (!rowVisible) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + if (rowIndex != -1) + { + int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : this.displayedBandsInfo.FirstDisplayedScrollingRow); + bottomArea.Height -= bottomArea.Y - topEdge; + bottomArea.Y = topEdge; + Invalidate(bottomArea); + } + + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + else + { + if (rowIndex == -1) + { + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + Invalidate(); + } + else + { + Debug.Assert(rowVisible); + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + if (this.inBulkLayoutCount == 0) + { + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + } + + if (this.inBulkPaintCount == 0) + { + Rectangle bottomArea = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + bottomArea = Rectangle.Union(bottomArea, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + bottomArea.X--; + } + bottomArea.Width++; + } + int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : this.displayedBandsInfo.FirstDisplayedScrollingRow); + bottomArea.Height -= bottomArea.Y - topEdge; + bottomArea.Y = topEdge; + Invalidate(bottomArea); + } + } + + if (this.editingControl != null) + { + PositionEditingControl(rowIndex == -1 || this.ptCurrentCell.Y != rowIndex, true, false); + } + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + } + + private void UpdateRowsDisplayedState(bool displayed) + { + // Make sure all displayed frozen rows have their Displayed state set to 'displayed' + int rowIndexTmp, numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + if (numDisplayedFrozenRows > 0) + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if (((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + } + + // Make sure all displayed scrolling rows have their Displayed state set to 'displayed' + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp > -1) + { + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if (((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + } + } + + private void UpdateMouseEnteredCell(HitTestInfo hti, MouseEventArgs e) + { + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo htiToUse; + if (hti != null) + { + htiToUse = hti; + } + else + { + htiToUse = HitTest(ptMouse.X, ptMouse.Y); + } + + if (htiToUse.Type != DataGridViewHitTestType.None && + htiToUse.Type != DataGridViewHitTestType.HorizontalScrollBar && + htiToUse.Type != DataGridViewHitTestType.VerticalScrollBar) + { + if (this.ptMouseEnteredCell.X != htiToUse.col || this.ptMouseEnteredCell.Y != htiToUse.row) + { + DataGridViewCellEventArgs dgvce; + if (this.ptMouseEnteredCell.X >= -1 && + this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && + this.ptMouseEnteredCell.Y < this.Rows.Count) + { + dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + dgvce = new DataGridViewCellEventArgs(htiToUse.col, htiToUse.row); + OnCellMouseEnter(dgvce); + } + if (e != null) + { + int mouseX = e.X - htiToUse.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((htiToUse.col == -1) ? this.RowHeadersWidth : this.Columns[htiToUse.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(htiToUse.col, htiToUse.row, mouseX, e.Y - htiToUse.RowY, e); + OnCellMouseMove(dgvcme); + } + } + else if (this.ptMouseEnteredCell.X != -2) + { + if (this.ptMouseEnteredCell.X >= -1 && + this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && + this.ptMouseEnteredCell.Y < this.Rows.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + else + { + this.ptMouseEnteredCell.X = this.ptMouseEnteredCell.Y = -2; + } + } + } + + private void UpdateSelectedCellsBlock(int anchorColumnIndex, ref int oldEdgeColumnIndex, int newEdgeColumnIndex, + int anchorRowIndex, ref int oldEdgeRowIndex, int newEdgeRowIndex) + { + Debug.Assert(anchorColumnIndex >= 0); + Debug.Assert(anchorRowIndex >= 0); + Debug.Assert(newEdgeColumnIndex >= 0); + Debug.Assert(newEdgeRowIndex >= 0); + Debug.Assert(this.noSelectionChangeCount > 0); + if ((this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) && + this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex)) || + (this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) && + this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) || + (anchorRowIndex < oldEdgeRowIndex && newEdgeRowIndex < anchorRowIndex) || + (oldEdgeRowIndex < anchorRowIndex && anchorRowIndex < newEdgeRowIndex)) + { + // deselecting all selected block + SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + // and reselecting new block + SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, newEdgeColumnIndex, newEdgeRowIndex, true); + oldEdgeColumnIndex = newEdgeColumnIndex; + oldEdgeRowIndex = newEdgeRowIndex; + return; + } + + if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) || anchorColumnIndex == oldEdgeColumnIndex) && + oldEdgeRowIndex == newEdgeRowIndex) + { + // h1 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + if (anchorRowIndex <= newEdgeRowIndex) + { + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, newEdgeColumnIndex, newEdgeRowIndex, true); + } + else + { + // newEdgeRowIndex < anchorRowIndex + SelectCellRange(dataGridViewColumn.Index, newEdgeRowIndex, newEdgeColumnIndex, anchorRowIndex, true); + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + (this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) || oldEdgeColumnIndex == anchorColumnIndex) && + oldEdgeRowIndex == newEdgeRowIndex) + { + // h2 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + if (anchorRowIndex <= newEdgeRowIndex) + { + SelectCellRange(newEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, newEdgeRowIndex, true); + } + else + { + // newEdgeRowIndex < anchorRowIndex + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, true); + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex && + anchorRowIndex <= oldEdgeRowIndex && + newEdgeColumnIndex == oldEdgeColumnIndex) + { + // h3 + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex) + { + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + else + { + // newEdgeColumnIndex before anchorColumnIndex + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex && + oldEdgeRowIndex <= anchorRowIndex && + newEdgeColumnIndex == oldEdgeColumnIndex) + { + // h4 + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex) + { + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + // newEdgeColumnIndex before anchorColumnIndex + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + !this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) && + newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex) + { + // h5 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, newEdgeColumnIndex, oldEdgeRowIndex, true); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + newEdgeRowIndex < oldEdgeRowIndex && oldEdgeRowIndex <= anchorRowIndex) + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + // h6 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, newEdgeColumnIndex, anchorRowIndex, true); + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) + { + if (anchorRowIndex == oldEdgeRowIndex) + { + // g2 + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, anchorColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, anchorColumnIndex, anchorRowIndex, true); + } + else + { + // b4 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(oldEdgeRowIndex < anchorRowIndex); + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + } + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + newEdgeRowIndex < oldEdgeRowIndex && anchorRowIndex >= oldEdgeRowIndex) + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + // h7 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(newEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, true); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) + { + // a4 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(oldEdgeRowIndex <= anchorRowIndex); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + // g8 + SelectCellRange(anchorColumnIndex, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, anchorColumnIndex, anchorRowIndex, true); + } + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + !this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) && + newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex) + { + // h8 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(newEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, true); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (newEdgeRowIndex == oldEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // a1 + Debug.Assert(dataGridViewColumn != null); + if (oldEdgeRowIndex > anchorRowIndex) + { + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + } + else + { + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (oldEdgeRowIndex > anchorRowIndex) + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)) + { + if (anchorRowIndex <= newEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + if (!this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex)) + { + // a2 + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + oldEdgeRowIndex, + false); + } + } + else + { + // d3 + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(oldEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + SelectCellRange(newEdgeColumnIndex, + anchorRowIndex, + dataGridViewColumn.Index, + newEdgeRowIndex, + true); + } + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (oldEdgeRowIndex < anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) && + newEdgeRowIndex <= anchorRowIndex) + { + // a3 + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c3 + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(oldEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + dataGridViewColumn.Index, + anchorRowIndex, + true); + } + } + } + } + else + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // a5 + Debug.Assert(oldEdgeRowIndex >= anchorRowIndex); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(anchorRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (newEdgeRowIndex == oldEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex)) + { + // b1 + Debug.Assert(dataGridViewColumn != null); + if (oldEdgeRowIndex > anchorRowIndex) + { + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (oldEdgeRowIndex > anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex)) && + newEdgeRowIndex >= anchorRowIndex) + { + // b2 + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + } + else + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + if (newEdgeRowIndex >= anchorRowIndex) + { + // d2 + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + oldEdgeColumnIndex, + oldEdgeRowIndex, + false); + SelectCellRange(dataGridViewColumn.Index, + anchorRowIndex, + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (oldEdgeRowIndex < anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) && + newEdgeRowIndex <= anchorRowIndex) + { + // b3 + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c2 + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + oldEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + SelectCellRange(dataGridViewColumn.Index, + newEdgeRowIndex, + newEdgeColumnIndex, + anchorRowIndex, + true); + } + } + } + } + else + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // b5 + Debug.Assert(oldEdgeRowIndex >= anchorRowIndex); + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (newEdgeColumnIndex == oldEdgeColumnIndex) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c1 + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + oldEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (newEdgeColumnIndex == oldEdgeColumnIndex) + { + if (newEdgeRowIndex >= anchorRowIndex) + { + // d1 + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + oldEdgeColumnIndex, + oldEdgeRowIndex, + false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + } + } + } + } + oldEdgeColumnIndex = newEdgeColumnIndex; + oldEdgeRowIndex = newEdgeRowIndex; + } + + private void VertScrollTimer_Tick(object sender, System.EventArgs e) + { + BeginInvoke(new MethodInvoker(VertScrollTimerHandler)); + } + + private void VertScrollTimerHandler() + { + // Why would the vertical scroll timer be enabled when vertical selection is not occurring + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]); + + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y); + int xOffset, yOffset, mouseX = ptMouse.X, mouseY = ptMouse.Y; + + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (yOffset != 0) + { + int absYOffset = Math.Abs(yOffset), normOffset = yOffset / absYOffset; + ScrollRowsByCount(normOffset, normOffset < 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement); + this.vertScrollTimer.Interval = GetRowScrollRate(absYOffset); + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect]) + { + hti = HitTest(mouseX, ptMouse.Y - yOffset - normOffset); + if (hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + if (xOffset != 0) + { + hti = HitTest(ptMouse.X - xOffset - (xOffset / Math.Abs(xOffset)), ptMouse.Y - yOffset - normOffset); + } + else + { + hti = HitTest(mouseX, ptMouse.Y - yOffset - normOffset); + } + if (hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + this.VertScrollTimer.Enabled = false; + } + } + } + + private void WireEditingControlEvents() + { + Debug.Assert(this.editingPanel != null); + this.editingPanel.Click += new System.EventHandler(EditingControls_Click); + this.editingPanel.DoubleClick += new System.EventHandler(EditingControls_DoubleClick); + this.editingPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingPanel.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingPanel.MouseEnter += new System.EventHandler(EditingControls_MouseEnter); + this.editingPanel.MouseLeave += new System.EventHandler(EditingControls_MouseLeave); + this.editingPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + + Debug.Assert(this.editingControl != null); + this.editingControl.Click += new System.EventHandler(EditingControls_Click); + this.editingControl.DoubleClick += new System.EventHandler(EditingControls_DoubleClick); + this.editingControl.MouseClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingControl.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingControl.MouseDown += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingControl.MouseEnter += new System.EventHandler(EditingControls_MouseEnter); + this.editingControl.MouseLeave += new System.EventHandler(EditingControls_MouseLeave); + this.editingControl.MouseMove += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingControl.MouseUp += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + } + + private void WireScrollBarsEvents() + { + if (this.horizScrollBar != null) + { + this.horizScrollBar.MouseEnter += new System.EventHandler(ScrollBar_MouseEnter); + this.horizScrollBar.MouseLeave += new System.EventHandler(ScrollBar_MouseLeave); + } + if (this.vertScrollBar != null) + { + this.vertScrollBar.MouseEnter += new System.EventHandler(ScrollBar_MouseEnter); + this.vertScrollBar.MouseLeave += new System.EventHandler(ScrollBar_MouseLeave); + } + } + + /// + /// Handles the WM_CONTEXTMENU message + /// + /// + internal override void WmContextMenu(ref Message m) + { + ContextMenuStrip contextMenuStrip; + int x = unchecked( (int)(short)(long)m.LParam); + int y = unchecked( (int) (long)m.LParam) >> 16; + Point client; + bool keyboardActivated = false; + // lparam will be exactly -1 when the user invokes the context menu + // with the keyboard. + // + if (unchecked( (int) (long)m.LParam) == -1) + { + keyboardActivated = true; + client = new Point(this.Width/2, this.Height/2); + contextMenuStrip = (ContextMenuStrip) this.ContextMenuStrip; + } + else + { + client = PointToClientInternal(new Point(x, y)); + HitTestInfo hti = HitTest(client.X, client.Y); + DataGridViewCell dataGridViewCell = null; + switch (hti.Type) + { + case DataGridViewHitTestType.Cell: + dataGridViewCell = this.Rows.SharedRow(hti.row).Cells[hti.col]; + break; + case DataGridViewHitTestType.ColumnHeader: + Debug.Assert(hti.row == -1); + dataGridViewCell = this.Columns[hti.col].HeaderCell; + break; + case DataGridViewHitTestType.RowHeader: + Debug.Assert(hti.col == -1); + dataGridViewCell = this.Rows.SharedRow(hti.row).HeaderCell; + break; + case DataGridViewHitTestType.TopLeftHeader: + Debug.Assert(hti.row == -1); + Debug.Assert(hti.col == -1); + dataGridViewCell = this.TopLeftHeaderCell; + break; + } + if (dataGridViewCell != null) + { + contextMenuStrip = dataGridViewCell.GetInheritedContextMenuStrip(hti.row); + } + else + { + contextMenuStrip = (ContextMenuStrip) this.ContextMenuStrip; + } + } + + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (contextMenuStrip != null && this.ClientRectangle.Contains(client)) + { + contextMenuStrip.ShowInternal(this, client, keyboardActivated); + } + else + { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_GETDLGCODE message + /// + private void WmGetDlgCode(ref Message m) + { + m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTARROWS | NativeMethods.DLGC_WANTCHARS); + + Keys modifierKeys = ModifierKeys; + if (GetTabKeyEffective((modifierKeys & Keys.Shift) == Keys.Shift, (modifierKeys & Keys.Control) == Keys.Control)) + { + m.Result = (IntPtr) ((long) m.Result | NativeMethods.DLGC_WANTTAB); + } + } + + private unsafe bool WmNotify(ref Message m) + { + if (m.LParam == IntPtr.Zero) + { + return false; + } + + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam; + if (nmhdr->code == NativeMethods.TTN_GETDISPINFO && !DesignMode) + { + string toolTip = this.ToolTipPrivate; + + if (!String.IsNullOrEmpty(toolTip)) + { + // MSDN: Setting the max width has the added benefit of enabling multiline tool tips! + UnsafeNativeMethods.SendMessage(new HandleRef(this, nmhdr->hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + + ttt.lpszText = toolTip; + + if (this.RightToLeft == RightToLeft.Yes) + { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + Marshal.StructureToPtr(ttt, m.LParam, false); + return true; + } + } + + return false; + } + + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_GETDLGCODE: + WmGetDlgCode(ref m); + return; + case NativeMethods.WM_LBUTTONDBLCLK: + case NativeMethods.WM_LBUTTONDOWN: + // If the OnEnter procedure is called, it's because of a mouse down event, and not a TAB key. + this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] = true; + try + { + base.WndProc(ref m); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] = false; + } + return; + case NativeMethods.WM_NOTIFY: + if (WmNotify(ref m)) + { + // we are done - skip default handling + return; + } + break; + + case NativeMethods.WM_IME_STARTCOMPOSITION: + case NativeMethods.WM_IME_COMPOSITION: + if (this.editingControl != null) + { + // Make sure that the first character is forwarded to the editing control. + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + } + break; + } + + base.WndProc(ref m); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs.back b/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs.back new file mode 100644 index 000000000..78c957f6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewMethods.cs.back @@ -0,0 +1,29998 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System.Collections; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Drawing; + using System.Windows.Forms.ComponentModel; + using System.Globalization; + using System.Diagnostics; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.Layout; + using Microsoft.Win32; + using System.Collections.Specialized; + using System.Windows.Forms.Internal; + using System.Runtime.Versioning; + using Runtime.CompilerServices; + + /// + public partial class DataGridView + { + /// + protected virtual void AccessibilityNotifyCurrentCellChanged(Point cellAddress) + { + if (cellAddress.X < 0 || cellAddress.X >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("cellAddress"); + } + + if (cellAddress.Y < 0 || cellAddress.Y >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("cellAddress"); + } + + int visibleRowIndex = this.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, cellAddress.Y); + int visibleColumnIndex = this.Columns.ColumnIndexToActualDisplayIndex(cellAddress.X, DataGridViewElementStates.Visible); + + int topHeaderRowIncrement = this.ColumnHeadersVisible ? 1 : 0; + int rowHeaderIncrement = this.RowHeadersVisible ? 1 : 0; + + int objectID = visibleRowIndex + topHeaderRowIncrement // + 1 because the top header row acc obj is at index 0 + + 1; // + 1 because objectID's need to be positive and non-zero + + int childID = visibleColumnIndex + rowHeaderIncrement; // + 1 because the column header cell is at index 0 in top header row acc obj + // same thing for the row header cell in the data grid view row acc obj + + if (this.ContainsFocus) + { + this.AccessibilityNotifyClients(AccessibleEvents.Focus, objectID, childID); + + if (AccessibilityImprovements.Level3) + { + CurrentCell?.AccessibilityObject.SetFocus(); + } + + } + this.AccessibilityNotifyClients(AccessibleEvents.Selection, objectID, childID); + } + + internal void ActivateToolTip(bool activate, string toolTipText, int columnIndex, int rowIndex) + { + this.toolTipCaption = toolTipText; + this.ptToolTipCell = new Point(columnIndex, rowIndex); + this.toolTipControl.Activate(activate); + } + + internal void AddNewRow(bool createdByEditing) + { + Debug.Assert(this.Columns.Count > 0); + Debug.Assert(this.newRowIndex == -1); + + this.Rows.AddInternal(true /*newRow*/, null /*values*/); + this.newRowIndex = this.Rows.Count - 1; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing] = createdByEditing; + + if (createdByEditing) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(this.Rows[this.newRowIndex]); + OnUserAddedRow(dgvre); + } + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual DataGridViewAdvancedBorderStyle AdjustColumnHeaderBorderStyle(DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStyleInput, + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder, + bool isFirstDisplayedColumn, + bool isLastVisibleColumn) + { + if (this.ApplyVisualStylesToHeaderCells) + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.Inset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.None : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + case DataGridViewAdvancedCellBorderStyle.Outset: + case DataGridViewAdvancedCellBorderStyle.Inset: + if (!isFirstDisplayedColumn || this.RowHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All; + return dataGridViewAdvancedBorderStylePlaceholder; + } + else + { + // isFirstDisplayedColumn == true && this.RowHeadersVisible == false + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = dataGridViewAdvancedBorderStyleInput.All; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All; + return dataGridViewAdvancedBorderStylePlaceholder; + } + } + } + else + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isLastVisibleColumn ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetPartial; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isLastVisibleColumn ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + } + else + { + if (isFirstDisplayedColumn) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = this.RowHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + if (!isFirstDisplayedColumn || this.RowHeadersVisible) + { + if (this.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + return dataGridViewAdvancedBorderStylePlaceholder; + } + break; + } + } + + return dataGridViewAdvancedBorderStyleInput; + } + + private bool AdjustExpandingColumn(DataGridViewColumn dataGridViewColumn, int rowIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(rowIndex > -1); + Debug.Assert(rowIndex < this.Rows.Count); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return false; + } + + bool ret = false; // No autosizing occurs by default. + try + { + this.noAutoSizeCount++; + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int preferredThickness = dataGridViewRow.Cells[dataGridViewColumn.Index].GetPreferredWidth(rowIndex, dataGridViewRow.GetHeight(rowIndex)); + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (dataGridViewColumn.Width < preferredThickness) + { + // Column needs to be expanded + dataGridViewColumn.ThicknessInternal = preferredThickness; + ret = true; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + + return ret; + } + + private bool AdjustExpandingColumns(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, int rowIndex) + { + Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0); + + bool ret = false; // No column autosizes by default + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria = (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = (inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter); + if (autoSizeColumnCriteriaFiltered != 0) + { + ret |= AdjustExpandingColumn(dataGridViewColumn, rowIndex); + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return ret; + } + + private int AdjustExpandingRow(int rowIndex, int columnIndex, bool fixedWidth) + { + Debug.Assert(columnIndex >= -1 && columnIndex < this.Columns.Count); + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders || + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders || + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedCells); + + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int width = 0; + DataGridViewCell dataGridViewCell; + if (columnIndex > -1 && (((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + if (fixedWidth) + { + width = this.Columns[columnIndex].Thickness; + } + } + else + { + Debug.Assert(columnIndex == -1); + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0); + dataGridViewCell = this.Rows.SharedRow(rowIndex).HeaderCell; + if (fixedWidth) + { + Debug.Assert(this.RowHeadersWidth > 0); + Debug.Assert(this.RowHeadersVisible); + width = this.RowHeadersWidth; + } + } + int preferredThickness; + if (fixedWidth) + { + preferredThickness = dataGridViewCell.GetPreferredHeight(rowIndex, width); + } + else + { + preferredThickness = dataGridViewCell.GetPreferredSize(rowIndex).Height; + } + int height, minimumHeight; + this.Rows.SharedRow(rowIndex).GetHeightInfo(rowIndex, out height, out minimumHeight); + if (preferredThickness < height) + { + preferredThickness = height; + } + Debug.Assert(preferredThickness >= minimumHeight); + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (height != preferredThickness) + { + Debug.Assert(this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None); + this.Rows[rowIndex].Thickness = preferredThickness; // unsharing the resized row + } + return preferredThickness; + } + + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] // Can't cache DataGridViewRow object because rowIndex is changing in loop. + private void AdjustExpandingRows(int columnIndex, bool fixedWidth) + { + if ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && this.RowHeadersVisible)) + { + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + // Very expensive processing - the developer should avoid this scenario. + // Switch to batch operation + this.inBulkPaintCount++; + try + { + if ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0) + { + this.inBulkLayoutCount++; + try + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + int width = 0; + DataGridViewCell dataGridViewCell; + if (columnIndex > -1 && (((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + if (fixedWidth) + { + width = this.Columns[columnIndex].Thickness; + } + } + else + { + //Debug.Assert(columnIndex == -1); + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0); + dataGridViewCell = this.Rows.SharedRow(rowIndex).HeaderCell; + if (fixedWidth) + { + Debug.Assert(this.RowHeadersWidth > 0); + Debug.Assert(this.RowHeadersVisible); + width = this.RowHeadersWidth; + } + } + int preferredHeight; + if (fixedWidth) + { + preferredHeight = dataGridViewCell.GetPreferredHeight(rowIndex, width); + } + else + { + preferredHeight = dataGridViewCell.GetPreferredSize(rowIndex).Height; + } + if (this.Rows.SharedRow(rowIndex).Height < preferredHeight) + { + this.Rows[rowIndex].Height = preferredHeight; // unsharing the row to be resized + } + } + } + finally + { + ExitBulkLayout(false /*invalidInAdjustFillingColumns*/); + } + } + else + { + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0); + int displayHeight = this.layout.Data.Height; + int cy = 0; + + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth); + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + if (cy < displayHeight) + { + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + } + finally + { + ExitBulkPaint(-1, -1); + } + } + } + + internal void AdjustFillingColumn(DataGridViewColumn dataGridViewColumn, int width) + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumn] = true; + + try + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Visible); + Debug.Assert(!dataGridViewColumn.Frozen); + Debug.Assert(dataGridViewColumn.MinimumWidth <= width); + Debug.Assert(dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill); + Debug.Assert(!this.layout.dirty); + + if (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) > this.layout.Data.Width) + { + // Columns are scrolling - this means that all columns have reached their minimum width. + // Do not affect their width or fill weight + Debug.Assert(dataGridViewColumn.MinimumWidth == dataGridViewColumn.Width); + return; + } + + int availableWidth = this.layout.Data.Width; // Width available for auto-filled columns + + // Check if the column is the first or last visible scrolling column + if (this.DesignMode || + dataGridViewColumn == this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) || + dataGridViewColumn == this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen)) + { + // Changing the width is equivalent to adjusting the FillWeight when: + // - the column is not scrolling and is the first non-frozen visible column + // - the column is not scrolling and is the last non-frozen visible column + + float weightSum = 0; // Weights of all auto filled visible columns. + int widthSum = 0; // Sum of widths of visible auto filled columns. + int imposedWidthSum = 0; // Minimum width required for all other columns. + bool otherFillingColumnExists = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible) + { + if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumnTmp.Frozen); + widthSum += dataGridViewColumnTmp.Width; + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index) + { + imposedWidthSum += dataGridViewColumnTmp.MinimumWidth; + otherFillingColumnExists = true; + } + weightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + imposedWidthSum += dataGridViewColumnTmp.Width; + availableWidth -= dataGridViewColumnTmp.Width; + } + } + } + + if (!otherFillingColumnExists) + { + // The resized column is the only one that is filling. This is a no-op operation. + // Neither the width nor the fill weight can change + return; + } + + int maximumPossibleWidth = this.layout.Data.Width - imposedWidthSum; + if (width > maximumPossibleWidth) + { + width = maximumPossibleWidth; + } + + // Determine fill weight equivalent to 'width' + float oldWeight = dataGridViewColumn.FillWeight; + float newWeight = (float)(width * weightSum) / (float)widthSum; + bool desiredWidthTooSmall = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.FillWeightInternal = (weightSum - newWeight) * dataGridViewColumnTmp.FillWeight / (weightSum - oldWeight); + + if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / (float)widthSum) + { + desiredWidthTooSmall = true; + dataGridViewColumnTmp.DesiredFillWidth = -1; + } + else + { + dataGridViewColumnTmp.DesiredFillWidth = 0; + } + } + } + + dataGridViewColumn.FillWeightInternal = newWeight; + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + float usedFillWeights = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + else if (dataGridViewColumnTmp.DesiredFillWidth == -1) + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + } + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumnTmp.DesiredFillWidth != -1) + { + dataGridViewColumnTmp.UsedFillWeight = Math.Max(dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal, + weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum); + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights; + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + } + } + #if DEBUG + float weightSumDbg = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + weightSumDbg += dataGridViewColumnTmp.UsedFillWeight; + } + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); + #endif + } + else + { + // This column is not the first nor the last visible non-frozen column + // Changing its width only affects the width and weight of the columns displayed after it + + // First figure out the maximum possible width + int imposedWidthSum = 0; // Minimum width required for all other columns + float weightSum = 0; // Weights of all auto filled visible columns. + float oldWeightSum = 0F; // Fill weights of the columns displayed after this resized column + bool otherFillingColumnExists = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible) + { + if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumnTmp.Frozen); + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index) + { + if (this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + imposedWidthSum += dataGridViewColumnTmp.MinimumWidth; // Column is allowed to shrink down to its minimum + oldWeightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + // Column is displayed before 'dataGridViewColumn', it is not allowed to shrink at all + imposedWidthSum += dataGridViewColumnTmp.Width; + } + otherFillingColumnExists = true; + } + weightSum += dataGridViewColumnTmp.FillWeight; + } + else + { + imposedWidthSum += dataGridViewColumnTmp.Width; + availableWidth -= dataGridViewColumnTmp.Width; + } + } + } + + if (!otherFillingColumnExists) + { + // The resized column is the only one that is filling. This is a no-op operation. + // Neither the width nor the fill weight can change + return; + } + + int maximumPossibleWidth = this.layout.Data.Width - imposedWidthSum; + if (width > maximumPossibleWidth) + { + width = maximumPossibleWidth; + } + + // Then figure out the target fill weights + float oldWeight = dataGridViewColumn.FillWeight; + float newWeight = weightSum * width / availableWidth; + float newWeightSum = oldWeightSum + oldWeight - newWeight; + Debug.Assert(newWeightSum > 0); + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + dataGridViewColumnTmp.FillWeightInternal = dataGridViewColumnTmp.FillWeight * newWeightSum / oldWeightSum; + } + } + + dataGridViewColumn.FillWeightInternal = newWeight; + + bool desiredWidthTooSmall = false; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / (float)availableWidth) + { + desiredWidthTooSmall = true; + dataGridViewColumnTmp.DesiredFillWidth = -1; + } + else + { + dataGridViewColumnTmp.DesiredFillWidth = 0; + } + } + } + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + float usedFillWeights = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index || + this.Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index)) + { + if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + else + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.Width / availableWidth; + } + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + else if (dataGridViewColumnTmp.DesiredFillWidth == -1) + { + dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth; + usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight; + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + } + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index) && + dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumnTmp.DesiredFillWidth != -1) + { + dataGridViewColumnTmp.UsedFillWeight = Math.Max(dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal, + weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth); + usedFillWeights += dataGridViewColumnTmp.UsedFillWeight; + } + } + dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights; + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight; + } + } + } + #if DEBUG + float weightSumDbg = 0F; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + weightSumDbg += dataGridViewColumnTmp.UsedFillWeight; + } + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); + #endif + } + + // UsedFillWeight properties are up-to-date + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + // AdjustFillingColumns() will resize columns based on the UsedFillWeight values + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumn] = false; + } + } + + private bool AdjustFillingColumns() + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns]) + { + // No need to auto fill columns while we're already doing it. + return false; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns] = true; + + bool columnsAdjusted = false; + try + { + // Determine free space for filling columns. + int numVisibleFillColumns = 0; // number of visible columns that are auto filled. + int imposedWidthSum = 0; // total width of columns that don't have a flexible width. + int requiredWidthSum = 0; // total of the minimum widths of the visible auto filled columns. + float weightSum = 0F; // total of the weights of the visible auto filled columns. + ArrayList autoFillColumns = null; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.Visible) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(!dataGridViewColumn.Frozen); + numVisibleFillColumns++; + requiredWidthSum += dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + weightSum += dataGridViewColumn.FillWeight; + if (autoFillColumns == null) + { + autoFillColumns = new ArrayList(this.Columns.Count); + } + autoFillColumns.Add(dataGridViewColumn); + } + else + { + imposedWidthSum += dataGridViewColumn.Width; + } + } + } + + if (numVisibleFillColumns > 0) + { + // Assuming no vertical scrollbar has been accounted for yet + Debug.Assert(this.layout.Data.Width == this.layout.Inside.Width - this.layout.RowHeaders.Width - (this.SingleVerticalBorderAdded ? 1 : 0)); + int availableWidth = this.layout.Data.Width - imposedWidthSum; + if ((this.scrollBars == ScrollBars.Both || this.scrollBars == ScrollBars.Vertical) /*&& + (availableWidth > requiredWidthSum || this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty])*/) + { + int totalVisibleRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible); + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + // Assuming there is no horizontal scrollbar, is a vertical scrollbar required? + ComputeVisibleRows(); // Make sure this.displayedBandsInfo.FirstDisplayedScrollingRow and other row count info variables have been set + + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + SystemInformation.VerticalScrollBarWidth <= this.layout.Data.Width) + { + // Vertical scrollbar is required, even when there is not horizontal one. + availableWidth -= SystemInformation.VerticalScrollBarWidth; + } + } + + int columnEntry; + + if (availableWidth <= requiredWidthSum) + { + // All auto filled columns need to take their minimum width. If (availableWidth < requiredWidthSum) a horizontal scrollbar appears. + availableWidth = 0; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + if (dataGridViewColumn.Thickness != minimumWidth) + { + columnsAdjusted = true; + dataGridViewColumn.ThicknessInternal = minimumWidth; + } + availableWidth += dataGridViewColumn.Thickness; + } + //if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + // Make sure the UsedFillWeight correspond to the actual column width + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.Width * weightSum / availableWidth; + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + } + return columnsAdjusted; + } + + // Auto filling columns can share some real estate. + + int usedWidth = 0; + + // Update the UsedFillWeight value if dirty + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + // Assign desired widths + Debug.Assert(weightSum > 0); + bool desiredWidthTooSmall = false; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (columnEntry == autoFillColumns.Count - 1) + { + dataGridViewColumn.DesiredFillWidth = availableWidth - usedWidth; + } + else + { + float desiredFillWidth = (dataGridViewColumn.FillWeight / weightSum) * availableWidth; + dataGridViewColumn.DesiredFillWidth = (int)Math.Round(desiredFillWidth, MidpointRounding.AwayFromZero); + usedWidth += dataGridViewColumn.DesiredFillWidth; + } + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + if (dataGridViewColumn.DesiredFillWidth < minimumWidth) + { + desiredWidthTooSmall = true; + dataGridViewColumn.DesiredFillWidth = -1; + } + } + + if (desiredWidthTooSmall) + { + // At least one column hits its minimum width + // Adjust UsedFillWeight values are adjusted FillWeight values + float usedWeightSumNoneMinimal = weightSum; + float weightSumNoneMinimal = weightSum; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth == -1) + { + int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth; + dataGridViewColumn.UsedFillWeight = weightSum * minimumWidth / availableWidth; + usedWeightSumNoneMinimal -= dataGridViewColumn.UsedFillWeight; + weightSumNoneMinimal -= dataGridViewColumn.FillWeight; + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth != -1) + { + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal; + } + } + } + else + { + // No column hits its minimum width + // Each UsedFillWeight simply equals the FillWeight + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight; + } + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = false; + this.availableWidthForFillColumns = availableWidth; + } + else if (availableWidth != this.availableWidthForFillColumns) + { + // The available width for auto-filled columns has changed - UsedFillWeight values need to be adjusted. + if (availableWidth > this.availableWidthForFillColumns) + { + // Available width increased + int widthGain = availableWidth - this.availableWidthForFillColumns; + // Allocate additional width according to UsedFillWeight and FillWeight values + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width; + } + float[] floatDesiredWidths = new float[autoFillColumns.Count]; + for (int gain = 0; gain < widthGain; gain++) + { + float fillWeightRatioSum = 0F; + bool minimumColumnExists = false; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + fillWeightRatioSum += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight; + if (dataGridViewColumn.DesiredFillWidth <= dataGridViewColumn.MinimumWidth) + { + minimumColumnExists = true; + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (gain == 0) + { + floatDesiredWidths[columnEntry] = this.availableWidthForFillColumns * dataGridViewColumn.UsedFillWeight / weightSum; + } + if (minimumColumnExists) + { + floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight / fillWeightRatioSum; + } + else + { + floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / weightSum; + } + } + } + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = weightSum / availableWidth * floatDesiredWidths[columnEntry]; + } + } + else + { + // availableWidth < this.availableWidthForFillColumns - Available width decreased + int totalWidthLoss = this.availableWidthForFillColumns - availableWidth; + int cumulatedWidthLoss = 0; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width; + } + // the width loss is accounted for in steps of 10% (see VSWhidbey 568343) + do + { + int stepDownAvailableWidthForFillColumns = this.availableWidthForFillColumns - cumulatedWidthLoss; + int widthLoss = Math.Min(stepDownAvailableWidthForFillColumns - availableWidth, Math.Max(1, (int)(stepDownAvailableWidthForFillColumns * 0.1F))); + cumulatedWidthLoss += widthLoss; + bool changeDone; + do + { + changeDone = false; + // Determine which column deserves to shrink the most + float biggestWeightDeficiency = 0F, fillWeightRatioSum = 0F, fillWeightRatio; + DataGridViewColumn mostDeservingDataGridViewColumn = null; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + if (dataGridViewColumn.DesiredFillWidth > dataGridViewColumn.MinimumWidth) + { + fillWeightRatio = dataGridViewColumn.UsedFillWeight / dataGridViewColumn.FillWeight; + fillWeightRatioSum += fillWeightRatio; + if (fillWeightRatio > biggestWeightDeficiency) + { + mostDeservingDataGridViewColumn = dataGridViewColumn; + biggestWeightDeficiency = fillWeightRatio; + } + } + } + if (mostDeservingDataGridViewColumn != null) + { + float floatDesiredWidth = (stepDownAvailableWidthForFillColumns * mostDeservingDataGridViewColumn.UsedFillWeight / weightSum) - widthLoss * mostDeservingDataGridViewColumn.UsedFillWeight / mostDeservingDataGridViewColumn.FillWeight / fillWeightRatioSum; + if (floatDesiredWidth < (float)mostDeservingDataGridViewColumn.MinimumWidth) + { + floatDesiredWidth = (int)mostDeservingDataGridViewColumn.MinimumWidth; + } + int oldDesiredWidth = mostDeservingDataGridViewColumn.DesiredFillWidth; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Min(oldDesiredWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero)); + changeDone = (oldDesiredWidth != mostDeservingDataGridViewColumn.DesiredFillWidth); + if (!changeDone && widthLoss == 1 && oldDesiredWidth > mostDeservingDataGridViewColumn.MinimumWidth) + { + mostDeservingDataGridViewColumn.DesiredFillWidth--; + changeDone = true; + } + Debug.Assert(oldDesiredWidth >= mostDeservingDataGridViewColumn.DesiredFillWidth); + widthLoss -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + if (changeDone) + { + stepDownAvailableWidthForFillColumns -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + dataGridViewColumn.UsedFillWeight = weightSum / stepDownAvailableWidthForFillColumns * dataGridViewColumn.DesiredFillWidth; + } + } + Debug.Assert(widthLoss >= 0); + } + } + while (changeDone && widthLoss > 0); + } + while (cumulatedWidthLoss < totalWidthLoss); + } + this.availableWidthForFillColumns = availableWidth; + } + +#if DEBUG + float weightSumDbg = 0F; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + weightSumDbg += dataGridViewColumn.UsedFillWeight; + + } + Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F); +#endif + + // Finally update the columns' width according the UsedFillWeight values. + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar] = false; + usedWidth = 0; + float carryover = 0F; + while (autoFillColumns.Count > 0) + { + DataGridViewColumn mostDeservingDataGridViewColumn = null; + if (autoFillColumns.Count == 1) + { + mostDeservingDataGridViewColumn = (DataGridViewColumn)autoFillColumns[0]; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(availableWidth - usedWidth, mostDeservingDataGridViewColumn.MinimumWidth); + autoFillColumns.Clear(); + } + else + { + float biggestWeightDiscrepancy = 0F, weightDiscrepancy; + for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)autoFillColumns[columnEntry]; + weightDiscrepancy = Math.Abs(dataGridViewColumn.UsedFillWeight - dataGridViewColumn.FillWeight) / dataGridViewColumn.FillWeight; + if (weightDiscrepancy > biggestWeightDiscrepancy || mostDeservingDataGridViewColumn == null) + { + mostDeservingDataGridViewColumn = dataGridViewColumn; + biggestWeightDiscrepancy = weightDiscrepancy; + } + } + float floatDesiredWidth = (mostDeservingDataGridViewColumn.UsedFillWeight * availableWidth / weightSum) + carryover; + mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(mostDeservingDataGridViewColumn.MinimumWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero)); + carryover = floatDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth; + usedWidth += mostDeservingDataGridViewColumn.DesiredFillWidth; + autoFillColumns.Remove(mostDeservingDataGridViewColumn); + } + if (mostDeservingDataGridViewColumn.DesiredFillWidth != mostDeservingDataGridViewColumn.Thickness) + { + columnsAdjusted = true; + mostDeservingDataGridViewColumn.ThicknessInternal = mostDeservingDataGridViewColumn.DesiredFillWidth; + } + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar] = true; + } + } +#if DEBUG + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty]) + { + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + Debug.Assert(Math.Abs(dataGridViewColumnTmp.UsedFillWeight * this.availableWidthForFillColumns - weightSum * dataGridViewColumnTmp.Width) / weightSum / dataGridViewColumnTmp.Width <= 1.25F / dataGridViewColumnTmp.Width); + } + } + } + + bool nonMinColumnExists = false; + int widthSum = 0; + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.Visible && + dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + widthSum += dataGridViewColumnTmp.Width; + if (dataGridViewColumnTmp.Width > dataGridViewColumnTmp.MinimumWidth) + { + nonMinColumnExists = true; + } + } + } + if (nonMinColumnExists) + { + Debug.Assert(widthSum == this.availableWidthForFillColumns); + } +#endif + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inAdjustFillingColumns] = false; + } + + return columnsAdjusted; + } + + private void AdjustShrinkingRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth, bool internalAutosizing) + { + if ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && this.RowHeadersVisible)) + { + // Switch to batch operation + this.inBulkPaintCount++; + try + { + if ((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0) + { + // Very expensive processing - the developer should avoid this scenario. + this.inBulkLayoutCount++; + try + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + } + } + finally + { + ExitBulkLayout(false /*invalidInAdjustFillingColumns*/); + } + } + else + { + Debug.Assert((((DataGridViewAutoSizeRowsModeInternal) autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0); + int displayHeight = this.layout.Data.Height; + int cy = 0; + + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + if (cy < displayHeight) + { + int cyFrozen = cy; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + rowIndex = oldFirstVisibleScrollingRow; + while (rowIndex != -1 && + cy < displayHeight && + oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + + do + { + oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (cy < displayHeight) + { + int rowAboveFirstVisibleScrollingRow = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowAboveFirstVisibleScrollingRow != -1) + { + AutoResizeRowInternal(rowAboveFirstVisibleScrollingRow, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + } + } + cy = cyFrozen; + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing); + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + while (oldFirstVisibleScrollingRow != this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + } + } + finally + { + ExitBulkPaint(-1, -1); + } + } + } + + /// + // Does not seem to be a valid fxcop violation report. Contacting fxcop team to double-check. + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] + public bool AreAllCellsSelected(bool includeInvisibleCells) + { + if (this.Columns.Count == 0 && this.Rows.Count == 0) + { + return true; + } + if (!includeInvisibleCells && + (this.Rows.GetFirstRow(DataGridViewElementStates.Visible) == -1 || + this.Columns.GetFirstColumn(DataGridViewElementStates.Visible) == null)) + { + return true; + } + + DataGridViewRow dataGridViewRow = null; + bool allCellsSelected; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + allCellsSelected = this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + return true; + } + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + allCellsSelected = this.selectedBandIndexes.Count * this.Rows.Count + this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!this.selectedBandIndexes.Contains(dataGridViewColumn.Index)) + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return true; + } + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + allCellsSelected = this.selectedBandIndexes.Count * this.Columns.Count + this.individualSelectedCells.Count == this.Columns.Count * this.Rows.Count; + if (allCellsSelected || includeInvisibleCells) + { + return allCellsSelected; + } + else + { + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + dataGridViewRow = this.Rows[rowIndex]; // unshares this row + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected) + { + return false; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + return true; + } + } + } + Debug.Fail("DataGridView.AreAllCellsSelected - Shouldn't reach this code"); + return false; + } + + /// + /// Assigns a new parent control to the DataGridView. + /// + internal override void AssignParent(Control value) + { + if (this.toolTipControl.Activated) + { + this.toolTipControl.Activate(false /*activate*/); + } + base.AssignParent(value); + } + + private void AutoGenerateDataBoundColumns(DataGridViewColumn[] boundColumns) + { + DataGridViewColumnCollection dataGridViewCols = this.Columns; + DataGridViewColumn[] clonedColumns = new DataGridViewColumn[dataGridViewCols.Count]; + int clonedColumnsCount = 0; + int i; + + + // 1. Clone all the columns which are currently bound and will also be bound under the new DataSource/DataMember combination. + // 2. Delete all the currently bound columns. + // 3. Sort the cloned columns in order of their DisplayIndex. + // 4. Add the new data bound columns. Here are the rules: + // a. if the cloned collection contains (possible multiple) columns with DataProperyHame == current data field, + // add the first cloned column that maps to the data field. + // b. other wise add the new bound column. + // 5. Add the remaining cloned columns in the order of their DisplayIndex. + + // 1. Clone all the currently bound columns. + // + // We can't do 1. and 2. in the same loop because we need to save the DisplayIndex. + for (i = 0; i < dataGridViewCols.Count; i++) + { + if (this.DataSource != null && + !String.IsNullOrEmpty(dataGridViewCols[i].DataPropertyName) && + !dataGridViewCols[i].IsDataBound) + { + MapDataGridViewColumnToDataBoundField(dataGridViewCols[i]); + } + + if (dataGridViewCols[i].IsDataBound) + { + // We only clone columns which are data bound w/ the new DataSource/DataMember combination. + if (this.dataConnection != null && this.dataConnection.BoundColumnIndex(dataGridViewCols[i].DataPropertyName) != -1) + { + clonedColumns[clonedColumnsCount] = (DataGridViewColumn) dataGridViewCols[i].Clone(); + clonedColumns[clonedColumnsCount].DisplayIndex = dataGridViewCols[i].DisplayIndex; + clonedColumnsCount ++; + } + } + } + + i = 0; + // 2. Delete all the currently bound columns. + while (i < dataGridViewCols.Count) + { + if (dataGridViewCols[i].IsDataBound) + { + dataGridViewCols.RemoveAtInternal(i, true /*force*/); + } + else + { + i++; + } + } + + // 3. Sort the cloned columns in the order of their DisplayIndex. + + // Sort the cloned columns array by the display index. + // We need to copy the cloned columns into a possibly smaller array. + DataGridViewColumn[] finalClonedColumns; + if (clonedColumns.Length == clonedColumnsCount) + { + finalClonedColumns = clonedColumns; + } + else + { + finalClonedColumns = new DataGridViewColumn[clonedColumnsCount]; + Array.Copy(clonedColumns, finalClonedColumns, clonedColumnsCount); + } + + // Sort the array. + Array.Sort(finalClonedColumns, System.Windows.Forms.DataGridViewColumnCollection.ColumnCollectionOrderComparer); + + // 4. Add new columns for the Fields which were not data bound previously ( ie, for fields which do not have a clone ). + if (boundColumns != null) + { + for (int j = 0; j < boundColumns.Length; j ++) + { + if (boundColumns[j] != null && boundColumns[j].IsBrowsableInternal) + { + bool addNewColumn = true; + // Go thru the list of cloned columns and see if there is another column w/ the same data property name. + int clonedColIndex = 0; + for (; clonedColIndex < clonedColumnsCount; clonedColIndex ++) + { + if (finalClonedColumns[clonedColIndex] != null && + String.Compare(finalClonedColumns[clonedColIndex].DataPropertyName, + boundColumns[j].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + addNewColumn = false; + break; + } + } + + if (addNewColumn) + { + dataGridViewCols.Add(boundColumns[j]); + } + else + { + // add the cloned column. + dataGridViewCols.Add(finalClonedColumns[clonedColIndex]); + MapDataGridViewColumnToDataBoundField(finalClonedColumns[clonedColIndex]); + Debug.Assert(finalClonedColumns[clonedColIndex].IsDataBound); + finalClonedColumns[clonedColIndex] = null; + } + } + } + } + #if DEBUG + else + { + // If there are no data bound columns then there are no cloned columns either. + Debug.Assert(finalClonedColumns.Length == 0); + Debug.Assert(clonedColumnsCount == 0); + } + #endif // DEBUG + + // 5. Add remaining cloned columns. + if (clonedColumnsCount > 0) + { + for (int k = 0; k < finalClonedColumns.Length; k++) + { + if (finalClonedColumns[k] != null) + { + dataGridViewCols.Add(finalClonedColumns[k]); + MapDataGridViewColumnToDataBoundField(finalClonedColumns[k]); + Debug.Assert(finalClonedColumns[k].IsDataBound); + } + } + } + } + + private bool AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, bool fixedHeight) + { + Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0); + + bool ret = false; // No column autosizes by default + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + while (dataGridViewColumn != null) + { + DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = (inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter); + if (autoSizeColumnCriteriaFiltered != 0) + { + ret |= AutoResizeColumnInternal(dataGridViewColumn.Index, inheritedAutoSizeColumnCriteria, fixedHeight); + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + return ret; + } + + /// + public void AutoResizeColumn(int columnIndex) + { + AutoResizeColumn(columnIndex, DataGridViewAutoSizeColumnMode.AllCells); + } + + /// + public void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode) + { + AutoResizeColumn(columnIndex, autoSizeColumnMode, true); + } + + /// + protected void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode, bool fixedHeight) + { + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.NotSet || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.None || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedColumnAutoSizingCriteria, "autoSizeColumnMode")); + } + + switch (autoSizeColumnMode) + { + case DataGridViewAutoSizeColumnMode.NotSet: + case DataGridViewAutoSizeColumnMode.None: + case DataGridViewAutoSizeColumnMode.ColumnHeader: + case DataGridViewAutoSizeColumnMode.AllCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.AllCells: + case DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader: + case DataGridViewAutoSizeColumnMode.DisplayedCells: + case DataGridViewAutoSizeColumnMode.Fill: + break; + default: + throw new InvalidEnumArgumentException("autoSizeColumnMode", (int)autoSizeColumnMode, typeof(DataGridViewAutoSizeColumnMode)); + } + + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.ColumnHeader && !this.ColumnHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeInvisibleColumnHeader)); + } + + AutoResizeColumnInternal(columnIndex, (DataGridViewAutoSizeColumnCriteriaInternal) autoSizeColumnMode, fixedHeight); + } + + /// + public void AutoResizeColumnHeadersHeight() + { + AutoResizeColumnHeadersHeight(true, true); + } + + /// + public void AutoResizeColumnHeadersHeight(int columnIndex) + { + AutoResizeColumnHeadersHeight(columnIndex, true, true); + } + + /// + protected void AutoResizeColumnHeadersHeight(bool fixedRowHeadersWidth, bool fixedColumnsWidth) + { + if (!this.ColumnHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredHeight = 0; + if (this.layout.TopLeftHeader.Width > 0) + { + if (fixedRowHeadersWidth) + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredHeight(-1, this.layout.TopLeftHeader.Width); + } + else + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredSize(-1).Height; + } + } + int columnsCount = this.Columns.Count; + for (int columnIndex = 0; columnIndex < columnsCount; columnIndex++) + { + if (this.Columns[columnIndex].Visible) + { + if (fixedColumnsWidth) + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndex].HeaderCell.GetPreferredHeight(-1, this.Columns[columnIndex].Thickness)); + } + else + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndex].HeaderCell.GetPreferredSize(-1).Height); + } + } + } + if (preferredHeight < minimumColumnHeadersHeight) + { + preferredHeight = minimumColumnHeadersHeight; + } + if (preferredHeight > maxHeadersThickness) + { + preferredHeight = maxHeadersThickness; + } + if (preferredHeight != this.ColumnHeadersHeight) + { + SetColumnHeadersHeightInternal(preferredHeight, !fixedColumnsWidth /*invalidInAdjustFillingColumns*/); + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + protected void AutoResizeColumnHeadersHeight(int columnIndex, bool fixedRowHeadersWidth, bool fixedColumnWidth) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (!this.ColumnHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredHeight = 0; + if (this.layout.TopLeftHeader.Width > 0) + { + if (columnIndex != -1 || fixedRowHeadersWidth) + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredHeight(-1, this.layout.TopLeftHeader.Width); + } + else + { + preferredHeight = this.TopLeftHeaderCell.GetPreferredSize(-1).Height; + } + } + int columnsCount = this.Columns.Count; + for (int columnIndexTmp = 0; columnIndexTmp < columnsCount; columnIndexTmp++) + { + if (this.Columns[columnIndexTmp].Visible) + { + if (columnIndex != columnIndexTmp || fixedColumnWidth) + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndexTmp].HeaderCell.GetPreferredHeight(-1, this.Columns[columnIndexTmp].Thickness)); + } + else + { + preferredHeight = Math.Max(preferredHeight, this.Columns[columnIndexTmp].HeaderCell.GetPreferredSize(-1).Height); + } + } + } + if (preferredHeight < minimumColumnHeadersHeight) + { + preferredHeight = minimumColumnHeadersHeight; + } + if (preferredHeight > maxHeadersThickness) + { + preferredHeight = maxHeadersThickness; + } + if (preferredHeight != this.ColumnHeadersHeight) + { + SetColumnHeadersHeightInternal(preferredHeight, !fixedColumnWidth /*invalidInAdjustFillingColumns*/); + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + private bool AutoResizeColumnInternal(int columnIndex, DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal, bool fixedHeight) + { + Debug.Assert(autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.Header || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.AllRows || + autoSizeColumnCriteriaInternal == DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows) || + autoSizeColumnCriteriaInternal == (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows)); + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Header || this.ColumnHeadersVisible); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return false; + } + + bool ret = false; // No autosizing occurs by default. + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns. + // Note: Even none-resizable column can programmatically be resized. + DataGridViewColumn dataGridViewColumn = this.Columns[columnIndex]; + int preferredColumnWidth = dataGridViewColumn.GetPreferredWidth((DataGridViewAutoSizeColumnMode) autoSizeColumnCriteriaInternal, fixedHeight); + if (preferredColumnWidth < dataGridViewColumn.MinimumThickness) + { + preferredColumnWidth = dataGridViewColumn.MinimumThickness; + } + if (preferredColumnWidth > DataGridViewBand.maxBandThickness) + { + preferredColumnWidth = DataGridViewBand.maxBandThickness; + } + if (preferredColumnWidth != dataGridViewColumn.Thickness) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + AdjustFillingColumn(dataGridViewColumn, preferredColumnWidth); + } + else + { + this.Columns[columnIndex].ThicknessInternal = preferredColumnWidth; + } + ret = true; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + return ret; + } + + /// + public void AutoResizeColumns() + { + AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); + } + + /// + public void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode) + { + AutoResizeColumns(autoSizeColumnsMode, true); + } + + /// + protected void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode, bool fixedHeight) + { + for (int columnIndex = 0; columnIndex < this.Columns.Count; columnIndex++) + { + AutoResizeColumn(columnIndex, (DataGridViewAutoSizeColumnMode)autoSizeColumnsMode, fixedHeight); + } + } + + /// + public void AutoResizeRow(int rowIndex) + { + AutoResizeRow(rowIndex, DataGridViewAutoSizeRowMode.AllCells); + } + + /// + public void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode) + { + AutoResizeRow(rowIndex, autoSizeRowMode, true /*fixedWidth*/); + } + + /// + protected void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + // not using ClientUtils here because it's a flags enum, masking instead. + if (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0) + { + throw new InvalidEnumArgumentException("autoSizeRowMode", (int) autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode)); + } + if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowInvisibleRowHeader)); + } + AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, false /*internalAutosizing*/); + } + + /// + // User can override this if there is a quicker way to determine preferred row headers width + public void AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode) + { + AutoResizeRowHeadersWidth(rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + + /// + // User can override this if there is a quicker way to determine preferred row headers width + protected void AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode, + bool fixedColumnHeadersHeight, + bool fixedRowsHeight) + { + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode")); + } + // custom range checking, not using ClientUtils. + if (rowHeadersWidthSizeMode < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || rowHeadersWidthSizeMode > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader) + { + throw new InvalidEnumArgumentException("rowHeadersWidthSizeMode", (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode)); + } + + + if (!this.RowHeadersVisible) + { + return; + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredWidth = 0, rowIndex; + if (this.layout.TopLeftHeader.Width > 0) + { + if (fixedColumnHeadersHeight) + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredWidth(-1, this.layout.TopLeftHeader.Height); + } + else + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredSize(-1).Width; + } + } + switch (rowHeadersWidthSizeMode) + { + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader: + { + rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex != -1) + { + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, this.Rows.SharedRow(rowIndex).GetHeight(rowIndex))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders: + { + int displayHeight = this.layout.Data.Height, cy = 0; + rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndex != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex); + cy += dataGridViewRowHeight; + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width); + } + rowIndex = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + if (cy < displayHeight) + { + rowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndex != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex); + cy += dataGridViewRowHeight; + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width); + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders: + { + for (rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + if (fixedRowsHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, this.Rows.SharedRow(rowIndex).GetHeight(rowIndex))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width); + } + } + break; + } + default: + { + Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth"); + break; + } + } + if (preferredWidth < minimumRowHeadersWidth) + { + preferredWidth = minimumRowHeadersWidth; + } + if (preferredWidth != this.RowHeadersWidth) + { + this.RowHeadersWidthInternal = preferredWidth; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + public void AutoResizeRowHeadersWidth(int rowIndex, DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode) + { + AutoResizeRowHeadersWidth(rowIndex, + rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowHeight*/); + } + + /// + protected void AutoResizeRowHeadersWidth(int rowIndex, + DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode, + bool fixedColumnHeadersHeight, + bool fixedRowHeight) + { + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode")); + } + if (rowHeadersWidthSizeMode < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || rowHeadersWidthSizeMode > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader) + { + throw new InvalidEnumArgumentException("rowHeadersWidthSizeMode", (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode)); + } + + if (!this.RowHeadersVisible) + { + return; + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader && + rowIndex != -1 && + rowIndex != this.Rows.GetFirstRow(DataGridViewElementStates.Visible)) + { + return; + } + if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && + rowIndex != -1) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + if (!rowDisplayed) + { + return; + } + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + int preferredWidth = 0, rowIndexTmp; + if (this.layout.TopLeftHeader.Width > 0) + { + if (rowIndex != -1 || fixedColumnHeadersHeight) + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredWidth(-1, this.layout.TopLeftHeader.Height); + } + else + { + preferredWidth = this.TopLeftHeaderCell.GetPreferredSize(-1).Width; + } + } + switch (rowHeadersWidthSizeMode) + { + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader: + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, this.Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders: + { + int displayHeight = this.layout.Data.Height, cy = 0; + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (rowIndexTmp != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndexTmp); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp); + cy += dataGridViewRowHeight; + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + if (cy < displayHeight) + { + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + while (rowIndexTmp != -1 && cy < displayHeight) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndexTmp); + int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp); + cy += dataGridViewRowHeight; + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight)); + } + else + { + preferredWidth = Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + break; + } + case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders: + { + for (rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndexTmp != -1; + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible)) + { + if (rowIndex != rowIndexTmp || fixedRowHeight) + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, this.Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp))); + } + else + { + preferredWidth = Math.Max(preferredWidth, this.Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width); + } + } + break; + } + default: + { + Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth"); + break; + } + } + if (preferredWidth < minimumRowHeadersWidth) + { + preferredWidth = minimumRowHeadersWidth; + } + if (preferredWidth != this.RowHeadersWidth) + { + this.RowHeadersWidthInternal = preferredWidth; + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + private void AutoResizeRowInternal(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth, bool internalAutosizing) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) == 0); + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + try + { + this.noAutoSizeCount++; + // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle + // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids, handle creation wipes out and recreates the columns/rows. + // Note: Even none-resizable row can programmatically be resized. + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(rowIndex, out height, out minimumHeight); + int preferredThickness = dataGridViewRow.GetPreferredHeight(rowIndex, autoSizeRowMode, fixedWidth); + if (preferredThickness < minimumHeight) + { + preferredThickness = minimumHeight; + } + if (preferredThickness > DataGridViewBand.maxBandThickness) + { + preferredThickness = DataGridViewBand.maxBandThickness; + } + if (height != preferredThickness) + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + if (!OnRowHeightInfoPushed(rowIndex, preferredThickness, minimumHeight)) + { + this.Rows[rowIndex].ThicknessInternal = preferredThickness; // unsharing the resized row + } + } + else + { + if (internalAutosizing) + { + this.Rows[rowIndex].ThicknessInternal = preferredThickness; // unsharing the resized row + } + else + { + this.Rows[rowIndex].Thickness = preferredThickness; // unsharing the resized row + } + } + } + } + finally + { + Debug.Assert(this.noAutoSizeCount > 0); + this.noAutoSizeCount--; + } + } + + /// + public void AutoResizeRows() + { + AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells); + } + + /// + public void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode) + { + AutoResizeRows(autoSizeRowsMode, true /*fixedWidth*/); + } + + /// + protected void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth) + { + switch (autoSizeRowsMode) + { + case DataGridViewAutoSizeRowsMode.None: + case DataGridViewAutoSizeRowsMode.AllHeaders: + case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.AllCells: + case DataGridViewAutoSizeRowsMode.DisplayedHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders: + case DataGridViewAutoSizeRowsMode.DisplayedCells: + break; + default: + throw new InvalidEnumArgumentException("value", (int)autoSizeRowsMode, typeof(DataGridViewAutoSizeRowsMode)); + } + + if (autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_NeedAutoSizingCriteria, "autoSizeRowsMode")); + } + + if ((autoSizeRowsMode == DataGridViewAutoSizeRowsMode.AllHeaders || autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders) && + !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader)); + } + + AdjustShrinkingRows(autoSizeRowsMode, fixedWidth, false /*internalAutosizing*/); + } + + /// + protected void AutoResizeRows(int rowIndexStart, int rowsCount, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth) + { + // not using ClientUtils.IsEnumValid here because DataGridViewAutoSizeRowCriteriaInternal is a flags enum. + if (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0) + { + throw new InvalidEnumArgumentException("autoSizeRowMode", (int) autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode)); + } + + if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !this.RowHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader)); + } + + if (rowsCount < 0) + { + throw new ArgumentOutOfRangeException("rowsCount"); + } + + if (rowIndexStart < 0) + { + throw new ArgumentOutOfRangeException("rowIndexStart"); + } + + if (!this.IsHandleCreated) + { + // auto sizing causes handle creation. + // don't create the handle inside InitializeComponent because that causes problems w/ data binding + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = true; + return; + } + + this.inBulkPaintCount++; + this.inBulkLayoutCount++; + try + { + int rowIndex = this.Rows.GetNextRow(rowIndexStart - 1, DataGridViewElementStates.Visible); + int autoSizedCount = 0; + while (rowIndex != -1 && autoSizedCount < rowsCount) + { + AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, false /*internalAutosizing*/); + autoSizedCount++; + if (autoSizedCount < rowsCount) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + finally + { + ExitBulkLayout(true /*invalidInAdjustFillingColumns*/); + ExitBulkPaint(-1, -1); + } + } + + private void BeginColumnHeadersResize(int mouseY, int mouseBarOffset) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data); + if (this.layout.TopLeftHeader.Width > 0) + { + clip = Rectangle.Union(this.layout.TopLeftHeader, clip); + } + clip.Y += minimumColumnHeadersHeight - mouseBarOffset - 1; + // No need to limit the bottom edge of the cursor clip since maxHeadersThickness is very large. + CaptureMouse(clip); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] = true; + this.trackRowAnchor = mouseY; + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastRowSplitBar == -1); + this.currentRowSplitBar = mouseY; + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void BeginColumnRelocation(int mouseX, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle cursorClip = this.layout.ColumnHeaders; + int frozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int scrollingWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - frozenWidth; + if (this.Columns[index].Frozen) + { + // A frozen column cannot be relocated into an unfrozen area + if (this.RightToLeftInternal) + { + cursorClip.X += cursorClip.Width - frozenWidth; + } + cursorClip.Width = Math.Min(frozenWidth, this.layout.Data.Width); + } + else + { + // An unfrozen column cannot be relocated into a frozen area + if (!this.RightToLeftInternal) + { + cursorClip.X += frozenWidth; + } + else if (this.layout.Data.Width > frozenWidth + scrollingWidth) + { + cursorClip.X += this.layout.Data.Width - frozenWidth - scrollingWidth; + } + cursorClip.Width = Math.Min(scrollingWidth, this.layout.Data.Width); + } + CaptureMouse(cursorClip); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] = true; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumn = index; + this.trackColumnEdge = -1; + + this.mouseBarOffset = GetColumnXFromIndex(index) - mouseX; + this.lastHeaderShadow = mouseX; + Invalidate(this.layout.ColumnHeaders); + } + + private void BeginColumnResize(int x, int columnIndex) + { + this.trackColAnchor = x; + this.trackColumn = columnIndex; + + this.currentColSplitBar = x; + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + private void BeginMouseColumnResize(int mouseX, int mouseBarOffset, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] = true; + this.mouseBarOffset = mouseBarOffset; + this.resizeClipRectangle = GetResizeClipRectangle(index); + CaptureMouse(this.resizeClipRectangle); + + BeginColumnResize(mouseX, index); + } + + private void BeginKeyboardColumnResize(int columnIndex) + { + if (this.IsMouseOperationActive()) + { + return; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] = true; + this.mouseBarOffset = 0; + this.resizeClipRectangle = GetResizeClipRectangle(columnIndex); + this.keyboardResizeStep = this.ScaleToCurrentDpi(this.RightToLeftInternal ? -1 : 1); + int x = GetColumnXFromIndex(columnIndex); + x += this.RightToLeftInternal ? -this.Columns[columnIndex].Width : this.Columns[columnIndex].Width; + + BeginColumnResize(x, columnIndex); + } + + private Rectangle GetResizeClipRectangle(int columnIndex) + { + Rectangle clip = Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data); + int leftEdge = GetColumnXFromIndex(columnIndex); + if (this.RightToLeftInternal) + { + clip.X = this.layout.Data.X - this.mouseBarOffset - 1; + clip.Width = leftEdge - this.Columns[columnIndex].MinimumThickness - this.layout.Data.X + 3; + int overflowWidth = leftEdge - this.mouseBarOffset - clip.Left - DataGridViewBand.maxBandThickness + 1; + if (overflowWidth > 0) + { + clip.X += overflowWidth; + clip.Width -= overflowWidth; + } + } + else + { + clip.X = leftEdge + this.Columns[columnIndex].MinimumThickness - this.mouseBarOffset - 1; + clip.Width = this.layout.Data.Right - leftEdge - 1; + int overflowWidth = clip.Right + this.mouseBarOffset - leftEdge - DataGridViewBand.maxBandThickness; + if (overflowWidth > 0) + { + clip.Width -= overflowWidth; + } + } + + return clip; + } + + /// + public virtual bool BeginEdit(bool selectAll) + { + if (this.ptCurrentCell.X == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_NoCurrentCell)); + } + + if (this.IsCurrentCellInEditMode) + { + return true; + } + + return BeginEditInternal(selectAll); + } + + private bool BeginEditInternal(bool selectAll) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_BeginEditNotReentrant)); + } + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit] = true; + Debug.Assert(this.ptCurrentCell.X >= 0 && this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y >= 0 && this.ptCurrentCell.Y < this.Rows.Count); + Debug.Assert(!this.IsCurrentCellInEditMode); + + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) || + !ColumnEditable(this.ptCurrentCell.X)) + { + return false; + } + + Type editControlType = dataGridViewCell.EditType; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + Type editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + if (editingCellInterface == null) + { + return false; + } + } + + DataGridViewCellCancelEventArgs dgvcce = new DataGridViewCellCancelEventArgs(this.ptCurrentCell.X, this.ptCurrentCell.Y); + OnCellBeginEdit(dgvcce); + if (dgvcce.Cancel) + { + return false; + } + Debug.Assert(!this.IsCurrentCellInEditMode); + + if (this.ptCurrentCell.X > -1) + { + DataGridViewCell previousCurrentCell = dataGridViewCell; + dataGridViewCell = this.CurrentCellInternal; + if (previousCurrentCell != dataGridViewCell) + { + // VSWhidbey 555494. The new current cell can be a whole different cell. + // In that case, all tests previously done are no longer valid. + if (IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) || + !ColumnEditable(this.ptCurrentCell.X)) + { + return false; + } + + editControlType = dataGridViewCell.EditType; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + Type editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + if (editingCellInterface == null) + { + return false; + } + } + } + } + else + { + return false; + } + + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + + if (editControlType == null) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] = true; + InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell); + ((IDataGridViewEditingCell)dataGridViewCell).PrepareEditingCellForEdit(selectAll); + return true; + } + + Type editingCtrlInterface = editControlType.GetInterface("System.Windows.Forms.IDataGridViewEditingControl"); + if (!editControlType.IsSubclassOf(Type.GetType("System.Windows.Forms.Control")) || + editingCtrlInterface == null) + { + throw new InvalidCastException(SR.GetString(SR.DataGridView_InvalidEditingControl)); + } + if (this.latestEditingControl != null && + editControlType.IsInstanceOfType(this.latestEditingControl) && + !this.latestEditingControl.GetType().IsSubclassOf(editControlType)) + { + this.editingControl = this.latestEditingControl; + Debug.Assert(((IDataGridViewEditingControl)this.editingControl).EditingControlDataGridView == this); + } + else + { + Debug.Assert(this.editingControl == null); + this.editingControl = (Control)SecurityUtils.SecureCreateInstance(editControlType); + Debug.Assert(this.editingControl != null); + + ((IDataGridViewEditingControl)this.editingControl).EditingControlDataGridView = this; + if (this.latestEditingControl != null) + { + this.latestEditingControl.Dispose(); + this.latestEditingControl = null; + } + } + + Debug.Assert(this.editingControl != null); + if (String.IsNullOrEmpty(this.editingControl.AccessibleName)) + { + this.editingControl.AccessibleName = SR.GetString(SR.DataGridView_AccEditingControlAccName); + } + this.editingControl.ImeMode = this.ImeMode; + + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = this.ptCurrentCell.Y; + + InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell); + + WireEditingControlEvents(); + + Debug.Assert(this.editingControl != null); + Debug.Assert(this.editingPanel != null); + DataGridViewEditingControlShowingEventArgs dgvese = new DataGridViewEditingControlShowingEventArgs(this.editingControl, dataGridViewCellStyle); + OnEditingControlShowing(dgvese); + Debug.Assert(dgvese.CellStyle != null); + if (this.editingPanel == null || this.editingControl == null) + { + return false; + } + this.editingPanel.BackColor = dgvese.CellStyle.BackColor; + ((IDataGridViewEditingControl)this.editingControl).ApplyCellStyleToEditingControl(dgvese.CellStyle); + + // Get rid of the tooltip if it's showing for the current cell + if (this.toolTipControl.Activated && this.ptToolTipCell == this.ptCurrentCell) + { + this.toolTipControl.Activate(false /*activate*/); + } + + PositionEditingControl(true, true, true); + + // Guarding against bugs in customer code. + // For example setting the CurrentCell to null in DataGridView_OnLostFocus(...) causes this.editingControl + // to become null. + if (this.editingPanel == null || this.editingControl == null) + { + return false; + } + else + { + ((IDataGridViewEditingControl)this.editingControl).PrepareEditingControlForEdit(selectAll); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + return true; + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inBeginEdit] = false; + } + } + + private void BeginRowHeadersResize(int mouseX, int mouseBarOffset) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.RowHeaders, this.layout.Data); + if (this.layout.TopLeftHeader.Width > 0) + { + clip = Rectangle.Union(this.layout.TopLeftHeader, clip); + } + if (this.RightToLeftInternal) + { + clip.X -= mouseBarOffset + 1; + clip.Width -= minimumRowHeadersWidth - 1; + // No need to limit the left edge of the cursor clip since maxHeadersThickness is very large. + } + else + { + clip.X += minimumRowHeadersWidth - mouseBarOffset - 1; + // No need to limit the right edge of the cursor clip since maxHeadersThickness is very large. + } + CaptureMouse(clip); + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] = true; + this.trackColAnchor = mouseX; + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastColSplitBar == -1); + this.currentColSplitBar = mouseX; + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + private void BeginRowResize(int mouseY, int mouseBarOffset, int index) + { + if (this.IsKeyboardOperationActive()) + { + return; + } + + Rectangle clip = Rectangle.Union(this.layout.RowHeaders, this.layout.Data); + int topEdge = GetRowYFromIndex(index); + clip.Y = topEdge + this.Rows.SharedRow(index).GetMinimumHeight(index) - mouseBarOffset - 1; + clip.Height = this.layout.Data.Y + this.layout.Data.Height - topEdge - 1; + CaptureMouse(clip); + + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] = true; + this.trackRowAnchor = mouseY; + this.trackRow = index; + + this.mouseBarOffset = mouseBarOffset; + Debug.Assert(this.lastRowSplitBar == -1); + this.currentRowSplitBar = mouseY; + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void BuildInheritedColumnHeaderCellStyle(DataGridViewCellStyle inheritedCellStyle, DataGridViewCell cell) + { + Debug.Assert(inheritedCellStyle != null); + + DataGridViewCellStyle cellStyle = null; + if (cell.HasStyle) + { + cellStyle = cell.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle columnHeadersStyle = this.ColumnHeadersDefaultCellStyle; + Debug.Assert(columnHeadersStyle != null); + + DataGridViewCellStyle dataGridViewStyle = this.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = cellStyle.BackColor; + } + else if (!columnHeadersStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = columnHeadersStyle.BackColor; + } + else + { + inheritedCellStyle.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = cellStyle.ForeColor; + } + else if (!columnHeadersStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = columnHeadersStyle.ForeColor; + } + else + { + inheritedCellStyle.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (!columnHeadersStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = columnHeadersStyle.SelectionBackColor; + } + else + { + inheritedCellStyle.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (!columnHeadersStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = columnHeadersStyle.SelectionForeColor; + } + else + { + inheritedCellStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyle.Font = cellStyle.Font; + } + else if (columnHeadersStyle.Font != null) + { + inheritedCellStyle.Font = columnHeadersStyle.Font; + } + else + { + inheritedCellStyle.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = cellStyle.NullValue; + } + else if (!columnHeadersStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = columnHeadersStyle.NullValue; + } + else + { + inheritedCellStyle.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (!columnHeadersStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = columnHeadersStyle.DataSourceNullValue; + } + else + { + inheritedCellStyle.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyle.Format = cellStyle.Format; + } + else if (columnHeadersStyle.Format.Length != 0) + { + inheritedCellStyle.Format = columnHeadersStyle.Format; + } + else + { + inheritedCellStyle.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = cellStyle.FormatProvider; + } + else if (!columnHeadersStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = columnHeadersStyle.FormatProvider; + } + else + { + inheritedCellStyle.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = cellStyle.Alignment; + } + else if (columnHeadersStyle != null && columnHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = columnHeadersStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyle.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = cellStyle.WrapMode; + } + else if (columnHeadersStyle != null && columnHeadersStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = columnHeadersStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyle.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyle.Tag = cellStyle.Tag; + } + else if (columnHeadersStyle.Tag != null) + { + inheritedCellStyle.Tag = columnHeadersStyle.Tag; + } + else + { + inheritedCellStyle.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = cellStyle.Padding; + } + else if (columnHeadersStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = columnHeadersStyle.Padding; + } + else + { + inheritedCellStyle.PaddingInternal = dataGridViewStyle.Padding; + } + } + + private Rectangle CalcColRelocationFeedbackRect(int mouseX) + { + Rectangle r, inside = this.layout.ColumnHeaders; + if (this.layout.TopLeftHeader.Width > 0) + { + inside = Rectangle.Union(this.layout.TopLeftHeader, inside); + } + if (this.RightToLeftInternal) + { + r = new Rectangle(mouseX + this.mouseBarOffset - this.Columns[this.trackColumn].Thickness + 1, + inside.Y, + this.Columns[this.trackColumn].Thickness, + inside.Height); + r.X = Math.Max(inside.Left, r.X); + r.X = Math.Min(r.X, inside.Right - r.Width); + } + else + { + r = new Rectangle(mouseX + this.mouseBarOffset - 1, inside.Y, this.Columns[this.trackColumn].Thickness, inside.Height); + r.X = Math.Min(inside.Right - r.Width, r.X); + r.X = Math.Max(r.X, inside.Left); + } + return r; + } + + private Rectangle CalcColResizeFeedbackRect(int mouseX) + { + Rectangle inside = this.layout.Data; + Rectangle r = new Rectangle(mouseX + this.mouseBarOffset - 1, inside.Y, 3, inside.Height); + if (this.RightToLeftInternal) + { + r.X = Math.Max(inside.Left, r.X); + } + else + { + r.X = Math.Min(inside.Right - 3, r.X); + r.X = Math.Max(r.X, 0); + } + return r; + } + + private Rectangle CalcRowResizeFeedbackRect(int mouseY) + { + Rectangle inside = this.layout.Data; + Rectangle r = new Rectangle(inside.X, mouseY + this.mouseBarOffset - 1, inside.Width, 3); + r.Y = Math.Min(inside.Bottom - 3, r.Y); + r.Y = Math.Max(r.Y, 0); + return r; + } + + /// + public bool CancelEdit() + { + return CancelEdit(false /*endEdit, DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration*/); + } + + private bool CancelEdit(bool endEdit /*, DataGridViewDataErrorContexts context*/) + { + if (this.ptCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + + int oldCurrentCellX = this.ptCurrentCell.X; + DataGridViewDataErrorEventArgs dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, context*/); + + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + + if (dgvdee.Cancel) + { + return false; + } + } + + if (this.IsCurrentCellInEditMode) + { + if (endEdit && this.EditMode != DataGridViewEditMode.EditOnEnter && this.editingControl != null) + { + bool success = EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration, + DataGridViewValidateCellInternal.Never /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell unused here*/, + true /*resetAnchorCell unused here*/); + Debug.Assert(success); + } + else + { + DataGridViewDataErrorEventArgs dgvdee2 = null; + IDataGridViewEditingCell dataGridViewEditingCell = null; + try + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlFormattedValue = this.uneditedFormattedValue; + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + dataGridViewEditingCell = this.CurrentCellInternal as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.EditingCellFormattedValue = this.uneditedFormattedValue; + dataGridViewEditingCell.EditingCellValueChanged = false; + } + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee2 = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee2 != null) + { + OnDataErrorInternal(dgvdee2); + if (dgvdee2.ThrowException) + { + throw dgvdee2.Exception; + } + } + + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + } + else + { + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + else if (this.ptCurrentCell.X == -1 && this.Focused) + { + Debug.Assert((this.AllowUserToAddRowsInternal && this.Rows.Count == 1) || + (!this.AllowUserToAddRowsInternal && this.Rows.Count == 0)); + if (this.Rows.Count > 0) + { + if (this.Columns.Count > oldCurrentCellX && this.Columns[oldCurrentCellX].Visible) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex != -1) + { + bool success = SetAndSelectCurrentCellAddress(oldCurrentCellX, + rowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + else + { + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + } + } + } + return true; + } + + private DataGridViewDataErrorEventArgs CancelEditPrivate(/*ref DataGridViewCell dataGridViewCurrentCell, DataGridViewDataErrorContexts context*/) + { + bool currentCellDirty = this.IsCurrentCellDirty; + bool currentRowDirty = this.IsCurrentRowDirty; + + if (this.IsCurrentCellInEditMode) + { + /* Do not push original value back into the cell - VS Whidbey bug 328624 + Exception exception; + if (!PushFormattedValue(ref dataGridViewCurrentCell, this.uneditedFormattedValue, out exception)) + { + Debug.Assert(dataGridViewCurrentCell.RowIndex > -1); + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + dataGridViewCurrentCell.ColumnIndex, + dataGridViewCurrentCell.RowIndex, + // dataGridViewCurrentCell.Value, + // this.uneditedFormattedValue, + context); + dgvdee.Cancel = true; + OnDataErrorInternal(dgvdee); + return dgvdee; + } + */ + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + ((IDataGridViewEditingCell)this.CurrentCellInternal).EditingCellValueChanged = false; + } + this.IsCurrentCellDirtyInternal = false; + } + + if (this.DataSource != null || this.VirtualMode) + { + if ((currentRowDirty && !currentCellDirty) || + (this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editedRowChanged])) + { + bool discardNewRow = this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited]; + this.IsCurrentRowDirtyInternal = false; + if (this.VirtualMode) + { + QuestionEventArgs qe = new QuestionEventArgs(discardNewRow); + OnCancelRowEdit(qe); + discardNewRow &= qe.Response; + } + if (this.DataSource != null) + { + int oldCurrentCellX = this.ptCurrentCell.X; + this.dataConnection.CancelRowEdit(true /*restoreRow*/, this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited]/*addNewFinished*/); + if (this.dataConnection.List.Count == 0) + { + // There are no rows left in the back end. + if (currentCellDirty || this.ptCurrentCell.Y == -1 || this.ptCurrentCell.X == -1) + { + if (!IsColumnOutOfBounds(oldCurrentCellX) && this.Columns[oldCurrentCellX].Visible) + { + Debug.Assert(0 == this.Rows.GetFirstRow(DataGridViewElementStates.Visible)); + // Setting the current cell to the current column in the first row + // will create the new row if the user was editing the cell. + SetAndSelectCurrentCellAddress(oldCurrentCellX, + 0, + true, /*setAnchorCellAddress*/ + false, /*validateCurrentCell*/ + false, /*throughMouseClick*/ + true /*clearSelecttion*/, + false /*forceCurrentCellSelection (unused)*/); + } + } + else + { + // Else, simply add a new row. + this.dataConnection.OnNewRowNeeded(); + } + } + + // CancelRowEdit discarded the new row if we were editing the new row. + discardNewRow = false; + } + if (this.ptCurrentCell.Y > -1) + { + InvalidateRowPrivate(this.ptCurrentCell.Y); + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + if (this.IsCurrentCellInEditMode) + { + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + if (this.editingControl != null) + { + InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell); + if (((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell); + } + } + } + if (discardNewRow && this.ptCurrentCell.Y == this.newRowIndex - 1) + { + DiscardNewRow(); + } + } + } + else + { + if (!this.IsCurrentRowDirty && + this.ptCurrentCell.Y == this.newRowIndex - 1 && + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing]) + { + DiscardNewRow(); + } + } + return null; + } + + internal bool CancelToolTipPopup(ToolTip toolTip) + { + if (this.toolTipControl.ToolTip == toolTip) + { + // Our own tool tip wants to show its text. + return false; + } + else + { + // This is an external tool tip control which wants to show a tool tip over the DataGridView. + // ToolTips from the data Grid view ( the error text, or the formatted text that does not fit in, or the tool tip text from the cell) + // and the ShowCellToolTips take precedence over the external tool tip. + return String.IsNullOrEmpty(this.toolTipCaption) && this.ShowCellToolTips; + } + } + + private bool CanSort(DataGridViewColumn dataGridViewColumn) + { + return dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && (!VirtualMode || dataGridViewColumn.IsDataBound); + } + + private bool IsSortable(DataGridViewColumn dataGridViewColumn) + { + return dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable && (!VirtualMode || dataGridViewColumn.IsDataBound); + } + + // determines if a data bound cell can be validated or not + private bool CanValidateDataBoundDataGridViewCell(DataGridViewCell dataGridViewCurrentCell) + { + if (dataGridViewCurrentCell == null) + { + if (this.ptCurrentCell.X > -1) + { + dataGridViewCurrentCell = this.CurrentCellInternal; + } + } + + if (dataGridViewCurrentCell == null) + { + return true; + } + + Debug.Assert(dataGridViewCurrentCell.OwningColumn != null); + + if (!dataGridViewCurrentCell.OwningColumn.IsDataBoundInternal) + { + // we are not data bound so it's not up to us to decide to stop validation + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + // Dispose is not the place to validate data. Also, chances are that the data source is also disposing itself. + return false; + } + + if (this.dataConnection == null) + { + // if there is no dataConnection then it is not up to this function to stop validation. + return true; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // // + // FROM HERE DOWN THE DATA CONNECTION DETERMINES IF THE DATAGRIDVIEW SHOULD VALIDATE THE CELL. // + // // + ///////////////////////////////////////////////////////////////////////////////////////////////// + + if (this.dataConnection.ProcessingMetaDataChanges) + { + // don't validate a cell in a data bound column while the property descriptors change under us + return false; + } + + if (this.dataConnection.CancellingRowEdit && !this.dataConnection.RestoreRow) + { + // don't validate a cell in a data bound column while we are cancelling a row edit and the old row is not restored + return false; + } + + if (this.dataConnection.CurrencyManager.Count <= this.ptCurrentCell.Y) + { + // don't validate a row beyond the last row in the back end list + return false; + } + + if (this.dataConnection.PositionChangingOutsideDataGridView) + { + // the position changed outside the data grid view and we haven't validated the data grid view cell already + // we can't validate it now because if the user cancels validation then we end up + // with a position different than the position in the currency manager + return false; + } + + if (this.dataConnection.ListWasReset) + { + // The list was reset outside data grid view. + // We can't validate it now because we would be pushing a value into a different object ( possibly located in a different list ). + return false; + } + + return true; + } + + private void CaptureMouse(Rectangle cursorClip) + { + this.CaptureInternal = true; + Cursor.ClipInternal = RectangleToScreen(cursorClip); + } + + private void ClearRegionCache() + { + this.cachedScrollableRegion = null; + } + + /// + public void ClearSelection() + { + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + + bool switchedToBulkPaint = false; + + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold || + this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + RemoveIndividuallySelectedCells(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and start using this.selectedBandIndexes in this SelectionMode, + // we'll have to clear those selections too. + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + while(this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + + // Force repainting of the current collumn's header cell to remove highlighting + if (this.ptCurrentCell.X != -1 && + this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && + AccessibilityImprovements.Level2) + { + InvalidateCellPrivate(this.ptCurrentCell.X, -1); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + while(this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + break; + } + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + /// + protected void ClearSelection(int columnIndexException, int rowIndexException, bool selectExceptionElement) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (columnIndexException < 0 || columnIndexException >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndexException"); + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (columnIndexException < -1 || columnIndexException >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndexException"); + } + break; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (rowIndexException < 0 || rowIndexException >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexException"); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (rowIndexException < -1 || rowIndexException >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexException"); + } + break; + } + } + + // Clears all selection except the row/column/cell specified as parameter + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + + bool switchedToBulkPaint = false; + + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold || + this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and start using this.selectedBandIndexes in this SelectionMode, + // we'll have to clear those selections too. + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != rowIndexException) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != columnIndexException) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + RemoveIndividuallySelectedCells(columnIndexException, rowIndexException); + } + break; + } + } + if (selectExceptionElement) + { + SetSelectedElementCore(columnIndexException, rowIndexException, true); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private bool ColumnEditable(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count, "Invalid columnIndex: " + columnIndex ); + if (this.Columns[columnIndex].IsDataBound && + this.dataConnection != null && + !this.dataConnection.AllowEdit) + { + return false; + } + return true; + } + + private bool ColumnNeedsDisplayedState(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + + if (!dataGridViewColumn.Visible) + { + return false; + } + + if (dataGridViewColumn.Frozen) + { + DataGridViewColumn firstVisibleFrozenColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(firstVisibleFrozenColumn != null); + if (firstVisibleFrozenColumn.Index == dataGridViewColumn.Index) + { + return this.displayedBandsInfo.NumDisplayedFrozenCols > 0; + } + Debug.Assert(this.Columns.DisplayInOrder(firstVisibleFrozenColumn.Index, dataGridViewColumn.Index)); + return this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, firstVisibleFrozenColumn.Index, dataGridViewColumn.Index) < this.displayedBandsInfo.NumDisplayedFrozenCols; + } + else + { + int firstDisplayedScrollingColumnIndex = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (firstDisplayedScrollingColumnIndex != -1) + { + if (firstDisplayedScrollingColumnIndex == dataGridViewColumn.Index) + { + return this.displayedBandsInfo.NumDisplayedScrollingCols > 0; + } + if (this.Columns.DisplayInOrder(firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index)) + { + return this.Columns.GetColumnCount(DataGridViewElementStates.Visible, firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index) < this.displayedBandsInfo.NumDisplayedScrollingCols; + } + } + } + return false; + } + + private bool ColumnRelocationTarget(MouseEventArgs e, HitTestInfo hti, out int previousColumnIndex) + { + previousColumnIndex = -1; + if (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeader || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight) + { + Debug.Assert(hti.col != -1); + if (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeader) + { + int xColumnLeftEdge = GetColumnXFromIndex(hti.col); + int wColumn = this.Columns[hti.col].Width; + if ((this.RightToLeftInternal && e.X < xColumnLeftEdge - wColumn / 2) || + (!this.RightToLeftInternal && e.X > xColumnLeftEdge + wColumn / 2)) + { + // Insert column on the right of hti.col + previousColumnIndex = hti.col; + } + else + { + // Insert column on the left of hti.col + DataGridViewColumn dataGridViewColumnPrev = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnPrev != null) + { + previousColumnIndex = dataGridViewColumnPrev.Index; + } + } + } + else + { + previousColumnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight) ? + hti.col : hti.adjacentCol; + } + DataGridViewColumn dataGridViewColumnNext = null; + if (previousColumnIndex != -1) + { + dataGridViewColumnNext = this.Columns.GetNextColumn(this.Columns[previousColumnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + if (this.trackColumn != previousColumnIndex && + !(previousColumnIndex == -1 && hti.col == this.trackColumn) && + (dataGridViewColumnNext == null || this.trackColumn != dataGridViewColumnNext.Index)) + { + return true; + } + } + else if (hti.typeInternal == DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight) + { + Debug.Assert(hti.col != -1); + if (hti.col != this.trackColumn) + { + return true; + } + } + return false; + } + + private static bool ColumnsDisplayInOrder(int columnIndex1, + int columnDisplayIndex1, + int columnIndex2, + int columnDisplayIndex2) + { + return columnDisplayIndex1 < columnDisplayIndex2 || + (columnDisplayIndex1 == columnDisplayIndex2 && columnIndex1 < columnIndex2); + } + + /// + public bool CommitEdit(DataGridViewDataErrorContexts context) + { + if (this.IsCurrentCellInEditMode) + { + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + context, + DataGridViewValidateCellInternal.Never, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + return true; + } + + private DataGridViewDataErrorEventArgs CommitEdit(ref DataGridViewCell dataGridViewCurrentCell, + DataGridViewDataErrorContexts context, + DataGridViewValidateCellInternal validateCell, + bool fireCellLeave, + bool fireCellEnter, + bool fireRowLeave, + bool fireRowEnter, + bool fireLeave) + { + if (validateCell == DataGridViewValidateCellInternal.Always) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (fireCellLeave) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellLeave(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (fireRowLeave) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnRowLeave(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (fireLeave) + { + base.OnLeave(EventArgs.Empty); + + // Microsoft: can we be smarter about this? What if validating the current cell below forces a repaint on the cell? + // we would end up repainting the current cell twice. + // + // invalidate the current cell so the data grid view does not paint the focus rectangle any longer + if (this.ptCurrentCell.X > -1 && this.ptCurrentCell.Y > -1) + { + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + // OnCellValidating returns true if the dev cancelled the validation. + bool validateFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell); + if (validateFormattedValue) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + if (OnCellValidating(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, context)) + { + if (fireRowEnter) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnRowEnter(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + } + if (fireCellEnter) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellEnter(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(null, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + // null, + // null, + context); + dgvdee.Cancel = true; + return dgvdee; + } + + if (!this.IsCurrentCellInEditMode || !this.IsCurrentCellDirty) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellValidated(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + + if (this.ptCurrentCell.X == -1 || !this.IsCurrentCellInEditMode) + { + return null; + } + + Debug.Assert( + ( + (this.editingControl != null && ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged) || + (this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] && ((IDataGridViewEditingCell)this.CurrentCellInternal).EditingCellValueChanged) + ) == this.IsCurrentCellDirty || + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges]); + + if (this.IsCurrentCellDirty) + { + bool validateAndPushFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell); + if (validateAndPushFormattedValue) + { + if (validateCell == DataGridViewValidateCellInternal.WhenChanged) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (this.ptCurrentCell.X == -1) + { + return null; + } + if (OnCellValidating(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, context)) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(null, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + context); + dgvdee.Cancel = true; + return dgvdee; + } + } + + Exception exception; + object formattedValue; + + if (this.editingControl != null) + { + formattedValue = ((IDataGridViewEditingControl)this.editingControl).GetEditingControlFormattedValue(context); + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + formattedValue = ((IDataGridViewEditingCell)this.CurrentCellInternal).GetEditingCellFormattedValue(context); + } + + if (!PushFormattedValue(ref dataGridViewCurrentCell, formattedValue, out exception)) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, + this.ptCurrentCell.X, + this.ptCurrentCell.Y, + // dataGridViewCurrentCell.Value, + // formattedValue, + context); + dgvdee.Cancel = true; + OnDataErrorInternal(dgvdee); + return dgvdee; + } + if (!this.IsCurrentCellInEditMode) + { + return null; + } + this.uneditedFormattedValue = formattedValue; + } + + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + ((IDataGridViewEditingCell) this.CurrentCellInternal).EditingCellValueChanged = false; + } + this.IsCurrentCellDirtyInternal = false; + this.IsCurrentRowDirtyInternal = true; + + if (validateAndPushFormattedValue) + { + if (validateCell == DataGridViewValidateCellInternal.Always || + validateCell == DataGridViewValidateCellInternal.WhenChanged) + { + if (this.ptCurrentCell.X == -1) + { + return null; + } + OnCellValidated(ref dataGridViewCurrentCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + } + return null; + } + + private bool CommitEdit(DataGridViewDataErrorContexts context, + bool forCurrentCellChange, + bool forCurrentRowChange) + { + // If we're already within a CellValidating event handler, don't try to commit the cell again. + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating]) + { + return false; + } + + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + context, + forCurrentCellChange ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.WhenChanged /*validateCell*/, + forCurrentCellChange /*fireCellLeave*/, + forCurrentCellChange /*fireCellEnter*/, + forCurrentRowChange /*fireRowLeave*/, + forCurrentRowChange /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, + DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll*/); // restore old value + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + + // See if we can leave the row + if (forCurrentRowChange && forCurrentCellChange) + { + Debug.Assert(this.ptCurrentCell.X > -1); + if (this.ptCurrentCell.X == -1) + { + return false; + } + int columnIndex = this.ptCurrentCell.X; + int rowIndex = this.ptCurrentCell.Y; + // OnRowValidating returns true when the row validation was cancelled. + if (OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex)) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnRowEnter(ref dataGridViewCurrentCell, columnIndex, rowIndex, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnCellEnter(ref dataGridViewCurrentCell, columnIndex, rowIndex); + return false; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex); + } + return true; + } + + private bool CommitEditForOperation(int columnIndex, int rowIndex, bool forCurrentCellChange) + { + if (forCurrentCellChange) + { + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + this.ptCurrentCell.Y != rowIndex /*fireRowLeave*/, + this.ptCurrentCell.Y != rowIndex /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (this.ptCurrentCell.Y != rowIndex && this.ptCurrentCell.Y != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + int columnIndexTmp = this.ptCurrentCell.X; + int rowIndexTmp = this.ptCurrentCell.Y; + if (OnRowValidating(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnRowEnter(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnCellEnter(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp); + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + + // Re-enter editing mode if needed + if (this.Focused && + (!this.IsCurrentCellInEditMode && (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)))) + { + BeginEditInternal(true /*selectAll*/); + } + + return false; + } + if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp)) + { + return false; + } + OnRowValidated(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp); + } + } + else + { + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + } + + // Row validation was not cancelled, but operation needs to be re-evaluated. + Debug.Assert(columnIndex < this.Columns.Count); + if (IsColumnOutOfBounds(columnIndex)) + { + return false; + } + if (rowIndex >= this.Rows.Count) + { + // CurrentCell was reset because the commit deleted row(s). + // Since the user wants to change the current cell, we don't + // want to end up with no CurrentCell. We pick the last visible + // row in the grid which may be the 'new row'. + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (forCurrentCellChange && + this.ptCurrentCell.X == -1 && + lastVisibleRowIndex != -1) + { + bool success = SetAndSelectCurrentCellAddress(columnIndex, lastVisibleRowIndex, true, false, false, false /*clearSelection*/, false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + // Interrupt operation because it has become invalid. + return false; + } + if (rowIndex > -1 && (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + // Interrupt operation because target row has become invisible. + return false; + } + return true; + } + + internal void CompleteCellsCollection(DataGridViewRow dataGridViewRow) + { + Debug.Assert(dataGridViewRow != null); + int cellsInCollection = dataGridViewRow.Cells.Count; + if (this.Columns.Count > cellsInCollection) + { + int cellCount = 0; + DataGridViewCell[] cells = new DataGridViewCell[this.Columns.Count - cellsInCollection]; + for (int columnIndex = cellsInCollection; columnIndex < this.Columns.Count; columnIndex++) + { + if (this.Columns[columnIndex].CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AColumnHasNoCellTemplate)); + } + DataGridViewCell dgvcNew = (DataGridViewCell) this.Columns[columnIndex].CellTemplate.Clone(); + cells[cellCount] = dgvcNew; + cellCount ++; + } + dataGridViewRow.Cells.AddRange(cells); + } + } + + /// + /// Determines which column is the first visible scrolling + /// column given the object's horizontalOffset. + /// + private int ComputeFirstVisibleScrollingColumn() + { + if (this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) >= this.layout.Data.Width) + { + // Not enough room for scrolling columns. + this.negOffset = 0; + return -1; + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + + if (this.horizontalOffset == 0) + { + this.negOffset = 0; + return (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + } + + int cx = 0; + while (dataGridViewColumn != null) + { + cx += dataGridViewColumn.Thickness; + if (cx > this.horizontalOffset) + { + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + + if (dataGridViewColumn == null) + { + Debug.Assert(cx <= this.horizontalOffset); + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (dataGridViewColumn == null) + { + this.negOffset = 0; + return -1; + } + else + { + if (this.negOffset != this.horizontalOffset) + { + this.negOffset = 0; + } + return dataGridViewColumn.Index; + } + } + else + { + this.negOffset = dataGridViewColumn.Thickness - (cx - this.horizontalOffset); + return dataGridViewColumn.Index; + } + } + + private int ComputeHeightOfFittingTrailingScrollingRows(int totalVisibleFrozenHeight) + { + // + int displayHeight = this.layout.Data.Height - totalVisibleFrozenHeight; + int rowHeight = 0, rowHeights = 0; + int indexTmp = this.Rows.Count; + + if (indexTmp == 0 || displayHeight <= 0) + { + return 0; + } + else + { + indexTmp--; + } + + DataGridViewElementStates rowState = this.Rows.GetRowState(indexTmp); + if ((rowState & DataGridViewElementStates.Frozen) != 0) + { + return 0; + } + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + if (indexTmp != -1) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (rowHeight > displayHeight) + { + return rowHeight; + } + } + + while (indexTmp != -1 && rowHeights + rowHeight <= displayHeight) + { + rowHeights += rowHeight; + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (indexTmp != -1) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + } + } + + return rowHeights; + } + + private int ComputeHeightOfScrolledOffRows() + { + // + int height = 0; + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowIndex != -1) + { + while (rowIndex != this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + Debug.Assert(rowIndex < this.displayedBandsInfo.FirstDisplayedScrollingRow); + height += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + } + return height; + } + + private int ComputeHeightOfTrailingScrollingRows() + { + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + return this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, lastVisibleRowIndex) + + this.Rows.SharedRow(lastVisibleRowIndex).GetHeight(lastVisibleRowIndex); + } + return 0; + } + + private bool ComputeLayout() + { + ClearRegionCache(); + + LayoutData newLayout = new LayoutData(this.layout); + Rectangle oldResizeRect = this.layout.ResizeBoxRect; + + // Inside region + if (this.normalClientRectangle.Width > 0 || this.normalClientRectangle.Height > 0) + { + newLayout.Inside = this.normalClientRectangle; + } + else + { + newLayout.Inside = this.ClientRectangle; + } + Rectangle inside = newLayout.Inside; + int borderWidth = this.BorderWidth; + inside.Inflate(-borderWidth, -borderWidth); + if (inside.Height < 0) + { + inside.Height = 0; + } + if (inside.Width < 0) + { + inside.Width = 0; + } + + Rectangle insideLeft = inside; + + // Headers + if (this.layout.ColumnHeadersVisible) + { + Rectangle colHeaders = insideLeft; + colHeaders.Height = Math.Min(this.columnHeadersHeight, colHeaders.Height); + insideLeft.Y += colHeaders.Height; + insideLeft.Height -= colHeaders.Height; + Debug.Assert(insideLeft.Height >= 0); + newLayout.ColumnHeaders = colHeaders; + } + else + { + newLayout.ColumnHeaders = Rectangle.Empty; + } + + if (this.layout.RowHeadersVisible) + { + Rectangle rowHeaders = insideLeft; + rowHeaders.Width = Math.Min(this.rowHeadersWidth, rowHeaders.Width); + if (this.RightToLeftInternal) + { + rowHeaders.X += insideLeft.Width - rowHeaders.Width; + } + else + { + insideLeft.X += rowHeaders.Width; + } + insideLeft.Width -= rowHeaders.Width; + Debug.Assert(insideLeft.Width >= 0); + newLayout.RowHeaders = rowHeaders; + + if (this.layout.ColumnHeadersVisible) + { + Rectangle topLeft; + Rectangle colHeaders = newLayout.ColumnHeaders; + topLeft = colHeaders; + topLeft.Width = Math.Min(this.rowHeadersWidth, topLeft.Width); + colHeaders.Width -= topLeft.Width; + if (this.RightToLeftInternal) + { + topLeft.X += insideLeft.Width; + } + else + { + colHeaders.X += topLeft.Width; + } + Debug.Assert(colHeaders.Width >= 0); + newLayout.TopLeftHeader = topLeft; + newLayout.ColumnHeaders = colHeaders; + } + else + { + newLayout.TopLeftHeader = Rectangle.Empty; + } + } + else + { + newLayout.RowHeaders = Rectangle.Empty; + newLayout.TopLeftHeader = Rectangle.Empty; + } + + // Adjust insideLeft in case static top / left edge needs to be painted + if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + insideLeft.X++; + } + if (insideLeft.Width > 0) + { + insideLeft.Width--; + } + } + if (this.SingleHorizontalBorderAdded) + { + insideLeft.Y++; + if (insideLeft.Height > 0) + { + insideLeft.Height--; + } + } + + // Data region + newLayout.Data = insideLeft; + newLayout.Inside = inside; + + Debug.Assert(newLayout.Data.X >= 0); + Debug.Assert(newLayout.Data.Y >= 0); + Debug.Assert(newLayout.Data.Width >= 0); + Debug.Assert(newLayout.Data.Height >= 0); + + this.layout = newLayout; + this.layout.dirty = false; + + bool columnsAdjusted = AdjustFillingColumns(); + + this.layout = newLayout; + Debug.Assert(!this.layout.dirty); + LayoutScrollBars(); + + // if the user shrank the grid client area, then OnResize invalidated the old + // resize area. however, we need to invalidate the left upper corner in the new ResizeArea + // note that we can't take the Invalidate call from the OnResize method, because if the + // user enlarges the form then the old area will not be invalidated. + // + if (!oldResizeRect.Equals(this.layout.ResizeBoxRect) && !this.layout.ResizeBoxRect.IsEmpty) + { + Invalidate(this.layout.ResizeBoxRect); + } + + return columnsAdjusted; + } + + private void ComputeLayoutShortcut(bool computeVisibleRows) + { + // Called instead of ComputeLayout when a row is added, inserted or deleted beyond the limits of + // the layout.Data area. + // this.layout is unchanged - only the potential vertical scrollbar is affected. + + if (computeVisibleRows) + { + ComputeVisibleRows(); + } + #if DEBUG + else + { + int oldNumTotallyVisibleFrozenRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + int oldNumVisibleScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + int oldNumTotallyVisibleScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + ComputeVisibleRows(); + Debug.Assert(oldNumTotallyVisibleFrozenRows == this.displayedBandsInfo.NumTotallyDisplayedFrozenRows); + Debug.Assert(oldNumVisibleScrollingRows == this.displayedBandsInfo.NumDisplayedScrollingRows); + Debug.Assert(oldNumTotallyVisibleScrollingRows == this.displayedBandsInfo.NumTotallyDisplayedScrollingRows); + Debug.Assert(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + #endif + + #if DEBUG + int newFirstVisibleScrollingCol = ComputeFirstVisibleScrollingColumn(); + Debug.Assert(newFirstVisibleScrollingCol == this.displayedBandsInfo.FirstDisplayedScrollingCol); + + int oldLastTotallyVisibleScrollingCol = this.displayedBandsInfo.LastTotallyDisplayedScrollingCol; + int oldFirstVisibleScrollingCol = this.displayedBandsInfo.FirstDisplayedScrollingCol; + ComputeVisibleColumns(); + Debug.Assert(oldLastTotallyVisibleScrollingCol == this.displayedBandsInfo.LastTotallyDisplayedScrollingCol); + Debug.Assert(oldFirstVisibleScrollingCol == this.displayedBandsInfo.FirstDisplayedScrollingCol); + #endif + + if (this.vertScrollBar.Enabled) + { + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int oldVertScrollBarValue = this.vertScrollBar.Value; + int oldThumbHeight = Math.Max(((this.vertScrollBar.Height - 2*SystemInformation.VerticalScrollBarArrowHeight) * this.vertScrollBar.LargeChange) / this.vertScrollBar.Maximum, 8); + + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + Debug.Assert(this.vertScrollBar.Maximum > 0); + this.vertScrollBar.Value = ComputeHeightOfScrolledOffRows(); + this.vertScrollBar.LargeChange = this.layout.Data.Height - totalVisibleFrozenHeight; + this.verticalOffset = this.vertScrollBar.Value; + + if (this.vertScrollBar.Visible && + (oldVertScrollBarValue != this.verticalOffset || + oldThumbHeight != Math.Max(((this.vertScrollBar.Height - 2*SystemInformation.VerticalScrollBarArrowHeight) * this.vertScrollBar.LargeChange) / this.vertScrollBar.Maximum, 8))) + { + // Only update the vertical scroll bar is the thumb moved or resized. + this.vertScrollBar.Invalidate(); + } + Debug.Assert(this.verticalOffset == this.vertScrollBar.Value); + } + } + + /* Unused for now + private int ComputeScrolledOffRowCount(int scrolledOffRowsHeight) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (rowIndex == -1) + { + // No scrolling rows + return 0; + } + else + { + int height = 0; + int rowCount = 0; + while (rowIndex != -1 && height < scrolledOffRowsHeight) + { + height += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + if (height <= scrolledOffRowsHeight) + { + rowCount++; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + return rowCount; + } + } + */ + + private void ComputeVisibleColumns() + { + DataGridViewColumn dataGridViewColumn = null; + int numVisibleScrollingCols = 0, visibleScrollingColumnsTmp = 0; + int displayWidth = this.layout.Data.Width, cx = 0; + int numDisplayedFrozenCols = 0, firstDisplayedFrozenCol = -1, lastDisplayedFrozenCol = -1; + int firstDisplayedScrollingCol = this.displayedBandsInfo.FirstDisplayedScrollingCol; + + // the same problem with negative numbers: + // if the width passed in is negative, then return 0 + if (displayWidth <= 0 || this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = -1; + this.displayedBandsInfo.NumDisplayedFrozenCols = 0; + this.displayedBandsInfo.FirstDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + this.displayedBandsInfo.LastDisplayedFrozenCol = -1; + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + return; + } + + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.None); + while (dataGridViewColumn != null) + { + if (!dataGridViewColumn.Frozen && dataGridViewColumn.Visible) + { + break; + } + if (dataGridViewColumn.Visible) + { + if (firstDisplayedFrozenCol == -1) + { + firstDisplayedFrozenCol = dataGridViewColumn.Index; + } + cx += dataGridViewColumn.Width; + numDisplayedFrozenCols++; + lastDisplayedFrozenCol = dataGridViewColumn.Index; + if (cx >= displayWidth) + { + break; + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.None, DataGridViewElementStates.None); + } + + Debug.Assert(cx <= this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + + if (cx < displayWidth && firstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[firstDisplayedScrollingCol]; + if (dataGridViewColumn.Frozen) + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + this.negOffset = 0; + if (dataGridViewColumn == null) + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol; + this.displayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol; + this.displayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols; + this.displayedBandsInfo.FirstDisplayedScrollingCol = this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + return; + } + else + { + firstDisplayedScrollingCol = dataGridViewColumn.Index; + } + } + + cx -= this.negOffset; + while (cx < displayWidth && dataGridViewColumn != null) + { + cx += dataGridViewColumn.Thickness; + visibleScrollingColumnsTmp++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + numVisibleScrollingCols = visibleScrollingColumnsTmp; + + // if we inflate the data area then we paint columns to the left of firstDisplayedScrollingCol + if (cx < displayWidth) + { + bool invalidate = false; + Debug.Assert(firstDisplayedScrollingCol >= 0); + //first minimize value of this.negOffset + if (this.negOffset > 0) + { + invalidate = true; + if (displayWidth - cx > this.negOffset) + { + cx += this.negOffset; + this.horizontalOffset -= this.negOffset; + this.negOffset = 0; + } + else + { + this.horizontalOffset -= displayWidth - cx; + this.negOffset -= displayWidth - cx; + cx = displayWidth; + } + } + // second try to scroll entire columns + if (cx < displayWidth && this.horizontalOffset > 0) + { + Debug.Assert(this.negOffset == 0); + dataGridViewColumn = this.Columns.GetPreviousColumn((this.Columns[firstDisplayedScrollingCol]), + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && cx + dataGridViewColumn.Thickness <= displayWidth) + { + cx += dataGridViewColumn.Thickness; + visibleScrollingColumnsTmp++; + invalidate = true; + firstDisplayedScrollingCol = dataGridViewColumn.Index; + this.horizontalOffset -= dataGridViewColumn.Thickness; + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + // third try to partially scroll in first scrolled off column + if (cx < displayWidth && this.horizontalOffset > 0 && firstDisplayedScrollingCol != 0) + { + Debug.Assert(this.negOffset == 0); + dataGridViewColumn = this.Columns.GetPreviousColumn((this.Columns[firstDisplayedScrollingCol]), + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Thickness > displayWidth - cx); + firstDisplayedScrollingCol = dataGridViewColumn.Index; + this.negOffset = dataGridViewColumn.Thickness - displayWidth + cx; + this.horizontalOffset -= displayWidth - cx; + visibleScrollingColumnsTmp++; + invalidate = true; + cx = displayWidth; + Debug.Assert(this.negOffset == GetNegOffsetFromHorizontalOffset(this.horizontalOffset)); + } + + // update the number of visible columns to the new reality + Debug.Assert(numVisibleScrollingCols <= visibleScrollingColumnsTmp, "the number of displayed columns can only grow"); + numVisibleScrollingCols = visibleScrollingColumnsTmp; + + if (invalidate) + { + InvalidateData(); + Invalidate(this.layout.ColumnHeaders); + } + } + + int jumpFromFirstVisibleScrollingCol = numVisibleScrollingCols - 1; + if (cx > displayWidth) + { + jumpFromFirstVisibleScrollingCol--; + } + + Debug.Assert(jumpFromFirstVisibleScrollingCol >= -1); + + if (jumpFromFirstVisibleScrollingCol < 0) + { + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; // no totally visible scrolling column at all + } + else + { + Debug.Assert(firstDisplayedScrollingCol >= 0); + dataGridViewColumn = this.Columns[firstDisplayedScrollingCol]; + for (int jump = 0; jump < jumpFromFirstVisibleScrollingCol; jump++) + { + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + } + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = dataGridViewColumn.Index; + } + } + else + { + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + } + this.displayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol; + this.displayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol; + this.displayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols; + this.displayedBandsInfo.FirstDisplayedScrollingCol = firstDisplayedScrollingCol; + this.displayedBandsInfo.NumDisplayedScrollingCols = numVisibleScrollingCols; + Debug.Assert((this.displayedBandsInfo.NumDisplayedScrollingCols > 0 && this.displayedBandsInfo.FirstDisplayedScrollingCol != -1) || + (this.displayedBandsInfo.NumDisplayedScrollingCols == 0 && this.displayedBandsInfo.FirstDisplayedScrollingCol == -1)); + } + + private void ComputeVisibleRows() + { + int firstDisplayedFrozenRow = -1; + int firstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int lastDisplayedFrozenRow = -1, lastDisplayedScrollingRow = -1; + int numTotallyDisplayedFrozenRows = 0; + int displayHeight = this.layout.Data.Height; + int cy = 0; + int visibleScrollingRows = 0; + int nRows = this.Rows.Count; + int rowIndex; + + // when minimizing the dataGridView window, we will get negative values for the + // layout.Data.Width and layout.Data.Height ( is this a bug or not? if layout.Data.Height == 0 in that case, + // the old code would have worked ) + // + // if this is the case, set numTotallyDisplayedFrozenRows = numDisplayedScrollingRows = numTotallyDisplayedScrollingRows = 0; + // + if (displayHeight <= 0 || nRows == 0) + { + this.displayedBandsInfo.NumDisplayedFrozenRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = + this.displayedBandsInfo.NumDisplayedScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + this.displayedBandsInfo.FirstDisplayedFrozenRow = this.displayedBandsInfo.FirstDisplayedScrollingRow = + this.displayedBandsInfo.LastDisplayedFrozenRow = this.displayedBandsInfo.LastDisplayedScrollingRow = -1; + return; + } + + for (rowIndex = 0; rowIndex < nRows; rowIndex++) + { + Debug.Assert(cy < displayHeight); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Frozen) == 0 && + (rowState & DataGridViewElementStates.Visible) != 0) + { + break; + } + if ((rowState & DataGridViewElementStates.Visible) != 0) + { + cy += this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + if (firstDisplayedFrozenRow == -1) + { + firstDisplayedFrozenRow = rowIndex; + } + lastDisplayedFrozenRow = rowIndex; + if (cy <= displayHeight) + { + numTotallyDisplayedFrozenRows++; + } + if (cy >= displayHeight) + { + break; + } + } + } + + if (cy > displayHeight) + { + this.displayedBandsInfo.NumDisplayedFrozenRows = numTotallyDisplayedFrozenRows + 1; + } + else + { + this.displayedBandsInfo.NumDisplayedFrozenRows = numTotallyDisplayedFrozenRows; + } + + // loop exited when: + // - all rows are frozen and fit in displayHeight: rowIndex == nRows, cy <= displayHeight + // - rowIndex is not frozen: rowIndex < nRows, cy <= displayHeight + // - there are more frozen rows than can fit in displayHeight: rowIndex <= nRows, cy > displayHeight + + if (cy < displayHeight && rowIndex < nRows) + { + if (firstDisplayedScrollingRow == -1) + { + firstDisplayedScrollingRow = rowIndex; + } + + while (firstDisplayedScrollingRow < nRows && + ( + (this.Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Frozen) != 0 || + (this.Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Visible) == 0 + ) + ) + { + firstDisplayedScrollingRow++; + } + + for (int i = firstDisplayedScrollingRow; i < nRows; i++) + { + if ((this.Rows.GetRowState(i) & DataGridViewElementStates.Visible) != 0) + { + cy += this.Rows.SharedRow(i).GetHeight(i); + visibleScrollingRows++; + lastDisplayedScrollingRow = i; + } + if (cy >= displayHeight) + { + break; + } + } + + if (cy < displayHeight) + { + for (int i = firstDisplayedScrollingRow - 1; i >= numTotallyDisplayedFrozenRows; i--) + { + if ((this.Rows.GetRowState(i) & (DataGridViewElementStates.Frozen | DataGridViewElementStates.Visible)) == DataGridViewElementStates.Visible) + { + int height = this.Rows.SharedRow(i).GetHeight(i); + if (cy + height > displayHeight) + { + break; + } + cy += height; + firstDisplayedScrollingRow = i; + visibleScrollingRows++; + lastDisplayedScrollingRow = i; + } + } + } + + this.displayedBandsInfo.NumDisplayedScrollingRows = visibleScrollingRows; + if (cy > displayHeight) + { + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = visibleScrollingRows - 1; + } + else + { + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = visibleScrollingRows; + } + if (visibleScrollingRows == 0) + { + firstDisplayedScrollingRow = -1; + Debug.Assert(lastDisplayedScrollingRow == -1); + } + } + else + { + this.displayedBandsInfo.NumDisplayedScrollingRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + firstDisplayedScrollingRow = -1; + } + + Debug.Assert(firstDisplayedFrozenRow < nRows, "firstDisplayedFrozenRow larger than number of rows"); + Debug.Assert(lastDisplayedFrozenRow < nRows, "lastDisplayedFrozenRow larger than number of rows"); + Debug.Assert(lastDisplayedScrollingRow < nRows, "lastDisplayedScrollingRow larger than number of rows"); + + this.displayedBandsInfo.FirstDisplayedFrozenRow = firstDisplayedFrozenRow; + this.displayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRow; + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = numTotallyDisplayedFrozenRows; + this.displayedBandsInfo.LastDisplayedFrozenRow = lastDisplayedFrozenRow; + this.displayedBandsInfo.LastDisplayedScrollingRow = lastDisplayedScrollingRow; + + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedFrozenRows >= 0, "the number of visible frozen rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.NumDisplayedScrollingRows >= 0, "the number of visible scrolling rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.NumTotallyDisplayedScrollingRows >= 0, "the number of totally visible scrolling rows can't be negative"); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow < nRows, "firstDisplayedScrollingRow larger than number of rows"); + } + + private Point ConvertCellToGridCoord(int columnIndex, int rowIndex, int x, int y) + { + int columnX, rowY; + if (columnIndex > -1) + { + columnX = GetColumnXFromIndex(columnIndex); + if (this.RightToLeftInternal) + { + columnX -= this.Columns[columnIndex].Width; + } + } + else + { + if (this.RightToLeftInternal) + { + columnX = this.layout.RowHeaders.Left - 1; + } + else + { + columnX = this.layout.RowHeaders.Left; + } + } + + if (rowIndex > -1) + { + rowY = GetRowYFromIndex(rowIndex); + } + else + { + rowY = this.layout.ColumnHeaders.Top; + } + + return new Point(columnX + x, rowY + y); + } + + private void CorrectColumnDisplayIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn) + { + // Column indexes have already been adjusted. + // This column has already been detached and has retained its old Index and DisplayIndex + + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == null); + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // All remaining columns with a DisplayIndex greater than dataGridViewColumn.DisplayIndex need to be decremented + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp.DisplayIndex > dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + // Now raise all the OnColumnDisplayIndexChanged events + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + FlushDisplayIndexChanged(false /*raiseEvent*/); + } + } + + private void CorrectColumnDisplayIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == this); + // dataGridViewColumn.DisplayIndex has been set already. + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0); + Debug.Assert(dataGridViewColumn.DisplayIndex < this.Columns.Count); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // All other columns with a DisplayIndex equal or greater than dataGridViewColumn.DisplayIndex need to be incremented + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumnTmp != dataGridViewColumn && dataGridViewColumnTmp.DisplayIndex >= dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + // Now raise all the OnColumnDisplayIndexChanged events + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + FlushDisplayIndexChanged(false /*raiseEvent*/); + } + } + + private void CorrectColumnFrozenState(DataGridViewColumn dataGridViewColumn, int anticipatedColumnIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(anticipatedColumnIndex >= 0 && anticipatedColumnIndex <= this.Columns.Count); + + int anticipatedColumnDisplayIndex; + if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex > this.Columns.Count) + { + anticipatedColumnDisplayIndex = anticipatedColumnIndex; // By default, we pick the Index as the DisplayIndex. + } + else + { + Debug.Assert(dataGridViewColumn.DisplayIndex >= 0 && dataGridViewColumn.DisplayIndex <= this.Columns.Count); + anticipatedColumnDisplayIndex = dataGridViewColumn.DisplayIndex; // The specified DisplayIndex is just fine. + } + + DataGridViewColumn dataGridViewColumnPrev; + int displayIndex = anticipatedColumnDisplayIndex-1; + do + { + dataGridViewColumnPrev = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex--; + } + while (displayIndex >= 0 && (dataGridViewColumnPrev == null || !dataGridViewColumnPrev.Visible)); + if (dataGridViewColumnPrev != null && !dataGridViewColumnPrev.Frozen && dataGridViewColumn.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenColumn)); + } + else + { + DataGridViewColumn dataGridViewColumnNext; + displayIndex = anticipatedColumnDisplayIndex; + do + { + dataGridViewColumnNext = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex++; + } + while (displayIndex < this.Columns.Count && (dataGridViewColumnNext == null || !dataGridViewColumnNext.Visible)); + if (dataGridViewColumnNext != null && dataGridViewColumnNext.Frozen && !dataGridViewColumn.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenColumn)); + } + } + } + + private void CorrectColumnFrozenStates(DataGridViewColumn[] dataGridViewColumns) + { + DataGridView dataGridViewTmp = new DataGridView(); + DataGridViewColumn dataGridViewColumnClone; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + dataGridViewColumnClone = (DataGridViewColumn) dataGridViewColumn.Clone(); + // DataGridViewColumn.Clone does not replicate the DisplayIndex value. + dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex; + dataGridViewTmp.Columns.Add(dataGridViewColumnClone); + } + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + dataGridViewColumnClone = (DataGridViewColumn) dataGridViewColumn.Clone(); + dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex; + dataGridViewTmp.Columns.Add(dataGridViewColumnClone); + } + } + + private void CorrectColumnFrozenStates(DataGridViewColumn dataGridViewColumn, bool frozenStateChanging) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumn dataGridViewColumnTmp; + if ((dataGridViewColumn.Frozen && !frozenStateChanging) || + (!dataGridViewColumn.Frozen && frozenStateChanging)) + { + // make sure the previous visible columns are frozen as well + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + if (dataGridViewColumnTmp == null) + { + DataGridViewColumn dataGridViewColumnFirst = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumnFirst != dataGridViewColumn) + { + dataGridViewColumnTmp = dataGridViewColumnFirst; + } + } + while (dataGridViewColumnTmp != null && this.Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index)) + { + dataGridViewColumnTmp.Frozen = true; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + else + { + // make sure the next visible columns are not frozen + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (dataGridViewColumnTmp == null) + { + DataGridViewColumn dataGridViewColumnLast = dataGridViewColumn; + do + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnLast, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnTmp != null) + { + dataGridViewColumnLast = dataGridViewColumnTmp; + } + } + while (dataGridViewColumnTmp != null); + if (dataGridViewColumnLast != dataGridViewColumn) + { + dataGridViewColumnTmp = dataGridViewColumnLast; + } + } + while (dataGridViewColumnTmp != null && this.Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)) + { + dataGridViewColumnTmp.Frozen = false; + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + } + + private void CorrectColumnFrozenStatesForMove(DataGridViewColumn dataGridViewColumn, int newDisplayIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex); + Debug.Assert(!this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); + + // No check necessary when: + // - column is invisible. + // - DisplayIndex decreases and column is frozen. + // - DisplayIndex increases and column is unfrozen. + + if (!dataGridViewColumn.Visible || + (newDisplayIndex < dataGridViewColumn.DisplayIndex && dataGridViewColumn.Frozen) || + (newDisplayIndex > dataGridViewColumn.DisplayIndex && !dataGridViewColumn.Frozen)) + { + return; + } + + int colCount = this.Columns.Count, displayIndex; + + if (newDisplayIndex < dataGridViewColumn.DisplayIndex) + { + // DisplayIndex decreases. + // Throw an exception if the visible unfrozen column is placed before a frozen column + // Get the closest visible column placed after the displaced column + DataGridViewColumn dataGridViewColumnNext; + displayIndex = newDisplayIndex; + do + { + dataGridViewColumnNext = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex++; + } + while (displayIndex < colCount && (dataGridViewColumnNext == null || dataGridViewColumnNext == dataGridViewColumn || !dataGridViewColumnNext.Visible)); + + if (dataGridViewColumnNext != null && dataGridViewColumnNext.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMoveNonFrozenColumn)); + } + } + else + { + // DisplayIndex increases. + // Throw an exception if the visible frozen column is placed after a non-frozen column + // Get the closest visible column placed before the displaced column + DataGridViewColumn dataGridViewColumnPrev; + displayIndex = newDisplayIndex; + do + { + dataGridViewColumnPrev = this.Columns.GetColumnAtDisplayIndex(displayIndex); + displayIndex--; + } + while (displayIndex >= 0 && (dataGridViewColumnPrev == null || !dataGridViewColumnPrev.Visible)); + + if (dataGridViewColumnPrev != null && !dataGridViewColumnPrev.Frozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMoveFrozenColumn)); + } + } + } + + private void CorrectColumnIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + for (int columnIndex = dataGridViewColumn.Index; columnIndex < this.Columns.Count; columnIndex++) + { + this.Columns[columnIndex].IndexInternal = this.Columns[columnIndex].Index - 1; + Debug.Assert(this.Columns[columnIndex].Index == columnIndex); + } + } + + private void CorrectColumnIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn, int insertionCount) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(insertionCount > 0); + for (int columnIndex = dataGridViewColumn.Index + insertionCount; columnIndex < this.Columns.Count; columnIndex++) + { + this.Columns[columnIndex].IndexInternal = columnIndex; + } + } + + private void CorrectFocus(bool onlyIfGridHasFocus) + { + if ((!onlyIfGridHasFocus || this.Focused) && this.editingControl != null) + { + Debug.Assert(this.CurrentCellInternal != null); + //Debug.Assert(this.editingControl.CanFocus); + this.editingControl.FocusInternal(); + } + } + + private void CorrectRowFrozenState(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, int anticipatedRowIndex) + { + Debug.Assert(dataGridViewRow != null); + Debug.Assert(anticipatedRowIndex >= 0 && anticipatedRowIndex <= this.Rows.Count); + + int previousRowIndex = this.Rows.GetPreviousRow(anticipatedRowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (previousRowIndex != -1 && + (this.Rows.GetRowState(previousRowIndex) & DataGridViewElementStates.Frozen) == 0 && + (rowState & DataGridViewElementStates.Frozen) != 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenRow)); + } + else + { + int nextRowIndex = this.Rows.GetNextRow((previousRowIndex == -1) ? anticipatedRowIndex - 1 : previousRowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (nextRowIndex != -1 && + (this.Rows.GetRowState(nextRowIndex) & DataGridViewElementStates.Frozen) != 0 && + (rowState & DataGridViewElementStates.Frozen) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenRow)); + } + } + } + + private void CorrectRowFrozenStates(DataGridViewRow[] dataGridViewRows, int rowIndexInserted) + { + bool nextVisibleRowPresent = false, previousRowFrozen = true, nextRowFrozen = false, currentRowFrozen; + + // Check if there is a visible row before the insertion point, and if it's frozen + int rowIndexTmp = this.Rows.GetPreviousRow(rowIndexInserted, DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + previousRowFrozen = (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + } + + // Check if there is a visible row at or after the insertion point, and if it's frozen + rowIndexTmp = this.Rows.GetNextRow(rowIndexInserted - 1, DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + nextVisibleRowPresent = true; + nextRowFrozen = (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + } + + for (int arrayIndex = 0; arrayIndex < dataGridViewRows.Length; arrayIndex++) + { + currentRowFrozen = ((DataGridViewRow)dataGridViewRows[arrayIndex]).Frozen; + if (!previousRowFrozen && currentRowFrozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddFrozenRow)); + } + previousRowFrozen = currentRowFrozen; + if (arrayIndex == dataGridViewRows.Length - 1 && + !currentRowFrozen && + nextVisibleRowPresent && + nextRowFrozen) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddNonFrozenRow)); + } + } + } + + private void CorrectRowFrozenStates(DataGridViewRow dataGridViewRow, int rowIndex, bool frozenStateChanging) + { + Debug.Assert(dataGridViewRow != null); + int rowIndexTmp; + if (((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) != 0 && !frozenStateChanging) || + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0 && frozenStateChanging)) + { + // make sure the previous visible rows are frozen as well + rowIndexTmp = this.Rows.GetPreviousRow(rowIndex, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (rowIndexTmp == -1) + { + int dataGridViewRowFirst = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (dataGridViewRowFirst != rowIndex) + { + rowIndexTmp = dataGridViewRowFirst; + } + } + while (rowIndexTmp != -1 && rowIndexTmp < rowIndex) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + } + else + { + // make sure the next visible rows are not frozen + rowIndexTmp = this.Rows.GetNextRow(rowIndex, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (rowIndexTmp == -1) + { + int dataGridViewRowLast = rowIndex; + do + { + rowIndexTmp = this.Rows.GetNextRow(dataGridViewRowLast, + DataGridViewElementStates.Visible); + if (rowIndexTmp != -1) + { + dataGridViewRowLast = rowIndexTmp; + } + } + while (rowIndexTmp != -1); + if (dataGridViewRowLast != rowIndex) + { + rowIndexTmp = dataGridViewRowLast; + } + } + while (rowIndexTmp != -1 && rowIndexTmp > rowIndex) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, false); + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + } + } + + private void CorrectRowIndexesAfterDeletion(int rowIndexDeleted) + { + Debug.Assert(rowIndexDeleted >= 0); + int rowsCount = this.Rows.Count; + for (int rowIndex = rowIndexDeleted; rowIndex < rowsCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Index >= 0) + { + dataGridViewRow.IndexInternal = dataGridViewRow.Index - 1; + Debug.Assert(dataGridViewRow.Index == rowIndex); + } + } + + // Fix 'new' row index if existant + if (this.newRowIndex == rowIndexDeleted) + { + this.newRowIndex = -1; // No more 'new' row. + } + else if (this.newRowIndex != -1) + { + this.newRowIndex--; + } + } + + private void CorrectRowIndexesAfterInsertion(int rowIndexInserted, int insertionCount) + { + Debug.Assert(rowIndexInserted >= 0); + Debug.Assert(insertionCount > 0); + int rowsCount = this.Rows.Count; + for (int rowIndex = rowIndexInserted + insertionCount; rowIndex < rowsCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Index >= 0) + { + dataGridViewRow.IndexInternal = dataGridViewRow.Index + insertionCount; + Debug.Assert(dataGridViewRow.Index == rowIndex); + } + } + + // Lastly update the 'new' row index if needed. + if (this.newRowIndex != -1) + { + this.newRowIndex += insertionCount; + } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewAccessibleObject(this); + } + + /// + protected override Control.ControlCollection CreateControlsInstance() + { + return new DataGridViewControlCollection(this); + } + + /// + /// + /// Constructs the new instance of the Columns collection objects. Subclasses + /// should not call base.CreateColumnsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual DataGridViewColumnCollection CreateColumnsInstance() + { + return new DataGridViewColumnCollection(this); + } + + /// + /// + /// Constructs the new instance of the Rows collection objects. Subclasses + /// should not call base.CreateRowsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual DataGridViewRowCollection CreateRowsInstance() + { + return new DataGridViewRowCollection(this); + } + + private NativeMethods.RECT[] CreateScrollableRegion(Rectangle scroll) + { + if (this.cachedScrollableRegion != null) + { + return this.cachedScrollableRegion; + } + + using (Region region = new Region(scroll)) + { + IntPtr handle = IntPtr.Zero; + using (Graphics graphics = CreateGraphicsInternal()) + { + handle = region.GetHrgn(graphics); + } + if (handle != IntPtr.Zero) + { + this.cachedScrollableRegion = UnsafeNativeMethods.GetRectsFromRegion(handle); + + // SECREVIEW : This assert is safe since we created the native region. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try + { + region.ReleaseHrgn(handle); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + } + return this.cachedScrollableRegion; + } + + private void DiscardNewRow() + { + Debug.Assert(this.Rows.Count > 1); + Debug.Assert(this.newRowIndex != -1); + + DataGridViewRowCancelEventArgs dgvrce = new DataGridViewRowCancelEventArgs(this.Rows[this.newRowIndex]); + OnUserDeletingRow(dgvrce); + if (dgvrce.Cancel) + { + return; + } + + // Delete the 'new' row + Debug.Assert(this.newRowIndex == this.Rows.Count - 1); + DataGridViewRow dataGridViewRow = this.Rows[this.newRowIndex]; + this.Rows.RemoveAtInternal(this.newRowIndex, false /*force*/); + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + // CorrectRowIndexesAfterDeletion resets this.newRowIndex to -1. + Debug.Assert(this.newRowIndex == -1); + + if (this.AllowUserToAddRowsInternal) + { + this.newRowIndex = this.Rows.Count - 1; + Debug.Assert((this.Rows.GetRowState(this.newRowIndex) & DataGridViewElementStates.Visible) != 0); + Debug.Assert(this.ptCurrentCell.Y == this.newRowIndex); + + OnDefaultValuesNeeded(new DataGridViewRowEventArgs(this.Rows[this.newRowIndex])); + InvalidateRowPrivate(this.newRowIndex); + } + } + + private void DiscardZonesInScrollingArea(ref Rectangle rectScrollingArea, + int emptyBackgroundWidth, + int emptyBackgroundHeight, + int frozenVisibleRowsHeight, + bool discardFrozenColumns, + bool discardFrozenRows) + { + // Discard empty background + rectScrollingArea.Width -= emptyBackgroundWidth; + rectScrollingArea.Height -= emptyBackgroundHeight; + if (this.RightToLeftInternal) + { + rectScrollingArea.X += emptyBackgroundWidth; + } + + if (discardFrozenColumns) + { + // Discard frozen columns + int frozenVisibleColumnsWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (!this.RightToLeftInternal) + { + rectScrollingArea.X += frozenVisibleColumnsWidth; + } + rectScrollingArea.Width -= frozenVisibleColumnsWidth; + } + if (discardFrozenRows) + { + // Discard frozen rows + rectScrollingArea.Y += frozenVisibleRowsHeight; + rectScrollingArea.Height -= frozenVisibleRowsHeight; + } + } + + /// + public int DisplayedColumnCount(bool includePartialColumns) + { + int cxMax = this.layout.Data.Width, cx = 0; + int completeColumns = 0, partialColumns = 0; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && cx < cxMax) + { + partialColumns++; + cx += dataGridViewColumn.Thickness; + if (cx <= cxMax) + { + completeColumns++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + + if (cx < cxMax && this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + if (this.negOffset > 0) + { + cx -= this.negOffset; + completeColumns--; + } + dataGridViewColumn = (DataGridViewColumn)this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + while (dataGridViewColumn != null && cx < cxMax) + { + partialColumns++; + cx += dataGridViewColumn.Thickness; + if (cx <= cxMax) + { + completeColumns++; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + + return includePartialColumns ? partialColumns : completeColumns; + } + + /// + public int DisplayedRowCount(bool includePartialRow) + { + return includePartialRow ? (this.displayedBandsInfo.NumDisplayedFrozenRows + this.displayedBandsInfo.NumDisplayedScrollingRows) : + (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows); + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] = true; + try + { + // Remove any Columns contained in this control + for (int i = 0; i < this.Columns.Count; i ++) + { + this.Columns[i].Dispose(); + } + + this.Columns.Clear(); + + UnwireScrollBarsEvents(); + if (this.vertScrollBar != null) + { + this.vertScrollBar.Dispose(); + this.vertScrollBar = null; + } + if (this.horizScrollBar != null) + { + this.horizScrollBar.Dispose(); + this.horizScrollBar = null; + } + + if (this.pens != null) + { + int nPenEntries = this.pens.Count; + if (nPenEntries > 0) + { + foreach (Pen pen in this.pens.Values) + { + pen.Dispose(); + } + this.pens.Clear(); + } + this.pens = null; + } + + if (this.brushes != null) + { + int nBrushEntries = this.brushes.Count; + if (nBrushEntries > 0) + { + foreach (SolidBrush brush in this.brushes.Values) + { + brush.Dispose(); + } + this.brushes.Clear(); + } + this.brushes = null; + } + + if (this.placeholderStringFormat != null) + { + this.placeholderStringFormat.Dispose(); + this.placeholderStringFormat = null; + } + + if (this.latestEditingControl != null) + { + this.latestEditingControl.Dispose(); + this.latestEditingControl = null; + } + if (this.editingControl != null) + { + this.editingControl.Dispose(); + this.editingControl = null; + } + if (this.editingPanel != null) + { + this.editingPanel.Dispose(); + this.editingPanel = null; + } + if (this.gridPen != null) + { + this.gridPen.Dispose(); + this.gridPen = null; + } + Debug.Assert(this.noSelectionChangeCount == 0); + + if (this.dataConnection != null) + { + this.dataConnection.Dispose(); + } + + // DGV should dispose the tool tip control before disposing itself. + // vsw 559812. + this.toolTipControl.Dispose(); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] = false; + } + } + base.Dispose(disposing); + } + + private void DrawColHeaderShadow(Graphics g, int mouseX) + { + Rectangle r = CalcColRelocationFeedbackRect(mouseX); + DrawShadowRect(r); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion]) + { + Rectangle rectInsertionBar = new Rectangle(0, this.layout.ColumnHeaders.Top, DATAGRIDVIEW_insertionBarWidth, this.layout.ColumnHeaders.Height); + // this.trackColumnEdge is the column preceeding the insertion point + if (this.trackColumnEdge == -1) + { + // Insert as first column + rectInsertionBar.X = GetColumnXFromIndex(this.Columns.GetFirstColumn(DataGridViewElementStates.Visible).Index); + if (this.RightToLeftInternal) + { + rectInsertionBar.X -= DATAGRIDVIEW_insertionBarWidth; + } + } + else + { + int offsetFromCenter = 0; + if (this.Columns.GetNextColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None) == null) + { + if (!this.RightToLeftInternal) + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth; + } + } + else + { + if (this.RightToLeftInternal) + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth / 2 - 1; + } + else + { + offsetFromCenter = DATAGRIDVIEW_insertionBarWidth / 2 + 1; + } + } + if (this.RightToLeftInternal) + { + rectInsertionBar.X = Math.Max(this.layout.ColumnHeaders.X, + GetColumnXFromIndex(this.trackColumnEdge) - this.Columns[this.trackColumnEdge].Width - offsetFromCenter); + } + else + { + rectInsertionBar.X = Math.Min(GetColumnXFromIndex(this.trackColumnEdge) + this.Columns[this.trackColumnEdge].Width - offsetFromCenter, + this.layout.ColumnHeaders.Right - DATAGRIDVIEW_insertionBarWidth); + } + } + if (this.ApplyVisualStylesToHeaderCells) + { + g.FillRectangle(GetCachedBrush(SystemColors.HotTrack), rectInsertionBar); + } + else + { + ControlPaint.FillReversibleRectangle(RectangleToScreen(rectInsertionBar), Color.White); + } + } + } + + /// + /// Draws an XOR region to give UI feedback for Column Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawColSplitBar(int mouseX) + { + Rectangle r = CalcColResizeFeedbackRect(mouseX); + DrawSplitBar(r); + } + + /// + /// Draws an XOR region to give UI feedback for Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawRowSplitBar(int mouseY) + { + Rectangle r = CalcRowResizeFeedbackRect(mouseY); + DrawSplitBar(r); + } + + private void DrawShadowRect(Rectangle r) + { + const byte DATAGRIDVIEW_shadowEdgeThickness = 3; + + IntPtr parentHandle = this.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y + r.Height - DATAGRIDVIEW_shadowEdgeThickness, r.Width, DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X + r.Width - DATAGRIDVIEW_shadowEdgeThickness, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, NativeMethods.PATINVERT); + + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc)); + } + + /// + /// Draws an XOR region to give UI feedback for Column/Row Resizing. + /// This looks just like the Splitter control's UI when resizing. + /// + private void DrawSplitBar(Rectangle r) + { + IntPtr parentHandle = this.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT); + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc)); + } + + private void EditingControls_CommonMouseEventHandler(object sender, MouseEventArgs e, DataGridViewMouseEvent dgvme) + { + Debug.Assert(this.ptCurrentCell.X != -1); + int adjustedX = this.editingPanel.Location.X + e.X; + int adjustedY = this.editingPanel.Location.Y + e.Y; + if (sender == this.editingControl) + { + adjustedX += this.editingControl.Location.X; + adjustedY += this.editingControl.Location.Y; + } + + if (dgvme == DataGridViewMouseEvent.MouseDown && e.Clicks == 1) + { + // Reset the flag that records single-click exposed as double-click. + this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled] = false; + } + + MouseEventArgs me = new MouseEventArgs(e.Button, + e.Clicks, + adjustedX, + adjustedY, + e.Delta); + + HitTestInfo hti = HitTest(me.X, me.Y); + int mouseX = me.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, me.Y - hti.RowY, me); + + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] = true; + // Check to see if this is a CellMouseDoubleClick situation + if ((dgvme == DataGridViewMouseEvent.MouseDown || + dgvme == DataGridViewMouseEvent.Click || + dgvme == DataGridViewMouseEvent.MouseClick) && + (DateTime.Now.Ticks - this.lastMouseClickInfo.timeStamp) / 10000 <= SystemInformation.DoubleClickTime && + e.Button == this.lastMouseClickInfo.button && + e.Clicks == 1 && + dgvcme.ColumnIndex == this.lastMouseClickInfo.col && + dgvcme.RowIndex == this.lastMouseClickInfo.row) + { + Size hotDoubleClickZone = SystemInformation.DoubleClickSize; + if (Math.Abs(dgvcme.X - this.lastMouseClickInfo.x) <= hotDoubleClickZone.Width / 2 && + Math.Abs(dgvcme.Y - this.lastMouseClickInfo.y) <= hotDoubleClickZone.Height / 2) + { + me = new MouseEventArgs(e.Button, + 2, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex, dgvcme.X, dgvcme.Y, me); + switch (dgvme) + { + case DataGridViewMouseEvent.MouseDown: + { + OnMouseDown(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDown(dgvcme); + } + break; + } + case DataGridViewMouseEvent.Click: + { + OnDoubleClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + } + case DataGridViewMouseEvent.MouseClick: + { + // Set the flag that prevents the triple-click to be exposed as a double-click + this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled] = true; + + OnMouseDoubleClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDoubleClick(dgvcme); + } + break; + } + } + return; + } + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_lastEditCtrlClickDoubled]) + { + // Make sure that the triple-click is exposed as a single-click and not a double-click. + if (e.Clicks == 2) + { + me = new MouseEventArgs(e.Button, + 1, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, me.Y - hti.RowY, me); + } + switch (dgvme) + { + case DataGridViewMouseEvent.DoubleClick: + dgvme = DataGridViewMouseEvent.Click; + break; + case DataGridViewMouseEvent.MouseDoubleClick: + dgvme = DataGridViewMouseEvent.MouseClick; + break; + } + } + + switch (dgvme) + { + case DataGridViewMouseEvent.Click: + OnClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + case DataGridViewMouseEvent.DoubleClick: + OnDoubleClick(me); + if (e.Button == MouseButtons.Left && + dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex)); + } + break; + case DataGridViewMouseEvent.MouseClick: + OnMouseClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseClick(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseDoubleClick: + OnMouseDoubleClick(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDoubleClick(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseDown: + OnMouseDown(me); + if (dgvcme.ColumnIndex < this.Columns.Count && + dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseDown(dgvcme); + } + break; + case DataGridViewMouseEvent.MouseUp: + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble]) + { + MouseEventArgs meTmp = new MouseEventArgs(e.Button, + 2, + adjustedX, + adjustedY, + e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex, dgvcme.X, dgvcme.Y, meTmp); + } + OnCellMouseUp(dgvcme); + OnMouseUp(me); + break; + case DataGridViewMouseEvent.MouseMove: + OnCellMouseMove(dgvcme); + break; + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] = false; + } + } + + private void EditingControls_Click(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + Debug.Assert(this.ptCurrentCell.X != -1); + System.Windows.Forms.MouseEventArgs me = e as System.Windows.Forms.MouseEventArgs; + if (me != null) + { + EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.Click); + } + } + + private void EditingControls_DoubleClick(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + Debug.Assert(this.ptCurrentCell.X != -1); + System.Windows.Forms.MouseEventArgs me = e as System.Windows.Forms.MouseEventArgs; + if (me != null) + { + EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.DoubleClick); + } + } + + private void EditingControls_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseClick); + } + + private void EditingControls_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDoubleClick); + } + + private void EditingControls_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDown); + } + + private void EditingControls_MouseEnter(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + if (sender == this.editingPanel) + { + Debug.Assert(this.editingControl != null); + Debug.Assert(!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + this.CursorInternal = ((IDataGridViewEditingControl)this.editingControl).EditingPanelCursor; + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected]) + { + OnMouseEnter(EventArgs.Empty); + } + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + private void EditingControls_MouseLeave(object sender, System.EventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + if (sender == this.editingPanel) + { + Debug.Assert(this.editingControl != null); + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + Point ptMouse = PointToClient(Control.MousePosition); + if (!this.ClientRectangle.Contains(ptMouse)) + { + OnMouseLeave(EventArgs.Empty); + } + } + + private void EditingControls_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseMove); + } + + private void EditingControls_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) + { + Debug.Assert(sender == this.editingControl || sender == this.editingPanel); + EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseUp); + } + + private void EndColumnHeadersResize(MouseEventArgs e) + { + try + { + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + + int y = Math.Min(e.Y + this.mouseBarOffset, this.layout.Data.Bottom - 1); + int delta = y - this.layout.ColumnHeaders.Y - this.ColumnHeadersHeight + 1; + if (this.trackRowAnchor != y && delta != 0) + { + this.ColumnHeadersHeight += delta; + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnRelocation(MouseEventArgs e, HitTestInfo hti) + { + try + { + if (this.lastHeaderShadow != -1) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumnEdge = -1; + this.lastHeaderShadow = -1; + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + + int previousColumnIndex; + if (ColumnRelocationTarget(e, hti, out previousColumnIndex)) + { + if (previousColumnIndex == -1) + { + this.Columns[this.trackColumn].DisplayIndex = 0; + } + else if (this.Columns[this.trackColumn].DisplayIndex > this.Columns[previousColumnIndex].DisplayIndex) + { + this.Columns[this.trackColumn].DisplayIndex = this.Columns[previousColumnIndex].DisplayIndex + 1; + } + else + { + this.Columns[this.trackColumn].DisplayIndex = this.Columns[previousColumnIndex].DisplayIndex; + } + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnResize(MouseEventArgs e) + { + try + { + EndColumnResize(e.X); + } + finally + { + RealeaseMouse(); + } + } + + private void EndColumnResize(int x) + { + int newX, delta; + if (this.RightToLeftInternal) + { + newX = Math.Max(x + this.mouseBarOffset, this.layout.Data.X); + delta = GetColumnXFromIndex(this.trackColumn) - this.Columns[this.trackColumn].Thickness - newX + 1; + } + else + { + newX = Math.Min(x + this.mouseBarOffset, this.layout.Data.Right - 1); + delta = newX - (GetColumnXFromIndex(this.trackColumn) + this.Columns[this.trackColumn].Thickness) + 1; + } + + if (this.trackColAnchor != newX && delta != 0) + { + int proposed = this.Columns[this.trackColumn].Thickness + delta; + Debug.Assert(proposed >= this.Columns[this.trackColumn].MinimumThickness); + Debug.Assert(proposed <= DataGridViewBand.maxBandThickness); + this.Columns[this.trackColumn].Thickness = proposed; + } + } + + /// + public bool EndEdit() + { + return EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit); + } + + /// + public bool EndEdit(DataGridViewDataErrorContexts context) + { + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + return CommitEdit(context); + } + else + { + return EndEdit(context, + DataGridViewValidateCellInternal.Never /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell*/, + true /*resetAnchorCell*/); + } + } + + private bool EndEdit(DataGridViewDataErrorContexts context, + DataGridViewValidateCellInternal validateCell, + bool fireCellLeave, + bool fireCellEnter, + bool fireRowLeave, + bool fireRowEnter, + bool fireLeave, + bool keepFocus, + bool resetCurrentCell, + bool resetAnchorCell) + { + if (this.ptCurrentCell.X == -1) + { + return true; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] = true; + try + { + int curRowIndex = this.ptCurrentCell.Y; + int curColIndex = this.ptCurrentCell.X; + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, context, validateCell, + fireCellLeave, fireCellEnter, fireRowLeave, fireRowEnter, fireLeave); + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + dgvdee = CancelEditPrivate(/*ref dataGridViewCurrentCell, context*/); // restore old value + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + if (dgvdee.Cancel) + { + return false; + } + } + } + + if (!this.IsCurrentCellInEditMode) + { + return true; + } + + if (curRowIndex != this.ptCurrentCell.Y || curColIndex != this.ptCurrentCell.X) + { + return true; + } + + if (this.editingControl != null) + { + UnwireEditingControlEvents(); + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] = this.MouseOverEditingControl; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] = this.MouseOverEditingPanel; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + try + { + dataGridViewCurrentCell.DetachEditingControl(); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + } + + ImeMode editingControlImeMode = this.editingControl.CachedImeMode; // If in restricted mode, ImeMode will be Disable. + this.latestEditingControl = this.editingControl; + Debug.Assert(this.editingPanel == null || this.editingPanel.Controls.Count == 0); + this.editingControl = null; + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + if (resetCurrentCell) + { + bool success = SetCurrentCellAddressCore(-1, -1, resetAnchorCell, false, false); + Debug.Assert(success); + } + } + if (keepFocus) + { + // Debug.Assert(this.CanFocus || this.Focused); // Invalid assertion (VS Whidbey 325154) + FocusInternal(); + } + this.ImeMode = editingControlImeMode; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode] = false; + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + if (!IsInnerCellOutOfBounds(curColIndex, curRowIndex)) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(curColIndex, curRowIndex); + OnCellEndEdit(dgvce); + } + + return true; + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] = false; + } + } + + private void EndRowHeadersResize(MouseEventArgs e) + { + try + { + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + + int x, delta; + if (this.RightToLeftInternal) + { + x = Math.Max(e.X + this.mouseBarOffset, this.layout.Data.Left - 1); + delta = this.layout.RowHeaders.Right - this.RowHeadersWidth - x - 1; + } + else + { + x = Math.Min(e.X + this.mouseBarOffset, this.layout.Data.Right - 1); + delta = x - this.layout.RowHeaders.X - this.RowHeadersWidth + 1; + } + if (this.trackColAnchor != x && delta != 0) + { + this.RowHeadersWidth += delta; + } + } + finally + { + RealeaseMouse(); + } + } + + private void EndRowResize(MouseEventArgs e) + { + try + { + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(this.trackRow); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(this.trackRow, out height, out minimumHeight); + int y = Math.Min(e.Y + this.mouseBarOffset, this.layout.Data.Bottom - 1); + int delta = y - (GetRowYFromIndex(this.trackRow) + height) + 1; + if (this.trackRowAnchor != y && delta != 0) + { + int proposedHeight = height + delta; + proposedHeight = Math.Max(proposedHeight, minimumHeight); + if (!OnRowHeightInfoPushed(this.trackRow, proposedHeight, minimumHeight)) + { + if (dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[this.trackRow]; // Unsharing row + } + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None); + dataGridViewRow.ThicknessInternal = proposedHeight; + } + } + } + finally + { + RealeaseMouse(); + } + } + + private void ExitBulkLayout(bool invalidInAdjustFillingColumns) + { + if (this.inBulkLayoutCount > 0) + { + this.inBulkLayoutCount--; + if (this.inBulkLayoutCount == 0) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + } + } + + private void ExitBulkPaint(int columnIndex, int rowIndex) + { + if (this.inBulkPaintCount > 0) + { + this.inBulkPaintCount--; + if (this.inBulkPaintCount == 0) + { + if (columnIndex >= 0) + { + InvalidateColumnInternal(columnIndex); + } + else if (rowIndex >= 0) + { + InvalidateRowPrivate(rowIndex); + } + else + { + Invalidate(); + } + } + } + } + + private void FirstVisibleScrollingRowTempted(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(rowIndex < this.Rows.Count); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0); + + int displayHeight = this.layout.Data.Height; + if (displayHeight <= 0) + { + return; + } + + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (totalVisibleFrozenHeight < displayHeight) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = rowIndex; + } + } + + private void FlushDisplayedChanged() + { + if (this.displayedBandsInfo.Dirty && this.Visible) + { + // Handle the rows + + if (!this.RowHeadersVisible && this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + // All rows are hidden + UpdateRowsDisplayedState(false /*displayed*/); + } + else + { + Rectangle rectScreen = Screen.FromControl(this).WorkingArea; + int maxDisplayedRows = (int) (rectScreen.Height / DataGridViewBand.minBandThickness); + + // Make sure all displayed scrolling rows have the Displayed state. + int rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp != -1) + { + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + + int rowIndexTmp2 = rowIndexTmp; + + // Make sure all scrolling rows before FirstDisplayedScrollingRow have their Displayed state set to false + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow != -1); + rowIndexTmp = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + + // Make sure all rows below last displayed scrolling row have Displayed state set to false (next loop) + rowIndexTmp = rowIndexTmp2; + } + else + { + // No displayed scrolling rows. Make sure all non-frozen rows have their Displayed state set to false (next loop) + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + + // Make sure all displayed frozen rows have their Displayed state set to true + int numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + + // Make sure all non-displayed frozen rows have their Displayed state set to false + while (rowIndexTmp != -1 && (this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + // Treat the cases where the old displayed rows are completely disjoint from the new displayed rows + int lastDisplayedFrozenRowIndex = -1; + int lastDisplayedScrollingRowIndex = -1; + + if (this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + int firstDisplayedFrozenRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(firstDisplayedFrozenRowIndex != -1); + if (this.displayedBandsInfo.NumDisplayedFrozenRows > 1) + { + lastDisplayedFrozenRowIndex = this.Rows.GetNextRow(firstDisplayedFrozenRowIndex, DataGridViewElementStates.Visible, this.displayedBandsInfo.NumDisplayedFrozenRows-2 /*skipRows*/); + } + else + { + lastDisplayedFrozenRowIndex = firstDisplayedFrozenRowIndex; + } + } + + if (this.displayedBandsInfo.FirstDisplayedScrollingRow != -1) + { + if (this.displayedBandsInfo.NumDisplayedScrollingRows > 1) + { + lastDisplayedScrollingRowIndex = this.Rows.GetNextRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, this.displayedBandsInfo.NumDisplayedScrollingRows - 2 /*skipRows*/); + } + else + { + lastDisplayedScrollingRowIndex = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + } + + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + while (rowIndexTmp != -1 && + rowIndexTmp < this.displayedBandsInfo.FirstDisplayedScrollingRow && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + break; + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + if (rowIndexTmp != -1 && + rowIndexTmp < this.Rows.Count && + (this.displayedBandsInfo.FirstDisplayedScrollingRow == -1 || this.displayedBandsInfo.FirstDisplayedScrollingRow < rowIndexTmp) && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + while (rowIndexTmp != -1) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) + { + break; + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + } + } + } + + if (this.displayedBandsInfo.RowInsertionOccurred) + { + // Adjust the scrolling rows that were pushed down by the rows insertion + rowIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingRow; + if (rowIndexTmp != -1) + { + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible, this.displayedBandsInfo.OldNumDisplayedScrollingRows - 1); + if (rowIndexTmp == -1) + { + rowIndexTmp = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + } + int rowCount = 0; + while (rowIndexTmp != -1 && + rowCount <= maxDisplayedRows && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + } + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible); + rowCount++; + } + } + + // Adjust the frozen rows that were pushed down by the rows insertion + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (rowIndexTmp != -1) + { + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, this.displayedBandsInfo.OldNumDisplayedFrozenRows - 1); + if (rowIndexTmp == -1) + { + rowIndexTmp = this.Rows.GetLastRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + int rowCount = 0; + while (rowIndexTmp != -1 && + rowCount <= maxDisplayedRows && + !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex)) + { + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false); + } + rowIndexTmp = this.Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible); + rowCount++; + } + } + } + +#if DEBUG + for (rowIndexTmp = 0; rowIndexTmp < this.Rows.Count; rowIndexTmp++) + { + DataGridViewElementStates rowStateDbg = this.Rows.GetRowState(rowIndexTmp); + bool rowNeedsDisplayedState = RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex); + if (((rowStateDbg & DataGridViewElementStates.Displayed) != 0) != rowNeedsDisplayedState) + { + Debug.Fail("Unexpected Displayed state for row"); + } + } +#endif + } + + // Handle the columns + + if (!this.ColumnHeadersVisible && this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 0) + { + // All columns are hidden + UpdateColumnsDisplayedState(false /*displayed*/); + } + else + { + // Make sure all displayed scrolling columns have the Displayed state. + DataGridViewColumn dataGridViewColumnTmp; + int columnIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (columnIndexTmp != -1) + { + int numDisplayedScrollingCols = this.displayedBandsInfo.NumDisplayedScrollingCols; + Debug.Assert(numDisplayedScrollingCols > 0); + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (numDisplayedScrollingCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (!dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = true; + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + numDisplayedScrollingCols--; + } + + DataGridViewColumn dataGridViewColumnTmp2 = dataGridViewColumnTmp; + + // Make sure all scrolling columns before FirstDisplayedScrollingCol have their Displayed state set to false + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol != -1); + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + + // Make sure all columns after last displayed scrolling column have Displayed state set to false (next loop) + dataGridViewColumnTmp = dataGridViewColumnTmp2; + } + else + { + // No displayed scrolling columns. Make sure all non-frozen columns have their Displayed state set to false (next loop) + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + + // Make sure all displayed frozen columns have their Displayed state set to true + int numDisplayedFrozenCols = this.displayedBandsInfo.NumDisplayedFrozenCols; + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (!dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = true; + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + numDisplayedFrozenCols--; + } + + // Make sure all non-displayed frozen columns have their Displayed state set to false + while (dataGridViewColumnTmp != null && dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + } + + // Treat the cases where the old displayed columns are completely disjoint from the new displayed columns + + columnIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingCol; + while (columnIndexTmp != -1 && + columnIndexTmp < this.Columns.Count && + this.displayedBandsInfo.FirstDisplayedScrollingCol != -1 && + columnIndexTmp != this.displayedBandsInfo.FirstDisplayedScrollingCol && + this.Columns.DisplayInOrder(columnIndexTmp, this.displayedBandsInfo.FirstDisplayedScrollingCol) && + !ColumnNeedsDisplayedState(this.Columns[columnIndexTmp])) + { + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + if (!dataGridViewColumnTmp.Displayed) + { + break; + } + else + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + columnIndexTmp = (dataGridViewColumnTmp == null) ? -1 : dataGridViewColumnTmp.Index; + } + } + + columnIndexTmp = this.displayedBandsInfo.OldFirstDisplayedScrollingCol; + if (columnIndexTmp != -1 && + columnIndexTmp < this.Columns.Count && + (this.displayedBandsInfo.FirstDisplayedScrollingCol == -1 || (this.displayedBandsInfo.FirstDisplayedScrollingCol != columnIndexTmp && this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, columnIndexTmp))) && + !ColumnNeedsDisplayedState(this.Columns[columnIndexTmp])) + { + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (dataGridViewColumnTmp != null) + { + if (!dataGridViewColumnTmp.Displayed) + { + break; + } + else + { + dataGridViewColumnTmp.DisplayedInternal = false; + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + } + + if (this.displayedBandsInfo.ColumnInsertionOccurred) + { + dataGridViewColumnTmp = this.Columns[this.Columns.Count - 1]; + while (dataGridViewColumnTmp != null && !ColumnNeedsDisplayedState(dataGridViewColumnTmp)) + { + if (dataGridViewColumnTmp.Displayed) + { + dataGridViewColumnTmp.DisplayedInternal = false; + } + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + +#if DEBUG + for (columnIndexTmp = 0; columnIndexTmp < this.Columns.Count; columnIndexTmp++) + { + DataGridViewElementStates colStateDbg = this.Columns[columnIndexTmp].State; + bool columnNeedsDisplayedState = ColumnNeedsDisplayedState(this.Columns[columnIndexTmp]); + if (((colStateDbg & DataGridViewElementStates.Displayed) != 0) != columnNeedsDisplayedState) + { + Debug.Fail("Unexpected Displayed state for column"); + } + } +#endif + } + + this.displayedBandsInfo.Dirty = false; + } + } + + private void FlushDisplayIndexChanged(bool raiseEvent) + { + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.DisplayIndexHasChanged) + { + dataGridViewColumn.DisplayIndexHasChanged = false; + if (raiseEvent) + { + OnColumnDisplayIndexChanged(dataGridViewColumn); + } + } + } + } + + private void FlushSelectionChanged() + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged]) + { + OnSelectionChanged(EventArgs.Empty); + } + } + + /// + protected override AccessibleObject GetAccessibilityObjectById(int objectId) + { + // decrement the objectId because in our implementation of AccessibilityClient notitification objectId's are 1 - based. + // 0 == NativeMethods.CHILDID_SELF corresponds to the AccessibleObject itself + return this.AccessibilityObject.GetChild(objectId - 1); + } + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal SolidBrush GetCachedBrush(Color color) + { + SolidBrush brush = (SolidBrush) this.brushes[color]; + if (brush == null) + { + brush = new SolidBrush(color); + this.brushes.Add(color, brush); + } + return brush; + } + +#if DGV_GDI + internal WindowsSolidBrush GetCachedWindowsBrush(Color color) + { + WindowsSolidBrush brush = (WindowsSolidBrush)this.brushes[color]; + if (brush == null) + { + brush = new WindowsSolidBrush(color); + this.brushes.Add(color, brush); + } + return brush; + } +#endif // DGV_GDI + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal Pen GetCachedPen(Color color) + { + Pen pen = (Pen) this.pens[color]; + if (pen == null) + { + pen = new Pen(color); + this.pens.Add(color, pen); + } + return pen; + } + +#if DGV_GDI + internal WindowsPen GetCachedWindowsPen(Color color) + { + WindowsPen pen = (WindowsPen)this.pens[color]; + if (pen == null) + { + pen = new WindowsPen(color); + this.pens.Add(color, pen); + } + return pen; + } +#endif // DGV_GDI + + internal TypeConverter GetCachedTypeConverter(Type type) + { + if (this.converters.ContainsKey(type)) + { + return (TypeConverter)this.converters[type]; + } + + TypeConverter converter = TypeDescriptor.GetConverter(type); + this.converters.Add(type, converter); + return converter; + } + + internal Rectangle GetCellAdjustedDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow) + { + Rectangle rect = GetCellDisplayRectangle(columnIndex, rowIndex, cutOverflow); + if (!rect.IsEmpty) + { + if (this.SingleVerticalBorderAdded && columnIndex == this.FirstDisplayedColumnIndex) + { + if (!this.RightToLeftInternal) + { + rect.X--; + } + rect.Width++; + } + if (this.SingleHorizontalBorderAdded && rowIndex == this.FirstDisplayedRowIndex) + { + rect.Y--; + rect.Height++; + } + } + return rect; + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // using specialized DataGridViewCellLinkedList class instead of generics + ] + public int GetCellCount(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + + int cellCount = 0; + bool displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired; + + if ((includeFilter & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected) + { + if (includeFilter == DataGridViewElementStates.Selected) + { + cellCount = this.individualSelectedCells.Count; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and decide that SelectAll() should use band selection, + // we need to take the bands into account. + return cellCount; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + return cellCount + this.selectedBandIndexes.Count * this.Rows.Count; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + return cellCount + this.selectedBandIndexes.Count * this.Columns.Count; + } + } + } + + displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed; + frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly; + visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible; + + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, dataGridViewCell.RowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // If we change the design and decide that SelectAll() should use band selection, + // we need to take the bands into account. + return cellCount; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (int columnIndex in this.selectedBandIndexes) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + return cellCount; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + foreach (int rowIndex in this.selectedBandIndexes) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + return cellCount; + } + } + } + + if ((includeFilter == DataGridViewElementStates.ReadOnly && this.ReadOnly) || + includeFilter == DataGridViewElementStates.None) + { + return this.Rows.Count * this.Columns.Count; + } + + displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed; + frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen; + resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly; + visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible; + + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (!visibleRequired || (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0) + { + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired)) + { + cellCount++; + } + } + } + } + return cellCount; + } + + private bool GetCellCount_CellIncluded(DataGridViewCell dataGridViewCell, + int rowIndex, + bool displayedRequired, + bool frozenRequired, + bool resizableRequired, + bool readOnlyRequired, + bool visibleRequired) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (displayedRequired) + { + bool cellDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0 && + dataGridViewCell.OwningColumn.Displayed; + if (!cellDisplayed) + { + return false; + } + } + if (frozenRequired) + { + bool cellFrozen = (rowState & DataGridViewElementStates.Frozen) != 0 || + dataGridViewCell.OwningColumn.Frozen || + dataGridViewCell.StateIncludes(DataGridViewElementStates.Frozen); + if (!cellFrozen) + { + return false; + } + } + if (resizableRequired) + { + if (!RowIsResizable(rowIndex) && dataGridViewCell.OwningColumn.Resizable != DataGridViewTriState.True) + { + return false; + } + } + if (readOnlyRequired) + { + bool cellReadOnly = this.ReadOnly || + (rowState & DataGridViewElementStates.ReadOnly) != 0 || + dataGridViewCell.OwningColumn.ReadOnly || + dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly); + if (!cellReadOnly) + { + return false; + } + } + if (visibleRequired) + { + bool cellVisible = (rowState & DataGridViewElementStates.Visible) != 0 && + dataGridViewCell.OwningColumn.Visible; + if (!cellVisible) + { + return false; + } + } + return true; + } + + /// + public Rectangle GetCellDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow) + { + Rectangle rowRect; + Rectangle columnRect; + + if (columnIndex >= 0) + { + if (columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + columnRect = GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow); + } + else + { + if (columnIndex != -1) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex >= 0) + { + columnRect = this.layout.RowHeaders; + } + else + { + columnRect = this.layout.TopLeftHeader; + } + } + + if (rowIndex >= 0) + { + if (rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + rowRect = GetRowDisplayRectanglePrivate(rowIndex, cutOverflow); + } + else + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (columnIndex >= 0) + { + rowRect = this.layout.ColumnHeaders; + } + else + { + rowRect = this.layout.TopLeftHeader; + } + } + + if (!cutOverflow) + { + int height = rowRect.Bottom - columnRect.Bottom; + if (height > 0) + { + columnRect.Height += height; + } + int width; + if (this.RightToLeftInternal) + { + width = rowRect.X - columnRect.X; + if (width > 0) + { + rowRect.Width += width; + rowRect.X -= width; + } + } + else + { + width = columnRect.Right - rowRect.Right; + if (width > 0) + { + rowRect.Width += width; + } + } + } + + rowRect.Intersect(columnRect); + + return rowRect; + } + + internal DataGridViewCell GetCellInternal(int columnIndex, int rowIndex) + { + Debug.Assert(columnIndex >= -1 && columnIndex < this.Columns.Count); + Debug.Assert(rowIndex >= -1 && rowIndex < this.Rows.Count); + if (rowIndex >= 0) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + Debug.Assert(dataGridViewRow != null); + if (columnIndex >= 0) + { + return dataGridViewRow.Cells[columnIndex]; + } + else + { + return dataGridViewRow.HeaderCell; + } + } + else + { + if (columnIndex >= 0) + { + return this.Columns[columnIndex].HeaderCell; + } + else + { + return this.TopLeftHeaderCell; + } + } + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes"), // using specialized DataGridViewCellLinkedList class instead of generics + ] + public virtual DataObject GetClipboardContent() + { + if (this.ClipboardCopyMode == DataGridViewClipboardCopyMode.Disable) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_DisabledClipboardCopy)); + } + + if (this.CurrentCellIsEditedAndOnlySelectedCell) + { + return null; + } + + string[] formats = new string[] { DataFormats.Html, DataFormats.Text, DataFormats.UnicodeText, DataFormats.CommaSeparatedValue }; + DataObject dataObject = new DataObject(); + bool includeColumnHeaders = false, includeRowHeaders = false; + string cellContent = null; + StringBuilder sbContent = null; + DataGridViewColumn dataGridViewColumn, prevDataGridViewColumn, nextDataGridViewColumn; + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0) + { + return null; + } + + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + includeColumnHeaders = (this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) == -1); + includeRowHeaders = true; + } + else + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible columns in their reverse display order + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible columns in their display order + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + } + // Cycle through the visible selected rows. + bool firstRowIndex = true; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + Debug.Assert(rowIndex != -1); + int nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + while (rowIndex != -1) + { + if (this.RightToLeftInternal) + { + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + + // Cycle through the visible columns in their reverse display order + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0 /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible columns in their display order + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + rowIndex = nextRowIndex; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + } + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0) + { + return null; + } + + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + includeColumnHeaders = true; + includeRowHeaders = (this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) == null); + } + else + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible & selected columns in their display order + DataGridViewColumn lastDataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + dataGridViewColumn = lastDataGridViewColumn; + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + lastDataGridViewColumn == null /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible & selected columns in their display order + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + firstVisibleRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + } + // Cycle through the visible rows. + bool firstRowIndex = true; + int rowIndex = firstVisibleRowIndex; + int nextRowIndex = -1; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + while (rowIndex != -1) + { + if (this.RightToLeftInternal) + { + DataGridViewColumn lastDataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + + // Cycle through the visible & selected columns in their reverse display order + dataGridViewColumn = lastDataGridViewColumn; + if (dataGridViewColumn != null) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (prevDataGridViewColumn != null) + { + dataGridViewColumn = prevDataGridViewColumn; + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + lastDataGridViewColumn == null /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected); + + // Get the row header clipboard content + if (includeRowHeaders) + { + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + dataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible & selected columns in their display order + if (dataGridViewColumn != null) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + while (nextDataGridViewColumn != null) + { + dataGridViewColumn = nextDataGridViewColumn; + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None); + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + } + rowIndex = nextRowIndex; + if (rowIndex != -1) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + bool selectedVisibleCellExists = false; + bool selectedVisibleColumnExists = false; + bool selectedVisibleRowExists = false; + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + selectedVisibleRowExists = this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0; + selectedVisibleCellExists = selectedVisibleRowExists && this.Columns.GetColumnCount(DataGridViewElementStates.Visible) != 0; + } + else if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + selectedVisibleColumnExists = this.Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0; + selectedVisibleCellExists = selectedVisibleColumnExists && this.Rows.GetRowCount(DataGridViewElementStates.Visible) != 0; + } + if (!selectedVisibleCellExists && this.individualSelectedCells.Count > 0) + { + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (dataGridViewCell.Visible) + { + selectedVisibleCellExists = true; + break; + } + } + } + if (!selectedVisibleCellExists) + { + return null; + } + + // There is at least one selected visible cell. + if (this.SelectionMode == DataGridViewSelectionMode.CellSelect) + { + includeColumnHeaders = includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + includeColumnHeaders &= this.ColumnHeadersVisible; + includeRowHeaders &= this.RowHeadersVisible; + } + else + { + includeColumnHeaders = includeRowHeaders = false; + if (this.ColumnHeadersVisible) + { + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + if (selectedVisibleColumnExists) + { + includeColumnHeaders = true; + } + /* Use this code if column headers should be included when all cells are selected in a visible column. + else + { + // Include column headers if there is a column where all visible cells are selected + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + // Cycle through the visible rows, see if the cell in that column is selected + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + while (rowIndex != -1) + { + if (!this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].Selected) + { + break; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + if (rowIndex == -1) + { + // All visible cells in column are selected + includeColumnHeaders = true; + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + } + */ + } + else + { + includeColumnHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + } + + if (this.RowHeadersVisible) + { + if (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText) + { + if (selectedVisibleRowExists) + { + includeRowHeaders = true; + } + /* Use this code if row headers should be included when all cells are selected in a visible row. + else + { + // Include row headers if there is a row where all visible cells are selected + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowIndex != -1); + while (rowIndex != -1) + { + // Cycle through the visible columns, see if the cell in that row is selected + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (!this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].Selected) + { + break; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + if (dataGridViewColumn == null) + { + // All visible cells in row are selected + includeRowHeaders = true; + break; + } + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + } + */ + } + else + { + includeRowHeaders = (clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText); + } + } + } + + // Get the four corners of the 'selected table' + int lRowIndex = int.MaxValue; + int uRowIndex = -1; + DataGridViewColumn lColumn = null, uColumn = null; + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + DataGridViewColumn firstVisibleColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + DataGridViewColumn lastVisibleColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None); + + Debug.Assert(firstVisibleColumn != null); + Debug.Assert(lastVisibleColumn != null); + foreach (int rowIndex in this.selectedBandIndexes) + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0) + { + if (rowIndex < lRowIndex) + { + lRowIndex = rowIndex; + } + if (rowIndex > uRowIndex) + { + uRowIndex = rowIndex; + } + lColumn = firstVisibleColumn; + uColumn = lastVisibleColumn; + } + } + } + else if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + Debug.Assert(firstVisibleRowIndex != -1); + Debug.Assert(lastVisibleRowIndex != -1); + foreach (int columnIndex in this.selectedBandIndexes) + { + if (this.Columns[columnIndex].Visible) + { + if (lColumn == null || this.Columns.DisplayInOrder(columnIndex, lColumn.Index)) + { + lColumn = this.Columns[columnIndex]; + } + if (uColumn == null || this.Columns.DisplayInOrder(uColumn.Index, columnIndex)) + { + uColumn = this.Columns[columnIndex]; + } + lRowIndex = firstVisibleRowIndex; + uRowIndex = lastVisibleRowIndex; + } + } + } + + // Go through the individually selected cells to potentially stretch the current 'selected table'. + foreach (DataGridViewCell dataGridViewCell in this.individualSelectedCells) + { + if (dataGridViewCell.Visible) + { + if (dataGridViewCell.RowIndex < lRowIndex) + { + lRowIndex = dataGridViewCell.RowIndex; + } + if (dataGridViewCell.RowIndex > uRowIndex) + { + uRowIndex = dataGridViewCell.RowIndex; + } + if (lColumn == null || this.Columns.DisplayInOrder(dataGridViewCell.ColumnIndex, lColumn.Index)) + { + lColumn = dataGridViewCell.OwningColumn; + } + if (uColumn == null || this.Columns.DisplayInOrder(uColumn.Index, dataGridViewCell.ColumnIndex)) + { + uColumn = dataGridViewCell.OwningColumn; + } + } + } + + Debug.Assert(lRowIndex != -1); + Debug.Assert(uRowIndex != -1); + Debug.Assert(lColumn != null); + Debug.Assert(uColumn != null); + Debug.Assert(lColumn.Index == uColumn.Index || this.Columns.DisplayInOrder(lColumn.Index, uColumn.Index)); + Debug.Assert(lRowIndex <= uRowIndex); + + foreach (string format in formats) + { + /* if (!String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase) && + !String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase)) + { + continue; + }*/ + + if (sbContent == null) + { + sbContent = new StringBuilder(1024); + } + else + { + sbContent.Length = 0; + } + + if (includeColumnHeaders) + { + if (this.RightToLeftInternal) + { + // Cycle through the visible columns from uColumn to lColumn + dataGridViewColumn = uColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != lColumn) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(prevDataGridViewColumn != null); + } + else + { + prevDataGridViewColumn = null; + } + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + dataGridViewColumn == uColumn /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = prevDataGridViewColumn; + } + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + false /*firstCell*/, + true /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + cellContent = this.TopLeftHeaderCell.GetClipboardContentInternal(-1, + true /*firstCell*/, + false /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + // Cycle through the visible columns from lColumn to uColumn + dataGridViewColumn = lColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != uColumn) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(nextDataGridViewColumn != null); + } + else + { + nextDataGridViewColumn = null; + } + cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(-1, + !includeRowHeaders && dataGridViewColumn == lColumn /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + true /*inFirstRow*/, + false /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = nextDataGridViewColumn; + } + } + } + // Cycle through the visible rows from lRowIndex to uRowIndex. + bool firstRowIndex = true; + int rowIndex = lRowIndex; + int nextRowIndex = -1; + Debug.Assert(rowIndex != -1); + while (rowIndex != -1) + { + if (rowIndex != uRowIndex) + { + nextRowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + Debug.Assert(nextRowIndex != -1); + } + else + { + nextRowIndex = -1; + } + + if (this.RightToLeftInternal) + { + // Cycle through the visible columns from uColumn to lColumn + dataGridViewColumn = uColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != lColumn) + { + prevDataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(prevDataGridViewColumn != null); + } + else + { + prevDataGridViewColumn = null; + } + + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + dataGridViewColumn == uColumn /*firstCell*/, + !includeRowHeaders && prevDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = prevDataGridViewColumn; + } + + if (includeRowHeaders) + { + // Get the row header clipboard content + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + false /*firstCell*/, + true /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + } + else + { + if (includeRowHeaders) + { + // Get the row header clipboard content + cellContent = this.Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(rowIndex, + true /*firstCell*/, + false /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + } + + // Cycle through the visible columns from lColumn to uColumn + dataGridViewColumn = lColumn; + Debug.Assert(dataGridViewColumn != null); + while (dataGridViewColumn != null) + { + if (dataGridViewColumn != uColumn) + { + nextDataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(nextDataGridViewColumn != null); + } + else + { + nextDataGridViewColumn = null; + } + + cellContent = this.Rows.SharedRow(rowIndex).Cells[dataGridViewColumn.Index].GetClipboardContentInternal(rowIndex, + !includeRowHeaders && dataGridViewColumn == lColumn /*firstCell*/, + nextDataGridViewColumn == null /*lastCell*/, + !includeColumnHeaders && firstRowIndex /*inFirstRow*/, + nextRowIndex == -1 /*inLastRow*/, + format) as string; + if (cellContent != null) + { + sbContent.Append(cellContent); + } + dataGridViewColumn = nextDataGridViewColumn; + } + } + rowIndex = nextRowIndex; + firstRowIndex = false; + } + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + System.IO.MemoryStream utf8Stream = null; + GetClipboardContentForHtml(sbContent, out utf8Stream); + dataObject.SetData(format, false /*autoConvert*/, utf8Stream); + } + else + { + dataObject.SetData(format, false /*autoConvert*/, sbContent.ToString()); + } + } + break; + } + return dataObject; + } + + private static void GetClipboardContentForHtml(StringBuilder sbContent, out System.IO.MemoryStream utf8Stream) + { + byte[] sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString()); + byte[] destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes); + + // Marshal.SystemDefaultCharSize is 2 on WinXP Pro - so the offsets seem to be in character counts instead of bytes. + // Test on JPN and Win9x machines. + int bytecountEndOfFragment = 135 + destinationBytes.Length; + int bytecountEndOfHtml = bytecountEndOfFragment + 36; + string prefix = string.Format(CultureInfo.InvariantCulture, DATAGRIDVIEW_htmlPrefix, bytecountEndOfHtml.ToString("00000000", CultureInfo.InvariantCulture), bytecountEndOfFragment.ToString("00000000", CultureInfo.InvariantCulture)) + DATAGRIDVIEW_htmlStartFragment; + sbContent.Insert(0, prefix); + sbContent.Append(DATAGRIDVIEW_htmlEndFragment); + + sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString()); + destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes); + + utf8Stream = new System.IO.MemoryStream(bytecountEndOfHtml + 1); + utf8Stream.Write(destinationBytes, 0, bytecountEndOfHtml); + utf8Stream.WriteByte((byte)0); + + #if DEBUG + Debug.Assert(destinationBytes[97] == '<'); + Debug.Assert(destinationBytes[bytecountEndOfHtml-1] == '>'); + Debug.Assert(destinationBytes[133] == '<'); + Debug.Assert(destinationBytes[bytecountEndOfFragment] == '<'); + #endif + } + + /// + // Rectangle returned includes the potential column header + public Rectangle GetColumnDisplayRectangle(int columnIndex, bool cutOverflow) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + return GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow); + } + + private Rectangle GetColumnDisplayRectanglePrivate(int columnIndex, bool cutOverflow) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (!this.Columns[columnIndex].Displayed) + { + return Rectangle.Empty; + } + + Rectangle data = this.layout.Data; + int cx; + bool columnFound = false; + DataGridViewColumn dataGridViewColumn; + if (this.RightToLeftInternal) + { + cx = data.Right; + } + else + { + cx = data.X; + } + for (dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + dataGridViewColumn != null && !columnFound; + ) + { + if ((this.RightToLeftInternal && cx < data.X) || + (!this.RightToLeftInternal && cx > data.Right)) + { + break; + } + if (dataGridViewColumn.Index == columnIndex) + { + columnFound = true; + } + else + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + } + + if (!columnFound && this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + for (dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + dataGridViewColumn != null && !columnFound; + ) + { + if ((this.RightToLeftInternal && cx < data.X) || + (!this.RightToLeftInternal && cx > data.Right)) + { + break; + } + if (dataGridViewColumn.Index == columnIndex) + { + columnFound = true; + } + else + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (dataGridViewColumn.Index == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + if (this.RightToLeftInternal) + { + cx += this.negOffset; + } + else + { + cx -= this.negOffset; + } + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + } + + if (columnFound) + { + Debug.Assert(dataGridViewColumn != null); + int displayWidth, viewedColumnWidth = dataGridViewColumn.Thickness; + if (dataGridViewColumn.Index == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + viewedColumnWidth -= this.negOffset; + } + if (cutOverflow && + ((!this.RightToLeftInternal && cx + viewedColumnWidth > data.Right) || + (this.RightToLeftInternal && cx - viewedColumnWidth < data.X))) + { + if (this.RightToLeftInternal) + { + displayWidth = cx - data.X; + } + else + { + displayWidth = data.Right - cx; + } + } + else + { + displayWidth = viewedColumnWidth; + } + + Rectangle columnRect; + if (this.RightToLeftInternal) + { + columnRect = new Rectangle(cx - displayWidth, data.Y, displayWidth, data.Height); + } + else + { + columnRect = new Rectangle(cx, data.Y, displayWidth, data.Height); + } + if (this.layout.ColumnHeadersVisible) + { + columnRect.Height += this.layout.ColumnHeaders.Height; + columnRect.Y -= this.layout.ColumnHeaders.Height; + } + return columnRect; + } + + return Rectangle.Empty; + } + + // xColumnLeftEdge returns the left edge of the column when RightToLeft is false. + // xColumnLeftEdge returns the right edge of the column when RightToLeft is true. + private int GetColumnIndexFromX(int x, out int xColumnLeftEdge) + { + Rectangle data = this.layout.Data; + Debug.Assert(this.RightToLeftInternal || (x >= data.X - 1 && x < data.Right), "x must be inside the horizontal bounds of this.layout.Data"); + Debug.Assert(!this.RightToLeftInternal || (x >= data.X && x <= data.Right), "x must be inside the horizontal bounds of this.layout.Data"); + + if (!this.RightToLeftInternal && x == data.X - 1) + { + x++; + } + else if (this.RightToLeftInternal && x == data.Right) + { + x--; + } + + int cx; + if (this.RightToLeftInternal) + { + cx = data.Right-1; + } + else + { + cx = data.X; + } + + // first try to match x against a frozen column + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && + ((!this.RightToLeftInternal && cx < data.Right) || (this.RightToLeftInternal && cx >= data.X))) + { + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (!this.RightToLeftInternal && cx > x) + { + xColumnLeftEdge = cx - dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + else if (this.RightToLeftInternal && cx < x) + { + xColumnLeftEdge = cx + dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + + if (this.RightToLeftInternal) + { + cx += this.negOffset; + } + else + { + cx -= this.negOffset; + } + + // second try to match x against a scrolling column + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + while (dataGridViewColumn != null && + ((!this.RightToLeftInternal && cx < data.Right) || (this.RightToLeftInternal && cx >= data.X))) + { + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + if (this.RightToLeftInternal) + { + cx -= dataGridViewColumn.Thickness; + } + else + { + cx += dataGridViewColumn.Thickness; + } + if (!this.RightToLeftInternal && cx > x) + { + xColumnLeftEdge = cx - dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + else if (this.RightToLeftInternal && cx < x) + { + xColumnLeftEdge = cx + dataGridViewColumn.Thickness; + return dataGridViewColumn.Index; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + } + + xColumnLeftEdge = -1; + return -1; + } + + private static int GetColumnScrollRate(int xOffset) + { + Debug.Assert(xOffset > 0); + // Counting 20ms for executing the actual column scrolling + if (xOffset <= 10) + { + return 480; // Two columns per second + } + if (xOffset <= 15) + { + return 313; // Three columns per second + } + if (xOffset <= 25) + { + return 180; // Five columns per second + } + if (xOffset <= 35) + { + return 123; // Seven columns per second + } + return Math.Max(1, 4000 / xOffset); + } + + /// + /// Returns the coordinate of the left edge of the given column. Note that + /// the column does not need to be completely visible on the display area. + /// Value returned is not necessarily within layout.Data because of the + /// this.negOffset value, or because the column may start to the right of + /// data area, or behind the frozen area, or completely on the left of the control. + /// The right edge is returned in RightToLeft mode. + /// + internal int GetColumnXFromIndex(int index) + { + Debug.Assert(index < this.Columns.Count); + Debug.Assert(this.Columns[index].Visible); + + int x; + if (this.RightToLeftInternal) + { + x = this.layout.Data.Right-1; + } + else + { + x = this.layout.Data.X; + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + if (index == dataGridViewColumn.Index) + { + return x; + } + if (this.RightToLeftInternal) + { + x -= dataGridViewColumn.Thickness; + } + else + { + x += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + } + + if (this.RightToLeftInternal) + { + x += this.negOffset; + } + else + { + x -= this.negOffset; + } + + int xFirstVisibleScrollingCol = x; + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + } + else + { + dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + } + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + while (dataGridViewColumn != null) + { + if (index == dataGridViewColumn.Index) + { + return x; + } + if (this.RightToLeftInternal) + { + x -= dataGridViewColumn.Thickness; + } + else + { + x += dataGridViewColumn.Thickness; + } + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + + // The column is completely hidden on the left/right of the dataGridView + x = xFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + if (this.RightToLeftInternal) + { + x += dataGridViewColumn.Thickness; + } + else + { + x -= dataGridViewColumn.Thickness; + } + if (index == dataGridViewColumn.Index) + { + return x; + } + dataGridViewColumn = this.Columns.GetPreviousColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + Debug.Fail("Could not find column in GetColumnXFromIndex"); + return 0; + } + + private int GetNegOffsetFromHorizontalOffset(int horizontalOffset) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null && dataGridViewColumn.Thickness <= horizontalOffset) + { + horizontalOffset -= dataGridViewColumn.Thickness; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + } + return horizontalOffset; + } + + private bool GetOutOfBoundCorrectedHitTestInfo(ref HitTestInfo hti, ref int mouseX, ref int mouseY, out int xOffset, out int yOffset) + { + xOffset = yOffset = 0; + Rectangle rectScrollingArea = this.layout.Data; + int visibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int frozenVisibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + int fittingTrailingScrollingRowsHeight = ComputeHeightOfFittingTrailingScrollingRows(frozenVisibleRowsHeight); + int trailingScrollingRowsHeight = ComputeHeightOfTrailingScrollingRows(); + int emptyBackgroundWidth = Math.Max(0, this.layout.Data.Width - this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible)); + int emptyBackgroundHeight = Math.Max(0, this.layout.Data.Height - frozenVisibleRowsHeight - trailingScrollingRowsHeight); + + Debug.Assert(!this.vertScrollBar.Enabled || + !this.vertScrollBar.Visible || + this.vertScrollBar.Maximum == visibleRowsHeight - frozenVisibleRowsHeight); + //VSWhidbey 525671 + //Debug.Assert(!this.vertScrollBar.Enabled || + // !this.vertScrollBar.Visible || + // this.vertScrollBar.Value >= this.verticalOffset); + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect]) + { + if (this.layout.RowHeadersVisible) + { + // Include row headers + rectScrollingArea = Rectangle.Union(rectScrollingArea, this.layout.RowHeaders); + } + // Discard frozen rows + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + false /*discardFrozenColumns*/, true /*discardFrozenRows*/); + + if (mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom) + { + // Mouse's Y is in-bound -- correct X value + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right-1 : rectScrollingArea.Left, mouseY); + if (this.ptAnchorCell.Y != -1 && + (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) != 0 && + this.trackRowEdge != -1 && + (this.Rows.GetRowState(this.trackRowEdge) & DataGridViewElementStates.Frozen) != 0 && + hti.row >= 0 && + (this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Frozen) == 0) + { + // Anchor cell is in frozen row and target cell is in unfrozen row. Make sure no row is scrolled off. + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstUnfrozenRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + int firstColumnIndex; + if (hti.col >= 0) + { + firstColumnIndex = hti.col; + } + else + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + firstColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + } + if (firstColumnIndex >= 0 && firstUnfrozenRowIndex >= 0) + { + if (!ScrollIntoView(firstColumnIndex, firstUnfrozenRowIndex, false /*forCurrentCellChange*/)) + { + return false; + } + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY); + } + } + return true; + } + + // Mouse's Y is outside of scrolling bands + if (mouseY < rectScrollingArea.Top) + { + if (this.ptAnchorCell.Y != -1 && + ((this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0 || + (this.trackRowEdge != -1 && (this.Rows.GetRowState(this.trackRowEdge) & DataGridViewElementStates.Frozen) == 0)) && + this.verticalOffset != 0) + { + // Up scrolling is required because the anchor's row is unfrozen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + yOffset = mouseY - rectScrollingArea.Top; // yOffset strictly negative + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Right-1; + } + else + { + mouseX = rectScrollingArea.Left+1; + } + } + else + { + hti = HitTest(this.RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY); + } + } + else + { + Debug.Assert(mouseY > rectScrollingArea.Bottom); + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (this.verticalOffset + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight) + { + // Down scrolling is required + yOffset = mouseY - rectScrollingArea.Bottom; // yOffset strictly positive + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Right-1; + } + else + { + mouseX = rectScrollingArea.Left+1; + } + } + } + } + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect]) + { + if (this.layout.ColumnHeadersVisible) + { + // Include column headers + rectScrollingArea = Rectangle.Union(rectScrollingArea, this.layout.ColumnHeaders); + } + + // Discard frozen columns + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + true /*discardFrozenColumns*/, false /*discardFrozenRows*/); + + if (mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right) + { + // Mouse's X is in-bound -- correct Y value + hti = HitTest(mouseX, rectScrollingArea.Top); + if (this.ptAnchorCell.X != -1 && + this.Columns[this.ptAnchorCell.X].Frozen && + this.trackColumnEdge != -1 && + this.Columns[this.trackColumnEdge].Frozen && + hti.col >= 0 && + !this.Columns[hti.col].Frozen) + { + // Anchor cell is in frozen column and target cell is in unfrozen column. Make sure no column is scrolled off. + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + int firstUnfrozenColumnIndex = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen).Index; + int firstRowIndex; + if (hti.row >= 0) + { + firstRowIndex = hti.row; + } + else + { + firstRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + } + if (firstRowIndex >= 0 && firstUnfrozenColumnIndex >= 0) + { + if (!ScrollIntoView(firstUnfrozenColumnIndex, firstRowIndex, false /*forCurrentCellChange*/)) + { + return false; + } + hti = HitTest(mouseX, rectScrollingArea.Top); + } + } + return true; + } + + // Mouse's X is outside of scrolling bands + if ((!this.RightToLeftInternal && mouseX < rectScrollingArea.Left) || + (this.RightToLeftInternal && mouseX > rectScrollingArea.Right)) + { + if (this.ptAnchorCell.X != -1 && + (!this.Columns[this.ptAnchorCell.X].Frozen || + (this.trackColumnEdge != -1 && !this.Columns[this.trackColumnEdge].Frozen)) && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.negOffset > 0 || + this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) != null)) + { + // xOffset strictly negative + if (this.RightToLeftInternal) + { + // Right scrolling is required + xOffset = rectScrollingArea.Right - mouseX; + } + else + { + // Left scrolling is required + xOffset = mouseX - rectScrollingArea.Left; + } + mouseY = rectScrollingArea.Top+1; + } + else + { + hti = HitTest(mouseX, rectScrollingArea.Top); + } + } + else + { + Debug.Assert((!this.RightToLeftInternal && mouseX > rectScrollingArea.Right) || (this.RightToLeftInternal && mouseX < rectScrollingArea.Left)); + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol != -1 && + this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.None) == null) + { + // No more columns to scroll + return true; + } + + DataGridViewColumn newFirstVisibleScrollingCol = this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + + if (this.HorizontalOffset != newColOffset) + { + // xOffset strictly positive + if (this.RightToLeftInternal) + { + // Left scrolling is required + xOffset = rectScrollingArea.Left - mouseX; + } + else + { + // Right scrolling is required + xOffset = mouseX - rectScrollingArea.Right; + } + mouseY = rectScrollingArea.Top+1; + } + } + } + return true; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + bool recomputeHitTestInfo = false; + + // Discard frozen columns and rows + DiscardZonesInScrollingArea(ref rectScrollingArea, emptyBackgroundWidth, emptyBackgroundHeight, frozenVisibleRowsHeight, + true /*discardFrozenColumns*/, true /*discardFrozenRows*/); + + if (mouseY < rectScrollingArea.Top) + { + // Mouse's Y is above scrolling bands + if ( + ( + (this.ptAnchorCell.Y != -1 && (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0) + || + (this.ptCurrentCell.Y != -1 && (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + ) + && + this.verticalOffset != 0 + ) + { + // Up scrolling is required - the anchor's row is unfrozen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + yOffset = mouseY - rectScrollingArea.Top; // yOffset strictly negative + } + else + { + // Correct mouse's Y - no scrolling can be performed + if (mouseY < this.layout.Data.Top) + { + mouseY = this.layout.Data.Top+1; + recomputeHitTestInfo = true; + } + } + } + else if (mouseY > rectScrollingArea.Bottom) + { + // Mouse's Y is below scrolling bands + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (this.verticalOffset + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight) + { + // Down scrolling is required + yOffset = mouseY - rectScrollingArea.Bottom; // yOffset strictly positive + } + else + { + // Correct mouse's Y - no scrolling can be performed + mouseY = rectScrollingArea.Bottom-1; + recomputeHitTestInfo = true; + } + } + else + { + // Correct mouse's Y - no scrolling can be performed + mouseY = rectScrollingArea.Bottom-1; + recomputeHitTestInfo = true; + } + } +#if DEBUG + else + { + // Mouse's Y is in-bound + Debug.Assert(mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom); + } +#endif + if ((!this.RightToLeftInternal && mouseX < rectScrollingArea.Left) || + (this.RightToLeftInternal && mouseX > rectScrollingArea.Right)) + { + // Mouse's X is on the left of scrolling bands (LTR) + if ( + ( + (this.ptAnchorCell.X != -1 && !this.Columns[this.ptAnchorCell.X].Frozen) + || + (this.ptCurrentCell.X != -1 && !this.Columns[this.ptCurrentCell.X].Frozen) + ) + && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.negOffset > 0 || + this.Columns.GetPreviousColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen) != null) + ) + { + // xOffset strictly negative + if (this.RightToLeftInternal) + { + // Right scrolling is required - anchor's column is unfrozen + xOffset = rectScrollingArea.Right - mouseX; + } + else + { + // Left scrolling is required - anchor's column is unfrozen + xOffset = mouseX - rectScrollingArea.Left; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (!this.RightToLeftInternal && mouseX < this.layout.Data.Left) + { + mouseX = this.layout.Data.Left+1; + recomputeHitTestInfo = true; + } + else if (this.RightToLeftInternal && mouseX > this.layout.Data.Right) + { + mouseX = this.layout.Data.Right-1; + recomputeHitTestInfo = true; + } + } + } + else if ((!this.RightToLeftInternal && mouseX > rectScrollingArea.Right) || + (this.RightToLeftInternal && mouseX < rectScrollingArea.Left)) + { + // Mouse's X is on the right of scrolling bands (LTR) + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && + (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol == -1 || + this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.None) != null)) + { + DataGridViewColumn newFirstVisibleScrollingCol = this.Columns.GetNextColumn(this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + if (this.HorizontalOffset != newColOffset) + { + // xOffset strictly positive + if (this.RightToLeftInternal) + { + // Left scrolling is required + xOffset = rectScrollingArea.Left - mouseX; + } + else + { + // Right scrolling is required + xOffset = mouseX - rectScrollingArea.Right; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Left+1; + } + else + { + mouseX = rectScrollingArea.Right-1; + } + recomputeHitTestInfo = true; + } + } + else + { + // Correct mouse's X - no scrolling can be performed + if (this.RightToLeftInternal) + { + mouseX = rectScrollingArea.Left+1; + } + else + { + mouseX = rectScrollingArea.Right-1; + } + recomputeHitTestInfo = true; + } + } +#if DEBUG + else + { + // Mouse's X is in-bound + Debug.Assert(mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right); + } +#endif + if (recomputeHitTestInfo) + { + hti = HitTest(mouseX, mouseY); + } + } + return true; + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + int bordersAndPaddingWidth = 2*(this.BorderWidth + this.Padding.Size.Width); + int bordersAndPaddingHeight = 2*(this.BorderWidth + this.Padding.Size.Height); + + bool allowHorizScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Horizontal); + bool allowVertScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Vertical); + + int minimumWidth = 16 + bordersAndPaddingWidth; + if (allowVertScrollbar) + { + minimumWidth += this.vertScrollBar.Width; + } + if (this.RowHeadersVisible) + { + minimumWidth += this.RowHeadersWidth; + } + int preferredWidth = Math.Min(minimumWidth + this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible), proposedConstraints.Width); + if (preferredWidth < minimumWidth) + { + preferredWidth = minimumWidth; + } + + int minimumHeight = 16 + bordersAndPaddingHeight; + if (allowHorizScrollbar) + { + minimumHeight += this.horizScrollBar.Height; + } + if (this.ColumnHeadersVisible) + { + minimumHeight += this.ColumnHeadersHeight; + } + int preferredHeight = Math.Min(minimumHeight + this.Rows.GetRowsHeight(DataGridViewElementStates.Visible), proposedConstraints.Height); + if (preferredHeight < minimumHeight) + { + preferredHeight = minimumHeight; + } + + return new Size(preferredWidth, preferredHeight); + } + + /// + // Rectangle returned includes the potential row header + public Rectangle GetRowDisplayRectangle(int rowIndex, bool cutOverflow) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return GetRowDisplayRectanglePrivate(rowIndex, cutOverflow); + } + + private Rectangle GetRowDisplayRectanglePrivate(int rowIndex, bool cutOverflow) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) == 0) + { + return Rectangle.Empty; + } + + Rectangle data = this.layout.Data; + int cy = data.Y; + bool rowFound = false; + int indexTmp; + for (indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + indexTmp != -1 && !rowFound; + ) + { + if (cy > data.Bottom) + { + break; + } + if (indexTmp == rowIndex) + { + rowFound = true; + } + else + { + cy += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + } + + if (!rowFound && this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + for (indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + indexTmp != -1 && !rowFound; + ) + { + if (cy > data.Bottom) + { + break; + } + if (indexTmp == rowIndex) + { + rowFound = true; + } + else + { + cy += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + } + } + } + + if (rowFound) + { + int displayHeight; + if (cutOverflow && cy + this.Rows.SharedRow(indexTmp).GetHeight(indexTmp) > data.Bottom) + { + displayHeight = data.Bottom - cy; + } + else + { + displayHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + } + + Rectangle rowRect = new Rectangle(data.X, + cy, + data.Width, + displayHeight); + if (this.layout.RowHeadersVisible) + { + rowRect.Width += this.layout.RowHeaders.Width; + if (!this.RightToLeftInternal) + { + rowRect.X -= this.layout.RowHeaders.Width; + } + } + return rowRect; + } + + return Rectangle.Empty; + } + + private int GetRowIndexFromY(int y, out int yRowTopEdge) + { + Rectangle data = this.layout.Data; + Debug.Assert(y >= data.Y-1 && y < data.Bottom, "y must be inside the vertical bounds of the data area."); + + if (y == data.Y-1) + { + y++; + } + + int rowHeight; + int cy = data.Y; + + // first try to match y against a frozen rows + int indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1 && cy < data.Bottom) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + cy += rowHeight; + if (cy > y) + { + yRowTopEdge = cy - rowHeight; + return indexTmp; + } + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + // second try to match y against a scrolling row + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0 && + (this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + + while (indexTmp != -1 && cy < data.Bottom) + { + rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + cy += rowHeight; + if (cy > y) + { + yRowTopEdge = cy - rowHeight; + return indexTmp; + } + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible); + } + } + + yRowTopEdge = -1; + return -1; + } + + private static int GetRowScrollRate(int yOffset) + { + Debug.Assert(yOffset > 0); + // Counting 10ms for executing the actual row scrolling + if (yOffset <= 10) + { + return 90; // Ten rows per second + } + if (yOffset <= 15) + { + return 57; // Fifteen rows per second + } + if (yOffset <= 25) + { + return 30; // Twenty-five rows per second + } + if (yOffset <= 35) + { + return 18; // Thirty-five rows per second + } + return Math.Max(1, 600 / yOffset); + } + + /// + /// Returns the coordinate of the upper edge of the given row. Note that + /// the row does not need to be completely visible on the display area. + /// Value returned is not necessarily within layout.Data because the row + /// may start below the data area. + /// + internal int GetRowYFromIndex(int index) + { + Debug.Assert(index >= 0 && index < this.Rows.Count); + Debug.Assert((this.Rows.GetRowState(index) & DataGridViewElementStates.Visible) != 0); + + int y = this.layout.Data.Y; + + int indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + if (index == indexTmp) + { + return y; + } + y += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + + int yFirstVisibleScrollingRow = y; + + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0) + { + if (index >= this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + // index is part of the scrolling rows below the frozen rows + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + } + else + { + // index is part of the rows hidden behind the frozen rows or on top of the control + indexTmp = -1; + } + } + else + { + // frozen rows cover all the rows real-estate. Look for index starting at the first visible non-frozen row. + indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(indexTmp != -1); + } + + if (indexTmp != -1) + { + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0 && + (this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + + while (indexTmp != -1) + { + if (index == indexTmp) + { + return y; + } + y += this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + indexTmp = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + } + } + + // Row is completely hidden behind frozen rows or on top of control + y = yFirstVisibleScrollingRow; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow != -1); + indexTmp = this.Rows.GetPreviousRow(this.displayedBandsInfo.FirstDisplayedScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + y -= this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (index == indexTmp) + { + return y; + } + indexTmp = this.Rows.GetPreviousRow(indexTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + } + + Debug.Fail("Could not find row in GetRowYFromIndex"); + return 0; + } + + private bool GetTabKeyEffective(bool shift, bool ctrl) + { + if (this.StandardTab) + { + return ctrl && + !((!shift && (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell)) || + (shift && (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell))); + } + else + { + return !ctrl && + !((!shift && (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell)) || + (shift && (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell))); + } + } + + /// + public HitTestInfo HitTest(int x, int y) + { + HitTestInfo hti = new HitTestInfo(); + + if (!this.layout.Inside.Contains(x, y)) + { + return hti; + } + + if (this.horizScrollBar != null && this.horizScrollBar.Visible && this.horizScrollBar.Bounds.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.HorizontalScrollBar; + return hti; + } + + if (this.vertScrollBar != null && this.vertScrollBar.Visible && this.vertScrollBar.Bounds.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.VerticalScrollBar; + return hti; + } + + if (this.layout.TopLeftHeader.Contains(x, y)) + { + hti.type = DataGridViewHitTestType.TopLeftHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeader; + if (this.RightToLeftInternal) + { + hti.colStart = this.layout.TopLeftHeader.Right-1; + } + else + { + hti.colStart = this.layout.TopLeftHeader.Left; + } + hti.rowStart = this.layout.TopLeftHeader.Top; + if ((!this.RightToLeftInternal && this.layout.TopLeftHeader.Right - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - this.layout.TopLeftHeader.Left < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = this.layout.TopLeftHeader.Left - x - 1; + } + else + { + hti.mouseBarOffset = this.layout.TopLeftHeader.Right - x - 1; + } + } + } + else if (this.layout.TopLeftHeader.Top + this.layout.TopLeftHeader.Height - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop; + hti.mouseBarOffset = this.layout.TopLeftHeader.Top + this.layout.TopLeftHeader.Height - y - 1; + } + } + return hti; + } + + // check for column resize / insertion + if (this.layout.ColumnHeaders.Contains(x, y)) + { + int xColumnLeftEdge; // this is actually the right edge in RTL mode + hti.col = GetColumnIndexFromX(x, out xColumnLeftEdge); + if (hti.col < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti.col)); + hti.type = DataGridViewHitTestType.ColumnHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeader; + hti.rowStart = this.layout.ColumnHeaders.Top; + hti.colStart = xColumnLeftEdge; + int columnWidth = this.Columns[hti.col].Thickness; + if ((!this.RightToLeftInternal && xColumnLeftEdge + columnWidth - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - xColumnLeftEdge + columnWidth < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - columnWidth - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge + columnWidth - x - 1; + } + DataGridViewColumn dataGridViewColumn = this.Columns[hti.col]; + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeRight; + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeaderRight; + } + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Left; + DataGridViewColumn dataGridViewColumn = null; + // VS Whidbey bug 317105 - Condition unnecessary + //if (hti.col != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + //{ + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + //} + if (dataGridViewColumn != null) + { + hti.adjacentCol = dataGridViewColumn.Index; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeLeft; + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeaderLeft; + } + } + else + { + if (this.RowHeadersVisible && this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + } + else + { + hti.typeInternal = DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft; + } + } + } + else if (this.layout.ColumnHeaders.Bottom - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + if (/*!this.RowHeadersVisible &&*/ this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom; + hti.mouseBarOffset = this.layout.ColumnHeaders.Bottom - y - 1; + } + } + } + + // check for row resize + if (this.layout.RowHeaders.Contains(x, y)) + { + int yRowTopEdge; + hti.row = GetRowIndexFromY(y, out yRowTopEdge); + if (hti.row < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti.row)); + hti.type = DataGridViewHitTestType.RowHeader; + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeader; + hti.rowStart = yRowTopEdge; + if (this.RightToLeftInternal) + { + hti.colStart = this.layout.RowHeaders.Right-1; + } + else + { + hti.colStart = this.layout.RowHeaders.Left; + } + int rowHeight = this.Rows.SharedRow(hti.row).GetHeight(hti.row); + if (yRowTopEdge + rowHeight - y < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom; + + if (RowIsResizable(hti.row) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom; + hti.mouseBarOffset = yRowTopEdge + rowHeight - y - 1; + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Top; + int indexTmp = -1; + if (hti.row != this.displayedBandsInfo.FirstDisplayedScrollingRow || this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + indexTmp = this.Rows.GetPreviousRow(hti.row, DataGridViewElementStates.Visible); + } + if (indexTmp != -1) + { + if (RowIsResizable(indexTmp) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop; + hti.adjacentRow = indexTmp; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + else + { + if (this.ColumnHeadersVisible && this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + else if ((!this.RightToLeftInternal && this.layout.RowHeaders.Right - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - this.layout.RowHeaders.Left < DATAGRIDVIEW_columnSizingHotZone)) + { + //hti.edge = DataGridViewHitTestTypeCloseEdge.Right; + if (this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeRight; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = this.layout.RowHeaders.Left - x - 1; + } + else + { + hti.mouseBarOffset = this.layout.RowHeaders.Right - x - 1; + } + } + } + } + + if (this.layout.Data.Contains(x, y)) + { + int xColumnLeftEdge, yRowTopEdge; + hti.col = GetColumnIndexFromX(x, out xColumnLeftEdge); + hti.row = GetRowIndexFromY(y, out yRowTopEdge); + if (hti.col < 0 || hti.row < 0) + { + return HitTestInfo.Nowhere; + } + Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti.col)); + Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti.row)); + hti.type = DataGridViewHitTestType.Cell; + hti.typeInternal = DataGridViewHitTestTypeInternal.Cell; + hti.rowStart = yRowTopEdge; + hti.colStart = xColumnLeftEdge; + if (!this.ColumnHeadersVisible) + { + int columnWidth = this.Columns[hti.col].Thickness; + if ((!this.RightToLeftInternal && xColumnLeftEdge + columnWidth - x < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && x - xColumnLeftEdge + columnWidth < DATAGRIDVIEW_columnSizingHotZone)) + { + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - columnWidth - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge + columnWidth - x - 1; + } + DataGridViewColumn dataGridViewColumn = this.Columns[hti.col]; + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeRight; + } + return hti; + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + DataGridViewColumn dataGridViewColumn = null; + if (hti.col != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + } + if (dataGridViewColumn != null) + { + hti.adjacentCol = dataGridViewColumn.Index; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x + 1; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + if (dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeLeft; + } + return hti; + } + else + { + if (this.RowHeadersVisible && this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + return hti; + } + } + } + } + else if ((!this.RightToLeftInternal && x - xColumnLeftEdge < DATAGRIDVIEW_columnSizingHotZone) || + (this.RightToLeftInternal && xColumnLeftEdge - x < DATAGRIDVIEW_columnSizingHotZone)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + Debug.Assert(dataGridViewColumn != null); + if (hti.col == dataGridViewColumn.Index && + this.RowHeadersVisible && + this.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft; + if (this.RightToLeftInternal) + { + hti.mouseBarOffset = xColumnLeftEdge - x; + } + else + { + hti.mouseBarOffset = xColumnLeftEdge - x - 1; + } + return hti; + } + } + + if (!this.RowHeadersVisible) + { + int rowHeight = this.Rows.SharedRow(hti.row).GetHeight(hti.row); + if (yRowTopEdge + rowHeight - y < DATAGRIDVIEW_rowSizingHotZone) + { + if (RowIsResizable(hti.row) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom; + hti.mouseBarOffset = yRowTopEdge + rowHeight - y - 1; + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + int indexTmp = -1; + if (hti.row != this.displayedBandsInfo.FirstDisplayedScrollingRow || this.displayedBandsInfo.NumDisplayedFrozenRows > 0) + { + indexTmp = this.Rows.GetPreviousRow(hti.row, + DataGridViewElementStates.Visible); + } + if (indexTmp != -1) + { + if (RowIsResizable(indexTmp) && this.AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop; + hti.adjacentRow = indexTmp; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + else + { + if (this.ColumnHeadersVisible && this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + } + else if (y - yRowTopEdge < DATAGRIDVIEW_rowSizingHotZone) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowIndex >= 0); + if (hti.row == rowIndex && + this.ColumnHeadersVisible && + this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing) + { + hti.typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop; + hti.mouseBarOffset = yRowTopEdge - y - 1; + } + } + } + + return hti; + } + + private void HorizScrollTimer_Tick(object sender, System.EventArgs e) + { + BeginInvoke(new MethodInvoker(HorizScrollTimerHandler)); + } + + private void HorizScrollTimerHandler() + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]); + + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y); + int xOffset, yOffset, mouseX = ptMouse.X, mouseY = ptMouse.Y; + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (xOffset != 0) + { + int absXOffset = Math.Abs(xOffset), normOffset = xOffset / absXOffset; + ScrollColumns(normOffset); + this.horizScrollTimer.Interval = GetColumnScrollRate(absXOffset); + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect]) + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), mouseY); + if (hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + if (yOffset != 0) + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), ptMouse.Y-yOffset-(yOffset/Math.Abs(yOffset))); + } + else + { + hti = HitTest(ptMouse.X+(this.RightToLeftInternal?1:-1)*(xOffset+normOffset), mouseY); + } + if (hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + this.HorizScrollTimer.Enabled = false; + } + } + } + + // Returns true for success, returns false when the OnDataError event cancels the operation. + private bool InitializeEditingCellValue(ref DataGridViewCellStyle dataGridViewCellStyle, ref DataGridViewCell dataGridViewCell) + { + DataGridViewDataErrorEventArgs dgvdee = null; + // Save unedited value so we can restore it later if parsing of new value fails + this.uneditedFormattedValue = dataGridViewCell.GetFormattedValue(this.ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + try + { + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + object currentFormattedValue = dataGridViewEditingCell.GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting); + if ((currentFormattedValue == null && this.uneditedFormattedValue != null) || + (currentFormattedValue != null && this.uneditedFormattedValue == null) || + (currentFormattedValue != null && !this.uneditedFormattedValue.Equals(currentFormattedValue))) + { + Debug.Assert(this.ptCurrentCell.X == dataGridViewCell.ColumnIndex); + dataGridViewCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unshare the edited cell + dataGridViewEditingCell = dataGridViewCell as IDataGridViewEditingCell; + dataGridViewEditingCell.EditingCellFormattedValue = this.uneditedFormattedValue; + dataGridViewEditingCell.EditingCellValueChanged = false; + } + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + OnDataErrorInternal(dgvdee); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + return !dgvdee.Cancel; + } + return true; + } + + // Returns true for success, returns false when the OnDataError event cancels the operation. + private bool InitializeEditingControlValue(ref DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(this.editingControl != null); + + DataGridViewDataErrorEventArgs dgvdee = null; + object initialFormattedValue = dataGridViewCell.GetFormattedValue(this.ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = true; + try + { + dataGridViewCell.InitializeEditingControl(this.ptCurrentCell.Y, initialFormattedValue, dataGridViewCellStyle); + ((IDataGridViewEditingControl)this.editingControl).EditingControlValueChanged = false; + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + dgvdee = new DataGridViewDataErrorEventArgs(exception, this.ptCurrentCell.X, + this.ptCurrentCell.Y, + DataGridViewDataErrorContexts.InitialValueRestoration); + OnDataErrorInternal(dgvdee); + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] = false; + } + if (dgvdee != null) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + return !dgvdee.Cancel; + } + + // Save unedited value so we can restore it later if parsing of new value fails + this.uneditedFormattedValue = initialFormattedValue; + return true; + } + + /// + public void InvalidateCell(DataGridViewCell dataGridViewCell) + { + if (dataGridViewCell == null) + { + throw new ArgumentNullException("dataGridViewCell"); + } + if (dataGridViewCell.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_CellDoesNotBelongToDataGridView)); + } + InvalidateCellPrivate(dataGridViewCell); + } + + private void InvalidateCellPrivate(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell != null); + Debug.Assert(dataGridViewCell.DataGridView == this); + InvalidateCell(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex); + } + + /// + public void InvalidateCell(int columnIndex, int rowIndex) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + InvalidateCellPrivate(columnIndex, rowIndex); + } + + private void InvalidateCellPrivate(int columnIndex, int rowIndex) + { + if (this.IsHandleCreated) + { + Rectangle cellDisplayRect = GetCellAdjustedDisplayRectangle(columnIndex, rowIndex, true); + if (!cellDisplayRect.IsEmpty) + { + Invalidate(cellDisplayRect); + } + } + } + + /// + /// + /// Invalidate the painting region for the column specified. + /// + public void InvalidateColumn(int columnIndex) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + InvalidateColumnInternal(columnIndex); + } + + internal void InvalidateColumnInternal(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.IsHandleCreated) + { + Rectangle columnDisplayRect = GetColumnDisplayRectanglePrivate(columnIndex, true); + if (!columnDisplayRect.IsEmpty) + { + Invalidate(columnDisplayRect); + } + } + } + + private void InvalidateData() + { + if (this.IsHandleCreated) + { + Invalidate(this.layout.Data); + } + } + + /// + /// Invalidates the scrollable area of the DataGridView. + /// + private void InvalidateInside() + { + if (this.IsHandleCreated) + { + Invalidate(this.layout.Inside); + } + } + + /// + /// + /// Invalidate the painting region for the row specified. + /// + public void InvalidateRow(int rowIndex) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + InvalidateRowPrivate(rowIndex); + } + + private void InvalidateRowPrivate(int rowIndex) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if (this.IsHandleCreated) + { + Rectangle rowDisplayRect = GetRowDisplayRectanglePrivate(rowIndex, true); + if (!rowDisplayRect.IsEmpty) + { + Invalidate(rowDisplayRect); + } + } + } + + private void InvalidateRowHeights() + { + this.Rows.InvalidateCachedRowsHeights(); + if (this.IsHandleCreated) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + } + } + + private void InvalidateRows(int lo, int hi) + { + Debug.Assert(lo <= hi); + Debug.Assert(lo < this.Rows.Count); + Debug.Assert(hi < this.Rows.Count); + + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 0) + { + return; + } + + Rectangle rowDisplayRect, data; + int top, bottom; + + data = this.layout.Data; + + // If "lo" is not visible, then get the next visible row + if ((this.Rows.GetRowState(lo) & DataGridViewElementStates.Visible) == 0) + { + lo = this.Rows.GetNextRow(lo, DataGridViewElementStates.Visible); + } + + if (lo == -1) + { + // there are no visible rows below "lo" so there is nothing to invalidate. + return; + } + + // If "hi" is not visible, then get the previous visible row + if ((this.Rows.GetRowState(hi) & DataGridViewElementStates.Visible) == 0) + { + hi = this.Rows.GetPreviousRow(hi, DataGridViewElementStates.Visible); + } + + Debug.Assert(lo <= hi); + Debug.Assert(lo > -1); + + rowDisplayRect = this.GetRowDisplayRectangle(lo, true /*cutOverflow*/); + + if (rowDisplayRect.IsEmpty) + { + // The top row is offscreen + if ((this.Rows.GetRowState(lo) & DataGridViewElementStates.Frozen) != 0) + { + // "lo" is a frozen row which is offscreen. + // This means that "lo" and any other row below it are offscreen. + return; + } + else if (this.displayedBandsInfo.NumDisplayedScrollingRows == 0) + { + // "lo" and all the rows below are scrolling rows but no scrolling rows are displayed. + return; + } + else if (lo >= this.displayedBandsInfo.FirstDisplayedScrollingRow && + this.Rows.GetRowCount(DataGridViewElementStates.Visible, + this.displayedBandsInfo.FirstDisplayedScrollingRow, + lo) >= this.displayedBandsInfo.NumDisplayedScrollingRows) + { + // "lo" is a scrolling row whose coordinates are below the last visible row. + return; + } + else + { + // "lo" is a scrolling row "behind" frozen rows. + // Start invalidating at the top of the first displayed scrolling row. + top = this.GetRowDisplayRectangle(this.displayedBandsInfo.FirstDisplayedScrollingRow, true /*cutOverflow*/).Top; + } + } + else + { + top = rowDisplayRect.Top; + } + + rowDisplayRect = this.GetRowDisplayRectangle(hi, true /*cutOverflow*/); + + if (rowDisplayRect.IsEmpty) + { + // The bottom row is offscreen. + if ((this.Rows.GetRowState(hi) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen) + { + // "hi" is a frozen row offscreen and "lo" is a frozen row on screen. + // Invalidate all the way to the bottom + bottom = data.Bottom; + } + else if (hi > this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + // "hi" is a scrolling row offscreen which is beyond the firstDisplayedScrollingRow + // Invalidate all the way to the bottom again. + bottom = data.Bottom; + } + else if (this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) == 0) + { + // "hi" is a scrolling row above the first displayed scrolling row and there are no frozen rows. + // There is nothing to invalidate. + return; + } + else + { + // "hi" is a scrolling row which is "behind" the frozen rows. + // Invalidate all the way to the bottom of the frozen rows + // Compute the bottom of the last displayed frozen row. + // There may be invisible rows between the frozen rows. + bottom = 0; + for (int i = 0; i < this.displayedBandsInfo.NumDisplayedFrozenRows;) + { + if ((this.Rows.GetRowState(i) & DataGridViewElementStates.Visible) == 0) + { + continue; + } + + if (i == this.displayedBandsInfo.NumDisplayedFrozenRows - 1) + { + bottom = this.GetRowDisplayRectangle(i, true /*cutOverflow*/).Bottom; + break; + } + + i ++; + } + + if (bottom <= top) + { + // In this case both "lo" and "hi" are two scrolling rows behind the frozen rows. + // Nothing to invalidate. + return; + } + } + } + else + { + bottom = rowDisplayRect.Bottom; + } + + Invalidate(new Rectangle(data.X, top, data.Width, bottom - top)); + } + + private void InvalidateScrollBars() + { + // invalidate the horizontal and the vertical scrollbars + // note that the scrollbars can be null - this happens when + // the control has been disposed. + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + this.horizScrollBar.Invalidate(); + } + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + this.vertScrollBar.Invalidate(); + } + } + + private bool IsColumnOutOfBounds(int columnIndex) + { + return columnIndex >= this.Columns.Count || columnIndex == -1; + } + + private bool IsInnerCellOutOfBounds(int columnIndex, int rowIndex) + { + return columnIndex >= this.Columns.Count || rowIndex >= this.Rows.Count || columnIndex == -1 || rowIndex == -1; + } + + private bool IsRowOutOfBounds(int rowIndex) + { + return rowIndex >= this.Rows.Count || rowIndex == -1; + } + + /// + protected override bool IsInputChar(char charCode) + { + if (this.editingControl != null && + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage]) + { + // Do not process key press in ProcessDialogChar. + return true; + } + else + { + return base.IsInputChar(charCode); + } + } + + /// + protected override bool IsInputKey(Keys keyData) + { + if ((keyData & Keys.Alt) == Keys.Alt) + { + return false; + } + switch (keyData & Keys.KeyCode) + { + case Keys.Escape: + { + return this.IsEscapeKeyEffective; + } + + case Keys.Tab: + { + return GetTabKeyEffective((keyData & Keys.Shift) == Keys.Shift, (keyData & Keys.Control) == Keys.Control); + } + + case Keys.A: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control) + { + return true; + } + break; + } + + case Keys.C: + case Keys.Insert: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control) + { + return true; + } + break; + } + + case Keys.Space: + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && + (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect || + this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) && + this.ptCurrentCell.X != -1) + { + return true; + } + break; + } + + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + case Keys.Home: + case Keys.End: + case Keys.Next: + case Keys.Prior: + case Keys.Enter: + case Keys.Delete: + case Keys.D0: + case Keys.NumPad0: + case Keys.F2: + case Keys.F3: + { + return true; + } + } + return base.IsInputKey(keyData); + } + + /// + /// Determines if Scrollbars should be visible, + /// updates their bounds and the bounds of all + /// other regions in the dataGridView's Layout. + /// + private void LayoutScrollBars() + { + SuspendLayout(); + try + { + // Scrollbars are a tricky issue. + // We need to see if we can cram our columns and rows + // in without scrollbars and if they don't fit, we make + // scrollbars visible and then fixup our regions for the + // data. + bool allowHorizScrollbar = ((this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Horizontal)) && + this.dataGridViewState2[DATAGRIDVIEWSTATE2_allowHorizontalScrollbar]; + bool allowVertScrollbar = (this.scrollBars == ScrollBars.Both) || (this.scrollBars == ScrollBars.Vertical); + bool needHorizScrollbarWithoutVertScrollbar = false; + bool needHorizScrollbar = false; + bool needVertScrollbar = false; + bool rightToLeftInternal = this.RightToLeftInternal; + int oldfirstDisplayedScrollingRow; + + int totalVisibleColCount = this.Columns.GetColumnCount(DataGridViewElementStates.Visible); + int totalVisibleRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible); + int totalVisibleWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible); + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + // Expensive call - dataGridView could have a mode where no row is resizable which would result in better perfs + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + int horizScrollBarHeight = this.horizScrollBar.Height = SystemInformation.HorizontalScrollBarHeight; + int vertScrollBarWidth = this.vertScrollBar.Width = SystemInformation.VerticalScrollBarWidth; + + if (allowHorizScrollbar && + totalVisibleWidth > this.layout.Data.Width && totalVisibleFrozenWidth < this.layout.Data.Width && + horizScrollBarHeight <= this.layout.Data.Height) + { + int oldDataHeight = this.layout.Data.Height; + this.layout.Data.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.Data.Height >= 0); + needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true; + if (totalVisibleWidth - this.layout.Data.Width <= vertScrollBarWidth || + this.layout.Data.Width - totalVisibleFrozenWidth <= vertScrollBarWidth) + { + // Would we still need a horizontal scrollbar if there were a vertical one? + oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + ComputeVisibleRows(); + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))) + { + needHorizScrollbar = (totalVisibleFrozenWidth < this.layout.Data.Width - vertScrollBarWidth); + } + this.displayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow; + } + + if (needHorizScrollbar) + { + if (this.layout.RowHeadersVisible) + { + this.layout.RowHeaders.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.RowHeaders.Height >= 0); + } + } + else + { + // Restore old data height because turns out a horizontal scroll bar wouldn't make sense + this.layout.Data.Height = oldDataHeight; + } + } + + oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + ComputeVisibleRows(); + if (allowVertScrollbar && + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + vertScrollBarWidth <= this.layout.Data.Width) + { + this.layout.Data.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.Data.Width >= 0); + if (rightToLeftInternal) + { + this.layout.Data.X += vertScrollBarWidth; + } + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.ColumnHeaders.Width >= 0); + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X += vertScrollBarWidth; + } + } + needVertScrollbar = true; + } + + this.displayedBandsInfo.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn(); + // we compute the number of visible columns only after we set up the vertical scroll bar. + ComputeVisibleColumns(); + + if (allowHorizScrollbar && + needVertScrollbar && !needHorizScrollbar && + totalVisibleWidth > this.layout.Data.Width && totalVisibleFrozenWidth < this.layout.Data.Width && + horizScrollBarHeight <= this.layout.Data.Height) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow; + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width += vertScrollBarWidth; + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X -= vertScrollBarWidth; + } + } + this.layout.Data.Width += vertScrollBarWidth; + if (rightToLeftInternal) + { + this.layout.Data.X -= vertScrollBarWidth; + } + this.layout.Data.Height -= horizScrollBarHeight; + Debug.Assert(this.layout.Data.Height >= 0); + needVertScrollbar = false; + + ComputeVisibleRows(); + if (this.displayedBandsInfo.NumTotallyDisplayedFrozenRows == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) && + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount && + (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) && + this.layout.Data.Height > totalVisibleFrozenHeight && + vertScrollBarWidth <= this.layout.Data.Width) + { + this.layout.Data.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.Data.Width >= 0); + if (rightToLeftInternal) + { + this.layout.Data.X += vertScrollBarWidth; + } + if (this.layout.ColumnHeadersVisible) + { + this.layout.ColumnHeaders.Width -= vertScrollBarWidth; + Debug.Assert(this.layout.ColumnHeaders.Width >= 0); + if (rightToLeftInternal) + { + this.layout.ColumnHeaders.X += vertScrollBarWidth; + } + } + needVertScrollbar = true; + } + if (needVertScrollbar) + { + needHorizScrollbar = true; + } + else + { + needHorizScrollbar = needHorizScrollbarWithoutVertScrollbar; + } + } + + this.layout.ResizeBoxRect = new Rectangle(); + if (needVertScrollbar && needHorizScrollbar) + { + this.layout.ResizeBoxRect = new Rectangle( + rightToLeftInternal ? this.layout.Data.X - this.vertScrollBar.Width : this.layout.Data.Right, + this.layout.Data.Bottom, + this.vertScrollBar.Width, + this.horizScrollBar.Height); + } + + if (needHorizScrollbar && totalVisibleColCount > 0) + { + int widthNotVisible = totalVisibleWidth - this.layout.Data.Width; + + this.horizScrollBar.Minimum = 0; + this.horizScrollBar.Maximum = totalVisibleWidth - totalVisibleFrozenWidth; + Debug.Assert(this.horizScrollBar.Maximum > 0); + this.horizScrollBar.SmallChange = 1; + this.horizScrollBar.LargeChange = Math.Max(totalVisibleWidth - totalVisibleFrozenWidth - widthNotVisible, 0); + this.horizScrollBar.Enabled = this.Enabled; + this.horizScrollBar.Bounds = new Rectangle( + rightToLeftInternal ? this.layout.Inside.X + this.layout.ResizeBoxRect.Width : this.layout.Inside.X, + this.layout.Data.Bottom, + this.layout.Inside.Width - this.layout.ResizeBoxRect.Width, + this.horizScrollBar.Height); + this.horizScrollBar.Visible = true; + this.horizScrollBar.Invalidate(); + } + else + { + this.horizScrollBar.Visible = false; + this.HorizontalOffset = 0; + + this.horizScrollBar.Enabled = false; + this.horizScrollBar.Minimum = 0; + this.horizScrollBar.Maximum = 1; + this.horizScrollBar.SmallChange = 1; + this.horizScrollBar.LargeChange = 1; + this.horizScrollBar.Value = 0; + } + + if (needVertScrollbar) + { + int vertScrollBarTop = this.layout.Data.Y; + int vertScrollBarHeight = this.layout.Data.Height; + if (this.layout.ColumnHeadersVisible) + { + vertScrollBarTop = this.layout.ColumnHeaders.Y; + vertScrollBarHeight += this.layout.ColumnHeaders.Height; + } + else if (this.SingleHorizontalBorderAdded) + { + vertScrollBarTop--; + vertScrollBarHeight++; + } + + this.vertScrollBar.Minimum = 0; + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + Debug.Assert(this.vertScrollBar.Maximum > 0); + this.vertScrollBar.Value = ComputeHeightOfScrolledOffRows(); + this.vertScrollBar.LargeChange = this.layout.Data.Height - totalVisibleFrozenHeight; + this.vertScrollBar.Bounds = new Rectangle( + rightToLeftInternal ? this.layout.Data.X - this.vertScrollBar.Width : this.layout.Data.Right, + vertScrollBarTop, + this.vertScrollBar.Width, + vertScrollBarHeight); + this.vertScrollBar.Enabled = this.Enabled; + this.vertScrollBar.Visible = true; + this.vertScrollBar.Invalidate(); + + this.verticalOffset = this.vertScrollBar.Value; + } + else + { + this.vertScrollBar.Visible = false; + this.verticalOffset = ComputeHeightOfScrolledOffRows(); + + this.vertScrollBar.Enabled = false; + this.vertScrollBar.Minimum = 0; + this.vertScrollBar.Maximum = 1; + this.vertScrollBar.LargeChange = 1; + this.vertScrollBar.Value = 0; + } + } + finally + { + ResumeLayout(false); + } + } + + private void MakeFirstDisplayedCellCurrentCell(bool includeNewRow) + { + // No current cell - try to set the first displayed cell to be the current one. + Point firstDisplayedCellAddress = this.FirstDisplayedCellAddress; + if (firstDisplayedCellAddress.X != -1 && + (includeNewRow || + !this.AllowUserToAddRowsInternal || + firstDisplayedCellAddress.Y != this.Rows.Count - 1)) + { + bool success = SetAndSelectCurrentCellAddress(firstDisplayedCellAddress.X, + firstDisplayedCellAddress.Y, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + true /*clearSelection*/, + false /*forceCurrentCellSelection (unused)*/); + Debug.Assert(success); + } + } + + private static DataGridViewAutoSizeRowMode MapAutoSizeRowsModeToRowMode(DataGridViewAutoSizeRowsMode autoSizeRowsMode) + { + switch (autoSizeRowsMode) + { + case DataGridViewAutoSizeRowsMode.AllHeaders: + return DataGridViewAutoSizeRowMode.RowHeader; + case DataGridViewAutoSizeRowsMode.DisplayedHeaders: + return DataGridViewAutoSizeRowMode.RowHeader; + case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders: + return DataGridViewAutoSizeRowMode.AllCellsExceptHeader; + case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders: + return DataGridViewAutoSizeRowMode.AllCellsExceptHeader; + case DataGridViewAutoSizeRowsMode.AllCells: + return DataGridViewAutoSizeRowMode.AllCells; + case DataGridViewAutoSizeRowsMode.DisplayedCells: + return DataGridViewAutoSizeRowMode.AllCells; + default: + Debug.Fail("Unexpected autoSizeRowsMode value in MapAutoSizeRowsModeToRowMode"); + return DataGridViewAutoSizeRowMode.RowHeader; + } + } + + private void MoveColumnHeadersOrRowResize(MouseEventArgs e) + { + this.lastRowSplitBar = this.currentRowSplitBar; + this.currentRowSplitBar = e.Y; + Rectangle lastSplitBarRect = CalcRowResizeFeedbackRect(this.lastRowSplitBar); + if (this.editingControl != null && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] && + this.editingPanel.Bounds.IntersectsWith(lastSplitBarRect)) + { + this.editingPanel.Invalidate(); + this.editingPanel.Update(); + this.editingControl.Invalidate(); + this.editingControl.Update(); + } + Invalidate(lastSplitBarRect); + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + + private void MapDataGridViewColumnToDataBoundField(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(this.DataSource != null, "this method should only be called when we have a data connection"); + Debug.Assert(dataGridViewColumn.DataPropertyName.Length != 0, "this method should be called only for columns which have DataPropertyName set"); + DataGridViewDataConnection conn = this.DataConnection; + + int boundColumnIndex = ((conn == null) ? -1 : conn.BoundColumnIndex(dataGridViewColumn.DataPropertyName)); + if (boundColumnIndex != -1) + { + dataGridViewColumn.IsDataBoundInternal = true; + dataGridViewColumn.BoundColumnIndex = boundColumnIndex; + dataGridViewColumn.BoundColumnConverter = conn.BoundColumnConverter(boundColumnIndex); + dataGridViewColumn.ValueType = conn.BoundColumnValueType(boundColumnIndex); + dataGridViewColumn.ReadOnly = conn.DataFieldIsReadOnly(dataGridViewColumn.BoundColumnIndex) || dataGridViewColumn.ReadOnly; + InvalidateColumnInternal(dataGridViewColumn.Index); + + // Set the Sorting information on the data grid view according to the new DataPropertyName. + // RefreshColumns() has its own routine for setting the Sorting information so don't do this step + // if we are in RefreshColumns(); + if (dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = conn.BoundColumnSortOrder(boundColumnIndex); + if (this.sortedColumn == null && dataGridViewColumn.HeaderCell.SortGlyphDirection != SortOrder.None) + { + this.sortedColumn = dataGridViewColumn; + this.sortOrder = dataGridViewColumn.HeaderCell.SortGlyphDirection; + // no need to sort because the back end is already sorted.... + } + } + } + else + { + dataGridViewColumn.IsDataBoundInternal = false; + dataGridViewColumn.BoundColumnIndex = -1; + dataGridViewColumn.BoundColumnConverter = null; + InvalidateColumnInternal(dataGridViewColumn.Index); + } + } + + private void MoveColumnRelocation(MouseEventArgs e, HitTestInfo hti) + { + this.lastHeaderShadow = e.X; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = ColumnRelocationTarget(e, hti, out this.trackColumnEdge); + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + + private void MoveRowHeadersOrColumnResize(int x) + { + this.lastColSplitBar = this.currentColSplitBar; + this.currentColSplitBar = x; + Rectangle lastSplitBarRect = CalcColResizeFeedbackRect(this.lastColSplitBar); + if (this.editingControl != null && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] && + this.editingPanel.Bounds.IntersectsWith(lastSplitBarRect)) + { + this.editingPanel.Invalidate(); + this.editingPanel.Update(); + this.editingControl.Invalidate(); + this.editingControl.Update(); + } + Invalidate(lastSplitBarRect); + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + /// + public virtual void NotifyCurrentCellDirty(bool dirty) + { + Debug.Assert(this.ptCurrentCell.X >= 0 && this.ptCurrentCell.X < this.Columns.Count); + Debug.Assert(this.ptCurrentCell.Y >= 0 && this.ptCurrentCell.Y < this.Rows.Count); + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ignoringEditingChanges] == false) + { + // autosizing has no effect since edited value hasn't been committed + // and autosizing code only looks at committed values. + + this.IsCurrentCellDirtyInternal = dirty; + if (dirty && this.editingControl != null && ((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + internal void OnAddedColumn(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.Index < this.Columns.Count); + Debug.Assert(dataGridViewColumn.DataGridView == this); + + if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex >= this.Columns.Count) + { + // Developer did not assign a DisplayIndex or picked a large number. + // Choose the Index as the DisplayIndex. + dataGridViewColumn.DisplayIndexInternal = dataGridViewColumn.Index; + this.Columns.InvalidateCachedColumnsOrder(); + } + + CorrectColumnDisplayIndexesAfterInsertion(dataGridViewColumn); + + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.DataGridViewInternal = this; + } + + AdjustExpandingRows(dataGridViewColumn.Index, false /*fixedWidth*/); + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + bool fixedColumnWidth = autoSizeColumnMode == DataGridViewAutoSizeColumnMode.None || + autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + + if (!fixedColumnWidth) + { + // This is the first time the column autosizes. Save current column width for later reuse. + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, true /*fixedHeight*/); + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + + // Raise the ColumnAdded event + OnColumnAdded(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + internal void OnAddedRow_PreNotification(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + + if (this.AllowUserToAddRowsInternal && this.newRowIndex == -1) + { + // The added row is necessarily the 'new row' + // Set the this.newRowIndex variable as early as possible. + Debug.Assert(rowIndex == this.Rows.Count - 1); + this.newRowIndex = rowIndex; + } + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + +#if DEBUG + DataGridViewRow dataGridViewRowDebug = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRowDebug.Cells) + { + Debug.Assert(!dataGridViewCell.Selected); + Debug.Assert(dataGridViewRowDebug.Index != -1 || !dataGridViewCell.HasValue); + } +#endif + + // Update this.individualReadOnlyCells + if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + if (!dataGridViewCell.OwningColumn.ReadOnly && IsSharedCellReadOnly(dataGridViewCell, rowIndex)) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + } + } + } + } + + internal void OnAddedRow_PostNotification(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + + if ((rowState & DataGridViewElementStates.Visible) != 0) + { + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + + bool autoSizeRow = false; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None && + !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + this.Rows.SharedRow(rowIndex).CachedThickness = rowHeight; + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + bool columnAutoSized; + if (this.Rows.GetRowCount(DataGridViewElementStates.Visible) > 1) + { + // Columns can only expand, and not collapse. + columnAutoSized = AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex); + } + else + { + columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + } + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool rowHeadersAutoSize = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + + if (!rowHeadersAutoSize && !columnAutoSized) + { + // No need to autosize the column headers when the row headers and columns don't change. + fixedColumnHeadersHeight = true; + } + + // Auto size row headers + if (rowHeadersAutoSize) + { + AutoResizeRowHeadersWidth(rowIndex, this.rowHeadersWidthSizeMode, fixedColumnHeadersHeight, true /*fixedRowsHeight*/); + } + + // Auto size column headers + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (rowHeadersAutoSize && !fixedColumnHeadersHeight) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(rowIndex, this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + } + } + + internal void OnAddedRows_PreNotification(DataGridViewRow[] dataGridViewRows) + { + // Note: no row can be added that breaks the frozen row packing on the top + + foreach(DataGridViewRow dataGridViewRow in dataGridViewRows) + { + OnAddedRow_PreNotification(dataGridViewRow.Index); + } + } + + internal void OnAddedRows_PostNotification(DataGridViewRow[] dataGridViewRows) + { + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + OnAddedRow_PostNotification(dataGridViewRow.Index); + } + } + + internal void OnAddingColumn(DataGridViewColumn dataGridViewColumn) + { + // throw an exception if the column to be added breaks the rules + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None) + dataGridViewColumn.FillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectColumnFrozenState(dataGridViewColumn, this.Columns.Count); + + // prepare the existing rows by appending cells of correct type + if (this.Rows.Count > 0) + { + // Only require a default cell type when there are rows to fill + if (dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + } + + int newColumnCount = this.Columns.Count + 1; + + try + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < newColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + dataGridViewRow.Cells.AddInternal(dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.SetValueInternal(rowIndex, dataGridViewCellNew.DefaultNewRowValue); + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + catch + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count == newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(newColumnCount - 1); + } + else + { + Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount); + break; + } + } + throw; + } + } + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // not legitimate + ] + internal void OnAddingColumns(DataGridViewColumn[] dataGridViewColumns) + { + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None); + Debug.Assert(weightSum <= (float)ushort.MaxValue); + + // throw an exception if any of the columns to be added breaks the rules + Debug.Assert(dataGridViewColumns != null); + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + if (dataGridViewColumn == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AtLeastOneColumnIsNull)); + } + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + // Only require a default cell type when there are rows to fill + if (this.Rows.Count > 0 && dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + weightSum += dataGridViewColumn.FillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + } + + Debug.Assert(weightSum <= (float)ushort.MaxValue); + + // make sure no two columns are identical + int columnCount = dataGridViewColumns.Length; + for (int column1 = 0; column1 < columnCount - 1; column1++) + { + for (int column2 = column1 + 1; column2 < columnCount; column2++) + { + if (dataGridViewColumns[column1] == dataGridViewColumns[column2]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddIdenticalColumns)); + } + } + } + + // check for correctness of frozen states - throws exception if any state is incorrect. + CorrectColumnFrozenStates(dataGridViewColumns); + + // prepare the existing rows by appending cells of correct type + if (this.Rows.Count > 0) + { + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + Debug.Assert(dataGridViewColumn.CellType != null); + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + break; + } + } + + int previousColumnCount = this.Columns.Count; + int addedColumnCount = 0; + + try + { + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + addedColumnCount++; + Debug.Assert(dataGridViewColumn.CellType != null); + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < previousColumnCount + addedColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + int indexCell = dataGridViewRow.Cells.AddInternal(dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue; + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + } + catch + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + while (dataGridViewRow.Cells.Count > previousColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(dataGridViewRow.Cells.Count - 1); + } + } + throw; + } + } + } + + internal void OnAddingRow(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, bool checkFrozenState) + { + // Note dataGridViewRow.DataGridView != null for duplication of shared rows. + + // throw an exception if the row to be added breaks the rules + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + + // !Do not check for dataGridViewRow.Selected flag. Caller does it instead! + // !Do not check for dataGridViewRow.DataGridView != null. Caller does it instead! + + if (checkFrozenState) + { + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectRowFrozenState(dataGridViewRow, rowState, this.Rows.Count); + } + + if (this.ReadOnly && dataGridViewRow.DataGridView == null && dataGridViewRow.ReadOnly) + { + // Clear the superfluous flag since the whole dataGridView is read-only + dataGridViewRow.ReadOnly = false; + } + + int columnIndex = 0; + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if ((this.ReadOnly || dataGridViewColumn.ReadOnly) && dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly)) + { + // Clear superfluous flag since the whole dataGridView or column is ReadOnly + dataGridViewCell.ReadOnlyInternal = false; + } + columnIndex++; + } + } + + internal void OnAddingRows(DataGridViewRow[] dataGridViewRows, bool checkFrozenStates) + { + // throw an exception if any of the rows to be added breaks the rules + Debug.Assert(dataGridViewRows != null); + + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + if (dataGridViewRow == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AtLeastOneRowIsNull)); + } + + if (dataGridViewRow.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + + if (dataGridViewRow.Selected) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CannotAddOrInsertSelectedRow)); + } + + if (dataGridViewRow.Cells.Count > this.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_TooManyCells)); + } + } + + // make sure no two rows are identical + int rowCount = dataGridViewRows.Length; + for (int row1 = 0; row1 < rowCount - 1; row1++) + { + for (int row2 = row1 + 1; row2 < rowCount; row2++) + { + if (dataGridViewRows[row1] == dataGridViewRows[row2]) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddIdenticalRows)); + } + } + } + + if (checkFrozenStates) + { + Debug.Assert(!this.AllowUserToAddRowsInternal); + CorrectRowFrozenStates(dataGridViewRows, this.Rows.Count /*rowIndexInserted*/); + } + + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + CompleteCellsCollection(dataGridViewRow); + OnAddingRow(dataGridViewRow, dataGridViewRow.State, false /*checkFrozenState*/); + } + } + + internal void OnAdvancedBorderStyleChanged(DataGridViewAdvancedBorderStyle dgvabs) + { + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inBorderStyleChange]) + { + if (dgvabs == this.advancedCellBorderStyle) + { + OnCellBorderStyleChanged(EventArgs.Empty); + } + else if (dgvabs == this.advancedColumnHeadersBorderStyle) + { + OnColumnHeadersBorderStyleChanged(EventArgs.Empty); + } + else if (dgvabs == this.advancedRowHeadersBorderStyle) + { + OnRowHeadersBorderStyleChanged(EventArgs.Empty); + } + } + } + + /// + protected virtual void OnAllowUserToAddRowsChanged(EventArgs e) + { + PushAllowUserToAddRows(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTOADDROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToDeleteRowsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTODELETEROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToOrderColumnsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTOORDERCOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToResizeColumnsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTORESIZECOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAllowUserToResizeRowsChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALLOWUSERTORESIZEROWSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAlternatingRowsDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + InvalidateData(); + } + else + { + OnRowsGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWALTERNATINGROWSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAutoGenerateColumnsChanged(EventArgs e) + { + if (this.AutoGenerateColumns && this.DataSource != null) + { + // refresh the list of columns and the rows + RefreshColumnsAndRows(); + } + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOGENERATECOLUMNSCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnAutoSizeColumnModeChanged(DataGridViewColumn dataGridViewColumn, DataGridViewAutoSizeColumnMode previousInheritedMode) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewAutoSizeColumnModeEventArgs dgvascme = new DataGridViewAutoSizeColumnModeEventArgs(dataGridViewColumn, previousInheritedMode); + OnAutoSizeColumnModeChanged(dgvascme); + } + + /// + protected virtual void OnAutoSizeColumnModeChanged(DataGridViewAutoSizeColumnModeEventArgs e) + { + DataGridViewColumn dataGridViewColumn = e.Column; + if (e.Column == null) + { + throw new InvalidOperationException(SR.GetString(SR.InvalidNullArgument, "e.Column")); + } + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + + DataGridViewAutoSizeColumnMode previousInheritedMode = e.PreviousMode; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet; + + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill || + previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None) + { + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + if (!previousInheritedModeAutoSized) + { + // Save current column width for later reuse + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + } + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal) autoSizeColumnMode, + fixedHeight); + } + } + else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized) + { + // Restoring cached column width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + + // Auto fill columns if needed + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + // Autosize rows and column headers if needed + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + + // Column gets autosized with 1 degree of freedom this time. + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + true /*fixedHeight*/); + } + } + + DataGridViewAutoSizeColumnModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZECOLUMNMODECHANGED] as DataGridViewAutoSizeColumnModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.PreviousModes is more precise than just e + protected virtual void OnAutoSizeColumnsModeChanged(DataGridViewAutoSizeColumnsModeEventArgs e) + { + DataGridViewAutoSizeColumnMode[] previousModes = e.PreviousModes; + if (previousModes == null) + { + throw new ArgumentNullException("e.PreviousModes"); + } + if (previousModes.Length != this.Columns.Count) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_PreviousModesHasWrongLength)); + } + + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + if (dataGridViewColumn.Visible) + { + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + DataGridViewAutoSizeColumnMode previousInheritedMode = previousModes[dataGridViewColumn.Index]; + bool previousInheritedModeAutoSized = previousInheritedMode != DataGridViewAutoSizeColumnMode.Fill && + previousInheritedMode != DataGridViewAutoSizeColumnMode.None && + previousInheritedMode != DataGridViewAutoSizeColumnMode.NotSet; + if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill || + previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + } + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None) + { + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + if (!previousInheritedModeAutoSized) + { + // Save current column width for later reuse + dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness; + } + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0 /*fixedHeight*/); + } + } + else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized) + { + // Restoring cached column width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + } + } + + // Auto fill columns if needed + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + // Autosize rows and column headers if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + + // Second pass of column autosizing with 1 degree of freedom + foreach (DataGridViewColumn dataGridViewColumn in this.Columns) + { + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(dataGridViewColumn.Index, + (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, + true /*fixedHeight*/); + } + } + } + + DataGridViewAutoSizeColumnsModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZECOLUMNSMODECHANGED] as DataGridViewAutoSizeColumnsModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnAutoSizeRowsModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + // restore cached rows thickness + RestoreRowsCachedThickness(); + } + else + { + if (!e.PreviousModeAutoSized) + { + // Save the rows thickness for later reuse + // Note that only visible rows are affected, contrary to columns in OnAutoSizeColumnsModeChanged where all columns are affected. + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + this.Rows.SharedRow(rowIndex).CachedThickness = rowHeight; + } + } + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + DataGridViewAutoSizeModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWAUTOSIZEROWSMODECHANGED] as DataGridViewAutoSizeModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnBackgroundColorChanged(EventArgs e) + { + InvalidateInside(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWBACKGROUNDCOLORCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnBandContextMenuStripChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnContextMenuStripChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowContextMenuStripChanged(dgvre); + } + } + + internal void OnBandDefaultCellStyleChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDefaultCellStyleChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowDefaultCellStyleChanged(dgvre); + } + } + + internal void OnBandDividerThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDividerWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowDividerHeightChanged(dgvre); + } + } + + internal void OnBandHeaderCellChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnHeaderCellChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowHeaderCellChanged(dgvre); + } + } + + internal void OnBandMinimumThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnMinimumWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowMinimumHeightChanged(dgvre); + } + } + + internal void OnBandThicknessChanged(DataGridViewBand dataGridViewBand) + { + DataGridViewColumn dataGridViewColumn = dataGridViewBand as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnWidthChanged(dgvce); + } + else + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs((DataGridViewRow) dataGridViewBand); + OnRowHeightChanged(dgvre); + } + } + + internal void OnBandThicknessChanging() + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + } + + /// + protected override void OnBindingContextChanged(EventArgs e) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged]) + { + return; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged] = true; + try + { + if (this.dataConnection != null) + { + this.CurrentCell = null; + try + { + this.dataConnection.SetDataConnection(this.DataSource, this.DataMember); + } + catch (ArgumentException) + { + if (this.DesignMode) + { + // This is the minimal fix for vsw 527646. + // If the DataMember became invalid at DesignTime then set it to String.Empty, + // regenerate the column collection and DO NOT send BindingContextChanged event. + this.DataMember = String.Empty; + RefreshColumnsAndRows(); + return; + } + else + { + throw; + } + } + RefreshColumnsAndRows(); + base.OnBindingContextChanged(e); + if (this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + else + { + base.OnBindingContextChanged(e); + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_inBindingContextChanged] = false; + } + } + + /// + protected virtual void OnBorderStyleChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCancelRowEdit(QuestionEventArgs e) + { + QuestionEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCANCELROWEDIT] as QuestionEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellBeginEdit(DataGridViewCellCancelEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLBEGINEDIT] as DataGridViewCellCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCellBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellClickInternal(DataGridViewCellEventArgs e) + { + OnCellClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnClickInternal(e); + } + else + { + dataGridViewCell.OnClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellCommonChange(int columnIndex, int rowIndex) + { + if (columnIndex == -1) + { + // row or topleft header characteristic has changed + OnRowHeaderGlobalAutoSize(rowIndex); + } + else + { + if (rowIndex == -1) + { + // column header characteristic has changed + OnColumnHeaderGlobalAutoSize(columnIndex); + } + else + { + // regular cell characteristic changed + InvalidateCellPrivate(columnIndex, rowIndex); + + bool rowDisplayed = false; + if (rowIndex != -1) + { + rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + } + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + bool autoSizeColumn = (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.AllRows) != 0; + if (rowDisplayed) + { + autoSizeColumn |= (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows) != 0; + } + + bool autoSizeRow = (((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0; + if (autoSizeRow) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), !autoSizeColumn /*fixedWidth*/, true /*internalAutosizing*/); + } + if (autoSizeColumn) + { + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + if (autoSizeRow) + { + // Second round of row autosizing with 1 degree of freedom. + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + } + } + + internal void OnCellContentClickInternal(DataGridViewCellEventArgs e) + { + OnCellContentClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContentClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ContentClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentClickInternal(e); + } + else + { + dataGridViewCell.OnContentClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTENTCLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellContentDoubleClickInternal(DataGridViewCellEventArgs e) + { + OnCellContentDoubleClick(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContentDoubleClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.ContentDoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnContentDoubleClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTENTDOUBLECLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellContextMenuStripChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellContextMenuStripChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContextMenuStripChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal ContextMenuStrip OnCellContextMenuStripNeeded(int columnIndex, int rowIndex, ContextMenuStrip contextMenuStrip) + { + DataGridViewCellContextMenuStripNeededEventArgs dgvccmsne = new DataGridViewCellContextMenuStripNeededEventArgs(columnIndex, rowIndex, contextMenuStrip); + OnCellContextMenuStripNeeded(dgvccmsne); + return dgvccmsne.ContextMenuStrip; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellContextMenuStripNeeded(DataGridViewCellContextMenuStripNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellContextMenuStripNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLCONTEXTMENUSTRIPNEEDED] as DataGridViewCellContextMenuStripNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellDoubleClick(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.DoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnDoubleClickInternal(e); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLDOUBLECLICK] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellEndEdit(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLENDEDIT] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellEnter(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellEnter(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellEnter(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal void OnCellErrorTextChanged(DataGridViewCell dataGridViewCell) + { + Debug.Assert(dataGridViewCell.RowIndex >= -1); + Debug.Assert(dataGridViewCell.ColumnIndex >= -1); + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellErrorTextChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellErrorTextChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + UpdateCellErrorText(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLERRORTEXTCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnCellErrorTextNeeded(int columnIndex, int rowIndex, string errorText) + { + Debug.Assert(columnIndex >= 0); + Debug.Assert(rowIndex >= 0); + DataGridViewCellErrorTextNeededEventArgs dgvcetne = new DataGridViewCellErrorTextNeededEventArgs(columnIndex, rowIndex, errorText); + OnCellErrorTextNeeded(dgvcetne); + return dgvcetne.ErrorText; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellErrorTextNeeded(DataGridViewCellErrorTextNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellErrorTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLERRORTEXTNEEDED] as DataGridViewCellErrorTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal DataGridViewCellFormattingEventArgs OnCellFormatting(int columnIndex, int rowIndex, object val, Type formattedValueType, DataGridViewCellStyle cellStyle) + { + DataGridViewCellFormattingEventArgs dgvcfe = new DataGridViewCellFormattingEventArgs(columnIndex, + rowIndex, + val, + formattedValueType, + cellStyle); + OnCellFormatting(dgvcfe); + return dgvcfe; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellFormatting(DataGridViewCellFormattingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellFormattingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLFORMATTING] as DataGridViewCellFormattingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellLeave(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellLeave(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellLeave(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLLEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseClick(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseClickInternal(e); + } + else + { + dataGridViewCell.OnMouseClickInternal(e); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble] = false; + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseDoubleClickUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDoubleClickInternal(e); + } + else + { + dataGridViewCell.OnMouseDoubleClickInternal(e); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble] = true; + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseDown(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + + // Only left clicks for now + Keys nModifier = ModifierKeys; + bool isControlDown = (nModifier & Keys.Control) == Keys.Control && (nModifier & Keys.Alt) == 0; + bool isShiftDown = (nModifier & Keys.Shift) == Keys.Shift; + bool isAltDown = (nModifier & Keys.Alt) == Keys.Alt; + + Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y); + + HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && e.Button == MouseButtons.Left) + { + Debug.Assert(hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar); + Debug.Assert(this.ptMouseDownCell.X == hti.col); + Debug.Assert(this.ptMouseDownCell.Y == hti.row); + + switch (hti.typeInternal) + { + // Check for column/row (headers) resize + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + this.dataGridViewOper[DATAGRIDVIEWOPER_resizingOperationAboutToStart] = (e.Clicks == 1); + break; + } + } + } + + try + { + if (e.RowIndex >= 0 && dataGridViewCell.MouseDownUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDownInternal(e); + } + else + { + dataGridViewCell.OnMouseDownInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEDOWN] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && e.Button == MouseButtons.Left) + { + switch (hti.typeInternal) + { + // Check column resize + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + { + int columnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti.col : hti.adjacentCol; + Debug.Assert(this.Columns[columnIndex].Resizable == DataGridViewTriState.True); + if (e.Clicks == 1) + { + BeginMouseColumnResize(ptGridCoord.X, hti.mouseBarOffset, columnIndex); + } + break; + } + + // Check row resize + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + { + int rowIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti.row : hti.adjacentRow; + if (e.Clicks == 1) + { + BeginRowResize(ptGridCoord.Y, hti.mouseBarOffset, rowIndex); + } + break; + } + + // Check for column header mouse down + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + if (isAltDown && this.AllowUserToOrderColumns && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + BeginColumnRelocation(ptGridCoord.X, hti.col); + } + else + { + OnColumnHeaderMouseDown(hti, isShiftDown, isControlDown); + } + break; + } + + // Check for row header mouse down + case DataGridViewHitTestTypeInternal.RowHeader: + { + OnRowHeaderMouseDown(hti, isShiftDown, isControlDown); + break; + } + + // Check for cell mouse down + case DataGridViewHitTestTypeInternal.Cell: + { + OnCellMouseDown(hti, isShiftDown, isControlDown); + break; + } + + // Check for top/left header mouse down + case DataGridViewHitTestTypeInternal.TopLeftHeader: + { + OnTopLeftHeaderMouseDown(); + break; + } + + // Check for row headers resize + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + { + if (e.Clicks == 1) + { + BeginRowHeadersResize(ptGridCoord.X, hti.mouseBarOffset); + } + break; + } + + // Check for column headers resize + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + if (e.Clicks == 1) + { + BeginColumnHeadersResize(ptGridCoord.Y, hti.mouseBarOffset); + } + break; + } + } + // Make sure that there is a current cell after this mouse down event. + if (this.ptCurrentCell.X == -1) + { + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_resizingOperationAboutToStart] = false; + } + } + + private void OnCellMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.Cell); + // Only commit cell if the target cell is different from the current one. + if (this.ptCurrentCell.X >= 0 && + (this.ptCurrentCell.X != hti.col || this.ptCurrentCell.Y != hti.row)) + { + Point ptOriginalCurrentCell = this.ptCurrentCell; + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + this.ptCurrentCell.X != hti.col || this.ptCurrentCell.Y != hti.row /*forCurrentCellChange*/, + this.ptCurrentCell.Y != hti.row /*forCurrentRowChange*/)) + { + // Return silently if validating/commit/abort failed + return; + } + if (this.ptCurrentCell != ptOriginalCurrentCell) + { + // VSWhidbey 492203. Somehow the fact that the current cell was committed altered the current cell value. + // To avoid unintentional multi-selections, we act as if Shift and Control keys were up. + isShiftDown = isControlDown = false; + } + } + + if (hti.col >= this.Columns.Count) + { + DataGridViewColumn dataGridViewLastVisibleColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (this.ptCurrentCell.X == -1 && dataGridViewLastVisibleColumn != null) + { + // CurrentCell was reset because CommitEdit deleted column(s). + // Since the user clicked on a cell, we don't want to end up + // with no CurrentCell. We pick the last visible column in the grid. + hti.col = dataGridViewLastVisibleColumn.Index; + } + else + { + return; + } + } + if (hti.row >= this.Rows.Count) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (this.ptCurrentCell.X == -1 && lastVisibleRowIndex != -1) + { + // CurrentCell was reset because CommitEdit deleted row(s). + // Since the user clicked on a cell, we don't want to end up + // with no CurrentCell. We pick the last visible row in the + // grid which may be the 'new row'. + hti.row = lastVisibleRowIndex; + } + else + { + return; + } + } + + bool select = true; + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (isControlDown && + IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if ((!this.MultiSelect || !isControlDown) && !(this.MultiSelect && isShiftDown)) + { + Debug.Assert(this.MultiSelect || this.individualSelectedCells.Count <= 1); + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + if (this.MultiSelect) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + { + if (isControlDown && this.Columns[hti.col].Selected) + { + select = false; + } + if (select) + { + bool selectColumnRange = false; + this.trackColumn = hti.col; + this.trackColumnEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.X > -1 && + this.Columns[this.ptAnchorCell.X].Selected) + { + selectColumnRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.col) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = true; + } + if (selectColumnRange) + { + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, hti.col)) + { + SelectColumnRange(this.ptAnchorCell.X, hti.col, true); + } + else + { + SelectColumnRange(hti.col, this.ptAnchorCell.X, true); + } + } + else if (!this.selectedBandIndexes.Contains(hti.col)) + { + SetSelectedColumnCore(hti.col, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.col)); + SetSelectedColumnCore(hti.col, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (isControlDown && + (this.Columns[hti.col].Selected || IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row)) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(hti.col, hti.row, true); + } + else + { + // this.MultiSelect == true + if (!isControlDown && !isShiftDown) + { + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + { + if (isControlDown && + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)) + { + select = false; + } + if (select) + { + bool selectRowRange = false; + this.trackRow = hti.row; + this.trackRowEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.Y > -1 && (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0) + { + selectRowRange = true; + } + + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.row) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = true; + } + if (selectRowRange) + { + if (hti.row >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, hti.row, true); + } + else + { + SelectRowRange(hti.row, this.ptAnchorCell.Y, true); + } + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row) == + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(hti.row, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row)); + SetSelectedRowCore(hti.row, false); + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + Debug.Assert(success); + break; + } + + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (isControlDown && + (((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0) || + IsSharedCellSelected(this.Rows.SharedRow(hti.row).Cells[hti.col], hti.row)) && + (!isShiftDown || !this.MultiSelect)) + { + select = false; + } + if (select) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(hti.col, hti.row, true); + } + else + { + // this.MultiSelect == true + if (!isControlDown && !isShiftDown) + { + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + RemoveIndividuallySelectedCells(hti.col, hti.row); + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = true; + } + if (isShiftDown) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + } + else + { + SetSelectedCellCore(hti.col, hti.row, true); + } + } + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + else + { + SetSelectedCellCore(hti.col, hti.row, false); + } + } + bool success = SetCurrentCellAddressCore(hti.col, hti.row, !isShiftDown, false, true); + + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseEnter(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + this.ptMouseEnteredCell.X = e.ColumnIndex; + this.ptMouseEnteredCell.Y = e.RowIndex; + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseEnterUnsharesRowInternal(e.RowIndex)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseEnterInternal(e.RowIndex); + } + else + { + dataGridViewCell.OnMouseEnterInternal(e.RowIndex); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseLeave(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + this.ptMouseEnteredCell.X = -2; + this.ptMouseEnteredCell.Y = -2; + + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseLeaveUnsharesRowInternal(e.RowIndex)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseLeaveInternal(e.RowIndex); + } + else + { + dataGridViewCell.OnMouseLeaveInternal(e.RowIndex); + } + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSELEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseMove(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseMoveUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseMoveInternal(e); + } + else + { + dataGridViewCell.OnMouseMoveInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEMOVE] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] && + !this.IsMouseOperationActive() && + this.AllowUserToOrderColumns && + this.SelectionMode != DataGridViewSelectionMode.FullColumnSelect && + this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect && + e.Button == MouseButtons.Left && + this.ptMouseDownCell.Y == -1 && + this.ptMouseDownCell.X >= 0 && + this.ptMouseDownCell.X < this.Columns.Count) + { + Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y); + + HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y); + + Debug.Assert(hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar); + + switch (hti.typeInternal) + { + // Check for column header mouse down + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]); + if (Math.Abs(this.ptMouseDownGridCoord.X - ptGridCoord.X) >= DataGridView.DragSize.Width || + Math.Abs(this.ptMouseDownGridCoord.Y - ptGridCoord.Y) >= DataGridView.DragSize.Height) + { + BeginColumnRelocation(this.ptMouseDownGridCoord.X, this.ptMouseDownCell.X); + } + break; + } + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellMouseUp(DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex); + Debug.Assert(dataGridViewCell != null); + if (e.RowIndex >= 0 && dataGridViewCell.MouseUpUnsharesRowInternal(e)) + { + DataGridViewRow dataGridViewRow = this.Rows[e.RowIndex]; + GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseUpInternal(e); + } + else + { + dataGridViewCell.OnMouseUpInternal(e); + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLMOUSEUP] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected internal virtual void OnCellPainting(DataGridViewCellPaintingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellPaintingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLPAINTING] as DataGridViewCellPaintingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal DataGridViewCellParsingEventArgs OnCellParsing(int rowIndex, int columnIndex, object formattedValue, Type valueType, DataGridViewCellStyle cellStyle) + { + DataGridViewCellParsingEventArgs dgvcpe = new DataGridViewCellParsingEventArgs(rowIndex, columnIndex, + formattedValue, + valueType, + cellStyle); + OnCellParsing(dgvcpe); + return dgvcpe; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellParsing(DataGridViewCellParsingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellParsingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLPARSING] as DataGridViewCellParsingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnCellSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(this.MultiSelect); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if ((hti.col != this.ptCurrentCell.X || hti.row != this.ptCurrentCell.Y) && + !CommitEditForOperation(hti.col, hti.row, true)) + { + // Return silently if validating/commit/abort failed + return; + } + this.noSelectionChangeCount++; + try + { + if (this.ptAnchorCell.X == -1 || IsInnerCellOutOfBounds(hti.col, hti.row)) + { + return; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, hti.col, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, hti.row); + if (hti.col != this.ptCurrentCell.X || hti.row != this.ptCurrentCell.Y) + { + bool success = SetCurrentCellAddressCore(hti.col, hti.row, false, false, false); + Debug.Assert(success); + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnCellStateChanged(DataGridViewCellStateChangedEventArgs e) + { + // At this point we assume that only the Selected state has an influence on the rendering of the cell. + // If there is a scenario where another state has an effect, then the dev will have to invalidate the cell by hand. + DataGridViewCell dataGridViewCell = e.Cell; + if (e.StateChanged == DataGridViewElementStates.Selected) + { + Debug.Assert(dataGridViewCell.RowIndex >= 0); + if (this.inBulkPaintCount == 0) + { + InvalidateCellPrivate(dataGridViewCell); + } + } + + DataGridViewCellStateChangedEventHandler eh = Events[EVENT_DATAGRIDVIEWCELLSTATECHANGED] as DataGridViewCellStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + this.ptCurrentCell.X == dataGridViewCell.ColumnIndex && + this.ptCurrentCell.Y == dataGridViewCell.RowIndex && + dataGridViewCell.RowIndex > -1 && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if (!dataGridViewCell.ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current cell becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnCellStyleChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellStyleChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellStyleChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + OnCellCommonChange(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLSTYLECHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellStyleContentChanged(DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCellStyle.DataGridViewCellStylePropertyInternal property) + { + Debug.Assert(dataGridViewCellStyle != null); + switch (property) + { + case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Font: + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = false; + } + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.ColumnHeaders) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] = false; + } + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.RowHeaders) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] = false; + } + break; + + case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor: + if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = false; + } + break; + } + + DataGridViewCellStyleContentChangedEventArgs dgvcscce = new DataGridViewCellStyleContentChangedEventArgs(dataGridViewCellStyle, + property != DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Color && + property != DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor /*changeAffectsPreferredSize*/); + OnCellStyleContentChanged(dgvcscce); + } + + /// + protected virtual void OnCellStyleContentChanged(DataGridViewCellStyleContentChangedEventArgs e) + { + // We assume that when a color changes, it has no effect on the autosize of elements + bool repositionEditingControl = false; + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Cell) == DataGridViewCellStyleScopes.Cell && (e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == 0) + { + // Same processing as in OnDefaultCellStyleChanged + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + OnGlobalAutoSize(); + } + else + { + Invalidate(); + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Column) == DataGridViewCellStyleScopes.Column) + { + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + OnColumnsGlobalAutoSize(); + } + else + { + InvalidateData(); + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Row) == DataGridViewCellStyleScopes.Row && (e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == 0 && (e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == 0) + { + // Same processing as in OnRowsDefaultCellStyleChanged + InvalidateData(); + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = true; + // Autosize rows if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + // Auto size columms also if needed + // Impossible to figure out if DisplayedRows filter should be added or not. Adding it to be on the conservative side. + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | + DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + true /*fixedHeight*/); + // Second round of rows autosizing + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == DataGridViewCellStyleScopes.DataGridView) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnDefaultCellStyleChanged will reposition the editing control. + } + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.ColumnHeaders) == DataGridViewCellStyleScopes.ColumnHeaders) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnColumnHeadersDefaultCellStyleChanged will reposition the editing control. + } + OnColumnHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.RowHeaders) == DataGridViewCellStyleScopes.RowHeaders) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnRowHeadersDefaultCellStyleChanged will reposition the editing control. + } + OnRowHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == DataGridViewCellStyleScopes.Rows) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnRowsDefaultCellStyleChanged will reposition the editing control. + } + OnRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if ((e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == DataGridViewCellStyleScopes.AlternatingRows) + { + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize; + if (e.ChangeAffectsPreferredSize) + { + repositionEditingControl = false; + // OnAlternatingRowsDefaultCellStyleChanged will reposition the editing control. + } + OnAlternatingRowsDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (repositionEditingControl && this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + + DataGridViewCellStyleContentChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLSTYLECONTENTCHANGED] as DataGridViewCellStyleContentChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellToolTipTextChanged(DataGridViewCell dataGridViewCell) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(dataGridViewCell); + OnCellToolTipTextChanged(dgvce); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellToolTipTextChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTCHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnCellToolTipTextNeeded(int columnIndex, int rowIndex, string toolTipText) + { + DataGridViewCellToolTipTextNeededEventArgs dgvctttne = new DataGridViewCellToolTipTextNeededEventArgs(columnIndex, rowIndex, toolTipText); + OnCellToolTipTextNeeded(dgvctttne); + return dgvctttne.ToolTipText; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellToolTipTextNeeded(DataGridViewCellToolTipTextNeededEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellToolTipTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLTOOLTIPTEXTNEEDED] as DataGridViewCellToolTipTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellValidated(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + OnCellValidated(new DataGridViewCellEventArgs(columnIndex, rowIndex)); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValidated(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALIDATED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal bool OnCellValidating(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex, DataGridViewDataErrorContexts context) + { + DataGridViewCell currentCell = (dataGridViewCell == null) ? this.CurrentCellInternal : dataGridViewCell; + DataGridViewCellStyle dataGridViewCellStyle = currentCell.GetInheritedStyle(null, rowIndex, false); + object val = currentCell.GetValueInternal(rowIndex); + object editedFormattedValue = currentCell.GetEditedFormattedValue(val, rowIndex, ref dataGridViewCellStyle, context); + DataGridViewCellValidatingEventArgs dgvcfvce = new DataGridViewCellValidatingEventArgs(columnIndex, rowIndex, editedFormattedValue); + OnCellValidating(dgvcfvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + return dgvcfvce.Cancel; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValidating(DataGridViewCellValidatingEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + try + { + this.noDimensionChangeCount++; + this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating] = true; + + DataGridViewCellValidatingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALIDATING] as DataGridViewCellValidatingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + this.dataGridViewOper[DATAGRIDVIEWOPER_inCellValidating] = false; + } + } + + internal void OnCellValueChangedInternal(DataGridViewCellEventArgs e) + { + // For now, same effect as if the cell style had changed. + OnCellValueChanged(e); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValueChanged(DataGridViewCellEventArgs e) + { + if (e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + OnCellCommonChange(e.ColumnIndex, e.RowIndex); + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUECHANGED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal object OnCellValueNeeded(int columnIndex, int rowIndex) + { + DataGridViewCellValueEventArgs dgvcve = this.CellValueEventArgs; + dgvcve.SetProperties(columnIndex, rowIndex, null); + OnCellValueNeeded(dgvcve); + return dgvcve.Value; + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValueNeeded(DataGridViewCellValueEventArgs e) + { + if (e.ColumnIndex < 0 || e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex < 0 || e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } +//#if DEBUG + // Some customer scenarios may result in accessing cell values while this.dataStoreAccessAllowed is false. This is bad practice, + // but since we're late in Whidbey, throwing an exception would be destabilizing our internal customers. + // Debug.Assert(this.dataStoreAccessAllowed); +//#endif + DataGridViewCellValueEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUENEEDED] as DataGridViewCellValueEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnCellValuePushed(int columnIndex, int rowIndex, object value) + { + DataGridViewCellValueEventArgs dgvcve = this.CellValueEventArgs; + dgvcve.SetProperties(columnIndex, rowIndex, value); + OnCellValuePushed(dgvcve); + } + + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] // e.ColumnIndex / e.RowIndex is more precise than just e + protected virtual void OnCellValuePushed(DataGridViewCellValueEventArgs e) + { + if (e.ColumnIndex < 0 || e.ColumnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("e.ColumnIndex"); + } + if (e.RowIndex < 0 || e.RowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("e.RowIndex"); + } + DataGridViewCellValueEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCELLVALUEPUSHED] as DataGridViewCellValueEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnClearedRows() + { + // Raise the RowStateChanged(Displayed->false) events + foreach (DataGridViewRow dataGridViewRow in this.lstRows) + { + if (dataGridViewRow.Displayed) + { + dataGridViewRow.DisplayedInternal = false; + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, DataGridViewElementStates.Displayed); + OnRowStateChanged(-1 /*rowIndex*/, dgvrsce); + } + } + this.lstRows.Clear(); + } + + internal void OnClearingColumns() + { + this.CurrentCell = null; + + // Rows need to be cleared first. There cannot be rows without also having columns. + this.Rows.ClearInternal(false /*recreateNewRow*/); + + // Reset sort related variables. + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + + // selectedBandIndexes, individualSelectedCells & individualReadOnlyCells cleared in OnClearingRows. + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + internal void OnClearingRows() + { + // Build a list of displayed rows in order to be able to raise their RowStateChanged(Displayed->false) events later on + this.lstRows.Clear(); + int numDisplayedRows = this.displayedBandsInfo.NumDisplayedFrozenRows + this.displayedBandsInfo.NumDisplayedScrollingRows; + if (numDisplayedRows > 0) + { + this.lstRows.Capacity = numDisplayedRows; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Displayed); + while (numDisplayedRows > 0 && rowIndex != -1) + { + this.lstRows.Add(this.Rows[rowIndex]); + numDisplayedRows--; + if (numDisplayedRows > 0) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Displayed); + } + } + } + + this.CurrentCell = null; + + this.newRowIndex = -1; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = this.selectedBandIndexes.Count > 0 || + this.individualSelectedCells.Count > 0; + this.selectedBandIndexes.Clear(); + if (this.selectedBandSnapshotIndexes != null) + { + this.selectedBandSnapshotIndexes.Clear(); + } + this.individualSelectedCells.Clear(); + this.individualReadOnlyCells.Clear(); + } + + /// + protected virtual void OnColumnAdded(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNADDED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnCollectionChanged_PreNotification(CollectionChangeEventArgs ccea) + { + // we need to map columns w/ DataPropertyName to bound columns + if (this.DataSource != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + if (ccea.Action == CollectionChangeAction.Add) + { + DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)ccea.Element; + if (dataGridViewColumn.DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(dataGridViewColumn); + } + } + else if (ccea.Action == CollectionChangeAction.Refresh) + { + for (int i = 0; i < this.Columns.Count; i++) + { + if (this.Columns[i].DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(this.Columns[i]); + } + } + } + } + + ResetUIState(false /*useRowShortcut*/, false /*computeVisibleRows*/); + } + + internal void OnColumnCollectionChanged_PostNotification(DataGridViewColumn dataGridViewColumn) + { + if (this.Columns.Count != 0 && this.Rows.Count == 0) + { + if (this.DataSource != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + // this will create the 'add new row' when AllowUserToAddRowsInternal == true + RefreshRows(true /*scrollIntoView*/); + } + else if (this.AllowUserToAddRowsInternal) + { + AddNewRow(false); + } + } + if (this.AutoSize && (dataGridViewColumn == null || dataGridViewColumn.Visible)) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Columns); + } + } + + internal void OnColumnCommonChange(int columnIndex) + { + OnColumnGlobalAutoSize(columnIndex); + } + + /// + protected virtual void OnColumnContextMenuStripChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNCONTEXTMENUSTRIPCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDataPropertyNameChanged(DataGridViewColumn dataGridViewColumn) + { + OnColumnDataPropertyNameChanged(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnDataPropertyNameChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + // map the dataGridView column to some data field + if (this.DataSource != null && e.Column.DataPropertyName.Length != 0 && !this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns]) + { + MapDataGridViewColumnToDataBoundField(e.Column); + } + else if (this.DataSource != null && e.Column.DataPropertyName.Length == 0) + { + if (e.Column.IsDataBound) + { + e.Column.IsDataBoundInternal = false; + e.Column.BoundColumnIndex = -1; + e.Column.BoundColumnConverter = null; + InvalidateColumnInternal(e.Column.Index); + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDATAPROPERTYNAMECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnDefaultCellStyleChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + OnColumnGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDEFAULTCELLSTYLECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDisplayIndexChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnDisplayIndexChanged(dgvce); + } + + internal void OnColumnDisplayIndexChanging(DataGridViewColumn dataGridViewColumn, int newDisplayIndex) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex); + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]) + { + // We are within columns display indexes adjustments. We do not allow changing display indexes while adjusting them. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments)); + } + + // Throws an exception if the requested move is illegal + CorrectColumnFrozenStatesForMove(dataGridViewColumn, newDisplayIndex); + + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = true; + + // Move is legal - let's adjust the affected display indexes. + if (newDisplayIndex < dataGridViewColumn.DisplayIndex) + { + // DisplayIndex decreases. All columns with newDisplayIndex <= DisplayIndex < dataGridViewColumn.DisplayIndex + // get their DisplayIndex incremented. + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (newDisplayIndex <= dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex < dataGridViewColumn.DisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + } + else + { + // DisplayIndex increases. All columns with dataGridViewColumn.DisplayIndex < DisplayIndex <= newDisplayIndex + // get their DisplayIndex incremented. + foreach (DataGridViewColumn dataGridViewColumnTmp in this.Columns) + { + if (dataGridViewColumn.DisplayIndex < dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex <= newDisplayIndex) + { + dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1; + dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on + } + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments] = false; + } + + // Note that displayIndex of moved column is updated by caller. + } + + /// + protected virtual void OnColumnDisplayIndexChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); +#if DEBUG + Debug.Assert(this.Columns.VerifyColumnDisplayIndexes()); +#endif + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDISPLAYINDEXCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnDisplayIndexChanged_PreNotification() + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inDisplayIndexAdjustments]); + + // column.DisplayIndex changed - this may require a complete re-layout of the control + this.Columns.InvalidateCachedColumnsOrder(); + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + if (this.editingControl != null) + { + PositionEditingControl(true, true, false); + } + Invalidate(Rectangle.Union(this.layout.ColumnHeaders, this.layout.Data)); + } + + internal void OnColumnDisplayIndexChanged_PostNotification() + { + // Notifications for adjusted display indexes. + FlushDisplayIndexChanged(true /*raiseEvent*/); + } + + /// + protected virtual void OnColumnDividerDoubleClick(DataGridViewColumnDividerDoubleClickEventArgs e) + { + DataGridViewColumnDividerDoubleClickEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDIVIDERDOUBLECLICK] as DataGridViewColumnDividerDoubleClickEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!e.Handled && e.Button == MouseButtons.Left && e.ColumnIndex < this.Columns.Count) + { + if (e.ColumnIndex == -1) + { + AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + else + { + DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = this.Columns[e.ColumnIndex].InheritedAutoSizeMode; + if (inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + AutoResizeColumnInternal(e.ColumnIndex, DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows, true /*fixedHeight*/); + } + else + { + AutoResizeColumnInternal(e.ColumnIndex, (DataGridViewAutoSizeColumnCriteriaInternal)inheritedAutoSizeMode, true /*fixedHeight*/); + } + } + } + } + + /// + protected virtual void OnColumnDividerWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + OnColumnGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNDIVIDERWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnFillWeightChanged(DataGridViewColumn dataGridViewColumn) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // UsedFillWeight properties need to be re-evaluated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + // Adjust filling columns based on new weight of this column + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + } + + internal void OnColumnFillWeightChanging(DataGridViewColumn dataGridViewColumn, float fillWeight) + { + if (this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + // Make sure the sum of the column weights does not exceed ushort.MaxValue + float weightSum = this.Columns.GetColumnsFillWeight(DataGridViewElementStates.None) - dataGridViewColumn.FillWeight + fillWeight; + if (weightSum > (float)ushort.MaxValue) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_WeightSumCannotExceedLongMaxValue, (ushort.MaxValue).ToString(CultureInfo.CurrentCulture))); + } + } + + private void OnColumnGlobalAutoSize(int columnIndex) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (!this.Columns[columnIndex].Visible) + { + return; + } + + InvalidateColumnInternal(columnIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + if (autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill) + { + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight); + } + + // Autosize rows and column headers if needed + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + if (!fixedHeight && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None && + autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill) + { + // Second round of column autosizing with 1 degree of freedom + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + } + + /// + protected virtual void OnColumnHeaderCellChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + OnColumnHeaderGlobalAutoSize(e.Column.Index); + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERCELLCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeaderGlobalAutoSize(int columnIndex) + { + if (!this.ColumnHeadersVisible) + { + return; + } + + InvalidateCellPrivate(columnIndex, -1); + + if (this.noAutoSizeCount > 0) + { + return; + } + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)this.Columns[columnIndex].InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0; + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + + if (!fixedColumnWidth) + { + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None); + Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill); + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing with 1 degree of freedom + AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing with 1 degree of freedom + AutoResizeColumnHeadersHeight(columnIndex, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + /// + protected virtual void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e) + { + if (e.Button == MouseButtons.Left && + this.SelectionMode != DataGridViewSelectionMode.FullColumnSelect && + this.SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect) + { + DataGridViewColumn dataGridViewColumn = this.Columns[e.ColumnIndex]; + + if (CanSort(dataGridViewColumn)) + { + ListSortDirection direction = ListSortDirection.Ascending; + + if (this.sortedColumn == dataGridViewColumn) + { + Debug.Assert(this.sortOrder != SortOrder.None); + if (this.sortOrder == SortOrder.Ascending) + { + direction = ListSortDirection.Descending; + } + } + + if ((this.DataSource == null) || + (this.DataSource != null && + (this.dataConnection.List is IBindingList) && + ((IBindingList) this.dataConnection.List).SupportsSorting && + dataGridViewColumn.IsDataBound)) + { + Sort(dataGridViewColumn, direction); + } + } + } + + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.ColumnHeader); + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + break; + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + bool select = true; + if (isControlDown && this.Columns[hti.col].Selected) + { + select = false; + } + if (select) + { + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (rowIndex > -1 && hti.col != this.ptCurrentCell.X) + { + // Make sure we will be able to scroll into view + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + rowIndex != this.ptCurrentCell.Y /*fireRowLeave*/, + rowIndex != this.ptCurrentCell.Y /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + true /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + return; + } + if (rowIndex != oldCurrentCellY && oldCurrentCellY != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + return; + } + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + if (IsColumnOutOfBounds(hti.col)) + { + return; + } + + bool selectColumnRange = false; + this.trackColumn = hti.col; + this.trackColumnEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.X > -1 && + this.Columns[this.ptAnchorCell.X].Selected) + { + selectColumnRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.col) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + RemoveIndividuallySelectedCells(); + } + else + { + Debug.Assert(this.individualSelectedCells.Count == 0); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = true; + } + if (selectColumnRange) + { + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, hti.col)) + { + SelectColumnRange(this.ptAnchorCell.X, hti.col, true); + } + else + { + SelectColumnRange(hti.col, this.ptAnchorCell.X, true); + } + } + else if (!this.selectedBandIndexes.Contains(hti.col)) + { + SetSelectedColumnCore(hti.col, true); + } + // set current cell to the top most visible cell in the column + if (rowIndex != -1) + { + if (hti.col != this.ptCurrentCell.X) + { + if (IsInnerCellOutOfBounds(hti.col, rowIndex)) + { + return; + } + bool success = ScrollIntoView(hti.col, rowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(hti.col, rowIndex)) + { + return; + } + success = SetCurrentCellAddressCore(hti.col, rowIndex, !isShiftDown, false, true); + Debug.Assert(success); + } + else if (-1 != this.ptCurrentCell.X) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false /*throughMouseClick*/); + Debug.Assert(success); + } + } + else + { + Debug.Assert(this.CurrentCellAddress == new Point(-1, -1)); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.col)); + SetSelectedColumnCore(hti.col, false); + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnColumnHeadersBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeadersDefaultCellStyleChanged(EventArgs e) + { + if (this.ColumnHeadersVisible) + { + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce == null || dgvcsce.ChangeAffectsPreferredSize) + { + OnColumnHeadersGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnHeadersGlobalAutoSize() + { + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, false /*fixedColumnsWidth*/); + } + + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, false /*fixedRowsHeight*/); + } + + // Autosize columns + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, false /*fixedHeight*/); + + if (!fixedRowHeadersWidth || columnAutoSized) + { + // Autosize rows + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (!fixedColumnHeadersHeight) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + + if (!fixedRowHeadersWidth) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + // Second round of columns autosizing + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, true /*fixedHeight*/); + } + + /// + protected virtual void OnColumnHeadersHeightChanged(EventArgs e) + { + if (this.editingControl != null) + { + PositionEditingControl(true, false, false); + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + OnColumnHeadersGlobalAutoSize(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnHeadersHeightSizeModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + if (!e.PreviousModeAutoSized) + { + // Save current column headers height for later reuse + this.cachedColumnHeadersHeight = this.ColumnHeadersHeight; + } + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + else if (e.PreviousModeAutoSized) + { + this.ColumnHeadersHeight = this.cachedColumnHeadersHeight; + } + + DataGridViewAutoSizeModeEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNHEADERSHEIGHTSIZEMODECHANGED] as DataGridViewAutoSizeModeEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnHidden(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + if (dataGridViewColumn.Displayed) + { + dataGridViewColumn.DisplayedInternal = false; + DataGridViewColumnStateChangedEventArgs dgvrsce = new DataGridViewColumnStateChangedEventArgs(dataGridViewColumn, DataGridViewElementStates.Displayed); + OnColumnStateChanged(dgvrsce); + } + } + + internal void OnColumnMinimumWidthChanging(DataGridViewColumn dataGridViewColumn, int minimumWidth) + { + if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && dataGridViewColumn.Width < minimumWidth) + { + // Force the filled column's width to be minimumWidth + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + dataGridViewColumn.DesiredMinimumWidth = minimumWidth; + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + finally + { + dataGridViewColumn.DesiredMinimumWidth = 0; + } + Debug.Assert(dataGridViewColumn.Width == minimumWidth); + } + } + + /// + protected virtual void OnColumnMinimumWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + if (e.Column.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // Column's width may adjust smaller + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNMINIMUMWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnNameChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnNameChanged(dgvce); + } + + /// + protected virtual void OnColumnNameChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + // Check if the column name is used as is in the column header + DataGridViewColumn dataGridViewColumn = e.Column; + + if (dataGridViewColumn.HasHeaderCell && dataGridViewColumn.HeaderCell.Value is string && + String.Compare((string)dataGridViewColumn.HeaderCell.Value, dataGridViewColumn.Name, false, CultureInfo.InvariantCulture) == 0) + { + InvalidateCellPrivate(dataGridViewColumn.Index, -1); + + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !this.ColumnHeadersVisible; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + if (!fixedColumnWidth) + { + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNNAMECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnColumnRemoved(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.DataGridView == null); + OnColumnRemoved(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnRemoved(DataGridViewColumnEventArgs e) + { + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNREMOVED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnColumnSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(hti.col >= 0); + Debug.Assert(this.MultiSelect); + + if (this.ptCurrentCell.X != -1 && + hti.col != this.ptCurrentCell.X && + !CommitEditForOperation(hti.col, this.ptCurrentCell.Y, true)) + { + // Return silently if validating/commit/abort failed + return; + } + if (IsColumnOutOfBounds(hti.col)) + { + return; + } + + this.noSelectionChangeCount++; + try + { + if (this.trackColumnEdge >= 0 && (this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) || this.trackColumnEdge == this.trackColumn) && this.Columns.DisplayInOrder(this.trackColumnEdge, hti.col)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) && this.Columns.DisplayInOrder(hti.col, this.trackColumnEdge) && (this.Columns.DisplayInOrder(this.trackColumn, hti.col) || hti.col == this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[hti.col], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, this.trackColumnEdge, false); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge == -1 && this.Columns.DisplayInOrder(this.trackColumn, hti.col)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && (this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn) || this.trackColumnEdge == this.trackColumn) && this.Columns.DisplayInOrder(hti.col, this.trackColumnEdge)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn) && this.Columns.DisplayInOrder(this.trackColumnEdge, hti.col) && (this.Columns.DisplayInOrder(hti.col, this.trackColumn) || hti.col == this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[hti.col], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(this.trackColumnEdge, dataGridViewColumn.Index, false); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge == -1 && this.Columns.DisplayInOrder(hti.col, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, this.trackColumnEdge) && this.Columns.DisplayInOrder(hti.col, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, this.trackColumnEdge, false); + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(hti.col, dataGridViewColumn.Index, true); + this.trackColumnEdge = hti.col; + } + else if (this.trackColumnEdge >= 0 && this.Columns.DisplayInOrder(this.trackColumn, hti.col) && this.Columns.DisplayInOrder(this.trackColumnEdge, this.trackColumn)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(this.trackColumnEdge, dataGridViewColumn.Index, false); + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectColumnRange(dataGridViewColumn.Index, hti.col, true); + this.trackColumnEdge = hti.col; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X != -1 && hti.col != this.ptCurrentCell.X) + { + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(hti.col)) + { + return; + } + bool success = SetCurrentCellAddressCore(hti.col, + this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + + private void OnColumnsGlobalAutoSize() + { + InvalidateData(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + // Auto-size columms if needed + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, fixedHeight); + // Autosize rows if needed + if (!fixedHeight) + { + if (columnAutoSized) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + // Second round of columns autosizing + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, true /*fixedHeight*/); + } + } + + internal void OnColumnSortModeChanged(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn != null); + DataGridViewColumnEventArgs dgvce = new DataGridViewColumnEventArgs(dataGridViewColumn); + OnColumnSortModeChanged(dgvce); + } + + /// + protected virtual void OnColumnSortModeChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumn dataGridViewColumn = e.Column; + + if (dataGridViewColumn.HasHeaderCell) + { + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.NotSortable || + (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Programmatic && this.SortedColumn == dataGridViewColumn)) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder.None; + // This call will trigger OnSortGlyphDirectionChanged which in turn does + // this.sortedColumn = null; and InvalidateCellPrivate(e.Column.Index, -1); + } + // Potential resizing of the column headers and/or affected column. + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode; + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header; + bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !this.ColumnHeadersVisible; + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, fixedColumnWidth); + } + if (!fixedColumnWidth) + { + bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0; + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight); + if (!fixedHeight) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, true /*fixedHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNSORTMODECHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnStateChanged(DataGridViewColumnStateChangedEventArgs e) + { + // column.Frozen | .Visible changed - this may require a complete re-layout of the control + DataGridViewColumn dataGridViewColumn = e.Column; + switch (e.StateChanged) + { + // At this point we assume that only the Selected state has an influence on the rendering of the column. + // If there is a customer scenario where another state has an influence, the dev must invalidate the column by hand. + // case DataGridViewElementStates.ReadOnly: + // case DataGridViewElementStates.Resizable: + + case DataGridViewElementStates.Selected: + if (dataGridViewColumn.Visible && this.inBulkPaintCount == 0) + { + InvalidateColumnInternal(dataGridViewColumn.Index); + } + break; + + case DataGridViewElementStates.Frozen: + if (dataGridViewColumn.Visible) + { + if (dataGridViewColumn.Frozen) + { + // visible column became frozen + if (this.horizontalOffset >= dataGridViewColumn.Thickness) + { + Debug.Assert(this.Columns.DisplayInOrder(dataGridViewColumn.Index, this.displayedBandsInfo.FirstDisplayedScrollingCol)); + this.horizontalOffset -= dataGridViewColumn.Thickness; + } + else + { + this.horizontalOffset = this.negOffset = 0; + } + } + else + { + // column was unfrozen - make it the first visible scrolling column if there is room + this.horizontalOffset = this.negOffset = 0; + } + if (this.horizScrollBar.Enabled) + { + this.horizScrollBar.Value = this.horizontalOffset; + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + } + break; + + case DataGridViewElementStates.Visible: + if (!dataGridViewColumn.Visible && dataGridViewColumn.Displayed) + { + // Displayed column becomes invisible. Turns off the Displayed state. + dataGridViewColumn.DisplayedInternal = false; + } + + // UsedFillWeight values need to be updated + this.dataGridViewState2[DATAGRIDVIEWSTATE2_usedFillWeightsDirty] = true; + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + + bool autoSizeRows = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0 || + ((((DataGridViewAutoSizeRowsModeInternal) this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && + this.RowHeadersVisible); + bool autoSizeColumn = false; + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode; + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill) + { + // Column autosizes + int width = dataGridViewColumn.ThicknessInternal; + if (dataGridViewColumn.Visible) + { + // Cache column's width before potential autosizing occurs + dataGridViewColumn.CachedThickness = width; + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, !autoSizeRows /*fixedHeight*/); + autoSizeColumn = true; + } + else if (width != dataGridViewColumn.CachedThickness) + { + // Columns that are made invisible in the collection take their non-autosized width + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + } + + if (autoSizeRows) + { + if (dataGridViewColumn.Visible) + { + AdjustExpandingRows(dataGridViewColumn.Index, true /*fixedWidth*/); + } + else + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (autoSizeColumn) + { + // Second round of column autosizing + AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, true /*fixedHeight*/); + } + } + else + { + Invalidate(); + } + break; + } + + DataGridViewColumnStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNSTATECHANGED] as DataGridViewColumnStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + dataGridViewColumn.Index == this.ptCurrentCell.X && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if (!dataGridViewColumn.ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.ReadOnly) == 0 && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current column becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnColumnToolTipTextChanged(DataGridViewColumn dataGridViewColumn) + { + OnColumnToolTipTextChanged(new DataGridViewColumnEventArgs(dataGridViewColumn)); + } + + /// + protected virtual void OnColumnToolTipTextChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNTOOLTIPTEXTCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnColumnWidthChanged(DataGridViewColumnEventArgs e) + { + if (e.Column.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + this.Columns.InvalidateCachedColumnsWidths(); + + // don't do any layout logic if the handle was not created already + if (e.Column.Visible && this.IsHandleCreated) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + + Rectangle rightArea = this.layout.Data; + if (this.layout.ColumnHeadersVisible) + { + rightArea = Rectangle.Union(rightArea, this.layout.ColumnHeaders); + } + else if (this.SingleHorizontalBorderAdded) + { + rightArea.Y--; + rightArea.Height++; + } + if (rightArea.Width > 0 && rightArea.Height > 0) + { + int leftEdge = GetColumnXFromIndex(e.Column.Index); + if (this.RightToLeftInternal) + { + rightArea.Width -= rightArea.Right - leftEdge; + } + else + { + rightArea.Width -= leftEdge - rightArea.X; + rightArea.X = leftEdge; + } + if (rightArea.Width > 0 && rightArea.Height > 0) + { + Invalidate(rightArea); + } + } + + if (this.editingControl != null) + { + PositionEditingControl(this.ptCurrentCell.X != e.Column.Index, true, false); + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Columns); + } + } + + DataGridViewColumnEventHandler eh = this.Events[EVENT_DATAGRIDVIEWCOLUMNWIDTHCHANGED] as DataGridViewColumnEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!this.InAdjustFillingColumns) + { + // Autosize rows and column headers if needed + OnColumnGlobalAutoSize(e.Column.Index); + } + } + + internal void OnCommonCellContentClick(int columnIndex, int rowIndex, bool doubleClick) + { + if (this.ptMouseDownCell.X == -2 || + (this.dataGridViewState2[DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds] && + this.ptMouseDownCell.X == columnIndex && this.ptMouseDownCell.Y == rowIndex && + (this.ptMouseDownCell.X == -1 || this.ptMouseDownCell.Y == -1 || + (columnIndex == this.ptCurrentCell.X && rowIndex == this.ptCurrentCell.Y)))) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + if (doubleClick) + { + OnCellContentDoubleClick(dgvce); + } + else + { + OnCellContentClick(dgvce); + } + } + } + + /// + protected virtual void OnCurrentCellChanged(EventArgs e) + { + VerifyImeRestrictedModeChanged(); + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCURRENTCELLCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnCurrentCellDirtyStateChanged(EventArgs e) + { + if (this.RowHeadersVisible && this.ShowEditingIcon) + { + // Force the pencil to appear in the row header + Debug.Assert(this.ptCurrentCell.Y >= 0); + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + if (this.IsCurrentCellDirty && this.newRowIndex == this.ptCurrentCell.Y) + { + Debug.Assert(this.newRowIndex != -1); + Debug.Assert(this.AllowUserToAddRowsInternal); + // First time the 'new' row gets edited. + // It becomes a regular row and a new 'new' row is appened. + this.newRowIndex = -1; + AddNewRow(true /*createdByEditing*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWCURRENTCELLDIRTYSTATECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnCursorChanged(EventArgs e) + { + base.OnCursorChanged(e); + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_ignoreCursorChange]) + { + this.oldCursor = this.Cursor; + } + } + + internal void OnDataBindingComplete(ListChangedType listChangedType) + { + OnDataBindingComplete(new DataGridViewBindingCompleteEventArgs(listChangedType)); + } + + /// + protected virtual void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e) + { + DataGridViewBindingCompleteEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATABINDINGCOMPLETE] as DataGridViewBindingCompleteEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions") // Not using options when RightToLeft == false + ] + protected virtual void OnDataError(bool displayErrorDialogIfNoHandler, DataGridViewDataErrorEventArgs e) + { + DataGridViewDataErrorEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATAERROR] as DataGridViewDataErrorEventHandler; + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + if (eh == null) + { + if (displayErrorDialogIfNoHandler) + { + string errorText; + if (e.Exception == null) + { + errorText = SR.GetString(SR.DataGridView_ErrorMessageText_NoException); + } + else + { + errorText = SR.GetString(SR.DataGridView_ErrorMessageText_WithException, e.Exception); + } + if (this.RightToLeftInternal) + { + MessageBox.Show(errorText, SR.GetString(SR.DataGridView_ErrorMessageCaption), MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading); + } + else + { + MessageBox.Show(errorText, SR.GetString(SR.DataGridView_ErrorMessageCaption), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + else + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + } + + internal void OnDataErrorInternal(DataGridViewDataErrorEventArgs e) + { + OnDataError(!this.DesignMode /*displayErrorDialogIfNoHandler*/, e); + } + + internal void OnDataGridViewElementStateChanged(DataGridViewElement element, int index, DataGridViewElementStates elementState) + { + DataGridViewColumn dataGridViewColumn = element as DataGridViewColumn; + if (dataGridViewColumn != null) + { + DataGridViewColumnStateChangedEventArgs dgvcsce = new DataGridViewColumnStateChangedEventArgs(dataGridViewColumn, elementState); + + OnColumnStateChanged(dgvcsce); + } + else + { + DataGridViewRow dataGridViewRow = element as DataGridViewRow; + if (dataGridViewRow != null) + { + DataGridViewRowStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSTATECHANGED] as DataGridViewRowStateChangedEventHandler; + + if (eh != null && dataGridViewRow.DataGridView != null && dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[index]; + } + + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, elementState); + + OnRowStateChanged(dataGridViewRow.Index == -1 ? index : dataGridViewRow.Index, dgvrsce); + } + else + { + DataGridViewCell dataGridViewCell = element as DataGridViewCell; + if (dataGridViewCell != null) + { + DataGridViewCellStateChangedEventArgs dgvcsce = new DataGridViewCellStateChangedEventArgs(dataGridViewCell, elementState); + + OnCellStateChanged(dgvcsce); + } + } + } + + if ((elementState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected) + { + if (this.noSelectionChangeCount > 0) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = true; + } + else + { + OnSelectionChanged(EventArgs.Empty); + } + } + } + + internal void OnDataGridViewElementStateChanging(DataGridViewElement element, int index, DataGridViewElementStates elementState) + { + DataGridViewColumn dataGridViewColumn = element as DataGridViewColumn; + if (dataGridViewColumn != null) + { + // column.Frozen | .Visible | .ReadOnly changing + switch (elementState) + { + case DataGridViewElementStates.Frozen: + case DataGridViewElementStates.Visible: + if (elementState == DataGridViewElementStates.Visible) + { + if (!dataGridViewColumn.Visible && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader && + !this.ColumnHeadersVisible) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMakeAutoSizedColumnVisible)); + } + else if (!dataGridViewColumn.Visible && + dataGridViewColumn.Frozen && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // alternative: throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotMakeAutoFillColumnVisible)); + // DataGridView_CannotMakeAutoFillColumnVisible=The column cannot be made visible because its autosizing mode is Fill and it is frozen. + // Removing the Fill auto size mode when frozen column becomes visible (instead of throwing an exception) + dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + } + else if (dataGridViewColumn.Visible && this.ptCurrentCell.X == dataGridViewColumn.Index) + { + // Column of the current cell is made invisible. Trying to reset the current cell. May throw an exception. + ResetCurrentCell(); + // Microsoft: Should the current cell be set to some cell after the operation? + } + } + if (elementState == DataGridViewElementStates.Frozen && + !dataGridViewColumn.Frozen && + dataGridViewColumn.Visible && + dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) + { + // Removing the Fill auto size mode when visible column becomes frozen (instead of throwing an exception) + dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + } + CorrectColumnFrozenStates(dataGridViewColumn, elementState == DataGridViewElementStates.Frozen); + break; + + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.X == dataGridViewColumn.Index && + this.IsCurrentCellInEditMode && + !dataGridViewColumn.ReadOnly && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + Debug.Assert(!this.ReadOnly); + // Column becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + DataGridViewRow dataGridViewRow = element as DataGridViewRow; + if (dataGridViewRow != null) + { + // row.Frozen | .Visible | .ReadOnly changing + int rowIndex = ((dataGridViewRow.Index > -1) ? dataGridViewRow.Index : index); + switch (elementState) + { + case DataGridViewElementStates.Frozen: + case DataGridViewElementStates.Visible: + if (elementState == DataGridViewElementStates.Visible && this.ptCurrentCell.Y == rowIndex) + { + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + // Row of the current cell is made invisible. + if (this.DataSource != null) + { + Debug.Assert(this.dataConnection != null); + Debug.Assert(this.dataConnection.CurrencyManager != null); + Debug.Assert(this.dataConnection.CurrencyManager.Position == this.ptCurrentCell.Y); + // the row associated with the currency manager's position cannot be made invisble. + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrencyManagerRowCannotBeInvisible)); + } + // Trying to reset the current cell. May throw an exception. + ResetCurrentCell(); + // Microsoft: Should the current cell be set to some cell after the operation? + } + CorrectRowFrozenStates(dataGridViewRow, rowIndex, elementState == DataGridViewElementStates.Frozen); + break; + + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.Y == rowIndex && + (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly && + this.IsCurrentCellInEditMode && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + // Row becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + DataGridViewCell dataGridViewCell = element as DataGridViewCell; + if (dataGridViewCell != null) + { + // cell.ReadOnly changing + switch (elementState) + { + case DataGridViewElementStates.ReadOnly: + if (this.ptCurrentCell.X == dataGridViewCell.ColumnIndex && + this.ptCurrentCell.Y == dataGridViewCell.RowIndex && + this.IsCurrentCellInEditMode && + !dataGridViewCell.ReadOnly && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + Debug.Assert(!this.Columns[dataGridViewCell.ColumnIndex].ReadOnly); + Debug.Assert(!this.Rows[dataGridViewCell.RowIndex].ReadOnly); + Debug.Assert(!this.ReadOnly); + // Current cell becomes read-only. Exit editing mode. + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.Always /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CommitFailedCannotCompleteOperation)); + } + } + break; + + default: + Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging"); + break; + } + } + else + { + Debug.Fail("Unexpected DataGridViewElement type in DataGridView.OnDataGridViewElementStateChanging"); + } + } + } + } + + /// + protected virtual void OnDataMemberChanged(EventArgs e) + { + RefreshColumnsAndRows(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATAMEMBERCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (this.dataConnection != null && this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + + /// + protected virtual void OnDataSourceChanged(EventArgs e) + { + RefreshColumnsAndRows(); + InvalidateRowHeights(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDATASOURCECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (this.dataConnection != null && this.dataConnection.CurrencyManager != null) + { + OnDataBindingComplete(ListChangedType.Reset); + } + } + + /// + protected virtual void OnDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + Invalidate(); + } + else + { + OnGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnDefaultValuesNeeded(DataGridViewRowEventArgs e) + { + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWDEFAULTVALUESNEEDED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnDoubleClick(EventArgs e) + { + base.OnDoubleClick(e); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + MouseEventArgs me = e as MouseEventArgs; + if (me != null) + { + HitTestInfo hti = HitTest(me.X, me.Y); + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar && + me.Button == MouseButtons.Left) + { + OnCellDoubleClick(new DataGridViewCellEventArgs(hti.col, hti.row)); + } + } + } + } + + /// + protected virtual void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e) + { + DataGridViewEditingControlShowingEventHandler eh = this.Events[EVENT_DATAGRIDVIEWEDITINGCONTROLSHOWING] as DataGridViewEditingControlShowingEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnEditModeChanged(EventArgs e) + { + if (this.Focused && + this.EditMode == DataGridViewEditMode.EditOnEnter && + this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode) + { + // New edit mode is EditOnEnter and there is an editable current cell, try to go to edit mode. + BeginEditInternal(true /*selectAll*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWEDITMODECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + if (GetAnyDisposingInHierarchy()) + { + return; + } + + if (this.IsHandleCreated && this.Enabled) + { + if (this.vertScrollBar != null && this.vertScrollBar.Visible) + { + int totalVisibleHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + this.vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight; + + this.vertScrollBar.Enabled = true; + } + if (this.horizScrollBar != null && this.horizScrollBar.Visible) + { + this.horizScrollBar.Enabled = true; + } + } + } + + /// + protected override void OnEnter(EventArgs e) + { + if (this.editingControl != null && this.editingControl.ContainsFocus) + { + return; + } + + base.OnEnter(e); + + // vsw 453314: there are cases when a control is on the designer and it still receives the OnEnter event. + // Guard against this. + if (this.DesignMode) + { + return; + } + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] = false; + + if (this.ptCurrentCell.X > -1) + { + DataGridViewCell dataGridViewCell = null; + // We're re-entering a row we already entered earlier. The first time we entered the row, + // the DataGridView didn't have focus. This time it does. We don't want to recreate the new row a second time. + OnRowEnter(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*canCreateNewRow*/, false /*validationFailureOccurred*/); + if (this.ptCurrentCell.X == -1) + { + return; + } + OnCellEnter(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + + // Force repainting of the current collumn's header cell to highlight it + if (this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && AccessibilityImprovements.Level2) + { + InvalidateCellPrivate(this.ptCurrentCell.X, -1); + } + } + else if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // Focus is given to the DataGridView control via a the TAB key. + MakeFirstDisplayedCellCurrentCell(true /*includeNewRow*/); + } + + if (this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode) + { + if (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)) + { + BeginEditInternal(true /*selectAll*/); + if (this.ptCurrentCell.X > -1 && this.CurrentCellInternal.EditType == null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // The current cell does not have an edit type so the data grid view did not put an edit control on top. + // We should invalidate the current cell so that the dataGridView repaints the focus around the current cell. + // But do that only if the dataGridView did not get the focus via mouse. + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + else if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown]) + { + // When the focus is given to the DataGridView control via mouse + // the dataGridView changes selection so it invalidates the current cell anyway + // + // In any other case Invalidate the current cell so the dataGridView repaints the focus around the current cell + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + + // Draw focus rectangle around the grid + if (this.IsGridFocusRectangleEnabled()) + { + this.InvalidateRectangleEdges(this.GetGridFocusRectangle()); + } + } + + /// + protected override void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + + if (GetAnyDisposingInHierarchy ()) + { + return; + } + + // Change may be due to an ambient font change. + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] && + this.ColumnHeadersDefaultCellStyle.Font != base.Font) + { + this.ColumnHeadersDefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientColumnHeadersFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnColumnHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] && + this.RowHeadersDefaultCellStyle.Font != base.Font) + { + this.RowHeadersDefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientRowHeadersFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnRowHeadersDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] && + this.DefaultCellStyle.Font != base.Font) + { + this.DefaultCellStyle.Font = base.Font; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientFont] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true; + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + + /// + protected override void OnForeColorChanged(EventArgs e) + { + base.OnForeColorChanged(e); + if (GetAnyDisposingInHierarchy()) + { + return; + } + + // Change may be due to an ambient forecolor change. + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] && this.DefaultCellStyle.ForeColor != base.ForeColor) + { + this.DefaultCellStyle.ForeColor = base.ForeColor; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_ambientForeColor] = true; + this.CellStyleChangedEventArgs.ChangeAffectsPreferredSize = false; + OnDefaultCellStyleChanged(this.CellStyleChangedEventArgs); + } + } + + private void OnGlobalAutoSize() + { + Invalidate(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool autoSizeRowHeaders = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + if (autoSizeRowHeaders) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, + this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize /*fixedColumnHeadersHeight*/, + this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None /*fixedRowsHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, false /*fixedColumnsWidth*/); + } + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, true /*fixedHeight*/); + + if (autoSizeRowHeaders && + (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize || this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None)) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None) + { + // Second round of rows autosizing + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + + /// + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + if (this.ptCurrentCell.X != -1) + { + InvalidateCell(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + // Inform Accessibility that our current cell contains the focus. + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] && + (!this.dataGridViewOper[DATAGRIDVIEWOPER_inEndEdit] || this.EditMode != DataGridViewEditMode.EditOnEnter) && + (!this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] || this.EditMode != DataGridViewEditMode.EditOnEnter) && + this.ptCurrentCell.X > -1) + { + // The name is misleading ( the current cell did not change ). + // However, AccessibilityNotifyCurrentCellChanged is now a public method so we can't change its name + // to better reflect its purpose. + AccessibilityNotifyCurrentCellChanged(this.ptCurrentCell); + } + } + + /// + protected virtual void OnGridColorChanged(EventArgs e) + { + InvalidateInside(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWGRIDCOLORCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + + if (this.layout.dirty) + { + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + if (this.ptCurrentCell.X == -1) + { + MakeFirstDisplayedCellCurrentCell(false /*includeNewRow*/); + } + else + { + ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false /*forCurrentCellChange*/); + } + + // do the AutoSize work that was skipped during initialization + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_autoSizedWithoutHandle] = false; + OnGlobalAutoSize(); + } + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + } + + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + internal void OnInsertedColumn_PreNotification(DataGridViewColumn dataGridViewColumn) + { + // Fix the OldFirstDisplayedScrollingCol + this.displayedBandsInfo.CorrectColumnIndexAfterInsertion(dataGridViewColumn.Index, 1); + + // Fix the Index of all following columns + CorrectColumnIndexesAfterInsertion(dataGridViewColumn, 1); + + // Same effect as appending a column + OnAddedColumn(dataGridViewColumn); + } + + internal void OnInsertedColumn_PostNotification(Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + this.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 1 /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertedRow_PreNotification(int rowIndex, int insertionCount) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(insertionCount > 0); + + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, insertionCount); + + // Fix the Index of all following rows + CorrectRowIndexesAfterInsertion(rowIndex, insertionCount); + + // Next, same effect as adding a row + OnAddedRow_PreNotification(rowIndex); + } + + internal void OnInsertedRow_PostNotification(int rowIndex, Point newCurrentCell, bool lastInsertion) + { + Debug.Assert(rowIndex >= 0); + + // Same effect as adding a row + OnAddedRow_PostNotification(rowIndex); + + // Update current cell if needed + if (lastInsertion && newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + this.Rows.GetRowCount(DataGridViewElementStates.Visible) == 1 /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertedRows_PreNotification(int rowIndex, DataGridViewRow[] dataGridViewRows) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(dataGridViewRows != null); + Debug.Assert(dataGridViewRows.Length > 0); + + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, dataGridViewRows.Length); + + // Fix the Index of all following rows + CorrectRowIndexesAfterInsertion(rowIndex, dataGridViewRows.Length); + + // Next, same effect as adding the rows + OnAddedRows_PreNotification(dataGridViewRows); + } + + internal void OnInsertedRows_PostNotification(DataGridViewRow[] dataGridViewRows, Point newCurrentCell) + { + Debug.Assert(dataGridViewRows != null); + Debug.Assert(dataGridViewRows.Length > 0); + + // Same effect as adding the rows + OnAddedRows_PostNotification(dataGridViewRows); + + // Update current cell if needed + if (newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, newCurrentCell.Y, true, false, false, false /*clearSelection*/, false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + } + + internal void OnInsertingColumn(int columnIndexInserted, DataGridViewColumn dataGridViewColumn, out Point newCurrentCell) + { + Debug.Assert(dataGridViewColumn != null); + + if (dataGridViewColumn.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ColumnAlreadyBelongsToDataGridView)); + } + + if (!this.InInitialization && + dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && + (this.SelectionMode == DataGridViewSelectionMode.FullColumnSelect || + this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), this.SelectionMode.ToString())); + } + + if (dataGridViewColumn.Visible) + { + // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used + + // Make sure the column does not autosize based only on header while column headers are invisible + if (!this.ColumnHeadersVisible && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoSizedColumn)); + } + + // Make sure the column is not frozen and auto fills + if (dataGridViewColumn.Frozen && + (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet && this.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill))) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddAutoFillColumn)); + } + } + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectColumnFrozenState(dataGridViewColumn, columnIndexInserted); + + // Reset current cell if there is one, no matter the relative position of the columns involved + if (this.ptCurrentCell.X != -1) + { + newCurrentCell = new Point(columnIndexInserted <= this.ptCurrentCell.X ? this.ptCurrentCell.X + 1 : this.ptCurrentCell.X, + this.ptCurrentCell.Y); + ResetCurrentCell(); + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // prepare the existing rows by inserting cells of correct type + if (this.Rows.Count > 0) + { + // Only require a default cell type when there are rows to fill + if (dataGridViewColumn.CellType == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAddUntypedColumn)); + } + + if (dataGridViewColumn.CellTemplate.DefaultNewRowValue != null && this.newRowIndex != -1) + { + // New row needs to be unshared before addition of new cell with a Value != null + DataGridViewRow newRow = this.Rows[this.newRowIndex]; + } + + int newColumnCount = this.Columns.Count + 1; + + try + { + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count < newColumnCount) + { + DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + dataGridViewRow.Cells.InsertInternal(columnIndexInserted, dataGridViewCellNew); + if (rowIndex == this.newRowIndex) + { + dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue; + } + dataGridViewCellNew.DataGridViewInternal = this; + dataGridViewCellNew.OwningRowInternal = dataGridViewRow; + dataGridViewCellNew.OwningColumnInternal = dataGridViewColumn; + } + } + } + catch + { + // An error occurred while inserting the cells. Revert all the insertions. + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count == newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(columnIndexInserted); + } + else + { + Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount); + break; + } + } + throw; + } + } + + // Update the indexes of selected columns to compensate for the insertion of this column + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + int columnEntries = this.selectedBandIndexes.Count; + int columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndex = this.selectedBandIndexes[columnEntry]; + if (columnIndexInserted <= columnIndex) + { + this.selectedBandIndexes[columnEntry] = columnIndex + 1; + } + columnEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + columnEntries = this.selectedBandSnapshotIndexes.Count; + columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndex = this.selectedBandSnapshotIndexes[columnEntry]; + if (columnIndexInserted <= columnIndex) + { + this.selectedBandSnapshotIndexes[columnEntry] = columnIndex + 1; + } + columnEntry++; + } + } + break; + } + } + + internal void OnInsertingRow(int rowIndexInserted, + DataGridViewRow dataGridViewRow, + DataGridViewElementStates rowState, + ref Point newCurrentCell, + bool firstInsertion, + int insertionCount, + bool force) + { + // Reset the current cell's address if it's after the inserted row. + if (firstInsertion) + { + if (this.ptCurrentCell.Y != -1 && rowIndexInserted <= this.ptCurrentCell.Y) + { + newCurrentCell = new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y + insertionCount); + if (force) + { + // When force is true, the underlying data was already added, therefore we need to avoid accessing any back-end data + // since we might be off by 1 row. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + ResetCurrentCell(); + } + } + else + { + newCurrentCell = new Point(-1, -1); + } + } + else + { + if (newCurrentCell.Y != -1) + { + newCurrentCell.Y += insertionCount; + } + } + + // For now same checks as for OnAddingRow + OnAddingRow(dataGridViewRow, rowState, false /*checkFrozenState*/); + + // check for correctness of frozen state - throws exception if state is incorrect. + CorrectRowFrozenState(dataGridViewRow, rowState, rowIndexInserted); + + // Update the indexes of selected rows to compensate for the insertion of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex + insertionCount; + } + rowEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex + insertionCount; + } + rowEntry++; + } + } + break; + } + } + + internal void OnInsertingRows(int rowIndexInserted, DataGridViewRow[] dataGridViewRows, ref Point newCurrentCell) + { + Debug.Assert(dataGridViewRows != null); + + // Reset the current cell's address if it's after the inserted row. + if (this.ptCurrentCell.Y != -1 && rowIndexInserted <= this.ptCurrentCell.Y) + { + newCurrentCell = new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y + dataGridViewRows.Length); + ResetCurrentCell(); + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // For now almost same checks as for OnAddingRows + // OnAddingRows checks for Selected status of rows. + OnAddingRows(dataGridViewRows, false /*checkFrozenStates*/); + + // Check for Frozen state correctness + CorrectRowFrozenStates(dataGridViewRows, rowIndexInserted); + + // Update the indexes of selected rows to compensate for the insertion of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex + dataGridViewRows.Length; + } + rowEntry++; + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexInserted <= rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex + dataGridViewRows.Length; + } + rowEntry++; + } + } + break; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if (e.Handled) + { + return; + } + + // Forward key down to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyDownUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyDownInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyDownInternal(e, this.ptCurrentCell.Y); + } + } + + if (!e.Handled) + { + switch (e.KeyData & Keys.KeyCode) + { + case Keys.A: + case Keys.C: + case Keys.D0: + case Keys.NumPad0: + case Keys.Delete: + case Keys.Down: + case Keys.F2: + case Keys.F3: + case Keys.End: + case Keys.Enter: + case Keys.Escape: + case Keys.Home: + case Keys.Insert: + case Keys.Left: + case Keys.Next: + case Keys.Prior: + case Keys.Right: + case Keys.Space: + case Keys.Tab: + case Keys.Up: + { + e.Handled = ProcessDataGridViewKey(e); + break; + } + } + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + if (e.Handled) + { + return; + } + + // Forward key press to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyPressUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyPressInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyPressInternal(e, this.ptCurrentCell.Y); + } + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if (e.Handled) + { + return; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] && (e.KeyData & Keys.Alt) != Keys.Alt && AccessibilityImprovements.Level2) + { + this.EndColumnResize(this.currentColSplitBar); + this.ResetKeyboardTrackingState(); + return; + } + + // Forward key up to current cell if any + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + if (dataGridViewCell.KeyUpUnsharesRowInternal(e, this.ptCurrentCell.Y)) + { + DataGridViewRow dataGridViewRow = this.Rows[this.ptCurrentCell.Y]; + this.CurrentCellInternal.OnKeyUpInternal(e, this.ptCurrentCell.Y); + } + else + { + dataGridViewCell.OnKeyUpInternal(e, this.ptCurrentCell.Y); + } + } + } + + /// + protected override void OnLayout(LayoutEventArgs e) + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging]) + { + return; + } + + base.OnLayout(e); + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + if (this.RightToLeftInternal) + { + Invalidate(); + } + if (this.editingControl != null) + { + PositionEditingControl(true, true, false); + } + } + + /// + protected override void OnLeave(EventArgs e) + { + if (this.ptCurrentCell.X > -1 && !this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + DataGridViewCell dataGridViewCell = null; + OnCellLeave(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (this.ptCurrentCell.X == -1) + { + return; + } + OnRowLeave(ref dataGridViewCell, this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + base.OnLeave(e); + + // invalidate the current cell so the data grid view does not paint the focus rectangle any longer + if (this.ptCurrentCell.X > -1 && this.ptCurrentCell.Y > -1) + { + InvalidateCellPrivate(this.ptCurrentCell.X /*columnIndex*/, this.ptCurrentCell.Y /*rowIndex*/); + } + } + + // Erase focus rectangle around the grid + if (this.IsGridFocusRectangleEnabled()) + { + this.InvalidateRectangleEdges(this.GetGridFocusRectangle()); + } + } + + /// + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + if (this.ptCurrentCell.X != -1) + { + InvalidateCell(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + } + + /// + protected override void OnMouseClick(MouseEventArgs e) + { + bool mouseClickRaised = false; + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + HitTestInfo hti = HitTest(e.X, e.Y); + + if (this.ptMouseDownCell.X == hti.col && + this.ptMouseDownCell.Y == hti.row && + (e.Button != MouseButtons.Left || + this.ptMouseDownCell.X == -1 || + this.ptMouseDownCell.Y == -1 || + (this.ptMouseDownCell.X == this.ptCurrentCell.X && + this.ptMouseDownCell.Y == this.ptCurrentCell.Y))) + { + DataGridViewCellMouseEventArgs dgvcme = null; + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + RecordCellMouseClick(dgvcme); + if (e.Button == MouseButtons.Left) + { + OnCellClick(new DataGridViewCellEventArgs(hti.col, hti.row)); + } + base.OnMouseClick(e); + mouseClickRaised = true; + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseClick(dgvcme); + } + } + else + { + base.OnMouseClick(e); + mouseClickRaised = true; + } + + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + switch (hti.typeInternal) + { + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnColumnHeaderMouseClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.RowHeader: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnRowHeaderMouseClick(dgvcme); + } + break; + } + } + } + } + } + if (!mouseClickRaised) + { + base.OnMouseClick(e); + } + } + + /// + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + HitTestInfo hti = HitTest(e.X, e.Y); + + if (this.ptMouseDownCell.X == hti.col && this.ptMouseDownCell.Y == hti.row) + { + DataGridViewCellMouseEventArgs dgvcme = null; + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + OnCellMouseDoubleClick(dgvcme); + } + + if (!this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + switch (hti.typeInternal) + { + case DataGridViewHitTestTypeInternal.ColumnHeader: + case DataGridViewHitTestTypeInternal.ColumnHeaderLeft: + case DataGridViewHitTestTypeInternal.ColumnHeaderRight: + case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnColumnHeaderMouseDoubleClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.ColumnResizeLeft: + case DataGridViewHitTestTypeInternal.ColumnResizeRight: + { + int columnIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti.col : hti.adjacentCol; + if (columnIndex < this.Columns.Count) + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new DataGridViewColumnDividerDoubleClickEventArgs(columnIndex, hme); + Debug.Assert(this.Columns[columnIndex].Resizable == DataGridViewTriState.True); + OnColumnDividerDoubleClick(dgvcddce); + } + break; + } + + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop: + case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom: + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new DataGridViewRowDividerDoubleClickEventArgs(-1, hme); + Debug.Assert(this.columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing); + OnRowDividerDoubleClick(dgvrddce); + break; + } + + case DataGridViewHitTestTypeInternal.RowHeader: + { + Debug.Assert(dgvcme != null); + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnRowHeaderMouseDoubleClick(dgvcme); + } + break; + } + + case DataGridViewHitTestTypeInternal.RowResizeBottom: + case DataGridViewHitTestTypeInternal.RowResizeTop: + { + int rowIndex = (hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti.row : hti.adjacentRow; + if (rowIndex < this.Rows.Count) + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new DataGridViewRowDividerDoubleClickEventArgs(rowIndex, hme); + Debug.Assert(this.Rows[rowIndex].Resizable == DataGridViewTriState.True); + OnRowDividerDoubleClick(dgvrddce); + } + break; + } + + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft: + case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight: + case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft: + case DataGridViewHitTestTypeInternal.RowHeadersResizeRight: + { + HandledMouseEventArgs hme = new HandledMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, false /*defaultHandledValue*/); + DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new DataGridViewColumnDividerDoubleClickEventArgs(-1, hme); + Debug.Assert(this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing); + OnColumnDividerDoubleClick(dgvcddce); + break; + } + } + } + } + } + } + + /// + protected override void OnMouseDown(MouseEventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves] = true; + } + + base.OnMouseDown(e); + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + return; + } + + if (this.ptMouseDownCell.X != -2) + { + // Happens when user pushes the mouse wheel down while the left mouse button is already down + Debug.Assert(this.ptMouseDownCell.Y != -2); + return; + } + + HitTestInfo hti = HitTest(e.X, e.Y); + + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + this.ptMouseDownCell.X = hti.col; + this.ptMouseDownCell.Y = hti.row; + this.ptMouseDownGridCoord = new Point(e.X, e.Y); + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + OnCellMouseDown(dgvcme); + } + } + + /// + protected override void OnMouseEnter(EventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] && + !this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] && + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] && + !this.toolTipControl.Activated) + { + base.OnMouseEnter(e); + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingCtrl] = false; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseOverRemovedEditingPanel] = false; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] = false; + } + + /// + protected override void OnMouseLeave(EventArgs e) + { + // when the mouse leaves the dataGridView control, reset the cursor to the previously cached one + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + + bool mouseOverEditingControl = this.MouseOverEditingControl; + bool mouseOverEditingPanel = this.MouseOverEditingPanel; + bool mouseOverToolTipControl = this.toolTipControl.Activated && this.ClientRectangle.Contains(PointToClient(Control.MousePosition)); + + if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && this.ptMouseEnteredCell.X != -2) + { + if (this.ptMouseEnteredCell.X >= -1 && this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && this.ptMouseEnteredCell.Y < this.Rows.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + else + { + this.ptMouseEnteredCell.X = this.ptMouseEnteredCell.Y = -2; + } + } + + ResetTrackingState(); + this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves] = false; + + if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && !this.MouseOverScrollBar) + { + this.toolTipControl.Activate(false /*activate*/); + base.OnMouseLeave(e); + this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected] = true; + } + } + + /// + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + HitTestInfo hti = HitTest(e.X, e.Y); + + UpdateMouseEnteredCell(hti, e); + + // We need to give UI feedback when the user is resizing a column + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize]) + { + MoveRowHeadersOrColumnResize(e.X); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]) + { + MoveColumnHeadersOrRowResize(e); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + MoveColumnRelocation(e, hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize]) + { + MoveColumnHeadersOrRowResize(e); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + MoveRowHeadersOrColumnResize(e.X); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] || + ((hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeRight) && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect])) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.SizeWE; + return; + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] || + ((hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom || + hti.typeInternal == DataGridViewHitTestTypeInternal.RowResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom) && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect])) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.SizeNS; + return; + } + /* Whidbey bug 156884 - no longer show hand cursor + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation] && + (hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.ColumnHeaderRight || + hti.typeInternal == DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft || + hti.typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight)) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = true; + this.oldCursor = this.Cursor; + } + this.CursorInternal = Cursors.Hand; + return; + }*/ + else if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_customCursorSet] = false; + this.CursorInternal = this.oldCursor; + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + int xOffset, yOffset, mouseX = e.X, mouseY = e.Y; + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (xOffset == 0) + { + if (this.horizScrollTimer != null && this.horizScrollTimer.Enabled) + { + // Mouse's X came in-bound - need to stop the horizontal scroll timer + this.horizScrollTimer.Enabled = false; + } + } + else if (this.horizScrollTimer == null || !this.horizScrollTimer.Enabled) + { + // Need to start delayed horizontal scroll + this.HorizScrollTimer.Interval = GetColumnScrollRate(Math.Abs(xOffset)); + this.HorizScrollTimer.Enabled = true; + } + + if (yOffset == 0) + { + if (this.vertScrollTimer != null && this.vertScrollTimer.Enabled) + { + // Mouse's Y came in-bound - need to stop the vertical scroll timer + this.vertScrollTimer.Enabled = false; + } + } + else if (this.vertScrollTimer == null || !this.vertScrollTimer.Enabled) + { + // Need to start delayed vertical scroll + this.VertScrollTimer.Interval = GetRowScrollRate(Math.Abs(yOffset)); + this.VertScrollTimer.Enabled = true; + } + + if (this.HorizScrollTimer.Enabled || this.VertScrollTimer.Enabled) + { + return; + } + + if (/*!this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] && */ + hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] && hti.col >= 0) + { + OnColumnSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + } +#if DEBUG + else + { + Debug.Assert(this.vertScrollTimer == null || !this.vertScrollTimer.Enabled); + Debug.Assert(this.horizScrollTimer == null || !this.horizScrollTimer.Enabled); + } +#endif + if (!this.toolTipControl.Activated) + { + // + // After processing the MouseMove event, the tool tip is still not activated. + // Reset the tool tip cell. + this.ptToolTipCell = new Point(-1, -1); + } + } + + /// + protected override void OnMouseUp(MouseEventArgs e) + { + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_messageFromEditingCtrls]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = false; + + HitTestInfo hti = HitTest(e.X, e.Y); + + if (!this.IsMouseOperationActive()) + { + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme; + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_nextMouseUpIsDouble]) + { + MouseEventArgs meTmp = new MouseEventArgs(e.Button, 2, e.X, e.Y, e.Delta); + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, meTmp); + } + else + { + dgvcme = new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e); + } + if (hti.col >= 0 && this.ptMouseDownCell.X == hti.col && + hti.row >= 0 && this.ptMouseDownCell.Y == hti.row && + this.EditMode == DataGridViewEditMode.EditOnEnter && + this.editingControl != null) + { + OnClick(e); + OnMouseClick(e); + } + + CorrectFocus(true /*onlyIfGridHasFocus*/); + + if (dgvcme.ColumnIndex < this.Columns.Count && dgvcme.RowIndex < this.Rows.Count) + { + OnCellMouseUp(dgvcme); + } + } + else if (hti.Type == DataGridViewHitTestType.None) + { + // VS Whidbey bug 469429 + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize]) + { + EndColumnResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]) + { + EndRowResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColRelocation]) + { + EndColumnRelocation(e, hti); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize]) + { + EndColumnHeadersResize(e); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize]) + { + EndRowHeadersResize(e); + } + + // VS Whidbey bug 256893 + CorrectFocus(true /*onlyIfGridHasFocus*/); + + // Updating the hit test info since the EndXXX operation above may have invalidated the previously + // determined hti. + hti = HitTest(e.X, e.Y); + if (hti.Type != DataGridViewHitTestType.None && + hti.Type != DataGridViewHitTestType.HorizontalScrollBar && + hti.Type != DataGridViewHitTestType.VerticalScrollBar) + { + int mouseX = e.X - hti.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((hti.col == -1) ? this.RowHeadersWidth : this.Columns[hti.col].Thickness); + } + OnCellMouseUp(new DataGridViewCellMouseEventArgs(hti.col, hti.row, mouseX, e.Y - hti.RowY, e)); + } + } + + ResetTrackingState(); + } + base.OnMouseUp(e); + } + + /// + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + HandledMouseEventArgs hme = e as HandledMouseEventArgs; + if (hme != null && hme.Handled) + { + // The application event handler handled the scrolling - don't do anything more. + return; + } + + if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) + { + return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down. + } + + bool verticalScroll = ((ModifierKeys & Keys.Control) == 0); + + ScrollBar sb = (verticalScroll ? (ScrollBar) this.vertScrollBar : (ScrollBar) this.horizScrollBar); + + if (!sb.Visible || !sb.Enabled) + { + return; // Do not scroll when the corresponding scrollbar is invisible or disabled + } + + if (hme != null) + { + hme.Handled = true; + } + + int wheelScrollLines = SystemInformation.MouseWheelScrollLines; + if (wheelScrollLines == 0) + { + return; // Do not scroll when the user system setting is 0 lines per notch + } + + Debug.Assert(this.cumulativeVerticalWheelDelta > -NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeVerticalWheelDelta < NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeHorizontalWheelDelta > -NativeMethods.WHEEL_DELTA); + Debug.Assert(this.cumulativeHorizontalWheelDelta < NativeMethods.WHEEL_DELTA); + + float partialNotches; + + if (verticalScroll) + { + this.cumulativeVerticalWheelDelta += e.Delta; + partialNotches = (float) this.cumulativeVerticalWheelDelta / (float) NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeHorizontalWheelDelta += e.Delta; + partialNotches = (float) this.cumulativeHorizontalWheelDelta / (float) NativeMethods.WHEEL_DELTA; + } + + int fullNotches = (int) partialNotches; + + if (wheelScrollLines == -1) + { + // Equivalent to large change scrolls + if (fullNotches != 0) + { + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value + return; + } + if (verticalScroll) + { + int originalVerticalOffset = this.VerticalOffset; + this.VerticalOffset -= fullNotches * this.vertScrollBar.LargeChange; + if (Math.Abs(this.VerticalOffset - originalVerticalOffset) >= Math.Abs(fullNotches * this.vertScrollBar.LargeChange)) + { + this.cumulativeVerticalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeVerticalWheelDelta = 0; + } + } + else + { + int originalHorizontalOffset = this.HorizontalOffset; + this.HorizontalOffset -= fullNotches * this.horizScrollBar.LargeChange; + if (Math.Abs(this.HorizontalOffset - originalHorizontalOffset) >= Math.Abs(fullNotches * this.horizScrollBar.LargeChange)) + { + this.cumulativeHorizontalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA; + } + else + { + this.cumulativeHorizontalWheelDelta = 0; + } + } + } + } + else + { + // Evaluate number of bands to scroll + int scrollBands = (int) ((float) wheelScrollLines * partialNotches); + if (scrollBands != 0) + { + if (this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange*/, false /*forCurrentRowChange*/)) + { + // Could not commit edited cell value + return; + } + int absScrollBands; + if (verticalScroll) + { + if (scrollBands > 0) + { + absScrollBands = scrollBands; + while (this.vertScrollBar.Value != this.vertScrollBar.Minimum && absScrollBands > 0) + { + ScrollRowsByCount(-1, ScrollEventType.SmallDecrement); + absScrollBands--; + } + if (this.vertScrollBar.Value == this.vertScrollBar.Minimum) + { + this.cumulativeVerticalWheelDelta = 0; + } + else + { + this.cumulativeVerticalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + else + { + absScrollBands = -scrollBands; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (absScrollBands > 0 && + this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + ScrollRowsByCount(1, ScrollEventType.SmallIncrement); + // Assuming totalVisibleFrozenHeight is unchanged by scrolling operation + Debug.Assert(totalVisibleFrozenHeight == this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + absScrollBands--; + } + if (this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) > + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + this.cumulativeVerticalWheelDelta = 0; + } + else + { + this.cumulativeVerticalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + } + else + { + int extremeScrollBarValue, scrollBand; + if (scrollBands > 0) + { + extremeScrollBarValue = this.horizScrollBar.Minimum; + scrollBand = -1; + } + else + { + extremeScrollBarValue = this.horizScrollBar.Maximum; + scrollBand = 1; + } + absScrollBands = Math.Abs(scrollBands); + while (this.horizScrollBar.Value != extremeScrollBarValue && absScrollBands > 0) + { + ScrollColumns(scrollBand); + absScrollBands--; + } + if (this.horizScrollBar.Value == extremeScrollBarValue) + { + this.cumulativeHorizontalWheelDelta = 0; + } + else + { + this.cumulativeHorizontalWheelDelta -= (int) ((float) scrollBands * ((float) NativeMethods.WHEEL_DELTA / (float) wheelScrollLines)); + } + } + } + } + } + + internal void OnMouseWheelInternal(MouseEventArgs e) + { + // Notification forwarded from editing control + OnMouseWheel(e); + } + + /// + protected virtual void OnMultiSelectChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWMULTISELECTCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnNewRowNeeded(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWNEWROWNEEDED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected override void OnPaint(PaintEventArgs e) + { + try + { + // We can't paint if we are disposed. + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] || this.IsDisposed) + { + return; + } + + if (this.layout.dirty) + { + PerformLayoutPrivate(false /*useRowShortcut*/, true /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + Graphics g = e.Graphics; + Rectangle clipRect = e.ClipRectangle; + Rectangle gridRect = this.GetGridRectangle(); + + if (this.currentRowSplitBar != -1) + { + clipRect = Rectangle.Union(clipRect, CalcRowResizeFeedbackRect(this.currentRowSplitBar)); + } + else if (this.currentColSplitBar != -1) + { + clipRect = Rectangle.Union(clipRect, CalcColResizeFeedbackRect(this.currentColSplitBar)); + } + + if (clipRect.IntersectsWith(gridRect)) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(gridRect); + PaintBackground(g, clipRect, gridRect); + PaintGrid(g, gridRect, clipRect, this.SingleVerticalBorderAdded, this.SingleHorizontalBorderAdded); + g.Clip = clipRegion; + } + } + + PaintBorder(g, clipRect, this.layout.ClientRectangle); + if (clipRect.IntersectsWith(this.layout.ResizeBoxRect)) + { + g.FillRectangle(SystemBrushes.Control, this.layout.ResizeBoxRect); + } + + // Draw focus rectangle around the grid + if (this.Focused && this.IsGridFocusRectangleEnabled()) + { + if (SystemInformation.HighContrast) + { + ControlPaint.DrawHighContrastFocusRectangle(g, this.GetGridFocusRectangle(), SystemColors.ActiveCaptionText); + } + else + { + ControlPaint.DrawFocusRectangle(g, this.GetGridFocusRectangle()); + } + } + + base.OnPaint(e); // raise paint event + } + catch (Exception ex) + { + #if DEBUG + Debug.Fail("DataGridView.OnPaint exception: " + ex.Message + " stack trace " + ex.StackTrace); + #endif + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + } + + // Determines if a focus rectangle may be drawn along the perimeter of the DataGridView control + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsGridFocusRectangleEnabled() + { + return this.ShowFocusCues && this.CurrentCell == null && AccessibilityImprovements.Level2; + } + + // Creates a rectangle by merging row headers, column headers + // and cells rectangles (from layout data) + private Rectangle GetGridRectangle() + { + Rectangle gridRect = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + gridRect = Rectangle.Union(gridRect, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + gridRect.X--; + } + gridRect.Width++; + } + + if (this.layout.ColumnHeadersVisible) + { + gridRect = Rectangle.Union(gridRect, this.layout.ColumnHeaders); + } + else if (this.SingleHorizontalBorderAdded) + { + if (gridRect.Y == this.layout.Data.Y) + { + gridRect.Y--; + gridRect.Height++; + } + } + + return gridRect; + } + + // Creates a grid focus rectangle + private Rectangle GetGridFocusRectangle() + { + Rectangle focusRect = this.GetGridRectangle(); + focusRect.Inflate(1 - FOCUS_RECT_OFFSET, 1 - FOCUS_RECT_OFFSET); + return focusRect; + } + + private void InvalidateGridFocusOnScroll(int change, ScrollOrientation orientation) + { + if (change == 0) + { + return; + } + + Rectangle focusRect = GetGridFocusRectangle(); + + if (orientation == ScrollOrientation.HorizontalScroll) + { + // Scroll right + if (change > 0) + { + focusRect.Width -= change; + } + // Scroll left + else + { + focusRect.X -= change; + focusRect.Width += change; + } + } + else + { + // Scroll down + if (change > 0) + { + focusRect.Height -= change; + } + // Scroll up + else + { + focusRect.Y -= change; + focusRect.Height += change; + } + } + + this.InvalidateRectangleEdges(focusRect); + } + + private void InvalidateRectangleEdges(Rectangle rect) + { + // Left edge + Rectangle edge = rect; + edge.Width = 1; + this.Invalidate(edge); + + // Right edge + edge.X += rect.Width - 1; + this.Invalidate(edge); + + // Top edge + edge = rect; + edge.Height = 1; + this.Invalidate(edge); + + // Bottom edge + edge.Y += rect.Height - 1; + this.Invalidate(edge); + } + + // See VSWhidbey 527459 & 526373. + internal override void OnParentBecameInvisible() + { + base.OnParentBecameInvisible(); + if (GetState(STATE_VISIBLE)) + { + // This control became invisible too - Update the Displayed properties of the bands. + OnVisibleChangedPrivate(); + } + } + + /// + protected virtual void OnReadOnlyChanged(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWREADONLYCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + VerifyImeRestrictedModeChanged(); + + if (!this.ReadOnly && + this.ptCurrentCell.X != -1 && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)) && + !this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X].ReadOnly) // Unshares the row + { + // Current cell becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + + internal void OnRemovedColumn_PreNotification(DataGridViewColumn dataGridViewColumn) + { + Debug.Assert(dataGridViewColumn.Index >= 0); + Debug.Assert(dataGridViewColumn.DataGridView == null); + + // Clear the potential header sort glyph + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.SortGlyphDirectionInternal = SortOrder.None; + } + // Intentionally keep the DisplayIndex intact after detaching the column. + CorrectColumnIndexesAfterDeletion(dataGridViewColumn); + + CorrectColumnDisplayIndexesAfterDeletion(dataGridViewColumn); + + // Fix the OldFirstDisplayedScrollingCol + this.displayedBandsInfo.CorrectRowIndexAfterDeletion(dataGridViewColumn.Index); + + // Raise the ColumnRemoved event + OnColumnRemoved(dataGridViewColumn); + } + + internal void OnRemovedColumn_PostNotification(DataGridViewColumn dataGridViewColumn, Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.X != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + + // Raise SelectionChanged event if needed + FlushSelectionChanged(); + + // Raise ColumnStateChanged event for Displayed state of deleted column + OnColumnHidden(dataGridViewColumn); + + // Columns that are removed from the collection take their non-autosized width + // Note that in some edge cases, the dev could have changed: + // - the grid's AutoSizeColumnsMode + // - the column's Width or AutoSizeMode + // in a ColumnRemoved event handler for example, in which case using the CachedThickness may be wrong. + // At least we make sure the column is not sized smaller than its minimum width. + DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.GetInheritedAutoSizeMode(this); + Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet); + if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None && + autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill && + dataGridViewColumn.ThicknessInternal != dataGridViewColumn.CachedThickness) + { + dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness); + } + + // Autosize rows if needed + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + internal void OnRemovedRow_PreNotification(int rowIndexDeleted) + { + // Fix the OldFirstDisplayedScrollingRow + this.displayedBandsInfo.CorrectRowIndexAfterDeletion(rowIndexDeleted); + + CorrectRowIndexesAfterDeletion(rowIndexDeleted); + ComputeVisibleRows(); + } + + internal void OnRemovedRow_PostNotification(DataGridViewRow dataGridViewRow, Point newCurrentCell) + { + // Update current cell if needed + if (newCurrentCell.Y != -1) + { + Debug.Assert(this.ptCurrentCell.X == -1); + bool success = SetAndSelectCurrentCellAddress(newCurrentCell.X, + newCurrentCell.Y, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + + // Raise SelectionChange event if needed + FlushSelectionChanged(); + + bool rowDisplayed = dataGridViewRow.DataGridView == null && dataGridViewRow.Displayed; + + // Raise RowStateChanged event for Displayed state of deleted row + if (rowDisplayed) + { + dataGridViewRow.DisplayedInternal = false; + DataGridViewRowStateChangedEventArgs dgvrsce = new DataGridViewRowStateChangedEventArgs(dataGridViewRow, DataGridViewElementStates.Displayed); + OnRowStateChanged(-1 /*rowIndex*/, dgvrsce); + } + + // Rows that are removed from the collection take their non-autosized height + // Note that in some edge cases, the dev could have changed: + // - the grid's AutoSizeRowsMode + // - the row's Height + // in a RowsRemoved event handler for example, in which case using the CachedThickness may be wrong. + // At least we make sure the row is not sized smaller than its minimum height. + if (this.autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None && dataGridViewRow.ThicknessInternal != dataGridViewRow.CachedThickness) + { + dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness); + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + bool fixedColumnHeadersHeight = this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + + if (fixedRowHeadersWidth && !columnAutoSized) + { + // No need to autosize the column headers when the row headers and columns don't change. + fixedColumnHeadersHeight = true; + } + + // Auto size column headers + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + if (!fixedColumnHeadersHeight && !fixedRowHeadersWidth) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + internal void OnRemovingColumn(DataGridViewColumn dataGridViewColumn, out Point newCurrentCell, bool force) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Index >= 0 && dataGridViewColumn.Index < this.Columns.Count); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + int columnIndex = dataGridViewColumn.Index; + + // Reset the current cell's address if there is one. + if (this.ptCurrentCell.X != -1) + { + int newX = this.ptCurrentCell.X; + if (columnIndex == this.ptCurrentCell.X) + { + DataGridViewColumn dataGridViewColumnNext = this.Columns.GetNextColumn( + this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnNext != null) + { + if (dataGridViewColumnNext.Index > columnIndex) + { + newX = dataGridViewColumnNext.Index - 1; + } + else + { + newX = dataGridViewColumnNext.Index; + } + } + else + { + DataGridViewColumn dataGridViewColumnPrevious = this.Columns.GetPreviousColumn( + this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumnPrevious != null) + { + if (dataGridViewColumnPrevious.Index > columnIndex) + { + newX = dataGridViewColumnPrevious.Index - 1; + } + else + { + newX = dataGridViewColumnPrevious.Index; + } + } + else + { + newX = -1; + } + } + } + else if (columnIndex < this.ptCurrentCell.X) + { + newX = this.ptCurrentCell.X - 1; + } + newCurrentCell = new Point(newX, (newX == -1) ? -1 : this.ptCurrentCell.Y); + if (columnIndex == this.ptCurrentCell.X) + { + // Left cell is not validated since cancelling validation wouldn't have any effect anyways. + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else if (force) + { + // Underlying data of deleted column is gone. It cannot be accessed anymore. + // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + // Quit editing mode and set the current cell to its new location once everything is in sync again. + ResetCurrentCell(); + } + } + else + { + newCurrentCell = new Point(-1, -1); + } + + // If the last column is removed, delete all the rows first. + if (this.Columns.Count == 1) + { + Debug.Assert(columnIndex == 0); + this.Rows.ClearInternal(false /*recreateNewRow*/); + } + + // Prepare the existing rows by removing cells at correct index + int newColumnCount = this.Columns.Count - 1; + + for (int rowIndex = 0; rowIndex < this.Rows.Count; rowIndex++) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + if (dataGridViewRow.Cells.Count > newColumnCount) + { + dataGridViewRow.Cells.RemoveAtInternal(columnIndex); + } + } + + // Detach column header cell + if (dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.DataGridViewInternal = null; + } + + // Reset sort related variables. + if (dataGridViewColumn == this.sortedColumn) + { + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + + if (dataGridViewColumn.IsDataBound) + { + // If the column being removed is the sorted column and it is also the dataBound column + // then see if there is another dataBound column which has the same property name as the sorted column. + // If so, then make that dataGridViewColumn the sorted column in the data grid view. + for (int i = 0; i < this.Columns.Count; i ++) + { + if (dataGridViewColumn != this.Columns[i] && + this.Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable && + String.Compare(dataGridViewColumn.DataPropertyName, + this.Columns[i].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + Debug.Assert(this.Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time"); + Debug.Assert(this.Columns[i].HeaderCell.SortGlyphDirection == dataGridViewColumn.HeaderCell.SortGlyphDirection, "DataBound columns should have the same SortGlyphDirection as the one on the DataGridView"); + this.sortedColumn = this.Columns[i]; + this.sortOrder = this.Columns[i].HeaderCell.SortGlyphDirection; + break; + } + } + } + } + + // Is deleted column scrolled off screen? + if (dataGridViewColumn.Visible && + !dataGridViewColumn.Frozen && + this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0) + { + // Deleted column is part of scrolling columns. + if (this.displayedBandsInfo.FirstDisplayedScrollingCol == dataGridViewColumn.Index) + { + // Deleted column is first scrolling column + this.horizontalOffset -= this.negOffset; + this.negOffset = 0; + } + else if (this.Columns.DisplayInOrder(this.displayedBandsInfo.FirstDisplayedScrollingCol, dataGridViewColumn.Index)) + { + // Deleted column is displayed after first scrolling column + if (this.horizScrollBar.Enabled) + { + int newHorizontalOffset = this.horizScrollBar.Maximum - this.horizScrollBar.LargeChange - dataGridViewColumn.Thickness; + if (newHorizontalOffset >= 0 && newHorizontalOffset < this.horizScrollBar.Value) + { + this.horizontalOffset = newHorizontalOffset; + this.negOffset = GetNegOffsetFromHorizontalOffset(this.horizontalOffset); + } + } + else + { + this.horizontalOffset = this.negOffset = 0; + } + } + else + { + // Deleted column is displayed before first scrolling column + Debug.Assert(this.horizontalOffset >= dataGridViewColumn.Thickness); + this.horizontalOffset -= dataGridViewColumn.Thickness; + } + + if (this.horizScrollBar.Enabled) + { + this.horizScrollBar.Value = this.horizontalOffset; + } + } + + bool raiseSelectionChanged = false; + + // Update the indexes of selected columns or individual cells to compensate for the removal of this column + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + int columnEntries = this.selectedBandIndexes.Count; + int columnEntry = 0; + while (columnEntry < columnEntries) + { + int columnIndexSelected = this.selectedBandIndexes[columnEntry]; + if (columnIndex == columnIndexSelected) + { + this.selectedBandIndexes.RemoveAt(columnEntry); + columnEntries--; + raiseSelectionChanged = true; + } + else + { + if (columnIndex < columnIndexSelected) + { + this.selectedBandIndexes[columnEntry] = columnIndexSelected - 1; + } + columnEntry++; + } + } + break; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] |= this.individualSelectedCells.RemoveAllCellsAtBand(true /*column*/, columnIndex) > 0 || + raiseSelectionChanged; + this.individualReadOnlyCells.RemoveAllCellsAtBand(true /*column*/, columnIndex); + } + + internal void OnRemovingRow(int rowIndexDeleted, out Point newCurrentCell, bool force) + { + // if force is true, the row needs to be deleted no matter what. The underlying data row was already deleted. + + Debug.Assert(rowIndexDeleted >= 0 && rowIndexDeleted < this.Rows.Count); + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + newCurrentCell = new Point(-1, -1); + + // Reset the current cell's address if it's on the deleted row, or after it. + if (this.ptCurrentCell.Y != -1 && rowIndexDeleted <= this.ptCurrentCell.Y) + { + int newY; + if (rowIndexDeleted == this.ptCurrentCell.Y) + { + int rowIndexPrevious = this.Rows.GetPreviousRow(rowIndexDeleted, DataGridViewElementStates.Visible); + int rowIndexNext = this.Rows.GetNextRow(rowIndexDeleted, DataGridViewElementStates.Visible); + if (rowIndexPrevious > -1 && this.AllowUserToAddRowsInternal) + { + Debug.Assert(this.newRowIndex != -1); + Debug.Assert(this.newRowIndex == this.Rows.Count-1); + if (rowIndexNext > -1 && rowIndexNext < this.Rows.Count - 1) + { + newY = rowIndexNext - 1; + } + else + { + newY = rowIndexPrevious; + } + } + else + { + if (rowIndexNext > -1) + { + newY = rowIndexNext - 1; + } + else + { + newY = rowIndexPrevious; + } + } + // Since the current row is deleted, the dirty states need to be reset + this.IsCurrentCellDirtyInternal = false; + this.IsCurrentRowDirtyInternal = false; + } + else + { + Debug.Assert(rowIndexDeleted < this.ptCurrentCell.Y); + newY = this.ptCurrentCell.Y - 1; + } + newCurrentCell = new Point(this.ptCurrentCell.X, newY); + if (rowIndexDeleted == this.ptCurrentCell.Y) + { + // Left cell is not validated since cancelling validation wouldn't have any effect anyways. + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else if (force) + { + // Underlying data of deleted row is gone. It cannot be accessed anymore. + // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value. + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = true; + bool success = SetCurrentCellAddressCore(-1, -1, true /*setAnchorCellAddress*/, false /*validateCurrentCell*/, false); + Debug.Assert(success); + } + else + { + // Quit editing mode and set the current cell to its new location once everything is in sync again. + ResetCurrentCell(); + } + } + + bool raiseSelectionChanged = false; + + // Update the indexes of selected rows to compensate for the removal of this row + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int rowEntries = this.selectedBandIndexes.Count; + int rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandIndexes[rowEntry]; + if (rowIndexDeleted == rowIndex) + { + raiseSelectionChanged = true; + this.selectedBandIndexes.RemoveAt(rowEntry); + rowEntries--; + } + else + { + if (rowIndexDeleted < rowIndex) + { + this.selectedBandIndexes[rowEntry] = rowIndex - 1; + } + rowEntry++; + } + } + if (this.selectedBandSnapshotIndexes != null) + { + rowEntries = this.selectedBandSnapshotIndexes.Count; + rowEntry = 0; + while (rowEntry < rowEntries) + { + int rowIndex = this.selectedBandSnapshotIndexes[rowEntry]; + if (rowIndexDeleted == rowIndex) + { + this.selectedBandSnapshotIndexes.RemoveAt(rowEntry); + rowEntries--; + } + else + { + if (rowIndexDeleted < rowIndex) + { + this.selectedBandSnapshotIndexes[rowEntry] = rowIndex - 1; + } + rowEntry++; + } + } + } + break; + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] |= this.individualSelectedCells.RemoveAllCellsAtBand(false /*column*/, rowIndexDeleted) > 0 || + raiseSelectionChanged; + this.individualReadOnlyCells.RemoveAllCellsAtBand(false /*column*/, rowIndexDeleted); + } + + internal void OnReplacedCell(DataGridViewRow dataGridViewRow, int columnIndex) + { + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected]) + { + this.individualSelectedCells.Add(dataGridViewCell); + } + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly]) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + } + + // AutoSize column and row if needed + OnCellCommonChange(columnIndex, dataGridViewRow.Index); + + if (this.ptCurrentCellCache.X != -1) + { + if (!IsInnerCellOutOfBounds(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y)) + { + SetCurrentCellAddressCore(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y, false, false, false); + } + this.ptCurrentCellCache.X = -1; + this.ptCurrentCellCache.Y = -1; + } + } + + internal void OnReplacingCell(DataGridViewRow dataGridViewRow, int columnIndex) + { + if (this.ptCurrentCell.X == dataGridViewRow.Index && + this.ptCurrentCell.Y == columnIndex) + { + // Trying to replace the current cell. Exiting editing mode first (if needed). + // Remember to reset the current cell in OnReplacedCell notification + this.ptCurrentCellCache.X = this.ptCurrentCell.X; + this.ptCurrentCellCache.Y = this.ptCurrentCell.Y; + // This may fail and throw an exception + ResetCurrentCell(); + } + else + { + this.ptCurrentCellCache.X = -1; + this.ptCurrentCellCache.Y = -1; + } + DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex]; + this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected] = this.individualSelectedCells.Contains(dataGridViewCell); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellSelected]) + { + this.individualSelectedCells.Remove(dataGridViewCell); + } + this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly] = this.individualReadOnlyCells.Contains(dataGridViewCell); + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_replacedCellReadOnly]) + { + this.individualReadOnlyCells.Remove(dataGridViewCell); + } + } + + /// + protected override void OnResize(EventArgs e) + { + int borderWidth = this.BorderWidth; + Rectangle right; + Rectangle bottom; + Rectangle oldClientRectangle = this.layout.ClientRectangle; + Rectangle oldGridFocusRectangle = this.GetGridFocusRectangle(); + + right = new Rectangle(oldClientRectangle.X + oldClientRectangle.Width - borderWidth, + oldClientRectangle.Y, + borderWidth, + oldClientRectangle.Height); + bottom = new Rectangle(oldClientRectangle.X, + oldClientRectangle.Y + oldClientRectangle.Height - borderWidth, + oldClientRectangle.Width, + borderWidth); + + if (!this.IsMinimized) + { + // When owning form is minimized, act as if it had a normal size + this.normalClientRectangle = this.ClientRectangle; + } + + Rectangle newClientRectangle = this.normalClientRectangle; + Rectangle newGridFocusRectangle = this.DisplayRectangle; + newGridFocusRectangle.Inflate(1 - borderWidth - FOCUS_RECT_OFFSET, 1 - borderWidth - FOCUS_RECT_OFFSET); + + if (newClientRectangle.Width != oldClientRectangle.Width) + { + Invalidate(right); + right = new Rectangle(newClientRectangle.X + newClientRectangle.Width - borderWidth, + newClientRectangle.Y, + borderWidth, + newClientRectangle.Height); + Invalidate(right); + } + if (newClientRectangle.Height != oldClientRectangle.Height) + { + Invalidate(bottom); + bottom = new Rectangle(newClientRectangle.X, + newClientRectangle.Y + newClientRectangle.Height - borderWidth, + newClientRectangle.Width, + borderWidth); + Invalidate(bottom); + } + + // Invalidate grid focus rectangle + if (this.Focused && this.IsGridFocusRectangleEnabled() && oldGridFocusRectangle != newGridFocusRectangle) + { + right = new Rectangle(oldGridFocusRectangle.X + oldGridFocusRectangle.Width - 1, + oldGridFocusRectangle.Y, + 1, + oldGridFocusRectangle.Height); + Invalidate(right); + + bottom = new Rectangle(oldGridFocusRectangle.X, + oldGridFocusRectangle.Y + oldGridFocusRectangle.Height - 1, + oldGridFocusRectangle.Width, + 1); + Invalidate(bottom); + + InvalidateRectangleEdges(newGridFocusRectangle); + } + + //also, invalidate the ResizeBoxRect + if (!this.layout.ResizeBoxRect.IsEmpty) + { + Invalidate(this.layout.ResizeBoxRect); + } + this.layout.ClientRectangle = newClientRectangle; + + int oldfirstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + base.OnResize(e); + if (oldfirstDisplayedScrollingRow != this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + Invalidate(); + } + } + + /// + protected override void OnRightToLeftChanged(EventArgs e) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rightToLeftValid] = false; + base.OnRightToLeftChanged(e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + } + + internal void OnRowCollectionChanged_PostNotification(bool recreateNewRow, + bool allowSettingCurrentCell, + CollectionChangeAction cca, + DataGridViewRow dataGridViewRow, + int rowIndex) + { + if (recreateNewRow && + cca == CollectionChangeAction.Refresh && + this.Columns.Count != 0 && + this.Rows.Count == 0 && + this.AllowUserToAddRowsInternal) + { + AddNewRow(false); + } + + if (cca == CollectionChangeAction.Refresh) + { + FlushSelectionChanged(); + } + + if ((cca == CollectionChangeAction.Refresh || cca == CollectionChangeAction.Add) && + this.ptCurrentCell.X == -1 && allowSettingCurrentCell && !this.InSortOperation) + { + MakeFirstDisplayedCellCurrentCell(false /*includeNewRow*/); + } + + if (this.AutoSize) + { + bool invalidatePreferredSizeCache = true; + switch (cca) + { + case CollectionChangeAction.Add: + Debug.Assert(rowIndex >= 0); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + invalidatePreferredSizeCache = ((rowState & DataGridViewElementStates.Visible) != 0); + break; + case CollectionChangeAction.Remove: + invalidatePreferredSizeCache = dataGridViewRow.DataGridView == null && dataGridViewRow.Visible; + break; + // case CollectionChangeAction.Refresh: invalidatePreferredSizeCache stays true + } + if (invalidatePreferredSizeCache) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + } + } + + /// + protected virtual void OnRowContextMenuStripChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal ContextMenuStrip OnRowContextMenuStripNeeded(int rowIndex, ContextMenuStrip contextMenuStrip) + { + DataGridViewRowContextMenuStripNeededEventArgs dgvrcmsne = new DataGridViewRowContextMenuStripNeededEventArgs(rowIndex, contextMenuStrip); + OnRowContextMenuStripNeeded(dgvrcmsne); + return dgvrcmsne.ContextMenuStrip; + } + + /// + protected virtual void OnRowContextMenuStripNeeded(DataGridViewRowContextMenuStripNeededEventArgs e) + { + DataGridViewRowContextMenuStripNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWCONTEXTMENUSTRIPNEEDED] as DataGridViewRowContextMenuStripNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDefaultCellStyleChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDEFAULTCELLSTYLECHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDirtyStateNeeded(QuestionEventArgs e) + { + QuestionEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIRTYSTATENEEDED] as QuestionEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowDividerDoubleClick(DataGridViewRowDividerDoubleClickEventArgs e) + { + DataGridViewRowDividerDoubleClickEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIVIDERDOUBLECLICK] as DataGridViewRowDividerDoubleClickEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (!e.Handled && e.Button == MouseButtons.Left && e.RowIndex < this.Rows.Count) + { + if (e.RowIndex == -1) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + else + { + if (this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None) + { + AutoResizeRowInternal(e.RowIndex, DataGridViewAutoSizeRowMode.AllCells, true /*fixedWidth*/, true /*internalAutosizing*/); + } + else + { + AutoResizeRowInternal(e.RowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + } + } + } + + /// + protected virtual void OnRowDividerHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWDIVIDERHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowEnter(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex, bool canCreateNewRow, bool validationFailureOccurred) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0); + + if (!validationFailureOccurred) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] = false; + } + if (rowIndex < this.Rows.Count && + columnIndex < this.Columns.Count) + { + bool calledAddNewOnTheDataConnection = false; + if (!validationFailureOccurred && this.AllowUserToAddRowsInternal && this.newRowIndex == rowIndex) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowEdited] = true; + + if (canCreateNewRow) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(this.Rows[this.newRowIndex]); + if (this.VirtualMode || this.DataSource != null) + { + if (this.dataConnection != null && this.dataConnection.InterestedInRowEvents) + { + this.dataConnection.OnNewRowNeeded(); + calledAddNewOnTheDataConnection = true; + } + if (this.VirtualMode) + { + OnNewRowNeeded(dgvre); + } + } + + // vsWhidbey 329429: AllowUserToAddRowsInternal can become FALSE while adding a row. + // NOTE: we don't need to invalidate if AllowUserToAddRowsInternal changed to FALSE. + // + if (this.AllowUserToAddRowsInternal) + { + OnDefaultValuesNeeded(dgvre); + InvalidateRowPrivate(this.newRowIndex); + } + #if DEBUG + else + { + Debug.Assert(this.newRowIndex == -1, "newRowIndex and AllowUserToAddRowsInternal became out of sync"); + } + #endif // + } + } + + if (calledAddNewOnTheDataConnection && rowIndex > this.Rows.Count - 1) + { + // Calling AddNew on the DataConnection can result in the entire list being wiped out. + // + rowIndex = Math.Min(rowIndex, this.Rows.Count - 1); + } + + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowEnter(dgvce); + if (this.dataConnection != null && + this.dataConnection.InterestedInRowEvents && + !this.dataConnection.PositionChangingOutsideDataGridView && + !this.dataConnection.ListWasReset && + (!calledAddNewOnTheDataConnection || this.dataConnection.List.Count > 0)) + { + this.dataConnection.OnRowEnter(dgvce); + } + + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + } + + /// + protected virtual void OnRowEnter(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWENTER] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + internal void OnRowErrorTextChanged(DataGridViewRow dataGridViewRow) + { + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnRowErrorTextChanged(dgvre); + } + + /// + protected virtual void OnRowErrorTextChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + UpdateRowErrorText(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWERRORTEXTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal string OnRowErrorTextNeeded(int rowIndex, string errorText) + { + Debug.Assert(rowIndex >= 0); + DataGridViewRowErrorTextNeededEventArgs dgvretne = new DataGridViewRowErrorTextNeededEventArgs(rowIndex, errorText); + OnRowErrorTextNeeded(dgvretne); + return dgvretne.ErrorText; + } + + /// + protected virtual void OnRowErrorTextNeeded(DataGridViewRowErrorTextNeededEventArgs e) + { + DataGridViewRowErrorTextNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWERRORTEXTNEEDED] as DataGridViewRowErrorTextNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowGlobalAutoSize(int rowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + return; + } + + InvalidateRowPrivate(rowIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + bool autoSizeRow = false; + bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0; + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None && + !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + // Auto size column headers + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth) + { + // Second round of column headers autosizing + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + /// + protected virtual void OnRowHeaderCellChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + + OnRowHeaderGlobalAutoSize(e.Row.Index); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERCELLCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeaderGlobalAutoSize(int rowIndex) + { + if (!this.RowHeadersVisible) + { + return; + } + + InvalidateCellPrivate(-1, rowIndex); + + if (this.noAutoSizeCount > 0) + { + return; + } + + bool rowDisplayed = false; + if (rowIndex != -1) + { + rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + } + + bool fixedColumnHeadersHeight = rowIndex != -1 || this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize; + bool fixedRowHeight = rowIndex == -1 || + ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0) || + ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && rowIndex != -1 && !rowDisplayed); + + bool autoSizeRowHeaders = false; + if (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders || + (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && rowIndex != -1 && rowDisplayed) || + (this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing && rowIndex == -1) || + (this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader && rowIndex != -1 && rowIndex == this.Rows.GetFirstRow(DataGridViewElementStates.Visible))) + { + AutoResizeRowHeadersWidth(rowIndex, + this.rowHeadersWidthSizeMode, + fixedColumnHeadersHeight, + fixedRowHeight); + autoSizeRowHeaders = true; + } + if (!fixedColumnHeadersHeight) + { + AutoResizeColumnHeadersHeight(-1, true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + if (!fixedRowHeight) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (autoSizeRowHeaders && (!fixedColumnHeadersHeight || !fixedRowHeight)) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(rowIndex, + this.rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowHeight*/); + } + } + + /// + protected virtual void OnRowHeaderMouseClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERMOUSECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) + { + DataGridViewCellMouseEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERMOUSEDOUBLECLICK] as DataGridViewCellMouseEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown) + { + Debug.Assert(hti.Type == DataGridViewHitTestType.RowHeader); + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + break; + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + bool select = true; + if (isControlDown && + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)) + { + select = false; + } + if (select) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null && hti.row != this.ptCurrentCell.Y) + { + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + // Make sure we will be able to scroll into view + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + DataGridViewValidateCellInternal.Always /*validateCell*/, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + hti.row != this.ptCurrentCell.Y /*fireRowLeave*/, + hti.row != this.ptCurrentCell.Y /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + true /*resetCurrentCell*/, + false /*resetAnchorCell*/)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + return; + } + if (oldCurrentCellY != -1) + { + DataGridViewCell dataGridViewCellTmp = null; + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + return; + } + if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + + // Row validation was not cancelled, but operation needs to be re-evaluated. + if (hti.row >= this.Rows.Count) + { + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (this.ptCurrentCell.X == -1 && lastVisibleRowIndex != -1) + { + // CurrentCell was reset because commit deleted row(s). + // Since the user wants to select a row, we don't want to + // end up with no CurrentCell. We pick the last visible + // row in the grid which may be the 'new row'. + if (IsColumnOutOfBounds(oldCurrentCellX)) + { + return; + } + bool success = SetAndSelectCurrentCellAddress(oldCurrentCellX, + lastVisibleRowIndex, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + return; + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Visible) == 0) + { + return; + } + } + } + bool selectRowRange = false; + this.trackRow = hti.row; + this.trackRowEdge = -1; + if (this.MultiSelect && + isShiftDown && + this.ptAnchorCell.Y > -1 && + (this.Rows.GetRowState(this.ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0) + { + selectRowRange = true; + } + if (!this.MultiSelect || !isControlDown || isShiftDown) + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != hti.row) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + RemoveIndividuallySelectedCells(); + } + else + { + Debug.Assert(this.individualSelectedCells.Count == 0); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + if (this.MultiSelect && this.dataGridViewOper[DATAGRIDVIEWOPER_trackMouseMoves]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = true; + } + if (selectRowRange) + { + if (hti.row >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, hti.row, true); + } + else + { + SelectRowRange(hti.row, this.ptAnchorCell.Y, true); + } + } + else if ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row) == + ((this.Rows.GetRowState(hti.row) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(hti.row, true); + } + if (dataGridViewColumn != null) + { + if (hti.row != this.ptCurrentCell.Y) + { + if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti.row)) + { + return; + } + // set current cell to the left most visible cell in the row + bool success = ScrollIntoView(dataGridViewColumn.Index, hti.row, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti.row)) + { + return; + } + success = SetCurrentCellAddressCore(dataGridViewColumn.Index, hti.row, !selectRowRange, false, true); + Debug.Assert(success); + } + else if (-1 != this.ptCurrentCell.Y) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + else + { + Debug.Assert(this.CurrentCellAddress == new Point(-1, -1)); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(hti.row)); + SetSelectedRowCore(hti.row, false); + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + } + + /// + protected virtual void OnRowHeadersBorderStyleChanged(EventArgs e) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, false /*repositionEditingControl*/); + Invalidate(); + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSBORDERSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeadersDefaultCellStyleChanged(EventArgs e) + { + if (this.RowHeadersVisible) + { + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.RowHeaders)); + + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce == null || dgvcsce.ChangeAffectsPreferredSize) + { + OnRowHeadersGlobalAutoSize(false /*expandingRows*/); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowHeadersGlobalAutoSize(bool expandingRows) + { + if (this.noAutoSizeCount > 0) + { + return; + } + + bool fixedRowsHeight = (((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0 || + !this.RowHeadersVisible; + bool autoSizeRowHeaders = this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing; + if (autoSizeRowHeaders) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, fixedRowsHeight); + } + + if (!fixedRowsHeight) + { + if (expandingRows) + { + AdjustExpandingRows(-1, true /*fixedWidth*/); + } + else + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + if (autoSizeRowHeaders) + { + // Second round of row headers autosizing + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + } + } + + /// + protected virtual void OnRowHeadersWidthChanged(EventArgs e) + { + if (this.RowHeadersVisible) + { + if (this.editingControl != null) + { + PositionEditingControl(true, false, false); + } + + if (this.IsHandleCreated) + { + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + OnRowHeadersGlobalAutoSize(false /*expandingRows*/); + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSWIDTHCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeadersWidthSizeModeChanged(DataGridViewAutoSizeModeEventArgs e) + { + if (this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing && + this.rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + if (!e.PreviousModeAutoSized) + { + // Save current row headers width for later reuse + this.cachedRowHeadersWidth = this.RowHeadersWidth; + } + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, + true /*fixedColumnHeadersHeight*/, + true /*fixedRowsHeight*/); + } + else if (e.PreviousModeAutoSized) + { + this.RowHeadersWidth = this.cachedRowHeadersWidth; + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEADERSWIDTHSIZEMODECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + UpdateRowHeightInfoPrivate(e.Row.Index, false, false /*invalidInAdjustFillingColumns*/); + + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + OnRowGlobalAutoSize(e.Row.Index); + } + + internal DataGridViewRowHeightInfoNeededEventArgs OnRowHeightInfoNeeded(int rowIndex, int height, int minimumHeight) + { + DataGridViewRowHeightInfoNeededEventArgs dgvrhine = this.RowHeightInfoNeededEventArgs; + dgvrhine.SetProperties(rowIndex, height, minimumHeight); + OnRowHeightInfoNeeded(dgvrhine); + return dgvrhine; + } + + /// + protected virtual void OnRowHeightInfoNeeded(DataGridViewRowHeightInfoNeededEventArgs e) + { + DataGridViewRowHeightInfoNeededEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTINFONEEDED] as DataGridViewRowHeightInfoNeededEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private bool OnRowHeightInfoPushed(int rowIndex, int height, int minimumHeight) + { + Debug.Assert(rowIndex != -1); + Debug.Assert(this.autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None); + if (this.VirtualMode || this.DataSource != null) + { + DataGridViewRowHeightInfoPushedEventArgs dgvrhipe = new DataGridViewRowHeightInfoPushedEventArgs(rowIndex, height, minimumHeight); + OnRowHeightInfoPushed(dgvrhipe); + if (dgvrhipe.Handled) + { + UpdateRowHeightInfoPrivate(rowIndex, false, true /*invalidInAdjustFillingColumns*/); + return true; + } + } + return false; + } + + /// + protected virtual void OnRowHeightInfoPushed(DataGridViewRowHeightInfoPushedEventArgs e) + { + DataGridViewRowHeightInfoPushedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWHEIGHTINFOPUSHED] as DataGridViewRowHeightInfoPushedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowLeave(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0); + if (rowIndex < this.Rows.Count && columnIndex < this.Columns.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowLeave(dgvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + } + + /// + protected virtual void OnRowLeave(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWLEAVE] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + /// + protected virtual void OnRowMinimumHeightChanged(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWMINIMUMHEIGHTCHANGED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected internal virtual void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e) + { + DataGridViewRowPostPaintEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWPOSTPAINT] as DataGridViewRowPostPaintEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected internal virtual void OnRowPrePaint(DataGridViewRowPrePaintEventArgs e) + { + DataGridViewRowPrePaintEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWPREPAINT] as DataGridViewRowPrePaintEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnRowsAddedInternal(int rowIndex, int rowCount) + { + OnRowsAdded(new DataGridViewRowsAddedEventArgs(rowIndex, rowCount)); + } + + /// + protected virtual void OnRowsAdded(DataGridViewRowsAddedEventArgs e) + { + DataGridViewRowsAddedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSADDED] as DataGridViewRowsAddedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowsDefaultCellStyleChanged(EventArgs e) + { + DataGridViewCellStyleChangedEventArgs dgvcsce = e as DataGridViewCellStyleChangedEventArgs; + if (dgvcsce != null && !dgvcsce.ChangeAffectsPreferredSize) + { + InvalidateData(); + } + else + { + OnRowsGlobalAutoSize(); + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSDEFAULTCELLSTYLECHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnRowSelectMouseMove(HitTestInfo hti) + { + Debug.Assert(hti.row >= 0); + Debug.Assert(this.MultiSelect); + + if (this.ptCurrentCell.Y != -1 && + hti.row != this.ptCurrentCell.Y && + !CommitEditForOperation(this.ptCurrentCell.X, hti.row, true)) + { + // Return silently if validating/commit/abort failed + return; + } + if (IsRowOutOfBounds(hti.row)) + { + return; + } + + this.noSelectionChangeCount++; + try + { + if (this.trackRowEdge >= this.trackRow && hti.row > this.trackRowEdge && this.trackRowEdge >= 0) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRowEdge, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge > this.trackRow && hti.row < this.trackRowEdge && hti.row >= this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.Rows.GetNextRow(hti.row, DataGridViewElementStates.Visible), + this.trackRowEdge, false); + this.trackRowEdge = hti.row; + } + else if (hti.row > this.trackRow && this.trackRowEdge == -1) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge <= this.trackRow && hti.row < this.trackRowEdge && this.trackRowEdge >= 0) + { + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRowEdge, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge < this.trackRow && hti.row > this.trackRowEdge && hti.row <= this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.trackRowEdge, + this.Rows.GetPreviousRow(hti.row, DataGridViewElementStates.Visible), + false); + this.trackRowEdge = hti.row; + } + else if (hti.row < this.trackRow && this.trackRowEdge == -1) + { + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (this.trackRowEdge > this.trackRow && hti.row < this.trackRow) + { + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + this.trackRowEdge, false); + SelectRowRange(hti.row, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + true); + this.trackRowEdge = hti.row; + } + else if (hti.row > this.trackRow && this.trackRowEdge < this.trackRow && this.trackRowEdge >= 0) + { + SelectRowRange(this.trackRowEdge, + this.Rows.GetPreviousRow(this.trackRow, DataGridViewElementStates.Visible), + false); + SelectRowRange(this.Rows.GetNextRow(this.trackRow, DataGridViewElementStates.Visible), + hti.row, true); + this.trackRowEdge = hti.row; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.Y != -1 && hti.row != this.ptCurrentCell.Y) + { + if (IsRowOutOfBounds(hti.row)) + { + return; + } + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, + hti.row, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + + private void OnRowsGlobalAutoSize() + { + InvalidateData(); + + if (this.noAutoSizeCount > 0) + { + return; + } + + // Autosize rows if needed + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, false /*fixedWidth*/, true /*internalAutosizing*/); + } + + // Auto size columms also if needed + AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | + DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, + true /*fixedHeight*/); + + bool fixedRowHeadersWidth = this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing; + // Auto size column headers + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, true /*fixedColumnsWidth*/); + } + + // Auto size row headers + if (!fixedRowHeadersWidth) + { + AutoResizeRowHeadersWidth(this.rowHeadersWidthSizeMode, true /*fixedColumnHeadersHeight*/, true /*fixedRowsHeight*/); + } + + // Second round of rows autosizing + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + + // Second round of column headers autosizing + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnsWidth*/); + } + } + + internal void OnRowsRemovedInternal(int rowIndex, int rowCount) + { + OnRowsRemoved(new DataGridViewRowsRemovedEventArgs(rowIndex, rowCount)); + } + + /// + protected virtual void OnRowsRemoved(DataGridViewRowsRemovedEventArgs e) + { + DataGridViewRowsRemovedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSREMOVED] as DataGridViewRowsRemovedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnRowStateChanged(int rowIndex, DataGridViewRowStateChangedEventArgs e) + { + Debug.Assert(rowIndex >= -1); + + // If row.Frozen changed, we need to update the vertical scroll bar + // A hidden row may become visible and vice-versa, we'd better repaint the whole control + DataGridViewRow dataGridViewRow = e.Row; + DataGridViewElementStates newState = DataGridViewElementStates.None; + bool rowVisible = false; + if (rowIndex >= 0) + { + newState = this.Rows.GetRowState(rowIndex); + rowVisible = ((newState & DataGridViewElementStates.Visible) != 0); + } + switch (e.StateChanged) + { + // At this point we assume that only the Selected state has an influence on the rendering of the row. + // If there is a customer scenario where another state has an influence, the dev will have to invalidate the row by hand. + // case DataGridViewElementStates.ReadOnly: + // case DataGridViewElementStates.Resizable: + + case DataGridViewElementStates.Selected: + if (rowVisible && this.inBulkPaintCount == 0) + { + InvalidateRowPrivate(rowIndex); + } + break; + + case DataGridViewElementStates.Frozen: + if (rowVisible) + { + if ((newState & DataGridViewElementStates.Frozen) == 0) + { + // row was unfrozen - make it the first visible scrolling row if there is room + FirstVisibleScrollingRowTempted(rowIndex); + } + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + } + break; + + case DataGridViewElementStates.Visible: + { + if (!rowVisible && (newState & DataGridViewElementStates.Displayed) != 0) + { + // Displayed row becomes invisible. Turns off the Displayed state. + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Displayed, false); + } + + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, true /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + Invalidate(); + + bool rowDisplayed = (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + + bool autoSizeRow = false; + + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + int height = dataGridViewRow.ThicknessInternal; + if (rowVisible) + { + // Cache row's height before potential autosizing occurs + // Valid operation even for shared rows + dataGridViewRow.CachedThickness = height; + if (!((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed)) + { + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + autoSizeRow = true; + } + } + else if (height != dataGridViewRow.CachedThickness) + { + // Rows that are made invisible in the collection take their non-autosized height + // Not calling OnRowHeightInfoPushed(...) because rows are autosized + // Make sure the affected row is unshared + if (dataGridViewRow.Index == -1) + { + dataGridViewRow = this.Rows[rowIndex]; + } + dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness); + } + } + + // Auto size columms also if needed + DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows; + if (rowDisplayed) + { + autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows; + } + if (rowVisible && this.Rows.GetRowCount(DataGridViewElementStates.Visible) > 1) + { + // Columns can only expand, and not collapse. + AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex); + } + else + { + AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, true /*fixedHeight*/); + } + + if (autoSizeRow) + { + // Second round of row autosizing + AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), true /*fixedWidth*/, true /*internalAutosizing*/); + } + break; + } + } + + DataGridViewRowStateChangedEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWSTATECHANGED] as DataGridViewRowStateChangedEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + + if (e.StateChanged == DataGridViewElementStates.ReadOnly && + rowIndex == this.ptCurrentCell.Y && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange]) + { + VerifyImeRestrictedModeChanged(); + + if ((newState & DataGridViewElementStates.ReadOnly) == 0 && + !this.ReadOnly && + !this.Columns[this.ptCurrentCell.X].ReadOnly && + ColumnEditable(this.ptCurrentCell.X) && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + // Current row becomes read/write. Enter editing mode. + BeginEditInternal(true /*selectAll*/); + } + } + } + + internal void OnRowUnshared(DataGridViewRow dataGridViewRow) + { + if (-1 != this.ptCurrentCell.X && dataGridViewRow.Index == this.ptCurrentCell.Y && this.editingControl != null) + { + this.CurrentCellInternal.CacheEditingControl(); + } + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnRowUnshared(dgvre); + } + + /// + protected virtual void OnRowUnshared(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWUNSHARED] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private bool OnRowValidating(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + DataGridViewCellCancelEventArgs dgvcce = new DataGridViewCellCancelEventArgs(columnIndex, rowIndex); + OnRowValidating(dgvcce); + if (!dgvcce.Cancel) + { + if (this.dataConnection != null && + this.dataConnection.InterestedInRowEvents && + !this.dataConnection.PositionChangingOutsideDataGridView && + !this.dataConnection.ListWasReset) + { + this.dataConnection.OnRowValidating(dgvcce); + } + } + if (dataGridViewCell != null && rowIndex < this.Rows.Count && columnIndex < this.Columns.Count) + { + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + return dgvcce.Cancel; + } + + /// + protected virtual void OnRowValidating(DataGridViewCellCancelEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWVALIDATING] as DataGridViewCellCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + private void OnRowValidated(ref DataGridViewCell dataGridViewCell, int columnIndex, int rowIndex) + { + this.IsCurrentRowDirtyInternal = false; + this.dataGridViewState1[DATAGRIDVIEWSTATE1_newRowCreatedByEditing] = false; + if (rowIndex == this.newRowIndex) + { + // Stop displaying the default cell values on the 'new row'. + InvalidateRowPrivate(rowIndex); + } + + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(columnIndex, rowIndex); + OnRowValidated(dgvce); + if (dataGridViewCell != null) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + dataGridViewCell = null; + } + else + { + Debug.Assert(rowIndex < this.Rows.Count && columnIndex < this.Columns.Count); + dataGridViewCell = this.Rows.SharedRow(rowIndex).Cells[columnIndex]; + } + } + } + + /// + protected virtual void OnRowValidated(DataGridViewCellEventArgs e) + { + try + { + this.noDimensionChangeCount++; + + DataGridViewCellEventHandler eh = this.Events[EVENT_DATAGRIDVIEWROWVALIDATED] as DataGridViewCellEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + CorrectFocus(true /*onlyIfGridHasFocus*/); + } + } + finally + { + this.noDimensionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + } + } + + private void RefreshByCurrentPos(int oldValue, int newValue) + { + Point pt = PointToScreen(Location); + int step = newValue - oldValue; + + // horizontal scrool left + if (pt.X < 0 && step < 0) + { + Invalidate(new Rectangle(new Point(-pt.X, ColumnHeadersHeight), + new Size(-step, ClientSize.Height))); + } + + pt.X += Width; + pt.Y += Height; + Rectangle rect = Screen.GetBounds(pt); + + // horizontal scrool right + if (pt.X > rect.Right && step > 0) + { + Invalidate(new Rectangle(new Point(ClientSize.Width - (pt.X - rect.Right) - step, ColumnHeadersHeight), + new Size(step, ClientSize.Height))); + } + + // vertical scrool up + if (pt.Y < 0 && step < 0) + { + Invalidate(new Rectangle(new Point(0, -pt.Y), + new Size(-step, ClientSize.Width))); + } + + // vertical scrool down + if (pt.Y > rect.Bottom && step > 0) + { + Invalidate(new Rectangle(new Point(0, ColumnHeadersHeight), + new Size(ClientSize.Width, ClientSize.Height - (pt.Y - rect.Bottom) - step))); + } + } + + private void OnScroll(ScrollEventType scrollEventType, int oldValue, int newValue, ScrollOrientation orientation) + { + ScrollEventArgs se = new ScrollEventArgs(scrollEventType, oldValue, newValue, orientation); + OnScroll(se); + RefreshByCurrentPos(oldValue, newValue); + if (this.Focused && this.IsGridFocusRectangleEnabled()) + { + InvalidateGridFocusOnScroll(newValue - oldValue, orientation); + } + if (ScrollOrientation.VerticalScroll == orientation) + { + if (se.NewValue != newValue) + { + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll] = true; + int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + int rowIndexPrevious = rowIndex; + newValue = se.NewValue; + while (rowIndex != -1 && newValue > 0) + { + rowIndexPrevious = rowIndex; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + newValue--; + } + if (rowIndex != -1) + { + rowIndexPrevious = rowIndex; + } + if (rowIndexPrevious != -1) + { + this.FirstDisplayedScrollingRowIndex = rowIndexPrevious; + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll] = false; + } + } + } + else + { + if (se.NewValue != newValue) + { + try + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll] = true; + this.HorizontalOffset = se.NewValue; + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingHorizontalScroll] = false; + } + } + } + } + + /// + protected virtual void OnScroll(ScrollEventArgs e) + { + ScrollEventHandler eh = this.Events[EVENT_DATAGRIDVIEWSCROLL] as ScrollEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnSelectionChanged(EventArgs e) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_raiseSelectionChanged] = false; + + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWSELECTIONCHANGED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal bool OnSortCompare(DataGridViewColumn dataGridViewSortedColumn, object value1, object value2, int rowIndex1, int rowIndex2, out int sortResult) + { + DataGridViewSortCompareEventArgs dgvsce = new DataGridViewSortCompareEventArgs(dataGridViewSortedColumn, value1, value2, rowIndex1, rowIndex2); + OnSortCompare(dgvsce); + sortResult = dgvsce.SortResult; + return dgvsce.Handled; + } + + /// + protected virtual void OnSortCompare(DataGridViewSortCompareEventArgs e) + { + DataGridViewSortCompareEventHandler eh = this.Events[EVENT_DATAGRIDVIEWSORTCOMPARE] as DataGridViewSortCompareEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnSorted(EventArgs e) + { + EventHandler eh = this.Events[EVENT_DATAGRIDVIEWSORTED] as EventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + internal void OnSortGlyphDirectionChanged(DataGridViewColumnHeaderCell dataGridViewColumnHeaderCell) + { + Debug.Assert(dataGridViewColumnHeaderCell != null); + + if (dataGridViewColumnHeaderCell.OwningColumn == this.SortedColumn) + { + if (dataGridViewColumnHeaderCell.SortGlyphDirection == SortOrder.None) + { + this.sortedColumn = null; + DataGridViewColumn dataGridViewColumn = dataGridViewColumnHeaderCell.OwningColumn; + + if (dataGridViewColumn.IsDataBound) + { + // If the column whose SortGlyphChanges is the sorted column and it is also the dataBound column + // then see if there is another dataBound column which has the same property name as the sorted column. + // If so, then make that dataGridViewColumn the sorted column in the data grid view. + for (int i = 0; i < this.Columns.Count; i ++) + { + if (dataGridViewColumn != this.Columns[i] && + this.Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable && + String.Compare(dataGridViewColumn.DataPropertyName, + this.Columns[i].DataPropertyName, + true /*ignoreCase*/, + CultureInfo.InvariantCulture) == 0) + { + Debug.Assert(this.Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time"); + this.sortedColumn = this.Columns[i]; + break; + } + } + } + } + + this.sortOrder = this.sortedColumn != null ? this.sortedColumn.HeaderCell.SortGlyphDirection : SortOrder.None; + } + + InvalidateCellPrivate(dataGridViewColumnHeaderCell); + } + + private void OnTopLeftHeaderMouseDown() + { + if (this.MultiSelect) + { + SelectAll(); + if (-1 != this.ptCurrentCell.X) + { + // Potentially have to give focus back to the current edited cell. + bool success = SetCurrentCellAddressCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, + false /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + } + + /// + protected virtual void OnUserAddedRow(DataGridViewRowEventArgs e) + { + if (e.Row.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "e.Row"); + } + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERADDEDROW] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnUserDeletedRow(DataGridViewRowEventArgs e) + { + DataGridViewRowEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERDELETEDROW] as DataGridViewRowEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + /// + protected virtual void OnUserDeletingRow(DataGridViewRowCancelEventArgs e) + { + DataGridViewRowCancelEventHandler eh = this.Events[EVENT_DATAGRIDVIEWUSERDELETINGROW] as DataGridViewRowCancelEventHandler; + if (eh != null && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose] && !this.IsDisposed) + { + eh(this, e); + } + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.Color || + e.Category == UserPreferenceCategory.Locale || + e.Category == UserPreferenceCategory.General || + e.Category == UserPreferenceCategory.Window || + e.Category == UserPreferenceCategory.VisualStyle) + { + OnGlobalAutoSize(); + if (e.Category == UserPreferenceCategory.Window) + { + this.cachedEditingControl = null; + if (this.editingControl != null) + { + // The editing control may not adapt well to the new system rendering, + // so instead of caching it into the this.cachedEditingControl variable + // next time editing mode is exited, simply discard the control. + this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl] = true; + } + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, false /*invalidInAdjustFillingColumns*/, true /*repositionEditingControl*/); + } + } + } + + /// + protected override void OnValidating(CancelEventArgs e) + { + // VSWhidbey 481170. Avoid Cell/Row Validation events when the grid or its editing control gets the focus + if (!this.BecomingActiveControl && (this.editingControl == null || !this.editingControl.BecomingActiveControl)) + { + if (!this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey]) + { + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl, + DataGridViewValidateCellInternal.Always, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + false /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + e.Cancel = true; + return; + } + } + + if (this.ptCurrentCell.X >= 0) + { + DataGridViewCell dataGridViewCellTmp = null; + if (OnRowValidating(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y)) + { + // Row validation was cancelled + e.Cancel = true; + return; + } + if (this.ptCurrentCell.X == -1) + { + return; + } + OnRowValidated(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y); + // Row validation was not cancelled, but does operation need to be re-evaluated. + if (this.DataSource != null && + this.ptCurrentCell.X >= 0 && + this.AllowUserToAddRowsInternal && + this.newRowIndex == this.ptCurrentCell.Y) + { + // Current cell needs to be moved to the row just above the 'new row' if possible. + int rowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + if (rowIndex > -1) + { + bool success = SetAndSelectCurrentCellAddress(this.ptCurrentCell.X, rowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + Debug.Assert(success); + } + else + { + bool success = SetCurrentCellAddressCore(-1, -1, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + Debug.Assert(success); + } + } + } + } + base.OnValidating(e); + } + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + OnVisibleChangedPrivate(); + } + + private void OnVisibleChangedPrivate() + { + // Debug.Assert(!this.displayedBandsInfo.Dirty); Not valid because EnsureDirtyState can potentially be called + // for example when RowHeadersVisible is changed while the control is invisible. + int rowIndexTmp; + + if (this.Visible) + { + // Make sure all displayed bands get the Displayed state: 1 & 2 for rows + + // 1. Make sure all displayed frozen rows have their Displayed state set to true + int numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + if (numDisplayedFrozenRows > 0) + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { +#if DEBUG + int numDisplayedFrozenRowsDbg = numDisplayedFrozenRows; + while (numDisplayedFrozenRowsDbg > 0) + { + Debug.Assert(rowIndexTmp != -1); + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRowsDbg--; + } +#endif + return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job. + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + } + } + + // 2. Make sure all displayed scrolling rows have their Displayed state set to true + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp > -1) + { + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Visible) != 0); + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if ((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0) + { +#if DEBUG + int numDisplayedScrollingRowsDbg = numDisplayedScrollingRows; + while (numDisplayedScrollingRowsDbg > 0) + { + Debug.Assert(rowIndexTmp != -1); + Debug.Assert((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRowsDbg--; + } +#endif + return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job. + } + else + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true); + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + } + } + } + else + { + // Make sure all displayed bands lose the Displayed state + UpdateRowsDisplayedState(false /*displayed*/); + } + + UpdateColumnsDisplayedState(this.Visible /*displayed*/); + } + + /// + protected virtual void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds) + { + // Paint potential block below rows + Rectangle rcBelowRows = gridBounds; + + // Fix Dev10 Bug473771 - Remaining cell images on DataGridView + int visibleRowsHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Displayed); + if (this.layout.ColumnHeadersVisible) + { + rcBelowRows.Y += this.layout.ColumnHeaders.Height; + rcBelowRows.Height -= this.layout.ColumnHeaders.Height; + } + else if (this.SingleHorizontalBorderAdded && visibleRowsHeight > 0) + { + rcBelowRows.Y++; + rcBelowRows.Height--; + } + + rcBelowRows.Y += visibleRowsHeight; + rcBelowRows.Height -= visibleRowsHeight; + if (rcBelowRows.Width > 0 && rcBelowRows.Height > 0) + { + graphics.FillRectangle(this.backgroundBrush, rcBelowRows); + } + + // Paint potential block next to column headers and rows + // Fix Dev10 Bug473771 - Remaining cell images on DataGridView + int visibleColumnsWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Displayed); + Rectangle rcNextRows = gridBounds; + if (this.Columns.Count > 0) + { + if (this.layout.RowHeadersVisible) + { + if (!this.RightToLeftInternal) + { + rcNextRows.X += this.layout.RowHeaders.Width; + } + rcNextRows.Width -= this.layout.RowHeaders.Width; + } + else if (this.SingleVerticalBorderAdded && visibleColumnsWidth > 0) + { + if (!this.RightToLeftInternal) + { + rcNextRows.X++; + } + rcNextRows.Width--; + } + } + int rowsWidth = visibleColumnsWidth - this.horizontalOffset; + if (!this.RightToLeftInternal) + { + rcNextRows.X += rowsWidth; + } + rcNextRows.Width -= rowsWidth; + if (rcBelowRows.Height > 0) + { + rcNextRows.Height = gridBounds.Height - rcBelowRows.Height; + } + if (rcNextRows.Width > 0 && rcNextRows.Height > 0) + { + graphics.FillRectangle(this.backgroundBrush, rcNextRows); + } + } + + private void PaintBorder(Graphics g, Rectangle clipRect, Rectangle bounds) + { + Debug.Assert(bounds.Left == 0); + Debug.Assert(bounds.Top == 0); + if (this.BorderStyle == BorderStyle.None) + { + return; + } + bool paintingNeeded = false; + int borderWidth = this.BorderWidth; + // Does the clipRect intersect with the top edge? + Rectangle edge = new Rectangle(0, 0, bounds.Width, borderWidth); + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the bottom edge? + edge.Y = bounds.Height - borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the left edge? + edge.Y = 0; + edge.Height = bounds.Height; + edge.Width = borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + if (!paintingNeeded) + { + // Does the clipRect intersect with the right edge? + edge.X = bounds.Width - borderWidth; + paintingNeeded = clipRect.IntersectsWith(edge); + } + } + } + + if (paintingNeeded) + { + if (this.BorderStyle == BorderStyle.Fixed3D) + { + if (Application.RenderWithVisualStyles) + { + Pen pen = GetCachedPen(VisualStyleInformation.TextControlBorder); + g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1)); + } + else + { + ControlPaint.DrawBorder3D(g, bounds, Border3DStyle.Sunken); + } + } + else if (this.BorderStyle == BorderStyle.FixedSingle) + { + Pen pen = GetCachedPen(SystemColors.ControlText); + g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1)); + } + else + { + Debug.Fail("DataGridView.PaintBorder - Unexpected BorderStyle value."); + } + } + } + + private void PaintColumnHeaders(Graphics g, Rectangle clipBounds, bool singleBorderAdded) + { + if (g.IsVisible(this.layout.ColumnHeaders)) + { + Rectangle bandBounds, cellBounds; + bandBounds = cellBounds = this.layout.ColumnHeaders; + bandBounds.Height = cellBounds.Height = this.columnHeadersHeight; + int cx = 0; + bool isFirstDisplayedColumn = true, isLastVisibleColumn = false; + DataGridViewCell cell; + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + DataGridViewColumn dataGridViewColumnNext = null; + + // first paint the visible frozen columns + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + cell = dataGridViewColumn.HeaderCell; + cellBounds.Width = dataGridViewColumn.Thickness; + if (singleBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (this.RightToLeftInternal) + { + cellBounds.X = bandBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = bandBounds.X + cx; + } + + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + + dataGridViewColumnNext = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + if (dataGridViewColumnNext == null) + { + isLastVisibleColumn = (this.displayedBandsInfo.FirstDisplayedScrollingCol < 0); + } + + dgvabsEffective = AdjustColumnHeaderBorderStyle(this.AdvancedColumnHeadersBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + isFirstDisplayedColumn, isLastVisibleColumn); + + // Microsoft: should paintSelectionBackground be dev-settable? + cell.PaintWork(g, + clipBounds, + cellBounds, + -1, + dataGridViewColumn.State, + inheritedCellStyle, + dgvabsEffective, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + + cx += cellBounds.Width; + if (cx >= bandBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + // then paint the visible scrolling ones + Rectangle scrollingBounds = bandBounds; + if (!this.RightToLeftInternal) + { + scrollingBounds.X -= this.negOffset; + } + scrollingBounds.Width += this.negOffset; + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0 && cx < scrollingBounds.Width) + { + Region clipRegion = null; + if (this.negOffset > 0) + { + clipRegion = g.Clip; + Rectangle rowRect = bandBounds; + if (!this.RightToLeftInternal) + { + rowRect.X += cx; + } + rowRect.Width -= cx; + g.SetClip(rowRect); + } + + dataGridViewColumn = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + while (dataGridViewColumn != null) + { + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + cell = dataGridViewColumn.HeaderCell; + cellBounds.Width = dataGridViewColumn.Thickness; + if (singleBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (this.RightToLeftInternal) + { + cellBounds.X = scrollingBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = scrollingBounds.X + cx; + } + + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + + dataGridViewColumnNext = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + isLastVisibleColumn = (dataGridViewColumnNext == null); + + dgvabsEffective = AdjustColumnHeaderBorderStyle(this.AdvancedColumnHeadersBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + isFirstDisplayedColumn, isLastVisibleColumn); + + cell.PaintWork(g, + clipBounds, + cellBounds, + -1, + dataGridViewColumn.State, + inheritedCellStyle, + dgvabsEffective, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + + cx += cellBounds.Width; + if (cx >= scrollingBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + if (this.negOffset > 0) + { + Debug.Assert(clipRegion != null); + g.Clip = clipRegion; + clipRegion.Dispose(); + } + } + } + } + + private void PaintGrid(Graphics g, + Rectangle gridBounds, + Rectangle clipRect, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded) + { + Rectangle rc = gridBounds; + + if (this.layout.TopLeftHeader.Width > 0 && + (clipRect.IntersectsWith(this.layout.TopLeftHeader) || this.lastHeaderShadow != -1)) + { + if (this.Columns.Count > 0 || this.Rows.Count > 0) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(this.layout.TopLeftHeader); + PaintTopLeftHeaderCell(g); + g.Clip = clipRegion; + } + } + } + + if (this.layout.ColumnHeadersVisible) + { + Rectangle columnHeadersClip = new Rectangle(); + columnHeadersClip = this.layout.ColumnHeaders; + if (singleVerticalBorderAdded) + { + columnHeadersClip.Width++; + } + if (clipRect.IntersectsWith(columnHeadersClip) || this.lastHeaderShadow != -1) + { + using (Region clipRegion = g.Clip) + { + g.SetClip(columnHeadersClip); + PaintColumnHeaders(g, columnHeadersClip, singleVerticalBorderAdded); + g.Clip = clipRegion; + } + } + int columnHeadersHeight = this.layout.ColumnHeaders.Height; + rc.Y += columnHeadersHeight; + rc.Height -= columnHeadersHeight; + + if (this.lastHeaderShadow != -1) + { + DrawColHeaderShadow(g, this.lastHeaderShadow); + } + } + + if (rc.Height > 0) + { + PaintRows(g, rc, clipRect, /*singleVerticalBorderAdded, */ singleHorizontalBorderAdded); + } + + if (this.currentRowSplitBar != -1) + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackColHeadersResize] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowResize]); + DrawRowSplitBar(this.currentRowSplitBar); + } + else if (this.currentColSplitBar != -1) + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowHeadersResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColResize] || + this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize]); + DrawColSplitBar(this.currentColSplitBar); + } + } + + private void PaintRows(Graphics g, + Rectangle boundingRect, + Rectangle clipRect, + /*bool singleVerticalBorderAdded,*/ + bool singleHorizontalBorderAdded) + { + int cy = 0; + Rectangle rowBounds; + DataGridViewRow dataGridViewRow; + bool isFirstDisplayedRow = true; + int indexTmp, indexTmpNext; + + // paint visible none-scrolling rows + indexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (indexTmp != -1) + { + rowBounds = boundingRect; + + // Dev 10 Bug #434494 - DataGridView AutoSizeRowsMode does not work properly after column sort + // Should unshared the row and set the thickness to a perfect value + // every time user scroll to display the specific row. + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + this.Rows.SharedRow(indexTmp).CachedThickness = rowHeight; + AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + } + + rowBounds.Height = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + rowBounds.Height++; + } + rowBounds.Y = boundingRect.Y + cy; + + indexTmpNext = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + + if (clipRect.IntersectsWith(rowBounds)) + { + dataGridViewRow = this.Rows.SharedRow(indexTmp); + dataGridViewRow.Paint(g, + clipRect, + rowBounds, + indexTmp, + this.Rows.GetRowState(indexTmp), + isFirstDisplayedRow, + (indexTmpNext == -1) && (this.displayedBandsInfo.FirstDisplayedScrollingRow == -1)); + } + cy += rowBounds.Height; + if (cy >= boundingRect.Height) + { + break; + } + indexTmp = indexTmpNext; + isFirstDisplayedRow = false; + } + + // paint scrolling rows + if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0 && cy < boundingRect.Height) + { + indexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0); + Debug.Assert((this.Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0); + while (indexTmp != -1) + { + rowBounds = boundingRect; + + // Dev 10 Bug #434494 - DataGridView AutoSizeRowsMode does not work properly after column sort + // Should unshared the row and set the thickness to a perfect value + // every time user scroll to display the specific row. + DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode; + // Auto size row if needed + if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None) + { + // this call may unshare the row. + int rowHeight = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + this.Rows.SharedRow(indexTmp).CachedThickness = rowHeight; + AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(this.autoSizeRowsMode), false /*fixedWidth*/, true /*internalAutosizing*/); + } + + rowBounds.Height = this.Rows.SharedRow(indexTmp).GetHeight(indexTmp); + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + rowBounds.Height++; + } + rowBounds.Y = boundingRect.Y + cy; + + indexTmpNext = this.Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible); + + if (clipRect.IntersectsWith(rowBounds)) + { + dataGridViewRow = this.Rows.SharedRow(indexTmp); + dataGridViewRow.Paint(g, + clipRect, + rowBounds, + indexTmp, + this.Rows.GetRowState(indexTmp), + isFirstDisplayedRow, + indexTmpNext == -1); + } + cy += rowBounds.Height; + if (cy >= boundingRect.Height) + { + break; + } + indexTmp = indexTmpNext; + isFirstDisplayedRow = false; + } + } + } + + private void PaintTopLeftHeaderCell(Graphics g) + { + if (g.IsVisible(this.layout.TopLeftHeader)) + { + DataGridViewCell cell = this.TopLeftHeaderCell; + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell); + Rectangle cellBounds = this.layout.TopLeftHeader; + cellBounds.Width = this.rowHeadersWidth; + cellBounds.Height = this.columnHeadersHeight; + // Microsoft: Should paintSelectionBackground be dev-settable? + cell.PaintWork(g, + this.layout.TopLeftHeader, + cellBounds, + -1, + cell.State, + inheritedCellStyle, + this.AdjustedTopLeftHeaderBorderStyle, + DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground); + } + } + + private void PerformLayoutPrivate(bool useRowShortcut, + bool computeVisibleRows, + bool invalidInAdjustFillingColumns, + bool repositionEditingControl) + { + this.inPerformLayoutCount++; + try + { + if (invalidInAdjustFillingColumns && this.InAdjustFillingColumns) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotAlterAutoFillColumnParameter)); + } + + if (this.IsHandleCreated) + { + bool columnsAdjusted = false; + if (useRowShortcut) + { + ComputeLayoutShortcut(computeVisibleRows); + } + else + { + columnsAdjusted = ComputeLayout(); + } + FlushDisplayedChanged(); + if (columnsAdjusted && this.inPerformLayoutCount < 3) + { + // Some columns were auto-filled, the rows and column headers may need to be autosized. + if ((((DataGridViewAutoSizeRowsModeInternal)this.autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0) + { + AdjustShrinkingRows(this.autoSizeRowsMode, true /*fixedWidth*/, true /*internalAutosizing*/); + } + if (this.ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize) + { + AutoResizeColumnHeadersHeight(true /*fixedRowHeadersWidth*/, true /*fixedColumnWidth*/); + } + } + if (repositionEditingControl && this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, false /*setSize*/, false /*setFocus*/); + } + } + else + { + this.displayedBandsInfo.FirstDisplayedFrozenCol = -1; + this.displayedBandsInfo.FirstDisplayedFrozenRow = -1; + this.displayedBandsInfo.FirstDisplayedScrollingRow = -1; + this.displayedBandsInfo.FirstDisplayedScrollingCol = -1; + this.displayedBandsInfo.NumDisplayedFrozenRows = 0; + this.displayedBandsInfo.NumDisplayedFrozenCols = 0; + this.displayedBandsInfo.NumDisplayedScrollingRows = 0; + this.displayedBandsInfo.NumDisplayedScrollingCols = 0; + this.displayedBandsInfo.NumTotallyDisplayedFrozenRows = 0; + this.displayedBandsInfo.NumTotallyDisplayedScrollingRows = 0; + this.displayedBandsInfo.LastDisplayedScrollingRow = -1; + this.displayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; + if (this.layout != null) + { + this.layout.dirty = true; + } + } + } + finally + { + this.inPerformLayoutCount--; + Debug.Assert(this.inPerformLayoutCount >= 0); + } + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + private void PopulateNewRowWithDefaultValues() + { + if (this.newRowIndex != -1) + { + DataGridViewRow newRow = this.Rows.SharedRow(this.newRowIndex); + DataGridViewCellCollection newRowCells = newRow.Cells; + foreach (DataGridViewCell dataGridViewCell in newRowCells) + { + if (dataGridViewCell.DefaultNewRowValue != null) + { + newRow = this.Rows[this.newRowIndex]; // unshare the 'new row'. + newRowCells = newRow.Cells; + break; + } + } + foreach (DataGridViewCell dataGridViewCell in newRowCells) + { + dataGridViewCell.SetValueInternal(this.newRowIndex, dataGridViewCell.DefaultNewRowValue); + } + } + } + + private void PositionEditingControl(bool setLocation, bool setSize, bool setFocus) + { + Debug.Assert(this.editingControl != null); + + if (!this.IsHandleCreated) + { + return; + } + + #if DEBUG + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + Debug.Assert(dataGridViewCell.ColumnIndex == this.ptCurrentCell.X); + Debug.Assert(dataGridViewCell.RowIndex == this.ptCurrentCell.Y || dataGridViewCell.RowIndex == -1); + #endif + + Rectangle editingZone = this.layout.Data; + if (editingZone.Width == 0 || editingZone.Height == 0) + { + return; + } + + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = true; + + try + { + int leftEdge = GetColumnXFromIndex(this.ptCurrentCell.X); + if (this.RightToLeftInternal) + { + leftEdge -= this.Columns[this.ptCurrentCell.X].Width-1; + } + Rectangle cellBounds = new Rectangle(leftEdge, GetRowYFromIndex(this.ptCurrentCell.Y), + this.Columns[this.ptCurrentCell.X].Width, this.Rows.SharedRow(this.ptCurrentCell.Y).GetHeight(this.ptCurrentCell.Y)); + Rectangle cellClip = cellBounds; + // Need to clip the zones of the frozen columns and rows and headers. + if (!this.Columns[this.ptCurrentCell.X].Frozen) + { + int totalVisibleFrozenWidth = this.Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (!this.RightToLeftInternal) + { + editingZone.X += totalVisibleFrozenWidth; + } + editingZone.Width = Math.Max(0, editingZone.Width - totalVisibleFrozenWidth); + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + { + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + editingZone.Y += totalVisibleFrozenHeight; + } + cellClip.Intersect(editingZone); + + if (cellClip.Width == 0 || cellClip.Height == 0) + { + // we cannot simply make the control invisible because we want it to keep the focus. + // (and Control::CanFocus returns false if the control is not visible). + // So we place the editing control to the right of the DataGridView. + Debug.Assert(this.editingControl != null); + this.editingPanel.Location = new Point(this.Width + 1, 0); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] = true; + } + else + { + bool singleVerticalBorderAdded = this.SingleVerticalBorderAdded; + bool singleHorizontalBorderAdded = this.SingleHorizontalBorderAdded; + bool isFirstDisplayedColumn = this.FirstDisplayedColumnIndex == this.ptCurrentCell.X; + bool isFirstDisplayedRow = this.FirstDisplayedRowIndex == this.ptCurrentCell.Y; + + if (singleVerticalBorderAdded && isFirstDisplayedColumn) + { + if (!this.RightToLeftInternal) + { + cellBounds.X--; + cellClip.X--; + } + cellBounds.Width++; + cellClip.Width++; + } + if (singleHorizontalBorderAdded && isFirstDisplayedRow) + { + cellBounds.Y--; + cellClip.Y--; + cellBounds.Height++; + cellClip.Height++; + } + + this.CurrentCellInternal.PositionEditingControl( + setLocation || this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden], + setSize || this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden], + cellBounds, cellClip, this.InheritedEditingCellStyle, + singleVerticalBorderAdded, singleHorizontalBorderAdded, + isFirstDisplayedColumn, isFirstDisplayedRow); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlHidden] = false; + } + this.editingPanel.Visible = true; + if (setFocus) + { + CorrectFocus(false /*onlyIfGridHasFocus*/); + } + } + finally + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_editingControlChanging] = false; + } + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessAKey(Keys keyData) + { + if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control && + this.MultiSelect) + { + SelectAll(); + return true; + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessDeleteKey(Keys keyData) + { + if (this.AllowUserToDeleteRowsInternal) + { + if (this.editingControl != null) + { + // editing control gets a chance to handle the Delete key first + return false; + } + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int remainingSelectedRows = 0; + try + { + this.selectedBandSnapshotIndexes = new DataGridViewIntLinkedList(this.selectedBandIndexes); + while (this.selectedBandSnapshotIndexes.Count > remainingSelectedRows) + { + int rowIndex = this.selectedBandSnapshotIndexes[remainingSelectedRows]; + Debug.Assert(rowIndex >= 0); + if (rowIndex == this.newRowIndex || rowIndex >= this.Rows.Count) + { + remainingSelectedRows++; + } + else + { + DataGridViewRowCancelEventArgs dgvrce = new DataGridViewRowCancelEventArgs(this.Rows[rowIndex]); + OnUserDeletingRow(dgvrce); + if (!dgvrce.Cancel) + { + DataGridViewRow dataGridViewRow = this.Rows[rowIndex]; + if (this.DataSource != null) + { + int dataGridRowsCount = this.Rows.Count; +#if DEBUG + int dataGridViewRowsCount = this.Rows.Count; // the number of rows in the dataGridView row collection not counting the AddNewRow + int rowCount = this.dataConnection.CurrencyManager.List.Count; + if (this.AllowUserToAddRowsInternal ) + { + if (this.newRowIndex < rowCount) + { + // the user did not type inside the 'add new row' + Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync in AddNewTransaction when the user did not type in the 'add new row'"); + } + else + { + dataGridViewRowsCount --; + } + } + + Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync"); +#endif + DataGridViewDataErrorEventArgs dgvdee = null; + try + { + this.DataConnection.DeleteRow(rowIndex); + } + catch (Exception exception) + { + if (ClientUtils.IsCriticalException(exception)) + { + throw; + } + // this is tricky. + // the back-end threw an exception. At that stage, we did not delete the dataGridView row + // from our collection of dataGridView rows. + // So all we do is to throw the exception if the user wants. Otherwise we don't do anything. + dgvdee = new DataGridViewDataErrorEventArgs(exception, + -1, + rowIndex, + // null, + // null, + DataGridViewDataErrorContexts.RowDeletion); + OnDataErrorInternal(dgvdee); + + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + else + { + remainingSelectedRows++; + } + } + + if (dataGridRowsCount != this.Rows.Count) + { + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + } + else if (dgvdee == null) + { + remainingSelectedRows++; + } + } + else + { + this.Rows.RemoveAtInternal(rowIndex, false /*force*/); + Debug.Assert(dataGridViewRow.Index == -1); + DataGridViewRowEventArgs dgvre = new DataGridViewRowEventArgs(dataGridViewRow); + OnUserDeletedRow(dgvre); + } + } + else + { + remainingSelectedRows++; + } + } + } + } + finally + { + this.selectedBandSnapshotIndexes = null; + } + return true; + } + } + return false; + } + + /// + /// + /// + /// Gets or sets a value that indicates whether a key should be processed + /// further. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) + { + Keys key = (keyData & Keys.KeyCode); + + if (key == Keys.Enter) + { + if (ProcessEnterKey(keyData)) + { + return true; + } + } + else if (key == Keys.Escape) + { + bool keyEffective = this.IsEscapeKeyEffective; + bool ret = base.ProcessDialogKey(keyData); + if (!keyEffective) + { + // This call may perform Click of Cancel button of form. + if (this.Focused) + { + // Make sure the current cell is in editing mode if needed. + if (this.ptCurrentCell.X > -1 && + !this.IsCurrentCellInEditMode && + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null))) + { + BeginEditInternal(true /*selectAll*/); + } + } + } + return ret; + } + else if (key == Keys.D0 || key == Keys.NumPad0) + { + if (ProcessZeroKey(keyData)) + { + return true; + } + } + else if (key == Keys.C || key == Keys.Insert) + { + if (ProcessInsertKey(keyData)) + { + return true; + } + } + else if (key == Keys.Tab) + { + IntSecurity.AllWindows.Demand(); + if (ProcessTabKey(keyData)) + { + return true; + } + else + { + if (this.editingControl != null) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] = true; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl, + DataGridViewValidateCellInternal.Always, + true /*fireCellLeave*/, + true /*fireCellEnter*/, + true /*fireRowLeave*/, + true /*fireRowEnter*/, + true /*fireLeave*/, + false /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return true; + } + } + + keyData &= ~Keys.Control; + bool ret = false; + + // SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission, + // we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability + // indirectly. + // + IntSecurity.ModifyFocus.Assert(); + try + { + ret = base.ProcessDialogKey(keyData); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] && this.Focused) + { + // There was no other control to tab to. The CellLeave, RowLeave, Leave events were raised. + // Since the DataGridView control still has the focus, Enter, RowEnter, CellEnter events need to be raised. + OnEnter(EventArgs.Empty); + } + + return ret; + } + } + return base.ProcessDialogKey(keyData); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessDownKey(Keys keyData) + { + bool moved; + return ProcessDownKeyInternal(keyData, out moved); + } + + private bool ProcessDownKeyInternal(Keys keyData, out bool moved) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || lastVisibleRowIndex == -1) + { + moved = false; + return false; + } + int nextVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + nextVisibleRowIndex = this.Rows.GetNextRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + moved = true; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell + // the list under the DataGridView changes. + // In this case set moved to false so the users that call ProcessDownKey + // will commit the data. + // See vsWhidbey: 325296. + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + //SelectCellRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, lastVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptCurrentCell.X == -1 || this.ptAnchorCell.X == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, nextVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true /*forCurrentCellChange*/)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, + nextVisibleRowIndex, + true /*setAnchorCellAddress*/, + false /*validateCurrentCell*/, + false /*throughMouseClick*/); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.Y == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + if (nextVisibleRowIndex >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, nextVisibleRowIndex, true); + } + else + { + SelectRowRange(nextVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + ClearSelection(); + SetSelectedRowCore(nextVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1 || this.ptCurrentCell.Y == -1 || + IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, lastVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, false, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + moved = false; + return true; + } + if (nextVisibleRowIndex >= this.ptAnchorCell.Y) + { + SelectRowRange(this.ptAnchorCell.Y, nextVisibleRowIndex, true); + } + else + { + SelectRowRange(nextVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextVisibleRowIndex, true); + } + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, this.ptAnchorCell.Y, this.ptCurrentCell.X, nextVisibleRowIndex, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + moved = false; + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, !this.MultiSelect, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(nextVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, lastVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + else + { + if (nextVisibleRowIndex == -1) + { + moved = false; + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, nextVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex)) + { + moved = false; + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextVisibleRowIndex, true, false, false); + if (!success) + { + moved = false; + } + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessEndKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int lastVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(this.ptCurrentCell.Y, lastVisibleRowIndex, true); + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(lastVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + else + { + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, lastVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.Y >= 0) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + SelectRowRange(this.ptAnchorCell.Y, lastVisibleRowIndex, true); + } + else + { + SetSelectedRowCore(lastVisibleRowIndex, true); + } + + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedRowCore(lastVisibleRowIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessEnterKey(Keys keyData) + { + // commitRow is commented out for Dev10 bug 473789. + // When Enter is pressed, no matter Ctrl is also pressed or not, + // changes in a cell should be commited. + // Therefore, commitRow should be always true, and useless here. + bool moved = false, ret = true;//, commitRow = true; + if ((keyData & Keys.Control) == 0) + { + // Enter behaves like down arrow - it commits the potential editing and goes down one cell. + // commitRow = false; + keyData &= ~Keys.Shift; + ret = ProcessDownKeyInternal(keyData, out moved); + } + + if (!moved) + { + DataGridViewCell dataGridViewCurrentCell = null; + // Try to commit the potential editing + if (this.EditMode == DataGridViewEditMode.EditOnEnter) + { + if (this.ptCurrentCell.X != -1) + { + dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewDataErrorEventArgs dgvdee = CommitEdit(ref dataGridViewCurrentCell, + DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.WhenChanged, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/); + if (null != dgvdee) + { + if (dgvdee.ThrowException) + { + throw dgvdee.Exception; + } + } + } + } + else + { + EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit, + DataGridViewValidateCellInternal.WhenChanged /*validateCell*/, + false /*fireCellLeave*/, + false /*fireCellEnter*/, + false /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + true /*keepFocus*/, + true /*resetCurrentCell unused here*/, + true /*resetAnchorCell unused here*/); + } + if (/*commitRow && */this.IsCurrentRowDirty) + { + dataGridViewCurrentCell = null; + int columnIndex = this.ptCurrentCell.X; + int rowIndex = this.ptCurrentCell.Y; + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return ret; + } + if (!OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex)) + { + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return ret; + } + OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex); + } + } + } + return ret; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessEscapeKey(Keys keyData) + { + if (this.IsEscapeKeyEffective) + { + if (this.IsMouseOperationActive()) + { + ResetTrackingState(); + } + else + { + CancelEdit(true /*endEdit, DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration*/); + } + return true; + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") // Derived controls may need keyData. + ] + protected bool ProcessF2Key(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && + !this.IsCurrentCellInEditMode && + ModifierKeys == 0) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + Debug.Assert(this.CurrentCellInternal != null); + Debug.Assert(this.EditMode != DataGridViewEditMode.EditOnEnter || + (IsSharedCellReadOnly(this.CurrentCellInternal, this.ptCurrentCell.Y) || !ColumnEditable(this.ptCurrentCell.X))); + if (ColumnEditable(this.ptCurrentCell.X) && + !IsSharedCellReadOnly(this.CurrentCellInternal, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2 || + this.EditMode == DataGridViewEditMode.EditOnF2)) + { + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + BeginEditInternal(this.EditMode == DataGridViewEditMode.EditOnF2 /*selectAll*/); + return true; + } + } + return false; + } + + /// + /// + /// Sorts the current column. + /// 'UseLegacyAccessibilityFeatures2' accessibility switch + /// should be set to false to enable the feature. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected bool ProcessF3Key(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && AccessibilityImprovements.Level2) + { + DataGridViewColumn dataGridViewColumn = Columns[this.ptCurrentCell.X]; + if (dataGridViewColumn != null && CanSort(dataGridViewColumn)) + { + ListSortDirection listSortDirection = this.SortedColumn == dataGridViewColumn && this.SortOrder == SortOrder.Ascending ? + ListSortDirection.Descending : ListSortDirection.Ascending; + + this.Sort(dataGridViewColumn, listSortDirection); + return true; + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessHomeKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptAnchorCell.X >= 0) + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + if (this.ptCurrentCell.Y > -1 && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + else + { + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + } + else + { + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + } + // Debug.Assert(success); + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect && this.ptCurrentCell.X >= 0) + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.ptCurrentCell.X >= 0 && this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == 0) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true)) + { + return true; + } + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessInsertKey(Keys keyData) + { + if (((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control || + ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == (Keys.Control | Keys.Shift) && (keyData & Keys.KeyCode) == Keys.C)) && + this.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable) + { + DataObject dataObject = GetClipboardContent(); + if (dataObject != null) + { + Clipboard.SetDataObject(dataObject); + return true; + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) + { + if (m.Msg == NativeMethods.WM_SYSKEYDOWN || m.Msg == NativeMethods.WM_KEYDOWN) + { + if (this.ptCurrentCell.X != -1) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (!this.IsCurrentCellInEditMode && + ColumnEditable(this.ptCurrentCell.X) && + !IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystroke || this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2)) + { + KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); + if (ke.KeyCode != Keys.ProcessKey || (int) m.LParam != 0x01) // Changing IME context does not trigger editing mode + { + Type editControlType = dataGridViewCell.EditType; + Type editingCellInterface = null; + if (editControlType == null) + { + // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell? + editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell"); + } + + if ((editControlType != null || editingCellInterface == null) && + dataGridViewCell.KeyEntersEditMode(ke)) + { + // Cell wants to go to edit mode + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + if (BeginEditInternal(!(ke.KeyCode == Keys.F2 && ModifierKeys == 0 && this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2) /*selectAll*/)) + { + // Forward the key message to the editing control if any + if (this.editingControl != null) + { + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] = true; + + return true; + } + } + } + } + } + } + } + else if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] && + (m.Msg == NativeMethods.WM_SYSCHAR || m.Msg == NativeMethods.WM_CHAR || m.Msg == NativeMethods.WM_IME_CHAR)) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_forwardCharMessage] = false; + if (this.editingControl != null) + { + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + return true; + } + } + return base.ProcessKeyEventArgs(ref m); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) + { + bool dataGridViewWantsInputKey; + KeyEventArgs ke = new KeyEventArgs((Keys)((int)m.WParam) | ModifierKeys); + + // Refactor the special keys into two parts. + // 1. Escape and Space exist in both WM_CHAR and WM_KEYDOWN, WM_KEYUP. + // 2. Other special keys do not exist in WM_CHAR message, and character code of WM_CHAR may have overlapped + // w/ some of the key code. (Like character code of lowcase "q" is 0x71, it's overlapped w/ Keys.F2). This + // may introduce problem when handling them. + if (m.Msg == NativeMethods.WM_CHAR) + { + switch (ke.KeyCode) + { + case Keys.Escape: + case Keys.Space: + dataGridViewWantsInputKey = true; + break; + + default: + dataGridViewWantsInputKey = false; + break; + } + } + else + { + switch (ke.KeyCode) + { + case Keys.Delete: + case Keys.Down: + case Keys.End: + case Keys.Enter: + case Keys.Escape: + case Keys.F2: + case Keys.F3: + case Keys.Home: + case Keys.Left: + case Keys.Next: + case Keys.Prior: + case Keys.Right: + case Keys.Space: + case Keys.Tab: + case Keys.Up: + dataGridViewWantsInputKey = true; + break; + + default: + dataGridViewWantsInputKey = false; + break; + } + } + + if (this.editingControl != null && (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN)) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey] = ((IDataGridViewEditingControl)this.editingControl).EditingControlWantsInputKey(ke.KeyData, dataGridViewWantsInputKey); + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey]) + { + return base.ProcessKeyPreview(ref m); + } + + if (dataGridViewWantsInputKey) + { + if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) + { + if (ProcessDataGridViewKey(ke)) + { + return true; + // Ctrl-Tab will be sent as a tab paired w/ a control on the KeyUp message + } + else + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_currentCellWantsInputKey] = true; + } + } + else + { + return true; + } + } + return base.ProcessKeyPreview(ref m); + } + + private bool? ProcessColumnResize(Keys keyData, int step) + { + if (AccessibilityImprovements.Level2 && (keyData & Keys.Alt) == Keys.Alt && this.AllowUserToResizeColumns && this.ptCurrentCell.X != -1) + { + if (this.currentColSplitBar == -1) + { + DataGridViewColumn dataGridViewColumn = Columns[this.ptCurrentCell.X]; + if (dataGridViewColumn != null && dataGridViewColumn.Resizable == DataGridViewTriState.True && + (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)) + { + BeginKeyboardColumnResize(this.ptCurrentCell.X); + return true; + } + return false; + } + else + { + int x = this.currentColSplitBar + step; + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackKeyboardColResize] && this.resizeClipRectangle.Contains(x, this.resizeClipRectangle.Top)) + { + MoveRowHeadersOrColumnResize(x); + return true; + } + return false; + } + } + + return null; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessLeftKey(Keys keyData) + { + if (this.RightToLeftInternal) + { + return ProcessRightKeyPrivate(keyData); + } + else + { + return ProcessLeftKeyPrivate(keyData); + } + } + + private bool ProcessLeftKeyPrivate(Keys keyData) + { + bool? resizeResult = this.ProcessColumnResize(keyData, -this.keyboardResizeStep); + if (resizeResult.HasValue) + { + return resizeResult.Value; + } + + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int previousVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + previousVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + //SelectCellUnorderedRange(previousVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, previousVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, previousVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, previousVisibleColumnIndex, true); + } + else + { + SelectColumnRange(previousVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(previousVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, previousVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, previousVisibleColumnIndex, true); + } + else + { + SelectColumnRange(previousVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(previousVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, previousVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(previousVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (previousVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(previousVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(previousVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + // Ctrl Left <==> Home + // Shift Ctrl Left <==> Shift Home + private bool ProcessLeftMost(bool shift, int firstVisibleColumnIndex, int firstVisibleRowIndex) + { + bool success; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + //SelectCellRange(firstVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.Y == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SelectColumnRange(firstVisibleColumnIndex, this.ptAnchorCell.X, true); + } + else + { + //ClearSelection(); + //SelectCellRange(firstVisibleColumnIndex, this.ptCurrentCell.Y, this.ptAnchorCell.X, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, firstVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || + IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(firstVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessNextKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + if (firstVisibleColumnIndex == -1) + { + return false; + } + int nextScreenVisibleRowIndexTmp, nextScreenVisibleRowIndex = -1, jumpRows = 0; + if (this.ptCurrentCell.Y == -1) + { + nextScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (nextScreenVisibleRowIndex == -1) + { + return false; + } + } + else + { + nextScreenVisibleRowIndex = this.ptCurrentCell.Y; + } + + if ((this.Rows.GetRowState(nextScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > 0) + { + int firstDisplayedScrollingRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(firstDisplayedScrollingRowIndex != -1); + if (!ScrollIntoView(this.ptCurrentCell.X == -1 ? firstVisibleColumnIndex : this.ptCurrentCell.X, + firstDisplayedScrollingRowIndex, true)) + { + return true; + } + jumpRows = this.Rows.GetRowCount(DataGridViewElementStates.Visible, + this.ptCurrentCell.Y, + firstDisplayedScrollingRowIndex)-1; + } + else + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + } + } + jumpRows += this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + + nextScreenVisibleRowIndexTmp = nextScreenVisibleRowIndex; + Debug.Assert(nextScreenVisibleRowIndexTmp != -1); + if (jumpRows == 0) + { + jumpRows = 1; + } + while (jumpRows > 0 && nextScreenVisibleRowIndexTmp != -1) + { + nextScreenVisibleRowIndexTmp = this.Rows.GetNextRow(nextScreenVisibleRowIndex, DataGridViewElementStates.Visible); + if (nextScreenVisibleRowIndexTmp != -1) + { + nextScreenVisibleRowIndex = nextScreenVisibleRowIndexTmp; + jumpRows--; + } + } + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + SetSelectedCellCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullRowSelect: + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullColumnSelect: + SetSelectedColumnCore(firstVisibleColumnIndex, true); + break; + } + success = ScrollIntoView(firstVisibleColumnIndex, nextScreenVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, nextScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + return true; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.Y == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextScreenVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false); + Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y < nextScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, nextScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(nextScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.ptAnchorCell.Y < nextScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, nextScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(nextScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, nextScreenVisibleRowIndex); + } + } + else + { + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(nextScreenVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, nextScreenVisibleRowIndex, true, false, false); + // Debug.Assert(success); + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessPriorKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + if (firstVisibleColumnIndex == -1) + { + return false; + } + int previousScreenVisibleRowIndexTmp, previousScreenVisibleRowIndex = -1; + if (this.ptCurrentCell.Y == -1) + { + previousScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (previousScreenVisibleRowIndex == -1) + { + return false; + } + } + else + { + previousScreenVisibleRowIndex = this.ptCurrentCell.Y; + } + + int jumpRows; + if ((this.Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedFrozenRows; + } + else + { + jumpRows = this.displayedBandsInfo.NumTotallyDisplayedScrollingRows; + } + if (jumpRows == 0) + { + jumpRows = 1; + } + previousScreenVisibleRowIndexTmp = previousScreenVisibleRowIndex; + Debug.Assert(previousScreenVisibleRowIndexTmp != -1); + while (jumpRows > 0 && previousScreenVisibleRowIndexTmp != -1) + { + previousScreenVisibleRowIndexTmp = this.Rows.GetPreviousRow(previousScreenVisibleRowIndex, DataGridViewElementStates.Visible); + if (previousScreenVisibleRowIndexTmp != -1) + { + previousScreenVisibleRowIndex = previousScreenVisibleRowIndexTmp; + } + jumpRows--; + } + + if ((this.Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0) + { + // Make sure the first scrolling row is visible if any + int firstDisplayedScrollingRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + if (firstDisplayedScrollingRowIndex != -1 && + !ScrollIntoView(this.ptCurrentCell.X == -1 ? firstVisibleColumnIndex : this.ptCurrentCell.X, + firstDisplayedScrollingRowIndex, true)) + { + return true; + } + // Also, first visible frozen row should become current one - there is no reason to jump to another frozen row. + previousScreenVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + } + Debug.Assert(previousScreenVisibleRowIndex != -1); + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + SetSelectedCellCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullRowSelect: + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + break; + case DataGridViewSelectionMode.FullColumnSelect: + SetSelectedColumnCore(firstVisibleColumnIndex, true); + break; + } + success = ScrollIntoView(firstVisibleColumnIndex, previousScreenVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, previousScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true, false, false); + // Debug.Assert(success); + return true; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousScreenVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y < previousScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, previousScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(previousScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + if ((keyData & Keys.Shift) == Keys.Shift && this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.ptAnchorCell.Y < previousScreenVisibleRowIndex) + { + SelectRowRange(this.ptAnchorCell.Y, previousScreenVisibleRowIndex, true); + } + else + { + SelectRowRange(previousScreenVisibleRowIndex, this.ptAnchorCell.Y, true); + } + } + else + { + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousScreenVisibleRowIndex); + } + } + else + { + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(previousScreenVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (!ScrollIntoView(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousScreenVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessRightKey(Keys keyData) + { + if (this.RightToLeftInternal) + { + return ProcessLeftKeyPrivate(keyData); + } + else + { + return ProcessRightKeyPrivate(keyData); + } + } + + private bool ProcessRightKeyPrivate(Keys keyData) + { + bool? resizeResult = this.ProcessColumnResize(keyData, this.keyboardResizeStep); + if (resizeResult.HasValue) + { + return resizeResult.Value; + } + + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + int lastVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int nextVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + nextVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.X == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, nextVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, nextVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, nextVisibleColumnIndex, true); + } + else + { + SelectColumnRange(nextVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(nextVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns.DisplayInOrder(this.ptAnchorCell.X, nextVisibleColumnIndex)) + { + SelectColumnRange(this.ptAnchorCell.X, nextVisibleColumnIndex, true); + } + else + { + SelectColumnRange(nextVisibleColumnIndex, this.ptAnchorCell.X, true); + } + } + else + { + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + } + else + { + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + //SelectCellUnorderedRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, nextVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, !this.MultiSelect, false, false); + Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(nextVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex); + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (nextVisibleColumnIndex == -1) + { + return true; + } + if (!ScrollIntoView(nextVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(nextVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + // Ctrl Right <==> End + // Shift Ctrl Right <==> Shift End + private bool ProcessRightMost(bool shift, int lastVisibleColumnIndex, int firstVisibleRowIndex) + { + bool success; + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.ptAnchorCell.X == -1) + { + return true; + } + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + if (this.ptAnchorCell.X == -1) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.X >= 0); + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.X >= 0); + if (this.ptAnchorCell.X == -1) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SelectColumnRange(this.ptAnchorCell.X, lastVisibleColumnIndex, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptAnchorCell.X, this.ptCurrentCell.Y, lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, lastVisibleColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, oldEdgeRowIndex); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, false, false, false); + Debug.Assert(success); + } + else + { + if (this.Columns[this.ptCurrentCell.X].Selected) + { + SetSelectedColumnCore(this.ptCurrentCell.X, false); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + if (this.Columns[this.ptCurrentCell.X].Selected) + { + ClearSelection(); + SetSelectedColumnCore(lastVisibleColumnIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true); + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + Debug.Assert(success); + } + else + { + if (!ScrollIntoView(lastVisibleColumnIndex, this.ptCurrentCell.Y, true)) + { + return true; + } + if (this.ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(lastVisibleColumnIndex, this.ptCurrentCell.Y, true, false, false); + Debug.Assert(success); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessSpaceKey(Keys keyData) + { + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && + this.ptCurrentCell.X != -1) + { + this.noSelectionChangeCount++; + bool switchedToBulkPaint = false; + if (this.selectedBandIndexes.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + // Same as clicking the column header cell + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != this.ptCurrentCell.X) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + RemoveIndividuallySelectedCells(); + if (!this.Columns[this.ptCurrentCell.X].Selected) + { + Debug.Assert(!this.selectedBandIndexes.Contains(this.ptCurrentCell.X)); + SetSelectedColumnCore(this.ptCurrentCell.X, true); + } + return true; + } + else if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + // Same as clicking the row header cell + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != this.ptCurrentCell.Y) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + RemoveIndividuallySelectedCells(); + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(!this.selectedBandIndexes.Contains(this.ptCurrentCell.Y)); + SetSelectedRowCore(this.ptCurrentCell.Y, true); + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + return false; + } + + /// + /// + /// + /// Gets a value indicating whether the Tab key should be processed. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected bool ProcessTabKey(Keys keyData) + { + if (this.StandardTab) + { + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell) + { + // Goto previous control + return false; + } + else + { + // Goto previous cell + return TabToPreviousCell(); + } + } + else + { + if (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell) + { + // Goto next control + return false; + } + else + { + // Goto next cell + return TabToNextCell(); + } + } + } + else + { + /* + if ((keyData & Keys.Shift) == Keys.Shift) + { + // Goto previous control + } + else + { + // Goto next control + } + */ + return false; + } + } + else + { + if ((keyData & Keys.Control) == Keys.Control) + { + /* + if ((keyData & Keys.Shift) == Keys.Shift) + { + // Goto previous control + } + else + { + // Goto next control + } + */ + return false; + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (!this.VisibleCellExists || this.CurrentCellIsFirstVisibleCell) + { + // Goto previous control + return false; + } + else + { + // Goto previous cell + return TabToPreviousCell(); + } + } + else + { + if (!this.VisibleCellExists || this.CurrentCellIsLastVisibleCell) + { + // Goto next control + return false; + } + else + { + // Goto next cell + return TabToNextCell(); + } + } + } + } + } + + /// + /// + /// + /// Processes keys for dataGridView navigation. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers") + ] + protected virtual bool ProcessDataGridViewKey(KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Tab: + { + return ProcessTabKey(e.KeyData); + } + case Keys.Up: + { + return ProcessUpKey(e.KeyData); + } + case Keys.Down: + { + return ProcessDownKey(e.KeyData); + } + case Keys.Next: + { + return ProcessNextKey(e.KeyData); + } + case Keys.Prior: + { + return ProcessPriorKey(e.KeyData); + } + case Keys.Left: + { + return ProcessLeftKey(e.KeyData); + } + case Keys.Right: + { + return ProcessRightKey(e.KeyData); + } + case Keys.F2: + { + return ProcessF2Key(e.KeyData); + } + case Keys.F3: + { + return ProcessF3Key(e.KeyData); + } + case Keys.Home: + { + return ProcessHomeKey(e.KeyData); + } + case Keys.D0: + case Keys.NumPad0: + { + return ProcessZeroKey(e.KeyData); + } + case Keys.Delete: + { + return ProcessDeleteKey(e.KeyData); + } + case Keys.End: + { + return ProcessEndKey(e.KeyData); + } + case Keys.Enter: + { + return ProcessEnterKey(e.KeyData); + } + case Keys.Escape: + { + return ProcessEscapeKey(e.KeyData); + } + case Keys.A: + { + return ProcessAKey(e.KeyData); + } + case Keys.C: + case Keys.Insert: + { + return ProcessInsertKey(e.KeyData); + } + case Keys.Space: + { + return ProcessSpaceKey(e.KeyData); + } + } + return false; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessUpKey(Keys keyData) + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int previousVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + previousVisibleRowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + //ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + //SelectCellRange(this.ptCurrentCell.X, firstVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + //SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if (this.MultiSelect) + { + Debug.Assert(this.ptAnchorCell.Y >= 0); + if (this.ptAnchorCell.Y == -1) + { + return true; + } + //SelectCellUnorderedRange(this.ptCurrentCell.X, previousVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullRowSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + Debug.Assert(this.ptAnchorCell.Y >= 0); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y >= previousVisibleRowIndex) + { + SelectRowRange(previousVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + SelectRowRange(this.ptAnchorCell.Y, previousVisibleRowIndex, true); + } + } + else + { + SetSelectedRowCore(previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(previousVisibleRowIndex, true); + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.RowHeaderSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (this.MultiSelect) + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptAnchorCell.X == -1 || this.ptCurrentCell.X == -1 || + IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + Debug.Assert(this.ptAnchorCell.Y >= 0); + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SelectRowRange(firstVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + //ClearSelection(); + //SelectCellRange(this.ptCurrentCell.X, firstVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, firstVisibleRowIndex); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, false, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + SetSelectedRowCore(this.ptCurrentCell.Y, false); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + SetSelectedCellCore(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, firstVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + else + { + if ((keyData & Keys.Shift) == Keys.Shift) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + if (this.MultiSelect) + { + if (this.ptAnchorCell.Y == -1) + { + return true; + } + if (this.ptAnchorCell.Y >= previousVisibleRowIndex) + { + SelectRowRange(previousVisibleRowIndex, this.ptAnchorCell.Y, true); + } + else + { + SelectRowRange(this.ptAnchorCell.Y, previousVisibleRowIndex, true); + } + } + SetSelectedRowCore(previousVisibleRowIndex, true); + } + else + { + if (this.MultiSelect) + { + //SelectCellUnorderedRange(this.ptCurrentCell.X, previousVisibleRowIndex, this.ptCurrentCell.X, this.ptAnchorCell.Y, true); + int oldEdgeColumnIndex = this.ptCurrentCell.X; + int oldEdgeRowIndex = this.ptCurrentCell.Y; + if (this.ptAnchorCell.Y == -1) + { + return true; + } + UpdateSelectedCellsBlock(this.ptAnchorCell.X, ref oldEdgeColumnIndex, oldEdgeColumnIndex, + this.ptAnchorCell.Y, ref oldEdgeRowIndex, previousVisibleRowIndex); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, !this.MultiSelect, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + if ((this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0) + { + ClearSelection(); + SetSelectedRowCore(previousVisibleRowIndex, true); + } + else + { + ClearSelection(); + SetSelectedCellCore(this.ptCurrentCell.X, previousVisibleRowIndex, true); + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + } + return true; + + case DataGridViewSelectionMode.FullColumnSelect: + if ((keyData & Keys.Control) == Keys.Control) + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (!ScrollIntoView(this.ptCurrentCell.X, firstVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + else + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + SetSelectedColumnCore(firstVisibleColumnIndex, true); + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + else + { + if (previousVisibleRowIndex == -1) + { + return true; + } + if (!ScrollIntoView(this.ptCurrentCell.X, previousVisibleRowIndex, true)) + { + return true; + } + if (this.ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(this.ptCurrentCell.X, previousVisibleRowIndex, true, false, false); + // Debug.Assert(success); + } + } + return true; + } + } + finally + { + this.NoSelectionChangeCount--; + } + return true; + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected bool ProcessZeroKey(Keys keyData) + { + if (this.ptCurrentCell.X != -1 && !this.IsCurrentCellInEditMode && ColumnEditable(this.ptCurrentCell.X)) + { + DataGridViewCell dataGridViewCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCell != null); + + if (!IsSharedCellReadOnly(dataGridViewCell, this.ptCurrentCell.Y) && + (this.EditMode == DataGridViewEditMode.EditOnKeystroke || this.EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2) && + dataGridViewCell.EditType != null) + { + bool success = ScrollIntoView(this.ptCurrentCell.X, this.ptCurrentCell.Y, false); + Debug.Assert(success); + if (!BeginEditInternal(false /*selectAll*/)) + { + return false; + } + } + } + + if ((keyData & (Keys.Alt | Keys.Shift | Keys.Control)) == Keys.Control && + this.IsCurrentCellInEditMode) + { + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + Debug.Assert(dataGridViewCurrentCell != null); + object nullValue = dataGridViewCurrentCell.GetInheritedStyle(null, this.ptCurrentCell.Y, false).NullValue; + if (nullValue == null || + (dataGridViewCurrentCell.FormattedValueType != null && dataGridViewCurrentCell.FormattedValueType.IsAssignableFrom(nullValue.GetType()))) + { + if (this.editingControl != null) + { + ((IDataGridViewEditingControl) this.editingControl).EditingControlFormattedValue = nullValue; + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = true; + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.EditingCellFormattedValue = nullValue; + dataGridViewEditingCell.EditingCellValueChanged = true; + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + } + NotifyCurrentCellDirty(true); + return true; + } + return false; + } + return false; + } + + private void PushAllowUserToAddRows() + { + if (this.AllowUserToAddRowsInternal) + { + if (this.Columns.Count > 0 && this.newRowIndex == -1) + { + AddNewRow(false); + } + } + else if (this.newRowIndex != -1) + { + // Delete the 'new' row + Debug.Assert(this.newRowIndex == this.Rows.Count - 1); + this.Rows.RemoveAtInternal(this.newRowIndex, false /*force*/); + } + } + + private bool PushFormattedValue(ref DataGridViewCell dataGridViewCurrentCell, object formattedValue, out Exception exception) + { + Debug.Assert(this.IsCurrentCellInEditMode); + + exception = null; + + DataGridViewCellStyle cellStyle = this.InheritedEditingCellStyle; + + DataGridViewCellParsingEventArgs dgvcpe = OnCellParsing(this.ptCurrentCell.Y, + this.ptCurrentCell.X, + formattedValue, + dataGridViewCurrentCell.ValueType, + cellStyle); + if (dgvcpe.ParsingApplied && + dgvcpe.Value != null && + dataGridViewCurrentCell.ValueType != null && + dataGridViewCurrentCell.ValueType.IsAssignableFrom(dgvcpe.Value.GetType())) + { + if (dataGridViewCurrentCell.RowIndex == -1) + { + dataGridViewCurrentCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unsharing the row before pushing the new value + } + return dataGridViewCurrentCell.SetValueInternal(this.ptCurrentCell.Y, dgvcpe.Value); + } + + object val; + try + { + val = dataGridViewCurrentCell.ParseFormattedValue(formattedValue, dgvcpe.InheritedCellStyle, null, null); + } + catch (Exception e) + { + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + exception = e; + return false; + } + if (dataGridViewCurrentCell.RowIndex == -1) + { + dataGridViewCurrentCell = this.Rows[this.ptCurrentCell.Y].Cells[this.ptCurrentCell.X]; // unsharing the row before pushing the new value + } + return dataGridViewCurrentCell.SetValueInternal(this.ptCurrentCell.Y, val); + } + + private void RecordCellMouseClick(DataGridViewCellMouseEventArgs dgvcme) + { + Debug.Assert(dgvcme.Clicks == 1); + this.lastMouseClickInfo.button = dgvcme.Button; + this.lastMouseClickInfo.timeStamp = DateTime.Now.Ticks; + this.lastMouseClickInfo.x = dgvcme.X; + this.lastMouseClickInfo.y = dgvcme.Y; + this.lastMouseClickInfo.col = dgvcme.ColumnIndex; + this.lastMouseClickInfo.row = dgvcme.RowIndex; + } + + private void RefreshColumnsAndRows() + { + this.Rows.ClearInternal(false /*recreateNewRow*/); + RefreshColumns(); + RefreshRows(true /*scrollIntoView*/); + } + + private void RefreshColumns() + { + // if AutoGenerateColumns == true then: + // 1. remove the columns which were previously bound + // 2. add new columns based on data source + // + + bool startUpdateInternal = this.Visible; + if (startUpdateInternal) + { + BeginUpdateInternal(); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns] = true; + try + { + DataGridViewColumnCollection dataGridViewCols = this.Columns; + DataGridViewColumn[] boundColumns = null; + + if (this.dataConnection != null) + { + boundColumns = this.dataConnection.GetCollectionOfBoundDataGridViewColumns(); + } + + if (this.AutoGenerateColumns) + { + AutoGenerateDataBoundColumns(boundColumns); + } + else + { + for (int j = 0; j < dataGridViewCols.Count; j ++) + { + // when we refresh the columns, always clear whatever binding information we had on the columns + dataGridViewCols[j].IsDataBoundInternal = false; + dataGridViewCols[j].BoundColumnIndex = -1; + dataGridViewCols[j].BoundColumnConverter = null; + // set up the columns which have DataPropertyName set to something + if (this.DataSource != null && dataGridViewCols[j].DataPropertyName.Length != 0) + { + MapDataGridViewColumnToDataBoundField(dataGridViewCols[j]); + } + } + } + + if (this.DataSource != null) + { + this.dataConnection.ApplySortingInformationFromBackEnd(); + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inRefreshColumns] = false; + + if (startUpdateInternal) + { + EndUpdateInternal(false); + Invalidate(true); + } + } + } + + /// + // Returns true for success, and false if an OnDataError event was raised and cancelled the operation (e.Cancel = true). + public bool RefreshEdit() + { + if (this.ptCurrentCell.X != -1 && this.IsCurrentCellInEditMode) + { + Debug.Assert(this.ptCurrentCell.Y != -1); + DataGridViewCell dataGridViewCurrentCell = this.CurrentCellInternal; + DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCurrentCell.GetInheritedStyle(null, this.ptCurrentCell.Y, true); + if (this.editingControl != null) + { + if (InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCurrentCell)) + { + if (((IDataGridViewEditingControl) this.editingControl).RepositionEditingControlOnValueChange) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + ((IDataGridViewEditingControl) this.editingControl).PrepareEditingControlForEdit(true /*selectAll*/); + ((IDataGridViewEditingControl) this.editingControl).EditingControlValueChanged = false; + this.IsCurrentCellDirtyInternal = false; + return true; + } + return false; + } + else + { + Debug.Assert(this.dataGridViewState1[DATAGRIDVIEWSTATE1_currentCellInEditMode]); + if (InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCurrentCell)) + { + IDataGridViewEditingCell dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell; + Debug.Assert(dataGridViewEditingCell != null); + dataGridViewEditingCell.PrepareEditingCellForEdit(true /*selectAll*/); + dataGridViewEditingCell.EditingCellValueChanged = false; + this.IsCurrentCellDirtyInternal = false; + return true; + } + return false; + } + } + return true; + } + + private void RefreshRows(bool scrollIntoView) + { + bool startBeginUpdate = this.Visible; + if (startBeginUpdate) + { + BeginUpdateInternal(); + } + + try + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = true; + } + + // Clear existing rows + this.Rows.ClearInternal(true /*recreateNewRow*/); + + // Add a row for each object in the data source + if (this.dataConnection != null && this.Columns.Count > 0) + { + IList list = this.dataConnection.List; + if (list != null && list.Count > 0) + { + int rowsCount = list.Count; + bool oldDoNotChangePositionInTheCurrencyManager = this.dataConnection.DoNotChangePositionInTheCurrencyManager; + + // scroll into view when we have a non-empty list + // and the layout is not dirty and we are not in a sort operation. + bool matchPositionInCurrencyManagerAfterRecreatingRows = !this.layout.dirty && !this.InSortOperation; + + if (matchPositionInCurrencyManagerAfterRecreatingRows) + { + // Prevent the data connection from changing position in the currency manager. + // But only if the data grid view will set the position in the currency manager after recreating the rows collection. + this.dataConnection.DoNotChangePositionInTheCurrencyManager = true; + } + try + { + this.Rows.AddInternal(this.RowTemplateClone); + Debug.Assert(list.Count == rowsCount); + if (rowsCount > 1) + { + this.Rows.AddCopiesInternal(0, rowsCount-1); + } + // Add selected Columns back in selectedBandIndexes + foreach(DataGridViewColumn column in Columns) + { + if (column.Selected && (!this.selectedBandIndexes.Contains(column.Index))) + { + this.selectedBandIndexes.Add(column.Index); + } + } + } + finally + { + this.dataConnection.DoNotChangePositionInTheCurrencyManager = oldDoNotChangePositionInTheCurrencyManager; + } + + if (matchPositionInCurrencyManagerAfterRecreatingRows) + { + dataConnection.MatchCurrencyManagerPosition(scrollIntoView, true /*clearSelection*/); + } + } + } + } + finally + { + if (startBeginUpdate) + { + EndUpdateInternal(false); + Invalidate(true); + } + } + } + + private void RealeaseMouse() + { + Cursor.ClipInternal = Rectangle.Empty; + this.CaptureInternal = false; + } + + private void RemoveIndividualReadOnlyCellsInColumn(int columnIndex) + { + int cellIndex = 0; + while (cellIndex < this.individualReadOnlyCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualReadOnlyCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + + private void RemoveIndividualReadOnlyCellsInRow(int rowIndex) + { + int cellIndex = 0; + while (cellIndex < this.individualReadOnlyCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualReadOnlyCells[cellIndex]; + if (dataGridViewCell.RowIndex == rowIndex) + { + SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, rowIndex, false); + } + else + { + cellIndex++; + } + } + } + + private void RemoveIndividuallySelectedCells() + { + Debug.Assert(this.noSelectionChangeCount > 0); + bool switchedToBulkPaint = false; + if (this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.individualSelectedCells.Count > 0) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void RemoveIndividuallySelectedCells(int columnIndexException, int rowIndexException) + { + Debug.Assert(this.noSelectionChangeCount > 0); + bool switchedToBulkPaint = false; + if (this.individualSelectedCells.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + while (this.individualSelectedCells.Count > 0) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + if (dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + while (this.individualSelectedCells.Count > 1) + { + dataGridViewCell = this.individualSelectedCells[1]; + Debug.Assert(dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException); + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + break; + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void RemoveIndividuallySelectedCellsInColumn(int columnIndex) + { + Debug.Assert(this.noSelectionChangeCount > 0); + int cellIndex = 0; + int cellCountInColumn = 0; + bool switchToBulkOperation = false; + DataGridViewCell dataGridViewCell; + + while (cellIndex < this.individualSelectedCells.Count) + { + dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + cellCountInColumn++; + if (cellCountInColumn > DATAGRIDVIEW_bulkPaintThreshold) + { + switchToBulkOperation = true; + break; + } + } + else + { + cellIndex++; + } + } + + if (switchToBulkOperation) + { + this.inBulkPaintCount++; + try + { + while (cellIndex < this.individualSelectedCells.Count) + { + dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.ColumnIndex == columnIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + finally + { + ExitBulkPaint(columnIndex, -1); + } + } + } + + private void RemoveIndividuallySelectedCellsInRow(int rowIndex) + { + Debug.Assert(this.noSelectionChangeCount > 0); + // Since there are typically not many columns in a row, we don't try to switch into a bulk operation + // as we do in RemoveIndividuallySelectedCellsInColumn. + int cellIndex = 0; + while (cellIndex < this.individualSelectedCells.Count) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells[cellIndex]; + if (dataGridViewCell.RowIndex == rowIndex) + { + SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false); + } + else + { + cellIndex++; + } + } + } + + // required by the Designer + private void ResetBackgroundColor() + { + this.BackgroundColor = DefaultBackgroundBrush.Color; + } + + // required by the Designer + private void ResetGridColor() + { + this.GridColor = DefaultGridColor; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetText() + { + base.ResetText(); + } + + /// + /// Re-initializes all tracking related state. + /// + private void ResetTrackingState() + { + if (this.IsKeyboardOperationActive()) + { + return; + } + if (this.horizScrollTimer != null && this.horizScrollTimer.Enabled) + { + this.horizScrollTimer.Enabled = false; + } + if (this.vertScrollTimer != null && this.vertScrollTimer.Enabled) + { + this.vertScrollTimer.Enabled = false; + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_mouseOperationMask] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackColSelect] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] = false; + this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] = false; + this.trackColumn = -1; + this.trackRow = -1; + this.ptMouseDownCell.X = -2; + this.ptMouseDownCell.Y = -2; + + if (this.currentRowSplitBar != -1) + { + Invalidate(CalcRowResizeFeedbackRect(this.currentRowSplitBar), true); + this.lastRowSplitBar = this.currentRowSplitBar = -1; + } + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + if (this.lastHeaderShadow != -1) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_showColumnRelocationInsertion] = false; + this.trackColumnEdge = -1; + this.lastHeaderShadow = -1; + Invalidate(Rectangle.Union(this.layout.TopLeftHeader, this.layout.ColumnHeaders)); + } + RealeaseMouse(); + } + + // Re-initializes all state that is related to tracking keyboard operations + private void ResetKeyboardTrackingState() + { + if (this.IsMouseOperationActive()) + { + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_keyboardOperationMask] = false; + this.trackColumn = -1; + if (this.currentColSplitBar != -1) + { + Invalidate(CalcColResizeFeedbackRect(this.currentColSplitBar), true); + this.lastColSplitBar = this.currentColSplitBar = -1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsMouseOperationActive() + { + return (this.dataGridViewOper.Data & DATAGRIDVIEWOPER_mouseOperationMask) != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsKeyboardOperationActive() + { + return (this.dataGridViewOper.Data & DATAGRIDVIEWOPER_keyboardOperationMask) != 0; + } + + /// + /// Re-initializes all UI related state. + /// + internal void ResetUIState(bool useRowShortcut, bool computeVisibleRows) + { + PerformLayoutPrivate(useRowShortcut, computeVisibleRows, true /*invalidInAdjustFillingColumns*/, !useRowShortcut /*repositionEditingControl*/); + if (!useRowShortcut) + { + Invalidate(); + InvalidateScrollBars(); + } + } + + private void RestoreRowsCachedThickness() + { + // Switch to batch operation + this.inBulkPaintCount++; + this.inBulkLayoutCount++; + try + { + // Only height of visible rows are restored since invisible rows are not autosized. + for (int rowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + rowIndex != -1; + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible)) + { + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + int height, minimumHeight; + dataGridViewRow.GetHeightInfo(rowIndex, out height, out minimumHeight); + if (height != dataGridViewRow.CachedThickness && + !OnRowHeightInfoPushed(rowIndex, dataGridViewRow.CachedThickness, minimumHeight)) + { + dataGridViewRow.ThicknessInternal = dataGridViewRow.CachedThickness; + } + } + } + finally + { + ExitBulkLayout(true /*invalidInAdjustFillingColumns*/); + ExitBulkPaint(-1, -1); + } + } + + // we need to access the GetRowState, otherwise we would unshare the row + private bool RowIsResizable(int rowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.ResizableSet) == DataGridViewElementStates.ResizableSet) + { + return (rowState & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable; + } + else + { + return this.AllowUserToResizeRows; + } + } + + private bool RowNeedsDisplayedState(int rowIndex, int lastDisplayedFrozenRowIndex, int lastDisplayedScrollingRowIndex) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(rowIndex < this.Rows.Count); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + + if ((rowState & DataGridViewElementStates.Visible) == 0) + { + return false; + } + + if ((rowState & DataGridViewElementStates.Frozen) != 0) + { + return rowIndex <= lastDisplayedFrozenRowIndex; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow != -1 && + rowIndex >= this.displayedBandsInfo.FirstDisplayedScrollingRow && + rowIndex <= lastDisplayedScrollingRowIndex) + { + return true; + } + return false; + } + + private void ScrollBar_MouseEnter(object sender, System.EventArgs e) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_mouseEnterExpected]) + { + OnMouseEnter(EventArgs.Empty); + } + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + + private void ScrollBar_MouseLeave(object sender, System.EventArgs e) + { + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + Point ptMouse = PointToClient(Control.MousePosition); + if (!this.ClientRectangle.Contains(ptMouse)) + { + OnMouseLeave(EventArgs.Empty); + } + } + + private bool ScrollColumnIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.displayedBandsInfo.FirstDisplayedScrollingCol != -1 && + !this.Columns[columnIndex].Frozen && + (columnIndex != this.displayedBandsInfo.FirstDisplayedScrollingCol || this.negOffset > 0)) + { + int columnsToScroll; + if (this.Columns.DisplayInOrder(columnIndex, this.displayedBandsInfo.FirstDisplayedScrollingCol)) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + columnsToScroll = this.Columns.GetColumnCount(DataGridViewElementStates.Visible, columnIndex, this.displayedBandsInfo.FirstDisplayedScrollingCol); + if (this.negOffset > 0) + { + columnsToScroll++; + } + ScrollColumns(-columnsToScroll); + } + else if (columnIndex == this.displayedBandsInfo.FirstDisplayedScrollingCol && this.negOffset > 0) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + ScrollColumns(-1); + } + else if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol == -1 || + (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol != columnIndex && + this.Columns.DisplayInOrder(this.displayedBandsInfo.LastTotallyDisplayedScrollingCol, columnIndex))) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + columnsToScroll = 0; + int firstDisplayedScrollingColumn = this.displayedBandsInfo.FirstDisplayedScrollingCol; + int xColumnRightEdge; + + if (this.RightToLeftInternal) + { + xColumnRightEdge = GetColumnXFromIndex(columnIndex) - this.Columns[columnIndex].Width; + while (xColumnRightEdge < this.layout.Data.X && this.Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex)) + { + xColumnRightEdge += this.Columns[firstDisplayedScrollingColumn].Width; + if (firstDisplayedScrollingColumn == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + xColumnRightEdge -= this.negOffset; + } + columnsToScroll++; + if (xColumnRightEdge < this.layout.Data.X) + { + firstDisplayedScrollingColumn = this.Columns.GetNextColumn(this.Columns[firstDisplayedScrollingColumn], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + } + } + } + else + { + xColumnRightEdge = GetColumnXFromIndex(columnIndex) + this.Columns[columnIndex].Width; + while (xColumnRightEdge > this.layout.Data.Right && this.Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex)) + { + xColumnRightEdge -= this.Columns[firstDisplayedScrollingColumn].Width; + if (firstDisplayedScrollingColumn == this.displayedBandsInfo.FirstDisplayedScrollingCol) + { + xColumnRightEdge += this.negOffset; + } + columnsToScroll++; + if (xColumnRightEdge > this.layout.Data.Right) + { + firstDisplayedScrollingColumn = this.Columns.GetNextColumn(this.Columns[firstDisplayedScrollingColumn], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None).Index; + } + } + } + if (columnsToScroll != 0) + { + ScrollColumns(columnsToScroll); + } + } + } + return true; + } + + private void ScrollColumns(int columns) + { + DataGridViewColumn newFirstVisibleScrollingCol = null; + DataGridViewColumn dataGridViewColumnTmp; + int colCount = 0; + //ScrollEventType scrollEventType; + if (columns > 0) + { + //scrollEventType = columns > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement; + if (this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0) + { + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.LastTotallyDisplayedScrollingCol]; + while (colCount < columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + colCount++; + } + + if (dataGridViewColumnTmp == null) + { + // no more column to display on the right of the last totally seen column + return; + } + } + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + colCount = 0; + while (colCount < columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + colCount++; + } + newFirstVisibleScrollingCol = dataGridViewColumnTmp; + } + + if (columns < 0) + { + //scrollEventType = columns < -1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= 0); + dataGridViewColumnTmp = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + if (this.negOffset > 0) + { + colCount++; + } + while (colCount < -columns && dataGridViewColumnTmp != null) + { + dataGridViewColumnTmp = this.Columns.GetPreviousColumn(dataGridViewColumnTmp, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + colCount++; + } + newFirstVisibleScrollingCol = dataGridViewColumnTmp; + if (newFirstVisibleScrollingCol == null) + { + if (this.negOffset == 0) + { + // no more column to display on the left of the first seen column + FlushDisplayedChanged(); + return; + } + else + { + newFirstVisibleScrollingCol = this.Columns[this.displayedBandsInfo.FirstDisplayedScrollingCol]; + } + } + } + + int newColOffset = 0; + for (DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + dataGridViewColumn != newFirstVisibleScrollingCol; + dataGridViewColumn = this.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None)) + { + newColOffset += dataGridViewColumn.Thickness; + } + + this.HorizontalOffset = newColOffset; + } + + private bool ScrollIntoView(int columnIndex, int rowIndex, bool forCurrentCellChange) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingCol >= -1 && this.displayedBandsInfo.FirstDisplayedScrollingCol < this.Columns.Count); + Debug.Assert(this.displayedBandsInfo.LastTotallyDisplayedScrollingCol >= -1 && this.displayedBandsInfo.LastTotallyDisplayedScrollingCol < this.Columns.Count); + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= -1 && this.displayedBandsInfo.FirstDisplayedScrollingRow < this.Rows.Count); + Debug.Assert(this.Columns[columnIndex].Visible); + Debug.Assert((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + + bool committed = false; + if (this.ptCurrentCell.X >= 0 && + (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex)) + { + if (!CommitEditForOperation(columnIndex, rowIndex, forCurrentCellChange)) + { + return false; + } + committed = true; + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + } + + //scroll horizontally + if (!ScrollColumnIntoView(columnIndex, rowIndex, committed, forCurrentCellChange)) + { + return false; + } + + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + + //scroll vertically + return ScrollRowIntoView(columnIndex, rowIndex, committed, forCurrentCellChange); + } + + private void ScrollRectangles(NativeMethods.RECT[] rects, int change) + { + if (rects != null) + { + if (MouseButtons != MouseButtons.None) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = true; + } + + NativeMethods.RECT scroll; + for (int r = 0; r < rects.Length; r++) + { + scroll = rects[r]; + SafeNativeMethods.ScrollWindow(new HandleRef(this, this.Handle), + change, + 0, + ref scroll, + ref scroll); + } + } + } + + private bool ScrollRowIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0) + { + int rowsToScroll; + if (rowIndex < this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + + rowsToScroll = this.Rows.GetRowCount(DataGridViewElementStates.Visible, rowIndex, this.displayedBandsInfo.FirstDisplayedScrollingRow); + ScrollRowsByCount(-rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement); + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0 && + rowIndex > this.displayedBandsInfo.FirstDisplayedScrollingRow) + { + rowsToScroll = 0; + int firstDisplayedScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int yRowBottomEdge = GetRowYFromIndex(rowIndex) + this.Rows.SharedRow(rowIndex).GetHeight(rowIndex); + while (yRowBottomEdge > this.layout.Data.Bottom && rowIndex > firstDisplayedScrollingRow) + { + yRowBottomEdge -= this.Rows.SharedRow(firstDisplayedScrollingRow).GetHeight(firstDisplayedScrollingRow); + rowsToScroll++; + if (yRowBottomEdge > this.layout.Data.Bottom) + { + firstDisplayedScrollingRow = this.Rows.GetNextRow(firstDisplayedScrollingRow, DataGridViewElementStates.Visible); + Debug.Assert(firstDisplayedScrollingRow != -1); + } + } + if (rowsToScroll != 0) + { + if (!committed && this.ptCurrentCell.X >= 0 && + !CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + forCurrentCellChange && (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex), + this.ptCurrentCell.Y != rowIndex /*forCurrentRowChange*/)) + { + return false; + } + ScrollRowsByCount(rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement); + } + } + } + return true; + } + + private void ScrollRows(int rowCount, int deltaY, ScrollEventType scrollEventType) + { + bool invalidateTopOfRowHeaders = false; + Debug.Assert(rowCount != 0); + Debug.Assert(deltaY != 0); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + this.verticalOffset -= deltaY; + if (this.vertScrollBar.Enabled) + { + this.vertScrollBar.Value = this.verticalOffset; + } + ClearRegionCache(); + int frozenRowsThickness = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Rectangle rowsRect = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + rowsRect = Rectangle.Union(rowsRect, this.layout.RowHeaders); + if (this.SingleHorizontalBorderAdded) + { + rowsRect.Y++; + rowsRect.Height--; + invalidateTopOfRowHeaders = true; + } + } + else if (this.SingleVerticalBorderAdded) + { + Debug.Assert(rowsRect.X > 0); + rowsRect.X--; + rowsRect.Width++; + } + rowsRect.Y += frozenRowsThickness; + rowsRect.Height -= frozenRowsThickness; + Debug.Assert(rowsRect.Height >= 0); + + if (this.editingControl != null && + (this.Rows.GetRowState(this.ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow > -1); + PositionEditingControl(true /*setLocation*/, false /*setSize*/, false /*setFocus*/); + } + + if (MouseButtons != MouseButtons.None) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_scrolledSinceMouseDown] = true; + } + + // The mouse probably is not over the same cell after the scroll. + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + + NativeMethods.RECT scrollArea = NativeMethods.RECT.FromXYWH(rowsRect.X, rowsRect.Y, rowsRect.Width, rowsRect.Height); + SafeNativeMethods.ScrollWindow(new HandleRef(this, this.Handle), 0, deltaY, ref scrollArea, ref scrollArea); + if (invalidateTopOfRowHeaders) + { + rowsRect.X = this.layout.Inside.X; + rowsRect.Y = this.layout.Inside.Y; + rowsRect.Width = this.layout.RowHeaders.Width; + rowsRect.Height = 1; + Invalidate(rowsRect); + } + if (!this.dataGridViewState2[DATAGRIDVIEWSTATE2_stopRaisingVerticalScroll]) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstVisibleScrollingRow = this.Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen); + Debug.Assert(firstVisibleScrollingRow != -1); + int newScrolledOffRowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible, firstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + Debug.Assert(newScrolledOffRowCount >= rowCount); + OnScroll(scrollEventType, newScrolledOffRowCount - rowCount, newScrolledOffRowCount, ScrollOrientation.VerticalScroll); + } + } + + private void ScrollRowsByCount(int rows, ScrollEventType scrollEventType) + { + Debug.Assert(rows != 0); + Debug.Assert((rows > 0 && (scrollEventType == ScrollEventType.SmallIncrement || scrollEventType == ScrollEventType.LargeIncrement)) || + (rows < 0 && (scrollEventType == ScrollEventType.SmallDecrement || scrollEventType == ScrollEventType.LargeDecrement))); + + int deltaY = 0; + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int newFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rows > 0) + { + for (int rowCount = rows; rowCount > 0; rowCount--) + { + deltaY -= this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + newFirstVisibleScrollingRow = this.Rows.GetNextRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible); + + Debug.Assert(newFirstVisibleScrollingRow != -1); + } + if (newFirstVisibleScrollingRow != -1) + { + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow. + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow + ComputeVisibleRows(); + // Compute the actual deltaY given the new this.displayedBandsInfo.FirstDisplayedScrollingRow + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow) + { + deltaY = -this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + rows = this.Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + } + else + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow == oldFirstVisibleScrollingRow); + rows = 0; + } + } + } + else + { + for (int rowCount = rows; rowCount < 0; rowCount++) + { + newFirstVisibleScrollingRow = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (newFirstVisibleScrollingRow != -1) + { + deltaY += this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + } + } + if (newFirstVisibleScrollingRow != -1) + { + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + ComputeVisibleRows(); + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow == newFirstVisibleScrollingRow); + } + } + + if (newFirstVisibleScrollingRow != -1 && rows != 0) + { + ScrollRows(rows, deltaY, scrollEventType); + } + + FlushDisplayedChanged(); + } + + private void ScrollRowsByHeight(int height) + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int deltaY, scrollHeight = 0; + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int newFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (height > 0) + { + deltaY = this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + while (deltaY <= height) + { + newFirstVisibleScrollingRow = this.Rows.GetNextRow(newFirstVisibleScrollingRow, DataGridViewElementStates.Visible); + if (newFirstVisibleScrollingRow == -1) + { + throw new InvalidOperationException(); // Occurs in case of VSWhidbey 533407 + } + else + { + deltaY += this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + } + } + } + else + { + newFirstVisibleScrollingRow = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + Debug.Assert(newFirstVisibleScrollingRow != -1); + deltaY = -this.Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow); + while (deltaY >= height) + { + int scrollingRowTmp = this.Rows.GetPreviousRow(newFirstVisibleScrollingRow, + DataGridViewElementStates.Visible, + DataGridViewElementStates.Frozen); + if (scrollingRowTmp != -1) + { + deltaY -= this.Rows.SharedRow(scrollingRowTmp).GetHeight(scrollingRowTmp); + if (deltaY >= height) + { + newFirstVisibleScrollingRow = scrollingRowTmp; + } + } + else + { + break; + } + } + } + + // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow. + this.displayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow; + // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow + ComputeVisibleRows(); + ScrollEventType scrollEventType = ScrollEventType.EndScroll; + int rowCount = 0; + // Compute the scrollHeight given the new this.displayedBandsInfo.FirstDisplayedScrollingRow + if (this.displayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow) + { + scrollHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + rowCount = this.Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, this.displayedBandsInfo.FirstDisplayedScrollingRow); + scrollEventType = rowCount > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement; + } + else if (this.displayedBandsInfo.FirstDisplayedScrollingRow < oldFirstVisibleScrollingRow) + { + scrollHeight = -this.Rows.GetRowsHeight(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow); + rowCount = -this.Rows.GetRowCount(DataGridViewElementStates.Visible, this.displayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow); + scrollEventType = rowCount < -1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement; + } + if (scrollHeight != 0) + { + ScrollRows(rowCount, -scrollHeight, scrollEventType); + } + + FlushDisplayedChanged(); + } + + /// + // Does not seem to be a valid fxcop violation report. Contacting fxcop team to double-check. + [SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")] + public void SelectAll() + { + if (!this.MultiSelect) + { + return; + } + this.inBulkPaintCount++; + this.noDimensionChangeCount++; + this.noSelectionChangeCount++; + try + { + DataGridViewRow dataGridViewRow = null; + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + // Bonjour the scalability issues! We select each cell, one at the time. + int maxColumnIndex = this.Columns.Count; + int rowIndex = 0, maxRowIndex = this.Rows.Count; + while (rowIndex < maxRowIndex) + { + dataGridViewRow = this.Rows[rowIndex]; //unsharing each row! + int columnIndex = 0; + while (columnIndex < maxColumnIndex) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + columnIndex++; + } + rowIndex++; + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int rowIndex = 0, maxRowIndex = this.Rows.Count; + while (rowIndex < maxRowIndex) + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if ((rowState & DataGridViewElementStates.Selected) == 0) + { + SetSelectedRowCore(rowIndex, true); + } + rowIndex++; + } + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int columnIndex = 0, maxColumnIndex = this.Columns.Count; + while (columnIndex < maxColumnIndex) + { + if (!this.Columns[columnIndex].Selected) + { + SetSelectedColumnCore(columnIndex, true); + } + columnIndex++; + } + break; + } + } + } + finally + { + this.noDimensionChangeCount--; + this.noSelectionChangeCount--; + Debug.Assert(this.noDimensionChangeCount >= 0); + Debug.Assert(this.noSelectionChangeCount >= 0); + ExitBulkPaint(-1, -1); + } + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (dataGridViewColumn != null && firstVisibleRowIndex != -1) + { + // This is the only place in the code outside of SetCurrentCellAddressCore where this.ptAnchorCell gets changed. + // There is no way in SetCurrentCellAddressCore to just change the anchor cell. + this.ptAnchorCell.X = dataGridViewColumn.Index; + this.ptAnchorCell.Y = firstVisibleRowIndex; + } + else + { + this.ptAnchorCell.X = -1; + this.ptAnchorCell.Y = -1; + } + + if (this.noSelectionChangeCount == 0) + { + FlushSelectionChanged(); + } + } + + private DataGridViewCell SelectedCell(int index) + { + Debug.Assert(index >= 0); + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + int selectedBand = 0, selectedBands = this.selectedBandIndexes.Count; + while (selectedBand < selectedBands && index >= 0) + { + if (index >= this.Rows.Count) + { + index -= this.Rows.Count; + selectedBand++; + } + else + { + int columnIndex = this.selectedBandIndexes[selectedBand]; + return this.Rows.SharedRow(index).Cells[columnIndex]; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect && + index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + int selectedBand = 0, selectedBands = this.selectedBandIndexes.Count; + while (selectedBand < selectedBands && index >= 0) + { + if (index >= this.Columns.Count) + { + index -= this.Columns.Count; + selectedBand++; + } + else + { + int rowIndex = this.selectedBandIndexes[selectedBand]; + return this.Rows.SharedRow(rowIndex).Cells[index]; + } + } + + if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect && + index < this.individualSelectedCells.Count) + { + return this.individualSelectedCells[index]; + } + break; + } + } + return null; + } + + private void SetColumnHeadersHeightInternal(int columnHeadersHeight, bool invalidInAdjustFillingColumns) + { + using (LayoutTransaction.CreateTransactionIf(this.AutoSize, this.ParentInternal, this, PropertyNames.ColumnHeadersHeight)) + { + Debug.Assert(this.columnHeadersHeight != columnHeadersHeight); + Debug.Assert(columnHeadersHeight >= minimumColumnHeadersHeight); + Debug.Assert(columnHeadersHeight <= maxHeadersThickness); + this.columnHeadersHeight = columnHeadersHeight; + if (this.AutoSize) + { + InvalidateInside(); + } + else + { + if (this.layout.ColumnHeadersVisible) + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, true /*repositionEditingControl*/); + InvalidateInside(); + } + } + OnColumnHeadersHeightChanged(EventArgs.Empty); + } + } + + /// + protected virtual bool SetCurrentCellAddressCore(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick) + { + if (columnIndex < -1 || + (columnIndex >= 0 && rowIndex == -1) || + columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || + (columnIndex == -1 && rowIndex >= 0) || + rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (columnIndex > -1 && + rowIndex > -1 && + !IsSharedCellVisible(this.Rows.SharedRow(rowIndex).Cells[columnIndex], rowIndex)) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrentCellCannotBeInvisible)); + } + + if (this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] && // Allow the code to be re-entrant only as a result of + (this.dataConnection == null || !this.dataConnection.ProcessingListChangedEvent)) // underlying data changing. + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_SetCurrentCellAddressCoreNotReentrant)); + } + + this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] = true; + try + { + DataGridViewCell dataGridViewCellTmp = null; + + if (columnIndex > -1) + { + Debug.Assert(rowIndex >= 0 && + columnIndex < this.Columns.Count && + rowIndex < this.Rows.Count); + + if (this.ptCurrentCell.X != columnIndex || this.ptCurrentCell.Y != rowIndex) + { + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell]) + { + this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] = false; + this.ptCurrentCell.X = columnIndex; + this.ptCurrentCell.Y = rowIndex; + if (this.cachedEditingControl != null) + { + this.editingControl = this.cachedEditingControl; + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = rowIndex; + this.cachedEditingControl = null; + PositionEditingControl(true, true, false); + } + OnCurrentCellChanged(EventArgs.Empty); + return true; + } + + DataGridViewCell currentCell; + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (oldCurrentCellX >= 0) + { + currentCell = this.CurrentCellInternal; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never /*validateCell*/, + validateCurrentCell /*fireCellLeave*/, + false /*fireCellEnter*/, + validateCurrentCell && oldCurrentCellY != rowIndex /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell = this.Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX]; + if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick)) + { + currentCell = this.Rows[oldCurrentCellY].Cells[oldCurrentCellX]; // unsharing the current row + } + currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick); + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + if (oldCurrentCellY != rowIndex) + { + if (validateCurrentCell) + { + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + Debug.Assert(oldCurrentCellX == this.ptCurrentCell.X); + Debug.Assert(oldCurrentCellY == this.ptCurrentCell.Y); + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + } + } + + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = false; + + try + { + if (oldCurrentCellY != rowIndex) + { + //Tentatively commenting out for bug #321924 + //this.ptCurrentCell.X = -1; + //this.ptCurrentCell.Y = -1; + //OnCurrentCellChanged(EventArgs.Empty); + if (!IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + OnRowEnter(ref dataGridViewCellTmp, columnIndex, rowIndex, true /*canCreateNewRow*/, false /*validationFailureOccurred*/); + } + } + + // Force repainting of the current and previous collumns` header cells to update highlighting + if (oldCurrentCellX != columnIndex && + this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && + AccessibilityImprovements.Level2) + { + if (oldCurrentCellX >= 0) + { + InvalidateCellPrivate(oldCurrentCellX, -1); + } + InvalidateCellPrivate(columnIndex, -1); + } + + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell]) + { + // + // The rows collection was cleared while processing OnRowEnter. + // If the new list is too small for rowIndex, fail the call to SetCurrentCellAddressCore. + // + if (rowIndex >= this.Rows.Count) + { + return false; + } + // rowIndex = Math.Min(rowIndex, this.Rows.GetRowCount(DataGridViewElementStates.Visible) - 1); + } + + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + + this.ptCurrentCell.X = columnIndex; + this.ptCurrentCell.Y = rowIndex; + if (this.editingControl != null) + { + ((IDataGridViewEditingControl)this.editingControl).EditingControlRowIndex = rowIndex; + } + OnCurrentCellChanged(EventArgs.Empty); + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = columnIndex; + this.ptAnchorCell.Y = rowIndex; + } + + #if FALSE + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell]) + { + // DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell bit will be cleared while executing the + // "finally" block. + return true; + } + #endif + + currentCell = this.CurrentCellInternal; + if (currentCell.EnterUnsharesRowInternal(rowIndex, throughMouseClick)) + { + currentCell = this.Rows[rowIndex].Cells[columnIndex]; // unsharing the row + } + currentCell.OnEnterInternal(rowIndex, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (oldCurrentCellX >= 0) + { + Debug.Assert(oldCurrentCellY >= 0); + if (oldCurrentCellX < this.Columns.Count && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY); + } + if (oldCurrentCellY != this.ptCurrentCell.Y && this.RowHeadersVisible && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(-1, oldCurrentCellY); + } + } + InvalidateCellPrivate(this.ptCurrentCell.X, this.ptCurrentCell.Y); + if (this.RowHeadersVisible && oldCurrentCellY != this.ptCurrentCell.Y) + { + InvalidateCellPrivate(-1, this.ptCurrentCell.Y); + } + if (this.Focused && + this.ptCurrentCell.X != -1 && + !this.IsCurrentCellInEditMode && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_leavingWithTabKey] && // don't edit if we're in the process of leaving the grid + !this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] && // don't edit if the rows collection changed + (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && currentCell.EditType == null))) + { + BeginEditInternal(true /*selectAll*/); + } + } + finally + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_rowsCollectionClearedInSetCell] = false; + } + + // Accessibility + if (this.ptCurrentCell.X != -1) + { + AccessibilityNotifyCurrentCellChanged(new Point(this.ptCurrentCell.X, this.ptCurrentCell.Y)); + } + } + else + { + // this.ptCurrentCell.X == columnIndex && this.ptCurrentCell.Y == rowIndex + // Not trying to change the current cell, but may need to edit it. + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = columnIndex; + this.ptAnchorCell.Y = rowIndex; + } + if (this.Focused && + (!this.IsCurrentCellInEditMode && (this.EditMode == DataGridViewEditMode.EditOnEnter || + (this.EditMode != DataGridViewEditMode.EditProgrammatically && this.CurrentCellInternal.EditType == null)))) + { + BeginEditInternal(true /*selectAll*/); + } + else + { + CorrectFocus(false /*onlyIfGridHasFocus*/); + } + } + } + else + { + int oldCurrentCellX = this.ptCurrentCell.X; + int oldCurrentCellY = this.ptCurrentCell.Y; + if (oldCurrentCellX >= 0 && + !this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell] && + !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + DataGridViewCell currentCell = this.CurrentCellInternal; + if (!EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange, + validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never, + validateCurrentCell /*fireCellLeave*/, + false /*fireCellEnter*/, + validateCurrentCell /*fireRowLeave*/, + false /*fireRowEnter*/, + false /*fireLeave*/, + this.EditMode != DataGridViewEditMode.EditOnEnter /*keepFocus*/, + false /*resetCurrentCell*/, + false /*resetAnchorCell unused here*/)) + { + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell = this.Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX]; + if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick)) + { + currentCell = this.Rows[oldCurrentCellY].Cells[oldCurrentCellX]; // unsharing the current row + } + currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick); + } + if (validateCurrentCell) + { + if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY)) + { + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + // Row validation was cancelled + Debug.Assert(oldCurrentCellX == this.ptCurrentCell.X); + Debug.Assert(oldCurrentCellY == this.ptCurrentCell.Y); + OnRowEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY, true /*canCreateNewRow*/, true /*validationFailureOccurred*/); + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick); + OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + return false; + } + if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY)) + { + OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY); + } + } + } + if (this.ptCurrentCell.X != -1) + { + this.ptCurrentCell.X = -1; + this.ptCurrentCell.Y = -1; + OnCurrentCellChanged(EventArgs.Empty); + } + if (setAnchorCellAddress) + { + this.ptAnchorCell.X = -1; + this.ptAnchorCell.Y = -1; + } + if (this.dataGridViewState1[DATAGRIDVIEWSTATE1_temporarilyResetCurrentCell]) + { + if (this.editingControl != null) + { + if (this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl]) + { + this.dataGridViewState2[DATAGRIDVIEWSTATE2_discardEditingControl] = false; + } + else + { + this.cachedEditingControl = this.editingControl; + } + this.editingControl = null; + } + } + else if (oldCurrentCellX >= 0 && !this.dataGridViewOper[DATAGRIDVIEWOPER_inDispose]) + { + Debug.Assert(oldCurrentCellY >= 0); + if (oldCurrentCellX < this.Columns.Count && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY); + } + if (this.RowHeadersVisible && oldCurrentCellY < this.Rows.Count) + { + InvalidateCellPrivate(-1, oldCurrentCellY); + } + } + } + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inCurrentCellChange] = false; + } + return true; + } + + internal void SetCurrentCellAddressCoreInternal(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick) + { + SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick); + } + + private void SelectCellRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select) + { + Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0); + Debug.Assert((this.Columns[columnIndexFrom]).DisplayIndex <= (this.Columns[columnIndexTo]).DisplayIndex); + Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0); + Debug.Assert(rowIndexFrom <= rowIndexTo); + Debug.Assert(this.noSelectionChangeCount > 0); + + bool switchedToBulkPaint = false; + if (rowIndexTo - rowIndexFrom > DATAGRIDVIEW_bulkPaintThreshold) + { + // Switch to batch operation + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + // Selection and deselection are done in reverse order for perf. reasons. + if (select) + { + int columnIndex = columnIndexFrom; + do + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + } + if (columnIndex != columnIndexTo) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexTo); + + if (columnIndexFrom != columnIndexTo) + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + SetSelectedCellCore(columnIndex, rowIndex, true); + } + } + } + else + { + int columnIndex = columnIndexTo; + do + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + if (columnIndex != columnIndexFrom) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexFrom); + + if (columnIndexFrom != columnIndexTo) + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private void SelectCellUnorderedRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select) + { + Debug.Assert(this.noSelectionChangeCount > 0); + int columnIndexFromTmp, rowIndexFromTmp, columnIndexToTmp, rowIndexToTmp; + + if (this.Columns.DisplayInOrder(columnIndexFrom, columnIndexTo)) + { + columnIndexFromTmp = columnIndexFrom; + columnIndexToTmp = columnIndexTo; + } + else + { + columnIndexFromTmp = columnIndexTo; + columnIndexToTmp = columnIndexFrom; + } + + if (rowIndexFrom < rowIndexTo) + { + rowIndexFromTmp = rowIndexFrom; + rowIndexToTmp = rowIndexTo; + } + else + { + rowIndexFromTmp = rowIndexTo; + rowIndexToTmp = rowIndexFrom; + } + + SelectCellRange(columnIndexFromTmp, rowIndexFromTmp, columnIndexToTmp, rowIndexToTmp, select); + } + + private void SelectColumnRange(int columnIndexFrom, int columnIndexTo, bool select) + { + Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0); + Debug.Assert((this.Columns[columnIndexFrom]).DisplayIndex <= (this.Columns[columnIndexTo]).DisplayIndex); + Debug.Assert(this.noSelectionChangeCount > 0); + + int columnIndex = columnIndexFrom; + do + { + if (select) + { + if (!this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + SetSelectedColumnCore(columnIndex, false); + } + if (columnIndex != columnIndexTo) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[columnIndex], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + columnIndex = dataGridViewColumn.Index; + } + } + while (columnIndex != columnIndexTo); + + if (columnIndexFrom != columnIndexTo) + { + if (select) + { + if (!this.selectedBandIndexes.Contains(columnIndexTo)) + { + SetSelectedColumnCore(columnIndexTo, true); + } + } + else + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndexTo)); + SetSelectedColumnCore(columnIndexTo, false); + } + } + } + + private void SelectRowRange(int rowIndexFrom, int rowIndexTo, bool select) + { + Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0); + Debug.Assert(rowIndexFrom <= rowIndexTo); + Debug.Assert(this.noSelectionChangeCount > 0); + + bool switchedToBulkPaint = false; + if (rowIndexTo - rowIndexFrom > DATAGRIDVIEW_bulkPaintThreshold) + { + // Switch to batch operation + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + // Selecting and deselecting rows in reverse order for perf. reasons + if (select) + { + for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++) + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(!this.selectedBandIndexes.Contains(rowIndex)); + SetSelectedRowCore(rowIndex, true); + } + } + } + else + { + for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + SetSelectedRowCore(rowIndex, false); + } + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, -1); + } + } + } + + private bool SetAndSelectCurrentCellAddress(int columnIndex, + int rowIndex, + bool setAnchorCellAddress, + bool validateCurrentCell, + bool throughMouseClick, + bool clearSelection, + bool forceCurrentCellSelection) + { + if (!SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick)) + { + return false; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return false; + } + if (clearSelection) + { + ClearSelection(columnIndex, rowIndex, true /*selectException*/); // we always select the new current cell when clearSelection is true + } + else + { + if (forceCurrentCellSelection) + { + SetSelectedElementCore(columnIndex, rowIndex, true); + } + else + { + if (this.MultiSelect && (this.individualSelectedCells.Count + this.selectedBandIndexes.Count) > 1) + { + return true; // Do not discard the multi-selection + } + if (this.individualSelectedCells.Count == 1) + { + DataGridViewCell dataGridViewCell = this.individualSelectedCells.HeadCell; + if (dataGridViewCell.ColumnIndex != columnIndex || dataGridViewCell.RowIndex != rowIndex) + { + return true; + } + } + else if (this.selectedBandIndexes.Count == 1) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullColumnSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (this.selectedBandIndexes.HeadInt != columnIndex) + { + return true; // Do not change a single selection that does not match the new current cell + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (this.selectedBandIndexes.HeadInt != rowIndex) + { + return true; // Do not change a single selection that does not match the new current cell + } + break; + } + } + } + SetSelectedElementCore(columnIndex, rowIndex, true); + } + } + return true; + } + + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) + { + if ((specified & BoundsSpecified.Width) == BoundsSpecified.Width && width > upperSize) { + throw new ArgumentOutOfRangeException("width", width, SR.GetString(SR.DataGridView_SizeTooLarge, (upperSize).ToString(CultureInfo.CurrentCulture))); + } + if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height && height > upperSize) { + throw new ArgumentOutOfRangeException("height", height, SR.GetString(SR.DataGridView_SizeTooLarge, (upperSize).ToString(CultureInfo.CurrentCulture))); + } + base.SetBoundsCore(x, y, width, height, specified); + } + + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + internal void SetReadOnlyCellCore(int columnIndex, int rowIndex, bool readOnly) + { + Debug.Assert(columnIndex >= 0 && rowIndex >= 0 && + columnIndex < this.Columns.Count && + rowIndex < this.Rows.Count); + + // cell's readonly state changes + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (IsSharedCellReadOnly(dataGridViewRow.Cells[columnIndex], rowIndex) != readOnly) + { + DataGridViewCell dataGridViewCell = this.Rows[rowIndex].Cells[columnIndex]; + if (readOnly) + { + if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && + !this.Columns[columnIndex].ReadOnly) + { + this.individualReadOnlyCells.Add(dataGridViewCell); + dataGridViewCell.ReadOnlyInternal = true; + } + } + else + { + if (this.individualReadOnlyCells.Contains(dataGridViewCell)) + { + this.individualReadOnlyCells.Remove(dataGridViewCell); + } + else + { + DataGridViewCell dataGridViewCellTmp; + if (this.Columns[columnIndex].ReadOnly) + { + this.Columns[columnIndex].ReadOnlyInternal = false; + // Perf Issue: this unshares all rows! + for (int row = 0; row < rowIndex; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + for (int row = rowIndex+1; row < this.Rows.Count; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + } + if ((rowState & DataGridViewElementStates.ReadOnly) != 0) + { + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false); + for (int column = 0; column < columnIndex; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + for (int column = columnIndex+1; column < this.Columns.Count; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.ReadOnlyInternal = true; + this.individualReadOnlyCells.Add(dataGridViewCellTmp); + } + } + } + if (dataGridViewCell.ReadOnly) + { + dataGridViewCell.ReadOnlyInternal = false; + } + } + } + } + + internal void SetReadOnlyColumnCore(int columnIndex, bool readOnly) + { + Debug.Assert(columnIndex >= 0 && columnIndex < this.Columns.Count); + + if (this.Columns[columnIndex].ReadOnly != readOnly) + { + // ReadOnly state of entire column changes + if (readOnly) + { + // column is made read-only + // remove individual read-only cells of this column + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = true; + RemoveIndividualReadOnlyCellsInColumn(columnIndex); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = false; + } + this.Columns[columnIndex].ReadOnlyInternal = true; + } + else + { + // column is made read-write + this.Columns[columnIndex].ReadOnlyInternal = false; + } + } + else if (!readOnly) + { + // remove any potentially individual read-only cells in the column + RemoveIndividualReadOnlyCellsInColumn(columnIndex); + } + } + + internal void SetReadOnlyRowCore(int rowIndex, bool readOnly) + { + Debug.Assert(rowIndex >= 0 && rowIndex < this.Rows.Count); + + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (((rowState & DataGridViewElementStates.ReadOnly) != 0) != readOnly) + { + // ReadOnly state of entire row changes + if (readOnly) + { + // row is made read-only + // first remove individual read-only cells of this row + try + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = true; + RemoveIndividualReadOnlyCellsInRow(rowIndex); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inReadOnlyChange] = false; + } + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, true); + } + else + { + // row is made read-write + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false); + } + } + else if (!readOnly) + { + // remove any potentially individual read-only cells in the row + RemoveIndividualReadOnlyCellsInRow(rowIndex); + } + } + + /// + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + protected virtual void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + // cell selection changes + DataGridViewRow dataGridViewRow = this.Rows.SharedRow(rowIndex); + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (IsSharedCellSelected(dataGridViewRow.Cells[columnIndex], rowIndex) != selected) + { + DataGridViewCell dataGridViewCell = this.Rows[rowIndex].Cells[columnIndex]; + if (selected) + { + if ((rowState & DataGridViewElementStates.Selected) == 0 && + !this.Columns[columnIndex].Selected) + { + this.individualSelectedCells.Add(dataGridViewCell); + dataGridViewCell.SelectedInternal = true; + } + } + else + { + if ((dataGridViewCell.State & DataGridViewElementStates.Selected) != 0) + { + Debug.Assert(this.individualSelectedCells.Contains(dataGridViewCell)); + this.individualSelectedCells.Remove(dataGridViewCell); + } + else + { + DataGridViewCell dataGridViewCellTmp; + bool switchedToBulkPaint = false; + if (this.SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect) + { + if (this.Rows.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + this.selectedBandIndexes.Remove(columnIndex); + this.Columns[columnIndex].SelectedInternal = false; + // Perf Issue: this unshares all rows! + for (int row = 0; row < rowIndex; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + for (int row = rowIndex+1; row < this.Rows.Count; row++) + { + dataGridViewCellTmp = this.Rows[row].Cells[columnIndex]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(columnIndex, -1); + } + } + } + else if (this.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + if (this.Columns.Count > DATAGRIDVIEW_bulkPaintThreshold) + { + this.inBulkPaintCount++; + switchedToBulkPaint = true; + } + try + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Remove(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false); + for (int column = 0; column < columnIndex; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + for (int column = columnIndex+1; column < this.Columns.Count; column++) + { + dataGridViewCellTmp = this.Rows[rowIndex].Cells[column]; + dataGridViewCellTmp.SelectedInternal = true; + this.individualSelectedCells.Add(dataGridViewCellTmp); + } + } + finally + { + if (switchedToBulkPaint) + { + ExitBulkPaint(-1, rowIndex); + } + } + } + } + if (dataGridViewCell.Selected) + { + dataGridViewCell.SelectedInternal = false; + } + } + } + } + + internal void SetSelectedCellCoreInternal(int columnIndex, int rowIndex, bool selected) + { + if (selected && !this.MultiSelect) + { + if (!this.Columns[columnIndex].Visible || + (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CurrentCellCannotBeInvisible)); + } + + if (!ScrollIntoView(columnIndex, rowIndex, true)) + { + return; + } + if (IsInnerCellOutOfBounds(columnIndex, rowIndex)) + { + return; + } + } + + this.noSelectionChangeCount++; + try + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + if (selected && !this.MultiSelect) + { + Debug.Assert(this.individualSelectedCells.Count <= 1); + RemoveIndividuallySelectedCells(); + } + SetSelectedCellCore(columnIndex, rowIndex, selected); + break; + } + + case DataGridViewSelectionMode.FullColumnSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != columnIndex) + { + // deselect currently selected column + SetSelectedColumnCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + if (!this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, true); + } + } + else + { + if (this.selectedBandIndexes.Contains(columnIndex)) + { + SetSelectedColumnCore(columnIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + } + SetSelectedCellCore(columnIndex, rowIndex, true); + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedColumnCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.FullRowSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + int bandIndex = 0; + while (bandIndex < this.selectedBandIndexes.Count) + { + if (this.selectedBandIndexes[bandIndex] != rowIndex) + { + // deselect currently selected row + SetSelectedRowCore(this.selectedBandIndexes[bandIndex], false); + } + else + { + bandIndex++; + } + } + } + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(rowIndex, true); + } + } + else + { + if ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0) + { + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex) == + ((this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)); + SetSelectedRowCore(rowIndex, false); + } + } + break; + } + + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (selected) + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + RemoveIndividuallySelectedCells(); + } + } + SetSelectedCellCore(columnIndex, rowIndex, true); + } + else + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + SetSelectedRowCore(this.selectedBandIndexes.HeadInt, false); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, false); + } + } + break; + } + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (selected && !this.MultiSelect) + { + bool success = SetCurrentCellAddressCore(columnIndex, rowIndex, true, false, true); + Debug.Assert(success); + } + } + + /// + protected virtual void SetSelectedColumnCore(int columnIndex, bool selected) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + + this.noSelectionChangeCount++; + try + { + if (this.Columns[columnIndex].Selected != selected) + { + // selection of entire column changes + if (selected) + { + // column is selected + // remove individually selected cells of this column + RemoveIndividuallySelectedCellsInColumn(columnIndex); + this.Columns[columnIndex].SelectedInternal = true; + Debug.Assert(!this.selectedBandIndexes.Contains(columnIndex)); + this.selectedBandIndexes.Add(columnIndex); + } + else + { + // column is deselected + Debug.Assert(this.selectedBandIndexes.Contains(columnIndex)); + this.Columns[columnIndex].SelectedInternal = false; + this.selectedBandIndexes.Remove(columnIndex); + } + } + else if (!selected) + { + // remove any potentially individually selected cells in the column + RemoveIndividuallySelectedCellsInColumn(columnIndex); + } + } + finally + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + this.NoSelectionChangeCount--; + } + } + + internal void SetSelectedColumnCoreInternal(int columnIndex, bool selected) + { + this.noSelectionChangeCount++; + try + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + int columnIndexSelected = this.selectedBandIndexes.HeadInt; + if (columnIndexSelected != columnIndex) + { + SetSelectedColumnCore(columnIndexSelected, false); + } + } + } + SetSelectedColumnCore(columnIndex, selected); + } + finally + { + this.NoSelectionChangeCount--; + } + } + + private void SetSelectedElementCore(int columnIndex, int rowIndex, bool selected) + { + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + break; + } + case DataGridViewSelectionMode.RowHeaderSelect: + { + if (columnIndex == -1) + { + SetSelectedRowCore(rowIndex, selected); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + } + break; + } + case DataGridViewSelectionMode.ColumnHeaderSelect: + { + if (rowIndex == -1) + { + SetSelectedColumnCore(columnIndex, selected); + } + else + { + SetSelectedCellCore(columnIndex, rowIndex, selected); + } + break; + } + case DataGridViewSelectionMode.FullRowSelect: + { + SetSelectedRowCore(rowIndex, selected); + break; + } + case DataGridViewSelectionMode.FullColumnSelect: + { + SetSelectedColumnCore(columnIndex, selected); + break; + } + } + } + + /// + protected virtual void SetSelectedRowCore(int rowIndex, bool selected) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + this.noSelectionChangeCount++; + try + { + DataGridViewElementStates rowState = this.Rows.GetRowState(rowIndex); + if (((rowState & DataGridViewElementStates.Selected) != 0) != selected) + { + // selection of entire row changes + if (selected) + { + // row is selected + // first remove individually selected cells of this row + RemoveIndividuallySelectedCellsInRow(rowIndex); + Debug.Assert(!this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Add(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, true); + } + else + { + // row is deselected + Debug.Assert(this.selectedBandIndexes.Contains(rowIndex)); + this.selectedBandIndexes.Remove(rowIndex); + this.Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false); + } + } + else if (!selected) + { + // remove any potentially individually selected cells in the row + RemoveIndividuallySelectedCellsInRow(rowIndex); + } + } + finally + { + Debug.Assert(this.MultiSelect || this.selectedBandIndexes.Count <= 1); + this.NoSelectionChangeCount--; + } + } + + internal void SetSelectedRowCoreInternal(int rowIndex, bool selected) + { + this.noSelectionChangeCount++; + try + { + if (!this.MultiSelect) + { + Debug.Assert(this.selectedBandIndexes.Count <= 1); + if (this.selectedBandIndexes.Count > 0) + { + int rowIndexSelected = this.selectedBandIndexes.HeadInt; + if (rowIndexSelected != rowIndex) + { + SetSelectedRowCore(rowIndexSelected, false); + } + } + } + SetSelectedRowCore(rowIndex, selected); + } + finally + { + this.NoSelectionChangeCount--; + } + } + + private bool ShouldSerializeAlternatingRowsDefaultCellStyle() + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + return !this.AlternatingRowsDefaultCellStyle.Equals(defaultStyle); + } + + private bool ShouldSerializeColumnHeadersDefaultCellStyle() + { + return !this.ColumnHeadersDefaultCellStyle.Equals(this.DefaultColumnHeadersDefaultCellStyle); + } + + private bool ShouldSerializeDefaultCellStyle() + { + return !this.DefaultCellStyle.Equals(this.DefaultDefaultCellStyle); + } + + private bool ShouldSerializeRowHeadersDefaultCellStyle() + { + return !this.RowHeadersDefaultCellStyle.Equals(this.DefaultRowHeadersDefaultCellStyle); + } + + private bool ShouldSerializeRowsDefaultCellStyle() + { + DataGridViewCellStyle defaultStyle = new DataGridViewCellStyle(); + return !this.RowsDefaultCellStyle.Equals(defaultStyle); + } + + /// + public virtual void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction) + { + if (dataGridViewColumn == null) + { + throw new ArgumentNullException("dataGridViewColumn"); + } + + if (direction != ListSortDirection.Ascending && direction != ListSortDirection.Descending) + { + throw new InvalidEnumArgumentException("direction", (int)direction, typeof(ListSortDirection)); + } + + if (dataGridViewColumn.DataGridView != this) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnDoesNotBelongToDataGridView)); + } + + if (this.VirtualMode && !dataGridViewColumn.IsDataBound) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_OperationDisabledInVirtualMode)); + } + + SortInternal(null, dataGridViewColumn, direction); + } + + /// + public virtual void Sort(IComparer comparer) + { + if (comparer == null) + { + throw new ArgumentNullException("comparer"); + } + + if (this.VirtualMode) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_OperationDisabledInVirtualMode)); + } + + // can't sort a data bound dataGridView control using a comparer + if (this.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotUseAComparerToSortDataGridViewWhenDataBound)); + } + + SortInternal(comparer, null, ListSortDirection.Ascending); + } + + private void SortDataBoundDataGridView_PerformCheck(DataGridViewColumn dataGridViewColumn) + { + IBindingList ibl = this.dataConnection.List as IBindingList; + if (ibl == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_CannotSortDataBoundDataGridViewBoundToNonIBindingList)); + } + + if (!ibl.SupportsSorting) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_IBindingListNeedsToSupportSorting)); + } + + if (!dataGridViewColumn.IsDataBound) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_ColumnNeedsToBeDataBoundWhenSortingDataBoundDataGridView), "dataGridViewColumn"); + } + } + + private void SortInternal(IComparer comparer, DataGridViewColumn dataGridViewColumn, ListSortDirection direction) + { + Debug.Assert(!(comparer != null && this.DataSource != null)); + Debug.Assert(direction == ListSortDirection.Ascending || direction == ListSortDirection.Descending); + + // Exit editing mode if needed + this.ptCurrentCellCache.X = this.ptCurrentCell.X; + this.ptCurrentCellCache.Y = this.ptCurrentCell.Y; + this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] = true; + try + { + if (!SetCurrentCellAddressCore(-1, -1, true, true, false)) + { + // Just cancel operation silently instead of throwing InvalidOperationException + // 'finally' below resets this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] to false + return; + } + + int firstDisplayedScrollingRowCache = this.displayedBandsInfo.FirstDisplayedScrollingRow; + int visibleFrozenRows = this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + if (visibleFrozenRows > 0 && this.DataSource == null) + { + int rowVFIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + Debug.Assert(rowVFIndex != -1); + this.Rows.SetRowState(rowVFIndex, DataGridViewElementStates.Frozen, false); + Debug.Assert(0 == this.Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)); + } + + if (this.sortedColumn != null && + this.sortedColumn.SortMode == DataGridViewColumnSortMode.Automatic && + this.sortedColumn.HasHeaderCell) + { + this.sortedColumn.HeaderCell.SortGlyphDirection = SortOrder.None; + } + + if (comparer == null) + { + Debug.Assert(dataGridViewColumn != null); + this.sortedColumn = dataGridViewColumn; + this.sortOrder = (direction == ListSortDirection.Ascending) ? SortOrder.Ascending : SortOrder.Descending; + if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && dataGridViewColumn.HasHeaderCell) + { + dataGridViewColumn.HeaderCell.SortGlyphDirection = this.sortOrder; + } + } + else + { + this.sortedColumn = null; + this.sortOrder = SortOrder.None; + } + + if (this.DataSource == null) + { + // Displayed rows may end up all spread out in the final layout. + // So we simply reset their displayed state before the sort. + UpdateRowsDisplayedState(false /*displayed*/); + this.Rows.Sort(comparer, direction == ListSortDirection.Ascending); + } + else + { + SortDataBoundDataGridView_PerformCheck(dataGridViewColumn); + // the check passed, do the sorting + this.dataConnection.Sort(dataGridViewColumn, direction); + } + + if (this.ptCurrentCellCache.X != -1) + { + if (!IsInnerCellOutOfBounds(this.ptCurrentCellCache.X, this.ptCurrentCellCache.Y)) + { + SetAndSelectCurrentCellAddress(this.ptCurrentCellCache.X, + this.ptCurrentCellCache.Y, + true, + false, + false, + false /*clearSelection*/, + false /*forceCurrentCellSelection*/); + } + } + if (visibleFrozenRows > 0) + { + int rowVIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + Debug.Assert(rowVIndex != -1); + while (visibleFrozenRows > 1) + { + rowVIndex = this.Rows.GetNextRow(rowVIndex, DataGridViewElementStates.Visible); + Debug.Assert(rowVIndex != -1); + visibleFrozenRows--; + } + this.Rows.SetRowState(rowVIndex, DataGridViewElementStates.Frozen, true); + } + this.displayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRowCache; + } + finally + { + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_inSort]); + this.dataGridViewOper[DATAGRIDVIEWOPER_inSort] = false; + } + // Same effect as changing a top level cell style + OnGlobalAutoSize(); + if (this.DataSource == null) + { + // VSWhidbey 500898. Ensure that the Displayed states get set properly because they were wiped out by UpdateRowsDisplayedState above. + this.displayedBandsInfo.EnsureDirtyState(); + } + ResetUIState(false /*useRowShortcut*/, false /*computeVisibleRows*/); + OnSorted(EventArgs.Empty); + + if (AccessibilityImprovements.Level3) + { + // RS4 narrator does not catch this event even though event is indeed raised. + AccessibilityNotifyClients(AccessibleEvents.Reorder, NativeMethods.OBJID_CLIENT, 0); + } + } + + internal void SwapSortedRows(int rowIndex1, int rowIndex2) + { + Debug.Assert(rowIndex1 != this.newRowIndex); + Debug.Assert(rowIndex2 != this.newRowIndex); + if (rowIndex1 == rowIndex2) + { + return; + } + + // Follow the move of the old current cell + if (rowIndex1 == this.ptCurrentCellCache.Y) + { + this.ptCurrentCellCache.Y = rowIndex2; + } + else if (rowIndex2 == this.ptCurrentCellCache.Y) + { + this.ptCurrentCellCache.Y = rowIndex1; + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.FullRowSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + int row1Selected = this.selectedBandIndexes.IndexOf(rowIndex1); + int row2Selected = this.selectedBandIndexes.IndexOf(rowIndex2); + if (row1Selected != -1 && row2Selected == -1) + { + this.selectedBandIndexes[row1Selected] = rowIndex2; + } + else if (row1Selected == -1 && row2Selected != -1) + { + this.selectedBandIndexes[row2Selected] = rowIndex1; + } + if (this.selectedBandSnapshotIndexes != null) + { + row1Selected = this.selectedBandSnapshotIndexes.IndexOf(rowIndex1); + row2Selected = this.selectedBandSnapshotIndexes.IndexOf(rowIndex2); + if (row1Selected != -1 && row2Selected == -1) + { + this.selectedBandSnapshotIndexes[row1Selected] = rowIndex2; + } + else if (row1Selected == -1 && row2Selected != -1) + { + this.selectedBandSnapshotIndexes[row2Selected] = rowIndex1; + } + } + break; + } + } + + private void DataGridViewHScrolled(object sender, ScrollEventArgs se) + { + if (!this.Enabled) + { + return; + } + + /* VS Whidbey bug 262445 - no more commit on scroll + if (this.ptCurrentCell.X >= 0) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + se.NewValue = this.HorizontalOffset; + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + this.horizScrollBar.Invalidate(); + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange-/, false /*forCurrentRowChange-/)) + { + se.NewValue = this.HorizontalOffset; + if (se.Type == ScrollEventType.SmallIncrement || se.Type == ScrollEventType.SmallDecrement) + { + // Workaround for a pbm where the scrollbar ends up in a bad state after a validation failure dialog is displayed. + this.horizScrollBar.RecreateScrollBarHandle(); + } + else + { + this.horizScrollBar.Invalidate(); + } + return; + } + else + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinHScroll] = false; + } + } + */ + + if (se.Type == ScrollEventType.SmallIncrement || + se.Type == ScrollEventType.SmallDecrement) + { + int dCols = (se.Type == ScrollEventType.SmallIncrement) ? 1 : -1; + ScrollColumns(dCols); + se.NewValue = this.HorizontalOffset; + } + else if (se.Type != ScrollEventType.EndScroll) + { + this.HorizontalOffset = se.NewValue; + } + } + + private void DataGridViewVScrolled(object sender, ScrollEventArgs se) + { + if (!this.Enabled) + { + return; + } + + /* VS Whidbey bug 262445 - no more commit on scroll + if (this.ptCurrentCell.X >= 0) + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll]) + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + se.NewValue = this.VerticalOffset; + return; + } + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = (se.Type == ScrollEventType.ThumbTrack || se.Type == ScrollEventType.ThumbPosition); + this.vertScrollBar.Invalidate(); + if (!CommitEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll, + false /*forCurrentCellChange-/, false /*forCurrentRowChange-/)) + { + se.NewValue = this.VerticalOffset; + if (se.Type == ScrollEventType.SmallIncrement || se.Type == ScrollEventType.SmallDecrement) + { + // Workaround for a pbm where the scrollbar ends up in a bad state after a validation failure dialog is displayed. + this.vertScrollBar.RecreateScrollBarHandle(); + } + else + { + this.vertScrollBar.Invalidate(); + } + return; + } + else + { + this.dataGridViewOper[DATAGRIDVIEWOPER_cancelCommitWithinVScroll] = false; + } + } + */ + + int totalVisibleFrozenHeight = this.Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + switch (se.Type) + { + case ScrollEventType.SmallIncrement: + { + // Making sure that when the last visible scrolling row is taller than the data area, it does not get scrolled off screen + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + if (this.vertScrollBar.Value + this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow) <= + this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)) + { + ScrollRowsByCount(1, ScrollEventType.SmallIncrement); + } + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.SmallDecrement: + { + if (this.vertScrollBar.Value != this.vertScrollBar.Minimum) + { + ScrollRowsByCount(-1, ScrollEventType.SmallDecrement); + } + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.LargeIncrement: + { + Debug.Assert(this.displayedBandsInfo.FirstDisplayedScrollingRow >= 0); + int firstDisplayedScrollingRowHeight = this.Rows.SharedRow(this.displayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(this.displayedBandsInfo.FirstDisplayedScrollingRow); + this.VerticalOffset += Math.Max(firstDisplayedScrollingRowHeight, this.vertScrollBar.LargeChange); + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.LargeDecrement: + { + this.VerticalOffset -= this.vertScrollBar.LargeChange; + se.NewValue = this.VerticalOffset; + break; + } + case ScrollEventType.ThumbTrack: + case ScrollEventType.First: + case ScrollEventType.Last: + { + if (se.NewValue >= this.vertScrollBar.Maximum - this.vertScrollBar.LargeChange) + { + // Need to display the last scrolling row + this.VerticalOffset = this.vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight); + } + else + { + this.VerticalOffset = se.NewValue; + } + break; + } + } + } + + private bool TabToNextCell() + { + bool success; + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + int nextVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + nextVisibleColumnIndex = dataGridViewColumn.Index; + } + } + int nextVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + nextVisibleRowIndex = this.Rows.GetNextRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + int targetRowIndex = -1, targetColumnIndex = -1; + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + } + else + { + if (nextVisibleColumnIndex == -1) + { + targetRowIndex = (nextVisibleRowIndex == -1) ? firstVisibleRowIndex : nextVisibleRowIndex; + targetColumnIndex = firstVisibleColumnIndex; + } + else + { + targetRowIndex = this.ptCurrentCell.Y; + targetColumnIndex = nextVisibleColumnIndex; + } + if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true)) + { + return true; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(targetColumnIndex, targetRowIndex, true); + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (IsColumnOutOfBounds(targetColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(targetColumnIndex, true); + } + break; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + if (targetRowIndex != this.ptCurrentCell.Y || this.MultiSelect) + { + if (IsRowOutOfBounds(targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(targetRowIndex, true); + } + } + break; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X == -1) + { + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + return true; + } + + private bool TabToPreviousCell() + { + bool success; + + DataGridViewColumn dataGridViewColumn = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + int firstVisibleColumnIndex = (dataGridViewColumn == null) ? -1 : dataGridViewColumn.Index; + int firstVisibleRowIndex = this.Rows.GetFirstRow(DataGridViewElementStates.Visible); + if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1) + { + return false; + } + + int previousVisibleColumnIndex = -1; + if (this.ptCurrentCell.X != -1) + { + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[this.ptCurrentCell.X], + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + if (dataGridViewColumn != null) + { + previousVisibleColumnIndex = dataGridViewColumn.Index; + } + } + + int previousVisibleRowIndex = -1; + if (this.ptCurrentCell.Y != -1) + { + previousVisibleRowIndex = this.Rows.GetPreviousRow(this.ptCurrentCell.Y, DataGridViewElementStates.Visible); + } + + dataGridViewColumn = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + int lastVisibleColumnIndex = dataGridViewColumn.Index; + int lastVisibleRowIndex = this.Rows.GetLastRow(DataGridViewElementStates.Visible); + Debug.Assert(lastVisibleRowIndex != -1); + + int targetRowIndex = -1, targetColumnIndex = -1; + + this.noSelectionChangeCount++; + try + { + if (this.ptCurrentCell.X == -1) + { + ClearSelection(); + } + else + { + if (previousVisibleColumnIndex == -1) + { + targetRowIndex = (previousVisibleRowIndex == -1) ? lastVisibleRowIndex : previousVisibleRowIndex; + targetColumnIndex = lastVisibleColumnIndex; + } + else + { + targetRowIndex = this.ptCurrentCell.Y; + targetColumnIndex = previousVisibleColumnIndex; + } + if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true)) + { + return true; + } + } + + switch (this.SelectionMode) + { + case DataGridViewSelectionMode.CellSelect: + case DataGridViewSelectionMode.RowHeaderSelect: + case DataGridViewSelectionMode.ColumnHeaderSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedCellCore(targetColumnIndex, targetRowIndex, true); + } + break; + + case DataGridViewSelectionMode.FullColumnSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsColumnOutOfBounds(firstVisibleColumnIndex)) + { + return true; + } + SetSelectedColumnCore(firstVisibleColumnIndex, true); + } + else + { + if (IsColumnOutOfBounds(targetColumnIndex)) + { + return true; + } + ClearSelection(); + SetSelectedColumnCore(targetColumnIndex, true); + } + break; + + case DataGridViewSelectionMode.FullRowSelect: + if (this.ptCurrentCell.X == -1) + { + if (IsRowOutOfBounds(firstVisibleRowIndex)) + { + return true; + } + SetSelectedRowCore(firstVisibleRowIndex, true); + } + else + { + if (targetRowIndex != this.ptCurrentCell.Y || this.MultiSelect) + { + if (IsRowOutOfBounds(targetRowIndex)) + { + return true; + } + ClearSelection(); + SetSelectedRowCore(targetRowIndex, true); + } + } + break; + } + } + finally + { + this.NoSelectionChangeCount--; + } + + if (this.ptCurrentCell.X == -1) + { + success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false); + Debug.Assert(success); + if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + else + { + if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex)) + { + return true; + } + success = SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false); + // Microsoft: SetCurrentCellAddressCore can fail if by navigating to a cell the list under the + // DataGridView changes. + // See vsWhidbey: 325296. + // Debug.Assert(success); + } + + return true; + } + + private void UnwireEditingControlEvents() + { + Debug.Assert(this.editingPanel != null); + this.editingPanel.Click -= new System.EventHandler(EditingControls_Click); + this.editingPanel.DoubleClick -= new System.EventHandler(EditingControls_DoubleClick); + this.editingPanel.MouseClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingPanel.MouseDoubleClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingPanel.MouseDown -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingPanel.MouseEnter -= new System.EventHandler(EditingControls_MouseEnter); + this.editingPanel.MouseLeave -= new System.EventHandler(EditingControls_MouseLeave); + this.editingPanel.MouseMove -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingPanel.MouseUp -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + + Debug.Assert(this.editingControl != null); + this.editingControl.Click -= new System.EventHandler(EditingControls_Click); + this.editingControl.DoubleClick -= new System.EventHandler(EditingControls_DoubleClick); + this.editingControl.MouseClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingControl.MouseDoubleClick -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingControl.MouseDown -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingControl.MouseEnter -= new System.EventHandler(EditingControls_MouseEnter); + this.editingControl.MouseLeave -= new System.EventHandler(EditingControls_MouseLeave); + this.editingControl.MouseMove -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingControl.MouseUp -= new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + } + + private void UnwireScrollBarsEvents() + { + if (this.horizScrollBar != null) + { + this.horizScrollBar.MouseEnter -= new System.EventHandler(ScrollBar_MouseEnter); + this.horizScrollBar.MouseLeave -= new System.EventHandler(ScrollBar_MouseLeave); + } + if (this.vertScrollBar != null) + { + this.vertScrollBar.MouseEnter -= new System.EventHandler(ScrollBar_MouseEnter); + this.vertScrollBar.MouseLeave -= new System.EventHandler(ScrollBar_MouseLeave); + } + } + + /// + public void UpdateCellErrorText(int columnIndex, int rowIndex) + { + if (columnIndex < -1 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < -1 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated) + { + InvalidateCellPrivate(columnIndex, rowIndex); + } + } + + /// + public void UpdateCellValue(int columnIndex, int rowIndex) + { + if (columnIndex < 0 || columnIndex >= this.Columns.Count) + { + throw new ArgumentOutOfRangeException("columnIndex"); + } + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated) + { + OnCellCommonChange(columnIndex, rowIndex); + } + } + + private void UpdateColumnsDisplayedState(bool displayed) + { + // Make sure all displayed frozen columns have their Displayed state set to this.Visible + DataGridViewColumn dataGridViewColumnTmp; + int numDisplayedFrozenCols = this.displayedBandsInfo.NumDisplayedFrozenCols; + if (numDisplayedFrozenCols > 0) + { + dataGridViewColumnTmp = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (dataGridViewColumnTmp.Displayed != displayed) + { + dataGridViewColumnTmp.DisplayedInternal = displayed; + Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp)); + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None); + numDisplayedFrozenCols--; + } + } + + // Make sure all displayed scrolling columns have the Displayed state set to this.Visible + int columnIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingCol; + if (columnIndexTmp != -1) + { + int numDisplayedScrollingCols = this.displayedBandsInfo.NumDisplayedScrollingCols; + Debug.Assert(numDisplayedScrollingCols > 0); + dataGridViewColumnTmp = this.Columns[columnIndexTmp]; + while (numDisplayedScrollingCols > 0) + { + Debug.Assert(dataGridViewColumnTmp != null); + if (dataGridViewColumnTmp.Displayed != displayed) + { + dataGridViewColumnTmp.DisplayedInternal = displayed; + Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp)); + } + dataGridViewColumnTmp = this.Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None); + numDisplayedScrollingCols--; + } + } + } + + /// + public void UpdateRowErrorText(int rowIndex) + { + if (rowIndex < 0 || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.IsHandleCreated && this.layout.RowHeadersVisible) + { + InvalidateCellPrivate(-1, rowIndex); + } + } + + /// + public void UpdateRowErrorText(int rowIndexStart, int rowIndexEnd) + { + if (rowIndexStart < 0 || rowIndexStart >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexStart"); + } + if (rowIndexEnd < 0 || rowIndexEnd >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndexEnd"); + } + if (rowIndexEnd < rowIndexStart) + { + throw new ArgumentOutOfRangeException("rowIndexEnd"); + } + if (this.IsHandleCreated && this.layout.RowHeadersVisible) + { + Rectangle rectUpper = GetCellAdjustedDisplayRectangle(-1, rowIndexStart, true); + Rectangle rectLower = GetCellAdjustedDisplayRectangle(-1, rowIndexEnd, true); + if (rectUpper.IsEmpty || rectLower.IsEmpty) + { + if (!rectUpper.IsEmpty || !rectLower.IsEmpty) + { + Invalidate(this.layout.RowHeaders); + } + } + else + { + Invalidate(Rectangle.Union(rectUpper, rectLower)); + } + } + } + + /// + public void UpdateRowHeightInfo(int rowIndex, bool updateToEnd) + { + UpdateRowHeightInfoPrivate(rowIndex, updateToEnd, true /*invalidInAdjustFillingColumns*/); + } + + private void UpdateRowHeightInfoPrivate(int rowIndex, bool updateToEnd, bool invalidInAdjustFillingColumns) + { + if ((updateToEnd && rowIndex < 0) || (!updateToEnd && rowIndex < -1) || rowIndex >= this.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + this.Rows.InvalidateCachedRowsHeights(); + + bool rowVisible = (rowIndex >= 0 && (this.Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0); + + // don't do any layout logic if the handled was not created already + if (this.IsHandleCreated && (rowIndex == -1 || rowVisible)) + { + if (updateToEnd) + { + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + + Rectangle bottomArea = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + bottomArea = Rectangle.Union(bottomArea, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + bottomArea.X--; + } + bottomArea.Width++; + } + + if (!rowVisible) + { + rowIndex = this.Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible); + } + if (rowIndex != -1) + { + int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : this.displayedBandsInfo.FirstDisplayedScrollingRow); + bottomArea.Height -= bottomArea.Y - topEdge; + bottomArea.Y = topEdge; + Invalidate(bottomArea); + } + + if (this.editingControl != null) + { + PositionEditingControl(true /*setLocation*/, true /*setSize*/, false /*setFocus*/); + } + } + else + { + if (rowIndex == -1) + { + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + Invalidate(); + } + else + { + Debug.Assert(rowVisible); + int oldFirstVisibleScrollingRow = this.displayedBandsInfo.FirstDisplayedScrollingRow; + + if (this.inBulkLayoutCount == 0) + { + if (this.AutoSize) + { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Rows); + } + else + { + PerformLayoutPrivate(false /*useRowShortcut*/, false /*computeVisibleRows*/, invalidInAdjustFillingColumns, false /*repositionEditingControl*/); + } + } + + if (this.inBulkPaintCount == 0) + { + Rectangle bottomArea = this.layout.Data; + if (this.layout.RowHeadersVisible) + { + bottomArea = Rectangle.Union(bottomArea, this.layout.RowHeaders); + } + else if (this.SingleVerticalBorderAdded) + { + if (!this.RightToLeftInternal) + { + bottomArea.X--; + } + bottomArea.Width++; + } + int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == this.displayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : this.displayedBandsInfo.FirstDisplayedScrollingRow); + bottomArea.Height -= bottomArea.Y - topEdge; + bottomArea.Y = topEdge; + Invalidate(bottomArea); + } + } + + if (this.editingControl != null) + { + PositionEditingControl(rowIndex == -1 || this.ptCurrentCell.Y != rowIndex, true, false); + } + } + + UpdateMouseEnteredCell(null /*HitTestInfo*/, null /*MouseEventArgs*/); + } + } + + private void UpdateRowsDisplayedState(bool displayed) + { + // Make sure all displayed frozen rows have their Displayed state set to 'displayed' + int rowIndexTmp, numDisplayedFrozenRows = this.displayedBandsInfo.NumDisplayedFrozenRows; + if (numDisplayedFrozenRows > 0) + { + rowIndexTmp = this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (numDisplayedFrozenRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if (((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + numDisplayedFrozenRows--; + } + } + + // Make sure all displayed scrolling rows have their Displayed state set to 'displayed' + rowIndexTmp = this.displayedBandsInfo.FirstDisplayedScrollingRow; + if (rowIndexTmp > -1) + { + int numDisplayedScrollingRows = this.displayedBandsInfo.NumDisplayedScrollingRows; + Debug.Assert(numDisplayedScrollingRows > 0); + while (numDisplayedScrollingRows > 0) + { + Debug.Assert(rowIndexTmp != -1); + if (((this.Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed) + { + this.Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed); + } + rowIndexTmp = this.Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible); + numDisplayedScrollingRows--; + } + } + } + + private void UpdateMouseEnteredCell(HitTestInfo hti, MouseEventArgs e) + { + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo htiToUse; + if (hti != null) + { + htiToUse = hti; + } + else + { + htiToUse = HitTest(ptMouse.X, ptMouse.Y); + } + + if (htiToUse.Type != DataGridViewHitTestType.None && + htiToUse.Type != DataGridViewHitTestType.HorizontalScrollBar && + htiToUse.Type != DataGridViewHitTestType.VerticalScrollBar) + { + if (this.ptMouseEnteredCell.X != htiToUse.col || this.ptMouseEnteredCell.Y != htiToUse.row) + { + DataGridViewCellEventArgs dgvce; + if (this.ptMouseEnteredCell.X >= -1 && + this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && + this.ptMouseEnteredCell.Y < this.Rows.Count) + { + dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + dgvce = new DataGridViewCellEventArgs(htiToUse.col, htiToUse.row); + OnCellMouseEnter(dgvce); + } + if (e != null) + { + int mouseX = e.X - htiToUse.ColumnX; + if (this.RightToLeftInternal) + { + mouseX += ((htiToUse.col == -1) ? this.RowHeadersWidth : this.Columns[htiToUse.col].Thickness); + } + DataGridViewCellMouseEventArgs dgvcme = new DataGridViewCellMouseEventArgs(htiToUse.col, htiToUse.row, mouseX, e.Y - htiToUse.RowY, e); + OnCellMouseMove(dgvcme); + } + } + else if (this.ptMouseEnteredCell.X != -2) + { + if (this.ptMouseEnteredCell.X >= -1 && + this.ptMouseEnteredCell.X < this.Columns.Count && + this.ptMouseEnteredCell.Y >= -1 && + this.ptMouseEnteredCell.Y < this.Rows.Count) + { + DataGridViewCellEventArgs dgvce = new DataGridViewCellEventArgs(this.ptMouseEnteredCell.X, this.ptMouseEnteredCell.Y); + OnCellMouseLeave(dgvce); + } + else + { + this.ptMouseEnteredCell.X = this.ptMouseEnteredCell.Y = -2; + } + } + } + + private void UpdateSelectedCellsBlock(int anchorColumnIndex, ref int oldEdgeColumnIndex, int newEdgeColumnIndex, + int anchorRowIndex, ref int oldEdgeRowIndex, int newEdgeRowIndex) + { + Debug.Assert(anchorColumnIndex >= 0); + Debug.Assert(anchorRowIndex >= 0); + Debug.Assert(newEdgeColumnIndex >= 0); + Debug.Assert(newEdgeRowIndex >= 0); + Debug.Assert(this.noSelectionChangeCount > 0); + if ((this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) && + this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex)) || + (this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) && + this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) || + (anchorRowIndex < oldEdgeRowIndex && newEdgeRowIndex < anchorRowIndex) || + (oldEdgeRowIndex < anchorRowIndex && anchorRowIndex < newEdgeRowIndex)) + { + // deselecting all selected block + SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + // and reselecting new block + SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, newEdgeColumnIndex, newEdgeRowIndex, true); + oldEdgeColumnIndex = newEdgeColumnIndex; + oldEdgeRowIndex = newEdgeRowIndex; + return; + } + + if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) || anchorColumnIndex == oldEdgeColumnIndex) && + oldEdgeRowIndex == newEdgeRowIndex) + { + // h1 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + if (anchorRowIndex <= newEdgeRowIndex) + { + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, newEdgeColumnIndex, newEdgeRowIndex, true); + } + else + { + // newEdgeRowIndex < anchorRowIndex + SelectCellRange(dataGridViewColumn.Index, newEdgeRowIndex, newEdgeColumnIndex, anchorRowIndex, true); + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + (this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) || oldEdgeColumnIndex == anchorColumnIndex) && + oldEdgeRowIndex == newEdgeRowIndex) + { + // h2 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + if (anchorRowIndex <= newEdgeRowIndex) + { + SelectCellRange(newEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, newEdgeRowIndex, true); + } + else + { + // newEdgeRowIndex < anchorRowIndex + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, true); + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex && + anchorRowIndex <= oldEdgeRowIndex && + newEdgeColumnIndex == oldEdgeColumnIndex) + { + // h3 + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex) + { + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + else + { + // newEdgeColumnIndex before anchorColumnIndex + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex && + oldEdgeRowIndex <= anchorRowIndex && + newEdgeColumnIndex == oldEdgeColumnIndex) + { + // h4 + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex) + { + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + // newEdgeColumnIndex before anchorColumnIndex + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + !this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) && + newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex) + { + // h5 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, newEdgeColumnIndex, oldEdgeRowIndex, true); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex) && + newEdgeRowIndex < oldEdgeRowIndex && oldEdgeRowIndex <= anchorRowIndex) + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + // h6 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, newEdgeColumnIndex, anchorRowIndex, true); + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) + { + if (anchorRowIndex == oldEdgeRowIndex) + { + // g2 + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, anchorColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, anchorColumnIndex, anchorRowIndex, true); + } + else + { + // b4 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(oldEdgeRowIndex < anchorRowIndex); + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + } + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + newEdgeRowIndex < oldEdgeRowIndex && anchorRowIndex >= oldEdgeRowIndex) + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + // h7 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(newEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, true); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex)) + { + // a4 + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(oldEdgeRowIndex <= anchorRowIndex); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(anchorColumnIndex, + newEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + true); + } + else + { + // g8 + SelectCellRange(anchorColumnIndex, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, newEdgeRowIndex, anchorColumnIndex, anchorRowIndex, true); + } + } + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex) && + !this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) && + newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex) + { + // h8 + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(newEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, true); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + else if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (newEdgeRowIndex == oldEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // a1 + Debug.Assert(dataGridViewColumn != null); + if (oldEdgeRowIndex > anchorRowIndex) + { + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + } + else + { + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (oldEdgeRowIndex > anchorRowIndex) + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)) + { + if (anchorRowIndex <= newEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + if (!this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex)) + { + // a2 + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + oldEdgeRowIndex, + false); + } + } + else + { + // d3 + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(oldEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + SelectCellRange(newEdgeColumnIndex, + anchorRowIndex, + dataGridViewColumn.Index, + newEdgeRowIndex, + true); + } + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (oldEdgeRowIndex < anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) && + newEdgeRowIndex <= anchorRowIndex) + { + // a3 + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, oldEdgeRowIndex, oldEdgeColumnIndex, anchorRowIndex, false); + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + newEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + if (!this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c3 + dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(oldEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + SelectCellRange(newEdgeColumnIndex, + newEdgeRowIndex, + dataGridViewColumn.Index, + anchorRowIndex, + true); + } + } + } + } + else + { + if (this.Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // a5 + Debug.Assert(oldEdgeRowIndex >= anchorRowIndex); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(dataGridViewColumn.Index, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(anchorRowIndex, DataGridViewElementStates.Visible), + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + else if (this.Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex)) + { + DataGridViewColumn dataGridViewColumn = this.Columns.GetPreviousColumn(this.Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + if (newEdgeRowIndex == oldEdgeRowIndex) + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex)) + { + // b1 + Debug.Assert(dataGridViewColumn != null); + if (oldEdgeRowIndex > anchorRowIndex) + { + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (oldEdgeRowIndex > anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex)) && + newEdgeRowIndex >= anchorRowIndex) + { + // b2 + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + } + else + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + if (newEdgeRowIndex >= anchorRowIndex) + { + // d2 + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + oldEdgeColumnIndex, + oldEdgeRowIndex, + false); + SelectCellRange(dataGridViewColumn.Index, + anchorRowIndex, + newEdgeColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (oldEdgeRowIndex < anchorRowIndex) + { + if ((this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) && + newEdgeRowIndex <= anchorRowIndex) + { + // b3 + SelectCellRange(oldEdgeColumnIndex, oldEdgeRowIndex, dataGridViewColumn.Index, anchorRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + if (!this.Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c2 + dataGridViewColumn = this.Columns.GetNextColumn(this.Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None); + Debug.Assert(dataGridViewColumn != null); + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + oldEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + SelectCellRange(dataGridViewColumn.Index, + newEdgeRowIndex, + newEdgeColumnIndex, + anchorRowIndex, + true); + } + } + } + } + else + { + if (this.Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex)) + { + // b5 + Debug.Assert(oldEdgeRowIndex >= anchorRowIndex); + SelectCellRange(oldEdgeColumnIndex, anchorRowIndex, dataGridViewColumn.Index, oldEdgeRowIndex, false); + SelectCellRange(newEdgeColumnIndex, + this.Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + newEdgeRowIndex, + true); + } + } + } + } + else if (newEdgeRowIndex > oldEdgeRowIndex) + { + if (newEdgeColumnIndex == oldEdgeColumnIndex) + { + if (newEdgeRowIndex <= anchorRowIndex) + { + // c1 + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + SelectCellRange(anchorColumnIndex, + oldEdgeRowIndex, + oldEdgeColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, + oldEdgeRowIndex, + anchorColumnIndex, + this.Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + false); + } + } + } + } + else if (newEdgeRowIndex < oldEdgeRowIndex) + { + if (newEdgeColumnIndex == oldEdgeColumnIndex) + { + if (newEdgeRowIndex >= anchorRowIndex) + { + // d1 + if (this.Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)) + { + SelectCellRange(anchorColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + oldEdgeColumnIndex, + oldEdgeRowIndex, + false); + } + else + { + SelectCellRange(oldEdgeColumnIndex, + this.Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible), + anchorColumnIndex, + oldEdgeRowIndex, + false); + } + } + } + } + oldEdgeColumnIndex = newEdgeColumnIndex; + oldEdgeRowIndex = newEdgeRowIndex; + } + + private void VertScrollTimer_Tick(object sender, System.EventArgs e) + { + BeginInvoke(new MethodInvoker(VertScrollTimerHandler)); + } + + private void VertScrollTimerHandler() + { + // Why would the vertical scroll timer be enabled when vertical selection is not occurring + Debug.Assert(this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] || this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]); + + Point ptMouse = PointToClient(Control.MousePosition); + HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y); + int xOffset, yOffset, mouseX = ptMouse.X, mouseY = ptMouse.Y; + + if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out xOffset, out yOffset)) + { + if (yOffset != 0) + { + int absYOffset = Math.Abs(yOffset), normOffset = yOffset / absYOffset; + ScrollRowsByCount(normOffset, normOffset < 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement); + this.vertScrollTimer.Interval = GetRowScrollRate(absYOffset); + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect]) + { + hti = HitTest(mouseX, ptMouse.Y - yOffset - normOffset); + if (hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect]) + { + if (xOffset != 0) + { + hti = HitTest(ptMouse.X - xOffset - (xOffset / Math.Abs(xOffset)), ptMouse.Y - yOffset - normOffset); + } + else + { + hti = HitTest(mouseX, ptMouse.Y - yOffset - normOffset); + } + if (hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + } + } + else + { + if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackRowSelect] && hti.row >= 0) + { + OnRowSelectMouseMove(hti); + } + else if (this.dataGridViewOper[DATAGRIDVIEWOPER_trackCellSelect] && hti.col >= 0 && hti.row >= 0) + { + OnCellSelectMouseMove(hti); + } + this.VertScrollTimer.Enabled = false; + } + } + } + + private void WireEditingControlEvents() + { + Debug.Assert(this.editingPanel != null); + this.editingPanel.Click += new System.EventHandler(EditingControls_Click); + this.editingPanel.DoubleClick += new System.EventHandler(EditingControls_DoubleClick); + this.editingPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingPanel.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingPanel.MouseEnter += new System.EventHandler(EditingControls_MouseEnter); + this.editingPanel.MouseLeave += new System.EventHandler(EditingControls_MouseLeave); + this.editingPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + + Debug.Assert(this.editingControl != null); + this.editingControl.Click += new System.EventHandler(EditingControls_Click); + this.editingControl.DoubleClick += new System.EventHandler(EditingControls_DoubleClick); + this.editingControl.MouseClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseClick); + this.editingControl.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDoubleClick); + this.editingControl.MouseDown += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseDown); + this.editingControl.MouseEnter += new System.EventHandler(EditingControls_MouseEnter); + this.editingControl.MouseLeave += new System.EventHandler(EditingControls_MouseLeave); + this.editingControl.MouseMove += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseMove); + this.editingControl.MouseUp += new System.Windows.Forms.MouseEventHandler(EditingControls_MouseUp); + } + + private void WireScrollBarsEvents() + { + if (this.horizScrollBar != null) + { + this.horizScrollBar.MouseEnter += new System.EventHandler(ScrollBar_MouseEnter); + this.horizScrollBar.MouseLeave += new System.EventHandler(ScrollBar_MouseLeave); + } + if (this.vertScrollBar != null) + { + this.vertScrollBar.MouseEnter += new System.EventHandler(ScrollBar_MouseEnter); + this.vertScrollBar.MouseLeave += new System.EventHandler(ScrollBar_MouseLeave); + } + } + + /// + /// Handles the WM_CONTEXTMENU message + /// + /// + internal override void WmContextMenu(ref Message m) + { + ContextMenuStrip contextMenuStrip; + int x = unchecked( (int)(short)(long)m.LParam); + int y = unchecked( (int) (long)m.LParam) >> 16; + Point client; + bool keyboardActivated = false; + // lparam will be exactly -1 when the user invokes the context menu + // with the keyboard. + // + if (unchecked( (int) (long)m.LParam) == -1) + { + keyboardActivated = true; + client = new Point(this.Width/2, this.Height/2); + contextMenuStrip = (ContextMenuStrip) this.ContextMenuStrip; + } + else + { + client = PointToClientInternal(new Point(x, y)); + HitTestInfo hti = HitTest(client.X, client.Y); + DataGridViewCell dataGridViewCell = null; + switch (hti.Type) + { + case DataGridViewHitTestType.Cell: + dataGridViewCell = this.Rows.SharedRow(hti.row).Cells[hti.col]; + break; + case DataGridViewHitTestType.ColumnHeader: + Debug.Assert(hti.row == -1); + dataGridViewCell = this.Columns[hti.col].HeaderCell; + break; + case DataGridViewHitTestType.RowHeader: + Debug.Assert(hti.col == -1); + dataGridViewCell = this.Rows.SharedRow(hti.row).HeaderCell; + break; + case DataGridViewHitTestType.TopLeftHeader: + Debug.Assert(hti.row == -1); + Debug.Assert(hti.col == -1); + dataGridViewCell = this.TopLeftHeaderCell; + break; + } + if (dataGridViewCell != null) + { + contextMenuStrip = dataGridViewCell.GetInheritedContextMenuStrip(hti.row); + } + else + { + contextMenuStrip = (ContextMenuStrip) this.ContextMenuStrip; + } + } + + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (contextMenuStrip != null && this.ClientRectangle.Contains(client)) + { + contextMenuStrip.ShowInternal(this, client, keyboardActivated); + } + else + { + DefWndProc(ref m); + } + } + + /// + /// Handles the WM_GETDLGCODE message + /// + private void WmGetDlgCode(ref Message m) + { + m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTARROWS | NativeMethods.DLGC_WANTCHARS); + + Keys modifierKeys = ModifierKeys; + if (GetTabKeyEffective((modifierKeys & Keys.Shift) == Keys.Shift, (modifierKeys & Keys.Control) == Keys.Control)) + { + m.Result = (IntPtr) ((long) m.Result | NativeMethods.DLGC_WANTTAB); + } + } + + private unsafe bool WmNotify(ref Message m) + { + if (m.LParam == IntPtr.Zero) + { + return false; + } + + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam; + if (nmhdr->code == NativeMethods.TTN_GETDISPINFO && !DesignMode) + { + string toolTip = this.ToolTipPrivate; + + if (!String.IsNullOrEmpty(toolTip)) + { + // MSDN: Setting the max width has the added benefit of enabling multiline tool tips! + UnsafeNativeMethods.SendMessage(new HandleRef(this, nmhdr->hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + + ttt.lpszText = toolTip; + + if (this.RightToLeft == RightToLeft.Yes) + { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + Marshal.StructureToPtr(ttt, m.LParam, false); + return true; + } + } + + return false; + } + + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_GETDLGCODE: + WmGetDlgCode(ref m); + return; + case NativeMethods.WM_LBUTTONDBLCLK: + case NativeMethods.WM_LBUTTONDOWN: + // If the OnEnter procedure is called, it's because of a mouse down event, and not a TAB key. + this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] = true; + try + { + base.WndProc(ref m); + } + finally + { + this.dataGridViewOper[DATAGRIDVIEWOPER_inMouseDown] = false; + } + return; + case NativeMethods.WM_NOTIFY: + if (WmNotify(ref m)) + { + // we are done - skip default handling + return; + } + break; + + case NativeMethods.WM_IME_STARTCOMPOSITION: + case NativeMethods.WM_IME_COMPOSITION: + if (this.editingControl != null) + { + // Make sure that the first character is forwarded to the editing control. + this.editingControl.SendMessage(m.Msg, m.WParam, m.LParam); + } + break; + } + + base.WndProc(ref m); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewPaintParts.cs b/WindowsForms/Managed/System/WinForms/DataGridViewPaintParts.cs new file mode 100644 index 000000000..93fbc012b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewPaintParts.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + [Flags] + public enum DataGridViewPaintParts + { + /// + None = 0x00, + /// + All = 0x7F, + /// + Background = 0x01, + /// + Border = 0x02, + /// + ContentBackground = 0x04, + /// + ContentForeground = 0x08, + /// + ErrorIcon = 0x10, + /// + Focus = 0x20, + /// + SelectionBackground = 0x40 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRow.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRow.cs new file mode 100644 index 000000000..6529a7393 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRow.cs @@ -0,0 +1,2569 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.Diagnostics; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.VisualStyles; + using System.Security.Permissions; + using System.Globalization; + + /// + /// + /// Identifies a row in the dataGridView. + /// + [ + TypeConverterAttribute(typeof(DataGridViewRowConverter)) + ] + public class DataGridViewRow : DataGridViewBand + { + private static Type rowType = typeof(DataGridViewRow); + private static readonly int PropRowErrorText = PropertyStore.CreateKey(); + private static readonly int PropRowAccessibilityObject = PropertyStore.CreateKey(); + + private const DataGridViewAutoSizeRowCriteriaInternal invalidDataGridViewAutoSizeRowCriteriaInternalMask = ~(DataGridViewAutoSizeRowCriteriaInternal.Header | DataGridViewAutoSizeRowCriteriaInternal.AllColumns); + + internal const int defaultMinRowThickness = 3; + + private DataGridViewCellCollection rowCells; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DataGridViewRow() : base() + { + this.bandIsRow = true; + this.MinimumThickness = defaultMinRowThickness; + this.Thickness = Control.DefaultFont.Height + 9; + } + + /// + [ + Browsable(false) + ] + public AccessibleObject AccessibilityObject + { + get + { + AccessibleObject result = (AccessibleObject) this.Properties.GetObject(PropRowAccessibilityObject); + + if (result == null) + { + result = this.CreateAccessibilityInstance(); + this.Properties.SetObject(PropRowAccessibilityObject, result); + } + + return result; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public DataGridViewCellCollection Cells + { + get + { + if (this.rowCells == null) + { + this.rowCells = CreateCellsInstance(); + } + return this.rowCells; + } + } + + /// + [ + DefaultValue(null), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_RowContextMenuStripDescr) + ] + public override ContextMenuStrip ContextMenuStrip + { + get + { + return base.ContextMenuStrip; + } + set + { + base.ContextMenuStrip = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public object DataBoundItem + { + get + { + if (this.DataGridView != null && + this.DataGridView.DataConnection != null && + this.Index > -1 && + this.Index != this.DataGridView.NewRowIndex) + { + return this.DataGridView.DataConnection.CurrencyManager[this.Index]; + } + else + { + return null; + } + } + } + + /// + [ + Browsable(true), + NotifyParentProperty(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowDefaultCellStyleDescr) + ] + public override DataGridViewCellStyle DefaultCellStyle + { + get + { + return base.DefaultCellStyle; + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "DefaultCellStyle")); + } + base.DefaultCellStyle = value; + } + } + + /// + [ + Browsable(false) + ] + public override bool Displayed + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "Displayed")); + } + return GetDisplayed(this.Index); + } + } + + /// + [ + DefaultValue(0), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowDividerHeightDescr) + ] + public int DividerHeight + { + get + { + return this.DividerThickness; + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "DividerHeight")); + } + this.DividerThickness = value; + } + } + + /// + [ + DefaultValue(""), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowErrorTextDescr) + ] + public string ErrorText + { + get + { + Debug.Assert(this.Index >= -1); + return GetErrorText(this.Index); + } + set + { + this.ErrorTextInternal = value; + } + } + + private string ErrorTextInternal + { + get + { + object errorText = this.Properties.GetObject(PropRowErrorText); + return (errorText == null) ? string.Empty : (string)errorText; + } + set + { + string errorText = this.ErrorTextInternal; + if (!string.IsNullOrEmpty(value) || this.Properties.ContainsObject(PropRowErrorText)) + { + this.Properties.SetObject(PropRowErrorText, value); + } + if (this.DataGridView != null && !errorText.Equals(this.ErrorTextInternal)) + { + this.DataGridView.OnRowErrorTextChanged(this); + } + } + } + + /// + [ + Browsable(false), + ] + public override bool Frozen + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "Frozen")); + } + return GetFrozen(this.Index); + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "Frozen")); + } + base.Frozen = value; + } + } + + internal bool HasErrorText + { + get + { + return this.Properties.ContainsObject(PropRowErrorText) && this.Properties.GetObject(PropRowErrorText) != null; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public DataGridViewRowHeaderCell HeaderCell + { + get + { + return (DataGridViewRowHeaderCell) base.HeaderCellCore; + } + set + { + base.HeaderCellCore = value; + } + } + + /// + [ + DefaultValue(22), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.DataGridView_RowHeightDescr) + ] + public int Height + { + get + { + return this.Thickness; + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "Height")); + } + this.Thickness = value; + } + } + + /// + public override DataGridViewCellStyle InheritedStyle + { + get + { + if (this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "InheritedStyle")); + } + + DataGridViewCellStyle inheritedRowStyle = new DataGridViewCellStyle(); + BuildInheritedRowStyle(this.Index, inheritedRowStyle); + return inheritedRowStyle; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool IsNewRow + { + get + { + return this.DataGridView != null && this.DataGridView.NewRowIndex == this.Index; + } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int MinimumHeight + { + get + { + return this.MinimumThickness; + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "MinimumHeight")); + } + this.MinimumThickness = value; + } + } + + /// + [ + Browsable(true), + DefaultValue(false), + NotifyParentProperty(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_RowReadOnlyDescr) + ] + public override bool ReadOnly + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "ReadOnly")); + } + return GetReadOnly(this.Index); + } + set + { + base.ReadOnly = value; + } + } + + /// + [ + NotifyParentProperty(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_RowResizableDescr) + ] + public override DataGridViewTriState Resizable + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "Resizable")); + } + return GetResizable(this.Index); + } + set + { + base.Resizable = value; + } + } + + /// + public override bool Selected + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "Selected")); + } + return GetSelected(this.Index); + } + set + { + base.Selected = value; + } + } + + /// + public override DataGridViewElementStates State + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "State")); + } + return GetState(this.Index); + } + } + + /// + [ + Browsable(false) + ] + public override bool Visible + { + get + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertyGetOnSharedRow, "Visible")); + } + return GetVisible(this.Index); + } + set + { + if (this.DataGridView != null && this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidPropertySetOnSharedRow, "Visible")); + } + base.Visible = value; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual DataGridViewAdvancedBorderStyle AdjustRowHeaderBorderStyle(DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStyleInput, + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + if (this.DataGridView != null && this.DataGridView.ApplyVisualStylesToHeaderCells) + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.Inset: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Outset: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + if (isFirstDisplayedRow && !this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Single; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + } + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.None; + return dataGridViewAdvancedBorderStylePlaceholder; + } + } + else + { + switch (dataGridViewAdvancedBorderStyleInput.All) + { + case DataGridViewAdvancedCellBorderStyle.Inset: + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + } + break; + + case DataGridViewAdvancedCellBorderStyle.Outset: + if (isFirstDisplayedRow && singleHorizontalBorderAdded) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + } + break; + + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + if (isFirstDisplayedRow) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = this.DataGridView.ColumnHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetPartial; + } + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = isLastVisibleRow ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetPartial; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + if (isFirstDisplayedRow) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = this.DataGridView.ColumnHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Outset : DataGridViewAdvancedCellBorderStyle.OutsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Outset; + } + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.InsetDouble: + if (this.DataGridView != null && this.DataGridView.RightToLeftInternal) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + if (isFirstDisplayedRow) + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = this.DataGridView.ColumnHeadersVisible ? DataGridViewAdvancedCellBorderStyle.Inset : DataGridViewAdvancedCellBorderStyle.InsetDouble; + } + else + { + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Inset; + } + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset; + return dataGridViewAdvancedBorderStylePlaceholder; + + case DataGridViewAdvancedCellBorderStyle.Single: + if (!isFirstDisplayedRow || this.DataGridView.ColumnHeadersVisible) + { + dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.None; + dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single; + dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single; + return dataGridViewAdvancedBorderStylePlaceholder; + } + break; + } + } + return dataGridViewAdvancedBorderStyleInput; + } + + private void BuildInheritedRowHeaderCellStyle(DataGridViewCellStyle inheritedCellStyle) + { + Debug.Assert(inheritedCellStyle != null); + + DataGridViewCellStyle cellStyle = null; + if (this.HeaderCell.HasStyle) + { + cellStyle = this.HeaderCell.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle rowHeadersStyle = this.DataGridView.RowHeadersDefaultCellStyle; + Debug.Assert(rowHeadersStyle != null); + + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = cellStyle.BackColor; + } + else if (!rowHeadersStyle.BackColor.IsEmpty) + { + inheritedCellStyle.BackColor = rowHeadersStyle.BackColor; + } + else + { + inheritedCellStyle.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = cellStyle.ForeColor; + } + else if (!rowHeadersStyle.ForeColor.IsEmpty) + { + inheritedCellStyle.ForeColor = rowHeadersStyle.ForeColor; + } + else + { + inheritedCellStyle.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (!rowHeadersStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyle.SelectionBackColor = rowHeadersStyle.SelectionBackColor; + } + else + { + inheritedCellStyle.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (!rowHeadersStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyle.SelectionForeColor = rowHeadersStyle.SelectionForeColor; + } + else + { + inheritedCellStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyle.Font = cellStyle.Font; + } + else if (rowHeadersStyle.Font != null) + { + inheritedCellStyle.Font = rowHeadersStyle.Font; + } + else + { + inheritedCellStyle.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = cellStyle.NullValue; + } + else if (!rowHeadersStyle.IsNullValueDefault) + { + inheritedCellStyle.NullValue = rowHeadersStyle.NullValue; + } + else + { + inheritedCellStyle.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (!rowHeadersStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyle.DataSourceNullValue = rowHeadersStyle.DataSourceNullValue; + } + else + { + inheritedCellStyle.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyle.Format = cellStyle.Format; + } + else if (rowHeadersStyle.Format.Length != 0) + { + inheritedCellStyle.Format = rowHeadersStyle.Format; + } + else + { + inheritedCellStyle.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = cellStyle.FormatProvider; + } + else if (!rowHeadersStyle.IsFormatProviderDefault) + { + inheritedCellStyle.FormatProvider = rowHeadersStyle.FormatProvider; + } + else + { + inheritedCellStyle.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = cellStyle.Alignment; + } + else if (rowHeadersStyle != null && rowHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyle.AlignmentInternal = rowHeadersStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyle.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = cellStyle.WrapMode; + } + else if (rowHeadersStyle != null && rowHeadersStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyle.WrapModeInternal = rowHeadersStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyle.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyle.Tag = cellStyle.Tag; + } + else if (rowHeadersStyle.Tag != null) + { + inheritedCellStyle.Tag = rowHeadersStyle.Tag; + } + else + { + inheritedCellStyle.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = cellStyle.Padding; + } + else if (rowHeadersStyle.Padding != Padding.Empty) + { + inheritedCellStyle.PaddingInternal = rowHeadersStyle.Padding; + } + else + { + inheritedCellStyle.PaddingInternal = dataGridViewStyle.Padding; + } + } + + private void BuildInheritedRowStyle(int rowIndex, DataGridViewCellStyle inheritedRowStyle) + { + Debug.Assert(inheritedRowStyle != null); + Debug.Assert(rowIndex >= 0); + Debug.Assert(this.DataGridView != null); + + DataGridViewCellStyle rowStyle = null; + if (this.HasDefaultCellStyle) + { + rowStyle = this.DefaultCellStyle; + Debug.Assert(rowStyle != null); + } + + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + DataGridViewCellStyle rowsDefaultCellStyle = this.DataGridView.RowsDefaultCellStyle; + Debug.Assert(rowsDefaultCellStyle != null); + + DataGridViewCellStyle alternatingRowsDefaultCellStyle = this.DataGridView.AlternatingRowsDefaultCellStyle; + Debug.Assert(alternatingRowsDefaultCellStyle != null); + + if (rowStyle != null && !rowStyle.BackColor.IsEmpty) + { + inheritedRowStyle.BackColor = rowStyle.BackColor; + } + else if (!rowsDefaultCellStyle.BackColor.IsEmpty && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.BackColor.IsEmpty)) + { + inheritedRowStyle.BackColor = rowsDefaultCellStyle.BackColor; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.BackColor.IsEmpty) + { + inheritedRowStyle.BackColor = alternatingRowsDefaultCellStyle.BackColor; + } + else + { + inheritedRowStyle.BackColor = dataGridViewStyle.BackColor; + } + + if (rowStyle != null && !rowStyle.ForeColor.IsEmpty) + { + inheritedRowStyle.ForeColor = rowStyle.ForeColor; + } + else if (!rowsDefaultCellStyle.ForeColor.IsEmpty && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.ForeColor.IsEmpty)) + { + inheritedRowStyle.ForeColor = rowsDefaultCellStyle.ForeColor; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.ForeColor.IsEmpty) + { + inheritedRowStyle.ForeColor = alternatingRowsDefaultCellStyle.ForeColor; + } + else + { + inheritedRowStyle.ForeColor = dataGridViewStyle.ForeColor; + } + + if (rowStyle != null && !rowStyle.SelectionBackColor.IsEmpty) + { + inheritedRowStyle.SelectionBackColor = rowStyle.SelectionBackColor; + } + else if (!rowsDefaultCellStyle.SelectionBackColor.IsEmpty && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.SelectionBackColor.IsEmpty)) + { + inheritedRowStyle.SelectionBackColor = rowsDefaultCellStyle.SelectionBackColor; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.SelectionBackColor.IsEmpty) + { + inheritedRowStyle.SelectionBackColor = alternatingRowsDefaultCellStyle.SelectionBackColor; + } + else + { + inheritedRowStyle.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (rowStyle != null && !rowStyle.SelectionForeColor.IsEmpty) + { + inheritedRowStyle.SelectionForeColor = rowStyle.SelectionForeColor; + } + else if (!rowsDefaultCellStyle.SelectionForeColor.IsEmpty && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.SelectionForeColor.IsEmpty)) + { + inheritedRowStyle.SelectionForeColor = rowsDefaultCellStyle.SelectionForeColor; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.SelectionForeColor.IsEmpty) + { + inheritedRowStyle.SelectionForeColor = alternatingRowsDefaultCellStyle.SelectionForeColor; + } + else + { + inheritedRowStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + + if (rowStyle != null && rowStyle.Font != null) + { + inheritedRowStyle.Font = rowStyle.Font; + } + else if (rowsDefaultCellStyle.Font != null && + (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.Font == null)) + { + inheritedRowStyle.Font = rowsDefaultCellStyle.Font; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.Font != null) + { + inheritedRowStyle.Font = alternatingRowsDefaultCellStyle.Font; + } + else + { + inheritedRowStyle.Font = dataGridViewStyle.Font; + } + + if (rowStyle != null && !rowStyle.IsNullValueDefault) + { + inheritedRowStyle.NullValue = rowStyle.NullValue; + } + else if (!rowsDefaultCellStyle.IsNullValueDefault && + (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.IsNullValueDefault)) + { + inheritedRowStyle.NullValue = rowsDefaultCellStyle.NullValue; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.IsNullValueDefault) + { + inheritedRowStyle.NullValue = alternatingRowsDefaultCellStyle.NullValue; + } + else + { + inheritedRowStyle.NullValue = dataGridViewStyle.NullValue; + } + + if (rowStyle != null && !rowStyle.IsDataSourceNullValueDefault) + { + inheritedRowStyle.DataSourceNullValue = rowStyle.DataSourceNullValue; + } + else if (!rowsDefaultCellStyle.IsDataSourceNullValueDefault && + (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.IsDataSourceNullValueDefault)) + { + inheritedRowStyle.DataSourceNullValue = rowsDefaultCellStyle.DataSourceNullValue; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.IsDataSourceNullValueDefault) + { + inheritedRowStyle.DataSourceNullValue = alternatingRowsDefaultCellStyle.DataSourceNullValue; + } + else + { + inheritedRowStyle.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (rowStyle != null && rowStyle.Format.Length != 0) + { + inheritedRowStyle.Format = rowStyle.Format; + } + else if (rowsDefaultCellStyle.Format.Length != 0 && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.Format.Length == 0)) + { + inheritedRowStyle.Format = rowsDefaultCellStyle.Format; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.Format.Length != 0) + { + inheritedRowStyle.Format = alternatingRowsDefaultCellStyle.Format; + } + else + { + inheritedRowStyle.Format = dataGridViewStyle.Format; + } + + if (rowStyle != null && !rowStyle.IsFormatProviderDefault) + { + inheritedRowStyle.FormatProvider = rowStyle.FormatProvider; + } + else if (!rowsDefaultCellStyle.IsFormatProviderDefault && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.IsFormatProviderDefault)) + { + inheritedRowStyle.FormatProvider = rowsDefaultCellStyle.FormatProvider; + } + else if (rowIndex % 2 == 1 && !alternatingRowsDefaultCellStyle.IsFormatProviderDefault) + { + inheritedRowStyle.FormatProvider = alternatingRowsDefaultCellStyle.FormatProvider; + } + else + { + inheritedRowStyle.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (rowStyle != null && rowStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedRowStyle.AlignmentInternal = rowStyle.Alignment; + } + else if (rowsDefaultCellStyle.Alignment != DataGridViewContentAlignment.NotSet && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.Alignment == DataGridViewContentAlignment.NotSet)) + { + inheritedRowStyle.AlignmentInternal = rowsDefaultCellStyle.Alignment; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedRowStyle.AlignmentInternal = alternatingRowsDefaultCellStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedRowStyle.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (rowStyle != null && rowStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedRowStyle.WrapModeInternal = rowStyle.WrapMode; + } + else if (rowsDefaultCellStyle.WrapMode != DataGridViewTriState.NotSet && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.WrapMode == DataGridViewTriState.NotSet)) + { + inheritedRowStyle.WrapModeInternal = rowsDefaultCellStyle.WrapMode; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedRowStyle.WrapModeInternal = alternatingRowsDefaultCellStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedRowStyle.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (rowStyle != null && rowStyle.Tag != null) + { + inheritedRowStyle.Tag = rowStyle.Tag; + } + else if (rowsDefaultCellStyle.Tag != null && (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.Tag == null)) + { + inheritedRowStyle.Tag = rowsDefaultCellStyle.Tag; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.Tag != null) + { + inheritedRowStyle.Tag = alternatingRowsDefaultCellStyle.Tag; + } + else + { + inheritedRowStyle.Tag = dataGridViewStyle.Tag; + } + + if (rowStyle != null && rowStyle.Padding != Padding.Empty) + { + inheritedRowStyle.PaddingInternal = rowStyle.Padding; + } + else if (rowsDefaultCellStyle.Padding != Padding.Empty && + (rowIndex % 2 == 0 || alternatingRowsDefaultCellStyle.Padding == Padding.Empty)) + { + inheritedRowStyle.PaddingInternal = rowsDefaultCellStyle.Padding; + } + else if (rowIndex % 2 == 1 && alternatingRowsDefaultCellStyle.Padding != Padding.Empty) + { + inheritedRowStyle.PaddingInternal = alternatingRowsDefaultCellStyle.Padding; + } + else + { + inheritedRowStyle.PaddingInternal = dataGridViewStyle.Padding; + } + } + + /// + public override object Clone() + { + DataGridViewRow dataGridViewRow; + Type thisType = this.GetType(); + + if (thisType == rowType) //performance improvement + { + dataGridViewRow = new DataGridViewRow(); + } + else + { + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + dataGridViewRow = (DataGridViewRow) System.Activator.CreateInstance(thisType); + } + if (dataGridViewRow != null) + { + base.CloneInternal(dataGridViewRow); + if (this.HasErrorText) + { + dataGridViewRow.ErrorText = this.ErrorTextInternal; + } + if (this.HasHeaderCell) + { + dataGridViewRow.HeaderCell = (DataGridViewRowHeaderCell) this.HeaderCell.Clone(); + } + dataGridViewRow.CloneCells(this); + } + return dataGridViewRow; + } + + private void CloneCells(DataGridViewRow rowTemplate) + { + int cellsCount = rowTemplate.Cells.Count; + if (cellsCount > 0) + { + DataGridViewCell[] cells = new DataGridViewCell[cellsCount]; + for (int i = 0; i < cellsCount; i ++) + { + DataGridViewCell dataGridViewCell = rowTemplate.Cells[i]; + DataGridViewCell dgvcNew = (DataGridViewCell) dataGridViewCell.Clone(); + cells[i] = dgvcNew; + } + this.Cells.AddRange(cells); + } + + // SECREVIEW: VSWhidbey 332064: if this code is re-enabled and tc.GetType().Assembly == Microsoft, + // consider adding a Demand for ReflectionPermission. + + /* object[] args = new object[1]; + foreach (DataGridViewCell tc in bandTemplate.Cells) + { + args[0] = tc; + + DataGridViewCell dgvcNew = (DataGridViewCell) System.Activator.CreateInstance(tc.GetType(), args); + Cells.Add(dgvcNew); + } */ + } + + /// + protected virtual AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewRowAccessibleObject(this); + } + + /// + public void CreateCells(DataGridView dataGridView) + { + if (dataGridView == null) + { + throw new ArgumentNullException("dataGridView"); + } + if (this.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + DataGridViewCellCollection cells = this.Cells; + // Clearing up the potential existing cells. We fill up the cells collection from scratch. + cells.Clear(); + DataGridViewColumnCollection dataGridViewColumns = dataGridView.Columns; + foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + if (dataGridViewColumn.CellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_AColumnHasNoCellTemplate)); + } + DataGridViewCell dgvcNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone(); + cells.Add(dgvcNew); + } + } + + /// + public void CreateCells(DataGridView dataGridView, params object[] values) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + /* Intentionally not being strict about this. We just take what we get. + if (dataGridView.Columns.Count != values.Length) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongValueCount), "values"); + }*/ + + CreateCells(dataGridView); + + Debug.Assert(this.Cells.Count == dataGridView.Columns.Count); + SetValuesInternal(values); + } + + /// + /// + /// Constructs the new instance of the Cells collection objects. Subclasses + /// should not call base.CreateCellsInstance. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual DataGridViewCellCollection CreateCellsInstance() + { + return new DataGridViewCellCollection(this); + } + + internal void DetachFromDataGridView() + { + if (this.DataGridView != null) + { + this.DataGridViewInternal = null; + this.IndexInternal = -1; + if (this.HasHeaderCell) + { + this.HeaderCell.DataGridViewInternal = null; + } + foreach (DataGridViewCell dataGridViewCell in this.Cells) + { + dataGridViewCell.DataGridViewInternal = null; + if (dataGridViewCell.Selected) + { + dataGridViewCell.SelectedInternal = false; + } + } + if (this.Selected) + { + this.SelectedInternal = false; + } + } + Debug.Assert(this.Index == -1); + Debug.Assert(!this.Selected); + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal virtual void DrawFocus(Graphics graphics, + Rectangle clipBounds, + Rectangle bounds, + int rowIndex, + DataGridViewElementStates rowState, + DataGridViewCellStyle cellStyle, + bool cellsPaintSelectionBackground) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowDoesNotYetBelongToDataGridView)); + } + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + Color backColor; + if (cellsPaintSelectionBackground && (rowState & DataGridViewElementStates.Selected) != 0) + { + backColor = cellStyle.SelectionBackColor; + } + else + { + backColor = cellStyle.BackColor; + } + ControlPaint.DrawFocusRectangle(graphics, bounds, Color.Empty, backColor); + } + + /// + public ContextMenuStrip GetContextMenuStrip(int rowIndex) + { + ContextMenuStrip contextMenuStrip = this.ContextMenuStripInternal; + if (this.DataGridView != null) + { + if (rowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedRow)); + } + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.DataGridView.VirtualMode || this.DataGridView.DataSource != null) + { + contextMenuStrip = this.DataGridView.OnRowContextMenuStripNeeded(rowIndex, contextMenuStrip); + } + } + return contextMenuStrip; + } + + internal bool GetDisplayed(int rowIndex) + { + // You would think that only attached and visible rows can be displayed. + // Actually this assertion is wrong when the row is being deleted. + // Debug.Assert(!displayed || (this.DataGridView != null && this.DataGridView.Visible && GetVisible(rowIndex))); + return (GetState(rowIndex) & DataGridViewElementStates.Displayed) != 0; + } + + /// + public string GetErrorText(int rowIndex) + { + string errorText = this.ErrorTextInternal; + if (this.DataGridView != null) + { + if (rowIndex == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedRow)); + } + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (string.IsNullOrEmpty(errorText) && + this.DataGridView.DataSource != null && + rowIndex != this.DataGridView.NewRowIndex) + { + errorText = this.DataGridView.DataConnection.GetError(rowIndex); + } + if (this.DataGridView.DataSource != null || this.DataGridView.VirtualMode) + { + errorText = this.DataGridView.OnRowErrorTextNeeded(rowIndex, errorText); + } + } + return errorText; + } + + internal bool GetFrozen(int rowIndex) + { + return (GetState(rowIndex) & DataGridViewElementStates.Frozen) != 0; + } + + internal int GetHeight(int rowIndex) + { + Debug.Assert(rowIndex >= -1); + int height, minimumHeight; + GetHeightInfo(rowIndex, out height, out minimumHeight); + return height; + } + + internal int GetMinimumHeight(int rowIndex) + { + Debug.Assert(rowIndex >= -1); + int height, minimumHeight; + GetHeightInfo(rowIndex, out height, out minimumHeight); + return minimumHeight; + } + + /// + public virtual int GetPreferredHeight(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth) + { + // not using IsEnumValid here because this is a flags enum, using mask instead. + if (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode & invalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0) + { + throw new InvalidEnumArgumentException("autoSizeRowMode", (int) autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode)); + } + if (!(this.DataGridView == null || (rowIndex >= 0 && rowIndex < this.DataGridView.Rows.Count))) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.DataGridView == null) + { + return -1; + } + + int preferredRowThickness = 0, preferredCellThickness; + // take into account the preferred height of the header cell if displayed and cared about + if (this.DataGridView.RowHeadersVisible && + (((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode) & DataGridViewAutoSizeRowCriteriaInternal.Header) != 0) + { + if (fixedWidth || + this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing || + this.DataGridView.RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.DisableResizing) + { + preferredRowThickness = Math.Max(preferredRowThickness, this.HeaderCell.GetPreferredHeight(rowIndex, this.DataGridView.RowHeadersWidth)); + } + else + { + preferredRowThickness = Math.Max(preferredRowThickness, this.HeaderCell.GetPreferredSize(rowIndex).Height); + } + } + if ((((DataGridViewAutoSizeRowCriteriaInternal) autoSizeRowMode) & DataGridViewAutoSizeRowCriteriaInternal.AllColumns) != 0) + { + foreach (DataGridViewCell dataGridViewCell in this.Cells) + { + DataGridViewColumn dataGridViewColumn = this.DataGridView.Columns[dataGridViewCell.ColumnIndex]; + if (dataGridViewColumn.Visible) + { + if (fixedWidth || + ((((DataGridViewAutoSizeColumnCriteriaInternal) dataGridViewColumn.InheritedAutoSizeMode) & (DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows)) == 0)) + { + preferredCellThickness = dataGridViewCell.GetPreferredHeight(rowIndex, dataGridViewColumn.Width); + } + else + { + preferredCellThickness = dataGridViewCell.GetPreferredSize(rowIndex).Height; + } + if (preferredRowThickness < preferredCellThickness) + { + preferredRowThickness = preferredCellThickness; + } + } + } + } + return preferredRowThickness; + } + + internal bool GetReadOnly(int rowIndex) + { + return (this.GetState(rowIndex) & DataGridViewElementStates.ReadOnly) != 0 || + (this.DataGridView != null && this.DataGridView.ReadOnly); + } + + internal DataGridViewTriState GetResizable(int rowIndex) + { + if ((GetState(rowIndex) & DataGridViewElementStates.ResizableSet) != 0) + { + return ((GetState(rowIndex) & DataGridViewElementStates.Resizable) != 0) ? DataGridViewTriState.True : DataGridViewTriState.False; + } + if (this.DataGridView != null) + { + return this.DataGridView.AllowUserToResizeRows ? DataGridViewTriState.True : DataGridViewTriState.False; + } + else + { + return DataGridViewTriState.NotSet; + } + } + + internal bool GetSelected(int rowIndex) + { + return (GetState(rowIndex) & DataGridViewElementStates.Selected) != 0; + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public virtual DataGridViewElementStates GetState(int rowIndex) + { + if (!(this.DataGridView == null || (rowIndex >= 0 && rowIndex < this.DataGridView.Rows.Count))) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + if (this.DataGridView == null || this.DataGridView.Rows.SharedRow(rowIndex).Index != -1) + { + if (rowIndex != this.Index) + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture))); + } + return base.State; + } + else + { + return this.DataGridView.Rows.GetRowState(rowIndex); + } + } + + internal bool GetVisible(int rowIndex) + { + return (GetState(rowIndex) & DataGridViewElementStates.Visible) != 0; + } + + internal void OnSharedStateChanged(int sharedRowIndex, DataGridViewElementStates elementState) + { + Debug.Assert(this.DataGridView != null); + this.DataGridView.Rows.InvalidateCachedRowCount(elementState); + this.DataGridView.Rows.InvalidateCachedRowsHeight(elementState); + this.DataGridView.OnDataGridViewElementStateChanged(this, sharedRowIndex, elementState); + } + + internal void OnSharedStateChanging(int sharedRowIndex, DataGridViewElementStates elementState) + { + Debug.Assert(this.DataGridView != null); + this.DataGridView.OnDataGridViewElementStateChanging(this, sharedRowIndex, elementState); + } + + /// + protected internal virtual void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowDoesNotYetBelongToDataGridView)); + } + DataGridView dataGridView = this.DataGridView; + Rectangle updatedClipBounds = clipBounds; + DataGridViewRow sharedRow = dataGridView.Rows.SharedRow(rowIndex); + DataGridViewCellStyle inheritedRowStyle = new DataGridViewCellStyle(); + BuildInheritedRowStyle(rowIndex, inheritedRowStyle); + DataGridViewRowPrePaintEventArgs dgvrprepe = dataGridView.RowPrePaintEventArgs; + dgvrprepe.SetProperties(graphics, + clipBounds, + rowBounds, + rowIndex, + rowState, + sharedRow.GetErrorText(rowIndex), + inheritedRowStyle, + isFirstDisplayedRow, + isLastVisibleRow); + dataGridView.OnRowPrePaint(dgvrprepe); + if (dgvrprepe.Handled) + { + return; + } + DataGridViewPaintParts paintParts = dgvrprepe.PaintParts; + updatedClipBounds = dgvrprepe.ClipBounds; + + // first paint the potential row header + PaintHeader(graphics, + updatedClipBounds, + rowBounds, + rowIndex, + rowState, + isFirstDisplayedRow, + isLastVisibleRow, + paintParts); + + // then paint the inner cells + PaintCells(graphics, + updatedClipBounds, + rowBounds, + rowIndex, + rowState, + isFirstDisplayedRow, + isLastVisibleRow, + paintParts); + + sharedRow = dataGridView.Rows.SharedRow(rowIndex); + BuildInheritedRowStyle(rowIndex, inheritedRowStyle); + DataGridViewRowPostPaintEventArgs dgvrpostpe = dataGridView.RowPostPaintEventArgs; + dgvrpostpe.SetProperties(graphics, + updatedClipBounds, + rowBounds, + rowIndex, + rowState, + sharedRow.GetErrorText(rowIndex), + inheritedRowStyle, + isFirstDisplayedRow, + isLastVisibleRow); + dataGridView.OnRowPostPaint(dgvrpostpe); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected internal virtual void PaintCells(Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + bool isFirstDisplayedRow, + bool isLastVisibleRow, + DataGridViewPaintParts paintParts) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowDoesNotYetBelongToDataGridView)); + } + if ((int) paintParts < (int) DataGridViewPaintParts.None || (int) paintParts > (int) DataGridViewPaintParts.All) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewPaintPartsCombination, "paintParts")); + } + + DataGridView dataGridView = this.DataGridView; + Rectangle cellBounds = rowBounds; + int cx = (dataGridView.RowHeadersVisible ? dataGridView.RowHeadersWidth : 0); + bool isFirstDisplayedColumn = true; + DataGridViewElementStates cellState = DataGridViewElementStates.None; + DataGridViewCell cell; + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + DataGridViewColumn dataGridViewColumnNext = null; + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + + // first paint the potential visible frozen cells + DataGridViewColumn dataGridViewColumn = dataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen); + while (dataGridViewColumn != null) + { + cell = this.Cells[dataGridViewColumn.Index]; + cellBounds.Width = dataGridViewColumn.Thickness; + if (dataGridView.SingleVerticalBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (dataGridView.RightToLeftInternal) + { + cellBounds.X = rowBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = rowBounds.X + cx; + } + + dataGridViewColumnNext = dataGridView.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, + DataGridViewElementStates.None); + + if (clipBounds.IntersectsWith(cellBounds)) + { + cellState = cell.CellStateFromColumnRowStates(rowState); + if (this.Index != -1) + { + cellState |= cell.State; + } + + cell.GetInheritedStyle(inheritedCellStyle, rowIndex, true); + + dgvabsEffective = cell.AdjustCellBorderStyle(dataGridView.AdvancedCellBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + dataGridView.SingleVerticalBorderAdded, + dataGridView.SingleHorizontalBorderAdded, + isFirstDisplayedColumn, + isFirstDisplayedRow); + + cell.PaintWork(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + inheritedCellStyle, + dgvabsEffective, + paintParts); + } + + cx += cellBounds.Width; + if (cx >= rowBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + // then paint the visible scrolling ones + Rectangle dataBounds = rowBounds; + + if (cx < dataBounds.Width) + { + if (dataGridView.FirstDisplayedScrollingColumnIndex >= 0) + { + if (!dataGridView.RightToLeftInternal) + { + dataBounds.X -= dataGridView.FirstDisplayedScrollingColumnHiddenWidth; + } + dataBounds.Width += dataGridView.FirstDisplayedScrollingColumnHiddenWidth; + + Region clipRegion = null; + if (dataGridView.FirstDisplayedScrollingColumnHiddenWidth > 0) + { + clipRegion = graphics.Clip; + Rectangle rowRect = rowBounds; + if (!dataGridView.RightToLeftInternal) + { + rowRect.X += cx; + } + rowRect.Width -= cx; + graphics.SetClip(rowRect); + } + + dataGridViewColumn = (DataGridViewColumn)dataGridView.Columns[dataGridView.FirstDisplayedScrollingColumnIndex]; + Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen); + + while (dataGridViewColumn != null) + { + cell = this.Cells[dataGridViewColumn.Index]; + cellBounds.Width = dataGridViewColumn.Thickness; + if (dataGridView.SingleVerticalBorderAdded && isFirstDisplayedColumn) + { + cellBounds.Width++; + } + Debug.Assert(cellBounds.Width > 0); + if (dataGridView.RightToLeftInternal) + { + cellBounds.X = dataBounds.Right - cx - cellBounds.Width; + } + else + { + cellBounds.X = dataBounds.X + cx; + } + + dataGridViewColumnNext = dataGridView.Columns.GetNextColumn(dataGridViewColumn, + DataGridViewElementStates.Visible, + DataGridViewElementStates.None); + + if (clipBounds.IntersectsWith(cellBounds)) + { + cellState = cell.CellStateFromColumnRowStates(rowState); + if (this.Index != -1) + { + cellState |= cell.State; + } + + cell.GetInheritedStyle(inheritedCellStyle, rowIndex, true); + + dgvabsEffective = cell.AdjustCellBorderStyle(dataGridView.AdvancedCellBorderStyle, dataGridViewAdvancedBorderStylePlaceholder, + dataGridView.SingleVerticalBorderAdded, + dataGridView.SingleHorizontalBorderAdded, + isFirstDisplayedColumn, + isFirstDisplayedRow); + + cell.PaintWork(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + inheritedCellStyle, + dgvabsEffective, + paintParts); + } + + cx += cellBounds.Width; + if (cx >= dataBounds.Width) + { + break; + } + + dataGridViewColumn = dataGridViewColumnNext; + isFirstDisplayedColumn = false; + } + + if (clipRegion != null) + { + graphics.Clip = clipRegion; + clipRegion.Dispose(); + } + } + } + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected internal virtual void PaintHeader(Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + bool isFirstDisplayedRow, + bool isLastVisibleRow, + DataGridViewPaintParts paintParts) + { + if (this.DataGridView == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowDoesNotYetBelongToDataGridView)); + } + + // not using ClientUtils.IsValidEnum here because this is a flags enum. + // everything is valid between 0x0 and 0x7F. + if ((int) paintParts < (int) DataGridViewPaintParts.None || (int) paintParts > (int) DataGridViewPaintParts.All) + { + throw new InvalidEnumArgumentException("paintParts", (int)paintParts, typeof(DataGridViewPaintParts)); + } + DataGridView dataGridView = this.DataGridView; + if (dataGridView.RowHeadersVisible) + { + Rectangle cellBounds = rowBounds; + cellBounds.Width = dataGridView.RowHeadersWidth; + Debug.Assert(cellBounds.Width > 0); + if (dataGridView.RightToLeftInternal) + { + cellBounds.X = rowBounds.Right - cellBounds.Width; + } + if (clipBounds.IntersectsWith(cellBounds)) + { + DataGridViewCellStyle inheritedCellStyle = new DataGridViewCellStyle(); + DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + BuildInheritedRowHeaderCellStyle(inheritedCellStyle); + dgvabsEffective = AdjustRowHeaderBorderStyle(dataGridView.AdvancedRowHeadersBorderStyle, + dataGridViewAdvancedBorderStylePlaceholder, + dataGridView.SingleVerticalBorderAdded, + dataGridView.SingleHorizontalBorderAdded, + isFirstDisplayedRow, + isLastVisibleRow); + this.HeaderCell.PaintWork(graphics, + clipBounds, + cellBounds, + rowIndex, + rowState, + inheritedCellStyle, + dgvabsEffective, + paintParts); + } + } + } + + internal void SetReadOnlyCellCore(DataGridViewCell dataGridViewCell, bool readOnly) + { + Debug.Assert(this.Index == -1); + if (this.ReadOnly && !readOnly) + { + // All cells need to switch to ReadOnly except for dataGridViewCell which needs to be !ReadOnly, + // plus the row become !ReadOnly. + foreach (DataGridViewCell dataGridViewCellTmp in this.Cells) + { + dataGridViewCellTmp.ReadOnlyInternal = true; + } + dataGridViewCell.ReadOnlyInternal = false; + this.ReadOnly = false; + } + else if (!this.ReadOnly && readOnly) + { + // dataGridViewCell alone becomes ReadOnly + dataGridViewCell.ReadOnlyInternal = true; + } + } + + /// + public bool SetValues(params object[] values) + { + if (values == null) + { + throw new ArgumentNullException("values"); + } + + if (this.DataGridView != null) + { + if (this.DataGridView.VirtualMode) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationInVirtualMode)); + } + if (this.Index == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedRow)); + } + } + + return SetValuesInternal(values); + } + + internal bool SetValuesInternal(params object[] values) + { + Debug.Assert(values != null); + bool setResult = true; + DataGridViewCellCollection cells = this.Cells; + int cellCount = cells.Count; + for (int columnIndex=0; columnIndex < cells.Count; columnIndex++) + { + if (columnIndex == values.Length) + { + break; + } + if (!cells[columnIndex].SetValueInternal(this.Index, values[columnIndex])) + { + setResult = false; + } + } + return setResult && values.Length <= cellCount; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(36); + sb.Append("DataGridViewRow { Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + + /// + [ + System.Runtime.InteropServices.ComVisible(true) + ] + protected class DataGridViewRowAccessibleObject : AccessibleObject + { + private int[] runtimeId; + private DataGridViewRow owner; + private DataGridViewSelectedRowCellsAccessibleObject selectedCellsAccessibilityObject = null; + + /// + public DataGridViewRowAccessibleObject() + { + } + + /// + public DataGridViewRowAccessibleObject(DataGridViewRow owner) + { + this.owner = owner; + } + + /// + public override Rectangle Bounds + { + get { + Rectangle rowRect; + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + if (this.owner.Index < this.owner.DataGridView.FirstDisplayedScrollingRowIndex) + { + // the row is scrolled off the DataGridView + // get the Accessible bounds for the following visible row + int visibleRowIndex = this.owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, this.owner.Index); + rowRect = this.ParentPrivate.GetChild(visibleRowIndex + + 1 // + 1 because the first acc obj in the DataGridView is the top row header + + 1).Bounds; // + 1 because we want to get the bounds for the next visible row + + rowRect.Y -= this.owner.Height; + rowRect.Height = this.owner.Height; + + } + else if (this.owner.Index >= this.owner.DataGridView.FirstDisplayedScrollingRowIndex && + this.owner.Index < this.owner.DataGridView.FirstDisplayedScrollingRowIndex + this.owner.DataGridView.DisplayedRowCount(true /*includePartialRow*/)) + { + rowRect = this.owner.DataGridView.GetRowDisplayRectangle(this.owner.Index, false /*cutOverflow*/); + rowRect = this.owner.DataGridView.RectangleToScreen(rowRect); + } + else + { + // the row is scrolled off the DataGridView + // use the Accessible bounds for the previous visible row + int visibleRowIndex = this.owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, this.owner.Index); + + // This is a tricky scenario + // If Visible of Row 0 is false, then visibleRowIndex is not the previous visible row. + // It turns out to be the current row, this will cause a stack overflow. + // We have to prevent this. + if (this.owner.DataGridView.Rows[0].Visible == false) + { + visibleRowIndex--; + } + + // we don't have to decrement the visible row index if the first acc obj in the data grid view is the top column header + if (!this.owner.DataGridView.ColumnHeadersVisible) + { + visibleRowIndex--; + } + + rowRect = this.ParentPrivate.GetChild(visibleRowIndex).Bounds; + rowRect.Y += rowRect.Height; + rowRect.Height = this.owner.Height; + } + + return rowRect; + } + } + + /// + public override string Name + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + return SR.GetString(SR.DataGridView_AccRowName, this.owner.Index.ToString(CultureInfo.CurrentCulture)); + } + } + + /// + public DataGridViewRow Owner + { + get + { + return this.owner; + } + set + { + if (this.owner != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerAlreadySet)); + } + this.owner = value; + } + } + + /// + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.ParentPrivate; + } + } + + private AccessibleObject ParentPrivate + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + return this.owner.DataGridView.AccessibilityObject; + } + } + + /// + public override AccessibleRole Role + { + get + { + return AccessibleRole.Row; + } + } + + internal override int[] RuntimeId + { + get + { + if (AccessibilityImprovements.Level3 && runtimeId == null) + { + runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.Parent.GetHashCode(); + runtimeId[2] = this.GetHashCode(); + } + + return runtimeId; + } + } + + private AccessibleObject SelectedCellsAccessibilityObject + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + if (this.selectedCellsAccessibilityObject == null) + { + this.selectedCellsAccessibilityObject = new DataGridViewSelectedRowCellsAccessibleObject(this.owner); + } + return this.selectedCellsAccessibilityObject; + } + } + + /// + public override AccessibleStates State + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + + AccessibleStates accState = AccessibleStates.Selectable; + + bool allCellsAreSelected = true; + if (this.owner.Selected) + { + allCellsAreSelected = true; + } + else + { + for (int i = 0; i < this.owner.Cells.Count; i ++) + { + if (!this.owner.Cells[i].Selected) + { + allCellsAreSelected = false; + break; + } + } + } + + if (allCellsAreSelected) + { + accState |= AccessibleStates.Selected; + } + + Rectangle rowBounds = this.owner.DataGridView.GetRowDisplayRectangle(this.owner.Index, true /*cutOverflow*/); + if (!rowBounds.IntersectsWith(this.owner.DataGridView.ClientRectangle)) + { + accState |= AccessibleStates.Offscreen; + } + + return accState; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + if (this.owner.DataGridView.AllowUserToAddRows && this.owner.Index == this.owner.DataGridView.NewRowIndex) + { + return SR.GetString(SR.DataGridView_AccRowCreateNew); + } + + StringBuilder sb = new StringBuilder(1024); + + int childCount = this.GetChildCount(); + + // filter out the row header acc object even when DataGridView::RowHeadersVisible is turned on + int startIndex = this.owner.DataGridView.RowHeadersVisible ? 1 : 0; + + for (int i = startIndex; i < childCount; i++) + { + AccessibleObject cellAccObj = this.GetChild(i); + if (cellAccObj != null) + { + sb.Append(cellAccObj.Value); + } + + if (i != childCount - 1) + { + sb.Append(";"); + } + } + + return sb.ToString(); + } + } + + /// + public override AccessibleObject GetChild(int index) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + if (index == 0 && this.owner.DataGridView.RowHeadersVisible) + { + return this.owner.HeaderCell.AccessibilityObject; + } + else + { + // decrement the index because the first child is the RowHeaderCell AccessibilityObject + if (this.owner.DataGridView.RowHeadersVisible) + { + index --; + } + Debug.Assert(index >= 0); + int columnIndex = this.owner.DataGridView.Columns.ActualDisplayIndexToColumnIndex(index, DataGridViewElementStates.Visible); + return this.owner.Cells[columnIndex].AccessibilityObject; + } + } + + /// + public override int GetChildCount() + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + int result = this.owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible); + + if (this.owner.DataGridView.RowHeadersVisible) + { + // + 1 comes from the row header cell accessibility object + result ++; + } + + return result; + } + + /// + public override AccessibleObject GetSelected() + { + return this.SelectedCellsAccessibilityObject; + } + + /// + public override AccessibleObject GetFocused() + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + if (this.owner.DataGridView.Focused && this.owner.DataGridView.CurrentCell != null && this.owner.DataGridView.CurrentCell.RowIndex == this.owner.Index) + { + return this.owner.DataGridView.CurrentCell.AccessibilityObject; + } + else + { + return null; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + switch (navigationDirection) + { + case AccessibleNavigation.Down: + case AccessibleNavigation.Next: + if (this.owner.Index != this.owner.DataGridView.Rows.GetLastRow(DataGridViewElementStates.Visible)) + { + int nextVisibleRow = this.owner.DataGridView.Rows.GetNextRow(this.owner.Index, DataGridViewElementStates.Visible); + int actualDisplayIndex = this.owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, nextVisibleRow); + if (this.owner.DataGridView.ColumnHeadersVisible) + { + return this.owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex + 1); + } + else + { + return this.owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex); + } + } + else + { + return null; + } + case AccessibleNavigation.Up: + case AccessibleNavigation.Previous: + if (this.owner.Index != this.owner.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible)) + { + int previousVisibleRow = this.owner.DataGridView.Rows.GetPreviousRow(this.owner.Index, DataGridViewElementStates.Visible); + int actualDisplayIndex = this.owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, previousVisibleRow); + if (this.owner.DataGridView.ColumnHeadersVisible) + { + return this.owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex + 1); + } + else + { + return this.owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex); + } + } + else if (this.owner.DataGridView.ColumnHeadersVisible) + { + // return the top row header acc obj + return this.ParentPrivate.GetChild(0); + } + else + { + // if this is the first row and the DataGridView RowHeaders are not visible return null; + return null; + } + case AccessibleNavigation.FirstChild: + if (this.GetChildCount() == 0) + { + return null; + } + else + { + return this.GetChild(0); + } + case AccessibleNavigation.LastChild: + int childCount = this.GetChildCount(); + if (childCount == 0) + { + return null; + } + else + { + return this.GetChild(childCount - 1); + } + default: + return null; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + + DataGridView dataGridView = this.owner.DataGridView; + if (dataGridView == null) + { + return; + } + if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) + { + dataGridView.FocusInternal(); + } + if ((flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) + { + if (this.owner.Cells.Count > 0) + { + if (dataGridView.CurrentCell != null && dataGridView.CurrentCell.OwningColumn != null) + { + dataGridView.CurrentCell = this.owner.Cells[dataGridView.CurrentCell.OwningColumn.Index]; // Do not change old selection + } + else + { + int firstVisibleCell = dataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Visible).Index; + if (firstVisibleCell > -1) + { + dataGridView.CurrentCell = this.owner.Cells[firstVisibleCell]; // Do not change old selection + } + } + } + } + + if ((flags & AccessibleSelection.AddSelection) == AccessibleSelection.AddSelection && (flags & AccessibleSelection.TakeSelection) == 0) + { + if (dataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || dataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + this.owner.Selected = true; + } + } + + if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection && + (flags & (AccessibleSelection.AddSelection | AccessibleSelection.TakeSelection)) == 0) + { + this.owner.Selected = false; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + { + if (Owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowAccessibleObject_OwnerNotSet)); + } + + var dataGridView = this.Owner.DataGridView; + + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return this.Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + return Navigate(AccessibleNavigation.Next); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return Navigate(AccessibleNavigation.Previous); + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return Navigate(AccessibleNavigation.FirstChild); + case UnsafeNativeMethods.NavigateDirection.LastChild: + return Navigate(AccessibleNavigation.LastChild); + default: + return null; + } + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return this.Owner.DataGridView.AccessibilityObject; + } + } + + #region IRawElementProviderSimple Implementation + + internal override bool IsPatternSupported(int patternId) + { + return patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId); + } + + internal override object GetPropertyValue(int propertyId) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_IsEnabledPropertyId: + return Owner.DataGridView.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return this.Help ?? string.Empty; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + } + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + + private class DataGridViewSelectedRowCellsAccessibleObject : AccessibleObject + { + DataGridViewRow owner; + + internal DataGridViewSelectedRowCellsAccessibleObject(DataGridViewRow owner) + { + this.owner = owner; + } + + public override string Name + { + get + { + return SR.GetString(SR.DataGridView_AccSelectedRowCellsName); + } + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.AccessibilityObject; + } + } + + public override AccessibleRole Role + { + get + { + return AccessibleRole.Grouping; + } + } + + public override AccessibleStates State + { + get + { + return AccessibleStates.Selected | AccessibleStates.Selectable; + } + } + + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.Name; + } + } + + public override AccessibleObject GetChild(int index) + { + if (index < this.GetChildCount()) + { + int selectedCellsCount = -1; + for (int i = 1; i < this.owner.AccessibilityObject.GetChildCount(); i ++) + { + if ((this.owner.AccessibilityObject.GetChild(i).State & AccessibleStates.Selected) == AccessibleStates.Selected) + { + selectedCellsCount ++; + } + + if (selectedCellsCount == index) + { + return this.owner.AccessibilityObject.GetChild(i); + } + } + Debug.Assert(false, "we should have found already the selected cell"); + return null; + } + else + { + return null; + } + } + + public override int GetChildCount() + { + int selectedCellsCount = 0; + + // start the enumeration from 1, because the first acc obj in the data grid view row is the row header cell + for (int i = 1; i < this.owner.AccessibilityObject.GetChildCount(); i ++) + { + if ((this.owner.AccessibilityObject.GetChild(i).State & AccessibleStates.Selected) == AccessibleStates.Selected) + { + selectedCellsCount ++; + } + } + + return selectedCellsCount; + } + + public override AccessibleObject GetSelected() + { + return this; + } + + public override AccessibleObject GetFocused() + { + if (this.owner.DataGridView.CurrentCell != null && this.owner.DataGridView.CurrentCell.Selected) + { + return this.owner.DataGridView.CurrentCell.AccessibilityObject; + } + else + { + return null; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + switch (navigationDirection) + { + case AccessibleNavigation.FirstChild: + if (GetChildCount() > 0) + { + return GetChild(0); + } + else + { + return null; + } + case AccessibleNavigation.LastChild: + if (GetChildCount() > 0) + { + return GetChild(GetChildCount() - 1); + } + else + { + return null; + } + default: + return null; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowCancelEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowCancelEventArgs.cs new file mode 100644 index 000000000..c7f747625 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowCancelEventArgs.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + /// + /// [To be supplied.] + /// + public class DataGridViewRowCancelEventArgs : CancelEventArgs + { + private DataGridViewRow dataGridViewRow; + + /// + public DataGridViewRowCancelEventArgs(DataGridViewRow dataGridViewRow) + { + Debug.Assert(dataGridViewRow != null); + Debug.Assert(dataGridViewRow.Index >= 0); + this.dataGridViewRow = dataGridViewRow; + } + + /// + /// + /// [To be supplied.] + /// + public DataGridViewRow Row + { + get + { + return this.dataGridViewRow; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowCollection.cs new file mode 100644 index 000000000..b45908569 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowCollection.cs @@ -0,0 +1,2965 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Diagnostics; + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Represents a collection of objects in the + /// control. + /// + [ + ListBindable(false), + DesignerSerializerAttribute("System.Windows.Forms.Design.DataGridViewRowCollectionCodeDomSerializer, " + AssemblyRef.SystemDesign, + "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewRowCollection : ICollection ,IList + { +#if DEBUG + // set to false when the cached row heights are dirty and should not be accessed. + private bool cachedRowHeightsAccessAllowed = true; + + // set to false when the cached row counts are dirty and should not be accessed. + private bool cachedRowCountsAccessAllowed = true; +#endif + + private CollectionChangeEventHandler onCollectionChanged; + private RowArrayList items; + private List rowStates; + private int rowCountsVisible, rowCountsVisibleFrozen, rowCountsVisibleSelected; + private int rowsHeightVisible, rowsHeightVisibleFrozen; + private DataGridView dataGridView; + + /* IList interface implementation */ + + /// + /// + int IList.Add(object value) + { + return this.Add((DataGridViewRow) value); + } + + /// + /// + void IList.Clear() + { + this.Clear(); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + this.Insert(index, (DataGridViewRow) value); + } + + /// + /// + void IList.Remove(object value) + { + this.Remove((DataGridViewRow) value); + } + + /// + /// + void IList.RemoveAt(int index) + { + this.RemoveAt(index); + } + + /// + /// + bool IList.IsFixedSize + { + get + { + return false; + } + } + + /// + /// + bool IList.IsReadOnly + { + get + { + return false; + } + } + + /// + /// + object IList.this[int index] + { + get + { + return this[index]; + } + set + { + throw new NotSupportedException(); + } + } + + /* ICollection interface implementation */ + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count + { + get + { + return this.Count; + } + } + + /// + /// + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + /// + object ICollection.SyncRoot + { + get + { + return this; + } + } + + /* IEnumerator interface implementation */ + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return new UnsharingRowEnumerator(this); + } + + + /// + public DataGridViewRowCollection(DataGridView dataGridView) + { + InvalidateCachedRowCounts(); + InvalidateCachedRowsHeights(); + this.dataGridView = dataGridView; + this.rowStates = new List(); + this.items = new RowArrayList(this); + } + + /// + public int Count + { + get + { + return this.items.Count; + } + } + + internal bool IsCollectionChangedListenedTo + { + get + { + return (this.onCollectionChanged != null); + } + } + + /// + protected ArrayList List + { + [ + SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops") // Illegitimate report. + ] + get + { + // All rows need to be unshared + // Accessing List property should be avoided. + int rowCount = this.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = this[rowIndex]; + } + return this.items; + } + } + + internal ArrayList SharedList + { + get + { + return this.items; + } + } + + /// + public DataGridViewRow SharedRow(int rowIndex) + { + return (DataGridViewRow) this.SharedList[rowIndex]; + } + + /// + protected DataGridView DataGridView + { + get + { + return this.dataGridView; + } + } + + /// + /// + /// Retrieves the DataGridViewRow with the specified index. + /// + public DataGridViewRow this[int index] + { + get + { + DataGridViewRow dataGridViewRow = SharedRow(index); + if (dataGridViewRow.Index == -1) + { + if (index == 0 && this.items.Count == 1) + { + // Fix for DevDiv Bugs 40780. The only row present in the grid gets unshared. + // Simply update the index and return the current row without cloning it. + dataGridViewRow.IndexInternal = 0; + dataGridViewRow.StateInternal = SharedRowState(0); + if (this.DataGridView != null) + { + this.DataGridView.OnRowUnshared(dataGridViewRow); + } + return dataGridViewRow; + } + + // unshare row + DataGridViewRow newDataGridViewRow = (DataGridViewRow) dataGridViewRow.Clone(); + newDataGridViewRow.IndexInternal = index; + newDataGridViewRow.DataGridViewInternal = dataGridViewRow.DataGridView; + newDataGridViewRow.StateInternal = SharedRowState(index); + this.SharedList[index] = newDataGridViewRow; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in newDataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = dataGridViewRow.DataGridView; + dataGridViewCell.OwningRowInternal = newDataGridViewRow; + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + if (newDataGridViewRow.HasHeaderCell) + { + newDataGridViewRow.HeaderCell.DataGridViewInternal = dataGridViewRow.DataGridView; + newDataGridViewRow.HeaderCell.OwningRowInternal = newDataGridViewRow; + } + if (this.DataGridView != null) + { + this.DataGridView.OnRowUnshared(newDataGridViewRow); + } + return newDataGridViewRow; + } + else + { + return dataGridViewRow; + } + } + } + + /// + public event CollectionChangeEventHandler CollectionChanged + { + add + { + this.onCollectionChanged += value; + } + remove + { + this.onCollectionChanged -= value; + } + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual int Add() + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + return AddInternal(false /*newRow*/, null); + } + + internal int AddInternal(bool newRow, object[] values) + { + Debug.Assert(this.DataGridView != null); + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + if (this.DataGridView.RowTemplate.Cells.Count > this.DataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_RowTemplateTooManyCells)); + } + + DataGridViewRow dataGridViewRow = this.DataGridView.RowTemplateClone; + Debug.Assert(dataGridViewRow.Cells.Count == this.DataGridView.Columns.Count); + if (newRow) + { + Debug.Assert(values == null); + // Note that we allow the 'new' row to be frozen. + Debug.Assert((dataGridViewRow.State & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + // Make sure the 'new row' is visible even when the row template isn't + dataGridViewRow.StateInternal = dataGridViewRow.State | DataGridViewElementStates.Visible; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.Value = dataGridViewCell.DefaultNewRowValue; + } + } + + if (values != null) + { + dataGridViewRow.SetValuesInternal(values); + } + + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + int insertionIndex = this.Count - 1; + Insert(insertionIndex, dataGridViewRow); + return insertionIndex; + } + + DataGridViewElementStates rowState = dataGridViewRow.State; + this.DataGridView.OnAddingRow(dataGridViewRow, rowState, true /*checkFrozenState*/); // will throw an exception if the addition is illegal + + dataGridViewRow.DataGridViewInternal = this.dataGridView; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == dataGridViewRow); + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.DataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + int index = this.SharedList.Add(dataGridViewRow); + Debug.Assert((rowState & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + this.rowStates.Add(rowState); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + if (values != null || !RowIsSharable(index) || RowHasValueOrToolTipText(dataGridViewRow) || this.IsCollectionChangedListenedTo) + { + dataGridViewRow.IndexInternal = index; + Debug.Assert(dataGridViewRow.State == SharedRowState(index)); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewRow), index, 1); + return index; + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual int Add(params object[] values) + { + Debug.Assert(this.DataGridView != null); + if (values == null) + { + throw new ArgumentNullException("values"); + } + + /* Intentionally not being strict about this. We just take what we get. + if (values.Length != this.DataGridView.Columns.Count) + { + // DataGridView_WrongValueCount=The array of cell values provided does not contain as many items as there are columns. + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongValueCount), "values"); + }*/ + + if (this.DataGridView.VirtualMode) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationInVirtualMode)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + /* Microsoft: Add once databinding is implemented + foreach (DataGridViewColumn dataGridViewColumn in this.DataGridView.Columns) + { + if (dataGridViewColumn.DataBound) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationInDataBoundMode)); + } + }*/ + return AddInternal(false /*newRow*/, values); + } + + /// + /// + /// Adds a to this collection. + /// + public virtual int Add(DataGridViewRow dataGridViewRow) + { + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + return AddInternal(dataGridViewRow); + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual int Add(int count) + { + Debug.Assert(this.DataGridView != null); + + if (count <= 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString(SR.DataGridViewRowCollection_CountOutOfRange)); + } + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.RowTemplate.Cells.Count > this.DataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_RowTemplateTooManyCells)); + } + + DataGridViewRow rowTemplate = this.DataGridView.RowTemplateClone; + Debug.Assert(rowTemplate.Cells.Count == this.DataGridView.Columns.Count); + DataGridViewElementStates rowTemplateState = rowTemplate.State; + Debug.Assert((rowTemplateState & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + rowTemplate.DataGridViewInternal = this.dataGridView; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in rowTemplate.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == rowTemplate); + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + if (rowTemplate.HasHeaderCell) + { + rowTemplate.HeaderCell.DataGridViewInternal = this.dataGridView; + rowTemplate.HeaderCell.OwningRowInternal = rowTemplate; + } + + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + int insertionIndex = this.Count - 1; + InsertCopiesPrivate(rowTemplate, rowTemplateState, insertionIndex, count); + return insertionIndex + count - 1; + } + + return AddCopiesPrivate(rowTemplate, rowTemplateState, count); + } + + internal int AddInternal(DataGridViewRow dataGridViewRow) + { + Debug.Assert(this.DataGridView != null); + + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + if (dataGridViewRow.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + if (dataGridViewRow.Cells.Count > this.DataGridView.Columns.Count) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewRowCollection_TooManyCells), "dataGridViewRow"); + } + + if (dataGridViewRow.Selected) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CannotAddOrInsertSelectedRow)); + } + + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + int insertionIndex = this.Count - 1; + InsertInternal(insertionIndex, dataGridViewRow); + return insertionIndex; + } + + this.DataGridView.CompleteCellsCollection(dataGridViewRow); + Debug.Assert(dataGridViewRow.Cells.Count == this.DataGridView.Columns.Count); + this.DataGridView.OnAddingRow(dataGridViewRow, dataGridViewRow.State, true /*checkFrozenState*/); // will throw an exception if the addition is illegal + + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == dataGridViewRow); + if (dataGridViewCell.ColumnIndex == -1) + { + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + } + columnIndex++; + } + + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.DataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + int index = this.SharedList.Add(dataGridViewRow); + Debug.Assert((dataGridViewRow.State & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + this.rowStates.Add(dataGridViewRow.State); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + + dataGridViewRow.DataGridViewInternal = this.dataGridView; + if (!RowIsSharable(index) || RowHasValueOrToolTipText(dataGridViewRow) || this.IsCollectionChangedListenedTo) + { + dataGridViewRow.IndexInternal = index; + Debug.Assert(dataGridViewRow.State == SharedRowState(index)); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewRow), index, 1); + return index; + } + + /// + public virtual int AddCopy(int indexSource) + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + return AddCopyInternal(indexSource, DataGridViewElementStates.None, DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed, false /*newRow*/); + } + + internal int AddCopyInternal(int indexSource, DataGridViewElementStates dgvesAdd, DataGridViewElementStates dgvesRemove, bool newRow) + { + Debug.Assert(this.DataGridView != null); + + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + Debug.Assert(!newRow); + int insertionIndex = this.Count - 1; + InsertCopy(indexSource, insertionIndex); + return insertionIndex; + } + + if (indexSource < 0 || indexSource >= this.Count) + { + throw new ArgumentOutOfRangeException("indexSource", SR.GetString(SR.DataGridViewRowCollection_IndexSourceOutOfRange)); + } + + int index; + DataGridViewRow rowTemplate = SharedRow(indexSource); + if (rowTemplate.Index == -1 && !this.IsCollectionChangedListenedTo && !newRow) + { + Debug.Assert(this.DataGridView != null); + DataGridViewElementStates rowState = this.rowStates[indexSource] & ~dgvesRemove; + rowState |= dgvesAdd; + this.DataGridView.OnAddingRow(rowTemplate, rowState, true /*checkFrozenState*/); // will throw an exception if the addition is illegal + + index = this.SharedList.Add(rowTemplate); + this.rowStates.Add(rowState); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, rowTemplate), index, 1); + return index; + } + else + { + index = AddDuplicateRow(rowTemplate, newRow); + if (!RowIsSharable(index) || RowHasValueOrToolTipText(SharedRow(index)) || this.IsCollectionChangedListenedTo) + { + UnshareRow(index); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, SharedRow(index)), index, 1); + return index; + } + } + + /// + public virtual int AddCopies(int indexSource, int count) + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + return AddCopiesInternal(indexSource, count); + } + + internal int AddCopiesInternal(int indexSource, int count) + { + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + int insertionIndex = this.Count - 1; + InsertCopiesPrivate(indexSource, insertionIndex, count); + return insertionIndex + count - 1; + } + + return AddCopiesInternal(indexSource, count, DataGridViewElementStates.None, DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed); + } + + internal int AddCopiesInternal(int indexSource, int count, DataGridViewElementStates dgvesAdd, DataGridViewElementStates dgvesRemove) + { + if (indexSource < 0 || this.Count <= indexSource) + { + throw new ArgumentOutOfRangeException("indexSource", SR.GetString(SR.DataGridViewRowCollection_IndexSourceOutOfRange)); + } + + if (count <= 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString(SR.DataGridViewRowCollection_CountOutOfRange)); + } + + DataGridViewElementStates rowTemplateState = this.rowStates[indexSource] & ~dgvesRemove; + rowTemplateState |= dgvesAdd; + + return AddCopiesPrivate(SharedRow(indexSource), rowTemplateState, count); + } + + private int AddCopiesPrivate(DataGridViewRow rowTemplate, DataGridViewElementStates rowTemplateState, int count) + { + int index, indexStart = this.items.Count; + if (rowTemplate.Index == -1) + { + this.DataGridView.OnAddingRow(rowTemplate, rowTemplateState, true /*checkFrozenState*/); // Done once only, continue to check if this is OK - will throw an exception if the addition is illegal. + for (int i = 0; i < count - 1; i++) + { + this.SharedList.Add(rowTemplate); + this.rowStates.Add(rowTemplateState); + } + index = this.SharedList.Add(rowTemplate); + this.rowStates.Add(rowTemplateState); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + this.DataGridView.OnAddedRow_PreNotification(index); // Only calling this once instead of 'count' times. Continue to check if this is OK. + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexStart, count); + for (int i = 0; i < count; i++) + { + this.DataGridView.OnAddedRow_PostNotification(index - (count - 1) + i); + } + return index; + } + else + { + index = AddDuplicateRow(rowTemplate, false /*newRow*/); + if (count > 1) + { + this.DataGridView.OnAddedRow_PreNotification(index); + if (RowIsSharable(index)) + { + DataGridViewRow rowTemplate2 = SharedRow(index); + this.DataGridView.OnAddingRow(rowTemplate2, rowTemplateState, true /*checkFrozenState*/); // done only once, continue to check if this is OK - will throw an exception if the addition is illegal + for (int i = 1; i < count - 1; i++) + { + this.SharedList.Add(rowTemplate2); + this.rowStates.Add(rowTemplateState); + } + index = this.SharedList.Add(rowTemplate2); + this.rowStates.Add(rowTemplateState); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + this.DataGridView.OnAddedRow_PreNotification(index); // Only calling this once instead of 'count-1' times. Continue to check if this is OK. + } + else + { + UnshareRow(index); + for (int i = 1; i < count; i++) + { + index = AddDuplicateRow(rowTemplate, false /*newRow*/); + UnshareRow(index); + this.DataGridView.OnAddedRow_PreNotification(index); + } + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexStart, count); + for (int i = 0; i < count; i++) + { + this.DataGridView.OnAddedRow_PostNotification(index - (count - 1) + i); + } + return index; + } + else + { + if (this.IsCollectionChangedListenedTo) + { + UnshareRow(index); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, SharedRow(index)), index, 1); + return index; + } + } + } + + private int AddDuplicateRow(DataGridViewRow rowTemplate, bool newRow) + { + Debug.Assert(this.DataGridView != null); + + DataGridViewRow dataGridViewRow = (DataGridViewRow) rowTemplate.Clone(); + dataGridViewRow.StateInternal = DataGridViewElementStates.None; + dataGridViewRow.DataGridViewInternal = this.dataGridView; + DataGridViewCellCollection dgvcc = dataGridViewRow.Cells; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dgvcc) + { + if (newRow) + { + dataGridViewCell.Value = dataGridViewCell.DefaultNewRowValue; + } + dataGridViewCell.DataGridViewInternal = this.dataGridView; + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + DataGridViewElementStates rowState = rowTemplate.State & ~(DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed); + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.dataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + this.DataGridView.OnAddingRow(dataGridViewRow, rowState, true /*checkFrozenState*/); // will throw an exception if the addition is illegal + +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + Debug.Assert(dataGridViewRow.Index == -1); + this.rowStates.Add(rowState); + return this.SharedList.Add(dataGridViewRow); + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual void AddRange(params DataGridViewRow[] dataGridViewRows) + { + if (dataGridViewRows == null) + { + throw new ArgumentNullException("dataGridViewRows"); + } + + Debug.Assert(this.DataGridView != null); + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NewRowIndex != -1) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + Debug.Assert(this.DataGridView.NewRowIndex == this.Count - 1); + InsertRange(this.Count - 1, dataGridViewRows); + return; + } + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + int indexStart = this.items.Count; + + // OnAddingRows checks for Selected flag of each row and their dimension. + this.DataGridView.OnAddingRows(dataGridViewRows, true /*checkFrozenStates*/); // will throw an exception if the addition is illegal + + foreach(DataGridViewRow dataGridViewRow in dataGridViewRows) + { + Debug.Assert(dataGridViewRow.Cells.Count == this.DataGridView.Columns.Count); + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == dataGridViewRow); + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.dataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + int index = this.SharedList.Add(dataGridViewRow); + Debug.Assert((dataGridViewRow.State & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + this.rowStates.Add(dataGridViewRow.State); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + dataGridViewRow.IndexInternal = index; + Debug.Assert(dataGridViewRow.State == SharedRowState(index)); + dataGridViewRow.DataGridViewInternal = this.dataGridView; + } + Debug.Assert(this.rowStates.Count == this.SharedList.Count); + + this.DataGridView.OnAddedRows_PreNotification(dataGridViewRows); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexStart, dataGridViewRows.Length); + this.DataGridView.OnAddedRows_PostNotification(dataGridViewRows); + } + + /// + public virtual void Clear() + { + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + if (this.DataGridView.DataSource != null) + { + IBindingList list = this.DataGridView.DataConnection.List as IBindingList; + if (list != null && list.AllowRemove && list.SupportsChangeNotification) + { + ((IList)list).Clear(); + } + else + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CantClearRowCollectionWithWrongSource)); + } + } + else + { + ClearInternal(true); + } + } + + internal void ClearInternal(bool recreateNewRow) + { + int rowCount = this.items.Count; + if (rowCount > 0) + { + this.DataGridView.OnClearingRows(); + + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + SharedRow(rowIndex).DetachFromDataGridView(); + } + + this.SharedList.Clear(); + this.rowStates.Clear(); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), 0, rowCount, true, false, recreateNewRow, new Point(-1, -1)); + } + else if (recreateNewRow && + this.DataGridView.Columns.Count != 0 && + this.DataGridView.AllowUserToAddRowsInternal && + this.items.Count == 0) // accessing AllowUserToAddRowsInternal can trigger a nested call to ClearInternal. Rows count needs to be checked again. + { + this.DataGridView.AddNewRow(false); + } + } + + /// + /// + /// Checks to see if a DataGridViewRow is contained in this collection. + /// + public virtual bool Contains(DataGridViewRow dataGridViewRow) + { + return this.items.IndexOf(dataGridViewRow) != -1; + } + + /// + public void CopyTo(DataGridViewRow[] array, int index) + { + this.items.CopyTo(array, index); + } + + // returns the row collection index for the n'th visible row + internal int DisplayIndexToRowIndex(int visibleRowIndex) + { + Debug.Assert(visibleRowIndex < GetRowCount(DataGridViewElementStates.Visible)); + + // go row by row + // the alternative would be to do a binary search using DataGridViewRowCollection::GetRowCount(...) + // but that method also iterates thru each row so we would not gain much + int indexOfCurrentVisibleRow = -1; + for (int i = 0; i < this.Count; i++) + { + if ((GetRowState(i) & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible) + { + indexOfCurrentVisibleRow++; + } + if (indexOfCurrentVisibleRow == visibleRowIndex) + { + return i; + } + } + Debug.Assert(false, "we should have found the row already"); + return -1; + } + + /// + public int GetFirstRow(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } +#if DEBUG + Debug.Assert(this.cachedRowCountsAccessAllowed); +#endif + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.rowCountsVisible == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.rowCountsVisibleFrozen == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + if (this.rowCountsVisibleSelected == 0) + { + return -1; + } + break; + } + + int index = 0; + while (index < this.items.Count && !((GetRowState(index) & includeFilter) == includeFilter)) + { + index++; + } + return (index < this.items.Count) ? index : -1; + } + + /// + public int GetFirstRow(DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (excludeFilter == DataGridViewElementStates.None) + { + return GetFirstRow(includeFilter); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } +#if DEBUG + Debug.Assert(this.cachedRowCountsAccessAllowed); +#endif + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.rowCountsVisible == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.rowCountsVisibleFrozen == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + if (this.rowCountsVisibleSelected == 0) + { + return -1; + } + break; + } + + int index = 0; + while (index < this.items.Count && (!((GetRowState(index) & includeFilter) == includeFilter) || !((GetRowState(index) & excludeFilter) == 0))) + { + index++; + } + return (index < this.items.Count) ? index : -1; + } + + /// + public int GetLastRow(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } +#if DEBUG + Debug.Assert(this.cachedRowCountsAccessAllowed); +#endif + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.rowCountsVisible == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.rowCountsVisibleFrozen == 0) + { + return -1; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + if (this.rowCountsVisibleSelected == 0) + { + return -1; + } + break; + } + + int index = this.items.Count - 1; + while (index >= 0 && !((GetRowState(index) & includeFilter) == includeFilter)) + { + index--; + } + return (index >= 0) ? index : -1; + } + + internal int GetNextRow(int indexStart, DataGridViewElementStates includeFilter, int skipRows) + { + Debug.Assert(skipRows >= 0); + + int rowIndex = indexStart; + do + { + rowIndex = GetNextRow(rowIndex, includeFilter); + skipRows--; + } + while (skipRows >= 0 && rowIndex != -1); + + return rowIndex; + } + + /// + public int GetNextRow(int indexStart, DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if (indexStart < -1) + { + throw new ArgumentOutOfRangeException("indexStart", SR.GetString(SR.InvalidLowBoundArgumentEx, "indexStart", (indexStart).ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + int index = indexStart + 1; + while (index < this.items.Count && !((GetRowState(index) & includeFilter) == includeFilter)) + { + index++; + } + return (index < this.items.Count) ? index : -1; + } + + /// + public int GetNextRow(int indexStart, + DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (excludeFilter == DataGridViewElementStates.None) + { + return GetNextRow(indexStart, includeFilter); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + if (indexStart < -1) + { + throw new ArgumentOutOfRangeException("indexStart", SR.GetString(SR.InvalidLowBoundArgumentEx, "indexStart", (indexStart).ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + int index = indexStart + 1; + while (index < this.items.Count && (!((GetRowState(index) & includeFilter) == includeFilter) || !((GetRowState(index) & excludeFilter) == 0))) + { + index++; + } + return (index < this.items.Count) ? index : -1; + } + + /// + public int GetPreviousRow(int indexStart, DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if (indexStart > this.items.Count) + { + throw new ArgumentOutOfRangeException("indexStart", SR.GetString(SR.InvalidHighBoundArgumentEx, "indexStart", (indexStart).ToString(CultureInfo.CurrentCulture), (this.items.Count).ToString(CultureInfo.CurrentCulture))); + } + + int index = indexStart - 1; + while (index >= 0 && !((GetRowState(index) & includeFilter) == includeFilter)) + { + index--; + } + return (index >= 0) ? index : -1; + } + + /// + public int GetPreviousRow(int indexStart, + DataGridViewElementStates includeFilter, + DataGridViewElementStates excludeFilter) + { + if (excludeFilter == DataGridViewElementStates.None) + { + return GetPreviousRow(indexStart, includeFilter); + } + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } + if ((excludeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "excludeFilter")); + } + if (indexStart > this.items.Count) + { + throw new ArgumentOutOfRangeException("indexStart", SR.GetString(SR.InvalidHighBoundArgumentEx, "indexStart", (indexStart).ToString(CultureInfo.CurrentCulture), (this.items.Count).ToString(CultureInfo.CurrentCulture))); + } + + int index = indexStart - 1; + while (index >= 0 && (!((GetRowState(index) & includeFilter) == includeFilter) || !((GetRowState(index) & excludeFilter) == 0))) + { + index--; + } + return (index >= 0) ? index : -1; + } + + /// + public int GetRowCount(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } +#if DEBUG + Debug.Assert(this.cachedRowCountsAccessAllowed); +#endif + // cache returned value and reuse it as long as none + // of the row's state has changed. + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.rowCountsVisible != -1) + { + return this.rowCountsVisible; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.rowCountsVisibleFrozen != -1) + { + return this.rowCountsVisibleFrozen; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + if (this.rowCountsVisibleSelected != -1) + { + return this.rowCountsVisibleSelected; + } + break; + } + + int rowCount = 0; + for (int rowIndex = 0; rowIndex < this.items.Count; rowIndex++) + { + if ((GetRowState(rowIndex) & includeFilter) == includeFilter) + { + rowCount++; + } + } + + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + this.rowCountsVisible = rowCount; + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + this.rowCountsVisibleFrozen = rowCount; + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Selected: + this.rowCountsVisibleSelected = rowCount; + break; + } + return rowCount; + } + + internal int GetRowCount(DataGridViewElementStates includeFilter, int fromRowIndex, int toRowIndex) + { + Debug.Assert(toRowIndex >= fromRowIndex); + Debug.Assert((GetRowState(toRowIndex) & includeFilter) == includeFilter); + + int jumpRows = 0; + for (int rowIndex = fromRowIndex + 1; rowIndex <= toRowIndex; rowIndex++) + { + if ((GetRowState(rowIndex) & includeFilter) == includeFilter) + { + jumpRows++; + } + } + + return jumpRows; + } + + /// + public int GetRowsHeight(DataGridViewElementStates includeFilter) + { + if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable | + DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewElementStateCombination, "includeFilter")); + } +#if DEBUG + Debug.Assert(this.cachedRowHeightsAccessAllowed); +#endif + // cache returned value and reuse it as long as none + // of the row's state/thickness has changed. + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + if (this.rowsHeightVisible != -1) + { + return this.rowsHeightVisible; + } + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + if (this.rowsHeightVisibleFrozen != -1) + { + return this.rowsHeightVisibleFrozen; + } + break; + } + + int rowsHeight = 0; + for (int rowIndex = 0; rowIndex < this.items.Count; rowIndex++) + { + if ((GetRowState(rowIndex) & includeFilter) == includeFilter) + { + rowsHeight += ((DataGridViewRow)this.items[rowIndex]).GetHeight(rowIndex); + } + } + + switch (includeFilter) + { + case DataGridViewElementStates.Visible: + this.rowsHeightVisible = rowsHeight; + break; + case DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen: + this.rowsHeightVisibleFrozen = rowsHeight; + break; + } + return rowsHeight; + } + + // Cumulates the height of the rows from fromRowIndex to toRowIndex-1. + internal int GetRowsHeight(DataGridViewElementStates includeFilter, int fromRowIndex, int toRowIndex) + { + Debug.Assert(toRowIndex >= fromRowIndex); + Debug.Assert((GetRowState(toRowIndex) & includeFilter) == includeFilter); + + int rowsHeight = 0; + for (int rowIndex = fromRowIndex; rowIndex < toRowIndex; rowIndex++) + { + if ((GetRowState(rowIndex) & includeFilter) == includeFilter) + { + rowsHeight += ((DataGridViewRow)this.items[rowIndex]).GetHeight(rowIndex); + } + } + return rowsHeight; + } + + // Checks if the cumulated row heights from fromRowIndex to toRowIndex-1 exceed heightLimit. + private bool GetRowsHeightExceedLimit(DataGridViewElementStates includeFilter, int fromRowIndex, int toRowIndex, int heightLimit) + { + Debug.Assert(toRowIndex >= fromRowIndex); + Debug.Assert(toRowIndex == this.items.Count || (GetRowState(toRowIndex) & includeFilter) == includeFilter); + + int rowsHeight = 0; + for (int rowIndex = fromRowIndex; rowIndex < toRowIndex; rowIndex++) + { + if ((GetRowState(rowIndex) & includeFilter) == includeFilter) + { + rowsHeight += ((DataGridViewRow)this.items[rowIndex]).GetHeight(rowIndex); + if (rowsHeight > heightLimit) + { + return true; + } + } + } + return rowsHeight > heightLimit; + } + + /// + public virtual DataGridViewElementStates GetRowState(int rowIndex) + { + if (rowIndex < 0 || rowIndex >= this.items.Count) + { + throw new ArgumentOutOfRangeException("rowIndex", SR.GetString(SR.DataGridViewRowCollection_RowIndexOutOfRange)); + } + DataGridViewRow dataGridViewRow = SharedRow(rowIndex); + if (dataGridViewRow.Index == -1) + { + return SharedRowState(rowIndex); + } + else + { + Debug.Assert(dataGridViewRow.Index == rowIndex); + return dataGridViewRow.GetState(rowIndex); + } + } + + /// + public int IndexOf(DataGridViewRow dataGridViewRow) + { + return this.items.IndexOf(dataGridViewRow); + } + + /// + public virtual void Insert(int rowIndex, params object[] values) + { + Debug.Assert(this.DataGridView != null); + + if (values == null) + { + throw new ArgumentNullException("values"); + } + + /* Intentionally not being strict about this. We just take what we get. + if (values.Length != this.DataGridView.Columns.Count) + { + // DataGridView_WrongValueCount=The array of cell values provided does not contain as many items as there are columns. + throw new ArgumentException(SR.GetString(SR.DataGridView_WrongValueCount), "values"); + }*/ + + if (this.DataGridView.VirtualMode) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationInVirtualMode)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + DataGridViewRow dataGridViewRow = this.DataGridView.RowTemplateClone; + dataGridViewRow.SetValuesInternal(values); + Insert(rowIndex, dataGridViewRow); + } + + /// + /// + /// Inserts a to this collection. + /// + public virtual void Insert(int rowIndex, DataGridViewRow dataGridViewRow) + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + InsertInternal(rowIndex, dataGridViewRow); + } + + /// + public virtual void Insert(int rowIndex, int count) + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (rowIndex < 0 || this.Count < rowIndex) + { + throw new ArgumentOutOfRangeException("rowIndex", SR.GetString(SR.DataGridViewRowCollection_IndexDestinationOutOfRange)); + } + + if (count <= 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString(SR.DataGridViewRowCollection_CountOutOfRange)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + if (this.DataGridView.RowTemplate.Cells.Count > this.DataGridView.Columns.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_RowTemplateTooManyCells)); + } + + if (this.DataGridView.NewRowIndex != -1 && rowIndex == this.Count) + { + // Trying to insert after the 'new' row. + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoInsertionAfterNewRow)); + } + + DataGridViewRow rowTemplate = this.DataGridView.RowTemplateClone; + Debug.Assert(rowTemplate.Cells.Count == this.DataGridView.Columns.Count); + DataGridViewElementStates rowTemplateState = rowTemplate.State; + Debug.Assert((rowTemplateState & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + rowTemplate.DataGridViewInternal = this.dataGridView; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in rowTemplate.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == rowTemplate); + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + if (rowTemplate.HasHeaderCell) + { + rowTemplate.HeaderCell.DataGridViewInternal = this.dataGridView; + rowTemplate.HeaderCell.OwningRowInternal = rowTemplate; + } + + InsertCopiesPrivate(rowTemplate, rowTemplateState, rowIndex, count); + } + + internal void InsertInternal(int rowIndex, DataGridViewRow dataGridViewRow) + { + Debug.Assert(this.DataGridView != null); + if (rowIndex < 0 || this.Count < rowIndex) + { + throw new ArgumentOutOfRangeException("rowIndex", SR.GetString(SR.DataGridViewRowCollection_RowIndexOutOfRange)); + } + + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + + if (dataGridViewRow.DataGridView != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_RowAlreadyBelongsToDataGridView)); + } + + if (this.DataGridView.NewRowIndex != -1 && rowIndex == this.Count) + { + // Trying to insert after the 'new' row. + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoInsertionAfterNewRow)); + } + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + if (dataGridViewRow.Cells.Count > this.DataGridView.Columns.Count) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewRowCollection_TooManyCells), "dataGridViewRow"); + } + + if (dataGridViewRow.Selected) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CannotAddOrInsertSelectedRow)); + } + + InsertInternal(rowIndex, dataGridViewRow, false); + } + + internal void InsertInternal(int rowIndex, DataGridViewRow dataGridViewRow, bool force) + { + Debug.Assert(this.DataGridView != null); + Debug.Assert(rowIndex >= 0 && rowIndex <= this.Count); + Debug.Assert(dataGridViewRow != null); + Debug.Assert(dataGridViewRow.DataGridView == null); + Debug.Assert(!this.DataGridView.NoDimensionChangeAllowed); + Debug.Assert(this.DataGridView.NewRowIndex == -1 || rowIndex != this.Count); + Debug.Assert(!dataGridViewRow.Selected); + + Point newCurrentCell = new Point(-1, -1); + + if (force) + { + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + if (dataGridViewRow.Cells.Count > this.DataGridView.Columns.Count) + { + throw new ArgumentException(SR.GetString(SR.DataGridViewRowCollection_TooManyCells), "dataGridViewRow"); + } + } + this.DataGridView.CompleteCellsCollection(dataGridViewRow); + Debug.Assert(dataGridViewRow.Cells.Count == this.DataGridView.Columns.Count); + this.DataGridView.OnInsertingRow(rowIndex, dataGridViewRow, dataGridViewRow.State, ref newCurrentCell, true, 1, force); // will throw an exception if the insertion is illegal + + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == dataGridViewRow); + if (dataGridViewCell.ColumnIndex == -1) + { + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + } + columnIndex++; + } + + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.DataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + this.SharedList.Insert(rowIndex, dataGridViewRow); + Debug.Assert((dataGridViewRow.State & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + this.rowStates.Insert(rowIndex, dataGridViewRow.State); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + + dataGridViewRow.DataGridViewInternal = this.dataGridView; + if (!RowIsSharable(rowIndex) || RowHasValueOrToolTipText(dataGridViewRow) || this.IsCollectionChangedListenedTo) + { + dataGridViewRow.IndexInternal = rowIndex; + Debug.Assert(dataGridViewRow.State == SharedRowState(rowIndex)); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataGridViewRow), rowIndex, 1, false, true, false, newCurrentCell); + } + + /// + public virtual void InsertCopy(int indexSource, int indexDestination) + { + InsertCopies(indexSource, indexDestination, 1); + } + + /// + public virtual void InsertCopies(int indexSource, int indexDestination, int count) + { + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + InsertCopiesPrivate(indexSource, indexDestination, count); + } + + private void InsertCopiesPrivate(int indexSource, int indexDestination, int count) + { + Debug.Assert(this.DataGridView != null); + + if (indexSource < 0 || this.Count <= indexSource) + { + throw new ArgumentOutOfRangeException("indexSource", SR.GetString(SR.DataGridViewRowCollection_IndexSourceOutOfRange)); + } + + if (indexDestination < 0 || this.Count < indexDestination) + { + throw new ArgumentOutOfRangeException("indexDestination", SR.GetString(SR.DataGridViewRowCollection_IndexDestinationOutOfRange)); + } + + if (count <= 0) + { + throw new ArgumentOutOfRangeException("count", SR.GetString(SR.DataGridViewRowCollection_CountOutOfRange)); + } + + if (this.DataGridView.NewRowIndex != -1 && indexDestination == this.Count) + { + // Trying to insert after the 'new' row. + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoInsertionAfterNewRow)); + } + + DataGridViewElementStates rowTemplateState = GetRowState(indexSource) & ~(DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed); + InsertCopiesPrivate(SharedRow(indexSource), rowTemplateState, indexDestination, count); + } + + private void InsertCopiesPrivate(DataGridViewRow rowTemplate, DataGridViewElementStates rowTemplateState, int indexDestination, int count) + { + Point newCurrentCell = new Point(-1, -1); + if (rowTemplate.Index == -1) + { + if (count > 1) + { + // Done once only, continue to check if this is OK - will throw an exception if the insertion is illegal. + this.DataGridView.OnInsertingRow(indexDestination, rowTemplate, rowTemplateState, ref newCurrentCell, true, count, false /*force*/); + for (int i = 0; i < count; i++) + { + this.SharedList.Insert(indexDestination + i, rowTemplate); + this.rowStates.Insert(indexDestination + i, rowTemplateState); + } +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + // Only calling this once instead of 'count' times. Continue to check if this is OK. + this.DataGridView.OnInsertedRow_PreNotification(indexDestination, count); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexDestination, count, false, true, false, newCurrentCell); + for (int i = 0; i < count; i++) + { + this.DataGridView.OnInsertedRow_PostNotification(indexDestination + i, newCurrentCell, i == count - 1); + } + } + else + { + this.DataGridView.OnInsertingRow(indexDestination, rowTemplate, rowTemplateState, ref newCurrentCell, true, 1, false /*force*/); // will throw an exception if the insertion is illegal + this.SharedList.Insert(indexDestination, rowTemplate); + this.rowStates.Insert(indexDestination, rowTemplateState); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, SharedRow(indexDestination)), indexDestination, count, false, true, false, newCurrentCell); + } + } + else + { + // Sets this.DataGridView.dataStoreAccessAllowed to false + InsertDuplicateRow(indexDestination, rowTemplate, true, ref newCurrentCell); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); + if (count > 1) + { + this.DataGridView.OnInsertedRow_PreNotification(indexDestination, 1); + if (RowIsSharable(indexDestination)) + { + DataGridViewRow rowTemplate2 = SharedRow(indexDestination); + // Done once only, continue to check if this is OK - will throw an exception if the insertion is illegal. + this.DataGridView.OnInsertingRow(indexDestination + 1, rowTemplate2, rowTemplateState, ref newCurrentCell, false, count-1, false /*force*/); + for (int i = 1; i < count; i++) + { + this.SharedList.Insert(indexDestination + i, rowTemplate2); + this.rowStates.Insert(indexDestination + i, rowTemplateState); + } +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + // Only calling this once instead of 'count-1' times. Continue to check if this is OK. + this.DataGridView.OnInsertedRow_PreNotification(indexDestination+1, count-1); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexDestination, count, false, true, false, newCurrentCell); + } + else + { + UnshareRow(indexDestination); + for (int i = 1; i < count; i++) + { + InsertDuplicateRow(indexDestination + i, rowTemplate, false, ref newCurrentCell); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); + UnshareRow(indexDestination + i); + this.DataGridView.OnInsertedRow_PreNotification(indexDestination + i, 1); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), indexDestination, count, false, true, false, newCurrentCell); + } + for (int i = 0; i < count; i++) + { + this.DataGridView.OnInsertedRow_PostNotification(indexDestination + i, newCurrentCell, i == count - 1); + } + } + else + { + if (this.IsCollectionChangedListenedTo) + { + UnshareRow(indexDestination); + } + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, SharedRow(indexDestination)), indexDestination, 1, false, true, false, newCurrentCell); + } + } + } + + private void InsertDuplicateRow(int indexDestination, DataGridViewRow rowTemplate, bool firstInsertion, ref Point newCurrentCell) + { + Debug.Assert(this.DataGridView != null); + + DataGridViewRow dataGridViewRow = (DataGridViewRow) rowTemplate.Clone(); + dataGridViewRow.StateInternal = DataGridViewElementStates.None; + dataGridViewRow.DataGridViewInternal = this.dataGridView; + DataGridViewCellCollection dgvcc = dataGridViewRow.Cells; + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dgvcc) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + columnIndex++; + } + DataGridViewElementStates rowState = rowTemplate.State & ~(DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed); + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.dataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + this.DataGridView.OnInsertingRow(indexDestination, dataGridViewRow, rowState, ref newCurrentCell, firstInsertion, 1, false /*force*/); // will throw an exception if the insertion is illegal + + Debug.Assert(dataGridViewRow.Index == -1); + this.SharedList.Insert(indexDestination, dataGridViewRow); + this.rowStates.Insert(indexDestination, rowState); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + } + + /// + /// + /// Inserts a range of to this collection. + /// + public virtual void InsertRange(int rowIndex, params DataGridViewRow[] dataGridViewRows) + { + Debug.Assert(this.DataGridView != null); + + if (dataGridViewRows == null) + { + throw new ArgumentNullException("dataGridViewRows"); + } + + if (dataGridViewRows.Length == 1) + { + Insert(rowIndex, dataGridViewRows[0]); + return; + } + + if (rowIndex < 0 || rowIndex > this.Count) + { + throw new ArgumentOutOfRangeException("rowIndex", SR.GetString(SR.DataGridViewRowCollection_IndexDestinationOutOfRange)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.NewRowIndex != -1 && rowIndex == this.Count) + { + // Trying to insert after the 'new' row. + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoInsertionAfterNewRow)); + } + + if (this.DataGridView.DataSource != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_AddUnboundRow)); + } + + if (this.DataGridView.Columns.Count == 0) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_NoColumns)); + } + + Point newCurrentCell = new Point(-1, -1); + + // OnInsertingRows checks for Selected flag of each row, among other things. + this.DataGridView.OnInsertingRows(rowIndex, dataGridViewRows, ref newCurrentCell); // will throw an exception if the insertion is illegal + + int rowIndexInserted = rowIndex; + foreach (DataGridViewRow dataGridViewRow in dataGridViewRows) + { + Debug.Assert(dataGridViewRow.Cells.Count == this.DataGridView.Columns.Count); + int columnIndex = 0; + foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells) + { + dataGridViewCell.DataGridViewInternal = this.dataGridView; + Debug.Assert(dataGridViewCell.OwningRow == dataGridViewRow); + if (dataGridViewCell.ColumnIndex == -1) + { + dataGridViewCell.OwningColumnInternal = this.DataGridView.Columns[columnIndex]; + } + columnIndex++; + } + + if (dataGridViewRow.HasHeaderCell) + { + dataGridViewRow.HeaderCell.DataGridViewInternal = this.DataGridView; + dataGridViewRow.HeaderCell.OwningRowInternal = dataGridViewRow; + } + + this.SharedList.Insert(rowIndexInserted, dataGridViewRow); + Debug.Assert((dataGridViewRow.State & (DataGridViewElementStates.Selected | DataGridViewElementStates.Displayed)) == 0); + this.rowStates.Insert(rowIndexInserted, dataGridViewRow.State); + Debug.Assert(this.rowStates.Count == this.SharedList.Count); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; + this.cachedRowHeightsAccessAllowed = false; + this.cachedRowCountsAccessAllowed = false; +#endif + + dataGridViewRow.IndexInternal = rowIndexInserted; + Debug.Assert(dataGridViewRow.State == SharedRowState(rowIndexInserted)); + dataGridViewRow.DataGridViewInternal = this.dataGridView; + rowIndexInserted++; + } + + this.DataGridView.OnInsertedRows_PreNotification(rowIndex, dataGridViewRows); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null), rowIndex, dataGridViewRows.Length, false, true, false, newCurrentCell); + this.DataGridView.OnInsertedRows_PostNotification(dataGridViewRows, newCurrentCell); + } + + internal void InvalidateCachedRowCount(DataGridViewElementStates includeFilter) + { + Debug.Assert(includeFilter == DataGridViewElementStates.Displayed || + includeFilter == DataGridViewElementStates.Selected || + includeFilter == DataGridViewElementStates.ReadOnly || + includeFilter == DataGridViewElementStates.Resizable || + includeFilter == DataGridViewElementStates.Frozen || + includeFilter == DataGridViewElementStates.Visible); + + if (includeFilter == DataGridViewElementStates.Visible) + { + InvalidateCachedRowCounts(); + } + else if (includeFilter == DataGridViewElementStates.Frozen) + { + this.rowCountsVisibleFrozen = -1; + } + else if (includeFilter == DataGridViewElementStates.Selected) + { + this.rowCountsVisibleSelected = -1; + } + +#if DEBUG + this.cachedRowCountsAccessAllowed = true; +#endif + } + + internal void InvalidateCachedRowCounts() + { + this.rowCountsVisible = this.rowCountsVisibleFrozen = this.rowCountsVisibleSelected = -1; +#if DEBUG + this.cachedRowCountsAccessAllowed = true; +#endif + } + + internal void InvalidateCachedRowsHeight(DataGridViewElementStates includeFilter) + { + Debug.Assert(includeFilter == DataGridViewElementStates.Displayed || + includeFilter == DataGridViewElementStates.Selected || + includeFilter == DataGridViewElementStates.ReadOnly || + includeFilter == DataGridViewElementStates.Resizable || + includeFilter == DataGridViewElementStates.Frozen || + includeFilter == DataGridViewElementStates.Visible); + + if (includeFilter == DataGridViewElementStates.Visible) + { + InvalidateCachedRowsHeights(); + } + else if (includeFilter == DataGridViewElementStates.Frozen) + { + this.rowsHeightVisibleFrozen = -1; + } + +#if DEBUG + this.cachedRowHeightsAccessAllowed = true; +#endif + } + + internal void InvalidateCachedRowsHeights() + { + this.rowsHeightVisible = this.rowsHeightVisibleFrozen = -1; +#if DEBUG + this.cachedRowHeightsAccessAllowed = true; +#endif + } + + /// + protected virtual void OnCollectionChanged(CollectionChangeEventArgs e) + { + if (this.onCollectionChanged != null) + { + this.onCollectionChanged(this, e); + } + } + + private void OnCollectionChanged(CollectionChangeEventArgs e, + int rowIndex, + int rowCount) + { + Debug.Assert(e.Action != CollectionChangeAction.Remove); + Point newCurrentCell = new Point(-1, -1); + DataGridViewRow dataGridViewRow = (DataGridViewRow) e.Element; + int originalIndex = 0; + if (dataGridViewRow != null && e.Action == CollectionChangeAction.Add) + { + originalIndex = SharedRow(rowIndex).Index; + } + OnCollectionChanged_PreNotification(e.Action, rowIndex, rowCount, ref dataGridViewRow, false); + if (originalIndex == -1 && SharedRow(rowIndex).Index != -1) + { + // row got unshared inside OnCollectionChanged_PreNotification + e = new CollectionChangeEventArgs(e.Action, dataGridViewRow); + } + OnCollectionChanged(e); + OnCollectionChanged_PostNotification(e.Action, rowIndex, rowCount, dataGridViewRow, false, false, false, newCurrentCell); + } + + private void OnCollectionChanged(CollectionChangeEventArgs e, + int rowIndex, + int rowCount, + bool changeIsDeletion, + bool changeIsInsertion, + bool recreateNewRow, + Point newCurrentCell) + { + DataGridViewRow dataGridViewRow = (DataGridViewRow)e.Element; + int originalIndex = 0; + if (dataGridViewRow != null && e.Action == CollectionChangeAction.Add) + { + originalIndex = SharedRow(rowIndex).Index; + } + OnCollectionChanged_PreNotification(e.Action, rowIndex, rowCount, ref dataGridViewRow, changeIsInsertion); + if (originalIndex == -1 && SharedRow(rowIndex).Index != -1) + { + // row got unshared inside OnCollectionChanged_PreNotification + e = new CollectionChangeEventArgs(e.Action, dataGridViewRow); + } + OnCollectionChanged(e); + OnCollectionChanged_PostNotification(e.Action, rowIndex, rowCount, dataGridViewRow, changeIsDeletion, changeIsInsertion, recreateNewRow, newCurrentCell); + } + + private void OnCollectionChanged_PreNotification(CollectionChangeAction cca, + int rowIndex, + int rowCount, + ref DataGridViewRow dataGridViewRow, + bool changeIsInsertion) + { + Debug.Assert(this.DataGridView != null); + bool useRowShortcut = false; + bool computeVisibleRows = false; + switch (cca) + { + case CollectionChangeAction.Add: + { + int firstDisplayedRowHeight = 0; + UpdateRowCaches(rowIndex, ref dataGridViewRow, true); + if ((GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0) + { + // Adding an invisible row - no need for repaint + useRowShortcut = true; + computeVisibleRows = changeIsInsertion; + } + else + { + int firstDisplayedRowIndex = this.DataGridView.FirstDisplayedRowIndex; + if (firstDisplayedRowIndex != -1) + { + firstDisplayedRowHeight = SharedRow(firstDisplayedRowIndex).GetHeight(firstDisplayedRowIndex); + } + } + if (changeIsInsertion) + { + this.DataGridView.OnInsertedRow_PreNotification(rowIndex, 1); + if (!useRowShortcut) + { + if ((GetRowState(rowIndex) & DataGridViewElementStates.Frozen) != 0) + { + // Inserted row is frozen + useRowShortcut = this.DataGridView.FirstDisplayedScrollingRowIndex == -1 && + GetRowsHeightExceedLimit(DataGridViewElementStates.Visible, 0, rowIndex, this.DataGridView.LayoutInfo.Data.Height); + } + else if (this.DataGridView.FirstDisplayedScrollingRowIndex != -1 && + rowIndex > this.DataGridView.FirstDisplayedScrollingRowIndex) + { + useRowShortcut = GetRowsHeightExceedLimit(DataGridViewElementStates.Visible, 0, rowIndex, this.DataGridView.LayoutInfo.Data.Height + this.DataGridView.VerticalScrollingOffset) && + firstDisplayedRowHeight <= this.DataGridView.LayoutInfo.Data.Height; + } + } + } + else + { + this.DataGridView.OnAddedRow_PreNotification(rowIndex); + if (!useRowShortcut) + { + int displayedRowsHeightBeforeAddition = GetRowsHeight(DataGridViewElementStates.Visible) - this.DataGridView.VerticalScrollingOffset - dataGridViewRow.GetHeight(rowIndex); + dataGridViewRow = SharedRow(rowIndex); + useRowShortcut = this.DataGridView.LayoutInfo.Data.Height < displayedRowsHeightBeforeAddition && + firstDisplayedRowHeight <= this.DataGridView.LayoutInfo.Data.Height; + } + } + break; + } + + case CollectionChangeAction.Remove: + { + Debug.Assert(rowCount == 1); + DataGridViewElementStates rowStates = GetRowState(rowIndex); + bool deletedRowVisible = (rowStates & DataGridViewElementStates.Visible) != 0; + bool deletedRowFrozen = (rowStates & DataGridViewElementStates.Frozen) != 0; + + // Can't do this earlier since it would break UpdateRowCaches + this.rowStates.RemoveAt(rowIndex); + this.SharedList.RemoveAt(rowIndex); +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = false; +#endif + this.DataGridView.OnRemovedRow_PreNotification(rowIndex); + if (deletedRowVisible) + { + if (deletedRowFrozen) + { + // Delete row is frozen + useRowShortcut = this.DataGridView.FirstDisplayedScrollingRowIndex == -1 && + GetRowsHeightExceedLimit(DataGridViewElementStates.Visible, 0, rowIndex, this.DataGridView.LayoutInfo.Data.Height + SystemInformation.HorizontalScrollBarHeight); + } + else if (this.DataGridView.FirstDisplayedScrollingRowIndex != -1 && + rowIndex > this.DataGridView.FirstDisplayedScrollingRowIndex) + { + int firstDisplayedRowHeight = 0; + int firstDisplayedRowIndex = this.DataGridView.FirstDisplayedRowIndex; + if (firstDisplayedRowIndex != -1) + { + firstDisplayedRowHeight = SharedRow(firstDisplayedRowIndex).GetHeight(firstDisplayedRowIndex); + } + useRowShortcut = GetRowsHeightExceedLimit(DataGridViewElementStates.Visible, 0, rowIndex, this.DataGridView.LayoutInfo.Data.Height + this.DataGridView.VerticalScrollingOffset + SystemInformation.HorizontalScrollBarHeight) && + firstDisplayedRowHeight <= this.DataGridView.LayoutInfo.Data.Height; + } + } + else + { + // Deleting an invisible row - no need for repaint + useRowShortcut = true; + } + break; + } + + case CollectionChangeAction.Refresh: + { + InvalidateCachedRowCounts(); + InvalidateCachedRowsHeights(); + break; + } + + default: + { + Debug.Fail("Unexpected cca value in DataGridViewRowCollecttion.OnCollectionChanged"); + break; + } + } + this.DataGridView.ResetUIState(useRowShortcut, computeVisibleRows); + } + + private void OnCollectionChanged_PostNotification(CollectionChangeAction cca, + int rowIndex, + int rowCount, + DataGridViewRow dataGridViewRow, + bool changeIsDeletion, + bool changeIsInsertion, + bool recreateNewRow, + Point newCurrentCell) + { + Debug.Assert(this.DataGridView != null); + if (changeIsDeletion) + { + this.DataGridView.OnRowsRemovedInternal(rowIndex, rowCount); + } + else + { + this.DataGridView.OnRowsAddedInternal(rowIndex, rowCount); + } + +#if DEBUG + this.DataGridView.dataStoreAccessAllowed = true; +#endif + switch (cca) + { + case CollectionChangeAction.Add: + { + if (changeIsInsertion) + { + this.DataGridView.OnInsertedRow_PostNotification(rowIndex, newCurrentCell, true); + } + else + { + this.DataGridView.OnAddedRow_PostNotification(rowIndex); + } + break; + } + + case CollectionChangeAction.Remove: + { + this.DataGridView.OnRemovedRow_PostNotification(dataGridViewRow, newCurrentCell); + break; + } + + case CollectionChangeAction.Refresh: + { + if (changeIsDeletion) + { + this.DataGridView.OnClearedRows(); + } + break; + } + } + + this.DataGridView.OnRowCollectionChanged_PostNotification(recreateNewRow, newCurrentCell.X == -1, cca, dataGridViewRow, rowIndex); + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // We don't want to use DataGridViewBand here. + ] + public virtual void Remove(DataGridViewRow dataGridViewRow) + { + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + + if (dataGridViewRow.DataGridView != this.DataGridView) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowDoesNotBelongToDataGridView), "dataGridViewRow"); + } + + if (dataGridViewRow.Index == -1) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_RowMustBeUnshared), "dataGridViewRow"); + } + else + { + RemoveAt(dataGridViewRow.Index); + } + } + + /// + public virtual void RemoveAt(int index) + { + if (index < 0 || index >= this.Count) + { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.DataGridViewRowCollection_RowIndexOutOfRange)); + } + + if (this.DataGridView.NewRowIndex == index) + { + Debug.Assert(this.DataGridView.AllowUserToAddRowsInternal); + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CannotDeleteNewRow)); + } + + if (this.DataGridView.NoDimensionChangeAllowed) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridView_ForbiddenOperationInEventHandler)); + } + + if (this.DataGridView.DataSource != null) + { + IBindingList list = this.DataGridView.DataConnection.List as IBindingList; + if (list != null && list.AllowRemove && list.SupportsChangeNotification) + { + ((IList)list).RemoveAt(index); + } + else + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_CantRemoveRowsWithWrongSource)); + } + } + else + { + RemoveAtInternal(index, false /*force*/); + } + } + + internal void RemoveAtInternal(int index, bool force) + { + // If force is true, the underlying data is gone and can't be accessed anymore. + + Debug.Assert(index >= 0 && index < this.Count); + Debug.Assert(this.DataGridView != null); + Debug.Assert(!this.DataGridView.NoDimensionChangeAllowed); + + DataGridViewRow dataGridViewRow = SharedRow(index); + Point newCurrentCell = new Point(-1, -1); + + if (this.IsCollectionChangedListenedTo || dataGridViewRow.GetDisplayed(index)) + { + dataGridViewRow = this[index]; // need to unshare row because dev is listening to OnCollectionChanged event or the row is displayed + } + + dataGridViewRow = SharedRow(index); + Debug.Assert(this.DataGridView != null); + this.DataGridView.OnRemovingRow(index, out newCurrentCell, force); + UpdateRowCaches(index, ref dataGridViewRow, false /*adding*/); + if (dataGridViewRow.Index != -1) + { + this.rowStates[index] = dataGridViewRow.State; + // Only detach unshared rows, since a shared row has never been accessed by the user + dataGridViewRow.DetachFromDataGridView(); + } + // Note: cannot set dataGridViewRow.DataGridView to null because this row may be shared and still be used. + // Note that OnCollectionChanged takes care of calling this.items.RemoveAt(index) & + // this.rowStates.RemoveAt(index). Can't do it here since OnCollectionChanged uses the arrays. + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataGridViewRow), index, 1, true, false, false, newCurrentCell); + } + + private static bool RowHasValueOrToolTipText(DataGridViewRow dataGridViewRow) + { + Debug.Assert(dataGridViewRow != null); + Debug.Assert(dataGridViewRow.Index == -1); + + DataGridViewCellCollection cells = dataGridViewRow.Cells; + foreach (DataGridViewCell dataGridViewCell in cells) + { + if (dataGridViewCell.HasValue || dataGridViewCell.HasToolTipText) + { + return true; + } + } + return false; + } + + internal bool RowIsSharable(int index) + { + Debug.Assert(index >= 0); + Debug.Assert(index < this.Count); + + DataGridViewRow dataGridViewRow = SharedRow(index); + if (dataGridViewRow.Index != -1) + { + return false; + } + + // A row is sharable if all of its cells' states can be deduced from + // the column and row states. + DataGridViewCellCollection cells = dataGridViewRow.Cells; + foreach (DataGridViewCell dataGridViewCell in cells) + { + if ((dataGridViewCell.State & ~(dataGridViewCell.CellStateFromColumnRowStates(this.rowStates[index]))) != 0) + { + return false; + } + } + return true; + } + + internal void SetRowState(int rowIndex, DataGridViewElementStates state, bool value) + { + Debug.Assert(state == DataGridViewElementStates.Displayed || state == DataGridViewElementStates.Selected || state == DataGridViewElementStates.ReadOnly || state == DataGridViewElementStates.Resizable || state == DataGridViewElementStates.Frozen || state == DataGridViewElementStates.Visible); + + DataGridViewRow dataGridViewRow = SharedRow(rowIndex); + if (dataGridViewRow.Index == -1) + { + // row is shared + if (((this.rowStates[rowIndex] & state) != 0) != value) + { + if (state == DataGridViewElementStates.Frozen || + state == DataGridViewElementStates.Visible || + state == DataGridViewElementStates.ReadOnly) + { + dataGridViewRow.OnSharedStateChanging(rowIndex, state); + } + if (value) + { + this.rowStates[rowIndex] = this.rowStates[rowIndex] | state; + } + else + { + this.rowStates[rowIndex] = this.rowStates[rowIndex] & ~state; + } + dataGridViewRow.OnSharedStateChanged(rowIndex, state); + } + } + else + { + // row is unshared + switch (state) + { + case DataGridViewElementStates.Displayed: + { + dataGridViewRow.DisplayedInternal = value; + break; + } + case DataGridViewElementStates.Selected: + { + dataGridViewRow.SelectedInternal = value; + break; + } + case DataGridViewElementStates.Visible: + { + dataGridViewRow.Visible = value; + break; + } + case DataGridViewElementStates.Frozen: + { + dataGridViewRow.Frozen = value; + break; + } + case DataGridViewElementStates.ReadOnly: + { + dataGridViewRow.ReadOnlyInternal = value; + break; + } + case DataGridViewElementStates.Resizable: + { + dataGridViewRow.Resizable = value ? DataGridViewTriState.True : DataGridViewTriState.False; + break; + } + default: + { + Debug.Fail("Unexpected DataGridViewElementStates parameter in DataGridViewRowCollection.SetRowState."); + break; + } + } + } + } + + internal DataGridViewElementStates SharedRowState(int rowIndex) + { + return this.rowStates[rowIndex]; + } + + internal void Sort(IComparer customComparer, bool ascending) + { + if (this.items.Count > 0) + { + RowComparer rowComparer = new RowComparer(this, customComparer, ascending); + this.items.CustomSort(rowComparer); + // Caller takes care of the dataGridView invalidation + } + } + + internal void SwapSortedRows(int rowIndex1, int rowIndex2) + { + // Deal with the current cell address updates + + // selected rows updates. + this.DataGridView.SwapSortedRows(rowIndex1, rowIndex2); + + DataGridViewRow dataGridViewRow1 = SharedRow(rowIndex1); + DataGridViewRow dataGridViewRow2 = SharedRow(rowIndex2); + + if (dataGridViewRow1.Index != -1) + { + dataGridViewRow1.IndexInternal = rowIndex2; + } + if (dataGridViewRow2.Index != -1) + { + dataGridViewRow2.IndexInternal = rowIndex1; + } + + if (this.DataGridView.VirtualMode) + { + // All cell contents on the involved rows need to be swapped + int columnCount = this.DataGridView.Columns.Count; + + for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) + { + DataGridViewCell dataGridViewCell1 = dataGridViewRow1.Cells[columnIndex]; + DataGridViewCell dataGridViewCell2 = dataGridViewRow2.Cells[columnIndex]; + object value1 = dataGridViewCell1.GetValueInternal(rowIndex1); + object value2 = dataGridViewCell2.GetValueInternal(rowIndex2); + dataGridViewCell1.SetValueInternal(rowIndex1, value2); + dataGridViewCell2.SetValueInternal(rowIndex2, value1); + } + } + + object item = this.items[rowIndex1]; + this.items[rowIndex1] = this.items[rowIndex2]; + this.items[rowIndex2] = item; + + DataGridViewElementStates rowStates = this.rowStates[rowIndex1]; + this.rowStates[rowIndex1] = this.rowStates[rowIndex2]; + this.rowStates[rowIndex2] = rowStates; + } + + // This function only adjusts the row's RowIndex and State properties - no more. + private void UnshareRow(int rowIndex) + { + SharedRow(rowIndex).IndexInternal = rowIndex; + SharedRow(rowIndex).StateInternal = SharedRowState(rowIndex); + } + + private void UpdateRowCaches(int rowIndex, ref DataGridViewRow dataGridViewRow, bool adding) + { + if (this.rowCountsVisible != -1 || this.rowCountsVisibleFrozen != -1 || this.rowCountsVisibleSelected != -1 || + this.rowsHeightVisible != -1 || this.rowsHeightVisibleFrozen != -1) + { + DataGridViewElementStates rowStates = GetRowState(rowIndex); + if ((rowStates & DataGridViewElementStates.Visible) != 0) + { + int rowCountIncrement = adding ? 1 : -1; + int rowHeightIncrement = 0; + if (this.rowsHeightVisible != -1 || + (this.rowsHeightVisibleFrozen != -1 && + ((rowStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)))) + { + // dataGridViewRow may become unshared in GetHeight call + rowHeightIncrement = adding ? dataGridViewRow.GetHeight(rowIndex) : -dataGridViewRow.GetHeight(rowIndex); + dataGridViewRow = SharedRow(rowIndex); + } + + if (this.rowCountsVisible != -1) + { + this.rowCountsVisible += rowCountIncrement; + } + if (this.rowsHeightVisible != -1) + { + Debug.Assert(rowHeightIncrement != 0); + this.rowsHeightVisible += rowHeightIncrement; + } + + if ((rowStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)) + { + if (this.rowCountsVisibleFrozen != -1) + { + this.rowCountsVisibleFrozen += rowCountIncrement; + } + if (this.rowsHeightVisibleFrozen != -1) + { + Debug.Assert(rowHeightIncrement != 0); + this.rowsHeightVisibleFrozen += rowHeightIncrement; + } + } + + if ((rowStates & (DataGridViewElementStates.Visible | DataGridViewElementStates.Selected)) == (DataGridViewElementStates.Visible | DataGridViewElementStates.Selected)) + { + if (this.rowCountsVisibleSelected != -1) + { + this.rowCountsVisibleSelected += rowCountIncrement; + } + } + } + } + +#if DEBUG + this.cachedRowCountsAccessAllowed = true; + this.cachedRowHeightsAccessAllowed = true; +#endif + } + +/*#if DEBUG + private bool inVerifyRowFrozenStates = false; + public void VerifyRowFrozenStates() + { + if (inVerifyRowFrozenStates) return; + + inVerifyRowFrozenStates = true; + try + { + bool previousVisibleRowFrozen = true; + for (int rowIndex = 0; rowIndex < this.items.Count; rowIndex++) + { + DataGridViewElementStates rowStates = GetRowState(rowIndex); + if (!previousVisibleRowFrozen && + (rowStates & DataGridViewElementStates.Visible) != 0 && + (rowStates & DataGridViewElementStates.Frozen) != 0) + { + Debug.Fail("VerifyRowFrozenStates - wrong frozen state"); + } + if ((rowStates & DataGridViewElementStates.Visible) != 0) + { + previousVisibleRowFrozen = (rowStates & DataGridViewElementStates.Frozen) != 0; + } + } + } + finally + { + inVerifyRowFrozenStates = false; + } + } +#endif*/ + + /* Private classes */ + + /*private class DefaultRowComparer : IComparer + { + private DataGridView dataGridView; + private DataGridViewRowCollection dataGridViewRows; + private DataGridViewColumn dataGridViewSortedColumn; + private int sortedColumnIndex; + + public DefaultRowComparer(DataGridViewRowCollection dataGridViewRows) + { + this.DataGridViewInternal = dataGridViewRows.DataGridView; + this.dataGridViewRows = dataGridViewRows; + this.dataGridViewSortedColumn = this.dataGridView.SortedColumn; + this.sortedColumnIndex = this.dataGridViewSortedColumn.Index; + } + + int IComparer.Compare(object x, object y) + { + DataGridViewRow dataGridViewRow1 = this.dataGridViewRows.SharedRow((int)x); + DataGridViewRow dataGridViewRow2 = this.dataGridViewRows.SharedRow((int)y); + Debug.Assert(dataGridViewRow1 != null); + Debug.Assert(dataGridViewRow2 != null); + object value1 = dataGridViewRow1.Cells[this.sortedColumnIndex].GetValueInternal((int)x); + object value2 = dataGridViewRow2.Cells[this.sortedColumnIndex].GetValueInternal((int)y); + DataGridViewSortEventArgs tsea = new DataGridViewSortEventArgs(this.dataGridViewSortedColumn, value1, value2); + this.dataGridView.OnSorting(tsea); + if (tsea.Handled) + { + return tsea.SortResult; + } + else + { + return Comparer.Default.Compare(value1, value2); + } + } + }*/ + + private class RowArrayList : ArrayList + { + private DataGridViewRowCollection owner; + private RowComparer rowComparer; + + public RowArrayList(DataGridViewRowCollection owner) + { + this.owner = owner; + } + + public void CustomSort(RowComparer rowComparer) + { + Debug.Assert(rowComparer != null); + Debug.Assert(this.Count > 0); + + this.rowComparer = rowComparer; + CustomQuickSort(0, this.Count - 1); + } + + private void CustomQuickSort(int left, int right) + { + // Custom recursive QuickSort needed because of the notion of shared rows. + // The indexes of the compared rows are required to do the comparisons. + // For a study comparing the iterative and recursive versions of the QuickSort + // see http://www.mathcs.carleton.edu/courses/course_resources/cs227_w96/wightmaj/data.html + // Is the recursive version going to cause trouble with large dataGridViews? + do + { + if (right - left < 2) // sort subarray of two elements + { + if (right - left > 0 && this.rowComparer.CompareObjects(this.rowComparer.GetComparedObject(left), this.rowComparer.GetComparedObject(right), left, right) > 0) + { + this.owner.SwapSortedRows(left, right); + } + return; + } + + int k = (left + right) >> 1; + object x = Pivot(left, k, right); + int i = left+1; + int j = right-1; + do + { + while (k != i && this.rowComparer.CompareObjects(this.rowComparer.GetComparedObject(i), x, i, k) < 0) + { + i++; + } + while (k != j && this.rowComparer.CompareObjects(x, this.rowComparer.GetComparedObject(j), k, j) < 0) + { + j--; + } + Debug.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?"); + if (i > j) + { + break; + } + if (i < j) + { + this.owner.SwapSortedRows(i, j); + if (i == k) + { + k = j; + } + else if (j == k) + { + k = i; + } + } + i++; + j--; + } + while (i <= j); + + if (j - left <= right - i) + { + if (left < j) + { + CustomQuickSort(left, j); + } + left = i; + } + else + { + if (i < right) + { + CustomQuickSort(i, right); + } + right = j; + } + } + while (left < right); + } + + private object Pivot(int left, int center, int right) + { + // find median-of-3 (left, center and right) and sort these 3 elements + if (this.rowComparer.CompareObjects(this.rowComparer.GetComparedObject(left), this.rowComparer.GetComparedObject(center), left, center) > 0) + { + this.owner.SwapSortedRows(left, center); + } + if (this.rowComparer.CompareObjects(this.rowComparer.GetComparedObject(left), this.rowComparer.GetComparedObject(right), left, right) > 0) + { + this.owner.SwapSortedRows(left, right); + } + if (this.rowComparer.CompareObjects(this.rowComparer.GetComparedObject(center), this.rowComparer.GetComparedObject(right), center, right) > 0) + { + this.owner.SwapSortedRows(center, right); + } + return this.rowComparer.GetComparedObject(center); + } + } + + private class RowComparer + { + private DataGridView dataGridView; + private DataGridViewRowCollection dataGridViewRows; + private DataGridViewColumn dataGridViewSortedColumn; + private int sortedColumnIndex; + private IComparer customComparer; + private bool ascending; + private static ComparedObjectMax max = new ComparedObjectMax(); + + public RowComparer(DataGridViewRowCollection dataGridViewRows, IComparer customComparer, bool ascending) + { + this.dataGridView = dataGridViewRows.DataGridView; + this.dataGridViewRows = dataGridViewRows; + this.dataGridViewSortedColumn = this.dataGridView.SortedColumn; + if (this.dataGridViewSortedColumn == null) + { + Debug.Assert(customComparer != null); + this.sortedColumnIndex = -1; + } + else + { + this.sortedColumnIndex = this.dataGridViewSortedColumn.Index; + } + this.customComparer = customComparer; + this.ascending = ascending; + } + + internal object GetComparedObject(int rowIndex) + { + if (this.dataGridView.NewRowIndex != -1) + { + Debug.Assert(this.dataGridView.AllowUserToAddRowsInternal); + if (rowIndex == this.dataGridView.NewRowIndex) + { + return max; + } + } + if (this.customComparer == null) + { + DataGridViewRow dataGridViewRow = this.dataGridViewRows.SharedRow(rowIndex); + Debug.Assert(dataGridViewRow != null); + Debug.Assert(this.sortedColumnIndex >= 0); + return dataGridViewRow.Cells[this.sortedColumnIndex].GetValueInternal(rowIndex); + } + else + { + return this.dataGridViewRows[rowIndex]; // Unsharing compared rows! + } + } + + internal int CompareObjects(object value1, object value2, int rowIndex1, int rowIndex2) + { + if (value1 is ComparedObjectMax) + { + return 1; + } + else if (value2 is ComparedObjectMax) + { + return -1; + } + int result = 0; + if (this.customComparer == null) + { + if (!this.dataGridView.OnSortCompare(this.dataGridViewSortedColumn, value1, value2, rowIndex1, rowIndex2, out result)) + { + if (!(value1 is IComparable) && !(value2 is IComparable)) + { + if (value1 == null) + { + if (value2 == null) + { + result = 0; + } + else + { + result = 1; + } + } + else if (value2 == null) + { + result = -1; + } + else + { + result = Comparer.Default.Compare(value1.ToString(), value2.ToString()); + } + } + else + { + result = Comparer.Default.Compare(value1, value2); + } + if (result == 0) + { + if (this.ascending) + { + result = rowIndex1 - rowIndex2; + } + else + { + result = rowIndex2 - rowIndex1; + } + } + } + } + else + { + Debug.Assert(value1 is DataGridViewRow); + Debug.Assert(value2 is DataGridViewRow); + Debug.Assert(value1 != null); + Debug.Assert(value2 != null); + // + + + result = this.customComparer.Compare(value1, value2); + } + + if (this.ascending) + { + return result; + } + else + { + return -result; + } + } + + class ComparedObjectMax + { + public ComparedObjectMax() { } + } + } + + private class UnsharingRowEnumerator : IEnumerator + { + private DataGridViewRowCollection owner; + private int current; + + /// + /// Creates a new enumerator that will enumerate over the rows and unshare the accessed rows if needed. + /// + public UnsharingRowEnumerator(DataGridViewRowCollection owner) + { + this.owner = owner; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() + { + + if (this.current < this.owner.Count - 1) + { + this.current++; + return true; + } + else + { + this.current = this.owner.Count; + return false; + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() + { + this.current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current + { + get + { + if (this.current == -1) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_EnumNotStarted)); + } + if (this.current == this.owner.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewRowCollection_EnumFinished)); + } + return this.owner[this.current]; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowContextMenuStripNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowContextMenuStripNeededEventArgs.cs new file mode 100644 index 000000000..c68a810b6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowContextMenuStripNeededEventArgs.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewRowContextMenuStripNeededEventArgs : EventArgs + { + private int rowIndex; + private ContextMenuStrip contextMenuStrip; + + /// + public DataGridViewRowContextMenuStripNeededEventArgs(int rowIndex) + { + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + this.rowIndex = rowIndex; + } + + internal DataGridViewRowContextMenuStripNeededEventArgs(int rowIndex, ContextMenuStrip contextMenuStrip) : this(rowIndex) + { + this.contextMenuStrip = contextMenuStrip; + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public ContextMenuStrip ContextMenuStrip + { + get + { + return this.contextMenuStrip; + } + set + { + this.contextMenuStrip = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowConverter.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowConverter.cs new file mode 100644 index 000000000..0eb3dc834 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowConverter.cs @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + internal class DataGridViewRowConverter : ExpandableObjectConverter { + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + DataGridViewRow dataGridViewRow = value as DataGridViewRow; + if (destinationType == typeof(InstanceDescriptor) && dataGridViewRow != null) { + // public DataGridViewRow() + // + ConstructorInfo ctor = dataGridViewRow.GetType().GetConstructor(new Type[0]); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[0], false); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowDividerDoubleClickEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowDividerDoubleClickEventArgs.cs new file mode 100644 index 000000000..5b18a91e9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowDividerDoubleClickEventArgs.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public class DataGridViewRowDividerDoubleClickEventArgs : HandledMouseEventArgs + { + private int rowIndex; + + /// + public DataGridViewRowDividerDoubleClickEventArgs(int rowIndex, HandledMouseEventArgs e) : base(e.Button, e.Clicks, e.X, e.Y, e.Delta, e.Handled) + { + if (rowIndex < -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + this.rowIndex = rowIndex; + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowErrorTextNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowErrorTextNeededEventArgs.cs new file mode 100644 index 000000000..8136475be --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowErrorTextNeededEventArgs.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewRowErrorTextNeededEventArgs : EventArgs + { + private int rowIndex; + private string errorText; + + internal DataGridViewRowErrorTextNeededEventArgs(int rowIndex, string errorText) + { + Debug.Assert(rowIndex >= -1); + this.rowIndex = rowIndex; + this.errorText = errorText; + } + + /// + public string ErrorText + { + get + { + return this.errorText; + } + set + { + this.errorText = value; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowEventArgs.cs new file mode 100644 index 000000000..4fc5b6c1b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowEventArgs.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + public class DataGridViewRowEventArgs : EventArgs + { + private DataGridViewRow dataGridViewRow; + + /// + public DataGridViewRowEventArgs(DataGridViewRow dataGridViewRow) + { + if (dataGridViewRow == null) + { + throw new ArgumentNullException("dataGridViewRow"); + } + Debug.Assert(dataGridViewRow.Index >= -1); + this.dataGridViewRow = dataGridViewRow; + } + + /// + public DataGridViewRow Row + { + get + { + return this.dataGridViewRow; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowHeaderCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeaderCell.cs new file mode 100644 index 000000000..82ad3c5f8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeaderCell.cs @@ -0,0 +1,1526 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.IO; + using System.Text; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Imaging; + using System.Windows.Forms.VisualStyles; + using System.ComponentModel; + using System.Windows.Forms.Internal; + using System.Security.Permissions; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// + /// + public class DataGridViewRowHeaderCell : DataGridViewHeaderCell + { + private static readonly VisualStyleElement HeaderElement = VisualStyleElement.Header.Item.Normal; + + // ColorMap used to map the black color of the resource bitmaps to the fore color in use in the row header cell + private static ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; + + private static Bitmap rightArrowBmp = null; + private static Bitmap leftArrowBmp = null; + private static Bitmap rightArrowStarBmp; + private static Bitmap leftArrowStarBmp; + //private static Bitmap errorBmp = null; + private static Bitmap pencilLTRBmp = null; + private static Bitmap pencilRTLBmp = null; + private static Bitmap starBmp = null; + + private static Type cellType = typeof(DataGridViewRowHeaderCell); + + private const byte DATAGRIDVIEWROWHEADERCELL_iconMarginWidth = 3; // 3 pixels of margin on the left and right of icons + private const byte DATAGRIDVIEWROWHEADERCELL_iconMarginHeight = 2; // 2 pixels of margin on the top and bottom of icons + private const byte DATAGRIDVIEWROWHEADERCELL_contentMarginWidth = 3; // 3 pixels of margin on the left and right of content + private const byte DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft = 1; + private const byte DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight = 2; + private const byte DATAGRIDVIEWROWHEADERCELL_verticalTextMargin = 1; + + /// + public DataGridViewRowHeaderCell() + { + } + + /* Unused for now. + private static Bitmap ErrorBitmap + { + get + { + if (errorBmp == null) + { + errorBmp = GetBitmap("DataGridViewRow.error.bmp"); + } + return errorBmp; + } + } + */ + + private static Bitmap LeftArrowBitmap + { + get + { + if (leftArrowBmp == null) + { + leftArrowBmp = GetBitmapFromIcon("DataGridViewRow.left.ico"); + } + return leftArrowBmp; + } + } + + private static Bitmap LeftArrowStarBitmap + { + get + { + if (leftArrowStarBmp == null) + { + leftArrowStarBmp = GetBitmapFromIcon("DataGridViewRow.leftstar.ico"); + } + return leftArrowStarBmp; + } + } + + private static Bitmap PencilLTRBitmap + { + get + { + if (pencilLTRBmp == null) + { + pencilLTRBmp = GetBitmapFromIcon("DataGridViewRow.pencil_ltr.ico"); + } + return pencilLTRBmp; + } + } + + private static Bitmap PencilRTLBitmap + { + get + { + if (pencilRTLBmp == null) + { + pencilRTLBmp = GetBitmapFromIcon("DataGridViewRow.pencil_rtl.ico"); + } + return pencilRTLBmp; + } + } + + private static Bitmap RightArrowBitmap + { + get + { + if (rightArrowBmp == null) + { + rightArrowBmp = GetBitmapFromIcon("DataGridViewRow.right.ico"); + } + return rightArrowBmp; + } + } + + private static Bitmap RightArrowStarBitmap + { + get + { + if (rightArrowStarBmp == null) + { + rightArrowStarBmp = GetBitmapFromIcon("DataGridViewRow.rightstar.ico"); + } + return rightArrowStarBmp; + } + } + + private static Bitmap StarBitmap + { + get + { + if (starBmp == null) + { + starBmp = GetBitmapFromIcon("DataGridViewRow.star.ico"); + } + return starBmp; + } + } + + /// + public override object Clone() + { + DataGridViewRowHeaderCell dataGridViewCell; + Type thisType = this.GetType(); + + if (thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewRowHeaderCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewRowHeaderCell) System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.Value = this.Value; + return dataGridViewCell; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewRowHeaderCellAccessibleObject(this); + } + + private static Bitmap GetArrowBitmap(bool rightToLeft) + { + return rightToLeft ? DataGridViewRowHeaderCell.LeftArrowBitmap : DataGridViewRowHeaderCell.RightArrowBitmap; + } + + private static Bitmap GetArrowStarBitmap(bool rightToLeft) + { + return rightToLeft ? DataGridViewRowHeaderCell.LeftArrowStarBitmap : DataGridViewRowHeaderCell.RightArrowStarBitmap; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static Bitmap GetBitmapFromIcon(string iconName) + { + Size desiredSize = new Size(iconsWidth, iconsHeight); + Icon icon = new Icon(BitmapSelector.GetResourceStream(typeof(DataGridViewRowHeaderCell), iconName), desiredSize); + Bitmap b = icon.ToBitmap(); + icon.Dispose(); + + if (DpiHelper.IsScalingRequired && (b.Size.Width != iconsWidth || b.Size.Height != iconsHeight)) + { + Bitmap scaledBitmap = DpiHelper.CreateResizedBitmap(b, desiredSize); + if (scaledBitmap != null) + { + b.Dispose(); + b = scaledBitmap; + } + } + return b; + } + + /// + protected override object GetClipboardContent(int rowIndex, + bool firstCell, + bool lastCell, + bool inFirstRow, + bool inLastRow, + string format) + { + if (this.DataGridView == null) + { + return null; + } + if (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + // Not using formatted values for header cells. + object val = GetValue(rowIndex); + StringBuilder sb = new StringBuilder(64); + + Debug.Assert((!this.DataGridView.RightToLeftInternal && firstCell) || (this.DataGridView.RightToLeftInternal && lastCell)); + + if (String.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase)) + { + if (inFirstRow) + { + sb.Append(""); + } + sb.Append(""); + sb.Append(""); + if (lastCell) + { + sb.Append(""); + if (inLastRow) + { + sb.Append("
"); + if (val != null) + { + sb.Append(""); + FormatPlainTextAsHtml(val.ToString(), new StringWriter(sb, CultureInfo.CurrentCulture)); + sb.Append(""); + } + else + { + sb.Append(" "); + } + sb.Append("
"); + } + } + return sb.ToString(); + } + else + { + bool csv = String.Equals(format, DataFormats.CommaSeparatedValue, StringComparison.OrdinalIgnoreCase); + if (csv || + String.Equals(format, DataFormats.Text, StringComparison.OrdinalIgnoreCase) || + String.Equals(format, DataFormats.UnicodeText, StringComparison.OrdinalIgnoreCase)) + { + if (val != null) + { + bool escapeApplied = false; + int insertionPoint = sb.Length; + FormatPlainText(val.ToString(), csv, new StringWriter(sb, CultureInfo.CurrentCulture), ref escapeApplied); + if (escapeApplied) + { + Debug.Assert(csv); + sb.Insert(insertionPoint, '"'); + } + } + if (lastCell) + { + if (!inLastRow) + { + sb.Append((char) Keys.Return); + sb.Append((char) Keys.LineFeed); + } + } + else + { + sb.Append(csv ? ',' : (char) Keys.Tab); + } + return sb.ToString(); + } + else + { + return null; + } + } + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || this.OwningRow == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + // the content bounds are computed on demand + // we mimic a lot of the painting code + + // get the borders + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle contentBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + null /*errorText*/, // contentBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle contentBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(contentBoundsDebug.Equals(contentBounds)); +#endif + + return contentBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + !this.DataGridView.ShowRowErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + object value = GetValue(rowIndex); + object formattedValue = GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + Rectangle errorBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue /*formattedValue*/, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + + return errorBounds; + } + + /// + protected internal override string GetErrorText(int rowIndex) + { + if (this.OwningRow == null) + { + return base.GetErrorText(rowIndex); + } + else + { + return this.OwningRow.GetErrorText(rowIndex); + } + } + + /// + public override ContextMenuStrip GetInheritedContextMenuStrip(int rowIndex) + { + if (this.DataGridView != null && (rowIndex < 0 || rowIndex >= this.DataGridView.Rows.Count)) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + ContextMenuStrip contextMenuStrip = GetContextMenuStrip(rowIndex); + if (contextMenuStrip != null) + { + return contextMenuStrip; + } + + if (this.DataGridView != null) + { + return this.DataGridView.ContextMenuStrip; + } + else + { + return null; + } + } + + /// + public override DataGridViewCellStyle GetInheritedStyle(DataGridViewCellStyle inheritedCellStyle, int rowIndex, bool includeColors) + { + Debug.Assert(this.DataGridView != null); + + DataGridViewCellStyle inheritedCellStyleTmp = (inheritedCellStyle == null) ? new DataGridViewCellStyle() : inheritedCellStyle; + + DataGridViewCellStyle cellStyle = null; + if (this.HasStyle) + { + cellStyle = this.Style; + Debug.Assert(cellStyle != null); + } + + DataGridViewCellStyle rowHeadersStyle = this.DataGridView.RowHeadersDefaultCellStyle; + Debug.Assert(rowHeadersStyle != null); + + DataGridViewCellStyle dataGridViewStyle = this.DataGridView.DefaultCellStyle; + Debug.Assert(dataGridViewStyle != null); + + if (includeColors) + { + if (cellStyle != null && !cellStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = cellStyle.BackColor; + } + else if (!rowHeadersStyle.BackColor.IsEmpty) + { + inheritedCellStyleTmp.BackColor = rowHeadersStyle.BackColor; + } + else + { + inheritedCellStyleTmp.BackColor = dataGridViewStyle.BackColor; + } + + if (cellStyle != null && !cellStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = cellStyle.ForeColor; + } + else if (!rowHeadersStyle.ForeColor.IsEmpty) + { + inheritedCellStyleTmp.ForeColor = rowHeadersStyle.ForeColor; + } + else + { + inheritedCellStyleTmp.ForeColor = dataGridViewStyle.ForeColor; + } + + if (cellStyle != null && !cellStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = cellStyle.SelectionBackColor; + } + else if (!rowHeadersStyle.SelectionBackColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionBackColor = rowHeadersStyle.SelectionBackColor; + } + else + { + inheritedCellStyleTmp.SelectionBackColor = dataGridViewStyle.SelectionBackColor; + } + + if (cellStyle != null && !cellStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = cellStyle.SelectionForeColor; + } + else if (!rowHeadersStyle.SelectionForeColor.IsEmpty) + { + inheritedCellStyleTmp.SelectionForeColor = rowHeadersStyle.SelectionForeColor; + } + else + { + inheritedCellStyleTmp.SelectionForeColor = dataGridViewStyle.SelectionForeColor; + } + } + + if (cellStyle != null && cellStyle.Font != null) + { + inheritedCellStyleTmp.Font = cellStyle.Font; + } + else if (rowHeadersStyle.Font != null) + { + inheritedCellStyleTmp.Font = rowHeadersStyle.Font; + } + else + { + inheritedCellStyleTmp.Font = dataGridViewStyle.Font; + } + + if (cellStyle != null && !cellStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = cellStyle.NullValue; + } + else if (!rowHeadersStyle.IsNullValueDefault) + { + inheritedCellStyleTmp.NullValue = rowHeadersStyle.NullValue; + } + else + { + inheritedCellStyleTmp.NullValue = dataGridViewStyle.NullValue; + } + + if (cellStyle != null && !cellStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = cellStyle.DataSourceNullValue; + } + else if (!rowHeadersStyle.IsDataSourceNullValueDefault) + { + inheritedCellStyleTmp.DataSourceNullValue = rowHeadersStyle.DataSourceNullValue; + } + else + { + inheritedCellStyleTmp.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue; + } + + if (cellStyle != null && cellStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = cellStyle.Format; + } + else if (rowHeadersStyle.Format.Length != 0) + { + inheritedCellStyleTmp.Format = rowHeadersStyle.Format; + } + else + { + inheritedCellStyleTmp.Format = dataGridViewStyle.Format; + } + + if (cellStyle != null && !cellStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = cellStyle.FormatProvider; + } + else if (!rowHeadersStyle.IsFormatProviderDefault) + { + inheritedCellStyleTmp.FormatProvider = rowHeadersStyle.FormatProvider; + } + else + { + inheritedCellStyleTmp.FormatProvider = dataGridViewStyle.FormatProvider; + } + + if (cellStyle != null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = cellStyle.Alignment; + } + else if (rowHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet) + { + inheritedCellStyleTmp.AlignmentInternal = rowHeadersStyle.Alignment; + } + else + { + Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet); + inheritedCellStyleTmp.AlignmentInternal = dataGridViewStyle.Alignment; + } + + if (cellStyle != null && cellStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = cellStyle.WrapMode; + } + else if (rowHeadersStyle.WrapMode != DataGridViewTriState.NotSet) + { + inheritedCellStyleTmp.WrapModeInternal = rowHeadersStyle.WrapMode; + } + else + { + Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet); + inheritedCellStyleTmp.WrapModeInternal = dataGridViewStyle.WrapMode; + } + + if (cellStyle != null && cellStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = cellStyle.Tag; + } + else if (rowHeadersStyle.Tag != null) + { + inheritedCellStyleTmp.Tag = rowHeadersStyle.Tag; + } + else + { + inheritedCellStyleTmp.Tag = dataGridViewStyle.Tag; + } + + if (cellStyle != null && cellStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = cellStyle.Padding; + } + else if (rowHeadersStyle.Padding != Padding.Empty) + { + inheritedCellStyleTmp.PaddingInternal = rowHeadersStyle.Padding; + } + else + { + inheritedCellStyleTmp.PaddingInternal = dataGridViewStyle.Padding; + } + + return inheritedCellStyleTmp; + } + + private static Bitmap GetPencilBitmap(bool rightToLeft) + { + return rightToLeft ? DataGridViewRowHeaderCell.PencilRTLBitmap : DataGridViewRowHeaderCell.PencilLTRBitmap; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + DataGridViewAdvancedBorderStyle dgvabsPlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective; + dgvabsEffective = this.OwningRow.AdjustRowHeaderBorderStyle(this.DataGridView.AdvancedRowHeadersBorderStyle, + dgvabsPlaceholder, + false /*singleVerticalBorderAdded*/, + false /*singleHorizontalBorderAdded*/, + false /*isFirstDisplayedRow*/, + false /*isLastVisibleRow*/); + Rectangle borderWidthsRect = BorderWidths(dgvabsEffective); + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + // Add the XP theming margins to the borders. + Rectangle rectThemeMargins = DataGridViewHeaderCell.GetThemeMargins(graphics); + borderAndPaddingWidths += rectThemeMargins.Y; + borderAndPaddingWidths += rectThemeMargins.Height; + borderAndPaddingHeights += rectThemeMargins.X; + borderAndPaddingHeights += rectThemeMargins.Width; + } + + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + object val = GetValue(rowIndex); + if (!(val is string)) + { + val = null; + } + return DataGridViewUtilities.GetPreferredRowHeaderSize(graphics, + (string) val, + cellStyle, + borderAndPaddingWidths, + borderAndPaddingHeights, + this.DataGridView.ShowRowErrors, + true /*showGlyph*/, + constraintSize, + flags); + } + + /// + protected override object GetValue(int rowIndex) + { + // We allow multiple rows to share the same row header value. The row header cell's cloning does this. + // So here we need to allow rowIndex == -1. + if (this.DataGridView != null && (rowIndex < -1 || rowIndex >= this.DataGridView.Rows.Count)) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + return this.Properties.GetObject(PropCellValue); + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/ + , + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates dataGridViewElementState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + // If computeContentBounds == TRUE then resultBounds will be the contentBounds. + // If computeErrorIconBounds == TRUE then resultBounds will be the error icon bounds. + // Else resultBounds will be Rectangle.Empty; + Rectangle resultBounds = Rectangle.Empty; + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + Rectangle backgroundBounds = valBounds; + + bool cellSelected = (dataGridViewElementState & DataGridViewElementStates.Selected) != 0; + + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + if (backgroundBounds.Width > 0 && backgroundBounds.Height > 0) + { + if (paint && DataGridViewCell.PaintBackground(paintParts)) + { + // XP Theming + int state = (int)HeaderItemState.Normal; + if (this.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || + this.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.ButtonState == ButtonState.Pushed); + state = (int)HeaderItemState.Pressed; + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && + this.DataGridView.MouseEnteredCellAddress.X == -1) + { + state = (int)HeaderItemState.Hot; + } + else if (cellSelected) + { + state = (int)HeaderItemState.Pressed; + } + } + // Flip the column header background + using (Bitmap bmFlipXPThemes = new Bitmap(backgroundBounds.Height, backgroundBounds.Width)) { + using (Graphics gFlip = Graphics.FromImage(bmFlipXPThemes)) { + DataGridViewRowHeaderCellRenderer.DrawHeader(gFlip, new Rectangle(0, 0, backgroundBounds.Height, backgroundBounds.Width), state); + bmFlipXPThemes.RotateFlip(this.DataGridView.RightToLeftInternal ? RotateFlipType.Rotate90FlipNone : RotateFlipType.Rotate270FlipY); + + graphics.DrawImage(bmFlipXPThemes, + backgroundBounds, + new Rectangle(0, 0, backgroundBounds.Width, backgroundBounds.Height), + GraphicsUnit.Pixel); + } + } + } + // update the val bounds + Rectangle rectThemeMargins = DataGridViewHeaderCell.GetThemeMargins(graphics); + if (this.DataGridView.RightToLeftInternal) + { + valBounds.X += rectThemeMargins.Height; + } + else + { + valBounds.X += rectThemeMargins.Y; + } + valBounds.Width -= rectThemeMargins.Y + rectThemeMargins.Height; + valBounds.Height -= rectThemeMargins.X + rectThemeMargins.Width; + valBounds.Y += rectThemeMargins.X; + } + } + else + { + // No visual style applied + if (valBounds.Width > 0 && valBounds.Height > 0) + { + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255) + { + graphics.FillRectangle(br, valBounds); + } + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + } + + Bitmap bmp = null; + + if (valBounds.Width > 0 && valBounds.Height > 0) + { + Rectangle errorBounds = valBounds; + string formattedString = formattedValue as string; + if (!String.IsNullOrEmpty(formattedString)) + { + // There is text to display + if (valBounds.Width >= iconsWidth + + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth && + valBounds.Height >= iconsHeight + + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight) + { + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + // There is enough room for the potential glyph which is the first priority + if (this.DataGridView.CurrentCellAddress.Y == rowIndex) + { + if (this.DataGridView.VirtualMode) + { + if (this.DataGridView.IsCurrentRowDirty && this.DataGridView.ShowEditingIcon) + { + bmp = GetPencilBitmap(this.DataGridView.RightToLeftInternal); + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = GetArrowStarBitmap(this.DataGridView.RightToLeftInternal); + } + else + { + bmp = GetArrowBitmap(this.DataGridView.RightToLeftInternal); + } + } + else + { + if (this.DataGridView.IsCurrentCellDirty && this.DataGridView.ShowEditingIcon) + { + bmp = GetPencilBitmap(this.DataGridView.RightToLeftInternal); + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = GetArrowStarBitmap(this.DataGridView.RightToLeftInternal); + } + else + { + bmp = GetArrowBitmap(this.DataGridView.RightToLeftInternal); + } + } + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = DataGridViewRowHeaderCell.StarBitmap; + } + if (bmp != null) + { + Color iconColor; + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + iconColor = DataGridViewRowHeaderCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + iconColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + lock (bmp) + { + PaintIcon(graphics, bmp, valBounds, iconColor); + } + } + } + if (!this.DataGridView.RightToLeftInternal) + { + valBounds.X += iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + valBounds.Width -= iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + Debug.Assert(valBounds.Width >= 0); + Debug.Assert(valBounds.Height >= 0); + } + // Second priority is text + // Font independent margins + valBounds.Offset(DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + DATAGRIDVIEWROWHEADERCELL_contentMarginWidth, DATAGRIDVIEWROWHEADERCELL_verticalTextMargin); + valBounds.Width -= DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + 2 * DATAGRIDVIEWROWHEADERCELL_contentMarginWidth + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight; + valBounds.Height -= 2 * DATAGRIDVIEWROWHEADERCELL_verticalTextMargin; + if (valBounds.Width > 0 && valBounds.Height > 0) + { + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (this.DataGridView.ShowRowErrors && valBounds.Width > iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth) + { + // Check if the text fits if we remove the room required for the row error icon + Size maxBounds = new Size(valBounds.Width - iconsWidth - 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth, valBounds.Height); + if (DataGridViewCell.TextFitsInBounds(graphics, + formattedString, + cellStyle.Font, + maxBounds, + flags)) + { + // There is enough room for both the text and the row error icon, so use it all. + if (this.DataGridView.RightToLeftInternal) + { + valBounds.X += iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + valBounds.Width -= iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + } + + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if (paint) + { + Color textColor; + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + textColor = DataGridViewRowHeaderCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + textColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + TextRenderer.DrawText(graphics, + formattedString, + cellStyle.Font, + valBounds, + textColor, + flags); + } + else if (computeContentBounds) + { + resultBounds = DataGridViewUtilities.GetTextBounds(valBounds, formattedString, flags, cellStyle); + } + } + } + // Third priority is the row error icon, which may be painted on top of text + if (errorBounds.Width >= 3 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + + 2 * iconsWidth) + { + // There is enough horizontal room for the error icon and the glyph + if (paint && this.DataGridView.ShowRowErrors && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(graphics, clipBounds, errorBounds, errorText); + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + } + } + } + else + { + // There is no text to display + if (valBounds.Width >= iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth && + valBounds.Height >= iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight) + { + if (paint && DataGridViewCell.PaintContentBackground(paintParts)) + { + // There is enough room for the potential icon + if (this.DataGridView.CurrentCellAddress.Y == rowIndex) + { + if (this.DataGridView.VirtualMode) + { + if (this.DataGridView.IsCurrentRowDirty && this.DataGridView.ShowEditingIcon) + { + bmp = GetPencilBitmap(this.DataGridView.RightToLeftInternal); + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = GetArrowStarBitmap(this.DataGridView.RightToLeftInternal); + } + else + { + bmp = GetArrowBitmap(this.DataGridView.RightToLeftInternal); + } + } + else + { + if (this.DataGridView.IsCurrentCellDirty && this.DataGridView.ShowEditingIcon) + { + bmp = GetPencilBitmap(this.DataGridView.RightToLeftInternal); + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = GetArrowStarBitmap(this.DataGridView.RightToLeftInternal); + } + else + { + bmp = GetArrowBitmap(this.DataGridView.RightToLeftInternal); + } + } + } + else if (this.DataGridView.NewRowIndex == rowIndex) + { + bmp = DataGridViewRowHeaderCell.StarBitmap; + } + if (bmp != null) + { + lock (bmp) + { + Color iconColor; + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + iconColor = DataGridViewRowHeaderCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + iconColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + PaintIcon(graphics, bmp, valBounds, iconColor); + } + } + } + } + + if (errorBounds.Width >= 3 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + + 2 * iconsWidth) + { + // There is enough horizontal room for the error icon + if (paint && this.DataGridView.ShowRowErrors && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(graphics, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + } + } + } + } + // else no room for content or error icon, resultBounds = Rectangle.Empty + + return resultBounds; + } + + private void PaintIcon(Graphics g, Bitmap bmp, Rectangle bounds, Color foreColor) + { + Rectangle bmpRect = new Rectangle(this.DataGridView.RightToLeftInternal ? + bounds.Right - DATAGRIDVIEWROWHEADERCELL_iconMarginWidth - iconsWidth : + bounds.Left + DATAGRIDVIEWROWHEADERCELL_iconMarginWidth, + bounds.Y + (bounds.Height - iconsHeight)/2, + iconsWidth, + iconsHeight); + colorMap[0].NewColor = foreColor; + colorMap[0].OldColor = Color.Black; + + ImageAttributes attr = new ImageAttributes(); + attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); + g.DrawImage(bmp, bmpRect, 0, 0, iconsWidth, iconsHeight, GraphicsUnit.Pixel, attr); + attr.Dispose(); + } + + /// + protected override bool SetValue(int rowIndex, object value) + { + object originalValue = GetValue(rowIndex); + if (value != null || this.Properties.ContainsObject(PropCellValue)) + { + this.Properties.SetObject(PropCellValue, value); + } + if (this.DataGridView != null && originalValue != value) + { + RaiseCellValueChanged(new DataGridViewCellEventArgs(-1, rowIndex)); + } + return true; + } + + /// + /// + /// + /// + public override string ToString() + { + return "DataGridViewRowHeaderCell { RowIndex=" + RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + private class DataGridViewRowHeaderCellRenderer + { + private static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewRowHeaderCellRenderer() + { + } + + public static VisualStyleRenderer VisualStyleRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(HeaderElement); + } + + return visualStyleRenderer; + } + } + + public static void DrawHeader(Graphics g, Rectangle bounds, int headerState) + { + VisualStyleRenderer.SetParameters(HeaderElement.ClassName, HeaderElement.Part, headerState); + VisualStyleRenderer.DrawBackground(g, bounds, Rectangle.Truncate(g.ClipBounds)); + } + } + + /// + protected class DataGridViewRowHeaderCellAccessibleObject : DataGridViewCellAccessibleObject + { + /// + public DataGridViewRowHeaderCellAccessibleObject(DataGridViewRowHeaderCell owner) : base(owner) + { + } + + /// + public override Rectangle Bounds + { + get + { + if (this.Owner.OwningRow == null) + { + return Rectangle.Empty; + } + + // use the parent row acc obj bounds + Rectangle rowRect = this.ParentPrivate.Bounds; + rowRect.Width = this.Owner.DataGridView.RowHeadersWidth; + return rowRect; + } + } + + /// + public override string DefaultAction + { + get + { + if (this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || + this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + return SR.GetString(SR.DataGridView_RowHeaderCellAccDefaultAction); + } + else + { + return String.Empty; + } + } + } + + /// + public override string Name + { + get + { + if (this.ParentPrivate != null) + { + return this.ParentPrivate.Name; + } + else + { + return String.Empty; + } + } + } + + /// + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.ParentPrivate; + } + } + + private AccessibleObject ParentPrivate + { + get + { + if (this.Owner.OwningRow == null) + { + return null; + } + else + { + return this.Owner.OwningRow.AccessibilityObject; + } + } + } + + /// + public override AccessibleRole Role + { + get + { + return AccessibleRole.RowHeader; + } + } + + /// + public override AccessibleStates State + { + get + { + AccessibleStates resultState = AccessibleStates.Selectable; + + // get the Offscreen state from the base method. + AccessibleStates state = base.State; + if ((state & AccessibleStates.Offscreen) == AccessibleStates.Offscreen) + { + resultState |= AccessibleStates.Offscreen; + } + + if (this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || + this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect) + { + if (this.Owner.OwningRow != null && this.Owner.OwningRow.Selected) + { + resultState |= AccessibleStates.Selected; + } + } + + return resultState; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return String.Empty; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + if ((this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || + this.Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect ) && + this.Owner.OwningRow != null) + { + this.Owner.OwningRow.Selected = true; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + Debug.Assert(this.Owner.DataGridView.RowHeadersVisible, "if the rows are not visible how did you get the row headers acc obj?"); + switch (navigationDirection) + { + case AccessibleNavigation.Next: + if (this.Owner.OwningRow != null && this.Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) > 0) + { + // go to the next sibling + return this.ParentPrivate.GetChild(1); + } + else + { + return null; + } + case AccessibleNavigation.Down: + if (this.Owner.OwningRow == null) + { + return null; + } + else + { + if (this.Owner.OwningRow.Index == this.Owner.DataGridView.Rows.GetLastRow(DataGridViewElementStates.Visible)) + { + return null; + } + else + { + int nextVisibleRow = this.Owner.DataGridView.Rows.GetNextRow(this.Owner.OwningRow.Index, DataGridViewElementStates.Visible); + int actualDisplayIndex = this.Owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, nextVisibleRow); + + if (this.Owner.DataGridView.ColumnHeadersVisible) + { + // + 1 because the first child in the data grid view acc obj is the top row header acc obj + return this.Owner.DataGridView.AccessibilityObject.GetChild(1 + actualDisplayIndex).GetChild(0); + } + else + { + return this.Owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex).GetChild(0); + } + } + } + case AccessibleNavigation.Previous: + return null; + case AccessibleNavigation.Up: + if (this.Owner.OwningRow == null) + { + return null; + } + else + { + if (this.Owner.OwningRow.Index == this.Owner.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible)) + { + if (this.Owner.DataGridView.ColumnHeadersVisible) + { + // Return the top left header cell accessible object. + Debug.Assert(this.Owner.DataGridView.TopLeftHeaderCell.AccessibilityObject == this.Owner.DataGridView.AccessibilityObject.GetChild(0).GetChild(0)); + return this.Owner.DataGridView.AccessibilityObject.GetChild(0).GetChild(0); + } + else + { + return null; + } + } + else + { + int previousVisibleRow = this.Owner.DataGridView.Rows.GetPreviousRow(this.Owner.OwningRow.Index, DataGridViewElementStates.Visible); + int actualDisplayIndex = this.Owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, previousVisibleRow); + if (this.Owner.DataGridView.ColumnHeadersVisible) + { + // + 1 because the first child in the data grid view acc obj is the top row header acc obj + return this.Owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex + 1).GetChild(0); + } + else + { + return this.Owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex).GetChild(0); + } + } + } + default: + return null; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) + { + if (this.Owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + DataGridViewRowHeaderCell dataGridViewCell = (DataGridViewRowHeaderCell)this.Owner; + DataGridView dataGridView = dataGridViewCell.DataGridView; + + if (dataGridView == null) + { + return; + } + if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) + { + dataGridView.FocusInternal(); + } + if (dataGridViewCell.OwningRow != null && + (dataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect || + dataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)) + { + if ((flags & (AccessibleSelection.TakeSelection | AccessibleSelection.AddSelection)) != 0) + { + dataGridViewCell.OwningRow.Selected = true; + } + else if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection) + { + dataGridViewCell.OwningRow.Selected = false; + } + } + } + + #region IRawElementProviderFragment Implementation + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + if (this.Owner.OwningRow == null) + { + return null; + } + + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return this.Owner.OwningRow.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + if (this.Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) > 0) + { + // go to the next sibling + return this.Owner.OwningRow.AccessibilityObject.GetChild(1); + } + else + { + return null; + } + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + default: + return null; + } + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override object GetPropertyValue(int propertyId) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_HeaderControlTypeId; + case NativeMethods.UIA_IsEnabledPropertyId: + return Owner.DataGridView.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return this.Help ?? string.Empty; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (this.State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (this.State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + } + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoNeededEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoNeededEventArgs.cs new file mode 100644 index 000000000..d61c9bd20 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoNeededEventArgs.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Globalization; + + /// + public class DataGridViewRowHeightInfoNeededEventArgs : EventArgs + { + private int rowIndex; + private int height; + private int minimumHeight; + + internal DataGridViewRowHeightInfoNeededEventArgs() + { + this.rowIndex = -1; + this.height = -1; + this.minimumHeight = -1; + } + + /// + public int Height + { + get + { + return this.height; + } + set + { + if (value < this.minimumHeight) + { + value = this.minimumHeight; + } + if (value > DataGridViewBand.maxBandThickness) + { + throw new ArgumentOutOfRangeException("Height", SR.GetString(SR.InvalidHighBoundArgumentEx, "Height", (value).ToString(CultureInfo.CurrentCulture), (DataGridViewBand.maxBandThickness).ToString(CultureInfo.CurrentCulture))); + } + this.height = value; + } + } + + /// + public int MinimumHeight + { + get + { + return this.minimumHeight; + } + set + { + if (value < DataGridViewBand.minBandThickness) + { + throw new ArgumentOutOfRangeException("MinimumHeight", value, SR.GetString(SR.DataGridViewBand_MinimumHeightSmallerThanOne, (DataGridViewBand.minBandThickness).ToString(CultureInfo.CurrentCulture))); + } + if (this.height < value) + { + this.height = value; + } + this.minimumHeight = value; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + internal void SetProperties(int rowIndex, int height, int minimumHeight) + { + Debug.Assert(rowIndex >= -1); + Debug.Assert(height > 0); + Debug.Assert(minimumHeight > 0); + Debug.Assert(height >= minimumHeight); + this.rowIndex = rowIndex; + this.height = height; + this.minimumHeight = minimumHeight; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoPushedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoPushedEventArgs.cs new file mode 100644 index 000000000..8108b5c37 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowHeightInfoPushedEventArgs.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + public class DataGridViewRowHeightInfoPushedEventArgs : HandledEventArgs + { + private int rowIndex; + private int height; + private int minimumHeight; + + internal DataGridViewRowHeightInfoPushedEventArgs(int rowIndex, int height, int minimumHeight) : base(false) + { + Debug.Assert(rowIndex >= -1); + this.rowIndex = rowIndex; + this.height = height; + this.minimumHeight = minimumHeight; + } + + /// + public int Height + { + get + { + return this.height; + } + } + + /// + public int MinimumHeight + { + get + { + return this.minimumHeight; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowPostPaintEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowPostPaintEventArgs.cs new file mode 100644 index 000000000..7596328c8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowPostPaintEventArgs.cs @@ -0,0 +1,279 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.ComponentModel; + using System.Diagnostics; + + /// + public class DataGridViewRowPostPaintEventArgs : EventArgs + { + private DataGridView dataGridView; + private Graphics graphics; + private Rectangle clipBounds; + private Rectangle rowBounds; + private DataGridViewCellStyle inheritedRowStyle; + private int rowIndex; + private DataGridViewElementStates rowState; + private string errorText; + private bool isFirstDisplayedRow; + private bool isLastVisibleRow; + + /// + public DataGridViewRowPostPaintEventArgs(DataGridView dataGridView, + Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + string errorText, + DataGridViewCellStyle inheritedRowStyle, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + if (dataGridView == null) + { + throw new ArgumentNullException("dataGridView"); + } + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + if (inheritedRowStyle == null) + { + throw new ArgumentNullException("inheritedRowStyle"); + } + this.dataGridView = dataGridView; + this.graphics = graphics; + this.clipBounds = clipBounds; + this.rowBounds = rowBounds; + this.rowIndex = rowIndex; + this.rowState = rowState; + this.errorText = errorText; + this.inheritedRowStyle = inheritedRowStyle; + this.isFirstDisplayedRow = isFirstDisplayedRow; + this.isLastVisibleRow = isLastVisibleRow; + } + + internal DataGridViewRowPostPaintEventArgs(DataGridView dataGridView) + { + Debug.Assert(dataGridView != null); + this.dataGridView = dataGridView; + } + + /// + public Rectangle ClipBounds + { + get + { + return this.clipBounds; + } + set + { + this.clipBounds = value; + } + } + + /// + public string ErrorText + { + get + { + return this.errorText; + } + } + + /// + public Graphics Graphics + { + get + { + return this.graphics; + } + } + + /// + public DataGridViewCellStyle InheritedRowStyle + { + get + { + return this.inheritedRowStyle; + } + } + + /// + public bool IsFirstDisplayedRow + { + get + { + return this.isFirstDisplayedRow; + } + } + + /// + public bool IsLastVisibleRow + { + get + { + return this.isLastVisibleRow; + } + } + + /// + public Rectangle RowBounds + { + get + { + return this.rowBounds; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public DataGridViewElementStates State + { + get + { + return this.rowState; + } + } + + /// + public void DrawFocus(Rectangle bounds, bool cellsPaintSelectionBackground) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(rowIndex).DrawFocus(this.graphics, + this.clipBounds, + bounds, + this.rowIndex, + this.rowState, + this.inheritedRowStyle, + cellsPaintSelectionBackground); + } + + /// + public void PaintCells(Rectangle clipBounds, DataGridViewPaintParts paintParts) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + /// + public void PaintCellsBackground(Rectangle clipBounds, bool cellsPaintSelectionBackground) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + DataGridViewPaintParts paintParts = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border; + if (cellsPaintSelectionBackground) + { + paintParts |= DataGridViewPaintParts.SelectionBackground; + } + this.dataGridView.Rows.SharedRow(rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + /// + public void PaintCellsContent(Rectangle clipBounds) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon); + } + + /// + public void PaintHeader(bool paintSelectionBackground) + { + DataGridViewPaintParts paintParts = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon; + if (paintSelectionBackground) + { + paintParts |= DataGridViewPaintParts.SelectionBackground; + } + PaintHeader(paintParts); + } + + /// + public void PaintHeader(DataGridViewPaintParts paintParts) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(rowIndex).PaintHeader(this.graphics, + this.clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + internal void SetProperties(Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + string errorText, + DataGridViewCellStyle inheritedRowStyle, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + Debug.Assert(graphics != null); + Debug.Assert(inheritedRowStyle != null); + + this.graphics = graphics; + this.clipBounds = clipBounds; + this.rowBounds = rowBounds; + this.rowIndex = rowIndex; + this.rowState = rowState; + this.errorText = errorText; + this.inheritedRowStyle = inheritedRowStyle; + this.isFirstDisplayedRow = isFirstDisplayedRow; + this.isLastVisibleRow = isLastVisibleRow; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowPrePaintEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowPrePaintEventArgs.cs new file mode 100644 index 000000000..e3e6d2d50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowPrePaintEventArgs.cs @@ -0,0 +1,300 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.ComponentModel; + using System.Diagnostics; + + /// + public class DataGridViewRowPrePaintEventArgs : HandledEventArgs + { + private DataGridView dataGridView; + private Graphics graphics; + private Rectangle clipBounds; + private Rectangle rowBounds; + private DataGridViewCellStyle inheritedRowStyle; + private int rowIndex; + private DataGridViewElementStates rowState; + private string errorText; + private bool isFirstDisplayedRow; + private bool isLastVisibleRow; + private DataGridViewPaintParts paintParts; + + /// + public DataGridViewRowPrePaintEventArgs(DataGridView dataGridView, + Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + string errorText, + DataGridViewCellStyle inheritedRowStyle, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + if (dataGridView == null) + { + throw new ArgumentNullException("dataGridView"); + } + if (graphics == null) + { + throw new ArgumentNullException("graphics"); + } + if (inheritedRowStyle == null) + { + throw new ArgumentNullException("inheritedRowStyle"); + } + this.dataGridView = dataGridView; + this.graphics = graphics; + this.clipBounds = clipBounds; + this.rowBounds = rowBounds; + this.rowIndex = rowIndex; + this.rowState = rowState; + this.errorText = errorText; + this.inheritedRowStyle = inheritedRowStyle; + this.isFirstDisplayedRow = isFirstDisplayedRow; + this.isLastVisibleRow = isLastVisibleRow; + this.paintParts = DataGridViewPaintParts.All; + } + + internal DataGridViewRowPrePaintEventArgs(DataGridView dataGridView) + { + Debug.Assert(dataGridView != null); + this.dataGridView = dataGridView; + } + + /// + public Rectangle ClipBounds + { + get + { + return this.clipBounds; + } + set + { + this.clipBounds = value; + } + } + + /// + public string ErrorText + { + get + { + return this.errorText; + } + } + + /// + public Graphics Graphics + { + get + { + return this.graphics; + } + } + + /// + public DataGridViewCellStyle InheritedRowStyle + { + get + { + return this.inheritedRowStyle; + } + } + + /// + public bool IsFirstDisplayedRow + { + get + { + return this.isFirstDisplayedRow; + } + } + + /// + public bool IsLastVisibleRow + { + get + { + return this.isLastVisibleRow; + } + } + + /// + public DataGridViewPaintParts PaintParts + { + get + { + return this.paintParts; + } + set + { + if ((value & ~DataGridViewPaintParts.All) != 0) + { + throw new ArgumentException(SR.GetString(SR.DataGridView_InvalidDataGridViewPaintPartsCombination, "value")); + } + this.paintParts = value; + } + } + + /// + public Rectangle RowBounds + { + get + { + return this.rowBounds; + } + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public DataGridViewElementStates State + { + get + { + return this.rowState; + } + } + + /// + public void DrawFocus(Rectangle bounds, bool cellsPaintSelectionBackground) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(this.rowIndex).DrawFocus(this.graphics, + this.clipBounds, + bounds, + this.rowIndex, + this.rowState, + this.inheritedRowStyle, + cellsPaintSelectionBackground); + } + + /// + public void PaintCells(Rectangle clipBounds, DataGridViewPaintParts paintParts) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(this.rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + /// + public void PaintCellsBackground(Rectangle clipBounds, bool cellsPaintSelectionBackground) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + DataGridViewPaintParts paintParts = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border; + if (cellsPaintSelectionBackground) + { + paintParts |= DataGridViewPaintParts.SelectionBackground; + } + this.dataGridView.Rows.SharedRow(this.rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + /// + public void PaintCellsContent(Rectangle clipBounds) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(this.rowIndex).PaintCells(this.graphics, + clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon); + } + + /// + public void PaintHeader(bool paintSelectionBackground) + { + DataGridViewPaintParts paintParts = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon; + if (paintSelectionBackground) + { + paintParts |= DataGridViewPaintParts.SelectionBackground; + } + PaintHeader(paintParts); + } + + /// + public void PaintHeader(DataGridViewPaintParts paintParts) + { + if (this.rowIndex < 0 || this.rowIndex >= this.dataGridView.Rows.Count) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewElementPaintingEventArgs_RowIndexOutOfRange)); + } + this.dataGridView.Rows.SharedRow(this.rowIndex).PaintHeader(this.graphics, + this.clipBounds, + this.rowBounds, + this.rowIndex, + this.rowState, + this.isFirstDisplayedRow, + this.isLastVisibleRow, + paintParts); + } + + internal void SetProperties(Graphics graphics, + Rectangle clipBounds, + Rectangle rowBounds, + int rowIndex, + DataGridViewElementStates rowState, + string errorText, + DataGridViewCellStyle inheritedRowStyle, + bool isFirstDisplayedRow, + bool isLastVisibleRow) + { + Debug.Assert(graphics != null); + Debug.Assert(inheritedRowStyle != null); + + this.graphics = graphics; + this.clipBounds = clipBounds; + this.rowBounds = rowBounds; + this.rowIndex = rowIndex; + this.rowState = rowState; + this.errorText = errorText; + this.inheritedRowStyle = inheritedRowStyle; + this.isFirstDisplayedRow = isFirstDisplayedRow; + this.isLastVisibleRow = isLastVisibleRow; + this.paintParts = DataGridViewPaintParts.All; + this.Handled = false; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowStateChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowStateChangedEventArgs.cs new file mode 100644 index 000000000..790ac5fcc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowStateChangedEventArgs.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + public class DataGridViewRowStateChangedEventArgs : EventArgs + { + private DataGridViewRow dataGridViewRow; + private DataGridViewElementStates stateChanged; + + /// + public DataGridViewRowStateChangedEventArgs(DataGridViewRow dataGridViewRow, DataGridViewElementStates stateChanged) + { + this.dataGridViewRow = dataGridViewRow; + this.stateChanged = stateChanged; + } + + /// + public DataGridViewRow Row + { + get + { + return this.dataGridViewRow; + } + } + + /// + public DataGridViewElementStates StateChanged + { + get + { + return this.stateChanged; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowsAddedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowsAddedEventArgs.cs new file mode 100644 index 000000000..70a7bccd2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowsAddedEventArgs.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + /// + /// [To be supplied.] + /// + public class DataGridViewRowsAddedEventArgs : EventArgs + { + private int rowIndex, rowCount; + + /// + public DataGridViewRowsAddedEventArgs(int rowIndex, int rowCount) + { + Debug.Assert(rowIndex >= 0); + Debug.Assert(rowCount >= 1); + this.rowIndex = rowIndex; + this.rowCount = rowCount; + } + + /// + /// + /// [To be supplied.] + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + /// + /// [To be supplied.] + /// + public int RowCount + { + get + { + return this.rowCount; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewRowsRemovedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewRowsRemovedEventArgs.cs new file mode 100644 index 000000000..28876eb0d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewRowsRemovedEventArgs.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Globalization; + + /// + public class DataGridViewRowsRemovedEventArgs : EventArgs + { + private int rowIndex, rowCount; + + /// + public DataGridViewRowsRemovedEventArgs(int rowIndex, int rowCount) + { + if (rowIndex < 0) + { + throw new ArgumentOutOfRangeException("rowIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "rowIndex", rowIndex.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (rowCount < 1) + { + throw new ArgumentOutOfRangeException("rowCount", SR.GetString(SR.InvalidLowBoundArgumentEx, "rowCount", rowCount.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + this.rowIndex = rowIndex; + this.rowCount = rowCount; + } + + /// + public int RowIndex + { + get + { + return this.rowIndex; + } + } + + /// + public int RowCount + { + get + { + return this.rowCount; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellCollection.cs new file mode 100644 index 000000000..93fe61278 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellCollection.cs @@ -0,0 +1,238 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Diagnostics; + using System; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Represents a collection of selected objects in the + /// control. + /// + [ + ListBindable(false), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewSelectedCellCollection : BaseCollection, IList + { + ArrayList items = new ArrayList(); + + /// + /// + int IList.Add(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Remove(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.RemoveAt(int index) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.IsFixedSize + { + get { return true; } + } + + /// + /// + bool IList.IsReadOnly + { + get { return true; } + } + + /// + /// + object IList.this[int index] + { + get { return this.items[index]; } + set { throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count + { + get { return this.items.Count; } + } + + /// + /// + bool ICollection.IsSynchronized + { + get { return false; } + } + + /// + /// + object ICollection.SyncRoot + { + get { return this; } + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + internal DataGridViewSelectedCellCollection() + { + } + + /// + protected override ArrayList List + { + get + { + return this.items; + } + } + + /// + public DataGridViewCell this[int index] + { + get + { + return (DataGridViewCell) this.items[index]; + } + } + + /// + /// + /// Adds a to this collection. + /// + internal int Add(DataGridViewCell dataGridViewCell) + { + Debug.Assert(!Contains(dataGridViewCell)); + return this.items.Add(dataGridViewCell); + } + + /* Not used for now + internal void AddRange(DataGridViewCell[] dataGridViewCells) + { + Debug.Assert(dataGridViewCells != null); + foreach(DataGridViewCell dataGridViewCell in dataGridViewCells) + { + Debug.Assert(!Contains(dataGridViewCell)); + this.items.Add(dataGridViewCell); + } + } + + internal void AddCellCollection(DataGridViewSelectedCellCollection dataGridViewCells) + { + Debug.Assert(dataGridViewCells != null); + foreach(DataGridViewCell dataGridViewCell in dataGridViewCells) + { + Debug.Assert(!Contains(dataGridViewCell)); + this.items.Add(dataGridViewCell); + } + } + */ + + /// + /// + /// Adds all the objects from the provided linked list to this collection. + /// + internal void AddCellLinkedList(DataGridViewCellLinkedList dataGridViewCells) + { + Debug.Assert(dataGridViewCells != null); + foreach (DataGridViewCell dataGridViewCell in dataGridViewCells) + { + Debug.Assert(!Contains(dataGridViewCell)); + this.items.Add(dataGridViewCell); + } + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public void Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + /// Checks to see if a DataGridViewCell is contained in this collection. + /// + public bool Contains(DataGridViewCell dataGridViewCell) + { + return this.items.IndexOf(dataGridViewCell) != -1; + } + + /// + public void CopyTo(DataGridViewCell[] array, int index) + { + this.items.CopyTo(array, index); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") + ] + public void Insert(int index, DataGridViewCell dataGridViewCell) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellsAccessibleObject.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellsAccessibleObject.cs new file mode 100644 index 000000000..9e365af2b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedCellsAccessibleObject.cs @@ -0,0 +1,129 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Security.Permissions; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + private class DataGridViewSelectedCellsAccessibleObject : AccessibleObject + { + DataGridView owner; + + public DataGridViewSelectedCellsAccessibleObject(DataGridView owner) + { + this.owner = owner; + } + + public override string Name + { + get + { + return SR.GetString(SR.DataGridView_AccSelectedCellsName); + } + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.owner.AccessibilityObject; + } + } + + public override AccessibleRole Role + { + get + { + return AccessibleRole.Grouping; + } + } + + public override AccessibleStates State + { + get + { + return AccessibleStates.Selected | AccessibleStates.Selectable; + } + } + + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.Name; + } + } + + public override AccessibleObject GetChild(int index) + { + if (index >= 0 && index < this.owner.GetCellCount(DataGridViewElementStates.Selected)) + { + return this.owner.SelectedCell(index).AccessibilityObject; + } + else + { + return null; + } + } + + public override int GetChildCount() + { + return this.owner.GetCellCount(DataGridViewElementStates.Selected); + } + + public override AccessibleObject GetSelected() + { + return this; + } + + public override AccessibleObject GetFocused() + { + if (this.owner.CurrentCell != null && this.owner.CurrentCell.Selected) + { + return this.owner.CurrentCell.AccessibilityObject; + } + else + { + return null; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + switch (navigationDirection) + { + case AccessibleNavigation.FirstChild: + if (this.owner.GetCellCount(DataGridViewElementStates.Selected) > 0) + { + return this.owner.SelectedCell(0).AccessibilityObject; + } + else + { + return null; + } + case AccessibleNavigation.LastChild: + if (this.owner.GetCellCount(DataGridViewElementStates.Selected) > 0) + { + return this.owner.SelectedCell(this.owner.GetCellCount(DataGridViewElementStates.Selected) - 1).AccessibilityObject; + } + else + { + return null; + } + default: + { + return null; + } + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSelectedColumnCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedColumnCollection.cs new file mode 100644 index 000000000..28cad41cd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedColumnCollection.cs @@ -0,0 +1,220 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Diagnostics; + + using System; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + [ + ListBindable(false), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewSelectedColumnCollection : BaseCollection, IList + { + ArrayList items = new ArrayList(); + + /// + /// + int IList.Add(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Remove(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.RemoveAt(int index) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.IsFixedSize + { + get { return true; } + } + + /// + /// + bool IList.IsReadOnly + { + get { return true; } + } + + /// + /// + object IList.this[int index] + { + get { return this.items[index]; } + set { throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count + { + get { return this.items.Count; } + } + + /// + /// + bool ICollection.IsSynchronized + { + get { return false; } + } + + /// + /// + object ICollection.SyncRoot + { + get { return this; } + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + internal DataGridViewSelectedColumnCollection() + { + } + + /// + protected override ArrayList List + { + get + { + return this.items; + } + } + + /// + public DataGridViewColumn this[int index] + { + get + { + return (DataGridViewColumn) this.items[index]; + } + } + + /// + /// + /// Adds a to this collection. + /// + internal int Add(DataGridViewColumn dataGridViewColumn) + { + return this.items.Add(dataGridViewColumn); + } + + /* Unused at this point + internal void AddRange(DataGridViewColumn[] dataGridViewColumns) + { + Debug.Assert(dataGridViewColumns != null); + foreach(DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + this.items.Add(dataGridViewColumn); + } + } + */ + + /* Unused at this point + internal void AddColumnCollection(DataGridViewColumnCollection dataGridViewColumns) + { + Debug.Assert(dataGridViewColumns != null); + foreach(DataGridViewColumn dataGridViewColumn in dataGridViewColumns) + { + this.items.Add(dataGridViewColumn); + } + } + */ + + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public void Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + /// Checks to see if a DataGridViewCell is contained in this collection. + /// + public bool Contains(DataGridViewColumn dataGridViewColumn) + { + return this.items.IndexOf(dataGridViewColumn) != -1; + } + + /// + public void CopyTo(DataGridViewColumn[] array, int index) + { + this.items.CopyTo(array, index); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") + ] + public void Insert(int index, DataGridViewColumn dataGridViewColumn) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSelectedRowCollection.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedRowCollection.cs new file mode 100644 index 000000000..d6e2308ae --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSelectedRowCollection.cs @@ -0,0 +1,221 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Diagnostics; + using System; + using System.Collections; + using System.Windows.Forms; + using System.ComponentModel; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Represents a collection of selected objects in the + /// control. + /// + [ + ListBindable(false), + SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface") // Consider adding an IList implementation + ] + public class DataGridViewSelectedRowCollection : BaseCollection, IList + { + ArrayList items = new ArrayList(); + + /// + /// + int IList.Add(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.Contains(object value) + { + return this.items.Contains(value); + } + + /// + /// + int IList.IndexOf(object value) + { + return this.items.IndexOf(value); + } + + /// + /// + void IList.Insert(int index, object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.Remove(object value) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + void IList.RemoveAt(int index) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + bool IList.IsFixedSize + { + get { return true; } + } + + /// + /// + bool IList.IsReadOnly + { + get { return true; } + } + + /// + /// + object IList.this[int index] + { + get { return this.items[index]; } + set { throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); } + } + + /// + /// + void ICollection.CopyTo(Array array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// + int ICollection.Count + { + get { return this.items.Count; } + } + + /// + /// + bool ICollection.IsSynchronized + { + get { return false; } + } + + /// + /// + object ICollection.SyncRoot + { + get { return this; } + } + + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + internal DataGridViewSelectedRowCollection() + { + } + + /// + protected override ArrayList List + { + get + { + return this.items; + } + } + + /// + public DataGridViewRow this[int index] + { + get + { + return (DataGridViewRow) this.items[index]; + } + } + + /// + /// + /// Adds a to this collection. + /// + internal int Add(DataGridViewRow dataGridViewRow) + { + return this.items.Add(dataGridViewRow); + } + + /* Unused at this point + internal void AddRange(DataGridViewRow[] dataGridViewRows) + { + Debug.Assert(dataGridViewRows != null); + foreach(DataGridViewRow dataGridViewRow in dataGridViewRows) + { + this.items.Add(dataGridViewRow); + } + } + + internal void AddRowCollection(DataGridViewRowCollection dataGridViewRows) + { + Debug.Assert(dataGridViewRows != null); + foreach(DataGridViewRow dataGridViewRow in dataGridViewRows) + { + this.items.Add(dataGridViewRow); + } + } + */ + + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public void Clear() + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + + /// + /// + /// Checks to see if a DataGridViewCell is contained in this collection. + /// + public bool Contains(DataGridViewRow dataGridViewRow) + { + return this.items.IndexOf(dataGridViewRow) != -1; + } + + /// + public void CopyTo(DataGridViewRow[] array, int index) + { + this.items.CopyTo(array, index); + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters") + ] + public void Insert(int index, DataGridViewRow dataGridViewRow) + { + throw new NotSupportedException(SR.GetString(SR.DataGridView_ReadOnlyCollection)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSelectionMode.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSelectionMode.cs new file mode 100644 index 000000000..089dfd7b2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSelectionMode.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + /// + public enum DataGridViewSelectionMode + { + /// + CellSelect = 0, + + /// + FullRowSelect = 1, + + /// + FullColumnSelect = 2, + + /// + RowHeaderSelect = 3, + + /// + ColumnHeaderSelect = 4 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewSortCompareEventArgs.cs b/WindowsForms/Managed/System/WinForms/DataGridViewSortCompareEventArgs.cs new file mode 100644 index 000000000..5aafbf4e8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewSortCompareEventArgs.cs @@ -0,0 +1,94 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + public class DataGridViewSortCompareEventArgs : HandledEventArgs + { + private DataGridViewColumn dataGridViewColumn; + private object cellValue1, cellValue2; + private int sortResult, rowIndex1, rowIndex2; + + /// + public DataGridViewSortCompareEventArgs(DataGridViewColumn dataGridViewColumn, + object cellValue1, + object cellValue2, + int rowIndex1, + int rowIndex2) + { + Debug.Assert(dataGridViewColumn != null); + Debug.Assert(dataGridViewColumn.Index >= 0); + this.dataGridViewColumn = dataGridViewColumn; + this.cellValue1 = cellValue1; + this.cellValue2 = cellValue2; + this.rowIndex1 = rowIndex1; + this.rowIndex2 = rowIndex2; + } + + /// + public object CellValue1 + { + get + { + return this.cellValue1; + } + } + + /// + public object CellValue2 + { + get + { + return this.cellValue2; + } + } + + /// + public DataGridViewColumn Column + { + get + { + return this.dataGridViewColumn; + } + } + + /// + public int RowIndex1 + { + get + { + return this.rowIndex1; + } + } + + /// + public int RowIndex2 + { + get + { + return this.rowIndex2; + } + } + + /// + public int SortResult + { + get + { + return this.sortResult; + } + set + { + this.sortResult = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxCell.cs new file mode 100644 index 000000000..ba3769aaf --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxCell.cs @@ -0,0 +1,856 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.ComponentModel; + using System.Windows.Forms.Internal; + using System.Globalization; + + /// + /// + /// Identifies a cell in the dataGridView. + /// + public class DataGridViewTextBoxCell : DataGridViewCell + { + private static readonly int PropTextBoxCellMaxInputLength = PropertyStore.CreateKey(); + private static readonly int PropTextBoxCellEditingTextBox = PropertyStore.CreateKey(); + + private const byte DATAGRIDVIEWTEXTBOXCELL_ignoreNextMouseClick = 0x01; + private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetLeft = 3; + private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetRight = 4; + private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft = 0; + private const byte DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight = 0; + private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetTop = 2; + private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetBottom = 1; + private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping = 1; + private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithoutWrapping = 2; + private const byte DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom = 1; + + private const int DATAGRIDVIEWTEXTBOXCELL_maxInputLength = 32767; + + private byte flagsState; // see DATAGRIDVIEWTEXTBOXCELL_ consts above + + private static Type defaultFormattedValueType = typeof(System.String); + private static Type defaultValueType = typeof(System.Object); + private static Type cellType = typeof(DataGridViewTextBoxCell); + + /// + public DataGridViewTextBoxCell() + { + } + + /// + /// Creates a new AccessibleObject for this DataGridViewTextBoxCell instance. + /// The AccessibleObject instance returned by this method supports ControlType UIA property. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.7.2 or opt-in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this DataGridViewTextBoxCell instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level1) + { + return new DataGridViewTextBoxCellAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + private DataGridViewTextBoxEditingControl EditingTextBox + { + get + { + return (DataGridViewTextBoxEditingControl) this.Properties.GetObject(PropTextBoxCellEditingTextBox); + } + set + { + if (value != null || this.Properties.ContainsObject(PropTextBoxCellEditingTextBox)) + { + this.Properties.SetObject(PropTextBoxCellEditingTextBox, value); + } + } + } + + /// + public override Type FormattedValueType + { + get + { + // we return string for the formatted type + return defaultFormattedValueType; + } + } + + /// + [DefaultValue(DATAGRIDVIEWTEXTBOXCELL_maxInputLength)] + public virtual int MaxInputLength + { + get + { + bool found; + int maxInputLength = this.Properties.GetInteger(PropTextBoxCellMaxInputLength, out found); + if (found) + { + return maxInputLength; + } + return DATAGRIDVIEWTEXTBOXCELL_maxInputLength; + } + set + { + // VSWhidbey 515823 + //if (this.DataGridView != null && this.RowIndex == -1) + //{ + // throw new InvalidOperationException(SR.GetString(SR.DataGridView_InvalidOperationOnSharedCell)); + //} + if (value < 0) + { + throw new ArgumentOutOfRangeException("MaxInputLength", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxInputLength", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + this.Properties.SetInteger(PropTextBoxCellMaxInputLength, value); + if (OwnsEditingTextBox(this.RowIndex)) + { + this.EditingTextBox.MaxLength = value; + } + } + } + + /// + public override Type ValueType + { + get + { + Type valueType = base.ValueType; + if (valueType != null) + { + return valueType; + } + return defaultValueType; + } + } + + // Called when the row that owns the editing control gets unshared. + internal override void CacheEditingControl() + { + this.EditingTextBox = this.DataGridView.EditingControl as DataGridViewTextBoxEditingControl; + } + + /// + public override object Clone() + { + DataGridViewTextBoxCell dataGridViewCell; + Type thisType = this.GetType(); + if(thisType == cellType) //performance improvement + { + dataGridViewCell = new DataGridViewTextBoxCell(); + } + else + { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + dataGridViewCell = (DataGridViewTextBoxCell)System.Activator.CreateInstance(thisType); + } + base.CloneInternal(dataGridViewCell); + dataGridViewCell.MaxInputLength = this.MaxInputLength; + return dataGridViewCell; + } + + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public override void DetachEditingControl() + { + DataGridView dgv = this.DataGridView; + if (dgv == null || dgv.EditingControl == null) + { + throw new InvalidOperationException(); + } + + TextBox textBox = dgv.EditingControl as TextBox; + if (textBox != null) + { + textBox.ClearUndo(); + } + + this.EditingTextBox = null; + + base.DetachEditingControl(); + } + + private Rectangle GetAdjustedEditingControlBounds(Rectangle editingControlBounds, DataGridViewCellStyle cellStyle) + { + Debug.Assert(cellStyle.WrapMode != DataGridViewTriState.NotSet); + Debug.Assert(this.DataGridView != null); + + TextBox txtEditingControl = this.DataGridView.EditingControl as TextBox; + int originalWidth = editingControlBounds.Width; + if (txtEditingControl != null) + { + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopLeft: + case DataGridViewContentAlignment.MiddleLeft: + case DataGridViewContentAlignment.BottomLeft: + // Add 3 pixels on the left of the editing control to match non-editing text position + if (this.DataGridView.RightToLeftInternal) + { + editingControlBounds.X += 1; + editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetLeft - 2); + } + else + { + editingControlBounds.X += DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetLeft; + editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetLeft - 1); + } + break; + + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.BottomCenter: + editingControlBounds.X += 1; + editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - 3); + break; + + case DataGridViewContentAlignment.TopRight: + case DataGridViewContentAlignment.MiddleRight: + case DataGridViewContentAlignment.BottomRight: + // Shorten the editing control by 5 pixels to match non-editing text position + if (this.DataGridView.RightToLeftInternal) + { + editingControlBounds.X += DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetRight - 1; + editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetRight); + } + else + { + editingControlBounds.X += 1; + editingControlBounds.Width = Math.Max(0, editingControlBounds.Width - DATAGRIDVIEWTEXTBOXCELL_horizontalTextOffsetRight - 1); + } + break; + } + + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopLeft: + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.TopRight: + editingControlBounds.Y += DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetTop; + editingControlBounds.Height = Math.Max(0, editingControlBounds.Height - DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetTop); + break; + + case DataGridViewContentAlignment.MiddleLeft: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.MiddleRight: + editingControlBounds.Height++; + break; + + case DataGridViewContentAlignment.BottomLeft: + case DataGridViewContentAlignment.BottomCenter: + case DataGridViewContentAlignment.BottomRight: + editingControlBounds.Height = Math.Max(0, editingControlBounds.Height - DATAGRIDVIEWTEXTBOXCELL_verticalTextOffsetBottom); + break; + } + + int preferredHeight; + if (cellStyle.WrapMode == DataGridViewTriState.False) + { + preferredHeight = txtEditingControl.PreferredSize.Height; + } + else + { + string editedFormattedValue = (string) ((IDataGridViewEditingControl) txtEditingControl).GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); + if (string.IsNullOrEmpty(editedFormattedValue)) + { + editedFormattedValue = " "; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) + { + preferredHeight = DataGridViewCell.MeasureTextHeight(g, editedFormattedValue, cellStyle.Font, originalWidth, flags); + } + } + if (preferredHeight < editingControlBounds.Height) + { + switch (cellStyle.Alignment) + { + case DataGridViewContentAlignment.TopLeft: + case DataGridViewContentAlignment.TopCenter: + case DataGridViewContentAlignment.TopRight: + // Single pixel move - leave it as is for now + break; + case DataGridViewContentAlignment.MiddleLeft: + case DataGridViewContentAlignment.MiddleCenter: + case DataGridViewContentAlignment.MiddleRight: + editingControlBounds.Y += (editingControlBounds.Height - preferredHeight) / 2; + break; + case DataGridViewContentAlignment.BottomLeft: + case DataGridViewContentAlignment.BottomCenter: + case DataGridViewContentAlignment.BottomRight: + editingControlBounds.Y += editingControlBounds.Height - preferredHeight; + break; + } + } + } + + return editingControlBounds; + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + object formattedValue = GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle textBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + null /*errorText*/, // textBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle textBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(textBoundsDebug.Equals(textBounds)); +#endif + + return textBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (this.DataGridView == null || + rowIndex < 0 || + this.OwningColumn == null || + !this.DataGridView.ShowCellErrors || + String.IsNullOrEmpty(GetErrorText(rowIndex))) + { + return Rectangle.Empty; + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errorBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // errorIconBounds is independent of formattedValue + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + object value = GetValue(rowIndex); + object formattedValue = GetFormattedValue(value, rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting); + + Rectangle errorBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(errorBoundsDebug.Equals(errorBounds)); +#endif + + return errorBounds; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Size preferredSize; + Rectangle borderWidthsRect = this.StdBorderWidths; + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + object formattedValue = GetFormattedValue(rowIndex, ref cellStyle, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize); + string formattedString = formattedValue as string; + if (string.IsNullOrEmpty(formattedString)) + { + formattedString = " "; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (cellStyle.WrapMode == DataGridViewTriState.True && formattedString.Length > 1) + { + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(DataGridViewCell.MeasureTextWidth(graphics, + formattedString, + cellStyle.Font, + Math.Max(1, constraintSize.Height - borderAndPaddingHeights - DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping - DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom), + flags), + 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextHeight(graphics, + formattedString, + cellStyle.Font, + Math.Max(1, constraintSize.Width - borderAndPaddingWidths - DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft - DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight), + flags)); + break; + } + default: + { + preferredSize = DataGridViewCell.MeasureTextPreferredSize(graphics, + formattedString, + cellStyle.Font, + 5.0F, + flags); + break; + } + } + } + else + { + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + preferredSize = new Size(DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags).Width, + 0); + break; + } + case DataGridViewFreeDimension.Height: + { + preferredSize = new Size(0, + DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags).Height); + break; + } + default: + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, formattedString, cellStyle.Font, flags); + break; + } + } + } + + if (freeDimension != DataGridViewFreeDimension.Height) + { + preferredSize.Width += DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft + DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight + borderAndPaddingWidths; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Width = Math.Max(preferredSize.Width, borderAndPaddingWidths + DATAGRIDVIEWCELL_iconMarginWidth * 2 + iconsWidth); + } + } + if (freeDimension != DataGridViewFreeDimension.Width) + { + int verticalTextMarginTop = cellStyle.WrapMode == DataGridViewTriState.True ? DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping : DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithoutWrapping; + preferredSize.Height += verticalTextMarginTop + DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom + borderAndPaddingHeights; + if (this.DataGridView.ShowCellErrors) + { + // Making sure that there is enough room for the potential error icon + preferredSize.Height = Math.Max(preferredSize.Height, borderAndPaddingHeights + DATAGRIDVIEWCELL_iconMarginHeight * 2 + iconsHeight); + } + } + return preferredSize; + } + + /// + public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) + { + Debug.Assert(this.DataGridView != null && + this.DataGridView.EditingPanel != null && + this.DataGridView.EditingControl != null); + Debug.Assert(!this.ReadOnly); + base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); + TextBox textBox = this.DataGridView.EditingControl as TextBox; + if (textBox != null) + { + textBox.BorderStyle = BorderStyle.None; + textBox.AcceptsReturn = textBox.Multiline = dataGridViewCellStyle.WrapMode == DataGridViewTriState.True; + textBox.MaxLength = this.MaxInputLength; + string initialFormattedValueStr = initialFormattedValue as string; + if (initialFormattedValueStr == null) + { + textBox.Text = string.Empty; + } + else + { + textBox.Text = initialFormattedValueStr; + } + + this.EditingTextBox = this.DataGridView.EditingControl as DataGridViewTextBoxEditingControl; + } + } + + /// + public override bool KeyEntersEditMode(KeyEventArgs e) + { + if (((char.IsLetterOrDigit((char)e.KeyCode) && !(e.KeyCode >= Keys.F1 && e.KeyCode <= Keys.F24)) || + (e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.Divide) || + (e.KeyCode >= Keys.OemSemicolon && e.KeyCode <= Keys.Oem102) || + (e.KeyCode == Keys.Space && !e.Shift)) && + !e.Alt && + !e.Control) + { + return true; + } + return base.KeyEntersEditMode(e); + } + + /// + protected override void OnEnter(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + if (throughMouseClick) + { + this.flagsState |= (byte)DATAGRIDVIEWTEXTBOXCELL_ignoreNextMouseClick; + } + } + + /// + protected override void OnLeave(int rowIndex, bool throughMouseClick) + { + if (this.DataGridView == null) + { + return; + } + this.flagsState = (byte)(this.flagsState & ~DATAGRIDVIEWTEXTBOXCELL_ignoreNextMouseClick); + } + + /// + protected override void OnMouseClick(DataGridViewCellMouseEventArgs e) + { + if (this.DataGridView == null) + { + return; + } + Debug.Assert(e.ColumnIndex == this.ColumnIndex); + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + if (ptCurrentCell.X == e.ColumnIndex && ptCurrentCell.Y == e.RowIndex && e.Button == MouseButtons.Left) + { + if ((this.flagsState & DATAGRIDVIEWTEXTBOXCELL_ignoreNextMouseClick) != 0x00) + { + this.flagsState = (byte)(this.flagsState & ~DATAGRIDVIEWTEXTBOXCELL_ignoreNextMouseClick); + } + else if (this.DataGridView.EditMode != DataGridViewEditMode.EditProgrammatically) + { + this.DataGridView.BeginEdit(true /*selectAll*/); + } + } + } + + private bool OwnsEditingTextBox(int rowIndex) + { + return rowIndex != -1 && this.EditingTextBox != null && rowIndex == ((IDataGridViewEditingControl)this.EditingTextBox).EditingControlRowIndex; + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + // If computeContentBounds == TRUE then resultBounds will be the contentBounds. + // If computeErrorIconBounds == TRUE then resultBounds will be the error icon bounds. + // Else resultBounds will be Rectangle.Empty; + Rectangle resultBounds = Rectangle.Empty; + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + Rectangle valBounds = cellBounds; + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + SolidBrush br; + + Point ptCurrentCell = this.DataGridView.CurrentCellAddress; + bool cellCurrent = ptCurrentCell.X == this.ColumnIndex && ptCurrentCell.Y == rowIndex; + bool cellEdited = cellCurrent && this.DataGridView.EditingControl != null; + bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0; + + if (DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected && !cellEdited) + { + br = this.DataGridView.GetCachedBrush(cellStyle.SelectionBackColor); + } + else + { + br = this.DataGridView.GetCachedBrush(cellStyle.BackColor); + } + + if (paint && DataGridViewCell.PaintBackground(paintParts) && br.Color.A == 255 && valBounds.Width > 0 && valBounds.Height > 0) + { + graphics.FillRectangle(br, valBounds); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + if (paint && cellCurrent && !cellEdited) + { + // Draw focus rectangle + if (DataGridViewCell.PaintFocus(paintParts) && + this.DataGridView.ShowFocusCues && + this.DataGridView.Focused && + valBounds.Width > 0 && + valBounds.Height > 0) + { + ControlPaint.DrawFocusRectangle(graphics, valBounds, Color.Empty, br.Color); + } + } + + Rectangle errorBounds = valBounds; + string formattedString = formattedValue as string; + + if (formattedString != null && ((paint && !cellEdited) || computeContentBounds)) + { + // Font independent margins + int verticalTextMarginTop = cellStyle.WrapMode == DataGridViewTriState.True ? DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithWrapping : DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginTopWithoutWrapping; + valBounds.Offset(DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft, verticalTextMarginTop); + valBounds.Width -= DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginLeft + DATAGRIDVIEWTEXTBOXCELL_horizontalTextMarginRight; + valBounds.Height -= verticalTextMarginTop + DATAGRIDVIEWTEXTBOXCELL_verticalTextMarginBottom; + if (valBounds.Width > 0 && valBounds.Height > 0) + { + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (paint) + { + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + TextRenderer.DrawText(graphics, + formattedString, + cellStyle.Font, + valBounds, + cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor, + flags); + } + } + else + { + resultBounds = DataGridViewUtilities.GetTextBounds(valBounds, formattedString, flags, cellStyle); + } + } + } + else if (computeErrorIconBounds) + { + if (!String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + } + else + { + Debug.Assert(cellEdited || formattedString == null); + Debug.Assert(paint || computeContentBounds); + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(graphics, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + + return resultBounds; + } + + /// + public override void PositionEditingControl(bool setLocation, + bool setSize, + Rectangle cellBounds, + Rectangle cellClip, + DataGridViewCellStyle cellStyle, + bool singleVerticalBorderAdded, + bool singleHorizontalBorderAdded, + bool isFirstDisplayedColumn, + bool isFirstDisplayedRow) + { + Rectangle editingControlBounds = PositionEditingPanel(cellBounds, + cellClip, + cellStyle, + singleVerticalBorderAdded, + singleHorizontalBorderAdded, + isFirstDisplayedColumn, + isFirstDisplayedRow); + editingControlBounds = GetAdjustedEditingControlBounds(editingControlBounds, cellStyle); + this.DataGridView.EditingControl.Location = new Point(editingControlBounds.X, editingControlBounds.Y); + this.DataGridView.EditingControl.Size = new Size(editingControlBounds.Width, editingControlBounds.Height); + } + + /// + public override string ToString() + { + return "DataGridViewTextBoxCell { ColumnIndex=" + ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + RowIndex.ToString(CultureInfo.CurrentCulture) + " }"; + } + + protected class DataGridViewTextBoxCellAccessibleObject : DataGridViewCellAccessibleObject + { + public DataGridViewTextBoxCellAccessibleObject(DataGridViewCell owner) : base(owner) + { + } + + internal override bool IsIAccessibleExSupported() + { + return true; + } + + internal override object GetPropertyValue(int propertyID) + { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) + { + return NativeMethods.UIA_EditControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxColumn.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxColumn.cs new file mode 100644 index 000000000..ede13b45e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxColumn.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Globalization; + + /// + [ToolboxBitmapAttribute(typeof(DataGridViewTextBoxColumn), "DataGridViewTextBoxColumn.bmp")] + public class DataGridViewTextBoxColumn : DataGridViewColumn + { + private const int DATAGRIDVIEWTEXTBOXCOLUMN_maxInputLength = 32767; + + /// + public DataGridViewTextBoxColumn() : base(new DataGridViewTextBoxCell()) + { + this.SortMode = DataGridViewColumnSortMode.Automatic; + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DataGridViewCell CellTemplate + { + get + { + return base.CellTemplate; + } + set + { + if (value != null && !(value is System.Windows.Forms.DataGridViewTextBoxCell)) + { + throw new InvalidCastException(SR.GetString(SR.DataGridViewTypeColumn_WrongCellTemplateType, "System.Windows.Forms.DataGridViewTextBoxCell")); + } + base.CellTemplate = value; + } + } + + /// + [ + DefaultValue(DATAGRIDVIEWTEXTBOXCOLUMN_maxInputLength), + SRCategory(SR.CatBehavior), + SRDescription(SR.DataGridView_TextBoxColumnMaxInputLengthDescr) + ] + public int MaxInputLength + { + get + { + if (this.TextBoxCellTemplate == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewColumn_CellTemplateRequired)); + } + return this.TextBoxCellTemplate.MaxInputLength; + } + set + { + if (this.MaxInputLength != value) + { + this.TextBoxCellTemplate.MaxInputLength = value; + if (this.DataGridView != null) + { + DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows; + int rowCount = dataGridViewRows.Count; + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex); + DataGridViewTextBoxCell dataGridViewCell = dataGridViewRow.Cells[this.Index] as DataGridViewTextBoxCell; + if (dataGridViewCell != null) + { + dataGridViewCell.MaxInputLength = value; + } + } + } + } + } + } + + /// + [ + DefaultValue(DataGridViewColumnSortMode.Automatic) + ] + public new DataGridViewColumnSortMode SortMode + { + get + { + return base.SortMode; + } + set + { + base.SortMode = value; + } + } + + private DataGridViewTextBoxCell TextBoxCellTemplate + { + get + { + return (DataGridViewTextBoxCell) this.CellTemplate; + } + } + + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(64); + sb.Append("DataGridViewTextBoxColumn { Name="); + sb.Append(this.Name); + sb.Append(", Index="); + sb.Append(this.Index.ToString(CultureInfo.CurrentCulture)); + sb.Append(" }"); + return sb.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs new file mode 100644 index 000000000..4e4bf693f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs @@ -0,0 +1,451 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Security.Permissions; + using System.Diagnostics; + using System.Runtime.InteropServices; + + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + public class DataGridViewTextBoxEditingControl : TextBox, IDataGridViewEditingControl + { + private static readonly DataGridViewContentAlignment anyTop = DataGridViewContentAlignment.TopLeft | DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.TopRight; + private static readonly DataGridViewContentAlignment anyRight = DataGridViewContentAlignment.TopRight | DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.BottomRight; + private static readonly DataGridViewContentAlignment anyCenter = DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.MiddleCenter | DataGridViewContentAlignment.BottomCenter; + + private DataGridView dataGridView; + private bool valueChanged; + private bool repositionOnValueChange; + private int rowIndex; + + /// + public DataGridViewTextBoxEditingControl() : base() + { + this.TabStop = false; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level3) + { + return new DataGridViewTextBoxEditingControlAccessibleObject(this); + } + else if (AccessibilityImprovements.Level2) + { + return new DataGridViewEditingControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + /// + public virtual DataGridView EditingControlDataGridView + { + get + { + return this.dataGridView; + } + set + { + this.dataGridView = value; + } + } + + /// + public virtual object EditingControlFormattedValue + { + get + { + return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); + } + set + { + this.Text = (string) value; + } + } + + /// + public virtual int EditingControlRowIndex + { + get + { + return this.rowIndex; + } + set + { + this.rowIndex = value; + } + } + + /// + public virtual bool EditingControlValueChanged + { + get + { + return this.valueChanged; + } + set + { + this.valueChanged = value; + } + } + + /// + public virtual Cursor EditingPanelCursor + { + get + { + return Cursors.Default; + } + } + + /// + public virtual bool RepositionEditingControlOnValueChange + { + get + { + return this.repositionOnValueChange; + } + } + + internal override bool SupportsUiaProviders + { + get + { + return AccessibilityImprovements.Level3; + } + } + + /// + public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) + { + this.Font = dataGridViewCellStyle.Font; + if (dataGridViewCellStyle.BackColor.A < 255) + { + // Our TextBox does not support transparent back colors + Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor); + this.BackColor = opaqueBackColor; + this.dataGridView.EditingPanel.BackColor = opaqueBackColor; + } + else + { + this.BackColor = dataGridViewCellStyle.BackColor; + } + this.ForeColor = dataGridViewCellStyle.ForeColor; + if (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True) + { + this.WordWrap = true; + } + this.TextAlign = TranslateAlignment(dataGridViewCellStyle.Alignment); + this.repositionOnValueChange = (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True && (dataGridViewCellStyle.Alignment & anyTop) == 0); + } + + /// + public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) + { + switch (keyData & Keys.KeyCode) + { + case Keys.Right: + // If the end of the selection is at the end of the string + // let the DataGridView treat the key message + if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length)) || + (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == 0))) + { + return true; + } + break; + + case Keys.Left: + // If the end of the selection is at the begining of the string + // or if the entire text is selected and we did not start editing + // send this character to the dataGridView, else process the key event + if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == 0)) || + (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length))) + { + return true; + } + break; + + case Keys.Down: + // If the end of the selection is on the last line of the text then + // send this character to the dataGridView, else process the key event + int end = this.SelectionStart + this.SelectionLength; + if (this.Text.IndexOf("\r\n", end) != -1) + { + return true; + } + break; + + case Keys.Up: + // If the end of the selection is on the first line of the text then + // send this character to the dataGridView, else process the key event + if (!(this.Text.IndexOf("\r\n") < 0 || this.SelectionStart + this.SelectionLength < this.Text.IndexOf("\r\n"))) + { + return true; + } + break; + + case Keys.Home: + case Keys.End: + if (this.SelectionLength != this.Text.Length) + { + return true; + } + break; + + case Keys.Prior: + case Keys.Next: + if (this.valueChanged) + { + return true; + } + break; + + case Keys.Delete: + if (this.SelectionLength > 0 || + this.SelectionStart < this.Text.Length) + { + return true; + } + break; + + case Keys.Enter: + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && this.Multiline && this.AcceptsReturn) + { + return true; + } + break; + } + return !dataGridViewWantsInputKey; + } + + /// + public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) + { + return this.Text; + } + + /// + public virtual void PrepareEditingControlForEdit(bool selectAll) + { + if (selectAll) + { + SelectAll(); + } + else + { + // Do not select all the text, but + // position the caret at the end of the text + this.SelectionStart = this.Text.Length; + } + } + + private void NotifyDataGridViewOfValueChange() + { + this.valueChanged = true; + this.dataGridView.NotifyCurrentCellDirty(true); + } + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + if (AccessibilityImprovements.Level3) + { + AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + + /// + protected override void OnMouseWheel(MouseEventArgs e) + { + // Forwarding to grid control. Can't prevent the TextBox from handling the mouse wheel as expected. + this.dataGridView.OnMouseWheelInternal(e); + } + + /// + protected override void OnTextChanged(EventArgs e) + { + base.OnTextChanged(e); + // Let the DataGridView know about the value change + NotifyDataGridViewOfValueChange(); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) + { + switch ((Keys)(int) m.WParam) + { + case Keys.Enter: + if (m.Msg == NativeMethods.WM_CHAR && + !(ModifierKeys == Keys.Shift && this.Multiline && this.AcceptsReturn)) + { + // Ignore the Enter key and don't add it to the textbox content. This happens when failing validation brings + // up a dialog box for example. + // Shift-Enter for multiline textboxes need to be accepted however. + return true; + } + break; + + case Keys.LineFeed: + if (m.Msg == NativeMethods.WM_CHAR && + ModifierKeys == Keys.Control && this.Multiline && this.AcceptsReturn) + { + // Ignore linefeed character when user hits Ctrl-Enter to commit the cell. + return true; + } + break; + + case Keys.A: + if (m.Msg == NativeMethods.WM_KEYDOWN && ModifierKeys == Keys.Control) + { + SelectAll(); + return true; + } + break; + + } + return base.ProcessKeyEventArgs(ref m); + } + + private static HorizontalAlignment TranslateAlignment(DataGridViewContentAlignment align) + { + if ((align & anyRight) != 0) + { + return HorizontalAlignment.Right; + } + else if ((align & anyCenter) != 0) + { + return HorizontalAlignment.Center; + } + else + { + return HorizontalAlignment.Left; + } + } + } + + /// + /// Defines the DataGridView TextBox EditingControl accessible object. + /// + /// + /// This accessible object is only available in AccessibilityImprovements of Level 3. + /// + internal class DataGridViewTextBoxEditingControlAccessibleObject : Control.ControlAccessibleObject + { + private DataGridViewTextBoxEditingControl ownerControl; + + /// + /// The parent is changed when the editing control is attached to another editing cell. + /// + private AccessibleObject _parentAccessibleObject = null; + + public DataGridViewTextBoxEditingControlAccessibleObject(DataGridViewTextBoxEditingControl ownerControl) : base(ownerControl) + { + this.ownerControl = ownerControl; + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return _parentAccessibleObject; + } + } + + public override string Name + { + get + { + string name = Owner.AccessibleName; + if (name != null) + { + return name; + } + else + { + return SR.GetString(SR.DataGridView_AccEditingControlAccName); + } + } + + set + { + base.Name = value; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var owner = Owner as IDataGridViewEditingControl; + if (owner != null && owner.EditingControlDataGridView.EditingControl == owner) + { + return _parentAccessibleObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.AccessibilityObject; + } + } + + internal override object GetPropertyValue(int propertyID) + { + switch (propertyID) + { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_EditControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + return true; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_ValuePatternId) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal override void SetParent(AccessibleObject parent) + { + _parentAccessibleObject = parent; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs.back b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs.back new file mode 100644 index 000000000..4e4bf693f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTextBoxEditingControl.cs.back @@ -0,0 +1,451 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Drawing; + using System.Security.Permissions; + using System.Diagnostics; + using System.Runtime.InteropServices; + + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + public class DataGridViewTextBoxEditingControl : TextBox, IDataGridViewEditingControl + { + private static readonly DataGridViewContentAlignment anyTop = DataGridViewContentAlignment.TopLeft | DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.TopRight; + private static readonly DataGridViewContentAlignment anyRight = DataGridViewContentAlignment.TopRight | DataGridViewContentAlignment.MiddleRight | DataGridViewContentAlignment.BottomRight; + private static readonly DataGridViewContentAlignment anyCenter = DataGridViewContentAlignment.TopCenter | DataGridViewContentAlignment.MiddleCenter | DataGridViewContentAlignment.BottomCenter; + + private DataGridView dataGridView; + private bool valueChanged; + private bool repositionOnValueChange; + private int rowIndex; + + /// + public DataGridViewTextBoxEditingControl() : base() + { + this.TabStop = false; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + if (AccessibilityImprovements.Level3) + { + return new DataGridViewTextBoxEditingControlAccessibleObject(this); + } + else if (AccessibilityImprovements.Level2) + { + return new DataGridViewEditingControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + /// + public virtual DataGridView EditingControlDataGridView + { + get + { + return this.dataGridView; + } + set + { + this.dataGridView = value; + } + } + + /// + public virtual object EditingControlFormattedValue + { + get + { + return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); + } + set + { + this.Text = (string) value; + } + } + + /// + public virtual int EditingControlRowIndex + { + get + { + return this.rowIndex; + } + set + { + this.rowIndex = value; + } + } + + /// + public virtual bool EditingControlValueChanged + { + get + { + return this.valueChanged; + } + set + { + this.valueChanged = value; + } + } + + /// + public virtual Cursor EditingPanelCursor + { + get + { + return Cursors.Default; + } + } + + /// + public virtual bool RepositionEditingControlOnValueChange + { + get + { + return this.repositionOnValueChange; + } + } + + internal override bool SupportsUiaProviders + { + get + { + return AccessibilityImprovements.Level3; + } + } + + /// + public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) + { + this.Font = dataGridViewCellStyle.Font; + if (dataGridViewCellStyle.BackColor.A < 255) + { + // Our TextBox does not support transparent back colors + Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor); + this.BackColor = opaqueBackColor; + this.dataGridView.EditingPanel.BackColor = opaqueBackColor; + } + else + { + this.BackColor = dataGridViewCellStyle.BackColor; + } + this.ForeColor = dataGridViewCellStyle.ForeColor; + if (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True) + { + this.WordWrap = true; + } + this.TextAlign = TranslateAlignment(dataGridViewCellStyle.Alignment); + this.repositionOnValueChange = (dataGridViewCellStyle.WrapMode == DataGridViewTriState.True && (dataGridViewCellStyle.Alignment & anyTop) == 0); + } + + /// + public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) + { + switch (keyData & Keys.KeyCode) + { + case Keys.Right: + // If the end of the selection is at the end of the string + // let the DataGridView treat the key message + if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length)) || + (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == 0))) + { + return true; + } + break; + + case Keys.Left: + // If the end of the selection is at the begining of the string + // or if the entire text is selected and we did not start editing + // send this character to the dataGridView, else process the key event + if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == 0)) || + (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length))) + { + return true; + } + break; + + case Keys.Down: + // If the end of the selection is on the last line of the text then + // send this character to the dataGridView, else process the key event + int end = this.SelectionStart + this.SelectionLength; + if (this.Text.IndexOf("\r\n", end) != -1) + { + return true; + } + break; + + case Keys.Up: + // If the end of the selection is on the first line of the text then + // send this character to the dataGridView, else process the key event + if (!(this.Text.IndexOf("\r\n") < 0 || this.SelectionStart + this.SelectionLength < this.Text.IndexOf("\r\n"))) + { + return true; + } + break; + + case Keys.Home: + case Keys.End: + if (this.SelectionLength != this.Text.Length) + { + return true; + } + break; + + case Keys.Prior: + case Keys.Next: + if (this.valueChanged) + { + return true; + } + break; + + case Keys.Delete: + if (this.SelectionLength > 0 || + this.SelectionStart < this.Text.Length) + { + return true; + } + break; + + case Keys.Enter: + if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && this.Multiline && this.AcceptsReturn) + { + return true; + } + break; + } + return !dataGridViewWantsInputKey; + } + + /// + public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) + { + return this.Text; + } + + /// + public virtual void PrepareEditingControlForEdit(bool selectAll) + { + if (selectAll) + { + SelectAll(); + } + else + { + // Do not select all the text, but + // position the caret at the end of the text + this.SelectionStart = this.Text.Length; + } + } + + private void NotifyDataGridViewOfValueChange() + { + this.valueChanged = true; + this.dataGridView.NotifyCurrentCellDirty(true); + } + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + if (AccessibilityImprovements.Level3) + { + AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + + /// + protected override void OnMouseWheel(MouseEventArgs e) + { + // Forwarding to grid control. Can't prevent the TextBox from handling the mouse wheel as expected. + this.dataGridView.OnMouseWheelInternal(e); + } + + /// + protected override void OnTextChanged(EventArgs e) + { + base.OnTextChanged(e); + // Let the DataGridView know about the value change + NotifyDataGridViewOfValueChange(); + } + + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + ] + protected override bool ProcessKeyEventArgs(ref Message m) + { + switch ((Keys)(int) m.WParam) + { + case Keys.Enter: + if (m.Msg == NativeMethods.WM_CHAR && + !(ModifierKeys == Keys.Shift && this.Multiline && this.AcceptsReturn)) + { + // Ignore the Enter key and don't add it to the textbox content. This happens when failing validation brings + // up a dialog box for example. + // Shift-Enter for multiline textboxes need to be accepted however. + return true; + } + break; + + case Keys.LineFeed: + if (m.Msg == NativeMethods.WM_CHAR && + ModifierKeys == Keys.Control && this.Multiline && this.AcceptsReturn) + { + // Ignore linefeed character when user hits Ctrl-Enter to commit the cell. + return true; + } + break; + + case Keys.A: + if (m.Msg == NativeMethods.WM_KEYDOWN && ModifierKeys == Keys.Control) + { + SelectAll(); + return true; + } + break; + + } + return base.ProcessKeyEventArgs(ref m); + } + + private static HorizontalAlignment TranslateAlignment(DataGridViewContentAlignment align) + { + if ((align & anyRight) != 0) + { + return HorizontalAlignment.Right; + } + else if ((align & anyCenter) != 0) + { + return HorizontalAlignment.Center; + } + else + { + return HorizontalAlignment.Left; + } + } + } + + /// + /// Defines the DataGridView TextBox EditingControl accessible object. + /// + /// + /// This accessible object is only available in AccessibilityImprovements of Level 3. + /// + internal class DataGridViewTextBoxEditingControlAccessibleObject : Control.ControlAccessibleObject + { + private DataGridViewTextBoxEditingControl ownerControl; + + /// + /// The parent is changed when the editing control is attached to another editing cell. + /// + private AccessibleObject _parentAccessibleObject = null; + + public DataGridViewTextBoxEditingControlAccessibleObject(DataGridViewTextBoxEditingControl ownerControl) : base(ownerControl) + { + this.ownerControl = ownerControl; + } + + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return _parentAccessibleObject; + } + } + + public override string Name + { + get + { + string name = Owner.AccessibleName; + if (name != null) + { + return name; + } + else + { + return SR.GetString(SR.DataGridView_AccEditingControlAccName); + } + } + + set + { + base.Name = value; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + var owner = Owner as IDataGridViewEditingControl; + if (owner != null && owner.EditingControlDataGridView.EditingControl == owner) + { + return _parentAccessibleObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return (Owner as IDataGridViewEditingControl)?.EditingControlDataGridView?.AccessibilityObject; + } + } + + internal override object GetPropertyValue(int propertyID) + { + switch (propertyID) + { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_EditControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + return true; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_ValuePatternId) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Sets the parent accessible object for the node which can be added or removed to/from hierachy nodes. + /// + /// The parent accessible object. + internal override void SetParent(AccessibleObject parent) + { + _parentAccessibleObject = parent; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewToolTip.cs b/WindowsForms/Managed/System/WinForms/DataGridViewToolTip.cs new file mode 100644 index 000000000..209f58865 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewToolTip.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Security; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + private class DataGridViewToolTip + { + DataGridView dataGridView = null; + ToolTip toolTip = null; + private bool toolTipActivated = false; + + public DataGridViewToolTip(DataGridView dataGridView) + { + this.dataGridView = dataGridView; + } + + public bool Activated + { + get + { + return this.toolTipActivated; + } + } + + public ToolTip ToolTip + { + get + { + return this.toolTip; + } + } + + public void Activate(bool activate) + { + if (this.dataGridView.DesignMode) + { + return; + } + + // Create the tool tip handle on demand. + if (activate && this.toolTip == null) + { + this.toolTip = new ToolTip(); + this.toolTip.ShowAlways = true; + this.toolTip.InitialDelay = 0; + this.toolTip.UseFading = false; + this.toolTip.UseAnimation = false; + this.toolTip.AutoPopDelay = 0; + } + + if (this.dataGridView.IsRestricted) + { + IntSecurity.AllWindows.Assert(); + } + try + { + if (activate) + { + this.toolTip.Active = true; + this.toolTip.Show(this.dataGridView.ToolTipPrivate, this.dataGridView); + } + else if (this.toolTip != null) + { + this.toolTip.Hide(this.dataGridView); + this.toolTip.Active = false; + } + } + finally + { + if (this.dataGridView.IsRestricted) + { + CodeAccessPermission.RevertAssert(); + } + } + + this.toolTipActivated = activate; + } + + public void Dispose() + { + if (this.toolTip != null) + { + this.toolTip.Dispose(); + this.toolTip = null; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTopLeftHeaderCell.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTopLeftHeaderCell.cs new file mode 100644 index 000000000..ab751e640 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTopLeftHeaderCell.cs @@ -0,0 +1,732 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.VisualStyles; + using System.ComponentModel; + using System.Windows.Forms.Internal; + using System.Security.Permissions; + + /// + /// + /// + /// + public class DataGridViewTopLeftHeaderCell : DataGridViewColumnHeaderCell + { + private static readonly VisualStyleElement HeaderElement = VisualStyleElement.Header.Item.Normal; + + private const byte DATAGRIDVIEWTOPLEFTHEADERCELL_horizontalTextMarginLeft = 1; + private const byte DATAGRIDVIEWTOPLEFTHEADERCELL_horizontalTextMarginRight = 2; + private const byte DATAGRIDVIEWTOPLEFTHEADERCELL_verticalTextMargin = 1; + + /// + public DataGridViewTopLeftHeaderCell() + { + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() + { + return new DataGridViewTopLeftHeaderCellAccessibleObject(this); + } + + /// + protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null) + { + return Rectangle.Empty; + } + + object value = GetValue(rowIndex); + + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + // the content bounds are computed on demand + // we mimic a lot of the painting code + + // get the borders + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle contentBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + null /*errorText*/, // contentBounds is independent of errorText + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + Rectangle contentBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + true /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(contentBoundsDebug.Equals(contentBounds)); +#endif + + return contentBounds; + } + + /// + protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null) + { + return Rectangle.Empty; + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + DataGridViewAdvancedBorderStyle dgvabsEffective; + DataGridViewElementStates cellState; + Rectangle cellBounds; + + ComputeBorderStyleCellStateAndCellBounds(rowIndex, out dgvabsEffective, out cellState, out cellBounds); + + Rectangle errorBounds = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + null /*formattedValue*/, // errorIconBounds is independent of formattedValue + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + +#if DEBUG + object value = GetValue(rowIndex); + + Rectangle errorBoundsDebug = PaintPrivate(graphics, + cellBounds, + cellBounds, + rowIndex, + cellState, + value, + GetErrorText(rowIndex), + cellStyle, + dgvabsEffective, + DataGridViewPaintParts.ContentForeground, + false /*computeContentBounds*/, + true /*computeErrorIconBounds*/, + false /*paint*/); + Debug.Assert(errorBoundsDebug.Equals(errorBounds)); +#endif + + return errorBounds; + } + + /// + protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) + { + if (rowIndex != -1) + { + throw new ArgumentOutOfRangeException("rowIndex"); + } + + if (this.DataGridView == null) + { + return new Size(-1, -1); + } + + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + Rectangle borderWidthsRect = BorderWidths(this.DataGridView.AdjustedTopLeftHeaderBorderStyle); + int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal; + int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical; + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + + // Intentionally not using GetFormattedValue because header cells don't typically perform formatting. + object val = GetValue(rowIndex); + if (!(val is string)) + { + val = null; + } + return DataGridViewUtilities.GetPreferredRowHeaderSize(graphics, + (string) val, + cellStyle, + borderAndPaddingWidths, + borderAndPaddingHeights, + this.DataGridView.ShowCellErrors, + false /*showGlyph*/, + constraintSize, + flags); + } + + /// + protected override void Paint(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object value, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts) + { + if (cellStyle == null) + { + throw new ArgumentNullException("cellStyle"); + } + + PaintPrivate(graphics, + clipBounds, + cellBounds, + rowIndex, + cellState, + formattedValue, + errorText, + cellStyle, + advancedBorderStyle, + paintParts, + false /*computeContentBounds*/, + false /*computeErrorIconBounds*/, + true /*paint*/); + } + + // PaintPrivate is used in three places that need to duplicate the paint code: + // 1. DataGridViewCell::Paint method + // 2. DataGridViewCell::GetContentBounds + // 3. DataGridViewCell::GetErrorIconBounds + // + // if computeContentBounds is true then PaintPrivate returns the contentBounds + // else if computeErrorIconBounds is true then PaintPrivate returns the errorIconBounds + // else it returns Rectangle.Empty; + private Rectangle PaintPrivate(Graphics graphics, + Rectangle clipBounds, + Rectangle cellBounds, + int rowIndex, + DataGridViewElementStates cellState, + object formattedValue, + string errorText, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle, + DataGridViewPaintParts paintParts, + bool computeContentBounds, + bool computeErrorIconBounds, + bool paint) + { + // Parameter checking. + // One bit and one bit only should be turned on + Debug.Assert(paint || computeContentBounds || computeErrorIconBounds); + Debug.Assert(!paint || !computeContentBounds || !computeErrorIconBounds); + Debug.Assert(!computeContentBounds || !computeErrorIconBounds || !paint); + Debug.Assert(!computeErrorIconBounds || !paint || !computeContentBounds); + Debug.Assert(cellStyle != null); + + // If computeContentBounds == TRUE then resultBounds will be the contentBounds. + // If computeErrorIconBounds == TRUE then resultBounds will be the error icon bounds. + // Else resultBounds will be Rectangle.Empty; + Rectangle resultBounds = Rectangle.Empty; + + Rectangle valBounds = cellBounds; + Rectangle borderWidths = BorderWidths(advancedBorderStyle); + + valBounds.Offset(borderWidths.X, borderWidths.Y); + valBounds.Width -= borderWidths.Right; + valBounds.Height -= borderWidths.Bottom; + + bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0; + + if (paint && DataGridViewCell.PaintBackground(paintParts)) + { + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + // XP Theming + int state = (int)HeaderItemState.Normal; + + if (this.ButtonState != ButtonState.Normal) + { + Debug.Assert(this.ButtonState == ButtonState.Pushed); + state = (int)HeaderItemState.Pressed; + } + else if (this.DataGridView.MouseEnteredCellAddress.Y == rowIndex && this.DataGridView.MouseEnteredCellAddress.X == this.ColumnIndex) + { + state = (int)HeaderItemState.Hot; + } + + valBounds.Inflate(16, 16); + DataGridViewTopLeftHeaderCellRenderer.DrawHeader(graphics, valBounds, state); + valBounds.Inflate(-16, -16); + } + else + { + SolidBrush br = this.DataGridView.GetCachedBrush((DataGridViewCell.PaintSelectionBackground(paintParts) && cellSelected) ? cellStyle.SelectionBackColor : cellStyle.BackColor); + if (br.Color.A == 255) + { + graphics.FillRectangle(br, valBounds); + } + } + } + + if (paint && DataGridViewCell.PaintBorder(paintParts)) + { + PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); + } + + if (cellStyle.Padding != Padding.Empty) + { + if (this.DataGridView.RightToLeftInternal) + { + valBounds.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); + } + else + { + valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top); + } + valBounds.Width -= cellStyle.Padding.Horizontal; + valBounds.Height -= cellStyle.Padding.Vertical; + } + + Rectangle errorBounds = valBounds; + string formattedValueStr = formattedValue as string; + + // Font independent margins + valBounds.Offset(DATAGRIDVIEWTOPLEFTHEADERCELL_horizontalTextMarginLeft, DATAGRIDVIEWTOPLEFTHEADERCELL_verticalTextMargin); + valBounds.Width -= DATAGRIDVIEWTOPLEFTHEADERCELL_horizontalTextMarginLeft + DATAGRIDVIEWTOPLEFTHEADERCELL_horizontalTextMarginRight; + valBounds.Height -= 2 * DATAGRIDVIEWTOPLEFTHEADERCELL_verticalTextMargin; + if (valBounds.Width > 0 && + valBounds.Height > 0 && + !String.IsNullOrEmpty(formattedValueStr) && + (paint || computeContentBounds)) + { + Color textColor; + if (this.DataGridView.ApplyVisualStylesToHeaderCells) + { + textColor = DataGridViewTopLeftHeaderCellRenderer.VisualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else + { + textColor = cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor; + } + TextFormatFlags flags = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode); + if (paint) + { + if (DataGridViewCell.PaintContentForeground(paintParts)) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + flags |= TextFormatFlags.EndEllipsis; + } + TextRenderer.DrawText(graphics, + formattedValueStr, + cellStyle.Font, + valBounds, + textColor, + flags); + } + } + else + { + Debug.Assert(computeContentBounds); + resultBounds = DataGridViewUtilities.GetTextBounds(valBounds, formattedValueStr, flags, cellStyle); + } + } + else if (computeErrorIconBounds && !String.IsNullOrEmpty(errorText)) + { + resultBounds = ComputeErrorIconBounds(errorBounds); + } + + if (this.DataGridView.ShowCellErrors && paint && DataGridViewCell.PaintErrorIcon(paintParts)) + { + PaintErrorIcon(graphics, cellStyle, rowIndex, cellBounds, errorBounds, errorText); + } + + return resultBounds; + } + + /// + protected override void PaintBorder(Graphics graphics, + Rectangle clipBounds, + Rectangle bounds, + DataGridViewCellStyle cellStyle, + DataGridViewAdvancedBorderStyle advancedBorderStyle) + { + if (this.DataGridView == null) + { + return; + } + + base.PaintBorder(graphics, clipBounds, bounds, cellStyle, advancedBorderStyle); + + if (!this.DataGridView.RightToLeftInternal && + this.DataGridView.ApplyVisualStylesToHeaderCells) + { + if (this.DataGridView.AdvancedColumnHeadersBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Inset) + { + Pen penControlDark = null, penControlLightLight = null; + GetContrastedPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + graphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + } + else if (this.DataGridView.AdvancedColumnHeadersBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Outset) + { + Pen penControlDark = null, penControlLightLight = null; + GetContrastedPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.X, bounds.Bottom - 1); + graphics.DrawLine(penControlLightLight, bounds.X, bounds.Y, bounds.Right - 1, bounds.Y); + } + else if (this.DataGridView.AdvancedColumnHeadersBorderStyle.All == DataGridViewAdvancedCellBorderStyle.InsetDouble) + { + Pen penControlDark = null, penControlLightLight = null; + GetContrastedPens(cellStyle.BackColor, ref penControlDark, ref penControlLightLight); + graphics.DrawLine(penControlDark, bounds.X + 1, bounds.Y + 1, bounds.X + 1, bounds.Bottom - 1); + graphics.DrawLine(penControlDark, bounds.X + 1, bounds.Y + 1, bounds.Right - 1, bounds.Y + 1); + } + } + } + + /// + /// + /// + /// + public override string ToString() + { + return "DataGridViewTopLeftHeaderCell"; + } + + private class DataGridViewTopLeftHeaderCellRenderer + { + private static VisualStyleRenderer visualStyleRenderer; + + private DataGridViewTopLeftHeaderCellRenderer() + { + } + + public static VisualStyleRenderer VisualStyleRenderer + { + get + { + if (visualStyleRenderer == null) + { + visualStyleRenderer = new VisualStyleRenderer(HeaderElement); + } + + return visualStyleRenderer; + } + } + + public static void DrawHeader(Graphics g, Rectangle bounds, int headerState) + { + VisualStyleRenderer.SetParameters(HeaderElement.ClassName, HeaderElement.Part, headerState); + VisualStyleRenderer.DrawBackground(g, bounds, Rectangle.Truncate(g.ClipBounds)); + } + } + + /// + protected class DataGridViewTopLeftHeaderCellAccessibleObject : DataGridViewColumnHeaderCellAccessibleObject + { + /// + public DataGridViewTopLeftHeaderCellAccessibleObject(DataGridViewTopLeftHeaderCell owner) : base (owner) + { + } + + /// + public override Rectangle Bounds + { + get + { + Rectangle cellRect = this.Owner.DataGridView.GetCellDisplayRectangle(-1, -1, false /*cutOverflow*/); + return this.Owner.DataGridView.RectangleToScreen(cellRect); + } + } + + /// + public override string DefaultAction + { + get + { + if (this.Owner.DataGridView.MultiSelect) + { + return SR.GetString(SR.DataGridView_AccTopLeftColumnHeaderCellDefaultAction); + } + else + { + return String.Empty; + } + } + } + + + /// + public override string Name + { + get + { + object value = this.Owner.Value; + if (value != null && !(value is String)) + { + // The user set the Value on the DataGridViewTopLeftHeaderCell and it did not set it to a string. + // Then the name of the DataGridViewTopLeftHeaderAccessibleObject is String.Empty; + // + return String.Empty; + } + string strValue = value as string; + if (String.IsNullOrEmpty(strValue)) + { + if (this.Owner.DataGridView != null) + { + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + return SR.GetString(SR.DataGridView_AccTopLeftColumnHeaderCellName); + } + else + { + return SR.GetString(SR.DataGridView_AccTopLeftColumnHeaderCellNameRTL); + } + } + else + { + return String.Empty; + } + } + else + { + return String.Empty; + } + } + } + + /// + public override AccessibleStates State + { + get + { + AccessibleStates resultState = AccessibleStates.Selectable; + + // get the Offscreen state from the base method. + AccessibleStates state = base.State; + if ((state & AccessibleStates.Offscreen) == AccessibleStates.Offscreen) + { + resultState |= AccessibleStates.Offscreen; + } + + // If all the cells are selected, then the top left header cell accessible object is considered to be selected as well. + if (this.Owner.DataGridView.AreAllCellsSelected(false /*includeInvisibleCells*/)) + { + resultState |= AccessibleStates.Selected; + } + + return resultState; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + // We changed DataGridViewTopLeftHeaderCellAccessibleObject::Name to return a string : vsw 393122 + // However, DataGridViewTopLeftHeaderCellAccessibleObject::Value should still return String.Empty. + return String.Empty; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() + { + this.Owner.DataGridView.SelectAll(); + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + Debug.Assert(this.Owner.DataGridView.RowHeadersVisible, "if the row headers are not visible how did you get the top left header cell acc object?"); + switch (navigationDirection) + { + case AccessibleNavigation.Previous: + return null; + case AccessibleNavigation.Left: + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + return null; + } + else + { + return NavigateForward(); + } + case AccessibleNavigation.Next: + return NavigateForward(); + case AccessibleNavigation.Right: + if (this.Owner.DataGridView.RightToLeft == RightToLeft.No) + { + return NavigateForward(); + } + else + { + return null; + } + default: + return null; + } + } + + private AccessibleObject NavigateForward() + { + if (this.Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + return null; + } + + // return the acc object for the first visible column + return this.Owner.DataGridView.AccessibilityObject.GetChild(0).GetChild(1); + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) + { + if (this.Owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewCellAccessibleObject_OwnerNotSet)); + } + + // AccessibleSelection.TakeFocus should focus the grid and then focus the first data grid view data cell + if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) + { + // Focus the grid + this.Owner.DataGridView.FocusInternal(); + if (this.Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) > 0 && + this.Owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible) > 0) + { + // This means that there are visible rows and columns. + // Focus the first data cell. + DataGridViewRow row = this.Owner.DataGridView.Rows[this.Owner.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible)]; + DataGridViewColumn col = this.Owner.DataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Visible); + + // DataGridView::set_CurrentCell clears the previous selection. + // So use SetCurrenCellAddressCore directly. + this.Owner.DataGridView.SetCurrentCellAddressCoreInternal(col.Index, row.Index, false /*setAnchorCellAddress*/, true /*validateCurrentCell*/, false /*thoughMouseClick*/); + } + } + + // AddSelection selects the entire grid. + if ((flags & AccessibleSelection.AddSelection) == AccessibleSelection.AddSelection) + { + if (this.Owner.DataGridView.MultiSelect) + { + this.Owner.DataGridView.SelectAll(); + } + } + + // RemoveSelection clears the selection on the entire grid. + // But only if AddSelection is not set. + if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection && + (flags & AccessibleSelection.AddSelection) == 0) + { + this.Owner.DataGridView.ClearSelection(); + } + } + + #region IRawElementProviderFragment Implementation + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + var dataGridView = this.Owner.DataGridView; + + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return dataGridView.AccessibilityObject.GetChild(0); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return null; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + if (dataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0) + { + return null; + } + + return NavigateForward(); + default: + return null; + } + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override object GetPropertyValue(int propertyId) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_HeaderControlTypeId; + case NativeMethods.UIA_IsEnabledPropertyId: + return Owner.DataGridView.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return this.Help ?? string.Empty; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + case NativeMethods.UIA_IsPasswordPropertyId: + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + } + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTopRowAccessibleObject.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTopRowAccessibleObject.cs new file mode 100644 index 000000000..ae731a600 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTopRowAccessibleObject.cs @@ -0,0 +1,310 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Security.Permissions; +using System.Drawing; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + /// + [ + System.Runtime.InteropServices.ComVisible(true) + ] + protected class DataGridViewTopRowAccessibleObject : AccessibleObject + { + private int[] runtimeId; + DataGridView owner; + + /// + public DataGridViewTopRowAccessibleObject() : base() + { + } + + /// + public DataGridViewTopRowAccessibleObject(DataGridView owner) : base() + { + this.owner = owner; + } + + /// + public override Rectangle Bounds + { + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerNotSet)); + } + if (this.owner.ColumnHeadersVisible) + { + Rectangle rect = Rectangle.Union(this.owner.layout.ColumnHeaders, this.owner.layout.TopLeftHeader); + return this.owner.RectangleToScreen(rect); + } + else + { + return Rectangle.Empty; + } + } + } + + /// + public override string Name + { + get + { + return SR.GetString(SR.DataGridView_AccTopRow); + } + } + + /// + public DataGridView Owner + { + get + { + return this.owner; + } + set + { + if (this.owner != null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerAlreadySet)); + } + this.owner = value; + } + } + + /// + public override AccessibleObject Parent + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerNotSet)); + } + return this.owner.AccessibilityObject; + } + } + + /// + public override AccessibleRole Role + { + get + { + return AccessibleRole.Row; + } + } + + internal override int[] RuntimeId + { + get + { + if (AccessibilityImprovements.Level3 && runtimeId == null) + { + runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a + runtimeId[1] = this.Parent.GetHashCode(); + runtimeId[2] = this.GetHashCode(); + } + + return runtimeId; + } + } + + /// + public override string Value + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + return this.Name; + } + } + + /// + public override AccessibleObject GetChild(int index) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerNotSet)); + } + + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (index == 0 && this.owner.RowHeadersVisible) + { + return this.owner.TopLeftHeaderCell.AccessibilityObject; + } + + if (this.owner.RowHeadersVisible) + { + // decrement the index because the first child is the top left header cell + index --; + } + + Debug.Assert(index >= 0); + + if (index < this.owner.Columns.GetColumnCount(DataGridViewElementStates.Visible)) + { + int actualColumnIndex = this.owner.Columns.ActualDisplayIndexToColumnIndex(index, DataGridViewElementStates.Visible); + return this.owner.Columns[actualColumnIndex].HeaderCell.AccessibilityObject; + } + else + { + return null; + } + } + + /// + public override int GetChildCount() + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerNotSet)); + } + int result = this.owner.Columns.GetColumnCount(DataGridViewElementStates.Visible); + if (this.owner.RowHeadersVisible) + { + // + 1 is the top left header cell accessibility object + result ++; + } + + return result; + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) + { + if (this.owner == null) + { + throw new InvalidOperationException(SR.GetString(SR.DataGridViewTopRowAccessibleObject_OwnerNotSet)); + } + switch (navigationDirection) + { + case AccessibleNavigation.Down: + case AccessibleNavigation.Next: + if (this.owner.AccessibilityObject.GetChildCount() > 1) + { + return this.owner.AccessibilityObject.GetChild(1); + } + else + { + return null; + } + case AccessibleNavigation.FirstChild: + return this.GetChild(0); + case AccessibleNavigation.LastChild: + return this.GetChild(this.GetChildCount() - 1); + default: + return null; + } + } + + #region IRawElementProviderFragment Implementation + + internal override Rectangle BoundingRectangle + { + get + { + return this.Bounds; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot + { + get + { + return this.owner.AccessibilityObject; + } + } + + [return: MarshalAs(UnmanagedType.IUnknown)] + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.Parent: + return Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + if (Parent.GetChildCount() > 1) + { + return Parent.GetChild(1); + } + break; + case UnsafeNativeMethods.NavigateDirection.FirstChild: + if (GetChildCount() > 0) + { + return GetChild(0); + } + break; + case UnsafeNativeMethods.NavigateDirection.LastChild: + if (GetChildCount() > 0) + { + return GetChild(GetChildCount() - 1); + } + break; + } + + return null; + } + + #endregion + + #region IRawElementProviderSimple Implementation + + internal override bool IsPatternSupported(int patternId) + { + if (AccessibilityImprovements.Level3 && patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId)) + { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyId) + { + if (AccessibilityImprovements.Level3) + { + switch (propertyId) + { + case NativeMethods.UIA_NamePropertyId: + return SR.GetString(SR.DataGridView_AccTopRow); + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return false; + case NativeMethods.UIA_IsEnabledPropertyId: + return owner.Enabled; + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_IsContentElementPropertyId: + return true; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_AccessKeyPropertyId: + case NativeMethods.UIA_HelpTextPropertyId: + return string.Empty; + case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId: + return true; + } + } + + return base.GetPropertyValue(propertyId); + } + + #endregion + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewTriState.cs b/WindowsForms/Managed/System/WinForms/DataGridViewTriState.cs new file mode 100644 index 000000000..c8fb80c96 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewTriState.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + public enum DataGridViewTriState + { + /// + NotSet = 0, + + /// + True, + + /// + False + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataGridViewUtilities.cs b/WindowsForms/Managed/System/WinForms/DataGridViewUtilities.cs new file mode 100644 index 000000000..decb1d831 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataGridViewUtilities.cs @@ -0,0 +1,469 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Imaging; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// + /// + internal class DataGridViewUtilities + { + private const byte DATAGRIDVIEWROWHEADERCELL_iconMarginWidth = 3; // 3 pixels of margin on the left and right of icons + private const byte DATAGRIDVIEWROWHEADERCELL_iconMarginHeight = 2; // 2 pixels of margin on the top and bottom of icons + private const byte DATAGRIDVIEWROWHEADERCELL_contentMarginWidth = 3; // 3 pixels of margin on the left and right of content + private const byte DATAGRIDVIEWROWHEADERCELL_contentMarginHeight = 3; // 3 pixels of margin on the top and bottom of content + private const byte DATAGRIDVIEWROWHEADERCELL_iconsWidth = 12; // all icons are 12 pixels wide - make sure that is stays that way + private const byte DATAGRIDVIEWROWHEADERCELL_iconsHeight = 11; // all icons are 11 pixels tall - make sure that is stays that way + + private const byte DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft = 1; + private const byte DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight = 2; + private const byte DATAGRIDVIEWROWHEADERCELL_verticalTextMargin = 1; + + /// + internal static System.Drawing.ContentAlignment ComputeDrawingContentAlignmentForCellStyleAlignment(DataGridViewContentAlignment alignment) + { + // Why isn't the DataGridView using System.Drawing.ContentAlignment? + switch (alignment) + { + case DataGridViewContentAlignment.TopLeft: + return System.Drawing.ContentAlignment.TopLeft; + case DataGridViewContentAlignment.TopCenter: + return System.Drawing.ContentAlignment.TopCenter; + case DataGridViewContentAlignment.TopRight: + return System.Drawing.ContentAlignment.TopRight; + case DataGridViewContentAlignment.MiddleLeft: + return System.Drawing.ContentAlignment.MiddleLeft; + case DataGridViewContentAlignment.MiddleCenter: + return System.Drawing.ContentAlignment.MiddleCenter; + case DataGridViewContentAlignment.MiddleRight: + return System.Drawing.ContentAlignment.MiddleRight; + case DataGridViewContentAlignment.BottomLeft: + return System.Drawing.ContentAlignment.BottomLeft; + case DataGridViewContentAlignment.BottomCenter: + return System.Drawing.ContentAlignment.BottomCenter; + case DataGridViewContentAlignment.BottomRight: + return System.Drawing.ContentAlignment.BottomRight; + default: + return System.Drawing.ContentAlignment.MiddleCenter; + } + } + + /// + internal static TextFormatFlags ComputeTextFormatFlagsForCellStyleAlignment(bool rightToLeft, + DataGridViewContentAlignment alignment, + DataGridViewTriState wrapMode) + { + TextFormatFlags tff; + switch (alignment) + { + case DataGridViewContentAlignment.TopLeft: + tff = TextFormatFlags.Top; + if (rightToLeft) + { + tff |= TextFormatFlags.Right; + } + else + { + tff |= TextFormatFlags.Left; + } + break; + case DataGridViewContentAlignment.TopCenter: + tff = TextFormatFlags.Top | TextFormatFlags.HorizontalCenter; + break; + case DataGridViewContentAlignment.TopRight: + tff = TextFormatFlags.Top; + if (rightToLeft) + { + tff |= TextFormatFlags.Left; + } + else + { + tff |= TextFormatFlags.Right; + } + break; + case DataGridViewContentAlignment.MiddleLeft: + tff = TextFormatFlags.VerticalCenter; + if (rightToLeft) + { + tff |= TextFormatFlags.Right; + } + else + { + tff |= TextFormatFlags.Left; + } + break; + case DataGridViewContentAlignment.MiddleCenter: + tff = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter; + break; + case DataGridViewContentAlignment.MiddleRight: + tff = TextFormatFlags.VerticalCenter; + if (rightToLeft) + { + tff |= TextFormatFlags.Left; + } + else + { + tff |= TextFormatFlags.Right; + } + break; + case DataGridViewContentAlignment.BottomLeft: + tff = TextFormatFlags.Bottom; + if (rightToLeft) + { + tff |= TextFormatFlags.Right; + } + else + { + tff |= TextFormatFlags.Left; + } + break; + case DataGridViewContentAlignment.BottomCenter: + tff = TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter; + break; + case DataGridViewContentAlignment.BottomRight: + tff = TextFormatFlags.Bottom; + if (rightToLeft) + { + tff |= TextFormatFlags.Left; + } + else + { + tff |= TextFormatFlags.Right; + } + break; + default: + tff = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter; + break; + } + if (wrapMode == DataGridViewTriState.False) + { + tff |= TextFormatFlags.SingleLine; + } + else + { + //tff |= TextFormatFlags.NoFullWidthCharacterBreak; VSWhidbey 518422 + tff |= TextFormatFlags.WordBreak; + } + tff |= TextFormatFlags.NoPrefix; + tff |= TextFormatFlags.PreserveGraphicsClipping; + if (rightToLeft) + { + tff |= TextFormatFlags.RightToLeft; + } + return tff; + } + + + /// + internal static Size GetPreferredRowHeaderSize(Graphics graphics, + string val, + DataGridViewCellStyle cellStyle, + int borderAndPaddingWidths, + int borderAndPaddingHeights, + bool showRowErrors, + bool showGlyph, + Size constraintSize, + TextFormatFlags flags) + { + Size preferredSize; + DataGridViewFreeDimension freeDimension = DataGridViewCell.GetFreeDimensionFromConstraint(constraintSize); + + switch (freeDimension) + { + case DataGridViewFreeDimension.Width: + { + int preferredWidth = 0, allowedHeight = constraintSize.Height - borderAndPaddingHeights; + if (!String.IsNullOrEmpty(val)) + { + int maxHeight = allowedHeight - 2 * DATAGRIDVIEWROWHEADERCELL_verticalTextMargin; + if (maxHeight > 0) + { + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + preferredWidth = DataGridViewCell.MeasureTextWidth(graphics, val, cellStyle.Font, maxHeight, flags); + } + else + { + preferredWidth = DataGridViewCell.MeasureTextSize(graphics, val, cellStyle.Font, flags).Width; + } + preferredWidth += 2 * DATAGRIDVIEWROWHEADERCELL_contentMarginWidth + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight; + } + } + if (allowedHeight >= DATAGRIDVIEWROWHEADERCELL_iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight) + { + if (showGlyph) + { + preferredWidth += DATAGRIDVIEWROWHEADERCELL_iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + if (showRowErrors) + { + preferredWidth += DATAGRIDVIEWROWHEADERCELL_iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + } + preferredWidth = Math.Max(preferredWidth, 1); + preferredWidth += borderAndPaddingWidths; + return new Size(preferredWidth, 0); + } + case DataGridViewFreeDimension.Height: + { + int minHeightIcon = 1, minHeightContent = 1; + int allowedWidth = constraintSize.Width - borderAndPaddingWidths; + if (!String.IsNullOrEmpty(val)) + { + if (showGlyph && allowedWidth >= 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + DATAGRIDVIEWROWHEADERCELL_iconsWidth) + { + // There is enough room for the status icon + minHeightIcon = DATAGRIDVIEWROWHEADERCELL_iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight; + // Status icon takes priority + allowedWidth -= 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + DATAGRIDVIEWROWHEADERCELL_iconsWidth; + } + if (showRowErrors && allowedWidth >= 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + DATAGRIDVIEWROWHEADERCELL_iconsWidth) + { + // There is enough room for the error icon + minHeightIcon = DATAGRIDVIEWROWHEADERCELL_iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight; + // There is enough room for both the status and error icons + allowedWidth -= 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + DATAGRIDVIEWROWHEADERCELL_iconsWidth; + } + if (allowedWidth > 2 * DATAGRIDVIEWROWHEADERCELL_contentMarginWidth + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight) + { + // There is enough room for text + allowedWidth -= 2 * DATAGRIDVIEWROWHEADERCELL_contentMarginWidth + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight; + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + minHeightContent = DataGridViewCell.MeasureTextHeight(graphics, val, cellStyle.Font, allowedWidth, flags); + } + else + { + minHeightContent = DataGridViewCell.MeasureTextSize(graphics, val, cellStyle.Font, flags).Height; + } + minHeightContent += 2 * DATAGRIDVIEWROWHEADERCELL_verticalTextMargin; + } + } + else + { + if ((showGlyph || showRowErrors) && allowedWidth >= 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth + DATAGRIDVIEWROWHEADERCELL_iconsWidth) + { + minHeightIcon = DATAGRIDVIEWROWHEADERCELL_iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight; + } + } + return new Size(0, Math.Max(minHeightIcon, minHeightContent) + borderAndPaddingHeights); + } + default: + { + if (!String.IsNullOrEmpty(val)) + { + if (cellStyle.WrapMode == DataGridViewTriState.True) + { + preferredSize = DataGridViewCell.MeasureTextPreferredSize(graphics, val, cellStyle.Font, 5.0F, flags); + } + else + { + preferredSize = DataGridViewCell.MeasureTextSize(graphics, val, cellStyle.Font, flags); + } + preferredSize.Width += 2 * DATAGRIDVIEWROWHEADERCELL_contentMarginWidth + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginLeft + + DATAGRIDVIEWROWHEADERCELL_horizontalTextMarginRight; + preferredSize.Height += 2*DATAGRIDVIEWROWHEADERCELL_verticalTextMargin; + } + else + { + preferredSize = new Size(0, 1); + } + if (showGlyph) + { + preferredSize.Width += DATAGRIDVIEWROWHEADERCELL_iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + if (showRowErrors) + { + preferredSize.Width += DATAGRIDVIEWROWHEADERCELL_iconsWidth + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginWidth; + } + if (showGlyph || showRowErrors) + { + preferredSize.Height = Math.Max(preferredSize.Height, + DATAGRIDVIEWROWHEADERCELL_iconsHeight + 2 * DATAGRIDVIEWROWHEADERCELL_iconMarginHeight); + } + preferredSize.Width += borderAndPaddingWidths; + preferredSize.Height += borderAndPaddingHeights; + return preferredSize; + } + } + } + + internal static Rectangle GetTextBounds(Rectangle cellBounds, + string text, + TextFormatFlags flags, + DataGridViewCellStyle cellStyle) + { + return GetTextBounds(cellBounds, text, flags, cellStyle, cellStyle.Font); + } + + internal static Rectangle GetTextBounds(Rectangle cellBounds, + string text, + TextFormatFlags flags, + DataGridViewCellStyle cellStyle, + Font font) + { + if ((flags & TextFormatFlags.SingleLine) != 0) + { + Size sizeRequired = TextRenderer.MeasureText(text, font, new Size(System.Int32.MaxValue, System.Int32.MaxValue), flags); + if (sizeRequired.Width > cellBounds.Width) + { + flags |= TextFormatFlags.EndEllipsis; + } + } + + Size sizeCell = new Size(cellBounds.Width, cellBounds.Height); + Size sizeConstraint = TextRenderer.MeasureText(text, font, sizeCell, flags); + if (sizeConstraint.Width > sizeCell.Width) + { + sizeConstraint.Width = sizeCell.Width; + } + if (sizeConstraint.Height > sizeCell.Height) + { + sizeConstraint.Height = sizeCell.Height; + } + if (sizeConstraint == sizeCell) + { + return cellBounds; + } + return new Rectangle(GetTextLocation(cellBounds, sizeConstraint, flags, cellStyle), sizeConstraint); + } + + internal static Point GetTextLocation(Rectangle cellBounds, + Size sizeText, + TextFormatFlags flags, + DataGridViewCellStyle cellStyle) + { + Point ptTextLocation = new Point(0, 0); + + // now use the alignment on the cellStyle to determine the final text location + DataGridViewContentAlignment alignment = cellStyle.Alignment; + if ((flags & TextFormatFlags.RightToLeft) != 0) + { + switch (alignment) + { + case DataGridViewContentAlignment.TopLeft: + alignment = DataGridViewContentAlignment.TopRight; + break; + + case DataGridViewContentAlignment.TopRight: + alignment = DataGridViewContentAlignment.TopLeft; + break; + + case DataGridViewContentAlignment.MiddleLeft: + alignment = DataGridViewContentAlignment.MiddleRight; + break; + + case DataGridViewContentAlignment.MiddleRight: + alignment = DataGridViewContentAlignment.MiddleLeft; + break; + + case DataGridViewContentAlignment.BottomLeft: + alignment = DataGridViewContentAlignment.BottomRight; + break; + + case DataGridViewContentAlignment.BottomRight: + alignment = DataGridViewContentAlignment.BottomLeft; + break; + } + } + + switch (alignment) + { + case DataGridViewContentAlignment.TopLeft: + ptTextLocation.X = cellBounds.X; + ptTextLocation.Y = cellBounds.Y; + break; + + case DataGridViewContentAlignment.TopCenter: + ptTextLocation.X = cellBounds.X + (cellBounds.Width - sizeText.Width) / 2; + ptTextLocation.Y = cellBounds.Y; + break; + + case DataGridViewContentAlignment.TopRight: + ptTextLocation.X = cellBounds.Right - sizeText.Width; + ptTextLocation.Y = cellBounds.Y; + break; + + case DataGridViewContentAlignment.MiddleLeft: + ptTextLocation.X = cellBounds.X; + ptTextLocation.Y = cellBounds.Y + (cellBounds.Height - sizeText.Height) / 2; + break; + + case DataGridViewContentAlignment.MiddleCenter: + ptTextLocation.X = cellBounds.X + (cellBounds.Width - sizeText.Width) / 2; + ptTextLocation.Y = cellBounds.Y + (cellBounds.Height - sizeText.Height) / 2; + break; + + case DataGridViewContentAlignment.MiddleRight: + ptTextLocation.X = cellBounds.Right - sizeText.Width; + ptTextLocation.Y = cellBounds.Y + (cellBounds.Height - sizeText.Height) / 2; + break; + + case DataGridViewContentAlignment.BottomLeft: + ptTextLocation.X = cellBounds.X; + ptTextLocation.Y = cellBounds.Bottom - sizeText.Height; + break; + + case DataGridViewContentAlignment.BottomCenter: + ptTextLocation.X = cellBounds.X + (cellBounds.Width - sizeText.Width) / 2; + ptTextLocation.Y = cellBounds.Bottom - sizeText.Height; + break; + + case DataGridViewContentAlignment.BottomRight: + ptTextLocation.X = cellBounds.Right - sizeText.Width; + ptTextLocation.Y = cellBounds.Bottom - sizeText.Height; + break; + + default: + Debug.Assert(cellStyle.Alignment == DataGridViewContentAlignment.NotSet, "this is the only alignment left"); + break; + } + return ptTextLocation; + } + + internal static bool ValidTextFormatFlags(TextFormatFlags flags) + { + return (flags & ~(TextFormatFlags.Bottom | + TextFormatFlags.Default | + TextFormatFlags.EndEllipsis | + TextFormatFlags.ExpandTabs | + TextFormatFlags.ExternalLeading | + TextFormatFlags.HidePrefix | + TextFormatFlags.HorizontalCenter | + TextFormatFlags.Internal | + TextFormatFlags.Left | + TextFormatFlags.ModifyString | + TextFormatFlags.NoClipping | + TextFormatFlags.NoFullWidthCharacterBreak | + TextFormatFlags.NoPrefix | + TextFormatFlags.PathEllipsis | + TextFormatFlags.PrefixOnly | + TextFormatFlags.PreserveGraphicsClipping | + TextFormatFlags.PreserveGraphicsTranslateTransform | + TextFormatFlags.Right | + TextFormatFlags.RightToLeft | + TextFormatFlags.SingleLine | + TextFormatFlags.TextBoxControl | + TextFormatFlags.Top | + TextFormatFlags.VerticalCenter | + TextFormatFlags.WordBreak | + TextFormatFlags.WordEllipsis)) == 0; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataObject.cs b/WindowsForms/Managed/System/WinForms/DataObject.cs new file mode 100644 index 000000000..d84edde12 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataObject.cs @@ -0,0 +1,2275 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters.Binary; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Serialization; + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Collections.Specialized; + using System.Diagnostics; + using System; + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.IO; + using System.Drawing; + using System.Windows.Forms; + using System.Security; + using System.Security.Permissions; + using System.Reflection; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using System.Globalization; + + /// + /// + /// Implements a basic data transfer mechanism. + /// + [ + ClassInterface(ClassInterfaceType.None) + ] + public class DataObject : IDataObject, IComDataObject { + + private static readonly string CF_DEPRECATED_FILENAME = "FileName"; + private static readonly string CF_DEPRECATED_FILENAMEW = "FileNameW"; + + private const int DV_E_FORMATETC = unchecked((int)0x80040064); + private const int DV_E_LINDEX = unchecked((int)0x80040068); + private const int DV_E_TYMED = unchecked((int)0x80040069); + private const int DV_E_DVASPECT = unchecked((int)0x8004006B); + private const int OLE_E_NOTRUNNING = unchecked((int)0x80040005); + private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003); + private const int DATA_S_SAMEFORMATETC = 0x00040130; + + private static readonly TYMED[] ALLOWED_TYMEDS = + new TYMED [] { + TYMED.TYMED_HGLOBAL, + TYMED.TYMED_ISTREAM, + TYMED.TYMED_ENHMF, + TYMED.TYMED_MFPICT, + TYMED.TYMED_GDI}; + + private IDataObject innerData = null; + internal bool RestrictedFormats {get;set;} + + // We use this to identify that a stream is actually a serialized object. On read, + // we don't know if the contents of a stream were saved "raw" or if the stream is really + // pointing to a serialized object. If we saved an object, we prefix it with this + // guid. + // + private static readonly byte[] serializedObjectID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray(); + + /// + /// + /// Initializes a new instance of the class, with the specified . + /// + internal DataObject(IDataObject data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Constructed DataObject based on IDataObject"); + innerData = data; + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + } + + /// + /// + /// Initializes a new instance of the class, with the specified . + /// + internal DataObject(IComDataObject data) { + if (data is DataObject) { + innerData = data as IDataObject; + } + else { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Constructed DataObject based on IComDataObject"); + innerData = new OleConverter(data); + } + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + } + + /// + /// + /// + /// Initializes a new instance of the + /// class, which can store arbitrary data. + /// + /// + public DataObject() { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Constructed DataObject standalone"); + innerData = new DataStore(); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + } + + /// + /// + /// Initializes a new instance of the class, containing the specified data. + /// + public DataObject(object data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Constructed DataObject base on Object: " + data.ToString()); + if (data is IDataObject && !Marshal.IsComObject(data)) { + innerData = (IDataObject)data; + } + else if (data is IComDataObject) { + innerData = new OleConverter((IComDataObject)data); + } + else { + innerData = new DataStore(); + SetData(data); + } + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + } + + /// + /// + /// Initializes a new instance of the class, containing the specified data and its + /// associated format. + /// + public DataObject(string format, object data) : this() { + SetData(format, data); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + } + + private IntPtr GetCompatibleBitmap(Bitmap bm) { + // GDI+ returns a DIBSECTION based HBITMAP. The clipboard deals well + // only with bitmaps created using CreateCompatibleBitmap(). So, we + // convert the DIBSECTION into a compatible bitmap. + // + IntPtr hBitmap = bm.GetHbitmap(); + + // Get the screen DC. + // + IntPtr hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + + // Create a compatible DC to render the source bitmap. + // + IntPtr dcSrc = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(null, hDC)); + IntPtr srcOld = SafeNativeMethods.SelectObject(new HandleRef(null, dcSrc), new HandleRef(bm, hBitmap)); + + // Create a compatible DC and a new compatible bitmap. + // + IntPtr dcDest = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(null, hDC)); + IntPtr hBitmapNew = SafeNativeMethods.CreateCompatibleBitmap(new HandleRef(null, hDC), bm.Size.Width, bm.Size.Height); + + // Select the new bitmap into a compatible DC and render the blt the original bitmap. + // + IntPtr destOld = SafeNativeMethods.SelectObject(new HandleRef(null, dcDest), new HandleRef(null, hBitmapNew)); + SafeNativeMethods.BitBlt(new HandleRef(null, dcDest), 0, 0, bm.Size.Width, bm.Size.Height, new HandleRef(null, dcSrc), 0, 0, 0x00CC0020); + + // Clear the source and destination compatible DCs. + // + SafeNativeMethods.SelectObject(new HandleRef(null, dcSrc), new HandleRef(null, srcOld)); + SafeNativeMethods.SelectObject(new HandleRef(null, dcDest), new HandleRef(null, destOld)); + + UnsafeNativeMethods.DeleteCompatibleDC(new HandleRef(null, dcSrc)); + UnsafeNativeMethods.DeleteCompatibleDC(new HandleRef(null, dcDest)); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); + + SafeNativeMethods.DeleteObject(new HandleRef(bm, hBitmap)); + + return hBitmapNew; + } + + /// + /// + /// Retrieves the data associated with the specified data + /// format, using an automated conversion parameter to determine whether to convert + /// the data to the format. + /// + public virtual object GetData(string format, bool autoConvert) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Request data: " + format + ", " + autoConvert.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + return innerData.GetData(format, autoConvert); + } + + /// + /// + /// Retrieves the data associated with the specified data + /// format. + /// + public virtual object GetData(string format) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Request data: " + format); + return GetData(format, true); + } + + /// + /// + /// Retrieves the data associated with the specified class + /// type format. + /// + public virtual object GetData(Type format) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Request data: " + format.FullName); + Debug.Assert(format != null, "Must specify a format type"); + if (format == null) { + return null; + } + return GetData(format.FullName); + } + + /// + /// + /// Determines whether data stored in this instance is + /// associated with, or can be converted to, the specified + /// format. + /// + public virtual bool GetDataPresent(Type format) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Check data: " + format.FullName); + Debug.Assert(format != null, "Must specify a format type"); + if (format == null) { + return false; + } + bool b = GetDataPresent(format.FullName); + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, " ret: " + b.ToString()); + return b; + } + + /// + /// + /// Determines whether data stored in this instance is + /// associated with the specified format, using an automatic conversion + /// parameter to determine whether to convert the data to the format. + /// + public virtual bool GetDataPresent(string format, bool autoConvert) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Check data: " + format + ", " + autoConvert.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + bool b = innerData.GetDataPresent(format, autoConvert); + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, " ret: " + b.ToString()); + return b; + } + + /// + /// + /// Determines whether data stored in this instance is + /// associated with, or can be converted to, the specified + /// format. + /// + public virtual bool GetDataPresent(string format) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Check data: " + format); + bool b = GetDataPresent(format, true); + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, " ret: " + b.ToString()); + return b; + } + + + /// + /// + /// Gets a list of all formats that data stored in this + /// instance is associated with or can be converted to, using an automatic + /// conversion parameterto + /// determine whether to retrieve all formats that the data can be converted to or + /// only native data formats. + /// + public virtual string[] GetFormats(bool autoConvert) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Check formats: " + autoConvert.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + return innerData.GetFormats(autoConvert); + } + + /// + /// + /// Gets a list of all formats that data stored in this instance is associated + /// with or can be converted to. + /// + public virtual string[] GetFormats() { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Check formats:"); + return GetFormats(true); + } + + // <-- WHIDBEY ADDITIONS + + /// + /// + /// [To be supplied.] + /// + public virtual bool ContainsAudio() { + return GetDataPresent(DataFormats.WaveAudio, false); + } + + /// + /// + /// [To be supplied.] + /// + public virtual bool ContainsFileDropList() { + return GetDataPresent(DataFormats.FileDrop, true); + } + + /// + /// + /// [To be supplied.] + /// + public virtual bool ContainsImage() { + return GetDataPresent(DataFormats.Bitmap, true); + } + + /// + /// + /// [To be supplied.] + /// + public virtual bool ContainsText() { + return ContainsText(TextDataFormat.UnicodeText); + } + + /// + /// + /// [To be supplied.] + /// + public virtual bool ContainsText(TextDataFormat format) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)){ + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + return GetDataPresent(ConvertToDataFormats(format), false); + } + + /// + /// + /// [To be supplied.] + /// + public virtual Stream GetAudioStream() { + return GetData(DataFormats.WaveAudio, false) as Stream; + } + + /// + /// + /// [To be supplied.] + /// + public virtual StringCollection GetFileDropList() { + StringCollection retVal = new StringCollection(); + string[] strings = GetData(DataFormats.FileDrop, true) as string[]; + if (strings != null) { + retVal.AddRange(strings); + } + return retVal; + } + + /// + /// + /// [To be supplied.] + /// + public virtual Image GetImage() { + return GetData(DataFormats.Bitmap, true) as Image; + } + + /// + /// + /// [To be supplied.] + /// + public virtual string GetText() { + return GetText(TextDataFormat.UnicodeText); + } + + /// + /// + /// [To be supplied.] + /// + public virtual string GetText(TextDataFormat format) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)){ + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + string text = GetData(ConvertToDataFormats(format), false) as string; + if (text != null) { + return text; + } + + return String.Empty; + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetAudio(byte[] audioBytes) { + if (audioBytes == null) { + throw new ArgumentNullException("audioBytes"); + } + SetAudio(new MemoryStream(audioBytes)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetAudio(Stream audioStream) { + if (audioStream == null) { + throw new ArgumentNullException("audioStream"); + } + SetData(DataFormats.WaveAudio, false, audioStream); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetFileDropList(StringCollection filePaths) { + if (filePaths == null) { + throw new ArgumentNullException("filePaths"); + } + string[] strings = new string[filePaths.Count]; + filePaths.CopyTo(strings, 0); + SetData(DataFormats.FileDrop, true, strings); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetImage(Image image) { + if (image == null) { + throw new ArgumentNullException("image"); + } + SetData(DataFormats.Bitmap, true, image); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetText(string textData) { + SetText(textData, TextDataFormat.UnicodeText); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void SetText(string textData, TextDataFormat format) { + if (String.IsNullOrEmpty(textData)) { + throw new ArgumentNullException("textData"); + } + + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue)) + { + throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat)); + } + + SetData(ConvertToDataFormats(format), false, textData); + } + + private static string ConvertToDataFormats(TextDataFormat format) { + switch (format) { + case TextDataFormat.UnicodeText: + return DataFormats.UnicodeText; + + case TextDataFormat.Rtf: + return DataFormats.Rtf; + + case TextDataFormat.Html: + return DataFormats.Html; + + case TextDataFormat.CommaSeparatedValue: + return DataFormats.CommaSeparatedValue; + } + + return DataFormats.UnicodeText; + } + + // END - WHIDBEY ADDITIONS --> + + /// + /// + /// Retrieves a list of distinct strings from the array. + /// + private static string[] GetDistinctStrings(string[] formats) { + ArrayList distinct = new ArrayList(); + for (int i=0; i + /// + /// Returns all the "synonyms" for the specified format. + /// + private static string[] GetMappedFormats(string format) { + if (format == null) { + return null; + } + + if (format.Equals(DataFormats.Text) + || format.Equals(DataFormats.UnicodeText) + || format.Equals(DataFormats.StringFormat)) { + + return new string[] { + DataFormats.StringFormat, + DataFormats.UnicodeText, + DataFormats.Text, + }; + } + + if (format.Equals(DataFormats.FileDrop) + || format.Equals(CF_DEPRECATED_FILENAME) + || format.Equals(CF_DEPRECATED_FILENAMEW)) { + + return new string[] { + DataFormats.FileDrop, + CF_DEPRECATED_FILENAMEW, + CF_DEPRECATED_FILENAME, + }; + } + + if (format.Equals(DataFormats.Bitmap) + || format.Equals((typeof(Bitmap)).FullName)) { + + return new string[] { + (typeof(Bitmap)).FullName, + DataFormats.Bitmap, + }; + } + +/*gpr + if (format.Equals(DataFormats.EnhancedMetafile) + || format.Equals((typeof(Metafile)).FullName)) { + + return new string[] {DataFormats.EnhancedMetafile, + (typeof(Metafile)).FullName}; + } +*/ + return new String[] {format}; + } + + /* + /// + /// + /// Returns all distinct "synonyms" for the each of the formats. + /// + private static string[] GetMappedFormats(string[] formats) { + ArrayList allChoices = new ArrayList(); + + for (int i=0; i + /// + /// Returns true if the tymed is useable. + /// + /// + private bool GetTymedUseable(TYMED tymed) { + for (int i=0; i + /// + /// Populates Ole datastructes from a Microsoft dataObject. This is the core + /// of Microsoft to OLE conversion. + /// + /// + private void GetDataIntoOleStructs(ref FORMATETC formatetc, + ref STGMEDIUM medium) { + + if (GetTymedUseable(formatetc.tymed) && GetTymedUseable(medium.tymed)) { + string format = DataFormats.GetFormat(formatetc.cfFormat).Name; + + if (GetDataPresent(format)) { + Object data = GetData(format); + + if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != 0) { + int hr = SaveDataToHandle(data, format, ref medium); + if (NativeMethods.Failed(hr)) { + Marshal.ThrowExceptionForHR(hr); + } + } + else if ((formatetc.tymed & TYMED.TYMED_GDI) != 0) { + if (format.Equals(DataFormats.Bitmap) && data is Bitmap) { + // save bitmap + // + Bitmap bm = (Bitmap)data; + if (bm != null) { + medium.unionmember = GetCompatibleBitmap(bm); // gpr: Does this get properly disposed? + } + } + } +/* gpr + else if ((formatetc.tymed & TYMED.TYMED_ENHMF) != 0) { + if (format.Equals(DataFormats.EnhancedMetafile) + && data is Metafile) { + // save metafile + + Metafile mf = (Metafile)data; + if (mf != null) { + medium.unionmember = mf.Handle; + } + } + } + */ + else { + Marshal.ThrowExceptionForHR (DV_E_TYMED); + } + } + else { + Marshal.ThrowExceptionForHR (DV_E_FORMATETC); + } + } + else { + Marshal.ThrowExceptionForHR (DV_E_TYMED); + } + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + int IComDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink pAdvSink, out int pdwConnection) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "DAdvise"); + if (innerData is OleConverter) { + return ((OleConverter)innerData).OleDataObject.DAdvise(ref pFormatetc, advf, pAdvSink, out pdwConnection); + } + pdwConnection = 0; + return (NativeMethods.E_NOTIMPL); + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + void IComDataObject.DUnadvise(int dwConnection) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "DUnadvise"); + if (innerData is OleConverter) { + ((OleConverter)innerData).OleDataObject.DUnadvise(dwConnection); + return; + } + Marshal.ThrowExceptionForHR(NativeMethods.E_NOTIMPL); + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + int IComDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "EnumDAdvise"); + if (innerData is OleConverter) { + return ((OleConverter)innerData).OleDataObject.EnumDAdvise(out enumAdvise); + } + enumAdvise = null; + return (OLE_E_ADVISENOTSUPPORTED); + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + IEnumFORMATETC IComDataObject.EnumFormatEtc(DATADIR dwDirection) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "EnumFormatEtc: " + dwDirection.ToString()); + if (innerData is OleConverter) { + return ((OleConverter)innerData).OleDataObject.EnumFormatEtc(dwDirection); + } + if (dwDirection == DATADIR.DATADIR_GET) { + return new FormatEnumerator(this); + } + else { + throw new ExternalException(SR.GetString(SR.ExternalException), NativeMethods.E_NOTIMPL); + } + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + int IComDataObject.GetCanonicalFormatEtc(ref FORMATETC pformatetcIn, out FORMATETC pformatetcOut) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "GetCanonicalFormatEtc"); + if (innerData is OleConverter) { + return ((OleConverter)innerData).OleDataObject.GetCanonicalFormatEtc(ref pformatetcIn, out pformatetcOut); + } + pformatetcOut = new FORMATETC(); + return (DATA_S_SAMEFORMATETC); + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + void IComDataObject.GetData(ref FORMATETC formatetc, out STGMEDIUM medium) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "GetData"); + if (innerData is OleConverter) { + ((OleConverter)innerData).OleDataObject.GetData(ref formatetc, out medium); + return; + } + + medium = new STGMEDIUM(); + + if (GetTymedUseable(formatetc.tymed)) { + if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != 0) { + medium.tymed = TYMED.TYMED_HGLOBAL; + medium.unionmember = UnsafeNativeMethods.GlobalAlloc(NativeMethods.GMEM_MOVEABLE + | NativeMethods.GMEM_DDESHARE + | NativeMethods.GMEM_ZEROINIT, + 1); + if (medium.unionmember == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + + try { + ((IComDataObject)this).GetDataHere(ref formatetc, ref medium); + } + catch { + UnsafeNativeMethods.GlobalFree(new HandleRef(medium, medium.unionmember)); + medium.unionmember = IntPtr.Zero; + throw; + } + } + else { + medium.tymed = formatetc.tymed; + ((IComDataObject)this).GetDataHere(ref formatetc, ref medium); + } + } + else { + Marshal.ThrowExceptionForHR(DV_E_TYMED); + } + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + void IComDataObject.GetDataHere(ref FORMATETC formatetc, ref STGMEDIUM medium) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "GetDataHere"); + if (innerData is OleConverter) { + ((OleConverter)innerData).OleDataObject.GetDataHere(ref formatetc, ref medium); + } + else { + GetDataIntoOleStructs(ref formatetc, ref medium); + } + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + int IComDataObject.QueryGetData(ref FORMATETC formatetc) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "QueryGetData"); + if (innerData is OleConverter) { + return ((OleConverter)innerData).OleDataObject.QueryGetData(ref formatetc); + } + if (formatetc.dwAspect == DVASPECT.DVASPECT_CONTENT) { + if (GetTymedUseable(formatetc.tymed)) { + + if (formatetc.cfFormat == 0) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "QueryGetData::returning S_FALSE because cfFormat == 0"); + return NativeMethods.S_FALSE; + } + else { + if (!GetDataPresent(DataFormats.GetFormat(formatetc.cfFormat).Name)) { + return (DV_E_FORMATETC); + } + } + } + else { + return (DV_E_TYMED); + } + } + else { + return (DV_E_DVASPECT); + } + #if DEBUG + int format = unchecked((ushort)formatetc.cfFormat); + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "QueryGetData::cfFormat " + format.ToString(CultureInfo.InvariantCulture) + " found"); + #endif + return NativeMethods.S_OK; + } + + // + // Part of IComDataObject, used to interop with OLE. + // + // + /// + /// + // SecReview : Vs# 416823 : Need to Demand UMC as IComDataObject is now public interface. + // This exposes security hole where the IComDataObject implementation can be used to bypass UMC security to call Win32 functions. + [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + void IComDataObject.SetData(ref FORMATETC pFormatetcIn, ref STGMEDIUM pmedium, bool fRelease) { + + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "SetData"); + if (innerData is OleConverter) { + ((OleConverter)innerData).OleDataObject.SetData(ref pFormatetcIn, ref pmedium, fRelease); + return; + } + // + + throw new NotImplementedException(); + } + + private int SaveDataToHandle(object data, string format, ref STGMEDIUM medium) { + int hr = NativeMethods.E_FAIL; + if (data is Stream) { + hr = SaveStreamToHandle(ref medium.unionmember, (Stream)data); + } + else if (format.Equals(DataFormats.Text) + || format.Equals(DataFormats.Rtf) + || format.Equals(DataFormats.OemText)) { + hr = SaveStringToHandle(medium.unionmember, data.ToString(), false); + } + else if (format.Equals(DataFormats.Html)) { + if (WindowsFormsUtils.TargetsAtLeast_v4_5) { + hr = SaveHtmlToHandle(medium.unionmember, data.ToString()); + } + else { + // This will return UTF-8 strings as an array of ANSI characters, which makes it the wrong behavior. + // Since there are enough samples online how to workaround that, we will continue to return the + // incorrect value to applications targeting netfx 4.0 + // DevDiv2 bug 862524 + hr = SaveStringToHandle(medium.unionmember, data.ToString(), false); + } + } + else if (format.Equals(DataFormats.UnicodeText)) { + hr = SaveStringToHandle(medium.unionmember, data.ToString(), true); + } + else if (format.Equals(DataFormats.FileDrop)) { + hr = SaveFileListToHandle(medium.unionmember, (string[])data); + } + else if (format.Equals(CF_DEPRECATED_FILENAME)) { + string[] filelist = (string[])data; + hr = SaveStringToHandle(medium.unionmember, filelist[0], false); + } + else if (format.Equals(CF_DEPRECATED_FILENAMEW)) { + string[] filelist = (string[])data; + hr = SaveStringToHandle(medium.unionmember, filelist[0], true); + } + else if (format.Equals(DataFormats.Dib) + && data is Image) { + // GDI+ does not properly handle saving to DIB images. Since the + // clipboard will take an HBITMAP and publish a Dib, we don't need + // to support this. + // + hr = DV_E_TYMED; // SaveImageToHandle(ref medium.unionmember, (Image)data); + } + else if (format.Equals(DataFormats.Serializable) + || data is ISerializable + || (data != null && data.GetType().IsSerializable)) { + hr = SaveObjectToHandle(ref medium.unionmember, data); + } + return hr; + } + + /* + private int SaveImageToHandle(ref IntPtr handle, Image data) { + MemoryStream stream = new MemoryStream(); + data.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); + return SaveStreamToHandle(ref handle, stream); + } + */ + + private int SaveObjectToHandle(ref IntPtr handle, Object data) { + Stream stream = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(stream); + bw.Write(serializedObjectID); + SaveObjectToHandleSerializer(stream, data); + return SaveStreamToHandle(ref handle, stream); + } + + [ + SecurityPermissionAttribute(SecurityAction.Assert, Flags=SecurityPermissionFlag.SerializationFormatter) + ] + private static void SaveObjectToHandleSerializer(Stream stream, Object data) { + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(stream, data); + } + + /// + /// + /// Saves stream out to handle. + /// + /// + private int SaveStreamToHandle(ref IntPtr handle, Stream stream) { + if (handle != IntPtr.Zero) { + UnsafeNativeMethods.GlobalFree(new HandleRef(null, handle)); + } + int size = (int)stream.Length; + handle = UnsafeNativeMethods.GlobalAlloc(NativeMethods.GMEM_MOVEABLE | NativeMethods.GMEM_DDESHARE, size); + if (handle == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, handle)); + if (ptr == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + try { + byte[] bytes = new byte[size]; + stream.Position = 0; + stream.Read(bytes, 0, size); + Marshal.Copy(bytes, 0, ptr, size); + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, handle)); + } + return NativeMethods.S_OK; + } + + /// + /// + /// Saves a list of files out to the handle in HDROP format. + /// + /// + private int SaveFileListToHandle(IntPtr handle, string[] files) { + if (files == null) { + return NativeMethods.S_OK; + } + else if (files.Length < 1) { + return NativeMethods.S_OK; + } + if (handle == IntPtr.Zero) { + return (NativeMethods.E_INVALIDARG); + } + + + bool unicode = (Marshal.SystemDefaultCharSize != 1); + + IntPtr currentPtr = IntPtr.Zero; + int baseStructSize = 4 + 8 + 4 + 4; + int sizeInBytes = baseStructSize; + + // First determine the size of the array + // + if (unicode) { + for (int i=0; i + /// + /// Save string to handle. If unicode is set to true + /// then the string is saved as Unicode, else it is saves as DBCS. + /// + /// + private int SaveStringToHandle(IntPtr handle, string str, bool unicode) { + if (handle == IntPtr.Zero) { + return (NativeMethods.E_INVALIDARG); + } + IntPtr newHandle = IntPtr.Zero; + if (unicode) { + int byteSize = (str.Length*2 + 2); + newHandle = UnsafeNativeMethods.GlobalReAlloc(new HandleRef(null, handle), + byteSize, + NativeMethods.GMEM_MOVEABLE + | NativeMethods.GMEM_DDESHARE + | NativeMethods.GMEM_ZEROINIT); + if (newHandle == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, newHandle)); + if (ptr == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + // NOTE: DllLib.copy(char[]...) converts to ANSI on Windows 95... + char[] chars = str.ToCharArray(0, str.Length); + UnsafeNativeMethods.CopyMemoryW(ptr, chars, chars.Length*2); + //NativeMethods.CopyMemoryW(ptr, string, string.Length()*2); + + } + else { + + + int pinvokeSize = UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, str, str.Length, null, 0, IntPtr.Zero, IntPtr.Zero); + + byte[] strBytes = new byte[pinvokeSize]; + UnsafeNativeMethods.WideCharToMultiByte(0 /*CP_ACP*/, 0, str, str.Length, strBytes, strBytes.Length, IntPtr.Zero, IntPtr.Zero); + + newHandle = UnsafeNativeMethods.GlobalReAlloc(new HandleRef(null, handle), + pinvokeSize + 1, + NativeMethods.GMEM_MOVEABLE + | NativeMethods.GMEM_DDESHARE + | NativeMethods.GMEM_ZEROINIT); + if (newHandle == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, newHandle)); + if (ptr == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + UnsafeNativeMethods.CopyMemory(ptr, strBytes, pinvokeSize); + Marshal.Copy(new byte[] {0}, 0, (IntPtr)((long)ptr+pinvokeSize), 1); + } + + if (newHandle != IntPtr.Zero) { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, newHandle)); + } + return NativeMethods.S_OK; + } + + private int SaveHtmlToHandle(IntPtr handle, string str) { + if (handle == IntPtr.Zero) { + return (NativeMethods.E_INVALIDARG); + } + IntPtr newHandle = IntPtr.Zero; + + System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); + byte[] bytes = encoding.GetBytes(str); + + newHandle = UnsafeNativeMethods.GlobalReAlloc(new HandleRef(null, handle), + bytes.Length +1, + NativeMethods.GMEM_MOVEABLE + | NativeMethods.GMEM_DDESHARE + | NativeMethods.GMEM_ZEROINIT); + if (newHandle == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, newHandle)); + if (ptr == IntPtr.Zero) { + return (NativeMethods.E_OUTOFMEMORY); + } + try { + UnsafeNativeMethods.CopyMemory(ptr, bytes, bytes.Length); + Marshal.Copy(new byte[] { 0 }, 0, (IntPtr)((long)ptr + bytes.Length), 1); + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, newHandle)); + } + return NativeMethods.S_OK; + } + + + /// + /// + /// Stores the specified data and its associated format in + /// this instance, using the automatic conversion parameter + /// to specify whether the + /// data can be converted to another format. + /// + public virtual void SetData(string format, bool autoConvert, Object data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Set data: " + format + ", " + autoConvert.ToString() + ", " + data.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + innerData.SetData(format, autoConvert, data); + } + + /// + /// + /// Stores the specified data and its associated format in this + /// instance. + /// + public virtual void SetData(string format, object data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Set data: " + format + ", " + data.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + innerData.SetData(format, data); + } + + /// + /// + /// Stores the specified data and + /// its + /// associated class type in this instance. + /// + public virtual void SetData(Type format, object data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Set data: " + format.FullName + ", " + data.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + innerData.SetData(format, data); + } + + /// + /// + /// Stores the specified data in + /// this instance, using the class of the data for the format. + /// + public virtual void SetData(object data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "Set data: " + data.ToString()); + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + innerData.SetData(data); + } + + /// + /// + /// Part of IComDataObject, used to interop with OLE. + /// + /// + private class FormatEnumerator : IEnumFORMATETC { + + internal IDataObject parent = null; + internal ArrayList formats = new ArrayList(); + internal int current = 0; + + public FormatEnumerator(IDataObject parent) : this(parent, parent.GetFormats()) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "FormatEnumerator: Constructed: " + parent.ToString()); + } + + public FormatEnumerator(IDataObject parent, FORMATETC[] formats) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "FormatEnumerator: Constructed: " + parent.ToString() + ", FORMATETC[]" + formats.Length.ToString(CultureInfo.InvariantCulture)); + this.formats.Clear(); + this.parent = parent; + current = 0; + if (formats != null) { + DataObject dataObject = parent as DataObject; + if (dataObject != null && dataObject.RestrictedFormats) { + if (!Clipboard.IsFormatValid(formats)) { + throw new SecurityException(SR.GetString(SR.ClipboardSecurityException)); + } + } + + for (int i=0; i 0) { + + FORMATETC current = (FORMATETC)formats[this.current]; + rgelt[0].cfFormat = current.cfFormat; + rgelt[0].tymed = current.tymed; + rgelt[0].dwAspect = DVASPECT.DVASPECT_CONTENT; + rgelt[0].ptd = IntPtr.Zero; + rgelt[0].lindex = -1; + + if (pceltFetched != null) { + pceltFetched[0] = 1; + } + this.current++; + } + else { + if (pceltFetched != null) { + pceltFetched[0] = 0; + } + return NativeMethods.S_FALSE; + } + return NativeMethods.S_OK; + } + + public int Skip(int celt) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "FormatEnumerator: Skip"); + if (current + celt >= this.formats.Count) { + return NativeMethods.S_FALSE; + } + current += celt; + return NativeMethods.S_OK; + } + + public int Reset() { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "FormatEnumerator: Reset"); + current = 0; + return NativeMethods.S_OK; + } + + public void Clone(out IEnumFORMATETC ppenum) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "FormatEnumerator: Clone"); + FORMATETC[] temp = new FORMATETC[formats.Count]; + formats.CopyTo(temp, 0); + ppenum = new FormatEnumerator(parent, temp); + } + } + + /// + /// + /// OLE Converter. This class embodies the nastiness required to convert from our + /// managed types to standard OLE clipboard formats. + /// + private class OleConverter : IDataObject { + internal IComDataObject innerData; + + public OleConverter(IComDataObject data) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "OleConverter: Constructed OleConverter"); + innerData = data; + } + + /// + /// + /// Returns the data Object we are wrapping + /// + /// + public IComDataObject OleDataObject { + get { + return innerData; + } + } + + /// + /// + /// Uses IStream and retrieves the specified format from the bound IComDataObject. + /// + /// + private Object GetDataFromOleIStream(string format) { + + FORMATETC formatetc = new FORMATETC(); + STGMEDIUM medium = new STGMEDIUM(); + + formatetc.cfFormat = unchecked((short)(ushort)(DataFormats.GetFormat(format).Id)); + formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = TYMED.TYMED_ISTREAM; + + medium.tymed = TYMED.TYMED_ISTREAM; + + // Limit the # of exceptions we may throw below. + if (NativeMethods.S_OK != QueryGetDataUnsafe(ref formatetc)){ + return null; + } + + try { + IntSecurity.UnmanagedCode.Assert(); + try { + innerData.GetData(ref formatetc, out medium); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + catch { + return null; + } + + if (medium.unionmember != IntPtr.Zero) { + UnsafeNativeMethods.IStream pStream = (UnsafeNativeMethods.IStream)Marshal.GetObjectForIUnknown(medium.unionmember); + Marshal.Release(medium.unionmember); + NativeMethods.STATSTG sstg = new NativeMethods.STATSTG(); + pStream.Stat(sstg, NativeMethods.STATFLAG_DEFAULT ); + int size = (int)sstg.cbSize; + + IntPtr hglobal = UnsafeNativeMethods.GlobalAlloc(NativeMethods.GMEM_MOVEABLE + | NativeMethods.GMEM_DDESHARE + | NativeMethods.GMEM_ZEROINIT, + size); + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(innerData, hglobal)); + pStream.Read(ptr, size); + UnsafeNativeMethods.GlobalUnlock(new HandleRef(innerData, hglobal)); + + return GetDataFromHGLOBLAL(format, hglobal); + } + + return null; + } + + + /// + /// + /// Retrieves the specified form from the specified hglobal. + /// + /// + private Object GetDataFromHGLOBLAL(string format, IntPtr hglobal) { + Object data = null; + + if (hglobal != IntPtr.Zero) { + //=----------------------------------------------------------------= + // Convert from OLE to IW objects + //=----------------------------------------------------------------= + // Add any new formats here... + + if (format.Equals(DataFormats.Text) + || format.Equals(DataFormats.Rtf) + || format.Equals(DataFormats.OemText)) { + data = ReadStringFromHandle(hglobal, false); + } + else if (format.Equals(DataFormats.Html)) { + if (WindowsFormsUtils.TargetsAtLeast_v4_5) { + data = ReadHtmlFromHandle(hglobal); + } + else { + // This will return UTF-8 strings as an array of ANSI characters, which makes it the wrong behavior. + // Since there are enough samples online how to workaround that, we will continue to return the + // incorrect value to applications targeting netfx 4.0 + // DevDiv2 bug 862524 + data = ReadStringFromHandle(hglobal, false); + } + } + + else if (format.Equals(DataFormats.UnicodeText)) { + data = ReadStringFromHandle(hglobal, true); + } + else if (format.Equals(DataFormats.FileDrop)) { + data = (Object)ReadFileListFromHandle(hglobal); + } + else if (format.Equals(CF_DEPRECATED_FILENAME)) { + data = new string[] {ReadStringFromHandle(hglobal, false)}; + } + else if (format.Equals(CF_DEPRECATED_FILENAMEW)) { + data = new string[] {ReadStringFromHandle(hglobal, true)}; + } + else if (!LocalAppContextSwitches.EnableLegacyDangerousClipboardDeserializationMode) { + // We are restricting all formats that might eventually be processed by the BinaryFormatter + // in ReadObjectFromHandleDeserializer to allow only primitive types. + // Application might opt-out from the secure deserialization mode by adding this + // switch to app.config file, unless the machine has device guard enabled, in that case + // applications can't can't opt out from restricted deserialization: + // + // + // + bool restrictDeserialization = + (format.Equals(DataFormats.StringFormat) || + format.Equals(typeof(Bitmap).FullName) || + format.Equals(DataFormats.CommaSeparatedValue) || + format.Equals(DataFormats.Dib) || + format.Equals(DataFormats.Dif) || + format.Equals(DataFormats.Locale) || + format.Equals(DataFormats.PenData) || + format.Equals(DataFormats.Riff) || + format.Equals(DataFormats.SymbolicLink) || + format.Equals(DataFormats.Tiff) || + format.Equals(DataFormats.WaveAudio) || + format.Equals(DataFormats.Bitmap) || + format.Equals(DataFormats.EnhancedMetafile) || + format.Equals(DataFormats.Palette) || + format.Equals(DataFormats.MetafilePict)); + data = ReadObjectFromHandle(hglobal, restrictDeserialization); + } + else { + data = ReadObjectFromHandle(hglobal, restrictDeserialization: false); + /* + spb - 93835 dib support is a mess + if (format.Equals(DataFormats.Dib) + && data is Stream) { + data = new Bitmap((Stream)data); + } + */ + } + + UnsafeNativeMethods.GlobalFree(new HandleRef(null, hglobal)); + } + + return data; + } + + /// + /// + /// Uses HGLOBALs and retrieves the specified format from the bound IComDatabject. + /// + /// + private Object GetDataFromOleHGLOBAL(string format, out bool done) { + done = false; + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + + FORMATETC formatetc = new FORMATETC(); + STGMEDIUM medium = new STGMEDIUM(); + + formatetc.cfFormat = unchecked((short)(ushort)(DataFormats.GetFormat(format).Id)); + formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = TYMED.TYMED_HGLOBAL; + + medium.tymed = TYMED.TYMED_HGLOBAL; + + object data = null; + + if (NativeMethods.S_OK == QueryGetDataUnsafe(ref formatetc)) { + try { + IntSecurity.UnmanagedCode.Assert(); + try { + innerData.GetData(ref formatetc, out medium); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (medium.unionmember != IntPtr.Zero) { + data = GetDataFromHGLOBLAL(format, medium.unionmember); + } + } + catch (RestrictedTypeDeserializationException) { + done = true; + } + catch { + } + } + return data; + } + + /// + /// + /// Retrieves the specified format data from the bound IComDataObject, from + /// other sources that IStream and HGLOBAL... this is really just a place + /// to put the "special" formats like BITMAP, ENHMF, etc. + /// + /// + private Object GetDataFromOleOther(string format) { + Debug.Assert(innerData != null, "You must have an innerData on all DataObjects"); + + FORMATETC formatetc = new FORMATETC(); + STGMEDIUM medium = new STGMEDIUM(); + + TYMED tymed = (TYMED)0; + + if (format.Equals(DataFormats.Bitmap)) { + tymed = TYMED.TYMED_GDI; + } + else if (format.Equals(DataFormats.EnhancedMetafile)) { + tymed = TYMED.TYMED_ENHMF; + } + + if (tymed == (TYMED)0) { + return null; + } + + formatetc.cfFormat = unchecked((short)(ushort)(DataFormats.GetFormat(format).Id)); + formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = tymed; + medium.tymed = tymed; + + Object data = null; + if (NativeMethods.S_OK == QueryGetDataUnsafe(ref formatetc)) { + try { + IntSecurity.UnmanagedCode.Assert(); + try { + innerData.GetData(ref formatetc, out medium); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + catch { + } + } + + if (medium.unionmember != IntPtr.Zero) { + + if (format.Equals(DataFormats.Bitmap) + //||format.Equals(DataFormats.Dib)) + ) { + // as/urt 140870 -- GDI+ doesn't own this HBITMAP, but we can't + // delete it while the object is still around. So we have to do the really expensive + // thing of cloning the image so we can release the HBITMAP. + // + + //This bitmap is created by the com object which originally copied the bitmap to tbe + //clipboard. We call Add here, since DeleteObject calls Remove. + System.Internal.HandleCollector.Add(medium.unionmember, NativeMethods.CommonHandles.GDI); + Image clipboardImage = null; + IntSecurity.ObjectFromWin32Handle.Assert(); + try + { + clipboardImage = Image.FromHbitmap(medium.unionmember); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + if (clipboardImage != null) { + Image firstImage = clipboardImage; + clipboardImage = (Image)clipboardImage.Clone(); + SafeNativeMethods.DeleteObject(new HandleRef(null, medium.unionmember)); + firstImage.Dispose(); + } + data = clipboardImage; + } +/* gpr: + else if (format.Equals(DataFormats.EnhancedMetafile)) { + data = new Metafile(medium.unionmember); + } +*/ + } + + return data; + } + + /// + /// + /// Extracts a managed Object from the innerData of the specified + /// format. This is the base of the OLE to managed conversion. + /// + /// + private Object GetDataFromBoundOleDataObject(string format, out bool done) { + Object data = null; + done = false; + try + { + data = GetDataFromOleOther(format); + if (data == null) + { + data = GetDataFromOleHGLOBAL(format, out done); + } + if (data == null && !done) + { + data = GetDataFromOleIStream(format); + } + } + catch (Exception e) + { + Debug.Fail(e.ToString()); + } + return data; + } + + /// + /// + /// Creates an Stream from the data stored in handle. + /// + /// + private Stream ReadByteStreamFromHandle(IntPtr handle, out bool isSerializedObject) { + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, handle)); + if (ptr == IntPtr.Zero){ + throw new ExternalException(SR.GetString(SR.ExternalException), NativeMethods.E_OUTOFMEMORY); + } + try { + int size = UnsafeNativeMethods.GlobalSize(new HandleRef(null, handle)); + byte[] bytes = new byte[size]; + Marshal.Copy(ptr, bytes, 0, size); + int index = 0; + + // The object here can either be a stream or a serialized + // object. We identify a serialized object by writing the + // bytes for the guid serializedObjectID at the front + // of the stream. Check for that here. + // + if (size > serializedObjectID.Length) { + isSerializedObject = true; + for(int i = 0; i < serializedObjectID.Length; i++) { + if (serializedObjectID[i] != bytes[i]) { + isSerializedObject = false; + break; + } + } + + // Advance the byte pointer. + // + if (isSerializedObject) { + index = serializedObjectID.Length; + } + } + else { + isSerializedObject = false; + } + + return new MemoryStream(bytes, index, bytes.Length - index); + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, handle)); + } + } + + /// + /// + /// Creates a new instance of the Object that has been persisted into the + /// handle. + /// + /// + private Object ReadObjectFromHandle(IntPtr handle, bool restrictDeserialization) + { + object value = null; + + bool isSerializedObject; + Stream stream = ReadByteStreamFromHandle(handle, out isSerializedObject); + + if (isSerializedObject) { + value = ReadObjectFromHandleDeserializer(stream, restrictDeserialization); + } + else { + value = stream; + } + + return value; + } + + private static Object ReadObjectFromHandleDeserializer(Stream stream, bool restrictDeserialization) { + BinaryFormatter formatter = new BinaryFormatter(); + + if (restrictDeserialization) { + formatter.Binder = new RestrictiveBinder(); + } + formatter.AssemblyFormat = FormatterAssemblyStyle.Simple; + return formatter.Deserialize(stream); + } + + /// + /// Binder that restricts dataobject content deserialization to safe types. + /// + private class RestrictiveBinder : SerializationBinder { + private static string s_allowedTypeName; + private static string s_allowedAssemblyName; + private static byte[] s_allowedToken; + + static RestrictiveBinder() { + s_allowedTypeName = typeof(Bitmap).FullName; + AssemblyName assemblyName = new AssemblyName(typeof(Bitmap).Assembly.FullName); + if (assemblyName != null) { + s_allowedAssemblyName = assemblyName.Name; + s_allowedToken = assemblyName.GetPublicKeyToken(); + } + } + + /// + /// Only safe to deserialize types are bypassing this callback, Strings + /// and arrays of primitive types in particular. We are explicitly allowing + /// System.Drawing.Bitmap type to bind using the default binder. + /// + /// + /// + /// + public override Type BindToType(string assemblyName, string typeName) { + if (string.CompareOrdinal(typeName, s_allowedTypeName) == 0) { + AssemblyName nameToBind = null; + try { + nameToBind = new AssemblyName(assemblyName); + } + catch { + } + if (nameToBind != null) { + if (string.CompareOrdinal(nameToBind.Name, s_allowedAssemblyName) == 0) { + byte[] tokenToBind = nameToBind.GetPublicKeyToken(); + if ((tokenToBind != null) && + (s_allowedToken != null) && + (tokenToBind.Length == s_allowedToken.Length)) { + bool block = false; + for (int i = 0; i < s_allowedToken.Length; i++) { + if (s_allowedToken[i] != tokenToBind[i]) { + block = true; + break; + } + } + if (!block) { + return null; + } + } + } + } + } + throw new RestrictedTypeDeserializationException(); + } + } + + /// + /// This exceptionm is used to indicate that clipboard contains a serialized + /// managed object that contains unexpected types. + /// + private class RestrictedTypeDeserializationException : Exception { + } + + /// + /// + /// Parses the HDROP format and returns a list of strings using + /// the DragQueryFile function. + /// + /// + private string[] ReadFileListFromHandle(IntPtr hdrop) { + + string[] files = null; + StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH); + + int count = UnsafeNativeMethods.DragQueryFile(new HandleRef(null, hdrop), unchecked((int)0xFFFFFFFF), null, 0); + if (count > 0) { + files = new string[count]; + + + for (int i=0; i + /// + /// Creates a string from the data stored in handle. If + /// unicode is set to true, then the string is assume to be Unicode, + /// else DBCS (ASCI) is assumed. + /// + /// + private unsafe string ReadStringFromHandle(IntPtr handle, bool unicode) { + string stringData = null; + + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, handle)); + try { + if (unicode) { + stringData = new string((char*)ptr); + } + else { + stringData = new string((sbyte*)ptr); + } + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, handle)); + } + + return stringData; + } + + // Dev11 92568 Garbled Japanese is produced when a text message is cut and pasted on the editor in Team Explorer of TFS 2010 (was DTS) + private unsafe string ReadHtmlFromHandle(IntPtr handle) { + string stringData = null; + IntPtr ptr = UnsafeNativeMethods.GlobalLock(new HandleRef(null, handle)); + try { + int size = UnsafeNativeMethods.GlobalSize(new HandleRef(null, handle)); + byte[] bytes = new byte[size]; + Marshal.Copy(ptr, bytes, 0, size); + stringData = Encoding.UTF8.GetString(bytes); + } + finally { + UnsafeNativeMethods.GlobalUnlock(new HandleRef(null, handle)); + } + + return stringData; + } + + + //=------------------------------------------------------------------------= + // IDataObject + //=------------------------------------------------------------------------= + public virtual Object GetData(string format, bool autoConvert) { + bool done = false; + Object baseVar = GetDataFromBoundOleDataObject(format, out done); + Object original = baseVar; + + if (!done && autoConvert && (baseVar == null || baseVar is MemoryStream)) { + string[] mappedFormats = GetMappedFormats(format); + if (mappedFormats != null) { + for (int i=0; ((!done) && (i 0) { + retrieved[0] = 0; + try { + enumFORMATETC.Next(1, formatetc, retrieved); + } + catch { + } + + if (retrieved[0] > 0) { + string name = DataFormats.GetFormat(formatetc[0].cfFormat).Name; + if (autoConvert) { + string[] mappedFormats = GetMappedFormats(name); + for (int i=0; i + /// + /// + private class DataStore : IDataObject { + private class DataStoreEntry { + public Object data; + public bool autoConvert; + + public DataStoreEntry(Object data, bool autoConvert) { + this.data = data; + this.autoConvert = autoConvert; + } + } + + private Hashtable data = new Hashtable(BackCompatibleStringComparer.Default); + + public DataStore() { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "DataStore: Constructed DataStore"); + } + + public virtual Object GetData(string format, bool autoConvert) { + Debug.WriteLineIf(CompModSwitches.DataObject.TraceVerbose, "DataStore: GetData: " + format + ", " + autoConvert.ToString()); + DataStoreEntry dse = (DataStoreEntry)data[format]; + Object baseVar = null; + if (dse != null) { + baseVar = dse.data; + } + Object original = baseVar; + + if (autoConvert + && (dse == null || dse.autoConvert) + && (baseVar == null || baseVar is MemoryStream)) { + + string[] mappedFormats = GetMappedFormats(format); + if (mappedFormats != null) { + for (int i=0; i +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + /// + /// Determines when changes to a data-bound control property get propagated back to the corresponding data source property. + /// + public enum DataSourceUpdateMode { + + /// + /// + /// Data source is updated when the control property is validated, ie. during the Validating event. + /// Typically this does not occur until the input focus leaves the control. + /// After validation, the value in the control property will also be re-formatted. + /// This is the default update mode. + /// + OnValidation = 0, + + /// + /// + /// Data source is updated whenever the control property changes (and also updated again during validation). + /// After validation, the value in the control property will also be re-formatted. + /// + OnPropertyChanged = 1, + + /// + /// + /// Data source is never updated. Binding is "read-only" with respect to the data source. + /// Values entered into the control are *not* parsed, validated or re-formatted. + /// To force the data source to be updated, use the Binding.WriteValue method. + /// + Never = 2, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DataStreamFromComStream.cs b/WindowsForms/Managed/System/WinForms/DataStreamFromComStream.cs new file mode 100644 index 000000000..20fc89c53 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DataStreamFromComStream.cs @@ -0,0 +1,137 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System; + using System.IO; + + + /// + /// + /// + /// + internal class DataStreamFromComStream : Stream { + + private UnsafeNativeMethods.IStream comStream; + + public DataStreamFromComStream(UnsafeNativeMethods.IStream comStream) : base() { + this.comStream = comStream; + } + + public override long Position { + get { + return Seek(0, SeekOrigin.Current); + } + + set { + Seek(value, SeekOrigin.Begin); + } + } + + public override bool CanWrite { + get { + return true; + } + } + + public override bool CanSeek { + get { + return true; + } + } + + public override bool CanRead { + get { + return true; + } + } + + public override long Length { + get { + long curPos = this.Position; + long endPos = Seek(0, SeekOrigin.End); + this.Position = curPos; + return endPos - curPos; + } + } + + /* + private void _NotImpl(string message) { + NotSupportedException ex = new NotSupportedException(message, new ExternalException(SR.GetString(SR.ExternalException), NativeMethods.E_NOTIMPL)); + throw ex; + } + */ + + private unsafe int _Read(void* handle, int bytes) { + return comStream.Read((IntPtr)handle, bytes); + } + + private unsafe int _Write(void* handle, int bytes) { + return comStream.Write((IntPtr)handle, bytes); + } + + public override void Flush() { + } + + public unsafe override int Read(byte[] buffer, int index, int count) { + int bytesRead = 0; + if (count > 0 && index >= 0 && (count + index) <= buffer.Length) { + fixed (byte* ch = buffer) { + bytesRead = _Read((void*)(ch + index), count); + } + } + return bytesRead; + } + + public override void SetLength(long value) { + comStream.SetSize(value); + } + + public override long Seek(long offset, SeekOrigin origin) { + return comStream.Seek(offset, (int)origin); + } + + public unsafe override void Write(byte[] buffer, int index, int count) { + int bytesWritten = 0; + if (count > 0 && index >= 0 && (count + index) <= buffer.Length) { + try { + fixed (byte* b = buffer) { + bytesWritten = _Write((void*)(b + index), count); + } + } + catch { + } + } + if (bytesWritten < count) { + throw new IOException(SR.GetString(SR.DataStreamWrite)); + } + } + + protected override void Dispose(bool disposing) { + try { + if (disposing && comStream != null) { + try { + comStream.Commit(NativeMethods.STGC_DEFAULT); + } + catch(Exception) { + } + } + // Can't release a COM stream from the finalizer thread. + comStream = null; + } + finally { + base.Dispose(disposing); + } + } + + ~DataStreamFromComStream() { + Dispose(false); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DatagridviewDisplayedBandsData.cs b/WindowsForms/Managed/System/WinForms/DatagridviewDisplayedBandsData.cs new file mode 100644 index 000000000..03caae963 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DatagridviewDisplayedBandsData.cs @@ -0,0 +1,365 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + public partial class DataGridView + { + internal class DisplayedBandsData + { + private bool dirty; + + private int firstDisplayedFrozenRow; + private int firstDisplayedFrozenCol; + private int numDisplayedFrozenRows; + private int numDisplayedFrozenCols; + private int numTotallyDisplayedFrozenRows; + private int firstDisplayedScrollingRow; + private int numDisplayedScrollingRows; + private int numTotallyDisplayedScrollingRows; + private int firstDisplayedScrollingCol; + private int numDisplayedScrollingCols; + private int lastTotallyDisplayedScrollingCol; + private int lastDisplayedScrollingRow; + private int lastDisplayedFrozenCol; + private int lastDisplayedFrozenRow; + private int oldFirstDisplayedScrollingRow; + private int oldFirstDisplayedScrollingCol; + private int oldNumDisplayedFrozenRows; + private int oldNumDisplayedScrollingRows; + + private bool rowInsertionOccurred, columnInsertionOccurred; + + public DisplayedBandsData() + { + this.firstDisplayedFrozenRow = -1; + this.firstDisplayedFrozenCol = -1; + this.firstDisplayedScrollingRow = -1; + this.firstDisplayedScrollingCol = -1; + this.lastTotallyDisplayedScrollingCol = -1; + this.lastDisplayedScrollingRow = -1; + this.lastDisplayedFrozenCol = -1; + this.lastDisplayedFrozenRow = -1; + this.oldFirstDisplayedScrollingRow = -1; + this.oldFirstDisplayedScrollingCol = -1; + } + + public bool ColumnInsertionOccurred + { + get + { + return this.columnInsertionOccurred; + } + } + + public bool Dirty + { + get + { + return this.dirty; + } + set + { + this.dirty = value; + } + } + + public int FirstDisplayedFrozenCol + { + set + { + if (value != this.firstDisplayedFrozenCol) + { + EnsureDirtyState(); + this.firstDisplayedFrozenCol = value; + } + } + } + + public int FirstDisplayedFrozenRow + { + set + { + if (value != this.firstDisplayedFrozenRow) + { + EnsureDirtyState(); + this.firstDisplayedFrozenRow = value; + } + } + } + + public int FirstDisplayedScrollingCol + { + get + { + return this.firstDisplayedScrollingCol; + } + set + { + if (value != this.firstDisplayedScrollingCol) + { + EnsureDirtyState(); + this.firstDisplayedScrollingCol = value; + } + } + } + + public int FirstDisplayedScrollingRow + { + get + { + return this.firstDisplayedScrollingRow; + } + set + { + if (value != this.firstDisplayedScrollingRow) + { + EnsureDirtyState(); + this.firstDisplayedScrollingRow = value; + } + } + } + + public int LastDisplayedFrozenCol + { + set + { + if (value != this.lastDisplayedFrozenCol) + { + EnsureDirtyState(); + this.lastDisplayedFrozenCol = value; + } + } + } + + public int LastDisplayedFrozenRow + { + set + { + if (value != this.lastDisplayedFrozenRow) + { + EnsureDirtyState(); + this.lastDisplayedFrozenRow = value; + } + } + } + + public int LastDisplayedScrollingRow + { + set + { + if (value != this.lastDisplayedScrollingRow) + { + EnsureDirtyState(); + this.lastDisplayedScrollingRow = value; + } + } + } + + public int LastTotallyDisplayedScrollingCol + { + get + { + return this.lastTotallyDisplayedScrollingCol; + } + set + { + if (value != this.lastTotallyDisplayedScrollingCol) + { + EnsureDirtyState(); + this.lastTotallyDisplayedScrollingCol = value; + } + } + } + + public int NumDisplayedFrozenCols + { + get + { + return this.numDisplayedFrozenCols; + } + set + { + if (value != this.numDisplayedFrozenCols) + { + EnsureDirtyState(); + this.numDisplayedFrozenCols = value; + } + } + } + + public int NumDisplayedFrozenRows + { + get + { + return this.numDisplayedFrozenRows; + } + set + { + if (value != this.numDisplayedFrozenRows) + { + EnsureDirtyState(); + this.numDisplayedFrozenRows = value; + } + } + } + + public int NumDisplayedScrollingRows + { + get + { + return this.numDisplayedScrollingRows; + } + set + { + if (value != this.numDisplayedScrollingRows) + { + EnsureDirtyState(); + this.numDisplayedScrollingRows = value; + } + } + } + + public int NumDisplayedScrollingCols + { + get + { + return this.numDisplayedScrollingCols; + } + set + { + if (value != this.numDisplayedScrollingCols) + { + EnsureDirtyState(); + this.numDisplayedScrollingCols = value; + } + } + } + + public int NumTotallyDisplayedFrozenRows + { + get + { + return this.numTotallyDisplayedFrozenRows; + } + set + { + if (value != this.numTotallyDisplayedFrozenRows) + { + EnsureDirtyState(); + this.numTotallyDisplayedFrozenRows = value; + } + } + } + + public int NumTotallyDisplayedScrollingRows + { + get + { + return this.numTotallyDisplayedScrollingRows; + } + set + { + if (value != this.numTotallyDisplayedScrollingRows) + { + EnsureDirtyState(); + this.numTotallyDisplayedScrollingRows = value; + } + } + } + + public int OldFirstDisplayedScrollingCol + { + get + { + return this.oldFirstDisplayedScrollingCol; + } + } + + public int OldFirstDisplayedScrollingRow + { + get + { + return this.oldFirstDisplayedScrollingRow; + } + } + + public int OldNumDisplayedFrozenRows + { + get + { + return this.oldNumDisplayedFrozenRows; + } + } + + public int OldNumDisplayedScrollingRows + { + get + { + return this.oldNumDisplayedScrollingRows; + } + } + + public bool RowInsertionOccurred + { + get + { + return this.rowInsertionOccurred; + } + } + + public void EnsureDirtyState() + { + if (!this.dirty) + { + this.dirty = true; + this.rowInsertionOccurred = false; + this.columnInsertionOccurred = false; + SetOldValues(); + } + } + + public void CorrectColumnIndexAfterInsertion(int columnIndex, int insertionCount) + { + EnsureDirtyState(); + if (this.oldFirstDisplayedScrollingCol != -1 && columnIndex <= this.oldFirstDisplayedScrollingCol) + { + this.oldFirstDisplayedScrollingCol += insertionCount; + } + this.columnInsertionOccurred = true; + } + + public void CorrectRowIndexAfterDeletion(int rowIndex) + { + EnsureDirtyState(); + if (this.oldFirstDisplayedScrollingRow != -1 && rowIndex <= this.oldFirstDisplayedScrollingRow) + { + this.oldFirstDisplayedScrollingRow--; + } + } + + public void CorrectRowIndexAfterInsertion(int rowIndex, int insertionCount) + { + EnsureDirtyState(); + if (this.oldFirstDisplayedScrollingRow != -1 && rowIndex <= this.oldFirstDisplayedScrollingRow) + { + this.oldFirstDisplayedScrollingRow += insertionCount; + } + this.rowInsertionOccurred = true; + this.oldNumDisplayedScrollingRows += insertionCount; + this.oldNumDisplayedFrozenRows += insertionCount; + } + + private void SetOldValues() + { + this.oldFirstDisplayedScrollingRow = this.firstDisplayedScrollingRow; + this.oldFirstDisplayedScrollingCol = this.firstDisplayedScrollingCol; + this.oldNumDisplayedFrozenRows = this.numDisplayedFrozenRows; + this.oldNumDisplayedScrollingRows = this.numDisplayedScrollingRows; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DateBoldEvent.cs b/WindowsForms/Managed/System/WinForms/DateBoldEvent.cs new file mode 100644 index 000000000..3e6f169ba --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateBoldEvent.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// The month calendar control fires this event to request information + /// about how the days within the visible months should be displayed. + /// + // + // + public class DateBoldEventArgs : EventArgs { + readonly DateTime startDate; //the starting date + readonly int size; // requested length of array + int[] daysToBold = null; + + internal DateBoldEventArgs(DateTime start, int size) { + startDate = start; + this.size = size; + } + /// + /// + /// [To be supplied.] + /// + public DateTime StartDate { + get { return startDate; } + } + /// + /// + /// [To be supplied.] + /// + public int Size { + get { return size; } + } + /// + /// + /// [To be supplied.] + /// + public int[] DaysToBold { + get { return daysToBold; } + set { daysToBold = value; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DateBoldEventHandler.cs b/WindowsForms/Managed/System/WinForms/DateBoldEventHandler.cs new file mode 100644 index 000000000..c05e5cda5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateBoldEventHandler.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// + public delegate void DateBoldEventHandler(object sender, DateBoldEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DateRangeEvent.cs b/WindowsForms/Managed/System/WinForms/DateRangeEvent.cs new file mode 100644 index 000000000..da39b5304 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateRangeEvent.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// The SelectEvent is fired when the user makes an explicit date + /// selection within a month calendar control. + /// + public class DateRangeEventArgs : EventArgs { + + readonly DateTime start; // The date for the first day in the user's selection range. + readonly DateTime end; // The date for the last day in the user's selection range. + + /// + /// + /// [To be supplied.] + /// + public DateRangeEventArgs(DateTime start, DateTime end) { + this.start = start; + this.end = end; + } + + /// + /// + /// [To be supplied.] + /// + public DateTime Start { + get { return start; } + } + /// + /// + /// [To be supplied.] + /// + public DateTime End { + get { return end; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DateRangeEventHandler.cs b/WindowsForms/Managed/System/WinForms/DateRangeEventHandler.cs new file mode 100644 index 000000000..caa852f41 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateRangeEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void DateRangeEventHandler(object sender, DateRangeEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DateTimePicker.cs b/WindowsForms/Managed/System/WinForms/DateTimePicker.cs new file mode 100644 index 000000000..52a94eba4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateTimePicker.cs @@ -0,0 +1,1865 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Windows.Forms.Layout; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Globalization; + + using Microsoft.Win32; + + /// + /// + /// Date/DateTime picker control + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultEvent("ValueChanged"), + DefaultBindingProperty("Value"), + Designer("System.Windows.Forms.Design.DateTimePickerDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionDateTimePicker) + ] + public class DateTimePicker : Control { + /// + /// + /// Specifies the default title back color. This field is read-only. + /// + protected static readonly Color DefaultTitleBackColor = SystemColors.ActiveCaption; + /// + /// + /// Specifies the default foreground color. This field is read-only. + /// + protected static readonly Color DefaultTitleForeColor = SystemColors.ActiveCaptionText; + /// + /// + /// Specifies the default month background color. This field is read-only. + /// + protected static readonly Color DefaultMonthBackColor = SystemColors.Window; + /// + /// + /// Specifies the default trailing forground color. This field is read-only. + /// + protected static readonly Color DefaultTrailingForeColor = SystemColors.GrayText; + + private static readonly object EVENT_FORMATCHANGED = new object(); + + private static readonly string DateTimePickerLocalizedControlTypeString = SR.GetString(SR.DateTimePickerLocalizedControlType); + + private const int TIMEFORMAT_NOUPDOWN = NativeMethods.DTS_TIMEFORMAT & (~NativeMethods.DTS_UPDOWN); + private EventHandler onCloseUp; + private EventHandler onDropDown; + private EventHandler onValueChanged; + private EventHandler onRightToLeftLayoutChanged; + + // We need to restrict the available dates because of limitations in the comctl + // DateTime and MonthCalendar controls + // + /// + /// + /// Specifies the minimum date value. This field is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public static readonly DateTime MinDateTime = new DateTime(1753, 1, 1); + + /// + /// + /// Specifies the maximum date value. This field is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public static readonly DateTime MaxDateTime = new DateTime(9998, 12, 31); + + private int style; + private short prefHeightCache = -1; + + /// + /// validTime determines whether the CheckBox in the DTP is checked. The CheckBox is only + /// displayed when ShowCheckBox is true. + /// + /// + private bool validTime = true; + + // DateTime changeover: DateTime is a value class, not an object, so we need to keep track + // of whether or not its values have been initialised in a separate boolean. + private bool userHasSetValue = false; + private DateTime value = DateTime.Now; + private DateTime creationTime = DateTime.Now; + // VSWhidbey 400284: Reconcile out-of-range min/max values in the property getters. + private DateTime max = DateTime.MaxValue; + private DateTime min = DateTime.MinValue; + private Color calendarForeColor = DefaultForeColor; + private Color calendarTitleBackColor = DefaultTitleBackColor; + private Color calendarTitleForeColor = DefaultTitleForeColor; + private Color calendarMonthBackground = DefaultMonthBackColor; + private Color calendarTrailingText = DefaultTrailingForeColor; + private Font calendarFont = null; + private FontHandleWrapper calendarFontHandleWrapper = null; + + // Since there is no way to get the customFormat from the DTP, we need to + // cache it. Also we have to track if the user wanted customFormat or + // shortDate format (shortDate is the lack of being in Long or DateTime format + // without a customFormat). What fun! + // + private string customFormat; + + private DateTimePickerFormat format; + + private bool rightToLeftLayout = false; + + /// + /// + /// Initializes a new instance of the class. + /// + public DateTimePicker() + : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetStyle(ControlStyles.FixedHeight, true); + + // Since DateTimePicker does its own mouse capturing, we do not want + // to receive standard click events, or we end up with mismatched mouse + // button up and button down messages. + // + SetStyle(ControlStyles.UserPaint | + ControlStyles.StandardClick, false); + + // Set default flags here... + // + format = DateTimePickerFormat.Long; + + if (AccessibilityImprovements.Level3) { + SetStyle(ControlStyles.UseTextForAccessibility, false); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// The current value of the CalendarForeColor property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerCalendarForeColorDescr) + ] + public Color CalendarForeColor { + get { + return calendarForeColor; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + if (!value.Equals(calendarForeColor)) { + calendarForeColor = value; + SetControlColor(NativeMethods.MCSC_TEXT, value); + } + } + } + + /// + /// + /// The current value of the CalendarFont property. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(null), + SRDescription(SR.DateTimePickerCalendarFontDescr) + ] + public Font CalendarFont { + get { + if (calendarFont == null) { + return Font; + } + return calendarFont; + } + + set { + if ((value == null && calendarFont != null) || (value != null && !value.Equals(calendarFont))) { + calendarFont = value; + calendarFontHandleWrapper = null; + SetControlCalendarFont(); + } + } + } + + private IntPtr CalendarFontHandle { + get { + if (calendarFont == null) { + Debug.Assert(calendarFontHandleWrapper == null, "font handle out of sync with Font"); + return FontHandle; + } + + if (calendarFontHandleWrapper == null) { + calendarFontHandleWrapper = new FontHandleWrapper(CalendarFont); + } + + return calendarFontHandleWrapper.Handle; + } + } + + /// + /// + /// The current value of the CalendarTitleBackColor property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerCalendarTitleBackColorDescr) + ] + public Color CalendarTitleBackColor { + get { + return calendarTitleBackColor; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + if (!value.Equals(calendarTitleBackColor)) { + calendarTitleBackColor = value; + SetControlColor(NativeMethods.MCSC_TITLEBK, value); + } + } + } + + /// + /// + /// The current value of the CalendarTitleForeColor property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerCalendarTitleForeColorDescr) + ] + public Color CalendarTitleForeColor { + get { + return calendarTitleForeColor; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + if (!value.Equals(calendarTitleForeColor)) { + calendarTitleForeColor = value; + SetControlColor(NativeMethods.MCSC_TITLETEXT, value); + } + } + } + + /// + /// + /// The current value of the CalendarTrailingForeColor property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerCalendarTrailingForeColorDescr) + ] + public Color CalendarTrailingForeColor { + get { + return calendarTrailingText; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + if (!value.Equals(calendarTrailingText)) { + calendarTrailingText = value; + SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, value); + } + } + } + + /// + /// + /// The current value of the CalendarMonthBackground property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerCalendarMonthBackgroundDescr) + ] + public Color CalendarMonthBackground { + get { + return calendarMonthBackground; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + if (!value.Equals(calendarMonthBackground)) { + calendarMonthBackground = value; + SetControlColor(NativeMethods.MCSC_MONTHBK, value); + } + } + } + + /// + /// + /// Indicates whether the property has been set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Bindable(true), + SRDescription(SR.DateTimePickerCheckedDescr) + ] + public bool Checked { + get { + // the information from win32 DateTimePicker is reliable only when ShowCheckBoxes is True + if (this.ShowCheckBox && IsHandleCreated) { + NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME(); + int gdt = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_GETSYSTEMTIME, 0, sys); + return gdt == NativeMethods.GDT_VALID; + } else { + return validTime; + } + } + set { + if (this.Checked != value) { + // set the information into the win32 DateTimePicker only if ShowCheckBoxes is True + if (this.ShowCheckBox && IsHandleCreated) { + if (value) { + int gdt = NativeMethods.GDT_VALID; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + else { + int gdt = NativeMethods.GDT_NONE; + NativeMethods.SYSTEMTIME sys = null; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + } + // this.validTime is used when the DateTimePicker receives date time change notification + // from the Win32 control. this.validTime will be used to know when we transition from valid time to unvalid time + // also, validTime will be used when ShowCheckBox == false + this.validTime = value; + } + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler Click { + add { base.Click += value; } + remove { base.Click -= value; } + } + + /// + /// + /// Returns the CreateParams used to create this window. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_DATETIMEPICK; + + cp.Style |= style; + + switch (format) { + case DateTimePickerFormat.Long: + cp.Style |= NativeMethods.DTS_LONGDATEFORMAT; + break; + case DateTimePickerFormat.Short: + break; + case DateTimePickerFormat.Time: + cp.Style |= TIMEFORMAT_NOUPDOWN; + break; + case DateTimePickerFormat.Custom: + break; + } + + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for DateTimePicker explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + + return cp; + } + } + + /// + /// + /// Returns the custom format. + /// + [ + DefaultValue(null), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatBehavior), + SRDescription(SR.DateTimePickerCustomFormatDescr) + ] + public string CustomFormat { + get { + return customFormat; + } + + set { + if ((value != null && !value.Equals(customFormat)) || + (value == null && customFormat != null)) { + + customFormat = value; + + if (IsHandleCreated) { + if (format == DateTimePickerFormat.Custom) + SendMessage(NativeMethods.DTM_SETFORMAT, 0, customFormat); + } + } + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(200, PreferredHeight); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler DoubleClick { + add { base.DoubleClick += value; } + remove { base.DoubleClick -= value; } + } + + /// + /// + /// The current value of the dropDownAlign property. The calendar + /// dropDown can be aligned to the left or right of the control. + /// + [ + DefaultValue(LeftRightAlignment.Left), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.DateTimePickerDropDownAlignDescr) + ] + public LeftRightAlignment DropDownAlign { + get { + return((style & NativeMethods.DTS_RIGHTALIGN) != 0) + ? LeftRightAlignment.Right + : LeftRightAlignment.Left; + } + + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)LeftRightAlignment.Left, (int)LeftRightAlignment.Right)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(LeftRightAlignment)); + } + + SetStyleBit((value == LeftRightAlignment.Right), NativeMethods.DTS_RIGHTALIGN); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// Returns the current value of the format property. This determines the + /// style of format the date is displayed in. + /// + [ + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.DateTimePickerFormatDescr) + ] + public DateTimePickerFormat Format { + get { + return format; + } + + set { + //valid values are 0x1, 0x2,0x4,0x8. max number of bits on at a time is 1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DateTimePickerFormat.Long, (int)DateTimePickerFormat.Custom, /*maxNumberOfBitsOn*/1)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DateTimePickerFormat)); + } + + if (format != value) { + + format = value; + RecreateHandle(); + + OnFormatChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.DateTimePickerOnFormatChangedDescr)] + public event EventHandler FormatChanged { + add { + Events.AddHandler(EVENT_FORMATCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMATCHANGED, value); + } + } + + /// + /// + /// DateTimePicker Paint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + //Make sure the passed in minDate respects the current culture: this + //is especially important if the culture changes from a Gregorian or + //other calendar with a lowish minDate (see comment on MinimumDateTime) + //to ----'s calendar, which has a minimum date of 1/1/1912 (VSW 400284). + static internal DateTime EffectiveMinDate(DateTime minDate) + { + DateTime minSupportedDate = DateTimePicker.MinimumDateTime; + if (minDate < minSupportedDate) + { + return minSupportedDate; + } + return minDate; + } + + //Similarly, make sure the maxDate respects the current culture. No + //problems are anticipated here: I don't believe there are calendars + //around that have max dates on them. But if there are, we'll deal with + //them correctly. + static internal DateTime EffectiveMaxDate(DateTime maxDate) + { + DateTime maxSupportedDate = DateTimePicker.MaximumDateTime; + if (maxDate > maxSupportedDate) + { + return maxSupportedDate; + } + return maxDate; + } + + + + /// + /// + /// Indicates the maximum date and time + /// selectable in the control. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DateTimePickerMaxDateDescr) + ] + public DateTime MaxDate { + get { + return EffectiveMaxDate(max); + } + set { + if (value != max) { + if (value < EffectiveMinDate(min)) + { + throw new ArgumentOutOfRangeException("MaxDate", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxDate", FormatDateTime(value), "MinDate")); + } + + // If trying to set the maximum greater than MaxDateTime, throw. + if (value > MaximumDateTime) { + throw new ArgumentOutOfRangeException("MaxDate", SR.GetString(SR.DateTimePickerMaxDate, FormatDateTime(DateTimePicker.MaxDateTime))); + } + + max = value; + SetRange(); + + //If Value (which was once valid) is suddenly greater than the max (since we just set it) + //then adjust this... + if (Value > max) { + Value = max; + } + } + } + } + + /// + /// + /// Specifies the maximum date value. This property is read-only. + /// + public static DateTime MaximumDateTime { + get { + DateTime maxSupportedDateTime = CultureInfo.CurrentCulture.Calendar.MaxSupportedDateTime; + if (maxSupportedDateTime.Year > MaxDateTime.Year) + { + return MaxDateTime; + } + return maxSupportedDateTime; + } + } + + /// + /// + /// Indicates the minimum date and time + /// selectable in the control. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.DateTimePickerMinDateDescr) + ] + public DateTime MinDate { + get { + return EffectiveMinDate(min); + } + set + { + if (value != min) + { + if (value > EffectiveMaxDate(max)) + { + throw new ArgumentOutOfRangeException("MinDate", SR.GetString(SR.InvalidHighBoundArgument, "MinDate", FormatDateTime(value), "MaxDate")); + } + + // If trying to set the minimum less than MinimumDateTime, throw. + if (value < MinimumDateTime) + { + throw new ArgumentOutOfRangeException("MinDate", SR.GetString(SR.DateTimePickerMinDate, FormatDateTime(DateTimePicker.MinimumDateTime))); + } + + min = value; + SetRange(); + + //If Value (which was once valid) is suddenly less than the min (since we just set it) + //then adjust this... + if (Value < min) + { + Value = min; + } + } + } + } + + // We restrict the available dates to >= 1753 because of oddness in the Gregorian calendar about + // that time. We do this even for cultures that don't use the Gregorian calendar -- we're not + // really that worried about calendars for >250 years ago. + // + /// + /// + /// Specifies the minimum date value. This property is read-only. + /// + public static DateTime MinimumDateTime { + get { + DateTime minSupportedDateTime = CultureInfo.CurrentCulture.Calendar.MinSupportedDateTime; + if (minSupportedDateTime.Year < 1753) + { + return new DateTime(1753, 1, 1); + } + return minSupportedDateTime; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event MouseEventHandler MouseClick { + add { base.MouseClick += value; } + remove { base.MouseClick -= value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event MouseEventHandler MouseDoubleClick { + add { base.MouseDoubleClick += value; } + remove { base.MouseDoubleClick -= value; } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// Indicates the preferred height of the DateTimePicker control. This property is read-only. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public int PreferredHeight { + get { + if (prefHeightCache > -1) + return(int)prefHeightCache; + + // Base the preferred height on the current font + int height = FontHeight; + + // Adjust for the border + height += SystemInformation.BorderSize.Height * 4 + 3; + prefHeightCache = (short)height; + + return height; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + + + /// + /// + /// + /// Indicates whether a check box is displayed to toggle the NoValueSelected property + /// value. + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerShowNoneDescr) + ] + public bool ShowCheckBox { + get { + return(style & NativeMethods.DTS_SHOWNONE) != 0; + } + set { + SetStyleBit(value, NativeMethods.DTS_SHOWNONE); + } + } + + /// + /// + /// Indicates + /// whether an up-down control is used to adjust the time values. + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.DateTimePickerShowUpDownDescr) + ] + public bool ShowUpDown { + get { + return(style & NativeMethods.DTS_UPDOWN) != 0; + } + set { + if (ShowUpDown != value) { + SetStyleBit(value, NativeMethods.DTS_UPDOWN); + } + } + } + + /// + /// + /// Overrides Text to allow for setting of the value via a string. Also, returns + /// a formatted Value when getting the text. The DateTime class will throw + /// an exception if the string (value) being passed in is invalid. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override string Text { + get { + return base.Text; + } + set { + // Clause to check length + // + if (value == null || value.Length == 0) { + ResetValue(); + } + else { + Value = DateTime.Parse(value, CultureInfo.CurrentCulture); + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// Indicates the DateTime value assigned to the control. + /// + [ + SRCategory(SR.CatBehavior), + Bindable(true), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.DateTimePickerValueDescr) + ] + public DateTime Value { + get { + //checkbox clicked, no value set - no value set state should never occur, but just in case + if (!userHasSetValue && validTime) + return creationTime; + else + return value; + } + set { + bool valueChanged = !DateTime.Equals(this.Value, value); + // Check for value set here; if we've not set the value yet, it'll be Now, so the second + // part of the test will fail. + // So, if userHasSetValue isn't set, we don't care if the value is still the same - and we'll + // update anyway. + if (!userHasSetValue || valueChanged) { + if ((value < MinDate) || (value > MaxDate)) { + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", FormatDateTime(value), "'MinDate'", "'MaxDate'")); + } + + string oldText = this.Text; + + this.value = value; + userHasSetValue = true; + + if (IsHandleCreated) { + /* + * Make sure any changes to this code + * get propagated to createHandle + */ + int gdt = NativeMethods.GDT_VALID; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + + if (valueChanged) { + OnValueChanged(EventArgs.Empty); + } + + if (!oldText.Equals(this.Text)) { + OnTextChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// Occurs when the dropdown calendar is dismissed and disappears. + /// + [SRCategory(SR.CatAction), SRDescription(SR.DateTimePickerOnCloseUpDescr)] + public event EventHandler CloseUp { + add { + onCloseUp += value; + } + remove { + onCloseUp -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + onRightToLeftLayoutChanged += value; + } + remove { + onRightToLeftLayoutChanged -= value; + } + } + + /// + /// + /// Occurs when the value for the control changes. + /// + [SRCategory(SR.CatAction), SRDescription(SR.valueChangedEventDescr)] + public event EventHandler ValueChanged { + add { + onValueChanged += value; + } + remove { + onValueChanged -= value; + } + } + + + /// + /// + /// Occurs when the drop down calendar is shown. + /// + [SRCategory(SR.CatAction), SRDescription(SR.DateTimePickerOnDropDownDescr)] + public event EventHandler DropDown { + add { + onDropDown += value; + } + remove { + onDropDown -= value; + } + } + + /// + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new DateTimePickerAccessibleObject(this); + } + + /// + /// + /// Creates the physical window handle. + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_DATE_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + + creationTime = DateTime.Now; + + base.CreateHandle(); + + if (userHasSetValue && validTime) { + /* + * Make sure any changes to this code + * get propagated to setValue + */ + int gdt = NativeMethods.GDT_VALID; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + else if (!validTime) { + int gdt = NativeMethods.GDT_NONE; + NativeMethods.SYSTEMTIME sys = null; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + + if (format == DateTimePickerFormat.Custom) { + SendMessage(NativeMethods.DTM_SETFORMAT, 0, customFormat); + } + + UpdateUpDown(); + SetAllControlColors(); + SetControlCalendarFont(); + SetRange(); + } + + /// + /// + /// Destroys the physical window handle. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override void DestroyHandle() { + value = Value; + base.DestroyHandle(); + } + + // Return a localized string representation of the given DateTime value. + // Used for throwing exceptions, etc. + // + private static string FormatDateTime(DateTime value) { + return value.ToString("G", CultureInfo.CurrentCulture); + } + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + // Lock DateTimePicker to its preferred height. + return base.ApplyBoundsConstraints(suggestedX,suggestedY, proposedWidth, PreferredHeight); + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + int height = PreferredHeight; + int width = CommonProperties.GetSpecifiedBounds(this).Width; + return new Size(width, height); + } + + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnCloseUp(EventArgs eventargs) { + if (onCloseUp != null) onCloseUp(this, eventargs); + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnDropDown(EventArgs eventargs) { + if (onDropDown != null) { + onDropDown(this, eventargs); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnFormatChanged(EventArgs e) { + EventHandler eh = Events[EVENT_FORMATCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Add/remove SystemEvents in OnHandleCreated/Destroyed for robustness + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.MarshaledUserPreferenceChanged); + } + + /// + /// + /// Add/remove SystemEvents in OnHandleCreated/Destroyed for robustness + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.MarshaledUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnValueChanged(EventArgs eventargs) { + if (onValueChanged != null) { + onValueChanged(this, eventargs); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + if (onRightToLeftLayoutChanged != null) { + onRightToLeftLayoutChanged(this, e); + } + } + + + /// + /// + /// + /// Occurs when a property for the control changes. + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + + //clear the pref height cache + prefHeightCache = -1; + + Height = PreferredHeight; + + if (calendarFont == null) { + calendarFontHandleWrapper = null; + SetControlCalendarFont(); + } + } + + private void ResetCalendarForeColor() { + CalendarForeColor = DefaultForeColor; + } + + private void ResetCalendarFont() { + CalendarFont = null; + } + + private void ResetCalendarMonthBackground() { + CalendarMonthBackground = DefaultMonthBackColor; + } + + private void ResetCalendarTitleBackColor() { + CalendarTitleBackColor = DefaultTitleBackColor; + } + + private void ResetCalendarTitleForeColor() { + CalendarTitleBackColor = DefaultForeColor; + } + + private void ResetCalendarTrailingForeColor() { + CalendarTrailingForeColor = DefaultTrailingForeColor; + } + + /// + /// Resets the property to its default + /// value. + /// + private void ResetFormat() { + Format = DateTimePickerFormat.Long; + } + + /// + /// Resets the property to its default value. + /// + private void ResetMaxDate() { + MaxDate = MaximumDateTime; + } + + /// + /// Resets the property to its default value. + /// + private void ResetMinDate() { + MinDate = MinimumDateTime; + } + + /// + /// Resets the property to its default value. + /// + private void ResetValue() { + + // always update on reset with ShowNone = false -- as it'll take the current time. + this.value = DateTime.Now; + + // If ShowCheckBox = true, then userHasSetValue can be false (null value). + // otherwise, userHasSetValue is valid... + // userHasSetValue = !ShowCheckBox; + + // After ResetValue() the flag indicating whether the user + // has set the value should be false. + userHasSetValue = false; + + // Update the text displayed in the DateTimePicker + if (IsHandleCreated) { + int gdt = NativeMethods.GDT_VALID; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + } + + // Updating Checked to false will set the control to "no date", + // and clear its checkbox. + Checked = false; + + OnValueChanged(EventArgs.Empty); + OnTextChanged(EventArgs.Empty); + } + + /// + /// If the handle has been created, this applies the color to the control + /// + /// + private void SetControlColor(int colorIndex, Color value) { + if (IsHandleCreated) { + SendMessage(NativeMethods.DTM_SETMCCOLOR, colorIndex, ColorTranslator.ToWin32(value)); + } + } + + /// + /// If the handle has been created, this applies the font to the control. + /// + /// + private void SetControlCalendarFont() { + if (IsHandleCreated) { + SendMessage(NativeMethods.DTM_SETMCFONT, CalendarFontHandle, NativeMethods.InvalidIntPtr); + } + } + + /// + /// Applies all the colors to the control. + /// + /// + private void SetAllControlColors() { + SetControlColor(NativeMethods.MCSC_MONTHBK, calendarMonthBackground); + SetControlColor(NativeMethods.MCSC_TEXT, calendarForeColor); + SetControlColor(NativeMethods.MCSC_TITLEBK, calendarTitleBackColor); + SetControlColor(NativeMethods.MCSC_TITLETEXT, calendarTitleForeColor); + SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, calendarTrailingText); + } + + /// + /// Updates the window handle with the min/max ranges if it has been + /// created. + /// + /// + private void SetRange() { + SetRange(EffectiveMinDate(min), EffectiveMaxDate(max)); + } + + private void SetRange(DateTime min, DateTime max) { + if (IsHandleCreated) { + int flags = 0; + + NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); + + flags |= NativeMethods.GDTR_MIN | NativeMethods.GDTR_MAX; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(min); + sa.wYear1 = sys.wYear; + sa.wMonth1 = sys.wMonth; + sa.wDayOfWeek1 = sys.wDayOfWeek; + sa.wDay1 = sys.wDay; + sa.wHour1 = sys.wHour; + sa.wMinute1 = sys.wMinute; + sa.wSecond1 = sys.wSecond; + sa.wMilliseconds1 = sys.wMilliseconds; + sys = DateTimePicker.DateTimeToSysTime(max); + sa.wYear2 = sys.wYear; + sa.wMonth2 = sys.wMonth; + sa.wDayOfWeek2 = sys.wDayOfWeek; + sa.wDay2 = sys.wDay; + sa.wHour2 = sys.wHour; + sa.wMinute2 = sys.wMinute; + sa.wSecond2 = sys.wSecond; + sa.wMilliseconds2 = sys.wMilliseconds; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETRANGE, flags, sa); + } + } + + /// + /// Turns on or off a given style bit + /// + /// + private void SetStyleBit(bool flag, int bit) { + if (((style & bit) != 0) == flag) return; + + if (flag) { + style |= bit; + } + else { + style &= ~bit; + } + + if (IsHandleCreated) { + RecreateHandle(); + Invalidate(); + Update(); + } + } + + /// + /// Determines if the property needs to be + /// persisted. + /// + private bool ShouldSerializeCalendarForeColor() { + return !CalendarForeColor.Equals(DefaultForeColor); + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeCalendarFont() { + return calendarFont != null; + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeCalendarTitleBackColor() { + return !calendarTitleBackColor.Equals(DefaultTitleBackColor); + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeCalendarTitleForeColor() { + return !calendarTitleForeColor.Equals(DefaultTitleForeColor); + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeCalendarTrailingForeColor() { + return !calendarTrailingText.Equals(DefaultTrailingForeColor); + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeCalendarMonthBackground() { + return !calendarMonthBackground.Equals(DefaultMonthBackColor); + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeMaxDate() { + return max != MaximumDateTime && max != DateTime.MaxValue; + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeMinDate() { + return min != MinimumDateTime && min != DateTime.MinValue; + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeValue() { + return userHasSetValue; + } + + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeFormat() { + return(Format != DateTimePickerFormat.Long); + } + + /// + /// + /// Returns the control as a string + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Value: " + FormatDateTime(Value); + } + + /// + /// Forces a repaint of the updown control if it is displayed. + /// + /// + private void UpdateUpDown() { + // The upDown control doesn't repaint correctly. + // + if (ShowUpDown) { + EnumChildren c = new EnumChildren(); + NativeMethods.EnumChildrenCallback cb = new NativeMethods.EnumChildrenCallback(c.enumChildren); + UnsafeNativeMethods.EnumChildWindows(new HandleRef(this, Handle), cb, NativeMethods.NullHandleRef); + if (c.hwndFound != IntPtr.Zero) { + SafeNativeMethods.InvalidateRect(new HandleRef(c, c.hwndFound), null, true); + SafeNativeMethods.UpdateWindow(new HandleRef(c, c.hwndFound)); + } + } + } + + private void MarshaledUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + try { + //use begininvoke instead of invoke in case the destination thread is not processing messages. + BeginInvoke(new UserPreferenceChangedEventHandler(this.UserPreferenceChanged), new object[] { sender, pref }); + } + catch (InvalidOperationException) { } //if the destination thread does not exist, don't send. + } + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + if (pref.Category == UserPreferenceCategory.Locale) { + // We need to recreate the monthcalendar handle when the locale changes, because + // the day names etc. are only updated on a handle recreate (comctl32 limitation). + // + RecreateHandle(); + } + } + + /// + /// Handles the DTN_CLOSEUP notification + /// + /// + private void WmCloseUp(ref Message m) { + OnCloseUp(EventArgs.Empty); + } + + /// + /// Handles the DTN_DATETIMECHANGE notification + /// + /// + private void WmDateTimeChange(ref Message m) { + NativeMethods.NMDATETIMECHANGE nmdtc = (NativeMethods.NMDATETIMECHANGE)m.GetLParam(typeof(NativeMethods.NMDATETIMECHANGE)); + DateTime temp = value; + bool oldvalid = validTime; + if (nmdtc.dwFlags != NativeMethods.GDT_NONE) { + validTime = true; + value = DateTimePicker.SysTimeToDateTime(nmdtc.st); + userHasSetValue = true; + } + else { + validTime = false; + } + if (value!=temp || oldvalid != validTime) { + OnValueChanged(EventArgs.Empty); + OnTextChanged(EventArgs.Empty); + } + } + + /// + /// Handles the DTN_DROPDOWN notification + /// + /// + private void WmDropDown(ref Message m) { + + if (this.RightToLeftLayout == true && this.RightToLeft == RightToLeft.Yes) { + IntPtr handle = SendMessage(NativeMethods.DTM_GETMONTHCAL, 0,0); + if (handle != IntPtr.Zero) { + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_EXSTYLE))); + style |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + style &= ~(NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_RTLREADING); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_EXSTYLE, new HandleRef(this, (IntPtr)style)); + } + } + OnDropDown(EventArgs.Empty); + } + + /// + /// + /// Handles system color changes + /// + /// + protected override void OnSystemColorsChanged(EventArgs e) { + SetAllControlColors(); + base.OnSystemColorsChanged(e); + } + + /// + /// Handles the WM_COMMAND messages reflected from the parent control. + /// + /// + private void WmReflectCommand(ref Message m) { + if (m.HWnd == Handle) { + + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (nmhdr.code) { + case NativeMethods.DTN_CLOSEUP: + WmCloseUp(ref m); + break; + case NativeMethods.DTN_DATETIMECHANGE: + WmDateTimeChange(ref m); + break; + case NativeMethods.DTN_DROPDOWN: + WmDropDown(ref m); + break; + } + } + } + + /// + /// + /// Overrided wndProc + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_LBUTTONDOWN: + FocusInternal(); + if (!ValidationCancelled) { + base.WndProc(ref m); + } + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + WmReflectCommand(ref m); + base.WndProc(ref m); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + base.WndProc(ref m); + UpdateUpDown(); + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + /// Takes a DateTime value and returns a SYSTEMTIME struct + /// Note: 1 second granularity + /// + internal static NativeMethods.SYSTEMTIME DateTimeToSysTime(DateTime time) { + NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME(); + sys.wYear = (short)time.Year; + sys.wMonth = (short)time.Month; + sys.wDayOfWeek = (short)time.DayOfWeek; + sys.wDay = (short)time.Day; + sys.wHour = (short)time.Hour; + sys.wMinute = (short)time.Minute; + sys.wSecond = (short)time.Second; + sys.wMilliseconds = 0; + return sys; + } + + /// + /// Takes a SYSTEMTIME struct and returns a DateTime value + /// Note: 1 second granularity. + /// + internal static DateTime SysTimeToDateTime(NativeMethods.SYSTEMTIME s) { + return new DateTime(s.wYear, s.wMonth, s.wDay, s.wHour, s.wMinute, s.wSecond); + } + + /// + /// + private sealed class EnumChildren { + public IntPtr hwndFound = IntPtr.Zero; + + public bool enumChildren(IntPtr hwnd, IntPtr lparam) { + hwndFound = hwnd; + return true; + } + } + + /// + /// + /// + /// + [ComVisible(true)] + public class DateTimePickerAccessibleObject : ControlAccessibleObject { + + /// + public DateTimePickerAccessibleObject(DateTimePicker owner) : base(owner) { + } + + /// + public override string KeyboardShortcut { + get { + // APP COMPAT. When computing DateTimePickerAccessibleObject::get_KeyboardShorcut the previous label + // takes precedence over DTP::Text. + // This code was copied from the Everett sources. + // vsw 541672. + Label previousLabel = this.PreviousLabel; + + if (previousLabel != null) { + char previousLabelMnemonic = WindowsFormsUtils.GetMnemonic(previousLabel.Text, false /*convertToUpperCase*/); + if (previousLabelMnemonic != (char) 0) { + return "Alt+" + previousLabelMnemonic; + } + } + + string baseShortcut = base.KeyboardShortcut; + + if ((baseShortcut == null || baseShortcut.Length == 0)) { + char ownerTextMnemonic = WindowsFormsUtils.GetMnemonic(this.Owner.Text, false /*convertToUpperCase*/); + if (ownerTextMnemonic != (char) 0) { + return "Alt+" + ownerTextMnemonic; + } + } + + return baseShortcut; + } + } + + /// + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + string baseValue = base.Value; + if (baseValue == null || baseValue.Length == 0) { + return Owner.Text; + } + return baseValue; + } + } + + /// + public override AccessibleStates State { + get { + AccessibleStates state = base.State; + + if(((DateTimePicker)Owner).ShowCheckBox && + ((DateTimePicker)Owner).Checked) { + state |= AccessibleStates.Checked; + } + + return state; + } + } + + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibilityImprovements.Level3 ? AccessibleRole.ComboBox : AccessibleRole.DropList; + } + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_IsTogglePatternAvailablePropertyId: + return IsPatternSupported(NativeMethods.UIA_TogglePatternId); + case NativeMethods.UIA_LocalizedControlTypePropertyId: + return DateTimePickerLocalizedControlTypeString; + default: + return base.GetPropertyValue(propertyID); + } + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_TogglePatternId && ((DateTimePicker)Owner).ShowCheckBox) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + #region Toggle Pattern + + internal override UnsafeNativeMethods.ToggleState ToggleState { + get { + return ((DateTimePicker)Owner).Checked ? + UnsafeNativeMethods.ToggleState.ToggleState_On : + UnsafeNativeMethods.ToggleState.ToggleState_Off; + } + } + + internal override void Toggle() { + ((DateTimePicker)Owner).Checked = !((DateTimePicker)Owner).Checked; + } + + #endregion + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/DateTimePickerFormats.cs b/WindowsForms/Managed/System/WinForms/DateTimePickerFormats.cs new file mode 100644 index 000000000..de6fde34a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DateTimePickerFormats.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Constants that specify how the date and time picker control displays + /// date and time information. + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum DateTimePickerFormat { + + /// + /// + /// Long format - produces output in the form "Wednesday, April 7, 1999" + /// + Long = 0x0001, + + /// + /// + /// Short format - produces output in the form "4/7/99" + /// + Short = 0x0002, + + /// + /// + /// Time format - produces output in time format + /// + Time = 0x0004, + + /// + /// + /// Custom format - produces output in custom format. + /// + Custom = 0x0008, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Day.cs b/WindowsForms/Managed/System/WinForms/Day.cs new file mode 100644 index 000000000..398a95aee --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Day.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the day of the week. + /// + /// + /// + public enum Day { + /// + /// + /// + /// The day Monday. + /// + /// + Monday = 0, + /// + /// + /// + /// The day Tuesday. + /// + /// + Tuesday = 1, + /// + /// + /// + /// The day Wednesday. + /// + /// + Wednesday = 2, + /// + /// + /// + /// The day Thursday. + /// + /// + Thursday = 3, + /// + /// + /// + /// The day Friday. + /// + /// + Friday = 4, + /// + /// + /// + /// The day Saturday. + /// + /// + Saturday = 5, + /// + /// + /// + /// The day Sunday. + /// + /// + Sunday = 6, + /// + /// + /// + /// A default day of the week specified by the application. + /// + /// + Default = 7, + } +} diff --git a/WindowsForms/Managed/System/WinForms/Design/ComponentEditorForm.cs b/WindowsForms/Managed/System/WinForms/Design/ComponentEditorForm.cs new file mode 100644 index 000000000..782532199 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/ComponentEditorForm.cs @@ -0,0 +1,862 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design { + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Windows.Forms.Internal; + using System.Drawing; + using System.Reflection; + using System.ComponentModel.Design; + using System.Windows.Forms.ComponentModel; + using Microsoft.Win32; + using Message = System.Windows.Forms.Message; + + /// + /// + /// Provides a user interface for . + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch) + ] + [ToolboxItem(false)] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class ComponentEditorForm : Form { + private IComponent component; + private Type[] pageTypes; + private ComponentEditorPageSite[] pageSites; + private Size maxSize = System.Drawing.Size.Empty; + private int initialActivePage; + private int activePage; + private bool dirty; + private bool firstActivate; + + private Panel pageHost = new Panel(); + private PageSelector selector; + private ImageList selectorImageList; + private Button okButton; + private Button cancelButton; + private Button applyButton; + private Button helpButton; + + // private DesignerTransaction transaction; + + private const int BUTTON_WIDTH = 80; + private const int BUTTON_HEIGHT = 23; + private const int BUTTON_PAD = 6; + private const int MIN_SELECTOR_WIDTH = 90; + private const int SELECTOR_PADDING = 10; + private const int STRIP_HEIGHT = 4; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public ComponentEditorForm(object component, Type[] pageTypes) : base() { + + if (!(component is IComponent)) { + throw new ArgumentException(SR.GetString(SR.ComponentEditorFormBadComponent),"component"); + } + this.component = (IComponent)component; + this.pageTypes = pageTypes; + dirty = false; + firstActivate = true; + activePage = -1; + initialActivePage = 0; + + FormBorderStyle = FormBorderStyle.FixedDialog; + MinimizeBox = false; + MaximizeBox = false; + ShowInTaskbar = false; + Icon = null; + StartPosition = FormStartPosition.CenterParent; + + OnNewObjects(); + OnConfigureUI(); + } + + /// + /// + /// Applies any changes in the set of ComponentPageControl to the actual component. + /// + /// + internal virtual void ApplyChanges(bool lastApply) { + if (dirty) { + IComponentChangeService changeService = null; + + if (component.Site != null) { + changeService = (IComponentChangeService)component.Site.GetService(typeof(IComponentChangeService)); + if (changeService != null) { + try { + changeService.OnComponentChanging(component, null); + } + catch (CheckoutException e) { + if (e == CheckoutException.Canceled) { + return; + } + throw e; + } + } + } + + for (int n = 0; n < pageSites.Length; n++) { + if (pageSites[n].Dirty) { + pageSites[n].GetPageControl().ApplyChanges(); + pageSites[n].Dirty = false; + } + } + + if (changeService != null) { + changeService.OnComponentChanged(component, null, null, null); + } + + applyButton.Enabled = false; + cancelButton.Text = SR.GetString(SR.CloseCaption); + dirty = false; + + if (lastApply == false) { + for (int n = 0; n < pageSites.Length; n++) { + pageSites[n].GetPageControl().OnApplyComplete(); + } + } + + /* + if (transaction != null) { + transaction.Commit(); + CreateNewTransaction(); + } + */ + } + } + + /// + /// + /// + /// Hide the property + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + /* + private void CreateNewTransaction() { + IDesignerHost host = component.Site.GetService(typeof(IDesignerHost)) as IDesignerHost; + transaction = host.CreateTransaction(SR.GetString(SR.ComponentEditorFormEditTransaction, component.Site.Name)); + } + */ + + /// + /// + /// Handles ok/cancel/apply/help button click events + /// + /// + private void OnButtonClick(object sender, EventArgs e) { + if (sender == okButton) { + ApplyChanges(true); + DialogResult = DialogResult.OK; + } + else if (sender == cancelButton) { + DialogResult = DialogResult.Cancel; + } + else if (sender == applyButton) { + ApplyChanges(false); + } + else if (sender == helpButton) { + ShowPageHelp(); + } + } + + /// + /// + /// Lays out the UI of the form. + /// + /// + private void OnConfigureUI() { + Font uiFont = Control.DefaultFont; + if (component.Site != null) { + IUIService uiService = (IUIService)component.Site.GetService(typeof(IUIService)); + if (uiService != null) { + uiFont = (Font)uiService.Styles["DialogFont"]; + } + } + + this.Font = uiFont; + + okButton = new Button(); + cancelButton = new Button(); + applyButton = new Button(); + helpButton = new Button(); + + selectorImageList = new ImageList(); + selectorImageList.ImageSize = new Size(16, 16); + selector = new PageSelector(); + + selector.ImageList = selectorImageList; + selector.AfterSelect += new TreeViewEventHandler(this.OnSelChangeSelector); + + Label grayStrip = new Label(); + grayStrip.BackColor = SystemColors.ControlDark; + + int selectorWidth = MIN_SELECTOR_WIDTH; + + if (pageSites != null) { + // Add the nodes corresponding to the pages + for (int n = 0; n < pageSites.Length; n++) { + ComponentEditorPage page = pageSites[n].GetPageControl(); + + string title = page.Title; + Graphics graphics = CreateGraphicsInternal(); + int titleWidth = (int) graphics.MeasureString(title, Font).Width; + graphics.Dispose(); + selectorImageList.Images.Add(page.Icon.ToBitmap()); + + selector.Nodes.Add(new TreeNode(title, n, n)); + if (titleWidth > selectorWidth) + selectorWidth = titleWidth; + } + } + selectorWidth += SELECTOR_PADDING; + + string caption = String.Empty; + ISite site = component.Site; + if (site != null) { + caption = SR.GetString(SR.ComponentEditorFormProperties, site.Name); + } + else { + caption = SR.GetString(SR.ComponentEditorFormPropertiesNoName); + } + this.Text = caption; + + + Rectangle pageHostBounds = new Rectangle(2 * BUTTON_PAD + selectorWidth, 2 * BUTTON_PAD + STRIP_HEIGHT, + maxSize.Width, maxSize.Height); + pageHost.Bounds = pageHostBounds; + grayStrip.Bounds = new Rectangle(pageHostBounds.X, BUTTON_PAD, + pageHostBounds.Width, STRIP_HEIGHT); + + if (pageSites != null) { + Rectangle pageBounds = new Rectangle(0, 0, pageHostBounds.Width, pageHostBounds.Height); + for (int n = 0; n < pageSites.Length; n++) { + ComponentEditorPage page = pageSites[n].GetPageControl(); + page.GetControl().Bounds = pageBounds; + } + } + + int xFrame = SystemInformation.FixedFrameBorderSize.Width; + Rectangle bounds = pageHostBounds; + Size size = new Size(bounds.Width + 3 * (BUTTON_PAD + xFrame) + selectorWidth, + bounds.Height + STRIP_HEIGHT + 4 * BUTTON_PAD + BUTTON_HEIGHT + + 2 * xFrame + SystemInformation.CaptionHeight); + this.Size = size; + + selector.Bounds = new Rectangle(BUTTON_PAD, BUTTON_PAD, + selectorWidth, bounds.Height + STRIP_HEIGHT + 2 * BUTTON_PAD + BUTTON_HEIGHT); + + bounds.X = bounds.Width + bounds.X - BUTTON_WIDTH; + bounds.Y = bounds.Height + bounds.Y + BUTTON_PAD; + bounds.Width = BUTTON_WIDTH; + bounds.Height = BUTTON_HEIGHT; + + helpButton.Bounds = bounds; + helpButton.Text = SR.GetString(SR.HelpCaption); + helpButton.Click += new EventHandler(this.OnButtonClick); + helpButton.Enabled = false; + helpButton.FlatStyle = FlatStyle.System; + + bounds.X -= (BUTTON_WIDTH + BUTTON_PAD); + applyButton.Bounds = bounds; + applyButton.Text = SR.GetString(SR.ApplyCaption); + applyButton.Click += new EventHandler(this.OnButtonClick); + applyButton.Enabled = false; + applyButton.FlatStyle = FlatStyle.System; + + bounds.X -= (BUTTON_WIDTH + BUTTON_PAD); + cancelButton.Bounds = bounds; + cancelButton.Text = SR.GetString(SR.CancelCaption); + cancelButton.Click += new EventHandler(this.OnButtonClick); + cancelButton.FlatStyle = FlatStyle.System; + this.CancelButton = cancelButton; + + bounds.X -= (BUTTON_WIDTH + BUTTON_PAD); + okButton.Bounds = bounds; + okButton.Text = SR.GetString(SR.OKCaption); + okButton.Click += new EventHandler(this.OnButtonClick); + okButton.FlatStyle = FlatStyle.System; + this.AcceptButton = okButton; + + this.Controls.Clear(); + this.Controls.AddRange(new Control[] { + selector, + grayStrip, + pageHost, + okButton, + cancelButton, + applyButton, + helpButton + }); + + #pragma warning disable 618 + // continuing with the old autoscale base size stuff, it works, + // and is currently set to a non-standard height + AutoScaleBaseSize = new Size(5, 14); + ApplyAutoScaling(); + #pragma warning restore 618 + + + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnActivated(EventArgs e) { + base.OnActivated(e); + + if (firstActivate) { + firstActivate = false; + + selector.SelectedNode = selector.Nodes[initialActivePage]; + pageSites[initialActivePage].Active = true; + activePage = initialActivePage; + + helpButton.Enabled = pageSites[activePage].GetPageControl().SupportsHelp(); + } + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnHelpRequested(HelpEventArgs e) { + base.OnHelpRequested(e); + ShowPageHelp(); + } + + /// + /// + /// Called to initialize this form with the new component. + /// + /// + private void OnNewObjects() { + pageSites = null; + maxSize = new Size(3 * (BUTTON_WIDTH + BUTTON_PAD), 24 * pageTypes.Length); + + pageSites = new ComponentEditorPageSite[pageTypes.Length]; + + // create sites for them + // + for (int n = 0; n < pageTypes.Length; n++) { + pageSites[n] = new ComponentEditorPageSite(pageHost, pageTypes[n], component, this); + ComponentEditorPage page = pageSites[n].GetPageControl(); + + Size pageSize = page.Size; + if (pageSize.Width > maxSize.Width) + maxSize.Width = pageSize.Width; + if (pageSize.Height > maxSize.Height) + maxSize.Height = pageSize.Height; + } + + // and set them all to an ideal size + // + for (int n = 0; n < pageSites.Length; n++) { + pageSites[n].GetPageControl().Size = maxSize; + } + } + + /// + /// + /// Handles switching between pages. + /// + /// + protected virtual void OnSelChangeSelector(object source, TreeViewEventArgs e) { + if (firstActivate == true) { + // treeview seems to fire a change event when it is first setup before + // the form is activated + return; + } + + int newPage = selector.SelectedNode.Index; + Debug.Assert((newPage >= 0) && (newPage < pageSites.Length), + "Invalid page selected"); + + if (newPage == activePage) + return; + + if (activePage != -1) { + if (pageSites[activePage].AutoCommit) + ApplyChanges(false); + pageSites[activePage].Active = false; + } + + activePage = newPage; + pageSites[activePage].Active = true; + helpButton.Enabled = pageSites[activePage].GetPageControl().SupportsHelp(); + } + + /// + /// + /// Provides a method to override in order to pre-process input messages before + /// they are dispatched. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public override bool PreProcessMessage(ref Message msg) { + if (null != pageSites && pageSites[activePage].GetPageControl().IsPageMessage(ref msg)) + return true; + + return base.PreProcessMessage(ref msg); + } + + /// + /// + /// Sets the controls of the form to dirty. This enables the "apply" + /// button. + /// + internal virtual void SetDirty() { + dirty = true; + applyButton.Enabled = true; + cancelButton.Text = SR.GetString(SR.CancelCaption); + } + + /// + /// + /// Shows the form. The form will have no owner window. + /// + public virtual DialogResult ShowForm() { + return ShowForm(null, 0); + } + + /// + /// + /// Shows the form and the specified page. The form will have no owner window. + /// + public virtual DialogResult ShowForm(int page) { + return ShowForm(null, page); + } + + /// + /// + /// Shows the form with the specified owner. + /// + public virtual DialogResult ShowForm(IWin32Window owner) { + return ShowForm(owner, 0); + } + + /// + /// + /// Shows the form and the specified page with the specified owner. + /// + public virtual DialogResult ShowForm(IWin32Window owner, int page) { + initialActivePage = page; + + // CreateNewTransaction(); + try { + ShowDialog(owner); + } + finally { + /* + if (DialogResult == DialogResult.OK) { + transaction.Commit(); + } + else + { + transaction.Cancel(); + } + */ + } + + return DialogResult; + } + + /// + /// + /// Shows help for the active page. + /// + /// + private void ShowPageHelp() { + Debug.Assert(activePage != -1); + + if (pageSites[activePage].GetPageControl().SupportsHelp()) { + pageSites[activePage].GetPageControl().ShowHelp(); + } + } + + /// + /// + /// Implements a standard version of ComponentEditorPageSite for use within a + /// ComponentEditorForm. + /// + /// + private sealed class ComponentEditorPageSite : IComponentEditorPageSite { + internal IComponent component; + internal ComponentEditorPage pageControl; + internal Control parent; + internal bool isActive; + internal bool isDirty; + private ComponentEditorForm form; + + /// + /// + /// Creates the page site. + /// + /// + internal ComponentEditorPageSite(Control parent, Type pageClass, IComponent component, ComponentEditorForm form) { + this.component = component; + this.parent = parent; + this.isActive = false; + this.isDirty = false; + + if (form == null) + throw new ArgumentNullException("form"); + + this.form = form; + + try { + pageControl = (ComponentEditorPage)SecurityUtils.SecureCreateInstance(pageClass); + } + catch (TargetInvocationException e) { + Debug.Fail(e.ToString()); + throw new TargetInvocationException(SR.GetString(SR.ExceptionCreatingCompEditorControl, e.ToString()), e.InnerException); + } + + pageControl.SetSite(this); + pageControl.SetComponent(component); + } + + /// + /// + /// Called by the ComponentEditorForm to activate / deactivate the page. + /// + /// + internal bool Active { + set { + if (value) { + // make sure the page has been created + pageControl.CreateControl(); + + // activate it and give it focus + pageControl.Activate(); + } + else { + pageControl.Deactivate(); + } + isActive = value; + } + } + + internal bool AutoCommit { + get { + return pageControl.CommitOnDeactivate; + } + } + + internal bool Dirty { + get { + return isDirty; + } + set { + isDirty = value; + } + } + + /// + /// + /// Called by a page to return a parenting control for itself. + /// + /// + public Control GetControl() { + return parent; + } + + /// + /// + /// Called by the ComponentEditorForm to get the actual page. + /// + /// + internal ComponentEditorPage GetPageControl() { + return pageControl; + } + + /// + /// + /// Called by a page to mark it's contents as dirty. + /// + /// + public void SetDirty() { + if (isActive) + Dirty = true; + form.SetDirty(); + } + } + + /// + /// + /// + /// + // + + internal sealed class PageSelector : TreeView { + private const int PADDING_VERT = 3; + private const int PADDING_HORZ = 4; + + private const int SIZE_ICON_X = 16; + private const int SIZE_ICON_Y = 16; + + private const int STATE_NORMAL = 0; + private const int STATE_SELECTED = 1; + private const int STATE_HOT = 2; + + private IntPtr hbrushDither; + + + public PageSelector() { + this.HotTracking = true; + this.HideSelection = false; + this.BackColor = SystemColors.Control; + this.Indent = 0; + this.LabelEdit = false; + this.Scrollable = false; + this.ShowLines = false; + this.ShowPlusMinus = false; + this.ShowRootLines = false; + this.BorderStyle = BorderStyle.None; + this.Indent = 0; + this.FullRowSelect = true; + } + + + + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + cp.ExStyle |= NativeMethods.WS_EX_STATICEDGE; + return cp; + } + } + + private void CreateDitherBrush() { + Debug.Assert(hbrushDither == IntPtr.Zero, "Brush should not be recreated."); + + short[] patternBits = new short[] { + unchecked((short)0xAAAA), unchecked((short)0x5555), unchecked((short)0xAAAA), unchecked((short)0x5555), + unchecked((short)0xAAAA), unchecked((short)0x5555), unchecked((short)0xAAAA), unchecked((short)0x5555) + }; + + IntPtr hbitmapTemp = SafeNativeMethods.CreateBitmap(8, 8, 1, 1, patternBits); + Debug.Assert(hbitmapTemp != IntPtr.Zero, + "could not create dither bitmap. Page selector UI will not be correct"); + + if (hbitmapTemp != IntPtr.Zero) { + hbrushDither = SafeNativeMethods.CreatePatternBrush(new HandleRef(null, hbitmapTemp)); + + Debug.Assert(hbrushDither != IntPtr.Zero, + "Unable to created dithered brush. Page selector UI will not be correct"); + + SafeNativeMethods.DeleteObject(new HandleRef(null, hbitmapTemp)); + } + } + + private void DrawTreeItem(string itemText, int imageIndex, IntPtr dc, NativeMethods.RECT rcIn, + int state, int backColor, int textColor) { + IntNativeMethods.SIZE size = new IntNativeMethods.SIZE(); + IntNativeMethods.RECT rc2 = new IntNativeMethods.RECT(); + IntNativeMethods.RECT rc = new IntNativeMethods.RECT(rcIn.left, rcIn.top, rcIn.right, rcIn.bottom); + ImageList imagelist = this.ImageList; + IntPtr hfontOld = IntPtr.Zero; + + // Select the font of the dialog, so we don't get the underlined font + // when the item is being tracked + if ((state & STATE_HOT) != 0) + hfontOld = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(Parent, ((Control)Parent).FontHandle)); + + // Fill the background + if (((state & STATE_SELECTED) != 0) && (hbrushDither != IntPtr.Zero)) { + FillRectDither(dc, rcIn); + SafeNativeMethods.SetBkMode(new HandleRef(null, dc), NativeMethods.TRANSPARENT); + } + else { + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), backColor); + IntUnsafeNativeMethods.ExtTextOut(new HandleRef(null, dc), 0, 0, NativeMethods.ETO_CLIPPED | NativeMethods.ETO_OPAQUE, ref rc, null, 0, null); + } + + // Get the height of the font + IntUnsafeNativeMethods.GetTextExtentPoint32(new HandleRef(null, dc), itemText, size); + + // Draw the caption + rc2.left = rc.left + SIZE_ICON_X + 2 * PADDING_HORZ; + rc2.top = rc.top + (((rc.bottom - rc.top) - size.cy) >> 1); + rc2.bottom = rc2.top + size.cy; + rc2.right = rc.right; + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), textColor); + IntUnsafeNativeMethods.DrawText(new HandleRef(null, dc), itemText, ref rc2, + IntNativeMethods.DT_LEFT | IntNativeMethods.DT_VCENTER | IntNativeMethods.DT_END_ELLIPSIS | IntNativeMethods.DT_NOPREFIX); + + SafeNativeMethods.ImageList_Draw(new HandleRef(imagelist, imagelist.Handle), imageIndex, new HandleRef(null, dc), + PADDING_HORZ, rc.top + (((rc.bottom - rc.top) - SIZE_ICON_Y) >> 1), + NativeMethods.ILD_TRANSPARENT); + + // Draw the hot-tracking border if needed + if ((state & STATE_HOT) != 0) { + int savedColor; + + // top left + savedColor = SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(SystemColors.ControlLightLight)); + rc2.left = rc.left; + rc2.top = rc.top; + rc2.bottom = rc.top + 1; + rc2.right = rc.right; + IntUnsafeNativeMethods.ExtTextOut(new HandleRef(null, dc), 0, 0, NativeMethods.ETO_OPAQUE, ref rc2, null, 0, null); + rc2.bottom = rc.bottom; + rc2.right = rc.left + 1; + IntUnsafeNativeMethods.ExtTextOut(new HandleRef(null, dc), 0, 0, NativeMethods.ETO_OPAQUE, ref rc2, null, 0, null); + + // bottom right + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(SystemColors.ControlDark)); + rc2.left = rc.left; + rc2.right = rc.right; + rc2.top = rc.bottom - 1; + rc2.bottom = rc.bottom; + IntUnsafeNativeMethods.ExtTextOut(new HandleRef(null, dc), 0, 0, NativeMethods.ETO_OPAQUE, ref rc2, null, 0, null); + rc2.left = rc.right - 1; + rc2.top = rc.top; + IntUnsafeNativeMethods.ExtTextOut(new HandleRef(null, dc), 0, 0, NativeMethods.ETO_OPAQUE, ref rc2, null, 0, null); + + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), savedColor); + } + + if (hfontOld != IntPtr.Zero) + SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(null, hfontOld)); + } + + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + int itemHeight; + + itemHeight = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_GETITEMHEIGHT, 0, 0); + itemHeight += 2 * PADDING_VERT; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_SETITEMHEIGHT, itemHeight, 0); + + if (hbrushDither == IntPtr.Zero) { + CreateDitherBrush(); + } + } + + private void OnCustomDraw(ref Message m) { + NativeMethods.NMTVCUSTOMDRAW nmtvcd = (NativeMethods.NMTVCUSTOMDRAW)m.GetLParam(typeof(NativeMethods.NMTVCUSTOMDRAW)); + + switch (nmtvcd.nmcd.dwDrawStage) { + case NativeMethods.CDDS_PREPAINT: + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYITEMDRAW | NativeMethods.CDRF_NOTIFYPOSTPAINT); + break; + case NativeMethods.CDDS_ITEMPREPAINT: + { + TreeNode itemNode = TreeNode.FromHandle(this, (IntPtr)nmtvcd.nmcd.dwItemSpec); + if (itemNode != null) { + int state = STATE_NORMAL; + int itemState = nmtvcd.nmcd.uItemState; + + if (((itemState & NativeMethods.CDIS_HOT) != 0) || + ((itemState & NativeMethods.CDIS_FOCUS) != 0)) + state |= STATE_HOT; + if ((itemState & NativeMethods.CDIS_SELECTED) != 0) + state |= STATE_SELECTED; + + DrawTreeItem(itemNode.Text, itemNode.ImageIndex, + nmtvcd.nmcd.hdc, nmtvcd.nmcd.rc, + state, ColorTranslator.ToWin32(SystemColors.Control), ColorTranslator.ToWin32(SystemColors.ControlText)); + } + m.Result = (IntPtr)NativeMethods.CDRF_SKIPDEFAULT; + + } + break; + case NativeMethods.CDDS_POSTPAINT: + m.Result = (IntPtr)NativeMethods.CDRF_SKIPDEFAULT; + break; + default: + m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; + break; + } + } + + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + + if (!RecreatingHandle && (hbrushDither != IntPtr.Zero)) { + SafeNativeMethods.DeleteObject(new HandleRef(this, hbrushDither)); + hbrushDither = IntPtr.Zero; + } + } + + private void FillRectDither(IntPtr dc, NativeMethods.RECT rc) { + IntPtr hbrushOld = SafeNativeMethods.SelectObject(new HandleRef(null, dc), new HandleRef(this, hbrushDither)); + + if (hbrushOld != IntPtr.Zero) { + int oldTextColor, oldBackColor; + + oldTextColor = SafeNativeMethods.SetTextColor(new HandleRef(null, dc), ColorTranslator.ToWin32(SystemColors.ControlLightLight)); + oldBackColor = SafeNativeMethods.SetBkColor(new HandleRef(null, dc), ColorTranslator.ToWin32(SystemColors.Control)); + + SafeNativeMethods.PatBlt(new HandleRef(null, dc), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NativeMethods.PATCOPY); + SafeNativeMethods.SetTextColor(new HandleRef(null, dc), oldTextColor); + SafeNativeMethods.SetBkColor(new HandleRef(null, dc), oldBackColor); + } + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + if (m.Msg == NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY) { + NativeMethods.NMHDR nmh = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + if (nmh.code == NativeMethods.NM_CUSTOMDRAW) { + OnCustomDraw(ref m); + return; + } + } + + base.WndProc(ref m); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Design/ComponentEditorPage.cs b/WindowsForms/Managed/System/WinForms/Design/ComponentEditorPage.cs new file mode 100644 index 000000000..f47a45b55 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/ComponentEditorPage.cs @@ -0,0 +1,360 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms.Design { + using System.Runtime.Remoting; + using System.ComponentModel; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Drawing; + using System.Windows.Forms; + using System.Windows.Forms.ComponentModel; + using System.ComponentModel.Design; + using Microsoft.Win32; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + + /// + /// + /// Provides a base implementation for a . + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett + ] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + public abstract class ComponentEditorPage : Panel { + + IComponentEditorPageSite pageSite; + IComponent component; + bool firstActivate; + bool loadRequired; + int loading; + Icon icon; + bool commitOnDeactivate; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public ComponentEditorPage() : base() { + commitOnDeactivate = false; + firstActivate = true; + loadRequired = false; + loading = 0; + + Visible = false; + } + + + /// + /// + /// + /// Hide the property + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// Gets or sets the page site. + /// + protected IComponentEditorPageSite PageSite { + get { return pageSite; } + set { pageSite = value; } + } + /// + /// + /// Gets or sets the component to edit. + /// + protected IComponent Component { + get { return component; } + set { component = value; } + } + /// + /// + /// Indicates whether the page is being activated for the first time. + /// + protected bool FirstActivate { + get { return firstActivate; } + set { firstActivate = value; } + } + /// + /// + /// Indicates whether a load is required previous to editing. + /// + protected bool LoadRequired { + get { return loadRequired; } + set { loadRequired = value; } + } + /// + /// + /// Indicates if loading is taking place. + /// + protected int Loading { + get { return loading; } + set { loading = value; } + } + + /// + /// + /// Indicates whether an editor should apply its + /// changes before it is deactivated. + /// + public bool CommitOnDeactivate { + get { + return commitOnDeactivate; + } + set { + commitOnDeactivate = value; + } + } + + /// + /// + /// Gets or sets the creation parameters for this control. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.Style &= ~(NativeMethods.WS_BORDER | NativeMethods.WS_OVERLAPPED | NativeMethods.WS_DLGFRAME); + return cp; + } + } + + /// + /// + /// Gets or sets the icon for this page. + /// + public Icon Icon { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (icon == null) { + icon = new Icon(typeof(ComponentEditorPage), "ComponentEditorPage.ico"); + } + return icon; + } + set { + icon = value; + } + } + + /// + /// + /// + /// Gets or sets the title of the page. + /// + public virtual string Title { + get { + return base.Text; + } + } + + /// + /// + /// Activates and displays the page. + /// + public virtual void Activate() { + if (loadRequired) { + EnterLoadingMode(); + LoadComponent(); + ExitLoadingMode(); + + loadRequired = false; + } + Visible = true; + firstActivate = false; + } + + /// + /// + /// Applies changes to all the components being edited. + /// + public virtual void ApplyChanges() { + SaveComponent(); + } + + /// + /// + /// Deactivates and hides the page. + /// + public virtual void Deactivate() { + Visible = false; + } + + /// + /// + /// Increments the loading counter, which determines whether a page + /// is in loading mode. + /// + protected void EnterLoadingMode() { + loading++; + } + + /// + /// + /// Decrements the loading counter, which determines whether a page + /// is in loading mode. + /// + protected void ExitLoadingMode() { + Debug.Assert(loading > 0, "Unbalanced Enter/ExitLoadingMode calls"); + loading--; + } + + /// + /// + /// Gets the control that represents the window for this page. + /// + public virtual Control GetControl() { + return this; + } + + /// + /// + /// Gets the component that is to be edited. + /// + protected IComponent GetSelectedComponent() { + return component; + } + + /// + /// + /// Processes messages that could be handled by the page. + /// + public virtual bool IsPageMessage(ref Message msg) { + return PreProcessMessage(ref msg); + } + + /// + /// + /// Gets a value indicating whether the page is being activated for the first time. + /// + protected bool IsFirstActivate() { + return firstActivate; + } + + /// + /// + /// Gets a value indicating whether the page is being loaded. + /// + protected bool IsLoading() { + return loading != 0; + } + + /// + /// + /// Loads the component into the page UI. + /// + protected abstract void LoadComponent(); + + /// + /// + /// + /// Called when the page along with its sibling + /// pages have applied their changes. + /// + public virtual void OnApplyComplete() { + ReloadComponent(); + } + + /// + /// + /// Called when the current component may have changed elsewhere + /// and needs to be reloded into the UI. + /// + protected virtual void ReloadComponent() { + if (Visible == false) { + loadRequired = true; + } + } + + /// + /// + /// Saves the component from the page UI. + /// + protected abstract void SaveComponent(); + + /// + /// + /// Sets the page to be in dirty state. + /// + protected virtual void SetDirty() { + if (IsLoading() == false) { + pageSite.SetDirty(); + } + } + + /// + /// + /// Sets the component to be edited. + /// + public virtual void SetComponent(IComponent component) { + this.component = component; + loadRequired = true; + } + + /// + /// + /// Sets the site for this page. + /// + public virtual void SetSite(IComponentEditorPageSite site) { + this.pageSite = site; + + pageSite.GetControl().Controls.Add(this); + } + + /// + /// + /// + /// Provides help information to the help system. + /// + public virtual void ShowHelp() { + } + + /// + /// + /// Gets a value indicating whether the editor supports Help. + /// + public virtual bool SupportsHelp() { + return false; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Design/EventsTab.cs b/WindowsForms/Managed/System/WinForms/Design/EventsTab.cs new file mode 100644 index 000000000..947883674 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/EventsTab.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Collections; + using Microsoft.Win32; + + + /// + /// + /// Provides a tab on the property browser to display events for selection and linking. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class EventsTab : PropertyTab { + private IServiceProvider sp; + private IDesignerHost currentHost; + private bool sunkEvent; + + /// + /// + /// Initializes a new instance of the class. + /// + public EventsTab(IServiceProvider sp){ + this.sp = sp; + } + + /// + /// + /// Gets or sets the name of the tab. + /// + public override string TabName { + get { + return SR.GetString(SR.PBRSToolTipEvents); + } + } + + /// + /// + /// Gets or sets the help keyword for the tab. + /// + public override string HelpKeyword { + get { + return "Events"; // do not localize. + } + } + + // override this to reject components you don't want to support. + /// + /// + /// Gets a value indicating whether the specified object can be extended. + /// + public override bool CanExtend(Object extendee) { + return !Marshal.IsComObject(extendee); + } + + private void OnActiveDesignerChanged(object sender, ActiveDesignerEventArgs adevent){ + currentHost = adevent.NewDesigner; + } + + /// + /// + /// Gets the default property from the specified object. + /// + public override PropertyDescriptor GetDefaultProperty(object obj) { + + IEventBindingService eventPropertySvc = GetEventPropertyService(obj, null); + + if (eventPropertySvc == null) { + return null; + } + + // Find the default event. Note that we rely on GetEventProperties caching + // the property to event match, so we can == on the default event. + // We assert that this always works. + // + EventDescriptor defEvent = TypeDescriptor.GetDefaultEvent(obj); + if (defEvent != null) { + return eventPropertySvc.GetEventProperty(defEvent); + } + return null; + } + + private IEventBindingService GetEventPropertyService(object obj, ITypeDescriptorContext context) { + + IEventBindingService eventPropertySvc = null; + + if (!sunkEvent){ + IDesignerEventService des = (IDesignerEventService)sp.GetService(typeof(IDesignerEventService)); + + if (des != null){ + des.ActiveDesignerChanged += new ActiveDesignerEventHandler(this.OnActiveDesignerChanged); + } + sunkEvent = true; + } + + if (eventPropertySvc == null && currentHost != null) { + eventPropertySvc = (IEventBindingService)currentHost.GetService(typeof(IEventBindingService)); + } + + if (eventPropertySvc == null && obj is IComponent){ + ISite site = ((IComponent)obj).Site; + + if (site != null) { + eventPropertySvc = (IEventBindingService)site.GetService(typeof(IEventBindingService)); + } + } + + if (eventPropertySvc == null && context != null) { + eventPropertySvc = (IEventBindingService)context.GetService(typeof(IEventBindingService)); + } + return eventPropertySvc; + } + + /// + /// + /// Gets all the properties of the tab. + /// + public override PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes) { + return GetProperties(null, component, attributes); + } + + /// + /// + /// Gets the properties of the specified component... + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attributes) { + //Debug.Assert(component != null, "Can't get properties for a null item!"); + + IEventBindingService eventPropertySvc = GetEventPropertyService(component, context); + + if (eventPropertySvc == null) { + return new PropertyDescriptorCollection(null); + } + EventDescriptorCollection events = TypeDescriptor.GetEvents(component, attributes); + PropertyDescriptorCollection realEvents = eventPropertySvc.GetEventProperties(events); + + + // Add DesignerSerializationVisibilityAttribute.Content to attributes to see if we have any. + Attribute[] attributesPlusNamespace = new Attribute[attributes.Length + 1]; + Array.Copy(attributes, 0, attributesPlusNamespace, 0, attributes.Length); + attributesPlusNamespace[attributes.Length] = DesignerSerializationVisibilityAttribute.Content; + + // If we do, then we traverse them to see if they have any events under the current attributes, + // and if so, we'll show them as top-level properties so they can be drilled down into to get events. + PropertyDescriptorCollection namespaceProperties = TypeDescriptor.GetProperties(component, attributesPlusNamespace); + if (namespaceProperties.Count > 0) { + ArrayList list = null; + for (int i = 0; i < namespaceProperties.Count; i++) { + PropertyDescriptor nsProp = namespaceProperties[i]; + + TypeConverter tc = nsProp.Converter; + + if (!tc.GetPropertiesSupported()) { + continue; + } + + Object namespaceValue = nsProp.GetValue(component); + EventDescriptorCollection namespaceEvents = TypeDescriptor.GetEvents(namespaceValue, attributes); + if (namespaceEvents.Count > 0) { + if (list == null) { + list = new ArrayList(); + } + + // make this non-mergable + // + nsProp = TypeDescriptor.CreateProperty(nsProp.ComponentType, nsProp, MergablePropertyAttribute.No); + list.Add(nsProp); + } + } + + // we've found some, so add them to the event list. + if (list != null) { + PropertyDescriptor[] realNamespaceProperties = new PropertyDescriptor[list.Count]; + list.CopyTo(realNamespaceProperties, 0); + PropertyDescriptor[] finalEvents = new PropertyDescriptor[realEvents.Count + realNamespaceProperties.Length]; + realEvents.CopyTo(finalEvents, 0); + Array.Copy(realNamespaceProperties, 0, finalEvents, realEvents.Count, realNamespaceProperties.Length); + realEvents = new PropertyDescriptorCollection(finalEvents); + } + } + + return realEvents; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Design/IUIService.cs b/WindowsForms/Managed/System/WinForms/Design/IUIService.cs new file mode 100644 index 000000000..e4ddae67c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/IUIService.cs @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.Design { + + using System; + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Windows.Forms; + + /// + /// + /// + /// Provides support + /// for interaction with the user + /// interface of the development environment object that is hosting the designer. + /// + [Guid("06A9C74B-5E32-4561-BE73-381B37869F4F")] + public interface IUIService { + /// + /// + /// Gets or sets the collections of + /// styles that are specific to the host's environment. + /// + IDictionary Styles {get;} + + /// + /// + /// Indicates whether the component can + /// display a ComponentDesigner. + /// + bool CanShowComponentEditor(object component); + + /// + /// + /// Gets the window that should be used for dialog parenting. + /// + IWin32Window GetDialogOwnerWindow(); + + + /// + /// + /// Sets a flag indicating the UI is dirty. + /// + void SetUIDirty(); + + /// + /// + /// Attempts to display a ComponentEditor for a component. + /// + bool ShowComponentEditor(object component, IWin32Window parent); + /// + /// + /// Attempts to display the specified form in a dialog box. + /// + + DialogResult ShowDialog(Form form); + + /// + /// + /// Displays the specified error message in a message box. + /// + void ShowError(string message); + + /// + /// + /// Displays the specified exception + /// and its information in a message box. + /// + void ShowError(Exception ex); + + /// + /// + /// Displays the specified exception + /// and its information in a message box. + /// + void ShowError(Exception ex, string message); + + /// + /// + /// Displays the specified message in a message box. + /// + void ShowMessage(string message); + + /// + /// + /// Displays the specified message in + /// a message box with the specified caption. + /// + void ShowMessage(string message, string caption); + + /// + /// + /// Displays the specified message in a message box with the specified caption and + /// buttons to place on the dialog box. + /// + DialogResult ShowMessage(string message, string caption, MessageBoxButtons buttons); + + /// + /// + /// Displays the specified tool window. + /// + bool ShowToolWindow(Guid toolWindow); + } +} diff --git a/WindowsForms/Managed/System/WinForms/Design/IWinFormsEditorService.cs b/WindowsForms/Managed/System/WinForms/Design/IWinFormsEditorService.cs new file mode 100644 index 000000000..0fe269bad --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/IWinFormsEditorService.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.Design { + + using System.Diagnostics; + + using System.Windows.Forms; + + /// + /// + /// + /// Provides an interface to display Win Forms dialog + /// boxes and controls. + /// + /// + public interface IWindowsFormsEditorService { + + /// + /// + /// Closes a previously opened drop down + /// list. + /// + void CloseDropDown(); + + /// + /// + /// Displays the specified control in a drop down list. + /// + void DropDownControl(Control control); + + /// + /// + /// + /// Shows the specified dialog box. + /// + /// + DialogResult ShowDialog(Form dialog); + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Design/PropertyTab.cs b/WindowsForms/Managed/System/WinForms/Design/PropertyTab.cs new file mode 100644 index 000000000..f73ad71f2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/PropertyTab.cs @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// Provides a base class for property tabs. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + public abstract class PropertyTab : IExtenderProvider { + + private Object[] components; + private Bitmap bitmap; + private bool checkedBmp; + + /// + ~PropertyTab() { + Dispose(false); + } + + // don't override this. Just put a 16x16 bitmap in a file with the same name as your class in your resources. + /// + /// + /// Gets or sets a bitmap to display in the property tab. + /// + public virtual Bitmap Bitmap { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (!checkedBmp && bitmap == null) { + string bmpName = GetType().Name + ".bmp"; + try + { + bitmap = new Bitmap(GetType(), bmpName); + } + catch (Exception ex) + { + Debug.Fail("Failed to find bitmap '" + bmpName + "' for class " + GetType().FullName, ex.ToString()); + } + checkedBmp = true; + } + return bitmap; + } + } + + // don't override this either. + /// + /// + /// Gets or sets the array of components the property tab is associated with. + /// + public virtual Object[] Components { + get { + return components; + } + set { + this.components = value; + } + } + + // okay. Override this to give a good TabName. + /// + /// + /// Gets or sets the name for the property tab. + /// + public abstract string TabName { + get; + } + + /// + /// + /// Gets or sets the help keyword that is to be associated with this tab. This defaults + /// to the tab name. + /// + public virtual string HelpKeyword { + get { + return TabName; + } + } + + // override this to reject components you don't want to support. + /// + /// + /// Gets a value indicating whether the specified object be can extended. + /// + public virtual bool CanExtend(Object extendee) { + return true; + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) { + if (disposing) { + if (bitmap != null) { + bitmap.Dispose(); + bitmap = null; + } + } + } + + // return the default property item + /// + /// + /// Gets the default property of the specified component. + /// + public virtual PropertyDescriptor GetDefaultProperty(Object component) { + return TypeDescriptor.GetDefaultProperty(component); + } + + // okay, override this to return whatever you want to return... All properties must apply to component. + /// + /// + /// Gets the properties of the specified component. + /// + public virtual PropertyDescriptorCollection GetProperties(Object component) { + return GetProperties(component, null); + } + + /// + /// + /// Gets the properties of the specified component which match the specified + /// attributes. + /// + public abstract PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes); + + /// + /// + /// Gets the properties of the specified component... + /// + public virtual PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attributes) { + return GetProperties(component, attributes); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailability.cs b/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailability.cs new file mode 100644 index 000000000..e30cf0916 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailability.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design { +using System.Diagnostics.CodeAnalysis; + [Flags()] + [SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] // PM reviewed the enum name + public enum ToolStripItemDesignerAvailability { + None = 0x00000000, + ToolStrip = 0x00000001, + MenuStrip = 0x00000002, + ContextMenuStrip = 0x00000004, + StatusStrip = 0x0000008, + All = ToolStrip | MenuStrip | ContextMenuStrip | StatusStrip + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailabilityAttribute.cs b/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailabilityAttribute.cs new file mode 100644 index 000000000..677ceb80c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/ToolStripDesignerAvailabilityAttribute.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Design { +using System.Diagnostics.CodeAnalysis; + + [AttributeUsage(AttributeTargets.Class)] + public sealed class ToolStripItemDesignerAvailabilityAttribute : Attribute { + private ToolStripItemDesignerAvailability visibility; + [ + SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // ToolStripDesignAvailabilityAttribute is + // actually immutable. + ] + public static readonly ToolStripItemDesignerAvailabilityAttribute Default = new ToolStripItemDesignerAvailabilityAttribute(); + + // + // Specifies which ToolStrip types the Item can appear in - ToolStrip,MenuStrip,StatusStrip,ContextMenuStrip + // Adding this attribute over a class lets you add to the list of custom items in the ToolStrip design time. + // + public ToolStripItemDesignerAvailabilityAttribute() { + this.visibility = ToolStripItemDesignerAvailability.None; + } + + public ToolStripItemDesignerAvailabilityAttribute(ToolStripItemDesignerAvailability visibility) { + this.visibility = visibility; + } + + public ToolStripItemDesignerAvailability ItemAdditionVisibility { + get { + return visibility; + } + } + public override bool Equals(object obj) { + if (obj == this) { + return true; + } + + ToolStripItemDesignerAvailabilityAttribute other = obj as ToolStripItemDesignerAvailabilityAttribute; + return (other != null) && other.ItemAdditionVisibility == this.visibility; + } + + public override int GetHashCode() { + return visibility.GetHashCode(); + } + + public override bool IsDefaultAttribute() { + return (this.Equals(Default)); + } + } + + +} + + diff --git a/WindowsForms/Managed/System/WinForms/Design/WinFormsComponentEditor.cs b/WindowsForms/Managed/System/WinForms/Design/WinFormsComponentEditor.cs new file mode 100644 index 000000000..0bda2857f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Design/WinFormsComponentEditor.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms.Design { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Windows.Forms; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Provides a base class for editors that support any type + /// of + /// objects. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + public abstract class WindowsFormsComponentEditor : ComponentEditor { + + /// + /// + /// Activates a UI used to edit the component. + /// + public override bool EditComponent(ITypeDescriptorContext context, object component){ + return EditComponent(context, component, null); + } + + /// + /// + /// + /// Activates the advanced UI used to edit the component. + /// + public bool EditComponent(object component, IWin32Window owner) { + return EditComponent(null, component, owner); + } + + /// + /// + /// + /// Activates the advanced UI used to edit the component. + /// + public virtual bool EditComponent(ITypeDescriptorContext context, object component, IWin32Window owner) { + bool changed = false; + Type[] pageControlTypes = GetComponentEditorPages(); + + if ((pageControlTypes != null) && (pageControlTypes.Length != 0)) { + ComponentEditorForm form = new ComponentEditorForm(component, + pageControlTypes); + + if (form.ShowForm(owner, GetInitialComponentEditorPageIndex()) == DialogResult.OK) + changed = true; + } + + return changed; + } + + /// + /// + /// Gets the set of pages to be used. + /// + protected virtual Type[] GetComponentEditorPages() { + return null; + } + + /// + /// + /// Gets the index of the to be shown by default as the + /// first active page. + /// + protected virtual int GetInitialComponentEditorPageIndex() { + return 0; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DialogResult.cs b/WindowsForms/Managed/System/WinForms/DialogResult.cs new file mode 100644 index 000000000..b1f7d345b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DialogResult.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies identifiers to + /// indicate the return value of a dialog box. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum DialogResult{ + + /// + /// + /// + /// + /// Nothing is returned from the dialog box. This + /// means that the modal dialog continues running. + /// + /// + /// + None = 0, + + /// + /// + /// + /// The + /// dialog box return value is + /// OK (usually sent from a button labeled OK). + /// + /// + /// + OK = 1, + + /// + /// + /// + /// The + /// dialog box return value is Cancel (usually sent + /// from a button labeled Cancel). + /// + /// + /// + Cancel = 2, + + /// + /// + /// + /// The dialog box return value is + /// Abort (usually sent from a button labeled Abort). + /// + /// + /// + Abort = 3, + + /// + /// + /// + /// The dialog box return value is + /// Retry (usually sent from a button labeled Retry). + /// + /// + /// + Retry = 4, + + /// + /// + /// + /// The + /// dialog box return value is Ignore (usually sent + /// from a button labeled Ignore). + /// + /// + /// + Ignore = 5, + + /// + /// + /// + /// The dialog box return value is + /// Yes (usually sent from a button labeled Yes). + /// + /// + /// + Yes = 6, + + /// + /// + /// + /// The dialog box return value is + /// No (usually sent from a button labeled No). + /// + /// + /// + No = 7, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DisplayInformation.cs b/WindowsForms/Managed/System/WinForms/DisplayInformation.cs new file mode 100644 index 000000000..3d653656f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DisplayInformation.cs @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +/* + */ + +namespace System.Windows.Forms { + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + + internal class DisplayInformation { + + private static bool highContrast; //whether we are under hight contrast mode + private static bool lowRes; //whether we are under low resolution mode + private static bool isTerminalServerSession; //whether this application is run on a terminal server (remote desktop) + private static bool highContrastSettingValid; //indicates whether the high contrast setting is correct + private static bool lowResSettingValid; //indicates whether the low resolution setting is correct + private static bool terminalSettingValid; //indicates whether the terminal server setting is correct + private static short bitsPerPixel; + private static bool dropShadowSettingValid; + private static bool dropShadowEnabled; + private static bool menuAccessKeysUnderlinedValid; + private static bool menuAccessKeysUnderlined; + + + static DisplayInformation() { + SystemEvents.UserPreferenceChanging += new UserPreferenceChangingEventHandler(UserPreferenceChanging); + SystemEvents.DisplaySettingsChanging += new EventHandler(DisplaySettingsChanging); + } + + public static short BitsPerPixel { + get { + if (bitsPerPixel == 0) { + // we used to iterate through all screens, but + // for some reason unused screens can temparily appear + // in the AllScreens collection - we would honor the display + // setting of an unused screen. + // According to EnumDisplayMonitors, a primary screen check should be sufficient + bitsPerPixel = (short)Screen.PrimaryScreen.BitsPerPixel; + + } + return bitsPerPixel; + } + } + + /// + ///tests to see if the monitor is in low resolution mode (8-bit color depth or less). + /// + public static bool LowResolution { + get { + + if (lowResSettingValid && !lowRes) { + return lowRes; + } + // dont cache if we're in low resolution. + lowRes = BitsPerPixel <= 8; + lowResSettingValid = true; + return lowRes; + } + } + + /// + ///tests to see if we are under high contrast mode + /// + public static bool HighContrast { + get { + if (highContrastSettingValid) { + return highContrast; + } + highContrast = SystemInformation.HighContrast; + highContrastSettingValid = true; + return highContrast; + } + } + public static bool IsDropShadowEnabled { + get { + if (dropShadowSettingValid) { + return dropShadowEnabled; + } + dropShadowEnabled = SystemInformation.IsDropShadowEnabled; + dropShadowSettingValid = true; + return dropShadowEnabled; + } + } + + /// + ///test to see if we are under terminal server mode + /// + public static bool TerminalServer { + get { + if (terminalSettingValid) { + return isTerminalServerSession; + } + + isTerminalServerSession = SystemInformation.TerminalServerSession; + terminalSettingValid = true; + return isTerminalServerSession; + } + } + + // return if mnemonic underlines should always be there regardless of ALT + public static bool MenuAccessKeysUnderlined { + get { + if (menuAccessKeysUnderlinedValid) { + return menuAccessKeysUnderlined; + } + menuAccessKeysUnderlined = SystemInformation.MenuAccessKeysUnderlined; + menuAccessKeysUnderlinedValid = true; + return menuAccessKeysUnderlined; + } + } + + /// + ///event handler for change in display setting + /// + private static void DisplaySettingsChanging(object obj, EventArgs ea) + { + highContrastSettingValid = false; + lowResSettingValid = false; + terminalSettingValid = false; + dropShadowSettingValid = false; + menuAccessKeysUnderlinedValid = false; + + } + + /// + ///event handler for change in user preference + /// + private static void UserPreferenceChanging(object obj, UserPreferenceChangingEventArgs e) { + highContrastSettingValid = false; + lowResSettingValid = false; + terminalSettingValid = false; + dropShadowSettingValid = false; + bitsPerPixel = 0; + + if (e.Category == UserPreferenceCategory.General) { + menuAccessKeysUnderlinedValid =false; + } + } + } + } diff --git a/WindowsForms/Managed/System/WinForms/DockStyle.cs b/WindowsForms/Managed/System/WinForms/DockStyle.cs new file mode 100644 index 000000000..ece9bbcf7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DockStyle.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing.Design; + using System.Windows.Forms.Design; + using Microsoft.Win32; + + + /* + * Copyright (c) 1997, Microsoft Corporation. All Rights Reserved. + * Information Contained Herein is Proprietary and Confidential. + */ + + + + /// + /// + /// Control Dock values. + /// + /// When a control is docked to an edge of it's container it will + /// always be positioned flush against that edge while the container + /// resizes. If more than one control is docked to an edge, the controls + /// will not be placed on top of each other. + /// + [ + Editor("System.Windows.Forms.Design.DockEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + ] + public enum DockStyle { + /// + /// + /// [To be supplied.] + /// + None = 0, + /// + /// + /// [To be supplied.] + /// + Top = 1, + /// + /// + /// [To be supplied.] + /// + Bottom = 2, + /// + /// + /// [To be supplied.] + /// + Left = 3, + /// + /// + /// [To be supplied.] + /// + Right = 4, + /// + /// + /// [To be supplied.] + /// + Fill = 5, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DockingAttribute.cs b/WindowsForms/Managed/System/WinForms/DockingAttribute.cs new file mode 100644 index 000000000..0a4dbce4f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DockingAttribute.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// Specifies how a control should be docked by default when added through the designer. + /// + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class DockingAttribute : Attribute { + private DockingBehavior dockingBehavior; + + /// + /// + /// + /// Default constructor. + /// + /// + public DockingAttribute() { + this.dockingBehavior = DockingBehavior.Never; + } + + /// + /// + /// + /// Constructor. + /// + /// + public DockingAttribute(DockingBehavior dockingBehavior) { + this.dockingBehavior = dockingBehavior; + } + + /// + /// + /// + /// Specifies the default value for the . + /// This field is read-only. + /// + /// + public static readonly DockingAttribute Default = new DockingAttribute(); + + /// + /// + /// + /// DockingBehavior property. + /// + /// + public DockingBehavior DockingBehavior { + get { + return dockingBehavior; + } + } + + /// + /// + /// + /// + public override bool Equals(object obj) { + if (obj == this) { + return true; + } + + DockingAttribute other = obj as DockingAttribute; + + return (other != null) && other.DockingBehavior == this.dockingBehavior; + } + + /// + /// + /// [To be supplied.] + /// + public override int GetHashCode() { + return dockingBehavior.GetHashCode(); + } + + /// + /// + /// + /// + public override bool IsDefaultAttribute() { + return (this.Equals(Default)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DockingBehavior.cs b/WindowsForms/Managed/System/WinForms/DockingBehavior.cs new file mode 100644 index 000000000..b73883a72 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DockingBehavior.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// Specifies whether any characters in the + /// current selection have the style or attribute. + /// + /// + /// + public enum DockingBehavior { + /// + /// + /// + /// Some but not all characters. + /// + /// + Never = 0, + + /// + /// + /// + /// No characters. + /// + /// + Ask = 1, + + /// + /// + /// + /// All characters. + /// + /// + AutoDock = 2 + } +} diff --git a/WindowsForms/Managed/System/WinForms/DomainUpDown.cs b/WindowsForms/Managed/System/WinForms/DomainUpDown.cs new file mode 100644 index 000000000..dfbd04a02 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DomainUpDown.cs @@ -0,0 +1,940 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Collections; + using Microsoft.Win32; + + /// + /// + /// Represents a Windows up-down control that displays string values. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Items"), + DefaultEvent("SelectedItemChanged"), + DefaultBindingProperty("SelectedItem"), + SRDescription(SR.DescriptionDomainUpDown) + ] + public class DomainUpDown : UpDownBase { + + private readonly static string DefaultValue = ""; + private readonly static bool DefaultWrap = false; + + ////////////////////////////////////////////////////////////// + // Member variables + // + ////////////////////////////////////////////////////////////// + + /// + /// + /// Allowable strings for the domain updown. + /// + + private DomainUpDownItemCollection domainItems = null; + + private string stringValue = DefaultValue; // Current string value + private int domainIndex = -1; // Index in the domain list + private bool sorted = false; // Sort the domain values + + private bool wrap = DefaultWrap; // Wrap around domain items + + private EventHandler onSelectedItemChanged = null; + + private bool inSort = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DomainUpDown() : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + Text = String.Empty; + } + + // Properties + + /// + /// + /// + /// Gets the collection of objects assigned to the + /// up-down control. + /// + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.DomainUpDownItemsDescr), + Localizable(true), + Editor("System.Windows.Forms.Design.StringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public DomainUpDownItemCollection Items { + + get { + if (domainItems == null) { + domainItems = new DomainUpDownItemCollection(this); + } + return domainItems; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// + /// Gets or sets the index value of the selected item. + /// + /// + [ + Browsable(false), + DefaultValue(-1), + SRCategory(SR.CatAppearance), + SRDescription(SR.DomainUpDownSelectedIndexDescr) + ] + public int SelectedIndex { + + get { + if (UserEdit) { + return -1; + } + else { + return domainIndex; + } + } + + set { + if (value < -1 || value >= Items.Count) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (value != SelectedIndex) { + SelectIndex(value); + } + } + } + + /// + /// + /// + /// Gets or sets the selected item based on the index value + /// of the selected item in the + /// collection. + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.DomainUpDownSelectedItemDescr) + ] + public object SelectedItem { + get { + int index = SelectedIndex; + return(index == -1) ? null : Items[index]; + } + set { + + // Treat null as selecting no item + // + if (value == null) { + SelectedIndex = -1; + } + else { + // Attempt to find the given item in the list of items + // + for (int i = 0; i < Items.Count; i++) { + if (value != null && value.Equals(Items[i])) { + SelectedIndex = i; + break; + } + } + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the item collection is sorted. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.DomainUpDownSortedDescr) + ] + public bool Sorted { + + get { + return sorted; + } + + set { + sorted = value; + if (sorted) { + SortDomainItems(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the collection of items continues to + /// the first or last item if the user continues past the end of the list. + /// + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(false), + SRDescription(SR.DomainUpDownWrapDescr) + ] + public bool Wrap { + + get { + return wrap; + } + + set { + wrap = value; + } + } + + ////////////////////////////////////////////////////////////// + // Methods + // + ////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Occurs when the property has + /// been changed. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.DomainUpDownOnSelectedItemChangedDescr)] + public event EventHandler SelectedItemChanged { + add { + onSelectedItemChanged += value; + } + remove { + onSelectedItemChanged -= value; + } + } + + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new DomainUpDownAccessibleObject(this); + } + + /// + /// + /// + /// Displays the next item in the object collection. + /// + /// + public override void DownButton() { + + // Make sure domain values exist, and there are >0 items + // + if (domainItems == null) { + return; + } + if (domainItems.Count <= 0) { + return; + } + + // If the user has entered text, attempt to match it to the domain list + // + int matchIndex = -1; + if (UserEdit) { + matchIndex = MatchIndex(Text, false, domainIndex); + } + if (matchIndex != -1) { + + // Found a match, so select this value + // + if (!LocalAppContextSwitches.UseLegacyDomainUpDownControlScrolling) { + domainIndex = matchIndex; + } + else { + SelectIndex(matchIndex); + return; + } + } + + // Otherwise, get the next string in the domain list + // + + if (domainIndex < domainItems.Count - 1) { + SelectIndex(domainIndex + 1); + } + else if (Wrap) { + SelectIndex(0); + } + + } + + /// + /// + /// Tries to find a match of the supplied text in the domain list. + /// If complete is true, a complete match is required for success + /// (i.e. the supplied text is the same length as the matched domain value) + /// Returns the index in the domain list if the match is successful, + /// returns -1 otherwise. + /// + + internal int MatchIndex(string text, bool complete) { + return MatchIndex(text, complete, domainIndex); + } + + internal int MatchIndex(string text, bool complete, int startPosition) { + + // Make sure domain values exist + if (domainItems == null) { + return -1; + } + + // Sanity check of parameters + if (text.Length < 1) { + return -1; + } + if (domainItems.Count <= 0) { + return -1; + } + if (startPosition < 0) { + startPosition = domainItems.Count - 1; + } + if (startPosition >= domainItems.Count) { + startPosition = 0; + } + + // Attempt to match the supplied string text with + // the domain list. Returns the index in the list if successful, + // otherwise returns -1. + int index = startPosition; + int matchIndex = -1; + bool found = false; + + if (!complete) { + text = text.ToUpper(CultureInfo.InvariantCulture); + } + + // Attempt to match the string with Items[index] + do { + if (complete) + found = Items[index].ToString().Equals(text); + else + found = Items[index].ToString().ToUpper(CultureInfo.InvariantCulture).StartsWith(text); + + if (found) { + matchIndex = index; + } + + // Calculate the next index to attempt to match + index++; + if (index >= domainItems.Count) { + index = 0; + } + + } while (!found && index != startPosition); + + return matchIndex; + } + + /// + /// + /// + /// + /// In the case of a DomainUpDown, the handler for changing + /// values is called OnSelectedItemChanged - so just forward it to that + /// function. + /// + /// + protected override void OnChanged(object source, EventArgs e) { + OnSelectedItemChanged(source, e); + } + + /// + /// + /// + /// Handles the + /// event, using the input character to find the next matching item in our + /// item collection. + /// + protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) { + if (ReadOnly) { + char[] character = new char[] { e.KeyChar }; + UnicodeCategory uc = Char.GetUnicodeCategory(character[0]); + + if (uc == UnicodeCategory.LetterNumber + || uc == UnicodeCategory.LowercaseLetter + || uc == UnicodeCategory.DecimalDigitNumber + || uc == UnicodeCategory.MathSymbol + || uc == UnicodeCategory.OtherLetter + || uc == UnicodeCategory.OtherNumber + || uc == UnicodeCategory.UppercaseLetter) { + + // Attempt to match the character to a domain item + int matchIndex = MatchIndex(new string(character), false, domainIndex + 1); + if (matchIndex != -1) { + + // Select the matching domain item + SelectIndex(matchIndex); + } + e.Handled = true; + } + } + base.OnTextBoxKeyPress(source, e); + } + + + /// + /// + /// + /// Raises the event. + /// + /// + protected void OnSelectedItemChanged(object source, EventArgs e) { + + // Call the event handler + if (onSelectedItemChanged != null) { + onSelectedItemChanged(this, e); + } + } + + /// + /// + /// Selects the item in the domain list at the given index + /// + private void SelectIndex(int index) { + + // Sanity check index + + Debug.Assert(domainItems != null, "Domain values array is null"); + Debug.Assert(index < domainItems.Count && index >= -1, "SelectValue: index out of range"); + if (domainItems == null || index < -1 || index >= domainItems.Count) { + // Defensive programming + index = -1; + return; + } + + // If the selected index has changed, update the text + // + domainIndex = index; + if (domainIndex >= 0) { + stringValue = domainItems[domainIndex].ToString(); + UserEdit = false; + UpdateEditText(); + } + else { + UserEdit = true; + } + + Debug.Assert(domainIndex >=0 || UserEdit == true, "UserEdit should be true when domainIndex < 0 " + UserEdit); + } + + /// + /// + /// Sorts the domain values + /// + private void SortDomainItems() { + if (inSort) + return; + + inSort = true; + try { + // Sanity check + Debug.Assert(sorted == true, "Sorted == false"); + if (!sorted) { + return; + } + + if (domainItems != null) { + + // Sort the domain values + ArrayList.Adapter(domainItems).Sort(new DomainUpDownItemCompare()); + + // Update the domain index + if (!UserEdit) { + int newIndex = MatchIndex(stringValue, true); + if (newIndex != -1) { + SelectIndex(newIndex); + } + } + } + } + finally { + inSort = false; + } + } + + /// + /// + /// Provides some interesting info about this control in String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + + if (Items != null) { + s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); + s += ", SelectedIndex: " + SelectedIndex.ToString(CultureInfo.CurrentCulture); + } + return s; + } + + /// + /// + /// + /// Displays the previous item in the collection. + /// + /// + public override void UpButton() { + + // Make sure domain values exist, and there are >0 items + if (domainItems == null) { + return; + } + if (domainItems.Count <= 0) { + return; + } + + // legacy behaviour. we do not want to void operation if domainIndex was -1 in latest runtime. + if (domainIndex == -1 && LocalAppContextSwitches.UseLegacyDomainUpDownControlScrolling) { + return; + } + + // If the user has entered text, attempt to match it to the domain list + int matchIndex = -1; + if (UserEdit) { + matchIndex = MatchIndex(Text, false, domainIndex); + } + if (matchIndex != -1) { + + // Found a match, so set the domain index accordingly + //In legacy (.NET framework 4.7.1 and below), we were just updating selected index but no actualy change in the spinner. + //with new runtime, we update the selected index and perform spinner action. + if(!LocalAppContextSwitches.UseLegacyDomainUpDownControlScrolling) { + domainIndex = matchIndex; + } + else { + SelectIndex(matchIndex); + return; + } + } + + // Otherwise, get the previous string in the domain list + + if (domainIndex > 0) { + SelectIndex(domainIndex - 1); + } + else if (Wrap) { + SelectIndex(domainItems.Count - 1); + } + } + + /// + /// + /// + /// Updates the text in the up-down control to display the selected item. + /// + /// + protected override void UpdateEditText() { + + Debug.Assert(!UserEdit, "UserEdit should be false"); + // Defensive programming + UserEdit = false; + + ChangingText = true; + Text = stringValue; + } + // This is not a breaking change -- Even though this control previously autosized to hieght, + // it didn't actually have an AutoSize property. The new AutoSize property enables the + // smarter behavior. + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + int height = PreferredHeight; + int width = LayoutUtils.OldGetLargestStringSizeInCollection(Font, Items).Width; + + // AdjuctWindowRect with our border, since textbox is borderless. + width = SizeFromClientSize(width, height).Width + upDownButtons.Width; + return new Size(width, height) + Padding.Size; + } + + // DomainUpDown collection class + + /// + /// + /// Encapsulates a collection of objects for use by the + /// class. + /// + public class DomainUpDownItemCollection : ArrayList { + DomainUpDown owner; + + internal DomainUpDownItemCollection(DomainUpDown owner) + : base() { + this.owner = owner; + } + + /// + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override object this[int index] { + get { + return base[index]; + } + + set { + base[index] = value; + + if (owner.SelectedIndex == index) { + owner.SelectIndex(index); + } + + if (owner.Sorted) { + owner.SortDomainItems(); + } + } + } + + /// + /// + /// + public override int Add(object item) { + // Overridden to perform sorting after adding an item + + int ret = base.Add(item); + if (owner.Sorted) { + owner.SortDomainItems(); + } + return ret; + } + + /// + /// + /// + public override void Remove(object item) { + int index = IndexOf(item); + + if (index == -1) { + throw new ArgumentOutOfRangeException("item", SR.GetString(SR.InvalidArgument, "item", item.ToString())); + } + else { + RemoveAt(index); + } + } + + /// + /// + /// + public override void RemoveAt(int item) { + // Overridden to update the domain index if neccessary + base.RemoveAt(item); + + if (item < owner.domainIndex) { + // The item removed was before the currently selected item + owner.SelectIndex(owner.domainIndex - 1); + } + else if (item == owner.domainIndex) { + // The currently selected item was removed + // + owner.SelectIndex(-1); + } + } + + /// + /// + /// + public override void Insert(int index, object item) { + base.Insert(index, item); + if (owner.Sorted) { + owner.SortDomainItems(); + } + } + } // end class DomainUpDownItemCollection + + /// + /// + /// + private sealed class DomainUpDownItemCompare : IComparer { + + public int Compare(object p, object q) { + if (p == q) return 0; + if (p == null || q == null) { + return 0; + } + + return String.Compare(p.ToString(), q.ToString(), false, CultureInfo.CurrentCulture); + } + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class DomainUpDownAccessibleObject : ControlAccessibleObject { + + private DomainItemListAccessibleObject itemList; + + /// + /// + /// + public DomainUpDownAccessibleObject(Control owner) : base(owner) { + } + + /// + /// Gets or sets the accessible name. + /// + public override string Name { + get { + string baseName = base.Name; + return ((DomainUpDown)Owner).GetAccessibleName(baseName); + } + set { + base.Name = value; + } + } + + private DomainItemListAccessibleObject ItemList { + get { + if (itemList == null) { + itemList = new DomainItemListAccessibleObject(this); + } + return itemList; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + if (AccessibilityImprovements.Level1) { + return AccessibleRole.SpinButton; + } + else { + return AccessibleRole.ComboBox; + } + } + } + } + + /// + /// + /// + public override AccessibleObject GetChild(int index) { + switch(index) { + // TextBox child + // + case 0: + return ((UpDownBase)Owner).TextBox.AccessibilityObject.Parent; + + // Up/down buttons + // + case 1: + return ((UpDownBase)Owner).UpDownButtonsInternal.AccessibilityObject.Parent; + + case 2: + return ItemList; + } + + return null; + } + + /// + /// + /// + public override int GetChildCount() { + return 3; + } + } + + internal class DomainItemListAccessibleObject : AccessibleObject { + + private DomainUpDownAccessibleObject parent; + + public DomainItemListAccessibleObject(DomainUpDownAccessibleObject parent) : base() { + this.parent = parent; + } + + public override string Name { + get { + string baseName = base.Name; + if (baseName == null || baseName.Length == 0) { + return "Items"; + } + return baseName; + } + set { + base.Name = value; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return parent; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.List; + } + } + + public override AccessibleStates State { + get { + return AccessibleStates.Invisible | AccessibleStates.Offscreen; + } + } + + public override AccessibleObject GetChild(int index) { + + if (index >=0 && index < GetChildCount()) { + return new DomainItemAccessibleObject(((DomainUpDown)parent.Owner).Items[index].ToString(), this); + } + + return null; + } + + public override int GetChildCount() { + return ((DomainUpDown)parent.Owner).Items.Count; + } + + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class DomainItemAccessibleObject : AccessibleObject { + + private string name; + private DomainItemListAccessibleObject parent; + + /// + /// + /// [To be supplied.] + /// + public DomainItemAccessibleObject(string name, AccessibleObject parent) : base() { + this.name = name; + this.parent = (DomainItemListAccessibleObject)parent; + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + return name; + } + set { + name = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return parent; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + return AccessibleRole.ListItem; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleStates State { + get { + return AccessibleStates.Selectable; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return name; + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs new file mode 100644 index 000000000..faceb0d70 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Diagnostics; + using Drawing; + using System; + using System.ComponentModel; + + /// + /// + /// Provides information about a DpiChanged event. + /// + public sealed class DpiChangedEventArgs : CancelEventArgs { + // Parameter units are pixels(dots) per inch. + internal DpiChangedEventArgs(int old, Message m) { + DeviceDpiOld = old; + DeviceDpiNew = NativeMethods.Util.SignedLOWORD(m.WParam); + Debug.Assert(NativeMethods.Util.SignedHIWORD(m.WParam) == DeviceDpiNew, "Non-square pixels!"); + NativeMethods.RECT suggestedRect = (NativeMethods.RECT)UnsafeNativeMethods.PtrToStructure(m.LParam, typeof(NativeMethods.RECT)); + SuggestedRectangle = Rectangle.FromLTRB(suggestedRect.left, suggestedRect.top, suggestedRect.right, suggestedRect.bottom); + } + + /// + /// + /// + public int DeviceDpiOld { + get; + private set; + } + + /// + /// + /// + public int DeviceDpiNew { + get; + private set; + } + + /// + /// + /// + public Rectangle SuggestedRectangle { + get; + private set; + } + + public override string ToString() { + return $"was: {DeviceDpiOld}, now: {DeviceDpiNew}"; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs.back b/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs.back new file mode 100644 index 000000000..e9bafbc1e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DpiChangedEventArgs.cs.back @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Diagnostics; + using Drawing; + using System; + using System.ComponentModel; + + /// + /// + /// Provides information about a DpiChanged event. + /// + public sealed class DpiChangedEventArgs : CancelEventArgs { + // Parameter units are pixels(dots) per inch. + internal DpiChangedEventArgs(int old, Message m) { + DeviceDpiOld = old; + DeviceDpiNew = NativeMethods.Util.SignedLOWORD(m.WParam); + Debug.Assert(NativeMethods.Util.SignedHIWORD(m.WParam) == DeviceDpiNew, "Non-square pixels!"); + NativeMethods.RECT suggestedRect = (NativeMethods.RECT)UnsafeNativeMethods.PtrToStructure(m.LParam, typeof(NativeMethods.RECT)); + SuggestedRectangle = Rectangle.FromLTRB(suggestedRect.left, suggestedRect.top, suggestedRect.right, suggestedRect.bottom); + } + + /// + /// + /// + public int DeviceDpiOld { + get; + private set; + } + + /// + /// + /// + public int DeviceDpiNew { + get; + private set; + } + + /// + /// + /// + public Rectangle SuggestedRectangle { + get; + private set; + } + + public override string ToString() { + return $"was: {DeviceDpiOld}, now: {DeviceDpiNew}"; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DpiHelper.WinForms.cs b/WindowsForms/Managed/System/WinForms/DpiHelper.WinForms.cs new file mode 100644 index 000000000..394c78bcf --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DpiHelper.WinForms.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.Configuration; + + /// + /// Part of DpiHelper class, with methods specific to Microsoft assembly + /// Also handles configuration switches that control states of various Microsoft features + /// + internal static partial class DpiHelper + { + /// + /// Sets DPI awareness for the process by reading configuration value from app.config file + /// + /// true/false - If process DPI awareness is successfully set, returns true. Otherwise false. + public static bool SetWinformsApplicationDpiAwareness() + { + NativeMethods.PROCESS_DPI_AWARENESS dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNINITIALIZED; + Version currentOSVersion = Environment.OSVersion.Version; + if (!IsDpiAwarenessValueSet() || Environment.OSVersion.Platform != System.PlatformID.Win32NT) + { + return false; + } + + string dpiAwareness = (dpiAwarenessValue ?? string.Empty).ToLowerInvariant(); + if (currentOSVersion.CompareTo(ConfigurationOptions.RS2Version) >= 0) + { + int rs2AndAboveDpiFlag; + switch (dpiAwareness) + { + case "true": + case "system": + rs2AndAboveDpiFlag = NativeMethods.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + break; + case "true/pm": + case "permonitor": + rs2AndAboveDpiFlag = NativeMethods.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + break; + case "permonitorv2": + rs2AndAboveDpiFlag = NativeMethods.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + break; + case "false": + default: + rs2AndAboveDpiFlag = NativeMethods.DPI_AWARENESS_CONTEXT_UNAWARE; + break; + } + if (!SafeNativeMethods.SetProcessDpiAwarenessContext(rs2AndAboveDpiFlag)) + { + return false; + } + } + // For operating systems windows 8.1 to Windows 10 redstone 1 version. + else if (currentOSVersion.CompareTo(new Version(6, 3, 0, 0)) >= 0 && currentOSVersion.CompareTo(ConfigurationOptions.RS2Version) < 0) + { + switch (dpiAwareness) + { + case "false": + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNAWARE; + break; + case "true": + case "system": + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE; + break; + case "true/pm": + case "permonitor": + case "permonitorv2": + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE; + break; + default: + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNINITIALIZED; + break; + } + + if (SafeNativeMethods.SetProcessDpiAwareness(dpiFlag) != NativeMethods.S_OK) + { + return false; + } + + } + // For operating systems windows 7 to windows 8 + else if (currentOSVersion.CompareTo(new Version(6, 1, 0, 0)) >= 0 && currentOSVersion.CompareTo(new Version(6, 3, 0, 0)) < 0) + { + switch (dpiAwareness) + { + case "false": + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNAWARE; + break; + case "true": + case "system": + case "true/pm": + case "permonitor": + case "permonitorv2": + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE; + break; + default: + dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNINITIALIZED; + break; + } + + if (dpiFlag == NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE) + { + if (!SafeNativeMethods.SetProcessDPIAware()) + { + return false; + } + } + } + // For windows vista and below, this is not a supported feature. + else + { + return false; + } + + return true; + } + + /// + /// Returns a new Padding with the input's + /// dimensions converted from logical units to device units. + /// + /// Padding in logical units + /// Padding in device units + public static Padding LogicalToDeviceUnits(Padding logicalPadding, int deviceDpi = 0) + { + return new Padding(LogicalToDeviceUnits(logicalPadding.Left, deviceDpi), + LogicalToDeviceUnits(logicalPadding.Top, deviceDpi), + LogicalToDeviceUnits(logicalPadding.Right, deviceDpi), + LogicalToDeviceUnits(logicalPadding.Bottom, deviceDpi)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DragAction.cs b/WindowsForms/Managed/System/WinForms/DragAction.cs new file mode 100644 index 000000000..bae06ad50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DragAction.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how and if a drag-and-drop operation should continue. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum DragAction { + /// + /// + /// + /// The operation will continue. + /// + /// + Continue = 0, + /// + /// + /// + /// The operation will stop with a drop. + /// + /// + Drop = 1, + /// + /// + /// + /// The operation is canceled with no + /// drop message. + /// + /// + /// + Cancel = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DragDropEffects.cs b/WindowsForms/Managed/System/WinForms/DragDropEffects.cs new file mode 100644 index 000000000..3e028bbb9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DragDropEffects.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics; + + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// Specifies the effects of a drag-and-drop operation. + /// + [SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags")] + [Flags] + public enum DragDropEffects { + + /// + /// + /// + /// The drop target does not accept the data. + /// + /// + None = 0x00000000, + /// + /// + /// + /// The data is copied to the drop target. + /// + /// + Copy = 0x00000001, + /// + /// + /// + /// The data from the drag source is moved to the drop target. + /// + /// + Move = 0x00000002, + /// + /// + /// + /// The data from the drag source is linked to the drop target. + /// + /// + Link = 0x00000004, + /// + /// + /// + /// Scrolling is about to start or is currently occurring in the drop target. + /// + /// + Scroll = unchecked((int)0x80000000), + /// + /// + /// + /// The data is copied, removed from the drag source, and scrolled in the + /// drop target. NOTE: Link is intentionally not present in All. + /// + /// + All = Copy | Move | Scroll, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DragEvent.cs b/WindowsForms/Managed/System/WinForms/DragEvent.cs new file mode 100644 index 000000000..57add9335 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DragEvent.cs @@ -0,0 +1,155 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the , , or event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class DragEventArgs : EventArgs { + /// + /// + /// The data associated with this event. + /// + private readonly IDataObject data; + /// + /// + /// The current state of the shift, ctrl, and alt keys. + /// + private readonly int keyState; + /// + /// + /// The mouse x location. + /// + private readonly int x; + /// + /// + /// The mouse y location. + /// + private readonly int y; + /// + /// + /// The effect that should be applied to the mouse cursor. + /// + private readonly DragDropEffects allowedEffect; + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// + private DragDropEffects effect; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public DragEventArgs(IDataObject data, int keyState, int x, int y, DragDropEffects allowedEffect, DragDropEffects effect) { + this.data = data; + this.keyState = keyState; + this.x = x; + this.y = y; + this.allowedEffect = allowedEffect; + this.effect = effect; + } + + /// + /// + /// + /// The + /// that contains the data associated with this event. + /// + /// + public IDataObject Data { + get { + return data; + } + } + /// + /// + /// + /// Gets + /// the current state of the SHIFT, CTRL, and ALT keys. + /// + /// + /// + public int KeyState { + get { + return keyState; + } + } + /// + /// + /// + /// Gets the + /// x-coordinate + /// of the mouse pointer. + /// + /// + public int X { + get { + return x; + } + } + /// + /// + /// + /// Gets + /// the y-coordinate + /// of the mouse pointer. + /// + /// + public int Y { + get { + return y; + } + } + /// + /// + /// + /// Gets which drag-and-drop operations are allowed by the + /// originator (or source) of the drag event. + /// + /// + public DragDropEffects AllowedEffect { + get { + return allowedEffect; + } + } + /// + /// + /// + /// Gets or sets which drag-and-drop operations are allowed by the target of the drag event. + /// + /// + public DragDropEffects Effect { + get { + return effect; + } + set { + effect = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DragEventHandler.cs b/WindowsForms/Managed/System/WinForms/DragEventHandler.cs new file mode 100644 index 000000000..93e765dbd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DragEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle + /// the , + /// , or event of a . + /// + /// + public delegate void DragEventHandler(object sender, DragEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawItemEvent.cs b/WindowsForms/Managed/System/WinForms/DrawItemEvent.cs new file mode 100644 index 000000000..dfb48faea --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawItemEvent.cs @@ -0,0 +1,201 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// This event is fired by owner draw Controls, such as ListBoxes and + /// ComboBoxes. It contains all the information needed for the user to + /// paint the given item, including the item index, the Rectangle in which + /// the drawing should be done, and the Graphics object with which the drawing + /// should be done. + /// + public class DrawItemEventArgs : EventArgs { + + /// + /// + /// The backColor to paint each menu item with. + /// + private Color backColor; + + /// + /// + /// The foreColor to paint each menu item with. + /// + private Color foreColor; + + /// + /// + /// The font used to draw the item's string. + /// + private Font font; + + /// + /// + /// The graphics object with which the drawing should be done. + /// + private readonly System.Drawing.Graphics graphics; + + /// + /// + /// The index of the item that should be painted. + /// + private readonly int index; + + /// + /// + /// The rectangle outlining the area in which the painting should be + /// done. + /// + private readonly Rectangle rect; + + /// + /// + /// Miscellaneous state information, such as whether the item is + /// "selected", "focused", or some other such information. ComboBoxes + /// have one special piece of information which indicates if the item + /// being painted is the editable portion of the ComboBox. + /// + private readonly DrawItemState state; + + /// + /// + /// Creates a new DrawItemEventArgs with the given parameters. + /// + public DrawItemEventArgs(Graphics graphics, Font font, Rectangle rect, + int index, DrawItemState state) { + this.graphics = graphics; + this.font = font; + this.rect = rect; + this.index = index; + this.state = state; + this.foreColor = SystemColors.WindowText; + this.backColor = SystemColors.Window; + } + + /// + /// + /// Creates a new DrawItemEventArgs with the given parameters, including the foreColor and backColor of the control. + /// + public DrawItemEventArgs(Graphics graphics, Font font, Rectangle rect, + int index, DrawItemState state, Color foreColor, Color backColor) { + this.graphics = graphics; + this.font = font; + this.rect = rect; + this.index = index; + this.state = state; + this.foreColor = foreColor; + this.backColor = backColor; + } + + /// + public Color BackColor { + get { + if ((state & DrawItemState.Selected) == DrawItemState.Selected) { + return SystemColors.Highlight; + } + return backColor; + } + } + + + + /// + /// + /// The rectangle outlining the area in which the painting should be + /// done. + /// + public Rectangle Bounds { + get { + return rect; + } + } + + /// + /// + /// A suggested font, usually the parent control's Font property. + /// + public Font Font { + get { + return font; + } + } + + /// + /// + /// A suggested color drawing: either SystemColors.WindowText or SystemColors.HighlightText, + /// depending on whether this item is selected. + /// + public Color ForeColor { + get { + if ((state & DrawItemState.Selected) == DrawItemState.Selected) { + return SystemColors.HighlightText; + } + return foreColor; + } + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics { + get { + return graphics; + } + } + + /// + /// + /// The index of the item that should be painted. + /// + public int Index { + get { + return index; + } + } + + /// + /// + /// Miscellaneous state information, such as whether the item is + /// "selected", "focused", or some other such information. ComboBoxes + /// have one special piece of information which indicates if the item + /// being painted is the editable portion of the ComboBox. + /// + public DrawItemState State { + get { + return state; + } + } + + /// + /// + /// Draws the background of the given rectangle with the color returned from the BackColor property. + /// + public virtual void DrawBackground() { + Brush backBrush = new SolidBrush(BackColor); + Graphics.FillRectangle(backBrush, rect); + backBrush.Dispose(); + } + + /// + /// + /// Draws a handy focus rect in the given rectangle. + /// + public virtual void DrawFocusRectangle() { + if ((state & DrawItemState.Focus) == DrawItemState.Focus + && (state & DrawItemState.NoFocusRect) != DrawItemState.NoFocusRect) + ControlPaint.DrawFocusRectangle(Graphics, rect, ForeColor, BackColor); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawItemEventHandler.cs new file mode 100644 index 000000000..933ec176d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawItemEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void DrawItemEventHandler(object sender, DrawItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawItemState.cs b/WindowsForms/Managed/System/WinForms/DrawItemState.cs new file mode 100644 index 000000000..bc411f312 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawItemState.cs @@ -0,0 +1,116 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// + /// Specifies + /// the state of an item that is being drawn. + /// + /// + [Flags] + public enum DrawItemState { + /// + /// + /// + /// The item is checked. Only menu controls use this value. + /// + /// + /// + Checked = NativeMethods.ODS_CHECKED, + /// + /// + /// + /// The item is the editing portion of a . + /// + /// + ComboBoxEdit = NativeMethods.ODS_COMBOBOXEDIT, + /// + /// + /// + /// The item is the default item of the control. + /// + /// + Default = NativeMethods.ODS_DEFAULT, + /// + /// + /// + /// The item is disabled. + /// + /// + Disabled = NativeMethods.ODS_DISABLED, + /// + /// + /// + /// The item has focus. + /// + /// + Focus = NativeMethods.ODS_FOCUS, + /// + /// + /// + /// The item + /// is grayed. Only menu controls use this value. + /// + /// + /// + Grayed = NativeMethods.ODS_GRAYED, + /// + /// + /// + /// The item is being hot-tracked. + /// + /// + HotLight = NativeMethods.ODS_HOTLIGHT, + /// + /// + /// + /// The item is inactive. + /// + /// + Inactive = NativeMethods.ODS_INACTIVE, + /// + /// + /// + /// The item displays without a keyboard accelarator. + /// + /// + NoAccelerator = NativeMethods.ODS_NOACCEL, + /// + /// + /// + /// The item displays without the visual cue that indicates it has the focus. + /// + /// + NoFocusRect = NativeMethods.ODS_NOFOCUSRECT, + /// + /// + /// + /// The item is selected. + /// + /// + Selected = NativeMethods.ODS_SELECTED, + /// + /// + /// + /// The item is in its default visual state. + /// + /// + None = 0, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventArgs.cs b/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventArgs.cs new file mode 100644 index 000000000..3deac98a2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventArgs.cs @@ -0,0 +1,218 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// This class contains the information a user needs to paint ListView column header (Details view only). + /// + public class DrawListViewColumnHeaderEventArgs : EventArgs + { + + private readonly Graphics graphics; + private readonly Rectangle bounds; + private readonly int columnIndex; + private readonly ColumnHeader header; + private readonly ListViewItemStates state; + private readonly Color foreColor; + private readonly Color backColor; + private readonly Font font; + private bool drawDefault; + + /// + /// + /// Creates a new DrawListViewColumnHeaderEventArgs with the given parameters. + /// + public DrawListViewColumnHeaderEventArgs(Graphics graphics, Rectangle bounds, int columnIndex, + ColumnHeader header, ListViewItemStates state, + Color foreColor, Color backColor, Font font) { + this.graphics = graphics; + this.bounds = bounds; + this.columnIndex = columnIndex; + this.header = header; + this.state = state; + this.foreColor = foreColor; + this.backColor = backColor; + this.font = font; + } + + + /// + /// + /// Causes the item do be drawn by the system instead of owner drawn. + /// + public bool DrawDefault { + get { + return drawDefault; + } + set { + drawDefault = value; + } + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics { + get { + return graphics; + } + } + + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Rectangle Bounds { + get { + return bounds; + } + } + + /// + /// + /// The index of this column. + /// + public int ColumnIndex { + get { + return columnIndex; + } + } + + /// + /// + /// The header object. + /// + public ColumnHeader Header { + get { + return header; + } + } + + /// + /// + /// State information pertaining to the header. + /// + public ListViewItemStates State { + get { + return state; + } + } + + /// + /// + /// Color used to draw the header's text. + /// + public Color ForeColor { + get { + return foreColor; + } + } + + /// + /// + /// Color used to draw the header's background. + /// + public Color BackColor { + get { + return backColor; + } + } + + /// + /// + /// Font used to render the header's text. + /// + public Font Font { + get { + return font; + } + } + + /// + /// + /// Draws the header's background. + /// + public void DrawBackground() { + if (Application.RenderWithVisualStyles) { + VisualStyleRenderer vsr = new VisualStyleRenderer(VisualStyleElement.Header.Item.Normal); + vsr.DrawBackground(graphics, bounds); + } + else { + using (Brush backBrush = new SolidBrush(backColor)) { + graphics.FillRectangle(backBrush, bounds); + } + + // draw the 3d header + // + Rectangle r = this.bounds; + + r.Width -= 1; + r.Height -= 1; + + // draw the dark border around the whole thing + // + graphics.DrawRectangle(SystemPens.ControlDarkDark, r); + + r.Width -= 1; + r.Height -= 1; + + // draw the light 3D border + // + graphics.DrawLine(SystemPens.ControlLightLight, r.X, r.Y, r.Right, r.Y); + graphics.DrawLine(SystemPens.ControlLightLight, r.X, r.Y, r.X, r.Bottom); + + // draw the dark 3D Border + // + graphics.DrawLine(SystemPens.ControlDark, r.X + 1, r.Bottom, r.Right, r.Bottom); + graphics.DrawLine(SystemPens.ControlDark, r.Right, r.Y + 1, r.Right, r.Bottom); + } + } + + /// + /// + /// Draws the header's text (overloaded) + /// + public void DrawText() { + HorizontalAlignment hAlign = header.TextAlign; + TextFormatFlags flags = (hAlign == HorizontalAlignment.Left) ? TextFormatFlags.Left : + ((hAlign == HorizontalAlignment.Center) ? TextFormatFlags.HorizontalCenter : + TextFormatFlags.Right); + flags |= TextFormatFlags.WordEllipsis; + + DrawText(flags); + } + + /// + /// + /// Draws the header's text (overloaded) - takes a TextFormatFlags argument. + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We want to measure the size of blank space. + // So we don't have to localize it. + ] + public void DrawText(TextFormatFlags flags) { + string text = header.Text; + int padding = TextRenderer.MeasureText(" ", font).Width; + Rectangle newBounds = Rectangle.Inflate(bounds, -padding, 0); + + TextRenderer.DrawText(graphics, text, font, newBounds, foreColor, flags); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventHandler.cs new file mode 100644 index 000000000..43f072a2f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewColumnHeaderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// Handler for the ListView's DrawColumnHeader event. + /// + public delegate void DrawListViewColumnHeaderEventHandler(object sender, DrawListViewColumnHeaderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewItemEventArgs.cs b/WindowsForms/Managed/System/WinForms/DrawListViewItemEventArgs.cs new file mode 100644 index 000000000..73ca7aed6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewItemEventArgs.cs @@ -0,0 +1,193 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// This class contains the information a user needs to paint ListView items. + /// + public class DrawListViewItemEventArgs : EventArgs { + + private readonly Graphics graphics; + private readonly ListViewItem item; + private readonly Rectangle bounds; + private readonly int itemIndex; + private readonly ListViewItemStates state; + private bool drawDefault; + + /// + /// + /// Creates a new DrawListViewItemEventArgs with the given parameters. + /// + public DrawListViewItemEventArgs(Graphics graphics, ListViewItem item, Rectangle bounds, + int itemIndex, ListViewItemStates state) + { + this.graphics = graphics; + this.item = item; + this.bounds = bounds; + this.itemIndex = itemIndex; + this.state = state; + this.drawDefault = false; + } + + /// + /// + /// Causes the item do be drawn by the system instead of owner drawn. + /// + public bool DrawDefault { + get { + return drawDefault; + } + set { + drawDefault = value; + } + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics + { + get + { + return graphics; + } + } + + /// + /// + /// The item to be painted. + /// + public ListViewItem Item + { + get + { + return item; + } + } + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Rectangle Bounds + { + get + { + return bounds; + } + } + + + /// + /// + /// The index of the item that should be painted. + /// + public int ItemIndex + { + get + { + return itemIndex; + } + } + + /// + /// + /// Miscellaneous state information. + /// + public ListViewItemStates State + { + get + { + return state; + } + } + + /// + /// + /// Draws the item's background. + /// + public void DrawBackground() + { + Brush backBrush = new SolidBrush(item.BackColor); + Graphics.FillRectangle(backBrush, bounds); + backBrush.Dispose(); + } + + /// + /// + /// Draws a focus rectangle in the given bounds, if the item is focused. In Details View, if FullRowSelect is + /// true, the rectangle is drawn around the whole item, else around the first sub-item's text area. + /// + public void DrawFocusRectangle() + { + if ((state & ListViewItemStates.Focused) == ListViewItemStates.Focused) { + + Rectangle focusBounds = bounds; + ControlPaint.DrawFocusRectangle(graphics, UpdateBounds(focusBounds, false /*drawText*/), item.ForeColor, item.BackColor); + } + } + + /// + /// + /// Draws the item's text (overloaded) - useful only when View != View.Details + /// + public void DrawText() + { + DrawText(TextFormatFlags.Left); + } + + /// + /// + /// Draws the item's text (overloaded) - useful only when View != View.Details - takes a TextFormatFlags argument. + /// + public void DrawText(TextFormatFlags flags) + { + TextRenderer.DrawText(graphics, item.Text, item.Font, UpdateBounds(bounds, true /*drawText*/), item.ForeColor, flags); + } + + private Rectangle UpdateBounds(Rectangle originalBounds, bool drawText) { + Rectangle resultBounds = originalBounds; + if (item.ListView.View == View.Details) { + + // Note: this logic will compute the bounds so they align w/ the system drawn bounds only + // for the default font. + if (!item.ListView.FullRowSelect && item.SubItems.Count > 0) { + ListViewItem.ListViewSubItem subItem = item.SubItems[0]; + Size textSize = TextRenderer.MeasureText(subItem.Text, subItem.Font); + + resultBounds = new Rectangle(originalBounds.X, originalBounds.Y, textSize.Width, textSize.Height); + + // Add some padding so we paint like the system control paints. + resultBounds.X += 4; + resultBounds.Width ++; + } + else + { + resultBounds.X +=4; + resultBounds.Width -= 4; + } + + if (drawText) { + resultBounds.X --; + } + } + + return resultBounds; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawListViewItemEventHandler.cs new file mode 100644 index 000000000..19d7b97ca --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewItemEventHandler.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// Handler for the ListView's DrawItem event. + /// + public delegate void DrawListViewItemEventHandler(object sender, DrawListViewItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventArgs.cs b/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventArgs.cs new file mode 100644 index 000000000..a36103cec --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventArgs.cs @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// This class contains the information a user needs to paint ListView sub-items (Details view only). + /// + public class DrawListViewSubItemEventArgs : EventArgs + { + + private readonly Graphics graphics; + private readonly Rectangle bounds; + private readonly ListViewItem item; + private readonly ListViewItem.ListViewSubItem subItem; + private readonly int itemIndex; + private readonly int columnIndex; + private readonly ColumnHeader header; + private readonly ListViewItemStates itemState; + private bool drawDefault; + + /// + /// + /// Creates a new DrawListViewSubItemEventArgs with the given parameters. + /// + public DrawListViewSubItemEventArgs(Graphics graphics, Rectangle bounds, ListViewItem item, + ListViewItem.ListViewSubItem subItem, int itemIndex, int columnIndex, + ColumnHeader header, ListViewItemStates itemState) + { + this.graphics = graphics; + this.bounds = bounds; + this.item = item; + this.subItem = subItem; + this.itemIndex = itemIndex; + this.columnIndex = columnIndex; + this.header = header; + this.itemState = itemState; + } + + + /// + /// + /// Causes the item do be drawn by the system instead of owner drawn. + /// + public bool DrawDefault { + get { + return drawDefault; + } + set { + drawDefault = value; + } + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics + { + get + { + return graphics; + } + } + + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Rectangle Bounds + { + get + { + return bounds; + } + } + + /// + /// + /// The parent item. + /// + public ListViewItem Item + { + get + { + return item; + } + } + + /// + /// + /// The parent item. + /// + public ListViewItem.ListViewSubItem SubItem + { + get + { + return subItem; + } + } + + /// + /// + /// The index in the ListView of the parent item. + /// + public int ItemIndex + { + get + { + return itemIndex; + } + } + + /// + /// + /// The column index of this sub-item. + /// + public int ColumnIndex + { + get + { + return columnIndex; + } + } + + /// + /// + /// The header of this sub-item's column + /// + public ColumnHeader Header + { + get + { + return header; + } + } + + /// + /// + /// Miscellaneous state information pertaining to the parent item. + /// + public ListViewItemStates ItemState + { + get + { + return itemState; + } + } + + /// + /// + /// Draws the sub-item's background. + /// + public void DrawBackground() + { + Color backColor = (itemIndex == -1) ? item.BackColor : subItem.BackColor; + using (Brush backBrush = new SolidBrush(backColor)) { + Graphics.FillRectangle(backBrush, bounds); + } + } + + /// + /// + /// Draws a focus rectangle in the given bounds, if the item has focus. + /// + public void DrawFocusRectangle(Rectangle bounds) + { + if((itemState & ListViewItemStates.Focused) == ListViewItemStates.Focused) + { + ControlPaint.DrawFocusRectangle(graphics, Rectangle.Inflate(bounds, -1, -1), item.ForeColor, item.BackColor); + } + } + + /// + /// + /// Draws the sub-item's text (overloaded) + /// + public void DrawText() + { + // Map the ColumnHeader::TextAlign to the TextFormatFlags. + HorizontalAlignment hAlign = header.TextAlign; + TextFormatFlags flags = (hAlign == HorizontalAlignment.Left) ? TextFormatFlags.Left : + ((hAlign == HorizontalAlignment.Center) ? TextFormatFlags.HorizontalCenter : + TextFormatFlags.Right); + flags |= TextFormatFlags.WordEllipsis; + + DrawText(flags); + } + + /// + /// + /// Draws the sub-item's text (overloaded) - takes a TextFormatFlags argument. + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We want to measure the size of blank space. + // So we don't have to localize it. + ] + public void DrawText(TextFormatFlags flags) + { + string text = (itemIndex == -1) ? item.Text : subItem.Text; + Font font = (itemIndex == -1) ? item.Font : subItem.Font; + Color color = (itemIndex == -1) ? item.ForeColor : subItem.ForeColor; + int padding = TextRenderer.MeasureText(" ", font).Width; + Rectangle newBounds = Rectangle.Inflate(bounds, -padding, 0); + + TextRenderer.DrawText(graphics, text, font, newBounds, color, flags); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventHandler.cs new file mode 100644 index 000000000..91c1cacf7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawListViewSubItemEventHandler.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + + using System; + + + /// + /// + /// Handler for the ListView's DrawSubItem event. + /// + public delegate void DrawListViewSubItemEventHandler(object sender, DrawListViewSubItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawMode.cs b/WindowsForms/Managed/System/WinForms/DrawMode.cs new file mode 100644 index 000000000..bfd29a5f0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawMode.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// + /// Specifies responsibility for drawing a control or portion of a control. + /// + /// + /// + public enum DrawMode { + /// + /// + /// + /// The + /// operating system paints the items in the control, and the items are each the + /// same height. + /// + /// + /// + Normal = 0, + + /// + /// + /// + /// The + /// programmer explicitly paints the items in the control, and the items are + /// each the same height. + /// + /// + /// + OwnerDrawFixed = 1, + + /// + /// + /// + /// The programmer explicitly paints the items in the control manually, and they + /// may be different heights. + /// + /// + OwnerDrawVariable = 2, + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawToolTipEventArgs.cs b/WindowsForms/Managed/System/WinForms/DrawToolTipEventArgs.cs new file mode 100644 index 000000000..03ed4878f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawToolTipEventArgs.cs @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + using System; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + + /// + /// + /// This class contains the information a user needs to paint the ToolTip. + /// + public class DrawToolTipEventArgs : EventArgs + { + + private readonly Graphics graphics; + private readonly IWin32Window associatedWindow; + private readonly Control associatedControl; + private readonly Rectangle bounds; + private readonly string toolTipText; + private readonly Color backColor; + private readonly Color foreColor; + private readonly Font font; + + /// + /// + /// Creates a new DrawToolTipEventArgs with the given parameters. + /// + public DrawToolTipEventArgs(Graphics graphics, IWin32Window associatedWindow, Control associatedControl, Rectangle bounds, string toolTipText, + Color backColor, Color foreColor, Font font) + { + this.graphics = graphics; + this.associatedWindow = associatedWindow; + this.associatedControl = associatedControl; + this.bounds = bounds; + this.toolTipText = toolTipText; + this.backColor = backColor; + this.foreColor = foreColor; + this.font = font; + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics + { + get + { + return graphics; + } + } + + /// + /// + /// The window for which the tooltip is being painted. + /// + public IWin32Window AssociatedWindow { + get { + return associatedWindow; + } + } + + /// + /// + /// The control for which the tooltip is being painted. + /// + public Control AssociatedControl + { + get + { + return associatedControl; + } + } + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Rectangle Bounds + { + get + { + return bounds; + } + } + + + /// + /// + /// The text that should be drawn. + /// + public string ToolTipText + { + get + { + return toolTipText; + } + } + + /// + /// + /// The font used to draw tooltip text. + /// + public Font Font + { + get + { + return font; + } + } + + /// + /// + /// Draws the background of the ToolTip. + /// + public void DrawBackground() + { + Brush backBrush = new SolidBrush(backColor); + Graphics.FillRectangle(backBrush, bounds); + backBrush.Dispose(); + } + + /// + /// + /// Draws the text (overloaded) + /// + public void DrawText() + { + //Pass in a set of flags to mimic default behavior + DrawText(TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.HidePrefix); + } + + /// + /// + /// Draws the text (overloaded) - takes a TextFormatFlags argument. + /// + public void DrawText(TextFormatFlags flags) + { + TextRenderer.DrawText(graphics, toolTipText, font, bounds, foreColor, flags); + } + + /// + /// + /// Draws a border for the ToolTip similar to the default border. + /// + public void DrawBorder() + { + ControlPaint.DrawBorder(graphics, bounds, SystemColors.WindowFrame, ButtonBorderStyle.Solid); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawToolTipEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawToolTipEventHandler.cs new file mode 100644 index 000000000..a605488a2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawToolTipEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// Handler for the Draw event of the ToolTip control. + /// + public delegate void DrawToolTipEventHandler(object sender, DrawToolTipEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventArgs.cs b/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventArgs.cs new file mode 100644 index 000000000..d7493d2f1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventArgs.cs @@ -0,0 +1,107 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// This class contains the information a user needs to paint TreeView nodes. + /// + public class DrawTreeNodeEventArgs : EventArgs + { + + private readonly Graphics graphics; + private readonly TreeNode node; + private readonly Rectangle bounds; + private readonly TreeNodeStates state; + private bool drawDefault; + + /// + /// + /// Creates a new DrawTreeNodeEventArgs with the given parameters. + /// + public DrawTreeNodeEventArgs(Graphics graphics, TreeNode node, Rectangle bounds, + TreeNodeStates state) + { + this.graphics = graphics; + this.node = node; + this.bounds = bounds; + this.state = state; + this.drawDefault = false; + } + + /// + /// + /// Causes the item do be drawn by the system instead of owner drawn. + /// NOTE: In OwnerDrawText mode, setting this to true is same as calling DrawText. + /// + public bool DrawDefault { + get { + return drawDefault; + } + set { + drawDefault = value; + } + } + + /// + /// + /// Graphics object with which painting should be done. + /// + public Graphics Graphics + { + get + { + return graphics; + } + } + + /// + /// + /// The node to be painted. + /// + public TreeNode Node + { + get + { + return node; + } + } + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Rectangle Bounds + { + get + { + return bounds; + } + } + + + /// + /// + /// Miscellaneous state information. + /// + public TreeNodeStates State + { + get + { + return state; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventHandler.cs b/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventHandler.cs new file mode 100644 index 000000000..f93621d59 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DrawTreeNodeEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// Handler for the DrawTreeNode event of the TreeView control. + /// + public delegate void DrawTreeNodeEventHandler(object sender, DrawTreeNodeEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/DropSource.cs b/WindowsForms/Managed/System/WinForms/DropSource.cs new file mode 100644 index 000000000..0b646a293 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DropSource.cs @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms +{ + using System; + + /// + internal class DropSource : UnsafeNativeMethods.IOleDropSource { + + private const int DragDropSDrop = 0x00040100; + private const int DragDropSCancel = 0x00040101; + private const int DragDropSUseDefaultCursors = 0x00040102; + + private ISupportOleDropSource peer; + + public DropSource(ISupportOleDropSource peer ) { + if (peer == null) + throw new ArgumentNullException("peer"); + this.peer = peer; + } + + public int OleQueryContinueDrag(int fEscapePressed, int grfKeyState) { + QueryContinueDragEventArgs qcdevent = null; + bool escapePressed = (fEscapePressed != 0); + DragAction action = DragAction.Continue; + if (escapePressed) { + action = DragAction.Cancel; + } + else if ((grfKeyState & NativeMethods.MK_LBUTTON) == 0 + && (grfKeyState & NativeMethods.MK_RBUTTON) == 0 + && (grfKeyState & NativeMethods.MK_MBUTTON) == 0) { + action = DragAction.Drop; + } + + qcdevent = new QueryContinueDragEventArgs(grfKeyState,escapePressed, action); + peer.OnQueryContinueDrag(qcdevent); + + int hr = 0; + + switch (qcdevent.Action) { + case DragAction.Drop: + hr = DragDropSDrop; + break; + case DragAction.Cancel: + hr = DragDropSCancel; + break; + } + + return hr; + } + + public int OleGiveFeedback(int dwEffect) { + GiveFeedbackEventArgs gfbevent = new GiveFeedbackEventArgs((DragDropEffects) dwEffect, true); + peer.OnGiveFeedback(gfbevent); + if (gfbevent.UseDefaultCursors) { + return DragDropSUseDefaultCursors; + } + return 0; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/DropTarget.cs b/WindowsForms/Managed/System/WinForms/DropTarget.cs new file mode 100644 index 000000000..eb4a59b5b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/DropTarget.cs @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Security.Permissions; + using System.Security; + using System.ComponentModel; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + + /// + /// + internal class DropTarget : UnsafeNativeMethods.IOleDropTarget { + private IDataObject lastDataObject = null; + private DragDropEffects lastEffect = DragDropEffects.None; + private IDropTarget owner; + + public DropTarget(IDropTarget owner) { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "DropTarget created"); + this.owner = owner; + } + +#if DEBUG + ~DropTarget() { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "DropTarget destroyed"); + } +#endif + + private DragEventArgs CreateDragEventArgs(object pDataObj, int grfKeyState, NativeMethods.POINTL pt, int pdwEffect) { + + IDataObject data = null; + + if (pDataObj == null) { + data = lastDataObject; + } + else { + if (pDataObj is IDataObject) { + data = (IDataObject)pDataObj; + } + else if (pDataObj is IComDataObject) { + data = new DataObject(pDataObj); + } + else { + return null; // Unknown data object interface; we can't work with this so return null + } + } + + DragEventArgs drgevent = new DragEventArgs(data, grfKeyState, pt.x, pt.y, (DragDropEffects)pdwEffect, lastEffect); + lastDataObject = data; + return drgevent; + } + + int UnsafeNativeMethods.IOleDropTarget.OleDragEnter(object pDataObj, int grfKeyState, + UnsafeNativeMethods.POINTSTRUCT pt, + ref int pdwEffect) { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "OleDragEnter recieved"); + NativeMethods.POINTL ptl = new NativeMethods.POINTL(); + ptl.x = pt.x; + ptl.y = pt.y; + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "\t" + (ptl.x) + "," + (ptl.y)); + Debug.Assert(pDataObj != null, "OleDragEnter didn't give us a valid data object."); + DragEventArgs drgevent = CreateDragEventArgs(pDataObj, grfKeyState, ptl, pdwEffect); + + if (drgevent != null) { + owner.OnDragEnter(drgevent); + pdwEffect = (int)drgevent.Effect; + lastEffect = drgevent.Effect; + } + else { + pdwEffect = (int)DragDropEffects.None; + } + return NativeMethods.S_OK; + } + int UnsafeNativeMethods.IOleDropTarget.OleDragOver(int grfKeyState, UnsafeNativeMethods.POINTSTRUCT pt, ref int pdwEffect) { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "OleDragOver recieved"); + NativeMethods.POINTL ptl = new NativeMethods.POINTL(); + ptl.x = pt.x; + ptl.y = pt.y; + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "\t" + (ptl.x) + "," + (ptl.y)); + DragEventArgs drgevent = CreateDragEventArgs(null, grfKeyState, ptl, pdwEffect); + owner.OnDragOver(drgevent); + pdwEffect = (int)drgevent.Effect; + lastEffect = drgevent.Effect; + return NativeMethods.S_OK; + } + int UnsafeNativeMethods.IOleDropTarget.OleDragLeave() { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "OleDragLeave recieved"); + owner.OnDragLeave(EventArgs.Empty); + return NativeMethods.S_OK; + } + int UnsafeNativeMethods.IOleDropTarget.OleDrop(object pDataObj, int grfKeyState, UnsafeNativeMethods.POINTSTRUCT pt, ref int pdwEffect) { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "OleDrop recieved"); + NativeMethods.POINTL ptl = new NativeMethods.POINTL(); + ptl.x = pt.x; + ptl.y = pt.y; + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "\t" + (ptl.x) + "," + (ptl.y)); + DragEventArgs drgevent = CreateDragEventArgs(pDataObj, grfKeyState, ptl, pdwEffect); + + if (drgevent != null) { + owner.OnDragDrop(drgevent); + pdwEffect = (int)drgevent.Effect; + } + else { + pdwEffect = (int)DragDropEffects.None; + } + + lastEffect = DragDropEffects.None; + lastDataObject = null; + return NativeMethods.S_OK; + } + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/EnumValAlphaComparer.cs b/WindowsForms/Managed/System/WinForms/EnumValAlphaComparer.cs new file mode 100644 index 000000000..0d0f72059 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/EnumValAlphaComparer.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.Globalization; + + internal class EnumValAlphaComparer : IComparer { + private CompareInfo m_compareInfo; + internal static readonly EnumValAlphaComparer Default = new EnumValAlphaComparer(); + + internal EnumValAlphaComparer() { + m_compareInfo = CultureInfo.InvariantCulture.CompareInfo; + } + + public int Compare(Object a, Object b) { + return m_compareInfo.Compare(a.ToString(), b.ToString()); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ErrorBlinkStyle.cs b/WindowsForms/Managed/System/WinForms/ErrorBlinkStyle.cs new file mode 100644 index 000000000..590e84b6c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ErrorBlinkStyle.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + /// + /// + /// Describes the times that the error icon supplied by an ErrorProvider + /// should blink to alert the user that an error has occurred. + /// + public enum ErrorBlinkStyle { + /// + /// + /// Blink only if the error icon is already displayed, but a new + /// error string is set for the control. If the icon did not blink + /// in this case, the user might not know that there is a new error. + /// + BlinkIfDifferentError, + + /// + /// + /// Blink the error icon when the error is first displayed, or when + /// a new error description string is set for the control and the + /// error icon is already displayed. + /// + AlwaysBlink, + + /// + /// + /// Never blink the error icon. + /// + NeverBlink + } +} diff --git a/WindowsForms/Managed/System/WinForms/ErrorIconAlignment.cs b/WindowsForms/Managed/System/WinForms/ErrorIconAlignment.cs new file mode 100644 index 000000000..25b5abf6f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ErrorIconAlignment.cs @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + /// + /// + /// Describes the set of locations that an error icon can appear in + /// relation to the control with the error. + /// + public enum ErrorIconAlignment { + + /// + /// + /// The icon appears aligned with the top of the control, and to + /// the left of the control. + /// + TopLeft, + + /// + /// + /// The icon appears aligned with the top of the control, and to + /// the right of the control. + /// + TopRight, + + /// + /// + /// The icon appears aligned with the middle of the control, and + /// the left of the control. + /// + MiddleLeft, + + /// + /// + /// The icon appears aligned with the middle of the control, and + /// the right of the control. + /// + MiddleRight, + + /// + /// + /// The icon appears aligned with the bottom of the control, and + /// the left of the control. + /// + BottomLeft, + + /// + /// + /// The icon appears aligned with the bottom of the control, and + /// the right of the control. + /// + BottomRight + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ErrorProvider.cs b/WindowsForms/Managed/System/WinForms/ErrorProvider.cs new file mode 100644 index 000000000..3c296952b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ErrorProvider.cs @@ -0,0 +1,1763 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Collections; + using System.Globalization; + using System.Drawing; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Windows.Forms.Internal; + using System.Security.Permissions; + using System.Runtime.Versioning; + + /// + /// + /// ErrorProvider presents a simple user interface for indicating to the + /// user that a control on a form has an error associated with it. If a + /// error description string is specified for the control, then an icon + /// will appear next to the control, and when the mouse hovers over the + /// icon, a tooltip will appear showing the error description string. + /// + [ + ProvideProperty("IconPadding", typeof(Control)), + ProvideProperty("IconAlignment", typeof(Control)), + ProvideProperty("Error", typeof(Control)), + ToolboxItemFilter("System.Windows.Forms"), + ComplexBindingProperties("DataSource", "DataMember"), + SRDescription(SR.DescriptionErrorProvider) + ] + public class ErrorProvider : Component, IExtenderProvider, ISupportInitialize { + + // + // FIELDS + // + + Hashtable items = new Hashtable(); + Hashtable windows = new Hashtable(); + Icon icon = DefaultIcon; + IconRegion region; + int itemIdCounter; + int blinkRate; + ErrorBlinkStyle blinkStyle; + bool showIcon = true; // used for blinking + private bool inSetErrorManager = false; + private bool setErrorManagerOnEndInit = false; + private bool initializing = false; + [ThreadStatic] + static Icon defaultIcon = null; + const int defaultBlinkRate = 250; + const ErrorBlinkStyle defaultBlinkStyle = ErrorBlinkStyle.BlinkIfDifferentError; + const ErrorIconAlignment defaultIconAlignment = ErrorIconAlignment.MiddleRight; + + // data binding + private ContainerControl parentControl; + private object dataSource = null; + private string dataMember = null; + private BindingManagerBase errorManager; + private EventHandler currentChanged; + + // listen to the OnPropertyChanged event in the ContainerControl + private EventHandler propChangedEvent; + + private EventHandler onRightToLeftChanged; + private bool rightToLeft = false; + + private object userData; + + // + // CONSTRUCTOR + // + + /// + /// + /// Default constructor. + /// + public ErrorProvider() { + icon = DefaultIcon; + blinkRate = defaultBlinkRate; + blinkStyle = defaultBlinkStyle; + currentChanged = new EventHandler(ErrorManager_CurrentChanged); + } + + /// + /// + /// [To be supplied.] + /// + public ErrorProvider(ContainerControl parentControl) : this() { + this.parentControl = parentControl; + propChangedEvent = new EventHandler(ParentControl_BindingContextChanged); + parentControl.BindingContextChanged += propChangedEvent; + } + + /// + /// + /// [To be supplied.] + /// + public ErrorProvider(IContainer container) : this() { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + // + // PROPERTIES + // + + /// + public override ISite Site { + set { + base.Site = value; + if (value == null) + return; + + IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost; + if (host != null) { + IComponent baseComp = host.RootComponent; + + if (baseComp is ContainerControl) { + this.ContainerControl = (ContainerControl) baseComp; + } + } + } + } + + /// + /// + /// Returns or sets when the error icon flashes. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(defaultBlinkStyle), + SRDescription(SR.ErrorProviderBlinkStyleDescr) + ] + public ErrorBlinkStyle BlinkStyle { + get { + if (blinkRate == 0) { + return ErrorBlinkStyle.NeverBlink; + } + return blinkStyle; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ErrorBlinkStyle.BlinkIfDifferentError, (int)ErrorBlinkStyle.NeverBlink)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ErrorBlinkStyle)); + } + + // If the blinkRate == 0, then set blinkStyle = neverBlink + // + if (this.blinkRate == 0) { + value = ErrorBlinkStyle.NeverBlink; + } + + if (this.blinkStyle == value) { + return; + } + + if (value == ErrorBlinkStyle.AlwaysBlink) { + // we need to startBlinking on all the controlItems + // in our items hashTable. + this.showIcon = true; + this.blinkStyle = ErrorBlinkStyle.AlwaysBlink; + foreach (ErrorWindow w in windows.Values) + { + w.StartBlinking(); + } + } + else if (blinkStyle == ErrorBlinkStyle.AlwaysBlink) { + // we need to stop blinking... + this.blinkStyle = value; + foreach (ErrorWindow w in windows.Values) { + w.StopBlinking(); + } + } + else { + this.blinkStyle = value; + } + } + } + + /// + /// + /// Indicates what container control (usually the form) should be inspected for bindings. + /// A binding will reveal what control to place errors on for a + /// error in the current row in the DataSource/DataMember pair. + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + SRDescription(SR.ErrorProviderContainerControlDescr) + ] + public ContainerControl ContainerControl { + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + get { + return parentControl; + } + set { + if (parentControl != value) { + if (parentControl != null) + parentControl.BindingContextChanged -= propChangedEvent; + + parentControl = value; + + if (parentControl != null) + parentControl.BindingContextChanged += propChangedEvent; + + Set_ErrorManager(this.DataSource, this.DataMember, true); + } + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftDescr) + ] + public virtual bool RightToLeft { + get { + + return rightToLeft; + } + + set { + if (value != rightToLeft) { + rightToLeft = value; + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftChangedDescr)] + public event EventHandler RightToLeftChanged { + add { + onRightToLeftChanged += value; + } + remove { + onRightToLeftChanged -= value; + } + } + + /// + /// + /// User defined data associated with the control. + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + private void Set_ErrorManager(object newDataSource, string newDataMember, bool force) { + if (inSetErrorManager) + return; + inSetErrorManager = true; + try + { + bool dataSourceChanged = this.DataSource != newDataSource; + bool dataMemberChanged = this.DataMember != newDataMember; + + //if nothing changed, then do not do any work + // + if (!dataSourceChanged && !dataMemberChanged && !force) + { + return; + } + + // set the dataSource and the dataMember + // + this.dataSource = newDataSource; + this.dataMember = newDataMember; + + if (initializing) { + setErrorManagerOnEndInit = true; + } + else { + // unwire the errorManager: + // + UnwireEvents(errorManager); + + // get the new errorManager + // + if (parentControl != null && this.dataSource != null && parentControl.BindingContext != null) { + errorManager = parentControl.BindingContext[this.dataSource, this.dataMember]; + } + else { + errorManager = null; + } + + // wire the events + // + WireEvents(errorManager); + + // see if there are errors at the current + // item in the list, w/o waiting for the position to change + if (errorManager != null) + UpdateBinding(); + } + } finally { + inSetErrorManager = false; + } + } + + /// + /// + /// Indicates the source of data to bind errors against. + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.ErrorProviderDataSourceDescr) + ] + public object DataSource { + get { + return dataSource; + } + set { + if (parentControl != null && value != null && !String.IsNullOrEmpty(this.dataMember)) { + // Let's check if the datamember exists in the new data source + try { + errorManager = parentControl.BindingContext[value, this.dataMember]; + } + catch (ArgumentException) { + // The data member doesn't exist in the data source, so set it to null + this.dataMember = ""; + } + } + + Set_ErrorManager(value, this.DataMember, false); + } + } + + /// + /// + /// [To be supplied.] + /// + private bool ShouldSerializeDataSource() { + return (dataSource != null); + } + + /// + /// + /// Indicates the sub-list of data from the DataSource to bind errors against. + /// + [ + DefaultValue(null), + SRCategory(SR.CatData), + Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.ErrorProviderDataMemberDescr) + ] + public string DataMember { + get { + return dataMember; + } + set { + if (value == null) value = ""; + Set_ErrorManager(this.DataSource, value, false); + } + } + + /// + /// + /// [To be supplied.] + /// + private bool ShouldSerializeDataMember() { + return (dataMember != null && dataMember.Length != 0); + } + + /// + /// + /// [To be supplied.] + /// + public void BindToDataAndErrors(object newDataSource, string newDataMember) { + Set_ErrorManager(newDataSource, newDataMember, false); + } + + private void WireEvents(BindingManagerBase listManager) { + if (listManager != null) { + listManager.CurrentChanged += currentChanged; + listManager.BindingComplete += new BindingCompleteEventHandler(this.ErrorManager_BindingComplete); + + CurrencyManager currManager = listManager as CurrencyManager; + + if (currManager != null) { + currManager.ItemChanged += new ItemChangedEventHandler(this.ErrorManager_ItemChanged); + currManager.Bindings.CollectionChanged += new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged); + } + } + } + + private void UnwireEvents(BindingManagerBase listManager) { + if (listManager != null) { + listManager.CurrentChanged -= currentChanged; + listManager.BindingComplete -= new BindingCompleteEventHandler(this.ErrorManager_BindingComplete); + + CurrencyManager currManager = listManager as CurrencyManager; + + if (currManager != null) { + currManager.ItemChanged -= new ItemChangedEventHandler(this.ErrorManager_ItemChanged); + currManager.Bindings.CollectionChanged -= new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged); + } + } + } + + private void ErrorManager_BindingComplete(object sender, BindingCompleteEventArgs e) { + Binding binding = e.Binding; + + if (binding != null && binding.Control != null) { + SetError(binding.Control, (e.ErrorText == null ? String.Empty : e.ErrorText)); + } + } + + private void ErrorManager_BindingsChanged(object sender, CollectionChangeEventArgs e) { + ErrorManager_CurrentChanged(errorManager, e); + } + + private void ParentControl_BindingContextChanged(object sender, EventArgs e) { + Set_ErrorManager(this.DataSource, this.DataMember, true); + } + + // Work around... we should figure out if errors changed automatically. + /// + /// + /// [To be supplied.] + /// + public void UpdateBinding() { + ErrorManager_CurrentChanged(errorManager, EventArgs.Empty); + } + + private void ErrorManager_ItemChanged(object sender, ItemChangedEventArgs e) { + BindingsCollection errBindings = errorManager.Bindings; + int bindingsCount = errBindings.Count; + + // If the list became empty then reset the errors + if (e.Index == -1 && errorManager.Count == 0) { + for (int j = 0; j < bindingsCount; j++) { + if (errBindings[j].Control != null) { + // ...ignore everything but bindings to Controls + SetError(errBindings[j].Control, ""); + } + } + } + else { + ErrorManager_CurrentChanged(sender, e); + } + } + + private void ErrorManager_CurrentChanged(object sender, EventArgs e) { + Debug.Assert(sender == errorManager, "who else can send us messages?"); + + // flush the old list + // + // items.Clear(); + + if (errorManager.Count == 0) { + return; + } + + object value = errorManager.Current; + if ( !(value is IDataErrorInfo)) { + return; + } + + BindingsCollection errBindings = errorManager.Bindings; + int bindingsCount = errBindings.Count; + + // we need to delete the blinkPhases from each controlItem (suppose + // that the error that we get is the same error. then we want to + // show the error and not blink ) + // + foreach (ControlItem ctl in items.Values) { + ctl.BlinkPhase = 0; + } + + // We can only show one error per control, so we will build up a string... + // + Hashtable controlError = new Hashtable(bindingsCount); + + for (int j = 0; j < bindingsCount; j++) { + + // Ignore everything but bindings to Controls + if (errBindings[j].Control == null) { + continue; + } + + BindToObject dataBinding = errBindings[j].BindToObject; + string error = ((IDataErrorInfo) value)[dataBinding.BindingMemberInfo.BindingField]; + + if (error == null) { + error = ""; + } + + string outputError = ""; + + if (controlError.Contains(errBindings[j].Control)) + outputError = (string) controlError[errBindings[j].Control]; + + // VSWhidbey 106890: Utilize the error string without including the field name. + if (String.IsNullOrEmpty(outputError)) { + outputError = error; + } else { + outputError = string.Concat(outputError, "\r\n", error); + } + + controlError[errBindings[j].Control] = outputError; + } + + IEnumerator enumerator = controlError.GetEnumerator(); + while (enumerator.MoveNext()) { + DictionaryEntry entry = (DictionaryEntry) enumerator.Current; + SetError((Control) entry.Key, (string) entry.Value); + } + } + + /// + /// + /// Returns or set the rate in milliseconds at which the error icon flashes. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(defaultBlinkRate), + SRDescription(SR.ErrorProviderBlinkRateDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int BlinkRate { + get { + return blinkRate; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("BlinkRate", value, SR.GetString(SR.BlinkRateMustBeZeroOrMore)); + } + blinkRate = value; + // If we set the blinkRate = 0 then set BlinkStyle = NeverBlink + if (blinkRate == 0) + BlinkStyle = ErrorBlinkStyle.NeverBlink; + } + } + + /// + /// + /// Demand load and cache the default icon. + /// + /// + static Icon DefaultIcon { + get { + if (defaultIcon == null) { + lock (typeof(ErrorProvider)) { + if (defaultIcon == null) { + defaultIcon = new Icon(typeof(ErrorProvider), "Error.ico"); + } + } + } + return defaultIcon; + } + } + + /// + /// + /// Returns or sets the Icon that displayed next to a control when an error + /// description string has been set for the control. For best results, an + /// icon containing a 16 by 16 icon should be used. + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ErrorProviderIconDescr) + ] + public Icon Icon { + get { + return icon; + } + set { + if (value == null) + throw new ArgumentNullException("value"); + icon = value; + DisposeRegion(); + ErrorWindow[] array = new ErrorWindow[windows.Values.Count]; + windows.Values.CopyTo(array, 0); + for (int i = 0; i < array.Length; i++) + array[i].Update(false /*timerCaused*/); + } + } + + /// + /// + /// Create the icon region on demand. + /// + /// + internal IconRegion Region { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (region == null) + region = new IconRegion(Icon); + return region; + } + } + + // + // METHODS + // + + // Begin bulk member initialization - deferring binding to data source until EndInit is reached + void ISupportInitialize.BeginInit() { + initializing = true; + } + + // End bulk member initialization by binding to data source + private void EndInitCore() { + initializing = false; + + if (setErrorManagerOnEndInit) { + setErrorManagerOnEndInit = false; + Set_ErrorManager(this.DataSource, this.DataMember, true); + } + } + + // Check to see if DataSource has completed its initialization, before ending our initialization. + // If DataSource is still initializing, hook its Initialized event and wait for it to signal completion. + // If DataSource is already initialized, just go ahead and complete our initialization now. + // + void ISupportInitialize.EndInit() { + ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); + + if (dsInit != null && !dsInit.IsInitialized) { + dsInit.Initialized += new EventHandler(DataSource_Initialized); + } + else { + EndInitCore(); + } + } + + // Respond to late completion of the DataSource's initialization, by completing our own initialization. + // This situation can arise if the call to the DataSource's EndInit() method comes after the call to the + // BindingSource's EndInit() method (since code-generated ordering of these calls is non-deterministic). + // + private void DataSource_Initialized(object sender, EventArgs e) { + ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); + + Debug.Assert(dsInit != null, "ErrorProvider: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!"); + Debug.Assert(dsInit.IsInitialized, "ErrorProvider: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing."); + + if (dsInit != null) { + dsInit.Initialized -= new EventHandler(DataSource_Initialized); + } + + EndInitCore(); + } + + /// + /// + /// Clears all errors being tracked by this error provider, ie. undoes all previous calls to SetError. + /// + public void Clear() { + ErrorWindow[] w = new ErrorWindow[windows.Values.Count]; + windows.Values.CopyTo(w, 0); + for (int i = 0; i < w.Length; i++) { + w[i].Dispose(); + } + windows.Clear(); + foreach (ControlItem item in items.Values) { + if (item != null) { + item.Dispose(); + } + } + items.Clear(); + } + + /// + /// + /// Returns whether a control can be extended. + /// + public bool CanExtend(object extendee) { + return extendee is Control && !(extendee is Form) && !(extendee is ToolBar); + } + + /// + /// + /// Release any resources that this component is using. After calling Dispose, + /// the component should no longer be used. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + Clear(); + DisposeRegion(); + UnwireEvents(errorManager); + } + base.Dispose(disposing); + } + + /// + /// + /// Helper to dispose the cached icon region. + /// + /// + void DisposeRegion() { + if (region != null) { + region.Dispose(); + region = null; + } + } + + /// + /// + /// Helper to make sure we have allocated a control item for this control. + /// + /// + private ControlItem EnsureControlItem(Control control) { + if (control == null) + throw new ArgumentNullException("control"); + ControlItem item = (ControlItem)items[control]; + if (item == null) { + item = new ControlItem(this, control, (IntPtr)(++itemIdCounter)); + items[control] = item; + } + return item; + } + + /// + /// + /// Helper to make sure we have allocated an error window for this control. + /// + /// + internal ErrorWindow EnsureErrorWindow(Control parent) { + ErrorWindow window = (ErrorWindow)windows[parent]; + if (window == null) { + window = new ErrorWindow(this, parent); + windows[parent] = window; + } + return window; + } + + /// + /// + /// Returns the current error description string for the specified control. + /// + [ + DefaultValue(""), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ErrorProviderErrorDescr) + ] + public string GetError(Control control) { + return EnsureControlItem(control).Error; + } + + /// + /// + /// Returns where the error icon should be placed relative to the control. + /// + [ + DefaultValue(defaultIconAlignment), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ErrorProviderIconAlignmentDescr) + ] + public ErrorIconAlignment GetIconAlignment(Control control) { + return EnsureControlItem(control).IconAlignment; + } + + /// + /// + /// Returns the amount of extra space to leave next to the error icon. + /// + [ + DefaultValue(0), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ErrorProviderIconPaddingDescr) + ] + public int GetIconPadding(Control control) { + return EnsureControlItem(control).IconPadding; + } + + private void ResetIcon() { + Icon = DefaultIcon; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftChanged(EventArgs e) { + + foreach (ErrorWindow w in windows.Values) + { + w.Update(false); + } + + if (onRightToLeftChanged != null) { + onRightToLeftChanged(this, e); + } + } + + /// + /// + /// Sets the error description string for the specified control. + /// + public void SetError(Control control, string value) { + EnsureControlItem(control).Error = value; + } + + /// + /// + /// Sets where the error icon should be placed relative to the control. + /// + public void SetIconAlignment(Control control, ErrorIconAlignment value) { + EnsureControlItem(control).IconAlignment = value; + } + + /// + /// + /// Sets the amount of extra space to leave next to the error icon. + /// + public void SetIconPadding(Control control, int padding) { + EnsureControlItem(control).IconPadding = padding; + } + + private bool ShouldSerializeIcon() { + return Icon != DefaultIcon; + } + + /// + /// + /// There is one ErrorWindow for each control parent. It is parented to the + /// control parent. The window's region is made up of the regions from icons + /// of all child icons. The window's size is the enclosing rectangle for all + /// the regions. A tooltip window is created as a child of this window. The + /// rectangle associated with each error icon being displayed is added as a + /// tool to the tooltip window. + /// + /// + internal class ErrorWindow : NativeWindow { + + // + // FIELDS + // + + ArrayList items = new ArrayList(); + Control parent; + ErrorProvider provider; + Rectangle windowBounds = Rectangle.Empty; + System.Windows.Forms.Timer timer; + NativeWindow tipWindow; + + + // VSWhidbey #455702 + DeviceContext mirrordc= null; + Size mirrordcExtent = Size.Empty; + Point mirrordcOrigin = Point.Empty; + DeviceContextMapMode mirrordcMode = DeviceContextMapMode.Text; + + // + // CONSTRUCTORS + // + + /// + /// + /// Construct an error window for this provider and control parent. + /// + public ErrorWindow(ErrorProvider provider, Control parent) { + this.provider = provider; + this.parent = parent; + } + + // + // METHODS + // + + /// + /// + /// This is called when a control would like to show an error icon. + /// + public void Add(ControlItem item) { + items.Add(item); + if (!EnsureCreated()) + { + return; + } + + NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(toolInfo); + toolInfo.hwnd = Handle; + toolInfo.uId = item.Id; + toolInfo.lpszText = item.Error; + toolInfo.uFlags = NativeMethods.TTF_SUBCLASS; + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_ADDTOOL, 0, toolInfo); + + Update(false /*timerCaused*/); + } + + /// + /// + /// Called to get rid of any resources the Object may have. + /// + public void Dispose() { + EnsureDestroyed(); + } + + /// + /// + /// Make sure the error window is created, and the tooltip window is created. + /// + bool EnsureCreated() { + if (Handle == IntPtr.Zero) { + if (!parent.IsHandleCreated) + { + return false; + } + CreateParams cparams = new CreateParams(); + cparams.Caption = String.Empty; + cparams.Style = NativeMethods.WS_VISIBLE | NativeMethods.WS_CHILD; + cparams.ClassStyle = NativeMethods.CS_DBLCLKS; + cparams.X = 0; + cparams.Y = 0; + cparams.Width = 0; + cparams.Height = 0; + cparams.Parent = parent.Handle; + + CreateHandle(cparams); + + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + icc.dwSize = Marshal.SizeOf(icc); + SafeNativeMethods.InitCommonControlsEx(icc); + cparams = new CreateParams(); + cparams.Parent = Handle; + cparams.ClassName = NativeMethods.TOOLTIPS_CLASS; + cparams.Style = NativeMethods.TTS_ALWAYSTIP; + tipWindow = new NativeWindow(); + tipWindow.CreateHandle(cparams); + + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + SafeNativeMethods.SetWindowPos(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.HWND_TOP, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOACTIVATE); + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETDELAYTIME, NativeMethods.TTDT_INITIAL, 0); + } + return true; + } + + /// + /// + /// Destroy the timer, toolwindow, and the error window itself. + /// + void EnsureDestroyed() { + if (timer != null) { + timer.Dispose(); + timer = null; + } + if (tipWindow != null) { + tipWindow.DestroyHandle(); + tipWindow = null; + } + + // Hide the window and invalidate the parent to ensure + // that we leave no visual artifacts... given that we + // have a bizare region window, this is needed. + // + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), + NativeMethods.HWND_TOP, + windowBounds.X, + windowBounds.Y, + windowBounds.Width, + windowBounds.Height, + NativeMethods.SWP_HIDEWINDOW + | NativeMethods.SWP_NOSIZE + | NativeMethods.SWP_NOMOVE); + if (parent != null) { + parent.Invalidate(true); + } + DestroyHandle(); + + Debug.Assert(mirrordc == null, "Why is mirrordc non-null?"); + if (mirrordc != null) { + mirrordc.Dispose(); + } + } + + /// + /// + /// VSWhidbey #455702. + /// + /// Since we added mirroring to certain controls, we need to make sure the + /// error icons show up in the correct place. We cannot mirror the errorwindow + /// in EnsureCreated (although that would have been really easy), since we use + /// GDI+ for some of this code, and as we all know, GDI+ does not handle mirroring + /// at all. + /// + /// To work around that we create our own mirrored dc when we need to. + /// + /// + void CreateMirrorDC(IntPtr hdc, int originOffset) { + + Debug.Assert(mirrordc == null, "Why is mirrordc non-null? Did you not call RestoreMirrorDC?"); + + mirrordc = DeviceContext.FromHdc(hdc); + if (parent.IsMirrored && mirrordc != null) { + mirrordc.SaveHdc(); + mirrordcExtent = mirrordc.ViewportExtent; + mirrordcOrigin = mirrordc.ViewportOrigin; + + mirrordcMode = mirrordc.SetMapMode(DeviceContextMapMode.Anisotropic); + mirrordc.ViewportExtent = new Size(-(mirrordcExtent.Width), mirrordcExtent.Height); + mirrordc.ViewportOrigin = new Point(mirrordcOrigin.X + originOffset, mirrordcOrigin.Y); + } + } + + void RestoreMirrorDC() { + + if (parent.IsMirrored && mirrordc != null) { + mirrordc.ViewportExtent = mirrordcExtent; + mirrordc.ViewportOrigin = mirrordcOrigin; + mirrordc.SetMapMode(mirrordcMode); + mirrordc.RestoreHdc(); + mirrordc.Dispose(); + } + + mirrordc= null; + mirrordcExtent = Size.Empty; + mirrordcOrigin = Point.Empty; + mirrordcMode = DeviceContextMapMode.Text; + } + + /// + /// + /// This is called when the error window needs to paint. We paint each icon at its + /// correct location. + /// + void OnPaint(ref Message m) { + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + IntPtr hdc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, Handle), ref ps); + try { + CreateMirrorDC(hdc, windowBounds.Width - 1); + + try { + for (int i = 0; i < items.Count; i++) { + ControlItem item = (ControlItem)items[i]; + Rectangle bounds = item.GetIconBounds(provider.Region.Size); + SafeNativeMethods.DrawIconEx(new HandleRef(this, mirrordc.Hdc), bounds.X - windowBounds.X, bounds.Y - windowBounds.Y, new HandleRef(provider.Region, provider.Region.IconHandle), bounds.Width, bounds.Height, 0, NativeMethods.NullHandleRef, NativeMethods.DI_NORMAL); + } + } + finally { + RestoreMirrorDC(); + } + } + finally { + UnsafeNativeMethods.EndPaint(new HandleRef(this, Handle), ref ps); + } + } + + protected override void OnThreadException(Exception e) { + Application.OnThreadException(e); + } + + /// + /// + /// This is called when an error icon is flashing, and the view needs to be updatd. + /// + void OnTimer(Object sender, EventArgs e) { + int blinkPhase = 0; + for (int i = 0; i < items.Count; i++) { + blinkPhase += ((ControlItem)items[i]).BlinkPhase; + } + if (blinkPhase == 0 && provider.BlinkStyle != ErrorBlinkStyle.AlwaysBlink) { + Debug.Assert(timer != null); + timer.Stop(); + } + Update(true /*timerCaused*/); + } + + private void OnToolTipVisibilityChanging(System.IntPtr id, bool toolTipShown) { + for (int i = 0; i < items.Count; i++) { + if (((ControlItem)items[i]).Id == id) { + ((ControlItem)items[i]).ToolTipShown = toolTipShown; + } + } +#if DEBUG + int shownTooltips = 0; + for (int j = 0; j < items.Count; j++) { + if (((ControlItem)items[j]).ToolTipShown) { + shownTooltips++; + } + } + Debug.Assert(shownTooltips <= 1); +#endif + } + + /// + /// + /// This is called when a control no longer needs to display an error icon. + /// + public void Remove(ControlItem item) { + items.Remove(item); + + if (tipWindow != null) { + NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(toolInfo); + toolInfo.hwnd = Handle; + toolInfo.uId = item.Id; + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_DELTOOL, 0, toolInfo); + } + + if (items.Count == 0) { + EnsureDestroyed(); + } + else { + Update(false /*timerCaused*/); + } + } + + /// + /// + /// Start the blinking process. The timer will fire until there are no more + /// icons that need to blink. + /// + internal void StartBlinking() { + if (timer == null) { + timer = new System.Windows.Forms.Timer(); + timer.Tick += new EventHandler(OnTimer); + } + timer.Interval = provider.BlinkRate; + timer.Start(); + Update(false /*timerCaused*/); + } + + internal void StopBlinking() { + if (timer != null) { + timer.Stop(); + } + Update(false /*timerCaused*/); + } + + /// + /// + /// Move and size the error window, compute and set the window region, + /// set the tooltip rectangles and descriptions. This basically brings + /// the error window up to date with the internal data structures. + /// + public void Update(bool timerCaused) { + IconRegion iconRegion = provider.Region; + Size size = iconRegion.Size; + windowBounds = Rectangle.Empty; + for (int i = 0; i < items.Count; i++) { + ControlItem item = (ControlItem)items[i]; + Rectangle iconBounds = item.GetIconBounds(size); + if (windowBounds.IsEmpty) + windowBounds = iconBounds; + else + windowBounds = Rectangle.Union(windowBounds, iconBounds); + } + + Region windowRegion = new Region(new Rectangle(0, 0, 0, 0)); + IntPtr windowRegionHandle = IntPtr.Zero; + try { + for (int i = 0; i < items.Count; i++) { + ControlItem item = (ControlItem)items[i]; + Rectangle iconBounds = item.GetIconBounds(size); + iconBounds.X -= windowBounds.X; + iconBounds.Y -= windowBounds.Y; + + bool showIcon = true; + if (!item.ToolTipShown) { + switch (provider.BlinkStyle) { + case ErrorBlinkStyle.NeverBlink: + // always show icon + break; + + case ErrorBlinkStyle.BlinkIfDifferentError: + showIcon = (item.BlinkPhase == 0) || (item.BlinkPhase > 0 && (item.BlinkPhase & 1) == (i & 1)); + break; + + case ErrorBlinkStyle.AlwaysBlink: + showIcon = ((i & 1) == 0) == provider.showIcon; + break; + } + } + + if (showIcon) + { + iconRegion.Region.Translate(iconBounds.X, iconBounds.Y); + windowRegion.Union(iconRegion.Region); + iconRegion.Region.Translate(-iconBounds.X, -iconBounds.Y); + } + + if (tipWindow != null) { + NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(toolInfo); + toolInfo.hwnd = Handle; + toolInfo.uId = item.Id; + toolInfo.lpszText = item.Error; + toolInfo.rect = NativeMethods.RECT.FromXYWH(iconBounds.X, iconBounds.Y, iconBounds.Width, iconBounds.Height); + toolInfo.uFlags = NativeMethods.TTF_SUBCLASS; + if (provider.RightToLeft) { + toolInfo.uFlags |= NativeMethods.TTF_RTLREADING; + } + UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETTOOLINFO, 0, toolInfo); + } + + if (timerCaused && item.BlinkPhase > 0) { + item.BlinkPhase--; + } + } + if (timerCaused) { + provider.showIcon = !provider.showIcon; + } + + + DeviceContext dc = null; + dc = DeviceContext.FromHwnd(this.Handle); + try { + CreateMirrorDC(dc.Hdc, windowBounds.Width); + + Graphics graphics = Graphics.FromHdcInternal(mirrordc.Hdc); + try { + windowRegionHandle = windowRegion.GetHrgn(graphics); + System.Internal.HandleCollector.Add(windowRegionHandle, NativeMethods.CommonHandles.GDI); + } + finally { + graphics.Dispose(); + RestoreMirrorDC(); + } + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(windowRegion, windowRegionHandle), true) != 0) { + //The HWnd owns the region. + windowRegionHandle = IntPtr.Zero; + } + } + + finally { + if (dc != null) { + dc.Dispose(); + } + } + + } + finally { + windowRegion.Dispose(); + if (windowRegionHandle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, windowRegionHandle)); + } + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOP, windowBounds.X, windowBounds.Y, + windowBounds.Width, windowBounds.Height, NativeMethods.SWP_NOACTIVATE); + SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), null, false); + } + + /// + /// + /// Called when the error window gets a windows message. + /// + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_NOTIFY: + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + if (nmhdr.code == NativeMethods.TTN_SHOW || nmhdr.code == NativeMethods.TTN_POP) + { + OnToolTipVisibilityChanging(nmhdr.idFrom, nmhdr.code == NativeMethods.TTN_SHOW); + } + break; + case NativeMethods.WM_ERASEBKGND: + break; + case NativeMethods.WM_PAINT: + OnPaint(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + } + + /// + /// + /// There is one ControlItem for each control that the ErrorProvider is + /// is tracking state for. It contains the values of all the extender + /// properties. + /// + internal class ControlItem { + + // + // FIELDS + // + + string error; + Control control; + ErrorWindow window; + ErrorProvider provider; + int blinkPhase; + IntPtr id; + int iconPadding; + bool toolTipShown; + ErrorIconAlignment iconAlignment; + const int startingBlinkPhase = 10; // cause we want to blink 5 times + + // + // CONSTRUCTORS + // + + /// + /// + /// Construct the item with its associated control, provider, and + /// a unique ID. The ID is used for the tooltip ID. + /// + public ControlItem(ErrorProvider provider, Control control, IntPtr id) { + this.toolTipShown = false; + this.iconAlignment = defaultIconAlignment; + this.error = String.Empty; + this.id = id; + this.control = control; + this.provider = provider; + this.control.HandleCreated += new EventHandler(OnCreateHandle); + this.control.HandleDestroyed += new EventHandler(OnDestroyHandle); + this.control.LocationChanged += new EventHandler(OnBoundsChanged); + this.control.SizeChanged += new EventHandler(OnBoundsChanged); + this.control.VisibleChanged += new EventHandler(OnParentVisibleChanged); + this.control.ParentChanged += new EventHandler(OnParentVisibleChanged); + } + + public void Dispose() { + if (control != null) { + control.HandleCreated -= new EventHandler(OnCreateHandle); + control.HandleDestroyed -= new EventHandler(OnDestroyHandle); + control.LocationChanged -= new EventHandler(OnBoundsChanged); + control.SizeChanged -= new EventHandler(OnBoundsChanged); + control.VisibleChanged -= new EventHandler(OnParentVisibleChanged); + control.ParentChanged -= new EventHandler(OnParentVisibleChanged); + } + error = string.Empty; + } + + // + // PROPERTIES + // + + /// + /// + /// Returns the unique ID for this control. The ID used as the tooltip ID. + /// + public IntPtr Id { + get { + return id; + } + } + + /// + /// + /// Returns or set the phase of blinking that this control is currently + /// in. If zero, the control is not blinking. If odd, then the control + /// is blinking, but invisible. If even, the control is blinking and + /// currently visible. Each time the blink timer fires, this value is + /// reduced by one (until zero), thus causing the error icon to appear + /// or disappear. + /// + public int BlinkPhase { + get { + return blinkPhase; + } + set { + blinkPhase = value; + } + } + + /// + /// + /// Returns or sets the icon padding for the control. + /// + public int IconPadding { + get { + return iconPadding; + } + set { + if (iconPadding != value) { + iconPadding = value; + UpdateWindow(); + } + } + } + + /// + /// + /// Returns or sets the error description string for the control. + /// + public string Error { + get { + return error; + } + set { + if (value == null) { + value = ""; + } + + // if the error is the same and the blinkStyle is not AlwaysBlink, then + // we should not add the error and not start blinking. + if (error.Equals(value) && provider.BlinkStyle != ErrorBlinkStyle.AlwaysBlink) { + return; + } + + bool adding = error.Length == 0; + error = value; + if (value.Length == 0) { + RemoveFromWindow(); + } + else { + if (adding) { + AddToWindow(); + } + else { + if (provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) { + StartBlinking(); + } + else { + UpdateWindow(); + } + } + } + } + } + + /// + /// + /// Returns or sets the location of the error icon for the control. + /// + public ErrorIconAlignment IconAlignment { + get { + return iconAlignment; + } + set { + if (iconAlignment != value) { + //valid values are 0x0 to 0x5 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ErrorIconAlignment.TopLeft, (int)ErrorIconAlignment.BottomRight)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ErrorIconAlignment)); + } + iconAlignment = value; + UpdateWindow(); + } + } + } + + /// + /// + /// Returns true if the tooltip for this control item is currently shown. + /// + public bool ToolTipShown + { + get { + return this.toolTipShown; + } + set { + this.toolTipShown = value; + } + } + + internal ErrorIconAlignment RTLTranslateIconAlignment(ErrorIconAlignment align) { + if (provider.RightToLeft) { + switch (align) { + case ErrorIconAlignment.TopLeft: + return ErrorIconAlignment.TopRight; + case ErrorIconAlignment.MiddleLeft: + return ErrorIconAlignment.MiddleRight; + case ErrorIconAlignment.BottomLeft: + return ErrorIconAlignment.BottomRight; + case ErrorIconAlignment.TopRight: + return ErrorIconAlignment.TopLeft; + case ErrorIconAlignment.MiddleRight: + return ErrorIconAlignment.MiddleLeft; + case ErrorIconAlignment.BottomRight: + return ErrorIconAlignment.BottomLeft; + default: + Debug.Fail("Unknown ErrorIconAlignment value"); + return align; + } + } + else { + return align; + } + } + + /// + /// + /// Returns the location of the icon in the same coordinate system as + /// the control being extended. The size passed in is the size of + /// the icon. + /// + internal Rectangle GetIconBounds(Size size) { + int x = 0; + int y = 0; + + switch (RTLTranslateIconAlignment(IconAlignment)) { + case ErrorIconAlignment.TopLeft: + case ErrorIconAlignment.MiddleLeft: + case ErrorIconAlignment.BottomLeft: + x = control.Left - size.Width - iconPadding; + break; + case ErrorIconAlignment.TopRight: + case ErrorIconAlignment.MiddleRight: + case ErrorIconAlignment.BottomRight: + x = control.Right + iconPadding; + break; + } + + switch (IconAlignment) { + case ErrorIconAlignment.TopLeft: + case ErrorIconAlignment.TopRight: + y = control.Top; + break; + case ErrorIconAlignment.MiddleLeft: + case ErrorIconAlignment.MiddleRight: + y = control.Top + (control.Height - size.Height) / 2; + break; + case ErrorIconAlignment.BottomLeft: + case ErrorIconAlignment.BottomRight: + y = control.Bottom - size.Height; + break; + } + + return new Rectangle(x, y, size.Width, size.Height); + } + + /// + /// + /// If this control's error icon has been added to the error + /// window, then update the window state because some property + /// has changed. + /// + void UpdateWindow() { + if (window != null) { + window.Update(false /*timerCaused*/); + } + } + + /// + /// + /// If this control's error icon has been added to the error + /// window, then start blinking the error window. The blink + /// count + /// + void StartBlinking() { + if (window != null) { + BlinkPhase = startingBlinkPhase; + window.StartBlinking(); + } + } + + /// + /// + /// Add this control's error icon to the error window. + /// + void AddToWindow() { + // if we are recreating the control, then add the control. + if (window == null && + (control.Created || control.RecreatingHandle) && + control.Visible && control.ParentInternal != null && + error.Length > 0) { + window = provider.EnsureErrorWindow(control.ParentInternal); + window.Add(this); + // Make sure that we blink if the style is set to AlwaysBlink or BlinkIfDifferrentError + if (provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) + { + StartBlinking(); + } + } + } + + /// + /// + /// Remove this control's error icon from the error window. + /// + void RemoveFromWindow() { + if (window != null) { + window.Remove(this); + window = null; + } + } + + /// + /// + /// This is called when a property on the control is changed. + /// + void OnBoundsChanged(Object sender, EventArgs e) { + UpdateWindow(); + } + + void OnParentVisibleChanged(Object sender, EventArgs e) { + this.BlinkPhase = 0; + RemoveFromWindow(); + AddToWindow(); + } + + /// + /// + /// This is called when the control's handle is created. + /// + void OnCreateHandle(Object sender, EventArgs e) { + AddToWindow(); + } + + /// + /// + /// This is called when the control's handle is destroyed. + /// + void OnDestroyHandle(Object sender, EventArgs e) { + RemoveFromWindow(); + } + } + + /// + /// + /// This represents the HRGN of icon. The region is calculate from the icon's mask. + /// + internal class IconRegion { + + // + // FIELDS + // + + Region region; + Icon icon; + + // + // CONSTRUCTORS + // + + /// + /// + /// Constructor that takes an Icon and extracts its 16x16 version. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public IconRegion(Icon icon) { + this.icon = new Icon(icon, 16, 16); + } + + // + // PROPERTIES + // + + /// + /// + /// Returns the handle of the icon. + /// + public IntPtr IconHandle { + get { + return icon.Handle; + } + } + + /// + /// + /// Returns the handle of the region. + /// + public Region Region { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process | ResourceScope.Machine, ResourceScope.Process | ResourceScope.Machine)] + get { + if (region == null) { + region = new Region(new Rectangle(0,0,0,0)); + + IntPtr mask = IntPtr.Zero; + try { + Size size = icon.Size; + Bitmap bitmap = icon.ToBitmap(); + bitmap.MakeTransparent(); + mask = ControlPaint.CreateHBitmapTransparencyMask(bitmap); + bitmap.Dispose(); + + // It is been observed that users can use non standard size icons (not a 16 bit multiples for width and height) + // and GetBitmapBits method allocate bytes in multiple of 16 bits for each row. Following calculation is to get right width in bytes. + int bitmapBitsAllocationSize = 16; + + //if width is not multiple of 16, we need to allocate BitmapBitsAllocationSize for remaining bits. + int widthInBytes = 2 * ((size.Width +15) / bitmapBitsAllocationSize); // its in bytes. + byte[] bits = new byte[widthInBytes * size.Height]; + SafeNativeMethods.GetBitmapBits(new HandleRef(null, mask), bits.Length, bits); + + for (int y = 0; y < size.Height; y++) { + for (int x = 0; x < size.Width; x++) { + + // see if bit is set in mask. bits in byte are reversed. 0 is black (set). + if ((bits[y * widthInBytes + x / 8] & (1 << (7 - (x % 8)))) == 0) { + region.Union(new Rectangle(x, y, 1, 1)); + } + } + } + region.Intersect(new Rectangle(0, 0, size.Width, size.Height)); + } + finally { + if (mask != IntPtr.Zero) + SafeNativeMethods.DeleteObject(new HandleRef(null, mask)); + } + } + + return region; + } + } + + /// + /// + /// Return the size of the icon. + /// + public Size Size { + get { + return icon.Size; + } + } + + // + // METHODS + // + + /// + /// + /// Release any resources held by this Object. + /// + public void Dispose() { + if (region != null) { + region.Dispose(); + region = null; + } + icon.Dispose(); + } + + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/FeatureSupport.cs b/WindowsForms/Managed/System/WinForms/FeatureSupport.cs new file mode 100644 index 000000000..80863da61 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FeatureSupport.cs @@ -0,0 +1,138 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Configuration.Assemblies; + + using System.Diagnostics; + + using System; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + + + /// + /// + /// Provides methods for retrieving feature information from the + /// current system. + /// + public abstract class FeatureSupport : IFeatureSupport { + + /// + /// + /// Determines whether any version of the specified feature + /// is installed in the system. This method is . + /// + public static bool IsPresent(string featureClassName, string featureConstName) { + return IsPresent(featureClassName, featureConstName, new Version(0, 0, 0, 0)); + } + + /// + /// + /// Determines whether the specified or newer version of the specified feature is + /// installed in the system. This method is . + /// + public static bool IsPresent(string featureClassName, string featureConstName, Version minimumVersion) + { + object featureId = null; + IFeatureSupport featureSupport = null; + + //APPCOMPAT: If Type.GetType() throws, we want to return + //null to preserve Everett behavior. + Type c = null; + try { + c = Type.GetType(featureClassName); + } + catch (ArgumentException) {} + + if (c != null) { + FieldInfo fi = c.GetField(featureConstName); + + if (fi != null) { + featureId = fi.GetValue(null); + } + } + + if (featureId != null && typeof(IFeatureSupport).IsAssignableFrom(c)) { + + featureSupport = (IFeatureSupport) SecurityUtils.SecureCreateInstance(c); + + if (featureSupport != null) { + return featureSupport.IsPresent(featureId, minimumVersion); + } + } + return false; + } + + /// + /// + /// Gets the version of the specified feature that is available on the system. + /// + public static Version GetVersionPresent(string featureClassName, string featureConstName) { + object featureId = null; + IFeatureSupport featureSupport = null; + + //APPCOMPAT: If Type.GetType() throws, we want to return + //null to preserve Everett behavior. + Type c = null; + try { + c = Type.GetType(featureClassName); + } + catch (ArgumentException) {} + + if (c != null) { + FieldInfo fi = c.GetField(featureConstName); + + if (fi != null) { + featureId = fi.GetValue(null); + } + } + + if (featureId != null) + { + featureSupport = (IFeatureSupport) SecurityUtils.SecureCreateInstance(c); + + if (featureSupport != null) { + return featureSupport.GetVersionPresent(featureId); + } + } + return null; + } + + /// + /// + /// Determines whether any version of the specified feature + /// is installed in the system. + /// + public virtual bool IsPresent(object feature) { + return IsPresent(feature, new Version(0, 0, 0, 0)); + } + + /// + /// + /// Determines whether the specified or newer version of the + /// specified feature is installed in the system. + /// + public virtual bool IsPresent(object feature, Version minimumVersion) { + Version ver = GetVersionPresent(feature); + + if (ver != null) { + return ver.CompareTo(minimumVersion) >= 0; + } + return false; + } + + /// + /// + /// When overridden in a derived class, gets the version of the specified + /// feature that is available on the system. + /// + public abstract Version GetVersionPresent(object feature); + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/FileDialog.cs b/WindowsForms/Managed/System/WinForms/FileDialog.cs new file mode 100644 index 000000000..90689bded --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FileDialog.cs @@ -0,0 +1,1087 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Text; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Security.Permissions; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms; + using System.IO; + using ArrayList = System.Collections.ArrayList; + + using Encoding = System.Text.Encoding; + using Microsoft.Win32; + using System.Security; + using System.Runtime.Versioning; + + using CharBuffer = System.Windows.Forms.UnsafeNativeMethods.CharBuffer; + + /// + /// + /// + /// Displays a dialog window from which the user can select a file. + /// + /// + [ + DefaultEvent("FileOk"), + DefaultProperty("FileName") + ] + public abstract partial class FileDialog : CommonDialog { + + private const int FILEBUFSIZE = 8192; + + /// + /// + /// [To be supplied.] + /// + /// + protected static readonly object EventFileOk = new object(); + + internal const int OPTION_ADDEXTENSION = unchecked(unchecked((int)0x80000000)); + + internal int options; + + private string title; + private string initialDir; + private string defaultExt; + private string[] fileNames; + private bool securityCheckFileNames; + private string filter; + private int filterIndex; + private bool supportMultiDottedExtensions; + private bool ignoreSecondFileOkNotification; // Used for VS Whidbey 95342 + private int okNotificationCount; // Same + private CharBuffer charBuffer; + private IntPtr dialogHWnd; + + /// + /// + /// + /// In an inherited class, + /// initializes a new instance of the + /// class. + /// + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not call Reset + // it would be a breaking change. + ] + internal FileDialog() { + Reset(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// dialog box automatically adds an extension to a + /// file name if the user omits the extension. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FDaddExtensionDescr) + ] + public bool AddExtension { + get { + return GetOption(OPTION_ADDEXTENSION); + } + + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(OPTION_ADDEXTENSION, value); + } + } + + /// + /// + /// Gets or sets a value indicating whether + /// the dialog box displays a warning if the user specifies a file name that does not exist. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FDcheckFileExistsDescr) + ] + public virtual bool CheckFileExists { + get { + return GetOption(NativeMethods.OFN_FILEMUSTEXIST); + } + + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(NativeMethods.OFN_FILEMUSTEXIST, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// dialog box displays a warning if the user specifies a path that does not exist. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FDcheckPathExistsDescr) + ] + public bool CheckPathExists { + get { + return GetOption(NativeMethods.OFN_PATHMUSTEXIST); + } + + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(NativeMethods.OFN_PATHMUSTEXIST, value); + } + } + + /// + /// + /// + /// Gets or sets the default file extension. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(""), + SRDescription(SR.FDdefaultExtDescr) + ] + public string DefaultExt { + get { + return defaultExt == null? "": defaultExt; + } + + set { + if (value != null) { + if (value.StartsWith(".")) + value = value.Substring(1); + else if (value.Length == 0) + value = null; + } + defaultExt = value; + } + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the dialog box returns the location of the file referenced by the shortcut or + /// whether it returns the location of the shortcut (.lnk). + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FDdereferenceLinksDescr) + ] + public bool DereferenceLinks { + get { + return !GetOption(NativeMethods.OFN_NODEREFERENCELINKS); + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(NativeMethods.OFN_NODEREFERENCELINKS, !value); + } + } + + internal string DialogCaption { + get { + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, dialogHWnd)); + StringBuilder sb = new StringBuilder(textLen+1); + UnsafeNativeMethods.GetWindowText(new HandleRef(this, dialogHWnd), sb, sb.Capacity); + return sb.ToString(); + } + } + + /// + /// + /// + /// Gets + /// or sets a string containing + /// the file name selected in the file dialog box. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(""), + SRDescription(SR.FDfileNameDescr) + ] + public string FileName { + get { + if (fileNames == null) { + return ""; + } + else { + if (fileNames[0].Length > 0) { + + // See if we need to perform a security check on file names. We need + // to do this if the set of file names was provided by the file dialog. + // A developer can set file names through the FileDialog API as well, + // but we don't need to check those since the developer can provide any + // name s/he wants. This is important because it is otherwise possible + // to get the FileName property to accept garbage, but throw during get. + + if (securityCheckFileNames) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + fileNames[0] + ") Demanded"); + IntSecurity.DemandFileIO(FileIOPermissionAccess.AllAccess, fileNames[0]); + } + + return fileNames[0]; + } + else { + return ""; + } + } + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + if (value == null) { + fileNames = null; + } + else { + fileNames = new string[] {value}; + } + + // As the developer has called this API and set the file name with an arbitrary value, + // we do not need to perform a security check on the name. + securityCheckFileNames = false; + } + } + + /// + /// + /// + /// Gets the file + /// names of all selected files in the dialog box. + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FDFileNamesDescr) + ] + public string[] FileNames { + get{ + string[] files = FileNamesInternal; + + // See if we need to perform a security check on file names. We need + // to do this if the set of file names was provided by the file dialog. + // A developer can set file names through the FileDialog API as well, + // but we don't need to check those since the developer can provide any + // name s/he wants. This is important because it is otherwise possible + // to get the FileName property to accept garbage, but throw during get. + + if (securityCheckFileNames) { + foreach (string file in files) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + file + ") Demanded"); + IntSecurity.DemandFileIO(FileIOPermissionAccess.AllAccess, file); + } + } + return files; + } + } + + internal string[] FileNamesInternal { + get { + + if (fileNames == null) { + return new string[0]; + } + else { + return(string[])fileNames.Clone(); + } + } + } + + + /// + /// + /// + /// Gets + /// or sets the current file name filter string, + /// which determines the choices that appear in the "Save as file type" or + /// "Files of type" box in the dialog box. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(""), + Localizable(true), + SRDescription(SR.FDfilterDescr) + ] + public string Filter { + get { + return filter == null? "": filter; + } + + set { + if (value != filter) { + if (value != null && value.Length > 0) { + string[] formats = value.Split('|'); + if (formats == null || formats.Length % 2 != 0) { + throw new ArgumentException(SR.GetString(SR.FileDialogInvalidFilter)); + } + } + else { + value = null; + } + filter = value; + } + } + } + + /// + /// + /// Extracts the file extensions specified by the current file filter into + /// an array of strings. None of the extensions contain .'s, and the + /// default extension is first. + /// + private string[] FilterExtensions { + get { + string filter = this.filter; + ArrayList extensions = new ArrayList(); + + // First extension is the default one. It's a little strange if DefaultExt + // is not in the filters list, but I guess it's legal. + if (defaultExt != null) + extensions.Add(defaultExt); + + if (filter != null) { + string[] tokens = filter.Split('|'); + + if ((filterIndex * 2) - 1 >= tokens.Length) { + throw new InvalidOperationException(SR.GetString(SR.FileDialogInvalidFilterIndex)); + } + + if (filterIndex > 0) { + string[] exts = tokens[(filterIndex * 2) - 1].Split(';'); + foreach (string ext in exts) { + int i = this.supportMultiDottedExtensions ? ext.IndexOf('.') : ext.LastIndexOf('.'); + if (i >= 0) { + extensions.Add(ext.Substring(i + 1, ext.Length - (i + 1))); + } + } + } + } + string[] temp = new string[extensions.Count]; + extensions.CopyTo(temp, 0); + return temp; + } + } + + /// + /// + /// + /// Gets or sets the index of the filter currently selected in the file dialog box. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(1), + SRDescription(SR.FDfilterIndexDescr) + ] + public int FilterIndex { + get { + return filterIndex; + } + + set { + filterIndex = value; + } + } + + /// + /// + /// + /// Gets or sets the initial directory displayed by the file dialog + /// box. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(""), + SRDescription(SR.FDinitialDirDescr) + ] + public string InitialDirectory { + get { + return initialDir == null? "": initialDir; + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + + initialDir = value; + } + } + + /// + /// + /// + /// + /// Gets the Win32 instance handle for the application. + /// + /// + /* SECURITYUNDONE : should require EventQueue permission */ + protected virtual IntPtr Instance { + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { return UnsafeNativeMethods.GetModuleHandle(null); } + } + + /// + /// + /// + /// + /// Gets the Win32 common Open File Dialog OFN_* option flags. + /// + /// + protected int Options { + get { + return options & (NativeMethods.OFN_READONLY | NativeMethods.OFN_HIDEREADONLY | + NativeMethods.OFN_NOCHANGEDIR | NativeMethods.OFN_SHOWHELP | NativeMethods.OFN_NOVALIDATE | + NativeMethods.OFN_ALLOWMULTISELECT | NativeMethods.OFN_PATHMUSTEXIST | + NativeMethods.OFN_NODEREFERENCELINKS); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box restores the current directory before + /// closing. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FDrestoreDirectoryDescr) + ] + public bool RestoreDirectory { + get { + return GetOption(NativeMethods.OFN_NOCHANGEDIR); + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + + SetOption(NativeMethods.OFN_NOCHANGEDIR, value); + } + } + + + /// + /// + /// + /// Gets or sets a value indicating + /// whether whether the Help button is displayed in the file dialog. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FDshowHelpDescr) + ] + public bool ShowHelp { + get { + return GetOption(NativeMethods.OFN_SHOWHELP); + } + set { + SetOption(NativeMethods.OFN_SHOWHELP, value); + } + } + + /// + /// + /// + /// Gets or sets whether def or abc.def is the extension of the file filename.abc.def + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FDsupportMultiDottedExtensionsDescr) + ] + public bool SupportMultiDottedExtensions + { + get + { + return this.supportMultiDottedExtensions; + } + set + { + this.supportMultiDottedExtensions = value; + } + } + + /// + /// + /// + /// Gets or sets the file dialog box title. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(""), + Localizable(true), + SRDescription(SR.FDtitleDescr) + ] + public string Title { + get { + return title == null? "": title; + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + + title = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box accepts only valid + /// Win32 file names. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FDvalidateNamesDescr) + ] + public bool ValidateNames { + get { + return !GetOption(NativeMethods.OFN_NOVALIDATE); + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + + SetOption(NativeMethods.OFN_NOVALIDATE, !value); + } + } + + /// + /// + /// + /// Occurs when the user clicks on the Open or Save button on a file dialog + /// box. + /// + /// + /// + /// For information about handling events, see . + /// + /// + /// + [SRDescription(SR.FDfileOkDescr)] + public event CancelEventHandler FileOk { + add { + Events.AddHandler(EventFileOk, value); + } + remove { + Events.RemoveHandler(EventFileOk, value); + } + } + + /// + /// + /// Processes the CDN_FILEOK notification. + /// + private bool DoFileOk(IntPtr lpOFN) { + NativeMethods.OPENFILENAME_I ofn = (NativeMethods.OPENFILENAME_I)UnsafeNativeMethods.PtrToStructure(lpOFN, typeof(NativeMethods.OPENFILENAME_I)); + int saveOptions = options; + int saveFilterIndex = filterIndex; + string[] saveFileNames = fileNames; + bool saveSecurityCheckFileNames = securityCheckFileNames; + bool ok = false; + try { + options = options & ~NativeMethods.OFN_READONLY | + ofn.Flags & NativeMethods.OFN_READONLY; + filterIndex = ofn.nFilterIndex; + charBuffer.PutCoTaskMem(ofn.lpstrFile); + + // We are filling in the file names list with secure + // data. Any access to this list now will require + // a security demand. We set this bit before actually + // setting the names; otherwise a thread ---- could + // expose them. + securityCheckFileNames = true; + Thread.MemoryBarrier(); + + if ((options & NativeMethods.OFN_ALLOWMULTISELECT) == 0) { + fileNames = new string[] {charBuffer.GetString()}; + } + else { + fileNames = GetMultiselectFiles(charBuffer); + } + + if (ProcessFileNames()) { + CancelEventArgs ceevent = new CancelEventArgs(); + if (NativeWindow.WndProcShouldBeDebuggable) { + OnFileOk(ceevent); + ok = !ceevent.Cancel; + } + else { + try + { + OnFileOk(ceevent); + ok = !ceevent.Cancel; + } + catch (Exception e) + { + Application.OnThreadException(e); + } + } + } + } + finally { + if (!ok) { + securityCheckFileNames = saveSecurityCheckFileNames; + Thread.MemoryBarrier(); + fileNames = saveFileNames; + + options = saveOptions; + filterIndex = saveFilterIndex; + } + } + return ok; + } + + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: filename is a local variable and not subject to race conditions. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + internal static bool FileExists(string fileName) + { + bool fileExists = false; + try { + // SECREVIEW : We must Assert just to check if the file exists. Since + // : we are doing this as part of the FileDialog, this is OK. + new FileIOPermission(FileIOPermissionAccess.Read, IntSecurity.UnsafeGetFullPath(fileName)).Assert(); + try { + fileExists = File.Exists(fileName); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + catch (System.IO.PathTooLongException) { + } + return fileExists; + } + + /// + /// + /// Extracts the filename(s) returned by the file dialog. + /// + private string[] GetMultiselectFiles(CharBuffer charBuffer) { + string directory = charBuffer.GetString(); + string fileName = charBuffer.GetString(); + if (fileName.Length == 0) return new string[] { + directory + }; + if (directory[directory.Length - 1] != '\\') { + directory = directory + "\\"; + } + ArrayList names = new ArrayList(); + do { + if (fileName[0] != '\\' && (fileName.Length <= 3 || + fileName[1] != ':' || fileName[2] != '\\')) { + fileName = directory + fileName; + } + names.Add(fileName); + fileName = charBuffer.GetString(); + } while (fileName.Length > 0); + string[] temp = new string[names.Count]; + names.CopyTo(temp, 0); + return temp; + } + + /// + /// + /// Returns the state of the given option flag. + /// + /// + + internal bool GetOption(int option) { + return(options & option) != 0; + } + + /// + /// + /// + /// Defines the common dialog box hook procedure that is overridden to add + /// specific functionality to the file dialog box. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + if (msg == NativeMethods.WM_NOTIFY) { + dialogHWnd = UnsafeNativeMethods.GetParent(new HandleRef(null, hWnd)); + try { + UnsafeNativeMethods.OFNOTIFY notify = (UnsafeNativeMethods.OFNOTIFY)UnsafeNativeMethods.PtrToStructure(lparam, typeof(UnsafeNativeMethods.OFNOTIFY)); + + switch (notify.hdr_code) { + case -601: /* CDN_INITDONE */ + MoveToScreenCenter(dialogHWnd); + break; + case -602: /* CDN_SELCHANGE */ + NativeMethods.OPENFILENAME_I ofn = (NativeMethods.OPENFILENAME_I)UnsafeNativeMethods.PtrToStructure(notify.lpOFN, typeof(NativeMethods.OPENFILENAME_I)); + // Get the buffer size required to store the selected file names. + int sizeNeeded = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, dialogHWnd), 1124 /*CDM_GETSPEC*/, System.IntPtr.Zero, System.IntPtr.Zero); + if (sizeNeeded > ofn.nMaxFile) { + // A bigger buffer is required. + try { + int newBufferSize = sizeNeeded + (FILEBUFSIZE / 4); + // Allocate new buffer + CharBuffer charBufferTmp = CharBuffer.CreateBuffer(newBufferSize); + IntPtr newBuffer = charBufferTmp.AllocCoTaskMem(); + // Free old buffer + Marshal.FreeCoTaskMem(ofn.lpstrFile); + // Substitute buffer + ofn.lpstrFile = newBuffer; + ofn.nMaxFile = newBufferSize; + this.charBuffer = charBufferTmp; + Marshal.StructureToPtr(ofn, notify.lpOFN, true); + Marshal.StructureToPtr(notify, lparam, true); + } + catch { + // intentionaly not throwing here. + } + } + this.ignoreSecondFileOkNotification = false; + break; + case -604: /* CDN_SHAREVIOLATION */ + // See VS Whidbey 95342. When the selected file is locked for writing, + // we get this notification followed by *two* CDN_FILEOK notifications. + this.ignoreSecondFileOkNotification = true; // We want to ignore the second CDN_FILEOK + this.okNotificationCount = 0; // to avoid a second prompt by PromptFileOverwrite. + break; + case -606: /* CDN_FILEOK */ + if (this.ignoreSecondFileOkNotification) + { + // We got a CDN_SHAREVIOLATION notification and want to ignore the second CDN_FILEOK notification + if (this.okNotificationCount == 0) + { + this.okNotificationCount = 1; // This one is the first and is all right. + } + else + { + // This is the second CDN_FILEOK, so we want to ignore it. + this.ignoreSecondFileOkNotification = false; + UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), 0, new HandleRef(null, NativeMethods.InvalidIntPtr)); + return NativeMethods.InvalidIntPtr; + } + } + if (!DoFileOk(notify.lpOFN)) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), 0, new HandleRef(null, NativeMethods.InvalidIntPtr)); + return NativeMethods.InvalidIntPtr; + } + break; + } + } + catch { + if (dialogHWnd != IntPtr.Zero) { + UnsafeNativeMethods.EndDialog(new HandleRef(this, dialogHWnd), IntPtr.Zero); + } + throw; + } + } + return IntPtr.Zero; + } + + /// + /// + /// Converts the given filter string to the format required in an OPENFILENAME_I + /// structure. + /// + private static string MakeFilterString(string s, bool dereferenceLinks) { + if (s == null || s.Length == 0) + { + // Workaround for Whidbey bug #5165 + // Apply the workaround only when DereferenceLinks is true and OS is at least WinXP. + if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5) + { + s = " |*.*"; + } + else if (s == null) + { + return null; + } + } + int length = s.Length; + char[] filter = new char[length + 2]; + s.CopyTo(0, filter, 0, length); + for (int i = 0; i < length; i++) { + if (filter[i] == '|') filter[i] = (char)0; + } + filter[length + 1] = (char)0; + return new string(filter); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected void OnFileOk(CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[EventFileOk]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Processes the filenames entered in the dialog according to the settings + /// of the "addExtension", "checkFileExists", "createPrompt", and + /// "overwritePrompt" properties. + /// + private bool ProcessFileNames() { + if ((options & NativeMethods.OFN_NOVALIDATE) == 0) { + string[] extensions = FilterExtensions; + for (int i = 0; i < fileNames.Length; i++) { + string fileName = fileNames[i]; + if ((options & OPTION_ADDEXTENSION) != 0 && !Path.HasExtension(fileName)) { + bool fileMustExist = (options & NativeMethods.OFN_FILEMUSTEXIST) != 0; + + for (int j = 0; j < extensions.Length; j++) { + string currentExtension = Path.GetExtension(fileName); + + Debug.Assert(!extensions[j].StartsWith("."), + "FileDialog.FilterExtensions should not return things starting with '.'"); + Debug.Assert(currentExtension.Length == 0 || currentExtension.StartsWith("."), + "File.GetExtension should return something that starts with '.'"); + + string s = fileName.Substring(0, fileName.Length - currentExtension.Length); + + // we don't want to append the extension if it contains wild cards + if (extensions[j].IndexOfAny(new char[] { '*', '?' }) == -1) { + s += "." + extensions[j]; + } + + if (!fileMustExist || FileExists(s)) { + fileName = s; + break; + } + } + fileNames[i] = fileName; + } + if (!PromptUserIfAppropriate(fileName)) + return false; + } + } + return true; + } + + /// + /// + /// + /// Prompts the user with a + /// with the given parameters. It also ensures that + /// the focus is set back on the window that had + /// the focus to begin with (before we displayed + /// the MessageBox). + /// + /// + internal bool MessageBoxWithFocusRestore(string message, string caption, + MessageBoxButtons buttons, MessageBoxIcon icon) + { + bool ret; + IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); + try { + ret = RTLAwareMessageBox.Show(null, message, caption, buttons, icon, + MessageBoxDefaultButton.Button1, 0) == DialogResult.Yes; + } + finally { + UnsafeNativeMethods.SetFocus(new HandleRef(null, focusHandle)); + } + return ret; + } + + /// + /// + /// + /// Prompts the user with a + /// when a file + /// does not exist. + /// + /// + private void PromptFileNotFound(string fileName) { + MessageBoxWithFocusRestore(SR.GetString(SR.FileDialogFileNotFound, fileName), DialogCaption, + MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + // If it's necessary to throw up a "This file exists, are you sure?" kind of + // MessageBox, here's where we do it + // Return value is whether or not the user hit "okay". + internal virtual bool PromptUserIfAppropriate(string fileName) { + if ((options & NativeMethods.OFN_FILEMUSTEXIST) != 0) { + if (!FileExists(fileName)) { + PromptFileNotFound(fileName); + return false; + } + } + return true; + } + + /// + /// + /// + /// Resets all properties to their default values. + /// + /// + public override void Reset() { + options = NativeMethods.OFN_HIDEREADONLY | NativeMethods.OFN_PATHMUSTEXIST | + OPTION_ADDEXTENSION; + title = null; + initialDir = null; + defaultExt = null; + fileNames = null; + filter = null; + filterIndex = 1; + supportMultiDottedExtensions = false; + this._customPlaces.Clear(); + } + + /// + /// + /// Implements running of a file dialog. + /// + /// + protected override bool RunDialog(IntPtr hWndOwner) { + // See VSWhidbey bug 107000. Shell APIs do not support multisthreaded apartment model. + if (Control.CheckForIllegalCrossThreadCalls && Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new System.Threading.ThreadStateException(SR.GetString(SR.DebuggingExceptionOnly, SR.GetString(SR.ThreadMustBeSTA))); + } + EnsureFileDialogPermission(); + if (this.UseVistaDialogInternal) + { + return RunDialogVista(hWndOwner); + } + else + { + return RunDialogOld(hWndOwner); + } + } + + internal abstract void EnsureFileDialogPermission(); + + private bool RunDialogOld(IntPtr hWndOwner) + { + NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); + NativeMethods.OPENFILENAME_I ofn = new NativeMethods.OPENFILENAME_I(); + try { + charBuffer = CharBuffer.CreateBuffer(FILEBUFSIZE); + if (fileNames != null) { + charBuffer.PutString(fileNames[0]); + } + ofn.lStructSize = Marshal.SizeOf(typeof(NativeMethods.OPENFILENAME_I)); + // Degrade to the older style dialog if we're not on Win2K. + // We do this by setting the struct size to a different value + // + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) { + ofn.lStructSize = 0x4C; + } + ofn.hwndOwner = hWndOwner; + ofn.hInstance = Instance; + ofn.lpstrFilter = MakeFilterString(filter, this.DereferenceLinks); + ofn.nFilterIndex = filterIndex; + ofn.lpstrFile = charBuffer.AllocCoTaskMem(); + ofn.nMaxFile = FILEBUFSIZE; + ofn.lpstrInitialDir = initialDir; + ofn.lpstrTitle = title; + ofn.Flags = Options | (NativeMethods.OFN_EXPLORER | NativeMethods.OFN_ENABLEHOOK | NativeMethods.OFN_ENABLESIZING); + ofn.lpfnHook = hookProcPtr; + ofn.FlagsEx = NativeMethods.OFN_USESHELLITEM; + if (defaultExt != null && AddExtension) { + ofn.lpstrDefExt = defaultExt; + } + //Security checks happen here + return RunFileDialog(ofn); + } + finally { + charBuffer = null; + if (ofn.lpstrFile != IntPtr.Zero) { + Marshal.FreeCoTaskMem(ofn.lpstrFile); + } + } + } + + /// + /// + /// Implements the actual call to GetOPENFILENAME_I or GetSaveFileName. + /// + /// + internal abstract bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn); + + /// + /// + /// Sets the given option to the given boolean value. + /// + /// + internal void SetOption(int option, bool value) { + if (value) { + options |= option; + } + else { + options &= ~option; + } + } + + /// + /// + /// + /// + /// Provides a string version of this Object. + /// + /// + public override string ToString() { + StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); + try + { + sb.Append(FileName); + } + catch (Exception e) + { + sb.Append("<"); + sb.Append(e.GetType().FullName); + sb.Append(">"); + } + return sb.ToString(); + } + + + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/FileDialogCustomPlace.cs b/WindowsForms/Managed/System/WinForms/FileDialogCustomPlace.cs new file mode 100644 index 000000000..28c0a07ea --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FileDialogCustomPlace.cs @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Security; +using System.Security.Permissions; +using System.Text; + +namespace System.Windows.Forms +{ +//Sample Guids +// internal const string ComputerFolder = "0AC0837C-BBF8-452A-850D-79D08E667CA7"; +// internal const string Favorites = "1777F761-68AD-4D8A-87BD-30B759FA33DD"; +// internal const string Documents = "FDD39AD0-238F-46AF-ADB4-6C85480369C7"; +// internal const string Profile = "5E6C858F-0E22-4760-9AFE-EA3317B67173"; + + public class FileDialogCustomPlace + { + private string _path = ""; + private Guid _knownFolderGuid = Guid.Empty; + + public FileDialogCustomPlace(string path) + { + this.Path = path; + } + + public FileDialogCustomPlace(Guid knownFolderGuid) + { + this.KnownFolderGuid = knownFolderGuid; + } + + public string Path + { + get + { + if (string.IsNullOrEmpty(this._path)) + { + return String.Empty; + } + return this._path; + } + set + { + this._path = value ?? ""; + this._knownFolderGuid = Guid.Empty; + } + } + + public Guid KnownFolderGuid + { + get + { + return this._knownFolderGuid; + } + set + { + this._path = String.Empty; + this._knownFolderGuid = value; + } + } + + public override string ToString() + { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0} Path: {1} KnownFolderGuid: {2}", base.ToString(), this.Path, this.KnownFolderGuid); + } + + internal FileDialogNative.IShellItem GetNativePath() + { + //This can throw in a multitude of ways if the path or Guid doesn't correspond + //to an actual filesystem directory. Caller is responsible for handling these situations. + string filePathString = ""; + if (!string.IsNullOrEmpty(this._path)) + { + filePathString = this._path; + } + else + { + filePathString = GetFolderLocation(this._knownFolderGuid); + } + + if (string.IsNullOrEmpty(filePathString)) + { + return null; + } + else + { + return FileDialog.GetShellItemForPath(filePathString); + } + } + + private static string GetFolderLocation(Guid folderGuid) + { + //returns a null string if the path can't be found + + //SECURITY: This exposes the filesystem path of the GUID. The returned value + // must not be made available to user code. + + if (!UnsafeNativeMethods.IsVista) + { + return null; + } + + StringBuilder path = new StringBuilder(); + + int result = UnsafeNativeMethods.Shell32.SHGetFolderPathEx(ref folderGuid, 0, IntPtr.Zero, path); + if (NativeMethods.S_OK == result) + { + string ret = path.ToString(); + return ret; + } + else + { + // 0x80070002 is an explicit FileNotFound error. + return null; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/FileDialogCustomPlacesCollection.cs b/WindowsForms/Managed/System/WinForms/FileDialogCustomPlacesCollection.cs new file mode 100644 index 000000000..d8cef29cd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FileDialogCustomPlacesCollection.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Security; +using System.Security.Permissions; + +namespace System.Windows.Forms +{ + public class FileDialogCustomPlacesCollection : Collection + { + internal void Apply(FileDialogNative.IFileDialog dialog) + { + //Walk backwards + for (int i = this.Items.Count - 1; i >= 0; --i) + { + FileDialogCustomPlace customPlace = this.Items[i]; + + // Fix for Dev10 bug 536188: we need permission to check whether the specified path exists + FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.PathDiscovery, customPlace.Path); + permission.Demand(); + + try + { + FileDialogNative.IShellItem shellItem = customPlace.GetNativePath(); + if (null != shellItem) + { + dialog.AddPlace(shellItem, 0); + } + } + catch (FileNotFoundException) + { + } + //Silently absorb FileNotFound exceptions (these could be caused by a path that disappeared after the place was added to the dialog). + } + } + + public void Add(string path) + { + Add(new FileDialogCustomPlace(path)); + } + + public void Add(Guid knownFolderGuid) + { + Add(new FileDialogCustomPlace(knownFolderGuid)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/FileDialog_Vista.cs b/WindowsForms/Managed/System/WinForms/FileDialog_Vista.cs new file mode 100644 index 000000000..de52fe5c7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FileDialog_Vista.cs @@ -0,0 +1,362 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections.Generic; + using System.IO; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Threading; + using System.Diagnostics.CodeAnalysis; + + public partial class FileDialog + { + private bool _autoUpgradeEnabled = true; + + internal virtual bool SettingsSupportVistaDialog + { + get + { + return !this.ShowHelp && + ((Application.VisualStyleState & VisualStyles.VisualStyleState.ClientAreaEnabled) == VisualStyles.VisualStyleState.ClientAreaEnabled); + } + } + + + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + internal bool UseVistaDialogInternal + { + get + { + if (UnsafeNativeMethods.IsVista && this._autoUpgradeEnabled && SettingsSupportVistaDialog) + { + // SECREVIEW : Querying for environment info is safe. + // + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try + { + return SystemInformation.BootMode == BootMode.Normal; // see DDB#169589 + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + + return false; + } + } + + internal abstract FileDialogNative.IFileDialog CreateVistaDialog(); + + [ + SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode), + SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive") + ] + private bool RunDialogVista(IntPtr hWndOwner) + { + FileDialogNative.IFileDialog dialog = CreateVistaDialog(); + OnBeforeVistaDialog(dialog); + VistaDialogEvents events = new VistaDialogEvents(this); + uint eventCookie; + dialog.Advise(events, out eventCookie); + try + { + int result = dialog.Show(hWndOwner); + return 0 == result; + } + finally + { + dialog.Unadvise(eventCookie); + //Make sure that the event interface doesn't get collected + GC.KeepAlive(events); + } + } + + internal virtual void OnBeforeVistaDialog(FileDialogNative.IFileDialog dialog) + { + dialog.SetDefaultExtension(this.DefaultExt); + + dialog.SetFileName(this.FileName); + + if (!string.IsNullOrEmpty(this.InitialDirectory)) + { + try + { + FileDialogNative.IShellItem initialDirectory = GetShellItemForPath(this.InitialDirectory); + + dialog.SetDefaultFolder(initialDirectory); + dialog.SetFolder(initialDirectory); + } + catch (FileNotFoundException) + { + } + } + + dialog.SetTitle(this.Title); + + dialog.SetOptions(GetOptions()); + + SetFileTypes(dialog); + + this._customPlaces.Apply(dialog); + } + + private FileDialogNative.FOS GetOptions() + { + const FileDialogNative.FOS BlittableOptions = + FileDialogNative.FOS.FOS_OVERWRITEPROMPT + | FileDialogNative.FOS.FOS_NOCHANGEDIR + | FileDialogNative.FOS.FOS_NOVALIDATE + | FileDialogNative.FOS.FOS_ALLOWMULTISELECT + | FileDialogNative.FOS.FOS_PATHMUSTEXIST + | FileDialogNative.FOS.FOS_FILEMUSTEXIST + | FileDialogNative.FOS.FOS_CREATEPROMPT + | FileDialogNative.FOS.FOS_NODEREFERENCELINKS + ; + const int UnexpectedOptions = + NativeMethods.OFN_USESHELLITEM //This is totally bogus (only used in FileDialog by accident to ensure that places are shown + | NativeMethods.OFN_SHOWHELP //If ShowHelp is true, we don't use the Vista Dialog + | NativeMethods.OFN_ENABLEHOOK //These shouldn't be set in options (only set in the flags for the legacy dialog) + | NativeMethods.OFN_ENABLESIZING //These shouldn't be set in options (only set in the flags for the legacy dialog) + | NativeMethods.OFN_EXPLORER //These shouldn't be set in options (only set in the flags for the legacy dialog) + ; + System.Diagnostics.Debug.Assert(0==(UnexpectedOptions & options), "Unexpected FileDialog options"); + + FileDialogNative.FOS ret = (FileDialogNative.FOS)options & BlittableOptions; + + //Force no mini mode for the SaveFileDialog + ret |= FileDialogNative.FOS.FOS_DEFAULTNOMINIMODE; + + // DevDiv Bugs 107025: Make sure that the Open dialog allows the user to specify + // non-file system locations. This flag will cause the dialog to copy the resource + // to a local cache (Temporary Internet Files), and return that path instead. This + // also affects the Save dialog by disallowing navigation to these areas. + // An example of a non-file system location is a URL (http://), or a file stored on + // a digital camera that is not mapped to a drive letter. + // This reproduces the behavior of the "classic" Open and Save dialogs. + ret |= FileDialogNative.FOS.FOS_FORCEFILESYSTEM; + + return ret; + } + + internal abstract string[] ProcessVistaFiles(FileDialogNative.IFileDialog dialog); + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private bool HandleVistaFileOk(FileDialogNative.IFileDialog dialog) + { + int saveOptions = options; + int saveFilterIndex = filterIndex; + string[] saveFileNames = fileNames; + bool saveSecurityCheckFileNames = securityCheckFileNames; + bool ok = false; + + try + { + securityCheckFileNames = true; + Thread.MemoryBarrier(); + uint filterIndexTemp; + dialog.GetFileTypeIndex(out filterIndexTemp); + filterIndex = unchecked((int)filterIndexTemp); + fileNames = ProcessVistaFiles(dialog); + if (ProcessFileNames()) + { + CancelEventArgs ceevent = new CancelEventArgs(); + if (NativeWindow.WndProcShouldBeDebuggable) + { + OnFileOk(ceevent); + ok = !ceevent.Cancel; + } + else + { + try + { + OnFileOk(ceevent); + ok = !ceevent.Cancel; + } + catch (Exception e) + { + Application.OnThreadException(e); + } + } + } + } + finally + { + if (!ok) + { + //Order here is important. We don't want a window where securityCheckFileNames is false, but the temporary fileNames is still in place + securityCheckFileNames = saveSecurityCheckFileNames; + Thread.MemoryBarrier(); + fileNames = saveFileNames; + + options = saveOptions; + filterIndex = saveFilterIndex; + } + else + { + if (0 != (options & NativeMethods.OFN_HIDEREADONLY)) + { + //When the dialog is dismissed OK, the Readonly bit can't + // be left on if ShowReadOnly was false + // Downlevel this happens automatically, on Vista mode, we need to watch out for it. + + options &= ~ NativeMethods.OFN_READONLY; + } + } + } + return ok; + } + + private class VistaDialogEvents : FileDialogNative.IFileDialogEvents + { + private FileDialog _dialog; + + public VistaDialogEvents(FileDialog dialog) + { + this._dialog = dialog; + } + + public int OnFileOk(FileDialogNative.IFileDialog pfd) + { + return this._dialog.HandleVistaFileOk(pfd) ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + + public int OnFolderChanging(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psiFolder) + { + return NativeMethods.S_OK; + } + + public void OnFolderChange(FileDialogNative.IFileDialog pfd) + { + } + + public void OnSelectionChange(FileDialogNative.IFileDialog pfd) + { + } + + public void OnShareViolation(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_SHAREVIOLATION_RESPONSE pResponse) + { + pResponse = FileDialogNative.FDE_SHAREVIOLATION_RESPONSE.FDESVR_DEFAULT; + } + + public void OnTypeChange(FileDialogNative.IFileDialog pfd) + { + } + + public void OnOverwrite(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_OVERWRITE_RESPONSE pResponse) + { + pResponse = FileDialogNative.FDE_OVERWRITE_RESPONSE.FDEOR_DEFAULT; + } + } + + private void SetFileTypes(FileDialogNative.IFileDialog dialog) + { + FileDialogNative.COMDLG_FILTERSPEC[] filterItems = FilterItems; + dialog.SetFileTypes((uint)filterItems.Length, filterItems); + if (filterItems.Length > 0) + { + dialog.SetFileTypeIndex(unchecked((uint)filterIndex)); + } + } + + private FileDialogNative.COMDLG_FILTERSPEC[] FilterItems + { + get + { + return GetFilterItems(this.filter); + } + } + + private static FileDialogNative.COMDLG_FILTERSPEC[] GetFilterItems(string filter) + { + //Expected input types + //"Text files (*.txt)|*.txt|All files (*.*)|*.*" + //"Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*" + List extensions = new List(); + if (!string.IsNullOrEmpty(filter)) + { + string[] tokens = filter.Split('|'); + if (0 == tokens.Length % 2) + { + //All even numbered tokens should be labels + //Odd numbered tokens are the associated extensions + for (int i = 1; i < tokens.Length; i += 2) + { + FileDialogNative.COMDLG_FILTERSPEC extension; + extension.pszSpec = tokens[i];//This may be a semicolon delimeted list of extensions (that's ok) + extension.pszName = tokens[i - 1]; + extensions.Add(extension); + } + } + } + return extensions.ToArray(); + } + + internal static FileDialogNative.IShellItem GetShellItemForPath(string path) + { + FileDialogNative.IShellItem ret = null; + IntPtr pidl = IntPtr.Zero; + uint zero = 0; + if (0 <= UnsafeNativeMethods.Shell32.SHILCreateFromPath(path, out pidl, ref zero)) + { + if (0 <= UnsafeNativeMethods.Shell32.SHCreateShellItem( + IntPtr.Zero, //No parent specified + IntPtr.Zero, + pidl, + out ret)) + { + return ret; + } + } + throw new System.IO.FileNotFoundException(); + } + + internal static string GetFilePathFromShellItem(FileDialogNative.IShellItem item) + { + string filename; + item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out filename); + return filename; + } + + private FileDialogCustomPlacesCollection _customPlaces = new FileDialogCustomPlacesCollection(); + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public FileDialogCustomPlacesCollection CustomPlaces + { + get + { + return this._customPlaces; + } + } + + /// + /// Gets or Sets whether the dialog will be automatically upgraded to enable new features. + /// + [ + DefaultValue(true) + ] + public bool AutoUpgradeEnabled + { + get + { + return this._autoUpgradeEnabled; + } + set + { + this._autoUpgradeEnabled = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/FileDialog_Vista_Interop.cs b/WindowsForms/Managed/System/WinForms/FileDialog_Vista_Interop.cs new file mode 100644 index 000000000..8b5333fd1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FileDialog_Vista_Interop.cs @@ -0,0 +1,391 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma warning disable 108 +namespace System.Windows.Forms +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Text; + + static class FileDialogNative + { + [ComImport] + [Guid(IIDGuid.IFileOpenDialog)] + [CoClass(typeof(FileOpenDialogRCW))] + internal interface NativeFileOpenDialog : IFileOpenDialog + { } + + [ComImport] + [Guid(IIDGuid.IFileSaveDialog)] + [CoClass(typeof(FileSaveDialogRCW))] + internal interface NativeFileSaveDialog : IFileSaveDialog + { } + + [ComImport] + [ClassInterface(ClassInterfaceType.None)] + [TypeLibType(TypeLibTypeFlags.FCanCreate)] + [Guid(CLSIDGuid.FileOpenDialog)] + internal class FileOpenDialogRCW + { } + + [ComImport] + [ClassInterface(ClassInterfaceType.None)] + [TypeLibType(TypeLibTypeFlags.FCanCreate)] + [Guid(CLSIDGuid.FileSaveDialog)] + internal class FileSaveDialogRCW + { } + + internal class IIDGuid + { + private IIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses + // IID GUID strings for relevant COM interfaces + internal const string IModalWindow = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802"; + internal const string IFileDialog = "42f85136-db7e-439c-85f1-e4075d135fc8"; + internal const string IFileOpenDialog = "d57c7288-d4ad-4768-be02-9d969532d960"; + internal const string IFileSaveDialog = "84bccd23-5fde-4cdb-aea4-af64b83d78ab"; + internal const string IFileDialogEvents = "973510DB-7D7F-452B-8975-74A85828D354"; + internal const string IShellItem = "43826D1E-E718-42EE-BC55-A1E261C37BFE"; + internal const string IShellItemArray = "B63EA76D-1F85-456F-A19C-48159EFA858B"; + } + + internal class CLSIDGuid + { + private CLSIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses + internal const string FileOpenDialog = "DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7"; + internal const string FileSaveDialog = "C0B4E2F3-BA21-4773-8DBA-335EC946EB8B"; + } + + [ComImport()] + [Guid(IIDGuid.IModalWindow)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IModalWindow + { + + [PreserveSig] + int Show([In] IntPtr parent); + } + + internal enum SIATTRIBFLAGS + { + SIATTRIBFLAGS_AND = 0x00000001, // if multiple items and the attributes together. + SIATTRIBFLAGS_OR = 0x00000002, // if multiple items or the attributes together. + SIATTRIBFLAGS_APPCOMPAT = 0x00000003, // Call GetAttributes directly on the ShellFolder for multiple attributes + } + + [ComImport] + [Guid(IIDGuid.IShellItemArray)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IShellItemArray + { + // Not supported: IBindCtx + + void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut); + + void GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv); + + void GetPropertyDescriptionList([In] ref PROPERTYKEY keyType, [In] ref Guid riid, out IntPtr ppv); + + void GetAttributes([In] SIATTRIBFLAGS dwAttribFlags, [In] uint sfgaoMask, out uint psfgaoAttribs); + + void GetCount(out uint pdwNumItems); + + void GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems); + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + internal struct PROPERTYKEY + { + internal Guid fmtid; + internal uint pid; + } + + [ComImport()] + [Guid(IIDGuid.IFileDialog)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IFileDialog + { + [PreserveSig] + int Show([In] IntPtr parent); + + void SetFileTypes([In] uint cFileTypes, [In] [MarshalAs(UnmanagedType.LPArray)]COMDLG_FILTERSPEC[] rgFilterSpec); + + void SetFileTypeIndex([In] uint iFileType); + + void GetFileTypeIndex(out uint piFileType); + + void Advise([In, MarshalAs(UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie); + + void Unadvise([In] uint dwCookie); + + void SetOptions([In] FOS fos); + + void GetOptions(out FOS pfos); + + void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); + + void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); + + void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); + + void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, int alignment); + + void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + void Close([MarshalAs(UnmanagedType.Error)] int hr); + + void SetClientGuid([In] ref Guid guid); + + void ClearClientData(); + + void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); + } + + [ComImport()] + [Guid(IIDGuid.IFileOpenDialog)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IFileOpenDialog : IFileDialog + { + [PreserveSig] + int Show([In] IntPtr parent); + + void SetFileTypes([In] uint cFileTypes, [In] ref COMDLG_FILTERSPEC rgFilterSpec); + + void SetFileTypeIndex([In] uint iFileType); + + void GetFileTypeIndex(out uint piFileType); + + void Advise([In, MarshalAs(UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie); + + void Unadvise([In] uint dwCookie); + + void SetOptions([In] FOS fos); + + void GetOptions(out FOS pfos); + + void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); + + void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); + + void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); + + void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, FileDialogCustomPlace fdcp); + + void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + void Close([MarshalAs(UnmanagedType.Error)] int hr); + + void SetClientGuid([In] ref Guid guid); + + void ClearClientData(); + + void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); + + void GetResults([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppenum); + + void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppsai); + } + + [ComImport(), + Guid(IIDGuid.IFileSaveDialog), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IFileSaveDialog : IFileDialog + { + [PreserveSig] + int Show([In] IntPtr parent); + + void SetFileTypes([In] uint cFileTypes, [In] ref COMDLG_FILTERSPEC rgFilterSpec); + + void SetFileTypeIndex([In] uint iFileType); + + void GetFileTypeIndex(out uint piFileType); + + void Advise([In, MarshalAs(UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie); + + void Unadvise([In] uint dwCookie); + + void SetOptions([In] FOS fos); + + void GetOptions(out FOS pfos); + + void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); + + void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); + + void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); + + void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, FileDialogCustomPlace fdcp); + + void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + void Close([MarshalAs(UnmanagedType.Error)] int hr); + + void SetClientGuid([In] ref Guid guid); + + void ClearClientData(); + + void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); + + void SetSaveAsItem([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); + + void SetProperties([In, MarshalAs(UnmanagedType.Interface)] IntPtr pStore); + + void SetCollectedProperties([In, MarshalAs(UnmanagedType.Interface)] IntPtr pList, [In] int fAppendDefault); + + void GetProperties([MarshalAs(UnmanagedType.Interface)] out IntPtr ppStore); + + void ApplyProperties([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In, MarshalAs(UnmanagedType.Interface)] IntPtr pStore, [In, ComAliasName("ShellObjects.wireHWND")] ref IntPtr hwnd, [In, MarshalAs(UnmanagedType.Interface)] IntPtr pSink); + } + + [ComImport, + Guid(IIDGuid.IFileDialogEvents), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IFileDialogEvents + { + // NOTE: some of these callbacks are cancelable - returning S_FALSE means that + // the dialog should not proceed (e.g. with closing, changing folder); to + // support this, we need to use the PreserveSig attribute to enable us to return + // the proper HRESULT + [PreserveSig] + int OnFileOk([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); + + [PreserveSig] + int OnFolderChanging([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psiFolder); + + void OnFolderChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); + + void OnSelectionChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); + + void OnShareViolation([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, out FDE_SHAREVIOLATION_RESPONSE pResponse); + + void OnTypeChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); + + void OnOverwrite([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, out FDE_OVERWRITE_RESPONSE pResponse); + } + + [ComImport, + Guid(IIDGuid.IShellItem), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IShellItem + { + void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid bhid, [In] ref Guid riid, out IntPtr ppv); + + void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); + + void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); + + void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs); + + void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder); + } + + internal enum SIGDN : uint + { + SIGDN_NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL + SIGDN_PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING + SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING + SIGDN_PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING + SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR + SIGDN_FILESYSPATH = 0x80058000, // SHGDN_FORPARSING + SIGDN_URL = 0x80068000, // SHGDN_FORPARSING + SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR + SIGDN_PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] + internal struct COMDLG_FILTERSPEC + { + [MarshalAs(UnmanagedType.LPWStr)] + internal string pszName; + [MarshalAs(UnmanagedType.LPWStr)] + internal string pszSpec; + } + + [Flags] + internal enum FOS : uint + { + FOS_OVERWRITEPROMPT = 0x00000002, + FOS_STRICTFILETYPES = 0x00000004, + FOS_NOCHANGEDIR = 0x00000008, + FOS_PICKFOLDERS = 0x00000020, + FOS_FORCEFILESYSTEM = 0x00000040, // Ensure that items returned are filesystem items. + FOS_ALLNONSTORAGEITEMS = 0x00000080, // Allow choosing items that have no storage. + FOS_NOVALIDATE = 0x00000100, + FOS_ALLOWMULTISELECT = 0x00000200, + FOS_PATHMUSTEXIST = 0x00000800, + FOS_FILEMUSTEXIST = 0x00001000, + FOS_CREATEPROMPT = 0x00002000, + FOS_SHAREAWARE = 0x00004000, + FOS_NOREADONLYRETURN = 0x00008000, + FOS_NOTESTFILECREATE = 0x00010000, + FOS_HIDEMRUPLACES = 0x00020000, + FOS_HIDEPINNEDPLACES = 0x00040000, + FOS_NODEREFERENCELINKS = 0x00100000, + FOS_DONTADDTORECENT = 0x02000000, + FOS_FORCESHOWHIDDEN = 0x10000000, + FOS_DEFAULTNOMINIMODE = 0x20000000 + } + + internal enum FDE_SHAREVIOLATION_RESPONSE + { + FDESVR_DEFAULT = 0x00000000, + FDESVR_ACCEPT = 0x00000001, + FDESVR_REFUSE = 0x00000002 + } + + internal enum FDE_OVERWRITE_RESPONSE + { + FDEOR_DEFAULT = 0x00000000, + FDEOR_ACCEPT = 0x00000001, + FDEOR_REFUSE = 0x00000002 + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/FixedPanel.cs b/WindowsForms/Managed/System/WinForms/FixedPanel.cs new file mode 100644 index 000000000..0f7aa4ea3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FixedPanel.cs @@ -0,0 +1,51 @@ + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies the Fixed Panel in the SplitContainer Control. + /// + /// + /// + public enum FixedPanel { + /// + /// + /// + /// No panel is fixed. Resize causes the Resize of both the panels. + /// + /// + None = 0, + + /// + /// + /// + /// Panel1 is Fixed. The resize will increase the size of second panel. + /// + /// + Panel1 = 1, + + /// + /// + /// + /// Panel2 is Fixed. The resize will increase the size of first panel. + /// + /// + Panel2 = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/FlatButtonAppearance.cs b/WindowsForms/Managed/System/WinForms/FlatButtonAppearance.cs new file mode 100644 index 000000000..f01277a6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FlatButtonAppearance.cs @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Globalization; + using System.ComponentModel; + using System.Windows.Forms.Layout; + + /// + /// + /// + [TypeConverter(typeof(FlatButtonAppearanceConverter))] + public class FlatButtonAppearance { + + private ButtonBase owner; + + private int borderSize = 1; + private Color borderColor = Color.Empty; + private Color checkedBackColor = Color.Empty; + private Color mouseDownBackColor = Color.Empty; + private Color mouseOverBackColor = Color.Empty; + + internal FlatButtonAppearance(ButtonBase owner) { + this.owner = owner; + } + + /// + /// + /// For buttons whose FlatStyle is FlatStyle.Flat, this property specifies the size, in pixels of the border around the button. + /// + [ + Browsable(true), + ApplicableToButton(), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonBorderSizeDescr), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(1), + ] + public int BorderSize { + get { + return borderSize; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException("BorderSize", value, SR.GetString(SR.InvalidLowBoundArgumentEx, "BorderSize", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + + if (borderSize != value) { + borderSize = value; + if (owner != null && owner.ParentInternal != null) { + LayoutTransaction.DoLayoutIf(owner.AutoSize, owner.ParentInternal, owner, PropertyNames.FlatAppearanceBorderSize); + } + owner.Invalidate(); + } + } + } + + /// + /// + /// For buttons whose FlatStyle is FlatStyle.Flat, this property specifies the color of the border around the button. + /// + [ + Browsable(true), + ApplicableToButton(), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonBorderColorDescr), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(typeof(Color), ""), + ] + public Color BorderColor { + get { + return borderColor; + } + set { + if (value.Equals(Color.Transparent)) { + throw new NotSupportedException(SR.GetString(SR.ButtonFlatAppearanceInvalidBorderColor)); + } + + if (borderColor != value) { + borderColor = value; + owner.Invalidate(); + } + } + } + + /// + /// + /// For buttons whose FlatStyle is FlatStyle.Flat, this property specifies the color of the client area + /// of the button when the button state is checked and the mouse cursor is NOT within the bounds of the control. + /// + [ + Browsable(true), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonCheckedBackColorDescr), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(typeof(Color), ""), + ] + public Color CheckedBackColor { + get { + return checkedBackColor; + } + set { + if (checkedBackColor != value) { + checkedBackColor = value; + owner.Invalidate(); + } + } + } + + /// + /// + /// For buttons whose FlatStyle is FlatStyle.Flat, this property specifies the color of the client area + /// of the button when the mouse cursor is within the bounds of the control and the left button is pressed. + /// + [ + Browsable(true), + ApplicableToButton(), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonMouseDownBackColorDescr), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(typeof(Color), ""), + ] + public Color MouseDownBackColor { + get { + return mouseDownBackColor; + } + set { + if (mouseDownBackColor != value) { + mouseDownBackColor = value; + owner.Invalidate(); + } + } + } + + /// + /// + /// For buttons whose FlatStyle is FlatStyle.Flat, this property specifies the color of the client + /// area of the button when the mouse cursor is within the bounds of the control. + /// + [ + Browsable(true), + ApplicableToButton(), + NotifyParentProperty(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ButtonMouseOverBackColorDescr), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(typeof(Color), ""), + ] + public Color MouseOverBackColor { + get { + return mouseOverBackColor; + } + set { + if (mouseOverBackColor != value) { + mouseOverBackColor = value; + owner.Invalidate(); + } + } + } + + } + + internal sealed class ApplicableToButtonAttribute : Attribute { + public ApplicableToButtonAttribute() { + } + } + + internal class FlatButtonAppearanceConverter : ExpandableObjectConverter { + + // Don't let the property grid display the full type name in the value cell + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == typeof(string)) { + return ""; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + // Don't let the property grid display the CheckedBackColor property for Button controls + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + if (context != null && context.Instance is Button) { + Attribute[] attributes2 = new Attribute[attributes.Length + 1]; + attributes.CopyTo(attributes2, 0); + attributes2[attributes.Length] = new ApplicableToButtonAttribute(); + attributes = attributes2; + } + + return TypeDescriptor.GetProperties(value, attributes); + } + + } + +} + diff --git a/WindowsForms/Managed/System/WinForms/FlatStyle.cs b/WindowsForms/Managed/System/WinForms/FlatStyle.cs new file mode 100644 index 000000000..26249a6e9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FlatStyle.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + /// + /// + /// Specifies the style of control to display. + /// + public enum FlatStyle { + /// + /// + /// + /// The control appears flat. + /// + /// + Flat, + /// + /// + /// + /// A control appears flat until the mouse pointer + /// moves over + /// it, at which point it appears three-dimensional. + /// + /// + Popup, + /// + /// + /// + /// The control appears three-dimensional. + /// + /// + Standard, + /// + /// + /// + /// The control appears three-dimensional. + /// + /// + System, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FlowDirection.cs b/WindowsForms/Managed/System/WinForms/FlowDirection.cs new file mode 100644 index 000000000..c2a5b8871 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FlowDirection.cs @@ -0,0 +1,13 @@ +namespace System.Windows.Forms { +/// + public enum FlowDirection { + /// + LeftToRight, + /// + TopDown, + /// + RightToLeft, + /// + BottomUp + } +} diff --git a/WindowsForms/Managed/System/WinForms/FlowLayoutPanel.cs b/WindowsForms/Managed/System/WinForms/FlowLayoutPanel.cs new file mode 100644 index 000000000..cc18f064e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FlowLayoutPanel.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Layout; + using System.Runtime.InteropServices; + + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [ProvideProperty("FlowBreak", typeof(Control))] + [DefaultProperty("FlowDirection")] + [Designer("System.Windows.Forms.Design.FlowLayoutPanelDesigner, " + AssemblyRef.SystemDesign)] + [Docking(DockingBehavior.Ask)] + [SRDescription(SR.DescriptionFlowLayoutPanel)] + public class FlowLayoutPanel : Panel, IExtenderProvider { + private FlowLayoutSettings _flowLayoutSettings; + + /// + public FlowLayoutPanel() { + _flowLayoutSettings = FlowLayout.CreateSettings(this); + } + + /// + public override LayoutEngine LayoutEngine { + get { return FlowLayout.Instance; } + } + + /// + [SRDescription(SR.FlowPanelFlowDirectionDescr)] + [DefaultValue(FlowDirection.LeftToRight)] + [SRCategory(SR.CatLayout)] + [Localizable(true)] + public FlowDirection FlowDirection { + get { return _flowLayoutSettings.FlowDirection; } + set { + _flowLayoutSettings.FlowDirection = value; + Debug.Assert(FlowDirection == value, "FlowDirection should be the same as we set it"); + } + } + + /// + [SRDescription(SR.FlowPanelWrapContentsDescr)] + [DefaultValue(true)] + [SRCategory(SR.CatLayout)] + [Localizable(true)] + public bool WrapContents { + get { return _flowLayoutSettings.WrapContents; } + set { + _flowLayoutSettings.WrapContents = value; + Debug.Assert(WrapContents == value, "WrapContents should be the same as we set it"); + } + } + + #region Provided properties + /// + /// + bool IExtenderProvider.CanExtend(object obj) { + Control control = obj as Control; + return control != null && control.Parent == this; + } + + [DefaultValue(false)] + [DisplayName("FlowBreak")] + public bool GetFlowBreak(Control control) { + return _flowLayoutSettings.GetFlowBreak(control); + } + + [DisplayName("FlowBreak")] + public void SetFlowBreak(Control control, bool value) { + _flowLayoutSettings.SetFlowBreak(control, value); + } + + #endregion + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FlowLayoutSettings.cs b/WindowsForms/Managed/System/WinForms/FlowLayoutSettings.cs new file mode 100644 index 000000000..c606b4fca --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FlowLayoutSettings.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Layout; + + /// + [DefaultProperty("FlowDirection")] + public class FlowLayoutSettings : LayoutSettings { + + internal FlowLayoutSettings(IArrangedElement owner) : base(owner) {} + + /// + public override LayoutEngine LayoutEngine { + get { return FlowLayout.Instance; } + } + + /// + [SRDescription(SR.FlowPanelFlowDirectionDescr)] + [DefaultValue(FlowDirection.LeftToRight)] + [SRCategory(SR.CatLayout)] + public FlowDirection FlowDirection { + get { return FlowLayout.GetFlowDirection(Owner); } + set { + FlowLayout.SetFlowDirection(Owner, value); + Debug.Assert(FlowDirection == value, "FlowDirection should be the same as we set it"); + } + } + + /// + [SRDescription(SR.FlowPanelWrapContentsDescr)] + [DefaultValue(true)] + [SRCategory(SR.CatLayout)] + public bool WrapContents { + get { return FlowLayout.GetWrapContents(Owner); } + set { + FlowLayout.SetWrapContents(Owner, value); + Debug.Assert(WrapContents == value, "WrapContents should be the same as we set it"); + } + } + + public void SetFlowBreak(object child, bool value) { + IArrangedElement element = FlowLayout.Instance.CastToArrangedElement(child); + if (GetFlowBreak(child) != value) { + CommonProperties.SetFlowBreak(element, value); + } + } + + public bool GetFlowBreak(object child) { + IArrangedElement element = FlowLayout.Instance.CastToArrangedElement(child); + return CommonProperties.GetFlowBreak(element); + } + + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FolderBrowserDialog.cs b/WindowsForms/Managed/System/WinForms/FolderBrowserDialog.cs new file mode 100644 index 000000000..a82fd6fab --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FolderBrowserDialog.cs @@ -0,0 +1,360 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.IO; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Represents a common dialog box that allows the user to specify options for + /// selecting a folder. This class cannot be inherited. + /// + /// + [ + DefaultEvent("HelpRequest"), + DefaultProperty("SelectedPath"), + Designer("System.Windows.Forms.Design.FolderBrowserDialogDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionFolderBrowserDialog) + ] + public sealed class FolderBrowserDialog : CommonDialog + { + // Root node of the tree view. + private Environment.SpecialFolder rootFolder; + + // Description text to show. + private string descriptionText; + + // Folder picked by the user. + private string selectedPath; + + // Show the 'New Folder' button? + private bool showNewFolderButton; + + // set to True when selectedPath is set after the dialog box returns + // set to False when selectedPath is set via the SelectedPath property. + // Warning! Be careful about race conditions when touching this variable. + // This variable is determining if the PathDiscovery security check should + // be bypassed or not. + private bool selectedPathNeedsCheck; + + // Callback function for the folder browser dialog (see VSWhidbey 551866) + private UnsafeNativeMethods.BrowseCallbackProc callback; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public FolderBrowserDialog() + { + Reset(); + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler HelpRequest + { + add + { + base.HelpRequest += value; + } + remove + { + base.HelpRequest -= value; + } + } + + /// + /// + /// Determines if the 'New Folder' button should be exposed. + /// + [ + Browsable(true), + DefaultValue(true), + Localizable(false), + SRCategory(SR.CatFolderBrowsing), + SRDescription(SR.FolderBrowserDialogShowNewFolderButton) + ] + public bool ShowNewFolderButton + { + get + { + return showNewFolderButton; + } + set + { + showNewFolderButton = value; + } + } + + /// + /// + /// Gets the directory path of the folder the user picked. + /// Sets the directory path of the initial folder shown in the dialog box. + /// + [ + Browsable(true), + DefaultValue(""), + Editor("System.Windows.Forms.Design.SelectedPathEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable(true), + SRCategory(SR.CatFolderBrowsing), + SRDescription(SR.FolderBrowserDialogSelectedPath) + ] + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: we want to protect when the dialog has been set by the user - this is directly set when in RunDialog + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + public string SelectedPath + { + get + { + if (selectedPath == null || selectedPath.Length == 0) + { + return selectedPath; + } + + if (selectedPathNeedsCheck) + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + selectedPath + ") Demanded"); + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, selectedPath).Demand(); + } + return selectedPath; + } + set + { + selectedPath = (value == null) ? String.Empty : value; + selectedPathNeedsCheck = false; + } + } + + /// + /// + /// Gets/sets the root node of the directory tree. + /// + [ + Browsable(true), + DefaultValue(System.Environment.SpecialFolder.Desktop), + Localizable(false), + SRCategory(SR.CatFolderBrowsing), + SRDescription(SR.FolderBrowserDialogRootFolder), + TypeConverter(typeof(SpecialFolderEnumConverter)) + ] + public System.Environment.SpecialFolder RootFolder + { + get + { + return rootFolder; + } + [SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")] + set + { + // FXCop: + // leaving in Enum.IsDefined because this Enum is likely to grow and we dont own it. + if (!Enum.IsDefined(typeof(System.Environment.SpecialFolder), value)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Environment.SpecialFolder)); + } + rootFolder = value; + } + } + + /// + /// + /// + /// Gets or sets a description to show above the folders. Here you can provide instructions for + /// selecting a folder. + /// + /// + [ + Browsable(true), + DefaultValue(""), + Localizable(true), + SRCategory(SR.CatFolderBrowsing), + SRDescription(SR.FolderBrowserDialogDescription) + ] + public string Description + { + get + { + return descriptionText; + } + set + { + descriptionText = (value == null) ? String.Empty : value; + } + } + + /// + /// Helper function that returns the IMalloc interface used by the shell. + /// + private static UnsafeNativeMethods.IMalloc GetSHMalloc() + { + UnsafeNativeMethods.IMalloc[] malloc = new UnsafeNativeMethods.IMalloc[1]; + UnsafeNativeMethods.Shell32.SHGetMalloc(malloc); + return malloc[0]; + } + + /// + /// + /// + /// Resets all properties to their default values. + /// + /// + public override void Reset() + { + rootFolder = System.Environment.SpecialFolder.Desktop; + descriptionText = String.Empty; + selectedPath = String.Empty; + selectedPathNeedsCheck = false; + showNewFolderButton = true; + } + + /// + /// + /// Implements running of a folder browser dialog. + /// + protected override bool RunDialog(IntPtr hWndOwner) + { + IntPtr pidlRoot = IntPtr.Zero; + bool returnValue = false; + + UnsafeNativeMethods.Shell32.SHGetSpecialFolderLocation(hWndOwner, (int) rootFolder, ref pidlRoot); + if (pidlRoot == IntPtr.Zero) + { + UnsafeNativeMethods.Shell32.SHGetSpecialFolderLocation(hWndOwner, NativeMethods.CSIDL_DESKTOP, ref pidlRoot); + if (pidlRoot == IntPtr.Zero) + { + throw new InvalidOperationException(SR.GetString(SR.FolderBrowserDialogNoRootFolder)); + } + } + + int mergedOptions = unchecked( (int) (long)UnsafeNativeMethods.BrowseInfos.NewDialogStyle); + if (!showNewFolderButton) + { + mergedOptions += unchecked( (int) (long)UnsafeNativeMethods.BrowseInfos.HideNewFolderButton); + } + + // The SHBrowserForFolder dialog is OLE/COM based, and documented as only being safe to use under the STA + // threading model if the BIF_NEWDIALOGSTYLE flag has been requested (which we always do in mergedOptions + // above). So make sure OLE is initialized, and throw an exception if caller attempts to invoke dialog + // under the MTA threading model (...dialog does appear under MTA, but is totally non-functional). + if (Control.CheckForIllegalCrossThreadCalls && Application.OleRequired() != System.Threading.ApartmentState.STA) + { + throw new System.Threading.ThreadStateException(SR.GetString(SR.DebuggingExceptionOnly, SR.GetString(SR.ThreadMustBeSTA))); + } + + IntPtr pidlRet = IntPtr.Zero; + IntPtr pszDisplayName = IntPtr.Zero; + IntPtr pszSelectedPath = IntPtr.Zero; + + try + { + // Construct a BROWSEINFO + UnsafeNativeMethods.BROWSEINFO bi = new UnsafeNativeMethods.BROWSEINFO(); + + pszDisplayName = Marshal.AllocHGlobal(NativeMethods.MAX_PATH * Marshal.SystemDefaultCharSize); + pszSelectedPath = Marshal.AllocHGlobal((NativeMethods.MAX_PATH + 1) * Marshal.SystemDefaultCharSize); + this.callback = new UnsafeNativeMethods.BrowseCallbackProc(this.FolderBrowserDialog_BrowseCallbackProc); + + bi.pidlRoot = pidlRoot; + bi.hwndOwner = hWndOwner; + bi.pszDisplayName = pszDisplayName; + bi.lpszTitle = descriptionText; + bi.ulFlags = mergedOptions; + bi.lpfn = callback; + bi.lParam = IntPtr.Zero; + bi.iImage = 0; + + // And show the dialog + pidlRet = UnsafeNativeMethods.Shell32.SHBrowseForFolder(bi); + + if (pidlRet != IntPtr.Zero) + { + // Then retrieve the path from the IDList + UnsafeNativeMethods.Shell32.SHGetPathFromIDListLongPath(pidlRet, ref pszSelectedPath); + + // set the flag to True before selectedPath is set to + // assure security check and avoid bogus race condition + selectedPathNeedsCheck = true; + + // Convert to a string + selectedPath = Marshal.PtrToStringAuto(pszSelectedPath); + + returnValue = true; + } + } + finally + { + UnsafeNativeMethods.CoTaskMemFree(pidlRoot); + if (pidlRet != IntPtr.Zero) + { + UnsafeNativeMethods.CoTaskMemFree(pidlRet); + } + + // Then free all the stuff we've allocated or the SH API gave us + if (pszSelectedPath != IntPtr.Zero) + { + Marshal.FreeHGlobal(pszSelectedPath); + } + if (pszDisplayName != IntPtr.Zero) + { + Marshal.FreeHGlobal(pszDisplayName); + } + + this.callback = null; + } + return returnValue; + } + + /// + /// Callback function used to enable/disable the OK button, + /// and select the initial folder. + /// + private int FolderBrowserDialog_BrowseCallbackProc(IntPtr hwnd, + int msg, + IntPtr lParam, + IntPtr lpData) + { + switch (msg) + { + case NativeMethods.BFFM_INITIALIZED: + // Indicates the browse dialog box has finished initializing. The lpData value is zero. + if (selectedPath.Length != 0) + { + // Try to select the folder specified by selectedPath + UnsafeNativeMethods.SendMessage(new HandleRef(null, hwnd), (int) NativeMethods.BFFM_SETSELECTION, 1, selectedPath); + } + break; + case NativeMethods.BFFM_SELCHANGED: + // Indicates the selection has changed. The lpData parameter points to the item identifier list for the newly selected item. + IntPtr selectedPidl = lParam; + if (selectedPidl != IntPtr.Zero) + { + IntPtr pszSelectedPath = Marshal.AllocHGlobal((NativeMethods.MAX_PATH + 1) * Marshal.SystemDefaultCharSize); + // Try to retrieve the path from the IDList + bool isFileSystemFolder = UnsafeNativeMethods.Shell32.SHGetPathFromIDListLongPath(selectedPidl, ref pszSelectedPath); + Marshal.FreeHGlobal(pszSelectedPath); + UnsafeNativeMethods.SendMessage(new HandleRef(null, hwnd), (int) NativeMethods.BFFM_ENABLEOK, 0, isFileSystemFolder ? 1 : 0); + } + break; + } + return 0; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/FontDialog.cs b/WindowsForms/Managed/System/WinForms/FontDialog.cs new file mode 100644 index 000000000..397f3d6f0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FontDialog.cs @@ -0,0 +1,713 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Drawing; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Windows.Forms; + using System.Security; + using System.Security.Permissions; + using System.Runtime.Versioning; + using Microsoft.Win32; + + /// + /// + /// + /// Represents + /// a common dialog box that displays a list of fonts that are currently installed + /// on + /// the system. + /// + /// + [ + DefaultEvent("Apply"), + DefaultProperty("Font"), + SRDescription(SR.DescriptionFontDialog) + ] + public class FontDialog : CommonDialog { + /// + /// + /// [To be supplied.] + /// + protected static readonly object EventApply = new object(); + + private const int defaultMinSize = 0; + private const int defaultMaxSize = 0; + + private int options; + private Font font; + private Color color; + private int minSize = defaultMinSize; + private int maxSize = defaultMaxSize; + private bool showColor = false; + private bool usingDefaultIndirectColor = false; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not call Reset + // it would be a breaking change. + ] + public FontDialog() { + Reset(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box allows graphics device interface + /// (GDI) font simulations. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FnDallowSimulationsDescr) + ] + public bool AllowSimulations { + get { + return !GetOption(NativeMethods.CF_NOSIMULATIONS); + } + + set { + SetOption(NativeMethods.CF_NOSIMULATIONS, !value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box allows vector font selections. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FnDallowVectorFontsDescr) + ] + public bool AllowVectorFonts { + get { + return !GetOption(NativeMethods.CF_NOVECTORFONTS); + } + + set { + SetOption(NativeMethods.CF_NOVECTORFONTS, !value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether + /// the dialog box displays both vertical and horizontal fonts or only + /// horizontal fonts. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FnDallowVerticalFontsDescr) + ] + public bool AllowVerticalFonts { + get { + return !GetOption(NativeMethods.CF_NOVERTFONTS); + } + + set { + SetOption(NativeMethods.CF_NOVERTFONTS, !value); + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating whether the user can change the character set specified + /// in the Script combo box to display a character set other than the one + /// currently displayed. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FnDallowScriptChangeDescr) + ] + public bool AllowScriptChange { + get { + return !GetOption(NativeMethods.CF_SELECTSCRIPT); + } + + set { + SetOption(NativeMethods.CF_SELECTSCRIPT, !value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating the selected font color. + /// + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.FnDcolorDescr), + DefaultValue(typeof(Color), "Black") + ] + public Color Color { + get { + // Convert to RGB and back to resolve indirect colors like SystemColors.ControlText + // to real color values like Color.Lime + if (usingDefaultIndirectColor) { + return ColorTranslator.FromWin32(ColorTranslator.ToWin32(color)); + } + return color; + } + set { + if (!value.IsEmpty) { + color = value; + usingDefaultIndirectColor = false; + } + else { + color = SystemColors.ControlText; + usingDefaultIndirectColor = true; + } + } + } + + /// + /// + /// + /// Gets or sets + /// a value indicating whether the dialog box allows only the selection of fixed-pitch fonts. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDfixedPitchOnlyDescr) + ] + public bool FixedPitchOnly { + get { + return GetOption(NativeMethods.CF_FIXEDPITCHONLY); + } + + set { + SetOption(NativeMethods.CF_FIXEDPITCHONLY, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating the selected font. + /// + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.FnDfontDescr) + ] + public Font Font { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + Font result = font; + if (result == null) + result = Control.DefaultFont; + + float actualSize = result.SizeInPoints; + if (minSize != defaultMinSize && actualSize < MinSize) + result = new Font(result.FontFamily, MinSize, result.Style, GraphicsUnit.Point); + if (maxSize != defaultMaxSize && actualSize > MaxSize) + result = new Font(result.FontFamily, MaxSize, result.Style, GraphicsUnit.Point); + + return result; + } + set { + font = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box specifies an error condition if the + /// user attempts to select a font or style that does not exist. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDfontMustExistDescr) + ] + public bool FontMustExist { + get { + return GetOption(NativeMethods.CF_FORCEFONTEXIST); + } + + set { + SetOption(NativeMethods.CF_FORCEFONTEXIST, value); + } + } + + /// + /// + /// + /// Gets or sets the maximum + /// point size a user can select. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(defaultMaxSize), + SRDescription(SR.FnDmaxSizeDescr) + ] + public int MaxSize { + get { + return maxSize; + } + set { + if (value < 0) { + value = 0; + } + maxSize = value; + + if (maxSize > 0 && maxSize < minSize) { + minSize = maxSize; + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating the minimum point size a user can select. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(defaultMinSize), + SRDescription(SR.FnDminSizeDescr) + ] + public int MinSize { + get { + return minSize; + } + set { + if (value < 0) { + value = 0; + } + minSize = value; + + if (maxSize > 0 && maxSize < minSize) { + maxSize = minSize; + } + } + } + + /// + /// + /// + /// + /// Gets the value passed to CHOOSEFONT.Flags. + /// + /// + protected int Options { + get { + return options; + } + } + + /// + /// + /// + /// Gets or sets a + /// value indicating whether the dialog box allows selection of fonts for all non-OEM and Symbol character + /// sets, as well as the ----n National Standards Institute (ANSI) character set. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDscriptsOnlyDescr) + ] + public bool ScriptsOnly { + get { + return GetOption(NativeMethods.CF_SCRIPTSONLY); + } + set { + SetOption(NativeMethods.CF_SCRIPTSONLY, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box contains an Apply button. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDshowApplyDescr) + ] + public bool ShowApply { + get { + return GetOption(NativeMethods.CF_APPLY); + } + set { + SetOption(NativeMethods.CF_APPLY, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box displays the color choice. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDshowColorDescr) + ] + public bool ShowColor { + get { + return showColor; + } + set { + this.showColor = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box contains controls that allow the + /// user to specify strikethrough, underline, and text color options. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.FnDshowEffectsDescr) + ] + public bool ShowEffects { + get { + return GetOption(NativeMethods.CF_EFFECTS); + } + set { + SetOption(NativeMethods.CF_EFFECTS, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box displays a Help button. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.FnDshowHelpDescr) + ] + public bool ShowHelp { + get { + return GetOption(NativeMethods.CF_SHOWHELP); + } + set { + SetOption(NativeMethods.CF_SHOWHELP, value); + } + } + + /// + /// + /// + /// Occurs when the user clicks the Apply button in the font + /// dialog box. + /// + /// + [SRDescription(SR.FnDapplyDescr)] + public event EventHandler Apply { + add { + Events.AddHandler(EventApply, value); + } + remove { + Events.RemoveHandler(EventApply, value); + } + } + + /// + /// + /// Returns the state of the given option flag. + /// + /// + internal bool GetOption(int option) { + return(options & option) != 0; + } + + /// + /// + /// + /// Specifies the common dialog box hook procedure that is overridden to add + /// specific functionality to a common dialog box. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + + switch (msg) { + case NativeMethods.WM_COMMAND: + if ((int)wparam == 0x402) { + NativeMethods.LOGFONT lf = new NativeMethods.LOGFONT(); + UnsafeNativeMethods.SendMessage(new HandleRef(null, hWnd), NativeMethods.WM_CHOOSEFONT_GETLOGFONT, 0, lf); + UpdateFont(lf); + int index = (int)UnsafeNativeMethods.SendDlgItemMessage(new HandleRef(null, hWnd), 0x473, NativeMethods.CB_GETCURSEL, IntPtr.Zero, IntPtr.Zero); + if (index != NativeMethods.CB_ERR) { + UpdateColor((int)UnsafeNativeMethods.SendDlgItemMessage(new HandleRef(null, hWnd), 0x473, + NativeMethods.CB_GETITEMDATA, (IntPtr) index, IntPtr.Zero)); + } + if (NativeWindow.WndProcShouldBeDebuggable) { + OnApply(EventArgs.Empty); + } + else { + try + { + OnApply(EventArgs.Empty); + } + catch (Exception e) + { + Application.OnThreadException(e); + } + } + } + break; + case NativeMethods.WM_INITDIALOG: + if (!showColor) { + IntPtr hWndCtl = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, hWnd), NativeMethods.cmb4); + SafeNativeMethods.ShowWindow(new HandleRef(null, hWndCtl), NativeMethods.SW_HIDE); + hWndCtl = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, hWnd), NativeMethods.stc4); + SafeNativeMethods.ShowWindow(new HandleRef(null, hWndCtl), NativeMethods.SW_HIDE); + } + break; + } + + return base.HookProc(hWnd, msg, wparam, lparam); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnApply(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventApply]; + if (handler != null) handler(this, e); + } + + /// + /// + /// + /// Resets all dialog box options to their default values. + /// + /// + public override void Reset() { + options = NativeMethods.CF_SCREENFONTS | NativeMethods.CF_EFFECTS; + font = null; + color = SystemColors.ControlText; + usingDefaultIndirectColor = true; + showColor = false; + minSize = defaultMinSize; + maxSize = defaultMaxSize; + SetOption(NativeMethods.CF_TTONLY, true); + } + + private void ResetFont() { + font = null; + } + + /// + /// + /// + /// + /// The actual implementation of running the dialog. Inheriting classes + /// should override this if they want to add more functionality, and call + /// base.runDialog() if necessary + /// + /// + /// + protected override bool RunDialog(IntPtr hWndOwner) { + NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); + NativeMethods.CHOOSEFONT cf = new NativeMethods.CHOOSEFONT(); + IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + NativeMethods.LOGFONT lf = new NativeMethods.LOGFONT(); + + Graphics graphics = Graphics.FromHdcInternal(screenDC); + + // SECREVIEW : The Font.ToLogFont method is marked with the 'unsafe' modifier cause it needs to write bytes into the logFont struct, + // here we are passing that struct so the assert is safe. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + Font.ToLogFont(lf, graphics); + } + finally { + CodeAccessPermission.RevertAssert(); + graphics.Dispose(); + } + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC)); + + IntPtr logFontPtr = IntPtr.Zero; + try { + logFontPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NativeMethods.LOGFONT))); + Marshal.StructureToPtr(lf, logFontPtr, false); + + cf.lStructSize = Marshal.SizeOf(typeof(NativeMethods.CHOOSEFONT)); + cf.hwndOwner = hWndOwner; + cf.hDC = IntPtr.Zero; + cf.lpLogFont = logFontPtr; + cf.Flags = Options | NativeMethods.CF_INITTOLOGFONTSTRUCT | NativeMethods.CF_ENABLEHOOK; + if (minSize > 0 || maxSize > 0) { + cf.Flags |= NativeMethods.CF_LIMITSIZE; + } + + //if ShowColor=true then try to draw the sample text in color, + //if ShowEffects=false then we will draw the sample text in standard control text color regardless. + //(limitation of windows control) + // + if (ShowColor || ShowEffects) { + cf.rgbColors = ColorTranslator.ToWin32(color); + } + else { + cf.rgbColors = ColorTranslator.ToWin32(SystemColors.ControlText); + } + + cf.lpfnHook = hookProcPtr; + cf.hInstance = UnsafeNativeMethods.GetModuleHandle(null); + cf.nSizeMin = minSize; + if (maxSize == 0) { + cf.nSizeMax = Int32.MaxValue; + } + else { + cf.nSizeMax = maxSize; + } + Debug.Assert(cf.nSizeMin <= cf.nSizeMax, "min and max font sizes are the wrong way around"); + if (!SafeNativeMethods.ChooseFont(cf)) return false; + + + NativeMethods.LOGFONT lfReturned = null; + lfReturned = (NativeMethods.LOGFONT)UnsafeNativeMethods.PtrToStructure(logFontPtr, typeof(NativeMethods.LOGFONT)); + + if (lfReturned.lfFaceName != null && lfReturned.lfFaceName.Length > 0) { + lf = lfReturned; + UpdateFont(lf); + UpdateColor(cf.rgbColors); + } + + return true; + } + finally { + if (logFontPtr != IntPtr.Zero) + Marshal.FreeCoTaskMem(logFontPtr); + } + } + + /// + /// + /// Sets the given option to the given boolean value. + /// + /// + internal void SetOption(int option, bool value) { + if (value) { + options |= option; + } + else { + options &= ~option; + } + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + private bool ShouldSerializeFont() { + return !Font.Equals(Control.DefaultFont); + } + + /// + /// + /// + /// + /// Retrieves a string that includes the name of the current font selected in + /// the dialog box. + /// + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", Font: " + Font.ToString(); + } + + /// + /// + /// + /// + private void UpdateColor(int rgb) { + if (ColorTranslator.ToWin32(color) != rgb) { + color = ColorTranslator.FromOle(rgb); + usingDefaultIndirectColor = false; + } + } + + /// + /// + /// + /// + private void UpdateFont(NativeMethods.LOGFONT lf) { + IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + try { + Font fontInWorldUnits = null; + try { + IntSecurity.UnmanagedCode.Assert(); + try { + fontInWorldUnits = Font.FromLogFont(lf, screenDC); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + // The dialog claims its working in points (a device-independent unit), + // but actually gives us something in world units (device-dependent). + font = ControlPaint.FontInPoints(fontInWorldUnits); + } + finally { + if (fontInWorldUnits != null) { + fontInWorldUnits.Dispose(); + } + } + } + finally { + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC)); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Form.cs b/WindowsForms/Managed/System/WinForms/Form.cs new file mode 100644 index 000000000..6c25c67c4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Form.cs @@ -0,0 +1,7972 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1999, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Globalization; + using System.Net; + using System.Reflection; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Security.Policy; + using System.Threading; + using System.Windows.Forms.Design; + using System.Windows.Forms.Layout; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// Represents a window or dialog box that makes up an application's user interface. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + ToolboxItemFilter("System.Windows.Forms.Control.TopLevel"), + ToolboxItem(false), + DesignTimeVisible(false), + Designer("System.Windows.Forms.Design.FormDocumentDesigner, " + AssemblyRef.SystemDesign, typeof(IRootDesigner)), + DesignerCategory("Form"), + DefaultEvent("Load"), + InitializationEvent("Load"), + ] + public class Form : ContainerControl { +#if DEBUG + static readonly BooleanSwitch AlwaysRestrictWindows = new BooleanSwitch("AlwaysRestrictWindows", "Always make Form classes behave as though they are restricted"); +#endif + private static readonly object EVENT_ACTIVATED = new object(); + private static readonly object EVENT_CLOSING = new object(); + private static readonly object EVENT_CLOSED = new object(); + private static readonly object EVENT_FORMCLOSING = new object(); + private static readonly object EVENT_FORMCLOSED = new object(); + private static readonly object EVENT_DEACTIVATE = new object(); + private static readonly object EVENT_LOAD = new object(); + private static readonly object EVENT_MDI_CHILD_ACTIVATE = new object(); + private static readonly object EVENT_INPUTLANGCHANGE = new object(); + private static readonly object EVENT_INPUTLANGCHANGEREQUEST = new object(); + private static readonly object EVENT_MENUSTART = new object(); + private static readonly object EVENT_MENUCOMPLETE = new object(); + private static readonly object EVENT_MAXIMUMSIZECHANGED = new object(); + private static readonly object EVENT_MINIMUMSIZECHANGED = new object(); + private static readonly object EVENT_HELPBUTTONCLICKED = new object(); + private static readonly object EVENT_SHOWN = new object(); + private static readonly object EVENT_RESIZEBEGIN = new object(); + private static readonly object EVENT_RESIZEEND = new object(); + private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object(); + private static readonly object EVENT_DPI_CHANGED = new object(); + + // + // The following flags should be used with formState[..] not formStateEx[..] + // Don't add any more sections to this vector, it is already full. + // + private static readonly BitVector32.Section FormStateAllowTransparency = BitVector32.CreateSection(1); + private static readonly BitVector32.Section FormStateBorderStyle = BitVector32.CreateSection(6, FormStateAllowTransparency); + private static readonly BitVector32.Section FormStateTaskBar = BitVector32.CreateSection(1, FormStateBorderStyle); + private static readonly BitVector32.Section FormStateControlBox = BitVector32.CreateSection(1, FormStateTaskBar); + private static readonly BitVector32.Section FormStateKeyPreview = BitVector32.CreateSection(1, FormStateControlBox); + private static readonly BitVector32.Section FormStateLayered = BitVector32.CreateSection(1, FormStateKeyPreview); + private static readonly BitVector32.Section FormStateMaximizeBox = BitVector32.CreateSection(1, FormStateLayered); + private static readonly BitVector32.Section FormStateMinimizeBox = BitVector32.CreateSection(1, FormStateMaximizeBox); + private static readonly BitVector32.Section FormStateHelpButton = BitVector32.CreateSection(1, FormStateMinimizeBox); + private static readonly BitVector32.Section FormStateStartPos = BitVector32.CreateSection(4, FormStateHelpButton); + private static readonly BitVector32.Section FormStateWindowState = BitVector32.CreateSection(2, FormStateStartPos); + private static readonly BitVector32.Section FormStateShowWindowOnCreate = BitVector32.CreateSection(1, FormStateWindowState); + private static readonly BitVector32.Section FormStateAutoScaling = BitVector32.CreateSection(1, FormStateShowWindowOnCreate); + private static readonly BitVector32.Section FormStateSetClientSize = BitVector32.CreateSection(1, FormStateAutoScaling); + private static readonly BitVector32.Section FormStateTopMost = BitVector32.CreateSection(1, FormStateSetClientSize); + private static readonly BitVector32.Section FormStateSWCalled = BitVector32.CreateSection(1, FormStateTopMost); + private static readonly BitVector32.Section FormStateMdiChildMax = BitVector32.CreateSection(1, FormStateSWCalled); + private static readonly BitVector32.Section FormStateRenderSizeGrip = BitVector32.CreateSection(1, FormStateMdiChildMax); + private static readonly BitVector32.Section FormStateSizeGripStyle = BitVector32.CreateSection(2, FormStateRenderSizeGrip); + private static readonly BitVector32.Section FormStateIsRestrictedWindow = BitVector32.CreateSection(1, FormStateSizeGripStyle); + private static readonly BitVector32.Section FormStateIsRestrictedWindowChecked = BitVector32.CreateSection(1, FormStateIsRestrictedWindow); + private static readonly BitVector32.Section FormStateIsWindowActivated = BitVector32.CreateSection(1, FormStateIsRestrictedWindowChecked); + private static readonly BitVector32.Section FormStateIsTextEmpty = BitVector32.CreateSection(1, FormStateIsWindowActivated); + private static readonly BitVector32.Section FormStateIsActive = BitVector32.CreateSection(1, FormStateIsTextEmpty); + private static readonly BitVector32.Section FormStateIconSet = BitVector32.CreateSection(1, FormStateIsActive); + +#if SECURITY_DIALOG + private static readonly BitVector32.Section FormStateAddedSecurityMenuItem = BitVector32.CreateSection(1, FormStateIconSet); +#endif + + // + // The following flags should be used with formStateEx[...] not formState[..] + // + private static readonly BitVector32.Section FormStateExCalledClosing = BitVector32.CreateSection(1); + private static readonly BitVector32.Section FormStateExUpdateMenuHandlesSuspendCount = BitVector32.CreateSection(8, FormStateExCalledClosing); + private static readonly BitVector32.Section FormStateExUpdateMenuHandlesDeferred = BitVector32.CreateSection(1, FormStateExUpdateMenuHandlesSuspendCount); + private static readonly BitVector32.Section FormStateExUseMdiChildProc = BitVector32.CreateSection(1, FormStateExUpdateMenuHandlesDeferred); + private static readonly BitVector32.Section FormStateExCalledOnLoad = BitVector32.CreateSection(1, FormStateExUseMdiChildProc); + private static readonly BitVector32.Section FormStateExCalledMakeVisible = BitVector32.CreateSection(1, FormStateExCalledOnLoad); + private static readonly BitVector32.Section FormStateExCalledCreateControl = BitVector32.CreateSection(1, FormStateExCalledMakeVisible); + private static readonly BitVector32.Section FormStateExAutoSize = BitVector32.CreateSection(1, FormStateExCalledCreateControl); + private static readonly BitVector32.Section FormStateExInUpdateMdiControlStrip = BitVector32.CreateSection(1, FormStateExAutoSize); + private static readonly BitVector32.Section FormStateExShowIcon = BitVector32.CreateSection(1, FormStateExInUpdateMdiControlStrip); + private static readonly BitVector32.Section FormStateExMnemonicProcessed = BitVector32.CreateSection(1, FormStateExShowIcon); + private static readonly BitVector32.Section FormStateExInScale = BitVector32.CreateSection(1, FormStateExMnemonicProcessed); + private static readonly BitVector32.Section FormStateExInModalSizingLoop = BitVector32.CreateSection(1, FormStateExInScale); + private static readonly BitVector32.Section FormStateExSettingAutoScale = BitVector32.CreateSection(1, FormStateExInModalSizingLoop); + private static readonly BitVector32.Section FormStateExWindowBoundsWidthIsClientSize = BitVector32.CreateSection(1, FormStateExSettingAutoScale); + private static readonly BitVector32.Section FormStateExWindowBoundsHeightIsClientSize = BitVector32.CreateSection(1, FormStateExWindowBoundsWidthIsClientSize); + private static readonly BitVector32.Section FormStateExWindowClosing = BitVector32.CreateSection(1, FormStateExWindowBoundsHeightIsClientSize); + + private const int SizeGripSize = 16; + + private static Icon defaultIcon = null; + private static Icon defaultRestrictedIcon = null; +#if MAGIC_PADDING + private static Padding FormPadding = new Padding(9); // UI guideline +#endif + private static object internalSyncObject = new object(); + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + // + private static readonly int PropAcceptButton = PropertyStore.CreateKey(); + private static readonly int PropCancelButton = PropertyStore.CreateKey(); + private static readonly int PropDefaultButton = PropertyStore.CreateKey(); + private static readonly int PropDialogOwner = PropertyStore.CreateKey(); + + private static readonly int PropMainMenu = PropertyStore.CreateKey(); + private static readonly int PropDummyMenu = PropertyStore.CreateKey(); + private static readonly int PropCurMenu = PropertyStore.CreateKey(); + private static readonly int PropMergedMenu = PropertyStore.CreateKey(); + + private static readonly int PropOwner = PropertyStore.CreateKey(); + private static readonly int PropOwnedForms = PropertyStore.CreateKey(); + private static readonly int PropMaximizedBounds = PropertyStore.CreateKey(); + private static readonly int PropOwnedFormsCount = PropertyStore.CreateKey(); + + private static readonly int PropMinTrackSizeWidth = PropertyStore.CreateKey(); + private static readonly int PropMinTrackSizeHeight = PropertyStore.CreateKey(); + private static readonly int PropMaxTrackSizeWidth = PropertyStore.CreateKey(); + private static readonly int PropMaxTrackSizeHeight = PropertyStore.CreateKey(); + + private static readonly int PropFormMdiParent = PropertyStore.CreateKey(); + private static readonly int PropActiveMdiChild = PropertyStore.CreateKey(); + private static readonly int PropFormerlyActiveMdiChild = PropertyStore.CreateKey(); + private static readonly int PropMdiChildFocusable = PropertyStore.CreateKey(); + + private static readonly int PropMainMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropMdiWindowListStrip = PropertyStore.CreateKey(); + private static readonly int PropMdiControlStrip = PropertyStore.CreateKey(); + private static readonly int PropSecurityTip = PropertyStore.CreateKey(); + + private static readonly int PropOpacity = PropertyStore.CreateKey(); + private static readonly int PropTransparencyKey = PropertyStore.CreateKey(); +#if SECURITY_DIALOG + private static readonly int PropSecuritySystemMenuItem = PropertyStore.CreateKey(); +#endif + + /////////////////////////////////////////////////////////////////////// + // Form per instance members + // + // Note: Do not add anything to this list unless absolutely neccessary. + // + // Begin Members { + + // List of properties that are generally set, so we keep them directly on + // Form. + // + + private BitVector32 formState = new BitVector32(0x21338); // magic value... all the defaults... see the ctor for details... + private BitVector32 formStateEx = new BitVector32(); + + + private Icon icon; + private Icon smallIcon; + private Size autoScaleBaseSize = System.Drawing.Size.Empty; + private Size minAutoSize = Size.Empty; + private Rectangle restoredWindowBounds = new Rectangle(-1, -1, -1, -1); + private BoundsSpecified restoredWindowBoundsSpecified; + private DialogResult dialogResult; + private MdiClient ctlClient; + private NativeWindow ownerWindow; + private string userWindowText; // Used to cache user's text in semi-trust since the window text is added security info. + private string securityZone; + private string securitySite; + private bool rightToLeftLayout = false; + + + //Whidbey RestoreBounds ... + private Rectangle restoreBounds = new Rectangle(-1, -1, -1, -1); + private CloseReason closeReason = CloseReason.None; + + private VisualStyleRenderer sizeGripRenderer; + + // } End Members + /////////////////////////////////////////////////////////////////////// + + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public Form() + : base() { + + // we must setup the formState *before* calling Control's ctor... so we do that + // at the member variable... that magic number is generated by switching + // the line below to "true" and running a form. + // + // keep the "init" and "assert" sections always in sync! + // +#if false + // init section... + // + formState[FormStateAllowTransparency] = 0; + formState[FormStateBorderStyle] = (int)FormBorderStyle.Sizable; + formState[FormStateTaskBar] = 1; + formState[FormStateControlBox] = 1; + formState[FormStateKeyPreview] = 0; + formState[FormStateLayered] = 0; + formState[FormStateMaximizeBox] = 1; + formState[FormStateMinimizeBox] = 1; + formState[FormStateHelpButton] = 0; + formState[FormStateStartPos] = (int)FormStartPosition.WindowsDefaultLocation; + formState[FormStateWindowState] = (int)FormWindowState.Normal; + formState[FormStateShowWindowOnCreate] = 0; + formState[FormStateAutoScaling] = 1; + formState[FormStateSetClientSize] = 0; + formState[FormStateTopMost] = 0; + formState[FormStateSWCalled] = 0; + formState[FormStateMdiChildMax] = 0; + formState[FormStateRenderSizeGrip] = 0; + formState[FormStateSizeGripStyle] = 0; + formState[FormStateIsRestrictedWindow] = 0; + formState[FormStateIsRestrictedWindowChecked] = 0; + formState[FormStateIsWindowActivated] = 0; + formState[FormStateIsTextEmpty] = 0; + formState[FormStateIsActive] = 0; + formState[FormStateIconSet] = 0; + +#if SECURITY_DIALOG + formState[FormStateAddedSecurityMenuItem] = 0; + +#endif + + + + + Debug.WriteLine("initial formState: 0x" + formState.Data.ToString("X")); +#endif + // assert section... + // + Debug.Assert(formState[FormStateAllowTransparency] == 0, "Failed to set formState[FormStateAllowTransparency]"); + Debug.Assert(formState[FormStateBorderStyle] == (int)FormBorderStyle.Sizable, "Failed to set formState[FormStateBorderStyle]"); + Debug.Assert(formState[FormStateTaskBar] == 1, "Failed to set formState[FormStateTaskBar]"); + Debug.Assert(formState[FormStateControlBox] == 1, "Failed to set formState[FormStateControlBox]"); + Debug.Assert(formState[FormStateKeyPreview] == 0, "Failed to set formState[FormStateKeyPreview]"); + Debug.Assert(formState[FormStateLayered] == 0, "Failed to set formState[FormStateLayered]"); + Debug.Assert(formState[FormStateMaximizeBox] == 1, "Failed to set formState[FormStateMaximizeBox]"); + Debug.Assert(formState[FormStateMinimizeBox] == 1, "Failed to set formState[FormStateMinimizeBox]"); + Debug.Assert(formState[FormStateHelpButton] == 0, "Failed to set formState[FormStateHelpButton]"); + Debug.Assert(formState[FormStateStartPos] == (int)FormStartPosition.WindowsDefaultLocation, "Failed to set formState[FormStateStartPos]"); + Debug.Assert(formState[FormStateWindowState] == (int)FormWindowState.Normal, "Failed to set formState[FormStateWindowState]"); + Debug.Assert(formState[FormStateShowWindowOnCreate] == 0, "Failed to set formState[FormStateShowWindowOnCreate]"); + Debug.Assert(formState[FormStateAutoScaling] == 1, "Failed to set formState[FormStateAutoScaling]"); + Debug.Assert(formState[FormStateSetClientSize] == 0, "Failed to set formState[FormStateSetClientSize]"); + Debug.Assert(formState[FormStateTopMost] == 0, "Failed to set formState[FormStateTopMost]"); + Debug.Assert(formState[FormStateSWCalled] == 0, "Failed to set formState[FormStateSWCalled]"); + Debug.Assert(formState[FormStateMdiChildMax] == 0, "Failed to set formState[FormStateMdiChildMax]"); + Debug.Assert(formState[FormStateRenderSizeGrip] == 0, "Failed to set formState[FormStateRenderSizeGrip]"); + Debug.Assert(formState[FormStateSizeGripStyle] == 0, "Failed to set formState[FormStateSizeGripStyle]"); + // can't check these... Control::.ctor may force the check + // of security... you can only assert these are 0 when running + // under full trust... + // + //Debug.Assert(formState[FormStateIsRestrictedWindow] == 0, "Failed to set formState[FormStateIsRestrictedWindow]"); + //Debug.Assert(formState[FormStateIsRestrictedWindowChecked] == 0, "Failed to set formState[FormStateIsRestrictedWindowChecked]"); + Debug.Assert(formState[FormStateIsWindowActivated] == 0, "Failed to set formState[FormStateIsWindowActivated]"); + Debug.Assert(formState[FormStateIsTextEmpty] == 0, "Failed to set formState[FormStateIsTextEmpty]"); + Debug.Assert(formState[FormStateIsActive] == 0, "Failed to set formState[FormStateIsActive]"); + Debug.Assert(formState[FormStateIconSet] == 0, "Failed to set formState[FormStateIconSet]"); + + +#if SECURITY_DIALOG + Debug.Assert(formState[FormStateAddedSecurityMenuItem] == 0, "Failed to set formState[FormStateAddedSecurityMenuItem]"); +#endif + + // SECURITY NOTE: The IsRestrictedWindow check is done once and cached. We force it to happen here + // since we want to ensure the check is done on the code that constructs the form. + bool temp = IsRestrictedWindow; + + formStateEx[FormStateExShowIcon] = 1; + + SetState(STATE_VISIBLE, false); + SetState(STATE_TOPLEVEL, true); + +#if EVERETTROLLBACK + // VSWhidbey# 393617 (MDI: Roll back feature to Everett + QFE source base). Code left here for ref. + // VSWhidbey# 357405 Enabling this code introduces a breaking change that has was approved. + // If this needs to be enabled, also CanTabStop and TabStop code needs to be added back in Control.cs + // and Form.cs. Look at RADBU CL#963988 for ref. + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Set this value to false + // so that the window style will not include the WS_TABSTOP bit, which is + // identical to WS_MAXIMIZEBOX. Otherwise, our test suite won't be able to + // determine whether or not the window utilizes the Maximize Box in the + // window caption. + SetState(STATE_TABSTOP, false); +#endif + } + + /// + /// + /// Indicates the control on the form that is clicked when + /// the user presses the ENTER key. + /// + [ + DefaultValue(null), + SRDescription(SR.FormAcceptButtonDescr) + ] + public IButtonControl AcceptButton { + get { + return (IButtonControl)Properties.GetObject(PropAcceptButton); + } + set { + if (AcceptButton != value) { + Properties.SetObject(PropAcceptButton, value); + UpdateDefaultButton(); + + // this was removed as it breaks any accept button that isn't + // an OK, like in the case of wizards 'next' button. it was + // added as a fix to 47209...which has been reactivated. + /* + if (acceptButton != null && acceptButton.DialogResult == DialogResult.None) { + acceptButton.DialogResult = DialogResult.OK; + } + */ + } + } + } + + /// + /// Retrieves true if this form is currently active. + /// + internal bool Active { + get { + Form parentForm = ParentFormInternal; + if (parentForm == null) { + return formState[FormStateIsActive] != 0; + } + return(parentForm.ActiveControl == this && parentForm.Active); + } + + set { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Form::set_Active - " + this.Name); + if ((formState[FormStateIsActive] != 0) != value) { + if (value) { + // There is a weird user32 bug (see ASURT 124232) where it will + // activate the MDIChild even when it is not visible, when + // we set it's parent handle. Ignore this. We will get + // activated again when the MDIChild's visibility is set to true. + if (!CanRecreateHandle()){ + //Debug.Fail("Setting Active window when not yet visible"); + return; + } + } + + formState[FormStateIsActive] = value ? 1 : 0; + + if (value) { + formState[FormStateIsWindowActivated] = 1; + if (IsRestrictedWindow) { + WindowText = userWindowText; + } + // VSWhidbey#367424 Check if validation has been cancelled to avoid raising Validation event multiple times. + if (!ValidationCancelled) { + if( ActiveControl == null ) { + // Security reviewed : This internal method is called from various places, all + // of which are OK. Since SelectNextControl (a public function) + // Demands ModifyFocus, we must call the internal version. + // + SelectNextControlInternal(null, true, true, true, false); + // If no control is selected focus will go to form + } + + InnerMostActiveContainerControl.FocusActiveControlInternal(); + } + OnActivated(EventArgs.Empty); + } + else { + formState[FormStateIsWindowActivated] = 0; + if (IsRestrictedWindow) { + Text = userWindowText; + } + OnDeactivate(EventArgs.Empty); + } + } + } + } + + /// + /// + /// Gets the currently active form for this application. + /// + public static Form ActiveForm { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + IntPtr hwnd = UnsafeNativeMethods.GetForegroundWindow(); + Control c = Control.FromHandleInternal(hwnd); + if (c != null && c is Form) { + return(Form)c; + } + return null; + } + } + + /// + /// + /// + /// Gets the currently active multiple document interface (MDI) child window. + /// Note: Don't use this property internally, use ActiveMdiChildInternal instead (see comments below). + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormActiveMDIChildDescr) + ] + public Form ActiveMdiChild { + get { + Form mdiChild = ActiveMdiChildInternal; + + // Hack: We keep the active mdi child in the cached in the property store; when changing its value + // (due to a change to one of the following properties/methods: Visible, Enabled, Active, Show/Hide, + // Focus() or as the result of WM_SETFOCUS/WM_ACTIVATE/WM_MDIACTIVATE) we temporarily set it to null + // (to properly handle menu merging among other things) rendering the cache out-of-date; the problem + // arises when the user has an event handler that is raised during this process; in that case we ask + // Windows for it (see ActiveMdiChildFromWindows). + + if( mdiChild == null ){ + // If this.MdiClient != null it means this.IsMdiContainer == true. + if( this.ctlClient != null && this.ctlClient.IsHandleCreated){ + IntPtr hwnd = this.ctlClient.SendMessage(NativeMethods.WM_MDIGETACTIVE, 0, 0); + mdiChild = Control.FromHandleInternal( hwnd ) as Form; + } + } + if( mdiChild != null && mdiChild.Visible && mdiChild.Enabled ){ + return mdiChild; + } + return null; + } + } + + /// + /// Property to be used internally. See comments a on ActiveMdiChild property. + /// + internal Form ActiveMdiChildInternal{ + get{ + return (Form)Properties.GetObject(PropActiveMdiChild); + } + + set{ + Properties.SetObject(PropActiveMdiChild, value); + } + } + + //VSWhidbey 439815: we don't repaint the mdi child that used to be active any more. We used to do this in Activated, but no + //longer do because of added event Deactivate. + private Form FormerlyActiveMdiChild + { + get + { + return (Form)Properties.GetObject(PropFormerlyActiveMdiChild); + } + + set + { + Properties.SetObject(PropFormerlyActiveMdiChild, value); + } + } + + /// + /// + /// + /// + /// Gets or sets + /// a value indicating whether the opacity of the form can be + /// adjusted. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAllowTransparencyDescr) + ] + public bool AllowTransparency { + get { + return formState[FormStateAllowTransparency] != 0; + } + set { + if (value != (formState[FormStateAllowTransparency] != 0) && + OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + formState[FormStateAllowTransparency] = (value ? 1 : 0); + + formState[FormStateLayered] = formState[FormStateAllowTransparency]; + + UpdateStyles(); + + if (!value) { + if (Properties.ContainsObject(PropOpacity)) { + Properties.SetObject(PropOpacity, (object)1.0f); + } + if (Properties.ContainsObject(PropTransparencyKey)) { + Properties.SetObject(PropTransparencyKey, Color.Empty); + } + UpdateLayered(); + } + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the form will adjust its size + /// to fit the height of the font used on the form and scale + /// its controls. + /// + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.FormAutoScaleDescr), + Obsolete("This property has been deprecated. Use the AutoScaleMode property instead. http://go.microsoft.com/fwlink/?linkid=14202"), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool AutoScale { + get { + return formState[FormStateAutoScaling] != 0; + } + + set { + formStateEx[FormStateExSettingAutoScale] = 1; + try { + if (value) { + formState[FormStateAutoScaling] = 1; + + // if someone insists on auto scaling, + // force the new property back to none so they + // don't compete. + AutoScaleMode = AutoScaleMode.None; + + + } + else { + formState[FormStateAutoScaling] = 0; + } + } + finally { + formStateEx[FormStateExSettingAutoScale] = 0; + } + } + } + +// VSWhidbey 498258: Our STRONG recommendation to customers is to upgrade to AutoScaleDimensions +// however, since this is generated by default in Everett, and there's not a 1:1 mapping of +// the old to the new, we are un-obsoleting the setter for AutoScaleBaseSize only. +#pragma warning disable 618 + /// + /// + /// The base size used for autoscaling. The AutoScaleBaseSize is used + /// internally to determine how much to scale the form when AutoScaling is + /// used. + /// + // + // Virtual so subclasses like PrintPreviewDialog can prevent changes. + [ + Localizable(true), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual Size AutoScaleBaseSize { + get { + if (autoScaleBaseSize.IsEmpty) { + SizeF real = GetAutoScaleSize(Font); + return new Size((int)Math.Round(real.Width), (int)Math.Round(real.Height)); + } + return autoScaleBaseSize; + } + + set { + // Only allow the set when not in designmode, this prevents us from + // preserving an old value. The form design should prevent this for + // us by shadowing this property, so we just assert that the designer + // is doing its job. + // + Debug.Assert(!DesignMode, "Form designer should not allow base size set in design mode."); + autoScaleBaseSize = value; + } + } +#pragma warning restore 618 + + + /// + /// + /// + /// Gets or sets a value indicating whether the form implements + /// autoscrolling. + /// + /// + [ + Localizable(true) + ] + public override bool AutoScroll { + get { return base.AutoScroll;} + + set { + if (value) { + IsMdiContainer = false; + } + base.AutoScroll = value; + } + } + + // Forms implement their own AutoSize in OnLayout so we shadow this property + // just in case someone parents a Form to a container control. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize { + get { return formStateEx[FormStateExAutoSize] != 0; } + set { + if (value != AutoSize) { + formStateEx[FormStateExAutoSize] = value ? 1 : 0; + if (!AutoSize) { + minAutoSize = Size.Empty; + // If we just disabled AutoSize, restore the original size. + this.Size = CommonProperties.GetSpecifiedBounds(this).Size; + } + LayoutTransaction.DoLayout(this, this, PropertyNames.AutoSize); + OnAutoSizeChanged(EventArgs.Empty); + } + Debug.Assert(AutoSize == value, "Error detected setting Form.AutoSize."); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRDescription(SR.ControlAutoSizeModeDescr), + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true) + ] + public AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + Control toLayout = DesignMode || ParentInternal == null ? this : ParentInternal; + + if(toLayout != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(toLayout.LayoutEngine == DefaultLayout.Instance) { + toLayout.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(toLayout, this, PropertyNames.AutoSize); + } + } + } + } + + /// + /// + /// Indicates whether controls in this container will be automatically validated when the focus changes. + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public override AutoValidate AutoValidate { + get { + return base.AutoValidate; + } + set { + base.AutoValidate = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public new event EventHandler AutoValidateChanged { + add { + base.AutoValidateChanged += value; + } + remove { + base.AutoValidateChanged -= value; + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + public override Color BackColor { + get { + // Forms should not inherit BackColor from their parent, + // particularly if the parent is an MDIClient. + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) + return c; + + return DefaultBackColor; + } + + set { + base.BackColor = value; + } + } + + private bool CalledClosing { + get{ + return formStateEx[FormStateExCalledClosing] != 0; + } + set{ + formStateEx[FormStateExCalledClosing] = (value ? 1 : 0); + } + } + + private bool CalledCreateControl { + get{ + return formStateEx[FormStateExCalledCreateControl] != 0; + } + set{ + formStateEx[FormStateExCalledCreateControl] = (value ? 1 : 0); + } + } + + private bool CalledMakeVisible { + get{ + return formStateEx[FormStateExCalledMakeVisible] != 0; + } + set{ + formStateEx[FormStateExCalledMakeVisible] = (value ? 1 : 0); + } + } + + private bool CalledOnLoad { + get{ + return formStateEx[FormStateExCalledOnLoad] != 0; + } + set{ + formStateEx[FormStateExCalledOnLoad] = (value ? 1 : 0); + } + } + + + /// + /// + /// + /// Gets or sets the border style of the form. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FormBorderStyle.Sizable), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.FormBorderStyleDescr) + ] + public FormBorderStyle FormBorderStyle { + get { + return(FormBorderStyle)formState[FormStateBorderStyle]; + } + + set { + //validate FormBorderStyle enum + // + //valid values are 0x0 to 0x6 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormBorderStyle.None, (int)FormBorderStyle.SizableToolWindow)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormBorderStyle)); + } + + // In rectricted mode we don't allow windows w/o min/max/close functionality. + if (IsRestrictedWindow) { + switch (value) { + case FormBorderStyle.None: + value = FormBorderStyle.FixedSingle; + break; + case FormBorderStyle.FixedSingle: + case FormBorderStyle.Fixed3D: + case FormBorderStyle.FixedDialog: + case FormBorderStyle.Sizable: + // nothing needed here, we can just let these stay + // + break; + case FormBorderStyle.FixedToolWindow: + value = FormBorderStyle.FixedSingle; + break; + case FormBorderStyle.SizableToolWindow: + value = FormBorderStyle.Sizable; + break; + default: + value = FormBorderStyle.Sizable; + break; + } + } + + formState[FormStateBorderStyle] = (int)value; + + //(bug 112024).. It seems that the FormBorderStyle is Code serialised after the ClientSize is Set... + //Hence in the Setter for FormBodrstyle we need to push in the Correct ClientSize once again.. + if (formState[FormStateSetClientSize] == 1 && !IsHandleCreated) { + ClientSize = ClientSize; + } + + // VSWhidbey 95327: Since setting the border style induces a call to SetBoundsCore, which, + // when the WindowState is not Normal, will cause the values stored in the field restoredWindowBounds + // to be replaced. The side-effect of this, of course, is that when the form window is restored, + // the Form's size is restored to the size it was when in a non-Normal state. + // So, we need to cache these values now before the call to UpdateFormStyles() to prevent + // these existing values from being lost. Then, if the WindowState is something other than + // FormWindowState.Normal after the call to UpdateFormStyles(), restore these cached values to + // the restoredWindowBounds field. + Rectangle preClientUpdateRestoredWindowBounds = restoredWindowBounds; + BoundsSpecified preClientUpdateRestoredWindowBoundsSpecified = restoredWindowBoundsSpecified; + int preWindowBoundsWidthIsClientSize = formStateEx[FormStateExWindowBoundsWidthIsClientSize]; + int preWindowBoundsHeightIsClientSize = formStateEx[FormStateExWindowBoundsHeightIsClientSize]; + + UpdateFormStyles(); + + // In Windows XP Theme, the FixedDialog tend to have a small Icon. + // So to make this behave uniformly with other styles, we need to make + // the call to UpdateIcon after the the form styles have been updated. + if (formState[FormStateIconSet] == 0 && !IsRestrictedWindow) { + UpdateWindowIcon(false); + } + + // VSWhidbey 95327: Now restore the values cached above. + if (WindowState != FormWindowState.Normal) { + restoredWindowBounds = preClientUpdateRestoredWindowBounds; + restoredWindowBoundsSpecified = preClientUpdateRestoredWindowBoundsSpecified; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = preWindowBoundsWidthIsClientSize; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = preWindowBoundsHeightIsClientSize; + } + } + } + + /// + /// + /// Gets + /// or + /// sets the button control that will be clicked when the + /// user presses the ESC key. + /// + [ + DefaultValue(null), + SRDescription(SR.FormCancelButtonDescr) + ] + public IButtonControl CancelButton { + get { + return (IButtonControl)Properties.GetObject(PropCancelButton); + } + set { + Properties.SetObject(PropCancelButton, value); + + if (value != null && value.DialogResult == DialogResult.None) { + value.DialogResult = DialogResult.Cancel; + } + } + } + + /// + /// + /// + /// Gets or sets the size of the client area of the form. + /// + /// + [ + Localizable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + new public Size ClientSize { + get { + return base.ClientSize; + } + set { + base.ClientSize = value; + } + } + + /// + /// + /// Gets or sets a value indicating whether a control box is displayed in the + /// caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormControlBoxDescr) + ] + public bool ControlBox { + get { + return formState[FormStateControlBox] != 0; + } + + set { + // Window style in restricted mode must always have a control box. + if (IsRestrictedWindow) { + return; + } + + if (value) { + formState[FormStateControlBox] = 1; + } + else { + formState[FormStateControlBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Retrieves the CreateParams used to create the window. + /// If a subclass overrides this function, it must call the base implementation. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + if (this.IsHandleCreated && (this.WindowStyle & NativeMethods.WS_DISABLED) != 0) + { + // Forms that are parent of a modal dialog must keep their WS_DISABLED style (VSWhidbey 449309) + cp.Style |= NativeMethods.WS_DISABLED; + } + else if (TopLevel) + { + // It doesn't seem to make sense to allow a top-level form to be disabled + // + cp.Style &= (~NativeMethods.WS_DISABLED); + } + + if (TopLevel && (formState[FormStateLayered] != 0)) { + cp.ExStyle |= NativeMethods.WS_EX_LAYERED; + } + + IWin32Window dialogOwner = (IWin32Window)Properties.GetObject(PropDialogOwner); + if (dialogOwner != null) { + cp.Parent = Control.GetSafeHandle(dialogOwner); + } + + FillInCreateParamsBorderStyles(cp); + FillInCreateParamsWindowState(cp); + FillInCreateParamsBorderIcons(cp); + + if (formState[FormStateTaskBar] != 0) { + cp.ExStyle |= NativeMethods.WS_EX_APPWINDOW; + } + + FormBorderStyle borderStyle = FormBorderStyle; + if (!ShowIcon && + (borderStyle == FormBorderStyle.Sizable || + borderStyle == FormBorderStyle.Fixed3D || + borderStyle == FormBorderStyle.FixedSingle)) + { + cp.ExStyle |= NativeMethods.WS_EX_DLGMODALFRAME; + } + + if (IsMdiChild) { + if (Visible + && (WindowState == FormWindowState.Maximized + || WindowState == FormWindowState.Normal)) { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + Form form = formMdiParent.ActiveMdiChildInternal; + + if (form != null + && form.WindowState == FormWindowState.Maximized) { + cp.Style |= NativeMethods.WS_MAXIMIZE; + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + SetState(STATE_SIZELOCKEDBYOS, true); + } + } + + if (formState[FormStateMdiChildMax] != 0) { + cp.Style |= NativeMethods.WS_MAXIMIZE; + } + cp.ExStyle |= NativeMethods.WS_EX_MDICHILD; + } + + if (TopLevel || IsMdiChild) { + FillInCreateParamsStartPosition(cp); + // Delay setting to visible until after the handle gets created + // to allow applyClientSize to adjust the size before displaying + // the form. + // + if ((cp.Style & NativeMethods.WS_VISIBLE) != 0) { + formState[FormStateShowWindowOnCreate] = 1; + cp.Style &= (~NativeMethods.WS_VISIBLE); + } + else { + formState[FormStateShowWindowOnCreate] = 0; + } + } + + if (IsRestrictedWindow) { + cp.Caption = RestrictedWindowText(cp.Caption); + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + internal CloseReason CloseReason { + get { return closeReason; } + set { closeReason = value; } + } + /// + /// The default icon used by the Form. This is the standard "windows forms" icon. + /// + internal static Icon DefaultIcon { + get { + // Avoid locking if the value is filled in... + // + if (defaultIcon == null) { + lock(internalSyncObject) { + // Once we grab the lock, we re-check the value to avoid a + // race condition. + // + if (defaultIcon == null) { + defaultIcon = new Icon(typeof(Form), "wfc.ico"); + } + } + } + return defaultIcon; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.NoControl; + } + } + + /// + /// The default icon used by the Form. This is the standard "windows forms" icon. + /// + private static Icon DefaultRestrictedIcon { + get { + // Note: We do this as a static property to allow delay + // loading of the resource. There are some issues with doing + // an OleInitialize from a static constructor... + // + + // Avoid locking if the value is filled in... + // + if (defaultRestrictedIcon == null) { + lock (internalSyncObject) + { + // Once we grab the lock, we re-check the value to avoid a + // race condition. + // + if (defaultRestrictedIcon == null) { + defaultRestrictedIcon = new Icon(typeof(Form), "wfsecurity.ico"); + } + } + } + return defaultRestrictedIcon; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(300, 300); + } + } + + /// + /// + /// Gets or sets the size and location of the form on the Windows desktop. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDesktopBoundsDescr) + ] + public Rectangle DesktopBounds { + get { + Rectangle screen = SystemInformation.WorkingArea; + Rectangle bounds = Bounds; + bounds.X -= screen.X; + bounds.Y -= screen.Y; + return bounds; + } + + set { + SetDesktopBounds(value.X, value.Y, value.Width, value.Height); + } + } + + /// + /// + /// Gets or sets the location of the form on the Windows desktop. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDesktopLocationDescr) + ] + public Point DesktopLocation { + get { + Rectangle screen = SystemInformation.WorkingArea; + Point loc = Location; + loc.X -= screen.X; + loc.Y -= screen.Y; + return loc; + } + + set { + SetDesktopLocation(value.X, value.Y); + } + } + + /// + /// + /// Gets or sets the dialog result for the form. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDialogResultDescr) + ] + public DialogResult DialogResult { + get { + return dialogResult; + } + + set { + //valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DialogResult.None, (int)DialogResult.No)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DialogResult)); + } + + dialogResult = value; + } + } + + internal override bool HasMenu { + get { + bool hasMenu = false; + + // VSWhidbey 94975: Verify that the menu actually contains items so that any + // size calculations will only include a menu height if the menu actually exists. + // Note that Windows will not draw a menu bar for a menu that does not contain + // any items. + Menu menu = Menu; + if (TopLevel && menu != null && menu.ItemCount > 0) { + hasMenu = true; + } + return hasMenu; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether a + /// help button should be displayed in the caption box of the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(false), + SRDescription(SR.FormHelpButtonDescr) + ] + public bool HelpButton { + get { + return formState[FormStateHelpButton] != 0; + } + + set { + if (value) { + formState[FormStateHelpButton] = 1; + } + else { + formState[FormStateHelpButton] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// [To be supplied.] + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + SRCategory(SR.CatBehavior), + SRDescription(SR.FormHelpButtonClickedDescr) + ] + public event CancelEventHandler HelpButtonClicked { + add { + Events.AddHandler(EVENT_HELPBUTTONCLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_HELPBUTTONCLICKED, value); + } + } + + /// + /// + /// + /// Gets or sets the icon for the form. + /// + /// + [ + AmbientValue(null), + Localizable(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormIconDescr) + ] + public Icon Icon { + get { + if (formState[FormStateIconSet] == 0) { + // In restricted mode, the security icon cannot be changed. + if (IsRestrictedWindow) { + return DefaultRestrictedIcon; + } + else { + return DefaultIcon; + } + } + + return icon; + } + + set { + if (icon != value && !IsRestrictedWindow) { + + // If the user is poking the default back in, + // treat this as a null (reset). + // + if (value == defaultIcon) { + value = null; + } + + // And if null is passed, reset the icon. + // + formState[FormStateIconSet] = (value == null ? 0 : 1); + this.icon = value; + + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + + UpdateWindowIcon(true); + } + } + } + + /// + /// Determines whether the window is closing. + /// + private bool IsClosing { + get { + return formStateEx[FormStateExWindowClosing] == 1; + } + set { + formStateEx[FormStateExWindowClosing] = value ? 1 : 0; + } + } + + // VSWhidbey 517376: returns a more accurate statement of whether or not the form is maximized. + // during handle creation, an MDIChild is created as not maximized. + private bool IsMaximized { + get { + return (WindowState == FormWindowState.Maximized || (IsMdiChild && (formState[FormStateMdiChildMax] ==1))); + } + } + + /// + /// + /// + /// Gets a value indicating whether the form is a multiple document + /// interface (MDI) child form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormIsMDIChildDescr) + ] + public bool IsMdiChild { + get { + return (Properties.GetObject(PropFormMdiParent) != null); + } + } + + // Created for fix VSWhidbey 182131. Deactivates active MDI child and temporarily marks it as unfocusable, + // so that WM_SETFOCUS sent to MDIClient does not activate that child. (See MdiClient.WndProc). + internal bool IsMdiChildFocusable { + get { + if (this.Properties.ContainsObject(PropMdiChildFocusable)) { + return (bool) this.Properties.GetObject(PropMdiChildFocusable); + } + return false; + } + set { + if (value != this.IsMdiChildFocusable) { + this.Properties.SetObject(PropMdiChildFocusable, value); + } + } + } + + + /// + /// + /// + /// Gets or sets a value indicating whether the form is a container for multiple document interface + /// (MDI) child forms. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(false), + SRDescription(SR.FormIsMDIContainerDescr) + ] + public bool IsMdiContainer { + get { + return ctlClient != null; + } + + set { + if (value == IsMdiContainer) + return; + + if (value) { + Debug.Assert(ctlClient == null, "why isn't ctlClient null"); + AllowTransparency = false; + Controls.Add(new MdiClient()); + } + else { + Debug.Assert(ctlClient != null, "why is ctlClient null"); + ActiveMdiChildInternal = null; + ctlClient.Dispose(); + } + //since we paint the background when mdi is true, we need + //to invalidate here + // + Invalidate(); + } + } + + /// + /// + /// + /// Determines if this form should display a warning banner + /// when the form is displayed in an unsecure mode. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsRestrictedWindow { + get { + /// SECREVIEW: make sure to keep changes here in sync with ToolStripDropDown.IsRestrictedWindow + if (formState[FormStateIsRestrictedWindowChecked] == 0) { + formState[FormStateIsRestrictedWindow] = 0; + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Checking for restricted window..."); + Debug.Indent(); +#if DEBUG + if (AlwaysRestrictWindows.Enabled) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Always restricted switch is on..."); + formState[FormStateIsRestrictedWindow] = 1; + formState[FormStateIsRestrictedWindowChecked] = 1; + Debug.Unindent(); + return true; + } +#endif + + try { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "WindowAdornmentModification Demanded"); + IntSecurity.WindowAdornmentModification.Demand(); + } + catch (SecurityException) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Caught security exception, we are restricted..."); + formState[FormStateIsRestrictedWindow] = 1; + } + catch { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Caught non-security exception..."); + Debug.Unindent(); + formState[FormStateIsRestrictedWindow] = 1; // To be on the safe side + formState[FormStateIsRestrictedWindowChecked] = 1; + throw; + } + Debug.Unindent(); + formState[FormStateIsRestrictedWindowChecked] = 1; + } + + return formState[FormStateIsRestrictedWindow] != 0; + } + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the form will receive key events + /// before the event is passed to the control that has focus. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.FormKeyPreviewDescr) + ] + public bool KeyPreview { + get { + return formState[FormStateKeyPreview] != 0; + } + set { + if (value) { + formState[FormStateKeyPreview] = 1; + } + else { + formState[FormStateKeyPreview] = 0; + } + } + } + + /// + /// + /// + /// Gets or sets the location of the form. + /// + /// + [SettingsBindable(true)] + public new Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + + /// + /// + /// + /// Gets the size of the form when it is + /// maximized. + /// + /// + protected Rectangle MaximizedBounds { + get { + return Properties.GetRectangle(PropMaximizedBounds); + } + set { + if (!value.Equals( MaximizedBounds )) { + Properties.SetRectangle(PropMaximizedBounds, value); + OnMaximizedBoundsChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_MAXIMIZEDBOUNDSCHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMaximizedBoundsChangedDescr)] + public event EventHandler MaximizedBoundsChanged { + add { + Events.AddHandler(EVENT_MAXIMIZEDBOUNDSCHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MAXIMIZEDBOUNDSCHANGED, value); + } + } + + /// + /// + /// + /// Gets the maximum size the form can be resized to. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormMaximumSizeDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(typeof(Size), "0, 0") + ] + public override Size MaximumSize { + get { + if (Properties.ContainsInteger(PropMaxTrackSizeWidth)) { + return new Size(Properties.GetInteger(PropMaxTrackSizeWidth), Properties.GetInteger(PropMaxTrackSizeHeight)); + } + return Size.Empty; + } + set { + if (!value.Equals( MaximumSize )) { + + if (value.Width < 0 || value.Height < 0 ) { + throw new ArgumentOutOfRangeException("MaximumSize"); + } + + Properties.SetInteger(PropMaxTrackSizeWidth, value.Width); + Properties.SetInteger(PropMaxTrackSizeHeight, value.Height); + + // Bump minimum size if necessary + // + if (!MinimumSize.IsEmpty && !value.IsEmpty) { + + if (Properties.GetInteger(PropMinTrackSizeWidth) > value.Width) { + Properties.SetInteger(PropMinTrackSizeWidth, value.Width); + } + + if (Properties.GetInteger(PropMinTrackSizeHeight) > value.Height) { + Properties.SetInteger(PropMinTrackSizeHeight, value.Height); + } + } + + // Keep form size within new limits + // + Size size = Size; + if (!value.IsEmpty && (size.Width > value.Width || size.Height > value.Height)) { + Size = new Size(Math.Min(size.Width, value.Width), Math.Min(size.Height, value.Height)); + } + + OnMaximumSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMaximumSizeChangedDescr)] + public event EventHandler MaximumSizeChanged { + add { + Events.AddHandler(EVENT_MAXIMUMSIZECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MAXIMUMSIZECHANGED, value); + } + } + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(null), + SRDescription(SR.FormMenuStripDescr), + TypeConverter(typeof(ReferenceConverter)) + ] + public MenuStrip MainMenuStrip { + get { + return (MenuStrip)Properties.GetObject(PropMainMenuStrip); + } + set { + Properties.SetObject(PropMainMenuStrip, value); + if (IsHandleCreated && Menu == null) { + UpdateMenuHandles(); + } + } + } + + /// + /// + /// Hide Margin/MarginChanged + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new Padding Margin { + get { return base.Margin; } + set { + base.Margin = value; + } + } + + + /// + /// + /// Hide Margin/MarginChanged + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MarginChanged { + add { + base.MarginChanged += value; + } + remove { + base.MarginChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the + /// that is displayed in the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(null), + SRDescription(SR.FormMenuDescr), + TypeConverter(typeof(ReferenceConverter)), + Browsable(false), + ] + public MainMenu Menu { + get { + return (MainMenu)Properties.GetObject(PropMainMenu); + } + set { + MainMenu mainMenu = Menu; + + if (mainMenu != value) { + if (mainMenu != null) { + mainMenu.form = null; + } + + Properties.SetObject(PropMainMenu, value); + + if (value != null) { + if (value.form != null) { + value.form.Menu = null; + } + value.form = this; + } + + if (formState[FormStateSetClientSize] == 1 && !IsHandleCreated) { + ClientSize = ClientSize; + } + + + MenuChanged(Windows.Forms.Menu.CHANGE_ITEMS, value); + } + } + } + + /// + /// + /// + /// Gets the minimum size the form can be resized to. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormMinimumSizeDescr), + RefreshProperties(RefreshProperties.Repaint), + ] + public override Size MinimumSize { + get { + if (Properties.ContainsInteger(PropMinTrackSizeWidth)) { + return new Size(Properties.GetInteger(PropMinTrackSizeWidth), Properties.GetInteger(PropMinTrackSizeHeight)); + } + return DefaultMinimumSize; + } + set { + if (!value.Equals( MinimumSize )) { + + if (value.Width < 0 || value.Height < 0 ) { + throw new ArgumentOutOfRangeException("MinimumSize"); + } + + // ensure that the size we've applied fits into the screen + // when IsRestrictedWindow. + Rectangle bounds = this.Bounds; + bounds.Size = value; + value = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(bounds).Size; + + Properties.SetInteger(PropMinTrackSizeWidth, value.Width); + Properties.SetInteger(PropMinTrackSizeHeight, value.Height); + + // Bump maximum size if necessary + // + if (!MaximumSize.IsEmpty && !value.IsEmpty) { + + if (Properties.GetInteger(PropMaxTrackSizeWidth) < value.Width) { + Properties.SetInteger(PropMaxTrackSizeWidth, value.Width); + } + + if (Properties.GetInteger(PropMaxTrackSizeHeight) < value.Height) { + Properties.SetInteger(PropMaxTrackSizeHeight, value.Height); + } + } + + // Keep form size within new limits + // + Size size = Size; + if (size.Width < value.Width || size.Height < value.Height) { + Size = new Size(Math.Max(size.Width, value.Width), Math.Max(size.Height, value.Height)); + } + + if (IsHandleCreated) { + // VSWhidbey 95302: "Move" the form to the same size and position to prevent windows from moving it + // when the user tries to grab a resizing border. + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.NullHandleRef, + Location.X, + Location.Y, + Size.Width, + Size.Height, + NativeMethods.SWP_NOZORDER); + } + + OnMinimumSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMinimumSizeChangedDescr)] + public event EventHandler MinimumSizeChanged { + add { + Events.AddHandler(EVENT_MINIMUMSIZECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MINIMUMSIZECHANGED, value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the maximize button is + /// displayed in the caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormMaximizeBoxDescr) + ] + public bool MaximizeBox { + get { + return formState[FormStateMaximizeBox] != 0; + } + set { + if (value) { + formState[FormStateMaximizeBox] = 1; + } + else { + formState[FormStateMaximizeBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Gets an array of forms that represent the + /// multiple document interface (MDI) child forms that are parented to this + /// form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMDIChildrenDescr) + ] + public Form[] MdiChildren { + get { + if (ctlClient != null) { + return ctlClient.MdiChildren; + } + else { + return new Form[0]; + } + } + } + + /// + /// + /// Gets the MDIClient that the MDI container form is using to contain Multiple Document Interface (MDI) child forms, + /// if this is an MDI container form. + /// Represents the client area of a Multiple Document Interface (MDI) Form window, also known as the MDI child window. + /// + /// + internal MdiClient MdiClient { + get { + return this.ctlClient; + } + } + + /// + /// + /// Indicates the current multiple document + /// interface (MDI) parent form of this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMDIParentDescr) + ] + public Form MdiParent { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + return MdiParentInternal; + } + set { + MdiParentInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + private Form MdiParentInternal { + get { + return (Form)Properties.GetObject(PropFormMdiParent); + } + set { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (value == formMdiParent && (value != null || ParentInternal == null)) { + return; + } + + if (value != null && this.CreateThreadId != value.CreateThreadId) { + throw new ArgumentException(SR.GetString(SR.AddDifferentThreads), "value"); + } + + bool oldVisibleBit = GetState(STATE_VISIBLE); + //bug(108303) .. this should apply whether or not value == null. + Visible = false; + + try { + if (value == null) { + ParentInternal = null; + // VSWhidbey 429924 - not calling SetTopLevelInternal so that IntSecurity.TopLevelWindow.Demand() isn't skipped. + SetTopLevel(true); + } + else { + if (IsMdiContainer) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentAndChild), "value"); + } + if (!value.IsMdiContainer) { + throw new ArgumentException(SR.GetString(SR.MDIParentNotContainer), "value"); + } + + // Setting TopLevel forces a handle recreate before Parent is set, + // which causes problems because we try to assign an MDI child to the parking window, + // which can't take MDI children. So we explicitly destroy and create the handle here. + + Dock = DockStyle.None; + Properties.SetObject(PropFormMdiParent, value); + + SetState(STATE_TOPLEVEL, false); + ParentInternal = value.MdiClient; + + // If it is an MDIChild, and it is not yet visible, we'll + // hold off on recreating the window handle. We'll do that + // when MdiChild's visibility is set to true (see bug 124232). + + // But if the handle has already been created, we need to destroy it + // so the form gets MDI-parented properly. See bug#382992. + if (ParentInternal.IsHandleCreated && IsMdiChild && IsHandleCreated) { + DestroyHandle(); + } + } + InvalidateMergedMenu(); + UpdateMenuHandles(); + } + finally { + UpdateStyles(); + Visible = oldVisibleBit; + } + } + } + + private MdiWindowListStrip MdiWindowListStrip { + get { return Properties.GetObject(PropMdiWindowListStrip) as MdiWindowListStrip; } + set { Properties.SetObject(PropMdiWindowListStrip, value); } + } + + private MdiControlStrip MdiControlStrip { + get { return Properties.GetObject(PropMdiControlStrip) as MdiControlStrip; } + set { Properties.SetObject(PropMdiControlStrip, value); } + } + + /// + /// + /// + /// Gets the merged menu for the + /// form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMergedMenuDescr), + ] + public MainMenu MergedMenu { + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + get { + return this.MergedMenuPrivate; + } + } + + private MainMenu MergedMenuPrivate { + get { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent == null) return null; + + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + if (mergedMenu != null) return mergedMenu; + + MainMenu parentMenu = formMdiParent.Menu; + MainMenu mainMenu = Menu; + + if (mainMenu == null) return parentMenu; + if (parentMenu == null) return mainMenu; + + // Create a menu that merges the two and save it for next time. + mergedMenu = new MainMenu(); + mergedMenu.ownerForm = this; + mergedMenu.MergeMenu(parentMenu); + mergedMenu.MergeMenu(mainMenu); + Properties.SetObject(PropMergedMenu, mergedMenu); + return mergedMenu; + } + } + + /// + /// + /// Gets or sets a value indicating whether the minimize button is displayed in the caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormMinimizeBoxDescr) + ] + public bool MinimizeBox { + get { + return formState[FormStateMinimizeBox] != 0; + } + set { + if (value) { + formState[FormStateMinimizeBox] = 1; + } + else { + formState[FormStateMinimizeBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Gets a value indicating whether this form is + /// displayed modally. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormModalDescr) + ] + public bool Modal { + get { + return GetState(STATE_MODAL); + } + } + + /// + /// + /// Determines the opacity of the form. This can only be set on top level + /// controls. Opacity requires Windows 2000 or later, and is ignored on earlier + /// operating systems. + /// + [ + SRCategory(SR.CatWindowStyle), + TypeConverterAttribute(typeof(OpacityConverter)), + SRDescription(SR.FormOpacityDescr), + DefaultValue(1.0) + ] + public double Opacity { + get { + object opacity = Properties.GetObject(PropOpacity); + if (opacity != null) { + return Convert.ToDouble(opacity, CultureInfo.InvariantCulture); + } + else { + return 1.0f; + } + } + set { + // In restricted mode a form cannot be made less visible than 50% opacity. + if (IsRestrictedWindow) { + value = Math.Max(value, .50f); + } + + if (value > 1.0) { + value = 1.0f; + } + else if (value < 0.0) { + value = 0.0f; + } + + Properties.SetObject(PropOpacity, value); + + bool oldLayered = (formState[FormStateLayered] != 0); + + if (OpacityAsByte < 255 && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) + { + AllowTransparency = true; + if (formState[FormStateLayered] != 1) { + formState[FormStateLayered] = 1; + if (!oldLayered) { + UpdateStyles(); + } + } + } + else { + formState[FormStateLayered] = (this.TransparencyKey != Color.Empty) ? 1 : 0; + if (oldLayered != (formState[FormStateLayered] != 0)) { + int exStyle = unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE)); + CreateParams cp = CreateParams; + if (exStyle != cp.ExStyle) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE, new HandleRef(null, (IntPtr)cp.ExStyle)); + } + } + } + + UpdateLayered(); + } + } + + private byte OpacityAsByte { + get { + return (byte)(Opacity * 255.0f); + } + } + + /// + /// + /// Gets an array of objects that represent all forms that are owned by this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormOwnedFormsDescr) + ] + public Form[] OwnedForms { + get { + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + Form[] result = new Form[ownedFormsCount]; + if (ownedFormsCount > 0) { + Array.Copy(ownedForms, 0, result, 0, ownedFormsCount); + } + + return result; + } + } + + /// + /// + /// Gets or sets the form that owns this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormOwnerDescr) + ] + public Form Owner { + get { + IntSecurity.GetParent.Demand(); + return OwnerInternal; + } + set { + Form ownerOld = OwnerInternal; + if (ownerOld == value) + return; + + if (value != null && !TopLevel) { + throw new ArgumentException(SR.GetString(SR.NonTopLevelCantHaveOwner), "value"); + } + + CheckParentingCycle(this, value); + CheckParentingCycle(value, this); + + Properties.SetObject(PropOwner, null); + + if (ownerOld != null) { + ownerOld.RemoveOwnedForm(this); + } + + Properties.SetObject(PropOwner, value); + + if (value != null) { + value.AddOwnedForm(this); + } + + UpdateHandleWithOwner(); + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Form OwnerInternal { + get { + return (Form)Properties.GetObject(PropOwner); + } + } + + /// + /// + /// Gets or sets the restored bounds of the Form. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public Rectangle RestoreBounds { + get { + if (restoreBounds.Width == -1 + && restoreBounds.Height == -1 + && restoreBounds.X == -1 + && restoreBounds.Y == -1) { + // VSWhidbey 470435: Form scaling depends on this property being + // set correctly. In some cases (where the size has not yet been set or + // has only been set to the default, restoreBounds will remain uninitialized until the + // handle has been created. In this case, return the current Bounds. + return Bounds; + } + return restoreBounds; + } + } + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal override Control ParentInternal { + get { + return base.ParentInternal; + } + set { + if (value != null) { + Owner = null; + } + base.ParentInternal = value; + } + } + + /// + /// + /// If ShowInTaskbar is true then the form will be displayed + /// in the Windows Taskbar. + /// + [ + DefaultValue(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormShowInTaskbarDescr) + ] + public bool ShowInTaskbar { + get { + return formState[FormStateTaskBar] != 0; + } + set { + // Restricted windows must always show in task bar. + if (IsRestrictedWindow) { + return; + } + + if (ShowInTaskbar != value) { + if (value) { + formState[FormStateTaskBar] = 1; + } + else { + formState[FormStateTaskBar] = 0; + } + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// Gets or sets a value indicating whether an icon is displayed in the + /// caption bar of the form. + /// If ControlBox == false, then the icon won't be shown no matter what + /// the value of ShowIcon is + /// + [ + DefaultValue(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormShowIconDescr) + ] + public bool ShowIcon { + get { + return formStateEx[FormStateExShowIcon] != 0; + } + + set { + if (value) { + formStateEx[FormStateExShowIcon] = 1; + } + else { + // The icon must always be shown for restricted forms. + if (IsRestrictedWindow) { + return; + } + formStateEx[FormStateExShowIcon] = 0; + UpdateStyles(); + } + UpdateWindowIcon(true); + } + } + + internal override int ShowParams { + get { + // From MSDN: + // The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter. + + // As noted in the discussion of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the STARTUPINFO structure. In this case, ShowWindow uses the information specified in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup information provided by the program that launched the application. This behavior is designed for the following situations: + // + // Applications create their main window by calling CreateWindow with the WS_VISIBLE flag set. + // Applications create their main window by calling CreateWindow with the WS_VISIBLE flag cleared, and later call ShowWindow with the SW_SHOW flag set to make it visible. + // + + switch(WindowState) { + case FormWindowState.Maximized: + return NativeMethods.SW_SHOWMAXIMIZED; + case FormWindowState.Minimized: + return NativeMethods.SW_SHOWMINIMIZED; + } + if (ShowWithoutActivation) + { + return NativeMethods.SW_SHOWNOACTIVATE; + } + return NativeMethods.SW_SHOW; + } + } + + /// + /// + /// When this property returns true, the internal ShowParams property will return NativeMethods.SW_SHOWNOACTIVATE. + /// + /// + [Browsable(false)] + protected virtual bool ShowWithoutActivation + { + get + { + return false; + } + } + + /// + /// + /// + /// Gets or sets the size of the form. + /// + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Localizable(false) + ] + new public Size Size { + get { + return base.Size; + } + set { + base.Size = value; + } + } + + /// + /// + /// + /// Gets or sets the style of size grip to display in the lower-left corner of the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(SizeGripStyle.Auto), + SRDescription(SR.FormSizeGripStyleDescr) + ] + public SizeGripStyle SizeGripStyle { + get { + return(SizeGripStyle)formState[FormStateSizeGripStyle]; + } + set { + if (SizeGripStyle != value) { + //do some bounds checking here + // + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SizeGripStyle.Auto, (int)SizeGripStyle.Hide)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SizeGripStyle)); + } + + formState[FormStateSizeGripStyle] = (int)value; + UpdateRenderSizeGrip(); + } + } + } + + /// + /// + /// + /// Gets or sets the + /// starting position of the form at run time. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatLayout), + DefaultValue(FormStartPosition.WindowsDefaultLocation), + SRDescription(SR.FormStartPositionDescr) + ] + public FormStartPosition StartPosition { + get { + return(FormStartPosition)formState[FormStateStartPos]; + } + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormStartPosition.Manual, (int)FormStartPosition.CenterParent)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormStartPosition)); + } + formState[FormStateStartPos] = (int)value; + } + } + + /// + /// + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + new public int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + /// + /// This property has no effect on Form, we need to hide it from browsers. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// For forms that are show in task bar false, this returns a HWND + /// they must be parented to in order for it to work. + /// + private HandleRef TaskbarOwner { + get { + if (ownerWindow == null) { + ownerWindow = new NativeWindow(); + } + + if (ownerWindow.Handle == IntPtr.Zero) { + CreateParams cp = new CreateParams(); + cp.ExStyle = NativeMethods.WS_EX_TOOLWINDOW; + ownerWindow.CreateHandle(cp); + } + + return new HandleRef(ownerWindow, ownerWindow.Handle); + } + } + + [SettingsBindable(true)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether to display the form as a top-level + /// window. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool TopLevel { + get { + return GetTopLevel(); + } + set { + if (!value && ((Form)this).IsMdiContainer && !DesignMode) { + throw new ArgumentException(SR.GetString(SR.MDIContainerMustBeTopLevel), "value"); + } + SetTopLevel(value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the form should be displayed as the top-most + /// form of your application. + /// + [ + DefaultValue(false), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormTopMostDescr) + ] + public bool TopMost { + get { + return formState[FormStateTopMost] != 0; + } + set { + // Restricted windows cannot be top most to avoid DOS attack by obscuring other windows. + if (IsRestrictedWindow) { + return; + } + + if (IsHandleCreated && TopLevel) { + HandleRef key = value ? NativeMethods.HWND_TOPMOST : NativeMethods.HWND_NOTOPMOST; + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), key, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + + if (value) { + formState[FormStateTopMost] = 1; + } + else { + formState[FormStateTopMost] = 0; + } + } + } + + /// + /// + /// Gets or sets the color that will represent transparent areas of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormTransparencyKeyDescr) + ] + public Color TransparencyKey { + get { + object key = Properties.GetObject(PropTransparencyKey); + if (key != null) { + return (Color)key; + } + return Color.Empty; + } + set { + Properties.SetObject(PropTransparencyKey, value); + if (!IsMdiContainer) { + bool oldLayered = (formState[FormStateLayered] == 1); + if (value != Color.Empty) + { + IntSecurity.TransparentWindows.Demand(); + AllowTransparency = true; + formState[FormStateLayered] = 1; + } + else + { + formState[FormStateLayered] = (this.OpacityAsByte < 255) ? 1 : 0; + } + if (oldLayered != (formState[FormStateLayered] != 0)) + { + UpdateStyles(); + } + UpdateLayered(); + } + } + } + + /// + // + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetVisibleCore(bool value) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Form::SetVisibleCore(" + value.ToString() + ") - " + this.Name); + + // If DialogResult.OK and the value == GetVisibleCore() then this code has been called either through + // ShowDialog( ) or explicit Hide( ) by the user. So dont go through this function again. + // This will avoid flashing during closing the dialog; refer to VsWhidbey : 456449. + if (GetVisibleCore() == value && dialogResult == DialogResult.OK) + { + return; + } + + // (!value || calledMakeVisible) is to make sure that we fall + // through and execute the code below atleast once. + if (GetVisibleCore() == value && (!value || CalledMakeVisible)) { + base.SetVisibleCore(value); + return; + } + + if (value) { + CalledMakeVisible = true; + if (CalledCreateControl) { + if (CalledOnLoad) { + // VSWhidbey 518085: make sure the form is in the Application.OpenForms collection + if (!Application.OpenFormsInternal.Contains(this)) { + Application.OpenFormsInternalAdd(this); + } + } + else { + CalledOnLoad = true; + OnLoad(EventArgs.Empty); + if (dialogResult != DialogResult.None) { + // Don't show the dialog if the dialog result was set + // in the OnLoad event. + // + value = false; + } + } + } + } + else { + ResetSecurityTip(true /* modalOnly */); + } + + if (!IsMdiChild) { + base.SetVisibleCore(value); + + // We need to force this call if we were created + // with a STARTUPINFO structure (e.g. launched from explorer), since + // it won't send a WM_SHOWWINDOW the first time it's called. + // when WM_SHOWWINDOW gets called, we'll flip this bit to true + // + if (0==formState[FormStateSWCalled]) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_SHOWWINDOW, value ? 1 : 0, 0); + } + } + else { + // Throw away any existing handle. + if (IsHandleCreated) { + // VSWhidbey 430476 - Everett/RTM used to wrap this in an assert for AWP. + DestroyHandle(); + } + + if (!value) { + InvalidateMergedMenu(); + SetState(STATE_VISIBLE, false); + } + else { + + // The ordering is important here... Force handle creation + // (getHandle) then show the window (ShowWindow) then finish + // creating children using createControl... + // + SetState(STATE_VISIBLE, true); + + // VSWhidbey 93522: Ask the mdiClient to re-layout the controls so that any docking or + // anchor settings for this mdi child window will be honored. + MdiParentInternal.MdiClient.PerformLayout(); + + if (ParentInternal != null && ParentInternal.Visible) { + SuspendLayout(); + try{ + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), NativeMethods.SW_SHOW); + CreateControl(); + + // If this form is mdichild and maximized, we need to redraw the MdiParent non-client area to + // update the menu bar because we always create the window as if it were not maximized. + // See comment on CreateHandle about this. VSWhidbey#93518 + if (WindowState == FormWindowState.Maximized) { + MdiParentInternal.UpdateWindowIcon(true); + } + } + finally{ + ResumeLayout(); + } + } + } + OnVisibleChanged(EventArgs.Empty); + } + + //(bug 111549)... For FormWindowState.Maximized.. Wm_activate is not Fired before setting Focus + //on Active Control.. + + if (value && !IsMdiChild && (WindowState == FormWindowState.Maximized || TopMost)) { + if (ActiveControl == null){ + SelectNextControlInternal(null, true, true, true, false); + } + FocusActiveControlInternal(); + } + } + + /// + /// + /// Gets or sets the form's window state. + /// + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(FormWindowState.Normal), + SRDescription(SR.FormWindowStateDescr) + ] + public FormWindowState WindowState { + get { + return(FormWindowState)formState[FormStateWindowState]; + } + set { + + //verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormWindowState.Normal, (int)FormWindowState.Maximized)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormWindowState)); + } + + if (TopLevel && IsRestrictedWindow) { + // We don't allow to minimize or maximze a top level window programatically if it is restricted. + // When maximized, the desktop is obscured by the window (DOS attack) and when minimize spoofing + // identity is the thread, the minimized window could steal the user's keystrokes and obtain a + // password for instance. + if (value != FormWindowState.Normal) { + return; + } + } + + switch (value) { + case FormWindowState.Normal: + SetState(STATE_SIZELOCKEDBYOS, false); + break; + case FormWindowState.Maximized: + case FormWindowState.Minimized: + SetState(STATE_SIZELOCKEDBYOS, true); + break; + } + + if (IsHandleCreated && Visible) { + IntPtr hWnd = Handle; + switch (value) { + case FormWindowState.Normal: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_NORMAL); + break; + case FormWindowState.Maximized: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_MAXIMIZE); + break; + case FormWindowState.Minimized: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_MINIMIZE); + break; + } + } + + // VSWhidbey 95295: Now set the local property to the passed in value so that + // when UpdateWindowState is by the ShowWindow call above, the window state in effect when + // this call was made will still be effective while processing that method. + formState[FormStateWindowState] = (int)value; + } + } + + /// + /// + /// + /// Gets or sets the text to display in the caption bar of the form. + /// + /// + internal override string WindowText { + get { + // In restricted mode, the windows caption (Text) is modified to show the url of the window. + // The userWindowText is used to cache the user's text. + if (IsRestrictedWindow && formState[FormStateIsWindowActivated] == 1) { + if (userWindowText == null) { + return ""; + } + return userWindowText; + } + + return base.WindowText; + + } + + set { + string oldText = this.WindowText; + + userWindowText = value; + + if (IsRestrictedWindow && formState[FormStateIsWindowActivated] == 1) { + if (value == null) { + value = ""; + } + base.WindowText = RestrictedWindowText(value); + } + else { + base.WindowText = value; + } + + // For non-default FormBorderStyles, we do not set the WS_CAPTION style if the Text property is null or "". + // When we reload the form from code view, the text property is not set till the very end, and so we do not + // end up updating the CreateParams with WS_CAPTION. Fixed this by making sure we call UpdateStyles() when + // we transition from a non-null value to a null value or vice versa in Form.WindowText. + // + if (oldText == null || (oldText.Length == 0)|| value == null || (value.Length == 0)) { + UpdateFormStyles(); + } + } + } + + /// + /// + /// Occurs when the form is activated in code or by the user. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.FormOnActivateDescr)] + public event EventHandler Activated { + add { + Events.AddHandler(EVENT_ACTIVATED, value); + } + remove { + Events.RemoveHandler(EVENT_ACTIVATED, value); + } + } + + + /// + /// + /// Occurs when the form is closing. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnClosingDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public event CancelEventHandler Closing { + add { + Events.AddHandler(EVENT_CLOSING, value); + } + remove { + Events.RemoveHandler(EVENT_CLOSING, value); + } + } + + + /// + /// + /// Occurs when the form is closed. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnClosedDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public event EventHandler Closed { + add { + Events.AddHandler(EVENT_CLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_CLOSED, value); + } + } + + + /// + /// + /// Occurs when the form loses focus and is not the active form. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.FormOnDeactivateDescr)] + public event EventHandler Deactivate { + add { + Events.AddHandler(EVENT_DEACTIVATE, value); + } + remove { + Events.RemoveHandler(EVENT_DEACTIVATE, value); + } + } + + + /// + /// + /// Occurs when the form is closing. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnFormClosingDescr)] + public event FormClosingEventHandler FormClosing { + add { + Events.AddHandler(EVENT_FORMCLOSING, value); + } + remove { + Events.RemoveHandler(EVENT_FORMCLOSING, value); + } + } + + + /// + /// + /// Occurs when the form is closed. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnFormClosedDescr)] + public event FormClosedEventHandler FormClosed { + add { + Events.AddHandler(EVENT_FORMCLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMCLOSED, value); + } + } + + + /// + /// + /// Occurs before the form becomes visible. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnLoadDescr)] + public event EventHandler Load { + add { + Events.AddHandler(EVENT_LOAD, value); + } + remove { + Events.RemoveHandler(EVENT_LOAD, value); + } + } + + /// + /// + /// Occurs when a Multiple Document Interface (MDI) child form is activated or closed + /// within an MDI application. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.FormOnMDIChildActivateDescr)] + public event EventHandler MdiChildActivate { + add { + Events.AddHandler(EVENT_MDI_CHILD_ACTIVATE, value); + } + remove { + Events.RemoveHandler(EVENT_MDI_CHILD_ACTIVATE, value); + } + } + + /// + /// + /// Occurs when the menu of a form loses focus. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnMenuCompleteDescr), + Browsable(false) + ] + public event EventHandler MenuComplete { + add { + Events.AddHandler(EVENT_MENUCOMPLETE, value); + } + remove { + Events.RemoveHandler(EVENT_MENUCOMPLETE, value); + } + } + + /// + /// + /// Occurs when the menu of a form receives focus. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnMenuStartDescr), + Browsable(false) + ] + public event EventHandler MenuStart { + add { + Events.AddHandler(EVENT_MENUSTART, value); + } + remove { + Events.RemoveHandler(EVENT_MENUSTART, value); + } + } + + + /// + /// + /// Occurs after the input language of the form has changed. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnInputLangChangeDescr)] + public event InputLanguageChangedEventHandler InputLanguageChanged { + add { + Events.AddHandler(EVENT_INPUTLANGCHANGE, value); + } + remove { + Events.RemoveHandler(EVENT_INPUTLANGCHANGE, value); + } + } + + + /// + /// + /// Occurs when the the user attempts to change the input language for the + /// form. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnInputLangChangeRequestDescr)] + public event InputLanguageChangingEventHandler InputLanguageChanging { + add { + Events.AddHandler(EVENT_INPUTLANGCHANGEREQUEST, value); + } + remove { + Events.RemoveHandler(EVENT_INPUTLANGCHANGEREQUEST, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + } + + /// + /// + /// Occurs whenever the form is first shown. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnShownDescr)] + public event EventHandler Shown { + add { + Events.AddHandler(EVENT_SHOWN, value); + } + remove { + Events.RemoveHandler(EVENT_SHOWN, value); + } + } + + /// + /// + /// Activates the form and gives it focus. + /// + public void Activate() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + if (Visible && IsHandleCreated) { + if (IsMdiChild) { + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + } + else { + UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this, Handle)); + } + } + } + + /// + /// + /// + /// This function handles the activation of a MDI child form. If a subclass + /// overrides this function, it must call base.ActivateMdiChild. + /// From MSDN: This member supports the .NET Framework infrastructure and is not intended + /// to be used directly from your code. + /// + protected void ActivateMdiChild(Form form) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + ActivateMdiChildInternal(form); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + private void ActivateMdiChildInternal(Form form) { + if (FormerlyActiveMdiChild != null && !FormerlyActiveMdiChild.IsClosing) { + FormerlyActiveMdiChild.UpdateWindowIcon(true); + FormerlyActiveMdiChild = null; + } + + Form activeMdiChild = ActiveMdiChildInternal; + if (activeMdiChild == form) { + return; + } + + //Don't believe we ever hit this with non-null, but leaving it intact in case removing it would cause a problem. + if (null != activeMdiChild) { + activeMdiChild.Active = false; + } + + activeMdiChild = form; + ActiveMdiChildInternal = form; + + if (null != activeMdiChild) { + activeMdiChild.IsMdiChildFocusable = true; + activeMdiChild.Active = true; + } + else if (this.Active) { + ActivateControlInternal(this); + } + + // Note: It is possible that we are raising this event when the activeMdiChild is null, + // this is the case when the only visible mdi child is being closed. See DeactivateMdiChild + // for more info. + OnMdiChildActivate(EventArgs.Empty); + } + + /// + /// + /// Adds + /// an owned form to this form. + /// + public void AddOwnedForm(Form ownedForm) { + if (ownedForm == null) + return; + + if (ownedForm.OwnerInternal != this) { + ownedForm.Owner = this; // NOTE: this calls AddOwnedForm again with correct owner set. + return; + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + // Make sure this isn't already in the list: + for (int i=0;i < ownedFormsCount;i++) { + if (ownedForms[i]==ownedForm) { + return; + } + } + + if (ownedForms == null) { + ownedForms = new Form[4]; + Properties.SetObject(PropOwnedForms, ownedForms); + } + else if (ownedForms.Length == ownedFormsCount) { + Form[] newOwnedForms = new Form[ownedFormsCount*2]; + Array.Copy(ownedForms, 0, newOwnedForms, 0, ownedFormsCount); + ownedForms = newOwnedForms; + Properties.SetObject(PropOwnedForms, ownedForms); + } + + + ownedForms[ownedFormsCount] = ownedForm; + Properties.SetInteger(PropOwnedFormsCount, ownedFormsCount + 1); + } + + // When shrinking the form (i.e. going from Large Fonts to Small + // Fonts) we end up making everything too small due to roundoff, + // etc... solution - just don't shrink as much. + // + private float AdjustScale(float scale) { + // NOTE : This function is cloned in FormDocumentDesigner... remember to keep + // : them in sync + // + + + // Map 0.0 - .92... increment by 0.08 + // + if (scale < .92f) { + return scale + 0.08f; + } + + // Map .92 - .99 to 1.0 + // + else if (scale < 1.0f) { + return 1.0f; + } + + // Map 1.02... increment by 0.08 + // + else if (scale > 1.01f) { + return scale + 0.08f; + } + + // Map 1.0 - 1.01... no change + // + else { + return scale; + } + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void AdjustFormScrollbars(bool displayScrollbars) { + if (WindowState != FormWindowState.Minimized) { + base.AdjustFormScrollbars(displayScrollbars); + } + } + + private void AdjustSystemMenu(IntPtr hmenu) { + UpdateWindowState(); + FormWindowState winState = WindowState; + FormBorderStyle borderStyle = FormBorderStyle; + bool sizableBorder = (borderStyle == FormBorderStyle.SizableToolWindow + || borderStyle == FormBorderStyle.Sizable); + + + bool showMin = MinimizeBox && winState != FormWindowState.Minimized; + bool showMax = MaximizeBox && winState != FormWindowState.Maximized; + bool showClose = ControlBox; + bool showRestore = winState != FormWindowState.Normal; + bool showSize = sizableBorder && winState != FormWindowState.Minimized + && winState != FormWindowState.Maximized; + + if (!showMin) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MINIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MINIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showMax) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MAXIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MAXIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showClose) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_CLOSE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_CLOSE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showRestore) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_RESTORE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_RESTORE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showSize) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_SIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_SIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + +#if SECURITY_DIALOG + AdjustSystemMenuForSecurity(hmenu); +#endif + } + +#if SECURITY_DIALOG + private void AdjustSystemMenuForSecurity(IntPtr hmenu) { + if (formState[FormStateAddedSecurityMenuItem] == 0) { + formState[FormStateAddedSecurityMenuItem] = 1; + + SecurityMenuItem securitySystemMenuItem = new SecurityMenuItem(this); + Properties.SetObject(PropSecuritySystemMenuItem, securitySystemMenuItem); + + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE | NativeMethods.MIIM_DATA; + info.fType = 0; + info.fState = 0; + info.wID = securitySystemMenuItem.ID; + info.hbmpChecked = IntPtr.Zero; + info.hbmpUnchecked = IntPtr.Zero; + info.dwItemData = IntPtr.Zero; + + // Note: This code is not shipping in the final product. We do not want to measure the + // : performance hit of loading the localized resource for this at startup, so I + // : am hard-wiring the strings below. If you need to localize these, move them to + // : a SECONDARY resource file so we don't have to contend with our big error message + // : file on startup. + // + if (IsRestrictedWindow) { + info.dwTypeData = ".NET Restricted Window..."; + } + else { + info.dwTypeData = ".NET Window..."; + } + info.cch = 0; + UnsafeNativeMethods.InsertMenuItem(new HandleRef(this, hmenu), 0, true, info); + + + NativeMethods.MENUITEMINFO_T sep = new NativeMethods.MENUITEMINFO_T(); + sep.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE | NativeMethods.MIIM_DATA; + sep.fType = NativeMethods.MFT_MENUBREAK; + UnsafeNativeMethods.InsertMenuItem(new HandleRef(this, hmenu), 1, true, sep); + + } + } +#endif + + /// + /// This forces the SystemMenu to look like we want. + /// + /// + private void AdjustSystemMenu() { + if (IsHandleCreated) { + IntPtr hmenu = UnsafeNativeMethods.GetSystemMenu(new HandleRef(this, Handle), false); + AdjustSystemMenu(hmenu); + hmenu = IntPtr.Zero; + } + } + + /// + /// + /// This auto scales the form based on the AutoScaleBaseSize. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This method has been deprecated. Use the ApplyAutoScaling method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + protected void ApplyAutoScaling() { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "ApplyAutoScaling... "); + Debug.Indent(); + // NOTE : This function is cloned in FormDocumentDesigner... remember to keep + // : them in sync + // + + + // We also don't do this if the property is empty. Otherwise we will perform + // two GetAutoScaleBaseSize calls only to find that they returned the same + // value. + // + if (!autoScaleBaseSize.IsEmpty) { + Size baseVar = AutoScaleBaseSize; + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "base =" + baseVar); + SizeF newVarF = GetAutoScaleSize(Font); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "new(f)=" + newVarF); + Size newVar = new Size((int)Math.Round(newVarF.Width), (int)Math.Round(newVarF.Height)); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "new(i)=" + newVar); + + // We save a significant amount of time by bailing early if there's no work to be done + if (baseVar.Equals(newVar)) + return; + + float percY = AdjustScale(((float)newVar.Height) / ((float)baseVar.Height)); + float percX = AdjustScale(((float)newVar.Width) / ((float)baseVar.Width)); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "scale=" + percX + ", " + percY); + Scale(percX, percY); + // This would ensure that we use the new + // font information to calculate the AutoScaleBaseSize. According to Triage + // this was decided to Fix in this version. + // + AutoScaleBaseSize = newVar; + } + Debug.Unindent(); + + } + + /// + /// This adjusts the size of the windowRect so that the client rect is the + /// correct size. + /// + /// + private void ApplyClientSize() { + if ((FormWindowState)formState[FormStateWindowState] != FormWindowState.Normal + || !IsHandleCreated) { + return; + } + + // Cache the clientSize, since calling setBounds will end up causing + // clientSize to get reset to the actual clientRect size... + // + Size correctClientSize = ClientSize; + bool hscr = HScroll; + bool vscr = VScroll; + + // This logic assumes that the caller of setClientSize() knows if the scrollbars + // are showing or not. Since the 90% case is that setClientSize() is the persisted + // ClientSize, this is correct. + // Without this logic persisted forms that were saved with the scrollbars showing, + // don't get set to the correct size. + // + bool adjustScroll = false; + if (formState[FormStateSetClientSize] != 0) { + adjustScroll = true; + formState[FormStateSetClientSize] = 0; + } + if (adjustScroll) { + if (hscr) { + correctClientSize.Height += SystemInformation.HorizontalScrollBarHeight; + } + if (vscr) { + correctClientSize.Width += SystemInformation.VerticalScrollBarWidth; + } + } + + IntPtr h = Handle; + NativeMethods.RECT rc = new NativeMethods.RECT(); + SafeNativeMethods.GetClientRect(new HandleRef(this, h), ref rc); + Rectangle currentClient = Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + + Rectangle bounds = Bounds; + + // If the width is incorrect, compute the correct size with + // computeWindowSize. We only do this logic if the width needs to + // be adjusted to avoid double adjusting the window. + // + if (correctClientSize.Width != currentClient.Width) { + Size correct = ComputeWindowSize(correctClientSize); + + // Since computeWindowSize ignores scrollbars, we must tack these on to + // assure the correct size. + // + if (vscr) { + correct.Width += SystemInformation.VerticalScrollBarWidth; + } + if (hscr) { + correct.Height += SystemInformation.HorizontalScrollBarHeight; + } + bounds.Width = correct.Width; + bounds.Height = correct.Height; + Bounds = bounds; + SafeNativeMethods.GetClientRect(new HandleRef(this, h), ref rc); + currentClient = Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + + // If it still isn't correct, then we assume that the problem is + // menu wrapping (since computeWindowSize doesn't take that into + // account), so we just need to adjust the height by the correct + // amount. + // + if (correctClientSize.Height != currentClient.Height) { + + int delta = correctClientSize.Height - currentClient.Height; + bounds.Height += delta; + Bounds = bounds; + } + + UpdateBounds(); + } + + /// + /// + /// Assigns a new parent control. Sends out the appropriate property change + /// notifications for properties that are affected by the change of parent. + /// + internal override void AssignParent(Control value) { + + // If we are being unparented from the MDI client control, remove + // formMDIParent as well. + // + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent != null && formMdiParent.MdiClient != value) { + Properties.SetObject(PropFormMdiParent, null); + } + + base.AssignParent(value); + } + + /// + /// Checks whether a modal dialog is ready to close. If the dialogResult + /// property is not DialogResult.None, the OnClosing and OnClosed events + /// are fired. A return value of true indicates that both events were + /// successfully dispatched and that event handlers did not cancel closing + /// of the dialog. User code should never have a reason to call this method. + /// It is internal only so that the Application class can call it from a + /// modal message loop. + /// + /// closingOnly is set when this is called from WmClose so that we can do just the beginning portion + /// of this and then let the message loop finish it off with the actual close code. + /// + /// Note we set a flag to determine if we've already called close or not. + /// + /// + internal bool CheckCloseDialog(bool closingOnly) { + if (dialogResult == DialogResult.None && Visible) { + return false; + } + try + { + FormClosingEventArgs e = new FormClosingEventArgs(closeReason, false); + + if (!CalledClosing) + { + OnClosing(e); + OnFormClosing(e); + if (e.Cancel) + { + dialogResult = DialogResult.None; + } + else + { + // we have called closing here, and it wasn't cancelled, so we're expecting a close + // call again soon. + CalledClosing = true; + } + } + + if (!closingOnly && dialogResult != DialogResult.None) + { + FormClosedEventArgs fc = new FormClosedEventArgs(closeReason); + OnClosed(fc); + OnFormClosed(fc); + + // reset called closing. + // + CalledClosing = false; + } + } + catch (Exception e) + { + dialogResult = DialogResult.None; + if (NativeWindow.WndProcShouldBeDebuggable) + { + throw; + } + else + { + Application.OnThreadException(e); + } + } + return dialogResult != DialogResult.None || !Visible; + } + + /// + /// + /// Closes the form. + /// + public void Close() { + + if (GetState(STATE_CREATINGHANDLE)) + throw new InvalidOperationException(SR.GetString(SR.ClosingWhileCreatingHandle, "Close")); + + if (IsHandleCreated) { + closeReason = CloseReason.UserClosing; + SendMessage(NativeMethods.WM_CLOSE, 0, 0); + } + else{ + // MSDN: When a form is closed, all resources created within the object are closed and the form is disposed. + // For MDI child: MdiChildren collection gets updated (VSWhidbey# 368642 & 93550) + Dispose(); + } + } + + /// + /// Computes the window size from the clientSize based on the styles + /// returned from CreateParams. + /// + private Size ComputeWindowSize(Size clientSize) { + CreateParams cp = CreateParams; + return ComputeWindowSize(clientSize, cp.Style, cp.ExStyle); + } + + /// + /// Computes the window size from the clientSize base on the specified + /// window styles. This will not return the correct size if menus wrap. + /// + private Size ComputeWindowSize(Size clientSize, int style, int exStyle) { + NativeMethods.RECT result = new NativeMethods.RECT(0, 0, clientSize.Width, clientSize.Height); + AdjustWindowRectEx(ref result, style, HasMenu, exStyle); + return new Size(result.right - result.left, result.bottom - result.top); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Control.ControlCollection CreateControlsInstance() { + return new ControlCollection(this); + } + + /// + /// Cleans up form state after a control has been removed. + /// Package scope for Control + /// + /// + internal override void AfterControlRemoved(Control control, Control oldParent) { + base.AfterControlRemoved(control, oldParent); + + if (control == AcceptButton) { + this.AcceptButton = null; + } + if (control == CancelButton) { + this.CancelButton = null; + } + if (control == ctlClient) { + ctlClient = null; + UpdateMenuHandles(); + } + } + + /// + /// + /// Creates the handle for the Form. If a + /// subclass overrides this function, + /// it must call the base implementation. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void CreateHandle() { + // In the windows MDI code we have to suspend menu + // updates on the parent while creating the handle. Otherwise if the + // child is created maximized, the menu ends up with two sets of + // MDI child ornaments. + Form form = (Form)Properties.GetObject(PropFormMdiParent); + if (form != null){ + form.SuspendUpdateMenuHandles(); + } + + try { + // VSWhidbey 426719 - if the child is created before the MDI parent we can + // get Win32 exceptions as the MDI child is parked to the parking window. + if (IsMdiChild && MdiParentInternal.IsHandleCreated) { + MdiClient mdiClient = MdiParentInternal.MdiClient; + if (mdiClient != null && !mdiClient.IsHandleCreated) { + mdiClient.CreateControl(); + } + } + + // If we think that we are maximized, then a duplicate set of mdi gadgets are created. + // If we create the window maximized, BUT our internal form state thinks it + // isn't... then everything is OK... + // + // We really should find out what causes this... but I can't find it... + // + if (IsMdiChild + && (FormWindowState)formState[FormStateWindowState] == FormWindowState.Maximized) { + + // VSWhidbey 517376: This is the reason why we see the blue borders + // when creating a maximized mdi child, unfortunately we cannot fix this now... + formState[FormStateWindowState] = (int)FormWindowState.Normal; + formState[FormStateMdiChildMax] = 1; + base.CreateHandle(); + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + formState[FormStateMdiChildMax] = 0; + } + else { + base.CreateHandle(); + } + + UpdateHandleWithOwner(); + UpdateWindowIcon(false); + + AdjustSystemMenu(); + + if ((FormStartPosition)formState[FormStateStartPos] != FormStartPosition.WindowsDefaultBounds) { + ApplyClientSize(); + } + if (formState[FormStateShowWindowOnCreate] == 1) { + Visible = true; + } + + + // avoid extra SetMenu calls for perf + if (Menu != null || !TopLevel || IsMdiContainer) + UpdateMenuHandles(); + + // In order for a window not to have a taskbar entry, it must + // be owned. + // + if (!ShowInTaskbar && OwnerInternal == null && TopLevel) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, TaskbarOwner); + + // Make sure the large icon is set so the ALT+TAB icon + // reflects the real icon of the application + Icon icon = Icon; + if (icon != null && TaskbarOwner.Handle != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(TaskbarOwner, NativeMethods.WM_SETICON, NativeMethods.ICON_BIG, icon.Handle); + } + } + + if (formState[FormStateTopMost] != 0) { + TopMost = true; + } + + } + finally { + if (form != null) + form.ResumeUpdateMenuHandles(); + + // We need to reset the styles in case Windows tries to set us up + // with "correct" styles... See ASURT 81646. + // + UpdateStyles(); + } + } + + // Created for fix VSWhidbey 182131. Deactivates active MDI child and temporarily marks it as unfocusable, + // so that WM_SETFOCUS sent to MDIClient does not activate that child. + private void DeactivateMdiChild() { + Form activeMdiChild = ActiveMdiChildInternal; + if (null != activeMdiChild) { + Form mdiParent = activeMdiChild.MdiParentInternal; + + activeMdiChild.Active = false; + activeMdiChild.IsMdiChildFocusable = false; + if (!activeMdiChild.IsClosing) { + FormerlyActiveMdiChild = activeMdiChild; + } + // Enter/Leave events on child controls are raised from the ActivateMdiChildInternal method, usually when another + // Mdi child is getting activated after deactivating this one; but if this is the only visible MDI child + // we need to fake the activation call so MdiChildActivate and Leave events are raised properly. (We say + // in the MSDN doc that the MdiChildActivate event is raised when an mdi child is activated or closed - + // we actually meant the last mdi child is closed). + bool fakeActivation = true; + foreach(Form mdiChild in mdiParent.MdiChildren ){ + if( mdiChild != this && mdiChild.Visible ){ + fakeActivation = false; // more than one mdi child visible. + break; + } + } + + if( fakeActivation ){ + mdiParent.ActivateMdiChildInternal(null); + } + + ActiveMdiChildInternal = null; + + // Note: WM_MDIACTIVATE message is sent to the form being activated and to the form being deactivated, ideally + // we would raise the event here accordingly but it would constitute a breaking change. + //OnMdiChildActivate(EventArgs.Empty); + + // undo merge + UpdateMenuHandles(); + UpdateToolStrip(); + } + } + + /// + /// + /// + /// Calls the default window proc for the form. If + /// a + /// subclass overrides this function, + /// it must call the base implementation. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected override void DefWndProc(ref Message m) { + if (ctlClient != null && ctlClient.IsHandleCreated && ctlClient.ParentInternal == this){ + m.Result = UnsafeNativeMethods.DefFrameProc(m.HWnd, ctlClient.Handle, m.Msg, m.WParam, m.LParam); + } + else if (0 != formStateEx[FormStateExUseMdiChildProc]){ + m.Result = UnsafeNativeMethods.DefMDIChildProc(m.HWnd, m.Msg, m.WParam, m.LParam); + } + else { + base.DefWndProc(ref m); + } + } + + /// + /// + /// Releases all the system resources associated with the Form. If a subclass + /// overrides this function, it must call the base implementation. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + CalledOnLoad = false; + CalledMakeVisible = false; + CalledCreateControl = false; + + if (Properties.ContainsObject(PropAcceptButton)) Properties.SetObject(PropAcceptButton, null); + if (Properties.ContainsObject(PropCancelButton)) Properties.SetObject(PropCancelButton, null); + if (Properties.ContainsObject(PropDefaultButton)) Properties.SetObject(PropDefaultButton, null); + if (Properties.ContainsObject(PropActiveMdiChild)) Properties.SetObject(PropActiveMdiChild, null); + + if (MdiWindowListStrip != null){ + MdiWindowListStrip.Dispose(); + MdiWindowListStrip = null; + } + + if (MdiControlStrip != null){ + MdiControlStrip.Dispose(); + MdiControlStrip = null; + } + + if (MainMenuStrip != null) { + // should NOT call dispose on MainMenuStrip - it's likely NOT to be in the form's control collection. + MainMenuStrip = null; + } + + Form owner = (Form)Properties.GetObject(PropOwner); + if (owner != null) { + owner.RemoveOwnedForm(this); + Properties.SetObject(PropOwner, null); + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + if (ownedForms[i] != null) { + // it calls remove and removes itself. + ownedForms[i].Dispose(); + } + } + + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + + ResetSecurityTip(false /* modalOnly */); + + base.Dispose(disposing); + ctlClient = null; + + MainMenu mainMenu = Menu; + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: We should only dispose this form's menus! + if (mainMenu != null && mainMenu.ownerForm == this) { + mainMenu.Dispose(); + Properties.SetObject(PropMainMenu, null); + } + + if (Properties.GetObject(PropCurMenu) != null) { + Properties.SetObject(PropCurMenu, null); + } + + MenuChanged(Windows.Forms.Menu.CHANGE_ITEMS, null); + + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + + if (dummyMenu != null) { + dummyMenu.Dispose(); + Properties.SetObject(PropDummyMenu, null); + } + + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + + if (mergedMenu != null) { + if (mergedMenu.ownerForm == this || mergedMenu.form == null) { + mergedMenu.Dispose(); + } + Properties.SetObject(PropMergedMenu, null); + } + } + else { + base.Dispose(disposing); + } + } + + /// + /// Adjusts the window style of the CreateParams to reflect the bordericons. + /// + /// + private void FillInCreateParamsBorderIcons(CreateParams cp) { + if (FormBorderStyle != FormBorderStyle.None) { + if (Text != null && Text.Length != 0) { + cp.Style |= NativeMethods.WS_CAPTION; + } + + // In restricted mode, the form must have a system menu, caption and max/min/close boxes. + + if (ControlBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_SYSMENU | NativeMethods.WS_CAPTION; + } + else { + cp.Style &= (~NativeMethods.WS_SYSMENU); + } + + if (MaximizeBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_MAXIMIZEBOX; + } + else { + cp.Style &= ~NativeMethods.WS_MAXIMIZEBOX; + } + + if (MinimizeBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_MINIMIZEBOX; + } + else { + cp.Style &= ~NativeMethods.WS_MINIMIZEBOX; + } + + if (HelpButton && !MaximizeBox && !MinimizeBox && ControlBox) { + // Windows should ignore WS_EX_CONTEXTHELP unless all those conditions hold. + // But someone must have failed the check, because Windows 2000 + // will show a help button if either the maximize or + // minimize button is disabled. + cp.ExStyle |= NativeMethods.WS_EX_CONTEXTHELP; + } + else { + cp.ExStyle &= ~NativeMethods.WS_EX_CONTEXTHELP; + } + } + } + + /// + /// Adjusts the window style of the CreateParams to reflect the borderstyle. + /// + private void FillInCreateParamsBorderStyles(CreateParams cp) { + switch ((FormBorderStyle)formState[FormStateBorderStyle]) { + case FormBorderStyle.None: + // SECREVIEW : The check for IsRestrictedWindow here doesn't seem to be needed since in restricted + // mode it is not allowed to have FormBorderSytle.None (see FormBorderStyle) and the + // default value is FormBorderStyle.Sizable. But this has been there for long so to be + // in the safe side is better to leave it (defense in depth), it's not harmful at all. + // + if (IsRestrictedWindow) { + goto case FormBorderStyle.FixedSingle; + } + break; + case FormBorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + case FormBorderStyle.Sizable: + cp.Style |= NativeMethods.WS_BORDER | NativeMethods.WS_THICKFRAME; + break; + case FormBorderStyle.Fixed3D: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case FormBorderStyle.FixedDialog: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_DLGMODALFRAME; + break; + case FormBorderStyle.FixedToolWindow: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + break; + case FormBorderStyle.SizableToolWindow: + cp.Style |= NativeMethods.WS_BORDER | NativeMethods.WS_THICKFRAME; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + break; + } + } + + /// + /// Adjusts the CreateParams to reflect the window bounds and start position. + /// + private void FillInCreateParamsStartPosition(CreateParams cp) { + + // V#42613 - removed logic that forced MDI children to always be + // default size and position... need to verify that + // this works on Win9X + /* + if (getIsMdiChild()) { + cp.x = NativeMethods.CW_USEDEFAULT; + cp.y = NativeMethods.CW_USEDEFAULT; + cp.width = NativeMethods.CW_USEDEFAULT; + cp.height = NativeMethods.CW_USEDEFAULT; + } + else { + */ + if (formState[FormStateSetClientSize] != 0) { + + // V7#37980 - when computing the client window size, don't tell them that + // we are going to be maximized! + // + int maskedStyle = cp.Style & ~(NativeMethods.WS_MAXIMIZE | NativeMethods.WS_MINIMIZE); + Size correct = ComputeWindowSize(ClientSize, maskedStyle, cp.ExStyle); + + if (IsRestrictedWindow) { + correct = ApplyBoundsConstraints(cp.X, cp.Y, correct.Width, correct.Height).Size; + } + cp.Width = correct.Width; + cp.Height = correct.Height; + + } + + switch ((FormStartPosition)formState[FormStateStartPos]) { + case FormStartPosition.WindowsDefaultBounds: + cp.Width = NativeMethods.CW_USEDEFAULT; + cp.Height = NativeMethods.CW_USEDEFAULT; + // no break, fall through to set the location to default... + goto case FormStartPosition.WindowsDefaultLocation; + case FormStartPosition.WindowsDefaultLocation: + case FormStartPosition.CenterParent: + // VSWhidbey 93522: Since FillInCreateParamsStartPosition() is called + // several times when a window is shown, we'll need to force the location + // each time for MdiChild windows that are docked so that the window will + // be created in the correct location and scroll bars will not be displayed. + if (IsMdiChild && DockStyle.None != Dock){ + break; + } + + cp.X = NativeMethods.CW_USEDEFAULT; + cp.Y = NativeMethods.CW_USEDEFAULT; + break; + case FormStartPosition.CenterScreen: + if (IsMdiChild) { + Control mdiclient = MdiParentInternal.MdiClient; + Rectangle clientRect = mdiclient.ClientRectangle; + + cp.X = Math.Max(clientRect.X,clientRect.X + (clientRect.Width - cp.Width)/2); + cp.Y = Math.Max(clientRect.Y,clientRect.Y + (clientRect.Height - cp.Height)/2); + } + else { + Screen desktop = null; + IWin32Window dialogOwner = (IWin32Window)Properties.GetObject(PropDialogOwner); + if ((OwnerInternal != null) || (dialogOwner != null)) { + IntPtr ownerHandle = (dialogOwner != null) ? Control.GetSafeHandle(dialogOwner) : OwnerInternal.Handle; + desktop = Screen.FromHandleInternal(ownerHandle); + } + else { + desktop = Screen.FromPoint(Control.MousePosition); + } + Rectangle screenRect = desktop.WorkingArea; + //if, we're maximized, then don't set the x & y coordinates (they're @ (0,0) ) + if (WindowState != FormWindowState.Maximized) { + cp.X = Math.Max(screenRect.X,screenRect.X + (screenRect.Width - cp.Width)/2); + cp.Y = Math.Max(screenRect.Y,screenRect.Y + (screenRect.Height - cp.Height)/2); + } + } + break; + } + } + + /// + /// Adjusts the Createparams to reflect the window state. + /// + private void FillInCreateParamsWindowState(CreateParams cp) { + // SECUNDONE: We don't need to check for restricted window here since the only way to set the WindowState + // programatically is by changing the property and we have a check for it in the property setter. + // We don't want to check it here again because it would not allow to set the CreateParams.Style + // to the current WindowState so the window will be set to its current Normal size + // (which in some cases is quite bogus) when Control.UpdateStylesCore is called. + // See VSWhidbey#388028. + // + // if( IsRestrictedWindow ){ + // return; + // } + + switch ((FormWindowState)formState[FormStateWindowState]) { + case FormWindowState.Maximized: + cp.Style |= NativeMethods.WS_MAXIMIZE; + break; + case FormWindowState.Minimized: + cp.Style |= NativeMethods.WS_MINIMIZE; + break; + } + } + + /// + /// Sets focus to the Form. + /// Attempts to set focus to this Form. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal override bool FocusInternal() { + Debug.Assert( IsHandleCreated, "Attempt to set focus to a form that has not yet created its handle." ); + //if this form is a MdiChild, then we need to set the focus in a different way... + // + if (IsMdiChild) { + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + return Focused; + } + + return base.FocusInternal(); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This method has been deprecated. Use the AutoScaleDimensions property instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "The quick ..." is the archetipal string + // used to measure font height. + // So we don't have to localize it. + ] + public static SizeF GetAutoScaleSize(Font font) { + + float height = font.Height; + float width = 9.0f; + + try { + using( Graphics graphics = Graphics.FromHwndInternal(IntPtr.Zero /*screen*/) ) { + string magicString = "The quick brown fox jumped over the lazy dog."; + double magicNumber = 44.549996948242189; // chosen for compatibility with older versions of windows forms, but approximately magicString.Length + float stringWidth = graphics.MeasureString(magicString, font).Width; + width = (float) (stringWidth / magicNumber); + } + } + catch { // We may get an bogus OutOfMemoryException + // (which is a critical exception - according to ClientUtils.IsCriticalException()) + // from GDI+. So we can't use ClientUtils.IsCriticalException here and rethrow. + } + + return new SizeF(width, height); + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + + // + + Size preferredSize = base.GetPreferredSizeCore(proposedSize); + +#if MAGIC_PADDING +//VSWhidbey 401943: We are removing the magic padding in runtime. + + bool dialogFixRequired = false; + + if (Padding == Padding.Empty) { + Padding paddingToAdd = Padding.Empty; + foreach (Control c in this.Controls) { + if (c.Dock == DockStyle.None) { + AnchorStyles anchor = c.Anchor; + if (anchor == AnchorStyles.None) { + break; + } + + // TOP + // if we are anchored to the top only add padding if the top edge is too far down + if ((paddingToAdd.Bottom == 0) && DefaultLayout.IsAnchored(anchor, AnchorStyles.Top)) { + if (c.Bottom > preferredSize.Height - FormPadding.Bottom) { + paddingToAdd.Bottom = FormPadding.Bottom; + + dialogFixRequired = true; + } + } + // BOTTOM + // if we are anchored to the bottom + // dont add any padding - it's way too confusing to be dragging a button up + // and have the form grow to the bottom. + + // LEFT + // if we are anchored to the left only add padding if the right edge is too far right + if ((paddingToAdd.Left == 0) && DefaultLayout.IsAnchored(anchor, AnchorStyles.Left)) { + if (c.Right > preferredSize.Width - FormPadding.Right) { + paddingToAdd.Right = FormPadding.Right; + dialogFixRequired = true; + } + } + // RIGHT + // if we are anchored to the bottom + // dont add any padding - it's way too confusing to be dragging a button left + // and have the form grow to the right + + } + } + Debug.Assert(!dialogFixRequired, "this dialog needs update: " + this.Name); + return preferredSize + paddingToAdd.Size; + } +#endif + return preferredSize; + } + + /// + /// This private method attempts to resolve security zone and site + /// information given a list of urls (sites). This list is supplied + /// by the runtime and will contain the paths of all loaded assemblies + /// on the stack. From here, we can examine zones and sites and + /// attempt to identify the unique and mixed scenarios for each. This + /// information will be displayed in the titlebar of the Form in a + /// semi-trust environment. + /// + private void ResolveZoneAndSiteNames(ArrayList sites, ref string securityZone, ref string securitySite) { + + //Start by defaulting to 'unknown zone' and 'unknown site' strings. We will return this + //information if anything goes wrong while trying to resolve this information. + // + securityZone = SR.GetString(SR.SecurityRestrictedWindowTextUnknownZone); + securitySite = SR.GetString(SR.SecurityRestrictedWindowTextUnknownSite); + + try + { + //these conditions must be met + // + if (sites == null || sites.Count == 0) + return; + + //create a new zone array list which has no duplicates and no + //instances of mycomputer + ArrayList zoneList = new ArrayList(); + foreach (object arrayElement in sites) + { + if (arrayElement == null) + return; + + string url = arrayElement.ToString(); + + if (url.Length == 0) + return; + + //into a zoneName + // + Zone currentZone = Zone.CreateFromUrl(url); + + //skip this if the zone is mycomputer + // + if (currentZone.SecurityZone.Equals(SecurityZone.MyComputer)) + continue; + + //add our unique zonename to our list of zones + // + string zoneName = currentZone.SecurityZone.ToString(); + + if (!zoneList.Contains(zoneName)) + { + zoneList.Add(zoneName); + } + } + + //now, we resolve the zone name based on the unique information + //left in the zoneList + // + if (zoneList.Count == 0) + { + //here, all the original zones were 'mycomputer' + //so we can just return that + securityZone = SecurityZone.MyComputer.ToString(); + } + else if (zoneList.Count == 1) + { + //we only found 1 unique zone other than + //mycomputer + securityZone = zoneList[0].ToString(); + } + else + { + //here, we found multiple zones + // + securityZone = SR.GetString(SR.SecurityRestrictedWindowTextMixedZone); + } + + //generate a list of loaded assemblies that came from the gac, this + //way we can safely ignore these from the url list + ArrayList loadedAssembliesFromGac = new ArrayList(); + + // SECREVIEW : Querying for assembly info is safe. + // + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + + try + { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) + { + if (asm.GlobalAssemblyCache) + { + loadedAssembliesFromGac.Add(asm.CodeBase.ToUpper(CultureInfo.InvariantCulture)); + } + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + //now, build up a sitelist which contains a friendly string + //we've extracted via the uri class and omit any urls that reference + //our local gac + // + ArrayList siteList = new ArrayList(); + foreach (object arrayElement in sites) + { + //we know that each element is valid because of our + //first pass + Uri currentSite = new Uri(arrayElement.ToString()); + + //if we see a site that contains the path to our gac, + //we'll just skip it + + if (loadedAssembliesFromGac.Contains(currentSite.AbsoluteUri.ToUpper(CultureInfo.InvariantCulture))) + { + continue; + } + + //add the unique host name to our list + string hostName = currentSite.Host; + if (hostName.Length > 0 && !siteList.Contains(hostName)) + siteList.Add(hostName); + } + + + //resolve the site name from our list, if siteList.count == 0 + //then we have already set our securitySite to "unknown site" + // + if (siteList.Count == 0) { + //here, we'll set the local machine name to the site string + // + + // SECREVIEW : Querying for environment info is safe. + // + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + securitySite = Environment.MachineName; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + else if (siteList.Count == 1) + { + //We found 1 unique site other than the info in the + //gac + securitySite = siteList[0].ToString(); + } + else + { + //multiple sites, we'll have to return 'mixed sites' + // + securitySite = SR.GetString(SR.SecurityRestrictedWindowTextMultipleSites); + } + } + catch + { + //We'll do nothing here. The idea is that zone and security strings are set + //to "unkown" at the top of this method - if an exception is thrown, we'll + //stick with those values + } + } + + /// + /// Sets the restricted window text (titlebar text of a form) when running + /// in a semi-trust environment. The format is: [zone info] - Form Text - [site info] + /// + private string RestrictedWindowText(string original) { + EnsureSecurityInformation(); + return string.Format(CultureInfo.CurrentCulture, Application.SafeTopLevelCaptionFormat, original, securityZone, securitySite); + } + + private void EnsureSecurityInformation() { + if (securityZone == null || securitySite == null) { + ArrayList zones; + ArrayList sites; + + SecurityManager.GetZoneAndOrigin( out zones, out sites ); + + ResolveZoneAndSiteNames(sites, ref securityZone, ref securitySite); + } + } + + private void CallShownEvent() + { + OnShown(EventArgs.Empty); + } + + /// + /// Override since CanProcessMnemonic is overriden too (base.CanSelectCore calls CanProcessMnemonic). + /// + internal override bool CanSelectCore() { + if( !GetStyle(ControlStyles.Selectable) || !this.Enabled || !this.Visible) { + return false; + } + return true; + } + + /// + /// When an MDI form is hidden it means its handle has not yet been created or has been destroyed (see + /// SetVisibleCore). If the handle is recreated, the form will be made visible which should be avoided. + /// + internal bool CanRecreateHandle() { + if( IsMdiChild ){ + // During changing visibility, it is possible that the style returns true for visible but the handle has + // not yet been created, add a check for both. + return GetState(STATE_VISIBLE) && IsHandleCreated; + } + return true; + } + + /// + /// Overriden to handle MDI mnemonic processing properly. + /// + internal override bool CanProcessMnemonic() { +#if DEBUG + TraceCanProcessMnemonic(); +#endif + // If this is a Mdi child form, child controls should process mnemonics only if this is the active mdi child. + if (this.IsMdiChild && (formStateEx[FormStateExMnemonicProcessed] == 1 || this != this.MdiParentInternal.ActiveMdiChildInternal || this.WindowState == FormWindowState.Minimized)){ + return false; + } + + return base.CanProcessMnemonic(); + } + + /// + /// Overriden to handle MDI mnemonic processing properly. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // MDI container form has at least one control, the MDI client which contains the MDI children. We need + // to allow the MDI children process the mnemonic before any other control in the MDI container (like + // menu items or any other control). + if( base.ProcessMnemonic( charCode ) ){ + return true; + } + + if( this.IsMdiContainer ){ + // ContainerControl have already processed the active MDI child for us (if any) in the call above, + // now process remaining controls (non-mdi children). + if( this.Controls.Count > 1 ){ // Ignore the MdiClient control + for( int index = 0; index < this.Controls.Count; index++ ){ + Control ctl = this.Controls[index]; + if( ctl is MdiClient ){ + continue; + } + + if( ctl.ProcessMnemonic( charCode ) ){ + return true; + } + } + } + + return false; + } + + return false; + } + + /// + /// + /// Centers the dialog to its parent. + /// + /// + protected void CenterToParent() { + if (TopLevel) { + Point p = new Point(); + Size s = Size; + IntPtr ownerHandle = IntPtr.Zero; + + ownerHandle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + if (ownerHandle != IntPtr.Zero) { + Screen desktop = Screen.FromHandleInternal(ownerHandle); + Rectangle screenRect = desktop.WorkingArea; + NativeMethods.RECT ownerRect = new NativeMethods.RECT(); + + UnsafeNativeMethods.GetWindowRect(new HandleRef(null, ownerHandle), ref ownerRect); + + p.X = (ownerRect.left + ownerRect.right - s.Width) / 2; + if (p.X < screenRect.X) + p.X = screenRect.X; + else if (p.X + s.Width > screenRect.X + screenRect.Width) + p.X = screenRect.X + screenRect.Width - s.Width; + + p.Y = (ownerRect.top + ownerRect.bottom - s.Height) / 2; + if (p.Y < screenRect.Y) + p.Y = screenRect.Y; + else if (p.Y + s.Height > screenRect.Y + screenRect.Height) + p.Y = screenRect.Y + screenRect.Height - s.Height; + + Location = p; + } + else { + CenterToScreen(); + } + } + } + + /// + /// + /// Centers the dialog to the screen. This will first attempt to use + /// the owner property to determine the correct screen, then + /// it will try the HWND owner of the form, and finally this will + /// center the form on the same monitor as the mouse cursor. + /// + /// + protected void CenterToScreen() { + Point p = new Point(); + Screen desktop = null; + if (OwnerInternal != null) { + desktop = Screen.FromControl(OwnerInternal); + } + else { + IntPtr hWndOwner = IntPtr.Zero; + if (TopLevel) { + hWndOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + } + if (hWndOwner != IntPtr.Zero) { + desktop = Screen.FromHandleInternal(hWndOwner); + } + else { + desktop = Screen.FromPoint(Control.MousePosition); + } + } + Rectangle screenRect = desktop.WorkingArea; + p.X = Math.Max(screenRect.X,screenRect.X + (screenRect.Width - Width)/2); + p.Y = Math.Max(screenRect.Y,screenRect.Y + (screenRect.Height - Height)/2); + Location = p; + } + + /// + /// Invalidates the merged menu, forcing the menu to be recreated if + /// needed again. + /// + private void InvalidateMergedMenu() { + // here, we just set the merged menu to null (indicating that the menu structure + // needs to be rebuilt). Then, we signal the parent to updated its menus. + if (Properties.ContainsObject(PropMergedMenu)) { + MainMenu menu = Properties.GetObject(PropMergedMenu) as MainMenu; + if (menu != null && menu.ownerForm == this) { + menu.Dispose(); + } + Properties.SetObject(PropMergedMenu, null); + } + + Form parForm = ParentFormInternal; + if (parForm != null) { + parForm.MenuChanged(0, parForm.Menu); + } + } + + /// + /// + /// Arranges the Multiple Document Interface + /// (MDI) child forms according to value. + /// + public void LayoutMdi(MdiLayout value) { + if (ctlClient == null){ + return; + } + ctlClient.LayoutMdi(value); + } + + // Package scope for menu interop + internal void MenuChanged(int change, Menu menu) { + Form parForm = ParentFormInternal; + if (parForm != null && this == parForm.ActiveMdiChildInternal) { + parForm.MenuChanged(change, menu); + return; + } + + switch (change) { + case Windows.Forms.Menu.CHANGE_ITEMS: + case Windows.Forms.Menu.CHANGE_MERGE: + if (ctlClient == null || !ctlClient.IsHandleCreated) { + if (menu == Menu && change == Windows.Forms.Menu.CHANGE_ITEMS) + UpdateMenuHandles(); + break; + } + + // Tell the children to toss their mergedMenu. + if (IsHandleCreated) { + UpdateMenuHandles(null, false); + } + + Control.ControlCollection children = ctlClient.Controls; + for (int i = children.Count; i-- > 0;) { + Control ctl = children[i]; + if (ctl is Form && ctl.Properties.ContainsObject(PropMergedMenu)) { + MainMenu mainMenu = ctl.Properties.GetObject(PropMergedMenu) as MainMenu; + if (mainMenu != null && mainMenu.ownerForm == ctl) { + mainMenu.Dispose(); + } + ctl.Properties.SetObject(PropMergedMenu, null); + } + } + + UpdateMenuHandles(); + break; + case Windows.Forms.Menu.CHANGE_VISIBLE: + if (menu == Menu || (this.ActiveMdiChildInternal != null && menu == this.ActiveMdiChildInternal.Menu)) { + UpdateMenuHandles(); + } + break; + case Windows.Forms.Menu.CHANGE_MDI: + if (ctlClient != null && ctlClient.IsHandleCreated) { + UpdateMenuHandles(); + } + break; + } + } + + /// + /// + /// The activate event is fired when the form is activated. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnActivated(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_ACTIVATED]; + if (handler != null) handler(this,e); + } + + /// + /// Override of AutoScaleModeChange method from ContainerControl. We use + /// this to keep our own AutoScale property in sync. + /// + internal override void OnAutoScaleModeChanged() { + base.OnAutoScaleModeChanged(); + // Obsolete code required here for backwards compat +#pragma warning disable 618 + if (formStateEx[FormStateExSettingAutoScale] != 1) { + AutoScale = false; + } +#pragma warning restore 618 + + } + + protected override void OnBackgroundImageChanged(EventArgs e) { + base.OnBackgroundImageChanged(e); + if (IsMdiContainer) + { + MdiClient.BackgroundImage = BackgroundImage; + // requires explicit invalidate to update the image. + MdiClient.Invalidate(); + } + } + + protected override void OnBackgroundImageLayoutChanged(EventArgs e) { + base.OnBackgroundImageLayoutChanged(e); + if (IsMdiContainer) + { + MdiClient.BackgroundImageLayout = BackgroundImageLayout; + // requires explicit invalidate to update the imageLayout. + MdiClient.Invalidate(); + } + } + + /// + /// + /// The Closing event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClosing(CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[EVENT_CLOSING]; + if (handler != null) handler(this,e); + } + + /// + /// + /// The Closed event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClosed(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_CLOSED]; + if (handler != null) handler(this,e); + } + + + /// + /// + /// The Closing event is fired before the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFormClosing(FormClosingEventArgs e) { + FormClosingEventHandler handler = (FormClosingEventHandler)Events[EVENT_FORMCLOSING]; + if (handler != null) handler(this,e); + } + + /// + /// + /// The Closed event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFormClosed(FormClosedEventArgs e) { + //Remove the form from Application.OpenForms (nothing happens if isn't present) + Application.OpenFormsInternalRemove(this); + + FormClosedEventHandler handler = (FormClosedEventHandler)Events[EVENT_FORMCLOSED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the CreateControl event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnCreateControl() { + CalledCreateControl = true; + base.OnCreateControl(); + + if (CalledMakeVisible && !CalledOnLoad) { + CalledOnLoad = true; + OnLoad(EventArgs.Empty); + } + } + + /// + /// + /// Raises the Deactivate event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDeactivate(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DEACTIVATE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the EnabledChanged event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + if (!DesignMode && Enabled && Active) { + // Make sure we activate the active control. See VSWhidbey#143277 + Control activeControl = ActiveControl; + + // Seems safe to call this here without demanding permissions, since we only + // get here if this form is enabled and active. + if( activeControl == null ){ + SelectNextControlInternal(this, true, true, true, true); + } + else{ + FocusActiveControlInternal(); + } + } + } + + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnEnter(EventArgs e) { + base.OnEnter(e); + + // VSWhidbey#93542. Enter events are not raised on mdi child form controls on form Enabled. + if( IsMdiChild ){ + UpdateFocusedControl(); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnFontChanged(EventArgs e) { + if (DesignMode) { + UpdateAutoScaleBaseSize(); + } + base.OnFontChanged(e); + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. + /// Call base.OnHandleCreated first. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleCreated(EventArgs e) { + formStateEx[FormStateExUseMdiChildProc] = (IsMdiChild && Visible) ? 1 : 0; + base.OnHandleCreated(e); + UpdateLayered(); + } + + /// + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle is about to be destroyed. + /// Call base.OnHandleDestroyed last. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + formStateEx[FormStateExUseMdiChildProc] = 0; + + // just make sure we're no longer in the forms collection list + Application.OpenFormsInternalRemove(this); + + // If the handle is being destroyed, and the security tip hasn't been dismissed + // then we remove it from the property bag. When we come back around and get + // an NCACTIVATE we will see that this is missing and recreate the security + // tip in it's default state. + // + ResetSecurityTip(true /* modalOnly */); + } + + /// + /// + /// Handles the event that a helpButton is clicked + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHelpButtonClicked(CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[EVENT_HELPBUTTONCLICKED]; + if (handler != null) { + handler(this,e); + } + } + + /// + protected override void OnLayout(LayoutEventArgs levent) { + + // Typically forms are either top level or parented to an MDIClient area. + // In either case, forms a responsible for managing their own size. + if(AutoSize /*&& DesignMode*/) { + // If AutoSized, set the Form to the maximum of its preferredSize or the user + // specified size. + Size prefSize = this.PreferredSize; + minAutoSize = prefSize; + + // This used to use "GetSpecifiedBounds" - but it was not updated when we're in the middle of + // a modal resizing loop (WM_WINDOWPOSCHANGED). + Size adjustedSize = AutoSizeMode == AutoSizeMode.GrowAndShrink ? prefSize : LayoutUtils.UnionSizes(prefSize, Size); + + IArrangedElement form = this as IArrangedElement; + if (form != null) { + form.SetBounds(new Rectangle(this.Left, this.Top, adjustedSize.Width, adjustedSize.Height), BoundsSpecified.None); + } + } + base.OnLayout(levent); + } + + /// + /// + /// The Load event is fired before the form becomes visible for the first time. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLoad(EventArgs e) { + //First - add the form to Application.OpenForms + Application.OpenFormsInternalAdd(this); + if (Application.UseWaitCursor) { + this.UseWaitCursor = true; + } + + // subhag: This will apply AutoScaling to the form just + // before the form becomes visible. + // + if (formState[FormStateAutoScaling] == 1 && !DesignMode) { + // Turn off autoscaling so we don't do this on every handle + // creation. + // + formState[FormStateAutoScaling] = 0; + // Obsolete code required here for backwards compat +#pragma warning disable 618 + ApplyAutoScaling(); +#pragma warning restore 618 + + } + +/* + /// + if (AutoSize) { + + + Size prefSize = this.PreferredSize; + minAutoSize = prefSize; + + + + Size adjustedSize = LayoutUtils.UnionSizes(prefSize, Size); + this.Size = adjustedSize; + + + }*/ + + // Also, at this time we can now locate the form the the correct + // area of the screen. We must do this after applying any + // autoscaling. + // + if (GetState(STATE_MODAL)) { + FormStartPosition startPos = (FormStartPosition)formState[FormStateStartPos]; + if (startPos == FormStartPosition.CenterParent) { + CenterToParent(); + } + else if (startPos == FormStartPosition.CenterScreen) { + CenterToScreen(); + } + } + + // There is no good way to explain this event except to say + // that it's just another name for OnControlCreated. + EventHandler handler = (EventHandler)Events[EVENT_LOAD]; + if (handler != null) { + string text = Text; + + handler(this,e); + + + // It seems that if you set a window style during the onload + // event, we have a problem initially painting the window. + // So in the event that the user has set the on load event + // in their application, we should go ahead and invalidate + // the controls in their collection so that we paint properly. + // This seems to manifiest itself in changes to the window caption, + // and changes to the control box and help. + + foreach (Control c in Controls) { + c.Invalidate(); + } + } + + //finally fire the newOnShown(unless the form has already been closed) + if (IsHandleCreated) + { + this.BeginInvoke(new MethodInvoker(CallShownEvent)); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMaximizedBoundsChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MAXIMIZEDBOUNDSCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMaximumSizeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MAXIMUMSIZECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMinimumSizeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MINIMUMSIZECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInputLanguageChanged(InputLanguageChangedEventArgs e) { + InputLanguageChangedEventHandler handler = (InputLanguageChangedEventHandler)Events[EVENT_INPUTLANGCHANGE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInputLanguageChanging(InputLanguageChangingEventArgs e) { + InputLanguageChangingEventHandler handler = (InputLanguageChangingEventHandler)Events[EVENT_INPUTLANGCHANGEREQUEST]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnVisibleChanged(EventArgs e) { + UpdateRenderSizeGrip(); + Form mdiParent = MdiParentInternal; + if (mdiParent != null) { + mdiParent.UpdateMdiWindowListStrip(); + } + base.OnVisibleChanged(e); + + // windows forms have to behave like dialog boxes sometimes. If the + // user has specified that the mouse should snap to the + // Accept button using the Mouse applet in the control panel, + // we have to respect that setting each time our form is made visible. + bool data = false; + if (IsHandleCreated + && Visible + && (AcceptButton != null) + && UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETSNAPTODEFBUTTON, 0, ref data, 0) + && data) { + + Control button = AcceptButton as Control; + NativeMethods.POINT ptToSnap = new NativeMethods.POINT( + button.Left + button.Width / 2, + button.Top + button.Height / 2); + + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, Handle), ptToSnap); + if (!button.IsWindowObscured) { + // SECREVIEW : It is safe to change the cursor position from here. + // + IntSecurity.AdjustCursorPosition.Assert(); + try { + Cursor.Position = new Point(ptToSnap.x, ptToSnap.y); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMdiChildActivate(EventArgs e) { + UpdateMenuHandles(); + UpdateToolStrip(); + EventHandler handler = (EventHandler)Events[EVENT_MDI_CHILD_ACTIVATE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMenuStart(EventArgs e) { + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip != null) { + secTip.Pop(true /*noLongerFirst*/); + } + EventHandler handler = (EventHandler)Events[EVENT_MENUSTART]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the MenuComplete event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMenuComplete(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_MENUCOMPLETE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// + /// Raises the Paint event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + if (formState[FormStateRenderSizeGrip] != 0) { + Size sz = ClientSize; + if (Application.RenderWithVisualStyles) { + if (sizeGripRenderer == null) { + sizeGripRenderer = new VisualStyleRenderer(VisualStyleElement.Status.Gripper.Normal); + } + + sizeGripRenderer.DrawBackground(e.Graphics, new Rectangle(sz.Width - SizeGripSize, sz.Height - SizeGripSize, SizeGripSize, SizeGripSize)); + } + else { + ControlPaint.DrawSizeGrip(e.Graphics, BackColor, sz.Width - SizeGripSize, sz.Height - SizeGripSize, SizeGripSize, SizeGripSize); + } + } + + if (IsMdiContainer) { + e.Graphics.FillRectangle(SystemBrushes.AppWorkspace, ClientRectangle); + } + } + + /// + /// + /// + /// Raises the Resize event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (formState[FormStateRenderSizeGrip] != 0) { + Invalidate(); + } + } + + /// + /// + /// + /// Raises the DpiChanged event. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + protected virtual void OnDpiChanged(DpiChangedEventArgs e) { + if (e.DeviceDpiNew != e.DeviceDpiOld) { + CommonProperties.xClearAllPreferredSizeCaches(this); + + // call any additional handlers + ((DpiChangedEventHandler)Events[EVENT_DPI_CHANGED])?.Invoke(this, e); + + if (!e.Cancel) { + float factor = (float)e.DeviceDpiNew / (float)e.DeviceDpiOld; + SuspendAllLayout(this); + try { + + // Form 'MinimumSize' is explicit set to make form shrink in case of DPI is changed. For scaling factor >1, + // Form layout automatically grows to fit to the container size. + if (DpiHelper.EnableDpiChangedHighDpiImprovements && factor < 1) { + MinimumSize = new Size(e.SuggestedRectangle.Width, e.SuggestedRectangle.Height); + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, HandleInternal), NativeMethods.NullHandleRef, e.SuggestedRectangle.X, e.SuggestedRectangle.Y, e.SuggestedRectangle.Width, e.SuggestedRectangle.Height, NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); + if (AutoScaleMode != AutoScaleMode.Font) { + Font = DpiHelper.EnableDpiChangedHighDpiImprovements ? + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style, this.Font.Unit, this.Font.GdiCharSet, this.Font.GdiVerticalFont) : + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style); + FormDpiChanged(factor); + } + else { + ScaleFont(factor); + FormDpiChanged(factor); + } + } + finally { + // We want to perform layout for dpi-changed HDpi improvements - setting the second parameter to 'true' + ResumeAllLayout(this, DpiHelper.EnableDpiChangedHighDpiImprovements); + } + } + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this top level window is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.FormOnDpiChangedDescr)] + public event DpiChangedEventHandler DpiChanged { + add { + Events.AddHandler(EVENT_DPI_CHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DPI_CHANGED, value); + } + } + + /// + /// Handles the WM_DPICHANGED message + /// + private void WmDpiChanged(ref Message m) { + DefWndProc(ref m); + + DpiChangedEventArgs e = new DpiChangedEventArgs(deviceDpi, m); + deviceDpi = e.DeviceDpiNew; + + OnDpiChanged(e); + } + + /// + /// + /// + /// Allows derived form to handle WM_GETDPISCALEDSIZE message. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual bool OnGetDpiScaledSize(int deviceDpiOld, int deviceDpiNew, ref Size desiredSize) { + return false; // scale linearly + } + + /// + /// Handles the WM_GETDPISCALEDSIZE message, this is a chance for the application to + /// scale window size non-lineary. If this message is not processed, the size is scaled linearly by Windows. + /// This message is sent to top level windows before WM_DPICHANGED. + /// If the application responds to this message, the resulting size will be the candidate rectangle + /// sent to WM_DPICHANGED. The WPARAM contains a DPI value.� The size needs to be computed if + /// the window were to switch to this DPI.�LPARAM is unused and will be zero. + /// The return value is a size, where the LOWORD is the desired width of the window and the HIWORD + /// is the desired height of the window.� A return value of zero indicates that the app does not + /// want any special behavior and the candidate rectangle will be computed linearly. + /// + private void WmGetDpiScaledSize(ref Message m) { + DefWndProc(ref m); + + Size desiredSize = new Size(); + if (OnGetDpiScaledSize(deviceDpi, NativeMethods.Util.SignedLOWORD(m.WParam), ref desiredSize)) { + m.Result = (IntPtr)(unchecked((Size.Height & 0xFFFF) << 16) | (Size.Width & 0xFFFF)); + } else { + m.Result = IntPtr.Zero; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + + // Want to do this after we fire the event. + if (RightToLeft == RightToLeft.Yes) { + foreach(Control c in this.Controls) { + c.RecreateHandleCore(); + } + } + + } + + /// + /// + /// Thi event fires whenever the form is first shown. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnShown(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SHOWN]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnTextChanged(EventArgs e) { + base.OnTextChanged(e); + + // If there is no control box, there should only be a title bar if text != "". + int newTextEmpty = Text.Length == 0 ? 1 : 0; + if (!ControlBox && formState[FormStateIsTextEmpty] != newTextEmpty) + this.RecreateHandle(); + + formState[FormStateIsTextEmpty] = newTextEmpty; + } + + /// + /// Simulates a InputLanguageChanged event. Used by Control to forward events + /// to the parent form. + /// + /// + internal void PerformOnInputLanguageChanged(InputLanguageChangedEventArgs iplevent) { + OnInputLanguageChanged(iplevent); + } + + /// + /// Simulates a InputLanguageChanging event. Used by Control to forward + /// events to the parent form. + /// + /// + internal void PerformOnInputLanguageChanging(InputLanguageChangingEventArgs iplcevent) { + OnInputLanguageChanging(iplcevent); + } + + /// + /// + /// Processes a command key. Overrides Control.processCmdKey() to provide + /// additional handling of main menu command keys and Mdi accelerators. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (base.ProcessCmdKey(ref msg, keyData)) return true; + + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu != null && curMenu.ProcessCmdKey(ref msg, keyData)) return true; + + // Process MDI accelerator keys. + + bool retValue = false; + + NativeMethods.MSG win32Message = new NativeMethods.MSG(); + win32Message.message = msg.Msg; + win32Message.wParam = msg.WParam; + win32Message.lParam = msg.LParam; + win32Message.hwnd = msg.HWnd; + + if (ctlClient != null && ctlClient.Handle != IntPtr.Zero && + UnsafeNativeMethods.TranslateMDISysAccel(ctlClient.Handle, ref win32Message)) { + + retValue = true; + } + + msg.Msg = win32Message.message; + msg.WParam = win32Message.wParam; + msg.LParam = win32Message.lParam; + msg.HWnd = win32Message.hwnd; + + return retValue; + } + + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the RETURN, and ESCAPE keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + IButtonControl button; + + switch (keyCode) { + case Keys.Return: + button = (IButtonControl)Properties.GetObject(PropDefaultButton); + if (button != null) { + //PerformClick now checks for validationcancelled... + if (button is Control) { + button.PerformClick(); + } + return true; + } + break; + case Keys.Escape: + button = (IButtonControl)Properties.GetObject(PropCancelButton); + if (button != null) { + // In order to keep the behavior in sync with native + // and MFC dialogs, we want to not give the cancel button + // the focus on Escape. If we do, we end up with giving it + // the focus when we reshow the dialog. + // + //if (button is Control) { + // ((Control)button).Focus(); + //} + button.PerformClick(); + return true; + } + break; + } + } + return base.ProcessDialogKey(keyData); + } + + /// + /// + /// + /// Processes a dialog character For a MdiChild. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogChar(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Form.ProcessDialogChar [" + charCode.ToString() + "]"); +#endif + // If we're the top-level form or control, we need to do the mnemonic handling + // + if (this.IsMdiChild && charCode != ' ') { + if (ProcessMnemonic(charCode)) { + return true; + } + + // ContainerControl calls ProcessMnemonic starting from the active MdiChild form (this) + // so let's flag it as processed. + formStateEx[FormStateExMnemonicProcessed] = 1; + try{ + return base.ProcessDialogChar(charCode); + } + finally{ + formStateEx[FormStateExMnemonicProcessed] = 0; + } + } + + // Non-MdiChild form, just pass the call to ContainerControl. + return base.ProcessDialogChar(charCode); + } + + + /// + /// + /// [To be supplied.] + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) { + if (formState[FormStateKeyPreview] != 0 && ProcessKeyEventArgs(ref m)) + return true; + return base.ProcessKeyPreview(ref m); + } + + /// + /// + /// [To be supplied.] + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessTabKey(bool forward) { + if (SelectNextControl(ActiveControl, forward, true, true, true)) + return true; + + // I've added a special case for UserControls because they shouldn't cycle back to the + // beginning if they don't have a parent form, such as when they're on an ActiveXBridge. + if (IsMdiChild || ParentFormInternal == null) { + bool selected = SelectNextControl(null, forward, true, true, false); + + if (selected) { + return true; + } + } + + return false; + } + + + /// + /// + /// Raises the FormClosed event for this form when Application.Exit is called. + /// + internal void RaiseFormClosedOnAppExit() { + if (!Modal) { + /* This is not required because Application.ExitPrivate() loops through all forms in the Application.OpenForms collection + // Fire FormClosed event on all MDI children + if (IsMdiContainer) { + FormClosedEventArgs fce = new FormClosedEventArgs(CloseReason.MdiFormClosing); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnFormClosed(fce); + } + } + } + */ + + // Fire FormClosed event on all the forms that this form owns and are not in the Application.OpenForms collection + // This is to be consistent with what WmClose does. + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + if (ownedFormsCount > 0) { + Form[] ownedForms = this.OwnedForms; + FormClosedEventArgs fce = new FormClosedEventArgs(CloseReason.FormOwnerClosing); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + if (ownedForms[i] != null && !Application.OpenFormsInternal.Contains(ownedForms[i])) { + ownedForms[i].OnFormClosed(fce); + } + } + } + } + OnFormClosed(new FormClosedEventArgs(CloseReason.ApplicationExitCall)); + } + + /// + /// + /// Raises the FormClosing event for this form when Application.Exit is called. + /// Returns e.Cancel returned by the event handler. + /// + internal bool RaiseFormClosingOnAppExit() { + FormClosingEventArgs e = new FormClosingEventArgs(CloseReason.ApplicationExitCall, false); + // e.Cancel = !Validate(true); This would cause a breaking change between v2.0 and v1.0/v1.1 in case validation fails. + if (!Modal) { + /* This is not required because Application.ExitPrivate() loops through all forms in the Application.OpenForms collection + // Fire FormClosing event on all MDI children + if (IsMdiContainer) { + FormClosingEventArgs fce = new FormClosingEventArgs(CloseReason.MdiFormClosing, e.Cancel); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnFormClosing(fce); + if (fce.Cancel) { + e.Cancel = true; + break; + } + } + } + } + */ + + // Fire FormClosing event on all the forms that this form owns and are not in the Application.OpenForms collection + // This is to be consistent with what WmClose does. + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + if (ownedFormsCount > 0) { + Form[] ownedForms = this.OwnedForms; + FormClosingEventArgs fce = new FormClosingEventArgs(CloseReason.FormOwnerClosing, false); + for (int i = ownedFormsCount - 1; i >= 0; i--) { + if (ownedForms[i] != null && !Application.OpenFormsInternal.Contains(ownedForms[i])) { + ownedForms[i].OnFormClosing(fce); + if (fce.Cancel) { + e.Cancel = true; + break; + } + } + } + } + } + OnFormClosing(e); + return e.Cancel; + } + + /// + /// + /// + [ + SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive") + ] + internal override void RecreateHandleCore() { + //Debug.Assert( CanRecreateHandle(), "Recreating handle when form is not ready yet." ); + NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); + FormStartPosition oldStartPosition = FormStartPosition.Manual; + + if (!IsMdiChild && (WindowState == FormWindowState.Minimized || WindowState == FormWindowState.Maximized)) { + wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT)); + UnsafeNativeMethods.GetWindowPlacement(new HandleRef(this, Handle), ref wp); + } + + if (StartPosition != FormStartPosition.Manual) { + oldStartPosition = StartPosition; + // Set the startup postion to manual, to stop the form from + // changing position each time RecreateHandle() is called. + StartPosition = FormStartPosition.Manual; + } + + EnumThreadWindowsCallback etwcb = null; + SafeNativeMethods.EnumThreadWindowsCallback callback = null; + if (IsHandleCreated) { + // First put all the owned windows into a list + etwcb = new EnumThreadWindowsCallback(); + if (etwcb != null) { + callback = new SafeNativeMethods.EnumThreadWindowsCallback(etwcb.Callback); + UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), + new NativeMethods.EnumThreadWindowsCallback(callback), + new HandleRef(this, this.Handle)); + // Reset the owner of the windows in the list + etwcb.ResetOwners(); + } + } + + base.RecreateHandleCore(); + + + if (etwcb != null) { + // Set the owner of the windows in the list back to the new Form's handle + etwcb.SetOwners(new HandleRef(this, this.Handle)); + } + + if (oldStartPosition != FormStartPosition.Manual) { + StartPosition = oldStartPosition; + } + + if (wp.length > 0) { + UnsafeNativeMethods.SetWindowPlacement(new HandleRef(this, Handle), ref wp); + } + + if (callback != null) { + GC.KeepAlive(callback); + } + } + + /// + /// + /// + /// Removes a form from the list of owned forms. Also sets the owner of the + /// removed form to null. + /// + /// + public void RemoveOwnedForm(Form ownedForm) { + if (ownedForm == null) + return; + + if (ownedForm.OwnerInternal != null) { + ownedForm.Owner = null; // NOTE: this will call RemoveOwnedForm again, bypassing if. + return; + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + if (ownedForms != null) { + for (int i = 0; i < ownedFormsCount; i++) { + if (ownedForm.Equals(ownedForms[i])) { + + // clear out the reference. + // + ownedForms[i] = null; + + // compact the array. + // + if (i + 1 < ownedFormsCount) { + Array.Copy(ownedForms, i + 1, ownedForms, i, ownedFormsCount - i - 1); + ownedForms[ownedFormsCount - 1] = null; + } + ownedFormsCount--; + } + } + + Properties.SetInteger(PropOwnedFormsCount, ownedFormsCount); + } + } + + /// + /// Resets the form's icon the the default value. + /// + private void ResetIcon() { + icon = null; + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + formState[FormStateIconSet] = 0; + UpdateWindowIcon(true); + } + + void ResetSecurityTip(bool modalOnly) { + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip != null) { + if ((modalOnly && secTip.Modal) || !modalOnly) { + secTip.Dispose(); + secTip = null; + Properties.SetObject(PropSecurityTip, null); + } + } + } + + /// + /// Resets the TransparencyKey to Color.Empty. + /// + private void ResetTransparencyKey() { + TransparencyKey = Color.Empty; + } + + /// + /// + /// Occurs when the form enters the sizing modal loop + /// + [SRCategory(SR.CatAction), SRDescription(SR.FormOnResizeBeginDescr)] + public event EventHandler ResizeBegin { + add { + Events.AddHandler(EVENT_RESIZEBEGIN, value); + } + remove { + Events.RemoveHandler(EVENT_RESIZEBEGIN, value); + } + } + + /// + /// + /// Occurs when the control exits the sizing modal loop. + /// + [SRCategory(SR.CatAction), SRDescription(SR.FormOnResizeEndDescr)] + public event EventHandler ResizeEnd { + add { + Events.AddHandler(EVENT_RESIZEEND, value); + } + remove { + Events.RemoveHandler(EVENT_RESIZEEND, value); + } + } + + /// + /// This is called when we have just been restored after being + /// minimized. At this point we resume our layout. + /// + private void ResumeLayoutFromMinimize() { + // If we're currently minimized, resume our layout because we are + // about to snap out of it. + if (formState[FormStateWindowState] == (int)FormWindowState.Minimized) { + ResumeLayout(); + } + } + + // If someone set Location or Size while the form was maximized or minimized, + // we had to cache the new value away until after the form was restored to normal size. + // This function is called after WindowState changes, and handles the above logic. + // In the normal case where no one sets Location or Size programmatically, + // Windows does the restoring for us. + // + private void RestoreWindowBoundsIfNecessary() { + if (WindowState == FormWindowState.Normal) { + Size restoredSize = restoredWindowBounds.Size; + if ((restoredWindowBoundsSpecified & BoundsSpecified.Size) != 0) { + restoredSize = SizeFromClientSize(restoredSize.Width, restoredSize.Height); + } + SetBounds(restoredWindowBounds.X, restoredWindowBounds.Y, + formStateEx[FormStateExWindowBoundsWidthIsClientSize]==1 ? restoredSize.Width : restoredWindowBounds.Width, + formStateEx[FormStateExWindowBoundsHeightIsClientSize]==1 ? restoredSize.Height : restoredWindowBounds.Height, + restoredWindowBoundsSpecified); + restoredWindowBoundsSpecified = 0; + restoredWindowBounds = new Rectangle(-1, -1, -1, -1); + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 0; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 0; + } + } + + void RestrictedProcessNcActivate() { + Debug.Assert(IsRestrictedWindow, "This should only be called for restricted windows"); + + // Ignore if tearing down... + // + if (IsDisposed || Disposing) { + return; + } + + // Note that this.Handle does not get called when the handle hasn't been created yet (See VSWhidbey 264275 & 264291) + // + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip == null) { + if (IsHandleCreated && UnsafeNativeMethods.GetForegroundWindow() == this.Handle) { + secTip = new SecurityToolTip(this); + Properties.SetObject(PropSecurityTip, secTip); + } + } + else if (!IsHandleCreated || UnsafeNativeMethods.GetForegroundWindow() != this.Handle) + { + secTip.Pop(false /*noLongerFirst*/); + } + else + { + secTip.Show(); + } + } + + /// + /// Decrements updateMenuHandleSuspendCount. If updateMenuHandleSuspendCount + /// becomes zero and updateMenuHandlesDeferred is true, updateMenuHandles + /// is called. + /// + private void ResumeUpdateMenuHandles() { + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + if (suspendCount <= 0) { + throw new InvalidOperationException(SR.GetString(SR.TooManyResumeUpdateMenuHandles)); + } + + formStateEx[FormStateExUpdateMenuHandlesSuspendCount] = --suspendCount; + if (suspendCount == 0 && formStateEx[FormStateExUpdateMenuHandlesDeferred] != 0) { + UpdateMenuHandles(); + } + } + + /// + /// + /// Selects this form, and optionally selects the next/previous control. + /// + protected override void Select(bool directed, bool forward) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + SelectInternal(directed, forward); + } + + /// + /// Selects this form, and optionally selects the next/previous control. + /// Does the actual work without the security check. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + private void SelectInternal(bool directed, bool forward) { + // SelectNextControl & set_ActiveControl below demand a ModifyFocus permission, let's revert it. + // Reviewed : This method is to be called by Form only so asserting here is safe. + // + IntSecurity.ModifyFocus.Assert(); + + if (directed) { + SelectNextControl(null, forward, true, true, false); + } + + if (TopLevel) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, Handle)); + } + else if (IsMdiChild) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(MdiParentInternal, MdiParentInternal.Handle)); + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + } + else { + Form form = ParentFormInternal; + if (form != null) form.ActiveControl = this; + } + + } + + /// + /// + /// Base function that performs scaling of the form. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float x, float y) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ScaleCore(" + x + ", " + y + ")"); + SuspendLayout(); + try { + if (WindowState == FormWindowState.Normal) { + //Get size values in advance to prevent one change from affecting another. + Size clientSize = ClientSize; + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + if (!MinimumSize.IsEmpty) { + MinimumSize = ScaleSize(minSize, x, y); + } + if (!MaximumSize.IsEmpty) { + MaximumSize = ScaleSize(maxSize, x, y); + } + ClientSize = ScaleSize(clientSize, x, y); + } + + ScaleDockPadding(x, y); + + foreach(Control control in Controls) { + if (control != null) { +#pragma warning disable 618 + control.Scale(x, y); +#pragma warning restore 618 + } + } + } + finally { + ResumeLayout(); + } + } + + /// + /// + /// For overrides this to calculate scaled bounds based on the restored rect + /// if it is maximized or minimized. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + // If we're maximized or minimized, scale using the restored bounds, not + // the real bounds. + if (WindowState != FormWindowState.Normal) { + bounds = RestoreBounds; + } + return base.GetScaledBounds(bounds, factor, specified); + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + formStateEx[FormStateExInScale] = 1; + try { + + // don't scale the location of MDI child forms + if (MdiParentInternal != null) { + specified &= ~BoundsSpecified.Location; + } + + base.ScaleControl(factor, specified); + } + finally { + formStateEx[FormStateExInScale] = 0; + } + } + + + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + if (WindowState != FormWindowState.Normal) { + // See RestoreWindowBoundsIfNecessary for an explanation of this + // VSWhidbey 503385. Only restore position when x,y is not -1,-1 + if (x != -1 || y != -1) + { + restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.X | BoundsSpecified.Y)); + } + restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.Width | BoundsSpecified.Height)); + + if ((specified & BoundsSpecified.X) != 0) + restoredWindowBounds.X = x; + if ((specified & BoundsSpecified.Y) != 0) + restoredWindowBounds.Y = y; + if ((specified & BoundsSpecified.Width) != 0) { + restoredWindowBounds.Width = width; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 0; + } + if ((specified & BoundsSpecified.Height) != 0) { + restoredWindowBounds.Height = height; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 0; + } + } + + //Update RestoreBounds + if ((specified & BoundsSpecified.X) != 0) + restoreBounds.X = x; + if ((specified & BoundsSpecified.Y) != 0) + restoreBounds.Y = y; + if ((specified & BoundsSpecified.Width) != 0 || restoreBounds.Width == -1) + restoreBounds.Width = width; + if ((specified & BoundsSpecified.Height) != 0 || restoreBounds.Height == -1) + restoreBounds.Height = height; + + // Enforce maximum size... + // + if (WindowState == FormWindowState.Normal + && (this.Height != height || this.Width != width)) { + + Size max = SystemInformation.MaxWindowTrackSize; + if (height > max.Height) { + height = max.Height; + } + if (width > max.Width) { + width = max.Width; + } + } + + // Only enforce the minimum size if the form has a border and is a top + // level form. + // + FormBorderStyle borderStyle = FormBorderStyle; + if (borderStyle != FormBorderStyle.None + && borderStyle != FormBorderStyle.FixedToolWindow + && borderStyle != FormBorderStyle.SizableToolWindow + && ParentInternal == null) { + + Size min = SystemInformation.MinWindowTrackSize; + if (height < min.Height) { + height = min.Height; + } + if (width < min.Width) { + width = min.Width; + } + } + + if (IsRestrictedWindow) { + // Check to ensure that the title bar, and all corners of the window, are visible on a monitor + // + + Rectangle adjustedBounds = ApplyBoundsConstraints(x,y,width,height); + if (adjustedBounds != new Rectangle(x,y,width,height)) { + + // SECREVIEW VSWhidbey 430541 - the base will call ApplyBoundsConstraints again, so + // we're still OK (even though we have security code in a virtual function). + // + // Keeping this here for compat reasons - we want to manually call base here with BoundsSpecified.All + // so we ---- over the specified bounds. + base.SetBoundsCore(adjustedBounds.X, adjustedBounds.Y, adjustedBounds.Width, adjustedBounds.Height, BoundsSpecified.All); + return; + } + } + + base.SetBoundsCore(x, y, width, height, specified); + } + + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + // apply min/max size constraints + Rectangle adjustedBounds = base.ApplyBoundsConstraints(suggestedX, suggestedY, proposedWidth, proposedHeight); + // run through size restrictions in Internet. + if (IsRestrictedWindow) { + // Check to ensure that the title bar, and all corners of the window, are visible on a monitor + // + + Screen[] screens = Screen.AllScreens; + bool topLeft = false; + bool topRight = false; + bool bottomLeft = false; + bool bottomRight = false; + + for (int i=0; i + /// Sets the defaultButton for the form. The defaultButton is "clicked" when + /// the user presses Enter. + /// + private void SetDefaultButton(IButtonControl button) { + IButtonControl defaultButton = (IButtonControl)Properties.GetObject(PropDefaultButton); + + if (defaultButton != button) { + if (defaultButton != null) defaultButton.NotifyDefault(false); + Properties.SetObject(PropDefaultButton, button); + if (button != null) button.NotifyDefault(true); + } + } + + + /// + /// + /// Sets the clientSize of the form. This will adjust the bounds of the form + /// to make the clientSize the requested size. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetClientSizeCore(int x, int y) { + bool hadHScroll = HScroll, hadVScroll = VScroll; + base.SetClientSizeCore(x, y); + + if (IsHandleCreated) { + // Adjust for the scrollbars, if they were introduced by + // the call to base.SetClientSizeCore + if (VScroll != hadVScroll) { + if (VScroll) x += SystemInformation.VerticalScrollBarWidth; + } + if (HScroll != hadHScroll) { + if (HScroll) y += SystemInformation.HorizontalScrollBarHeight; + } + if (x != ClientSize.Width || y != ClientSize.Height) { + base.SetClientSizeCore(x, y); + } + } + formState[FormStateSetClientSize] = 1; + } + + /// + /// + /// + /// Sets the bounds of the form in desktop coordinates. + /// + public void SetDesktopBounds(int x, int y, int width, int height) { + Rectangle workingArea = SystemInformation.WorkingArea; + SetBounds(x + workingArea.X, y + workingArea.Y, width, height, BoundsSpecified.All); + } + + /// + /// + /// Sets the location of the form in desktop coordinates. + /// + public void SetDesktopLocation(int x, int y) { + Rectangle workingArea = SystemInformation.WorkingArea; + Location = new Point(workingArea.X + x, workingArea.Y + y); + } + + + /// + /// + /// Makes the control display by setting the visible property to true + /// + public void Show(IWin32Window owner) { + if (owner == this) { + throw new InvalidOperationException(SR.GetString(SR.OwnsSelfOrOwner, + "Show")); + } + else if (Visible) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnVisible, + "Show")); + } + else if (!Enabled) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnDisabled, + "Show")); + } + else if (!TopLevel) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnNonTopLevel, + "Show")); + } + else if (!SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + else if ( (owner != null) && ((int)UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, Control.GetSafeHandle(owner)), NativeMethods.GWL_EXSTYLE) + & NativeMethods.WS_EX_TOPMOST) == 0 ) { // It's not the top-most window + if (owner is Control) { + owner = ((Control)owner).TopLevelControlInternal; + } + } + IntPtr hWndActive = UnsafeNativeMethods.GetActiveWindow(); + IntPtr hWndOwner = owner == null ? hWndActive : Control.GetSafeHandle(owner); + IntPtr hWndOldOwner = IntPtr.Zero; + Properties.SetObject(PropDialogOwner, owner); + Form oldOwner = OwnerInternal; + if (owner is Form && owner != oldOwner) { + Owner = (Form)owner; + } + if (hWndOwner != IntPtr.Zero && hWndOwner != Handle) { + // Catch the case of a window trying to own its owner + if (UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, hWndOwner), NativeMethods.GWL_HWNDPARENT) == Handle) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "show"), "owner"); + } + + // Set the new owner. + hWndOldOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(owner, hWndOwner)); + + } + Visible = true; + } + + /// + /// + /// Displays this form as a modal dialog box with no owner window. + /// + public DialogResult ShowDialog() { + return ShowDialog(null); + } + + /// + /// + /// Shows this form as a modal dialog with the specified owner. + /// + public DialogResult ShowDialog(IWin32Window owner) { + if (owner == this) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "showDialog"), "owner"); + } + else if (Visible) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnVisible, + "showDialog")); + } + else if (!Enabled) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnDisabled, + "showDialog")); + } + else if (!TopLevel) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnNonTopLevel, + "showDialog")); + } + else if (Modal) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnModal, + "showDialog")); + } + else if (!SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + else if ( (owner != null) && ((int)UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, Control.GetSafeHandle(owner)), NativeMethods.GWL_EXSTYLE) + & NativeMethods.WS_EX_TOPMOST) == 0 ) { // It's not the top-most window + if (owner is Control) { + owner = ((Control)owner).TopLevelControlInternal; + } + } + + this.CalledOnLoad = false; + this.CalledMakeVisible = false; + + // for modal dialogs make sure we reset close reason. + this.CloseReason = CloseReason.None; + + IntPtr hWndCapture = UnsafeNativeMethods.GetCapture(); + if (hWndCapture != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(new HandleRef(null, hWndCapture), NativeMethods.WM_CANCELMODE, IntPtr.Zero, IntPtr.Zero); + SafeNativeMethods.ReleaseCapture(); + } + IntPtr hWndActive = UnsafeNativeMethods.GetActiveWindow(); + IntPtr hWndOwner = owner == null ? hWndActive : Control.GetSafeHandle(owner); + IntPtr hWndOldOwner = IntPtr.Zero; + Properties.SetObject(PropDialogOwner, owner); + + Form oldOwner = OwnerInternal; + + if (owner is Form && owner != oldOwner) { + Owner = (Form)owner; + } + + try { + SetState(STATE_MODAL, true); + + // ASURT 102728 + // It's possible that while in the process of creating the control, + // (i.e. inside the CreateControl() call) the dialog can be closed. + // e.g. A user might call Close() inside the OnLoad() event. + // Calling Close() will set the DialogResult to some value, so that + // we'll know to terminate the RunDialog loop immediately. + // Thus we must initialize the DialogResult *before* the call + // to CreateControl(). + // + dialogResult = DialogResult.None; + + // V#36617 - if "this" is an MDI parent then the window gets activated, + // causing GetActiveWindow to return "this.handle"... to prevent setting + // the owner of this to this, we must create the control AFTER calling + // GetActiveWindow. + // + CreateControl(); + + if (hWndOwner != IntPtr.Zero && hWndOwner != Handle) { + // Catch the case of a window trying to own its owner + if (UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, hWndOwner), NativeMethods.GWL_HWNDPARENT) == Handle) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "showDialog"), "owner"); + } + + // Set the new owner. + hWndOldOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(owner, hWndOwner)); + } + + try { + // If the DialogResult was already set, then there's + // no need to actually display the dialog. + // + if (dialogResult == DialogResult.None) + { + // Application.RunDialog sets this dialog to be visible. + Application.RunDialog(this); + } + } + finally { + // Call SetActiveWindow before setting Visible = false. + // + + if (!UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndActive))) hWndActive = hWndOwner; + if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndActive)) && SafeNativeMethods.IsWindowVisible(new HandleRef(null, hWndActive))) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, hWndActive)); + } + else if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndOwner)) && SafeNativeMethods.IsWindowVisible(new HandleRef(null, hWndOwner))){ + UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, hWndOwner)); + } + + SetVisibleCore(false); + if (IsHandleCreated) { + + // VSWhidbey 94917: If this is a dialog opened from an MDI Container, then invalidate + // so that child windows will be properly updated. + if (this.OwnerInternal != null && + this.OwnerInternal.IsMdiContainer) { + this.OwnerInternal.Invalidate(true); + this.OwnerInternal.Update(); + } + + // VSWhidbey 430476 - Everett/RTM used to wrap this in an assert for AWP. + DestroyHandle(); + } + SetState(STATE_MODAL, false); + } + } + finally { + Owner = oldOwner; + Properties.SetObject(PropDialogOwner, null); + } + return DialogResult; + } + + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeAutoScaleBaseSize() { + return formState[FormStateAutoScaling] != 0; + } + + private bool ShouldSerializeClientSize() { + return true; + } + + /// + /// Indicates whether the property should be persisted. + /// + private bool ShouldSerializeIcon() { + return formState[FormStateIconSet] == 1; + } + + /// + /// Determines if the Location property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeLocation() { + return Left != 0 || Top != 0; + } + + /// + /// + /// + /// Indicates whether the property should be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeSize() { + return false; + } + + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializeTransparencyKey() { + return !TransparencyKey.Equals(Color.Empty); + } + + /// + /// This is called when we are about to become minimized. Laying out + /// while minimized can be a problem because the physical dimensions + /// of the window are very small. So, we simply suspend. + /// + private void SuspendLayoutForMinimize() { + // If we're not currently minimized, suspend our layout because we are + // about to become minimized + if (formState[FormStateWindowState] != (int)FormWindowState.Minimized) { + SuspendLayout(); + } + } + + /// + /// Increments updateMenuHandleSuspendCount. + /// + private void SuspendUpdateMenuHandles() { + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + formStateEx[FormStateExUpdateMenuHandlesSuspendCount] = ++suspendCount; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Text: " + Text; + } + + /// + /// Updates the autoscalebasesize based on the current font. + /// + /// + private void UpdateAutoScaleBaseSize() { + autoScaleBaseSize = Size.Empty; + } + + private void UpdateRenderSizeGrip() { + int current = formState[FormStateRenderSizeGrip]; + switch (FormBorderStyle) { + case FormBorderStyle.None: + case FormBorderStyle.FixedSingle: + case FormBorderStyle.Fixed3D: + case FormBorderStyle.FixedDialog: + case FormBorderStyle.FixedToolWindow: + formState[FormStateRenderSizeGrip] = 0; + break; + case FormBorderStyle.Sizable: + case FormBorderStyle.SizableToolWindow: + switch (SizeGripStyle) { + case SizeGripStyle.Show: + formState[FormStateRenderSizeGrip] = 1; + break; + case SizeGripStyle.Hide: + formState[FormStateRenderSizeGrip] = 0; + break; + case SizeGripStyle.Auto: + if (GetState(STATE_MODAL)) { + formState[FormStateRenderSizeGrip] = 1; + } + else { + formState[FormStateRenderSizeGrip] = 0; + } + break; + } + break; + } + + if (formState[FormStateRenderSizeGrip] != current) { + Invalidate(); + } + } + + /// + /// + /// Updates the default button based on current selection, and the + /// acceptButton property. + /// + /// + protected override void UpdateDefaultButton() { + ContainerControl cc = this; + + while (cc.ActiveControl is ContainerControl) + { + cc = cc.ActiveControl as ContainerControl; + Debug.Assert(cc != null); + + if (cc is Form) + { + // VSWhidbey#291004: Don't allow a parent form to get its default button from a child form, + // otherwise the two forms will 'compete' for the Enter key and produce unpredictable results. + // This is aimed primarily at fixing the behavior of MDI container forms. + cc = this; + break; + } + } + + if (cc.ActiveControl is IButtonControl) + { + SetDefaultButton((IButtonControl) cc.ActiveControl); + } + else + { + SetDefaultButton(AcceptButton); + } + } + + /// + /// Updates the underlying hWnd with the correct parent/owner of the form. + /// + /// + private void UpdateHandleWithOwner() { + if (IsHandleCreated && TopLevel) { + HandleRef ownerHwnd = NativeMethods.NullHandleRef; + + Form owner = (Form)Properties.GetObject(PropOwner); + + if (owner != null) { + ownerHwnd = new HandleRef(owner, owner.Handle); + } + else { + if (!ShowInTaskbar) { + ownerHwnd = TaskbarOwner; + } + } + + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, ownerHwnd); + } + } + + /// + /// Updates the layered window attributes if the control + /// is in layered mode. + /// + private void UpdateLayered() { + if ((formState[FormStateLayered] != 0) && IsHandleCreated && TopLevel && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + bool result; + + Color transparencyKey = TransparencyKey; + + if (transparencyKey.IsEmpty) { + + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), 0, OpacityAsByte, NativeMethods.LWA_ALPHA); + } + else if (OpacityAsByte == 255) { + // Windows doesn't do so well setting colorkey and alpha, so avoid it if we can + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), ColorTranslator.ToWin32(transparencyKey), 0, NativeMethods.LWA_COLORKEY); + } + else { + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), ColorTranslator.ToWin32(transparencyKey), + OpacityAsByte, NativeMethods.LWA_ALPHA | NativeMethods.LWA_COLORKEY); + } + + if (!result) { + throw new Win32Exception(); + } + } + } + + /// + private void UpdateMenuHandles() { + Form form; + + // Forget the current menu. + if (Properties.GetObject(PropCurMenu) != null) { + Properties.SetObject(PropCurMenu, null); + } + + if (IsHandleCreated) { + if (!TopLevel) { + UpdateMenuHandles(null, true); + } + else { + form = ActiveMdiChildInternal; + if (form != null) { + UpdateMenuHandles(form.MergedMenuPrivate, true); + } + else { + UpdateMenuHandles(Menu, true); + } + } + } + } + + private void UpdateMenuHandles(MainMenu menu, bool forceRedraw) { + Debug.Assert(IsHandleCreated, "shouldn't call when handle == 0"); + + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + if (suspendCount > 0 && menu != null) { + formStateEx[FormStateExUpdateMenuHandlesDeferred] = 1; + return; + } + + MainMenu curMenu = menu; + if (curMenu != null) { + curMenu.form = this; + } + + if (curMenu != null || Properties.ContainsObject(PropCurMenu)) { + Properties.SetObject(PropCurMenu, curMenu); + } + + if (ctlClient == null || !ctlClient.IsHandleCreated) { + if (menu != null) { + UnsafeNativeMethods.SetMenu(new HandleRef(this, Handle), new HandleRef(menu, menu.Handle)); + } + else { + UnsafeNativeMethods.SetMenu(new HandleRef(this, Handle), NativeMethods.NullHandleRef); + } + } + else { + Debug.Assert( IsMdiContainer, "Not an MDI container!" ); + // when both MainMenuStrip and Menu are set, we honor the win32 menu over + // the MainMenuStrip as the place to store the system menu controls for the maximized MDI child. + + MenuStrip mainMenuStrip = MainMenuStrip; + if( mainMenuStrip == null || menu!=null){ // We are dealing with a Win32 Menu; MenuStrip doesn't have control buttons. + + // We have a MainMenu and we're going to use it + + // VSWhidbey 202747: We need to set the "dummy" menu even when a menu is being removed + // (set to null) so that duplicate control buttons are not placed on the menu bar when + // an ole menu is being removed. + // Make MDI forget the mdi item position. + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + + if (dummyMenu == null) { + dummyMenu = new MainMenu(); + dummyMenu.ownerForm = this; + Properties.SetObject(PropDummyMenu, dummyMenu); + } + UnsafeNativeMethods.SendMessage(new HandleRef(ctlClient, ctlClient.Handle), NativeMethods.WM_MDISETMENU, dummyMenu.Handle, IntPtr.Zero); + + if (menu != null) { + + // Microsoft, 5/2/1998 - don't use Win32 native Mdi lists... + // + UnsafeNativeMethods.SendMessage(new HandleRef(ctlClient, ctlClient.Handle), NativeMethods.WM_MDISETMENU, menu.Handle, IntPtr.Zero); + } + } + + // VSWhidbey#93544 (New fix: Only destroy Win32 Menu if using a MenuStrip) + if( menu == null && mainMenuStrip != null ){ // If MainMenuStrip, we need to remove any Win32 Menu to make room for it. + IntPtr hMenu = UnsafeNativeMethods.GetMenu(new HandleRef(this, this.Handle)); + if (hMenu != IntPtr.Zero) { + + // We had a MainMenu and now we're switching over to MainMenuStrip + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Remove the current menu. + UnsafeNativeMethods.SetMenu(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef); + + // because we have messed with the child's system menu by shoving in our own dummy menu, + // once we clear the main menu we're in trouble - this eats the close, minimize, maximize gadgets + // of the child form. (See WM_MDISETMENU in MSDN) + Form activeMdiChild = this.ActiveMdiChildInternal; + if (activeMdiChild != null && activeMdiChild.WindowState == FormWindowState.Maximized) { + activeMdiChild.RecreateHandle(); + } + + // VSWhidbey 202747: Since we're removing a menu but we possibly had a menu previously, + // we need to clear the cached size so that new size calculations will be performed correctly. + CommonProperties.xClearPreferredSizeCache(this); + } + } + } + if (forceRedraw) { + SafeNativeMethods.DrawMenuBar(new HandleRef(this, Handle)); + } + formStateEx[FormStateExUpdateMenuHandlesDeferred] = 0; + } + + // Call this function instead of UpdateStyles() when the form's client-size must + // be preserved e.g. when changing the border style. + // + internal void UpdateFormStyles() { + Size previousClientSize = ClientSize; + base.UpdateStyles(); + if (!ClientSize.Equals(previousClientSize)) { + ClientSize = previousClientSize; + } + } + + private static Type FindClosestStockType(Type type) { + Type[] stockTypes = new Type[] { typeof (MenuStrip) }; // as opposed to what we had before... see VSWhidbey 516929 + // simply add other types here from most specific to most generic if we want to merge other types of toolstrips... + foreach(Type t in stockTypes) { + if(t.IsAssignableFrom(type)) { + return t; + } + } + return null; + } + + /// ToolStrip MDI Merging support + private void UpdateToolStrip() { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "\r\n============"); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: starting merge operation"); + + // try to merge each one of the MDI Child toolstrip with the first toolstrip + // in the parent form that has the same type NOTE: THESE LISTS ARE ORDERED (See ToolstripManager) + ToolStrip thisToolstrip = MainMenuStrip; + ArrayList childrenToolStrips = ToolStripManager.FindMergeableToolStrips(ActiveMdiChildInternal); + + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: found "+ thisToolStrips.Count +" mergeable toolstrip in this"); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: found "+childrenToolStrips.Count+" mergeable toolstrip in children"); + + // revert any previous merge + if(thisToolstrip != null) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: reverting merge in " + destinationToolStrip.Name); + ToolStripManager.RevertMerge(thisToolstrip); + } + + // if someone has a MdiWindowListItem specified we should merge in the + // names of all the MDI child forms. + UpdateMdiWindowListStrip(); + + if(ActiveMdiChildInternal != null) { + + // do the new merging + foreach(ToolStrip sourceToolStrip in childrenToolStrips) { + Type closestMatchingSourceType = FindClosestStockType(sourceToolStrip.GetType()); + if(thisToolstrip != null) { + Type closestMatchingTargetType = FindClosestStockType(thisToolstrip.GetType()); + if (closestMatchingTargetType != null && closestMatchingSourceType != null && + closestMatchingSourceType == closestMatchingTargetType && + thisToolstrip.GetType().IsAssignableFrom(sourceToolStrip.GetType())) { + ToolStripManager.Merge(sourceToolStrip, thisToolstrip); + break; + } + } + } + } + + + // add in the control gadgets for the mdi child form to the first menu strip + Form activeMdiForm = ActiveMdiChildInternal; + UpdateMdiControlStrip(activeMdiForm != null && activeMdiForm.IsMaximized); + } + + private void UpdateMdiControlStrip(bool maximized) { + + if (formStateEx[FormStateExInUpdateMdiControlStrip] != 0) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Detected re-entrant call to UpdateMdiControlStrip, returning."); + return; + } + + // we dont want to be redundantly called as we could merge in two control menus. + formStateEx[FormStateExInUpdateMdiControlStrip] = 1; + + try { + MdiControlStrip mdiControlStrip = this.MdiControlStrip; + + if (MdiControlStrip != null) { + if (mdiControlStrip.MergedMenu != null) { +#if DEBUG + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Calling RevertMerge on MDIControl strip."); + int numWindowListItems = 0; + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null) { + numWindowListItems = MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count; + } +#endif + + ToolStripManager.RevertMergeInternal(mdiControlStrip.MergedMenu,mdiControlStrip,/*revertMDIStuff*/true); + +#if DEBUG + // double check that RevertMerge doesnt accidentally revert more than it should. + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null) { + Debug.Assert(numWindowListItems == MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count, "Calling RevertMerge modified the mdiwindowlistitem"); + } +#endif + } + mdiControlStrip.MergedMenu = null; + mdiControlStrip.Dispose(); + MdiControlStrip = null; + } + + if (ActiveMdiChildInternal != null && maximized) { + if (ActiveMdiChildInternal.ControlBox) { + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Detected ControlBox on ActiveMDI child, adding in MDIControlStrip."); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && this.Menu != null, "UpdateMdiControlStrip: Bailing as we detect there's already an HMenu to do this for us."); + + // determine if we need to add control gadgets into the MenuStrip + if (this.Menu == null) { + // double check GetMenu incase someone is using interop + IntPtr hMenu = UnsafeNativeMethods.GetMenu(new HandleRef(this, this.Handle)); + if (hMenu == IntPtr.Zero) { + MenuStrip sourceMenuStrip = ToolStripManager.GetMainMenuStrip(this); + if (sourceMenuStrip != null) { + this.MdiControlStrip = new MdiControlStrip(ActiveMdiChildInternal); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: built up an MDI control strip for " + ActiveMdiChildInternal.Text + " with " + MdiControlStrip.Items.Count.ToString(CultureInfo.InvariantCulture) + " items."); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: merging MDI control strip into source menustrip - items before: " + sourceMenuStrip.Items.Count.ToString(CultureInfo.InvariantCulture)); + ToolStripManager.Merge(this.MdiControlStrip, sourceMenuStrip); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: merging MDI control strip into source menustrip - items after: " + sourceMenuStrip.Items.Count.ToString(CultureInfo.InvariantCulture)); + this.MdiControlStrip.MergedMenu = sourceMenuStrip; + } + } + } + } + } + } + finally { + formStateEx[FormStateExInUpdateMdiControlStrip] = 0; + } + } + + internal void UpdateMdiWindowListStrip() { + if (IsMdiContainer) { + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: Calling RevertMerge on MDIWindowList strip."); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null, "UpdateMdiWindowListStrip: MdiWindowListItem dropdown item count before: " + MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count.ToString()); + ToolStripManager.RevertMergeInternal(MdiWindowListStrip.MergedMenu,MdiWindowListStrip,/*revertMdiStuff*/true); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null, "UpdateMdiWindowListStrip: MdiWindowListItem dropdown item count after: " + MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count.ToString()); + } + + MenuStrip sourceMenuStrip = ToolStripManager.GetMainMenuStrip(this); + if (sourceMenuStrip != null && sourceMenuStrip.MdiWindowListItem != null) { + if (MdiWindowListStrip == null) { + MdiWindowListStrip = new MdiWindowListStrip(); + } + int nSubItems = sourceMenuStrip.MdiWindowListItem.DropDownItems.Count; + bool shouldIncludeSeparator = (nSubItems > 0 && + !(sourceMenuStrip.MdiWindowListItem.DropDownItems[nSubItems-1] is ToolStripSeparator)); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: Calling populate items."); + MdiWindowListStrip.PopulateItems(this, sourceMenuStrip.MdiWindowListItem, shouldIncludeSeparator); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: mdiwindowlist dd item count before: " + sourceMenuStrip.MdiWindowListItem.DropDownItems.Count.ToString()); + ToolStripManager.Merge(MdiWindowListStrip, sourceMenuStrip); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: mdiwindowlist dd item count after: " + sourceMenuStrip.MdiWindowListItem.DropDownItems.Count.ToString()); + MdiWindowListStrip.MergedMenu = sourceMenuStrip; + } + + } + } + + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResizeBegin(EventArgs e) + { + if (CanRaiseEvents) + { + EventHandler handler = (EventHandler)Events[EVENT_RESIZEBEGIN]; + if (handler != null) handler(this, e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResizeEnd(EventArgs e) + { + if (CanRaiseEvents) + { + EventHandler handler = (EventHandler)Events[EVENT_RESIZEEND]; + if (handler != null) handler(this, e); + } + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnStyleChanged(EventArgs e) { + base.OnStyleChanged(e); + AdjustSystemMenu(); + } + + /// + /// Updates the window icon. + /// + /// + private void UpdateWindowIcon(bool redrawFrame) { + if (IsHandleCreated) { + Icon icon; + + // Preserve Win32 behavior by keeping the icon we set NULL if + // the user hasn't specified an icon and we are a dialog frame. + // + if ((FormBorderStyle == FormBorderStyle.FixedDialog && formState[FormStateIconSet] == 0 && !IsRestrictedWindow) || !ShowIcon) { + icon = null; + } + else { + icon = Icon; + } + + if (icon != null) { + if (smallIcon == null) { + try { + smallIcon = new Icon(icon, SystemInformation.SmallIconSize); + } + catch { + } + } + + if (smallIcon != null) { + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_SMALL,smallIcon.Handle); + } + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_BIG,icon.Handle); + } + else { + + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_SMALL,0); + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_BIG,0); + } + + if (redrawFrame) { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); + } + } + } + + /// + /// Updated the window state from the handle, if created. + /// + /// + // + // This function is called from all over the place, including my personal favorite, + // WM_ERASEBKGRND. Seems that's one of the first messages we get when a user clicks the min/max + // button, even before WM_WINDOWPOSCHANGED. + private void UpdateWindowState() { + if (IsHandleCreated) { + FormWindowState oldState = WindowState; + NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); + wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT)); + UnsafeNativeMethods.GetWindowPlacement(new HandleRef(this, Handle), ref wp); + + switch (wp.showCmd) { + case NativeMethods.SW_NORMAL: + case NativeMethods.SW_RESTORE: + case NativeMethods.SW_SHOW: + case NativeMethods.SW_SHOWNA: + case NativeMethods.SW_SHOWNOACTIVATE: + if (formState[FormStateWindowState] != (int)FormWindowState.Normal) { + formState[FormStateWindowState] = (int)FormWindowState.Normal; + } + break; + case NativeMethods.SW_SHOWMAXIMIZED: + if (formState[FormStateMdiChildMax] == 0) { + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + } + break; + case NativeMethods.SW_SHOWMINIMIZED: + case NativeMethods.SW_MINIMIZE: + case NativeMethods.SW_SHOWMINNOACTIVE: + if (formState[FormStateMdiChildMax] == 0) { + formState[FormStateWindowState] = (int)FormWindowState.Minimized; + } + break; + case NativeMethods.SW_HIDE: + default: + break; + } + + // If we used to be normal and we just became minimized or maximized, + // stash off our current bounds so we can properly restore. + // + if (oldState == FormWindowState.Normal && WindowState != FormWindowState.Normal) { + + if (WindowState == FormWindowState.Minimized) { + SuspendLayoutForMinimize(); + } + + restoredWindowBounds.Size = ClientSize; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 1; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 1; + restoredWindowBoundsSpecified = BoundsSpecified.Size; + restoredWindowBounds.Location = Location; + restoredWindowBoundsSpecified |= BoundsSpecified.Location; + + //stash off restoreBounds As well... + restoreBounds.Size = Size; + restoreBounds.Location = Location; + } + + // If we just became normal or maximized resume + if (oldState == FormWindowState.Minimized && WindowState != FormWindowState.Minimized) { + ResumeLayoutFromMinimize(); + } + + switch (WindowState) { + case FormWindowState.Normal: + SetState(STATE_SIZELOCKEDBYOS, false); + break; + case FormWindowState.Maximized: + case FormWindowState.Minimized: + SetState(STATE_SIZELOCKEDBYOS, true); + break; + } + + if (oldState != WindowState) { + AdjustSystemMenu(); + } + } + } + + /// + /// + /// Validates all selectable child controls in the container, including descendants. This is + /// equivalent to calling ValidateChildren(ValidationConstraints.Selectable). See + /// for details of exactly which child controls will be validated. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren() { + return base.ValidateChildren(); + } + + /// + /// + /// Validates all the child controls in the container. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren(ValidationConstraints validationConstraints) { + return base.ValidateChildren(validationConstraints); + } + + /// + /// WM_ACTIVATE handler + /// + /// + private void WmActivate(ref Message m) { + Application.FormActivated(this.Modal, true); // inform MsoComponentManager we're active + Active = NativeMethods.Util.LOWORD(m.WParam) != NativeMethods.WA_INACTIVE; + Application.FormActivated(this.Modal, Active); // inform MsoComponentManager we're active + } + + /// + /// WM_ENTERSIZEMOVE handler, so that user can hook up OnResizeBegin event. + /// + /// + private void WmEnterSizeMove(ref Message m) { + formStateEx[FormStateExInModalSizingLoop] = 1; + OnResizeBegin(EventArgs.Empty); + } + + /// + /// WM_EXITSIZEMOVE handler, so that user can hook up OnResizeEnd event. + /// + /// + private void WmExitSizeMove(ref Message m) { + formStateEx[FormStateExInModalSizingLoop] = 0; + OnResizeEnd(EventArgs.Empty); + } + + /// + /// WM_CREATE handler + /// + /// + private void WmCreate(ref Message m) { + base.WndProc(ref m); + NativeMethods.STARTUPINFO_I si = new NativeMethods.STARTUPINFO_I(); + UnsafeNativeMethods.GetStartupInfo(si); + + // If we've been created from explorer, it may + // force us to show up normal. Force our current window state to + // the specified state, unless it's _specified_ max or min + if (TopLevel && (si.dwFlags & NativeMethods.STARTF_USESHOWWINDOW) != 0) { + switch (si.wShowWindow) { + case NativeMethods.SW_MAXIMIZE: + WindowState = FormWindowState.Maximized; + break; + case NativeMethods.SW_MINIMIZE: + WindowState = FormWindowState.Minimized; + break; + } + } + } + + /// + /// WM_CLOSE, WM_QUERYENDSESSION, and WM_ENDSESSION handler + /// + /// + private void WmClose(ref Message m) { + FormClosingEventArgs e = new FormClosingEventArgs(CloseReason, false); + + // Pass 1 (WM_CLOSE & WM_QUERYENDSESSION)... Closing + // + if (m.Msg != NativeMethods.WM_ENDSESSION) { + if (Modal) { + if (dialogResult == DialogResult.None) { + dialogResult = DialogResult.Cancel; + } + CalledClosing = false; + + // if this comes back false, someone canceled the close. we want + // to call this here so that we can get the cancel event properly, + // and if this is a WM_QUERYENDSESSION, appriopriately set the result + // based on this call. + // + // NOTE: We should also check !Validate(true) below too in the modal case, + // but we cannot, because we didn't to this in Everett (bug), and doing so + // now would introduce a breaking change. User can always validate in the + // FormClosing event if they really need to. :-( + // + e.Cancel = !CheckCloseDialog(true); + } + else { + e.Cancel = !Validate(true); + + // Call OnClosing/OnFormClosing on all MDI children + if (IsMdiContainer) { + FormClosingEventArgs fe = new FormClosingEventArgs(CloseReason.MdiFormClosing, e.Cancel); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnClosing(fe); + mdiChild.OnFormClosing(fe); + if (fe.Cancel) { + // VSWhidbey 173789: Set the Cancel property for the MDI Container's + // FormClosingEventArgs, as well, so that closing the MDI container + // will be cancelled. + e.Cancel = true; + break; + } + } + } + } + + //Always fire OnClosing irrespectively of the validation result + //Pass the validation result into the EventArgs... + + // VSWhidbey#278060. Call OnClosing/OnFormClosing on all the forms that current form owns. + Form[] ownedForms = this.OwnedForms; + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + FormClosingEventArgs cfe = new FormClosingEventArgs(CloseReason.FormOwnerClosing, e.Cancel); + if (ownedForms[i] != null) { + //Call OnFormClosing on the child forms. + ownedForms[i].OnFormClosing(cfe); + if (cfe.Cancel) { + // Set the cancel flag for the Owner form + e.Cancel = true; + break; + } + } + } + + OnClosing(e); + OnFormClosing(e); + } + + if (m.Msg == NativeMethods.WM_QUERYENDSESSION) { + m.Result = (IntPtr)(e.Cancel ? 0 : 1); + } + else if (e.Cancel && (MdiParent != null)) { + // This is the case of an MDI child close event being canceled by the user. + CloseReason = CloseReason.None; + } + + if (Modal) { + return; + } + } + else { + e.Cancel = m.WParam == IntPtr.Zero; + } + + // Pass 2 (WM_CLOSE & WM_ENDSESSION)... Fire closed + // event on all mdi children and ourselves + // + if (m.Msg != NativeMethods.WM_QUERYENDSESSION) { + FormClosedEventArgs fc; + if (!e.Cancel) { + IsClosing = true; + + if (IsMdiContainer) { + fc = new FormClosedEventArgs(CloseReason.MdiFormClosing); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.IsTopMdiWindowClosing = IsClosing; + mdiChild.OnClosed(fc); + mdiChild.OnFormClosed(fc); + } + } + } + + // VSWhidbey#278060. Call OnClosed/OnFormClosed on all the forms that current form owns. + Form[] ownedForms = this.OwnedForms; + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + fc = new FormClosedEventArgs(CloseReason.FormOwnerClosing); + if (ownedForms[i] != null) { + //Call OnClosed and OnFormClosed on the child forms. + ownedForms[i].OnClosed(fc); + ownedForms[i].OnFormClosed(fc); + } + } + + fc = new FormClosedEventArgs(CloseReason); + OnClosed(fc); + OnFormClosed(fc); + + Dispose(); + } + } + } + + /// + /// WM_ENTERMENULOOP handler + /// + /// + private void WmEnterMenuLoop(ref Message m) { + OnMenuStart(EventArgs.Empty); + base.WndProc(ref m); + } + + /// + /// Handles the WM_ERASEBKGND message + /// + /// + private void WmEraseBkgnd(ref Message m) { + UpdateWindowState(); + base.WndProc(ref m); + } + + /// + /// WM_EXITMENULOOP handler + /// + /// + private void WmExitMenuLoop(ref Message m) { + OnMenuComplete(EventArgs.Empty); + base.WndProc(ref m); + } + + /// + /// WM_GETMINMAXINFO handler + /// + /// + private void WmGetMinMaxInfo(ref Message m) { + + // VSWhidbey 256556: Form should gracefully stop at the minimum preferred size. + // When we're set to AutoSize true, we should take a look at minAutoSize - which is snapped in onlayout. + // as the form contracts, we should not let it size past here as we're just going to readjust the size + // back to it later. + Size minTrack = (AutoSize && formStateEx[FormStateExInModalSizingLoop] == 1) ? LayoutUtils.UnionSizes(minAutoSize, MinimumSize) : MinimumSize; + + Size maxTrack = MaximumSize; + Rectangle maximizedBounds = MaximizedBounds; + + if (!minTrack.IsEmpty + || !maxTrack.IsEmpty + || !maximizedBounds.IsEmpty + || IsRestrictedWindow) { + + WmGetMinMaxInfoHelper(ref m, minTrack, maxTrack, maximizedBounds); + } + if (IsMdiChild) { + base.WndProc(ref m); + return; + } + } + + // PERFTRACK : Microsoft, 2/22/2000 - Refer to MINMAXINFO in a separate method + // : to avoid loading the class in the common case. + // + private void WmGetMinMaxInfoHelper(ref Message m, Size minTrack, Size maxTrack, Rectangle maximizedBounds) { + + NativeMethods.MINMAXINFO mmi = (NativeMethods.MINMAXINFO)m.GetLParam(typeof(NativeMethods.MINMAXINFO)); + + if (!minTrack.IsEmpty) { + + mmi.ptMinTrackSize.x = minTrack.Width; + mmi.ptMinTrackSize.y = minTrack.Height; + + // VSWhidbey 95302: When the MinTrackSize is set to a value larger than the screen + // size but the MaxTrackSize is not set to a value equal to or greater than the + // MinTrackSize and the user attempts to "grab" a resizing handle, Windows makes + // the window move a distance equal to either the width when attempting to resize + // horizontally or the height of the window when attempting to resize vertically. + // So, the workaround to prevent this problem is to set the MaxTrackSize to something + // whenever the MinTrackSize is set to a value larger than the respective dimension + // of the virtual screen. + + if (maxTrack.IsEmpty) { + + // Only set the max track size dimensions if the min track size dimensions + // are larger than the VirtualScreen dimensions. + Size virtualScreen = SystemInformation.VirtualScreen.Size; + if (minTrack.Height > virtualScreen.Height) { + mmi.ptMaxTrackSize.y = int.MaxValue; + } + if (minTrack.Width > virtualScreen.Width) { + mmi.ptMaxTrackSize.x = int.MaxValue; + } + } + } + if (!maxTrack.IsEmpty) { + // Is the specified MaxTrackSize smaller than the smallest allowable Window size? + Size minTrackWindowSize = SystemInformation.MinWindowTrackSize; + mmi.ptMaxTrackSize.x = Math.Max(maxTrack.Width, minTrackWindowSize.Width); + mmi.ptMaxTrackSize.y = Math.Max(maxTrack.Height, minTrackWindowSize.Height); + } + + if (!maximizedBounds.IsEmpty && !IsRestrictedWindow) { + mmi.ptMaxPosition.x = maximizedBounds.X; + mmi.ptMaxPosition.y = maximizedBounds.Y; + mmi.ptMaxSize.x = maximizedBounds.Width; + mmi.ptMaxSize.y = maximizedBounds.Height; + } + + if (IsRestrictedWindow) { + mmi.ptMinTrackSize.x = Math.Max(mmi.ptMinTrackSize.x, 100); + mmi.ptMinTrackSize.y = Math.Max(mmi.ptMinTrackSize.y, SystemInformation.CaptionButtonSize.Height * 3); + } + + Marshal.StructureToPtr(mmi, m.LParam, false); + m.Result = IntPtr.Zero; + } + + /// + /// WM_INITMENUPOPUP handler + /// + /// + private void WmInitMenuPopup(ref Message m) { + + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu != null) { + + //curMenu.UpdateRtl((RightToLeft == RightToLeft.Yes)); + + if (curMenu.ProcessInitMenuPopup(m.WParam)) + return; + } + base.WndProc(ref m); + } + + /// + /// Handles the WM_MENUCHAR message + /// + /// + private void WmMenuChar(ref Message m) { + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu == null) { + // KB article Q92527 tells us to forward these to our parent... + // + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent != null && formMdiParent.Menu != null) { + UnsafeNativeMethods.PostMessage(new HandleRef(formMdiParent, formMdiParent.Handle), NativeMethods.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_KEYMENU), m.WParam); + m.Result = (IntPtr)NativeMethods.Util.MAKELONG(0, 1); + return; + } + } + if (curMenu != null) { + curMenu.WmMenuChar(ref m); + if (m.Result != IntPtr.Zero) { + // This char is a mnemonic on our menu. + return; + } + } + + base.WndProc(ref m); + } + + /// + /// WM_MDIACTIVATE handler + /// + /// + private void WmMdiActivate(ref Message m) { + base.WndProc(ref m); + Debug.Assert(Properties.GetObject(PropFormMdiParent) != null, "how is formMdiParent null?"); + Debug.Assert(IsHandleCreated, "how is handle 0?"); + + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + + if (formMdiParent != null) { + // VSWhidbey 93530: This message is propagated twice by the MDIClient window. Once to the + // window being deactivated and once to the window being activated. + if (Handle == m.WParam) { + formMdiParent.DeactivateMdiChild(); + } + else if (Handle == m.LParam) { + formMdiParent.ActivateMdiChildInternal(this); + } + } + } + + private void WmNcButtonDown(ref Message m) { + if (IsMdiChild) { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent.ActiveMdiChildInternal == this) { + if (ActiveControl != null && !ActiveControl.ContainsFocus) { + InnerMostActiveContainerControl.FocusActiveControlInternal(); + } + } + } + base.WndProc(ref m); + } + + /// + /// WM_NCDESTROY handler + /// + /// + private void WmNCDestroy(ref Message m) { + MainMenu mainMenu = Menu; + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + + if (mainMenu != null) { + mainMenu.ClearHandles(); + } + if (curMenu != null) { + curMenu.ClearHandles(); + } + if (mergedMenu != null) { + mergedMenu.ClearHandles(); + } + if (dummyMenu != null) { + dummyMenu.ClearHandles(); + } + + base.WndProc(ref m); + + // Destroy the owner window, if we created one. We + // cannot do this in OnHandleDestroyed, because at + // that point our handle is not actually destroyed so + // destroying our parent actually causes a recursive + // WM_DESTROY. + if (ownerWindow != null) { + ownerWindow.DestroyHandle(); + ownerWindow = null; + } + + if (Modal && dialogResult == DialogResult.None) { + DialogResult = DialogResult.Cancel; + } + } + + /// + /// WM_NCHITTEST handler + /// + /// + private void WmNCHitTest(ref Message m) { + if (formState[FormStateRenderSizeGrip] != 0 ) { + int x = NativeMethods.Util.LOWORD(m.LParam); + int y = NativeMethods.Util.HIWORD(m.LParam); + + // Convert to client coordinates + // + NativeMethods.POINT pt = new NativeMethods.POINT(x, y); + UnsafeNativeMethods.ScreenToClient(new HandleRef(this, this.Handle), pt ); + + Size clientSize = ClientSize; + + // If the grip is not fully visible the grip area could overlap with the system control box; we need to disable + // the grip area in this case not to get in the way of the control box. We only need to check for the client's + // height since the window width will be at least the size of the control box which is always bigger than the + // grip width. + if( pt.x >= (clientSize.Width - SizeGripSize) && + pt.y >= (clientSize.Height - SizeGripSize) && + clientSize.Height >= SizeGripSize){ + m.Result = IsMirrored ? (IntPtr)NativeMethods.HTBOTTOMLEFT : (IntPtr)NativeMethods.HTBOTTOMRIGHT; + return; + } + } + + base.WndProc(ref m); + + // If we got any of the "edge" hits (bottom, top, topleft, etc), + // and we're AutoSizeMode.GrowAndShrink, return non-resizable border + // The edge values are the 8 values from HTLEFT (10) to HTBOTTOMRIGHT (17). + if (AutoSizeMode == AutoSizeMode.GrowAndShrink) + { + int result = unchecked( (int) (long)m.Result); + if (result >= NativeMethods.HTLEFT && + result <= NativeMethods.HTBOTTOMRIGHT) { + m.Result = (IntPtr)NativeMethods.HTBORDER; + } + } + } + + + /// + /// WM_SHOWWINDOW handler + /// + /// + private void WmShowWindow(ref Message m) { + formState[FormStateSWCalled] = 1; + base.WndProc(ref m); + } + + + /// + /// WM_SYSCOMMAND handler + /// + /// + private void WmSysCommand(ref Message m) { + bool callDefault = true; + + int sc = (NativeMethods.Util.LOWORD(m.WParam) & 0xFFF0); + + switch (sc) { + case NativeMethods.SC_CLOSE: + CloseReason = CloseReason.UserClosing; + if (IsMdiChild && !ControlBox) { + callDefault = false; + } + break; + case NativeMethods.SC_KEYMENU: + if (IsMdiChild && !ControlBox) { + callDefault = false; + } + break; + case NativeMethods.SC_SIZE: + case NativeMethods.SC_MOVE: + // VSWhidbey 514810: set this before WM_ENTERSIZELOOP because WM_GETMINMAXINFO can be called before WM_ENTERSIZELOOP. + formStateEx[FormStateExInModalSizingLoop] = 1; + break; + case NativeMethods.SC_CONTEXTHELP: + CancelEventArgs e = new CancelEventArgs(false); + OnHelpButtonClicked(e); + if (e.Cancel == true) { + callDefault = false; + } + break; + } + + if (Command.DispatchID(NativeMethods.Util.LOWORD(m.WParam))) { + callDefault = false; + } + + if (callDefault) { + + base.WndProc(ref m); + } + } + + /// + /// WM_SIZE handler + /// + /// + private void WmSize(ref Message m) { + + // If this is an MDI parent, don't pass WM_SIZE to the default + // window proc. We handle resizing the MDIClient window ourselves + // (using ControlDock.FILL). + // + if (ctlClient == null) { + base.WndProc(ref m); + if (MdiControlStrip == null && MdiParentInternal != null && MdiParentInternal.ActiveMdiChildInternal == this) { + int wParam = m.WParam.ToInt32(); + MdiParentInternal.UpdateMdiControlStrip(wParam == NativeMethods.SIZE_MAXIMIZED); + } + } + } + + /// + /// WM_UNINITMENUPOPUP handler + /// + /// + private void WmUnInitMenuPopup(ref Message m) { + if (Menu != null) { + //Whidbey addition - also raise the MainMenu.Collapse event for the current menu + Menu.OnCollapse(EventArgs.Empty); + } + } + + /// + /// WM_WINDOWPOSCHANGED handler + /// + /// + private void WmWindowPosChanged(ref Message m) { + + // V#40654 - We must update the windowState, because resize is fired + // from here... (in Control) + UpdateWindowState(); + base.WndProc(ref m); + + RestoreWindowBoundsIfNecessary(); + } + + /* + * THIS CODE LEFT HERE FOR REF. + * There are several issues with the location of minimized MDI child forms that + * we are not fixing. SEE VSW#431080 + /// + /// WM_WINDOWPOSCHANGING handler + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + // Handle minimized window location ourselves - see VSW#431080 for details. + if (IsMdiChild && WindowState == FormWindowState.Minimized) { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS*)m.LParam; + + if ((wp->flags & NativeMethods.SWP_NOMOVE) == 0 && (wp->flags & NativeMethods.SWP_NOSIZE) != 0) { + int menuHeight = SystemInformation.MenuHeight; + if ((MdiParent.MdiClient.ClientSize.Height - wp->y) < menuHeight) { + wp->y -= menuHeight; + } + } + } + + base.WndProc(ref m); + } + */ + + /// + /// + /// Base wndProc encapsulation. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_NCACTIVATE: + if (IsRestrictedWindow) { + BeginInvoke(new MethodInvoker(RestrictedProcessNcActivate)); + } + base.WndProc(ref m); + break; + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + case NativeMethods.WM_NCXBUTTONDOWN: + WmNcButtonDown(ref m); + break; + case NativeMethods.WM_ACTIVATE: + WmActivate(ref m); + break; + case NativeMethods.WM_MDIACTIVATE: + WmMdiActivate(ref m); + break; + case NativeMethods.WM_CLOSE: + if (CloseReason == CloseReason.None) { + CloseReason = CloseReason.TaskManagerClosing; + } + WmClose(ref m); + break; + + case NativeMethods.WM_QUERYENDSESSION: + case NativeMethods.WM_ENDSESSION: + CloseReason = CloseReason.WindowsShutDown; + WmClose(ref m); + break; + case NativeMethods.WM_ENTERSIZEMOVE: + WmEnterSizeMove(ref m); + DefWndProc(ref m); + break; + case NativeMethods.WM_EXITSIZEMOVE: + WmExitSizeMove(ref m); + DefWndProc(ref m); + break; + case NativeMethods.WM_CREATE: + WmCreate(ref m); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + + case NativeMethods.WM_INITMENUPOPUP: + WmInitMenuPopup(ref m); + break; + case NativeMethods.WM_UNINITMENUPOPUP: + WmUnInitMenuPopup(ref m); + break; + case NativeMethods.WM_MENUCHAR: + WmMenuChar(ref m); + break; + case NativeMethods.WM_NCDESTROY: + WmNCDestroy(ref m); + break; + case NativeMethods.WM_NCHITTEST: + WmNCHitTest(ref m); + break; + case NativeMethods.WM_SHOWWINDOW: + WmShowWindow(ref m); + break; + case NativeMethods.WM_SIZE: + WmSize(ref m); + break; + case NativeMethods.WM_SYSCOMMAND: + WmSysCommand(ref m); + break; + case NativeMethods.WM_GETMINMAXINFO: + WmGetMinMaxInfo(ref m); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + WmWindowPosChanged(ref m); + break; + //case NativeMethods.WM_WINDOWPOSCHANGING: + // WmWindowPosChanging(ref m); + // break; + case NativeMethods.WM_ENTERMENULOOP: + WmEnterMenuLoop(ref m); + break; + case NativeMethods.WM_EXITMENULOOP: + WmExitMenuLoop(ref m); + break; + case NativeMethods.WM_CAPTURECHANGED: + base.WndProc(ref m); + // This is a work-around for the Win32 scroll bar; it + // doesn't release it's capture in response to a CAPTURECHANGED + // message, so we force capture away if no button is down. + // + if (CaptureInternal && MouseButtons == (MouseButtons)0) { + CaptureInternal = false; + } + break; + case NativeMethods.WM_GETDPISCALEDSIZE: + { + Debug.Assert(NativeMethods.Util.SignedLOWORD(m.WParam) == NativeMethods.Util.SignedHIWORD(m.WParam), "Non-square pixels!"); + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmGetDpiScaledSize(ref m); + } else { + m.Result = IntPtr.Zero; + } + } + break; + case NativeMethods.WM_DPICHANGED: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChanged(ref m); + // If the application processes this message, return zero. + m.Result = IntPtr.Zero; + } else { + m.Result = (IntPtr)1; + } + break; + default: + base.WndProc(ref m); + break; + } + } + +#if SECURITY_DIALOG + private class SecurityMenuItem : ICommandExecutor { + Form owner; + Command cmd; + + internal SecurityMenuItem(Form owner) { + this.owner = owner; + cmd = new Command(this); + } + + internal int ID { + get { + return cmd.ID; + } + } + + [ + ReflectionPermission(SecurityAction.Assert, TypeInformation=true, MemberAccess=true), + UIPermission(SecurityAction.Assert, Window=UIPermissionWindow.AllWindows), + EnvironmentPermission(SecurityAction.Assert, Unrestricted=true), + FileIOPermission(SecurityAction.Assert, Unrestricted=true), + SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode), + ] + void ICommandExecutor.Execute() { + /// SECREVIEW VSWhidbey 332064: this looks to be correct, although not used in retail. + Form information = (Form)Activator.CreateInstance(typeof(Form).Module.Assembly.GetType("System.Windows.Forms.SysInfoForm"), new object[] {owner.IsRestrictedWindow}); + information.ShowDialog(); + } + } +#endif + + /// + /// + /// Represents a collection of controls on the form. + /// + [ComVisible(false)] + public new class ControlCollection : Control.ControlCollection { + + private Form owner; + + /*C#r:protected*/ + + /// + /// + /// Initializes a new instance of the ControlCollection class. + /// + public ControlCollection(Form owner) + : base(owner) { + this.owner = owner; + } + + /// + /// + /// Adds a control + /// to the form. + /// + public override void Add(Control value) { + if (value is MdiClient && owner.ctlClient == null) { + if (!owner.TopLevel && !owner.DesignMode) { + throw new ArgumentException(SR.GetString(SR.MDIContainerMustBeTopLevel), "value"); + } + owner.AutoScroll = false; + if (owner.IsMdiChild) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentAndChild), "value"); + } + owner.ctlClient = (MdiClient)value; + } + + // make sure we don't add a form that has a valid mdi parent + // + if (value is Form && ((Form)value).MdiParentInternal != null) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentCannotAdd), "value"); + } + + base.Add(value); + + if (owner.ctlClient != null) { + owner.ctlClient.SendToBack(); + } + } + + /// + /// + /// + /// Removes a control from the form. + /// + public override void Remove(Control value) { + if (value == owner.ctlClient) { + owner.ctlClient = null; + } + base.Remove(value); + } + } + + // Class used to temporarily reset the owners of windows owned by this Form + // before its handle recreation, then setting them back to the new handle + // after handle recreation + private class EnumThreadWindowsCallback + { + private List ownedWindows; + + internal EnumThreadWindowsCallback() + { + } + + internal bool Callback(IntPtr hWnd, IntPtr lParam) + { + Debug.Assert(lParam != IntPtr.Zero); + HandleRef hRef = new HandleRef(null, hWnd); + IntPtr parent = UnsafeNativeMethods.GetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT); + if (parent == lParam) + { + // Enumerated window is owned by this Form. + // Store it in a list for further treatment. + if (this.ownedWindows == null) + { + this.ownedWindows = new List(); + } + this.ownedWindows.Add(hRef); + } + return true; + } + + // Resets the owner of all the windows owned by this Form before handle recreation. + internal void ResetOwners() + { + if (this.ownedWindows != null) + { + foreach (HandleRef hRef in this.ownedWindows) + { + UnsafeNativeMethods.SetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT, NativeMethods.NullHandleRef); + } + } + } + + // Sets the owner of the windows back to this Form after its handle recreation. + internal void SetOwners(HandleRef hRefOwner) + { + if (this.ownedWindows != null) + { + foreach (HandleRef hRef in this.ownedWindows) + { + UnsafeNativeMethods.SetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT, hRefOwner); + } + } + } + } + + private class SecurityToolTip : IDisposable { + Form owner; + string toolTipText; + bool first = true; + ToolTipNativeWindow window; + + internal SecurityToolTip(Form owner) { + this.owner = owner; + SetupText(); + window = new ToolTipNativeWindow(this); + SetupToolTip(); + owner.LocationChanged += new EventHandler(FormLocationChanged); + owner.HandleCreated += new EventHandler(FormHandleCreated); + } + + CreateParams CreateParams { + get { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + + CreateParams cp = new CreateParams(); + cp.Parent = owner.Handle; + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + cp.Style |= NativeMethods.TTS_ALWAYSTIP | NativeMethods.TTS_BALLOON; + cp.ExStyle = 0; + cp.Caption = null; + return cp; + } + } + + internal bool Modal { + get { + return first; + } + } + + public void Dispose() { + if (owner != null) { + owner.LocationChanged -= new EventHandler(FormLocationChanged); + } + if (window.Handle != IntPtr.Zero) { + window.DestroyHandle(); + window = null; + } + } + + private NativeMethods.TOOLINFO_T GetTOOLINFO() { + NativeMethods.TOOLINFO_T toolInfo; + toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_T)); + toolInfo.uFlags |= NativeMethods.TTF_SUBCLASS; + toolInfo.lpszText = this.toolTipText; + if (owner.RightToLeft == RightToLeft.Yes) { + toolInfo.uFlags |= NativeMethods.TTF_RTLREADING; + } + if (!first) { + toolInfo.uFlags |= NativeMethods.TTF_TRANSPARENT; + toolInfo.hwnd = owner.Handle; + Size s = SystemInformation.CaptionButtonSize; + Rectangle r = new Rectangle(owner.Left, owner.Top, s.Width, SystemInformation.CaptionHeight); + r = owner.RectangleToClient(r); + r.Width -= r.X; + r.Y += 1; + toolInfo.rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + toolInfo.uId = IntPtr.Zero; + } + else { + toolInfo.uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRACK; + toolInfo.hwnd = IntPtr.Zero; + toolInfo.uId = owner.Handle; + } + return toolInfo; + } + + private void SetupText() { + owner.EnsureSecurityInformation(); + string mainText = SR.GetString(SR.SecurityToolTipMainText); + string sourceInfo = SR.GetString(SR.SecurityToolTipSourceInformation, owner.securitySite); + this.toolTipText = SR.GetString(SR.SecurityToolTipTextFormat, mainText, sourceInfo); + } + + private void SetupToolTip() { + window.CreateHandle(CreateParams); + + SafeNativeMethods.SetWindowPos(new HandleRef(window, window.Handle), NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOACTIVATE); + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, owner.Width); + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_SETTITLE, NativeMethods.TTI_WARNING, SR.GetString(SR.SecurityToolTipCaption)); + + if (0 == (int)UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO())) { + Debug.Fail("TTM_ADDTOOL failed for security tip"); + } + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ACTIVATE, 1, 0); + Show(); + } + + private void RecreateHandle() { + if (window != null) + { + if (window.Handle != IntPtr.Zero) { + window.DestroyHandle(); + } + SetupToolTip(); + } + } + + private void FormHandleCreated(object sender, EventArgs e) { + RecreateHandle(); + } + + private void FormLocationChanged(object sender, EventArgs e) { + if (window != null && first) { + Size s = SystemInformation.CaptionButtonSize; + + if (owner.WindowState == FormWindowState.Minimized) { + Pop(true /*noLongerFirst*/); + } + else { + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(owner.Left + s.Width / 2, owner.Top + SystemInformation.CaptionHeight)); + } + } + else { + Pop(true /*noLongerFirst*/); + } + } + + internal void Pop(bool noLongerFirst) { + if (noLongerFirst) { + first = false; + } + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKACTIVATE, 0, GetTOOLINFO()); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_DELTOOL, 0, GetTOOLINFO()); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO()); + } + + internal void Show() { + if (first) { + Size s = SystemInformation.CaptionButtonSize; + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(owner.Left + s.Width / 2, owner.Top + SystemInformation.CaptionHeight)); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKACTIVATE, 1, GetTOOLINFO()); + } + } + + private void WndProc(ref Message msg) { + if (first) { + if (msg.Msg == NativeMethods.WM_LBUTTONDOWN + || msg.Msg == NativeMethods.WM_RBUTTONDOWN + || msg.Msg == NativeMethods.WM_MBUTTONDOWN + || msg.Msg == NativeMethods.WM_XBUTTONDOWN) { + + Pop(true /*noLongerFirst*/); + } + } + window.DefWndProc(ref msg); + } + + private sealed class ToolTipNativeWindow : NativeWindow { + SecurityToolTip control; + + internal ToolTipNativeWindow(SecurityToolTip control) { + this.control = control; + } + + protected override void WndProc(ref Message m) { + if (control != null) { + control.WndProc(ref m); + } + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Form.cs.back b/WindowsForms/Managed/System/WinForms/Form.cs.back new file mode 100644 index 000000000..531ad4f59 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Form.cs.back @@ -0,0 +1,7972 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1999, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Globalization; + using System.Net; + using System.Reflection; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Security.Policy; + using System.Threading; + using System.Windows.Forms.Design; + using System.Windows.Forms.Layout; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// Represents a window or dialog box that makes up an application's user interface. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + ToolboxItemFilter("System.Windows.Forms.Control.TopLevel"), + ToolboxItem(false), + DesignTimeVisible(false), + Designer("System.Windows.Forms.Design.FormDocumentDesigner, " + AssemblyRef.SystemDesign, typeof(IRootDesigner)), + DesignerCategory("Form"), + DefaultEvent("Load"), + InitializationEvent("Load"), + ] + public class Form : ContainerControl { +#if DEBUG + static readonly BooleanSwitch AlwaysRestrictWindows = new BooleanSwitch("AlwaysRestrictWindows", "Always make Form classes behave as though they are restricted"); +#endif + private static readonly object EVENT_ACTIVATED = new object(); + private static readonly object EVENT_CLOSING = new object(); + private static readonly object EVENT_CLOSED = new object(); + private static readonly object EVENT_FORMCLOSING = new object(); + private static readonly object EVENT_FORMCLOSED = new object(); + private static readonly object EVENT_DEACTIVATE = new object(); + private static readonly object EVENT_LOAD = new object(); + private static readonly object EVENT_MDI_CHILD_ACTIVATE = new object(); + private static readonly object EVENT_INPUTLANGCHANGE = new object(); + private static readonly object EVENT_INPUTLANGCHANGEREQUEST = new object(); + private static readonly object EVENT_MENUSTART = new object(); + private static readonly object EVENT_MENUCOMPLETE = new object(); + private static readonly object EVENT_MAXIMUMSIZECHANGED = new object(); + private static readonly object EVENT_MINIMUMSIZECHANGED = new object(); + private static readonly object EVENT_HELPBUTTONCLICKED = new object(); + private static readonly object EVENT_SHOWN = new object(); + private static readonly object EVENT_RESIZEBEGIN = new object(); + private static readonly object EVENT_RESIZEEND = new object(); + private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object(); + private static readonly object EVENT_DPI_CHANGED = new object(); + + // + // The following flags should be used with formState[..] not formStateEx[..] + // Don't add any more sections to this vector, it is already full. + // + private static readonly BitVector32.Section FormStateAllowTransparency = BitVector32.CreateSection(1); + private static readonly BitVector32.Section FormStateBorderStyle = BitVector32.CreateSection(6, FormStateAllowTransparency); + private static readonly BitVector32.Section FormStateTaskBar = BitVector32.CreateSection(1, FormStateBorderStyle); + private static readonly BitVector32.Section FormStateControlBox = BitVector32.CreateSection(1, FormStateTaskBar); + private static readonly BitVector32.Section FormStateKeyPreview = BitVector32.CreateSection(1, FormStateControlBox); + private static readonly BitVector32.Section FormStateLayered = BitVector32.CreateSection(1, FormStateKeyPreview); + private static readonly BitVector32.Section FormStateMaximizeBox = BitVector32.CreateSection(1, FormStateLayered); + private static readonly BitVector32.Section FormStateMinimizeBox = BitVector32.CreateSection(1, FormStateMaximizeBox); + private static readonly BitVector32.Section FormStateHelpButton = BitVector32.CreateSection(1, FormStateMinimizeBox); + private static readonly BitVector32.Section FormStateStartPos = BitVector32.CreateSection(4, FormStateHelpButton); + private static readonly BitVector32.Section FormStateWindowState = BitVector32.CreateSection(2, FormStateStartPos); + private static readonly BitVector32.Section FormStateShowWindowOnCreate = BitVector32.CreateSection(1, FormStateWindowState); + private static readonly BitVector32.Section FormStateAutoScaling = BitVector32.CreateSection(1, FormStateShowWindowOnCreate); + private static readonly BitVector32.Section FormStateSetClientSize = BitVector32.CreateSection(1, FormStateAutoScaling); + private static readonly BitVector32.Section FormStateTopMost = BitVector32.CreateSection(1, FormStateSetClientSize); + private static readonly BitVector32.Section FormStateSWCalled = BitVector32.CreateSection(1, FormStateTopMost); + private static readonly BitVector32.Section FormStateMdiChildMax = BitVector32.CreateSection(1, FormStateSWCalled); + private static readonly BitVector32.Section FormStateRenderSizeGrip = BitVector32.CreateSection(1, FormStateMdiChildMax); + private static readonly BitVector32.Section FormStateSizeGripStyle = BitVector32.CreateSection(2, FormStateRenderSizeGrip); + private static readonly BitVector32.Section FormStateIsRestrictedWindow = BitVector32.CreateSection(1, FormStateSizeGripStyle); + private static readonly BitVector32.Section FormStateIsRestrictedWindowChecked = BitVector32.CreateSection(1, FormStateIsRestrictedWindow); + private static readonly BitVector32.Section FormStateIsWindowActivated = BitVector32.CreateSection(1, FormStateIsRestrictedWindowChecked); + private static readonly BitVector32.Section FormStateIsTextEmpty = BitVector32.CreateSection(1, FormStateIsWindowActivated); + private static readonly BitVector32.Section FormStateIsActive = BitVector32.CreateSection(1, FormStateIsTextEmpty); + private static readonly BitVector32.Section FormStateIconSet = BitVector32.CreateSection(1, FormStateIsActive); + +#if SECURITY_DIALOG + private static readonly BitVector32.Section FormStateAddedSecurityMenuItem = BitVector32.CreateSection(1, FormStateIconSet); +#endif + + // + // The following flags should be used with formStateEx[...] not formState[..] + // + private static readonly BitVector32.Section FormStateExCalledClosing = BitVector32.CreateSection(1); + private static readonly BitVector32.Section FormStateExUpdateMenuHandlesSuspendCount = BitVector32.CreateSection(8, FormStateExCalledClosing); + private static readonly BitVector32.Section FormStateExUpdateMenuHandlesDeferred = BitVector32.CreateSection(1, FormStateExUpdateMenuHandlesSuspendCount); + private static readonly BitVector32.Section FormStateExUseMdiChildProc = BitVector32.CreateSection(1, FormStateExUpdateMenuHandlesDeferred); + private static readonly BitVector32.Section FormStateExCalledOnLoad = BitVector32.CreateSection(1, FormStateExUseMdiChildProc); + private static readonly BitVector32.Section FormStateExCalledMakeVisible = BitVector32.CreateSection(1, FormStateExCalledOnLoad); + private static readonly BitVector32.Section FormStateExCalledCreateControl = BitVector32.CreateSection(1, FormStateExCalledMakeVisible); + private static readonly BitVector32.Section FormStateExAutoSize = BitVector32.CreateSection(1, FormStateExCalledCreateControl); + private static readonly BitVector32.Section FormStateExInUpdateMdiControlStrip = BitVector32.CreateSection(1, FormStateExAutoSize); + private static readonly BitVector32.Section FormStateExShowIcon = BitVector32.CreateSection(1, FormStateExInUpdateMdiControlStrip); + private static readonly BitVector32.Section FormStateExMnemonicProcessed = BitVector32.CreateSection(1, FormStateExShowIcon); + private static readonly BitVector32.Section FormStateExInScale = BitVector32.CreateSection(1, FormStateExMnemonicProcessed); + private static readonly BitVector32.Section FormStateExInModalSizingLoop = BitVector32.CreateSection(1, FormStateExInScale); + private static readonly BitVector32.Section FormStateExSettingAutoScale = BitVector32.CreateSection(1, FormStateExInModalSizingLoop); + private static readonly BitVector32.Section FormStateExWindowBoundsWidthIsClientSize = BitVector32.CreateSection(1, FormStateExSettingAutoScale); + private static readonly BitVector32.Section FormStateExWindowBoundsHeightIsClientSize = BitVector32.CreateSection(1, FormStateExWindowBoundsWidthIsClientSize); + private static readonly BitVector32.Section FormStateExWindowClosing = BitVector32.CreateSection(1, FormStateExWindowBoundsHeightIsClientSize); + + private const int SizeGripSize = 16; + + private static Icon defaultIcon = null; + private static Icon defaultRestrictedIcon = null; +#if MAGIC_PADDING + private static Padding FormPadding = new Padding(9); // UI guideline +#endif + private static object internalSyncObject = new object(); + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + // + private static readonly int PropAcceptButton = PropertyStore.CreateKey(); + private static readonly int PropCancelButton = PropertyStore.CreateKey(); + private static readonly int PropDefaultButton = PropertyStore.CreateKey(); + private static readonly int PropDialogOwner = PropertyStore.CreateKey(); + + private static readonly int PropMainMenu = PropertyStore.CreateKey(); + private static readonly int PropDummyMenu = PropertyStore.CreateKey(); + private static readonly int PropCurMenu = PropertyStore.CreateKey(); + private static readonly int PropMergedMenu = PropertyStore.CreateKey(); + + private static readonly int PropOwner = PropertyStore.CreateKey(); + private static readonly int PropOwnedForms = PropertyStore.CreateKey(); + private static readonly int PropMaximizedBounds = PropertyStore.CreateKey(); + private static readonly int PropOwnedFormsCount = PropertyStore.CreateKey(); + + private static readonly int PropMinTrackSizeWidth = PropertyStore.CreateKey(); + private static readonly int PropMinTrackSizeHeight = PropertyStore.CreateKey(); + private static readonly int PropMaxTrackSizeWidth = PropertyStore.CreateKey(); + private static readonly int PropMaxTrackSizeHeight = PropertyStore.CreateKey(); + + private static readonly int PropFormMdiParent = PropertyStore.CreateKey(); + private static readonly int PropActiveMdiChild = PropertyStore.CreateKey(); + private static readonly int PropFormerlyActiveMdiChild = PropertyStore.CreateKey(); + private static readonly int PropMdiChildFocusable = PropertyStore.CreateKey(); + + private static readonly int PropMainMenuStrip = PropertyStore.CreateKey(); + private static readonly int PropMdiWindowListStrip = PropertyStore.CreateKey(); + private static readonly int PropMdiControlStrip = PropertyStore.CreateKey(); + private static readonly int PropSecurityTip = PropertyStore.CreateKey(); + + private static readonly int PropOpacity = PropertyStore.CreateKey(); + private static readonly int PropTransparencyKey = PropertyStore.CreateKey(); +#if SECURITY_DIALOG + private static readonly int PropSecuritySystemMenuItem = PropertyStore.CreateKey(); +#endif + + /////////////////////////////////////////////////////////////////////// + // Form per instance members + // + // Note: Do not add anything to this list unless absolutely neccessary. + // + // Begin Members { + + // List of properties that are generally set, so we keep them directly on + // Form. + // + + private BitVector32 formState = new BitVector32(0x21338); // magic value... all the defaults... see the ctor for details... + private BitVector32 formStateEx = new BitVector32(); + + + private Icon icon; + private Icon smallIcon; + private Size autoScaleBaseSize = System.Drawing.Size.Empty; + private Size minAutoSize = Size.Empty; + private Rectangle restoredWindowBounds = new Rectangle(-1, -1, -1, -1); + private BoundsSpecified restoredWindowBoundsSpecified; + private DialogResult dialogResult; + private MdiClient ctlClient; + private NativeWindow ownerWindow; + private string userWindowText; // Used to cache user's text in semi-trust since the window text is added security info. + private string securityZone; + private string securitySite; + private bool rightToLeftLayout = false; + + + //Whidbey RestoreBounds ... + private Rectangle restoreBounds = new Rectangle(-1, -1, -1, -1); + private CloseReason closeReason = CloseReason.None; + + private VisualStyleRenderer sizeGripRenderer; + + // } End Members + /////////////////////////////////////////////////////////////////////// + + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public Form() + : base() { + + // we must setup the formState *before* calling Control's ctor... so we do that + // at the member variable... that magic number is generated by switching + // the line below to "true" and running a form. + // + // keep the "init" and "assert" sections always in sync! + // +#if false + // init section... + // + formState[FormStateAllowTransparency] = 0; + formState[FormStateBorderStyle] = (int)FormBorderStyle.Sizable; + formState[FormStateTaskBar] = 1; + formState[FormStateControlBox] = 1; + formState[FormStateKeyPreview] = 0; + formState[FormStateLayered] = 0; + formState[FormStateMaximizeBox] = 1; + formState[FormStateMinimizeBox] = 1; + formState[FormStateHelpButton] = 0; + formState[FormStateStartPos] = (int)FormStartPosition.WindowsDefaultLocation; + formState[FormStateWindowState] = (int)FormWindowState.Normal; + formState[FormStateShowWindowOnCreate] = 0; + formState[FormStateAutoScaling] = 1; + formState[FormStateSetClientSize] = 0; + formState[FormStateTopMost] = 0; + formState[FormStateSWCalled] = 0; + formState[FormStateMdiChildMax] = 0; + formState[FormStateRenderSizeGrip] = 0; + formState[FormStateSizeGripStyle] = 0; + formState[FormStateIsRestrictedWindow] = 0; + formState[FormStateIsRestrictedWindowChecked] = 0; + formState[FormStateIsWindowActivated] = 0; + formState[FormStateIsTextEmpty] = 0; + formState[FormStateIsActive] = 0; + formState[FormStateIconSet] = 0; + +#if SECURITY_DIALOG + formState[FormStateAddedSecurityMenuItem] = 0; + +#endif + + + + + Debug.WriteLine("initial formState: 0x" + formState.Data.ToString("X")); +#endif + // assert section... + // + Debug.Assert(formState[FormStateAllowTransparency] == 0, "Failed to set formState[FormStateAllowTransparency]"); + Debug.Assert(formState[FormStateBorderStyle] == (int)FormBorderStyle.Sizable, "Failed to set formState[FormStateBorderStyle]"); + Debug.Assert(formState[FormStateTaskBar] == 1, "Failed to set formState[FormStateTaskBar]"); + Debug.Assert(formState[FormStateControlBox] == 1, "Failed to set formState[FormStateControlBox]"); + Debug.Assert(formState[FormStateKeyPreview] == 0, "Failed to set formState[FormStateKeyPreview]"); + Debug.Assert(formState[FormStateLayered] == 0, "Failed to set formState[FormStateLayered]"); + Debug.Assert(formState[FormStateMaximizeBox] == 1, "Failed to set formState[FormStateMaximizeBox]"); + Debug.Assert(formState[FormStateMinimizeBox] == 1, "Failed to set formState[FormStateMinimizeBox]"); + Debug.Assert(formState[FormStateHelpButton] == 0, "Failed to set formState[FormStateHelpButton]"); + Debug.Assert(formState[FormStateStartPos] == (int)FormStartPosition.WindowsDefaultLocation, "Failed to set formState[FormStateStartPos]"); + Debug.Assert(formState[FormStateWindowState] == (int)FormWindowState.Normal, "Failed to set formState[FormStateWindowState]"); + Debug.Assert(formState[FormStateShowWindowOnCreate] == 0, "Failed to set formState[FormStateShowWindowOnCreate]"); + Debug.Assert(formState[FormStateAutoScaling] == 1, "Failed to set formState[FormStateAutoScaling]"); + Debug.Assert(formState[FormStateSetClientSize] == 0, "Failed to set formState[FormStateSetClientSize]"); + Debug.Assert(formState[FormStateTopMost] == 0, "Failed to set formState[FormStateTopMost]"); + Debug.Assert(formState[FormStateSWCalled] == 0, "Failed to set formState[FormStateSWCalled]"); + Debug.Assert(formState[FormStateMdiChildMax] == 0, "Failed to set formState[FormStateMdiChildMax]"); + Debug.Assert(formState[FormStateRenderSizeGrip] == 0, "Failed to set formState[FormStateRenderSizeGrip]"); + Debug.Assert(formState[FormStateSizeGripStyle] == 0, "Failed to set formState[FormStateSizeGripStyle]"); + // can't check these... Control::.ctor may force the check + // of security... you can only assert these are 0 when running + // under full trust... + // + //Debug.Assert(formState[FormStateIsRestrictedWindow] == 0, "Failed to set formState[FormStateIsRestrictedWindow]"); + //Debug.Assert(formState[FormStateIsRestrictedWindowChecked] == 0, "Failed to set formState[FormStateIsRestrictedWindowChecked]"); + Debug.Assert(formState[FormStateIsWindowActivated] == 0, "Failed to set formState[FormStateIsWindowActivated]"); + Debug.Assert(formState[FormStateIsTextEmpty] == 0, "Failed to set formState[FormStateIsTextEmpty]"); + Debug.Assert(formState[FormStateIsActive] == 0, "Failed to set formState[FormStateIsActive]"); + Debug.Assert(formState[FormStateIconSet] == 0, "Failed to set formState[FormStateIconSet]"); + + +#if SECURITY_DIALOG + Debug.Assert(formState[FormStateAddedSecurityMenuItem] == 0, "Failed to set formState[FormStateAddedSecurityMenuItem]"); +#endif + + // SECURITY NOTE: The IsRestrictedWindow check is done once and cached. We force it to happen here + // since we want to ensure the check is done on the code that constructs the form. + bool temp = IsRestrictedWindow; + + formStateEx[FormStateExShowIcon] = 1; + + SetState(STATE_VISIBLE, false); + SetState(STATE_TOPLEVEL, true); + +#if EVERETTROLLBACK + // VSWhidbey# 393617 (MDI: Roll back feature to Everett + QFE source base). Code left here for ref. + // VSWhidbey# 357405 Enabling this code introduces a breaking change that has was approved. + // If this needs to be enabled, also CanTabStop and TabStop code needs to be added back in Control.cs + // and Form.cs. Look at RADBU CL#963988 for ref. + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Set this value to false + // so that the window style will not include the WS_TABSTOP bit, which is + // identical to WS_MAXIMIZEBOX. Otherwise, our test suite won't be able to + // determine whether or not the window utilizes the Maximize Box in the + // window caption. + SetState(STATE_TABSTOP, false); +#endif + } + + /// + /// + /// Indicates the control on the form that is clicked when + /// the user presses the ENTER key. + /// + [ + DefaultValue(null), + SRDescription(SR.FormAcceptButtonDescr) + ] + public IButtonControl AcceptButton { + get { + return (IButtonControl)Properties.GetObject(PropAcceptButton); + } + set { + if (AcceptButton != value) { + Properties.SetObject(PropAcceptButton, value); + UpdateDefaultButton(); + + // this was removed as it breaks any accept button that isn't + // an OK, like in the case of wizards 'next' button. it was + // added as a fix to 47209...which has been reactivated. + /* + if (acceptButton != null && acceptButton.DialogResult == DialogResult.None) { + acceptButton.DialogResult = DialogResult.OK; + } + */ + } + } + } + + /// + /// Retrieves true if this form is currently active. + /// + internal bool Active { + get { + Form parentForm = ParentFormInternal; + if (parentForm == null) { + return formState[FormStateIsActive] != 0; + } + return(parentForm.ActiveControl == this && parentForm.Active); + } + + set { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Form::set_Active - " + this.Name); + if ((formState[FormStateIsActive] != 0) != value) { + if (value) { + // There is a weird user32 bug (see ASURT 124232) where it will + // activate the MDIChild even when it is not visible, when + // we set it's parent handle. Ignore this. We will get + // activated again when the MDIChild's visibility is set to true. + if (!CanRecreateHandle()){ + //Debug.Fail("Setting Active window when not yet visible"); + return; + } + } + + formState[FormStateIsActive] = value ? 1 : 0; + + if (value) { + formState[FormStateIsWindowActivated] = 1; + if (IsRestrictedWindow) { + WindowText = userWindowText; + } + // VSWhidbey#367424 Check if validation has been cancelled to avoid raising Validation event multiple times. + if (!ValidationCancelled) { + if( ActiveControl == null ) { + // Security reviewed : This internal method is called from various places, all + // of which are OK. Since SelectNextControl (a public function) + // Demands ModifyFocus, we must call the internal version. + // + SelectNextControlInternal(null, true, true, true, false); + // If no control is selected focus will go to form + } + + InnerMostActiveContainerControl.FocusActiveControlInternal(); + } + OnActivated(EventArgs.Empty); + } + else { + formState[FormStateIsWindowActivated] = 0; + if (IsRestrictedWindow) { + Text = userWindowText; + } + OnDeactivate(EventArgs.Empty); + } + } + } + } + + /// + /// + /// Gets the currently active form for this application. + /// + public static Form ActiveForm { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + IntPtr hwnd = UnsafeNativeMethods.GetForegroundWindow(); + Control c = Control.FromHandleInternal(hwnd); + if (c != null && c is Form) { + return(Form)c; + } + return null; + } + } + + /// + /// + /// + /// Gets the currently active multiple document interface (MDI) child window. + /// Note: Don't use this property internally, use ActiveMdiChildInternal instead (see comments below). + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormActiveMDIChildDescr) + ] + public Form ActiveMdiChild { + get { + Form mdiChild = ActiveMdiChildInternal; + + // Hack: We keep the active mdi child in the cached in the property store; when changing its value + // (due to a change to one of the following properties/methods: Visible, Enabled, Active, Show/Hide, + // Focus() or as the result of WM_SETFOCUS/WM_ACTIVATE/WM_MDIACTIVATE) we temporarily set it to null + // (to properly handle menu merging among other things) rendering the cache out-of-date; the problem + // arises when the user has an event handler that is raised during this process; in that case we ask + // Windows for it (see ActiveMdiChildFromWindows). + + if( mdiChild == null ){ + // If this.MdiClient != null it means this.IsMdiContainer == true. + if( this.ctlClient != null && this.ctlClient.IsHandleCreated){ + IntPtr hwnd = this.ctlClient.SendMessage(NativeMethods.WM_MDIGETACTIVE, 0, 0); + mdiChild = Control.FromHandleInternal( hwnd ) as Form; + } + } + if( mdiChild != null && mdiChild.Visible && mdiChild.Enabled ){ + return mdiChild; + } + return null; + } + } + + /// + /// Property to be used internally. See comments a on ActiveMdiChild property. + /// + internal Form ActiveMdiChildInternal{ + get{ + return (Form)Properties.GetObject(PropActiveMdiChild); + } + + set{ + Properties.SetObject(PropActiveMdiChild, value); + } + } + + //VSWhidbey 439815: we don't repaint the mdi child that used to be active any more. We used to do this in Activated, but no + //longer do because of added event Deactivate. + private Form FormerlyActiveMdiChild + { + get + { + return (Form)Properties.GetObject(PropFormerlyActiveMdiChild); + } + + set + { + Properties.SetObject(PropFormerlyActiveMdiChild, value); + } + } + + /// + /// + /// + /// + /// Gets or sets + /// a value indicating whether the opacity of the form can be + /// adjusted. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAllowTransparencyDescr) + ] + public bool AllowTransparency { + get { + return formState[FormStateAllowTransparency] != 0; + } + set { + if (value != (formState[FormStateAllowTransparency] != 0) && + OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + formState[FormStateAllowTransparency] = (value ? 1 : 0); + + formState[FormStateLayered] = formState[FormStateAllowTransparency]; + + UpdateStyles(); + + if (!value) { + if (Properties.ContainsObject(PropOpacity)) { + Properties.SetObject(PropOpacity, (object)1.0f); + } + if (Properties.ContainsObject(PropTransparencyKey)) { + Properties.SetObject(PropTransparencyKey, Color.Empty); + } + UpdateLayered(); + } + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the form will adjust its size + /// to fit the height of the font used on the form and scale + /// its controls. + /// + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.FormAutoScaleDescr), + Obsolete("This property has been deprecated. Use the AutoScaleMode property instead. http://go.microsoft.com/fwlink/?linkid=14202"), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool AutoScale { + get { + return formState[FormStateAutoScaling] != 0; + } + + set { + formStateEx[FormStateExSettingAutoScale] = 1; + try { + if (value) { + formState[FormStateAutoScaling] = 1; + + // if someone insists on auto scaling, + // force the new property back to none so they + // don't compete. + AutoScaleMode = AutoScaleMode.None; + + + } + else { + formState[FormStateAutoScaling] = 0; + } + } + finally { + formStateEx[FormStateExSettingAutoScale] = 0; + } + } + } + +// VSWhidbey 498258: Our STRONG recommendation to customers is to upgrade to AutoScaleDimensions +// however, since this is generated by default in Everett, and there's not a 1:1 mapping of +// the old to the new, we are un-obsoleting the setter for AutoScaleBaseSize only. +#pragma warning disable 618 + /// + /// + /// The base size used for autoscaling. The AutoScaleBaseSize is used + /// internally to determine how much to scale the form when AutoScaling is + /// used. + /// + // + // Virtual so subclasses like PrintPreviewDialog can prevent changes. + [ + Localizable(true), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public virtual Size AutoScaleBaseSize { + get { + if (autoScaleBaseSize.IsEmpty) { + SizeF real = GetAutoScaleSize(Font); + return new Size((int)Math.Round(real.Width), (int)Math.Round(real.Height)); + } + return autoScaleBaseSize; + } + + set { + // Only allow the set when not in designmode, this prevents us from + // preserving an old value. The form design should prevent this for + // us by shadowing this property, so we just assert that the designer + // is doing its job. + // + Debug.Assert(!DesignMode, "Form designer should not allow base size set in design mode."); + autoScaleBaseSize = value; + } + } +#pragma warning restore 618 + + + /// + /// + /// + /// Gets or sets a value indicating whether the form implements + /// autoscrolling. + /// + /// + [ + Localizable(true) + ] + public override bool AutoScroll { + get { return base.AutoScroll;} + + set { + if (value) { + IsMdiContainer = false; + } + base.AutoScroll = value; + } + } + + // Forms implement their own AutoSize in OnLayout so we shadow this property + // just in case someone parents a Form to a container control. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize { + get { return formStateEx[FormStateExAutoSize] != 0; } + set { + if (value != AutoSize) { + formStateEx[FormStateExAutoSize] = value ? 1 : 0; + if (!AutoSize) { + minAutoSize = Size.Empty; + // If we just disabled AutoSize, restore the original size. + this.Size = CommonProperties.GetSpecifiedBounds(this).Size; + } + LayoutTransaction.DoLayout(this, this, PropertyNames.AutoSize); + OnAutoSizeChanged(EventArgs.Empty); + } + Debug.Assert(AutoSize == value, "Error detected setting Form.AutoSize."); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRDescription(SR.ControlAutoSizeModeDescr), + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true) + ] + public AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + Control toLayout = DesignMode || ParentInternal == null ? this : ParentInternal; + + if(toLayout != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(toLayout.LayoutEngine == DefaultLayout.Instance) { + toLayout.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(toLayout, this, PropertyNames.AutoSize); + } + } + } + } + + /// + /// + /// Indicates whether controls in this container will be automatically validated when the focus changes. + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public override AutoValidate AutoValidate { + get { + return base.AutoValidate; + } + set { + base.AutoValidate = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public new event EventHandler AutoValidateChanged { + add { + base.AutoValidateChanged += value; + } + remove { + base.AutoValidateChanged -= value; + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + public override Color BackColor { + get { + // Forms should not inherit BackColor from their parent, + // particularly if the parent is an MDIClient. + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) + return c; + + return DefaultBackColor; + } + + set { + base.BackColor = value; + } + } + + private bool CalledClosing { + get{ + return formStateEx[FormStateExCalledClosing] != 0; + } + set{ + formStateEx[FormStateExCalledClosing] = (value ? 1 : 0); + } + } + + private bool CalledCreateControl { + get{ + return formStateEx[FormStateExCalledCreateControl] != 0; + } + set{ + formStateEx[FormStateExCalledCreateControl] = (value ? 1 : 0); + } + } + + private bool CalledMakeVisible { + get{ + return formStateEx[FormStateExCalledMakeVisible] != 0; + } + set{ + formStateEx[FormStateExCalledMakeVisible] = (value ? 1 : 0); + } + } + + private bool CalledOnLoad { + get{ + return formStateEx[FormStateExCalledOnLoad] != 0; + } + set{ + formStateEx[FormStateExCalledOnLoad] = (value ? 1 : 0); + } + } + + + /// + /// + /// + /// Gets or sets the border style of the form. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FormBorderStyle.Sizable), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.FormBorderStyleDescr) + ] + public FormBorderStyle FormBorderStyle { + get { + return(FormBorderStyle)formState[FormStateBorderStyle]; + } + + set { + //validate FormBorderStyle enum + // + //valid values are 0x0 to 0x6 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormBorderStyle.None, (int)FormBorderStyle.SizableToolWindow)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormBorderStyle)); + } + + // In rectricted mode we don't allow windows w/o min/max/close functionality. + if (IsRestrictedWindow) { + switch (value) { + case FormBorderStyle.None: + value = FormBorderStyle.FixedSingle; + break; + case FormBorderStyle.FixedSingle: + case FormBorderStyle.Fixed3D: + case FormBorderStyle.FixedDialog: + case FormBorderStyle.Sizable: + // nothing needed here, we can just let these stay + // + break; + case FormBorderStyle.FixedToolWindow: + value = FormBorderStyle.FixedSingle; + break; + case FormBorderStyle.SizableToolWindow: + value = FormBorderStyle.Sizable; + break; + default: + value = FormBorderStyle.Sizable; + break; + } + } + + formState[FormStateBorderStyle] = (int)value; + + //(bug 112024).. It seems that the FormBorderStyle is Code serialised after the ClientSize is Set... + //Hence in the Setter for FormBodrstyle we need to push in the Correct ClientSize once again.. + if (formState[FormStateSetClientSize] == 1 && !IsHandleCreated) { + ClientSize = ClientSize; + } + + // VSWhidbey 95327: Since setting the border style induces a call to SetBoundsCore, which, + // when the WindowState is not Normal, will cause the values stored in the field restoredWindowBounds + // to be replaced. The side-effect of this, of course, is that when the form window is restored, + // the Form's size is restored to the size it was when in a non-Normal state. + // So, we need to cache these values now before the call to UpdateFormStyles() to prevent + // these existing values from being lost. Then, if the WindowState is something other than + // FormWindowState.Normal after the call to UpdateFormStyles(), restore these cached values to + // the restoredWindowBounds field. + Rectangle preClientUpdateRestoredWindowBounds = restoredWindowBounds; + BoundsSpecified preClientUpdateRestoredWindowBoundsSpecified = restoredWindowBoundsSpecified; + int preWindowBoundsWidthIsClientSize = formStateEx[FormStateExWindowBoundsWidthIsClientSize]; + int preWindowBoundsHeightIsClientSize = formStateEx[FormStateExWindowBoundsHeightIsClientSize]; + + UpdateFormStyles(); + + // In Windows XP Theme, the FixedDialog tend to have a small Icon. + // So to make this behave uniformly with other styles, we need to make + // the call to UpdateIcon after the the form styles have been updated. + if (formState[FormStateIconSet] == 0 && !IsRestrictedWindow) { + UpdateWindowIcon(false); + } + + // VSWhidbey 95327: Now restore the values cached above. + if (WindowState != FormWindowState.Normal) { + restoredWindowBounds = preClientUpdateRestoredWindowBounds; + restoredWindowBoundsSpecified = preClientUpdateRestoredWindowBoundsSpecified; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = preWindowBoundsWidthIsClientSize; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = preWindowBoundsHeightIsClientSize; + } + } + } + + /// + /// + /// Gets + /// or + /// sets the button control that will be clicked when the + /// user presses the ESC key. + /// + [ + DefaultValue(null), + SRDescription(SR.FormCancelButtonDescr) + ] + public IButtonControl CancelButton { + get { + return (IButtonControl)Properties.GetObject(PropCancelButton); + } + set { + Properties.SetObject(PropCancelButton, value); + + if (value != null && value.DialogResult == DialogResult.None) { + value.DialogResult = DialogResult.Cancel; + } + } + } + + /// + /// + /// + /// Gets or sets the size of the client area of the form. + /// + /// + [ + Localizable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + new public Size ClientSize { + get { + return base.ClientSize; + } + set { + base.ClientSize = value; + } + } + + /// + /// + /// Gets or sets a value indicating whether a control box is displayed in the + /// caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormControlBoxDescr) + ] + public bool ControlBox { + get { + return formState[FormStateControlBox] != 0; + } + + set { + // Window style in restricted mode must always have a control box. + if (IsRestrictedWindow) { + return; + } + + if (value) { + formState[FormStateControlBox] = 1; + } + else { + formState[FormStateControlBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Retrieves the CreateParams used to create the window. + /// If a subclass overrides this function, it must call the base implementation. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + if (this.IsHandleCreated && (this.WindowStyle & NativeMethods.WS_DISABLED) != 0) + { + // Forms that are parent of a modal dialog must keep their WS_DISABLED style (VSWhidbey 449309) + cp.Style |= NativeMethods.WS_DISABLED; + } + else if (TopLevel) + { + // It doesn't seem to make sense to allow a top-level form to be disabled + // + cp.Style &= (~NativeMethods.WS_DISABLED); + } + + if (TopLevel && (formState[FormStateLayered] != 0)) { + cp.ExStyle |= NativeMethods.WS_EX_LAYERED; + } + + IWin32Window dialogOwner = (IWin32Window)Properties.GetObject(PropDialogOwner); + if (dialogOwner != null) { + cp.Parent = Control.GetSafeHandle(dialogOwner); + } + + FillInCreateParamsBorderStyles(cp); + FillInCreateParamsWindowState(cp); + FillInCreateParamsBorderIcons(cp); + + if (formState[FormStateTaskBar] != 0) { + cp.ExStyle |= NativeMethods.WS_EX_APPWINDOW; + } + + FormBorderStyle borderStyle = FormBorderStyle; + if (!ShowIcon && + (borderStyle == FormBorderStyle.Sizable || + borderStyle == FormBorderStyle.Fixed3D || + borderStyle == FormBorderStyle.FixedSingle)) + { + cp.ExStyle |= NativeMethods.WS_EX_DLGMODALFRAME; + } + + if (IsMdiChild) { + if (Visible + && (WindowState == FormWindowState.Maximized + || WindowState == FormWindowState.Normal)) { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + Form form = formMdiParent.ActiveMdiChildInternal; + + if (form != null + && form.WindowState == FormWindowState.Maximized) { + cp.Style |= NativeMethods.WS_MAXIMIZE; + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + SetState(STATE_SIZELOCKEDBYOS, true); + } + } + + if (formState[FormStateMdiChildMax] != 0) { + cp.Style |= NativeMethods.WS_MAXIMIZE; + } + cp.ExStyle |= NativeMethods.WS_EX_MDICHILD; + } + + if (TopLevel || IsMdiChild) { + FillInCreateParamsStartPosition(cp); + // Delay setting to visible until after the handle gets created + // to allow applyClientSize to adjust the size before displaying + // the form. + // + if ((cp.Style & NativeMethods.WS_VISIBLE) != 0) { + formState[FormStateShowWindowOnCreate] = 1; + cp.Style &= (~NativeMethods.WS_VISIBLE); + } + else { + formState[FormStateShowWindowOnCreate] = 0; + } + } + + if (IsRestrictedWindow) { + cp.Caption = RestrictedWindowText(cp.Caption); + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + internal CloseReason CloseReason { + get { return closeReason; } + set { closeReason = value; } + } + /// + /// The default icon used by the Form. This is the standard "windows forms" icon. + /// + internal static Icon DefaultIcon { + get { + // Avoid locking if the value is filled in... + // + if (defaultIcon == null) { + lock(internalSyncObject) { + // Once we grab the lock, we re-check the value to avoid a + // race condition. + // + if (defaultIcon == null) { + defaultIcon = new Icon(typeof(Form), "wfc.ico"); + } + } + } + return defaultIcon; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.NoControl; + } + } + + /// + /// The default icon used by the Form. This is the standard "windows forms" icon. + /// + private static Icon DefaultRestrictedIcon { + get { + // Note: We do this as a static property to allow delay + // loading of the resource. There are some issues with doing + // an OleInitialize from a static constructor... + // + + // Avoid locking if the value is filled in... + // + if (defaultRestrictedIcon == null) { + lock (internalSyncObject) + { + // Once we grab the lock, we re-check the value to avoid a + // race condition. + // + if (defaultRestrictedIcon == null) { + defaultRestrictedIcon = new Icon(typeof(Form), "wfsecurity.ico"); + } + } + } + return defaultRestrictedIcon; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(300, 300); + } + } + + /// + /// + /// Gets or sets the size and location of the form on the Windows desktop. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDesktopBoundsDescr) + ] + public Rectangle DesktopBounds { + get { + Rectangle screen = SystemInformation.WorkingArea; + Rectangle bounds = Bounds; + bounds.X -= screen.X; + bounds.Y -= screen.Y; + return bounds; + } + + set { + SetDesktopBounds(value.X, value.Y, value.Width, value.Height); + } + } + + /// + /// + /// Gets or sets the location of the form on the Windows desktop. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDesktopLocationDescr) + ] + public Point DesktopLocation { + get { + Rectangle screen = SystemInformation.WorkingArea; + Point loc = Location; + loc.X -= screen.X; + loc.Y -= screen.Y; + return loc; + } + + set { + SetDesktopLocation(value.X, value.Y); + } + } + + /// + /// + /// Gets or sets the dialog result for the form. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormDialogResultDescr) + ] + public DialogResult DialogResult { + get { + return dialogResult; + } + + set { + //valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DialogResult.None, (int)DialogResult.No)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DialogResult)); + } + + dialogResult = value; + } + } + + internal override bool HasMenu { + get { + bool hasMenu = false; + + // VSWhidbey 94975: Verify that the menu actually contains items so that any + // size calculations will only include a menu height if the menu actually exists. + // Note that Windows will not draw a menu bar for a menu that does not contain + // any items. + Menu menu = Menu; + if (TopLevel && menu != null && menu.ItemCount > 0) { + hasMenu = true; + } + return hasMenu; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether a + /// help button should be displayed in the caption box of the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(false), + SRDescription(SR.FormHelpButtonDescr) + ] + public bool HelpButton { + get { + return formState[FormStateHelpButton] != 0; + } + + set { + if (value) { + formState[FormStateHelpButton] = 1; + } + else { + formState[FormStateHelpButton] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// [To be supplied.] + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + SRCategory(SR.CatBehavior), + SRDescription(SR.FormHelpButtonClickedDescr) + ] + public event CancelEventHandler HelpButtonClicked { + add { + Events.AddHandler(EVENT_HELPBUTTONCLICKED, value); + } + remove { + Events.RemoveHandler(EVENT_HELPBUTTONCLICKED, value); + } + } + + /// + /// + /// + /// Gets or sets the icon for the form. + /// + /// + [ + AmbientValue(null), + Localizable(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormIconDescr) + ] + public Icon Icon { + get { + if (formState[FormStateIconSet] == 0) { + // In restricted mode, the security icon cannot be changed. + if (IsRestrictedWindow) { + return DefaultRestrictedIcon; + } + else { + return DefaultIcon; + } + } + + return icon; + } + + set { + if (icon != value && !IsRestrictedWindow) { + + // If the user is poking the default back in, + // treat this as a null (reset). + // + if (value == defaultIcon) { + value = null; + } + + // And if null is passed, reset the icon. + // + formState[FormStateIconSet] = (value == null ? 0 : 1); + this.icon = value; + + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + + UpdateWindowIcon(true); + } + } + } + + /// + /// Determines whether the window is closing. + /// + private bool IsClosing { + get { + return formStateEx[FormStateExWindowClosing] == 1; + } + set { + formStateEx[FormStateExWindowClosing] = value ? 1 : 0; + } + } + + // VSWhidbey 517376: returns a more accurate statement of whether or not the form is maximized. + // during handle creation, an MDIChild is created as not maximized. + private bool IsMaximized { + get { + return (WindowState == FormWindowState.Maximized || (IsMdiChild && (formState[FormStateMdiChildMax] ==1))); + } + } + + /// + /// + /// + /// Gets a value indicating whether the form is a multiple document + /// interface (MDI) child form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormIsMDIChildDescr) + ] + public bool IsMdiChild { + get { + return (Properties.GetObject(PropFormMdiParent) != null); + } + } + + // Created for fix VSWhidbey 182131. Deactivates active MDI child and temporarily marks it as unfocusable, + // so that WM_SETFOCUS sent to MDIClient does not activate that child. (See MdiClient.WndProc). + internal bool IsMdiChildFocusable { + get { + if (this.Properties.ContainsObject(PropMdiChildFocusable)) { + return (bool) this.Properties.GetObject(PropMdiChildFocusable); + } + return false; + } + set { + if (value != this.IsMdiChildFocusable) { + this.Properties.SetObject(PropMdiChildFocusable, value); + } + } + } + + + /// + /// + /// + /// Gets or sets a value indicating whether the form is a container for multiple document interface + /// (MDI) child forms. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(false), + SRDescription(SR.FormIsMDIContainerDescr) + ] + public bool IsMdiContainer { + get { + return ctlClient != null; + } + + set { + if (value == IsMdiContainer) + return; + + if (value) { + Debug.Assert(ctlClient == null, "why isn't ctlClient null"); + AllowTransparency = false; + Controls.Add(new MdiClient()); + } + else { + Debug.Assert(ctlClient != null, "why is ctlClient null"); + ActiveMdiChildInternal = null; + ctlClient.Dispose(); + } + //since we paint the background when mdi is true, we need + //to invalidate here + // + Invalidate(); + } + } + + /// + /// + /// + /// Determines if this form should display a warning banner + /// when the form is displayed in an unsecure mode. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsRestrictedWindow { + get { + /// SECREVIEW: make sure to keep changes here in sync with ToolStripDropDown.IsRestrictedWindow + if (formState[FormStateIsRestrictedWindowChecked] == 0) { + formState[FormStateIsRestrictedWindow] = 0; + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Checking for restricted window..."); + Debug.Indent(); +#if DEBUG + if (AlwaysRestrictWindows.Enabled) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Always restricted switch is on..."); + formState[FormStateIsRestrictedWindow] = 1; + formState[FormStateIsRestrictedWindowChecked] = 1; + Debug.Unindent(); + return true; + } +#endif + + try { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "WindowAdornmentModification Demanded"); + IntSecurity.WindowAdornmentModification.Demand(); + } + catch (SecurityException) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Caught security exception, we are restricted..."); + formState[FormStateIsRestrictedWindow] = 1; + } + catch { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Caught non-security exception..."); + Debug.Unindent(); + formState[FormStateIsRestrictedWindow] = 1; // To be on the safe side + formState[FormStateIsRestrictedWindowChecked] = 1; + throw; + } + Debug.Unindent(); + formState[FormStateIsRestrictedWindowChecked] = 1; + } + + return formState[FormStateIsRestrictedWindow] != 0; + } + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the form will receive key events + /// before the event is passed to the control that has focus. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.FormKeyPreviewDescr) + ] + public bool KeyPreview { + get { + return formState[FormStateKeyPreview] != 0; + } + set { + if (value) { + formState[FormStateKeyPreview] = 1; + } + else { + formState[FormStateKeyPreview] = 0; + } + } + } + + /// + /// + /// + /// Gets or sets the location of the form. + /// + /// + [SettingsBindable(true)] + public new Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + + /// + /// + /// + /// Gets the size of the form when it is + /// maximized. + /// + /// + protected Rectangle MaximizedBounds { + get { + return Properties.GetRectangle(PropMaximizedBounds); + } + set { + if (!value.Equals( MaximizedBounds )) { + Properties.SetRectangle(PropMaximizedBounds, value); + OnMaximizedBoundsChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_MAXIMIZEDBOUNDSCHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMaximizedBoundsChangedDescr)] + public event EventHandler MaximizedBoundsChanged { + add { + Events.AddHandler(EVENT_MAXIMIZEDBOUNDSCHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MAXIMIZEDBOUNDSCHANGED, value); + } + } + + /// + /// + /// + /// Gets the maximum size the form can be resized to. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormMaximumSizeDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(typeof(Size), "0, 0") + ] + public override Size MaximumSize { + get { + if (Properties.ContainsInteger(PropMaxTrackSizeWidth)) { + return new Size(Properties.GetInteger(PropMaxTrackSizeWidth), Properties.GetInteger(PropMaxTrackSizeHeight)); + } + return Size.Empty; + } + set { + if (!value.Equals( MaximumSize )) { + + if (value.Width < 0 || value.Height < 0 ) { + throw new ArgumentOutOfRangeException("MaximumSize"); + } + + Properties.SetInteger(PropMaxTrackSizeWidth, value.Width); + Properties.SetInteger(PropMaxTrackSizeHeight, value.Height); + + // Bump minimum size if necessary + // + if (!MinimumSize.IsEmpty && !value.IsEmpty) { + + if (Properties.GetInteger(PropMinTrackSizeWidth) > value.Width) { + Properties.SetInteger(PropMinTrackSizeWidth, value.Width); + } + + if (Properties.GetInteger(PropMinTrackSizeHeight) > value.Height) { + Properties.SetInteger(PropMinTrackSizeHeight, value.Height); + } + } + + // Keep form size within new limits + // + Size size = Size; + if (!value.IsEmpty && (size.Width > value.Width || size.Height > value.Height)) { + Size = new Size(Math.Min(size.Width, value.Width), Math.Min(size.Height, value.Height)); + } + + OnMaximumSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMaximumSizeChangedDescr)] + public event EventHandler MaximumSizeChanged { + add { + Events.AddHandler(EVENT_MAXIMUMSIZECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MAXIMUMSIZECHANGED, value); + } + } + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(null), + SRDescription(SR.FormMenuStripDescr), + TypeConverter(typeof(ReferenceConverter)) + ] + public MenuStrip MainMenuStrip { + get { + return (MenuStrip)Properties.GetObject(PropMainMenuStrip); + } + set { + Properties.SetObject(PropMainMenuStrip, value); + if (IsHandleCreated && Menu == null) { + UpdateMenuHandles(); + } + } + } + + /// + /// + /// Hide Margin/MarginChanged + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new Padding Margin { + get { return base.Margin; } + set { + base.Margin = value; + } + } + + + /// + /// + /// Hide Margin/MarginChanged + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MarginChanged { + add { + base.MarginChanged += value; + } + remove { + base.MarginChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the + /// that is displayed in the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(null), + SRDescription(SR.FormMenuDescr), + TypeConverter(typeof(ReferenceConverter)), + Browsable(false), + ] + public MainMenu Menu { + get { + return (MainMenu)Properties.GetObject(PropMainMenu); + } + set { + MainMenu mainMenu = Menu; + + if (mainMenu != value) { + if (mainMenu != null) { + mainMenu.form = null; + } + + Properties.SetObject(PropMainMenu, value); + + if (value != null) { + if (value.form != null) { + value.form.Menu = null; + } + value.form = this; + } + + if (formState[FormStateSetClientSize] == 1 && !IsHandleCreated) { + ClientSize = ClientSize; + } + + + MenuChanged(Windows.Forms.Menu.CHANGE_ITEMS, value); + } + } + } + + /// + /// + /// + /// Gets the minimum size the form can be resized to. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormMinimumSizeDescr), + RefreshProperties(RefreshProperties.Repaint), + ] + public override Size MinimumSize { + get { + if (Properties.ContainsInteger(PropMinTrackSizeWidth)) { + return new Size(Properties.GetInteger(PropMinTrackSizeWidth), Properties.GetInteger(PropMinTrackSizeHeight)); + } + return DefaultMinimumSize; + } + set { + if (!value.Equals( MinimumSize )) { + + if (value.Width < 0 || value.Height < 0 ) { + throw new ArgumentOutOfRangeException("MinimumSize"); + } + + // ensure that the size we've applied fits into the screen + // when IsRestrictedWindow. + Rectangle bounds = this.Bounds; + bounds.Size = value; + value = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(bounds).Size; + + Properties.SetInteger(PropMinTrackSizeWidth, value.Width); + Properties.SetInteger(PropMinTrackSizeHeight, value.Height); + + // Bump maximum size if necessary + // + if (!MaximumSize.IsEmpty && !value.IsEmpty) { + + if (Properties.GetInteger(PropMaxTrackSizeWidth) < value.Width) { + Properties.SetInteger(PropMaxTrackSizeWidth, value.Width); + } + + if (Properties.GetInteger(PropMaxTrackSizeHeight) < value.Height) { + Properties.SetInteger(PropMaxTrackSizeHeight, value.Height); + } + } + + // Keep form size within new limits + // + Size size = Size; + if (size.Width < value.Width || size.Height < value.Height) { + Size = new Size(Math.Max(size.Width, value.Width), Math.Max(size.Height, value.Height)); + } + + if (IsHandleCreated) { + // VSWhidbey 95302: "Move" the form to the same size and position to prevent windows from moving it + // when the user tries to grab a resizing border. + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.NullHandleRef, + Location.X, + Location.Y, + Size.Width, + Size.Height, + NativeMethods.SWP_NOZORDER); + } + + OnMinimumSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.FormOnMinimumSizeChangedDescr)] + public event EventHandler MinimumSizeChanged { + add { + Events.AddHandler(EVENT_MINIMUMSIZECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_MINIMUMSIZECHANGED, value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the maximize button is + /// displayed in the caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormMaximizeBoxDescr) + ] + public bool MaximizeBox { + get { + return formState[FormStateMaximizeBox] != 0; + } + set { + if (value) { + formState[FormStateMaximizeBox] = 1; + } + else { + formState[FormStateMaximizeBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Gets an array of forms that represent the + /// multiple document interface (MDI) child forms that are parented to this + /// form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMDIChildrenDescr) + ] + public Form[] MdiChildren { + get { + if (ctlClient != null) { + return ctlClient.MdiChildren; + } + else { + return new Form[0]; + } + } + } + + /// + /// + /// Gets the MDIClient that the MDI container form is using to contain Multiple Document Interface (MDI) child forms, + /// if this is an MDI container form. + /// Represents the client area of a Multiple Document Interface (MDI) Form window, also known as the MDI child window. + /// + /// + internal MdiClient MdiClient { + get { + return this.ctlClient; + } + } + + /// + /// + /// Indicates the current multiple document + /// interface (MDI) parent form of this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMDIParentDescr) + ] + public Form MdiParent { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "GetParent Demanded"); + IntSecurity.GetParent.Demand(); + + return MdiParentInternal; + } + set { + MdiParentInternal = value; + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + private Form MdiParentInternal { + get { + return (Form)Properties.GetObject(PropFormMdiParent); + } + set { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (value == formMdiParent && (value != null || ParentInternal == null)) { + return; + } + + if (value != null && this.CreateThreadId != value.CreateThreadId) { + throw new ArgumentException(SR.GetString(SR.AddDifferentThreads), "value"); + } + + bool oldVisibleBit = GetState(STATE_VISIBLE); + //bug(108303) .. this should apply whether or not value == null. + Visible = false; + + try { + if (value == null) { + ParentInternal = null; + // VSWhidbey 429924 - not calling SetTopLevelInternal so that IntSecurity.TopLevelWindow.Demand() isn't skipped. + SetTopLevel(true); + } + else { + if (IsMdiContainer) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentAndChild), "value"); + } + if (!value.IsMdiContainer) { + throw new ArgumentException(SR.GetString(SR.MDIParentNotContainer), "value"); + } + + // Setting TopLevel forces a handle recreate before Parent is set, + // which causes problems because we try to assign an MDI child to the parking window, + // which can't take MDI children. So we explicitly destroy and create the handle here. + + Dock = DockStyle.None; + Properties.SetObject(PropFormMdiParent, value); + + SetState(STATE_TOPLEVEL, false); + ParentInternal = value.MdiClient; + + // If it is an MDIChild, and it is not yet visible, we'll + // hold off on recreating the window handle. We'll do that + // when MdiChild's visibility is set to true (see bug 124232). + + // But if the handle has already been created, we need to destroy it + // so the form gets MDI-parented properly. See bug#382992. + if (ParentInternal.IsHandleCreated && IsMdiChild && IsHandleCreated) { + DestroyHandle(); + } + } + InvalidateMergedMenu(); + UpdateMenuHandles(); + } + finally { + UpdateStyles(); + Visible = oldVisibleBit; + } + } + } + + private MdiWindowListStrip MdiWindowListStrip { + get { return Properties.GetObject(PropMdiWindowListStrip) as MdiWindowListStrip; } + set { Properties.SetObject(PropMdiWindowListStrip, value); } + } + + private MdiControlStrip MdiControlStrip { + get { return Properties.GetObject(PropMdiControlStrip) as MdiControlStrip; } + set { Properties.SetObject(PropMdiControlStrip, value); } + } + + /// + /// + /// + /// Gets the merged menu for the + /// form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormMergedMenuDescr), + ] + public MainMenu MergedMenu { + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + get { + return this.MergedMenuPrivate; + } + } + + private MainMenu MergedMenuPrivate { + get { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent == null) return null; + + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + if (mergedMenu != null) return mergedMenu; + + MainMenu parentMenu = formMdiParent.Menu; + MainMenu mainMenu = Menu; + + if (mainMenu == null) return parentMenu; + if (parentMenu == null) return mainMenu; + + // Create a menu that merges the two and save it for next time. + mergedMenu = new MainMenu(); + mergedMenu.ownerForm = this; + mergedMenu.MergeMenu(parentMenu); + mergedMenu.MergeMenu(mainMenu); + Properties.SetObject(PropMergedMenu, mergedMenu); + return mergedMenu; + } + } + + /// + /// + /// Gets or sets a value indicating whether the minimize button is displayed in the caption bar of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(true), + SRDescription(SR.FormMinimizeBoxDescr) + ] + public bool MinimizeBox { + get { + return formState[FormStateMinimizeBox] != 0; + } + set { + if (value) { + formState[FormStateMinimizeBox] = 1; + } + else { + formState[FormStateMinimizeBox] = 0; + } + UpdateFormStyles(); + } + } + + /// + /// + /// + /// Gets a value indicating whether this form is + /// displayed modally. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormModalDescr) + ] + public bool Modal { + get { + return GetState(STATE_MODAL); + } + } + + /// + /// + /// Determines the opacity of the form. This can only be set on top level + /// controls. Opacity requires Windows 2000 or later, and is ignored on earlier + /// operating systems. + /// + [ + SRCategory(SR.CatWindowStyle), + TypeConverterAttribute(typeof(OpacityConverter)), + SRDescription(SR.FormOpacityDescr), + DefaultValue(1.0) + ] + public double Opacity { + get { + object opacity = Properties.GetObject(PropOpacity); + if (opacity != null) { + return Convert.ToDouble(opacity, CultureInfo.InvariantCulture); + } + else { + return 1.0f; + } + } + set { + // In restricted mode a form cannot be made less visible than 50% opacity. + if (IsRestrictedWindow) { + value = Math.Max(value, .50f); + } + + if (value > 1.0) { + value = 1.0f; + } + else if (value < 0.0) { + value = 0.0f; + } + + Properties.SetObject(PropOpacity, value); + + bool oldLayered = (formState[FormStateLayered] != 0); + + if (OpacityAsByte < 255 && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) + { + AllowTransparency = true; + if (formState[FormStateLayered] != 1) { + formState[FormStateLayered] = 1; + if (!oldLayered) { + UpdateStyles(); + } + } + } + else { + formState[FormStateLayered] = (this.TransparencyKey != Color.Empty) ? 1 : 0; + if (oldLayered != (formState[FormStateLayered] != 0)) { + int exStyle = unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE)); + CreateParams cp = CreateParams; + if (exStyle != cp.ExStyle) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_EXSTYLE, new HandleRef(null, (IntPtr)cp.ExStyle)); + } + } + } + + UpdateLayered(); + } + } + + private byte OpacityAsByte { + get { + return (byte)(Opacity * 255.0f); + } + } + + /// + /// + /// Gets an array of objects that represent all forms that are owned by this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormOwnedFormsDescr) + ] + public Form[] OwnedForms { + get { + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + Form[] result = new Form[ownedFormsCount]; + if (ownedFormsCount > 0) { + Array.Copy(ownedForms, 0, result, 0, ownedFormsCount); + } + + return result; + } + } + + /// + /// + /// Gets or sets the form that owns this form. + /// + [ + SRCategory(SR.CatWindowStyle), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormOwnerDescr) + ] + public Form Owner { + get { + IntSecurity.GetParent.Demand(); + return OwnerInternal; + } + set { + Form ownerOld = OwnerInternal; + if (ownerOld == value) + return; + + if (value != null && !TopLevel) { + throw new ArgumentException(SR.GetString(SR.NonTopLevelCantHaveOwner), "value"); + } + + CheckParentingCycle(this, value); + CheckParentingCycle(value, this); + + Properties.SetObject(PropOwner, null); + + if (ownerOld != null) { + ownerOld.RemoveOwnedForm(this); + } + + Properties.SetObject(PropOwner, value); + + if (value != null) { + value.AddOwnedForm(this); + } + + UpdateHandleWithOwner(); + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal Form OwnerInternal { + get { + return (Form)Properties.GetObject(PropOwner); + } + } + + /// + /// + /// Gets or sets the restored bounds of the Form. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public Rectangle RestoreBounds { + get { + if (restoreBounds.Width == -1 + && restoreBounds.Height == -1 + && restoreBounds.X == -1 + && restoreBounds.Y == -1) { + // VSWhidbey 470435: Form scaling depends on this property being + // set correctly. In some cases (where the size has not yet been set or + // has only been set to the default, restoreBounds will remain uninitialized until the + // handle has been created. In this case, return the current Bounds. + return Bounds; + } + return restoreBounds; + } + } + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + // SECURITY WARNING: This property bypasses a security demand. Use with caution! + internal override Control ParentInternal { + get { + return base.ParentInternal; + } + set { + if (value != null) { + Owner = null; + } + base.ParentInternal = value; + } + } + + /// + /// + /// If ShowInTaskbar is true then the form will be displayed + /// in the Windows Taskbar. + /// + [ + DefaultValue(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormShowInTaskbarDescr) + ] + public bool ShowInTaskbar { + get { + return formState[FormStateTaskBar] != 0; + } + set { + // Restricted windows must always show in task bar. + if (IsRestrictedWindow) { + return; + } + + if (ShowInTaskbar != value) { + if (value) { + formState[FormStateTaskBar] = 1; + } + else { + formState[FormStateTaskBar] = 0; + } + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// Gets or sets a value indicating whether an icon is displayed in the + /// caption bar of the form. + /// If ControlBox == false, then the icon won't be shown no matter what + /// the value of ShowIcon is + /// + [ + DefaultValue(true), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormShowIconDescr) + ] + public bool ShowIcon { + get { + return formStateEx[FormStateExShowIcon] != 0; + } + + set { + if (value) { + formStateEx[FormStateExShowIcon] = 1; + } + else { + // The icon must always be shown for restricted forms. + if (IsRestrictedWindow) { + return; + } + formStateEx[FormStateExShowIcon] = 0; + UpdateStyles(); + } + UpdateWindowIcon(true); + } + } + + internal override int ShowParams { + get { + // From MSDN: + // The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter. + + // As noted in the discussion of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the STARTUPINFO structure. In this case, ShowWindow uses the information specified in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup information provided by the program that launched the application. This behavior is designed for the following situations: + // + // Applications create their main window by calling CreateWindow with the WS_VISIBLE flag set. + // Applications create their main window by calling CreateWindow with the WS_VISIBLE flag cleared, and later call ShowWindow with the SW_SHOW flag set to make it visible. + // + + switch(WindowState) { + case FormWindowState.Maximized: + return NativeMethods.SW_SHOWMAXIMIZED; + case FormWindowState.Minimized: + return NativeMethods.SW_SHOWMINIMIZED; + } + if (ShowWithoutActivation) + { + return NativeMethods.SW_SHOWNOACTIVATE; + } + return NativeMethods.SW_SHOW; + } + } + + /// + /// + /// When this property returns true, the internal ShowParams property will return NativeMethods.SW_SHOWNOACTIVATE. + /// + /// + [Browsable(false)] + protected virtual bool ShowWithoutActivation + { + get + { + return false; + } + } + + /// + /// + /// + /// Gets or sets the size of the form. + /// + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Localizable(false) + ] + new public Size Size { + get { + return base.Size; + } + set { + base.Size = value; + } + } + + /// + /// + /// + /// Gets or sets the style of size grip to display in the lower-left corner of the form. + /// + /// + [ + SRCategory(SR.CatWindowStyle), + DefaultValue(SizeGripStyle.Auto), + SRDescription(SR.FormSizeGripStyleDescr) + ] + public SizeGripStyle SizeGripStyle { + get { + return(SizeGripStyle)formState[FormStateSizeGripStyle]; + } + set { + if (SizeGripStyle != value) { + //do some bounds checking here + // + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SizeGripStyle.Auto, (int)SizeGripStyle.Hide)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SizeGripStyle)); + } + + formState[FormStateSizeGripStyle] = (int)value; + UpdateRenderSizeGrip(); + } + } + } + + /// + /// + /// + /// Gets or sets the + /// starting position of the form at run time. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatLayout), + DefaultValue(FormStartPosition.WindowsDefaultLocation), + SRDescription(SR.FormStartPositionDescr) + ] + public FormStartPosition StartPosition { + get { + return(FormStartPosition)formState[FormStateStartPos]; + } + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormStartPosition.Manual, (int)FormStartPosition.CenterParent)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormStartPosition)); + } + formState[FormStateStartPos] = (int)value; + } + } + + /// + /// + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + new public int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + /// + /// This property has no effect on Form, we need to hide it from browsers. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// For forms that are show in task bar false, this returns a HWND + /// they must be parented to in order for it to work. + /// + private HandleRef TaskbarOwner { + get { + if (ownerWindow == null) { + ownerWindow = new NativeWindow(); + } + + if (ownerWindow.Handle == IntPtr.Zero) { + CreateParams cp = new CreateParams(); + cp.ExStyle = NativeMethods.WS_EX_TOOLWINDOW; + ownerWindow.CreateHandle(cp); + } + + return new HandleRef(ownerWindow, ownerWindow.Handle); + } + } + + [SettingsBindable(true)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether to display the form as a top-level + /// window. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool TopLevel { + get { + return GetTopLevel(); + } + set { + if (!value && ((Form)this).IsMdiContainer && !DesignMode) { + throw new ArgumentException(SR.GetString(SR.MDIContainerMustBeTopLevel), "value"); + } + SetTopLevel(value); + } + } + + /// + /// + /// Gets or sets a value indicating whether the form should be displayed as the top-most + /// form of your application. + /// + [ + DefaultValue(false), + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormTopMostDescr) + ] + public bool TopMost { + get { + return formState[FormStateTopMost] != 0; + } + set { + // Restricted windows cannot be top most to avoid DOS attack by obscuring other windows. + if (IsRestrictedWindow) { + return; + } + + if (IsHandleCreated && TopLevel) { + HandleRef key = value ? NativeMethods.HWND_TOPMOST : NativeMethods.HWND_NOTOPMOST; + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), key, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE); + } + + if (value) { + formState[FormStateTopMost] = 1; + } + else { + formState[FormStateTopMost] = 0; + } + } + } + + /// + /// + /// Gets or sets the color that will represent transparent areas of the form. + /// + [ + SRCategory(SR.CatWindowStyle), + SRDescription(SR.FormTransparencyKeyDescr) + ] + public Color TransparencyKey { + get { + object key = Properties.GetObject(PropTransparencyKey); + if (key != null) { + return (Color)key; + } + return Color.Empty; + } + set { + Properties.SetObject(PropTransparencyKey, value); + if (!IsMdiContainer) { + bool oldLayered = (formState[FormStateLayered] == 1); + if (value != Color.Empty) + { + IntSecurity.TransparentWindows.Demand(); + AllowTransparency = true; + formState[FormStateLayered] = 1; + } + else + { + formState[FormStateLayered] = (this.OpacityAsByte < 255) ? 1 : 0; + } + if (oldLayered != (formState[FormStateLayered] != 0)) + { + UpdateStyles(); + } + UpdateLayered(); + } + } + } + + /// + // + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetVisibleCore(bool value) { + Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Form::SetVisibleCore(" + value.ToString() + ") - " + this.Name); + + // If DialogResult.OK and the value == GetVisibleCore() then this code has been called either through + // ShowDialog( ) or explicit Hide( ) by the user. So dont go through this function again. + // This will avoid flashing during closing the dialog; refer to VsWhidbey : 456449. + if (GetVisibleCore() == value && dialogResult == DialogResult.OK) + { + return; + } + + // (!value || calledMakeVisible) is to make sure that we fall + // through and execute the code below atleast once. + if (GetVisibleCore() == value && (!value || CalledMakeVisible)) { + base.SetVisibleCore(value); + return; + } + + if (value) { + CalledMakeVisible = true; + if (CalledCreateControl) { + if (CalledOnLoad) { + // VSWhidbey 518085: make sure the form is in the Application.OpenForms collection + if (!Application.OpenFormsInternal.Contains(this)) { + Application.OpenFormsInternalAdd(this); + } + } + else { + CalledOnLoad = true; + OnLoad(EventArgs.Empty); + if (dialogResult != DialogResult.None) { + // Don't show the dialog if the dialog result was set + // in the OnLoad event. + // + value = false; + } + } + } + } + else { + ResetSecurityTip(true /* modalOnly */); + } + + if (!IsMdiChild) { + base.SetVisibleCore(value); + + // We need to force this call if we were created + // with a STARTUPINFO structure (e.g. launched from explorer), since + // it won't send a WM_SHOWWINDOW the first time it's called. + // when WM_SHOWWINDOW gets called, we'll flip this bit to true + // + if (0==formState[FormStateSWCalled]) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_SHOWWINDOW, value ? 1 : 0, 0); + } + } + else { + // Throw away any existing handle. + if (IsHandleCreated) { + // VSWhidbey 430476 - Everett/RTM used to wrap this in an assert for AWP. + DestroyHandle(); + } + + if (!value) { + InvalidateMergedMenu(); + SetState(STATE_VISIBLE, false); + } + else { + + // The ordering is important here... Force handle creation + // (getHandle) then show the window (ShowWindow) then finish + // creating children using createControl... + // + SetState(STATE_VISIBLE, true); + + // VSWhidbey 93522: Ask the mdiClient to re-layout the controls so that any docking or + // anchor settings for this mdi child window will be honored. + MdiParentInternal.MdiClient.PerformLayout(); + + if (ParentInternal != null && ParentInternal.Visible) { + SuspendLayout(); + try{ + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), NativeMethods.SW_SHOW); + CreateControl(); + + // If this form is mdichild and maximized, we need to redraw the MdiParent non-client area to + // update the menu bar because we always create the window as if it were not maximized. + // See comment on CreateHandle about this. VSWhidbey#93518 + if (WindowState == FormWindowState.Maximized) { + MdiParentInternal.UpdateWindowIcon(true); + } + } + finally{ + ResumeLayout(); + } + } + } + OnVisibleChanged(EventArgs.Empty); + } + + //(bug 111549)... For FormWindowState.Maximized.. Wm_activate is not Fired before setting Focus + //on Active Control.. + + if (value && !IsMdiChild && (WindowState == FormWindowState.Maximized || TopMost)) { + if (ActiveControl == null){ + SelectNextControlInternal(null, true, true, true, false); + } + FocusActiveControlInternal(); + } + } + + /// + /// + /// Gets or sets the form's window state. + /// + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(FormWindowState.Normal), + SRDescription(SR.FormWindowStateDescr) + ] + public FormWindowState WindowState { + get { + return(FormWindowState)formState[FormStateWindowState]; + } + set { + + //verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FormWindowState.Normal, (int)FormWindowState.Maximized)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FormWindowState)); + } + + if (TopLevel && IsRestrictedWindow) { + // We don't allow to minimize or maximze a top level window programatically if it is restricted. + // When maximized, the desktop is obscured by the window (DOS attack) and when minimize spoofing + // identity is the thread, the minimized window could steal the user's keystrokes and obtain a + // password for instance. + if (value != FormWindowState.Normal) { + return; + } + } + + switch (value) { + case FormWindowState.Normal: + SetState(STATE_SIZELOCKEDBYOS, false); + break; + case FormWindowState.Maximized: + case FormWindowState.Minimized: + SetState(STATE_SIZELOCKEDBYOS, true); + break; + } + + if (IsHandleCreated && Visible) { + IntPtr hWnd = Handle; + switch (value) { + case FormWindowState.Normal: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_NORMAL); + break; + case FormWindowState.Maximized: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_MAXIMIZE); + break; + case FormWindowState.Minimized: + SafeNativeMethods.ShowWindow(new HandleRef(this, hWnd), NativeMethods.SW_MINIMIZE); + break; + } + } + + // VSWhidbey 95295: Now set the local property to the passed in value so that + // when UpdateWindowState is by the ShowWindow call above, the window state in effect when + // this call was made will still be effective while processing that method. + formState[FormStateWindowState] = (int)value; + } + } + + /// + /// + /// + /// Gets or sets the text to display in the caption bar of the form. + /// + /// + internal override string WindowText { + get { + // In restricted mode, the windows caption (Text) is modified to show the url of the window. + // The userWindowText is used to cache the user's text. + if (IsRestrictedWindow && formState[FormStateIsWindowActivated] == 1) { + if (userWindowText == null) { + return ""; + } + return userWindowText; + } + + return base.WindowText; + + } + + set { + string oldText = this.WindowText; + + userWindowText = value; + + if (IsRestrictedWindow && formState[FormStateIsWindowActivated] == 1) { + if (value == null) { + value = ""; + } + base.WindowText = RestrictedWindowText(value); + } + else { + base.WindowText = value; + } + + // For non-default FormBorderStyles, we do not set the WS_CAPTION style if the Text property is null or "". + // When we reload the form from code view, the text property is not set till the very end, and so we do not + // end up updating the CreateParams with WS_CAPTION. Fixed this by making sure we call UpdateStyles() when + // we transition from a non-null value to a null value or vice versa in Form.WindowText. + // + if (oldText == null || (oldText.Length == 0)|| value == null || (value.Length == 0)) { + UpdateFormStyles(); + } + } + } + + /// + /// + /// Occurs when the form is activated in code or by the user. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.FormOnActivateDescr)] + public event EventHandler Activated { + add { + Events.AddHandler(EVENT_ACTIVATED, value); + } + remove { + Events.RemoveHandler(EVENT_ACTIVATED, value); + } + } + + + /// + /// + /// Occurs when the form is closing. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnClosingDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public event CancelEventHandler Closing { + add { + Events.AddHandler(EVENT_CLOSING, value); + } + remove { + Events.RemoveHandler(EVENT_CLOSING, value); + } + } + + + /// + /// + /// Occurs when the form is closed. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnClosedDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public event EventHandler Closed { + add { + Events.AddHandler(EVENT_CLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_CLOSED, value); + } + } + + + /// + /// + /// Occurs when the form loses focus and is not the active form. + /// + [SRCategory(SR.CatFocus), SRDescription(SR.FormOnDeactivateDescr)] + public event EventHandler Deactivate { + add { + Events.AddHandler(EVENT_DEACTIVATE, value); + } + remove { + Events.RemoveHandler(EVENT_DEACTIVATE, value); + } + } + + + /// + /// + /// Occurs when the form is closing. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnFormClosingDescr)] + public event FormClosingEventHandler FormClosing { + add { + Events.AddHandler(EVENT_FORMCLOSING, value); + } + remove { + Events.RemoveHandler(EVENT_FORMCLOSING, value); + } + } + + + /// + /// + /// Occurs when the form is closed. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnFormClosedDescr)] + public event FormClosedEventHandler FormClosed { + add { + Events.AddHandler(EVENT_FORMCLOSED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMCLOSED, value); + } + } + + + /// + /// + /// Occurs before the form becomes visible. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnLoadDescr)] + public event EventHandler Load { + add { + Events.AddHandler(EVENT_LOAD, value); + } + remove { + Events.RemoveHandler(EVENT_LOAD, value); + } + } + + /// + /// + /// Occurs when a Multiple Document Interface (MDI) child form is activated or closed + /// within an MDI application. + /// + [SRCategory(SR.CatLayout), SRDescription(SR.FormOnMDIChildActivateDescr)] + public event EventHandler MdiChildActivate { + add { + Events.AddHandler(EVENT_MDI_CHILD_ACTIVATE, value); + } + remove { + Events.RemoveHandler(EVENT_MDI_CHILD_ACTIVATE, value); + } + } + + /// + /// + /// Occurs when the menu of a form loses focus. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnMenuCompleteDescr), + Browsable(false) + ] + public event EventHandler MenuComplete { + add { + Events.AddHandler(EVENT_MENUCOMPLETE, value); + } + remove { + Events.RemoveHandler(EVENT_MENUCOMPLETE, value); + } + } + + /// + /// + /// Occurs when the menu of a form receives focus. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.FormOnMenuStartDescr), + Browsable(false) + ] + public event EventHandler MenuStart { + add { + Events.AddHandler(EVENT_MENUSTART, value); + } + remove { + Events.RemoveHandler(EVENT_MENUSTART, value); + } + } + + + /// + /// + /// Occurs after the input language of the form has changed. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnInputLangChangeDescr)] + public event InputLanguageChangedEventHandler InputLanguageChanged { + add { + Events.AddHandler(EVENT_INPUTLANGCHANGE, value); + } + remove { + Events.RemoveHandler(EVENT_INPUTLANGCHANGE, value); + } + } + + + /// + /// + /// Occurs when the the user attempts to change the input language for the + /// form. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnInputLangChangeRequestDescr)] + public event InputLanguageChangingEventHandler InputLanguageChanging { + add { + Events.AddHandler(EVENT_INPUTLANGCHANGEREQUEST, value); + } + remove { + Events.RemoveHandler(EVENT_INPUTLANGCHANGEREQUEST, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + } + + /// + /// + /// Occurs whenever the form is first shown. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.FormOnShownDescr)] + public event EventHandler Shown { + add { + Events.AddHandler(EVENT_SHOWN, value); + } + remove { + Events.RemoveHandler(EVENT_SHOWN, value); + } + } + + /// + /// + /// Activates the form and gives it focus. + /// + public void Activate() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + if (Visible && IsHandleCreated) { + if (IsMdiChild) { + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + } + else { + UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this, Handle)); + } + } + } + + /// + /// + /// + /// This function handles the activation of a MDI child form. If a subclass + /// overrides this function, it must call base.ActivateMdiChild. + /// From MSDN: This member supports the .NET Framework infrastructure and is not intended + /// to be used directly from your code. + /// + protected void ActivateMdiChild(Form form) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + ActivateMdiChildInternal(form); + } + + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + private void ActivateMdiChildInternal(Form form) { + if (FormerlyActiveMdiChild != null && !FormerlyActiveMdiChild.IsClosing) { + FormerlyActiveMdiChild.UpdateWindowIcon(true); + FormerlyActiveMdiChild = null; + } + + Form activeMdiChild = ActiveMdiChildInternal; + if (activeMdiChild == form) { + return; + } + + //Don't believe we ever hit this with non-null, but leaving it intact in case removing it would cause a problem. + if (null != activeMdiChild) { + activeMdiChild.Active = false; + } + + activeMdiChild = form; + ActiveMdiChildInternal = form; + + if (null != activeMdiChild) { + activeMdiChild.IsMdiChildFocusable = true; + activeMdiChild.Active = true; + } + else if (this.Active) { + ActivateControlInternal(this); + } + + // Note: It is possible that we are raising this event when the activeMdiChild is null, + // this is the case when the only visible mdi child is being closed. See DeactivateMdiChild + // for more info. + OnMdiChildActivate(EventArgs.Empty); + } + + /// + /// + /// Adds + /// an owned form to this form. + /// + public void AddOwnedForm(Form ownedForm) { + if (ownedForm == null) + return; + + if (ownedForm.OwnerInternal != this) { + ownedForm.Owner = this; // NOTE: this calls AddOwnedForm again with correct owner set. + return; + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + // Make sure this isn't already in the list: + for (int i=0;i < ownedFormsCount;i++) { + if (ownedForms[i]==ownedForm) { + return; + } + } + + if (ownedForms == null) { + ownedForms = new Form[4]; + Properties.SetObject(PropOwnedForms, ownedForms); + } + else if (ownedForms.Length == ownedFormsCount) { + Form[] newOwnedForms = new Form[ownedFormsCount*2]; + Array.Copy(ownedForms, 0, newOwnedForms, 0, ownedFormsCount); + ownedForms = newOwnedForms; + Properties.SetObject(PropOwnedForms, ownedForms); + } + + + ownedForms[ownedFormsCount] = ownedForm; + Properties.SetInteger(PropOwnedFormsCount, ownedFormsCount + 1); + } + + // When shrinking the form (i.e. going from Large Fonts to Small + // Fonts) we end up making everything too small due to roundoff, + // etc... solution - just don't shrink as much. + // + private float AdjustScale(float scale) { + // NOTE : This function is cloned in FormDocumentDesigner... remember to keep + // : them in sync + // + + + // Map 0.0 - .92... increment by 0.08 + // + if (scale < .92f) { + return scale + 0.08f; + } + + // Map .92 - .99 to 1.0 + // + else if (scale < 1.0f) { + return 1.0f; + } + + // Map 1.02... increment by 0.08 + // + else if (scale > 1.01f) { + return scale + 0.08f; + } + + // Map 1.0 - 1.01... no change + // + else { + return scale; + } + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void AdjustFormScrollbars(bool displayScrollbars) { + if (WindowState != FormWindowState.Minimized) { + base.AdjustFormScrollbars(displayScrollbars); + } + } + + private void AdjustSystemMenu(IntPtr hmenu) { + UpdateWindowState(); + FormWindowState winState = WindowState; + FormBorderStyle borderStyle = FormBorderStyle; + bool sizableBorder = (borderStyle == FormBorderStyle.SizableToolWindow + || borderStyle == FormBorderStyle.Sizable); + + + bool showMin = MinimizeBox && winState != FormWindowState.Minimized; + bool showMax = MaximizeBox && winState != FormWindowState.Maximized; + bool showClose = ControlBox; + bool showRestore = winState != FormWindowState.Normal; + bool showSize = sizableBorder && winState != FormWindowState.Minimized + && winState != FormWindowState.Maximized; + + if (!showMin) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MINIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MINIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showMax) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MAXIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_MAXIMIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showClose) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_CLOSE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_CLOSE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showRestore) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_RESTORE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_RESTORE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + if (!showSize) { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_SIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_GRAYED); + } + else { + UnsafeNativeMethods.EnableMenuItem(new HandleRef(this, hmenu), NativeMethods.SC_SIZE, + NativeMethods.MF_BYCOMMAND | NativeMethods.MF_ENABLED); + } + +#if SECURITY_DIALOG + AdjustSystemMenuForSecurity(hmenu); +#endif + } + +#if SECURITY_DIALOG + private void AdjustSystemMenuForSecurity(IntPtr hmenu) { + if (formState[FormStateAddedSecurityMenuItem] == 0) { + formState[FormStateAddedSecurityMenuItem] = 1; + + SecurityMenuItem securitySystemMenuItem = new SecurityMenuItem(this); + Properties.SetObject(PropSecuritySystemMenuItem, securitySystemMenuItem); + + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE | NativeMethods.MIIM_DATA; + info.fType = 0; + info.fState = 0; + info.wID = securitySystemMenuItem.ID; + info.hbmpChecked = IntPtr.Zero; + info.hbmpUnchecked = IntPtr.Zero; + info.dwItemData = IntPtr.Zero; + + // Note: This code is not shipping in the final product. We do not want to measure the + // : performance hit of loading the localized resource for this at startup, so I + // : am hard-wiring the strings below. If you need to localize these, move them to + // : a SECONDARY resource file so we don't have to contend with our big error message + // : file on startup. + // + if (IsRestrictedWindow) { + info.dwTypeData = ".NET Restricted Window..."; + } + else { + info.dwTypeData = ".NET Window..."; + } + info.cch = 0; + UnsafeNativeMethods.InsertMenuItem(new HandleRef(this, hmenu), 0, true, info); + + + NativeMethods.MENUITEMINFO_T sep = new NativeMethods.MENUITEMINFO_T(); + sep.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE | NativeMethods.MIIM_DATA; + sep.fType = NativeMethods.MFT_MENUBREAK; + UnsafeNativeMethods.InsertMenuItem(new HandleRef(this, hmenu), 1, true, sep); + + } + } +#endif + + /// + /// This forces the SystemMenu to look like we want. + /// + /// + private void AdjustSystemMenu() { + if (IsHandleCreated) { + IntPtr hmenu = UnsafeNativeMethods.GetSystemMenu(new HandleRef(this, Handle), false); + AdjustSystemMenu(hmenu); + hmenu = IntPtr.Zero; + } + } + + /// + /// + /// This auto scales the form based on the AutoScaleBaseSize. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This method has been deprecated. Use the ApplyAutoScaling method instead. http://go.microsoft.com/fwlink/?linkid=14202")] + protected void ApplyAutoScaling() { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "ApplyAutoScaling... "); + Debug.Indent(); + // NOTE : This function is cloned in FormDocumentDesigner... remember to keep + // : them in sync + // + + + // We also don't do this if the property is empty. Otherwise we will perform + // two GetAutoScaleBaseSize calls only to find that they returned the same + // value. + // + if (!autoScaleBaseSize.IsEmpty) { + Size baseVar = AutoScaleBaseSize; + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "base =" + baseVar); + SizeF newVarF = GetAutoScaleSize(Font); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "new(f)=" + newVarF); + Size newVar = new Size((int)Math.Round(newVarF.Width), (int)Math.Round(newVarF.Height)); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "new(i)=" + newVar); + + // We save a significant amount of time by bailing early if there's no work to be done + if (baseVar.Equals(newVar)) + return; + + float percY = AdjustScale(((float)newVar.Height) / ((float)baseVar.Height)); + float percX = AdjustScale(((float)newVar.Width) / ((float)baseVar.Width)); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "scale=" + percX + ", " + percY); + Scale(percX, percY); + // This would ensure that we use the new + // font information to calculate the AutoScaleBaseSize. According to Triage + // this was decided to Fix in this version. + // + AutoScaleBaseSize = newVar; + } + Debug.Unindent(); + + } + + /// + /// This adjusts the size of the windowRect so that the client rect is the + /// correct size. + /// + /// + private void ApplyClientSize() { + if ((FormWindowState)formState[FormStateWindowState] != FormWindowState.Normal + || !IsHandleCreated) { + return; + } + + // Cache the clientSize, since calling setBounds will end up causing + // clientSize to get reset to the actual clientRect size... + // + Size correctClientSize = ClientSize; + bool hscr = HScroll; + bool vscr = VScroll; + + // This logic assumes that the caller of setClientSize() knows if the scrollbars + // are showing or not. Since the 90% case is that setClientSize() is the persisted + // ClientSize, this is correct. + // Without this logic persisted forms that were saved with the scrollbars showing, + // don't get set to the correct size. + // + bool adjustScroll = false; + if (formState[FormStateSetClientSize] != 0) { + adjustScroll = true; + formState[FormStateSetClientSize] = 0; + } + if (adjustScroll) { + if (hscr) { + correctClientSize.Height += SystemInformation.HorizontalScrollBarHeight; + } + if (vscr) { + correctClientSize.Width += SystemInformation.VerticalScrollBarWidth; + } + } + + IntPtr h = Handle; + NativeMethods.RECT rc = new NativeMethods.RECT(); + SafeNativeMethods.GetClientRect(new HandleRef(this, h), ref rc); + Rectangle currentClient = Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + + Rectangle bounds = Bounds; + + // If the width is incorrect, compute the correct size with + // computeWindowSize. We only do this logic if the width needs to + // be adjusted to avoid double adjusting the window. + // + if (correctClientSize.Width != currentClient.Width) { + Size correct = ComputeWindowSize(correctClientSize); + + // Since computeWindowSize ignores scrollbars, we must tack these on to + // assure the correct size. + // + if (vscr) { + correct.Width += SystemInformation.VerticalScrollBarWidth; + } + if (hscr) { + correct.Height += SystemInformation.HorizontalScrollBarHeight; + } + bounds.Width = correct.Width; + bounds.Height = correct.Height; + Bounds = bounds; + SafeNativeMethods.GetClientRect(new HandleRef(this, h), ref rc); + currentClient = Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + + // If it still isn't correct, then we assume that the problem is + // menu wrapping (since computeWindowSize doesn't take that into + // account), so we just need to adjust the height by the correct + // amount. + // + if (correctClientSize.Height != currentClient.Height) { + + int delta = correctClientSize.Height - currentClient.Height; + bounds.Height += delta; + Bounds = bounds; + } + + UpdateBounds(); + } + + /// + /// + /// Assigns a new parent control. Sends out the appropriate property change + /// notifications for properties that are affected by the change of parent. + /// + internal override void AssignParent(Control value) { + + // If we are being unparented from the MDI client control, remove + // formMDIParent as well. + // + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent != null && formMdiParent.MdiClient != value) { + Properties.SetObject(PropFormMdiParent, null); + } + + base.AssignParent(value); + } + + /// + /// Checks whether a modal dialog is ready to close. If the dialogResult + /// property is not DialogResult.None, the OnClosing and OnClosed events + /// are fired. A return value of true indicates that both events were + /// successfully dispatched and that event handlers did not cancel closing + /// of the dialog. User code should never have a reason to call this method. + /// It is internal only so that the Application class can call it from a + /// modal message loop. + /// + /// closingOnly is set when this is called from WmClose so that we can do just the beginning portion + /// of this and then let the message loop finish it off with the actual close code. + /// + /// Note we set a flag to determine if we've already called close or not. + /// + /// + internal bool CheckCloseDialog(bool closingOnly) { + if (dialogResult == DialogResult.None && Visible) { + return false; + } + try + { + FormClosingEventArgs e = new FormClosingEventArgs(closeReason, false); + + if (!CalledClosing) + { + OnClosing(e); + OnFormClosing(e); + if (e.Cancel) + { + dialogResult = DialogResult.None; + } + else + { + // we have called closing here, and it wasn't cancelled, so we're expecting a close + // call again soon. + CalledClosing = true; + } + } + + if (!closingOnly && dialogResult != DialogResult.None) + { + FormClosedEventArgs fc = new FormClosedEventArgs(closeReason); + OnClosed(fc); + OnFormClosed(fc); + + // reset called closing. + // + CalledClosing = false; + } + } + catch (Exception e) + { + dialogResult = DialogResult.None; + if (NativeWindow.WndProcShouldBeDebuggable) + { + throw; + } + else + { + Application.OnThreadException(e); + } + } + return dialogResult != DialogResult.None || !Visible; + } + + /// + /// + /// Closes the form. + /// + public void Close() { + + if (GetState(STATE_CREATINGHANDLE)) + throw new InvalidOperationException(SR.GetString(SR.ClosingWhileCreatingHandle, "Close")); + + if (IsHandleCreated) { + closeReason = CloseReason.UserClosing; + SendMessage(NativeMethods.WM_CLOSE, 0, 0); + } + else{ + // MSDN: When a form is closed, all resources created within the object are closed and the form is disposed. + // For MDI child: MdiChildren collection gets updated (VSWhidbey# 368642 & 93550) + Dispose(); + } + } + + /// + /// Computes the window size from the clientSize based on the styles + /// returned from CreateParams. + /// + private Size ComputeWindowSize(Size clientSize) { + CreateParams cp = CreateParams; + return ComputeWindowSize(clientSize, cp.Style, cp.ExStyle); + } + + /// + /// Computes the window size from the clientSize base on the specified + /// window styles. This will not return the correct size if menus wrap. + /// + private Size ComputeWindowSize(Size clientSize, int style, int exStyle) { + NativeMethods.RECT result = new NativeMethods.RECT(0, 0, clientSize.Width, clientSize.Height); + AdjustWindowRectEx(ref result, style, HasMenu, exStyle); + return new Size(result.right - result.left, result.bottom - result.top); + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Control.ControlCollection CreateControlsInstance() { + return new ControlCollection(this); + } + + /// + /// Cleans up form state after a control has been removed. + /// Package scope for Control + /// + /// + internal override void AfterControlRemoved(Control control, Control oldParent) { + base.AfterControlRemoved(control, oldParent); + + if (control == AcceptButton) { + this.AcceptButton = null; + } + if (control == CancelButton) { + this.CancelButton = null; + } + if (control == ctlClient) { + ctlClient = null; + UpdateMenuHandles(); + } + } + + /// + /// + /// Creates the handle for the Form. If a + /// subclass overrides this function, + /// it must call the base implementation. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void CreateHandle() { + // In the windows MDI code we have to suspend menu + // updates on the parent while creating the handle. Otherwise if the + // child is created maximized, the menu ends up with two sets of + // MDI child ornaments. + Form form = (Form)Properties.GetObject(PropFormMdiParent); + if (form != null){ + form.SuspendUpdateMenuHandles(); + } + + try { + // VSWhidbey 426719 - if the child is created before the MDI parent we can + // get Win32 exceptions as the MDI child is parked to the parking window. + if (IsMdiChild && MdiParentInternal.IsHandleCreated) { + MdiClient mdiClient = MdiParentInternal.MdiClient; + if (mdiClient != null && !mdiClient.IsHandleCreated) { + mdiClient.CreateControl(); + } + } + + // If we think that we are maximized, then a duplicate set of mdi gadgets are created. + // If we create the window maximized, BUT our internal form state thinks it + // isn't... then everything is OK... + // + // We really should find out what causes this... but I can't find it... + // + if (IsMdiChild + && (FormWindowState)formState[FormStateWindowState] == FormWindowState.Maximized) { + + // VSWhidbey 517376: This is the reason why we see the blue borders + // when creating a maximized mdi child, unfortunately we cannot fix this now... + formState[FormStateWindowState] = (int)FormWindowState.Normal; + formState[FormStateMdiChildMax] = 1; + base.CreateHandle(); + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + formState[FormStateMdiChildMax] = 0; + } + else { + base.CreateHandle(); + } + + UpdateHandleWithOwner(); + UpdateWindowIcon(false); + + AdjustSystemMenu(); + + if ((FormStartPosition)formState[FormStateStartPos] != FormStartPosition.WindowsDefaultBounds) { + ApplyClientSize(); + } + if (formState[FormStateShowWindowOnCreate] == 1) { + Visible = true; + } + + + // avoid extra SetMenu calls for perf + if (Menu != null || !TopLevel || IsMdiContainer) + UpdateMenuHandles(); + + // In order for a window not to have a taskbar entry, it must + // be owned. + // + if (!ShowInTaskbar && OwnerInternal == null && TopLevel) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, TaskbarOwner); + + // Make sure the large icon is set so the ALT+TAB icon + // reflects the real icon of the application + Icon icon = Icon; + if (icon != null && TaskbarOwner.Handle != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(TaskbarOwner, NativeMethods.WM_SETICON, NativeMethods.ICON_BIG, icon.Handle); + } + } + + if (formState[FormStateTopMost] != 0) { + TopMost = true; + } + + } + finally { + if (form != null) + form.ResumeUpdateMenuHandles(); + + // We need to reset the styles in case Windows tries to set us up + // with "correct" styles... See ASURT 81646. + // + UpdateStyles(); + } + } + + // Created for fix VSWhidbey 182131. Deactivates active MDI child and temporarily marks it as unfocusable, + // so that WM_SETFOCUS sent to MDIClient does not activate that child. + private void DeactivateMdiChild() { + Form activeMdiChild = ActiveMdiChildInternal; + if (null != activeMdiChild) { + Form mdiParent = activeMdiChild.MdiParentInternal; + + activeMdiChild.Active = false; + activeMdiChild.IsMdiChildFocusable = false; + if (!activeMdiChild.IsClosing) { + FormerlyActiveMdiChild = activeMdiChild; + } + // Enter/Leave events on child controls are raised from the ActivateMdiChildInternal method, usually when another + // Mdi child is getting activated after deactivating this one; but if this is the only visible MDI child + // we need to fake the activation call so MdiChildActivate and Leave events are raised properly. (We say + // in the MSDN doc that the MdiChildActivate event is raised when an mdi child is activated or closed - + // we actually meant the last mdi child is closed). + bool fakeActivation = true; + foreach(Form mdiChild in mdiParent.MdiChildren ){ + if( mdiChild != this && mdiChild.Visible ){ + fakeActivation = false; // more than one mdi child visible. + break; + } + } + + if( fakeActivation ){ + mdiParent.ActivateMdiChildInternal(null); + } + + ActiveMdiChildInternal = null; + + // Note: WM_MDIACTIVATE message is sent to the form being activated and to the form being deactivated, ideally + // we would raise the event here accordingly but it would constitute a breaking change. + //OnMdiChildActivate(EventArgs.Empty); + + // undo merge + UpdateMenuHandles(); + UpdateToolStrip(); + } + } + + /// + /// + /// + /// Calls the default window proc for the form. If + /// a + /// subclass overrides this function, + /// it must call the base implementation. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected override void DefWndProc(ref Message m) { + if (ctlClient != null && ctlClient.IsHandleCreated && ctlClient.ParentInternal == this){ + m.Result = UnsafeNativeMethods.DefFrameProc(m.HWnd, ctlClient.Handle, m.Msg, m.WParam, m.LParam); + } + else if (0 != formStateEx[FormStateExUseMdiChildProc]){ + m.Result = UnsafeNativeMethods.DefMDIChildProc(m.HWnd, m.Msg, m.WParam, m.LParam); + } + else { + base.DefWndProc(ref m); + } + } + + /// + /// + /// Releases all the system resources associated with the Form. If a subclass + /// overrides this function, it must call the base implementation. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + CalledOnLoad = false; + CalledMakeVisible = false; + CalledCreateControl = false; + + if (Properties.ContainsObject(PropAcceptButton)) Properties.SetObject(PropAcceptButton, null); + if (Properties.ContainsObject(PropCancelButton)) Properties.SetObject(PropCancelButton, null); + if (Properties.ContainsObject(PropDefaultButton)) Properties.SetObject(PropDefaultButton, null); + if (Properties.ContainsObject(PropActiveMdiChild)) Properties.SetObject(PropActiveMdiChild, null); + + if (MdiWindowListStrip != null){ + MdiWindowListStrip.Dispose(); + MdiWindowListStrip = null; + } + + if (MdiControlStrip != null){ + MdiControlStrip.Dispose(); + MdiControlStrip = null; + } + + if (MainMenuStrip != null) { + // should NOT call dispose on MainMenuStrip - it's likely NOT to be in the form's control collection. + MainMenuStrip = null; + } + + Form owner = (Form)Properties.GetObject(PropOwner); + if (owner != null) { + owner.RemoveOwnedForm(this); + Properties.SetObject(PropOwner, null); + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + if (ownedForms[i] != null) { + // it calls remove and removes itself. + ownedForms[i].Dispose(); + } + } + + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + + ResetSecurityTip(false /* modalOnly */); + + base.Dispose(disposing); + ctlClient = null; + + MainMenu mainMenu = Menu; + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: We should only dispose this form's menus! + if (mainMenu != null && mainMenu.ownerForm == this) { + mainMenu.Dispose(); + Properties.SetObject(PropMainMenu, null); + } + + if (Properties.GetObject(PropCurMenu) != null) { + Properties.SetObject(PropCurMenu, null); + } + + MenuChanged(Windows.Forms.Menu.CHANGE_ITEMS, null); + + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + + if (dummyMenu != null) { + dummyMenu.Dispose(); + Properties.SetObject(PropDummyMenu, null); + } + + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + + if (mergedMenu != null) { + if (mergedMenu.ownerForm == this || mergedMenu.form == null) { + mergedMenu.Dispose(); + } + Properties.SetObject(PropMergedMenu, null); + } + } + else { + base.Dispose(disposing); + } + } + + /// + /// Adjusts the window style of the CreateParams to reflect the bordericons. + /// + /// + private void FillInCreateParamsBorderIcons(CreateParams cp) { + if (FormBorderStyle != FormBorderStyle.None) { + if (Text != null && Text.Length != 0) { + cp.Style |= NativeMethods.WS_CAPTION; + } + + // In restricted mode, the form must have a system menu, caption and max/min/close boxes. + + if (ControlBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_SYSMENU | NativeMethods.WS_CAPTION; + } + else { + cp.Style &= (~NativeMethods.WS_SYSMENU); + } + + if (MaximizeBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_MAXIMIZEBOX; + } + else { + cp.Style &= ~NativeMethods.WS_MAXIMIZEBOX; + } + + if (MinimizeBox || IsRestrictedWindow) { + cp.Style |= NativeMethods.WS_MINIMIZEBOX; + } + else { + cp.Style &= ~NativeMethods.WS_MINIMIZEBOX; + } + + if (HelpButton && !MaximizeBox && !MinimizeBox && ControlBox) { + // Windows should ignore WS_EX_CONTEXTHELP unless all those conditions hold. + // But someone must have failed the check, because Windows 2000 + // will show a help button if either the maximize or + // minimize button is disabled. + cp.ExStyle |= NativeMethods.WS_EX_CONTEXTHELP; + } + else { + cp.ExStyle &= ~NativeMethods.WS_EX_CONTEXTHELP; + } + } + } + + /// + /// Adjusts the window style of the CreateParams to reflect the borderstyle. + /// + private void FillInCreateParamsBorderStyles(CreateParams cp) { + switch ((FormBorderStyle)formState[FormStateBorderStyle]) { + case FormBorderStyle.None: + // SECREVIEW : The check for IsRestrictedWindow here doesn't seem to be needed since in restricted + // mode it is not allowed to have FormBorderSytle.None (see FormBorderStyle) and the + // default value is FormBorderStyle.Sizable. But this has been there for long so to be + // in the safe side is better to leave it (defense in depth), it's not harmful at all. + // + if (IsRestrictedWindow) { + goto case FormBorderStyle.FixedSingle; + } + break; + case FormBorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + case FormBorderStyle.Sizable: + cp.Style |= NativeMethods.WS_BORDER | NativeMethods.WS_THICKFRAME; + break; + case FormBorderStyle.Fixed3D: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case FormBorderStyle.FixedDialog: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_DLGMODALFRAME; + break; + case FormBorderStyle.FixedToolWindow: + cp.Style |= NativeMethods.WS_BORDER; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + break; + case FormBorderStyle.SizableToolWindow: + cp.Style |= NativeMethods.WS_BORDER | NativeMethods.WS_THICKFRAME; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + break; + } + } + + /// + /// Adjusts the CreateParams to reflect the window bounds and start position. + /// + private void FillInCreateParamsStartPosition(CreateParams cp) { + + // V#42613 - removed logic that forced MDI children to always be + // default size and position... need to verify that + // this works on Win9X + /* + if (getIsMdiChild()) { + cp.x = NativeMethods.CW_USEDEFAULT; + cp.y = NativeMethods.CW_USEDEFAULT; + cp.width = NativeMethods.CW_USEDEFAULT; + cp.height = NativeMethods.CW_USEDEFAULT; + } + else { + */ + if (formState[FormStateSetClientSize] != 0) { + + // V7#37980 - when computing the client window size, don't tell them that + // we are going to be maximized! + // + int maskedStyle = cp.Style & ~(NativeMethods.WS_MAXIMIZE | NativeMethods.WS_MINIMIZE); + Size correct = ComputeWindowSize(ClientSize, maskedStyle, cp.ExStyle); + + if (IsRestrictedWindow) { + correct = ApplyBoundsConstraints(cp.X, cp.Y, correct.Width, correct.Height).Size; + } + cp.Width = correct.Width; + cp.Height = correct.Height; + + } + + switch ((FormStartPosition)formState[FormStateStartPos]) { + case FormStartPosition.WindowsDefaultBounds: + cp.Width = NativeMethods.CW_USEDEFAULT; + cp.Height = NativeMethods.CW_USEDEFAULT; + // no break, fall through to set the location to default... + goto case FormStartPosition.WindowsDefaultLocation; + case FormStartPosition.WindowsDefaultLocation: + case FormStartPosition.CenterParent: + // VSWhidbey 93522: Since FillInCreateParamsStartPosition() is called + // several times when a window is shown, we'll need to force the location + // each time for MdiChild windows that are docked so that the window will + // be created in the correct location and scroll bars will not be displayed. + if (IsMdiChild && DockStyle.None != Dock){ + break; + } + + cp.X = NativeMethods.CW_USEDEFAULT; + cp.Y = NativeMethods.CW_USEDEFAULT; + break; + case FormStartPosition.CenterScreen: + if (IsMdiChild) { + Control mdiclient = MdiParentInternal.MdiClient; + Rectangle clientRect = mdiclient.ClientRectangle; + + cp.X = Math.Max(clientRect.X,clientRect.X + (clientRect.Width - cp.Width)/2); + cp.Y = Math.Max(clientRect.Y,clientRect.Y + (clientRect.Height - cp.Height)/2); + } + else { + Screen desktop = null; + IWin32Window dialogOwner = (IWin32Window)Properties.GetObject(PropDialogOwner); + if ((OwnerInternal != null) || (dialogOwner != null)) { + IntPtr ownerHandle = (dialogOwner != null) ? Control.GetSafeHandle(dialogOwner) : OwnerInternal.Handle; + desktop = Screen.FromHandleInternal(ownerHandle); + } + else { + desktop = Screen.FromPoint(Control.MousePosition); + } + Rectangle screenRect = desktop.WorkingArea; + //if, we're maximized, then don't set the x & y coordinates (they're @ (0,0) ) + if (WindowState != FormWindowState.Maximized) { + cp.X = Math.Max(screenRect.X,screenRect.X + (screenRect.Width - cp.Width)/2); + cp.Y = Math.Max(screenRect.Y,screenRect.Y + (screenRect.Height - cp.Height)/2); + } + } + break; + } + } + + /// + /// Adjusts the Createparams to reflect the window state. + /// + private void FillInCreateParamsWindowState(CreateParams cp) { + // SECUNDONE: We don't need to check for restricted window here since the only way to set the WindowState + // programatically is by changing the property and we have a check for it in the property setter. + // We don't want to check it here again because it would not allow to set the CreateParams.Style + // to the current WindowState so the window will be set to its current Normal size + // (which in some cases is quite bogus) when Control.UpdateStylesCore is called. + // See VSWhidbey#388028. + // + // if( IsRestrictedWindow ){ + // return; + // } + + switch ((FormWindowState)formState[FormStateWindowState]) { + case FormWindowState.Maximized: + cp.Style |= NativeMethods.WS_MAXIMIZE; + break; + case FormWindowState.Minimized: + cp.Style |= NativeMethods.WS_MINIMIZE; + break; + } + } + + /// + /// Sets focus to the Form. + /// Attempts to set focus to this Form. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + internal override bool FocusInternal() { + Debug.Assert( IsHandleCreated, "Attempt to set focus to a form that has not yet created its handle." ); + //if this form is a MdiChild, then we need to set the focus in a different way... + // + if (IsMdiChild) { + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + return Focused; + } + + return base.FocusInternal(); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This method has been deprecated. Use the AutoScaleDimensions property instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "The quick ..." is the archetipal string + // used to measure font height. + // So we don't have to localize it. + ] + public static SizeF GetAutoScaleSize(Font font) { + + float height = font.Height; + float width = 9.0f; + + try { + using( Graphics graphics = Graphics.FromHwndInternal(IntPtr.Zero /*screen*/) ) { + string magicString = "The quick brown fox jumped over the lazy dog."; + double magicNumber = 44.549996948242189; // chosen for compatibility with older versions of windows forms, but approximately magicString.Length + float stringWidth = graphics.MeasureString(magicString, font).Width; + width = (float) (stringWidth / magicNumber); + } + } + catch { // We may get an bogus OutOfMemoryException + // (which is a critical exception - according to ClientUtils.IsCriticalException()) + // from GDI+. So we can't use ClientUtils.IsCriticalException here and rethrow. + } + + return new SizeF(width, height); + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + + // + + Size preferredSize = base.GetPreferredSizeCore(proposedSize); + +#if MAGIC_PADDING +//VSWhidbey 401943: We are removing the magic padding in runtime. + + bool dialogFixRequired = false; + + if (Padding == Padding.Empty) { + Padding paddingToAdd = Padding.Empty; + foreach (Control c in this.Controls) { + if (c.Dock == DockStyle.None) { + AnchorStyles anchor = c.Anchor; + if (anchor == AnchorStyles.None) { + break; + } + + // TOP + // if we are anchored to the top only add padding if the top edge is too far down + if ((paddingToAdd.Bottom == 0) && DefaultLayout.IsAnchored(anchor, AnchorStyles.Top)) { + if (c.Bottom > preferredSize.Height - FormPadding.Bottom) { + paddingToAdd.Bottom = FormPadding.Bottom; + + dialogFixRequired = true; + } + } + // BOTTOM + // if we are anchored to the bottom + // dont add any padding - it's way too confusing to be dragging a button up + // and have the form grow to the bottom. + + // LEFT + // if we are anchored to the left only add padding if the right edge is too far right + if ((paddingToAdd.Left == 0) && DefaultLayout.IsAnchored(anchor, AnchorStyles.Left)) { + if (c.Right > preferredSize.Width - FormPadding.Right) { + paddingToAdd.Right = FormPadding.Right; + dialogFixRequired = true; + } + } + // RIGHT + // if we are anchored to the bottom + // dont add any padding - it's way too confusing to be dragging a button left + // and have the form grow to the right + + } + } + Debug.Assert(!dialogFixRequired, "this dialog needs update: " + this.Name); + return preferredSize + paddingToAdd.Size; + } +#endif + return preferredSize; + } + + /// + /// This private method attempts to resolve security zone and site + /// information given a list of urls (sites). This list is supplied + /// by the runtime and will contain the paths of all loaded assemblies + /// on the stack. From here, we can examine zones and sites and + /// attempt to identify the unique and mixed scenarios for each. This + /// information will be displayed in the titlebar of the Form in a + /// semi-trust environment. + /// + private void ResolveZoneAndSiteNames(ArrayList sites, ref string securityZone, ref string securitySite) { + + //Start by defaulting to 'unknown zone' and 'unknown site' strings. We will return this + //information if anything goes wrong while trying to resolve this information. + // + securityZone = SR.GetString(SR.SecurityRestrictedWindowTextUnknownZone); + securitySite = SR.GetString(SR.SecurityRestrictedWindowTextUnknownSite); + + try + { + //these conditions must be met + // + if (sites == null || sites.Count == 0) + return; + + //create a new zone array list which has no duplicates and no + //instances of mycomputer + ArrayList zoneList = new ArrayList(); + foreach (object arrayElement in sites) + { + if (arrayElement == null) + return; + + string url = arrayElement.ToString(); + + if (url.Length == 0) + return; + + //into a zoneName + // + Zone currentZone = Zone.CreateFromUrl(url); + + //skip this if the zone is mycomputer + // + if (currentZone.SecurityZone.Equals(SecurityZone.MyComputer)) + continue; + + //add our unique zonename to our list of zones + // + string zoneName = currentZone.SecurityZone.ToString(); + + if (!zoneList.Contains(zoneName)) + { + zoneList.Add(zoneName); + } + } + + //now, we resolve the zone name based on the unique information + //left in the zoneList + // + if (zoneList.Count == 0) + { + //here, all the original zones were 'mycomputer' + //so we can just return that + securityZone = SecurityZone.MyComputer.ToString(); + } + else if (zoneList.Count == 1) + { + //we only found 1 unique zone other than + //mycomputer + securityZone = zoneList[0].ToString(); + } + else + { + //here, we found multiple zones + // + securityZone = SR.GetString(SR.SecurityRestrictedWindowTextMixedZone); + } + + //generate a list of loaded assemblies that came from the gac, this + //way we can safely ignore these from the url list + ArrayList loadedAssembliesFromGac = new ArrayList(); + + // SECREVIEW : Querying for assembly info is safe. + // + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + + try + { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) + { + if (asm.GlobalAssemblyCache) + { + loadedAssembliesFromGac.Add(asm.CodeBase.ToUpper(CultureInfo.InvariantCulture)); + } + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + //now, build up a sitelist which contains a friendly string + //we've extracted via the uri class and omit any urls that reference + //our local gac + // + ArrayList siteList = new ArrayList(); + foreach (object arrayElement in sites) + { + //we know that each element is valid because of our + //first pass + Uri currentSite = new Uri(arrayElement.ToString()); + + //if we see a site that contains the path to our gac, + //we'll just skip it + + if (loadedAssembliesFromGac.Contains(currentSite.AbsoluteUri.ToUpper(CultureInfo.InvariantCulture))) + { + continue; + } + + //add the unique host name to our list + string hostName = currentSite.Host; + if (hostName.Length > 0 && !siteList.Contains(hostName)) + siteList.Add(hostName); + } + + + //resolve the site name from our list, if siteList.count == 0 + //then we have already set our securitySite to "unknown site" + // + if (siteList.Count == 0) { + //here, we'll set the local machine name to the site string + // + + // SECREVIEW : Querying for environment info is safe. + // + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + securitySite = Environment.MachineName; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + else if (siteList.Count == 1) + { + //We found 1 unique site other than the info in the + //gac + securitySite = siteList[0].ToString(); + } + else + { + //multiple sites, we'll have to return 'mixed sites' + // + securitySite = SR.GetString(SR.SecurityRestrictedWindowTextMultipleSites); + } + } + catch + { + //We'll do nothing here. The idea is that zone and security strings are set + //to "unkown" at the top of this method - if an exception is thrown, we'll + //stick with those values + } + } + + /// + /// Sets the restricted window text (titlebar text of a form) when running + /// in a semi-trust environment. The format is: [zone info] - Form Text - [site info] + /// + private string RestrictedWindowText(string original) { + EnsureSecurityInformation(); + return string.Format(CultureInfo.CurrentCulture, Application.SafeTopLevelCaptionFormat, original, securityZone, securitySite); + } + + private void EnsureSecurityInformation() { + if (securityZone == null || securitySite == null) { + ArrayList zones; + ArrayList sites; + + SecurityManager.GetZoneAndOrigin( out zones, out sites ); + + ResolveZoneAndSiteNames(sites, ref securityZone, ref securitySite); + } + } + + private void CallShownEvent() + { + OnShown(EventArgs.Empty); + } + + /// + /// Override since CanProcessMnemonic is overriden too (base.CanSelectCore calls CanProcessMnemonic). + /// + internal override bool CanSelectCore() { + if( !GetStyle(ControlStyles.Selectable) || !this.Enabled || !this.Visible) { + return false; + } + return true; + } + + /// + /// When an MDI form is hidden it means its handle has not yet been created or has been destroyed (see + /// SetVisibleCore). If the handle is recreated, the form will be made visible which should be avoided. + /// + internal bool CanRecreateHandle() { + if( IsMdiChild ){ + // During changing visibility, it is possible that the style returns true for visible but the handle has + // not yet been created, add a check for both. + return GetState(STATE_VISIBLE) && IsHandleCreated; + } + return true; + } + + /// + /// Overriden to handle MDI mnemonic processing properly. + /// + internal override bool CanProcessMnemonic() { +#if DEBUG + TraceCanProcessMnemonic(); +#endif + // If this is a Mdi child form, child controls should process mnemonics only if this is the active mdi child. + if (this.IsMdiChild && (formStateEx[FormStateExMnemonicProcessed] == 1 || this != this.MdiParentInternal.ActiveMdiChildInternal || this.WindowState == FormWindowState.Minimized)){ + return false; + } + + return base.CanProcessMnemonic(); + } + + /// + /// Overriden to handle MDI mnemonic processing properly. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // MDI container form has at least one control, the MDI client which contains the MDI children. We need + // to allow the MDI children process the mnemonic before any other control in the MDI container (like + // menu items or any other control). + if( base.ProcessMnemonic( charCode ) ){ + return true; + } + + if( this.IsMdiContainer ){ + // ContainerControl have already processed the active MDI child for us (if any) in the call above, + // now process remaining controls (non-mdi children). + if( this.Controls.Count > 1 ){ // Ignore the MdiClient control + for( int index = 0; index < this.Controls.Count; index++ ){ + Control ctl = this.Controls[index]; + if( ctl is MdiClient ){ + continue; + } + + if( ctl.ProcessMnemonic( charCode ) ){ + return true; + } + } + } + + return false; + } + + return false; + } + + /// + /// + /// Centers the dialog to its parent. + /// + /// + protected void CenterToParent() { + if (TopLevel) { + Point p = new Point(); + Size s = Size; + IntPtr ownerHandle = IntPtr.Zero; + + ownerHandle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + if (ownerHandle != IntPtr.Zero) { + Screen desktop = Screen.FromHandleInternal(ownerHandle); + Rectangle screenRect = desktop.WorkingArea; + NativeMethods.RECT ownerRect = new NativeMethods.RECT(); + + UnsafeNativeMethods.GetWindowRect(new HandleRef(null, ownerHandle), ref ownerRect); + + p.X = (ownerRect.left + ownerRect.right - s.Width) / 2; + if (p.X < screenRect.X) + p.X = screenRect.X; + else if (p.X + s.Width > screenRect.X + screenRect.Width) + p.X = screenRect.X + screenRect.Width - s.Width; + + p.Y = (ownerRect.top + ownerRect.bottom - s.Height) / 2; + if (p.Y < screenRect.Y) + p.Y = screenRect.Y; + else if (p.Y + s.Height > screenRect.Y + screenRect.Height) + p.Y = screenRect.Y + screenRect.Height - s.Height; + + Location = p; + } + else { + CenterToScreen(); + } + } + } + + /// + /// + /// Centers the dialog to the screen. This will first attempt to use + /// the owner property to determine the correct screen, then + /// it will try the HWND owner of the form, and finally this will + /// center the form on the same monitor as the mouse cursor. + /// + /// + protected void CenterToScreen() { + Point p = new Point(); + Screen desktop = null; + if (OwnerInternal != null) { + desktop = Screen.FromControl(OwnerInternal); + } + else { + IntPtr hWndOwner = IntPtr.Zero; + if (TopLevel) { + hWndOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + } + if (hWndOwner != IntPtr.Zero) { + desktop = Screen.FromHandleInternal(hWndOwner); + } + else { + desktop = Screen.FromPoint(Control.MousePosition); + } + } + Rectangle screenRect = desktop.WorkingArea; + p.X = Math.Max(screenRect.X,screenRect.X + (screenRect.Width - Width)/2); + p.Y = Math.Max(screenRect.Y,screenRect.Y + (screenRect.Height - Height)/2); + Location = p; + } + + /// + /// Invalidates the merged menu, forcing the menu to be recreated if + /// needed again. + /// + private void InvalidateMergedMenu() { + // here, we just set the merged menu to null (indicating that the menu structure + // needs to be rebuilt). Then, we signal the parent to updated its menus. + if (Properties.ContainsObject(PropMergedMenu)) { + MainMenu menu = Properties.GetObject(PropMergedMenu) as MainMenu; + if (menu != null && menu.ownerForm == this) { + menu.Dispose(); + } + Properties.SetObject(PropMergedMenu, null); + } + + Form parForm = ParentFormInternal; + if (parForm != null) { + parForm.MenuChanged(0, parForm.Menu); + } + } + + /// + /// + /// Arranges the Multiple Document Interface + /// (MDI) child forms according to value. + /// + public void LayoutMdi(MdiLayout value) { + if (ctlClient == null){ + return; + } + ctlClient.LayoutMdi(value); + } + + // Package scope for menu interop + internal void MenuChanged(int change, Menu menu) { + Form parForm = ParentFormInternal; + if (parForm != null && this == parForm.ActiveMdiChildInternal) { + parForm.MenuChanged(change, menu); + return; + } + + switch (change) { + case Windows.Forms.Menu.CHANGE_ITEMS: + case Windows.Forms.Menu.CHANGE_MERGE: + if (ctlClient == null || !ctlClient.IsHandleCreated) { + if (menu == Menu && change == Windows.Forms.Menu.CHANGE_ITEMS) + UpdateMenuHandles(); + break; + } + + // Tell the children to toss their mergedMenu. + if (IsHandleCreated) { + UpdateMenuHandles(null, false); + } + + Control.ControlCollection children = ctlClient.Controls; + for (int i = children.Count; i-- > 0;) { + Control ctl = children[i]; + if (ctl is Form && ctl.Properties.ContainsObject(PropMergedMenu)) { + MainMenu mainMenu = ctl.Properties.GetObject(PropMergedMenu) as MainMenu; + if (mainMenu != null && mainMenu.ownerForm == ctl) { + mainMenu.Dispose(); + } + ctl.Properties.SetObject(PropMergedMenu, null); + } + } + + UpdateMenuHandles(); + break; + case Windows.Forms.Menu.CHANGE_VISIBLE: + if (menu == Menu || (this.ActiveMdiChildInternal != null && menu == this.ActiveMdiChildInternal.Menu)) { + UpdateMenuHandles(); + } + break; + case Windows.Forms.Menu.CHANGE_MDI: + if (ctlClient != null && ctlClient.IsHandleCreated) { + UpdateMenuHandles(); + } + break; + } + } + + /// + /// + /// The activate event is fired when the form is activated. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnActivated(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_ACTIVATED]; + if (handler != null) handler(this,e); + } + + /// + /// Override of AutoScaleModeChange method from ContainerControl. We use + /// this to keep our own AutoScale property in sync. + /// + internal override void OnAutoScaleModeChanged() { + base.OnAutoScaleModeChanged(); + // Obsolete code required here for backwards compat +#pragma warning disable 618 + if (formStateEx[FormStateExSettingAutoScale] != 1) { + AutoScale = false; + } +#pragma warning restore 618 + + } + + protected override void OnBackgroundImageChanged(EventArgs e) { + base.OnBackgroundImageChanged(e); + if (IsMdiContainer) + { + MdiClient.BackgroundImage = BackgroundImage; + // requires explicit invalidate to update the image. + MdiClient.Invalidate(); + } + } + + protected override void OnBackgroundImageLayoutChanged(EventArgs e) { + base.OnBackgroundImageLayoutChanged(e); + if (IsMdiContainer) + { + MdiClient.BackgroundImageLayout = BackgroundImageLayout; + // requires explicit invalidate to update the imageLayout. + MdiClient.Invalidate(); + } + } + + /// + /// + /// The Closing event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClosing(CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[EVENT_CLOSING]; + if (handler != null) handler(this,e); + } + + /// + /// + /// The Closed event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnClosed(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_CLOSED]; + if (handler != null) handler(this,e); + } + + + /// + /// + /// The Closing event is fired before the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFormClosing(FormClosingEventArgs e) { + FormClosingEventHandler handler = (FormClosingEventHandler)Events[EVENT_FORMCLOSING]; + if (handler != null) handler(this,e); + } + + /// + /// + /// The Closed event is fired when the form is closed. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFormClosed(FormClosedEventArgs e) { + //Remove the form from Application.OpenForms (nothing happens if isn't present) + Application.OpenFormsInternalRemove(this); + + FormClosedEventHandler handler = (FormClosedEventHandler)Events[EVENT_FORMCLOSED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the CreateControl event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnCreateControl() { + CalledCreateControl = true; + base.OnCreateControl(); + + if (CalledMakeVisible && !CalledOnLoad) { + CalledOnLoad = true; + OnLoad(EventArgs.Empty); + } + } + + /// + /// + /// Raises the Deactivate event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDeactivate(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_DEACTIVATE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the EnabledChanged event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + if (!DesignMode && Enabled && Active) { + // Make sure we activate the active control. See VSWhidbey#143277 + Control activeControl = ActiveControl; + + // Seems safe to call this here without demanding permissions, since we only + // get here if this form is enabled and active. + if( activeControl == null ){ + SelectNextControlInternal(this, true, true, true, true); + } + else{ + FocusActiveControlInternal(); + } + } + } + + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnEnter(EventArgs e) { + base.OnEnter(e); + + // VSWhidbey#93542. Enter events are not raised on mdi child form controls on form Enabled. + if( IsMdiChild ){ + UpdateFocusedControl(); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnFontChanged(EventArgs e) { + if (DesignMode) { + UpdateAutoScaleBaseSize(); + } + base.OnFontChanged(e); + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. + /// Call base.OnHandleCreated first. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleCreated(EventArgs e) { + formStateEx[FormStateExUseMdiChildProc] = (IsMdiChild && Visible) ? 1 : 0; + base.OnHandleCreated(e); + UpdateLayered(); + } + + /// + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle is about to be destroyed. + /// Call base.OnHandleDestroyed last. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + formStateEx[FormStateExUseMdiChildProc] = 0; + + // just make sure we're no longer in the forms collection list + Application.OpenFormsInternalRemove(this); + + // If the handle is being destroyed, and the security tip hasn't been dismissed + // then we remove it from the property bag. When we come back around and get + // an NCACTIVATE we will see that this is missing and recreate the security + // tip in it's default state. + // + ResetSecurityTip(true /* modalOnly */); + } + + /// + /// + /// Handles the event that a helpButton is clicked + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnHelpButtonClicked(CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[EVENT_HELPBUTTONCLICKED]; + if (handler != null) { + handler(this,e); + } + } + + /// + protected override void OnLayout(LayoutEventArgs levent) { + + // Typically forms are either top level or parented to an MDIClient area. + // In either case, forms a responsible for managing their own size. + if(AutoSize /*&& DesignMode*/) { + // If AutoSized, set the Form to the maximum of its preferredSize or the user + // specified size. + Size prefSize = this.PreferredSize; + minAutoSize = prefSize; + + // This used to use "GetSpecifiedBounds" - but it was not updated when we're in the middle of + // a modal resizing loop (WM_WINDOWPOSCHANGED). + Size adjustedSize = AutoSizeMode == AutoSizeMode.GrowAndShrink ? prefSize : LayoutUtils.UnionSizes(prefSize, Size); + + IArrangedElement form = this as IArrangedElement; + if (form != null) { + form.SetBounds(new Rectangle(this.Left, this.Top, adjustedSize.Width, adjustedSize.Height), BoundsSpecified.None); + } + } + base.OnLayout(levent); + } + + /// + /// + /// The Load event is fired before the form becomes visible for the first time. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLoad(EventArgs e) { + //First - add the form to Application.OpenForms + Application.OpenFormsInternalAdd(this); + if (Application.UseWaitCursor) { + this.UseWaitCursor = true; + } + + // subhag: This will apply AutoScaling to the form just + // before the form becomes visible. + // + if (formState[FormStateAutoScaling] == 1 && !DesignMode) { + // Turn off autoscaling so we don't do this on every handle + // creation. + // + formState[FormStateAutoScaling] = 0; + // Obsolete code required here for backwards compat +#pragma warning disable 618 + ApplyAutoScaling(); +#pragma warning restore 618 + + } + +/* + /// + + + + + + + + + + + + +*/ + + // Also, at this time we can now locate the form the the correct + // area of the screen. We must do this after applying any + // autoscaling. + // + if (GetState(STATE_MODAL)) { + FormStartPosition startPos = (FormStartPosition)formState[FormStateStartPos]; + if (startPos == FormStartPosition.CenterParent) { + CenterToParent(); + } + else if (startPos == FormStartPosition.CenterScreen) { + CenterToScreen(); + } + } + + // There is no good way to explain this event except to say + // that it's just another name for OnControlCreated. + EventHandler handler = (EventHandler)Events[EVENT_LOAD]; + if (handler != null) { + string text = Text; + + handler(this,e); + + + // It seems that if you set a window style during the onload + // event, we have a problem initially painting the window. + // So in the event that the user has set the on load event + // in their application, we should go ahead and invalidate + // the controls in their collection so that we paint properly. + // This seems to manifiest itself in changes to the window caption, + // and changes to the control box and help. + + foreach (Control c in Controls) { + c.Invalidate(); + } + } + + //finally fire the newOnShown(unless the form has already been closed) + if (IsHandleCreated) + { + this.BeginInvoke(new MethodInvoker(CallShownEvent)); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMaximizedBoundsChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MAXIMIZEDBOUNDSCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMaximumSizeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MAXIMUMSIZECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMinimumSizeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MINIMUMSIZECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInputLanguageChanged(InputLanguageChangedEventArgs e) { + InputLanguageChangedEventHandler handler = (InputLanguageChangedEventHandler)Events[EVENT_INPUTLANGCHANGE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnInputLanguageChanging(InputLanguageChangingEventArgs e) { + InputLanguageChangingEventHandler handler = (InputLanguageChangingEventHandler)Events[EVENT_INPUTLANGCHANGEREQUEST]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnVisibleChanged(EventArgs e) { + UpdateRenderSizeGrip(); + Form mdiParent = MdiParentInternal; + if (mdiParent != null) { + mdiParent.UpdateMdiWindowListStrip(); + } + base.OnVisibleChanged(e); + + // windows forms have to behave like dialog boxes sometimes. If the + // user has specified that the mouse should snap to the + // Accept button using the Mouse applet in the control panel, + // we have to respect that setting each time our form is made visible. + bool data = false; + if (IsHandleCreated + && Visible + && (AcceptButton != null) + && UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETSNAPTODEFBUTTON, 0, ref data, 0) + && data) { + + Control button = AcceptButton as Control; + NativeMethods.POINT ptToSnap = new NativeMethods.POINT( + button.Left + button.Width / 2, + button.Top + button.Height / 2); + + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, Handle), ptToSnap); + if (!button.IsWindowObscured) { + // SECREVIEW : It is safe to change the cursor position from here. + // + IntSecurity.AdjustCursorPosition.Assert(); + try { + Cursor.Position = new Point(ptToSnap.x, ptToSnap.y); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMdiChildActivate(EventArgs e) { + UpdateMenuHandles(); + UpdateToolStrip(); + EventHandler handler = (EventHandler)Events[EVENT_MDI_CHILD_ACTIVATE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMenuStart(EventArgs e) { + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip != null) { + secTip.Pop(true /*noLongerFirst*/); + } + EventHandler handler = (EventHandler)Events[EVENT_MENUSTART]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the MenuComplete event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnMenuComplete(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_MENUCOMPLETE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// + /// Raises the Paint event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + if (formState[FormStateRenderSizeGrip] != 0) { + Size sz = ClientSize; + if (Application.RenderWithVisualStyles) { + if (sizeGripRenderer == null) { + sizeGripRenderer = new VisualStyleRenderer(VisualStyleElement.Status.Gripper.Normal); + } + + sizeGripRenderer.DrawBackground(e.Graphics, new Rectangle(sz.Width - SizeGripSize, sz.Height - SizeGripSize, SizeGripSize, SizeGripSize)); + } + else { + ControlPaint.DrawSizeGrip(e.Graphics, BackColor, sz.Width - SizeGripSize, sz.Height - SizeGripSize, SizeGripSize, SizeGripSize); + } + } + + if (IsMdiContainer) { + e.Graphics.FillRectangle(SystemBrushes.AppWorkspace, ClientRectangle); + } + } + + /// + /// + /// + /// Raises the Resize event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (formState[FormStateRenderSizeGrip] != 0) { + Invalidate(); + } + } + + /// + /// + /// + /// Raises the DpiChanged event. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + protected virtual void OnDpiChanged(DpiChangedEventArgs e) { + if (e.DeviceDpiNew != e.DeviceDpiOld) { + CommonProperties.xClearAllPreferredSizeCaches(this); + + // call any additional handlers + ((DpiChangedEventHandler)Events[EVENT_DPI_CHANGED])?.Invoke(this, e); + + if (!e.Cancel) { + float factor = (float)e.DeviceDpiNew / (float)e.DeviceDpiOld; + SuspendAllLayout(this); + try { + + // Form 'MinimumSize' is explicit set to make form shrink in case of DPI is changed. For scaling factor >1, + // Form layout automatically grows to fit to the container size. + if (DpiHelper.EnableDpiChangedHighDpiImprovements && factor < 1) { + MinimumSize = new Size(e.SuggestedRectangle.Width, e.SuggestedRectangle.Height); + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, HandleInternal), NativeMethods.NullHandleRef, e.SuggestedRectangle.X, e.SuggestedRectangle.Y, e.SuggestedRectangle.Width, e.SuggestedRectangle.Height, NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); + if (AutoScaleMode != AutoScaleMode.Font) { + Font = DpiHelper.EnableDpiChangedHighDpiImprovements ? + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style, this.Font.Unit, this.Font.GdiCharSet, this.Font.GdiVerticalFont) : + new Font(this.Font.FontFamily, this.Font.Size * factor, this.Font.Style); + FormDpiChanged(factor); + } + else { + ScaleFont(factor); + FormDpiChanged(factor); + } + } + finally { + // We want to perform layout for dpi-changed HDpi improvements - setting the second parameter to 'true' + ResumeAllLayout(this, DpiHelper.EnableDpiChangedHighDpiImprovements); + } + } + } + } + + /// + /// + /// Occurs when the DPI resolution of the screen this top level window is displayed on changes, + /// either when the top level window is moved between monitors or when the OS settings are changed. + /// + /// + [SRCategory(SR.CatLayout), SRDescription(SR.FormOnDpiChangedDescr)] + public event DpiChangedEventHandler DpiChanged { + add { + Events.AddHandler(EVENT_DPI_CHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DPI_CHANGED, value); + } + } + + /// + /// Handles the WM_DPICHANGED message + /// + private void WmDpiChanged(ref Message m) { + DefWndProc(ref m); + + DpiChangedEventArgs e = new DpiChangedEventArgs(deviceDpi, m); + deviceDpi = e.DeviceDpiNew; + + OnDpiChanged(e); + } + + /// + /// + /// + /// Allows derived form to handle WM_GETDPISCALEDSIZE message. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual bool OnGetDpiScaledSize(int deviceDpiOld, int deviceDpiNew, ref Size desiredSize) { + return false; // scale linearly + } + + /// + /// Handles the WM_GETDPISCALEDSIZE message, this is a chance for the application to + /// scale window size non-lineary. If this message is not processed, the size is scaled linearly by Windows. + /// This message is sent to top level windows before WM_DPICHANGED. + /// If the application responds to this message, the resulting size will be the candidate rectangle + /// sent to WM_DPICHANGED. The WPARAM contains a DPI value.  The size needs to be computed if + /// the window were to switch to this DPI. LPARAM is unused and will be zero. + /// The return value is a size, where the LOWORD is the desired width of the window and the HIWORD + /// is the desired height of the window.  A return value of zero indicates that the app does not + /// want any special behavior and the candidate rectangle will be computed linearly. + /// + private void WmGetDpiScaledSize(ref Message m) { + DefWndProc(ref m); + + Size desiredSize = new Size(); + if (OnGetDpiScaledSize(deviceDpi, NativeMethods.Util.SignedLOWORD(m.WParam), ref desiredSize)) { + m.Result = (IntPtr)(unchecked((Size.Height & 0xFFFF) << 16) | (Size.Width & 0xFFFF)); + } else { + m.Result = IntPtr.Zero; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + + // Want to do this after we fire the event. + if (RightToLeft == RightToLeft.Yes) { + foreach(Control c in this.Controls) { + c.RecreateHandleCore(); + } + } + + } + + /// + /// + /// Thi event fires whenever the form is first shown. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnShown(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SHOWN]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnTextChanged(EventArgs e) { + base.OnTextChanged(e); + + // If there is no control box, there should only be a title bar if text != "". + int newTextEmpty = Text.Length == 0 ? 1 : 0; + if (!ControlBox && formState[FormStateIsTextEmpty] != newTextEmpty) + this.RecreateHandle(); + + formState[FormStateIsTextEmpty] = newTextEmpty; + } + + /// + /// Simulates a InputLanguageChanged event. Used by Control to forward events + /// to the parent form. + /// + /// + internal void PerformOnInputLanguageChanged(InputLanguageChangedEventArgs iplevent) { + OnInputLanguageChanged(iplevent); + } + + /// + /// Simulates a InputLanguageChanging event. Used by Control to forward + /// events to the parent form. + /// + /// + internal void PerformOnInputLanguageChanging(InputLanguageChangingEventArgs iplcevent) { + OnInputLanguageChanging(iplcevent); + } + + /// + /// + /// Processes a command key. Overrides Control.processCmdKey() to provide + /// additional handling of main menu command keys and Mdi accelerators. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (base.ProcessCmdKey(ref msg, keyData)) return true; + + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu != null && curMenu.ProcessCmdKey(ref msg, keyData)) return true; + + // Process MDI accelerator keys. + + bool retValue = false; + + NativeMethods.MSG win32Message = new NativeMethods.MSG(); + win32Message.message = msg.Msg; + win32Message.wParam = msg.WParam; + win32Message.lParam = msg.LParam; + win32Message.hwnd = msg.HWnd; + + if (ctlClient != null && ctlClient.Handle != IntPtr.Zero && + UnsafeNativeMethods.TranslateMDISysAccel(ctlClient.Handle, ref win32Message)) { + + retValue = true; + } + + msg.Msg = win32Message.message; + msg.WParam = win32Message.wParam; + msg.LParam = win32Message.lParam; + msg.HWnd = win32Message.hwnd; + + return retValue; + } + + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the RETURN, and ESCAPE keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + IButtonControl button; + + switch (keyCode) { + case Keys.Return: + button = (IButtonControl)Properties.GetObject(PropDefaultButton); + if (button != null) { + //PerformClick now checks for validationcancelled... + if (button is Control) { + button.PerformClick(); + } + return true; + } + break; + case Keys.Escape: + button = (IButtonControl)Properties.GetObject(PropCancelButton); + if (button != null) { + // In order to keep the behavior in sync with native + // and MFC dialogs, we want to not give the cancel button + // the focus on Escape. If we do, we end up with giving it + // the focus when we reshow the dialog. + // + //if (button is Control) { + // ((Control)button).Focus(); + //} + button.PerformClick(); + return true; + } + break; + } + } + return base.ProcessDialogKey(keyData); + } + + /// + /// + /// + /// Processes a dialog character For a MdiChild. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogChar(char charCode) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Form.ProcessDialogChar [" + charCode.ToString() + "]"); +#endif + // If we're the top-level form or control, we need to do the mnemonic handling + // + if (this.IsMdiChild && charCode != ' ') { + if (ProcessMnemonic(charCode)) { + return true; + } + + // ContainerControl calls ProcessMnemonic starting from the active MdiChild form (this) + // so let's flag it as processed. + formStateEx[FormStateExMnemonicProcessed] = 1; + try{ + return base.ProcessDialogChar(charCode); + } + finally{ + formStateEx[FormStateExMnemonicProcessed] = 0; + } + } + + // Non-MdiChild form, just pass the call to ContainerControl. + return base.ProcessDialogChar(charCode); + } + + + /// + /// + /// [To be supplied.] + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) { + if (formState[FormStateKeyPreview] != 0 && ProcessKeyEventArgs(ref m)) + return true; + return base.ProcessKeyPreview(ref m); + } + + /// + /// + /// [To be supplied.] + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessTabKey(bool forward) { + if (SelectNextControl(ActiveControl, forward, true, true, true)) + return true; + + // I've added a special case for UserControls because they shouldn't cycle back to the + // beginning if they don't have a parent form, such as when they're on an ActiveXBridge. + if (IsMdiChild || ParentFormInternal == null) { + bool selected = SelectNextControl(null, forward, true, true, false); + + if (selected) { + return true; + } + } + + return false; + } + + + /// + /// + /// Raises the FormClosed event for this form when Application.Exit is called. + /// + internal void RaiseFormClosedOnAppExit() { + if (!Modal) { + /* This is not required because Application.ExitPrivate() loops through all forms in the Application.OpenForms collection + // Fire FormClosed event on all MDI children + if (IsMdiContainer) { + FormClosedEventArgs fce = new FormClosedEventArgs(CloseReason.MdiFormClosing); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnFormClosed(fce); + } + } + } + */ + + // Fire FormClosed event on all the forms that this form owns and are not in the Application.OpenForms collection + // This is to be consistent with what WmClose does. + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + if (ownedFormsCount > 0) { + Form[] ownedForms = this.OwnedForms; + FormClosedEventArgs fce = new FormClosedEventArgs(CloseReason.FormOwnerClosing); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + if (ownedForms[i] != null && !Application.OpenFormsInternal.Contains(ownedForms[i])) { + ownedForms[i].OnFormClosed(fce); + } + } + } + } + OnFormClosed(new FormClosedEventArgs(CloseReason.ApplicationExitCall)); + } + + /// + /// + /// Raises the FormClosing event for this form when Application.Exit is called. + /// Returns e.Cancel returned by the event handler. + /// + internal bool RaiseFormClosingOnAppExit() { + FormClosingEventArgs e = new FormClosingEventArgs(CloseReason.ApplicationExitCall, false); + // e.Cancel = !Validate(true); This would cause a breaking change between v2.0 and v1.0/v1.1 in case validation fails. + if (!Modal) { + /* This is not required because Application.ExitPrivate() loops through all forms in the Application.OpenForms collection + // Fire FormClosing event on all MDI children + if (IsMdiContainer) { + FormClosingEventArgs fce = new FormClosingEventArgs(CloseReason.MdiFormClosing, e.Cancel); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnFormClosing(fce); + if (fce.Cancel) { + e.Cancel = true; + break; + } + } + } + } + */ + + // Fire FormClosing event on all the forms that this form owns and are not in the Application.OpenForms collection + // This is to be consistent with what WmClose does. + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + if (ownedFormsCount > 0) { + Form[] ownedForms = this.OwnedForms; + FormClosingEventArgs fce = new FormClosingEventArgs(CloseReason.FormOwnerClosing, false); + for (int i = ownedFormsCount - 1; i >= 0; i--) { + if (ownedForms[i] != null && !Application.OpenFormsInternal.Contains(ownedForms[i])) { + ownedForms[i].OnFormClosing(fce); + if (fce.Cancel) { + e.Cancel = true; + break; + } + } + } + } + } + OnFormClosing(e); + return e.Cancel; + } + + /// + /// + /// + [ + SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive") + ] + internal override void RecreateHandleCore() { + //Debug.Assert( CanRecreateHandle(), "Recreating handle when form is not ready yet." ); + NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); + FormStartPosition oldStartPosition = FormStartPosition.Manual; + + if (!IsMdiChild && (WindowState == FormWindowState.Minimized || WindowState == FormWindowState.Maximized)) { + wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT)); + UnsafeNativeMethods.GetWindowPlacement(new HandleRef(this, Handle), ref wp); + } + + if (StartPosition != FormStartPosition.Manual) { + oldStartPosition = StartPosition; + // Set the startup postion to manual, to stop the form from + // changing position each time RecreateHandle() is called. + StartPosition = FormStartPosition.Manual; + } + + EnumThreadWindowsCallback etwcb = null; + SafeNativeMethods.EnumThreadWindowsCallback callback = null; + if (IsHandleCreated) { + // First put all the owned windows into a list + etwcb = new EnumThreadWindowsCallback(); + if (etwcb != null) { + callback = new SafeNativeMethods.EnumThreadWindowsCallback(etwcb.Callback); + UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), + new NativeMethods.EnumThreadWindowsCallback(callback), + new HandleRef(this, this.Handle)); + // Reset the owner of the windows in the list + etwcb.ResetOwners(); + } + } + + base.RecreateHandleCore(); + + + if (etwcb != null) { + // Set the owner of the windows in the list back to the new Form's handle + etwcb.SetOwners(new HandleRef(this, this.Handle)); + } + + if (oldStartPosition != FormStartPosition.Manual) { + StartPosition = oldStartPosition; + } + + if (wp.length > 0) { + UnsafeNativeMethods.SetWindowPlacement(new HandleRef(this, Handle), ref wp); + } + + if (callback != null) { + GC.KeepAlive(callback); + } + } + + /// + /// + /// + /// Removes a form from the list of owned forms. Also sets the owner of the + /// removed form to null. + /// + /// + public void RemoveOwnedForm(Form ownedForm) { + if (ownedForm == null) + return; + + if (ownedForm.OwnerInternal != null) { + ownedForm.Owner = null; // NOTE: this will call RemoveOwnedForm again, bypassing if. + return; + } + + Form[] ownedForms = (Form[])Properties.GetObject(PropOwnedForms); + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + + if (ownedForms != null) { + for (int i = 0; i < ownedFormsCount; i++) { + if (ownedForm.Equals(ownedForms[i])) { + + // clear out the reference. + // + ownedForms[i] = null; + + // compact the array. + // + if (i + 1 < ownedFormsCount) { + Array.Copy(ownedForms, i + 1, ownedForms, i, ownedFormsCount - i - 1); + ownedForms[ownedFormsCount - 1] = null; + } + ownedFormsCount--; + } + } + + Properties.SetInteger(PropOwnedFormsCount, ownedFormsCount); + } + } + + /// + /// Resets the form's icon the the default value. + /// + private void ResetIcon() { + icon = null; + if (smallIcon != null) { + smallIcon.Dispose(); + smallIcon = null; + } + formState[FormStateIconSet] = 0; + UpdateWindowIcon(true); + } + + void ResetSecurityTip(bool modalOnly) { + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip != null) { + if ((modalOnly && secTip.Modal) || !modalOnly) { + secTip.Dispose(); + secTip = null; + Properties.SetObject(PropSecurityTip, null); + } + } + } + + /// + /// Resets the TransparencyKey to Color.Empty. + /// + private void ResetTransparencyKey() { + TransparencyKey = Color.Empty; + } + + /// + /// + /// Occurs when the form enters the sizing modal loop + /// + [SRCategory(SR.CatAction), SRDescription(SR.FormOnResizeBeginDescr)] + public event EventHandler ResizeBegin { + add { + Events.AddHandler(EVENT_RESIZEBEGIN, value); + } + remove { + Events.RemoveHandler(EVENT_RESIZEBEGIN, value); + } + } + + /// + /// + /// Occurs when the control exits the sizing modal loop. + /// + [SRCategory(SR.CatAction), SRDescription(SR.FormOnResizeEndDescr)] + public event EventHandler ResizeEnd { + add { + Events.AddHandler(EVENT_RESIZEEND, value); + } + remove { + Events.RemoveHandler(EVENT_RESIZEEND, value); + } + } + + /// + /// This is called when we have just been restored after being + /// minimized. At this point we resume our layout. + /// + private void ResumeLayoutFromMinimize() { + // If we're currently minimized, resume our layout because we are + // about to snap out of it. + if (formState[FormStateWindowState] == (int)FormWindowState.Minimized) { + ResumeLayout(); + } + } + + // If someone set Location or Size while the form was maximized or minimized, + // we had to cache the new value away until after the form was restored to normal size. + // This function is called after WindowState changes, and handles the above logic. + // In the normal case where no one sets Location or Size programmatically, + // Windows does the restoring for us. + // + private void RestoreWindowBoundsIfNecessary() { + if (WindowState == FormWindowState.Normal) { + Size restoredSize = restoredWindowBounds.Size; + if ((restoredWindowBoundsSpecified & BoundsSpecified.Size) != 0) { + restoredSize = SizeFromClientSize(restoredSize.Width, restoredSize.Height); + } + SetBounds(restoredWindowBounds.X, restoredWindowBounds.Y, + formStateEx[FormStateExWindowBoundsWidthIsClientSize]==1 ? restoredSize.Width : restoredWindowBounds.Width, + formStateEx[FormStateExWindowBoundsHeightIsClientSize]==1 ? restoredSize.Height : restoredWindowBounds.Height, + restoredWindowBoundsSpecified); + restoredWindowBoundsSpecified = 0; + restoredWindowBounds = new Rectangle(-1, -1, -1, -1); + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 0; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 0; + } + } + + void RestrictedProcessNcActivate() { + Debug.Assert(IsRestrictedWindow, "This should only be called for restricted windows"); + + // Ignore if tearing down... + // + if (IsDisposed || Disposing) { + return; + } + + // Note that this.Handle does not get called when the handle hasn't been created yet (See VSWhidbey 264275 & 264291) + // + SecurityToolTip secTip = (SecurityToolTip)Properties.GetObject(PropSecurityTip); + if (secTip == null) { + if (IsHandleCreated && UnsafeNativeMethods.GetForegroundWindow() == this.Handle) { + secTip = new SecurityToolTip(this); + Properties.SetObject(PropSecurityTip, secTip); + } + } + else if (!IsHandleCreated || UnsafeNativeMethods.GetForegroundWindow() != this.Handle) + { + secTip.Pop(false /*noLongerFirst*/); + } + else + { + secTip.Show(); + } + } + + /// + /// Decrements updateMenuHandleSuspendCount. If updateMenuHandleSuspendCount + /// becomes zero and updateMenuHandlesDeferred is true, updateMenuHandles + /// is called. + /// + private void ResumeUpdateMenuHandles() { + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + if (suspendCount <= 0) { + throw new InvalidOperationException(SR.GetString(SR.TooManyResumeUpdateMenuHandles)); + } + + formStateEx[FormStateExUpdateMenuHandlesSuspendCount] = --suspendCount; + if (suspendCount == 0 && formStateEx[FormStateExUpdateMenuHandlesDeferred] != 0) { + UpdateMenuHandles(); + } + } + + /// + /// + /// Selects this form, and optionally selects the next/previous control. + /// + protected override void Select(bool directed, bool forward) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded"); + IntSecurity.ModifyFocus.Demand(); + + SelectInternal(directed, forward); + } + + /// + /// Selects this form, and optionally selects the next/previous control. + /// Does the actual work without the security check. + /// + // SECURITY WARNING: This method bypasses a security demand. Use with caution! + private void SelectInternal(bool directed, bool forward) { + // SelectNextControl & set_ActiveControl below demand a ModifyFocus permission, let's revert it. + // Reviewed : This method is to be called by Form only so asserting here is safe. + // + IntSecurity.ModifyFocus.Assert(); + + if (directed) { + SelectNextControl(null, forward, true, true, false); + } + + if (TopLevel) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, Handle)); + } + else if (IsMdiChild) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(MdiParentInternal, MdiParentInternal.Handle)); + MdiParentInternal.MdiClient.SendMessage(NativeMethods.WM_MDIACTIVATE, Handle, 0); + } + else { + Form form = ParentFormInternal; + if (form != null) form.ActiveControl = this; + } + + } + + /// + /// + /// Base function that performs scaling of the form. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float x, float y) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ScaleCore(" + x + ", " + y + ")"); + SuspendLayout(); + try { + if (WindowState == FormWindowState.Normal) { + //Get size values in advance to prevent one change from affecting another. + Size clientSize = ClientSize; + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + if (!MinimumSize.IsEmpty) { + MinimumSize = ScaleSize(minSize, x, y); + } + if (!MaximumSize.IsEmpty) { + MaximumSize = ScaleSize(maxSize, x, y); + } + ClientSize = ScaleSize(clientSize, x, y); + } + + ScaleDockPadding(x, y); + + foreach(Control control in Controls) { + if (control != null) { +#pragma warning disable 618 + control.Scale(x, y); +#pragma warning restore 618 + } + } + } + finally { + ResumeLayout(); + } + } + + /// + /// + /// For overrides this to calculate scaled bounds based on the restored rect + /// if it is maximized or minimized. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + // If we're maximized or minimized, scale using the restored bounds, not + // the real bounds. + if (WindowState != FormWindowState.Normal) { + bounds = RestoreBounds; + } + return base.GetScaledBounds(bounds, factor, specified); + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + formStateEx[FormStateExInScale] = 1; + try { + + // don't scale the location of MDI child forms + if (MdiParentInternal != null) { + specified &= ~BoundsSpecified.Location; + } + + base.ScaleControl(factor, specified); + } + finally { + formStateEx[FormStateExInScale] = 0; + } + } + + + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + if (WindowState != FormWindowState.Normal) { + // See RestoreWindowBoundsIfNecessary for an explanation of this + // VSWhidbey 503385. Only restore position when x,y is not -1,-1 + if (x != -1 || y != -1) + { + restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.X | BoundsSpecified.Y)); + } + restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.Width | BoundsSpecified.Height)); + + if ((specified & BoundsSpecified.X) != 0) + restoredWindowBounds.X = x; + if ((specified & BoundsSpecified.Y) != 0) + restoredWindowBounds.Y = y; + if ((specified & BoundsSpecified.Width) != 0) { + restoredWindowBounds.Width = width; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 0; + } + if ((specified & BoundsSpecified.Height) != 0) { + restoredWindowBounds.Height = height; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 0; + } + } + + //Update RestoreBounds + if ((specified & BoundsSpecified.X) != 0) + restoreBounds.X = x; + if ((specified & BoundsSpecified.Y) != 0) + restoreBounds.Y = y; + if ((specified & BoundsSpecified.Width) != 0 || restoreBounds.Width == -1) + restoreBounds.Width = width; + if ((specified & BoundsSpecified.Height) != 0 || restoreBounds.Height == -1) + restoreBounds.Height = height; + + // Enforce maximum size... + // + if (WindowState == FormWindowState.Normal + && (this.Height != height || this.Width != width)) { + + Size max = SystemInformation.MaxWindowTrackSize; + if (height > max.Height) { + height = max.Height; + } + if (width > max.Width) { + width = max.Width; + } + } + + // Only enforce the minimum size if the form has a border and is a top + // level form. + // + FormBorderStyle borderStyle = FormBorderStyle; + if (borderStyle != FormBorderStyle.None + && borderStyle != FormBorderStyle.FixedToolWindow + && borderStyle != FormBorderStyle.SizableToolWindow + && ParentInternal == null) { + + Size min = SystemInformation.MinWindowTrackSize; + if (height < min.Height) { + height = min.Height; + } + if (width < min.Width) { + width = min.Width; + } + } + + if (IsRestrictedWindow) { + // Check to ensure that the title bar, and all corners of the window, are visible on a monitor + // + + Rectangle adjustedBounds = ApplyBoundsConstraints(x,y,width,height); + if (adjustedBounds != new Rectangle(x,y,width,height)) { + + // SECREVIEW VSWhidbey 430541 - the base will call ApplyBoundsConstraints again, so + // we're still OK (even though we have security code in a virtual function). + // + // Keeping this here for compat reasons - we want to manually call base here with BoundsSpecified.All + // so we ---- over the specified bounds. + base.SetBoundsCore(adjustedBounds.X, adjustedBounds.Y, adjustedBounds.Width, adjustedBounds.Height, BoundsSpecified.All); + return; + } + } + + base.SetBoundsCore(x, y, width, height, specified); + } + + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + // apply min/max size constraints + Rectangle adjustedBounds = base.ApplyBoundsConstraints(suggestedX, suggestedY, proposedWidth, proposedHeight); + // run through size restrictions in Internet. + if (IsRestrictedWindow) { + // Check to ensure that the title bar, and all corners of the window, are visible on a monitor + // + + Screen[] screens = Screen.AllScreens; + bool topLeft = false; + bool topRight = false; + bool bottomLeft = false; + bool bottomRight = false; + + for (int i=0; i + /// Sets the defaultButton for the form. The defaultButton is "clicked" when + /// the user presses Enter. + /// + private void SetDefaultButton(IButtonControl button) { + IButtonControl defaultButton = (IButtonControl)Properties.GetObject(PropDefaultButton); + + if (defaultButton != button) { + if (defaultButton != null) defaultButton.NotifyDefault(false); + Properties.SetObject(PropDefaultButton, button); + if (button != null) button.NotifyDefault(true); + } + } + + + /// + /// + /// Sets the clientSize of the form. This will adjust the bounds of the form + /// to make the clientSize the requested size. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void SetClientSizeCore(int x, int y) { + bool hadHScroll = HScroll, hadVScroll = VScroll; + base.SetClientSizeCore(x, y); + + if (IsHandleCreated) { + // Adjust for the scrollbars, if they were introduced by + // the call to base.SetClientSizeCore + if (VScroll != hadVScroll) { + if (VScroll) x += SystemInformation.VerticalScrollBarWidth; + } + if (HScroll != hadHScroll) { + if (HScroll) y += SystemInformation.HorizontalScrollBarHeight; + } + if (x != ClientSize.Width || y != ClientSize.Height) { + base.SetClientSizeCore(x, y); + } + } + formState[FormStateSetClientSize] = 1; + } + + /// + /// + /// + /// Sets the bounds of the form in desktop coordinates. + /// + public void SetDesktopBounds(int x, int y, int width, int height) { + Rectangle workingArea = SystemInformation.WorkingArea; + SetBounds(x + workingArea.X, y + workingArea.Y, width, height, BoundsSpecified.All); + } + + /// + /// + /// Sets the location of the form in desktop coordinates. + /// + public void SetDesktopLocation(int x, int y) { + Rectangle workingArea = SystemInformation.WorkingArea; + Location = new Point(workingArea.X + x, workingArea.Y + y); + } + + + /// + /// + /// Makes the control display by setting the visible property to true + /// + public void Show(IWin32Window owner) { + if (owner == this) { + throw new InvalidOperationException(SR.GetString(SR.OwnsSelfOrOwner, + "Show")); + } + else if (Visible) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnVisible, + "Show")); + } + else if (!Enabled) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnDisabled, + "Show")); + } + else if (!TopLevel) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnNonTopLevel, + "Show")); + } + else if (!SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + else if ( (owner != null) && ((int)UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, Control.GetSafeHandle(owner)), NativeMethods.GWL_EXSTYLE) + & NativeMethods.WS_EX_TOPMOST) == 0 ) { // It's not the top-most window + if (owner is Control) { + owner = ((Control)owner).TopLevelControlInternal; + } + } + IntPtr hWndActive = UnsafeNativeMethods.GetActiveWindow(); + IntPtr hWndOwner = owner == null ? hWndActive : Control.GetSafeHandle(owner); + IntPtr hWndOldOwner = IntPtr.Zero; + Properties.SetObject(PropDialogOwner, owner); + Form oldOwner = OwnerInternal; + if (owner is Form && owner != oldOwner) { + Owner = (Form)owner; + } + if (hWndOwner != IntPtr.Zero && hWndOwner != Handle) { + // Catch the case of a window trying to own its owner + if (UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, hWndOwner), NativeMethods.GWL_HWNDPARENT) == Handle) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "show"), "owner"); + } + + // Set the new owner. + hWndOldOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(owner, hWndOwner)); + + } + Visible = true; + } + + /// + /// + /// Displays this form as a modal dialog box with no owner window. + /// + public DialogResult ShowDialog() { + return ShowDialog(null); + } + + /// + /// + /// Shows this form as a modal dialog with the specified owner. + /// + public DialogResult ShowDialog(IWin32Window owner) { + if (owner == this) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "showDialog"), "owner"); + } + else if (Visible) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnVisible, + "showDialog")); + } + else if (!Enabled) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnDisabled, + "showDialog")); + } + else if (!TopLevel) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnNonTopLevel, + "showDialog")); + } + else if (Modal) { + throw new InvalidOperationException(SR.GetString(SR.ShowDialogOnModal, + "showDialog")); + } + else if (!SystemInformation.UserInteractive) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + else if ( (owner != null) && ((int)UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, Control.GetSafeHandle(owner)), NativeMethods.GWL_EXSTYLE) + & NativeMethods.WS_EX_TOPMOST) == 0 ) { // It's not the top-most window + if (owner is Control) { + owner = ((Control)owner).TopLevelControlInternal; + } + } + + this.CalledOnLoad = false; + this.CalledMakeVisible = false; + + // for modal dialogs make sure we reset close reason. + this.CloseReason = CloseReason.None; + + IntPtr hWndCapture = UnsafeNativeMethods.GetCapture(); + if (hWndCapture != IntPtr.Zero) { + UnsafeNativeMethods.SendMessage(new HandleRef(null, hWndCapture), NativeMethods.WM_CANCELMODE, IntPtr.Zero, IntPtr.Zero); + SafeNativeMethods.ReleaseCapture(); + } + IntPtr hWndActive = UnsafeNativeMethods.GetActiveWindow(); + IntPtr hWndOwner = owner == null ? hWndActive : Control.GetSafeHandle(owner); + IntPtr hWndOldOwner = IntPtr.Zero; + Properties.SetObject(PropDialogOwner, owner); + + Form oldOwner = OwnerInternal; + + if (owner is Form && owner != oldOwner) { + Owner = (Form)owner; + } + + try { + SetState(STATE_MODAL, true); + + // ASURT 102728 + // It's possible that while in the process of creating the control, + // (i.e. inside the CreateControl() call) the dialog can be closed. + // e.g. A user might call Close() inside the OnLoad() event. + // Calling Close() will set the DialogResult to some value, so that + // we'll know to terminate the RunDialog loop immediately. + // Thus we must initialize the DialogResult *before* the call + // to CreateControl(). + // + dialogResult = DialogResult.None; + + // V#36617 - if "this" is an MDI parent then the window gets activated, + // causing GetActiveWindow to return "this.handle"... to prevent setting + // the owner of this to this, we must create the control AFTER calling + // GetActiveWindow. + // + CreateControl(); + + if (hWndOwner != IntPtr.Zero && hWndOwner != Handle) { + // Catch the case of a window trying to own its owner + if (UnsafeNativeMethods.GetWindowLong(new HandleRef(owner, hWndOwner), NativeMethods.GWL_HWNDPARENT) == Handle) { + throw new ArgumentException(SR.GetString(SR.OwnsSelfOrOwner, + "showDialog"), "owner"); + } + + // Set the new owner. + hWndOldOwner = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(owner, hWndOwner)); + } + + try { + // If the DialogResult was already set, then there's + // no need to actually display the dialog. + // + if (dialogResult == DialogResult.None) + { + // Application.RunDialog sets this dialog to be visible. + Application.RunDialog(this); + } + } + finally { + // Call SetActiveWindow before setting Visible = false. + // + + if (!UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndActive))) hWndActive = hWndOwner; + if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndActive)) && SafeNativeMethods.IsWindowVisible(new HandleRef(null, hWndActive))) { + UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, hWndActive)); + } + else if (UnsafeNativeMethods.IsWindow(new HandleRef(null, hWndOwner)) && SafeNativeMethods.IsWindowVisible(new HandleRef(null, hWndOwner))){ + UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, hWndOwner)); + } + + SetVisibleCore(false); + if (IsHandleCreated) { + + // VSWhidbey 94917: If this is a dialog opened from an MDI Container, then invalidate + // so that child windows will be properly updated. + if (this.OwnerInternal != null && + this.OwnerInternal.IsMdiContainer) { + this.OwnerInternal.Invalidate(true); + this.OwnerInternal.Update(); + } + + // VSWhidbey 430476 - Everett/RTM used to wrap this in an assert for AWP. + DestroyHandle(); + } + SetState(STATE_MODAL, false); + } + } + finally { + Owner = oldOwner; + Properties.SetObject(PropDialogOwner, null); + } + return DialogResult; + } + + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeAutoScaleBaseSize() { + return formState[FormStateAutoScaling] != 0; + } + + private bool ShouldSerializeClientSize() { + return true; + } + + /// + /// Indicates whether the property should be persisted. + /// + private bool ShouldSerializeIcon() { + return formState[FormStateIconSet] == 1; + } + + /// + /// Determines if the Location property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeLocation() { + return Left != 0 || Top != 0; + } + + /// + /// + /// + /// Indicates whether the property should be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeSize() { + return false; + } + + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal bool ShouldSerializeTransparencyKey() { + return !TransparencyKey.Equals(Color.Empty); + } + + /// + /// This is called when we are about to become minimized. Laying out + /// while minimized can be a problem because the physical dimensions + /// of the window are very small. So, we simply suspend. + /// + private void SuspendLayoutForMinimize() { + // If we're not currently minimized, suspend our layout because we are + // about to become minimized + if (formState[FormStateWindowState] != (int)FormWindowState.Minimized) { + SuspendLayout(); + } + } + + /// + /// Increments updateMenuHandleSuspendCount. + /// + private void SuspendUpdateMenuHandles() { + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + formStateEx[FormStateExUpdateMenuHandlesSuspendCount] = ++suspendCount; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Text: " + Text; + } + + /// + /// Updates the autoscalebasesize based on the current font. + /// + /// + private void UpdateAutoScaleBaseSize() { + autoScaleBaseSize = Size.Empty; + } + + private void UpdateRenderSizeGrip() { + int current = formState[FormStateRenderSizeGrip]; + switch (FormBorderStyle) { + case FormBorderStyle.None: + case FormBorderStyle.FixedSingle: + case FormBorderStyle.Fixed3D: + case FormBorderStyle.FixedDialog: + case FormBorderStyle.FixedToolWindow: + formState[FormStateRenderSizeGrip] = 0; + break; + case FormBorderStyle.Sizable: + case FormBorderStyle.SizableToolWindow: + switch (SizeGripStyle) { + case SizeGripStyle.Show: + formState[FormStateRenderSizeGrip] = 1; + break; + case SizeGripStyle.Hide: + formState[FormStateRenderSizeGrip] = 0; + break; + case SizeGripStyle.Auto: + if (GetState(STATE_MODAL)) { + formState[FormStateRenderSizeGrip] = 1; + } + else { + formState[FormStateRenderSizeGrip] = 0; + } + break; + } + break; + } + + if (formState[FormStateRenderSizeGrip] != current) { + Invalidate(); + } + } + + /// + /// + /// Updates the default button based on current selection, and the + /// acceptButton property. + /// + /// + protected override void UpdateDefaultButton() { + ContainerControl cc = this; + + while (cc.ActiveControl is ContainerControl) + { + cc = cc.ActiveControl as ContainerControl; + Debug.Assert(cc != null); + + if (cc is Form) + { + // VSWhidbey#291004: Don't allow a parent form to get its default button from a child form, + // otherwise the two forms will 'compete' for the Enter key and produce unpredictable results. + // This is aimed primarily at fixing the behavior of MDI container forms. + cc = this; + break; + } + } + + if (cc.ActiveControl is IButtonControl) + { + SetDefaultButton((IButtonControl) cc.ActiveControl); + } + else + { + SetDefaultButton(AcceptButton); + } + } + + /// + /// Updates the underlying hWnd with the correct parent/owner of the form. + /// + /// + private void UpdateHandleWithOwner() { + if (IsHandleCreated && TopLevel) { + HandleRef ownerHwnd = NativeMethods.NullHandleRef; + + Form owner = (Form)Properties.GetObject(PropOwner); + + if (owner != null) { + ownerHwnd = new HandleRef(owner, owner.Handle); + } + else { + if (!ShowInTaskbar) { + ownerHwnd = TaskbarOwner; + } + } + + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, ownerHwnd); + } + } + + /// + /// Updates the layered window attributes if the control + /// is in layered mode. + /// + private void UpdateLayered() { + if ((formState[FormStateLayered] != 0) && IsHandleCreated && TopLevel && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + bool result; + + Color transparencyKey = TransparencyKey; + + if (transparencyKey.IsEmpty) { + + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), 0, OpacityAsByte, NativeMethods.LWA_ALPHA); + } + else if (OpacityAsByte == 255) { + // Windows doesn't do so well setting colorkey and alpha, so avoid it if we can + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), ColorTranslator.ToWin32(transparencyKey), 0, NativeMethods.LWA_COLORKEY); + } + else { + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), ColorTranslator.ToWin32(transparencyKey), + OpacityAsByte, NativeMethods.LWA_ALPHA | NativeMethods.LWA_COLORKEY); + } + + if (!result) { + throw new Win32Exception(); + } + } + } + + /// + private void UpdateMenuHandles() { + Form form; + + // Forget the current menu. + if (Properties.GetObject(PropCurMenu) != null) { + Properties.SetObject(PropCurMenu, null); + } + + if (IsHandleCreated) { + if (!TopLevel) { + UpdateMenuHandles(null, true); + } + else { + form = ActiveMdiChildInternal; + if (form != null) { + UpdateMenuHandles(form.MergedMenuPrivate, true); + } + else { + UpdateMenuHandles(Menu, true); + } + } + } + } + + private void UpdateMenuHandles(MainMenu menu, bool forceRedraw) { + Debug.Assert(IsHandleCreated, "shouldn't call when handle == 0"); + + int suspendCount = formStateEx[FormStateExUpdateMenuHandlesSuspendCount]; + if (suspendCount > 0 && menu != null) { + formStateEx[FormStateExUpdateMenuHandlesDeferred] = 1; + return; + } + + MainMenu curMenu = menu; + if (curMenu != null) { + curMenu.form = this; + } + + if (curMenu != null || Properties.ContainsObject(PropCurMenu)) { + Properties.SetObject(PropCurMenu, curMenu); + } + + if (ctlClient == null || !ctlClient.IsHandleCreated) { + if (menu != null) { + UnsafeNativeMethods.SetMenu(new HandleRef(this, Handle), new HandleRef(menu, menu.Handle)); + } + else { + UnsafeNativeMethods.SetMenu(new HandleRef(this, Handle), NativeMethods.NullHandleRef); + } + } + else { + Debug.Assert( IsMdiContainer, "Not an MDI container!" ); + // when both MainMenuStrip and Menu are set, we honor the win32 menu over + // the MainMenuStrip as the place to store the system menu controls for the maximized MDI child. + + MenuStrip mainMenuStrip = MainMenuStrip; + if( mainMenuStrip == null || menu!=null){ // We are dealing with a Win32 Menu; MenuStrip doesn't have control buttons. + + // We have a MainMenu and we're going to use it + + // VSWhidbey 202747: We need to set the "dummy" menu even when a menu is being removed + // (set to null) so that duplicate control buttons are not placed on the menu bar when + // an ole menu is being removed. + // Make MDI forget the mdi item position. + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + + if (dummyMenu == null) { + dummyMenu = new MainMenu(); + dummyMenu.ownerForm = this; + Properties.SetObject(PropDummyMenu, dummyMenu); + } + UnsafeNativeMethods.SendMessage(new HandleRef(ctlClient, ctlClient.Handle), NativeMethods.WM_MDISETMENU, dummyMenu.Handle, IntPtr.Zero); + + if (menu != null) { + + // Microsoft, 5/2/1998 - don't use Win32 native Mdi lists... + // + UnsafeNativeMethods.SendMessage(new HandleRef(ctlClient, ctlClient.Handle), NativeMethods.WM_MDISETMENU, menu.Handle, IntPtr.Zero); + } + } + + // VSWhidbey#93544 (New fix: Only destroy Win32 Menu if using a MenuStrip) + if( menu == null && mainMenuStrip != null ){ // If MainMenuStrip, we need to remove any Win32 Menu to make room for it. + IntPtr hMenu = UnsafeNativeMethods.GetMenu(new HandleRef(this, this.Handle)); + if (hMenu != IntPtr.Zero) { + + // We had a MainMenu and now we're switching over to MainMenuStrip + + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Remove the current menu. + UnsafeNativeMethods.SetMenu(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef); + + // because we have messed with the child's system menu by shoving in our own dummy menu, + // once we clear the main menu we're in trouble - this eats the close, minimize, maximize gadgets + // of the child form. (See WM_MDISETMENU in MSDN) + Form activeMdiChild = this.ActiveMdiChildInternal; + if (activeMdiChild != null && activeMdiChild.WindowState == FormWindowState.Maximized) { + activeMdiChild.RecreateHandle(); + } + + // VSWhidbey 202747: Since we're removing a menu but we possibly had a menu previously, + // we need to clear the cached size so that new size calculations will be performed correctly. + CommonProperties.xClearPreferredSizeCache(this); + } + } + } + if (forceRedraw) { + SafeNativeMethods.DrawMenuBar(new HandleRef(this, Handle)); + } + formStateEx[FormStateExUpdateMenuHandlesDeferred] = 0; + } + + // Call this function instead of UpdateStyles() when the form's client-size must + // be preserved e.g. when changing the border style. + // + internal void UpdateFormStyles() { + Size previousClientSize = ClientSize; + base.UpdateStyles(); + if (!ClientSize.Equals(previousClientSize)) { + ClientSize = previousClientSize; + } + } + + private static Type FindClosestStockType(Type type) { + Type[] stockTypes = new Type[] { typeof (MenuStrip) }; // as opposed to what we had before... see VSWhidbey 516929 + // simply add other types here from most specific to most generic if we want to merge other types of toolstrips... + foreach(Type t in stockTypes) { + if(t.IsAssignableFrom(type)) { + return t; + } + } + return null; + } + + /// ToolStrip MDI Merging support + private void UpdateToolStrip() { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "\r\n============"); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: starting merge operation"); + + // try to merge each one of the MDI Child toolstrip with the first toolstrip + // in the parent form that has the same type NOTE: THESE LISTS ARE ORDERED (See ToolstripManager) + ToolStrip thisToolstrip = MainMenuStrip; + ArrayList childrenToolStrips = ToolStripManager.FindMergeableToolStrips(ActiveMdiChildInternal); + + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: found "+ thisToolStrips.Count +" mergeable toolstrip in this"); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: found "+childrenToolStrips.Count+" mergeable toolstrip in children"); + + // revert any previous merge + if(thisToolstrip != null) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MergeDebug.TraceVerbose, "ToolStripMerging: reverting merge in " + destinationToolStrip.Name); + ToolStripManager.RevertMerge(thisToolstrip); + } + + // if someone has a MdiWindowListItem specified we should merge in the + // names of all the MDI child forms. + UpdateMdiWindowListStrip(); + + if(ActiveMdiChildInternal != null) { + + // do the new merging + foreach(ToolStrip sourceToolStrip in childrenToolStrips) { + Type closestMatchingSourceType = FindClosestStockType(sourceToolStrip.GetType()); + if(thisToolstrip != null) { + Type closestMatchingTargetType = FindClosestStockType(thisToolstrip.GetType()); + if (closestMatchingTargetType != null && closestMatchingSourceType != null && + closestMatchingSourceType == closestMatchingTargetType && + thisToolstrip.GetType().IsAssignableFrom(sourceToolStrip.GetType())) { + ToolStripManager.Merge(sourceToolStrip, thisToolstrip); + break; + } + } + } + } + + + // add in the control gadgets for the mdi child form to the first menu strip + Form activeMdiForm = ActiveMdiChildInternal; + UpdateMdiControlStrip(activeMdiForm != null && activeMdiForm.IsMaximized); + } + + private void UpdateMdiControlStrip(bool maximized) { + + if (formStateEx[FormStateExInUpdateMdiControlStrip] != 0) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Detected re-entrant call to UpdateMdiControlStrip, returning."); + return; + } + + // we dont want to be redundantly called as we could merge in two control menus. + formStateEx[FormStateExInUpdateMdiControlStrip] = 1; + + try { + MdiControlStrip mdiControlStrip = this.MdiControlStrip; + + if (MdiControlStrip != null) { + if (mdiControlStrip.MergedMenu != null) { +#if DEBUG + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Calling RevertMerge on MDIControl strip."); + int numWindowListItems = 0; + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null) { + numWindowListItems = MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count; + } +#endif + + ToolStripManager.RevertMergeInternal(mdiControlStrip.MergedMenu,mdiControlStrip,/*revertMDIStuff*/true); + +#if DEBUG + // double check that RevertMerge doesnt accidentally revert more than it should. + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null) { + Debug.Assert(numWindowListItems == MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count, "Calling RevertMerge modified the mdiwindowlistitem"); + } +#endif + } + mdiControlStrip.MergedMenu = null; + mdiControlStrip.Dispose(); + MdiControlStrip = null; + } + + if (ActiveMdiChildInternal != null && maximized) { + if (ActiveMdiChildInternal.ControlBox) { + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: Detected ControlBox on ActiveMDI child, adding in MDIControlStrip."); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && this.Menu != null, "UpdateMdiControlStrip: Bailing as we detect there's already an HMenu to do this for us."); + + // determine if we need to add control gadgets into the MenuStrip + if (this.Menu == null) { + // double check GetMenu incase someone is using interop + IntPtr hMenu = UnsafeNativeMethods.GetMenu(new HandleRef(this, this.Handle)); + if (hMenu == IntPtr.Zero) { + MenuStrip sourceMenuStrip = ToolStripManager.GetMainMenuStrip(this); + if (sourceMenuStrip != null) { + this.MdiControlStrip = new MdiControlStrip(ActiveMdiChildInternal); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: built up an MDI control strip for " + ActiveMdiChildInternal.Text + " with " + MdiControlStrip.Items.Count.ToString(CultureInfo.InvariantCulture) + " items."); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: merging MDI control strip into source menustrip - items before: " + sourceMenuStrip.Items.Count.ToString(CultureInfo.InvariantCulture)); + ToolStripManager.Merge(this.MdiControlStrip, sourceMenuStrip); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiControlStrip: merging MDI control strip into source menustrip - items after: " + sourceMenuStrip.Items.Count.ToString(CultureInfo.InvariantCulture)); + this.MdiControlStrip.MergedMenu = sourceMenuStrip; + } + } + } + } + } + } + finally { + formStateEx[FormStateExInUpdateMdiControlStrip] = 0; + } + } + + internal void UpdateMdiWindowListStrip() { + if (IsMdiContainer) { + if (MdiWindowListStrip != null && MdiWindowListStrip.MergedMenu != null) { + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: Calling RevertMerge on MDIWindowList strip."); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null, "UpdateMdiWindowListStrip: MdiWindowListItem dropdown item count before: " + MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count.ToString()); + ToolStripManager.RevertMergeInternal(MdiWindowListStrip.MergedMenu,MdiWindowListStrip,/*revertMdiStuff*/true); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose && MdiWindowListStrip.MergedMenu.MdiWindowListItem != null, "UpdateMdiWindowListStrip: MdiWindowListItem dropdown item count after: " + MdiWindowListStrip.MergedMenu.MdiWindowListItem.DropDownItems.Count.ToString()); + } + + MenuStrip sourceMenuStrip = ToolStripManager.GetMainMenuStrip(this); + if (sourceMenuStrip != null && sourceMenuStrip.MdiWindowListItem != null) { + if (MdiWindowListStrip == null) { + MdiWindowListStrip = new MdiWindowListStrip(); + } + int nSubItems = sourceMenuStrip.MdiWindowListItem.DropDownItems.Count; + bool shouldIncludeSeparator = (nSubItems > 0 && + !(sourceMenuStrip.MdiWindowListItem.DropDownItems[nSubItems-1] is ToolStripSeparator)); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: Calling populate items."); + MdiWindowListStrip.PopulateItems(this, sourceMenuStrip.MdiWindowListItem, shouldIncludeSeparator); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: mdiwindowlist dd item count before: " + sourceMenuStrip.MdiWindowListItem.DropDownItems.Count.ToString()); + ToolStripManager.Merge(MdiWindowListStrip, sourceMenuStrip); + //MERGEDEBUG Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "UpdateMdiWindowListStrip: mdiwindowlist dd item count after: " + sourceMenuStrip.MdiWindowListItem.DropDownItems.Count.ToString()); + MdiWindowListStrip.MergedMenu = sourceMenuStrip; + } + + } + } + + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResizeBegin(EventArgs e) + { + if (CanRaiseEvents) + { + EventHandler handler = (EventHandler)Events[EVENT_RESIZEBEGIN]; + if (handler != null) handler(this, e); + } + } + + /// + /// + /// Raises the + /// event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnResizeEnd(EventArgs e) + { + if (CanRaiseEvents) + { + EventHandler handler = (EventHandler)Events[EVENT_RESIZEEND]; + if (handler != null) handler(this, e); + } + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnStyleChanged(EventArgs e) { + base.OnStyleChanged(e); + AdjustSystemMenu(); + } + + /// + /// Updates the window icon. + /// + /// + private void UpdateWindowIcon(bool redrawFrame) { + if (IsHandleCreated) { + Icon icon; + + // Preserve Win32 behavior by keeping the icon we set NULL if + // the user hasn't specified an icon and we are a dialog frame. + // + if ((FormBorderStyle == FormBorderStyle.FixedDialog && formState[FormStateIconSet] == 0 && !IsRestrictedWindow) || !ShowIcon) { + icon = null; + } + else { + icon = Icon; + } + + if (icon != null) { + if (smallIcon == null) { + try { + smallIcon = new Icon(icon, SystemInformation.SmallIconSize); + } + catch { + } + } + + if (smallIcon != null) { + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_SMALL,smallIcon.Handle); + } + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_BIG,icon.Handle); + } + else { + + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_SMALL,0); + SendMessage(NativeMethods.WM_SETICON,NativeMethods.ICON_BIG,0); + } + + if (redrawFrame) { + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); + } + } + } + + /// + /// Updated the window state from the handle, if created. + /// + /// + // + // This function is called from all over the place, including my personal favorite, + // WM_ERASEBKGRND. Seems that's one of the first messages we get when a user clicks the min/max + // button, even before WM_WINDOWPOSCHANGED. + private void UpdateWindowState() { + if (IsHandleCreated) { + FormWindowState oldState = WindowState; + NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); + wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT)); + UnsafeNativeMethods.GetWindowPlacement(new HandleRef(this, Handle), ref wp); + + switch (wp.showCmd) { + case NativeMethods.SW_NORMAL: + case NativeMethods.SW_RESTORE: + case NativeMethods.SW_SHOW: + case NativeMethods.SW_SHOWNA: + case NativeMethods.SW_SHOWNOACTIVATE: + if (formState[FormStateWindowState] != (int)FormWindowState.Normal) { + formState[FormStateWindowState] = (int)FormWindowState.Normal; + } + break; + case NativeMethods.SW_SHOWMAXIMIZED: + if (formState[FormStateMdiChildMax] == 0) { + formState[FormStateWindowState] = (int)FormWindowState.Maximized; + } + break; + case NativeMethods.SW_SHOWMINIMIZED: + case NativeMethods.SW_MINIMIZE: + case NativeMethods.SW_SHOWMINNOACTIVE: + if (formState[FormStateMdiChildMax] == 0) { + formState[FormStateWindowState] = (int)FormWindowState.Minimized; + } + break; + case NativeMethods.SW_HIDE: + default: + break; + } + + // If we used to be normal and we just became minimized or maximized, + // stash off our current bounds so we can properly restore. + // + if (oldState == FormWindowState.Normal && WindowState != FormWindowState.Normal) { + + if (WindowState == FormWindowState.Minimized) { + SuspendLayoutForMinimize(); + } + + restoredWindowBounds.Size = ClientSize; + formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 1; + formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 1; + restoredWindowBoundsSpecified = BoundsSpecified.Size; + restoredWindowBounds.Location = Location; + restoredWindowBoundsSpecified |= BoundsSpecified.Location; + + //stash off restoreBounds As well... + restoreBounds.Size = Size; + restoreBounds.Location = Location; + } + + // If we just became normal or maximized resume + if (oldState == FormWindowState.Minimized && WindowState != FormWindowState.Minimized) { + ResumeLayoutFromMinimize(); + } + + switch (WindowState) { + case FormWindowState.Normal: + SetState(STATE_SIZELOCKEDBYOS, false); + break; + case FormWindowState.Maximized: + case FormWindowState.Minimized: + SetState(STATE_SIZELOCKEDBYOS, true); + break; + } + + if (oldState != WindowState) { + AdjustSystemMenu(); + } + } + } + + /// + /// + /// Validates all selectable child controls in the container, including descendants. This is + /// equivalent to calling ValidateChildren(ValidationConstraints.Selectable). See + /// for details of exactly which child controls will be validated. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren() { + return base.ValidateChildren(); + } + + /// + /// + /// Validates all the child controls in the container. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren(ValidationConstraints validationConstraints) { + return base.ValidateChildren(validationConstraints); + } + + /// + /// WM_ACTIVATE handler + /// + /// + private void WmActivate(ref Message m) { + Application.FormActivated(this.Modal, true); // inform MsoComponentManager we're active + Active = NativeMethods.Util.LOWORD(m.WParam) != NativeMethods.WA_INACTIVE; + Application.FormActivated(this.Modal, Active); // inform MsoComponentManager we're active + } + + /// + /// WM_ENTERSIZEMOVE handler, so that user can hook up OnResizeBegin event. + /// + /// + private void WmEnterSizeMove(ref Message m) { + formStateEx[FormStateExInModalSizingLoop] = 1; + OnResizeBegin(EventArgs.Empty); + } + + /// + /// WM_EXITSIZEMOVE handler, so that user can hook up OnResizeEnd event. + /// + /// + private void WmExitSizeMove(ref Message m) { + formStateEx[FormStateExInModalSizingLoop] = 0; + OnResizeEnd(EventArgs.Empty); + } + + /// + /// WM_CREATE handler + /// + /// + private void WmCreate(ref Message m) { + base.WndProc(ref m); + NativeMethods.STARTUPINFO_I si = new NativeMethods.STARTUPINFO_I(); + UnsafeNativeMethods.GetStartupInfo(si); + + // If we've been created from explorer, it may + // force us to show up normal. Force our current window state to + // the specified state, unless it's _specified_ max or min + if (TopLevel && (si.dwFlags & NativeMethods.STARTF_USESHOWWINDOW) != 0) { + switch (si.wShowWindow) { + case NativeMethods.SW_MAXIMIZE: + WindowState = FormWindowState.Maximized; + break; + case NativeMethods.SW_MINIMIZE: + WindowState = FormWindowState.Minimized; + break; + } + } + } + + /// + /// WM_CLOSE, WM_QUERYENDSESSION, and WM_ENDSESSION handler + /// + /// + private void WmClose(ref Message m) { + FormClosingEventArgs e = new FormClosingEventArgs(CloseReason, false); + + // Pass 1 (WM_CLOSE & WM_QUERYENDSESSION)... Closing + // + if (m.Msg != NativeMethods.WM_ENDSESSION) { + if (Modal) { + if (dialogResult == DialogResult.None) { + dialogResult = DialogResult.Cancel; + } + CalledClosing = false; + + // if this comes back false, someone canceled the close. we want + // to call this here so that we can get the cancel event properly, + // and if this is a WM_QUERYENDSESSION, appriopriately set the result + // based on this call. + // + // NOTE: We should also check !Validate(true) below too in the modal case, + // but we cannot, because we didn't to this in Everett (bug), and doing so + // now would introduce a breaking change. User can always validate in the + // FormClosing event if they really need to. :-( + // + e.Cancel = !CheckCloseDialog(true); + } + else { + e.Cancel = !Validate(true); + + // Call OnClosing/OnFormClosing on all MDI children + if (IsMdiContainer) { + FormClosingEventArgs fe = new FormClosingEventArgs(CloseReason.MdiFormClosing, e.Cancel); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.OnClosing(fe); + mdiChild.OnFormClosing(fe); + if (fe.Cancel) { + // VSWhidbey 173789: Set the Cancel property for the MDI Container's + // FormClosingEventArgs, as well, so that closing the MDI container + // will be cancelled. + e.Cancel = true; + break; + } + } + } + } + + //Always fire OnClosing irrespectively of the validation result + //Pass the validation result into the EventArgs... + + // VSWhidbey#278060. Call OnClosing/OnFormClosing on all the forms that current form owns. + Form[] ownedForms = this.OwnedForms; + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + FormClosingEventArgs cfe = new FormClosingEventArgs(CloseReason.FormOwnerClosing, e.Cancel); + if (ownedForms[i] != null) { + //Call OnFormClosing on the child forms. + ownedForms[i].OnFormClosing(cfe); + if (cfe.Cancel) { + // Set the cancel flag for the Owner form + e.Cancel = true; + break; + } + } + } + + OnClosing(e); + OnFormClosing(e); + } + + if (m.Msg == NativeMethods.WM_QUERYENDSESSION) { + m.Result = (IntPtr)(e.Cancel ? 0 : 1); + } + else if (e.Cancel && (MdiParent != null)) { + // This is the case of an MDI child close event being canceled by the user. + CloseReason = CloseReason.None; + } + + if (Modal) { + return; + } + } + else { + e.Cancel = m.WParam == IntPtr.Zero; + } + + // Pass 2 (WM_CLOSE & WM_ENDSESSION)... Fire closed + // event on all mdi children and ourselves + // + if (m.Msg != NativeMethods.WM_QUERYENDSESSION) { + FormClosedEventArgs fc; + if (!e.Cancel) { + IsClosing = true; + + if (IsMdiContainer) { + fc = new FormClosedEventArgs(CloseReason.MdiFormClosing); + foreach(Form mdiChild in MdiChildren) { + if (mdiChild.IsHandleCreated) { + mdiChild.IsTopMdiWindowClosing = IsClosing; + mdiChild.OnClosed(fc); + mdiChild.OnFormClosed(fc); + } + } + } + + // VSWhidbey#278060. Call OnClosed/OnFormClosed on all the forms that current form owns. + Form[] ownedForms = this.OwnedForms; + int ownedFormsCount = Properties.GetInteger(PropOwnedFormsCount); + for (int i = ownedFormsCount-1 ; i >= 0; i--) { + fc = new FormClosedEventArgs(CloseReason.FormOwnerClosing); + if (ownedForms[i] != null) { + //Call OnClosed and OnFormClosed on the child forms. + ownedForms[i].OnClosed(fc); + ownedForms[i].OnFormClosed(fc); + } + } + + fc = new FormClosedEventArgs(CloseReason); + OnClosed(fc); + OnFormClosed(fc); + + Dispose(); + } + } + } + + /// + /// WM_ENTERMENULOOP handler + /// + /// + private void WmEnterMenuLoop(ref Message m) { + OnMenuStart(EventArgs.Empty); + base.WndProc(ref m); + } + + /// + /// Handles the WM_ERASEBKGND message + /// + /// + private void WmEraseBkgnd(ref Message m) { + UpdateWindowState(); + base.WndProc(ref m); + } + + /// + /// WM_EXITMENULOOP handler + /// + /// + private void WmExitMenuLoop(ref Message m) { + OnMenuComplete(EventArgs.Empty); + base.WndProc(ref m); + } + + /// + /// WM_GETMINMAXINFO handler + /// + /// + private void WmGetMinMaxInfo(ref Message m) { + + // VSWhidbey 256556: Form should gracefully stop at the minimum preferred size. + // When we're set to AutoSize true, we should take a look at minAutoSize - which is snapped in onlayout. + // as the form contracts, we should not let it size past here as we're just going to readjust the size + // back to it later. + Size minTrack = (AutoSize && formStateEx[FormStateExInModalSizingLoop] == 1) ? LayoutUtils.UnionSizes(minAutoSize, MinimumSize) : MinimumSize; + + Size maxTrack = MaximumSize; + Rectangle maximizedBounds = MaximizedBounds; + + if (!minTrack.IsEmpty + || !maxTrack.IsEmpty + || !maximizedBounds.IsEmpty + || IsRestrictedWindow) { + + WmGetMinMaxInfoHelper(ref m, minTrack, maxTrack, maximizedBounds); + } + if (IsMdiChild) { + base.WndProc(ref m); + return; + } + } + + // PERFTRACK : Microsoft, 2/22/2000 - Refer to MINMAXINFO in a separate method + // : to avoid loading the class in the common case. + // + private void WmGetMinMaxInfoHelper(ref Message m, Size minTrack, Size maxTrack, Rectangle maximizedBounds) { + + NativeMethods.MINMAXINFO mmi = (NativeMethods.MINMAXINFO)m.GetLParam(typeof(NativeMethods.MINMAXINFO)); + + if (!minTrack.IsEmpty) { + + mmi.ptMinTrackSize.x = minTrack.Width; + mmi.ptMinTrackSize.y = minTrack.Height; + + // VSWhidbey 95302: When the MinTrackSize is set to a value larger than the screen + // size but the MaxTrackSize is not set to a value equal to or greater than the + // MinTrackSize and the user attempts to "grab" a resizing handle, Windows makes + // the window move a distance equal to either the width when attempting to resize + // horizontally or the height of the window when attempting to resize vertically. + // So, the workaround to prevent this problem is to set the MaxTrackSize to something + // whenever the MinTrackSize is set to a value larger than the respective dimension + // of the virtual screen. + + if (maxTrack.IsEmpty) { + + // Only set the max track size dimensions if the min track size dimensions + // are larger than the VirtualScreen dimensions. + Size virtualScreen = SystemInformation.VirtualScreen.Size; + if (minTrack.Height > virtualScreen.Height) { + mmi.ptMaxTrackSize.y = int.MaxValue; + } + if (minTrack.Width > virtualScreen.Width) { + mmi.ptMaxTrackSize.x = int.MaxValue; + } + } + } + if (!maxTrack.IsEmpty) { + // Is the specified MaxTrackSize smaller than the smallest allowable Window size? + Size minTrackWindowSize = SystemInformation.MinWindowTrackSize; + mmi.ptMaxTrackSize.x = Math.Max(maxTrack.Width, minTrackWindowSize.Width); + mmi.ptMaxTrackSize.y = Math.Max(maxTrack.Height, minTrackWindowSize.Height); + } + + if (!maximizedBounds.IsEmpty && !IsRestrictedWindow) { + mmi.ptMaxPosition.x = maximizedBounds.X; + mmi.ptMaxPosition.y = maximizedBounds.Y; + mmi.ptMaxSize.x = maximizedBounds.Width; + mmi.ptMaxSize.y = maximizedBounds.Height; + } + + if (IsRestrictedWindow) { + mmi.ptMinTrackSize.x = Math.Max(mmi.ptMinTrackSize.x, 100); + mmi.ptMinTrackSize.y = Math.Max(mmi.ptMinTrackSize.y, SystemInformation.CaptionButtonSize.Height * 3); + } + + Marshal.StructureToPtr(mmi, m.LParam, false); + m.Result = IntPtr.Zero; + } + + /// + /// WM_INITMENUPOPUP handler + /// + /// + private void WmInitMenuPopup(ref Message m) { + + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu != null) { + + //curMenu.UpdateRtl((RightToLeft == RightToLeft.Yes)); + + if (curMenu.ProcessInitMenuPopup(m.WParam)) + return; + } + base.WndProc(ref m); + } + + /// + /// Handles the WM_MENUCHAR message + /// + /// + private void WmMenuChar(ref Message m) { + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + if (curMenu == null) { + // KB article Q92527 tells us to forward these to our parent... + // + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent != null && formMdiParent.Menu != null) { + UnsafeNativeMethods.PostMessage(new HandleRef(formMdiParent, formMdiParent.Handle), NativeMethods.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_KEYMENU), m.WParam); + m.Result = (IntPtr)NativeMethods.Util.MAKELONG(0, 1); + return; + } + } + if (curMenu != null) { + curMenu.WmMenuChar(ref m); + if (m.Result != IntPtr.Zero) { + // This char is a mnemonic on our menu. + return; + } + } + + base.WndProc(ref m); + } + + /// + /// WM_MDIACTIVATE handler + /// + /// + private void WmMdiActivate(ref Message m) { + base.WndProc(ref m); + Debug.Assert(Properties.GetObject(PropFormMdiParent) != null, "how is formMdiParent null?"); + Debug.Assert(IsHandleCreated, "how is handle 0?"); + + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + + if (formMdiParent != null) { + // VSWhidbey 93530: This message is propagated twice by the MDIClient window. Once to the + // window being deactivated and once to the window being activated. + if (Handle == m.WParam) { + formMdiParent.DeactivateMdiChild(); + } + else if (Handle == m.LParam) { + formMdiParent.ActivateMdiChildInternal(this); + } + } + } + + private void WmNcButtonDown(ref Message m) { + if (IsMdiChild) { + Form formMdiParent = (Form)Properties.GetObject(PropFormMdiParent); + if (formMdiParent.ActiveMdiChildInternal == this) { + if (ActiveControl != null && !ActiveControl.ContainsFocus) { + InnerMostActiveContainerControl.FocusActiveControlInternal(); + } + } + } + base.WndProc(ref m); + } + + /// + /// WM_NCDESTROY handler + /// + /// + private void WmNCDestroy(ref Message m) { + MainMenu mainMenu = Menu; + MainMenu dummyMenu = (MainMenu)Properties.GetObject(PropDummyMenu); + MainMenu curMenu = (MainMenu)Properties.GetObject(PropCurMenu); + MainMenu mergedMenu = (MainMenu)Properties.GetObject(PropMergedMenu); + + if (mainMenu != null) { + mainMenu.ClearHandles(); + } + if (curMenu != null) { + curMenu.ClearHandles(); + } + if (mergedMenu != null) { + mergedMenu.ClearHandles(); + } + if (dummyMenu != null) { + dummyMenu.ClearHandles(); + } + + base.WndProc(ref m); + + // Destroy the owner window, if we created one. We + // cannot do this in OnHandleDestroyed, because at + // that point our handle is not actually destroyed so + // destroying our parent actually causes a recursive + // WM_DESTROY. + if (ownerWindow != null) { + ownerWindow.DestroyHandle(); + ownerWindow = null; + } + + if (Modal && dialogResult == DialogResult.None) { + DialogResult = DialogResult.Cancel; + } + } + + /// + /// WM_NCHITTEST handler + /// + /// + private void WmNCHitTest(ref Message m) { + if (formState[FormStateRenderSizeGrip] != 0 ) { + int x = NativeMethods.Util.LOWORD(m.LParam); + int y = NativeMethods.Util.HIWORD(m.LParam); + + // Convert to client coordinates + // + NativeMethods.POINT pt = new NativeMethods.POINT(x, y); + UnsafeNativeMethods.ScreenToClient(new HandleRef(this, this.Handle), pt ); + + Size clientSize = ClientSize; + + // If the grip is not fully visible the grip area could overlap with the system control box; we need to disable + // the grip area in this case not to get in the way of the control box. We only need to check for the client's + // height since the window width will be at least the size of the control box which is always bigger than the + // grip width. + if( pt.x >= (clientSize.Width - SizeGripSize) && + pt.y >= (clientSize.Height - SizeGripSize) && + clientSize.Height >= SizeGripSize){ + m.Result = IsMirrored ? (IntPtr)NativeMethods.HTBOTTOMLEFT : (IntPtr)NativeMethods.HTBOTTOMRIGHT; + return; + } + } + + base.WndProc(ref m); + + // If we got any of the "edge" hits (bottom, top, topleft, etc), + // and we're AutoSizeMode.GrowAndShrink, return non-resizable border + // The edge values are the 8 values from HTLEFT (10) to HTBOTTOMRIGHT (17). + if (AutoSizeMode == AutoSizeMode.GrowAndShrink) + { + int result = unchecked( (int) (long)m.Result); + if (result >= NativeMethods.HTLEFT && + result <= NativeMethods.HTBOTTOMRIGHT) { + m.Result = (IntPtr)NativeMethods.HTBORDER; + } + } + } + + + /// + /// WM_SHOWWINDOW handler + /// + /// + private void WmShowWindow(ref Message m) { + formState[FormStateSWCalled] = 1; + base.WndProc(ref m); + } + + + /// + /// WM_SYSCOMMAND handler + /// + /// + private void WmSysCommand(ref Message m) { + bool callDefault = true; + + int sc = (NativeMethods.Util.LOWORD(m.WParam) & 0xFFF0); + + switch (sc) { + case NativeMethods.SC_CLOSE: + CloseReason = CloseReason.UserClosing; + if (IsMdiChild && !ControlBox) { + callDefault = false; + } + break; + case NativeMethods.SC_KEYMENU: + if (IsMdiChild && !ControlBox) { + callDefault = false; + } + break; + case NativeMethods.SC_SIZE: + case NativeMethods.SC_MOVE: + // VSWhidbey 514810: set this before WM_ENTERSIZELOOP because WM_GETMINMAXINFO can be called before WM_ENTERSIZELOOP. + formStateEx[FormStateExInModalSizingLoop] = 1; + break; + case NativeMethods.SC_CONTEXTHELP: + CancelEventArgs e = new CancelEventArgs(false); + OnHelpButtonClicked(e); + if (e.Cancel == true) { + callDefault = false; + } + break; + } + + if (Command.DispatchID(NativeMethods.Util.LOWORD(m.WParam))) { + callDefault = false; + } + + if (callDefault) { + + base.WndProc(ref m); + } + } + + /// + /// WM_SIZE handler + /// + /// + private void WmSize(ref Message m) { + + // If this is an MDI parent, don't pass WM_SIZE to the default + // window proc. We handle resizing the MDIClient window ourselves + // (using ControlDock.FILL). + // + if (ctlClient == null) { + base.WndProc(ref m); + if (MdiControlStrip == null && MdiParentInternal != null && MdiParentInternal.ActiveMdiChildInternal == this) { + int wParam = m.WParam.ToInt32(); + MdiParentInternal.UpdateMdiControlStrip(wParam == NativeMethods.SIZE_MAXIMIZED); + } + } + } + + /// + /// WM_UNINITMENUPOPUP handler + /// + /// + private void WmUnInitMenuPopup(ref Message m) { + if (Menu != null) { + //Whidbey addition - also raise the MainMenu.Collapse event for the current menu + Menu.OnCollapse(EventArgs.Empty); + } + } + + /// + /// WM_WINDOWPOSCHANGED handler + /// + /// + private void WmWindowPosChanged(ref Message m) { + + // V#40654 - We must update the windowState, because resize is fired + // from here... (in Control) + UpdateWindowState(); + base.WndProc(ref m); + + RestoreWindowBoundsIfNecessary(); + } + + /* + * THIS CODE LEFT HERE FOR REF. + * There are several issues with the location of minimized MDI child forms that + * we are not fixing. SEE VSW#431080 + /// + /// WM_WINDOWPOSCHANGING handler + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + // Handle minimized window location ourselves - see VSW#431080 for details. + if (IsMdiChild && WindowState == FormWindowState.Minimized) { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS*)m.LParam; + + if ((wp->flags & NativeMethods.SWP_NOMOVE) == 0 && (wp->flags & NativeMethods.SWP_NOSIZE) != 0) { + int menuHeight = SystemInformation.MenuHeight; + if ((MdiParent.MdiClient.ClientSize.Height - wp->y) < menuHeight) { + wp->y -= menuHeight; + } + } + } + + base.WndProc(ref m); + } + */ + + /// + /// + /// Base wndProc encapsulation. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_NCACTIVATE: + if (IsRestrictedWindow) { + BeginInvoke(new MethodInvoker(RestrictedProcessNcActivate)); + } + base.WndProc(ref m); + break; + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + case NativeMethods.WM_NCXBUTTONDOWN: + WmNcButtonDown(ref m); + break; + case NativeMethods.WM_ACTIVATE: + WmActivate(ref m); + break; + case NativeMethods.WM_MDIACTIVATE: + WmMdiActivate(ref m); + break; + case NativeMethods.WM_CLOSE: + if (CloseReason == CloseReason.None) { + CloseReason = CloseReason.TaskManagerClosing; + } + WmClose(ref m); + break; + + case NativeMethods.WM_QUERYENDSESSION: + case NativeMethods.WM_ENDSESSION: + CloseReason = CloseReason.WindowsShutDown; + WmClose(ref m); + break; + case NativeMethods.WM_ENTERSIZEMOVE: + WmEnterSizeMove(ref m); + DefWndProc(ref m); + break; + case NativeMethods.WM_EXITSIZEMOVE: + WmExitSizeMove(ref m); + DefWndProc(ref m); + break; + case NativeMethods.WM_CREATE: + WmCreate(ref m); + break; + case NativeMethods.WM_ERASEBKGND: + WmEraseBkgnd(ref m); + break; + + case NativeMethods.WM_INITMENUPOPUP: + WmInitMenuPopup(ref m); + break; + case NativeMethods.WM_UNINITMENUPOPUP: + WmUnInitMenuPopup(ref m); + break; + case NativeMethods.WM_MENUCHAR: + WmMenuChar(ref m); + break; + case NativeMethods.WM_NCDESTROY: + WmNCDestroy(ref m); + break; + case NativeMethods.WM_NCHITTEST: + WmNCHitTest(ref m); + break; + case NativeMethods.WM_SHOWWINDOW: + WmShowWindow(ref m); + break; + case NativeMethods.WM_SIZE: + WmSize(ref m); + break; + case NativeMethods.WM_SYSCOMMAND: + WmSysCommand(ref m); + break; + case NativeMethods.WM_GETMINMAXINFO: + WmGetMinMaxInfo(ref m); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + WmWindowPosChanged(ref m); + break; + //case NativeMethods.WM_WINDOWPOSCHANGING: + // WmWindowPosChanging(ref m); + // break; + case NativeMethods.WM_ENTERMENULOOP: + WmEnterMenuLoop(ref m); + break; + case NativeMethods.WM_EXITMENULOOP: + WmExitMenuLoop(ref m); + break; + case NativeMethods.WM_CAPTURECHANGED: + base.WndProc(ref m); + // This is a work-around for the Win32 scroll bar; it + // doesn't release it's capture in response to a CAPTURECHANGED + // message, so we force capture away if no button is down. + // + if (CaptureInternal && MouseButtons == (MouseButtons)0) { + CaptureInternal = false; + } + break; + case NativeMethods.WM_GETDPISCALEDSIZE: + { + Debug.Assert(NativeMethods.Util.SignedLOWORD(m.WParam) == NativeMethods.Util.SignedHIWORD(m.WParam), "Non-square pixels!"); + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmGetDpiScaledSize(ref m); + } else { + m.Result = IntPtr.Zero; + } + } + break; + case NativeMethods.WM_DPICHANGED: + if (DpiHelper.EnableDpiChangedMessageHandling) { + WmDpiChanged(ref m); + // If the application processes this message, return zero. + m.Result = IntPtr.Zero; + } else { + m.Result = (IntPtr)1; + } + break; + default: + base.WndProc(ref m); + break; + } + } + +#if SECURITY_DIALOG + private class SecurityMenuItem : ICommandExecutor { + Form owner; + Command cmd; + + internal SecurityMenuItem(Form owner) { + this.owner = owner; + cmd = new Command(this); + } + + internal int ID { + get { + return cmd.ID; + } + } + + [ + ReflectionPermission(SecurityAction.Assert, TypeInformation=true, MemberAccess=true), + UIPermission(SecurityAction.Assert, Window=UIPermissionWindow.AllWindows), + EnvironmentPermission(SecurityAction.Assert, Unrestricted=true), + FileIOPermission(SecurityAction.Assert, Unrestricted=true), + SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode), + ] + void ICommandExecutor.Execute() { + /// SECREVIEW VSWhidbey 332064: this looks to be correct, although not used in retail. + Form information = (Form)Activator.CreateInstance(typeof(Form).Module.Assembly.GetType("System.Windows.Forms.SysInfoForm"), new object[] {owner.IsRestrictedWindow}); + information.ShowDialog(); + } + } +#endif + + /// + /// + /// Represents a collection of controls on the form. + /// + [ComVisible(false)] + public new class ControlCollection : Control.ControlCollection { + + private Form owner; + + /*C#r:protected*/ + + /// + /// + /// Initializes a new instance of the ControlCollection class. + /// + public ControlCollection(Form owner) + : base(owner) { + this.owner = owner; + } + + /// + /// + /// Adds a control + /// to the form. + /// + public override void Add(Control value) { + if (value is MdiClient && owner.ctlClient == null) { + if (!owner.TopLevel && !owner.DesignMode) { + throw new ArgumentException(SR.GetString(SR.MDIContainerMustBeTopLevel), "value"); + } + owner.AutoScroll = false; + if (owner.IsMdiChild) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentAndChild), "value"); + } + owner.ctlClient = (MdiClient)value; + } + + // make sure we don't add a form that has a valid mdi parent + // + if (value is Form && ((Form)value).MdiParentInternal != null) { + throw new ArgumentException(SR.GetString(SR.FormMDIParentCannotAdd), "value"); + } + + base.Add(value); + + if (owner.ctlClient != null) { + owner.ctlClient.SendToBack(); + } + } + + /// + /// + /// + /// Removes a control from the form. + /// + public override void Remove(Control value) { + if (value == owner.ctlClient) { + owner.ctlClient = null; + } + base.Remove(value); + } + } + + // Class used to temporarily reset the owners of windows owned by this Form + // before its handle recreation, then setting them back to the new handle + // after handle recreation + private class EnumThreadWindowsCallback + { + private List ownedWindows; + + internal EnumThreadWindowsCallback() + { + } + + internal bool Callback(IntPtr hWnd, IntPtr lParam) + { + Debug.Assert(lParam != IntPtr.Zero); + HandleRef hRef = new HandleRef(null, hWnd); + IntPtr parent = UnsafeNativeMethods.GetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT); + if (parent == lParam) + { + // Enumerated window is owned by this Form. + // Store it in a list for further treatment. + if (this.ownedWindows == null) + { + this.ownedWindows = new List(); + } + this.ownedWindows.Add(hRef); + } + return true; + } + + // Resets the owner of all the windows owned by this Form before handle recreation. + internal void ResetOwners() + { + if (this.ownedWindows != null) + { + foreach (HandleRef hRef in this.ownedWindows) + { + UnsafeNativeMethods.SetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT, NativeMethods.NullHandleRef); + } + } + } + + // Sets the owner of the windows back to this Form after its handle recreation. + internal void SetOwners(HandleRef hRefOwner) + { + if (this.ownedWindows != null) + { + foreach (HandleRef hRef in this.ownedWindows) + { + UnsafeNativeMethods.SetWindowLong(hRef, NativeMethods.GWL_HWNDPARENT, hRefOwner); + } + } + } + } + + private class SecurityToolTip : IDisposable { + Form owner; + string toolTipText; + bool first = true; + ToolTipNativeWindow window; + + internal SecurityToolTip(Form owner) { + this.owner = owner; + SetupText(); + window = new ToolTipNativeWindow(this); + SetupToolTip(); + owner.LocationChanged += new EventHandler(FormLocationChanged); + owner.HandleCreated += new EventHandler(FormHandleCreated); + } + + CreateParams CreateParams { + get { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + + CreateParams cp = new CreateParams(); + cp.Parent = owner.Handle; + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + cp.Style |= NativeMethods.TTS_ALWAYSTIP | NativeMethods.TTS_BALLOON; + cp.ExStyle = 0; + cp.Caption = null; + return cp; + } + } + + internal bool Modal { + get { + return first; + } + } + + public void Dispose() { + if (owner != null) { + owner.LocationChanged -= new EventHandler(FormLocationChanged); + } + if (window.Handle != IntPtr.Zero) { + window.DestroyHandle(); + window = null; + } + } + + private NativeMethods.TOOLINFO_T GetTOOLINFO() { + NativeMethods.TOOLINFO_T toolInfo; + toolInfo = new NativeMethods.TOOLINFO_T(); + toolInfo.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_T)); + toolInfo.uFlags |= NativeMethods.TTF_SUBCLASS; + toolInfo.lpszText = this.toolTipText; + if (owner.RightToLeft == RightToLeft.Yes) { + toolInfo.uFlags |= NativeMethods.TTF_RTLREADING; + } + if (!first) { + toolInfo.uFlags |= NativeMethods.TTF_TRANSPARENT; + toolInfo.hwnd = owner.Handle; + Size s = SystemInformation.CaptionButtonSize; + Rectangle r = new Rectangle(owner.Left, owner.Top, s.Width, SystemInformation.CaptionHeight); + r = owner.RectangleToClient(r); + r.Width -= r.X; + r.Y += 1; + toolInfo.rect = NativeMethods.RECT.FromXYWH(r.X, r.Y, r.Width, r.Height); + toolInfo.uId = IntPtr.Zero; + } + else { + toolInfo.uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRACK; + toolInfo.hwnd = IntPtr.Zero; + toolInfo.uId = owner.Handle; + } + return toolInfo; + } + + private void SetupText() { + owner.EnsureSecurityInformation(); + string mainText = SR.GetString(SR.SecurityToolTipMainText); + string sourceInfo = SR.GetString(SR.SecurityToolTipSourceInformation, owner.securitySite); + this.toolTipText = SR.GetString(SR.SecurityToolTipTextFormat, mainText, sourceInfo); + } + + private void SetupToolTip() { + window.CreateHandle(CreateParams); + + SafeNativeMethods.SetWindowPos(new HandleRef(window, window.Handle), NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOACTIVATE); + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, owner.Width); + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_SETTITLE, NativeMethods.TTI_WARNING, SR.GetString(SR.SecurityToolTipCaption)); + + if (0 == (int)UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO())) { + Debug.Fail("TTM_ADDTOOL failed for security tip"); + } + + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ACTIVATE, 1, 0); + Show(); + } + + private void RecreateHandle() { + if (window != null) + { + if (window.Handle != IntPtr.Zero) { + window.DestroyHandle(); + } + SetupToolTip(); + } + } + + private void FormHandleCreated(object sender, EventArgs e) { + RecreateHandle(); + } + + private void FormLocationChanged(object sender, EventArgs e) { + if (window != null && first) { + Size s = SystemInformation.CaptionButtonSize; + + if (owner.WindowState == FormWindowState.Minimized) { + Pop(true /*noLongerFirst*/); + } + else { + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(owner.Left + s.Width / 2, owner.Top + SystemInformation.CaptionHeight)); + } + } + else { + Pop(true /*noLongerFirst*/); + } + } + + internal void Pop(bool noLongerFirst) { + if (noLongerFirst) { + first = false; + } + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKACTIVATE, 0, GetTOOLINFO()); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_DELTOOL, 0, GetTOOLINFO()); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO()); + } + + internal void Show() { + if (first) { + Size s = SystemInformation.CaptionButtonSize; + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(owner.Left + s.Width / 2, owner.Top + SystemInformation.CaptionHeight)); + UnsafeNativeMethods.SendMessage(new HandleRef(window, window.Handle), NativeMethods.TTM_TRACKACTIVATE, 1, GetTOOLINFO()); + } + } + + private void WndProc(ref Message msg) { + if (first) { + if (msg.Msg == NativeMethods.WM_LBUTTONDOWN + || msg.Msg == NativeMethods.WM_RBUTTONDOWN + || msg.Msg == NativeMethods.WM_MBUTTONDOWN + || msg.Msg == NativeMethods.WM_XBUTTONDOWN) { + + Pop(true /*noLongerFirst*/); + } + } + window.DefWndProc(ref msg); + } + + private sealed class ToolTipNativeWindow : NativeWindow { + SecurityToolTip control; + + internal ToolTipNativeWindow(SecurityToolTip control) { + this.control = control; + } + + protected override void WndProc(ref Message m) { + if (control != null) { + control.WndProc(ref m); + } + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FormBorderStyle.cs b/WindowsForms/Managed/System/WinForms/FormBorderStyle.cs new file mode 100644 index 000000000..67e067f1c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormBorderStyle.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + using System.Drawing; + + /// + /// + /// + /// Specifies the border styles for a form. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum FormBorderStyle { + + /// + /// + /// + /// No border. + /// + /// + /// + None = 0, + + /// + /// + /// + /// A fixed, single line border. + /// + /// + /// + FixedSingle = 1, + + /// + /// + /// + /// A fixed, three-dimensional border. + /// + /// + /// + Fixed3D = 2, + + /// + /// + /// + /// A thick, fixed dialog-style border. + /// + /// + /// + FixedDialog = 3, + + /// + /// + /// + /// A resizable border. + /// + /// + /// + Sizable = 4, + + /// + /// + /// + /// A tool window border + /// that is not resizable. + /// + /// + FixedToolWindow = 5, + + /// + /// + /// + /// A resizable tool window border. + /// + /// + /// + SizableToolWindow = 6, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/FormClosedEvent.cs b/WindowsForms/Managed/System/WinForms/FormClosedEvent.cs new file mode 100644 index 000000000..a20e0abb3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormClosedEvent.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the , + /// + /// event. + /// + /// + /// + public class FormClosedEventArgs : EventArgs { + private CloseReason closeReason; + + /// + public FormClosedEventArgs(CloseReason closeReason) { + this.closeReason = closeReason; + } + + /// + /// + /// + /// Provides the reason for the Form Close. + /// + /// + public CloseReason CloseReason { + get { + return closeReason; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FormClosedEventHandler.cs b/WindowsForms/Managed/System/WinForms/FormClosedEventHandler.cs new file mode 100644 index 000000000..d1cb9e134 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormClosedEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// Represents a method that will handle Form closed event. + /// + /// + public delegate void FormClosedEventHandler(object sender, FormClosedEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/FormClosingEvent.cs b/WindowsForms/Managed/System/WinForms/FormClosingEvent.cs new file mode 100644 index 000000000..74edbb551 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormClosingEvent.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the , + /// + /// event. + /// + /// + public class FormClosingEventArgs : CancelEventArgs { + private CloseReason closeReason; + + /// + public FormClosingEventArgs(CloseReason closeReason, bool cancel) + : base(cancel) { + this.closeReason = closeReason; + } + + /// + /// + /// + /// Provides the reason for the Form close. + /// + /// + public CloseReason CloseReason { + get { + return closeReason; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FormClosingEventHandler.cs b/WindowsForms/Managed/System/WinForms/FormClosingEventHandler.cs new file mode 100644 index 000000000..bee00c856 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormClosingEventHandler.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// Represents a method that will handle Form Closing event. + /// + /// + public delegate void FormClosingEventHandler(object sender, FormClosingEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/FormCollection.cs b/WindowsForms/Managed/System/WinForms/FormCollection.cs new file mode 100644 index 000000000..4d1b79fb9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormCollection.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Globalization; + + /// + /// + /// + /// This is a read only collection of Forms exposed as a static property of the + /// Application class. This is used to store all the currently loaded forms in an app. + /// + /// + public class FormCollection : ReadOnlyCollectionBase { + + internal static object CollectionSyncRoot = new object(); + + /// + /// + /// + /// Gets a form specified by name, if present, else returns null. If there are multiple + /// forms with matching names, the first form found is returned. + /// + /// + public virtual Form this[string name] { + get { + if (name != null) { + lock (CollectionSyncRoot) { + foreach(Form form in InnerList) { + if (string.Equals(form.Name, name, StringComparison.OrdinalIgnoreCase)) { + return form; + } + } + } + } + return null; + } + } + + /// + /// + /// + /// Gets a form specified by index. + /// + /// + public virtual Form this[int index] { + get { + Form f = null; + + lock (CollectionSyncRoot) { + f = (Form) InnerList[index]; + } + return f; + } + } + + /// + /// Used internally to add a Form to the FormCollection + /// + internal void Add(Form form) { + lock (CollectionSyncRoot) { + InnerList.Add(form); + } + } + + /// + /// Used internally to check if a Form is in the FormCollection + /// + internal bool Contains(Form form) + { + bool inCollection = false; + lock (CollectionSyncRoot) + { + inCollection = InnerList.Contains(form); + } + return inCollection; + } + + /// + /// Used internally to add a Form to the FormCollection + /// + internal void Remove(Form form) { + lock (CollectionSyncRoot) { + InnerList.Remove(form); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/FormStartPosition.cs b/WindowsForms/Managed/System/WinForms/FormStartPosition.cs new file mode 100644 index 000000000..312291a2f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormStartPosition.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using Microsoft.Win32; + using System.Drawing; + + + + /// + /// + /// + /// Specifies the initial position of a + /// form. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum FormStartPosition { + + /// + /// + /// + /// + /// The location and size of the form will determine its + /// starting position. + /// + /// + /// + Manual = 0, + /// + /// + /// + /// The form is centered on the current display, + /// and has the dimensions specified in the form's size. + /// + /// + CenterScreen = 1, + /// + /// + /// + /// The form is positioned at the Windows default + /// location and has the dimensions specified in the form's size. + /// + /// + WindowsDefaultLocation = 2, + /// + /// + /// + /// The form is positioned at the Windows default + /// location and has the bounds determined by Windows default. + /// + /// + WindowsDefaultBounds = 3, + /// + /// + /// + /// The form is centered within the bounds of its parent form. + /// + /// + CenterParent = 4 + } +} diff --git a/WindowsForms/Managed/System/WinForms/FormWindowState.cs b/WindowsForms/Managed/System/WinForms/FormWindowState.cs new file mode 100644 index 000000000..79dc37215 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FormWindowState.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using Microsoft.Win32; + using System.Drawing; + + + + /// + /// + /// + /// Specifies how a form window + /// is displayed. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum FormWindowState { + + /// + /// + /// + /// A default sized window. + /// + /// + /// + Normal = 0, + + /// + /// + /// + /// A minimized window. + /// + /// + /// + Minimized = 1, + + /// + /// + /// + /// A maximized window. + /// + /// + Maximized = 2, + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Formatter.cs b/WindowsForms/Managed/System/WinForms/Formatter.cs new file mode 100644 index 000000000..015026c46 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Formatter.cs @@ -0,0 +1,504 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + using System.Globalization; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + + internal class Formatter + { + static private Type stringType = typeof(String); + static private Type booleanType = typeof(bool); + static private Type checkStateType = typeof(CheckState); + static private Object parseMethodNotFound = new Object(); + static private Object defaultDataSourceNullValue = System.DBNull.Value; + + /// + /// + /// Converts a binary value into a format suitable for display to the end user. + /// Used when pushing a value from a back-end data source into a data-bound property on a control. + /// + /// The real conversion work happens inside FormatObjectInternal(). Before calling FormatObjectInternal(), + /// we check for any use of nullable types or values (eg. Nullable) and 'unwrap' them to get at the real + /// types and values, which are then used in the actual conversion. If the caller is expecting a nullable + /// value back, we must also re-wrap the final result inside a nullable value before returning. + /// + /// + public static object FormatObject(object value, + Type targetType, + TypeConverter sourceConverter, + TypeConverter targetConverter, + string formatString, + IFormatProvider formatInfo, + object formattedNullValue, + object dataSourceNullValue) { + // + // On the way in, see if value represents 'null' for this back-end field type, and substitute DBNull. + // For most types, 'null' is actually represented by DBNull. But for a nullable type, its represented + // by an instance of that type with no value. And for business objects it may be represented by a + // simple null reference. + // + + if (Formatter.IsNullData(value, dataSourceNullValue)) { + value = System.DBNull.Value; + } + + // + // Strip away any use of nullable types (eg. Nullable), leaving just the 'real' types + // + + Type oldTargetType = targetType; + + targetType = NullableUnwrap(targetType); + sourceConverter = NullableUnwrap(sourceConverter); + targetConverter = NullableUnwrap(targetConverter); + + bool isNullableTargetType = (targetType != oldTargetType); + + // + // Call the 'real' method to perform the conversion + // + + object result = FormatObjectInternal(value, targetType, sourceConverter, targetConverter, formatString, formatInfo, formattedNullValue); + + if (oldTargetType.IsValueType && result == null && !isNullableTargetType) + { + throw new FormatException(GetCantConvertMessage(value, targetType)); + } + return result; + } + + /// + /// + /// Converts a value into a format suitable for display to the end user. + /// + /// - Converts DBNull or null into a suitable formatted representation of 'null' + /// - Performs some special-case conversions (eg. Boolean to CheckState) + /// - Uses TypeConverters or IConvertible where appropriate + /// - Throws a FormatException is no suitable conversion can be found + /// + /// + private static object FormatObjectInternal(object value, + Type targetType, + TypeConverter sourceConverter, + TypeConverter targetConverter, + string formatString, + IFormatProvider formatInfo, + object formattedNullValue) { + if (value == System.DBNull.Value || value == null) { + // + // Convert DBNull to the formatted representation of 'null' (if possible) + // + if (formattedNullValue != null) + { + return formattedNullValue; + } + + // + // Convert DBNull or null to a specific 'known' representation of null (otherwise fail) + // + if (targetType == stringType) + { + return String.Empty; + } + + if (targetType == checkStateType) { + return CheckState.Indeterminate; + } + + // Just pass null through: if this is a value type, it's been unwrapped here, so we return null + // and the caller has to wrap if appropriate. + return null; + } + + // + // Special case conversions + // + + if (targetType == stringType) { + if (value is IFormattable && !String.IsNullOrEmpty(formatString)) { + return (value as IFormattable).ToString(formatString, formatInfo); + } + } + + //The converters for properties should take precedence. Unfortunately, we don't know whether we have one. Check vs. the + //type's TypeConverter. We're punting the case where the property-provided converter is the same as the type's converter. + Type sourceType = value.GetType(); + TypeConverter sourceTypeTypeConverter = TypeDescriptor.GetConverter(sourceType); + if (sourceConverter != null && sourceConverter != sourceTypeTypeConverter && sourceConverter.CanConvertTo(targetType)) { + return sourceConverter.ConvertTo(null, GetFormatterCulture(formatInfo), value, targetType); + } + + TypeConverter targetTypeTypeConverter = TypeDescriptor.GetConverter(targetType); + if (targetConverter != null && targetConverter != targetTypeTypeConverter && targetConverter.CanConvertFrom(sourceType)) { + return targetConverter.ConvertFrom(null, GetFormatterCulture(formatInfo), value); + } + + if (targetType == checkStateType) { + if (sourceType == booleanType) { + return ((bool)value) ? CheckState.Checked : CheckState.Unchecked; + } + else { + if (sourceConverter == null) { + sourceConverter = sourceTypeTypeConverter; + } + if (sourceConverter != null && sourceConverter.CanConvertTo(booleanType)) { + return (bool)sourceConverter.ConvertTo(null, GetFormatterCulture(formatInfo), value, booleanType) + ? CheckState.Checked : CheckState.Unchecked; + } + } + } + + if (targetType.IsAssignableFrom(sourceType)) { + return value; + } + + // + // If explicit type converters not provided, supply default ones instead + // + + if (sourceConverter == null) { + sourceConverter = sourceTypeTypeConverter; + } + + if (targetConverter == null) { + targetConverter = targetTypeTypeConverter; + } + + // + // Standardized conversions + // + + if (sourceConverter != null && sourceConverter.CanConvertTo(targetType)) { + return sourceConverter.ConvertTo(null, GetFormatterCulture(formatInfo), value, targetType); + } + else if (targetConverter != null && targetConverter.CanConvertFrom(sourceType)) { + return targetConverter.ConvertFrom(null, GetFormatterCulture(formatInfo), value); + } + else if (value is IConvertible) { + return ChangeType(value, targetType, formatInfo); + } + + // + // Fail if no suitable conversion found + // + + throw new FormatException(GetCantConvertMessage(value, targetType)); + } + + /// + /// + /// Converts a value entered by the end user (through UI) into the corresponding binary value. + /// Used when pulling input from a data-bound property on a control to store in a back-end data source. + /// + /// The real conversion work happens inside ParseObjectInternal(). Before calling ParseObjectInternal(), + /// we check for any use of nullable types or values (eg. Nullable) and 'unwrap' them to get at the real + /// types and values, which are then used in the actual conversion. If the caller is expecting a nullable + /// value back, we must also re-wrap the final result inside a nullable value before returning. + /// + /// + public static object ParseObject(object value, + Type targetType, + Type sourceType, + TypeConverter targetConverter, + TypeConverter sourceConverter, + IFormatProvider formatInfo, + object formattedNullValue, + object dataSourceNullValue) { + // + // Strip away any use of nullable types (eg. Nullable), leaving just the 'real' types + // + + Type oldTargetType = targetType; + + sourceType = NullableUnwrap(sourceType); + targetType = NullableUnwrap(targetType); + sourceConverter = NullableUnwrap(sourceConverter); + targetConverter = NullableUnwrap(targetConverter); + + bool isNullableTargetType = (targetType != oldTargetType); + + // + // Call the 'real' method to perform the conversion + // + + object result = ParseObjectInternal(value, targetType, sourceType, targetConverter, sourceConverter, formatInfo, formattedNullValue); + + // + // On the way out, substitute DBNull with the appropriate representation of 'null' for the final target type. + // For most types, this is just DBNull. But for a nullable type, its an instance of that type with no value. + // + + if (result == System.DBNull.Value) { + return Formatter.NullData(oldTargetType, dataSourceNullValue); + } + + return result; + } + + /// + /// + /// Converts a value entered by the end user (through UI) into the corresponding binary value. + /// + /// - Converts formatted representations of 'null' into DBNull + /// - Performs some special-case conversions (eg. CheckState to Boolean) + /// - Uses TypeConverters or IConvertible where appropriate + /// - Throws a FormatException is no suitable conversion can be found + /// + /// + private static object ParseObjectInternal(object value, + Type targetType, + Type sourceType, + TypeConverter targetConverter, + TypeConverter sourceConverter, + IFormatProvider formatInfo, + object formattedNullValue) { + // + // Convert the formatted representation of 'null' to DBNull (if possible) + // + + if (EqualsFormattedNullValue(value, formattedNullValue, formatInfo) || value == System.DBNull.Value) { + return System.DBNull.Value; + } + + // + // Special case conversions + // + + TypeConverter targetTypeTypeConverter = TypeDescriptor.GetConverter(targetType); + if (targetConverter != null && targetTypeTypeConverter != targetConverter && targetConverter.CanConvertFrom(sourceType)) { + return targetConverter.ConvertFrom(null, GetFormatterCulture(formatInfo), value); + } + + TypeConverter sourceTypeTypeConverter = TypeDescriptor.GetConverter(sourceType); + if (sourceConverter != null && sourceTypeTypeConverter != sourceConverter && sourceConverter.CanConvertTo(targetType)) { + return sourceConverter.ConvertTo(null, GetFormatterCulture(formatInfo), value, targetType); + } + + if (value is string) { + // If target type has a suitable Parse method, use that to parse strings + object parseResult = InvokeStringParseMethod(value, targetType, formatInfo); + if (parseResult != parseMethodNotFound) { + return parseResult; + } + } + else if (value is CheckState) { + CheckState state = (CheckState)value; + if (state == CheckState.Indeterminate) { + return DBNull.Value; + } + // Explicit conversion from CheckState to Boolean + if (targetType == booleanType) { + return (state == CheckState.Checked); + } + if (targetConverter == null) { + targetConverter = targetTypeTypeConverter; + } + if (targetConverter != null && targetConverter.CanConvertFrom(booleanType)) { + return targetConverter.ConvertFrom(null, GetFormatterCulture(formatInfo), state == CheckState.Checked); + } + } + else if (value != null && targetType.IsAssignableFrom(value.GetType())) { + // If value is already of a compatible type, just go ahead and use it + return value; + } + + // + // If explicit type converters not provided, supply default ones instead + // + + if (targetConverter == null) { + targetConverter = targetTypeTypeConverter; + } + + if (sourceConverter == null) { + sourceConverter = sourceTypeTypeConverter; + } + + // + // Standardized conversions + // + + if (targetConverter != null && targetConverter.CanConvertFrom(sourceType)) { + return targetConverter.ConvertFrom(null, GetFormatterCulture(formatInfo), value); + } + else if (sourceConverter != null && sourceConverter.CanConvertTo(targetType)) { + return sourceConverter.ConvertTo(null, GetFormatterCulture(formatInfo), value, targetType); + } + else if (value is IConvertible) { + return ChangeType(value, targetType, formatInfo); + } + + // + // Fail if no suitable conversion found + // + + throw new FormatException(GetCantConvertMessage(value, targetType)); + } + + /// + /// Converts a value to the specified type using Convert.ChangeType() + /// + private static object ChangeType(object value, Type type, IFormatProvider formatInfo) { + try { + if (formatInfo == null) { + formatInfo = CultureInfo.CurrentCulture; + } + + return Convert.ChangeType(value, type, formatInfo); + } + catch (InvalidCastException ex) { + throw new FormatException(ex.Message, ex); + } + } + + /// + /// Indicates whether the specified value matches the display-formatted representation of 'null data' for a given binding. + /// + private static bool EqualsFormattedNullValue(object value, object formattedNullValue, IFormatProvider formatInfo) { + string formattedNullValueStr = formattedNullValue as string; + string valueStr = value as string; + if (formattedNullValueStr != null && valueStr != null) { + // Use same optimization as in WindowsFormsUtils.SafeCompareStrings(...). This addresses bug DevDiv Bugs 110336. + if (formattedNullValueStr.Length != valueStr.Length) { + return false; + } + // Always do a case insensitive comparison for strings + return String.Compare(valueStr, formattedNullValueStr, true, GetFormatterCulture(formatInfo)) == 0; + } + else { + // Otherwise perform default comparison based on object types + return Object.Equals(value, formattedNullValue); + } + } + + /// + /// Returns the FormatException message used when formatting/parsing fails to find any suitable conversion + /// + private static string GetCantConvertMessage(object value, Type targetType) { + string stringResId = (value == null) ? SR.Formatter_CantConvertNull : SR.Formatter_CantConvert; + return String.Format(CultureInfo.CurrentCulture, SR.GetString(stringResId), value, targetType.Name); + } + + /// + /// Determines the correct culture to use during formatting and parsing + /// + private static CultureInfo GetFormatterCulture(IFormatProvider formatInfo) { + if (formatInfo is CultureInfo) { + return formatInfo as CultureInfo; + } + else { + return CultureInfo.CurrentCulture; + } + } + + /// + /// Converts a value to the specified type using best Parse() method on that type + /// + public static object InvokeStringParseMethod(object value, Type targetType, IFormatProvider formatInfo) { + try { + MethodInfo mi; + + mi = targetType.GetMethod("Parse", + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] {stringType, typeof(System.Globalization.NumberStyles), typeof(System.IFormatProvider)}, + null); + if (mi != null) { + return mi.Invoke(null, new object [] {(string) value, NumberStyles.Any, formatInfo}); + } + + mi = targetType.GetMethod("Parse", + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] {stringType, typeof(System.IFormatProvider)}, + null); + if (mi != null) { + return mi.Invoke(null, new object [] {(string) value, formatInfo}); + } + + mi = targetType.GetMethod("Parse", + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] {stringType}, + null); + if (mi != null) { + return mi.Invoke(null, new object [] {(string) value}); + } + + return parseMethodNotFound; + } + catch (TargetInvocationException ex) { + throw new FormatException(ex.InnerException.Message, ex.InnerException); + } + } + + /// + /// Indicates whether a given value represents 'null' for data source fields of the same type. + /// + public static bool IsNullData(object value, object dataSourceNullValue) { + return value == null || + value == System.DBNull.Value || + Object.Equals(value, NullData(value.GetType(), dataSourceNullValue)); + } + + /// + /// Returns the default representation of 'null' for a given data source field type. + /// + public static object NullData(Type type, object dataSourceNullValue) { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { + // For nullable types, null is represented by an instance of that type with no assigned value. + // The value could also be DBNull.Value (the default for dataSourceNullValue). + if (dataSourceNullValue == null || dataSourceNullValue == DBNull.Value) + { + // We don't have a special value that represents null on the data source: + // use the Nullable's representation + return null; + } + else + { + return dataSourceNullValue; + } + } + else { + // For all other types, the default representation of null is defined by + // the caller (this will usually be System.DBNull.Value for ADO.NET data + // sources, or a null reference for 'business object' data sources). + return dataSourceNullValue; + } + } + + /// + /// Extract the inner type from a nullable type + /// + private static Type NullableUnwrap(Type type) { + if (type == stringType) // ...performance optimization for the most common case + return stringType; + + + Type underlyingType = Nullable.GetUnderlyingType(type); + return underlyingType ?? type; + } + + /// + /// Extract the inner type converter from a nullable type converter + /// + private static TypeConverter NullableUnwrap(TypeConverter typeConverter) { + NullableConverter nullableConverter = typeConverter as NullableConverter; + return (nullableConverter != null) ? nullableConverter.UnderlyingTypeConverter : typeConverter; + } + + public static object GetDefaultDataSourceNullValue(Type type) { + return (type != null && !type.IsValueType) ? null : defaultDataSourceNullValue; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/FrameStyle.cs b/WindowsForms/Managed/System/WinForms/FrameStyle.cs new file mode 100644 index 000000000..91720a431 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FrameStyle.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + + /// + /// + /// + /// Specifies the frame style of the selected control. + /// + /// + public enum FrameStyle { + + /// + /// + /// + /// A thin, dashed border. + /// + /// + Dashed, + + /// + /// + /// + /// A thick, solid border. + /// + /// + Thick, + } +} diff --git a/WindowsForms/Managed/System/WinForms/FxCopSuppression.cs b/WindowsForms/Managed/System/WinForms/FxCopSuppression.cs new file mode 100644 index 000000000..02de2b8ef --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/FxCopSuppression.cs @@ -0,0 +1,1648 @@ +using System.Diagnostics.CodeAnalysis; +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.BindingCompleteEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.BindingManagerDataErrorEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.CacheVirtualItemsEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ColumnClickEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ColumnReorderedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ColumnWidthChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ColumnWidthChangingEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.Com2EventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.GetAttributesEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.GetBoolValueEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.GetNameItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.GetRefreshStateEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ComponentModel.Com2Interop.GetTypeConverterAndTypeEditorEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ContentsResizedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.Control+IPropertyNotifySinkEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ControlEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ConvertEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DateBoldEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DateRangeEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DragEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawListViewColumnHeaderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawListViewItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawListViewSubItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawToolTipEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.DrawTreeNodeEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.FormClosedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.FormClosingEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.GiveFeedbackEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.HelpEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.HtmlElementErrorEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.HtmlElementEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.InputLanguageChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.InputLanguageChangingEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.InvalidateEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ItemChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ItemCheckedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ItemCheckEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ItemDragEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.KeyEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.KeyPressEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.LabelEditEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.LayoutEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.LinkClickedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.LinkLabelLinkClickedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ListControlConvertEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ListViewItemMouseHoverEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ListViewItemSelectionChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ListViewVirtualItemsSelectionRangeChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.MaskInputRejectedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.MeasureItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.MouseEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.NavigateEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.NodeLabelEditEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PaintEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PopupEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PreviewKeyDownEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PropertyGridInternal.GridEntryRecreateChildrenEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PropertyTabChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.PropertyValueChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.QueryAccessibilityHelpEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.QueryContinueDragEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.QuestionEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.RaftingContainerRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.RetrieveVirtualItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ScrollEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.SearchForVirtualItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.SelectedGridItemChangedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.SplitterCancelEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.SplitterEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.StatusBarDrawItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.StatusBarPanelClickEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TabControlCancelEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TabControlEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TableLayoutCellPaintEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolBarButtonClickEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripArrowRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripContentPanelRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripDropDownClosedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripDropDownClosingEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripGripRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripItemClickedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripItemEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripItemImageRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripItemRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripItemTextRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripPanelRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.ToolStripSeparatorRenderEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TreeNodeMouseClickEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TreeNodeMouseHoverEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TreeViewCancelEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TreeViewEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.TypeValidationEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.UICuesEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.UpDownEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.WebBrowserDocumentCompletedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.WebBrowserNavigatedEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.WebBrowserNavigatingEventHandler")] +[assembly: SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances", Scope="type", Target="System.Windows.Forms.WebBrowserProgressChangedEventHandler")] + + +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="checkbox")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="toolbars")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="scrollbars")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="scrollbar")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="AutoSuggest")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="shorcut")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Sortable")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Sel")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="multiline")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Multiline")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="resheader")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="objs")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Renderer")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Lresult")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="System.Windows.Forms.resources", MessageId="Dn")] + +// SECREVIEW : See comments on the methods for the following message suppression attributes. +// +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Application.EnableVisualStyles():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Application.GetAppFileVersionInfo():System.Diagnostics.FileVersionInfo")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.get_ParkingWindow():System.Windows.Forms.Application+ParkingWindow")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.OnThreadException(System.Exception):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ContainerControl.AfterControlRemoved(System.Windows.Forms.Control):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control+ControlVersionInfo.GetFileVersionInfo():System.Diagnostics.FileVersionInfo")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control+ControlAccessibleObject..ctor(System.Windows.Forms.Control)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control+ControlAccessibleObject.get_PreviousLabel():System.Windows.Forms.Label")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Label.ProcessMnemonic(System.Char):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.FontDialog.RunDialog(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.FontDialog.UpdateFont(System.Windows.Forms.NativeMethods+LOGFONT):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ImageList+NativeImageList..ctor(System.IntPtr)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGridView+DataGridViewToolTip.ComputeToolTipPosition():System.Drawing.Point")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.InputLanguage.get_LayoutName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.TabControl.WmSelChanging():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.TabControl.UpdateTabSelection(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.GroupBox.ProcessMnemonic(System.Char):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.OnVisibleChanged(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.get_DefaultIcon():System.Drawing.Icon")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window):System.Windows.Forms.DialogResult")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.SetVisibleCore(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.get_DefaultRestrictedIcon():System.Drawing.Icon")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Form.set_Active(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Help.Resolve(System.String):System.Uri")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Splitter.SplitBegin(System.Int32,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ImageList.GetBitmap(System.Int32):System.Drawing.Bitmap")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Cursor.get_HotSpot():System.Drawing.Point")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Cursor.LoadPicture(System.Windows.Forms.UnsafeNativeMethods+IStream):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Cursor.get_CurrentInternal():System.Windows.Forms.Cursor")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.LinkUtilities.GetIEColor(System.String):System.Drawing.Color")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.LinkUtilities.GetIELinkBehavior():System.Windows.Forms.LinkBehavior")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ListView.SetBackgroundImage():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ListView.Dispose(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.ListView.DeleteFileName(System.String):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGrid.CreateScrollableRegion(System.Drawing.Rectangle):System.Windows.Forms.NativeMethods+RECT[]")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.InstallWindowsFormsSyncContextIfNeeded():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.SystemInformation.get_MenuFont():System.Drawing.Font")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.SplitContainer.SplitBegin(System.Int32,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control.OnHandleCreated(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control.UpdateReflectParent(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope = "member", Target = "System.Windows.Forms.Control.SelectNextIfFocused():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope = "member", Target = "System.Windows.Forms.Control.SelectNextControlInternal(System.Windows.Forms.Control,System.Boolean,System.Boolean,System.Boolean,System.Boolean):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGridView.CreateScrollableRegion(System.Drawing.Rectangle):System.Windows.Forms.NativeMethods+RECT[]")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.ComponentModel.WeakDelegateHolder.DynamicInvoke(System.Object[]):System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetFont(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.FontProperty):System.Drawing.Font")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PtrToStructure(System.IntPtr,System.Type):System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGrid.RowResizeBegin(System.Windows.Forms.MouseEventArgs,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGrid.ColResizeBegin(System.Windows.Forms.MouseEventArgs,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGridView.CaptureMouse(System.Drawing.Rectangle):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.Control+ActiveXImpl.OnFocus(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataObject.SaveObjectToHandleSerializer(System.IO.Stream,System.Object):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.DataGridView+DataGridViewToolTip.Activate(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.FileDialog.RunDialogVista(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Scope="member", Target="System.Windows.Forms.FileDialogCustomPlacesCollection.Apply(System.Windows.Forms.FileDialogNative+IFileDialog):System.Void")] + +// SECREVIEW : Most of the following message suppression have been added to safe methods that are used for getting data from the system. In most cases we create the output structure +// and/or the input string for the methods that require them. There are a few exceptions where text is passed from user code to the system but that are considered safe, +// like SendMessage and SetWindowText. +// +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.Internal.IntSafeNativeMethods.ExtTextOut(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32,System.Windows.Forms.Internal.IntNativeMethods+RECT&,System.String,System.Int32,System.Int32[]):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.Internal.IntSafeNativeMethods.FormatMessage(System.Int32,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Text.StringBuilder,System.Int32,System.Runtime.InteropServices.HandleRef):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.Internal.IntSafeNativeMethods.GetObject(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.Internal.IntNativeMethods+LOGFONT):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.Internal.IntUnsafeNativeMethods.IntCreateDC(System.String,System.String,System.String,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.Internal.IntUnsafeNativeMethods.IntCreateIC(System.String,System.String,System.String,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.NativeMethods+Util.lstrlen(System.String):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.NativeMethods+Util.RegisterWindowMessage(System.String):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.ChooseColor(System.Windows.Forms.NativeMethods+CHOOSECOLOR):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.ChooseFont(System.Windows.Forms.NativeMethods+CHOOSEFONT):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.EnumDisplaySettings(System.String,System.Int32,System.Windows.Forms.NativeMethods+DEVMODE&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.FormatMessage(System.Int32,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Text.StringBuilder,System.Int32,System.Runtime.InteropServices.HandleRef):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetClipboardFormatName(System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetCurrentThemeName(System.Text.StringBuilder,System.Int32,System.Text.StringBuilder,System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetThemeDocumentationProperty(System.String,System.String,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetThemeFilename(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetThemeFont(System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LOGFONT):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.GetThemeString(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.HtmlHelp(System.Runtime.InteropServices.HandleRef,System.String,System.Int32,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.HtmlHelp(System.Runtime.InteropServices.HandleRef,System.String,System.Int32,System.String):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.HtmlHelp(System.Runtime.InteropServices.HandleRef,System.String,System.Int32,System.Windows.Forms.NativeMethods+HH_AKLINK):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.HtmlHelp(System.Runtime.InteropServices.HandleRef,System.String,System.Int32,System.Windows.Forms.NativeMethods+HH_FTS_QUERY):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.HtmlHelp(System.Runtime.InteropServices.HandleRef,System.String,System.Int32,System.Windows.Forms.NativeMethods+HH_POPUP):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef,System.String,System.String,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreateFontIndirect(System.Windows.Forms.NativeMethods+tagFONTDESC,System.Guid&):System.Windows.Forms.SafeNativeMethods+IFont")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.OleCreateIFontDispIndirect(System.Windows.Forms.NativeMethods+FONTDESC,System.Guid&):System.Windows.Forms.SafeNativeMethods+IFontDisp")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreateIFontIndirect(System.Windows.Forms.NativeMethods+FONTDESC,System.Guid&):System.Windows.Forms.SafeNativeMethods+IFont")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.RegisterClipboardFormat(System.String):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.SafeNativeMethods.RegisterWindowMessage(System.String):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemoryA(System.IntPtr,System.String,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.DragQueryFile(System.Runtime.InteropServices.HandleRef,System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.FindWindow(System.String,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetClassInfo(System.Runtime.InteropServices.HandleRef,System.String,System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetClassInfo(System.Runtime.InteropServices.HandleRef,System.String,System.Windows.Forms.NativeMethods+WNDCLASS_I):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetClassName(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetComputerName(System.Text.StringBuilder,System.Int32[]):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetLocaleInfo(System.Int32,System.Int32,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetMenuItemInfo(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Windows.Forms.NativeMethods+MENUITEMINFO_T):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetModuleFileName(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetModuleHandle(System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetObject(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+LOGFONT):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetOpenFileName(System.Windows.Forms.NativeMethods+OPENFILENAME_I):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetProcAddress(System.Runtime.InteropServices.HandleRef,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetSaveFileName(System.Windows.Forms.NativeMethods+OPENFILENAME_I):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetShortPathName(System.String,System.String,System.UInt32):System.UInt32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetTempFileName(System.String,System.String,System.Int32,System.Text.StringBuilder):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetUserName(System.Text.StringBuilder,System.Int32[]):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetWindowText(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalAddAtom(System.String):System.Int16")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.InsertMenuItem(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Windows.Forms.NativeMethods+MENUITEMINFO_T):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCreateDC(System.String,System.String,System.String,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCreateIC(System.String,System.String,System.String,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCreateWindowEx(System.Int32,System.String,System.String,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Object):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.LoadLibrary(System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.LookupAccountName(System.String,System.String,System.Byte[],System.Int32&,System.Text.StringBuilder,System.Int32&,System.Int32&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreatePropertyFrameIndirect(System.Windows.Forms.NativeMethods+OCPFIPARAMS):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PageSetupDlg(System.Windows.Forms.NativeMethods+PAGESETUPDLG):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PrintDlg(System.Windows.Forms.NativeMethods+PRINTDLG):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PrintDlgEx(System.Windows.Forms.NativeMethods+PRINTDLGEX):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.RegisterClass(System.Windows.Forms.NativeMethods+WNDCLASS_D):System.Int16")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Text.StringBuilder):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+FINDTEXT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LOGFONT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVBKIMAGE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVCOLUMN_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TCITEM_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TOOLINFO_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.IntPtr,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SetMenuItemInfo(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Windows.Forms.NativeMethods+MENUITEMINFO_T):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SetWindowText(System.Runtime.InteropServices.HandleRef,System.String):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.Shell_NotifyIcon(System.Int32,System.Windows.Forms.NativeMethods+NOTIFYICONDATA):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.ShellExecute(System.Runtime.InteropServices.HandleRef,System.String,System.String,System.String,System.String,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SystemParametersInfo(System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LOGFONT,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SystemParametersInfo(System.Int32,System.Int32,System.Windows.Forms.NativeMethods+NONCLIENTMETRICS,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.UnregisterClass(System.String,System.Runtime.InteropServices.HandleRef):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+Shell32.SHBrowseForFolder(System.Windows.Forms.UnsafeNativeMethods+BROWSEINFO):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreateFontIndirect(System.Windows.Forms.NativeMethods+tagFONTDESC,System.Guid&):System.Windows.Forms.UnsafeNativeMethods+IFont")] +[assembly: SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreateIFontIndirect(System.Windows.Forms.NativeMethods+FONTDESC,System.Guid&):System.Windows.Forms.UnsafeNativeMethods+IFont")] + +// SECREVIEW : Late-binding does not represent a security threat. +// +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.ColumnHeader.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewComboBoxColumn.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewColumn.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewHeaderCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewImageCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewCheckBoxCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewButtonColumn.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewLinkCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewColumnHeaderCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.TreeNode.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewImageColumn.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewRow.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.ListViewItem.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewTextBoxCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewRowHeaderCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewBand.get_HeaderCellCore():System.Windows.Forms.DataGridViewHeaderCell")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewBand.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewBand.set_DefaultHeaderCellType(System.Type):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewLinkColumn.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.MenuItem.MergeMenu():System.Windows.Forms.MenuItem")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewComboBoxCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.DataGridViewButtonCell.Clone():System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.FeatureSupport.IsPresent(System.String,System.String,System.Version):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.Windows.Forms.FeatureSupport.GetVersionPresent(System.String,System.String):System.Version")] + +// Following message suppressions are for MaskedTextBox unsupported base methods/properties. +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.add_AcceptsTabChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.add_AcceptsTabChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.add_MultilineChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.add_MultilineChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.GetFirstCharIndexFromLine(System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.GetFirstCharIndexFromLine(System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.remove_AcceptsTabChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.remove_AcceptsTabChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.remove_MultilineChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.remove_MultilineChanged(System.EventHandler):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_AcceptsTab(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_AcceptsTab(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_Lines(System.String[]):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_Lines(System.String[]):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_Padding(System.Windows.Forms.Padding):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_Padding(System.Windows.Forms.Padding):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_WordWrap(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters", Scope="member", Target="System.Windows.Forms.MaskedTextBox.set_WordWrap(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope="member", Target="System.Windows.Forms.MaskedTextBox.Lines")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope="member", Target="System.Windows.Forms.MaskedTextBox.Lines")] +[assembly: SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope="member", Target="System.Windows.Forms.MaskedTextBox.GetFirstCharIndexOfCurrentLine():System.Int32")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope="member", Target="System.Windows.Forms.MaskedTextBox.MultilineChanged")] +[assembly: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope="member", Target="System.Windows.Forms.MaskedTextBox.PerformTypeValidation(System.ComponentModel.CancelEventArgs):System.Object")] +[assembly: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope="member", Target="System.Windows.Forms.MaskedTextBox.WmCopy():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope="member", Target="System.Windows.Forms.MaskedTextBox.WmPaste():System.Void")] + +//SafeHandle Suppressions. +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Design.ComponentEditorForm+PageSelector.hbrushDither")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer+ThemeHandle._hTheme")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolStripMenuItem.nativeMenuHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolStripMenuItem.targetWindowHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.Com2FontConverter.lastHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.BaseCAMarshaler.caArrayAddress")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.Com2PictureConverter.lastNativeHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.PropertyGridInternal.PropertyGridView.baseHfont")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.PropertyGridInternal.PropertyGridView.boldHfont")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.PropertyGridInternal.PropertyGridView+MouseHook.mouseHookHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Internal.WindowsBrush.nativeHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Internal.WindowsFont.hFont")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Internal.WindowsPen.nativeHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Internal.WindowsBitmap.nativeHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.WindowsFormsUtils+ScreenGraphics.hDC")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MEASUREITEMSTRUCT.itemData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolBarButton.stringIndex")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target=",System.Windows.Forms.NativeMethods+LOGBRUSH.lbHatch")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.AxHost.hwndFocus")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.AxHost.wndprocAddr")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.RichTextBox.moduleHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+EDITSTREAM.dwCookie")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+CLIENTCREATESTRUCT.hWindowMenu")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+COPYDATASTRUCT.lpData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVITEM.puColumns")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVITEM.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ImageList+NativeImageList.himl")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WINDOWPOS.hwndInsertAfter")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WINDOWPOS.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PAINTSTRUCT.hdc")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TEXTRANGE.lpstrText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+tagQACONTAINER.hpal")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+VARIANT.data2")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+VARIANT.data1")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+OCPFIPARAMS.hwndOwner")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+OCPFIPARAMS.ppUnk")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+OCPFIPARAMS.uuid")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+ActiveXImpl.accelTable")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+ActiveXImpl.clipRegion")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+ActiveXImpl.hwndParent")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVITEM_NOTEXT.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVITEM_NOTEXT.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.CreateParams.parent")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.InputLanguage.handle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagFUNCDESC.lprgelemdescParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM.hbm")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM.pvFilter")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMHEADER.pItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PICTDESCicon.hicon")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ENDROPFILES.hDrop")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T.hbmpChecked")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T.dwItemData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T.hSubMenu")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T.hbmpUnchecked")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PICTDESC.union1")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.hbmpChecked")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.hbmpItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.dwItemData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.dwTypeData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.hSubMenu")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW.hbmpUnchecked")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_ITEM.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_ITEM.hItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_ITEM.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagPARAMDESC.pparamdescex")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Message.result")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Message.wparam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Message.hWnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Message.lparam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLISTVIEW.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolStrip.hwndThatLostFocus")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TCITEM_T.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_T.uId")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_T.hinst")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_T.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_T.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMTOOLBAR.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+EVENTMSG.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVDISPINFO_UNSAFE.idFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVDISPINFO_UNSAFE.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVDISPINFO_UNSAFE.hwndFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVDISPINFO_UNSAFE.puColumns")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVDISPINFO_UNSAFE.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMTVGETINFOTIP.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMTVGETINFOTIP.item")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control.originalImeContext")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.CommonDialog.defaultControlHwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MSG.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MSG.wParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MSG.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolStripManager+ModalMenuFilter._lastActiveWindow")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ToolStripManager+ModalMenuFilter._activeHwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_D.hbrBackground")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_D.hIcon")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_D.hInstance")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_D.hCursor")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+FontHandleWrapper.handle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Screen.hmonitor")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagVARDESC.lpstrSchema")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagVARDESC.unionMember")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.DataGridRow.tooltipID")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagCONTROLINFO.hAccel")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PICTDESCemf.hemf")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ICONINFO.hbmMask")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ICONINFO.hbmColor")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.DateTimePicker+EnumChildren.hwndFound")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDLAYOUT.prc")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDLAYOUT.pwpos")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagTYPEATTR.lpstrSchema")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.lpReserved2")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.lpReserved")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.lpDesktop")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.lpTitle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.hStdError")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.hStdOutput")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+STARTUPINFO_I.hStdInput")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMDAYSTATE.prgDayState")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PICTDESCbmp.hbitmap")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+PICTDESCbmp.hpalette")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NOTIFYICONDATA.hWnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NOTIFYICONDATA.hIcon")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+CA_STRUCT.pElems")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVFINDINFO.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.hbrBackground")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.lpszMenuName")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.hIcon")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.hInstance")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.lpszClassName")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.lpfnWndProc")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+WNDCLASS_I.hCursor")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLTIPTEXTA.hinst")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMHDR.hwndFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMHDR.idFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ENLINK.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ENLINK.wParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagCAUUID.pElems")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagOIFI.hAccel")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagOIFI.hwndFrame")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods.InvalidIntPtr")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods.LPSTR_TEXTCALLBACK")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods.NullHandleRef")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.SendKeys+SKEvent.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HELPINFO.hItemHandle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ENPROTECTED.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+ENPROTECTED.wParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVCOLUMN.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+IMAGELISTDRAWPARAMS.hdcDst")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+IMAGELISTDRAWPARAMS.himl")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.CachedItemHdcInfo.cachedItemBitmap")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.CachedItemHdcInfo.cachedItemHDC")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMCUSTOMDRAW.hdc")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMCUSTOMDRAW.lItemlParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMCUSTOMDRAW.dwItemSpec")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TVSORTCB.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TVSORTCB.hParent")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+DRAWITEMSTRUCT.hDC")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+DRAWITEMSTRUCT.hwndItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+DRAWITEMSTRUCT.itemData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM2.pvFilter")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM2.hbm")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM2.pszText_notUsed")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HDITEM2.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.StatusBar+ControlToolTip+Tool.id")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TBBUTTON.iString")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TBBUTTON.dwData")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.handle")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+OFNOTIFY.lpOFN")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+OFNOTIFY.pszFile")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+OFNOTIFY.hdr_hwndFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+OFNOTIFY.hdr_idFrom")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP.uId")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP.lpszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP.hinst")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP.hwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ErrorProvider+ControlItem.id")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeWindow+WindowClass.defWindowProc")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+MOUSEHOOKSTRUCT.hWnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagDISPPARAMS.rgdispidNamedArgs")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagDISPPARAMS.rgvarg")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_HITTESTINFO.hItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT.item_pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT.item_hItem")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT.hInsertAfter")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT.item_lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT.hParent")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVGROUP.pszHeader")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVGROUP.pszFooter")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+IMAGEINFO.hbmImage")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+IMAGEINFO.hbmMask")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+MetafileDCWrapper.hMetafileDC")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+MetafileDCWrapper.hBitmap")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+MetafileDCWrapper.hBitmapDC")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Control+MetafileDCWrapper.hOriginalBmp")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TRACKMOUSEEVENT.hwndTrack")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagTYPEDESC.unionMember")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LVBKIMAGE.hBmp")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.WebBrowserBase.hwndFocus")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVGETINFOTIP.lpszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+NMLVGETINFOTIP.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TBBUTTONINFO.pszText")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TBBUTTONINFO.lParam")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Application+ThreadWindows.focusedHwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.Application+ThreadWindows.activeHwnd")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLTIPTEXT.hinst")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagEXCEPINFO.pvReserved")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+tagEXCEPINFO.pfnDeferredFillIn")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.SystemInformation.processWinStation")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+HIGHCONTRAST_I.lpszDefaultScheme")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.NativeMethods+LOGBRUSH.lbHatch")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.Com2PictureConverter.lastPalette")] + +//LinkDemands reviewed as part of security push +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Design.WindowsFormsComponentEditor.EditComponent(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Windows.Forms.IWin32Window):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Design.ComponentEditorPage.IsPageMessage(System.Windows.Forms.Message&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetBackgroundRegion(System.Drawing.IDeviceContext,System.Drawing.Rectangle):System.Drawing.Region")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetMargins(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.MarginProperty):System.Windows.Forms.Padding")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.HitTestBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.IntPtr,System.Drawing.Point,System.Windows.Forms.VisualStyles.HitTestOptions):System.Windows.Forms.VisualStyles.HitTestCode")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetPartSize(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.ThemeSizeType):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.DrawText(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.String,System.Boolean,System.Windows.Forms.TextFormatFlags):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.DrawBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetTextExtent(System.Drawing.IDeviceContext,System.String,System.Windows.Forms.TextFormatFlags):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetTextExtent(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.String,System.Windows.Forms.TextFormatFlags):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetBackgroundContentRectangle(System.Drawing.IDeviceContext,System.Drawing.Rectangle):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetBackgroundExtent(System.Drawing.IDeviceContext,System.Drawing.Rectangle):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetFont(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.FontProperty):System.Drawing.Font")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetPartSize(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.ThemeSizeType):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.GetTextMetrics(System.Drawing.IDeviceContext):System.Windows.Forms.VisualStyles.TextMetrics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.DrawParentBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.Control):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.DrawBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Drawing.Rectangle):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.HitTestBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Drawing.Point,System.Windows.Forms.VisualStyles.HitTestOptions):System.Windows.Forms.VisualStyles.HitTestCode")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.VisualStyles.VisualStyleRenderer.DrawEdge(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.Edges,System.Windows.Forms.VisualStyles.EdgeStyle,System.Windows.Forms.VisualStyles.EdgeEffects):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Internal.DeviceContext.Dispose(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Internal.DeviceContext.get_Hdc():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Internal.IntNativeMethods+DRAWTEXTPARAMS..ctor(System.Int32,System.Int32)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Internal.IntUnsafeNativeMethods.GetObject(System.Runtime.InteropServices.HandleRef,System.Windows.Forms.Internal.IntNativeMethods+LOGFONT):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Internal.WindowsGraphics.FromGraphics(System.Drawing.Graphics,System.Windows.Forms.Internal.ApplyGraphicsProperties):System.Windows.Forms.Internal.WindowsGraphics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolBarButton.GetTBBUTTONINFO(System.Boolean,System.Int32):System.Windows.Forms.NativeMethods+TBBUTTONINFO")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlVersionInfo.get_ProductName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlVersionInfo.GetFileVersionInfo():System.Diagnostics.FileVersionInfo")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlVersionInfo.get_ProductVersion():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlVersionInfo.get_CompanyName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WindowsGraphicsWrapper..ctor(System.Drawing.IDeviceContext,System.Windows.Forms.TextFormatFlags)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.LinkLabel.PaintLink(System.Drawing.Graphics,System.Windows.Forms.LinkLabel+Link,System.Drawing.SolidBrush,System.Drawing.SolidBrush,System.Boolean,System.Drawing.RectangleF):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.LinkLabel.OnPaint(System.Windows.Forms.PaintEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+PARAFORMAT..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Label.OnPaint(System.Windows.Forms.PaintEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlNativeWindow..ctor(System.Windows.Forms.Control)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ControlNativeWindow.LockReference(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application.get_ProductName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application.GetAppFileVersionInfo():System.Diagnostics.FileVersionInfo")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application.get_ProductVersion():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application.get_CompanyName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.FontDialog.RunDialog(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer+TimerNativeWindow.StartTimer(System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer+TimerNativeWindow.get_IsTimerRunning():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer+TimerNativeWindow..ctor(System.Windows.Forms.Timer)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer+TimerNativeWindow.StopTimer(System.Boolean,System.IntPtr):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer+TimerNativeWindow.EnsureHandle():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NotifyIcon.Dispose(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.ComputeLayout():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.StatusBar.OnHandleDestroyed(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.StatusBar.get_SizeGripWidth():System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.StatusBar.UpdateTooltip(System.Windows.Forms.StatusBarPanel):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.StatusBar.set_ShowPanels(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.get_Focused():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.EndUpdate():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.UpdateText():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.ReleaseChildWindow():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.SetAutoComplete(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.set_SelectedText(System.String):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox.OnHandleCreated(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+HH_AKLINK..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ControlBindingsCollection.RemoveCore(System.Windows.Forms.Binding):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ControlBindingsCollection.AddCore(System.Windows.Forms.Binding):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+LVINSERTMARK..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control+ActiveXImpl..ctor(System.Windows.Forms.Control)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.FolderBrowserDialog.RunDialog(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridViewCheckBoxCell.PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MonthCalendar.HitTest(System.Int32,System.Int32):System.Windows.Forms.MonthCalendar+HitTestInfo")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MonthCalendar.Dispose(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox+ACNativeWindow.get_Visible():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TabControl.OnHandleDestroyed(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TabControl.OnHandleCreated(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ImageList+ImageCollection.set_Item(System.Int32,System.Drawing.Image):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ControlPaint.DrawFlatCheckBox(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Brush,System.Windows.Forms.ButtonState):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ControlPaint.CopyPixels(System.IntPtr,System.Drawing.IDeviceContext,System.Drawing.Point,System.Drawing.Point,System.Drawing.Size,System.Drawing.CopyPixelOperation):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.SetVisibleCore(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.get_TaskbarOwner():System.Runtime.InteropServices.HandleRef")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.UpdateWindowState():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window):System.Windows.Forms.DialogResult")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.ComputeWindowSize(System.Drawing.Size):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.set_MdiParentInternal(System.Windows.Forms.Form):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form.CheckCloseDialog(System.Boolean):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TreeNode.Realize(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TreeNode.UpdateNode(System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+HH_FTS_QUERY..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WebBrowserNavigatedEventArgs.get_Url():System.Uri")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStripDropDownMenu.CalculateInternalLayoutMetrics():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form+SecurityToolTip.Dispose():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form+SecurityToolTip.Pop():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Form+SecurityToolTip.GetTOOLINFO():System.Windows.Forms.NativeMethods+TOOLINFO_T")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Help.ShowPopup(System.Windows.Forms.Control,System.String,System.Drawing.Point):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MenuItem.CreateMenuItem():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MenuItem.UpdateMenuItem(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MenuItem.UpdateItemRtl(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MenuItem.AllocMsaaMenuInfo():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MenuItem.FreeMsaaMenuInfo():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStrip.OnPaint(System.Windows.Forms.PaintEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStrip.get_DropDownOwnerWindow():System.Windows.Forms.NativeWindow")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_T..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ContainerControl.UpdateFocusedControl():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+MCHITTESTINFO..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ColorDialog.RunDialog(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.CreateHandle():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control..ctor(System.Boolean)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.FromHandleInternal(System.IntPtr):System.Windows.Forms.Control")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.get_HandleInternal():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.InvokeMarshaledCallbacks():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.ScaleControl(System.Drawing.SizeF,System.Windows.Forms.BoundsSpecified):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.SizeFromClientSize(System.Int32,System.Int32):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control+ThreadMethodEntry):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.Dispose(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.UpdateBounds(System.Int32,System.Int32,System.Int32,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.PerformControlValidation(System.Boolean):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.PaintTransparentBackground(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Drawing.Region):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.get_IsMirrored():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.ProcessUICues(System.Windows.Forms.Message&):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.CreateGraphicsInternal():System.Drawing.Graphics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.UpdateStylesCore():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.RecreateHandleCore():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.get_IsHandleCreated():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.OnPrint(System.Windows.Forms.PaintEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Control.GetScaledBounds(System.Drawing.Rectangle,System.Drawing.SizeF,System.Windows.Forms.BoundsSpecified):System.Drawing.Rectangle")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+CHARFORMAT2A..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WebBrowserNavigatingEventArgs.get_Url():System.Uri")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WebBrowserNavigatingEventArgs.get_TargetFrameName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.BindingContext.CheckPropertyBindingCycles(System.Windows.Forms.BindingContext,System.Windows.Forms.Binding):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ComboBox+ComboBoxChildNativeWindow..ctor(System.Windows.Forms.ComboBox)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataObject..ctor(System.Object)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.ReleaseComObject(System.Object):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PtrToStructure(System.IntPtr,System.Type):System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetRectsFromRegion(System.IntPtr):System.Windows.Forms.NativeMethods+RECT[]")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IsComObject(System.Object):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SizeOf(System.Type):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolBar.RealizeButtons():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolBar.OnHandleCreated(System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolBar.InternalSetButton(System.Int32,System.Windows.Forms.ToolBarButton,System.Boolean,System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolBar.ForceButtonWidths():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridToolTip.Destroy():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridToolTip.RemoveToolTip(System.IntPtr):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridToolTip.AddToolTip(System.String,System.IntPtr,System.Drawing.Rectangle):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridToolTip.CreateToolTipHandle():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ParkingWindow.CheckDestroy():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ParkingWindow.Destroy():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NotifyIcon+NotifyIconNativeWindow..ctor(System.Windows.Forms.NotifyIcon)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.FileDialog.RunDialog(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ImageList.GetBitmap(System.Int32):System.Drawing.Bitmap")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ImageList.set_ImageStream(System.Windows.Forms.ImageListStreamer):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ImageList.AddToHandle(System.Windows.Forms.ImageList+Original,System.Drawing.Bitmap):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ImageList.Draw(System.Drawing.Graphics,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip+ToolTipNativeWindow..ctor(System.Windows.Forms.ToolTip)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.SystemInformation.get_HighContrast():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.SystemInformation.get_UserInteractive():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.SystemInformation.get_ScreenOrientation():System.Windows.Forms.ScreenOrientation")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color,System.Drawing.Color):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.MeasureText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Size):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.TextRenderer.MeasureText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Cursor.DrawImageCore(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Cursor.GetIconSize(System.IntPtr):System.Drawing.Size")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Cursor.LoadPicture(System.Windows.Forms.UnsafeNativeMethods+IStream):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.InsertItemsNative(System.Int32,System.Windows.Forms.ListViewItem[]):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.SetColumnInfo(System.Int32,System.Windows.Forms.ColumnHeader):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.DestroyLVGROUP(System.Windows.Forms.NativeMethods+LVGROUP):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.GetLVGROUP(System.Windows.Forms.ListViewGroup):System.Windows.Forms.NativeMethods+LVGROUP")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.GenerateRandomName():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ListView.PositionHeader():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+IconRegion.get_Region():System.Drawing.Region")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Timer.set_Enabled(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+MSOCRINFOSTRUCT..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ScrollBar.UpdateScrollInfo():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ScrollBar..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MdiClient.SetWindowRgn():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.MdiClient.SetBoundsCore(System.Int32,System.Int32,System.Int32,System.Int32,System.Windows.Forms.BoundsSpecified):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+CHOOSEFONT..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStripMenuItem.GetNativeMenuItemTextAndShortcut():System.String")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStripMenuItem.GetNativeMenuItemEnabled():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+SCROLLINFO..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+MONITORINFOEX..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WindowsFormsUtils+DCMapping.get_Graphics():System.Drawing.Graphics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGrid.OnKeyDown(System.Windows.Forms.KeyEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataFormats.GetFormat(System.String):System.Windows.Forms.DataFormats+Format")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.AxHost+AxContainer.ListAxControls(System.Collections.ArrayList,System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.AxHost+AxContainer.FormCreated():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.get_ComponentManager():System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.ProcessFilters(System.Windows.Forms.NativeMethods+MSG&,System.Boolean&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.InstallWindowsFormsSyncContextIfNeeded():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.RevokeComponent():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.PreTranslateMessage(System.Windows.Forms.NativeMethods+MSG&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(System.Int32,System.Windows.Forms.ApplicationContext):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+ThemingScope.CreateActivationContext(System.String,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ScrollProperties.UpdateScrollInfo():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.HtmlElementCollection.GetElementsByName(System.String):System.Windows.Forms.HtmlElementCollection")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.HtmlElementCollection.get_Item(System.String):System.Windows.Forms.HtmlElement")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WebBrowserSiteBase.StopEvents():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow.EnsureDestroyed():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow.Update(System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow.Remove(System.Windows.Forms.ErrorProvider+ControlItem):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow.EnsureCreated():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow.Add(System.Windows.Forms.ErrorProvider+ControlItem):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider+ErrorWindow..ctor(System.Windows.Forms.ErrorProvider,System.Windows.Forms.Control)")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.GetMinTOOLINFO(System.Windows.Forms.Control):System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.SetToolInfo(System.Windows.Forms.Control,System.String):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.GetHandleCreated():System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.SetTool(System.Windows.Forms.IWin32Window,System.String,System.Windows.Forms.ToolTip+TipInfo+Type,System.Drawing.Point):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.GetWinTOOLINFO(System.IntPtr):System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.DestroyHandle():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.CreateHandle():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.GetTOOLINFO(System.Windows.Forms.Control,System.String):System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolTip.get_Handle():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+LVGROUP..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ErrorProvider.ErrorManager_CurrentChanged(System.Object,System.EventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+TRACKMOUSEEVENT..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ToolStripDropDown.ReparentToDropDownOwnerWindow():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+LVTILEVIEWINFO..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+CHARFORMATA..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.BindToObject.CheckBinding():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+CHARFORMATW..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+CHOOSECOLOR..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WindowsFormsUtils.CreateMeasurementGraphics():System.Drawing.Graphics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.WindowsFormsUtils.RotateLeft(System.Int32,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.PaintEventArgs.get_Graphics():System.Drawing.Graphics")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+NONCLIENTMETRICS..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridView.OnKeyDown(System.Windows.Forms.KeyEventArgs):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.DataGridView.DrawColHeaderShadow(System.Drawing.Graphics,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+HH_POPUP..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.NativeMethods+OPENFILENAME_I..ctor()")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.GetCheckBoxImage(System.Drawing.Color,System.Drawing.Rectangle,System.Drawing.Color&,System.Drawing.Bitmap&):System.Drawing.Bitmap")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorOptions.Calculate():System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.Draw3DLiteBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData,System.Boolean):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.DrawFlatFocus(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.PaintButtonBackground(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.Internal.WindowsBrush):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.Windows.Forms.FileDialog.RunDialogOld(System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Windows.Forms.ToolStripDropDownMenu..ctor(System.Windows.Forms.ToolStripItem)")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.ToolStripDropDownMenu.ShortcutToText(System.Windows.Forms.Keys,System.String):System.String")] + +[module: SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatRequireUnboxing", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor.OnPropsInfoDisposed(System.Object,System.EventArgs):System.Void", MessageId="System.Collections.IEnumerator.get_Current")] +[module: SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatRequireUnboxing", Scope="member", Target="System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor.CheckClear(System.Object):System.Void", MessageId="System.Collections.IEnumerator.get_Current")] +//End SafeHandle Suppressions. + +// InProc SxS Supperssions: Methods wich consume but not expose resources +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Resources.AssemblyNamesTypeResolutionService.#GetType(System.String,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Resources.ResXResourceReader.#EnsureResData()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManager.#GetZonePromptingLevel(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManager.#get_DefaultBrowserExePath()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerMoreInformation.#.ctor(System.Security.Policy.TrustManagerPromptOptions,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerMoreInformation.#LoadWarningBitmap(System.Security.Policy.TrustManagerWarningLevel,System.Windows.Forms.PictureBox)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerMoreInformation.#OnUserPreferenceChanged(System.Object,Microsoft.Win32.UserPreferenceChangedEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerPromptUI.#LoadGlobeBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerPromptUI.#LoadWarningBitmap(System.Security.Policy.TrustManagerWarningLevel)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerPromptUI.#TrustManagerPromptUI_ShowSupportPage(System.Object,System.Windows.Forms.LinkLabelLinkClickedEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Security.Policy.TrustManagerPromptUI.#UpdateFonts()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ComponentManager.#System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ModalApplicationContext.#DisableThreadWindows(System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ParkingWindow.#CheckDestroy()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ParkingWindow.#RemoveReflectChild()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ParkingWindow.#UnparkHandle(System.Runtime.InteropServices.HandleRef)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#Dispose(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#DisposeParkingWindow()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#OnThreadException(System.Exception)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#PreTranslateMessage(System.Windows.Forms.NativeMethods+MSG&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#RunMessageLoop(System.Int32,System.Windows.Forms.ApplicationContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadContext.#System.Windows.Forms.UnsafeNativeMethods+IMsoComponent.FContinueMessageLoop(System.Int32,System.Int32,System.Windows.Forms.NativeMethods+MSG[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadWindows.#.ctor(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application+ThreadWindows.#Enable(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#AddMessageFilter(System.Windows.Forms.IMessageFilter)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#add_EnterThreadModal(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#add_Idle(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#add_LeaveThreadModal(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#add_ThreadException(System.Threading.ThreadExceptionEventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#BeginModalMessageLoop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#DoEvents()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#DoEventsModal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#EnableVisualStyles()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#EndModalMessageLoop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#ExitThreadInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#FilterMessage(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#FormActivated(System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_AllowQuit()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_CanContinueIdle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_ComCtlSupportsVisualStyles()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_CustomThreadExceptionHandlerAttached()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_MessageLoop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#get_UseEverettThreadAffinity()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#OleRequired()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#OnThreadException(System.Exception)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#ParkHandle(System.Runtime.InteropServices.HandleRef)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#ParkHandle(System.Windows.Forms.CreateParams)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#RaiseIdle(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#RegisterMessageLoop(System.Windows.Forms.Application+MessageLoopCallback)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#RemoveMessageFilter(System.Windows.Forms.IMessageFilter)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#remove_EnterThreadModal(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#remove_Idle(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#remove_LeaveThreadModal(System.EventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#remove_ThreadException(System.Threading.ThreadExceptionEventHandler)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#Restart()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#Run()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#Run(System.Windows.Forms.ApplicationContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#Run(System.Windows.Forms.Form)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#RunDialog(System.Windows.Forms.Form)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#SendThemeChanged(System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#UnparkHandle(System.Runtime.InteropServices.HandleRef)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Application.#UnregisterMessageLoop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AutomationMessages.#GenerateLogFileName(System.IntPtr&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AutomationMessages.#ReadAutomationText(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AutomationMessages.#WriteAutomationText(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost+OleInterfaces.#System.Windows.Forms.UnsafeNativeMethods+IOleClientSite.ShowObject()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost+State.#CreateStorage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost+State.#RefreshStorage(System.Windows.Forms.UnsafeNativeMethods+IPersistStorage)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#CreateWithLicense(System.String,System.Guid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#CreateWithoutLicense(System.Guid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#DetachAndForward(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#GetFontFromIFont(System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#GetFontFromIFontDisp(System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#GetIFontDispFromFont(System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#GetIFontFromFont(System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#GetLicenseKey(System.Guid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#OnLostFocus(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.AxHost.#SetupLogPixels(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.BindingNavigator.#AddStandardItems()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Button.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonBase.#set_Image(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorOptions.#Calculate()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutOptions.#GetTextSize(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#CommonLayout()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#Draw3DBorderHighContrastRaised(System.Drawing.Graphics,System.Drawing.Rectangle&,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#Draw3DBorderNormal(System.Drawing.Graphics,System.Drawing.Rectangle&,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#Draw3DBorderRaised(System.Drawing.Graphics,System.Drawing.Rectangle&,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#Draw3DLiteBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawDefaultBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawDitheredFill(System.Drawing.Graphics,System.Drawing.Color,System.Drawing.Color,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawFlatBorderWithSize(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawFlatFocus(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawImage(System.Drawing.Graphics,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawImageCore(System.Drawing.Graphics,System.Drawing.Image,System.Drawing.Rectangle,System.Drawing.Point,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#DrawText(System.Drawing.Graphics,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData,System.Drawing.Color,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonBaseAdapter.#GetPreferredSizeCore(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonPopupAdapter.#PaintOver(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonPopupAdapter.#PaintUp(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonStandardAdapter.#PaintThemedButtonBackground(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.ButtonStandardAdapter.#PaintWorker(System.Windows.Forms.PaintEventArgs,System.Boolean,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckableControlBaseAdapter.#GetPreferredSizeCore(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.#DrawCheckBackground(System.Boolean,System.Windows.Forms.CheckState,System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color,System.Boolean,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.#DrawCheckFlat(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData,System.Drawing.Color,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.#DrawCheckOnly(System.Int32,System.Boolean,System.Boolean,System.Windows.Forms.CheckState,System.Drawing.Graphics,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData,System.Drawing.Color,System.Drawing.Color,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.#DrawPopupBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxPopupAdapter.#PaintDown(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxPopupAdapter.#PaintOver(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxPopupAdapter.#PaintUp(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.CheckState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxStandardAdapter.#GetPreferredSizeCore(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxStandardAdapter.#Layout(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.RadioButtonBaseAdapter.#DrawCheckBackground3DLite(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+ColorData,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.RadioButtonBaseAdapter.#DrawCheckBackgroundFlat(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.RadioButtonBaseAdapter.#DrawCheckOnly(System.Windows.Forms.PaintEventArgs,System.Windows.Forms.ButtonInternal.ButtonBaseAdapter+LayoutData,System.Drawing.Color,System.Drawing.Color,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.RadioButtonStandardAdapter.#Layout(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.CheckBox.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.CheckedListBox.#OnDrawItem(System.Windows.Forms.DrawItemEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Clipboard.#SetFileDropList(System.Collections.Specialized.StringCollection)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ColorDialog.#RunDialog(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox+ACNativeWindow.#get_Visible()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox+ACNativeWindow.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox+AutoCompleteDropDownFinder.#FindDropDowns(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox+ComboBoxChildNativeWindow.#WmGetObject(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox+FlatComboAdapter.#DrawFlatCombo(System.Windows.Forms.ComboBox,System.Drawing.Graphics)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#ChildWndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#DefChildWndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#EditToComboboxMapping(System.Windows.Forms.Message)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#EndUpdate()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#GetComboHeight()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#get_Focused()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#OnHandleCreated(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#SetAutoComplete(System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#set_SelectedText(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#UpdateItemHeight()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#UpdateText()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#WmEraseBkgnd(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#WmReflectDrawItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#WmReflectMeasureItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.CommonDialog.#HookProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.CommonDialog.#ShowDialog(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentManagerProxy.#.ctor(System.Windows.Forms.ComponentManagerBroker,System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentManagerProxy.#System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FRevokeComponent(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor.#GetPropertyValue(System.Object,System.String,System.Boolean&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ContainerControl.#FocusActiveControlInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ContainerControl.#GetFontAutoScaleDimensions()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ContainerControl.#get_CurrentAutoScaleDimensions()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ContextMenu.#Show(System.Windows.Forms.Control,System.Drawing.Point,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXImpl.#GetControlInfo(System.Windows.Forms.NativeMethods+tagCONTROLINFO)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXImpl.#get_LogPixels()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXImpl.#QuickActivate(System.Windows.Forms.UnsafeNativeMethods+tagQACONTAINER,System.Windows.Forms.UnsafeNativeMethods+tagQACONTROL)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXImpl.#SetObjectRects(System.Windows.Forms.NativeMethods+COMRECT,System.Windows.Forms.NativeMethods+COMRECT)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ControlAccessibleObject.#set_Handle(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ControlCollection.#Add(System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ControlNativeWindow.#OnHandleChange()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+FontHandleWrapper.#.ctor(System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+MetafileDCWrapper.#DICopy(System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Windows.Forms.NativeMethods+RECT,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+MetafileDCWrapper.#System.IDisposable.Dispose()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#.cctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#DrawToBitmap(System.Drawing.Bitmap,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#EndInvoke(System.IAsyncResult)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#FocusInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#FromChildHandleInternal(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#GetChildWindowsTabOrderList()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#GetDefaultFontHandleWrapper()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#GetHRgn(System.Drawing.Region)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#get_HostedInWin32DialogManager()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#get_InvokeRequired()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#get_IsHandleCreated()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#get_IsWindowObscured()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#get_PropagatingImeMode()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#InitMouseWheelSupport()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#Invalidate(System.Drawing.Region,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#MarshaledInvoke(System.Windows.Forms.Control,System.Delegate,System.Object[],System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#OnHandleCreated(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#OnPrint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PaintBackColor(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PaintException(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PaintTransparentBackground(System.Windows.Forms.PaintEventArgs,System.Drawing.Rectangle,System.Drawing.Region)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PrintToMetaFile(System.Runtime.InteropServices.HandleRef,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PrintToMetaFileRecursive(System.Runtime.InteropServices.HandleRef,System.IntPtr,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#PrintToMetaFile_SendPrintMessage(System.Runtime.InteropServices.HandleRef,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#ProcessUICues(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#RecreateHandleCore()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#SetParentHandle(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#set_BecomingActiveControl(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#set_Region(System.Drawing.Region)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#UpdateBounds()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#UpdateChildControlIndex(System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#UpdateChildZOrder(System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#UpdateReflectParent(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WaitForWaitHandle(System.Threading.WaitHandle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmClose(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmCtlColorControl(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmMouseUp(System.Windows.Forms.Message&,System.Windows.Forms.MouseButtons,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmPaint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmParentNotify(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmQueryNewPalette(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WmWindowPosChanged(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#CopyPixels(System.IntPtr,System.Drawing.IDeviceContext,System.Drawing.Point,System.Drawing.Point,System.Drawing.Size,System.Drawing.CopyPixelOperation)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawBackgroundImage(System.Drawing.Graphics,System.Drawing.Image,System.Drawing.Color,System.Windows.Forms.ImageLayout,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Drawing.Point,System.Windows.Forms.RightToLeft)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Int32,System.Windows.Forms.ButtonBorderStyle,System.Drawing.Color,System.Int32,System.Windows.Forms.ButtonBorderStyle,System.Drawing.Color,System.Int32,System.Windows.Forms.ButtonBorderStyle,System.Drawing.Color,System.Int32,System.Windows.Forms.ButtonBorderStyle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawBorder3D(System.Drawing.Graphics,System.Int32,System.Int32,System.Int32,System.Int32,System.Windows.Forms.Border3DStyle,System.Windows.Forms.Border3DSide)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawBorderComplex(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Windows.Forms.ButtonBorderStyle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawBorderSimple(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Windows.Forms.ButtonBorderStyle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawContainerGrabHandle(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawFlatCheckBox(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Brush,System.Windows.Forms.ButtonState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawFocusRectangle(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawFrameControl(System.Drawing.Graphics,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Drawing.Color,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawGrabHandle(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawGrid(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Size,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawImageColorized(System.Drawing.Graphics,System.Drawing.Image,System.Drawing.Rectangle,System.Drawing.Imaging.ColorMatrix)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawImageDisabled(System.Drawing.Graphics,System.Drawing.Image,System.Drawing.Rectangle,System.Drawing.Color,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawImageReplaceColor(System.Drawing.Graphics,System.Drawing.Image,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawLockedFrame(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawReversibleFrame(System.Drawing.Rectangle,System.Drawing.Color,System.Windows.Forms.FrameStyle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawReversibleLine(System.Drawing.Point,System.Drawing.Point,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawSelectionFrame(System.Drawing.Graphics,System.Boolean,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawSizeGrip(System.Drawing.Graphics,System.Drawing.Color,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawStringDisabled(System.Drawing.Graphics,System.String,System.Drawing.Font,System.Drawing.Color,System.Drawing.RectangleF,System.Drawing.StringFormat)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#DrawVisualStyleBorder(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#FillReversibleRectangle(System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#PaintTableCellBorder(System.Windows.Forms.TableLayoutPanelCellBorderStyle,System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ControlPaint.#PaintTableControlBorder(System.Windows.Forms.TableLayoutPanelCellBorderStyle,System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Cursor.#.ctor(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Cursor.#DrawImageCore(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Cursor.#LoadPicture(System.Windows.Forms.UnsafeNativeMethods+IStream)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#.cctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ColAutoResize(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#CreateScrollableRegion(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#DrawSplitBar(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#OnBackColorChanged(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#OnForeColorChanged(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#PaintColumnHeaderText(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#PaintGrid(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#RecalculateFonts()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetAlternatingBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetBackgroundColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetGridLineColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetHeaderBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetHeaderForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetLinkColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetParentRowsBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetParentRowsForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetSelectionBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ResetSelectionForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#RowAutoResize(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_AlternatingBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_BackgroundColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_GridLineColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_HeaderBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_HeaderForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_LinkColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_SelectionBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#set_SelectionForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeAlternatingBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeBackgroundColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeGridLineColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeHeaderBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeHeaderForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeLinkColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeParentRowsBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeParentRowsForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeSelectionBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#ShouldSerializeSelectionForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridBoolColumn.#Paint(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.CurrencyManager,System.Int32,System.Drawing.Brush,System.Drawing.Brush,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#.ctor(System.Windows.Forms.DataGrid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#GetBackButtonRect(System.Drawing.Rectangle,System.Boolean,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#GetDetailsButtonRect(System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#GetDetailsButtonWidth()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#PaintBackButton(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#PaintDownButton(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#ResetBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#ResetForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#set_BackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#set_ForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#ShouldSerializeBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#ShouldSerializeForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#.ctor(System.Windows.Forms.DataGrid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintLeftArrow(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintRightArrow(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#set_BackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#set_ForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridRelationshipRow.#PaintCellContents(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.DataGridColumnStyle,System.Drawing.Brush,System.Drawing.Brush,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridRelationshipRow.#PaintRelations(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Int32,System.Int32,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridRelationshipRow.#PaintRelationText(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridRow.#PaintHeader(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridRow.#PaintIcon(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean,System.Boolean,System.Drawing.Bitmap,System.Drawing.Brush)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#.cctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#.ctor(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ComputeRelationshipRect()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetAlternatingBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetGridLineColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetHeaderBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetHeaderForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetLinkColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetSelectionBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ResetSelectionForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_AlternatingBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_BackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_FocusedRelation(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_ForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_GridLineColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_HeaderBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_HeaderForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_LinkColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_SelectionBackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#set_SelectionForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeAlternatingBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeGridLineColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeHeaderBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeHeaderForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeLinkColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeSelectionBackColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#ShouldSerializeSelectionForeColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTextBoxColumn.#PaintText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String,System.Drawing.Brush,System.Drawing.Brush,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridToolTip.#AddToolTip(System.String,System.IntPtr,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridToolTip.#CreateToolTipHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridToolTip.#RemoveToolTip(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#CreateScrollableRegion(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#DrawColHeaderShadow(System.Drawing.Graphics,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#DrawShadowRect(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#DrawSplitBar(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#get_CachedGraphics()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#get_DefaultCellStyle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#get_DefaultColumnHeadersDefaultCellStyle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#get_DefaultDefaultCellStyle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#get_DefaultRowHeadersDefaultCellStyle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#PaintBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#PaintColumnHeaders(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#PaintGrid(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#ResetBackgroundColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#set_BackgroundColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#set_GridColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridView.#ShouldSerializeBackgroundColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewButtonCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCell.#DetachEditingControl()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCell.#GetContrastedPens(System.Drawing.Color,System.Drawing.Pen&,System.Drawing.Pen&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCell.#PaintBorder(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCell.#PaintErrorIcon(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCheckBoxCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewColumnHeaderCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewComboBoxCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Drawing.Rectangle&,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewHeaderCell.#Paint(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageCell.#set_ValueIsIconInternal(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageColumn.#.ctor(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageColumn.#set_ValuesAreIcons(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageColumn.#ShouldSerializeDefaultCellStyle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewLinkCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRow.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRow.#PaintCells(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Boolean,System.Boolean,System.Windows.Forms.DataGridViewPaintParts)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#PaintIcon(System.Drawing.Graphics,System.Drawing.Bitmap,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewTextBoxCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewTopLeftHeaderCell.#PaintPrivate(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Int32,System.Windows.Forms.DataGridViewElementStates,System.Object,System.String,System.Windows.Forms.DataGridViewCellStyle,System.Windows.Forms.DataGridViewAdvancedBorderStyle,System.Windows.Forms.DataGridViewPaintParts,System.Boolean,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject+OleConverter.#GetDataFromOleIStream(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject+OleConverter.#GetDataFromOleOther(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject+OleConverter.#ReadByteStreamFromHandle(System.IntPtr,System.Boolean&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject+OleConverter.#ReadFileListFromHandle(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject+OleConverter.#ReadStringFromHandle(System.IntPtr,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject.#GetCompatibleBitmap(System.Drawing.Bitmap)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject.#SaveFileListToHandle(System.IntPtr,System.String[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject.#SaveStreamToHandle(System.IntPtr&,System.IO.Stream)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject.#SaveStringToHandle(System.IntPtr,System.String,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataObject.#System.Runtime.InteropServices.ComTypes.IDataObject.GetData(System.Runtime.InteropServices.ComTypes.FORMATETC&,System.Runtime.InteropServices.ComTypes.STGMEDIUM&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DateTimePicker.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Design.ComponentEditorForm+PageSelector.#CreateDitherBrush()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Design.ComponentEditorForm+PageSelector.#DrawTreeItem(System.String,System.Int32,System.IntPtr,System.Windows.Forms.NativeMethods+RECT,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Design.ComponentEditorForm+PageSelector.#FillRectDither(System.IntPtr,System.Windows.Forms.NativeMethods+RECT)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Design.ComponentEditorForm.#OnConfigureUI()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawItemEventArgs.#DrawBackground()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewColumnHeaderEventArgs.#DrawBackground()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewItemEventArgs.#DrawBackground()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewItemEventArgs.#DrawText(System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewItemEventArgs.#UpdateBounds(System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewSubItemEventArgs.#DrawBackground()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawListViewSubItemEventArgs.#DrawText(System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DrawToolTipEventArgs.#DrawBackground()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#Add(System.Windows.Forms.ErrorProvider+ControlItem)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#CreateMirrorDC(System.IntPtr,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#EnsureCreated()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#EnsureDestroyed()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#OnPaint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#Remove(System.Windows.Forms.ErrorProvider+ControlItem)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider+ErrorWindow.#Update(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider.#ResetIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider.#ShouldSerializeIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FileDialog.#HookProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FileDialog.#MessageBoxWithFocusRestore(System.String,System.String,System.Windows.Forms.MessageBoxButtons,System.Windows.Forms.MessageBoxIcon)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FileDialog.#ProcessFileNames()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FileDialog.#PromptUserIfAppropriate(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FileDialog.#RunDialogOld(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FontDialog.#HookProc(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FontDialog.#RunDialog(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FontDialog.#ShouldSerializeFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FontDialog.#ToString()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.FontDialog.#UpdateFont(System.Windows.Forms.NativeMethods+LOGFONT)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#Dispose()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#FormLocationChanged(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#Pop(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#RecreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#SetupToolTip()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form+SecurityToolTip.#Show()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#GetAutoScaleSize(System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#get_ActiveForm()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#get_TaskbarOwner()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#ResolveZoneAndSiteNames(System.Collections.ArrayList,System.String&,System.String&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#RestrictedProcessNcActivate()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#SelectInternal(System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#Show(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#ShowDialog(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#UpdateMenuHandles(System.Windows.Forms.MainMenu,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#UpdateWindowIcon(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.GroupBox.#DrawGroupBox(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.GroupBox.#WmEraseBkgnd(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.GroupBoxRenderer.#DrawUnthemedGroupBoxNoText(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.GroupBoxState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.GroupBoxRenderer.#DrawUnthemedGroupBoxWithText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String,System.Drawing.Font,System.Drawing.Color,System.Windows.Forms.TextFormatFlags,System.Windows.Forms.VisualStyles.GroupBoxState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Help.#Resolve(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Help.#ShowHTML10Help(System.Windows.Forms.Control,System.String,System.Windows.Forms.HelpNavigator,System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Help.#ShowHTMLFile(System.Windows.Forms.Control,System.String,System.Windows.Forms.HelpNavigator,System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.HtmlDocument.#InvokeScript(System.String,System.Object[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.HtmlElement.#InvokeMember(System.String,System.Object[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList+ImageCollection.#Add(System.Drawing.Icon)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList+ImageCollection.#Add(System.Windows.Forms.ImageList+Original,System.Windows.Forms.ImageList+ImageCollection+ImageInfo)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList+ImageCollection.#GetEnumerator()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList+ImageCollection.#set_Item(System.Int32,System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList+ImageCollection.#System.Collections.ICollection.CopyTo(System.Array,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList.#AddToHandle(System.Windows.Forms.ImageList+Original,System.Drawing.Bitmap)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList.#Draw(System.Drawing.Graphics,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageList.#set_ImageStream(System.Windows.Forms.ImageListStreamer)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ImageListStreamer.#.ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.InputLanguage.#get_LayoutName()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#DisposeFont(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#GetDeviceCapabilities(System.Windows.Forms.Internal.DeviceCapabilities)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_BackgroundColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_BackgroundMode()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_BinaryRasterOperation()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_MapMode()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_TextAlignment()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_TextColor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_ViewportExtent()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#get_ViewportOrigin()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#ResetFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetBackgroundColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetBackgroundMode(System.Windows.Forms.Internal.DeviceContextBackgroundMode)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetMapMode(System.Windows.Forms.Internal.DeviceContextMapMode)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetRasterOperation(System.Windows.Forms.Internal.DeviceContextBinaryRasterOperationFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetTextAlignment(System.Windows.Forms.Internal.DeviceContextTextAlignment)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetTextColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetViewportExtent(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SetViewportOrigin(System.Drawing.Point)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.MeasurementDCInfo.#IsMeasurementDC(System.Windows.Forms.Internal.DeviceContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.MeasurementDCInfo.#ResetIfIsMeasurementDC(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsFont.#get_Height()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsFont.#get_Size()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#DrawEllipse(System.Windows.Forms.Internal.WindowsPen,System.Windows.Forms.Internal.WindowsBrush,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#DrawLine(System.Windows.Forms.Internal.WindowsPen,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#DrawPie(System.Windows.Forms.Internal.WindowsPen,System.Drawing.Rectangle,System.Single,System.Single)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#DrawRectangle(System.Windows.Forms.Internal.WindowsPen,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#DrawText(System.String,System.Windows.Forms.Internal.WindowsFont,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.Internal.IntTextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#FillRectangle(System.Windows.Forms.Internal.WindowsBrush,System.Int32,System.Int32,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#GetNearestColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#GetOverhangPadding(System.Windows.Forms.Internal.WindowsFont)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#GetTextExtent(System.String,System.Windows.Forms.Internal.WindowsFont)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#GetTextMetrics()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsGraphics.#MeasureText(System.String,System.Windows.Forms.Internal.WindowsFont,System.Drawing.Size,System.Windows.Forms.Internal.IntTextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.IntSecurity.#DemandFileIO(System.Security.Permissions.FileIOPermissionAccess,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#Dispose(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#GetLeadingTextPaddingFromTextFormatFlags()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#GetPreferredSizeCore(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#PrintToMetaFileRecursive(System.Runtime.InteropServices.HandleRef,System.IntPtr,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Label.#set_Image(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel+LinkAccessibleObject.#get_Bounds()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel.#EnsureRun(System.Drawing.Graphics)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel.#OnPaintBackground(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel.#PaintLink(System.Drawing.Graphics,System.Windows.Forms.LinkLabel+Link,System.Drawing.SolidBrush,System.Drawing.SolidBrush,System.Boolean,System.Drawing.RectangleF)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkLabel.#PointInLink(System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkUtilities.#EnsureLinkFonts(System.Drawing.Font,System.Windows.Forms.LinkBehavior,System.Drawing.Font&,System.Drawing.Font&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkUtilities.#GetIEColor(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.LinkUtilities.#GetIELinkBehavior()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#Refresh()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#UpdateMaxItemWidth(System.Object,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#WmPrint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#WmReflectDrawItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#WmReflectMeasureItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListBox.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#CleanPreviousBackgroundImageFiles()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#CustomDraw(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#DeleteFileName(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#Dispose(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#GenerateRandomName()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#PositionHeader()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#Scroll(System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#SetBackgroundImage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#SetItemText(System.Int32,System.Int32,System.String,System.Windows.Forms.NativeMethods+LVITEM&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#SetToolTip(System.Windows.Forms.ToolTip,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#WmNotify(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#WmPrint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListViewItem.#Clone()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListViewItem.#Serialize(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListViewItemConverter.#ConvertTo(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListViewSubItemConverter.#ConvertTo(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MaskedTextBox.#WmPrint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MdiClient.#SetWindowRgn()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MdiControlStrip.#.ctor(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MdiControlStrip.#OnTargetWindowHandleRecreated(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MenuItem.#CreateMenuItemInfo()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MenuItem.#get_MenuIndex()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MenuItem.#WmDrawItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MenuItem.#WmMeasureItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MenuStrip.#ProcessCmdKey(System.Windows.Forms.Message&,System.Windows.Forms.Keys)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MessageBox.#ShowCore(System.Windows.Forms.IWin32Window,System.String,System.String,System.Windows.Forms.MessageBoxButtons,System.Windows.Forms.MessageBoxIcon,System.Windows.Forms.MessageBoxDefaultButton,System.Windows.Forms.MessageBoxOptions,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MonthCalendar.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MonthCalendar.#GetMinReqRect(System.Int32,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeMethods+PICTDESCbmp.#.ctor(System.Drawing.Bitmap)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow+WindowClass.#RegisterClass()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow+WindowClass.#UnregisterClass()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow.#AdjustWndProcFlagsFromRegistry(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow.#AssignHandle(System.IntPtr,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow.#CreateHandle(System.Windows.Forms.CreateParams)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow.#ForceExitMessageLoop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NativeWindow.#get_WndProcFlags()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NotifyIcon+NotifyIconNativeWindow.#Finalize()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NotifyIcon.#Dispose(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NotifyIcon.#ShowBalloonTip(System.Int32,System.String,System.String,System.Windows.Forms.ToolTipIcon)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NotifyIcon.#ShowContextMenu()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.NotifyIcon.#UpdateIcon(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PageSetupDialog.#RunDialog(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PaintEventArgs.#get_Graphics()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Panel.#PrintToMetaFileRecursive(System.Runtime.InteropServices.HandleRef,System.IntPtr,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#CalculateUri(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#Load()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#LoadAsync()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#LoadCompletedDelegate(System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#set_ErrorImage(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#set_InitialImage(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PictureBox.#ShouldSerializeImage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintControllerWithStatusDialog.#OnStartPrint(System.Drawing.Printing.PrintDocument,System.Drawing.Printing.PrintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintDialog.#CreatePRINTDLGEX()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintDialog.#ShowPrintDialog(System.IntPtr,System.Windows.Forms.NativeMethods+PRINTDLGEX)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintDialog.#ShowPrintDialog(System.IntPtr,System.Windows.Forms.NativeMethods+WndProc,System.Windows.Forms.NativeMethods+PRINTDLG)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewControl.#ComputeLayout()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewControl.#ComputePreview()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewControl.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewDialog.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewDialog.#OncloseToolStripButtonPaint(System.Object,System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PrintPreviewDialog.#OnprintToolStripButtonClick(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ProfessionalColorTable.#InitCommonColors(System.Collections.Generic.Dictionary`2&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ProgressBar.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGrid.#CreateTab(System.Type,System.ComponentModel.Design.IDesignerHost)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGrid.#EnsureLargeButtons()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGrid.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGrid.#ProcessDialogKey(System.Windows.Forms.Keys)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGrid.#SetupToolbar(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.CategoryGridEntry.#PaintOutline(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.DocComment.#GetOptimalHeight(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.DocComment.#UpdateUIWithFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridEntry.#GetPropEntries(System.Windows.Forms.PropertyGridInternal.GridEntry,System.Object,System.Type)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridEntry.#PaintLabel(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridEntry.#PaintOutline(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridEntry.#PaintValue(System.Object,System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Rectangle,System.Windows.Forms.PropertyGridInternal.GridEntry+PaintValueFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridErrorDlg.#.ctor(System.Windows.Forms.PropertyGrid)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.GridErrorDlg.#OnVisibleChanged(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView+DropDownHolder.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView+DropDownHolder.#SetComponent(System.Windows.Forms.Control,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView+GridViewEdit.#OnMouseEnter(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView+MouseHook.#ProcessMouseDown(System.IntPtr,System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView+MouseHook.#set_HookMouseDown(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#CommonEditorHide(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#GetBaseHfont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#GetBoldFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#GetBoldHfont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#GetLineBrush(System.Drawing.Graphics)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#get_DialogButton()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#get_DropDownButton()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#OnEditLostFocus(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#OnMouseMove(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#PopupDialog(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#set_BackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.PropertyGridInternal.PropertyGridView.#ShowDialog(System.Windows.Forms.Form)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RadioButton.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox+OleCallback.#GetContextMenu(System.Int16,System.IntPtr,System.Windows.Forms.NativeMethods+CHARRANGE,System.IntPtr&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox.#GetCharFormatFont(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox.#get_CreateParams()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox.#SetupLogPixels(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox.#set_RightMargin(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.RichTextBox.#WmReflectNotify(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SaveFileDialog.#PromptUserIfAppropriate(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Screen.#.ctor(System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Screen.#get_AllScreens()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SendKeys.#InstallHook()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SendKeys.#TestHook()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SplitContainer.#DrawSplitHelper(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SplitContainer.#OnKeyUp(System.Windows.Forms.KeyEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SplitContainer.#RepaintSplitterRect()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Splitter.#DrawSplitHelper(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar+ControlToolTip.#AddTool(System.Windows.Forms.StatusBar+ControlToolTip+Tool)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar+ControlToolTip.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar+ControlToolTip.#get_IsHandleCreated()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar+ControlToolTip.#RemoveTool(System.Windows.Forms.StatusBar+ControlToolTip+Tool)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar+ControlToolTip.#UpdateTool(System.Windows.Forms.StatusBar+ControlToolTip+Tool)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar.#get_SizeGripWidth()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBar.#WmDrawItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusBarPanel.#GetContentsWidth(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusStrip.#get_ShowSizingGrip()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StatusStrip.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StringSource.#.ctor(System.String[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SysInfoForm.#GetSwitchesFromLoadedAssemblies()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SysInfoForm.#InitializeComponent()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SysInfoForm.#PopulateAssemblyInfo()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SysInfoForm.#SaveBugReport(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SysInfoForm.#WriteBugReport(System.IO.TextWriter)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SystemInformation.#get_MouseWheelPresent()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SystemInformation.#get_MouseWheelScrollLines()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.SystemInformation.#get_UserInteractive()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TabControl.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TabControl.#SetToolTip(System.Windows.Forms.ToolTip,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TabControl.#WmReflectDrawItem(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TableLayoutPanel.#OnPaintBackground(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextBox.#WmPrint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextBoxBase.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextBoxRenderer.#DrawBackground(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.TextBoxState)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Point,System.Drawing.Color,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#DrawText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Rectangle,System.Drawing.Color,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.Drawing.IDeviceContext,System.String,System.Drawing.Font,System.Drawing.Size,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.String,System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.String,System.Drawing.Font,System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TextRenderer.#MeasureText(System.String,System.Drawing.Font,System.Drawing.Size,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ThreadExceptionDialog.#.ctor(System.Exception)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#EnsureHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#GetInvokeRequired(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#get_IsTimerRunning()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#StartTimer(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#StopTimer(System.Boolean,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Timer+TimerNativeWindow.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolBar.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolBar.#SetToolTip(System.Windows.Forms.ToolTip)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolBar.#WmNotifyDropDown(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolBarButton.#get_Width()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip+RestoreFocusMessageFilter.#PreFilterMessage(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip+RestoreFocusMessageFilter.#RestoreFocusInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#ClearAllSelectionsExcept(System.Windows.Forms.ToolStripItem)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#Dispose(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#GetMeasurementGraphics()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#get_DropDownOwnerWindow()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#OnLeave(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#OnPaintBackground(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#PrintToMetaFileRecursive(System.Runtime.InteropServices.HandleRef,System.IntPtr,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#RestoreFocus()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#RestoreFocusInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#SnapFocus(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStrip.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripButton.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripComboBox+ToolStripComboBoxControl+ToolStripComboBoxFlatComboAdapter.#DrawFlatComboDropDown(System.Windows.Forms.ComboBox,System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripComboBox.#CreateControlInstance()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripComboBox.#OnDropDown(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripComboBox.#OnDropDownClosed(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripComboBox.#ShouldSerializeFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripDropDown.#ReparentToDropDownOwnerWindow()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripDropDown.#SetVisibleCore(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripDropDownButton.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripDropDownMenu.#CalculateInternalLayoutMetrics()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripHighContrastRenderer.#OnRenderItemImage(System.Windows.Forms.ToolStripItemImageRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripHighContrastRenderer.#RenderToolStripBackgroundInternal(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#Animate(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#GetTextSize()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#SetOwner(System.Windows.Forms.ToolStrip)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#set_Image(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#set_ImageTransparentColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#ShouldSerializeImage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#ShouldSerializeImageIndex()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItem.#ShouldSerializeImageKey()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItemImageRenderEventArgs.#.ctor(System.Drawing.Graphics,System.Windows.Forms.ToolStripItem,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripItemInternalLayout.#CommonLayoutOptions()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripLabel.#PaintText(System.Drawing.Graphics)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter+HostedWindowsFormsMessageHook.#InstallMessageHook()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter+HostedWindowsFormsMessageHook.#MessageHookProc(System.Int32,System.IntPtr,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter.#EnterMenuModeCore()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter.#ExitMenuModeCore()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter.#PreFilterMessage(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager+ModalMenuFilter.#SetActiveToolStripCore(System.Windows.Forms.ToolStrip)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager.#IsOnSameWindow(System.Windows.Forms.Control,System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager.#ProcessMenuKey(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager.#ProcessShortcut(System.Windows.Forms.Message&,System.Windows.Forms.Keys)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItem.#.ctor(System.IntPtr,System.Int32,System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItem.#Clone()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItem.#GetShortcutTextSize()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItem.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItemInternalLayout.#get_CheckRectangle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripMenuItemInternalLayout.#get_ImageRectangle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripPanel+FeedbackRectangle+FeedbackDropDown.#.ctor(System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalLowResolutionRenderer.#RenderToolStripBorderInternal(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#FillWithDoubleGradient(System.Drawing.Color,System.Drawing.Color,System.Drawing.Color,System.Drawing.Graphics,System.Drawing.Rectangle,System.Int32,System.Int32,System.Drawing.Drawing2D.LinearGradientMode,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#GetTransparentRegion(System.Windows.Forms.ToolStrip)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#OnRenderButtonBackground(System.Windows.Forms.ToolStripItemRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#OnRenderGrip(System.Windows.Forms.ToolStripGripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#OnRenderMenuItemBackground(System.Windows.Forms.ToolStripItemRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#OnRenderSplitButtonBackground(System.Windows.Forms.ToolStripItemRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#OnRenderToolStripBorder(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderBackgroundGradient(System.Drawing.Graphics,System.Windows.Forms.Control,System.Drawing.Color,System.Drawing.Color,System.Windows.Forms.Orientation)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderCheckBackground(System.Windows.Forms.ToolStripItemImageRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderCheckedButtonFill(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderItemInternal(System.Windows.Forms.ToolStripItemRenderEventArgs,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderOverflowBackground(System.Windows.Forms.ToolStripItemRenderEventArgs,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderOverflowButtonEffectsOverBorder(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderPressedButtonFill(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderPressedGradient(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderSelectedButtonFill(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderSeparatorInternal(System.Drawing.Graphics,System.Windows.Forms.ToolStripItem,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderToolStripCurve(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderToolStripDropDownBackground(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripProfessionalRenderer.#RenderToolStripDropDownBorder(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripRenderer.#CreateDisabledImage(System.Drawing.Image)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripRenderer.#OnRenderArrow(System.Windows.Forms.ToolStripArrowRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripRenderer.#OnRenderItemText(System.Windows.Forms.ToolStripItemTextRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripScrollButton.#GetPreferredSize(System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripScrollButton.#get_DownImage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripScrollButton.#get_UpImage()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripSplitButton.#get_SplitButtonButton()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripSplitButton.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripSystemRenderer.#FillBackground(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripSystemRenderer.#GetPen(System.Drawing.Color,System.Drawing.Pen&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripSystemRenderer.#OnRenderToolStripBorder(System.Windows.Forms.ToolStripRenderEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl.#InvalidateNonClient()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl.#OnUserPreferenceChanged(System.Object,Microsoft.Win32.UserPreferenceChangedEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl.#WmNCPaint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripTextBox.#ShouldSerializeFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#DestroyRegion(System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#GetDelayTime(System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#GetHandleCreated()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#Hide(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#IsWindowActive(System.Windows.Forms.IWin32Window)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#Reposition(System.Drawing.Point,System.Drawing.Size)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#SetDelayTime(System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#SetTool(System.Windows.Forms.IWin32Window,System.String,System.Windows.Forms.ToolTip+TipInfo+Type,System.Drawing.Point)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#SetToolInfo(System.Windows.Forms.Control,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#SetToolTipInternal(System.Windows.Forms.Control,System.Windows.Forms.ToolTip+TipInfo)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#SetTrackPosition(System.Int32,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#set_Active(System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#set_BackColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#set_ForeColor(System.Drawing.Color)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#set_ToolTipIcon(System.Windows.Forms.ToolTipIcon)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#set_ToolTipTitle(System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmMouseActivate(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmMove()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmPop()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmShow()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmWindowFromPoint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmWindowPosChanged()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WmWindowPosChanging(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TrackBar.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#CreateHandle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#CustomDraw(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#OnHandleCreated(System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#SetToolTip(System.Windows.Forms.ToolTip,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#set_StateImageList(System.Windows.Forms.ImageList)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#ShowContextMenu(System.Windows.Forms.TreeNode)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#StateImageListChangedHandle(System.Object,System.EventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.TreeView.#WmPrint(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase+UpDownButtons.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase+UpDownButtons.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase+UpDownEdit.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase.#OnMouseUp(System.Windows.Forms.MouseEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase.#OnPaint(System.Windows.Forms.PaintEventArgs)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.UpDownBase.#WndProc(System.Windows.Forms.Message&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawEdge(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.Edges,System.Windows.Forms.VisualStyles.EdgeStyle,System.Windows.Forms.VisualStyles.EdgeEffects)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawImage(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.ImageList,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawParentBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#DrawText(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.String,System.Boolean,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetBackgroundContentRectangle(System.Drawing.IDeviceContext,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetBackgroundExtent(System.Drawing.IDeviceContext,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetBackgroundRegion(System.Drawing.IDeviceContext,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetFont(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.FontProperty)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetMargins(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.MarginProperty)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetPartSize(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Windows.Forms.VisualStyles.ThemeSizeType)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetPartSize(System.Drawing.IDeviceContext,System.Windows.Forms.VisualStyles.ThemeSizeType)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetTextExtent(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.String,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetTextExtent(System.Drawing.IDeviceContext,System.String,System.Windows.Forms.TextFormatFlags)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#GetTextMetrics(System.Drawing.IDeviceContext)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#HitTestBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.Drawing.Point,System.Windows.Forms.VisualStyles.HitTestOptions)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.VisualStyles.VisualStyleRenderer.#HitTestBackground(System.Drawing.IDeviceContext,System.Drawing.Rectangle,System.IntPtr,System.Drawing.Point,System.Windows.Forms.VisualStyles.HitTestOptions)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowser.#get_Version()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowserBase.#FindContainerControlInternal()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowserBase.#TransitionFromPassiveToLoaded()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowserHelper.#get_LogPixelsX()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowserHelper.#get_LogPixelsY()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WebBrowserSiteBase.#System.Windows.Forms.UnsafeNativeMethods+IOleInPlaceSite.GetWindow()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.WindowsFormsSynchronizationContext.#.ctor()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComboBox.#InitializeDCForWmCtlColor(System.IntPtr,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.Com2AboutBoxPropertyDescriptor+AboutBoxUITypeEditor.#EditValue(System.ComponentModel.ITypeDescriptorContext,System.IServiceProvider,System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.Com2PropertyDescriptor.#GetNativeValue(System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.Com2PropertyPageUITypeEditor.#ShowPropertyPage(System.String,System.Object,System.Int32,System.Guid,System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.Com2TypeInfoProcessor.#FindTypeInfo(System.Object,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.Com2TypeInfoProcessor.#GetNameDispId(System.Windows.Forms.UnsafeNativeMethods+IDispatch)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor.#GetPropertyValue(System.Object,System.Int32,System.Object[])")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXFontMarshaler.#MarshalManagedToNative(System.Object)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Control+ActiveXFontMarshaler.#MarshalNativeToManaged(System.IntPtr)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGrid.#get_DefaultHeaderForePen()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#get_DefaultTextBorderPen()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#OnGridFontChanged()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#PaintIcon(System.Drawing.Graphics,System.Drawing.Rectangle,System.Drawing.Bitmap)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridCaption.#PaintText(System.Drawing.Graphics,System.Drawing.Rectangle,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#GetTableBoxWidth(System.Drawing.Graphics,System.Drawing.Font)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintBitmap(System.Drawing.Graphics,System.Drawing.Bitmap,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintColumns(System.Drawing.Graphics,System.Drawing.Rectangle,System.Windows.Forms.DataGridState,System.Drawing.Font,System.Boolean,System.Int32[],System.Int32[],System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintDownButton(System.Drawing.Graphics,System.Drawing.Rectangle)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridParentRows.#PaintText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String,System.Drawing.Font,System.Boolean,System.Boolean)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridTableStyle.#get_DefaultHeaderForePen()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#RecreateHandleCore()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#set_MdiParentInternal(System.Windows.Forms.Form)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ListView.#GetListHeaderFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.MdiClient+ControlCollection.#Add(System.Windows.Forms.Control)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Menu.#get_Handle()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StringSorter.#.ctor(System.Globalization.CultureInfo,System.String[],System.Object[],System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StringSorter.#Compare(System.String,System.String)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.StringSorter.#Compare(System.String,System.String,System.Int32)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolStripManager.#get_DefaultFont()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ToolTip.#GetWindowFromPoint(System.Drawing.Point,System.Boolean&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ButtonInternal.CheckBoxBaseAdapter.#GetCheckBoxImage(System.Drawing.Color,System.Drawing.Rectangle,System.Drawing.Color&,System.Drawing.Bitmap&)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_LeftArrowBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_LeftArrowStarBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_PencilLTRBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_PencilRTLBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_RightArrowBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_RightArrowStarBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewRowHeaderCell.#get_StarBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewCell.#get_ErrorBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageCell.#get_ErrorBitmap()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.DataGridViewImageCell.#get_ErrorIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.ErrorProvider.#get_DefaultIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#get_DefaultIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Form.#get_DefaultRestrictedIcon()")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SelectFont(System.Windows.Forms.Internal.WindowsFont)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.DeviceContext.#SelectObject(System.IntPtr,System.Windows.Forms.Internal.GdiObjectType)")] +[module: SuppressMessage("Microsoft.SideBySide", "CA710:ExposedResourcesMustBeConsumed", Scope = "member", Target = "System.Windows.Forms.Internal.WindowsPen.#get_HPen()")] + +namespace System.Windows.Forms { +} diff --git a/WindowsForms/Managed/System/WinForms/GDI/TextFormatFlags.cs b/WindowsForms/Managed/System/WinForms/GDI/TextFormatFlags.cs new file mode 100644 index 000000000..8287dd7e7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GDI/TextFormatFlags.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Windows.Forms.Internal; + using System.Diagnostics.CodeAnalysis; + + /// + /// TextFormatFlags enum defining options for drawing/measuring text in + /// TextRenderer. + /// + /// Note: This is a public enum wrapping the internal IntTextFormatFlags + /// defined in the System.Windows.Forms.Internal namespace, we need to do this to + /// be able to compile the internal one into different assemblies w/o + /// creating any conflict/dependency on public namespaces. + /// Additionally, TextFormatFlags has some extra values. + /// + [Flags] + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum TextFormatFlags + { + Bottom = IntTextFormatFlags.Bottom, + // NOTE: This flag is used for measuring text and TextRenderer has methods for doing that + // so we don't expose it to avoid confusion. + // CalculateRectangle = IntTextFormatFlags.CalculateRectangle, + EndEllipsis = IntTextFormatFlags.EndEllipsis, + ExpandTabs = IntTextFormatFlags.ExpandTabs, + ExternalLeading = IntTextFormatFlags.ExternalLeading, + Default = IntTextFormatFlags.Default, + HidePrefix = IntTextFormatFlags.HidePrefix, + HorizontalCenter = IntTextFormatFlags.HorizontalCenter, + Internal = IntTextFormatFlags.Internal, + Left = IntTextFormatFlags.Left, // default + ModifyString = IntTextFormatFlags.ModifyString, + NoClipping = IntTextFormatFlags.NoClipping, + NoPrefix = IntTextFormatFlags.NoPrefix, + NoFullWidthCharacterBreak = IntTextFormatFlags.NoFullWidthCharacterBreak, + PathEllipsis = IntTextFormatFlags.PathEllipsis, + PrefixOnly = IntTextFormatFlags.PrefixOnly, + Right = IntTextFormatFlags.Right, + RightToLeft = IntTextFormatFlags.RightToLeft, + SingleLine = IntTextFormatFlags.SingleLine, + // NOTE: TextRenderer does not expose a way to set the tab stops (VSW#481267). Observe that ExapandTabs is available. + // TabStop = IntTextFormatFlags.TabStop, + TextBoxControl = IntTextFormatFlags.TextBoxControl, + Top = IntTextFormatFlags.Top, // default + VerticalCenter = IntTextFormatFlags.VerticalCenter, + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] //Not a compound word + WordBreak = IntTextFormatFlags.WordBreak, + WordEllipsis = IntTextFormatFlags.WordEllipsis, + + /// + /// The following flags are exclusive of TextRenderer (no Windows native flags) + /// and apply to methods receiving a Graphics as the IDeviceContext object, and + /// specify whether to reapply clipping and coordintate transformations to the hdc + /// obtained from the Graphics object, which returns a clean hdc. + /// + PreserveGraphicsClipping = 0x01000000, + PreserveGraphicsTranslateTransform = 0x02000000, + + /// + /// Adds padding related to the drawing binding box, computed according to the font size. + /// Match the System.Internal.GDI.TextPaddingOptions. + /// + GlyphOverhangPadding = 0x00000000, // default. + NoPadding = 0x10000000, + LeftAndRightPadding = 0x20000000 + } +} diff --git a/WindowsForms/Managed/System/WinForms/GDI/TextRenderer.cs b/WindowsForms/Managed/System/WinForms/GDI/TextRenderer.cs new file mode 100644 index 000000000..0c1e9ac3e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GDI/TextRenderer.cs @@ -0,0 +1,354 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms +{ + using System.Internal; + using System; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Diagnostics; + + + /// + /// + /// + /// This class provides API for drawing GDI text. + /// + /// + public sealed class TextRenderer + { + //cannot instantiate + private TextRenderer() + { + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Point pt, Color foreColor) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wg.DrawText(text, wf, pt, foreColor); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wg.DrawText(text, wf, pt, foreColor, backColor); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, flags )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wgr.WindowsGraphics.DrawText(text, wf, pt, foreColor, GetIntTextFormatFlags(flags)); + } + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, flags )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wgr.WindowsGraphics.DrawText(text, wf, pt, foreColor, backColor, GetIntTextFormatFlags(flags)); + } + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wg.DrawText(text, wf, bounds, foreColor); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + wg.DrawText(text, wf, bounds, foreColor, backColor); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, flags )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont( font, fontQuality )) { + wgr.WindowsGraphics.DrawText( text, wf, bounds, foreColor, GetIntTextFormatFlags( flags ) ); + } + } + } + + /// + public static void DrawText(IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, flags )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont( font, fontQuality )) { + wgr.WindowsGraphics.DrawText(text, wf, bounds, foreColor, backColor, GetIntTextFormatFlags(flags)); + } + } + } + + private static IntTextFormatFlags GetIntTextFormatFlags(TextFormatFlags flags) + { + if( ((uint)flags & WindowsGraphics.GdiUnsupportedFlagMask) == 0 ) + { + return (IntTextFormatFlags) flags; + } + + // Clear TextRenderer custom flags. + IntTextFormatFlags windowsGraphicsSupportedFlags = (IntTextFormatFlags) ( ((uint)flags) & ~WindowsGraphics.GdiUnsupportedFlagMask ); + + return windowsGraphicsSupportedFlags; + } + + /// MeasureText wrappers. + + public static Size MeasureText(string text, Font font ) + { + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font)) { + return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf); + } + } + + public static Size MeasureText(string text, Font font, Size proposedSize ) + { + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font)) { + return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, WindowsGraphicsCacheManager.GetWindowsFont(font), proposedSize); + } + } + + public static Size MeasureText(string text, Font font, Size proposedSize, TextFormatFlags flags ) + { + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font)) { + return WindowsGraphicsCacheManager.MeasurementGraphics.MeasureText(text, wf, proposedSize, GetIntTextFormatFlags(flags)); + } + } + + public static Size MeasureText(IDeviceContext dc, string text, Font font) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + return wg.MeasureText(text, wf); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + public static Size MeasureText(IDeviceContext dc, string text, Font font, Size proposedSize ) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + IntPtr hdc = dc.GetHdc(); + + try + { + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + return wg.MeasureText(text, wf, proposedSize); + } + } + } + finally + { + dc.ReleaseHdc(); + } + } + + public static Size MeasureText(IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags ) + { + if (dc == null) + { + throw new ArgumentNullException("dc"); + } + if (string.IsNullOrEmpty(text)) + { + return Size.Empty; + } + WindowsFontQuality fontQuality = WindowsFont.WindowsFontQualityFromTextRenderingHint(dc as Graphics); + + using (WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper(dc, flags)) + { + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(font, fontQuality)) { + return wgr.WindowsGraphics.MeasureText(text, wf, proposedSize, GetIntTextFormatFlags(flags)); + } + } + } + + + internal static Color DisabledTextColor(Color backColor) { + if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) { + return SystemColors.GrayText; + } + + //Theme specs -- if the backcolor is darker than Control, we use + // ControlPaint.Dark(backcolor). Otherwise we use ControlDark. + // see VS#357226 + Color disabledTextForeColor = SystemColors.ControlDark; + if (ControlPaint.IsDarker(backColor, SystemColors.Control)) { + disabledTextForeColor = ControlPaint.Dark(backColor); + } + return disabledTextForeColor; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/GDI/WindowsGraphicsWrapper.cs b/WindowsForms/Managed/System/WinForms/GDI/WindowsGraphicsWrapper.cs new file mode 100644 index 000000000..9d52265eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GDI/WindowsGraphicsWrapper.cs @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms +{ + using System.Internal; + using System; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Diagnostics; + using System.Runtime.Versioning; + + /// + /// This class wrapps a WindowsGraphics and is provided to be able to manipulate WindowsGraphics objects + /// created from a Graphics object in the same way as one created from any other IDeviceContext object, + /// which could be a custom one. + /// This class was designed to help TextRenderer determine how to create the underlying WindowsGraphics. + /// + internal sealed class WindowsGraphicsWrapper : IDisposable + { + IDeviceContext idc; + WindowsGraphics wg; + + /// + /// Constructor that determines how to create the WindowsGraphics, there are three posible cases + /// for the IDeviceContext object type: + /// 1. It is a Graphics object: In this case we need to check the TextFormatFlags to determine whether + /// we need to re-apply some of the Graphics properties to the WindowsGraphics, if so we call + /// WindowsGraphics.FromGraphics passing the corresponding flags. If not, we treat it as a custom + /// IDeviceContext (see below). + /// 2. It is a WindowsGraphics object: + /// In this case we just need to use the wg directly and be careful not to dispose of it since + /// it is owned by the caller. + /// 3. It is a custom IDeviceContext object: + /// In this case we create the WindowsGraphics from the native DC by calling IDeviceContext.GetHdc, + /// on dispose we need to call IDeviceContext.ReleaseHdc. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public WindowsGraphicsWrapper( IDeviceContext idc, TextFormatFlags flags) + { + if( idc is Graphics ) + { + ApplyGraphicsProperties properties = ApplyGraphicsProperties.None; + + if( (flags & TextFormatFlags.PreserveGraphicsClipping) != 0) + { + properties |= ApplyGraphicsProperties.Clipping; + } + + if( (flags & TextFormatFlags.PreserveGraphicsTranslateTransform) != 0) + { + properties |= ApplyGraphicsProperties.TranslateTransform; + } + + // Create the WindowsGraphics from the Grahpics object only if Graphics properties need + // to be reapplied to the DC wrapped by the WindowsGraphics. + if( properties != ApplyGraphicsProperties.None ) + { + this.wg = WindowsGraphics.FromGraphics( idc as Graphics, properties); + } + } + else + { + // If passed-in IDeviceContext object is a WindowsGraphics we can use it directly. + this.wg = idc as WindowsGraphics; + + if( this.wg != null ) + { + // In this case we cache the idc to compare it against the wg in the Dispose method to avoid + // disposing of the wg. + this.idc = idc; + } + } + + if( this.wg == null ) + { + // The IDeviceContext object is not a WindowsGraphics, or it is a custom IDeviceContext, or + // it is a Graphics object but we did not need to re-apply Graphics propertiesto the hdc. + // So create the WindowsGraphics from the hdc directly. + // Cache the IDC so the hdc can be released on dispose. + this.idc = idc; + this.wg = WindowsGraphics.FromHdc( idc.GetHdc() ); + } + + // Set text padding on the WindowsGraphics (if any). + if( (flags & TextFormatFlags.LeftAndRightPadding) != 0 ) + { + wg.TextPadding = TextPaddingOptions.LeftAndRightPadding; + } + else if ((flags & TextFormatFlags.NoPadding) != 0 ) + { + wg.TextPadding = TextPaddingOptions.NoPadding; + } + // else wg.TextPadding = TextPaddingOptions.GlyphOverhangPadding - the default value. + } + + public WindowsGraphics WindowsGraphics + { + get + { + Debug.Assert( this.wg != null, "WindowsGraphics is null." ); + return this.wg; + } + } + + ~WindowsGraphicsWrapper() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Dispose( bool disposing ) + { + Debug.Assert( disposing, "We should always dispose of this guy and not let GC do it for us!" ); + + if( this.wg != null ) + { + // We need to dispose of the WindowsGraphics if it is created by this class only, if the IDeviceContext is + // a WindowsGraphics object we must not dispose of it since it is owned by the caller. + if( this.wg != this.idc ) + { + // resets the hdc and disposes of the internal Graphics (if inititialized from one) which releases the hdc. + this.wg.Dispose(); + + if( this.idc != null ) // not initialized from a Graphics idc. + { + this.idc.ReleaseHdc(); + } + } + + this.idc = null; + this.wg = null; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/GetChildAtPointSkip.cs b/WindowsForms/Managed/System/WinForms/GetChildAtPointSkip.cs new file mode 100644 index 000000000..7a2ec17aa --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GetChildAtPointSkip.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms; + using System.ComponentModel; + + /// + [ + Flags, + ] + [SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] // PM reviewed the enum name + public enum GetChildAtPointSkip { + /// + None = 0x0000, + /// + Invisible = 0x0001, + /// + Disabled = 0x0002, + /// + Transparent = 0x0004 + } +} + diff --git a/WindowsForms/Managed/System/WinForms/GiveFeedbackEvent.cs b/WindowsForms/Managed/System/WinForms/GiveFeedbackEvent.cs new file mode 100644 index 000000000..edc5384d0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GiveFeedbackEvent.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + //=--------------------------------------------------------------------------= + // GiveFeedbackEventArgs.cs + //=--------------------------------------------------------------------------= + // Copyright (c) 1997 Microsoft Corporation. All Rights Reserved. + // + // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF + // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO + // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A + // PARTICULAR PURPOSE. + //=--------------------------------------------------------------------------= + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class GiveFeedbackEventArgs : EventArgs { + private readonly DragDropEffects effect; + private bool useDefaultCursors; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public GiveFeedbackEventArgs(DragDropEffects effect, bool useDefaultCursors) { + this.effect = effect; + this.useDefaultCursors = useDefaultCursors; + } + + /// + /// + /// + /// Gets the type of drag-and-drop operation. + /// + /// + public DragDropEffects Effect { + get { + return effect; + } + } + + /// + /// + /// + /// Gets or sets a + /// value + /// indicating whether a default pointer is used. + /// + /// + public bool UseDefaultCursors { + get { + return useDefaultCursors; + } + set { + useDefaultCursors = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/GiveFeedbackEventHandler.cs b/WindowsForms/Managed/System/WinForms/GiveFeedbackEventHandler.cs new file mode 100644 index 000000000..332eaba29 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GiveFeedbackEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that handles the event + /// of a . + /// + /// + public delegate void GiveFeedbackEventHandler(object sender, GiveFeedbackEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/GridItem.cs b/WindowsForms/Managed/System/WinForms/GridItem.cs new file mode 100644 index 000000000..2ba01b087 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GridItem.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// Representaion of one row item in the PropertyGrid. These items represent the + /// hierarchy of the grid's "tree-like" view and can be used to get information about + /// the grid's state and contents. + /// These objects should not be cached because they represent a snapshot of the PropertyGrid's state + /// and may be disposed by grid activity. The PropertyGrid often recretates these objects internally even if + /// it doesn't appear to change to the user. + /// + public abstract class GridItem { + + private object userData; + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Retrieves the child GridItems, if any, of this GridItem + /// + public abstract GridItemCollection GridItems { + get; + } + + /// + /// + /// Retrieves type of this GridItem, as a value from System.Windows.Forms.GridItemType + /// + public abstract GridItemType GridItemType { + get; + } + + /// + /// + /// Retrieves the text label of this GridItem. This may be different from the actual PropertyName. + /// For GridItemType.Property GridItems, retrieve the PropertyDescriptor and check its Name property. + /// + public abstract string Label { + get; + } + + /// + /// + /// Retrieves parent GridItem of this GridItem, if any + /// + public abstract GridItem Parent { + get; + } + + /// + /// + /// If this item is a GridItemType.Property GridItem, this retreives the System.ComponentModel.PropertyDescriptor that is + /// associated with this GridItem. This can be used to retrieve infomration such as property Type, Name, or TypeConverter. + /// + public abstract PropertyDescriptor PropertyDescriptor { + get; + } + + /// + /// + /// Retrieves the current Value of this grid Item. This may be null. + /// + public abstract object Value { + get; + // note: we don't do set because of the value class semantics, etc. + } + + /// + /// + /// Retreives whether the given property is expandable. + /// + public virtual bool Expandable { + get { + return false; + } + } + + /// + /// + /// Retreives or sets whether the GridItem is in an expanded state. + /// + public virtual bool Expanded { + get { + return false; + } + set { + throw new NotSupportedException(SR.GetString(SR.GridItemNotExpandable)); + } + } + + /// + /// + /// Attempts to select this GridItem in the PropertyGrid. + /// + public abstract bool Select(); + } +} diff --git a/WindowsForms/Managed/System/WinForms/GridItemCollection.cs b/WindowsForms/Managed/System/WinForms/GridItemCollection.cs new file mode 100644 index 000000000..5a3c6d489 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GridItemCollection.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Collections; + + /// + /// + /// A read-only collection of GridItem objects + /// + public class GridItemCollection : ICollection { + + /// + /// + /// [To be supplied.] + /// + public static GridItemCollection Empty = new GridItemCollection(new GridItem[0]); + + internal GridItem[] entries; + + internal GridItemCollection(GridItem[] entries) { + if (entries == null) { + this.entries = new GridItem[0]; + } + else { + this.entries = entries; + } + } + + /// + /// + /// Retrieves the number of member attributes. + /// + public int Count { + get { + return entries.Length; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + /// Retrieves the member attribute with the specified index. + /// + public GridItem this[int index] { + get { + return entries[index]; + } + } + + /// + /// + /// [To be supplied.] + /// + public GridItem this[string label]{ + get { + foreach(GridItem g in entries) { + if (g.Label == label) { + return g; + } + } + return null; + } + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (entries.Length > 0) { + System.Array.Copy(entries, 0, dest, index, entries.Length); + } + } + /// + /// + /// Creates and retrieves a new enumerator for this collection. + /// + public IEnumerator GetEnumerator() { + return entries.GetEnumerator(); + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/GridItemType.cs b/WindowsForms/Managed/System/WinForms/GridItemType.cs new file mode 100644 index 000000000..d85f187e4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GridItemType.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// + /// [To be supplied.] + /// + public enum GridItemType { + /// + /// + /// [To be supplied.] + /// + Property, + /// + /// + /// [To be supplied.] + /// + Category, + /// + /// + /// [To be supplied.] + /// + ArrayValue, + /// + /// + /// [To be supplied.] + /// + Root + } + +} diff --git a/WindowsForms/Managed/System/WinForms/GroupBox.cs b/WindowsForms/Managed/System/WinForms/GroupBox.cs new file mode 100644 index 000000000..09008fea0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GroupBox.cs @@ -0,0 +1,855 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security.Permissions; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.Layout; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Encapsulates + /// a standard Windows(r) group + /// box. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("Enter"), + DefaultProperty("Text"), + Designer("System.Windows.Forms.Design.GroupBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionGroupBox) + ] + public class GroupBox : Control { + int fontHeight = -1; + Font cachedFont; + FlatStyle flatStyle = FlatStyle.Standard; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public GroupBox() : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetStyle(ControlStyles.ContainerControl, true); + SetStyle(ControlStyles.SupportsTransparentBackColor | + ControlStyles.UserPaint | + ControlStyles.ResizeRedraw, OwnerDraw); + + SetStyle(ControlStyles.Selectable, false); + TabStop = false; + } + + /// + /// + /// + /// + /// Gets or sets a value indicating whether the control will allow drag and + /// drop operations and events to be used. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + + /// + /// + /// Override to re-expose AutoSize. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRDescription(SR.ControlAutoSizeModeDescr), + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true) + ] + public AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + if(ParentInternal != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(ParentInternal.LayoutEngine == DefaultLayout.Instance) { + ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize); + } + } + } + } + + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + if (!OwnerDraw) { + cp.ClassName = "BUTTON"; + cp.Style |= NativeMethods.BS_GROUPBOX; + } + else { + // if we swap back to a different flat style + // we need to reset these guys. + cp.ClassName = null; + cp.Style &= ~NativeMethods.BS_GROUPBOX; + } + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + + return cp; + } + } + + /// + /// Set the default Padding to 3 so that it is consistent with Everett + /// + protected override Padding DefaultPadding { + get { + return new Padding(3); + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(200, 100); + } + } + + /// + /// + /// + /// + /// Gets a rectangle that represents the + /// dimensions of the + /// . + /// + /// + public override Rectangle DisplayRectangle { + get { + Size size = ClientSize; + + if (fontHeight == -1) { + fontHeight = (int)Font.Height; + cachedFont = Font; + } + else if (!object.ReferenceEquals(cachedFont, Font)) { + // Must also cache font identity here because + // we need to provide an accurate DisplayRectangle + // picture even before the OnFontChanged event bubbles + // through. + fontHeight = (int)Font.Height; + cachedFont = Font; + } + + + //for efficiency, so that we don't need to read property store four times + Padding padding = Padding; + return new Rectangle(padding.Left, fontHeight + padding.Top, Math.Max(size.Width - padding.Horizontal, 0), Math.Max(size.Height - fontHeight - padding.Vertical, 0)); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Standard), + SRDescription(SR.ButtonFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { + return flatStyle; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + + if (flatStyle != value) { + + bool originalOwnerDraw = OwnerDraw; + flatStyle = value; + + // In CreateParams, we pick our class style based on OwnerDraw + // if this has changed we need to recreate + bool needRecreate = (OwnerDraw != originalOwnerDraw); + + SetStyle(ControlStyles.ContainerControl, true); + + SetStyle(ControlStyles.SupportsTransparentBackColor | + ControlStyles.UserPaint | + ControlStyles.ResizeRedraw | + ControlStyles.UserMouse, OwnerDraw); + + if (needRecreate) { + RecreateHandle(); + } + else { + Refresh(); + } + + + } + } + } + + private bool OwnerDraw { + get { + return FlatStyle != FlatStyle.System; + } + } + + /// + /// + /// + /// + /// Gets or sets a value indicating whether the user may + /// press the TAB key to give the focus to the + /// . + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true) + ] + public override string Text { + get{ + return base.Text; + } + set { + // the GroupBox controls immediately draws when teh WM_SETTEXT comes through, but + // does so in the wrong font, so we suspend that behavior, and then + // invalidate. + bool suspendRedraw = this.Visible; + try { + if (suspendRedraw && IsHandleCreated) { + SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); + } + base.Text = value; + } + finally { + if (suspendRedraw && IsHandleCreated) { + SendMessage(NativeMethods.WM_SETREDRAW, 1, 0); + } + } + Invalidate(true); + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public bool UseCompatibleTextRendering { + get{ + return base.UseCompatibleTextRenderingInt; + } + set{ + base.UseCompatibleTextRenderingInt = value; + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same + /// value if the child control supports it. + /// + internal override bool SupportsUseCompatibleTextRendering { + get { + return true; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseDown { + add { + base.MouseDown += value; + } + remove { + base.MouseDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseUp { + add { + base.MouseUp += value; + } + remove { + base.MouseUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseMove { + add { + base.MouseMove += value; + } + remove { + base.MouseMove -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler MouseEnter { + add { + base.MouseEnter += value; + } + remove { + base.MouseEnter -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler MouseLeave { + add { + base.MouseLeave += value; + } + remove { + base.MouseLeave -= value; + } + } + + /// + /// + protected override void OnPaint(PaintEventArgs e) { + + + // BACKCOMPAT HACK: + // Why the Height/Width >= 10 check? This is because uxtheme doesn't seem to handle those cases + // similar to what we do for the non-themed case, so if someone is using the groupbox as a + // separator, their app will look weird in Whidbey. We render the old way in these cases. + if (Application.RenderWithVisualStyles && Width >= 10 && Height >= 10) { + GroupBoxState gbState = Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled; + TextFormatFlags textFlags = TextFormatFlags.Default | TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak | TextFormatFlags.PreserveGraphicsTranslateTransform | TextFormatFlags.PreserveGraphicsClipping; + + if (!ShowKeyboardCues) { + textFlags |= TextFormatFlags.HidePrefix; + } + + if (RightToLeft == RightToLeft.Yes) { + textFlags |= (TextFormatFlags.Right | TextFormatFlags.RightToLeft); + } + + // We only pass in the text color if it is explicitly set, else we let the renderer use the + // color specified by the theme. This is a temporary workaround till we find a good + // solution for the "default theme color" issue. + if (ShouldSerializeForeColor() || this.Enabled == false) { + Color textcolor = this.Enabled ? ForeColor : TextRenderer.DisabledTextColor(this.BackColor); + GroupBoxRenderer.DrawGroupBox(e.Graphics, new Rectangle(0, 0, Width, Height), Text, Font, textcolor, textFlags, gbState); + } + else { + GroupBoxRenderer.DrawGroupBox(e.Graphics, new Rectangle(0, 0, Width, Height), Text, Font, textFlags, gbState); + } + } + else { + DrawGroupBox(e); + } + base.OnPaint(e); // raise paint event + } + + private void DrawGroupBox(PaintEventArgs e) { + Graphics graphics = e.Graphics; + Rectangle textRectangle = ClientRectangle; // Max text bounding box passed to drawing methods to support RTL. + + int textOffset = 8; // Offset from the left bound. + + Color backColor = DisabledColor; + + Pen light = new Pen(ControlPaint.Light(backColor, 1.0f)); + Pen dark = new Pen(ControlPaint.Dark(backColor, 0f)); + Size textSize; + + textRectangle.X += textOffset; + textRectangle.Width -= 2 * textOffset; + + try { + if( UseCompatibleTextRendering ){ + using( Brush textBrush = new SolidBrush(ForeColor)){ + using( StringFormat format = new StringFormat() ){ + format.HotkeyPrefix = ShowKeyboardCues ? System.Drawing.Text.HotkeyPrefix.Show : System.Drawing.Text.HotkeyPrefix.Hide; + + // Adjust string format for Rtl controls + + if (RightToLeft == RightToLeft.Yes) { + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + } + + textSize = Size.Ceiling(graphics.MeasureString(Text, Font, textRectangle.Width, format)); + + if (Enabled) { + graphics.DrawString(Text, Font, textBrush, textRectangle, format); + } + else { + ControlPaint.DrawStringDisabled(graphics, Text, Font, backColor, textRectangle, format); + } + } + } + } + else { + using( WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics) ){ + IntTextFormatFlags flags = IntTextFormatFlags.WordBreak | IntTextFormatFlags.TextBoxControl; + + if(!ShowKeyboardCues){ + flags |= IntTextFormatFlags.HidePrefix; + } + + if( RightToLeft == RightToLeft.Yes ){ + flags |= IntTextFormatFlags.RightToLeft; + flags |= IntTextFormatFlags.Right; + } + + + using (WindowsFont wfont = WindowsGraphicsCacheManager.GetWindowsFont(this.Font)) { + textSize = wg.MeasureText(Text, wfont, new Size(textRectangle.Width , int.MaxValue), flags ); + + if( Enabled ) { + wg.DrawText(Text, wfont, textRectangle, ForeColor, flags); + } + else{ + ControlPaint.DrawStringDisabled(wg, Text, Font, backColor, textRectangle, ((TextFormatFlags) flags)); + } + } + } + } + + int textLeft = textOffset; // Left side of binding box (independent on RTL). + + if (RightToLeft == RightToLeft.Yes) + { + textLeft += textRectangle.Width - textSize.Width; + } + + // Math.Min to assure we paint at least a small line. + int textRight = Math.Min( textLeft + textSize.Width, Width - 6); + + int boxTop = FontHeight / 2; + + if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) { + Color boxColor; + if (Enabled) { + boxColor = ForeColor; + } + else { + boxColor = SystemColors.GrayText; + } + bool needToDispose = !boxColor.IsSystemColor; + Pen boxPen = null; + try { + if (needToDispose) { + boxPen = new Pen(boxColor); + } + else { + boxPen = SystemPens.FromSystemColor(boxColor); + } + + // left + graphics.DrawLine(boxPen, 0, boxTop, 0, Height); + //bottom + graphics.DrawLine(boxPen, 0, Height-1, Width, Height-1); + //top-left + graphics.DrawLine(boxPen, 0, boxTop, textLeft, boxTop); + //top-right + graphics.DrawLine(boxPen, textRight, boxTop, Width-1, boxTop); + //right + graphics.DrawLine(boxPen, Width-1, boxTop, Width-1, Height-1); + } + finally { + if (needToDispose && boxPen != null) { + boxPen.Dispose(); + } + } + } + else { + // left + graphics.DrawLine(light, 1, boxTop, 1, Height - 1); + graphics.DrawLine(dark, 0, boxTop, 0, Height - 2); + + // bottom + graphics.DrawLine(light, 0, Height - 1, Width, Height - 1); + graphics.DrawLine(dark, 0, Height - 2, Width - 1, Height - 2); + + // top-left + + graphics.DrawLine(dark, 0, boxTop - 1, textLeft, boxTop - 1); + graphics.DrawLine(light, 1, boxTop, textLeft, boxTop); + + // top-right + graphics.DrawLine(dark, textRight, boxTop - 1, Width - 2, boxTop - 1); + graphics.DrawLine(light, textRight, boxTop, Width - 1, boxTop); + + // right + graphics.DrawLine(light, Width - 1, boxTop - 1, Width - 1, Height - 1); + graphics.DrawLine(dark, Width - 2, boxTop, Width - 2, Height - 2); + } + } + finally { + light.Dispose(); + dark.Dispose(); + } + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + // Translating 0,0 from ClientSize to actual Size tells us how much space + // is required for the borders. + Size borderSize = SizeFromClientSize(Size.Empty); + Size totalPadding = borderSize + new Size(0,fontHeight) + Padding.Size; + + Size prefSize = LayoutEngine.GetPreferredSize(this, proposedSize - totalPadding); + return prefSize + totalPadding; + } + + /// + /// + /// + /// + protected override void OnFontChanged(EventArgs e) { + fontHeight = -1; + cachedFont = null; + Invalidate(); + base.OnFontChanged(e); + } + + /// + /// + /// We use this to process mnemonics and send them on to the first child + /// control. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (IsMnemonic(charCode, Text) && CanProcessMnemonic()) { + // Reviewed: This seems safe, because SelectNextControl does not allow + // you to step out of the current control - it only selects children. With + // this assumption, it is okay to assert here. + IntSecurity.ModifyFocus.Assert(); + try { + SelectNextControl(null, true, true, true, false); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + return true; + } + return false; + } + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + if (factor.Width != 1F && factor.Height != 1F) { + // VSWHIDBEY 471445: make sure when we're scaling by non-unity to clear the font cache + // as the font has likely changed, but we dont know it yet as OnFontChanged has yet to + // be called on us by our parent. + fontHeight = -1; + cachedFont = null; + } + base.ScaleControl(factor, specified); + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Text: " + Text; + } + + /// + /// The Windows group box doesn't erase the background so we do it + /// ourselves here. + /// + /// + private void WmEraseBkgnd(ref Message m) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + SafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rect); + using (Graphics graphics = Graphics.FromHdcInternal(m.WParam)) { + using (Brush b = new SolidBrush(BackColor)) { + graphics.FillRectangle(b, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + } + } + m.Result = (IntPtr)1; + } + + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (OwnerDraw) { + base.WndProc(ref m); + return; + } + + switch (m.Msg) { + case NativeMethods.WM_ERASEBKGND: + case NativeMethods.WM_PRINTCLIENT: + WmEraseBkgnd(ref m); + break; + case NativeMethods.WM_GETOBJECT: + base.WndProc(ref m); + + // Force MSAA to always treat a group box as a custom window. This ensures its child controls + // will always be exposed through MSAA. Reason: When FlatStyle=System, we map down to the Win32 + // "Button" window class to get OS group box rendering; but the OS does not expose the children + // of buttons to MSAA (beacuse it assumes buttons won't have children). + if (unchecked((int)(long)m.LParam) == NativeMethods.OBJID_QUERYCLASSNAMEIDX) { + m.Result = IntPtr.Zero; + } + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new GroupBoxAccessibleObject(this); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class GroupBoxAccessibleObject : ControlAccessibleObject + { + internal GroupBoxAccessibleObject(GroupBox owner) : base(owner) { + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.Grouping; + } + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_GroupControlTypeId; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return true; + } + + return base.GetPropertyValue(propertyID); + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/GroupBoxRenderer.cs b/WindowsForms/Managed/System/WinForms/GroupBoxRenderer.cs new file mode 100644 index 000000000..2b0aa0d94 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/GroupBoxRenderer.cs @@ -0,0 +1,337 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Windows.Forms.Internal; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + + /// + /// + /// + /// This is a rendering class for the GroupBox control. + /// + /// + public sealed class GroupBoxRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement GroupBoxElement = VisualStyleElement.Button.GroupBox.Normal; + private const int textOffset = 8; //MAGIC NUMBER - WHERE DID IT COME FROM? + private const int boxHeaderWidth = 7; // VSWhidbey 514912: The groupbox frame shows 7 pixels before the caption. + private static bool renderMatchingApplicationState = true; + //cannot instantiate + private GroupBoxRenderer() { + } + + /// + /// + /// + /// If this property is true, then the renderer will use the setting from Application.RenderWithVisualStyles to + /// determine how to render. + /// If this property is false, the renderer will always render with visualstyles. + /// + /// + public static bool RenderMatchingApplicationState { + get { + return renderMatchingApplicationState; + } + set { + renderMatchingApplicationState = value; + } + } + + private static bool RenderWithVisualStyles { + get { + return (!renderMatchingApplicationState || Application.RenderWithVisualStyles); + } + } + + /// + /// + /// + /// Returns true if the background corresponding to the given state is partially transparent, else false. + /// + /// + public static bool IsBackgroundPartiallyTransparent(GroupBoxState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + return visualStyleRenderer.IsBackgroundPartiallyTransparent(); + } + else { + return false; //for downlevel, this is false + } + } + + /// + /// + /// + /// This is just a convenience wrapper for VisualStyleRenderer.DrawThemeParentBackground. For downlevel, + /// this isn't required and does nothing. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawParentBackground(Graphics g, Rectangle bounds, Control childControl) { + if (RenderWithVisualStyles) { + InitializeRenderer(0); + visualStyleRenderer.DrawParentBackground(g, bounds, childControl); + } + } + + /// + /// + /// + /// Renders a GroupBox control. + /// + /// + public static void DrawGroupBox(Graphics g, Rectangle bounds, GroupBoxState state) { + if (RenderWithVisualStyles) + DrawThemedGroupBoxNoText(g, bounds, state); + else + DrawUnthemedGroupBoxNoText(g, bounds, state); + } + + /// + /// + /// + /// Renders a GroupBox control. Uses the text color specified by the theme. + /// + /// + public static void DrawGroupBox(Graphics g, Rectangle bounds, string groupBoxText, Font font, GroupBoxState state) { + DrawGroupBox(g, bounds, groupBoxText, font, TextFormatFlags.Top | TextFormatFlags.Left, state); + } + + /// + /// + /// + /// Renders a GroupBox control. + /// + /// + public static void DrawGroupBox(Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, GroupBoxState state) { + DrawGroupBox(g, bounds, groupBoxText, font, textColor, TextFormatFlags.Top | TextFormatFlags.Left, state); + } + + /// + /// + /// + /// Renders a GroupBox control. Uses the text color specified by the theme. + /// + /// + public static void DrawGroupBox(Graphics g, Rectangle bounds, string groupBoxText, Font font, TextFormatFlags flags, GroupBoxState state) { + if (RenderWithVisualStyles) + DrawThemedGroupBoxWithText(g, bounds, groupBoxText, font, DefaultTextColor(state), flags, state); + else + DrawUnthemedGroupBoxWithText(g, bounds, groupBoxText, font, DefaultTextColor(state), flags, state); + } + + /// + /// + /// + /// Renders a GroupBox control. + /// + /// + public static void DrawGroupBox(Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, TextFormatFlags flags, GroupBoxState state) { + if (RenderWithVisualStyles) + DrawThemedGroupBoxWithText(g, bounds, groupBoxText, font, textColor, flags, state); + else + DrawUnthemedGroupBoxWithText(g, bounds, groupBoxText, font, textColor, flags, state); + } + + /// + /// Draws a themed GroupBox with no text label. + /// + /// + private static void DrawThemedGroupBoxNoText(Graphics g, Rectangle bounds, GroupBoxState state) { + InitializeRenderer((int)state); + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// Draws a themed GroupBox with a text label. + /// + /// + private static void DrawThemedGroupBoxWithText(Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, TextFormatFlags flags, GroupBoxState state) { + InitializeRenderer((int)state); + + // Calculate text area, and render text inside it + Rectangle textBounds = bounds; + + textBounds.Width -= 2 * boxHeaderWidth; + Size measuredBounds = TextRenderer.MeasureText(g, groupBoxText, font, new Size(textBounds.Width, textBounds.Height), flags); + textBounds.Width = measuredBounds.Width; + textBounds.Height = measuredBounds.Height; + + if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + textBounds.X = bounds.Right - textBounds.Width - boxHeaderWidth + 1; // +1 to account for the margin built in the MeasureText result + } + else { + textBounds.X += boxHeaderWidth - 1; // -1 to account for the margin built in the MeasureText result + } + + TextRenderer.DrawText(g, groupBoxText, font, textBounds, textColor, flags); + + // Calculate area for background box + Rectangle boxBounds = bounds; + boxBounds.Y += font.Height / 2; + boxBounds.Height -= font.Height / 2; + + // Break box into three segments, that don't overlap the text area + Rectangle clipLeft = boxBounds; + Rectangle clipMiddle = boxBounds; + Rectangle clipRight = boxBounds; + + clipLeft.Width = boxHeaderWidth; + clipMiddle.Width = Math.Max(0, textBounds.Width - 3); // -3 to account for the margin built in the MeasureText result + if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right) + { + clipLeft.X = boxBounds.Right - boxHeaderWidth; + clipMiddle.X = clipLeft.Left - clipMiddle.Width; + clipRight.Width = clipMiddle.X - boxBounds.X; + } + else + { + clipMiddle.X = clipLeft.Right; + clipRight.X = clipMiddle.Right; + clipRight.Width = boxBounds.Right - clipRight.X; + } + clipMiddle.Y = textBounds.Bottom; + clipMiddle.Height -= (textBounds.Bottom - boxBounds.Top); + + Debug.Assert(textBounds.Y <= boxBounds.Y, "if text below box, need to render area of box above text"); + + // Render clipped portion of background in each segment + visualStyleRenderer.DrawBackground(g, boxBounds, clipLeft); + visualStyleRenderer.DrawBackground(g, boxBounds, clipMiddle); + visualStyleRenderer.DrawBackground(g, boxBounds, clipRight); + } + + /// + /// Draws an un-themed GroupBox with no text label. + /// + /// + private static void DrawUnthemedGroupBoxNoText(Graphics g, Rectangle bounds, GroupBoxState state) { + Color backColor = SystemColors.Control; + Pen light = new Pen(ControlPaint.Light(backColor, 1.0f)); + Pen dark = new Pen(ControlPaint.Dark(backColor, 0f)); + try { + // left + g.DrawLine(light, bounds.Left + 1, bounds.Top + 1, bounds.Left + 1, bounds.Height - 1); + g.DrawLine(dark, bounds.Left, bounds.Top + 1, bounds.Left, bounds.Height - 2); + + // bottom + g.DrawLine(light, bounds.Left, bounds.Height - 1, bounds.Width - 1, bounds.Height - 1); + g.DrawLine(dark, bounds.Left, bounds.Height - 2, bounds.Width - 1, bounds.Height - 2); + + // top + g.DrawLine(light, bounds.Left + 1, bounds.Top + 1, bounds.Width - 1, bounds.Top + 1); + g.DrawLine(dark, bounds.Left, bounds.Top, bounds.Width - 2, bounds.Top); + + // right + g.DrawLine(light, bounds.Width - 1, bounds.Top, bounds.Width - 1, bounds.Height - 1); + g.DrawLine(dark, bounds.Width - 2, bounds.Top, bounds.Width - 2, bounds.Height - 2); + } + finally { + if (light != null) { + light.Dispose(); + } + if (dark != null) { + dark.Dispose(); + } + } + } + + /// + /// Draws an un-themed GroupBox with a text label. + /// Variation of the logic in GroupBox.DrawGroupBox(). + /// + /// + private static void DrawUnthemedGroupBoxWithText(Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, TextFormatFlags flags, GroupBoxState state) { + // Calculate text area, and render text inside it + Rectangle textBounds = bounds; + + textBounds.Width -= textOffset; + Size measuredBounds = TextRenderer.MeasureText(g, groupBoxText, font, new Size(textBounds.Width, textBounds.Height), flags); + textBounds.Width = measuredBounds.Width; + textBounds.Height = measuredBounds.Height; + + if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + textBounds.X = bounds.Right - textBounds.Width - textOffset; + } + else { + textBounds.X += textOffset; + } + + TextRenderer.DrawText(g, groupBoxText, font, textBounds, textColor, flags); + + // Pad text area to stop background from touching text + if (textBounds.Width > 0) + textBounds.Inflate(2, 0); + + Pen light = new Pen(SystemColors.ControlLight); + Pen dark = new Pen(SystemColors.ControlDark); + + int boxTop = bounds.Top + font.Height / 2; + + // left + g.DrawLine(light, bounds.Left + 1, boxTop, bounds.Left + 1, bounds.Height - 1); + g.DrawLine(dark, bounds.Left, boxTop - 1, bounds.Left, bounds.Height - 2); + + // bottom + g.DrawLine(light, bounds.Left, bounds.Height - 1, bounds.Width, bounds.Height - 1); + g.DrawLine(dark, bounds.Left, bounds.Height - 2, bounds.Width - 1, bounds.Height - 2); + + // top-left + g.DrawLine(light, bounds.Left + 1, boxTop, textBounds.X - 2, boxTop); + g.DrawLine(dark, bounds.Left, boxTop - 1, textBounds.X - 3, boxTop - 1); + + // top-right + g.DrawLine(light, textBounds.X + textBounds.Width + 1, boxTop, bounds.Width - 1, boxTop); + g.DrawLine(dark, textBounds.X + textBounds.Width + 2, boxTop - 1, bounds.Width - 2, boxTop - 1); + + // right + g.DrawLine(light, bounds.Width - 1, boxTop, bounds.Width - 1, bounds.Height - 1); + g.DrawLine(dark, bounds.Width - 2, boxTop - 1, bounds.Width - 2, bounds.Height - 2); + + light.Dispose(); + dark.Dispose(); + } + + private static Color DefaultTextColor(GroupBoxState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + return visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + return SystemColors.ControlText; + } + } + + private static void InitializeRenderer(int state) { + int part = GroupBoxElement.Part; + if (AccessibilityImprovements.Level2 + && SystemInformation.HighContrast + && ((GroupBoxState)state == GroupBoxState.Disabled) + && VisualStyleRenderer.IsCombinationDefined(GroupBoxElement.ClassName, VisualStyleElement.Button.GroupBox.HighContrastDisabledPart)) { + part = VisualStyleElement.Button.GroupBox.HighContrastDisabledPart; + } + + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(GroupBoxElement.ClassName, part, state); + } + else { + visualStyleRenderer.SetParameters(GroupBoxElement.ClassName, part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HScrollBar.cs b/WindowsForms/Managed/System/WinForms/HScrollBar.cs new file mode 100644 index 000000000..cea6a5d91 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HScrollBar.cs @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + using System.Runtime.InteropServices; + + /// + /// + /// + /// Represents + /// a standard Windows horizontal scroll bar. + /// + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + SRDescription(SR.DescriptionHScrollBar) + ] + public class HScrollBar : ScrollBar { + + /// + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.Style |= NativeMethods.SBS_HORZ; + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(80, SystemInformation.HorizontalScrollBarHeight); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HScrollProperties.cs b/WindowsForms/Managed/System/WinForms/HScrollProperties.cs new file mode 100644 index 000000000..b4d37f183 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HScrollProperties.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms; + + + + + /// + /// + /// + /// Basic Properties for VScroll. + /// + /// + public class HScrollProperties : ScrollProperties { + + /// + public HScrollProperties(ScrollableControl container) : base(container) { + } + + internal override int PageSize { + get { + return ParentControl.ClientRectangle.Width; + } + } + + internal override int Orientation { + get { + return NativeMethods.SB_HORZ; + } + } + + internal override int HorizontalDisplayPosition { + get { + return -this.value; + } + } + + internal override int VerticalDisplayPosition { + get { + return ParentControl.DisplayRectangle.Y; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HandledMouseEvent.cs b/WindowsForms/Managed/System/WinForms/HandledMouseEvent.cs new file mode 100644 index 000000000..b6a8f9205 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HandledMouseEvent.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + public class HandledMouseEventArgs : MouseEventArgs + { + /// + /// Indicates, on return, whether or not the event was handled in the application's event handler. + /// 'true' means the application handled the event, 'false' means it didn't. + /// + private bool handled; + + /// + public HandledMouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta) : this(button, clicks, x, y, delta, false) + { + } + + /// + public HandledMouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta, bool defaultHandledValue) : base(button, clicks, x, y, delta) + { + this.handled = defaultHandledValue; + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the event is handled. + /// + /// + public bool Handled + { + get + { + return this.handled; + } + set + { + this.handled = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Help.cs b/WindowsForms/Managed/System/WinForms/Help.cs new file mode 100644 index 000000000..8fda8887c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Help.cs @@ -0,0 +1,486 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1997, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security; + using System.Security.Permissions; + using System.Net; + using System.Drawing; + using System.ComponentModel; + using System.IO; + using System.Text; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// + /// Represents the HTML 1.0 Help engine. + /// + /// + public class Help { +#if DEBUG + internal static readonly TraceSwitch WindowsFormsHelpTrace = new TraceSwitch("WindowsFormsHelpTrace", "Debug help system"); +#else + internal static readonly TraceSwitch WindowsFormsHelpTrace; +#endif + + private const int HH_DISPLAY_TOPIC =0x0000; + private const int HH_HELP_FINDER =0x0000; // WinHelp equivalent + private const int HH_DISPLAY_TOC =0x0001; // not currently implemented + private const int HH_DISPLAY_INDEX =0x0002; // not currently implemented + private const int HH_DISPLAY_SEARCH =0x0003; // not currently implemented + private const int HH_SET_WIN_TYPE =0x0004; + private const int HH_GET_WIN_TYPE =0x0005; + private const int HH_GET_WIN_HANDLE =0x0006; + private const int HH_ENUM_INFO_TYPE =0x0007; // Get Info type name, call repeatedly to enumerate, -1 at end + private const int HH_SET_INFO_TYPE =0x0008; // Add Info type to filter. + private const int HH_SYNC =0x0009; + private const int HH_ADD_NAV_UI =0x000A; // not currently implemented + private const int HH_ADD_BUTTON =0x000B; // not currently implemented + private const int HH_GETBROWSER_APP =0x000C; // not currently implemented + private const int HH_KEYWORD_LOOKUP =0x000D; + private const int HH_DISPLAY_TEXT_POPUP =0x000E; // display string resource id or text in a popup window + private const int HH_HELP_CONTEXT =0x000F; // display mapped numeric value in dwData + private const int HH_TP_HELP_CONTEXTMENU =0x0010; // text popup help, same as WinHelp HELP_CONTEXTMENU + private const int HH_TP_HELP_WM_HELP =0x0011; // text popup help, same as WinHelp HELP_WM_HELP + private const int HH_CLOSE_ALL =0x0012; // close all windows opened directly or indirectly by the caller + private const int HH_ALINK_LOOKUP =0x0013; // ALink version of HH_KEYWORD_LOOKUP + private const int HH_GET_LAST_ERROR =0x0014; // not currently implemented // See HHERROR.h + private const int HH_ENUM_CATEGORY =0x0015; // Get category name, call repeatedly to enumerate, -1 at end + private const int HH_ENUM_CATEGORY_IT =0x0016; // Get category info type members, call repeatedly to enumerate, -1 at end + private const int HH_RESET_IT_FILTER =0x0017; // Clear the info type filter of all info types. + private const int HH_SET_INCLUSIVE_FILTER =0x0018; // set inclusive filtering method for untyped topics to be included in display + private const int HH_SET_EXCLUSIVE_FILTER =0x0019; // set exclusive filtering method for untyped topics to be excluded from display + private const int HH_SET_GUID =0x001A; // For Microsoft Installer -- dwData is a pointer to the GUID string + + private const int HTML10HELP = 2; + private const int HTMLFILE = 3; + + // not creatable + // + private Help() { + } + + /// + /// + /// + /// Displays + /// the contents of the Help file at located at a specified Url. + /// + /// + public static void ShowHelp(Control parent, string url) { + ShowHelp(parent, url, HelpNavigator.TableOfContents, null); + } + + /// + /// + /// + /// Displays the contents of + /// the Help + /// file for a specific topic found at the specified Url. + /// + /// + public static void ShowHelp(Control parent, string url, HelpNavigator navigator) { + ShowHelp(parent, url, navigator, null); + } + + /// + /// + /// + /// Displays the contents of + /// the Help + /// file for a specific topic found at the specified Url. + /// + /// + public static void ShowHelp(Control parent, string url, string keyword) { + if (keyword != null && keyword.Length != 0) { + ShowHelp(parent, url, HelpNavigator.Topic, keyword); + } + else { + ShowHelp(parent, url, HelpNavigator.TableOfContents, null); + } + } + + /// + /// + /// + /// Displays the contents of the Help file located at + /// the Url + /// supplied by the + /// user. + /// + /// + public static void ShowHelp(Control parent, string url, HelpNavigator command, object parameter) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: ShowHelp"); + + switch (GetHelpFileType(url)) { + case HTML10HELP: + ShowHTML10Help(parent, url, command, parameter); + break; + case HTMLFILE: + ShowHTMLFile(parent, url, command, parameter); + break; + } + } + + /// + /// + /// + /// Displays the index of the specified file. + /// + /// + public static void ShowHelpIndex(Control parent, string url) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: ShowHelpIndex"); + + ShowHelp(parent, url, HelpNavigator.Index, null); + } + + /// + /// + /// + /// Displays a Help pop-up window. + /// + /// + public static void ShowPopup(Control parent, string caption, Point location) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: ShowPopup"); + + NativeMethods.HH_POPUP pop = new NativeMethods.HH_POPUP(); + + // ASURT 148069 + // We have to marshal the string ourselves to prevent access violations. + IntPtr pszText = Marshal.StringToCoTaskMemAuto(caption); + + try { + pop.pszText = pszText; + pop.idString = 0; + pop.pt = new NativeMethods.POINT(location.X, location.Y); + + // ASURT 108580 + // Looks like a windows bug causes the -1 value for clrBackground to not + // do the right thing for High Contrast Black color scheme (and probably others) + // + pop.clrBackground = Color.FromKnownColor(KnownColor.Window).ToArgb() & 0x00ffffff; + + ShowHTML10Help(parent, null, HelpNavigator.Topic, pop); + } + finally { + Marshal.FreeCoTaskMem(pszText); + } + + } + + /// + /// + /// Displays HTML 1.0 Help with the specified parameters + /// + /// + private static void ShowHTML10Help(Control parent, string url, HelpNavigator command, object param) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: ShowHTML10Help:: " + url + ", " + command.ToString("G") + ", " + param); + + // See VSWhidbey #538252 for details on why we decided to add this demand. + IntSecurity.UnmanagedCode.Demand(); + + // VSWhidbey 106914: See if we can get a full path and file name and if that will + // resolve the out of memory condition with file names that include spaces. + // If we can't, though, we can't assume that the path's no good: it might be in + // the Windows help directory (VSWhidbey 474069). + Uri file = null; + string pathAndFileName = url; //This is our best guess at the path yet. + + file = Resolve(url); + if (file != null) { // VSWhidbey 271337: can't assume we have a good url + pathAndFileName = file.AbsoluteUri; + } + if (file == null || file.IsFile) { + StringBuilder newPath = new StringBuilder(); + string localPath = (file != null && file.IsFile) ? file.LocalPath : url; + + // If this is a local path, convert it to a short path name. Pass 0 as the length the first time + uint requiredStringSize = UnsafeNativeMethods.GetShortPathName(localPath, newPath, 0); + if (requiredStringSize > 0) { + //It's able to make it a short path. Happy day. + newPath.Capacity = (int)requiredStringSize; + requiredStringSize = UnsafeNativeMethods.GetShortPathName(localPath, newPath, requiredStringSize); + //If it can't make it a short path, just leave the path we had. + pathAndFileName = newPath.ToString(); + } + } + + HandleRef handle; + if (parent != null) { + handle = new HandleRef(parent, parent.Handle); + } + else { + handle = new HandleRef(null, UnsafeNativeMethods.GetActiveWindow()); + } + + object htmlParam; + string stringParam = param as string; + if (stringParam != null) { + int htmlCommand = MapCommandToHTMLCommand(command, stringParam, out htmlParam); + + string stringHtmlParam = htmlParam as string; + if (stringHtmlParam != null) { + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, htmlCommand, stringHtmlParam); + } + else if (htmlParam is int) { + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, htmlCommand, (int)htmlParam); + } + else if (htmlParam is NativeMethods.HH_FTS_QUERY) { + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, htmlCommand, (NativeMethods.HH_FTS_QUERY)htmlParam); + } + else if (htmlParam is NativeMethods.HH_AKLINK) { + // According to MSDN documentation, we have to ensure that the help window is up + // before we call ALINK lookup. + // + SafeNativeMethods.HtmlHelp(NativeMethods.NullHandleRef, pathAndFileName, HH_DISPLAY_TOPIC, (string)null); + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, htmlCommand, (NativeMethods.HH_AKLINK)htmlParam); + } + else { + Debug.Fail("Cannot handle HTML parameter of type: " + htmlParam.GetType()); + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, htmlCommand, (string)param); + } + } + else if (param == null ) { + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, MapCommandToHTMLCommand(command, null, out htmlParam), 0); + } + else if (param is NativeMethods.HH_POPUP) { + SafeNativeMethods.HtmlHelp(handle, pathAndFileName, HH_DISPLAY_TEXT_POPUP, (NativeMethods.HH_POPUP)param); + } + else if (param.GetType() == typeof(Int32)) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "param", "Integer")); + } + } + + + /// + /// + /// Displays HTMLFile with the specified parameters + /// + /// + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: url is directly scrubbed in resolve, is a local member, will not change before it's passed into WebPermission. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + private static void ShowHTMLFile(Control parent, string url, HelpNavigator command, object param) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: ShowHTMLHelp:: " + url + ", " + command.ToString("G") + ", " + param); + + Uri file = Resolve(url); + + if (file == null) { + throw new ArgumentException(SR.GetString(SR.HelpInvalidURL, url), "url"); + } + + switch (file.Scheme) { + case "http": + case "https": + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "WebPermission Demanded"); + new WebPermission(NetworkAccess.Connect, url).Demand(); + break; + default: + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "UnmanagedCode Demanded"); + IntSecurity.UnmanagedCode.Demand(); + break; + } + + switch (command) { + case HelpNavigator.TableOfContents: + case HelpNavigator.Find: + case HelpNavigator.Index: + // nothing needed... + // + break; + case HelpNavigator.Topic: + if (param != null && param is string) { + file = new Uri(file.ToString() + "#" + (string)param); + } + break; + } + + HandleRef handle; + if (parent != null) { + handle = new HandleRef(parent, parent.Handle); + } + else { + handle = new HandleRef(null, UnsafeNativeMethods.GetActiveWindow()); + } + + + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "\tExecuting '" + file.ToString() + "'"); + UnsafeNativeMethods.ShellExecute_NoBFM(handle, null, file.ToString(), null, null, NativeMethods.SW_NORMAL); + } + + private static Uri Resolve(string partialUri) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: Resolve " + partialUri); + Debug.Indent(); + + Uri file = null; + // VSW 426515: Catch specific exceptions + if (!string.IsNullOrEmpty(partialUri)) { + try { + file = new Uri(partialUri); + } + catch (UriFormatException) { + // eat URI parse exceptions... + // + } + catch (ArgumentNullException) { + // VSW 426515: Catch specific exceptions + // Shouldnt get here... + } + } + + if (file != null && file.Scheme == "file") { + string localPath = NativeMethods.GetLocalPath(partialUri); + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "file, check for existence"); + + // SECREVIEW : Checking for file here is safe. + // + new FileIOPermission(FileIOPermissionAccess.Read, localPath).Assert(); + try { + if (!File.Exists(localPath)) { + // clear, and try relative to AppBase... + // + file = null; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + + if (file == null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "try appbase relative"); + try { + // try relative to AppBase... + // + file = new Uri(new Uri(AppDomain.CurrentDomain.SetupInformation.ApplicationBase), + partialUri); + } + catch (UriFormatException) { + // VSW 426515: Catch specific exceptions + // eat URI parse exceptions... + // + } + catch (ArgumentNullException) { + // VSW 426515: Catch specific exceptions + // Shouldnt get here... + } + + if (file != null && file.Scheme == "file") { + string localPath = file.LocalPath + file.Fragment; + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "file, check for existence"); + new FileIOPermission(FileIOPermissionAccess.Read, localPath).Assert(); + try { + if (!File.Exists(localPath)) { + // clear - file isn't there... + // + file = null; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + + + Debug.Unindent(); + return file; + } + + /// + /// + private static int GetHelpFileType(string url) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "Help:: GetHelpFileType " + url); + + if (url == null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "\tnull, must be Html File"); + return HTMLFILE; + } + + Uri file = Resolve(url); + + if (file == null || file.Scheme == "file") { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "\tfile"); + + string ext = Path.GetExtension(file == null ? url : file.LocalPath + file.Fragment).ToLower(CultureInfo.InvariantCulture); + if (ext == ".chm" || ext == ".col") { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "\tchm or col, HtmlHelp 1.0 file"); + return HTML10HELP; + } + } + + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "\tnot file, or odd extension, but be HTML"); + return HTMLFILE; + } + + /// + /// + /// Maps one of the COMMAND_* constants to the HTML 1.0 Help equivalent. + /// + /// + private static int MapCommandToHTMLCommand(HelpNavigator command, string param, out object htmlParam) { + htmlParam = param; + + if ((String.IsNullOrEmpty(param)) && + (command == HelpNavigator.AssociateIndex || command == HelpNavigator.KeywordIndex)) { + return HH_DISPLAY_INDEX; + } + + switch (command) { + case HelpNavigator.Topic: + return HH_DISPLAY_TOPIC; + + case HelpNavigator.TableOfContents: + return HH_DISPLAY_TOC; + + case HelpNavigator.Index: + return HH_DISPLAY_INDEX; + + case HelpNavigator.Find: { + NativeMethods.HH_FTS_QUERY ftsQuery = new NativeMethods.HH_FTS_QUERY(); + ftsQuery.pszSearchQuery = param; + htmlParam = ftsQuery; + return HH_DISPLAY_SEARCH; + } + case HelpNavigator.TopicId: { + try { + htmlParam = int.Parse(param, CultureInfo.InvariantCulture); + return HH_HELP_CONTEXT; + } + catch { + // default to just showing the index + return HH_DISPLAY_INDEX; + } + } + case HelpNavigator.KeywordIndex: + case HelpNavigator.AssociateIndex: { + NativeMethods.HH_AKLINK alink = new NativeMethods.HH_AKLINK(); + alink.pszKeywords = param; + alink.fIndexOnFail = true; + alink.fReserved = false; + htmlParam = alink; + return (command == HelpNavigator.KeywordIndex) ? HH_KEYWORD_LOOKUP : HH_ALINK_LOOKUP; + } + + default: + return (int)command; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HelpEvent.cs b/WindowsForms/Managed/System/WinForms/HelpEvent.cs new file mode 100644 index 000000000..7ba0b8df2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HelpEvent.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the Control.HelpRequest event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class HelpEventArgs : EventArgs { + private readonly Point mousePos; + private bool handled = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public HelpEventArgs(Point mousePos) { + this.mousePos = mousePos; + } + + /// + /// + /// + /// Gets the screen coordinates of the mouse pointer. + /// + /// + public Point MousePos { + get { + return mousePos; + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating + /// whether the Help event was handled. + /// + /// + public bool Handled { + get { + return handled; + } + set { + handled = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HelpEventHandler.cs b/WindowsForms/Managed/System/WinForms/HelpEventHandler.cs new file mode 100644 index 000000000..f7826fec8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HelpEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the Help event of a Control. + /// + /// + public delegate void HelpEventHandler(object sender, HelpEventArgs hlpevent); +} diff --git a/WindowsForms/Managed/System/WinForms/HelpInfo.cs b/WindowsForms/Managed/System/WinForms/HelpInfo.cs new file mode 100644 index 000000000..c6abf2c89 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HelpInfo.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Diagnostics; + using System; + + + internal class HelpInfo { + + private string helpFilePath; + private string keyword; + private HelpNavigator navigator; + private object param; + private int option; + + public HelpInfo(string helpfilepath) { + this.helpFilePath= helpfilepath; + this.keyword = ""; + this.navigator = HelpNavigator.TableOfContents; + this.param = null; + this.option = NativeMethods.HLP_FILE; + } + + public HelpInfo(string helpfilepath, string keyword) { + this.helpFilePath= helpfilepath; + this.keyword = keyword; + this.navigator = HelpNavigator.TableOfContents; + this.param = null; + this.option = NativeMethods.HLP_KEYWORD; + } + + public HelpInfo(string helpfilepath, HelpNavigator navigator) { + this.helpFilePath= helpfilepath; + this.keyword = ""; + this.navigator = navigator; + this.param = null; + this.option = NativeMethods.HLP_NAVIGATOR; + } + + + public HelpInfo(string helpfilepath, HelpNavigator navigator, object param) { + this.helpFilePath= helpfilepath; + this.keyword = ""; + this.navigator = navigator; + this.param = param; + this.option = NativeMethods.HLP_OBJECT; + } + + public int Option { + get { + return option; + } + } + + public string HelpFilePath { + get { + return helpFilePath; + } + } + + public string Keyword { + get { + return keyword; + } + } + + public HelpNavigator Navigator { + get { + return navigator; + } + } + + public object Param { + get { + return param; + } + } + + + public override string ToString() { + return "{HelpFilePath=" + helpFilePath + ", keyword =" + keyword + ", navigator=" + navigator.ToString() + "}"; + } + + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/HelpNavigator.cs b/WindowsForms/Managed/System/WinForms/HelpNavigator.cs new file mode 100644 index 000000000..8f0b7bbab --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HelpNavigator.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +* Copyright (c) 1997, Microsoft Corporation. All Rights Reserved. +* Information Contained Herein is Proprietary and Confidential. +*/ + +namespace System.Windows.Forms { + using System; + + /// + /// + /// + /// Represents the HTML 1.0 Help engine. + /// + /// + public enum HelpNavigator { + + /// + /// + /// + /// Displays the topic referenced by the topic referenced by + /// the specified Url. + /// This field is + /// constant. + /// + /// + Topic = unchecked((int)0x80000001), + /// + /// + /// + /// Displays the contents of the + /// HTML 1.0 Help file. This field is constant. + /// + /// + TableOfContents = unchecked((int)0x80000002), + /// + /// + /// + /// Displays the index of a specified + /// Url. This field is constant. + /// + /// + Index = unchecked((int)0x80000003), + /// + /// + /// + /// Displays the search page + /// of a specified Url. This field is constant. + /// + /// + Find = unchecked((int)0x80000004), + /// + /// + /// + /// Displays the topic referenced by the topic referenced by + /// the specified Url. + /// This field is + /// constant. + /// + /// + AssociateIndex = unchecked((int)0x80000005), + /// + /// + /// + /// Displays the topic referenced by the topic referenced by + /// the specified Url. + /// This field is + /// constant. + /// + /// + KeywordIndex = unchecked((int)0x80000006), + /// + /// + /// + /// Displays the topic referenced by the topic ID + /// This field is + /// constant. + /// + /// + TopicId = unchecked((int)0x80000007) + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/HelpProvider.cs b/WindowsForms/Managed/System/WinForms/HelpProvider.cs new file mode 100644 index 000000000..7610280d6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HelpProvider.cs @@ -0,0 +1,366 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using Microsoft.Win32; + using Hashtable = System.Collections.Hashtable; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + + + /// + /// + /// + /// Provides pop-up or online Help for controls. + /// + /// + [ + ProvideProperty("HelpString", typeof(Control)), + ProvideProperty("HelpKeyword", typeof(Control)), + ProvideProperty("HelpNavigator", typeof(Control)), + ProvideProperty("ShowHelp", typeof(Control)), + ToolboxItemFilter("System.Windows.Forms"), + SRDescription(SR.DescriptionHelpProvider) + ] + public class HelpProvider : Component, IExtenderProvider { + + private string helpNamespace = null; + private Hashtable helpStrings = new Hashtable(); + private Hashtable showHelp = new Hashtable(); + private Hashtable boundControls = new Hashtable(); + private Hashtable keywords = new Hashtable(); + private Hashtable navigators = new Hashtable(); + + private object userData; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public HelpProvider() { + } + + /// + /// + /// + /// Gets or sets a string indicating the name of the Help + /// file associated with this object. + /// + /// + [ + Localizable(true), + DefaultValue(null), + Editor("System.Windows.Forms.Design.HelpNamespaceEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SRDescription(SR.HelpProviderHelpNamespaceDescr) + ] + public virtual string HelpNamespace { + get { + return helpNamespace; + } + + set { + this.helpNamespace = value; + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// + /// Determines if the help provider can offer it's extender properties + /// to the specified target object. + /// + /// + public virtual bool CanExtend(object target) { + return(target is Control); + } + + /// + /// + /// + /// Retrieves the Help Keyword displayed when the + /// user invokes Help for the specified control. + /// + /// + [ + DefaultValue(null), + Localizable(true), + SRDescription(SR.HelpProviderHelpKeywordDescr) + ] + public virtual string GetHelpKeyword(Control ctl) { + return(string)keywords[ctl]; + } + + /// + /// + /// + /// Retrieves the contents of the pop-up help window for the specified + /// control. + /// + /// + [ + DefaultValue(HelpNavigator.AssociateIndex), + Localizable(true), + SRDescription(SR.HelpProviderNavigatorDescr) + ] + public virtual HelpNavigator GetHelpNavigator(Control ctl) { + object nav = navigators[ctl]; + return (nav == null) ? HelpNavigator.AssociateIndex : (HelpNavigator)nav; + } + + /// + /// + /// + /// Retrieves the contents of the pop-up help window for the specified + /// control. + /// + /// + [ + DefaultValue(null), + Localizable(true), + SRDescription(SR.HelpProviderHelpStringDescr) + ] + public virtual string GetHelpString(Control ctl) { + return(string)helpStrings[ctl]; + } + + /// + /// + /// + /// Retrieves a value indicating whether Help displays for + /// the specified control. + /// + /// + [ + Localizable(true), + SRDescription(SR.HelpProviderShowHelpDescr) + ] + public virtual bool GetShowHelp(Control ctl) { + object b = showHelp[ctl]; + if (b == null) { + return false; + } + else { + return(Boolean) b; + } + } + + /// + /// + /// Handles the help event for any bound controls. + /// + /// + private void OnControlHelp(object sender, HelpEventArgs hevent) { + Control ctl = (Control)sender; + string helpString = GetHelpString(ctl); + string keyword = GetHelpKeyword(ctl); + HelpNavigator navigator = GetHelpNavigator(ctl); + bool show = GetShowHelp(ctl); + + if (!show) { + return; + } + + // If the mouse was down, we first try whats this help + // + if (Control.MouseButtons != MouseButtons.None && helpString != null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "HelpProvider:: Mouse down w/ helpstring"); + + if (helpString.Length > 0) { + Help.ShowPopup(ctl, helpString, hevent.MousePos); + hevent.Handled = true; + } + } + + // If we have a help file, and help keyword we try F1 help next + // + if (!hevent.Handled && helpNamespace != null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "HelpProvider:: F1 help"); + if (keyword != null) { + if (keyword.Length > 0) { + Help.ShowHelp(ctl, helpNamespace, navigator, keyword); + hevent.Handled = true; + } + } + + if (!hevent.Handled) { + Help.ShowHelp(ctl, helpNamespace, navigator); + hevent.Handled = true; + } + } + + // So at this point we don't have a help keyword, so try to display + // the whats this help + // + if (!hevent.Handled && helpString != null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "HelpProvider:: back to helpstring"); + + if (helpString.Length > 0) { + Help.ShowPopup(ctl, helpString, hevent.MousePos); + hevent.Handled = true; + } + } + + // As a last resort, just popup the contents page of the help file... + // + if (!hevent.Handled && helpNamespace != null) { + Debug.WriteLineIf(Help.WindowsFormsHelpTrace.TraceVerbose, "HelpProvider:: contents"); + + Help.ShowHelp(ctl, helpNamespace); + hevent.Handled = true; + } + } + + /// + /// + /// Handles the help event for any bound controls. + /// + /// + private void OnQueryAccessibilityHelp(object sender, QueryAccessibilityHelpEventArgs e) { + Control ctl = (Control)sender; + + e.HelpString = GetHelpString(ctl); + e.HelpKeyword = GetHelpKeyword(ctl); + e.HelpNamespace = HelpNamespace; + } + + /// + /// + /// + /// Specifies + /// a Help string associated with a control. + /// + /// + public virtual void SetHelpString(Control ctl, string helpString) { + helpStrings[ctl] = helpString; + if (helpString != null) { + if (helpString.Length > 0) { + SetShowHelp(ctl, true); + } + } + UpdateEventBinding(ctl); + } + + /// + /// + /// + /// Specifies the Help keyword to display when + /// the user invokes Help for a control. + /// + /// + public virtual void SetHelpKeyword(Control ctl, string keyword) { + keywords[ctl] = keyword; + if (keyword != null) { + if (keyword.Length > 0) { + SetShowHelp(ctl, true); + } + } + UpdateEventBinding(ctl); + } + + /// + /// + /// + /// Specifies the Help keyword to display when + /// the user invokes Help for a control. + /// + /// + public virtual void SetHelpNavigator(Control ctl, HelpNavigator navigator) { + //valid values are 0x80000001 to 0x80000007 + if (!ClientUtils.IsEnumValid(navigator, (int)navigator, (int)HelpNavigator.Topic, (int)HelpNavigator.TopicId)){ + //validate the HelpNavigator enum + throw new InvalidEnumArgumentException("navigator", (int)navigator, typeof(HelpNavigator)); + } + + navigators[ctl] = navigator; + SetShowHelp(ctl, true); + UpdateEventBinding(ctl); + } + /// + /// + /// + /// Specifies whether Help is displayed for a given control. + /// + /// + public virtual void SetShowHelp(Control ctl, bool value) { + showHelp[ ctl] = value ; + UpdateEventBinding(ctl); + } + + /// + /// + /// Used by the designer + /// + /// + internal virtual bool ShouldSerializeShowHelp(Control ctl) { + return showHelp.ContainsKey(ctl); + } + + /// + /// + /// Used by the designer + /// + /// + public virtual void ResetShowHelp(Control ctl) { + showHelp.Remove(ctl); + } + + /// + /// + /// Binds/unbinds event handlers to ctl + /// + /// + private void UpdateEventBinding(Control ctl) { + if (GetShowHelp(ctl) && !boundControls.ContainsKey(ctl)) { + ctl.HelpRequested += new HelpEventHandler(this.OnControlHelp); + ctl.QueryAccessibilityHelp += new QueryAccessibilityHelpEventHandler(this.OnQueryAccessibilityHelp); + boundControls[ctl] = ctl; + } + else if (!GetShowHelp(ctl) && boundControls.ContainsKey(ctl)) { + ctl.HelpRequested -= new HelpEventHandler(this.OnControlHelp); + ctl.QueryAccessibilityHelp -= new QueryAccessibilityHelpEventHandler(this.OnQueryAccessibilityHelp); + boundControls.Remove(ctl); + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", HelpNamespace: " + HelpNamespace; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/HorizontalAlignment.cs b/WindowsForms/Managed/System/WinForms/HorizontalAlignment.cs new file mode 100644 index 000000000..d2a9421a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HorizontalAlignment.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how an object or text in a control is + /// horizontally aligned relative to an element of the control. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum HorizontalAlignment { + + /// + /// + /// + /// The object or text is aligned on the left of the control element. + /// + /// + Left = 0, + + /// + /// + /// + /// The object or text is aligned on the right of the control element. + /// + /// + Right = 1, + + /// + /// + /// + /// The object or text is aligned in the center of the control element. + /// + /// + Center = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/HtmlDocument.cs b/WindowsForms/Managed/System/WinForms/HtmlDocument.cs new file mode 100644 index 000000000..da248a8b4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlDocument.cs @@ -0,0 +1,1249 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Printing; +using System.Globalization; +using System.IO; +using System.Net; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; + + +namespace System.Windows.Forms +{ + /// + /// + /// [To be supplied.] + /// + [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")] + public sealed class HtmlDocument + { + internal static object EventClick = new object(); + internal static object EventContextMenuShowing = new object(); + internal static object EventFocusing = new object(); + internal static object EventLosingFocus = new object(); + internal static object EventMouseDown = new object(); + internal static object EventMouseLeave = new object(); + internal static object EventMouseMove = new object(); + internal static object EventMouseOver = new object(); + internal static object EventMouseUp = new object(); + internal static object EventStop = new object(); + + private UnsafeNativeMethods.IHTMLDocument2 htmlDocument2; + private HtmlShimManager shimManager; + + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + internal HtmlDocument(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLDocument doc) + { + this.htmlDocument2 = (UnsafeNativeMethods.IHTMLDocument2)doc; + Debug.Assert(this.NativeHtmlDocument2 != null, "The document should implement IHtmlDocument2"); + + this.shimManager = shimManager; + + } + + internal UnsafeNativeMethods.IHTMLDocument2 NativeHtmlDocument2 + { + get + { + return this.htmlDocument2; + } + } + + private HtmlDocumentShim DocumentShim + { + get + { + if (ShimManager != null) + { + HtmlDocumentShim shim = ShimManager.GetDocumentShim(this); + if (shim == null) + { + shimManager.AddDocumentShim(this); + shim = ShimManager.GetDocumentShim(this); + } + return shim; + } + return null; + } + } + + private HtmlShimManager ShimManager + { + get + { + return this.shimManager; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement ActiveElement + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlDocument2.GetActiveElement(); + return iHtmlElement != null ? new HtmlElement(ShimManager, iHtmlElement) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement Body + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlDocument2.GetBody(); + return iHtmlElement != null ? new HtmlElement(ShimManager, iHtmlElement) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public string Domain + { + get + { + return this.NativeHtmlDocument2.GetDomain(); + } + set + { + try + { + this.NativeHtmlDocument2.SetDomain(value); + } + catch (ArgumentException) + { + // Give a better message describing the error + throw new ArgumentException(SR.GetString(SR.HtmlDocumentInvalidDomain)); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public string Title + { + get + { + return this.NativeHtmlDocument2.GetTitle(); + } + set + { + this.NativeHtmlDocument2.SetTitle(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public Uri Url + { + get + { + UnsafeNativeMethods.IHTMLLocation iHtmlLocation = this.NativeHtmlDocument2.GetLocation(); + string stringLocation = (iHtmlLocation == null) ? "" : iHtmlLocation.GetHref(); + return string.IsNullOrEmpty(stringLocation) ? null : new Uri(stringLocation); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlWindow Window + { + get + { + UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlDocument2.GetParentWindow(); + return iHTMLWindow2 != null ? new HtmlWindow(ShimManager, iHTMLWindow2) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public Color BackColor + { + get + { + Color c = Color.Empty; + try + { + c = this.ColorFromObject(this.NativeHtmlDocument2.GetBgColor()); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return c; + } + set + { + int color = value.R << 16 | value.G << 8 | value.B; + this.NativeHtmlDocument2.SetBgColor(color); + } + } + + /// + /// + /// [To be supplied.] + /// + public Color ForeColor + { + get + { + Color c = Color.Empty; + try + { + c = this.ColorFromObject(this.NativeHtmlDocument2.GetFgColor()); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return c; + } + set + { + int color = value.R << 16 | value.G << 8 | value.B; + this.NativeHtmlDocument2.SetFgColor(color); + } + } + + /// + /// + /// [To be supplied.] + /// + public Color LinkColor + { + get + { + Color c = Color.Empty; + try + { + c = this.ColorFromObject(this.NativeHtmlDocument2.GetLinkColor()); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return c; + } + set + { + int color = value.R << 16 | value.G << 8 | value.B; + this.NativeHtmlDocument2.SetLinkColor(color); + } + } + + /// + /// + /// [To be supplied.] + /// + public Color ActiveLinkColor + { + get + { + Color c = Color.Empty; + try + { + c = this.ColorFromObject(this.NativeHtmlDocument2.GetAlinkColor()); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return c; + } + set + { + int color = value.R << 16 | value.G << 8 | value.B; + this.NativeHtmlDocument2.SetAlinkColor(color); + } + } + + /// + /// + /// [To be supplied.] + /// + public Color VisitedLinkColor + { + get + { + Color c = Color.Empty; + try + { + c = this.ColorFromObject(this.NativeHtmlDocument2.GetVlinkColor()); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return c; + } + set + { + int color = value.R << 16 | value.G << 8 | value.B; + this.NativeHtmlDocument2.SetVlinkColor(color); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Focused + { + get + { + return ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).HasFocus(); + } + } + + /// + /// + /// [To be supplied.] + /// + public object DomDocument + { + get + { + return this.NativeHtmlDocument2; + } + } + + /// + /// + /// [To be supplied.] + /// + public string Cookie + { + get + { + return this.NativeHtmlDocument2.GetCookie(); + } + set + { + this.NativeHtmlDocument2.SetCookie(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool RightToLeft + { + get + { + return ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetDir() == "rtl"; + } + set + { + ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).SetDir(value ? "rtl" : "ltr"); + } + } + + /// + /// [To be supplied.] + /// + public string Encoding + { + get + { + return this.NativeHtmlDocument2.GetCharset(); + } + set + { + this.NativeHtmlDocument2.SetCharset(value); + } + } + + /// + /// [To be supplied.] + /// + public string DefaultEncoding + { + get + { + return this.NativeHtmlDocument2.GetDefaultCharset(); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection All + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetAll(); + return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection Links + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetLinks(); + return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection Images + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetImages(); + return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection Forms + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlDocument2.GetForms(); + return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Write(string text) + { + object[] strs = new object[] { (object)text }; + this.NativeHtmlDocument2.Write(strs); + } + + /// + /// Executes a command on the document + /// + public void ExecCommand(string command, bool showUI, object value) + { + this.NativeHtmlDocument2.ExecCommand(command, showUI, value); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void Focus() + { + ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).Focus(); + // Seems to have a problem in really setting focus the first time + ((UnsafeNativeMethods.IHTMLDocument4)this.NativeHtmlDocument2).Focus(); + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement GetElementById(string id) + { + UnsafeNativeMethods.IHTMLElement iHTMLElement = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetElementById(id); + return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null; + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement GetElementFromPoint(Point point) + { + UnsafeNativeMethods.IHTMLElement iHTMLElement = this.NativeHtmlDocument2.ElementFromPoint(point.X, point.Y); + return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null; + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection GetElementsByTagName(string tagName) + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).GetElementsByTagName(tagName); + return iHTMLElementCollection != null ? new HtmlElementCollection(ShimManager, iHTMLElementCollection) : new HtmlElementCollection(ShimManager); + } + + /// + /// + /// [To be supplied.] + /// + public HtmlDocument OpenNew(bool replaceInHistory) + { + object name = (object)(replaceInHistory ? "replace" : ""); + object nullObject = null; + object ohtmlDocument = this.NativeHtmlDocument2.Open("text/html", name, nullObject, nullObject); + UnsafeNativeMethods.IHTMLDocument iHTMLDocument = ohtmlDocument as UnsafeNativeMethods.IHTMLDocument; + return iHTMLDocument != null ? new HtmlDocument(ShimManager, iHTMLDocument) : null; + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement CreateElement(string elementTag) + { + UnsafeNativeMethods.IHTMLElement iHTMLElement = this.NativeHtmlDocument2.CreateElement(elementTag); + return iHTMLElement != null ? new HtmlElement(ShimManager, iHTMLElement) : null; + } + + /// + /// + /// [To be supplied.] + /// + public object InvokeScript(string scriptName, object[] args) + { + object retVal = null; + NativeMethods.tagDISPPARAMS dp = new NativeMethods.tagDISPPARAMS(); + dp.rgvarg = IntPtr.Zero; + try + { + UnsafeNativeMethods.IDispatch scriptObject = this.NativeHtmlDocument2.GetScript() as UnsafeNativeMethods.IDispatch; + if (scriptObject != null) + { + Guid g = Guid.Empty; + string[] names = new string[] { scriptName }; + int[] dispids = new int[] { NativeMethods.ActiveX.DISPID_UNKNOWN }; + int hr = scriptObject.GetIDsOfNames(ref g, names, 1, + SafeNativeMethods.GetThreadLCID(), dispids); + if (NativeMethods.Succeeded(hr) && (dispids[0] != NativeMethods.ActiveX.DISPID_UNKNOWN)) + { + if (args != null) + { + // Reverse the arg order so that parms read naturally after IDispatch. (bug 187662) + Array.Reverse(args); + } + dp.rgvarg = (args == null) ? IntPtr.Zero : HtmlDocument.ArrayToVARIANTVector(args); + dp.cArgs = (args == null) ? 0 : args.Length; + dp.rgdispidNamedArgs = IntPtr.Zero; + dp.cNamedArgs = 0; + + object[] retVals = new object[1]; + + hr = scriptObject.Invoke(dispids[0], ref g, SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_METHOD, dp, + retVals, new NativeMethods.tagEXCEPINFO(), null); + if (hr == NativeMethods.S_OK) + { + retVal = retVals[0]; + } + } + } + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + finally + { + if (dp.rgvarg != IntPtr.Zero) + { + HtmlDocument.FreeVARIANTVector(dp.rgvarg, args.Length); + } + } + return retVal; + } + + /// + /// + /// [To be supplied.] + /// + public object InvokeScript(string scriptName) + { + return InvokeScript(scriptName, null); + } + + + /// + /// + /// [To be supplied.] + /// + public void AttachEventHandler(string eventName, EventHandler eventHandler) + { + HtmlDocumentShim shim = DocumentShim; + if (shim != null) + { + shim.AttachEventHandler(eventName, eventHandler); + } + } + + /// + /// + /// [To be supplied.] + /// + public void DetachEventHandler(string eventName, EventHandler eventHandler) + { + HtmlDocumentShim shim = DocumentShim; + if (shim != null) + { + shim.DetachEventHandler(eventName, eventHandler); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Click + { + add + { + DocumentShim.AddHandler(EventClick, value); + } + remove + { + DocumentShim.RemoveHandler(EventClick, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler ContextMenuShowing + { + add + { + DocumentShim.AddHandler(EventContextMenuShowing, value); + } + remove + { + DocumentShim.RemoveHandler(EventContextMenuShowing, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Focusing + { + add + { + DocumentShim.AddHandler(EventFocusing, value); + } + remove + { + DocumentShim.RemoveHandler(EventFocusing, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler LosingFocus + { + add + { + DocumentShim.AddHandler(EventLosingFocus, value); + } + remove + { + DocumentShim.RemoveHandler(EventLosingFocus, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseDown + { + add + { + DocumentShim.AddHandler(EventMouseDown, value); + } + remove + { + DocumentShim.RemoveHandler(EventMouseDown, value); + } + } + + /// + /// + /// Occurs when the mouse leaves the document + /// + public event HtmlElementEventHandler MouseLeave + { + add + { + DocumentShim.AddHandler(EventMouseLeave, value); + } + remove + { + DocumentShim.RemoveHandler(EventMouseLeave, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseMove + { + add + { + DocumentShim.AddHandler(EventMouseMove, value); + } + remove + { + DocumentShim.RemoveHandler(EventMouseMove, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseOver + { + add + { + DocumentShim.AddHandler(EventMouseOver, value); + } + remove + { + DocumentShim.RemoveHandler(EventMouseOver, value); + } + + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseUp + { + add + { + DocumentShim.AddHandler(EventMouseUp, value); + } + remove + { + DocumentShim.RemoveHandler(EventMouseUp, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Stop + { + add + { + DocumentShim.AddHandler(EventStop, value); + } + remove + { + DocumentShim.RemoveHandler(EventStop, value); + } + } + + // + // Private helper methods: + // + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct FindSizeOfVariant + { + [MarshalAs(UnmanagedType.Struct)] + public object var; + public byte b; + } + private static readonly int VariantSize = (int)Marshal.OffsetOf(typeof(FindSizeOfVariant), "b"); + // + // Convert a object[] into an array of VARIANT, allocated with CoTask allocators. + internal unsafe static IntPtr ArrayToVARIANTVector(object[] args) + { + int len = args.Length; + IntPtr mem = Marshal.AllocCoTaskMem(len * VariantSize); + byte* a = (byte*)(void*)mem; + for (int i = 0; i < len; ++i) + { + Marshal.GetNativeVariantForObject(args[i], (IntPtr)(a + VariantSize * i)); + } + return mem; + } + + // + // Free a Variant array created with the above function + internal unsafe static void FreeVARIANTVector(IntPtr mem, int len) + { + byte* a = (byte*)(void*)mem; + for (int i = 0; i < len; ++i) + { + SafeNativeMethods.VariantClear(new HandleRef(null, (IntPtr)(a + VariantSize * i))); + } + Marshal.FreeCoTaskMem(mem); + } + + private Color ColorFromObject(object oColor) + { + try + { + if (oColor is string) + { + string strColor = oColor as String; + int index = strColor.IndexOf('#'); + if (index >= 0) + { + // The string is of the form: #ff00a0. Skip past the # + string hexColor = strColor.Substring(index + 1); + // The actual color is non-transparent. So set alpha = 255. + return Color.FromArgb(255, Color.FromArgb(int.Parse(hexColor, NumberStyles.HexNumber, CultureInfo.InvariantCulture))); + } + else + { + return Color.FromName(strColor); + } + } + else if (oColor is int) + { + // The actual color is non-transparent. So set alpha = 255. + return Color.FromArgb(255, Color.FromArgb((int)oColor)); + } + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + + return Color.Empty; + } + + + /// + /// HtmlDocumentShim - this is the glue between the DOM eventing mechanisms + /// and our CLR callbacks. + /// + /// There are two kinds of events: HTMLWindowEvents2 and IHtmlWindow3.AttachHandler style + /// HTMLDocumentEvents2: we create an IConnectionPoint (via ConnectionPointCookie) between us and MSHTML and it calls back + /// on our an instance of HTMLDocumentEvents2. The HTMLDocumentEvents2 class then fires the event. + /// + /// IHTMLDocument3.AttachHandler: MSHML calls back on an HtmlToClrEventProxy that we've created, looking + /// for a method named DISPID=0. For each event that's subscribed, we create + /// a new HtmlToClrEventProxy, detect the callback and fire the corresponding + /// CLR event. + /// + internal class HtmlDocumentShim : HtmlShim + { + private AxHost.ConnectionPointCookie cookie; + private HtmlDocument htmlDocument; + private UnsafeNativeMethods.IHTMLWindow2 associatedWindow = null; + + internal HtmlDocumentShim(HtmlDocument htmlDocument) + { + this.htmlDocument = htmlDocument; + // snap our associated window so we know when to disconnect. + if (this.htmlDocument != null) + { + HtmlWindow window = htmlDocument.Window; + if (window != null) + { + associatedWindow = window.NativeHtmlWindow; + } + } + } + + public override UnsafeNativeMethods.IHTMLWindow2 AssociatedWindow + { + get { return associatedWindow; } + } + + public UnsafeNativeMethods.IHTMLDocument2 NativeHtmlDocument2 + { + get { return htmlDocument.NativeHtmlDocument2; } + } + + internal HtmlDocument Document + { + get { return htmlDocument; } + } + + /// Support IHtmlDocument3.AttachHandler + public override void AttachEventHandler(string eventName, EventHandler eventHandler) + { + + // IE likes to call back on an IDispatch of DISPID=0 when it has an event, + // the HtmlToClrEventProxy helps us fake out the CLR so that we can call back on + // our EventHandler properly. + + HtmlToClrEventProxy proxy = AddEventProxy(eventName, eventHandler); + bool success = ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).AttachEvent(eventName, proxy); + Debug.Assert(success, "failed to add event"); + } + + /// Support IHtmlDocument3.DetachHandler + public override void DetachEventHandler(string eventName, EventHandler eventHandler) + { + HtmlToClrEventProxy proxy = RemoveEventProxy(eventHandler); + if (proxy != null) + { + ((UnsafeNativeMethods.IHTMLDocument3)this.NativeHtmlDocument2).DetachEvent(eventName, proxy); + } + + } + + // + // Connect to standard events + // + public override void ConnectToEvents() + { + if (cookie == null || !cookie.Connected) + { + this.cookie = new AxHost.ConnectionPointCookie(this.NativeHtmlDocument2, + new HTMLDocumentEvents2(htmlDocument), + typeof(UnsafeNativeMethods.DHTMLDocumentEvents2), + /*throwException*/ false); + if (!cookie.Connected) + { + cookie = null; + } + } + } + + // + // Disconnect from standard events + // + public override void DisconnectFromEvents() + { + if (this.cookie != null) + { + this.cookie.Disconnect(); + this.cookie = null; + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + if (htmlDocument != null) + { + Marshal.FinalReleaseComObject(htmlDocument.NativeHtmlDocument2); + } + htmlDocument = null; + } + + } + + protected override object GetEventSender() + { + return htmlDocument; + } + } + + // + // Private classes: + // + [ClassInterface(ClassInterfaceType.None)] + private class HTMLDocumentEvents2 : StandardOleMarshalObject, /*Enforce calling back on the same thread*/ + UnsafeNativeMethods.DHTMLDocumentEvents2 + { + private HtmlDocument parent; + + public HTMLDocumentEvents2(HtmlDocument htmlDocument) + { + this.parent = htmlDocument; + } + + private void FireEvent(object key, EventArgs e) + { + if (this.parent != null) + { + parent.DocumentShim.FireEvent(key, e); + } + } + + public bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventClick, e); + + return e.ReturnValue; + } + + public bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventContextMenuShowing, e); + return e.ReturnValue; + } + + public void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventFocusing, e); + } + + public void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventLosingFocus, e); + } + + public void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventMouseMove, e); + } + + public void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventMouseDown, e); + } + + public void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventMouseLeave, e); + } + + public void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventMouseOver, e); + } + + public void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventMouseUp, e); + } + + public bool onstop(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlDocument.EventStop, e); + return e.ReturnValue; + } + + public bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onbeforeeditfocus(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onselectionchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + } + + #region operators + + /// + [SuppressMessage("Microsoft.Design", "CA1046:DoNotOverrideOperatorEqualsOnReferenceTypes")] + public static bool operator ==(HtmlDocument left, HtmlDocument right) + { + //Not equal if only one's null. + if (object.ReferenceEquals(left, null) != object.ReferenceEquals(right, null)) + { + return false; + } + + //Equal if both are null. + if (object.ReferenceEquals(left, null)) + { + return true; + } + + //Neither are null. Get the IUnknowns and compare them. + IntPtr leftPtr = IntPtr.Zero; + IntPtr rightPtr = IntPtr.Zero; + try + { + leftPtr = Marshal.GetIUnknownForObject(left.NativeHtmlDocument2); + rightPtr = Marshal.GetIUnknownForObject(right.NativeHtmlDocument2); + return leftPtr == rightPtr; + } + finally + { + if (leftPtr != IntPtr.Zero) + { + Marshal.Release(leftPtr); + } + if (rightPtr != IntPtr.Zero) + { + Marshal.Release(rightPtr); + } + } + } + + /// + public static bool operator !=(HtmlDocument left, HtmlDocument right) + { + return !(left == right); + } + + /// + public override int GetHashCode() + { + return htmlDocument2 == null ? 0 : htmlDocument2.GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + return (this == (HtmlDocument)obj); + } + #endregion + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElement.cs b/WindowsForms/Managed/System/WinForms/HtmlElement.cs new file mode 100644 index 000000000..ec7f2724e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElement.cs @@ -0,0 +1,1602 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Printing; +using System.IO; +using System.Net; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; + +namespace System.Windows.Forms +{ + /// + /// + /// [To be supplied.] + /// + [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")] + public sealed class HtmlElement + { + internal static readonly object EventClick = new object(); + internal static readonly object EventDoubleClick = new object(); + internal static readonly object EventDrag = new object(); + internal static readonly object EventDragEnd = new object(); + internal static readonly object EventDragLeave = new object(); + internal static readonly object EventDragOver = new object(); + internal static readonly object EventFocusing = new object(); + internal static readonly object EventGotFocus = new object(); + internal static readonly object EventLosingFocus = new object(); + internal static readonly object EventLostFocus = new object(); + internal static readonly object EventKeyDown = new object(); + internal static readonly object EventKeyPress = new object(); + internal static readonly object EventKeyUp = new object(); + internal static readonly object EventMouseDown = new object(); + internal static readonly object EventMouseEnter = new object(); + internal static readonly object EventMouseLeave = new object(); + internal static readonly object EventMouseMove = new object(); + internal static readonly object EventMouseOver = new object(); + internal static readonly object EventMouseUp = new object(); + + private UnsafeNativeMethods.IHTMLElement htmlElement; + private HtmlShimManager shimManager; + + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + internal HtmlElement(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLElement element) + { + this.htmlElement = element; + Debug.Assert(this.NativeHtmlElement != null, "The element object should implement IHTMLElement"); + + this.shimManager = shimManager; + + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection All + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlElement.GetAll() as UnsafeNativeMethods.IHTMLElementCollection; + return iHTMLElementCollection != null ? new HtmlElementCollection(shimManager, iHTMLElementCollection) : new HtmlElementCollection(shimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection Children + { + get + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = this.NativeHtmlElement.GetChildren() as UnsafeNativeMethods.IHTMLElementCollection; + return iHTMLElementCollection != null ? new HtmlElementCollection(shimManager, iHTMLElementCollection) : new HtmlElementCollection(shimManager); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool CanHaveChildren + { + get + { + return ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).CanHaveChildren(); + } + } + + /// + /// + /// [To be supplied.] + /// + public Rectangle ClientRectangle + { + get + { + UnsafeNativeMethods.IHTMLElement2 htmlElement2 = (UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement; + return new Rectangle(htmlElement2.ClientLeft(), htmlElement2.ClientTop(), + htmlElement2.ClientWidth(), htmlElement2.ClientHeight()); + } + } + + + /// + /// + /// [To be supplied.] + /// + public HtmlDocument Document + { + get + { + UnsafeNativeMethods.IHTMLDocument iHTMLDocument = this.NativeHtmlElement.GetDocument() as UnsafeNativeMethods.IHTMLDocument; + return iHTMLDocument != null ? new HtmlDocument(shimManager, iHTMLDocument) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Enabled + { + get + { + return !(((UnsafeNativeMethods.IHTMLElement3)this.NativeHtmlElement).GetDisabled()); + } + set + { + ((UnsafeNativeMethods.IHTMLElement3)this.NativeHtmlElement).SetDisabled(!value); + } + } + + private HtmlElementShim ElementShim + { + get + { + if (ShimManager != null) + { + HtmlElementShim shim = ShimManager.GetElementShim(this); + if (shim == null) + { + shimManager.AddElementShim(this); + shim = ShimManager.GetElementShim(this); + } + return shim; + } + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement FirstChild + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = null; + UnsafeNativeMethods.IHTMLDOMNode iHtmlDomNode = this.NativeHtmlElement as UnsafeNativeMethods.IHTMLDOMNode; + + if( iHtmlDomNode != null ) + { + iHtmlElement = iHtmlDomNode.FirstChild() as UnsafeNativeMethods.IHTMLElement; + } + return iHtmlElement != null ? new HtmlElement(shimManager, iHtmlElement) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public string Id + { + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + get + { + return this.NativeHtmlElement.GetId(); + } + set + { + this.NativeHtmlElement.SetId(value); + } + } + + + /// + /// + /// [To be supplied.] + /// + public string InnerHtml + { + get + { + return this.NativeHtmlElement.GetInnerHTML(); + } + set + { + try + { + this.NativeHtmlElement.SetInnerHTML(value); + } + catch (COMException ex) + { + if (ex.ErrorCode == unchecked((int)0x800a0258)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementPropertyNotSupported)); + } + throw; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public string InnerText + { + get + { + return this.NativeHtmlElement.GetInnerText(); + } + set + { + try + { + this.NativeHtmlElement.SetInnerText(value); + } + catch (COMException ex) + { + if (ex.ErrorCode == unchecked((int)0x800a0258)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementPropertyNotSupported)); + } + throw; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public string Name + { + get + { + return this.GetAttribute("Name"); + } + set + { + this.SetAttribute("Name", value); + } + } + + + private UnsafeNativeMethods.IHTMLElement NativeHtmlElement + { + get + { + return this.htmlElement; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement NextSibling + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = null; + UnsafeNativeMethods.IHTMLDOMNode iHtmlDomNode = this.NativeHtmlElement as UnsafeNativeMethods.IHTMLDOMNode; + + if( iHtmlDomNode != null ) + { + iHtmlElement = iHtmlDomNode.NextSibling() as UnsafeNativeMethods.IHTMLElement; + } + return iHtmlElement != null ? new HtmlElement(shimManager, iHtmlElement) : null; + } + } + + + /// + /// + /// [To be supplied.] + /// + public Rectangle OffsetRectangle + { + get + { + return new Rectangle(this.NativeHtmlElement.GetOffsetLeft(), this.NativeHtmlElement.GetOffsetTop(), + this.NativeHtmlElement.GetOffsetWidth(), this.NativeHtmlElement.GetOffsetHeight()); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement OffsetParent + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlElement.GetOffsetParent(); + return iHtmlElement != null ? new HtmlElement(shimManager, iHtmlElement) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public string OuterHtml + { + get + { + return this.NativeHtmlElement.GetOuterHTML(); + } + set + { + try + { + this.NativeHtmlElement.SetOuterHTML(value); + } + catch (COMException ex) + { + if (ex.ErrorCode == unchecked((int)0x800a0258)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementPropertyNotSupported)); + } + throw; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public string OuterText + { + get + { + return this.NativeHtmlElement.GetOuterText(); + } + set + { + try + { + this.NativeHtmlElement.SetOuterText(value); + } + catch (COMException ex) + { + if (ex.ErrorCode == unchecked((int)0x800a0258)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementPropertyNotSupported)); + } + throw; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement Parent + { + get + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = this.NativeHtmlElement.GetParentElement(); + return iHtmlElement != null ? new HtmlElement(shimManager, iHtmlElement) : null; + } + } + + + + /// + /// + /// [To be supplied.] + /// + public Rectangle ScrollRectangle + { + get + { + UnsafeNativeMethods.IHTMLElement2 htmlElement2 = (UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement; + return new Rectangle(htmlElement2.GetScrollLeft(), htmlElement2.GetScrollTop(), + htmlElement2.GetScrollWidth(), htmlElement2.GetScrollHeight()); + } + } + + /// + /// + /// [To be supplied.] + /// + public int ScrollLeft + { + get + { + return ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).GetScrollLeft(); + } + set + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).SetScrollLeft(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public int ScrollTop + { + get + { + return ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).GetScrollTop(); + } + set + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).SetScrollTop(value); + } + } + + private HtmlShimManager ShimManager + { + get + { + return shimManager; + } + } + + /// + /// + /// [To be supplied.] + /// + public string Style + { + get + { + return this.NativeHtmlElement.GetStyle().GetCssText(); + } + set + { + this.NativeHtmlElement.GetStyle().SetCssText(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public string TagName + { + get + { + return this.NativeHtmlElement.GetTagName(); + } + } + + /// + /// + /// [To be supplied.] + /// + public short TabIndex + { + get + { + return ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).GetTabIndex(); + } + set + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).SetTabIndex(value); + } + } + + + /// + /// + /// [To be supplied.] + /// + public object DomElement + { + get + { + return this.NativeHtmlElement; + } + } + + + /// + /// + /// [To be supplied.] + /// + public HtmlElement AppendChild(HtmlElement newElement) + { + return this.InsertAdjacentElement(HtmlElementInsertionOrientation.BeforeEnd, newElement); + } + + /// + /// + /// [To be supplied.] + /// + public void AttachEventHandler(string eventName, EventHandler eventHandler) + { + ElementShim.AttachEventHandler(eventName, eventHandler); + } + + + /// + /// + /// [To be supplied.] + /// + public void DetachEventHandler(string eventName, EventHandler eventHandler) + { + ElementShim.DetachEventHandler(eventName, eventHandler); + } + + /// + /// + /// [To be supplied.] + /// + public void Focus() + { + try + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).Focus(); + } + catch (COMException ex) + { + if (ex.ErrorCode == unchecked((int)0x800a083e)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementMethodNotSupported)); + } + throw; + } + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public string GetAttribute(string attributeName) + { + object oAttributeValue = this.NativeHtmlElement.GetAttribute(attributeName, 0); + return oAttributeValue == null ? "" : oAttributeValue.ToString(); + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection GetElementsByTagName(string tagName) + { + UnsafeNativeMethods.IHTMLElementCollection iHTMLElementCollection = ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).GetElementsByTagName(tagName); + return iHTMLElementCollection != null ? new HtmlElementCollection(shimManager, iHTMLElementCollection) : new HtmlElementCollection(shimManager); + } + /// + /// + /// [To be supplied.] + /// + public HtmlElement InsertAdjacentElement(HtmlElementInsertionOrientation orient, HtmlElement newElement) + { + UnsafeNativeMethods.IHTMLElement iHtmlElement = ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).InsertAdjacentElement(orient.ToString(), + (UnsafeNativeMethods.IHTMLElement)newElement.DomElement); + return iHtmlElement != null ? new HtmlElement(shimManager, iHtmlElement) : null; + } + + /// + /// + /// [To be supplied.] + /// + public object InvokeMember(string methodName) + { + return InvokeMember(methodName, null); + } + + /// + /// + /// [To be supplied.] + /// + public object InvokeMember(string methodName, params object[] parameter) + { + object retVal = null; + NativeMethods.tagDISPPARAMS dp = new NativeMethods.tagDISPPARAMS(); + dp.rgvarg = IntPtr.Zero; + try + { + UnsafeNativeMethods.IDispatch scriptObject = this.NativeHtmlElement as UnsafeNativeMethods.IDispatch; + if (scriptObject != null) + { + Guid g = Guid.Empty; + string[] names = new string[] { methodName }; + int[] dispids = new int[] { NativeMethods.ActiveX.DISPID_UNKNOWN }; + int hr = scriptObject.GetIDsOfNames(ref g, names, 1, + SafeNativeMethods.GetThreadLCID(), dispids); + if (NativeMethods.Succeeded(hr) && (dispids[0] != NativeMethods.ActiveX.DISPID_UNKNOWN)) + { + // Reverse the arg order below so that parms are read properly thru IDispatch. (bug 187662) + if (parameter != null) + { + // Reverse the parm order so that they read naturally after IDispatch. (bug 187662) + Array.Reverse(parameter); + } + dp.rgvarg = (parameter == null) ? IntPtr.Zero : HtmlDocument.ArrayToVARIANTVector(parameter); + dp.cArgs = (parameter == null) ? 0 : parameter.Length; + dp.rgdispidNamedArgs = IntPtr.Zero; + dp.cNamedArgs = 0; + + object[] retVals = new object[1]; + + hr = scriptObject.Invoke(dispids[0], ref g, SafeNativeMethods.GetThreadLCID(), + NativeMethods.DISPATCH_METHOD, dp, + retVals, new NativeMethods.tagEXCEPINFO(), null); + if (hr == NativeMethods.S_OK) + { + retVal = retVals[0]; + } + } + } + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + finally + { + if (dp.rgvarg != IntPtr.Zero) + { + HtmlDocument.FreeVARIANTVector(dp.rgvarg, parameter.Length); + } + } + return retVal; + } + + + /// + /// + /// [To be supplied.] + /// + public void RemoveFocus() + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).Blur(); + } + + /// + /// + /// [To be supplied.] + /// + // PM review done + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] + public void RaiseEvent(string eventName) + { + ((UnsafeNativeMethods.IHTMLElement3)this.NativeHtmlElement).FireEvent(eventName, IntPtr.Zero); + } + + + /// + /// + /// [To be supplied.] + /// + public void ScrollIntoView(bool alignWithTop) + { + this.NativeHtmlElement.ScrollIntoView((object)alignWithTop); + } + + /// + /// + /// [To be supplied.] + /// + public void SetAttribute(string attributeName, string value) + { + try + { + this.NativeHtmlElement.SetAttribute(attributeName, (object)value, 0); + } + catch (COMException comException) + { + if (comException.ErrorCode == unchecked((int)0x80020009)) + { + throw new NotSupportedException(SR.GetString(SR.HtmlElementAttributeNotSupported)); + } + throw; + } + } + + + // + // Events: + // + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Click + { + add + { + ElementShim.AddHandler(EventClick, value); + } + remove + { + ElementShim.RemoveHandler(EventClick, value); + } + + } + + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler DoubleClick + { + add + { + ElementShim.AddHandler(EventDoubleClick, value); + } + remove + { + ElementShim.RemoveHandler(EventDoubleClick, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Drag + { + add + { + ElementShim.AddHandler(EventDrag, value); + } + remove + { + ElementShim.RemoveHandler(EventDrag, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler DragEnd + { + add + { + ElementShim.AddHandler(EventDragEnd, value); + } + remove + { + ElementShim.RemoveHandler(EventDragEnd, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler DragLeave + { + add + { + ElementShim.AddHandler(EventDragLeave, value); + } + remove + { + ElementShim.RemoveHandler(EventDragLeave, value); + } + + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler DragOver + { + add + { + ElementShim.AddHandler(EventDragOver, value); + } + remove + { + ElementShim.RemoveHandler(EventDragOver, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Focusing + { + add + { + ElementShim.AddHandler(EventFocusing, value); + } + remove + { + ElementShim.RemoveHandler(EventFocusing, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler GotFocus + { + add + { + ElementShim.AddHandler(EventGotFocus, value); + } + remove + { + ElementShim.RemoveHandler(EventGotFocus, value); + } + + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler LosingFocus + { + add + { + ElementShim.AddHandler(EventLosingFocus, value); + } + remove + { + ElementShim.RemoveHandler(EventLosingFocus, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler LostFocus + { + add + { + ElementShim.AddHandler(EventLostFocus, value); + } + remove + { + ElementShim.RemoveHandler(EventLostFocus, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler KeyDown + { + add + { + ElementShim.AddHandler(EventKeyDown, value); + } + remove + { + ElementShim.RemoveHandler(EventKeyDown, value); + } + } + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler KeyPress + { + add + { + ElementShim.AddHandler(EventKeyPress, value); + } + remove + { + ElementShim.RemoveHandler(EventKeyPress, value); + } + + } + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler KeyUp + { + add + { + ElementShim.AddHandler(EventKeyUp, value); + } + remove + { + ElementShim.RemoveHandler(EventKeyUp, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseMove + { + add + { + ElementShim.AddHandler(EventMouseMove, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseMove, value); + } + } + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseDown + { + add + { + ElementShim.AddHandler(EventMouseDown, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseDown, value); + } + } + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseOver + { + add + { + ElementShim.AddHandler(EventMouseOver, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseOver, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler MouseUp + { + add + { + ElementShim.AddHandler(EventMouseUp, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseUp, value); + } + } + + /// + /// + /// Fires when the mouse enters the element + /// + public event HtmlElementEventHandler MouseEnter + { + add + { + ElementShim.AddHandler(EventMouseEnter, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseEnter, value); + } + } + + /// + /// + /// Fires when the mouse leaves the element + /// + public event HtmlElementEventHandler MouseLeave + { + add + { + ElementShim.AddHandler(EventMouseLeave, value); + } + remove + { + ElementShim.RemoveHandler(EventMouseLeave, value); + } + } + + // + // Private classes: + // + [ClassInterface(ClassInterfaceType.None)] + private class HTMLElementEvents2 : StandardOleMarshalObject, /*Enforce calling back on the same thread*/ + UnsafeNativeMethods.DHTMLElementEvents2, + UnsafeNativeMethods.DHTMLAnchorEvents2, + UnsafeNativeMethods.DHTMLAreaEvents2, + UnsafeNativeMethods.DHTMLButtonElementEvents2, + UnsafeNativeMethods.DHTMLControlElementEvents2, + UnsafeNativeMethods.DHTMLFormElementEvents2, + UnsafeNativeMethods.DHTMLFrameSiteEvents2, + UnsafeNativeMethods.DHTMLImgEvents2, + UnsafeNativeMethods.DHTMLInputFileElementEvents2, + UnsafeNativeMethods.DHTMLInputImageEvents2, + UnsafeNativeMethods.DHTMLInputTextElementEvents2, + UnsafeNativeMethods.DHTMLLabelEvents2, + UnsafeNativeMethods.DHTMLLinkElementEvents2, + UnsafeNativeMethods.DHTMLMapEvents2, + UnsafeNativeMethods.DHTMLMarqueeElementEvents2, + UnsafeNativeMethods.DHTMLOptionButtonElementEvents2, + UnsafeNativeMethods.DHTMLSelectElementEvents2, + UnsafeNativeMethods.DHTMLStyleElementEvents2, + UnsafeNativeMethods.DHTMLTableEvents2, + UnsafeNativeMethods.DHTMLTextContainerEvents2, + UnsafeNativeMethods.DHTMLScriptEvents2 + { + + private HtmlElement parent; + + public HTMLElementEvents2(HtmlElement htmlElement) + { + this.parent = htmlElement; + } + private void FireEvent(object key, EventArgs e) + { + if (this.parent != null) + { + parent.ElementShim.FireEvent(key, e); + } + } + + + public bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventClick, e); + return e.ReturnValue; + } + + public bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventDoubleClick, e); + return e.ReturnValue; + } + + public bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventKeyPress, e); + return e.ReturnValue; + } + + public void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventKeyDown, e); + } + + public void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventKeyUp, e); + } + + public void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseOver, e); + } + + public void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseMove, e); + } + + public void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseDown, e); + } + + public void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseUp, e); + } + + public void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseEnter, e); + } + + public void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventMouseLeave, e); + } + + public bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventGotFocus, e); + } + + public bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventDrag, e); + return e.ReturnValue; + } + + public void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventDragEnd, e); + } + + public void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventDragLeave, e); + } + + public bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventDragOver, e); + return e.ReturnValue; + } + + public void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventFocusing, e); + } + + public void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventLosingFocus, e); + } + + public void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlElement.EventLostFocus, e); + } + + public void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onchange(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onselect(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onload(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onabort(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public bool onsubmit(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public bool onreset(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onchange_void(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onbounce(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onfinish(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onstart(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + } + + + /// + /// HtmlElementShim - this is the glue between the DOM eventing mechanisms + /// and our CLR callbacks. + /// + /// HTMLElementEvents2: we create an IConnectionPoint (via ConnectionPointCookie) between us and MSHTML and it calls back + /// on our an instance of HTMLElementEvents2. The HTMLElementEvents2 class then fires the event. + /// + /// + internal class HtmlElementShim : HtmlShim + { + + private static Type[] dispInterfaceTypes = {typeof(UnsafeNativeMethods.DHTMLElementEvents2), + typeof(UnsafeNativeMethods.DHTMLAnchorEvents2), + typeof(UnsafeNativeMethods.DHTMLAreaEvents2), + typeof(UnsafeNativeMethods.DHTMLButtonElementEvents2), + typeof(UnsafeNativeMethods.DHTMLControlElementEvents2), + typeof(UnsafeNativeMethods.DHTMLFormElementEvents2), + typeof(UnsafeNativeMethods.DHTMLFrameSiteEvents2), + typeof(UnsafeNativeMethods.DHTMLImgEvents2), + typeof(UnsafeNativeMethods.DHTMLInputFileElementEvents2), + typeof(UnsafeNativeMethods.DHTMLInputImageEvents2), + typeof(UnsafeNativeMethods.DHTMLInputTextElementEvents2), + typeof(UnsafeNativeMethods.DHTMLLabelEvents2), + typeof(UnsafeNativeMethods.DHTMLLinkElementEvents2), + typeof(UnsafeNativeMethods.DHTMLMapEvents2), + typeof(UnsafeNativeMethods.DHTMLMarqueeElementEvents2), + typeof(UnsafeNativeMethods.DHTMLOptionButtonElementEvents2), + typeof(UnsafeNativeMethods.DHTMLSelectElementEvents2), + typeof(UnsafeNativeMethods.DHTMLStyleElementEvents2), + typeof(UnsafeNativeMethods.DHTMLTableEvents2), + typeof(UnsafeNativeMethods.DHTMLTextContainerEvents2), + typeof(UnsafeNativeMethods.DHTMLScriptEvents2)}; + + private AxHost.ConnectionPointCookie cookie; // To hook up events from the native HtmlElement + private HtmlElement htmlElement; + private UnsafeNativeMethods.IHTMLWindow2 associatedWindow = null; + + public HtmlElementShim(HtmlElement element) + { + this.htmlElement = element; + + // snap our associated window so we know when to disconnect. + if (this.htmlElement != null) + { + HtmlDocument doc = this.htmlElement.Document; + if (doc != null) + { + HtmlWindow window = doc.Window; + if (window != null) + { + associatedWindow = window.NativeHtmlWindow; + } + } + } + } + + public UnsafeNativeMethods.IHTMLElement NativeHtmlElement + { + get { return htmlElement.NativeHtmlElement; } + } + + internal HtmlElement Element + { + get { return htmlElement; } + } + + public override UnsafeNativeMethods.IHTMLWindow2 AssociatedWindow + { + get { return associatedWindow; } + } + + /// Support IHTMLElement2.AttachEventHandler + public override void AttachEventHandler(string eventName, System.EventHandler eventHandler) + { + + // IE likes to call back on an IDispatch of DISPID=0 when it has an event, + // the HtmlToClrEventProxy helps us fake out the CLR so that we can call back on + // our EventHandler properly. + + HtmlToClrEventProxy proxy = AddEventProxy(eventName, eventHandler); + bool success = ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).AttachEvent(eventName, proxy); + Debug.Assert(success, "failed to add event"); + } + + public override void ConnectToEvents() + { + if (cookie == null || !cookie.Connected) + { + for (int i = 0; i < dispInterfaceTypes.Length && this.cookie == null; i++) + { + this.cookie = new AxHost.ConnectionPointCookie(this.NativeHtmlElement, + new HTMLElementEvents2(htmlElement), + dispInterfaceTypes[i], + /*throwException*/ false); + if (!cookie.Connected) + { + cookie = null; + } + } + } + } + + /// Support IHTMLElement2.DetachHandler + public override void DetachEventHandler(string eventName, System.EventHandler eventHandler) + { + HtmlToClrEventProxy proxy = RemoveEventProxy(eventHandler); + if (proxy != null) + { + ((UnsafeNativeMethods.IHTMLElement2)this.NativeHtmlElement).DetachEvent(eventName, proxy); + } + } + + + public override void DisconnectFromEvents() + { + if (this.cookie != null) + { + this.cookie.Disconnect(); + this.cookie = null; + } + + } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (htmlElement != null) + { + Marshal.FinalReleaseComObject(htmlElement.NativeHtmlElement); + } + htmlElement = null; + } + + protected override object GetEventSender() + { + return htmlElement; + } + + } + + #region operators + /// + [SuppressMessage("Microsoft.Design", "CA1046:DoNotOverrideOperatorEqualsOnReferenceTypes")] + public static bool operator ==(HtmlElement left, HtmlElement right) + { + //Not equal if only one's null. + if (object.ReferenceEquals(left, null) != object.ReferenceEquals(right, null)) + { + return false; + } + + //Equal if both are null. + if (object.ReferenceEquals(left, null)) + { + return true; + } + + //Neither are null. Get the IUnknowns and compare them. + IntPtr leftPtr = IntPtr.Zero; + IntPtr rightPtr = IntPtr.Zero; + try + { + leftPtr = Marshal.GetIUnknownForObject(left.NativeHtmlElement); + rightPtr = Marshal.GetIUnknownForObject(right.NativeHtmlElement); + return leftPtr == rightPtr; + } + finally + { + if (leftPtr != IntPtr.Zero) + { + Marshal.Release(leftPtr); + } + if (rightPtr != IntPtr.Zero) + { + Marshal.Release(rightPtr); + } + } + } + + /// + public static bool operator !=(HtmlElement left, HtmlElement right) + { + return !(left == right); + } + + /// + public override int GetHashCode() + { + return htmlElement == null ? 0 : htmlElement.GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + //If obj isn't an HtmlElement, we want Equals to return false. this will + //never be null, so now it will return false as expected (not throw). + return this == (obj as HtmlElement); + } + #endregion + + + + } + + /// + /// + /// [To be supplied.] + /// + public enum HtmlElementInsertionOrientation + { + /// + BeforeBegin, + /// + AfterBegin, + /// + BeforeEnd, + /// + AfterEnd + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElementCollection.cs b/WindowsForms/Managed/System/WinForms/HtmlElementCollection.cs new file mode 100644 index 000000000..c12cbe2f1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElementCollection.cs @@ -0,0 +1,190 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Security.Permissions; +using System.Security; +using System.Runtime.InteropServices; +using System.Net; +using System.Collections; + +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + public sealed class HtmlElementCollection : ICollection { + private UnsafeNativeMethods.IHTMLElementCollection htmlElementCollection; + private HtmlElement[] elementsArray; + private HtmlShimManager shimManager; + + internal HtmlElementCollection(HtmlShimManager shimManager) { + this.htmlElementCollection = null; + this.elementsArray = null; + + this.shimManager = shimManager; + } + + internal HtmlElementCollection(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLElementCollection elements) { + this.htmlElementCollection = elements; + this.elementsArray = null; + this.shimManager = shimManager; + Debug.Assert(this.NativeHtmlElementCollection != null, "The element collection object should implement IHTMLElementCollection"); + } + + internal HtmlElementCollection(HtmlShimManager shimManager, HtmlElement[] array) { + this.htmlElementCollection = null; + this.elementsArray = array; + this.shimManager = shimManager; + } + + private UnsafeNativeMethods.IHTMLElementCollection NativeHtmlElementCollection { + get { + return this.htmlElementCollection; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement this[int index] { + get { + //do some bounds checking here... + if (index < 0 || index >= this.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidBoundArgument, "index", index, 0, this.Count - 1)); + } + + if (this.NativeHtmlElementCollection != null) { + UnsafeNativeMethods.IHTMLElement htmlElement = + this.NativeHtmlElementCollection.Item((object)index, (object)0) as UnsafeNativeMethods.IHTMLElement; + return (htmlElement != null) ? new HtmlElement(shimManager, htmlElement) : null; + } + else if (elementsArray != null) { + return this.elementsArray[index]; + } + else { + return null; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement this[string elementId] { + get { + if (this.NativeHtmlElementCollection != null) { + UnsafeNativeMethods.IHTMLElement htmlElement = + this.NativeHtmlElementCollection.Item((object)elementId, (object)0) as UnsafeNativeMethods.IHTMLElement; + return (htmlElement != null) ? new HtmlElement(shimManager, htmlElement) : null; + } + else if (elementsArray != null) { + int count = this.elementsArray.Length; + for (int i = 0; i < count; i++) { + HtmlElement element = this.elementsArray[i]; + if (element.Id == elementId) { + return element; + } + } + return null; // not found + } + else { + return null; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElementCollection GetElementsByName(string name) { + int count = this.Count; + HtmlElement[] temp = new HtmlElement[count]; // count is the maximum # of matches + int tempIndex = 0; + + for (int i = 0; i < count; i++) { + HtmlElement element = this[i]; + if (element.GetAttribute("name") == name) { + temp[tempIndex] = element; + tempIndex++; + } + } + + if (tempIndex == 0) { + return new HtmlElementCollection(shimManager); + } + else { + HtmlElement[] elements = new HtmlElement[tempIndex]; + for (int i = 0; i < tempIndex; i++) { + elements[i] = temp[i]; + } + return new HtmlElementCollection(shimManager, elements); + } + } + + /// + /// + /// Returns the total number of elements in the collection. + /// + public int Count { + get { + if (this.NativeHtmlElementCollection != null) { + return this.NativeHtmlElementCollection.GetLength(); + } + else if (elementsArray != null) { + return this.elementsArray.Length; + } + else { + return 0; + } + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + int count = this.Count; + for (int i = 0; i < count; i++) { + dest.SetValue(this[i], index++); + } + } + + /// + /// + public IEnumerator GetEnumerator() { + HtmlElement[] htmlElements = new HtmlElement[this.Count]; + ((ICollection)this).CopyTo(htmlElements, 0); + + return htmlElements.GetEnumerator(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventArgs.cs b/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventArgs.cs new file mode 100644 index 000000000..9d223126a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventArgs.cs @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; + +namespace System.Windows.Forms { + + /// + /// + /// EventArgs for onerror event of HtmlElement + /// + public sealed class HtmlElementErrorEventArgs : EventArgs { + private string description; + private string urlString; + private Uri url; + private int lineNumber; + private bool handled; + + internal HtmlElementErrorEventArgs(string description, string urlString, int lineNumber) + { + this.description = description; + this.urlString = urlString; + this.lineNumber = lineNumber; + } + + /// + /// + /// Description of error + /// + public string Description + { + get + { + return description; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// event was handled. + /// + /// + public bool Handled { + get { + return handled; + } + set { + handled = value; + } + } + + /// + /// + /// Line number where error occurred + /// + public int LineNumber + { + get + { + return lineNumber; + } + } + + /// + /// + /// Url where error occurred + /// + public Uri Url + { + get + { + if (url == null) + { + url = new Uri(urlString); + } + return url; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventHandler.cs b/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventHandler.cs new file mode 100644 index 000000000..e3d09540e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElementErrorEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; + +namespace System.Windows.Forms { + + /// + /// + /// EventHandler for HtmlElementErrorEventArgs + /// + public delegate void HtmlElementErrorEventHandler(object sender, HtmlElementErrorEventArgs e); + +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElementEventArgs.cs b/WindowsForms/Managed/System/WinForms/HtmlElementEventArgs.cs new file mode 100644 index 000000000..af17af08f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElementEventArgs.cs @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Net; + +namespace System.Windows.Forms { + + /// + /// + /// [To be supplied.] + /// + public sealed class HtmlElementEventArgs : EventArgs { + private UnsafeNativeMethods.IHTMLEventObj htmlEventObj; + private HtmlShimManager shimManager; + + internal HtmlElementEventArgs(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLEventObj eventObj) { + this.htmlEventObj = eventObj; + Debug.Assert(this.NativeHTMLEventObj != null, "The event object should implement IHTMLEventObj"); + + this.shimManager = shimManager; + } + + private UnsafeNativeMethods.IHTMLEventObj NativeHTMLEventObj { + get { + return this.htmlEventObj; + } + } + + /// + /// + /// [To be supplied.] + /// + public MouseButtons MouseButtonsPressed { + get { + MouseButtons buttons = MouseButtons.None; + int nButtons = this.NativeHTMLEventObj.GetButton(); + if ((nButtons & 1) != 0) { + buttons |= MouseButtons.Left; + } + if ((nButtons & 2) != 0) { + buttons |= MouseButtons.Right; + } + if ((nButtons & 4) != 0) { + buttons |= MouseButtons.Middle; + } + return buttons; + } + } + + /// + /// + /// [To be supplied.] + /// + public Point ClientMousePosition { + get { + return new Point(this.NativeHTMLEventObj.GetClientX(), this.NativeHTMLEventObj.GetClientY()); + } + } + + /// + /// + /// [To be supplied.] + /// + public Point OffsetMousePosition { + get { + return new Point(this.NativeHTMLEventObj.GetOffsetX(), this.NativeHTMLEventObj.GetOffsetY()); + } + } + + /// + /// + /// [To be supplied.] + /// + public Point MousePosition { + get { + return new Point(this.NativeHTMLEventObj.GetX(), this.NativeHTMLEventObj.GetY()); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool BubbleEvent { + get { + return !this.NativeHTMLEventObj.GetCancelBubble(); + } + set { + this.NativeHTMLEventObj.SetCancelBubble(!value); + } + } + + /// + /// + /// [To be supplied.] + /// + public int KeyPressedCode { + get { + return this.NativeHTMLEventObj.GetKeyCode(); + } + } + + /// + /// + /// Indicates whether the Alt key was pressed, if this information is + /// provided to the IHtmlEventObj + /// + public bool AltKeyPressed + { + get + { + return this.NativeHTMLEventObj.GetAltKey(); + } + } + + /// + /// + /// Indicates whether the Ctrl key was pressed, if this information is + /// provided to the IHtmlEventObj + /// + public bool CtrlKeyPressed + { + get + { + return this.NativeHTMLEventObj.GetCtrlKey(); + } + } + + /// + /// + /// Indicates whether the Shift key was pressed, if this information is + /// provided to the IHtmlEventObj + /// + public bool ShiftKeyPressed + { + get + { + return this.NativeHTMLEventObj.GetShiftKey(); + } + } + + /// + /// + /// [To be supplied.] + /// + public string EventType { + get { + return this.NativeHTMLEventObj.GetEventType(); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool ReturnValue { + get { + object obj = this.NativeHTMLEventObj.GetReturnValue(); + return obj == null ? true : (bool)obj; + } + set { + object objValue = value; + this.NativeHTMLEventObj.SetReturnValue(objValue); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public HtmlElement FromElement { + get { + UnsafeNativeMethods.IHTMLElement htmlElement = this.NativeHTMLEventObj.GetFromElement(); + return htmlElement == null ? null : new HtmlElement(shimManager, htmlElement); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public HtmlElement ToElement { + get { + UnsafeNativeMethods.IHTMLElement htmlElement = this.NativeHTMLEventObj.GetToElement(); + return htmlElement == null ? null : new HtmlElement(shimManager, htmlElement); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlElementEventHandler.cs b/WindowsForms/Managed/System/WinForms/HtmlElementEventHandler.cs new file mode 100644 index 000000000..34964b6e2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlElementEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + /// + /// + /// [To be supplied.] + /// + public delegate void HtmlElementEventHandler(object sender, HtmlElementEventArgs e); + +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlHistory.cs b/WindowsForms/Managed/System/WinForms/HtmlHistory.cs new file mode 100644 index 000000000..c34fd2f99 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlHistory.cs @@ -0,0 +1,138 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Security.Permissions; +using System.Security; +using System.Runtime.InteropServices; +using System.Net; +using System.Globalization; + +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")] + public sealed class HtmlHistory : IDisposable + { + private UnsafeNativeMethods.IOmHistory htmlHistory; + private bool disposed; + + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + internal HtmlHistory(UnsafeNativeMethods.IOmHistory history) + { + this.htmlHistory = history; + Debug.Assert(this.NativeOmHistory != null, "The history object should implement IOmHistory"); + } + + private UnsafeNativeMethods.IOmHistory NativeOmHistory { + get { + if (this.disposed) { + throw new System.ObjectDisposedException(GetType().Name); + } + return this.htmlHistory; + } + } + + /// + public void Dispose() { + this.htmlHistory = null; + this.disposed = true; + GC.SuppressFinalize(this); + } + + /// + /// + /// [To be supplied.] + /// + public int Length { + get { + return (int)this.NativeOmHistory.GetLength(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Back(int numberBack) { + if (numberBack < 0) { + throw new ArgumentOutOfRangeException("numberBack", SR.GetString(SR.InvalidLowBoundArgumentEx, "numberBack", numberBack.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + else if (numberBack > 0) { + object oNumForward = (object)(-numberBack); + this.NativeOmHistory.Go(ref oNumForward); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Forward(int numberForward) { + if (numberForward < 0) { + throw new ArgumentOutOfRangeException("numberForward", SR.GetString(SR.InvalidLowBoundArgumentEx, "numberForward", numberForward.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + else if (numberForward > 0) { + object oNumForward = (object)numberForward; + this.NativeOmHistory.Go(ref oNumForward); + } + } + + /// + /// + /// Go to a specific Uri in the history + /// + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public void Go(Uri url) + { + Go(url.ToString()); + } + + /// + /// + /// Go to a specific url(string) in the history + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")] + public void Go(string urlString) + { + object loc = (object)urlString; + this.NativeOmHistory.Go(ref loc); + } + + /// + /// + /// Go to the specified position in the history list + /// + public void Go(int relativePosition) { + object loc = (object)relativePosition; + this.NativeOmHistory.Go(ref loc); + } + + /// + /// + /// [To be supplied.] + /// + public object DomHistory { + get { + return this.NativeOmHistory; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/HtmlShim.cs b/WindowsForms/Managed/System/WinForms/HtmlShim.cs new file mode 100644 index 000000000..34f435171 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlShim.cs @@ -0,0 +1,168 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + + /// This is essentially a proxy object between the native + /// html objects and our managed ones. We want the managed + /// HtmlDocument, HtmlWindow and HtmlElement to be super-lightweight, + /// which means that we shouldnt have things that tie up their lifetimes + /// contained within them. The "Shim" is essentially the object that + /// manages events coming out of the HtmlDocument, HtmlElement and HtmlWindow + /// and serves them back up to the user. + + internal abstract class HtmlShim : IDisposable { + + private EventHandlerList events; + private int eventCount = 0; + private Dictionary attachedEventList; + + + protected HtmlShim() { + } + ~HtmlShim() { + Dispose(false); + } + + private EventHandlerList Events { + get { + if (events == null) { + events = new EventHandlerList(); + } + return events; + } + } + + /// Support IHtml*3.AttachHandler + public abstract void AttachEventHandler(string eventName, EventHandler eventHandler); + + + public void AddHandler(object key, Delegate value) { + eventCount++; + Events.AddHandler(key, value); + OnEventHandlerAdded(); + } + + protected HtmlToClrEventProxy AddEventProxy(string eventName, EventHandler eventHandler) { + if (attachedEventList == null) { + attachedEventList = new Dictionary(); + } + HtmlToClrEventProxy proxy = new HtmlToClrEventProxy(this, eventName, eventHandler); + attachedEventList[eventHandler] = proxy; + return proxy; + } + + + public abstract UnsafeNativeMethods.IHTMLWindow2 AssociatedWindow { + get; + } + + + /// create connectionpoint cookie + public abstract void ConnectToEvents(); + + /// Support IHtml*3.DetachEventHandler + public abstract void DetachEventHandler(string eventName, EventHandler eventHandler); + + + /// disconnect from connectionpoint cookie + /// inheriting classes should override to disconnect from ConnectionPoint and call base. + public virtual void DisconnectFromEvents() { + + if (attachedEventList != null) { + EventHandler[] events = new EventHandler[attachedEventList.Count]; + attachedEventList.Keys.CopyTo(events,0); + + foreach (EventHandler eh in events) { + HtmlToClrEventProxy proxy = attachedEventList[eh]; + DetachEventHandler(proxy.EventName, eh); + } + } + + } + + /// return the sender for events, usually the HtmlWindow, HtmlElement, HtmlDocument + protected abstract object GetEventSender(); + + + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (disposing) { + DisconnectFromEvents(); + if (this.events != null) { + this.events.Dispose(); + this.events = null; + } + } + } + + public void FireEvent(object key, EventArgs e) + { + + System.Delegate delegateToInvoke = (System.Delegate)Events[key]; + + if (delegateToInvoke != null) { + try { + delegateToInvoke.DynamicInvoke(GetEventSender(), e); + } + catch (Exception ex) { + // Note: this check is for the debugger, so we can catch exceptions in the debugger instead of + // throwing a thread exception. + if (NativeWindow.WndProcShouldBeDebuggable) + { + throw; + } + else { + Application.OnThreadException(ex); + } + } + } + } + protected virtual void OnEventHandlerAdded() { + ConnectToEvents(); + } + + protected virtual void OnEventHandlerRemoved() { + if (eventCount <=0) { + DisconnectFromEvents(); + eventCount = 0; + } + } + + public void RemoveHandler(object key, Delegate value) { + eventCount--; + Events.RemoveHandler(key, value); + OnEventHandlerRemoved(); + } + + protected HtmlToClrEventProxy RemoveEventProxy(EventHandler eventHandler) { + if (attachedEventList == null) { + return null; + } + + if (attachedEventList.ContainsKey(eventHandler)) { + HtmlToClrEventProxy proxy = attachedEventList[eventHandler] as HtmlToClrEventProxy; + attachedEventList.Remove(eventHandler); + return proxy; + } + return null; + } + } + +} + + + + diff --git a/WindowsForms/Managed/System/WinForms/HtmlShimManager.cs b/WindowsForms/Managed/System/WinForms/HtmlShimManager.cs new file mode 100644 index 000000000..c072e6fe7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlShimManager.cs @@ -0,0 +1,223 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + using System; + using System.Collections.Generic; + using System.Collections; + using System.Diagnostics; + + /// + /// HtmlShimManager - this class manages the shims for HtmlWindows, HtmlDocuments, and HtmlElements. + /// essentially we need a long-lasting object to call back on events from the web browser, and the + /// manager is the one in charge of making sure this list stays around as long as needed. + /// + /// When a HtmlWindow unloads we prune our list of corresponding document, window, and element shims. + /// + /// + internal sealed class HtmlShimManager :IDisposable { + + private Dictionary htmlWindowShims; + private Dictionary htmlElementShims; + private Dictionary htmlDocumentShims; + + internal HtmlShimManager() { + } + + /// AddDocumentShim - adds a HtmlDocumentShim to list of shims to manage + /// Can create a WindowShim as a side effect so it knows when to self prune from the list. + /// + public void AddDocumentShim(HtmlDocument doc) { + HtmlDocument.HtmlDocumentShim shim = null; + + if (htmlDocumentShims == null) { + htmlDocumentShims = new Dictionary(); + shim = new HtmlDocument.HtmlDocumentShim(doc); + htmlDocumentShims[doc] = shim; + } + else if (!htmlDocumentShims.ContainsKey(doc)) { + shim = new HtmlDocument.HtmlDocumentShim(doc); + htmlDocumentShims[doc] = shim; + } + if (shim != null) { + OnShimAdded(shim); + } + + } + + /// AddWindowShim - adds a HtmlWindowShim to list of shims to manage + /// + + public void AddWindowShim(HtmlWindow window) { + HtmlWindow.HtmlWindowShim shim = null; + if (htmlWindowShims == null) { + htmlWindowShims = new Dictionary(); + shim = new HtmlWindow.HtmlWindowShim(window); + htmlWindowShims[window] = shim; + } + else if (!htmlWindowShims.ContainsKey(window)) { + shim = new HtmlWindow.HtmlWindowShim(window); + htmlWindowShims[window] = shim; + } + if (shim != null) { + // strictly not necessary, but here for future use. + OnShimAdded(shim); + } + } + + /// AddElementShim - adds a HtmlDocumentShim to list of shims to manage + /// Can create a WindowShim as a side effect so it knows when to self prune from the list. + /// + public void AddElementShim(HtmlElement element) { + HtmlElement.HtmlElementShim shim = null; + + if (htmlElementShims == null) { + htmlElementShims = new Dictionary(); + shim = new HtmlElement.HtmlElementShim(element); + htmlElementShims[element] = shim; + } + else if (!htmlElementShims.ContainsKey(element)) { + shim = new HtmlElement.HtmlElementShim(element); + htmlElementShims[element] = shim; + } + if (shim != null) { + OnShimAdded(shim); + } + + } + + internal HtmlDocument.HtmlDocumentShim GetDocumentShim(HtmlDocument document) { + if (htmlDocumentShims == null) { + return null; + } + if (htmlDocumentShims.ContainsKey(document)) { + return htmlDocumentShims[document]; + } + return null; + } + + internal HtmlElement.HtmlElementShim GetElementShim(HtmlElement element) { + if (htmlElementShims == null) { + return null; + } + if (htmlElementShims.ContainsKey(element)) { + return htmlElementShims[element]; + } + return null; + } + + internal HtmlWindow.HtmlWindowShim GetWindowShim(HtmlWindow window) { + if (htmlWindowShims == null) { + return null; + } + if (htmlWindowShims.ContainsKey(window)) { + return htmlWindowShims[window]; + } + return null; + } + + private void OnShimAdded(HtmlShim addedShim) { + + Debug.Assert(addedShim != null, "Why are we calling this with a null shim?"); + if (addedShim != null) { + if (!(addedShim is HtmlWindow.HtmlWindowShim)) { + // we need to add a window shim here for documents and elements + // so we can sync Window.Unload. The window shim itself will trap + // the unload event and call back on us on OnWindowUnloaded. When + // that happens we know we can free all our ptrs to COM. + AddWindowShim(new HtmlWindow(this, addedShim.AssociatedWindow)); + } + } + } + /// + /// HtmlWindowShim calls back on us when it has unloaded the page. At this point we need to + /// walk through our lists and make sure we've cleaned up + /// + internal void OnWindowUnloaded(HtmlWindow unloadedWindow) { + Debug.Assert(unloadedWindow != null, "Why are we calling this with a null window?"); + if (unloadedWindow != null) { + // + // prune documents + // + if (htmlDocumentShims != null) { + HtmlDocument.HtmlDocumentShim[] shims = new HtmlDocument.HtmlDocumentShim[htmlDocumentShims.Count]; + htmlDocumentShims.Values.CopyTo(shims,0); + + foreach (HtmlDocument.HtmlDocumentShim shim in shims) { + if (shim.AssociatedWindow == unloadedWindow.NativeHtmlWindow) { + htmlDocumentShims.Remove(shim.Document); + shim.Dispose(); + } + } + } + // + // prune elements + // + if (htmlElementShims != null) { + HtmlElement.HtmlElementShim[] shims = new HtmlElement.HtmlElementShim[htmlElementShims.Count]; + htmlElementShims.Values.CopyTo(shims,0); + + foreach (HtmlElement.HtmlElementShim shim in shims) { + if (shim.AssociatedWindow == unloadedWindow.NativeHtmlWindow) { + htmlElementShims.Remove(shim.Element); + shim.Dispose(); + } + } + } + + // + // prune the particular window from the list. + // + if (htmlWindowShims != null) { + if (htmlWindowShims.ContainsKey(unloadedWindow)) { + HtmlWindow.HtmlWindowShim shim = htmlWindowShims[unloadedWindow]; + htmlWindowShims.Remove(unloadedWindow); + shim.Dispose(); + } + } + } + } + + public void Dispose() { + Dispose(true); + } + + private void Dispose(bool disposing) { + if (disposing) { + + if (htmlElementShims != null){ + foreach (HtmlElement.HtmlElementShim shim in htmlElementShims.Values) { + shim.Dispose(); + } + } + if (htmlDocumentShims != null) { + foreach (HtmlDocument.HtmlDocumentShim shim in htmlDocumentShims.Values) { + shim.Dispose(); + } + } + + if (htmlWindowShims != null) { + foreach (HtmlWindow.HtmlWindowShim shim in htmlWindowShims.Values) { + shim.Dispose(); + } + } + htmlWindowShims = null; + htmlDocumentShims = null; + htmlWindowShims = null; + + + } + } + ~HtmlShimManager() { + Dispose(false); + } + + + + } + } diff --git a/WindowsForms/Managed/System/WinForms/HtmlToClrEventProxy.cs b/WindowsForms/Managed/System/WinForms/HtmlToClrEventProxy.cs new file mode 100644 index 000000000..e854b1228 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlToClrEventProxy.cs @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Security.Permissions; +using System.Security; +using System.Runtime.InteropServices; +using System.Net; +using System.Globalization; +using System.Collections; +using System.Reflection; + +namespace System.Windows.Forms { + + + /// + /// This class is here for IHTML*3.AttachHandler style eventing. + /// We need a way of routing requests for DISPID(0) to a particular CLR event without creating + /// a public class. In order to accomplish this we implement IReflect and handle InvokeMethod + /// to call back on a CLR event handler. + /// + + internal class HtmlToClrEventProxy : IReflect { + private EventHandler eventHandler; + private IReflect typeIReflectImplementation; + private object sender = null; + private string eventName; + + public HtmlToClrEventProxy(object sender, string eventName, EventHandler eventHandler) { + this.eventHandler = eventHandler; + this.eventName = eventName; + + Type htmlToClrEventProxyType = typeof(HtmlToClrEventProxy); + typeIReflectImplementation = htmlToClrEventProxyType as IReflect; + } + + public string EventName { + get { return eventName; } + } + + [DispId(0)] + public void OnHtmlEvent() { + InvokeClrEvent(); + } + + private void InvokeClrEvent() { + if (eventHandler != null) { + eventHandler(sender, EventArgs.Empty); + } + } + + +#region IReflect + + Type IReflect.UnderlyingSystemType { + get { + return typeIReflectImplementation.UnderlyingSystemType; + } + } + + // Methods + System.Reflection.FieldInfo IReflect.GetField(string name, System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetField(name, bindingAttr); + } + System.Reflection.FieldInfo[] IReflect.GetFields(System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetFields(bindingAttr); + } + System.Reflection.MemberInfo[] IReflect.GetMember(string name, System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetMember(name, bindingAttr); + } + System.Reflection.MemberInfo[] IReflect.GetMembers(System.Reflection.BindingFlags bindingAttr){ + return typeIReflectImplementation.GetMembers(bindingAttr); + } + System.Reflection.MethodInfo IReflect.GetMethod(string name, System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetMethod(name, bindingAttr); + } + System.Reflection.MethodInfo IReflect.GetMethod(string name, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, Type[] types, System.Reflection.ParameterModifier[] modifiers) { + return typeIReflectImplementation.GetMethod(name, bindingAttr, binder, types, modifiers); + } + System.Reflection.MethodInfo[] IReflect.GetMethods(System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetMethods(bindingAttr); + } + System.Reflection.PropertyInfo[] IReflect.GetProperties(System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetProperties(bindingAttr); + } + System.Reflection.PropertyInfo IReflect.GetProperty(string name, System.Reflection.BindingFlags bindingAttr) { + return typeIReflectImplementation.GetProperty(name, bindingAttr); + } + System.Reflection.PropertyInfo IReflect.GetProperty(string name, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, Type returnType, Type[] types, System.Reflection.ParameterModifier[] modifiers) { + return typeIReflectImplementation.GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + // InvokeMember: + // If we get a call for DISPID=0, fire the CLR event. + // + object IReflect.InvokeMember(string name, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object target, object[] args, System.Reflection.ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) { + + // + if (name == "[DISPID=0]") { + // we know we're getting called back to fire the event - translate this now into a CLR event. + OnHtmlEvent(); + // since there's no return value for void, return null. + return null; + } + else { + return typeIReflectImplementation.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + } + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/HtmlWindow.cs b/WindowsForms/Managed/System/WinForms/HtmlWindow.cs new file mode 100644 index 000000000..1fa5dd7aa --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlWindow.cs @@ -0,0 +1,826 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Printing; +using System.IO; +using System.Net; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; + +namespace System.Windows.Forms +{ + /// + /// + /// [To be supplied.] + /// + [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")] + public sealed class HtmlWindow + { + internal static readonly object EventError = new object(); + internal static readonly object EventGotFocus = new object(); + internal static readonly object EventLoad = new object(); + internal static readonly object EventLostFocus = new object(); + internal static readonly object EventResize = new object(); + internal static readonly object EventScroll = new object(); + internal static readonly object EventUnload = new object(); + + private HtmlShimManager shimManager; + private UnsafeNativeMethods.IHTMLWindow2 htmlWindow2; + + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + internal HtmlWindow(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLWindow2 win) + { + this.htmlWindow2 = win; + Debug.Assert(this.NativeHtmlWindow != null, "The window object should implement IHTMLWindow2"); + + this.shimManager = shimManager; + } + + internal UnsafeNativeMethods.IHTMLWindow2 NativeHtmlWindow + { + get + { + return this.htmlWindow2; + } + } + + private HtmlShimManager ShimManager + { + get { return shimManager; } + } + + private HtmlWindowShim WindowShim + { + get + { + if (ShimManager != null) + { + HtmlWindowShim shim = ShimManager.GetWindowShim(this); + if (shim == null) + { + shimManager.AddWindowShim(this); + shim = ShimManager.GetWindowShim(this); + } + return shim; + } + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlDocument Document + { + get + { + UnsafeNativeMethods.IHTMLDocument iHTMLDocument = this.NativeHtmlWindow.GetDocument() as UnsafeNativeMethods.IHTMLDocument; + return iHTMLDocument != null ? new HtmlDocument(ShimManager, iHTMLDocument) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public object DomWindow + { + get + { + return this.NativeHtmlWindow; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlWindowCollection Frames + { + get + { + UnsafeNativeMethods.IHTMLFramesCollection2 iHTMLFramesCollection2 = this.NativeHtmlWindow.GetFrames(); + return (iHTMLFramesCollection2 != null) ? new HtmlWindowCollection(ShimManager, iHTMLFramesCollection2) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlHistory History + { + get + { + UnsafeNativeMethods.IOmHistory iOmHistory = this.NativeHtmlWindow.GetHistory(); + return iOmHistory != null ? new HtmlHistory(iOmHistory) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsClosed + { + get + { + return this.NativeHtmlWindow.GetClosed(); + } + } + + /// + /// + /// Name of the NativeHtmlWindow + /// + public string Name + { + get + { + return this.NativeHtmlWindow.GetName(); + } + set + { + this.NativeHtmlWindow.SetName(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlWindow Opener + { + get + { + UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlWindow.GetOpener() as UnsafeNativeMethods.IHTMLWindow2; + return (iHTMLWindow2 != null) ? new HtmlWindow(ShimManager, iHTMLWindow2) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlWindow Parent + { + get + { + UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlWindow.GetParent(); + return (iHTMLWindow2 != null) ? new HtmlWindow(ShimManager, iHTMLWindow2) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public Point Position + { + get + { + return new Point(((UnsafeNativeMethods.IHTMLWindow3)this.NativeHtmlWindow).GetScreenLeft(), + ((UnsafeNativeMethods.IHTMLWindow3)this.NativeHtmlWindow).GetScreenTop()); + } + } + + /// + /// + /// Gets or sets size for the window + /// + public Size Size + { + get + { + UnsafeNativeMethods.IHTMLElement bodyElement = this.NativeHtmlWindow.GetDocument().GetBody(); + return new Size(bodyElement.GetOffsetWidth(), bodyElement.GetOffsetHeight()); + } + set + { + ResizeTo(value.Width, value.Height); + } + } + + /// + /// + /// [To be supplied.] + /// + public string StatusBarText + { + get + { + return this.NativeHtmlWindow.GetStatus(); + } + set + { + this.NativeHtmlWindow.SetStatus(value); + } + } + + /// + /// + /// [To be supplied.] + /// + public Uri Url + { + get + { + UnsafeNativeMethods.IHTMLLocation iHtmlLocation = this.NativeHtmlWindow.GetLocation(); + string stringLocation = (iHtmlLocation == null) ? "" : iHtmlLocation.GetHref(); + return string.IsNullOrEmpty(stringLocation) ? null : new Uri(stringLocation); + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlElement WindowFrameElement + { + get + { + UnsafeNativeMethods.IHTMLElement htmlElement = ((UnsafeNativeMethods.IHTMLWindow4)this.NativeHtmlWindow).frameElement() as UnsafeNativeMethods.IHTMLElement; + return (htmlElement != null) ? new HtmlElement(ShimManager, htmlElement) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public void Alert(string message) + { + this.NativeHtmlWindow.Alert(message); + } + + /// + /// + /// [To be supplied.] + /// + public void AttachEventHandler(string eventName, EventHandler eventHandler) + { + WindowShim.AttachEventHandler(eventName, eventHandler); + } + + /// + /// + /// [To be supplied.] + /// + public void Close() + { + this.NativeHtmlWindow.Close(); + } + + /// + /// + /// [To be supplied.] + /// + public bool Confirm(string message) + { + return this.NativeHtmlWindow.Confirm(message); + } + + /// + /// + /// [To be supplied.] + /// + public void DetachEventHandler(string eventName, EventHandler eventHandler) + { + WindowShim.DetachEventHandler(eventName, eventHandler); + } + + /// + /// + /// [To be supplied.] + /// + public void Focus() + { + this.NativeHtmlWindow.Focus(); + } + + /// + /// + /// Moves the Window to the position requested + /// + public void MoveTo(int x, int y) + { + this.NativeHtmlWindow.MoveTo(x, y); + } + + /// + /// + /// Moves the Window to the point requested + /// + public void MoveTo(Point point) + { + this.NativeHtmlWindow.MoveTo(point.X, point.Y); + } + + /// + /// + /// [To be supplied.] + /// + public void Navigate(Uri url) + { + this.NativeHtmlWindow.Navigate(url.ToString()); + } + + /// + /// + /// [To be supplied.] + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")] + public void Navigate(string urlString) + { + this.NativeHtmlWindow.Navigate(urlString); + } + + /// + /// + /// [To be supplied.] + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")] + public HtmlWindow Open(string urlString, string target, string windowOptions, bool replaceEntry) + { + UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlWindow.Open(urlString, target, windowOptions, replaceEntry); + return (iHTMLWindow2 != null) ? new HtmlWindow(ShimManager, iHTMLWindow2) : null; + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public HtmlWindow Open(Uri url, string target, string windowOptions, bool replaceEntry) + { + return Open(url.ToString(), target, windowOptions, replaceEntry); + } + + /// + /// + /// [To be supplied.] + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")] + public HtmlWindow OpenNew(string urlString, string windowOptions) + { + UnsafeNativeMethods.IHTMLWindow2 iHTMLWindow2 = this.NativeHtmlWindow.Open(urlString, "_blank", windowOptions, true); + return (iHTMLWindow2 != null) ? new HtmlWindow(ShimManager, iHTMLWindow2) : null; + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public HtmlWindow OpenNew(Uri url, string windowOptions) + { + return OpenNew(url.ToString(), windowOptions); + } + + /// + /// + /// [To be supplied.] + /// + public string Prompt(string message, string defaultInputValue) + { + return this.NativeHtmlWindow.Prompt(message, defaultInputValue).ToString(); + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveFocus() + { + this.NativeHtmlWindow.Blur(); + } + + /// + /// + /// Resize the window to the width/height requested + /// + public void ResizeTo(int width, int height) + { + this.NativeHtmlWindow.ResizeTo(width, height); + } + + /// + /// Resize the window to the Size requested + /// + public void ResizeTo(Size size) + { + this.NativeHtmlWindow.ResizeTo(size.Width, size.Height); + } + + /// + /// + /// Scroll the window to the position requested + /// + public void ScrollTo(int x, int y) + { + this.NativeHtmlWindow.ScrollTo(x, y); + } + + /// + /// + /// Scroll the window to the point requested + /// + public void ScrollTo(Point point) + { + this.NativeHtmlWindow.ScrollTo(point.X, point.Y); + } + + // + // Events + // + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementErrorEventHandler Error + { + add + { + WindowShim.AddHandler(EventError, value); + } + remove + { + WindowShim.RemoveHandler(EventError, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler GotFocus + { + add + { + WindowShim.AddHandler(EventGotFocus, value); + } + remove + { + WindowShim.RemoveHandler(EventGotFocus, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Load + { + add + { + WindowShim.AddHandler(EventLoad, value); + } + remove + { + WindowShim.RemoveHandler(EventLoad, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler LostFocus + { + add + { + WindowShim.AddHandler(EventLostFocus, value); + } + remove + { + WindowShim.RemoveHandler(EventLostFocus, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Resize + { + add + { + WindowShim.AddHandler(EventResize, value); + } + remove + { + WindowShim.RemoveHandler(EventResize, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Scroll + { + add + { + WindowShim.AddHandler(EventScroll, value); + } + remove + { + WindowShim.RemoveHandler(EventScroll, value); + } + } + + /// + /// + /// [To be supplied.] + /// + public event HtmlElementEventHandler Unload + { + add + { + WindowShim.AddHandler(EventUnload, value); + } + remove + { + WindowShim.RemoveHandler(EventUnload, value); + } + } + + + + + // + // Private classes: + // + [ClassInterface(ClassInterfaceType.None)] + private class HTMLWindowEvents2 : StandardOleMarshalObject, /*Enforce calling back on the same thread*/ + UnsafeNativeMethods.DHTMLWindowEvents2 + { + private HtmlWindow parent; + + public HTMLWindowEvents2(HtmlWindow htmlWindow) + { + this.parent = htmlWindow; + } + + private void FireEvent(object key, EventArgs e) + { + if (this.parent != null) + { + parent.WindowShim.FireEvent(key, e); + } + } + + public void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventGotFocus, e); + } + + public void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventLostFocus, e); + } + + public bool onerror(string description, string urlString, int line) + { + HtmlElementErrorEventArgs e = new HtmlElementErrorEventArgs(description, urlString, line); + FireEvent(HtmlWindow.EventError, e); + return e.Handled; + } + + public void onload(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventLoad, e); + } + + public void onunload(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventUnload, e); + if (parent != null) + { + parent.WindowShim.OnWindowUnload(); + } + } + + public void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventScroll, e); + } + + public void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + FireEvent(HtmlWindow.EventResize, e); + } + + public bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj) + { + HtmlElementEventArgs e = new HtmlElementEventArgs(parent.ShimManager, evtObj); + return e.ReturnValue; + } + + public void onbeforeunload(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onbeforeprint(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + + public void onafterprint(UnsafeNativeMethods.IHTMLEventObj evtObj) { } + } + + + + /// + /// HtmlWindowShim - this is the glue between the DOM eventing mechanisms + /// and our CLR callbacks. + /// + /// There are two kinds of events: HTMLWindowEvents2 and IHtmlWindow3.AttachHandler style + /// HTMLWindowEvents2: we create an IConnectionPoint (via ConnectionPointCookie) between us and MSHTML and it calls back + /// on an instance of HTMLWindowEvents2. The HTMLWindowEvents2 class then fires the event. + /// + /// IHTMLWindow3.AttachHandler: MSHML calls back on an HtmlToClrEventProxy that we've created, looking + /// for a method named DISPID=0. For each event that's subscribed, we create + /// a new HtmlToClrEventProxy, detect the callback and fire the corresponding + /// CLR event. + /// + internal class HtmlWindowShim : HtmlShim + { + private AxHost.ConnectionPointCookie cookie; + private HtmlWindow htmlWindow; + + public HtmlWindowShim(HtmlWindow window) + { + this.htmlWindow = window; + } + + public UnsafeNativeMethods.IHTMLWindow2 NativeHtmlWindow + { + get { return htmlWindow.NativeHtmlWindow; } + } + + public override UnsafeNativeMethods.IHTMLWindow2 AssociatedWindow + { + get { return htmlWindow.NativeHtmlWindow; } + } + + /// Support IHtmlDocument3.AttachHandler + public override void AttachEventHandler(string eventName, System.EventHandler eventHandler) + { + + // IE likes to call back on an IDispatch of DISPID=0 when it has an event, + // the HtmlToClrEventProxy helps us fake out the CLR so that we can call back on + // our EventHandler properly. + + HtmlToClrEventProxy proxy = AddEventProxy(eventName, eventHandler); + bool success = ((UnsafeNativeMethods.IHTMLWindow3)this.NativeHtmlWindow).AttachEvent(eventName, proxy); + Debug.Assert(success, "failed to add event"); + } + + /// Support HTMLWindowEvents2 + public override void ConnectToEvents() + { + if (cookie == null || !cookie.Connected) + { + this.cookie = new AxHost.ConnectionPointCookie(NativeHtmlWindow, + new HTMLWindowEvents2(htmlWindow), + typeof(UnsafeNativeMethods.DHTMLWindowEvents2), + /*throwException*/ false); + if (!cookie.Connected) + { + cookie = null; + } + } + } + + /// Support IHTMLWindow3.DetachHandler + public override void DetachEventHandler(string eventName, System.EventHandler eventHandler) + { + HtmlToClrEventProxy proxy = RemoveEventProxy(eventHandler); + if (proxy != null) + { + ((UnsafeNativeMethods.IHTMLWindow3)this.NativeHtmlWindow).DetachEvent(eventName, proxy); + } + } + + public override void DisconnectFromEvents() + { + if (this.cookie != null) + { + this.cookie.Disconnect(); + this.cookie = null; + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + if (htmlWindow != null && htmlWindow.NativeHtmlWindow != null) + { + Marshal.FinalReleaseComObject(htmlWindow.NativeHtmlWindow); + } + htmlWindow = null; + } + } + + protected override object GetEventSender() + { + return htmlWindow; + } + + public void OnWindowUnload() + { + if (htmlWindow != null) + { + htmlWindow.ShimManager.OnWindowUnloaded(htmlWindow); + } + } + } + + #region operators + + /// + [SuppressMessage("Microsoft.Design", "CA1046:DoNotOverrideOperatorEqualsOnReferenceTypes")] + public static bool operator ==(HtmlWindow left, HtmlWindow right) + { + //Not equal if only one's null. + if (object.ReferenceEquals(left, null) != object.ReferenceEquals(right, null)) + { + return false; + } + + //Equal if both are null. + if (object.ReferenceEquals(left, null)) + { + return true; + } + + //Neither are null. Get the IUnknowns and compare them. + IntPtr leftPtr = IntPtr.Zero; + IntPtr rightPtr = IntPtr.Zero; + try + { + leftPtr = Marshal.GetIUnknownForObject(left.NativeHtmlWindow); + rightPtr = Marshal.GetIUnknownForObject(right.NativeHtmlWindow); + return leftPtr == rightPtr; + } + finally + { + if (leftPtr != IntPtr.Zero) + { + Marshal.Release(leftPtr); + } + if (rightPtr != IntPtr.Zero) + { + Marshal.Release(rightPtr); + } + } + } + + /// + public static bool operator !=(HtmlWindow left, HtmlWindow right) + { + return !(left == right); + } + + /// + public override int GetHashCode() + { + return htmlWindow2 == null ? 0 : htmlWindow2.GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + return (this == (HtmlWindow)obj); + } + #endregion + + } +} diff --git a/WindowsForms/Managed/System/WinForms/HtmlWindowCollection.cs b/WindowsForms/Managed/System/WinForms/HtmlWindowCollection.cs new file mode 100644 index 000000000..5145f642a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/HtmlWindowCollection.cs @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Security.Permissions; +using System.Security; +using System.Runtime.InteropServices; +using System.Net; +using System.Collections; + +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + public class HtmlWindowCollection : ICollection { + private UnsafeNativeMethods.IHTMLFramesCollection2 htmlFramesCollection2; + private HtmlShimManager shimManager; + + internal HtmlWindowCollection(HtmlShimManager shimManager, UnsafeNativeMethods.IHTMLFramesCollection2 collection) { + this.htmlFramesCollection2 = collection; + this.shimManager = shimManager; + + Debug.Assert(this.NativeHTMLFramesCollection2 != null, "The window collection object should implement IHTMLFramesCollection2"); + } + + private UnsafeNativeMethods.IHTMLFramesCollection2 NativeHTMLFramesCollection2 { + get { + return this.htmlFramesCollection2; + } + } + + + /// + /// + /// [To be supplied.] + /// + public HtmlWindow this[int index] { + get { + if (index < 0 || index >= this.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidBoundArgument, "index", index, 0, this.Count - 1)); + } + + object oIndex = (object)index; + UnsafeNativeMethods.IHTMLWindow2 htmlWindow2 = this.NativeHTMLFramesCollection2.Item(ref oIndex) + as UnsafeNativeMethods.IHTMLWindow2; + return (htmlWindow2 != null) ? new HtmlWindow(shimManager, htmlWindow2) : null; + } + } + + /// + /// + /// [To be supplied.] + /// + public HtmlWindow this[string windowId] { + get { + object oWindowId = (object)windowId; + UnsafeNativeMethods.IHTMLWindow2 htmlWindow2 = null; + try { + htmlWindow2 = this.htmlFramesCollection2.Item(ref oWindowId) + as UnsafeNativeMethods.IHTMLWindow2; + } + catch (COMException) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "windowId", windowId)); + } + return (htmlWindow2 != null) ? new HtmlWindow(shimManager, htmlWindow2) : null; + } + } + + /// + /// + /// Returns the total number of elements in the collection. + /// + public int Count { + get { + return this.NativeHTMLFramesCollection2.GetLength(); + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + int count = this.Count; + for (int i = 0; i < count; i++) { + dest.SetValue(this[i], index++); + } + } + + /// + /// + public IEnumerator GetEnumerator() { + HtmlWindow[] htmlWindows = new HtmlWindow[this.Count]; + ((ICollection)this).CopyTo(htmlWindows, 0); + + return htmlWindows.GetEnumerator(); + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/IBindableComponent.cs b/WindowsForms/Managed/System/WinForms/IBindableComponent.cs new file mode 100644 index 000000000..852c1994b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IBindableComponent.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + + /// + /// + /// + public interface IBindableComponent : IComponent { + + /// + /// + /// + ControlBindingsCollection DataBindings { get; } + + /// + /// + /// + BindingContext BindingContext { get; set; } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/IButtonControl.cs b/WindowsForms/Managed/System/WinForms/IButtonControl.cs new file mode 100644 index 000000000..451d2c36f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IButtonControl.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + /* + * Copyright (c) 1997, Microsoft Corporation. All Rights Reserved. + * Information Contained Herein is Proprietary and Confidential. + * + * @security(checkClassLinking=on) + */ + + using System; + + /// + /// + /// + /// Allows a control to act like a button + /// on a form. + /// + /// + /// + public interface IButtonControl { + + /// + /// + /// Gets and sets the dialog result of the Button control. This is + /// used as the result for the dialog on which the button is set to + /// be an "accept" or "cancel" button. + /// + /// + DialogResult DialogResult {get; set;} + + /// + /// + /// Notifies a control that it is the default button so that its appearance and behavior + /// is adjusted accordingly. + /// + /// + void NotifyDefault(bool value); + + /// + /// + /// Generates a + /// event for the control. + /// + void PerformClick(); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ICommandExecutor.cs b/WindowsForms/Managed/System/WinForms/ICommandExecutor.cs new file mode 100644 index 000000000..9be19ec70 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ICommandExecutor.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// + public interface ICommandExecutor { + /// + void Execute(); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ICompletion.cs b/WindowsForms/Managed/System/WinForms/ICompletion.cs new file mode 100644 index 000000000..13c2b9284 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ICompletion.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +#if false +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// + public interface ICompletion { + /// + /// + /// This function will be called by the ThreadPool's worker threads when a + /// packet is ready. + /// + /// + void CompletionStatusChanged(bool status, int size, NativeMethods.OVERLAPPED overlapped); + } +} +#endif diff --git a/WindowsForms/Managed/System/WinForms/IContainerControl.cs b/WindowsForms/Managed/System/WinForms/IContainerControl.cs new file mode 100644 index 000000000..8885cba39 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IContainerControl.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.ComponentModel; + + using System.Diagnostics; + + using System; + + /// + /// + /// Provides functionality for a control + /// to parent other controls. + /// + public interface IContainerControl { + /// + /// + /// Indicates the control that is currently active on the container control. + /// + Control ActiveControl { get; set; } + /// + /// + /// Activates the specified control. + /// + bool ActivateControl(Control active); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ICurrencyManagerProvider.cs b/WindowsForms/Managed/System/WinForms/ICurrencyManagerProvider.cs new file mode 100644 index 000000000..68751d955 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ICurrencyManagerProvider.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + [SRDescription(SR.ICurrencyManagerProviderDescr)] + public interface ICurrencyManagerProvider { + + /// + /// + /// Return the main currency manager for this data source. + /// + CurrencyManager CurrencyManager { get; } + + /// + /// + /// Return a related currency manager for specified data member on this data source. + /// If data member is null or empty, this method returns the data source's main currency + /// manager (ie. this method returns the same value as the CurrencyManager property). + /// + CurrencyManager GetRelatedCurrencyManager(string dataMember); + } + +} diff --git a/WindowsForms/Managed/System/WinForms/IDataGridEditingService.cs b/WindowsForms/Managed/System/WinForms/IDataGridEditingService.cs new file mode 100644 index 000000000..17189f7d1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IDataGridEditingService.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// The DataGrid exposes hooks to request editing commands + /// via this interface. + /// + public interface IDataGridEditingService { + /// + bool BeginEdit(DataGridColumnStyle gridColumn, int rowNumber); + + /// + bool EndEdit(DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort); + } +} diff --git a/WindowsForms/Managed/System/WinForms/IDataObject.cs b/WindowsForms/Managed/System/WinForms/IDataObject.cs new file mode 100644 index 000000000..846ec90d5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IDataObject.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// Provides a format-independent mechanism for transferring + /// data. + /// + [System.Runtime.InteropServices.ComVisible(true)] + public interface IDataObject { + /// + /// + /// Retrieves the data associated with the specified data + /// format, using autoConvert to determine whether to convert the data to the + /// format. + /// + object GetData(string format, bool autoConvert); + + /// + /// + /// Retrieves the data associated with the specified data format. + /// + object GetData(string format); + + /// + /// + /// Retrieves the data associated with the specified class + /// type format. + /// + object GetData(Type format); + + /// + /// + /// Stores the specified data and its associated format in + /// this instance, using autoConvert to specify whether the data can be converted + /// to another format. + /// + void SetData(string format, bool autoConvert, object data); + + /// + /// + /// Stores the specified data and its associated format in this + /// instance. + /// + void SetData(string format, object data); + + /// + /// + /// Stores the specified data and its associated class type in this + /// instance. + /// + void SetData(Type format, object data); + + /// + /// + /// Stores the specified data in this instance, using the class of the + /// data for the format. + /// + void SetData(object data); + + /// + /// + /// Determines whether data stored in this instance is + /// associated with the specified format, using autoConvert to determine whether to + /// convert the data to the format. + /// + bool GetDataPresent(string format, bool autoConvert); + + /// + /// + /// Determines whether data stored in this instance is associated with, or can be + /// converted to, the specified format. + /// + bool GetDataPresent(string format); + + /// + /// + /// Determines whether data stored in this instance is associated with, or can be converted to, the specified format. + /// + bool GetDataPresent(Type format); + + /// + /// + /// Gets a list of all formats that data stored in this + /// instance is associated with or can be converted to, using autoConvert to determine whether to + /// retrieve all formats that the data can be converted to or only native data + /// formats. + /// + string[] GetFormats(bool autoConvert); + + /// + /// + /// Gets a list of all formats that data stored in this instance is associated + /// with or can be converted to. + /// + string[] GetFormats(); + } +} diff --git a/WindowsForms/Managed/System/WinForms/IFeatureSupport.cs b/WindowsForms/Managed/System/WinForms/IFeatureSupport.cs new file mode 100644 index 000000000..dfdbdeb24 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IFeatureSupport.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Configuration.Assemblies; + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// Specifies a standard + /// interface for retrieving feature information from the current system. + /// + /// + + public interface IFeatureSupport { + + /// + /// + /// + /// Determines whether any version of the specified feature + /// is currently available + /// on the system. + /// + /// + bool IsPresent(object feature); + + /// + /// + /// + /// Determines whether the specified or newer version of the + /// specified feature + /// is currently available on the system. + /// + /// + bool IsPresent(object feature, Version minimumVersion); + + /// + /// + /// + /// Retrieves the version + /// of the specified feature. + /// + /// + Version GetVersionPresent(object feature); + } + +} diff --git a/WindowsForms/Managed/System/WinForms/IFileReaderService.cs b/WindowsForms/Managed/System/WinForms/IFileReaderService.cs new file mode 100644 index 000000000..9b0f48ec6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IFileReaderService.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.IO; + + // CONSIDER: chrisan, REMOVE ME! I'm a workaround for security... + /// + /// + /// + /// + public interface IFileReaderService { + /// + Stream OpenFileFromSource(string relativePath); + } +} diff --git a/WindowsForms/Managed/System/WinForms/IMEMode.cs b/WindowsForms/Managed/System/WinForms/IMEMode.cs new file mode 100644 index 000000000..17af8c41e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IMEMode.cs @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies a value that determines the IME (Input Method Editor) status of the + /// object when that object is selected. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum ImeMode { + + /// + /// + /// + /// Inherit (Default). This value indicates inherit the ImeMode from the parent control. For controls with no parent, + /// the ImeMode will default to NoControl. + /// + /// + Inherit = -1, + + /// + /// + /// + /// None. This value indicates "No control to IME". When the IMEMode property is set to 0, you can use the + /// IMEStatus function to determine the current IME status. + /// + /// + NoControl = 0, + + /// + /// + /// + /// IME on. This value indicates that the IME is on and characters specific to Chinese or Japanese can be entered. + /// This setting is valid for Japanese, Simplified Chinese, and Traditional Chinese IME only. + /// + /// + On = 1, + + /// + /// + /// + /// IME off. This mode indicates that the IME is off, meaning that the object behaves the same as English entry mode. + /// This setting is valid for Japanese, Simplified Chinese, and Traditional Chinese IME only. + /// + /// + Off = 2, + + /// + /// + /// + /// IME disabled. This mode is similar to IMEMode = 2, except the value 3 disables IME. With this setting, the users + /// cannot turn the IME on from the keyboard, and the IME floating window is hidden. This setting is valid for Japanese IME only. + /// + /// + Disable = 3, + + /// + /// + /// + /// Hiragana double-byte characters (DBC). This setting is valid for Japanese IME only. + /// + /// + Hiragana = 4, + + /// + /// + /// + /// Katakana DBC. This setting is valid for Japanese IME only. + /// + /// + Katakana = 5, + + /// + /// + /// + /// Katakana single-byte characters (SBC). This setting is valid for Japanese IME only. + /// + /// + KatakanaHalf = 6, + + /// + /// + /// + /// Alphanumeric DBC. This setting is valid for Japanese IME only. + /// + /// + AlphaFull = 7, + + /// + /// + /// + /// Alphanumeric SBC. This setting is valid for Japanese IME only. + /// + /// + Alpha = 8, + + /// + /// + /// + /// Hangeul DBC. This setting is valid for Korean IME only. + /// + /// + HangulFull = 9, + + /// + /// + /// + /// Hangeul SBC. This setting is valid for Korean IME only. + /// + /// + Hangul = 10, + + /// + /// + /// + /// Ime Closed. This setting is valid for Chinese IME only. + /// + /// + Close = 11, + + /// + /// + /// + /// Ime On HalfShape. This setting is valid for Chinese IME only. + /// Note: This value is for internal use only - See QFE 4448. + /// + /// + OnHalf = 12, + } +} diff --git a/WindowsForms/Managed/System/WinForms/IMessageFilter.cs b/WindowsForms/Managed/System/WinForms/IMessageFilter.cs new file mode 100644 index 000000000..f28073be8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IMessageFilter.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// + /// Defines a message filter interface. + /// + public interface IMessageFilter { + + /// + /// + /// Filters out a message before it is dispatched. + /// + /// SECREVIEW: + /// + /// The link demand on the interface method isn't sufficient to prevent a partial trust caller from re-implementing this interface on subclasses + /// of Framework object that already implement this interface and utilize AddMessageFilter. + /// As a result, the subclass's implementation will get called by the message pump instead of the base class's implementation. + /// This would allow untrusted code to start examining message traffic or filter out any messages at whim.So please be careful while implementing this Interface. + /// Please refer to VsWhidbey : 423553 for more details. + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + bool PreFilterMessage(ref Message m); + } +} diff --git a/WindowsForms/Managed/System/WinForms/IMessageModifyAndFilter.cs b/WindowsForms/Managed/System/WinForms/IMessageModifyAndFilter.cs new file mode 100644 index 000000000..24432a682 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IMessageModifyAndFilter.cs @@ -0,0 +1,9 @@ +namespace System.Windows.Forms { + using System; + + // this is used by Application.cs to detect if we should respect changes to + // the message as well as whether or not we should filter the message. + internal interface IMessageModifyAndFilter : IMessageFilter { + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ISupportOleDropSource.cs b/WindowsForms/Managed/System/WinForms/ISupportOleDropSource.cs new file mode 100644 index 000000000..aff2fb917 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ISupportOleDropSource.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + internal interface ISupportOleDropSource { + + /// + /// + /// Summary of OnQueryContinueDrag. + /// + /// + void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent); + + /// + /// + /// Summary of OnGiveFeedback. + /// + /// + void OnGiveFeedback(GiveFeedbackEventArgs gfbevent); + } +} diff --git a/WindowsForms/Managed/System/WinForms/ISupportOleDropTarget.cs b/WindowsForms/Managed/System/WinForms/ISupportOleDropTarget.cs new file mode 100644 index 000000000..5be99b517 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ISupportOleDropTarget.cs @@ -0,0 +1,38 @@ + //------------------------------------------------------------------------------ + // + // Copyright (c) Microsoft Corporation. All rights reserved. + // + //------------------------------------------------------------------------------ + + + namespace System.Windows.Forms { + using System; + + public interface IDropTarget { + + /// + /// + /// Summary of OnDragEnter. + /// + /// + void OnDragEnter(DragEventArgs e); + /// + /// + /// Summary of OnDragLeave. + /// + /// + void OnDragLeave(System.EventArgs e); + /// + /// + /// Summary of OnDragDrop. + /// + /// + void OnDragDrop(DragEventArgs e); + /// + /// + /// Summary of OnDragOver. + /// + /// + void OnDragOver(DragEventArgs e); + } + } diff --git a/WindowsForms/Managed/System/WinForms/ISupportToolStripPanel.cs b/WindowsForms/Managed/System/WinForms/ISupportToolStripPanel.cs new file mode 100644 index 000000000..8cf9eab32 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ISupportToolStripPanel.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + + internal interface ISupportToolStripPanel { + ToolStripPanelRow ToolStripPanelRow { + get; set; + } + + ToolStripPanelCell ToolStripPanelCell { + get; + } + + + bool Stretch { + get; set; + } + + bool IsCurrentlyDragging { + get; + } + + void BeginDrag(); + + void EndDrag(); + + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/IWin32window.cs b/WindowsForms/Managed/System/WinForms/IWin32window.cs new file mode 100644 index 000000000..482c99c86 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IWin32window.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// Provides an interface to expose Win32 HWND handles. + /// + [System.Runtime.InteropServices.Guid("458AB8A2-A1EA-4d7b-8EBE-DEE5D3D9442C"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] + [System.Runtime.InteropServices.ComVisible(true)] + public interface IWin32Window { + + /// + /// + /// Gets the handle to the window represented by the implementor. + /// + IntPtr Handle { get; } + } +} diff --git a/WindowsForms/Managed/System/WinForms/IWindowTarget.cs b/WindowsForms/Managed/System/WinForms/IWindowTarget.cs new file mode 100644 index 000000000..72135b660 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/IWindowTarget.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.Windows.Forms; + using Microsoft.Win32; + + /// + /// + /// This interface defines the communication layer between + /// a Control object and the Win32 API. Each Control object + /// has an internal implementation this interface that is called + /// by the Win32 window. + /// + /// + public interface IWindowTarget { + + /// + /// + /// Called when the window handle of the control has changed. + /// + void OnHandleChange(IntPtr newHandle); + + /// + /// + /// Called to do control-specific processing for this window. + /// + void OnMessage(ref Message m); + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ImageIndexConverter.cs b/WindowsForms/Managed/System/WinForms/ImageIndexConverter.cs new file mode 100644 index 000000000..78d7140c9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ImageIndexConverter.cs @@ -0,0 +1,186 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// ImageIndexConverter is a class that can be used to convert + /// image index values one data type to another. + /// + public class ImageIndexConverter : Int32Converter { + + private string parentImageListProperty = "Parent"; + + /// + protected virtual bool IncludeNoneAsStandardValue { + get { + return true; + } + } + + /// + /// this is the property to look at when there is no ImageList property + /// on the current object. For example, in ToolBarButton - the ImageList is + /// on the ToolBarButton.Parent property. In WinBarItem, the ImageList is on + /// the WinBarItem.Owner property. + /// + internal string ParentImageListProperty { + get { + return parentImageListProperty; + } + set { + parentImageListProperty = value; + } + } + + /// + /// + /// + /// Converts the given value object to a 32-bit signed integer object. + /// + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + String stringValue = value as String; + if (stringValue != null && String.Compare(stringValue, SR.GetString(SR.toStringNone), true, culture) == 0) { + return -1; + } + + return base.ConvertFrom(context, culture, value); + } + + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string) && value is int && ((int)value) == -1) { + return SR.GetString(SR.toStringNone); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (context != null && context.Instance != null) { + object instance = context.Instance; + + PropertyDescriptor imageListProp = ImageListUtils.GetImageListProperty(context.PropertyDescriptor, ref instance); + + while (instance != null && imageListProp == null) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(instance); + + foreach (PropertyDescriptor prop in props) { + if (typeof(ImageList).IsAssignableFrom(prop.PropertyType)) { + imageListProp = prop; + break; + } + } + + if (imageListProp == null) { + + // We didn't find the image list in this component. See if the + // component has a "parent" property. If so, walk the tree... + // + PropertyDescriptor parentProp = props[ParentImageListProperty]; + if (parentProp != null) { + instance = parentProp.GetValue(instance); + } + else { + // Stick a fork in us, we're done. + // + instance = null; + } + } + } + + if (imageListProp != null) { + ImageList imageList = (ImageList)imageListProp.GetValue(instance); + + if (imageList != null) { + + // Create array to contain standard values + // + object[] values; + int nImages = imageList.Images.Count; + if (IncludeNoneAsStandardValue) { + values = new object[nImages + 1]; + values[nImages] = -1; + } + else { + values = new object[nImages]; + } + + + // Fill in the array + // + for (int i = 0; i < nImages; i++) { + values[i] = i; + } + + return new StandardValuesCollection(values); + } + } + } + + if (IncludeNoneAsStandardValue) { + return new StandardValuesCollection(new object[] {-1}); + } + else { + return new StandardValuesCollection(new object[0]); + } + } + + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + return false; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ImageKeyConverter.cs b/WindowsForms/Managed/System/WinForms/ImageKeyConverter.cs new file mode 100644 index 000000000..e90427122 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ImageKeyConverter.cs @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + using System.Collections.Specialized; + + /// + /// + /// ImageIndexConverter is a class that can be used to convert + /// image index values one data type to another. + /// + public class ImageKeyConverter : StringConverter { + + private string parentImageListProperty = "Parent"; + + /// + protected virtual bool IncludeNoneAsStandardValue { + get { + return true; + } + } + + /// + /// this is the property to look at when there is no ImageList property + /// on the current object. For example, in ToolBarButton - the ImageList is + /// on the ToolBarButton.Parent property. In WinBarItem, the ImageList is on + /// the WinBarItem.Owner property. + /// + internal string ParentImageListProperty { + get { + return parentImageListProperty; + } + set { + parentImageListProperty = value; + } + } + /// + /// + /// Gets a value indicating whether this converter can convert an object in the + /// given source type to a string using the specified context. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Converts the specified value object to a string object. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is string) { + return (string)value; + } + if (value == null) { + return ""; + } + return base.ConvertFrom(context, culture, value); + } + + + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string) && value != null && value is string && ((string)value).Length == 0) { + return SR.GetString(SR.toStringNone); + } + else if (destinationType == typeof(string) && (value == null)) { + return SR.GetString(SR.toStringNone); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (context != null && context.Instance != null) { + object instance = context.Instance; + PropertyDescriptor imageListProp = ImageListUtils.GetImageListProperty(context.PropertyDescriptor, ref instance); + + while (instance != null && imageListProp == null) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(instance); + + foreach (PropertyDescriptor prop in props) { + if (typeof(ImageList).IsAssignableFrom(prop.PropertyType)) { + imageListProp = prop; + break; + } + } + + if (imageListProp == null) { + + // We didn't find the image list in this component. See if the + // component has a "parent" property. If so, walk the tree... + // + PropertyDescriptor parentProp = props[ParentImageListProperty]; + if (parentProp != null) { + instance = parentProp.GetValue(instance); + } + else { + // Stick a fork in us, we're done. + // + instance = null; + } + } + } + + if (imageListProp != null) { + ImageList imageList = (ImageList)imageListProp.GetValue(instance); + + if (imageList != null) { + + // Create array to contain standard values + // + object[] values; + int nImages = imageList.Images.Count; + if (IncludeNoneAsStandardValue) { + values = new object[nImages + 1]; + values[nImages] = ""; + } + else { + values = new object[nImages]; + } + + + // Fill in the array + // + StringCollection imageKeys = imageList.Images.Keys; + for (int i = 0; i < imageKeys.Count; i++) { + if ((imageKeys[i] != null) && (imageKeys[i].Length != 0)) + values[i] = imageKeys[i]; + } + + return new StandardValuesCollection(values); + } + } + } + + if (IncludeNoneAsStandardValue) { + return new StandardValuesCollection(new object[] {""}); + } + else { + return new StandardValuesCollection(new object[0]); + } + } + + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ImageLayout.cs b/WindowsForms/Managed/System/WinForms/ImageLayout.cs new file mode 100644 index 000000000..63f200bd5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ImageLayout.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + /// + /// + /// Specifies the position of the image on the control. + /// + public enum ImageLayout { + /// + /// + /// + /// The image is aligned TOP - LEFT across the controls client rectangle. + /// + /// + None, + /// + /// + /// + /// The image is tiled across the controls client rectangle. + /// + /// + Tile, + /// + /// + /// + /// The image is centred within the controls client rectangle. + /// + /// + Center, + /// + /// + /// + /// The image is streched across the controls client rectangle. + /// + /// + Stretch, + /// + /// + /// + /// The image is streched across the controls client rectangle. + /// + /// + Zoom, + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ImageList.cs b/WindowsForms/Managed/System/WinForms/ImageList.cs new file mode 100644 index 000000000..2b82ba80e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ImageList.cs @@ -0,0 +1,1654 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Collections.Specialized; + using System.Collections; + using System.Drawing; + using System.Drawing.Imaging; + using System.Drawing.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.IO; + using System.ComponentModel.Design.Serialization; + using System.Runtime.Versioning; + + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + + /// + /// + /// The ImageList is an object that stores a collection of Images, most + /// commonly used by other controls, such as the ListView, TreeView, or + /// Toolbar. You can add either bitmaps or Icons to the ImageList, and the + /// other controls will be able to use the Images as they desire. + /// + [ + Designer("System.Windows.Forms.Design.ImageListDesigner, " + AssemblyRef.SystemDesign), + ToolboxItemFilter("System.Windows.Forms"), + DefaultProperty("Images"), + TypeConverter(typeof(ImageListConverter)), + DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionImageList) + ] + public sealed class ImageList : Component { + + // gpr: Copied from Icon + private static Color fakeTransparencyColor = Color.FromArgb(0x0d, 0x0b, 0x0c); + private static Size DefaultImageSize = new Size(16, 16); + + private const int INITIAL_CAPACITY = 4; + private const int GROWBY = 4; + + private const int MAX_DIMENSION = 256; + private static int maxImageWidth = MAX_DIMENSION; + private static int maxImageHeight = MAX_DIMENSION; + private static bool isScalingInitialized; + + private NativeImageList nativeImageList; + + // private int himlTemp; + // private Bitmap temp = null; // Used for drawing + + private ColorDepth colorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + private Color transparentColor = Color.Transparent; + private Size imageSize = DefaultImageSize; + + private ImageCollection imageCollection; + + private object userData; + + // The usual handle virtualization problem, with a new twist: image + // lists are lossy. At runtime, we delay handle creation as long as possible, and store + // away the original images until handle creation (and hope no one disposes of the images!). At design time, we keep the originals around indefinitely. + // This variable will become null when the original images are lost. See ASURT 65162. + private IList /* of Original */ originals = new ArrayList(); + private EventHandler recreateHandler = null; + private EventHandler changeHandler = null; + + private bool inAddRange = false; + + /// + /// + /// Creates a new ImageList Control with a default image size of 16x16 + /// pixels + /// + public ImageList() { // DO NOT DELETE -- AUTOMATION BP 1 + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + maxImageWidth = DpiHelper.LogicalToDeviceUnitsX(MAX_DIMENSION); + maxImageHeight = DpiHelper.LogicalToDeviceUnitsY(MAX_DIMENSION); + } + isScalingInitialized = true; + } + } + + /// + /// + /// Creates a new ImageList Control with a default image size of 16x16 + /// pixels and adds the ImageList to the passed in container. + /// + public ImageList(IContainer container) : this() { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + // This class is for classes that want to support both an ImageIndex + // and ImageKey. We want to toggle between using keys or indexes. + // Default is to use the integer index. + internal class Indexer { + private string key = String.Empty; + private int index = -1; + private bool useIntegerIndex = true; + private ImageList imageList = null; + + public virtual ImageList ImageList { + get { return imageList; } + set { imageList = value; } + } + + public virtual string Key { + get { return key; } + set { + index = -1; + key = (value == null ? String.Empty : value); + useIntegerIndex = false; + } + } + + public virtual int Index { + get { return index; } + set { + key = String.Empty; + index = value; + useIntegerIndex = true; + } + + } + + public virtual int ActualIndex { + get { + if (useIntegerIndex) { + return Index; + } + else if (ImageList != null) { + return ImageList.Images.IndexOfKey(Key); + } + + return -1; + } + } + } + + /// + /// + /// Retrieves the color depth of the imagelist. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ImageListColorDepthDescr) + ] + public ColorDepth ColorDepth { + get { + return colorDepth; + } + set { + // ColorDepth is not conitguous - list the members instead. + if (!ClientUtils.IsEnumValid_NotSequential(value, + (int)value, + (int)ColorDepth.Depth4Bit, + (int)ColorDepth.Depth8Bit, + (int)ColorDepth.Depth16Bit, + (int)ColorDepth.Depth24Bit, + (int)ColorDepth.Depth32Bit)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth)); + } + + if (colorDepth != value) { + colorDepth = value; + PerformRecreateHandle("ColorDepth"); + } + } + } + + private bool ShouldSerializeColorDepth() { + return (Images.Count==0); + } + private void ResetColorDepth() { + ColorDepth = ColorDepth.Depth8Bit; + } + + /// + /// + /// The handle of the ImageList object. This corresponds to a win32 + /// HIMAGELIST Handle. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ImageListHandleDescr) + ] + public IntPtr Handle { + get { + if (nativeImageList == null) { + CreateHandle(); + } + return nativeImageList.Handle; + } + } + + /// + /// + /// Whether or not the underlying Win32 handle has been created. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ImageListHandleCreatedDescr) + ] + public bool HandleCreated { + get { return nativeImageList != null; } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(null), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ImageListImagesDescr), + MergableProperty(false) + ] + public ImageCollection Images { + get { + if (imageCollection == null) + imageCollection = new ImageCollection(this); + return imageCollection; + } + } + + /// + /// + /// Returns the size of the images in the ImageList + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ImageListSizeDescr) + ] + public Size ImageSize { + get { + return imageSize; + } + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "ImageSize", "Size.Empty")); + } + + // ImageList appears to consume an exponential amount of memory + // based on image size x bpp. Restrict this to a reasonable maximum + // to keep people's systems from crashing. + // + if (value.Width <= 0 || value.Width > maxImageWidth) { + throw new ArgumentOutOfRangeException("ImageSize", SR.GetString(SR.InvalidBoundArgument, "ImageSize.Width", value.Width.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture), maxImageWidth.ToString())); + } + + if (value.Height <= 0 || value.Height > maxImageHeight) { + throw new ArgumentOutOfRangeException("ImageSize", SR.GetString(SR.InvalidBoundArgument, "ImageSize.Height", value.Height.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture), maxImageHeight.ToString())); + } + + if (imageSize.Width != value.Width || imageSize.Height != value.Height) { + imageSize = new Size(value.Width, value.Height); + PerformRecreateHandle("ImageSize"); + } + } + } + + private bool ShouldSerializeImageSize() { + return (Images.Count==0); + } + + /// + /// + /// Returns an ImageListStreamer, or null if the image list is empty. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(null), + SRDescription(SR.ImageListImageStreamDescr) + ] + public ImageListStreamer ImageStream { + get { + if (Images.Empty) + return null; + + // No need for us to create the handle, because any serious attempts to use the + // ImageListStreamer will do it for us. + return new ImageListStreamer(this); + } + set { + if (value != null) { + + NativeImageList himl = value.GetNativeImageList(); + if (himl != null && himl != this.nativeImageList) { + bool recreatingHandle = this.HandleCreated;//We only need to fire RecreateHandle if there was a previous handle + DestroyHandle(); + originals = null; + this.nativeImageList = new NativeImageList(SafeNativeMethods.ImageList_Duplicate(new HandleRef(himl, himl.Handle))); + int x, y; + if(SafeNativeMethods.ImageList_GetIconSize(new HandleRef(this, this.nativeImageList.Handle), out x, out y)) { + imageSize = new Size(x,y); + } + // need to get the image bpp + NativeMethods.IMAGEINFO imageInfo = new NativeMethods.IMAGEINFO(); // review? do I need to delete the mask and image? + if(SafeNativeMethods.ImageList_GetImageInfo(new HandleRef(this, this.nativeImageList.Handle), 0, imageInfo)) { + NativeMethods.BITMAP bmp = new NativeMethods.BITMAP(); + UnsafeNativeMethods.GetObject(new HandleRef(null, imageInfo.hbmImage), Marshal.SizeOf(bmp), bmp); + switch(bmp.bmBitsPixel) { + case 4: + colorDepth = ColorDepth.Depth4Bit; + break; + case 8: + colorDepth = ColorDepth.Depth8Bit; + break; + case 16: + colorDepth = ColorDepth.Depth16Bit; + break; + case 24: + colorDepth = ColorDepth.Depth24Bit; + break; + case 32: + colorDepth = ColorDepth.Depth32Bit; + break; + default: + Debug.Fail("Unknown color depth"); + break; + } + } + + Images.ResetKeys(); + if (recreatingHandle) { + OnRecreateHandle(new EventArgs()); + } + } + } + else + { + DestroyHandle(); + Images.Clear(); + } + + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// The color to treat as transparent. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ImageListTransparentColorDescr) + ] + public Color TransparentColor { + get { + return transparentColor; + } + set { + transparentColor = value; + } + } + + // Whether to use the transparent color, or rely on alpha instead + private bool UseTransparentColor { + get { return TransparentColor.A > 0;} + } + + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.ImageListOnRecreateHandleDescr) + ] + public event EventHandler RecreateHandle { + add { + recreateHandler += value; + } + remove { + recreateHandler -= value; + } + } + + internal event EventHandler ChangeHandle { + add { + changeHandler += value; + } + remove { + changeHandler -= value; + } + } + + //Creates a bitmap from the original image source.. + // + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap CreateBitmap(Original original, out bool ownsBitmap) { + Color transparent = transparentColor; + ownsBitmap = false; + if ((original.options & OriginalOptions.CustomTransparentColor) != 0) + transparent = original.customTransparentColor; + + Bitmap bitmap; + if (original.image is Bitmap) { + bitmap = (Bitmap) original.image; + } + else if (original.image is Icon) { + bitmap = ((Icon)original.image).ToBitmap(); + ownsBitmap = true; + } + else { + bitmap = new Bitmap((Image)original.image); + ownsBitmap = true; + } + + if (transparent.A > 0) { + // ImageList_AddMasked doesn't work on high color bitmaps, + // so we always create the mask ourselves + Bitmap source = bitmap; + bitmap = (Bitmap) bitmap.Clone(); + bitmap.MakeTransparent(transparent); + if(ownsBitmap) + source.Dispose(); + ownsBitmap = true; + } + + Size size = bitmap.Size; + if ((original.options & OriginalOptions.ImageStrip) != 0) { + // strip width must be a positive multiple of image list width + if (size.Width == 0 || (size.Width % imageSize.Width) != 0) + throw new ArgumentException(SR.GetString(SR.ImageListStripBadWidth), "original"); + if (size.Height != imageSize.Height) + throw new ArgumentException(SR.GetString(SR.ImageListImageTooShort), "original"); + } + else if (!size.Equals(ImageSize)) { + Bitmap source = bitmap; + bitmap = new Bitmap(source, ImageSize); + if(ownsBitmap) + source.Dispose(); + ownsBitmap = true; + } + return bitmap; + + } + + private int AddIconToHandle(Original original, Icon icon) { + try { + Debug.Assert(HandleCreated, "Calling AddIconToHandle when there is no handle"); + int index = SafeNativeMethods.ImageList_ReplaceIcon(new HandleRef(this, Handle), -1, new HandleRef(icon, icon.Handle)); + if (index == -1) throw new InvalidOperationException(SR.GetString(SR.ImageListAddFailed)); + return index; + } finally { + if((original.options & OriginalOptions.OwnsImage) != 0) { /// this is to handle the case were we clone the icon (see WHY WHY WHY below) + icon.Dispose(); + } + } + } + // Adds bitmap to the Imagelist handle... + // + private int AddToHandle(Original original, Bitmap bitmap) { + + Debug.Assert(HandleCreated, "Calling AddToHandle when there is no handle"); + IntPtr hMask = ControlPaint.CreateHBitmapTransparencyMask(bitmap); // Calls GDI to create Bitmap. + IntPtr hBitmap = ControlPaint.CreateHBitmapColorMask(bitmap, hMask); // Calls GDI+ to create Bitmap. Need to add handle to HandleCollector. + int index = SafeNativeMethods.ImageList_Add(new HandleRef(this, Handle), new HandleRef(null, hBitmap), new HandleRef(null, hMask)); + SafeNativeMethods.DeleteObject(new HandleRef(null, hBitmap)); + SafeNativeMethods.DeleteObject(new HandleRef(null, hMask)); + + if (index == -1) throw new InvalidOperationException(SR.GetString(SR.ImageListAddFailed)); + return index; + } + + /// + /// + /// Creates the underlying HIMAGELIST handle, and sets up all the + /// appropriate values with it. Inheriting classes overriding this method + /// should not forget to call base.createHandle(); + /// + private void CreateHandle() { + Debug.Assert(nativeImageList == null, "Handle already created, this may be a source of temporary GDI leaks"); + + int flags = NativeMethods.ILC_MASK; + switch (colorDepth) { + case ColorDepth.Depth4Bit: + flags |= NativeMethods.ILC_COLOR4; + break; + case ColorDepth.Depth8Bit: + flags |= NativeMethods.ILC_COLOR8; + break; + case ColorDepth.Depth16Bit: + flags |= NativeMethods.ILC_COLOR16; + break; + case ColorDepth.Depth24Bit: + flags |= NativeMethods.ILC_COLOR24; + break; + case ColorDepth.Depth32Bit: + flags |= NativeMethods.ILC_COLOR32; + break; + default: + Debug.Fail("Unknown color depth in ImageList"); + break; + } + + //VSW #123063: We enclose the imagelist handle create in a theming scope. This is a temporary solution + // till we tackle the bigger issue tracked by VSW #95247 + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + SafeNativeMethods.InitCommonControls(); + nativeImageList = new NativeImageList(SafeNativeMethods.ImageList_Create(imageSize.Width, imageSize.Height, flags, INITIAL_CAPACITY, GROWBY)); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + if (Handle == IntPtr.Zero) throw new InvalidOperationException(SR.GetString(SR.ImageListCreateFailed)); + SafeNativeMethods.ImageList_SetBkColor(new HandleRef(this, Handle), NativeMethods.CLR_NONE); + + Debug.Assert(originals != null, "Handle not yet created, yet original images are gone"); + for (int i = 0; i < originals.Count; i++) { + Original original = (Original) originals[i]; + if (original.image is Icon) { + AddIconToHandle(original, (Icon)original.image); + // NOTE: if we own the icon (it's been created by us) this WILL dispose the icon to avoid a GDI leak + // **** original.image is NOT LONGER VALID AFTER THIS POINT *** + } + else { + bool ownsBitmap = false; + Bitmap bitmapValue = CreateBitmap(original, out ownsBitmap); + AddToHandle(original, bitmapValue); + if(ownsBitmap) + bitmapValue.Dispose(); + } + } + originals = null; + } + + // Don't merge this function into Dispose() -- that base.Dispose() will damage the design time experience + private void DestroyHandle() { + if (HandleCreated) { + // Fix Dev10 TFS Bug 392946 - + // ImageList/NativeImageList leaks HIMAGELIST until finalized + nativeImageList.Dispose(); + nativeImageList = null; + originals = new ArrayList(); + } + } + + /// + /// + /// Frees all resources assocaited with this component. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if(originals != null) { // we might own some of the stuff that's not been created yet + foreach(Original original in originals) { + if((original.options & OriginalOptions.OwnsImage) != 0) { + ((IDisposable)original.image).Dispose(); + } + } + } + DestroyHandle(); + } + base.Dispose(disposing); + } + + /// + /// + /// Draw the image indicated by the given index on the given Graphics + /// at the given location. + /// + public void Draw(Graphics g, Point pt, int index) { + Draw(g, pt.X, pt.Y, index); + } + + /// + /// + /// Draw the image indicated by the given index on the given Graphics + /// at the given location. + /// + public void Draw(Graphics g, int x, int y, int index) { + Draw(g, x, y, imageSize.Width, imageSize.Height, index); + } + + /// + /// + /// Draw the image indicated by the given index using the location, size + /// and raster op code specified. The image is stretched or compressed as + /// necessary to fit the bounds provided. + /// + public void Draw(Graphics g, int x, int y, int width, int height, int index) { + if (index < 0 || index >= Images.Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + IntPtr dc = g.GetHdc(); + try { + SafeNativeMethods.ImageList_DrawEx(new HandleRef(this, Handle), index, new HandleRef(g, dc), x, y, + width, height, NativeMethods.CLR_NONE, NativeMethods.CLR_NONE, NativeMethods.ILD_TRANSPARENT); + } + finally { + g.ReleaseHdcInternal(dc); + } + } + + + private void CopyBitmapData(BitmapData sourceData, BitmapData targetData) { + // do the actual copy + int offsetSrc = 0; + int offsetDest = 0; + unsafe { + for (int i = 0; i < targetData.Height; i++) + { + IntPtr srcPtr, destPtr; + if (IntPtr.Size == 4) { + srcPtr = new IntPtr(sourceData.Scan0.ToInt32() + offsetSrc); + destPtr = new IntPtr(targetData.Scan0.ToInt32() + offsetDest); + } else { + srcPtr = new IntPtr(sourceData.Scan0.ToInt64() + offsetSrc); + destPtr = new IntPtr(targetData.Scan0.ToInt64() + offsetDest); + } + UnsafeNativeMethods.CopyMemory(new HandleRef(this, destPtr), new HandleRef(this, srcPtr), Math.Abs(targetData.Stride)); + offsetSrc += sourceData.Stride; + offsetDest += targetData.Stride; + } + } + } + + private static bool BitmapHasAlpha(BitmapData bmpData) { + if(bmpData.PixelFormat != PixelFormat.Format32bppArgb && bmpData.PixelFormat != PixelFormat.Format32bppRgb) { + return false; + } + bool hasAlpha = false; + unsafe { + for (int i = 0; i < bmpData.Height; i++) { + int offsetRow = i * bmpData.Stride; + for (int j = 3; j < bmpData.Width*4; j += 4) { // *4 is safe since we know PixelFormat is ARGB + unsafe { + byte* candidate = ((byte*)bmpData.Scan0.ToPointer()) + offsetRow + j; + if (*candidate != 0) { + hasAlpha = true; + goto Found; // gotos are fugly but it's the best thing here... + } + } + } + } + Found: + return hasAlpha; + } + } + + /// + /// + /// Returns the image specified by the given index. The bitmap returned is a + /// copy of the original image. + /// + // NOTE: forces handle creation, so doesn't return things from the original list + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.Process, ResourceScope.Machine | ResourceScope.Process)] + private Bitmap GetBitmap(int index) { + if (index < 0 || index >= Images.Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + Bitmap result=null; + + // if the imagelist is 32bpp, if the image slot at index + // has valid alpha information (not all zero... which is cause by windows just painting RGB values + // and not touching the alpha byte for images < 32bpp painted to a 32bpp imagelist) + // we're not using the mask. That means that + // we can just get the whole image strip, cut out the piece that we want + // and return that, that way we don't flatten the alpha by painting the value with the alpha... (ie using the alpha) + + if(ColorDepth == ColorDepth.Depth32Bit) { + + NativeMethods.IMAGEINFO imageInfo = new NativeMethods.IMAGEINFO(); // review? do I need to delete the mask and image inside of imageinfo? + if(SafeNativeMethods.ImageList_GetImageInfo(new HandleRef(this, this.Handle), index, imageInfo)) { + Bitmap tmpBitmap = null; + BitmapData bmpData = null; + BitmapData targetData = null; + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + tmpBitmap = Bitmap.FromHbitmap(imageInfo.hbmImage); + // + + + + + bmpData = tmpBitmap.LockBits(new Rectangle(imageInfo.rcImage_left,imageInfo.rcImage_top, imageInfo.rcImage_right-imageInfo.rcImage_left, imageInfo.rcImage_bottom-imageInfo.rcImage_top), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat); + + int offset = bmpData.Stride * imageSize.Height * index; + // we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data + if(BitmapHasAlpha(bmpData)) { + result = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format32bppArgb); + targetData = result.LockBits(new Rectangle(0, 0, imageSize.Width, imageSize.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + CopyBitmapData(bmpData, targetData); + } + } finally { + CodeAccessPermission.RevertAssert(); + if(tmpBitmap != null) { + if(bmpData != null) { + tmpBitmap.UnlockBits(bmpData); + } + tmpBitmap.Dispose(); + } + if(result != null && targetData != null) { + result.UnlockBits(targetData); + } + } + } + } + + if(result == null) { // paint with the mask but no alpha... + result = new Bitmap(imageSize.Width, imageSize.Height); + + Graphics graphics = Graphics.FromImage(result); + try { + IntPtr dc = graphics.GetHdc(); + try { + SafeNativeMethods.ImageList_DrawEx(new HandleRef(this, Handle), index, new HandleRef(graphics, dc), 0, 0, + imageSize.Width, imageSize.Height, NativeMethods.CLR_NONE, NativeMethods.CLR_NONE, NativeMethods.ILD_TRANSPARENT); + + } + finally { + graphics.ReleaseHdcInternal(dc); + } + } + finally { + graphics.Dispose(); + } + } + + // gpr: See Icon for description of fakeTransparencyColor + result.MakeTransparent(fakeTransparencyColor); + return result; + } + + + +#if DEBUG_ONLY_APIS + /// + /// + /// [To be supplied.] + /// + public Bitmap DebugOnly_GetMasterImage() { + if (Images.Empty) + return null; + + return Image.FromHBITMAP(GetImageInfo(0).hbmImage); + } + + /// + /// + /// [To be supplied.] + /// + public Bitmap DebugOnly_GetMasterMask() { + if (Images.Empty) + return null; + + return Image.FromHBITMAP(GetImageInfo(0).hbmMask); + } +#endif // DEBUG_ONLY_APIS + + /// + /// + /// Called when the Handle property changes. + /// + private void OnRecreateHandle(EventArgs eventargs) { + if (recreateHandler != null) { + recreateHandler(this, eventargs); + } + } + + private void OnChangeHandle(EventArgs eventargs) { + if (changeHandler != null) { + changeHandler(this, eventargs); + } + } + +#if false + /// + /// + /// Copies the image at the specified index into the temporary Bitmap object. + /// The temporary Bitmap object is used for stuff that the Windows ImageList + /// control doesn't support, such as stretching images or copying images from + /// different image lists. Since bitmap creation is expensive, the same instance + /// of the temporary Bitmap is reused. + /// + private void PutImageInTempBitmap(int index, bool useSnapshot) { + Debug.Assert(!useSnapshot || himlTemp != 0, "Where's himlTemp?"); + + IntPtr handleUse = (useSnapshot ? himlTemp : Handle); + int count = SafeNativeMethods.ImageList_GetImageCount(handleUse); + + if (index < 0 || index >= count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString())); + + if (temp != null) { + Size size = temp.Size; + if (!temp.Size.Equals(imageSize)) { + temp.Dispose(); + temp = null; + } + } + if (temp == null) { + temp = new Bitmap(imageSize.Width, imageSize.Height); + } + + temp.Transparent = useMask; + // OldGraphics gTemp = /*gpr useMask ? temp.ColorMask.GetGraphics() :*/ temp.GetGraphics(); + SafeNativeMethods.ImageList_DrawEx(handleUse, index, gTemp.Handle, 0, 0, + imageSize.Width, imageSize.Height, useMask ? 0 : NativeMethods.CLR_DEFAULT, NativeMethods.CLR_NONE, NativeMethods.ILD_NORMAL); + + if (useMask) { + gTemp = temp/*gpr .MonochromeMask*/.GetGraphics(); + SafeNativeMethods.ImageList_DrawEx(handleUse, index, gTemp.Handle, 0, 0, imageSize.Width, imageSize.Height, NativeMethods.CLR_DEFAULT, NativeMethods.CLR_NONE, NativeMethods.ILD_MASK); + } + } +#endif + + // PerformRecreateHandle doesn't quite do what you would suspect. + // Any existing images in the imagelist will NOT be copied to the + // new image list -- they really should. This bug has existed for a + // loooong time. + // The net effect is that if you add images to an imagelist, and + // then e.g. change the ImageSize any existing images will be lost + // and you will have to add them back. This is probably a corner case + // but it should be mentioned. + // + // The fix isn't as straightforward as you might think, i.e. we + // cannot just blindly store off the images and copy them into + // the newly created imagelist. E.g. say you change the ColorDepth + // from 8-bit to 32-bit. Just copying the 8-bit images would be wrong. + // Therefore we are going to leave this as is. Users should make sure + // to set these properties before actually adding the images. + + // The Designer works around this by shadowing any Property that ends + // up calling PerformRecreateHandle (ImageSize, ColorDepth, ImageStream). + + // Thus, if you add a new Property to ImageList which ends up calling + // PerformRecreateHandle, you must shadow the property in ImageListDesigner. + private void PerformRecreateHandle(string reason) { + if (!HandleCreated) return; + + if (originals == null || Images.Empty) + originals = new ArrayList(); // spoof it into thinking this is the first CreateHandle + + if (originals == null) + throw new InvalidOperationException(SR.GetString(SR.ImageListCantRecreate, reason)); + + DestroyHandle(); + CreateHandle(); + OnRecreateHandle(new EventArgs()); + } + + private void ResetImageSize() { + ImageSize = DefaultImageSize; + } + + private void ResetTransparentColor() { + TransparentColor = Color.LightGray; + } + + private bool ShouldSerializeTransparentColor() { + return !TransparentColor.Equals(Color.LightGray); + } + + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + string s = base.ToString(); + if (Images != null) { + return s + " Images.Count: " + Images.Count.ToString(CultureInfo.CurrentCulture) + ", ImageSize: " + ImageSize.ToString(); + } + else { + return s; + } + } + + internal class NativeImageList : IDisposable { + private IntPtr himl; +#if DEBUG + private string callStack; +#endif + + internal NativeImageList(IntPtr himl) { + this.himl = himl; +#if DEBUG + new EnvironmentPermission(PermissionState.Unrestricted).Assert(); + try { + callStack = Environment.StackTrace; + } + finally { + CodeAccessPermission.RevertAssert(); + } +#endif + } + + internal IntPtr Handle + { + get + { + return himl; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Dispose(bool disposing) { + if (himl != IntPtr.Zero) { + SafeNativeMethods.ImageList_Destroy(new HandleRef(null, himl)); + himl = IntPtr.Zero; + } + } + + ~NativeImageList() { + Dispose(false); + } + + } + + // An image before we add it to the image list, along with a few details about how to add it. + private class Original { + internal object image; + internal OriginalOptions options; + internal Color customTransparentColor = Color.Transparent; + + internal int nImages = 1; + + internal Original(object image, OriginalOptions options) + : this(image, options, Color.Transparent) { + } + + internal Original(object image, OriginalOptions options, int nImages) + : this(image, options, Color.Transparent) { + this.nImages = nImages; + } + + internal Original(object image, OriginalOptions options, Color customTransparentColor) { + Debug.Assert(image != null, "image is null"); + if (!(image is Icon) && !(image is Image)) { + throw new InvalidOperationException(SR.GetString(SR.ImageListEntryType)); + } + this.image = image; + this.options = options; + this.customTransparentColor = customTransparentColor; + if ((options & OriginalOptions.CustomTransparentColor) == 0) { + Debug.Assert(customTransparentColor.Equals(Color.Transparent), + "Specified a custom transparent color then told us to ignore it"); + } + } + } + + [Flags] + private enum OriginalOptions { + Default = 0x00, + + ImageStrip = 0x01, + CustomTransparentColor = 0x02, + OwnsImage = 0x04 + } + + + + + + // Everything other than set_All, Add, and Clear will force handle creation. + /// + /// + /// [To be supplied.] + /// + [ + Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public sealed class ImageCollection : IList { + private ImageList owner; + private ArrayList imageInfoCollection = new ArrayList(); + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + /// + /// + /// Returns the keys in the image list - images without keys return String.Empty. + /// + /// + public StringCollection Keys { + get { + // pass back a copy of the current state. + StringCollection keysCollection = new StringCollection(); + + for (int i = 0; i < imageInfoCollection.Count; i++) { + ImageInfo image = imageInfoCollection[i] as ImageInfo; + if ((image != null) && (image.Name != null) && (image.Name.Length != 0)) { + keysCollection.Add(image.Name); + } else { + keysCollection.Add(string.Empty); + } + } + return keysCollection; + } + } + internal ImageCollection(ImageList owner) { + this.owner = owner; + } + + internal void ResetKeys() { + if (imageInfoCollection!= null) + imageInfoCollection.Clear(); + + for (int i = 0; i < this.Count; i++) { + imageInfoCollection.Add(new ImageCollection.ImageInfo()); + } + } + + [Conditional("DEBUG")] + private void AssertInvariant() { + Debug.Assert(owner != null, "ImageCollection has no owner (ImageList)"); + Debug.Assert( (owner.originals == null) == (owner.HandleCreated), " Either we should have the original images, or the handle should be created"); + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false)] + public int Count { + [ResourceExposure(ResourceScope.None)] + get { + Debug.Assert(owner != null, "ImageCollection has no owner (ImageList)"); + + if (owner.HandleCreated) { + return SafeNativeMethods.ImageList_GetImageCount(new HandleRef(owner, owner.Handle)); + } + else { + int count = 0; + foreach(Original original in owner.originals) { + if (original != null) { + count += original.nImages; + } + } + return count; + } + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Determines if the ImageList has any images, without forcing a handle creation. + /// + public bool Empty { + get { + return Count == 0; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Image this[int index] { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + return owner.GetBitmap(index); + } + set { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + if (value == null) { + throw new ArgumentNullException("value"); + } + + if (!(value is Bitmap)) + throw new ArgumentException(SR.GetString(SR.ImageListBitmap)); + + AssertInvariant(); + Bitmap bitmap = (Bitmap)value; + + bool ownsImage = false; + if (owner.UseTransparentColor) { + // Since there's no ImageList_ReplaceMasked, we need to generate + // a transparent bitmap + Bitmap source = bitmap; + bitmap = (Bitmap) bitmap.Clone(); + bitmap.MakeTransparent(owner.transparentColor); + ownsImage = true; + } + + try { + IntPtr hMask = ControlPaint.CreateHBitmapTransparencyMask(bitmap); + IntPtr hBitmap = ControlPaint.CreateHBitmapColorMask(bitmap, hMask); + bool ok = SafeNativeMethods.ImageList_Replace(new HandleRef(owner, owner.Handle), index, new HandleRef(null, hBitmap), new HandleRef(null, hMask)); + SafeNativeMethods.DeleteObject(new HandleRef(null, hBitmap)); + SafeNativeMethods.DeleteObject(new HandleRef(null, hMask)); + + if (!ok) + throw new InvalidOperationException(SR.GetString(SR.ImageListReplaceFailed)); + + } finally { + if(ownsImage) { + bitmap.Dispose(); + } + } + } + } + + /// + /// + object IList.this[int index] { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return this[index]; + } + set { + if (value is Image) { + this[index] = (Image)value; + } + else { + throw new ArgumentException(SR.GetString(SR.ImageListBadImage), "value"); + } + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public Image this[string key] { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + // We do not support null and empty string as valid keys. + if ((key == null) || (key.Length == 0)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + + /// + /// + /// Adds an image to the end of the image list with a key accessor. + /// + public void Add(string key, Image image) { + Debug.Assert((this.Count == imageInfoCollection.Count), "The count of these two collections should be equal."); + + // Store off the name. + ImageInfo imageInfo = new ImageInfo(); + imageInfo.Name = key; + + // Add the image to the IList + Original original = new Original(image, OriginalOptions.Default); + Add(original, imageInfo); + + } + + /// + /// + /// Adds an icon to the end of the image list with a key accessor. + /// + public void Add(string key, Icon icon) { + Debug.Assert((this.Count == imageInfoCollection.Count), "The count of these two collections should be equal."); + + // Store off the name. + ImageInfo imageInfo = new ImageInfo(); + imageInfo.Name = key; + + // Add the image to the IList + Original original = new Original(icon, OriginalOptions.Default); + Add(original, imageInfo); + + + } + + + /// + /// + int IList.Add(object value) { + if (value is Image) { + Add((Image)value); + return Count - 1; + } + else { + throw new ArgumentException(SR.GetString(SR.ImageListBadImage), "value"); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Add(Icon value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + Add(new Original(value.Clone(), OriginalOptions.OwnsImage), null); // WHY WHY WHY do we clone here... + // changing it now is a breaking change, so we have to keep track of this specific icon and dispose that + } + + /// + /// + /// Add the given image to the ImageList. + /// + public void Add(Image value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + Original original = new Original(value, OriginalOptions.Default); + Add(original, null); + } + + /// + /// + /// Add the given image to the ImageList, using the given color + /// to generate the mask. The number of images to add is inferred from + /// the width of the given image. + /// + public int Add(Image value, Color transparentColor) { + if (value == null) { + throw new ArgumentNullException("value"); + } + Original original = new Original(value, OriginalOptions.CustomTransparentColor, + transparentColor); + return Add(original, null); + } + + private int Add(Original original, ImageInfo imageInfo) { + if (original == null || original.image == null) { + throw new ArgumentNullException("original"); + } + + int index = -1; + + AssertInvariant(); + + if (original.image is Bitmap) { + if (owner.originals != null) { + index = owner.originals.Add(original); + } + + if (owner.HandleCreated) { + bool ownsBitmap = false; + Bitmap bitmapValue = owner.CreateBitmap(original, out ownsBitmap); + index = owner.AddToHandle(original, bitmapValue); + if(ownsBitmap) + bitmapValue.Dispose(); + } + } + else if (original.image is Icon) { + if (owner.originals != null) { + index = owner.originals.Add(original); + } + if (owner.HandleCreated) { + index = owner.AddIconToHandle(original, (Icon)original.image); + // NOTE: if we own the icon (it's been created by us) this WILL dispose the icon to avoid a GDI leak + // **** original.image is NOT LONGER VALID AFTER THIS POINT *** + } + } + else { + throw new ArgumentException(SR.GetString(SR.ImageListBitmap)); + } + + // update the imageInfoCollection + // support AddStrip + if ((original.options & OriginalOptions.ImageStrip) != 0) { + for (int i = 0; i < original.nImages; i++) { + imageInfoCollection.Add(new ImageInfo()); + } + } else { + if (imageInfo == null) + imageInfo = new ImageInfo(); + imageInfoCollection.Add(imageInfo); + } + + if (!owner.inAddRange) + owner.OnChangeHandle(new EventArgs()); + + return index; + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(Image[] images) { + if (images == null) { + throw new ArgumentNullException("images"); + } + owner.inAddRange = true; + foreach(Image image in images) { + Add(image); + } + owner.inAddRange = false; + owner.OnChangeHandle(new EventArgs()); + } + + /// + /// + /// Add an image strip the given image to the ImageList. A strip is a single Image + /// which is treated as multiple images arranged side-by-side. + /// + public int AddStrip(Image value) { + + if (value == null) { + throw new ArgumentNullException("value"); + } + + // strip width must be a positive multiple of image list width + // + if (value.Width == 0 || (value.Width % owner.ImageSize.Width) != 0) + throw new ArgumentException(SR.GetString(SR.ImageListStripBadWidth), "value"); + if (value.Height != owner.ImageSize.Height) + throw new ArgumentException(SR.GetString(SR.ImageListImageTooShort), "value"); + + int nImages = value.Width / owner.ImageSize.Width; + + Original original = new Original(value, OriginalOptions.ImageStrip, nImages); + + return Add(original, null); + } + + /// + /// + /// Remove all images and masks from the ImageList. + /// + public void Clear() { + AssertInvariant(); + if (owner.originals != null) + owner.originals.Clear(); + + imageInfoCollection.Clear(); + + if (owner.HandleCreated) + SafeNativeMethods.ImageList_Remove(new HandleRef(owner, owner.Handle), -1); + + owner.OnChangeHandle(new EventArgs()); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool Contains(Image image) { + throw new NotSupportedException(); + } + + /// + /// + bool IList.Contains(object image) { + if (image is Image) { + return Contains((Image)image); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int IndexOf(Image image) { + throw new NotSupportedException(); + } + + /// + /// + int IList.IndexOf(object image) { + if (image is Image) { + return IndexOf((Image)image); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, + /// if found; otherwise, -1. + /// + public int IndexOfKey(String key) { + // Step 0 - Arg validation + if ((key == null) || (key.Length == 0)){ + return -1; // we dont support empty or null keys. + } + + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if ((imageInfoCollection[lastAccessedIndex] != null) && + (WindowsFormsUtils.SafeCompareStrings(((ImageInfo)imageInfoCollection[lastAccessedIndex]).Name, key, /* ignoreCase = */ true))) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if ((imageInfoCollection[i] != null) && + (WindowsFormsUtils.SafeCompareStrings(((ImageInfo)imageInfoCollection[i]).Name, key, /* ignoreCase = */ true))) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + AssertInvariant(); + for (int i = 0; i < Count; ++i) { + dest.SetValue(owner.GetBitmap(i), index++); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + // Forces handle creation + + AssertInvariant(); + Image[] images = new Image[Count]; + for (int i = 0; i < images.Length; ++i) + images[i] = owner.GetBitmap(i); + + return images.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void Remove(Image image) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object image) { + if (image is Image) { + Remove((Image)image); + owner.OnChangeHandle(new EventArgs()); + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + AssertInvariant(); + bool ok = SafeNativeMethods.ImageList_Remove(new HandleRef(owner, owner.Handle), index); + if (!ok) { + throw new InvalidOperationException(SR.GetString(SR.ImageListRemoveFailed)); + } else { + if ((imageInfoCollection != null) && (index >= 0 && index < imageInfoCollection.Count)) { + imageInfoCollection.RemoveAt(index); + owner.OnChangeHandle(new EventArgs()); + } + } + } + + + /// + /// + /// Removes the child control with the specified key. + /// + public void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// Sets/Resets the key accessor for an image already in the image list. + /// + public void SetKeyName(int index, string name) { + if (!IsValidIndex(index)) { + throw new IndexOutOfRangeException(); // + } + + if (imageInfoCollection[index] == null) { + imageInfoCollection[index] = new ImageInfo(); + } + + ((ImageInfo)imageInfoCollection[index]).Name = name; + } + + /// + /// + internal class ImageInfo { + private string name; + public ImageInfo() { + } + + public string Name { + get { return name; } + set { name = value; } + } + } + + } // end class ImageCollection + } + + /// + /// + internal class ImageListConverter : ComponentConverter { + + public ImageListConverter() : base(typeof(ImageList)) { + } + + /// + /// + /// + /// Gets a value indicating + /// whether this object supports properties using the + /// specified context. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ImageListStreamer.cs b/WindowsForms/Managed/System/WinForms/ImageListStreamer.cs new file mode 100644 index 000000000..ebfa6aa51 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ImageListStreamer.cs @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Runtime.Serialization; + using System.Diagnostics; + using System; + using System.Drawing; + using System.ComponentModel; + using System.IO; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Globalization; + using System.Security.Permissions; + + /// + /// + /// + [Serializable] + public sealed class ImageListStreamer : ISerializable, IDisposable { + + // compressed magic header. If we see this, the image stream is compressed. + // (unicode for MSFT). + // + private static readonly byte[] HEADER_MAGIC = new byte[] {0x4D, 0x53, 0x46, 0X74}; + private static object internalSyncObject = new object(); + + private ImageList imageList; + private ImageList.NativeImageList nativeImageList; + + internal ImageListStreamer(ImageList il) { + imageList = il; + } + + /** + * Constructor used in deserialization + */ + private ImageListStreamer(SerializationInfo info, StreamingContext context) + { + SerializationInfoEnumerator sie = info.GetEnumerator(); + if (sie == null) + { + return; + } + while (sie.MoveNext()) + { + if (String.Equals(sie.Name, "Data", StringComparison.OrdinalIgnoreCase)) + { +#if DEBUG + try { +#endif + byte[] dat = (byte[])sie.Value; + if (dat != null) + { + //VSW #123063: We enclose this imagelist handle create in a theming scope. This is a temporary + // solution till we tackle the bigger issue tracked by VSW #95247 + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try + { + MemoryStream ms = new MemoryStream(Decompress(dat)); + + lock (internalSyncObject) { + SafeNativeMethods.InitCommonControls(); + nativeImageList = new ImageList.NativeImageList( + SafeNativeMethods.ImageList_Read(new UnsafeNativeMethods.ComStreamFromDataStream(ms))); + } + } + finally + { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + if (nativeImageList.Handle == IntPtr.Zero) + { + throw new InvalidOperationException(SR.GetString(SR.ImageListStreamerLoadFailed)); + } + } +#if DEBUG + } + catch (Exception e) { + Debug.Fail("ImageList serialization failure: " + e.ToString()); + throw; + } +#endif + } + } + } + + /// + /// Compresses the given input, returning a new array that represents + /// the compressed data. + /// + private byte[] Compress(byte[] input) { + + int finalLength = 0; + int idx = 0; + int compressedIdx = 0; + + while(idx < input.Length) { + + byte current = input[idx++]; + byte runLength = 1; + + while(idx < input.Length && input[idx] == current && runLength < 0xFF) { + runLength++; + idx++; + } + + finalLength += 2; + } + + byte[] output = new byte[finalLength + HEADER_MAGIC.Length]; + + Buffer.BlockCopy(HEADER_MAGIC, 0, output, 0, HEADER_MAGIC.Length); + int idxOffset = HEADER_MAGIC.Length; + idx = 0; + + while(idx < input.Length) { + + byte current = input[idx++]; + byte runLength = 1; + + while(idx < input.Length && input[idx] == current && runLength < 0xFF) { + runLength++; + idx++; + } + + output[idxOffset + compressedIdx++] = runLength; + output[idxOffset + compressedIdx++] = current; + } + + Debug.Assert(idxOffset + compressedIdx == output.Length, "RLE Compression failure in ImageListStreamer -- didn't fill array"); + + // Validate that our compression routine works + #if DEBUG + byte[] debugCompare = Decompress(output); + Debug.Assert(debugCompare.Length == input.Length, "RLE Compression in ImageListStreamer is broken."); + int debugMaxCompare = input.Length; + for(int debugIdx = 0; debugIdx < debugMaxCompare; debugIdx++) { + if (debugCompare[debugIdx] != input[debugIdx]) { + Debug.Fail("RLE Compression failure in ImageListStreamer at byte offset " + debugIdx); + break; + } + } + #endif // DEBUG + + return output; + } + + /// + /// Decompresses the given input, returning a new array that represents + /// the uncompressed data. + /// + private byte[] Decompress(byte[] input) { + + int finalLength = 0; + int idx = 0; + int outputIdx = 0; + + // Check for our header. If we don't have one, + // we're not actually decompressed, so just return + // the original. + // + if (input.Length < HEADER_MAGIC.Length) { + return input; + } + + for(idx = 0; idx < HEADER_MAGIC.Length; idx++) { + if (input[idx] != HEADER_MAGIC[idx]) { + return input; + } + } + + // Ok, we passed the magic header test. + + for (idx = HEADER_MAGIC.Length; idx < input.Length; idx+=2) { + finalLength += input[idx]; + } + + byte[] output = new byte[finalLength]; + + idx = HEADER_MAGIC.Length; + + while(idx < input.Length) { + byte runLength = input[idx++]; + byte current = input[idx++]; + + int startIdx = outputIdx; + int endIdx = outputIdx + runLength; + + while(startIdx < endIdx) { + output[startIdx++] = current; + } + + outputIdx += runLength; + } + + return output; + } + + /// + /// + /// [To be supplied.] + /// + [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + public void /*cpr: ISerializable*/GetObjectData(SerializationInfo si, StreamingContext context) { + MemoryStream stream = new MemoryStream(); + + IntPtr handle = IntPtr.Zero; + if (imageList != null) { + handle = imageList.Handle; + } + else if (nativeImageList != null) { + handle = nativeImageList.Handle; + } + + if (handle == IntPtr.Zero || !WriteImageList(handle, stream)) + throw new InvalidOperationException(SR.GetString(SR.ImageListStreamerSaveFailed)); + + si.AddValue("Data", Compress(stream.ToArray())); + } + + /// + /// + internal ImageList.NativeImageList GetNativeImageList() { + return nativeImageList; + } + + private bool WriteImageList(IntPtr imagelistHandle, Stream stream) { + // What we need to do here is use WriteEx if comctl 6 or above, and Write otherwise. However, till we can fix + // VSWhidbey #248889, there isn't a reliable way to tell which version of comctl fusion is binding to. + // So for now, we try to bind to WriteEx, and if that entry point isn't found, we use Write. + + try { + int hResult = SafeNativeMethods.ImageList_WriteEx(new HandleRef(this, imagelistHandle), NativeMethods.ILP_DOWNLEVEL, new UnsafeNativeMethods.ComStreamFromDataStream(stream)); + return (hResult == NativeMethods.S_OK); + } + catch (EntryPointNotFoundException) { + // WriteEx wasn't found - that's fine - we will use Write. + } + + return SafeNativeMethods.ImageList_Write(new HandleRef(this, imagelistHandle), new UnsafeNativeMethods.ComStreamFromDataStream(stream)); + } + + /// + /// + /// Disposes the native image list handle. + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) { + if (disposing) { + if (nativeImageList != null) { + nativeImageList.Dispose(); + nativeImageList = null; + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/InputLangChangeEvent.cs b/WindowsForms/Managed/System/WinForms/InputLangChangeEvent.cs new file mode 100644 index 000000000..7e1bb3092 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLangChangeEvent.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.Globalization; + using System.Windows.Forms; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class InputLanguageChangedEventArgs : EventArgs { + + /// + /// + /// The input language. + /// + private readonly InputLanguage inputLanguage; + + /// + /// + /// The culture of the input language. + /// + private readonly CultureInfo culture; + /// + /// + /// The charSet associated with the new input language. + /// + private readonly byte charSet; + + /** + * @deprecated. Use the other constructor instead. + */ + /// + /// + /// + /// + /// Initializes a new instance of the class with the + /// specified locale and character set. + /// + /// + public InputLanguageChangedEventArgs(CultureInfo culture, byte charSet) { + this.inputLanguage = System.Windows.Forms.InputLanguage.FromCulture(culture); + this.culture = culture; + this.charSet = charSet; + } + + /// + /// + /// + /// Initializes a new instance of the class with the specified input language and + /// character set. + /// + /// + public InputLanguageChangedEventArgs(InputLanguage inputLanguage, byte charSet) { + this.inputLanguage = inputLanguage; + this.culture = inputLanguage.Culture; + this.charSet = charSet; + } + + /// + /// + /// + /// Gets the input language. + /// + /// + public InputLanguage InputLanguage { + get { + return inputLanguage; + } + } + + /// + /// + /// + /// Gets the locale of the input language. + /// + /// + public CultureInfo Culture { + get { + return culture; + } + } + + /// + /// + /// + /// Gets the character set associated with the new input language. + /// + /// + public byte CharSet { + get { + return charSet; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/InputLangChangeEventHandler.cs b/WindowsForms/Managed/System/WinForms/InputLangChangeEventHandler.cs new file mode 100644 index 000000000..8c0761dc6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLangChangeEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the event of a + /// . + /// + /// + public delegate void InputLanguageChangedEventHandler(object sender, InputLanguageChangedEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEvent.cs b/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEvent.cs new file mode 100644 index 000000000..54c7ee5a5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEvent.cs @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Globalization; + using System.Windows.Forms; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class InputLanguageChangingEventArgs : CancelEventArgs { + + /// + /// + /// The requested input language. + /// + private readonly InputLanguage inputLanguage; + + /// + /// + /// The locale of the requested input langugage. + /// + private readonly CultureInfo culture; + /// + /// + /// Set to true if the system default font supports the character + /// set required for the requested input language. + /// + private readonly bool sysCharSet; + + /** + * @deprecated Should use the new constructor instead. + */ + /// + /// + /// + /// + /// Initializes a new instance of the class with the + /// specified locale, character set, and acceptance. + /// + /// + public InputLanguageChangingEventArgs(CultureInfo culture, bool sysCharSet) { + + this.inputLanguage = System.Windows.Forms.InputLanguage.FromCulture(culture); + this.culture = culture; + this.sysCharSet = sysCharSet; + } + + /// + /// + /// + /// Initializes a new instance of the class with the + /// specified input language, character set, and acceptance of + /// a language change. + /// + /// + public InputLanguageChangingEventArgs(InputLanguage inputLanguage, bool sysCharSet) { + + if (inputLanguage == null) + throw new ArgumentNullException("inputLanguage"); + + this.inputLanguage = inputLanguage; + this.culture = inputLanguage.Culture; + this.sysCharSet = sysCharSet; + } + + /// + /// + /// + /// Gets the requested input language. + /// + /// + public InputLanguage InputLanguage { + get { + return inputLanguage; + } + } + + /// + /// + /// + /// Gets the locale of the requested input language. + /// + /// + public CultureInfo Culture { + get { + return culture; + } + } + + /// + /// + /// + /// Gets a value indicating whether the system default font supports the character + /// set required for the requested input language. + /// + /// + public bool SysCharSet { + get { + return sysCharSet; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEventHandler.cs b/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEventHandler.cs new file mode 100644 index 000000000..67747d900 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLangChangeRequestEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the event of a . + /// + /// + public delegate void InputLanguageChangingEventHandler(object sender, InputLanguageChangingEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/InputLanguage.cs b/WindowsForms/Managed/System/WinForms/InputLanguage.cs new file mode 100644 index 000000000..15a4cd076 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLanguage.cs @@ -0,0 +1,394 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security.Permissions; + + using System.Diagnostics; + + using System; + using Microsoft.Win32; + using System.Globalization; + using System.ComponentModel; + using System.Drawing; + + /// + /// + /// + /// Provides methods and fields to manage the input language. + /// + /// + public sealed class InputLanguage { + + /// + /// + /// The HKL handle. + /// + /// + private readonly IntPtr handle; + + /// + /// + /// + /// + internal InputLanguage(IntPtr handle) { + this.handle = handle; + } + + /// + /// + /// + /// Returns + /// the culture of the current input language. + /// + /// + public CultureInfo Culture { + get { + return new CultureInfo((int)handle & 0xFFFF); + } + } + + /// + /// + /// + /// Gets or sets the input language for the current thread. + /// + /// + public static InputLanguage CurrentInputLanguage { + get { + Application.OleRequired(); + // note we can obtain the KeyboardLayout for a given thread... + return new InputLanguage(SafeNativeMethods.GetKeyboardLayout(0)); + } + set { + IntSecurity.AffectThreadBehavior.Demand(); + + // (NDPWhidbey 8362) OleInitialize needs to be called before we can call ActivateKeyboardLayout. + Application.OleRequired(); + if (value == null) { + value = InputLanguage.DefaultInputLanguage; + } + IntPtr handleOld = SafeNativeMethods.ActivateKeyboardLayout(new HandleRef(value, value.handle), 0); + if (handleOld == IntPtr.Zero) { + throw new ArgumentException(SR.GetString(SR.ErrorBadInputLanguage), "value"); + } + } + } + + /// + /// + /// + /// Returns the default input language for the system. + /// + /// + public static InputLanguage DefaultInputLanguage { + get { + IntPtr[] data = new IntPtr[1]; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETDEFAULTINPUTLANG, 0, data, 0); + return new InputLanguage(data[0]); + } + } + + /// + /// + /// + /// Returns the handle for the input language. + /// + /// + public IntPtr Handle { + get { + return handle; + } + } + + /// + /// + /// + /// Returns a list of all installed input languages. + /// + /// + public static InputLanguageCollection InstalledInputLanguages { + get { + int size = SafeNativeMethods.GetKeyboardLayoutList(0, null); + + IntPtr[] handles = new IntPtr[size]; + SafeNativeMethods.GetKeyboardLayoutList(size, handles); + + InputLanguage[] ils = new InputLanguage[size]; + for (int i = 0; i < size; i++) + ils[i] = new InputLanguage(handles[i]); + + return new InputLanguageCollection(ils); + } + } + + /// + /// + /// + /// Returns + /// the name of the current keyboard layout as it appears in the Windows Regional Settings on the computer. + /// + /// + public string LayoutName { + get { + // There is no good way to do this in Win32... GetKeyboardLayoutName does what we want, + // but only for the current input language; setting and resetting the current input language + // would generate spurious InputLanguageChanged events. + + /* + HKL is a 32 bit value. HIWORD is a Device Handle. LOWORD is Language ID. + + HKL + +------------------------+-------------------------+ + | Device Handle | Language ID | + +------------------------+-------------------------+ + 31 16 15 0 bit + + + Language ID + +---------------------------+-----------------------+ + | Sublanguage ID | Primary Language ID | + +---------------------------+-----------------------+ + 15 10 9 0 bit + + WORD LangId = MAKELANGID(primary, sublang) + BYTE primary = PRIMARYLANGID(LangId) + BYTE sublang = PRIMARYLANGID(LangID) + + How Preload is interpreted: example US-Dvorak + Look in HKEY_CURRENT_USER\Keyboard Layout\Preload + Name="4" (may vary) + Value="d0000409" -> Language ID = 0409 + Look in HKEY_CURRENT_USER\Keyboard Layout\Substitutes + Name="d0000409" + Value="00010409" + Look in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010409 + "Layout File": name of keyboard layout DLL (KBDDV.DLL) + "Layout Id": ID of this layout (0002) + Win32k will change the top nibble of layout ID to F, which makes F002. + Combined with Language ID, the final HKL is F0020409. + */ + + string layoutName = null; + + IntPtr currentHandle = handle; + int language = unchecked( (int) (long)currentHandle) & 0xffff; + int device = (unchecked( (int) (long)currentHandle) >> 16) & 0x0fff; + + // SECREVIEW : We have to get the input information from the registry. These two + // : keys only contain keyboard information. This is safe to do. + // + new RegistryPermission(PermissionState.Unrestricted).Assert(); + + try { + if (device == language || device == 0) { + // Default keyboard for language + string keyName = Convert.ToString(language, 16); + keyName = PadWithZeroes(keyName, 8); + RegistryKey key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + keyName); + + // Attempt to extract the localized keyboard layout name + // using the SHLoadIndirectString API... + layoutName = GetLocalizedKeyboardLayoutName(key.GetValue("Layout Display Name") as string); + + // Default back to our legacy codepath and obtain the name + // directly through the registry value + if (layoutName == null) { + layoutName = (string) key.GetValue("Layout Text"); + } + + key.Close(); + } + else { + // Look for a substitution + // + RegistryKey substitutions = Registry.CurrentUser.OpenSubKey("Keyboard Layout\\Substitutes"); + string[] encodings = null; + if (substitutions != null) { + encodings = substitutions.GetValueNames(); + + foreach (string encoding in encodings) { + int encodingValue = Convert.ToInt32(encoding, 16); + if (encodingValue == unchecked( (int) (long)currentHandle) || + (encodingValue & 0x0FFFFFFF) == (unchecked( (int) (long)currentHandle) & 0x0FFFFFFF) || + (encodingValue & 0xFFFF) == language) { + + currentHandle = (IntPtr)Convert.ToInt32((string)substitutions.GetValue(encoding), 16); + language = unchecked( (int) (long)currentHandle) & 0xFFFF; + device = (unchecked( (int) (long)currentHandle) >> 16) & 0xFFF; + break; + } + } + + substitutions.Close(); + } + + RegistryKey layouts = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts"); + if (layouts != null) { + encodings = layouts.GetSubKeyNames(); + + // Check to see if the encoding directly matches the handle -- some do. + // + foreach (string encoding in encodings) { + Debug.Assert(encoding.Length == 8, "unexpected key in registry: hklm\\SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + encoding); + if (currentHandle == (IntPtr)Convert.ToInt32(encoding, 16)) { + RegistryKey key = layouts.OpenSubKey(encoding); + + // Attempt to extract the localized keyboard layout name + // using the SHLoadIndirectString API... + layoutName = GetLocalizedKeyboardLayoutName(key.GetValue("Layout Display Name") as string); + + // Default back to our legacy codepath and obtain the name + // directly through the registry value + if (layoutName == null) { + layoutName = (string) key.GetValue("Layout Text"); + } + + key.Close(); + break; + } + } + } + + if (layoutName == null) { + + // No luck there. Match the language first, then try to find a layout ID + // + foreach (string encoding in encodings) { + Debug.Assert(encoding.Length == 8, "unexpected key in registry: hklm\\SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + encoding); + if (language == (0xffff & Convert.ToInt32(encoding.Substring(4,4), 16))) { + RegistryKey key = layouts.OpenSubKey(encoding); + string codeValue = (string) key.GetValue("Layout Id"); + if (codeValue != null) { + int value = Convert.ToInt32(codeValue, 16); + if (value == device) { + + // Attempt to extract the localized keyboard layout name + // using the SHLoadIndirectString API... + layoutName = GetLocalizedKeyboardLayoutName(key.GetValue("Layout Display Name") as string); + + // Default back to our legacy codepath and obtain the name + // directly through the registry value + if (layoutName == null) { + layoutName = (string) key.GetValue("Layout Text"); + } + } + } + key.Close(); + if (layoutName != null) { + break; + } + } + } + } + + layouts.Close(); + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + if (layoutName == null) { + layoutName = SR.GetString(SR.UnknownInputLanguageLayout); + } + return layoutName; + } + } + + /// + /// Attempts to extract the localized keyboard layout name using the + /// SHLoadIndirectString API (only on OSVersions >= 5). Returning + /// null from this method will force us to use the legacy codepath + /// (pulling the text directly from the registry). + /// + private static string GetLocalizedKeyboardLayoutName(string layoutDisplayName) { + + if (layoutDisplayName != null && Environment.OSVersion.Version.Major >= 5) { + + StringBuilder sb = new StringBuilder(512); + + uint res = UnsafeNativeMethods.SHLoadIndirectString(layoutDisplayName, sb, (uint)sb.Capacity, IntPtr.Zero); + + if (res == NativeMethods.S_OK) { + return sb.ToString(); + } + } + + return null; + } + + /// + /// + /// Creates an InputLanguageChangedEventArgs given a windows message. + /// + /// + internal static InputLanguageChangedEventArgs CreateInputLanguageChangedEventArgs(Message m) { + return new InputLanguageChangedEventArgs(new InputLanguage(m.LParam), unchecked((byte)(long)m.WParam)); + } + + /// + /// + /// Creates an InputLanguageChangingEventArgs given a windows message. + /// + /// + internal static InputLanguageChangingEventArgs CreateInputLanguageChangingEventArgs(Message m) { + InputLanguage inputLanguage = new InputLanguage(m.LParam); + + // NOTE: by default we should allow any locale switch + // + bool localeSupportedBySystem = !(m.WParam == IntPtr.Zero); + return new InputLanguageChangingEventArgs(inputLanguage, localeSupportedBySystem); + + } + + /// + /// + /// Specifies whether two input languages are equal. + /// + public override bool Equals(object value) { + if (value is InputLanguage) { + return(this.handle == ((InputLanguage)value).handle); + } + return false; + } + + /// + /// + /// Returns the input language associated with the specified + /// culture. + /// + public static InputLanguage FromCulture(CultureInfo culture) { + + // KeyboardLayoutId is the LCID for built-in cultures, but it + // is the CU-preferred keyboard language for custom cultures. + int lcid = culture.KeyboardLayoutId; + + foreach(InputLanguage lang in InstalledInputLanguages) { + if ((unchecked( (int) (long)lang.handle) & 0xFFFF) == lcid) { + return lang; + } + } + + return null; + } + + /// + /// + /// Hash code for this input language. + /// + public override int GetHashCode() { + return unchecked( (int) (long)handle); + } + + private static string PadWithZeroes(string input, int length) { + return "0000000000000000".Substring(0, length - input.Length) + input; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/InputLanguageCollection.cs b/WindowsForms/Managed/System/WinForms/InputLanguageCollection.cs new file mode 100644 index 000000000..bd14d818b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InputLanguageCollection.cs @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ------------------------------------------------------------------------------ +// +namespace System.Windows.Forms { + using System; + using System.Collections; + + + /// + /// + /// + /// A collection that stores objects. + /// + /// + public class InputLanguageCollection : ReadOnlyCollectionBase { + + /// + /// + /// + /// Initializes a new instance of containing any array of objects. + /// + /// + internal InputLanguageCollection(InputLanguage[] value) { + InnerList.AddRange(value); + } + + /// + /// + /// Represents the entry at the specified index of the . + /// + public InputLanguage this[int index] { + get { + return ((InputLanguage)(InnerList[index])); + } + } + + /// + /// + /// Gets a value indicating whether the + /// contains the specified . + /// + public bool Contains(InputLanguage value) { + return InnerList.Contains(value); + } + + /// + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + public void CopyTo(InputLanguage[] array, int index) { + InnerList.CopyTo(array, index); + } + + /// + /// + /// Returns the index of a in + /// the . + /// + public int IndexOf(InputLanguage value) { + return InnerList.IndexOf(value); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/InsertKeyMode.cs b/WindowsForms/Managed/System/WinForms/InsertKeyMode.cs new file mode 100644 index 000000000..3d054653a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InsertKeyMode.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// Represents the mode characters are entered in a text box. + /// + public enum InsertKeyMode + { + /// + /// Honors the Insert key mode. + /// + Default, + + /// + /// Forces insertion mode to be 'on' regardless of the Insert key mode. + /// + Insert, + + /// + /// Forces insertion mode to be 'off' regardless of the Insert key mode. + /// + Overwrite + } +} diff --git a/WindowsForms/Managed/System/WinForms/InvalidateEvent.cs b/WindowsForms/Managed/System/WinForms/InvalidateEvent.cs new file mode 100644 index 000000000..8956120b3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InvalidateEvent.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class InvalidateEventArgs : EventArgs { + + /// + /// + /// Rectangle that bounds the window area which has been invalidated. + /// + private readonly Rectangle invalidRect; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public InvalidateEventArgs(Rectangle invalidRect) { + this.invalidRect = invalidRect; + } + + /// + /// + /// + /// Gets a value + /// indicating the + /// that contains the invalidated window area. + /// + /// + public Rectangle InvalidRect { + get { + return invalidRect; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/InvalidateEventHandler.cs b/WindowsForms/Managed/System/WinForms/InvalidateEventHandler.cs new file mode 100644 index 000000000..f518dacc6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/InvalidateEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents the method that will handle the event + /// of a . + /// + /// + public delegate void InvalidateEventHandler(object sender, InvalidateEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ItemActivation.cs b/WindowsForms/Managed/System/WinForms/ItemActivation.cs new file mode 100644 index 000000000..765e8b683 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemActivation.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies how the user activates items and the appearance + /// of items as the mouse cursor moves over them. + /// + /// + /// + public enum ItemActivation { + + /// + /// + /// + /// Activate items with a double-click. + /// Items do not change appearance. + /// + /// + /// + Standard = 0, + + /// + /// + /// + /// Activate + /// items with a single click. The cursor changes shape and the item + /// text changes color. + /// + /// + /// + OneClick = 1, + + /// + /// + /// + /// Activate items with a + /// double click. The item text changes color. + /// + /// + /// + TwoClick = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemBoundsPortion.cs b/WindowsForms/Managed/System/WinForms/ItemBoundsPortion.cs new file mode 100644 index 000000000..c6a1df05c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemBoundsPortion.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies the display bounds of a ListItem. + /// + /// + public enum ItemBoundsPortion { + + /// + /// + /// + /// Both the icon and label + /// portions. In Report View, this includes subitems. + /// + /// + /// + Entire = NativeMethods.LVIR_BOUNDS, + + /// + /// + /// + /// Only the icon portion. + /// + /// + Icon = NativeMethods.LVIR_ICON, + + /// + /// + /// + /// Only the label portion. + /// + /// + Label = NativeMethods.LVIR_LABEL, + + /// + /// + /// + /// Both the icon and label portions. In Report view, this + /// does not include subitems. In all other views, this is the same as + /// + /// . + /// + /// + ItemOnly = NativeMethods.LVIR_SELECTBOUNDS, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemChangedEventArgs.cs b/WindowsForms/Managed/System/WinForms/ItemChangedEventArgs.cs new file mode 100644 index 000000000..dd1292b9a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemChangedEventArgs.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// [To be supplied.] + /// + public class ItemChangedEventArgs : EventArgs { + + private int index; + + internal ItemChangedEventArgs(int index) { + this.index = index; + } + + /// + /// + /// [To be supplied.] + /// + public int Index { + get { + return index; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ItemChangedEventHandler.cs new file mode 100644 index 000000000..4feeb8cf1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemChangedEventHandler.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// [To be supplied.] + /// + public delegate void ItemChangedEventHandler(object sender, ItemChangedEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/ItemCheckEvent.cs b/WindowsForms/Managed/System/WinForms/ItemCheckEvent.cs new file mode 100644 index 000000000..3d62886a1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemCheckEvent.cs @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ItemCheckEventArgs : EventArgs { + + readonly int index; + CheckState newValue; + readonly CheckState currentValue; + + /// + /// + /// [To be supplied.] + /// + public ItemCheckEventArgs(int index, CheckState newCheckValue, CheckState currentValue) { + this.index = index; + this.newValue = newCheckValue; + this.currentValue = currentValue; + } + /// + /// + /// The index of the item that is about to change. + /// + public int Index { + get { return index; } + } + + /// + /// + /// The proposed new value of the CheckBox. + /// + public CheckState NewValue { + get { return newValue; } + set { newValue = value; } + } + + /// + /// + /// The current state of the CheckBox. + /// + public CheckState CurrentValue { + get { return currentValue; } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemCheckEventHandler.cs b/WindowsForms/Managed/System/WinForms/ItemCheckEventHandler.cs new file mode 100644 index 000000000..3d7426be7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemCheckEventHandler.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will + /// handle the event of a + /// or + /// . + /// + /// + /// + public delegate void ItemCheckEventHandler(object sender, ItemCheckEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ItemCheckedEvent.cs b/WindowsForms/Managed/System/WinForms/ItemCheckedEvent.cs new file mode 100644 index 000000000..a54567766 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemCheckedEvent.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + /// + + public class ItemCheckedEventArgs : EventArgs { + private ListViewItem lvi; + + + /// + /// + /// [To be supplied.] + /// + public ItemCheckedEventArgs(ListViewItem item) { + this.lvi = item; + } + /// + /// + /// The index of the item that is about to change. + /// + public ListViewItem Item { + get { return lvi; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemCheckedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ItemCheckedEventHandler.cs new file mode 100644 index 000000000..8793032b2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemCheckedEventHandler.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will + /// handle the event of a + /// or + /// . + /// + /// + /// + public delegate void ItemCheckedEventHandler(object sender, ItemCheckedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ItemDragEvent.cs b/WindowsForms/Managed/System/WinForms/ItemDragEvent.cs new file mode 100644 index 000000000..4265288a5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemDragEvent.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ItemDragEventArgs : EventArgs { + readonly MouseButtons button; + readonly object item; + + /// + /// + /// [To be supplied.] + /// + public ItemDragEventArgs(MouseButtons button) { + this.button = button; + this.item = null; + } + + /// + /// + /// [To be supplied.] + /// + public ItemDragEventArgs(MouseButtons button, object item) { + this.button = button; + this.item = item; + } + + /// + /// + /// [To be supplied.] + /// + public MouseButtons Button { + get { return button; } + } + + /// + /// + /// [To be supplied.] + /// + public object Item { + get { return item; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ItemDragEventHandler.cs b/WindowsForms/Managed/System/WinForms/ItemDragEventHandler.cs new file mode 100644 index 000000000..3a3c7f9b5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ItemDragEventHandler.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// + /// event of a + /// + /// . + /// + /// + /// + public delegate void ItemDragEventHandler(object sender, ItemDragEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/KeyEvent.cs b/WindowsForms/Managed/System/WinForms/KeyEvent.cs new file mode 100644 index 000000000..9d2828d65 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyEvent.cs @@ -0,0 +1,193 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// + /// Provides data for the or + /// event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class KeyEventArgs : EventArgs { + + /// + /// + /// Contains key data for KeyDown and KeyUp events. This is a combination + /// of keycode and modifer flags. + /// + private readonly Keys keyData; + + /// + /// + /// Determines if this event has been handled by a handler. If handled, the + /// key event will not be sent along to Windows. If not handled, the event + /// will be sent to Windows for default processing. + /// + private bool handled = false; + + /// + /// + /// + private bool suppressKeyPress = false; + + /// + /// + /// + /// Initializes a new + /// instance of the class. + /// + /// + public KeyEventArgs(Keys keyData) { + this.keyData = keyData; + } + + /// + /// + /// + /// Gets a value indicating whether the ALT key was pressed. + /// + /// + public virtual bool Alt { + get { + return (keyData & Keys.Alt) == Keys.Alt; + } + } + + /// + /// + /// + /// Gets a value indicating whether the CTRL key was pressed. + /// + /// + public bool Control { + get { + return (keyData & Keys.Control) == Keys.Control; + } + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the event was handled. + /// + /// + // + public bool Handled { + get { + return handled; + } + set { + handled = value; + } + } + + /// + /// + /// + /// Gets the keyboard code for a or + /// event. + /// + /// + //subhag : changed the behaviour of the KeyCode as per the new requirements. + public Keys KeyCode { + [ + // Keys is discontiguous so we have to use Enum.IsDefined. + SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible") + ] + get { + Keys keyGenerated = keyData & Keys.KeyCode; + + // since Keys can be discontiguous, keeping Enum.IsDefined. + if (!Enum.IsDefined(typeof(Keys),(int)keyGenerated)) + return Keys.None; + else + return keyGenerated; + } + } + + /// + /// + /// + /// Gets the keyboard value for a or + /// event. + /// + /// + //subhag : added the KeyValue as per the new requirements. + public int KeyValue { + get { + return (int)(keyData & Keys.KeyCode); + } + } + + /// + /// + /// + /// Gets the key data for a or + /// event. + /// + /// + public Keys KeyData { + get { + return keyData; + } + } + + /// + /// + /// + /// Gets the modifier flags for a or event. + /// This indicates which modifier keys (CTRL, SHIFT, and/or ALT) were pressed. + /// + /// + public Keys Modifiers { + get { + return keyData & Keys.Modifiers; + } + } + + /// + /// + /// + /// Gets + /// a value indicating whether the SHIFT key was pressed. + /// + /// + public virtual bool Shift { + get { + return (keyData & Keys.Shift) == Keys.Shift; + } + } + + /// + /// + /// + // + public bool SuppressKeyPress { + get { + return suppressKeyPress; + } + set { + suppressKeyPress = value; + handled = value; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/KeyEventHandler.cs b/WindowsForms/Managed/System/WinForms/KeyEventHandler.cs new file mode 100644 index 000000000..0a372aad8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents a method that will handle the or event of a . + /// + /// + public delegate void KeyEventHandler(object sender, KeyEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/KeyPressEvent.cs b/WindowsForms/Managed/System/WinForms/KeyPressEvent.cs new file mode 100644 index 000000000..941833a60 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyPressEvent.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class KeyPressEventArgs : EventArgs { + + /// + /// + /// Contains the character of the current KeyPress event. + /// + private char keyChar; + + /// + /// + /// Determines if this event has been handled by a handler. If handled, the + /// key event will not be sent along to Windows. If not handled, the event + /// will be sent to Windows for default processing. + /// + private bool handled; + + /// + /// + /// + /// Initializes a new + /// instance of the + /// class. + /// + /// + public KeyPressEventArgs(char keyChar) { + this.keyChar = keyChar; + } + + /// + /// + /// + /// Gets the character corresponding to the key + /// pressed. + /// + /// + public char KeyChar { + get { + return keyChar; + } + set { + keyChar = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// event was handled. + /// + /// + public bool Handled { + get { + return handled; + } + set { + handled = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/KeyPressEventHandler.cs b/WindowsForms/Managed/System/WinForms/KeyPressEventHandler.cs new file mode 100644 index 000000000..f57b0e677 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyPressEventHandler.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents a method that will handle the event of a + /// . + /// + /// + public delegate void KeyPressEventHandler(object sender, KeyPressEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs b/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs new file mode 100644 index 000000000..31252bdcd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs @@ -0,0 +1,362 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + * KeyboardToolTipStateMachine implements keyboard ToolTips for controls with ToolTip set on them. + * + * A keyboard ToolTip is shown when a user focuses a control using keyboard keys such as Tab, arrow keys etc. + * This state machine attempts to simulate the mouse ToolTip behavior. + * The control should be focused with keyboard for an amount of time specified with TTDT_INITIAL flag to make the keyboard ToolTip appear. + * Once visible, the keyboard ToolTip will be demonstrated for an amount of time specified with TTDT_AUTOPOP flag. The ToolTip will disappear afterwards. + * If the keyboard ToolTip is visible and the focus moves from one ToolTip-enabled control to another, the keyboard ToolTip will be shown after a time interval specified with TTDT_RESHOW flag has passed. + * + * This behavior is disabled by default and can be enabled by adding the "Switch.System.Windows.Forms.UseLegacyToolTipDisplay=false" line to an application's App.config file: + + + + + + + + + + + * Please note that disabling Switch.UseLegacyAccessibilityFeatures, Switch.UseLegacyAccessibilityFeatures.2 and Switch.UseLegacyAccessibilityFeatures.3 is required to disable Switch.System.Windows.Forms.UseLegacyToolTipDisplay + */ +namespace System.Windows.Forms { + using Runtime.CompilerServices; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + internal sealed class KeyboardToolTipStateMachine { + + public static KeyboardToolTipStateMachine Instance { + get { + if (KeyboardToolTipStateMachine.instance == null) { + KeyboardToolTipStateMachine.instance = new KeyboardToolTipStateMachine(); + } + return KeyboardToolTipStateMachine.instance; + } + } + + [ThreadStatic] + private static KeyboardToolTipStateMachine instance; + + private readonly Dictionary> transitions; + private readonly ToolToTipDictionary toolToTip = new ToolToTipDictionary(); + + private SmState currentState = SmState.Hidden; + private IKeyboardToolTip currentTool; + private readonly InternalStateMachineTimer timer = new InternalStateMachineTimer(); + private SendOrPostCallback refocusDelayExpirationCallback; + + private readonly WeakReference lastFocusedTool = new WeakReference(null); + + private KeyboardToolTipStateMachine() { + this.transitions = new Dictionary> { + [new SmTransition(SmState.Hidden, SmEvent.FocusedTool)] = this.SetupInitShowTimer, + [new SmTransition(SmState.Hidden, SmEvent.LeftTool)] = this.DoNothing, // OK + + [new SmTransition(SmState.ReadyForInitShow, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.ReadyForInitShow, SmEvent.LeftTool)] = this.ResetFsmToHidden, + [new SmTransition(SmState.ReadyForInitShow, SmEvent.InitialDelayTimerExpired)] = this.ShowToolTip, + + [new SmTransition(SmState.Shown, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.Shown, SmEvent.LeftTool)] = this.HideAndStartWaitingForRefocus, + [new SmTransition(SmState.Shown, SmEvent.AutoPopupDelayTimerExpired)] = this.ResetFsmToHidden, + + [new SmTransition(SmState.WaitForRefocus, SmEvent.FocusedTool)] = this.SetupReshowTimer, + [new SmTransition(SmState.WaitForRefocus, SmEvent.LeftTool)] = this.DoNothing, // OK + [new SmTransition(SmState.WaitForRefocus, SmEvent.RefocusWaitDelayExpired)] = this.ResetFsmToHidden, + + [new SmTransition(SmState.ReadyForReshow, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.ReadyForReshow, SmEvent.LeftTool)] = this.StartWaitingForRefocus, + [new SmTransition(SmState.ReadyForReshow, SmEvent.ReshowDelayTimerExpired)] = this.ShowToolTip + }; + } + + public void ResetStateMachine(ToolTip toolTip) { + this.Reset(toolTip); + } + + public void Hook(IKeyboardToolTip tool, ToolTip toolTip) { + if (tool.AllowsToolTip()) { + this.StartTracking(tool, toolTip); + tool.OnHooked(toolTip); + } + } + + public void NotifyAboutMouseEnter(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip()) { + this.Reset(null); + } + } + + private bool IsToolTracked(IKeyboardToolTip sender) { + return this.toolToTip[sender] != null; + } + + public void NotifyAboutLostFocus(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip()) { + this.Transit(SmEvent.LeftTool, sender); + if (this.currentTool == null) { + this.lastFocusedTool.SetTarget(null); + } + } + } + + public void NotifyAboutGotFocus(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip() && sender.IsBeingTabbedTo()) { + this.Transit(SmEvent.FocusedTool, sender); + if (this.currentTool == sender) { + this.lastFocusedTool.SetTarget(sender); + } + } + } + + public void Unhook(IKeyboardToolTip tool, ToolTip toolTip) { + if (tool.AllowsToolTip()) { + this.StopTracking(tool, toolTip); + tool.OnUnhooked(toolTip); + } + } + + public void NotifyAboutFormDeactivation(ToolTip sender) { + this.OnFormDeactivation(sender); + } + + + internal IKeyboardToolTip LastFocusedTool { + get { + IKeyboardToolTip tool; + if(this.lastFocusedTool.TryGetTarget(out tool)) { + return tool; + } + + return Control.FromHandleInternal(UnsafeNativeMethods.GetFocus()); + } + } + + private SmState HideAndStartWaitingForRefocus(IKeyboardToolTip tool, ToolTip toolTip) { + toolTip.HideToolTip(this.currentTool); + return StartWaitingForRefocus(tool, toolTip); + } + + private SmState StartWaitingForRefocus(IKeyboardToolTip tool, ToolTip toolTip) { + this.ResetTimer(); + this.currentTool = null; + SendOrPostCallback expirationCallback = null; + this.refocusDelayExpirationCallback = expirationCallback = (object toolObject) => { + if (this.currentState == SmState.WaitForRefocus && this.refocusDelayExpirationCallback == expirationCallback) { + this.Transit(SmEvent.RefocusWaitDelayExpired, (IKeyboardToolTip)toolObject); + } + }; + WindowsFormsSynchronizationContext.Current.Post(expirationCallback, tool); + return SmState.WaitForRefocus; + } + + private SmState SetupReshowTimer(IKeyboardToolTip tool, ToolTip toolTip) { + this.currentTool = tool; + this.ResetTimer(); + this.StartTimer(toolTip.GetDelayTime(NativeMethods.TTDT_RESHOW), this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.ReshowDelayTimerExpired, tool))); + return SmState.ReadyForReshow; + } + + private SmState ShowToolTip(IKeyboardToolTip tool, ToolTip toolTip) { + string toolTipText = tool.GetCaptionForTool(toolTip); + int autoPopDelay = toolTip.GetDelayTime(NativeMethods.TTDT_AUTOPOP); + if (!this.currentTool.IsHoveredWithMouse()) { + toolTip.ShowKeyboardToolTip(toolTipText, this.currentTool, autoPopDelay); + } + this.StartTimer(autoPopDelay, this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.AutoPopupDelayTimerExpired, this.currentTool))); + return SmState.Shown; + } + + private SmState ResetFsmToHidden(IKeyboardToolTip tool, ToolTip toolTip) { + return this.FullFsmReset(); + } + + private SmState DoNothing(IKeyboardToolTip tool, ToolTip toolTip) { + return this.currentState; + } + + private SmState SetupInitShowTimer(IKeyboardToolTip tool, ToolTip toolTip) { + this.currentTool = tool; + this.ResetTimer(); + this.StartTimer(toolTip.GetDelayTime(NativeMethods.TTDT_INITIAL), this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.InitialDelayTimerExpired, this.currentTool))); + + return SmState.ReadyForInitShow; + } + + private void StartTimer(int interval, EventHandler eventHandler) { + this.timer.Interval = interval; + this.timer.Tick += eventHandler; + this.timer.Start(); + } + + private EventHandler GetOneRunTickHandler(Action handler) { + EventHandler wrapper = null; + wrapper = (object sender, EventArgs eventArgs) => { + this.timer.Stop(); + this.timer.Tick -= wrapper; + handler(this.timer); + }; + return wrapper; + } + + private void Transit(SmEvent @event, IKeyboardToolTip source) { + Debug.Assert(transitions.ContainsKey(new SmTransition(this.currentState, @event)), "Unsupported KeyboardToolTipFsmTransition!"); + bool fullFsmResetRequired = false; + try { + ToolTip toolTip = this.toolToTip[source]; + if ((this.currentTool == null || this.currentTool.CanShowToolTipsNow()) && toolTip != null) { + Func transitionFunction = transitions[new SmTransition(this.currentState, @event)]; + this.currentState = transitionFunction(source, toolTip); + } + else { + fullFsmResetRequired = true; + } + } + catch { + fullFsmResetRequired = true; + throw; + } + finally { + if (fullFsmResetRequired) { + this.FullFsmReset(); + } + } + } + + private SmState FullFsmReset() { + if (this.currentState == SmState.Shown && this.currentTool != null) { + ToolTip currentToolTip = this.toolToTip[this.currentTool]; + if (currentToolTip != null) { + currentToolTip.HideToolTip(this.currentTool); + } + } + this.ResetTimer(); + this.currentTool = null; + return this.currentState = SmState.Hidden; + } + + private void ResetTimer() { + this.timer.ClearTimerTickHandlers(); + this.timer.Stop(); + } + + private void Reset(ToolTip toolTipToReset) { + if (toolTipToReset == null || (this.currentTool != null && this.toolToTip[this.currentTool] == toolTipToReset)) { + this.FullFsmReset(); + } + } + + private void StartTracking(IKeyboardToolTip tool, ToolTip toolTip) { + this.toolToTip[tool] = toolTip; + } + + private void StopTracking(IKeyboardToolTip tool, ToolTip toolTip) { + this.toolToTip.Remove(tool, toolTip); + } + + private void OnFormDeactivation(ToolTip sender) { + if (this.currentTool != null && this.toolToTip[this.currentTool] == sender) { + this.FullFsmReset(); + } + } + + private enum SmEvent : byte { + FocusedTool, + LeftTool, + InitialDelayTimerExpired, // internal + ReshowDelayTimerExpired, // internal + AutoPopupDelayTimerExpired, // internal + RefocusWaitDelayExpired // internal + } + + private enum SmState : byte { + Hidden, + ReadyForInitShow, + Shown, + ReadyForReshow, + WaitForRefocus + } + + private struct SmTransition : IEquatable { + private readonly SmState currentState; + private readonly SmEvent @event; + + public SmTransition(SmState currentState, SmEvent @event) { + this.currentState = currentState; + this.@event = @event; + } + + public bool Equals(SmTransition other) { + return this.currentState == other.currentState && this.@event == other.@event; + } + + public override bool Equals(object obj) { + return obj is SmTransition && this.Equals((SmTransition)obj); + } + + public override int GetHashCode() { + return (byte)this.currentState << 16 | (byte)this.@event; + } + } + + private sealed class InternalStateMachineTimer : Timer { + public void ClearTimerTickHandlers() { + this.onTimer = null; + } + } + + private sealed class ToolToTipDictionary { + private ConditionalWeakTable> table = new ConditionalWeakTable>(); + + public ToolTip this[IKeyboardToolTip tool] { + get { + WeakReference toolTipReference; + ToolTip toolTip = null; + if (this.table.TryGetValue(tool, out toolTipReference)) { + if (!toolTipReference.TryGetTarget(out toolTip)) { + // removing dead reference + this.table.Remove(tool); + } + } + return toolTip; + } + + set { + WeakReference toolTipReference; + if (this.table.TryGetValue(tool, out toolTipReference)) { + toolTipReference.SetTarget(value); + } + else { + this.table.Add(tool, new WeakReference(value)); + } + } + } + + public void Remove(IKeyboardToolTip tool, ToolTip toolTip) { + WeakReference toolTipReference; + ToolTip existingToolTip; + if (this.table.TryGetValue(tool, out toolTipReference)) { + if (toolTipReference.TryGetTarget(out existingToolTip)) { + if (existingToolTip == toolTip) { + this.table.Remove(tool); + } + } + else { + // removing dead reference + this.table.Remove(tool); + } + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs.back b/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs.back new file mode 100644 index 000000000..1fb803522 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeyboardToolTipStateMachine.cs.back @@ -0,0 +1,362 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + * KeyboardToolTipStateMachine implements keyboard ToolTips for controls with ToolTip set on them. + * + * A keyboard ToolTip is shown when a user focuses a control using keyboard keys such as Tab, arrow keys etc. + * This state machine attempts to simulate the mouse ToolTip behavior. + * The control should be focused with keyboard for an amount of time specified with TTDT_INITIAL flag to make the keyboard ToolTip appear. + * Once visible, the keyboard ToolTip will be demonstrated for an amount of time specified with TTDT_AUTOPOP flag. The ToolTip will disappear afterwards. + * If the keyboard ToolTip is visible and the focus moves from one ToolTip-enabled control to another, the keyboard ToolTip will be shown after a time interval specified with TTDT_RESHOW flag has passed. + * + * This behavior is disabled by default and can be enabled by adding the "Switch.System.Windows.Forms.UseLegacyToolTipDisplay=false" line to an application's App.config file: + + + + + + + + + + + * Please note that disabling Switch.UseLegacyAccessibilityFeatures, Switch.UseLegacyAccessibilityFeatures.2 and Switch.UseLegacyAccessibilityFeatures.3 is required to disable Switch.System.Windows.Forms.UseLegacyToolTipDisplay + */ +namespace System.Windows.Forms { + using Runtime.CompilerServices; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + internal sealed class KeyboardToolTipStateMachine { + + public static KeyboardToolTipStateMachine Instance { + get { + if (KeyboardToolTipStateMachine.instance == null) { + KeyboardToolTipStateMachine.instance = new KeyboardToolTipStateMachine(); + } + return KeyboardToolTipStateMachine.instance; + } + } + + [ThreadStatic] + private static KeyboardToolTipStateMachine instance; + + private readonly Dictionary> transitions; + private readonly ToolToTipDictionary toolToTip = new ToolToTipDictionary(); + + private SmState currentState = SmState.Hidden; + private IKeyboardToolTip currentTool; + private readonly InternalStateMachineTimer timer = new InternalStateMachineTimer(); + private SendOrPostCallback refocusDelayExpirationCallback; + + private readonly WeakReference lastFocusedTool = new WeakReference(null); + + private KeyboardToolTipStateMachine() { + this.transitions = new Dictionary> { + [new SmTransition(SmState.Hidden, SmEvent.FocusedTool)] = this.SetupInitShowTimer, + [new SmTransition(SmState.Hidden, SmEvent.LeftTool)] = this.DoNothing, // OK + + [new SmTransition(SmState.ReadyForInitShow, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.ReadyForInitShow, SmEvent.LeftTool)] = this.ResetFsmToHidden, + [new SmTransition(SmState.ReadyForInitShow, SmEvent.InitialDelayTimerExpired)] = this.ShowToolTip, + + [new SmTransition(SmState.Shown, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.Shown, SmEvent.LeftTool)] = this.HideAndStartWaitingForRefocus, + [new SmTransition(SmState.Shown, SmEvent.AutoPopupDelayTimerExpired)] = this.ResetFsmToHidden, + + [new SmTransition(SmState.WaitForRefocus, SmEvent.FocusedTool)] = this.SetupReshowTimer, + [new SmTransition(SmState.WaitForRefocus, SmEvent.LeftTool)] = this.DoNothing, // OK + [new SmTransition(SmState.WaitForRefocus, SmEvent.RefocusWaitDelayExpired)] = this.ResetFsmToHidden, + + [new SmTransition(SmState.ReadyForReshow, SmEvent.FocusedTool)] = this.DoNothing, // unlikely: focus without leave + [new SmTransition(SmState.ReadyForReshow, SmEvent.LeftTool)] = this.StartWaitingForRefocus, + [new SmTransition(SmState.ReadyForReshow, SmEvent.ReshowDelayTimerExpired)] = this.ShowToolTip + }; + } + + public void ResetStateMachine(ToolTip toolTip) { + this.Reset(toolTip); + } + + public void Hook(IKeyboardToolTip tool, ToolTip toolTip) { + if (tool.AllowsToolTip()) { + this.StartTracking(tool, toolTip); + tool.OnHooked(toolTip); + } + } + + public void NotifyAboutMouseEnter(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip()) { + this.Reset(null); + } + } + + private bool IsToolTracked(IKeyboardToolTip sender) { + return this.toolToTip[sender] != null; + } + + public void NotifyAboutLostFocus(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip()) { + this.Transit(SmEvent.LeftTool, sender); + if (this.currentTool == null) { + this.lastFocusedTool.SetTarget(null); + } + } + } + + public void NotifyAboutGotFocus(IKeyboardToolTip sender) { + if (this.IsToolTracked(sender) && sender.ShowsOwnToolTip() && sender.IsBeingTabbedTo()) { + this.Transit(SmEvent.FocusedTool, sender); + if (this.currentTool == sender) { + this.lastFocusedTool.SetTarget(sender); + } + } + } + + public void Unhook(IKeyboardToolTip tool, ToolTip toolTip) { + if (tool.AllowsToolTip()) { + this.StopTracking(tool, toolTip); + tool.OnUnhooked(toolTip); + } + } + + public void NotifyAboutFormDeactivation(ToolTip sender) { + this.OnFormDeactivation(sender); + } + + + internal IKeyboardToolTip LastFocusedTool { + get { + IKeyboardToolTip tool; + if(this.lastFocusedTool.TryGetTarget(out tool)) { + return tool; + } + + return Control.FromHandleInternal(UnsafeNativeMethods.GetFocus()); + } + } + + private SmState HideAndStartWaitingForRefocus(IKeyboardToolTip tool, ToolTip toolTip) { + toolTip.HideToolTip(this.currentTool); + return StartWaitingForRefocus(tool, toolTip); + } + + private SmState StartWaitingForRefocus(IKeyboardToolTip tool, ToolTip toolTip) { + this.ResetTimer(); + this.currentTool = null; + SendOrPostCallback expirationCallback = null; + this.refocusDelayExpirationCallback = expirationCallback = (object toolObject) => { + if (this.currentState == SmState.WaitForRefocus && this.refocusDelayExpirationCallback == expirationCallback) { + this.Transit(SmEvent.RefocusWaitDelayExpired, (IKeyboardToolTip)toolObject); + } + }; + WindowsFormsSynchronizationContext.Current.Post(expirationCallback, tool); + return SmState.WaitForRefocus; + } + + private SmState SetupReshowTimer(IKeyboardToolTip tool, ToolTip toolTip) { + this.currentTool = tool; + this.ResetTimer(); + this.StartTimer(toolTip.GetDelayTime(NativeMethods.TTDT_RESHOW), this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.ReshowDelayTimerExpired, tool))); + return SmState.ReadyForReshow; + } + + private SmState ShowToolTip(IKeyboardToolTip tool, ToolTip toolTip) { + string toolTipText = tool.GetCaptionForTool(toolTip); + int autoPopDelay = toolTip.GetDelayTime(NativeMethods.TTDT_AUTOPOP); + if (!this.currentTool.IsHoveredWithMouse()) { + toolTip.ShowKeyboardToolTip(toolTipText, this.currentTool, autoPopDelay); + } + this.StartTimer(autoPopDelay, this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.AutoPopupDelayTimerExpired, this.currentTool))); + return SmState.Shown; + } + + private SmState ResetFsmToHidden(IKeyboardToolTip tool, ToolTip toolTip) { + return this.FullFsmReset(); + } + + private SmState DoNothing(IKeyboardToolTip tool, ToolTip toolTip) { + return this.currentState; + } + + private SmState SetupInitShowTimer(IKeyboardToolTip tool, ToolTip toolTip) { + this.currentTool = tool; + this.ResetTimer(); + this.StartTimer(toolTip.GetDelayTime(NativeMethods.TTDT_INITIAL), this.GetOneRunTickHandler((Timer sender) => this.Transit(SmEvent.InitialDelayTimerExpired, this.currentTool))); + + return SmState.ReadyForInitShow; + } + + private void StartTimer(int interval, EventHandler eventHandler) { + this.timer.Interval = interval; + this.timer.Tick += eventHandler; + this.timer.Start(); + } + + private EventHandler GetOneRunTickHandler(Action handler) { + EventHandler wrapper = null; + wrapper = (object sender, EventArgs eventArgs) => { + this.timer.Stop(); + this.timer.Tick -= wrapper; + handler(this.timer); + }; + return wrapper; + } + + private void Transit(SmEvent @event, IKeyboardToolTip source) { + Debug.Assert(transitions.ContainsKey(new SmTransition(this.currentState, @event)), "Unsupported KeyboardToolTipFsmTransition!"); + bool fullFsmResetRequired = false; + try { + ToolTip toolTip = this.toolToTip[source]; + if ((this.currentTool == null || this.currentTool.CanShowToolTipsNow()) && toolTip != null) { + Func transitionFunction = transitions[new SmTransition(this.currentState, @event)]; + this.currentState = transitionFunction(source, toolTip); + } + else { + fullFsmResetRequired = true; + } + } + catch { + fullFsmResetRequired = true; + throw; + } + finally { + if (fullFsmResetRequired) { + this.FullFsmReset(); + } + } + } + + private SmState FullFsmReset() { + if (this.currentState == SmState.Shown && this.currentTool != null) { + ToolTip currentToolTip = this.toolToTip[this.currentTool]; + if (currentToolTip != null) { + currentToolTip.HideToolTip(this.currentTool); + } + } + this.ResetTimer(); + this.currentTool = null; + return this.currentState = SmState.Hidden; + } + + private void ResetTimer() { + this.timer.ClearTimerTickHandlers(); + this.timer.Stop(); + } + + private void Reset(ToolTip toolTipToReset) { + if (toolTipToReset == null || (this.currentTool != null && this.toolToTip[this.currentTool] == toolTipToReset)) { + this.FullFsmReset(); + } + } + + private void StartTracking(IKeyboardToolTip tool, ToolTip toolTip) { + this.toolToTip[tool] = toolTip; + } + + private void StopTracking(IKeyboardToolTip tool, ToolTip toolTip) { + this.toolToTip.Remove(tool, toolTip); + } + + private void OnFormDeactivation(ToolTip sender) { + if (this.currentTool != null && this.toolToTip[this.currentTool] == sender) { + this.FullFsmReset(); + } + } + + private enum SmEvent : byte { + FocusedTool, + LeftTool, + InitialDelayTimerExpired, // internal + ReshowDelayTimerExpired, // internal + AutoPopupDelayTimerExpired, // internal + RefocusWaitDelayExpired // internal + } + + private enum SmState : byte { + Hidden, + ReadyForInitShow, + Shown, + ReadyForReshow, + WaitForRefocus + } + + private struct SmTransition : IEquatable { + private readonly SmState currentState; + private readonly SmEvent @event; + + public SmTransition(SmState currentState, SmEvent @event) { + this.currentState = currentState; + this.@event = @event; + } + + public bool Equals(SmTransition other) { + return this.currentState == other.currentState && this.@event == other.@event; + } + + public override bool Equals(object obj) { + return obj is SmTransition && this.Equals((SmTransition)obj); + } + + public override int GetHashCode() { + return (byte)this.currentState << 16 | (byte)this.@event; + } + } + + private sealed class InternalStateMachineTimer : Timer { + public void ClearTimerTickHandlers() { + this.onTimer = null; + } + } + + private sealed class ToolToTipDictionary { + private ConditionalWeakTable> table = new ConditionalWeakTable>(); + + public ToolTip this[IKeyboardToolTip tool] { + get { + WeakReference toolTipReference; + ToolTip toolTip = null; + if (this.table.TryGetValue(tool, out toolTipReference)) { + if (!toolTipReference.TryGetTarget(out toolTip)) { + // removing dead reference + this.table.Remove(tool); + } + } + return toolTip; + } + + set { + WeakReference toolTipReference; + if (this.table.TryGetValue(tool, out toolTipReference)) { + toolTipReference.SetTarget(value); + } + else { + this.table.Add(tool, new WeakReference(value)); + } + } + } + + public void Remove(IKeyboardToolTip tool, ToolTip toolTip) { + WeakReference toolTipReference; + ToolTip existingToolTip; + if (this.table.TryGetValue(tool, out toolTipReference)) { + if (toolTipReference.TryGetTarget(out existingToolTip)) { + if (existingToolTip == toolTip) { + this.table.Remove(tool); + } + } + else { + // removing dead reference + this.table.Remove(tool); + } + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Keys.cs b/WindowsForms/Managed/System/WinForms/Keys.cs new file mode 100644 index 000000000..8b05a6291 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Keys.cs @@ -0,0 +1,1429 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms; + using System.Drawing.Design; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies key codes and modifiers. + /// + /// + [ + Flags, + TypeConverterAttribute(typeof(KeysConverter)), + Editor("System.Windows.Forms.Design.ShortcutKeysEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + [System.Runtime.InteropServices.ComVisible(true)] + [ + SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags") // Certain memberd of Keys enum are actually meant to be OR'ed. + ] + public enum Keys { + /// + /// + /// + /// The bit mask to extract a key code from a key value. + /// + /// + /// + KeyCode = 0x0000FFFF, + + /// + /// + /// + /// The bit mask to extract modifiers from a key value. + /// + /// + /// + Modifiers = unchecked((int)0xFFFF0000), + + /// + /// + /// + /// No key pressed. + /// + /// + None = 0x00, + + /// + /// + /// + /// The left mouse button. + /// + /// + /// + LButton = 0x01, + /// + /// + /// + /// The right mouse button. + /// + /// + RButton = 0x02, + /// + /// + /// + /// The CANCEL key. + /// + /// + Cancel = 0x03, + /// + /// + /// + /// The middle mouse button (three-button mouse). + /// + /// + MButton = 0x04, + /// + /// + /// + /// The first x mouse button (five-button mouse). + /// + /// + XButton1 = 0x05, + /// + /// + /// + /// The second x mouse button (five-button mouse). + /// + /// + XButton2 = 0x06, + /// + /// + /// + /// The BACKSPACE key. + /// + /// + Back = 0x08, + /// + /// + /// + /// The TAB key. + /// + /// + Tab = 0x09, + /// + /// + /// + /// The CLEAR key. + /// + /// + LineFeed = 0x0A, + /// + /// + /// + /// The CLEAR key. + /// + /// + Clear = 0x0C, + /// + /// + /// + /// The RETURN key. + /// + /// + /// + Return = 0x0D, + /// + /// + /// + /// The ENTER key. + /// + /// + /// + Enter = Return, + /// + /// + /// + /// The SHIFT key. + /// + /// + ShiftKey = 0x10, + /// + /// + /// + /// The CTRL key. + /// + /// + ControlKey = 0x11, + /// + /// + /// + /// The ALT key. + /// + /// + Menu = 0x12, + /// + /// + /// + /// The PAUSE key. + /// + /// + Pause = 0x13, + /// + /// + /// + /// The CAPS LOCK key. + /// + /// + /// + Capital = 0x14, + /// + /// + /// + /// The CAPS LOCK key. + /// + /// + CapsLock = 0x14, + /// + /// + /// + /// The IME Kana mode key. + /// + /// + KanaMode = 0x15, + /// + /// + /// + /// The IME Hanguel mode key. + /// + /// + HanguelMode = 0x15, + /// + /// + /// + /// The IME Hangul mode key. + /// + /// + HangulMode = 0x15, + /// + /// + /// + /// The IME Junja mode key. + /// + /// + JunjaMode = 0x17, + /// + /// + /// + /// The IME Final mode key. + /// + /// + FinalMode = 0x18, + /// + /// + /// + /// The IME Hanja mode key. + /// + /// + HanjaMode = 0x19, + /// + /// + /// + /// The IME Kanji mode key. + /// + /// + KanjiMode = 0x19, + /// + /// + /// + /// The ESC key. + /// + /// + Escape = 0x1B, + /// + /// + /// + /// The IME Convert key. + /// + /// + IMEConvert = 0x1C, + /// + /// + /// + /// The IME NonConvert key. + /// + /// + IMENonconvert = 0x1D, + /// + /// + /// + /// The IME Accept key. + /// + /// + IMEAccept = 0x1E, + /// + /// + /// + /// The IME Accept key. + /// + /// + IMEAceept = IMEAccept, + /// + /// + /// + /// The IME Mode change request. + /// + /// + IMEModeChange = 0x1F, + /// + /// + /// + /// The SPACEBAR key. + /// + /// + Space = 0x20, + /// + /// + /// + /// The PAGE UP key. + /// + /// + Prior = 0x21, + /// + /// + /// + /// The PAGE UP key. + /// + /// + PageUp = Prior, + /// + /// + /// + /// The PAGE DOWN key. + /// + /// + Next = 0x22, + /// + /// + /// + /// The PAGE DOWN key. + /// + /// + PageDown = Next, + /// + /// + /// + /// The END key. + /// + /// + End = 0x23, + /// + /// + /// + /// The HOME key. + /// + /// + Home = 0x24, + /// + /// + /// + /// The LEFT ARROW key. + /// + /// + Left = 0x25, + /// + /// + /// + /// The UP ARROW key. + /// + /// + Up = 0x26, + /// + /// + /// + /// The RIGHT ARROW key. + /// + /// + Right = 0x27, + /// + /// + /// + /// The DOWN ARROW key. + /// + /// + Down = 0x28, + /// + /// + /// + /// The SELECT key. + /// + /// + Select = 0x29, + /// + /// + /// + /// The PRINT key. + /// + /// + Print = 0x2A, + /// + /// + /// + /// The EXECUTE key. + /// + /// + Execute = 0x2B, + /// + /// + /// + /// The PRINT SCREEN key. + /// + /// + /// + Snapshot = 0x2C, + /// + /// + /// + /// The PRINT SCREEN key. + /// + /// + PrintScreen = Snapshot, + /// + /// + /// + /// The INS key. + /// + /// + Insert = 0x2D, + /// + /// + /// + /// The DEL key. + /// + /// + Delete = 0x2E, + /// + /// + /// + /// The HELP key. + /// + /// + Help = 0x2F, + /// + /// + /// + /// The 0 key. + /// + /// + D0 = 0x30, // 0 + /// + /// + /// + /// The 1 key. + /// + /// + D1 = 0x31, // 1 + /// + /// + /// + /// The 2 key. + /// + /// + D2 = 0x32, // 2 + /// + /// + /// + /// The 3 key. + /// + /// + D3 = 0x33, // 3 + /// + /// + /// + /// The 4 key. + /// + /// + D4 = 0x34, // 4 + /// + /// + /// + /// The 5 key. + /// + /// + D5 = 0x35, // 5 + /// + /// + /// + /// The 6 key. + /// + /// + D6 = 0x36, // 6 + /// + /// + /// + /// The 7 key. + /// + /// + D7 = 0x37, // 7 + /// + /// + /// + /// The 8 key. + /// + /// + D8 = 0x38, // 8 + /// + /// + /// + /// The 9 key. + /// + /// + D9 = 0x39, // 9 + /// + /// + /// + /// The A key. + /// + /// + A = 0x41, + /// + /// + /// + /// The B key. + /// + /// + B = 0x42, + /// + /// + /// + /// The C key. + /// + /// + C = 0x43, + /// + /// + /// + /// The D key. + /// + /// + D = 0x44, + /// + /// + /// + /// The E key. + /// + /// + E = 0x45, + /// + /// + /// + /// The F key. + /// + /// + F = 0x46, + /// + /// + /// + /// The G key. + /// + /// + G = 0x47, + /// + /// + /// + /// The H key. + /// + /// + H = 0x48, + /// + /// + /// + /// The I key. + /// + /// + I = 0x49, + /// + /// + /// + /// The J key. + /// + /// + J = 0x4A, + /// + /// + /// + /// The K key. + /// + /// + K = 0x4B, + /// + /// + /// + /// The L key. + /// + /// + L = 0x4C, + /// + /// + /// + /// The M key. + /// + /// + M = 0x4D, + /// + /// + /// + /// The N key. + /// + /// + N = 0x4E, + /// + /// + /// + /// The O key. + /// + /// + O = 0x4F, + /// + /// + /// + /// The P key. + /// + /// + P = 0x50, + /// + /// + /// + /// The Q key. + /// + /// + Q = 0x51, + /// + /// + /// + /// The R key. + /// + /// + R = 0x52, + /// + /// + /// + /// The S key. + /// + /// + S = 0x53, + /// + /// + /// + /// The T key. + /// + /// + T = 0x54, + /// + /// + /// + /// The U key. + /// + /// + U = 0x55, + /// + /// + /// + /// The V key. + /// + /// + V = 0x56, + /// + /// + /// + /// The W key. + /// + /// + W = 0x57, + /// + /// + /// + /// The X key. + /// + /// + X = 0x58, + /// + /// + /// + /// The Y key. + /// + /// + Y = 0x59, + /// + /// + /// + /// The Z key. + /// + /// + Z = 0x5A, + /// + /// + /// + /// The left Windows logo key (Microsoft Natural Keyboard). + /// + /// + LWin = 0x5B, + /// + /// + /// + /// The right Windows logo key (Microsoft Natural Keyboard). + /// + /// + RWin = 0x5C, + /// + /// + /// + /// The Application key (Microsoft Natural Keyboard). + /// + /// + Apps = 0x5D, + /// + /// + /// + /// The Computer Sleep key. + /// + /// + Sleep = 0x5F, + /// + /// + /// + /// The 0 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad0 = 0x60, + /// + /// + /// + /// The 1 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad1 = 0x61, + /// + /// + /// + /// The 2 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad2 = 0x62, + /// + /// + /// + /// The 3 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad3 = 0x63, + /// + /// + /// + /// The 4 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad4 = 0x64, + /// + /// + /// + /// The 5 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad5 = 0x65, + /// + /// + /// + /// The 6 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad6 = 0x66, + /// + /// + /// + /// The 7 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad7 = 0x67, + /// + /// + /// + /// The 8 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad8 = 0x68, + /// + /// + /// + /// The 9 key on the numeric keypad. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumPad9 = 0x69, + /// + /// + /// + /// The Multiply key. + /// + /// + Multiply = 0x6A, + /// + /// + /// + /// The Add key. + /// + /// + Add = 0x6B, + /// + /// + /// + /// The Separator key. + /// + /// + Separator = 0x6C, + /// + /// + /// + /// The Subtract key. + /// + /// + Subtract = 0x6D, + /// + /// + /// + /// The Decimal key. + /// + /// + Decimal = 0x6E, + /// + /// + /// + /// The Divide key. + /// + /// + Divide = 0x6F, + /// + /// + /// + /// The F1 key. + /// + /// + F1 = 0x70, + /// + /// + /// + /// The F2 key. + /// + /// + F2 = 0x71, + /// + /// + /// + /// The F3 key. + /// + /// + F3 = 0x72, + /// + /// + /// + /// The F4 key. + /// + /// + F4 = 0x73, + /// + /// + /// + /// The F5 key. + /// + /// + F5 = 0x74, + /// + /// + /// + /// The F6 key. + /// + /// + F6 = 0x75, + /// + /// + /// + /// The F7 key. + /// + /// + F7 = 0x76, + /// + /// + /// + /// The F8 key. + /// + /// + F8 = 0x77, + /// + /// + /// + /// The F9 key. + /// + /// + F9 = 0x78, + /// + /// + /// + /// The F10 key. + /// + /// + F10 = 0x79, + /// + /// + /// + /// The F11 key. + /// + /// + F11 = 0x7A, + /// + /// + /// + /// The F12 key. + /// + /// + F12 = 0x7B, + /// + /// + /// + /// The F13 key. + /// + /// + F13 = 0x7C, + /// + /// + /// + /// The F14 key. + /// + /// + F14 = 0x7D, + /// + /// + /// + /// The F15 key. + /// + /// + F15 = 0x7E, + /// + /// + /// + /// The F16 key. + /// + /// + F16 = 0x7F, + /// + /// + /// + /// The F17 key. + /// + /// + F17 = 0x80, + /// + /// + /// + /// The F18 key. + /// + /// + F18 = 0x81, + /// + /// + /// + /// The F19 key. + /// + /// + F19 = 0x82, + /// + /// + /// + /// The F20 key. + /// + /// + F20 = 0x83, + /// + /// + /// + /// The F21 key. + /// + /// + F21 = 0x84, + /// + /// + /// + /// The F22 key. + /// + /// + F22 = 0x85, + /// + /// + /// + /// The F23 key. + /// + /// + F23 = 0x86, + /// + /// + /// + /// The F24 key. + /// + /// + F24 = 0x87, + /// + /// + /// + /// The NUM LOCK key. + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + NumLock = 0x90, + /// + /// + /// + /// The SCROLL LOCK key. + /// + /// + Scroll = 0x91, + /// + /// + /// + /// The left SHIFT key. + /// + /// + LShiftKey = 0xA0, + /// + /// + /// + /// The right SHIFT key. + /// + /// + RShiftKey = 0xA1, + /// + /// + /// + /// The left CTRL key. + /// + /// + LControlKey = 0xA2, + /// + /// + /// + /// The right CTRL key. + /// + /// + RControlKey = 0xA3, + /// + /// + /// + /// The left ALT key. + /// + /// + LMenu = 0xA4, + /// + /// + /// + /// The right ALT key. + /// + /// + RMenu = 0xA5, + /// + /// + /// + /// The Browser Back key. + /// + /// + BrowserBack = 0xA6, + /// + /// + /// + /// The Browser Forward key. + /// + /// + BrowserForward= 0xA7, + /// + /// + /// + /// The Browser Refresh key. + /// + /// + BrowserRefresh= 0xA8, + /// + /// + /// + /// The Browser Stop key. + /// + /// + BrowserStop = 0xA9, + /// + /// + /// + /// The Browser Search key. + /// + /// + BrowserSearch = 0xAA, + /// + /// + /// + /// The Browser Favorites key. + /// + /// + BrowserFavorites = 0xAB, + /// + /// + /// + /// The Browser Home key. + /// + /// + BrowserHome = 0xAC, + /// + /// + /// + /// The Volume Mute key. + /// + /// + VolumeMute = 0xAD, + /// + /// + /// + /// The Volume Down key. + /// + /// + VolumeDown = 0xAE, + /// + /// + /// + /// The Volume Up key. + /// + /// + VolumeUp = 0xAF, + /// + /// + /// + /// The Media Next Track key. + /// + /// + MediaNextTrack = 0xB0, + /// + /// + /// + /// The Media Previous Track key. + /// + /// + MediaPreviousTrack = 0xB1, + /// + /// + /// + /// The Media Stop key. + /// + /// + MediaStop = 0xB2, + /// + /// + /// + /// The Media Play Pause key. + /// + /// + MediaPlayPause = 0xB3, + /// + /// + /// + /// The Launch Mail key. + /// + /// + LaunchMail = 0xB4, + /// + /// + /// + /// The Select Media key. + /// + /// + SelectMedia = 0xB5, + /// + /// + /// + /// The Launch Application1 key. + /// + /// + LaunchApplication1 = 0xB6, + /// + /// + /// + /// The Launch Application2 key. + /// + /// + LaunchApplication2 = 0xB7, + /// + /// + /// + /// The Oem Semicolon key. + /// + /// + OemSemicolon = 0xBA, + /// + /// + /// + /// The Oem 1 key. + /// + /// + Oem1 = OemSemicolon, + /// + /// + /// + /// The Oem plus key. + /// + /// + Oemplus = 0xBB, + /// + /// + /// + /// The Oem comma key. + /// + /// + Oemcomma = 0xBC, + /// + /// + /// + /// The Oem Minus key. + /// + /// + OemMinus = 0xBD, + /// + /// + /// + /// The Oem Period key. + /// + /// + OemPeriod = 0xBE, + /// + /// + /// + /// The Oem Question key. + /// + /// + OemQuestion = 0xBF, + /// + /// + /// + /// The Oem 2 key. + /// + /// + Oem2 = OemQuestion, + /// + /// + /// + /// The Oem tilde key. + /// + /// + Oemtilde = 0xC0, + /// + /// + /// + /// The Oem 3 key. + /// + /// + Oem3 = Oemtilde, + /// + /// + /// + /// The Oem Open Brackets key. + /// + /// + OemOpenBrackets = 0xDB, + /// + /// + /// + /// The Oem 4 key. + /// + /// + Oem4 = OemOpenBrackets, + /// + /// + /// + /// The Oem Pipe key. + /// + /// + OemPipe = 0xDC, + /// + /// + /// + /// The Oem 5 key. + /// + /// + Oem5 = OemPipe, + /// + /// + /// + /// The Oem Close Brackets key. + /// + /// + OemCloseBrackets = 0xDD, + /// + /// + /// + /// The Oem 6 key. + /// + /// + Oem6 = OemCloseBrackets, + /// + /// + /// + /// The Oem Quotes key. + /// + /// + OemQuotes = 0xDE, + /// + /// + /// + /// The Oem 7 key. + /// + /// + Oem7 = OemQuotes, + /// + /// + /// + /// The Oem8 key. + /// + /// + Oem8 = 0xDF, + /// + /// + /// + /// The Oem Backslash key. + /// + /// + OemBackslash = 0xE2, + /// + /// + /// + /// The Oem 102 key. + /// + /// + Oem102 = OemBackslash, + /// + /// + /// + /// The PROCESS KEY key. + /// + /// + ProcessKey = 0xE5, + /// + /// + /// + /// The Packet KEY key. + /// + /// + Packet = 0xE7, + /// + /// + /// + /// The ATTN key. + /// + /// + Attn = 0xF6, + /// + /// + /// + /// The CRSEL key. + /// + /// + Crsel = 0xF7, + /// + /// + /// + /// The EXSEL key. + /// + /// + Exsel = 0xF8, + /// + /// + /// + /// The ERASE EOF key. + /// + /// + EraseEof = 0xF9, + /// + /// + /// + /// The PLAY key. + /// + /// + Play = 0xFA, + /// + /// + /// + /// The ZOOM key. + /// + /// + Zoom = 0xFB, + /// + /// + /// + /// A constant reserved for future use. + /// + /// + NoName = 0xFC, + /// + /// + /// + /// The PA1 key. + /// + /// + Pa1 = 0xFD, + /// + /// + /// + /// The CLEAR key. + /// + /// + OemClear = 0xFE, + /// + /// + /// + /// The SHIFT modifier key. + /// + /// + Shift = 0x00010000, + /// + /// + /// + /// The + /// CTRL modifier key. + /// + /// + /// + Control = 0x00020000, + /// + /// + /// + /// The ALT modifier key. + /// + /// + /// + Alt = 0x00040000, + } +} diff --git a/WindowsForms/Managed/System/WinForms/KeysConverter.cs b/WindowsForms/Managed/System/WinForms/KeysConverter.cs new file mode 100644 index 000000000..84d1d76a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/KeysConverter.cs @@ -0,0 +1,381 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Text; + using Microsoft.Win32; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + using System.Diagnostics.CodeAnalysis; + + /// + /// + /// Provides a type converter to convert objects to and from various + /// other representations. + /// + public class KeysConverter : TypeConverter, IComparer { + private IDictionary keyNames; + private List displayOrder; + private StandardValuesCollection values; + + private const Keys FirstDigit = System.Windows.Forms.Keys.D0; + private const Keys LastDigit = System.Windows.Forms.Keys.D9; + private const Keys FirstAscii = System.Windows.Forms.Keys.A; + private const Keys LastAscii = System.Windows.Forms.Keys.Z; + private const Keys FirstNumpadDigit = System.Windows.Forms.Keys.NumPad0; + private const Keys LastNumpadDigit = System.Windows.Forms.Keys.NumPad9; + + private void AddKey(string key, Keys value ) { + keyNames[key] = value; + displayOrder.Add(key); + } + + private void Initialize() { + keyNames = new Hashtable(34); + displayOrder = new List(34); + + AddKey(SR.GetString(SR.toStringEnter), Keys.Return); + AddKey("F12", Keys.F12); + AddKey("F11", Keys.F11); + AddKey("F10", Keys.F10); + AddKey(SR.GetString(SR.toStringEnd), Keys.End); + AddKey(SR.GetString(SR.toStringControl), Keys.Control); + AddKey("F8", Keys.F8); + AddKey("F9", Keys.F9); + AddKey(SR.GetString(SR.toStringAlt), Keys.Alt); + AddKey("F4", Keys.F4); + AddKey("F5", Keys.F5); + AddKey("F6", Keys.F6); + AddKey("F7", Keys.F7); + AddKey("F1", Keys.F1); + AddKey("F2", Keys.F2); + AddKey("F3", Keys.F3); + AddKey(SR.GetString(SR.toStringPageDown), Keys.Next); + AddKey(SR.GetString(SR.toStringInsert), Keys.Insert); + AddKey(SR.GetString(SR.toStringHome), Keys.Home); + AddKey(SR.GetString(SR.toStringDelete), Keys.Delete); + AddKey(SR.GetString(SR.toStringShift), Keys.Shift); + AddKey(SR.GetString(SR.toStringPageUp), Keys.Prior); + AddKey(SR.GetString(SR.toStringBack), Keys.Back); + + //new whidbey keys follow here... + // VSWhidbey 95006: Add string mappings for these values (numbers 0-9) so that the keyboard shortcuts + // will be displayed properly in menus. + AddKey("0", Keys.D0); + AddKey("1", Keys.D1); + AddKey("2", Keys.D2); + AddKey("3", Keys.D3); + AddKey("4", Keys.D4); + AddKey("5", Keys.D5); + AddKey("6", Keys.D6); + AddKey("7", Keys.D7); + AddKey("8", Keys.D8); + AddKey("9", Keys.D9); + } + + /// + /// + /// Access to a lookup table of name/value pairs for keys. These are localized + /// names. + /// + private IDictionary KeyNames { + get { + if (keyNames == null) { + Debug.Assert(displayOrder == null); + Initialize(); + } + return keyNames; + } + } + + private List DisplayOrder { + get { + if (displayOrder == null) { + Debug.Assert(keyNames == null); + Initialize(); + } + return displayOrder; + } + } + + /// + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string) || sourceType == typeof(Enum[])) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(Enum[])) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Compares two key values for equivalence. + /// + public int Compare(object a, object b) { + return String.Compare(ConvertToString(a), ConvertToString(b), false, CultureInfo.InvariantCulture); + } + + /// + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + + string text = ((string)value).Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse an array of key tokens. + // + string[] tokens = text.Split(new char[] {'+'}); + for (int i = 0; i < tokens.Length; i++) { + tokens[i] = tokens[i].Trim(); + } + + // Now lookup each key token in our key hashtable. + // + Keys key = (Keys)0; + bool foundKeyCode = false; + + for (int i = 0; i < tokens.Length; i++) { + object obj = KeyNames[tokens[i]]; + + if (obj == null) { + + // Key was not found in our table. See if it is a valid value in + // the Keys enum. + // + obj = Enum.Parse(typeof(Keys), tokens[i]); + } + + if (obj != null) { + Keys currentKey = (Keys)obj; + + if ((currentKey & Keys.KeyCode) != 0) { + + // We found a match. If we have previously found a + // key code, then check to see that this guy + // isn't a key code (it is illegal to have, say, + // "A + B" + // + if (foundKeyCode) { + throw new FormatException(SR.GetString(SR.KeysConverterInvalidKeyCombination)); + } + foundKeyCode = true; + } + + // Now OR the key into our current key + // + key |= currentKey; + } + else { + + // We did not match this key. Report this as an error too. + // + throw new FormatException(SR.GetString(SR.KeysConverterInvalidKeyName, tokens[i])); + + } + } + + return (object)key; + } + } + else if (value is Enum[]) { + long finalValue = 0; + foreach(Enum e in (Enum[])value) { + finalValue |= Convert.ToInt64(e, CultureInfo.InvariantCulture); + } + return Enum.ToObject(typeof(Keys), finalValue); + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + [SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")] + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (value is Keys || value is int) { + bool asString = destinationType == typeof(string); + bool asEnum = false; + if (!asString) asEnum = destinationType == typeof(Enum[]); + if (asString || asEnum) { + Keys key = (Keys)value; + bool added = false; + ArrayList terms = new ArrayList(); + Keys modifiers = (key & Keys.Modifiers); + + // First, iterate through and do the modifiers. These are + // additive, so we support things like Ctrl + Alt + // + for (int i = 0; i < DisplayOrder.Count; i++) { + string keyString = (string)DisplayOrder[i]; + Keys keyValue = (Keys)keyNames[keyString]; + if (((int)(keyValue) & (int)modifiers) != 0) { + + if (asString) { + if (added) { + terms.Add("+"); + } + + terms.Add((string)keyString); + } + else { + terms.Add(keyValue); + } + + added = true; + } + } + + // Now reset and do the key values. Here, we quit if + // we find a match. + // + Keys keyOnly = (key & Keys.KeyCode); + bool foundKey = false; + + if (added && asString) { + terms.Add("+"); + } + + for (int i = 0; i < DisplayOrder.Count; i++) { + string keyString = (string)DisplayOrder[i]; + Keys keyValue = (Keys)keyNames[keyString]; + if (keyValue.Equals(keyOnly)) { + + if (asString) { + terms.Add((string)keyString); + } + else { + terms.Add(keyValue); + } + added = true; + foundKey = true; + break; + } + } + + // Finally, if the key wasn't in our list, add it to + // the end anyway. Here we just pull the key value out + // of the enum. + // + if (!foundKey && Enum.IsDefined(typeof(Keys), (int)keyOnly)) { + if (asString) { + terms.Add(((Enum)keyOnly).ToString()); + } + else { + terms.Add((Enum)keyOnly); + } + } + + if (asString) { + StringBuilder b = new StringBuilder(32); + foreach(string t in terms) { + b.Append(t); + } + return b.ToString(); + } + else { + return (Enum[])terms.ToArray(typeof(Enum)); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (values == null) { + ArrayList list = new ArrayList(); + + ICollection keys = KeyNames.Values; + + foreach (object o in keys) { + list.Add(o); + } + + list.Sort(this); + + values = new StandardValuesCollection(list.ToArray()); + } + return values; + } + + /// + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + return false; + } + + /// + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Label.cs b/WindowsForms/Managed/System/WinForms/Label.cs new file mode 100644 index 000000000..504d408b2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Label.cs @@ -0,0 +1,1858 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Security.Permissions; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using System.Drawing.Imaging; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Serialization.Formatters; + using System.Windows.Forms.Layout; + using Microsoft.Win32; + using System.Collections; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Versioning; + using Automation; + + + /// + /// + /// + /// Represents a standard Windows label. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Text"), + DefaultBindingProperty("Text"), + Designer("System.Windows.Forms.Design.LabelDesigner, " + AssemblyRef.SystemDesign), + ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionLabel) + ] + // If not for FormatControl, we could inherit from ButtonBase and get foreground images for free. + public class Label : Control, IAutomationLiveRegion { + + private static readonly object EVENT_TEXTALIGNCHANGED = new object(); + + private static readonly BitVector32.Section StateUseMnemonic = BitVector32.CreateSection(1); + private static readonly BitVector32.Section StateAutoSize = BitVector32.CreateSection(1, StateUseMnemonic); + private static readonly BitVector32.Section StateAnimating = BitVector32.CreateSection(1, StateAutoSize); + private static readonly BitVector32.Section StateFlatStyle = BitVector32.CreateSection((int)FlatStyle.System, StateAnimating); + private static readonly BitVector32.Section StateBorderStyle = BitVector32.CreateSection((int)BorderStyle.Fixed3D, StateFlatStyle); + private static readonly BitVector32.Section StateAutoEllipsis = BitVector32.CreateSection(1, StateBorderStyle); + + private static readonly int PropImageList = PropertyStore.CreateKey(); + private static readonly int PropImage = PropertyStore.CreateKey(); + + private static readonly int PropTextAlign = PropertyStore.CreateKey(); + private static readonly int PropImageAlign = PropertyStore.CreateKey(); + private static readonly int PropImageIndex = PropertyStore.CreateKey(); + + /////////////////////////////////////////////////////////////////////// + // Label per instance members + // + // Note: Do not add anything to this list unless absolutely neccessary. + // + // Begin Members { + + // List of properties that are generally set, so we keep them directly on + // Label. + // + + BitVector32 labelState = new BitVector32(); + int requestedHeight; + int requestedWidth; + LayoutUtils.MeasureTextCache textMeasurementCache; + + //Tooltip is shown only if the Text in the Label is cut. + internal bool showToolTip = false; + ToolTip textToolTip; + // This bool suggests that the User has added a toolTip to this label + // In such a case we should not show the AutoEllipsis tooltip. + bool controlToolTip = false; + AutomationLiveSetting liveSetting; + + // } End Members + /////////////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public Label() + : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetStyle(ControlStyles.UserPaint | + ControlStyles.SupportsTransparentBackColor | + ControlStyles.OptimizedDoubleBuffer, IsOwnerDraw()); + + SetStyle(ControlStyles.FixedHeight | + ControlStyles.Selectable, false); + + SetStyle(ControlStyles.ResizeRedraw, true); + + CommonProperties.SetSelfAutoSizeInDefaultLayout(this,true); + + labelState[StateFlatStyle] = (int)FlatStyle.Standard; + labelState[StateUseMnemonic] = 1; + labelState[StateBorderStyle] = (int)BorderStyle.None; + + TabStop = false; + requestedHeight = Height; + requestedWidth = Width; + } + + + /// + /// + /// Indicates whether the control is automatically resized + /// to fit its contents. + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(false), + RefreshProperties(RefreshProperties.All), + Localizable(true), + SRDescription(SR.LabelAutoSizeDescr), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + if (AutoSize != value) { + base.AutoSize = value; + AdjustSize(); + } + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// This property controls the activation handling of bleedover for the text that + /// extends beyond the width of the label. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + SRDescription(SR.LabelAutoEllipsisDescr) + ] + public bool AutoEllipsis { + get { + return labelState[StateAutoEllipsis] != 0; + } + + set { + if (AutoEllipsis != value) { + labelState[StateAutoEllipsis] = value ? 1 : 0; + MeasureTextCache.InvalidateCache(); + + OnAutoEllipsisChanged(/*EventArgs.Empty*/); + + if (value) { + if (textToolTip == null) { + textToolTip = new ToolTip(); + } + } + + + if (ParentInternal != null) { + LayoutTransaction.DoLayoutIf(AutoSize,ParentInternal, this, PropertyNames.AutoEllipsis); + } + Invalidate(); + } + } + } + + /// + /// + /// + /// + /// Gets or sets the image rendered on the background of the control. + /// + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.LabelBackgroundImageDescr) + ] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// + /// + /// Gets or sets the image layout for the background of the control. + /// + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the border style for the control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.None), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.LabelBorderDescr) + ] + public virtual BorderStyle BorderStyle { + get { + return (BorderStyle)labelState[StateBorderStyle]; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (BorderStyle != value) { + labelState[StateBorderStyle] = (int)value; + if (ParentInternal != null) { + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.BorderStyle); + } + if (AutoSize) { + AdjustSize(); + } + RecreateHandle(); + } + } + } + + /// + /// Determines whether the current state of the control allows for rendering text using TextRenderer (GDI). + /// See LinkLabel implementation for details. + /// + internal virtual bool CanUseTextRenderer { + get{ + return true; + } + } + + /// + /// + /// + /// Overrides Control. A Label is a Win32 STATIC control, which we setup here. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "STATIC"; + + if (OwnerDraw) { + // An unfortunate side effect of this style is Windows sends us WM_DRAWITEM + // messages instead of WM_PAINT, but since Windows insists on repainting + // *without* a WM_PAINT after SetWindowText, I don't see much choice. + cp.Style |= NativeMethods.SS_OWNERDRAW; + + // Since we're owner draw, I don't see any point in setting the + // SS_CENTER/SS_RIGHT styles. + // + cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the SS_XXXX alignment styles + } + + + if (!OwnerDraw) { + switch(TextAlign) { + case ContentAlignment.TopLeft: + case ContentAlignment.MiddleLeft: + case ContentAlignment.BottomLeft: + cp.Style |= NativeMethods.SS_LEFT; + break; + + case ContentAlignment.TopRight: + case ContentAlignment.MiddleRight: + case ContentAlignment.BottomRight: + cp.Style |= NativeMethods.SS_RIGHT; + break; + + case ContentAlignment.TopCenter: + case ContentAlignment.MiddleCenter: + case ContentAlignment.BottomCenter: + cp.Style |= NativeMethods.SS_CENTER; + break; + } + } + else + cp.Style |= NativeMethods.SS_LEFT; + + switch (BorderStyle) { + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + case BorderStyle.Fixed3D: + cp.Style |= NativeMethods.SS_SUNKEN; + break; + } + + if (!UseMnemonic) + cp.Style |= NativeMethods.SS_NOPREFIX; + + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + protected override Padding DefaultMargin { + get { + return new Padding(3, 0, 3, 0); + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, AutoSize ? PreferredHeight : 23); + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Standard), + SRDescription(SR.ButtonFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { + return (FlatStyle)labelState[StateFlatStyle]; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle)); + } + + if (labelState[StateFlatStyle] != (int)value) { + bool needRecreate = (labelState[StateFlatStyle] == (int)FlatStyle.System) || (value == FlatStyle.System); + + labelState[StateFlatStyle] = (int)value; + + SetStyle(ControlStyles.UserPaint + | ControlStyles.SupportsTransparentBackColor + | ControlStyles.OptimizedDoubleBuffer, OwnerDraw); + + if (needRecreate) { + // this will clear the preferred size cache - it's OK if the parent is null - it would be a NOP. + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.BorderStyle); + if (AutoSize) { + AdjustSize(); + } + RecreateHandle(); + } + else { + Refresh(); + } + } + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRDescription(SR.ButtonImageDescr), + SRCategory(SR.CatAppearance) + ] + public Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + Image image = (Image)Properties.GetObject(PropImage); + + if (image == null && ImageList != null && ImageIndexer.ActualIndex >= 0) { + return ImageList.Images[ImageIndexer.ActualIndex]; + } + else { + return image; + } + } + set { + if (Image != value) { + StopAnimate(); + + Properties.SetObject(PropImage, value); + if (value != null) { + ImageIndex = -1; + ImageList = null; + } + + // Hook up the frame changed event + // + Animate(); + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the index value of the images displayed on the + /// . + /// + /// + [ + TypeConverterAttribute(typeof(ImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(-1), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ButtonImageIndexDescr), + SRCategory(SR.CatAppearance) + ] + public int ImageIndex { + get { + + if (ImageIndexer != null) { + int index = ImageIndexer.Index; + + if (ImageList != null && (index >= ImageList.Images.Count)) { + return ImageList.Images.Count - 1; + } + return index; + + } + return -1; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", (value).ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + if (ImageIndex != value) { + if (value != -1) { + // Image.set calls ImageIndex = -1 + Properties.SetObject(PropImage, null); + } + + ImageIndexer.Index = value; + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the key accessor for the image list. This specifies the image + /// from the image list to display on + /// . + /// + /// + [ + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(""), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ButtonImageIndexDescr), + SRCategory(SR.CatAppearance) + ] + public string ImageKey { + get { + + if (ImageIndexer != null) { + return ImageIndexer.Key; + } + return null; + } + set { + if (ImageKey != value) { + + // Image.set calls ImageIndex = -1 + Properties.SetObject(PropImage, null); + + ImageIndexer.Key = value; + Invalidate(); + } + } + } + + internal LabelImageIndexer ImageIndexer{ + get { + bool found; + LabelImageIndexer imageIndexer = Properties.GetObject(PropImageIndex, out found) as LabelImageIndexer; + + // Demand create the ImageIndexer property + if ((imageIndexer == null) || (!found)) { + imageIndexer = new LabelImageIndexer(this); + ImageIndexer = imageIndexer; + } + + return imageIndexer; + } + set { + Properties.SetObject(PropImageIndex, value); + } + + } + + /// + /// + /// + /// Gets or sets the images displayed in a . + /// + /// + [ + DefaultValue(null), + SRDescription(SR.ButtonImageListDescr), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatAppearance) + ] + public ImageList ImageList { + get { + return (ImageList)Properties.GetObject(PropImageList); + } + set { + if (ImageList != value) { + + EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + // Remove the previous imagelist handle recreate handler + // + ImageList imageList = ImageList; + if (imageList != null) { + imageList.RecreateHandle -= recreateHandler; + imageList.Disposed -= disposedHandler; + } + + // Make sure we don't have an Image as well as an ImageList + // + if (value != null) { + Properties.SetObject(PropImage, null); // Image.set calls ImageList = null + } + + Properties.SetObject(PropImageList, value); + + // Add the new imagelist handle recreate handler + // + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets or sets the alignment of the image on the . + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRDescription(SR.ButtonImageAlignDescr), + SRCategory(SR.CatAppearance) + ] + public ContentAlignment ImageAlign { + get { + bool found; + int imageAlign = Properties.GetInteger(PropImageAlign, out found); + if (found) { + return (ContentAlignment)imageAlign; + } + return ContentAlignment.MiddleCenter; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if(value != ImageAlign) { + Properties.SetInteger(PropImageAlign, (int)value); + LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.ImageAlign); + Invalidate(); + } + } + } + /* FxCop rule 'AvoidBuildingNonCallableCode' - Left here in case it is needed in the future. + private Rectangle ImageBounds { + get { + Image image = (Image)Properties.GetObject(PropImage); + return CalcImageRenderBounds(image, ClientRectangle, RtlTranslateAlignment(ImageAlign)); + } + } + */ + + /// + /// Indicates the "politeness" level that a client should use + /// to notify the user of changes to the live region. + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AutomationLiveSetting.Off), + SRDescription(SR.LiveRegionAutomationLiveSettingDescr), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + public AutomationLiveSetting LiveSetting { + get { + return liveSetting; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutomationLiveSetting.Off, (int)AutomationLiveSetting.Assertive)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutomationLiveSetting)); + } + liveSetting = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + internal LayoutUtils.MeasureTextCache MeasureTextCache { + get { + if (textMeasurementCache == null) { + textMeasurementCache = new LayoutUtils.MeasureTextCache(); + } + return textMeasurementCache; + } + } + + internal virtual bool OwnerDraw { + get { + return IsOwnerDraw(); + } + } + + /// + /// + /// + /// Gets the height of the control (in pixels), assuming a + /// single line of text is displayed. + /// + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.LabelPreferredHeightDescr) + ] + public virtual int PreferredHeight { + get { return PreferredSize.Height; } + } + + /// + /// + /// + /// Gets the width of the control (in pixels), assuming a single line + /// of text is displayed. + /// + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.LabelPreferredWidthDescr) + ] + public virtual int PreferredWidth { + get { return PreferredSize.Width; } + } + + /// + /// + /// Indicates whether + /// the container control background is rendered on the . + /// + [Obsolete("This property has been deprecated. Use BackColor instead. http://go.microsoft.com/fwlink/?linkid=14202")] + virtual new protected bool RenderTransparent + { + get { + return((Control) this).RenderTransparent; + } + set { + } + } + + + private bool SelfSizing { + get { + return CommonProperties.ShouldSelfSize(this); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the user can tab to the + /// . + /// + /// + [DefaultValue(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the + /// horizontal alignment of the text in the control. + /// + /// + [ + SRDescription(SR.LabelTextAlignDescr), + Localizable(true), + DefaultValue(ContentAlignment.TopLeft), + SRCategory(SR.CatAppearance) + ] + public virtual ContentAlignment TextAlign { + get { + bool found; + int textAlign = Properties.GetInteger(PropTextAlign, out found); + if (found) { + return (ContentAlignment)textAlign; + } + + return ContentAlignment.TopLeft; + } + set { + + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + + if (TextAlign != value) { + Properties.SetInteger(PropTextAlign, (int)value); + Invalidate(); + //Change the TextAlignment for SystemDrawn Labels .... + if (!OwnerDraw) { + RecreateHandle(); + } + OnTextAlignChanged(EventArgs.Empty); + + } + } + } + + /// + /// + /// + /// Gets or sets the text in the Label. Since we can have multiline support + /// this property just overides the base to pluck in the Multiline editor. + /// + /// + [ + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SettingsBindable(true) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.LabelOnTextAlignChangedDescr)] + public event EventHandler TextAlignChanged { + add { + Events.AddHandler(EVENT_TEXTALIGNCHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_TEXTALIGNCHANGED, value); + } + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public bool UseCompatibleTextRendering { + get{ + if( CanUseTextRenderer ){ + return base.UseCompatibleTextRenderingInt; + } + + // Use compat text rendering (GDI+). + return true; + } + set{ + if( base.UseCompatibleTextRenderingInt != value ) { + base.UseCompatibleTextRenderingInt = value; + AdjustSize(); + } + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same + /// value if the child control supports it. + /// + internal override bool SupportsUseCompatibleTextRendering { + get { + return true; + } + } + + /// + /// + /// Gets or sets a value indicating whether an ampersand (&) included in the text of + /// the control. + /// + [ + SRDescription(SR.LabelUseMnemonicDescr), + DefaultValue(true), + SRCategory(SR.CatAppearance) + ] + public bool UseMnemonic { + get { + return labelState[StateUseMnemonic] != 0; + } + + set { + + if (UseMnemonic != value) { + labelState[StateUseMnemonic] = value ? 1 : 0; + MeasureTextCache.InvalidateCache(); + + + // The size of the label need to be adjusted when the Mnemonic + // is set irrespective of auto-sizing + using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { + AdjustSize(); + Invalidate(); + } + + //set windowStyle directly instead of recreating handle to increase efficiency + if (IsHandleCreated) { + int style = this.WindowStyle; + if (!UseMnemonic) { + + style |= NativeMethods.SS_NOPREFIX; + } + else { + style &= ~NativeMethods.SS_NOPREFIX; + } + this.WindowStyle = style; + } + } + } + } + + + /// + /// Updates the control in response to events that could affect either + /// the size of the control, or the size of the text within it. + /// + /// + internal void AdjustSize() { + if (!SelfSizing) { + return; + } + + // the rest is here for RTM compat. + + // If width and/or height are constrained by anchoring, don't adjust control size + // to fit around text, since this will cause us to lose the original anchored size. + if (!AutoSize && + ((this.Anchor & (AnchorStyles.Left | AnchorStyles.Right)) == (AnchorStyles.Left | AnchorStyles.Right) || + (this.Anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom))) { + return; + } + + // Resize control to fit around current text + // + int saveHeight = requestedHeight; + int saveWidth = requestedWidth; + try { + Size preferredSize = (AutoSize) ? PreferredSize : new Size(saveWidth, saveHeight); + Size = preferredSize; + } + finally { + requestedHeight = saveHeight; + requestedWidth = saveWidth; + } + } + + internal void Animate() { + Animate(!DesignMode && Visible && Enabled && ParentInternal != null); + } + + internal void StopAnimate() { + Animate(false); + } + + private void Animate(bool animate) { + bool currentlyAnimating = labelState[StateAnimating] != 0; + if (animate != currentlyAnimating) { + Image image = (Image)Properties.GetObject(PropImage); + if (animate) { + if (image != null) { + ImageAnimator.Animate(image, new EventHandler(this.OnFrameChanged)); + labelState[StateAnimating] = animate ? 1 : 0; + } + } + else { + if (image != null) { + ImageAnimator.StopAnimate(image, new EventHandler(this.OnFrameChanged)); + labelState[StateAnimating] = animate ? 1 : 0; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected Rectangle CalcImageRenderBounds(Image image, Rectangle r, ContentAlignment align) { + Size pointImageSize = image.Size; + + int xLoc = r.X + 2; + int yLoc = r.Y + 2; + + if ((align & WindowsFormsUtils.AnyRightAlign) !=0) { + xLoc = (r.X+r.Width - 4)-pointImageSize.Width; + } + else if ((align & WindowsFormsUtils.AnyCenterAlign) != 0) { + xLoc = r.X + (r.Width - pointImageSize.Width)/2; + } + + + if ((align & WindowsFormsUtils.AnyBottomAlign) != 0) { + yLoc = (r.Y+r.Height - 4)-pointImageSize.Height; + } + else if ((align & WindowsFormsUtils.AnyTopAlign) != 0) { + yLoc = r.Y + 2; + } + else { + yLoc = r.Y + (r.Height - pointImageSize.Height)/2; + } + + return new Rectangle(xLoc, yLoc, pointImageSize.Width, pointImageSize.Height); + } + + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new LabelAccessibleObject(this); + } + + /// + /// Get StringFormat object for rendering text using GDI+ (Graphics). + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + internal virtual StringFormat CreateStringFormat() { + return ControlPaint.CreateStringFormat( this, this.TextAlign, this.AutoEllipsis, this.UseMnemonic ); + } + + private TextFormatFlags CreateTextFormatFlags() { + return CreateTextFormatFlags(this.Size - GetBordersAndPadding()); + } + /// + /// Get TextFormatFlags flags for rendering text using GDI (TextRenderer). + /// + internal virtual TextFormatFlags CreateTextFormatFlags(Size constrainingSize){ + + // PREFERRED SIZE CACHING: + // Please read if you're adding a new TextFormatFlag. + // whenever something can change the TextFormatFlags used + // MeasureTextCache.InvalidateCache() should be called so we can approprately clear. + + TextFormatFlags flags = ControlPaint.CreateTextFormatFlags( this, this.TextAlign, this.AutoEllipsis, this.UseMnemonic ); + + // Remove WordBreak if the size is large enough to display all the text. + if (!MeasureTextCache.TextRequiresWordBreak(Text, Font, constrainingSize, flags)) { + // The effect of the TextBoxControl flag is that in-word line breaking will occur if needed, this happens when AutoSize + // is false and a one-word line still doesn't fit the binding box (width). The other effect is that partially visible + // lines are clipped; this is how GDI+ works by default. + flags &= ~(TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl); + } + + return flags; + } + + private void DetachImageList(object sender, EventArgs e) { + ImageList = null; + } + + + /// + /// + /// [To be supplied.] + /// + protected override void Dispose(bool disposing) { + if (disposing) { + StopAnimate(); + // VSWhidbey 318551: holding on to images and image list is a memory leak. + if (ImageList != null) { + ImageList.Disposed -= new EventHandler(this.DetachImageList); + ImageList.RecreateHandle -= new EventHandler(ImageListRecreateHandle); + Properties.SetObject(PropImageList, null); + } + if (Image != null) { + Properties.SetObject(PropImage, null); + } + + //Dipose the tooltip if one present.. + if (textToolTip != null) { + textToolTip.Dispose(); + textToolTip = null; + } + controlToolTip = false; + } + base.Dispose(disposing); + } + + /// + /// + /// + /// Draws an within the specified bounds. + /// + /// + protected void DrawImage(System.Drawing.Graphics g, Image image, Rectangle r, ContentAlignment align) { + Rectangle loc = CalcImageRenderBounds(image, r, align); + + if (!Enabled) { + ControlPaint.DrawImageDisabled(g, image, loc.X, loc.Y, BackColor); + } + else { + g.DrawImage(image, loc.X, loc.Y, image.Width, image.Height); + } + } + + /// + /// + /// + /// + /// This Function returns the number of lines in the label Text. + /// + /// + /* FxCop rule 'AvoidBuildingNonCallableCode' - Left here in case it is needed in the future. + private ArrayList GetLines() { + string text = Text; + ArrayList list = new ArrayList(); + + int lineStart = 0; + while (lineStart < text.Length) { + int lineEnd = lineStart; + for (; lineEnd < text.Length; lineEnd++) { + char c = text[lineEnd]; + if (c == '\r' || c == '\n') + break; + } + + string line = text.Substring(lineStart, lineEnd - lineStart); + list.Add(line); + + // Treat "\r", "\r\n", and "\n" as new lines + if (lineEnd < text.Length && text[lineEnd] == '\r') + lineEnd++; + if (lineEnd < text.Length && text[lineEnd] == '\n') + lineEnd++; + + lineStart = lineEnd; + } + return list; + } + */ + private Size GetBordersAndPadding() { + Size bordersAndPadding = Padding.Size; + + // COMPAT VSW 248415: Everett added random numbers to the height of the label + if (UseCompatibleTextRendering) { + //Always return the Fontheight + some buffer else the Text gets clipped for Autosize = true.. + //(bug 118909) + if (BorderStyle != BorderStyle.None) { + bordersAndPadding.Height += 6; // taken from Everett.PreferredHeight + bordersAndPadding.Width += 2; // taken from Everett.PreferredWidth + } + else { + bordersAndPadding.Height += 3; // taken from Everett.PreferredHeight + } + } + else { + // in Whidbey we'll actually ask the control the border size. + + bordersAndPadding += SizeFromClientSize(Size.Empty); + if (BorderStyle == BorderStyle.Fixed3D) { + bordersAndPadding += new Size(2, 2); + } + } + return bordersAndPadding; + + } + + + public override Size GetPreferredSize(Size proposedSize) { + //make sure the behavior is consistent with GetPreferredSizeCore + if (proposedSize.Width == 1) { + proposedSize.Width = 0; + } + if (proposedSize.Height == 1) { + proposedSize.Height = 0; + } + return base.GetPreferredSize(proposedSize); + } + + internal virtual bool UseGDIMeasuring() { + return (FlatStyle == FlatStyle.System || !UseCompatibleTextRendering); + } + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // See ComboBox.cs GetComboHeight + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + Size bordersAndPadding = GetBordersAndPadding(); + // Subtract border area from constraints + proposedConstraints -= bordersAndPadding; + + // keep positive + proposedConstraints = LayoutUtils.UnionSizes(proposedConstraints,Size.Empty); + + Size requiredSize; + + // + // TEXT Measurement + // + + if (string.IsNullOrEmpty(Text)) { + // COMPAT VSW 248415: empty labels return the font height + borders + using (WindowsFont font = WindowsFont.FromFont(this.Font)) { + // this is the character that Windows uses to determine the extent + requiredSize = WindowsGraphicsCacheManager.MeasurementGraphics.GetTextExtent("0", font); + requiredSize.Width = 0; + } + } + else if (UseGDIMeasuring()) { + TextFormatFlags format = FlatStyle == FlatStyle.System ? TextFormatFlags.Default : CreateTextFormatFlags(proposedConstraints); + requiredSize = MeasureTextCache.GetTextSize(Text, Font, proposedConstraints, format); + } + else { + // GDI+ rendering. + using (Graphics measurementGraphics = WindowsFormsUtils.CreateMeasurementGraphics()) { + using (StringFormat stringFormat = CreateStringFormat()) { + SizeF bounds = (proposedConstraints.Width == 1) ? + new SizeF(0, proposedConstraints.Height) : + new SizeF(proposedConstraints.Width, proposedConstraints.Height); + + requiredSize = Size.Ceiling(measurementGraphics.MeasureString(Text, Font, bounds, stringFormat)); + } + } + + } + + requiredSize += bordersAndPadding; + + return requiredSize; + } + + /// + /// This method is to be called by LabelDesigner, using private reflection, to get the location of the text snaplines. + /// + [ + // This method is called by LabelDesigner, using private reflection. + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode") + ] + private int GetLeadingTextPaddingFromTextFormatFlags(){ + if( !IsHandleCreated ){ + return 0; + } + + //If we are using GDI+ the code below will not work, except + //if the style is FlatStyle.System since GDI will be used in that case. + if (UseCompatibleTextRendering && FlatStyle != FlatStyle.System) { + return 0; + } + + using( WindowsGraphics wg = WindowsGraphics.FromHwnd(this.Handle) ){ + TextFormatFlags flags = CreateTextFormatFlags(); + + if( (flags & TextFormatFlags.NoPadding ) == TextFormatFlags.NoPadding ){ + wg.TextPadding = TextPaddingOptions.NoPadding; + } + else if( (flags & TextFormatFlags.LeftAndRightPadding ) == TextFormatFlags.LeftAndRightPadding ){ + wg.TextPadding = TextPaddingOptions.LeftAndRightPadding; + } + + using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont(this.Font)) { + IntNativeMethods.DRAWTEXTPARAMS dtParams = wg.GetTextMargins(wf); + + // This is actually leading margin. + return dtParams.iLeftMargin; + } + } + } + + private void ImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + Invalidate(); + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal override bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + /// + /// This method is required because the Label constructor needs to know if the control is OwnerDraw but it should + /// not call the virtual property for the following reason (from FxCop rule 'ConstructorsShouldNotCallBaseClassVirtualMethods'): + /// Virtual methods defined on the class should not be called from constructors. If a derived class has overridden + /// the method, the derived class version will be called (before the derived class constructor is called). + /// + internal bool IsOwnerDraw(){ + return FlatStyle != FlatStyle.System; + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseEnter(EventArgs e) { + if (!controlToolTip && !DesignMode && AutoEllipsis && showToolTip && textToolTip != null) { + + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + controlToolTip = true; + textToolTip.Show(WindowsFormsUtils.TextWithoutMnemonics(Text), this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + controlToolTip = false; + } + + } + base.OnMouseEnter(e); + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + protected override void OnMouseLeave(EventArgs e) { + if (!controlToolTip && textToolTip != null && textToolTip.GetHandleCreated()) { + textToolTip.RemoveAll(); + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + textToolTip.Hide(this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + + base.OnMouseLeave(e); + } + + private void OnFrameChanged(object o, EventArgs e) { + if (Disposing || IsDisposed) { + return; + } + if (IsHandleCreated && InvokeRequired) + { + BeginInvoke(new EventHandler(this.OnFrameChanged), o, e); + return; + } + + Invalidate(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + MeasureTextCache.InvalidateCache(); + base.OnFontChanged(e); + AdjustSize(); + Invalidate(); + } + + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + if (textToolTip != null && textToolTip.GetHandleCreated()) + { + textToolTip.DestroyHandle(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnTextChanged(EventArgs e) { + + using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { + MeasureTextCache.InvalidateCache(); + base.OnTextChanged(e); + AdjustSize(); + Invalidate(); + } + + if (AccessibilityImprovements.Level3 && LiveSetting != AutomationLiveSetting.Off) { + AccessibilityObject.RaiseLiveRegionChanged(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnTextAlignChanged(EventArgs e) { + EventHandler eh = Events[EVENT_TEXTALIGNCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + protected override void OnPaddingChanged(EventArgs e) { + base.OnPaddingChanged(e); + AdjustSize(); + } + + /// + /// + /// + /// + protected override void OnPaint(PaintEventArgs e) { + Animate(); + ImageAnimator.UpdateFrames(this.Image); + + Rectangle face = LayoutUtils.DeflateRect(ClientRectangle, Padding); + Image i = this.Image; + if (i != null) { + DrawImage(e.Graphics, i, face, RtlTranslateAlignment(ImageAlign)); + } + + Color color; + IntPtr hdc = e.Graphics.GetHdc(); + try { + using (WindowsGraphics wg = WindowsGraphics.FromHdc( hdc )) { + color = wg.GetNearestColor((Enabled) ? ForeColor : DisabledColor); + } + } + finally { + e.Graphics.ReleaseHdc(); + } + + // Do actual drawing + + if (AutoEllipsis) { + Rectangle clientRect = this.ClientRectangle; + Size preferredSize = GetPreferredSize(new Size(clientRect.Width, clientRect.Height)); + showToolTip = (clientRect.Width < preferredSize.Width || clientRect.Height < preferredSize.Height); + } + else { + showToolTip = false; + } + + if (UseCompatibleTextRendering) { + using (StringFormat stringFormat = CreateStringFormat()) { + if (Enabled) { + using (Brush brush = new SolidBrush(color)) { + e.Graphics.DrawString(Text, Font, brush, face, stringFormat); + } + } + else { + ControlPaint.DrawStringDisabled(e.Graphics, Text, Font, color, face, stringFormat); + } + } + } + else{ + TextFormatFlags flags = CreateTextFormatFlags(); + + if (Enabled) { + TextRenderer.DrawText(e.Graphics, Text, Font, face, color, flags); + } + else { + //Theme specs -- if the backcolor is darker than Control, we use + // ControlPaint.Dark(backcolor). Otherwise we use ControlDark. + // see VS#357226 + Color disabledTextForeColor = TextRenderer.DisabledTextColor(this.BackColor); + TextRenderer.DrawText(e.Graphics, Text, Font, face, disabledTextForeColor, flags); + } + } + + base.OnPaint(e); // raise paint event + } + + /// + /// Overriden by LinkLabel. + /// + internal virtual void OnAutoEllipsisChanged(/*EventArgs e*/) { + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + Animate(); + } + + + /// + /// + /// [To be supplied.] + /// + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + if (SelfSizing) { + // VSWhidbey 368232: in the case of SelfSizing + // we dont know what size to be until we're parented + AdjustSize(); + } + Animate(); + } + + protected override void OnRightToLeftChanged(EventArgs e) { + MeasureTextCache.InvalidateCache(); + base.OnRightToLeftChanged(e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + Animate(); + } + + internal override void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + base.PrintToMetaFileRecursive(hDC, lParam, bounds); + + using (WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, bounds)) { + using(Graphics g = Graphics.FromHdcInternal(hDC.Handle)) { + ControlPaint.PrintBorder(g, new Rectangle(Point.Empty, Size), BorderStyle, Border3DStyle.SunkenOuter); + } + } + } + + + + /// + /// + /// + /// + /// Overrides Control. This is called when the user has pressed an Alt-CHAR + /// key combination and determines if that combination is an interesting + /// mnemonic for this control. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (UseMnemonic && IsMnemonic(charCode, Text) && CanProcessMnemonic()) { + Control parent = ParentInternal; + if (parent != null) { + // SECREVIEW: This assert is required because the call to SelectNextControl will generate a call to SetActiveControl which have a demand, + // it is safe since no input from user code is processed while setting the active control originated this way. + // + IntSecurity.ModifyFocus.Assert(); + try { + if (parent.SelectNextControl(this, true, false, true, false)) { + if (!parent.ContainsFocus) { + parent.Focus(); + } + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + return true; + } + return false; + } + + /// + /// + /// Overrides Control.setBoundsCore to enforce autoSize. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) + requestedHeight = height; + if ((specified & BoundsSpecified.Width) != BoundsSpecified.None) + requestedWidth = width; + + if (AutoSize && SelfSizing) { + Size preferredSize = PreferredSize; + width = preferredSize.Width; + height = preferredSize.Height; + } + + base.SetBoundsCore(x, y, width, height, specified); + + Debug.Assert(!AutoSize || (AutoSize && !SelfSizing) || Size == PreferredSize, + "It is SetBoundsCore's responsibility to ensure Size = PreferredSize when AutoSize is true."); + } + + private void ResetImage() { + Image = null; + } + + private bool ShouldSerializeImage() { + return Properties.GetObject(PropImage) != null; + } + + + /// + /// + /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. + /// + /// + internal void SetToolTip(ToolTip toolTip) { + if (toolTip != null && !controlToolTip) { + controlToolTip = true; + } + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", Text: " + Text; + } + + /// + /// + /// + /// + /// Overrides Control. This processes certain messages that the Win32 STATIC + /// class would normally override. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_NCHITTEST: + // label returns HT_TRANSPARENT for everything, so all messages get + // routed to the parent. Change this so we can tell what's going on. + // + Rectangle rectInScreen = this.RectangleToScreen(new Rectangle(0, 0, Width, Height)); + Point pt = new Point(unchecked((int)(long)m.LParam)); + m.Result = (IntPtr)((rectInScreen.Contains(pt) ? NativeMethods.HTCLIENT : NativeMethods.HTNOWHERE)); + break; + + default: + base.WndProc(ref m); + break; + } + } + + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (!DpiHelper.EnableDpiChangedHighDpiImprovements) { + return; + } + + // When Font is derived from parent, Dpi changed event required to clear the cache + // to recalculate the label size. + MeasureTextCache.InvalidateCache(); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class LabelAccessibleObject : ControlAccessibleObject { + + public LabelAccessibleObject(Label owner) : base(owner) { + } + + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.StaticText; + } + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_TextControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } + + + + /// + /// + /// + /// + /// Override ImageList.Indexer to support Label's ImageList semantics. + /// + /// + internal class LabelImageIndexer : ImageList.Indexer { + private Label owner; + private bool useIntegerIndex = true; + + public LabelImageIndexer(Label owner) { + this.owner = owner; + } + + public override ImageList ImageList { + get { return (owner == null) ? null : owner.ImageList; } + + set { Debug.Assert(false, "Setting the image list in this class is not supported");} + } + + + public override string Key { + get { return base.Key; } + set { + base.Key = value; + useIntegerIndex = false; + } + } + + public override int Index { + get { return base.Index; } + set { + base.Index = value; + useIntegerIndex = true; + } + + } + + + public override int ActualIndex { + get { + if (useIntegerIndex) { + // The behavior of label is to return the last item in the Images collection + // if the index is currently set higher than the count. + return (Index < ImageList.Images.Count) ? Index : ImageList.Images.Count - 1; + } + else if (ImageList != null) { + return ImageList.Images.IndexOfKey(Key); + } + + return -1; + } + } + + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/LabelEditEvent.cs b/WindowsForms/Managed/System/WinForms/LabelEditEvent.cs new file mode 100644 index 000000000..0ef16f84b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LabelEditEvent.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class LabelEditEventArgs : EventArgs { + private readonly string label; + private readonly int item; + private bool cancelEdit = false; + + /// + /// + /// + /// Initializes a new instance + /// of the class with the specified + /// index to the to edit. + /// + /// + public LabelEditEventArgs(int item) { + this.item = item; + this.label = null; + } + + /// + /// + /// + /// Initializes a new instance + /// of the class with the specified index to the being + /// edited and the new text for the label of the . + /// + /// + public LabelEditEventArgs(int item, string label) { + this.item = item; + this.label = label; + } + + /// + /// + /// + /// Gets the new text assigned to the label of the . + /// + /// + public string Label { + get { + return label; + } + } + + /// + /// + /// + /// Gets the zero-based index of the containing the label to + /// edit. + /// + /// + public int Item { + get { + return item; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether changes made to the label of + /// the should be canceled. + /// + /// + public bool CancelEdit { + get { + return cancelEdit; + } + set { + cancelEdit = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/LabelEditEventHandler.cs b/WindowsForms/Managed/System/WinForms/LabelEditEventHandler.cs new file mode 100644 index 000000000..a03122076 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LabelEditEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the event of a + /// + /// . + /// + /// + public delegate void LabelEditEventHandler(object sender, LabelEditEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/ArrangedElementCollection.cs b/WindowsForms/Managed/System/WinForms/Layout/ArrangedElementCollection.cs new file mode 100644 index 000000000..ddfa1195d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/ArrangedElementCollection.cs @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + + using System.Collections; + using System.Security.Permissions; + + /// + public class ArrangedElementCollection : IList + { + internal static ArrangedElementCollection Empty = new ArrangedElementCollection(0); + + // We wrap an ArrayList rather than inherit from CollectionBase because we + // do not want to break binary compatibility with ControlCollection. + private ArrayList _innerList; + + // Internal constructor prevents externals from getting a hold of one of these. + // We'll open this up in Orcas. + internal ArrangedElementCollection() { + _innerList = new ArrayList(4); + } + + internal ArrangedElementCollection(ArrayList innerList) { + _innerList = innerList; + } + + private ArrangedElementCollection(int size) { + _innerList = new ArrayList(size); + } + + internal ArrayList InnerList { + get { return _innerList; } + } + + internal virtual IArrangedElement this[int index] { + get { return (IArrangedElement) InnerList[index]; } + } + + /// + public override bool Equals(object obj) { + ArrangedElementCollection other = obj as ArrangedElementCollection; + + if (other == null || Count != other.Count) { + return false; + } + + for (int i = 0; i < Count; i++) { + if (this.InnerList[i] != other.InnerList[i]) { + return false; + } + } + return true; + } + + // Required if you override Equals. + /// + public override int GetHashCode() { return base.GetHashCode(); } + + // Repositions a element in this list. + internal void MoveElement(IArrangedElement element, int fromIndex, int toIndex) { + int delta = toIndex - fromIndex; + + switch (delta) { + case -1: + case 1: + // simple swap + this.InnerList[fromIndex] = this.InnerList[toIndex]; + break; + + default: + int start = 0; + int dest = 0; + + // which direction are we moving? + if (delta > 0) { + // shift down by the delta to open the new spot + start = fromIndex + 1; + dest = fromIndex; + } + else { + // shift up by the delta to open the new spot + start = toIndex; + dest = toIndex + 1; + + // make it positive + delta = -delta; + } + Copy(this, start, this, dest, delta); + break; + } + this.InnerList[toIndex] = element; + } + + private static void Copy (ArrangedElementCollection sourceList, int sourceIndex, ArrangedElementCollection destinationList, int destinationIndex, int length) { + if(sourceIndex < destinationIndex) { + // We need to copy from the back forward to prevent overwrite if source and + // destination lists are the same, so we need to flip the source/dest indices + // to point at the end of the spans to be copied. + sourceIndex = sourceIndex + length; + destinationIndex = destinationIndex + length; + + for(;length > 0; length--) { + destinationList.InnerList[--destinationIndex] = sourceList.InnerList[--sourceIndex]; + } + } else { + for(;length > 0; length--) { + destinationList.InnerList[destinationIndex++] = sourceList.InnerList[sourceIndex++]; + } + } + } + + #region IList Members + void IList.Clear() { InnerList.Clear(); } + bool IList.IsFixedSize { get { return InnerList.IsFixedSize; }} + bool IList.Contains(object value) { return InnerList.Contains(value); } + /// + public virtual bool IsReadOnly { get { return InnerList.IsReadOnly; }} + void IList.RemoveAt(int index) { InnerList.RemoveAt(index); } + void IList.Remove(object value) { InnerList.Remove(value); } + int IList.Add(object value) { return InnerList.Add(value); } + int IList.IndexOf(object value) { return InnerList.IndexOf(value); } + void IList.Insert(int index, object value) { throw new NotSupportedException(); /* InnerList.Insert(index, value); */ } + + /// + /// + object IList.this[int index] { + get { return InnerList[index]; } + set { throw new NotSupportedException(); } + } + #endregion + + #region ICollection Members + /// + public virtual int Count { + get { return InnerList.Count; } + } + object ICollection.SyncRoot { get { return InnerList.SyncRoot; }} + /// + public void CopyTo(Array array, int index) { InnerList.CopyTo(array, index); } + bool ICollection.IsSynchronized { get { return InnerList.IsSynchronized; }} + #endregion + + #region IEnumerable Members + /// + public virtual IEnumerator GetEnumerator() { return InnerList.GetEnumerator(); } + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/CommonProperties.cs b/WindowsForms/Managed/System/WinForms/Layout/CommonProperties.cs new file mode 100644 index 000000000..4d5e0cbf9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/CommonProperties.cs @@ -0,0 +1,737 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System; + using System.Collections.Specialized; + using System.Collections; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + + + // Some LayoutEngines extend the same properties to their children. We want + // these extended properties to retain their value when moved from one container + // to another. (For example, set BoxStretchInternal on a control in FlowPanel and then move + // the control to GridPanel.) CommonProperties is a place to define keys and + // accessors for such properties. + internal class CommonProperties { + + + private static readonly int _layoutStateProperty = PropertyStore.CreateKey(); + private static readonly int _specifiedBoundsProperty = PropertyStore.CreateKey(); + private static readonly int _preferredSizeCacheProperty = PropertyStore.CreateKey(); + private static readonly int _paddingProperty = PropertyStore.CreateKey(); + + private static readonly int _marginProperty = PropertyStore.CreateKey(); + private static readonly int _minimumSizeProperty = PropertyStore.CreateKey(); + private static readonly int _maximumSizeProperty = PropertyStore.CreateKey(); + private static readonly int _layoutBoundsProperty = PropertyStore.CreateKey(); + +#if DEBUG + private static readonly int _lastKnownStateProperty = PropertyStore.CreateKey(); + +#endif + + internal const ContentAlignment DefaultAlignment = ContentAlignment.TopLeft; + internal const AnchorStyles DefaultAnchor = AnchorStyles.Top | AnchorStyles.Left; + internal const bool DefaultAutoSize = false; + + internal const DockStyle DefaultDock = DockStyle.None; + internal static readonly Padding DefaultMargin = new Padding(3); + internal static readonly Size DefaultMinimumSize = new Size(0, 0); + internal static readonly Size DefaultMaximumSize = new Size(0, 0); + + + // DO NOT MOVE THE FOLLOWING 4 SECTIONS + // We have done some special arranging here so that if the first 7 bits of state are zero, we know + // that the control is purely absolutely positioned and DefaultLayout does not need to do + // anything. + // + private static readonly BitVector32.Section _dockAndAnchorNeedsLayoutSection = BitVector32.CreateSection(0x7F); + private static readonly BitVector32.Section _dockAndAnchorSection = BitVector32.CreateSection(0x0F); + private static readonly BitVector32.Section _dockModeSection = BitVector32.CreateSection(0x01, _dockAndAnchorSection); + private static readonly BitVector32.Section _autoSizeSection = BitVector32.CreateSection(0x01, _dockModeSection); + private static readonly BitVector32.Section _BoxStretchInternalSection = BitVector32.CreateSection(0x03, _autoSizeSection); + private static readonly BitVector32.Section _anchorNeverShrinksSection = BitVector32.CreateSection(0x01, _BoxStretchInternalSection); + private static readonly BitVector32.Section _flowBreakSection = BitVector32.CreateSection(0x01, _anchorNeverShrinksSection); + private static readonly BitVector32.Section _selfAutoSizingSection = BitVector32.CreateSection(0x01, _flowBreakSection); + private static readonly BitVector32.Section _autoSizeModeSection = BitVector32.CreateSection(0x01, _selfAutoSizingSection); + + + + private enum DockAnchorMode{ + Anchor = 0, + Dock = 1 + } + +#region AppliesToAllLayouts + + /// ClearMaximumSize + /// Removes the maximum size from the property store, making it "unset". + /// + internal static void ClearMaximumSize(IArrangedElement element) { + if (element.Properties.ContainsObject(_maximumSizeProperty)) { + element.Properties.RemoveObject(_maximumSizeProperty); + } + } + + /// GetAutoSize + /// Determines whether or not the System.Windows.Forms.Layout LayoutEngines + /// think the element is AutoSized. + /// + /// A control can thwart the layout engine by overriding its virtual AutoSize + /// property and not calling base. If CommonProperties.GetAutoSize(element) is false, + /// a layout engine will treat it as AutoSize = false and not size the element to its + /// preferred size. + internal static bool GetAutoSize(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + int value = state[_autoSizeSection]; + return value != 0; + } + + /// GetMargin + /// Returns the Margin (exterior space) for an item + /// + /// We can not use our pattern of passing the default value into Margin because the + /// LayoutEngines read this property and do not know each element's DefaultMargin. + /// Instead the Element sets the Margin in its ctor. + internal static Padding GetMargin(IArrangedElement element) { + bool found; + Padding padding = element.Properties.GetPadding(_marginProperty, out found); + if (found) { + return padding; + } + return DefaultMargin; + } + + /// GetMaximumSize + /// Returns the maximum size for an element + internal static Size GetMaximumSize(IArrangedElement element, Size defaultMaximumSize) { + bool found; + Size size = element.Properties.GetSize(_maximumSizeProperty, out found); + if (found) { + return size; + } + return defaultMaximumSize; + } + + + /// GetMinimumSize + /// Returns the minimum size for an element + internal static Size GetMinimumSize(IArrangedElement element, Size defaultMinimumSize) { + bool found; + Size size = element.Properties.GetSize(_minimumSizeProperty, out found); + if (found) { + return size; + } + return defaultMinimumSize; + } + + /// GetPadding + /// Returns the padding for an element + /// Typically the padding is accounted for in either the DisplayRectangle calculation + /// and/or the GetPreferredSize calculation of a control. + /// + /// NOTE: LayoutEngines should never read this property. Padding gets incorperated into + /// layout by modifying what the control reports for preferred size. + internal static Padding GetPadding(IArrangedElement element, Padding defaultPadding) { + bool found; + Padding padding = element.Properties.GetPadding(_paddingProperty, out found); + if (found) { + return padding; + } + return defaultPadding; + } + + /// GetSpecifiedBounds + /// Returns the last size manually set into the element. See UpdateSpecifiedBounds. + internal static Rectangle GetSpecifiedBounds(IArrangedElement element) { + bool found; + Rectangle rectangle = element.Properties.GetRectangle(_specifiedBoundsProperty, out found); + if (found && rectangle != LayoutUtils.MaxRectangle) { + return rectangle; + } + return element.Bounds; + } + + /// ResetPadding + /// clears out the padding from the property store + internal static void ResetPadding(IArrangedElement element) { + object value = element.Properties.GetObject(_paddingProperty); + if(value != null) { + element.Properties.RemoveObject(_paddingProperty); + } + } + + + /// SetAutoSize + /// Sets whether or not the layout engines should treat this control as auto sized. + internal static void SetAutoSize(IArrangedElement element, bool value) { + Debug.Assert(value != GetAutoSize(element), "PERF: Caller should guard against setting AutoSize to original value."); + + BitVector32 state = GetLayoutState(element); + state[_autoSizeSection] = value ? 1 : 0; + SetLayoutState(element, state); + if(value == false) { + // If autoSize is being turned off, restore the control to its specified bounds. + element.SetBounds(GetSpecifiedBounds(element), BoundsSpecified.None); + } + + Debug.Assert(GetAutoSize(element) == value, "Error detected setting AutoSize."); + } + + /// SetMargin + /// Sets the margin (exterior space) for an element. + internal static void SetMargin(IArrangedElement element, Padding value) { + Debug.Assert(value != GetMargin(element), "PERF: Caller should guard against setting Margin to original value."); + + element.Properties.SetPadding(_marginProperty, value); + + Debug.Assert(GetMargin(element) == value, "Error detected setting Margin."); + + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.Margin); + + } + + + /// SetMaximumSize + /// Sets the maximum size for an element. + internal static void SetMaximumSize(IArrangedElement element, Size value) { + Debug.Assert(value != GetMaximumSize(element, new Size(-7109, -7107)), + "PERF: Caller should guard against setting MaximumSize to original value."); + + element.Properties.SetSize(_maximumSizeProperty, value); + + // Element bounds may need to truncated to new maximum + // + Rectangle bounds = element.Bounds; + bounds.Width = Math.Min(bounds.Width, value.Width); + bounds.Height = Math.Min(bounds.Height, value.Height); + element.SetBounds(bounds, BoundsSpecified.Size); + + // element.SetBounds does a SetBoundsCore. We still need to explicitly refresh parent layout. + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.MaximumSize); + + Debug.Assert(GetMaximumSize(element, new Size(-7109, -7107)) == value, "Error detected setting MaximumSize."); + } + + /// SetMinimumSize + /// Sets the minimum size for an element. + internal static void SetMinimumSize(IArrangedElement element, Size value) { + Debug.Assert(value != GetMinimumSize(element, new Size(-7109, -7107)), + "PERF: Caller should guard against setting MinimumSize to original value."); + + element.Properties.SetSize(_minimumSizeProperty, value); + + using (new LayoutTransaction(element.Container as Control, element, PropertyNames.MinimumSize)) { + // Element bounds may need to inflated to new minimum + // + Rectangle bounds = element.Bounds; + bounds.Width = Math.Max(bounds.Width, value.Width); + bounds.Height = Math.Max(bounds.Height, value.Height); + element.SetBounds(bounds, BoundsSpecified.Size); + } + + Debug.Assert(GetMinimumSize(element, new Size(-7109, -7107)) == value, "Error detected setting MinimumSize."); + } + + + /// SetPadding + /// Sets the padding (interior space) for an element. See GetPadding for more detiails. + /// NOTE: It is the callers responsibility to do layout. See Control.Padding for details. + internal static void SetPadding(IArrangedElement element, Padding value) { + Debug.Assert(value != GetPadding(element, new Padding(-7105)), + "PERF: Caller should guard against setting Padding to original value."); + + value = LayoutUtils.ClampNegativePaddingToZero(value); + element.Properties.SetPadding(_paddingProperty, value); + + + Debug.Assert(GetPadding(element, new Padding(-7105)) == value, "Error detected setting Padding."); + } + + + /// UpdateSpecifiedBounds + /// The main purpose of this function is to remember what size someone specified in the Size, Width, Height, Bounds + /// property. (Its the whole reason the BoundsSpecified enum exists.) Consider this scenario. You set a Button + /// to DockStyle.Fill, then DockStyle.None. When Dock.Filled, the Size changed to 300,300. When you + /// set it back to DockStyle.None, the size switches back to 100,23. How does this happen? + /// + /// Setting the control to Dock.Fill (via DefaultLayout engine) + /// element.SetBounds(newElementBounds, BoundsSpecified.None); + /// + /// (If someone happens to set the Size property here the specified bounds gets updated via Control.Size) + /// SetBounds(x, y, value.Width, value.Height, BoundsSpecified.Size); + /// + /// Setting the control to Dock.None (via DefaultLayout.SetDock) + /// element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.None); + internal static void UpdateSpecifiedBounds(IArrangedElement element, int x, int y, int width, int height, BoundsSpecified specified) { + Rectangle originalBounds = CommonProperties.GetSpecifiedBounds(element); + + // PERF note: Bitwise operator usage intentional to optimize out branching. + + bool xChangedButNotSpecified = ((specified & BoundsSpecified.X) == BoundsSpecified.None) & x != originalBounds.X; + bool yChangedButNotSpecified = ((specified & BoundsSpecified.Y) == BoundsSpecified.None) & y != originalBounds.Y; + bool wChangedButNotSpecified = ((specified & BoundsSpecified.Width) == BoundsSpecified.None) & width != originalBounds.Width; + bool hChangedButNotSpecified = ((specified & BoundsSpecified.Height) == BoundsSpecified.None) & height != originalBounds.Height; + + if(xChangedButNotSpecified | yChangedButNotSpecified | wChangedButNotSpecified | hChangedButNotSpecified) { + // if any of them are changed and specified cache the new value. + + if (!xChangedButNotSpecified) originalBounds.X = x; + if (!yChangedButNotSpecified) originalBounds.Y = y; + if (!wChangedButNotSpecified) originalBounds.Width = width; + if (!hChangedButNotSpecified) originalBounds.Height = height; + + element.Properties.SetRectangle(_specifiedBoundsProperty, originalBounds); + + } else { + // SetBoundsCore is going to call this a lot with the same bounds. Avoid the set object + // (which indirectly may causes an allocation) if we can. + if(element.Properties.ContainsObject(_specifiedBoundsProperty)) { + // use MaxRectangle instead of null so we can reuse the SizeWrapper in the property store. + element.Properties.SetRectangle(_specifiedBoundsProperty, LayoutUtils.MaxRectangle); + } + } + } + + // Used by ToolStripControlHost.Size. + internal static void UpdateSpecifiedBounds(IArrangedElement element, int x, int y, int width, int height) { + Rectangle bounds = new Rectangle(x, y, width, height); + element.Properties.SetRectangle(_specifiedBoundsProperty, bounds); + } + + + + /// xClearPreferredSizeCache + /// clears the preferred size cached for any control that overrides + /// the internal GetPreferredSizeCore method. DO NOT CALL DIRECTLY + /// unless it is understood how the size of the control is going to be updated. + /// + + + internal static void xClearPreferredSizeCache(IArrangedElement element) { + element.Properties.SetSize(_preferredSizeCacheProperty, LayoutUtils.InvalidSize); +#if DEBUG + Debug_ClearProperties(element); +#endif + + Debug.Assert(xGetPreferredSizeCache(element) == Size.Empty, "Error detected in xClearPreferredSizeCache."); + } + + /// xClearAllPreferredSizeCaches + /// clears all the caching for an IArrangedElement hierarchy + /// typically done in dispose. + internal static void xClearAllPreferredSizeCaches(IArrangedElement start) { + CommonProperties.xClearPreferredSizeCache(start); + + ArrangedElementCollection controlsCollection = start.Children; + // This may have changed the sizes of our children. + // PERFNOTE: This is more efficient than using Foreach. Foreach + // forces the creation of an array subset enum each time we + // enumerate + for(int i = 0; i < controlsCollection.Count; i++) { + xClearAllPreferredSizeCaches(controlsCollection[i]); + } + } + + /// xGetPreferredSizeCache + /// This value is the cached result of the return value from + /// a control's GetPreferredSizeCore implementation when asked + /// for a constraining value of LayoutUtils.MaxValue (or Size.Empty too). + internal static Size xGetPreferredSizeCache(IArrangedElement element) { + bool found; + Size size = element.Properties.GetSize(_preferredSizeCacheProperty, out found); + if (found && (size != LayoutUtils.InvalidSize)) { + return size; + } + return Size.Empty; + } + + /// xSetPreferredSizeCache + /// Sets a control's preferred size. See xGetPreferredSizeCache. + internal static void xSetPreferredSizeCache(IArrangedElement element, Size value) { + Debug.Assert(value == Size.Empty || value != xGetPreferredSizeCache(element), "PERF: Caller should guard against setting PreferredSizeCache to original value."); +#if DEBUG + Debug_SnapProperties(element); +#endif + element.Properties.SetSize(_preferredSizeCacheProperty, value); + Debug.Assert(xGetPreferredSizeCache(element) == value, "Error detected in xGetPreferredSizeCache."); + } + +#endregion + +#region DockAndAnchorLayoutSpecific + + /// GetAutoSizeMode + /// Returns whether or not a control should snap to its smallest size + /// or retain its original size and only grow if the preferred size is larger. + /// We tried not having GrowOnly as the default, but it becomes difficult + /// to design panels or have Buttons maintain their default size of 100,23 + internal static AutoSizeMode GetAutoSizeMode(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + return state[_autoSizeModeSection] == 0 ? AutoSizeMode.GrowOnly : AutoSizeMode.GrowAndShrink; + } + + /// GetNeedsDockAndAnchorLayout + /// Do not use. Internal property for DockAndAnchor layout. + /// Returns true if DefaultLayout needs to do any work for this element. + /// (Returns false if the element is purely absolutely positioned) + internal static bool GetNeedsDockAndAnchorLayout(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + bool result = state[_dockAndAnchorNeedsLayoutSection] != 0; + + Debug.Assert( + (xGetAnchor(element) == DefaultAnchor + && xGetDock(element) == DefaultDock + && GetAutoSize(element) == DefaultAutoSize) != result, + "Individual values of Anchor/Dock/AutoRelocate/Autosize contradict GetNeedsDockAndAnchorLayout()."); + + return result; + } + + /// GetNeedsAnchorLayout + /// Do not use. Internal property for DockAndAnchor layout. + /// Returns true if DefaultLayout needs to do anchoring for this element. + internal static bool GetNeedsAnchorLayout(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + bool result = (state[_dockAndAnchorNeedsLayoutSection] != 0) && (state[_dockModeSection] == (int) DockAnchorMode.Anchor); + + Debug.Assert( + (xGetAnchor(element) != DefaultAnchor + || (GetAutoSize(element) != DefaultAutoSize && xGetDock(element) == DockStyle.None)) == result, + "Individual values of Anchor/Dock/AutoRelocate/Autosize contradict GetNeedsAnchorLayout()."); + + return result; + } + + /// GetNeedsDockLayout + /// Do not use. Internal property for DockAndAnchor layout. + /// Returns true if DefaultLayout needs to do docking for this element. + internal static bool GetNeedsDockLayout(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + bool result = state[_dockModeSection] == (int) DockAnchorMode.Dock && element.ParticipatesInLayout; + + Debug.Assert(((xGetDock(element) != DockStyle.None) && element.ParticipatesInLayout) == result, + "Error detected in GetNeedsDockLayout()."); + + return result; + } + + /// GetSelfAutoSize + /// Compat flag for controls that previously sized themselves. + /// Some controls rolled their own implementation of AutoSize in V1 for Dock & Anchor + /// In V2, the LayoutEngine is the one responsible for sizing the child items when + /// they're AutoSized. For new layout engines, the controls will let the layout engine + /// size them, but for DefaultLayout, they're left to size themselves. + internal static bool GetSelfAutoSizeInDefaultLayout(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + int value = state[_selfAutoSizingSection]; + return (value == 1); + } + + + /// SetAutoSizeMode + /// Returns whether or not a control should snap to its smallest size + /// or retain its original size and only grow if the preferred size is larger. + /// We tried not having GrowOnly as the default, but it becomes difficult + /// to design panels or have Buttons maintain their default size of 100,23 + internal static void SetAutoSizeMode(IArrangedElement element, AutoSizeMode mode) { + BitVector32 state = GetLayoutState(element); + state[_autoSizeModeSection] = mode == AutoSizeMode.GrowAndShrink ? 1 : 0; + SetLayoutState(element, state); + } + + /// ShouldSelfSize + /// Compat flag for controls that previously sized themselves. + /// See GetSelfAutoSize comments. + internal static bool ShouldSelfSize(IArrangedElement element) { + if (GetAutoSize(element)) { + // check for legacy layout engine + if (element.Container is Control) { + if (((Control)element.Container).LayoutEngine is DefaultLayout) { + return GetSelfAutoSizeInDefaultLayout(element); + } + } + // else + // - unknown element type + // - new LayoutEngine which should set the size to the preferredSize anyways. + return false; + } + // autosize false things should selfsize. + return true; + } + + /// SetSelfAutoSizeInDefaultLayout + /// Compat flag for controls that previously sized themselves. + /// See GetSelfAutoSize comments. + internal static void SetSelfAutoSizeInDefaultLayout(IArrangedElement element, bool value) { + Debug.Assert(value != GetSelfAutoSizeInDefaultLayout(element), "PERF: Caller should guard against setting AutoSize to original value."); + + BitVector32 state = GetLayoutState(element); + state[_selfAutoSizingSection] = value ? 1 : 0; + SetLayoutState(element, state); + + Debug.Assert(GetSelfAutoSizeInDefaultLayout(element) == value, "Error detected setting AutoSize."); + } + + + /// xGetAnchor - + /// Do not use this. Use DefaultLayout.GetAnchor. + /// NOTE that Dock and Anchor are exclusive, so we store their enums in the same section. + internal static AnchorStyles xGetAnchor(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + AnchorStyles value = (AnchorStyles) state[_dockAndAnchorSection]; + DockAnchorMode mode = (DockAnchorMode) state[_dockModeSection]; + + // If we are docked, or if it the value is 0, we return DefaultAnchor + value = mode == DockAnchorMode.Anchor ? xTranslateAnchorValue(value) : DefaultAnchor; + + Debug.Assert(mode == DockAnchorMode.Anchor || value == DefaultAnchor, "xGetAnchor needs to return DefaultAnchor when docked."); + return value; + } + + /// xGetAutoSizedAndAnchored - + /// Do not use. Internal property for DockAndAnchor layout. + /// Returns true if the element is both AutoSized and Anchored. + internal static bool xGetAutoSizedAndAnchored(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + + if (state[_selfAutoSizingSection] != 0) { + return false; + } + bool result = (state[_autoSizeSection] != 0) && (state[_dockModeSection] == (int) DockAnchorMode.Anchor); + Debug.Assert(result == (GetAutoSize(element) && xGetDock(element) == DockStyle.None), + "Error detected in xGetAutoSizeAndAnchored."); + + return result; + } + + /// xGetDock + /// Do not use this. Use DefaultLayout.GetDock. + /// Note that Dock and Anchor are exclusive, so we store their enums in the same section. + internal static DockStyle xGetDock(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + DockStyle value = (DockStyle) state[_dockAndAnchorSection]; + DockAnchorMode mode = (DockAnchorMode) state[_dockModeSection]; + + // If we are anchored we return DefaultDock + value = mode == DockAnchorMode.Dock ? value : DefaultDock; + Debug.Assert(ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill), "Illegal value returned form xGetDock."); + + Debug.Assert(mode == DockAnchorMode.Dock || value == DefaultDock, + "xGetDock needs to return the DefaultDock style when not docked."); + + return value; + } + + + + /// xSetAnchor - + /// Do not use this. Use DefaultLayout.SetAnchor. + /// Note that Dock and Anchor are exclusive, so we store their enums in the same section. + internal static void xSetAnchor(IArrangedElement element, AnchorStyles value) { + Debug.Assert(value != xGetAnchor(element), "PERF: Caller should guard against setting Anchor to original value."); + + BitVector32 state = GetLayoutState(element); + + // We translate DefaultAnchor to zero - see the _dockAndAnchorNeedsLayoutSection section above. + state[_dockAndAnchorSection] = (int) xTranslateAnchorValue(value); + state[_dockModeSection] = (int) DockAnchorMode.Anchor; + + SetLayoutState(element, state); + + Debug.Assert(xGetAnchor(element) == value, "Error detected setting Anchor."); + Debug.Assert(GetLayoutState(element)[_dockModeSection] == (int) DockAnchorMode.Anchor, + "xSetAnchor did not set mode to Anchor."); + } + + /// xSetDock + /// Do not use this. Use DefaultLayout.SetDock. + /// Note that Dock and Anchor are exclusive, so we store their enums in the same section. + internal static void xSetDock(IArrangedElement element, DockStyle value) { + Debug.Assert(value != xGetDock(element), "PERF: Caller should guard against setting Dock to original value."); + Debug.Assert(ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill), "Illegal value passed to xSetDock."); + + BitVector32 state = GetLayoutState(element); + + state[_dockAndAnchorSection] = (int) value; // See xTranslateAnchorValue for why this works with Dock.None. + state[_dockModeSection] = (int) (value == DockStyle.None ? DockAnchorMode.Anchor : DockAnchorMode.Dock); + + SetLayoutState(element, state); + + Debug.Assert(xGetDock(element) == value, "Error detected setting Dock."); + Debug.Assert((GetLayoutState(element)[_dockModeSection] == (int) DockAnchorMode.Dock) + == (value != DockStyle.None), "xSetDock set DockMode incorrectly."); + } + + /// xTranslateAnchorValue - + /// Helper method for xGetAnchor / xSetAnchor. + /// We store anchor DefualtAnchor as None and vice versa. + /// We either had to do this or map Dock.None to DefaultAnchor (Dock & Anchor share the same section + /// in LayoutState.) Mapping DefaultAnchor to 0 is nicer because we do not need to allocate anything in + /// the PropertyStore to get a 0 back from PropertyStore.GetInteger(). + private static AnchorStyles xTranslateAnchorValue(AnchorStyles anchor) { + switch(anchor) { + case AnchorStyles.None: + return DefaultAnchor; + case DefaultAnchor: + return AnchorStyles.None; + } + return anchor; + } + +#endregion + +#region FlowLayoutSpecific + // + + + + + internal static bool GetFlowBreak(IArrangedElement element) { + BitVector32 state = GetLayoutState(element); + int value = state[_flowBreakSection]; + return value == 1; + } + + + /// SetFlowBreak + /// Use FlowLayoutSettings.SetFlowBreak instead. + /// See GetFlowBreak. + internal static void SetFlowBreak(IArrangedElement element, bool value) { + Debug.Assert(value != GetFlowBreak(element), "PERF: Caller should guard against setting FlowBreak to original value."); + + BitVector32 state = GetLayoutState(element); + state[_flowBreakSection] = value ? 1 : 0; + SetLayoutState(element, state); + + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.FlowBreak); + + Debug.Assert(GetFlowBreak(element) == value, "Error detected setitng SetFlowBreak."); + } +#endregion +#region AutoScrollSpecific + + /// GetLayoutBounds - + /// This is the size used to determine whether or not we need scrollbars. + /// + /// Used if the layoutengine always want to return the same layout bounds regardless + /// of how it lays out. Example is TLP in RTL and LTR. VSWhidbey# 392913 + internal static Size GetLayoutBounds(IArrangedElement element) { + bool found; + Size size = element.Properties.GetSize(_layoutBoundsProperty, out found); + if (found) { + return size; + } + return Size.Empty; + } + + + /// SetLayoutBounds - + /// This is the size used to determine whether or not we need scrollbars. + /// + /// The TableLayout engine now calls CommonProperties.SetLayoutBounds when + /// it is done with its layout. The layoutbounds are the total column width + /// and the total row height. ScrollableControl checks if the LayoutBounds + /// has been set in the CommonProperties when it tries to figure out if it + /// should add scrollbars - but only if the layout engine is not the default + /// layout engine. If the bounds has been set, ScrollableControl will use + /// those bounds to check if scrollbars should be added, rather than doing + /// its own magic to figure it out. + internal static void SetLayoutBounds(IArrangedElement element, Size value) { + element.Properties.SetSize(_layoutBoundsProperty, value); + } + + + /// HasLayoutBounds - + /// Returns whether we have layout bounds stored for this element. + internal static bool HasLayoutBounds(IArrangedElement element) { + bool found; + element.Properties.GetSize(_layoutBoundsProperty, out found); + return found; + } + +#endregion +#region InternalCommonPropertiesHelpers + + /// GetLayoutState - returns the layout state bit vector from the property store. + /// CAREFUL: this is a copy of the state. You need to SetLayoutState() to save your changes. + /// + internal static BitVector32 GetLayoutState(IArrangedElement element) { + return new BitVector32(element.Properties.GetInteger(_layoutStateProperty)); + } + + internal static void SetLayoutState(IArrangedElement element, BitVector32 state) { + element.Properties.SetInteger(_layoutStateProperty, state.Data); + } +#endregion + +#region DebugHelpers +#if DEBUG + + + internal static readonly TraceSwitch PreferredSize = new TraceSwitch("PreferredSize", "Debug preferred size assertion"); + + internal static string Debug_GetChangedProperties(IArrangedElement element) { + string diff = ""; + if (PreferredSize.TraceVerbose) { + Hashtable propertyHash = element.Properties.GetObject(_lastKnownStateProperty) as Hashtable; + if(propertyHash != null) { + foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(element)) { + if (propertyHash.ContainsKey(pd.Name) && (propertyHash[pd.Name].ToString() != pd.Converter.ConvertToString(pd.GetValue(element)))) { + diff += "Prop [ " + pd.Name + "] OLD [" + propertyHash[pd.Name] + "] NEW [" + pd.Converter.ConvertToString(pd.GetValue(element)) + "]\r\n"; + } + } + } + } + else { + diff = "For more info, try enabling PreferredSize trace switch"; + } + return diff; + + } + + internal static void Debug_SnapProperties(IArrangedElement element) { + // DEBUG - store off the old state so we can figure out what has changed in a GPS assert + element.Properties.SetObject(_lastKnownStateProperty, Debug_GetCurrentPropertyState(element)); + } + internal static void Debug_ClearProperties(IArrangedElement element) { + // DEBUG - clear off the old state so we can figure out what has changed in a GPS assert + element.Properties.SetObject(_lastKnownStateProperty, null); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static Hashtable Debug_GetCurrentPropertyState(object obj) { + + Hashtable propertyHash = new Hashtable(); + if (PreferredSize.TraceVerbose) { + + foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(obj)) { + if (pd.Name == "PreferredSize") { + continue; // avoid accidentally forcing a call to GetPreferredSize + } + try { + if (pd.IsBrowsable && !pd.IsReadOnly && pd.SerializationVisibility != DesignerSerializationVisibility.Hidden) { + propertyHash[pd.Name] = pd.Converter.ConvertToString(pd.GetValue(obj)); + } + } + catch { + } + } + } + return propertyHash; + + } + + +#endif +#endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/DockAndAnchorLayout.cs b/WindowsForms/Managed/System/WinForms/Layout/DockAndAnchorLayout.cs new file mode 100644 index 000000000..23dc03e71 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/DockAndAnchorLayout.cs @@ -0,0 +1,1013 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + + using ControlCollection = System.Windows.Forms.Control.ControlCollection; + using CompModSwitches = System.ComponentModel.CompModSwitches; + + internal class DefaultLayout : LayoutEngine { + + // Singleton instance shared by all controls. + internal static readonly DefaultLayout Instance = new DefaultLayout(); + + private static readonly int _layoutInfoProperty = PropertyStore.CreateKey(); + private static readonly int _cachedBoundsProperty = PropertyStore.CreateKey(); + + // Loop through the AutoSized controls and expand them if they are smaller than + // their preferred size. If expanding the controls causes overlap, bump the overlapped + // control if it is AutoRelocatable. + private static void LayoutAutoSizedControls(IArrangedElement container) { + ArrangedElementCollection children = container.Children; + for (int i = children.Count - 1; i >= 0; i--) { + IArrangedElement element = children[i]; + + if(CommonProperties.xGetAutoSizedAndAnchored(element)) { + Rectangle bounds = GetCachedBounds(element); + + AnchorStyles anchor = GetAnchor(element); + Size proposedConstraints = LayoutUtils.MaxSize; + + if ((anchor & (AnchorStyles.Left | AnchorStyles.Right)) == (AnchorStyles.Left | AnchorStyles.Right)) { + proposedConstraints.Width = bounds.Width; + } + if ((anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) { + proposedConstraints.Height = bounds.Height; + } + Size prefSize = element.GetPreferredSize(proposedConstraints); + + Rectangle newBounds = bounds; + + if (CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink) { + // this is the case for simple things like radio button, checkbox, etc. + newBounds = GetGrowthBounds(element, prefSize); + } + else { + // we had whacked this check, but it turns out it causes undesirable + // behavior in things like panel. a panel with no elements sizes to 0,0. + if(bounds.Width < prefSize.Width || bounds.Height < prefSize.Height) { + Size newSize = LayoutUtils.UnionSizes(bounds.Size, prefSize); + newBounds = GetGrowthBounds(element, newSize); + } + } + if (newBounds != bounds) { + SetCachedBounds(element, newBounds); + } + } + } + } + + // Gets the bounds of the element after growing to newSize (note that depending on + // anchoring the element may grow to the left/updwards rather than to the + // right/downwards. i.e., it may be translated.) + private static Rectangle GetGrowthBounds(IArrangedElement element, Size newSize) { + GrowthDirection direction = GetGrowthDirection(element); + Rectangle oldBounds = GetCachedBounds(element); + Point location = oldBounds.Location; + + Debug.Assert((CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink || newSize.Height >= oldBounds.Height && newSize.Width >= oldBounds.Width), + "newSize expected to be >= current size."); + + if((direction & GrowthDirection.Left) != GrowthDirection.None) { + // We are growing towards the left, translate X + location.X -= newSize.Width - oldBounds.Width; + } + + if((direction & GrowthDirection.Upward) != GrowthDirection.None) { + // We are growing towards the top, translate Y + location.Y -= newSize.Height - oldBounds.Height; + } + + Rectangle newBounds = new Rectangle(location, newSize); + + Debug.Assert((CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink || newBounds.Contains(oldBounds)), "How did we resize in such a way we no longer contain our old bounds?"); + + return newBounds; + } + // Examines an elements anchoring to figure out which direction it should grow. + private static GrowthDirection GetGrowthDirection(IArrangedElement element) { + AnchorStyles anchor = GetAnchor(element); + GrowthDirection growthDirection = GrowthDirection.None; + + if((anchor & AnchorStyles.Right) != AnchorStyles.None + && (anchor & AnchorStyles.Left) == AnchorStyles.None) { + // element is anchored to the right, but not the left. + growthDirection |= GrowthDirection.Left; + } else { + // otherwise we grow towards the right (common case) + growthDirection |= GrowthDirection.Right; + } + + if((anchor & AnchorStyles.Bottom) != AnchorStyles.None + && (anchor & AnchorStyles.Top) == AnchorStyles.None) { + // element is anchored to the bottom, but not the top. + growthDirection |= GrowthDirection.Upward; + } else { + // otherwise we grow towards the bottom. (common case) + growthDirection |= GrowthDirection.Downward; + } + + Debug.Assert((growthDirection & GrowthDirection.Left) == GrowthDirection.None + || (growthDirection & GrowthDirection.Right) == GrowthDirection.None, + "We shouldn't allow growth to both the left and right."); + Debug.Assert((growthDirection & GrowthDirection.Upward) == GrowthDirection.None + || (growthDirection & GrowthDirection.Downward) == GrowthDirection.None, + "We shouldn't allow both upward and downward growth."); + return growthDirection; + } + + // Layout for a single anchored control. There's no order dependency when laying out anchored controls. + private static Rectangle GetAnchorDestination(IArrangedElement element, Rectangle displayRect, bool measureOnly) { + // Container can not be null since we AschorControls takes a non-null container. + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t'" + element + "' is anchored at " + GetCachedBounds(element).ToString()); + + AnchorInfo layout = GetAnchorInfo(element); + + int left = layout.Left + displayRect.X; + int top = layout.Top + displayRect.Y; + int right = layout.Right + displayRect.X; + int bottom = layout.Bottom + displayRect.Y; + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...anchor dim (l,t,r,b) {" + + (left) + + ", " + (top) + + ", " + (right) + + ", " + (bottom) + + "}"); + + AnchorStyles anchor = GetAnchor(element); + + if (IsAnchored(anchor, AnchorStyles.Right)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting right"); + right += displayRect.Width; + + if (!IsAnchored(anchor, AnchorStyles.Left)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left"); + left += displayRect.Width; + } + } + else if (!IsAnchored(anchor, AnchorStyles.Left)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left & right"); + right += (displayRect.Width / 2); + left += (displayRect.Width / 2); + } + + if (IsAnchored(anchor, AnchorStyles.Bottom)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting bottom"); + bottom += displayRect.Height; + + if (!IsAnchored(anchor, AnchorStyles.Top)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top"); + top += displayRect.Height; + } + } + else if (!IsAnchored(anchor, AnchorStyles.Top)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top & bottom"); + bottom += (displayRect.Height/2); + top += (displayRect.Height/2); + } + + if (!measureOnly) { + // the size is actually zero, set the width and heights appropriately. + if (right < left) right = left; + if (bottom < top) bottom = top; + } + else { + Rectangle cachedBounds = GetCachedBounds(element); + // in this scenario we've likely been passed a 0 sized display rectangle to determine our height. + // we will need to translate the right and bottom edges as necessary to the positive plane. + + // right < left means the control is anchored both left and right. + // cachedBounds != element.Bounds means the element's size has changed + // any, all, or none of these can be true. + if (right < left || cachedBounds.Width != element.Bounds.Width || cachedBounds.X != element.Bounds.X) { + if (cachedBounds != element.Bounds) { + left = Math.Max(Math.Abs(left), Math.Abs(cachedBounds.Left)); + } + right = left + Math.Max(element.Bounds.Width, cachedBounds.Width) + Math.Abs(right); + } + else { + left = left > 0 ? left : element.Bounds.Left; + right = right > 0 ? right : element.Bounds.Right + Math.Abs(right); + } + + // bottom < top means the control is anchored both top and bottom. + // cachedBounds != element.Bounds means the element's size has changed + // any, all, or none of these can be true. + if (bottom < top || cachedBounds.Height != element.Bounds.Height || cachedBounds.Y != element.Bounds.Y) { + if (cachedBounds != element.Bounds) { + top = Math.Max(Math.Abs(top), Math.Abs(cachedBounds.Top)); + } + bottom = top + Math.Max(element.Bounds.Height, cachedBounds.Height) + Math.Abs(bottom); + } + else { + top = top > 0 ? top : element.Bounds.Top; + bottom = bottom > 0 ? bottom : element.Bounds.Bottom + Math.Abs(bottom); + } + } + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...new anchor dim (l,t,r,b) {" + + (left) + + ", " + (top) + + ", " + (right) + + ", " + (bottom) + + "}"); + + return new Rectangle(left, top, right - left, bottom - top); + } + + private static void LayoutAnchoredControls(IArrangedElement container) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tAnchor Processing"); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\tdisplayRect: " + container.DisplayRectangle.ToString()); + + Rectangle displayRectangle = container.DisplayRectangle; + if (CommonProperties.GetAutoSize(container) && ((displayRectangle.Width == 0) || (displayRectangle.Height == 0))) { + // we havent set oursleves to the preferred size yet. proceeding will + // just set all the control widths to zero. let's return here + return; + } + + + ArrangedElementCollection children = container.Children; + for (int i = children.Count - 1; i >= 0; i--) { + IArrangedElement element = children[i]; + + if (CommonProperties.GetNeedsAnchorLayout(element)) { + Debug.Assert(GetAnchorInfo(element) != null, "AnchorInfo should be initialized before LayoutAnchorControls()."); + SetCachedBounds(element, GetAnchorDestination(element, displayRectangle, /*measureOnly=*/false)); + } + } + } + + private static Size LayoutDockedControls(IArrangedElement container, bool measureOnly) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tDock Processing"); + Debug.Assert(!HasCachedBounds(container), + "Do not call this method with an active cached bounds list."); + + // If measuring, we start with an empty rectangle and add as needed. + // If doing actual layout, we start with the container's rect and subtract as we layout. + Rectangle remainingBounds = measureOnly ? Rectangle.Empty : container.DisplayRectangle; + Size preferredSize = Size.Empty; + + IArrangedElement mdiClient = null; + + // Docking layout is order dependent. After much debate, we decided to use z-order as the + // docking order. (Introducing a DockOrder property was a close second) + ArrangedElementCollection children = container.Children; + for (int i = children.Count - 1; i >= 0; i--) { + IArrangedElement element = children[i]; + + Debug.Assert(element.Bounds == GetCachedBounds(element), "Why do we have cachedBounds for a docked element?"); + + if (CommonProperties.GetNeedsDockLayout(element)) { + // VSWhidbey #109974: Some controls modify their bounds when you call SetBoundsCore. We + // therefore need to read the value of bounds back when adjusting our layout rectangle. + switch (GetDock(element)) { + case DockStyle.Top: { + Size elementSize = GetVerticalDockedSize(element, remainingBounds.Size, measureOnly); + Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height); + + xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds); + + // What we are really doing here: top += element.Bounds.Height; + remainingBounds.Y += element.Bounds.Height; + remainingBounds.Height -= element.Bounds.Height; + break; + } + case DockStyle.Bottom: { + Size elementSize = GetVerticalDockedSize(element, remainingBounds.Size, measureOnly); + Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Bottom - elementSize.Height, elementSize.Width, elementSize.Height); + + xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds); + + // What we are really doing here: bottom -= element.Bounds.Height; + remainingBounds.Height -= element.Bounds.Height; + + break; + } + case DockStyle.Left: { + Size elementSize = GetHorizontalDockedSize(element, remainingBounds.Size, measureOnly); + Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height); + + xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds); + + // What we are really doing here: left += element.Bounds.Width; + remainingBounds.X += element.Bounds.Width; + remainingBounds.Width -= element.Bounds.Width; + break; + } + case DockStyle.Right: { + Size elementSize = GetHorizontalDockedSize(element, remainingBounds.Size, measureOnly); + Rectangle newElementBounds = new Rectangle(remainingBounds.Right - elementSize.Width, remainingBounds.Y, elementSize.Width, elementSize.Height); + + xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds); + + // What we are really doing here: right -= element.Bounds.Width; + remainingBounds.Width -= element.Bounds.Width; + break; + } + case DockStyle.Fill: + if (element is MdiClient) { + Debug.Assert(mdiClient == null, "How did we end up with multiple MdiClients?"); + mdiClient = element; + } + else { + Size elementSize = remainingBounds.Size;//GetFillDockedSize(element, remainingBounds.Size, measureOnly); + Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height); + + xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds); + } + break; + default: + Debug.Fail("Unsupported value for dock."); + break; + } + } + + // Treat the MDI client specially, since it's supposed to blend in with the parent form + if (mdiClient != null) { + SetCachedBounds(mdiClient, remainingBounds); + } + } + return preferredSize; + } + + // Helper method that either sets the element bounds or does the preferredSize computation based on + // the value of measureOnly. + private static void xLayoutDockedControl(IArrangedElement element, Rectangle newElementBounds, bool measureOnly, ref Size preferredSize, ref Rectangle remainingBounds) { + if (measureOnly) { + Size neededSize = new Size( + Math.Max(0, newElementBounds.Width - remainingBounds.Width), + Math.Max(0, newElementBounds.Height - remainingBounds.Height)); + + DockStyle dockStyle = GetDock(element); + if ((dockStyle == DockStyle.Top) || (dockStyle == DockStyle.Bottom)) { + neededSize.Width = 0; + } + if ((dockStyle == DockStyle.Left) || (dockStyle == DockStyle.Right)) { + neededSize.Height = 0; + } + if (dockStyle != DockStyle.Fill) { + preferredSize += neededSize; + remainingBounds.Size += neededSize; + } + else if(dockStyle == DockStyle.Fill && CommonProperties.GetAutoSize(element)) { + Size elementPrefSize = element.GetPreferredSize(neededSize); + remainingBounds.Size += elementPrefSize; + preferredSize += elementPrefSize; + } + } else { + element.SetBounds(newElementBounds, BoundsSpecified.None); + +#if DEBUG + Control control = element as Control; + newElementBounds.Size = control.ApplySizeConstraints(newElementBounds.Size); + // This usually happens when a Control overrides its SetBoundsCore or sets size during OnResize + // to enforce constraints like AutoSize. Generally you can just move this code to Control.GetAdjustedSize + // and then PreferredSize will also pick up these constraints. See ComboBox as an example. + // + if (CommonProperties.GetAutoSize(element) && !CommonProperties.GetSelfAutoSizeInDefaultLayout(element)) { + Debug.Assert( + (newElementBounds.Width < 0 || element.Bounds.Width == newElementBounds.Width) && + (newElementBounds.Height < 0 || element.Bounds.Height == newElementBounds.Height), + "Element modified its bounds during docking -- PreferredSize will be wrong. See comment near this assert."); + } +#endif + } + } + + private static Size GetVerticalDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) { + Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ new Size(remainingSize.Width, 1), measureOnly); + + if (!measureOnly) { + newSize.Width = remainingSize.Width; + } else { + newSize.Width = Math.Max(newSize.Width, remainingSize.Width); + } + + Debug.Assert((measureOnly && (newSize.Width >= remainingSize.Width)) || (newSize.Width == remainingSize.Width), + "Error detected in GetVerticalDockedSize: Dock size computed incorrectly during layout."); + + return newSize; + } + + private static Size GetHorizontalDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) { + Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ new Size(1, remainingSize.Height), measureOnly); + + if (!measureOnly) { + newSize.Height = remainingSize.Height; + } else { + newSize.Height = Math.Max(newSize.Height, remainingSize.Height); + } + + Debug.Assert((measureOnly && (newSize.Height >= remainingSize.Height)) || (newSize.Height == remainingSize.Height), + "Error detected in GetHorizontalDockedSize: Dock size computed incorrectly during layout."); + + return newSize; + } + + //Unused + //private static Size GetFillDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) { + // Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ remainingSize, measureOnly); + // newSize = LayoutUtils.UnionSizes(newSize, remainingSize); + + // if (!measureOnly) { + // newSize = LayoutUtils.IntersectSizes(newSize, remainingSize); + // } + + // Debug.Assert(newSize.Width >= remainingSize.Width && newSize.Height >= remainingSize.Height, + // "Error detected in GetFillDockedSize: Element did not stretch to remaning size."); + // Debug.Assert(measureOnly || (newSize.Width == remainingSize.Width && newSize.Height == remainingSize.Height), + // "Error detected in GetFillDockedSize: Dock size computed incorrectly during layout."); + + // return newSize; + //} + + private static Size xGetDockedSize(IArrangedElement element, Size remainingSize, Size constraints, bool measureOnly) { + Size desiredSize; + + if (CommonProperties.GetAutoSize(element)) { + // Ask control for its desired size using the provided constraints. + // (e.g., a control docked to top will constrain width to remaining width + // and minimize height.) + desiredSize = element.GetPreferredSize(constraints); + } else { + desiredSize = element.Bounds.Size; + } + + Debug.Assert((desiredSize.Width >= 0 && desiredSize.Height >= 0), "Error detected in xGetDockSize: Element size was negative."); + return desiredSize; + } + + internal override bool LayoutCore(IArrangedElement container, LayoutEventArgs args) { + Size garbage; // PreferredSize is garbage if we are not measuring. + return xLayout(container, /* measureOnly = */ false, out garbage); + } + + // Note: PreferredSize is only computed if measureOnly = true. + private static bool xLayout(IArrangedElement container, bool measureOnly, out Size preferredSize) { + /* + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, + string.Format("{1}(hwnd=0x{0:X}).OnLayout", + (container.IsHandleCreated ? container.Handle : IntPtr.Zero), + container.GetType().FullName)); + */ + + ArrangedElementCollection children = container.Children; + preferredSize = new Size(-7103, -7105); // PreferredSize is garbage unless measureOnly is specified + + // short circuit for items with no children + if (!measureOnly && children.Count == 0) { + return CommonProperties.GetAutoSize(container); + } + + bool dock = false; + bool anchor = false; + bool autoSize = false; + + + for (int i = children.Count - 1; i >= 0; i--) { + IArrangedElement element = children[i]; + + if (CommonProperties.GetNeedsDockAndAnchorLayout(element)) { + if (!dock && CommonProperties.GetNeedsDockLayout(element)) dock = true; + if (!anchor && CommonProperties.GetNeedsAnchorLayout(element)) anchor = true; + if (!autoSize && CommonProperties.xGetAutoSizedAndAnchored(element)) autoSize = true; + } + } + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tanchor : " + anchor.ToString()); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tdock : " + dock.ToString()); + + Size preferredSizeForDocking = Size.Empty; + Size preferredSizeForAnchoring = Size.Empty; + + if (dock) preferredSizeForDocking = LayoutDockedControls(container, measureOnly); + if (anchor && !measureOnly) { + // in the case of anchor, where we currently are defines the preferred size, + // so dont recalculate the positions of everything. + LayoutAnchoredControls(container); + } + + if (autoSize) LayoutAutoSizedControls(container); + + if (!measureOnly) { + // Set the anchored controls to their computed positions. + ApplyCachedBounds(container); + } + else { + + // Finish the preferredSize computation and clear cached anchored positions. + preferredSizeForAnchoring = GetAnchorPreferredSize(container); + + Padding containerPadding = Padding.Empty; + Control control = container as Control; + if (control != null) { + // calling this will respect Control.DefaultPadding. + containerPadding = control.Padding; + } + else { // not likely to happen but… + containerPadding = CommonProperties.GetPadding(container, Padding.Empty); + } + + preferredSizeForAnchoring.Width -= containerPadding.Left; + preferredSizeForAnchoring.Height -= containerPadding.Top; + + ClearCachedBounds(container); + preferredSize = LayoutUtils.UnionSizes(preferredSizeForDocking, preferredSizeForAnchoring); + + } + return CommonProperties.GetAutoSize(container); + } + + /// + /// Updates the Anchor information based on the controls current bounds. + /// This should only be called when the parent control changes or the + /// anchor mode changes. + /// + private static void UpdateAnchorInfo(IArrangedElement element) { + Debug.Assert(!HasCachedBounds(element.Container), + "Do not call this method with an active cached bounds list."); + + AnchorInfo anchorInfo = GetAnchorInfo(element); + if(anchorInfo == null) { + anchorInfo = new AnchorInfo(); + SetAnchorInfo(element, anchorInfo); + } + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Update anchor info"); + Debug.Indent(); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, element.Container == null ? "No parent" : "Parent"); + + + if (CommonProperties.GetNeedsAnchorLayout(element) && element.Container != null) { + + Rectangle bounds = GetCachedBounds(element); + + AnchorInfo oldAnchorInfo = new AnchorInfo(); + oldAnchorInfo.Left = anchorInfo.Left; + oldAnchorInfo.Top = anchorInfo.Top; + oldAnchorInfo.Right = anchorInfo.Right; + oldAnchorInfo.Bottom = anchorInfo.Bottom; + + anchorInfo.Left = element.Bounds.Left; + anchorInfo.Top = element.Bounds.Top; + anchorInfo.Right = element.Bounds.Right; + anchorInfo.Bottom = element.Bounds.Bottom; + + Rectangle parentDisplayRect = element.Container.DisplayRectangle; + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Parent displayRectangle" + parentDisplayRect); + int parentWidth = parentDisplayRect.Width; + int parentHeight = parentDisplayRect.Height; + + // VS#46140 + // The anchor is relative to the parent DisplayRectangle, so offset the anchor + // by the DisplayRect origin + anchorInfo.Left -= parentDisplayRect.X; + anchorInfo.Top -= parentDisplayRect.Y; + anchorInfo.Right -= parentDisplayRect.X; + anchorInfo.Bottom -= parentDisplayRect.Y; + + AnchorStyles anchor = GetAnchor(element); + if (IsAnchored(anchor, AnchorStyles.Right)) { + if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (anchorInfo.Right - parentWidth > 0) && (oldAnchorInfo.Right < 0)) { + // parent was resized to fit its parent, or screen, we need to reuse old anchor info to prevent losing control beyond right edge + anchorInfo.Right = oldAnchorInfo.Right; + anchorInfo.Left = oldAnchorInfo.Right - bounds.Width; // control might have been resized, update Left anchor + } + else { + anchorInfo.Right -= parentWidth; + + if (!IsAnchored(anchor, AnchorStyles.Left)) { + anchorInfo.Left -= parentWidth; + } + } + } + else if (!IsAnchored(anchor, AnchorStyles.Left)) { + anchorInfo.Right -= (parentWidth/2); + anchorInfo.Left -= (parentWidth/2); + } + + if (IsAnchored(anchor, AnchorStyles.Bottom)) { + if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (anchorInfo.Bottom - parentHeight > 0) && (oldAnchorInfo.Bottom < 0)) { + // parent was resized to fit its parent, or screen, we need to reuse old anchor info to prevent losing control beyond bottom edge + anchorInfo.Bottom = oldAnchorInfo.Bottom; + anchorInfo.Top = oldAnchorInfo.Bottom - bounds.Height; // control might have been resized, update Top anchor + } + else { + anchorInfo.Bottom -= parentHeight; + + if (!IsAnchored(anchor, AnchorStyles.Top)) { + anchorInfo.Top -= parentHeight; + } + } + } + else if (!IsAnchored(anchor, AnchorStyles.Top)) { + anchorInfo.Bottom -= (parentHeight/2); + anchorInfo.Top -= (parentHeight/2); + } + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "anchor info (l,t,r,b): (" + anchorInfo.Left + ", " + anchorInfo.Top + ", " + anchorInfo.Right + ", " + anchorInfo.Bottom + ")"); + } + Debug.Unindent(); + } + + public static AnchorStyles GetAnchor(IArrangedElement element) { + return CommonProperties.xGetAnchor(element); + } + + public static void SetAnchor(IArrangedElement container, IArrangedElement element, AnchorStyles value) { + AnchorStyles oldValue = GetAnchor(element); + if (oldValue != value) { + if(CommonProperties.GetNeedsDockLayout(element)) { + // We set dock back to none to cause the element to size back to its original bounds. + SetDock(element, DockStyle.None); + } + + CommonProperties.xSetAnchor(element, value); + + if (CommonProperties.GetNeedsAnchorLayout(element)) { + UpdateAnchorInfo(element); + } + else { + SetAnchorInfo(element, null); + } + + if (element.Container != null) { + bool rightReleased = IsAnchored(oldValue, AnchorStyles.Right) && !IsAnchored(value, AnchorStyles.Right); + bool bottomReleased = IsAnchored(oldValue, AnchorStyles.Bottom) && !IsAnchored(value, AnchorStyles.Bottom); + if(element.Container.Container != null && (rightReleased || bottomReleased)) { + // If the right or bottom anchor is being released, we have a special case where the element's + // margin may affect preferredSize where it didn't previously. Rather than do an expensive + // check for this in OnLayout, we just detect the case her and force a relayout. + LayoutTransaction.DoLayout(element.Container.Container, element, PropertyNames.Anchor); + } + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.Anchor); + } + } + Debug.Assert(GetAnchor(element) == value, "Error setting Anchor value."); + } + + public static DockStyle GetDock(IArrangedElement element) { + return CommonProperties.xGetDock(element); + } + + public static void SetDock(IArrangedElement element, DockStyle value) { + Debug.Assert(!HasCachedBounds(element.Container), + "Do not call this method with an active cached bounds list."); + + if (GetDock(element) != value) { + //valid values are 0x0 to 0x5 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle)); + } + + bool dockNeedsLayout = CommonProperties.GetNeedsDockLayout(element); + CommonProperties.xSetDock(element, value); + + using (new LayoutTransaction(element.Container as Control, element, PropertyNames.Dock)) { + // VSWHDIBEY 227715 if the item is autosized, calling setbounds performs a layout, which + // if we havent set the anchor info properly yet makes dock/anchor layout cranky. + + if (value == DockStyle.None) { + if (dockNeedsLayout) { + // We are transitioning from docked to not docked, restore the original bounds. + element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.None); + // VSWHIDBEY 159532 restore Anchor information as its now relevant again. + UpdateAnchorInfo(element); + } + } + else { + // Now setup the new bounds. + // + element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.All); + } + } + + } + Debug.Assert(GetDock(element) == value, "Error setting Dock value."); + } + + public static void ScaleAnchorInfo(IArrangedElement element, SizeF factor) { + AnchorInfo anchorInfo = GetAnchorInfo(element); + + // some controls don't have AnchorInfo, i.e. Panels + if (anchorInfo != null) { + anchorInfo.Left = (int)((float)anchorInfo.Left * factor.Width); + anchorInfo.Top = (int)((float)anchorInfo.Top * factor.Height); + anchorInfo.Right = (int)((float)anchorInfo.Right * factor.Width); + anchorInfo.Bottom = (int)((float)anchorInfo.Bottom * factor.Height); + + SetAnchorInfo(element, anchorInfo); + } + } + + private static Rectangle GetCachedBounds(IArrangedElement element) { + if(element.Container != null) { + IDictionary dictionary = (IDictionary) element.Container.Properties.GetObject(_cachedBoundsProperty); + if(dictionary != null) { + object bounds = dictionary[element]; + if(bounds != null) { + return (Rectangle) bounds; + } + } + } + return element.Bounds; + } + + private static bool HasCachedBounds(IArrangedElement container) { + return container != null && container.Properties.GetObject(_cachedBoundsProperty) != null; + } + + private static void ApplyCachedBounds(IArrangedElement container) { + if (CommonProperties.GetAutoSize(container)) { + //Avoiding calling DisplayRectangle before checking AutoSize for Everett compat (VSWhidbey 421433) + Rectangle displayRectangle = container.DisplayRectangle; + if ((displayRectangle.Width == 0) || (displayRectangle.Height == 0)) { + ClearCachedBounds(container); + return; + } + } + + + + IDictionary dictionary = (IDictionary) container.Properties.GetObject(_cachedBoundsProperty); + if(dictionary != null) { +#if DEBUG + // In debug builds, we need to modify the collection, so we add a break and an + // outer loop to prevent attempting to IEnumerator.MoveNext() on a modified + // collection. + while(dictionary.Count > 0) { +#endif + foreach(DictionaryEntry entry in dictionary) { + IArrangedElement element = (IArrangedElement) entry.Key; + + Debug.Assert(element.Container == container,"We have non-children in our containers cached bounds store."); +#if DEBUG + // We are about to set the bounds to the cached value. We clear the cached value + // before SetBounds because some controls fiddle with the bounds on SetBounds + // and will callback InitLayout with a different bounds and BoundsSpecified. + dictionary.Remove(entry.Key); +#endif + Rectangle bounds = (Rectangle) entry.Value; + element.SetBounds(bounds, BoundsSpecified.None); +#if DEBUG + break; + } +#endif + } + ClearCachedBounds(container); + } + } + + private static void ClearCachedBounds(IArrangedElement container) { + container.Properties.SetObject(_cachedBoundsProperty, null); + } + + private static void SetCachedBounds(IArrangedElement element, Rectangle bounds) { + if(bounds != GetCachedBounds(element)) { + IDictionary dictionary = (IDictionary) element.Container.Properties.GetObject(_cachedBoundsProperty); + if (dictionary == null) { + dictionary = new HybridDictionary(); + element.Container.Properties.SetObject(_cachedBoundsProperty, dictionary); + } + dictionary[element] = bounds; + } + } + + private static AnchorInfo GetAnchorInfo(IArrangedElement element) { + return (AnchorInfo) element.Properties.GetObject(_layoutInfoProperty); + } + + private static void SetAnchorInfo(IArrangedElement element, AnchorInfo value) { + element.Properties.SetObject(_layoutInfoProperty, value); + } + + internal override void InitLayoutCore(IArrangedElement element, BoundsSpecified specified) { + Debug.Assert(specified == BoundsSpecified.None || GetCachedBounds(element) == element.Bounds, + "Attempt to InitLayout while element has active cached bounds."); + + if(specified != BoundsSpecified.None && CommonProperties.GetNeedsAnchorLayout(element)) { + UpdateAnchorInfo(element); + } + } + + internal override Size GetPreferredSize(IArrangedElement container, Size proposedBounds) { + Debug.Assert(!HasCachedBounds(container), + "Do not call this method with an active cached bounds list."); + + Size prefSize; + + xLayout(container, /* measureOnly = */ true, out prefSize); +/* container.Bounds = prefSize; + xLayout(container, false, out prefSize); + + // make sure controls are big enough to fit on form if not - increase container size + container.Bounds = newBounds; + + xLayout(container, true, out prefSize);*/ + + return prefSize; + } + + private static Size GetAnchorPreferredSize(IArrangedElement container) { + Size prefSize = Size.Empty; + + ArrangedElementCollection children = container.Children; + for (int i = children.Count - 1; i >= 0; i--) { + IArrangedElement element = container.Children[i]; + if(!CommonProperties.GetNeedsDockLayout(element) && element.ParticipatesInLayout) { + AnchorStyles anchor = GetAnchor(element); + Padding margin = CommonProperties.GetMargin(element); + Rectangle elementSpace = LayoutUtils.InflateRect(GetCachedBounds(element), margin); + + if (IsAnchored(anchor, AnchorStyles.Left) && !IsAnchored(anchor, AnchorStyles.Right)) { + // If we are anchored to the left we make sure the container is large enough not to clip us + // (unless we are right anchored, in which case growing the container will just resize us.) + prefSize.Width = Math.Max(prefSize.Width, elementSpace.Right); + } + + if (/*IsAnchored(anchor, AnchorStyles.Top) &&*/ !IsAnchored(anchor, AnchorStyles.Bottom)) { + // If we are anchored to the top we make sure the container is large enough not to clip us + // (unless we are bottom anchored, in which case growing the container will just resize us.) + prefSize.Height = Math.Max(prefSize.Height, elementSpace.Bottom); + + } + + if (IsAnchored(anchor, AnchorStyles.Right)) { + // If we are right anchored, see what the anchor distance between our right edge and + // the container is, and make sure our container is large enough to accomodate us. + Rectangle anchorDest = GetAnchorDestination(element, Rectangle.Empty, /*measureOnly=*/true); + if (anchorDest.Width < 0) { + prefSize.Width = Math.Max(prefSize.Width, elementSpace.Right + anchorDest.Width); + } + else { + prefSize.Width = Math.Max(prefSize.Width, anchorDest.Right); + } + } + + if (IsAnchored(anchor, AnchorStyles.Bottom)) { + // If we are right anchored, see what the anchor distance between our right edge and + // the container is, and make sure our container is large enough to accomodate us. + Rectangle anchorDest = GetAnchorDestination(element, Rectangle.Empty, /*measureOnly=*/true); + if (anchorDest.Height < 0) { + prefSize.Height = Math.Max(prefSize.Height, elementSpace.Bottom + anchorDest.Height); + } + else { + prefSize.Height = Math.Max(prefSize.Height, anchorDest.Bottom); + } + } + + + } + } + return prefSize; + } + + public static bool IsAnchored(AnchorStyles anchor, AnchorStyles desiredAnchor) { + return (anchor & desiredAnchor) == desiredAnchor; + } + + [Flags] + private enum GrowthDirection { + None = 0, + Upward = 0x01, + Downward = 0x02, + Left = 0x04, + Right = 0x08 + } + + +#if DEBUG_PAINT_ANCHOR + // handy method for drawing out the child anchor infos + internal static void DebugPaintAnchor(Graphics g, Control parent) { + foreach (Control child in parent.Controls) { + + + AnchorInfo layout = GetAnchorInfo(child as IArrangedElement); + Rectangle displayRect = parent.DisplayRectangle; + + if (layout == null) { + continue; + } + int left = layout.Left + displayRect.X; + int top = layout.Top + displayRect.Y; + int right = layout.Right + displayRect.X; + int bottom = layout.Bottom + displayRect.Y; + + AnchorStyles anchor = GetAnchor(child as IArrangedElement); + + // Repeat of GetAnchorDestination + if (IsAnchored(anchor, AnchorStyles.Right)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting right"); + right += displayRect.Width; + + if (!IsAnchored(anchor, AnchorStyles.Left)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left"); + left += displayRect.Width; + } + } + else if (!IsAnchored(anchor, AnchorStyles.Left)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left & right"); + right += (displayRect.Width / 2); + left += (displayRect.Width / 2); + } + + if (IsAnchored(anchor, AnchorStyles.Bottom)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting bottom"); + bottom += displayRect.Height; + + if (!IsAnchored(anchor, AnchorStyles.Top)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top"); + top += displayRect.Height; + } + } + else if (!IsAnchored(anchor, AnchorStyles.Top)) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top & bottom"); + bottom += (displayRect.Height/2); + top += (displayRect.Height/2); + } + + if (IsAnchored(anchor, AnchorStyles.Right)) { + TextRenderer.DrawText(g, "right " + layout.Right.ToString(), parent.Font, new Point(right/2, child.Top - 20), Color.HotPink); + g.FillRectangle(Brushes.HotPink, 0, child.Top -4, right, 1); + } + if (IsAnchored(anchor, AnchorStyles.Left)) { + TextRenderer.DrawText(g, "left " + layout.Left.ToString(), parent.Font, new Point(left/2, child.Top - 16), Color.Green); + g.FillRectangle(Brushes.Green, 0, child.Top -2, left, 1); + } + if (IsAnchored(anchor, AnchorStyles.Top)) { + TextRenderer.DrawText(g, "top " + layout.Top.ToString(), parent.Font, new Point(child.Left -100, top/2), Color.Blue); + + g.FillRectangle(Brushes.Blue, child.Left -1, 0, 1, top); + } + if (IsAnchored(anchor, AnchorStyles.Bottom)) { + TextRenderer.DrawText(g, "bottom " + layout.Bottom.ToString(), parent.Font, new Point(child.Left -100, right/2), Color.Red); + g.FillRectangle(Brushes.Red, child.Left -2, 1, 1, bottom); + } + } + } +#endif + +#if DEBUG_LAYOUT +#if DEBUG + + internal static string Debug_GetAllLayoutInformation(Control start, int indents) { + string info = Debug_GetLayoutInfo(start, indents) + "\r\n"; + + for (int i = 0; i < start.Controls.Count; i++) { + info += Debug_GetIndents(indents) + "+-->" + Debug_GetAllLayoutInformation(start.Controls[i], indents + 1) + "\r\n"; + } + return info; + } + internal static string Debug_GetIndents(int indents) { + string str = ""; + for (int i = 0; i < indents; i++) { + str += "\t"; + } + return str; + } + internal static string Debug_GetLayoutInfo(Control control, int indents) { + string lineBreak = "\r\n" + Debug_GetIndents(indents + 1); + string layoutInfo = string.Format(System.Globalization.CultureInfo.CurrentCulture, + "Handle {9} Name {1} Type {2} {0} Bounds {3} {0} AutoSize {4} {0} Dock [{5}] Anchor [{6}] {0} Padding [{7}] Margin [{8}]", + lineBreak, + control.Name, + control.GetType().Name, + control.Bounds, + control.AutoSize, + control.Dock, + control.Anchor, + control.Padding, + control.Margin, + !control.IsHandleCreated ? "[not created]" : "0x" + ((int)(control.Handle)).ToString("x")); + if (control is TableLayoutPanel) { + TypeConverter converter = TypeDescriptor.GetConverter(typeof(TableLayoutSettings)); + + layoutInfo += lineBreak + converter.ConvertTo(control as TableLayoutPanel, typeof(string)); + } + return layoutInfo; + + } +#endif +#endif + + #region AnchorInfo + private sealed class AnchorInfo { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + #endregion AnchorInfo + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/FlowLayout.cs b/WindowsForms/Managed/System/WinForms/Layout/FlowLayout.cs new file mode 100644 index 000000000..effd352af --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/FlowLayout.cs @@ -0,0 +1,694 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms; + + internal class FlowLayout : LayoutEngine { + + // Singleton instance shared by all FlowPanels. + internal static readonly FlowLayout Instance = new FlowLayout(); + + private static readonly int _wrapContentsProperty = PropertyStore.CreateKey(); + private static readonly int _flowDirectionProperty = PropertyStore.CreateKey(); + + internal static FlowLayoutSettings CreateSettings(IArrangedElement owner) { + return new FlowLayoutSettings(owner); + } + + // Entry point from LayoutEngine + internal override bool LayoutCore(IArrangedElement container, LayoutEventArgs args) { +#if DEBUG + if (CompModSwitches.FlowLayout.TraceInfo) { + Debug.WriteLine("FlowLayout::Layout(" + + "container=" + container.ToString() + ", " + + "displayRect=" + container.DisplayRectangle.ToString() + ", " + + "args=" + args.ToString() + ")"); + } + Debug.Indent(); +#endif + + // ScrollableControl will first try to get the layoutbounds from the derived control when + // trying to figure out if ScrollBars should be added. + // VSWhidbey #392913 + CommonProperties.SetLayoutBounds(container, xLayout(container, container.DisplayRectangle, /* measureOnly = */ false)); +#if DEBUG + Debug.Unindent(); +#endif + return CommonProperties.GetAutoSize(container); + } + + internal override Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) { +#if DEBUG + if (CompModSwitches.FlowLayout.TraceInfo) { + Debug.WriteLine("FlowLayout::GetPreferredSize(" + + "container=" + container.ToString() + ", " + + "proposedConstraints=" + proposedConstraints.ToString() + ")"); + Debug.Indent(); + } +#endif + Rectangle measureBounds = new Rectangle(new Point(0, 0), proposedConstraints); + Size prefSize = xLayout(container, measureBounds, /* measureOnly = */ true); + + if(prefSize.Width > proposedConstraints.Width || prefSize.Height> proposedConstraints.Height) { + // Controls measured earlier than a control which couldn't be fit to constraints may + // shift around with the new bounds. We need to make a 2nd pass through the + // controls using these bounds which are gauranteed to fit. + measureBounds.Size = prefSize; + prefSize = xLayout(container, measureBounds, /* measureOnly = */ true); + } + +#if DEBUG + if (CompModSwitches.FlowLayout.TraceInfo) { + Debug.Unindent(); + Debug.WriteLine("GetPreferredSize returned " + prefSize); + } +#endif + return prefSize; + } + + private static ContainerProxy CreateContainerProxy(IArrangedElement container, FlowDirection flowDirection) { + + switch (flowDirection) { + case FlowDirection.RightToLeft: + return new RightToLeftProxy(container); + case FlowDirection.TopDown: + return new TopDownProxy(container); + case FlowDirection.BottomUp: + return new BottomUpProxy(container); + case FlowDirection.LeftToRight: + default: + return new ContainerProxy(container); + } + + } + + + // Both LayoutCore and GetPreferredSize forward to this method. The measureOnly flag determines which + // behavior we get. + private Size xLayout(IArrangedElement container, Rectangle displayRect, bool measureOnly) { + FlowDirection flowDirection = GetFlowDirection(container); + bool wrapContents = GetWrapContents(container); + + ContainerProxy containerProxy = CreateContainerProxy(container, flowDirection); + containerProxy.DisplayRect = displayRect; + + // refetch as it's now adjusted for Vertical. + displayRect = containerProxy.DisplayRect; + + ElementProxy elementProxy = containerProxy.ElementProxy; + Size layoutSize = Size.Empty; + + if(!wrapContents) { + // pretend that the container is infinitely wide to prevent wrapping. + // VSWhidbey 161993: displayRectangle.Right is Width + X - subtract X to prevent overflow. + displayRect.Width = Int32.MaxValue - displayRect.X; + } + + for(int i = 0; i < container.Children.Count;) { + int breakIndex; + Size rowSize = Size.Empty; + + Rectangle measureBounds = new Rectangle(displayRect.X, displayRect.Y, displayRect.Width, displayRect.Height - layoutSize.Height); + rowSize = MeasureRow(containerProxy, elementProxy, i, measureBounds, out breakIndex); + + + // if we are not wrapping contents, then the breakIndex (as set in MeasureRow) + // should be equal to the count of child items in the container. + Debug.Assert(wrapContents == true || breakIndex == container.Children.Count, + "We should not be trying to break the row if we are not wrapping contents."); + + if(!measureOnly) { + Rectangle rowBounds = new Rectangle(displayRect.X, + layoutSize.Height + displayRect.Y, + rowSize.Width, + rowSize.Height); + LayoutRow(containerProxy, elementProxy, /* startIndex = */ i, /* endIndex = */ breakIndex, rowBounds); + } + layoutSize.Width = Math.Max(layoutSize.Width, rowSize.Width); + layoutSize.Height += rowSize.Height; + i = breakIndex; + } + //verify that our alignment is correct + if (container.Children.Count != 0 && !measureOnly) { + Debug_VerifyAlignment(container, flowDirection); + } + return LayoutUtils.FlipSizeIf(flowDirection == FlowDirection.TopDown || GetFlowDirection(container) == FlowDirection.BottomUp, layoutSize); + } + + // Just forwards to xLayoutRow. This will layout elements from the start index to the end index. RowBounds + // was computed by a call to measure row and is used for alignment/boxstretch. See the ElementProxy class + // for an explaination of the elementProxy parameter. + private void LayoutRow(ContainerProxy containerProxy, ElementProxy elementProxy, int startIndex, int endIndex, Rectangle rowBounds) { + int dummy; + Size outSize = xLayoutRow(containerProxy, elementProxy, startIndex, endIndex, rowBounds, /* breakIndex = */ out dummy, /* measureOnly = */ false); + Debug.Assert(dummy == endIndex, "EndIndex / BreakIndex mismatch."); + } + + // Just forwards to xLayoutRow. breakIndex is the index of the first control not to fit in the displayRectangle. The + // returned Size is the size required to layout the controls from startIndex up to but not including breakIndex. See + // the ElementProxy class for an explaination of the elementProxy parameter. + private Size MeasureRow(ContainerProxy containerProxy, ElementProxy elementProxy, int startIndex, Rectangle displayRectangle, out int breakIndex) { + return xLayoutRow(containerProxy, elementProxy, startIndex, /* endIndex = */ containerProxy.Container.Children.Count, displayRectangle, out breakIndex, /* measureOnly = */ true); + } + + // LayoutRow and MeasureRow both forward to this method. The measureOnly flag determines which behavior we get. + private Size xLayoutRow(ContainerProxy containerProxy, ElementProxy elementProxy, int startIndex, int endIndex, Rectangle rowBounds, out int breakIndex, bool measureOnly) { + Debug.Assert(startIndex < endIndex, "Loop should be in forward Z-order."); + Point location = rowBounds.Location; + Size rowSize = Size.Empty; + int laidOutItems = 0; + breakIndex = startIndex; + + bool wrapContents = GetWrapContents(containerProxy.Container); + bool breakOnNextItem = false; + + + ArrangedElementCollection collection = containerProxy.Container.Children; + for(int i = startIndex; i < endIndex; i++, breakIndex++) { + elementProxy.Element = collection[i]; + + if(!elementProxy.ParticipatesInLayout) { + continue; + } + + // Figure out how much space this element is going to need (requiredSize) + // + Size prefSize; + if(elementProxy.AutoSize) { + Size elementConstraints = new Size(Int32.MaxValue, rowBounds.Height - elementProxy.Margin.Size.Height); + if(i == startIndex) { + // If the element is the first in the row, attempt to pack it to the row width. (If its not 1st, it will wrap + // to the first on the next row if its too long and then be packed if needed by the next call to xLayoutRow). + elementConstraints.Width = rowBounds.Width - rowSize.Width - elementProxy.Margin.Size.Width; + } + + // Make sure that subtracting the margins does not cause width/height to be <= 0, or we will + // size as if we had infinite space when in fact we are trying to be as small as possible. + elementConstraints = LayoutUtils.UnionSizes(new Size(1, 1), elementConstraints); + prefSize = elementProxy.GetPreferredSize(elementConstraints); + } else { + // If autosizing is turned off, we just use the element's current size as its preferred size. + prefSize = elementProxy.SpecifiedSize; + + // VSWhidbey 406227 + // except if it is stretching - then ignore the affect of the height dimension. + if (elementProxy.Stretches) { + prefSize.Height = 0; + } + + // Enforce MinimumSize + if (prefSize.Height < elementProxy.MinimumSize.Height) { + prefSize.Height = elementProxy.MinimumSize.Height; + } + } + Size requiredSize = prefSize + elementProxy.Margin.Size; + + // Position the element (if applicable). + // + if(!measureOnly) { + // If measureOnly = false, rowBounds.Height = measured row hieght + // (otherwise its the remaining displayRect of the container) + + Rectangle cellBounds = new Rectangle(location, new Size(requiredSize.Width, rowBounds.Height)); + + // We laid out the rows with the elementProxy's margins included. + // We now deflate the rect to get the actual elementProxy bounds. + cellBounds = LayoutUtils.DeflateRect(cellBounds, elementProxy.Margin); + + AnchorStyles anchorStyles = elementProxy.AnchorStyles; + containerProxy.Bounds = LayoutUtils.AlignAndStretch(prefSize, cellBounds, anchorStyles); + } + + // Keep track of how much space is being used in this row + // + + location.X += requiredSize.Width; + + + + if (laidOutItems > 0) { + // If control does not fit on this row, exclude it from row and stop now. + // Exception: If row is empty, allow this control to fit on it. So controls + // that exceed the maximum row width will end up occupying their own rows. + if(location.X > rowBounds.Right) { + break; + } + + } + // Control fits on this row, so update the row size. + // rowSize.Width != location.X because with a scrollable control + // we could have started with a location like -100. + + rowSize.Width = location.X - rowBounds.X; + + rowSize.Height = Math.Max(rowSize.Height, requiredSize.Height); + + // check for line breaks. + if (wrapContents) { + if (breakOnNextItem) { + break; + } + else if (i+1 < endIndex && CommonProperties.GetFlowBreak(elementProxy.Element)) { + if (laidOutItems == 0) { + breakOnNextItem = true; + } + else { + breakIndex++; + break; + } + } + } + ++laidOutItems; + } + + return rowSize; + } + + #region Provided properties + public static bool GetWrapContents(IArrangedElement container) { + int wrapContents = container.Properties.GetInteger(_wrapContentsProperty); + return (wrapContents == 0); // if not set return true. + } + + public static void SetWrapContents(IArrangedElement container, bool value) { + container.Properties.SetInteger(_wrapContentsProperty, value ? 0 : 1); // set to 0 if true, 1 if false + LayoutTransaction.DoLayout(container, container, PropertyNames.WrapContents); + Debug.Assert(GetWrapContents(container) == value, "GetWrapContents should return the same value as we set"); + } + + public static FlowDirection GetFlowDirection(IArrangedElement container) { + return (FlowDirection) container.Properties.GetInteger(_flowDirectionProperty); + } + + public static void SetFlowDirection(IArrangedElement container, FlowDirection value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlowDirection.LeftToRight, (int)FlowDirection.BottomUp)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(FlowDirection)); + } + container.Properties.SetInteger(_flowDirectionProperty, (int) value); + LayoutTransaction.DoLayout(container, container, PropertyNames.FlowDirection); + Debug.Assert(GetFlowDirection(container) == value, "GetFlowDirection should return the same value as we set"); + } + #endregion Provided properties + + #region ContainerProxy + + // What is a ContainerProxy? + // + // The goal of the FlowLayout Engine is to always layout from left to right. In order to achieve different + // flow directions we have "Proxies" for the Container (the thing laying out) and for setting the bounds of the + // child elements. + // + // We have a base ContainerProxy, and derived proxies for all of the flow directions. In order to achieve flow direction of RightToLeft, + // the RightToLeft container proxy detects when we're going to set the bounds and translates it to the right. + // + // In order to do a vertical flow, such as TopDown, we pretend we're laying out horizontally. The main way this is + // achieved is through the use of the VerticalElementProxy, which flips all rectangles and sizes. + // + // In order to do BottomUp, we combine the same techniques of TopDown with the RightToLeft flow. That is, + // we override the bounds, and translate from left to right, AND use the VerticalElementProxy. + // + // A final note: This layout engine does all its RightToLeft translation itself. It does not support + // WS_EX_LAYOUTRTL (OS mirroring). + + private class ContainerProxy { + private IArrangedElement _container; + private ElementProxy _elementProxy; + private Rectangle _displayRect; + private bool _isContainerRTL; + public ContainerProxy(IArrangedElement container) { + this._container = container; + this._isContainerRTL = false; + if (_container is Control) { + _isContainerRTL = ((Control)(_container)).RightToLeft == RightToLeft.Yes; + } + } + + // method for setting the bounds of a child element. + public virtual Rectangle Bounds { + set { + if (IsContainerRTL) { + // Offset the X coordinate... + if (IsVertical) { + //Offset the Y value here, since it is really the X value.... + value.Y = DisplayRect.Bottom - value.Bottom; + } + else { + value.X = DisplayRect.Right - value.Right; + } + + FlowLayoutPanel flp = Container as FlowLayoutPanel; + if (flp != null) { + Point ptScroll = flp.AutoScrollPosition; + if (ptScroll != Point.Empty) { + Point pt = new Point(value.X, value.Y); + if (IsVertical) { + //Offset the Y value here, since it is really the X value.... + pt.Offset(0, ptScroll.X); + } + else { + pt.Offset(ptScroll.X, 0); + } + value.Location = pt; + } + } + } + + ElementProxy.Bounds = value; + + } + } + + // specifies the container laying out + public IArrangedElement Container { + get { return _container; } + } + + // specifies if we're TopDown or BottomUp and should use the VerticalElementProxy to translate + protected virtual bool IsVertical { + get { return false; } + } + + // returns true if container is RTL.Yes + protected bool IsContainerRTL { + get { return _isContainerRTL; } + } + + // returns the display rectangle of the container - this WILL BE FLIPPED if the layout + // is a vertical layout. + public Rectangle DisplayRect { + get { + return _displayRect; + } + set { + if (_displayRect != value) { + + //flip the displayRect since when we do layout direction TopDown/BottomUp, we layout the controls + //on the flipped rectangle as if our layout direction were LeftToRight/RightToLeft. In this case + //we can save some code bloat + _displayRect = LayoutUtils.FlipRectangleIf(IsVertical, value); + } + } + } + + // returns the element proxy to use. A vertical element proxy will typically + // flip all the sizes and rectangles so that it can fake being laid out in a horizontal manner. + public ElementProxy ElementProxy { + get { + if (_elementProxy == null) { + _elementProxy = (IsVertical) ? new VerticalElementProxy() : new ElementProxy(); + } + return _elementProxy; + + } + + } + + // used when you want to translate from right to left, but preserve Margin.Right & Margin.Left. + protected Rectangle RTLTranslateNoMarginSwap(Rectangle bounds) { + + Rectangle newBounds = bounds; + + newBounds.X = DisplayRect.Right - bounds.X - bounds.Width + ElementProxy.Margin.Left - ElementProxy.Margin.Right; + + // Since DisplayRect.Right and bounds.X are both adjusted for the AutoScrollPosition, we need add it back here. + FlowLayoutPanel flp = Container as FlowLayoutPanel; + if (flp != null) { + Point ptScroll = flp.AutoScrollPosition; + if (ptScroll != Point.Empty) { + Point pt = new Point(newBounds.X, newBounds.Y); + + if (IsVertical) { + // TRICKY TRICKY TRICKY.... HACK HACK HACK... + // + // We need to treat Vertical a litte differently. It really helps if you draw this out. + // Remember that when we layout BottomUp, we first layout TopDown, then call this method. + // When we layout TopDown we layout in flipped rectangles. I.e. x becomes y, y becomes x, + // height becomes width, width becomes height. We do our layout, then when we eventually + // set the bounds of the child elements, we flip back. Thus, x will eventually + // become y. We need to adjust for scrolling - but only in the y direction - + // and since x becomes y, we adjust x. But since AutoScrollPoisition has not been swapped, + // we need to use its Y coordinate when offsetting. + // + // DRAW THIS OUT!!! + // + // Did I mention you should draw this out? + // + // Did I mention that everything is flipped? + pt.Offset(ptScroll.Y, 0); + } else { + pt.Offset(ptScroll.X, 0); + } + newBounds.Location = pt; + } + } + + return newBounds; + } + + } + + + // FlowDirection.RightToLeft proxy. + private class RightToLeftProxy : ContainerProxy { + public RightToLeftProxy(IArrangedElement container) : base(container) { + } + + + public override Rectangle Bounds { + set { + // if the container is RTL, align to the left, otherwise, align to the right. + // Do NOT use LayoutUtils.RTLTranslate as we want to preserve the padding.Right on the right... + base.Bounds = RTLTranslateNoMarginSwap(value); + } + } + } + + + // FlowDirection.TopDown proxy. + // For TopDown we're really still laying out horizontally. The element proxy is the one + // which flips all the rectangles and rotates itself into the vertical orientation. + // to achieve right to left, we actually have to do something non-intuitive - instead of + // sending the control to the right, we have to send the control to the bottom. When the rotation + // is complete - that's equivilant to pushing it to the right. + private class TopDownProxy : ContainerProxy { + public TopDownProxy(IArrangedElement container) : base(container) { + } + protected override bool IsVertical { + get { return true; } + } + } + + // FlowDirection.BottomUp proxy. + private class BottomUpProxy : ContainerProxy { + public BottomUpProxy(IArrangedElement container) : base(container) { + } + protected override bool IsVertical { + get { return true; } + } + + // For BottomUp we're really still laying out horizontally. The element proxy is the one + // which flips all the rectangles and rotates itself into the vertical orientation. + // BottomUp is the analog of RightToLeft - meaning, in order to place a control at the bottom, + // the control has to be placed to the right. When the rotation is complete, that's the equivilant of + // pushing it to the right. This must be done all the time. + // + // To achieve right to left, we actually have to do something non-intuitive - instead of + // sending the control to the right, we have to send the control to the bottom. When the rotation + // is complete - that's equivilant to pushing it to the right. + + public override Rectangle Bounds { + set { + // push the control to the bottom. + // Do NOT use LayoutUtils.RTLTranslate as we want to preserve the padding.Right on the right... + base.Bounds = RTLTranslateNoMarginSwap(value); + } + } + } + + #endregion ContainerProxy + #region ElementProxy + // ElementProxy inserts a level of indirection between the LayoutEngine + // and the IArrangedElement that allows us to use the same code path + // for Vertical and Horizontal flow layout. (see VerticalElementProxy) + private class ElementProxy { + private IArrangedElement _element; + + public virtual AnchorStyles AnchorStyles { + get { + AnchorStyles anchorStyles = LayoutUtils.GetUnifiedAnchor(Element); + bool isStretch = (anchorStyles & LayoutUtils.VerticalAnchorStyles) == LayoutUtils.VerticalAnchorStyles; //whether the control stretches to fill in the whole space + bool isTop = (anchorStyles & AnchorStyles.Top) != 0; //whether the control anchors to top and does not stretch; + bool isBottom = (anchorStyles & AnchorStyles.Bottom) != 0; //whether the control anchors to bottom and does not stretch; + + if(isStretch) { + //the element stretches to fill in the whole row. Equivalent to AnchorStyles.Top|AnchorStyles.Bottom + return LayoutUtils.VerticalAnchorStyles; + } + if(isTop) { + //the element anchors to top and doesn't stretch + return AnchorStyles.Top; + } + if(isBottom) { + //the element anchors to bottom and doesn't stretch + return AnchorStyles.Bottom; + } + return AnchorStyles.None; + } + } + + public bool AutoSize { + get { return CommonProperties.GetAutoSize(_element); } + } + + public virtual Rectangle Bounds { + set { _element.SetBounds(value, BoundsSpecified.None); } + } + + public IArrangedElement Element { + get { return _element; } + set { + _element = value; + Debug.Assert(Element == value, "Element should be the same as we set it to"); + } + } + + public bool Stretches { + get { + AnchorStyles styles = AnchorStyles; + if ((LayoutUtils.VerticalAnchorStyles & styles) == LayoutUtils.VerticalAnchorStyles) { + return true; + } + else { + return false; + } + } + } + + public virtual Padding Margin { + get { return CommonProperties.GetMargin(Element); } + } + + public virtual Size MinimumSize { + get { return CommonProperties.GetMinimumSize(Element, Size.Empty); } + } + + public bool ParticipatesInLayout { + get { return _element.ParticipatesInLayout; } + } + + public virtual Size SpecifiedSize { + get { return CommonProperties.GetSpecifiedBounds(_element).Size; } + } + + public virtual Size GetPreferredSize(Size proposedSize) { + return _element.GetPreferredSize(proposedSize); + } + } + + // VerticalElementProxy swaps Top/Left, Bottom/Right, and other properties + // so that the same code path used for horizantal flow can be applied to + // vertical flow. + private class VerticalElementProxy : ElementProxy { + public override AnchorStyles AnchorStyles { + get { + AnchorStyles anchorStyles = LayoutUtils.GetUnifiedAnchor(Element); + bool isStretch = (anchorStyles & LayoutUtils.HorizontalAnchorStyles) == LayoutUtils.HorizontalAnchorStyles; //whether the control stretches to fill in the whole space + bool isLeft = (anchorStyles & AnchorStyles.Left) != 0; //whether the control anchors to left and does not stretch; + bool isRight = (anchorStyles & AnchorStyles.Right) != 0; //whether the control anchors to right and does not stretch; + if(isStretch) { + return LayoutUtils.VerticalAnchorStyles; + } + if(isLeft) { + return AnchorStyles.Top; + } + if(isRight) { + return AnchorStyles.Bottom; + } + return AnchorStyles.None; + } + } + + public override Rectangle Bounds { + set { base.Bounds = LayoutUtils.FlipRectangle(value); } + } + + public override Padding Margin { + get { return LayoutUtils.FlipPadding(base.Margin); } + } + + public override Size MinimumSize { + get { return LayoutUtils.FlipSize(base.MinimumSize); } + } + + public override Size SpecifiedSize { + get { return LayoutUtils.FlipSize(base.SpecifiedSize); } + } + + public override Size GetPreferredSize(Size proposedSize) { + return LayoutUtils.FlipSize(base.GetPreferredSize(LayoutUtils.FlipSize(proposedSize))); + } + } + #endregion ElementProxy + + #region DEBUG + [Conditional("DEBUG_VERIFY_ALIGNMENT")] + private void Debug_VerifyAlignment(IArrangedElement container, FlowDirection flowDirection) { +#if DEBUG + //We cannot apply any of these checks @ design-time since dragging new children into a FlowLayoutPanel + //will attempt to set the children at the mouse position when the child was dropped - we rely on the controil + //to reposition the children once added. + Control flp = container as Control; + if (flp != null && flp.Site != null && flp.Site.DesignMode) { + return; + } + + //check to see if the first element is in its right place + Padding margin = CommonProperties.GetMargin(container.Children[0]); + switch (flowDirection) { + case FlowDirection.LeftToRight: + case FlowDirection.TopDown: + Debug.Assert(container.Children[0].Bounds.Y == margin.Top + container.DisplayRectangle.Y); + Debug.Assert(container.Children[0].Bounds.X == margin.Left + container.DisplayRectangle.X); + break; + case FlowDirection.RightToLeft: + Debug.Assert(container.Children[0].Bounds.X == container.DisplayRectangle.X + container.DisplayRectangle.Width - container.Children[0].Bounds.Width - margin.Right); + Debug.Assert(container.Children[0].Bounds.Y == margin.Top + container.DisplayRectangle.Y); + break; + case FlowDirection.BottomUp: + Debug.Assert(container.Children[0].Bounds.Y == container.DisplayRectangle.Y + container.DisplayRectangle.Height - container.Children[0].Bounds.Height - margin.Bottom); + Debug.Assert(container.Children[0].Bounds.X == margin.Left + container.DisplayRectangle.X); + break; + } + //next check to see if everything is in bound + ArrangedElementCollection collection = container.Children; + for (int i = 1; i < collection.Count; i++) { + switch (flowDirection) { + case FlowDirection.LeftToRight: + case FlowDirection.TopDown: + Debug.Assert(collection[i].Bounds.Y >= container.DisplayRectangle.Y); + Debug.Assert(collection[i].Bounds.X >= container.DisplayRectangle.X); + break; + case FlowDirection.RightToLeft: + Debug.Assert(collection[i].Bounds.Y >= container.DisplayRectangle.Y); + Debug.Assert(collection[i].Bounds.X + collection[i].Bounds.Width <= container.DisplayRectangle.X + container.DisplayRectangle.Width); + break; + case FlowDirection.BottomUp: + Debug.Assert(collection[i].Bounds.Y + collection[i].Bounds.Height <= container.DisplayRectangle.Y + container.DisplayRectangle.Height); + Debug.Assert(collection[i].Bounds.X >= container.DisplayRectangle.X); + break; + } + } +#endif + } + #endregion DEBUG + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/IArrangedElement.cs b/WindowsForms/Managed/System/WinForms/Layout/IArrangedElement.cs new file mode 100644 index 000000000..4dbb6d567 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/IArrangedElement.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + + // SECREVIEW: If this interface were to be made public, implementions like that on + // control.cs would allow for untrusted parent discovery. + internal interface IArrangedElement : IComponent { + // Bounding rectangle of the element. + Rectangle Bounds {get; } + + // Sets the bounds for an element. Implementors should call CommonProperties.SetSpecifiedBounds. + // See Control.SetBoundsCore. + void SetBounds(Rectangle bounds, BoundsSpecified specified); + + // Query element for its preferred size. There is no guarantee + // that layout engine will assign the element the returned size. + // ProposedSize is a hint about constraints. + Size GetPreferredSize(Size proposedSize); + + // DisplayRectangle is the client area of a container element. + // Could possibly disappear if we change control to keep an + // up-to-date copy of display rectangle in the property store. + Rectangle DisplayRectangle { get; } + + // True if the element is currently visible (some layouts, like + // flow, need to skip non-visible elements.) + bool ParticipatesInLayout { get; } + + // Internally, layout engines will get properties from the + // property store on this interface. In Orcas, this will be + // replaced with a global PropertyManager for DPs. + PropertyStore Properties { get; } + + // When an extender provided property is changed, we call this + // method to update the layout on the element. In Orcas, we + // will sync the DPs changed event. + void PerformLayout(IArrangedElement affectedElement, string propertyName); + + // Returns the element's parent container (on a control, this forwands to Parent) + IArrangedElement Container { get; } + + // Returns the element's childern (on a control, this forwands to Controls) + ArrangedElementCollection Children { get; } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/LayoutEngine.cs b/WindowsForms/Managed/System/WinForms/Layout/LayoutEngine.cs new file mode 100644 index 000000000..fd3a3e1fd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/LayoutEngine.cs @@ -0,0 +1,69 @@ +//#define LAYOUT_PERFWATCH +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System; + using System.Collections; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security.Permissions; + + /// + public abstract class LayoutEngine { + internal IArrangedElement CastToArrangedElement(object obj) { + IArrangedElement element = obj as IArrangedElement; + if(obj == null) { + throw new NotSupportedException(SR.GetString(SR.LayoutEngineUnsupportedType, obj.GetType())); + } + return element; + } + + internal virtual Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) { return Size.Empty; } + + /// + public virtual void InitLayout(object child, BoundsSpecified specified) { + InitLayoutCore(CastToArrangedElement(child), specified); + } + + internal virtual void InitLayoutCore(IArrangedElement element, BoundsSpecified bounds) {} + + internal virtual void ProcessSuspendedLayoutEventArgs(IArrangedElement container, LayoutEventArgs args) {} + +#if LAYOUT_PERFWATCH +private static int LayoutWatch = 100; +#endif + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + public virtual bool Layout(object container, LayoutEventArgs layoutEventArgs) { + +#if LAYOUT_PERFWATCH + Debug.WriteLine(container.GetType().Name + "::Layout(" + + (layoutEventArgs.AffectedControl != null ? layoutEventArgs.AffectedControl.Name : "null") + + ", " + layoutEventArgs.AffectedProperty + ")"); + Debug.Indent(); + Stopwatch sw = new Stopwatch(); + sw.Start(); +#endif + bool parentNeedsLayout = LayoutCore(CastToArrangedElement(container), layoutEventArgs); + +#if LAYOUT_PERFWATCH + sw.Stop(); + if (sw.ElapsedMilliseconds > LayoutWatch && Debugger.IsAttached) { + Debugger.Break(); + } + Debug.Unindent(); + Debug.WriteLine(container.GetType().Name + "::Layout elapsed " + sw.ElapsedMilliseconds.ToString() + " returned: " + parentNeedsLayout); +#endif + return parentNeedsLayout; + } + + internal virtual bool LayoutCore(IArrangedElement container, LayoutEventArgs layoutEventArgs) { return false; } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/LayoutSettings.cs b/WindowsForms/Managed/System/WinForms/Layout/LayoutSettings.cs new file mode 100644 index 000000000..e83c8640e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/LayoutSettings.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Windows.Forms.Layout; +namespace System.Windows.Forms { + + /// + public abstract class LayoutSettings { + private IArrangedElement _owner; + + protected LayoutSettings() { + } + + internal LayoutSettings(IArrangedElement owner) { + this._owner = owner; + } + + /// + public virtual LayoutEngine LayoutEngine { + get { return null;} + } + + internal IArrangedElement Owner { + get { return _owner; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/LayoutUtils.cs b/WindowsForms/Managed/System/WinForms/Layout/LayoutUtils.cs new file mode 100644 index 000000000..157fc712c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/LayoutUtils.cs @@ -0,0 +1,758 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System; + using System.Collections; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Text; + using System.Runtime.InteropServices; + using System.Windows.Forms; + using System.Windows.Forms.ComponentModel; + using System.Collections.Generic; + + // Utilities used by layout code. If you use these outside of the layout + // namespace, you should probably move them to WindowsFormsUtils. + internal class LayoutUtils { + + public static readonly Size MaxSize = new Size(Int32.MaxValue, Int32.MaxValue); + public static readonly Size InvalidSize = new Size(Int32.MinValue, Int32.MinValue); + + public static readonly Rectangle MaxRectangle = new Rectangle(0, 0, Int32.MaxValue, Int32.MaxValue); + + public const ContentAlignment AnyTop = ContentAlignment.TopLeft | ContentAlignment.TopCenter | ContentAlignment.TopRight; + public const ContentAlignment AnyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight; + public const ContentAlignment AnyLeft = ContentAlignment.TopLeft | ContentAlignment.MiddleLeft | ContentAlignment.BottomLeft; + public const ContentAlignment AnyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight; + public const ContentAlignment AnyCenter = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter; + public const ContentAlignment AnyMiddle = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight; + + public const AnchorStyles HorizontalAnchorStyles = AnchorStyles.Left | AnchorStyles.Right; + public const AnchorStyles VerticalAnchorStyles = AnchorStyles.Top | AnchorStyles.Bottom; + + private static readonly AnchorStyles[] dockingToAnchor = new AnchorStyles[] { + /* None */ AnchorStyles.Top | AnchorStyles.Left, + /* Top */ AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right, + /* Bottom */ AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, + /* Left */ AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom, + /* Right */ AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom, + /* Fill */ AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left + }; + + // A good, short test string for measuring control height. + public readonly static string TestString = "j^"; + + + // Returns the size of the largest string in the given collection. Non-string objects are converted + // with ToString(). Uses OldMeasureString, not GDI+. Does not support multiline. + public static Size OldGetLargestStringSizeInCollection(Font font, ICollection objects) { + Size largestSize = Size.Empty; + if (objects != null) { + foreach(object obj in objects) { + Size textSize = TextRenderer.MeasureText(obj.ToString(), font, new Size(Int16.MaxValue, Int16.MaxValue), TextFormatFlags.SingleLine); + largestSize.Width = Math.Max(largestSize.Width, textSize.Width); + largestSize.Height = Math.Max(largestSize.Height, textSize.Height); + } + } + return largestSize; + } + + + + /* + * We can cut ContentAlignment from a max index of 1024 (12b) down to 11 (4b) through + * bit twiddling. The int result of this function maps to the ContentAlignment as indicated + * by the table below: + * + * Left Center Right + * Top 0000 0x0 0001 0x1 0010 0x2 + * Middle 0100 0x4 0101 0x5 0110 0x6 + * Bottom 1000 0x8 1001 0x9 1010 0xA + * + * (The high 2 bits determine T/M/B. The low 2 bits determine L/C/R.) + */ + + public static int ContentAlignmentToIndex(ContentAlignment alignment) { + /* + * Here is what content alignment looks like coming in: + * + * Left Center Right + * Top 0x001 0x002 0x004 + * Middle 0x010 0x020 0x040 + * Bottom 0x100 0x200 0x400 + * + * (L/C/R determined bit 1,2,4. T/M/B determined by 4 bit shift.) + */ + + int topBits = xContentAlignmentToIndex(((int)alignment) & 0x0F); + int middleBits = xContentAlignmentToIndex(((int)alignment >> 4) & 0x0F); + int bottomBits = xContentAlignmentToIndex(((int)alignment >> 8) & 0x0F); + + Debug.Assert((topBits != 0 && (middleBits == 0 && bottomBits == 0)) + || (middleBits != 0 && (topBits == 0 && bottomBits == 0)) + || (bottomBits != 0 && (topBits == 0 && middleBits == 0)), + "One (and only one) of topBits, middleBits, or bottomBits should be non-zero."); + + int result = (middleBits != 0 ? 0x04 : 0) | (bottomBits != 0 ? 0x08 : 0) | topBits | middleBits | bottomBits; + + // zero isn't used, so we can subtract 1 and start with index 0. + result --; + + Debug.Assert(result >= 0x00 && result <=0x0A, "ContentAlignmentToIndex result out of range."); + Debug.Assert(result != 0x00 || alignment == ContentAlignment.TopLeft, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x01 || alignment == ContentAlignment.TopCenter, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x02 || alignment == ContentAlignment.TopRight, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x03, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x04 || alignment == ContentAlignment.MiddleLeft, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x05 || alignment == ContentAlignment.MiddleCenter, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x06 || alignment == ContentAlignment.MiddleRight, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x07, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x08 || alignment == ContentAlignment.BottomLeft, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x09 || alignment == ContentAlignment.BottomCenter, "Error detected in ContentAlignmentToIndex."); + Debug.Assert(result != 0x0A || alignment == ContentAlignment.BottomRight, "Error detected in ContentAlignmentToIndex."); + + return result; + } + + // Converts 0x00, 0x01, 0x02, 0x04 (3b flag) to 0, 1, 2, 3 (2b index) + private static byte xContentAlignmentToIndex(int threeBitFlag) { + Debug.Assert(threeBitFlag >= 0x00 && threeBitFlag <= 0x04 && threeBitFlag != 0x03, "threeBitFlag out of range."); + byte result = threeBitFlag == 0x04 ? (byte) 3 : (byte) threeBitFlag; + Debug.Assert((result & 0x03) == result, "Result out of range."); + return result; + } + + public static Size ConvertZeroToUnbounded(Size size) { + if(size.Width == 0) size.Width = Int32.MaxValue; + if(size.Height == 0) size.Height = Int32.MaxValue; + return size; + } + + // Clamps negative values in Padding struct to zero. + public static Padding ClampNegativePaddingToZero(Padding padding) { + // Careful: Setting the LRTB properties causes Padding.All to be -1 even if LRTB all agree. + if(padding.All < 0) { + padding.Left = Math.Max(0, padding.Left); + padding.Top = Math.Max(0, padding.Top); + padding.Right = Math.Max(0, padding.Right); + padding.Bottom = Math.Max(0, padding.Bottom); + } + return padding; + } + + /* + * Maps an anchor to its opposite. Does not support combinations. None returns none. + * + * Top = 0x01 + * Bottom = 0x02 + * Left = 0x04 + * Right = 0x08 + */ + + + // Returns the positive opposite of the given anchor (e.g., L -> R, LT -> RB, LTR -> LBR, etc.). None return none. + private static AnchorStyles GetOppositeAnchor(AnchorStyles anchor) { + AnchorStyles result = AnchorStyles.None; + if (anchor == AnchorStyles.None){ + return result; + } + + // iterate through T,B,L,R + // bitwise or B,T,R,L as appropriate + for (int i = 1; i <= (int)AnchorStyles.Right; i=i <<1) { + switch (anchor & (AnchorStyles)i) { + case AnchorStyles.None: + break; + case AnchorStyles.Left: + result |= AnchorStyles.Right; + break; + case AnchorStyles.Top: + result |= AnchorStyles.Bottom; + break; + case AnchorStyles.Right: + result |= AnchorStyles.Left; + break; + case AnchorStyles.Bottom: + result |= AnchorStyles.Top; + break; + default: + break; + } + + } + + return result; + } + + public static TextImageRelation GetOppositeTextImageRelation(TextImageRelation relation) { + return (TextImageRelation) GetOppositeAnchor((AnchorStyles)relation); + } + + public static Size UnionSizes(Size a, Size b) { + return new Size( + Math.Max(a.Width, b.Width), + Math.Max(a.Height, b.Height)); + } + + public static Size IntersectSizes(Size a, Size b) { + return new Size( + Math.Min(a.Width, b.Width), + Math.Min(a.Height, b.Height)); + } + + public static bool IsIntersectHorizontally(Rectangle rect1, Rectangle rect2) { + if (!rect1.IntersectsWith(rect2)) { + return false; + } + if (rect1.X <= rect2.X && rect1.X + rect1.Width >= rect2.X + rect2.Width) { + //rect 1 contains rect 2 horizontally + return true; + } + if (rect2.X <= rect1.X && rect2.X + rect2.Width >= rect1.X + rect1.Width) { + //rect 2 contains rect 1 horizontally + return true; + } + return false; + } + + public static bool IsIntersectVertically(Rectangle rect1, Rectangle rect2) { + if (!rect1.IntersectsWith(rect2)) { + return false; + } + if (rect1.Y <= rect2.Y && rect1.Y + rect1.Width >= rect2.Y + rect2.Width) { + //rect 1 contains rect 2 vertically + return true; + } + if (rect2.Y <= rect1.Y && rect2.Y + rect2.Width >= rect1.Y + rect1.Width) { + //rect 2 contains rect 1 vertically + return true; + } + return false; + } + + //returns anchorStyles, transforms from DockStyle if necessary + internal static AnchorStyles GetUnifiedAnchor(IArrangedElement element) { + DockStyle dockStyle = DefaultLayout.GetDock(element); + if (dockStyle != DockStyle.None) { + return dockingToAnchor[(int)dockStyle]; + } + return DefaultLayout.GetAnchor(element); + } + + public static Rectangle AlignAndStretch(Size fitThis, Rectangle withinThis, AnchorStyles anchorStyles) { + return Align(Stretch(fitThis, withinThis.Size, anchorStyles), withinThis, anchorStyles); + } + + public static Rectangle Align(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) { + return VAlign(alignThis, HAlign(alignThis, withinThis, anchorStyles), anchorStyles); + } + + public static Rectangle Align(Size alignThis, Rectangle withinThis, ContentAlignment align) { + return VAlign(alignThis, HAlign(alignThis, withinThis, align), align); + } + + public static Rectangle HAlign(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) { + if ((anchorStyles & AnchorStyles.Right) != 0) { + withinThis.X += withinThis.Width - alignThis.Width; + } + else if (anchorStyles == AnchorStyles.None || (anchorStyles & HorizontalAnchorStyles) == 0) { + withinThis.X += (withinThis.Width - alignThis.Width) / 2; + } + withinThis.Width = alignThis.Width; + + return withinThis; + } + + private static Rectangle HAlign(Size alignThis, Rectangle withinThis, ContentAlignment align) { + if ((align & AnyRight) != 0) { + withinThis.X += withinThis.Width - alignThis.Width; + } + else if ((align & AnyCenter) != 0) { + withinThis.X += (withinThis.Width - alignThis.Width) / 2; + } + withinThis.Width = alignThis.Width; + + return withinThis; + } + + public static Rectangle VAlign(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) { + if ((anchorStyles & AnchorStyles.Bottom) != 0) { + withinThis.Y += withinThis.Height - alignThis.Height; + } + else if (anchorStyles == AnchorStyles.None || (anchorStyles & VerticalAnchorStyles) == 0) { + withinThis.Y += (withinThis.Height - alignThis.Height) / 2; + } + + withinThis.Height = alignThis.Height; + + return withinThis; + } + + public static Rectangle VAlign(Size alignThis, Rectangle withinThis, ContentAlignment align) { + if ((align & AnyBottom) != 0) { + withinThis.Y += withinThis.Height - alignThis.Height; + } + else if ((align & AnyMiddle) != 0) { + withinThis.Y += (withinThis.Height - alignThis.Height) / 2; + } + + withinThis.Height = alignThis.Height; + + return withinThis; + } + + public static Size Stretch(Size stretchThis, Size withinThis, AnchorStyles anchorStyles) { + Size stretchedSize = new Size( + (anchorStyles & HorizontalAnchorStyles) == HorizontalAnchorStyles ? withinThis.Width : stretchThis.Width, + (anchorStyles & VerticalAnchorStyles) == VerticalAnchorStyles ? withinThis.Height : stretchThis.Height + ); + if (stretchedSize.Width > withinThis.Width) { + stretchedSize.Width = withinThis.Width; + } + if (stretchedSize.Height > withinThis.Height) { + stretchedSize.Height = withinThis.Height; + } + return stretchedSize; + } + + public static Rectangle InflateRect(Rectangle rect, Padding padding) { + rect.X -= padding.Left; + rect.Y -= padding.Top; + rect.Width += padding.Horizontal; + rect.Height += padding.Vertical; + return rect; + } + + public static Rectangle DeflateRect(Rectangle rect, Padding padding) { + rect.X += padding.Left; + rect.Y += padding.Top; + rect.Width -= padding.Horizontal; + rect.Height -= padding.Vertical; + return rect; + } + + public static Size AddAlignedRegion(Size textSize, Size imageSize, TextImageRelation relation) { + return AddAlignedRegionCore(textSize, imageSize, IsVerticalRelation(relation)); + } + + public static Size AddAlignedRegionCore(Size currentSize, Size contentSize, bool vertical) { + if(vertical) { + currentSize.Width = Math.Max(currentSize.Width, contentSize.Width); + currentSize.Height += contentSize.Height; + } else { + currentSize.Width += contentSize.Width; + currentSize.Height = Math.Max(currentSize.Height, contentSize.Height); + } + return currentSize; + } + + public static Padding FlipPadding(Padding padding) { + // If Padding.All != -1, then TLRB are all the same and there is no work to be done. + if(padding.All != -1) { + return padding; + } + + // Padding is a stuct (passed by value, no need to make a copy) + int temp; + + temp = padding.Top; + padding.Top = padding.Left; + padding.Left = temp; + + temp = padding.Bottom; + padding.Bottom = padding.Right; + padding.Right = temp; + + return padding; + } + + public static Point FlipPoint(Point point) { + // Point is a struct (passed by value, no need to make a copy) + int temp = point.X; + point.X = point.Y; + point.Y = temp; + return point; + } + + public static Rectangle FlipRectangle(Rectangle rect) { + // Rectangle is a stuct (passed by value, no need to make a copy) + rect.Location = FlipPoint(rect.Location); + rect.Size = FlipSize(rect.Size); + return rect; + } + + public static Rectangle FlipRectangleIf(bool condition, Rectangle rect) { + return condition ? FlipRectangle(rect) : rect; + } + + public static Size FlipSize(Size size) { + // Size is a struct (passed by value, no need to make a copy) + int temp = size.Width; + size.Width = size.Height; + size.Height = temp; + return size; + } + + public static Size FlipSizeIf(bool condition, Size size) { + return condition ? FlipSize(size) : size; + } + + public static bool IsHorizontalAlignment(ContentAlignment align) { + return !IsVerticalAlignment(align); + } + + // True if text & image should be lined up horizontally. False if vertical or overlay. + public static bool IsHorizontalRelation(TextImageRelation relation) { + return (relation & (TextImageRelation.TextBeforeImage | TextImageRelation.ImageBeforeText)) != 0; + } + + public static bool IsVerticalAlignment(ContentAlignment align) { + Debug.Assert(align != ContentAlignment.MiddleCenter, "Result is ambiguous with an alignment of MiddleCenter."); + return (align & (ContentAlignment.TopCenter | ContentAlignment.BottomCenter)) != 0; + } + + // True if text & image should be lined up vertically. False if horizontal or overlay. + public static bool IsVerticalRelation(TextImageRelation relation) { + return (relation & (TextImageRelation.TextAboveImage | TextImageRelation.ImageAboveText)) != 0; + } + + public static bool IsZeroWidthOrHeight(Rectangle rectangle) { + return (rectangle.Width == 0 || rectangle.Height == 0); + } + + public static bool IsZeroWidthOrHeight(Size size) { + return (size.Width == 0 || size.Height == 0); + } + + public static bool AreWidthAndHeightLarger(Size size1, Size size2){ + return ((size1.Width >= size2.Width) && (size1.Height >= size2.Height)); + } + + public static void SplitRegion(Rectangle bounds, Size specifiedContent, AnchorStyles region1Align, out Rectangle region1, out Rectangle region2) { + region1 = region2 = bounds; + switch(region1Align) { + case AnchorStyles.Left: + region1.Width = specifiedContent.Width; + region2.X += specifiedContent.Width; + region2.Width -= specifiedContent.Width; + break; + case AnchorStyles.Right: + region1.X += bounds.Width - specifiedContent.Width; + region1.Width = specifiedContent.Width; + region2.Width -= specifiedContent.Width; + break; + case AnchorStyles.Top: + region1.Height = specifiedContent.Height; + region2.Y += specifiedContent.Height; + region2.Height -= specifiedContent.Height; + break; + case AnchorStyles.Bottom: + region1.Y += bounds.Height - specifiedContent.Height; + region1.Height = specifiedContent.Height; + region2.Height -= specifiedContent.Height; + break; + default: + Debug.Fail("Unsupported value for region1Align."); + break; + } + + Debug.Assert(Rectangle.Union(region1, region2) == bounds, + "Regions do not add up to bounds."); + } + + // Expands adjacent regions to bounds. region1Align indicates which way the adjacency occurs. + public static void ExpandRegionsToFillBounds(Rectangle bounds, AnchorStyles region1Align, ref Rectangle region1, ref Rectangle region2) { + switch(region1Align) { + case AnchorStyles.Left: + Debug.Assert(region1.Right == region2.Left, "Adjacency error."); + region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Right); + region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Left); + break; + case AnchorStyles.Right: + Debug.Assert(region2.Right == region1.Left, "Adjacency error."); + region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Left); + region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Right); + break; + case AnchorStyles.Top: + Debug.Assert(region1.Bottom == region2.Top, "Adjacency error."); + region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Bottom); + region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Top); + break; + case AnchorStyles.Bottom: + Debug.Assert(region2.Bottom == region1.Top, "Adjacency error."); + region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Top); + region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Bottom); + break; + default: + Debug.Fail("Unsupported value for region1Align."); + break; + } + Debug.Assert(Rectangle.Union(region1, region2) == bounds, "region1 and region2 do not add up to bounds."); + } + + public static Size SubAlignedRegion(Size currentSize, Size contentSize, TextImageRelation relation) { + return SubAlignedRegionCore(currentSize, contentSize, IsVerticalRelation(relation)); + } + + public static Size SubAlignedRegionCore(Size currentSize, Size contentSize, bool vertical) { + if(vertical) { + currentSize.Height -= contentSize.Height; + } else { + currentSize.Width -= contentSize.Width; + } + return currentSize; + } + + private static Rectangle SubstituteSpecifiedBounds(Rectangle originalBounds, Rectangle substitutionBounds, AnchorStyles specified) { + int left = (specified & AnchorStyles.Left) != 0 ? substitutionBounds.Left : originalBounds.Left; + int top = (specified & AnchorStyles.Top) != 0 ? substitutionBounds.Top : originalBounds.Top; + int right = (specified & AnchorStyles.Right) != 0 ? substitutionBounds.Right : originalBounds.Right; + int bottom = (specified & AnchorStyles.Bottom) != 0 ? substitutionBounds.Bottom : originalBounds.Bottom; + return Rectangle.FromLTRB(left, top, right, bottom); + } + + // given a rectangle, flip to the other side of (withinBounds) + // + // Never call this if you derive from ScrollableControl + public static Rectangle RTLTranslate(Rectangle bounds, Rectangle withinBounds) { + bounds.X = withinBounds.Width - bounds.Right; + return bounds; + } + + /// MeasureTextCache + /// Cache mechanism added for VSWhidbey 500516 + /// 3000 character strings take 9 seconds to load the form + public sealed class MeasureTextCache { + private Size unconstrainedPreferredSize = LayoutUtils.InvalidSize; + private const int MaxCacheSize = 6; // the number of preferred sizes to store + private int nextCacheEntry = -1; // the next place in the ring buffer to store a preferred size + + private PreferredSizeCache[] sizeCacheList; // MRU of size MaxCacheSize + + + /// InvalidateCache + /// Clears out the cached values, should be called whenever Text, Font or a TextFormatFlag has changed + public void InvalidateCache() { + unconstrainedPreferredSize = LayoutUtils.InvalidSize; + sizeCacheList = null; + } + + + /// GetTextSize + /// Given constraints, format flags a font and text, determine the size of the string + /// employs an MRU of the last several constraints passed in via a ring-buffer of size MaxCacheSize. + /// Assumes Text and TextFormatFlags are the same, if either were to change, a call to + /// InvalidateCache should be made + public Size GetTextSize(string text, Font font, Size proposedConstraints, TextFormatFlags flags) { + + + if (!TextRequiresWordBreak(text, font, proposedConstraints, flags)) { + // Text fits within proposed width + + // IF we're here, this means we've got text that can fit into the proposedConstraints + // without wrapping. We've determined this because our + + // as a side effect of calling TextRequiresWordBreak, + // unconstrainedPreferredSize is set. + return unconstrainedPreferredSize; + } + else { + // Text does NOT fit within proposed width - requires WordBreak + + // IF we're here, this means that the wrapping width is smaller + // than our max width. For example: we measure the text with infinite + // bounding box and we determine the width to fit all the characters + // to be 200 px wide. We would come here only for proposed widths less + // than 200 px. + + + // Create our ring buffer if we dont have one + if (sizeCacheList == null) { + sizeCacheList = new PreferredSizeCache[MaxCacheSize]; + } + + // check the existing constraints from previous calls + foreach (PreferredSizeCache sizeCache in sizeCacheList) { + + if (sizeCache.ConstrainingSize == proposedConstraints) { + return sizeCache.PreferredSize; + } + else if ((sizeCache.ConstrainingSize.Width == proposedConstraints.Width) + && (sizeCache.PreferredSize.Height <= proposedConstraints.Height)) { + + // Caching a common case where the width matches perfectly, and the stored preferred height + // is smaller or equal to the constraining size. + // prefSize = GetPreferredSize(w,Int32.MaxValue); + // prefSize = GetPreferredSize(w,prefSize.Height); + + return sizeCache.PreferredSize; + } + // + } + + // if we've gotten here, it means we dont have a cache entry, therefore + // we should add a new one in the next available slot. + Size prefSize = TextRenderer.MeasureText(text, font, proposedConstraints, flags); + nextCacheEntry = (nextCacheEntry+1)%MaxCacheSize; + sizeCacheList[nextCacheEntry] = new PreferredSizeCache(proposedConstraints, prefSize); + + return prefSize; + + } + + } + + /// GetUnconstrainedSize + /// Gets the unconstrained (Int32.MaxValue, Int32.MaxValue) size for a piece of text + private Size GetUnconstrainedSize(string text, Font font, TextFormatFlags flags) { + + if (unconstrainedPreferredSize == LayoutUtils.InvalidSize) { + // we also investigated setting the SingleLine flag, however this did not yield as much benefit as the word break + // and had possibility of causing internationalization issues. + + flags = (flags & ~TextFormatFlags.WordBreak); // rip out the wordbreak flag + unconstrainedPreferredSize = TextRenderer.MeasureText(text, font, LayoutUtils.MaxSize, flags); + } + return unconstrainedPreferredSize; + } + + + /// TextRequiresWordBreak + /// If you give the text all the space in the world it wants, then there should be no reason + /// for it to break on a word. So we find out what the unconstrained size is (Int32.MaxValue, Int32.MaxValue) + /// for a string - eg. 35, 13. If the size passed in has a larger width than 35, then we know that + /// the WordBreak flag is not necessary. + public bool TextRequiresWordBreak(string text, Font font, Size size, TextFormatFlags flags) { + + // if the unconstrained size of the string is larger than the proposed width + // we need the word break flag, otherwise we dont, its a perf hit to use it. + return GetUnconstrainedSize(text, font, flags).Width > size.Width; + } + + private struct PreferredSizeCache { + public PreferredSizeCache(Size constrainingSize, Size preferredSize) { + this.ConstrainingSize = constrainingSize; + this.PreferredSize = preferredSize; + } + public Size ConstrainingSize; + public Size PreferredSize; + } + + } + + + } + + // Frequently when you need to do a PreformLayout, you also need to invalidate the + // PreferredSizeCache (you are laying out because you know that the action has changed + // the PreferredSize of the control and/or its container). LayoutTransaction wraps both + // of these operations into one, plus adds a check for null to make our code more + // concise. + // + // Usage1: (When we are not calling to other code which may cause a layout:) + // + // LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds); + // + // Usage2: (When we need to wrap code which may cause additional layouts:) + // + // using(new LayoutTransaction(ParentInternal, this, PropertyNames.Bounds)) { + // OnBoundsChanged(); + // } + // + // The second usage spins off 12b for garbage collection, but I did some profiling and + // it didn't seem significant (we were spinning off more from LayoutEventArgs.) + internal sealed class LayoutTransaction : IDisposable { + Control _controlToLayout; + bool _resumeLayout; + +#if DEBUG + int _layoutSuspendCount; +#endif + public LayoutTransaction(Control controlToLayout, IArrangedElement controlCausingLayout, string property) : + this(controlToLayout, controlCausingLayout, property, true) { + } + + public LayoutTransaction(Control controlToLayout, IArrangedElement controlCausingLayout, string property, bool resumeLayout) { + CommonProperties.xClearPreferredSizeCache(controlCausingLayout); + _controlToLayout = controlToLayout; + + _resumeLayout = resumeLayout; + if(_controlToLayout != null) { +#if DEBUG + _layoutSuspendCount = _controlToLayout.LayoutSuspendCount; +#endif + _controlToLayout.SuspendLayout(); + CommonProperties.xClearPreferredSizeCache(_controlToLayout); + + // Same effect as calling performLayout on Dispose but then we would have to keep + // controlCausingLayout and property around as state. + if (resumeLayout) { + _controlToLayout.PerformLayout(new LayoutEventArgs(controlCausingLayout, property)); + } + } + } + + public void Dispose() { + if(_controlToLayout != null) { + _controlToLayout.ResumeLayout(_resumeLayout); + +#if DEBUG + Debug.Assert(_controlToLayout.LayoutSuspendCount == _layoutSuspendCount, "Suspend/Resume layout mismatch!"); +#endif + } + } + + // This overload should be used when a property has changed that affects preferred size, + // but you only want to layout if a certain condition exists (say you want to layout your + // parent because your preferred size has changed). + public static IDisposable CreateTransactionIf(bool condition, Control controlToLayout, IArrangedElement elementCausingLayout, string property) { + if (condition) { + return new LayoutTransaction(controlToLayout, elementCausingLayout, property); + } + else { + CommonProperties.xClearPreferredSizeCache(elementCausingLayout); + return new NullLayoutTransaction(); + + } + } + + + public static void DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, string property) { + if (elementCausingLayout != null) { + CommonProperties.xClearPreferredSizeCache(elementCausingLayout); + if(elementToLayout != null) { + CommonProperties.xClearPreferredSizeCache(elementToLayout); + elementToLayout.PerformLayout(elementCausingLayout, property); + + } + } + Debug.Assert(elementCausingLayout != null, "LayoutTransaction.DoLayout - elementCausingLayout is null, no layout performed - did you mix up your parameters?"); + + } + + + // This overload should be used when a property has changed that affects preferred size, + // but you only want to layout if a certain condition exists (say you want to layout your + // parent because your preferred size has changed). + public static void DoLayoutIf(bool condition, IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, string property) { + if (!condition) { + if (elementCausingLayout != null) { + CommonProperties.xClearPreferredSizeCache(elementCausingLayout); + } + } + else { + LayoutTransaction.DoLayout(elementToLayout, elementCausingLayout, property); + } + } + + } + internal struct NullLayoutTransaction : IDisposable { + public void Dispose() { + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/PropertyNames.cs b/WindowsForms/Managed/System/WinForms/Layout/PropertyNames.cs new file mode 100644 index 000000000..3eb3351a8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/PropertyNames.cs @@ -0,0 +1,160 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + using System.Diagnostics.CodeAnalysis; + + // LayoutEventArgs takes a string for AffectedProperty. This class contains const + // strings to use as property names. Doing this allows us to use reference comparisons + // which is advantageous because 1) pref and 2) we will not accidently collide with + // names that extenders provide. + internal class PropertyNames { + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Alignment = "Alignment"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Anchor = "Anchor"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string AutoScroll = "AutoScroll"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string AutoSize = "AutoSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Appearance = "Appearance"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string AutoEllipsis = "AutoEllipsis"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string BorderStyle = "BorderStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string CellBorderStyle = "CellBorderStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Bounds = "Bounds"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string CheckAlign = "CheckAlign"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ChildIndex = "ChildIndex"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ColumnHeadersHeight = "ColumnHeadersHeight"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ColumnHeadersVisible = "ColumnHeadersVisible"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Columns = "Columns"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ColumnSpan = "ColumnSpan"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ColumnStyles = "ColumnStyles"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Controls = "Controls"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Dock = "Dock"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string DisplayRectangle = "DisplayRectangle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string DisplayStyle = "DisplayStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string DrawMode = "DrawMode"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string DropDownButtonWidth = "DropDownButtonWidth"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string FlatAppearanceBorderSize = "FlatAppearance.BorderSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string FlatStyle = "FlatStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string FlowBreak = "FlowBreak"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string FlowDirection = "FlowDirection"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Font = "Font"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string GripStyle = "GripStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string GrowStyle = "GrowStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Image= "Image"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ImageIndex= "ImageIndex"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ImageScaling= "ImageScaling"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ImageScalingSize= "ImageScalingSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ImageKey= "ImageKey"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ImageAlign = "ImageAlign"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Items = "Items"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string LayoutSettings = "LayoutSettings"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string LinkArea = "LinkArea"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Links = "Links"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string LayoutStyle = "LayoutStyle"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Location = "Location"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Margin = "Margin"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string MaximumSize = "MaximumSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string MinimumSize = "MinimumSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Multiline = "Multiline"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Orientation = "Orientation"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string PreferredSize = "PreferredSize"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Padding = "Padding"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Parent = "Parent"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RightToLeft = "RightToLeft"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RightToLeftLayout = "RightToLeftLayout"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RowHeadersVisible = "RowHeadersVisible"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RowHeadersWidth = "RowHeadersWidth"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Rows = "Rows"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RowSpan = "RowSpan"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string RowStyles = "RowStyles"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Renderer = "Renderer"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ScrollBars = "ScrollBars"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Size = "Size"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ShowDropDownArrow = "ShowDropDownArrow"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ShowImageMargin = "ShowCheckMargin"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string ShowCheckMargin = "ShowCheckMargin"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Spring = "Spring"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Style = "Style"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string TableIndex = "TableIndex"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Text = "Text"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string TextAlign = "TextAlign"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string TextImageRelation = "TextImageRelation"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string UseCompatibleTextRendering = "UseCompatibleTextRendering"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string Visible = "Visible"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string WordWrap = "WordWrap"; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly string WrapContents = "WrapContents"; + } +} diff --git a/WindowsForms/Managed/System/WinForms/Layout/TableLayout.cs b/WindowsForms/Managed/System/WinForms/Layout/TableLayout.cs new file mode 100644 index 000000000..08b35b5cd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Layout/TableLayout.cs @@ -0,0 +1,2120 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Layout { + + using System; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Collections.Generic; + using System.Globalization; + + internal class TableLayout : LayoutEngine { + + // This code was taken from ndp\clr\src\BCL\System\Array.cs - CS#545966 + // This changeset replaced the sorting algorithm when elements with the same + // value are sorted. While Array.Sort() was documented as not a being stable sort, + // we need to preserve the order of same elements as it used to be with the old algorithm since + // some customers are putting more than one control in the same TableLayout cell + // and we rely on this order to resovle the conflict + + private static int GetMedian(int low, int hi) { + // Note both may be negative, if we are dealing with arrays w/ negative lower bounds. + return low + ((hi - low) >> 1); + } + + // Private value type used by the Sort methods. + private struct SorterObjectArray + { + private Object[] keys; + private IComparer comparer; + + internal SorterObjectArray(Object[] keys, IComparer comparer) { + if (comparer == null) comparer = Comparer.Default; + this.keys = keys; + this.comparer = comparer; + } + + internal void SwapIfGreaterWithItems(int a, int b) { + if (a != b) { + try { + if (comparer.Compare(keys[a], keys[b]) > 0) { + Object temp = keys[a]; + keys[a] = keys[b]; + keys[b] = temp; + } + } + catch (IndexOutOfRangeException) { + throw new ArgumentException(); + } + catch (Exception) { + throw new InvalidOperationException(); + } + } + } + + + internal void QuickSort(int left, int right) { + // Can use the much faster jit helpers for array access. + do { + int i = left; + int j = right; + + // pre-sort the low, middle (pivot), and high values in place. + // this improves performance in the face of already sorted data, or + // data that is made up of multiple sorted runs appended together. + int middle = GetMedian(i, j); + SwapIfGreaterWithItems(i, middle); // swap the low with the mid point + SwapIfGreaterWithItems(i, j); // swap the low with the high + SwapIfGreaterWithItems(middle, j); // swap the middle with the high + + Object x = keys[middle]; + do { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try { + while (comparer.Compare(keys[i], x) < 0) i++; + while (comparer.Compare(x, keys[j]) < 0) j--; + } + catch (IndexOutOfRangeException) { + throw new ArgumentException(); + } + catch (Exception) { + throw new InvalidOperationException(); + } + if (i > j) break; + if (i < j) { + Object key = keys[i]; + keys[i] = keys[j]; + keys[j] = key; + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + } + + private static void Sort(Object[] array, IComparer comparer) { + if (array == null) + throw new ArgumentNullException("array"); + + if (array.Length > 1) { + SorterObjectArray sorter = new SorterObjectArray(array, comparer); + sorter.QuickSort(0, array.Length - 1); + } + } + + // End of sorting code + + // Singleton instance shared by all containers. + internal static readonly TableLayout Instance = new TableLayout(); + + private static readonly int _containerInfoProperty = PropertyStore.CreateKey(); + private static readonly int _layoutInfoProperty = PropertyStore.CreateKey(); + + + private static string[] _propertiesWhichInvalidateCache = new string[] { + //suspend layout before changing one of the above property will cause the AffectedProperty of LayoutEventArgs to be set to null + //for more information, see http://wiki/default.aspx/Microsoft.Projects.DotNetClient.LayoutEventArgs + + null, + PropertyNames.ChildIndex, // Changing Z-order changes the row/column assignments + PropertyNames.Parent, // So does adding/removing controls + PropertyNames.Visible, // as well as visibility + PropertyNames.Items, // VSWhidbey 396702: Changing toolstrip items collection + PropertyNames.Rows, + PropertyNames.Columns, + PropertyNames.RowStyles, + PropertyNames.ColumnStyles, + // RowSpan, ColumnSpan, TableIndex manually call ClearCachedAssignments. + }; + + internal static TableLayoutSettings CreateSettings(IArrangedElement owner) { + return new TableLayoutSettings(owner); + } + + internal override void ProcessSuspendedLayoutEventArgs(IArrangedElement container, LayoutEventArgs args) { + ContainerInfo containerInfo = GetContainerInfo(container); + + foreach(string propertyName in _propertiesWhichInvalidateCache) { + if(Object.ReferenceEquals(args.AffectedProperty, propertyName)) { + ClearCachedAssignments(containerInfo); + break; + } + } + } + + + /// + /// LayoutCore: EntryPoint from LayoutEngine. + /// Container: IArrangedElement to layout (could be table layout panel but doesnt have to be - eg. ToolStrip) + /// LayoutEventArgs: args created from PerformLayout. + /// + /// Summary of algorithm: + /// (1). Determine the row and column assignments of all the children of the container. (This can be cached) + /// (2). Apply column styles, then row styles for all the rows: + /// (a). Create a list of column or row sizes (Strip[]) - initialize absolute columns/rows sizes. + /// (b). Determine the minimum size of all the controls + /// (c). Determine the maximum size of all the controls + /// (d). Distribute the minimum size of the control to the corresponding Strip for column or row + /// (e). Distribute the remaining size to the column or row as according to the Row/Column style. + /// (3). Expand the last row/column to fit the table + /// (4). Set the bounds of the child elements as according to the row/column heights specified in Strip[] + /// (a) Calculate bounds of item + /// (b) Align and stretch item to fill column/row as according to Dock&Anchor properties. + /// + internal override bool LayoutCore(IArrangedElement container, LayoutEventArgs args) { + + ProcessSuspendedLayoutEventArgs(container, args); + + ContainerInfo containerInfo = GetContainerInfo(container); + EnsureRowAndColumnAssignments(container, containerInfo, /* doNotCache = */false); + + int cellBorderWidth = containerInfo.CellBorderWidth; + + //shrink the size of the display rectangle so that we have space to draw the border + Size containerSize = container.DisplayRectangle.Size - new Size(cellBorderWidth, cellBorderWidth); + + //make sure our sizes are non-negative + containerSize.Width = Math.Max(containerSize.Width, 1); + containerSize.Height = Math.Max(containerSize.Height, 1); + + Size usedSpace = ApplyStyles(containerInfo, containerSize, /*measureOnly=*/false); + ExpandLastElement(containerInfo, usedSpace, containerSize); + RectangleF displayRect = (RectangleF)container.DisplayRectangle; + displayRect.Inflate(-(cellBorderWidth / 2.0f), -(cellBorderWidth) / 2.0f); + SetElementBounds(containerInfo, displayRect); + + // ScrollableControl will first try to get the layoutbounds from the derived control when + // trying to figure out if ScrollBars should be added. + // VSWhidbey #392913 + CommonProperties.SetLayoutBounds(containerInfo.Container, new Size(SumStrips(containerInfo.Columns, 0, containerInfo.Columns.Length), + SumStrips(containerInfo.Rows, 0, containerInfo.Rows.Length))); + + return CommonProperties.GetAutoSize(container); + } + + + /// + /// GetPreferredSize: Called on the container to determine the size that best fits its contents. + /// Container: IArrangedElement to determine preferredSize (could be table layout panel but doesnt have to be - eg. ToolStrip) + /// ProposedContstraints: the suggested size that the table layout should fit into. If either argument is 0, + /// TableLayout pretends it's unconstrained for perfomance reasons. + /// + /// Summary of Algorithm: + /// Similar to LayoutCore. Row/Column assignments are NOT cached. TableLayout uses AGRESSIVE + /// caching for performance reasons. + /// + internal override Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) { + + ContainerInfo containerInfo = GetContainerInfo(container); + + // PERF: Optimizing nested table layouts. + // The problem: TableLayout asks for GPS(0,0) GPS(1,0) GPS(0,1) and GPS(w,0) and GPS(w,h). + // if the table layout is nested, this becomes pretty nasty, as we dont cache row, column + // assignments in preferred size. + // GPS(0,1) GPS(1,0) should return same as GPS(0,0)- if that's already cached return it. + bool isCacheValid = false; + float oldWidth = -1f; + Size prefSize = containerInfo.GetCachedPreferredSize(proposedConstraints, out isCacheValid); + if(isCacheValid) { + return prefSize; + } + + //create a dummy containerInfo and we operate on the dummy containerInfo only + //the reason to do so is that when we call ApplyStyles, we change the information + //in the containerInfo. Specifically it will change the strip size so that the + //size information may no longer be accurate. + ContainerInfo tempInfo = new ContainerInfo(containerInfo); + int cellBorderWidth = containerInfo.CellBorderWidth; + + //pretend the last column is the size of the container if it is absolutely sized + //Whidbey 341438 + if (containerInfo.MaxColumns == 1 && containerInfo.ColumnStyles.Count > 0 && containerInfo.ColumnStyles[0].SizeType == SizeType.Absolute) { + //shrink the size of the display rectangle so that we have space to draw the border + Size containerSize = container.DisplayRectangle.Size - new Size(cellBorderWidth * 2, cellBorderWidth * 2); + + //make sure our sizes are non-negative + containerSize.Width = Math.Max(containerSize.Width, 1); + containerSize.Height = Math.Max(containerSize.Height, 1); + oldWidth = containerInfo.ColumnStyles[0].Size; + containerInfo.ColumnStyles[0].SetSize(Math.Max(oldWidth, Math.Min(proposedConstraints.Width, containerSize.Width))); + } + + //notice we always assign rows and columns here since the user may change the properties of the table while the layout is + //suspended, so we have no way to know whether the cache is invalid or not here. + EnsureRowAndColumnAssignments(container, tempInfo, /* doNotCache = */ true); + + //deduct the padding for cell border before doing layout + Size cellBorderSize = new Size(cellBorderWidth, cellBorderWidth); + proposedConstraints -= cellBorderSize; + proposedConstraints.Width = Math.Max(proposedConstraints.Width, 1); + proposedConstraints.Height = Math.Max(proposedConstraints.Height, 1); + if (tempInfo.Columns != null && containerInfo.Columns != null && (tempInfo.Columns.Length != containerInfo.Columns.Length)) + { + ClearCachedAssignments(containerInfo); + } + if (tempInfo.Rows != null && containerInfo.Rows != null && (tempInfo.Rows.Length != containerInfo.Rows.Length)) + { + ClearCachedAssignments(containerInfo); + } + + prefSize = ApplyStyles(tempInfo, proposedConstraints, /*measureOnly=*/true); + //prefSize = MeasureStyles(tempInfo); + + //restores the old strip size + if (oldWidth >= 0) { + containerInfo.ColumnStyles[0].SetSize(oldWidth); + } + + //add back the padding for the cell border + return (prefSize + cellBorderSize); + } + + /// + /// EnsureRowAndColumnAssignments: Sets up Row/Column assignments for all the children of the container + /// - Does nothing if Cache is valid + /// - sets RowStart,RowSpan,ColumnStart,ColumnSpan into the LayoutInfo[] collection (containerInfo.ChildrenInfo) + /// + private void EnsureRowAndColumnAssignments(IArrangedElement container, ContainerInfo containerInfo, bool doNotCache) { + // Assign new rows and columns if the cache is invalid or if we are in GetPreferredSize + if(!HasCachedAssignments(containerInfo) || doNotCache) { + AssignRowsAndColumns(containerInfo); + } + + Debug_VerifyAssignmentsAreCurrent(container, containerInfo); + + } + + /// + /// ExpandLastElement: expands the row/column to fill the rest of the space in the container. + /// + private void ExpandLastElement(ContainerInfo containerInfo, Size usedSpace, Size totalSpace) { + Strip[] rows = containerInfo.Rows; + Strip[] cols = containerInfo.Columns; + if (cols.Length != 0 && totalSpace.Width > usedSpace.Width) { + cols[cols.Length - 1].MinSize += totalSpace.Width - usedSpace.Width; + } + if (rows.Length != 0 && totalSpace.Height > usedSpace.Height) { + rows[rows.Length - 1].MinSize += totalSpace.Height - usedSpace.Height; + } + } + + /// + /// AssignRowsAndColumns: part of EnsureRowAndColumnAssignments. + /// determines the number of rows and columns we need to create + /// + private void AssignRowsAndColumns(ContainerInfo containerInfo) { + + int numCols = containerInfo.MaxColumns; + int numRows = containerInfo.MaxRows; + + // forces iteration over child collection - this is cached and + // sets containerInfo MinRowsAndColumns and MinColumns/MinRows + LayoutInfo[] childrenInfo = containerInfo.ChildrenInfo; + int minSpace = containerInfo.MinRowsAndColumns; + int minColumn = containerInfo.MinColumns; + int minRow = containerInfo.MinRows; + + + TableLayoutPanelGrowStyle growStyle = containerInfo.GrowStyle; + if (growStyle == TableLayoutPanelGrowStyle.FixedSize) { + //if we're a fixed size - check to see if we have enough room + if (containerInfo.MinRowsAndColumns > numCols * numRows) { + throw new ArgumentException(SR.GetString(SR.TableLayoutPanelFullDesc)); + } + + if ((minColumn > numCols) || (minRow > numRows)) { + throw new ArgumentException(SR.GetString(SR.TableLayoutPanelSpanDesc)); + } + + numRows = Math.Max(1, numRows); + numCols = Math.Max(1, numCols); + // numRows = numRows - numRows indicates that columns are specified and rows should grow + } + else if (growStyle == TableLayoutPanelGrowStyle.AddRows) { + numRows = 0;//indicates that columns are specified and rows should grow + } + else {//must be addcolumns + numCols = 0;//indicates that rows are specified and columns should grow + } + + if(numCols > 0) { + // The user specified the number of column (the simple/fast case) + xAssignRowsAndColumns(containerInfo, childrenInfo, numCols, numRows == 0 ? Int32.MaxValue : numRows, growStyle); + } else if(numRows > 0) { + // The user specified rows only (we need to compute the number of columns) + + int estimatedCols = Math.Max((int)Math.Ceiling((float)minSpace / numRows), minColumn); + + //make sure that estimatedCols is positive + estimatedCols = Math.Max(estimatedCols, 1); + + while(!xAssignRowsAndColumns(containerInfo, childrenInfo, estimatedCols, numRows, growStyle)) { + // I am assuming that the division will put us pretty close to the right + // number of columns. If this assumption is wrong, a binary search + // between the number we get by dividing and childrenInfos.Count + // could be more efficient. It would certainly degenerate better. + estimatedCols++; + } + } else { + // No rows or columns specified, just do a vertical stack. + xAssignRowsAndColumns(containerInfo, childrenInfo, /* numCols = */ Math.Max(minColumn,1), /* numRows = */ Int32.MaxValue, growStyle); + } + } + + + + + /// + /// xAssignRowsAndColumns: part of AssignRowsAndColumns. + /// def: fixed element: has a specific row/column assignment (assigned by SetRow,SetColumn, or Add(c,row,column) + /// def: flow element: does NOT have a specific row/column assignment. + /// + /// Determines the placement of fixed and flow elements. Walks through the rows/columns - if there's a + /// spot for the fixed element, place it, else place the next flow element. + /// + private bool xAssignRowsAndColumns(ContainerInfo containerInfo, LayoutInfo[] childrenInfo, int maxColumns, int maxRows, TableLayoutPanelGrowStyle growStyle) { + Debug.Assert(maxColumns > 0, "maxColumn must be positive"); + + int numColumns = 0; + int numRows = 0; + ReservationGrid reservationGrid = new ReservationGrid(); + + int currentRow = 0; + int currentCol = 0; + + int fixedElementIndex = -1; + int flowElementIndex = -1; + + // make sure to snap these two collections as we're not in a "containerInfo.Valid" state + // so we'll wind up building up the lists over and over again. + LayoutInfo[] fixedChildrenInfo = containerInfo.FixedChildrenInfo; + + + //the element at the head of the absolutely positioned element queue + LayoutInfo fixedElement = GetNextLayoutInfo(fixedChildrenInfo, ref fixedElementIndex, /*absolutelyPositioned*/true); + + //the element at the head of the non-absolutely positioned element queue + LayoutInfo flowElement = GetNextLayoutInfo(childrenInfo, ref flowElementIndex, /*absolutelyPositioned*/false); + + while (fixedElement != null || flowElement != null) { + + int colStop = currentCol; + int rowStop; + if (flowElement != null) { + flowElement.RowStart = currentRow; + flowElement.ColumnStart = currentCol; + //try to layout the flowElement to see if it overlaps with the fixedElement + AdvanceUntilFits(maxColumns, reservationGrid, flowElement, out colStop); + //we have exceeded the row limit. just return + if (flowElement.RowStart >= maxRows) { + return false; + } + } + //test to see if either the absolutely positioned element is null or it fits. + if (flowElement != null && (fixedElement == null || (!IsCursorPastInsertionPoint(fixedElement, flowElement.RowStart, colStop) && !IsOverlappingWithReservationGrid(fixedElement, reservationGrid, currentRow)))) { + //Place the flow element. + + //advance the rows in reservation grid + for (int j = 0; j < flowElement.RowStart - currentRow; j++) { + reservationGrid.AdvanceRow(); + } + + currentRow = flowElement.RowStart; + rowStop = Math.Min(currentRow + flowElement.RowSpan, maxRows); + + //reserve spaces in the reservationGrid + reservationGrid.ReserveAll(flowElement, rowStop, colStop); + flowElement = GetNextLayoutInfo(childrenInfo, ref flowElementIndex, /*absolutelyPositioned*/false); + } + else { + // + //otherwise we place the fixed element. + // + if (currentCol >= maxColumns) { + //we have already passed the boundary. Go to next row + currentCol = 0; + currentRow++; + reservationGrid.AdvanceRow(); + } + //set the rowStart and columnStart to fixedElement's specifed position + fixedElement.RowStart = Math.Min(fixedElement.RowPosition, maxRows - 1); + fixedElement.ColumnStart = Math.Min(fixedElement.ColumnPosition, maxColumns - 1); + if (currentRow > fixedElement.RowStart) { + //we have already passed the specifed position. set the start column to the current column + fixedElement.ColumnStart = currentCol; + } + else if (currentRow == fixedElement.RowStart) { + //set the start column to be the max of the specifed column and current column + fixedElement.ColumnStart = Math.Max(fixedElement.ColumnStart, currentCol); + } + else { + //set the start column to the specified column, which we have already done + } + fixedElement.RowStart = Math.Max(fixedElement.RowStart, currentRow); + + //advance the reservation grid + int j; + for (j = 0; j < fixedElement.RowStart - currentRow; j++) { + reservationGrid.AdvanceRow(); + } + + //try to layout the absolutely positioned element as if it were non-absolutely positioned. + //In this way we can tell whether this element overlapps with others or fits on the table. + AdvanceUntilFits(maxColumns, reservationGrid, fixedElement, out colStop); + + //we have exceeded the row limit. just return + if (fixedElement.RowStart >= maxRows) { + return false; + } + + for (; j < fixedElement.RowStart - currentRow; j++) { + //advance the reservation grid if the fixedElement's row position has changed during layout + reservationGrid.AdvanceRow(); + } + currentRow = fixedElement.RowStart; + + //make sure that we truncate the element's column span if it is too big + colStop = Math.Min(fixedElement.ColumnStart + fixedElement.ColumnSpan, maxColumns); + rowStop = Math.Min(fixedElement.RowStart + fixedElement.RowSpan, maxRows); + + //reserve space in the reservation grid + reservationGrid.ReserveAll(fixedElement, rowStop, colStop); + + fixedElement = GetNextLayoutInfo(fixedChildrenInfo, ref fixedElementIndex, /*absolutelyPositioned*/true); + } + currentCol = colStop; + numRows = (numRows == Int32.MaxValue) ? rowStop : Math.Max(numRows, rowStop); + numColumns = (numColumns == Int32.MaxValue) ? colStop : Math.Max(numColumns, colStop); + } + + Debug.Assert(numRows <= maxRows, "number of rows allocated shouldn't exceed max number of rows"); + Debug.Assert(numColumns <= maxColumns, "number of columns allocated shouldn't exceed max number of columns"); + + + // we should respect columncount and rowcount as according to GrowStyle. + if (growStyle == TableLayoutPanelGrowStyle.FixedSize) { + // now that we've calculated the assignments - use the "max" as the actual number of rows. + numColumns = maxColumns; + numRows = maxRows; + } + else if (growStyle == TableLayoutPanelGrowStyle.AddRows) { + numColumns = maxColumns; + numRows = Math.Max(containerInfo.MaxRows, numRows); + } + else { // add columns + numRows = (maxRows == Int32.MaxValue) ? numRows : maxRows; + numColumns = Math.Max(containerInfo.MaxColumns, numColumns); + } + + // PERF: prevent overallocation of Strip[] arrays. We're going to null these guys out + // anyways... so only allocate when the number of rows and columns is different. + if (containerInfo.Rows == null || containerInfo.Rows.Length != numRows) { + containerInfo.Rows = new Strip[numRows]; + } + if (containerInfo.Columns == null || containerInfo.Columns.Length != numColumns) { + containerInfo.Columns = new Strip[numColumns]; + } + + containerInfo.Valid = true; + return true; + + } + + /// + /// GetNextLayoutInfo: part of xAssignRowsAndColumns. + /// helper function that walks through the collection picking out the next flow element or fixed element. + /// + private static LayoutInfo GetNextLayoutInfo(LayoutInfo[] layoutInfo, ref int index, bool absolutelyPositioned) { + + for (int i = ++index; i < layoutInfo.Length; i++) { + if (absolutelyPositioned == layoutInfo[i].IsAbsolutelyPositioned) { + index = i; + return layoutInfo[i]; + } + } + index = layoutInfo.Length; + return null; + } + + + /// + /// IsCursorPastInsertionPoint: part of xAssignRowsAndColumns. + /// check to see if the user specified location for fixedLayoutInfo has passed the insertion point specified by the cursor + /// + private bool IsCursorPastInsertionPoint(LayoutInfo fixedLayoutInfo, int insertionRow, int insertionCol) { + Debug.Assert(fixedLayoutInfo.IsAbsolutelyPositioned, "should only check for those elements which are absolutely positioned"); + + //if the element is bumped to a row below its specified row position, it means that the element overlaps with previous controls + if (fixedLayoutInfo.RowPosition < insertionRow) { + return true; + } + //if the element is bumped to a column after its specified column position, it also means that the element overlaps with previous controls + if (fixedLayoutInfo.RowPosition == insertionRow && fixedLayoutInfo.ColumnPosition < insertionCol) { + return true; + } + return false; + } + + /// + /// IsOverlappingWithReservationGrid: part of xAssignRowsAndColumns. + /// check to see if the absolutely positioned layoutInfo fits in the reservation grid + /// + private bool IsOverlappingWithReservationGrid(LayoutInfo fixedLayoutInfo, ReservationGrid reservationGrid, int currentRow) { + //since we shall not put anything above our current row, this means that the fixedLayoutInfo overlaps with something already placed on the table + if (fixedLayoutInfo.RowPosition < currentRow) { + return true; + } + for (int rowOffset = fixedLayoutInfo.RowPosition - currentRow; rowOffset < fixedLayoutInfo.RowPosition - currentRow + fixedLayoutInfo.RowSpan; rowOffset++) { + for (int colOffset = fixedLayoutInfo.ColumnPosition; colOffset < fixedLayoutInfo.ColumnPosition + fixedLayoutInfo.ColumnSpan; colOffset++) { + if (reservationGrid.IsReserved(colOffset, rowOffset)) { + return true; + } + } + } + return false; + } + + /// + /// AdvanceUntilFits: part of xAssignRowsAndColumns. + /// Advances the position of layoutInfo until we have enough space and do not + /// collide with a rowSpanned element. ColStop will be the column on which the + /// element ends (exclusive). + /// + private void AdvanceUntilFits(int maxColumns, ReservationGrid reservationGrid, LayoutInfo layoutInfo, out int colStop) { + int prevRow = layoutInfo.RowStart; + do { + GetColStartAndStop(maxColumns, reservationGrid, layoutInfo, out colStop); + } while(ScanRowForOverlap(maxColumns, reservationGrid, layoutInfo, colStop, layoutInfo.RowStart - prevRow)); + } + + /// + /// GetColStartAndStop: part of xAssignRowsAndColumns. + /// + /// + private void GetColStartAndStop(int maxColumns, ReservationGrid reservationGrid, LayoutInfo layoutInfo, out int colStop) { + // Compute the column our element ends on + colStop = layoutInfo.ColumnStart + layoutInfo.ColumnSpan; + if(colStop > maxColumns) { + if(layoutInfo.ColumnStart != 0) { + // If we are not already at the beginning or a row, move down + // to the next row. + layoutInfo.ColumnStart = 0; + layoutInfo.RowStart++; + } + // Cap colStop in case we have a element too large to fit on any row. + colStop = Math.Min(layoutInfo.ColumnSpan, maxColumns); + } + } + + private bool ScanRowForOverlap(int maxColumns, ReservationGrid reservationGrid, LayoutInfo layoutInfo, int stopCol, int rowOffset) { + for(int i = layoutInfo.ColumnStart; i < stopCol; i++) { + if(reservationGrid.IsReserved(i, rowOffset)) { + // If we hit reserved space, advance startCol past it. If we hit the end of the row, + // just stop. AdvanceUntilFits will move to the next row and call us again. + for(layoutInfo.ColumnStart = i + 1; + layoutInfo.ColumnStart < maxColumns && reservationGrid.IsReserved(layoutInfo.ColumnStart, rowOffset); + layoutInfo.ColumnStart++); + return true; + } + } + return false; + } + + private Size ApplyStyles(ContainerInfo containerInfo, Size proposedConstraints, bool measureOnly) { + Size preferredSize = Size.Empty; + + //allocate space for all absolutely sized strips. Set the size of the rest of the strips to 0. + InitializeStrips(containerInfo.Columns, containerInfo.ColumnStyles); + InitializeStrips(containerInfo.Rows, containerInfo.RowStyles); + + // perf optimization - detect if we should worry about column spans + containerInfo.ChildHasColumnSpan = false; + containerInfo.ChildHasRowSpan = false; + + //detect all rows/columns which have control starting from it + foreach(LayoutInfo layoutInfo in containerInfo.ChildrenInfo) { + containerInfo.Columns[layoutInfo.ColumnStart].IsStart = true; + containerInfo.Rows[layoutInfo.RowStart].IsStart = true; + if (layoutInfo.ColumnSpan > 1) { + containerInfo.ChildHasColumnSpan = true; + } + if (layoutInfo.RowSpan > 1) { + containerInfo.ChildHasRowSpan = true; + } + } + preferredSize.Width = InflateColumns(containerInfo, proposedConstraints, measureOnly); + int expandLastElementWidth = Math.Max(0,proposedConstraints.Width - preferredSize.Width); + preferredSize.Height = InflateRows(containerInfo, proposedConstraints, expandLastElementWidth, measureOnly); + return preferredSize; + } + + //allocate space for all absolutely sized strips. Set the size of the rest of the strips to 0. + private void InitializeStrips(Strip[] strips, IList styles) { + Strip strip; + for (int i = 0; i < strips.Length; i++) { + TableLayoutStyle style = i < styles.Count ? (TableLayoutStyle)styles[i] : null; + strip = strips[i]; + if (style != null && style.SizeType == SizeType.Absolute) { + strip.MinSize = (int)Math.Round((double)((TableLayoutStyle)styles[i]).Size); + strip.MaxSize = strip.MinSize; + } + else { + strip.MinSize = 0; + strip.MaxSize = 0; + } + strip.IsStart = false; + strips[i] = strip; + } + } + + private int InflateColumns(ContainerInfo containerInfo, Size proposedConstraints, bool measureOnly) { + + bool dontHonorConstraint = measureOnly; + + LayoutInfo[] sortedChildren = containerInfo.ChildrenInfo; + if (containerInfo.ChildHasColumnSpan) { + Sort(sortedChildren, ColumnSpanComparer.GetInstance); + } + + // Normally when we are called from GetPreferredSize (measureOnly is true), we want to treat ourselves + // as being unbounded. But in some scenarios, we actually do want to honor the constraints. When + // we are docked or anchored in the right combination, and our parent's layout engine supports + // dock and anchoring, and we are actually constrained in at least one direction, + // then we should honor the constraint. VSWhidbey #498627. + + // The Int16.MaxValue check will tell us whether we are actually constrained or not. It's a bit of a hack, + // but it should be okay. + if (dontHonorConstraint && (proposedConstraints.Width < Int16.MaxValue)) { + TableLayoutPanel tlp = containerInfo.Container as TableLayoutPanel; + if (tlp != null && tlp.ParentInternal != null && tlp.ParentInternal.LayoutEngine == DefaultLayout.Instance) { + if (tlp.Dock == DockStyle.Top || tlp.Dock == DockStyle.Bottom || tlp.Dock == DockStyle.Fill) { + dontHonorConstraint = false; // we want to honor constraints + } + + if ((tlp.Anchor & (AnchorStyles.Left | AnchorStyles.Right)) == (AnchorStyles.Left | AnchorStyles.Right)) { + dontHonorConstraint = false; // we want to honor constraints + } + } + } + + foreach(LayoutInfo layoutInfo in sortedChildren) { + IArrangedElement element = layoutInfo.Element; + + int columnSpan = layoutInfo.ColumnSpan; + + // since InitializeStrips already allocated absolutely sized columns, we can skip over them + if (columnSpan > 1 || !IsAbsolutelySized(layoutInfo.ColumnStart, containerInfo.ColumnStyles)) { + int minWidth =0, maxWidth = 0; + // optimize for the case where one of the parameters is known. + if ((columnSpan == 1 && layoutInfo.RowSpan == 1) && + (IsAbsolutelySized(layoutInfo.RowStart, containerInfo.RowStyles))) { + int constrainingHeight = (int)containerInfo.RowStyles[layoutInfo.RowStart].Size; + minWidth = GetElementSize(element, new Size(0, constrainingHeight)).Width; + maxWidth = minWidth; + } + else { + minWidth = GetElementSize(element, new Size(1, 0)).Width; + maxWidth = GetElementSize(element, Size.Empty).Width; + } + + // tack on margins + Padding margin = CommonProperties.GetMargin(element); + minWidth += margin.Horizontal; + maxWidth += margin.Horizontal; + + // distribute the minimum size, then maximum size over the columns + int colStop = Math.Min(layoutInfo.ColumnStart + layoutInfo.ColumnSpan, containerInfo.Columns.Length); + DistributeSize(containerInfo.ColumnStyles, containerInfo.Columns, layoutInfo.ColumnStart, colStop, minWidth, maxWidth, containerInfo.CellBorderWidth); + } + } + + int width = DistributeStyles(containerInfo.CellBorderWidth, containerInfo.ColumnStyles, containerInfo.Columns, proposedConstraints.Width, dontHonorConstraint); + + // VSWhidbey 344609 - TLP doesn't honor proposedConstraints + if (dontHonorConstraint && width > proposedConstraints.Width && proposedConstraints.Width > 1) { + // Step 1: iterate through the rows or columns, + // - calculate the amount of space allocated + // - for percent size columns + // - sum up the total "%"s being used + // - eg totalPercent = 22 + 22 + 22 = 66%. each column should take up 1/3 of the remaining space. + + Strip[] strips = containerInfo.Columns; + float totalPercent = 0; + int totalPercentAllocatedSpace = 0; + TableLayoutStyleCollection styles = containerInfo.ColumnStyles; + + for(int i = 0; i < strips.Length; i++) { + Strip strip = strips[i]; + if(i < styles.Count) { + TableLayoutStyle style = (TableLayoutStyle) styles[i]; + if (style.SizeType == SizeType.Percent) { + totalPercent += style.Size; + totalPercentAllocatedSpace += strip.MinSize; + } + } + } + + // We will attempt to steal from percentage size columns in order to + // meet the proposed constraints as closely as possible + int currentOverflow = width - proposedConstraints.Width; + + int stealAmount = Math.Min(currentOverflow, totalPercentAllocatedSpace); + + for (int i = 0; i < strips.Length; i++) { + if(i < styles.Count) { + TableLayoutStyle style = (TableLayoutStyle) styles[i]; + if (style.SizeType == SizeType.Percent) { + float percentageOfTotal = style.Size / (float) totalPercent; + strips[i].MinSize -= (int) (percentageOfTotal * stealAmount); + } + } + } + + return width - stealAmount; + } + + return width; + } + + private int InflateRows(ContainerInfo containerInfo, Size proposedConstraints, int expandLastElementWidth, bool measureOnly) { + bool dontHonorConstraint = measureOnly; + + LayoutInfo[] sortedChildren = containerInfo.ChildrenInfo; + if (containerInfo.ChildHasRowSpan) { + Sort(sortedChildren, RowSpanComparer.GetInstance); + } + + bool multiplePercent = containerInfo.HasMultiplePercentColumns; + + // Normally when we are called from GetPreferredSize (measureOnly is true), we want to treat ourselves + // as being unbounded. But in some scenarios, we actually do want to honor the constraints. When + // we are docked or anchored in the right combination, and our parent's layout engine supports + // dock and anchoring, and we are actually constrained in at least one direction, + // then we should honor the constraint. VSWhidbey #498627. + + // The Int16.MaxValue check will tell us whether we are actually constrained or not. It's a bit of a hack, + // but it should be okay. + if (dontHonorConstraint && (proposedConstraints.Height < Int16.MaxValue)) { + TableLayoutPanel tlp = containerInfo.Container as TableLayoutPanel; + if (tlp != null && tlp.ParentInternal != null && tlp.ParentInternal.LayoutEngine == DefaultLayout.Instance) { + if (tlp.Dock == DockStyle.Left || tlp.Dock == DockStyle.Right || tlp.Dock == DockStyle.Fill) { + dontHonorConstraint = false; // we want to honor constraints + } + + if ((tlp.Anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) { + dontHonorConstraint = false; // we want to honor constraints + } + } + } + + foreach(LayoutInfo layoutInfo in sortedChildren) { + IArrangedElement element = layoutInfo.Element; + int rowSpan = layoutInfo.RowSpan; + + // we can skip over absolute row styles, as they've been preallocated + if (rowSpan > 1 || !IsAbsolutelySized(layoutInfo.RowStart, containerInfo.RowStyles)) { + + int currentWidth = SumStrips(containerInfo.Columns, layoutInfo.ColumnStart, layoutInfo.ColumnSpan); + //make sure that the total width is the actual final width to avoid + //inconsistency of width between the ApplyStyles and SetElementBounds + // VSWhidbey 382647 - only apply when there is one multiple percentage column + if (!dontHonorConstraint && layoutInfo.ColumnStart + layoutInfo.ColumnSpan >= containerInfo.MaxColumns && !multiplePercent) { + currentWidth += expandLastElementWidth; + } + + // since we know the width at this point, use that as the constraining width. + Padding margin = CommonProperties.GetMargin(element); + int minHeight = GetElementSize(element, new Size(currentWidth-margin.Horizontal, 0)).Height + margin.Vertical; + int maxHeight = minHeight; + int rowStop = Math.Min(layoutInfo.RowStart + layoutInfo.RowSpan, containerInfo.Rows.Length); + DistributeSize(containerInfo.RowStyles, containerInfo.Rows, layoutInfo.RowStart, rowStop, minHeight, maxHeight, containerInfo.CellBorderWidth); + } + } + return DistributeStyles(containerInfo.CellBorderWidth, containerInfo.RowStyles, containerInfo.Rows, proposedConstraints.Height, dontHonorConstraint); + + } + + + private Size GetElementSize(IArrangedElement element, Size proposedConstraints) { + if(CommonProperties.GetAutoSize(element)) { + return element.GetPreferredSize(proposedConstraints); + } else { + return CommonProperties.GetSpecifiedBounds(element).Size; + } + } + + internal int SumStrips(Strip[] strips, int start, int span) { + int size = 0; + for(int i = start; i < Math.Min(start + span, strips.Length); i++) { + Strip strip = strips[i]; + size += strip.MinSize; + } + return size; + } + + //set the minimum size for each element + private void DistributeSize(IList styles, Strip[] strips, int start, int stop, int min, int max, int cellBorderWidth) { + Debug.Assert(min <= max, "Error computing min/max strip size. (Min expected to be less than max)."); + + xDistributeSize(styles, strips, start, stop, min, MinSizeProxy.GetInstance, cellBorderWidth); + xDistributeSize(styles, strips, start, stop, max, MaxSizeProxy.GetInstance, cellBorderWidth); + } + + private void xDistributeSize(IList styles, Strip[] strips, int start, int stop, int desiredLength, SizeProxy sizeProxy, int cellBorderWidth) { + + int currentLength = 0; //total length allocated so far + int numUninitializedStrips = 0; //number of strips whose Size is 0 and is not absolutely positioned + + //subtract the space for cell borders. Notice if a control spans two columns its + //proposed size is 10 and the border width is 3, we actually only need to distribute + //7 pixels among the two cells it spans + desiredLength -= cellBorderWidth * (stop - start - 1); + desiredLength = Math.Max(0, desiredLength); + + for(int i = start; i < stop; i++) { + sizeProxy.Strip = strips[i]; + if (!IsAbsolutelySized(i, styles) && sizeProxy.Size == 0) { + //the strip is not absolutely sized and it hasn't been initialized + numUninitializedStrips++; + } + currentLength += sizeProxy.Size; + } + int missingLength = desiredLength - currentLength; + + if (missingLength <= 0) { + //no extra space left. Simply return. + return; + } + + if (numUninitializedStrips == 0) { + //look for any strip whose style is percent. If there is one, dump all space in it + int lastPercent; + for (lastPercent = stop - 1; lastPercent >= start; lastPercent--) { + if (lastPercent < styles.Count && ((TableLayoutStyle) styles[lastPercent]).SizeType == SizeType.Percent) { + break; + } + } + //we have found one strip whose style is percent. + //make sure that the for loop below only looks at this strip + if (lastPercent != start - 1) { + stop = lastPercent + 1; + } + //every strip has absolute width or all of them have already been allocated space + //walk backwards + for (int i = stop - 1; i >= start; i--) { + if (!IsAbsolutelySized(i, styles)) { + //dump the extra space to this strip + sizeProxy.Strip = strips[i]; + + if (!(i == strips.Length - 1) //this is not the last strip + && !strips[i+1].IsStart //and there is no control starting from the strip next to it + && !IsAbsolutelySized(i+1, styles)) { // and the strip next to it is not absolutely sized + //try to "borrow" space from the strip next to it + sizeProxy.Strip = strips[i+1]; + int offset = Math.Min(sizeProxy.Size, missingLength); + sizeProxy.Size -= offset; + strips[i+1] = sizeProxy.Strip; + + sizeProxy.Strip = strips[i]; + } + //put whatever left into this strip + sizeProxy.Size += missingLength; + strips[i] = sizeProxy.Strip; + break; + } + } + //if we fall through here, everything is absolutely positioned. discard the extra space + } + //there are some uninitialized strips + else { + //average space to be distributed + int average = missingLength / numUninitializedStrips; + //total number of uninitialized strips encountered so far + int uninitializedStripIndex = 0; + for (int i = start; i < stop; i++) { + sizeProxy.Strip = strips[i]; + //this is an uninitialized strip + if (!IsAbsolutelySized(i, styles) && sizeProxy.Size == 0) { + uninitializedStripIndex++; + if (uninitializedStripIndex == numUninitializedStrips) { + //we are at the last strip. place round off error here + average = missingLength - average * (numUninitializedStrips - 1); + } + sizeProxy.Size += average; + strips[i] = sizeProxy.Strip; + } + } + +#if DEBUG + // not using assert here to prevent concatination in debug builds. + if (uninitializedStripIndex != numUninitializedStrips) { + Debug.Fail("should always get the same number of strips with size 0. " + "expecting: " + numUninitializedStrips + " actual: " + uninitializedStripIndex); + } +#endif + + } + } + + //determines whether strip[index]'s style is absolutely sized + private bool IsAbsolutelySized(int index, IList styles) { + return (index < styles.Count) && ((TableLayoutStyle)styles[index]).SizeType == SizeType.Absolute; + } + + /// + /// Now that we've allocated minimum and maximum sizes to everyone (the strips), distribute the extra space + /// as according to the Row/Column styles. + /// + private int DistributeStyles(int cellBorderWidth, IList styles, Strip[] strips, int maxSize, bool dontHonorConstraint) { + int usedSpace = 0; + float desiredScaleSpace = 0; + //first, allocate the minimum space required for each element + + float totalPercent = 0; + float totalPercentAllocatedSpace = 0; + float totalAbsoluteAndAutoSizeAllocatedSpace = 0; + bool hasAutoSizeColumn = false; + + // Step 1: iterate through the rows or columns, + // - calculate the amount of space allocated + // - for autosize and fixed size columns + // - for percent size columns + // - sum up the total "%"s being used + // - eg totalPercent = 22 + 22 + 22 = 66%. each column should take up 1/3 of the remaining space. + for(int i = 0; i < strips.Length; i++) { + Strip strip = strips[i]; + if(i < styles.Count) { + TableLayoutStyle style = (TableLayoutStyle) styles[i]; + switch(style.SizeType) { + case SizeType.Absolute: + totalAbsoluteAndAutoSizeAllocatedSpace += strip.MinSize; + // We gaurantee a strip will be exactly abs pixels + Debug.Assert((strip.MinSize == style.Size), "absolutely sized strip's size should be set before we call ApplyStyles"); + break; + case SizeType.Percent: + totalPercent += style.Size; + totalPercentAllocatedSpace += strip.MinSize; + break; + default: + totalAbsoluteAndAutoSizeAllocatedSpace += strip.MinSize; + hasAutoSizeColumn = true; + Debug.Assert(style.SizeType == SizeType.AutoSize, "Unsupported SizeType."); + break; + } + } + else { + hasAutoSizeColumn = true; + } + strip.MaxSize += cellBorderWidth; + strip.MinSize += cellBorderWidth; //add the padding for the cell border + + strips[i] = strip; + usedSpace += strip.MinSize; + } + + int remainingSpace = maxSize - usedSpace; + + + + // Step 2: (ONLY if we have % style column) + // - distribute unused space for absolute/autosize columns to percentage columns + // - determine the extra space that is not being used for autosize and fixed columns + // - divide space amongst % style columns using ratio of %/total % * total extra space + if (totalPercent > 0) { + + + if (!dontHonorConstraint) { + + if (totalPercentAllocatedSpace > maxSize - totalAbsoluteAndAutoSizeAllocatedSpace) { + // fixup for the case where we've actually allocated more space than we have. + // this can happen when the sum of the widths/heights of the controls are larger than the size of the + // table (aka maxSize) + + //Don't want negative size... + totalPercentAllocatedSpace = Math.Max(0, maxSize - totalAbsoluteAndAutoSizeAllocatedSpace); + } + + if (remainingSpace > 0) { + // If there's space left over, then give it to the percentage columns/rows + totalPercentAllocatedSpace += remainingSpace; + } + + else if (remainingSpace < 0) { + // If there's not enough space, then remove space from the percentage columns. + // We do this by recalculating the space available. + totalPercentAllocatedSpace = maxSize - totalAbsoluteAndAutoSizeAllocatedSpace - (strips.Length * cellBorderWidth); + remainingSpace = 0; + } + + + // in this case the strips fill up the remaining space. + for (int i = 0; i < strips.Length; i++) { + Strip strip = strips[i]; + SizeType sizeType = i < styles.Count ? ((TableLayoutStyle)styles[i]).SizeType : SizeType.AutoSize; + if (sizeType == SizeType.Percent) { + TableLayoutStyle style = (TableLayoutStyle)styles[i]; + + // cast to int / (take the floor) so we know we dont accidentally go over our limit. + // the rest will be distributed later. + int stripSize = (int)(style.Size * totalPercentAllocatedSpace / totalPercent); + usedSpace -= strip.MinSize; // back out the size we thought we were allocating before. + usedSpace += stripSize + cellBorderWidth; // add in the new size we think we're going to use. + strip.MinSize = stripSize + cellBorderWidth; + strips[i] = strip; + } + + } + } + else { + // the size of the item defines the size allocated to the percentage style columns. + // this supports [Ok][Cancel] in a 50% 50% column arrangement. When one grows it pushes the whole table + // larger. + int maxPercentWidth = 0; + for (int i = 0; i < strips.Length; i++) { + Strip strip = strips[i]; + SizeType sizeType = i < styles.Count ? ((TableLayoutStyle)styles[i]).SizeType : SizeType.AutoSize; + + + // Performing the inverse calculation for GetPreferredSize: + // + // stylePercent * totalWidth colWidth * totalPercent + // colWidth = ------------------------- ---> totalWidth = ----------------------- + // totalPercent stylePercent + // + // we'll take the max of the total widths as the one for our preferred size. + if (sizeType == SizeType.Percent) { + TableLayoutStyle style = (TableLayoutStyle)styles[i]; + + int totalWidth = (int)Math.Round(((strip.MinSize * totalPercent)/ style.Size)); + maxPercentWidth = Math.Max(maxPercentWidth, totalWidth); + usedSpace -= strip.MinSize; + } + } + usedSpace += maxPercentWidth; + } + + + } + remainingSpace = maxSize - usedSpace; + + + + // Step 3: add remaining space to autosize columns + // - usually we only do this if we're not in preferred size (remaingSpace would be < 0) + // - and there are no % style columns + + if(/*!dontHonorConstraint && */hasAutoSizeColumn && remainingSpace > 0) { + float scaleAdjustment = 1.0f; + if(remainingSpace < desiredScaleSpace) { + scaleAdjustment = remainingSpace / desiredScaleSpace; + } + remainingSpace -= (int) Math.Ceiling(desiredScaleSpace); + + for(int i = 0; i < strips.Length; i++) { + Strip strip = strips[i]; + SizeType sizeType = i < styles.Count ? ((TableLayoutStyle)styles[i]).SizeType : SizeType.AutoSize; + if (sizeType == SizeType.AutoSize) { + int delta = Math.Min(strip.MaxSize - strip.MinSize, remainingSpace); + if(delta > 0) { + usedSpace += delta; + remainingSpace -= delta; + strip.MinSize += delta; + strips[i] = strip; + } + } + } + } + Debug.Assert((dontHonorConstraint || (usedSpace == SumStrips(strips, 0, strips.Length))), "Error computing usedSpace."); + return usedSpace; + } + + private void SetElementBounds(ContainerInfo containerInfo, RectangleF displayRectF) { + int cellBorderWidth = containerInfo.CellBorderWidth; + + float top = displayRectF.Y; + int currentCol = 0; + int currentRow = 0; + bool isContainerRTL = false; + Rectangle displayRect = Rectangle.Truncate(displayRectF); + + if (containerInfo.Container is Control) { + Control control = containerInfo.Container as Control; + isContainerRTL = control.RightToLeft == RightToLeft.Yes; + } + LayoutInfo[] childrenInfo = containerInfo.ChildrenInfo; + float startX = isContainerRTL ? displayRectF.Right : displayRectF.X; + + //sort everything according to row major, column minor order + //so that we can ensure that as we walk through all elements + //the cursor always goes from left to right and from top to bottom. + Sort(childrenInfo,PostAssignedPositionComparer.GetInstance); + for (int i = 0; i < childrenInfo.Length; i++) { + LayoutInfo layoutInfo = (LayoutInfo)childrenInfo[i]; + + IArrangedElement element = layoutInfo.Element; + + // Advance top to the beginning of this elements row. + Debug.Assert(currentRow <= layoutInfo.RowStart, "RowStart should increase in forward Z-order."); + if(currentRow != layoutInfo.RowStart) { + for(; currentRow < layoutInfo.RowStart; currentRow++) { + top += containerInfo.Rows[currentRow].MinSize; + } + startX = isContainerRTL ? displayRectF.Right : displayRectF.X; + currentCol = 0; + } + + // Advance left to the beginning of this elements column. + Debug.Assert(currentCol <= layoutInfo.ColumnStart, "ColumnStart should increase in forward Z-order."); + for(; currentCol < layoutInfo.ColumnStart; currentCol++) { + if (isContainerRTL) { + startX -= containerInfo.Columns[currentCol].MinSize; + } + else { + startX += containerInfo.Columns[currentCol].MinSize; + } + } + + // Sum the total width of the span. We increment currentCol as we + // do this. + int colStop = currentCol + layoutInfo.ColumnSpan; + int width = 0; + for(;currentCol < colStop && currentCol < containerInfo.Columns.Length; currentCol++) { + width += containerInfo.Columns[currentCol].MinSize; + } + + if (isContainerRTL) { + startX -= width; + } + + // Sum the total height of the span. We do not increment RowSpan + // as we do this because there may be more elements on this row. + int rowStop = currentRow + layoutInfo.RowSpan; + int height = 0; + for(int rowIndex = currentRow; rowIndex < rowStop && rowIndex < containerInfo.Rows.Length; rowIndex++) { + height += containerInfo.Rows[rowIndex].MinSize; + } + + Rectangle cellBounds = new Rectangle((int)(startX + cellBorderWidth / 2.0f), (int)(top + cellBorderWidth / 2.0f), width - cellBorderWidth, height - cellBorderWidth); + + // We laid out the rows and columns with the element's margins included. + // We now deflate the rect to get the actual element bounds. + Padding elementMargin = CommonProperties.GetMargin(element); + if (isContainerRTL) { + int temp = elementMargin.Right; + elementMargin.Right = elementMargin.Left; + elementMargin.Left = temp; + } + + cellBounds = LayoutUtils.DeflateRect(cellBounds, elementMargin); + + //make sure our sizes are non-negative + cellBounds.Width = Math.Max(cellBounds.Width, 1); + cellBounds.Height = Math.Max(cellBounds.Height , 1); + + AnchorStyles anchorStyles = LayoutUtils.GetUnifiedAnchor(element); + + Rectangle elementBounds = LayoutUtils.AlignAndStretch(GetElementSize(element, cellBounds.Size), cellBounds, anchorStyles); + + // If the element was not BoxStretch.Both, AlignAndStretch does not gaurantee + // that the element has been clipped to the cell bounds. + elementBounds.Width = Math.Min(cellBounds.Width, elementBounds.Width); + elementBounds.Height = Math.Min(cellBounds.Height, elementBounds.Height); + + if (isContainerRTL) { + elementBounds.X = cellBounds.X + (cellBounds.Right - elementBounds.Right); + } + + element.SetBounds(elementBounds, BoundsSpecified.None); + if (!isContainerRTL) { + startX += width; + } + } + + Debug_VerifyNoOverlapping(containerInfo.Container); + } + + internal IArrangedElement GetControlFromPosition (IArrangedElement container, int column, int row) { + if (row < 0) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "RowPosition", row.ToString(CultureInfo.CurrentCulture))); + } + if (column < 0) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "ColumnPosition", column.ToString(CultureInfo.CurrentCulture))); + } + ArrangedElementCollection children = container.Children; + ContainerInfo containerInfo = GetContainerInfo(container); + + if (children == null || children.Count == 0) { + //nothing in the container. returns null. + return null; + } + if (!containerInfo.Valid) { + //hasn't performed layout yet. assign rows and columns first + EnsureRowAndColumnAssignments(container, containerInfo, /* doNotCache = */ true); + } + for (int i = 0; i < children.Count; i++) { + LayoutInfo layoutInfo = GetLayoutInfo(children[i]); + //the row and column specified is within the region enclosed by the element. + if (layoutInfo.ColumnStart <= column && (layoutInfo.ColumnStart + layoutInfo.ColumnSpan - 1) >= column && + layoutInfo.RowStart <= row && (layoutInfo.RowStart + layoutInfo.RowSpan - 1) >= row) { + return layoutInfo.Element; + } + } + return null; + } + + internal TableLayoutPanelCellPosition GetPositionFromControl(IArrangedElement container, IArrangedElement child) { + if (container == null || child == null) { + Debug.Fail("Why are we here when child or container is null?"); + return new TableLayoutPanelCellPosition(-1,-1); + } + ArrangedElementCollection children = container.Children; + ContainerInfo containerInfo = GetContainerInfo(container); + + if (children == null || children.Count == 0) { + //nothing in the container. returns null. + return new TableLayoutPanelCellPosition(-1,-1); + } + if (!containerInfo.Valid) { + //hasn't performed layout yet. assign rows and columns first + EnsureRowAndColumnAssignments(container, containerInfo, /* doNotCache = */ true); + } + LayoutInfo layoutInfo = GetLayoutInfo(child); + return new TableLayoutPanelCellPosition(layoutInfo.ColumnStart, layoutInfo.RowStart); + + } + + + + #region LayoutInfo + internal static LayoutInfo GetLayoutInfo(IArrangedElement element) { + LayoutInfo layoutInfo = (LayoutInfo)element.Properties.GetObject(_layoutInfoProperty); + if(layoutInfo == null) { + layoutInfo = new LayoutInfo(element); + SetLayoutInfo(element, layoutInfo); + } + return layoutInfo; + } + + internal static void SetLayoutInfo(IArrangedElement element, LayoutInfo value) { + element.Properties.SetObject(_layoutInfoProperty, value); + Debug.Assert(GetLayoutInfo(element) == value, "GetLayoutInfo should return the same value as we set it to"); + } + + + /// + /// This class contains layout related information pertaining + /// to a child control of the container being laid out. + /// it contains Row,column assignments as well as RowSpan/ColumnSpan. + /// This class is used from ContainerInfo as a way of caching information + /// about child controls. + /// + internal sealed class LayoutInfo { + //the actual row and column position of this control + private int _rowStart = -1; //if change the default value, change the code in GetControlFromPosition also + private int _columnStart = -1; + private int _columnSpan = 1; + private int _rowSpan = 1; + //the row and column specified by the user. Only set when the element is absolutely positioned + private int _rowPos = -1; + private int _colPos = -1; + + //the element which owns this layoutInfo + private IArrangedElement _element; + + public LayoutInfo(IArrangedElement element) { + _element = element; + } + + internal bool IsAbsolutelyPositioned { + get { return _rowPos >= 0 && _colPos >= 0; } + } + + internal IArrangedElement Element { + get { return _element; } + } + + ///Corresponds to TableLayoutSettings.SetRow + ///Can be -1 indicating that it is a "flow" element and + ///will fit in as necessary. This occurs when a control + ///is just added without specific position. + internal int RowPosition { + get { return _rowPos; } + set { + // all validation should occur in TableLayoutSettings. + _rowPos = value; + } + } + + + ///Corresponds to TableLayoutSettings.SetColumn + ///Can be -1 indicating that it is a "flow" element and + ///will fit in as necessary. This occurs when a control + ///is just added without specific position. + internal int ColumnPosition { + get { return _colPos;} + set { + // all validation should occur in TableLayoutSettings. + _colPos = value; + } + } + + internal int RowStart { + get { return _rowStart; } + set { _rowStart = value; } + } + + internal int ColumnStart { + get { return _columnStart; } + set { _columnStart = value; } + } + + internal int ColumnSpan { + get { return _columnSpan; } + set { + _columnSpan = value; + } + } + + internal int RowSpan { + get { return _rowSpan; } + set { + _rowSpan = value; + } + } + +#if DEBUG + public LayoutInfo Clone() { + LayoutInfo clone = new LayoutInfo(_element); + clone.RowStart = RowStart; + clone.ColumnStart = ColumnStart; + clone.RowSpan = RowSpan; + clone.ColumnSpan = ColumnSpan; + clone.RowPosition = RowPosition; + clone.ColumnPosition = ColumnPosition; + return clone; + } + + public override bool Equals(object obj) { + LayoutInfo other = obj as LayoutInfo; + if(other == null) return false; + + return other.RowStart == RowStart + && other.ColumnStart == ColumnStart + && other.RowSpan == RowSpan + && other.ColumnSpan == ColumnSpan + && other.RowPosition == RowPosition + && other.ColumnPosition == ColumnPosition; + } + + // Required if you override Equals() + public override int GetHashCode() { return base.GetHashCode(); } +#endif + } + #endregion + + #region ContainerInfo + internal static bool HasCachedAssignments(ContainerInfo containerInfo){ + return containerInfo.Valid; + } + + internal static void ClearCachedAssignments(ContainerInfo containerInfo) { + containerInfo.Valid = false; + } + + //we make sure that our conatinerInfo never returns null. If there is no + //existing containerInfo, instantiate a new one and store it in the property + //store. + internal static ContainerInfo GetContainerInfo(IArrangedElement container) { + ContainerInfo containerInfo = (ContainerInfo) container.Properties.GetObject(_containerInfoProperty); + if (containerInfo == null) { + containerInfo = new ContainerInfo(container); + container.Properties.SetObject(_containerInfoProperty, containerInfo); + } + return containerInfo; + } + + + /// + /// this class contains layout related information pertaining to the container + /// being laid out by this instance of the TableLayout. It contains references + /// to all the information that should be used from the table layout engine, + /// as this class is responsible for caching information about the control and + /// it's children being layed out. + /// + internal sealed class ContainerInfo { + private static Strip[] emptyStrip = new Strip[0]; + + private static readonly int stateValid = BitVector32.CreateMask(); + private static readonly int stateChildInfoValid = BitVector32.CreateMask(stateValid); + private static readonly int stateChildHasColumnSpan = BitVector32.CreateMask(stateChildInfoValid); + private static readonly int stateChildHasRowSpan = BitVector32.CreateMask(stateChildHasColumnSpan); + + private int _cellBorderWidth; //the width for the cell border + private Strip[] _cols = emptyStrip; + private Strip[] _rows = emptyStrip; + private int _maxRows; + private int _maxColumns; + private TableLayoutRowStyleCollection _rowStyles; + private TableLayoutColumnStyleCollection _colStyles; + private TableLayoutPanelGrowStyle _growStyle; + private IArrangedElement _container; + private LayoutInfo[] _childInfo; + private int _countFixedChildren; + private int _minRowsAndColumns; // The minimum space required to put all the controls without overlapping + private int _minColumns; // The minimum number of columns required in order to put all absolutely positioned control on the table + private int _minRows; // The minimum number of rows required in order to put all absolutely positioned control on the table + + private BitVector32 _state = new BitVector32(); + + + public ContainerInfo(IArrangedElement container) { + _container = container; + _growStyle = TableLayoutPanelGrowStyle.AddRows; + } + + public ContainerInfo(ContainerInfo containerInfo) { + _cellBorderWidth = containerInfo.CellBorderWidth; + _maxRows = containerInfo.MaxRows; + _maxColumns = containerInfo.MaxColumns; + _growStyle = containerInfo.GrowStyle; + _container = containerInfo.Container; + _rowStyles = containerInfo.RowStyles; + _colStyles = containerInfo.ColumnStyles; + } + + + + /// + /// the container being laid out + /// + public IArrangedElement Container { + get { return _container; } + } + + /* Unused + //indicates whether the user has only specified the row number of the table + public bool IsRowDefined { + get { return (_maxRows != 0 && _maxColumns == 0); } + } + */ + + public int CellBorderWidth { + get { return _cellBorderWidth; } + set { _cellBorderWidth = value; } + } + + + /// + /// list of ints that represent the sizes of individual columns + /// + public Strip[] Columns { + get { return _cols; } + set { + Debug.Assert(_cols.Length != value.Length, "PERF: should not allocate strips, we've already got an array"); + _cols = value; + } + } + + /// + /// list of ints that represent the sizes of individual rows + /// + public Strip[] Rows { + get { return _rows; } + + set { + Debug.Assert(_rows.Length != value.Length, "PERF: should not allocate strips, we've already got an array"); + _rows = value; + + } + + } + + /// + /// Same as TableLayoutSettings.RowCount + /// + public int MaxRows { + + get { return _maxRows; } + set { + if (_maxRows != value) { + _maxRows = value; + + //invalidate the cache whenever we change the number of rows + Valid = false; + } + + } + } + + /// + /// Same as TableLayoutSettings.ColumnCount + /// + public int MaxColumns { + + get { return _maxColumns; } + + set { + if (_maxColumns != value) { + _maxColumns = value; + + //invalidate the cache whenever we change the number of columns + Valid = false; + } + } + } + + + /// Cached information + public int MinRowsAndColumns { + get { + Debug.Assert(ChildInfoValid, "Fetching invalid information"); + return _minRowsAndColumns; + } + } + + /// Cached information + public int MinColumns { + get { + Debug.Assert(ChildInfoValid, "Fetching invalid information"); + return _minColumns; + + } + } + + + /// Cached information + public int MinRows { + get { + Debug.Assert(ChildInfoValid, "Fetching invalid information"); + return _minRows; + + } + } + + /// + /// Gets/sets the grow style for our containerinfo. This + /// is used to determine if we will add rows/cols/or throw + /// when the table gets full. + /// + public TableLayoutPanelGrowStyle GrowStyle { + get { + return _growStyle; + } + set { + if (_growStyle != value) { + _growStyle = value; + Valid = false; // throw away cached row and column assignments + } + } + } + + public TableLayoutRowStyleCollection RowStyles { + get { + if (_rowStyles == null) { + _rowStyles = new TableLayoutRowStyleCollection(_container); + } + return _rowStyles; + + } + set { + _rowStyles = value; + if (_rowStyles != null) { + _rowStyles.EnsureOwnership(_container); + } + + } + } + + public TableLayoutColumnStyleCollection ColumnStyles { + get { + if (_colStyles == null) { + _colStyles = new TableLayoutColumnStyleCollection(_container); + } + return _colStyles; + } + set { + _colStyles = value; + if (_colStyles != null) { + _colStyles.EnsureOwnership(_container); + } + } + } + + + /// + /// gets cached information about the children of the control being layed out. + /// + public LayoutInfo[] ChildrenInfo { + get { + if (!_state[stateChildInfoValid]) { + _countFixedChildren = 0; + + _minRowsAndColumns = 0; + _minColumns = 0; + _minRows = 0; + ArrangedElementCollection children = Container.Children; + LayoutInfo[] childInfo = new LayoutInfo[children.Count]; + int nonParticipatingElements = 0; + int index = 0; + for (int i = 0; i < children.Count; i++) { + IArrangedElement element = children[i]; + if (!element.ParticipatesInLayout) { + // If the element does not participate in layout (i.e., Visible = false), we + // exclude it from the childrenInfos list and it is ignored by the engine. + nonParticipatingElements++; + continue; + } + + LayoutInfo layoutInfo = GetLayoutInfo(element); + if (layoutInfo.IsAbsolutelyPositioned) { + _countFixedChildren++; + + } + childInfo[index++] = layoutInfo; + _minRowsAndColumns += layoutInfo.RowSpan * layoutInfo.ColumnSpan; + if (layoutInfo.IsAbsolutelyPositioned) { + _minColumns = Math.Max(_minColumns, layoutInfo.ColumnPosition + layoutInfo.ColumnSpan); + _minRows = Math.Max(_minRows, layoutInfo.RowPosition + layoutInfo.RowSpan); + } + + } + + // shorten the array if necessary. + if (nonParticipatingElements > 0) { + LayoutInfo[] trimmedChildInfo = new LayoutInfo[childInfo.Length - nonParticipatingElements]; + Array.Copy(childInfo, trimmedChildInfo, trimmedChildInfo.Length); + _childInfo = trimmedChildInfo; + } + else { + _childInfo = childInfo; + } + _state[stateChildInfoValid] = true; + } + return (_childInfo == null) ? new LayoutInfo[0] : _childInfo; + } + } + + public bool ChildInfoValid { + get { return _state[stateChildInfoValid]; } + } + public LayoutInfo[] FixedChildrenInfo { + get { + Debug.Assert(ChildInfoValid, "Fetched invalid information"); + // we only get this in a cached scenario - so we dont have to worry about caching it. + LayoutInfo[] fixedChildren = new LayoutInfo[_countFixedChildren]; + if (HasChildWithAbsolutePositioning) { + int index = 0; + for (int i = 0; i < _childInfo.Length; i++) { + if (_childInfo[i].IsAbsolutelyPositioned) { + fixedChildren[index++] = _childInfo[i]; + } + } + Sort(fixedChildren, PreAssignedPositionComparer.GetInstance); + } + return fixedChildren; + } + } + public bool Valid { + get { return _state[stateValid]; } + set { _state[stateValid] = value; + if (!_state[stateValid]) { + _state[stateChildInfoValid] = false; + } + } + } + + public bool HasChildWithAbsolutePositioning { + get { return _countFixedChildren > 0; } + } + + public bool HasMultiplePercentColumns { + get { + if (_colStyles != null) { + bool foundAny = false; + foreach (ColumnStyle style in _colStyles) { + if (style.SizeType == SizeType.Percent) { + if (foundAny) { + return true; + } + foundAny = true; + } + } + } + + return false; + } + } + + public bool ChildHasColumnSpan { + get { return _state[stateChildHasColumnSpan]; } + set { _state[stateChildHasColumnSpan] = value; } + } + + public bool ChildHasRowSpan { + get { return _state[stateChildHasRowSpan]; } + set { _state[stateChildHasRowSpan] = value; } + } + + public Size GetCachedPreferredSize(Size proposedContstraints, out bool isValid) { + isValid = false; + if (proposedContstraints.Height == 0 || proposedContstraints.Width == 0) { + Size cachedSize = CommonProperties.xGetPreferredSizeCache(Container); + + if(!cachedSize.IsEmpty) { + isValid = true; + return cachedSize; + } + } + return Size.Empty; + } + + + } + #endregion + + #region SizeProxy implementations + //sizeProxy. Takes a strip and return its minSize or maxSize accordingly + private abstract class SizeProxy { + protected Strip strip; + public Strip Strip { + get { return strip; } + set { strip = value; } + } + + public abstract int Size { + get; set; + } + } + + private class MinSizeProxy : SizeProxy { + private static readonly MinSizeProxy instance = new MinSizeProxy(); + public override int Size { + get { return strip.MinSize; } + set { strip.MinSize = value; } + } + + public static MinSizeProxy GetInstance { + get { + return instance; + } + } + } + + private class MaxSizeProxy : SizeProxy { + private static readonly MaxSizeProxy instance = new MaxSizeProxy(); + public override int Size { + get { return strip.MaxSize; } + set { strip.MaxSize = value; } + } + + public static MaxSizeProxy GetInstance { + get { + return instance; + } + } + } + #endregion + + #region ICompare implementations + private abstract class SpanComparer : IComparer { + public abstract int GetSpan(LayoutInfo layoutInfo); + + public int Compare(object x, object y) { + LayoutInfo xInfo = (LayoutInfo)x; + LayoutInfo yInfo = (LayoutInfo)y; + return GetSpan(xInfo) - GetSpan(yInfo); + } + } + + private class RowSpanComparer : SpanComparer { + private static readonly RowSpanComparer instance = new RowSpanComparer(); + + public override int GetSpan(LayoutInfo layoutInfo) { + return layoutInfo.RowSpan; + } + + public static RowSpanComparer GetInstance { + get { return instance; } + } + } + + private class ColumnSpanComparer : SpanComparer { + private static readonly ColumnSpanComparer instance = new ColumnSpanComparer(); + + public override int GetSpan(LayoutInfo layoutInfo) { + return layoutInfo.ColumnSpan; + } + + public static ColumnSpanComparer GetInstance { + get { return instance; } + } + } + + private class PostAssignedPositionComparer : IComparer { + private static readonly PostAssignedPositionComparer instance = new PostAssignedPositionComparer(); + + public static PostAssignedPositionComparer GetInstance { + get { return instance; } + } + + public int Compare (object x, object y) { + LayoutInfo xInfo = (LayoutInfo)x; + LayoutInfo yInfo = (LayoutInfo)y; + if (xInfo.RowStart < yInfo.RowStart) { + return -1; + } + if (xInfo.RowStart > yInfo.RowStart) { + return 1; + } + if (xInfo.ColumnStart < yInfo.ColumnStart) { + return -1; + } + if (xInfo.ColumnStart > yInfo.ColumnStart) { + return 1; + } + return 0; + } + } + + private class PreAssignedPositionComparer : IComparer { + private static readonly PreAssignedPositionComparer instance = new PreAssignedPositionComparer(); + + public static PreAssignedPositionComparer GetInstance { + get { return instance; } + } + + public int Compare (object x, object y) { + LayoutInfo xInfo = (LayoutInfo)x; + LayoutInfo yInfo = (LayoutInfo)y; + if (xInfo.RowPosition < yInfo.RowPosition) { + return -1; + } + if (xInfo.RowPosition > yInfo.RowPosition) { + return 1; + } + if (xInfo.ColumnPosition < yInfo.ColumnPosition) { + return -1; + } + if (xInfo.ColumnPosition > yInfo.ColumnPosition) { + return 1; + } + return 0; + } + } + #endregion + + #region ReservationGrid + // The ReservationGrid is used to track elements which span rows to prevent overlap. + private sealed class ReservationGrid { + int _numColumns = 1; + ArrayList _rows = new ArrayList(); + + public bool IsReserved(int column, int rowOffset) { + if(rowOffset >= _rows.Count) { + return false; + } + if (column >= ((BitArray)_rows[rowOffset]).Length) { + return false; + } + return ((BitArray)_rows[rowOffset])[column]; + } + + public void Reserve(int column, int rowOffset) { + Debug.Assert(!IsReserved(column, rowOffset), "we should not be reserving already reserved space."); + while(rowOffset >= _rows.Count) { + _rows.Add(new BitArray(_numColumns)); + } + //Debug.Assert(_numColumns == ((BitArray)_rows[rowOffset]).Length, "length doesn't match"); + //increase the length of the _rows[rowOffset] if necessary + if (column >= ((BitArray)_rows[rowOffset]).Length) { + ((BitArray)_rows[rowOffset]).Length = column + 1; + if (column >= _numColumns) { + _numColumns = column + 1; + } + } + + ((BitArray)_rows[rowOffset])[column] = true; + Debug.Assert(IsReserved(column, rowOffset), "IsReserved/Reserved mismatch."); + } + + //reserve all spaces taken by layoutInfo.Element, up till colStop + public void ReserveAll(LayoutInfo layoutInfo, int rowStop, int colStop) { + for (int rowOffset = 1; rowOffset < rowStop - layoutInfo.RowStart; rowOffset++) { + for (int reservedCol = layoutInfo.ColumnStart; reservedCol < colStop; reservedCol++) { + Reserve(reservedCol, rowOffset); + } + } + } + + public void AdvanceRow() { + if(_rows.Count > 0) { + _rows.RemoveAt(0); + } + } + + } + #endregion ReservationGrid + + internal struct Strip { + private int _maxSize; + private int _minSize; + private bool _isStart; //whether there is an element starting in this strip + + public int MinSize { + get { return _minSize; } + set { _minSize = value; } + } + + public int MaxSize { + get { return _maxSize; } + set { _maxSize = value; } + } + + public bool IsStart { + get { return _isStart; } + set { _isStart = value; } + } + } + + #region DEBUG + // Verify that the Row/Column assignments on the control are current. + [Conditional("DEBUG_LAYOUT")] + private void Debug_VerifyAssignmentsAreCurrent(IArrangedElement container, ContainerInfo containerInfo) { +#if DEBUG + Hashtable oldLayoutInfo = new Hashtable(); + ArrangedElementCollection children = container.Children; + ArrayList childrenInfo = new ArrayList(children.Count); + + int minSpace = 0; + int minColumn = 0; + for (int i = 0; i < children.Count; i++) { + IArrangedElement element = children[i]; + if (!element.ParticipatesInLayout) { + // If the element does not participate in layout (i.e., Visible = false), we + // exclude it from the childrenInfos list and it is ignored by the engine. + continue; + } + + LayoutInfo layoutInfo = GetLayoutInfo(element); + childrenInfo.Add(layoutInfo); + minSpace += layoutInfo.RowSpan * layoutInfo.ColumnSpan; + if (layoutInfo.IsAbsolutelyPositioned) { + minColumn = Math.Max(minColumn, layoutInfo.ColumnPosition + layoutInfo.ColumnSpan); + } + } + + // Create a copy of the layoutInfos so we can restore to our original state + foreach(LayoutInfo layoutInfo in childrenInfo) { + oldLayoutInfo[layoutInfo.Element] = layoutInfo.Clone(); + } + + Strip[] rows = containerInfo.Rows; + Strip[] cols = containerInfo.Columns; + + AssignRowsAndColumns(containerInfo); + + Debug.Assert((containerInfo.Columns == null && cols == null) || containerInfo.Columns.Length == cols.Length, + "Cached assignment info is invalid: Number of required columns has changed."); + Debug.Assert((containerInfo.Rows == null && rows == null) || containerInfo.Rows.Length == rows.Length, + "Cached assignment info is invalid: Number of required rows has changed."); + + + foreach(LayoutInfo layoutInfo in childrenInfo) { + Debug.Assert(layoutInfo.Equals(oldLayoutInfo[layoutInfo.Element]), + "Cached assignment info is invalid: LayoutInfo has changed." + + " old layoutinfo: " + ((LayoutInfo)oldLayoutInfo[layoutInfo.Element]).RowStart + " " + ((LayoutInfo)oldLayoutInfo[layoutInfo.Element]).ColumnStart + + " new layoutinfo: " + layoutInfo.RowStart + " " + layoutInfo.ColumnStart + + " and the element is " + layoutInfo.Element.ToString()); + SetLayoutInfo(layoutInfo.Element, (LayoutInfo) oldLayoutInfo[layoutInfo.Element]); + } + + // Restore the information in row and column strips. Note that whenever we do a AssignRowAndColumns() + // we instantiate new row and column strip collections, we have to restore the value back later. + + + containerInfo.Rows = rows; + containerInfo.Columns = cols; +#endif // DEBUG + } + + // Verifies that there is no overlapping of controls on the table (unless forced to do so via abs. positioning) + [Conditional("DEBUG_LAYOUT")] + private void Debug_VerifyNoOverlapping(IArrangedElement container) { + + // this code may be useful for debugging, but doesnt work well with + // row styles + + ArrayList layoutInfos = new ArrayList(container.Children.Count); + ContainerInfo containerInfo = GetContainerInfo(container); + Strip[] rows = containerInfo.Rows; + Strip[] columns = containerInfo.Columns; + + foreach(IArrangedElement element in container.Children) { + if (!element.ParticipatesInLayout) { + // If the element does not participate in layout (i.e., Visible = false), we + // exclude it from the layoutInfos list and it is ignored by the engine. + continue; + } + + layoutInfos.Add(GetLayoutInfo(element)); + } + + for (int i = 0; i < layoutInfos.Count; i++) { + LayoutInfo layoutInfo1 = (LayoutInfo)layoutInfos[i]; + + Rectangle elementBounds1 = layoutInfo1.Element.Bounds; + Rectangle cellsOccupied1 = new Rectangle(layoutInfo1.ColumnStart, layoutInfo1.RowStart, layoutInfo1.ColumnSpan, layoutInfo1.RowSpan); + for (int j = i + 1; j < layoutInfos.Count; j++) { + LayoutInfo layoutInfo2 = (LayoutInfo)layoutInfos[j]; + Rectangle elementBounds2 = layoutInfo2.Element.Bounds; + Rectangle cellsOccupied2 = new Rectangle(layoutInfo2.ColumnStart, layoutInfo2.RowStart, layoutInfo2.ColumnSpan, layoutInfo2.RowSpan); + Debug.Assert(!cellsOccupied1.IntersectsWith(cellsOccupied2), "controls overlap in the same cell"); + // The actual control overlaps horizontally. this can only happen if all columns are absolutely sized + if (LayoutUtils.IsIntersectHorizontally(elementBounds1, elementBounds2)) { + int k; + Debug.Assert(containerInfo.ColumnStyles.Count >= layoutInfo1.ColumnStart + layoutInfo1.ColumnSpan, "length of column style too short"); + Debug.Assert(containerInfo.ColumnStyles.Count >= layoutInfo1.ColumnStart + layoutInfo2.ColumnSpan, "length of column style too short"); + for (k = layoutInfo1.ColumnStart; k < layoutInfo1.ColumnStart + layoutInfo1.ColumnSpan; k++) { + Debug.Assert(containerInfo.ColumnStyles[k].SizeType == SizeType.Absolute, "column " + k + " is not absolutely sized"); + } + for (k = layoutInfo2.ColumnStart; k < layoutInfo2.ColumnStart + layoutInfo2.ColumnSpan; k++) { + Debug.Assert(containerInfo.ColumnStyles[k].SizeType == SizeType.Absolute, "column " + k + " is not absolutely sized"); + } + } + + // The actual control overlaps vertically. + if (LayoutUtils.IsIntersectVertically(elementBounds1, elementBounds2)) { + int k; + Debug.Assert(containerInfo.RowStyles.Count >= layoutInfo1.RowStart + layoutInfo1.RowSpan, "length of row style too short"); + Debug.Assert(containerInfo.RowStyles.Count >= layoutInfo2.RowStart + layoutInfo2.RowSpan, "length of row style too short"); + for (k = layoutInfo1.RowStart; k < layoutInfo1.RowStart + layoutInfo1.RowSpan; k++) { + Debug.Assert(containerInfo.RowStyles[k].SizeType == SizeType.Absolute, "column " + k + " is not absolutely sized"); + } + for (k = layoutInfo2.RowStart; k < layoutInfo2.RowStart + layoutInfo2.RowSpan; k++) { + Debug.Assert(containerInfo.RowStyles[k].SizeType == SizeType.Absolute, "column " + k + " is not absolutely sized"); + } + } + } + } + } + #endregion + } +} diff --git a/WindowsForms/Managed/System/WinForms/LayoutEvent.cs b/WindowsForms/Managed/System/WinForms/LayoutEvent.cs new file mode 100644 index 000000000..4628fe4cb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LayoutEvent.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + + /// + /// + /// + public sealed class LayoutEventArgs : EventArgs { + private readonly IComponent affectedComponent; + private readonly string affectedProperty; + + /// + public LayoutEventArgs(IComponent affectedComponent, string affectedProperty) { + this.affectedComponent = affectedComponent; + this.affectedProperty = affectedProperty; + } + + // This ctor required for binary compatibility with RTM. + /// + public LayoutEventArgs(Control affectedControl, string affectedProperty) + : this((IComponent)affectedControl, affectedProperty) { + } + + /// + public IComponent AffectedComponent { + get { + return affectedComponent; + } + } + + /// + /// + /// [To be supplied.] + /// + public Control AffectedControl { + get { + return affectedComponent as Control; + } + } + + /// + /// + /// [To be supplied.] + /// + public string AffectedProperty { + get { + return affectedProperty; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/LayoutEventHandler.cs b/WindowsForms/Managed/System/WinForms/LayoutEventHandler.cs new file mode 100644 index 000000000..db4ee01af --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LayoutEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void LayoutEventHandler(object sender, LayoutEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/LeftRightAlignment.cs b/WindowsForms/Managed/System/WinForms/LeftRightAlignment.cs new file mode 100644 index 000000000..c1e1a16d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LeftRightAlignment.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies whether an object or text is aligned to + /// the left or + /// right of a reference point. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum LeftRightAlignment { + + /// + /// + /// + /// The object or + /// text is aligned to the left of the reference + /// point. + /// + /// + Left = 0, + + /// + /// + /// + /// The object or text is aligned to the right of the reference point. + /// + /// + Right = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkArea.cs b/WindowsForms/Managed/System/WinForms/LinkArea.cs new file mode 100644 index 000000000..5b5f9941d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkArea.cs @@ -0,0 +1,255 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Reflection; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Collections; + + /// + /// + /// [To be supplied.] + /// + [ + TypeConverterAttribute(typeof(LinkArea.LinkAreaConverter)), + Serializable + ] + public struct LinkArea { + int start; + int length; + + /// + /// + /// [To be supplied.] + /// + public LinkArea(int start, int length) { + this.start = start; + this.length = length; + } + + /// + /// + /// [To be supplied.] + /// + public int Start { + get { + return start; + } + set { + start = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public int Length { + get { + return length; + } + set { + length = value; + } + } + + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool IsEmpty { + get { + return length == start && start == 0; + } + } + /// + public override bool Equals(object o) { + if (!(o is LinkArea)) { + return false; + } + + LinkArea a = (LinkArea)o; + return this == a; + } + + public override string ToString() { + return "{Start=" + Start.ToString(CultureInfo.CurrentCulture) + ", Length=" + Length.ToString(CultureInfo.CurrentCulture) + "}"; + } + + public static bool operator == (LinkArea linkArea1, LinkArea linkArea2){ + return (linkArea1.start == linkArea2.start) && (linkArea1.length == linkArea2.length); + } + + public static bool operator != (LinkArea linkArea1, LinkArea linkArea2) { + return !(linkArea1 == linkArea2); + } + + /// + public override int GetHashCode() { + return start << 4 | length; + } + + /// + /// + /// LinkAreaConverter is a class that can be used to convert + /// LinkArea from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class LinkAreaConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + + string text = ((string)value).Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 2 integer values. + // + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(new char[] {sep}); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 2) { + return new LinkArea(values[0], values[1]); + } + else { + throw new ArgumentException(SR.GetString(SR.TextParseFailedFormat, + text, + "start, length")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string) && value is LinkArea) { + LinkArea pt = (LinkArea)value; + + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args = new string[2]; + int nArg = 0; + + args[nArg++] = intConverter.ConvertToString(context, culture, pt.Start); + args[nArg++] = intConverter.ConvertToString(context, culture, pt.Length); + + return string.Join(sep, args); + } + if (destinationType == typeof(InstanceDescriptor) && value is LinkArea) { + LinkArea pt = (LinkArea)value; + + ConstructorInfo ctor = typeof(LinkArea).GetConstructor(new Type[] {typeof(int), typeof(int)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {pt.Start, pt.Length}); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + return new LinkArea((int)propertyValues["Start"], + (int)propertyValues["Length"]); + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(LinkArea), attributes); + return props.Sort(new string[] {"Start", "Length"}); + } + + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkBehavior.cs b/WindowsForms/Managed/System/WinForms/LinkBehavior.cs new file mode 100644 index 000000000..6ad9143f4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkBehavior.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// [To be supplied.] + /// + public enum LinkBehavior { + /// + /// + /// [To be supplied.] + /// + SystemDefault, + /// + /// + /// [To be supplied.] + /// + AlwaysUnderline, + /// + /// + /// [To be supplied.] + /// + HoverUnderline, + /// + /// + /// [To be supplied.] + /// + NeverUnderline, + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkClickEvent.cs b/WindowsForms/Managed/System/WinForms/LinkClickEvent.cs new file mode 100644 index 000000000..7fe13b302 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkClickEvent.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class LinkClickedEventArgs : EventArgs { + private string linkText; + + /// + /// + /// + /// Gets the text of the link being clicked. + /// + /// + public string LinkText { + get { + return linkText; + } + } + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public LinkClickedEventArgs(string linkText) { + this.linkText = linkText; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/LinkClickEventHandler.cs b/WindowsForms/Managed/System/WinForms/LinkClickEventHandler.cs new file mode 100644 index 000000000..84f47e15b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkClickEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle + /// the event of + /// a . + /// + /// + public delegate void LinkClickedEventHandler(object sender, LinkClickedEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/LinkConverter.cs b/WindowsForms/Managed/System/WinForms/LinkConverter.cs new file mode 100644 index 000000000..12f24917a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkConverter.cs @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// A TypeConverter for LinkLabel.Link objects. Access this + /// class through the TypeDescriptor. + /// + public class LinkConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + + string text = ((string)value).Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 2 integer values - Start & Length of the Link. + // + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(new char[] {sep}); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 2) { + return new LinkLabel.Link(values[0], values[1]); + } + else { + throw new ArgumentException(SR.GetString(SR.TextParseFailedFormat, + text, + "Start, Length")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (value is LinkLabel.Link) { + if (destinationType == typeof(string)) { + LinkLabel.Link link = (LinkLabel.Link)value; + + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args = new string[2]; + int nArg = 0; + + args[nArg++] = intConverter.ConvertToString(context, culture, link.Start); + args[nArg++] = intConverter.ConvertToString(context, culture, link.Length); + + return string.Join(sep, args); + } + + if (destinationType == typeof(InstanceDescriptor)) { + LinkLabel.Link link = (LinkLabel.Link)value; + MemberInfo info; + if (link.LinkData == null) { + info = typeof(LinkLabel.Link).GetConstructor(new Type[] {typeof(int), typeof(int)}); + if (info != null) { + return new InstanceDescriptor(info, new object[] {link.Start, link.Length}, true); + } + } + else { + info = typeof(LinkLabel.Link).GetConstructor(new Type[] {typeof(int), typeof(int), typeof(object)}); + if (info != null) { + return new InstanceDescriptor(info, new object[] {link.Start, link.Length, link.LinkData}, true); + } + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/LinkLabel.cs b/WindowsForms/Managed/System/WinForms/LinkLabel.cs new file mode 100644 index 000000000..2767a0ddc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkLabel.cs @@ -0,0 +1,2855 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing.Design; + using System.Drawing.Drawing2D; + using System.Drawing.Text; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Serialization.Formatters; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Windows.Forms.ComponentModel; + using System.Globalization; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Windows.Forms.Internal; + using System; + using System.Runtime.Versioning; + + /// + /// + /// + /// Displays text that can contain a hyperlink. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("LinkClicked"), + ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionLinkLabel) + ] + public class LinkLabel : Label, IButtonControl { + + static readonly object EventLinkClicked = new object(); + static Color iedisabledLinkColor = Color.Empty; + + static LinkComparer linkComparer = new LinkComparer(); + + /// + /// + /// The dialog result that will be sent to the parent dialog form when + /// we are clicked. + /// + DialogResult dialogResult; + + Color linkColor = Color.Empty; + Color activeLinkColor = Color.Empty; + Color visitedLinkColor = Color.Empty; + Color disabledLinkColor = Color.Empty; + + Font linkFont; + Font hoverLinkFont; + + bool textLayoutValid = false; + bool receivedDoubleClick = false; + + ArrayList links = new ArrayList(2); + + Link focusLink = null; + LinkCollection linkCollection = null; + Region textRegion = null; + Cursor overrideCursor = null; + + bool processingOnGotFocus; // used to avoid raising the OnGotFocus event twice after selecting a focus link. + + LinkBehavior linkBehavior = System.Windows.Forms.LinkBehavior.SystemDefault; + + /// + /// + /// + /// Initializes a new default instance of the class. + /// + /// + public LinkLabel() : base() { + SetStyle(ControlStyles.AllPaintingInWmPaint + | ControlStyles.OptimizedDoubleBuffer + | ControlStyles.Opaque + | ControlStyles.UserPaint + | ControlStyles.StandardClick + | ControlStyles.ResizeRedraw, true); + ResetLinkArea(); + } + + /// + /// + /// + /// Gets or sets the color used to display active links. + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.LinkLabelActiveLinkColorDescr) + ] + public Color ActiveLinkColor { + get { + if (activeLinkColor.IsEmpty) { + return IEActiveLinkColor; + } + else { + return activeLinkColor; + } + } + set { + if (activeLinkColor != value) { + activeLinkColor = value; + InvalidateLink(null); + } + } + } + + /// + /// + /// + /// Gets or sets the color used to display disabled links. + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.LinkLabelDisabledLinkColorDescr) + ] + public Color DisabledLinkColor { + get { + if (disabledLinkColor.IsEmpty) { + return IEDisabledLinkColor; + } + else { + return disabledLinkColor; + } + } + set { + if (disabledLinkColor != value) { + disabledLinkColor = value; + InvalidateLink(null); + } + } + } + + private Link FocusLink { + get { + return focusLink; + } + set { + if (focusLink != value) { + + if (focusLink != null) { + InvalidateLink(focusLink); + } + + focusLink = value; + + if (focusLink != null) { + InvalidateLink(focusLink); + + UpdateAccessibilityLink(focusLink); + + } + } + } + } + + private Color IELinkColor { + get { + return LinkUtilities.IELinkColor; + } + } + + private Color IEActiveLinkColor { + get { + return LinkUtilities.IEActiveLinkColor; + } + } + private Color IEVisitedLinkColor { + get { + return LinkUtilities.IEVisitedLinkColor; + } + } + private Color IEDisabledLinkColor { + get { + if (iedisabledLinkColor.IsEmpty) { + iedisabledLinkColor = ControlPaint.Dark(DisabledColor); + } + return iedisabledLinkColor; + } + } + + private Rectangle ClientRectWithPadding { + get { + return LayoutUtils.DeflateRect(ClientRectangle, Padding); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new FlatStyle FlatStyle + { + get + { + return base.FlatStyle; + } + set + { + base.FlatStyle = value; + } + } + + /// + /// + /// + /// Gets or sets the range in the text that is treated as a link. + /// + /// + [ + Editor("System.Windows.Forms.Design.LinkAreaEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.LinkLabelLinkAreaDescr) + ] + public LinkArea LinkArea { + get { + if (links.Count == 0) { + return new LinkArea(0, 0); + } + return new LinkArea(((Link)links[0]).Start, ((Link)links[0]).Length); + } + set { + LinkArea pt = LinkArea; + + links.Clear(); + + if (!value.IsEmpty) { + if (value.Start < 0) { + throw new ArgumentOutOfRangeException("LinkArea", value, SR.GetString(SR.LinkLabelAreaStart)); + } + if (value.Length < -1) { + throw new ArgumentOutOfRangeException("LinkArea", value, SR.GetString(SR.LinkLabelAreaLength)); + } + + if (value.Start != 0 || value.Length != 0) { + Links.Add(new Link(this)); + + // Update the link area of the first link + // + ((Link)links[0]).Start = value.Start; + ((Link)links[0]).Length = value.Length; + } + } + + UpdateSelectability(); + + if (!pt.Equals(LinkArea)) { + InvalidateTextLayout(); + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.LinkArea); + base.AdjustSize(); + Invalidate(); + } + } + } + + /// + /// + /// + /// Gets ir sets a value that represents how the link will be underlined. + /// + /// + [ + DefaultValue(LinkBehavior.SystemDefault), + SRCategory(SR.CatBehavior), + SRDescription(SR.LinkLabelLinkBehaviorDescr) + ] + public LinkBehavior LinkBehavior { + get { + return linkBehavior; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)LinkBehavior.SystemDefault, (int)LinkBehavior.NeverUnderline)){ + throw new InvalidEnumArgumentException("LinkBehavior", (int)value, typeof(LinkBehavior)); + } + if (value != linkBehavior) { + linkBehavior = value; + InvalidateLinkFonts(); + InvalidateLink(null); + } + } + } + + /// + /// + /// + /// Gets or sets the color used to display links in normal cases. + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.LinkLabelLinkColorDescr) + ] + public Color LinkColor { + get { + if (linkColor.IsEmpty) { + if (SystemInformation.HighContrast) { + return SystemColors.HotTrack; + } + return IELinkColor; + } + else { + return linkColor; + } + } + set { + if (linkColor != value) { + linkColor = value; + InvalidateLink(null); + } + } + } + + /// + /// + /// + /// Gets the collection of links used in a . + /// + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public LinkCollection Links { + get { + if (linkCollection == null) { + linkCollection = new LinkCollection(this); + } + return linkCollection; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the link should be displayed as if it was visited. + /// + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.LinkLabelLinkVisitedDescr) + ] + public bool LinkVisited { + get { + if (links.Count == 0) { + return false; + } + else { + return((Link)links[0]).Visited; + } + } + set { + if (value != LinkVisited) { + if (links.Count == 0) { + Links.Add(new Link(this)); + } + ((Link)links[0]).Visited = value; + } + } + } + + + // link labels must always ownerdraw + // + internal override bool OwnerDraw { + get { + return true; + } + } + + + /// + /// + /// [To be supplied.] + /// + protected Cursor OverrideCursor { + get { + return overrideCursor; + } + set { + if (overrideCursor != value) { + overrideCursor = value; + + if (IsHandleCreated) { + // We want to instantly change the cursor if the mouse is within our bounds. + // This includes the case where the mouse is over one of our children + NativeMethods.POINT p = new NativeMethods.POINT(); + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetCursorPos(p); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + + // + if ((r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom) || UnsafeNativeMethods.GetCapture() == Handle) + SendMessage(NativeMethods.WM_SETCURSOR, Handle, NativeMethods.HTCLIENT); + } + } + } + } + + /// + /// + // VSWhidbey 106827: Make this event visible through the property browser. + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + [RefreshProperties(RefreshProperties.Repaint)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + [RefreshProperties(RefreshProperties.Repaint)] + public new Padding Padding { + get {return base.Padding;} + set { base.Padding = value;} + } + + /// + /// + /// + /// Gets or sets the color used to display the link once it has been visited. + /// + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.LinkLabelVisitedLinkColorDescr) + ] + public Color VisitedLinkColor { + get { + if (visitedLinkColor.IsEmpty) { + if (SystemInformation.HighContrast) { + return LinkUtilities.GetVisitedLinkColor(); + } + return IEVisitedLinkColor; + } + else { + return visitedLinkColor; + } + } + set { + if (visitedLinkColor != value) { + visitedLinkColor = value; + InvalidateLink(null); + } + } + } + + /// + /// + /// + /// Occurs when the link is clicked. + /// + /// + [WinCategory("Action"), SRDescription(SR.LinkLabelLinkClickedDescr)] + public event LinkLabelLinkClickedEventHandler LinkClicked { + add { + Events.AddHandler(EventLinkClicked, value); + } + remove { + Events.RemoveHandler(EventLinkClicked, value); + } + } + + internal static Rectangle CalcTextRenderBounds(Rectangle textRect, Rectangle clientRect, ContentAlignment align) { + int xLoc, yLoc, width, height; + + if ((align & WindowsFormsUtils.AnyRightAlign) != 0) { + xLoc = clientRect.Right - textRect.Width; + } else if ((align & WindowsFormsUtils.AnyCenterAlign) != 0) { + xLoc = (clientRect.Width - textRect.Width) / 2; + } else { + xLoc = clientRect.X; + } + + if ((align & WindowsFormsUtils.AnyBottomAlign) != 0) { + yLoc = clientRect.Bottom - textRect.Height; + } else if ((align & WindowsFormsUtils.AnyMiddleAlign) != 0) { + yLoc = (clientRect.Height - textRect.Height) / 2; + } else { + yLoc = clientRect.Y; + } + + // If the text rect does not fit in the client rect, make it fit. + if (textRect.Width > clientRect.Width) { + xLoc = clientRect.X; + width = clientRect.Width; + } else { + width = textRect.Width; + } + + if (textRect.Height > clientRect.Height) { + yLoc = clientRect.Y; + height = clientRect.Height; + } else { + height = textRect.Height; + } + + return new Rectangle(xLoc, yLoc, width, height); + } + + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new LinkLabelAccessibleObject(this); + } + + /// + /// + /// + /// Creates a handle for this control. This method is called by the .NET Framework, + /// this should not be called. Inheriting classes should always call + /// base.createHandle when overriding this method. + /// + /// + protected override void CreateHandle() { + base.CreateHandle(); + InvalidateTextLayout(); + } + + /// + /// Determines whether the current state of the control allows for rendering text using + /// TextRenderer (GDI). + /// The Gdi library doesn't currently have a way to calculate character ranges so we cannot + /// use it for painting link(s) within the text, but if the link are is null or covers the + /// entire text we are ok since it is just one area with the same size of the text binding + /// area. + /// + internal override bool CanUseTextRenderer { + get{ + // If no link or the LinkArea is one and covers the entire text, we can support UseCompatibleTextRendering = false. + // Observe that LinkArea refers to the first link always. + StringInfo stringInfo = new StringInfo( this.Text ); + return this.LinkArea.Start == 0 && ( this.LinkArea.Length == 0 || this.LinkArea.Length == stringInfo.LengthInTextElements ); + } + } + + internal override bool UseGDIMeasuring() { + return !UseCompatibleTextRendering; + } + + /// + /// Converts the character index into char index of the string + /// This method is copied in LinkCollectionEditor.cs. Update the other + /// one as well if you change this method. + /// This method mainly deal with surrogate. Suppose we + /// have a string consisting of 3 surrogates, and we want the + /// second character, then the index we need should be 2 instead of + /// 1, and this method returns the correct index. + /// + private static int ConvertToCharIndex(int index, string text) { + if (index <= 0) { + return 0; + } + if (String.IsNullOrEmpty(text)) { + Debug.Assert(text != null, "string should not be null"); + //do no conversion, just return the original value passed in + return index; + } + + //VSWhidbey 217272: Dealing with surrogate characters + //in some languages, characters can expand over multiple + //chars, using StringInfo lets us properly deal with it. + StringInfo stringInfo = new StringInfo(text); + int numTextElements = stringInfo.LengthInTextElements; + + //index is greater than the length of the string + if (index > numTextElements) { + return index - numTextElements + text.Length; //pretend all the characters after are ASCII characters + } + //return the length of the substring which has specified number of characters + string sub = stringInfo.SubstringByTextElements(0, index); + return sub.Length; + } + + + /// + /// + /// Ensures that we have analyzed the text run so that we can render each segment + /// and link. + /// + private void EnsureRun(Graphics g) { + + // bail early if everything is valid! + // + if (textLayoutValid) { + return; + } + if (this.textRegion != null) { + this.textRegion.Dispose(); + this.textRegion = null; + } + + // bail early for no text + // + if (Text.Length == 0) { + Links.Clear(); + Links.Add (new Link (0, -1)); // default 'magic' link. + textLayoutValid = true; + return; + } + + StringFormat textFormat = CreateStringFormat(); + string text = Text; + try { + + Font alwaysUnderlined = new Font(Font, Font.Style | FontStyle.Underline); + Graphics created = null; + + try { + if (g == null) { + g = created = CreateGraphicsInternal(); + } + + if( UseCompatibleTextRendering ){ + Region[] textRegions = g.MeasureCharacterRanges (text, alwaysUnderlined, ClientRectWithPadding, textFormat); + + int regionIndex = 0; + + for (int i=0; i + /// Calculate character ranges taking into account the locale. Provided for surrogate chars support. + /// + private CharacterRange[] AdjustCharacterRangesForSurrogateChars(){ + string text = Text; + + if (String.IsNullOrEmpty(text)) { + return new CharacterRange[]{}; + } + + StringInfo stringInfo = new StringInfo(text); + int textLen = stringInfo.LengthInTextElements; + ArrayList ranges = new ArrayList(Links.Count); + + foreach (Link link in Links) { + int charStart = ConvertToCharIndex(link.Start, text); + int charEnd = ConvertToCharIndex(link.Start + link.Length, text); + if (LinkInText(charStart, charEnd - charStart)) { + int length = (int) Math.Min(link.Length, textLen - link.Start); + ranges.Add(new CharacterRange(charStart, ConvertToCharIndex(link.Start + length, text) - charStart)); + } + } + + CharacterRange[] regions = new CharacterRange[ranges.Count + 1]; + ranges.CopyTo(regions, 0); + regions[regions.Length - 1] = new CharacterRange(0, text.Length); + + return regions; + } + + /// + /// Determines whether the whole link label contains only one link, + /// and the link runs from the beginning of the label to the end of it + /// + private bool IsOneLink() { + if (links == null || links.Count != 1 || Text == null) { + return false; + } + StringInfo stringInfo = new StringInfo(Text); + if (LinkArea.Start == 0 && LinkArea.Length == stringInfo.LengthInTextElements) { + return true; + } + return false; + } + + /// + /// + /// Determines if the given client coordinates is contained within a portion + /// of a link area. + /// + protected Link PointInLink(int x, int y) { + Graphics g = CreateGraphicsInternal(); + Link hit = null; + try { + EnsureRun(g); + foreach (Link link in links) { + if (link.VisualRegion != null && link.VisualRegion.IsVisible(x, y, g)) { + hit = link; + break; + } + } + } + finally { + g.Dispose(); + g = null; + } + return hit; + } + + /// + /// + /// Invalidates only the portions of the text that is linked to + /// the specified link. If link is null, then all linked text + /// is invalidated. + /// + private void InvalidateLink(Link link) { + if (IsHandleCreated) { + if (link == null || link.VisualRegion == null || IsOneLink()) { + Invalidate(); + } + else{ + Invalidate(link.VisualRegion); + } + } + } + + /// + /// + /// Invalidates the current set of fonts we use when painting + /// links. The fonts will be recreated when needed. + /// + private void InvalidateLinkFonts() { + + if (linkFont != null) { + linkFont.Dispose(); + } + + if (hoverLinkFont != null && hoverLinkFont != linkFont) { + hoverLinkFont.Dispose(); + } + + linkFont = null; + hoverLinkFont = null; + } + + private void InvalidateTextLayout() { + textLayoutValid = false; + } + + private bool LinkInText(int start, int length) { + return(0 <= start && start < Text.Length && 0 < length); + } + + /// + /// + /// + /// + /// Gets or sets a value that is returned to the + /// parent form when the link label. + /// is clicked. + /// + /// + DialogResult IButtonControl.DialogResult { + get { + return dialogResult; + } + + set { + //valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DialogResult.None, (int)DialogResult.No)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DialogResult)); + } + + dialogResult = value; + } + } + + /// + /// + void IButtonControl.NotifyDefault(bool value) { + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + protected override void OnGotFocus(EventArgs e) { + if (!this.processingOnGotFocus) { + base.OnGotFocus(e); + this.processingOnGotFocus = true; + } + + try { + Link focusLink = FocusLink; + if (focusLink == null) { + // SECREVIEW: This assert is required because the call to Select will generate a call to SetActiveControl which have a demand, + // it is safe since no input from user code is processed while setting the active control originated this way. + // + IntSecurity.ModifyFocus.Assert(); + + // Set focus on first link. + // This will raise the OnGotFocus event again but it will not be processed because processingOnGotFocus is true. + Select(true /*directed*/, true /*forward*/); + } + else { + InvalidateLink(focusLink); + UpdateAccessibilityLink(focusLink); + } + } + finally { + if (this.processingOnGotFocus) { + this.processingOnGotFocus = false; + } + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + + if (FocusLink != null) { + InvalidateLink(FocusLink); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnKeyDown(KeyEventArgs e) { + base.OnKeyDown(e); + + if (e.KeyCode == Keys.Enter) { + if (FocusLink != null && FocusLink.Enabled) { + OnLinkClicked(new LinkLabelLinkClickedEventArgs(FocusLink)); + } + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + if (!Enabled) { + return; + } + + foreach (Link link in links) { + if ((link.State & LinkState.Hover) == LinkState.Hover + || (link.State & LinkState.Active) == LinkState.Active) { + + bool activeChanged = (link.State & LinkState.Active) == LinkState.Active; + link.State &= ~(LinkState.Hover | LinkState.Active); + + if (activeChanged || hoverLinkFont != linkFont) { + InvalidateLink(link); + } + OverrideCursor = null; + } + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown(e); + + if (!Enabled || e.Clicks > 1) { + receivedDoubleClick = true; + return; + } + + for (int i=0; i + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnMouseUp(MouseEventArgs e) { + + base.OnMouseUp(e); + + // bug 95211 : don't do any Handle-related operations when in disposing state + if (Disposing || IsDisposed) { + return; + } + + if (!Enabled || e.Clicks > 1 || receivedDoubleClick) { + receivedDoubleClick = false; + return; + } + + for (int i=0; i + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + + if (!Enabled) { + return; + } + + Link hoverLink = null; + foreach (Link link in links) { + if ((link.State & LinkState.Hover) == LinkState.Hover) { + hoverLink = link; + break; + } + } + + Link pointIn = PointInLink(e.X, e.Y); + + if (pointIn != hoverLink) { + if (hoverLink != null) { + hoverLink.State &= ~LinkState.Hover; + } + if (pointIn != null) { + pointIn.State |= LinkState.Hover; + if (pointIn.Enabled) { + OverrideCursor = Cursors.Hand; + } + } + else { + OverrideCursor = null; + } + + if (hoverLinkFont != linkFont) { + if (hoverLink != null) { + InvalidateLink(hoverLink); + } + if (pointIn != null) { + InvalidateLink(pointIn); + } + } + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnLinkClicked(LinkLabelLinkClickedEventArgs e) { + LinkLabelLinkClickedEventHandler handler = (LinkLabelLinkClickedEventHandler)Events[EventLinkClicked]; + if (handler != null) { + handler(this, e); + } + } + + protected override void OnPaddingChanged(EventArgs e) { + base.OnPaddingChanged(e); + InvalidateTextLayout(); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnPaint(PaintEventArgs e) { + RectangleF finalrect = RectangleF.Empty; //the focus rectangle if there is only one link + Animate(); + + ImageAnimator.UpdateFrames(this.Image); + EnsureRun(e.Graphics); + + // bail early for no text + // + if (Text.Length == 0) { + PaintLinkBackground(e.Graphics); + } + // Paint enabled link label + // + else { + if (AutoEllipsis) { + Rectangle clientRect = this.ClientRectWithPadding; + Size preferredSize = GetPreferredSize(new Size(clientRect.Width, clientRect.Height)); + showToolTip = (clientRect.Width < preferredSize.Width || clientRect.Height < preferredSize.Height); + } + else { + showToolTip = false; + } + + if (this.Enabled) { // Control.Enabled not to be confused with Link.Enabled + bool optimizeBackgroundRendering = !GetStyle(ControlStyles.OptimizedDoubleBuffer); + SolidBrush foreBrush = new SolidBrush(ForeColor); + SolidBrush linkBrush = new SolidBrush(LinkColor); + + try { + if (!optimizeBackgroundRendering) { + PaintLinkBackground(e.Graphics); + } + + LinkUtilities.EnsureLinkFonts(this.Font, this.LinkBehavior, ref this.linkFont, ref this.hoverLinkFont); + + Region originalClip = e.Graphics.Clip; + + try { + if (IsOneLink()) { + //exclude the area to draw the focus rectangle + e.Graphics.Clip = originalClip; + RectangleF[] rects = ((Link)links[0]).VisualRegion.GetRegionScans(e.Graphics.Transform); + if (rects != null && rects.Length > 0) { + if (UseCompatibleTextRendering) { + finalrect = new RectangleF(rects[0].Location, SizeF.Empty); + foreach (RectangleF rect in rects) { + finalrect = RectangleF.Union(finalrect, rect); + } + } + else { + finalrect = this.ClientRectWithPadding; + Size finalRectSize = finalrect.Size.ToSize(); + + Size requiredSize = MeasureTextCache.GetTextSize(Text, Font, finalRectSize, CreateTextFormatFlags(finalRectSize)); + + finalrect.Width = requiredSize.Width; + + if (requiredSize.Height < finalrect.Height) { + finalrect.Height = requiredSize.Height; + } + finalrect = CalcTextRenderBounds(System.Drawing.Rectangle.Round(finalrect) /*textRect*/, this.ClientRectWithPadding /*clientRect*/, RtlTranslateContent(this.TextAlign)); + } + using (Region region = new Region(finalrect)) { + e.Graphics.ExcludeClip(region); + } + } + } + else { + foreach (Link link in links) { + if (link.VisualRegion != null) { + e.Graphics.ExcludeClip(link.VisualRegion); + } + } + } + + // Fix for Windows 7 bug 361974 + // When there is only one link in link label, + // it's not necessary to paint with forebrush first + // as it will be overlapped by linkbrush in the following steps + if (!IsOneLink()) { + PaintLink(e.Graphics, null, foreBrush, linkBrush, optimizeBackgroundRendering, finalrect); + } + + foreach (Link link in links) { + PaintLink(e.Graphics, link, foreBrush, linkBrush, optimizeBackgroundRendering, finalrect); + } + + if (optimizeBackgroundRendering) { + e.Graphics.Clip = originalClip; + e.Graphics.ExcludeClip(this.textRegion); + PaintLinkBackground(e.Graphics); + } + } + finally { + e.Graphics.Clip = originalClip; + } + } + finally { + foreBrush.Dispose(); + linkBrush.Dispose(); + } + } + // Paint disabled link label (disabled control, not to be confused with disabled link). + // + else { + Region originalClip = e.Graphics.Clip; + + try { + // We need to paint the background first before clipping to textRegion because it is calculated using + // ClientRectWithPadding which in some cases is smaller that ClientRectangle. + // + PaintLinkBackground(e.Graphics); + e.Graphics.IntersectClip(this.textRegion); + + Color foreColor; + + if (UseCompatibleTextRendering) { + // APPCOMPAT: Use DisabledColor because Everett used DisabledColor. + // (ie, dont use Graphics.GetNearestColor(DisabledColor.) + // vsw 495322. + StringFormat stringFormat = CreateStringFormat(); + ControlPaint.DrawStringDisabled(e.Graphics, Text, Font, DisabledColor, ClientRectWithPadding, stringFormat); + } + else { + IntPtr hdc = e.Graphics.GetHdc(); + try { + using (WindowsGraphics wg = WindowsGraphics.FromHdc(hdc)) { + foreColor = wg.GetNearestColor(DisabledColor); + } + } + finally { + e.Graphics.ReleaseHdc(); + } + Rectangle clientRectWidthPadding = ClientRectWithPadding; + + ControlPaint.DrawStringDisabled(e.Graphics, Text, Font, foreColor, clientRectWidthPadding, CreateTextFormatFlags(clientRectWidthPadding.Size)); + } + } + finally { + e.Graphics.Clip = originalClip; + } + } + } + + // We can't call base.OnPaint because labels paint differently from link labels, + // but we still need to raise the Paint event. + // + RaisePaintEvent(this, e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnPaintBackground(PaintEventArgs e) { + Image i = this.Image; + + if (i != null) { + Region oldClip = e.Graphics.Clip; + Rectangle imageBounds = CalcImageRenderBounds(i, ClientRectangle, RtlTranslateAlignment(ImageAlign)); + e.Graphics.ExcludeClip(imageBounds); + try { + base.OnPaintBackground(e); + } + finally { + e.Graphics.Clip = oldClip; + } + + e.Graphics.IntersectClip(imageBounds); + try { + base.OnPaintBackground(e); + DrawImage(e.Graphics, i, ClientRectangle, RtlTranslateAlignment(ImageAlign)); + } + finally { + e.Graphics.Clip = oldClip; + } + } + else { + base.OnPaintBackground(e); + } + + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + InvalidateTextLayout(); + InvalidateLinkFonts(); + Invalidate(); + } + + /// + /// + protected override void OnAutoSizeChanged(EventArgs e) { + base.OnAutoSizeChanged(e); + InvalidateTextLayout(); + } + + /// + /// Overriden by LinkLabel. + /// + internal override void OnAutoEllipsisChanged(/*EventArgs e*/) { + base.OnAutoEllipsisChanged(/*e*/); + InvalidateTextLayout(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + + if (!Enabled) { + for (int i=0; i + /// + /// [To be supplied.] + /// + protected override void OnTextChanged(EventArgs e) { + base.OnTextChanged(e); + InvalidateTextLayout(); + UpdateSelectability(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnTextAlignChanged(EventArgs e) { + base.OnTextAlignChanged(e); + InvalidateTextLayout(); + UpdateSelectability(); + } + + private void PaintLink(Graphics g, Link link, SolidBrush foreBrush, SolidBrush linkBrush, bool optimizeBackgroundRendering, RectangleF finalrect) { + + // link = null means paint the whole text + + Debug.Assert(g != null, "Must pass valid graphics"); + Debug.Assert(foreBrush != null, "Must pass valid foreBrush"); + Debug.Assert(linkBrush != null, "Must pass valid linkBrush"); + + Font font = Font; + + if (link != null) { + if (link.VisualRegion != null) { + Color brushColor = Color.Empty; + LinkState linkState = link.State; + + if ((linkState & LinkState.Hover) == LinkState.Hover) { + font = hoverLinkFont; + } + else { + font = linkFont; + } + + if (link.Enabled) { // Not to be confused with Control.Enabled. + if ((linkState & LinkState.Active) == LinkState.Active) { + brushColor = ActiveLinkColor; + } + else if ((linkState & LinkState.Visited) == LinkState.Visited) { + brushColor = VisitedLinkColor; + } + // else use linkBrush + } + else { + brushColor = DisabledLinkColor; + } + + if (IsOneLink()) { + g.Clip = new Region(finalrect); + } + else { + g.Clip = link.VisualRegion; + } + + if (optimizeBackgroundRendering) { + PaintLinkBackground(g); + } + + if( UseCompatibleTextRendering ){ + SolidBrush useBrush = brushColor == Color.Empty ? linkBrush : new SolidBrush( brushColor ); + StringFormat stringFormat = CreateStringFormat(); + + g.DrawString(Text, font, useBrush, ClientRectWithPadding, stringFormat); + + if( useBrush != linkBrush ){ + useBrush.Dispose(); + } + } + else{ + if(brushColor == Color.Empty ){ + brushColor = linkBrush.Color; + } + + IntPtr hdc = g.GetHdc(); + try{ + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc ) ) { + brushColor = wg.GetNearestColor(brushColor); + } + } + finally{ + g.ReleaseHdc(); + } + Rectangle clientRectWithPadding = ClientRectWithPadding; + TextRenderer.DrawText(g, Text, font, clientRectWithPadding, brushColor, CreateTextFormatFlags(clientRectWithPadding.Size)); + } + + if (Focused && ShowFocusCues && FocusLink == link) { + // Get the rectangles making up the visual region, and draw + // each one. + RectangleF[] rects = link.VisualRegion.GetRegionScans(g.Transform); + if( rects != null && rects.Length > 0 ){ + Rectangle focusRect; + + if (IsOneLink()) { + //draw one merged focus rectangle + focusRect = Rectangle.Ceiling(finalrect); + Debug.Assert(finalrect != RectangleF.Empty, "finalrect should be initialized"); + + ControlPaint.DrawFocusRectangle(g, focusRect, ForeColor, BackColor); + } + else { + foreach (RectangleF rect in rects) { + ControlPaint.DrawFocusRectangle(g, Rectangle.Ceiling(rect), ForeColor, BackColor); + } + } + } + } + } + + // no else clause... we don't paint anything if we are given a link with no visual region. + // + } + else { // Painting with no link. + g.IntersectClip(this.textRegion); + + if (optimizeBackgroundRendering) { + PaintLinkBackground(g); + } + + if( UseCompatibleTextRendering ){ + StringFormat stringFormat = CreateStringFormat(); + g.DrawString(Text, font, foreBrush, ClientRectWithPadding, stringFormat); + } + else{ + Color color; + + IntPtr hdc = g.GetHdc(); + try{ + using( WindowsGraphics wg = WindowsGraphics.FromHdc( hdc ) ) { + color = wg.GetNearestColor(foreBrush.Color); + } + } + finally{ + g.ReleaseHdc(); + } + Rectangle clientRectWithPadding = ClientRectWithPadding; + TextRenderer.DrawText(g, Text, font, clientRectWithPadding, color, CreateTextFormatFlags(clientRectWithPadding.Size)); + } + } + } + + private void PaintLinkBackground(Graphics g) { + using (PaintEventArgs e = new PaintEventArgs(g, ClientRectangle)) { + InvokePaintBackground(this, e); + } + } + + /// + /// + void IButtonControl.PerformClick() { + + // If a link is not currently focused, focus on the first link + // + if (FocusLink == null && Links.Count > 0) { + string text = Text; + foreach (Link link in Links) { + int charStart = ConvertToCharIndex(link.Start, text); + int charEnd = ConvertToCharIndex(link.Start + link.Length, text); + if (link.Enabled && LinkInText(charStart, charEnd - charStart)) { + FocusLink = link; + break; + } + } + } + + // Act as if the focused link was clicked + // + if (FocusLink != null) { + OnLinkClicked(new LinkLabelLinkClickedEventArgs(FocusLink)); + } + } + + /// + /// + /// + /// Processes a dialog key. This method is called during message pre-processing + /// to handle dialog characters, such as TAB, RETURN, ESCAPE, and arrow keys. This + /// method is called only if the isInputKey() method indicates that the control + /// isn't interested in the key. processDialogKey() simply sends the character to + /// the parent's processDialogKey() method, or returns false if the control has no + /// parent. The Form class overrides this method to perform actual processing + /// of dialog keys. When overriding processDialogKey(), a control should return true + /// to indicate that it has processed the key. For keys that aren't processed by the + /// control, the result of "base.processDialogChar()" should be returned. Controls + /// will seldom, if ever, need to override this method. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + if ((keyData & (Keys.Alt | Keys.Control)) != Keys.Alt) { + Keys keyCode = keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Tab: + if (TabStop) { + bool forward = (keyData & Keys.Shift) != Keys.Shift; + if (FocusNextLink(forward)) { + return true; + } + } + break; + case Keys.Up: + case Keys.Left: + if (FocusNextLink(false)) { + return true; + } + break; + case Keys.Down: + case Keys.Right: + if (FocusNextLink(true)) { + return true; + } + break; + } + } + return base.ProcessDialogKey(keyData); + } + + private bool FocusNextLink(bool forward) { + int focusIndex = -1; + if (focusLink != null) { + for (int i=0; i= 0) { + test = Links[focusIndex]; + charStart = ConvertToCharIndex(test.Start, text); + charEnd = ConvertToCharIndex(test.Start + test.Length, text); + } + else { + test = null; + } + + } while (test != null + && !test.Enabled + && LinkInText(charStart, charEnd - charStart)); + } + + if (focusIndex < 0 || focusIndex >= links.Count) { + return -1; + } + else { + return focusIndex; + } + } + + private void ResetLinkArea() { + LinkArea = new LinkArea(0, -1); + } + + internal void ResetActiveLinkColor() { + activeLinkColor = Color.Empty; + } + + internal void ResetDisabledLinkColor() { + disabledLinkColor = Color.Empty; + } + + internal void ResetLinkColor() { + linkColor = Color.Empty; + InvalidateLink(null); + } + + private void ResetVisitedLinkColor() { + visitedLinkColor = Color.Empty; + } + + + /// + /// + /// + /// Performs the work of setting the bounds of this control. Inheriting classes + /// can overide this function to add size restrictions. Inheriting classes must call + /// base.setBoundsCore to actually cause the bounds of the control to change. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + // we cache too much state to try and optimize this (regions, etc)... it is best + // to always relayout here... If we want to resurect this code in the future, + // remember that we need to handle a word wrapped top aligned text that + // will become newly exposed (and therefore layed out) when we resize... + // + /* + ContentAlignment anyTop = ContentAlignment.TopLeft | ContentAlignment.TopCenter | ContentAlignment.TopRight; + + if ((TextAlign & anyTop) == 0 || Width != width || (Image != null && (ImageAlign & anyTop) == 0)) { + InvalidateTextLayout(); + Invalidate(); + } + */ + + InvalidateTextLayout(); + Invalidate(); + + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// [To be supplied.] + /// + protected override void Select(bool directed, bool forward) { + + if (directed) { + // In a multi-link label, if the tab came from another control, we want to keep the currently + // focused link, otherwise, we set the focus to the next link. + if (links.Count > 0) { + + // Find which link is currently focused + // + int focusIndex = -1; + if (FocusLink != null) { + focusIndex = links.IndexOf(FocusLink); + } + + // We could be getting focus from ourself, so we must + // invalidate each time. + // + FocusLink = null; + + int newFocus = GetNextLinkIndex(focusIndex, forward); + if (newFocus == -1) { + if (forward) { + newFocus = GetNextLinkIndex(-1, forward); // -1, so "next" will be 0 + } + else { + newFocus = GetNextLinkIndex(links.Count, forward); // Count, so "next" will be Count-1 + } + } + + if (newFocus != -1) { + FocusLink = (Link)links[newFocus]; + } + } + } + base.Select(directed, forward); + } + + /// + /// + /// + /// Determines if the color for active links should remain the same. + /// + /// + internal bool ShouldSerializeActiveLinkColor() { + return !activeLinkColor.IsEmpty; + } + + /// + /// + /// + /// Determines if the color for disabled links should remain the same. + /// + /// + internal bool ShouldSerializeDisabledLinkColor() { + return !disabledLinkColor.IsEmpty; + } + + /// + /// + /// + /// Determines if the range in text that is treated as a + /// link should remain the same. + /// + /// + private bool ShouldSerializeLinkArea() { + if (links.Count == 1) { + // use field access to find out if "length" is really -1 + return Links[0].Start != 0 || Links[0].length != -1; + } + return true; + + } + + /// + /// + /// + /// Determines if the color of links in normal cases should remain the same. + /// + /// + internal bool ShouldSerializeLinkColor() { + return !linkColor.IsEmpty; + } + + /// + /// Determines whether designer should generate code for setting the UseCompatibleTextRendering or not. + /// DefaultValue(false) + /// + private bool ShouldSerializeUseCompatibleTextRendering() { + // Serialize code if LinkLabel cannot support the feature or the property's value is not the default. + return !CanUseTextRenderer || UseCompatibleTextRendering != Control.UseCompatibleTextRenderingDefault; + } + + /// + /// + /// + /// Determines if the color of links that have been visited should remain the same. + /// + /// + private bool ShouldSerializeVisitedLinkColor() { + return !visitedLinkColor.IsEmpty; + } + + + + /// + /// + /// Update accessibility with the currently focused link. + /// + /// + private void UpdateAccessibilityLink(Link focusLink) { + + if (!IsHandleCreated) { + + return; + } + + int focusIndex = -1; + for (int i=0; i + /// + /// Validates that no links overlap. This will throw an exception if + /// they do. + /// + private void ValidateNoOverlappingLinks() { + for (int x=0; x + /// + /// Updates the label's ability to get focus. If there are + /// any links in the label, then the label can get focus, + /// else it can't. + /// + private void UpdateSelectability() { + LinkArea pt = LinkArea; + bool selectable = false; + string text = Text; + int charStart = ConvertToCharIndex(pt.Start, text); + int charEnd = ConvertToCharIndex(pt.Start + pt.Length, text); + + if (LinkInText(charStart, charEnd - charStart)) { + selectable = true; + } + else { + // If a link is currently focused, de-select it + // + if (FocusLink != null) { + FocusLink = null; + } + } + + OverrideCursor = null; + TabStop = selectable; + SetStyle(ControlStyles.Selectable, selectable); + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + // DefaultValue(false), - // See ShouldSerailizeUseCompatibleTextRendering method. + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public new bool UseCompatibleTextRendering { + get { + Debug.Assert( CanUseTextRenderer || base.UseCompatibleTextRendering, "Using GDI text rendering when CanUseTextRenderer reported false." ); + return base.UseCompatibleTextRendering; + } + set { + if (base.UseCompatibleTextRendering != value) { + // Cache the value so it is restored if CanUseTextRenderer becomes true and the designer can undo changes to this as side effect. + base.UseCompatibleTextRendering = value; + InvalidateTextLayout(); + } + } + } + + internal override bool SupportsUiaProviders { + get { + return false; + } + } + + /// + /// + /// Handles the WM_SETCURSOR message + /// + /// + private void WmSetCursor(ref Message m) { + + // Accessing through the Handle property has side effects that break this + // logic. You must use InternalHandle. + // + if (m.WParam == InternalHandle && NativeMethods.Util.LOWORD(m.LParam) == NativeMethods.HTCLIENT) { + if (OverrideCursor != null) { + Cursor.CurrentInternal = OverrideCursor; + } + else { + Cursor.CurrentInternal = Cursor; + } + } + else { + DefWndProc(ref m); + } + + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message msg) { + switch (msg.Msg) { + case NativeMethods.WM_SETCURSOR: + WmSetCursor(ref msg); + break; + default: + base.WndProc(ref msg); + break; + } + } + + /// + /// + /// [To be supplied.] + /// + public class LinkCollection : IList { + private LinkLabel owner; + private bool linksAdded = false; //whether we should serialize the linkCollection + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways. + private int lastAccessedIndex = -1; + + /// + /// + /// [To be supplied.] + /// + public LinkCollection(LinkLabel owner) { + if (owner == null) + throw new ArgumentNullException("owner"); + this.owner = owner; + } + + /// + /// + /// [To be supplied.] + /// + public virtual Link this[int index] { + get { + return(Link)owner.links[index]; + } + set { + owner.links[index] = value; + + owner.links.Sort(LinkLabel.linkComparer); + + owner.InvalidateTextLayout(); + owner.Invalidate(); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is Link) { + this[index] = (Link)value; + } + else { + throw new ArgumentException(SR.GetString(SR.LinkLabelBadLink),"value"); + } + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual Link this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false)] + public int Count { + get { + return owner.links.Count; + } + } + + /// + /// whether we have added a non-trivial link to the collection + /// + + public bool LinksAdded { + get { + return linksAdded; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public Link Add(int start, int length) { + if (length != 0) { + linksAdded = true; + } + return Add(start, length, null); + } + + /// + /// + /// [To be supplied.] + /// + public Link Add(int start, int length, object linkData) { + if (length != 0) { + linksAdded = true; + } + // check for the special case where the list is in the "magic" + // state of having only the default link in it. In that case + // we want to clear the list before adding this link. + // + if (owner.links.Count == 1 + && this[0].Start == 0 + && this[0].length == -1) { + + owner.links.Clear(); + owner.FocusLink = null; + } + + Link l = new Link(owner); + l.Start = start; + l.Length = length; + l.LinkData = linkData; + Add(l); + return l; + } + + /// + /// + /// [To be supplied.] + /// + public int Add(Link value) { + if (value != null && value.Length != 0) { + linksAdded = true; + } + // check for the special case where the list is in the "magic" + // state of having only the default link in it. In that case + // we want to clear the list before adding this link. + // + if (owner.links.Count == 1 + && this[0].Start == 0 + && this[0].length == -1) { + + owner.links.Clear(); + owner.FocusLink = null; + } + + // Set the owner control for this link + value.Owner = this.owner; + + owner.links.Add(value); + + if (this.owner.AutoSize) { + LayoutTransaction.DoLayout(this.owner.ParentInternal, this.owner, PropertyNames.Links); + this.owner.AdjustSize(); + this.owner.Invalidate(); + } + + if (owner.Links.Count > 1) { + owner.links.Sort(LinkLabel.linkComparer); + } + + owner.ValidateNoOverlappingLinks(); + owner.UpdateSelectability(); + owner.InvalidateTextLayout(); + owner.Invalidate(); + + if (owner.Links.Count > 1) { + return IndexOf(value); + } + else { + return 0; + } + } + + /// + /// + int IList.Add(object value) { + if (value is Link) { + return Add((Link)value); + } + else { + throw new ArgumentException(SR.GetString(SR.LinkLabelBadLink),"value"); + } + } + + /// + /// + void IList.Insert(int index, object value) { + if (value is Link) { + Add((Link)value); + } + else { + throw new ArgumentException(SR.GetString(SR.LinkLabelBadLink), "value"); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(Link link) { + return owner.links.Contains(link); + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + bool IList.Contains(object link) { + if (link is Link) { + return Contains((Link)link); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(Link link) { + return owner.links.IndexOf(link); + } + + /// + /// + int IList.IndexOf(object link) { + if (link is Link) { + return IndexOf((Link)link); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + + /// + /// + /// Remove all links from the linkLabel. + /// + public virtual void Clear() { + bool doLayout = this.owner.links.Count > 0 && this.owner.AutoSize; + owner.links.Clear(); + + if (doLayout) { + LayoutTransaction.DoLayout(this.owner.ParentInternal, this.owner, PropertyNames.Links); + this.owner.AdjustSize(); + this.owner.Invalidate(); + } + + owner.UpdateSelectability(); + owner.InvalidateTextLayout(); + owner.Invalidate(); + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + owner.links.CopyTo(dest, index); + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (owner.links != null) { + return owner.links.GetEnumerator(); + } + else { + return new Link[0].GetEnumerator(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(Link value) { + + if (value.Owner != this.owner) { + return; + } + + owner.links.Remove(value); + + if (this.owner.AutoSize) { + LayoutTransaction.DoLayout(this.owner.ParentInternal, this.owner, PropertyNames.Links); + this.owner.AdjustSize(); + this.owner.Invalidate(); + } + + owner.links.Sort(LinkLabel.linkComparer); + + owner.ValidateNoOverlappingLinks(); + owner.UpdateSelectability(); + owner.InvalidateTextLayout(); + owner.Invalidate(); + + if (owner.FocusLink == null && owner.links.Count > 0) { + owner.FocusLink = (Link)owner.links[0]; + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + Remove(this[index]); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + void IList.Remove(object value) { + if (value is Link) { + Remove((Link)value); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + TypeConverter(typeof(LinkConverter)) + ] + public class Link { + private int start = 0; + private object linkData = null; + private LinkState state = LinkState.Normal; + private bool enabled = true; + private Region visualRegion; + internal int length = 0; + private LinkLabel owner = null; + private string name = null; + private string description = null; + + private object userData; + + /// + public Link() { + } + + /// + public Link(int start, int length) { + this.start = start; + this.length = length; + } + + /// + public Link(int start, int length, object linkData) { + this.start = start; + this.length = length; + this.linkData = linkData; + } + + internal Link(LinkLabel owner) { + this.owner = owner; + } + + /// + /// Description for accessibility + /// + public string Description { + get { + return description; + } + set { + description = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [DefaultValue(true)] + public bool Enabled { + get { + return enabled; + } + set { + if (enabled != value) { + enabled = value; + + if ((int)(state & (LinkState.Hover | LinkState.Active)) != 0) { + state &= ~(LinkState.Hover | LinkState.Active); + if (owner != null) { + owner.OverrideCursor = null; + } + } + + if (owner != null) { + owner.InvalidateLink(this); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public int Length { + get { + if (length == -1) { + if (owner != null && !String.IsNullOrEmpty(owner.Text)) { + StringInfo stringInfo = new StringInfo(owner.Text); + return stringInfo.LengthInTextElements - Start; + } + else { + return 0; + } + } + return length; + } + set { + if (length != value) { + length = value; + if (owner != null) { + owner.InvalidateTextLayout(); + owner.Invalidate(); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [DefaultValue(null)] + public object LinkData { + get { + return linkData; + } + set { + linkData = value; + } + } + + /// + /// + /// The LinkLabel object that owns this link. + /// + internal LinkLabel Owner { + get { + return owner; + } + set { + owner = value; + } + } + + internal LinkState State { + get { + return state; + } + set { + state = value; + } + } + /// + /// + /// The name for the link - useful for indexing by key. + /// + [ + DefaultValue(""), + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeNodeNameDescr) + ] + public string Name { + get { + return name == null ? "" : name; + } + set { + this.name = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public int Start { + get { + return start; + } + set { + if (start != value) { + start = value; + + if (owner != null) { + owner.links.Sort(LinkLabel.linkComparer); + owner.InvalidateTextLayout(); + owner.Invalidate(); + } + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [DefaultValue(false)] + public bool Visited { + get { + return(State & LinkState.Visited) == LinkState.Visited; + } + set { + bool old = Visited; + + if (value) { + State |= LinkState.Visited; + } + else { + State &= ~LinkState.Visited; + } + + if (old != Visited && owner != null) { + owner.InvalidateLink(this); + } + } + } + + internal Region VisualRegion { + get { + return visualRegion; + } + set { + visualRegion = value; + } + } + } + + /// + /// + /// [To be supplied.] + /// + private class LinkComparer : IComparer { + int IComparer.Compare(object link1, object link2) { + + Debug.Assert(link1 != null && link2 != null, "Null objects sent for comparison"); + + int pos1 = ((Link)link1).Start; + int pos2 = ((Link)link2).Start; + + return pos1 - pos2; + } + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class LinkLabelAccessibleObject : LabelAccessibleObject { + /// + /// + /// + public LinkLabelAccessibleObject(LinkLabel owner) : base(owner) { + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + /// + /// + /// + public override AccessibleObject GetChild(int index) { + if (index >= 0 && index < ((LinkLabel)Owner).Links.Count) { + return new LinkAccessibleObject(((LinkLabel)Owner).Links[index]); + } + else { + return null; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_IsEnabledPropertyId) { + if (!Owner.Enabled) { + return false; + } + } + + return base.GetPropertyValue(propertyID); + } + + public override System.Windows.Forms.AccessibleObject HitTest(int x, int y) { + Point p = Owner.PointToClient(new Point(x, y)); + Link hit = ((LinkLabel)Owner).PointInLink(p.X, p.Y); + if (hit != null) { + return new LinkAccessibleObject(hit); + } + if (this.Bounds.Contains(x, y)) { + return this; + } + return null; + } + + /// + /// + /// + public override int GetChildCount() { + return((LinkLabel)Owner).Links.Count; + } + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class LinkAccessibleObject : AccessibleObject { + + private Link link; + + public LinkAccessibleObject(Link link) : base() { + this.link = link; + } + + public override Rectangle Bounds { + get { + Region region = link.VisualRegion; + Graphics g = null; + + IntSecurity.ObjectFromWin32Handle.Assert(); + try + { + g = Graphics.FromHwnd(link.Owner.Handle); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + // Make sure we have a region for this link + // + if (region == null) { + link.Owner.EnsureRun(g); + region = link.VisualRegion; + if (region == null) { + g.Dispose(); + return Rectangle.Empty; + } + } + + Rectangle rect; + try { + rect = Rectangle.Ceiling(region.GetBounds(g)); + } + finally { + g.Dispose(); + } + + + // Translate rect to screen coordinates + // + return link.Owner.RectangleToScreen(rect); + } + } + + public override string DefaultAction { + get { + return SR.GetString(SR.AccessibleActionClick); + } + } + + public override string Description { + get { + return link.Description; + } + } + + public override string Name { + get { + string text = link.Owner.Text; + string name; + if (AccessibilityImprovements.Level3) { + // return the full name of the link label for AI.Level3 + // as sometimes the link name in isolation is unusable + // to a customer using a screen reader + name = text; + if (link.Owner.UseMnemonic) { + name = WindowsFormsUtils.TextWithoutMnemonics(name); + } + } else { + int charStart = LinkLabel.ConvertToCharIndex(link.Start, text); + int charEnd = LinkLabel.ConvertToCharIndex(link.Start + link.Length, text); + name = text.Substring(charStart, charEnd - charStart); + if (AccessibilityImprovements.Level1 && link.Owner.UseMnemonic) { + // return the same value as the tooltip shows. + name = WindowsFormsUtils.TextWithoutMnemonics(name); + } + } + + return name; + } + set { + base.Name = value; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return link.Owner.AccessibilityObject; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.Link; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Focusable; + + // Selected state + // + if (link.Owner.FocusLink == link) { + state |= AccessibleStates.Focused; + } + + return state; + + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (AccessibilityImprovements.Level1) { + // Narrator announces Link's text twice, once as a Name property and once as a Value, thus removing value. + // Value is optional for this role (Link). + return string.Empty; + } + return Name; + } + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + link.Owner.OnLinkClicked(new LinkLabelLinkClickedEventArgs(link)); + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_IsEnabledPropertyId) { + if (!link.Owner.Enabled) { + return false; + } + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEvent.cs b/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEvent.cs new file mode 100644 index 000000000..8dfee8ee4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEvent.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class LinkLabelLinkClickedEventArgs : EventArgs { + private readonly LinkLabel.Link link; + private readonly MouseButtons button; + /// + /// + /// + /// Initializes a new instance of the class, given the link. + /// + /// + public LinkLabelLinkClickedEventArgs(LinkLabel.Link link) { + this.link = link; + this.button = MouseButtons.Left; + } + + public LinkLabelLinkClickedEventArgs(LinkLabel.Link link, MouseButtons button) : this(link) { + this.button = button; + } + + /// + /// + /// Gets the mouseButton which causes the link to be clicked + /// + /// + public MouseButtons Button { + get { + return button; + } + } + + /// + /// + /// + /// Gets the that was clicked. + /// + /// + public LinkLabel.Link Link { + get { + return link; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEventHandler.cs b/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEventHandler.cs new file mode 100644 index 000000000..02aaf86d4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkLabelLinkClickedEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the event of a . + /// + /// + public delegate void LinkLabelLinkClickedEventHandler(object sender, LinkLabelLinkClickedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/LinkState.cs b/WindowsForms/Managed/System/WinForms/LinkState.cs new file mode 100644 index 000000000..d81d7f56a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkState.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + + /// + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum LinkState { + /// + /// + /// [To be supplied.] + /// + Normal = 0x00, + /// + /// + /// [To be supplied.] + /// + Hover = 0x01, + /// + /// + /// [To be supplied.] + /// + Active = 0x02, + /// + /// + /// [To be supplied.] + /// + Visited = 0x04 + } +} diff --git a/WindowsForms/Managed/System/WinForms/LinkUtilities.cs b/WindowsForms/Managed/System/WinForms/LinkUtilities.cs new file mode 100644 index 000000000..ea6ab05eb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LinkUtilities.cs @@ -0,0 +1,230 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { +using System; +using System.Drawing; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Globalization; +using System.Security.Permissions; + + internal class LinkUtilities { + + // IE fonts and colors + static Color ielinkColor = Color.Empty; + static Color ieactiveLinkColor = Color.Empty; + static Color ievisitedLinkColor = Color.Empty; + + const string IESettingsRegPath = "Software\\Microsoft\\Internet Explorer\\Settings"; + public const string IEMainRegPath = "Software\\Microsoft\\Internet Explorer\\Main"; + const string IEAnchorColor = "Anchor Color"; + const string IEAnchorColorVisited = "Anchor Color Visited"; + const string IEAnchorColorHover = "Anchor Color Hover"; + + /// + /// + /// Retrieves a named IE color from the registry. There are constants at the top + /// of this file of the valid names to retrieve. + /// + private static Color GetIEColor(string name) { + // SECREVIEW : We are just reading the IE color settings from the registry... + // + // SECUNDONE : This assert doesn't work... assert everything for now... + // + //new RegistryPermission(RegistryPermissionAccess.Read, "HKCU\\" + IESettingsRegPath).Assert(); + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try { + RegistryKey key = Registry.CurrentUser.OpenSubKey(IESettingsRegPath); + + if (key != null) { + + // Since this comes from the registry, be very careful about its contents. + // + string s = (string)key.GetValue(name); + key.Close(); + + if (s != null) { + string[] rgbs = s.Split(new char[] {','}); + int[] rgb = new int[3]; + + int nMax = Math.Min(rgb.Length, rgbs.Length); + + //NOTE: if we can't parse rgbs[i], rgb[i] will be set to 0. + for (int i = 0; i < nMax; i++) + { + int.TryParse(rgbs[i], out rgb[i]); + } + + return Color.FromArgb(rgb[0], rgb[1], rgb[2]); + } + } + + if (string.Equals(name, IEAnchorColor, StringComparison.OrdinalIgnoreCase)) { + return Color.Blue; + } + else if (string.Equals(name, IEAnchorColorVisited, StringComparison.OrdinalIgnoreCase)) { + return Color.Purple; + } + else if (string.Equals(name, IEAnchorColorHover, StringComparison.OrdinalIgnoreCase)) { + return Color.Red; + } + else { + return Color.Red; + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + } + + public static Color IELinkColor { + get { + if (ielinkColor.IsEmpty) { + ielinkColor = GetIEColor(IEAnchorColor); + } + return ielinkColor; + } + } + + public static Color IEActiveLinkColor { + get { + if (ieactiveLinkColor.IsEmpty) { + ieactiveLinkColor = GetIEColor(IEAnchorColorHover); + } + return ieactiveLinkColor; + } + } + public static Color IEVisitedLinkColor { + get { + if (ievisitedLinkColor.IsEmpty) { + ievisitedLinkColor = GetIEColor(IEAnchorColorVisited); + } + return ievisitedLinkColor; + } + } + + /// Produces a color for visited links using SystemColors + public static Color GetVisitedLinkColor() + { + int r = (SystemColors.Window.R + SystemColors.WindowText.R + 1) / 2; + int g = SystemColors.WindowText.G; + int b = (SystemColors.Window.B + SystemColors.WindowText.B + 1) / 2; + + return Color.FromArgb(r, g, b); + } + + /// + /// + /// Retrieves the IE settings for link behavior from the registry. + /// + public static LinkBehavior GetIELinkBehavior() { + // SECREVIEW : We are just reading the IE color settings from the registry... + // + // SECUNDONE : This assert doesn't work... assert everything for now... + // + //new RegistryPermission(RegistryPermissionAccess.Read, "HKCU\\" + IEMainRegPath).Assert(); + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try { + RegistryKey key = null; + try { + key = Registry.CurrentUser.OpenSubKey(IEMainRegPath); + } + catch (System.Security.SecurityException) { + // User does not have right to access Registry path HKCU\\Software\\Microsoft\\Internet Explorer\\Main. + // Catch SecurityException silently and let the return value fallback to AlwaysUnderline. + } + + if (key != null) { + string s = (string)key.GetValue("Anchor Underline"); + key.Close(); + + if (s != null && string.Compare(s, "no", true, CultureInfo.InvariantCulture) == 0) { + return LinkBehavior.NeverUnderline; + } + if (s != null && string.Compare(s, "hover", true, CultureInfo.InvariantCulture) == 0) { + return LinkBehavior.HoverUnderline; + } + else { + return LinkBehavior.AlwaysUnderline; + } + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + return LinkBehavior.AlwaysUnderline; + } + + public static void EnsureLinkFonts(Font baseFont, LinkBehavior link, ref Font linkFont, ref Font hoverLinkFont) { + if (linkFont != null && hoverLinkFont != null) { + return; + } + + bool underlineLink = true; + bool underlineHover = true; + + if (link == LinkBehavior.SystemDefault) { + link = GetIELinkBehavior(); + } + + switch (link) { + case LinkBehavior.AlwaysUnderline: + underlineLink = true; + underlineHover = true; + break; + case LinkBehavior.HoverUnderline: + underlineLink = false; + underlineHover = true; + break; + case LinkBehavior.NeverUnderline: + underlineLink = false; + underlineHover = false; + break; + } + + Font f = baseFont; + + // We optimize for the "same" value (never & always) to avoid creating an + // extra font object. + // + if (underlineHover == underlineLink) { + FontStyle style = f.Style; + if (underlineHover) { + style |= FontStyle.Underline; + } + else { + style &= ~FontStyle.Underline; + } + hoverLinkFont = new Font(f, style); + linkFont = hoverLinkFont; + } + else { + FontStyle hoverStyle = f.Style; + if (underlineHover) { + hoverStyle |= FontStyle.Underline; + } + else { + hoverStyle &= ~FontStyle.Underline; + } + + hoverLinkFont = new Font(f, hoverStyle); + + FontStyle linkStyle = f.Style; + if (underlineLink) { + linkStyle |= FontStyle.Underline; + } + else { + linkStyle &= ~FontStyle.Underline; + } + + linkFont = new Font(f, linkStyle); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListBindingConverter.cs b/WindowsForms/Managed/System/WinForms/ListBindingConverter.cs new file mode 100644 index 000000000..258e11ddc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListBindingConverter.cs @@ -0,0 +1,191 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Collections; + using System.Globalization; + using System.Reflection; + + /// + /// + /// [To be supplied.] + /// + public class ListBindingConverter : TypeConverter { + + private static Type[] ctorTypes = null; // the list of type of our ctor parameters. + private static string[] ctorParamProps = null; // the name of each property to check to see if we need to init with a ctor. + + /// + /// Creates our array of types on demand. + /// + private static Type[] ConstructorParamaterTypes { + get { + if (ctorTypes == null) { + ctorTypes = new Type[]{typeof(string), typeof(object), typeof(string), typeof(bool), typeof(DataSourceUpdateMode), typeof(object), typeof(string), typeof(IFormatProvider)}; + } + return ctorTypes; + } + } + + /// + /// Creates our array of param names on demand. + /// + + private static string[] ConstructorParameterProperties { + get { + if (ctorParamProps == null) { + ctorParamProps = new string[]{null, null, null, "FormattingEnabled", "DataSourceUpdateMode", "NullValue", "FormatString", "FormatInfo", }; + } + return ctorParamProps; + } + } + + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is Binding) { + Binding b = (Binding)value; + return GetInstanceDescriptorFromValues(b); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + try + { + return new Binding((string)propertyValues["PropertyName"], + propertyValues["DataSource"], + (string)propertyValues["DataMember"]); + } + catch (InvalidCastException invalidCast) + { + throw new ArgumentException(SR.GetString(SR.PropertyValueInvalidEntry), invalidCast); + } + catch (NullReferenceException nullRef) + { + throw new ArgumentException(SR.GetString(SR.PropertyValueInvalidEntry), nullRef); + } + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// Gets the best matching ctor for a given binding and fills it out, based on the + /// state of the Binding and the optimal ctor. + /// + private InstanceDescriptor GetInstanceDescriptorFromValues(Binding b) { + + // The BindingFormattingDialog turns on Binding::FormattingEnabled property. + // however, when the user data binds a property using the PropertyBrowser, Binding::FormattingEnabled is set to false + // The Binding class is not a component class, so we don't have the ComponentInitialize method where we can set FormattingEnabled to true + // so we set it here. VsWhidbey 241361 + b.FormattingEnabled = true; + + bool isComplete = true; + int lastItem = ConstructorParameterProperties.Length - 1; + + for (; lastItem >= 0; lastItem--) { + + // null means no prop is available, we quit here. + // + if (ConstructorParameterProperties[lastItem] == null) { + break; + } + + // get the property and see if it needs to be serialized. + // + PropertyDescriptor prop = TypeDescriptor.GetProperties(b)[ConstructorParameterProperties[lastItem]]; + if (prop != null && prop.ShouldSerializeValue(b)) { + break; + } + } + + // now copy the type array up to the point we quit. + // + Type[] ctorParams = new Type[lastItem + 1]; + Array.Copy(ConstructorParamaterTypes, 0, ctorParams, 0, ctorParams.Length); + + // Get the ctor info. + // + ConstructorInfo ctor = typeof(Binding).GetConstructor(ctorParams); + Debug.Assert(ctor != null, "Failed to find Binding ctor for types!"); + if (ctor == null) { + isComplete = false; + ctor = typeof(Binding).GetConstructor(new Type[] { + typeof(string), + typeof(object), + typeof(string)}); + } + + // now fill in the values. + // + object[] values = new object[ctorParams.Length]; + + for (int i = 0; i < values.Length; i++) { + object val = null; + switch (i) { + case 0: + val = b.PropertyName; + break; + case 1: + val = b.BindToObject.DataSource; + break; + case 2: + val = b.BindToObject.BindingMemberInfo.BindingMember; + break; + default: + val = TypeDescriptor.GetProperties(b)[ConstructorParameterProperties[i]].GetValue(b); + break; + } + values[i] = val; + } + return new InstanceDescriptor(ctor, values, isComplete); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListBindingHelper.cs b/WindowsForms/Managed/System/WinForms/ListBindingHelper.cs new file mode 100644 index 000000000..609e4cacd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListBindingHelper.cs @@ -0,0 +1,681 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics; + using System.ComponentModel; + using System.Collections; + using System.Collections.Generic; + using System.Reflection; + + /// + /// + /// + public static class ListBindingHelper { + + private static Attribute[] browsableAttribute; + + private static Attribute[] BrowsableAttributeList { + get { + if (browsableAttribute == null) { + browsableAttribute = new Attribute[] {new BrowsableAttribute(true)}; + } + + return browsableAttribute; + } + } + + /// + /// + /// [To be supplied.] + /// + public static object GetList(object list) { + if (list is IListSource) { + return (list as IListSource).GetList(); + } + else { + return list; + } + } + + /// + /// + /// [To be supplied.] + /// + public static object GetList(object dataSource, string dataMember) { + // + // The purpose of this method is to find a list, given a 'data source' object and a + // decription of some 'data member' property of that object which returns the list. + // + // - If the data source is not a list, we get the list by just querying for the + // current value of that property on the data source itself. + // + // - If the data source is a list, we have to first pick some item from that list, + // then query for the value of that property on the individual list item. + // + + dataSource = GetList(dataSource); + if (dataSource == null || dataSource is Type || String.IsNullOrEmpty(dataMember)) { + return dataSource; + } + + PropertyDescriptorCollection dsProps = ListBindingHelper.GetListItemProperties(dataSource); + PropertyDescriptor dmProp = dsProps.Find(dataMember, true); + if (dmProp == null) { + throw new System.ArgumentException(SR.GetString(SR.DataSourceDataMemberPropNotFound, dataMember)); + } + + object currentItem; + + if (dataSource is ICurrencyManagerProvider) { + // Data source is another BindingSource so ask for its current item + CurrencyManager cm = (dataSource as ICurrencyManagerProvider).CurrencyManager; + bool currentKnown = (cm != null && cm.Position >= 0 && cm.Position <= cm.Count - 1); + currentItem = currentKnown ? cm.Current : null; + } + else if (dataSource is IEnumerable) { + // Data source is an enumerable list, so walk to the first item + currentItem = GetFirstItemByEnumerable(dataSource as IEnumerable); + } + else { + // Data source is not a list, so just use the data source itself + currentItem = dataSource; + } + + // Query the data member property on the chosen object to get back the list + return (currentItem == null) ? null : dmProp.GetValue(currentItem); + } + + /// + /// + /// [To be supplied.] + /// + public static string GetListName(object list, PropertyDescriptor[] listAccessors) { + string name; + + if (list == null) { + return string.Empty; + } + ITypedList typedList = list as ITypedList; + if (typedList != null) { + // Use typed list + name = typedList.GetListName(listAccessors); + } + else { + Type type; + // We always resolve via type in this case (not an instance) + if ((null == listAccessors) || (listAccessors.Length == 0)) { + Type listAsType = list as Type; + if (listAsType != null) { + type = listAsType; + } + else { + type = list.GetType(); + } + } + else { + // We don't walk down - always use type name + PropertyDescriptor pd = listAccessors[0]; + type = pd.PropertyType; + } + + name = GetListNameFromType(type); + } + + return name; + } + + /// + /// + /// [To be supplied.] + /// + public static PropertyDescriptorCollection GetListItemProperties(object list) { + PropertyDescriptorCollection pdc; + + if (list == null) { + return new PropertyDescriptorCollection(null); + } else if (list is Type) { + pdc = GetListItemPropertiesByType(list as Type); + } + else { + object target = GetList(list); + + if (target is ITypedList) { + pdc = (target as ITypedList).GetItemProperties(null); + } + else if (target is IEnumerable) { + pdc = GetListItemPropertiesByEnumerable(target as IEnumerable); + } + else { + pdc = TypeDescriptor.GetProperties(target); + } + } + + return pdc; + } + + /// + /// + /// [To be supplied.] + /// + public static PropertyDescriptorCollection GetListItemProperties(object list, PropertyDescriptor[] listAccessors) { + PropertyDescriptorCollection pdc; + + if ((null == listAccessors) || (listAccessors.Length == 0)) { + pdc = GetListItemProperties(list); + } + else { + if (list is Type) { + pdc = GetListItemPropertiesByType(list as Type, listAccessors); + } + else { + object target = GetList(list); + + if (target is ITypedList) { + pdc = (target as ITypedList).GetItemProperties(listAccessors); + } + else if (target is IEnumerable) { + pdc = GetListItemPropertiesByEnumerable(target as IEnumerable, listAccessors); + } + else { + pdc = GetListItemPropertiesByInstance(target, listAccessors, 0); + } + } + } + + return pdc; + } + + /// + /// + /// [To be supplied.] + /// + public static PropertyDescriptorCollection GetListItemProperties(object dataSource, string dataMember, PropertyDescriptor[] listAccessors) { + dataSource = GetList(dataSource); + + if (!String.IsNullOrEmpty(dataMember)) { + // Find the property on the data source specified by the data member + PropertyDescriptorCollection dsProps = ListBindingHelper.GetListItemProperties(dataSource); + PropertyDescriptor dmProp = dsProps.Find(dataMember, true); + + // Error: Property not found - data member is invalid + if (dmProp == null) { + throw new System.ArgumentException(SR.GetString(SR.DataSourceDataMemberPropNotFound, dataMember)); + } + + // Add the data member property to the list accessors + int len = (listAccessors == null) ? 1 : (listAccessors.Length + 1); + PropertyDescriptor[] listAccessors2 = new PropertyDescriptor[len]; + listAccessors2[0] = dmProp; + for (int i = 1; i < len; ++i) { + listAccessors2[i] = listAccessors[i - 1]; + } + + // Replace old accessors with new accessors + listAccessors = listAccessors2; + } + + return GetListItemProperties(dataSource, listAccessors); + } + + /// + /// + /// [To be supplied.] + /// + public static Type GetListItemType(object list) { + if (list == null) { + return null; + } + + Type itemType = null; + + // special case for IListSource + if ((list is Type) && (typeof(IListSource).IsAssignableFrom(list as Type))) { + list = CreateInstanceOfType(list as Type); + } + + list = GetList(list); + Type listType = (list is Type) ? (list as Type) : list.GetType(); + object listInstance = (list is Type) ? null : list; + + if (typeof(Array).IsAssignableFrom(listType)) { + itemType = listType.GetElementType(); + } + else { + PropertyInfo indexer = GetTypedIndexer(listType); + + if (indexer != null) { + itemType = indexer.PropertyType; + } + else if (listInstance is IEnumerable) { + itemType = GetListItemTypeByEnumerable(listInstance as IEnumerable); + } + else { + itemType = listType; + } + } + + return itemType; + } + + // Create an object of the given type. Throw an exception if this fails. + private static object CreateInstanceOfType(Type type) { + object instancedObject = null; + Exception instanceException = null; + + try { + instancedObject = SecurityUtils.SecureCreateInstance(type); + } + catch (TargetInvocationException ex) { + instanceException = ex; // Default ctor threw an exception + } + catch (MethodAccessException ex) { + instanceException = ex; // Default ctor was not public + } + catch (MissingMethodException ex) { + instanceException = ex; // No default ctor defined + } + + if (instanceException != null) { + throw new NotSupportedException(SR.GetString(SR.BindingSourceInstanceError), instanceException); + } + + return instancedObject; + } + + /// + /// + /// [To be supplied.] + /// + public static Type GetListItemType(object dataSource, string dataMember) { + // No data source + if (dataSource == null) { + return typeof(Object); + } + + // No data member - Determine item type directly from data source + if (String.IsNullOrEmpty(dataMember)) { + return GetListItemType(dataSource); + } + + // Get list item properties for this data source + PropertyDescriptorCollection dsProps = GetListItemProperties(dataSource); + if (dsProps == null) { + return typeof(Object); + } + + // Find the property specified by the data member + PropertyDescriptor dmProp = dsProps.Find(dataMember, true); + if (dmProp == null || dmProp.PropertyType is ICustomTypeDescriptor) { + return typeof(Object); + } + + // Determine item type from data member property + return GetListItemType(dmProp.PropertyType); + } + + private static string GetListNameFromType(Type type) { + string name; + + if (typeof(Array).IsAssignableFrom(type)) { + // If the type is Customers[], this will return "Customers" + name = type.GetElementType().Name; + } + else if (typeof(IList).IsAssignableFrom(type)) { + // If the type is BindingList, TCollection, TList (or equiv), this will return "T" + PropertyInfo indexer = GetTypedIndexer(type); + if (indexer != null) + { + name = indexer.PropertyType.Name; + } + else + { + name = type.Name; + } + } + else { + // Fallback to type name + name = type.Name; + } + + return name; + } + + private static PropertyDescriptorCollection GetListItemPropertiesByType(Type type, PropertyDescriptor[] listAccessors) { + PropertyDescriptorCollection pdc = null; + + if ((null == listAccessors) || (listAccessors.Length == 0)) { + pdc = GetListItemPropertiesByType(type); + } + else { + pdc = GetListItemPropertiesByType(type, listAccessors, 0); + } + + return pdc; + } + + private static PropertyDescriptorCollection GetListItemPropertiesByType(Type type, PropertyDescriptor[] listAccessors, int startIndex) { + PropertyDescriptorCollection pdc = null; + Type subType = listAccessors[startIndex].PropertyType; + // subType is the property type - which is not to be confused with the item type. + // For example, if a class Customer has a property of type Orders[], then Given: + // GetListItemProperties(typeof(Customer), PDForOrders) + // PDForOrders.PropertyType will be Array (not Orders) + // + // If there are no more ListAccessors, then we want: + // GetListItemProperties(PDForOrders.PropertyType) // this returns the shape of Orders not Array + // If there are more listAccessors, then we'll call + // GetListItemProperties(PDForOrders.PropertyType, listAccessors, startIndex++) + startIndex = startIndex + 1; + + if (startIndex >= listAccessors.Length) { + // Last item, return shape of item + pdc = GetListItemProperties(subType); + } + else { + // Walk down the tree + pdc = GetListItemPropertiesByType(subType, listAccessors, startIndex); + } + // Return descriptors + return pdc; + } + + private static PropertyDescriptorCollection GetListItemPropertiesByEnumerable(IEnumerable iEnumerable, PropertyDescriptor[] listAccessors, int startIndex) { + PropertyDescriptorCollection pdc = null; + object subList = null; + // Walk down the tree - first try and get the value + // This is tricky, because we can't do a standard GetValue - we need an instance of one of the + // items in the list. + // + // For example: + // + // Customer has a property Orders which is of type Order[] + // + // Customers is a Customer[] (this is the IList) + // Customers does not have the property "Orders" - Customer has that property + // So we need to get the value of Customers[0] + // + object instance = GetFirstItemByEnumerable(iEnumerable); + + if (instance != null) { + // This calls GetValue(Customers[0], "Orders") - or Customers[0].Orders + // If this list is non-null, it is an instance of Orders (Order[]) for the first customer + subList = GetList(listAccessors[startIndex].GetValue(instance)); + } + + if (null == subList) { + // Can't get shape by Instance, try by Type + pdc = GetListItemPropertiesByType(listAccessors[startIndex].PropertyType, listAccessors, startIndex); + } + else { + // We have the Instance (e.g. Orders) + ++startIndex; + + IEnumerable ienumerableSubList = subList as IEnumerable; + if (ienumerableSubList != null) { + if (startIndex == listAccessors.Length) { + // Last one, so get the shape + pdc = GetListItemPropertiesByEnumerable(ienumerableSubList); + } + else { + // Looks like they want more (e.g. Customers.Orders.OrderDetails) + pdc = GetListItemPropertiesByEnumerable(ienumerableSubList, listAccessors, startIndex); + } + } + else { + // Not a list, so switch to a non-list based method of retrieving properties + pdc = GetListItemPropertiesByInstance(subList, listAccessors, startIndex); + } + } + + return pdc; + } + + private static PropertyDescriptorCollection GetListItemPropertiesByEnumerable(IEnumerable enumerable, PropertyDescriptor[] listAccessors) { + PropertyDescriptorCollection pdc = null; + + if ((null == listAccessors) || (listAccessors.Length == 0)) { + pdc = GetListItemPropertiesByEnumerable(enumerable); + } + else { + ITypedList typedList = enumerable as ITypedList; + if (typedList != null) { + pdc = typedList.GetItemProperties(listAccessors); + } + else { + // Walk the tree + pdc = GetListItemPropertiesByEnumerable(enumerable, listAccessors, 0); + } + } + return pdc; + } + + private static Type GetListItemTypeByEnumerable(IEnumerable iEnumerable) { + object instance = GetFirstItemByEnumerable(iEnumerable); + return (instance != null) ? instance.GetType() : typeof(object); + } + + private static PropertyDescriptorCollection GetListItemPropertiesByInstance(object target, PropertyDescriptor[] listAccessors, int startIndex) { + // At this point, things can be simplified because: + // We know target is _not_ a list + // We have an instance + + PropertyDescriptorCollection pdc; + + // Get the value of the first listAccessor + if (listAccessors != null && listAccessors.Length > startIndex) { + // Get the value (e.g. given Foo with property Bar, this gets Foo.Bar) + object value = listAccessors[startIndex].GetValue(target); + + if (value == null) { + // It's null - we can't walk down by Instance so use Type + pdc = GetListItemPropertiesByType(listAccessors[startIndex].PropertyType, listAccessors, startIndex); + } + else { + PropertyDescriptor[] accessors = null; + + if (listAccessors.Length > startIndex + 1) { + int accessorsCount = listAccessors.Length - (startIndex + 1); + accessors = new PropertyDescriptor[accessorsCount]; + for (int i = 0; i < accessorsCount; ++i) { + accessors[i] = listAccessors[startIndex + 1 + i]; + } + } + + // We've got the instance of Bar - now get it's shape + pdc = GetListItemProperties(value, accessors); + } + } + else { + // Fallback to TypeDescriptor + pdc = TypeDescriptor.GetProperties(target, BrowsableAttributeList); + } + + return pdc; + } + + // returns true if 'type' can be treated as a list + private static bool IsListBasedType(Type type) + { + // check for IList, ITypedList, IListSource + if (typeof(IList).IsAssignableFrom(type) || + typeof(ITypedList).IsAssignableFrom(type) || + typeof(IListSource).IsAssignableFrom(type)) { + return true; + } + + // check for IList<>: + if (type.IsGenericType && !type.IsGenericTypeDefinition) { + if (typeof(IList<>).IsAssignableFrom(type.GetGenericTypeDefinition())) { + return true; + } + } + + // check for SomeObject : IList / SomeObject : IList<(SpecificListObjectType)> + foreach (Type curInterface in type.GetInterfaces()) { + if (curInterface.IsGenericType) { + if (typeof(IList<>).IsAssignableFrom(curInterface.GetGenericTypeDefinition())) { + return true; + } + } + } + + return false; + } + + /// + /// + /// Returns info about the 'indexer' property on the specified type. The presence of an indexer is used to + /// determine that the type represents a collection or list. The return type of that indexer is used to + /// determine the underlying item type. + /// + /// PROCESS: We look for the first public instance property on the type that is an 'indexer'. This property + /// is usually - but not always - called "Item". So we look at 'indexer parameters' to identify true indexers, + /// rather than looking at the property name. And we also ignore any indexers that return an item type of just + /// Object, since we are trying to use indexers here to determine the actual underlying item type! + /// + /// NOTE: A special rule is also enforced here - we only want to consider using the typed indexer on list + /// based types, ie. types we already know are supposed to be treated as lists (rather than list items). + /// + /// + private static PropertyInfo GetTypedIndexer(Type type) + { + PropertyInfo indexer = null; + + if (!IsListBasedType(type)) { + return null; + } + + System.Reflection.PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + + for (int idx = 0; idx < props.Length; idx++) { + if (props[idx].GetIndexParameters().Length > 0 && props[idx].PropertyType != typeof(object)) { + indexer = props[idx]; + //Prefer the standard indexer, if there is one + if (indexer.Name == "Item") { + break; + } + } + } + + return indexer; + } + + private static PropertyDescriptorCollection GetListItemPropertiesByType(Type type) { + return TypeDescriptor.GetProperties(GetListItemType(type), BrowsableAttributeList); + } + + private static PropertyDescriptorCollection GetListItemPropertiesByEnumerable(IEnumerable enumerable) { + PropertyDescriptorCollection pdc = null; + Type targetType = enumerable.GetType(); + + if (typeof(Array).IsAssignableFrom(targetType)) { + pdc = TypeDescriptor.GetProperties(targetType.GetElementType(), BrowsableAttributeList); + } + else { + ITypedList typedListEnumerable = enumerable as ITypedList; + if (typedListEnumerable != null) { + pdc = typedListEnumerable.GetItemProperties(null); + } + else { + PropertyInfo indexer = GetTypedIndexer(targetType); + + if (indexer != null && !typeof(ICustomTypeDescriptor).IsAssignableFrom(indexer.PropertyType)) { + Type type = indexer.PropertyType; + pdc = TypeDescriptor.GetProperties(type, BrowsableAttributeList); + + // Reflection, and consequently TypeDescriptor would not return properties defined on the "base" interface, + // for example + // public interface IPerson {String FirstName { get; set; }} + // public interface ITeacher : IPerson {int ClassRoom { get; set; }} + // typeof (ITeacher).GetProperties() would return only the "ClassRoom" property + + // DevDiv2 518025 + // if (type.IsInterface) { + // Type[] interfaces = type.GetInterfaces(); + // // initialize the list to an arbitrary length greater than pdc.Count + // List merged = new List(pdc.Count * 2 + 1); + // foreach (Type baseInterface in interfaces) { + // PropertyDescriptorCollection props = TypeDescriptor.GetProperties(baseInterface, BrowsableAttributeList); + // if (props != null) { + // foreach (PropertyDescriptor p in props) { + // merged.Add(p); + // } + // } + // } + // if (merged.Count != 0) { + // PropertyDescriptor[] props = new PropertyDescriptor[pdc.Count]; + // pdc.CopyTo(props, 0); + // merged.AddRange(props); + // pdc = new PropertyDescriptorCollection(merged.ToArray()); + // } + // } + + } + } + } + + // See if we were successful - if not, return the shape of the first + // item in the list + if (null == pdc) { + object instance = GetFirstItemByEnumerable(enumerable); + if (enumerable is string) { + pdc = TypeDescriptor.GetProperties(enumerable, BrowsableAttributeList); + } + else if (instance == null) { + pdc = new PropertyDescriptorCollection(null); + } + else { + pdc = TypeDescriptor.GetProperties(instance, BrowsableAttributeList); + + if (!(enumerable is IList) && (pdc == null || pdc.Count == 0)) { + pdc = TypeDescriptor.GetProperties(enumerable, BrowsableAttributeList); + } + } + + } + + // Return results + return pdc; + } + + private static object GetFirstItemByEnumerable(IEnumerable enumerable) { + object instance = null; + + if (enumerable is IList) { + // If the list supports IList (which is a superset of IEnumerable), then try to use its IList indexer + // to get the first item, since some ILists don't support use of their plain IEnumerable interface. + IList list = enumerable as IList; + instance = (list.Count > 0) ? list[0] : null; + } + else { + // Otherwise use the enumerator to get the first item... + try { + IEnumerator listEnumerator = enumerable.GetEnumerator(); + + listEnumerator.Reset(); + + if (listEnumerator.MoveNext()) + instance = listEnumerator.Current; + + // after we are done w/ the enumerator, reset it + listEnumerator.Reset(); + } + catch (NotSupportedException) { + // Some data sources do not offer a full implementation of IEnumerable. For example, SqlDataReader + // only supports reading forwards through items, so it does not support calls to IEnumerable.Reset(). + instance = null; + } + } + + return instance; + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ListBox.cs b/WindowsForms/Managed/System/WinForms/ListBox.cs new file mode 100644 index 000000000..8c79617b5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListBox.cs @@ -0,0 +1,4472 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + using System.Windows.Forms.Layout; + + using System.Drawing.Design; + using System.ComponentModel; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.VisualStyles; + + using System.Collections; + using System.Drawing; + using Microsoft.Win32; + using System.Text; + + /// + /// + /// + /// This is a control that presents a list of items to the user. They may be + /// navigated using the keyboard, or the scrollbar on the right side of the + /// control. One or more items may be selected as well. + /// + /// + /// The preferred way to add items is to set them all via an array at once, + /// which is definitely the most efficient way. The following is an example + /// of this: + /// + /// + /// ListBox lb = new ListBox(); + /// // set up properties on the listbox here. + /// lb.Items.All = new String [] { + /// "A", + /// "B", + /// "C", + /// "D"}; + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.ListBoxDesigner, " + AssemblyRef.SystemDesign), + DefaultEvent("SelectedIndexChanged"), + DefaultProperty("Items"), + DefaultBindingProperty("SelectedValue"), + SRDescription(SR.DescriptionListBox) + ] + public class ListBox : ListControl { + /// + /// + /// while doing a search, if no matches are found, this is returned + /// + public const int NoMatches = NativeMethods.LB_ERR; + /// + /// + /// The default item height for an owner-draw ListBox. + /// + public const int DefaultItemHeight = 13; // 13 == listbox's non-ownerdraw item height. That's with Win2k and + // the default font; on other platforms and with other fonts, it may be different. + + private const int maxWin9xHeight = 32767; //Win9x doesn't deal with height > 32K + + private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); + private static readonly object EVENT_DRAWITEM = new object(); + private static readonly object EVENT_MEASUREITEM = new object(); + + static bool checkedOS = false; + static bool runningOnWin2K = true; + + SelectedObjectCollection selectedItems; + SelectedIndexCollection selectedIndices; + ObjectCollection itemsCollection; + + // + + int itemHeight = DefaultItemHeight; + int columnWidth; + int requestedHeight; + int topIndex; + int horizontalExtent = 0; + int maxWidth = -1; + int updateCount = 0; + + bool sorted = false; + bool scrollAlwaysVisible = false; + bool integralHeight = true; + bool integralHeightAdjust = false; + bool multiColumn = false; + bool horizontalScrollbar = false; + bool useTabStops = true; + bool useCustomTabOffsets = false; + bool fontIsChanged = false; + bool doubleClickFired = false; + bool selectedValueChangedFired = false; + + DrawMode drawMode = System.Windows.Forms.DrawMode.Normal; + BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + SelectionMode selectionMode = System.Windows.Forms.SelectionMode.One; + + // VsWhidbey : 447524 + SelectionMode cachedSelectionMode = System.Windows.Forms.SelectionMode.One; + //We need to know that we are in middle of handleRecreate through Setter of SelectionMode. + //In this case we set a bool denoting that we are changing SelectionMode and + //in this case we should always use the cachedValue instead of the currently set value. + //We need to change this in the count as well as SelectedIndex code where we access the SelectionMode. + private bool selectionModeChanging = false; + + /// + /// This value stores the array of custom tabstops in the listbox. the array should be populated by + /// integers in a ascending order. + /// + private IntegerCollection customTabOffsets; + + /// + /// Default start position of items in the checked list box + /// + private const int defaultListItemStartPos = 1; + + /// + /// Borders are 1 pixel height. + /// + private const int defaultListItemBorderHeight = 1; + + /// + /// Borders are 1 pixel width and a pixel buffer + /// + private const int defaultListItemPaddingBuffer = 3; + + + internal int scaledListItemStartPosition = defaultListItemStartPos; + internal int scaledListItemBordersHeight = 2 * defaultListItemBorderHeight; + internal int scaledListItemPaddingBuffer = defaultListItemPaddingBuffer; + + + /// + /// + /// Creates a basic win32 list box with default values for everything. + /// + public ListBox() : base() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.StandardClick | + ControlStyles.UseTextForAccessibility, false); + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetBounds(0, 0, 120, 96); + + requestedHeight = Height; + + PrepareForDrawing(); + } + + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + PrepareForDrawing(); + } + + private void PrepareForDrawing() { + // Scale paddings + if (DpiHelper.EnableCheckedListBoxHighDpiImprovements) { + scaledListItemStartPosition = LogicalToDeviceUnits(defaultListItemStartPos); + + // height inlude 2 borders ( top and bottom). we are using multiplication by 2 instead of scaling doubled value to get an even number + // that might helps us in positioning control in the center for list items. + scaledListItemBordersHeight = 2 * LogicalToDeviceUnits(defaultListItemBorderHeight); + scaledListItemPaddingBuffer = LogicalToDeviceUnits(defaultListItemPaddingBuffer); + } + } + + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Retrieves the current border style. Values for this are taken from + /// The System.Windows.Forms.BorderStyle enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.ListBoxBorderDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (value != borderStyle) { + borderStyle = value; + RecreateHandle(); + // Avoid the listbox and textbox behavior in Collection editors + // + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + } + } + } + + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(0), + SRDescription(SR.ListBoxColumnWidthDescr) + ] + public int ColumnWidth { + get { + return columnWidth; + } + + set { + if (value < 0) { + throw new ArgumentException(SR.GetString(SR.InvalidLowBoundArgumentEx, "value", + (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (columnWidth != value) { + columnWidth = value; + // if it's zero, we need to reset, and only way to do + // that is to recreate the handle. + if (columnWidth == 0) { + RecreateHandle(); + } + else if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); + } + } + } + } + + /// + /// + /// Retrieves the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "LISTBOX"; + + cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.LBS_NOTIFY | NativeMethods.LBS_HASSTRINGS; + if (scrollAlwaysVisible) cp.Style |= NativeMethods.LBS_DISABLENOSCROLL; + if (!integralHeight) cp.Style |= NativeMethods.LBS_NOINTEGRALHEIGHT; + if (useTabStops) cp.Style |= NativeMethods.LBS_USETABSTOPS; + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + if (multiColumn) { + cp.Style |= NativeMethods.LBS_MULTICOLUMN | NativeMethods.WS_HSCROLL; + } + else if (horizontalScrollbar) { + cp.Style |= NativeMethods.WS_HSCROLL; + } + + switch (selectionMode) { + case SelectionMode.None: + cp.Style |= NativeMethods.LBS_NOSEL; + break; + case SelectionMode.MultiSimple: + cp.Style |= NativeMethods.LBS_MULTIPLESEL; + break; + case SelectionMode.MultiExtended: + cp.Style |= NativeMethods.LBS_EXTENDEDSEL; + break; + case SelectionMode.One: + break; + } + + switch (drawMode) { + case DrawMode.Normal: + break; + case DrawMode.OwnerDrawFixed: + cp.Style |= NativeMethods.LBS_OWNERDRAWFIXED; + break; + case DrawMode.OwnerDrawVariable: + cp.Style |= NativeMethods.LBS_OWNERDRAWVARIABLE; + break; + } + + return cp; + } + } + + + /// + /// + /// Enables a list box to recognize and expand tab characters when drawing + /// its strings using the CustomTabOffsets integer array. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Browsable(false) + ] + public bool UseCustomTabOffsets { + get { + return useCustomTabOffsets; + } + set { + if (useCustomTabOffsets != value) { + useCustomTabOffsets = value; + RecreateHandle(); + } + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(120, 96); + } + } + + /// + /// + /// Retrieves the style of the listbox. This will indicate if the system + /// draws it, or if the user paints each item manually. It also indicates + /// whether or not items have to be of the same height. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DrawMode.Normal), + SRDescription(SR.ListBoxDrawModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual DrawMode DrawMode { + get { + return drawMode; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); + } + if (drawMode != value) { + if (MultiColumn && value == DrawMode.OwnerDrawVariable) { + throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); + } + drawMode = value; + RecreateHandle(); + if (drawMode == DrawMode.OwnerDrawVariable) { + // VSWhidbey 139179 - force a layout after RecreateHandle() completes because now + // the LB is definitely fully populated and can report a preferred size accurately. + LayoutTransaction.DoLayoutIf(AutoSize, this.ParentInternal, this, PropertyNames.DrawMode); + } + } + } + } + + // Used internally to find the currently focused item + // + internal int FocusedIndex { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCARETINDEX, 0, 0)); + } + + return -1; + } + } + + /// + // VSWhidbey 95179: The scroll bars don't display properly when the IntegralHeight == false + // and the control is resized before the font size is change and the new font size causes + // the height of all the items to exceed the new height of the control. This is a bug in + // the control, but can be easily worked around by removing and re-adding all the items. + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + + if (false == integralHeight) { + // VSWhidbey 95179: Refresh the list to force the scroll bars to display + // when the integral height is false. + RefreshItems(); + } + } + } + + /// + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// Indicates the width, in pixels, by which a list box can be scrolled horizontally (the scrollable width). + /// This property will only have an effect if HorizontalScrollbars is true. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.ListBoxHorizontalExtentDescr) + ] + public int HorizontalExtent { + get { + return horizontalExtent; + } + + set { + if (value != horizontalExtent) { + horizontalExtent = value; + UpdateHorizontalExtent(); + } + } + } + + /// + /// + /// Indicates whether or not the ListBox should display a horizontal scrollbar + /// when the items extend beyond the right edge of the ListBox. + /// If true, the scrollbar will automatically set its extent depending on the length + /// of items in the ListBox. The exception is if the ListBox is owner-draw, in + /// which case HorizontalExtent will need to be explicitly set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.ListBoxHorizontalScrollbarDescr) + ] + public bool HorizontalScrollbar { + get { + return horizontalScrollbar; + } + + set { + if (value != horizontalScrollbar) { + horizontalScrollbar = value; + + // There seems to be a bug in the native ListBox in that the addition + // of the horizontal scroll bar does not get reflected in the control + // rightaway. So, we refresh the items here. + RefreshItems(); + + // Only need to recreate the handle if not MultiColumn + // (HorizontalScrollbar has no effect on a MultiColumn listbox) + // + if (!MultiColumn) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Indicates if the listbox should avoid showing partial Items. If so, + /// then only full items will be displayed, and the listbox will be resized + /// to prevent partial items from being shown. Otherwise, they will be + /// shown + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ListBoxIntegralHeightDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public bool IntegralHeight { + get { + return integralHeight; + } + + set { + if (integralHeight != value) { + integralHeight = value; + RecreateHandle(); + // Avoid the listbox and textbox behaviour in Collection editors + // + + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + } + } + } + + /// + /// + /// + /// Returns + /// the height of an item in an owner-draw list box. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DefaultItemHeight), + Localizable(true), + SRDescription(SR.ListBoxItemHeightDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual int ItemHeight { + get { + if (drawMode == DrawMode.OwnerDrawFixed || + drawMode == DrawMode.OwnerDrawVariable) { + return itemHeight; + } + + return GetItemHeight(0); + } + + set { + if (value < 1 || value > 255) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidExBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "256")); + } + if (itemHeight != value) { + itemHeight = value; + if (drawMode == DrawMode.OwnerDrawFixed && IsHandleCreated) { + BeginUpdate(); + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, value); + + // Changing the item height might require a resize for IntegralHeight list boxes + // + if (IntegralHeight) { + Size oldSize = Size; + Size = new Size(oldSize.Width + 1, oldSize.Height); + Size = oldSize; + } + + EndUpdate(); + } + } + } + } + + /// + /// + /// Collection of items in this listbox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ListBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + MergableProperty(false) + ] + public ObjectCollection Items { + get { + if (itemsCollection == null) { + itemsCollection = CreateItemCollection(); + } + return itemsCollection; + } + } + + // Computes the maximum width of all items in the ListBox + // + internal virtual int MaxItemWidth { + get { + + if (horizontalExtent > 0) { + return horizontalExtent; + } + + if (DrawMode != DrawMode.Normal) { + return -1; + } + + // Return cached maxWidth if available + // + if (maxWidth > -1) { + return maxWidth; + } + + // Compute maximum width + // + maxWidth = ComputeMaxItemWidth(maxWidth); + + return maxWidth; + } + } + + /// + /// + /// + /// Indicates if the listbox is multi-column + /// or not. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListBoxMultiColumnDescr) + ] + public bool MultiColumn { + get { + return multiColumn; + } + set { + if (multiColumn != value) { + if (value && drawMode == DrawMode.OwnerDrawVariable) { + throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); + } + multiColumn = value; + RecreateHandle(); + } + } + } + + /// + /// + /// The total height of the items in the list box. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxPreferredHeightDescr) + ] + public int PreferredHeight { + get { + int height = 0; + + if (drawMode == DrawMode.OwnerDrawVariable) { + // VSWhidbey 139179 - don't try to get item heights from the LB when items haven't been + // added to the LB yet. Just return current height. + if (RecreatingHandle || GetState(STATE_CREATINGHANDLE)) { + height = this.Height; + } + else { + if (itemsCollection != null) { + int cnt = itemsCollection.Count; + for (int i = 0; i < cnt; i++) { + height += GetItemHeight(i); + } + } + } + } + else { + //VSWhidbey #148270 + //When the list is empty, we don't want to multiply by 0 here. + int cnt = (itemsCollection == null || itemsCollection.Count == 0) ? 1 : itemsCollection.Count; + height = GetItemHeight(0) * cnt; + } + + if (borderStyle != BorderStyle.None) { + height += SystemInformation.BorderSize.Height * 4 + 3; + } + + return height; + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + int height = PreferredHeight; + int width; + + // Convert with a dummy height to add space required for borders + // VSWhidbey #151141 -PreferredSize should return either the new + // size of the control, or the default size if the handle has not been + // created + if (IsHandleCreated) + { + width = SizeFromClientSize(new Size(MaxItemWidth, height)).Width; + width += SystemInformation.VerticalScrollBarWidth + 4; + } + else + { + return DefaultSize; + } + return new Size(width, height) + Padding.Size; + } + + /// + public override RightToLeft RightToLeft { + get { + if (!RunningOnWin2K) { + return RightToLeft.No; + } + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + static bool RunningOnWin2K { + get { + if (!checkedOS) { + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) { + runningOnWin2K = false; + checkedOS = true; + } + } + return runningOnWin2K; + } + } + + /// + /// + /// + /// Gets or sets whether the scrollbar is shown at all times. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.ListBoxScrollIsVisibleDescr) + ] + public bool ScrollAlwaysVisible { + get { + return scrollAlwaysVisible; + } + set { + if (scrollAlwaysVisible != value) { + scrollAlwaysVisible = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates whether list currently allows selection of list items. + /// For ListBox, this returns true unless SelectionMode is SelectionMode.None. + /// + protected override bool AllowSelection { + get { + return selectionMode != SelectionMode.None; + } + } + + /// + /// + /// The index of the currently selected item in the list, if there + /// is one. If the value is -1, there is currently no selection. If the + /// value is 0 or greater, than the value is the index of the currently + /// selected item. If the MultiSelect property on the ListBox is true, + /// then a non-zero value for this property is the index of the first + /// selection + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedIndexDescr) + ] + public override int SelectedIndex { + get { + + SelectionMode current = (selectionModeChanging) ? cachedSelectionMode : selectionMode; + + if (current == SelectionMode.None) { + return -1; + } + + if (current == SelectionMode.One && IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0)); + } + + if (itemsCollection != null && SelectedItems.Count > 0) { + return Items.IndexOfIdentifier(SelectedItems.GetObjectAt(0)); + } + + return -1; + } + set { + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (value < -1 || value >= itemCount) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (selectionMode == SelectionMode.None) { + throw new ArgumentException(SR.GetString(SR.ListBoxInvalidSelectionMode), "SelectedIndex"); + } + + if (selectionMode == SelectionMode.One && value != -1) { + + // Single select an individual value. + int currentIndex = SelectedIndex; + + if (currentIndex != value) { + if (currentIndex != -1) { + SelectedItems.SetSelected(currentIndex, false); + } + SelectedItems.SetSelected(value, true); + + if (IsHandleCreated) { + NativeSetSelected(value, true); + } + + OnSelectedIndexChanged(EventArgs.Empty); + } + } + else if (value == -1) { + if (SelectedIndex != -1) { + ClearSelected(); + // ClearSelected raises OnSelectedIndexChanged for us + } + } + else { + if (!SelectedItems.GetSelected(value)) { + + // Select this item while keeping any previously selected items selected. + // + SelectedItems.SetSelected(value, true); + if (IsHandleCreated) { + NativeSetSelected(value, true); + } + OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// A collection of the indices of the selected items in the + /// list box. If there are no selected items in the list box, the result is + /// an empty collection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedIndicesDescr) + ] + public SelectedIndexCollection SelectedIndices { + get { + if (selectedIndices == null) { + selectedIndices = new SelectedIndexCollection(this); + } + return selectedIndices; + } + } + + /// + /// + /// The value of the currently selected item in the list, if there + /// is one. If the value is null, there is currently no selection. If the + /// value is non-null, then the value is that of the currently selected + /// item. If the MultiSelect property on the ListBox is true, then a + /// non-null return value for this method is the value of the first item + /// selected + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedItemDescr) + ] + public object SelectedItem { + get { + if (SelectedItems.Count > 0) { + return SelectedItems[0]; + } + + return null; + } + set { + if (itemsCollection != null) { + if (value != null) { + int index = itemsCollection.IndexOf(value); + if (index != -1) { + SelectedIndex = index; + } + } + else { + SelectedIndex = -1; + } + } + } + } + + /// + /// + /// The collection of selected items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedItemsDescr) + ] + public SelectedObjectCollection SelectedItems { + get { + if (selectedItems == null) { + selectedItems = new SelectedObjectCollection(this); + } + return selectedItems; + } + } + + /// + /// + /// Controls how many items at a time can be selected in the listbox. Valid + /// values are from the System.Windows.Forms.SelectionMode enumeration. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(SelectionMode.One), + SRDescription(SR.ListBoxSelectionModeDescr) + ] + public virtual SelectionMode SelectionMode { + get { + return selectionMode; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SelectionMode.None, (int)SelectionMode.MultiExtended)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SelectionMode)); + } + + if (selectionMode != value) { + SelectedItems.EnsureUpToDate(); + selectionMode = value; + try + { + selectionModeChanging = true; + RecreateHandle(); + } + finally + { + selectionModeChanging = false; + cachedSelectionMode = selectionMode; + // update the selectedItems list and SelectedItems index collection + if (IsHandleCreated) + { + NativeUpdateSelection(); + } + } + } + } + } + + /// + /// + /// Indicates if the ListBox is sorted or not. 'true' means that strings in + /// the list will be sorted alphabetically + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListBoxSortedDescr) + ] + public bool Sorted { + get { + return sorted; + } + set { + if (sorted != value) { + sorted = value; + + if (sorted && itemsCollection != null && itemsCollection.Count >= 1) { + Sort(); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Bindable(false) + ] + public override string Text { + get { + if (SelectionMode != SelectionMode.None && SelectedItem != null) { + if (FormattingEnabled) { + return GetItemText(SelectedItem); + } else { + return FilterItemOnProperty(SelectedItem).ToString(); + } + } + else { + return base.Text; + } + } + set { + base.Text = value; + + // Scan through the list items looking for the supplied text string. If we find it, + // select it. + // + if (SelectionMode != SelectionMode.None && value != null && (SelectedItem == null || !value.Equals(GetItemText(SelectedItem)))) { + + int cnt = Items.Count; + for (int index=0; index < cnt; ++index) { + if (String.Compare(value, GetItemText(Items[index]), true, CultureInfo.CurrentCulture) == 0) { + SelectedIndex = index; + return; + } + } + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The index of the first visible item in a list box. Initially + /// the item with index 0 is at the top of the list box, but if the list + /// box contents have been scrolled another item may be at the top. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxTopIndexDescr) + ] + public int TopIndex { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETTOPINDEX, 0, 0)); + } + else { + return topIndex; + } + } + set { + if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETTOPINDEX, value, 0); + } + else { + topIndex = value; + } + } + } + + /// + /// + /// Enables a list box to recognize and expand tab characters when drawing + /// its strings. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListBoxUseTabStopsDescr) + ] + public bool UseTabStops { + get { + return useTabStops; + } + set { + if (useTabStops != value) { + useTabStops = value; + RecreateHandle(); + } + } + } + /// + /// + /// Allows to set the width of the tabs between the items in the list box. + /// The integer array should have the tab spaces in the ascending order. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ListBoxCustomTabOffsetsDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Browsable(false) + ] + public IntegerCollection CustomTabOffsets { + get { + if (customTabOffsets == null) { + customTabOffsets = new IntegerCollection(this); + } + return customTabOffsets; + } + } + + /// + /// + /// Performs the work of adding the specified items to the Listbox + /// + [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] + protected virtual void AddItemsCore(object[] value) { + int count = value == null? 0: value.Length; + if (count == 0) { + return; + } + + Items.AddRangeInternal(value); + } + + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// ListBox / CheckedListBox Onpaint. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_DRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWITEM, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] + public event MeasureItemEventHandler MeasureItem { + add { + Events.AddHandler(EVENT_MEASUREITEM, value); + } + remove { + Events.RemoveHandler(EVENT_MEASUREITEM, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + } + + /// + /// + /// While the preferred way to insert items is to set Items.All, + /// and set all the items at once, there are times when you may wish to + /// insert each item one at a time. To help with the performance of this, + /// it is desirable to prevent the ListBox from painting during these + /// operations. This method, along with EndUpdate, is the preferred + /// way of doing this. Don't forget to call EndUpdate when you're done, + /// or else the ListBox won't paint properly afterwards. + /// + public void BeginUpdate() { + BeginUpdateInternal(); + updateCount++; + } + + private void CheckIndex(int index) { + if (index < 0 || index >= Items.Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); + } + + private void CheckNoDataSource() { + if (DataSource != null) + throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual ObjectCollection CreateItemCollection() { + return new ObjectCollection(this); + } + + internal virtual int ComputeMaxItemWidth(int oldMax) { + // pass LayoutUtils the collection of strings + string[] strings = new string[this.Items.Count]; + + for (int i = 0; i < Items.Count; i ++) { + strings[i] = GetItemText(Items[i]); + } + + Size textSize = LayoutUtils.OldGetLargestStringSizeInCollection(Font, strings); + return Math.Max(oldMax, textSize.Width); + } + + /// + /// + /// Unselects all currently selected items. + /// + public void ClearSelected() { + + bool hadSelection = false; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + for (int x = 0; x < itemCount;x++) { + if (SelectedItems.GetSelected(x)) { + hadSelection = true; + SelectedItems.SetSelected(x, false); + if (IsHandleCreated) { + NativeSetSelected(x, false); + } + } + } + + if (hadSelection) { + OnSelectedIndexChanged(EventArgs.Empty); + } + } + + /// + /// + /// While the preferred way to insert items is to set Items.All, + /// and set all the items at once, there are times when you may wish to + /// insert each item one at a time. To help with the performance of this, + /// it is desirable to prevent the ListBox from painting during these + /// operations. This method, along with BeginUpdate, is the preferred + /// way of doing this. BeginUpdate should be called first, and this method + /// should be called when you want the control to start painting again. + /// + public void EndUpdate() { + EndUpdateInternal(); + --updateCount; + } + + /// + /// + /// Finds the first item in the list box that starts with the given string. + /// The search is not case sensitive. + /// + public int FindString(string s) { + return FindString(s, -1); + } + + /// + /// + /// Finds the first item after the given index which starts with the given + /// string. The search is not case sensitive. + /// + public int FindString(string s, int startIndex) { + if (s == null) return -1; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (itemCount == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemCount) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of LB_FINDSTRING. + // The managed version correctly handles Turkish I. + return FindStringInternal(s, Items, startIndex, false); + } + + /// + /// + /// Finds the first item in the list box that matches the given string. + /// The strings must match exactly, except for differences in casing. + /// + public int FindStringExact(string s) { + return FindStringExact(s, -1); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match excatly, except for differences in + /// casing. + /// + public int FindStringExact(string s, int startIndex) { + if (s == null) return -1; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (itemCount == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemCount) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of LB_FINDSTRING. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, true); + } + + /// + /// + /// Returns the height of the given item in a list box. The index parameter + /// is ignored if drawMode is not OwnerDrawVariable. + /// + public int GetItemHeight(int index) { + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + // Note: index == 0 is OK even if the ListBox currently has + // no items. + // + if (index < 0 || (index > 0 && index >= itemCount)) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + if (drawMode != DrawMode.OwnerDrawVariable) index = 0; + + if (IsHandleCreated) { + int h = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETITEMHEIGHT, index, 0)); + if (h == -1) + throw new Win32Exception(); + return h; + } + + return itemHeight; + } + + /// + /// + /// Retrieves a Rectangle object which describes the bounding rectangle + /// around an item in the list. If the item in question is not visible, + /// the rectangle will be outside the visible portion of the control. + /// + public Rectangle GetItemRectangle(int index) { + CheckIndex(index); + NativeMethods.RECT rect = new NativeMethods.RECT(); + SendMessage(NativeMethods.LB_GETITEMRECT, index, ref rect); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// List box overrides GetScaledBounds to ensure we always scale the requested + /// height, not the current height. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + // update bounds' height to use the requested height, not the current height. These + // can be different if integral height is turned on. + bounds.Height = requestedHeight; + return base.GetScaledBounds(bounds, factor, specified); + } + + /// + /// + /// Tells you whether or not the item at the supplied index is selected + /// or not. + /// + public bool GetSelected(int index) { + CheckIndex(index); + return GetSelectedInternal(index); + } + + private bool GetSelectedInternal(int index) { + if (IsHandleCreated) { + int sel = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSEL, index, 0)); + if (sel == -1) { + throw new Win32Exception(); + } + return sel > 0; + } + else { + if (itemsCollection != null && SelectedItems.GetSelected(index)) { + return true; + } + return false; + } + } + + /// + /// + /// Retrieves the index of the item at the given coordinates. + /// + public int IndexFromPoint(Point p) { + return IndexFromPoint(p.X, p.Y); + } + + /// + /// + /// Retrieves the index of the item at the given coordinates. + /// + public int IndexFromPoint(int x, int y) { + //NT4 SP6A : SendMessage Fails. So First check whether the point is in Client Co-ordinates and then + //call Sendmessage. + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref r); + if (r.left <= x && x < r.right && r.top <= y && y < r.bottom) { + int index = unchecked( (int) (long)SendMessage(NativeMethods.LB_ITEMFROMPOINT, 0, unchecked( (int) (long)NativeMethods.Util.MAKELPARAM(x, y)))); + if (NativeMethods.Util.HIWORD(index) == 0) { + // Inside ListBox client area + return NativeMethods.Util.LOWORD(index); + } + } + + return NoMatches; + } + + /// + /// Adds the given item to the native combo box. This asserts if the handle hasn't been + /// created. + /// + private int NativeAdd(object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.LB_ADDSTRING, 0, GetItemText(item))); + + if (insertIndex == NativeMethods.LB_ERRSPACE) { + throw new OutOfMemoryException(); + } + + if (insertIndex == NativeMethods.LB_ERR) { + // On some platforms (e.g. Win98), the ListBox control + // appears to return LB_ERR if there are a large number (>32000) + // of items. It doesn't appear to set error codes appropriately, + // so we'll have to assume that LB_ERR corresponds to item + // overflow. + // + throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); + } + + return insertIndex; + } + + /// + /// Clears the contents of the combo box. + /// + private void NativeClear() { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + SendMessage(NativeMethods.LB_RESETCONTENT, 0, 0); + } + + /// + /// Get the text stored by the native control for the specified list item. + /// + internal string NativeGetItemText(int index) { + int len = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETTEXTLEN, index, 0)); + StringBuilder sb = new StringBuilder(len + 1); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETTEXT, index, sb); + return sb.ToString(); + } + + /// + /// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been + /// created or if the resulting insert index doesn't match the passed in index. + /// + private int NativeInsert(int index, object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.LB_INSERTSTRING, index, GetItemText(item))); + + if (insertIndex == NativeMethods.LB_ERRSPACE) { + throw new OutOfMemoryException(); + } + + if (insertIndex == NativeMethods.LB_ERR) { + // On some platforms (e.g. Win98), the ListBox control + // appears to return LB_ERR if there are a large number (>32000) + // of items. It doesn't appear to set error codes appropriately, + // so we'll have to assume that LB_ERR corresponds to item + // overflow. + // + throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); + } + + Debug.Assert(insertIndex == index, "NativeListBox inserted at " + insertIndex + " not the requested index of " + index); + return insertIndex; + } + + /// + /// Removes the native item from the given index. + /// + private void NativeRemoveAt(int index) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + + bool selected = (unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSEL, (IntPtr)index, IntPtr.Zero)) > 0); + SendMessage(NativeMethods.LB_DELETESTRING, index, 0); + + //If the item currently selected is removed then we should fire a Selectionchanged event... + //as the next time selected index returns -1... + + if (selected) { + OnSelectedIndexChanged(EventArgs.Empty); + } + } + + /// + /// Sets the selection of the given index to the native window. This does not change + /// the collection; you must update the collection yourself. + /// + private void NativeSetSelected(int index, bool value) { + Debug.Assert(IsHandleCreated, "Should only call Native methods after the handle has been created"); + Debug.Assert(selectionMode != SelectionMode.None, "Guard against setting selection for None selection mode outside this code."); + + if (selectionMode == SelectionMode.One) { + SendMessage(NativeMethods.LB_SETCURSEL, (value ? index : -1), 0); + } + else { + SendMessage(NativeMethods.LB_SETSEL, value? -1: 0, index); + } + } + + /// + /// This is called by the SelectedObjectCollection in response to the first + /// query on that collection after we have called Dirty(). Dirty() is called + /// when we receive a LBN_SELCHANGE message. + /// + private void NativeUpdateSelection() { + Debug.Assert(IsHandleCreated, "Should only call native methods if handle is created"); + + // Clear the selection state. + // + int cnt = Items.Count; + for (int i = 0; i < cnt; i++) { + SelectedItems.SetSelected(i, false); + } + + int[] result = null; + + switch (selectionMode) { + + case SelectionMode.One: + int index = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0)); + if (index >= 0) result = new int[] {index}; + break; + + case SelectionMode.MultiSimple: + case SelectionMode.MultiExtended: + int count = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0)); + if (count > 0) { + result = new int[count]; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETSELITEMS, count, result); + } + break; + } + + // Now set the selected state on the appropriate items. + // + if (result != null) { + foreach(int i in result) { + SelectedItems.SetSelected(i, true); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnChangeUICues(UICuesEventArgs e) { + + // ListBox seems to get a bit confused when the UI cues change for the first + // time - it draws the focus rect when it shouldn't and vice-versa. So when + // the UI cues change, we just do an extra invalidate to get it into the + // right state. + // + Invalidate(); + + base.OnChangeUICues(e); + } + + /// + /// + /// Actually goes and fires the drawItem event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler yourself for this event]. They should, + /// however, remember to call base.onDrawItem(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// We need to know when the window handle has been created so we can + /// set up a few things, like column width, etc! Inheriting classes should + /// not forget to call base.OnHandleCreated(). + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + + //for getting the current Locale to set the Scrollbars... + // + SendMessage(NativeMethods.LB_SETLOCALE, CultureInfo.CurrentCulture.LCID, 0); + + if (columnWidth != 0) { + SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); + } + if (drawMode == DrawMode.OwnerDrawFixed) { + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); + } + + if (topIndex != 0) { + SendMessage(NativeMethods.LB_SETTOPINDEX, topIndex, 0); + } + + if (UseCustomTabOffsets && CustomTabOffsets != null) { + int wpar = CustomTabOffsets.Count; + int[] offsets = new int[wpar]; + CustomTabOffsets.CopyTo(offsets, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); + } + + if (itemsCollection != null) { + + int count = itemsCollection.Count; + + for(int i = 0; i < count; i++) { + NativeAdd(itemsCollection[i]); + + if (selectionMode != SelectionMode.None) { + if (selectedItems != null) { + selectedItems.PushSelectionIntoNativeListBox(i); + } + } + } + } + if (selectedItems != null) { + if (selectedItems.Count > 0 && selectionMode == SelectionMode.One) { + SelectedItems.Dirty(); + SelectedItems.EnsureUpToDate(); + } + } + UpdateHorizontalExtent(); + } + + /// + /// + /// Overridden to make sure that we set up and clear out items + /// correctly. Inheriting controls should not forget to call + /// base.OnHandleDestroyed() + /// + protected override void OnHandleDestroyed(EventArgs e) { + SelectedItems.EnsureUpToDate(); + if (Disposing) { + itemsCollection = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnMeasureItem(MeasureItemEventArgs e) { + MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + + // Changing the font causes us to resize, always rounding down. + // Make sure we do this after base.OnPropertyChanged, which sends the WM_SETFONT message + + // Avoid the listbox and textbox behaviour in Collection editors + // + UpdateFontCache(); + } + + + /// + /// + /// We override this so we can re-create the handle if the parent has changed. + /// + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + //No need to RecreateHandle if we are removing the Listbox from controls collection... + //so check the parent before recreating the handle... + if (this.ParentInternal != null) { + RecreateHandle(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnResize(EventArgs e) { + + base.OnResize(e); + + // There are some repainting issues for RightToLeft - so invalidate when we resize. + // + if (RightToLeft == RightToLeft.Yes || this.HorizontalScrollbar) { + Invalidate(); + } + + } + + /// + /// + /// Actually goes and fires the selectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected override void OnSelectedIndexChanged(EventArgs e) { + base.OnSelectedIndexChanged(e); + + // set the position in the dataSource, if there is any + // we will only set the position in the currencyManager if it is different + // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) + // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls + // into the backEnd. We do not need to do that. + // + if (this.DataManager != null && DataManager.Position != SelectedIndex) { + //read this as "if everett or (whidbey and selindex is valid)" + if (!FormattingEnabled || this.SelectedIndex != -1) + { + // VSWhidbey 95176: don't change dataManager position if we simply unselected everything. + // (Doing so would cause the first LB item to be selected...) + this.DataManager.Position = this.SelectedIndex; + } + } + + // VSWhidbey 163411: Call the handler after updating the DataManager's position so that + // the DataManager's selected index will be correct in an event handler. + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; + if (handler != null) { + handler(this, e); + } + } + + /// + protected override void OnSelectedValueChanged(EventArgs e) { + base.OnSelectedValueChanged(e); + selectedValueChangedFired = true; + } + + /// + protected override void OnDataSourceChanged(EventArgs e) { + if (DataSource == null) + { + BeginUpdate(); + SelectedIndex = -1; + Items.ClearInternal(); + EndUpdate(); + } + base.OnDataSourceChanged(e); + RefreshItems(); + } + + /// + protected override void OnDisplayMemberChanged(EventArgs e) { + base.OnDisplayMemberChanged(e); + + // we want to use the new DisplayMember even if there is no data source + RefreshItems(); + + if (SelectionMode != SelectionMode.None && this.DataManager != null) + this.SelectedIndex = this.DataManager.Position; + } + + /// + /// + /// Forces the ListBox to invalidate and immediately + /// repaint itself and any children if OwnerDrawVariable. + /// + public override void Refresh() { + if (drawMode == DrawMode.OwnerDrawVariable) { + //Fire MeasureItem for Each Item in the Listbox... + int cnt = Items.Count; + Graphics graphics = CreateGraphicsInternal(); + + try + { + for (int i = 0; i < cnt; i++) { + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, i, ItemHeight); + OnMeasureItem(mie); + } + } + finally { + graphics.Dispose(); + } + + } + base.Refresh(); + } + /// + /// + /// Reparses the objects, getting new text strings for them. + /// + /// + protected override void RefreshItems() { + + // Store the currently selected object collection. + // + ObjectCollection savedItems = itemsCollection; + + // Clear the items. + // + itemsCollection = null; + selectedIndices = null; + + if (IsHandleCreated) { + NativeClear(); + } + + object[] newItems = null; + + // if we have a dataSource and a DisplayMember, then use it + // to populate the Items collection + // + if (this.DataManager != null && this.DataManager.Count != -1) { + newItems = new object[this.DataManager.Count]; + for(int i = 0; i < newItems.Length; i++) { + newItems[i] = this.DataManager[i]; + } + } + else if (savedItems != null) { + newItems = new object[savedItems.Count]; + savedItems.CopyTo(newItems, 0); + } + + // Store the current list of items + // + if (newItems != null) { + Items.AddRangeInternal(newItems); + } + + // Restore the selected indices if SelectionMode allows it. + // + if (SelectionMode != SelectionMode.None) { + if (this.DataManager != null) { + // put the selectedIndex in sync w/ the position in the dataManager + this.SelectedIndex = this.DataManager.Position; + } + else { + if (savedItems != null) { + int cnt = savedItems.Count; + for(int index = 0; index < cnt; index++) { + if (savedItems.InnerArray.GetState(index, SelectedObjectCollection.SelectedObjectMask)) { + SelectedItem = savedItems[index]; + } + } + } + } + } + + } + + /// + /// + /// Reparses the object at the given index, getting new text string for it. + /// + /// + protected override void RefreshItem(int index) { + Items.SetItemInternal(index, Items[index]); + } + + public override void ResetBackColor() { + base.ResetBackColor(); + } + + public override void ResetForeColor() { + base.ResetForeColor(); + } + + + private void ResetItemHeight() { + itemHeight = DefaultItemHeight; + } + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + if (factor.Width != 1F && factor.Height != 1F) { + UpdateFontCache(); + } + base.ScaleControl(factor, specified); + } + + + /// + /// + /// Overrides Control.SetBoundsCore to remember the requestedHeight. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + // Avoid the listbox and textbox behaviour in Collection editors + // + + + if (!integralHeightAdjust && height != Height) + requestedHeight = height; + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Performs the work of setting the specified items into the ListBox. + /// + protected override void SetItemsCore(IList value) { + BeginUpdate(); + Items.ClearInternal(); + Items.AddRangeInternal(value); + + this.SelectedItems.Dirty(); + + // if the list changed, we want to keep the same selected index + // CurrencyManager will provide the PositionChanged event + // it will be provided before changing the list though... + if (this.DataManager != null) { + if (this.DataSource is ICurrencyManagerProvider) { + // Everett ListControl's had a bug where they would not fire + // OnSelectedValueChanged if their list of items were refreshed. + // We fix this post-Everett. + // However, for APPCOMPAT reasons, we only want to fix it when binding to + // Whidbey components. + // vsw 547279. + this.selectedValueChangedFired = false; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETCURSEL, DataManager.Position, 0); + } + + // if the list changed and we still did not fire the + // onselectedChanged event, then fire it now; + if (!selectedValueChangedFired) { + OnSelectedValueChanged(EventArgs.Empty); + selectedValueChangedFired = false; + } + } + EndUpdate(); + } + + /// + protected override void SetItemCore(int index, object value) { + Items.SetItemInternal(index, value); + } + + /// + /// + /// Allows the user to set an item as being selected or not. This should + /// only be used with ListBoxes that allow some sort of multi-selection. + /// + public void SetSelected(int index, bool value) { + int itemCount = (itemsCollection == null) ? 0: itemsCollection.Count; + if (index < 0 || index >= itemCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + if (selectionMode == SelectionMode.None) + throw new InvalidOperationException(SR.GetString(SR.ListBoxInvalidSelectionMode)); + + SelectedItems.SetSelected(index, value); + if (IsHandleCreated) { + NativeSetSelected(index, value); + } + SelectedItems.Dirty(); + OnSelectedIndexChanged(EventArgs.Empty); + } + + /// + /// + /// Sorts the items in the listbox. + /// + protected virtual void Sort() { + // This will force the collection to add each item back to itself + // if sorted is now true, then the add method will insert the item + // into the correct position + // + CheckNoDataSource(); + + SelectedObjectCollection currentSelections = SelectedItems; + currentSelections.EnsureUpToDate(); + + if (sorted && itemsCollection != null) { + itemsCollection.InnerArray.Sort(); + + // Now that we've sorted, update our handle + // if it has been created. + if (IsHandleCreated) { + NativeClear(); + int count = itemsCollection.Count; + for(int i = 0; i < count; i++) { + NativeAdd(itemsCollection[i]); + if (currentSelections.GetSelected(i)) { + NativeSetSelected(i, true); + } + } + } + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + if (itemsCollection != null) { + s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); + if (Items.Count > 0) { + string z = GetItemText(Items[0]); + string txt = (z.Length > 40) ? z.Substring(0, 40) : z; + s += ", Items[0]: " + txt; + } + } + return s; + } + private void UpdateFontCache() { + fontIsChanged = true; + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + maxWidth = -1; + UpdateHorizontalExtent(); + // clear the preferred size cache. + CommonProperties.xClearPreferredSizeCache(this); + + } + + private void UpdateHorizontalExtent() { + if (!multiColumn && horizontalScrollbar && IsHandleCreated) { + int width = horizontalExtent; + if (width == 0) { + width = MaxItemWidth; + } + SendMessage(NativeMethods.LB_SETHORIZONTALEXTENT, width, 0); + } + } + + // Updates the cached max item width + // + private void UpdateMaxItemWidth(object item, bool removing) { + + // We shouldn't be caching maxWidth if we don't have horizontal scrollbars, + // or horizontal extent has been set + // + if (!horizontalScrollbar || horizontalExtent > 0) { + maxWidth = -1; + return; + } + + // Only update if we are currently caching maxWidth + // + if (maxWidth > -1) { + + // Compute item width + // + int width; + using (Graphics graphics = CreateGraphicsInternal()) { + width = (int)(Math.Ceiling(graphics.MeasureString(GetItemText(item), this.Font).Width)); + } + + if (removing) { + // We're removing this item, so if it's the longest + // in the list, reset the cache + // + if (width >= maxWidth) { + maxWidth = -1; + } + } + else { + // We're adding or inserting this item - update the cache + // + if (width > maxWidth) { + maxWidth = width; + } + } + } + } + + // Updates the Custom TabOffsets + // + + private void UpdateCustomTabOffsets() { + if (IsHandleCreated && UseCustomTabOffsets && CustomTabOffsets != null) { + int wpar = CustomTabOffsets.Count; + int[] offsets = new int[wpar]; + CustomTabOffsets.CopyTo(offsets, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); + Invalidate(); + } + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + using (Pen pen = new Pen(VisualStyleInformation.TextControlBorder)) { + g.DrawRectangle(pen, rect); + } + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual void WmReflectCommand(ref Message m) { + switch (NativeMethods.Util.HIWORD(m.WParam)) { + case NativeMethods.LBN_SELCHANGE: + if (selectedItems != null) { + selectedItems.Dirty(); + } + OnSelectedIndexChanged(EventArgs.Empty); + break; + case NativeMethods.LBN_DBLCLK: + // Handle this inside WM_LBUTTONDBLCLK + // OnDoubleClick(EventArgs.Empty); + break; + } + } + + /// + /// + /// + /// + private void WmReflectDrawItem(ref Message m) { + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + IntPtr dc = dis.hDC; + IntPtr oldPal = SetUpPalette(dc, false /*force*/, false /*realize*/); + try { + Graphics g = Graphics.FromHdcInternal(dc); + + try { + Rectangle bounds = Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); + + if (HorizontalScrollbar) { + if (MultiColumn) { + bounds.Width = Math.Max(ColumnWidth, bounds.Width); + } + else { + bounds.Width = Math.Max(MaxItemWidth, bounds.Width); + } + } + + + OnDrawItem(new DrawItemEventArgs(g, Font, bounds, dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); + } + finally { + g.Dispose(); + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); + } + } + m.Result = (IntPtr)1; + } + + /// + /// + /// + /// + // This method is only called if in owner draw mode + private void WmReflectMeasureItem(ref Message m) { + + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + if (drawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { + Graphics graphics = CreateGraphicsInternal(); + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); + try { + OnMeasureItem(mie); + mis.itemHeight = mie.ItemHeight; + } + finally { + graphics.Dispose(); + } + } + else { + mis.itemHeight = ItemHeight; + } + Marshal.StructureToPtr(mis, m.LParam, false); + m.Result = (IntPtr)1; + } + + /// + /// + /// The list's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the list continues to function properly. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmReflectDrawItem(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: + WmReflectMeasureItem(ref m); + break; + case NativeMethods.WM_PRINT: + WmPrint(ref m); + break; + case NativeMethods.WM_LBUTTONDOWN: + if (selectedItems != null) { + selectedItems.Dirty(); + } + base.WndProc(ref m); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x,y); + pt = PointToScreen(pt); + bool captured = Capture; + if (captured && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + + + if (!doubleClickFired && !ValidationCancelled) { + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + else { + doubleClickFired = false; + // WM_COMMAND is only fired if the user double clicks an item, + // so we can't use that as a double-click substitute + if (!ValidationCancelled) { + OnDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + } + } + + // + // If this control has been disposed in the user's event handler, then we need to ignore the WM_LBUTTONUP + // message to avoid exceptions thrown as a result of handle re-creation (VSWhidbey#95150). + // We handle this situation here and not at the top of the window procedure since this is the only place + // where we can get disposed as an effect of external code (form.Close() for instance) and then pass the + // message to the base class. + // + if (GetState(STATE_DISPOSED)) + { + base.DefWndProc(ref m); + } + else + { + base.WndProc(ref m); + } + + doubleClickFired = false; + break; + + case NativeMethods.WM_RBUTTONUP: + // Get the mouse location + // + int rx = NativeMethods.Util.SignedLOWORD(m.LParam); + int ry = NativeMethods.Util.SignedHIWORD(m.LParam); + Point rpt = new Point(rx,ry); + rpt = PointToScreen(rpt); + bool rCaptured = Capture; + if (rCaptured && UnsafeNativeMethods.WindowFromPoint(rpt.X, rpt.Y) == Handle) { + if (selectedItems != null) { + selectedItems.Dirty(); + } + } + base.WndProc(ref m); + break; + + case NativeMethods.WM_LBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //the first WM_LBUTTONUP, resets the flag for Doubleclick + //So its necessary for us to set it again... + doubleClickFired = true; + base.WndProc(ref m); + break; + + case NativeMethods.WM_WINDOWPOSCHANGED: + base.WndProc(ref m); + if (integralHeight && fontIsChanged) { + Height = Math.Max(Height,ItemHeight); + fontIsChanged = false; + } + break; + + default: + base.WndProc(ref m); + break; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ListBoxAccessibleObject(this); + } + else { + return base.CreateAccessibilityInstance(); + } + } + + /// + /// This is similar to ArrayList except that it also + /// mantains a bit-flag based state element for each item + /// in the array. + /// + /// The methods to enumerate, count and get data support + /// virtualized indexes. Indexes are virtualized according + /// to the state mask passed in. This allows ItemArray + /// to be the backing store for one read-write "master" + /// collection and serveral read-only collections based + /// on masks. ItemArray supports up to 31 masks. + /// + internal class ItemArray : IComparer { + + private static int lastMask = 1; + + private ListControl listControl; + private Entry[] entries; + private int count; + private int version; + + public ItemArray(ListControl listControl) { + this.listControl = listControl; + } + + /// + /// The version of this array. This number changes with each + /// change to the item list. + /// + public int Version { + get { + return version; + } + } + + /// + /// Adds the given item to the array. The state is initially + /// zero. + /// + public object Add(object item) { + EnsureSpace(1); + version++; + entries[count] = new Entry(item); + return entries[count++]; + } + + /// + /// Adds the given collection of items to the array. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void AddRange(ICollection items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + EnsureSpace(items.Count); + foreach(object i in items) { + entries[count++] = new Entry(i); + } + version++; + } + + /// + /// Clears this array. + /// + public void Clear() { + if (count > 0) { + Array.Clear(entries, 0, count); + } + + count = 0; + version++; + } + + /// + /// Allocates a new bitmask for use. + /// + public static int CreateMask() { + int mask = lastMask; + lastMask = lastMask << 1; + Debug.Assert(lastMask > mask, "We have overflowed our state mask."); + return mask; + } + + /// + /// Ensures that our internal array has space for + /// the requested # of elements. + /// + private void EnsureSpace(int elements) { + if (entries == null) { + entries = new Entry[Math.Max(elements, 4)]; + } + else if (count + elements >= entries.Length) { + int newLength = Math.Max(entries.Length * 2, entries.Length + elements); + Entry[] newEntries = new Entry[newLength]; + entries.CopyTo(newEntries, 0); + entries = newEntries; + } + } + + /// + /// Turns a virtual index into an actual index. + /// + public int GetActualIndex(int virtualIndex, int stateMask) { + if (stateMask == 0) { + return virtualIndex; + } + + // More complex; we must compute this index. + int calcIndex = -1; + for(int i = 0; i < count; i++) { + if ((entries[i].state & stateMask) != 0) { + calcIndex++; + if (calcIndex == virtualIndex) { + return i; + } + } + } + + return -1; + } + + /// + /// Gets the count of items matching the given mask. + /// + public int GetCount(int stateMask) { + // If mask is zero, then just give the main count + if (stateMask == 0) { + return count; + } + + // more complex: must provide a count of items + // based on a mask. + + int filteredCount = 0; + + for(int i = 0; i < count; i++) { + if ((entries[i].state & stateMask) != 0) { + filteredCount++; + } + } + + return filteredCount; + } + + /// + /// Retrieves an enumerator that will enumerate based on + /// the given mask. + /// + public IEnumerator GetEnumerator(int stateMask) { + return GetEnumerator(stateMask, false); + } + + /// + /// Retrieves an enumerator that will enumerate based on + /// the given mask. + /// + public IEnumerator GetEnumerator(int stateMask, bool anyBit) { + return new EntryEnumerator(this, stateMask, anyBit); + } + + /// + /// Gets the item at the given index. The index is + /// virtualized against the given mask value. + /// + public object GetItem(int virtualIndex, int stateMask) { + int actualIndex = GetActualIndex(virtualIndex, stateMask); + + if (actualIndex == -1) { + throw new IndexOutOfRangeException(); + } + + return entries[actualIndex].item; + } + /// + /// Gets the item at the given index. The index is + /// virtualized against the given mask value. + /// + internal object GetEntryObject(int virtualIndex, int stateMask) { + int actualIndex = GetActualIndex(virtualIndex, stateMask); + + if (actualIndex == -1) { + throw new IndexOutOfRangeException(); + } + + return entries[actualIndex]; + } + /// + /// Returns true if the requested state mask is set. + /// The index is the actual index to the array. + /// + public bool GetState(int index, int stateMask) { + return ((entries[index].state & stateMask) == stateMask); + } + + /// + /// Returns the virtual index of the item based on the + /// state mask. + /// + public int IndexOf(object item, int stateMask) { + + int virtualIndex = -1; + + for(int i = 0; i < count; i++) { + if (stateMask == 0 || (entries[i].state & stateMask) != 0) { + virtualIndex++; + if (entries[i].item.Equals(item)) { + return virtualIndex; + } + } + } + + return -1; + } + + /// + /// Returns the virtual index of the item based on the + /// state mask. Uses reference equality to identify the + /// given object in the list. + /// + public int IndexOfIdentifier(object identifier, int stateMask) { + int virtualIndex = -1; + + for(int i = 0; i < count; i++) { + if (stateMask == 0 || (entries[i].state & stateMask) != 0) { + virtualIndex++; + if (entries[i] == identifier) { + return virtualIndex; + } + } + } + + return -1; + } + + /// + /// Inserts item at the given index. The index + /// is not virtualized. + /// + public void Insert(int index, object item) { + EnsureSpace(1); + + if (index < count) { + System.Array.Copy(entries, index, entries, index + 1, count - index); + } + + entries[index] = new Entry(item); + count++; + version++; + } + + /// + /// Removes the given item from the array. If + /// the item is not in the array, this does nothing. + /// + public void Remove(object item) { + int index = IndexOf(item, 0); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// Removes the item at the given index. + /// + public void RemoveAt(int index) { + count--; + for (int i = index; i < count; i++) { + entries[i] = entries[i+1]; + } + entries[count] = null; + version++; + } + + /// + /// Sets the item at the given index to a new value. + /// + public void SetItem(int index, object item) { + entries[index].item = item; + } + + /// + /// Sets the state data for the given index. + /// + public void SetState(int index, int stateMask, bool value) { + if (value) { + entries[index].state |= stateMask; + } + else { + entries[index].state &= ~stateMask; + } + version++; + } + + /// + /// Find element in sorted array. If element is not found returns a binary complement of index for inserting + /// + public int BinarySearch(object element) + { + return Array.BinarySearch(entries, 0, count, element, this); + } + + + /// + /// Sorts our array. + /// + public void Sort() { + Array.Sort(entries, 0, count, this); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void Sort(Array externalArray) { + Array.Sort(externalArray, this); + } + + int IComparer.Compare(object item1, object item2) { + if (item1 == null) { + if (item2 == null) + return 0; //both null, then they are equal + + return -1; //item1 is null, but item2 is valid (greater) + } + if (item2 == null) + return 1; //item2 is null, so item 1 is greater + + if (item1 is Entry) { + item1 = ((Entry)item1).item; + } + + if (item2 is Entry) { + item2 = ((Entry)item2).item; + } + + String itemName1 = listControl.GetItemText(item1); + String itemName2 = listControl.GetItemText(item2); + + CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; + return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); + } + + /// + /// This is a single entry in our item array. + /// + private class Entry { + public object item; + public int state; + + public Entry(object item) { + this.item = item; + this.state = 0; + } + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class EntryEnumerator : IEnumerator { + private ItemArray items; + private bool anyBit; + private int state; + private int current; + private int version; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public EntryEnumerator(ItemArray items, int state, bool anyBit) { + this.items = items; + this.state = state; + this.anyBit = anyBit; + this.version = items.version; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); + + while(true) { + if (current < items.count - 1) { + current++; + if (anyBit) { + if ((items.entries[current].state & state) != 0) { + return true; + } + } + else { + if ((items.entries[current].state & state) == state) { + return true; + } + } + } + else { + current = items.count; + return false; + } + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items.entries[current].item; + } + } + } + } + + // Items + /// + /// + /// + /// A collection that stores objects. + /// + /// + [ListBindable(false)] + public class ObjectCollection : IList { + + private ListBox owner; + private ItemArray items; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// + /// Initializes a new instance of ListBox.ObjectCollection based on another ListBox.ObjectCollection. + /// + /// + public ObjectCollection(ListBox owner, ObjectCollection value) { + this.owner = owner; + this.AddRange(value); + } + + /// + /// + /// + /// Initializes a new instance of ListBox.ObjectCollection containing any array of objects. + /// + /// + public ObjectCollection(ListBox owner, object[] value) { + this.owner = owner; + this.AddRange(value); + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count { + get { + return InnerArray.GetCount(0); + } + } + + /// + /// Internal access to the actual data store. + /// + internal ItemArray InnerArray { + get { + if (items == null) { + items = new ItemArray(owner); + } + return items; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Adds an item to the List box. For an unsorted List box, the item is + /// added to the end of the existing list of items. For a sorted List box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + + public int Add(object item) + { + owner.CheckNoDataSource(); + int index = AddInternal(item); + owner.UpdateHorizontalExtent(); + return index; + } + + + private int AddInternal(object item) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + int index = -1; + if (!owner.sorted) + { + InnerArray.Add(item); + } + else + { + if (Count > 0) + { + index = InnerArray.BinarySearch(item); + if (index < 0) + { + index = ~index; // getting the index of the first element that is larger than the search value + //this index will be used for insert + } + } + else + index = 0; + + Debug.Assert(index >= 0 && index <= Count, "Wrong index for insert"); + InnerArray.Insert(index, item); + } + bool successful = false; + + try + { + if (owner.sorted) + { + if (owner.IsHandleCreated) + { + owner.NativeInsert(index, item); + owner.UpdateMaxItemWidth(item, false); + if (owner.selectedItems != null) + { + // VSWhidbey 95187: sorting may throw the LB contents and the selectedItem array out of synch. + owner.selectedItems.Dirty(); + } + } + } + else + { + index = Count - 1; + if (owner.IsHandleCreated) + { + owner.NativeAdd(item); + owner.UpdateMaxItemWidth(item, false); + } + } + successful = true; + } + finally + { + if (!successful) + { + InnerArray.Remove(item); + } + } + + return index; + } + + + /// + /// + int IList.Add(object item) { + return Add(item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ObjectCollection value) { + owner.CheckNoDataSource(); + AddRangeInternal((ICollection)value); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(object[] items) { + owner.CheckNoDataSource(); + AddRangeInternal((ICollection)items); + } + + internal void AddRangeInternal(ICollection items) { + + if (items == null) + { + throw new ArgumentNullException("items"); + } + owner.BeginUpdate(); + try + { + foreach (object item in items) + { + // adding items one-by-one for performance + // not using sort because after the array is sorted index of each newly added item will need to be found + // AddInternal is based on BinarySearch and finds index without any additional cost + AddInternal(item); + } + } + finally + { + owner.UpdateHorizontalExtent(); + owner.EndUpdate(); + } + } + + /// + /// + /// Retrieves the item with the specified index. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual object this[int index] { + get { + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + return InnerArray.GetItem(index, 0); + } + set { + owner.CheckNoDataSource(); + SetItemInternal(index, value); + } + } + + /// + /// + /// Removes all items from the ListBox. + /// + public virtual void Clear() { + owner.CheckNoDataSource(); + ClearInternal(); + } + + /// + /// Removes all items from the ListBox. Bypasses the data source check. + /// + internal void ClearInternal() { + + //update the width.. to reset Scrollbars.. + // Clear the selection state. + // + int cnt = owner.Items.Count; + for (int i = 0; i < cnt; i++) { + owner.UpdateMaxItemWidth(InnerArray.GetItem(i, 0), true); + } + + + if (owner.IsHandleCreated) { + owner.NativeClear(); + } + InnerArray.Clear(); + owner.maxWidth = -1; + owner.UpdateHorizontalExtent(); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object value) { + return IndexOf(value) != -1; + } + + /// + /// + /// Copies the ListBox Items collection to a destination array. + /// + public void CopyTo(object[] destination, int arrayIndex) { + int count = InnerArray.GetCount(0); + for(int i = 0; i < count; i++) { + destination[i + arrayIndex] = InnerArray.GetItem(i, 0); + } + } + + /// + /// + void ICollection.CopyTo(Array destination, int index) { + int count = InnerArray.GetCount(0); + for(int i = 0; i < count; i++) { + destination.SetValue(InnerArray.GetItem(i, 0), i + index); + } + } + + /// + /// + /// Returns an enumerator for the ListBox Items collection. + /// + public IEnumerator GetEnumerator() { + return InnerArray.GetEnumerator(0); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerArray.IndexOf(value,0); + } + + /// + /// + /// [To be supplied.] + /// + /// + internal int IndexOfIdentifier(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerArray.IndexOfIdentifier(value,0); + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public void Insert(int index, object item) { + owner.CheckNoDataSource(); + + if (index < 0 || index > InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (item == null) { + throw new ArgumentNullException("item"); + } + + // If the combo box is sorted, then nust treat this like an add + // because we are going to twiddle the index anyway. + // + if (owner.sorted) { + Add(item); + } + else { + InnerArray.Insert(index, item); + if (owner.IsHandleCreated) { + + bool successful = false; + + try { + owner.NativeInsert(index, item); + owner.UpdateMaxItemWidth(item, false); + successful = true; + } + finally { + if (!successful) { + InnerArray.RemoveAt(index); + } + } + } + } + owner.UpdateHorizontalExtent(); + } + + /// + /// + /// Removes the given item from the ListBox, provided that it is + /// actually in the list. + /// + public void Remove(object value) { + + int index = InnerArray.IndexOf(value, 0); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + /// Removes an item from the ListBox at the given index. + /// + public void RemoveAt(int index) { + owner.CheckNoDataSource(); + + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); + + // VSWhidbey 95181: Update InnerArray before calling NativeRemoveAt to ensure that when + // SelectedIndexChanged is raised (by NativeRemoveAt), InnerArray's state matches wrapped LB state. + InnerArray.RemoveAt(index); + + if (owner.IsHandleCreated) { + owner.NativeRemoveAt(index); + } + + owner.UpdateHorizontalExtent(); + } + + internal void SetItemInternal(int index, object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); + InnerArray.SetItem(index, value); + + // If the native control has been created, and the display text of the new list item object + // is different to the current text in the native list item, recreate the native list item... + if (owner.IsHandleCreated) { + bool selected = (owner.SelectedIndex == index); + if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { + owner.NativeRemoveAt(index); + owner.SelectedItems.SetSelected(index, false); + owner.NativeInsert(index, value); + owner.UpdateMaxItemWidth(value, false); + if (selected) { + owner.SelectedIndex = index; + } + } + else { + // NEW - FOR COMPATIBILITY REASONS + // Minimum compatibility fix for VSWhidbey 377287 + if (selected) { + owner.OnSelectedIndexChanged(EventArgs.Empty); //will fire selectedvaluechanged + } + } + } + owner.UpdateHorizontalExtent(); + } + } // end ObjectCollection + + //****************************************************************************************** + // IntegerCollection + /// + /// + /// [To be supplied.] + /// + public class IntegerCollection : IList { + private ListBox owner; + private int[] innerArray; + private int count=0; + + /// + /// + /// [To be supplied.] + /// + public IntegerCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current selected items. + /// + [Browsable(false)] + public int Count { + get { + return count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + bool IList.IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int item) { + return IndexOf(item) != -1; + } + + /// + /// + bool IList.Contains(object item) { + if (item is Int32) { + return Contains((int)item); + } + else { + return false; + } + } + + public void Clear() + { + count = 0; + innerArray = null; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int item) { + int index = -1; + + if (innerArray != null) { + index = Array.IndexOf(innerArray, item); + + // We initialize innerArray with more elements than needed in the method EnsureSpace, + // and we don't actually remove element from innerArray in the method RemoveAt, + // so there maybe some elements which are not actually in innerArray will be found + // and we need to filter them out + if (index >= count) { + index = -1; + } + } + + return index; + } + + /// + /// + int IList.IndexOf(object item) { + if (item is Int32) { + return IndexOf((int)item); + } + else { + return -1; + } + } + + + /// + /// Add a unique integer to the collection in sorted order. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + private int AddInternal(int item) { + + EnsureSpace(1); + + int index = IndexOf(item); + if (index == -1) { + innerArray[count++] = item; + Array.Sort(innerArray,0,count); + index = IndexOf(item); + } + return index; + } + + /// + /// + /// Adds a unique integer to the collection in sorted order. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public int Add(int item) { + int index = AddInternal(item); + owner.UpdateCustomTabOffsets(); + + return index; + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. + // So we don't have to localize it. + ] + int IList.Add(object item) { + if (!(item is int)) { + throw new ArgumentException("item"); + } + return Add((int)item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(int[] items) { + AddRangeInternal((ICollection)items); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(IntegerCollection value) { + AddRangeInternal((ICollection)value); + } + + /// + /// Add range that bypasses the data source check. + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. + // So we don't have to localize it. + ] + private void AddRangeInternal(ICollection items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + owner.BeginUpdate(); + try + { + EnsureSpace(items.Count); + foreach(object item in items) { + if (!(item is int)) { + throw new ArgumentException("item"); + } + else { + AddInternal((int)item); + } + } + owner.UpdateCustomTabOffsets(); + } + finally + { + owner.EndUpdate(); + } + } + + + /// + /// Ensures that our internal array has space for + /// the requested # of elements. + /// + private void EnsureSpace(int elements) { + if (innerArray == null) { + innerArray = new int[Math.Max(elements, 4)]; + } + else if (count + elements >= innerArray.Length) { + int newLength = Math.Max(innerArray.Length * 2, innerArray.Length + elements); + int[] newEntries = new int[newLength]; + innerArray.CopyTo(newEntries, 0); + innerArray = newEntries; + } + } + + /// + /// + void IList.Clear() { + Clear(); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxCantInsertIntoIntegerCollection)); + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "value" is the name of the param passed in. + // So we don't have to localize it. + ] + void IList.Remove(object value) { + if (!(value is int)) { + throw new ArgumentException("value"); + } + Remove((int)value); + } + + /// + /// + void IList.RemoveAt(int index) { + RemoveAt(index); + } + + /// + /// + /// Removes the given item from the array. If + /// the item is not in the array, this does nothing. + /// + public void Remove(int item) { + + int index = IndexOf(item); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + /// Removes the item at the given index. + /// + public void RemoveAt(int index) { + if (index < 0 || index >= count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + count--; + for (int i = index; i < count; i++) { + innerArray[i] = innerArray[i+1]; + } + } + + /// + /// + /// Retrieves the specified selected item. + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "index" is the name of the param passed in. + // So we don't have to localize it. + ] + public int this[int index] { + get { + return innerArray[index]; + } + [ + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. + // We can't change its text. + ] + set { + + if (index < 0 || index >= count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + innerArray[index] = (int)value; + owner.UpdateCustomTabOffsets(); + + + } + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + object IList.this[int index] { + get { + return this[index]; + } + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // "value" is the name of the param. + // So we don't have to localize it. + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. + // We can't change its text. + ] + set { + if (!(value is int)) { + throw new ArgumentException("value"); + } + else { + this[index] = (int)value; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = Count; + for (int i = 0; i < cnt; i++) { + destination.SetValue(this[i], i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + IEnumerator IEnumerable.GetEnumerator() { + return new CustomTabOffsetsEnumerator(this); + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class CustomTabOffsetsEnumerator : IEnumerator { + private IntegerCollection items; + private int current; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public CustomTabOffsetsEnumerator(IntegerCollection items) { + this.items = items; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + + if (current < items.Count - 1) { + current++; + return true; + } + else { + current = items.Count; + return false; + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.Count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items[current]; + } + } + } + } + + //****************************************************************************************** + + // SelectedIndices + /// + /// + /// [To be supplied.] + /// + public class SelectedIndexCollection : IList { + private ListBox owner; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedIndexCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current selected items. + /// + [Browsable(false)] + public int Count { + get { + return owner.SelectedItems.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int selectedIndex) { + return IndexOf(selectedIndex) != -1; + } + + /// + /// + bool IList.Contains(object selectedIndex) { + if (selectedIndex is Int32) { + return Contains((int)selectedIndex); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int selectedIndex) { + + // Just what does this do? The selectedIndex parameter above is the index into the + // main object collection. We look at the state of that item, and if the state indicates + // that it is selected, we get back the virtualized index into this collection. Indexes on + // this collection match those on the SelectedObjectCollection. + if (selectedIndex >= 0 && + selectedIndex < InnerArray.GetCount(0) && + InnerArray.GetState(selectedIndex, SelectedObjectCollection.SelectedObjectMask)) { + + return InnerArray.IndexOf(InnerArray.GetItem(selectedIndex, 0), SelectedObjectCollection.SelectedObjectMask); + } + + return -1; + } + + /// + /// + int IList.IndexOf(object selectedIndex) { + if (selectedIndex is Int32) { + return IndexOf((int)selectedIndex); + } + else { + return -1; + } + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + /// Retrieves the specified selected item. + /// + public int this[int index] { + get { + object identifier = InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); + return InnerArray.IndexOfIdentifier(identifier, 0); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + owner.SelectedItems.EnsureUpToDate(); + return ((ObjectCollection)owner.Items).InnerArray; + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = Count; + for (int i = 0; i < cnt; i++) { + destination.SetValue(this[i], i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + if (owner != null) { + owner.ClearSelected(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Add(int index) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null) { + if (index != -1 && !Contains(index)) { + owner.SetSelected(index, true); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(int index) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null) { + if (index != -1 && Contains(index)) { + owner.SetSelected(index, false); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return new SelectedIndexEnumerator(this); + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class SelectedIndexEnumerator : IEnumerator { + private SelectedIndexCollection items; + private int current; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public SelectedIndexEnumerator(SelectedIndexCollection items) { + this.items = items; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + + if (current < items.Count - 1) { + current++; + return true; + } + else { + current = items.Count; + return false; + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.Count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items[current]; + } + } + } + } + + // Should be "ObjectCollection", except we already have one of those. + /// + /// + /// [To be supplied.] + /// + public class SelectedObjectCollection : IList { + + // This is the bitmask used within ItemArray to identify selected objects. + internal static int SelectedObjectMask = ItemArray.CreateMask(); + + private ListBox owner; + private bool stateDirty; + private int lastVersion; + private int count; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedObjectCollection(ListBox owner) { + this.owner = owner; + this.stateDirty = true; + this.lastVersion = -1; + } + + /// + /// + /// Number of current selected items. + /// + public int Count { + get { + if (owner.IsHandleCreated) { + SelectionMode current = (owner.selectionModeChanging) ? owner.cachedSelectionMode : owner.selectionMode; + switch (current) { + + case SelectionMode.None: + return 0; + + case SelectionMode.One: + int index = owner.SelectedIndex; + if (index >= 0) { + return 1; + } + return 0; + + case SelectionMode.MultiSimple: + case SelectionMode.MultiExtended: + return unchecked( (int) (long)owner.SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0)); + } + + return 0; + } + + // If the handle hasn't been created, we must do this the hard way. + // Getting the count when using a mask is expensive, so cache it. + // + if (lastVersion != InnerArray.Version) { + lastVersion = InnerArray.Version; + count = InnerArray.GetCount(SelectedObjectMask); + } + + return count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// Called by the list box to dirty the selected item state. + /// + internal void Dirty() { + stateDirty = true; + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + EnsureUpToDate(); + return ((ObjectCollection)owner.Items).InnerArray; + } + } + + + /// + /// This is the function that Ensures that the selections are uptodate with + /// current listbox handle selections. + /// + internal void EnsureUpToDate() { + if (stateDirty) { + stateDirty = false; + if (owner.IsHandleCreated) { + owner.NativeUpdateSelection(); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object selectedObject) { + return IndexOf(selectedObject) != -1; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object selectedObject) { + return InnerArray.IndexOf(selectedObject, SelectedObjectMask); + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + // A new internal method used in SelectedIndex getter... + // For a Multi select ListBox there can be two items with the same name ... + // and hence a object comparison is required... + // This method returns the "object" at the passed index rather than the "item" ... + // this "object" is then compared in the IndexOf( ) method of the itemsCollection. + // + /// + /// + internal object GetObjectAt(int index) { + return InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); + } + + /// + /// + /// Retrieves the specified selected item. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object this[int index] { + get { + return InnerArray.GetItem(index, SelectedObjectMask); + } + set { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = InnerArray.GetCount(SelectedObjectMask); + for (int i = 0; i < cnt; i++) { + destination.SetValue(InnerArray.GetItem(i, SelectedObjectMask), i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return InnerArray.GetEnumerator(SelectedObjectMask); + } + + /// + /// This method returns if the actual item index is selected. The index is the index to the MAIN + /// collection, not this one. + /// + internal bool GetSelected(int index) { + return InnerArray.GetState(index, SelectedObjectMask); + } + + // when SelectedObjectsCollection::ItemArray is accessed we push the selection from Native ListBox into our .Net ListBox - see EnsureUpToDate() + // when we create the handle we need to be able to do the opposite : push the selection from .Net ListBox into Native ListBox + internal void PushSelectionIntoNativeListBox(int index) { + // we can't use ItemArray accessor because this will wipe out our Selection collection + bool selected = ((ObjectCollection)owner.Items).InnerArray.GetState(index, SelectedObjectMask); + // push selection only if the item is actually selected + // this also takes care of the case where owner.SelectionMode == SelectionMode.One + if (selected) { + this.owner.NativeSetSelected(index, true /*we signal selection to the native listBox only if the item is actually selected*/); + } + } + + /// + /// Same thing for GetSelected. + /// + internal void SetSelected(int index, bool value) { + InnerArray.SetState(index, SelectedObjectMask, value); + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + if (owner != null) { + owner.ClearSelected(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Add(object value) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null && value != null) { + int index = items.IndexOf(value); + if (index != -1 && !GetSelected(index)) { + owner.SelectedIndex = index; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(object value) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null & value != null) { + int index = items.IndexOf(value); + if (index != -1 && GetSelected(index)) { + owner.SetSelected(index, false); + } + } + } + } + } + + private sealed class ListBoxAccessibleObject : ControlAccessibleObject { + private readonly ListBox owner; + + public ListBoxAccessibleObject(ListBox control) : base(control) { + this.owner = control; + } + + #region IAccessibleEx related overrides + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override object GetObjectForChild(int childId) { + Accessibility.IAccessible systemIAccessible = this.GetSystemIAccessibleInternal(); + if (IsChildIdValid(childId, systemIAccessible)) { + if ((AccessibleRole)systemIAccessible.accRole[childId] == AccessibleRole.ListItem) { + return new ListBoxItemAccessibleObject(this.owner, childId); + } + } + + return base.GetObjectForChild(childId); + } + + #endregion + + private static bool IsChildIdValid(int childId, Accessibility.IAccessible systemIAccessible) { + // 0 (i.e. CHILDID_SELF) references the control itself, so it is considered "invalid" in this scope + return childId > 0 && childId <= systemIAccessible.accChildCount; + } + + private sealed class ListBoxItemAccessibleObject : AccessibleObject { + private readonly int childId; + private readonly ListBox owner; + + public ListBoxItemAccessibleObject(ListBox owner, int childId) { + Debug.Assert(owner != null, $"{nameof(owner)} should not be null"); + Debug.Assert(childId > 0, $"{nameof(childId)} has unexpected value"); + + this.owner = owner; + this.childId = childId; + } + + #region IAccessibleEx related overrides + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ScrollItemPatternId) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + #endregion + + #region IScrollItemProvider related overrides + + internal override void ScrollIntoView() { + if (this.owner.IsHandleCreated && IsChildIdValid(this.childId, this.owner.AccessibilityObject.GetSystemIAccessibleInternal())) { + this.owner.TopIndex = this.childId - 1; + } + } + + #endregion + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListBox.cs.back b/WindowsForms/Managed/System/WinForms/ListBox.cs.back new file mode 100644 index 000000000..8c79617b5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListBox.cs.back @@ -0,0 +1,4472 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + using System.Windows.Forms.Layout; + + using System.Drawing.Design; + using System.ComponentModel; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.VisualStyles; + + using System.Collections; + using System.Drawing; + using Microsoft.Win32; + using System.Text; + + /// + /// + /// + /// This is a control that presents a list of items to the user. They may be + /// navigated using the keyboard, or the scrollbar on the right side of the + /// control. One or more items may be selected as well. + /// + /// + /// The preferred way to add items is to set them all via an array at once, + /// which is definitely the most efficient way. The following is an example + /// of this: + /// + /// + /// ListBox lb = new ListBox(); + /// // set up properties on the listbox here. + /// lb.Items.All = new String [] { + /// "A", + /// "B", + /// "C", + /// "D"}; + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.ListBoxDesigner, " + AssemblyRef.SystemDesign), + DefaultEvent("SelectedIndexChanged"), + DefaultProperty("Items"), + DefaultBindingProperty("SelectedValue"), + SRDescription(SR.DescriptionListBox) + ] + public class ListBox : ListControl { + /// + /// + /// while doing a search, if no matches are found, this is returned + /// + public const int NoMatches = NativeMethods.LB_ERR; + /// + /// + /// The default item height for an owner-draw ListBox. + /// + public const int DefaultItemHeight = 13; // 13 == listbox's non-ownerdraw item height. That's with Win2k and + // the default font; on other platforms and with other fonts, it may be different. + + private const int maxWin9xHeight = 32767; //Win9x doesn't deal with height > 32K + + private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); + private static readonly object EVENT_DRAWITEM = new object(); + private static readonly object EVENT_MEASUREITEM = new object(); + + static bool checkedOS = false; + static bool runningOnWin2K = true; + + SelectedObjectCollection selectedItems; + SelectedIndexCollection selectedIndices; + ObjectCollection itemsCollection; + + // + + int itemHeight = DefaultItemHeight; + int columnWidth; + int requestedHeight; + int topIndex; + int horizontalExtent = 0; + int maxWidth = -1; + int updateCount = 0; + + bool sorted = false; + bool scrollAlwaysVisible = false; + bool integralHeight = true; + bool integralHeightAdjust = false; + bool multiColumn = false; + bool horizontalScrollbar = false; + bool useTabStops = true; + bool useCustomTabOffsets = false; + bool fontIsChanged = false; + bool doubleClickFired = false; + bool selectedValueChangedFired = false; + + DrawMode drawMode = System.Windows.Forms.DrawMode.Normal; + BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + SelectionMode selectionMode = System.Windows.Forms.SelectionMode.One; + + // VsWhidbey : 447524 + SelectionMode cachedSelectionMode = System.Windows.Forms.SelectionMode.One; + //We need to know that we are in middle of handleRecreate through Setter of SelectionMode. + //In this case we set a bool denoting that we are changing SelectionMode and + //in this case we should always use the cachedValue instead of the currently set value. + //We need to change this in the count as well as SelectedIndex code where we access the SelectionMode. + private bool selectionModeChanging = false; + + /// + /// This value stores the array of custom tabstops in the listbox. the array should be populated by + /// integers in a ascending order. + /// + private IntegerCollection customTabOffsets; + + /// + /// Default start position of items in the checked list box + /// + private const int defaultListItemStartPos = 1; + + /// + /// Borders are 1 pixel height. + /// + private const int defaultListItemBorderHeight = 1; + + /// + /// Borders are 1 pixel width and a pixel buffer + /// + private const int defaultListItemPaddingBuffer = 3; + + + internal int scaledListItemStartPosition = defaultListItemStartPos; + internal int scaledListItemBordersHeight = 2 * defaultListItemBorderHeight; + internal int scaledListItemPaddingBuffer = defaultListItemPaddingBuffer; + + + /// + /// + /// Creates a basic win32 list box with default values for everything. + /// + public ListBox() : base() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.StandardClick | + ControlStyles.UseTextForAccessibility, false); + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + SetBounds(0, 0, 120, 96); + + requestedHeight = Height; + + PrepareForDrawing(); + } + + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + PrepareForDrawing(); + } + + private void PrepareForDrawing() { + // Scale paddings + if (DpiHelper.EnableCheckedListBoxHighDpiImprovements) { + scaledListItemStartPosition = LogicalToDeviceUnits(defaultListItemStartPos); + + // height inlude 2 borders ( top and bottom). we are using multiplication by 2 instead of scaling doubled value to get an even number + // that might helps us in positioning control in the center for list items. + scaledListItemBordersHeight = 2 * LogicalToDeviceUnits(defaultListItemBorderHeight); + scaledListItemPaddingBuffer = LogicalToDeviceUnits(defaultListItemPaddingBuffer); + } + } + + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Retrieves the current border style. Values for this are taken from + /// The System.Windows.Forms.BorderStyle enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.ListBoxBorderDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (value != borderStyle) { + borderStyle = value; + RecreateHandle(); + // Avoid the listbox and textbox behavior in Collection editors + // + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + } + } + } + + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(0), + SRDescription(SR.ListBoxColumnWidthDescr) + ] + public int ColumnWidth { + get { + return columnWidth; + } + + set { + if (value < 0) { + throw new ArgumentException(SR.GetString(SR.InvalidLowBoundArgumentEx, "value", + (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (columnWidth != value) { + columnWidth = value; + // if it's zero, we need to reset, and only way to do + // that is to recreate the handle. + if (columnWidth == 0) { + RecreateHandle(); + } + else if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); + } + } + } + } + + /// + /// + /// Retrieves the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "LISTBOX"; + + cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.LBS_NOTIFY | NativeMethods.LBS_HASSTRINGS; + if (scrollAlwaysVisible) cp.Style |= NativeMethods.LBS_DISABLENOSCROLL; + if (!integralHeight) cp.Style |= NativeMethods.LBS_NOINTEGRALHEIGHT; + if (useTabStops) cp.Style |= NativeMethods.LBS_USETABSTOPS; + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + if (multiColumn) { + cp.Style |= NativeMethods.LBS_MULTICOLUMN | NativeMethods.WS_HSCROLL; + } + else if (horizontalScrollbar) { + cp.Style |= NativeMethods.WS_HSCROLL; + } + + switch (selectionMode) { + case SelectionMode.None: + cp.Style |= NativeMethods.LBS_NOSEL; + break; + case SelectionMode.MultiSimple: + cp.Style |= NativeMethods.LBS_MULTIPLESEL; + break; + case SelectionMode.MultiExtended: + cp.Style |= NativeMethods.LBS_EXTENDEDSEL; + break; + case SelectionMode.One: + break; + } + + switch (drawMode) { + case DrawMode.Normal: + break; + case DrawMode.OwnerDrawFixed: + cp.Style |= NativeMethods.LBS_OWNERDRAWFIXED; + break; + case DrawMode.OwnerDrawVariable: + cp.Style |= NativeMethods.LBS_OWNERDRAWVARIABLE; + break; + } + + return cp; + } + } + + + /// + /// + /// Enables a list box to recognize and expand tab characters when drawing + /// its strings using the CustomTabOffsets integer array. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Browsable(false) + ] + public bool UseCustomTabOffsets { + get { + return useCustomTabOffsets; + } + set { + if (useCustomTabOffsets != value) { + useCustomTabOffsets = value; + RecreateHandle(); + } + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(120, 96); + } + } + + /// + /// + /// Retrieves the style of the listbox. This will indicate if the system + /// draws it, or if the user paints each item manually. It also indicates + /// whether or not items have to be of the same height. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DrawMode.Normal), + SRDescription(SR.ListBoxDrawModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual DrawMode DrawMode { + get { + return drawMode; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); + } + if (drawMode != value) { + if (MultiColumn && value == DrawMode.OwnerDrawVariable) { + throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); + } + drawMode = value; + RecreateHandle(); + if (drawMode == DrawMode.OwnerDrawVariable) { + // VSWhidbey 139179 - force a layout after RecreateHandle() completes because now + // the LB is definitely fully populated and can report a preferred size accurately. + LayoutTransaction.DoLayoutIf(AutoSize, this.ParentInternal, this, PropertyNames.DrawMode); + } + } + } + } + + // Used internally to find the currently focused item + // + internal int FocusedIndex { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCARETINDEX, 0, 0)); + } + + return -1; + } + } + + /// + // VSWhidbey 95179: The scroll bars don't display properly when the IntegralHeight == false + // and the control is resized before the font size is change and the new font size causes + // the height of all the items to exceed the new height of the control. This is a bug in + // the control, but can be easily worked around by removing and re-adding all the items. + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + + if (false == integralHeight) { + // VSWhidbey 95179: Refresh the list to force the scroll bars to display + // when the integral height is false. + RefreshItems(); + } + } + } + + /// + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// Indicates the width, in pixels, by which a list box can be scrolled horizontally (the scrollable width). + /// This property will only have an effect if HorizontalScrollbars is true. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.ListBoxHorizontalExtentDescr) + ] + public int HorizontalExtent { + get { + return horizontalExtent; + } + + set { + if (value != horizontalExtent) { + horizontalExtent = value; + UpdateHorizontalExtent(); + } + } + } + + /// + /// + /// Indicates whether or not the ListBox should display a horizontal scrollbar + /// when the items extend beyond the right edge of the ListBox. + /// If true, the scrollbar will automatically set its extent depending on the length + /// of items in the ListBox. The exception is if the ListBox is owner-draw, in + /// which case HorizontalExtent will need to be explicitly set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.ListBoxHorizontalScrollbarDescr) + ] + public bool HorizontalScrollbar { + get { + return horizontalScrollbar; + } + + set { + if (value != horizontalScrollbar) { + horizontalScrollbar = value; + + // There seems to be a bug in the native ListBox in that the addition + // of the horizontal scroll bar does not get reflected in the control + // rightaway. So, we refresh the items here. + RefreshItems(); + + // Only need to recreate the handle if not MultiColumn + // (HorizontalScrollbar has no effect on a MultiColumn listbox) + // + if (!MultiColumn) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Indicates if the listbox should avoid showing partial Items. If so, + /// then only full items will be displayed, and the listbox will be resized + /// to prevent partial items from being shown. Otherwise, they will be + /// shown + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ListBoxIntegralHeightDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public bool IntegralHeight { + get { + return integralHeight; + } + + set { + if (integralHeight != value) { + integralHeight = value; + RecreateHandle(); + // Avoid the listbox and textbox behaviour in Collection editors + // + + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + } + } + } + + /// + /// + /// + /// Returns + /// the height of an item in an owner-draw list box. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DefaultItemHeight), + Localizable(true), + SRDescription(SR.ListBoxItemHeightDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public virtual int ItemHeight { + get { + if (drawMode == DrawMode.OwnerDrawFixed || + drawMode == DrawMode.OwnerDrawVariable) { + return itemHeight; + } + + return GetItemHeight(0); + } + + set { + if (value < 1 || value > 255) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidExBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "256")); + } + if (itemHeight != value) { + itemHeight = value; + if (drawMode == DrawMode.OwnerDrawFixed && IsHandleCreated) { + BeginUpdate(); + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, value); + + // Changing the item height might require a resize for IntegralHeight list boxes + // + if (IntegralHeight) { + Size oldSize = Size; + Size = new Size(oldSize.Width + 1, oldSize.Height); + Size = oldSize; + } + + EndUpdate(); + } + } + } + } + + /// + /// + /// Collection of items in this listbox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ListBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + MergableProperty(false) + ] + public ObjectCollection Items { + get { + if (itemsCollection == null) { + itemsCollection = CreateItemCollection(); + } + return itemsCollection; + } + } + + // Computes the maximum width of all items in the ListBox + // + internal virtual int MaxItemWidth { + get { + + if (horizontalExtent > 0) { + return horizontalExtent; + } + + if (DrawMode != DrawMode.Normal) { + return -1; + } + + // Return cached maxWidth if available + // + if (maxWidth > -1) { + return maxWidth; + } + + // Compute maximum width + // + maxWidth = ComputeMaxItemWidth(maxWidth); + + return maxWidth; + } + } + + /// + /// + /// + /// Indicates if the listbox is multi-column + /// or not. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListBoxMultiColumnDescr) + ] + public bool MultiColumn { + get { + return multiColumn; + } + set { + if (multiColumn != value) { + if (value && drawMode == DrawMode.OwnerDrawVariable) { + throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); + } + multiColumn = value; + RecreateHandle(); + } + } + } + + /// + /// + /// The total height of the items in the list box. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxPreferredHeightDescr) + ] + public int PreferredHeight { + get { + int height = 0; + + if (drawMode == DrawMode.OwnerDrawVariable) { + // VSWhidbey 139179 - don't try to get item heights from the LB when items haven't been + // added to the LB yet. Just return current height. + if (RecreatingHandle || GetState(STATE_CREATINGHANDLE)) { + height = this.Height; + } + else { + if (itemsCollection != null) { + int cnt = itemsCollection.Count; + for (int i = 0; i < cnt; i++) { + height += GetItemHeight(i); + } + } + } + } + else { + //VSWhidbey #148270 + //When the list is empty, we don't want to multiply by 0 here. + int cnt = (itemsCollection == null || itemsCollection.Count == 0) ? 1 : itemsCollection.Count; + height = GetItemHeight(0) * cnt; + } + + if (borderStyle != BorderStyle.None) { + height += SystemInformation.BorderSize.Height * 4 + 3; + } + + return height; + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + int height = PreferredHeight; + int width; + + // Convert with a dummy height to add space required for borders + // VSWhidbey #151141 -PreferredSize should return either the new + // size of the control, or the default size if the handle has not been + // created + if (IsHandleCreated) + { + width = SizeFromClientSize(new Size(MaxItemWidth, height)).Width; + width += SystemInformation.VerticalScrollBarWidth + 4; + } + else + { + return DefaultSize; + } + return new Size(width, height) + Padding.Size; + } + + /// + public override RightToLeft RightToLeft { + get { + if (!RunningOnWin2K) { + return RightToLeft.No; + } + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + static bool RunningOnWin2K { + get { + if (!checkedOS) { + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) { + runningOnWin2K = false; + checkedOS = true; + } + } + return runningOnWin2K; + } + } + + /// + /// + /// + /// Gets or sets whether the scrollbar is shown at all times. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.ListBoxScrollIsVisibleDescr) + ] + public bool ScrollAlwaysVisible { + get { + return scrollAlwaysVisible; + } + set { + if (scrollAlwaysVisible != value) { + scrollAlwaysVisible = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates whether list currently allows selection of list items. + /// For ListBox, this returns true unless SelectionMode is SelectionMode.None. + /// + protected override bool AllowSelection { + get { + return selectionMode != SelectionMode.None; + } + } + + /// + /// + /// The index of the currently selected item in the list, if there + /// is one. If the value is -1, there is currently no selection. If the + /// value is 0 or greater, than the value is the index of the currently + /// selected item. If the MultiSelect property on the ListBox is true, + /// then a non-zero value for this property is the index of the first + /// selection + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedIndexDescr) + ] + public override int SelectedIndex { + get { + + SelectionMode current = (selectionModeChanging) ? cachedSelectionMode : selectionMode; + + if (current == SelectionMode.None) { + return -1; + } + + if (current == SelectionMode.One && IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0)); + } + + if (itemsCollection != null && SelectedItems.Count > 0) { + return Items.IndexOfIdentifier(SelectedItems.GetObjectAt(0)); + } + + return -1; + } + set { + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (value < -1 || value >= itemCount) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (selectionMode == SelectionMode.None) { + throw new ArgumentException(SR.GetString(SR.ListBoxInvalidSelectionMode), "SelectedIndex"); + } + + if (selectionMode == SelectionMode.One && value != -1) { + + // Single select an individual value. + int currentIndex = SelectedIndex; + + if (currentIndex != value) { + if (currentIndex != -1) { + SelectedItems.SetSelected(currentIndex, false); + } + SelectedItems.SetSelected(value, true); + + if (IsHandleCreated) { + NativeSetSelected(value, true); + } + + OnSelectedIndexChanged(EventArgs.Empty); + } + } + else if (value == -1) { + if (SelectedIndex != -1) { + ClearSelected(); + // ClearSelected raises OnSelectedIndexChanged for us + } + } + else { + if (!SelectedItems.GetSelected(value)) { + + // Select this item while keeping any previously selected items selected. + // + SelectedItems.SetSelected(value, true); + if (IsHandleCreated) { + NativeSetSelected(value, true); + } + OnSelectedIndexChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// A collection of the indices of the selected items in the + /// list box. If there are no selected items in the list box, the result is + /// an empty collection. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedIndicesDescr) + ] + public SelectedIndexCollection SelectedIndices { + get { + if (selectedIndices == null) { + selectedIndices = new SelectedIndexCollection(this); + } + return selectedIndices; + } + } + + /// + /// + /// The value of the currently selected item in the list, if there + /// is one. If the value is null, there is currently no selection. If the + /// value is non-null, then the value is that of the currently selected + /// item. If the MultiSelect property on the ListBox is true, then a + /// non-null return value for this method is the value of the first item + /// selected + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedItemDescr) + ] + public object SelectedItem { + get { + if (SelectedItems.Count > 0) { + return SelectedItems[0]; + } + + return null; + } + set { + if (itemsCollection != null) { + if (value != null) { + int index = itemsCollection.IndexOf(value); + if (index != -1) { + SelectedIndex = index; + } + } + else { + SelectedIndex = -1; + } + } + } + } + + /// + /// + /// The collection of selected items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxSelectedItemsDescr) + ] + public SelectedObjectCollection SelectedItems { + get { + if (selectedItems == null) { + selectedItems = new SelectedObjectCollection(this); + } + return selectedItems; + } + } + + /// + /// + /// Controls how many items at a time can be selected in the listbox. Valid + /// values are from the System.Windows.Forms.SelectionMode enumeration. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(SelectionMode.One), + SRDescription(SR.ListBoxSelectionModeDescr) + ] + public virtual SelectionMode SelectionMode { + get { + return selectionMode; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SelectionMode.None, (int)SelectionMode.MultiExtended)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SelectionMode)); + } + + if (selectionMode != value) { + SelectedItems.EnsureUpToDate(); + selectionMode = value; + try + { + selectionModeChanging = true; + RecreateHandle(); + } + finally + { + selectionModeChanging = false; + cachedSelectionMode = selectionMode; + // update the selectedItems list and SelectedItems index collection + if (IsHandleCreated) + { + NativeUpdateSelection(); + } + } + } + } + } + + /// + /// + /// Indicates if the ListBox is sorted or not. 'true' means that strings in + /// the list will be sorted alphabetically + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListBoxSortedDescr) + ] + public bool Sorted { + get { + return sorted; + } + set { + if (sorted != value) { + sorted = value; + + if (sorted && itemsCollection != null && itemsCollection.Count >= 1) { + Sort(); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Bindable(false) + ] + public override string Text { + get { + if (SelectionMode != SelectionMode.None && SelectedItem != null) { + if (FormattingEnabled) { + return GetItemText(SelectedItem); + } else { + return FilterItemOnProperty(SelectedItem).ToString(); + } + } + else { + return base.Text; + } + } + set { + base.Text = value; + + // Scan through the list items looking for the supplied text string. If we find it, + // select it. + // + if (SelectionMode != SelectionMode.None && value != null && (SelectedItem == null || !value.Equals(GetItemText(SelectedItem)))) { + + int cnt = Items.Count; + for (int index=0; index < cnt; ++index) { + if (String.Compare(value, GetItemText(Items[index]), true, CultureInfo.CurrentCulture) == 0) { + SelectedIndex = index; + return; + } + } + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The index of the first visible item in a list box. Initially + /// the item with index 0 is at the top of the list box, but if the list + /// box contents have been scrolled another item may be at the top. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListBoxTopIndexDescr) + ] + public int TopIndex { + get { + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.LB_GETTOPINDEX, 0, 0)); + } + else { + return topIndex; + } + } + set { + if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETTOPINDEX, value, 0); + } + else { + topIndex = value; + } + } + } + + /// + /// + /// Enables a list box to recognize and expand tab characters when drawing + /// its strings. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListBoxUseTabStopsDescr) + ] + public bool UseTabStops { + get { + return useTabStops; + } + set { + if (useTabStops != value) { + useTabStops = value; + RecreateHandle(); + } + } + } + /// + /// + /// Allows to set the width of the tabs between the items in the list box. + /// The integer array should have the tab spaces in the ascending order. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ListBoxCustomTabOffsetsDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Browsable(false) + ] + public IntegerCollection CustomTabOffsets { + get { + if (customTabOffsets == null) { + customTabOffsets = new IntegerCollection(this); + } + return customTabOffsets; + } + } + + /// + /// + /// Performs the work of adding the specified items to the Listbox + /// + [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] + protected virtual void AddItemsCore(object[] value) { + int count = value == null? 0: value.Length; + if (count == 0) { + return; + } + + Items.AddRangeInternal(value); + } + + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// ListBox / CheckedListBox Onpaint. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_DRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWITEM, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] + public event MeasureItemEventHandler MeasureItem { + add { + Events.AddHandler(EVENT_MEASUREITEM, value); + } + remove { + Events.RemoveHandler(EVENT_MEASUREITEM, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + } + + /// + /// + /// While the preferred way to insert items is to set Items.All, + /// and set all the items at once, there are times when you may wish to + /// insert each item one at a time. To help with the performance of this, + /// it is desirable to prevent the ListBox from painting during these + /// operations. This method, along with EndUpdate, is the preferred + /// way of doing this. Don't forget to call EndUpdate when you're done, + /// or else the ListBox won't paint properly afterwards. + /// + public void BeginUpdate() { + BeginUpdateInternal(); + updateCount++; + } + + private void CheckIndex(int index) { + if (index < 0 || index >= Items.Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); + } + + private void CheckNoDataSource() { + if (DataSource != null) + throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual ObjectCollection CreateItemCollection() { + return new ObjectCollection(this); + } + + internal virtual int ComputeMaxItemWidth(int oldMax) { + // pass LayoutUtils the collection of strings + string[] strings = new string[this.Items.Count]; + + for (int i = 0; i < Items.Count; i ++) { + strings[i] = GetItemText(Items[i]); + } + + Size textSize = LayoutUtils.OldGetLargestStringSizeInCollection(Font, strings); + return Math.Max(oldMax, textSize.Width); + } + + /// + /// + /// Unselects all currently selected items. + /// + public void ClearSelected() { + + bool hadSelection = false; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + for (int x = 0; x < itemCount;x++) { + if (SelectedItems.GetSelected(x)) { + hadSelection = true; + SelectedItems.SetSelected(x, false); + if (IsHandleCreated) { + NativeSetSelected(x, false); + } + } + } + + if (hadSelection) { + OnSelectedIndexChanged(EventArgs.Empty); + } + } + + /// + /// + /// While the preferred way to insert items is to set Items.All, + /// and set all the items at once, there are times when you may wish to + /// insert each item one at a time. To help with the performance of this, + /// it is desirable to prevent the ListBox from painting during these + /// operations. This method, along with BeginUpdate, is the preferred + /// way of doing this. BeginUpdate should be called first, and this method + /// should be called when you want the control to start painting again. + /// + public void EndUpdate() { + EndUpdateInternal(); + --updateCount; + } + + /// + /// + /// Finds the first item in the list box that starts with the given string. + /// The search is not case sensitive. + /// + public int FindString(string s) { + return FindString(s, -1); + } + + /// + /// + /// Finds the first item after the given index which starts with the given + /// string. The search is not case sensitive. + /// + public int FindString(string s, int startIndex) { + if (s == null) return -1; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (itemCount == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemCount) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of LB_FINDSTRING. + // The managed version correctly handles Turkish I. + return FindStringInternal(s, Items, startIndex, false); + } + + /// + /// + /// Finds the first item in the list box that matches the given string. + /// The strings must match exactly, except for differences in casing. + /// + public int FindStringExact(string s) { + return FindStringExact(s, -1); + } + + /// + /// + /// Finds the first item after the given index that matches the given + /// string. The strings must match excatly, except for differences in + /// casing. + /// + public int FindStringExact(string s, int startIndex) { + if (s == null) return -1; + + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + if (itemCount == 0) { + return -1; + } + + // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. + if (startIndex < -1 || startIndex >= itemCount) { + throw new ArgumentOutOfRangeException("startIndex"); + } + + // Always use the managed FindStringInternal instead of LB_FINDSTRING. + // The managed version correctly handles Turkish I. + // + return FindStringInternal(s, Items, startIndex, true); + } + + /// + /// + /// Returns the height of the given item in a list box. The index parameter + /// is ignored if drawMode is not OwnerDrawVariable. + /// + public int GetItemHeight(int index) { + int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; + + // Note: index == 0 is OK even if the ListBox currently has + // no items. + // + if (index < 0 || (index > 0 && index >= itemCount)) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + if (drawMode != DrawMode.OwnerDrawVariable) index = 0; + + if (IsHandleCreated) { + int h = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETITEMHEIGHT, index, 0)); + if (h == -1) + throw new Win32Exception(); + return h; + } + + return itemHeight; + } + + /// + /// + /// Retrieves a Rectangle object which describes the bounding rectangle + /// around an item in the list. If the item in question is not visible, + /// the rectangle will be outside the visible portion of the control. + /// + public Rectangle GetItemRectangle(int index) { + CheckIndex(index); + NativeMethods.RECT rect = new NativeMethods.RECT(); + SendMessage(NativeMethods.LB_GETITEMRECT, index, ref rect); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// List box overrides GetScaledBounds to ensure we always scale the requested + /// height, not the current height. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + // update bounds' height to use the requested height, not the current height. These + // can be different if integral height is turned on. + bounds.Height = requestedHeight; + return base.GetScaledBounds(bounds, factor, specified); + } + + /// + /// + /// Tells you whether or not the item at the supplied index is selected + /// or not. + /// + public bool GetSelected(int index) { + CheckIndex(index); + return GetSelectedInternal(index); + } + + private bool GetSelectedInternal(int index) { + if (IsHandleCreated) { + int sel = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSEL, index, 0)); + if (sel == -1) { + throw new Win32Exception(); + } + return sel > 0; + } + else { + if (itemsCollection != null && SelectedItems.GetSelected(index)) { + return true; + } + return false; + } + } + + /// + /// + /// Retrieves the index of the item at the given coordinates. + /// + public int IndexFromPoint(Point p) { + return IndexFromPoint(p.X, p.Y); + } + + /// + /// + /// Retrieves the index of the item at the given coordinates. + /// + public int IndexFromPoint(int x, int y) { + //NT4 SP6A : SendMessage Fails. So First check whether the point is in Client Co-ordinates and then + //call Sendmessage. + // + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref r); + if (r.left <= x && x < r.right && r.top <= y && y < r.bottom) { + int index = unchecked( (int) (long)SendMessage(NativeMethods.LB_ITEMFROMPOINT, 0, unchecked( (int) (long)NativeMethods.Util.MAKELPARAM(x, y)))); + if (NativeMethods.Util.HIWORD(index) == 0) { + // Inside ListBox client area + return NativeMethods.Util.LOWORD(index); + } + } + + return NoMatches; + } + + /// + /// Adds the given item to the native combo box. This asserts if the handle hasn't been + /// created. + /// + private int NativeAdd(object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.LB_ADDSTRING, 0, GetItemText(item))); + + if (insertIndex == NativeMethods.LB_ERRSPACE) { + throw new OutOfMemoryException(); + } + + if (insertIndex == NativeMethods.LB_ERR) { + // On some platforms (e.g. Win98), the ListBox control + // appears to return LB_ERR if there are a large number (>32000) + // of items. It doesn't appear to set error codes appropriately, + // so we'll have to assume that LB_ERR corresponds to item + // overflow. + // + throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); + } + + return insertIndex; + } + + /// + /// Clears the contents of the combo box. + /// + private void NativeClear() { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + SendMessage(NativeMethods.LB_RESETCONTENT, 0, 0); + } + + /// + /// Get the text stored by the native control for the specified list item. + /// + internal string NativeGetItemText(int index) { + int len = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETTEXTLEN, index, 0)); + StringBuilder sb = new StringBuilder(len + 1); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETTEXT, index, sb); + return sb.ToString(); + } + + /// + /// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been + /// created or if the resulting insert index doesn't match the passed in index. + /// + private int NativeInsert(int index, object item) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + int insertIndex = unchecked( (int) (long)SendMessage(NativeMethods.LB_INSERTSTRING, index, GetItemText(item))); + + if (insertIndex == NativeMethods.LB_ERRSPACE) { + throw new OutOfMemoryException(); + } + + if (insertIndex == NativeMethods.LB_ERR) { + // On some platforms (e.g. Win98), the ListBox control + // appears to return LB_ERR if there are a large number (>32000) + // of items. It doesn't appear to set error codes appropriately, + // so we'll have to assume that LB_ERR corresponds to item + // overflow. + // + throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); + } + + Debug.Assert(insertIndex == index, "NativeListBox inserted at " + insertIndex + " not the requested index of " + index); + return insertIndex; + } + + /// + /// Removes the native item from the given index. + /// + private void NativeRemoveAt(int index) { + Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); + + bool selected = (unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSEL, (IntPtr)index, IntPtr.Zero)) > 0); + SendMessage(NativeMethods.LB_DELETESTRING, index, 0); + + //If the item currently selected is removed then we should fire a Selectionchanged event... + //as the next time selected index returns -1... + + if (selected) { + OnSelectedIndexChanged(EventArgs.Empty); + } + } + + /// + /// Sets the selection of the given index to the native window. This does not change + /// the collection; you must update the collection yourself. + /// + private void NativeSetSelected(int index, bool value) { + Debug.Assert(IsHandleCreated, "Should only call Native methods after the handle has been created"); + Debug.Assert(selectionMode != SelectionMode.None, "Guard against setting selection for None selection mode outside this code."); + + if (selectionMode == SelectionMode.One) { + SendMessage(NativeMethods.LB_SETCURSEL, (value ? index : -1), 0); + } + else { + SendMessage(NativeMethods.LB_SETSEL, value? -1: 0, index); + } + } + + /// + /// This is called by the SelectedObjectCollection in response to the first + /// query on that collection after we have called Dirty(). Dirty() is called + /// when we receive a LBN_SELCHANGE message. + /// + private void NativeUpdateSelection() { + Debug.Assert(IsHandleCreated, "Should only call native methods if handle is created"); + + // Clear the selection state. + // + int cnt = Items.Count; + for (int i = 0; i < cnt; i++) { + SelectedItems.SetSelected(i, false); + } + + int[] result = null; + + switch (selectionMode) { + + case SelectionMode.One: + int index = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0)); + if (index >= 0) result = new int[] {index}; + break; + + case SelectionMode.MultiSimple: + case SelectionMode.MultiExtended: + int count = unchecked( (int) (long)SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0)); + if (count > 0) { + result = new int[count]; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETSELITEMS, count, result); + } + break; + } + + // Now set the selected state on the appropriate items. + // + if (result != null) { + foreach(int i in result) { + SelectedItems.SetSelected(i, true); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnChangeUICues(UICuesEventArgs e) { + + // ListBox seems to get a bit confused when the UI cues change for the first + // time - it draws the focus rect when it shouldn't and vice-versa. So when + // the UI cues change, we just do an extra invalidate to get it into the + // right state. + // + Invalidate(); + + base.OnChangeUICues(e); + } + + /// + /// + /// Actually goes and fires the drawItem event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler yourself for this event]. They should, + /// however, remember to call base.onDrawItem(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// We need to know when the window handle has been created so we can + /// set up a few things, like column width, etc! Inheriting classes should + /// not forget to call base.OnHandleCreated(). + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + + //for getting the current Locale to set the Scrollbars... + // + SendMessage(NativeMethods.LB_SETLOCALE, CultureInfo.CurrentCulture.LCID, 0); + + if (columnWidth != 0) { + SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); + } + if (drawMode == DrawMode.OwnerDrawFixed) { + SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); + } + + if (topIndex != 0) { + SendMessage(NativeMethods.LB_SETTOPINDEX, topIndex, 0); + } + + if (UseCustomTabOffsets && CustomTabOffsets != null) { + int wpar = CustomTabOffsets.Count; + int[] offsets = new int[wpar]; + CustomTabOffsets.CopyTo(offsets, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); + } + + if (itemsCollection != null) { + + int count = itemsCollection.Count; + + for(int i = 0; i < count; i++) { + NativeAdd(itemsCollection[i]); + + if (selectionMode != SelectionMode.None) { + if (selectedItems != null) { + selectedItems.PushSelectionIntoNativeListBox(i); + } + } + } + } + if (selectedItems != null) { + if (selectedItems.Count > 0 && selectionMode == SelectionMode.One) { + SelectedItems.Dirty(); + SelectedItems.EnsureUpToDate(); + } + } + UpdateHorizontalExtent(); + } + + /// + /// + /// Overridden to make sure that we set up and clear out items + /// correctly. Inheriting controls should not forget to call + /// base.OnHandleDestroyed() + /// + protected override void OnHandleDestroyed(EventArgs e) { + SelectedItems.EnsureUpToDate(); + if (Disposing) { + itemsCollection = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnMeasureItem(MeasureItemEventArgs e) { + MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + + // Changing the font causes us to resize, always rounding down. + // Make sure we do this after base.OnPropertyChanged, which sends the WM_SETFONT message + + // Avoid the listbox and textbox behaviour in Collection editors + // + UpdateFontCache(); + } + + + /// + /// + /// We override this so we can re-create the handle if the parent has changed. + /// + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + //No need to RecreateHandle if we are removing the Listbox from controls collection... + //so check the parent before recreating the handle... + if (this.ParentInternal != null) { + RecreateHandle(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnResize(EventArgs e) { + + base.OnResize(e); + + // There are some repainting issues for RightToLeft - so invalidate when we resize. + // + if (RightToLeft == RightToLeft.Yes || this.HorizontalScrollbar) { + Invalidate(); + } + + } + + /// + /// + /// Actually goes and fires the selectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected override void OnSelectedIndexChanged(EventArgs e) { + base.OnSelectedIndexChanged(e); + + // set the position in the dataSource, if there is any + // we will only set the position in the currencyManager if it is different + // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) + // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls + // into the backEnd. We do not need to do that. + // + if (this.DataManager != null && DataManager.Position != SelectedIndex) { + //read this as "if everett or (whidbey and selindex is valid)" + if (!FormattingEnabled || this.SelectedIndex != -1) + { + // VSWhidbey 95176: don't change dataManager position if we simply unselected everything. + // (Doing so would cause the first LB item to be selected...) + this.DataManager.Position = this.SelectedIndex; + } + } + + // VSWhidbey 163411: Call the handler after updating the DataManager's position so that + // the DataManager's selected index will be correct in an event handler. + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; + if (handler != null) { + handler(this, e); + } + } + + /// + protected override void OnSelectedValueChanged(EventArgs e) { + base.OnSelectedValueChanged(e); + selectedValueChangedFired = true; + } + + /// + protected override void OnDataSourceChanged(EventArgs e) { + if (DataSource == null) + { + BeginUpdate(); + SelectedIndex = -1; + Items.ClearInternal(); + EndUpdate(); + } + base.OnDataSourceChanged(e); + RefreshItems(); + } + + /// + protected override void OnDisplayMemberChanged(EventArgs e) { + base.OnDisplayMemberChanged(e); + + // we want to use the new DisplayMember even if there is no data source + RefreshItems(); + + if (SelectionMode != SelectionMode.None && this.DataManager != null) + this.SelectedIndex = this.DataManager.Position; + } + + /// + /// + /// Forces the ListBox to invalidate and immediately + /// repaint itself and any children if OwnerDrawVariable. + /// + public override void Refresh() { + if (drawMode == DrawMode.OwnerDrawVariable) { + //Fire MeasureItem for Each Item in the Listbox... + int cnt = Items.Count; + Graphics graphics = CreateGraphicsInternal(); + + try + { + for (int i = 0; i < cnt; i++) { + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, i, ItemHeight); + OnMeasureItem(mie); + } + } + finally { + graphics.Dispose(); + } + + } + base.Refresh(); + } + /// + /// + /// Reparses the objects, getting new text strings for them. + /// + /// + protected override void RefreshItems() { + + // Store the currently selected object collection. + // + ObjectCollection savedItems = itemsCollection; + + // Clear the items. + // + itemsCollection = null; + selectedIndices = null; + + if (IsHandleCreated) { + NativeClear(); + } + + object[] newItems = null; + + // if we have a dataSource and a DisplayMember, then use it + // to populate the Items collection + // + if (this.DataManager != null && this.DataManager.Count != -1) { + newItems = new object[this.DataManager.Count]; + for(int i = 0; i < newItems.Length; i++) { + newItems[i] = this.DataManager[i]; + } + } + else if (savedItems != null) { + newItems = new object[savedItems.Count]; + savedItems.CopyTo(newItems, 0); + } + + // Store the current list of items + // + if (newItems != null) { + Items.AddRangeInternal(newItems); + } + + // Restore the selected indices if SelectionMode allows it. + // + if (SelectionMode != SelectionMode.None) { + if (this.DataManager != null) { + // put the selectedIndex in sync w/ the position in the dataManager + this.SelectedIndex = this.DataManager.Position; + } + else { + if (savedItems != null) { + int cnt = savedItems.Count; + for(int index = 0; index < cnt; index++) { + if (savedItems.InnerArray.GetState(index, SelectedObjectCollection.SelectedObjectMask)) { + SelectedItem = savedItems[index]; + } + } + } + } + } + + } + + /// + /// + /// Reparses the object at the given index, getting new text string for it. + /// + /// + protected override void RefreshItem(int index) { + Items.SetItemInternal(index, Items[index]); + } + + public override void ResetBackColor() { + base.ResetBackColor(); + } + + public override void ResetForeColor() { + base.ResetForeColor(); + } + + + private void ResetItemHeight() { + itemHeight = DefaultItemHeight; + } + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + if (factor.Width != 1F && factor.Height != 1F) { + UpdateFontCache(); + } + base.ScaleControl(factor, specified); + } + + + /// + /// + /// Overrides Control.SetBoundsCore to remember the requestedHeight. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + // Avoid the listbox and textbox behaviour in Collection editors + // + + + if (!integralHeightAdjust && height != Height) + requestedHeight = height; + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Performs the work of setting the specified items into the ListBox. + /// + protected override void SetItemsCore(IList value) { + BeginUpdate(); + Items.ClearInternal(); + Items.AddRangeInternal(value); + + this.SelectedItems.Dirty(); + + // if the list changed, we want to keep the same selected index + // CurrencyManager will provide the PositionChanged event + // it will be provided before changing the list though... + if (this.DataManager != null) { + if (this.DataSource is ICurrencyManagerProvider) { + // Everett ListControl's had a bug where they would not fire + // OnSelectedValueChanged if their list of items were refreshed. + // We fix this post-Everett. + // However, for APPCOMPAT reasons, we only want to fix it when binding to + // Whidbey components. + // vsw 547279. + this.selectedValueChangedFired = false; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.LB_SETCURSEL, DataManager.Position, 0); + } + + // if the list changed and we still did not fire the + // onselectedChanged event, then fire it now; + if (!selectedValueChangedFired) { + OnSelectedValueChanged(EventArgs.Empty); + selectedValueChangedFired = false; + } + } + EndUpdate(); + } + + /// + protected override void SetItemCore(int index, object value) { + Items.SetItemInternal(index, value); + } + + /// + /// + /// Allows the user to set an item as being selected or not. This should + /// only be used with ListBoxes that allow some sort of multi-selection. + /// + public void SetSelected(int index, bool value) { + int itemCount = (itemsCollection == null) ? 0: itemsCollection.Count; + if (index < 0 || index >= itemCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + if (selectionMode == SelectionMode.None) + throw new InvalidOperationException(SR.GetString(SR.ListBoxInvalidSelectionMode)); + + SelectedItems.SetSelected(index, value); + if (IsHandleCreated) { + NativeSetSelected(index, value); + } + SelectedItems.Dirty(); + OnSelectedIndexChanged(EventArgs.Empty); + } + + /// + /// + /// Sorts the items in the listbox. + /// + protected virtual void Sort() { + // This will force the collection to add each item back to itself + // if sorted is now true, then the add method will insert the item + // into the correct position + // + CheckNoDataSource(); + + SelectedObjectCollection currentSelections = SelectedItems; + currentSelections.EnsureUpToDate(); + + if (sorted && itemsCollection != null) { + itemsCollection.InnerArray.Sort(); + + // Now that we've sorted, update our handle + // if it has been created. + if (IsHandleCreated) { + NativeClear(); + int count = itemsCollection.Count; + for(int i = 0; i < count; i++) { + NativeAdd(itemsCollection[i]); + if (currentSelections.GetSelected(i)) { + NativeSetSelected(i, true); + } + } + } + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + if (itemsCollection != null) { + s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); + if (Items.Count > 0) { + string z = GetItemText(Items[0]); + string txt = (z.Length > 40) ? z.Substring(0, 40) : z; + s += ", Items[0]: " + txt; + } + } + return s; + } + private void UpdateFontCache() { + fontIsChanged = true; + integralHeightAdjust = true; + try { + Height = requestedHeight; + } + finally { + integralHeightAdjust = false; + } + maxWidth = -1; + UpdateHorizontalExtent(); + // clear the preferred size cache. + CommonProperties.xClearPreferredSizeCache(this); + + } + + private void UpdateHorizontalExtent() { + if (!multiColumn && horizontalScrollbar && IsHandleCreated) { + int width = horizontalExtent; + if (width == 0) { + width = MaxItemWidth; + } + SendMessage(NativeMethods.LB_SETHORIZONTALEXTENT, width, 0); + } + } + + // Updates the cached max item width + // + private void UpdateMaxItemWidth(object item, bool removing) { + + // We shouldn't be caching maxWidth if we don't have horizontal scrollbars, + // or horizontal extent has been set + // + if (!horizontalScrollbar || horizontalExtent > 0) { + maxWidth = -1; + return; + } + + // Only update if we are currently caching maxWidth + // + if (maxWidth > -1) { + + // Compute item width + // + int width; + using (Graphics graphics = CreateGraphicsInternal()) { + width = (int)(Math.Ceiling(graphics.MeasureString(GetItemText(item), this.Font).Width)); + } + + if (removing) { + // We're removing this item, so if it's the longest + // in the list, reset the cache + // + if (width >= maxWidth) { + maxWidth = -1; + } + } + else { + // We're adding or inserting this item - update the cache + // + if (width > maxWidth) { + maxWidth = width; + } + } + } + } + + // Updates the Custom TabOffsets + // + + private void UpdateCustomTabOffsets() { + if (IsHandleCreated && UseCustomTabOffsets && CustomTabOffsets != null) { + int wpar = CustomTabOffsets.Count; + int[] offsets = new int[wpar]; + CustomTabOffsets.CopyTo(offsets, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); + Invalidate(); + } + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + using (Pen pen = new Pen(VisualStyleInformation.TextControlBorder)) { + g.DrawRectangle(pen, rect); + } + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected virtual void WmReflectCommand(ref Message m) { + switch (NativeMethods.Util.HIWORD(m.WParam)) { + case NativeMethods.LBN_SELCHANGE: + if (selectedItems != null) { + selectedItems.Dirty(); + } + OnSelectedIndexChanged(EventArgs.Empty); + break; + case NativeMethods.LBN_DBLCLK: + // Handle this inside WM_LBUTTONDBLCLK + // OnDoubleClick(EventArgs.Empty); + break; + } + } + + /// + /// + /// + /// + private void WmReflectDrawItem(ref Message m) { + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + IntPtr dc = dis.hDC; + IntPtr oldPal = SetUpPalette(dc, false /*force*/, false /*realize*/); + try { + Graphics g = Graphics.FromHdcInternal(dc); + + try { + Rectangle bounds = Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); + + if (HorizontalScrollbar) { + if (MultiColumn) { + bounds.Width = Math.Max(ColumnWidth, bounds.Width); + } + else { + bounds.Width = Math.Max(MaxItemWidth, bounds.Width); + } + } + + + OnDrawItem(new DrawItemEventArgs(g, Font, bounds, dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); + } + finally { + g.Dispose(); + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); + } + } + m.Result = (IntPtr)1; + } + + /// + /// + /// + /// + // This method is only called if in owner draw mode + private void WmReflectMeasureItem(ref Message m) { + + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + if (drawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { + Graphics graphics = CreateGraphicsInternal(); + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); + try { + OnMeasureItem(mie); + mis.itemHeight = mie.ItemHeight; + } + finally { + graphics.Dispose(); + } + } + else { + mis.itemHeight = ItemHeight; + } + Marshal.StructureToPtr(mis, m.LParam, false); + m.Result = (IntPtr)1; + } + + /// + /// + /// The list's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the list continues to function properly. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmReflectDrawItem(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: + WmReflectMeasureItem(ref m); + break; + case NativeMethods.WM_PRINT: + WmPrint(ref m); + break; + case NativeMethods.WM_LBUTTONDOWN: + if (selectedItems != null) { + selectedItems.Dirty(); + } + base.WndProc(ref m); + break; + case NativeMethods.WM_LBUTTONUP: + // Get the mouse location + // + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point pt = new Point(x,y); + pt = PointToScreen(pt); + bool captured = Capture; + if (captured && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + + + if (!doubleClickFired && !ValidationCancelled) { + OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + else { + doubleClickFired = false; + // WM_COMMAND is only fired if the user double clicks an item, + // so we can't use that as a double-click substitute + if (!ValidationCancelled) { + OnDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + } + } + } + + // + // If this control has been disposed in the user's event handler, then we need to ignore the WM_LBUTTONUP + // message to avoid exceptions thrown as a result of handle re-creation (VSWhidbey#95150). + // We handle this situation here and not at the top of the window procedure since this is the only place + // where we can get disposed as an effect of external code (form.Close() for instance) and then pass the + // message to the base class. + // + if (GetState(STATE_DISPOSED)) + { + base.DefWndProc(ref m); + } + else + { + base.WndProc(ref m); + } + + doubleClickFired = false; + break; + + case NativeMethods.WM_RBUTTONUP: + // Get the mouse location + // + int rx = NativeMethods.Util.SignedLOWORD(m.LParam); + int ry = NativeMethods.Util.SignedHIWORD(m.LParam); + Point rpt = new Point(rx,ry); + rpt = PointToScreen(rpt); + bool rCaptured = Capture; + if (rCaptured && UnsafeNativeMethods.WindowFromPoint(rpt.X, rpt.Y) == Handle) { + if (selectedItems != null) { + selectedItems.Dirty(); + } + } + base.WndProc(ref m); + break; + + case NativeMethods.WM_LBUTTONDBLCLK: + //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... + //sequence for doubleclick... + //the first WM_LBUTTONUP, resets the flag for Doubleclick + //So its necessary for us to set it again... + doubleClickFired = true; + base.WndProc(ref m); + break; + + case NativeMethods.WM_WINDOWPOSCHANGED: + base.WndProc(ref m); + if (integralHeight && fontIsChanged) { + Height = Math.Max(Height,ItemHeight); + fontIsChanged = false; + } + break; + + default: + base.WndProc(ref m); + break; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ListBoxAccessibleObject(this); + } + else { + return base.CreateAccessibilityInstance(); + } + } + + /// + /// This is similar to ArrayList except that it also + /// mantains a bit-flag based state element for each item + /// in the array. + /// + /// The methods to enumerate, count and get data support + /// virtualized indexes. Indexes are virtualized according + /// to the state mask passed in. This allows ItemArray + /// to be the backing store for one read-write "master" + /// collection and serveral read-only collections based + /// on masks. ItemArray supports up to 31 masks. + /// + internal class ItemArray : IComparer { + + private static int lastMask = 1; + + private ListControl listControl; + private Entry[] entries; + private int count; + private int version; + + public ItemArray(ListControl listControl) { + this.listControl = listControl; + } + + /// + /// The version of this array. This number changes with each + /// change to the item list. + /// + public int Version { + get { + return version; + } + } + + /// + /// Adds the given item to the array. The state is initially + /// zero. + /// + public object Add(object item) { + EnsureSpace(1); + version++; + entries[count] = new Entry(item); + return entries[count++]; + } + + /// + /// Adds the given collection of items to the array. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void AddRange(ICollection items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + EnsureSpace(items.Count); + foreach(object i in items) { + entries[count++] = new Entry(i); + } + version++; + } + + /// + /// Clears this array. + /// + public void Clear() { + if (count > 0) { + Array.Clear(entries, 0, count); + } + + count = 0; + version++; + } + + /// + /// Allocates a new bitmask for use. + /// + public static int CreateMask() { + int mask = lastMask; + lastMask = lastMask << 1; + Debug.Assert(lastMask > mask, "We have overflowed our state mask."); + return mask; + } + + /// + /// Ensures that our internal array has space for + /// the requested # of elements. + /// + private void EnsureSpace(int elements) { + if (entries == null) { + entries = new Entry[Math.Max(elements, 4)]; + } + else if (count + elements >= entries.Length) { + int newLength = Math.Max(entries.Length * 2, entries.Length + elements); + Entry[] newEntries = new Entry[newLength]; + entries.CopyTo(newEntries, 0); + entries = newEntries; + } + } + + /// + /// Turns a virtual index into an actual index. + /// + public int GetActualIndex(int virtualIndex, int stateMask) { + if (stateMask == 0) { + return virtualIndex; + } + + // More complex; we must compute this index. + int calcIndex = -1; + for(int i = 0; i < count; i++) { + if ((entries[i].state & stateMask) != 0) { + calcIndex++; + if (calcIndex == virtualIndex) { + return i; + } + } + } + + return -1; + } + + /// + /// Gets the count of items matching the given mask. + /// + public int GetCount(int stateMask) { + // If mask is zero, then just give the main count + if (stateMask == 0) { + return count; + } + + // more complex: must provide a count of items + // based on a mask. + + int filteredCount = 0; + + for(int i = 0; i < count; i++) { + if ((entries[i].state & stateMask) != 0) { + filteredCount++; + } + } + + return filteredCount; + } + + /// + /// Retrieves an enumerator that will enumerate based on + /// the given mask. + /// + public IEnumerator GetEnumerator(int stateMask) { + return GetEnumerator(stateMask, false); + } + + /// + /// Retrieves an enumerator that will enumerate based on + /// the given mask. + /// + public IEnumerator GetEnumerator(int stateMask, bool anyBit) { + return new EntryEnumerator(this, stateMask, anyBit); + } + + /// + /// Gets the item at the given index. The index is + /// virtualized against the given mask value. + /// + public object GetItem(int virtualIndex, int stateMask) { + int actualIndex = GetActualIndex(virtualIndex, stateMask); + + if (actualIndex == -1) { + throw new IndexOutOfRangeException(); + } + + return entries[actualIndex].item; + } + /// + /// Gets the item at the given index. The index is + /// virtualized against the given mask value. + /// + internal object GetEntryObject(int virtualIndex, int stateMask) { + int actualIndex = GetActualIndex(virtualIndex, stateMask); + + if (actualIndex == -1) { + throw new IndexOutOfRangeException(); + } + + return entries[actualIndex]; + } + /// + /// Returns true if the requested state mask is set. + /// The index is the actual index to the array. + /// + public bool GetState(int index, int stateMask) { + return ((entries[index].state & stateMask) == stateMask); + } + + /// + /// Returns the virtual index of the item based on the + /// state mask. + /// + public int IndexOf(object item, int stateMask) { + + int virtualIndex = -1; + + for(int i = 0; i < count; i++) { + if (stateMask == 0 || (entries[i].state & stateMask) != 0) { + virtualIndex++; + if (entries[i].item.Equals(item)) { + return virtualIndex; + } + } + } + + return -1; + } + + /// + /// Returns the virtual index of the item based on the + /// state mask. Uses reference equality to identify the + /// given object in the list. + /// + public int IndexOfIdentifier(object identifier, int stateMask) { + int virtualIndex = -1; + + for(int i = 0; i < count; i++) { + if (stateMask == 0 || (entries[i].state & stateMask) != 0) { + virtualIndex++; + if (entries[i] == identifier) { + return virtualIndex; + } + } + } + + return -1; + } + + /// + /// Inserts item at the given index. The index + /// is not virtualized. + /// + public void Insert(int index, object item) { + EnsureSpace(1); + + if (index < count) { + System.Array.Copy(entries, index, entries, index + 1, count - index); + } + + entries[index] = new Entry(item); + count++; + version++; + } + + /// + /// Removes the given item from the array. If + /// the item is not in the array, this does nothing. + /// + public void Remove(object item) { + int index = IndexOf(item, 0); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// Removes the item at the given index. + /// + public void RemoveAt(int index) { + count--; + for (int i = index; i < count; i++) { + entries[i] = entries[i+1]; + } + entries[count] = null; + version++; + } + + /// + /// Sets the item at the given index to a new value. + /// + public void SetItem(int index, object item) { + entries[index].item = item; + } + + /// + /// Sets the state data for the given index. + /// + public void SetState(int index, int stateMask, bool value) { + if (value) { + entries[index].state |= stateMask; + } + else { + entries[index].state &= ~stateMask; + } + version++; + } + + /// + /// Find element in sorted array. If element is not found returns a binary complement of index for inserting + /// + public int BinarySearch(object element) + { + return Array.BinarySearch(entries, 0, count, element, this); + } + + + /// + /// Sorts our array. + /// + public void Sort() { + Array.Sort(entries, 0, count, this); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void Sort(Array externalArray) { + Array.Sort(externalArray, this); + } + + int IComparer.Compare(object item1, object item2) { + if (item1 == null) { + if (item2 == null) + return 0; //both null, then they are equal + + return -1; //item1 is null, but item2 is valid (greater) + } + if (item2 == null) + return 1; //item2 is null, so item 1 is greater + + if (item1 is Entry) { + item1 = ((Entry)item1).item; + } + + if (item2 is Entry) { + item2 = ((Entry)item2).item; + } + + String itemName1 = listControl.GetItemText(item1); + String itemName2 = listControl.GetItemText(item2); + + CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; + return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); + } + + /// + /// This is a single entry in our item array. + /// + private class Entry { + public object item; + public int state; + + public Entry(object item) { + this.item = item; + this.state = 0; + } + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class EntryEnumerator : IEnumerator { + private ItemArray items; + private bool anyBit; + private int state; + private int current; + private int version; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public EntryEnumerator(ItemArray items, int state, bool anyBit) { + this.items = items; + this.state = state; + this.anyBit = anyBit; + this.version = items.version; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); + + while(true) { + if (current < items.count - 1) { + current++; + if (anyBit) { + if ((items.entries[current].state & state) != 0) { + return true; + } + } + else { + if ((items.entries[current].state & state) == state) { + return true; + } + } + } + else { + current = items.count; + return false; + } + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items.entries[current].item; + } + } + } + } + + // Items + /// + /// + /// + /// A collection that stores objects. + /// + /// + [ListBindable(false)] + public class ObjectCollection : IList { + + private ListBox owner; + private ItemArray items; + + /// + /// + /// [To be supplied.] + /// + public ObjectCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// + /// Initializes a new instance of ListBox.ObjectCollection based on another ListBox.ObjectCollection. + /// + /// + public ObjectCollection(ListBox owner, ObjectCollection value) { + this.owner = owner; + this.AddRange(value); + } + + /// + /// + /// + /// Initializes a new instance of ListBox.ObjectCollection containing any array of objects. + /// + /// + public ObjectCollection(ListBox owner, object[] value) { + this.owner = owner; + this.AddRange(value); + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count { + get { + return InnerArray.GetCount(0); + } + } + + /// + /// Internal access to the actual data store. + /// + internal ItemArray InnerArray { + get { + if (items == null) { + items = new ItemArray(owner); + } + return items; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Adds an item to the List box. For an unsorted List box, the item is + /// added to the end of the existing list of items. For a sorted List box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + + public int Add(object item) + { + owner.CheckNoDataSource(); + int index = AddInternal(item); + owner.UpdateHorizontalExtent(); + return index; + } + + + private int AddInternal(object item) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + int index = -1; + if (!owner.sorted) + { + InnerArray.Add(item); + } + else + { + if (Count > 0) + { + index = InnerArray.BinarySearch(item); + if (index < 0) + { + index = ~index; // getting the index of the first element that is larger than the search value + //this index will be used for insert + } + } + else + index = 0; + + Debug.Assert(index >= 0 && index <= Count, "Wrong index for insert"); + InnerArray.Insert(index, item); + } + bool successful = false; + + try + { + if (owner.sorted) + { + if (owner.IsHandleCreated) + { + owner.NativeInsert(index, item); + owner.UpdateMaxItemWidth(item, false); + if (owner.selectedItems != null) + { + // VSWhidbey 95187: sorting may throw the LB contents and the selectedItem array out of synch. + owner.selectedItems.Dirty(); + } + } + } + else + { + index = Count - 1; + if (owner.IsHandleCreated) + { + owner.NativeAdd(item); + owner.UpdateMaxItemWidth(item, false); + } + } + successful = true; + } + finally + { + if (!successful) + { + InnerArray.Remove(item); + } + } + + return index; + } + + + /// + /// + int IList.Add(object item) { + return Add(item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ObjectCollection value) { + owner.CheckNoDataSource(); + AddRangeInternal((ICollection)value); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(object[] items) { + owner.CheckNoDataSource(); + AddRangeInternal((ICollection)items); + } + + internal void AddRangeInternal(ICollection items) { + + if (items == null) + { + throw new ArgumentNullException("items"); + } + owner.BeginUpdate(); + try + { + foreach (object item in items) + { + // adding items one-by-one for performance + // not using sort because after the array is sorted index of each newly added item will need to be found + // AddInternal is based on BinarySearch and finds index without any additional cost + AddInternal(item); + } + } + finally + { + owner.UpdateHorizontalExtent(); + owner.EndUpdate(); + } + } + + /// + /// + /// Retrieves the item with the specified index. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual object this[int index] { + get { + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + return InnerArray.GetItem(index, 0); + } + set { + owner.CheckNoDataSource(); + SetItemInternal(index, value); + } + } + + /// + /// + /// Removes all items from the ListBox. + /// + public virtual void Clear() { + owner.CheckNoDataSource(); + ClearInternal(); + } + + /// + /// Removes all items from the ListBox. Bypasses the data source check. + /// + internal void ClearInternal() { + + //update the width.. to reset Scrollbars.. + // Clear the selection state. + // + int cnt = owner.Items.Count; + for (int i = 0; i < cnt; i++) { + owner.UpdateMaxItemWidth(InnerArray.GetItem(i, 0), true); + } + + + if (owner.IsHandleCreated) { + owner.NativeClear(); + } + InnerArray.Clear(); + owner.maxWidth = -1; + owner.UpdateHorizontalExtent(); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object value) { + return IndexOf(value) != -1; + } + + /// + /// + /// Copies the ListBox Items collection to a destination array. + /// + public void CopyTo(object[] destination, int arrayIndex) { + int count = InnerArray.GetCount(0); + for(int i = 0; i < count; i++) { + destination[i + arrayIndex] = InnerArray.GetItem(i, 0); + } + } + + /// + /// + void ICollection.CopyTo(Array destination, int index) { + int count = InnerArray.GetCount(0); + for(int i = 0; i < count; i++) { + destination.SetValue(InnerArray.GetItem(i, 0), i + index); + } + } + + /// + /// + /// Returns an enumerator for the ListBox Items collection. + /// + public IEnumerator GetEnumerator() { + return InnerArray.GetEnumerator(0); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerArray.IndexOf(value,0); + } + + /// + /// + /// [To be supplied.] + /// + /// + internal int IndexOfIdentifier(object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + return InnerArray.IndexOfIdentifier(value,0); + } + + /// + /// + /// Adds an item to the combo box. For an unsorted combo box, the item is + /// added to the end of the existing list of items. For a sorted combo box, + /// the item is inserted into the list according to its sorted position. + /// The item's toString() method is called to obtain the string that is + /// displayed in the combo box. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public void Insert(int index, object item) { + owner.CheckNoDataSource(); + + if (index < 0 || index > InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (item == null) { + throw new ArgumentNullException("item"); + } + + // If the combo box is sorted, then nust treat this like an add + // because we are going to twiddle the index anyway. + // + if (owner.sorted) { + Add(item); + } + else { + InnerArray.Insert(index, item); + if (owner.IsHandleCreated) { + + bool successful = false; + + try { + owner.NativeInsert(index, item); + owner.UpdateMaxItemWidth(item, false); + successful = true; + } + finally { + if (!successful) { + InnerArray.RemoveAt(index); + } + } + } + } + owner.UpdateHorizontalExtent(); + } + + /// + /// + /// Removes the given item from the ListBox, provided that it is + /// actually in the list. + /// + public void Remove(object value) { + + int index = InnerArray.IndexOf(value, 0); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + /// Removes an item from the ListBox at the given index. + /// + public void RemoveAt(int index) { + owner.CheckNoDataSource(); + + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); + + // VSWhidbey 95181: Update InnerArray before calling NativeRemoveAt to ensure that when + // SelectedIndexChanged is raised (by NativeRemoveAt), InnerArray's state matches wrapped LB state. + InnerArray.RemoveAt(index); + + if (owner.IsHandleCreated) { + owner.NativeRemoveAt(index); + } + + owner.UpdateHorizontalExtent(); + } + + internal void SetItemInternal(int index, object value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + if (index < 0 || index >= InnerArray.GetCount(0)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); + InnerArray.SetItem(index, value); + + // If the native control has been created, and the display text of the new list item object + // is different to the current text in the native list item, recreate the native list item... + if (owner.IsHandleCreated) { + bool selected = (owner.SelectedIndex == index); + if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { + owner.NativeRemoveAt(index); + owner.SelectedItems.SetSelected(index, false); + owner.NativeInsert(index, value); + owner.UpdateMaxItemWidth(value, false); + if (selected) { + owner.SelectedIndex = index; + } + } + else { + // NEW - FOR COMPATIBILITY REASONS + // Minimum compatibility fix for VSWhidbey 377287 + if (selected) { + owner.OnSelectedIndexChanged(EventArgs.Empty); //will fire selectedvaluechanged + } + } + } + owner.UpdateHorizontalExtent(); + } + } // end ObjectCollection + + //****************************************************************************************** + // IntegerCollection + /// + /// + /// [To be supplied.] + /// + public class IntegerCollection : IList { + private ListBox owner; + private int[] innerArray; + private int count=0; + + /// + /// + /// [To be supplied.] + /// + public IntegerCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current selected items. + /// + [Browsable(false)] + public int Count { + get { + return count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + bool IList.IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int item) { + return IndexOf(item) != -1; + } + + /// + /// + bool IList.Contains(object item) { + if (item is Int32) { + return Contains((int)item); + } + else { + return false; + } + } + + public void Clear() + { + count = 0; + innerArray = null; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int item) { + int index = -1; + + if (innerArray != null) { + index = Array.IndexOf(innerArray, item); + + // We initialize innerArray with more elements than needed in the method EnsureSpace, + // and we don't actually remove element from innerArray in the method RemoveAt, + // so there maybe some elements which are not actually in innerArray will be found + // and we need to filter them out + if (index >= count) { + index = -1; + } + } + + return index; + } + + /// + /// + int IList.IndexOf(object item) { + if (item is Int32) { + return IndexOf((int)item); + } + else { + return -1; + } + } + + + /// + /// Add a unique integer to the collection in sorted order. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + private int AddInternal(int item) { + + EnsureSpace(1); + + int index = IndexOf(item); + if (index == -1) { + innerArray[count++] = item; + Array.Sort(innerArray,0,count); + index = IndexOf(item); + } + return index; + } + + /// + /// + /// Adds a unique integer to the collection in sorted order. + /// A SystemException occurs if there is insufficient space available to + /// store the new item. + /// + public int Add(int item) { + int index = AddInternal(item); + owner.UpdateCustomTabOffsets(); + + return index; + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. + // So we don't have to localize it. + ] + int IList.Add(object item) { + if (!(item is int)) { + throw new ArgumentException("item"); + } + return Add((int)item); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(int[] items) { + AddRangeInternal((ICollection)items); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(IntegerCollection value) { + AddRangeInternal((ICollection)value); + } + + /// + /// Add range that bypasses the data source check. + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. + // So we don't have to localize it. + ] + private void AddRangeInternal(ICollection items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + owner.BeginUpdate(); + try + { + EnsureSpace(items.Count); + foreach(object item in items) { + if (!(item is int)) { + throw new ArgumentException("item"); + } + else { + AddInternal((int)item); + } + } + owner.UpdateCustomTabOffsets(); + } + finally + { + owner.EndUpdate(); + } + } + + + /// + /// Ensures that our internal array has space for + /// the requested # of elements. + /// + private void EnsureSpace(int elements) { + if (innerArray == null) { + innerArray = new int[Math.Max(elements, 4)]; + } + else if (count + elements >= innerArray.Length) { + int newLength = Math.Max(innerArray.Length * 2, innerArray.Length + elements); + int[] newEntries = new int[newLength]; + innerArray.CopyTo(newEntries, 0); + innerArray = newEntries; + } + } + + /// + /// + void IList.Clear() { + Clear(); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxCantInsertIntoIntegerCollection)); + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "value" is the name of the param passed in. + // So we don't have to localize it. + ] + void IList.Remove(object value) { + if (!(value is int)) { + throw new ArgumentException("value"); + } + Remove((int)value); + } + + /// + /// + void IList.RemoveAt(int index) { + RemoveAt(index); + } + + /// + /// + /// Removes the given item from the array. If + /// the item is not in the array, this does nothing. + /// + public void Remove(int item) { + + int index = IndexOf(item); + + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + /// Removes the item at the given index. + /// + public void RemoveAt(int index) { + if (index < 0 || index >= count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + count--; + for (int i = index; i < count; i++) { + innerArray[i] = innerArray[i+1]; + } + } + + /// + /// + /// Retrieves the specified selected item. + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "index" is the name of the param passed in. + // So we don't have to localize it. + ] + public int this[int index] { + get { + return innerArray[index]; + } + [ + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. + // We can't change its text. + ] + set { + + if (index < 0 || index >= count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + innerArray[index] = (int)value; + owner.UpdateCustomTabOffsets(); + + + } + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + object IList.this[int index] { + get { + return this[index]; + } + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // "value" is the name of the param. + // So we don't have to localize it. + SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. + // We can't change its text. + ] + set { + if (!(value is int)) { + throw new ArgumentException("value"); + } + else { + this[index] = (int)value; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = Count; + for (int i = 0; i < cnt; i++) { + destination.SetValue(this[i], i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + IEnumerator IEnumerable.GetEnumerator() { + return new CustomTabOffsetsEnumerator(this); + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class CustomTabOffsetsEnumerator : IEnumerator { + private IntegerCollection items; + private int current; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public CustomTabOffsetsEnumerator(IntegerCollection items) { + this.items = items; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + + if (current < items.Count - 1) { + current++; + return true; + } + else { + current = items.Count; + return false; + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.Count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items[current]; + } + } + } + } + + //****************************************************************************************** + + // SelectedIndices + /// + /// + /// [To be supplied.] + /// + public class SelectedIndexCollection : IList { + private ListBox owner; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedIndexCollection(ListBox owner) { + this.owner = owner; + } + + /// + /// + /// Number of current selected items. + /// + [Browsable(false)] + public int Count { + get { + return owner.SelectedItems.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int selectedIndex) { + return IndexOf(selectedIndex) != -1; + } + + /// + /// + bool IList.Contains(object selectedIndex) { + if (selectedIndex is Int32) { + return Contains((int)selectedIndex); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int selectedIndex) { + + // Just what does this do? The selectedIndex parameter above is the index into the + // main object collection. We look at the state of that item, and if the state indicates + // that it is selected, we get back the virtualized index into this collection. Indexes on + // this collection match those on the SelectedObjectCollection. + if (selectedIndex >= 0 && + selectedIndex < InnerArray.GetCount(0) && + InnerArray.GetState(selectedIndex, SelectedObjectCollection.SelectedObjectMask)) { + + return InnerArray.IndexOf(InnerArray.GetItem(selectedIndex, 0), SelectedObjectCollection.SelectedObjectMask); + } + + return -1; + } + + /// + /// + int IList.IndexOf(object selectedIndex) { + if (selectedIndex is Int32) { + return IndexOf((int)selectedIndex); + } + else { + return -1; + } + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + + /// + /// + /// Retrieves the specified selected item. + /// + public int this[int index] { + get { + object identifier = InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); + return InnerArray.IndexOfIdentifier(identifier, 0); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); + } + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + owner.SelectedItems.EnsureUpToDate(); + return ((ObjectCollection)owner.Items).InnerArray; + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = Count; + for (int i = 0; i < cnt; i++) { + destination.SetValue(this[i], i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + if (owner != null) { + owner.ClearSelected(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Add(int index) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null) { + if (index != -1 && !Contains(index)) { + owner.SetSelected(index, true); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(int index) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null) { + if (index != -1 && Contains(index)) { + owner.SetSelected(index, false); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return new SelectedIndexEnumerator(this); + } + + /// + /// EntryEnumerator is an enumerator that will enumerate over + /// a given state mask. + /// + private class SelectedIndexEnumerator : IEnumerator { + private SelectedIndexCollection items; + private int current; + + /// + /// Creates a new enumerator that will enumerate over the given state. + /// + public SelectedIndexEnumerator(SelectedIndexCollection items) { + this.items = items; + this.current = -1; + } + + /// + /// Moves to the next element, or returns false if at the end. + /// + bool IEnumerator.MoveNext() { + + if (current < items.Count - 1) { + current++; + return true; + } + else { + current = items.Count; + return false; + } + } + + /// + /// Resets the enumeration back to the beginning. + /// + void IEnumerator.Reset() { + current = -1; + } + + /// + /// Retrieves the current value in the enumerator. + /// + object IEnumerator.Current { + get { + if (current == -1 || current == items.Count) { + throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); + } + + return items[current]; + } + } + } + } + + // Should be "ObjectCollection", except we already have one of those. + /// + /// + /// [To be supplied.] + /// + public class SelectedObjectCollection : IList { + + // This is the bitmask used within ItemArray to identify selected objects. + internal static int SelectedObjectMask = ItemArray.CreateMask(); + + private ListBox owner; + private bool stateDirty; + private int lastVersion; + private int count; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedObjectCollection(ListBox owner) { + this.owner = owner; + this.stateDirty = true; + this.lastVersion = -1; + } + + /// + /// + /// Number of current selected items. + /// + public int Count { + get { + if (owner.IsHandleCreated) { + SelectionMode current = (owner.selectionModeChanging) ? owner.cachedSelectionMode : owner.selectionMode; + switch (current) { + + case SelectionMode.None: + return 0; + + case SelectionMode.One: + int index = owner.SelectedIndex; + if (index >= 0) { + return 1; + } + return 0; + + case SelectionMode.MultiSimple: + case SelectionMode.MultiExtended: + return unchecked( (int) (long)owner.SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0)); + } + + return 0; + } + + // If the handle hasn't been created, we must do this the hard way. + // Getting the count when using a mask is expensive, so cache it. + // + if (lastVersion != InnerArray.Version) { + lastVersion = InnerArray.Version; + count = InnerArray.GetCount(SelectedObjectMask); + } + + return count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// Called by the list box to dirty the selected item state. + /// + internal void Dirty() { + stateDirty = true; + } + + /// + /// This is the item array that stores our data. We share this backing store + /// with the main object collection. + /// + private ItemArray InnerArray { + get { + EnsureUpToDate(); + return ((ObjectCollection)owner.Items).InnerArray; + } + } + + + /// + /// This is the function that Ensures that the selections are uptodate with + /// current listbox handle selections. + /// + internal void EnsureUpToDate() { + if (stateDirty) { + stateDirty = false; + if (owner.IsHandleCreated) { + owner.NativeUpdateSelection(); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(object selectedObject) { + return IndexOf(selectedObject) != -1; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(object selectedObject) { + return InnerArray.IndexOf(selectedObject, SelectedObjectMask); + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + + // A new internal method used in SelectedIndex getter... + // For a Multi select ListBox there can be two items with the same name ... + // and hence a object comparison is required... + // This method returns the "object" at the passed index rather than the "item" ... + // this "object" is then compared in the IndexOf( ) method of the itemsCollection. + // + /// + /// + internal object GetObjectAt(int index) { + return InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); + } + + /// + /// + /// Retrieves the specified selected item. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object this[int index] { + get { + return InnerArray.GetItem(index, SelectedObjectMask); + } + set { + throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array destination, int index) { + int cnt = InnerArray.GetCount(SelectedObjectMask); + for (int i = 0; i < cnt; i++) { + destination.SetValue(InnerArray.GetItem(i, SelectedObjectMask), i + index); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return InnerArray.GetEnumerator(SelectedObjectMask); + } + + /// + /// This method returns if the actual item index is selected. The index is the index to the MAIN + /// collection, not this one. + /// + internal bool GetSelected(int index) { + return InnerArray.GetState(index, SelectedObjectMask); + } + + // when SelectedObjectsCollection::ItemArray is accessed we push the selection from Native ListBox into our .Net ListBox - see EnsureUpToDate() + // when we create the handle we need to be able to do the opposite : push the selection from .Net ListBox into Native ListBox + internal void PushSelectionIntoNativeListBox(int index) { + // we can't use ItemArray accessor because this will wipe out our Selection collection + bool selected = ((ObjectCollection)owner.Items).InnerArray.GetState(index, SelectedObjectMask); + // push selection only if the item is actually selected + // this also takes care of the case where owner.SelectionMode == SelectionMode.One + if (selected) { + this.owner.NativeSetSelected(index, true /*we signal selection to the native listBox only if the item is actually selected*/); + } + } + + /// + /// Same thing for GetSelected. + /// + internal void SetSelected(int index, bool value) { + InnerArray.SetState(index, SelectedObjectMask, value); + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + if (owner != null) { + owner.ClearSelected(); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Add(object value) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null && value != null) { + int index = items.IndexOf(value); + if (index != -1 && !GetSelected(index)) { + owner.SelectedIndex = index; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(object value) { + if (owner != null) { + ObjectCollection items = owner.Items; + if (items != null & value != null) { + int index = items.IndexOf(value); + if (index != -1 && GetSelected(index)) { + owner.SetSelected(index, false); + } + } + } + } + } + + private sealed class ListBoxAccessibleObject : ControlAccessibleObject { + private readonly ListBox owner; + + public ListBoxAccessibleObject(ListBox control) : base(control) { + this.owner = control; + } + + #region IAccessibleEx related overrides + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override object GetObjectForChild(int childId) { + Accessibility.IAccessible systemIAccessible = this.GetSystemIAccessibleInternal(); + if (IsChildIdValid(childId, systemIAccessible)) { + if ((AccessibleRole)systemIAccessible.accRole[childId] == AccessibleRole.ListItem) { + return new ListBoxItemAccessibleObject(this.owner, childId); + } + } + + return base.GetObjectForChild(childId); + } + + #endregion + + private static bool IsChildIdValid(int childId, Accessibility.IAccessible systemIAccessible) { + // 0 (i.e. CHILDID_SELF) references the control itself, so it is considered "invalid" in this scope + return childId > 0 && childId <= systemIAccessible.accChildCount; + } + + private sealed class ListBoxItemAccessibleObject : AccessibleObject { + private readonly int childId; + private readonly ListBox owner; + + public ListBoxItemAccessibleObject(ListBox owner, int childId) { + Debug.Assert(owner != null, $"{nameof(owner)} should not be null"); + Debug.Assert(childId > 0, $"{nameof(childId)} has unexpected value"); + + this.owner = owner; + this.childId = childId; + } + + #region IAccessibleEx related overrides + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ScrollItemPatternId) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + #endregion + + #region IScrollItemProvider related overrides + + internal override void ScrollIntoView() { + if (this.owner.IsHandleCreated && IsChildIdValid(this.childId, this.owner.AccessibilityObject.GetSystemIAccessibleInternal())) { + this.owner.TopIndex = this.childId - 1; + } + } + + #endregion + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListControl.cs b/WindowsForms/Managed/System/WinForms/ListControl.cs new file mode 100644 index 000000000..c70cbdcaa --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListControl.cs @@ -0,0 +1,874 @@ +//------------------------------------------------------------------------------ + +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Globalization; + using System.Windows.Forms; + + using System.Drawing.Design; + using System.ComponentModel; + using System.Windows.Forms.ComponentModel; + + using System.Collections; + using System.Drawing; + using Microsoft.Win32; + using System.Text; + + /// + /// + /// [To be supplied.] + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + LookupBindingProperties("DataSource", "DisplayMember", "ValueMember", "SelectedValue") + ] + public abstract class ListControl : Control { + + private static readonly object EVENT_DATASOURCECHANGED = new object(); + private static readonly object EVENT_DISPLAYMEMBERCHANGED = new object(); + private static readonly object EVENT_VALUEMEMBERCHANGED = new object(); + private static readonly object EVENT_SELECTEDVALUECHANGED = new object(); + private static readonly object EVENT_FORMATINFOCHANGED = new object(); + private static readonly object EVENT_FORMATSTRINGCHANGED = new object(); + private static readonly object EVENT_FORMATTINGENABLEDCHANGED = new object(); + + private object dataSource; + private CurrencyManager dataManager; + private BindingMemberInfo displayMember; + private BindingMemberInfo valueMember; + + // Formatting stuff + private string formatString = String.Empty; + private IFormatProvider formatInfo = null; + private bool formattingEnabled = false; + private static readonly object EVENT_FORMAT = new object(); + private TypeConverter displayMemberConverter = null; + private static TypeConverter stringTypeConverter = null; + + private bool isDataSourceInitialized; + private bool isDataSourceInitEventHooked; + private bool inSetDataConnection = false; + + /// + /// + /// The ListSource to consume as this ListBox's source of data. + /// When set, a user can not modify the Items collection. + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + RefreshProperties(RefreshProperties.Repaint), + AttributeProvider(typeof(IListSource)), + SRDescription(SR.ListControlDataSourceDescr) + ] + public object DataSource { + get { + return dataSource; + } + set { + if (value != null && !(value is IList || value is IListSource)) + throw new ArgumentException(SR.GetString(SR.BadDataSourceForComplexBinding)); + if (dataSource == value) + return; + // When we change the dataSource to null, we should reset + // the displayMember to "". See ASURT 85662. + try { + SetDataConnection(value, displayMember, false); + } catch { + // There are several possibilities why setting the data source throws an exception: + // 1. the app throws an exception in the events that fire when we change the data source: DataSourceChanged, + // 2. we get an exception when we set the data source and populate the list controls (say,something went wrong while formatting the data) + // 3. the DisplayMember does not fit w/ the new data source (this could happen if the user resets the data source but did not reset the DisplayMember) + // in all cases ListControl should reset the DisplayMember to String.Empty + // the ListControl should also eat the exception - this is the RTM behavior and doing anything else is a breaking change + DisplayMember = ""; + } + if (value == null) + DisplayMember = ""; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnDataSourceChangedDescr)] + public event EventHandler DataSourceChanged { + add { + Events.AddHandler(EVENT_DATASOURCECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DATASOURCECHANGED, value); + } + } + + /// + protected CurrencyManager DataManager { + get { + return this.dataManager; + } + } + + /// + /// + /// If the ListBox contains objects that support properties, this indicates + /// which property of the object to show. If "", the object shows it's ToString(). + /// + [ + SRCategory(SR.CatData), + DefaultValue(""), + TypeConverterAttribute("System.Windows.Forms.Design.DataMemberFieldConverter, " + AssemblyRef.SystemDesign), + Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.ListControlDisplayMemberDescr) + ] + public string DisplayMember { + get { + return displayMember.BindingMember; + } + set { + BindingMemberInfo oldDisplayMember = displayMember; + try { + SetDataConnection(dataSource, new BindingMemberInfo(value), false); + } catch { + displayMember = oldDisplayMember; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnDisplayMemberChangedDescr)] + public event EventHandler DisplayMemberChanged { + add { + Events.AddHandler(EVENT_DISPLAYMEMBERCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_DISPLAYMEMBERCHANGED, value); + } + } + + // Cached type converter of the property associated with the display member + private TypeConverter DisplayMemberConverter { + get { + if (this.displayMemberConverter == null && + this.DataManager != null && + this.displayMember != null) { + PropertyDescriptorCollection props = this.DataManager.GetItemProperties(); + if (props != null) { + PropertyDescriptor displayMemberProperty = props.Find(this.displayMember.BindingField, true); + if (displayMemberProperty != null) + { + this.displayMemberConverter = displayMemberProperty.Converter; + } + } + } + return this.displayMemberConverter; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlFormatDescr)] + public event ListControlConvertEventHandler Format + { + add { + Events.AddHandler(EVENT_FORMAT, value); + RefreshItems(); + } + remove { + Events.RemoveHandler(EVENT_FORMAT, value); + RefreshItems(); + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(null) + ] + public IFormatProvider FormatInfo { + get { + return this.formatInfo; + } + set { + if (value != formatInfo) { + formatInfo = value; + RefreshItems(); + OnFormatInfoChanged(EventArgs.Empty); + } + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ListControlFormatInfoChangedDescr) + ] + public event EventHandler FormatInfoChanged { + add { + Events.AddHandler(EVENT_FORMATINFOCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMATINFOCHANGED, value); + } + } + + [ + DefaultValue(""), + SRDescription(SR.ListControlFormatStringDescr), + EditorAttribute("System.Windows.Forms.Design.FormatStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + MergableProperty(false) + ] + /// + public string FormatString { + get { + return formatString; + } + set { + if (value == null) + value = String.Empty; + if (!value.Equals(formatString)) { + formatString = value; + RefreshItems(); + OnFormatStringChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ListControlFormatStringChangedDescr) + ] + public event EventHandler FormatStringChanged { + add { + Events.AddHandler(EVENT_FORMATSTRINGCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMATSTRINGCHANGED, value); + } + } + + /// + [ + DefaultValue(false), + SRDescription(SR.ListControlFormattingEnabledDescr) + ] + public bool FormattingEnabled { + get { + return formattingEnabled; + } + set { + if (value != formattingEnabled) { + formattingEnabled = value; + RefreshItems(); + OnFormattingEnabledChanged(EventArgs.Empty); + } + } + } + + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ListControlFormattingEnabledChangedDescr) + ] + public event EventHandler FormattingEnabledChanged { + add { + Events.AddHandler(EVENT_FORMATTINGENABLEDCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_FORMATTINGENABLEDCHANGED, value); + } + } + + private bool BindingMemberInfoInDataManager(BindingMemberInfo bindingMemberInfo) { + if (dataManager == null) + return false; + PropertyDescriptorCollection props = dataManager.GetItemProperties(); + int propsCount = props.Count; + + for (int i = 0; i < propsCount; i++) { + if (typeof(IList).IsAssignableFrom(props[i].PropertyType)) + continue; + if (props[i].Name.Equals(bindingMemberInfo.BindingField)) { + return true; + } + } + + for (int i = 0; i < propsCount; i++) { + if (typeof(IList).IsAssignableFrom(props[i].PropertyType)) + continue; + if (String.Compare(props[i].Name, bindingMemberInfo.BindingField, true, CultureInfo.CurrentCulture) == 0) { + return true; + } + } + + return false; + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatData), + DefaultValue(""), + Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), + SRDescription(SR.ListControlValueMemberDescr) + ] + public string ValueMember { + get { + return valueMember.BindingMember; + } + set { + if (value == null) + value = ""; + BindingMemberInfo newValueMember = new BindingMemberInfo(value); + BindingMemberInfo oldValueMember = valueMember; + if (!newValueMember.Equals(valueMember)) { + // If the displayMember is set to the EmptyString, then recreate the dataConnection + // + if (DisplayMember.Length == 0) + SetDataConnection(DataSource, newValueMember, false); + // See if the valueMember is a member of + // the properties in the dataManager + if (this.dataManager != null && value != null && value.Length != 0) + if (!BindingMemberInfoInDataManager(newValueMember)) { + throw new ArgumentException(SR.GetString(SR.ListControlWrongValueMember), "value"); + } + + valueMember = newValueMember; + OnValueMemberChanged(EventArgs.Empty); + OnSelectedValueChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnValueMemberChangedDescr)] + public event EventHandler ValueMemberChanged { + add { + Events.AddHandler(EVENT_VALUEMEMBERCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_VALUEMEMBERCHANGED, value); + } + } + + /// + /// + /// Indicates whether list currently allows selection of list items. + /// + protected virtual bool AllowSelection { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public abstract int SelectedIndex { + get; + set; + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListControlSelectedValueDescr), + Bindable(true) + ] + public object SelectedValue { + get { + if (SelectedIndex != -1 && dataManager != null ) { + object currentItem = dataManager[SelectedIndex]; + object filteredItem = FilterItemOnProperty(currentItem, valueMember.BindingField); + return filteredItem; + } + return null; + } + set { + if (dataManager != null) { + string propertyName = valueMember.BindingField; + // we can't set the SelectedValue property when the listManager does not + // have a ValueMember set. + if (string.IsNullOrEmpty(propertyName)) + throw new InvalidOperationException(SR.GetString(SR.ListControlEmptyValueMemberInSettingSelectedValue)); + PropertyDescriptorCollection props = dataManager.GetItemProperties(); + PropertyDescriptor property = props.Find(propertyName, true); + int index = dataManager.Find(property, value, true); + this.SelectedIndex = index; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnSelectedValueChangedDescr)] + public event EventHandler SelectedValueChanged { + add { + Events.AddHandler(EVENT_SELECTEDVALUECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDVALUECHANGED, value); + } + } + + private void DataManager_PositionChanged(object sender, EventArgs e) { + if (this.dataManager != null) { + if (AllowSelection) { + this.SelectedIndex = dataManager.Position; + } + } + } + + private void DataManager_ItemChanged(object sender, ItemChangedEventArgs e) { + // Note this is being called internally with a null event. + if (dataManager != null) { + if (e.Index == -1) { + SetItemsCore(dataManager.List); + if (AllowSelection) { + this.SelectedIndex = this.dataManager.Position; + } + } + else { + SetItemCore(e.Index, dataManager[e.Index]); + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + protected object FilterItemOnProperty(object item) { + return FilterItemOnProperty(item, displayMember.BindingField); + } + + /// + /// + /// + /// [To be supplied.] + /// + protected object FilterItemOnProperty(object item, string field) { + if (item != null && field.Length > 0) { + try { + // if we have a dataSource, then use that to display the string + PropertyDescriptor descriptor; + if (this.dataManager != null) + descriptor = this.dataManager.GetItemProperties().Find(field, true); + else + descriptor = TypeDescriptor.GetProperties(item).Find(field, true); + if (descriptor != null) { + item = descriptor.GetValue(item); + } + } + catch { + } + } + return item; + } + + //We use this to prevent getting the selected item when mouse is hovering over the dropdown. + // + internal bool BindingFieldEmpty { + get { + return (displayMember.BindingField.Length > 0 ? false : true); + } + + } + + internal int FindStringInternal(string str, IList items, int startIndex, bool exact) { + return FindStringInternal(str, items, startIndex, exact, true); + } + + internal int FindStringInternal(string str, IList items, int startIndex, bool exact, bool ignorecase) { + + // Sanity check parameters + // + if (str == null || items == null) { + return -1; + } + // VSWhidbey 95158: The last item in the list is still a valid place to start looking! + if (startIndex < -1 || startIndex >= items.Count) { + return -1; + } + + bool found = false; + int length = str.Length; + + // VSWhidbey 215677: start from the start index and wrap around until we find the string + // in question. Use a separate counter to ensure that we arent cycling through the list infinitely. + int numberOfTimesThroughLoop = 0; + + // this API is really Find NEXT String... + for (int index = (startIndex+1) % items.Count; numberOfTimesThroughLoop < items.Count; index = (index+1) % items.Count) { + numberOfTimesThroughLoop++; + if (exact) { + found = String.Compare(str, GetItemText(items[index]), ignorecase, CultureInfo.CurrentCulture) == 0; + } + else { + found = String.Compare(str, 0, GetItemText(items[index]), 0, length, ignorecase, CultureInfo.CurrentCulture) == 0; + } + + if (found) { + return index; + } + + } + + return -1; + } + + /// + public string GetItemText(object item) + { + + // !formattingEnabled == RTM behaviour + if (!formattingEnabled) { + + // Microsoft gave his blessing to this RTM breaking change + if (item == null) { + return String.Empty; + } + + item = FilterItemOnProperty(item, displayMember.BindingField); + return (item != null) ? Convert.ToString(item, CultureInfo.CurrentCulture) : ""; + } + + // + // Whidbey formatting features + // + + object filteredItem = FilterItemOnProperty(item, displayMember.BindingField); + + // first try: the OnFormat event + ListControlConvertEventArgs e = new ListControlConvertEventArgs(filteredItem, typeof(String), item); + OnFormat(e); + + // Microsoft: we need a better check. Should add the Handled property on the ListControlConvertEventArgs? + if (e.Value != item && e.Value is String) { + return (string) e.Value; + } + + // try Formatter::FormatObject + if (stringTypeConverter == null) + { + stringTypeConverter = TypeDescriptor.GetConverter(typeof(String)); + } + try + { + return (string) Formatter.FormatObject(filteredItem, typeof(String), this.DisplayMemberConverter, stringTypeConverter, formatString, formatInfo, null, System.DBNull.Value); + } + catch (Exception exception) + { + if (ClientUtils.IsSecurityOrCriticalException(exception)) + { + throw; + } + // if we did not do any work then return the old ItemText + return (filteredItem != null) ? Convert.ToString(item, CultureInfo.CurrentCulture) : ""; + } + } + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBindingContextChanged(EventArgs e) { + SetDataConnection(dataSource, displayMember, true); + + base.OnBindingContextChanged(e); + } + + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnDataSourceChanged(EventArgs e) { + EventHandler eh = Events[EVENT_DATASOURCECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnDisplayMemberChanged(EventArgs e) { + EventHandler eh = Events[EVENT_DISPLAYMEMBERCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + protected virtual void OnFormat(ListControlConvertEventArgs e) { + ListControlConvertEventHandler eh = Events[EVENT_FORMAT] as ListControlConvertEventHandler; + if (eh != null) + eh(this, e); + } + + /// + protected virtual void OnFormatInfoChanged(EventArgs e) { + EventHandler eh = Events[EVENT_FORMATINFOCHANGED] as EventHandler; + if (eh != null) + eh(this,e); + } + + /// + protected virtual void OnFormatStringChanged(EventArgs e) { + EventHandler eh = Events[EVENT_FORMATSTRINGCHANGED] as EventHandler; + if (eh != null) + eh(this,e); + } + + /// + protected virtual void OnFormattingEnabledChanged(EventArgs e) { + EventHandler eh = Events[EVENT_FORMATTINGENABLEDCHANGED] as EventHandler; + if (eh != null) + eh(this,e); + } + + /// + /// + /// Actually goes and fires the selectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnSelectedIndexChanged(EventArgs e) { + OnSelectedValueChanged(EventArgs.Empty); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnValueMemberChanged(EventArgs e) { + EventHandler eh = Events[EVENT_VALUEMEMBERCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnSelectedValueChanged(EventArgs e) { + EventHandler eh = Events[EVENT_SELECTEDVALUECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected abstract void RefreshItem(int index); + + /// + /// + /// [To be supplied.] + /// + protected virtual void RefreshItems() { + } + + private void DataSourceDisposed(object sender, EventArgs e) { + Debug.Assert(sender == this.dataSource, "how can we get dispose notification for anything other than our dataSource?"); + SetDataConnection(null, new BindingMemberInfo(""), true); + } + + private void DataSourceInitialized(object sender, EventArgs e) { + ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification); + Debug.Assert(dsInit != null, "ListControl: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!"); + Debug.Assert(dsInit.IsInitialized, "ListControl: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing."); + SetDataConnection(this.dataSource, this.displayMember, true); + } + + private void SetDataConnection(object newDataSource, BindingMemberInfo newDisplayMember, bool force) { + bool dataSourceChanged = dataSource != newDataSource; + bool displayMemberChanged = !displayMember.Equals(newDisplayMember); + + // make sure something interesting is happening... + // + //force = force && (dataSource != null || newDataSource != null); + if (inSetDataConnection) { + return; + } + try { + if (force || dataSourceChanged || displayMemberChanged) { + inSetDataConnection = true; + IList currentList = this.DataManager != null ? this.DataManager.List : null; + bool currentManagerIsNull = this.DataManager == null; + + UnwireDataSource(); + + dataSource = newDataSource; + displayMember = newDisplayMember; + + WireDataSource(); + + // Provided the data source has been fully initialized, start listening to change events on its + // currency manager and refresh our list. If the data source has not yet been initialized, we will + // skip this step for now, and try again later (once the data source has fired its Initialized event). + // + if (isDataSourceInitialized) { + + CurrencyManager newDataManager = null; + if (newDataSource != null && BindingContext != null && !(newDataSource == Convert.DBNull)) { + newDataManager = (CurrencyManager)BindingContext[newDataSource, newDisplayMember.BindingPath]; + } + + if (dataManager != newDataManager) { + if (dataManager != null) { + dataManager.ItemChanged -= new ItemChangedEventHandler(DataManager_ItemChanged); + dataManager.PositionChanged -= new EventHandler(DataManager_PositionChanged); + } + + dataManager = newDataManager; + + if (dataManager != null) { + dataManager.ItemChanged += new ItemChangedEventHandler(DataManager_ItemChanged); + dataManager.PositionChanged += new EventHandler(DataManager_PositionChanged); + } + } + + // See if the BindingField in the newDisplayMember is valid + // The same thing if dataSource Changed + // "" is a good value for displayMember + if (dataManager != null && (displayMemberChanged || dataSourceChanged) && displayMember.BindingMember != null && displayMember.BindingMember.Length != 0) { + + if (!BindingMemberInfoInDataManager(displayMember)) + throw new ArgumentException(SR.GetString(SR.ListControlWrongDisplayMember), "newDisplayMember"); + } + + if (dataManager != null && (dataSourceChanged || displayMemberChanged || force)) { + // if we force a new data manager, then change the items in the list control + // only if the list changed or if we go from a null dataManager to a full fledged one + // or if the DisplayMember changed + if (displayMemberChanged || (force && (currentList != this.dataManager.List || currentManagerIsNull))) { + DataManager_ItemChanged(dataManager, new ItemChangedEventArgs(-1)); + } + } + } + this.displayMemberConverter = null; + } + + if (dataSourceChanged) { + OnDataSourceChanged(EventArgs.Empty); + } + + if (displayMemberChanged) { + OnDisplayMemberChanged(EventArgs.Empty); + } + } + finally { + inSetDataConnection = false; + } + } + + private void UnwireDataSource() { + // If the source is a component, then unhook the Disposed event + if (this.dataSource is IComponent) { + ((IComponent) this.dataSource).Disposed -= new EventHandler(DataSourceDisposed); + } + + ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification); + + if (dsInit != null && isDataSourceInitEventHooked) { + // If we previously hooked the data source's ISupportInitializeNotification + // Initialized event, then unhook it now (we don't always hook this event, + // only if we needed to because the data source was previously uninitialized) + dsInit.Initialized -= new EventHandler(DataSourceInitialized); + isDataSourceInitEventHooked = false; + } + } + + private void WireDataSource() { + // If the source is a component, then hook the Disposed event, + // so we know when the component is deleted from the form + if (this.dataSource is IComponent) { + ((IComponent) this.dataSource).Disposed += new EventHandler(DataSourceDisposed); + } + + ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification); + + if (dsInit != null && !dsInit.IsInitialized) { + // If the source provides initialization notification, and is not yet + // fully initialized, then hook the Initialized event, so that we can + // delay connecting to it until it *is* initialized. + dsInit.Initialized += new EventHandler(DataSourceInitialized); + isDataSourceInitEventHooked = true; + isDataSourceInitialized = false; + } + else { + // Otherwise either the data source says it *is* initialized, or it + // does not support the capability to report whether its initialized, + // in which case we have to just assume it that is initialized. + isDataSourceInitialized = true; + } + } + + /// + /// + /// [To be supplied.] + /// + protected abstract void SetItemsCore(IList items); + + /// + protected virtual void SetItemCore(int index, object value) {} + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListControlConvertEventArgs.cs b/WindowsForms/Managed/System/WinForms/ListControlConvertEventArgs.cs new file mode 100644 index 000000000..0986cc4ea --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListControlConvertEventArgs.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + + /// + public class ListControlConvertEventArgs : ConvertEventArgs { + object listItem; + /// + public ListControlConvertEventArgs(object value, Type desiredType, object listItem) : base(value, desiredType) { + this.listItem = listItem; + } + + /// + public object ListItem { + get { + return this.listItem; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListControlConvertEventHandler.cs b/WindowsForms/Managed/System/WinForms/ListControlConvertEventHandler.cs new file mode 100644 index 000000000..0e203ba4d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListControlConvertEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the ListControlConvert event of a ListView. + /// + /// + public delegate void ListControlConvertEventHandler(object sender, ListControlConvertEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ListItemConverter.cs b/WindowsForms/Managed/System/WinForms/ListItemConverter.cs new file mode 100644 index 000000000..ad886508a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListItemConverter.cs @@ -0,0 +1,234 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// ListViewItemConverter is a class that can be used to convert + /// ListViewItem objects from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class ListViewItemConverter : ExpandableObjectConverter { + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is ListViewItem) { + ListViewItem item = (ListViewItem)value; + ConstructorInfo ctor; + + // Should we use the subitem constructor? + // + for(int i=1; i < item.SubItems.Count; ++i) { + if (item.SubItems[i].CustomStyle) { + if (!String.IsNullOrEmpty(item.ImageKey)) { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { typeof(ListViewItem.ListViewSubItem[]), typeof(string)}); + if (ctor != null) { + ListViewItem.ListViewSubItem[] subItemArray = new ListViewItem.ListViewSubItem[item.SubItems.Count]; + ((ICollection)item.SubItems).CopyTo(subItemArray, 0); + return new InstanceDescriptor(ctor, new object[] {subItemArray, item.ImageKey}, false); + } + else { + break; + } + } else { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { typeof(ListViewItem.ListViewSubItem[]), typeof(int)}); + if (ctor != null) { + ListViewItem.ListViewSubItem[] subItemArray = new ListViewItem.ListViewSubItem[item.SubItems.Count]; + ((ICollection)item.SubItems).CopyTo(subItemArray, 0); + return new InstanceDescriptor(ctor, new object[] {subItemArray, item.ImageIndex}, false); + } + else { + break; + } + } + } + } + + // Convert SubItem array to string array + // + string[] subItems = new string[item.SubItems.Count]; + for(int i=0; i < subItems.Length; ++i) { + subItems[i] = item.SubItems[i].Text; + } + + // ForeColor, BackColor or ItemFont set + // + if (item.SubItems[0].CustomStyle) { + if (!String.IsNullOrEmpty(item.ImageKey)) { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string[]), + typeof(string), + typeof(Color), + typeof(Color), + typeof(Font)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { + subItems, + item.ImageKey, + item.SubItems[0].CustomForeColor ? item.ForeColor : Color.Empty, + item.SubItems[0].CustomBackColor ? item.BackColor : Color.Empty, + item.SubItems[0].CustomFont ? item.Font : null + }, false); + } + } else { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string[]), + typeof(int), + typeof(Color), + typeof(Color), + typeof(Font)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { + subItems, + item.ImageIndex, + item.SubItems[0].CustomForeColor ? item.ForeColor : Color.Empty, + item.SubItems[0].CustomBackColor ? item.BackColor : Color.Empty, + item.SubItems[0].CustomFont ? item.Font : null + }, false); + } + } + } + + // Text + // + if (item.ImageIndex == -1 && String.IsNullOrEmpty(item.ImageKey) && item.SubItems.Count <= 1) { + ctor = typeof(ListViewItem).GetConstructor(new Type[] {typeof(string)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {item.Text}, false); + } + } + + // Text and Image + // + if (item.SubItems.Count <= 1) { + if (!String.IsNullOrEmpty(item.ImageKey)) { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string), + typeof(string)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {item.Text, item.ImageKey}, false); + } + } else { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string), + typeof(int)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {item.Text, item.ImageIndex}, false); + } + } + } + + // Text, Image and SubItems + // + if (!String.IsNullOrEmpty(item.ImageKey)) { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string[]), + typeof(string)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {subItems, item.ImageKey}, false); + } + } else { + ctor = typeof(ListViewItem).GetConstructor(new Type[] { + typeof(string[]), + typeof(int)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {subItems, item.ImageIndex}, false); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + + internal class ListViewSubItemConverter : ExpandableObjectConverter { + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is ListViewItem.ListViewSubItem) { + ListViewItem.ListViewSubItem item = (ListViewItem.ListViewSubItem)value; + ConstructorInfo ctor; + + // Subitem has custom style + // + if (item.CustomStyle) { + ctor = typeof(ListViewItem.ListViewSubItem).GetConstructor(new Type[] { + typeof(ListViewItem), + typeof(string), + typeof(Color), + typeof(Color), + typeof(Font)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { + null, + item.Text, + item.ForeColor, + item.BackColor, + item.Font}, true); + } + } + + // Otherwise, just use the text constructor + // + ctor = typeof(ListViewItem.ListViewSubItem).GetConstructor(new Type[] {typeof(ListViewItem), typeof(string)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {null, item.Text}, true); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListManagerBindingsCollection.cs b/WindowsForms/Managed/System/WinForms/ListManagerBindingsCollection.cs new file mode 100644 index 000000000..9932c035c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListManagerBindingsCollection.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.ComponentModel; + using System.Collections; + + /// + /// + /// BindingsCollection is a collection of bindings for a Control. It has Add/Remove capabilities, + /// as well as an All array property, enumeration, etc. + /// + [DefaultEvent("CollectionChanged")] + internal class ListManagerBindingsCollection : BindingsCollection { + + private BindingManagerBase bindingManagerBase; + + /// + /// + /// ColumnsCollection constructor. Used only by DataSource. + /// + internal ListManagerBindingsCollection(BindingManagerBase bindingManagerBase) : base() { + Debug.Assert(bindingManagerBase != null, "How could a listmanagerbindingscollection not have a bindingManagerBase associated with it!"); + this.bindingManagerBase = bindingManagerBase; + } + + protected override void AddCore(Binding dataBinding) { + if (dataBinding == null) + throw new ArgumentNullException("dataBinding"); + if (dataBinding.BindingManagerBase == bindingManagerBase) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionAdd1), "dataBinding"); + if (dataBinding.BindingManagerBase != null) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionAdd2), "dataBinding"); + + // important to set prop first for error checking. + dataBinding.SetListManager(bindingManagerBase); + + base.AddCore(dataBinding); + } + + protected override void ClearCore() { + int numLinks = Count; + for (int i = 0; i < numLinks; i++) { + Binding dataBinding = this[i]; + dataBinding.SetListManager(null); + } + base.ClearCore(); + } + + protected override void RemoveCore(Binding dataBinding) { + if (dataBinding.BindingManagerBase != bindingManagerBase) + throw new ArgumentException(SR.GetString(SR.BindingsCollectionForeign)); + dataBinding.SetListManager(null); + base.RemoveCore(dataBinding); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListView.cs b/WindowsForms/Managed/System/WinForms/ListView.cs new file mode 100644 index 000000000..8dcaaa4a3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListView.cs @@ -0,0 +1,9250 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing.Design; + using System.Security.Permissions; + using System.Security; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.ComponentModel.Design; + using System.Collections; + using Microsoft.Win32; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// + /// Displays a list of items in one of four + /// views. Each item displays a caption and optionally an image. + /// + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Docking(DockingBehavior.Ask), + Designer("System.Windows.Forms.Design.ListViewDesigner, " + AssemblyRef.SystemDesign), + DefaultProperty("Items"), + DefaultEvent("SelectedIndexChanged"), + SRDescription(SR.DescriptionListView) + ] + public class ListView : Control { + + //members + private const int MASK_HITTESTFLAG = 0x00F7; + + private static readonly object EVENT_CACHEVIRTUALITEMS = new object(); + private static readonly object EVENT_COLUMNREORDERED = new object(); + private static readonly object EVENT_COLUMNWIDTHCHANGED = new object(); + private static readonly object EVENT_COLUMNWIDTHCHANGING = new object(); + private static readonly object EVENT_DRAWCOLUMNHEADER = new object(); + private static readonly object EVENT_DRAWITEM = new object(); + private static readonly object EVENT_DRAWSUBITEM = new object(); + private static readonly object EVENT_ITEMSELECTIONCHANGED = new object(); + private static readonly object EVENT_RETRIEVEVIRTUALITEM = new object(); + private static readonly object EVENT_SEARCHFORVIRTUALITEM = new object(); + private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); + private static readonly object EVENT_VIRTUALITEMSSELECTIONRANGECHANGED = new object(); + private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object(); + + + private ItemActivation activation = ItemActivation.Standard; + private ListViewAlignment alignStyle = ListViewAlignment.Top; + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + private ColumnHeaderStyle headerStyle = ColumnHeaderStyle.Clickable; + private SortOrder sorting = SortOrder.None; + private View viewStyle = System.Windows.Forms.View.LargeIcon; + private string toolTipCaption = String.Empty; + + + private const int LISTVIEWSTATE_ownerDraw = 0x00000001; + private const int LISTVIEWSTATE_allowColumnReorder = 0x00000002; + private const int LISTVIEWSTATE_autoArrange = 0x00000004; + private const int LISTVIEWSTATE_checkBoxes = 0x00000008; + private const int LISTVIEWSTATE_fullRowSelect = 0x00000010; + private const int LISTVIEWSTATE_gridLines = 0x00000020; + private const int LISTVIEWSTATE_hideSelection = 0x00000040; + private const int LISTVIEWSTATE_hotTracking = 0x00000080; + private const int LISTVIEWSTATE_labelEdit = 0x00000100; + private const int LISTVIEWSTATE_labelWrap = 0x00000200; + private const int LISTVIEWSTATE_multiSelect = 0x00000400; + private const int LISTVIEWSTATE_scrollable = 0x00000800; + private const int LISTVIEWSTATE_hoverSelection = 0x00001000; + private const int LISTVIEWSTATE_nonclickHdr = 0x00002000; + private const int LISTVIEWSTATE_inLabelEdit = 0x00004000; + private const int LISTVIEWSTATE_showItemToolTips = 0x00008000; + private const int LISTVIEWSTATE_backgroundImageTiled = 0x00010000; + private const int LISTVIEWSTATE_columnClicked = 0x00020000; + private const int LISTVIEWSTATE_doubleclickFired = 0x00040000; + private const int LISTVIEWSTATE_mouseUpFired = 0x00080000; + private const int LISTVIEWSTATE_expectingMouseUp = 0x00100000; + private const int LISTVIEWSTATE_comctlSupportsVisualStyles = 0x00200000; + private const int LISTVIEWSTATE_comctlSupportsVisualStylesTested = 0x00400000; + private const int LISTVIEWSTATE_showGroups = 0x00800000; + private const int LISTVIEWSTATE_handleDestroyed = 0x01000000; // while we are recreating the handle we want to know if we can still get data from the handle + private const int LISTVIEWSTATE_virtualMode = 0x02000000; + private const int LISTVIEWSTATE_headerControlTracking = 0x04000000; + private const int LISTVIEWSTATE_itemCollectionChangedInMouseDown = 0x08000000; + private const int LISTVIEWSTATE_flipViewToLargeIconAndSmallIcon = 0x10000000; + private const int LISTVIEWSTATE_headerDividerDblClick = 0x20000000; + private const int LISTVIEWSTATE_columnResizeCancelled = 0x40000000; + + private const int LISTVIEWSTATE1_insertingItemsNatively = 0x00000001; + private const int LISTVIEWSTATE1_cancelledColumnWidthChanging = 0x00000002; + private const int LISTVIEWSTATE1_disposingImageLists = 0x00000004; + private const int LISTVIEWSTATE1_useCompatibleStateImageBehavior = 0x00000008; + private const int LISTVIEWSTATE1_selectedIndexChangedSkipped = 0x00000010; + + private const int LVTOOLTIPTRACKING = 0x30; + private const int MAXTILECOLUMNS = 20; + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 listViewState; // see LISTVIEWSTATE_ consts above + private System.Collections.Specialized.BitVector32 listViewState1;// see LISTVIEWSTATE1_ consts above + + + + // Ownerdraw data caches... Only valid inside WM_PAINT. + // + + private Color odCacheForeColor = SystemColors.WindowText; + private Color odCacheBackColor = SystemColors.Window; + private Font odCacheFont = null; + private IntPtr odCacheFontHandle = IntPtr.Zero; + private FontHandleWrapper odCacheFontHandleWrapper = null; + + private ImageList imageListLarge; + private ImageList imageListSmall; + private ImageList imageListState; + + private MouseButtons downButton; + private int itemCount; + private int columnIndex = 0; + private int topIndex; // Needed for the HACK ALERT below. + private bool hoveredAlready = false; + + private bool rightToLeftLayout = false; + + + // member variables which are used for VirtualMode + private int virtualListSize = 0; + + private ListViewGroup defaultGroup = null; + + // Invariant: the table always contains all Items in the ListView, and maps IDs -> Items. + // listItemsArray is null if the handle is created; otherwise, it contains all Items. + // We do not try to sort listItemsArray as items are added, but during a handle recreate + // we will make sure we get the items in the same order the ListView displays them. + private Hashtable listItemsTable = new Hashtable(); // elements are ListViewItem's + private ArrayList listItemsArray = new ArrayList(); // elements are ListViewItem's + + private Size tileSize = Size.Empty; + + + // when we are in delayed update mode (that is when BeginUpdate has been called, we want to cache the items to + // add until EndUpdate is called. To do that, we push in an array list into our PropertyStore + // under this key. When Endupdate is fired, we process the items all at once. + // + private static readonly int PropDelayedUpdateItems = PropertyStore.CreateKey(); + + private int updateCounter = 0; // the counter we use to track how many BeginUpdate/EndUpdate calls there have been. + + private ColumnHeader[] columnHeaders; + private ListViewItemCollection listItemCollection; + private ColumnHeaderCollection columnHeaderCollection; + private CheckedIndexCollection checkedIndexCollection; + private CheckedListViewItemCollection checkedListViewItemCollection; + private SelectedListViewItemCollection selectedListViewItemCollection; + private SelectedIndexCollection selectedIndexCollection; + private ListViewGroupCollection groups; + private ListViewInsertionMark insertionMark; + private LabelEditEventHandler onAfterLabelEdit; + private LabelEditEventHandler onBeforeLabelEdit; + private ColumnClickEventHandler onColumnClick; + private EventHandler onItemActivate; + private ItemCheckedEventHandler onItemChecked; + private ItemDragEventHandler onItemDrag; + private ItemCheckEventHandler onItemCheck; + private ListViewItemMouseHoverEventHandler onItemMouseHover; + + // IDs for identifying ListViewItem's + private int nextID = 0; + + + // We save selected and checked items between handle creates. + private List savedSelectedItems; + private List savedCheckedItems; + + // Sorting + private IComparer listItemSorter = null; + + private ListViewItem prevHoveredItem = null; + + // Background image stuff + // Because we have to create a temporary file and the OS does not clean up the temporary files from the machine + // we have to do that ourselves + string backgroundImageFileName = string.Empty; + + // it *seems* that if the user changes the background image then the win32 listView will hang on to the previous + // background image until it gets the first WM_PAINT message - I use words like *seems* because nothing is guaranteed + // when it comes to win32 listView. + // so our wrapper has to hang on to the previousBackgroundImageFileNames and destroy them after it gets the first WM_PAINT message + // vsWhidbey 243708 + int bkImgFileNamesCount = -1; + string[] bkImgFileNames = null; + private const int BKIMGARRAYSIZE = 8; + + // If the user clicked on the column divider, the native ListView fires HDN_ITEMCHANGED on each mouse up event. + // This means that even if the user did not change the column width our wrapper will still think + // that the column header width changed. + // We need to make our ListView wrapper more robust in face of this limitation inside ComCtl ListView. + // columnHeaderClicked will be set in HDN_BEGINTRACK and reset in HDN_ITEMCHANGED. + ColumnHeader columnHeaderClicked; + int columnHeaderClickedWidth; + + // The user cancelled the column width changing event. + // We cache the NewWidth supplied by the user and use it on HDN_ENDTRACK to set the final column width. + int newWidthForColumnWidthChangingCancelled = -1; + + /// + /// + /// Creates an empty ListView with default styles. + /// + public ListView() : base() { + + int listViewStateFlags = LISTVIEWSTATE_scrollable | + LISTVIEWSTATE_multiSelect | + LISTVIEWSTATE_labelWrap | + LISTVIEWSTATE_autoArrange | + LISTVIEWSTATE_showGroups; + if (!AccessibilityImprovements.Level3) { + // Show grey rectangle around the selected list item if the list view is out of focus. + listViewStateFlags |= LISTVIEWSTATE_hideSelection; + } + + listViewState = new System.Collections.Specialized.BitVector32(listViewStateFlags); + + listViewState1 = new System.Collections.Specialized.BitVector32(LISTVIEWSTATE1_useCompatibleStateImageBehavior); + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.StandardClick, false); + SetStyle(ControlStyles.UseTextForAccessibility, false); + + odCacheFont = Font; + odCacheFontHandle = FontHandle; + SetBounds(0,0,121,97); + + listItemCollection = new ListViewItemCollection(new ListViewNativeItemCollection(this)); + columnHeaderCollection = new ColumnHeaderCollection(this); + } + + /// + /// + /// The activation style specifies what kind of user action is required to + /// activate an item. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(ItemActivation.Standard), + SRDescription(SR.ListViewActivationDescr) + ] + public ItemActivation Activation { + get { + return activation; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ItemActivation.Standard, (int)ItemActivation.TwoClick)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ItemActivation)); + } + + if (this.HotTracking && value != ItemActivation.OneClick) { + throw new ArgumentException(SR.GetString(SR.ListViewActivationMustBeOnWhenHotTrackingIsOn), "value"); + } + + if (activation != value) { + activation = value; + UpdateExtendedStyles(); + } + } + } + + /// + /// + /// The alignment style specifies which side of the window items are aligned + /// to by default + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(ListViewAlignment.Top), + Localizable(true), + SRDescription(SR.ListViewAlignmentDescr) + ] + public ListViewAlignment Alignment { + get { + return alignStyle; + } + + set { + // using this as ListViewAlignment has discontiguous values. + if (!ClientUtils.IsEnumValid_NotSequential(value, + (int)value, + (int)ListViewAlignment.Default, + (int)ListViewAlignment.Top, + (int)ListViewAlignment.Left, + (int)ListViewAlignment.SnapToGrid)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ListViewAlignment)); + } + if (alignStyle != value) { + alignStyle = value; + RecreateHandleInternal(); + } + } + } + + /// + /// + /// Specifies whether the user can drag column headers to + /// other column positions, thus changing the order of displayed columns. + /// This property is only meaningful in Details view. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewAllowColumnReorderDescr) + ] + public bool AllowColumnReorder { + get { + return listViewState[LISTVIEWSTATE_allowColumnReorder]; + } + + set { + if (AllowColumnReorder != value) { + listViewState[LISTVIEWSTATE_allowColumnReorder] = value; + UpdateExtendedStyles(); + } + } + } + + /// + /// + /// If AutoArrange is true items are automatically arranged according to + /// the alignment property. Items are also kept snapped to grid. + /// This property is only meaningful in Large Icon or Small Icon views. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListViewAutoArrangeDescr) + ] + public bool AutoArrange { + get { + return listViewState[LISTVIEWSTATE_autoArrange]; + } + + set { + if (AutoArrange != value) { + listViewState[LISTVIEWSTATE_autoArrange] = value; + UpdateStyles(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.ListViewBackgroundImageTiledDescr) + ] + public bool BackgroundImageTiled { + get { + return listViewState[LISTVIEWSTATE_backgroundImageTiled]; + } + set { + if (BackgroundImageTiled != value) { + listViewState[LISTVIEWSTATE_backgroundImageTiled] = value; + if (IsHandleCreated && BackgroundImage != null) { + // Don't call SetBackgroundImage because SetBackgroundImage deletes the existing image + // We don't need to delete it and this causes BAD problems w/ the Win32 list view control. + NativeMethods.LVBKIMAGE lvbkImage = new NativeMethods.LVBKIMAGE(); + lvbkImage.xOffset = 0; + lvbkImage.yOffset = 0; + + if (BackgroundImageTiled) + lvbkImage.ulFlags = NativeMethods.LVBKIF_STYLE_TILE; + else + lvbkImage.ulFlags = NativeMethods.LVBKIF_STYLE_NORMAL; + + lvbkImage.ulFlags |= NativeMethods.LVBKIF_SOURCE_URL; + lvbkImage.pszImage = this.backgroundImageFileName; + lvbkImage.cchImageMax = this.backgroundImageFileName.Length + 1; + + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SETBKIMAGE, 0, lvbkImage); + } + } + } + } + + /// + /// + /// Describes the border style of the window. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.borderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// If CheckBoxes is true, every item will display a checkbox next + /// to it. The user can change the state of the item by clicking the checkbox. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.ListViewCheckBoxesDescr) + ] + public bool CheckBoxes { + get { + return listViewState[LISTVIEWSTATE_checkBoxes]; + } + + set { + if (this.UseCompatibleStateImageBehavior) { + if (CheckBoxes != value) { + + if (value && this.View == View.Tile) { + throw new NotSupportedException(SR.GetString(SR.ListViewCheckBoxesNotSupportedInTileView)); + } + + if (CheckBoxes) { + // Save away the checked items just in case we re-activate checkboxes + // + savedCheckedItems = new List(CheckedItems.Count); + ListViewItem[] items = new ListViewItem[CheckedItems.Count]; + CheckedItems.CopyTo(items, 0); + for (int i = 0; i < items.Length; i ++) { + savedCheckedItems.Add(items[i]); + } + } + + listViewState[LISTVIEWSTATE_checkBoxes] = value; + UpdateExtendedStyles(); + + if (CheckBoxes && savedCheckedItems != null) { + // Check the saved checked items. + // + if (savedCheckedItems.Count > 0) { + foreach(ListViewItem item in savedCheckedItems) { + item.Checked = true; + } + } + savedCheckedItems = null; + } + + // Comctl should handle auto-arrange for us, but doesn't + if (AutoArrange) + ArrangeIcons(Alignment); + } + } else { + if (CheckBoxes != value) { + + if (value && this.View == View.Tile) { + throw new NotSupportedException(SR.GetString(SR.ListViewCheckBoxesNotSupportedInTileView)); + } + + if (CheckBoxes) { + // Save away the checked items just in case we re-activate checkboxes + // + savedCheckedItems = new List(CheckedItems.Count); + ListViewItem[] items = new ListViewItem[CheckedItems.Count]; + CheckedItems.CopyTo(items, 0); + for (int i = 0; i < items.Length; i ++) { + savedCheckedItems.Add(items[i]); + } + } + + listViewState[LISTVIEWSTATE_checkBoxes] = value; + + if ((!value && this.StateImageList != null && this.IsHandleCreated) || + (!value && this.Alignment == ListViewAlignment.Left && this.IsHandleCreated) || + (value && this.View == View.List && this.IsHandleCreated) || + (value && (this.View == View.SmallIcon || this.View == View.LargeIcon) && this.IsHandleCreated)) { + // we have to recreate the handle when we are going from CheckBoxes == true to CheckBoxes == false + // if we want to have the bitmaps from the StateImageList on the items. + + /** + *** there are a LOT of bugs w/ setting CheckBoxes to TRUE when in View.List, View.SmallIcon or View.LargeIcon: + *** vsWhidbey 168958, 309997, 304288, 156370 + *** + *** these bugs are caused by the fact that the win32 ListView control does not resize its column width + *** when CheckBoxes changes from FALSE to TRUE. + *** we need to recreate the handle when we set CheckBoxes to TRUE + **/ + RecreateHandleInternal(); + } else { + UpdateExtendedStyles(); + + } + + if (CheckBoxes && savedCheckedItems != null) { + // Check the saved checked items. + // + if (savedCheckedItems.Count > 0) { + foreach(ListViewItem item in savedCheckedItems) { + item.Checked = true; + } + } + savedCheckedItems = null; + } + + // Setting the LVS_CHECKBOXES window style also causes the ListView to display the default checkbox + // images rather than the user specified StateImageList. We send a LVM_SETIMAGELIST to restore the + // user's images. + if (IsHandleCreated && imageListState != null) { + if (CheckBoxes) { // we want custom checkboxes + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, imageListState.Handle); + } else { + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, IntPtr.Zero); + } + } + + // Comctl should handle auto-arrange for us, but doesn't + if (AutoArrange) { + ArrangeIcons(Alignment); + } + + } + } + } + } + + /// + /// + /// The indices of the currently checked list items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public CheckedIndexCollection CheckedIndices { + get { + if (checkedIndexCollection == null) { + checkedIndexCollection = new CheckedIndexCollection(this); + } + return checkedIndexCollection; + } + } + + /// + /// + /// The currently checked list items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public CheckedListViewItemCollection CheckedItems { + get { + if (checkedListViewItemCollection == null) { + checkedListViewItemCollection = new CheckedListViewItemCollection(this); + } + return checkedListViewItemCollection; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Editor("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + AssemblyRef.SystemDesign,typeof(UITypeEditor)), + SRDescription(SR.ListViewColumnsDescr), + Localizable(true), + MergableProperty(false) + ] + public ColumnHeaderCollection Columns { + get { + return columnHeaderCollection; + } + } + + /// + /// Actually we are using this to indicate whether ComCtl supports + /// the new listview features. This is true for ComCtl 6 and above, same as + /// the versions that support visual styles. + /// + private bool ComctlSupportsVisualStyles { + get { + if(!listViewState[LISTVIEWSTATE_comctlSupportsVisualStylesTested]) { + listViewState[LISTVIEWSTATE_comctlSupportsVisualStylesTested] = true; + listViewState[LISTVIEWSTATE_comctlSupportsVisualStyles] = Application.ComCtlSupportsVisualStyles; + } + return listViewState[LISTVIEWSTATE_comctlSupportsVisualStyles]; + } + } + + /// + /// + /// Computes the handle creation parameters for the ListView control. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + cp.ClassName = NativeMethods.WC_LISTVIEW; + + // Keep the scrollbar if we are just updating styles... + // + if (IsHandleCreated) { + int currentStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + cp.Style |= (currentStyle & (NativeMethods.WS_HSCROLL | NativeMethods.WS_VSCROLL)); + } + + cp.Style |= NativeMethods.LVS_SHAREIMAGELISTS; + + switch (alignStyle) { + case ListViewAlignment.Top: + cp.Style |= NativeMethods.LVS_ALIGNTOP; + break; + case ListViewAlignment.Left: + cp.Style |= NativeMethods.LVS_ALIGNLEFT; + break; + } + + if (AutoArrange) + cp.Style |= NativeMethods.LVS_AUTOARRANGE; + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + switch (headerStyle) { + case ColumnHeaderStyle.None: + cp.Style |= NativeMethods.LVS_NOCOLUMNHEADER; + break; + case ColumnHeaderStyle.Nonclickable: + cp.Style |= NativeMethods.LVS_NOSORTHEADER; + break; + } + + if (LabelEdit) + cp.Style |= NativeMethods.LVS_EDITLABELS; + + if (!LabelWrap) + cp.Style |= NativeMethods.LVS_NOLABELWRAP; + + if (!HideSelection) + cp.Style |= NativeMethods.LVS_SHOWSELALWAYS; + + if (!MultiSelect) + cp.Style |= NativeMethods.LVS_SINGLESEL; + + if (listItemSorter == null) { + switch (sorting) { + case SortOrder.Ascending: + cp.Style |= NativeMethods.LVS_SORTASCENDING; + break; + case SortOrder.Descending: + cp.Style |= NativeMethods.LVS_SORTDESCENDING; + break; + } + } + + if (VirtualMode) + cp.Style |= NativeMethods.LVS_OWNERDATA; + + // We can do this 'cuz the viewStyle enums are the same values as the actual LVS styles + // this new check since the value for LV_VIEW_TILE == LVS_SINGLESEL; so dont OR that value since + // LV_VIEW_TILE is not a STYLE but should be Send via a SENDMESSAGE. + if (viewStyle != View.Tile ) { + cp.Style |= (int)viewStyle; + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + internal ListViewGroup DefaultGroup { + get + { + if (defaultGroup == null) { + defaultGroup = new ListViewGroup(SR.GetString(SR.ListViewGroupDefaultGroup, "1")); + } + return defaultGroup; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize + { + get + { + return new Size(121, 97); + } + } + + /// + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + if (this.DoubleBuffered != value) + { + base.DoubleBuffered = value; + UpdateExtendedStyles(); + } + } + } + + internal bool ExpectingMouseUp { + get { + return this.listViewState[LISTVIEWSTATE_expectingMouseUp]; + } + } + + /// + /// + /// Retreives the item which currently has the user focus. This is the + /// item that's drawn with the dotted focus rectangle around it. + /// Returns null if no item is currently focused. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewFocusedItemDescr) + ] + public ListViewItem FocusedItem { + get { + if (IsHandleCreated) { + int displayIndex = unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETNEXTITEM, -1, NativeMethods.LVNI_FOCUSED)); + if (displayIndex > -1) + return Items[displayIndex]; + } + return null; + } + set { + if (IsHandleCreated && value != null) { + value.Focused = true; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override Color ForeColor + { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + } + } + } + + private bool FlipViewToLargeIconAndSmallIcon { + get { + // it never hurts to check that our house is in order + Debug.Assert(!this.listViewState[LISTVIEWSTATE_flipViewToLargeIconAndSmallIcon] || this.View == View.SmallIcon, "we need this bit only in SmallIcon view"); + Debug.Assert(!this.listViewState[LISTVIEWSTATE_flipViewToLargeIconAndSmallIcon] || this.ComctlSupportsVisualStyles, "we need this bit only when loading ComCtl 6.0"); + + return this.listViewState[LISTVIEWSTATE_flipViewToLargeIconAndSmallIcon]; + } + set { + // it never hurts to check that our house is in order + Debug.Assert(!value || this.View == View.SmallIcon, "we need this bit only in SmallIcon view"); + Debug.Assert(!value || this.ComctlSupportsVisualStyles, "we need this bit only when loading ComCtl 6.0"); + + this.listViewState[LISTVIEWSTATE_flipViewToLargeIconAndSmallIcon] = value; + } + } + + /// + /// + /// Specifies whether a click on an item will select the entire row instead + /// of just the item itself. + /// This property is only meaningful in Details view + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.ListViewFullRowSelectDescr) + ] + public bool FullRowSelect { + get { + return listViewState[LISTVIEWSTATE_fullRowSelect]; + } + set { + if (FullRowSelect != value) { + listViewState[LISTVIEWSTATE_fullRowSelect] = value; + UpdateExtendedStyles(); + } + } + } + + /// + /// + /// If true, draws grid lines between items and subItems. + /// This property is only meaningful in Details view + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.ListViewGridLinesDescr) + ] + public bool GridLines { + get { + return listViewState[LISTVIEWSTATE_gridLines]; + } + + set { + if (GridLines != value) { + listViewState[LISTVIEWSTATE_gridLines] = value; + UpdateExtendedStyles(); + } + } + } + + /// + /// + /// The collection of groups belonging to this ListView + /// + [ + SRCategory(SR.CatBehavior), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + Editor("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + AssemblyRef.SystemDesign,typeof(UITypeEditor)), + SRDescription(SR.ListViewGroupsDescr), + MergableProperty(false) + ] + public ListViewGroupCollection Groups { + get + { + if (groups == null) { + groups = new ListViewGroupCollection(this); + } + return groups; + } + } + + // this essentially means that the version of CommCtl supports list view grouping + // and that the user wants to make use of list view groups + internal bool GroupsEnabled + { + get { + return this.ShowGroups && groups != null && groups.Count > 0 && ComctlSupportsVisualStyles && !VirtualMode; + } + } + + /// + /// + /// Column headers can either be invisible, clickable, or non-clickable. + /// This property is only meaningful in Details view + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(ColumnHeaderStyle.Clickable), + SRDescription(SR.ListViewHeaderStyleDescr) + ] + public ColumnHeaderStyle HeaderStyle { + get { return headerStyle;} + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ColumnHeaderStyle.None, (int)ColumnHeaderStyle.Clickable)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ColumnHeaderStyle)); + } + if (headerStyle != value) { + // We can switch between NONE and either *one* of the other styles without + // recreating the handle, but if we change from CLICKABLE to NONCLICKABLE + // or vice versa, with or without an intervening setting of NONE, then + // the handle needs to be recreated. + headerStyle = value; + if ((listViewState[LISTVIEWSTATE_nonclickHdr] && value == ColumnHeaderStyle.Clickable) || + (!listViewState[LISTVIEWSTATE_nonclickHdr] && value == ColumnHeaderStyle.Nonclickable)) { + listViewState[LISTVIEWSTATE_nonclickHdr] = !listViewState[LISTVIEWSTATE_nonclickHdr]; + RecreateHandleInternal(); + } + else + UpdateStyles(); + } + } + } + + /// + /// + /// If false, selected items will still be highlighted (in a + /// different color) when focus is moved away from the ListView. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListViewHideSelectionDescr) + ] + public bool HideSelection { + get { + return listViewState[LISTVIEWSTATE_hideSelection]; + } + + set { + if (HideSelection != value) { + listViewState[LISTVIEWSTATE_hideSelection] = value; + UpdateStyles(); + } + } + } + + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewHotTrackingDescr) + ] + public bool HotTracking { + get { + return listViewState[LISTVIEWSTATE_hotTracking]; + } + set { + if (HotTracking != value) { + + listViewState[LISTVIEWSTATE_hotTracking] = value; + if (value) { + this.HoverSelection = true; + this.Activation = ItemActivation.OneClick; + } + UpdateExtendedStyles(); + } + } + } + + /// + /// + /// Determines whether items can be selected by hovering over them with the mouse. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewHoverSelectDescr) + ] + public bool HoverSelection { + get { + return listViewState[LISTVIEWSTATE_hoverSelection]; + } + + set { + if (HoverSelection != value) { + if (HotTracking && !value) { + throw new ArgumentException(SR.GetString(SR.ListViewHoverMustBeOnWhenHotTrackingIsOn), "value"); + } + + listViewState[LISTVIEWSTATE_hoverSelection] = value; + UpdateExtendedStyles(); + } + } + } + + internal bool InsertingItemsNatively + { + get + { + return this.listViewState1[LISTVIEWSTATE1_insertingItemsNatively]; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewInsertionMarkDescr) + ] + public ListViewInsertionMark InsertionMark { + get + { + if(insertionMark == null) { + insertionMark = new ListViewInsertionMark(this); + } + return insertionMark; + } + } + + private bool ItemCollectionChangedInMouseDown { + get { + return this.listViewState[LISTVIEWSTATE_itemCollectionChangedInMouseDown]; + } + set { + this.listViewState[LISTVIEWSTATE_itemCollectionChangedInMouseDown] = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + Editor("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + AssemblyRef.SystemDesign,typeof(UITypeEditor)), + SRDescription(SR.ListViewItemsDescr), + MergableProperty(false) + ] + public ListViewItemCollection Items { + get { + return listItemCollection; + } + } + + /// + /// + /// Tells whether the EditLabels style is currently set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewLabelEditDescr) + ] + public bool LabelEdit { + get { + return listViewState[LISTVIEWSTATE_labelEdit]; + } + set { + if (LabelEdit != value) { + listViewState[LISTVIEWSTATE_labelEdit] = value; + UpdateStyles(); + } + } + } + + /// + /// + /// Tells whether the LabelWrap style is currently set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ListViewLabelWrapDescr) + ] + public bool LabelWrap { + get { + return listViewState[LISTVIEWSTATE_labelWrap]; + } + set { + if (LabelWrap != value) { + listViewState[LISTVIEWSTATE_labelWrap] = value; + UpdateStyles(); + } + } + } + + /// + /// + /// The Currently set ImageList for Large Icon mode. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ListViewLargeImageListDescr) + ] + public ImageList LargeImageList { + get { + return imageListLarge; + } + set { + if (value != imageListLarge) { + + EventHandler recreateHandler = new EventHandler(LargeImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + EventHandler changeHandler = new EventHandler(LargeImageListChangedHandle); + + if (imageListLarge != null) { + imageListLarge.RecreateHandle -= recreateHandler; + imageListLarge.Disposed -= disposedHandler; + imageListLarge.ChangeHandle -= changeHandler; + } + + imageListLarge = value; + + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + value.ChangeHandle += changeHandler; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETIMAGELIST, (IntPtr)NativeMethods.LVSIL_NORMAL, value == null ? IntPtr.Zero: value.Handle); + if (AutoArrange && !listViewState1[LISTVIEWSTATE1_disposingImageLists]) { + UpdateListViewItemsLocations(); + } + } + } + } + } + + /// + /// Returns the current LISTVIEWSTATE_handleDestroyed value so that this + /// value can be accessed from child classes. + /// + internal bool ListViewHandleDestroyed { + get { + return listViewState[LISTVIEWSTATE_handleDestroyed]; + } + set { + listViewState[LISTVIEWSTATE_handleDestroyed] = value; + } + } + + /// + /// + /// The sorting comparer for this ListView. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewItemSorterDescr) + ] + public IComparer ListViewItemSorter { + get { + return listItemSorter; + } + set { + if (listItemSorter != value) { + listItemSorter = value; + + if (!this.VirtualMode) { + Sort(); + } + } + } + } + + /// + /// + /// Tells whether the MultiSelect style is currently set. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListViewMultiSelectDescr) + ] + public bool MultiSelect { + get { + return listViewState[LISTVIEWSTATE_multiSelect]; + } + set { + if (MultiSelect != value) { + listViewState[LISTVIEWSTATE_multiSelect] = value; + UpdateStyles(); + } + } + } + + /// + /// + /// Indicates whether the list view items (and sub-items in the Details view) will be + /// drawn by the system or the user. This includes the column header when item index = -1. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewOwnerDrawDescr) + ] + public bool OwnerDraw { + get { + return listViewState[LISTVIEWSTATE_ownerDraw]; + } + + set { + if (OwnerDraw != value) + { + listViewState[LISTVIEWSTATE_ownerDraw] = value; + Invalidate(true); + } + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + } + + + /// + /// + /// Tells whether the ScrollBars are visible or not. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListViewScrollableDescr) + ] + public bool Scrollable { + get { + return listViewState[LISTVIEWSTATE_scrollable]; + } + set { + if (Scrollable != value) { + listViewState[LISTVIEWSTATE_scrollable] = value; + RecreateHandleInternal(); + } + } + } + + /// + /// + /// The indices of the currently selected list items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public SelectedIndexCollection SelectedIndices { + get { + if (selectedIndexCollection == null) { + selectedIndexCollection = new SelectedIndexCollection(this); + } + return selectedIndexCollection; + } + } + + /// + /// + /// The currently selected list items. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewSelectedItemsDescr) + ] + public SelectedListViewItemCollection SelectedItems { + get { + if (selectedListViewItemCollection == null) { + selectedListViewItemCollection = new SelectedListViewItemCollection(this); + } + return selectedListViewItemCollection; + } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ListViewShowGroupsDescr) + ] + public bool ShowGroups { + get { + return this.listViewState[LISTVIEWSTATE_showGroups]; + } + set { + if (value != this.ShowGroups) { + this.listViewState[LISTVIEWSTATE_showGroups] = value; + if (IsHandleCreated) { + UpdateGroupView(); + } + } + } + } + + /// + /// + /// The currently set SmallIcon image list. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ListViewSmallImageListDescr) + ] + public ImageList SmallImageList { + get { + return imageListSmall; + } + set { + if (imageListSmall != value) { + + EventHandler recreateHandler = new EventHandler(SmallImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + if (imageListSmall != null) { + imageListSmall.RecreateHandle -= recreateHandler; + imageListSmall.Disposed -= disposedHandler; + } + imageListSmall = value; + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + + if (IsHandleCreated) { + + SendMessage(NativeMethods.LVM_SETIMAGELIST, (IntPtr)NativeMethods.LVSIL_SMALL, value == null ? IntPtr.Zero: value.Handle); + + if (this.View == View.SmallIcon) { + this.View = View.LargeIcon; + this.View = View.SmallIcon; + } else if (!listViewState1[LISTVIEWSTATE1_disposingImageLists]) { + UpdateListViewItemsLocations(); + } + + if (this.View == View.Details) { + this.Invalidate(true /*invalidateChildren*/); + } + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ListViewShowItemToolTipsDescr) + ] + public bool ShowItemToolTips { + get { + return listViewState[LISTVIEWSTATE_showItemToolTips]; + } + set { + if (ShowItemToolTips != value) { + listViewState[LISTVIEWSTATE_showItemToolTips] = value; + RecreateHandleInternal(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(SortOrder.None), + SRDescription(SR.ListViewSortingDescr) + ] + public SortOrder Sorting { + get { + return sorting; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)SortOrder.None, (int)SortOrder.Descending)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(SortOrder)); + } + if (sorting != value) { + sorting = value; + if (this.View == View.LargeIcon || this.View == View.SmallIcon) { + if (listItemSorter == null) { + listItemSorter = new IconComparer(sorting); + } + else if (listItemSorter is IconComparer) { + ((IconComparer)listItemSorter).SortOrder = sorting; + } + + } else if (value == SortOrder.None) { + listItemSorter = null; + } + + // If we're changing to No Sorting, no need to recreate the handle + // because none of the existing items need to be rearranged. + if (value == SortOrder.None) + UpdateStyles(); + else + RecreateHandleInternal(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ListViewStateImageListDescr) + ] + public ImageList StateImageList { + get { + return imageListState; + } + set { + if (this.UseCompatibleStateImageBehavior) { + if (imageListState != value) { + + EventHandler recreateHandler = new EventHandler(StateImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + if (imageListState != null) { + imageListState.RecreateHandle -= recreateHandler; + imageListState.Disposed -= disposedHandler; + } + imageListState = value; + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + + if (IsHandleCreated) + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, value == null ? IntPtr.Zero: value.Handle); + } + } else { + if (imageListState != value) { + + EventHandler recreateHandler = new EventHandler(StateImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + if (imageListState != null) { + imageListState.RecreateHandle -= recreateHandler; + imageListState.Disposed -= disposedHandler; + } + + if (this.IsHandleCreated && imageListState != null && this.CheckBoxes) { + // vsWhidbey 395361. + // If CheckBoxes are set to true, then we will have to recreate the handle. + // For some reason, if CheckBoxes are set to true and the list view has a state imageList, then the native listView destroys + // the state imageList. + // (Yes, it does exactly that even though our wrapper sets LVS_SHAREIMAGELISTS on the native listView.) + // So we make the native listView forget about its StateImageList just before we recreate the handle. + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, IntPtr.Zero); + } + + imageListState = value; + + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + + if (IsHandleCreated) { + if (CheckBoxes) { + // need to recreate to get the new images pushed in. + RecreateHandleInternal(); + } else { + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, (imageListState == null || imageListState.Images.Count == 0) ? IntPtr.Zero : imageListState.Handle); + } + + // Comctl should handle auto-arrange for us, but doesn't + if (!listViewState1[LISTVIEWSTATE1_disposingImageLists]) { + UpdateListViewItemsLocations(); + } + } + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + Browsable(true), + SRDescription(SR.ListViewTileSizeDescr), + ] + public Size TileSize { + get { + if (tileSize.IsEmpty) { + if (this.IsHandleCreated) { + // Get the default value from the ListView + // + NativeMethods.LVTILEVIEWINFO tileViewInfo = new NativeMethods.LVTILEVIEWINFO(); + tileViewInfo.dwMask = NativeMethods.LVTVIM_TILESIZE; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_GETTILEVIEWINFO, 0, tileViewInfo); + return new Size(tileViewInfo.sizeTile.cx, tileViewInfo.sizeTile.cy); + } else { + return Size.Empty; + } + } else { + return tileSize; + } + } + set { + if (tileSize != value) { + if (value.IsEmpty || value.Height <= 0 || value.Width <= 0) { + throw new ArgumentOutOfRangeException("TileSize", SR.GetString(SR.ListViewTileSizeMustBePositive)); + } + + tileSize = value; + if (this.IsHandleCreated) { + NativeMethods.LVTILEVIEWINFO tileViewInfo = new NativeMethods.LVTILEVIEWINFO(); + tileViewInfo.dwMask = NativeMethods.LVTVIM_TILESIZE; + tileViewInfo.dwFlags = NativeMethods.LVTVIF_FIXEDSIZE; + tileViewInfo.sizeTile = new NativeMethods.SIZE(tileSize.Width, tileSize.Height); + bool retval = UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SETTILEVIEWINFO, 0, tileViewInfo); + Debug.Assert(retval, "LVM_SETTILEVIEWINFO failed"); + if (this.AutoArrange) { + this.UpdateListViewItemsLocations(); + } + } + } + } + } + + private bool ShouldSerializeTileSize() { + return !this.tileSize.Equals(Size.Empty); + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewTopItemDescr) + ] + public ListViewItem TopItem { + get { + if (viewStyle == View.LargeIcon || viewStyle == View.SmallIcon || viewStyle == View.Tile) + throw new InvalidOperationException(SR.GetString(SR.ListViewGetTopItem)); + + if (!IsHandleCreated) { + if (Items.Count > 0) { + return Items[0]; + } + else { + return null; + } + } + topIndex = unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETTOPINDEX, 0, 0)); + if (topIndex >= 0 && topIndex < Items.Count) + return Items[topIndex]; + + return null; + } + set { + if (viewStyle == View.LargeIcon || viewStyle == View.SmallIcon || viewStyle == View.Tile) + throw new InvalidOperationException(SR.GetString(SR.ListViewSetTopItem)); + + if (value == null) + return; + if (value.ListView != this) + return; + if (!IsHandleCreated) { + CreateHandle(); + } + if (value == TopItem) + return; + EnsureVisible(value.Index); + ListViewItem topItem = TopItem; + + if ((topItem == null) && (topIndex == Items.Count)) // HACK ALERT! VSWhidbey bug 154094/Windows OS Bugs bug 872012 + { // There's a bug in the common controls when the listview window is too small to fully display + topItem = value; // a single item. Result of the bug is that the return value from a LVM_GETTOPINDEX + if (Scrollable) // message is the number of items in the list rather than an index of an item in the list. + { // This causes TopItem to return null. A side issue is that EnsureVisible doesn't do too well + EnsureVisible(0); // here either, because it causes the listview to go blank rather than displaying anything useful. + Scroll(0, value.Index); // To work around this, we force the listbox to display the first item, then scroll down to the item + } // user is setting as the top item. + return; // + } // END HACK ALERT + + if (value.Index == topItem.Index) + return; + if (Scrollable) + Scroll(topItem.Index, value.Index); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DefaultValue(true) + ] + public bool UseCompatibleStateImageBehavior { + get { + return this.listViewState1[LISTVIEWSTATE1_useCompatibleStateImageBehavior]; + } + set + { + this.listViewState1[LISTVIEWSTATE1_useCompatibleStateImageBehavior] = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(View.LargeIcon), + SRDescription(SR.ListViewViewDescr) + ] + public View View { + get { + return viewStyle; + } + set { + if (value == View.Tile && this.CheckBoxes) { + throw new NotSupportedException(SR.GetString(SR.ListViewTileViewDoesNotSupportCheckBoxes)); + } + + this.FlipViewToLargeIconAndSmallIcon = false; + + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)View.LargeIcon, (int)View.Tile)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(View)); + } + + if (value == View.Tile && VirtualMode) { + throw new NotSupportedException(SR.GetString(SR.ListViewCantSetViewToTileViewInVirtualMode)); + } + + if (viewStyle != value) { + viewStyle = value; + if (IsHandleCreated && ComctlSupportsVisualStyles) { + SendMessage(NativeMethods.LVM_SETVIEW,(int)viewStyle,0); + UpdateGroupView(); + + // if we switched to Tile view we should update the win32 list view tile view info + if (viewStyle == View.Tile) { + UpdateTileView(); + } + } + else { + UpdateStyles(); + } + + UpdateListViewItemsLocations(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ListViewVirtualListSizeDescr) + ] + public int VirtualListSize { + get { + return this.virtualListSize; + } + set { + if (value < 0) + throw new System.ArgumentException(SR.GetString(SR.ListViewVirtualListSizeInvalidArgument, "value", (value.ToString(CultureInfo.CurrentCulture)))); + if (value == virtualListSize) + return; + bool keepTopItem = this.IsHandleCreated && VirtualMode && this.View == View.Details && !this.DesignMode; + int topIndex = -1; + if (keepTopItem) { + topIndex = unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETTOPINDEX, 0, 0)); + } + + virtualListSize = value; + + if (IsHandleCreated && VirtualMode && !DesignMode) + SendMessage(NativeMethods.LVM_SETITEMCOUNT, virtualListSize, 0); + + if (keepTopItem) { + topIndex = Math.Min(topIndex, this.VirtualListSize - 1); + // After setting the virtual list size ComCtl makes the first item the top item. + // So we set the top item only if it wasn't the first item to begin with. + if (topIndex > 0) { + ListViewItem lvItem = this.Items[topIndex]; + this.TopItem = lvItem; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ListViewVirtualModeDescr) + ] + public bool VirtualMode { + get { + return listViewState[LISTVIEWSTATE_virtualMode]; + } + set { + if (value == VirtualMode) + return; + + if (value && Items.Count > 0) + throw new InvalidOperationException(SR.GetString(SR.ListViewVirtualListViewRequiresNoItems)); + + if (value && CheckedItems.Count > 0) { + throw new InvalidOperationException(SR.GetString(SR.ListViewVirtualListViewRequiresNoCheckedItems)); + } + + if (value && SelectedItems.Count > 0) { + throw new InvalidOperationException(SR.GetString(SR.ListViewVirtualListViewRequiresNoSelectedItems)); + } + + // Tile view does not work w/ VirtualMode. + if (value && View == View.Tile) { + throw new NotSupportedException(SR.GetString(SR.ListViewCantSetVirtualModeWhenInTileView)); + } + + listViewState[LISTVIEWSTATE_virtualMode] = value; + + RecreateHandleInternal(); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewAfterLabelEditDescr)] + public event LabelEditEventHandler AfterLabelEdit { + add { + onAfterLabelEdit += value; + } + remove { + onAfterLabelEdit -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewBeforeLabelEditDescr)] + public event LabelEditEventHandler BeforeLabelEdit { + add { + onBeforeLabelEdit += value; + } + remove { + onBeforeLabelEdit -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewCacheVirtualItemsEventDescr)] + public event CacheVirtualItemsEventHandler CacheVirtualItems { + add { + Events.AddHandler(EVENT_CACHEVIRTUALITEMS, value); + } + remove { + Events.RemoveHandler(EVENT_CACHEVIRTUALITEMS, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewColumnClickDescr)] + public event ColumnClickEventHandler ColumnClick { + add { + onColumnClick += value; + } + remove { + onColumnClick -= value; + } + } + + /// + /// + /// Tell the user that the column headers are being rearranged + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListViewColumnReorderedDscr)] + public event ColumnReorderedEventHandler ColumnReordered { + add { + Events.AddHandler(EVENT_COLUMNREORDERED, value); + } + remove { + Events.RemoveHandler(EVENT_COLUMNREORDERED, value); + } + } + + /// + /// + /// Tell the user that the column width changed + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListViewColumnWidthChangedDscr)] + public event ColumnWidthChangedEventHandler ColumnWidthChanged { + add { + Events.AddHandler(EVENT_COLUMNWIDTHCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_COLUMNWIDTHCHANGED, value); + } + } + + /// + /// + /// Tell the user that the column width is being changed + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListViewColumnWidthChangingDscr)] + public event ColumnWidthChangingEventHandler ColumnWidthChanging { + add { + Events.AddHandler(EVENT_COLUMNWIDTHCHANGING, value); + } + remove { + Events.RemoveHandler(EVENT_COLUMNWIDTHCHANGING, value); + } + } + + /// + /// + /// Fires in owner draw + Details mode when a column header needs to be drawn. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewDrawColumnHeaderEventDescr)] + public event DrawListViewColumnHeaderEventHandler DrawColumnHeader { + add { + Events.AddHandler(EVENT_DRAWCOLUMNHEADER, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWCOLUMNHEADER, value); + } + } + + /// + /// + /// Fires in owner draw mode when a ListView item needs to be drawn. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewDrawItemEventDescr)] + public event DrawListViewItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_DRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWITEM, value); + } + } + + /// + /// + /// Fires in owner draw mode and Details view when a ListView sub-item needs to be drawn. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewDrawSubItemEventDescr)] + public event DrawListViewSubItemEventHandler DrawSubItem { + add { + Events.AddHandler(EVENT_DRAWSUBITEM, value); + } + remove { + Events.RemoveHandler(EVENT_DRAWSUBITEM, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemClickDescr)] + public event EventHandler ItemActivate { + add { + onItemActivate += value; + } + remove { + onItemActivate -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.CheckedListBoxItemCheckDescr)] + public event ItemCheckEventHandler ItemCheck { + add { + onItemCheck += value; + } + remove { + onItemCheck -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewItemCheckedDescr)] + public event ItemCheckedEventHandler ItemChecked { + add { + onItemChecked += value; + } + remove { + onItemChecked -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemDragDescr)] + public event ItemDragEventHandler ItemDrag { + add { + onItemDrag += value; + } + remove { + onItemDrag -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemMouseHoverDescr)] + public event ListViewItemMouseHoverEventHandler ItemMouseHover { + add { + onItemMouseHover += value; + } + remove { + onItemMouseHover -= value; + } + } + + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewItemSelectionChangedDescr)] + public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged { + add { + Events.AddHandler(EVENT_ITEMSELECTIONCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_ITEMSELECTIONCHANGED, value); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// ListView Onpaint. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewRetrieveVirtualItemEventDescr)] + public event RetrieveVirtualItemEventHandler RetrieveVirtualItem { + add { + Events.AddHandler(EVENT_RETRIEVEVIRTUALITEM, value); + } + remove { + Events.RemoveHandler(EVENT_RETRIEVEVIRTUALITEM, value); + } + } + + /// + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewSearchForVirtualItemDescr)] + public event SearchForVirtualItemEventHandler SearchForVirtualItem { + add { + Events.AddHandler(EVENT_SEARCHFORVIRTUALITEM, value); + } + remove { + Events.RemoveHandler(EVENT_SEARCHFORVIRTUALITEM, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewSelectedIndexChangedDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); + } + } + + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ListViewVirtualItemsSelectionRangeChangedDescr)] + public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged { + add { + Events.AddHandler(EVENT_VIRTUALITEMSSELECTIONRANGECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_VIRTUALITEMSSELECTIONRANGECHANGED, value); + } + } + + + /// + /// Called to add any delayed update items we have to the list view. We do this because + /// we have optimnized the case where a user is only adding items within a beginupdate/endupdate + /// block. If they do any other operations (get the count, remove, insert, etc.), we push in the + /// cached up items first, then do the requested operation. This keeps it simple so we don't have to + /// try to maintain parellel state of the cache during a begin update end update. + /// + private void ApplyUpdateCachedItems() { + // first check if there is a delayed update array + // + ArrayList newItems = (ArrayList)Properties.GetObject(PropDelayedUpdateItems); + if (newItems != null) { + // if there is, clear it and push the items in. + // + Properties.SetObject(PropDelayedUpdateItems, null); + ListViewItem[] items = (ListViewItem[])newItems.ToArray(typeof(ListViewItem)); + if (items.Length > 0) { + InsertItems(itemCount, items, false /*checkHosting*/); + } + } + } + + /// + /// + /// In Large Icon or Small Icon view, arranges the items according to one + /// of the following behaviors: + /// + public void ArrangeIcons(ListViewAlignment value) { + // LVM_ARRANGE only work in SmallIcon view + if (viewStyle != View.SmallIcon) return; + + switch ((int)value) { + case NativeMethods.LVA_DEFAULT: + case NativeMethods.LVA_ALIGNLEFT: + case NativeMethods.LVA_ALIGNTOP: + case NativeMethods.LVA_SNAPTOGRID: + if (IsHandleCreated) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_ARRANGE, (int) value, 0); + } + break; + + default: + throw new ArgumentException(SR.GetString(SR.InvalidArgument, + "value", + ((value).ToString()))); + } + + if (!VirtualMode && sorting != SortOrder.None) { + Sort(); + } + + } + + /// + /// + /// In Large Icon or Small Icon view, arranges items according to the ListView's + /// current alignment style. + /// + public void ArrangeIcons() { + ArrangeIcons((ListViewAlignment)NativeMethods.LVA_DEFAULT); + } + + /// + public void AutoResizeColumns(ColumnHeaderAutoResizeStyle headerAutoResize) { + if (!this.IsHandleCreated) { + this.CreateHandle(); + } + UpdateColumnWidths(headerAutoResize); + } + + /// + public void AutoResizeColumn(int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize) { + if (!this.IsHandleCreated) { + this.CreateHandle(); + } + SetColumnWidth(columnIndex, headerAutoResize); + } + + /// + /// + /// Prevents the ListView from redrawing itself until EndUpdate is called. + /// Calling this method before individually adding or removing a large number of Items + /// will improve performance and reduce flicker on the ListView as items are + /// being updated. Always call EndUpdate immediately after the last item is updated. + /// + public void BeginUpdate() { + BeginUpdateInternal(); + + // if this is the first BeginUpdate call, push an ArrayList into the PropertyStore so + // we can cache up any items that have been added while this is active. + // + if (updateCounter++ == 0 && null == Properties.GetObject(PropDelayedUpdateItems)) { + Properties.SetObject(PropDelayedUpdateItems, new ArrayList()); + } + } + + internal void CacheSelectedStateForItem(ListViewItem lvi, bool selected) { + if (selected) { + if (this.savedSelectedItems == null) { + this.savedSelectedItems = new List(); + } + if (!this.savedSelectedItems.Contains(lvi)) { + this.savedSelectedItems.Add(lvi); + } + } else { + if (this.savedSelectedItems != null && this.savedSelectedItems.Contains(lvi)) { + this.savedSelectedItems.Remove(lvi); + } + } + } + +#if DEBUG + private void CheckDisplayIndices() { + // sanity check + // all the column headers should have a displayIndex between 0 and this.Columns.Count - 1; + // DisplayIndex should be different for different column headers + int sumOfDisplayIndices = 0; + for (int i = 0; i < this.Columns.Count; i ++) { + sumOfDisplayIndices += this.Columns[i].DisplayIndex; + Debug.Assert(this.Columns[i].DisplayIndex > -1 && this.Columns[i].DisplayIndex < this.Columns.Count, "display indices out of whack"); + } + int colsCount = this.Columns.Count; + Debug.Assert(sumOfDisplayIndices == (colsCount - 1) * colsCount / 2, "display indices out of whack"); + } +#endif + + private void CleanPreviousBackgroundImageFiles() { + if (this.bkImgFileNames == null) { + return; + } + + // SECREVIEW : Safe to assert FileIO here since we are deleting files created by us. + // + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.Assert(); + try { + System.IO.FileInfo fi; + for (int i = 0; i <= this.bkImgFileNamesCount; i ++) { + fi = new System.IO.FileInfo(this.bkImgFileNames[i]); + if (fi.Exists) { + // Microsoft: vsWhidbey 417804. + // ComCtl ListView uses COM objects to manipulate the bitmap we send it to them. + // I could not find any resources which explain in detail when the IImgCtx objects + // release the temporary file. So if we get a FileIO when we delete the temporary file + // we don't do anything about it ( because we don't know what is a good time to try to delete the file again ). + try { + fi.Delete(); + } catch (System.IO.IOException){} + } + } + } finally { + System.Security.PermissionSet.RevertAssert(); + } + + this.bkImgFileNames = null; + this.bkImgFileNamesCount = -1; + } + + /// + /// + /// Removes all items and columns from the ListView. + /// + public void Clear() { + Items.Clear(); + Columns.Clear(); + } + + /// + /// + /// This is the sorting callback function called by the system ListView control. + /// + /// + private int CompareFunc(IntPtr lparam1, IntPtr lparam2, IntPtr lparamSort) { + + Debug.Assert(listItemSorter != null, "null sorter!"); + if (listItemSorter != null) { + return listItemSorter.Compare(listItemsTable[(int)lparam1], listItemsTable[(int)lparam2]); + } + else { + return 0; + } + } + + private int CompensateColumnHeaderResize(Message m, bool columnResizeCancelled) { + if (this.ComctlSupportsVisualStyles && + this.View == View.Details && + !columnResizeCancelled && + this.Items.Count > 0) { + NativeMethods.NMHEADER header = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + + return CompensateColumnHeaderResize(header.iItem, columnResizeCancelled); + } else { + return 0; + } + } + + private int CompensateColumnHeaderResize(int columnIndex, bool columnResizeCancelled) { + // We need to compensate padding only when ComCtl60 is loaded. + // We need to compensate padding only if the list view is in Details mode. + // We need to compensate padding only if the user did not cancel ColumnWidthChanging event. + // We need to compensate padding only if the list view contains items. + // We need to compensate padding if the user resizes the first column. + // If the list view has a small image list then: + // 1. if there is a list view item w/ ImageIndex > -1 then we don't have to resize the first column. + // 2. Otherwise, we need to add 18 pixels. + // If the list view does not have a small image list then we need to add 2 pixels. + + if (this.ComctlSupportsVisualStyles && + this.View == View.Details && + !columnResizeCancelled && + this.Items.Count > 0) { + // The user resized the first column. + if (columnIndex == 0) { + ColumnHeader col = (this.columnHeaders != null && this.columnHeaders.Length > 0) ? this.columnHeaders[0] : null; + if (col != null) { + if (this.SmallImageList == null) { + return 2; + } else { + // If the list view contains an item w/ a non-negative ImageIndex then we don't need to + // add extra padding. + bool addPadding = true; + for (int i = 0; i < this.Items.Count; i ++) { + if (this.Items[i].ImageIndexer.ActualIndex > -1) { + addPadding = false; + break; + } + } + + if (addPadding) { + // 18 = 16 + 2. + // 16 = size of small image list. + // 2 is the padding we add when there is no small image list. + return 18; + } + } + } + } + } + + return 0; + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_LISTVIEW_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + + // image location + if (this.BackgroundImage != null) + SetBackgroundImage(); + } + + // + + + + /// + /// + /// Handles custom drawing of list items - for individual item font/color changes. + /// + /// If OwnerDraw is true, we fire the OnDrawItem and OnDrawSubItem (in Details view) + /// events and let the user do the drawing. + /// + /// + unsafe void CustomDraw(ref Message m) { + + bool dontmess = false; + bool itemDrawDefault = false; + + try + { + NativeMethods.NMLVCUSTOMDRAW* nmcd = (NativeMethods.NMLVCUSTOMDRAW*)m.LParam; + // Find out which stage we're drawing + switch (nmcd->nmcd.dwDrawStage) + { + case NativeMethods.CDDS_PREPAINT: + if (OwnerDraw) + { + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYITEMDRAW); + return; + } + // We want custom draw for this paint cycle + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYSUBITEMDRAW | NativeMethods.CDRF_NEWFONT); + // refresh the cache of the current color & font settings for this paint cycle + odCacheBackColor = this.BackColor; + odCacheForeColor = this.ForeColor; + odCacheFont = this.Font; + odCacheFontHandle = this.FontHandle; + + // If preparing to paint a group item, make sure its bolded. + if (nmcd->dwItemType == NativeMethods.LVCDI_GROUP) + { + if (odCacheFontHandleWrapper != null) + { + odCacheFontHandleWrapper.Dispose(); + } + odCacheFont = new Font(odCacheFont, FontStyle.Bold); + odCacheFontHandleWrapper = new Control.FontHandleWrapper(odCacheFont); + odCacheFontHandle = odCacheFontHandleWrapper.Handle; + SafeNativeMethods.SelectObject(new HandleRef(nmcd->nmcd, nmcd->nmcd.hdc), new HandleRef(odCacheFontHandleWrapper, odCacheFontHandleWrapper.Handle)); + m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; + } + return; + + //We have to return a NOTIFYSUBITEMDRAW (called NOTIFYSUBITEMREDRAW in the docs) here to + //get it to enter "change all subitems instead of whole rows" mode. + + //HOWEVER... we only want to do this for report styles... + + case NativeMethods.CDDS_ITEMPREPAINT: + + int itemIndex = (int)nmcd->nmcd.dwItemSpec; + //VSWhidbey #163674 - the following call silently returns Rectangle.Empty if no corresponding + // item was found. We do this because the native listview, under some circumstances, seems + // to send spurious custom draw notifications. The check below does the rest. + Rectangle itemBounds = GetItemRectOrEmpty(itemIndex); + + if (!ClientRectangle.IntersectsWith(itemBounds)) + { + // we don't need to bother painting this one. + return; + } + + // If OwnerDraw is true, fire the onDrawItem event. + if (OwnerDraw) + { + + Graphics g = Graphics.FromHdcInternal(nmcd->nmcd.hdc); + +#if DEBUGGING + Rectangle r = itemBounds; + Rectangle r2 = Rectangle.FromLTRB(nmcd->nmcd.rc.left, nmcd->nmcd.rc.top, nmcd->nmcd.rc.right, nmcd->nmcd.rc.bottom); + Debug.WriteLine("ClipBounds : l {0} t {1} r {2} b {3}", g.ClipBounds.Left, g.ClipBounds.Top, g.ClipBounds.Right, g.ClipBounds.Bottom); + Debug.WriteLine("Rect (Send Msg) : l {0} t {1} r {2} b {3}", r.Left, r.Top, r.Right, r.Bottom); + Debug.WriteLine("Rect (NM) : l {0} t {1} r {2} b {3}", r2.Left, r2.Top, r2.Right, r2.Bottom); +#endif + DrawListViewItemEventArgs e = null; + try + { + e = new DrawListViewItemEventArgs(g, + Items[(int)nmcd->nmcd.dwItemSpec], + itemBounds, + (int)nmcd->nmcd.dwItemSpec, + (ListViewItemStates)(nmcd->nmcd.uItemState)); + + OnDrawItem(e); + } + finally + { + g.Dispose(); + } + + itemDrawDefault = e.DrawDefault; + + // For the Details view, we send a SKIPDEFAULT when we get a sub-item drawing notification. + // For other view styles, we do it here. + if (viewStyle == View.Details) + { + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYSUBITEMDRAW); + } + else + { + if (!e.DrawDefault) + { + m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); + } + } + + if (!e.DrawDefault) + { + return; // skip our regular drawing code + } + } + + if (viewStyle == View.Details || viewStyle == View.Tile) + { + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYSUBITEMDRAW | NativeMethods.CDRF_NEWFONT); + dontmess = true; // don't mess with our return value! + + //ITEMPREPAINT is used to work out the rect for the first column!!! GAH!!! + //(which means we can't just do our color/font work on SUBITEM|ITEM_PREPAINT) + //so fall through... and tell the end of SUBITEM|ITEM_PREPAINT not to mess + //with our return value... + + } + + //If it's not a report, we fall through and change the main item's styles + + goto case (NativeMethods.CDDS_SUBITEM | NativeMethods.CDDS_ITEMPREPAINT); + + case (NativeMethods.CDDS_SUBITEM | NativeMethods.CDDS_ITEMPREPAINT): + + itemIndex = (int)nmcd->nmcd.dwItemSpec; + //VSWhidbey #163674 - the following call silently returns Rectangle.Empty if no corresponding + // item was found. We do this because the native listview, under some circumstances, seems + // to send spurious custom draw notifications. The check below does the rest. + itemBounds = GetItemRectOrEmpty(itemIndex); + + if (!ClientRectangle.IntersectsWith(itemBounds)) + { + // we don't need to bother painting this one. + return; + } + + // If OwnerDraw is true, fire the onDrawSubItem event. + if (OwnerDraw && !itemDrawDefault) + { + + Graphics g = Graphics.FromHdcInternal(nmcd->nmcd.hdc); + DrawListViewSubItemEventArgs e = null; + + // by default, we want to skip the customDrawCode + bool skipCustomDrawCode = true; + try + { + + //The ListView will send notifications for every column, even if no + //corresponding subitem exists for a particular item. We shouldn't + //fire events in such cases. + if (nmcd->iSubItem < Items[itemIndex].SubItems.Count) + { + Rectangle subItemBounds = GetSubItemRect(itemIndex, nmcd->iSubItem); + + // For the first sub-item, the rectangle corresponds to the whole item. + // We need to handle this case separately. + if (nmcd->iSubItem == 0 && Items[itemIndex].SubItems.Count > 1) + { + // Use the width for the first column header. + subItemBounds.Width = this.columnHeaders[0].Width; + } + + if (this.ClientRectangle.IntersectsWith(subItemBounds)) + { + e = new DrawListViewSubItemEventArgs(g, + subItemBounds, + Items[itemIndex], + Items[itemIndex].SubItems[nmcd->iSubItem], + itemIndex, + nmcd->iSubItem, + columnHeaders[nmcd->iSubItem], + (ListViewItemStates)(nmcd->nmcd.uItemState)); + OnDrawSubItem(e); + + // the customer still wants to draw the default. + // Don't skip the custom draw code then + skipCustomDrawCode = !e.DrawDefault; + } + } + } + finally + { + g.Dispose(); + } + + if (skipCustomDrawCode) + { + m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); + return; // skip our custom draw code + } + } + + // get the node + ListViewItem item = Items[(int)(nmcd->nmcd.dwItemSpec)]; + // if we're doing the whole row in one style, change our result! + if (dontmess && item.UseItemStyleForSubItems) + { + m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; + } + Debug.Assert(item != null, "Item was null in ITEMPREPAINT"); + + int state = nmcd->nmcd.uItemState; + // There is a known and documented problem in the ListView winctl control - + // if the LVS_SHOWSELALWAYS style is set, then the item state will have + // the CDIS_SELECTED bit set for all items. So we need to verify with the + // real item state to be sure. + if (!HideSelection) + { + int realState = GetItemState((int)(nmcd->nmcd.dwItemSpec)); + if ((realState & NativeMethods.LVIS_SELECTED) == 0) + { + state &= ~NativeMethods.CDIS_SELECTED; + } + } + + // subitem is invalid if the flag isn't set -- and we also use this code in + // cases where subitems aren't visible (ie. non-Details modes), so if subitem + // is invalid, point it at the main item's render info + + int subitem = ((nmcd->nmcd.dwDrawStage & NativeMethods.CDDS_SUBITEM) != 0) ? nmcd->iSubItem : 0; + + // Work out the style in which to render this item + // + Font subItemFont = null; + Color subItemForeColor = Color.Empty; + Color subItemBackColor = Color.Empty; + bool haveRenderInfo = false; + bool disposeSubItemFont = false; + if (item != null && subitem < item.SubItems.Count) + { + haveRenderInfo = true; + if (subitem == 0 && (state & NativeMethods.CDIS_HOT) != 0 && HotTracking) + { + disposeSubItemFont = true; + subItemFont = new Font(item.SubItems[0].Font, FontStyle.Underline); + } + else + { + subItemFont = item.SubItems[subitem].Font; + } + + if (subitem > 0 || (state & (NativeMethods.CDIS_SELECTED | NativeMethods.CDIS_GRAYED | NativeMethods.CDIS_HOT | NativeMethods.CDIS_DISABLED)) == 0) + { + // we only propogate colors if we're displaying things normally + // the user can override this method to do all kinds of other crazy things if they + // want to though - but we don't support that. + subItemForeColor = item.SubItems[subitem].ForeColor; + subItemBackColor = item.SubItems[subitem].BackColor; + } + } + + // We always have to set font and color data, because of comctl design + + // + + + + + Color riFore = Color.Empty; + Color riBack = Color.Empty; + + if (haveRenderInfo) + { + riFore = subItemForeColor; + riBack = subItemBackColor; + } + + bool changeColor = true; + if (!Enabled) + { + changeColor = false; + } + else if ((activation == ItemActivation.OneClick) + || (activation == ItemActivation.TwoClick)) + { + if ((state & (NativeMethods.CDIS_SELECTED + | NativeMethods.CDIS_GRAYED + | NativeMethods.CDIS_HOT + | NativeMethods.CDIS_DISABLED)) != 0) + { + changeColor = false; + } + } + + if (changeColor) + { + if (!haveRenderInfo || riFore.IsEmpty) + { + nmcd->clrText = ColorTranslator.ToWin32(odCacheForeColor); + } + else + { + nmcd->clrText = ColorTranslator.ToWin32(riFore); + } + + // Work-around for a comctl quirk where, + // if clrText is the same as SystemColors.HotTrack, + // the subitem's color is not changed to nmcd->clrText. + // + // Try to tweak the blue component of clrText first, then green, then red. + // Basically, if the color component is 0xFF, subtract 1 from it + // (adding 1 will overflow), else add 1 to it. If the color component is 0, + // skip it and go to the next color (unless it is our last option). + if (nmcd->clrText == ColorTranslator.ToWin32(SystemColors.HotTrack)) + { + int totalshift = 0; + bool clrAdjusted = false; + int mask = 0xFF0000; + do + { + int C = nmcd->clrText & mask; + if (C != 0 || (mask == 0x0000FF)) // The color is not 0 + // or this is the last option + { + int n = 16 - totalshift; + // Make sure the value doesn't overflow + if (C == mask) + { + C = ((C >> n) - 1) << n; + } + else + { + C = ((C >> n) + 1) << n; + } + // Copy the adjustment into nmcd->clrText + nmcd->clrText = (nmcd->clrText & (~mask)) | C; + clrAdjusted = true; + } + else + { + mask >>= 8; // Try the next color. + // We try adjusting Blue, Green, Red in that order, + // since 0x0000FF is the most likely value of + // SystemColors.HotTrack + totalshift += 8; + } + } while (!clrAdjusted); + } + + if (!haveRenderInfo || riBack.IsEmpty) + { + nmcd->clrTextBk = ColorTranslator.ToWin32(odCacheBackColor); + } + else + { + nmcd->clrTextBk = ColorTranslator.ToWin32(riBack); + } + } + + if (!haveRenderInfo || subItemFont == null) + { + // safety net code just in case + if (odCacheFont != null) + { + SafeNativeMethods.SelectObject(new HandleRef(nmcd->nmcd, nmcd->nmcd.hdc), new HandleRef(null, odCacheFontHandle)); + } + } + else + { + if (odCacheFontHandleWrapper != null) + { + odCacheFontHandleWrapper.Dispose(); + } + odCacheFontHandleWrapper = new Control.FontHandleWrapper(subItemFont); + SafeNativeMethods.SelectObject(new HandleRef(nmcd->nmcd, nmcd->nmcd.hdc), new HandleRef(odCacheFontHandleWrapper, odCacheFontHandleWrapper.Handle)); + } + + if (!dontmess) + { + m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; + } + if (disposeSubItemFont) + { + subItemFont.Dispose(); + } + return; + + default: + m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; + return; + } + } + catch (Exception e) + { + Debug.Fail("Exception occurred attempting to setup custom draw. Disabling custom draw for this control", e.ToString()); + m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; + } + } + + private void DeleteFileName(string fileName) { + if (!String.IsNullOrEmpty(fileName)) { + // the list view needs the FileIOPermission when the app runs on an UNC share + // and the list view creates / destroys temporary files for its background image + + // SECREVIEW : Safe to assert FileIO here since we are deleting files created by us. + // + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.Assert(); + try { + System.IO.FileInfo fi = new System.IO.FileInfo(fileName); + if (fi.Exists) { + // Microsoft: vsWhidbey 417804. + // ComCtl ListView uses COM objects to manipulate the bitmap we send it to them. + // I could not find any resources which explain in detail when the IImgCtx objects + // release the temporary file. So if we get a FileIO when we delete the temporary file + // we don't do anything about it ( because we don't know what is a good time to try to delete the file again ). + try { + fi.Delete(); + } catch (System.IO.IOException){} + } + } finally { + System.Security.PermissionSet.RevertAssert(); + } + } + } + + private void DestroyLVGROUP(NativeMethods.LVGROUP lvgroup) { + if(lvgroup.pszHeader != IntPtr.Zero) { + Marshal.FreeHGlobal(lvgroup.pszHeader); + } + } + + /// + /// + /// Resets the imageList to null. We wire this method up to the imageList's + /// Dispose event, so that we don't hang onto an imageList that's gone away. + /// + private void DetachImageList(object sender, EventArgs e) { + listViewState1[LISTVIEWSTATE1_disposingImageLists] = true; + try { + #if DEBUG + if (sender != imageListSmall && sender != imageListState && sender != imageListLarge) + { + Debug.Fail("ListView sunk dispose event from unknown component"); + } + #endif // DEBUG + if (sender == imageListSmall) + SmallImageList = null; + if (sender == imageListLarge) + LargeImageList = null; + if (sender == imageListState) + StateImageList = null; + } + finally { + listViewState1[LISTVIEWSTATE1_disposingImageLists] = false; + } + + UpdateListViewItemsLocations(); + + + } + + /// + /// + /// Disposes of the component. Call dispose when the component is no longer needed. + /// This method removes the component from its container (if the component has a site) + /// and triggers the dispose event. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + // Remove any event sinks we have hooked up to imageLists + if (imageListSmall != null) { + imageListSmall.Disposed -= new EventHandler(this.DetachImageList); + imageListSmall = null; + } + if (imageListLarge != null) { + imageListLarge.Disposed -= new EventHandler(this.DetachImageList); + imageListLarge = null; + } + if (imageListState != null) { + imageListState.Disposed -= new EventHandler(this.DetachImageList); + imageListState = null; + } + + // Remove any ColumnHeaders contained in this control + if (columnHeaders != null) { + for (int colIdx = columnHeaders.Length-1; colIdx >= 0; colIdx--) { + columnHeaders[colIdx].OwnerListview = null; + columnHeaders[colIdx].Dispose(); + } + columnHeaders = null; + } + + // Remove any items we have + Items.Clear(); + + if (odCacheFontHandleWrapper != null) + { + odCacheFontHandleWrapper.Dispose(); + odCacheFontHandleWrapper = null; + } + + if (!String.IsNullOrEmpty(this.backgroundImageFileName) || this.bkImgFileNames != null) { + // we need the fileIoPermission when the app runs on an UNC share and + // the list view creates/deletes temporary files for its background image + + // SECREVIEW : Safe to assert FileIO here since we are deleting files created by us. + // + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.Assert(); + + try { + System.IO.FileInfo fi; + if (!String.IsNullOrEmpty(this.backgroundImageFileName)) { + fi = new System.IO.FileInfo(this.backgroundImageFileName); + Debug.Assert(fi.Exists, "who deleted our temp file?"); + // Microsoft: vsWhidbey 417804. + // ComCtl ListView uses COM objects to manipulate the bitmap we send it to them. + // I could not find any resources which explain in detail when the IImgCtx objects + // release the temporary file. So if we get a FileIO when we delete the temporary file + // we don't do anything about it ( because we don't know what is a good time to try to delete the file again ). + try { + fi.Delete(); + } catch (System.IO.IOException){} + this.backgroundImageFileName = String.Empty; + } + for (int i = 0; i <= this.bkImgFileNamesCount; i++) { + fi = new System.IO.FileInfo(this.bkImgFileNames[i]); + Debug.Assert(fi.Exists, "who deleted our temp file?"); + // Microsoft: vsWhidbey 417804. + // ComCtl ListView uses COM objects to manipulate the bitmap we send it to them. + // I could not find any resources which explain in detail when the IImgCtx objects + // release the temporary file. So if we get a FileIO when we delete the temporary file + // we don't do anything about it ( because we don't know what is a good time to try to delete the file again ). + try { + fi.Delete(); + } catch (System.IO.IOException){} + } + + this.bkImgFileNames = null; + this.bkImgFileNamesCount = -1; + } finally { + System.Security.PermissionSet.RevertAssert(); + } + } + + } + + base.Dispose(disposing); + } + + /// + /// + /// Cancels the effect of BeginUpdate. + /// + public void EndUpdate() { + + // On the final EndUpdate, check to see if we've got any cached items. + // If we do, insert them as normal, then turn off the painting freeze. + // + if (--updateCounter == 0 && null != Properties.GetObject(PropDelayedUpdateItems)) { + ApplyUpdateCachedItems(); + } + EndUpdateInternal(); + } + + private void EnsureDefaultGroup() { + if (IsHandleCreated && ComctlSupportsVisualStyles && GroupsEnabled) { + if (SendMessage(NativeMethods.LVM_HASGROUP, DefaultGroup.ID, 0) == IntPtr.Zero) { + UpdateGroupView(); + InsertGroupNative(0, DefaultGroup); + } + } + } + + /// + /// + /// Ensure that the item is visible, scrolling the view as necessary. + /// @index Index of item to scroll into view + /// + public void EnsureVisible(int index) { + if (index < 0 || index >= Items.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + if (IsHandleCreated) + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_ENSUREVISIBLE, index, 0); + } + + /// + /// + /// + public ListViewItem FindItemWithText(string text) { + // if the user does not use the FindItemWithText overloads that specify a StartIndex and the listView is empty then return null + if (this.Items.Count == 0) { + return null; + } + + return FindItemWithText(text, true, 0, true); + } + + /// + /// + /// + public ListViewItem FindItemWithText(string text, bool includeSubItemsInSearch, int startIndex) { + return FindItemWithText(text, includeSubItemsInSearch, startIndex, true); + } + + /// + /// + /// + public ListViewItem FindItemWithText(string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch) { + if (startIndex < 0 || startIndex >= this.Items.Count) + { + throw new ArgumentOutOfRangeException("startIndex", SR.GetString(SR.InvalidArgument, "startIndex", (startIndex).ToString(CultureInfo.CurrentCulture))); + } + return FindItem(true, text, isPrefixSearch, new Point(0,0), SearchDirectionHint.Down, startIndex, includeSubItemsInSearch); + } + + /// + /// + /// + public ListViewItem FindNearestItem(SearchDirectionHint dir, Point point) { + return FindNearestItem(dir, point.X, point.Y); + } + + /// + /// + /// + public ListViewItem FindNearestItem(SearchDirectionHint searchDirection, int x, int y) + { + if (this.View != View.SmallIcon && this.View != View.LargeIcon) { + throw new InvalidOperationException(SR.GetString(SR.ListViewFindNearestItemWorksOnlyInIconView)); + } + + if ( searchDirection < SearchDirectionHint.Left || searchDirection > SearchDirectionHint.Down) { + throw new ArgumentOutOfRangeException("searchDirection", SR.GetString(SR.InvalidArgument, "searchDirection", (searchDirection).ToString())); + } + + // the win32 ListView::FindNearestItem does some pretty weird things to determine the nearest item. + // simply passing the (x,y) coordinates will cause problems when we call FindNearestItem for a point inside an item. + // so we have to do some special processing when (x,y) falls inside an item; + // take a look at VSWHIDBEY bug 178646 and the attached ListView_IFindNearestItem.c file for a complete story. + ListViewItem lvi = this.GetItemAt(x,y); + + if (lvi != null) { + Rectangle itemBounds = lvi.Bounds; + // LVM_FINDITEM is a nightmare + // LVM_FINDITEM will use the top left corner of icon rectangle to determine the closest item + // What happens if there is no icon for this item? then the top left corner of the icon rectangle falls INSIDE the item label (???) + // + + Rectangle iconBounds = this.GetItemRect(lvi.Index, ItemBoundsPortion.Icon); + + switch (searchDirection) { + case SearchDirectionHint.Up: + y = Math.Max(itemBounds.Top, iconBounds.Top) - 1; + break; + case SearchDirectionHint.Down: + y = Math.Max(itemBounds.Top, iconBounds.Top) + 1; + break; + case SearchDirectionHint.Left: + x = Math.Max(itemBounds.Left, iconBounds.Left) - 1; + break; + case SearchDirectionHint.Right: + x = Math.Max(itemBounds.Left, iconBounds.Left) + 1; + break; + default: + Debug.Assert(false, "these are all the search directions"); + break; + } + } + + return FindItem(false, String.Empty, false, new Point(x, y), searchDirection, -1, false); + } + + private ListViewItem FindItem(bool isTextSearch, string text, bool isPrefixSearch, Point pt, SearchDirectionHint dir, int startIndex, bool includeSubItemsInSearch) { + if (this.Items.Count == 0) { + return null; + } + + if (!IsHandleCreated) + CreateHandle(); + if (VirtualMode) { + SearchForVirtualItemEventArgs sviEvent = new SearchForVirtualItemEventArgs(isTextSearch, isPrefixSearch, includeSubItemsInSearch, text, pt, dir, startIndex); + + OnSearchForVirtualItem(sviEvent); + // NOTE: this will cause a RetrieveVirtualItem event w/o a corresponding cache hint event. + if (sviEvent.Index != -1) { + return this.Items[sviEvent.Index]; + } else { + return null; + } + } else { + NativeMethods.LVFINDINFO lvFindInfo = new NativeMethods.LVFINDINFO(); + if (isTextSearch) { + lvFindInfo.flags = NativeMethods.LVFI_STRING; + lvFindInfo.flags |= (isPrefixSearch ? NativeMethods.LVFI_PARTIAL : 0); + lvFindInfo.psz = text; + } else { + lvFindInfo.flags = NativeMethods.LVFI_NEARESTXY; + lvFindInfo.ptX = pt.X; + lvFindInfo.ptY = pt.Y; + // we can do this because SearchDirectionHint is set to the VK_* + lvFindInfo.vkDirection = (int)dir; + } + lvFindInfo.lParam = IntPtr.Zero; + int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), + NativeMethods.LVM_FINDITEM, + startIndex-1, // decrement startIndex so that the search is 0-based + ref lvFindInfo); + if (index >= 0) { + return Items[index]; + } else if (isTextSearch && includeSubItemsInSearch) { + // win32 listView control can't search inside sub items + for (int i = startIndex; i < this.Items.Count; i ++) { + ListViewItem lvi = this.Items[i]; + for (int j = 0; j < lvi.SubItems.Count; j ++) { + ListViewItem.ListViewSubItem lvsi = lvi.SubItems[j]; + // the win32 list view search for items w/ text is case insensitive + // do the same for sub items + // because we are comparing user defined strings we have to do the slower String search + // ie, use String.Compare(string, string, case sensitive, CultureInfo) + // instead of new Whidbey String.Equals overload + // String.Equals(string, string, StringComparison.OrdinalIgnoreCase + if (String.Equals(text,lvsi.Text, StringComparison.OrdinalIgnoreCase)) { + return lvi; + } else if (isPrefixSearch && CultureInfo.CurrentCulture.CompareInfo.IsPrefix(lvsi.Text, text, CompareOptions.IgnoreCase)) { + return lvi; + } + } + } + return null; + } else { + return null; + } + } + } + + private void ForceCheckBoxUpdate() { + // Force ListView to update its checkbox bitmaps. + // + if (CheckBoxes && IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETEXTENDEDLISTVIEWSTYLE, NativeMethods.LVS_EX_CHECKBOXES, 0); + SendMessage(NativeMethods.LVM_SETEXTENDEDLISTVIEWSTYLE, NativeMethods.LVS_EX_CHECKBOXES, NativeMethods.LVS_EX_CHECKBOXES); + + // Comctl should handle auto-arrange for us, but doesn't + if (AutoArrange) { + ArrangeIcons(Alignment); + } + } + } + + private string GenerateRandomName() { + Debug.Assert(this.BackgroundImage != null, "we need to generate random numbers only when saving the background image to disk"); + Bitmap bm = new Bitmap(this.BackgroundImage); + int handle = 0; + + try { + handle = unchecked ((int) (long) bm.GetHicon()); + } catch { + bm.Dispose(); + } + + Random rnd; + if (handle == 0) { + // there was a problem when we got the icon handle + // use DateTime.Now to seed the randomizer + rnd = new Random((int)System.DateTime.Now.Ticks); + } else { + rnd = new Random(handle); + } + return rnd.Next().ToString(CultureInfo.InvariantCulture); + } + + // IDs for identifying ListViewItem's + private int GenerateUniqueID() { + // Okay, if someone adds several billion items to the list and doesn't remove all of them, + // we can reuse the same ID, but I'm willing to take that risk. We are even tracking IDs + // on a per-list view basis to reduce the problem. + int result = nextID++; + if (result == -1) {// leave -1 as a "no such value" ID + result = 0; + nextID = 1; + } + return result; + } + + /// + /// Gets the real index for the given item. lastIndex is the last return + /// value from GetDisplayIndex, or -1 if you don't know. If provided, + /// the search for the index can be greatly improved. + /// + internal int GetDisplayIndex(ListViewItem item, int lastIndex) { + + Debug.Assert(item.listView == this, "Can't GetDisplayIndex if the list item doesn't belong to us"); + Debug.Assert(item.ID != -1, "ListViewItem has no ID yet"); + + ApplyUpdateCachedItems(); + if (IsHandleCreated && !ListViewHandleDestroyed) { + Debug.Assert(listItemsArray == null, "listItemsArray not null, even though handle created"); + + NativeMethods.LVFINDINFO info = new NativeMethods.LVFINDINFO(); + info.lParam = (IntPtr) item.ID; + info.flags = NativeMethods.LVFI_PARAM; + + int displayIndex = -1; + + if (lastIndex != -1) { + displayIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_FINDITEM, lastIndex - 1, ref info); + } + + if (displayIndex == -1) { + displayIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_FINDITEM, -1 /* beginning */, ref info); + } + Debug.Assert(displayIndex != -1, "This item is in the list view -- why can't we find a display index for it?"); + return displayIndex; + } + else { + // PERF: The only reason we should ever call this before the handle is created + // is if the user calls ListViewItem.Index. + Debug.Assert(listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + + int index = 0; + foreach (object o in listItemsArray) { + if (o == item) + return index; + index++; + } + return -1; + } + } + + /// + /// + /// Called by ColumnHeader objects to determine their position + /// in the ListView + /// + /// + internal int GetColumnIndex(ColumnHeader ch) { + if (columnHeaders == null) + return -1; + + for (int i = 0; i < columnHeaders.Length; i++) { + if (columnHeaders[i] == ch) + return i; + } + + return -1; + } + + /// + /// + /// Returns the current ListViewItem corresponding to the specific + /// x,y co-ordinate. + /// + public ListViewItem GetItemAt(int x, int y) { + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + + lvhi.pt_x = x; + lvhi.pt_y = y; + + int displayIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_HITTEST, 0, lvhi); + + ListViewItem li = null; + if (displayIndex >= 0 && ((lvhi.flags & NativeMethods.LVHT_ONITEM) != 0)) + li = Items[displayIndex]; + + return li; + } + + internal int GetNativeGroupId(ListViewItem item) { + item.UpdateGroupFromName(); + + if (item.Group != null && Groups.Contains(item.Group)) { + return item.Group.ID; + } + else { + EnsureDefaultGroup(); + return DefaultGroup.ID; + } + } + + internal void GetSubItemAt(int x, int y, out int iItem, out int iSubItem) { + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + + lvhi.pt_x = x; + lvhi.pt_y = y; + + int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SUBITEMHITTEST, 0, lvhi); + + if (index > -1) { + iItem = lvhi.iItem; + iSubItem = lvhi.iSubItem; + } else { + iItem = -1; + iSubItem = -1; + } + } + + internal Point GetItemPosition(int index) { + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_GETITEMPOSITION, index, pt); + + return new Point(pt.x, pt.y); + } + + internal int GetItemState(int index) { + return GetItemState(index, NativeMethods.LVIS_FOCUSED | NativeMethods.LVIS_SELECTED | NativeMethods.LVIS_CUT | + NativeMethods.LVIS_DROPHILITED | NativeMethods.LVIS_OVERLAYMASK | + NativeMethods.LVIS_STATEIMAGEMASK); + } + + internal int GetItemState(int index, int mask) { + if (index < 0 || ((this.VirtualMode && index >= this.VirtualListSize) || (!this.VirtualMode && index >= itemCount))) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + Debug.Assert(IsHandleCreated, "How did we add items without a handle?"); + + return unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETITEMSTATE, index, mask)); + } + + /// + /// + /// Returns a list item's bounding rectangle, including subitems. + /// + public Rectangle GetItemRect(int index) { + return GetItemRect(index, 0); + } + + /// + /// + /// Returns a specific portion of a list item's bounding rectangle. + /// + public Rectangle GetItemRect(int index, ItemBoundsPortion portion) { + if (index < 0 || index >= this.Items.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(portion, (int)portion, (int)ItemBoundsPortion.Entire, (int)ItemBoundsPortion.ItemOnly)){ + throw new InvalidEnumArgumentException("portion", (int)portion, typeof(ItemBoundsPortion)); + } + + if (this.View == View.Details && this.Columns.Count == 0) { + return Rectangle.Empty; + } + + + NativeMethods.RECT itemrect = new NativeMethods.RECT(); + itemrect.left = (int)portion; + if (unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETITEMRECT, index, ref itemrect)) == 0) + throw new ArgumentException(SR.GetString(SR.InvalidArgument, + "index", + (index).ToString(CultureInfo.CurrentCulture))); + + return Rectangle.FromLTRB(itemrect.left, itemrect.top, itemrect.right, itemrect.bottom); + } + + /// + /// Private version of GetItemRect that fails silently. We use this instead of catching + /// exceptions thrown by GetItemRect, to avoid first chance exceptions confusing the user. + /// + private Rectangle GetItemRectOrEmpty(int index) { + if (index < 0 || index >= this.Items.Count) + return Rectangle.Empty; + + if (this.View == View.Details && this.Columns.Count == 0) { + return Rectangle.Empty; + } + + + NativeMethods.RECT itemrect = new NativeMethods.RECT(); + itemrect.left = 0; + if (unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETITEMRECT, index, ref itemrect)) == 0) + return Rectangle.Empty; + + return Rectangle.FromLTRB(itemrect.left, itemrect.top, itemrect.right, itemrect.bottom); + } + + private NativeMethods.LVGROUP GetLVGROUP(ListViewGroup group) + { + NativeMethods.LVGROUP lvgroup = new NativeMethods.LVGROUP(); + lvgroup.mask = NativeMethods.LVGF_HEADER | NativeMethods.LVGF_GROUPID | NativeMethods.LVGF_ALIGN; + + // Header + // + String header = group.Header; + lvgroup.pszHeader = Marshal.StringToHGlobalAuto(header); + lvgroup.cchHeader = header.Length; + + // Group ID + // + lvgroup.iGroupId = group.ID; + + // Alignment + // + switch(group.HeaderAlignment) { + case HorizontalAlignment.Left: + lvgroup.uAlign = NativeMethods.LVGA_HEADER_LEFT; + break; + case HorizontalAlignment.Right: + lvgroup.uAlign = NativeMethods.LVGA_HEADER_RIGHT; + break; + case HorizontalAlignment.Center: + lvgroup.uAlign = NativeMethods.LVGA_HEADER_CENTER; + break; + } + return lvgroup; + } + + /// + /// + /// Returns a listview sub-item's bounding rectangle. + /// + internal Rectangle GetSubItemRect(int itemIndex, int subItemIndex) { + return GetSubItemRect(itemIndex, subItemIndex, 0); + } + + internal Rectangle GetSubItemRect(int itemIndex, int subItemIndex, ItemBoundsPortion portion) { + // it seems that getting the rectangle for a sub item only works for list view which are in Details view + if (this.View != View.Details) { + return Rectangle.Empty; + } + if (itemIndex < 0 || itemIndex >= this.Items.Count) { + throw new ArgumentOutOfRangeException("itemIndex", SR.GetString(SR.InvalidArgument, "itemIndex", (itemIndex).ToString(CultureInfo.CurrentCulture))); + } + int subItemCount = Items[itemIndex].SubItems.Count; + + if (subItemIndex < 0 || subItemIndex >= subItemCount) { + throw new ArgumentOutOfRangeException("subItemIndex", SR.GetString(SR.InvalidArgument, "subItemIndex", (subItemIndex).ToString(CultureInfo.CurrentCulture))); + } + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(portion, (int)portion, (int)ItemBoundsPortion.Entire, (int)ItemBoundsPortion.ItemOnly)) + { + throw new InvalidEnumArgumentException("portion", (int)portion, typeof(ItemBoundsPortion)); + } + + if (this.Columns.Count == 0) { + return Rectangle.Empty; + } + + NativeMethods.RECT itemrect = new NativeMethods.RECT(); + itemrect.left = (int)portion; + itemrect.top = subItemIndex; + if (unchecked( (int) (long)SendMessage(NativeMethods.LVM_GETSUBITEMRECT, itemIndex, ref itemrect)) == 0) + throw new ArgumentException(SR.GetString(SR.InvalidArgument, + "itemIndex", + (itemIndex).ToString(CultureInfo.CurrentCulture))); + + Rectangle result = Rectangle.FromLTRB(itemrect.left, itemrect.top, itemrect.right, itemrect.bottom); + + return result; + } + + /// + /// + /// + public ListViewHitTestInfo HitTest(Point point) { + return HitTest(point.X, point.Y); + } + + /// + /// + /// + public ListViewHitTestInfo HitTest(int x, int y) { + if (!this.ClientRectangle.Contains(x, y)) { + return new ListViewHitTestInfo(null /*hitItem*/, null /*hitSubItem*/, ListViewHitTestLocations.None /*hitLocation*/); + } + + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + lvhi.pt_x = x; + lvhi.pt_y = y; + + int iItem; + + if (this.View == View.Details) { + iItem = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SUBITEMHITTEST, 0, lvhi)); + } else { + iItem = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_HITTEST, 0, lvhi)); + } + + ListViewItem item = (iItem == -1) ? null : Items[iItem]; + ListViewHitTestLocations location = ListViewHitTestLocations.None; + + if (item == null && (NativeMethods.LVHT_ABOVE & lvhi.flags) == NativeMethods.LVHT_ABOVE) { + location = (ListViewHitTestLocations)((MASK_HITTESTFLAG & lvhi.flags) | (int)ListViewHitTestLocations.AboveClientArea); + } + else if (item != null && (NativeMethods.LVHT_ONITEMSTATEICON & lvhi.flags) == NativeMethods.LVHT_ONITEMSTATEICON) { + location = (ListViewHitTestLocations)((MASK_HITTESTFLAG & lvhi.flags) | (int)ListViewHitTestLocations.StateImage); + } + else { + location = (ListViewHitTestLocations)lvhi.flags; + } + + if (this.View == View.Details && item != null) { + if (lvhi.iSubItem < item.SubItems.Count) { + return new ListViewHitTestInfo(item, item.SubItems[lvhi.iSubItem], location); + } else { + return new ListViewHitTestInfo(item, null, location); + } + } else { + return (new ListViewHitTestInfo(item, null, location)); + } + } + + private void InvalidateColumnHeaders() { + if (viewStyle == View.Details && IsHandleCreated) { + // + + IntPtr hwndHdr = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_GETHEADER, 0, 0); + if (hwndHdr != IntPtr.Zero) + SafeNativeMethods.InvalidateRect(new HandleRef(this, hwndHdr), null, true); + } + } + + /// + /// + /// Inserts a new Column into the ListView + /// + internal ColumnHeader InsertColumn(int index, ColumnHeader ch) { + return InsertColumn(index, ch, true); + } + + /// + /// + /// + /// + internal ColumnHeader InsertColumn(int index, ColumnHeader ch, bool refreshSubItems) { + if (ch == null) + throw new ArgumentNullException("ch"); + if (ch.OwnerListview != null) + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, ch.Text), "ch"); + + + int idx; + // in Tile view the ColumnHeaders collection is used for the Tile Information + // recreate the handle in that case + if (IsHandleCreated && this.View != View.Tile) { + idx = InsertColumnNative(index, ch); + } + else { + idx = index; + } + + // First column must be left aligned + + if (-1 == idx) + throw new InvalidOperationException(SR.GetString(SR.ListViewAddColumnFailed)); + + // Add the column to our internal array + int columnCount = (columnHeaders == null ? 0 : columnHeaders.Length); + if (columnCount > 0) { + ColumnHeader[] newHeaders = new ColumnHeader[columnCount + 1]; + if (columnCount > 0) + System.Array.Copy(columnHeaders, 0, newHeaders, 0, columnCount); + columnHeaders = newHeaders; + } + else + columnHeaders = new ColumnHeader[1]; + + if (idx < columnCount) { + System.Array.Copy(columnHeaders, idx, columnHeaders, idx + 1, columnCount - idx); + } + columnHeaders[idx] = ch; + ch.OwnerListview = this; + + // in Tile view the ColumnHeaders collection is used for the Tile Information + // recreate the handle in that case + if (ch.ActualImageIndex_Internal != -1 && this.IsHandleCreated && this.View != View.Tile) { + SetColumnInfo(NativeMethods.LVCF_IMAGE, ch); + } + + + // update the DisplayIndex for each column + // we are only setting an integer in the ColumnHeader, this is not expensive + int[] indices = new int[this.Columns.Count]; + for (int i = 0; i < this.Columns.Count; i ++) { + ColumnHeader hdr = this.Columns[i]; + if (hdr == ch) { + // the newly added column + hdr.DisplayIndexInternal = index; + } else if (hdr.DisplayIndex >= index) { + hdr.DisplayIndexInternal ++; + } + indices[i] = hdr.DisplayIndexInternal; + } + + SetDisplayIndices( indices ); + +#if DEBUG + CheckDisplayIndices(); +#endif + // in Tile view the ColumnHeaders collection is used for the Tile Information + // recreate the handle in that case + if (this.IsHandleCreated && this.View == View.Tile) { + this.RecreateHandleInternal(); + } else if (IsHandleCreated && refreshSubItems) + RealizeAllSubItems(); + + return ch; + } + + private int InsertColumnNative(int index, ColumnHeader ch) { + NativeMethods.LVCOLUMN_T lvColumn = new NativeMethods.LVCOLUMN_T(); + lvColumn.mask = NativeMethods.LVCF_FMT | NativeMethods.LVCF_TEXT | NativeMethods.LVCF_WIDTH;// | NativeMethods.LVCF_ORDER | NativeMethods.LVCF_IMAGE; + + if (ch.OwnerListview != null && ch.ActualImageIndex_Internal != -1) { + lvColumn.mask |= NativeMethods.LVCF_IMAGE; + lvColumn.iImage = ch.ActualImageIndex_Internal; + } + + lvColumn.fmt = (int)ch.TextAlign; + lvColumn.cx = ch.Width; + lvColumn.pszText = ch.Text; + + return (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_INSERTCOLUMN, index, lvColumn); + } + + // when the user adds a group, this helper method makes sure that all the items + // in the list view are parented by a group - be it the DefaultGroup or some other group + internal void InsertGroupInListView(int index, ListViewGroup group) { + Debug.Assert(this.groups != null && this.groups.Count > 0, "this method should be used only when the user adds a group, not when we add our own DefaultGroup"); + Debug.Assert(group != this.DefaultGroup, "this method should be used only when the user adds a group, not when we add our own DefaultGroup"); + + // the first time we add a group we have to group the items in the Default Group + bool groupItems = (this.groups.Count == 1) && this.GroupsEnabled; + + UpdateGroupView(); + EnsureDefaultGroup(); + InsertGroupNative(index, group); + + // take all the list view items which don't belong to any group and put them in the default group + // + if (groupItems) { + for (int i = 0; i < this.Items.Count; i ++) { + ListViewItem item = this.Items[i]; + if (item.Group == null) { + item.UpdateStateToListView(item.Index); + } + } + } + +#if DEBUG + // sanity check: all the items in the list view should have a group ID + if (this.GroupsEnabled) { + for (int i = 0; i < this.Items.Count; i ++) { + ListViewItem item = this.Items[i]; + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.iItem = item.Index; + lvItem.mask = NativeMethods.LVIF_GROUPID; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_GETITEM, 0, ref lvItem); + Debug.Assert(lvItem.iGroupId != -1, "there is a list view item which is not parented"); + } + } +#endif + } + + // does the Win32 part of the job of inserting the group + private void InsertGroupNative(int index, ListViewGroup group) { + Debug.Assert(IsHandleCreated,"InsertGroupNative precondition: list-view handle must be created"); + Debug.Assert(group == DefaultGroup || this.Groups.Contains(group),"Make sure ListView.Groups contains this group before adding the native LVGROUP. Otherwise, custom-drawing may break."); + + NativeMethods.LVGROUP lvgroup = new NativeMethods.LVGROUP(); + try + { + lvgroup = GetLVGROUP(group); + int retval = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this,this.Handle),NativeMethods.LVM_INSERTGROUP,index,lvgroup); + Debug.Assert(retval != -1,"Failed to insert group"); + } + finally + { + DestroyLVGROUP(lvgroup); + } + } + + /// + /// + /// Inserts a new ListViewItem into the ListView. The item will be inserted + /// either in the correct sorted position, or, if no sorting is set, at the + /// position indicated by the index parameter. + /// + private void InsertItems(int displayIndex, ListViewItem[] items, bool checkHosting) { + + if (items == null || items.Length == 0) { + return; + } + + if (this.IsHandleCreated && this.Items.Count == 0 && this.View == View.SmallIcon && this.ComctlSupportsVisualStyles) + { + this.FlipViewToLargeIconAndSmallIcon = true; + } + + // if we're in the middle of a Begin/EndUpdate, just push the items into our array list + // as they'll get processed on EndUpdate. + // + if (updateCounter > 0 && Properties.GetObject(PropDelayedUpdateItems) != null) { + // CheckHosting. + if (checkHosting) { + for (int i = 0; i < items.Length; i ++) { + if (items[i].listView != null) { + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, items[i].Text), "item"); + } + } + } + + ArrayList itemList = (ArrayList)Properties.GetObject(PropDelayedUpdateItems); + Debug.Assert(itemList != null, "In Begin/EndUpdate with no delayed array!"); + if (itemList != null) { + itemList.AddRange(items); + } + + // vsw 518637: add the list view item to the list view + // this way we can retrieve the item's index inside BeginUpdate/EndUpdate + for (int i = 0; i < items.Length; i ++) { + items[i].Host(this, GenerateUniqueID(), -1); + } + + this.FlipViewToLargeIconAndSmallIcon = false; + + return; + } + + // loop through the items and give them id's so we can identify them later. + // + for (int i = 0; i < items.Length; i++) { + ListViewItem item = items[i]; + + if (checkHosting && item.listView != null) { + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, item.Text), "item"); + } + + // create an ID.. + // + int itemID = GenerateUniqueID(); + Debug.Assert(!listItemsTable.ContainsKey(itemID), "internal hash table inconsistent -- inserting item, but it's already in the hash table"); + listItemsTable.Add(itemID, item); + + itemCount++; + item.Host(this,itemID, -1); + + // if there's no handle created, just ad them to our list items array. + // + if (!IsHandleCreated) { + Debug.Assert(listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + listItemsArray.Insert(displayIndex + i, item); + } + } + + // finally if the handle is created, do the actual add into the real list view + // + if (IsHandleCreated) { + Debug.Assert(listItemsArray == null, "listItemsArray not null, even though handle created"); + InsertItemsNative(displayIndex, items); + } + + Invalidate(); + ArrangeIcons(alignStyle); + + // Microsoft: this is bad for perf...I don't think we need this here. + // Any newly added items shoul dhave the correct location. + // UpdateListViewItemsLocations(); + + + // Update sorted order + if (!VirtualMode) { + Sort(); + } + + Debug.Assert(!this.FlipViewToLargeIconAndSmallIcon, "if we added even 1 item then we should have been done w/ FlipViewToLargeIconAndSmallIcon"); + } + + + /// + /// Inserts a new ListViewItem into the list view itself. + /// This only will be called when the Handle has been created for the list view. + /// This method loops through the items, sets up their state then adds them. + /// + private int InsertItemsNative(int index, ListViewItem[] items) { + if (items == null || items.Length == 0) { + return 0; + } + Debug.Assert(IsHandleCreated, "InsertItemsNative precondition: list-view handle must be created"); + + // Much more efficient to call the native insert with max + 1, than with max. The + 1 + // for the display index accounts for itemCount++ above. + // + if (index == itemCount - 1) { + index++; + } + + // Create and add the LVITEM + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + int actualIndex = -1; + IntPtr hGlobalColumns = IntPtr.Zero; + int maxColumns = 0; + this.listViewState1[LISTVIEWSTATE1_insertingItemsNatively] = true; + + try { + // Set the count of items first. + // + SendMessage(NativeMethods.LVM_SETITEMCOUNT, itemCount, 0); + + // Now add the items. + // + for (int i = 0; i < items.Length; i++) { + ListViewItem li = items[i]; + + Debug.Assert(this.Items.Contains(li), "Make sure ListView.Items contains this item before adding the native LVITEM. Otherwise, custom-drawing may break."); + + lvItem.Reset(); + lvItem.mask = NativeMethods.LVIF_TEXT | NativeMethods.LVIF_IMAGE | NativeMethods.LVIF_PARAM | NativeMethods.LVIF_INDENT; + lvItem.iItem = index + i; + lvItem.pszText = li.Text; + lvItem.iImage = li.ImageIndexer.ActualIndex; + lvItem.iIndent = li.IndentCount; + lvItem.lParam = (IntPtr)li.ID; + + if (GroupsEnabled) { + lvItem.mask |= NativeMethods.LVIF_GROUPID; + lvItem.iGroupId = GetNativeGroupId(li); + + #if DEBUG + Debug.Assert(SendMessage(NativeMethods.LVM_ISGROUPVIEWENABLED, 0, 0) != IntPtr.Zero, "Groups not enabled"); + Debug.Assert(SendMessage(NativeMethods.LVM_HASGROUP, lvItem.iGroupId, 0) != IntPtr.Zero, "Doesn't contain group id: " + lvItem.iGroupId.ToString(CultureInfo.InvariantCulture)); + #endif + } + + lvItem.mask |= NativeMethods.LVIF_COLUMNS; + lvItem.cColumns = this.columnHeaders != null ? Math.Min(MAXTILECOLUMNS, this.columnHeaders.Length) : 0; + + // make sure that our columns memory is big enough. + // if not, then realloc it. + // + if (lvItem.cColumns > maxColumns || hGlobalColumns == IntPtr.Zero) { + if (hGlobalColumns != IntPtr.Zero) { + Marshal.FreeHGlobal(hGlobalColumns); + } + hGlobalColumns = Marshal.AllocHGlobal(lvItem.cColumns * Marshal.SizeOf(typeof(int))); + maxColumns = lvItem.cColumns; + } + + // now build and copy in the column indexes. + // + lvItem.puColumns = hGlobalColumns; + int[] columns = new int[lvItem.cColumns]; + for(int c = 0; c < lvItem.cColumns; c++) { + columns[c] = c + 1; + } + Marshal.Copy(columns, 0, lvItem.puColumns, lvItem.cColumns); + + // Inserting an item into a ListView with checkboxes causes one or more + // item check events to be fired for the newly added item. + // Therefore, we disable the item check event handler temporarily. + // + ItemCheckEventHandler oldOnItemCheck = onItemCheck; + onItemCheck = null; + + int insertIndex; + + try { + + li.UpdateStateToListView(lvItem.iItem, ref lvItem, false); + + insertIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_INSERTITEM, 0, ref lvItem); + if (actualIndex == -1) { + actualIndex = insertIndex; + + // and update our starting index. so we're going from the same point. + // + index = actualIndex; + } + } finally { + + // Restore the item check event handler. + // + onItemCheck = oldOnItemCheck; + } + + if (-1 == insertIndex) { + throw new InvalidOperationException(SR.GetString(SR.ListViewAddItemFailed)); + } + + // add all sub items + for (int nItem = 1; nItem < li.SubItems.Count; ++nItem) { + SetItemText(insertIndex, nItem, li.SubItems[nItem].Text, ref lvItem); + } + + // PERF. + // Use StateSelected in order to avoid a call into the native list view. + if (li.StateImageSet || li.StateSelected) { + // lvItem.state and lvItem.stateMask are set when the lvItem is updated in UpdateStateToListView call. + SetItemState(insertIndex, lvItem.state, lvItem.stateMask); + } + } + } + finally { + if (hGlobalColumns != IntPtr.Zero) { + Marshal.FreeHGlobal(hGlobalColumns); + } + this.listViewState1[LISTVIEWSTATE1_insertingItemsNatively] = false; + } + + if (this.listViewState1[LISTVIEWSTATE1_selectedIndexChangedSkipped]) + { + // VSWhidbey 549967 - SelectedIndexChanged event was delayed + this.listViewState1[LISTVIEWSTATE1_selectedIndexChangedSkipped] = false; + OnSelectedIndexChanged(EventArgs.Empty); + } + + if (this.FlipViewToLargeIconAndSmallIcon) { + this.FlipViewToLargeIconAndSmallIcon = false; + + this.View = View.LargeIcon; + this.View = View.SmallIcon; + } + + return actualIndex; + } + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + + bool isInputKey = base.IsInputKey(keyData); + if (isInputKey) + return true; + + if (listViewState[LISTVIEWSTATE_inLabelEdit]) { + switch (keyData & Keys.KeyCode) { + case Keys.Return: + case Keys.Escape: + return true; + } + } + + return false; + } + + private void LargeImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + IntPtr handle = (LargeImageList == null) ? IntPtr.Zero : LargeImageList.Handle; + SendMessage(NativeMethods.LVM_SETIMAGELIST, (IntPtr) NativeMethods.LVSIL_NORMAL, handle); + + ForceCheckBoxUpdate(); + } + } + + private void LargeImageListChangedHandle(object sender, EventArgs e) { + if (!this.VirtualMode && (null != sender) && (sender == imageListLarge) && IsHandleCreated) { + foreach (ListViewItem item in Items) { + if (item.ImageIndexer.ActualIndex != -1 && item.ImageIndexer.ActualIndex >= imageListLarge.Images.Count) { + SetItemImage(item.Index,imageListLarge.Images.Count - 1); + } + else { + SetItemImage(item.Index,item.ImageIndexer.ActualIndex); + } + } + } + } + + internal void ListViewItemToolTipChanged(ListViewItem item) { + if (this.IsHandleCreated) { + // If we reset the item text then we also reset the tool tip text + // + SetItemText(item.Index, 0 /*subItemIndex*/, item.Text); + } + } + + private void LvnBeginDrag(MouseButtons buttons, NativeMethods.NMLISTVIEW nmlv) { + ListViewItem item = Items[nmlv.iItem]; + OnItemDrag(new ItemDragEventArgs(buttons, item)); + } + + /// + /// + /// Fires the afterLabelEdit event. + /// + protected virtual void OnAfterLabelEdit(LabelEditEventArgs e) { + if (onAfterLabelEdit != null) onAfterLabelEdit(this, e); + } + + /// + protected override void OnBackgroundImageChanged(EventArgs e) { + if (IsHandleCreated) { + SetBackgroundImage(); + } + base.OnBackgroundImageChanged(e); + } + + /// + /// + /// We keep track of if we've hovered already so we don't fire multiple hover events + /// + /// + protected override void OnMouseLeave(EventArgs e) { + hoveredAlready = false; + base.OnMouseLeave(e); + } + + /// + /// + /// In order for the MouseHover event to fire for each item in a ListView, + /// the item the mouse is hovering over is found. Each time a new item is hovered + /// over a new event is raised. + /// + protected override void OnMouseHover(EventArgs e) { + + /// Hover events need to be caught for each node + /// within the TreeView so the appropriate + /// NodeHovered event can be raised. + + ListViewItem item = null; + + if (this.Items.Count > 0) { + // APPCOMPAT + // V1.* users implement virtualization by communicating directly to the native ListView and by passing our virtualization implementation. + // In that case, the native list view may have an item under the mouse even if our wrapper thinks the item count is 0. + // And that may cause GetItemAt to throw an out of bounds exception. + // See VSW 418791. + + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + item = GetItemAt(pos.X, pos.Y); + } + + if (item != prevHoveredItem && item != null) { + OnItemMouseHover(new ListViewItemMouseHoverEventArgs(item)); + prevHoveredItem = item; + } + + if (!hoveredAlready) { + base.OnMouseHover(e); + hoveredAlready = true; + } + + ResetMouseEventArgs(); + + } + + /// + /// + /// Fires the beforeLabelEdit event. + /// + protected virtual void OnBeforeLabelEdit(LabelEditEventArgs e) { + if (onBeforeLabelEdit != null) onBeforeLabelEdit(this, e); + } + + /// + /// + /// + protected virtual void OnCacheVirtualItems(CacheVirtualItemsEventArgs e) + { + CacheVirtualItemsEventHandler handler = (CacheVirtualItemsEventHandler)Events[EVENT_CACHEVIRTUALITEMS]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// Fires the columnClick event. + /// + protected virtual void OnColumnClick(ColumnClickEventArgs e) { + if (onColumnClick != null) onColumnClick(this, e); + } + + /// + /// + /// Fires the column header rearranged event. + /// + protected virtual void OnColumnReordered(ColumnReorderedEventArgs e) { + ColumnReorderedEventHandler handler = (ColumnReorderedEventHandler) Events[EVENT_COLUMNREORDERED]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// Fires the column width changing event. + /// + protected virtual void OnColumnWidthChanged(ColumnWidthChangedEventArgs e) { + ColumnWidthChangedEventHandler handler = (ColumnWidthChangedEventHandler)Events[EVENT_COLUMNWIDTHCHANGED]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// Fires the column width changing event. + /// + protected virtual void OnColumnWidthChanging(ColumnWidthChangingEventArgs e) { + ColumnWidthChangingEventHandler handler = (ColumnWidthChangingEventHandler)Events[EVENT_COLUMNWIDTHCHANGING]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// Fires the DrawColumnHeader event. + /// + protected virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e) { + DrawListViewColumnHeaderEventHandler handler = (DrawListViewColumnHeaderEventHandler)Events[EVENT_DRAWCOLUMNHEADER]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// Fires the DrawItem event. + /// + protected virtual void OnDrawItem(DrawListViewItemEventArgs e) { + DrawListViewItemEventHandler handler = (DrawListViewItemEventHandler)Events[EVENT_DRAWITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// Fires the DrawSubItem event. + /// + protected virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e) { + DrawListViewSubItemEventHandler handler = (DrawListViewSubItemEventHandler)Events[EVENT_DRAWSUBITEM]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + + // When the user sets the font on the Form, the layout engine will compute the bounds for the native list view + // and AFTER that will set the window font on the native list view. + // That means that when the list view computed its item's position it did so w/ the previous font. + // The solution is to send LVM_UPDATE to the native list view EVEN if the list view is not in SmallIcon or LargeIcon. + // vsw 463436. + if (!VirtualMode && IsHandleCreated && AutoArrange) { + BeginUpdate(); + try { + SendMessage(NativeMethods.LVM_UPDATE, -1, 0); + } finally { + EndUpdate(); + } + } + + // If font changes and we have headers, they need to be expicitly invalidated + // + InvalidateColumnHeaders(); + } + + + /// + /// + /// + /// + protected override void OnHandleCreated(EventArgs e) { + // uncache the "ComctlSupportsVisualStyles" property on a handle creation + listViewState[LISTVIEWSTATE_comctlSupportsVisualStylesTested] = false; + + // don't persist flipViewToLargeIconAndSmallIcon accross handle recreations... + this.FlipViewToLargeIconAndSmallIcon = false; + + base.OnHandleCreated(e); + //ComCtl 5 has some bug fixes that, to enable, require us to send the control + //a CCM_SETVERSION with 5 as the version. The one we want in particular is + //the fix for the node clipping issue when a font is set by means of CDRF_NEWFONT. + //The fix is not perfect, but the behavior is better than before. + int version = unchecked((int)(long)SendMessage(NativeMethods.CCM_GETVERSION, 0, 0)); + if (version < 5) { + SendMessage(NativeMethods.CCM_SETVERSION, 5, 0); + } + UpdateExtendedStyles(); + RealizeProperties(); + int color = ColorTranslator.ToWin32(BackColor); + SendMessage(NativeMethods.LVM_SETBKCOLOR,0,color); + SendMessage(NativeMethods.LVM_SETTEXTCOLOR,0,ColorTranslator.ToWin32(base.ForeColor)); + + // vsw 496340. + // The native list view will not invalidate the entire list view item area if the BkColor is not CLR_NONE. + // This not noticeable if the customer paints the items w/ the same background color as the list view itself. + // However, if the customer paints the items w/ a color different from the list view's back color + // then when the user changes selection the native list view will not invalidate the entire list view item area. + SendMessage(NativeMethods.LVM_SETTEXTBKCOLOR, 0,NativeMethods.CLR_NONE); + + // LVS_NOSCROLL does not work well when the list view is in View.Details or in View.List modes. + // we have to set this style after the list view was created and before we position the native list view items. + // + if (!Scrollable) { + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + style |= NativeMethods.LVS_NOSCROLL; + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); + } + + // in VirtualMode we have to tell the list view to ask for the list view item's state image index + if (VirtualMode) { + int callbackMask = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_GETCALLBACKMASK, 0, 0)); + callbackMask |= NativeMethods.LVIS_STATEIMAGEMASK; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SETCALLBACKMASK, callbackMask, 0); + } + + if (ComctlSupportsVisualStyles) { + SendMessage(NativeMethods.LVM_SETVIEW,(int)viewStyle,0); + UpdateGroupView(); + + // Add groups + // + if (groups != null) { + for(int index = 0;index < groups.Count;index++) { + InsertGroupNative(index,groups[index]); + } + } + + // Set tile view settings + // + if (viewStyle == View.Tile) { + UpdateTileView(); + } + } + + ListViewHandleDestroyed = false; + + // Use a copy of the list items array so that we can maintain the (handle created || listItemsArray != null) invariant + // + ListViewItem[] listViewItemsToAdd = null; + if(listItemsArray != null) { + listViewItemsToAdd = (ListViewItem[])listItemsArray.ToArray(typeof(ListViewItem)); + listItemsArray = null; + } + + int columnCount = (columnHeaders == null ? 0 : columnHeaders.Length); + if(columnCount > 0) { + int[] indices = new int [columnHeaders.Length]; + int index = 0; + foreach(ColumnHeader column in columnHeaders) { + indices[index] = column.DisplayIndex; + InsertColumnNative(index++,column); + } + SetDisplayIndices( indices ); + } + + // make sure that we're not in a begin/end update call. + // + if (itemCount > 0 && listViewItemsToAdd != null) { + InsertItemsNative(0, listViewItemsToAdd); + } + + if (VirtualMode && VirtualListSize > -1 && !DesignMode) { + SendMessage(NativeMethods.LVM_SETITEMCOUNT, VirtualListSize, 0); + } + + if(columnCount > 0) { + UpdateColumnWidths(ColumnHeaderAutoResizeStyle.None); + } + + ArrangeIcons(alignStyle); + UpdateListViewItemsLocations(); + + if (!VirtualMode) { + Sort(); + } + if (ComctlSupportsVisualStyles && (InsertionMark.Index > 0)) { + InsertionMark.UpdateListView(); + } + + // + // When the handle is recreated, update the SavedCheckedItems. + // It is possible some checked items were added to the list view while its handle was null. + // + this.savedCheckedItems = null; + if (!this.CheckBoxes && !this.VirtualMode) { + for (int i = 0; i < this.Items.Count; i ++) { + if (this.Items[i].Checked) { + this.UpdateSavedCheckedItems(this.Items[i], true /*addItem*/); + } + } + } + + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnHandleDestroyed(EventArgs e) { + // don't save the list view items state when in virtual mode : it is the responsability of the + // user to cache the list view items in virtual mode + if (!Disposing && !VirtualMode) { + + int count = Items.Count; + for (int i = 0; i < count; i++) { + Items[i].UpdateStateFromListView(i, true); + } + + // Save away the selected and checked items + // + if (SelectedItems != null && !VirtualMode) { + // Create an array because the SelectedItems collection is tuned for CopyTo() + ListViewItem[] lviArr = new ListViewItem[SelectedItems.Count]; + SelectedItems.CopyTo(lviArr, 0); + savedSelectedItems = new List(lviArr.Length); + for (int i = 0; i < lviArr.Length; i ++) { + savedSelectedItems.Add(lviArr[i]); + } + } + Debug.Assert(listItemsArray == null, "listItemsArray not null, even though handle created"); + ListViewItem[] items = null; + ListViewItemCollection tempItems = Items; + + if (tempItems != null) { + items = new ListViewItem[tempItems.Count]; + tempItems.CopyTo(items, 0); + } + + if (items != null) { + listItemsArray = new ArrayList(items.Length); + listItemsArray.AddRange(items); + } + + ListViewHandleDestroyed = true; + } + + base.OnHandleDestroyed(e); + } + + /// + /// + /// Fires the itemActivate event. + /// + protected virtual void OnItemActivate(EventArgs e) { + if (onItemActivate != null) onItemActivate(this, e); + } + + /// + /// + /// This is the code that actually fires the KeyEventArgs. Don't + /// forget to call base.onItemCheck() to ensure that itemCheck vents + /// are correctly fired for all other keys. + /// + protected virtual void OnItemCheck(ItemCheckEventArgs ice) { + if (onItemCheck != null) { + onItemCheck(this, ice); + } + } + + /// + protected virtual void OnItemChecked(ItemCheckedEventArgs e) { + if (onItemChecked != null) { + onItemChecked(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnItemDrag(ItemDragEventArgs e) { + if (onItemDrag != null) onItemDrag(this, e); + } + + /// + /// + /// Fires the ItemMouseHover event. + /// + protected virtual void OnItemMouseHover(ListViewItemMouseHoverEventArgs e) { + if (onItemMouseHover != null) onItemMouseHover(this, e); + } + + /// + /// + /// Fires the ItemSelectionChanged event. + /// + protected virtual void OnItemSelectionChanged(ListViewItemSelectionChangedEventArgs e) { + ListViewItemSelectionChangedEventHandler eh = (ListViewItemSelectionChangedEventHandler)Events[EVENT_ITEMSELECTIONCHANGED]; + if (eh != null) + eh(this, e); + } + + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + + // We must do this because the list view control caches the parent + // handle and always sends notifications to the same handle. + if (IsHandleCreated) { + RecreateHandleInternal(); + } + } + + /// + /// + /// + protected override void OnResize(EventArgs e) { + // KB 137520: if the list view is in Details mode and it is not Scrollable, we need to reposition the column header control. + if (this.View == View.Details && !this.Scrollable && this.IsHandleCreated) { + PositionHeader(); + } + + base.OnResize(e); + } + + /// + /// + /// + protected virtual void OnRetrieveVirtualItem(RetrieveVirtualItemEventArgs e) { + RetrieveVirtualItemEventHandler handler = (RetrieveVirtualItemEventHandler) Events[EVENT_RETRIEVEVIRTUALITEM]; + if (handler != null) + handler(this, e); + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandleInternal(); + } + + EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Fires the search for virtual item event. + /// + protected virtual void OnSearchForVirtualItem(SearchForVirtualItemEventArgs e) { + SearchForVirtualItemEventHandler handler = (SearchForVirtualItemEventHandler) Events[EVENT_SEARCHFORVIRTUALITEM]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// Actually goes and fires the selectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.onSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnSelectedIndexChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; + if (handler != null) handler(this,e); + } + + /// + protected override void OnSystemColorsChanged(EventArgs e) { + base.OnSystemColorsChanged(e); + + if (IsHandleCreated) { + int color = ColorTranslator.ToWin32(BackColor); + SendMessage(NativeMethods.LVM_SETBKCOLOR, 0,color); + // We should probably be OK if we don't set the TEXTBKCOLOR to CLR_NONE. + // However, for the sake of being more robust, reset the TECTBKCOLOR to CLR_NONE when the system palette changes. + SendMessage(NativeMethods.LVM_SETTEXTBKCOLOR, 0,NativeMethods.CLR_NONE); + } + } + + /// + protected virtual void OnVirtualItemsSelectionRangeChanged(ListViewVirtualItemsSelectionRangeChangedEventArgs e) { + ListViewVirtualItemsSelectionRangeChangedEventHandler eh = (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events[EVENT_VIRTUALITEMSSELECTIONRANGECHANGED]; + if (eh != null) + eh(this, e); + } + + private unsafe void PositionHeader() { + IntPtr hdrHWND = UnsafeNativeMethods.GetWindow(new HandleRef(this, this.Handle), NativeMethods.GW_CHILD); + if (hdrHWND != IntPtr.Zero) { + + IntPtr prc = IntPtr.Zero; + IntPtr pwpos = IntPtr.Zero; + + prc = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.RECT))); + if (prc == IntPtr.Zero) { + return; + } + + try { + pwpos = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDOWPOS))); + + if (prc == IntPtr.Zero) { + // we could not allocate memory. + // return + return; + } + + UnsafeNativeMethods.GetClientRect(new HandleRef(this, this.Handle), prc); + + NativeMethods.HDLAYOUT hd = new NativeMethods.HDLAYOUT(); + hd.prc = prc; + hd.pwpos = pwpos; + + // get the layout information + UnsafeNativeMethods.SendMessage(new HandleRef(this, hdrHWND), NativeMethods.HDM_LAYOUT, 0, ref hd); + + // now take the information from the native wpos struct and put it into a managed WINDOWPOS + NativeMethods.WINDOWPOS wpos = (NativeMethods.WINDOWPOS) Marshal.PtrToStructure(pwpos, typeof(NativeMethods.WINDOWPOS)); + + // position the header control + SafeNativeMethods.SetWindowPos(new HandleRef(this, hdrHWND), + new HandleRef(this, wpos.hwndInsertAfter), + wpos.x, + wpos.y, + wpos.cx, + wpos.cy, + wpos.flags | NativeMethods.SWP_SHOWWINDOW); + } finally { + + // clean up our memory + if (prc != IntPtr.Zero) { + Marshal.FreeHGlobal(prc); + } + if (pwpos != IntPtr.Zero) { + Marshal.FreeHGlobal(pwpos); + } + } + } + } + + /// + /// + /// + /// + private void RealizeAllSubItems() { + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + for (int i = 0; i < itemCount; i++) { + int subItemCount = Items[i].SubItems.Count; + for (int j = 0; j < subItemCount; j++) { + SetItemText(i, j, Items[i].SubItems[j].Text, ref lvItem); + } + } + } + + /// + /// + /// + /// + protected void RealizeProperties() { + //Realize state information + Color c; + + c = BackColor; + if (c != SystemColors.Window) { + SendMessage(NativeMethods.LVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(c)); + } + c = ForeColor; + if (c != SystemColors.WindowText) + SendMessage(NativeMethods.LVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(c)); + + + if (null != imageListLarge) + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_NORMAL, imageListLarge.Handle); + + if (null != imageListSmall) + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_SMALL, imageListSmall.Handle); + + if (null != imageListState) { + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, imageListState.Handle); + } + } + + /// + /// + /// Forces the redraw of a range of listview items. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly) + { + if (this.VirtualMode) + { + if (startIndex < 0 || startIndex >= this.VirtualListSize) + { + throw new ArgumentOutOfRangeException("startIndex", SR.GetString(SR.InvalidArgument, "startIndex", (startIndex).ToString(CultureInfo.CurrentCulture))); + } + if (endIndex < 0 || endIndex >= this.VirtualListSize) + { + throw new ArgumentOutOfRangeException("endIndex", SR.GetString(SR.InvalidArgument, "endIndex", (endIndex).ToString(CultureInfo.CurrentCulture))); + } + } + else + { + if (startIndex < 0 || startIndex >= this.Items.Count) + { + throw new ArgumentOutOfRangeException("startIndex", SR.GetString(SR.InvalidArgument, "startIndex", (startIndex).ToString(CultureInfo.CurrentCulture))); + } + if (endIndex < 0 || endIndex >= this.Items.Count) + { + throw new ArgumentOutOfRangeException("endIndex", SR.GetString(SR.InvalidArgument, "endIndex", (endIndex).ToString(CultureInfo.CurrentCulture))); + } + } + if (startIndex > endIndex) + { + throw new ArgumentException(SR.GetString(SR.ListViewStartIndexCannotBeLargerThanEndIndex)); + } + if (this.IsHandleCreated) + { + int retval = (int) UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), + NativeMethods.LVM_REDRAWITEMS, + startIndex, endIndex); + Debug.Assert(retval != 0); + // ListView control seems to be bogus. Items affected need to be invalidated in LargeIcon and SmallIcons views. + if (this.View == View.LargeIcon || this.View == View.SmallIcon) + { + Rectangle rectInvalid = this.Items[startIndex].Bounds; + for (int index = startIndex+1; index <= endIndex; index++) + { + rectInvalid = Rectangle.Union(rectInvalid, this.Items[index].Bounds); + } + if (startIndex > 0) + { + rectInvalid = Rectangle.Union(rectInvalid, this.Items[startIndex - 1].Bounds); + } + else + { + rectInvalid.Width += rectInvalid.X; + rectInvalid.Height += rectInvalid.Y; + rectInvalid.X = rectInvalid.Y = 0; + } + if (endIndex < this.Items.Count - 1) + { + rectInvalid = Rectangle.Union(rectInvalid, this.Items[endIndex + 1].Bounds); + } + else + { + rectInvalid.Height += this.ClientRectangle.Bottom - rectInvalid.Bottom; + rectInvalid.Width += this.ClientRectangle.Right - rectInvalid.Right; + } + if (this.View == View.LargeIcon) + { + rectInvalid.Inflate(1, this.Font.Height + 1); + } + Invalidate(rectInvalid); + } + if (!invalidateOnly) + { + Update(); + } + } + } + + // makes sure that the list view items which are w/o a listView group are parented to the DefaultGroup - if necessary + // and then tell win32 to remove this group + internal void RemoveGroupFromListView(ListViewGroup group) { + EnsureDefaultGroup(); + + foreach(ListViewItem item in group.Items) { + if(item.ListView == this) { + item.UpdateStateToListView(item.Index); + } + } + + RemoveGroupNative(group); + + UpdateGroupView(); + } + + // does the job of telling win32 listView to remove this group + private void RemoveGroupNative(ListViewGroup group) { + Debug.Assert(IsHandleCreated,"RemoveGroupNative precondition: list-view handle must be created"); + Debug.Assert(ComctlSupportsVisualStyles, "we should have checked this already"); + int retval = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_REMOVEGROUP, group.ID, IntPtr.Zero); + Debug.Assert(retval != -1,"Failed to remove group"); + + // it is the job of whoever deletes this group to also turn off grouping if this was the last + // group deleted + return; + } + + private void Scroll(int fromLVItem, int toLVItem) { + int scrollY = GetItemPosition(toLVItem).Y - GetItemPosition(fromLVItem).Y; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SCROLL, 0, scrollY); + } + + private void SetBackgroundImage() { + // needed for OleInitialize + Application.OleRequired(); + + NativeMethods.LVBKIMAGE lvbkImage = new NativeMethods.LVBKIMAGE(); + lvbkImage.xOffset = 0; + lvbkImage.yOffset = 0; + + // first, is there an existing temporary file to delete, remember its name + // so that we can delete it if the list control doesn't... + string fileNameToDelete = this.backgroundImageFileName; + + if (this.BackgroundImage != null) { + + // the list view needs these permissions when the app runs on an UNC share + // and the list view creates / destroys temporary files for its background image + + // SECREVIEW : Safe to assert FileIO & Environment permissions here, just creating/deleting temp files. + // + EnvironmentPermission envPermission = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "TEMP"); + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + System.Security.PermissionSet permSet = new System.Security.PermissionSet(PermissionState.Unrestricted); + permSet.AddPermission(envPermission); + permSet.AddPermission(fiop); + permSet.Assert(); + + // save the image to a temporary file name + try { + string tempDirName = System.IO.Path.GetTempPath(); + System.Text.StringBuilder sb = new System.Text.StringBuilder(1024); + UnsafeNativeMethods.GetTempFileName(tempDirName, this.GenerateRandomName(), 0, sb); + + this.backgroundImageFileName = sb.ToString(); + + this.BackgroundImage.Save(this.backgroundImageFileName, System.Drawing.Imaging.ImageFormat.Bmp); + } finally { + System.Security.PermissionSet.RevertAssert(); + } + + lvbkImage.pszImage = this.backgroundImageFileName; + lvbkImage.cchImageMax = this.backgroundImageFileName.Length + 1; + lvbkImage.ulFlags = NativeMethods.LVBKIF_SOURCE_URL; + if (BackgroundImageTiled) + lvbkImage.ulFlags |= NativeMethods.LVBKIF_STYLE_TILE; + else + lvbkImage.ulFlags |= NativeMethods.LVBKIF_STYLE_NORMAL; + + } else { + lvbkImage.ulFlags = NativeMethods.LVBKIF_SOURCE_NONE; + this.backgroundImageFileName = String.Empty; + } + + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SETBKIMAGE, 0, lvbkImage); + + if (String.IsNullOrEmpty(fileNameToDelete)) { + return; + } + + // we need to cause a paint message on the win32 list view. This way the win 32 list view gives up + // its reference to the previous image file it was hanging on to. + // vsWhidbey 243708 + + // 8 strings should be good enough for us + if (this.bkImgFileNames == null) { + this.bkImgFileNames = new string[BKIMGARRAYSIZE]; + this.bkImgFileNamesCount = -1; + } + + if (this.bkImgFileNamesCount == BKIMGARRAYSIZE - 1) { + // it should be fine to delete the file name that was added first. + // if it's not fine, then increase BKIMGARRAYSIZE + this.DeleteFileName(this.bkImgFileNames[0]); + this.bkImgFileNames[0] = this.bkImgFileNames[1]; + this.bkImgFileNames[1] = this.bkImgFileNames[2]; + this.bkImgFileNames[2] = this.bkImgFileNames[3]; + this.bkImgFileNames[3] = this.bkImgFileNames[4]; + this.bkImgFileNames[4] = this.bkImgFileNames[5]; + this.bkImgFileNames[5] = this.bkImgFileNames[6]; + this.bkImgFileNames[6] = this.bkImgFileNames[7]; + this.bkImgFileNames[7] = null; + + this.bkImgFileNamesCount --; + } + + this.bkImgFileNamesCount ++; + this.bkImgFileNames[this.bkImgFileNamesCount] = fileNameToDelete; + + // now force the paint + this.Refresh(); + } + + /// + /// + /// + /// + internal void SetColumnInfo(int mask, ColumnHeader ch) { + if (IsHandleCreated) { + Debug.Assert((mask & ~(NativeMethods.LVCF_FMT | NativeMethods.LVCF_TEXT | NativeMethods.LVCF_IMAGE)) == 0, "Unsupported mask in setColumnInfo"); + NativeMethods.LVCOLUMN lvColumn = new NativeMethods.LVCOLUMN(); + lvColumn.mask = mask; + + if ((mask & NativeMethods.LVCF_IMAGE) != 0 || (mask & NativeMethods.LVCF_FMT) != 0) { + // When we set the ImageIndex we also have to alter the column format. + // This means that we have to include the TextAlign into the column format. + + lvColumn.mask |= NativeMethods.LVCF_FMT; + + if (ch.ActualImageIndex_Internal > -1) { + // you would think that setting iImage would be enough. + // actually we also have to set the format to include LVCFMT_IMAGE + lvColumn.iImage = ch.ActualImageIndex_Internal; + lvColumn.fmt |= NativeMethods.LVCFMT_IMAGE; + } + + lvColumn.fmt |= (int) ch.TextAlign; + } + + if ((mask & NativeMethods.LVCF_TEXT) != 0) { + lvColumn.pszText = Marshal.StringToHGlobalAuto(ch.Text); + } + + int retval = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETCOLUMN, ch.Index, lvColumn); + if ((mask & NativeMethods.LVCF_TEXT) != 0) { + Marshal.FreeHGlobal(lvColumn.pszText); + } + + if (0 == retval) + throw new InvalidOperationException(SR.GetString(SR.ListViewColumnInfoSet)); + // vsw 383220: when running on AMD64 the list view does not invalidate the column header. + // So we do it ourselves. + InvalidateColumnHeaders(); + } + } + + /// + /// + /// Setting width is a special case 'cuz LVM_SETCOLUMNWIDTH accepts more values + /// for width than LVM_SETCOLUMN does. + /// + /// + internal void SetColumnWidth(int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize) { + if ((columnIndex < 0) || + (columnIndex >= 0 && this.columnHeaders == null) || + (columnIndex >= this.columnHeaders.Length)) { + throw new ArgumentOutOfRangeException("columnIndex", SR.GetString(SR.InvalidArgument, "columnIndex", (columnIndex).ToString(CultureInfo.CurrentCulture))); + } + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(headerAutoResize, (int)headerAutoResize, (int)ColumnHeaderAutoResizeStyle.None, (int)ColumnHeaderAutoResizeStyle.ColumnContent)) + { + throw new InvalidEnumArgumentException("headerAutoResize", (int)headerAutoResize, typeof(ColumnHeaderAutoResizeStyle)); + } + + + int width = 0; + int compensate = 0; + + if (headerAutoResize == ColumnHeaderAutoResizeStyle.None) { + width = this.columnHeaders[columnIndex].WidthInternal; + + // If the width maps to a LVCSW_ const, then native control will autoresize. + // We may need to compensate for that. + if (width == NativeMethods.LVSCW_AUTOSIZE_USEHEADER) { + headerAutoResize = ColumnHeaderAutoResizeStyle.HeaderSize; + } else if (width == NativeMethods.LVSCW_AUTOSIZE) { + headerAutoResize = ColumnHeaderAutoResizeStyle.ColumnContent; + } + } + + if (headerAutoResize == ColumnHeaderAutoResizeStyle.HeaderSize) { + compensate = this.CompensateColumnHeaderResize(columnIndex, false /*columnHeaderResizeCancelled*/); + width = NativeMethods.LVSCW_AUTOSIZE_USEHEADER; + } else if (headerAutoResize == ColumnHeaderAutoResizeStyle.ColumnContent) { + compensate = this.CompensateColumnHeaderResize(columnIndex, false /*columnHeaderResizeCancelled*/); + width = NativeMethods.LVSCW_AUTOSIZE; + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETCOLUMNWIDTH, columnIndex, NativeMethods.Util.MAKELPARAM(width, 0)); + } + + if (this.IsHandleCreated && + (headerAutoResize == ColumnHeaderAutoResizeStyle.ColumnContent || + headerAutoResize == ColumnHeaderAutoResizeStyle.HeaderSize)) { + if (compensate != 0) { + int newWidth = this.columnHeaders[columnIndex].Width + compensate; + SendMessage(NativeMethods.LVM_SETCOLUMNWIDTH, columnIndex, NativeMethods.Util.MAKELPARAM(newWidth, 0)); + } + } + } + + private void SetColumnWidth(int index, int width) { + if (IsHandleCreated) { + SendMessage(NativeMethods.LVM_SETCOLUMNWIDTH, index, NativeMethods.Util.MAKELPARAM(width,0)); + } + } + + // set the display indices of the listview columns + private void SetDisplayIndices(int[] indices) { + + int[] orderedColumns = new int[ indices.Length ]; + for (int i=0; i + /// + /// This is a new internal method added which is used by ListView Item to set + /// the check state of the item in the savedCheckedItems collection + /// if the ListView Checkboxes is OFF. + /// + /// + internal void UpdateSavedCheckedItems(ListViewItem item, bool addItem) + { + if (addItem && this.savedCheckedItems == null) { + this.savedCheckedItems = new List(); + } + + if (addItem) { + this.savedCheckedItems.Add(item); + } else if (this.savedCheckedItems != null) { + Debug.Assert(this.savedCheckedItems.Contains(item), "somehow we lost track of one item"); + this.savedCheckedItems.Remove(item); + } + #if FALSE + if (savedCheckedItems != null && savedCheckedItems.Length > 0) { + ListViewItem[] newSavedCheckedItems; + int index = 0; + if (!addItem) { + int current = 0; + newSavedCheckedItems = new ListViewItem[savedCheckedItems.Length -1]; + for(index = 0 ; index < savedCheckedItems.Length ; index++) + { + if (savedCheckedItems[index] == item) { + current = 1; + continue; + } + newSavedCheckedItems[index - current] = savedCheckedItems[index]; + } + } + else { + newSavedCheckedItems = new ListViewItem[savedCheckedItems.Length +1]; + for(index = 0 ; index < savedCheckedItems.Length ; index++) + { + newSavedCheckedItems[index] = savedCheckedItems[index]; + } + newSavedCheckedItems[index] = item; + } + savedCheckedItems = newSavedCheckedItems; + } + else if (addItem) { + savedCheckedItems = new ListViewItem[1]; + savedCheckedItems[0] = item; + } + #endif // FALSE + } + + /// + /// + /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. + /// + /// + internal void SetToolTip(ToolTip toolTip, string toolTipCaption) { + this.toolTipCaption = toolTipCaption; + //native ListView expects tooltip HWND as a wParam and ignores lParam + IntPtr oldHandle = UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); + UnsafeNativeMethods.DestroyWindow(new HandleRef(null, oldHandle)); + } + + internal void SetItemImage(int index, int image) { + if (index < 0 || ((this.VirtualMode && index >= this.VirtualListSize) || (!this.VirtualMode && index >= itemCount))) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + if (this.IsHandleCreated) { + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.mask = NativeMethods.LVIF_IMAGE; + lvItem.iItem = index; + lvItem.iImage = image; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETITEM, 0, ref lvItem); + } + } + + internal void SetItemIndentCount(int index, int indentCount) { + if (index < 0 || ((this.VirtualMode && index >= this.VirtualListSize) || (!this.VirtualMode && index >= itemCount))) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + if (this.IsHandleCreated) { + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.mask = NativeMethods.LVIF_INDENT; + lvItem.iItem = index; + lvItem.iIndent = indentCount; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETITEM, 0, ref lvItem); + } + } + + internal void SetItemPosition(int index, int x, int y) { + if (VirtualMode) + return; + if (index < 0 || index >= itemCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + Debug.Assert(IsHandleCreated, "How did we add items without a handle?"); + + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = x; + pt.y = y; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETITEMPOSITION32, index, pt); + } + + internal void SetItemState(int index, int state, int mask) { + if (index < -1 || ((this.VirtualMode && index >= this.VirtualListSize) || (!this.VirtualMode && index >= itemCount))) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + Debug.Assert(index == -1 || this.IsHandleCreated, "How did we add items without a handle?"); + + if (this.IsHandleCreated) { + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.mask = NativeMethods.LVIF_STATE; + lvItem.state = state; + lvItem.stateMask = mask; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETITEMSTATE, index, ref lvItem); + } + } + + internal void SetItemText(int itemIndex, int subItemIndex, string text) { + + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + SetItemText(itemIndex, subItemIndex, text, ref lvItem); + } + + /// + /// For perf, allow a LVITEM to be passed in so we can reuse in tight loops. + /// + private void SetItemText(int itemIndex, int subItemIndex, string text, ref NativeMethods.LVITEM lvItem) { + + Debug.Assert(IsHandleCreated, "SetItemText with no handle"); + + // bug 185563 : a listView in list mode will not increase the item width if the length of the item's text increases + // We have to make sure that the width of the "column" contains the string + if (this.View == View.List && subItemIndex == 0) { + int colWidth = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_GETCOLUMNWIDTH, 0, 0)); + + Graphics g = this.CreateGraphicsInternal(); + int textWidth = 0; + + try { + textWidth = Size.Ceiling(g.MeasureString(text, this.Font)).Width; + } + finally { + g.Dispose(); + } + + if (textWidth > colWidth) { + SetColumnWidth(0, textWidth); + } + } + + lvItem.mask = NativeMethods.LVIF_TEXT; + lvItem.iItem = itemIndex; + lvItem.iSubItem = subItemIndex; + lvItem.pszText = text; + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SETITEMTEXT, itemIndex, ref lvItem); + } + + // + // ComCtl32 list view uses a selection mark to keep track of selection state - iMark. + // ComCtl32 list view updates iMark only when the user hovers over the item. + // This means that if we programatically set the selection item, then the list view will not update + // its selection mark. + // So we explicitly set the selection mark. + // + internal void SetSelectionMark(int itemIndex) { + if (itemIndex < 0 || itemIndex >= this.Items.Count) { + return; + } + SendMessage(NativeMethods.LVM_SETSELECTIONMARK, 0, itemIndex); + } + + private void SmallImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + IntPtr handle = (SmallImageList == null) ? IntPtr.Zero : SmallImageList.Handle; + SendMessage(NativeMethods.LVM_SETIMAGELIST, (IntPtr) NativeMethods.LVSIL_SMALL, handle); + + ForceCheckBoxUpdate(); + } + } + + /// + /// + /// Updated the sorted order + /// + public void Sort() { + if (VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewSortNotAllowedInVirtualListView)); + } + + ApplyUpdateCachedItems(); + if (IsHandleCreated && listItemSorter != null) { + NativeMethods.ListViewCompareCallback callback = new NativeMethods.ListViewCompareCallback(this.CompareFunc); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_SORTITEMS, IntPtr.Zero, callback); + } + } + + private void StateImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + IntPtr handle = IntPtr.Zero; + if (StateImageList != null) { + handle = imageListState.Handle; + } + SendMessage(NativeMethods.LVM_SETIMAGELIST, (IntPtr) NativeMethods.LVSIL_STATE, handle); + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + + if (listItemsArray != null) { + s += ", Items.Count: " + listItemsArray.Count.ToString(CultureInfo.CurrentCulture); + if (listItemsArray.Count > 0) { + string z = listItemsArray[0].ToString(); + string txt = (z.Length > 40) ? z.Substring(0, 40) : z; + s += ", Items[0]: " + txt; + } + } + else if (Items != null) { + s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); + if (Items.Count > 0 && !VirtualMode) { + //Debug.Assert(Items[0] != null, "Why is there a null item in the list view item collection?"); + string z = (Items[0] == null) ? "null" : Items[0].ToString(); + string txt = (z.Length > 40) ? z.Substring(0, 40) : z; + s += ", Items[0]: " + txt; + } + + } + return s; + } + + internal void UpdateListViewItemsLocations() { + if (!VirtualMode && IsHandleCreated && AutoArrange && (View == View.LargeIcon || View == View.SmallIcon)) { + + // this only has an affect for large icon and small icon views. + // + try { + BeginUpdate(); + /* + Not sure this does anything that just calling the function doesn't... + + for (int i = 0; i < this.itemCount; i ++) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), NativeMethods.LVM_UPDATE, this.Items[i].Index, 0); + } */ + SendMessage(NativeMethods.LVM_UPDATE, -1, 0); + } + finally { + EndUpdate(); + } + } + + } + + private void UpdateColumnWidths(ColumnHeaderAutoResizeStyle headerAutoResize) { + if (columnHeaders != null) { + for (int i = 0; i < this.columnHeaders.Length; i ++) { + SetColumnWidth(i, headerAutoResize); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected void UpdateExtendedStyles() { + + if (IsHandleCreated) { + int exStyle = 0; + int exMask = NativeMethods.LVS_EX_ONECLICKACTIVATE | NativeMethods.LVS_EX_TWOCLICKACTIVATE | + NativeMethods.LVS_EX_TRACKSELECT | NativeMethods.LVS_EX_UNDERLINEHOT | NativeMethods.LVS_EX_ONECLICKACTIVATE | + NativeMethods.LVS_EX_HEADERDRAGDROP | NativeMethods.LVS_EX_CHECKBOXES | + NativeMethods.LVS_EX_FULLROWSELECT | NativeMethods.LVS_EX_GRIDLINES | + NativeMethods.LVS_EX_INFOTIP | NativeMethods.LVS_EX_DOUBLEBUFFER; + + switch (activation) { + case ItemActivation.OneClick: + exStyle |= NativeMethods.LVS_EX_ONECLICKACTIVATE; + break; + case ItemActivation.TwoClick: + exStyle |= NativeMethods.LVS_EX_TWOCLICKACTIVATE; + break; + } + + if (AllowColumnReorder) + exStyle |= NativeMethods.LVS_EX_HEADERDRAGDROP; + + if (CheckBoxes) + exStyle |= NativeMethods.LVS_EX_CHECKBOXES; + + if (DoubleBuffered) + exStyle |= NativeMethods.LVS_EX_DOUBLEBUFFER; + + if (FullRowSelect) + exStyle |= NativeMethods.LVS_EX_FULLROWSELECT; + + if (GridLines) + exStyle |= NativeMethods.LVS_EX_GRIDLINES; + + if (HoverSelection) + exStyle |= NativeMethods.LVS_EX_TRACKSELECT; + + if (HotTracking) + exStyle |= NativeMethods.LVS_EX_UNDERLINEHOT; + + if (ShowItemToolTips) + exStyle |= NativeMethods.LVS_EX_INFOTIP; + + SendMessage(NativeMethods.LVM_SETEXTENDEDLISTVIEWSTYLE, exMask, exStyle); + Invalidate(); + } + } + + internal void UpdateGroupNative(ListViewGroup group) { + Debug.Assert(IsHandleCreated,"UpdateGroupNative precondition: list-view handle must be created"); + Debug.Assert(ComctlSupportsVisualStyles, "we should have checked this already"); + + NativeMethods.LVGROUP lvgroup = new NativeMethods.LVGROUP(); + try { + lvgroup = GetLVGROUP(group); + int retval = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), + NativeMethods.LVM_SETGROUPINFO, + group.ID, + lvgroup); + Debug.Assert(retval != -1, "Failed to set group info"); + } + finally + { + DestroyLVGROUP(lvgroup); + } + + // The comctl32 ListView does not correctly invalidate itself, so we need to invalidate the entire ListView + // + Invalidate(); + } + + // ListViewGroupCollection::Clear needs to remove the items from the Default group + // + internal void UpdateGroupView() { + if (IsHandleCreated && ComctlSupportsVisualStyles && !VirtualMode) { + int retval = unchecked( (int) (long)SendMessage(NativeMethods.LVM_ENABLEGROUPVIEW, GroupsEnabled ? 1 : 0, 0)); + Debug.Assert(retval != -1, "Error enabling group view"); + } + } + + // updates the win32 list view w/ our tile info - columns + tile size + private void UpdateTileView() { + Debug.Assert(ComctlSupportsVisualStyles, "this function works only when ComCtl 6.0 and higher is loaded"); + Debug.Assert(this.viewStyle == View.Tile, "this function should be called only in Tile view"); + NativeMethods.LVTILEVIEWINFO tileViewInfo = new NativeMethods.LVTILEVIEWINFO(); + // the tile view info line count + tileViewInfo.dwMask = NativeMethods.LVTVIM_COLUMNS; + tileViewInfo.cLines = this.columnHeaders != null ? this.columnHeaders.Length : 0; + + // the tile view info size + tileViewInfo.dwMask |= NativeMethods.LVTVIM_TILESIZE; + tileViewInfo.dwFlags = NativeMethods.LVTVIF_FIXEDSIZE; + tileViewInfo.sizeTile = new NativeMethods.SIZE(this.TileSize.Width, this.TileSize.Height); + + bool retval = UnsafeNativeMethods.SendMessage(new HandleRef(this,Handle),NativeMethods.LVM_SETTILEVIEWINFO,0,tileViewInfo); + Debug.Assert(retval,"LVM_SETTILEVIEWINFO failed"); + } + + private void WmNmClick(ref Message m) { + + // If we're checked, hittest to see if we're + // on the check mark + + if (CheckBoxes) { + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + + lvhi.pt_x = pos.X; + lvhi.pt_y = pos.Y; + + int displayIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_HITTEST, 0, lvhi); + if (displayIndex != -1 && (lvhi.flags & NativeMethods.LVHT_ONITEMSTATEICON) != 0) { + ListViewItem clickedItem = Items[displayIndex]; + if (clickedItem.Selected) { + bool check = !clickedItem.Checked; + if (!VirtualMode) { + foreach(ListViewItem item in SelectedItems) { + if (item != clickedItem) { + item.Checked = check; + } + } + } + } + } + } + } + + private void WmNmDblClick(ref Message m) { + + // If we're checked, hittest to see if we're + // on the item + + if (CheckBoxes) { + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + + lvhi.pt_x = pos.X; + lvhi.pt_y = pos.Y; + + int displayIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_HITTEST, 0, lvhi); + if (displayIndex != -1 && (lvhi.flags & NativeMethods.LVHT_ONITEM) != 0) { + ListViewItem clickedItem = Items[displayIndex]; + clickedItem.Checked = !clickedItem.Checked; + } + } + } + + private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { + + //Always Reset the MouseupFired.... + listViewState[LISTVIEWSTATE_mouseUpFired] = false; + listViewState[LISTVIEWSTATE_expectingMouseUp] = true; + + //This is required to FORCE Validation before Windows ListView pushes its own message loop... + FocusInternal(); + + // Windows ListView pushes its own Windows ListView in WM_xBUTTONDOWN, so fire the + // event before calling defWndProc or else it won't get fired until the button + // comes back up. + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + OnMouseDown(new MouseEventArgs(button, clicks, x, y, 0)); + + //If Validation is cancelled dont fire any events through the Windows ListView's message loop... + if (!ValidationCancelled) { + + if (CheckBoxes) { + ListViewHitTestInfo lvhti = this.HitTest(x, y); + if (imageListState != null && imageListState.Images.Count < 2) { + // vsw 156366: when the user clicks on the check box and the listView's state image list + // does not have 2 images, comctl will give us an AttemptToDivideByZero exception. + // So don't send the message to DefWndProc in this situation. + if (lvhti.Location != ListViewHitTestLocations.StateImage) { + DefWndProc(ref m); + } + } + else { + // When a user clicks on the state image, focus the item. + if (AccessibilityImprovements.Level2 && lvhti.Item != null && lvhti.Location == ListViewHitTestLocations.StateImage) { + lvhti.Item.Focused = true; + } + DefWndProc(ref m); + } + } + else + { + DefWndProc(ref m); + } + } + + } + + private unsafe bool WmNotify(ref Message m) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + + // column header custom draw message handling + if (nmhdr->code == NativeMethods.NM_CUSTOMDRAW && OwnerDraw) + { + try + { + NativeMethods.NMCUSTOMDRAW* nmcd = (NativeMethods.NMCUSTOMDRAW*)m.LParam; + // Find out which stage we're drawing + switch (nmcd->dwDrawStage) + { + case NativeMethods.CDDS_PREPAINT: + { + m.Result = (IntPtr)(NativeMethods.CDRF_NOTIFYITEMDRAW); + return true; // we are done - don't do default handling + + } + case NativeMethods.CDDS_ITEMPREPAINT: + { + Graphics g = Graphics.FromHdcInternal(nmcd->hdc); + Rectangle r = Rectangle.FromLTRB(nmcd->rc.left, nmcd->rc.top, nmcd->rc.right, nmcd->rc.bottom); + DrawListViewColumnHeaderEventArgs e = null; + + try + { + Color foreColor = ColorTranslator.FromWin32(SafeNativeMethods.GetTextColor(new HandleRef(this, nmcd->hdc))); + Color backColor = ColorTranslator.FromWin32(SafeNativeMethods.GetBkColor(new HandleRef(this, nmcd->hdc))); + Font font = GetListHeaderFont(); + e = new DrawListViewColumnHeaderEventArgs(g, r, (int)(nmcd->dwItemSpec), + columnHeaders[(int)nmcd->dwItemSpec], + (ListViewItemStates)(nmcd->uItemState), + foreColor, backColor, font); + + OnDrawColumnHeader(e); + } + finally + { + g.Dispose(); + } + + if (e.DrawDefault) + { + m.Result = (IntPtr)(NativeMethods.CDRF_DODEFAULT); + return false; + } + else + { + + m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); + return true; // we are done - don't do default handling + } + } + + default: + return false; //default handling + } + } + catch (Exception e) + { + Debug.Fail("Exception occurred attempting to setup header custom draw. Disabling custom draw for the column header", e.ToString()); + m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; + } + } + + + if (nmhdr->code == NativeMethods.NM_RELEASEDCAPTURE && listViewState[LISTVIEWSTATE_columnClicked]) { + listViewState[LISTVIEWSTATE_columnClicked] = false; + OnColumnClick(new ColumnClickEventArgs(columnIndex)); + } + + if (nmhdr->code == NativeMethods.HDN_BEGINTRACKA || + nmhdr->code == NativeMethods.HDN_BEGINTRACKW) { + this.listViewState[LISTVIEWSTATE_headerControlTracking] = true; + + // Reset our tracking information for the new BEGINTRACK cycle. + this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false; + this.newWidthForColumnWidthChangingCancelled = -1; + this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false; + + NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) { + this.columnHeaderClicked = this.columnHeaders[nmheader.iItem]; + this.columnHeaderClickedWidth = this.columnHeaderClicked.Width; + } else { + this.columnHeaderClickedWidth = -1; + this.columnHeaderClicked = null; + } + } + + if (nmhdr->code == NativeMethods.HDN_ITEMCHANGINGA || + nmhdr->code == NativeMethods.HDN_ITEMCHANGINGW) { + NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + + if (columnHeaders != null && nmheader.iItem < columnHeaders.Length && + (this.listViewState[LISTVIEWSTATE_headerControlTracking] || this.listViewState[LISTVIEWSTATE_headerDividerDblClick])) { + // SECREVIEW: + // UnsafeNativeMethods.PtrToStructure asserts ReflectionPermission. + // We are fine asserting this permission because the HDITEM2 type is our type and we have control over it + // and over what it does in its constructor. + NativeMethods.HDITEM2 hdItem = (NativeMethods.HDITEM2) UnsafeNativeMethods.PtrToStructure((IntPtr) nmheader.pItem, typeof(NativeMethods.HDITEM2)); + int newColumnWidth = ((hdItem.mask & NativeMethods.HDI_WIDTH) != 0) ? hdItem.cxy : -1; + ColumnWidthChangingEventArgs colWidthChanging = new ColumnWidthChangingEventArgs(nmheader.iItem, newColumnWidth); + OnColumnWidthChanging(colWidthChanging); + m.Result = (IntPtr) (colWidthChanging.Cancel ? 1 : 0); + if (colWidthChanging.Cancel) { + hdItem.cxy = colWidthChanging.NewWidth; + + // We are called inside HDN_DIVIDERDBLCLICK. + // Turn off the compensation that our processing of HDN_DIVIDERDBLCLICK would otherwise add. + if (this.listViewState[LISTVIEWSTATE_headerDividerDblClick]) { + this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = true; + } + + this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = true; + this.newWidthForColumnWidthChangingCancelled = colWidthChanging.NewWidth; + + // skip default processing + return true; + } else { + return false; + } + } + } + + if ((nmhdr->code == NativeMethods.HDN_ITEMCHANGEDA || + nmhdr->code == NativeMethods.HDN_ITEMCHANGEDW) && + !this.listViewState[LISTVIEWSTATE_headerControlTracking]) { + NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)m.GetLParam(typeof(NativeMethods.NMHEADER)); + if (columnHeaders != null && nmheader.iItem < columnHeaders.Length) { + int w = columnHeaders[nmheader.iItem].Width; + + if (this.columnHeaderClicked == null || + (this.columnHeaderClicked == this.columnHeaders[nmheader.iItem] && + this.columnHeaderClickedWidth != -1 && + this.columnHeaderClickedWidth != w)) { + + // + // If the user double clicked on the column header and we still need to compensate for the column resize + // then don't fire ColumnWidthChanged because at this point the column header does not have the final width. + // + if (this.listViewState[LISTVIEWSTATE_headerDividerDblClick]) { + if (this.CompensateColumnHeaderResize(m, this.listViewState[LISTVIEWSTATE_columnResizeCancelled]) == 0) { + OnColumnWidthChanged(new ColumnWidthChangedEventArgs(nmheader.iItem)); + } + } else { + OnColumnWidthChanged(new ColumnWidthChangedEventArgs(nmheader.iItem)); + } + + } + } + + this.columnHeaderClicked = null; + this.columnHeaderClickedWidth = -1; + + ISite site = Site; + + // Microsoft, this seems like a really wierd place to annouce this change... + if (site != null) { + IComponentChangeService cs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (cs != null) { + try { + cs.OnComponentChanging(this, null); + } + catch (CheckoutException coEx) { + if (coEx == CheckoutException.Canceled) { + return false; + } + throw coEx; + } + } + } + } + + if (nmhdr->code == NativeMethods.HDN_ENDTRACKA || + nmhdr->code == NativeMethods.HDN_ENDTRACKW) { + Debug.Assert(this.listViewState[LISTVIEWSTATE_headerControlTracking], "HDN_ENDTRACK and HDN_BEGINTRACK are out of sync..."); + this.listViewState[LISTVIEWSTATE_headerControlTracking] = false; + if (this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging]) { + m.Result = (IntPtr) 1; + if (this.newWidthForColumnWidthChangingCancelled != -1) { + NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)m.GetLParam(typeof(NativeMethods.NMHEADER)); + if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) { + this.columnHeaders[nmheader.iItem].Width = this.newWidthForColumnWidthChangingCancelled; + } + } + + this.listViewState1[LISTVIEWSTATE1_cancelledColumnWidthChanging] = false; + this.newWidthForColumnWidthChangingCancelled = -1; + + // skip default processing + return true; + } else { + return false; + } + } + + if (nmhdr->code == NativeMethods.HDN_ENDDRAG) { + NativeMethods.NMHEADER header = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + if (header.pItem != IntPtr.Zero) { + // SECREVIEW: + // UnsafeNativeMethods.PtrToStructure asserts ReflectionPermission. + // We are fine asserting this permission because the HDITEM type is our type and we have control over it + // and over what it does in its constructor. + NativeMethods.HDITEM2 hdItem = (NativeMethods.HDITEM2) UnsafeNativeMethods.PtrToStructure((IntPtr) header.pItem, typeof(NativeMethods.HDITEM2)); + if ((hdItem.mask & NativeMethods.HDI_ORDER) == NativeMethods.HDI_ORDER) { + + int from = this.Columns[header.iItem].DisplayIndex; + int to = hdItem.iOrder; + // check this + if (from == to) { + return false; + } + + // sometimes ComCtl gives us bogus values for HDIItem.iOrder. + // vsw 541880. + if (to < 0) { + return false; + } + ColumnReorderedEventArgs chrevent = new ColumnReorderedEventArgs(from, + to, + this.Columns[header.iItem]); + OnColumnReordered(chrevent); + if (chrevent.Cancel) { + m.Result = new IntPtr(1); + return true; + } else { + // set the display indices. This is not an expensive operation because + // we only set an integer in the column header class + int lowDI = Math.Min(from, to); + int hiDI = Math.Max(from, to); + bool hdrMovedForward = to > from; + ColumnHeader movedHdr = null; + int[] indices = new int[this.Columns.Count]; + for (int i = 0; i < this.Columns.Count; i ++) { + + ColumnHeader hdr = this.Columns[i]; + if (hdr.DisplayIndex == from) { + movedHdr = hdr; + } else if (hdr.DisplayIndex >= lowDI && hdr.DisplayIndex <= hiDI) { + hdr.DisplayIndexInternal -= hdrMovedForward ? 1 : -1; + } + indices[i] = hdr.DisplayIndexInternal; + } + + movedHdr.DisplayIndexInternal = to; + indices[movedHdr.Index] = movedHdr.DisplayIndexInternal; + SetDisplayIndices( indices ); +#if DEBUG + CheckDisplayIndices(); +#endif + } + } + } + } + + if (nmhdr->code == NativeMethods.HDN_DIVIDERDBLCLICKA || + nmhdr->code == NativeMethods.HDN_DIVIDERDBLCLICKW) { + // We need to keep track that the user double clicked the column header divider + // so we know that the column header width is changing. + this.listViewState[LISTVIEWSTATE_headerDividerDblClick] = true; + + // Reset ColumnResizeCancelled. + // It will be set if the user cancels the ColumnWidthChanging event. + this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = false; + + bool columnResizeCancelled = false; + + // ComCtl32 does not add enough padding when resizing the first column via mouse double click. + // See vsw 336709 for a complete explanation including listing of the comctl32 code. + // Our wrapper will add 2 pixels. (1 pixel is not enough, 3 pixels is too much) + + // Send the message to ComCtl32 so that it resizes the column. + try + { + DefWndProc(ref m); + } + finally + { + this.listViewState[LISTVIEWSTATE_headerDividerDblClick] = false; + columnResizeCancelled = this.listViewState[LISTVIEWSTATE_columnResizeCancelled]; + this.listViewState[LISTVIEWSTATE_columnResizeCancelled] = false; + } + + + this.columnHeaderClicked = null; + this.columnHeaderClickedWidth = -1; + + if (columnResizeCancelled) { + // If the column resize was cancelled then apply the NewWidth supplied by the user. + if (this.newWidthForColumnWidthChangingCancelled != -1) { + NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + if (this.columnHeaders != null && this.columnHeaders.Length > nmheader.iItem) { + this.columnHeaders[nmheader.iItem].Width = this.newWidthForColumnWidthChangingCancelled; + } + } + + // Tell ComCtl that the HDN_DIVIDERDBLCLICK was cancelled. + m.Result = (IntPtr) 1; + } else { + // Compensate for the column resize. + int compensateForColumnResize = this.CompensateColumnHeaderResize(m, columnResizeCancelled); + if (compensateForColumnResize != 0) { + #if DEBUG + NativeMethods.NMHEADER header = (NativeMethods.NMHEADER) m.GetLParam(typeof(NativeMethods.NMHEADER)); + Debug.Assert(header.iItem == 0, "we only need to compensate for the first column resize"); + Debug.Assert(this.columnHeaders.Length > 0, "there should be a column that we need to compensate for"); + #endif + + ColumnHeader col = this.columnHeaders[0]; + col.Width += compensateForColumnResize; + } + } + + // We called DefWndProc so we don't need default handling. + return true; + } + + return false; // still need default handling + } + + private Font GetListHeaderFont(){ + IntPtr hwndHdr = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_GETHEADER, 0, 0); + IntPtr hFont = UnsafeNativeMethods.SendMessage(new HandleRef(this, hwndHdr), NativeMethods.WM_GETFONT, 0, 0); + IntSecurity.ObjectFromWin32Handle.Assert(); + return Font.FromHfont(hFont); + } + + + + /// + /// + /// + /// + private int GetIndexOfClickedItem(NativeMethods.LVHITTESTINFO lvhi) { + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + lvhi.pt_x = pos.X; + lvhi.pt_y = pos.Y; + return (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LVM_HITTEST, 0, lvhi); + + } + + internal void RecreateHandleInternal() { + // vsWhidbey 395361. + // For some reason, if CheckBoxes are set to true and the list view has a state imageList, then the native listView destroys + // the state imageList. + // (Yes, it does exactly that even though our wrapper sets LVS_SHAREIMAGELISTS on the native listView.) + if (this.IsHandleCreated && this.StateImageList != null) { + SendMessage(NativeMethods.LVM_SETIMAGELIST, NativeMethods.LVSIL_STATE, IntPtr.Zero); + } + + RecreateHandle(); + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + private unsafe void WmReflectNotify(ref Message m) { + + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + + switch (nmhdr->code) { + case NativeMethods.NM_CUSTOMDRAW: + CustomDraw(ref m); + break; + + case NativeMethods.LVN_BEGINLABELEDITA: + case NativeMethods.LVN_BEGINLABELEDITW: { + NativeMethods.NMLVDISPINFO_NOTEXT nmlvdp = (NativeMethods.NMLVDISPINFO_NOTEXT)m.GetLParam(typeof(NativeMethods.NMLVDISPINFO_NOTEXT)); + LabelEditEventArgs e = new LabelEditEventArgs(nmlvdp.item.iItem); + OnBeforeLabelEdit(e); + m.Result = (IntPtr)(e.CancelEdit ? 1 : 0); + listViewState[LISTVIEWSTATE_inLabelEdit] = !e.CancelEdit; + break; + } + + case NativeMethods.LVN_COLUMNCLICK: { + NativeMethods.NMLISTVIEW nmlv = (NativeMethods.NMLISTVIEW)m.GetLParam(typeof(NativeMethods.NMLISTVIEW)); + listViewState[LISTVIEWSTATE_columnClicked] = true; + columnIndex = nmlv.iSubItem; + break; + } + + case NativeMethods.LVN_ENDLABELEDITA: + case NativeMethods.LVN_ENDLABELEDITW: { + listViewState[LISTVIEWSTATE_inLabelEdit] = false; + NativeMethods.NMLVDISPINFO nmlvdp = (NativeMethods.NMLVDISPINFO)m.GetLParam(typeof(NativeMethods.NMLVDISPINFO)); + LabelEditEventArgs e = new LabelEditEventArgs(nmlvdp.item.iItem, nmlvdp.item.pszText); + OnAfterLabelEdit(e); + m.Result = (IntPtr)(e.CancelEdit ? 0 : 1); + // from msdn: + // "If the user cancels editing, the pszText member of the LVITEM structure is NULL" + if (!e.CancelEdit && nmlvdp.item.pszText != null) + Items[nmlvdp.item.iItem].Text = nmlvdp.item.pszText; + break; + } + + case NativeMethods.LVN_ITEMACTIVATE: + OnItemActivate(EventArgs.Empty); + break; + + case NativeMethods.LVN_BEGINDRAG: { + // the items collection was modified while dragging + // that means that we can't reliably give the user the item on which the dragging started + // so don't tell the user about this operation... + // vsWhidbey 235027 + if (!this.ItemCollectionChangedInMouseDown) { + NativeMethods.NMLISTVIEW nmlv = (NativeMethods.NMLISTVIEW)m.GetLParam(typeof(NativeMethods.NMLISTVIEW)); + LvnBeginDrag(MouseButtons.Left, nmlv); + } + + break; + } + + case NativeMethods.LVN_BEGINRDRAG: { + // the items collection was modified while dragging + // that means that we can't reliably give the user the item on which the dragging started + // so don't tell the user about this operation... + // vsWhidbey 235027 + if (!this.ItemCollectionChangedInMouseDown) { + NativeMethods.NMLISTVIEW nmlv = (NativeMethods.NMLISTVIEW)m.GetLParam(typeof(NativeMethods.NMLISTVIEW)); + LvnBeginDrag(MouseButtons.Right, nmlv); + } + break; + } + + + case NativeMethods.LVN_ITEMCHANGING: { + NativeMethods.NMLISTVIEW* nmlv = (NativeMethods.NMLISTVIEW*)m.LParam; + if ((nmlv->uChanged & NativeMethods.LVIF_STATE) != 0) { + // Because the state image mask is 1-based, a value of 1 means unchecked, + // anything else means checked. We convert this to the more standard 0 or 1 + CheckState oldState = (CheckState)(((nmlv->uOldState & NativeMethods.LVIS_STATEIMAGEMASK) >> 12) == 1 ? 0 : 1); + CheckState newState = (CheckState)(((nmlv->uNewState & NativeMethods.LVIS_STATEIMAGEMASK) >> 12) == 1 ? 0 : 1); + + if (oldState != newState) { + ItemCheckEventArgs e = new ItemCheckEventArgs(nmlv->iItem, newState, oldState); + OnItemCheck(e); + m.Result = (IntPtr)(((int)e.NewValue == 0 ? 0 : 1) == (int)oldState ? 1 : 0); + } + } + break; + } + + case NativeMethods.LVN_ITEMCHANGED: { + NativeMethods.NMLISTVIEW* nmlv = (NativeMethods.NMLISTVIEW*)m.LParam; + // Check for state changes to the selected state... + if ((nmlv->uChanged & NativeMethods.LVIF_STATE) != 0) { + // Because the state image mask is 1-based, a value of 1 means unchecked, + // anything else means checked. We convert this to the more standard 0 or 1 + CheckState oldValue = (CheckState)(((nmlv->uOldState & NativeMethods.LVIS_STATEIMAGEMASK) >> 12) == 1 ? 0 : 1); + CheckState newValue = (CheckState)(((nmlv->uNewState & NativeMethods.LVIS_STATEIMAGEMASK) >> 12) == 1 ? 0 : 1); + + if (newValue != oldValue) { + ItemCheckedEventArgs e = new ItemCheckedEventArgs(Items[nmlv->iItem]); + OnItemChecked(e); + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, nmlv->iItem); + AccessibilityNotifyClients(AccessibleEvents.NameChange, nmlv->iItem); + } + } + + int oldState = nmlv->uOldState & NativeMethods.LVIS_SELECTED; + int newState = nmlv->uNewState & NativeMethods.LVIS_SELECTED; + // Windows common control always fires + // this event twice, once with newState, oldState, and again with + // oldState, newState. + // Changing this affects the behaviour as the control never + // fires the event on a Deselct of an Items from multiple selections. + // So leave it as it is... + if (newState != oldState) { + if (this.VirtualMode && nmlv->iItem == -1) + { + if (this.VirtualListSize > 0) { + ListViewVirtualItemsSelectionRangeChangedEventArgs lvvisrce = new ListViewVirtualItemsSelectionRangeChangedEventArgs(0, this.VirtualListSize-1, newState != 0); + OnVirtualItemsSelectionRangeChanged(lvvisrce); + } + } + else + { + // APPCOMPAT + // V1.* users implement virtualization by communicating directly to the native ListView and + // by passing our virtualization implementation. + // In that case, the native list view may have an item under the mouse even if our wrapper thinks the item count is 0. + // And that may cause GetItemAt to throw an out of bounds exception. + // See VSW 418791. + + if (this.Items.Count > 0) { + ListViewItemSelectionChangedEventArgs lvisce = new ListViewItemSelectionChangedEventArgs(this.Items[nmlv->iItem], + nmlv->iItem, + newState != 0); + OnItemSelectionChanged(lvisce); + } + } + // VSWhidbey 549967 - Delay SelectedIndexChanged event because the last item isn't present yet. + if (this.Items.Count == 0 || this.Items[this.Items.Count - 1] != null) + { + this.listViewState1[LISTVIEWSTATE1_selectedIndexChangedSkipped] = false; + OnSelectedIndexChanged(EventArgs.Empty); + } + else + { + this.listViewState1[LISTVIEWSTATE1_selectedIndexChangedSkipped] = true; + } + } + } + break; + } + + case NativeMethods.NM_CLICK: + WmNmClick(ref m); + // FALL THROUGH // + goto case NativeMethods.NM_RCLICK; + + case NativeMethods.NM_RCLICK: + NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO(); + int displayIndex = GetIndexOfClickedItem(lvhi); + + MouseButtons button = nmhdr->code == NativeMethods.NM_CLICK ? MouseButtons.Left : MouseButtons.Right; + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + + if (!ValidationCancelled && displayIndex != -1) { + OnClick(EventArgs.Empty); + OnMouseClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); + } + if (!listViewState[LISTVIEWSTATE_mouseUpFired]) + { + OnMouseUp(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); + listViewState[LISTVIEWSTATE_mouseUpFired] = true; + } + break; + + case NativeMethods.NM_DBLCLK: + WmNmDblClick(ref m); + // FALL THROUGH // + goto case NativeMethods.NM_RDBLCLK; + + case NativeMethods.NM_RDBLCLK: + NativeMethods.LVHITTESTINFO lvhip = new NativeMethods.LVHITTESTINFO(); + int index = GetIndexOfClickedItem(lvhip); + + if (index != -1) { + //just maintain state and fire double click.. in final mouseUp... + listViewState[LISTVIEWSTATE_doubleclickFired] = true; + } + //fire Up in the Wndproc !! + listViewState[LISTVIEWSTATE_mouseUpFired] = false; + //problem getting the UP... outside the control... + // + CaptureInternal = true; + break; + + case NativeMethods.LVN_KEYDOWN: + if (CheckBoxes) { + NativeMethods.NMLVKEYDOWN lvkd = (NativeMethods.NMLVKEYDOWN)m.GetLParam(typeof(NativeMethods.NMLVKEYDOWN)); + if (lvkd.wVKey == (short)Keys.Space) { + ListViewItem focusedItem = FocusedItem; + if (focusedItem != null) { + bool check = !focusedItem.Checked; + if (!VirtualMode) { + foreach(ListViewItem item in SelectedItems) { + if (item != focusedItem) { + item.Checked = check; + } + } + } + } + } + } + break; + + case NativeMethods.LVN_ODCACHEHINT: + // tell the user to prepare the cache: + NativeMethods.NMLVCACHEHINT cacheHint = (NativeMethods.NMLVCACHEHINT) m.GetLParam(typeof(NativeMethods.NMLVCACHEHINT)); + OnCacheVirtualItems(new CacheVirtualItemsEventArgs(cacheHint.iFrom, cacheHint.iTo)); + break; + + default: + if (nmhdr->code == NativeMethods.LVN_GETDISPINFO) { + // we use the LVN_GETDISPINFO message only in virtual mode + if (this.VirtualMode && m.LParam != IntPtr.Zero) { + // we HAVE to use unsafe code because of a bug in the CLR: WHIDBEY bug 20313 + NativeMethods.NMLVDISPINFO_NOTEXT dispInfo= (NativeMethods.NMLVDISPINFO_NOTEXT) m.GetLParam(typeof(NativeMethods.NMLVDISPINFO_NOTEXT)); + + RetrieveVirtualItemEventArgs rVI = new RetrieveVirtualItemEventArgs(dispInfo.item.iItem); + OnRetrieveVirtualItem(rVI); + ListViewItem lvItem = rVI.Item; + if (lvItem == null) + { + throw new InvalidOperationException(SR.GetString(SR.ListViewVirtualItemRequired)); + } + + lvItem.SetItemIndex(this, dispInfo.item.iItem); + if ((dispInfo.item.mask & NativeMethods.LVIF_TEXT) != 0) { + string text; + if (dispInfo.item.iSubItem == 0) { + text = lvItem.Text; // we want the item + } + else { + if (lvItem.SubItems.Count <= dispInfo.item.iSubItem) { + throw new InvalidOperationException(SR.GetString(SR.ListViewVirtualModeCantAccessSubItem)); + } + else { + text = lvItem.SubItems[dispInfo.item.iSubItem].Text; // we want the sub item + } + } + + // use the buffer provided by the ComCtrl list view. + if (dispInfo.item.cchTextMax <= text.Length) { + text = text.Substring(0, dispInfo.item.cchTextMax - 1); + } + + if (Marshal.SystemDefaultCharSize == 1) { + // ANSI. Use byte + byte[] buff = System.Text.Encoding.Default.GetBytes(text + "\0"); + Marshal.Copy(buff, 0, dispInfo.item.pszText, text.Length + 1); + } else { + char[] buff = (text + "\0").ToCharArray(); + Marshal.Copy(buff, 0, dispInfo.item.pszText, text.Length + 1); + } + } + + if ((dispInfo.item.mask & NativeMethods.LVIF_IMAGE) != 0 && lvItem.ImageIndex != -1) { + dispInfo.item.iImage = lvItem.ImageIndex; + } + + if ((dispInfo.item.mask & NativeMethods.LVIF_INDENT) != 0) { + dispInfo.item.iIndent = lvItem.IndentCount; + } + + /* Microsoft: Couldn't make this work. The dispInfo.item.iSubItem received for the subitems' text + are invalid + if ((dispInfo.item.mask & NativeMethods.LVIF_COLUMNS) != 0) { + int cColumns = this.columnHeaders != null ? this.columnHeaders.Length : 0; + dispInfo.item.cColumns = cColumns; + if (cColumns > 0) { + dispInfo.item.puColumns = Marshal.AllocHGlobal(cColumns * Marshal.SizeOf(typeof(int))); + int[] columns = new int[cColumns]; + for (int c = 0; c < cColumns; c++) { + columns[c] = c + 1; + } + Marshal.Copy(columns, 0, dispInfo.item.puColumns, cColumns); + } + } + */ + + /* Microsoft: VirtualMode and grouping seem to be incompatible. + dispInfo.item.mask never includes NativeMethods.LVIF_GROUPID. + Besides, trying to send LVM_ENABLEGROUPVIEW to the listview fails in virtual mode. + if (this.GroupsEnabled && (dispInfo.item.mask & NativeMethods.LVIF_GROUPID) != 0) + { + dispInfo.item.iGroupId = GetNativeGroupId(lvItem); + #if DEBUG + Debug.Assert(SendMessage(NativeMethods.LVM_ISGROUPVIEWENABLED, 0, 0) != IntPtr.Zero, "Groups not enabled"); + Debug.Assert(SendMessage(NativeMethods.LVM_HASGROUP, dispInfo.item.iGroupId, 0) != IntPtr.Zero, "Doesn't contain group id: " + dispInfo.item.iGroupId.ToString(CultureInfo.InvariantCulture)); + #endif + } + */ + + if ((dispInfo.item.stateMask & NativeMethods.LVIS_STATEIMAGEMASK) != 0) { + dispInfo.item.state |= lvItem.RawStateImageIndex; + } + Marshal.StructureToPtr(dispInfo, (IntPtr) m.LParam, false); + + } + } + else if (nmhdr->code == NativeMethods.LVN_ODSTATECHANGED) { + if (VirtualMode && m.LParam != IntPtr.Zero) { + NativeMethods.NMLVODSTATECHANGE odStateChange = (NativeMethods.NMLVODSTATECHANGE) m.GetLParam(typeof(NativeMethods.NMLVODSTATECHANGE)); + bool selectedChanged = (odStateChange.uNewState & NativeMethods.LVIS_SELECTED) != (odStateChange.uOldState & NativeMethods.LVIS_SELECTED); + if (selectedChanged) { + // we have to substract 1 from iTo due to an imbecility in the win32 ListView control + // see vsWhidbey 275717 - especially the ListView_CalcMinMaxIndex method. + // in Vista, 275717 is fixed in windows OS side, we have don't need to work around here any more. + int iTo = odStateChange.iTo; + if (!UnsafeNativeMethods.IsVista) { + iTo--; + } + ListViewVirtualItemsSelectionRangeChangedEventArgs lvvisrce = new ListViewVirtualItemsSelectionRangeChangedEventArgs(odStateChange.iFrom, iTo, (odStateChange.uNewState & NativeMethods.LVIS_SELECTED) != 0); + OnVirtualItemsSelectionRangeChanged(lvvisrce); + } + } + } + else if (nmhdr->code == NativeMethods.LVN_GETINFOTIP) { + if (ShowItemToolTips && m.LParam != IntPtr.Zero) { + NativeMethods.NMLVGETINFOTIP infoTip = (NativeMethods.NMLVGETINFOTIP) m.GetLParam(typeof(NativeMethods.NMLVGETINFOTIP)); + ListViewItem lvi = Items[infoTip.item]; + if (lvi != null && !string.IsNullOrEmpty(lvi.ToolTipText)) { + + // MSDN: + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + + UnsafeNativeMethods.SendMessage(new HandleRef(this, nmhdr->hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + + if (Marshal.SystemDefaultCharSize == 1) { + // ANSI. Use byte. + // we need to copy the null terminator character ourselves + byte[] byteBuf = System.Text.Encoding.Default.GetBytes(lvi.ToolTipText + "\0"); + Marshal.Copy(byteBuf, 0, infoTip.lpszText, Math.Min(byteBuf.Length, infoTip.cchTextMax)); + } else { + // UNICODE. Use char. + // we need to copy the null terminator character ourselves + char[] charBuf = (lvi.ToolTipText + "\0").ToCharArray(); + Marshal.Copy(charBuf, 0, infoTip.lpszText, Math.Min(charBuf.Length, infoTip.cchTextMax)); + } + Marshal.StructureToPtr(infoTip, (IntPtr) m.LParam, false); + } + } + } + else if (nmhdr->code == NativeMethods.LVN_ODFINDITEM) { + if (VirtualMode) { + NativeMethods.NMLVFINDITEM nmlvif = (NativeMethods.NMLVFINDITEM) m.GetLParam(typeof(NativeMethods.NMLVFINDITEM)); + + if ((nmlvif.lvfi.flags & NativeMethods.LVFI_PARAM) != 0) { + m.Result = (IntPtr) (-1); + return; + } + + bool isTextSearch = ((nmlvif.lvfi.flags & NativeMethods.LVFI_STRING) != 0) || + ((nmlvif.lvfi.flags & NativeMethods.LVFI_PARTIAL) != 0); + + bool isPrefixSearch = (nmlvif.lvfi.flags & NativeMethods.LVFI_PARTIAL) != 0; + + string text = String.Empty; + if (isTextSearch) { + text = nmlvif.lvfi.psz; + } + + Point startingPoint = Point.Empty; + if ((nmlvif.lvfi.flags & NativeMethods.LVFI_NEARESTXY) != 0) { + startingPoint = new Point(nmlvif.lvfi.ptX, nmlvif.lvfi.ptY); + } + + SearchDirectionHint dir = SearchDirectionHint.Down; + if ((nmlvif.lvfi.flags & NativeMethods.LVFI_NEARESTXY) != 0) { + // We can do this because SearchDirectionHint is set to the VK_* + dir = (SearchDirectionHint) nmlvif.lvfi.vkDirection; + } + + int startIndex = nmlvif.iStart; + if (startIndex >= this.VirtualListSize) { + // we want to search starting from the last item. Wrap around the first item. + startIndex = 0; + } + + + SearchForVirtualItemEventArgs sviEvent = new SearchForVirtualItemEventArgs( + isTextSearch, + isPrefixSearch, + false, /* includeSubItemsInSearch */ + text, + startingPoint, + dir, + nmlvif.iStart); + + OnSearchForVirtualItem(sviEvent); + if (sviEvent.Index != -1) { + m.Result = (IntPtr) sviEvent.Index; + } else { + m.Result = (IntPtr) (-1); + } + } + } + break; + } + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + WmReflectNotify(ref m); + break; + case NativeMethods.WM_LBUTTONDBLCLK: + + // Ensure that the itemCollectionChangedInMouseDown is not set + // before processing the mousedown event. + this.ItemCollectionChangedInMouseDown = false; + CaptureInternal = true; + WmMouseDown(ref m, MouseButtons.Left, 2); + break; + + case NativeMethods.WM_LBUTTONDOWN: + + // Ensure that the itemCollectionChangedInMouseDown is not set + // before processing the mousedown event. + this.ItemCollectionChangedInMouseDown = false; + WmMouseDown(ref m, MouseButtons.Left, 1); + downButton = MouseButtons.Left; + break; + + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_RBUTTONUP: + case NativeMethods.WM_MBUTTONUP: + + // see the mouse is on item + // + NativeMethods.LVHITTESTINFO lvhip = new NativeMethods.LVHITTESTINFO(); + int index = GetIndexOfClickedItem(lvhip); + + if (!ValidationCancelled && listViewState[LISTVIEWSTATE_doubleclickFired] && index != -1) { + listViewState[LISTVIEWSTATE_doubleclickFired] = false; + OnDoubleClick(EventArgs.Empty); + OnMouseDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + if (!listViewState[LISTVIEWSTATE_mouseUpFired]) + { + OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + listViewState[LISTVIEWSTATE_expectingMouseUp] = false; + } + + this.ItemCollectionChangedInMouseDown = false; + + listViewState[LISTVIEWSTATE_mouseUpFired] = true; + CaptureInternal = false; + break; + case NativeMethods.WM_MBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Middle, 2); + break; + case NativeMethods.WM_MBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Middle, 1); + downButton = MouseButtons.Middle; + break; + case NativeMethods.WM_RBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Right, 2); + break; + case NativeMethods.WM_RBUTTONDOWN: + WmMouseDown(ref m, MouseButtons.Right, 1); + downButton = MouseButtons.Right; + break; + case NativeMethods.WM_MOUSEMOVE: + if (listViewState[LISTVIEWSTATE_expectingMouseUp] && !listViewState[LISTVIEWSTATE_mouseUpFired] && MouseButtons == MouseButtons.None) + { + OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + listViewState[LISTVIEWSTATE_mouseUpFired] = true; + } + CaptureInternal = false; + base.WndProc(ref m); + break; + case NativeMethods.WM_MOUSEHOVER: + if (HoverSelection) { + base.WndProc(ref m); + } + else + OnMouseHover(EventArgs.Empty); + break; + case NativeMethods.WM_NOTIFY: + if(WmNotify(ref m)) + { + break; // we are done - skip default handling + } + else + { + goto default; //default handling needed + } + case NativeMethods.WM_SETFOCUS: + base.WndProc(ref m); + + if (!this.RecreatingHandle && !this.ListViewHandleDestroyed) { + // This means that we get a WM_SETFOCUS on the hWnd that was destroyed. + // Don't do anything because the information on the previous hWnd is most likely + // out of sync w/ the information in our ListView wrapper. + // See comment in vsw 451268. + + // We should set focus to the first item, + // if none of the items are focused already. + if (FocusedItem == null && Items.Count > 0) + { + Items[0].Focused = true; + } + } + break; + case NativeMethods.WM_MOUSELEAVE: + // if the mouse leaves and then re-enters the ListView + // ItemHovered events should be raised. + prevHoveredItem = null; + base.WndProc(ref m); + break; + + case NativeMethods.WM_PAINT: + base.WndProc(ref m); + + // win32 ListView - look for comments about vsWhidbey 243708 + BeginInvoke(new MethodInvoker(this.CleanPreviousBackgroundImageFiles)); + break; + case NativeMethods.WM_PRINT: + WmPrint(ref m); + break; + case NativeMethods.WM_TIMER: + if (unchecked( (int) (long)m.WParam) != LVTOOLTIPTRACKING || !ComctlSupportsVisualStyles) { + base.WndProc(ref m); + } + break; + default: + base.WndProc(ref m); + break; + }; + } + + ///new class for comparing and sorting Icons .... + //subhag + internal class IconComparer: IComparer { + private SortOrder sortOrder; + + public IconComparer(SortOrder currentSortOrder) { + this.sortOrder = currentSortOrder; + } + + public SortOrder SortOrder { + set { + sortOrder = value; + } + } + + public int Compare(object obj1, object obj2) { + //subhag + ListViewItem currentItem = (ListViewItem)obj1; + ListViewItem nextItem = (ListViewItem)obj2; + if (sortOrder == SortOrder.Ascending) { + return (String.Compare(currentItem.Text,nextItem.Text, false, CultureInfo.CurrentCulture)); + } + else { + return (String.Compare(nextItem.Text,currentItem.Text, false, CultureInfo.CurrentCulture)); + } + } + } + //end subhag + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class CheckedIndexCollection : IList { + private ListView owner; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public CheckedIndexCollection(ListView owner) { + this.owner = owner; + } + + /// + /// + /// Number of currently selected items. + /// + [Browsable(false)] + public int Count { + get { + if (!owner.CheckBoxes) { + return 0; + } + + // Count the number of checked items + // + int count = 0; + foreach(ListViewItem item in owner.Items) { + if (item != null && item.Checked) { + count++; + } + } + return count; + } + } + + private int[] IndicesArray { + get { + int[] indices = new int[Count]; + int index = 0; + for(int i=0; i < owner.Items.Count && index < indices.Length; ++i) { + if (owner.Items[i].Checked) { + indices[index++] = i; + } + } + return indices; + } + } + + /// + /// + /// Selected item in the list. + /// + public int this[int index] { + get { + + if (index < 0) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + // Loop through the main collection until we find the right index. + // + int cnt = owner.Items.Count; + int nChecked = 0; + for(int i = 0; i < cnt; i++) { + ListViewItem item = owner.Items[i]; + + if (item.Checked) { + if (nChecked == index) { + return i; + } + nChecked++; + } + } + + // Should never get to this point. + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(); + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int checkedIndex) { + if (owner.Items[checkedIndex].Checked) { + return true; + } + else { + return false; + } + } + + /// + /// + bool IList.Contains(object checkedIndex) { + if (checkedIndex is Int32) { + return Contains((int)checkedIndex); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int checkedIndex) { + int[] indices = IndicesArray; + for(int index=0; index < indices.Length; ++index) { + if (indices[index] == checkedIndex) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object checkedIndex) { + if (checkedIndex is Int32) { + return IndexOf((int)checkedIndex); + } + else { + return -1; + } + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(); + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (Count > 0) { + System.Array.Copy(IndicesArray, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + int[] indices = IndicesArray; + if (indices != null) { + return indices.GetEnumerator(); + } + else { + return new int[0].GetEnumerator(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class CheckedListViewItemCollection : IList { + private ListView owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public CheckedListViewItemCollection(ListView owner) { + this.owner = owner; + } + + /// + /// + /// Number of currently selected items. + /// + [Browsable(false)] + public int Count { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + return owner.CheckedIndices.Count; + } + } + + private ListViewItem[] ItemArray { + get { + ListViewItem[] items = new ListViewItem[Count]; + int index = 0; + for(int i=0; i < owner.Items.Count && index < items.Length; ++i) { + if (owner.Items[i].Checked) { + items[index++] = owner.Items[i]; + } + } + return items; + } + } + + /// + /// + /// Selected item in the list. + /// + public ListViewItem this[int index] { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + int itemIndex = owner.CheckedIndices[index]; + return owner.Items[itemIndex]; + } + } + + /// + /// + object IList.this[int index] { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + return this[index]; + } + set { + throw new NotSupportedException(); + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ListViewItem this[string key] { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + // We do not support null and empty string as valid keys. + if(string.IsNullOrEmpty(key)) { + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if(IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ListViewItem item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + if (item != null && item.ListView == owner && item.Checked) { + return true; + } + else { + return false; + } + } + + /// + /// + bool IList.Contains(object item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + if (item is ListViewItem) { + return Contains((ListViewItem)item); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ListViewItem item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + ListViewItem[] items = ItemArray; + for(int index=0; index < items.Length; ++index) { + if (items[index] == item) { + return index; + } + } + return -1; + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)) { + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + int IList.IndexOf(object item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + if (item is ListViewItem) { + return IndexOf((ListViewItem)item); + } + else { + return -1; + } + } + + /// + /// + int IList.Add(object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Clear() { + throw new NotSupportedException(); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(); + } + + /// + public void CopyTo(Array dest, int index) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + if (Count > 0) { + System.Array.Copy(ItemArray, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessCheckedItemsCollectionWhenInVirtualMode)); + } + + ListViewItem[] items = ItemArray; + if (items != null) { + return items.GetEnumerator(); + } + else { + return new ListViewItem[0].GetEnumerator(); + } + } + + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class SelectedIndexCollection : IList { + private ListView owner; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedIndexCollection(ListView owner) { + this.owner = owner; + } + + /// + /// + /// Number of currently selected items. + /// + [Browsable(false)] + public int Count { + get { + if (owner.IsHandleCreated) { + return unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETSELECTEDCOUNT, 0, 0)); + } + else { + if (owner.savedSelectedItems != null) { + return owner.savedSelectedItems.Count; + } + return 0; + } + } + } + + private int[] IndicesArray { + get { + int count = Count; + int[] indices = new int[count]; + + if (owner.IsHandleCreated) { + int displayIndex = -1; + for (int i = 0; i < count; i++) { + int fidx = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETNEXTITEM, displayIndex, NativeMethods.LVNI_SELECTED)); + + if (fidx > -1) { + indices[i] = fidx; + displayIndex = fidx; + } + else + throw new InvalidOperationException(SR.GetString(SR.SelectedNotEqualActual)); + } + } + else { + Debug.Assert(owner.savedSelectedItems != null || count == 0, "if the count of selectedItems is greater than 0 then the selectedItems should have been saved by now"); + for (int i = 0; i < count; i++) { + indices[i] = owner.savedSelectedItems[i].Index; + } + } + + return indices; + } + } + + /// + /// + /// Selected item in the list. + /// + public int this[int index] { + get { + + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.IsHandleCreated) { + + // Count through the selected items in the ListView, until + // we reach the 'index'th selected item. + // + int fidx = -1; + for(int count = 0; count <= index; count++) { + fidx = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETNEXTITEM, fidx, NativeMethods.LVNI_SELECTED)); + Debug.Assert(fidx != -1, "Invalid index returned from LVM_GETNEXTITEM"); + } + + return fidx; + } + else { + Debug.Assert(owner.savedSelectedItems != null, "Null selected items collection"); + return owner.savedSelectedItems[index].Index; + } + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(); + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(int selectedIndex) { + return owner.Items[selectedIndex].Selected; + } + + /// + /// + bool IList.Contains(object selectedIndex) { + if (selectedIndex is Int32) { + return Contains((int)selectedIndex); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(int selectedIndex) { + int[] indices = IndicesArray; + for(int index=0; index < indices.Length; ++index) { + if (indices[index] == selectedIndex) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object selectedIndex) { + if (selectedIndex is Int32) { + return IndexOf((int)selectedIndex); + } + else { + return -1; + } + } + + /// + /// + int IList.Add(object value) + { + if (value is int) + { + return Add((int) value); + } + else + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "value", ((value).ToString()))); + } + } + + /// + /// + void IList.Clear() { + Clear(); + } + + /// + /// + void IList.Insert(int index, object value) { + throw new NotSupportedException(); + } + + /// + /// + void IList.Remove(object value) + { + if (value is int) + { + Remove((int) value); + } + else + { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "value", ((value).ToString()))); + } + } + + /// + /// + void IList.RemoveAt(int index) { + throw new NotSupportedException(); + } + + /// + public int Add(int itemIndex) + { + if (this.owner.VirtualMode) + { + if (itemIndex < 0 || itemIndex >= this.owner.VirtualListSize) + { + throw new ArgumentOutOfRangeException("itemIndex", SR.GetString(SR.InvalidArgument, "itemIndex", (itemIndex).ToString(CultureInfo.CurrentCulture))); + } + if (this.owner.IsHandleCreated) + { + this.owner.SetItemState(itemIndex, NativeMethods.LVIS_SELECTED, NativeMethods.LVIS_SELECTED); + return this.Count; + } + else + { + return -1; + } + } + else + { + if (itemIndex < 0 || itemIndex >= this.owner.Items.Count) + { + throw new ArgumentOutOfRangeException("itemIndex", SR.GetString(SR.InvalidArgument, "itemIndex", (itemIndex).ToString(CultureInfo.CurrentCulture))); + } + this.owner.Items[itemIndex].Selected = true; + return this.Count; + } + } + + /// + public void Clear() + { + if (!this.owner.VirtualMode) + { + this.owner.savedSelectedItems = null; + } + if (this.owner.IsHandleCreated) + { + this.owner.SetItemState(-1, 0, NativeMethods.LVIS_SELECTED); + } + } + + /// + public void CopyTo(Array dest, int index) { + if (Count > 0) { + System.Array.Copy(IndicesArray, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + int[] indices = IndicesArray; + if (indices != null) { + return indices.GetEnumerator(); + } + else { + return new int[0].GetEnumerator(); + } + } + + /// + public void Remove(int itemIndex) + { + if (this.owner.VirtualMode) + { + if (itemIndex < 0 || itemIndex >= this.owner.VirtualListSize) + { + throw new ArgumentOutOfRangeException("itemIndex", SR.GetString(SR.InvalidArgument, "itemIndex", (itemIndex).ToString(CultureInfo.CurrentCulture))); + } + if (this.owner.IsHandleCreated) + { + this.owner.SetItemState(itemIndex, 0, NativeMethods.LVIS_SELECTED); + } + } + else + { + if (itemIndex < 0 || itemIndex >= this.owner.Items.Count) + { + throw new ArgumentOutOfRangeException("itemIndex", SR.GetString(SR.InvalidArgument, "itemIndex", (itemIndex).ToString(CultureInfo.CurrentCulture))); + } + this.owner.Items[itemIndex].Selected = false; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class SelectedListViewItemCollection : IList { + private ListView owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + /* C#r: protected */ + /// + /// + /// [To be supplied.] + /// + public SelectedListViewItemCollection(ListView owner) { + this.owner = owner; + } + + private ListViewItem[] SelectedItemArray { + get { + if (owner.IsHandleCreated) { + int cnt = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETSELECTEDCOUNT, 0, 0)); + + ListViewItem[] lvitems = new ListViewItem[cnt]; + + int displayIndex = -1; + + for (int i = 0; i < cnt; i++) { + int fidx = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETNEXTITEM, displayIndex, NativeMethods.LVNI_SELECTED)); + + if (fidx > -1) { + lvitems[i] = owner.Items[fidx]; + displayIndex = fidx; + } + else + throw new InvalidOperationException(SR.GetString(SR.SelectedNotEqualActual)); + } + + return lvitems; + + } + else { + if (owner.savedSelectedItems != null) { + ListViewItem[] cloned = new ListViewItem[owner.savedSelectedItems.Count]; + for (int i = 0; i < owner.savedSelectedItems.Count; i ++) { + cloned[i] = owner.savedSelectedItems[i]; + } + return cloned; + } + else { + return new ListViewItem[0]; + } + } + } + } + + /// + /// + /// Number of currently selected items. + /// + [Browsable(false)] + public int Count { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + if (owner.IsHandleCreated) { + return unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETSELECTEDCOUNT, 0, 0)); + } + else { + if (owner.savedSelectedItems != null) { + return owner.savedSelectedItems.Count; + } + return 0; + } + } + } + + /// + /// + /// Selected item in the list. + /// + public ListViewItem this[int index] { + get { + + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.IsHandleCreated) { + + // Count through the selected items in the ListView, until + // we reach the 'index'th selected item. + // + int fidx = -1; + for(int count = 0; count <= index; count++) { + fidx = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_GETNEXTITEM, fidx, NativeMethods.LVNI_SELECTED)); + Debug.Assert(fidx != -1, "Invalid index returned from LVM_GETNEXTITEM"); + } + + return owner.Items[fidx]; + } + else { + Debug.Assert(owner.savedSelectedItems != null, "Null selected items collection"); + return owner.savedSelectedItems[index]; + } + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ListViewItem this[string key] { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + object IList.this[int index] { + get { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + return this[index]; + } + set { + // SelectedListViewItemCollection is read-only + throw new NotSupportedException(); + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return true; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + int IList.Add(object value) { + // SelectedListViewItemCollection is read-only + throw new NotSupportedException(); + } + + /// + /// + void IList.Insert(int index, object value) { + // SelectedListViewItemCollection is read-only + throw new NotSupportedException(); + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + void IList.Remove(object value) { + // SelectedListViewItemCollection is read-only + throw new NotSupportedException(); + } + + /// + /// + void IList.RemoveAt(int index) { + // SelectedListViewItemCollection is read-only + throw new NotSupportedException(); + } + + /// + /// + /// Unselects all items. + /// + public void Clear() { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + ListViewItem[] items = SelectedItemArray; + for (int i=0; i < items.Length; i++) { + items[i].Selected = false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ListViewItem item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + return (IndexOf(item) != -1); + } + + /// + /// + bool IList.Contains(object item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + if (item is ListViewItem) { + return Contains((ListViewItem)item); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + if (Count > 0) { + System.Array.Copy(SelectedItemArray, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + ListViewItem[] items = SelectedItemArray; + if (items != null) { + return items.GetEnumerator(); + } + else { + return new ListViewItem[0].GetEnumerator(); + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ListViewItem item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + ListViewItem[] items = SelectedItemArray; + for(int index=0; index < items.Length; ++index) { + if (items[index] == item) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object item) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + if (item is ListViewItem) { + return IndexOf((ListViewItem)item); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode)); + } + + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class ColumnHeaderCollection : IList { + private ListView owner; + + /// + /// + /// [To be supplied.] + /// + public ColumnHeaderCollection(ListView owner) { + this.owner = owner; + } + + /// + /// + /// Given a Zero based index, returns the ColumnHeader object + /// for the column at that index + /// + public virtual ColumnHeader this[int index] { + get { + if (owner.columnHeaders == null || index < 0 || index >= owner.columnHeaders.Length) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + return owner.columnHeaders[index]; + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(); + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ColumnHeader this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + /// The number of columns the ListView currently has in Details view. + /// + [Browsable(false)] + public int Count { + get { + return owner.columnHeaders == null ? 0 : owner.columnHeaders.Length; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + + + + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways. + private int lastAccessedIndex = -1; + + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + + /// + /// + /// Adds a column to the end of the Column list + /// + public virtual ColumnHeader Add(string text, int width, HorizontalAlignment textAlign) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + return owner.InsertColumn(Count, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public virtual int Add(ColumnHeader value) { + int index = Count; + owner.InsertColumn(index, value); + return index; + } + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string text) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + return owner.InsertColumn(Count, columnHeader); + } + + // <-- NEW ADD OVERLOADS IN WHIDBEY + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string text, int width) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + columnHeader.Width = width; + return owner.InsertColumn(Count, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string key, string text) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Name = key; + columnHeader.Text = text; + return owner.InsertColumn(Count, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string key, string text, int width) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + return owner.InsertColumn(Count, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string key, string text, int width, HorizontalAlignment textAlign, string imageKey) { + ColumnHeader columnHeader = new ColumnHeader(imageKey); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + return owner.InsertColumn(Count, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ColumnHeader Add(string key, string text, int width, HorizontalAlignment textAlign, int imageIndex) { + ColumnHeader columnHeader = new ColumnHeader(imageIndex); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + return owner.InsertColumn(Count, columnHeader); + } + + // END - NEW ADD OVERLOADS IN WHIDBEY --> + + /// + /// + /// [To be supplied.] + /// + public virtual void AddRange(ColumnHeader[] values) { + if (values == null) { + throw new ArgumentNullException("values"); + } + + Hashtable usedIndices = new Hashtable(); + int [] indices = new int[values.Length]; + + for (int i=0; i= 0 && values[i].DisplayIndex < values.Length) { + usedIndices.Add(values[i].DisplayIndex, i); + } + + indices[i] = values[i].DisplayIndex; + Add( values[i] ); + } + + if (usedIndices.Count == values.Length) { + owner.SetDisplayIndices( indices ); + } + } + + /// + /// + int IList.Add(object value) { + if (value is ColumnHeader) { + return Add((ColumnHeader)value); + } + else { + throw new ArgumentException(SR.GetString(SR.ColumnHeaderCollectionInvalidArgument)); + } + } + + /// + /// + /// Removes all columns from the list view. + /// + public virtual void Clear() { + // Delete the columns + if (owner.columnHeaders != null) { + if (owner.View == View.Tile) { + // in Tile view our ListView uses the column header collection to update the Tile Information + for (int colIdx = owner.columnHeaders.Length-1; colIdx >= 0; colIdx--) { + int w = owner.columnHeaders[colIdx].Width; // Update width before detaching from ListView + owner.columnHeaders[colIdx].OwnerListview = null; + } + owner.columnHeaders = null; + if (owner.IsHandleCreated) { + owner.RecreateHandleInternal(); + } + } else { + for (int colIdx = owner.columnHeaders.Length-1; colIdx >= 0; colIdx--) { + int w = owner.columnHeaders[colIdx].Width; // Update width before detaching from ListView + if (owner.IsHandleCreated) { + owner.SendMessage(NativeMethods.LVM_DELETECOLUMN, colIdx, 0); + } + owner.columnHeaders[colIdx].OwnerListview = null; + } + owner.columnHeaders = null; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ColumnHeader value) { + return IndexOf(value) != -1; + } + + /// + /// + bool IList.Contains(object value) { + if (value is ColumnHeader) { + return Contains((ColumnHeader)value); + } + return false; + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (Count > 0) { + System.Array.Copy(owner.columnHeaders, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ColumnHeader value) { + for(int index=0; index < Count; ++index) { + if (this[index] == value) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object value) { + if (value is ColumnHeader) { + return IndexOf((ColumnHeader)value); + } + return -1; + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, ColumnHeader value) { + if (index < 0 || index > Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + owner.InsertColumn(index, value); + } + + /// + /// + void IList.Insert(int index, object value) { + if (value is ColumnHeader) { + Insert(index, (ColumnHeader)value); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string text, int width, HorizontalAlignment textAlign) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + Insert(index, columnHeader); + } + + // <-- NEW INSERT OVERLOADS IN WHIDBEY + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string text) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + Insert(index, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string text, int width) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Text = text; + columnHeader.Width = width; + Insert(index, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Name = key; + columnHeader.Text = text; + Insert(index, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text, int width) { + ColumnHeader columnHeader = new ColumnHeader(); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + Insert(index, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey) { + ColumnHeader columnHeader = new ColumnHeader(imageKey); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + Insert(index, columnHeader); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex) { + ColumnHeader columnHeader = new ColumnHeader(imageIndex); + columnHeader.Name = key; + columnHeader.Text = text; + columnHeader.Width = width; + columnHeader.TextAlign = textAlign; + Insert(index, columnHeader); + } + + + // END - NEW INSERT OVERLOADS IN WHIDBEY --> + + /// + /// + /// removes a column from the ListView + /// + public virtual void RemoveAt(int index) { + if (index < 0 || index >= owner.columnHeaders.Length) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + int w = owner.columnHeaders[index].Width; // Update width before detaching from ListView + + // in Tile view our ListView uses the column header collection to update the Tile Information + if (owner.IsHandleCreated && this.owner.View != View.Tile) { + int retval = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_DELETECOLUMN, index, 0)); + + if (0 == retval) + throw new ArgumentException(SR.GetString(SR.InvalidArgument, + "index", + (index).ToString(CultureInfo.CurrentCulture))); + } + + // we need to update the display indices + int[] indices = new int[this.Count-1]; + + ColumnHeader removeHdr = this[index]; + for (int i = 0; i < this.Count; i ++) { + ColumnHeader hdr = this[i]; + if (i != index) { + if (hdr.DisplayIndex >= removeHdr.DisplayIndex) { + hdr.DisplayIndexInternal --; + } + indices[ i>index ? i-1:i ] = hdr.DisplayIndexInternal; + } + } + + removeHdr.DisplayIndexInternal = -1; + + owner.columnHeaders[index].OwnerListview = null; + int columnCount = owner.columnHeaders.Length; + Debug.Assert(columnCount >= 1, "Column mismatch"); + if (columnCount == 1) + owner.columnHeaders = null; + else { + ColumnHeader[] newHeaders = new ColumnHeader[--columnCount]; + if (index > 0) + System.Array.Copy(owner.columnHeaders, 0, newHeaders, 0, index); + if (index < columnCount) + System.Array.Copy(owner.columnHeaders, index+1, newHeaders, index, columnCount - index); + owner.columnHeaders = newHeaders; + } + + // in Tile view our ListView uses the column header collection to update the Tile Information + if (owner.IsHandleCreated && this.owner.View == View.Tile) { + this.owner.RecreateHandleInternal(); + } + + owner.SetDisplayIndices( indices ); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Remove(ColumnHeader column) { + int index = IndexOf(column); + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + void IList.Remove(object value) { + if (value is ColumnHeader) { + Remove((ColumnHeader)value); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (owner.columnHeaders != null) { + return owner.columnHeaders.GetEnumerator(); + } + else + { + return new ColumnHeader[0].GetEnumerator(); + } + } + } + + /// + /// + /// Represents the collection of items in a ListView or ListViewGroup + /// + [ListBindable(false)] + public class ListViewItemCollection : IList { + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + internal interface IInnerList + { + int Count { get; } + bool OwnerIsVirtualListView { get; } + bool OwnerIsDesignMode{ get; } + ListViewItem this[int index] { get; set; } + ListViewItem Add(ListViewItem item); + void AddRange(ListViewItem[] items); + void Clear(); + bool Contains(ListViewItem item); + void CopyTo(Array dest, int index); + IEnumerator GetEnumerator(); + int IndexOf(ListViewItem item); + ListViewItem Insert(int index, ListViewItem item); + void Remove(ListViewItem item); + void RemoveAt(int index); + } + + private IInnerList innerList; + + /// + /// + /// [To be supplied.] + /// + public ListViewItemCollection(ListView owner) { + // Kept for APPCOMPAT reasons. + // In Whidbey this constructor is a no-op. + + //vsw 481554: initialize the inner list w/ a dummy list. + this.innerList = new ListViewNativeItemCollection(owner); + } + + internal ListViewItemCollection(IInnerList innerList) { + Debug.Assert(innerList != null, "Can't pass in null innerList"); + this.innerList = innerList; + } + + private IInnerList InnerList { + get + { + return innerList; + } + } + + /// + /// + /// Returns the total number of items within the list view. + /// + [Browsable(false)] + public int Count { + get { + return InnerList.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Returns the ListViewItem at the given index. + /// + public virtual ListViewItem this[int index] { + get { + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + return InnerList[index]; + } + set { + if (index < 0 || index >= InnerList.Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + InnerList[index] = value; + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is ListViewItem) { + this[index] = (ListViewItem)value; + } + else if (value != null) { + this[index] = new ListViewItem(value.ToString(), -1); + } + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ListViewItem this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(string text) { + return Add(text, -1); + } + + /// + /// + int IList.Add(object item) { + if (item is ListViewItem) { + return IndexOf(Add((ListViewItem)item)); + } + else if (item != null) { + return IndexOf(Add(item.ToString())); + } + return -1; + } + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(string text, int imageIndex) { + ListViewItem li = new ListViewItem(text, imageIndex); + Add(li); + return li; + } + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(ListViewItem value) { + InnerList.Add(value); + return value; + } + + // <-- NEW ADD OVERLOADS IN WHIDBEY + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(string text, string imageKey) { + ListViewItem li = new ListViewItem(text, imageKey); + Add(li); + return li; + } + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(string key, string text, string imageKey) { + ListViewItem li = new ListViewItem(text, imageKey); + li.Name = key; + Add(li); + return li; + } + + /// + /// + /// Add an item to the ListView. The item will be inserted either in + /// the correct sorted position, or, if no sorting is set, at the end + /// of the list. + /// + public virtual ListViewItem Add(string key, string text, int imageIndex) { + ListViewItem li = new ListViewItem(text, imageIndex); + li.Name = key; + Add(li); + return li; + } + + + // END - NEW ADD OVERLOADS IN WHIDBEY --> + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ListViewItem[] items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + + InnerList.AddRange(items); + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ListViewItemCollection items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + + ListViewItem[] itemArray = new ListViewItem[items.Count]; + items.CopyTo(itemArray, 0); + InnerList.AddRange(itemArray); + } + + /// + /// + /// Removes all items from the list view. + /// + public virtual void Clear() { + InnerList.Clear(); + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ListViewItem item) { + return InnerList.Contains(item); + } + + /// + /// + bool IList.Contains(object item) { + if (item is ListViewItem) { + return Contains((ListViewItem)item); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + InnerList.CopyTo(dest, index); + } + + /// + /// + /// Searches for Controls by their Name property, builds up an array + /// of all the controls that match. + /// + /// + public ListViewItem[] Find (string key, bool searchAllSubItems) { + ArrayList foundItems = FindInternal(key, searchAllSubItems, this, new ArrayList()); + + ListViewItem[] stronglyTypedFoundItems= new ListViewItem[foundItems.Count]; + foundItems.CopyTo(stronglyTypedFoundItems, 0); + + return stronglyTypedFoundItems; + } + + /// + /// + /// Searches for Controls by their Name property, builds up an arraylist + /// of all the controls that match. + /// + /// + /// + private ArrayList FindInternal(string key, bool searchAllSubItems, ListViewItemCollection listViewItems, ArrayList foundItems) { + if ((listViewItems == null) || (foundItems == null)) { + return null; // + } + + for (int i = 0; i < listViewItems.Count; i++) { + + if (WindowsFormsUtils.SafeCompareStrings(listViewItems[i].Name, key, /* ignoreCase = */ true)) { + foundItems.Add(listViewItems[i]); + } + else { + if (searchAllSubItems) { + // start from 1, as we've already compared subitems[0] + for(int j = 1; j < listViewItems[i].SubItems.Count; j++) { + if (WindowsFormsUtils.SafeCompareStrings(listViewItems[i].SubItems[j].Name, key, /* ignoreCase = */ true)) { + foundItems.Add(listViewItems[i]); + break; + } + } + } + } + } + + return foundItems; + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (this.InnerList.OwnerIsVirtualListView && !this.InnerList.OwnerIsDesignMode) { + // Throw the exception only at runtime. + throw new InvalidOperationException(SR.GetString(SR.ListViewCantGetEnumeratorInVirtualMode)); + } + return InnerList.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ListViewItem item) { + for(int index=0; index < Count; ++index) { + if (this[index] == item) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object item) { + if (item is ListViewItem) { + return IndexOf((ListViewItem)item); + } + else { + return -1; + } + } + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem Insert(int index, ListViewItem item) { + if (index < 0 || index > Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + InnerList.Insert(index, item); + return item; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem Insert(int index, string text) { + return Insert(index, new ListViewItem(text)); + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem Insert(int index, string text, int imageIndex) { + return Insert(index, new ListViewItem(text, imageIndex)); + } + + /// + /// + void IList.Insert(int index, object item) { + if (item is ListViewItem) { + Insert(index, (ListViewItem)item); + } + else if (item != null) { + Insert(index, item.ToString()); + } + } + + // <-- NEW INSERT OVERLOADS IN WHIDBEY + + /// + /// + /// [To be supplied.] + /// + public ListViewItem Insert(int index, string text, string imageKey) { + return Insert(index, new ListViewItem(text, imageKey)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ListViewItem Insert(int index, string key, string text, string imageKey) { + ListViewItem li = new ListViewItem(text, imageKey); + li.Name = key; + return Insert(index, li); + } + + /// + /// + /// [To be supplied.] + /// + public virtual ListViewItem Insert(int index, string key, string text, int imageIndex) { + ListViewItem li = new ListViewItem(text, imageIndex); + li.Name = key; + return Insert(index, li); + } + + // END - NEW INSERT OVERLOADS IN WHIDBEY --> + + /// + /// + /// Removes an item from the ListView + /// + public virtual void Remove(ListViewItem item) { + InnerList.Remove(item); + } + + /// + /// + /// Removes an item from the ListView + /// + public virtual void RemoveAt(int index) { + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + InnerList.RemoveAt(index); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + void IList.Remove(object item) { + if (item == null || !(item is ListViewItem)) { + return; + } + + Remove((ListViewItem)item); + } + } + + // Overrides ListViewItemCollection methods and properties to automatically update + // the native listview. + // + internal class ListViewNativeItemCollection : ListViewItemCollection.IInnerList { + private ListView owner; + + public ListViewNativeItemCollection(ListView owner) { + this.owner = owner; + } + + public int Count { + get { + owner.ApplyUpdateCachedItems(); + if (owner.VirtualMode) { + return owner.VirtualListSize; + } + else { + return owner.itemCount; + } + } + } + + public bool OwnerIsVirtualListView { + get { + return owner.VirtualMode; + } + } + + public bool OwnerIsDesignMode { + get { + return owner.DesignMode; + } + } + + public ListViewItem this[int displayIndex] { + get { + owner.ApplyUpdateCachedItems(); + + if (owner.VirtualMode) { + + // if we are showing virtual items, we need to get the item from the user + RetrieveVirtualItemEventArgs rVI = new RetrieveVirtualItemEventArgs(displayIndex); + owner.OnRetrieveVirtualItem(rVI); + rVI.Item.SetItemIndex(this.owner, displayIndex); + return rVI.Item; + } + else { + if (displayIndex < 0 || displayIndex >= owner.itemCount) + throw new ArgumentOutOfRangeException("displayIndex", SR.GetString(SR.InvalidArgument, "displayIndex", (displayIndex).ToString(CultureInfo.CurrentCulture))); + + if (owner.IsHandleCreated && !owner.ListViewHandleDestroyed) { + Debug.Assert(owner.listItemsArray == null, "listItemsArray not null, even though handle created"); + return (ListViewItem)owner.listItemsTable[DisplayIndexToID(displayIndex)]; + } + else { + Debug.Assert(owner.listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + return (ListViewItem)owner.listItemsArray[displayIndex]; + } + } + } + set { + owner.ApplyUpdateCachedItems(); + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantModifyTheItemCollInAVirtualListView)); + } + + if (displayIndex < 0 || displayIndex >= owner.itemCount) + throw new ArgumentOutOfRangeException("displayIndex", SR.GetString(SR.InvalidArgument, "displayIndex", (displayIndex).ToString(CultureInfo.CurrentCulture))); + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + RemoveAt(displayIndex); + Insert(displayIndex, value); + } + } + + public ListViewItem Add(ListViewItem value) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAddItemsToAVirtualListView)); + } else { + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon || this.Count == 0, "the FlipView... bit is turned off after adding 1 item."); + + // PERF. + // Get the Checked bit before adding it to the back end. + // This saves a call into NativeListView to retrieve the real index. + bool valueChecked = value.Checked; + + owner.InsertItems(owner.itemCount, new ListViewItem[]{value}, true); + + if (owner.IsHandleCreated && !owner.CheckBoxes && valueChecked) { + owner.UpdateSavedCheckedItems(value, true /*addItem*/); + } + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + return value; + } + } + + public void AddRange(ListViewItem[] values) { + if (values == null) { + throw new ArgumentNullException("values"); + } + + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAddItemsToAVirtualListView)); + } + + IComparer comparer = owner.listItemSorter; + owner.listItemSorter = null; + + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon || this.Count == 0, "the FlipView... bit is turned off after adding 1 item."); + + bool[] checkedValues = null; + + if (owner.IsHandleCreated && !owner.CheckBoxes) { + // PERF. + // Cache the Checked bit before adding the item to the list view. + // This saves a bunch of calls to native list view when we want to UpdateSavedCheckedItems. + // + checkedValues = new bool[values.Length]; + for (int i = 0; i < values.Length; i ++) { + checkedValues[i] = values[i].Checked; + } + } + + try { + owner.BeginUpdate(); + owner.InsertItems(owner.itemCount, values, true); + + if (owner.IsHandleCreated && !owner.CheckBoxes) { + for (int i = 0; i < values.Length; i ++) { + if (checkedValues[i]) { + owner.UpdateSavedCheckedItems(values[i], true /*addItem*/); + } + } + } + + } + finally { + owner.listItemSorter = comparer; + owner.EndUpdate(); + } + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + if (comparer != null || (owner.Sorting != SortOrder.None) && !owner.VirtualMode) { + owner.Sort(); + } + } + + private int DisplayIndexToID(int displayIndex) { + Debug.Assert(!owner.VirtualMode, "in virtual mode, this method does not make any sense"); + if (owner.IsHandleCreated && !owner.ListViewHandleDestroyed) { + // Obtain internal index of the item + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.mask = NativeMethods.LVIF_PARAM; + lvItem.iItem = displayIndex; + UnsafeNativeMethods.SendMessage(new HandleRef(owner, owner.Handle), NativeMethods.LVM_GETITEM, 0, ref lvItem); + return (int) lvItem.lParam; + } + else + { + return this[displayIndex].ID; + } + } + + public void Clear() { + if (owner.itemCount > 0) { + owner.ApplyUpdateCachedItems(); + + if (owner.IsHandleCreated && !owner.ListViewHandleDestroyed) { + // walk the items to see which ones are selected. + // we use the LVM_GETNEXTITEM message to see what the next selected item is + // so we can avoid checking selection for each one. + // + int count = owner.Items.Count; + int nextSelected = (int)UnsafeNativeMethods.SendMessage(new HandleRef(owner, owner.Handle), NativeMethods.LVM_GETNEXTITEM, -1, NativeMethods.LVNI_SELECTED); + for (int i = 0; i < count; i++) { + ListViewItem item = owner.Items[i]; + Debug.Assert(item != null, "Failed to get item at index " + i.ToString(CultureInfo.InvariantCulture)); + if (item != null) { + + // if it's the one we're looking for, ask for the next one + // + if (i == nextSelected) { + item.StateSelected = true; + nextSelected = (int)UnsafeNativeMethods.SendMessage(new HandleRef(owner, owner.Handle), NativeMethods.LVM_GETNEXTITEM, nextSelected, NativeMethods.LVNI_SELECTED); + } + else { + // otherwise it's false + // + item.StateSelected = false; + } + item.UnHost(i, false); + } + } + Debug.Assert(owner.listItemsArray == null, "listItemsArray not null, even though handle created"); + + UnsafeNativeMethods.SendMessage(new HandleRef(owner, owner.Handle), NativeMethods.LVM_DELETEALLITEMS, 0, 0); + + // There's a problem in the list view that if it's in small icon, it won't pick upo the small icon + // sizes until it changes from large icon, so we flip it twice here... + // + if (owner.View == View.SmallIcon) { + if (this.owner.ComctlSupportsVisualStyles) { + this.owner.FlipViewToLargeIconAndSmallIcon = true; + } else { + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon, "we only set this when comctl 6.0 is loaded"); + owner.View = View.LargeIcon; + owner.View = View.SmallIcon; + } + } + } + else { + + int count = owner.Items.Count; + + for (int i = 0; i < count; i++) { + ListViewItem item = owner.Items[i]; + if (item != null) { + item.UnHost(i, true); + } + } + + Debug.Assert(owner.listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + owner.listItemsArray.Clear(); + } + + owner.listItemsTable.Clear(); + if (owner.IsHandleCreated && !owner.CheckBoxes) { + owner.savedCheckedItems = null; + } + owner.itemCount = 0; + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + } + } + + public bool Contains(ListViewItem item) { + + owner.ApplyUpdateCachedItems(); + if (owner.IsHandleCreated && !owner.ListViewHandleDestroyed) { + Debug.Assert(owner.listItemsArray == null, "listItemsArray not null, even though handle created"); + return owner.listItemsTable[item.ID] == item; + } + else { + Debug.Assert(owner.listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + return owner.listItemsArray.Contains(item); + } + } + + public ListViewItem Insert(int index, ListViewItem item) { + int count = 0; + if (owner.VirtualMode) { + count = Count; + } + else { + count = owner.itemCount; + } + + if (index < 0 || index > count) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantAddItemsToAVirtualListView)); + } + + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon || this.Count == 0, "the FlipView... bit is turned off after adding 1 item."); + + if (index < count) { + // if we're not inserting at the end, force the add. + // + owner.ApplyUpdateCachedItems(); + } + + owner.InsertItems(index, new ListViewItem[]{item}, true); + if (owner.IsHandleCreated && !owner.CheckBoxes && item.Checked) { + owner.UpdateSavedCheckedItems(item, true /*addItem*/); + } + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + return item; + } + + public int IndexOf(ListViewItem item) { + Debug.Assert(!owner.VirtualMode, "in virtual mode, this function does not make any sense"); + for(int i=0; i < Count; i++) { + if (item == this[i]) { + return i; + } + } + return -1; + } + + public void Remove(ListViewItem item) { + int index = owner.VirtualMode ? Count - 1 : IndexOf(item); + + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon || this.Count == 0, "the FlipView... bit is turned off after adding 1 item."); + + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantRemoveItemsFromAVirtualListView)); + } + + if (index != -1) { + RemoveAt(index); + } + } + + public void RemoveAt(int index) { + if (owner.VirtualMode) { + throw new InvalidOperationException(SR.GetString(SR.ListViewCantRemoveItemsFromAVirtualListView)); + } + + if (index < 0 || index >= owner.itemCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + Debug.Assert(!this.owner.FlipViewToLargeIconAndSmallIcon || this.Count == 0, "the FlipView... bit is turned off after adding 1 item."); + + if (owner.IsHandleCreated && !owner.CheckBoxes && this[index].Checked) { + owner.UpdateSavedCheckedItems(this[index], false /*addItem*/); + } + + owner.ApplyUpdateCachedItems(); + int itemID = DisplayIndexToID(index); + this[index].UnHost(true); + + if (owner.IsHandleCreated) { + + Debug.Assert(owner.listItemsArray == null, "listItemsArray not null, even though handle created"); + int retval = unchecked( (int) (long)owner.SendMessage(NativeMethods.LVM_DELETEITEM, index, 0)); + + if (0 == retval) + throw new ArgumentException(SR.GetString(SR.InvalidArgument, + "index", + (index).ToString(CultureInfo.CurrentCulture))); + } + else { + Debug.Assert(owner.listItemsArray != null, "listItemsArray is null, but the handle isn't created"); + owner.listItemsArray.RemoveAt(index); + } + + owner.itemCount--; + owner.listItemsTable.Remove(itemID); + + if (this.owner.ExpectingMouseUp) { + this.owner.ItemCollectionChangedInMouseDown = true; + } + + } + + public void CopyTo(Array dest, int index) { + if (owner.itemCount > 0) { + for(int displayIndex=0; displayIndex < Count; ++displayIndex) { + dest.SetValue(this[displayIndex], index++); + } + } + } + + public IEnumerator GetEnumerator() { + ListViewItem[] items = new ListViewItem[this.owner.itemCount]; + this.CopyTo(items, 0); + + return items.GetEnumerator(); + } + } + + /// + /// Creates the new instance of AccessibleObject for this ListView control. + /// Returning ListViewAccessibleObject is only available in applications that + /// are recompiled to target .NET Framework 4.7.3 or opt-in into this feature + /// using a compatibility switch. + /// + /// + /// The AccessibleObject for this ListView instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ListViewAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + internal class ListViewAccessibleObject : ControlAccessibleObject { + + private ListView owner; + + internal ListViewAccessibleObject(ListView owner) : base(owner) { + this.owner = owner; + } + + internal override bool IsIAccessibleExSupported() + { + if (owner != null) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ItemStatusPropertyId) { + switch (owner.Sorting) { + case SortOrder.None: + return SR.GetString(SR.NotSortedAccessibleStatus); + case SortOrder.Ascending: + return SR.GetString(SR.SortedAscendingAccessibleStatus); + case SortOrder.Descending: + return SR.GetString(SR.SortedDescendingAccessibleStatus); + } + } + + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewAlignment.cs b/WindowsForms/Managed/System/WinForms/ListViewAlignment.cs new file mode 100644 index 000000000..67690a6d5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewAlignment.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies how items align in the . + /// + /// + public enum ListViewAlignment { + + /// + /// + /// + /// When the user moves an + /// item, it remains where it is dropped. + /// + /// + Default = NativeMethods.LVA_DEFAULT, + + /// + /// + /// + /// Items are aligned to the top of the control. + /// + /// + Top = NativeMethods.LVA_ALIGNTOP, + + /// + /// + /// + /// Items are aligned to the left of the control. + /// + /// + Left = NativeMethods.LVA_ALIGNLEFT, + + /// + /// + /// + /// Items + /// are aligned to an invisible grid in the control. + /// When the user moves an item, it moves to the + /// closest juncture in the grid. + /// + /// + SnapToGrid = NativeMethods.LVA_SNAPTOGRID, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListViewGroup.cs b/WindowsForms/Managed/System/WinForms/ListViewGroup.cs new file mode 100644 index 000000000..9612abe46 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewGroup.cs @@ -0,0 +1,660 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +using System.Collections; +using System.ComponentModel; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Win32; +using System.Globalization; +using System.Security.Permissions; + +namespace System.Windows.Forms { + + /// + /// + /// + /// Represents a group within a ListView. + /// + /// + /// + [ + TypeConverterAttribute(typeof(ListViewGroupConverter)), + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Header"), + Serializable + ] + public sealed class ListViewGroup : ISerializable { + + private ListView listView; + private int id; + + private string header; + private HorizontalAlignment headerAlignment = HorizontalAlignment.Left; + + private ListView.ListViewItemCollection items; + + private static int nextID; + + private static int nextHeader = 1; + + private object userData; + + private string name; + + /// + /// + /// Creates a ListViewGroup. + /// + public ListViewGroup() : this(SR.GetString(SR.ListViewGroupDefaultHeader, nextHeader++)) + { + } + + /// + /// + /// Creates a ListViewItem object from an Stream. + /// + private ListViewGroup(SerializationInfo info, StreamingContext context) : this() { + Deserialize(info, context); + } + + /// + /// + /// Creates a ListViewItem object from a Key and a Name + /// + public ListViewGroup(string key, string headerText) : this() { + this.name = key; + this.header = headerText; + } + + /// + /// + /// Creates a ListViewGroup. + /// + public ListViewGroup(string header) + { + this.header = header; + this.id = nextID++; + } + + /// + /// + /// Creates a ListViewGroup. + /// + public ListViewGroup(string header, HorizontalAlignment headerAlignment) : this(header) { + this.headerAlignment = headerAlignment; + } + + /// + /// + /// The text displayed in the group header. + /// + [SRCategory(SR.CatAppearance)] + public string Header { + get + { + return header == null ? "" : header; + } + set + { + if (header != value) { + header = value; + + if (listView != null) { + listView.RecreateHandleInternal(); + } + } + } + } + + /// + /// + /// The alignment of the group header. + /// + [ + DefaultValue(HorizontalAlignment.Left), + SRCategory(SR.CatAppearance) + ] + public HorizontalAlignment HeaderAlignment { + get + { + return headerAlignment; + } + set + { + // VSWhidbey 144699: Verify that the value is within the enum's range. + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + if (headerAlignment != value) { + headerAlignment = value; + UpdateListView(); + } + } + } + + internal int ID { + get + { + return id; + } + } + + /// + /// + /// The items that belong to this group. + /// + [Browsable(false)] + public ListView.ListViewItemCollection Items { + get + { + if (items == null) { + items = new ListView.ListViewItemCollection(new ListViewGroupItemCollection(this)); + } + return items; + } + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ListView ListView + { + get + { + return listView; + } + } + + internal ListView ListViewInternal + { + get + { + return listView; + } + set + { + if (listView != value) + { + listView = value; + } + } + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ListViewGroupNameDescr), + Browsable(true), + DefaultValue("") + ] + public string Name + { + get + { + return this.name; + } + set + { + this.name = value; + } + } + + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + + /// + /// + /// [To be supplied.] + /// + private void Deserialize(SerializationInfo info, StreamingContext context) { + + int count = 0; + + foreach (SerializationEntry entry in info) { + if (entry.Name == "Header") { + Header = (string)entry.Value; + } + else if (entry.Name == "HeaderAlignment") { + HeaderAlignment = (HorizontalAlignment)entry.Value; + } + else if (entry.Name == "Tag") { + Tag = entry.Value; + } + else if (entry.Name == "ItemsCount") { + count = (int)entry.Value; + } + else if (entry.Name == "Name") { + Name = (string) entry.Value; + } + } + if (count > 0) { + ListViewItem[] items = new ListViewItem[count]; + + for (int i = 0; i < count; i++) { + // SEC + items[i] = (ListViewItem)info.GetValue("Item" + i, typeof(ListViewItem)); + } + Items.AddRange(items); + } + } + + /// + public override string ToString() { + return Header; + } + + private void UpdateListView() { + if (listView != null && listView.IsHandleCreated) { + listView.UpdateGroupNative(this); + } + } + + /// + /// + /// To be supplied. + /// + [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("Header", this.Header); + info.AddValue("HeaderAlignment", this.HeaderAlignment); + info.AddValue("Tag", this.Tag); + if (!String.IsNullOrEmpty(this.Name)) { + info.AddValue("Name", this.Name); + } + if (items != null && items.Count > 0) { + info.AddValue("ItemsCount", this.Items.Count); + for (int i = 0; i < Items.Count; i ++) { + info.AddValue("Item" + i.ToString(CultureInfo.InvariantCulture), Items[i], typeof(ListViewItem)); + } + } + } + } + + /// + /// + /// + /// A collection of listview groups. + /// + /// + [ListBindable(false)] + public class ListViewGroupCollection : IList { + + private ListView listView; + + private ArrayList list; + + internal ListViewGroupCollection(ListView listView) { + this.listView = listView; + } + + /// + /// + /// To be supplied. + /// + public int Count + { + get + { + return this.List.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + bool IList.IsReadOnly { + get { + return false; + } + } + + private ArrayList List + { + get + { + if (list == null) { + list = new ArrayList(); + } + return list; + } + } + + /// + /// + /// To be supplied. + /// + public ListViewGroup this[int index] { + get + { + return (ListViewGroup)this.List[index]; + } + set + { + if (this.List.Contains(value)) { + return; + } + this.List[index] = value; + } + } + + /// + /// + /// To be supplied. + /// + public ListViewGroup this[string key] { + get { + + if (list == null) { + return null; + } + + for (int i = 0; i < list.Count; i ++) { + if (String.Compare(key, this[i].Name, false /*case insensitive*/, System.Globalization.CultureInfo.CurrentCulture) == 0) { + return this[i]; + } + } + + return null; + } + set { + int index = -1; + + if (this.list == null) { + return; // nothing to do + } + + for (int i = 0; i < this.list.Count; i ++) { + if (String.Compare(key, this[i].Name, false /*case insensitive*/, System.Globalization.CultureInfo.CurrentCulture) ==0) { + index = i; + break; + } + } + + if (index != -1) { + this.list[index] = value; + } + } + } + + /// + /// + object IList.this[int index] { + get + { + return this[index]; + } + set + { + if (value is ListViewGroup) { + this[index] = (ListViewGroup)value; + } + } + } + + /// + /// + /// To be supplied. + /// + public int Add(ListViewGroup group) + { + if (this.Contains(group)) { + return -1; + } + + // Will throw InvalidOperationException if group contains items which are parented by another listView. + CheckListViewItems(group); + group.ListViewInternal = this.listView; + int index = this.List.Add(group); + if (listView.IsHandleCreated) { + listView.InsertGroupInListView(this.List.Count, group); + MoveGroupItems(group); + } + return index; + } + + /// + /// + /// To be supplied. + /// + public ListViewGroup Add(string key, string headerText) + { + ListViewGroup group = new ListViewGroup(key, headerText); + this.Add(group); + return group; + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "value" is the name of the param passed in. + // So we don't have to localize it. + ] + int IList.Add(object value) { + if (value is ListViewGroup) { + return Add((ListViewGroup)value); + } + throw new ArgumentException("value"); + } + + /// + /// + /// To be supplied. + /// + public void AddRange(ListViewGroup[] groups) + { + for(int i=0; i < groups.Length; i++) { + Add(groups[i]); + } + } + + /// + /// + /// To be supplied. + /// + public void AddRange(ListViewGroupCollection groups) + { + for(int i=0; i < groups.Count; i++) { + Add(groups[i]); + } + } + + private void CheckListViewItems(ListViewGroup group) { + for (int i = 0; i < group.Items.Count; i ++) { + ListViewItem item = group.Items[i]; + if (item.ListView != null && item.ListView != this.listView) { + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, item.Text)); + } + } + } + + /// + /// + /// To be supplied. + /// + public void Clear() { + if (listView.IsHandleCreated) { + for(int i=0; i < Count; i++) { + listView.RemoveGroupFromListView(this[i]); + } + } + // Dissociate groups from the ListView + // + for(int i=0; i < Count; i++) { + this[i].ListViewInternal = null; + } + this.List.Clear(); + + // we have to tell the listView that there are no more groups + // so the list view knows to remove items from the default group + this.listView.UpdateGroupView(); + } + + /// + /// + /// To be supplied. + /// + public bool Contains(ListViewGroup value) { + return this.List.Contains(value); + } + + /// + /// + bool IList.Contains(object value) + { + if (value is ListViewGroup) { + return Contains((ListViewGroup)value); + } + return false; + } + + /// + /// + /// To be supplied. + /// + public void CopyTo(Array array, int index) { + this.List.CopyTo(array, index); + } + + /// + /// + /// To be supplied. + /// + public IEnumerator GetEnumerator() + { + return this.List.GetEnumerator(); + } + + /// + /// + /// To be supplied. + /// + public int IndexOf(ListViewGroup value) { + return this.List.IndexOf(value); + } + + /// + /// + int IList.IndexOf(object value) { + if (value is ListViewGroup) { + return IndexOf((ListViewGroup)value); + } + return -1; + } + + /// + /// + /// To be supplied. + /// + public void Insert(int index, ListViewGroup group) { + if (Contains(group)) { + return; + } + group.ListViewInternal = this.listView; + this.List.Insert(index, group); + if (listView.IsHandleCreated) { + listView.InsertGroupInListView(index, group); + MoveGroupItems(group); + } + } + + /// + /// + void IList.Insert(int index, object value) { + if (value is ListViewGroup) { + Insert(index, (ListViewGroup)value); + } + } + + private void MoveGroupItems(ListViewGroup group) { + Debug.Assert(listView.IsHandleCreated, "MoveGroupItems pre-condition: listView handle must be created"); + + foreach(ListViewItem item in group.Items) { + if (item.ListView == this.listView) { + item.UpdateStateToListView(item.Index); + } + } + } + + /// + /// + /// To be supplied. + /// + public void Remove(ListViewGroup group) { + group.ListViewInternal = null; + this.List.Remove(group); + + if (listView.IsHandleCreated) { + listView.RemoveGroupFromListView(group); + } + } + + /// + /// + void IList.Remove(object value) + { + if (value is ListViewGroup) { + Remove((ListViewGroup)value); + } + } + + /// + /// + /// To be supplied. + /// + public void RemoveAt(int index) { + Remove(this[index]); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListViewGroupConverter.cs b/WindowsForms/Managed/System/WinForms/ListViewGroupConverter.cs new file mode 100644 index 000000000..85063e8f5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewGroupConverter.cs @@ -0,0 +1,164 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// ListViewGroupConverter is a class that can be used to convert + /// ListViewGroup objects from one data type to another. Access this + /// class through the TypeDescriptor. + /// + internal class ListViewGroupConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string) && context != null && context.Instance is ListViewItem) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + if (destinationType == typeof(string) && context != null && context.Instance is ListViewItem) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + string text = ((string)value).Trim(); + + if (context != null && context.Instance != null) { + ListViewItem item = context.Instance as ListViewItem; + if (item != null && item.ListView != null) { + + foreach(ListViewGroup group in item.ListView.Groups) { + if (group.Header == text) { + return group; + } + } + } + } + } + + if (value == null || value.Equals(SR.GetString(SR.toStringNone))) { + return null; + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is ListViewGroup) { + ListViewGroup group = (ListViewGroup)value; + ConstructorInfo ctor; + + // Header + // + ctor = typeof(ListViewGroup).GetConstructor(new Type[] {typeof(string), typeof(HorizontalAlignment)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { group.Header, group.HeaderAlignment }, false); + } + } + + if (destinationType == typeof(string) && value == null) { + return SR.GetString(SR.toStringNone); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (context != null && context.Instance != null) { + ListViewItem item = context.Instance as ListViewItem; + if (item != null && item.ListView != null) { + ArrayList list = new ArrayList(); + foreach (ListViewGroup group in item.ListView.Groups) { + list.Add(group); + } + list.Add(null); + return new StandardValuesCollection(list); + } + } + return null; + } + + /// + /// + /// Determines if the list of standard values returned from + /// GetStandardValues is an exclusive list. If the list + /// is exclusive, then no other values are valid, such as + /// in an enum data type. If the list is not exclusive, + /// then there are other valid values besides the list of + /// standard values GetStandardValues provides. + /// + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListViewGroupItemCollection.cs b/WindowsForms/Managed/System/WinForms/ListViewGroupItemCollection.cs new file mode 100644 index 000000000..99cfbe466 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewGroupItemCollection.cs @@ -0,0 +1,174 @@ +using System.ComponentModel; +using System.Diagnostics; +using System; +using System.Security.Permissions; +using System.Drawing; +using System.Windows.Forms; +using System.ComponentModel.Design; +using System.Collections; +using Microsoft.Win32; +using System.Globalization; + +namespace System.Windows.Forms +{ + // A collection of items in a ListViewGroup. + // + internal class ListViewGroupItemCollection : ListView.ListViewItemCollection.IInnerList { + private ListViewGroup group; + private ArrayList items; + + public ListViewGroupItemCollection(ListViewGroup group) { + this.group = group; + } + + public int Count + { + get + { + return Items.Count; + } + } + + private ArrayList Items + { + get + { + if (items == null) { + items = new ArrayList(); + } + return items; + } + } + + public bool OwnerIsVirtualListView { + get { + if (this.group.ListView != null) { + return group.ListView.VirtualMode; + } else { + return false; + } + } + } + + public bool OwnerIsDesignMode { + get { + if (this.group.ListView != null) { + ISite s = group.ListView.Site; + return(s == null) ? false : s.DesignMode; + } else { + return false; + } + } + } + + public ListViewItem this[int index] { + get { + return (ListViewItem)Items[index]; + } + set { + if (value != Items[index]) { + MoveToGroup((ListViewItem)Items[index], null); + Items[index] = value; + MoveToGroup((ListViewItem)Items[index], this.group); + } + } + } + + public ListViewItem Add(ListViewItem value) { + CheckListViewItem(value); + + MoveToGroup(value, this.group); + Items.Add(value); + return value; + } + + public void AddRange(ListViewItem[] items) { + for (int i = 0; i < items.Length; i ++) { + CheckListViewItem(items[i]); + } + + Items.AddRange(items); + + for(int i=0; i < items.Length; i++) { + MoveToGroup(items[i], this.group); + } + } + + /// + /// throws an ArgumentException if the listViewItem is in another listView already. + /// + private void CheckListViewItem(ListViewItem item) { + if (item.ListView != null && item.ListView != this.group.ListView) { + // Microsoft: maybe we should throw an InvalidOperationException when we add an item from another listView + // into this group's collection. + // But in a similar situation, ListViewCollection throws an ArgumentException. This is the v1.* behavior + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, item.Text), "item"); + } + } + + public void Clear() { + for(int i=0; i < this.Count; i++) { + MoveToGroup(this[i], null); + } + Items.Clear(); + } + + public bool Contains(ListViewItem item) { + return Items.Contains(item); + } + + public void CopyTo(Array dest, int index) { + Items.CopyTo(dest, index); + } + + public IEnumerator GetEnumerator() { + return Items.GetEnumerator(); + } + + public int IndexOf(ListViewItem item) + { + return Items.IndexOf(item); + } + + public ListViewItem Insert(int index, ListViewItem item) { + CheckListViewItem(item); + + MoveToGroup(item, this.group); + Items.Insert(index, item); + return item; + } + + private void MoveToGroup(ListViewItem item, ListViewGroup newGroup) { + + ListViewGroup oldGroup = item.Group; + if (oldGroup != newGroup) { + item.group = newGroup; + if (oldGroup != null) { + oldGroup.Items.Remove(item); + } + UpdateNativeListViewItem(item); + } + } + + public void Remove(ListViewItem item) + { + Items.Remove(item); + + if (item.group == this.group) { + item.group = null; + UpdateNativeListViewItem(item); + } + } + + public void RemoveAt(int index) { + Remove(this[index]); + } + + private void UpdateNativeListViewItem(ListViewItem item) { + if (item.ListView != null && item.ListView.IsHandleCreated && !item.ListView.InsertingItemsNatively) { + item.UpdateStateToListView(item.Index); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ListViewHitTestInfo.cs b/WindowsForms/Managed/System/WinForms/ListViewHitTestInfo.cs new file mode 100644 index 000000000..e1852368f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewHitTestInfo.cs @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// + /// Specifies the return value for HITTEST on ListView. + /// + /// + public class ListViewHitTestInfo { + + private ListViewHitTestLocations loc; + private ListViewItem item; + private ListViewItem.ListViewSubItem subItem; + + /// + /// + /// Creates a ListViewHitTestInfo instance. + /// + public ListViewHitTestInfo(ListViewItem hitItem, ListViewItem.ListViewSubItem hitSubItem, ListViewHitTestLocations hitLocation) { + this.item = hitItem; + this.subItem = hitSubItem; + this.loc = hitLocation; + } + + + /// + /// + /// This gives the exact location returned by hit test on listview. + /// + public ListViewHitTestLocations Location { + get { + return loc; + } + } + + /// + /// + /// This gives the ListViewItem returned by hit test on listview. + /// + public ListViewItem Item { + get { + return item; + } + } + + /// + /// + /// This gives the ListViewSubItem returned by hit test on listview. + /// + public ListViewItem.ListViewSubItem SubItem { + get { + return subItem; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewHitTestLocation.cs b/WindowsForms/Managed/System/WinForms/ListViewHitTestLocation.cs new file mode 100644 index 000000000..01d4aa26d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewHitTestLocation.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + [ + Flags + ] + /// + public enum ListViewHitTestLocations { + /// + None = NativeMethods.LVHT_NOWHERE, + /// + AboveClientArea = 0x0100, + /// + BelowClientArea = NativeMethods.LVHT_BELOW, + /// + LeftOfClientArea = NativeMethods.LVHT_LEFT, + /// + RightOfClientArea = NativeMethods.LVHT_RIGHT, + /// + Image = NativeMethods.LVHT_ONITEMICON, + /// + StateImage = 0x0200, + /// + Label = NativeMethods.LVHT_ONITEMLABEL + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewInsertionMark.cs b/WindowsForms/Managed/System/WinForms/ListViewInsertionMark.cs new file mode 100644 index 000000000..c8a608eb6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewInsertionMark.cs @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Drawing; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace System.Windows.Forms { + + /// + /// + /// + /// Encapsulates insertion-mark information + /// + /// + public sealed class ListViewInsertionMark + { + private ListView listView; + + private int index = 0; + private Color color = Color.Empty; + private bool appearsAfterItem = false; + + internal ListViewInsertionMark(ListView listView) { + this.listView = listView; + } + + /// + /// + /// Specifies whether the insertion mark appears + /// after the item - otherwise it appears + /// before the item (the default). + /// + /// + public bool AppearsAfterItem { + get + { + return appearsAfterItem; + } + set + { + if (appearsAfterItem != value) { + appearsAfterItem = value; + + if (listView.IsHandleCreated) { + UpdateListView(); + } + } + } + } + + /// + /// + /// Returns bounds of the insertion-mark. + /// + /// + public Rectangle Bounds { + get + { + NativeMethods.RECT rect = new NativeMethods.RECT(); + listView.SendMessage(NativeMethods.LVM_GETINSERTMARKRECT, 0, ref rect); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + } + + /// + /// + /// The color of the insertion-mark. + /// + /// + public Color Color { + get + { + if (color.IsEmpty) { + color = SafeNativeMethods.ColorFromCOLORREF((int)listView.SendMessage(NativeMethods.LVM_GETINSERTMARKCOLOR, 0, 0)); + } + return color; + } + set + { + if (color != value) { + color = value; + if (listView.IsHandleCreated) { + listView.SendMessage(NativeMethods.LVM_SETINSERTMARKCOLOR, 0, SafeNativeMethods.ColorToCOLORREF(color)); + } + } + } + } + + /// + /// + /// Item next to which the insertion-mark appears. + /// + /// + public int Index { + get + { + return index; + } + set + { + if (index != value) { + index = value; + if (listView.IsHandleCreated) { + UpdateListView(); + } + } + } + } + + /// + /// + /// Performs a hit-test at the specified insertion point + /// and returns the closest item. + /// + /// + public int NearestIndex(Point pt) + { + NativeMethods.POINT point = new NativeMethods.POINT(); + point.x = pt.X; + point.y = pt.Y; + + NativeMethods.LVINSERTMARK lvInsertMark = new NativeMethods.LVINSERTMARK(); + UnsafeNativeMethods.SendMessage(new HandleRef(listView, listView.Handle), NativeMethods.LVM_INSERTMARKHITTEST, point, lvInsertMark); + + return lvInsertMark.iItem; + } + + internal void UpdateListView() { + Debug.Assert(listView.IsHandleCreated, "ApplySavedState Precondition: List-view handle must be created"); + NativeMethods.LVINSERTMARK lvInsertMark = new NativeMethods.LVINSERTMARK(); + lvInsertMark.dwFlags = appearsAfterItem ? NativeMethods.LVIM_AFTER : 0; + lvInsertMark.iItem = index; + UnsafeNativeMethods.SendMessage(new HandleRef(listView, listView.Handle), NativeMethods.LVM_SETINSERTMARK, 0, lvInsertMark); + + if (!color.IsEmpty) { + listView.SendMessage(NativeMethods.LVM_SETINSERTMARKCOLOR, 0, SafeNativeMethods.ColorToCOLORREF(color)); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItem.cs b/WindowsForms/Managed/System/WinForms/ListViewItem.cs new file mode 100644 index 000000000..7abbdd5a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItem.cs @@ -0,0 +1,2182 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System; + using System.Drawing; + using System.Drawing.Design; + using System.ComponentModel; + using System.IO; + using Microsoft.Win32; + using System.Collections; + using System.Collections.Specialized; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel.Design.Serialization; + using System.Reflection; + using System.Globalization; + + /// + /// + /// + /// Implements an item of a . + /// + /// + [ + TypeConverterAttribute(typeof(ListViewItemConverter)), + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Text"), + Serializable, + SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly") + ] + public class ListViewItem : ICloneable, ISerializable { + + private const int MAX_SUBITEMS = 4096; + + private static readonly BitVector32.Section StateSelectedSection = BitVector32.CreateSection(1); + private static readonly BitVector32.Section StateImageMaskSet = BitVector32.CreateSection(1, StateSelectedSection); + private static readonly BitVector32.Section StateWholeRowOneStyleSection = BitVector32.CreateSection(1, StateImageMaskSet); + private static readonly BitVector32.Section SavedStateImageIndexSection = BitVector32.CreateSection(15, StateWholeRowOneStyleSection); + private static readonly BitVector32.Section SubItemCountSection = BitVector32.CreateSection(MAX_SUBITEMS, SavedStateImageIndexSection); + + private int indentCount = 0; + private Point position = new Point(-1,-1); + + internal ListView listView; + + internal ListViewGroup group; + private string groupName; + + private ListViewSubItemCollection listViewSubItemCollection = null; + private ListViewSubItem[] subItems; + + // we stash the last index we got as a seed to GetDisplayIndex. + private int lastIndex = -1; + + // An ID unique relative to a given list view that comctl uses to identify items. + internal int ID = -1; + + private BitVector32 state = new BitVector32(); + private ListViewItemImageIndexer imageIndexer; + private String toolTipText = String.Empty; + object userData; + + // We need a special way to defer to the ListView's image + // list for indexing purposes. + internal class ListViewItemImageIndexer : ImageList.Indexer { + private ListViewItem owner; + + + /// + public ListViewItemImageIndexer(ListViewItem item) { + owner = item; + } + + + public override ImageList ImageList { + get { + if (owner != null) { + return owner.ImageList; + } + return null; + } + set { Debug.Assert(false, "We should never set the image list"); } + } + } + + + /// + /// + /// [To be supplied.] + /// + public ListViewItem() { + StateSelected = false; + UseItemStyleForSubItems = true; + SavedStateImageIndex = -1; + } + + /// + /// + /// Creates a ListViewItem object from an Stream. + /// The Serialization constructor is protected, as per FxCop Microsoft.Usage, CA2229 Rule. + /// + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Changing Deserialize to be non-virtual + // would be a breaking change. + ] + protected ListViewItem(SerializationInfo info, StreamingContext context) : this() { + Deserialize(info, context); + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text) : this(text, -1) { + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text, int imageIndex) : this() { + this.ImageIndexer.Index = imageIndex; + Text = text; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items) : this(items, -1) { + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, int imageIndex) : this() { + + this.ImageIndexer.Index = imageIndex; + if (items != null && items.Length > 0) { + this.subItems = new ListViewSubItem[items.Length]; + for (int i = 0; i < items.Length; i++) { + subItems[i] = new ListViewSubItem(this, items[i]); + } + this.SubItemCount = items.Length; + } + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, int imageIndex, Color foreColor, Color backColor, Font font) : this(items, imageIndex) { + this.ForeColor = foreColor; + this.BackColor = backColor; + this.Font = font; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(ListViewSubItem[] subItems, int imageIndex) : this() { + + this.ImageIndexer.Index = imageIndex; + this.subItems = subItems; + this.SubItemCount = this.subItems.Length; + + // Update the owner of these subitems + // + for(int i=0; i < subItems.Length; ++i) { + subItems[i].owner = this; + } + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(ListViewGroup group) : this() { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text, ListViewGroup group) : this(text) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text, int imageIndex, ListViewGroup group) : this(text, imageIndex) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, ListViewGroup group) : this(items) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, int imageIndex, ListViewGroup group) : this(items, imageIndex) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, int imageIndex, Color foreColor, Color backColor, Font font, ListViewGroup group) : + this(items, imageIndex, foreColor, backColor, font) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(ListViewSubItem[] subItems, int imageIndex, ListViewGroup group) : this(subItems, imageIndex) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text, string imageKey) : this() { + this.ImageIndexer.Key = imageKey; + Text = text; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, string imageKey) : this() { + + this.ImageIndexer.Key = imageKey; + if (items != null && items.Length > 0) { + this.subItems = new ListViewSubItem[items.Length]; + for (int i = 0; i < items.Length; i++) { + subItems[i] = new ListViewSubItem(this, items[i]); + } + this.SubItemCount = items.Length; + } + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, string imageKey, Color foreColor, Color backColor, Font font) : this(items, imageKey) { + this.ForeColor = foreColor; + this.BackColor = backColor; + this.Font = font; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(ListViewSubItem[] subItems, string imageKey) : this() { + + this.ImageIndexer.Key = imageKey; + this.subItems = subItems; + this.SubItemCount = this.subItems.Length; + + // Update the owner of these subitems + // + for(int i=0; i < subItems.Length; ++i) { + subItems[i].owner = this; + } + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string text, string imageKey, ListViewGroup group) : this(text, imageKey) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, string imageKey, ListViewGroup group) : this(items, imageKey) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(string[] items, string imageKey, Color foreColor, Color backColor, Font font, ListViewGroup group) : + this(items, imageKey, foreColor, backColor, font) { + this.Group = group; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem(ListViewSubItem[] subItems, string imageKey, ListViewGroup group) : this(subItems, imageKey) { + this.Group = group; + } + + /// + /// + /// The font that this item will be displayed in. If its value is null, it will be displayed + /// using the global font for the ListView control that hosts it. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatAppearance) + ] + public Color BackColor { + get { + if (SubItemCount == 0) { + if (listView != null) { + return listView.BackColor; + } + return SystemColors.Window; + } + else { + return subItems[0].BackColor; + } + } + set { + SubItems[0].BackColor = value; + } + } + + /// + /// + /// Returns the ListViewItem's bounding rectangle, including subitems. The bounding rectangle is empty if + /// the ListViewItem has not been added to a ListView control. + /// + [Browsable(false)] + public Rectangle Bounds { + get { + if (listView != null) { + return listView.GetItemRect(Index); + } + else + return new Rectangle(); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + DefaultValue(false), + RefreshPropertiesAttribute(RefreshProperties.Repaint), + SRCategory(SR.CatAppearance) + ] + public bool Checked { + get { + return StateImageIndex > 0; + } + + set { + if (Checked != value) { + if (listView != null && listView.IsHandleCreated) { + StateImageIndex = value ? 1 : 0; + + // the setter for StateImageIndex calls ItemChecked handler + // thus need to verify validity of the listView again + if ((this.listView != null) && !this.listView.UseCompatibleStateImageBehavior) { + if (!listView.CheckBoxes) { + listView.UpdateSavedCheckedItems(this, value); + } + } + + } + else { + SavedStateImageIndex = value ? 1 : 0; + } + } + } + } + + /// + /// + /// Returns the focus state of the ListViewItem. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public bool Focused { + get { + if (listView != null && listView.IsHandleCreated) { + return(listView.GetItemState(Index, NativeMethods.LVIS_FOCUSED) != 0); + } + else return false; + } + + set { + if (listView != null && listView.IsHandleCreated) { + listView.SetItemState(Index, value ? NativeMethods.LVIS_FOCUSED : 0, NativeMethods.LVIS_FOCUSED); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatAppearance) + ] + public Font Font { + get { + if (SubItemCount == 0) { + if (listView != null) { + return listView.Font; + } + return Control.DefaultFont; + } + else { + return subItems[0].Font; + } + } + set { + SubItems[0].Font = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatAppearance) + ] + public Color ForeColor { + get { + if (SubItemCount == 0) { + if (listView != null) { + return listView.ForeColor; + } + return SystemColors.WindowText; + } + else { + return subItems[0].ForeColor; + } + } + set { + SubItems[0].ForeColor = value; + } + } + + /// + /// + /// The group to which this item belongs + /// + [ + DefaultValue(null), + Localizable(true), + SRCategory(SR.CatBehavior) + ] + public ListViewGroup Group { + get + { + return group; + } + set + { + if (group != value) { + if (value != null) { + value.Items.Add(this); + } + else { + group.Items.Remove(this); + } + } + Debug.Assert(this.group == value, "BUG: group member variable wasn't updated!"); + + // If the user specifically sets the group then don't use the groupName again. + this.groupName = null; + } + } + + /// + /// + /// Returns the ListViewItem's currently set image index + /// + [ + DefaultValue(-1), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatBehavior), + SRDescription(SR.ListViewItemImageIndexDescr), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)) + ] + public int ImageIndex { + get { + if (ImageIndexer.Index != -1 && ImageList != null && ImageIndexer.Index >= ImageList.Images.Count) { + return ImageList.Images.Count - 1; + } + return this.ImageIndexer.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + ImageIndexer.Index = value; + + if (listView != null && listView.IsHandleCreated) { + listView.SetItemImage(Index, ImageIndexer.ActualIndex); + } + } + } + + internal ListViewItemImageIndexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ListViewItemImageIndexer(this); + } + return imageIndexer; + } + } + + /// + /// + /// Returns the ListViewItem's currently set image index + /// + [ + DefaultValue(""), + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + RefreshProperties(RefreshProperties.Repaint), + SRCategory(SR.CatBehavior), + Localizable(true) + ] + public string ImageKey { + get { + return this.ImageIndexer.Key; + } + set { + ImageIndexer.Key = value; + + if (listView != null && listView.IsHandleCreated) { + listView.SetItemImage(Index, ImageIndexer.ActualIndex); + } + } + } + + /// + [Browsable(false)] + public ImageList ImageList { + get { + if (listView != null) { + switch(listView.View) { + case View.LargeIcon: + case View.Tile: + return listView.LargeImageList; + case View.SmallIcon: + case View.Details: + case View.List: + return listView.SmallImageList; + } + } + return null; + } + } + + /// + [ + DefaultValue(0), + SRDescription(SR.ListViewItemIndentCountDescr), + SRCategory(SR.CatDisplay) + ] + public int IndentCount { + get { + return indentCount; + } + set { + if (value == indentCount) + return; + if (value < 0) { + throw new ArgumentOutOfRangeException("IndentCount", SR.GetString(SR.ListViewIndentCountCantBeNegative)); + } + indentCount = value; + if (listView != null && listView.IsHandleCreated) { + listView.SetItemIndentCount(Index, indentCount); + } + } + } + + + /// + /// + /// Returns ListViewItem's current index in the listview, or -1 if it has not been added to a ListView control. + /// + [Browsable(false)] + public int Index { + get { + if (listView != null) { + // if the list is virtual, the ComCtrl control does not keep any information + // about any list view items, so we use our cache instead. + if (!listView.VirtualMode) { + lastIndex = listView.GetDisplayIndex(this, lastIndex); + } + return lastIndex; + } + else { + return -1; + } + } + } + + /// + /// + /// Returns the ListView control that holds this ListViewItem. May be null if no + /// control has been assigned yet. + /// + [Browsable(false)] + public ListView ListView { + get { + return listView; + } + } + + + /// + /// + /// Name associated with this ListViewItem + /// + [ + Localizable(true), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public string Name { + get { + if (SubItemCount == 0) { + return string.Empty; + } + else { + return subItems[0].Name; + } + } + set { + SubItems[0].Name = value; + } + } + + /// + [ + SRCategory(SR.CatDisplay), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public Point Position { + get { + if (listView != null && listView.IsHandleCreated) { + position = listView.GetItemPosition(Index); + } + return position; + } + set { + if (value.Equals(position)) + return; + position = value; + if (listView != null && listView.IsHandleCreated) { + if (!listView.VirtualMode) { + listView.SetItemPosition(Index, position.X, position.Y); + } + } + } + } + + // the listView needs the raw encoding for the state image index when in VirtualMode + internal int RawStateImageIndex { + get { + return (this.SavedStateImageIndex + 1) << 12; + } + } + + /// + /// Accessor for our state bit vector. + /// + private int SavedStateImageIndex { + get { + // State goes from zero to 15, but we need a negative + // number, so we store + 1. + return state[SavedStateImageIndexSection] - 1; + } + set { + // flag whether we've set a value. + // + state[StateImageMaskSet] = (value == -1 ? 0 : 1); + + // push in the actual value + // + state[SavedStateImageIndexSection] = value + 1; + } + } + + /// + /// + /// Treats the ListViewItem as a row of strings, and returns an array of those strings + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool Selected { + get { + if (listView != null && listView.IsHandleCreated) { + return(listView.GetItemState(Index, NativeMethods.LVIS_SELECTED) != 0); + } + else + return StateSelected; + } + set { + if (listView != null && listView.IsHandleCreated) { + listView.SetItemState(Index, value ? NativeMethods.LVIS_SELECTED: 0, NativeMethods.LVIS_SELECTED); + + // vsw 451976: update comctl32's selection information. + listView.SetSelectionMark(Index); + } + else { + StateSelected = value; + if (this.listView != null && this.listView.IsHandleCreated) { + // APPCOMPAT: set the selected state on the list view item only if the list view's Handle is already created. + // vsw 448052. + listView.CacheSelectedStateForItem(this, value); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + DefaultValue(-1), + SRDescription(SR.ListViewItemStateImageIndexDescr), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RelatedImageList("ListView.StateImageList") + ] + public int StateImageIndex { + get { + if (listView != null && listView.IsHandleCreated) { + int state = listView.GetItemState(Index, NativeMethods.LVIS_STATEIMAGEMASK); + return ((state >> 12) - 1); // index is 1-based + } + else return SavedStateImageIndex; + } + set { + if (value < -1 || value > 14) + throw new ArgumentOutOfRangeException("StateImageIndex", SR.GetString(SR.InvalidArgument, "StateImageIndex", (value).ToString(CultureInfo.CurrentCulture))); + + if (listView != null && listView.IsHandleCreated) { + this.state[StateImageMaskSet] = (value == -1 ? 0 : 1); + int state = ((value + 1) << 12); // index is 1-based + listView.SetItemState(Index, state, NativeMethods.LVIS_STATEIMAGEMASK); + } + SavedStateImageIndex = value; + } + } + + + internal bool StateImageSet { + get { + return (this.state[StateImageMaskSet] != 0); + } + } + + + /// + /// Accessor for our state bit vector. + /// + internal bool StateSelected { + get { + return state[StateSelectedSection] == 1; + } + set { + state[StateSelectedSection] = value ? 1 : 0; + } + } + + /// + /// Accessor for our state bit vector. + /// + private int SubItemCount { + get { + return state[SubItemCountSection]; + } + set { + state[SubItemCountSection] = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ListViewItemSubItemsDescr), + Editor("System.Windows.Forms.Design.ListViewSubItemCollectionEditor, " + AssemblyRef.SystemDesign,typeof(UITypeEditor)), + ] + public ListViewSubItemCollection SubItems { + get { + if (SubItemCount == 0) { + subItems = new ListViewSubItem[1]; + subItems[0] = new ListViewSubItem(this, string.Empty); + SubItemCount = 1; + } + + if (listViewSubItemCollection == null) { + listViewSubItemCollection = new ListViewSubItemCollection(this); + } + return listViewSubItemCollection; + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Text associated with this ListViewItem + /// + [ + Localizable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatAppearance) + ] + public string Text { + get { + if (SubItemCount == 0) { + return string.Empty; + } + else { + return subItems[0].Text; + } + } + set { + SubItems[0].Text = value; + } + } + + /// + /// + /// Tool tip text associated with this ListViewItem + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue("") + ] + public string ToolTipText { + get { + return toolTipText; + } + set { + if (value == null) + value = String.Empty; + if (WindowsFormsUtils.SafeCompareStrings(toolTipText, value, false /*ignoreCase*/)) { + return; + } + + toolTipText = value; + + // tell the list view about this change + if (this.listView != null && this.listView.IsHandleCreated) { + this.listView.ListViewItemToolTipChanged(this); + } + } + } + + /// + /// + /// Whether or not the font and coloring for the ListViewItem will be used for all of its subitems. + /// If true, the ListViewItem style will be used when drawing the subitems. + /// If false, the ListViewItem and its subitems will be drawn in their own individual styles + /// if any have been set. + /// + [ + DefaultValue(true), + SRCategory(SR.CatAppearance) + ] + public bool UseItemStyleForSubItems { + get { + return state[StateWholeRowOneStyleSection] == 1; + } + set { + state[StateWholeRowOneStyleSection] = value ? 1 : 0; + } + } + + /// + /// + /// Initiate editing of the item's label. + /// Only effective if LabelEdit property is true. + /// + public void BeginEdit() { + if (Index >= 0) { + ListView lv = ListView; + if (lv.LabelEdit == false) + throw new InvalidOperationException(SR.GetString(SR.ListViewBeginEditFailed)); + if (!lv.Focused) + lv.FocusInternal(); + UnsafeNativeMethods.SendMessage(new HandleRef(lv, lv.Handle), NativeMethods.LVM_EDITLABEL, Index, 0); + } + } + + /// + /// + /// [To be supplied.] + /// + public virtual object Clone() { + ListViewSubItem[] clonedSubItems = new ListViewSubItem[this.SubItems.Count]; + for(int index=0; index < this.SubItems.Count; ++index) { + ListViewSubItem subItem = this.SubItems[index]; + clonedSubItems[index] = new ListViewSubItem(null, + subItem.Text, + subItem.ForeColor, + subItem.BackColor, + subItem.Font); + clonedSubItems[index].Tag = subItem.Tag; + } + + Type clonedType = this.GetType(); + ListViewItem newItem = null; + + if (clonedType == typeof(ListViewItem)) { + newItem = new ListViewItem(clonedSubItems, this.ImageIndexer.Index); + } + else { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + newItem = (ListViewItem)Activator.CreateInstance(clonedType); + } + newItem.subItems = clonedSubItems; + newItem.ImageIndexer.Index = this.ImageIndexer.Index; + newItem.SubItemCount = this.SubItemCount; + newItem.Checked = this.Checked; + newItem.UseItemStyleForSubItems = this.UseItemStyleForSubItems; + newItem.Tag = this.Tag; + + // Only copy over the ImageKey if we're using it. + if (!String.IsNullOrEmpty(this.ImageIndexer.Key)) { + newItem.ImageIndexer.Key = this.ImageIndexer.Key; + } + + newItem.indentCount = this.indentCount; + newItem.StateImageIndex = this.StateImageIndex; + newItem.toolTipText = this.toolTipText; + newItem.BackColor = this.BackColor; + newItem.ForeColor = this.ForeColor; + newItem.Font = this.Font; + newItem.Text = this.Text; + newItem.Group = this.Group; + + return newItem; + } + + /// + /// + /// Ensure that the item is visible, scrolling the view as necessary. + /// + public virtual void EnsureVisible() { + if (listView != null && listView.IsHandleCreated) { + listView.EnsureVisible(Index); + } + } + + /// + public ListViewItem FindNearestItem(SearchDirectionHint searchDirection) { + Rectangle r = this.Bounds; + + switch (searchDirection) { + case SearchDirectionHint.Up: + return this.ListView.FindNearestItem(searchDirection, r.Left, r.Top); + case SearchDirectionHint.Down: + return this.ListView.FindNearestItem(searchDirection, r.Left, r.Bottom); + case SearchDirectionHint.Left: + return this.ListView.FindNearestItem(searchDirection, r.Left, r.Top); + case SearchDirectionHint.Right: + return this.ListView.FindNearestItem(searchDirection, r.Right, r.Top); + default : + Debug.Fail("we handled all the 4 directions"); + return null; + } + } + + /// + /// + /// Returns a specific portion of the ListViewItem's bounding rectangle. + /// The rectangle returned is empty if the ListViewItem has not been added to a ListView control. + /// + public Rectangle GetBounds(ItemBoundsPortion portion) { + if (listView != null && listView.IsHandleCreated) { + return listView.GetItemRect(Index, portion); + } + else return new Rectangle(); + } + + /// + public ListViewSubItem GetSubItemAt(int x, int y) { + if (listView != null && listView.IsHandleCreated && listView.View == View.Details) { + int iItem = -1; + int iSubItem = -1; + + listView.GetSubItemAt(x,y, out iItem, out iSubItem); + + // bug in commctl list view: + // if the user clicks to the RIGHT of the last subitem the commctl returns the index of the subitem which would have fit there + // we have to check if iSubItem falls in the sub item collection + if (iItem == this.Index && iSubItem != -1 && iSubItem < SubItems.Count) { + return SubItems[iSubItem]; + } else { + return null; + } + } else { + return null; + } + } + + /// + /// + /// + /// + internal void Host(ListView parent, int ID, int index) { + // Don't let the name "host" fool you -- Handle is not necessarily created + Debug.Assert(this.listView == null || !this.listView.VirtualMode, "ListViewItem::Host can't be used w/ a virtual item"); + Debug.Assert(parent == null || !parent.VirtualMode, "ListViewItem::Host can't be used w/ a virtual list"); + + this.ID = ID; + listView = parent; + + // If the index is valid, then the handle has been created. + if (index != -1) { + UpdateStateToListView(index); + } + } + + /// + /// This is used to map list view items w/ their respective groups + /// in localized forms. + /// + internal void UpdateGroupFromName() { + Debug.Assert(this.listView != null, "This method is used only when items are parented in a list view"); + Debug.Assert(!this.listView.VirtualMode, "we need to update the group only when the user specifies the list view items in localizable forms"); + if (String.IsNullOrEmpty(this.groupName)) { + return; + } + + ListViewGroup group = this.listView.Groups[this.groupName]; + this.Group = group; + + // Use the group name only once. + this.groupName = null; + } + + internal void UpdateStateToListView(int index) { + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + UpdateStateToListView(index, ref lvItem, true); + } + + /// + /// Called when we have just pushed this item into a list view and we need + /// to configure the list view's state for the item. Use a valid index + /// if you can, or use -1 if you can't. + /// + internal void UpdateStateToListView(int index, ref NativeMethods.LVITEM lvItem, bool updateOwner) { + + Debug.Assert(listView.IsHandleCreated, "Should only invoke UpdateStateToListView when handle is created."); + + if (index == -1) { + index = Index; + } + else { + lastIndex = index; + } + + // Update Item state in one shot + // + int itemState = 0; + int stateMask = 0; + + if (StateSelected) { + itemState |= NativeMethods.LVIS_SELECTED; + stateMask |= NativeMethods.LVIS_SELECTED; + } + + if (SavedStateImageIndex > -1) { + itemState |= ((SavedStateImageIndex + 1) << 12); + stateMask |= NativeMethods.LVIS_STATEIMAGEMASK; + } + + lvItem.mask |= NativeMethods.LVIF_STATE; + lvItem.iItem = index; + lvItem.stateMask |= stateMask; + lvItem.state |= itemState; + + if (listView.GroupsEnabled) { + lvItem.mask |= NativeMethods.LVIF_GROUPID; + lvItem.iGroupId = listView.GetNativeGroupId(this); + + Debug.Assert(!updateOwner || listView.SendMessage(NativeMethods.LVM_ISGROUPVIEWENABLED, 0, 0) != IntPtr.Zero, "Groups not enabled"); + Debug.Assert(!updateOwner || listView.SendMessage(NativeMethods.LVM_HASGROUP, lvItem.iGroupId, 0) != IntPtr.Zero, "Doesn't contain group id: " + lvItem.iGroupId.ToString(CultureInfo.InvariantCulture)); + } + + if (updateOwner) { + UnsafeNativeMethods.SendMessage(new HandleRef(listView, listView.Handle), NativeMethods.LVM_SETITEM, 0, ref lvItem); + } + } + + internal void UpdateStateFromListView(int displayIndex, bool checkSelection) { + if (listView != null && listView.IsHandleCreated && displayIndex != -1) { + + // Get information from comctl control + // + NativeMethods.LVITEM lvItem = new NativeMethods.LVITEM(); + lvItem.mask = NativeMethods.LVIF_PARAM | NativeMethods.LVIF_STATE | NativeMethods.LVIF_GROUPID; + + if (checkSelection) { + lvItem.stateMask = NativeMethods.LVIS_SELECTED; + } + + + // we want to get all the information, including the state image mask + lvItem.stateMask |= NativeMethods.LVIS_STATEIMAGEMASK; + + if (lvItem.stateMask == 0) { + // perf optimization: no work to do. + // + return; + } + + + lvItem.iItem = displayIndex; + UnsafeNativeMethods.SendMessage(new HandleRef(listView, listView.Handle), NativeMethods.LVM_GETITEM, 0, ref lvItem); + + // Update this class' information + // + if (checkSelection) { + StateSelected = (lvItem.state & NativeMethods.LVIS_SELECTED) != 0; + } + SavedStateImageIndex = ((lvItem.state & NativeMethods.LVIS_STATEIMAGEMASK) >> 12) - 1; + + // a-jegros: bug 154131 - group needs to be updated, too! + group = null; + foreach (ListViewGroup lvg in ListView.Groups) { + if (lvg.ID == lvItem.iGroupId) { + group = lvg; + break; + } + } + } + } + + /// + /// + /// + /// + internal void UnHost(bool checkSelection) { + UnHost(Index, checkSelection); + } + + internal void UnHost(int displayIndex, bool checkSelection) { + UpdateStateFromListView(displayIndex, checkSelection); + + if (this.listView != null && (this.listView.Site == null || !this.listView.Site.DesignMode) && this.group != null) { + this.group.Items.Remove(this); + } + + // Make sure you do these last, as the first several lines depends on this information + ID = -1; + listView = null; + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Remove() { + if (listView != null) { + listView.Items.Remove(this); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void Deserialize(SerializationInfo info, StreamingContext context) { + + bool foundSubItems = false; + + string imageKey = null; + int imageIndex = -1; + + foreach (SerializationEntry entry in info) { + if (entry.Name == "Text") { + Text = info.GetString(entry.Name); + } + else if (entry.Name == "ImageIndex") { + imageIndex = info.GetInt32(entry.Name); + } + else if (entry.Name == "ImageKey") { + imageKey = info.GetString(entry.Name); + } + else if (entry.Name == "SubItemCount") { + SubItemCount = info.GetInt32(entry.Name); + // foundSubItems true only if count > 0 + if (SubItemCount > 0) + { + foundSubItems = true; + } + } + else if (entry.Name == "BackColor") { + BackColor = (Color)info.GetValue(entry.Name, typeof(Color)); + } + else if (entry.Name == "Checked") { + Checked = info.GetBoolean(entry.Name); + } + else if (entry.Name == "Font") { + Font = (Font)info.GetValue(entry.Name, typeof(Font)); + } + else if (entry.Name == "ForeColor") { + ForeColor = (Color)info.GetValue(entry.Name, typeof(Color)); + } + else if (entry.Name == "UseItemStyleForSubItems") { + UseItemStyleForSubItems = info.GetBoolean(entry.Name); + } + else if (entry.Name == "Group") { + ListViewGroup group = (ListViewGroup) info.GetValue(entry.Name, typeof(ListViewGroup)); + this.groupName = group.Name; + } + } + + // let image key take precidence + if (imageKey != null) { + ImageKey = imageKey; + } + else if (imageIndex != -1) { + ImageIndex = imageIndex; + } + + if (foundSubItems) { + ListViewSubItem[] newItems = new ListViewSubItem[SubItemCount]; + for (int i = 1; i < SubItemCount; i++) { + // SEC + ListViewSubItem newItem = (ListViewSubItem)info.GetValue("SubItem" + i.ToString(CultureInfo.InvariantCulture), typeof(ListViewSubItem)); + newItem.owner = this; + newItems[i] = newItem; + } + newItems[0] = subItems[0]; + subItems = newItems; + } + } + + /// + /// + /// Saves this ListViewItem object to the given data stream. + /// + /// SECREVIEW: Since ISerializable.GetObjectData and Deserialize require SerializationFormatter - locking down. + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.SerializationFormatter), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + protected virtual void Serialize(SerializationInfo info, StreamingContext context) { + info.AddValue("Text", Text); + info.AddValue("ImageIndex", ImageIndexer.Index); + if (!String.IsNullOrEmpty(ImageIndexer.Key)) { + info.AddValue("ImageKey", ImageIndexer.Key); + } + if (SubItemCount > 1) { + info.AddValue("SubItemCount", SubItemCount); + for (int i = 1; i < SubItemCount; i++) { + info.AddValue("SubItem" + i.ToString(CultureInfo.InvariantCulture), subItems[i], typeof(ListViewSubItem)); + } + } + info.AddValue("BackColor", BackColor); + info.AddValue("Checked", Checked); + info.AddValue("Font", Font); + info.AddValue("ForeColor", ForeColor); + info.AddValue("UseItemStyleForSubItems", UseItemStyleForSubItems); + if (this.Group != null) { + info.AddValue("Group", this.Group); + } + } + + // we need this function to set the index when the list view is in virtual mode. + // the index of the list view item is used in ListView::set_TopItem property + internal void SetItemIndex(ListView listView, int index) { + Debug.Assert(listView != null && listView.VirtualMode, "ListViewItem::SetItemIndex should be used only when the list is virtual"); + Debug.Assert(index > -1, "can't set the index on a virtual list view item to -1"); + this.listView = listView; + this.lastIndex = index; + } + + /// + internal bool ShouldSerializeText() { + return false; + } + + private bool ShouldSerializePosition() { + return !this.position.Equals(new Point(-1,-1)); + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + return "ListViewItem: {" + Text + "}"; + } + + // The ListItem's state (or a SubItem's state) has changed, so invalidate the ListView control + internal void InvalidateListView() { + if (listView != null && listView.IsHandleCreated) { + listView.Invalidate(); + } + } + + internal void UpdateSubItems(int index){ + UpdateSubItems(index, SubItemCount); + } + + internal void UpdateSubItems(int index, int oldCount){ + if (listView != null && listView.IsHandleCreated) { + int subItemCount = SubItemCount; + + int itemIndex = Index; + + if (index != -1) { + listView.SetItemText(itemIndex, index, subItems[index].Text); + } + else { + for(int i=0; i < subItemCount; i++) { + listView.SetItemText(itemIndex, i, subItems[i].Text); + } + } + + for (int i = subItemCount; i < oldCount; i++) { + listView.SetItemText(itemIndex, i, string.Empty); + } + } + } + + /// + /// + [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { + Serialize(info, context); + } + + /// + /// + /// [To be supplied.] + /// + [ + TypeConverterAttribute(typeof(ListViewSubItemConverter)), + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Text"), + Serializable + ] + public class ListViewSubItem { + + [NonSerialized] + internal ListViewItem owner; + + private string text; + + [OptionalField(VersionAdded=2)] + private string name = null; + + private SubItemStyle style; + + [OptionalField(VersionAdded=2)] + private object userData; + + /// + /// + /// [To be supplied.] + /// + public ListViewSubItem() { + } + + /// + /// + /// [To be supplied.] + /// + public ListViewSubItem(ListViewItem owner, string text) { + this.owner = owner; + this.text = text; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewSubItem(ListViewItem owner, string text, Color foreColor, Color backColor, Font font) { + this.owner = owner; + this.text = text; + this.style = new SubItemStyle(); + this.style.foreColor = foreColor; + this.style.backColor = backColor; + this.style.font = font; + } + + + /// + /// + /// [To be supplied.] + /// + public Color BackColor { + get { + if (style != null && style.backColor != Color.Empty) { + return style.backColor; + } + + if (owner != null && owner.listView != null) { + return owner.listView.BackColor; + } + + return SystemColors.Window; + } + set { + if (style == null) { + style = new SubItemStyle(); + } + + if (style.backColor != value) { + style.backColor = value; + if (owner != null) { + owner.InvalidateListView(); + } + } + } + } + /// + [Browsable(false)] + public Rectangle Bounds { + get { + if(owner != null && owner.listView != null && owner.listView.IsHandleCreated) { + return owner.listView.GetSubItemRect(owner.Index, owner.SubItems.IndexOf(this)); + } else { + return Rectangle.Empty; + } + } + } + + internal bool CustomBackColor { + get { + return style != null && !style.backColor.IsEmpty; + } + } + + internal bool CustomFont { + get { + return style != null && style.font != null; + } + } + + internal bool CustomForeColor { + get { + return style != null && !style.foreColor.IsEmpty; + } + } + + internal bool CustomStyle { + get { + return style != null; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true) + ] + public Font Font { + get { + if (style != null && style.font != null) { + return style.font; + } + + if (owner != null && owner.listView != null) { + return owner.listView.Font; + } + + return Control.DefaultFont; + } + set { + if (style == null) { + style = new SubItemStyle(); + } + + if (style.font != value) { + style.font = value; + if (owner != null) { + owner.InvalidateListView(); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public Color ForeColor { + get { + if (style != null && style.foreColor != Color.Empty) { + return style.foreColor; + } + + if (owner != null && owner.listView != null) { + return owner.listView.ForeColor; + } + + return SystemColors.WindowText; + } + set { + if (style == null) { + style = new SubItemStyle(); + } + + if (style.foreColor != value) { + style.foreColor = value; + if (owner != null) { + owner.InvalidateListView(); + } + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true) + ] + public string Text { + get { + return text == null ? "" : text; + } + set { + text = value; + if (owner != null) { + owner.UpdateSubItems(-1); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true) + ] + public string Name { + get { + return (name == null) ? "": name; + } + set { + name = value; + if (owner != null) { + owner.UpdateSubItems(-1); + } + } + } + + // + // Fix for Serialization Breaking change from v1.* to v2.0 + // + // see http://devdiv/SpecTool/SHADOW/Documents/Whidbey/Versioning/VersionTolerantSerializationGuidelines(1.1).doc + // + [OnDeserializing] + private void OnDeserializing(StreamingContext ctx) { + } + + [OnDeserialized] + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + private void OnDeserialized(StreamingContext ctx) { + this.name = null; + this.userData = null; + } + + [OnSerializing] + private void OnSerializing(StreamingContext ctx) { + } + + [OnSerialized] + private void OnSerialized(StreamingContext ctx) { + } + + // + // End fix for Serialization Breaking change from v1.* to v2.0 + // + + /// + /// + /// [To be supplied.] + /// + public void ResetStyle() { + if (style != null) { + style = null; + if (owner != null) { + owner.InvalidateListView(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + return "ListViewSubItem: {" + Text + "}"; + } + + [Serializable] + private class SubItemStyle { + public Color backColor = Color.Empty; + public Color foreColor = Color.Empty; + public Font font = null; + } + } + + /// + /// + /// [To be supplied.] + /// + public class ListViewSubItemCollection : IList { + private ListViewItem owner; + + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + + /// + /// + /// [To be supplied.] + /// + public ListViewSubItemCollection(ListViewItem owner) { + this.owner = owner; + } + + /// + /// + /// Returns the total number of items within the list view. + /// + [Browsable(false)] + public int Count { + get { + return owner.SubItemCount; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return true; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Returns a ListViewSubItem given it's zero based index into the ListViewSubItemCollection. + /// + public ListViewSubItem this[int index] { + get { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + return owner.subItems[index]; + } + set { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + + owner.subItems[index] = value; + owner.UpdateSubItems(index); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is ListViewSubItem) { + this[index] = (ListViewSubItem)value; + } + else { + throw new ArgumentException(SR.GetString(SR.ListViewBadListViewSubItem),"value"); + } + } + } + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ListViewSubItem this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public ListViewSubItem Add(ListViewSubItem item) { + EnsureSubItemSpace(1, -1); + item.owner = this.owner; + owner.subItems[owner.SubItemCount] = item; + owner.UpdateSubItems(owner.SubItemCount++); + return item; + } + + /// + public ListViewSubItem Add(string text) { + ListViewSubItem item = new ListViewSubItem(owner, text); + Add(item); + return item; + } + + /// + public ListViewSubItem Add(string text, Color foreColor, Color backColor, Font font) { + ListViewSubItem item = new ListViewSubItem(owner, text, foreColor, backColor, font); + Add(item); + return item; + } + + /// + public void AddRange(ListViewSubItem[] items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + EnsureSubItemSpace(items.Length, -1); + + foreach(ListViewSubItem item in items) { + if (item != null) { + owner.subItems[owner.SubItemCount++] = item; + } + } + + owner.UpdateSubItems(-1); + } + + /// + public void AddRange(string[] items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + EnsureSubItemSpace(items.Length, -1); + + foreach(string item in items) { + if (item != null) { + owner.subItems[owner.SubItemCount++] = new ListViewSubItem(owner, item); + } + } + + owner.UpdateSubItems(-1); + } + + /// + public void AddRange(string[] items, Color foreColor, Color backColor, Font font) { + if (items == null) { + throw new ArgumentNullException("items"); + } + EnsureSubItemSpace(items.Length, -1); + + foreach(string item in items) { + if (item != null) { + owner.subItems[owner.SubItemCount++] = new ListViewSubItem(owner, item, foreColor, backColor, font); + } + } + + owner.UpdateSubItems(-1); + } + + /// + /// + int IList.Add(object item) { + if (item is ListViewSubItem) { + return IndexOf(Add((ListViewSubItem)item)); + } + else { + throw new ArgumentException(SR.GetString(SR.ListViewSubItemCollectionInvalidArgument)); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Clear() { + int oldCount = owner.SubItemCount; + if (oldCount > 0) { + owner.SubItemCount = 0; + owner.UpdateSubItems(-1, oldCount); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ListViewSubItem subItem) { + return IndexOf(subItem) != -1; + } + + + /// + /// + bool IList.Contains(object subItem) { + if (subItem is ListViewSubItem) { + return Contains((ListViewSubItem)subItem); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// Ensures that the sub item array has the given + /// capacity. If it doesn't, it enlarges the + /// array until it does. If index is -1, additional + /// space is tacked onto the end. If it is a valid + /// insertion index into the array, this will move + /// the array data to accomodate the space. + /// + private void EnsureSubItemSpace(int size, int index) { + + // Range check subItems. + if (owner.SubItemCount == ListViewItem.MAX_SUBITEMS) { + throw new InvalidOperationException(SR.GetString(SR.ErrorCollectionFull)); + } + + if (owner.SubItemCount + size > owner.subItems.Length) { + + // must grow array. Don't do it just by size, though; + // chunk it for efficiency. + + if (owner.subItems == null) { + int newSize = (size > 4) ? size : 4; + owner.subItems = new ListViewSubItem[newSize]; + } + else { + int newSize = owner.subItems.Length * 2; + while(newSize - owner.SubItemCount < size) { + newSize *= 2; + } + + ListViewSubItem[] newItems = new ListViewSubItem[newSize]; + + // Now, when copying to the member variable, use index + // if it was provided. + // + if (index != -1) { + Array.Copy(owner.subItems, 0, newItems, 0, index); + Array.Copy(owner.subItems, index, newItems, index + size, owner.SubItemCount - index); + } + else { + Array.Copy(owner.subItems, newItems, owner.SubItemCount); + } + owner.subItems = newItems; + } + } + else { + + // We had plenty of room. Just move the items if we need to + // + if (index != -1) { + for(int i = owner.SubItemCount - 1; i >= index; i--) { + owner.subItems[i + size] = owner.subItems[i]; + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ListViewSubItem subItem) { + for(int index=0; index < Count; ++index) { + if (owner.subItems[index] == subItem) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object subItem) { + if (subItem is ListViewSubItem) { + return IndexOf((ListViewSubItem)subItem); + } + else { + return -1; + } + } + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, ListViewSubItem item) { + + if (index < 0 || index > Count) { + throw new ArgumentOutOfRangeException("index"); + } + + item.owner = owner; + + EnsureSubItemSpace(1, index); + + // Insert new item + // + owner.subItems[index] = item; + owner.SubItemCount++; + owner.UpdateSubItems(-1); + } + + /// + /// + void IList.Insert(int index, object item) { + if (item is ListViewSubItem) { + Insert(index, (ListViewSubItem)item); + } + else { + throw new ArgumentException(SR.GetString(SR.ListViewBadListViewSubItem),"item"); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(ListViewSubItem item) { + int index = IndexOf(item); + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + void IList.Remove(object item) { + if (item is ListViewSubItem) { + Remove((ListViewSubItem)item); + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + + if (index < 0 || index >= Count) { + throw new ArgumentOutOfRangeException("index"); + } + + // Collapse the items + for (int i = index + 1; i < owner.SubItemCount; i++) { + owner.subItems[i - 1] = owner.subItems[i]; + } + + int oldCount = owner.SubItemCount; + owner.SubItemCount--; + owner.subItems[owner.SubItemCount] = null; + owner.UpdateSubItems(-1, oldCount); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (Count > 0) { + System.Array.Copy(owner.subItems, 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + if (owner.subItems != null) { + return new WindowsFormsUtils.ArraySubsetEnumerator(owner.subItems, owner.SubItemCount); + } + else + { + return new ListViewSubItem[0].GetEnumerator(); + } + + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEvent.cs b/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEvent.cs new file mode 100644 index 000000000..8775480e4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEvent.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ListViewItemMouseHoverEventArgs : EventArgs { + readonly ListViewItem item; + + /// + /// + /// [To be supplied.] + /// + public ListViewItemMouseHoverEventArgs(ListViewItem item) { + this.item = item; + } + + /// + /// + /// [To be supplied.] + /// + public ListViewItem Item { + get { return item; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEventHandler.cs b/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEventHandler.cs new file mode 100644 index 000000000..63a7cf85d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItemMouseHoverEventHandler.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// + /// event of a + /// + /// . + /// + /// + /// + public delegate void ListViewItemMouseHoverEventHandler(object sender, ListViewItemMouseHoverEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEvent.cs b/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEvent.cs new file mode 100644 index 000000000..c7fa28117 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEvent.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms +{ + using System.ComponentModel; + + /// + /// + /// The event class that is created when the selection state of a ListViewItem is changed. + /// + public class ListViewItemSelectionChangedEventArgs : EventArgs + { + private ListViewItem item; + private int itemIndex; + private bool isSelected; + + /// + /// + /// Constructs a ListViewItemSelectionChangedEventArgs object. + /// + public ListViewItemSelectionChangedEventArgs(ListViewItem item, int itemIndex, bool isSelected) + { + this.item = item; + this.itemIndex = itemIndex; + this.isSelected = isSelected; + } + + /// + /// + /// Return true if the item is selected + /// + public bool IsSelected + { + get + { + return this.isSelected; + } + } + + /// + /// + /// The list view item whose selection changed + /// + public ListViewItem Item + { + get + { + return this.item; + } + } + + /// + /// + /// The list view item's index + /// + public int ItemIndex + { + get + { + return this.itemIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEventHandler.cs new file mode 100644 index 000000000..38ec46d60 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItemSelectionChangedEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// + /// + /// Represents the method that will + /// handle the event of a + /// . + /// + /// + public delegate void ListViewItemSelectionChangedEventHandler(object sender, ListViewItemSelectionChangedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewItemState.cs b/WindowsForms/Managed/System/WinForms/ListViewItemState.cs new file mode 100644 index 000000000..296490dcf --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewItemState.cs @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// + /// Gives state information about a ListView item/sub-item. Used with owner draw. + /// + /// + /// + + [Flags] + public enum ListViewItemStates { + /// + /// + /// + /// [To be supplied.] + /// + /// + Checked = NativeMethods.CDIS_CHECKED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Default = NativeMethods.CDIS_DEFAULT, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Focused = NativeMethods.CDIS_FOCUS, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Grayed = NativeMethods.CDIS_GRAYED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Hot = NativeMethods.CDIS_HOT, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Indeterminate = NativeMethods.CDIS_INDETERMINATE, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Marked = NativeMethods.CDIS_MARKED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Selected = NativeMethods.CDIS_SELECTED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + ShowKeyboardCues = NativeMethods.CDIS_SHOWKEYBOARDCUES + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEvent.cs b/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEvent.cs new file mode 100644 index 000000000..44feec6ec --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEvent.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//----------------------------------------------------------------------------------------- + +/* + */ +namespace System.Windows.Forms +{ + using System.ComponentModel; + + /// + /// + /// The event class that is created when the selection state of a ListViewItem is changed. + /// + public class ListViewVirtualItemsSelectionRangeChangedEventArgs : EventArgs + { + private int startIndex; + private int endIndex; + private bool isSelected; + + /// + /// + /// Constructs a ListViewVirtualItemsSelectionRangeChangedEventArgs object. + /// + public ListViewVirtualItemsSelectionRangeChangedEventArgs(int startIndex, int endIndex, bool isSelected) + { + if (startIndex > endIndex) + { + throw new ArgumentException(SR.GetString(SR.ListViewStartIndexCannotBeLargerThanEndIndex)); + } + this.startIndex = startIndex; + this.endIndex = endIndex; + this.isSelected = isSelected; + } + + /// + /// + /// Returns the end of the range where the selection changed + /// + public int EndIndex + { + get + { + return this.endIndex; + } + } + + /// + /// + /// Return true if the items are selected + /// + public bool IsSelected + { + get + { + return this.isSelected; + } + } + + /// + /// + /// Returns the begining of the range where the selection changed + /// + public int StartIndex + { + get + { + return this.startIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEventHandler.cs new file mode 100644 index 000000000..23dfc72f6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ListViewVirtualItemsSelectionRangeChangedEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// + /// + /// Represents the method that will + /// handle the event of a + /// . + /// + /// + public delegate void ListViewVirtualItemsSelectionRangeChangedEventHandler(object sender, ListViewVirtualItemsSelectionRangeChangedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/LocalAppContextSwitches.cs b/WindowsForms/Managed/System/WinForms/LocalAppContextSwitches.cs new file mode 100644 index 000000000..ec4c219c5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/LocalAppContextSwitches.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.CompilerServices; + + internal static class LocalAppContextSwitches { + internal const string DontSupportReentrantFilterMessageSwitchName = @"Switch.System.Windows.Forms.DontSupportReentrantFilterMessage"; + internal const string DoNotSupportSelectAllShortcutInMultilineTextBoxSwitchName = @"Switch.System.Windows.Forms.DoNotSupportSelectAllShortcutInMultilineTextBox"; + internal const string DoNotLoadLatestRichEditControlSwitchName = @"Switch.System.Windows.Forms.DoNotLoadLatestRichEditControl"; + internal const string UseLegacyContextMenuStripSourceControlValueSwitchName = @"Switch.System.Windows.Forms.UseLegacyContextMenuStripSourceControlValue"; + internal const string DomainUpDownUseLegacyScrollingSwitchName = @"Switch.System.Windows.Forms.DomainUpDown.UseLegacyScrolling"; + internal const string AllowUpdateChildControlIndexForTabControlsSwitchName = @"Switch.System.Windows.Forms.AllowUpdateChildControlIndexForTabControls"; + internal const string UseLegacyImagesSwitchName = @"Switch.System.Windows.Forms.UseLegacyImages"; + internal const string EnableVisualStyleValidationSwitchName = @"Switch.System.Windows.Forms.EnableVisualStyleValidation"; + internal const string EnableLegacyDangerousClipboardDeserializationModeSwitchName = @"Switch.System.Windows.Forms.EnableLegacyDangerousClipboardDeserializationMode"; + internal const string EnableLegacyChineseIMEIndicatorSwitchName = @"Switch.System.Windows.Forms.EnableLegacyChineseIMEIndicator"; + internal const string EnableLegacyIMEFocusInComboBoxSwitchName = @"Switch.System.Windows.Forms.EnableLegacyIMEFocusInComboBox"; + + private static int _dontSupportReentrantFilterMessage; + private static int _doNotSupportSelectAllShortcutInMultilineTextBox; + private static int _doNotLoadLatestRichEditControl; + private static int _useLegacyContextMenuStripSourceControlValue; + private static int _useLegacyDomainUpDownScrolling; + private static int _allowUpdateChildControlIndexForTabControls; + private static int _useLegacyImages; + private static int _enableVisualStyleValidation; + private static int _enableLegacyDangerousClipboardDeserializationMode; + private static int _enableLegacyChineseIMEIndicator; + private static int _enableLegacyIMEFocusInComboBox; + + public static bool DontSupportReentrantFilterMessage { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.DontSupportReentrantFilterMessageSwitchName, ref _dontSupportReentrantFilterMessage); + } + } + + public static bool DoNotSupportSelectAllShortcutInMultilineTextBox { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.DoNotSupportSelectAllShortcutInMultilineTextBoxSwitchName, ref _doNotSupportSelectAllShortcutInMultilineTextBox); + } + } + + public static bool DoNotLoadLatestRichEditControl { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.DoNotLoadLatestRichEditControlSwitchName, ref _doNotLoadLatestRichEditControl); + } + } + + public static bool UseLegacyContextMenuStripSourceControlValue { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.UseLegacyContextMenuStripSourceControlValueSwitchName, ref _useLegacyContextMenuStripSourceControlValue); + } + } + + public static bool UseLegacyDomainUpDownControlScrolling { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.DomainUpDownUseLegacyScrollingSwitchName, ref _useLegacyDomainUpDownScrolling); + } + } + + public static bool AllowUpdateChildControlIndexForTabControls { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.AllowUpdateChildControlIndexForTabControlsSwitchName, ref _allowUpdateChildControlIndexForTabControls); + } + } + + public static bool UseLegacyImages { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.UseLegacyImagesSwitchName, ref _useLegacyImages); + } + } + + public static bool EnableVisualStyleValidation { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.EnableVisualStyleValidationSwitchName, ref _enableVisualStyleValidation); + } + } + + /// + /// This AppContext switch controls reading from the clipboard. When reading a "System.String", "System.Drawing.Bitmap", + /// or an OLE format, we restrict deserialization to primitive types, thus blocking malicious code execution. + /// + public static bool EnableLegacyDangerousClipboardDeserializationMode { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + if (_enableLegacyDangerousClipboardDeserializationMode < 0) return false; + if (_enableLegacyDangerousClipboardDeserializationMode > 0) return true; + + // Device guard overrides the app context value when enabled. + if (UnsafeNativeMethods.IsDynamicCodePolicyEnabled()) { + _enableLegacyDangerousClipboardDeserializationMode = -1; + } else { + LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.EnableLegacyDangerousClipboardDeserializationModeSwitchName, ref _enableLegacyDangerousClipboardDeserializationMode); + } + + return (_enableLegacyDangerousClipboardDeserializationMode > 0); + } + } + + /// + /// When activated, this quirk enables controls with ImeMode.NoControl in Chinese IME to accept input according to the current OS setting. + /// This fix is on by default. Please set this value to true in app.config file in order to opt-out and get the 4.5.1 behavior. + /// + public static bool EnableLegacyChineseIMEIndicator { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.EnableLegacyChineseIMEIndicatorSwitchName, ref _enableLegacyChineseIMEIndicator); + } + } + + /// + /// When activated, this quirk enables ComboBoxes with ImeMode.NoControl in Chinese IME Pinyin to accept input according to the current OS setting, + /// when setting focus to this control with a mouse click. This fix is on by default. Please set this value to true in app.config file. + /// + public static bool EnableLegacyIMEFocusInComboBox { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { + return LocalAppContext.GetCachedSwitchValue(LocalAppContextSwitches.EnableLegacyIMEFocusInComboBoxSwitchName, ref _enableLegacyIMEFocusInComboBox); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/MDIClient.cs b/WindowsForms/Managed/System/WinForms/MDIClient.cs new file mode 100644 index 000000000..f06d96e8a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MDIClient.cs @@ -0,0 +1,454 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.Security.Permissions; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Globalization; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Threading; + using System.Windows.Forms; + + /// + /// + /// + /// + /// Summary to + /// Come + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + ToolboxItem(false), + DesignTimeVisible(false) + ] + public sealed class MdiClient : Control { + + // kept in add order, not ZOrder. Need to return the correct + // array of items... + // + private ArrayList children = new ArrayList(); + + + /// + /// + /// Creates a new MdiClient. + /// + /// + public MdiClient() : base() { + SetStyle(ControlStyles.Selectable, false); + BackColor = SystemColors.AppWorkspace; + Dock = DockStyle.Fill; + } + + /// + /// + /// Use parent's BackgroundImage if our BackgroundImage isn't set. + /// + [ + Localizable(true) + ] + public override Image BackgroundImage { + get { + Image result = base.BackgroundImage; + if (result == null && ParentInternal != null) + result = ParentInternal.BackgroundImage; + return result; + } + + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + Image backgroundImage = BackgroundImage; + if (backgroundImage != null && ParentInternal != null) + { + ImageLayout imageLayout = base.BackgroundImageLayout; + if (imageLayout != ParentInternal.BackgroundImageLayout) + { + // if the Layout is set on the parent use that. + return ParentInternal.BackgroundImageLayout; + } + } + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + cp.ClassName = "MDICLIENT"; + + // Note: Don't set the MDIS_ALLCHILDSTYLES CreatParams.Style bit, it prevents an MDI child form from getting activated + // when made visible (no WM_MDIACTIVATE sent to it), and forcing activation on it changes the activation event sequence + // (MdiChildActivate/Enter/Focus/Activate/etc.). + // Comment for removed code: + // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Add the style MDIS_ALLCHILDSTYLES + // so that MDI Client windows can have the WS_VISIBLE style removed from the window style + // to make them not visible but still present. + cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.WS_HSCROLL; + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + cp.Param = new NativeMethods.CLIENTCREATESTRUCT(IntPtr.Zero, 1); + ISite site = (ParentInternal == null) ? null : ParentInternal.Site; + if (site != null && site.DesignMode) { + cp.Style |= NativeMethods.WS_DISABLED; + SetState(STATE_ENABLED, false); + } + + if (this.RightToLeft == RightToLeft.Yes && this.ParentInternal != null && this.ParentInternal.IsMirrored) { + //We want to turn on mirroring for MdiClient explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + + return cp; + } + } + + /// + /// + /// The list of MDI children contained. This list + /// will be sorted by the order in which the children were + /// added to the form, not the current ZOrder. + /// + public Form[] MdiChildren { + get { + Form[] temp = new Form[children.Count]; + children.CopyTo(temp, 0); + return temp; + } + } + + /// + /// + /// [To be supplied.] + /// + protected override Control.ControlCollection CreateControlsInstance() { + return new ControlCollection(this); + } + + /// + /// + /// Arranges the MDI child forms according to value, which should be a + /// member of the MdiLayout enum. + /// + public void LayoutMdi(MdiLayout value) { + if (Handle == IntPtr.Zero) + return; + + switch (value) { + case MdiLayout.Cascade: + SendMessage(NativeMethods.WM_MDICASCADE, 0, 0); + break; + case MdiLayout.TileVertical: + SendMessage(NativeMethods.WM_MDITILE, NativeMethods.MDITILE_VERTICAL, 0); + break; + case MdiLayout.TileHorizontal: + SendMessage(NativeMethods.WM_MDITILE, NativeMethods.MDITILE_HORIZONTAL, 0); + break; + case MdiLayout.ArrangeIcons: + SendMessage(NativeMethods.WM_MDIICONARRANGE, 0, 0); + break; + } + } + + + /// + /// + /// + /// + protected override void OnResize(EventArgs e) { + ISite site = (ParentInternal == null) ? null : ParentInternal.Site; + if (site != null && site.DesignMode && Handle != IntPtr.Zero) { + SetWindowRgn(); + } + base.OnResize(e); + } + + + /// + /// + /// Performs the work of scaling the entire control and any child controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + + // Don't scale child forms... + // + + SuspendLayout(); + try { + Rectangle bounds = Bounds; + int sx = (int)Math.Round(bounds.X * dx); + int sy = (int)Math.Round(bounds.Y * dy); + int sw = (int)Math.Round((bounds.X + bounds.Width) * dx - sx); + int sh = (int)Math.Round((bounds.Y + bounds.Height) * dy - sy); + SetBounds(sx, sy, sw, sh, BoundsSpecified.All); + } + finally { + ResumeLayout(); + } + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + // never scale X and Y of an MDI client form + specified &= ~BoundsSpecified.Location; + base.ScaleControl(factor, specified); + } + + /// + /// + /// [To be supplied.] + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + ISite site = (ParentInternal == null) ? null : ParentInternal.Site; + if (IsHandleCreated && (site == null || !site.DesignMode)) { + Rectangle oldBounds = Bounds; + base.SetBoundsCore(x, y, width, height, specified); + Rectangle newBounds = Bounds; + + int yDelta = oldBounds.Height - newBounds.Height; + if (yDelta != 0) { + // NOTE: This logic is to keep minimized MDI children anchored to + // the bottom left of the client area, normally they are anchored + // to the top right which just looks wierd! + // + NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); + wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT)); + + for (int i=0; i < Controls.Count; i++) { + Control ctl = Controls[i]; + if (ctl != null && ctl is Form) { + Form child = (Form)ctl; + // VSWhidbey 93551: Only adjust the window position for visible MDI Child windows to prevent + // them from being re-displayed. + if (child.CanRecreateHandle() && child.WindowState == FormWindowState.Minimized) { + UnsafeNativeMethods.GetWindowPlacement(new HandleRef(child, child.Handle), ref wp); + wp.ptMinPosition_y -= yDelta; + if (wp.ptMinPosition_y == -1) { + if (yDelta < 0) { + wp.ptMinPosition_y = 0; + } + else { + wp.ptMinPosition_y = -2; + } + } + wp.flags = NativeMethods.WPF_SETMINPOSITION; + UnsafeNativeMethods.SetWindowPlacement(new HandleRef(child, child.Handle), ref wp); + wp.flags = 0; + } + } + } + } + } + else { + base.SetBoundsCore(x, y, width, height, specified); + } + } + + /// + /// Refer to 496832. This code is required to set the correct window region during the resize of the Form at design time. + /// There is case when the form contains a MainMenu and also has IsMdiContainer property set, in which, the MdiClient fails to + /// resize and hence draw the correct backcolor. + /// + /// + private void SetWindowRgn() { + IntPtr rgn1 = IntPtr.Zero; + IntPtr rgn2 = IntPtr.Zero; + NativeMethods.RECT rect = new NativeMethods.RECT(); + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle); + + Rectangle bounds = Bounds; + rgn1 = SafeNativeMethods.CreateRectRgn(0, 0, bounds.Width, bounds.Height); + try { + rgn2 = SafeNativeMethods.CreateRectRgn(-rect.left, -rect.top, + bounds.Width - rect.right, bounds.Height - rect.bottom); + try { + if (rgn1 == IntPtr.Zero || rgn2 == IntPtr.Zero) + throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion)); + + if (SafeNativeMethods.CombineRgn(new HandleRef(null, rgn1), new HandleRef(null, rgn1), new HandleRef(null, rgn2), NativeMethods.RGN_DIFF) == 0) + throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion)); + + if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(null, rgn1), true) == 0) { + throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion)); + } + else { + // The hwnd now owns the region. + rgn1 = IntPtr.Zero; + } + } + finally { + if (rgn2 != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, rgn2)); + } + } + } + finally { + if (rgn1 != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(new HandleRef(null, rgn1)); + } + } + } + + + /// + internal override bool ShouldSerializeBackColor() { + return BackColor != SystemColors.AppWorkspace; + } + + /// + private bool ShouldSerializeLocation() { + return false; + } + + /// + internal override bool ShouldSerializeSize() { + return false; + } + + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + + case NativeMethods.WM_CREATE: + if (ParentInternal != null && ParentInternal.Site != null && ParentInternal.Site.DesignMode && Handle != IntPtr.Zero) { + SetWindowRgn(); + } + break; + + case NativeMethods.WM_SETFOCUS: + InvokeGotFocus(ParentInternal, EventArgs.Empty); + Form childForm = null; + if (ParentInternal is Form) { + childForm = ((Form)ParentInternal).ActiveMdiChildInternal; + } + if (childForm == null && MdiChildren.Length > 0 && MdiChildren[0].IsMdiChildFocusable) { + childForm = MdiChildren[0]; + } + if (childForm != null && childForm.Visible) { + childForm.Active = true; + } + + // Do not use control's implementation of WmSetFocus + // as it will improperly activate this control. + WmImeSetFocus(); + DefWndProc(ref m); + InvokeGotFocus(this, EventArgs.Empty); + return; + case NativeMethods.WM_KILLFOCUS: + InvokeLostFocus(ParentInternal, EventArgs.Empty); + break; + } + base.WndProc(ref m); + } + + internal override void OnInvokedSetScrollPosition(object sender, EventArgs e) { + Application.Idle += new EventHandler(this.OnIdle); //do this on idle (it must be mega-delayed). + } + + private void OnIdle(object sender, EventArgs e) { + Application.Idle -= new EventHandler(this.OnIdle); + base.OnInvokedSetScrollPosition(sender, e); + } + + /// + /// + /// Collection of controls... + /// + [ComVisible(false)] + new public class ControlCollection : Control.ControlCollection { + private MdiClient owner; + + /*C#r: protected*/ + + /// + /// + /// [To be supplied.] + /// + public ControlCollection(MdiClient owner) + : base(owner) { + this.owner = owner; + } + + /// + /// + /// Adds a control to the MDI Container. This child must be + /// a Form that is marked as an MDI Child to be added to the + /// container. You should not call this directly, but rather + /// set the child form's (ctl) MDIParent property: + /// + /// // wrong + /// Form child = new ChildForm(); + /// this.getMdiClient().add(child); + /// // right + /// Form child = new ChildForm(); + /// child.setMdiParent(this); + /// + /// + public override void Add(Control value) { + if (value == null) { + return; + } + if (!(value is Form) || !((Form)value).IsMdiChild) { + throw new ArgumentException(SR.GetString(SR.MDIChildAddToNonMDIParent), "value"); + } + if (owner.CreateThreadId != value.CreateThreadId) { + throw new ArgumentException(SR.GetString(SR.AddDifferentThreads), "value"); + } + owner.children.Add((Form)value); + base.Add(value); + } + + /// + /// + /// Removes a child control. + /// + public override void Remove(Control value) { + owner.children.Remove(value); + base.Remove(value); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MDIControlStrip.cs b/WindowsForms/Managed/System/WinForms/MDIControlStrip.cs new file mode 100644 index 000000000..7d311ed92 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MDIControlStrip.cs @@ -0,0 +1,253 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Security; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + + /// this is the toolstrip used for merging the [:)] [_][#][X] buttons onto an + /// mdi parent when an MDI child is maximized. + /// + internal class MdiControlStrip : MenuStrip { + + private ToolStripMenuItem system; + private ToolStripMenuItem close; + private ToolStripMenuItem minimize; + private ToolStripMenuItem restore; + private MenuStrip mergedMenu; + + private IWin32Window target; + + /// target is ideally the MDI Child to send the system commands to. + /// although there's nothing MDI child specific to it... you could have this + /// a toplevel window. + /// + public MdiControlStrip(IWin32Window target) { + IntPtr hMenu= UnsafeNativeMethods.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(target)), /*bRevert=*/false); + this.target = target; + + // The menu item itself takes care of enabledness and sending WM_SYSCOMMAND messages to the target. + minimize = new ControlBoxMenuItem(hMenu, NativeMethods.SC_MINIMIZE, target); + close = new ControlBoxMenuItem(hMenu, NativeMethods.SC_CLOSE, target); + restore = new ControlBoxMenuItem(hMenu, NativeMethods.SC_RESTORE, target); + + // The dropDown of the system menu is the one that talks to native. + system = new SystemMenuItem(); + + // However in the event that the target handle changes we have to push the new handle into everyone. + Control controlTarget = target as Control; + if (controlTarget != null) { + controlTarget.HandleCreated += new EventHandler(OnTargetWindowHandleRecreated); + controlTarget.Disposed += new EventHandler(OnTargetWindowDisposed); + } + + // add in opposite order to how you want it merged + this.Items.AddRange(new ToolStripItem[] { minimize, restore,close, system }); + this.SuspendLayout(); + foreach (ToolStripItem item in this.Items) { + item.DisplayStyle = ToolStripItemDisplayStyle.Image; + item.MergeIndex = 0; + item.MergeAction = MergeAction.Insert; + item.Overflow = ToolStripItemOverflow.Never; + item.Alignment = ToolStripItemAlignment.Right; + item.Padding = Padding.Empty; + // image is not scaled well on high dpi devices. Setting property to fit to size. + item.ImageScaling = ToolStripItemImageScaling.SizeToFit; + } + + // set up the sytem menu + + + system.Image = GetTargetWindowIcon(); + system.Alignment = ToolStripItemAlignment.Left; + system.DropDownOpening += new EventHandler(OnSystemMenuDropDownOpening); + system.ImageScaling = ToolStripItemImageScaling.None; + system.DoubleClickEnabled = true; + system.DoubleClick += new EventHandler(OnSystemMenuDoubleClick); + system.Padding = Padding.Empty; + system.ShortcutKeys = Keys.Alt | Keys.OemMinus; + this.ResumeLayout(false); + } + + + #region Buttons + /* Unused + public ToolStripMenuItem System { + get { return system; } + } + */ + + public ToolStripMenuItem Close { + get { return close; } + } + + /* Unused + public ToolStripMenuItem Minimize { + get { return minimize; } + } + + public ToolStripMenuItem Restore { + get { return restore; } + } + */ + #endregion + + internal MenuStrip MergedMenu { + get { + return mergedMenu; + } + set { + mergedMenu = value; + } + } + +/* PERF: consider shutting off layout +#region ShutOffLayout + protected override void OnLayout(LayoutEventArgs e) { + return; // if someone attempts + } + + protected override Size GetPreferredSize(Size proposedSize) { + return Size.Empty; + } +#endregion +*/ + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Image GetTargetWindowIcon() { + Image systemIcon = null; + IntPtr hIcon = UnsafeNativeMethods.SendMessage(new HandleRef(this, Control.GetSafeHandle(target)), NativeMethods.WM_GETICON, NativeMethods.ICON_SMALL, 0); + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + Icon icon = (hIcon != IntPtr.Zero) ? Icon.FromHandle(hIcon) : Form.DefaultIcon; + Icon smallIcon = new Icon(icon, SystemInformation.SmallIconSize); + + systemIcon = smallIcon.ToBitmap(); + smallIcon.Dispose(); + } finally { + CodeAccessPermission.RevertAssert(); + } + + return systemIcon; + + } + + protected internal override void OnItemAdded(ToolStripItemEventArgs e) { + base.OnItemAdded(e); + Debug.Assert(Items.Count <= 4, "Too many items in the MDIControlStrip. How did we get into this situation?"); + } + + private void OnTargetWindowDisposed(object sender, EventArgs e) { + UnhookTarget(); + target = null; + } + + private void OnTargetWindowHandleRecreated(object sender, EventArgs e) { + + // in the case that the handle for the form is recreated we need to set + // up the handles to point to the new window handle for the form. + + system.SetNativeTargetWindow(target); + minimize.SetNativeTargetWindow(target); + close.SetNativeTargetWindow(target); + restore.SetNativeTargetWindow(target); + + IntPtr hMenu= UnsafeNativeMethods.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(target)), /*bRevert=*/false); + system.SetNativeTargetMenu(hMenu); + minimize.SetNativeTargetMenu(hMenu); + close.SetNativeTargetMenu(hMenu); + restore.SetNativeTargetMenu(hMenu); + + // clear off the System DropDown. + if (system.HasDropDownItems) { + // next time we need one we'll just fetch it fresh. + system.DropDown.Items.Clear(); + system.DropDown.Dispose(); + } + + system.Image = GetTargetWindowIcon(); + } + + private void OnSystemMenuDropDownOpening(object sender, EventArgs e) { + if (!system.HasDropDownItems && (target != null)) { + system.DropDown = ToolStripDropDownMenu.FromHMenu(UnsafeNativeMethods.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(target)), /*bRevert=*/false), target); + } + else if (MergedMenu == null) { + system.DropDown.Dispose(); + } + } + + private void OnSystemMenuDoubleClick(object sender, EventArgs e) { + Close.PerformClick(); + } + + protected override void Dispose(bool disposing) { + if (disposing) { + UnhookTarget(); + target = null; + } + base.Dispose(disposing); + } + + private void UnhookTarget() { + if (target != null) { + Control controlTarget = target as Control; + if (controlTarget != null) { + controlTarget.HandleCreated -= new EventHandler(OnTargetWindowHandleRecreated); + controlTarget.Disposed -= new EventHandler(OnTargetWindowDisposed); + } + target = null; + } + + } + + // when the system menu item shortcut is evaluated - pop the dropdown + internal class ControlBoxMenuItem : ToolStripMenuItem { + internal ControlBoxMenuItem(IntPtr hMenu, int nativeMenuCommandId, IWin32Window targetWindow) : + base(hMenu, nativeMenuCommandId, targetWindow) { + } + + internal override bool CanKeyboardSelect { + get { + return false; + } + } + } + + // when the system menu item shortcut is evaluated - pop the dropdown + internal class SystemMenuItem : ToolStripMenuItem { + public SystemMenuItem(){ + if (AccessibilityImprovements.Level1) { + AccessibleName = SR.GetString(SR.MDIChildSystemMenuItemAccessibleName); + } + } + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + if (Visible && ShortcutKeys == keyData) { + ShowDropDown(); + this.DropDown.SelectNextToolStripItem(null, true); + return true; + } + return base.ProcessCmdKey(ref m, keyData); + } + protected override void OnOwnerChanged(EventArgs e) { + if (HasDropDownItems && DropDown.Visible) { + HideDropDown(); + } + base.OnOwnerChanged(e); + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MDILayout.cs b/WindowsForms/Managed/System/WinForms/MDILayout.cs new file mode 100644 index 000000000..256cfb474 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MDILayout.cs @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the layout of multiple document interface (MDI) child windows in an MDI parent window. + /// + /// + public enum MdiLayout { + + /// + /// + /// + /// All MDI child windows are cascaded within the client + /// region of + /// the MDI parent form. + /// + /// + Cascade = 0, + /// + /// + /// + /// All MDI child windows are tiled horizontally within the client region of the MDI parent form. + /// + /// + TileHorizontal = 1, + /// + /// + /// + /// All MDI child windows are tiled vertically within the client region of the MDI parent form. + /// + /// + TileVertical = 2, + /// + /// + /// + /// All MDI child icons are arranged within the client region of the MDI parent form. + /// An application sets this layout to arrange all minimized MDI child windows (in the bottom of the client area). + /// It does not affect child windows that are not minimized. + /// + /// + ArrangeIcons = 3, + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MDIWindowDialog.cs b/WindowsForms/Managed/System/WinForms/MDIWindowDialog.cs new file mode 100644 index 000000000..13c464917 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MDIWindowDialog.cs @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Windows.Forms; + using Microsoft.Win32; + + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + internal sealed class MdiWindowDialog : Form { + private System.Windows.Forms.ListBox itemList; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.TableLayoutPanel okCancelTableLayoutPanel; + Form active; + + public MdiWindowDialog() + : base() { + + InitializeComponent(); + } + + public Form ActiveChildForm { + get { +#if DEBUG + ListItem item = (ListItem)itemList.SelectedItem; + Debug.Assert(item != null, "No item selected!"); +#endif + return active; + } + } + + + /// + /// + /// + private class ListItem { + public Form form; + + public ListItem(Form f) { + form = f; + } + + public override string ToString() { + return form.Text; + } + } + + public void SetItems(Form active, Form[] all) { + int selIndex = 0; + for (int i=0; i + /// + /// NOTE: The following code is required by the Windows Forms + /// designer. It can be modified using the form editor. Do not + /// modify it using the code editor. + /// + + private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MdiWindowDialog)); + this.itemList = new System.Windows.Forms.ListBox(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.okCancelTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.okCancelTableLayoutPanel.SuspendLayout(); + this.itemList.DoubleClick += new System.EventHandler(this.ItemList_doubleClick); + this.itemList.SelectedIndexChanged += new EventHandler(this.ItemList_selectedIndexChanged); + this.SuspendLayout(); +// +// itemList +// + resources.ApplyResources(this.itemList, "itemList"); + this.itemList.FormattingEnabled = true; + this.itemList.Name = "itemList"; +// +// okButton +// + resources.ApplyResources(this.okButton, "okButton"); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.okButton.Name = "okButton"; +// +// cancelButton +// + resources.ApplyResources(this.cancelButton, "cancelButton"); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.cancelButton.Name = "cancelButton"; +// +// okCancelTableLayoutPanel +// + resources.ApplyResources(this.okCancelTableLayoutPanel, "okCancelTableLayoutPanel"); + this.okCancelTableLayoutPanel.ColumnCount = 2; + this.okCancelTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.okCancelTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.okCancelTableLayoutPanel.Controls.Add(this.okButton, 0, 0); + this.okCancelTableLayoutPanel.Controls.Add(this.cancelButton, 1, 0); + this.okCancelTableLayoutPanel.Name = "okCancelTableLayoutPanel"; + this.okCancelTableLayoutPanel.RowCount = 1; + this.okCancelTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); +// +// MdiWindowDialog +// + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.okCancelTableLayoutPanel); + this.Controls.Add(this.itemList); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MdiWindowDialog"; + this.ShowIcon = false; + this.okCancelTableLayoutPanel.ResumeLayout(false); + this.okCancelTableLayoutPanel.PerformLayout(); + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + + this.ResumeLayout(false); + this.PerformLayout(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MainMenu.cs b/WindowsForms/Managed/System/WinForms/MainMenu.cs new file mode 100644 index 000000000..2e7bade1a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MainMenu.cs @@ -0,0 +1,220 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Security.Permissions; + using System.Runtime.Versioning; + + /// + /// + /// + /// Represents + /// a menu structure for a form. + /// + [ToolboxItemFilter("System.Windows.Forms.MainMenu")] + public class MainMenu : Menu + { + internal Form form; + internal Form ownerForm; // this is the form that created this menu, and is the only form allowed to dispose it. + private RightToLeft rightToLeft = System.Windows.Forms.RightToLeft.Inherit; + private EventHandler onCollapse; + + /// + /// + /// Creates a new MainMenu control. + /// + public MainMenu() + : base(null) { + + } + + /// + /// + /// Initializes a new instance of the class with the specified container. + /// + public MainMenu(IContainer container) : this() { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + /// + /// + /// Creates a new MainMenu control with the given items to start + /// with. + /// + public MainMenu(MenuItem[] items) + : base(items) { + + } + + /// + /// + /// [To be supplied.] + /// + [SRDescription(SR.MainMenuCollapseDescr)] + public event EventHandler Collapse { + add { + onCollapse += value; + } + remove { + onCollapse -= value; + } + } + + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// text alignment and reading order will be from right to left. + /// + // VSWhidbey 94189: Add an AmbientValue attribute so that the Reset context menu becomes available in the Property Grid. + [ + Localizable(true), + AmbientValue(RightToLeft.Inherit), + SRDescription(SR.MenuRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + if (System.Windows.Forms.RightToLeft.Inherit == rightToLeft) { + if (form != null) { + return form.RightToLeft; + } + else { + return RightToLeft.Inherit; + } + } + else { + return rightToLeft; + } + } + set { + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)){ + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + if (rightToLeft != value) { + rightToLeft = value; + UpdateRtl((value == System.Windows.Forms.RightToLeft.Yes)); + } + + } + } + + internal override bool RenderIsRightToLeft { + get { + return (RightToLeft == System.Windows.Forms.RightToLeft.Yes && (form == null || !form.IsMirrored)); + } + } + + /// + /// + /// Creates a new MainMenu object which is a dupliate of this one. + /// + public virtual MainMenu CloneMenu() { + MainMenu newMenu = new MainMenu(); + newMenu.CloneMenu(this); + return newMenu; + } + + /// + /// + /// + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + protected override IntPtr CreateMenuHandle() { + return UnsafeNativeMethods.CreateMenu(); + } + + /// + /// + /// Clears out this MainMenu object and discards all of it's resources. + /// If the menu is parented in a form, it is disconnected from that as + /// well. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (form != null && (ownerForm == null || form == ownerForm)) { + form.Menu = null; + } + } + base.Dispose(disposing); + } + + /// + /// + /// Indicates which form in which we are currently residing [if any] + /// + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + public Form GetForm() { + return form; + } + + internal Form GetFormUnsafe() { + return form; + } + + /// + /// + /// + /// + internal override void ItemsChanged(int change) { + base.ItemsChanged(change); + if (form != null) + form.MenuChanged(change, this); + } + + /// + /// + /// + /// + internal virtual void ItemsChanged(int change, Menu menu) { + if (form != null) + form.MenuChanged(change, menu); + } + + /// + /// + /// Fires the collapse event + /// + protected internal virtual void OnCollapse(EventArgs e) { + if (onCollapse != null) { + onCollapse(this, e); + } + } + + /// + /// + /// Returns true if the RightToLeft should be persisted in code gen. + /// + internal virtual bool ShouldSerializeRightToLeft() { + if (System.Windows.Forms.RightToLeft.Inherit == RightToLeft) { + return false; + } + return true; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + // VSWhidbey 495300: removing GetForm information + return base.ToString(); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MaskFormat.cs b/WindowsForms/Managed/System/WinForms/MaskFormat.cs new file mode 100644 index 000000000..ef050877a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MaskFormat.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// Enum defining inclusion of special characters. + /// + public enum MaskFormat + { + IncludePrompt = 0x0001, + IncludeLiterals = 0x0002, + + // both of the above + IncludePromptAndLiterals = 0x0003, + + // Never include special characters. + ExcludePromptAndLiterals = 0x000 + } +} diff --git a/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventArgs.cs b/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventArgs.cs new file mode 100644 index 000000000..8edba54e2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventArgs.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System.ComponentModel; + using System.Diagnostics; + + /// + /// MaskInputRejectedEventArgs. Provides data for the MaskInputRejected event. + /// + public class MaskInputRejectedEventArgs : EventArgs + { + private int position; + MaskedTextResultHint hint; + + public MaskInputRejectedEventArgs(int position, MaskedTextResultHint rejectionHint) + { + Debug.Assert(!MaskedTextProvider.GetOperationResultFromHint(rejectionHint), "Rejection hint is not on a failure."); + this.position = position; + this.hint = rejectionHint; + } + + /// + /// The position where the test failed the mask constraint. + /// + public int Position + { + get + { + return this.position; + } + } + + /// + /// Retreives a hint on why the input is rejected. + /// + public MaskedTextResultHint RejectionHint + { + get + { + return this.hint; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventHandler.cs b/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventHandler.cs new file mode 100644 index 000000000..619cd9809 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MaskInputRejectedEventHandler.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// Describes a delegate for an event that has a MaskInputRejectedEventArgs as + /// a parameter. + /// + public delegate void MaskInputRejectedEventHandler(object sender, MaskInputRejectedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/MaskedTextBox.cs b/WindowsForms/Managed/System/WinForms/MaskedTextBox.cs new file mode 100644 index 000000000..0c42f4851 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MaskedTextBox.cs @@ -0,0 +1,3322 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Text; + using System.ComponentModel; + using System.Runtime.InteropServices; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using System.Diagnostics; + using System.Collections; + using System.Collections.Specialized; + using System.Drawing; + using System.Drawing.Design; + using System.Windows.Forms.Layout; + using System.Windows.Forms.VisualStyles; + + /// + /// MaskedTextBox control definition class. + /// Uses the services from the System.ComponentModel.MaskedTextBoxProvider class. + /// See spec at http://dotnetclient/whidbey/Specs/MaskEdit.doc + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("MaskInputRejected"), + DefaultBindingProperty("Text"), + DefaultProperty("Mask"), + Designer("System.Windows.Forms.Design.MaskedTextBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionMaskedTextBox) + ] + public class MaskedTextBox : TextBoxBase + { + // Consider: The MaskedTextBox control, when initialized with a non-null/empty mask, processes all + // WM_CHAR messages and always sets the text using the SetWindowText Windows function in the furthest base + // class. This means that the underlying Edit control won't enable Undo operations and the context + // menu behavior will be a bit different (for instance Copy option is enabled when PasswordChar is set). + // To provide Undo functionality and make the context menu behave like the Edit control, we would have + // to implement our own. See http://msdn.microsoft.com/msdnmag/issues/1100/c/default.aspx for more info + // about how to do this. See postponed bug VSWhidbey#218402. + + private const bool forward = true; + private const bool backward = false; + private const string nullMask = "<>"; // any char/str is OK here. + + private static readonly object EVENT_MASKINPUTREJECTED = new object(); + private static readonly object EVENT_VALIDATIONCOMPLETED = new object(); + private static readonly object EVENT_TEXTALIGNCHANGED = new object(); + private static readonly object EVENT_ISOVERWRITEMODECHANGED = new object(); + private static readonly object EVENT_MASKCHANGED = new object(); + + // The native edit control's default password char (per thread). See corresponding property for more info. + private static char systemPwdChar; + + // Values to track changes in IME composition string (if any). Having const variables is a bit more efficient + // than having an enum (which creates a class). + private const byte imeConvertionNone = 0; // no convertion has been performed in the composition string. + private const byte imeConvertionUpdate = 1; // the char being composed has been updated but not coverted yet. + private const byte imeConvertionCompleted = 2; // the char being composed has been fully converted. + + ///////// Instance fields + + // Used for keeping selection when prompt is hidden on leave (text changes). + private int lastSelLength; + + // Used for caret positioning. + private int caretTestPos; + + // Bit mask - Determines when the Korean IME composition string is completed so converted character can be processed. + private static int IME_ENDING_COMPOSITION = BitVector32.CreateMask(); + + // Bit mask - Determines when the Korean IME is completing a composition, used when forcing convertion. + private static int IME_COMPLETING = BitVector32.CreateMask(IME_ENDING_COMPOSITION); + + // Used for handling characters that have a modifier (Ctrl-A, Shift-Del...). + private static int HANDLE_KEY_PRESS = BitVector32.CreateMask(IME_COMPLETING); + + // Bit mask - Used to simulate a null mask. Needed since a MaskedTextProvider object cannot be + // initialized with a null mask but we need one even in this case as a backend for + // default properties. This is to support creating a MaskedTextBox with the default + // constructor, specially at design time. + private static int IS_NULL_MASK = BitVector32.CreateMask(HANDLE_KEY_PRESS); + + // Bit mask - Used in conjuction with get_Text to return the text that is actually set in the native + // control. This is required to be able to measure text correctly (GetPreferredSize) and + // to compare against during set_Text (to bail if the same and not to raise TextChanged event). + private static int QUERY_BASE_TEXT = BitVector32.CreateMask(IS_NULL_MASK); + + // If true, the input text is rejected whenever a character does not comply with the mask; a MaskInputRejected + // event is fired for the failing character. + // If false, characters in the input string are processed one by one accepting the ones that comply + // with the mask and raising the MaskInputRejected event for the rejected ones. + private static int REJECT_INPUT_ON_FIRST_FAILURE = BitVector32.CreateMask( QUERY_BASE_TEXT ); + + // Bit masks for boolean properties. + private static int HIDE_PROMPT_ON_LEAVE = BitVector32.CreateMask(REJECT_INPUT_ON_FIRST_FAILURE); + private static int BEEP_ON_ERROR = BitVector32.CreateMask(HIDE_PROMPT_ON_LEAVE); + private static int USE_SYSTEM_PASSWORD_CHAR = BitVector32.CreateMask(BEEP_ON_ERROR); + private static int INSERT_TOGGLED = BitVector32.CreateMask(USE_SYSTEM_PASSWORD_CHAR); + private static int CUTCOPYINCLUDEPROMPT = BitVector32.CreateMask(INSERT_TOGGLED); + private static int CUTCOPYINCLUDELITERALS = BitVector32.CreateMask(CUTCOPYINCLUDEPROMPT); + + ///////// Properties backend fields. See corresponding property comments for more info. + + private char passwordChar; // control's pwd char, it could be different from the one displayed if using system password. + private Type validatingType; + private IFormatProvider formatProvider; + private MaskedTextProvider maskedTextProvider; + private InsertKeyMode insertMode; + private HorizontalAlignment textAlign; + + // Bit vector to represent bool variables. + private BitVector32 flagState; + + /// + /// Constructs the MaskedTextBox with the specified MaskedTextProvider object. + /// + public MaskedTextBox() + { + MaskedTextProvider maskedTextProvider = new MaskedTextProvider(nullMask, CultureInfo.CurrentCulture); + this.flagState[IS_NULL_MASK] = true; + Initialize(maskedTextProvider); + } + + /// + /// Constructs the MaskedTextBox with the specified MaskedTextProvider object. + /// + public MaskedTextBox(string mask) + { + if (mask == null) + { + throw new ArgumentNullException(); + } + + MaskedTextProvider maskedTextProvider = new MaskedTextProvider(mask, CultureInfo.CurrentCulture); + this.flagState[IS_NULL_MASK] = false; + Initialize(maskedTextProvider); + } + + /// + /// Constructs the MaskedTextBox with the specified MaskedTextProvider object. + /// + public MaskedTextBox(MaskedTextProvider maskedTextProvider) + { + if (maskedTextProvider == null) + { + throw new ArgumentNullException(); + } + + this.flagState[IS_NULL_MASK] = false; + Initialize(maskedTextProvider); + } + + /// + /// Initializes the object with the specified MaskedTextProvider object and default + /// property values. + /// + private void Initialize(MaskedTextProvider maskedTextProvider) + { + Debug.Assert(maskedTextProvider != null, "Initializing from a null MaskProvider ref."); + + this.maskedTextProvider = maskedTextProvider; + + // set the initial display text. + if (!this.flagState[IS_NULL_MASK]) + { + SetWindowText(); + } + + // set default values. + this.passwordChar = this.maskedTextProvider.PasswordChar; + this.insertMode = InsertKeyMode.Default; + + this.flagState[HIDE_PROMPT_ON_LEAVE ] = false; + this.flagState[BEEP_ON_ERROR ] = false; + this.flagState[USE_SYSTEM_PASSWORD_CHAR ] = false; + this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = false; + + // CutCopyMaskFormat - set same defaults as TextMaskFormat (IncludePromptAndLiterals). + // It is a lot easier to handle this flags individually since that's the way the MaskedTextProvider does it. + this.flagState[CUTCOPYINCLUDEPROMPT ] = this.maskedTextProvider.IncludePrompt; + this.flagState[CUTCOPYINCLUDELITERALS ] = this.maskedTextProvider.IncludeLiterals; + + // fields for internal use. + this.flagState[HANDLE_KEY_PRESS] = true; + this.caretTestPos = 0; + } + + + /////////////////// Properties + /// + + /// + /// Unsupported method/property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool AcceptsTab + { + get { return false; } + set {} + } + + /// + /// Specifies whether the prompt character should be treated as a valid input character or not. + /// The setter resets the underlying MaskedTextProvider object and attempts + /// to add the existing input text (if any) using the new mask, failure is ignored. + /// This property has no particular effect if no mask has been set. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxAllowPromptAsInputDescr), + DefaultValue(true) + ] + public bool AllowPromptAsInput + { + get + { + return this.maskedTextProvider.AllowPromptAsInput; + } + set + { + if( value != this.maskedTextProvider.AllowPromptAsInput ) + { + // Recreate masked text provider since this property is read-only. + MaskedTextProvider newProvider = new MaskedTextProvider( + this.maskedTextProvider.Mask, + this.maskedTextProvider.Culture, + value, + this.maskedTextProvider.PromptChar, + this.maskedTextProvider.PasswordChar, + this.maskedTextProvider.AsciiOnly ); + + SetMaskedTextProvider( newProvider ); + } + } + } + + /// + /// Unsupported method/property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler AcceptsTabChanged + { + add { } + remove { } + } + + /// + /// Specifies whether only ASCII characters are accepted as valid input. + /// This property has no particular effect if no mask has been set. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxAsciiOnlyDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(false) + ] + public bool AsciiOnly + { + get + { + return this.maskedTextProvider.AsciiOnly; + } + + set + { + if( value != this.maskedTextProvider.AsciiOnly ) + { + // Recreate masked text provider since this property is read-only. + MaskedTextProvider newProvider = new MaskedTextProvider( + this.maskedTextProvider.Mask, + this.maskedTextProvider.Culture, + this.maskedTextProvider.AllowPromptAsInput, + this.maskedTextProvider.PromptChar, + this.maskedTextProvider.PasswordChar, + value ); + + SetMaskedTextProvider( newProvider ); + } + } + } + + /// + /// Specifies whether to play a beep when the input is not valid according to the mask. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxBeepOnErrorDescr), + DefaultValue(false) + ] + public bool BeepOnError + { + get + { + return this.flagState[BEEP_ON_ERROR]; + } + set + { + this.flagState[BEEP_ON_ERROR] = value; + } + } + + /// + /// Gets a value indicating whether the user can undo the previous operation in a text box control. + /// Unsupported method/property. + /// WndProc ignores EM_CANUNDO. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool CanUndo + { + get + { + return false; + } + } + + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + protected override CreateParams CreateParams + { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get + { + CreateParams cp = base.CreateParams; + + // Translate for Rtl if necessary + // + HorizontalAlignment align = RtlTranslateHorizontal(textAlign); + cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the ES_XXXX alignment styles + switch (align) + { + case HorizontalAlignment.Left: + cp.Style |= NativeMethods.ES_LEFT; + break; + case HorizontalAlignment.Center: + cp.Style |= NativeMethods.ES_CENTER; + break; + case HorizontalAlignment.Right: + cp.Style |= NativeMethods.ES_RIGHT; + break; + } + + return cp; + } + } + + /// + /// The culture that determines the value of the localizable mask language separators and placeholders. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxCultureDescr), + RefreshProperties(RefreshProperties.Repaint), + ] + public CultureInfo Culture + { + get + { + return this.maskedTextProvider.Culture; + } + + set + { + if( value == null ) + { + throw new ArgumentNullException(); + } + + if( !this.maskedTextProvider.Culture.Equals(value) ) + { + // Recreate masked text provider since this property is read-only. + MaskedTextProvider newProvider = new MaskedTextProvider( + this.maskedTextProvider.Mask, + value, + this.maskedTextProvider.AllowPromptAsInput, + this.maskedTextProvider.PromptChar, + this.maskedTextProvider.PasswordChar, + this.maskedTextProvider.AsciiOnly ); + + SetMaskedTextProvider( newProvider ); + } + } + } + + /// + /// Specifies the formatting options for text cut/copited to the clipboard (Whether the mask returned from the Text + /// property includes Literals and/or prompt characters). + /// When prompt characters are excluded, theyare returned as spaces in the string returned. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxCutCopyMaskFormat), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(MaskFormat.IncludeLiterals) + ] + public MaskFormat CutCopyMaskFormat + { + get + { + if( this.flagState[CUTCOPYINCLUDEPROMPT] ) + { + if( this.flagState[CUTCOPYINCLUDELITERALS] ) + { + return MaskFormat.IncludePromptAndLiterals; + } + + return MaskFormat.IncludePrompt; + } + + if( this.flagState[CUTCOPYINCLUDELITERALS] ) + { + return MaskFormat.IncludeLiterals; + } + + return MaskFormat.ExcludePromptAndLiterals; + } + + set + { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); + } + + if( value == MaskFormat.IncludePrompt ) + { + this.flagState[CUTCOPYINCLUDEPROMPT] = true; + this.flagState[CUTCOPYINCLUDELITERALS] = false; + } + else if( value == MaskFormat.IncludeLiterals ) + { + this.flagState[CUTCOPYINCLUDEPROMPT] = false; + this.flagState[CUTCOPYINCLUDELITERALS] = true; + } + else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals + { + bool include = value == MaskFormat.IncludePromptAndLiterals; + this.flagState[CUTCOPYINCLUDEPROMPT] = include; + this.flagState[CUTCOPYINCLUDELITERALS] = include; + } + } + } + + /// + /// Specifies the IFormatProvider to be used when parsing the string to the ValidatingType. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public IFormatProvider FormatProvider + { + get + { + return this.formatProvider; + } + + set + { + this.formatProvider = value; + } + } + + /// + /// Specifies whether the PromptCharacter is displayed when the control loses focus. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxHidePromptOnLeaveDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(false) + ] + public bool HidePromptOnLeave + { + get + { + return this.flagState[HIDE_PROMPT_ON_LEAVE]; + } + set + { + if( this.flagState[HIDE_PROMPT_ON_LEAVE] != value ) + { + this.flagState[HIDE_PROMPT_ON_LEAVE] = value; + + // If the control is not focused and there are available edit positions (mask not full) we need to + // update the displayed text. + if( !this.flagState[IS_NULL_MASK]&& !this.Focused && !this.MaskFull && !this.DesignMode ) + { + SetWindowText(); + } + } + } + } + + /// + /// Specifies whether to include mask literal characters when formatting the text. + /// + private bool IncludeLiterals + { + get + { + return this.maskedTextProvider.IncludeLiterals; + } + set + { + this.maskedTextProvider.IncludeLiterals = value; + } + } + + /// + /// Specifies whether to include the mask prompt character when formatting the text in places + /// where an edit char has not being assigned. + /// + private bool IncludePrompt + { + get + { + return this.maskedTextProvider.IncludePrompt; + } + set + { + this.maskedTextProvider.IncludePrompt = value; + } + } + + /// + /// Specifies the text insertion mode of the text box. This can be used to simulated the Access masked text + /// control behavior where insertion is set to TextInsertionMode.AlwaysOverwrite + /// This property has no particular effect if no mask has been set. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxInsertKeyModeDescr), + DefaultValue(InsertKeyMode.Default) + ] + public InsertKeyMode InsertKeyMode + { + get + { + return this.insertMode; + } + set + { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)InsertKeyMode.Default, (int)InsertKeyMode.Overwrite)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(InsertKeyMode)); + } + + if (this.insertMode != value) + { + bool isOverwrite = this.IsOverwriteMode; + this.insertMode = value; + + if (isOverwrite != this.IsOverwriteMode) + { + OnIsOverwriteModeChanged(EventArgs.Empty); + } + } + } + } + + /// + /// Overridden to handle unsupported RETURN key. + /// + protected override bool IsInputKey(Keys keyData) + { + if ((keyData & Keys.KeyCode) == Keys.Return) + { + return false; + } + return base.IsInputKey(keyData); + } + + /// + /// Specifies whether text insertion mode in 'on' or not. + /// + [ + Browsable(false) + ] + public bool IsOverwriteMode + { + get + { + if( this.flagState[IS_NULL_MASK]) + { + return false; // EditBox always inserts. + } + + switch (this.insertMode) + { + case InsertKeyMode.Overwrite: + return true; + + case InsertKeyMode.Insert: + return false; + + case InsertKeyMode.Default: + + // Note that the insert key state should be per process and its initial state insert, this is the + // behavior of apps like WinWord, WordPad and VS; so we have to keep track of it and not query its + // system value. + //return Control.IsKeyLocked(Keys.Insert); + return this.flagState[INSERT_TOGGLED]; + + default: + Debug.Fail("Invalid InsertKeyMode. This code path should have never been executed."); + return false; + } + } + } + + + /// + /// Event to notify when the insert mode has changed. This is required for data binding. + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.MaskedTextBoxIsOverwriteModeChangedDescr) + ] + public event EventHandler IsOverwriteModeChanged + { + add + { + Events.AddHandler(EVENT_ISOVERWRITEMODECHANGED, value); + } + remove + { + Events.RemoveHandler(EVENT_ISOVERWRITEMODECHANGED, value); + } + } + + /// + /// Unsupported method/property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new string[] Lines + { + get + { + string[] lines; + + this.flagState[QUERY_BASE_TEXT] = true; + try + { + lines = base.Lines; + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + + return lines; + } + + set {} + } + + /// + /// The mask applied to this control. The setter resets the underlying MaskedTextProvider object and attempts + /// to add the existing input text (if any) using the new mask, failure is ignored. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxMaskDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(""), + MergableProperty(false), + Localizable(true), + Editor("System.Windows.Forms.Design.MaskPropertyEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public string Mask + { + get + { + return this.flagState[IS_NULL_MASK]? string.Empty : this.maskedTextProvider.Mask; + } + set + { + // + // We dont' do anything if: + // 1. IsNullOrEmpty( value )->[Reset control] && this.flagState[IS_NULL_MASK]==>Already Reset. + // 2. !IsNullOrEmpty( value )->[Set control] && !this.flagState[IS_NULL_MASK][control is set] && [value is the same]==>No need to update. + // + if( this.flagState[IS_NULL_MASK] == string.IsNullOrEmpty( value ) && (this.flagState[IS_NULL_MASK] || value == this.maskedTextProvider.Mask) ) + { + return; + } + + string text = null; + string newMask = value; + + // We need to update the this.flagState[IS_NULL_MASK]field before raising any events (when setting the maskedTextProvider) so + // querying for properties from an event handler returns the right value (i.e: Text). + + if( string.IsNullOrEmpty( value ) ) // Resetting the control, the native edit control will be in charge. + { + // Need to get the formatted & unformatted text before resetting the mask, they'll be used to determine whether we need to + // raise the TextChanged event. + string formattedText = TextOutput; + string unformattedText = this.maskedTextProvider.ToString(false, false); + + this.flagState[IS_NULL_MASK] = true; + + if( this.maskedTextProvider.IsPassword ) + { + SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); + } + + // Set the window text to the unformatted text before raising events. Also, TextChanged needs to be raised after MaskChanged so + // pass false to SetWindowText 'raiseTextChanged' param. + SetWindowText(unformattedText, false, false ); + + EventArgs e = EventArgs.Empty; + + OnMaskChanged(e); + + if( unformattedText != formattedText ) + { + OnTextChanged(e); + } + + newMask = nullMask; + } + else // Setting control to a new value. + { + foreach( char c in value ) + { + if( !MaskedTextProvider.IsValidMaskChar( c ) ) + { + // Same message as in SR.MaskedTextProviderMaskInvalidChar in System.txt + throw new ArgumentException( SR.GetString( SR.MaskedTextBoxMaskInvalidChar) ); + } + } + + if( this.flagState[IS_NULL_MASK] ) + { + // If this.IsNullMask, we are setting the mask to a new value; in this case we need to get the text because + // the underlying MTP does not have it (used as a property backend only) and pass it to SetMaskedTextProvider + // method below to update the provider. + + text = this.Text; + } + } + + // Recreate masked text provider since this property is read-only. + MaskedTextProvider newProvider = new MaskedTextProvider( + newMask, + this.maskedTextProvider.Culture, + this.maskedTextProvider.AllowPromptAsInput, + this.maskedTextProvider.PromptChar, + this.maskedTextProvider.PasswordChar, + this.maskedTextProvider.AsciiOnly ); + + //text == null when setting to a different mask value or when resetting the mask to null. + //text != null only when setting the mask from null to some value. + SetMaskedTextProvider( newProvider, text ); + } + } + + /// + /// Event to notify when the mask has changed. + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.MaskedTextBoxMaskChangedDescr) + ] + public event EventHandler MaskChanged + { + add + { + Events.AddHandler(EVENT_MASKCHANGED, value); + } + remove + { + Events.RemoveHandler(EVENT_MASKCHANGED, value); + } + } + + /// + /// Specifies whether the test string required input positions, as specified by the mask, have + /// all been assigned. + /// + [ + Browsable(false) + ] + public bool MaskCompleted + { + get + { + return this.maskedTextProvider.MaskCompleted; + } + } + + /// + /// Specifies whether all inputs (required and optional) have been provided into the mask successfully. + /// + [ + Browsable(false) + ] + public bool MaskFull + { + get + { + return this.maskedTextProvider.MaskFull; + } + } + + /// + /// Returns a copy of the control's internal MaskedTextProvider. This is useful for user's to provide + /// cloning semantics for the control (we don't want to do it) w/o incurring in any perf penalty since + /// some of the properties require recreating the underlying provider when they are changed. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public MaskedTextProvider MaskedTextProvider + { + get + { + return this.flagState[IS_NULL_MASK] ? null : (MaskedTextProvider) this.maskedTextProvider.Clone(); + } + } + + /// + /// Event to notify when an input has been rejected according to the mask. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxMaskInputRejectedDescr) + ] + public event MaskInputRejectedEventHandler MaskInputRejected + { + add + { + Events.AddHandler(EVENT_MASKINPUTREJECTED, value); + } + remove + { + Events.RemoveHandler(EVENT_MASKINPUTREJECTED, value); + } + } + + /// + /// Unsupported method/property. + /// WndProc ignores EM_LIMITTEXT & this is a virtual method. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override int MaxLength + { + get{ return base.MaxLength; } + set{} + } + + /// + /// Unsupported method/property. + /// virtual method. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool Multiline + { + get { return false; } + set {} + } + + /// + /// Unsupported method/property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler MultilineChanged + { + add { } + remove { } + } + + /// + /// Specifies the character to be used in the formatted string in place of editable characters, if + /// set to any printable character, the text box becomes a password text box, to reset it use the null + /// character. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxPasswordCharDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue('\0') // This property is shadowed by MaskedTextBoxDesigner. + ] + public char PasswordChar + { + get + { + // The password char could be the one set in the control or the system password char, + // in any case the maskedTextProvider has the correct one. + return this.maskedTextProvider.PasswordChar; + } + set + { + if( !MaskedTextProvider.IsValidPasswordChar(value) ) // null character accepted (resets value) + { + // Same message as in SR.MaskedTextProviderInvalidCharError. + throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); + } + + if( this.passwordChar != value ) + { + if( value == this.maskedTextProvider.PromptChar ) + { + // Prompt and password chars must be different. + throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); + } + + this.passwordChar = value; + + // UseSystemPasswordChar take precedence over PasswordChar...Let's check. + if (!this.UseSystemPasswordChar) + { + this.maskedTextProvider.PasswordChar = value; + + if( this.flagState[IS_NULL_MASK]) + { + SetEditControlPasswordChar(value); + } + else + { + SetWindowText(); + } + + VerifyImeRestrictedModeChanged(); + } + } + } + } + + /// + /// Determines if the control is in password protect mode. + /// + internal override bool PasswordProtect + { + get + { + if( this.maskedTextProvider != null ) // could be queried during object construction. + { + return this.maskedTextProvider.IsPassword; + } + return base.PasswordProtect; + } + } + + /// + /// Specifies the prompt character to be used in the formatted string for unsupplied characters. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.MaskedTextBoxPromptCharDescr), + RefreshProperties(RefreshProperties.Repaint), + Localizable(true), + DefaultValue('_') + ] + public char PromptChar + { + get + { + return this.maskedTextProvider.PromptChar; + } + set + { + if( !MaskedTextProvider.IsValidInputChar(value) ) + { + // This message is the same as the one in SR.MaskedTextProviderInvalidCharError. + throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); + } + + if( this.maskedTextProvider.PromptChar != value ) + { + // We need to check maskedTextProvider password char in case it is using the system password. + if( value == this.passwordChar || value == this.maskedTextProvider.PasswordChar ) + { + // Prompt and password chars must be different. + throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); + } + + // Recreate masked text provider to be consistent with AllowPromptAsInput - current text may have chars with same value as new prompt. + MaskedTextProvider newProvider = new MaskedTextProvider( + this.maskedTextProvider.Mask, + this.maskedTextProvider.Culture, + this.maskedTextProvider.AllowPromptAsInput, + value, + this.maskedTextProvider.PasswordChar, + this.maskedTextProvider.AsciiOnly ); + + SetMaskedTextProvider( newProvider ); + } + } + } + + /// + /// Overwrite base class' property. + /// + public new bool ReadOnly + { + get + { + return base.ReadOnly; + } + + set + { + if (this.ReadOnly != value) + { + // if true, this disables IME in the base class. + base.ReadOnly = value; + + if (!this.flagState[IS_NULL_MASK]) + { + // Prompt will be hidden. + SetWindowText(); + } + } + } + } + + /// + /// Specifies whether to include the mask prompt character when formatting the text in places + /// where an edit char has not being assigned. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxRejectInputOnFirstFailureDescr), + DefaultValue(false) + ] + public bool RejectInputOnFirstFailure + { + get + { + return this.flagState[REJECT_INPUT_ON_FIRST_FAILURE]; + } + set + { + this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = value; + } + } + + /// + /// Designe time support for resetting the Culture property. + /// + /* No longer needed since Culture has been removed from the property browser - Left here for documentation. + [EditorBrowsable(EditorBrowsableState.Never)] + private void ResetCulture() + { + this.Culture = CultureInfo.CurrentCulture; + }*/ + + + /// + /// Specifies whether to reset and skip the current position if editable, when the input character + /// has the same value as the prompt. This property takes precedence over AllowPromptAsInput. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxResetOnPrompt), + DefaultValue(true) + ] + public bool ResetOnPrompt + { + get + { + return this.maskedTextProvider.ResetOnPrompt; + } + set + { + this.maskedTextProvider.ResetOnPrompt = value; + } + } + + /// + /// Specifies whether to reset and skip the current position if editable, when the input + /// is the space character. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxResetOnSpace), + DefaultValue(true) + ] + public bool ResetOnSpace + { + get + { + return this.maskedTextProvider.ResetOnSpace; + } + set + { + this.maskedTextProvider.ResetOnSpace = value; + } + } + + /// + /// Specifies whether to skip the current position if non-editable and the input character has + /// the same value as the literal at that position. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxSkipLiterals), + DefaultValue(true) + ] + public bool SkipLiterals + { + get + { + return this.maskedTextProvider.SkipLiterals; + } + set + { + this.maskedTextProvider.SkipLiterals = value; + } + } + + /// + /// The currently selected text (if any) in the control. + /// + public override string SelectedText + { + get + { + if( this.flagState[IS_NULL_MASK]) + { + return base.SelectedText; + } + + return GetSelectedText(); + } + set + { + SetSelectedTextInternal(value, true); + } + } + + internal override void SetSelectedTextInternal(string value, bool clearUndo) + { + if (this.flagState[IS_NULL_MASK]) + { + base.SetSelectedTextInternal(value, true); // Operates as a regular text box base. + return; + } + + PasteInt( value ); + } + + /// + /// Set the composition string as the result string. + /// + private void ImeComplete() + { + this.flagState[IME_COMPLETING] = true; + ImeNotify(NativeMethods.CPS_COMPLETE); + } + + /// + /// Notifies the IMM about changes to the status of the IME input context. + /// + private void ImeNotify(int action) + { + HandleRef handle = new HandleRef(this, this.Handle); + IntPtr inputContext = UnsafeNativeMethods.ImmGetContext(handle); + + if (inputContext != IntPtr.Zero) + { + try + { + UnsafeNativeMethods.ImmNotifyIME(new HandleRef(null, inputContext), NativeMethods.NI_COMPOSITIONSTR, action, 0); + } + finally + { + UnsafeNativeMethods.ImmReleaseContext(handle, new HandleRef(null, inputContext)); + } + } + else + { + Debug.Fail("Could not get IME input context."); + } + } + + /// + /// Sets the underlying edit control's password char to the one obtained from this.PasswordChar. + /// This is used when the control is passworded and this.flagState[IS_NULL_MASK]. + /// + private void SetEditControlPasswordChar( char pwdChar ) + { + if (this.IsHandleCreated) + { + // This message does not return a value. + SendMessage(NativeMethods.EM_SETPASSWORDCHAR, pwdChar, 0); + Invalidate(); + } + } + + /// + /// The value of the Edit control default password char. + /// + private char SystemPasswordChar + { + get + { + if (MaskedTextBox.systemPwdChar == '\0') + { + // This is the hard way to get the password char - left here for information. + // It is picked up from Comctl32.dll. If VisualStyles is enabled it will get the dot char. + /* + StringBuilder charVal = new StringBuilder(20); // it could be 0x0000000000009999 format. + bool foundRsc = false; + int IDS_PASSWORDCHAR = 0x1076; // %ntsdx%\shell\comctrl32\v6\rcids.h + // defined in en.rc as: IDS_PASSWORDCHAR "9679" // 0x25cf - Black Circle + + IntSecurity.UnmanagedCode.Assert(); + + try + { + // The GetModuleHandle function returns a handle to a mapped module without incrementing its reference count. + // @"C:\windows\winsxs\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.10.0_x-ww_f7fb5805\comctl32.dll if VisulaStyles enabled. + + IntPtr hModule = UnsafeNativeMethods.GetModuleHandle("comctl32.dll"); + Debug.Assert(hModule != IntPtr.Zero, String.Format("Could not get a handle to comctl32.dll - Error: 0x{0:X8}", Marshal.GetLastWin32Error())); + + foundRsc = UnsafeNativeMethods.LoadString(new HandleRef(null, hModule), IDS_PASSWORDCHAR, charVal, charVal.Capacity); + } + catch( Exception ex ) + { + if( ClientUtils.IsSecurityOrCriticalException( ex ) ) + { + throw; + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + MaskedTextBox.systemPwdChar = foundRsc ? (char) int.Parse(charVal.ToString()) : MaskedTextProvider.DefaultPasswordChar; + */ + + // We need to temporarily create an edit control to get the default password character. + // We cannot use this control because we would have to reset the native control's password char to use + // the defult one so we can get it; this would change the text displayed in the box (even for a short time) + // opening a sec hole. + + TextBox txtBox = new TextBox(); + txtBox.UseSystemPasswordChar = true; // this forces the creation of the control handle. + + MaskedTextBox.systemPwdChar = txtBox.PasswordChar; + + txtBox.Dispose(); + } + + return MaskedTextBox.systemPwdChar; + } + } + + /// + /// The Text setter validates the input char by char, raising the MaskInputRejected event for invalid chars. + /// The Text getter returns the formatted text according to the IncludeLiterals and IncludePrompt properties. + /// + [ + Editor("System.Windows.Forms.Design.MaskedTextBoxTextEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.Repaint), + Bindable(true), + DefaultValue(""), // This property is shadowed by MaskedTextBoxDesigner. + Localizable(true) + ] + public override string Text + { + get + { + if( this.flagState[IS_NULL_MASK] || this.flagState[QUERY_BASE_TEXT]) + { + return base.Text; + } + + return TextOutput; + } + set + { + if (this.flagState[IS_NULL_MASK]) + { + base.Text = value; + return; + } + + if (string.IsNullOrEmpty(value)) + { + // reset the input text. + Delete(Keys.Delete, 0, this.maskedTextProvider.Length); + } + else + { + if( this.RejectInputOnFirstFailure ) + { + MaskedTextResultHint hint; + string oldText = TextOutput; + if (this.maskedTextProvider.Set(value, out this.caretTestPos, out hint)) + { + //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) + if( TextOutput != oldText ) + { + SetText(); + } + this.SelectionStart = ++this.caretTestPos; + } + else + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); + } + } + else + { + Replace(value, /*startPosition*/ 0, /*selectionLen*/ this.maskedTextProvider.Length); + } + } + } + } + + /// + /// Returns the length of the displayed text. See VSW#502543. + /// + [Browsable( false )] + public override int TextLength + { + get + { + if( this.flagState[IS_NULL_MASK] ) + { + return base.TextLength; + } + + // In Win9x systems TextBoxBase.TextLength calls Text.Length directly and does not query the window for the actual text length. + // If TextMaskFormat is set to a anything different from IncludePromptAndLiterals or HidePromptOnLeave is true the return value + // may be incorrect because the Text property value and the display text may be different. We need to handle this here. + + return GetFormattedDisplayString().Length; + } + } + + /// + /// The formatted text, it is what the Text getter returns when a mask has been applied to the control. + /// The text format follows the IncludeLiterals and IncludePrompt properties (See MaskedTextProvider.ToString()). + /// + private string TextOutput + { + get + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + return this.maskedTextProvider.ToString(); + } + } + + /// + /// Gets or sets how text is aligned in the control. + /// Note: This code is duplicated in TextBox for simplicity. + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(HorizontalAlignment.Left), + SRDescription(SR.TextBoxTextAlignDescr) + ] + public HorizontalAlignment TextAlign + { + get + { + return textAlign; + } + set + { + if (textAlign != value) + { + //verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + + textAlign = value; + RecreateHandle(); + OnTextAlignChanged(EventArgs.Empty); + } + } + } + + /// + /// Event to notify the text alignment has changed. + /// + [ + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.RadioButtonOnTextAlignChangedDescr) + ] + public event EventHandler TextAlignChanged + { + add + { + Events.AddHandler(EVENT_TEXTALIGNCHANGED, value); + } + + remove + { + Events.RemoveHandler(EVENT_TEXTALIGNCHANGED, value); + } + } + + /// + /// Specifies the formatting options for text output (Whether the mask returned from the Text + /// property includes Literals and/or prompt characters). + /// When prompt characters are excluded, theyare returned as spaces in the string returned. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxTextMaskFormat), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(MaskFormat.IncludeLiterals) + ] + public MaskFormat TextMaskFormat + { + get + { + if( this.IncludePrompt ) + { + if( this.IncludeLiterals ) + { + return MaskFormat.IncludePromptAndLiterals; + } + + return MaskFormat.IncludePrompt; + } + + if( this.IncludeLiterals ) + { + return MaskFormat.IncludeLiterals; + } + + return MaskFormat.ExcludePromptAndLiterals; + } + + set + { + if( this.TextMaskFormat == value ) + { + return; + } + + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); + } + + // Changing the TextMaskFormat will likely change the 'output' text (Text getter value). Cache old value to + // verify it against the new value and raise OnTextChange if needed. + string oldText = this.flagState[IS_NULL_MASK] ? null : TextOutput; + + if( value == MaskFormat.IncludePrompt ) + { + this.IncludePrompt = true; + this.IncludeLiterals = false; + } + else if( value == MaskFormat.IncludeLiterals ) + { + this.IncludePrompt = false; + this.IncludeLiterals = true; + } + else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals + { + bool include = value == MaskFormat.IncludePromptAndLiterals; + this.IncludePrompt = include; + this.IncludeLiterals = include; + } + + if( oldText != null && oldText != TextOutput ) + { + OnTextChanged(EventArgs.Empty); + } + } + } + + /// + /// Provides some interesting information for the TextBox control in String form. + /// Returns the test string (no password, including literals and prompt). + /// + public override string ToString() + { + if( this.flagState[IS_NULL_MASK] ) + { + return base.ToString(); + } + + // base.ToString will call Text, we want to always display prompt and literals. + bool includePrompt = this.IncludePrompt; + bool includeLits = this.IncludeLiterals; + string str; + try + { + this.IncludePrompt = this.IncludeLiterals = true; + str = base.ToString(); + } + finally + { + this.IncludePrompt = includePrompt; + this.IncludeLiterals = includeLits; + } + + return str; + } + + /// + /// Event to notify when the validating object completes parsing the formatted text. + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.MaskedTextBoxTypeValidationCompletedDescr) + ] + public event TypeValidationEventHandler TypeValidationCompleted + { + add + { + Events.AddHandler(EVENT_VALIDATIONCOMPLETED, value); + } + remove + { + Events.RemoveHandler(EVENT_VALIDATIONCOMPLETED, value); + } + } + + /// + /// Indicates if the text in the edit control should appear as the default password character. + /// This property has precedence over the PasswordChar property. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MaskedTextBoxUseSystemPasswordCharDescr), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(false) + ] + public bool UseSystemPasswordChar + { + get + { + return this.flagState[USE_SYSTEM_PASSWORD_CHAR]; + } + set + { + if (value != this.flagState[USE_SYSTEM_PASSWORD_CHAR]) + { + if (value) + { + if( this.SystemPasswordChar == this.PromptChar ) + { + // Prompt and password chars must be different. + throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); + } + + this.maskedTextProvider.PasswordChar = this.SystemPasswordChar; + } + else + { + // this.passwordChar could be '\0', in which case we are resetting the display to show the input char. + this.maskedTextProvider.PasswordChar = this.passwordChar; + } + + this.flagState[USE_SYSTEM_PASSWORD_CHAR] = value; + + if( this.flagState[IS_NULL_MASK]) + { + SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); + } + else + { + SetWindowText(); + } + + VerifyImeRestrictedModeChanged(); + } + } + } + + /// + /// Type of the object to be used to parse the text when the user leaves the control. + /// A ValidatingType object must implement a method with one fo the following signature: + /// public static Object Parse(string) + /// public static Object Parse(string, IFormatProvider) + /// See DateTime.Parse(...) for an example. + /// + [ + Browsable(false), + DefaultValue(null) + ] + public Type ValidatingType + { + get + { + return this.validatingType; + } + set + { + if( this.validatingType != value ) + { + this.validatingType = value; + } + } + } + + /// + /// Unsupported method/property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool WordWrap + { + get { return false; } + set {} + } + + + ////////////// Methods + + /// + /// Clears information about the most recent operation from the undo buffer of the control. + /// Unsupported property/method. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public new void ClearUndo() + { + } + + /// + /// Creates a handle for this control. This method is called by the .NET Framework, this should + /// not be called. Inheriting classes should always call base.createHandle when overriding this method. + /// Overridden to be able to set the control text with the masked (passworded) value when recreating + /// handle, since the underlying native edit control is not aware of it. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced), + UIPermission(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows) + ] + protected override void CreateHandle() + { + if (!this.flagState[IS_NULL_MASK] && RecreatingHandle) + { + // update cached text value in Control. Don't preserve caret, cannot query for selection start at this time. + SetWindowText(GetFormattedDisplayString(), false, false); + } + + base.CreateHandle(); + } + + /// + /// + /// Deletes characters from the control's text according to the key pressed (Delete/Backspace). + /// Returns true if something gets actually deleted, false otherwise. + /// + private void Delete(Keys keyCode, int startPosition, int selectionLen) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + Debug.Assert( keyCode == Keys.Delete || keyCode == Keys.Back, "Delete called with keyCode == " + keyCode.ToString() ); + Debug.Assert( startPosition >= 0 && ((startPosition + selectionLen) <= this.maskedTextProvider.Length), "Invalid position range." ); + + // On backspace, moving the start postion back by one has the same effect as delete. If text is selected, there is no + // need for moving the position back. + + this.caretTestPos = startPosition; + + if( selectionLen == 0 ) + { + if( keyCode == Keys.Back ) + { + if( startPosition == 0 ) // At beginning of string, backspace does nothing. + { + return; + } + + startPosition--; // so it can be treated as delete. + } + else // (keyCode == Keys.Delete) + { + if( (startPosition + selectionLen) == this.maskedTextProvider.Length ) // At end of string, delete does nothing. + { + return; + } + } + } + + int tempPos; + int endPos = selectionLen > 0 ? startPosition + selectionLen - 1 : startPosition; + MaskedTextResultHint hint; + + string oldText = TextOutput; + if (this.maskedTextProvider.RemoveAt(startPosition, endPos, out tempPos, out hint)) + { + //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect) // Text was changed. + if( TextOutput != oldText ) + { + SetText(); + this.caretTestPos = startPosition; + } + else + { + // If succeeded but nothing removed, the caret should move as follows: + // 1. If selectionLen > 0, or on back and hint == SideEffect: move to selectionStart. + // 2. If hint == NoEffect, On Delete move to next edit position, if any or not already in one. + // On back move to the next edit postion at the left if no more assigned position at the right, + // in such case find an assigned position and move one past or one position left if no assigned pos found + // (taken care by 'startPosition--' above). + // 3. If hint == SideEffect, on Back move like arrow key, (startPosition is already moved, startPosition-- above). + + if( selectionLen > 0 ) + { + this.caretTestPos = startPosition; + } + else + { + if( hint == MaskedTextResultHint.NoEffect ) // Case 2. + { + if( keyCode == Keys.Delete ) + { + this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, forward); + } + else + { + if( this.maskedTextProvider.FindAssignedEditPositionFrom( startPosition, forward ) == MaskedTextProvider.InvalidIndex ) + { + // No assigned position at the right, nothing to shift then move to the next assigned position at the + // left (if any). + this.caretTestPos = this.maskedTextProvider.FindAssignedEditPositionFrom(startPosition, backward); + } + else + { + // there are assigned positions at the right so move to an edit position at the left to get ready for + // removing the character on it or just shifting the characters at the right + this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, backward); + } + + if( this.caretTestPos != MaskedTextProvider.InvalidIndex ) + { + this.caretTestPos++; // backspace gets ready to remove one position past the edit position. + } + } + + if( this.caretTestPos == MaskedTextProvider.InvalidIndex ) + { + this.caretTestPos = startPosition; + } + } + else // (hint == MaskedTextProvider.OperationHint.SideEffect) + { + if( keyCode == Keys.Back ) // Case 3. + { + this.caretTestPos = startPosition; + } + } + } + } + } + else + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(tempPos, hint)); + } + + // Reposition caret. Call base.SelectInternal for perf reasons. + //this.SelectionLength = 0; + //this.SelectionStart = this.caretTestPos; // new caret position. + base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); + + return; + } + + /// + /// Returns the character nearest to the given point. + /// + public override char GetCharFromPosition(Point pt) + { + char ch; + + this.flagState[QUERY_BASE_TEXT] = true; + try + { + ch = base.GetCharFromPosition(pt); + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + return ch; + } + + + /// + /// Returns the index of the character nearest to the given point. + /// + public override int GetCharIndexFromPosition(Point pt) + { + int index; + + this.flagState[QUERY_BASE_TEXT] = true; + try + { + index = base.GetCharIndexFromPosition(pt); + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + return index; + } + + /// + /// Returns the position of the last input character (or if available, the next edit position). + /// This is used by base.AppendText. + /// + internal override int GetEndPosition() + { + if( this.flagState[IS_NULL_MASK]) + { + return base.GetEndPosition(); + } + + int pos = this.maskedTextProvider.FindEditPositionFrom( this.maskedTextProvider.LastAssignedPosition + 1, forward ); + + if( pos == MaskedTextProvider.InvalidIndex ) + { + pos = this.maskedTextProvider.LastAssignedPosition + 1; + } + + return pos; + } + + /// + /// Unsupported method/property. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public new int GetFirstCharIndexOfCurrentLine() + { + return 0; + } + + /// + /// Unsupported method/property. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public new int GetFirstCharIndexFromLine(int lineNumber) + { + return 0; + } + + /// + /// Gets the string in the text box following the formatting parameters includePrompt and includeLiterals and + /// honoring the PasswordChar property. + /// + private string GetFormattedDisplayString() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + bool includePrompt; + + if (this.ReadOnly) // Always hide prompt. + { + includePrompt = false; + } + else if (this.DesignMode) // Not RO and at design time, always show prompt. + { + includePrompt = true; + } + else // follow HidePromptOnLeave property. + { + includePrompt = !(this.HidePromptOnLeave && !this.Focused); + } + + return this.maskedTextProvider.ToString(/*ignorePwdChar */ false, includePrompt, /*includeLiterals*/ true, 0, this.maskedTextProvider.Length); + } + + /// + /// Unsupported method/property. + /// virtual method. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public override int GetLineFromCharIndex(int index) + { + return 0; + } + + /// + /// Returns the location of the character at the given index. + /// + public override Point GetPositionFromCharIndex(int index) + { + Point pos; + + this.flagState[QUERY_BASE_TEXT] = true; + try + { + pos = base.GetPositionFromCharIndex(index); + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + return pos; + } + + /// + /// Need to override this method so when get_Text is called we return the text that is actually + /// painted in the control so measuring text works on the actual text and not the formatted one. + /// + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + Size size; + + this.flagState[QUERY_BASE_TEXT] = true; + try + { + size = base.GetPreferredSizeCore( proposedConstraints ); + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + return size; + } + + /// + /// The selected text in the control according to the CutCopyMaskFormat properties (IncludePrompt/IncludeLiterals). + /// This is used in Cut/Copy operations (SelectedText). + /// The prompt character is always replaced with a blank character. + /// + private string GetSelectedText() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + int selStart, selLength; + base.GetSelectionStartAndLength( out selStart, out selLength ); + + if( selLength == 0 ) + { + return string.Empty; + } + + bool includePrompt = (CutCopyMaskFormat & MaskFormat.IncludePrompt ) != 0; + bool includeLiterals = (CutCopyMaskFormat & MaskFormat.IncludeLiterals) != 0; + + return this.maskedTextProvider.ToString( /*ignorePasswordChar*/ true, includePrompt, includeLiterals, selStart, selLength ); + } + + + /// + protected override void OnBackColorChanged(EventArgs e) + { + base.OnBackColorChanged(e); + // VSWhidbey 465708. Force repainting of the entire window frame + if (Application.RenderWithVisualStyles && this.IsHandleCreated && this.BorderStyle == BorderStyle.Fixed3D) + { + SafeNativeMethods.RedrawWindow(new HandleRef(this, this.Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); + } + } + + /// + /// Overridden to update the newly created handle with the settings of the PasswordChar properties + /// if no mask has been set. + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + base.SetSelectionOnHandle(); + + if( this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.IsPassword ) + { + SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); + } + } + + /// + /// Raises the IsOverwriteModeChanged event. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void OnIsOverwriteModeChanged(EventArgs e) + { + EventHandler eh = Events[EVENT_ISOVERWRITEMODECHANGED] as EventHandler; + + if (eh != null) + { + eh(this, e); + } + } + + /// + /// Raises the event. + /// + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if( this.flagState[IS_NULL_MASK]) + { + // Operates as a regular text box base. + return; + } + + Keys keyCode = e.KeyCode; + + // Special-case Return & Esc since they generate invalid characters we should not process OnKeyPress. + if( keyCode == Keys.Return || keyCode == Keys.Escape ) + { + this.flagState[HANDLE_KEY_PRESS] = false; + } + + + // Insert is toggled when not modified with some other key (ctrl, shift...). Note that shift-Insert is + // same as paste. + if (keyCode == Keys.Insert && e.Modifiers == Keys.None && this.insertMode == InsertKeyMode.Default) + { + this.flagState[INSERT_TOGGLED] = !this.flagState[INSERT_TOGGLED]; + OnIsOverwriteModeChanged(EventArgs.Empty); + return; + } + + if (e.Control && char.IsLetter((char)keyCode)) + { + switch (keyCode) + { + // Unsupported keys should not be handled to allow generatating the corresponding message + // which is handled in the WndProc. + //case Keys.Z: // ctrl-z == Undo. + //case Keys.Y: // ctrl-y == Redo. + // e.Handled = true; + // return; + + // Note: Ctrl-Insert (Copy -Shortcut.CtrlIns) and Shft-Insert (Paste - Shortcut.ShiftIns) are + // handled by the base class and behavior depend on ShortcutsEnabled property. + + // Special cases: usually cases where the native edit control would modify the mask. + case Keys.H: // ctrl-h == Backspace == '\b' + keyCode = Keys.Back; // handle it below. + break; + + default: + // Next OnKeyPress should not be handled to allow Ctrl- to be processed in the + // base class so corresponding messages can be generated (WM_CUT/WM_COPY/WM_PASTE). + // Combined characters don't generate OnKeyDown by themselves but they generate OnKeyPress. + this.flagState[HANDLE_KEY_PRESS] = false; + return; + } + } + + if ( keyCode == Keys.Delete || keyCode == Keys.Back ) // Deletion keys. + { + if (!this.ReadOnly) + { + int selectionLen; + int startPosition; + + base.GetSelectionStartAndLength( out startPosition, out selectionLen ); + + switch (e.Modifiers) + { + case Keys.Shift: + if( keyCode == Keys.Delete ) + { + keyCode = Keys.Back; + } + goto default; + + case Keys.Control: + if( selectionLen == 0 ) // In other case, the selected text should be deleted. + { + if( keyCode == Keys.Delete ) // delete to the end of the string. + { + selectionLen = this.maskedTextProvider.Length - startPosition; + } + else // ( keyCode == Keys.Back ) // delete to the beginning of the string. + { + selectionLen = startPosition == this.maskedTextProvider.Length /*at end of text*/ ? startPosition : startPosition + 1; + startPosition = 0; + } + } + goto default; + + default: + if( !this.flagState[HANDLE_KEY_PRESS] ) + { + this.flagState[HANDLE_KEY_PRESS] = true; + } + break; + } + + // + // Handle special case when using Korean IME and ending a composition. + // + /* This code is no longer needed after fixing bug#517013 - Left here for reference DON'T DELETE. + if ((ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) && this.flagState[IME_ENDING_COMPOSITION]) + { + // Korean IME & Edit control weirdness in action: + // When pressing Del/Esc/Enter/BckSpc during a composition, the character is converted and a + // corresponding WM_IME_CHAR in placed in the app queue. + + if(keyCode == Keys.Back && selectionLen == 0) + { + // After the WM_IME_CHAR message is processed, a WM_CHAR message is queued for the backspace; the + // edit control processes the message deleting the previous character. Since we don't pass this + // message to the edit control we need to do it ourselves (move position one ahead because it is + // set at the composition window which is inserted in the test string. + startPosition++; + + // Note: If the converted character is invalid and there is a character already placed in the previous + // position, it will be deleted. THIS IS BY DESIGN: It is exactly like the user performing the convertion + // and then pressing backspace, there's no way to differenciate these two scenarios. + // See VSWhidbey#200690 for the sequence of messages generated. + } + } + */ + + Delete(keyCode, startPosition, selectionLen); + e.SuppressKeyPress = true; + } + } + } + + + /// + /// Raises the event. + /// + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + + if( this.flagState[IS_NULL_MASK]) + { + // Operates as a regular text box base. + return; + } + + // This key may be a combined key involving a letter, like Ctrl-A; let the native control handle it. + if( !this.flagState[HANDLE_KEY_PRESS] ) + { + this.flagState[HANDLE_KEY_PRESS] = true; + + // When the combined key involves a letter, the final character is not a letter. There are some + // Ctrl combined keys that generate a letter and can be confusing; we do not mean to pass those + // characters to the underlying Edit control. These combinations are: Ctrl-F<#> and Ctrl-Atl- + if (!char.IsLetter(e.KeyChar)) + { + return; + } + } + + if( !this.ReadOnly) + { + // At this point the character needs to be processed ... + + MaskedTextResultHint hint; + + int selectionStart; + int selectionLen; + + base.GetSelectionStartAndLength( out selectionStart, out selectionLen ); + + string oldText = TextOutput; + if (PlaceChar(e.KeyChar, selectionStart, selectionLen, this.IsOverwriteMode, out hint)) + { + //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) + if( TextOutput != oldText ) + { + SetText(); // Now set the text in the display. + } + + this.SelectionStart = ++this.caretTestPos; // caretTestPos is updated in PlaceChar. + + if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) + { + // Korean IMEs complete composition when a character has been fully converted, so the composition string + // is only one-character long; once composed we block the IME if there ins't more room in the test string. + + int editPos = this.maskedTextProvider.FindUnassignedEditPositionFrom(this.caretTestPos, forward); + if (editPos == MaskedTextProvider.InvalidIndex) + { + ImeComplete(); // Force completion of compostion. + } + } + } + else + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); // caretTestPos is updated in PlaceChar. + } + + if( selectionLen > 0 ) + { + this.SelectionLength = 0; + } + + e.Handled = true; + } + } + + /// + /// Raises the event. + /// + protected override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + + // KeyUp is the last message to be processed so it is the best place to reset these flags. + + if (this.flagState[IME_COMPLETING]) + { + this.flagState[IME_COMPLETING] = false; + } + + if( this.flagState[IME_ENDING_COMPOSITION] ) + { + this.flagState[IME_ENDING_COMPOSITION] = false; + } + } + + /// + /// Raises the MaskChanged event. + /// + [ + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected virtual void OnMaskChanged(EventArgs e) + { + EventHandler eh = Events[EVENT_MASKCHANGED] as EventHandler; + + if (eh != null) + { + eh(this, e); + } + } + + /// + /// Raises the MaskInputRejected event. + /// + private void OnMaskInputRejected(MaskInputRejectedEventArgs e) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + if (this.BeepOnError) + { + System.Media.SoundPlayer sp = new System.Media.SoundPlayer(); + sp.Play(); + } + + MaskInputRejectedEventHandler eh = Events[EVENT_MASKINPUTREJECTED] as MaskInputRejectedEventHandler; + + if (eh != null) + { + eh(this, e); + } + } + + /// + /// Unsupported method/property. + /// virtual method. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + protected override void OnMultilineChanged(EventArgs e) + { + } + + /// + /// Raises the TextAlignChanged event. + /// + protected virtual void OnTextAlignChanged(EventArgs e) + { + EventHandler eh = Events[EVENT_TEXTALIGNCHANGED] as EventHandler; + if (eh != null) + { + eh(this, e); + } + } + + + /// + /// Raises the TypeValidationCompleted event. + /// + private void OnTypeValidationCompleted(TypeValidationEventArgs e) + { + TypeValidationEventHandler eh = Events[EVENT_VALIDATIONCOMPLETED] as TypeValidationEventHandler; + if (eh != null) + { + eh(this, e); + } + } + + /// + /// Raises the System.Windows.Forms.Control.Validating event. + /// Overridden here to be able to control the order validating events are + /// raised [TypeValidationCompleted - Validating - Validated - Leave - KillFocus] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnValidating(CancelEventArgs e) + { + // Note: It seems impractical to perform type validation here if the control is read only but we need + // to be consistent with other TextBoxBase controls which don't check for RO; and we don't want + // to fix them to avoid introducing breaking changes. + PerformTypeValidation(e); + base.OnValidating(e); + } + + /// + /// Raises the TextChanged event and related Input/Output text events when mask is null. + /// Overriden here to be able to control order of text changed events. + /// + protected override void OnTextChanged(EventArgs e) + { + // A text changed event handler will most likely query for the Text value, we need to return the + // formatted one. + bool queryBaseText = this.flagState[QUERY_BASE_TEXT]; + this.flagState[QUERY_BASE_TEXT] = false; + try + { + base.OnTextChanged(e); + } + finally + { + this.flagState[QUERY_BASE_TEXT] = queryBaseText; + } + } + /// + /// Replaces the current selection in the text box specified by the startPosition and selectionLen parameters + /// with the contents of the supplied string. + /// + private void Replace(string text, int startPosition, int selectionLen) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + Debug.Assert(text != null, "text is null."); + + // Clone the MaskedTextProvider so text properties are not modified until the paste operation is + // completed. This is needed in case one of these properties is retreived in a MaskedInputRejected + // event handler (clipboard text is attempted to be set into the input text char by char). + + MaskedTextProvider clonedProvider = (MaskedTextProvider) this.maskedTextProvider.Clone(); + + // Cache the current caret position so we restore it in case the text does not change. VSW#498875. + int currentCaretPos = this.caretTestPos; + + // First replace characters in the selection (if any and if any edit positions) until completed, or the test position falls + // outside the selection range, or there's no more room in the test string for editable characters. + // Then insert any remaining characters from the input. + + MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; + int endPos = startPosition + selectionLen - 1; + + if( this.RejectInputOnFirstFailure ) + { + bool succeeded; + + succeeded = (startPosition > endPos) ? + clonedProvider.InsertAt(text, startPosition, out this.caretTestPos, out hint ) : + clonedProvider.Replace(text, startPosition, endPos, out this.caretTestPos, out hint); + + if( !succeeded ) + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); + } + } + else + { + // temp hint used to preserve the 'primary' operation hint (no side effects). + MaskedTextResultHint tempHint = hint; + int testPos; + + foreach (char ch in text) + { + if( !this.maskedTextProvider.VerifyEscapeChar( ch, startPosition )) // char won't be escaped, find and edit position for it. + { + // Observe that we look for a position w/o respecting the selection length, because the input text could be larger than + // the number of edit positions in the selection. + testPos = clonedProvider.FindEditPositionFrom(startPosition, forward); + + if( testPos == MaskedTextProvider.InvalidIndex ) + { + // this will continue to execute (fail) until the end of the text so we fire the event for each remaining char. + OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); + continue; + } + + startPosition = testPos; + } + + int length = endPos >= startPosition ? 1 : 0; + + // if length > 0 we are (re)placing the input char in the current startPosition, otherwise we are inserting the input. + bool replace = length > 0; + + if (PlaceChar(clonedProvider, ch, startPosition, length, replace, out tempHint)) + { + // caretTestPos is updated in PlaceChar call. + startPosition = this.caretTestPos + 1; + + // place char will insert or replace a single character so the hint must be success, and that will be the final operation + // result hint. + if (tempHint == MaskedTextResultHint.Success && hint != tempHint) + { + hint = tempHint; + } + } + else + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, tempHint)); + } + } + + if (selectionLen > 0) + { + // At this point we have processed all characters from the input text (if any) but still need to + // remove remaining characters from the selected text (if editable and valid chars). + + if (startPosition <= endPos) + { + if (!clonedProvider.RemoveAt(startPosition, endPos, out this.caretTestPos, out tempHint)) + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, tempHint)); + } + + // If 'replace' is not actually performed (maybe the input is empty which means 'remove', hint will be whatever + // the 'remove' operation result hint is. + if (hint == MaskedTextResultHint.NoEffect && hint != tempHint) + { + hint = tempHint; + } + } + } + } + + bool updateText = TextOutput != clonedProvider.ToString(); + + // Always set the mtp, the formatted text could be the same but the assigned positions may be different. + this.maskedTextProvider = clonedProvider; + + // Update text if needed. + if( updateText ) + { + SetText(); + + // Update caret position. + this.caretTestPos = startPosition; + base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); + } + else + { + this.caretTestPos = currentCaretPos; + } + + return; + } + + /// + /// Pastes specified text over the currently selected text (if any) shifting upper characters if + /// input is longer than selected text, and/or removing remaining characters from the selection if + /// input contains less characters. + /// + private void PasteInt( string text ) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + int selStart, selLength; + base.GetSelectionStartAndLength(out selStart, out selLength); + + if( string.IsNullOrEmpty(text) ) + { + Delete( Keys.Delete, selStart, selLength ); + } + else + { + Replace(text, selStart, selLength); + } + } + + /// + /// Performs validation of the input string using the provided ValidatingType object (if any). + /// Returns an object created from the formatted text. + /// If the CancelEventArgs param is not null, it is assumed the control is leaving focus and + /// the validation event chain is being executed (TypeValidationCompleted - Validating - Validated...); + /// the value of the CancelEventArgs.Cancel property is the same as the TypeValidationEventArgs.Cancel + /// on output (Cancel provides proper handling of focus shifting at the Control class level). + /// Note: The text being validated does not include prompt chars. + /// + private object PerformTypeValidation(CancelEventArgs e) + { + object parseRetVal = null; + + if (this.validatingType != null) + { + string message = null; + + if (!this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.MaskCompleted == false) + { + message = SR.GetString(SR.MaskedTextBoxIncompleteMsg); + } + else + { + string textValue; + + if( !this.flagState[IS_NULL_MASK]) // replace prompt with space. + { + textValue = this.maskedTextProvider.ToString(/*includePrompt*/ false, this.IncludeLiterals); + } + else + { + textValue = base.Text; + } + + try + { + parseRetVal = Formatter.ParseObject( + textValue, // data + this.validatingType, // targetType + typeof(string), // sourceType + null, // targetConverter + null, // sourceConverter + this.formatProvider, // formatInfo + null, // nullValue + Formatter.GetDefaultDataSourceNullValue(this.validatingType)); // dataSourceNullValue + } + catch (Exception exception) + { + if (ClientUtils.IsSecurityOrCriticalException(exception)) + { + throw; + } + + if (exception.InnerException != null) // Outer exception is a generic TargetInvocationException. + { + exception = exception.InnerException; + } + + message = exception.GetType().ToString() + ": " + exception.Message; + } + } + + bool isValidInput = false; + if (message == null) + { + isValidInput = true; + message = SR.GetString(SR.MaskedTextBoxTypeValidationSucceeded); + } + + TypeValidationEventArgs tve = new TypeValidationEventArgs(this.validatingType, isValidInput, parseRetVal, message); + OnTypeValidationCompleted(tve); + + if( e != null ) + { + e.Cancel = tve.Cancel; + } + } + + return parseRetVal; + } + + /// + /// Insert or replaces the specified character into the control's text and updates the caret position. + /// If overwrite is true, it replaces the character at the selection start position. + /// + private bool PlaceChar(char ch, int startPosition, int length, bool overwrite, + out MaskedTextResultHint hint) + { + return PlaceChar(this.maskedTextProvider, ch, startPosition, length, overwrite, out hint ); + } + + /// + /// Override version to be able to perform the operation on a cloned provider. + /// + private bool PlaceChar(MaskedTextProvider provider, char ch, int startPosition, int length, bool overwrite, + out MaskedTextResultHint hint) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + this.caretTestPos = startPosition; + + if (startPosition < this.maskedTextProvider.Length) + { + if (length > 0) // Replacing selection with input char. + { + int endPos = startPosition + length - 1; + return provider.Replace(ch, startPosition, endPos, out this.caretTestPos, out hint); + } + else + { + if (overwrite) + { + // overwrite character at next edit position from startPosition (inclusive). + return provider.Replace(ch, startPosition, out this.caretTestPos, out hint); + } + else // insert. + { + return provider.InsertAt(ch, startPosition, out this.caretTestPos, out hint); + } + } + } + + hint = MaskedTextResultHint.UnavailableEditPosition; + return false; + } + + /// + /// : + /// Processes a command key. This method is called during message + /// pre-processing to handle command keys. Command keys are keys that always + /// take precedence over regular input keys. Examples of command keys + /// include accelerators and menu shortcuts. The method must return true to + /// indicate that it has processed the command key, or false to indicate + /// that the key is not a command key. + /// + /// processCmdKey() first checks if the control has a context menu, and if + /// so calls the menu's processCmdKey() to check for menu shortcuts. If the + /// command key isn't a menu shortcut, and if the control has a parent, the + /// key is passed to the parent's processCmdKey() method. The net effect is + /// that command keys are "bubbled" up the control hierarchy. + /// + /// When overriding processCmdKey(), a control should return true to + /// indicate that it has processed the key. For keys that aren't processed by + /// the control, the result of "base.processCmdKey()" should be returned. + /// + /// Controls will seldom, if ever, need to override this method. + /// + /// + /// Implements the handling of Ctrl+A (select all). Note: Code copied from TextBox. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + // + // The base class should be called first because it implements ShortcutsEnabled, + // which takes precedence over Ctrl+A + // + bool msgProcessed = base.ProcessCmdKey(ref msg, keyData); + + if (!msgProcessed) + { + if ((int)keyData == (int)Shortcut.CtrlA) + { + base.SelectAll(); + msgProcessed = true; // This prevents generating a WM_CHAR for 'A'. + } + } + + return msgProcessed; + } + + /// + /// We need to override this method so we can handle input language changes properly. Control + /// doesn't handle the WM_CHAR messages generated after WM_IME_CHAR messages, it passes them + /// to DefWndProc (the characters would be displayed in the text box always). + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessKeyMessage(ref Message m) + { + // call base's method so the WM_CHAR and other messages are processed; this gives Control the + // chance to flush all pending WM_CHAR processing after WM_IME_CHAR messages are generated. + + bool msgProcessed = base.ProcessKeyMessage(ref m); + + if (this.flagState[IS_NULL_MASK]) + { + return msgProcessed; // Operates as a regular text box base. + } + + // If this WM_CHAR message is sent after WM_IME_CHAR, we ignore it since we already processed + // the corresponding WM_IME_CHAR message. + + if( m.Msg == NativeMethods.WM_CHAR && base.ImeWmCharsToIgnore > 0 ) { + return true; // meaning, we handled the message so it is not passed to the default WndProc. + } + + return msgProcessed; + } + + + /// + /// Designe time support for resetting Culture property.. + /// + private void ResetCulture() + { + this.Culture = CultureInfo.CurrentCulture; + } + + /// + /// Unsupported method/property. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public new void ScrollToCaret() + { + } + + /// + /// Sets the underlying MaskedTextProvider object. Used when the control is initialized + /// and one of its properties, backed up by the MaskedTextProvider, changes; this requires + /// recreating the provider because it is immutable. + /// + private void SetMaskedTextProvider( MaskedTextProvider newProvider ) + { + SetMaskedTextProvider( newProvider, null); + } + + /// + /// Overload to allow for passing the text when the mask is being changed from null, + /// in this case the maskedTextProvider holds backend info only (not the text). + /// + private void SetMaskedTextProvider( MaskedTextProvider newProvider, string textOnInitializingMask ) + { + Debug.Assert( newProvider != null, "Initializing from a null MaskProvider ref." ); + + // Set R/W properties. + newProvider.IncludePrompt = this.maskedTextProvider.IncludePrompt; + newProvider.IncludeLiterals = this.maskedTextProvider.IncludeLiterals; + newProvider.SkipLiterals = this.maskedTextProvider.SkipLiterals; + newProvider.ResetOnPrompt = this.maskedTextProvider.ResetOnPrompt; + newProvider.ResetOnSpace = this.maskedTextProvider.ResetOnSpace; + + // If mask not initialized and not initializing it, the new provider is just a property backend. + // Change won't have any effect in text. + if( this.flagState[IS_NULL_MASK] && textOnInitializingMask == null) + { + this.maskedTextProvider = newProvider; + return; + } + + int testPos = 0; + bool raiseOnMaskInputRejected = false; // Raise if new provider rejects old text. + MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; + MaskedTextProvider oldProvider = this.maskedTextProvider; + + // Attempt to add previous text. + // If the mask is the same, we need to preserve the caret and character positions if the text is added successfully. + bool preserveCharPos = oldProvider.Mask == newProvider.Mask; + + // Cache text output text before setting the new provider to determine whether we need to raise the TextChanged event. + string oldText; + + // NOTE: Whenever changing the MTP, the text is lost if any character in the old text violates the new provider's mask. + + if( textOnInitializingMask != null ) // Changing Mask (from null), which is the only RO property that requires passing text. + { + oldText = textOnInitializingMask; + raiseOnMaskInputRejected = !newProvider.Set( textOnInitializingMask, out testPos, out hint ); + } + else + { + oldText = TextOutput; + + // We need to attempt to set the input characters one by one in the edit positions so they are not + // escaped. + int assignedCount = oldProvider.AssignedEditPositionCount; + int srcPos = 0; + int dstPos = 0; + + while( assignedCount > 0 ) + { + srcPos = oldProvider.FindAssignedEditPositionFrom( srcPos, forward ); + Debug.Assert( srcPos != MaskedTextProvider.InvalidIndex, "InvalidIndex unexpected at this time." ); + + if (preserveCharPos) + { + dstPos = srcPos; + } + else + { + dstPos = newProvider.FindEditPositionFrom(dstPos, forward); + + if (dstPos == MaskedTextProvider.InvalidIndex) + { + newProvider.Clear(); + + testPos = newProvider.Length; + hint = MaskedTextResultHint.UnavailableEditPosition; + break; + } + } + + if( !newProvider.Replace( oldProvider[srcPos], dstPos, out testPos, out hint )) + { + preserveCharPos = false; + newProvider.Clear(); + break; + } + + srcPos++; + dstPos++; + assignedCount--; + } + + raiseOnMaskInputRejected = !MaskedTextProvider.GetOperationResultFromHint(hint); + } + + + // Set provider. + this.maskedTextProvider = newProvider; + + if( this.flagState[IS_NULL_MASK] ) + { + this.flagState[IS_NULL_MASK] = false; + } + + // Raising events need to be done only after the new provider has been set so the MTB is in a state where properties + // can be queried from event handlers safely. + if( raiseOnMaskInputRejected ) + { + OnMaskInputRejected(new MaskInputRejectedEventArgs(testPos, hint)); + } + + if( newProvider.IsPassword ) + { + // Reset native edit control so the MaskedTextBox will take control over the characters that + // need to be replaced with the password char (the input text characters). + // MTB takes over. + SetEditControlPasswordChar('\0'); + } + + EventArgs e = EventArgs.Empty; + + if (textOnInitializingMask != null /*changing mask from null*/ || oldProvider.Mask != newProvider.Mask) + { + OnMaskChanged(e); + } + + SetWindowText(GetFormattedDisplayString(), oldText != TextOutput, preserveCharPos); + } + + /// + /// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. + /// TextChanged is raised always, this assumes the display or the output text changed. + /// The caret position is lost (unless cached somewhere else like when lossing the focus). + /// This is the common way of changing the text in the control. + /// + private void SetText() + { + SetWindowText(GetFormattedDisplayString(), true, false); + } + + /// + /// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. + /// TextChanged is not raised. [PasswordChar] + /// The caret position is preserved. + /// + private void SetWindowText() + { + SetWindowText(GetFormattedDisplayString(), false, true); + } + + /// + /// Sets the text directly in the underlying edit control to the value specified. + /// The 'raiseTextChangedEvent' param determines whether TextChanged event is raised or not. + /// The 'preserveCaret' param determines whether an attempt to preserve the caret position should be made or not + /// after the call to SetWindowText (WindowText) is performed. + /// + private void SetWindowText(string text, bool raiseTextChangedEvent, bool preserveCaret) + { + this.flagState[QUERY_BASE_TEXT] = true; + + try + { + if( preserveCaret ) + { + this.caretTestPos = this.SelectionStart; + } + + WindowText = text; // this calls Win32::SetWindowText directly, no OnTextChanged raised. + + if( raiseTextChangedEvent ) + { + OnTextChanged(EventArgs.Empty); + } + + if( preserveCaret ) + { + this.SelectionStart = this.caretTestPos; + } + } + finally + { + this.flagState[QUERY_BASE_TEXT] = false; + } + } + + /// + /// Designe time support for checking if Culture value in the designer should be serialized. + /// + private bool ShouldSerializeCulture() + { + return !CultureInfo.CurrentCulture.Equals(this.Culture); + } + + /// + /// Undoes the last edit operation in the text box. + /// Unsupported property/method. + /// WndProc ignores EM_UNDO. + /// + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public new void Undo() + { + } + + /// + /// Forces type validation. Returns the validated text value. + /// + public object ValidateText() + { + return PerformTypeValidation(null); + } + + /// + /// Deletes all input characters in the current selection. + /// + private bool WmClear() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + if( !this.ReadOnly ) + { + int selStart, selLength; + base.GetSelectionStartAndLength( out selStart, out selLength ); + Delete(Keys.Delete, selStart, selLength); + return true; + } + + return false; + } + + /// + /// Copies current selection text to the clipboard, formatted according to the IncludeLiterals properties but + /// ignoring the prompt character. + /// Returns true if the operation succeeded, false otherwise. + /// + private bool WmCopy() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + if (this.maskedTextProvider.IsPassword) // cannot copy password to clipboard. + { + return false; + } + + string text = GetSelectedText(); + + try + { + // SECREVIEW : Copy needs to work even if the OwnClipboard permission isn't available. We need to assert here. + // This assert is ok, observe that if the control is in password mode it won't reach this point, + // see code above. + // Be careful if code is added after the security assert, you may need to put it in a try-finally + // block to revert the security assert. + IntSecurity.ClipboardWrite.Assert(); + + if (text.Length == 0) + { + Clipboard.Clear(); + } + else + { + Clipboard.SetText(text); + } + } + catch (Exception ex) + { + // Note: Sometimes the above operation throws but it successfully sets the + // data in the clipboard. This usually happens when the Application's Main + // is not attributed with [STAThread]. + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + return true; + } + + /// + /// Processes the WM_IME_COMPOSITION message when using Korean IME. + /// Korean IME uses the control's caret as the composition string (it processes only one character at a time), + /// we need to have special message handling for it. + /// Returns true if the message is handled, false otherwise. + /// + private bool WmImeComposition(ref Message m) + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + +#if DEBUG + if (this.ReadOnly || this.maskedTextProvider.IsPassword) + { + // This should have been already handled by the ReadOnly, PasswordChar and ImeMode properties. + Debug.Assert(this.ImeMode == ImeMode.Disable, "IME enabled when in RO or Pwd mode."); + } +#endif + // Non-Korean IMEs complete compositon when all characters in the string has been composed (when user hits enter); + // Currently, we don't support checking the composition string characters because it would require similar logic + // as the MaskedTextBox itself. + + if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) + { + byte imeConvertionType = imeConvertionNone; + + // Check if there's an update to the compositon string: + if ((m.LParam.ToInt32() & NativeMethods.GCS_COMPSTR) != 0) + { + // The character in the composition has been updated but not yet converted. + imeConvertionType = imeConvertionUpdate; + } + else if ((m.LParam.ToInt32() & NativeMethods.GCS_RESULTSTR) != 0) + { + // The character(s) in the composition has been fully converted. + imeConvertionType = imeConvertionCompleted; + } + + // Process any update in the composition string. + if (imeConvertionType != imeConvertionNone) + { + if (this.flagState[IME_ENDING_COMPOSITION]) + { + // If IME is completing the convertion, we don't want to process further characters. + return this.flagState[IME_COMPLETING]; + } + } + } + + return false; //message not handled. + } + + /// + /// Processes the WM_IME_STARTCOMPOSITION message. + /// Returns true if the message is handled, false otherwise. + /// + private bool WmImeStartComposition() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + // Position the composition window in a valid place. + + int startPosition, selectionLen; + base.GetSelectionStartAndLength( out startPosition, out selectionLen ); + + int startEditPos = this.maskedTextProvider.FindEditPositionFrom( startPosition, forward ); + + if( startEditPos != MaskedTextProvider.InvalidIndex ) + { + if (selectionLen > 0 && (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable)) + { + // Korean IME: We need to delete the selected text and reposition the caret so the IME processes one + // character only, otherwise it would overwrite the selection with the caret (composition string), + // deleting a portion of the mask. + + int endEditPos = this.maskedTextProvider.FindEditPositionFrom(startPosition + selectionLen - 1, backward); + + if (endEditPos >= startEditPos) + { + selectionLen = endEditPos - startEditPos + 1; + Delete(Keys.Delete, startEditPos, selectionLen); + } + else + { + ImeComplete(); + OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); + return true; + } + } + + // update caret position. + if( startPosition != startEditPos ) + { + this.caretTestPos = startEditPos; + this.SelectionStart = this.caretTestPos; + } + + this.SelectionLength = 0; + } + else + { + ImeComplete(); + OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); + return true; + } + + return false; + } + + + /// + /// Processes the WM_PASTE message. Copies the text from the clipboard, if is valid, + /// formatted according to the mask applied to this control. + /// Returns true if the operation succeeded, false otherwise. + /// + private void WmPaste() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + if( this.ReadOnly ) + { + return; + } + + // Get the text from the clipboard. + + string text; + + try + { + IntSecurity.ClipboardRead.Assert(); + text = Clipboard.GetText(); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + Debug.Fail(ex.ToString()); + return; + } + + PasteInt( text ); + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + if ((NativeMethods.PRF_NONCLIENT & unchecked( (int) (long)m.LParam)) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + using (Pen pen = new Pen(VisualStyleInformation.TextControlBorder)) { + g.DrawRectangle(pen, rect); + } + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// We need to override the WndProc method to have full control over what characters can be + /// displayed in the text box; particularly, we have special handling when IME is turned on. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) + { + // Handle messages for special cases (unsupported operations or cases where mask doesn not matter). + switch (m.Msg) + { + case NativeMethods.WM_PRINT: + WmPrint(ref m); + return; + case NativeMethods.WM_CONTEXTMENU: + case NativeMethods.EM_CANUNDO: + base.ClearUndo(); // resets undo buffer. + base.WndProc(ref m); + return; + + case NativeMethods.EM_SCROLLCARET: // No scroll for single-line control. + case NativeMethods.EM_LIMITTEXT: // Max/Min text is defined by the mask. + case NativeMethods.EM_UNDO: + case NativeMethods.WM_UNDO: + return; + + default: + break; // continue. + } + + if( this.flagState[IS_NULL_MASK]) + { + base.WndProc(ref m); // Operates as a regular text box base. + return; + } + + switch (m.Msg) + { + case NativeMethods.WM_IME_STARTCOMPOSITION: + if( WmImeStartComposition() ) + { + break; + } + goto default; + + case NativeMethods.WM_IME_ENDCOMPOSITION: + this.flagState[IME_ENDING_COMPOSITION] = true; + goto default; + + case NativeMethods.WM_IME_COMPOSITION: + if( WmImeComposition( ref m ) ) + { + break; + } + goto default; + + case NativeMethods.WM_CUT: + if (!this.ReadOnly && WmCopy()) + { + WmClear(); + } + break; + + case NativeMethods.WM_COPY: + WmCopy(); + break; + + case NativeMethods.WM_PASTE: + WmPaste(); + break; + + case NativeMethods.WM_CLEAR: + WmClear(); + break; + + case NativeMethods.WM_KILLFOCUS: + base.WndProc(ref m); + WmKillFocus(); + break; + + case NativeMethods.WM_SETFOCUS: + WmSetFocus(); + base.WndProc(ref m); + break; + + default: + base.WndProc(ref m); + break; + } + } + + /// + /// Processes the WM_KILLFOCUS message. Updates control's text replacing promp chars with space. + /// + private void WmKillFocus() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + base.GetSelectionStartAndLength( out this.caretTestPos, out this.lastSelLength ); + + if (this.HidePromptOnLeave && !this.MaskFull) + { + SetWindowText(); // Update text w/ no prompt. + + // We need to update selection info in case the control is queried for it while it doesn't have the focus. + base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); + } + } + + /// + /// Processes the WM_SETFOCUS message. Updates control's text with formatted text according to + /// the include prompt property. + /// + private void WmSetFocus() + { + Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); + + if (this.HidePromptOnLeave && !this.MaskFull) // Prompt will show up. + { + SetWindowText(); + } + + // Restore previous selection. Do this always (as opposed to within the condition above as in WmKillFocus) + // because HidePromptOnLeave could have changed while the control did not have the focus. + base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MdiWindowListItemConverter.cs b/WindowsForms/Managed/System/WinForms/MdiWindowListItemConverter.cs new file mode 100644 index 000000000..5cfd7db1d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MdiWindowListItemConverter.cs @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Collections; + + internal class MdiWindowListItemConverter : ComponentConverter { + public MdiWindowListItemConverter(Type type) : base(type) { + } + + /// + /// + /// + /// Gets a collection of standard values for the data type this validator is + /// designed for. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + MenuStrip menu = context.Instance as MenuStrip; + if (menu != null) + { + StandardValuesCollection values = base.GetStandardValues(context); + ArrayList list = new ArrayList(); + int count = values.Count; + for (int i=0; i +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security; + using System.Globalization; + + /// this is the menu that merges into the MdiWindowListItem + /// in an MDI parent when an MDI child is maximized. + /// + internal class MdiWindowListStrip : MenuStrip { + + private Form mdiParent = null; + private ToolStripMenuItem mergeItem; + private MenuStrip mergedMenu; + + public MdiWindowListStrip() { + } + + protected override void Dispose(bool disposing) { + if (disposing) { + mdiParent = null; + } + base.Dispose(disposing); + } + + internal ToolStripMenuItem MergeItem { + get { + if (mergeItem == null) { + mergeItem = new ToolStripMenuItem(); + mergeItem.MergeAction = MergeAction.MatchOnly; + } + + if (mergeItem.Owner == null) { + this.Items.Add(mergeItem); + } + return mergeItem; + } + } + + internal MenuStrip MergedMenu { + get { + return mergedMenu; + } + set { + mergedMenu = value; + } + } + + /// Given a form, the items on this toolstrip populate with the mdi children + /// with mnemonics 1-9 and More Windows menu item. + /// These items can then be merged into a menustrip. + /// + /// Based on similar code in MenuItem.cs::PopulateMdiList(), which is unfortunately just different + /// enough in its working environment that we can't readily combine the two. + /// But if you're fixing something here, chances are that the same issue will need scrutiny over there. + /// + public void PopulateItems(Form mdiParent, ToolStripMenuItem mdiMergeItem, bool includeSeparator) { + this.mdiParent = mdiParent; + this.SuspendLayout(); + MergeItem.DropDown.SuspendLayout(); + try { + ToolStripMenuItem mergeItem = MergeItem; + mergeItem.DropDownItems.Clear(); + mergeItem.Text = mdiMergeItem.Text; + + Form[] forms = mdiParent.MdiChildren; + if(forms != null && forms.Length != 0){ + + if(includeSeparator) { + ToolStripSeparator separator = new ToolStripSeparator(); + separator.MergeAction = MergeAction.Append; + separator.MergeIndex = -1; + mergeItem.DropDownItems.Add(separator); + } + + Form activeMdiChild = mdiParent.ActiveMdiChild; + + const int maxMenuForms = 9; // max number of Window menu items for forms + int visibleChildren = 0; // number of visible child forms (so we know if we need to show More Windows... + int accel = 1; // prefix the form name with this digit, underlined, as an accelerator + int formsAddedToMenu = 0; + bool activeFormAdded = false; + + for (int i = 0; i < forms.Length; i++) { + // we need to check close reason here because we could be getting called + // here in the midst of a WM_CLOSE - WM_MDIDESTROY eventually fires a WM_MDIACTIVATE + if (forms[i].Visible && (forms[i].CloseReason == CloseReason.None)) { + visibleChildren++; + if ((activeFormAdded && (formsAddedToMenu < maxMenuForms)) || // don't exceed max + (!activeFormAdded && (formsAddedToMenu < (maxMenuForms-1)) || // save room for active if it's not in yet + (forms[i].Equals(activeMdiChild)))){ // there's always room for activeMdiChild + string text = WindowsFormsUtils.EscapeTextWithAmpersands(mdiParent.MdiChildren[i].Text); + text = (text == null) ? String.Empty : text; + ToolStripMenuItem windowListItem = new ToolStripMenuItem(mdiParent.MdiChildren[i]); + windowListItem.Text = String.Format(CultureInfo.CurrentCulture, "&{0} {1}", accel, text); + windowListItem.MergeAction = MergeAction.Append; + windowListItem.MergeIndex = accel; + windowListItem.Click += new EventHandler(OnWindowListItemClick); + if (forms[i].Equals(activeMdiChild)) { // if this the active one, check it off. + windowListItem.Checked = true; + activeFormAdded = true; + } + accel++; + formsAddedToMenu++; + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose,"\tPopulateItems: Added " + windowListItem.Text); + mergeItem.DropDownItems.Add(windowListItem); + } + } + } + + // show the More Windows... item if necessary. + if (visibleChildren > maxMenuForms) { + ToolStripMenuItem moreWindowsMenuItem = new ToolStripMenuItem(); + moreWindowsMenuItem.Text = SR.GetString(SR.MDIMenuMoreWindows); + Debug.WriteLineIf(ToolStrip.MDIMergeDebug.TraceVerbose, "\tPopulateItems: Added " + moreWindowsMenuItem.Text); + moreWindowsMenuItem.Click += new EventHandler(OnMoreWindowsMenuItemClick); + moreWindowsMenuItem.MergeAction = MergeAction.Append; + mergeItem.DropDownItems.Add(moreWindowsMenuItem); + } + } + } + finally { + // this is an invisible toolstrip dont even bother doing layout. + this.ResumeLayout(false); + MergeItem.DropDown.ResumeLayout(false); + } + } + + /// handler for More Windows... This is similar to MenuItem.cs + private void OnMoreWindowsMenuItemClick(object sender, EventArgs e) { + + Form[] forms = mdiParent.MdiChildren; + + if (forms != null) { + // SECREVIEW : "System" style dialog, no user code will execute, and + // : we don't want the restricted dialog options... + // + IntSecurity.AllWindows.Assert(); + try { + using (MdiWindowDialog dialog = new MdiWindowDialog()) { + dialog.SetItems(mdiParent.ActiveMdiChild, forms); + DialogResult result = dialog.ShowDialog(); + if (result == DialogResult.OK) { + + // AllWindows Assert above allows this... + // + dialog.ActiveChildForm.Activate(); + if (dialog.ActiveChildForm.ActiveControl != null && !dialog.ActiveChildForm.ActiveControl.Focused) { + dialog.ActiveChildForm.ActiveControl.Focus(); + } + } + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// handler for 1 - 9. This is similar to MenuItem.cs + private void OnWindowListItemClick(object sender, EventArgs e) { + ToolStripMenuItem windowListItem = sender as ToolStripMenuItem; + + if (windowListItem != null) { + Form boundForm = windowListItem.MdiForm; + + if (boundForm != null) { + // SECREVIEW : User selected a window, that means it is OK + // : to move focus + // + IntSecurity.ModifyFocus.Assert(); + try { + boundForm.Activate(); + if (boundForm.ActiveControl != null && !boundForm.ActiveControl.Focused) { + boundForm.ActiveControl.Focus(); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MeasureItemEvent.cs b/WindowsForms/Managed/System/WinForms/MeasureItemEvent.cs new file mode 100644 index 000000000..46c7e7f62 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MeasureItemEvent.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// This event is sent by controls such as the ListBox or ComboBox that need users + /// to tell them how large a given item is to be. + /// + public class MeasureItemEventArgs : EventArgs { + + private int itemHeight; + private int itemWidth; + private int index; + + private readonly System.Drawing.Graphics graphics; + + + /// + /// + /// [To be supplied.] + /// + public MeasureItemEventArgs(Graphics graphics, int index, int itemHeight) { + this.graphics = graphics; + this.index = index; + this.itemHeight = itemHeight; + this.itemWidth = 0; + } + + /// + /// + /// [To be supplied.] + /// + public MeasureItemEventArgs(Graphics graphics, int index) { + this.graphics = graphics; + this.index = index; + this.itemHeight = 0; + this.itemWidth = 0; + } + + /// + /// + /// A Graphics object to measure relative to. + /// + public System.Drawing.Graphics Graphics { + get { + return graphics; + } + } + + /// + /// + /// The index of item for which the height/width is needed. + /// + public int Index { + get { + return index; + } + } + + /// + /// + /// Where the recipient of the event should put the height of the + /// item specified by the index. + /// + public int ItemHeight { + get { + return itemHeight; + } + set { + itemHeight = value; + } + } + + /// + /// + /// Where the recipient of the event should put the width of the + /// item specified by the index. + /// + public int ItemWidth { + get { + return itemWidth; + } + set { + itemWidth = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MeasureItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/MeasureItemEventHandler.cs new file mode 100644 index 000000000..42d77f8c3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MeasureItemEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void MeasureItemEventHandler(object sender, MeasureItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/Menu.cs b/WindowsForms/Managed/System/WinForms/Menu.cs new file mode 100644 index 000000000..8f26cc6cf --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Menu.cs @@ -0,0 +1,1175 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security.Permissions; + using System.Collections; + using System.Windows.Forms.Design; + using System.Drawing; + using Microsoft.Win32; + using System.Security; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// This is the base class for all menu components (MainMenu, MenuItem, and ContextMenu). + /// + [ + ToolboxItemFilter("System.Windows.Forms"), + ListBindable(false) + ] + public abstract class Menu : Component { + internal const int CHANGE_ITEMS = 0; // item(s) added or removed + internal const int CHANGE_VISIBLE = 1; // item(s) hidden or shown + internal const int CHANGE_MDI = 2; // mdi item changed + internal const int CHANGE_MERGE = 3; // mergeType or mergeOrder changed + internal const int CHANGE_ITEMADDED = 4; // mergeType or mergeOrder changed + + /// + /// + /// Used by findMenuItem + /// + /// + public const int FindHandle = 0; + /// + /// + /// Used by findMenuItem + /// + /// + public const int FindShortcut = 1; + + private MenuItemCollection itemsCollection; + internal MenuItem[] items; + private int _itemCount; + internal IntPtr handle; + internal bool created; + private object userData; + private string name; + + /// + /// + /// This is an abstract class. Instances cannot be created, so the constructor + /// is only called from derived classes. + /// + /// + protected Menu(MenuItem[] items) { + if (items != null) { + MenuItems.AddRange(items); + } + } + + /// + /// + /// The HMENU handle corresponding to this menu. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHandleDescr) + ] + public IntPtr Handle { + get { + if (handle == IntPtr.Zero) handle = CreateMenuHandle(); + CreateMenuItems(); + return handle; + } + } + + /// + /// + /// Specifies whether this menu contains any items. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MenuIsParentDescr) + ] + public virtual bool IsParent { + [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + get { + return null != items && ItemCount > 0; + } + } + + internal int ItemCount { + get { + return _itemCount; + } + } + + /// + /// + /// The MenuItem that contains the list of MDI child windows. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MenuMDIListItemDescr) + ] + public MenuItem MdiListItem { + get { + for (int i = 0; i < ItemCount; i++) { + MenuItem item = items[i]; + if (item.MdiList) + return item; + if (item.IsParent) { + item = item.MdiListItem; + if (item != null) return item; + } + } + return null; + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control - however this + /// property has no bearing on the runtime aspects of this control. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this, name); + } + set { + if (value == null || value.Length == 0) { + name = null; + } + else { + name = value; + } + if(Site!= null) { + Site.Name = name; + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.MenuMenuItemsDescr), + MergableProperty(false) + ] + public MenuItemCollection MenuItems { + get { + if (itemsCollection == null) { + itemsCollection = new MenuItemCollection(this); + } + return itemsCollection; + } + } + + internal virtual bool RenderIsRightToLeft { + get { + Debug.Assert(true, "Should never get called"); + return false; + + } + } + + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Notifies Menu that someone called Windows.DeleteMenu on its handle. + /// + /// + internal void ClearHandles() { + if (handle != IntPtr.Zero) { + UnsafeNativeMethods.DestroyMenu(new HandleRef(this, handle)); + } + handle = IntPtr.Zero; + if (created) { + for (int i = 0; i < ItemCount; i++) { + items[i].ClearHandles(); + } + created = false; + } + } + + /// + /// + /// Sets this menu to be an identical copy of another menu. + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Shipped as is in Everett + ] + protected internal void CloneMenu(Menu menuSrc) { + MenuItem[] newItems = null; + if (menuSrc.items != null) { + int count = menuSrc.MenuItems.Count; + newItems = new MenuItem[count]; + for (int i = 0; i < count; i++) + newItems[i] = menuSrc.MenuItems[i].CloneMenu(); + } + MenuItems.Clear(); + if (newItems != null) { + MenuItems.AddRange(newItems); + } + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + protected virtual IntPtr CreateMenuHandle() { + return UnsafeNativeMethods.CreatePopupMenu(); + } + + /// + /// + /// + /// + internal void CreateMenuItems() { + if (!created) { + for (int i = 0; i < ItemCount; i++) { + items[i].CreateMenuItem(); + } + created = true; + } + } + + /// + /// + /// + /// + internal void DestroyMenuItems() { + if (created) { + for (int i = 0; i < ItemCount; i++) { + items[i].ClearHandles(); + } + while (UnsafeNativeMethods.GetMenuItemCount(new HandleRef(this, handle)) > 0) { + UnsafeNativeMethods.RemoveMenu(new HandleRef(this, handle), 0, NativeMethods.MF_BYPOSITION); + } + created = false; + } + } + + /// + /// + /// Disposes of the component. Call dispose when the component is no longer needed. + /// This method removes the component from its container (if the component has a site) + /// and triggers the dispose event. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + while (ItemCount > 0) { + MenuItem item = items[--_itemCount]; + + // remove the item before we dispose it so it still has valid state + // for undo/redo + // + if (item.Site != null && item.Site.Container != null) { + item.Site.Container.Remove(item); + } + + item.Menu = null; + item.Dispose(); + } + items = null; + } + if (handle != IntPtr.Zero) { + UnsafeNativeMethods.DestroyMenu(new HandleRef(this, handle)); + this.handle = IntPtr.Zero; + if (disposing) { + ClearHandles(); + } + } + base.Dispose(disposing); + } + + /// + /// + /// + /// + public MenuItem FindMenuItem(int type, IntPtr value) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ControlFromHandleOrLocation Demanded"); + IntSecurity.ControlFromHandleOrLocation.Demand(); + return FindMenuItemInternal(type, value); + } + + private MenuItem FindMenuItemInternal(int type, IntPtr value) { + for (int i = 0; i < ItemCount; i++) { + MenuItem item = items[i]; + switch (type) { + case FindHandle: + if (item.handle == value) return item; + break; + case FindShortcut: + if (item.Shortcut == (Shortcut)(int)value) return item; + break; + } + item = item.FindMenuItemInternal(type, value); + if (item != null) return item; + } + return null; + } + + /// + /// + /// + /// + protected int FindMergePosition(int mergeOrder) { + int iMin, iLim, iT; + + for (iMin = 0, iLim = ItemCount; iMin < iLim;) { + iT = (iMin + iLim) / 2; + if (items[iT].MergeOrder <= mergeOrder) + iMin = iT + 1; + else + iLim = iT; + } + return iMin; + } + + /// + /// + /// + /// + // VSWhidbey 94987: A new method for finding the approximate merge position. The original + // method assumed (incorrectly) that the MergeOrder of the target menu would be sequential + // as it's guaranteed to be in the MDI imlementation of merging container and child + // menus. However, user code can call MergeMenu independently on a source and target + // menu whose MergeOrder values are not necessarily pre-sorted. + internal int xFindMergePosition(int mergeOrder) { + int nPosition = 0; + + // Iterate from beginning to end since we can't assume any sequential ordering to MergeOrder + for (int nLoop = 0; nLoop < ItemCount; nLoop++) { + + if (items[nLoop].MergeOrder > mergeOrder) { + // We didn't find what we're looking for, but we've found a stopping point. + break; + } + else if (items[nLoop].MergeOrder < mergeOrder) { + // We might have found what we're looking for, but we'll have to come around again + // to know. + nPosition = nLoop + 1; + } + else if (mergeOrder == items[nLoop].MergeOrder) { + // We've found what we're looking for, so use this value for the merge order + nPosition = nLoop; + break; + } + } + + return nPosition; + } + + //There's a win32 problem that doesn't allow menus to cascade right to left + //unless we explicitely set the bit on the menu the first time it pops up + internal void UpdateRtl(bool setRightToLeftBit) { + foreach (MenuItem item in MenuItems) { + item.UpdateItemRtl(setRightToLeftBit); + item.UpdateRtl(setRightToLeftBit); + } + } + + /// + /// + /// Returns the ContextMenu that contains this menu. The ContextMenu + /// is at the top of this menu's parent chain. + /// Returns null if this menu is not contained in a ContextMenu. + /// This can occur if it's contained in a MainMenu or if it isn't + /// currently contained in any menu at all. + /// + public ContextMenu GetContextMenu() { + Menu menuT; + for (menuT = this; !(menuT is ContextMenu);) { + if (!(menuT is MenuItem)) return null; + menuT = ((MenuItem)menuT).Menu; + } + return(ContextMenu)menuT; + + } + + /// + /// + /// Returns the MainMenu item that contains this menu. The MainMenu + /// is at the top of this menu's parent chain. + /// Returns null if this menu is not contained in a MainMenu. + /// This can occur if it's contained in a ContextMenu or if it isn't + /// currently contained in any menu at all. + /// + public MainMenu GetMainMenu() { + Menu menuT; + for (menuT = this; !(menuT is MainMenu);) { + if (!(menuT is MenuItem)) return null; + menuT = ((MenuItem)menuT).Menu; + } + return(MainMenu)menuT; + } + + /// + /// + /// + /// + internal virtual void ItemsChanged(int change) { + switch (change) { + case CHANGE_ITEMS: + case CHANGE_VISIBLE: + DestroyMenuItems(); + break; + } + } + + /// + /// + /// Walks the menu item collection, using a caller-supplied delegate to find one + /// with a matching access key. Walk starts at specified item index and performs one + /// full pass of the entire collection, looping back to the top if necessary. + /// + /// Return value is intended for return from WM_MENUCHAR message. It includes both + /// index of matching item, and action for OS to take (execute or select). Zero is + /// used to indicate that no match was found (OS should ignore key and beep). + /// + /// + private IntPtr MatchKeyToMenuItem(int startItem, char key, MenuItemKeyComparer comparer) { + int firstMatch = -1; + bool multipleMatches = false; + + for (int i = 0; i < items.Length && !multipleMatches; ++i) { + int itemIndex = (startItem + i) % items.Length; + MenuItem mi = items[itemIndex]; + if (mi != null && comparer(mi, key)) { + if (firstMatch < 0){ + // VSWhidbey 218021 using Index doesnt respect hidden items. + firstMatch = mi.MenuIndex; + } + else { + multipleMatches = true; + } + } + } + + if (firstMatch < 0) + return IntPtr.Zero; + + int action = multipleMatches ? NativeMethods.MNC_SELECT : NativeMethods.MNC_EXECUTE; + return (IntPtr) NativeMethods.Util.MAKELONG(firstMatch, action); + } + + /// Delegate type used by MatchKeyToMenuItem + private delegate bool MenuItemKeyComparer(MenuItem mi, char key); + + /// + /// + /// Merges another menu's items with this one's. Menu items are merged according to their + /// mergeType and mergeOrder properties. This function is typically used to + /// merge an MDI container's menu with that of its active MDI child. + /// + [ + SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly") // Shipped as is in Everett + ] + public virtual void MergeMenu(Menu menuSrc) { + int i, j; + MenuItem item; + MenuItem itemDst; + + if (menuSrc == this) + throw new ArgumentException(SR.GetString(SR.MenuMergeWithSelf), "menuSrc"); + + if (menuSrc.items != null && items == null) { + MenuItems.Clear(); + } + + for (i = 0; i < menuSrc.ItemCount; i++) { + item = menuSrc.items[i]; + + switch (item.MergeType) { + default: + continue; + case MenuMerge.Add: + MenuItems.Add(FindMergePosition(item.MergeOrder), item.MergeMenu()); + continue; + case MenuMerge.Replace: + case MenuMerge.MergeItems: + break; + } + + int mergeOrder = item.MergeOrder; + // Can we find a menu item with a matching merge order? + // VSWhidbey 94987: Use new method to find the approximate merge position. The original + // method assumed (incorrectly) that the MergeOrder of the target menu would be sequential + // as it's guaranteed to be in the MDI imlementation of merging container and child + // menus. However, user code can call MergeMenu independently on a source and target + // menu whose MergeOrder values are not necessarily pre-sorted. + for (j = xFindMergePosition(mergeOrder); ; j++) { + + if (j >= ItemCount) { + // A matching merge position could not be found, + // so simply append this menu item to the end. + MenuItems.Add(j, item.MergeMenu()); + break; + } + itemDst = items[j]; + if (itemDst.MergeOrder != mergeOrder) { + MenuItems.Add(j, item.MergeMenu()); + break; + } + if (itemDst.MergeType != MenuMerge.Add) { + if (item.MergeType != MenuMerge.MergeItems + || itemDst.MergeType != MenuMerge.MergeItems) { + itemDst.Dispose(); + MenuItems.Add(j, item.MergeMenu()); + } + else { + itemDst.MergeMenu(item); + } + break; + } + } + } + } + + /// + /// + /// + /// + internal virtual bool ProcessInitMenuPopup(IntPtr handle) { + MenuItem item = FindMenuItemInternal(FindHandle, handle); + if (item != null) { + item._OnInitMenuPopup(EventArgs.Empty); + item.CreateMenuItems(); + return true; + } + return false; + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessCmdKey(ref Message msg, Keys keyData) { + MenuItem item = FindMenuItemInternal(FindShortcut, (IntPtr)(int)keyData); + return item != null? item.ShortcutClick(): false; + } + + /// + /// + /// Returns index of currently selected menu item in + /// this menu, or -1 if no item is currently selected. + /// + /// + internal int SelectedMenuItemIndex { + get { + for (int i = 0; i < items.Length; ++i) { + MenuItem mi = items[i]; + if (mi != null && mi.Selected) + return i; + } + return -1; + } + } + + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Items.Count: " + ItemCount.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// Handles the WM_MENUCHAR message, forwarding it to the intended Menu + /// object. All the real work is done inside WmMenuCharInternal(). + /// + /// + internal void WmMenuChar(ref Message m) { + Menu menu = (m.LParam == handle) ? this : FindMenuItemInternal(FindHandle, m.LParam); + + if (menu == null) + return; + + char menuKey = Char.ToUpper((char) NativeMethods.Util.LOWORD(m.WParam), CultureInfo.CurrentCulture); + + m.Result = menu.WmMenuCharInternal(menuKey); + } + + /// + /// + /// Handles WM_MENUCHAR to provide access key support for owner-draw menu items (which + /// means *all* menu items on a menu when IsImageMarginPresent == true). Attempts to + /// simulate the exact behavior that the OS provides for non owner-draw menu items. + /// + /// + internal IntPtr WmMenuCharInternal(char key) { + // Start looking just beyond the current selected item (otherwise just start at the top) + int startItem = (SelectedMenuItemIndex + 1) % items.Length; + + // First, search for match among owner-draw items with explicitly defined access keys (eg. "S&ave") + IntPtr result = MatchKeyToMenuItem(startItem, key, new MenuItemKeyComparer(CheckOwnerDrawItemWithMnemonic)); + + // Next, search for match among owner-draw items with no access keys (looking at first char of item text) + if (result == IntPtr.Zero) + result = MatchKeyToMenuItem(startItem, key, new MenuItemKeyComparer(CheckOwnerDrawItemNoMnemonic)); + + return result; + } + + /// MenuItemKeyComparer delegate used by WmMenuCharInternal + private bool CheckOwnerDrawItemWithMnemonic(MenuItem mi, char key) { + return mi.OwnerDraw && + mi.Mnemonic == key; + } + + /// MenuItemKeyComparer delegate used by WmMenuCharInternal + private bool CheckOwnerDrawItemNoMnemonic(MenuItem mi, char key) { + return mi.OwnerDraw && + mi.Mnemonic == 0 && + mi.Text.Length > 0 && + Char.ToUpper(mi.Text[0], CultureInfo.CurrentCulture) == key; + } + + /// + /// + /// [To be supplied.] + /// + [ListBindable(false)] + public class MenuItemCollection : IList { + private Menu owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + /// + /// + /// [To be supplied.] + /// + public MenuItemCollection(Menu owner) { + this.owner = owner; + } + + /// + /// + /// [To be supplied.] + /// + public virtual MenuItem this[int index] { + get { + if (index < 0 || index >= owner.ItemCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + return owner.items[index]; + } + // set not supported + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + throw new NotSupportedException(); + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual MenuItem this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + /// [To be supplied.] + /// + public int Count { + get { + return owner.ItemCount; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + + /// + /// + /// Adds a new MenuItem to the end of this menu with the specified caption. + /// + public virtual MenuItem Add(string caption) { + MenuItem item = new MenuItem(caption); + Add(item); + return item; + } + + /// + /// + /// Adds a new MenuItem to the end of this menu with the specified caption, + /// and click handler. + /// + public virtual MenuItem Add(string caption, EventHandler onClick) { + MenuItem item = new MenuItem(caption, onClick); + Add(item); + return item; + } + + /// + /// + /// Adds a new MenuItem to the end of this menu with the specified caption, + /// click handler, and items. + /// + public virtual MenuItem Add(string caption, MenuItem[] items) { + MenuItem item = new MenuItem(caption, items); + Add(item); + return item; + } + + /// + /// + /// Adds a MenuItem to the end of this menu + /// MenuItems can only be contained in one menu at a time, and may not be added + /// more than once to the same menu. + /// + public virtual int Add(MenuItem item) { + return Add(owner.ItemCount, item); + } + + /// + /// + /// Adds a MenuItem to this menu at the specified index. The item currently at + /// that index, and all items after it, will be moved up one slot. + /// MenuItems can only be contained in one menu at a time, and may not be added + /// more than once to the same menu. + /// + public virtual int Add(int index, MenuItem item) { + + // MenuItems can only belong to one menu at a time + if (item.Menu != null) { + + // First check that we're not adding ourself, i.e. walk + // the parent chain for equality + if (owner is MenuItem) { + MenuItem parent = (MenuItem)owner; + while (parent != null) { + if (parent.Equals(item)) { + throw new ArgumentException(SR.GetString(SR.MenuItemAlreadyExists, item.Text), "item"); + } + if (parent.Parent is MenuItem) + parent = (MenuItem)parent.Parent; + else + break; + } + } + + //if we're re-adding an item back to the same collection + //the target index needs to be decremented since we're + //removing an item from the collection + if (item.Menu.Equals(owner) && index > 0) { + index--; + } + + item.Menu.MenuItems.Remove(item); + } + + // Validate our index + if (index < 0 || index > owner.ItemCount) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument,"index",(index).ToString(CultureInfo.CurrentCulture))); + } + + if (owner.items == null || owner.items.Length == owner.ItemCount) { + MenuItem[] newItems = new MenuItem[owner.ItemCount < 2? 4: owner.ItemCount * 2]; + if (owner.ItemCount > 0) System.Array.Copy(owner.items, 0, newItems, 0, owner.ItemCount); + owner.items = newItems; + } + System.Array.Copy(owner.items, index, owner.items, index + 1, owner.ItemCount - index); + owner.items[index] = item; + owner._itemCount++; + item.Menu = owner; + owner.ItemsChanged(CHANGE_ITEMS); + if (owner is MenuItem) { + ((MenuItem) owner).ItemsChanged(CHANGE_ITEMADDED, item); + } + + return index; + } + + /// + /// + /// [To be supplied.] + /// + public virtual void AddRange(MenuItem[] items) { + if (items == null) { + throw new ArgumentNullException("items"); + } + foreach(MenuItem item in items) { + Add(item); + } + } + + /// + /// + int IList.Add(object value) { + if (value is MenuItem) { + return Add((MenuItem)value); + } + else { + throw new ArgumentException(SR.GetString(SR.MenuBadMenuItem), "value"); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(MenuItem value) { + return IndexOf(value) != -1; + } + + /// + /// + bool IList.Contains(object value) { + if (value is MenuItem) { + return Contains((MenuItem)value); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + /// + /// + /// Searches for Controls by their Name property, builds up an array + /// of all the controls that match. + /// + /// + public MenuItem [] Find(string key, bool searchAllChildren) { + + if ((key == null) || (key.Length == 0)) { + throw new System.ArgumentNullException("key", SR.GetString(SR.FindKeyMayNotBeEmptyOrNull)); + } + + + ArrayList foundMenuItems = FindInternal(key, searchAllChildren, this, new ArrayList()); + + // Make this a stongly typed collection. + MenuItem[] stronglyTypedfoundMenuItems = new MenuItem[foundMenuItems.Count]; + foundMenuItems.CopyTo(stronglyTypedfoundMenuItems, 0); + + return stronglyTypedfoundMenuItems; + } + + /// + /// + /// Searches for Controls by their Name property, builds up an array list + /// of all the controls that match. + /// + /// + /// + private ArrayList FindInternal(string key, bool searchAllChildren, MenuItemCollection menuItemsToLookIn, ArrayList foundMenuItems) { + if ((menuItemsToLookIn == null) || (foundMenuItems == null)) { + return null; // + } + + // Perform breadth first search - as it's likely people will want controls belonging + // to the same parent close to each other. + + for (int i = 0; i < menuItemsToLookIn.Count; i++) { + if (menuItemsToLookIn[i] == null){ + continue; + } + + if (WindowsFormsUtils.SafeCompareStrings(menuItemsToLookIn[i].Name, key, /* ignoreCase = */ true)) { + foundMenuItems.Add(menuItemsToLookIn[i]); + } + } + + // Optional recurive search for controls in child collections. + + if (searchAllChildren){ + for (int i = 0; i < menuItemsToLookIn.Count; i++) { + if (menuItemsToLookIn[i] == null){ + continue; + } + if ((menuItemsToLookIn[i].MenuItems != null) && menuItemsToLookIn[i].MenuItems.Count > 0){ + // if it has a valid child collecion, append those results to our collection + foundMenuItems = FindInternal(key, searchAllChildren, menuItemsToLookIn[i].MenuItems, foundMenuItems); + } + } + } + return foundMenuItems; + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(MenuItem value) { + for(int index=0; index < Count; ++index) { + if (this[index] == value) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object value) { + if (value is MenuItem) { + return IndexOf((MenuItem)value); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + void IList.Insert(int index, object value) { + if (value is MenuItem) { + Add(index, (MenuItem)value); + } + else { + throw new ArgumentException(SR.GetString(SR.MenuBadMenuItem),"value"); + } + } + + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + + /// + /// + /// Removes all existing MenuItems from this menu + /// + public virtual void Clear() { + if (owner.ItemCount > 0) { + + for (int i = 0; i < owner.ItemCount; i++) { + owner.items[i].Menu = null; + } + + owner._itemCount = 0; + owner.items = null; + + owner.ItemsChanged(CHANGE_ITEMS); + + if (owner is MenuItem) { + ((MenuItem)(owner)).UpdateMenuItem(true); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + if (owner.ItemCount > 0) { + System.Array.Copy(owner.items, 0, dest, index, owner.ItemCount); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return new WindowsFormsUtils.ArraySubsetEnumerator(owner.items, owner.ItemCount); + } + + /// + /// + /// Removes the item at the specified index in this menu. All subsequent + /// items are moved up one slot. + /// + public virtual void RemoveAt(int index) { + if (index < 0 || index >= owner.ItemCount) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument,"index",(index).ToString(CultureInfo.CurrentCulture))); + } + + MenuItem item = owner.items[index]; + item.Menu = null; + owner._itemCount--; + System.Array.Copy(owner.items, index + 1, owner.items, index, owner.ItemCount - index); + owner.items[owner.ItemCount] = null; + owner.ItemsChanged(CHANGE_ITEMS); + + //if the last item was removed, clear the collection + // + if (owner.ItemCount == 0) { + Clear(); + } + + } + + /// + /// + /// Removes the menu iteml with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// Removes the specified item from this menu. All subsequent + /// items are moved down one slot. + /// + public virtual void Remove(MenuItem item) { + if (item.Menu == owner) { + RemoveAt(item.Index); + } + } + + /// + /// + void IList.Remove(object value) { + if (value is MenuItem) { + Remove((MenuItem)value); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MenuGlyph.cs b/WindowsForms/Managed/System/WinForms/MenuGlyph.cs new file mode 100644 index 000000000..5a7bc29f8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MenuGlyph.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// Enum to be used with the drawMenuGlyph function. + /// + /// + public enum MenuGlyph { + + /// + /// + /// Draws a submenu arrow. + /// + Arrow = NativeMethods.DFCS_MENUARROW, + + /// + /// + /// Draws a menu checkmark. + /// + Checkmark = NativeMethods.DFCS_MENUCHECK, + + /// + /// + /// Draws a menu bullet. + /// + Bullet = NativeMethods.DFCS_MENUBULLET, + + /// + /// + /// [To be supplied.] + /// + Min = NativeMethods.DFCS_MENUARROW, + /// + /// + /// [To be supplied.] + /// + Max = NativeMethods.DFCS_MENUBULLET, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/MenuItem.cs b/WindowsForms/Managed/System/WinForms/MenuItem.cs new file mode 100644 index 000000000..37ebce89c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MenuItem.cs @@ -0,0 +1,1860 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Configuration.Assemblies; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Collections; + + using System.ComponentModel; + using System.Windows.Forms.Design; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using System.Drawing.Text; + using System.Drawing.Imaging; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + using System.Threading; + + /// + /// + /// + /// Represents an individual item that is displayed within + /// a or . + /// + /// + [ + ToolboxItem(false), + DesignTimeVisible(false), + DefaultEvent("Click"), + DefaultProperty("Text") + ] + public class MenuItem : Menu { + internal const int STATE_BARBREAK = 0x00000020; + internal const int STATE_BREAK = 0x00000040; + internal const int STATE_CHECKED = 0x00000008; + internal const int STATE_DEFAULT = 0x00001000; + internal const int STATE_DISABLED = 0x00000003; + internal const int STATE_RADIOCHECK = 0x00000200; + internal const int STATE_HIDDEN = 0x00010000; + internal const int STATE_MDILIST = 0x00020000; + internal const int STATE_CLONE_MASK = 0x0003136B; + internal const int STATE_OWNERDRAW = 0x00000100; + internal const int STATE_INMDIPOPUP = 0x00000200; + internal const int STATE_HILITE = 0x00000080; + + private Menu menu; + private bool hasHandle; + private MenuItemData data; + private int dataVersion; + private MenuItem nextLinkedItem; // Next item linked to the same MenuItemData. + + // We need to store a table of all created menuitems, so that other objects + // such as ContainerControl can get a reference to a particular menuitem, + // given a unique ID. + private static Hashtable allCreatedMenuItems = new Hashtable(); + private const uint firstUniqueID = 0xC0000000; + private static long nextUniqueID = firstUniqueID; + private uint uniqueID = 0; + private IntPtr msaaMenuInfoPtr = IntPtr.Zero; + private bool menuItemIsCreated = false; + + /// + /// + /// + /// Initializes a with + /// a blank caption. + /// + /// + public MenuItem() : this(MenuMerge.Add, 0, 0, null, null, null, null, null) { + } + + /// + /// + /// + /// Initializes a new instance of the + /// class with a specified caption for + /// the menu item. + /// + /// + public MenuItem(string text) : this(MenuMerge.Add, 0, 0, text, null, null, null, null) { + } + + /// + /// + /// + /// Initializes a new instance of the + /// class with a + /// specified caption and + /// event handler for the menu item. + /// + /// + public MenuItem(string text, EventHandler onClick) : this(MenuMerge.Add, 0, 0, text, onClick, null, null, null) { + } + + /// + /// + /// + /// Initializes a new instance of the + /// class with a + /// specified caption, event handler, and associated + /// shorcut key for the menu item. + /// + /// + public MenuItem(string text, EventHandler onClick, Shortcut shortcut) : this(MenuMerge.Add, 0, shortcut, text, onClick, null, null, null) { + } + + /// + /// + /// + /// Initializes a new instance of the + /// class with a + /// specified caption and an array of + /// submenu items defined for the menu item. + /// + /// + public MenuItem(string text, MenuItem[] items) : this(MenuMerge.Add, 0, 0, text, null, null, null, items) { + } + + internal MenuItem(MenuItemData data) + : base(null) { + data.AddItem(this); + + #if DEBUG + _debugText = data.caption; + #endif + } + + /// + /// + /// + /// Initializes a new instance of the class with a specified + /// caption, defined event-handlers for the Click, Select and + /// Popup events, a shortcut key, + /// a merge type, and order specified for the menu item. + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + public MenuItem(MenuMerge mergeType, int mergeOrder, Shortcut shortcut, + string text, EventHandler onClick, EventHandler onPopup, + EventHandler onSelect, MenuItem[] items) + + : base(items) { + + new MenuItemData(this, mergeType, mergeOrder, shortcut, true, + text, onClick, onPopup, onSelect, null, null); + + #if DEBUG + _debugText = text; + _creationNumber = CreateCount++; + #endif + } + + #if DEBUG + private string _debugText; + private int _creationNumber; + private Menu _debugParentMenu; + private static int CreateCount; + #endif + + + /// + /// + /// + /// Gets or sets a value indicating whether the item is + /// placed on a new line (for a menu item added to a object) or in a new + /// column (for a submenu or menu displayed in a + /// ). + /// + /// + [ + Browsable(false), + DefaultValue(false) + ] + public bool BarBreak { + get { + return(data.State & STATE_BARBREAK) != 0; + } + + set { + data.SetState(STATE_BARBREAK, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the item is + /// placed on a new line (for a menu item added to a object) or in a new column (for a + /// submenu or menu displayed in a ). + /// + /// + [ + Browsable(false), + DefaultValue(false) + ] + public bool Break { + get { + return(data.State & STATE_BREAK) != 0; + } + + set { + data.SetState(STATE_BREAK, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether a checkmark + /// appears beside the text of the menu item. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.MenuItemCheckedDescr) + ] + public bool Checked { + get { + return(data.State & STATE_CHECKED) != 0; + } + + set { + //if trying to set checked=true - if we're a top-level item (from a mainmenu) or have children, don't do this... + if (value == true && (ItemCount != 0 || (Parent != null && (Parent is MainMenu)))) { + throw new ArgumentException(SR.GetString(SR.MenuItemInvalidCheckProperty)); + } + + data.SetState(STATE_CHECKED, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating + /// whether the menu item is the default. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.MenuItemDefaultDescr) + ] + public bool DefaultItem { + get { return(data.State & STATE_DEFAULT) != 0;} + set { + if (menu != null) { + if (value) { + UnsafeNativeMethods.SetMenuDefaultItem(new HandleRef(menu, menu.handle), MenuID, false); + } + else if (DefaultItem) { + UnsafeNativeMethods.SetMenuDefaultItem(new HandleRef(menu, menu.handle), -1, false); + } + } + data.SetState(STATE_DEFAULT, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether code + /// that you provide draws the menu item or Windows draws the + /// menu item. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.MenuItemOwnerDrawDescr) + ] + public bool OwnerDraw { + get { + return((data.State & STATE_OWNERDRAW) != 0); + } + set { + data.SetState(STATE_OWNERDRAW, value); + } + + } + + /// + /// + /// + /// Gets or sets a value indicating whether the menu + /// item is enabled. + /// + /// + [ + Localizable(true), + DefaultValue(true), + SRDescription(SR.MenuItemEnabledDescr) + ] + public bool Enabled { + get { + return(data.State & STATE_DISABLED) == 0; + } + + set { + data.SetState(STATE_DISABLED, !value); + } + } + + /// + /// + /// + /// Gets or sets the menu item's position in its parent menu. + /// + /// + [ + Browsable(false), + ] + public int Index { + get { + if (menu != null) { + for (int i = 0; i < menu.ItemCount; i++) { + if (menu.items[i] == this) return i; + } + } + return -1; + } + + set { + int oldIndex = Index; + if (oldIndex >= 0) { + if (value < 0 || value >= menu.ItemCount) { + throw new ArgumentOutOfRangeException("Index", SR.GetString(SR.InvalidArgument, "Index", (value).ToString(CultureInfo.CurrentCulture))); + } + + if (value != oldIndex) { + // this.menu reverts to null when we're removed, so hold onto it in a local variable + Menu parent = menu; + parent.MenuItems.RemoveAt(oldIndex); + parent.MenuItems.Add(value, this); + } + } + } + } + + /// + /// + /// + /// Gets a value indicating whether the menu item contains + /// child menu items. + /// + /// + [ + Browsable(false), + ] + public override bool IsParent { + get { + bool parent = false; + if (data != null && MdiList) { + for (int i=0; i 0) { + parent = true; + } + } + if (!parent) { + if (menu != null && !(menu is MenuItem)) { + parent = true; + } + } + } + else { + parent = base.IsParent; + } + return parent; + } + } + + /// + /// + /// + /// Gets or sets + /// a value indicating whether the menu item will be populated + /// with a list of the MDI child windows that are displayed within the + /// associated form. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.MenuItemMDIListDescr) + ] + public bool MdiList { + get { + return(data.State & STATE_MDILIST) != 0; + } + set { + data.MdiList = value; + CleanListItems(this); + } + } + + internal Menu Menu { + get { + return menu; + } + set { + menu = value; + #if DEBUG + _debugParentMenu = value; + #endif + } + } + + /// + /// + /// + /// Gets the Windows identifier for this menu item. + /// + /// + protected int MenuID { + get { return data.GetMenuID();} + } + + /// + /// + /// Is this menu item currently selected (highlighted) by the user? + /// + internal bool Selected { + get { + if (menu == null) + return false; + + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + info.fMask = NativeMethods.MIIM_STATE; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, info); + + return (info.fState & STATE_HILITE) != 0; + } + } + + + /// + /// + /// + /// Gets the zero-based index of this menu + /// item in the parent menu, or -1 if this + /// menu item is not associated with a + /// parent menu. + /// + /// + internal int MenuIndex { + get { + if (menu == null) return -1; + + int count = UnsafeNativeMethods.GetMenuItemCount(new HandleRef(menu, menu.Handle)); + int id = MenuID; + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + info.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_SUBMENU; + + for(int i = 0; i < count; i++) { + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), i, true, info); + + // For sub menus, the handle is always valid. For + // items, however, it is always zero. + // + if ((info.hSubMenu == IntPtr.Zero || info.hSubMenu == Handle) && info.wID == id) { + return i; + } + } + return -1; + } + } + + /// + /// + /// + /// Gets or sets a value that indicates the behavior of this + /// menu item when its menu is merged with another. + /// + /// + /// + [ + DefaultValue(MenuMerge.Add), + SRDescription(SR.MenuItemMergeTypeDescr) + ] + public MenuMerge MergeType { + get { + return data.mergeType; + } + set { + + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)MenuMerge.Add, (int)MenuMerge.Remove)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(MenuMerge)); + } + + data.MergeType = value; + } + } + + /// + /// + /// + /// Gets or sets the relative position the menu item when its + /// menu is merged with another. + /// + /// + /// + [ + DefaultValue(0), + SRDescription(SR.MenuItemMergeOrderDescr) + ] + public int MergeOrder { + get { + return data.mergeOrder; + } + set { + data.MergeOrder = value; + } + } + + /// + /// + /// + /// Retrieves the hotkey mnemonic that is associated with this menu item. + /// The mnemonic is the first character after an ampersand symbol in the menu's text + /// that is not itself an ampersand symbol. If no such mnemonic is defined this + /// will return zero. + /// + /// + [Browsable(false)] + public char Mnemonic { + get { + return data.Mnemonic; + } + } + + /// + /// + /// + /// Gets the menu in which this menu item + /// appears. + /// + /// + [Browsable(false)] + public Menu Parent { + get {return menu;} + } + + /// + /// + /// + /// Gets or sets a value that indicates whether the menu item, + /// if checked, displays a radio-button mark instead of a check mark. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.MenuItemRadioCheckDescr) + ] + public bool RadioCheck { + get { + return(data.State & STATE_RADIOCHECK) != 0; + } + set { + data.SetState(STATE_RADIOCHECK, value); + } + } + + internal override bool RenderIsRightToLeft { + get { + if(Parent == null) + return false; + else + return Parent.RenderIsRightToLeft; + } + } + + /// + /// + /// + /// Gets or sets the text of the menu item. + /// + /// + [ + Localizable(true), + SRDescription(SR.MenuItemTextDescr) + ] + public string Text { + get { + return data.caption; + } + set { + data.SetCaption(value); + } + } + + /// + /// + /// + /// Gets or sets the shortcut key associated with the menu + /// item. + /// + /// + [ + Localizable(true), + DefaultValue(Shortcut.None), + SRDescription(SR.MenuItemShortCutDescr) + ] + public Shortcut Shortcut { + get { + return data.shortcut; + } + [SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")] + set { + if (!Enum.IsDefined(typeof(Shortcut), value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(Shortcut)); + } + + data.shortcut = value; + UpdateMenuItem(true); + } + } + + /// + /// + /// + /// Gets or sets a value that indicates whether the shortcut + /// key that is assocaited + /// with the menu item is displayed next to the menu item + /// caption. + /// + /// + [ + DefaultValue(true), + Localizable(true), + SRDescription(SR.MenuItemShowShortCutDescr) + ] + public bool ShowShortcut { + get { + + return data.showShortcut; + } + set { + if (value != data.showShortcut) { + data.showShortcut = value; + UpdateMenuItem(true); + } + } + } + + /// + /// + /// + /// Gets or sets a value that indicates + /// whether the menu item is visible on its parent menu. + /// + /// + [ + Localizable(true), + DefaultValue(true), + SRDescription(SR.MenuItemVisibleDescr) + ] + public bool Visible { + get { + return(data.State & STATE_HIDDEN) == 0; + } + set { + data.Visible = value; + } + } + + /// + /// + /// + /// Occurs when the menu item is clicked or selected using a + /// shortcut key defined for the menu item. + /// + /// + [SRDescription(SR.MenuItemOnClickDescr)] + public event EventHandler Click { + add { + data.onClick += value; + } + remove { + data.onClick -= value; + } + } + + /// + /// + /// + /// Occurs when when the property of a menu item is set + /// to + /// + /// and a request is made to draw the menu item. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + data.onDrawItem += value; + } + remove { + data.onDrawItem -= value; + } + } + + /// + /// + /// + /// Occurs when when the menu needs to know the size of a + /// menu item before drawing it. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] + public event MeasureItemEventHandler MeasureItem { + add { + data.onMeasureItem += value; + } + remove { + data.onMeasureItem -= value; + } + } + + /* + private bool ParentIsRightToLeft { + get { + Menu parent = GetMainMenu(); + if (parent != null) { + if (parent is MenuItem && ((MainMenu)parent).RightToLeft == RightToLeft.Inherit) { + // recursivly go up the chain + return ((MenuItem)parent).ParentIsRightToLeft; + } else { + + return((MainMenu)parent).RightToLeft == RightToLeft.Yes; + } + } + else { + parent = GetContextMenu(); + if (parent != null) { + if (parent is MenuItem && ((ContextMenu)parent).RightToLeft == RightToLeft.Inherit) { + // recursivly go up the chain + return ((MenuItem)parent).ParentIsRightToLeft; + } else { + return((ContextMenu)parent).RightToLeft == RightToLeft.Yes; + } + } + } + + return false; + } + } */ + + /// + /// + /// + /// Occurs before a menu item's list of menu items is + /// displayed. + /// + /// + [SRDescription(SR.MenuItemOnInitDescr)] + public event EventHandler Popup { + add { + data.onPopup += value; + } + remove { + data.onPopup -= value; + } + } + + /// + /// + /// + /// Occurs when the user hovers their mouse over a menu + /// item + /// or selects it with the keyboard but has not activated it. + /// + /// + [SRDescription(SR.MenuItemOnSelectDescr)] + public event EventHandler Select { + add { + data.onSelect += value; + } + remove { + data.onSelect -= value; + } + } + + private static void CleanListItems(MenuItem senderMenu) { + + // remove dynamic items. + + for (int i = senderMenu.MenuItems.Count - 1; i >= 0; i--) { + MenuItem item = senderMenu.MenuItems[i]; + if (item.data.UserData is MdiListUserData) { + // this is a dynamic item. clean it up! + // + item.Dispose(); + continue; + } + } + } + + /// + /// + /// + /// Creates and returns an identical copy of this menu item. + /// + /// + public virtual MenuItem CloneMenu() { + MenuItem newItem = new MenuItem(); + newItem.CloneMenu(this); + return newItem; + } + + /// + /// + /// + /// Creates a copy of the specified menu item. + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1806:DoNotIgnoreMethodResults")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] // Shipped in Everett + protected void CloneMenu(MenuItem itemSrc) { + base.CloneMenu(itemSrc); + int state = itemSrc.data.State; + new MenuItemData(this, + itemSrc.MergeType, itemSrc.MergeOrder, itemSrc.Shortcut, itemSrc.ShowShortcut, + itemSrc.Text, itemSrc.data.onClick, itemSrc.data.onPopup, itemSrc.data.onSelect, + itemSrc.data.onDrawItem, itemSrc.data.onMeasureItem); + data.SetState(state & STATE_CLONE_MASK, true); + } + + internal virtual void CreateMenuItem() { + if ((data.State & STATE_HIDDEN) == 0) { + NativeMethods.MENUITEMINFO_T info = CreateMenuItemInfo(); + UnsafeNativeMethods.InsertMenuItem(new HandleRef(menu, menu.handle), -1, true, info); + + hasHandle = info.hSubMenu != IntPtr.Zero; + dataVersion = data.version; + + menuItemIsCreated = true; + if(RenderIsRightToLeft) { + Menu.UpdateRtl(true); + } + +#if DEBUG + NativeMethods.MENUITEMINFO_T infoVerify = new NativeMethods.MENUITEMINFO_T(); + infoVerify.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + infoVerify.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, infoVerify); +#endif + } + } + + private NativeMethods.MENUITEMINFO_T CreateMenuItemInfo() { + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE | NativeMethods.MIIM_DATA; + info.fType = data.State & (STATE_BARBREAK | STATE_BREAK | STATE_RADIOCHECK | STATE_OWNERDRAW); + + // V7#646 - Top level menu items shouldn't have barbreak or break + // bits set on them... + // + bool isTopLevel = false; + if (menu == GetMainMenu()) { + isTopLevel = true; + } + + if (data.caption.Equals("-")) { + if (isTopLevel) { + data.caption = " "; + info.fType |= NativeMethods.MFT_MENUBREAK; + } + else { + info.fType |= NativeMethods.MFT_SEPARATOR; + } + } + + + info.fState = data.State & (STATE_CHECKED | STATE_DEFAULT | STATE_DISABLED); + + info.wID = MenuID; + if (IsParent) { + info.hSubMenu = Handle; + } + info.hbmpChecked = IntPtr.Zero; + info.hbmpUnchecked = IntPtr.Zero; + + // Assign a unique ID to this menu item object... + // The ID is stored in the dwItemData of the corresponding Win32 menu item, so that when we get Win32 + // messages about the item later, we can delegate to the original object menu item object. A static + // hash table is used to map IDs to menu item objects. + // + if (uniqueID == 0) { + lock(allCreatedMenuItems) { + uniqueID = (uint)Interlocked.Increment(ref nextUniqueID); + Debug.Assert(uniqueID >= firstUniqueID); // ...check for ID range exhaustion (unlikely!) + // We add a weak ref wrapping a MenuItem to the static hash table, as supposed to adding the item + // ref itself, to allow the item to be finalized in case it is not disposed and no longer referenced + // anywhere else, hence preventing leaks. See bug#352644 + allCreatedMenuItems.Add(uniqueID, new WeakReference(this)); + } + } + + // To check it's 32-bit OS or 64-bit OS. + if (IntPtr.Size == 4) { + // Store the unique ID in the dwItemData... + // For simple menu items, we can just put the unique ID in the dwItemData. But for owner-draw items, + // we need to point the dwItemData at an MSAAMENUINFO structure so that MSAA can get the item text. + // To allow us to reliably distinguish between IDs and structure pointers later on, we keep IDs in + // the 0xC0000000-0xFFFFFFFF range. This is the top 1Gb of unmananged process memory, where an app's + // heap allocations should never come from. So that we can still get the ID from the dwItemData for + // an owner-draw item later on, a copy of the ID is tacked onto the end of the MSAAMENUINFO structure. + // + if (data.OwnerDraw) + info.dwItemData = AllocMsaaMenuInfo(); + else + info.dwItemData = (IntPtr) unchecked((int) uniqueID); + } + else { + // On Win64, there are no reserved address ranges we can use for menu item IDs. So instead we will + // have to allocate an MSAMENUINFO heap structure for all menu items, not just owner-drawn ones. + info.dwItemData = AllocMsaaMenuInfo(); + } + + // We won't render the shortcut if: 1) it's not set, 2) we're a parent, 3) we're toplevel + // + if (data.showShortcut && data.shortcut != 0 && !IsParent && !isTopLevel) { + info.dwTypeData = data.caption + "\t" + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString((Keys)(int)data.shortcut); + } + else { + // Windows issue: Items with empty captions sometimes block keyboard + // access to other items in same menu. + info.dwTypeData = (data.caption.Length == 0 ? " " : data.caption); + } + info.cch = 0; + + return info; + } + + /// + /// + /// + /// Disposes the . + /// + /// + protected override void Dispose(bool disposing) { + + if (disposing) { + if (menu != null) { + menu.MenuItems.Remove(this); + } + + if (data != null) { + data.RemoveItem(this); + } + lock(allCreatedMenuItems) { + allCreatedMenuItems.Remove(uniqueID); + } + this.uniqueID = 0; + + } + FreeMsaaMenuInfo(); + base.Dispose(disposing); + } + + + // Given a unique menu item ID, find the corresponding MenuItem + // object, using the master lookup table of all created MenuItems. + internal static MenuItem GetMenuItemFromUniqueID(uint uniqueID) { + WeakReference weakRef = (WeakReference)allCreatedMenuItems[uniqueID]; + if (weakRef != null && weakRef.IsAlive) { + return (MenuItem)weakRef.Target; + } + Debug.Fail("Weakref for menu item has expired or has been removed! Who is trying to access this ID?"); + return null; + } + + // Given the "item data" value of a Win32 menu item, find the corresponding MenuItem object (using + // the master lookup table of all created MenuItems). The item data may be either the unique menu + // item ID, or a pointer to an MSAAMENUINFO structure with a copy of the unique ID tacked to the end. + // To reliably tell IDs and structure addresses apart, IDs live in the 0xC0000000-0xFFFFFFFF range. + // This is the top 1Gb of unmananged process memory, where an app's heap allocations should never be. + [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] + internal static MenuItem GetMenuItemFromItemData(IntPtr itemData) { + uint uniqueID = (uint) (ulong) itemData; + + if (uniqueID == 0) + return null; + + // To check it's 32-bit OS or 64-bit OS. + if (IntPtr.Size == 4) { + if (uniqueID < firstUniqueID) { + MsaaMenuInfoWithId msaaMenuInfo = (MsaaMenuInfoWithId) Marshal.PtrToStructure(itemData, typeof(MsaaMenuInfoWithId)); + uniqueID = msaaMenuInfo.uniqueID; + } + } + else { + // ...its always a pointer on Win64 (see CreateMenuItemInfo) + MsaaMenuInfoWithId msaaMenuInfo = (MsaaMenuInfoWithId) Marshal.PtrToStructure(itemData, typeof(MsaaMenuInfoWithId)); + uniqueID = msaaMenuInfo.uniqueID; + } + + return GetMenuItemFromUniqueID(uniqueID); + } + + // MsaaMenuInfoWithId is an MSAAMENUINFO structure with a menu item ID field tacked onto the + // end. This allows us to pass the data we need to Win32 / MSAA, and still be able to get the ID + // out again later on, so we can delegate Win32 menu messages back to the correct MenuItem object. + [StructLayout(LayoutKind.Sequential)] + private struct MsaaMenuInfoWithId { + public NativeMethods.MSAAMENUINFO msaaMenuInfo; + public uint uniqueID; + + public MsaaMenuInfoWithId(string text, uint uniqueID) { + msaaMenuInfo = new NativeMethods.MSAAMENUINFO(text); + this.uniqueID = uniqueID; + } + } + + // Creates an MSAAMENUINFO structure (in the unmanaged heap) based on the current state + // of this MenuItem object. Address of this structure is cached in the object so we can + // free it later on using FreeMsaaMenuInfo(). If structure has already been allocated, + // it is destroyed and a new one created. + private IntPtr AllocMsaaMenuInfo() { + FreeMsaaMenuInfo(); + msaaMenuInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MsaaMenuInfoWithId))); + + // To check it's 32-bit OS or 64-bit OS. + if (IntPtr.Size == 4) { + // We only check this on Win32, irrelevant on Win64 (see CreateMenuItemInfo) + Debug.Assert(((uint) (ulong) msaaMenuInfoPtr) < firstUniqueID); // ...check for incursion into menu item ID range (unlikely!) + } + + MsaaMenuInfoWithId msaaMenuInfoStruct = new MsaaMenuInfoWithId(data.caption, uniqueID); + Marshal.StructureToPtr(msaaMenuInfoStruct, msaaMenuInfoPtr, false); + Debug.Assert(msaaMenuInfoPtr != IntPtr.Zero); + return msaaMenuInfoPtr; + } + + // Frees the MSAAMENUINFO structure (in the unmanaged heap) for the current MenuObject object, + // if one has previously been allocated. Takes care to free sub-structures too, to avoid leaks! + private void FreeMsaaMenuInfo() { + if (msaaMenuInfoPtr != IntPtr.Zero) { + Marshal.DestroyStructure(msaaMenuInfoPtr, typeof(MsaaMenuInfoWithId)); + Marshal.FreeHGlobal(msaaMenuInfoPtr); + msaaMenuInfoPtr = IntPtr.Zero; + } + } + + internal override void ItemsChanged(int change) { + base.ItemsChanged(change); + + if (change == CHANGE_ITEMS) { + // when the menu collection changes deal w/ it locally + Debug.Assert(!this.created, "base.ItemsChanged should have wiped out our handles"); + if (menu != null && menu.created) { + UpdateMenuItem(true); + CreateMenuItems(); + } + } else { + if (!hasHandle && IsParent) + UpdateMenuItem(true); + + MainMenu main = GetMainMenu(); + if (main != null && ((data.State & STATE_INMDIPOPUP) == 0)) { + main.ItemsChanged(change, this); + } + } + } + + internal void ItemsChanged(int change, MenuItem item) { + if (change == CHANGE_ITEMADDED && + this.data != null && + this.data.baseItem != null && + this.data.baseItem.MenuItems.Contains(item)) { + if (menu != null && menu.created) { + UpdateMenuItem(true); + CreateMenuItems(); + } else if (this.data != null) { + MenuItem currentMenuItem = this.data.firstItem; + while (currentMenuItem != null) { + if (currentMenuItem.created) { + MenuItem newItem = item.CloneMenu(); + item.data.AddItem(newItem); + currentMenuItem.MenuItems.Add(newItem); + // newItem.menu = currentMenuItem; + // newItem.CreateMenuItem(); + break; + } + currentMenuItem = currentMenuItem.nextLinkedItem; + } + } + } + } + + internal Form[] FindMdiForms() { + Form[] forms = null; + MainMenu main = GetMainMenu(); + Form menuForm = null; + if (main != null) { + menuForm = main.GetFormUnsafe(); + } + if (menuForm != null) { + forms = menuForm.MdiChildren; + } + if (forms == null) { + forms = new Form[0]; + } + return forms; + } + + /// + /// See the similar code in MdiWindowListStripcs::PopulateItems(), which is + /// unfortunately just different enough in its working environment that we + /// can't readily combine the two. But if you're fixing something here, chances + /// are that the same issue will need scrutiny over there. + /// + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // "-" is OK + private void PopulateMdiList() { + MenuItem senderMenu = this; + data.SetState(STATE_INMDIPOPUP, true); + try { + CleanListItems(this); + + // add new items + // + Form[] forms = FindMdiForms(); + if (forms != null && forms.Length > 0) { + + Form activeMdiChild = GetMainMenu().GetFormUnsafe().ActiveMdiChild; + + if (senderMenu.MenuItems.Count > 0) { + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + MenuItem sep = (MenuItem)Activator.CreateInstance(this.GetType()); + sep.data.UserData = new MdiListUserData(); + sep.Text = "-"; + senderMenu.MenuItems.Add(sep); + } + + // VSWhidbey 93540: Build a list of child windows to be displayed in + // the MDIList menu item... + // Show the first maxMenuForms visible elements of forms[] as Window menu items, except: + // Always show the active form, even if it's not in the first maxMenuForms visible elements of forms[]. + // If the active form isn't in the first maxMenuForms forms, then show the first maxMenuForms-1 elements + // in forms[], and make the active form the last one on the menu. + // VSWhidbey 260405: don't count nonvisible forms against the limit on Window menu items. + + const int maxMenuForms = 9; // Max number of Window menu items for forms + int visibleChildren = 0; // the number of visible child forms (so we know to show More Windows...) + int accel = 1; // prefix the form name with this digit, underlined, as an accelerator + int formsAddedToMenu = 0; + bool activeFormAdded = false; + for (int i = 0; i < forms.Length; i++) { + if (forms[i].Visible) { + visibleChildren++; + if ((activeFormAdded && (formsAddedToMenu < maxMenuForms)) || // don't exceed max + (!activeFormAdded && (formsAddedToMenu < (maxMenuForms-1)) || // save room for active if it's not in yet + (forms[i].Equals(activeMdiChild)))){ // there's always room for activeMdiChild + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + MenuItem windowItem = (MenuItem)Activator.CreateInstance(this.GetType()); + windowItem.data.UserData = new MdiListFormData(this, i); + + if (forms[i].Equals(activeMdiChild)) { + windowItem.Checked = true; + activeFormAdded = true; + } + windowItem.Text = String.Format(CultureInfo.CurrentUICulture, "&{0} {1}", accel, forms[i].Text); + accel++; + formsAddedToMenu++; + senderMenu.MenuItems.Add(windowItem); + } + } + } + + // VSWhidbey 93540: Display the More Windows menu option when there are more than 9 MDI + // Child menu items to be displayed. This is necessary because we're managing our own + // MDI lists, rather than letting Windows do this for us. + if (visibleChildren > maxMenuForms) { + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + MenuItem moreWindows = (MenuItem)Activator.CreateInstance(this.GetType()); + moreWindows.data.UserData = new MdiListMoreWindowsData(this); + moreWindows.Text = SR.GetString(SR.MDIMenuMoreWindows); + senderMenu.MenuItems.Add(moreWindows); + } + } + } + finally { + data.SetState(STATE_INMDIPOPUP, false); + } + } + + /// + /// + /// + /// Merges this menu item with another menu item and returns + /// the resulting merged . + /// + /// + public virtual MenuItem MergeMenu() { + // SECREVIEW : Late-binding does not represent a security threat, see bug#411899 for more info.. + // + MenuItem newItem = (MenuItem)Activator.CreateInstance(this.GetType()); + data.AddItem(newItem); + newItem.MergeMenu(this); + return newItem; + } + + /// + /// + /// + /// Merges another menu item with this menu item. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Shipped in Everett + public void MergeMenu(MenuItem itemSrc) { + base.MergeMenu(itemSrc); + itemSrc.data.AddItem(this); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnClick(EventArgs e) { + if (data.UserData is MdiListUserData) { + ((MdiListUserData)data.UserData).OnClick(e); + } + else if (data.baseItem != this) { + data.baseItem.OnClick(e); + } + else if (data.onClick != null) { + data.onClick(this, e); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + if (data.baseItem != this) { + data.baseItem.OnDrawItem(e); + } + else if (data.onDrawItem != null) { + data.onDrawItem(this, e); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnMeasureItem(MeasureItemEventArgs e) { + if (data.baseItem != this) { + data.baseItem.OnMeasureItem(e); + } + else if (data.onMeasureItem != null) { + data.onMeasureItem(this, e); + } + + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnPopup(EventArgs e) { + bool recreate = false; + for (int i=0; i + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnSelect(EventArgs e) { + if (data.baseItem != this) { + data.baseItem.OnSelect(e); + } + else if (data.onSelect != null) { + data.onSelect(this, e); + } + } + + /// + /// + protected virtual void OnInitMenuPopup(EventArgs e) { + OnPopup(e); + } + + // C#r + internal virtual void _OnInitMenuPopup( EventArgs e ) { + OnInitMenuPopup( e ); + } + + /// + /// + /// + /// Generates a + /// event for the MenuItem, simulating a click by a + /// user. + /// + /// + public void PerformClick() { + OnClick(EventArgs.Empty); + } + + /// + /// + /// + /// Raises the + /// event for this menu item. + /// + /// + public virtual void PerformSelect() { + OnSelect(EventArgs.Empty); + } + + internal virtual bool ShortcutClick() { + if (menu is MenuItem) { + MenuItem parent = (MenuItem)menu; + if (!parent.ShortcutClick() || menu != parent) return false; + } + if ((data.State & STATE_DISABLED) != 0) return false; + if (ItemCount > 0) + OnPopup(EventArgs.Empty); + else + OnClick(EventArgs.Empty); + return true; + } + + + /// + /// + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + + String menuItemText = String.Empty; + + if (data != null && data.caption != null) + menuItemText = data.caption; + + return s + ", Text: " + menuItemText; + } + + internal void UpdateMenuItemIfDirty() { + if (dataVersion != data.version) + UpdateMenuItem(true); + } + + internal void UpdateItemRtl(bool setRightToLeftBit) { + if(!menuItemIsCreated) { + return; + } + + NativeMethods.MENUITEMINFO_T info = new NativeMethods.MENUITEMINFO_T(); + info.fMask = NativeMethods.MIIM_TYPE | NativeMethods.MIIM_STATE | NativeMethods.MIIM_SUBMENU; + info.dwTypeData = new string('\0', Text.Length + 2); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + info.cch = info.dwTypeData.Length - 1; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, info); + if(setRightToLeftBit) { + info.fType |= NativeMethods.MFT_RIGHTJUSTIFY | NativeMethods.MFT_RIGHTORDER; + } else { + info.fType &= ~(NativeMethods.MFT_RIGHTJUSTIFY | NativeMethods.MFT_RIGHTORDER); + } + UnsafeNativeMethods.SetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, info); + +#if DEBUG + + info.fMask = NativeMethods.MIIM_TYPE | NativeMethods.MIIM_STATE | NativeMethods.MIIM_SUBMENU; + info.dwTypeData = new string('\0', 256); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + info.cch = info.dwTypeData.Length - 1; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, info); + Debug.Assert(((info.fType & NativeMethods.MFT_RIGHTORDER) != 0) == setRightToLeftBit, "Failed to set bit!"); + +#endif + } + + + internal void UpdateMenuItem(bool force) { + if (menu == null || !menu.created) { + return; + } + + if (force || menu is MainMenu || menu is ContextMenu) { + NativeMethods.MENUITEMINFO_T info = CreateMenuItemInfo(); + UnsafeNativeMethods.SetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, info); +#if DEBUG + NativeMethods.MENUITEMINFO_T infoVerify = new NativeMethods.MENUITEMINFO_T(); + infoVerify.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T)); + infoVerify.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | + NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_TYPE; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(menu, menu.handle), MenuID, false, infoVerify); +#endif + + if (hasHandle && info.hSubMenu == IntPtr.Zero) { + ClearHandles(); + } + hasHandle = info.hSubMenu != IntPtr.Zero; + dataVersion = data.version; + if (menu is MainMenu) { + Form f = ((MainMenu)menu).GetFormUnsafe(); + if (f != null) { + SafeNativeMethods.DrawMenuBar(new HandleRef(f, f.Handle)); + } + } + } + } + + /// + /// + /// + /// + internal void WmDrawItem(ref Message m) { + + // Handles the OnDrawItem message sent from ContainerControl + + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + Debug.WriteLineIf(Control.PaletteTracing.TraceVerbose, Handle + ": Force set palette in MenuItem drawitem"); + IntPtr oldPal = Control.SetUpPalette(dis.hDC, false /*force*/, false); + try { + Graphics g = Graphics.FromHdcInternal(dis.hDC); + try { + OnDrawItem(new DrawItemEventArgs(g, SystemInformation.MenuFont, Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom), Index, (DrawItemState)dis.itemState)); + } + finally { + g.Dispose(); + } + } + finally { + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dis.hDC), new HandleRef(null, oldPal), 0); + } + } + + m.Result = (IntPtr)1; + } + + /// + /// + /// + /// + internal void WmMeasureItem(ref Message m) { + + // Handles the OnMeasureItem message sent from ContainerControl + + // Obtain the measure item struct + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + // The OnMeasureItem handler now determines the height and width of the item + + IntPtr screendc = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + Graphics graphics = Graphics.FromHdcInternal(screendc); + MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, Index); + try { + OnMeasureItem(mie); + } + finally { + graphics.Dispose(); + } + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screendc)); + + // Update the measure item struct with the new width and height + mis.itemHeight = mie.ItemHeight; + mis.itemWidth = mie.ItemWidth; + Marshal.StructureToPtr(mis, m.LParam, false); + + m.Result = (IntPtr)1; + } + + + /// + /// + /// + internal class MenuItemData : ICommandExecutor { + internal MenuItem baseItem; + internal MenuItem firstItem; + + private int state; + internal int version; + internal MenuMerge mergeType; + internal int mergeOrder; + internal string caption; + internal short mnemonic; + internal Shortcut shortcut; + internal bool showShortcut; + internal EventHandler onClick; + internal EventHandler onPopup; + internal EventHandler onSelect; + internal DrawItemEventHandler onDrawItem; + internal MeasureItemEventHandler onMeasureItem; + + private object userData = null; + internal Command cmd; + + internal MenuItemData(MenuItem baseItem, MenuMerge mergeType, int mergeOrder, Shortcut shortcut, bool showShortcut, + string caption, EventHandler onClick, EventHandler onPopup, EventHandler onSelect, + DrawItemEventHandler onDrawItem, MeasureItemEventHandler onMeasureItem) { + AddItem(baseItem); + this.mergeType = mergeType; + this.mergeOrder = mergeOrder; + this.shortcut = shortcut; + this.showShortcut = showShortcut; + this.caption = caption == null? "": caption; + this.onClick = onClick; + this.onPopup = onPopup; + this.onSelect = onSelect; + this.onDrawItem = onDrawItem; + this.onMeasureItem = onMeasureItem; + this.version = 1; + this.mnemonic = -1; + } + + + internal bool OwnerDraw { + get { + return ((State & STATE_OWNERDRAW) != 0); + } + set { + SetState(STATE_OWNERDRAW, value); + } + } + + internal bool MdiList { + get { + return HasState(STATE_MDILIST); + } + set { + if (((state & STATE_MDILIST) != 0) != value) { + SetState(STATE_MDILIST, value); + for (MenuItem item = firstItem; item != null; item = item.nextLinkedItem) { + item.ItemsChanged(Menu.CHANGE_MDI); + } + } + } + } + + internal MenuMerge MergeType { + get { + return mergeType; + } + set { + if (mergeType != value) { + mergeType = value; + ItemsChanged(Menu.CHANGE_MERGE); + } + } + } + + internal int MergeOrder { + get { + return mergeOrder; + } + set { + if (mergeOrder != value) { + mergeOrder = value; + ItemsChanged(Menu.CHANGE_MERGE); + } + } + } + + internal char Mnemonic { + get { + if (mnemonic == -1) { + mnemonic = (short) WindowsFormsUtils.GetMnemonic(caption, true); + } + return(char)mnemonic; + } + } + + internal int State { + get { + return state; + } + } + + internal bool Visible { + get { + return(state & MenuItem.STATE_HIDDEN) == 0; + } + set { + if (((state & MenuItem.STATE_HIDDEN) == 0) != value) { + state = value? state & ~MenuItem.STATE_HIDDEN: state | MenuItem.STATE_HIDDEN; + ItemsChanged(Menu.CHANGE_VISIBLE); + } + } + } + + + internal object UserData { + get { + return userData; + } + set { + userData = value; + } + } + + internal void AddItem(MenuItem item) { + if (item.data != this) { + if (item.data != null) { + item.data.RemoveItem(item); + } + + item.nextLinkedItem = firstItem; + firstItem = item; + if (baseItem == null) baseItem = item; + item.data = this; + item.dataVersion = 0; + item.UpdateMenuItem(false); + } + } + + public void Execute() { + if (baseItem != null) { + baseItem.OnClick(EventArgs.Empty); + } + } + + internal int GetMenuID() { + if (null == cmd) + cmd = new Command(this); + return cmd.ID; + } + + internal void ItemsChanged(int change) { + for (MenuItem item = firstItem; item != null; item = item.nextLinkedItem) { + if (item.menu != null) + item.menu.ItemsChanged(change); + } + } + + internal void RemoveItem(MenuItem item) { + Debug.Assert(item.data == this, "bad item passed to MenuItemData.removeItem"); + + if (item == firstItem) { + firstItem = item.nextLinkedItem; + } + else { + MenuItem itemT; + for (itemT = firstItem; item != itemT.nextLinkedItem;) + itemT = itemT.nextLinkedItem; + itemT.nextLinkedItem = item.nextLinkedItem; + } + item.nextLinkedItem = null; + item.data = null; + item.dataVersion = 0; + + if (item == baseItem) { + baseItem = firstItem; + } + + if (firstItem == null) { + // No longer needed. Toss all references and the Command object. + Debug.Assert(baseItem == null, "why isn't baseItem null?"); + onClick = null; + onPopup = null; + onSelect = null; + onDrawItem = null; + onMeasureItem = null; + if (cmd != null) { + cmd.Dispose(); + cmd = null; + } + } + } + + internal void SetCaption(string value) { + if (value == null) + value = ""; + if (!caption.Equals(value)) { + caption = value; + UpdateMenuItems(); + } + + #if DEBUG + if (value.Length > 0) { + baseItem._debugText = value; + } + #endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal bool HasState(int flag) { + return((State & flag) == flag); + } + + internal void SetState(int flag, bool value) { + if (((state & flag) != 0) != value) { + state = value? state | flag: state & ~flag; + UpdateMenuItems(); + } + } + + internal void UpdateMenuItems() { + version++; + for (MenuItem item = firstItem; item != null; item = item.nextLinkedItem) { + item.UpdateMenuItem(true); + } + } + + } + + private class MdiListUserData { + public virtual void OnClick(EventArgs e) { + } + } + + private class MdiListFormData : MdiListUserData { + private MenuItem parent; + private int boundIndex; + + public MdiListFormData(MenuItem parentItem, int boundFormIndex) { + boundIndex = boundFormIndex; + parent = parentItem; + } + + public override void OnClick(EventArgs e) { + if (boundIndex != -1) { + // SECREVIEW : User selected a window, that means it is OK + // : to move focus + // + IntSecurity.ModifyFocus.Assert(); + try { + Form[] forms = parent.FindMdiForms(); + Debug.Assert(forms != null, "Didn't get a list of the MDI Forms."); + + if (forms != null && forms.Length > boundIndex) { + Form boundForm = forms[boundIndex]; + boundForm.Activate(); + if (boundForm.ActiveControl != null && !boundForm.ActiveControl.Focused) { + boundForm.ActiveControl.Focus(); + } + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + } + + private class MdiListMoreWindowsData : MdiListUserData { + + private MenuItem parent; + + public MdiListMoreWindowsData(MenuItem parent) { + this.parent = parent; + } + + public override void OnClick(EventArgs e) { + Form[] forms = parent.FindMdiForms(); + Debug.Assert(forms != null, "Didn't get a list of the MDI Forms."); + Form active = parent.GetMainMenu().GetFormUnsafe().ActiveMdiChild; + Debug.Assert(active != null, "Didn't get the active MDI child"); + if (forms != null && forms.Length > 0 && active != null) { + + + + // SECREVIEW : "System" style dialog, no user code will execute, and + // : we don't want the restricted dialog options... + // + IntSecurity.AllWindows.Assert(); + try { + using (MdiWindowDialog dialog = new MdiWindowDialog()) { + dialog.SetItems(active, forms); + DialogResult result = dialog.ShowDialog(); + if (result == DialogResult.OK) { + + // AllWindows Assert above allows this... + // + dialog.ActiveChildForm.Activate(); + if (dialog.ActiveChildForm.ActiveControl != null && !dialog.ActiveChildForm.ActiveControl.Focused) { + dialog.ActiveChildForm.ActiveControl.Focus(); + } + } + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MenuMerge.cs b/WindowsForms/Managed/System/WinForms/MenuMerge.cs new file mode 100644 index 000000000..676d9dd18 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MenuMerge.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the behavior of a when it is merged with items in another menu. + /// + /// + public enum MenuMerge { + + /// + /// + /// + /// The is added to + /// the existing objects in a merged menu. + /// + /// + Add = 0, + + /// + /// + /// + /// The replaces the + /// existing + /// at the same position in a + /// merged menu. + /// + /// + /// + Replace = 1, + + /// + /// + /// + /// Subitems of this are merged with + /// those of existing objects + /// at the same position in a merged menu. + /// + /// + MergeItems = 2, + + /// + /// + /// + /// The is not included in a merged menu. + /// + /// + Remove = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/MenuStrip.cs b/WindowsForms/Managed/System/WinForms/MenuStrip.cs new file mode 100644 index 000000000..f56a82428 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MenuStrip.cs @@ -0,0 +1,310 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + SRDescription(SR.DescriptionMenuStrip) + ] + public class MenuStrip : ToolStrip { + + + private ToolStripMenuItem mdiWindowListItem = null; + + private static readonly object EventMenuActivate = new object(); + private static readonly object EventMenuDeactivate = new object(); + + + + /// + public MenuStrip() { + this.CanOverflow = false; + this.GripStyle = ToolStripGripStyle.Hidden; + this.Stretch = true; + + } + + internal override bool KeyboardActive { + get { return base.KeyboardActive; } + set { + if (base.KeyboardActive != value) { + base.KeyboardActive = value; + if (value) { + OnMenuActivate(EventArgs.Empty); + } + else { + OnMenuDeactivate(EventArgs.Empty); + } + } + } + } + + + [ + DefaultValue(false), + SRDescription(SR.ToolStripCanOverflowDescr), + SRCategory(SR.CatLayout), + Browsable(false) + ] + public new bool CanOverflow { + get { + return base.CanOverflow; + } + set { + base.CanOverflow = value; + } + } + protected override bool DefaultShowItemToolTips { + get { + return false; + } + } + protected override Padding DefaultGripMargin { + get { + // MenuStrip control is scaled by Control::ScaleControl() + // VSWhidbey 448134 - ensure grip aligns properly when set visible. + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Padding(2, 2, 0, 2), DeviceDpi) : + new Padding(2, 2, 0, 2); + } + } + + /// + protected override Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(200, 24), DeviceDpi) : + new Size(200, 24); + } + } + + protected override Padding DefaultPadding { + get { + // MenuStrip control is scaled by Control::ScaleControl() + // VSWhidbey 448134: scoot the grip over when present + if (GripStyle == ToolStripGripStyle.Visible) { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Padding(3, 2, 0, 2), DeviceDpi) : + new Padding(3, 2, 0, 2); + } + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Padding(6, 2, 0, 2), DeviceDpi) : + new Padding(6, 2, 0, 2); + } + } + + [DefaultValue(ToolStripGripStyle.Hidden)] + public new ToolStripGripStyle GripStyle { + get { + return base.GripStyle; + } + set { + base.GripStyle = value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.MenuStripMenuActivateDescr)] + public event EventHandler MenuActivate { + add { + Events.AddHandler(EventMenuActivate, value); + } + remove { + Events.RemoveHandler(EventMenuActivate, value); + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.MenuStripMenuDeactivateDescr)] + public event EventHandler MenuDeactivate { + add { + Events.AddHandler(EventMenuDeactivate, value); + } + remove { + Events.RemoveHandler(EventMenuDeactivate, value); + } + } + + [DefaultValue(false)] + [SRDescription(SR.ToolStripShowItemToolTipsDescr)] + [SRCategory(SR.CatBehavior)] + public new bool ShowItemToolTips { + get { + return base.ShowItemToolTips; + } + set { + base.ShowItemToolTips = value; + } + } + + [DefaultValue(true)] + [SRCategory(SR.CatLayout)] + [SRDescription(SR.ToolStripStretchDescr)] + public new bool Stretch { + get { + return base.Stretch; + } + set { + base.Stretch = value; + } + } + + [DefaultValue(null)] + [MergableProperty(false)] + [SRDescription(SR.MenuStripMdiWindowListItem)] + [SRCategory(SR.CatBehavior)] + [TypeConverterAttribute(typeof(MdiWindowListItemConverter))] + public ToolStripMenuItem MdiWindowListItem { + get { + return mdiWindowListItem; + } + set { + mdiWindowListItem = value; + } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new MenuStripAccessibleObject(this); + } + protected internal override ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) { + if (text == "-") { + return new ToolStripSeparator(); + } + else { + return new ToolStripMenuItem(text, image, onClick); + } + } + + internal override ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) { + ToolStripItem nextItem = base.GetNextItem(start, direction, rtlAware); + if (nextItem is MdiControlStrip.SystemMenuItem && AccessibilityImprovements.Level2) { + nextItem = base.GetNextItem(nextItem, direction, rtlAware); + } + return nextItem; + } + + protected virtual void OnMenuActivate(EventArgs e) { + if (IsHandleCreated) { + AccessibilityNotifyClients(AccessibleEvents.SystemMenuStart, -1); + } + EventHandler handler = (EventHandler)Events[EventMenuActivate]; + if (handler != null) handler(this, e); + } + + protected virtual void OnMenuDeactivate(EventArgs e) { + if (IsHandleCreated) { + AccessibilityNotifyClients(AccessibleEvents.SystemMenuEnd, -1); + } + EventHandler handler = (EventHandler)Events[EventMenuDeactivate]; + if (handler != null) handler(this, e); + } + + /// + /// Called from ToolStripManager.ProcessMenuKey. Fires MenuActivate event and sets focus. + /// + internal bool OnMenuKey() { + if (!(Focused || ContainsFocus)) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] set focus to menustrip"); + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this, /*menuKeyPressed=*/true); + + if (DisplayedItems.Count > 0) { + if (DisplayedItems[0] is MdiControlStrip.SystemMenuItem) { + SelectNextToolStripItem(DisplayedItems[0], /*forward=*/true); + } + else { + // first alt should select "File". Future keydowns of alt should restore focus. + SelectNextToolStripItem(null, /*forward=*/(RightToLeft == RightToLeft.No)); + } + } + + return true; + } + return false; + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message m, Keys keyData) { + + if (ToolStripManager.ModalMenuFilter.InMenuMode) { + // ALT, then space should dismiss the menu and activate the system menu. + if (keyData == Keys.Space) { + // if we're focused it's ok to activate system menu + // if we're not focused - we should not activate if we contain focus - this means a text box or something + // has focus. + if (Focused || !ContainsFocus) { + NotifySelectionChange(null); + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[MenuStrip.ProcessCmdKey] Rolling up the menu and invoking the system menu"); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + // send a WM_SYSCOMMAND SC_KEYMENU + Space to activate the system menu. + UnsafeNativeMethods.PostMessage(WindowsFormsUtils.GetRootHWnd(this), NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_KEYMENU, (int)Keys.Space); + return true; + } + } + } + return base.ProcessCmdKey(ref m, keyData); + + + } + /// + /// + /// Summary of WndProc. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (m.Msg == NativeMethods.WM_MOUSEACTIVATE && (ActiveDropDowns.Count == 0)) { + // call menu activate before we actually take focus. + Point pt = PointToClient(WindowsFormsUtils.LastCursorPoint); + ToolStripItem item = GetItemAt(pt); + if (item != null && !(item is ToolStripControlHost)) { + // verify the place where we've clicked is a place where we have to do "fake" focus + // e.g. an item that isnt a control. + KeyboardActive = true; + } + } + + base.WndProc(ref m); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class MenuStripAccessibleObject : ToolStripAccessibleObject { + + public MenuStripAccessibleObject(MenuStrip owner) + : base(owner) { + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.MenuBar; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_MenuBarControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/MergeAction.cs b/WindowsForms/Managed/System/WinForms/MergeAction.cs new file mode 100644 index 000000000..eb99b39b7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MergeAction.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + public enum MergeAction { + /// + Append, + /// + Insert, + /// + Replace , + /// + Remove, + /// + MatchOnly + }; +} + diff --git a/WindowsForms/Managed/System/WinForms/Message.cs b/WindowsForms/Managed/System/WinForms/Message.cs new file mode 100644 index 000000000..e87673218 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Message.cs @@ -0,0 +1,169 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System; + using System.Windows.Forms; + + + /// + /// + /// + /// Implements a Windows message. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [SuppressMessage("Microsoft.Security", "CA2108:ReviewDeclarativeSecurityOnValueTypes")] + public struct Message { +#if DEBUG + static TraceSwitch AllWinMessages = new TraceSwitch("AllWinMessages", "Output every received message"); +#endif + + IntPtr hWnd; + int msg; + IntPtr wparam; + IntPtr lparam; + IntPtr result; + + /// + /// + /// Specifies the window handle of the message. + /// + + public IntPtr HWnd { + get { return hWnd; } + set { hWnd = value; } + } + + /// + /// + /// Specifies the ID number for the message. + /// + public int Msg { + get { return msg; } + set { msg = value; } + } + + /// + /// + /// Specifies the of the message. + /// + public IntPtr WParam { + get { return wparam; } + set { wparam = value; } + } + + /// + /// + /// Specifies the of the message. + /// + public IntPtr LParam { + get { return lparam; } + set { lparam = value; } + } + + /// + /// + /// Specifies the return value of the message. + /// + public IntPtr Result { + get { return result; } + set { result = value; } + } + + /// + /// + /// Gets the value, and converts the value to an object. + /// + public object GetLParam(Type cls) { + return UnsafeNativeMethods.PtrToStructure(lparam, cls); + } + + /// + /// + /// Creates a new object. + /// + public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + Message m = new Message(); + m.hWnd = hWnd; + m.msg = msg; + m.wparam = wparam; + m.lparam = lparam; + m.result = IntPtr.Zero; + +#if DEBUG + if(AllWinMessages.TraceVerbose) { + Debug.WriteLine(m.ToString()); + } +#endif + return m; + } + + /// + public override bool Equals(object o) { + if (!(o is Message)) { + return false; + } + + Message m = (Message)o; + return hWnd == m.hWnd && + msg == m.msg && + wparam == m.wparam && + lparam == m.lparam && + result == m.result; + } + + public static bool operator !=(Message a, Message b) { + return !a.Equals(b); + } + + public static bool operator ==(Message a, Message b) { + return a.Equals(b); + } + + /// + public override int GetHashCode() { + return (int)hWnd << 4 | msg; + } + + /// + /// + /// + /// + public override string ToString() { + // ----URT : 151574. Link Demand on System.Windows.Forms.Message + // fails to protect overriden methods. + bool unrestricted = false; + try + { + IntSecurity.UnmanagedCode.Demand(); + unrestricted = true; + } + catch (SecurityException) + { + // eat the exception. + } + + if (unrestricted) + { + return MessageDecoder.ToString(this); + } + else + { + return base.ToString(); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageBox.cs b/WindowsForms/Managed/System/WinForms/MessageBox.cs new file mode 100644 index 000000000..f478adca5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageBox.cs @@ -0,0 +1,501 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Windows.Forms; + using System.Collections; + + /// + /// + /// + /// Displays a + /// message box that can contain text, buttons, and symbols that + /// inform and instruct the + /// user. + /// + /// + public class MessageBox { + private const int IDOK = 1; + private const int IDCANCEL = 2; + private const int IDABORT = 3; + private const int IDRETRY = 4; + private const int IDIGNORE = 5; + private const int IDYES = 6; + private const int IDNO = 7; + + + private const int HELP_BUTTON = 0x00004000; + + [ ThreadStatic ] + private static HelpInfo[] helpInfoTable; + + + + /// + /// + /// This constructor is private so people aren't tempted to try and create + /// instances of these -- they should just use the static show + /// methods. + /// + private MessageBox() { + } + + private static DialogResult Win32ToDialogResult(int value) { + switch (value) { + case IDOK: + return DialogResult.OK; + case IDCANCEL: + return DialogResult.Cancel; + case IDABORT: + return DialogResult.Abort; + case IDRETRY: + return DialogResult.Retry; + case IDIGNORE: + return DialogResult.Ignore; + case IDYES: + return DialogResult.Yes; + case IDNO: + return DialogResult.No; + default: + return DialogResult.No; + } + } + + + internal static HelpInfo HelpInfo { + get { + // unfortunately, there's no easy way to obtain handle of a message box. + // we'll have to rely on the fact that modal message loops have to pop off in an orderly way. + + if (helpInfoTable != null && helpInfoTable.Length > 0) { + // the top of the stack is actually at the end of the array. + return helpInfoTable[helpInfoTable.Length - 1]; + } + + return null; + } + } + + + private static void PopHelpInfo() { + + // we roll our own stack here because we want a pretty lightweight implementation. + // usually there's only going to be one message box shown at a time. But if + // someone shows two message boxes (say by launching them via a WM_TIMER message) + // we've got to gracefully handle the current help info. + if (helpInfoTable == null) { + Debug.Fail("Why are we being called when there's nothing to pop?"); + + } + else { + if (helpInfoTable.Length == 1) { + helpInfoTable = null; + } + else { + int newCount = helpInfoTable.Length -1; + HelpInfo[] newTable = new HelpInfo[newCount]; + Array.Copy(helpInfoTable, newTable, newCount); + helpInfoTable = newTable; + + } + } + + } + private static void PushHelpInfo(HelpInfo hpi) { + + // we roll our own stack here because we want a pretty lightweight implementation. + // usually there's only going to be one message box shown at a time. But if + // someone shows two message boxes (say by launching them via a WM_TIMER message) + // we've got to gracefully handle the current help info. + + int lastCount = 0; + HelpInfo[] newTable; + + if (helpInfoTable == null) { + newTable = new HelpInfo[lastCount+1]; + } + else { + // if we already have a table - allocate a new slot + lastCount = helpInfoTable.Length; + newTable = new HelpInfo[lastCount+1]; + Array.Copy(helpInfoTable, newTable, lastCount); + } + newTable[lastCount] = hpi; + helpInfoTable = newTable; + + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //START WHIDBEY ADDS // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Displays a message box with specified text, caption, and style with Help Button. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options,bool displayHelpButton) { + + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, displayHelpButton); + } + + + /// + /// + /// + /// Displays a message box with specified text, caption, style and Help file Path . + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath) { + + HelpInfo hpi = new HelpInfo(helpFilePath); + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, hpi); + } + + + /// + /// + /// + /// Displays a message box with specified text, caption, style and Help file Path for a IWin32Window. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath) { + + HelpInfo hpi = new HelpInfo(helpFilePath); + return ShowCore(owner, text, caption, buttons, icon, defaultButton, options, hpi); + } + + + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path and keyword. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, string keyword) { + + HelpInfo hpi = new HelpInfo(helpFilePath, keyword); + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, hpi); + } + + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path and keyword for a IWin32Window. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, string keyword) { + + HelpInfo hpi = new HelpInfo(helpFilePath, keyword); + return ShowCore(owner, text, caption, buttons, icon, defaultButton, options, hpi); + } + + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path and HelpNavigator. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options,string helpFilePath, HelpNavigator navigator) { + + HelpInfo hpi = new HelpInfo(helpFilePath, navigator); + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, hpi); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path and HelpNavigator for IWin32Window. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, HelpNavigator navigator) { + + HelpInfo hpi = new HelpInfo(helpFilePath, navigator); + return ShowCore(owner, text, caption, buttons, icon, defaultButton, options, hpi); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path ,HelpNavigator and object. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options,string helpFilePath, HelpNavigator navigator, object param) { + + HelpInfo hpi = new HelpInfo(helpFilePath, navigator, param); + + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, hpi); + } + + + /// + /// + /// + /// Displays a message box with specified text, caption, style, Help file Path ,HelpNavigator and object for a IWin32Window. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, HelpNavigator navigator, object param) { + + HelpInfo hpi = new HelpInfo(helpFilePath, navigator, param); + + return ShowCore(owner, text, caption, buttons, icon, defaultButton, options, hpi); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //END ADD // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options) { + return ShowCore(null, text, caption, buttons, icon, defaultButton, options, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton) { + return ShowCore(null, text, caption, buttons, icon, defaultButton, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { + return ShowCore(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons) { + return ShowCore(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text and caption. + /// + /// + public static DialogResult Show(string text, string caption) { + return ShowCore(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text. + /// + /// + public static DialogResult Show(string text) { + return ShowCore(null, text, String.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options) { + return ShowCore(owner, text, caption, buttons, icon, defaultButton, options, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton) { + return ShowCore(owner, text, caption, buttons, icon, defaultButton, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { + return ShowCore(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) { + return ShowCore(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text and caption. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption) { + return ShowCore(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + /// + /// + /// + /// Displays a message box with specified text. + /// + /// + public static DialogResult Show(IWin32Window owner, string text) { + return ShowCore(owner, text, String.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false); + } + + private static DialogResult ShowCore(IWin32Window owner, string text, string caption, + MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, + MessageBoxOptions options, HelpInfo hpi) { + DialogResult result = DialogResult.None; + try { + PushHelpInfo(hpi); + result = ShowCore(owner, text, caption, buttons, icon, defaultButton, options, true); + } + finally { + PopHelpInfo(); + } + return result; + + } + + private static DialogResult ShowCore(IWin32Window owner, string text, string caption, + MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, + MessageBoxOptions options, bool showHelp) { + + if (!ClientUtils.IsEnumValid(buttons, (int)buttons, (int)MessageBoxButtons.OK, (int)MessageBoxButtons.RetryCancel)){ + throw new InvalidEnumArgumentException("buttons", (int)buttons, typeof(MessageBoxButtons)); + } + + // valid values are 0x0 0x10 0x20 0x30 0x40, chop off the last 4 bits and check that it's between 0 and 4. + if (!WindowsFormsUtils.EnumValidator.IsEnumWithinShiftedRange(icon, /*numBitsToShift*/4, /*min*/0x0,/*max*/0x4)) { + throw new InvalidEnumArgumentException("icon", (int)icon, typeof(MessageBoxIcon)); + } + // valid values are 0x0 0x100, 0x200, chop off the last 8 bits and check that it's between 0 and 2. + if (!WindowsFormsUtils.EnumValidator.IsEnumWithinShiftedRange(defaultButton, /*numBitsToShift*/8, /*min*/0x0,/*max*/0x2)) { + throw new InvalidEnumArgumentException("defaultButton", (int)defaultButton, typeof(DialogResult)); + } + + // options intentionally not verified because we don't expose all the options Win32 supports. + + if (!SystemInformation.UserInteractive && (options & (MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly)) == 0) { + throw new InvalidOperationException(SR.GetString(SR.CantShowModalOnNonInteractive)); + } + if (owner != null && (options & (MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly)) != 0) { + throw new ArgumentException(SR.GetString(SR.CantShowMBServiceWithOwner), "options"); + } + if (showHelp && (options & (MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly)) != 0) { + throw new ArgumentException(SR.GetString(SR.CantShowMBServiceWithHelp), "options"); + } + + // demand if not safe known options. + if ((options & ~(MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading)) != 0) { + // See DDB#163043. + IntSecurity.UnmanagedCode.Demand(); + } + + IntSecurity.SafeSubWindows.Demand(); + + int style = (showHelp) ? HELP_BUTTON : 0; + style |= (int) buttons | (int) icon | (int) defaultButton | (int) options; + + IntPtr handle = IntPtr.Zero; + if (showHelp || ((options & (MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly)) == 0)) { + if (owner == null) { + handle = UnsafeNativeMethods.GetActiveWindow(); + } + else { + handle = Control.GetSafeHandle(owner); + } + } + + IntPtr userCookie = IntPtr.Zero; + + if (Application.UseVisualStyles) { + // CLR4.0 or later, shell32.dll needs to be loaded explicitly. + if (UnsafeNativeMethods.GetModuleHandle(ExternDll.Shell32) == IntPtr.Zero) { + if (UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(ExternDll.Shell32) == IntPtr.Zero) { + int lastWin32Error = Marshal.GetLastWin32Error(); + throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError, ExternDll.Shell32)); + } + } + + // Activate theming scope to get theming for controls at design time and when hosted in browser. + // NOTE: If a theming context is already active, this call is very fast, so shouldn't be a perf issue. + userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + } + + Application.BeginModalMessageLoop(); + DialogResult result; + try { + result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, handle), text, caption, style)); + } + finally { + Application.EndModalMessageLoop(); + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + // Right after the dialog box is closed, Windows sends WM_SETFOCUS back to the previously active control + // but since we have disabled this thread main window the message is lost. So we have to send it again after + // we enable the main window. + // + UnsafeNativeMethods.SendMessage(new HandleRef(owner, handle), NativeMethods.WM_SETFOCUS, 0, 0); + return result; + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageBoxButtons.cs b/WindowsForms/Managed/System/WinForms/MessageBoxButtons.cs new file mode 100644 index 000000000..85737e1c5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageBoxButtons.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + public enum MessageBoxButtons { + /// + /// + /// + /// Specifies that the + /// message box contains an OK button. This field is + /// constant. + /// + /// + OK = 0x00000000, + + /// + /// + /// + /// Specifies that the + /// message box contains OK and Cancel buttons. This field + /// is + /// constant. + /// + /// + OKCancel = 0x00000001, + + /// + /// + /// + /// Specifies that the + /// message box contains Abort, Retry, and Ignore buttons. + /// This field is + /// constant. + /// + /// + AbortRetryIgnore = 0x00000002, + + /// + /// + /// + /// Specifies that the + /// message box contains Yes, No, and Cancel buttons. This + /// field is + /// constant. + /// + /// + YesNoCancel = 0x00000003, + + /// + /// + /// + /// Specifies that the + /// message box contains Yes and No buttons. This field is + /// constant. + /// + /// + YesNo = 0x00000004, + + /// + /// + /// + /// Specifies that the + /// message box contains Retry and Cancel buttons. This field + /// is + /// constant. + /// + /// + RetryCancel = 0x00000005 + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageBoxDefaultButton.cs b/WindowsForms/Managed/System/WinForms/MessageBoxDefaultButton.cs new file mode 100644 index 000000000..8c0f21e51 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageBoxDefaultButton.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System.Diagnostics.CodeAnalysis; + +/* + */ +namespace System.Windows.Forms { + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum MessageBoxDefaultButton { + /// + /// + /// + /// Specifies that the first + /// button on the message box should be the default button. + /// + /// + Button1 = 0x00000000, + /// + /// + /// + /// Specifies that the second + /// button on the message box should be the default button. + /// + /// + Button2 = 0x00000100, + + /// + /// + /// + /// Specifies that the third + /// button on the message box should be the default button. + /// + /// + Button3 = 0x00000200, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageBoxIcon.cs b/WindowsForms/Managed/System/WinForms/MessageBoxIcon.cs new file mode 100644 index 000000000..8041427b2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageBoxIcon.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System.Diagnostics.CodeAnalysis; + + +/* + */ +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum MessageBoxIcon { + /// + /// + /// + /// Specifies that the + /// message box contain no symbols. + /// + /// + None = 0, + + /// + /// + /// + /// Specifies that the + /// message box contains a + /// hand symbol. + /// + /// + Hand = 0x00000010, + + /// + /// + /// + /// Specifies + /// that the message + /// box contains a question + /// mark symbol. + /// + /// + Question = 0x00000020, + + /// + /// + /// + /// Specifies that the + /// message box contains an + /// exclamation symbol. + /// + /// + Exclamation = 0x00000030, + + /// + /// + /// + /// Specifies that the + /// message box contains an + /// asterisk symbol. + /// + /// + Asterisk = 0x00000040, + + /// + /// + /// + /// Specifies that the message box contains a hand icon. This field is + /// constant. + /// + /// + Stop = Hand, + + /// + /// + /// + /// Specifies that the + /// message box contains a + /// hand icon. + /// + /// + Error = Hand, + + /// + /// + /// + /// Specifies that the message box contains an exclamation icon. + /// + /// + Warning = Exclamation, + + /// + /// + /// + /// Specifies that the + /// message box contains an + /// asterisk icon. + /// + /// + Information = Asterisk, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageBoxOptions.cs b/WindowsForms/Managed/System/WinForms/MessageBoxOptions.cs new file mode 100644 index 000000000..d23975f58 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageBoxOptions.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + /// + /// + /// [To be supplied.] + /// + [Flags] + public enum MessageBoxOptions { + /// + /// + /// + /// Specifies that the message box is displayed on the active desktop. + /// + /// + ServiceNotification = 0x00200000, + + /// + /// + /// + /// Specifies that the message box is displayed on the active desktop. + /// + /// + DefaultDesktopOnly = 0x00020000, + + /// + /// + /// + /// Specifies that the message box text is right-aligned. + /// + /// + RightAlign = 0x00080000, + + /// + /// + /// + /// Specifies that the message box text is displayed with Rtl reading order. + /// + /// + RtlReading = 0x00100000, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MessageDecoder.cs b/WindowsForms/Managed/System/WinForms/MessageDecoder.cs new file mode 100644 index 000000000..2e2022ba8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MessageDecoder.cs @@ -0,0 +1,377 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Text; + using System.Runtime.Remoting; + using System.Diagnostics; + + using System; + using System.Windows.Forms; + + + /// + /// + /// Decodes Windows messages. This is in a separate class from Message + /// so we can avoid loading it in the 99% case where we don't need it. + /// + internal static class MessageDecoder { + + /// + /// + /// Returns the symbolic name of the msg value, or null if it + /// isn't one of the existing constants. + /// + private static string MsgToString(int msg) { + string text; + switch (msg) { + case NativeMethods.WM_NULL: text = "WM_NULL"; break; + case NativeMethods.WM_CREATE: text = "WM_CREATE"; break; + case NativeMethods.WM_DESTROY: text = "WM_DESTROY"; break; + case NativeMethods.WM_MOVE: text = "WM_MOVE"; break; + case NativeMethods.WM_SIZE: text = "WM_SIZE"; break; + case NativeMethods.WM_ACTIVATE: text = "WM_ACTIVATE"; break; + //case NativeMethods.WA_INACTIVE: text = "WA_INACTIVE"; break; + //case NativeMethods.WA_ACTIVE: text = "WA_ACTIVE"; break; + //case NativeMethods.WA_CLICKACTIVE: text = "WA_CLICKACTIVE"; break; + case NativeMethods.WM_SETFOCUS: text = "WM_SETFOCUS"; break; + case NativeMethods.WM_KILLFOCUS: text = "WM_KILLFOCUS"; break; + case NativeMethods.WM_ENABLE: text = "WM_ENABLE"; break; + case NativeMethods.WM_SETREDRAW: text = "WM_SETREDRAW"; break; + case NativeMethods.WM_SETTEXT: text = "WM_SETTEXT"; break; + case NativeMethods.WM_GETTEXT: text = "WM_GETTEXT"; break; + case NativeMethods.WM_GETTEXTLENGTH: text = "WM_GETTEXTLENGTH"; break; + case NativeMethods.WM_PAINT: text = "WM_PAINT"; break; + case NativeMethods.WM_CLOSE: text = "WM_CLOSE"; break; + case NativeMethods.WM_QUERYENDSESSION: text = "WM_QUERYENDSESSION"; break; + case NativeMethods.WM_QUIT: text = "WM_QUIT"; break; + case NativeMethods.WM_QUERYOPEN: text = "WM_QUERYOPEN"; break; + case NativeMethods.WM_ERASEBKGND: text = "WM_ERASEBKGND"; break; + case NativeMethods.WM_SYSCOLORCHANGE: text = "WM_SYSCOLORCHANGE"; break; + case NativeMethods.WM_ENDSESSION: text = "WM_ENDSESSION"; break; + case NativeMethods.WM_SHOWWINDOW: text = "WM_SHOWWINDOW"; break; + case NativeMethods.WM_WININICHANGE: text = "WM_WININICHANGE"; break; + //case NativeMethods.WM_SETTINGCHANGE: text = "WM_SETTINGCHANGE"; break; + case NativeMethods.WM_DEVMODECHANGE: text = "WM_DEVMODECHANGE"; break; + case NativeMethods.WM_ACTIVATEAPP: text = "WM_ACTIVATEAPP"; break; + case NativeMethods.WM_FONTCHANGE: text = "WM_FONTCHANGE"; break; + case NativeMethods.WM_TIMECHANGE: text = "WM_TIMECHANGE"; break; + case NativeMethods.WM_CANCELMODE: text = "WM_CANCELMODE"; break; + case NativeMethods.WM_SETCURSOR: text = "WM_SETCURSOR"; break; + case NativeMethods.WM_MOUSEACTIVATE: text = "WM_MOUSEACTIVATE"; break; + case NativeMethods.WM_CHILDACTIVATE: text = "WM_CHILDACTIVATE"; break; + case NativeMethods.WM_QUEUESYNC: text = "WM_QUEUESYNC"; break; + case NativeMethods.WM_GETMINMAXINFO: text = "WM_GETMINMAXINFO"; break; + case NativeMethods.WM_PAINTICON: text = "WM_PAINTICON"; break; + case NativeMethods.WM_ICONERASEBKGND: text = "WM_ICONERASEBKGND"; break; + case NativeMethods.WM_NEXTDLGCTL: text = "WM_NEXTDLGCTL"; break; + case NativeMethods.WM_SPOOLERSTATUS: text = "WM_SPOOLERSTATUS"; break; + case NativeMethods.WM_DRAWITEM: text = "WM_DRAWITEM"; break; + case NativeMethods.WM_MEASUREITEM: text = "WM_MEASUREITEM"; break; + case NativeMethods.WM_DELETEITEM: text = "WM_DELETEITEM"; break; + case NativeMethods.WM_VKEYTOITEM: text = "WM_VKEYTOITEM"; break; + case NativeMethods.WM_CHARTOITEM: text = "WM_CHARTOITEM"; break; + case NativeMethods.WM_SETFONT: text = "WM_SETFONT"; break; + case NativeMethods.WM_GETFONT: text = "WM_GETFONT"; break; + case NativeMethods.WM_SETHOTKEY: text = "WM_SETHOTKEY"; break; + case NativeMethods.WM_GETHOTKEY: text = "WM_GETHOTKEY"; break; + case NativeMethods.WM_QUERYDRAGICON: text = "WM_QUERYDRAGICON"; break; + case NativeMethods.WM_COMPAREITEM: text = "WM_COMPAREITEM"; break; + case NativeMethods.WM_GETOBJECT: text = "WM_GETOBJECT"; break; + case NativeMethods.WM_COMPACTING: text = "WM_COMPACTING"; break; + case NativeMethods.WM_COMMNOTIFY: text = "WM_COMMNOTIFY"; break; + case NativeMethods.WM_WINDOWPOSCHANGING: text = "WM_WINDOWPOSCHANGING"; break; + case NativeMethods.WM_WINDOWPOSCHANGED: text = "WM_WINDOWPOSCHANGED"; break; + case NativeMethods.WM_POWER: text = "WM_POWER"; break; + case NativeMethods.WM_COPYDATA: text = "WM_COPYDATA"; break; + case NativeMethods.WM_CANCELJOURNAL: text = "WM_CANCELJOURNAL"; break; + case NativeMethods.WM_NOTIFY: text = "WM_NOTIFY"; break; + case NativeMethods.WM_INPUTLANGCHANGEREQUEST: text = "WM_INPUTLANGCHANGEREQUEST"; break; + case NativeMethods.WM_INPUTLANGCHANGE: text = "WM_INPUTLANGCHANGE"; break; + case NativeMethods.WM_TCARD: text = "WM_TCARD"; break; + case NativeMethods.WM_HELP: text = "WM_HELP"; break; + case NativeMethods.WM_USERCHANGED: text = "WM_USERCHANGED"; break; + case NativeMethods.WM_NOTIFYFORMAT: text = "WM_NOTIFYFORMAT"; break; + case NativeMethods.WM_CONTEXTMENU: text = "WM_CONTEXTMENU"; break; + case NativeMethods.WM_STYLECHANGING: text = "WM_STYLECHANGING"; break; + case NativeMethods.WM_STYLECHANGED: text = "WM_STYLECHANGED"; break; + case NativeMethods.WM_DISPLAYCHANGE: text = "WM_DISPLAYCHANGE"; break; + case NativeMethods.WM_GETICON: text = "WM_GETICON"; break; + case NativeMethods.WM_SETICON: text = "WM_SETICON"; break; + case NativeMethods.WM_NCCREATE: text = "WM_NCCREATE"; break; + case NativeMethods.WM_NCDESTROY: text = "WM_NCDESTROY"; break; + case NativeMethods.WM_NCCALCSIZE: text = "WM_NCCALCSIZE"; break; + case NativeMethods.WM_NCHITTEST: text = "WM_NCHITTEST"; break; + case NativeMethods.WM_NCPAINT: text = "WM_NCPAINT"; break; + case NativeMethods.WM_NCACTIVATE: text = "WM_NCACTIVATE"; break; + case NativeMethods.WM_GETDLGCODE: text = "WM_GETDLGCODE"; break; + case NativeMethods.WM_NCMOUSEMOVE: text = "WM_NCMOUSEMOVE"; break; + case NativeMethods.WM_NCLBUTTONDOWN: text = "WM_NCLBUTTONDOWN"; break; + case NativeMethods.WM_NCLBUTTONUP: text = "WM_NCLBUTTONUP"; break; + case NativeMethods.WM_NCLBUTTONDBLCLK: text = "WM_NCLBUTTONDBLCLK"; break; + case NativeMethods.WM_NCRBUTTONDOWN: text = "WM_NCRBUTTONDOWN"; break; + case NativeMethods.WM_NCRBUTTONUP: text = "WM_NCRBUTTONUP"; break; + case NativeMethods.WM_NCRBUTTONDBLCLK: text = "WM_NCRBUTTONDBLCLK"; break; + case NativeMethods.WM_NCMBUTTONDOWN: text = "WM_NCMBUTTONDOWN"; break; + case NativeMethods.WM_NCMBUTTONUP: text = "WM_NCMBUTTONUP"; break; + case NativeMethods.WM_NCMBUTTONDBLCLK: text = "WM_NCMBUTTONDBLCLK"; break; + //case NativeMethods.WM_KEYFIRST: text = "WM_KEYFIRST"; break; + case NativeMethods.WM_KEYDOWN: text = "WM_KEYDOWN"; break; + case NativeMethods.WM_KEYUP: text = "WM_KEYUP"; break; + case NativeMethods.WM_CHAR: text = "WM_CHAR"; break; + case NativeMethods.WM_DEADCHAR: text = "WM_DEADCHAR"; break; + case NativeMethods.WM_SYSKEYDOWN: text = "WM_SYSKEYDOWN"; break; + case NativeMethods.WM_SYSKEYUP: text = "WM_SYSKEYUP"; break; + case NativeMethods.WM_SYSCHAR: text = "WM_SYSCHAR"; break; + case NativeMethods.WM_SYSDEADCHAR: text = "WM_SYSDEADCHAR"; break; + case NativeMethods.WM_KEYLAST: text = "WM_KEYLAST"; break; + case NativeMethods.WM_IME_STARTCOMPOSITION: text = "WM_IME_STARTCOMPOSITION"; break; + case NativeMethods.WM_IME_ENDCOMPOSITION: text = "WM_IME_ENDCOMPOSITION"; break; + case NativeMethods.WM_IME_COMPOSITION: text = "WM_IME_COMPOSITION"; break; + //case NativeMethods.WM_IME_KEYLAST: text = "WM_IME_KEYLAST"; break; + case NativeMethods.WM_INITDIALOG: text = "WM_INITDIALOG"; break; + case NativeMethods.WM_COMMAND: text = "WM_COMMAND"; break; + case NativeMethods.WM_SYSCOMMAND: text = "WM_SYSCOMMAND"; break; + case NativeMethods.WM_TIMER: text = "WM_TIMER"; break; + case NativeMethods.WM_HSCROLL: text = "WM_HSCROLL"; break; + case NativeMethods.WM_VSCROLL: text = "WM_VSCROLL"; break; + case NativeMethods.WM_INITMENU: text = "WM_INITMENU"; break; + case NativeMethods.WM_INITMENUPOPUP: text = "WM_INITMENUPOPUP"; break; + case NativeMethods.WM_MENUSELECT: text = "WM_MENUSELECT"; break; + case NativeMethods.WM_MENUCHAR: text = "WM_MENUCHAR"; break; + case NativeMethods.WM_ENTERIDLE: text = "WM_ENTERIDLE"; break; + case NativeMethods.WM_CTLCOLORMSGBOX: text = "WM_CTLCOLORMSGBOX"; break; + case NativeMethods.WM_CTLCOLOREDIT: text = "WM_CTLCOLOREDIT"; break; + case NativeMethods.WM_CTLCOLORLISTBOX: text = "WM_CTLCOLORLISTBOX"; break; + case NativeMethods.WM_CTLCOLORBTN: text = "WM_CTLCOLORBTN"; break; + case NativeMethods.WM_CTLCOLORDLG: text = "WM_CTLCOLORDLG"; break; + case NativeMethods.WM_CTLCOLORSCROLLBAR: text = "WM_CTLCOLORSCROLLBAR"; break; + case NativeMethods.WM_CTLCOLORSTATIC: text = "WM_CTLCOLORSTATIC"; break; + //case NativeMethods.WM_MOUSEFIRST: text = "WM_MOUSEFIRST"; break; + case NativeMethods.WM_MOUSEMOVE: text = "WM_MOUSEMOVE"; break; + case NativeMethods.WM_LBUTTONDOWN: text = "WM_LBUTTONDOWN"; break; + case NativeMethods.WM_LBUTTONUP: text = "WM_LBUTTONUP"; break; + case NativeMethods.WM_LBUTTONDBLCLK: text = "WM_LBUTTONDBLCLK"; break; + case NativeMethods.WM_RBUTTONDOWN: text = "WM_RBUTTONDOWN"; break; + case NativeMethods.WM_RBUTTONUP: text = "WM_RBUTTONUP"; break; + case NativeMethods.WM_RBUTTONDBLCLK: text = "WM_RBUTTONDBLCLK"; break; + case NativeMethods.WM_MBUTTONDOWN: text = "WM_MBUTTONDOWN"; break; + case NativeMethods.WM_MBUTTONUP: text = "WM_MBUTTONUP"; break; + case NativeMethods.WM_MBUTTONDBLCLK: text = "WM_MBUTTONDBLCLK"; break; + case NativeMethods.WM_MOUSEWHEEL: text = "WM_MOUSEWHEEL"; break; + //case NativeMethods.WM_MOUSELAST: text = "WM_MOUSELAST"; break; + case NativeMethods.WM_PARENTNOTIFY: text = "WM_PARENTNOTIFY"; break; + case NativeMethods.WM_ENTERMENULOOP: text = "WM_ENTERMENULOOP"; break; + case NativeMethods.WM_EXITMENULOOP: text = "WM_EXITMENULOOP"; break; + case NativeMethods.WM_NEXTMENU: text = "WM_NEXTMENU"; break; + case NativeMethods.WM_SIZING: text = "WM_SIZING"; break; + case NativeMethods.WM_CAPTURECHANGED: text = "WM_CAPTURECHANGED"; break; + case NativeMethods.WM_MOVING: text = "WM_MOVING"; break; + case NativeMethods.WM_POWERBROADCAST: text = "WM_POWERBROADCAST"; break; + case NativeMethods.WM_DEVICECHANGE: text = "WM_DEVICECHANGE"; break; + case NativeMethods.WM_IME_SETCONTEXT: text = "WM_IME_SETCONTEXT"; break; + case NativeMethods.WM_IME_NOTIFY: text = "WM_IME_NOTIFY"; break; + case NativeMethods.WM_IME_CONTROL: text = "WM_IME_CONTROL"; break; + case NativeMethods.WM_IME_COMPOSITIONFULL: text = "WM_IME_COMPOSITIONFULL"; break; + case NativeMethods.WM_IME_SELECT: text = "WM_IME_SELECT"; break; + case NativeMethods.WM_IME_CHAR: text = "WM_IME_CHAR"; break; + case NativeMethods.WM_IME_KEYDOWN: text = "WM_IME_KEYDOWN"; break; + case NativeMethods.WM_IME_KEYUP: text = "WM_IME_KEYUP"; break; + case NativeMethods.WM_MDICREATE: text = "WM_MDICREATE"; break; + case NativeMethods.WM_MDIDESTROY: text = "WM_MDIDESTROY"; break; + case NativeMethods.WM_MDIACTIVATE: text = "WM_MDIACTIVATE"; break; + case NativeMethods.WM_MDIRESTORE: text = "WM_MDIRESTORE"; break; + case NativeMethods.WM_MDINEXT: text = "WM_MDINEXT"; break; + case NativeMethods.WM_MDIMAXIMIZE: text = "WM_MDIMAXIMIZE"; break; + case NativeMethods.WM_MDITILE: text = "WM_MDITILE"; break; + case NativeMethods.WM_MDICASCADE: text = "WM_MDICASCADE"; break; + case NativeMethods.WM_MDIICONARRANGE: text = "WM_MDIICONARRANGE"; break; + case NativeMethods.WM_MDIGETACTIVE: text = "WM_MDIGETACTIVE"; break; + case NativeMethods.WM_MDISETMENU: text = "WM_MDISETMENU"; break; + case NativeMethods.WM_ENTERSIZEMOVE: text = "WM_ENTERSIZEMOVE"; break; + case NativeMethods.WM_EXITSIZEMOVE: text = "WM_EXITSIZEMOVE"; break; + case NativeMethods.WM_DROPFILES: text = "WM_DROPFILES"; break; + case NativeMethods.WM_MDIREFRESHMENU: text = "WM_MDIREFRESHMENU"; break; + case NativeMethods.WM_MOUSEHOVER: text = "WM_MOUSEHOVER"; break; + case NativeMethods.WM_MOUSELEAVE: text = "WM_MOUSELEAVE"; break; + case NativeMethods.WM_CUT: text = "WM_CUT"; break; + case NativeMethods.WM_COPY: text = "WM_COPY"; break; + case NativeMethods.WM_PASTE: text = "WM_PASTE"; break; + case NativeMethods.WM_CLEAR: text = "WM_CLEAR"; break; + case NativeMethods.WM_UNDO: text = "WM_UNDO"; break; + case NativeMethods.WM_RENDERFORMAT: text = "WM_RENDERFORMAT"; break; + case NativeMethods.WM_RENDERALLFORMATS: text = "WM_RENDERALLFORMATS"; break; + case NativeMethods.WM_DESTROYCLIPBOARD: text = "WM_DESTROYCLIPBOARD"; break; + case NativeMethods.WM_DRAWCLIPBOARD: text = "WM_DRAWCLIPBOARD"; break; + case NativeMethods.WM_PAINTCLIPBOARD: text = "WM_PAINTCLIPBOARD"; break; + case NativeMethods.WM_VSCROLLCLIPBOARD: text = "WM_VSCROLLCLIPBOARD"; break; + case NativeMethods.WM_SIZECLIPBOARD: text = "WM_SIZECLIPBOARD"; break; + case NativeMethods.WM_ASKCBFORMATNAME: text = "WM_ASKCBFORMATNAME"; break; + case NativeMethods.WM_CHANGECBCHAIN: text = "WM_CHANGECBCHAIN"; break; + case NativeMethods.WM_HSCROLLCLIPBOARD: text = "WM_HSCROLLCLIPBOARD"; break; + case NativeMethods.WM_QUERYNEWPALETTE: text = "WM_QUERYNEWPALETTE"; break; + case NativeMethods.WM_PALETTEISCHANGING: text = "WM_PALETTEISCHANGING"; break; + case NativeMethods.WM_PALETTECHANGED: text = "WM_PALETTECHANGED"; break; + case NativeMethods.WM_HOTKEY: text = "WM_HOTKEY"; break; + case NativeMethods.WM_PRINT: text = "WM_PRINT"; break; + case NativeMethods.WM_PRINTCLIENT: text = "WM_PRINTCLIENT"; break; + case NativeMethods.WM_HANDHELDFIRST: text = "WM_HANDHELDFIRST"; break; + case NativeMethods.WM_HANDHELDLAST: text = "WM_HANDHELDLAST"; break; + case NativeMethods.WM_AFXFIRST: text = "WM_AFXFIRST"; break; + case NativeMethods.WM_AFXLAST: text = "WM_AFXLAST"; break; + case NativeMethods.WM_PENWINFIRST: text = "WM_PENWINFIRST"; break; + case NativeMethods.WM_PENWINLAST: text = "WM_PENWINLAST"; break; + case NativeMethods.WM_APP: text = "WM_APP"; break; + case NativeMethods.WM_USER: text = "WM_USER"; break; + + case NativeMethods.WM_CTLCOLOR: text = "WM_CTLCOLOR"; break; + + // RichEdit messages + //case RichTextBoxConstants.WM_CONTEXTMENU: text = "WM_CONTEXTMENU"; break; + + //case RichTextBoxConstants.WM_PRINTCLIENT: text = "WM_PRINTCLIENT"; break; + + case RichTextBoxConstants.EM_GETLIMITTEXT: text = "EM_GETLIMITTEXT"; break; + + case RichTextBoxConstants.EM_POSFROMCHAR: text = "EM_POSFROMCHAR"; break; + case RichTextBoxConstants.EM_CHARFROMPOS: text = "EM_CHARFROMPOS"; break; + + case RichTextBoxConstants.EM_SCROLLCARET: text = "EM_SCROLLCARET"; break; + case RichTextBoxConstants.EM_CANPASTE: text = "EM_CANPASTE"; break; + case RichTextBoxConstants.EM_DISPLAYBAND: text = "EM_DISPLAYBAND"; break; + case RichTextBoxConstants.EM_EXGETSEL: text = "EM_EXGETSEL"; break; + case RichTextBoxConstants.EM_EXLIMITTEXT: text = "EM_EXLIMITTEXT"; break; + case RichTextBoxConstants.EM_EXLINEFROMCHAR: text = "EM_EXLINEFROMCHAR"; break; + case RichTextBoxConstants.EM_EXSETSEL: text = "EM_EXSETSEL"; break; + case RichTextBoxConstants.EM_FINDTEXT: text = "EM_FINDTEXT"; break; + case RichTextBoxConstants.EM_FORMATRANGE: text = "EM_FORMATRANGE"; break; + case RichTextBoxConstants.EM_GETCHARFORMAT: text = "EM_GETCHARFORMAT"; break; + case RichTextBoxConstants.EM_GETEVENTMASK: text = "EM_GETEVENTMASK"; break; + case RichTextBoxConstants.EM_GETOLEINTERFACE: text = "EM_GETOLEINTERFACE"; break; + case RichTextBoxConstants.EM_GETPARAFORMAT: text = "EM_GETPARAFORMAT"; break; + case RichTextBoxConstants.EM_GETSELTEXT: text = "EM_GETSELTEXT"; break; + case RichTextBoxConstants.EM_HIDESELECTION: text = "EM_HIDESELECTION"; break; + case RichTextBoxConstants.EM_PASTESPECIAL: text = "EM_PASTESPECIAL"; break; + case RichTextBoxConstants.EM_REQUESTRESIZE: text = "EM_REQUESTRESIZE"; break; + case RichTextBoxConstants.EM_SELECTIONTYPE: text = "EM_SELECTIONTYPE"; break; + case RichTextBoxConstants.EM_SETBKGNDCOLOR: text = "EM_SETBKGNDCOLOR"; break; + case RichTextBoxConstants.EM_SETCHARFORMAT: text = "EM_SETCHARFORMAT"; break; + case RichTextBoxConstants.EM_SETEVENTMASK: text = "EM_SETEVENTMASK"; break; + case RichTextBoxConstants.EM_SETOLECALLBACK: text = "EM_SETOLECALLBACK"; break; + case RichTextBoxConstants.EM_SETPARAFORMAT: text = "EM_SETPARAFORMAT"; break; + case RichTextBoxConstants.EM_SETTARGETDEVICE: text = "EM_SETTARGETDEVICE"; break; + case RichTextBoxConstants.EM_STREAMIN: text = "EM_STREAMIN"; break; + case RichTextBoxConstants.EM_STREAMOUT: text = "EM_STREAMOUT"; break; + case RichTextBoxConstants.EM_GETTEXTRANGE: text = "EM_GETTEXTRANGE"; break; + case RichTextBoxConstants.EM_FINDWORDBREAK: text = "EM_FINDWORDBREAK"; break; + case RichTextBoxConstants.EM_SETOPTIONS: text = "EM_SETOPTIONS"; break; + case RichTextBoxConstants.EM_GETOPTIONS: text = "EM_GETOPTIONS"; break; + case RichTextBoxConstants.EM_FINDTEXTEX: text = "EM_FINDTEXTEX"; break; + case RichTextBoxConstants.EM_GETWORDBREAKPROCEX: text = "EM_GETWORDBREAKPROCEX"; break; + case RichTextBoxConstants.EM_SETWORDBREAKPROCEX: text = "EM_SETWORDBREAKPROCEX"; break; + + // Richedit v2.0 messages + case RichTextBoxConstants.EM_SETUNDOLIMIT: text = "EM_SETUNDOLIMIT"; break; + case RichTextBoxConstants.EM_REDO: text = "EM_REDO"; break; + case RichTextBoxConstants.EM_CANREDO: text = "EM_CANREDO"; break; + case RichTextBoxConstants.EM_GETUNDONAME: text = "EM_GETUNDONAME"; break; + case RichTextBoxConstants.EM_GETREDONAME: text = "EM_GETREDONAME"; break; + case RichTextBoxConstants.EM_STOPGROUPTYPING: text = "EM_STOPGROUPTYPING"; break; + + case RichTextBoxConstants.EM_SETTEXTMODE: text = "EM_SETTEXTMODE"; break; + case RichTextBoxConstants.EM_GETTEXTMODE: text = "EM_GETTEXTMODE"; break; + + case RichTextBoxConstants.EM_AUTOURLDETECT: text = "EM_AUTOURLDETECT"; break; + case RichTextBoxConstants.EM_GETAUTOURLDETECT: text = "EM_GETAUTOURLDETECT"; break; + case RichTextBoxConstants.EM_SETPALETTE: text = "EM_SETPALETTE"; break; + case RichTextBoxConstants.EM_GETTEXTEX: text = "EM_GETTEXTEX"; break; + case RichTextBoxConstants.EM_GETTEXTLENGTHEX: text = "EM_GETTEXTLENGTHEX"; break; + + // Asia specific messages + case RichTextBoxConstants.EM_SETPUNCTUATION: text = "EM_SETPUNCTUATION"; break; + case RichTextBoxConstants.EM_GETPUNCTUATION: text = "EM_GETPUNCTUATION"; break; + case RichTextBoxConstants.EM_SETWORDWRAPMODE: text = "EM_SETWORDWRAPMODE"; break; + case RichTextBoxConstants.EM_GETWORDWRAPMODE: text = "EM_GETWORDWRAPMODE"; break; + case RichTextBoxConstants.EM_SETIMECOLOR: text = "EM_SETIMECOLOR"; break; + case RichTextBoxConstants.EM_GETIMECOLOR: text = "EM_GETIMECOLOR"; break; + case RichTextBoxConstants.EM_SETIMEOPTIONS: text = "EM_SETIMEOPTIONS"; break; + case RichTextBoxConstants.EM_GETIMEOPTIONS: text = "EM_GETIMEOPTIONS"; break; + case RichTextBoxConstants.EM_CONVPOSITION: text = "EM_CONVPOSITION"; break; + + case RichTextBoxConstants.EM_SETLANGOPTIONS: text = "EM_SETLANGOPTIONS"; break; + case RichTextBoxConstants.EM_GETLANGOPTIONS: text = "EM_GETLANGOPTIONS"; break; + case RichTextBoxConstants.EM_GETIMECOMPMODE: text = "EM_GETIMECOMPMODE"; break; + + case RichTextBoxConstants.EM_FINDTEXTW: text = "EM_FINDTEXTW"; break; + case RichTextBoxConstants.EM_FINDTEXTEXW: text = "EM_FINDTEXTEXW"; break; + + //Rich Edit 3.0 Asia msgs + case RichTextBoxConstants.EM_RECONVERSION: text = "EM_RECONVERSION"; break; + case RichTextBoxConstants.EM_SETIMEMODEBIAS: text = "EM_SETIMEMODEBIAS"; break; + case RichTextBoxConstants.EM_GETIMEMODEBIAS: text = "EM_GETIMEMODEBIAS"; break; + + // BiDi Specific messages + case RichTextBoxConstants.EM_SETBIDIOPTIONS: text = "EM_SETBIDIOPTIONS"; break; + case RichTextBoxConstants.EM_GETBIDIOPTIONS: text = "EM_GETBIDIOPTIONS"; break; + + case RichTextBoxConstants.EM_SETTYPOGRAPHYOPTIONS: text = "EM_SETTYPOGRAPHYOPTIONS"; break; + case RichTextBoxConstants.EM_GETTYPOGRAPHYOPTIONS: text = "EM_GETTYPOGRAPHYOPTIONS"; break; + + // Extended Edit style specific messages + case RichTextBoxConstants.EM_SETEDITSTYLE: text = "EM_SETEDITSTYLE"; break; + case RichTextBoxConstants.EM_GETEDITSTYLE: text = "EM_GETEDITSTYLE"; break; + + default: text = null; break; + } + + if (text == null && ((msg & NativeMethods.WM_REFLECT) == NativeMethods.WM_REFLECT)) { + string subtext = MsgToString(msg - NativeMethods.WM_REFLECT); + if (subtext == null) subtext = "???"; + text = "WM_REFLECT + " + subtext; + } + + return text; + } + + private static string Parenthesize(string input) { + if (input == null) + return ""; + else + return " (" + input + ")"; + } + +#if FALSE + // If you want to use MessageDecoder.ToString(int msg) for debugging uncomment this block. + // Don't forget to comment it back before checking in or else you will have an FxCop error. + public static string ToString(int msg) { + string ID = Parenthesize(MsgToString(msg)); + return "msg=0x" + Convert.ToString(msg, 16) + ID; + } +#endif //FALSE + + public static string ToString(Message message) { + return ToString(message.HWnd, message.Msg, message.WParam, message.LParam, message.Result); + } + + public static string ToString(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam, IntPtr result) { + string ID = Parenthesize(MsgToString(msg)); + + string lDescription = ""; + if (msg == NativeMethods.WM_PARENTNOTIFY) + lDescription = Parenthesize(MsgToString(NativeMethods.Util.LOWORD(wparam))); + + return "msg=0x" + Convert.ToString(msg, 16) + ID + + " hwnd=0x" + Convert.ToString((long)hWnd, 16) + + " wparam=0x" + Convert.ToString((long)wparam, 16) + + " lparam=0x" + Convert.ToString((long)lparam, 16) + lDescription + + " result=0x" + Convert.ToString((long)result, 16); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/MethodInvoker.cs b/WindowsForms/Managed/System/WinForms/MethodInvoker.cs new file mode 100644 index 000000000..79fc7e48f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MethodInvoker.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + /// + /// + /// Represents the method that will handle the + /// event for a method. + /// + public delegate void MethodInvoker(); +} diff --git a/WindowsForms/Managed/System/WinForms/MonthCalendar.cs b/WindowsForms/Managed/System/WinForms/MonthCalendar.cs new file mode 100644 index 000000000..204c2aa5f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MonthCalendar.cs @@ -0,0 +1,2761 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Globalization; + + using System.Windows.Forms.Internal; + using System.ComponentModel; + using System.ComponentModel.Design; + using ArrayList = System.Collections.ArrayList; + + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms.Layout; + + /// + /// + /// This control is an encapsulateion of the Windows month calendar control. + /// A month calendar control implements a calendar-like user interface, that + /// provides the user with a very intuitive and recognizable method of entering + /// or selecting a date. + /// Users can also select which days bold. The most efficient way to add the + /// bolded dates is via an array all at once. (The below descriptions can be applied + /// equally to annually and monthly bolded dates as well) + /// The following is an example of this: + /// + /// MonthCalendar mc = new MonthCalendar(); + /// // add specific dates to bold + /// DateTime[] time = new DateTime[3]; + /// time[0] = DateTime.Now; + /// time[1] = time[0].addDays(2); + /// time[2] = time[1].addDays(2); + /// mc.setBoldedDates(time); + /// + /// Removal of all bolded dates is accomplished with: + /// + /// mc.removeAllBoldedDates(); + /// + /// Although less efficient, the user may need to add or remove bolded dates one at + /// a time. To improve the performance of this, neither addBoldedDate nor + /// removeBoldedDate repaints the monthcalendar. The user must call updateBoldedDates + /// to force the repaint of the bolded dates, otherwise the monthCalendar will not + /// paint properly. + /// The following is an example of this: + /// + /// DateTime time1 = new DateTime("3/5/98"); + /// DateTime time2 = new DateTime("4/19/98"); + /// mc.addBoldedDate(time1); + /// mc.addBoldedDate(time2); + /// mc.removeBoldedDate(time1); + /// mc.updateBoldedDates(); + /// + /// The same applies to addition and removal of annual and monthly bolded dates. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("SelectionRange"), + DefaultEvent("DateChanged"), + DefaultBindingProperty("SelectionRange"), + Designer("System.Windows.Forms.Design.MonthCalendarDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionMonthCalendar) + ] + public class MonthCalendar : Control { + const long DAYS_TO_1601 = 548229; + const long DAYS_TO_10000 = 3615900; + static readonly Color DEFAULT_TITLE_BACK_COLOR = SystemColors.ActiveCaption; + static readonly Color DEFAULT_TITLE_FORE_COLOR = SystemColors.ActiveCaptionText; + static readonly Color DEFAULT_TRAILING_FORE_COLOR = SystemColors.GrayText; + private const int MINIMUM_ALLOC_SIZE = 12; // minimum size to expand the buffer by + private const int MONTHS_IN_YEAR = 12; + /// + /// + /// This is the arbitrary number of pixels that the Win32 control + /// inserts between calendars horizontally, regardless of font. + /// + /// + private const int INSERT_WIDTH_SIZE = 6; + /// + /// + /// This is the arbitrary number of pixels that the Win32 control + /// inserts between calendars vertically, regardless of font. + /// + /// + private const int INSERT_HEIGHT_SIZE = 6; // From comctl32 MonthCalendar sources CALBORDER + private const Day DEFAULT_FIRST_DAY_OF_WEEK = Day.Default; + private const int DEFAULT_MAX_SELECTION_COUNT = 7; + private const int DEFAULT_SCROLL_CHANGE = 0; + private const int UNIQUE_DATE = 0; + private const int ANNUAL_DATE = 1; + private const int MONTHLY_DATE = 2; + + private static readonly Size DefaultSingleMonthSize = new Size(176, 153); + + private const int MaxScrollChange = 20000; + + private const int ExtraPadding = 2; + private int scaledExtraPadding = ExtraPadding; + + private IntPtr mdsBuffer = IntPtr.Zero; + private int mdsBufferSize = 0; + + // styles + private Color titleBackColor = DEFAULT_TITLE_BACK_COLOR; + private Color titleForeColor = DEFAULT_TITLE_FORE_COLOR; + private Color trailingForeColor = DEFAULT_TRAILING_FORE_COLOR; + private bool showToday = true; + private bool showTodayCircle = true; + private bool showWeekNumbers = false; + private bool rightToLeftLayout = false; + + + // properties + private Size dimensions = new Size(1, 1); + private int maxSelectionCount = DEFAULT_MAX_SELECTION_COUNT; + // VSWhidbey 400284: Reconcile out-of-range min/max values in the property getters. + private DateTime maxDate = DateTime.MaxValue; + private DateTime minDate = DateTime.MinValue; + private int scrollChange = DEFAULT_SCROLL_CHANGE; + private bool todayDateSet = false; // Has TodayDate been explicitly set? + private DateTime todayDate = DateTime.Now.Date; + private DateTime selectionStart; + private DateTime selectionEnd; + private Day firstDayOfWeek = DEFAULT_FIRST_DAY_OF_WEEK; + private NativeMethods.MONTCALENDAR_VIEW_MODE mcCurView = NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH; + private NativeMethods.MONTCALENDAR_VIEW_MODE mcOldView = NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH; + + /// + /// + /// Bitmask for the annually bolded dates. Months start on January. + /// + /// + private int[] monthsOfYear = new int[12]; + /// + /// + /// Bitmask for the dates bolded monthly. + /// + /// + private int datesToBoldMonthly = 0; + /// + /// + /// Lists are slow, so this section can be optimized. + /// Implementation is such that inserts are fast, removals are slow. + /// + /// + private ArrayList arrayOfDates = new ArrayList(); + private ArrayList annualArrayOfDates = new ArrayList(); // we have to maintain these lists too. + private ArrayList monthlyArrayOfDates = new ArrayList(); + + // notifications + private DateRangeEventHandler onDateChanged; + private DateRangeEventHandler onDateSelected; + private EventHandler onRightToLeftLayoutChanged; + + private int nativeWndProcCount = 0; + private static bool? restrictUnmanagedCode; + + /// + /// + /// Creates a new MonthCalendar object. Styles are the default for a + /// regular month calendar control. + /// + public MonthCalendar() + : base() { + + PrepareForDrawing(); + + selectionStart = todayDate; + selectionEnd = todayDate; + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.StandardClick, false); + + TabStop = true; + + if (!restrictUnmanagedCode.HasValue) { + bool demandUnmanagedFailed = false; + try { + IntSecurity.UnmanagedCode.Demand(); + restrictUnmanagedCode = false; + } + catch { + // ensure that the static field is written to exactly once to avoid race conditions + demandUnmanagedFailed = true; + } + + if (demandUnmanagedFailed) { + // Demand for unmanaged code failed, thus we are running in partial trust. + // We need to assert a registry access permission, this is safe because + // we are reading the registry and are not returning the information + // from the registry to the user. + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try { + // for 32 bit applications on 64 bit machines this code is reading + // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node node + + // to opt out, set a DWORD value AllowWindowsFormsReentrantDestroy=1 + RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"); + if (key != null) { + object o = key.GetValue("AllowWindowsFormsReentrantDestroy"); + if ((o != null) && (o is int) && ((int)o == 1)) { + restrictUnmanagedCode = false; + } + else { + restrictUnmanagedCode = true; + } + } + else { + restrictUnmanagedCode = true; + } + } + catch { + restrictUnmanagedCode = true; + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + } + + /// + /// MonthCalendar control accessbile object. + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level1) { + return new MonthCalendarAccessibleObject(this); + } + else { + return base.CreateAccessibilityInstance(); + } + } + + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + PrepareForDrawing(); + } + + private void PrepareForDrawing() { + if (DpiHelper.EnableMonthCalendarHighDpiImprovements) { + scaledExtraPadding = LogicalToDeviceUnits(ExtraPadding); + } + } + + /// + /// + /// The array of DateTime objects that determines which annual days are shown + /// in bold. + /// + [ + Localizable(true), + SRDescription(SR.MonthCalendarAnnuallyBoldedDatesDescr) + ] + public DateTime[] AnnuallyBoldedDates { + get { + DateTime[] dateTimes = new DateTime[annualArrayOfDates.Count]; + + for (int i=0;i < annualArrayOfDates.Count; ++i) { + dateTimes[i] = (DateTime)this.annualArrayOfDates[i]; + } + return dateTimes; + } + set { + // + + + + this.annualArrayOfDates.Clear(); + for (int i=0; i 0) { + + //add each boldeddate to our ArrayList... + for (int i = 0; i < value.Length; i++) { + this.annualArrayOfDates.Add(value[i]); + } + + for (int i = 0; i < value.Length; ++i) { + monthsOfYear[value[i].Month-1] |= 0x00000001<<(value[i].Day-1); + } + + } + RecreateHandle(); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRDescription(SR.MonthCalendarMonthBackColorDescr)] + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + + /// + /// + /// The array of DateTime objects that determines which non-recurring + /// specified dates are shown in bold. + /// + /*Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),*/ + [Localizable(true)] + public DateTime[] BoldedDates { + get { + DateTime[] dateTimes = new DateTime[arrayOfDates.Count]; + + for (int i=0;i < arrayOfDates.Count; ++i) { + dateTimes[i] = (DateTime)this.arrayOfDates[i]; + } + return dateTimes; + } + set { + // + + + + this.arrayOfDates.Clear(); + if (value != null && value.Length > 0) { + + //add each boldeddate to our ArrayList... + for (int i = 0; i < value.Length; i++) { + this.arrayOfDates.Add(value[i]); + } + + } + RecreateHandle(); + } + } + + /// + /// + /// The number of columns and rows of months that will be displayed + /// in the MonthCalendar control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.MonthCalendarDimensionsDescr) + ] + public Size CalendarDimensions { + get { + return dimensions; + } + set { + if (!this.dimensions.Equals(value)) + SetCalendarDimensions(value.Width, value.Height); + } + } + + /// + /// + /// This is called when creating a window. Inheriting classes can ovveride + /// this to add extra functionality, but should not forget to first call + /// base.getCreateParams() to make sure the control continues to work + /// correctly. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_MONTHCAL; + cp.Style |= NativeMethods.MCS_MULTISELECT | NativeMethods.MCS_DAYSTATE; + if (!showToday) cp.Style |= NativeMethods.MCS_NOTODAY; + if (!showTodayCircle) cp.Style |= NativeMethods.MCS_NOTODAYCIRCLE; + if (showWeekNumbers) cp.Style |= NativeMethods.MCS_WEEKNUMBERS; + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + protected override Padding DefaultMargin { + get { return new Padding(9); } + } + + /// + protected override Size DefaultSize { + get { + return GetMinReqRect(); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// The first day of the week for the month calendar control. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(DEFAULT_FIRST_DAY_OF_WEEK), + SRDescription(SR.MonthCalendarFirstDayOfWeekDescr) + ] + public Day FirstDayOfWeek { + get { + return firstDayOfWeek; + } + + set { + //valid values are 0x0 to 0x7 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)Day.Monday, (int)Day.Default)){ + throw new InvalidEnumArgumentException("FirstDayOfWeek", (int)value, typeof(Day)); + } + + if (value != firstDayOfWeek) { + firstDayOfWeek = value; + if (IsHandleCreated) { + if (value == Day.Default) { + RecreateHandle(); + } + else { + SendMessage(NativeMethods.MCM_SETFIRSTDAYOFWEEK, 0, (int) value); + } + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRDescription(SR.MonthCalendarForeColorDescr)] + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// The maximum allowable date that can be selected. By default, there + /// is no maximum date. The maximum date is not set if max less than the + /// current minimum date. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MonthCalendarMaxDateDescr) + ] + public DateTime MaxDate { + get { + return DateTimePicker.EffectiveMaxDate(maxDate); + } + set { + if (value != maxDate) { + if (value < DateTimePicker.EffectiveMinDate(minDate)) { + throw new ArgumentOutOfRangeException("MaxDate", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxDate", FormatDate(value), "MinDate")); + } + maxDate = value; + SetRange(); + } + } + } + + /// + /// + /// The maximum number of days that can be selected in a + /// month calendar control. This method does not affect the current + /// selection range. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DEFAULT_MAX_SELECTION_COUNT), + SRDescription(SR.MonthCalendarMaxSelectionCountDescr) + ] + public int MaxSelectionCount { + get { + return maxSelectionCount; + } + set { + if (value < 1) { + throw new ArgumentOutOfRangeException("MaxSelectionCount", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxSelectionCount", (value).ToString("D", CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + + if (value != maxSelectionCount) { + if (IsHandleCreated) { + if (unchecked( (int) (long)SendMessage(NativeMethods.MCM_SETMAXSELCOUNT, value, 0)) == 0) + throw new ArgumentException(SR.GetString(SR.MonthCalendarMaxSelCount, (value).ToString("D", CultureInfo.CurrentCulture)), "MaxSelectionCount"); + } + maxSelectionCount = value; + } + } + } + + /// + /// + /// The minimum allowable date that can be selected. By default, there + /// is no minimum date. The minimum date is not set if min greater than the + /// current maximum date. MonthCalendar does not support dates prior to 1753. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MonthCalendarMinDateDescr) + ] + public DateTime MinDate { + get { + return DateTimePicker.EffectiveMinDate(minDate); + } + set { + if (value != minDate) { + if (value > DateTimePicker.EffectiveMaxDate(maxDate)) { + throw new ArgumentOutOfRangeException("MinDate", SR.GetString(SR.InvalidHighBoundArgument, "MinDate", FormatDate(value), "MaxDate")); + } + + // If trying to set the minimum less than DateTimePicker.MinimumDateTime, throw + // an exception. + if (value < DateTimePicker.MinimumDateTime) { + throw new ArgumentOutOfRangeException("MinDate", SR.GetString(SR.InvalidLowBoundArgumentEx, "MinDate", FormatDate(value), FormatDate(DateTimePicker.MinimumDateTime))); + } + + minDate = value; + SetRange(); + } + } + } + + /// + /// + /// The array of DateTime objects that determine which monthly days to bold. + /// + [ + Localizable(true), + SRDescription(SR.MonthCalendarMonthlyBoldedDatesDescr) + ] + public DateTime[] MonthlyBoldedDates { + get { + DateTime[] dateTimes = new DateTime[monthlyArrayOfDates.Count]; + + for (int i=0;i < monthlyArrayOfDates.Count; ++i) { + dateTimes[i] = (DateTime)this.monthlyArrayOfDates[i]; + } + return dateTimes; + } + set { + // + + + + this.monthlyArrayOfDates.Clear(); + datesToBoldMonthly = 0; + + if (value != null && value.Length > 0) { + + //add each boldeddate to our ArrayList... + for (int i = 0; i < value.Length; i++) { + this.monthlyArrayOfDates.Add(value[i]); + } + + for (int i = 0; i < value.Length; ++i) { + datesToBoldMonthly |= 0x00000001<<(value[i].Day-1); + } + + } + RecreateHandle(); + } + } + + /// + /// + /// + /// + private DateTime Now { + get { + return DateTime.Now.Date; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// The scroll rate for a month calendar control. The scroll + /// rate is the number of months that the control moves its display + /// when the user clicks a scroll button. If this value is zero, + /// the month delta is reset to the default, which is the number of + /// months displayed in the control. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DEFAULT_SCROLL_CHANGE), + SRDescription(SR.MonthCalendarScrollChangeDescr) + ] + public int ScrollChange { + get { + return scrollChange; + } + set { + if (scrollChange != value) { + + if (value < 0) { + throw new ArgumentOutOfRangeException("ScrollChange", SR.GetString(SR.InvalidLowBoundArgumentEx, "ScrollChange", (value).ToString("D", CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (value > MaxScrollChange) { + throw new ArgumentOutOfRangeException("ScrollChange", SR.GetString(SR.InvalidHighBoundArgumentEx, "ScrollChange", (value).ToString("D", CultureInfo.CurrentCulture), (MaxScrollChange).ToString("D", CultureInfo.CurrentCulture))); + } + + if (IsHandleCreated) { + SendMessage(NativeMethods.MCM_SETMONTHDELTA, value, 0); + } + scrollChange = value; + } + } + } + + + /// + /// + /// Indicates the end date of the selected range of dates. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MonthCalendarSelectionEndDescr) + ] + public DateTime SelectionEnd { + get { + return selectionEnd; + } + set { + if (selectionEnd != value) { + + // Keep SelectionEnd within min and max + if (value < MinDate) { + throw new ArgumentOutOfRangeException("SelectionEnd", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionEnd", FormatDate(value), "MinDate")); + } + if (value > MaxDate) { + throw new ArgumentOutOfRangeException("SelectionEnd", SR.GetString(SR.InvalidHighBoundArgumentEx, "SelectionEnd", FormatDate(value), "MaxDate")); + } + + // If we've moved SelectionEnd before SelectionStart, move SelectionStart back + if (selectionStart > value) { + selectionStart = value; + } + + // If we've moved SelectionEnd too far beyond SelectionStart, move SelectionStart forward + if ((value - selectionStart).Days >= maxSelectionCount) { + selectionStart = value.AddDays(1 - maxSelectionCount); + } + + // Set the new selection range + SetSelRange(selectionStart, value); + } + } + } + + /// + /// + /// + /// Indicates + /// the start date of the selected range of dates. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MonthCalendarSelectionStartDescr) + ] + public DateTime SelectionStart { + get { + return selectionStart; + } + set { + if (selectionStart != value) { + + // Keep SelectionStart within min and max + // + if (value < minDate) { + throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionStart", FormatDate(value), "MinDate")); + } + if (value > maxDate) { + throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidHighBoundArgumentEx, "SelectionStart", FormatDate(value), "MaxDate")); + } + + // If we've moved SelectionStart beyond SelectionEnd, move SelectionEnd forward + if (selectionEnd < value) { + selectionEnd = value; + } + + // If we've moved SelectionStart too far back from SelectionEnd, move SelectionEnd back + if ((selectionEnd - value).Days >= maxSelectionCount) { + selectionEnd = value.AddDays(maxSelectionCount - 1); + } + + // Set the new selection range + SetSelRange(value, selectionEnd); + } + } + } + + /// + /// + /// Retrieves the selection range for a month calendar control. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MonthCalendarSelectionRangeDescr), + Bindable(true) + ] + public SelectionRange SelectionRange { + get { + return new SelectionRange(SelectionStart, SelectionEnd); + } + set { + SetSelectionRange(value.Start, value.End); + } + } + + /// + /// + /// Indicates whether the month calendar control will display + /// the "today" date at the bottom of the control. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.MonthCalendarShowTodayDescr) + ] + public bool ShowToday { + get { + return showToday; + } + set { + if (showToday != value) { + showToday = value; + UpdateStyles(); + AdjustSize(); + } + } + } + + /// + /// + /// Indicates whether the month calendar control will circle + /// the "today" date. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.MonthCalendarShowTodayCircleDescr) + ] + public bool ShowTodayCircle { + get { + return showTodayCircle; + } + set { + if (showTodayCircle != value) { + showTodayCircle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// Indicates whether the month calendar control will the display + /// week numbers (1-52) to the left of each row of days. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(false), + SRDescription(SR.MonthCalendarShowWeekNumbersDescr) + ] + public bool ShowWeekNumbers { + get { + return showWeekNumbers; + } + set { + if (showWeekNumbers != value) { + showWeekNumbers = value; + UpdateStyles(); + AdjustSize(); + } + } + } + + /// + /// + /// The minimum size required to display a full month. The size + /// information is presented in the form of a Point, with the x + /// and y members representing the minimum width and height required + /// for the control. The minimum required window size for a month calendar + /// control depends on the currently selected font. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MonthCalendarSingleMonthSizeDescr) + ] + public Size SingleMonthSize { + get { + NativeMethods.RECT rect = new NativeMethods.RECT(); + + if (IsHandleCreated) { + + if (unchecked( (int) (long)SendMessage(NativeMethods.MCM_GETMINREQRECT, 0, ref rect)) == 0) + throw new InvalidOperationException(SR.GetString(SR.InvalidSingleMonthSize)); + + return new Size(rect.right, rect.bottom); + } + + return DefaultSingleMonthSize; + } + } + + /// + /// + /// Unlike most controls, serializing the MonthCalendar's Size is really bad: + /// when it's restored at runtime, it uses a a default SingleMonthSize, which + /// may not be right, especially for JPN/CHS machines. See VSWhidbey 527753. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Localizable(false) + ] + public new Size Size { + get { + return base.Size; + } + set { + base.Size = value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The date shown as "Today" in the Month Calendar control. + /// By default, "Today" is the current date at the time + /// the MonthCalendar control is created. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.MonthCalendarTodayDateDescr) + ] + public DateTime TodayDate { + get { + if (todayDateSet) return todayDate; + if (IsHandleCreated) { + NativeMethods.SYSTEMTIME st = new NativeMethods.SYSTEMTIME(); + int res = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_GETTODAY, 0, st); + Debug.Assert(res != 0, "MCM_GETTODAY failed"); + return DateTimePicker.SysTimeToDateTime(st).Date; + } + else return Now.Date; + } + set { + if (!(todayDateSet) || (DateTime.Compare(value, todayDate) != 0)) { + + // throw if trying to set the TodayDate to a value greater than MaxDate + if (DateTime.Compare(value, maxDate) > 0) { + throw new ArgumentOutOfRangeException("TodayDate", SR.GetString(SR.InvalidHighBoundArgumentEx, "TodayDate", FormatDate(value), FormatDate(maxDate))); + } + + // throw if trying to set the TodayDate to a value less than MinDate + if (DateTime.Compare(value, minDate) < 0) { + throw new ArgumentOutOfRangeException("TodayDate", SR.GetString(SR.InvalidLowBoundArgument, "TodayDate", FormatDate(value), FormatDate(minDate))); + } + + todayDate = value.Date; + todayDateSet = true; + UpdateTodayDate(); + } + } + } + + /// + /// + /// Indicates whether or not the TodayDate property has been explicitly + /// set by the user. If TodayDateSet is true, TodayDate will return whatever + /// the user has set it to. If TodayDateSet is false, TodayDate will follow + /// wall-clock time; ie. TodayDate will always equal the current system date. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.MonthCalendarTodayDateSetDescr) + ] + public bool TodayDateSet { + get { + return todayDateSet; + } + } + + /// + /// + /// The background color displayed in the month calendar's + /// title. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.MonthCalendarTitleBackColorDescr) + ] + public Color TitleBackColor { + get { + return titleBackColor; + } + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + titleBackColor = value; + SetControlColor(NativeMethods.MCSC_TITLEBK, value); + } + } + + /// + /// + /// The foreground color used to display text within the month + /// calendar's title. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.MonthCalendarTitleForeColorDescr) + ] + public Color TitleForeColor { + get { + return titleForeColor; + } + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + titleForeColor = value; + SetControlColor(NativeMethods.MCSC_TITLETEXT, value); + } + } + + /// + /// + /// The color used to display the previous and following months that + /// appear on the current month calendar. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.MonthCalendarTrailingForeColorDescr) + ] + public Color TrailingForeColor { + get { + return trailingForeColor; + } + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, + "value")); + } + trailingForeColor = value; + SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, value); + } + } + + /// + /// + /// Adds a day that will be bolded annually on the month calendar. + /// Be sure to call updateBoldedDates() afterwards. + /// + public void AddAnnuallyBoldedDate(DateTime date) { + annualArrayOfDates.Add(date); + monthsOfYear[date.Month-1] |= 0x00000001<<(date.Day-1); + } + + /// + /// + /// Adds a day that will be bolded on the month calendar. + /// Be sure to call updateBoldedDates() afterwards. + /// + public void AddBoldedDate(DateTime date) { + if (!this.arrayOfDates.Contains(date)) { + this.arrayOfDates.Add(date); + } + } + + /// + /// + /// Adds a day that will be bolded monthly on the month calendar. + /// Be sure to call updateBoldedDates() afterwards. + /// + public void AddMonthlyBoldedDate(DateTime date) { + this.monthlyArrayOfDates.Add(date); + datesToBoldMonthly |= 0x00000001<<(date.Day-1); + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.MonthCalendarOnDateChangedDescr)] + public event DateRangeEventHandler DateChanged { + add { + onDateChanged += value; + } + remove { + onDateChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.MonthCalendarOnDateSelectedDescr)] + public event DateRangeEventHandler DateSelected { + add { + onDateSelected += value; + } + remove { + onDateSelected -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + /// MonthCalendar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + onRightToLeftLayoutChanged += value; + } + remove { + onRightToLeftLayoutChanged -= value; + } + } + + + /// + /// + /// Used to auto-size the control. The requested number of rows and columns are + /// restricted by the maximum size of the parent control, hence the requested number + /// of rows and columns may not be what you get. + /// + /// + private void AdjustSize() { + Size minSize = GetMinReqRect(); + Size = minSize; + } + + /// + /// + /// Event handler that bolds dates indicated by arrayOfDates + /// + /// + private void BoldDates(DateBoldEventArgs e) { + int months = e.Size; + e.DaysToBold = new int[months]; + SelectionRange range = GetDisplayRange(false); + int startMonth = range.Start.Month; + int startYear = range.Start.Year; + int numDates = arrayOfDates.Count; + for (int i=0; i= 0 && DateTime.Compare(date, range.End) <= 0) { + int month = date.Month; + int year = date.Year; + int index = (year == startYear) ? month - startMonth : month + year*MONTHS_IN_YEAR - startYear*MONTHS_IN_YEAR - startMonth; + e.DaysToBold[index] |= (0x00000001<<(date.Day-1)); + } + } + //now we figure out which monthly and annual dates to bold + --startMonth; + for (int i=0; i + /// + /// Compares only the day and month of each time. + /// + /// + private bool CompareDayAndMonth(DateTime t1, DateTime t2) { + return(t1.Day == t2.Day && t1.Month == t2.Month); + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_DATE_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// Called to cleanup a MonthCalendar. Normally you do not need + /// to call this as the garbage collector will cleanup the buffer + /// for you. However, there may be times when you may want to expedite + /// the garbage collectors cleanup. + /// + protected override void Dispose(bool disposing) { + if (mdsBuffer != IntPtr.Zero) { + Marshal.FreeHGlobal(mdsBuffer); + mdsBuffer = IntPtr.Zero; + } + base.Dispose(disposing); + } + + // Return a localized string representation of the given DateTime value. + // Used for throwing exceptions, etc. + // + private static string FormatDate(DateTime value) { + return value.ToString("d", CultureInfo.CurrentCulture); + } + + /// + /// + /// Retrieves date information that represents the low and high limits of the + /// control's display. + /// + public SelectionRange GetDisplayRange(bool visible) { + if (visible) + return GetMonthRange(NativeMethods.GMR_VISIBLE); + else + return GetMonthRange(NativeMethods.GMR_DAYSTATE); + } + + /// + /// + /// Retrieves the enumeration value corresponding to the hit area. + /// + /// + private HitArea GetHitArea(int hit) { + switch (hit) { + case NativeMethods.MCHT_TITLEBK: + return HitArea.TitleBackground; + case NativeMethods.MCHT_TITLEMONTH: + return HitArea.TitleMonth; + case NativeMethods.MCHT_TITLEYEAR: + return HitArea.TitleYear; + case NativeMethods.MCHT_TITLEBTNNEXT: + return HitArea.NextMonthButton; + case NativeMethods.MCHT_TITLEBTNPREV: + return HitArea.PrevMonthButton; + case NativeMethods.MCHT_CALENDARBK: + return HitArea.CalendarBackground; + case NativeMethods.MCHT_CALENDARDATE: + return HitArea.Date; + case NativeMethods.MCHT_CALENDARDATENEXT: + return HitArea.NextMonthDate; + case NativeMethods.MCHT_CALENDARDATEPREV: + return HitArea.PrevMonthDate; + case NativeMethods.MCHT_CALENDARDAY: + return HitArea.DayOfWeek; + case NativeMethods.MCHT_CALENDARWEEKNUM: + return HitArea.WeekNumbers; + case NativeMethods.MCHT_TODAYLINK: + return HitArea.TodayLink; + default: + return HitArea.Nowhere; + } + } + + /// + /// + /// stub for getMinReqRect (int, boolean) + /// + /// + private Size GetMinReqRect() { + return GetMinReqRect(0, false, false); + } + + /// + /// + /// Used internally to get the minimum size needed to display the + /// MonthCalendar. This is needed because + /// NativeMethods.MCM_GETMINREQRECT returns an incorrect value if showToday + /// is set to false. If updateRows is true, then the + /// number of rows will be updated according to height. + /// + /// + private Size GetMinReqRect(int newDimensionLength, bool updateRows, bool updateCols) { + Size minSize = SingleMonthSize; + + // Calculate calendar height + // + Size textExtent; + using (WindowsFont font = WindowsFont.FromFont(this.Font)) + { + // this is the string that Windows uses to determine the extent of the today string + textExtent = WindowsGraphicsCacheManager.MeasurementGraphics.GetTextExtent(DateTime.Now.ToShortDateString(), font); + } + int todayHeight = textExtent.Height + 4; // The constant 4 is from the comctl32 MonthCalendar source code + int calendarHeight = minSize.Height; + if (ShowToday) { + // If ShowToday is true, then minSize already includes the height of the today string. + // So we remove it to get the actual calendar height. + // + calendarHeight -= todayHeight; + } + + if (updateRows) { + Debug.Assert(calendarHeight > INSERT_HEIGHT_SIZE, "Divide by 0"); + int nRows = (newDimensionLength - todayHeight + INSERT_HEIGHT_SIZE)/(calendarHeight + INSERT_HEIGHT_SIZE); + this.dimensions.Height = (nRows < 1) ? 1 : nRows; + } + + if (updateCols) { + Debug.Assert(minSize.Width > INSERT_WIDTH_SIZE, "Divide by 0"); + int nCols = (newDimensionLength - scaledExtraPadding) /minSize.Width; + this.dimensions.Width = (nCols < 1) ? 1 : nCols; + } + + minSize.Width = (minSize.Width + INSERT_WIDTH_SIZE) * dimensions.Width - INSERT_WIDTH_SIZE; + minSize.Height = (calendarHeight + INSERT_HEIGHT_SIZE) * dimensions.Height - INSERT_HEIGHT_SIZE + todayHeight; + + // If the width we've calculated is too small to fit the Today string, enlarge the width to fit + // + if (IsHandleCreated) { + int maxTodayWidth = unchecked( (int) (long)SendMessage(NativeMethods.MCM_GETMAXTODAYWIDTH, 0, 0)); + if (maxTodayWidth > minSize.Width) { + minSize.Width = maxTodayWidth; + } + } + + // Fudge factor + // + minSize.Width += scaledExtraPadding; + minSize.Height += scaledExtraPadding; + return minSize; + } + + /// + /// + /// + /// + private SelectionRange GetMonthRange(int flag) { + NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); + SelectionRange range = new SelectionRange(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_GETMONTHRANGE, flag, sa); + + NativeMethods.SYSTEMTIME st = new NativeMethods.SYSTEMTIME(); + st.wYear = sa.wYear1; + st.wMonth = sa.wMonth1; + st.wDayOfWeek = sa.wDayOfWeek1; + st.wDay = sa.wDay1; + + range.Start = DateTimePicker.SysTimeToDateTime(st); + st.wYear = sa.wYear2; + st.wMonth = sa.wMonth2; + st.wDayOfWeek = sa.wDayOfWeek2; + st.wDay = sa.wDay2; + range.End = DateTimePicker.SysTimeToDateTime(st); + + return range; + } + + /// + /// + /// Called by setBoundsCore. If updateRows is true, then the + /// number of rows will be updated according to height. + /// + /// + private int GetPreferredHeight(int height, bool updateRows) { + Size preferredSize = GetMinReqRect(height, updateRows, false); + return preferredSize.Height; + } + + /// + /// + /// Called by setBoundsCore. If updateCols is true, then the + /// number of columns will be updated according to width. + /// + /// + private int GetPreferredWidth(int width, bool updateCols) { + Size preferredSize = GetMinReqRect(width, false, updateCols); + return preferredSize.Width; + } + + /// + /// + /// Determines which portion of a month calendar control is at + /// at a given point on the screen. + /// + public HitTestInfo HitTest(int x, int y) { + NativeMethods.MCHITTESTINFO mchi = new NativeMethods.MCHITTESTINFO(); + mchi.pt_x = x; + mchi.pt_y = y; + mchi.cbSize = Marshal.SizeOf(typeof(NativeMethods.MCHITTESTINFO)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_HITTEST, 0, mchi); + + // If the hit area has an associated valid date, get it + // + HitArea hitArea = GetHitArea(mchi.uHit); + if (HitTestInfo.HitAreaHasValidDateTime(hitArea)) { + NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME(); + sys.wYear = mchi.st_wYear; + sys.wMonth = mchi.st_wMonth; + sys.wDayOfWeek = mchi.st_wDayOfWeek; + sys.wDay = mchi.st_wDay; + sys.wHour = mchi.st_wHour; + sys.wMinute = mchi.st_wMinute; + sys.wSecond = mchi.st_wSecond; + sys.wMilliseconds = mchi.st_wMilliseconds; + return new HitTestInfo(new Point(mchi.pt_x, mchi.pt_y), hitArea, DateTimePicker.SysTimeToDateTime(sys)); + } + else { + return new HitTestInfo(new Point(mchi.pt_x, mchi.pt_y), hitArea); + } + } + + /// + /// + /// Determines which portion of a month calendar control is at + /// at a given point on the screen. + /// + public HitTestInfo HitTest(Point point) { + return HitTest(point.X, point.Y); + } + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// Overrides Control.OnHandleCreated() + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SetSelRange(selectionStart, selectionEnd); + if (maxSelectionCount != DEFAULT_MAX_SELECTION_COUNT) { + SendMessage(NativeMethods.MCM_SETMAXSELCOUNT, maxSelectionCount, 0); + } + AdjustSize(); + + if (todayDateSet) { + NativeMethods.SYSTEMTIME st = DateTimePicker.DateTimeToSysTime(todayDate); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETTODAY, 0, st); + } + + SetControlColor(NativeMethods.MCSC_TEXT, ForeColor); + SetControlColor(NativeMethods.MCSC_MONTHBK, BackColor); + SetControlColor(NativeMethods.MCSC_TITLEBK, titleBackColor); + SetControlColor(NativeMethods.MCSC_TITLETEXT, titleForeColor); + SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, trailingForeColor); + + int firstDay; + if (firstDayOfWeek == Day.Default) { + firstDay = NativeMethods.LOCALE_IFIRSTDAYOFWEEK; + } + else { + firstDay = (int)firstDayOfWeek; + } + SendMessage(NativeMethods.MCM_SETFIRSTDAYOFWEEK, 0, firstDay); + + SetRange(); + if (scrollChange != DEFAULT_SCROLL_CHANGE) { + SendMessage(NativeMethods.MCM_SETMONTHDELTA, scrollChange, 0); + } + + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.MarshaledUserPreferenceChanged); + } + + + /// + /// + /// Overrides Control.OnHandleDestroyed() + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.MarshaledUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + + /// + /// + /// Fires the event indicating that the currently selected date + /// or range of dates has changed. + /// + protected virtual void OnDateChanged(DateRangeEventArgs drevent) { + if (onDateChanged != null) { + onDateChanged(this, drevent); + } + } + + /// + /// + /// Fires the event indicating that the user has changed his\her selection. + /// + protected virtual void OnDateSelected(DateRangeEventArgs drevent) { + if (onDateSelected != null) { + onDateSelected(this, drevent); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + AdjustSize(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnForeColorChanged(EventArgs e) { + base.OnForeColorChanged(e); + SetControlColor(NativeMethods.MCSC_TEXT, ForeColor); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + SetControlColor(NativeMethods.MCSC_MONTHBK, BackColor); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + if (onRightToLeftLayoutChanged != null) { + onRightToLeftLayoutChanged(this, e); + } + } + + + + /// + /// + /// Removes all annually bolded days. Be sure to call updateBoldedDates() afterwards. + /// + public void RemoveAllAnnuallyBoldedDates() { + this.annualArrayOfDates.Clear(); + for (int i=0; i + /// + /// Removes all the bolded days. Be sure to call updateBoldedDates() afterwards. + /// + public void RemoveAllBoldedDates() { + this.arrayOfDates.Clear(); + } + + /// + /// + /// Removes all monthly bolded days. Be sure to call updateBoldedDates() afterwards. + /// + public void RemoveAllMonthlyBoldedDates() { + this.monthlyArrayOfDates.Clear(); + datesToBoldMonthly = 0; + } + + /// + /// + /// Removes an annually bolded date. If the date is not found in the + /// bolded date list, then no action is taken. If date occurs more than + /// once in the bolded date list, then only the first date is removed. When + /// comparing dates, only the day and month are used. Be sure to call + /// updateBoldedDates afterwards. + /// + public void RemoveAnnuallyBoldedDate(DateTime date) { + int length = annualArrayOfDates.Count; + int i=0; + for (; i + /// + /// Removes a bolded date. If the date is not found in the + /// bolded date list, then no action is taken. If date occurs more than + /// once in the bolded date list, then only the first date is removed. + /// Be sure to call updateBoldedDates() afterwards. + /// + public void RemoveBoldedDate(DateTime date) { + int length = arrayOfDates.Count; + for (int i=0; i + /// + /// Removes a monthly bolded date. If the date is not found in the + /// bolded date list, then no action is taken. If date occurs more than + /// once in the bolded date list, then only the first date is removed. When + /// comparing dates, only the day and month are used. Be sure to call + /// updateBoldedDates afterwards. + /// + public void RemoveMonthlyBoldedDate(DateTime date) { + int length = monthlyArrayOfDates.Count; + int i=0; + for (; i + /// + /// Resets the maximum selectable date. By default value, there is no + /// upper limit. + /// + private void ResetMaxDate() { + MaxDate = DateTime.MaxValue; + } + + /// + /// + /// Resets the minimum selectable date. By default value, there is no + /// lower limit. + /// + private void ResetMinDate() { + MinDate = DateTime.MinValue; + } + + + private void ResetMonthlyBoldedDates() { + monthlyArrayOfDates.Clear(); + } + + /// + /// + /// Resets the limits of the selection range. By default value, the upper + /// and lower limit is the current date. + /// + private void ResetSelectionRange() { + SetSelectionRange(Now, Now); + } + + private void ResetTrailingForeColor() { + TrailingForeColor = DEFAULT_TRAILING_FORE_COLOR; + } + + private void ResetTitleForeColor() { + TitleForeColor = DEFAULT_TITLE_FORE_COLOR; + } + + private void ResetTitleBackColor() { + TitleBackColor = DEFAULT_TITLE_BACK_COLOR; + } + + /// + /// + /// Resets the "today"'s date. By default value, "today" is the + /// current date (and is automatically updated when the clock crosses + /// over to the next day). + /// If you set the today date yourself (using the TodayDate property) + /// the control will no longer automatically update the current day + /// for you. To re-enable this behavior, ResetTodayDate() is used. + /// + private void ResetTodayDate() { + todayDateSet = false; + UpdateTodayDate(); + } + + /// + /// + /// reqSize = # elements in int[] array + /// + /// The size argument should be greater than 0. + /// Because of the nature of MonthCalendar, we can expect that + /// the requested size will not be ridiculously large, hence + /// it is not necessary to decrease the size of an allocated + /// block if the new requested size is smaller. + /// + /// + private IntPtr RequestBuffer(int reqSize) { + Debug.Assert(reqSize > 0, "Requesting a ridiculously small buffer"); + int intSize = 4; + // if the current buffer size is insufficient... + if (reqSize * intSize > mdsBufferSize) { + // free and expand the buffer, + if (mdsBuffer != IntPtr.Zero) { + Marshal.FreeHGlobal(mdsBuffer); + mdsBuffer = IntPtr.Zero; + } + + // Round up to the nearest multiple of MINIMUM_ALLOC_SIZE + float quotient = (float) (reqSize-1) / MINIMUM_ALLOC_SIZE; + int actualSize = ((int) (quotient+1)) * MINIMUM_ALLOC_SIZE; + Debug.Assert(actualSize >= reqSize, "Tried to round up, but got it wrong"); + + mdsBufferSize = actualSize * intSize; + mdsBuffer = Marshal.AllocHGlobal(mdsBufferSize); + return mdsBuffer; + } + return mdsBuffer; + } + + /// + /// + /// Overrides Control.SetBoundsCore to enforce auto-sizing. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + Rectangle oldBounds = Bounds; + Size max = SystemInformation.MaxWindowTrackSize; + + // Second argument to GetPreferredWidth and GetPreferredHeight is a boolean specifying if we should update the number of rows/columns. + // We only want to update the number of rows/columns if we are not currently being scaled or if MonthCalendarHighDpiImprovements are off. + bool updateRowsAndColumns = !DpiHelper.EnableMonthCalendarHighDpiImprovements || !IsCurrentlyBeingScaled; + + if (width != oldBounds.Width) { + if (width > max.Width) + width = max.Width; + width = GetPreferredWidth(width, updateRowsAndColumns); + } + if (height != oldBounds.Height) { + if (height > max.Height) + height = max.Height; + height = GetPreferredHeight(height, updateRowsAndColumns); + } + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// If the handle has been created, this applies the color to the control + /// + /// + private void SetControlColor(int colorIndex, Color value) { + if (IsHandleCreated) { + SendMessage(NativeMethods.MCM_SETCOLOR, colorIndex, ColorTranslator.ToWin32(value)); + } + } + + /// + /// + /// Updates the window handle with the min/max ranges if it has been + /// created. + /// + /// + private void SetRange() { + SetRange(DateTimePicker.EffectiveMinDate(minDate), DateTimePicker.EffectiveMaxDate(maxDate)); + } + + private void SetRange(DateTime minDate, DateTime maxDate) { + // Keep selection range within passed in minDate and maxDate + if (selectionStart < minDate) { + selectionStart = minDate; + } + if (selectionStart > maxDate) { + selectionStart = maxDate; + } + if (selectionEnd < minDate) { + selectionEnd = minDate; + } + if (selectionEnd > maxDate) { + selectionEnd = maxDate; + } + SetSelRange(selectionStart, selectionEnd); + + // Updated the calendar range + // + if (IsHandleCreated) { + int flag = 0; + + NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); + flag |= NativeMethods.GDTR_MIN | NativeMethods.GDTR_MAX; + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(minDate); + sa.wYear1 = sys.wYear; + sa.wMonth1 = sys.wMonth; + sa.wDayOfWeek1 = sys.wDayOfWeek; + sa.wDay1 = sys.wDay; + sys = DateTimePicker.DateTimeToSysTime(maxDate); + sa.wYear2 = sys.wYear; + sa.wMonth2 = sys.wMonth; + sa.wDayOfWeek2 = sys.wDayOfWeek; + sa.wDay2 = sys.wDay; + + if ((int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETRANGE, flag, sa) == 0) + throw new InvalidOperationException(SR.GetString(SR.MonthCalendarRange, minDate.ToShortDateString(), maxDate.ToShortDateString())); + } + } + + /// + /// + /// Sets the number of columns and rows to display. + /// + public void SetCalendarDimensions(int x, int y) { + if (x < 1) { + throw new ArgumentOutOfRangeException("x", SR.GetString(SR.MonthCalendarInvalidDimensions, (x).ToString("D", CultureInfo.CurrentCulture), (y).ToString("D", CultureInfo.CurrentCulture))); + } + if (y < 1) { + throw new ArgumentOutOfRangeException("y", SR.GetString(SR.MonthCalendarInvalidDimensions, (x).ToString("D", CultureInfo.CurrentCulture), (y).ToString("D", CultureInfo.CurrentCulture))); + } + + // MonthCalendar limits the dimensions to x * y <= 12 + // i.e. a maximum of twelve months can be displayed at a time + // The following code emulates what is done inside monthcalendar (in comctl32.dll): + // The dimensions are gradually reduced until the inequality above holds. + // + while (x * y > 12) { + if (x > y) { + x--; + } + else { + y--; + } + } + + if (dimensions.Width != x || dimensions.Height != y) { + this.dimensions.Width = x; + this.dimensions.Height = y; + AdjustSize(); + } + } + + /// + /// + /// Sets date as the current selected date. The start and begin of + /// the selection range will both be equal to date. + /// + public void SetDate(DateTime date) { + + if (date.Ticks < minDate.Ticks) { + throw new ArgumentOutOfRangeException("date", SR.GetString(SR.InvalidLowBoundArgumentEx, "date", FormatDate(date), "MinDate")); + } + if (date.Ticks > maxDate.Ticks) { + throw new ArgumentOutOfRangeException("date", SR.GetString(SR.InvalidHighBoundArgumentEx, "date", FormatDate(date), "MaxDate")); + } + + SetSelectionRange(date, date); + } + + /// + /// + /// Sets the selection for a month calendar control to a given date range. + /// The selection range will not be set if the selection range exceeds the + /// maximum selection count. + /// + public void SetSelectionRange(DateTime date1, DateTime date2) { + + // Keep the dates within the min and max dates + if (date1.Ticks < minDate.Ticks) { + throw new ArgumentOutOfRangeException("date1", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionStart", FormatDate(date1), "MinDate")); + } + if (date1.Ticks > maxDate.Ticks) { + throw new ArgumentOutOfRangeException("date1", SR.GetString(SR.InvalidHighBoundArgumentEx, "SelectionEnd", FormatDate(date1), "MaxDate")); + } + if (date2.Ticks < minDate.Ticks) { + throw new ArgumentOutOfRangeException("date2", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionStart", FormatDate(date2), "MinDate")); + } + if (date2.Ticks > maxDate.Ticks) { + throw new ArgumentOutOfRangeException("date2", SR.GetString(SR.InvalidHighBoundArgumentEx, "SelectionEnd", FormatDate(date2), "MaxDate")); + } + + // If date1 > date2, we just select date2 (compat) + // + if (date1 > date2) { + date2 = date1; + } + + // If the range exceeds maxSelectionCount, compare with the previous range and adjust whichever + // limit hasn't changed. + // + if ((date2 - date1).Days >= maxSelectionCount) { + + if (date1.Ticks == selectionStart.Ticks) { + // Bring start date forward + // + date1 = date2.AddDays(1 - maxSelectionCount); + } + else { + // Bring end date back + // + date2 = date1.AddDays(maxSelectionCount - 1); + } + } + + // Set the range + SetSelRange(date1, date2); + } + + /// + /// + /// Upper must be greater than Lower + /// + /// + private void SetSelRange(DateTime lower, DateTime upper) { + + Debug.Assert(lower.Ticks <= upper.Ticks, "lower must be less than upper"); + + bool changed = false; + if (selectionStart != lower || selectionEnd != upper) { + changed = true; + selectionStart = lower; + selectionEnd = upper; + } + + // always set the value on the control, to ensure that + // it is up to date. + // + if (IsHandleCreated) { + NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); + + NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(lower); + sa.wYear1 = sys.wYear; + sa.wMonth1 = sys.wMonth; + sa.wDayOfWeek1 = sys.wDayOfWeek; + sa.wDay1 = sys.wDay; + sys = DateTimePicker.DateTimeToSysTime(upper); + sa.wYear2 = sys.wYear; + sa.wMonth2 = sys.wMonth; + sa.wDayOfWeek2 = sys.wDayOfWeek; + sa.wDay2 = sys.wDay; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETSELRANGE , 0, sa); + } + + if (changed) { + OnDateChanged(new DateRangeEventArgs(lower, upper)); + } + } + + private bool ShouldSerializeAnnuallyBoldedDates() { + return annualArrayOfDates.Count > 0; + } + + private bool ShouldSerializeBoldedDates() { + return arrayOfDates.Count > 0; + } + + private bool ShouldSerializeCalendarDimensions() { + return !dimensions.Equals(new Size(1, 1)); + } + + private bool ShouldSerializeTrailingForeColor() { + return !TrailingForeColor.Equals(DEFAULT_TRAILING_FORE_COLOR); + } + + private bool ShouldSerializeTitleForeColor() { + return !TitleForeColor.Equals(DEFAULT_TITLE_FORE_COLOR); + } + + private bool ShouldSerializeTitleBackColor() { + return !TitleBackColor.Equals(DEFAULT_TITLE_BACK_COLOR); + } + + private bool ShouldSerializeMonthlyBoldedDates() { + return monthlyArrayOfDates.Count > 0; + } + + /// + /// + /// Retrieves true if the maxDate should be persisted in code gen. + /// + private bool ShouldSerializeMaxDate() { + return maxDate != DateTimePicker.MaximumDateTime && maxDate != DateTime.MaxValue; + } + + /// + /// + /// Retrieves true if the minDate should be persisted in code gen. + /// + private bool ShouldSerializeMinDate() { + return minDate != DateTimePicker.MinimumDateTime && minDate != DateTime.MinValue; + } + + /// + /// + /// Retrieves true if the selectionRange should be persisted in code gen. + /// + private bool ShouldSerializeSelectionRange() { + return !DateTime.Equals(selectionEnd, selectionStart); + } + + /// + /// + /// Retrieves true if the todayDate should be persisted in code gen. + /// + private bool ShouldSerializeTodayDate() { + return todayDateSet; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", " + SelectionRange.ToString(); + } + + /// + /// + /// Forces month calendar to display the current set of bolded dates. + /// + public void UpdateBoldedDates() { + RecreateHandle(); + } + + /// + /// + /// Updates the current setting for "TODAY" in the MonthCalendar control + /// If the today date is set, the control will be set to that. Otherwise, + /// it will be set to null (running clock mode - the today date will be + /// automatically updated). + /// + private void UpdateTodayDate() { + if (IsHandleCreated) { + NativeMethods.SYSTEMTIME st = null; + if (todayDateSet) { + st = DateTimePicker.DateTimeToSysTime(todayDate); + } + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETTODAY, 0, st); + } + } + + private void MarshaledUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + try { + //use begininvoke instead of invoke in case the destination thread is not processing messages. + BeginInvoke(new UserPreferenceChangedEventHandler(this.UserPreferenceChanged), new object[] { sender, pref }); + } + catch (InvalidOperationException) { } //if the destination thread does not exist, don't send. + } + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + if (pref.Category == UserPreferenceCategory.Locale) { + // We need to recreate the monthcalendar handle when the locale changes, because + // the day names etc. are only updated on a handle recreate (comctl32 limitation). + // + RecreateHandle(); + } + } + + /// + /// + /// Handles the MCN_SELCHANGE notification + /// + /// + private void WmDateChanged(ref Message m) { + NativeMethods.NMSELCHANGE nmmcsc = (NativeMethods.NMSELCHANGE)m.GetLParam(typeof(NativeMethods.NMSELCHANGE)); + DateTime start = selectionStart = DateTimePicker.SysTimeToDateTime(nmmcsc.stSelStart); + DateTime end = selectionEnd = DateTimePicker.SysTimeToDateTime(nmmcsc.stSelEnd); + + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + AccessibilityNotifyClients(AccessibleEvents.ValueChange, -1); + } + + //subhag + if (start.Ticks < minDate.Ticks || end.Ticks < minDate.Ticks) + SetSelRange(minDate,minDate); + else if (start.Ticks > maxDate.Ticks || end.Ticks > maxDate.Ticks) + SetSelRange(maxDate,maxDate); + //end subhag + OnDateChanged(new DateRangeEventArgs(start, end)); + } + + /// + /// + /// Handles the MCN_GETDAYSTATE notification + /// + /// + private void WmDateBold(ref Message m) { + NativeMethods.NMDAYSTATE nmmcds = (NativeMethods.NMDAYSTATE)m.GetLParam(typeof(NativeMethods.NMDAYSTATE)); + DateTime start = DateTimePicker.SysTimeToDateTime(nmmcds.stStart); + DateBoldEventArgs boldEvent = new DateBoldEventArgs(start, nmmcds.cDayState); + BoldDates(boldEvent); + mdsBuffer = RequestBuffer(boldEvent.Size); + // copy boldEvent into mdsBuffer + Marshal.Copy(boldEvent.DaysToBold, 0, mdsBuffer, boldEvent.Size); + // now we replug DateBoldEventArgs info into NMDAYSTATE + nmmcds.prgDayState = mdsBuffer; + Marshal.StructureToPtr(nmmcds, m.LParam, false); + } + + /// + /// + /// Handles the MCN_VIEWCHANGE notification + /// + /// + + private void WmCalViewChanged (ref Message m) { + NativeMethods.NMVIEWCHANGE nmmcvm = (NativeMethods.NMVIEWCHANGE)m.GetLParam(typeof(NativeMethods.NMVIEWCHANGE)); + Debug.Assert(mcCurView == (NativeMethods.MONTCALENDAR_VIEW_MODE)nmmcvm.uOldView, "Calendar view mode is out of sync with native control"); + if (mcCurView != (NativeMethods.MONTCALENDAR_VIEW_MODE)nmmcvm.uNewView) { + mcOldView = mcCurView; + mcCurView = (NativeMethods.MONTCALENDAR_VIEW_MODE)nmmcvm.uNewView; + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.ValueChange, -1); + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + } + } + } + /// + /// + /// Handles the MCN_SELECT notification + /// + /// + private void WmDateSelected(ref Message m) { + NativeMethods.NMSELCHANGE nmmcsc = (NativeMethods.NMSELCHANGE)m.GetLParam(typeof(NativeMethods.NMSELCHANGE)); + DateTime start = selectionStart = DateTimePicker.SysTimeToDateTime(nmmcsc.stSelStart); + DateTime end = selectionEnd = DateTimePicker.SysTimeToDateTime(nmmcsc.stSelEnd); + + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + AccessibilityNotifyClients(AccessibleEvents.ValueChange, -1); + } + + //subhag + if (start.Ticks < minDate.Ticks || end.Ticks < minDate.Ticks) + SetSelRange(minDate,minDate); + else if (start.Ticks > maxDate.Ticks || end.Ticks > maxDate.Ticks) + SetSelRange(maxDate,maxDate); + + //end subhag + OnDateSelected(new DateRangeEventArgs(start, end)); + + } + + /// + /// + /// Handles the WM_GETDLGCODE message + /// + /// + private void WmGetDlgCode(ref Message m) { + // The MonthCalendar does its own handling of arrow keys + m.Result = (IntPtr)NativeMethods.DLGC_WANTARROWS; + } + + /// + /// + /// Handles the WM_COMMAND messages reflected from the parent control. + /// + /// + private void WmReflectCommand(ref Message m) { + if (m.HWnd == Handle) { + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (nmhdr.code) { + case NativeMethods.MCN_SELECT: + WmDateSelected(ref m); + break; + case NativeMethods.MCN_SELCHANGE: + WmDateChanged(ref m); + break; + case NativeMethods.MCN_GETDAYSTATE: + WmDateBold(ref m); + break; + case NativeMethods.MCN_VIEWCHANGE: + if (AccessibilityImprovements.Level1) { + WmCalViewChanged(ref m); + } + break; + } + } + } + + /// + /// + /// Overrided wndProc + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_LBUTTONDOWN: + FocusInternal(); + if (!ValidationCancelled) { + base.WndProc(ref m); + } + break; + case NativeMethods.WM_GETDLGCODE: + WmGetDlgCode(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + WmReflectCommand(ref m); + base.WndProc(ref m); + break; + case NativeMethods.WM_DESTROY: + if (restrictUnmanagedCode == true && nativeWndProcCount > 0) { + throw new InvalidOperationException(); + } + base.WndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + /// Calls the default window procedure for the MonthCalendar control. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + protected override void DefWndProc(ref Message m) { + if (restrictUnmanagedCode == true) { + nativeWndProcCount++; + try { + base.DefWndProc(ref m); + } + finally { + nativeWndProcCount--; + } + + return; + } + + base.DefWndProc(ref m); + } + + /// + /// + /// HitTestInfo objects are returned by MonthCalendar in response to the hitTest method. + /// HitTestInfo is for informational purposes only; the user should not construct these objects, and + /// cannot modify any of the members. + /// + public sealed class HitTestInfo { + readonly Point point; + readonly HitArea hitArea; + readonly DateTime time; + + /// + /// + /// + /// + internal HitTestInfo(Point pt, HitArea area, DateTime time) { + this.point = pt; + this.hitArea = area; + this.time = time; + } + + /// + /// + /// This constructor is used when the DateTime member is invalid. + /// + /// + internal HitTestInfo(Point pt, HitArea area) { + this.point = pt; + this.hitArea = area; + } + + /// + /// + /// The point that was hit-tested + /// + public Point Point { + get { return point; } + } + + /// + /// + /// Output member that receives an enumeration value from System.Windows.Forms.MonthCalendar.HitArea + /// representing the result of the hit-test operation. + /// + public HitArea HitArea { + get { return hitArea; } + } + + /// + /// + /// The time information specific to the location that was hit-tested. This value + /// will only be valid at certain values of hitArea. + /// + public DateTime Time { + get { return time; } + } + + /// + /// + /// Determines whether a given HitArea should have a corresponding valid DateTime + /// + /// + internal static bool HitAreaHasValidDateTime(HitArea hitArea) { + switch (hitArea) { + case HitArea.Date: + //case HitArea.DayOfWeek: comCtl does not provide a valid date + case HitArea.WeekNumbers: + return true; + } + return false; + } + } + + /// + /// + /// This enumeration has specific areas of the MonthCalendar control as its enumerated values. + /// The hitArea member of System.Windows.Forms.Win32.HitTestInfo will be one of these enumerated values, and + /// indicates which portion of a month calendar is under a specific point. + /// + public enum HitArea { + /// + /// + /// The given point was not on the month calendar control, or it was in an inactive portion of the control. + /// + Nowhere = 0, + + /// + /// + /// The given point was over the background of a month's title + /// + TitleBackground = 1, + + /// + /// + /// The given point was in a month's title bar, over a month name + /// + TitleMonth = 2, + + /// + /// + /// The given point was in a month's title bar, over the year value + /// + TitleYear = 3, + + /// + /// + /// The given point was over the button at the top right corner of the control. + /// If the user clicks here, the month calendar will scroll its display to the next + /// month or set of months + /// + NextMonthButton = 4, + + /// + /// + /// The given point was over the button at the top left corner of the control. If the + /// user clicks here, the month calendar will scroll its display to the previous month + /// or set of months + /// + PrevMonthButton = 5, + + /// + /// + /// The given point was in the calendar's background + /// + CalendarBackground = 6, + + /// + /// + /// The given point was on a particular date within the calendar, and the time member of + /// HitTestInfo will be set to the date at the given point. + /// + Date = 7, + + /// + /// + /// The given point was over a date from the next month (partially displayed at the end of + /// the currently displayed month). If the user clicks here, the month calendar will scroll + /// its display to the next month or set of months. + /// + NextMonthDate = 8, + + /// + /// + /// The given point was over a date from the previous month (partially displayed at the end + /// of the currently displayed month). If the user clicks here, the month calendar will scroll + /// its display to the previous month or set of months. + /// + PrevMonthDate = 9, + + /// + /// + /// The given point was over a day abbreviation ("Fri", for example). The time member + /// of HitTestInfo will be set to the corresponding date on the top row. + /// + DayOfWeek = 10, + + /// + /// + /// The given point was over a week number. This will only occur if the showWeekNumbers + /// property of MonthCalendar is enabled. The time member of HitTestInfo will be set to + /// the corresponding date in the leftmost column. + /// + WeekNumbers = 11, + + /// + /// + /// The given point was on the "today" link at the bottom of the month calendar control + /// + TodayLink = 12, + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class MonthCalendarAccessibleObject : ControlAccessibleObject { + + private MonthCalendar calendar; + + /// + public MonthCalendarAccessibleObject(Control owner) + : base(owner) { + calendar = owner as MonthCalendar; + } + + /// + public override AccessibleRole Role { + get { + if (calendar != null) { + AccessibleRole role = calendar.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + } + return AccessibleRole.Table; + } + } + + /// + public override string Help { + get { + var help = base.Help; + if (help != null) { + return help; + } + else { + if (calendar != null) { + return calendar.GetType().Name + "(" + calendar.GetType().BaseType.Name + ")"; + } + } + return string.Empty; + } + } + + /// + public override string Name { + get { + string name = base.Name; + if (name != null) { + return name; + } + + if (calendar != null) { + + if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) { + if (System.DateTime.Equals(calendar.SelectionStart.Date, calendar.SelectionEnd.Date)) { + name = SR.GetString(SR.MonthCalendarSingleDateSelected, calendar.SelectionStart.ToLongDateString()); + } + else { + name = SR.GetString(SR.MonthCalendarRangeSelected, calendar.SelectionStart.ToLongDateString(), calendar.SelectionEnd.ToLongDateString()); + } + } + else if (this.calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) { + if (System.DateTime.Equals(this.calendar.SelectionStart.Month, this.calendar.SelectionEnd.Month)) { + name = SR.GetString(SR.MonthCalendarSingleDateSelected, calendar.SelectionStart.ToString("y")); + } + else { + name = SR.GetString(SR.MonthCalendarRangeSelected, calendar.SelectionStart.ToString("y"), calendar.SelectionEnd.ToString("y")); + } + } + else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_DECADE) { + if (System.DateTime.Equals(calendar.SelectionStart.Year, calendar.SelectionEnd.Year)) { + name = SR.GetString(SR.MonthCalendarSingleYearSelected, calendar.SelectionStart.ToString("yyyy")); + } + else { + name = SR.GetString(SR.MonthCalendarYearRangeSelected, calendar.SelectionStart.ToString("yyyy"), calendar.SelectionEnd.ToString("yyyy")); + } + } + else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_CENTURY) { + name = SR.GetString(SR.MonthCalendarSingleDecadeSelected, calendar.SelectionStart.ToString("yyyy")); + } + } + return name; + } + } + + public override string Value { + get { + var value = string.Empty; + try { + if (calendar != null) { + if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) { + if (System.DateTime.Equals(calendar.SelectionStart.Date, calendar.SelectionEnd.Date)) { + value = calendar.SelectionStart.ToLongDateString(); + } + else { + value = string.Format("{0} - {1}", calendar.SelectionStart.ToLongDateString(), calendar.SelectionEnd.ToLongDateString()); + } + } + else if (this.calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) { + if (System.DateTime.Equals(this.calendar.SelectionStart.Month, this.calendar.SelectionEnd.Month)) { + value = calendar.SelectionStart.ToString("y"); + } + else { + value = string.Format("{0} - {1}", calendar.SelectionStart.ToString("y"), calendar.SelectionEnd.ToString("y")); + } + } + else { + value = string.Format("{0} - {1}", calendar.SelectionRange.Start.ToString(), calendar.SelectionRange.End.ToString()); + } + } + } + catch { + value = base.Value; + } + return value; + } + set { + base.Value = value; + } + } + } + + } // end class MonthCalendar +} + diff --git a/WindowsForms/Managed/System/WinForms/MouseButtons.cs b/WindowsForms/Managed/System/WinForms/MouseButtons.cs new file mode 100644 index 000000000..b5e0a7420 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MouseButtons.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies constants that define which mouse button was pressed. + /// + /// + [Flags] + [System.Runtime.InteropServices.ComVisible(true)] + public enum MouseButtons { + + /// + /// + /// + /// + /// The left mouse button was pressed. + /// + /// + /// + Left = 0x00100000, + + /// + /// + /// + /// + /// No mouse button was pressed. + /// + /// + /// + None = 0x00000000, + + /// + /// + /// + /// + /// The right mouse button was pressed. + /// + /// + /// + Right = 0x00200000, + + /// + /// + /// + /// + /// The middle mouse button was pressed. + /// + /// + /// + Middle = 0x00400000, + + /// + /// + /// [To be supplied.] + /// + XButton1 = 0x00800000, + + /// + /// + /// [To be supplied.] + /// + XButton2 = 0x01000000, + } +} diff --git a/WindowsForms/Managed/System/WinForms/MouseEvent.cs b/WindowsForms/Managed/System/WinForms/MouseEvent.cs new file mode 100644 index 000000000..0dbb9057a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MouseEvent.cs @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the , + /// , and + /// events. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class MouseEventArgs : EventArgs { + + /// + /// + /// Which button generated this event [if applicable] + /// + private readonly MouseButtons button; + + /// + /// + /// If the user has clicked the mouse more than once, this contains the + /// count of clicks so far. + /// + private readonly int clicks; + + /// + /// + /// The x portion of the coordinate where this event occurred. + /// + private readonly int x; + + /// + /// + /// The y portion of the coordinate where this event occurred. + /// + private readonly int y; + + private readonly int delta; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public MouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta) { + Debug.Assert((button & (MouseButtons.Left | MouseButtons.None | MouseButtons.Right | MouseButtons.Middle | MouseButtons.XButton1 | MouseButtons.XButton2)) == + button, "Invalid information passed into MouseEventArgs constructor!"); + + this.button = button; + this.clicks = clicks; + this.x = x; + this.y = y; + this.delta = delta; + } + + /// + /// + /// + /// Gets which mouse button was pressed. + /// + /// + public MouseButtons Button { + get { + return button; + } + } + + /// + /// + /// + /// Gets the + /// number of times the mouse + /// button was pressed and released. + /// + /// + public int Clicks { + get { + return clicks; + } + } + + /// + /// + /// + /// Gets the x-coordinate + /// of a mouse click. + /// + /// + public int X { + get { + return x; + } + } + + /// + /// + /// + /// Gets the y-coordinate of a mouse click. + /// + /// + public int Y { + get { + return y; + } + } + + /// + /// + /// + /// Gets + /// a signed count of the number of detents the mouse wheel has rotated. + /// + /// + public int Delta { + get { + return delta; + } + } + + /// + /// + /// + /// Gets the location of the mouse during MouseEvent. + /// + /// + public Point Location { + get { + return new Point(x,y); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/MouseEventHandler.cs b/WindowsForms/Managed/System/WinForms/MouseEventHandler.cs new file mode 100644 index 000000000..1366069f0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/MouseEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents the method that will handle the + /// , , or event of a form, control, or other component. + /// + /// + public delegate void MouseEventHandler(object sender, MouseEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/NativeMethods.cs b/WindowsForms/Managed/System/WinForms/NativeMethods.cs new file mode 100644 index 000000000..f470d4c6b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NativeMethods.cs @@ -0,0 +1,6778 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+BITMAPINFO")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+BITMAPINFO_ARRAY")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+CommonHandles")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+CREATESTRUCT")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+DIBSECTION")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+DLLVERSIONINFO")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+LOGPALETTE")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+MSOCM")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+NMDATETIMEFORMAT")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+NMDATETIMEFORMATQUERY")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+NMDATETIMEWMKEYDOWN")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+NMTTCUSTOMDRAW")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+Ole")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+OLECMD")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+OVERLAPPED")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+SHFILEINFO")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+STARTUPINFO")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+tagDVTARGETDEVICE")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+tagLOGPALETTE")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+tagOleMenuGroupWidths")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+tagSIZE")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+tagTLIBATTR")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+WNDCLASS")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope="type", Target="System.Windows.Forms.NativeMethods+XFORM")] + + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+MSOCM..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+Ole..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+Util..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.ComponentModel.WeakDelegateHolder.get_IsAlive():System.Boolean")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+CommonHandles..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+tagDVTARGETDEVICE..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+tagLOGPALETTE..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+tagOleMenuGroupWidths..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+tagSIZE..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.NativeMethods+OLECMD..ctor()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Scope="member", Target="System.Windows.Forms.NativeMethods+INPUTUNION.hi")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Scope="member", Target="System.Windows.Forms.NativeMethods+INPUTUNION.mi")] + +namespace System.Windows.Forms { + using Accessibility; + using System.Runtime.InteropServices; + using System; + using System.Security.Permissions; + using System.Collections; + using System.Diagnostics; + using System.IO; + using System.Text; + using Microsoft.Win32; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Runtime.Versioning; + + /// + internal static class NativeMethods { + + public static IntPtr InvalidIntPtr = (IntPtr)(-1); + public static IntPtr LPSTR_TEXTCALLBACK = (IntPtr)(-1); + public static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); + + public const int BITMAPINFO_MAX_COLORSIZE = 256; + public const int BI_BITFIELDS = 3; + + public enum RegionFlags { + ERROR = 0, + NULLREGION = 1, + SIMPLEREGION = 2, + COMPLEXREGION = 3, + } + + public const int STATUS_PENDING = 0x103; //259 = STILL_ALIVE + + public const int + DESKTOP_SWITCHDESKTOP = 0x0100, + ERROR_ACCESS_DENIED = 0x0005; + + public const int + /* FONT WEIGHT (BOLD) VALUES */ + FW_DONTCARE = 0, + FW_NORMAL = 400, + FW_BOLD = 700, + // some others... + + /* FONT CHARACTER SET */ + ANSI_CHARSET = 0, + DEFAULT_CHARSET = 1, + // plus others .... + + /* Font OutPrecision */ + OUT_DEFAULT_PRECIS = 0, + OUT_TT_PRECIS = 4, + OUT_TT_ONLY_PRECIS = 7, + + /* polygon fill mode */ + ALTERNATE = 1, + WINDING = 2, + + // text align + TA_DEFAULT = 0, + + // brush + BS_SOLID = 0, + HOLLOW_BRUSH = 5, + + // Binary raster operations. + R2_BLACK = 1, /* 0 */ + R2_NOTMERGEPEN = 2, /* DPon */ + R2_MASKNOTPEN = 3, /* DPna */ + R2_NOTCOPYPEN = 4, /* PN */ + R2_MASKPENNOT = 5, /* PDna */ + R2_NOT = 6, /* Dn */ + R2_XORPEN = 7, /* DPx */ + R2_NOTMASKPEN = 8, /* DPan */ + R2_MASKPEN = 9, /* DPa */ + R2_NOTXORPEN = 10, /* DPxn */ + R2_NOP = 11, /* D */ + R2_MERGENOTPEN = 12, /* DPno */ + R2_COPYPEN = 13, /* P */ + R2_MERGEPENNOT = 14, /* PDno */ + R2_MERGEPEN = 15, /* DPo */ + R2_WHITE = 16 /* 1 */; + + + public const int + /* SetGraphicsMode(hdc, iMode ) */ + GM_COMPATIBLE = 1, + GM_ADVANCED = 2, + MWT_IDENTITY = 1; + + public const int + PAGE_READONLY = 0x02, + PAGE_READWRITE = 0x04, + PAGE_WRITECOPY = 0x08, + FILE_MAP_COPY = 0x0001, + FILE_MAP_WRITE = 0x0002, + FILE_MAP_READ = 0x0004; + + public const int SHGFI_ICON = 0x000000100 , // get icon + SHGFI_DISPLAYNAME = 0x000000200, // get display name + SHGFI_TYPENAME = 0x000000400, // get type name + SHGFI_ATTRIBUTES = 0x000000800, // get attributes + SHGFI_ICONLOCATION = 0x000001000, // get icon location + SHGFI_EXETYPE = 0x000002000, // return exe type + SHGFI_SYSICONINDEX = 0x000004000, // get system icon index + SHGFI_LINKOVERLAY = 0x000008000, // put a link overlay on icon + SHGFI_SELECTED = 0x000010000, // show icon in selected state + SHGFI_ATTR_SPECIFIED = 0x000020000, // get only specified attributes + SHGFI_LARGEICON = 0x000000000, // get large icon + SHGFI_SMALLICON = 0x000000001, // get small icon + SHGFI_OPENICON = 0x000000002, // get open icon + SHGFI_SHELLICONSIZE = 0x000000004, // get shell size icon + SHGFI_PIDL = 0x000000008, // pszPath is a pidl + SHGFI_USEFILEATTRIBUTES = 0x000000010, // use passed dwFileAttribute + SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays + SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay + + public const int DM_DISPLAYORIENTATION = 0x00000080; + + public const int AUTOSUGGEST = 0x10000000, + AUTOSUGGEST_OFF = 0x20000000, + AUTOAPPEND = 0x40000000, + AUTOAPPEND_OFF = (unchecked((int)0x80000000)); + + public const int ARW_BOTTOMLEFT = 0x0000, + ARW_BOTTOMRIGHT = 0x0001, + ARW_TOPLEFT = 0x0002, + ARW_TOPRIGHT = 0x0003, + ARW_LEFT = 0x0000, + ARW_RIGHT = 0x0000, + ARW_UP = 0x0004, + ARW_DOWN = 0x0004, + ARW_HIDE = 0x0008, + ACM_OPENA = (0x0400+100), + ACM_OPENW = (0x0400+103), + ADVF_NODATA = 1, + ADVF_ONLYONCE = 4, + ADVF_PRIMEFIRST = 2; + // Note: ADVF_ONLYONCE and ADVF_PRIMEFIRST values now conform with objidl.dll but are backwards from + // Platform SDK documentation as of 07/21/2003. + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/htm/oen_a2z_8jxi.asp. + // See VSWhidbey bug#96162. + + public const int BCM_GETIDEALSIZE = 0x1601, + BI_RGB = 0, + BS_PATTERN = 3, + BITSPIXEL = 12, + BDR_RAISEDOUTER = 0x0001, + BDR_SUNKENOUTER = 0x0002, + BDR_RAISEDINNER = 0x0004, + BDR_SUNKENINNER = 0x0008, + BDR_RAISED = 0x0005, + BDR_SUNKEN = 0x000a, + BF_LEFT = 0x0001, + BF_TOP = 0x0002, + BF_RIGHT = 0x0004, + BF_BOTTOM = 0x0008, + BF_ADJUST = 0x2000, + BF_FLAT = 0x4000, + BF_MIDDLE = 0x0800, + BFFM_INITIALIZED = 1, + BFFM_SELCHANGED = 2, + BFFM_SETSELECTIONA = 0x400 + 102, + BFFM_SETSELECTIONW = 0x400 + 103, + BFFM_ENABLEOK = 0x400 + 101, + BS_PUSHBUTTON = 0x00000000, + BS_DEFPUSHBUTTON = 0x00000001, + BS_MULTILINE = 0x00002000, + BS_PUSHLIKE = 0x00001000, + BS_OWNERDRAW = 0x0000000B, + BS_RADIOBUTTON = 0x00000004, + BS_3STATE = 0x00000005, + BS_GROUPBOX = 0x00000007, + BS_LEFT = 0x00000100, + BS_RIGHT = 0x00000200, + BS_CENTER = 0x00000300, + BS_TOP = 0x00000400, + BS_BOTTOM = 0x00000800, + BS_VCENTER = 0x00000C00, + BS_RIGHTBUTTON = 0x00000020, + BN_CLICKED = 0, + BM_SETCHECK = 0x00F1, + BM_SETSTATE = 0x00F3, + BM_CLICK = 0x00F5; + + public const int CDERR_DIALOGFAILURE = 0xFFFF, + CDERR_STRUCTSIZE = 0x0001, + CDERR_INITIALIZATION = 0x0002, + CDERR_NOTEMPLATE = 0x0003, + CDERR_NOHINSTANCE = 0x0004, + CDERR_LOADSTRFAILURE = 0x0005, + CDERR_FINDRESFAILURE = 0x0006, + CDERR_LOADRESFAILURE = 0x0007, + CDERR_LOCKRESFAILURE = 0x0008, + CDERR_MEMALLOCFAILURE = 0x0009, + CDERR_MEMLOCKFAILURE = 0x000A, + CDERR_NOHOOK = 0x000B, + CDERR_REGISTERMSGFAIL = 0x000C, + CFERR_NOFONTS = 0x2001, + CFERR_MAXLESSTHANMIN = 0x2002, + CC_RGBINIT = 0x00000001, + CC_FULLOPEN = 0x00000002, + CC_PREVENTFULLOPEN = 0x00000004, + CC_SHOWHELP = 0x00000008, + CC_ENABLEHOOK = 0x00000010, + CC_SOLIDCOLOR = 0x00000080, + CC_ANYCOLOR = 0x00000100, + CF_SCREENFONTS = 0x00000001, + CF_SHOWHELP = 0x00000004, + CF_ENABLEHOOK = 0x00000008, + CF_INITTOLOGFONTSTRUCT = 0x00000040, + CF_EFFECTS = 0x00000100, + CF_APPLY = 0x00000200, + CF_SCRIPTSONLY = 0x00000400, + CF_NOVECTORFONTS = 0x00000800, + CF_NOSIMULATIONS = 0x00001000, + CF_LIMITSIZE = 0x00002000, + CF_FIXEDPITCHONLY = 0x00004000, + CF_FORCEFONTEXIST = 0x00010000, + CF_TTONLY = 0x00040000, + CF_SELECTSCRIPT = 0x00400000, + CF_NOVERTFONTS = 0x01000000, + CP_WINANSI = 1004; + + public const int cmb4 = 0x0473, + CS_DBLCLKS = 0x0008, + CS_DROPSHADOW = 0x00020000, + CS_SAVEBITS = 0x0800, + CF_TEXT = 1, + CF_BITMAP = 2, + CF_METAFILEPICT = 3, + CF_SYLK = 4, + CF_DIF = 5, + CF_TIFF = 6, + CF_OEMTEXT = 7, + CF_DIB = 8, + CF_PALETTE = 9, + CF_PENDATA = 10, + CF_RIFF = 11, + CF_WAVE = 12, + CF_UNICODETEXT = 13, + CF_ENHMETAFILE = 14, + CF_HDROP = 15, + CF_LOCALE = 16, + CLSCTX_INPROC_SERVER = 0x1, + CLSCTX_LOCAL_SERVER = 0x4, + CW_USEDEFAULT = (unchecked((int)0x80000000)), + CWP_SKIPINVISIBLE = 0x0001, + COLOR_WINDOW = 5, + CB_ERR = (-1), + CBN_SELCHANGE = 1, + CBN_DBLCLK = 2, + CBN_EDITCHANGE = 5, + CBN_EDITUPDATE = 6, + CBN_DROPDOWN = 7, + CBN_CLOSEUP = 8, + CBN_SELENDOK = 9, + CBS_SIMPLE = 0x0001, + CBS_DROPDOWN = 0x0002, + CBS_DROPDOWNLIST = 0x0003, + CBS_OWNERDRAWFIXED = 0x0010, + CBS_OWNERDRAWVARIABLE = 0x0020, + CBS_AUTOHSCROLL = 0x0040, + CBS_HASSTRINGS = 0x0200, + CBS_NOINTEGRALHEIGHT = 0x0400, + CB_GETEDITSEL = 0x0140, + CB_LIMITTEXT = 0x0141, + CB_SETEDITSEL = 0x0142, + CB_ADDSTRING = 0x0143, + CB_DELETESTRING = 0x0144, + CB_GETCURSEL = 0x0147, + CB_GETLBTEXT = 0x0148, + CB_GETLBTEXTLEN = 0x0149, + CB_INSERTSTRING = 0x014A, + CB_RESETCONTENT = 0x014B, + CB_FINDSTRING = 0x014C, + CB_SETCURSEL = 0x014E, + CB_SHOWDROPDOWN = 0x014F, + CB_GETITEMDATA = 0x0150, + CB_SETITEMHEIGHT = 0x0153, + CB_GETITEMHEIGHT = 0x0154, + CB_GETDROPPEDSTATE = 0x0157, + CB_FINDSTRINGEXACT = 0x0158, + CB_GETDROPPEDWIDTH = 0x015F, + CB_SETDROPPEDWIDTH = 0x0160, + CDRF_DODEFAULT = 0x00000000, + CDRF_NEWFONT = 0x00000002, + CDRF_SKIPDEFAULT = 0x00000004, + CDRF_NOTIFYPOSTPAINT = 0x00000010, + CDRF_NOTIFYITEMDRAW = 0x00000020, + CDRF_NOTIFYSUBITEMDRAW = CDRF_NOTIFYITEMDRAW, + CDDS_PREPAINT = 0x00000001, + CDDS_POSTPAINT = 0x00000002, + CDDS_ITEM = 0x00010000, + CDDS_SUBITEM = 0x00020000, + CDDS_ITEMPREPAINT = (0x00010000|0x00000001), + CDDS_ITEMPOSTPAINT = (0x00010000|0x00000002), + CDIS_SELECTED = 0x0001, + CDIS_GRAYED = 0x0002, + CDIS_DISABLED = 0x0004, + CDIS_CHECKED = 0x0008, + CDIS_FOCUS = 0x0010, + CDIS_DEFAULT = 0x0020, + CDIS_HOT = 0x0040, + CDIS_MARKED = 0x0080, + CDIS_INDETERMINATE = 0x0100, + CDIS_SHOWKEYBOARDCUES = 0x0200, + CLR_NONE = unchecked((int)0xFFFFFFFF), + CLR_DEFAULT = unchecked((int)0xFF000000), + CCM_SETVERSION = (0x2000+0x7), + CCM_GETVERSION = (0x2000+0x8), + CCS_NORESIZE = 0x00000004, + CCS_NOPARENTALIGN = 0x00000008, + CCS_NODIVIDER = 0x00000040, + CBEM_INSERTITEMA = (0x0400+1), + CBEM_GETITEMA = (0x0400+4), + CBEM_SETITEMA = (0x0400+5), + CBEM_INSERTITEMW = (0x0400+11), + CBEM_SETITEMW = (0x0400+12), + CBEM_GETITEMW = (0x0400+13), + CBEN_ENDEDITA = ((0-800)-5), + CBEN_ENDEDITW = ((0-800)-6), + CONNECT_E_NOCONNECTION = unchecked((int)0x80040200), + CONNECT_E_CANNOTCONNECT = unchecked((int)0x80040202), + CTRLINFO_EATS_RETURN = 1, + CTRLINFO_EATS_ESCAPE = 2, + CSIDL_DESKTOP = 0x0000, // + CSIDL_INTERNET = 0x0001, // Internet Explorer (icon on desktop) + CSIDL_PROGRAMS = 0x0002, // Start Menu\Programs + CSIDL_PERSONAL = 0x0005, // My Documents + CSIDL_FAVORITES = 0x0006, // \Favorites + CSIDL_STARTUP = 0x0007, // Start Menu\Programs\Startup + CSIDL_RECENT = 0x0008, // \Recent + CSIDL_SENDTO = 0x0009, // \SendTo + CSIDL_STARTMENU = 0x000b, // \Start Menu + CSIDL_DESKTOPDIRECTORY = 0x0010, // \Desktop + CSIDL_TEMPLATES = 0x0015, + CSIDL_APPDATA = 0x001a, // \Application Data + CSIDL_LOCAL_APPDATA = 0x001c, // \Local Settings\Applicaiton Data (non roaming) + CSIDL_INTERNET_CACHE = 0x0020, + CSIDL_COOKIES = 0x0021, + CSIDL_HISTORY = 0x0022, + CSIDL_COMMON_APPDATA = 0x0023, // All Users\Application Data + CSIDL_SYSTEM = 0x0025, // GetSystemDirectory() + CSIDL_PROGRAM_FILES = 0x0026, // C:\Program Files + CSIDL_PROGRAM_FILES_COMMON = 0x002b; // C:\Program Files\Common + + public const int DUPLICATE = 0x06, + DISPID_UNKNOWN = (-1), + DISPID_PROPERTYPUT = (-3), + DISPATCH_METHOD = 0x1, + DISPATCH_PROPERTYGET = 0x2, + DISPATCH_PROPERTYPUT = 0x4, + DV_E_DVASPECT = unchecked((int)0x8004006B), + DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003), + DISP_E_PARAMNOTFOUND = unchecked((int)0x80020004), + DISP_E_EXCEPTION = unchecked((int)0x80020009), + DEFAULT_GUI_FONT = 17, + DIB_RGB_COLORS = 0, + DRAGDROP_E_NOTREGISTERED = unchecked((int)0x80040100), + DRAGDROP_E_ALREADYREGISTERED = unchecked((int)0x80040101), + DUPLICATE_SAME_ACCESS = 0x00000002, + DFC_CAPTION = 1, + DFC_MENU = 2, + DFC_SCROLL = 3, + DFC_BUTTON = 4, + DFCS_CAPTIONCLOSE = 0x0000, + DFCS_CAPTIONMIN = 0x0001, + DFCS_CAPTIONMAX = 0x0002, + DFCS_CAPTIONRESTORE = 0x0003, + DFCS_CAPTIONHELP = 0x0004, + DFCS_MENUARROW = 0x0000, + DFCS_MENUCHECK = 0x0001, + DFCS_MENUBULLET = 0x0002, + DFCS_SCROLLUP = 0x0000, + DFCS_SCROLLDOWN = 0x0001, + DFCS_SCROLLLEFT = 0x0002, + DFCS_SCROLLRIGHT = 0x0003, + DFCS_SCROLLCOMBOBOX = 0x0005, + DFCS_BUTTONCHECK = 0x0000, + DFCS_BUTTONRADIO = 0x0004, + DFCS_BUTTON3STATE = 0x0008, + DFCS_BUTTONPUSH = 0x0010, + DFCS_INACTIVE = 0x0100, + DFCS_PUSHED = 0x0200, + DFCS_CHECKED = 0x0400, + DFCS_FLAT = 0x4000, + DCX_WINDOW = 0x00000001, + DCX_CACHE = 0x00000002, + DCX_LOCKWINDOWUPDATE = 0x00000400, + DCX_INTERSECTRGN = 0x00000080, + DI_NORMAL = 0x0003, + DLGC_WANTARROWS = 0x0001, + DLGC_WANTTAB = 0x0002, + DLGC_WANTALLKEYS = 0x0004, + DLGC_WANTCHARS = 0x0080, + DLGC_WANTMESSAGE = 0x0004, /* Pass message to control */ + DLGC_HASSETSEL = 0x0008, /* Understands EM_SETSEL message */ + DTM_GETSYSTEMTIME = (0x1000+1), + DTM_SETSYSTEMTIME = (0x1000+2), + DTM_SETRANGE = (0x1000+4), + DTM_SETFORMATA = (0x1000+5), + DTM_SETFORMATW = (0x1000+50), + DTM_SETMCCOLOR = (0x1000+6), + DTM_GETMONTHCAL = (0x1000+8), + DTM_SETMCFONT = (0x1000+9), + DTS_UPDOWN = 0x0001, + DTS_SHOWNONE = 0x0002, + DTS_LONGDATEFORMAT = 0x0004, + DTS_TIMEFORMAT = 0x0009, + DTS_RIGHTALIGN = 0x0020, + DTN_DATETIMECHANGE = ((0-760)+1), + DTN_USERSTRINGA = ((0-760)+2), + DTN_USERSTRINGW = ((0-760)+15), + DTN_WMKEYDOWNA = ((0-760)+3), + DTN_WMKEYDOWNW = ((0-760)+16), + DTN_FORMATA = ((0-760)+4), + DTN_FORMATW = ((0-760)+17), + DTN_FORMATQUERYA = ((0-760)+5), + DTN_FORMATQUERYW = ((0-760)+18), + DTN_DROPDOWN = ((0-760)+6), + DTN_CLOSEUP = ((0-760)+7), + DVASPECT_CONTENT = 1, + DVASPECT_TRANSPARENT = 32, + DVASPECT_OPAQUE = 16; + + public const int E_NOTIMPL = unchecked((int)0x80004001), + E_OUTOFMEMORY = unchecked((int)0x8007000E), + E_INVALIDARG = unchecked((int)0x80070057), + E_NOINTERFACE = unchecked((int)0x80004002), + E_POINTER = unchecked((int)0x80004003), + E_FAIL = unchecked((int)0x80004005), + E_ABORT = unchecked((int)0x80004004), + E_UNEXPECTED = unchecked((int)0x8000FFFF), + INET_E_DEFAULT_ACTION = unchecked((int)0x800C0011), + ETO_OPAQUE = 0x0002, + ETO_CLIPPED = 0x0004, + EMR_POLYTEXTOUTA = 96, + EMR_POLYTEXTOUTW = 97, + EDGE_RAISED = (0x0001|0x0004), + EDGE_SUNKEN = (0x0002|0x0008), + EDGE_ETCHED = (0x0002|0x0004), + EDGE_BUMP = (0x0001|0x0008), + ES_LEFT = 0x0000, + ES_CENTER = 0x0001, + ES_RIGHT = 0x0002, + ES_MULTILINE = 0x0004, + ES_UPPERCASE = 0x0008, + ES_LOWERCASE = 0x0010, + ES_AUTOVSCROLL = 0x0040, + ES_AUTOHSCROLL = 0x0080, + ES_NOHIDESEL = 0x0100, + ES_READONLY = 0x0800, + ES_PASSWORD = 0x0020, + EN_CHANGE = 0x0300, + EN_UPDATE = 0x0400, + EN_HSCROLL = 0x0601, + EN_VSCROLL = 0x0602, + EN_ALIGN_LTR_EC = 0x0700, + EN_ALIGN_RTL_EC = 0x0701, + EC_LEFTMARGIN = 0x0001, + EC_RIGHTMARGIN = 0x0002, + EM_GETSEL = 0x00B0, + EM_SETSEL = 0x00B1, + EM_SCROLL = 0x00B5, + EM_SCROLLCARET = 0x00B7, + EM_GETMODIFY = 0x00B8, + EM_SETMODIFY = 0x00B9, + EM_GETLINECOUNT = 0x00BA, + EM_REPLACESEL = 0x00C2, + EM_GETLINE = 0x00C4, + EM_LIMITTEXT = 0x00C5, + EM_CANUNDO = 0x00C6, + EM_UNDO = 0x00C7, + EM_SETPASSWORDCHAR = 0x00CC, + EM_GETPASSWORDCHAR = 0x00D2, + EM_EMPTYUNDOBUFFER = 0x00CD, + EM_SETREADONLY = 0x00CF, + EM_SETMARGINS = 0x00D3, + EM_POSFROMCHAR = 0x00D6, + EM_CHARFROMPOS = 0x00D7, + EM_LINEFROMCHAR = 0x00C9, + EM_GETFIRSTVISIBLELINE = 0x00CE, + EM_LINEINDEX = 0x00BB; + + public const int ERROR_INVALID_HANDLE = 6; + public const int ERROR_CLASS_ALREADY_EXISTS = 1410; + + public const int FNERR_SUBCLASSFAILURE = 0x3001, + FNERR_INVALIDFILENAME = 0x3002, + FNERR_BUFFERTOOSMALL = 0x3003, + FRERR_BUFFERLENGTHZERO = 0x4001, + FADF_BSTR = (0x100), + FADF_UNKNOWN = (0x200), + FADF_DISPATCH = (0x400), + FADF_VARIANT = (unchecked((int)0x800)), + FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000, + FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200, + FVIRTKEY = 0x01, + FSHIFT = 0x04, + FALT = 0x10; + + + public const int GMEM_FIXED = 0x0000, + GMEM_MOVEABLE = 0x0002, + GMEM_NOCOMPACT = 0x0010, + GMEM_NODISCARD = 0x0020, + GMEM_ZEROINIT = 0x0040, + GMEM_MODIFY = 0x0080, + GMEM_DISCARDABLE = 0x0100, + GMEM_NOT_BANKED = 0x1000, + GMEM_SHARE = 0x2000, + GMEM_DDESHARE = 0x2000, + GMEM_NOTIFY = 0x4000, + GMEM_LOWER = GMEM_NOT_BANKED, + GMEM_VALID_FLAGS = 0x7F72, + GMEM_INVALID_HANDLE = 0x8000, + GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT), + GPTR = (GMEM_FIXED | GMEM_ZEROINIT), + GCL_WNDPROC = (-24), + GWL_WNDPROC = (-4), + GWL_HWNDPARENT = (-8), + GWL_STYLE = (-16), + GWL_EXSTYLE = (-20), + GWL_ID = (-12), + GW_HWNDFIRST = 0, + GW_HWNDLAST = 1, + GW_HWNDNEXT = 2, + GW_HWNDPREV = 3, + GW_CHILD = 5, + GMR_VISIBLE = 0, + GMR_DAYSTATE = 1, + GDI_ERROR = (unchecked((int)0xFFFFFFFF)), + GDTR_MIN = 0x0001, + GDTR_MAX = 0x0002, + GDT_VALID = 0, + GDT_NONE = 1, + GA_PARENT = 1, + GA_ROOT = 2; + + // ImmGetCompostionString index. + public const int + GCS_COMPSTR = 0x0008, + GCS_COMPATTR = 0x0010, + GCS_RESULTSTR = 0x0800, + + // attribute for COMPOSITIONSTRING Structure + ATTR_INPUT = 0x00, + ATTR_TARGET_CONVERTED = 0x01, + ATTR_CONVERTED = 0x02, + ATTR_TARGET_NOTCONVERTED = 0x03, + ATTR_INPUT_ERROR = 0x04, + ATTR_FIXEDCONVERTED = 0x05, + + // dwAction for ImmNotifyIME + NI_COMPOSITIONSTR = 0x0015, + + // dwIndex for ImmNotifyIME/NI_COMPOSITIONSTR + CPS_COMPLETE = 0x01, + CPS_CANCEL = 0x04; + + public const int + HC_ACTION = 0, + HC_GETNEXT = 1, + HC_SKIP = 2, + HTTRANSPARENT = (-1), + HTNOWHERE = 0, + HTCLIENT = 1, + HTLEFT = 10, + HTBOTTOM = 15, + HTBOTTOMLEFT = 16, + HTBOTTOMRIGHT = 17, + HTBORDER = 18, + HELPINFO_WINDOW = 0x0001, + HCF_HIGHCONTRASTON = 0x00000001, + HDI_ORDER = 0x0080, + HDI_WIDTH = 0x0001, + HDM_GETITEMCOUNT = (0x1200+0), + HDM_INSERTITEMA = (0x1200+1), + HDM_INSERTITEMW = (0x1200+10), + HDM_GETITEMA = (0x1200+3), + HDM_GETITEMW = (0x1200+11), + HDM_LAYOUT = (0x1200+5), + HDM_SETITEMA = (0x1200+4), + HDM_SETITEMW = (0x1200+12), + HDN_ITEMCHANGINGA = ((0-300)-0), + HDN_ITEMCHANGINGW = ((0-300)-20), + HDN_ITEMCHANGEDA = ((0-300)-1), + HDN_ITEMCHANGEDW = ((0-300)-21), + HDN_ITEMCLICKA = ((0-300)-2), + HDN_ITEMCLICKW = ((0-300)-22), + HDN_ITEMDBLCLICKA = ((0-300)-3), + HDN_ITEMDBLCLICKW = ((0-300)-23), + HDN_DIVIDERDBLCLICKA = ((0-300)-5), + HDN_DIVIDERDBLCLICKW = ((0-300)-25), + HDN_BEGINTDRAG = ((0-300)-10), + HDN_BEGINTRACKA = ((0-300)-6), + HDN_BEGINTRACKW = ((0-300)-26), + HDN_ENDDRAG = ((0-300)-11), + HDN_ENDTRACKA = ((0-300)-7), + HDN_ENDTRACKW = ((0-300)-27), + HDN_TRACKA = ((0-300)-8), + HDN_TRACKW = ((0-300)-28), + HDN_GETDISPINFOA = ((0-300)-9), + HDN_GETDISPINFOW = ((0-300)-29); + // HOVER_DEFAULT = Do not use this value ever! It crashes entire servers. + + public const int HDS_FULLDRAG = 0x0080; + + // Corresponds to bitmaps in MENUITEMINFO + public const int HBMMENU_CALLBACK = -1, + HBMMENU_SYSTEM = 1, + HBMMENU_MBAR_RESTORE = 2, + HBMMENU_MBAR_MINIMIZE = 3, + HBMMENU_MBAR_CLOSE = 5, + HBMMENU_MBAR_CLOSE_D = 6, + HBMMENU_MBAR_MINIMIZE_D = 7, + HBMMENU_POPUP_CLOSE = 8, + HBMMENU_POPUP_RESTORE = 9, + HBMMENU_POPUP_MAXIMIZE = 10, + HBMMENU_POPUP_MINIMIZE = 11; + + + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public static HandleRef HWND_TOP = new HandleRef(null, (IntPtr)0); + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public static HandleRef HWND_BOTTOM = new HandleRef(null, (IntPtr)1); + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public static HandleRef HWND_TOPMOST = new HandleRef(null, new IntPtr(-1)); + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public static HandleRef HWND_NOTOPMOST = new HandleRef(null, new IntPtr(-2)); + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public static HandleRef HWND_MESSAGE = new HandleRef(null, new IntPtr(-3)); + + public const int IME_CMODE_NATIVE = 0x0001, + IME_CMODE_KATAKANA = 0x0002, + IME_CMODE_FULLSHAPE = 0x0008, + INPLACE_E_NOTOOLSPACE = unchecked((int)0x800401A1), + ICON_SMALL = 0, + ICON_BIG = 1, + IDC_ARROW = 32512, + IDC_IBEAM = 32513, + IDC_WAIT = 32514, + IDC_CROSS = 32515, + IDC_SIZEALL = 32646, + IDC_SIZENWSE = 32642, + IDC_SIZENESW = 32643, + IDC_SIZEWE = 32644, + IDC_SIZENS = 32645, + IDC_UPARROW = 32516, + IDC_NO = 32648, + IDC_APPSTARTING = 32650, + IDC_HELP = 32651, + IMAGE_ICON = 1, + IMAGE_CURSOR = 2, + ICC_LISTVIEW_CLASSES = 0x00000001, + ICC_TREEVIEW_CLASSES = 0x00000002, + ICC_BAR_CLASSES = 0x00000004, + ICC_TAB_CLASSES = 0x00000008, + ICC_PROGRESS_CLASS = 0x00000020, + ICC_DATE_CLASSES = 0x00000100, + ILC_MASK = 0x0001, + ILC_COLOR = 0x0000, + ILC_COLOR4 = 0x0004, + ILC_COLOR8 = 0x0008, + ILC_COLOR16 = 0x0010, + ILC_COLOR24 = 0x0018, + ILC_COLOR32 = 0x0020, + ILC_MIRROR = 0x00002000, + ILD_NORMAL = 0x0000, + ILD_TRANSPARENT = 0x0001, + ILD_MASK = 0x0010, + ILD_ROP = 0x0040, + + // ImageList + // + ILP_NORMAL = 0, + ILP_DOWNLEVEL = 1, + ILS_NORMAL = 0x0, + ILS_GLOW = 0x1, + ILS_SHADOW = 0x2, + ILS_SATURATE = 0x4, + ILS_ALPHA = 0x8; + + public const int IDM_PRINT = 27, + IDM_PAGESETUP = 2004, + IDM_PRINTPREVIEW = 2003, + IDM_PROPERTIES = 28, + IDM_SAVEAS = 71; + + public const int CSC_NAVIGATEFORWARD = 0x00000001, + CSC_NAVIGATEBACK = 0x00000002; + + public const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001); + public const int STG_E_FILENOTFOUND = unchecked((int)0x80030002); + public const int STG_E_PATHNOTFOUND = unchecked((int)0x80030003); + public const int STG_E_TOOMANYOPENFILES = unchecked((int)0x80030004); + public const int STG_E_ACCESSDENIED = unchecked((int)0x80030005); + public const int STG_E_INVALIDHANDLE = unchecked((int)0x80030006); + public const int STG_E_INSUFFICIENTMEMORY = unchecked((int)0x80030008); + public const int STG_E_INVALIDPOINTER = unchecked((int)0x80030009); + public const int STG_E_NOMOREFILES = unchecked((int)0x80030012); + public const int STG_E_DISKISWRITEPROTECTED = unchecked((int)0x80030013); + public const int STG_E_SEEKERROR = unchecked((int)0x80030019); + public const int STG_E_WRITEFAULT = unchecked((int)0x8003001D); + public const int STG_E_READFAULT = unchecked((int)0x8003001E); + public const int STG_E_SHAREVIOLATION = unchecked((int)0x80030020); + public const int STG_E_LOCKVIOLATION = unchecked((int)0x80030021); + + public const int INPUT_KEYBOARD = 1; + + public const int KEYEVENTF_EXTENDEDKEY = 0x0001; + public const int KEYEVENTF_KEYUP = 0x0002; + public const int KEYEVENTF_UNICODE = 0x0004; + + public const int LOGPIXELSX = 88, + LOGPIXELSY = 90, + LB_ERR = (-1), + LB_ERRSPACE = (-2), + LBN_SELCHANGE = 1, + LBN_DBLCLK = 2, + LB_ADDSTRING = 0x0180, + LB_INSERTSTRING = 0x0181, + LB_DELETESTRING = 0x0182, + LB_RESETCONTENT = 0x0184, + LB_SETSEL = 0x0185, + LB_SETCURSEL = 0x0186, + LB_GETSEL = 0x0187, + LB_GETCARETINDEX = 0x019F, + LB_GETCURSEL = 0x0188, + LB_GETTEXT = 0x0189, + LB_GETTEXTLEN = 0x018A, + LB_GETTOPINDEX = 0x018E, + LB_FINDSTRING = 0x018F, + LB_GETSELCOUNT = 0x0190, + LB_GETSELITEMS = 0x0191, + LB_SETTABSTOPS = 0x0192, + LB_SETHORIZONTALEXTENT = 0x0194, + LB_SETCOLUMNWIDTH = 0x0195, + LB_SETTOPINDEX = 0x0197, + LB_GETITEMRECT = 0x0198, + LB_SETITEMHEIGHT = 0x01A0, + LB_GETITEMHEIGHT = 0x01A1, + LB_FINDSTRINGEXACT = 0x01A2, + LB_ITEMFROMPOINT = 0x01A9, + LB_SETLOCALE = 0x01A5; + + public const int LBS_NOTIFY = 0x0001, + LBS_MULTIPLESEL = 0x0008, + LBS_OWNERDRAWFIXED = 0x0010, + LBS_OWNERDRAWVARIABLE = 0x0020, + LBS_HASSTRINGS = 0x0040, + LBS_USETABSTOPS = 0x0080, + LBS_NOINTEGRALHEIGHT = 0x0100, + LBS_MULTICOLUMN = 0x0200, + LBS_WANTKEYBOARDINPUT = 0x0400, + LBS_EXTENDEDSEL = 0x0800, + LBS_DISABLENOSCROLL = 0x1000, + LBS_NOSEL = 0x4000, + LOCK_WRITE = 0x1, + LOCK_EXCLUSIVE = 0x2, + LOCK_ONLYONCE = 0x4, + LV_VIEW_TILE = 0x0004, + LVBKIF_SOURCE_NONE = 0x0000, + LVBKIF_SOURCE_URL = 0x0002, + LVBKIF_STYLE_NORMAL = 0x0000, + LVBKIF_STYLE_TILE = 0x0010, + LVS_ICON = 0x0000, + LVS_REPORT = 0x0001, + LVS_SMALLICON = 0x0002, + LVS_LIST = 0x0003, + LVS_SINGLESEL = 0x0004, + LVS_SHOWSELALWAYS = 0x0008, + LVS_SORTASCENDING = 0x0010, + LVS_SORTDESCENDING = 0x0020, + LVS_SHAREIMAGELISTS = 0x0040, + LVS_NOLABELWRAP = 0x0080, + LVS_AUTOARRANGE = 0x0100, + LVS_EDITLABELS = 0x0200, + LVS_NOSCROLL = 0x2000, + LVS_ALIGNTOP = 0x0000, + LVS_ALIGNLEFT = 0x0800, + LVS_NOCOLUMNHEADER = 0x4000, + LVS_NOSORTHEADER = unchecked((int)0x8000), + LVS_OWNERDATA = 0x1000, + LVSCW_AUTOSIZE = -1, + LVSCW_AUTOSIZE_USEHEADER = -2, + LVM_REDRAWITEMS = (0x1000+21), + LVM_SCROLL=(0x1000+20), + LVM_SETBKCOLOR = (0x1000+1), + LVM_SETBKIMAGEA = (0x1000+68), + LVM_SETBKIMAGEW = (0x1000+138), + LVM_SETCALLBACKMASK = (0x1000+11), + LVM_GETCALLBACKMASK = (0x1000+10), + LVM_GETCOLUMNORDERARRAY = (0x1000+59), + LVM_GETITEMCOUNT = (0x1000+4), + LVM_SETCOLUMNORDERARRAY = (0x1000+58), + LVM_SETINFOTIP = (0x1000+173), + LVSIL_NORMAL = 0, + LVSIL_SMALL = 1, + LVSIL_STATE = 2, + LVM_SETIMAGELIST = (0x1000+3), + LVM_SETSELECTIONMARK = (0x1000+67), + LVM_SETTOOLTIPS = (0x1000+74), + LVIF_TEXT = 0x0001, + LVIF_IMAGE = 0x0002, + LVIF_INDENT = 0x0010, + LVIF_PARAM = 0x0004, + LVIF_STATE = 0x0008, + LVIF_GROUPID = 0x0100, + LVIF_COLUMNS = 0x0200, + LVIS_FOCUSED = 0x0001, + LVIS_SELECTED = 0x0002, + LVIS_CUT = 0x0004, + LVIS_DROPHILITED = 0x0008, + LVIS_OVERLAYMASK = 0x0F00, + LVIS_STATEIMAGEMASK = 0xF000, + LVM_GETITEMA = (0x1000+5), + LVM_GETITEMW = (0x1000+75), + LVM_SETITEMA = (0x1000+6), + LVM_SETITEMW = (0x1000+76), + LVM_SETITEMPOSITION32 = (0x01000 + 49), + LVM_INSERTITEMA = (0x1000+7), + LVM_INSERTITEMW = (0x1000+77), + LVM_DELETEITEM = (0x1000+8), + LVM_DELETECOLUMN = (0x1000+28), + LVM_DELETEALLITEMS = (0x1000+9), + LVM_UPDATE = (0x1000+42), + LVNI_FOCUSED = 0x0001, + LVNI_SELECTED = 0x0002, + LVM_GETNEXTITEM = (0x1000+12), + LVFI_PARAM = 0x0001, + LVFI_NEARESTXY = 0x0040, + LVFI_PARTIAL = 0x0008, + LVFI_STRING = 0x0002, + LVM_FINDITEMA = (0x1000+13), + LVM_FINDITEMW = (0x1000+83), + LVIR_BOUNDS = 0, + LVIR_ICON = 1, + LVIR_LABEL = 2, + LVIR_SELECTBOUNDS = 3, + LVM_GETITEMPOSITION = (0x1000+16), + LVM_GETITEMRECT = (0x1000+14), + LVM_GETSUBITEMRECT = (0x1000+56), + LVM_GETSTRINGWIDTHA = (0x1000+17), + LVM_GETSTRINGWIDTHW = (0x1000+87), + LVHT_NOWHERE = 0x0001, + LVHT_ONITEMICON = 0x0002, + LVHT_ONITEMLABEL = 0x0004, + LVHT_ABOVE = 0x0008, + LVHT_BELOW = 0x0010, + LVHT_RIGHT = 0x0020, + LVHT_LEFT = 0x0040, + LVHT_ONITEM = (0x0002|0x0004|0x0008), + LVHT_ONITEMSTATEICON = 0x0008, + LVM_SUBITEMHITTEST = (0x1000 + 57), + LVM_HITTEST = (0x1000+18), + LVM_ENSUREVISIBLE = (0x1000+19), + LVA_DEFAULT = 0x0000, + LVA_ALIGNLEFT = 0x0001, + LVA_ALIGNTOP = 0x0002, + LVA_SNAPTOGRID = 0x0005, + LVM_ARRANGE = (0x1000+22), + LVM_EDITLABELA = (0x1000+23), + LVM_EDITLABELW = (0x1000+118), + LVCDI_ITEM = 0x0000, + LVCDI_GROUP = 0x00000001, + LVCF_FMT = 0x0001, + LVCF_WIDTH = 0x0002, + LVCF_TEXT = 0x0004, + LVCF_SUBITEM = 0x0008, + LVCF_IMAGE = 0x0010, + LVCF_ORDER = 0x0020, + LVCFMT_IMAGE = 0x0800, + LVGA_HEADER_LEFT = 0x00000001, + LVGA_HEADER_CENTER = 0x00000002, + LVGA_HEADER_RIGHT = 0x00000004, + LVGA_FOOTER_LEFT = 0x00000008, + LVGA_FOOTER_CENTER = 0x00000010, + LVGA_FOOTER_RIGHT = 0x00000020, + LVGF_NONE = 0x00000000, + LVGF_HEADER = 0x00000001, + LVGF_FOOTER = 0x00000002, + LVGF_STATE = 0x00000004, + LVGF_ALIGN = 0x00000008, + LVGF_GROUPID = 0x00000010, + LVGS_NORMAL = 0x00000000, + LVGS_COLLAPSED = 0x00000001, + LVGS_HIDDEN = 0x00000002, + LVIM_AFTER = 0x00000001, + LVTVIF_FIXEDSIZE = 0x00000003, + LVTVIM_TILESIZE = 0x00000001, + LVTVIM_COLUMNS = 0x00000002, + LVM_ENABLEGROUPVIEW = (0x1000 + 157), + LVM_MOVEITEMTOGROUP = (0x1000 + 154), + LVM_GETCOLUMNA = (0x1000+25), + LVM_GETCOLUMNW = (0x1000+95), + LVM_SETCOLUMNA = (0x1000+26), + LVM_SETCOLUMNW = (0x1000+96), + LVM_INSERTCOLUMNA = (0x1000+27), + LVM_INSERTCOLUMNW = (0x1000+97), + LVM_INSERTGROUP = (0x1000 + 145), + LVM_REMOVEGROUP = (0x1000 + 150), + LVM_INSERTMARKHITTEST = (0x1000 + 168), + LVM_REMOVEALLGROUPS = (0x1000 + 160), + LVM_GETCOLUMNWIDTH = (0x1000+29), + LVM_SETCOLUMNWIDTH = (0x1000+30), + LVM_SETINSERTMARK = (0x1000 + 166), + LVM_GETHEADER = (0x1000+31), + LVM_SETTEXTCOLOR = (0x1000+36), + LVM_SETTEXTBKCOLOR = (0x1000+38), + LVM_GETTOPINDEX = (0x1000+39), + LVM_SETITEMPOSITION = (0x1000+15), + LVM_SETITEMSTATE = (0x1000+43), + LVM_GETITEMSTATE = (0x1000+44), + LVM_GETITEMTEXTA = (0x1000+45), + LVM_GETITEMTEXTW = (0x1000+115), + LVM_GETHOTITEM = (0x1000+61), + LVM_SETITEMTEXTA = (0x1000+46), + LVM_SETITEMTEXTW = (0x1000+116), + LVM_SETITEMCOUNT = (0x1000+47), + LVM_SORTITEMS = (0x1000+48), + LVM_GETSELECTEDCOUNT = (0x1000+50), + LVM_GETISEARCHSTRINGA = (0x1000+52), + LVM_GETISEARCHSTRINGW = (0x1000+117), + LVM_SETEXTENDEDLISTVIEWSTYLE = (0x1000+54), + LVM_SETVIEW = (0x1000 + 142), + LVM_GETGROUPINFO = (0x1000 + 149), + LVM_SETGROUPINFO = (0x1000 + 147), + LVM_HASGROUP = (0x1000 + 161), + LVM_SETTILEVIEWINFO = (0x1000 + 162), + LVM_GETTILEVIEWINFO = (0x1000 + 163), + LVM_GETINSERTMARK = (0x1000 + 167), + LVM_GETINSERTMARKRECT = (0x1000 + 169), + LVM_SETINSERTMARKCOLOR = (0x1000 + 170), + LVM_GETINSERTMARKCOLOR = (0x1000 + 171), + LVM_ISGROUPVIEWENABLED = (0x1000 + 175), + LVS_EX_GRIDLINES = 0x00000001, + LVS_EX_CHECKBOXES = 0x00000004, + LVS_EX_TRACKSELECT = 0x00000008, + LVS_EX_HEADERDRAGDROP = 0x00000010, + LVS_EX_FULLROWSELECT = 0x00000020, + LVS_EX_ONECLICKACTIVATE = 0x00000040, + LVS_EX_TWOCLICKACTIVATE = 0x00000080, + LVS_EX_INFOTIP = 0x00000400, + LVS_EX_UNDERLINEHOT = 0x00000800, + LVS_EX_DOUBLEBUFFER = 0x00010000, + LVN_ITEMCHANGING = ((0-100)-0), + LVN_ITEMCHANGED = ((0-100)-1), + LVN_BEGINLABELEDITA = ((0-100)-5), + LVN_BEGINLABELEDITW = ((0-100)-75), + LVN_ENDLABELEDITA = ((0-100)-6), + LVN_ENDLABELEDITW = ((0-100)-76), + LVN_COLUMNCLICK = ((0-100)-8), + LVN_BEGINDRAG = ((0-100)-9), + LVN_BEGINRDRAG = ((0-100)-11), + LVN_ODFINDITEMA = ((0-100)-52), + LVN_ODFINDITEMW = ((0-100)-79), + LVN_ITEMACTIVATE = ((0-100)-14), + LVN_GETDISPINFOA = ((0-100)-50), + LVN_GETDISPINFOW = ((0-100)-77), + LVN_ODCACHEHINT = ((0-100) - 13), + LVN_ODSTATECHANGED = ((0-100) - 15), + LVN_SETDISPINFOA = ((0-100)-51), + LVN_SETDISPINFOW = ((0-100)-78), + LVN_GETINFOTIPA = ((0-100)-57), + LVN_GETINFOTIPW = ((0-100)- 58), + LVN_KEYDOWN = ((0-100)-55), + + LWA_COLORKEY = 0x00000001, + LWA_ALPHA = 0x00000002; + + public const int LANG_NEUTRAL = 0x00, + LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; /* first day of week specifier */ + + public const int LOCALE_IMEASURE = 0x0000000D; // 0 = metric, 1 = US + + public static readonly int LOCALE_USER_DEFAULT = MAKELCID(LANG_USER_DEFAULT); + public static readonly int LANG_USER_DEFAULT = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); + + public static int MAKELANGID(int primary, int sub) { + return ((((ushort)(sub)) << 10) | (ushort)(primary)); + } + + /// + /// + /// Creates an LCID from a LangId + /// + public static int MAKELCID(int lgid) { + return MAKELCID(lgid, SORT_DEFAULT); + } + + /// + /// + /// Creates an LCID from a LangId + /// + public static int MAKELCID(int lgid, int sort) { + return ((0xFFFF & lgid) | (((0x000f) & sort) << 16)); + } + + + public const int MEMBERID_NIL = (-1), + MAX_PATH = 260, + MAX_UNICODESTRING_LEN = short.MaxValue, // maximum unicode string length + ERROR_INSUFFICIENT_BUFFER = 122, //https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx + MA_ACTIVATE = 0x0001, + MA_ACTIVATEANDEAT = 0x0002, + MA_NOACTIVATE = 0x0003, + MA_NOACTIVATEANDEAT = 0x0004, + MM_TEXT = 1, + MM_ANISOTROPIC = 8, + MK_LBUTTON = 0x0001, + MK_RBUTTON = 0x0002, + MK_SHIFT = 0x0004, + MK_CONTROL = 0x0008, + MK_MBUTTON = 0x0010, + MNC_EXECUTE = 2, + MNC_SELECT = 3, + MIIM_STATE = 0x00000001, + MIIM_ID = 0x00000002, + MIIM_SUBMENU = 0x00000004, + MIIM_TYPE = 0x00000010, + MIIM_DATA = 0x00000020, + MIIM_STRING = 0x00000040, + MIIM_BITMAP = 0x00000080, + MIIM_FTYPE = 0x00000100, + MB_OK = 0x00000000, + MF_BYCOMMAND = 0x00000000, + MF_BYPOSITION = 0x00000400, + MF_ENABLED = 0x00000000, + MF_GRAYED = 0x00000001, + MF_POPUP = 0x00000010, + MF_SYSMENU = 0x00002000, + MFS_DISABLED = 0x00000003, + MFT_MENUBREAK = 0x00000040, + MFT_SEPARATOR = 0x00000800, + MFT_RIGHTORDER = 0x00002000, + MFT_RIGHTJUSTIFY = 0x00004000, + MDIS_ALLCHILDSTYLES = 0x0001, + MDITILE_VERTICAL = 0x0000, + MDITILE_HORIZONTAL = 0x0001, + MDITILE_SKIPDISABLED = 0x0002, + MCM_SETMAXSELCOUNT = (0x1000+4), + MCM_SETSELRANGE = (0x1000+6), + MCM_GETMONTHRANGE = (0x1000+7), + MCM_GETMINREQRECT = (0x1000+9), + MCM_SETCOLOR = (0x1000+10), + MCM_SETTODAY = (0x1000+12), + MCM_GETTODAY = (0x1000+13), + MCM_HITTEST = (0x1000+14), + MCM_SETFIRSTDAYOFWEEK = (0x1000+15), + MCM_SETRANGE = (0x1000+18), + MCM_SETMONTHDELTA = (0x1000+20), + MCM_GETMAXTODAYWIDTH = (0x1000+21), + MCHT_TITLE = 0x00010000, + MCHT_CALENDAR = 0x00020000, + MCHT_TODAYLINK = 0x00030000, + MCHT_TITLEBK = (0x00010000), + MCHT_TITLEMONTH = (0x00010000|0x0001), + MCHT_TITLEYEAR = (0x00010000|0x0002), + MCHT_TITLEBTNNEXT = (0x00010000|0x01000000|0x0003), + MCHT_TITLEBTNPREV = (0x00010000|0x02000000|0x0003), + MCHT_CALENDARBK = (0x00020000), + MCHT_CALENDARDATE = (0x00020000|0x0001), + MCHT_CALENDARDATENEXT = ((0x00020000|0x0001)|0x01000000), + MCHT_CALENDARDATEPREV = ((0x00020000|0x0001)|0x02000000), + MCHT_CALENDARDAY = (0x00020000|0x0002), + MCHT_CALENDARWEEKNUM = (0x00020000|0x0003), + MCSC_TEXT = 1, + MCSC_TITLEBK = 2, + MCSC_TITLETEXT = 3, + MCSC_MONTHBK = 4, + MCSC_TRAILINGTEXT = 5, + MCN_VIEWCHANGE = (0 - 750), // MCN_SELECT -4 - give state of calendar view + MCN_SELCHANGE = ((0-750)+1), + MCN_GETDAYSTATE = ((0-750)+3), + MCN_SELECT = ((0-750)+4), + MCS_DAYSTATE = 0x0001, + MCS_MULTISELECT = 0x0002, + MCS_WEEKNUMBERS = 0x0004, + MCS_NOTODAYCIRCLE = 0x0008, + MCS_NOTODAY = 0x0010, + MSAA_MENU_SIG = (unchecked((int) 0xAA0DF00D)); + + public const int NIM_ADD = 0x00000000, + NIM_MODIFY = 0x00000001, + NIM_DELETE = 0x00000002, + NIF_MESSAGE = 0x00000001, + NIM_SETVERSION = 0x00000004, + NIF_ICON = 0x00000002, + NIF_INFO = 0x00000010, + NIF_TIP = 0x00000004, + NIIF_NONE = 0x00000000, + NIIF_INFO = 0x00000001, + NIIF_WARNING = 0x00000002, + NIIF_ERROR = 0x00000003, + NIN_BALLOONSHOW = (WM_USER + 2), + NIN_BALLOONHIDE = (WM_USER + 3), + NIN_BALLOONTIMEOUT = (WM_USER + 4), + NIN_BALLOONUSERCLICK = (WM_USER + 5), + NFR_ANSI = 1, + NFR_UNICODE = 2, + NM_CLICK = ((0-0)-2), + NM_DBLCLK = ((0-0)-3), + NM_RCLICK = ((0-0)-5), + NM_RDBLCLK = ((0-0)-6), + NM_CUSTOMDRAW = ((0-0)-12), + NM_RELEASEDCAPTURE = ((0-0)-16), + NONANTIALIASED_QUALITY = 3; + + public const int OFN_READONLY = 0x00000001, + OFN_OVERWRITEPROMPT = 0x00000002, + OFN_HIDEREADONLY = 0x00000004, + OFN_NOCHANGEDIR = 0x00000008, + OFN_SHOWHELP = 0x00000010, + OFN_ENABLEHOOK = 0x00000020, + OFN_NOVALIDATE = 0x00000100, + OFN_ALLOWMULTISELECT = 0x00000200, + OFN_PATHMUSTEXIST = 0x00000800, + OFN_FILEMUSTEXIST = 0x00001000, + OFN_CREATEPROMPT = 0x00002000, + OFN_EXPLORER = 0x00080000, + OFN_NODEREFERENCELINKS = 0x00100000, + OFN_ENABLESIZING = 0x00800000, + OFN_USESHELLITEM = 0x01000000, + OLEIVERB_PRIMARY = 0, + OLEIVERB_SHOW = -1, + OLEIVERB_HIDE = -3, + OLEIVERB_UIACTIVATE = -4, + OLEIVERB_INPLACEACTIVATE = -5, + OLEIVERB_DISCARDUNDOSTATE= -6, + OLEIVERB_PROPERTIES = -7, + OLE_E_INVALIDRECT = unchecked((int)0x8004000D), + OLE_E_NOCONNECTION = unchecked((int)0x80040004), + OLE_E_PROMPTSAVECANCELLED = unchecked((int)0x8004000C), + OLEMISC_RECOMPOSEONRESIZE = 0x00000001, + OLEMISC_INSIDEOUT = 0x00000080, + OLEMISC_ACTIVATEWHENVISIBLE = 0x0000100, + OLEMISC_ACTSLIKEBUTTON = 0x00001000, + OLEMISC_SETCLIENTSITEFIRST = 0x00020000, + OBJ_PEN = 1, + OBJ_BRUSH = 2, + OBJ_DC = 3, + OBJ_METADC = 4, + OBJ_PAL = 5, + OBJ_FONT = 6, + OBJ_BITMAP = 7, + OBJ_REGION = 8, + OBJ_METAFILE = 9, + OBJ_MEMDC = 10, + OBJ_EXTPEN = 11, + OBJ_ENHMETADC = 12, + ODS_CHECKED = 0x0008, + ODS_COMBOBOXEDIT = 0x1000, + ODS_DEFAULT = 0x0020, + ODS_DISABLED = 0x0004, + ODS_FOCUS = 0x0010, + ODS_GRAYED = 0x0002, + ODS_HOTLIGHT = 0x0040, + ODS_INACTIVE = 0x0080, + ODS_NOACCEL = 0x0100, + ODS_NOFOCUSRECT = 0x0200, + ODS_SELECTED = 0x0001, + OLECLOSE_SAVEIFDIRTY = 0, + OLECLOSE_PROMPTSAVE = 2; + + public const int PDERR_SETUPFAILURE = 0x1001, + PDERR_PARSEFAILURE = 0x1002, + PDERR_RETDEFFAILURE = 0x1003, + PDERR_LOADDRVFAILURE = 0x1004, + PDERR_GETDEVMODEFAIL = 0x1005, + PDERR_INITFAILURE = 0x1006, + PDERR_NODEVICES = 0x1007, + PDERR_NODEFAULTPRN = 0x1008, + PDERR_DNDMMISMATCH = 0x1009, + PDERR_CREATEICFAILURE = 0x100A, + PDERR_PRINTERNOTFOUND = 0x100B, + PDERR_DEFAULTDIFFERENT = 0x100C, + PD_ALLPAGES = 0x00000000, + PD_SELECTION = 0x00000001, + PD_PAGENUMS = 0x00000002, + PD_NOSELECTION = 0x00000004, + PD_NOPAGENUMS = 0x00000008, + PD_COLLATE = 0x00000010, + PD_PRINTTOFILE = 0x00000020, + PD_PRINTSETUP = 0x00000040, + PD_NOWARNING = 0x00000080, + PD_RETURNDC = 0x00000100, + PD_RETURNIC = 0x00000200, + PD_RETURNDEFAULT = 0x00000400, + PD_SHOWHELP = 0x00000800, + PD_ENABLEPRINTHOOK = 0x00001000, + PD_ENABLESETUPHOOK = 0x00002000, + PD_ENABLEPRINTTEMPLATE = 0x00004000, + PD_ENABLESETUPTEMPLATE = 0x00008000, + PD_ENABLEPRINTTEMPLATEHANDLE = 0x00010000, + PD_ENABLESETUPTEMPLATEHANDLE = 0x00020000, + PD_USEDEVMODECOPIES = 0x00040000, + PD_USEDEVMODECOPIESANDCOLLATE = 0x00040000, + PD_DISABLEPRINTTOFILE = 0x00080000, + PD_HIDEPRINTTOFILE = 0x00100000, + PD_NONETWORKBUTTON = 0x00200000, + PD_CURRENTPAGE = 0x00400000, + PD_NOCURRENTPAGE = 0x00800000, + PD_EXCLUSIONFLAGS = 0x01000000, + PD_USELARGETEMPLATE = 0x10000000, + PSD_MINMARGINS = 0x00000001, + PSD_MARGINS = 0x00000002, + PSD_INHUNDREDTHSOFMILLIMETERS = 0x00000008, + PSD_DISABLEMARGINS = 0x00000010, + PSD_DISABLEPRINTER = 0x00000020, + PSD_DISABLEORIENTATION = 0x00000100, + PSD_DISABLEPAPER = 0x00000200, + PSD_SHOWHELP = 0x00000800, + PSD_ENABLEPAGESETUPHOOK = 0x00002000, + PSD_NONETWORKBUTTON = 0x00200000, + PS_SOLID = 0, + PS_DOT = 2, + PLANES = 14, + PRF_CHECKVISIBLE = 0x00000001, + PRF_NONCLIENT = 0x00000002, + PRF_CLIENT = 0x00000004, + PRF_ERASEBKGND = 0x00000008, + PRF_CHILDREN = 0x00000010, + PM_NOREMOVE = 0x0000, + PM_REMOVE = 0x0001, + PM_NOYIELD = 0x0002, + PBM_SETRANGE = (0x0400+1), + PBM_SETPOS = (0x0400+2), + PBM_SETSTEP = (0x0400+4), + PBM_SETRANGE32 = (0x0400+6), + PBM_SETBARCOLOR = (0x0400+9), + PBM_SETMARQUEE = (0x0400+10), + PBM_SETBKCOLOR = (0x2000 +1), + PSM_SETTITLEA = (0x0400+111), + PSM_SETTITLEW = (0x0400+120), + PSM_SETFINISHTEXTA = (0x0400+115), + PSM_SETFINISHTEXTW = (0x0400+121), + PATCOPY = 0x00F00021, + PATINVERT = 0x005A0049; + + public const int PBS_SMOOTH = 0x01, + PBS_MARQUEE = 0x08; + + public const int QS_KEY = 0x0001, + QS_MOUSEMOVE = 0x0002, + QS_MOUSEBUTTON = 0x0004, + QS_POSTMESSAGE = 0x0008, + QS_TIMER = 0x0010, + QS_PAINT = 0x0020, + QS_SENDMESSAGE = 0x0040, + QS_HOTKEY = 0x0080, + QS_ALLPOSTMESSAGE = 0x0100, + QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON, + QS_INPUT = QS_MOUSE | QS_KEY, + QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY, + QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE; + + public const int MWMO_INPUTAVAILABLE = 0x0004; // don't use MWMO_WAITALL, see ddb#176342 + + //public const int RECO_PASTE = 0x00000000; // paste from clipboard + public const int RECO_DROP = 0x00000001; // drop + //public const int RECO_COPY = 0x00000002; // copy to the clipboard + //public const int RECO_CUT = 0x00000003; // cut to the clipboard + //public const int RECO_DRAG = 0x00000004; // drag + + public const int RPC_E_CHANGED_MODE = unchecked((int)0x80010106), + RPC_E_CANTCALLOUT_ININPUTSYNCCALL = unchecked((int)0x8001010D), + RGN_AND = 1, + RGN_XOR = 3, + RGN_DIFF = 4, + RDW_INVALIDATE = 0x0001, + RDW_ERASE = 0x0004, + RDW_ALLCHILDREN = 0x0080, + RDW_ERASENOW = 0x0200, + RDW_UPDATENOW = 0x0100, + RDW_FRAME = 0x0400, + RB_INSERTBANDA = (0x0400+1), + RB_INSERTBANDW = (0x0400+10); + + public const int stc4 = 0x0443, + SHGFP_TYPE_CURRENT = 0, + STGM_READ = 0x00000000, + STGM_WRITE = 0x00000001, + STGM_READWRITE = 0x00000002, + STGM_SHARE_EXCLUSIVE = 0x00000010, + STGM_CREATE = 0x00001000, + STGM_TRANSACTED = 0x00010000, + STGM_CONVERT = 0x00020000, + STGM_DELETEONRELEASE = 0x04000000, + STARTF_USESHOWWINDOW = 0x00000001, + SB_HORZ = 0, + SB_VERT = 1, + SB_CTL = 2, + SB_LINEUP = 0, + SB_LINELEFT = 0, + SB_LINEDOWN = 1, + SB_LINERIGHT = 1, + SB_PAGEUP = 2, + SB_PAGELEFT = 2, + SB_PAGEDOWN = 3, + SB_PAGERIGHT = 3, + SB_THUMBPOSITION = 4, + SB_THUMBTRACK = 5, + SB_LEFT = 6, + SB_RIGHT = 7, + SB_ENDSCROLL = 8, + SB_TOP = 6, + SB_BOTTOM = 7, + SIZE_RESTORED = 0, + SIZE_MAXIMIZED = 2, + ESB_ENABLE_BOTH = 0x0000, + ESB_DISABLE_BOTH =0x0003, + SORT_DEFAULT =0x0, + SUBLANG_DEFAULT = 0x01, + SW_HIDE = 0, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_MAX = 10, + SWP_NOSIZE = 0x0001, + SWP_NOMOVE = 0x0002, + SWP_NOZORDER = 0x0004, + SWP_NOACTIVATE = 0x0010, + SWP_SHOWWINDOW = 0x0040, + SWP_HIDEWINDOW = 0x0080, + SWP_DRAWFRAME = 0x0020, + SWP_NOOWNERZORDER = 0x0200, + SM_CXSCREEN = 0, + SM_CYSCREEN = 1, + SM_CXVSCROLL = 2, + SM_CYHSCROLL = 3, + SM_CYCAPTION = 4, + SM_CXBORDER = 5, + SM_CYBORDER = 6, + SM_CYVTHUMB = 9, + SM_CXHTHUMB = 10, + SM_CXICON = 11, + SM_CYICON = 12, + SM_CXCURSOR = 13, + SM_CYCURSOR = 14, + SM_CYMENU = 15, + SM_CYKANJIWINDOW = 18, + SM_MOUSEPRESENT = 19, + SM_CYVSCROLL = 20, + SM_CXHSCROLL = 21, + SM_DEBUG = 22, + SM_SWAPBUTTON = 23, + SM_CXMIN = 28, + SM_CYMIN = 29, + SM_CXSIZE = 30, + SM_CYSIZE = 31, + SM_CXFRAME = 32, + SM_CYFRAME = 33, + SM_CXMINTRACK = 34, + SM_CYMINTRACK = 35, + SM_CXDOUBLECLK = 36, + SM_CYDOUBLECLK = 37, + SM_CXICONSPACING = 38, + SM_CYICONSPACING = 39, + SM_MENUDROPALIGNMENT = 40, + SM_PENWINDOWS = 41, + SM_DBCSENABLED = 42, + SM_CMOUSEBUTTONS = 43, + SM_CXFIXEDFRAME = 7, + SM_CYFIXEDFRAME = 8, + SM_SECURE = 44, + SM_CXEDGE = 45, + SM_CYEDGE = 46, + SM_CXMINSPACING = 47, + SM_CYMINSPACING = 48, + SM_CXSMICON = 49, + SM_CYSMICON = 50, + SM_CYSMCAPTION = 51, + SM_CXSMSIZE = 52, + SM_CYSMSIZE = 53, + SM_CXMENUSIZE = 54, + SM_CYMENUSIZE = 55, + SM_ARRANGE = 56, + SM_CXMINIMIZED = 57, + SM_CYMINIMIZED = 58, + SM_CXMAXTRACK = 59, + SM_CYMAXTRACK = 60, + SM_CXMAXIMIZED = 61, + SM_CYMAXIMIZED = 62, + SM_NETWORK = 63, + SM_CLEANBOOT = 67, + SM_CXDRAG = 68, + SM_CYDRAG = 69, + SM_SHOWSOUNDS = 70, + SM_CXMENUCHECK = 71, + SM_CYMENUCHECK = 72, + SM_MIDEASTENABLED = 74, + SM_MOUSEWHEELPRESENT = 75, + SM_XVIRTUALSCREEN = 76, + SM_YVIRTUALSCREEN = 77, + SM_CXVIRTUALSCREEN = 78, + SM_CYVIRTUALSCREEN = 79, + SM_CMONITORS = 80, + SM_SAMEDISPLAYFORMAT = 81, + SM_REMOTESESSION = 0x1000; + + + public const int HLP_FILE = 1, + HLP_KEYWORD = 2, + HLP_NAVIGATOR = 3, + HLP_OBJECT = 4; + + public const int SW_SCROLLCHILDREN = 0x0001, + SW_INVALIDATE = 0x0002, + SW_ERASE = 0x0004, + SW_SMOOTHSCROLL = 0x0010, + SC_SIZE = 0xF000, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_CLOSE = 0xF060, + SC_KEYMENU = 0xF100, + SC_RESTORE = 0xF120, + SC_MOVE = 0xF010, + SC_CONTEXTHELP = 0xF180, + SS_LEFT = 0x00000000, + SS_CENTER = 0x00000001, + SS_RIGHT = 0x00000002, + SS_OWNERDRAW = 0x0000000D, + SS_NOPREFIX = 0x00000080, + SS_SUNKEN = 0x00001000, + SBS_HORZ = 0x0000, + SBS_VERT = 0x0001, + SIF_RANGE = 0x0001, + SIF_PAGE = 0x0002, + SIF_POS = 0x0004, + SIF_TRACKPOS = 0x0010, + SIF_ALL = (0x0001|0x0002|0x0004|0x0010), + SPI_GETFONTSMOOTHING = 0x004A, + SPI_GETDROPSHADOW = 0x1024, + SPI_GETFLATMENU = 0x1022, + SPI_GETFONTSMOOTHINGTYPE = 0x200A, + SPI_GETFONTSMOOTHINGCONTRAST = 0x200C, + SPI_ICONHORIZONTALSPACING = 0x000D, + SPI_ICONVERTICALSPACING = 0x0018, + // SPI_GETICONMETRICS = 0x002D, + SPI_GETICONTITLEWRAP = 0x0019, + SPI_GETICONTITLELOGFONT = 0x001F, + SPI_GETKEYBOARDCUES = 0x100A, + SPI_GETKEYBOARDDELAY = 0x0016, + SPI_GETKEYBOARDPREF = 0x0044, + SPI_GETKEYBOARDSPEED = 0x000A, + SPI_GETMOUSEHOVERWIDTH = 0x0062, + SPI_GETMOUSEHOVERHEIGHT = 0x0064, + SPI_GETMOUSEHOVERTIME = 0x0066, + SPI_GETMOUSESPEED = 0x0070, + SPI_GETMENUDROPALIGNMENT = 0x001B, + SPI_GETMENUFADE = 0x1012, + SPI_GETMENUSHOWDELAY = 0x006A, + SPI_GETCOMBOBOXANIMATION = 0x1004, + SPI_GETGRADIENTCAPTIONS = 0x1008, + SPI_GETHOTTRACKING = 0x100E, + SPI_GETLISTBOXSMOOTHSCROLLING = 0x1006, + SPI_GETMENUANIMATION = 0x1002, + SPI_GETSELECTIONFADE = 0x1014, + SPI_GETTOOLTIPANIMATION = 0x1016, + SPI_GETUIEFFECTS = 0x103E, + SPI_GETACTIVEWINDOWTRACKING = 0x1000, + SPI_GETACTIVEWNDTRKTIMEOUT = 0x2002, + SPI_GETANIMATION = 0x0048, + SPI_GETBORDER = 0x0005, + SPI_GETCARETWIDTH = 0x2006, + SM_CYFOCUSBORDER = 84, + SM_CXFOCUSBORDER = 83, + SM_CYSIZEFRAME = SM_CYFRAME, + SM_CXSIZEFRAME = SM_CXFRAME, + SPI_GETDRAGFULLWINDOWS = 38, + SPI_GETNONCLIENTMETRICS = 41, + SPI_GETWORKAREA = 48, + SPI_GETHIGHCONTRAST = 66, + SPI_GETDEFAULTINPUTLANG = 89, + SPI_GETSNAPTODEFBUTTON = 95, + SPI_GETWHEELSCROLLLINES = 104, + SBARS_SIZEGRIP = 0x0100, + SB_SETTEXTA = (0x0400+1), + SB_SETTEXTW = (0x0400+11), + SB_GETTEXTA = (0x0400+2), + SB_GETTEXTW = (0x0400+13), + SB_GETTEXTLENGTHA = (0x0400+3), + SB_GETTEXTLENGTHW = (0x0400+12), + SB_SETPARTS = (0x0400+4), + SB_SIMPLE = (0x0400+9), + SB_GETRECT = (0x0400+10), + SB_SETICON = (0x0400+15), + SB_SETTIPTEXTA = (0x0400+16), + SB_SETTIPTEXTW = (0x0400+17), + SB_GETTIPTEXTA = (0x0400+18), + SB_GETTIPTEXTW = (0x0400+19), + SBT_OWNERDRAW = 0x1000, + SBT_NOBORDERS = 0x0100, + SBT_POPOUT = 0x0200, + SBT_RTLREADING = 0x0400, + SRCCOPY = 0x00CC0020, + SRCAND = 0x008800C6, /* dest = source AND dest */ + SRCPAINT = 0x00EE0086, /* dest = source OR dest */ + NOTSRCCOPY = 0x00330008, /* dest = (NOT source) */ + STATFLAG_DEFAULT = 0x0, + STATFLAG_NONAME = 0x1, + STATFLAG_NOOPEN = 0x2, + STGC_DEFAULT = 0x0, + STGC_OVERWRITE = 0x1, + STGC_ONLYIFCURRENT = 0x2, + STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4, + STREAM_SEEK_SET = 0x0, + STREAM_SEEK_CUR = 0x1, + STREAM_SEEK_END = 0x2; + + public const int S_OK = 0x00000000; + public const int S_FALSE = 0x00000001; + + public static bool Succeeded(int hr) { + return(hr >= 0); + } + + public static bool Failed(int hr) { + return(hr < 0); + } + + public const int TRANSPARENT = 1, + OPAQUE = 2, + TME_HOVER = 0x00000001, + TME_LEAVE = 0x00000002, + TPM_LEFTBUTTON = 0x0000, + TPM_RIGHTBUTTON = 0x0002, + TPM_LEFTALIGN = 0x0000, + TPM_RIGHTALIGN = 0x0008, + TPM_VERTICAL = 0x0040, + TV_FIRST = 0x1100, + TBSTATE_CHECKED = 0x01, + TBSTATE_ENABLED = 0x04, + TBSTATE_HIDDEN = 0x08, + TBSTATE_INDETERMINATE = 0x10, + TBSTYLE_BUTTON = 0x00, + TBSTYLE_SEP = 0x01, + TBSTYLE_CHECK = 0x02, + TBSTYLE_DROPDOWN = 0x08, + TBSTYLE_TOOLTIPS = 0x0100, + TBSTYLE_FLAT = 0x0800, + TBSTYLE_LIST = 0x1000, + TBSTYLE_EX_DRAWDDARROWS = 0x00000001, + TB_ENABLEBUTTON = (0x0400 + 1), + TB_ISBUTTONCHECKED = (0x0400 + 10), + TB_ISBUTTONINDETERMINATE = (0x0400 + 13), + TB_ADDBUTTONSA = (0x0400 + 20), + TB_ADDBUTTONSW = (0x0400 + 68), + TB_INSERTBUTTONA = (0x0400 + 21), + TB_INSERTBUTTONW = (0x0400 + 67), + TB_DELETEBUTTON = (0x0400 + 22), + TB_GETBUTTON = (0x0400 + 23), + TB_SAVERESTOREA = (0x0400 + 26), + TB_SAVERESTOREW = (0x0400 + 76), + TB_ADDSTRINGA = (0x0400 + 28), + TB_ADDSTRINGW = (0x0400 + 77), + TB_BUTTONSTRUCTSIZE = (0x0400 + 30), + TB_SETBUTTONSIZE = (0x0400 + 31), + TB_AUTOSIZE = (0x0400 + 33), + TB_GETROWS = (0x0400 + 40), + TB_GETBUTTONTEXTA = (0x0400 + 45), + TB_GETBUTTONTEXTW = (0x0400 + 75), + TB_SETIMAGELIST = (0x0400 + 48), + TB_GETRECT = (0x0400 + 51), + TB_GETBUTTONSIZE = (0x0400 + 58), + TB_GETBUTTONINFOW = (0x0400 + 63), + TB_SETBUTTONINFOW = (0x0400 + 64), + TB_GETBUTTONINFOA = (0x0400 + 65), + TB_SETBUTTONINFOA = (0x0400 + 66), + TB_MAPACCELERATORA = (0x0400 + 78), + TB_SETEXTENDEDSTYLE = (0x0400 + 84), + TB_MAPACCELERATORW = (0x0400 + 90), + TB_GETTOOLTIPS = (0x0400 + 35), + TB_SETTOOLTIPS = (0x0400 + 36), + TBIF_IMAGE = 0x00000001, + TBIF_TEXT = 0x00000002, + TBIF_STATE = 0x00000004, + TBIF_STYLE = 0x00000008, + TBIF_COMMAND = 0x00000020, + TBIF_SIZE = 0x00000040, + TBN_GETBUTTONINFOA = ((0 - 700) - 0), + TBN_GETBUTTONINFOW = ((0 - 700) - 20), + TBN_QUERYINSERT = ((0 - 700) - 6), + TBN_DROPDOWN = ((0 - 700) - 10), + TBN_HOTITEMCHANGE = ((0 - 700) - 13), + TBN_GETDISPINFOA = ((0 - 700) - 16), + TBN_GETDISPINFOW = ((0 - 700) - 17), + TBN_GETINFOTIPA = ((0 - 700) - 18), + TBN_GETINFOTIPW = ((0 - 700) - 19), + TTS_ALWAYSTIP = 0x01, + TTS_NOPREFIX = 0x02, + TTS_NOANIMATE = 0x10, + TTS_NOFADE = 0x20, + TTS_BALLOON = 0x40, + //TTI_NONE =0, + //TTI_INFO =1, + TTI_WARNING = 2, + //TTI_ERROR =3, + TTF_IDISHWND = 0x0001, + TTF_RTLREADING = 0x0004, + TTF_TRACK = 0x0020, + TTF_CENTERTIP = 0x0002, + TTF_SUBCLASS = 0x0010, + TTF_TRANSPARENT = 0x0100, + TTF_ABSOLUTE = 0x0080, + TTDT_AUTOMATIC = 0, + TTDT_RESHOW = 1, + TTDT_AUTOPOP = 2, + TTDT_INITIAL = 3, + TTM_TRACKACTIVATE = (0x0400 + 17), + TTM_TRACKPOSITION = (0x0400 + 18), + TTM_ACTIVATE = (0x0400 + 1), + TTM_POP = (0x0400 + 28), + TTM_ADJUSTRECT = (0x400 + 31), + TTM_SETDELAYTIME = (0x0400 + 3), + TTM_SETTITLEA = (WM_USER + 32), // wParam = TTI_*, lParam = char* szTitle + TTM_SETTITLEW = (WM_USER + 33), // wParam = TTI_*, lParam = wchar* szTitle + TTM_ADDTOOLA = (0x0400 + 4), + TTM_ADDTOOLW = (0x0400 + 50), + TTM_DELTOOLA = (0x0400 + 5), + TTM_DELTOOLW = (0x0400 + 51), + TTM_NEWTOOLRECTA = (0x0400 + 6), + TTM_NEWTOOLRECTW = (0x0400 + 52), + TTM_RELAYEVENT = (0x0400 + 7), + TTM_GETTIPBKCOLOR = (0x0400 + 22), + TTM_SETTIPBKCOLOR = (0x0400 + 19), + TTM_SETTIPTEXTCOLOR = (0x0400 + 20), + TTM_GETTIPTEXTCOLOR = (0x0400 + 23), + TTM_GETTOOLINFOA = (0x0400 + 8), + TTM_GETTOOLINFOW = (0x0400 + 53), + TTM_SETTOOLINFOA = (0x0400 + 9), + TTM_SETTOOLINFOW = (0x0400 + 54), + TTM_HITTESTA = (0x0400 + 10), + TTM_HITTESTW = (0x0400 + 55), + TTM_GETTEXTA = (0x0400 + 11), + TTM_GETTEXTW = (0x0400 + 56), + TTM_UPDATE = (0x0400 + 29), + TTM_UPDATETIPTEXTA = (0x0400 + 12), + TTM_UPDATETIPTEXTW = (0x0400 + 57), + TTM_ENUMTOOLSA = (0x0400 + 14), + TTM_ENUMTOOLSW = (0x0400 + 58), + TTM_GETCURRENTTOOLA = (0x0400 + 15), + TTM_GETCURRENTTOOLW = (0x0400 + 59), + TTM_WINDOWFROMPOINT = (0x0400 + 16), + TTM_GETDELAYTIME = (0x0400 + 21), + TTM_SETMAXTIPWIDTH = (0x0400 + 24), + TTM_GETBUBBLESIZE = (0x0400 + 30), + TTN_GETDISPINFOA = ((0 - 520) - 0), + TTN_GETDISPINFOW = ((0 - 520) - 10), + TTN_SHOW = ((0 - 520) - 1), + TTN_POP = ((0 - 520) - 2), + TTN_NEEDTEXTA = ((0 - 520) - 0), + TTN_NEEDTEXTW = ((0 - 520) - 10), + TBS_AUTOTICKS = 0x0001, + TBS_VERT = 0x0002, + TBS_TOP = 0x0004, + TBS_BOTTOM = 0x0000, + TBS_BOTH = 0x0008, + TBS_NOTICKS = 0x0010, + TBM_GETPOS = (0x0400), + TBM_SETTIC = (0x0400 + 4), + TBM_SETPOS = (0x0400 + 5), + TBM_SETRANGE = (0x0400 + 6), + TBM_SETRANGEMIN = (0x0400 + 7), + TBM_SETRANGEMAX = (0x0400 + 8), + TBM_SETTICFREQ = (0x0400 + 20), + TBM_SETPAGESIZE = (0x0400 + 21), + TBM_SETLINESIZE = (0x0400 + 23), + TB_LINEUP = 0, + TB_LINEDOWN = 1, + TB_PAGEUP = 2, + TB_PAGEDOWN = 3, + TB_THUMBPOSITION = 4, + TB_THUMBTRACK = 5, + TB_TOP = 6, + TB_BOTTOM = 7, + TB_ENDTRACK = 8, + TVS_HASBUTTONS = 0x0001, + TVS_HASLINES = 0x0002, + TVS_LINESATROOT = 0x0004, + TVS_EDITLABELS = 0x0008, + TVS_SHOWSELALWAYS = 0x0020, + TVS_RTLREADING = 0x0040, + TVS_CHECKBOXES = 0x0100, + TVS_TRACKSELECT = 0x0200, + TVS_FULLROWSELECT = 0x1000, + TVS_NONEVENHEIGHT = 0x4000, + TVS_INFOTIP = 0x0800, + TVS_NOTOOLTIPS = 0x0080, + TVIF_TEXT = 0x0001, + TVIF_IMAGE = 0x0002, + TVIF_PARAM = 0x0004, + TVIF_STATE = 0x0008, + TVIF_HANDLE = 0x0010, + TVIF_SELECTEDIMAGE = 0x0020, + TVIS_SELECTED = 0x0002, + TVIS_EXPANDED = 0x0020, + TVIS_EXPANDEDONCE = 0x0040, + TVIS_STATEIMAGEMASK = 0xF000, + TVI_ROOT = (unchecked((int)0xFFFF0000)), + TVI_FIRST = (unchecked((int)0xFFFF0001)), + TVM_INSERTITEMA = (0x1100 + 0), + TVM_INSERTITEMW = (0x1100 + 50), + TVM_DELETEITEM = (0x1100 + 1), + TVM_EXPAND = (0x1100 + 2), + TVE_COLLAPSE = 0x0001, + TVE_EXPAND = 0x0002, + TVM_GETITEMRECT = (0x1100 + 4), + TVM_GETINDENT = (0x1100 + 6), + TVM_SETINDENT = (0x1100 + 7), + TVM_GETIMAGELIST = (0x1100 + 8), + TVM_SETIMAGELIST = (0x1100 + 9), + TVM_GETNEXTITEM = (0x1100 + 10), + TVGN_NEXT = 0x0001, + TVGN_PREVIOUS = 0x0002, + TVGN_FIRSTVISIBLE = 0x0005, + TVGN_NEXTVISIBLE = 0x0006, + TVGN_PREVIOUSVISIBLE = 0x0007, + TVGN_DROPHILITE= 0x0008, + TVGN_CARET = 0x0009, + TVM_SELECTITEM = (0x1100 + 11), + TVM_GETITEMA = (0x1100 + 12), + TVM_GETITEMW = (0x1100 + 62), + TVM_SETITEMA = (0x1100 + 13), + TVM_SETITEMW = (0x1100 + 63), + TVM_EDITLABELA = (0x1100 + 14), + TVM_EDITLABELW = (0x1100 + 65), + TVM_GETEDITCONTROL = (0x1100 + 15), + TVM_GETVISIBLECOUNT = (0x1100 + 16), + TVM_HITTEST = (0x1100 + 17), + TVM_ENSUREVISIBLE = (0x1100 + 20), + TVM_ENDEDITLABELNOW = (0x1100 + 22), + TVM_GETISEARCHSTRINGA = (0x1100 + 23), + TVM_GETISEARCHSTRINGW = (0x1100 + 64), + TVM_SETITEMHEIGHT = (0x1100 + 27), + TVM_GETITEMHEIGHT = (0x1100 + 28), + TVN_SELCHANGINGA = ((0 - 400) - 1), + TVN_SELCHANGINGW = ((0 - 400) - 50), + TVN_GETINFOTIPA = ((0 - 400) - 13), + TVN_GETINFOTIPW = ((0 - 400) - 14), + TVN_SELCHANGEDA = ((0 - 400) - 2), + TVN_SELCHANGEDW = ((0 - 400) - 51), + TVC_UNKNOWN = 0x0000, + TVC_BYMOUSE = 0x0001, + TVC_BYKEYBOARD = 0x0002, + TVN_GETDISPINFOA = ((0 - 400) - 3), + TVN_GETDISPINFOW = ((0 - 400) - 52), + TVN_SETDISPINFOA = ((0 - 400) - 4), + TVN_SETDISPINFOW = ((0 - 400) - 53), + TVN_ITEMEXPANDINGA = ((0 - 400) - 5), + TVN_ITEMEXPANDINGW = ((0 - 400) - 54), + TVN_ITEMEXPANDEDA = ((0 - 400) - 6), + TVN_ITEMEXPANDEDW = ((0 - 400) - 55), + TVN_BEGINDRAGA = ((0 - 400) - 7), + TVN_BEGINDRAGW = ((0 - 400) - 56), + TVN_BEGINRDRAGA = ((0 - 400) - 8), + TVN_BEGINRDRAGW = ((0 - 400) - 57), + TVN_BEGINLABELEDITA = ((0 - 400) - 10), + TVN_BEGINLABELEDITW = ((0 - 400) - 59), + TVN_ENDLABELEDITA = ((0 - 400) - 11), + TVN_ENDLABELEDITW = ((0 - 400) - 60), + TCS_BOTTOM = 0x0002, + TCS_RIGHT = 0x0002, + TCS_FLATBUTTONS = 0x0008, + TCS_HOTTRACK = 0x0040, + TCS_VERTICAL = 0x0080, + TCS_TABS = 0x0000, + TCS_BUTTONS = 0x0100, + TCS_MULTILINE = 0x0200, + TCS_RIGHTJUSTIFY = 0x0000, + TCS_FIXEDWIDTH = 0x0400, + TCS_RAGGEDRIGHT = 0x0800, + TCS_OWNERDRAWFIXED = 0x2000, + TCS_TOOLTIPS = 0x4000, + TCM_SETIMAGELIST = (0x1300 + 3), + TCIF_TEXT = 0x0001, + TCIF_IMAGE = 0x0002, + TCM_GETITEMA = (0x1300 + 5), + TCM_GETITEMW = (0x1300 + 60), + TCM_SETITEMA = (0x1300 + 6), + TCM_SETITEMW = (0x1300 + 61), + TCM_INSERTITEMA = (0x1300 + 7), + TCM_INSERTITEMW = (0x1300 + 62), + TCM_DELETEITEM = (0x1300 + 8), + TCM_DELETEALLITEMS = (0x1300 + 9), + TCM_GETITEMRECT = (0x1300 + 10), + TCM_GETCURSEL = (0x1300 + 11), + TCM_SETCURSEL = (0x1300 + 12), + TCM_ADJUSTRECT = (0x1300 + 40), + TCM_SETITEMSIZE = (0x1300 + 41), + TCM_SETPADDING = (0x1300 + 43), + TCM_GETROWCOUNT = (0x1300 + 44), + TCM_GETTOOLTIPS = (0x1300 + 45), + TCM_SETTOOLTIPS = (0x1300 + 46), + TCN_SELCHANGE = ((0 - 550) - 1), + TCN_SELCHANGING = ((0 - 550) - 2), + TBSTYLE_WRAPPABLE = 0x0200, + TVM_SETBKCOLOR = (TV_FIRST + 29), + TVM_SETTEXTCOLOR = (TV_FIRST + 30), + TYMED_NULL = 0, + TVM_GETLINECOLOR = (TV_FIRST + 41), + TVM_SETLINECOLOR = (TV_FIRST + 40), + TVM_SETTOOLTIPS = (TV_FIRST + 24), + TVSIL_STATE = 2, + TVM_SORTCHILDRENCB = (TV_FIRST + 21), + TMPF_FIXED_PITCH = 0x01; + + public const int TVHT_NOWHERE = 0x0001, + TVHT_ONITEMICON = 0x0002, + TVHT_ONITEMLABEL = 0x0004, + TVHT_ONITEM = (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON), + TVHT_ONITEMINDENT = 0x0008, + TVHT_ONITEMBUTTON = 0x0010, + TVHT_ONITEMRIGHT = 0x0020, + TVHT_ONITEMSTATEICON = 0x0040, + TVHT_ABOVE = 0x0100, + TVHT_BELOW = 0x0200, + TVHT_TORIGHT = 0x0400, + TVHT_TOLEFT = 0x0800; + + public const int UIS_SET = 1, + UIS_CLEAR = 2, + UIS_INITIALIZE = 3, + UISF_HIDEFOCUS = 0x1, + UISF_HIDEACCEL = 0x2, + USERCLASSTYPE_FULL = 1, + USERCLASSTYPE_SHORT = 2, + USERCLASSTYPE_APPNAME = 3, + UOI_FLAGS = 1; + + + public const int VIEW_E_DRAW = unchecked((int)0x80040140), + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_TAB = 0x09, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12, + VK_CAPITAL = 0x14, + VK_KANA = 0x15, + VK_ESCAPE = 0x1B, + VK_END = 0x23, + VK_HOME = 0x24, + VK_NUMLOCK = 0x90, + VK_SCROLL = 0x91, + VK_INSERT = 0x002D, + VK_DELETE = 0x002E; + + public const int WH_JOURNALPLAYBACK = 1, + WH_GETMESSAGE = 3, + WH_MOUSE = 7, + WSF_VISIBLE = 0x0001, + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DELETEITEM = 0x002D, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WA_INACTIVE = 0, + WA_ACTIVE = 1, + WA_CLICKACTIVE = 2, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + WM_SHOWWINDOW = 0x0018, + WM_WININICHANGE = 0x001A, + WM_SETTINGCHANGE = 0x001A, + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + WM_SPOOLERSTATUS = 0x002A, + WM_DRAWITEM = 0x002B, + WM_MEASUREITEM = 0x002C, + WM_VKEYTOITEM = 0x002E, + WM_CHARTOITEM = 0x002F, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + WM_GETOBJECT = 0x003D, + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYDATA = 0x004A, + WM_CANCELJOURNAL = 0x004B, + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_NCMOUSEMOVE = 0x00A0, + WM_NCMOUSELEAVE = 0x02A2, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + WM_NCXBUTTONDOWN = 0x00AB, + WM_NCXBUTTONUP = 0x00AC, + WM_NCXBUTTONDBLCLK = 0x00AD, + WM_KEYFIRST = 0x0100, + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_CTLCOLOR = 0x0019, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = 0x010F, + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_UNINITMENUPOPUP = 0x0125, + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + WM_MOUSEFIRST = 0x0200, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_XBUTTONDOWN = 0x020B, + WM_XBUTTONUP = 0x020C, + WM_XBUTTONDBLCLK = 0x020D, + WM_MOUSEWHEEL = 0x020A, + WM_MOUSELAST = 0x020A; + + + + public const int WHEEL_DELTA = 120, + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + WM_POWERBROADCAST = 0x0218, + WM_DEVICECHANGE = 0x0219, + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_CONTROL = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_KEYDOWN = 0x0290, + WM_IME_KEYUP = 0x0291, + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + WM_MOUSEHOVER = 0x02A1, + WM_MOUSELEAVE = 0x02A3, + WM_DPICHANGED = 0x02E0, + WM_GETDPISCALEDSIZE = 0x02e1, + WM_DPICHANGED_BEFOREPARENT = 0x02E2, + WM_DPICHANGED_AFTERPARENT = 0x02E3, + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030A, + WM_SIZECLIPBOARD = 0x030B, + WM_ASKCBFORMATNAME = 0x030C, + WM_CHANGECBCHAIN = 0x030D, + WM_HSCROLLCLIPBOARD = 0x030E, + WM_QUERYNEWPALETTE = 0x030F, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + WM_THEMECHANGED = 0x031A, + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035F, + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037F, + WM_PENWINFIRST = 0x0380, + WM_PENWINLAST = 0x038F, + WM_APP = unchecked((int)0x8000), + WM_USER = 0x0400, + WM_REFLECT = NativeMethods.WM_USER + 0x1C00, + WS_OVERLAPPED = 0x00000000, + WS_POPUP = unchecked((int)0x80000000), + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_CAPTION = 0x00C00000, + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_TABSTOP = 0x00010000, + WS_MINIMIZEBOX = 0x00020000, + WS_MAXIMIZEBOX = 0x00010000, + WS_EX_DLGMODALFRAME = 0x00000001, + WS_EX_MDICHILD = 0x00000040, + WS_EX_TOOLWINDOW = 0x00000080, + WS_EX_CLIENTEDGE = 0x00000200, + WS_EX_CONTEXTHELP = 0x00000400, + WS_EX_RIGHT = 0x00001000, + WS_EX_LEFT = 0x00000000, + WS_EX_RTLREADING = 0x00002000, + WS_EX_LEFTSCROLLBAR = 0x00004000, + WS_EX_CONTROLPARENT = 0x00010000, + WS_EX_STATICEDGE = 0x00020000, + WS_EX_APPWINDOW = 0x00040000, + WS_EX_LAYERED = 0x00080000, + WS_EX_TOPMOST = 0x00000008, + WS_EX_LAYOUTRTL = 0x00400000, + WS_EX_NOINHERITLAYOUT = 0x00100000, + WPF_SETMINPOSITION = 0x0001, + WM_CHOOSEFONT_GETLOGFONT = (0x0400+1); + + // wParam of report message WM_IME_NOTIFY (public\sdk\imm.h) + public const int + //IMN_CLOSESTATUSWINDOW = 0x0001, + IMN_OPENSTATUSWINDOW = 0x0002, + //IMN_CHANGECANDIDATE = 0x0003, + //IMN_CLOSECANDIDATE = 0x0004, + //IMN_OPENCANDIDATE = 0x0005, + IMN_SETCONVERSIONMODE = 0x0006, + //IMN_SETSENTENCEMODE = 0x0007, + IMN_SETOPENSTATUS = 0x0008; + //IMN_SETCANDIDATEPOS = 0x0009, + //IMN_SETCOMPOSITIONFONT = 0x000A, + //IMN_SETCOMPOSITIONWINDOW = 0x000B, + //IMN_SETSTATUSWINDOWPOS = 0x000C, + //IMN_GUIDELINE = 0x000D, + //IMN_PRIVATE = 0x000E; + + + public static int START_PAGE_GENERAL = unchecked((int)0xffffffff); + + // Result action ids for PrintDlgEx. + public const int PD_RESULT_CANCEL = 0; + public const int PD_RESULT_PRINT = 1; + public const int PD_RESULT_APPLY = 2; + + private static int wmMouseEnterMessage = -1; + public static int WM_MOUSEENTER { + get { + if (wmMouseEnterMessage == -1) { + wmMouseEnterMessage = SafeNativeMethods.RegisterWindowMessage("WinFormsMouseEnter"); + } + return wmMouseEnterMessage; + } + } + + private static int wmUnSubclass = -1; + public static int WM_UIUNSUBCLASS { + get { + if (wmUnSubclass == -1) { + wmUnSubclass = SafeNativeMethods.RegisterWindowMessage("WinFormsUnSubclass"); + } + return wmUnSubclass; + } + } + + public const int XBUTTON1 = 0x0001; + public const int XBUTTON2 = 0x0002; + + + // These are initialized in a static constructor for speed. That way we don't have to + // evaluate the char size each time. + // + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int BFFM_SETSELECTION; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int CBEM_GETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int CBEM_SETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int CBEN_ENDEDIT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int CBEM_INSERTITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_GETITEMTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_SETITEMTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int ACM_OPEN; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int DTM_SETFORMAT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int DTN_USERSTRING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int DTN_WMKEYDOWN; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int DTN_FORMAT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int DTN_FORMATQUERY; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int EMR_POLYTEXTOUT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDM_INSERTITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDM_GETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDM_SETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_ITEMCHANGING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_ITEMCHANGED; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_ITEMCLICK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_ITEMDBLCLICK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_DIVIDERDBLCLICK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_BEGINTRACK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_ENDTRACK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_TRACK; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int HDN_GETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_GETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_SETBKIMAGE; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_SETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_INSERTITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_FINDITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_GETSTRINGWIDTH; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_EDITLABEL; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_GETCOLUMN; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_SETCOLUMN; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_GETISEARCHSTRING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVM_INSERTCOLUMN; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_BEGINLABELEDIT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_ENDLABELEDIT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_ODFINDITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_GETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_GETINFOTIP; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int LVN_SETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int PSM_SETTITLE; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int PSM_SETFINISHTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int RB_INSERTBAND; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int SB_SETTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int SB_GETTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int SB_GETTEXTLENGTH; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int SB_SETTIPTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int SB_GETTIPTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_SAVERESTORE; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_ADDSTRING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_GETBUTTONTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_MAPACCELERATOR; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_GETBUTTONINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_SETBUTTONINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_INSERTBUTTON; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TB_ADDBUTTONS; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TBN_GETBUTTONINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TBN_GETINFOTIP; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TBN_GETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_ADDTOOL; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_SETTITLE; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_DELTOOL; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_NEWTOOLRECT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_GETTOOLINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_SETTOOLINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_HITTEST; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_GETTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_UPDATETIPTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_ENUMTOOLS; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTM_GETCURRENTTOOL; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTN_GETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TTN_NEEDTEXT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVM_INSERTITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVM_GETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVM_SETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVM_EDITLABEL; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVM_GETISEARCHSTRING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_SELCHANGING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_SELCHANGED; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_GETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_SETDISPINFO; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_ITEMEXPANDING; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_ITEMEXPANDED; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_BEGINDRAG; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_BEGINRDRAG; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_BEGINLABELEDIT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TVN_ENDLABELEDIT; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TCM_GETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TCM_SETITEM; + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public static readonly int TCM_INSERTITEM; + + public const string TOOLTIPS_CLASS = "tooltips_class32"; + + public const string WC_DATETIMEPICK = "SysDateTimePick32", + WC_LISTVIEW = "SysListView32", + WC_MONTHCAL = "SysMonthCal32", + WC_PROGRESS = "msctls_progress32", + WC_STATUSBAR = "msctls_statusbar32", + WC_TOOLBAR = "ToolbarWindow32", + WC_TRACKBAR = "msctls_trackbar32", + WC_TREEVIEW = "SysTreeView32", + WC_TABCONTROL = "SysTabControl32", + MSH_MOUSEWHEEL = "MSWHEEL_ROLLMSG", + MSH_SCROLL_LINES = "MSH_SCROLL_LINES_MSG", + MOUSEZ_CLASSNAME = "MouseZ", + MOUSEZ_TITLE = "Magellan MSWHEEL"; + + public const int CHILDID_SELF = 0; + + public const int OBJID_QUERYCLASSNAMEIDX = unchecked(unchecked((int)0xFFFFFFF4)); + public const int OBJID_CLIENT = unchecked(unchecked((int)0xFFFFFFFC)); + public const int OBJID_WINDOW = unchecked(unchecked((int)0x00000000)); + + public const int UiaRootObjectId = -25; + public const int UiaAppendRuntimeId = 3; + + public const string uuid_IAccessible = "{618736E0-3C3D-11CF-810C-00AA00389B71}"; + public const string uuid_IEnumVariant = "{00020404-0000-0000-C000-000000000046}"; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline"), + SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + static NativeMethods() + { + if (Marshal.SystemDefaultCharSize == 1) { + BFFM_SETSELECTION = NativeMethods.BFFM_SETSELECTIONA; + CBEM_GETITEM = NativeMethods.CBEM_GETITEMA; + CBEM_SETITEM = NativeMethods.CBEM_SETITEMA; + CBEN_ENDEDIT = NativeMethods.CBEN_ENDEDITA; + CBEM_INSERTITEM = NativeMethods.CBEM_INSERTITEMA; + LVM_GETITEMTEXT = NativeMethods.LVM_GETITEMTEXTA; + LVM_SETITEMTEXT = NativeMethods.LVM_SETITEMTEXTA; + ACM_OPEN = NativeMethods.ACM_OPENA; + DTM_SETFORMAT = NativeMethods.DTM_SETFORMATA; + DTN_USERSTRING = NativeMethods.DTN_USERSTRINGA; + DTN_WMKEYDOWN = NativeMethods.DTN_WMKEYDOWNA; + DTN_FORMAT = NativeMethods.DTN_FORMATA; + DTN_FORMATQUERY = NativeMethods.DTN_FORMATQUERYA; + EMR_POLYTEXTOUT = NativeMethods.EMR_POLYTEXTOUTA; + HDM_INSERTITEM = NativeMethods.HDM_INSERTITEMA; + HDM_GETITEM = NativeMethods.HDM_GETITEMA; + HDM_SETITEM = NativeMethods.HDM_SETITEMA; + HDN_ITEMCHANGING = NativeMethods.HDN_ITEMCHANGINGA; + HDN_ITEMCHANGED = NativeMethods.HDN_ITEMCHANGEDA; + HDN_ITEMCLICK = NativeMethods.HDN_ITEMCLICKA; + HDN_ITEMDBLCLICK = NativeMethods.HDN_ITEMDBLCLICKA; + HDN_DIVIDERDBLCLICK = NativeMethods.HDN_DIVIDERDBLCLICKA; + HDN_BEGINTRACK = NativeMethods.HDN_BEGINTRACKA; + HDN_ENDTRACK = NativeMethods.HDN_ENDTRACKA; + HDN_TRACK = NativeMethods.HDN_TRACKA; + HDN_GETDISPINFO = NativeMethods.HDN_GETDISPINFOA; + LVM_SETBKIMAGE = NativeMethods.LVM_SETBKIMAGEA; + LVM_GETITEM = NativeMethods.LVM_GETITEMA; + LVM_SETITEM = NativeMethods.LVM_SETITEMA; + LVM_INSERTITEM = NativeMethods.LVM_INSERTITEMA; + LVM_FINDITEM = NativeMethods.LVM_FINDITEMA; + LVM_GETSTRINGWIDTH = NativeMethods.LVM_GETSTRINGWIDTHA; + LVM_EDITLABEL = NativeMethods.LVM_EDITLABELA; + LVM_GETCOLUMN = NativeMethods.LVM_GETCOLUMNA; + LVM_SETCOLUMN = NativeMethods.LVM_SETCOLUMNA; + LVM_GETISEARCHSTRING = NativeMethods.LVM_GETISEARCHSTRINGA; + LVM_INSERTCOLUMN = NativeMethods.LVM_INSERTCOLUMNA; + LVN_BEGINLABELEDIT = NativeMethods.LVN_BEGINLABELEDITA; + LVN_ENDLABELEDIT = NativeMethods.LVN_ENDLABELEDITA; + LVN_ODFINDITEM = NativeMethods.LVN_ODFINDITEMA; + LVN_GETDISPINFO = NativeMethods.LVN_GETDISPINFOA; + LVN_GETINFOTIP = NativeMethods.LVN_GETINFOTIPA; + LVN_SETDISPINFO = NativeMethods.LVN_SETDISPINFOA; + PSM_SETTITLE = NativeMethods.PSM_SETTITLEA; + PSM_SETFINISHTEXT = NativeMethods.PSM_SETFINISHTEXTA; + RB_INSERTBAND = NativeMethods.RB_INSERTBANDA; + SB_SETTEXT = NativeMethods.SB_SETTEXTA; + SB_GETTEXT = NativeMethods.SB_GETTEXTA; + SB_GETTEXTLENGTH = NativeMethods.SB_GETTEXTLENGTHA; + SB_SETTIPTEXT = NativeMethods.SB_SETTIPTEXTA; + SB_GETTIPTEXT = NativeMethods.SB_GETTIPTEXTA; + TB_SAVERESTORE = NativeMethods.TB_SAVERESTOREA; + TB_ADDSTRING = NativeMethods.TB_ADDSTRINGA; + TB_GETBUTTONTEXT = NativeMethods.TB_GETBUTTONTEXTA; + TB_MAPACCELERATOR = NativeMethods.TB_MAPACCELERATORA; + TB_GETBUTTONINFO = NativeMethods.TB_GETBUTTONINFOA; + TB_SETBUTTONINFO = NativeMethods.TB_SETBUTTONINFOA; + TB_INSERTBUTTON = NativeMethods.TB_INSERTBUTTONA; + TB_ADDBUTTONS = NativeMethods.TB_ADDBUTTONSA; + TBN_GETBUTTONINFO = NativeMethods.TBN_GETBUTTONINFOA; + TBN_GETINFOTIP = NativeMethods.TBN_GETINFOTIPA; + TBN_GETDISPINFO = NativeMethods.TBN_GETDISPINFOA; + TTM_ADDTOOL = NativeMethods.TTM_ADDTOOLA; + TTM_SETTITLE = NativeMethods.TTM_SETTITLEA; + TTM_DELTOOL = NativeMethods.TTM_DELTOOLA; + TTM_NEWTOOLRECT = NativeMethods.TTM_NEWTOOLRECTA; + TTM_GETTOOLINFO = NativeMethods.TTM_GETTOOLINFOA; + TTM_SETTOOLINFO = NativeMethods.TTM_SETTOOLINFOA; + TTM_HITTEST = NativeMethods.TTM_HITTESTA; + TTM_GETTEXT = NativeMethods.TTM_GETTEXTA; + TTM_UPDATETIPTEXT = NativeMethods.TTM_UPDATETIPTEXTA; + TTM_ENUMTOOLS = NativeMethods.TTM_ENUMTOOLSA; + TTM_GETCURRENTTOOL = NativeMethods.TTM_GETCURRENTTOOLA; + TTN_GETDISPINFO = NativeMethods.TTN_GETDISPINFOA; + TTN_NEEDTEXT = NativeMethods.TTN_NEEDTEXTA; + TVM_INSERTITEM = NativeMethods.TVM_INSERTITEMA; + TVM_GETITEM = NativeMethods.TVM_GETITEMA; + TVM_SETITEM = NativeMethods.TVM_SETITEMA; + TVM_EDITLABEL = NativeMethods.TVM_EDITLABELA; + TVM_GETISEARCHSTRING = NativeMethods.TVM_GETISEARCHSTRINGA; + TVN_SELCHANGING = NativeMethods.TVN_SELCHANGINGA; + TVN_SELCHANGED = NativeMethods.TVN_SELCHANGEDA; + TVN_GETDISPINFO = NativeMethods.TVN_GETDISPINFOA; + TVN_SETDISPINFO = NativeMethods.TVN_SETDISPINFOA; + TVN_ITEMEXPANDING = NativeMethods.TVN_ITEMEXPANDINGA; + TVN_ITEMEXPANDED = NativeMethods.TVN_ITEMEXPANDEDA; + TVN_BEGINDRAG = NativeMethods.TVN_BEGINDRAGA; + TVN_BEGINRDRAG = NativeMethods.TVN_BEGINRDRAGA; + TVN_BEGINLABELEDIT = NativeMethods.TVN_BEGINLABELEDITA; + TVN_ENDLABELEDIT = NativeMethods.TVN_ENDLABELEDITA; + TCM_GETITEM = NativeMethods.TCM_GETITEMA; + TCM_SETITEM = NativeMethods.TCM_SETITEMA; + TCM_INSERTITEM = NativeMethods.TCM_INSERTITEMA; + } + else { + BFFM_SETSELECTION = NativeMethods.BFFM_SETSELECTIONW; + CBEM_GETITEM = NativeMethods.CBEM_GETITEMW; + CBEM_SETITEM = NativeMethods.CBEM_SETITEMW; + CBEN_ENDEDIT = NativeMethods.CBEN_ENDEDITW; + CBEM_INSERTITEM = NativeMethods.CBEM_INSERTITEMW; + LVM_GETITEMTEXT = NativeMethods.LVM_GETITEMTEXTW; + LVM_SETITEMTEXT = NativeMethods.LVM_SETITEMTEXTW; + ACM_OPEN = NativeMethods.ACM_OPENW; + DTM_SETFORMAT = NativeMethods.DTM_SETFORMATW; + DTN_USERSTRING = NativeMethods.DTN_USERSTRINGW; + DTN_WMKEYDOWN = NativeMethods.DTN_WMKEYDOWNW; + DTN_FORMAT = NativeMethods.DTN_FORMATW; + DTN_FORMATQUERY = NativeMethods.DTN_FORMATQUERYW; + EMR_POLYTEXTOUT = NativeMethods.EMR_POLYTEXTOUTW; + HDM_INSERTITEM = NativeMethods.HDM_INSERTITEMW; + HDM_GETITEM = NativeMethods.HDM_GETITEMW; + HDM_SETITEM = NativeMethods.HDM_SETITEMW; + HDN_ITEMCHANGING = NativeMethods.HDN_ITEMCHANGINGW; + HDN_ITEMCHANGED = NativeMethods.HDN_ITEMCHANGEDW; + HDN_ITEMCLICK = NativeMethods.HDN_ITEMCLICKW; + HDN_ITEMDBLCLICK = NativeMethods.HDN_ITEMDBLCLICKW; + HDN_DIVIDERDBLCLICK = NativeMethods.HDN_DIVIDERDBLCLICKW; + HDN_BEGINTRACK = NativeMethods.HDN_BEGINTRACKW; + HDN_ENDTRACK = NativeMethods.HDN_ENDTRACKW; + HDN_TRACK = NativeMethods.HDN_TRACKW; + HDN_GETDISPINFO = NativeMethods.HDN_GETDISPINFOW; + LVM_SETBKIMAGE = NativeMethods.LVM_SETBKIMAGEW; + LVM_GETITEM = NativeMethods.LVM_GETITEMW; + LVM_SETITEM = NativeMethods.LVM_SETITEMW; + LVM_INSERTITEM = NativeMethods.LVM_INSERTITEMW; + LVM_FINDITEM = NativeMethods.LVM_FINDITEMW; + LVM_GETSTRINGWIDTH = NativeMethods.LVM_GETSTRINGWIDTHW; + LVM_EDITLABEL = NativeMethods.LVM_EDITLABELW; + LVM_GETCOLUMN = NativeMethods.LVM_GETCOLUMNW; + LVM_SETCOLUMN = NativeMethods.LVM_SETCOLUMNW; + LVM_GETISEARCHSTRING = NativeMethods.LVM_GETISEARCHSTRINGW; + LVM_INSERTCOLUMN = NativeMethods.LVM_INSERTCOLUMNW; + LVN_BEGINLABELEDIT = NativeMethods.LVN_BEGINLABELEDITW; + LVN_ENDLABELEDIT = NativeMethods.LVN_ENDLABELEDITW; + LVN_ODFINDITEM = NativeMethods.LVN_ODFINDITEMW; + LVN_GETDISPINFO = NativeMethods.LVN_GETDISPINFOW; + LVN_GETINFOTIP = NativeMethods.LVN_GETINFOTIPW; + LVN_SETDISPINFO = NativeMethods.LVN_SETDISPINFOW; + PSM_SETTITLE = NativeMethods.PSM_SETTITLEW; + PSM_SETFINISHTEXT = NativeMethods.PSM_SETFINISHTEXTW; + RB_INSERTBAND = NativeMethods.RB_INSERTBANDW; + SB_SETTEXT = NativeMethods.SB_SETTEXTW; + SB_GETTEXT = NativeMethods.SB_GETTEXTW; + SB_GETTEXTLENGTH = NativeMethods.SB_GETTEXTLENGTHW; + SB_SETTIPTEXT = NativeMethods.SB_SETTIPTEXTW; + SB_GETTIPTEXT = NativeMethods.SB_GETTIPTEXTW; + TB_SAVERESTORE = NativeMethods.TB_SAVERESTOREW; + TB_ADDSTRING = NativeMethods.TB_ADDSTRINGW; + TB_GETBUTTONTEXT = NativeMethods.TB_GETBUTTONTEXTW; + TB_MAPACCELERATOR = NativeMethods.TB_MAPACCELERATORW; + TB_GETBUTTONINFO = NativeMethods.TB_GETBUTTONINFOW; + TB_SETBUTTONINFO = NativeMethods.TB_SETBUTTONINFOW; + TB_INSERTBUTTON = NativeMethods.TB_INSERTBUTTONW; + TB_ADDBUTTONS = NativeMethods.TB_ADDBUTTONSW; + TBN_GETBUTTONINFO = NativeMethods.TBN_GETBUTTONINFOW; + TBN_GETINFOTIP = NativeMethods.TBN_GETINFOTIPW; + TBN_GETDISPINFO = NativeMethods.TBN_GETDISPINFOW; + TTM_ADDTOOL = NativeMethods.TTM_ADDTOOLW; + TTM_SETTITLE = NativeMethods.TTM_SETTITLEW; + TTM_DELTOOL = NativeMethods.TTM_DELTOOLW; + TTM_NEWTOOLRECT = NativeMethods.TTM_NEWTOOLRECTW; + TTM_GETTOOLINFO = NativeMethods.TTM_GETTOOLINFOW; + TTM_SETTOOLINFO = NativeMethods.TTM_SETTOOLINFOW; + TTM_HITTEST = NativeMethods.TTM_HITTESTW; + TTM_GETTEXT = NativeMethods.TTM_GETTEXTW; + TTM_UPDATETIPTEXT = NativeMethods.TTM_UPDATETIPTEXTW; + TTM_ENUMTOOLS = NativeMethods.TTM_ENUMTOOLSW; + TTM_GETCURRENTTOOL = NativeMethods.TTM_GETCURRENTTOOLW; + TTN_GETDISPINFO = NativeMethods.TTN_GETDISPINFOW; + TTN_NEEDTEXT = NativeMethods.TTN_NEEDTEXTW; + TVM_INSERTITEM = NativeMethods.TVM_INSERTITEMW; + TVM_GETITEM = NativeMethods.TVM_GETITEMW; + TVM_SETITEM = NativeMethods.TVM_SETITEMW; + TVM_EDITLABEL = NativeMethods.TVM_EDITLABELW; + TVM_GETISEARCHSTRING = NativeMethods.TVM_GETISEARCHSTRINGW; + TVN_SELCHANGING = NativeMethods.TVN_SELCHANGINGW; + TVN_SELCHANGED = NativeMethods.TVN_SELCHANGEDW; + TVN_GETDISPINFO = NativeMethods.TVN_GETDISPINFOW; + TVN_SETDISPINFO = NativeMethods.TVN_SETDISPINFOW; + TVN_ITEMEXPANDING = NativeMethods.TVN_ITEMEXPANDINGW; + TVN_ITEMEXPANDED = NativeMethods.TVN_ITEMEXPANDEDW; + TVN_BEGINDRAG = NativeMethods.TVN_BEGINDRAGW; + TVN_BEGINRDRAG = NativeMethods.TVN_BEGINRDRAGW; + TVN_BEGINLABELEDIT = NativeMethods.TVN_BEGINLABELEDITW; + TVN_ENDLABELEDIT = NativeMethods.TVN_ENDLABELEDITW; + TCM_GETITEM = NativeMethods.TCM_GETITEMW; + TCM_SETITEM = NativeMethods.TCM_SETITEMW; + TCM_INSERTITEM = NativeMethods.TCM_INSERTITEMW; + } + } + + /* + * MISCELLANEOUS + */ + + + + [StructLayout(LayoutKind.Sequential), CLSCompliant(false)] + public class OLECMD { + [MarshalAs(UnmanagedType.U4)] + public int cmdID = 0; + [MarshalAs(UnmanagedType.U4)] + public int cmdf = 0; + + } + + + [ComVisible(true), ComImport(), Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown), CLSCompliantAttribute(false)] + public interface IOleCommandTarget + { + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int QueryStatus( + ref Guid pguidCmdGroup, + int cCmds, + [In, Out] + OLECMD prgCmds, + [In, Out] + IntPtr pCmdText); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int Exec( + ref Guid pguidCmdGroup, + int nCmdID, + int nCmdexecopt, + // we need to have this an array because callers need to be able to specify NULL or VT_NULL + [In, MarshalAs(UnmanagedType.LPArray)] + Object[] pvaIn, + int pvaOut); + } + + /* Unused + public static int SignedHIWORD(int n) { + int i = (int)(short)((n >> 16) & 0xffff); + + return i; + } + + public static int SignedLOWORD(int n) { + int i = (int)(short)(n & 0xFFFF); + + return i; + } + */ + + /// + /// + /// + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public class FONTDESC { + public int cbSizeOfStruct = Marshal.SizeOf(typeof(FONTDESC)); + public string lpstrName; + public long cySize; + public short sWeight; + public short sCharset; + public bool fItalic; + public bool fUnderline; + public bool fStrikethrough; + } + + + + /// + /// + /// + [StructLayout(LayoutKind.Sequential)] + public class PICTDESCbmp { + internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PICTDESCbmp)); + internal int picType = Ole.PICTYPE_BITMAP; + internal IntPtr hbitmap = IntPtr.Zero; + internal IntPtr hpalette = IntPtr.Zero; + internal int unused = 0; + + public PICTDESCbmp(System.Drawing.Bitmap bitmap) { + hbitmap = bitmap.GetHbitmap(); + // gpr: What about palettes? + } + } + + /// + /// + /// + [StructLayout(LayoutKind.Sequential)] + public class PICTDESCicon { + internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PICTDESCicon)); + internal int picType = Ole.PICTYPE_ICON; + internal IntPtr hicon = IntPtr.Zero; + internal int unused1 = 0; + internal int unused2 = 0; + + public PICTDESCicon(System.Drawing.Icon icon) { + hicon = SafeNativeMethods.CopyImage(new HandleRef(icon, icon.Handle), NativeMethods.IMAGE_ICON, icon.Size.Width, icon.Size.Height, 0); + } + } + + /// + /// + /// + [StructLayout(LayoutKind.Sequential)] + public class PICTDESCemf { + internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PICTDESCemf)); + internal int picType = Ole.PICTYPE_ENHMETAFILE; + internal IntPtr hemf = IntPtr.Zero; + internal int unused1 = 0; + internal int unused2 = 0; + + public PICTDESCemf(System.Drawing.Imaging.Metafile metafile) { + //gpr hemf = metafile.CopyHandle(); + } + } + + [StructLayout(LayoutKind.Sequential)] + public class USEROBJECTFLAGS { + public int fInherit = 0; + public int fReserved = 0; + public int dwFlags = 0; + } + + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] + internal class SYSTEMTIMEARRAY { + public short wYear1; + public short wMonth1; + public short wDayOfWeek1; + public short wDay1; + public short wHour1; + public short wMinute1; + public short wSecond1; + public short wMilliseconds1; + public short wYear2; + public short wMonth2; + public short wDayOfWeek2; + public short wDay2; + public short wHour2; + public short wMinute2; + public short wSecond2; + public short wMilliseconds2; + } + + public delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam); + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class HH_AKLINK { + internal int cbStruct=Marshal.SizeOf(typeof(HH_AKLINK)); + internal bool fReserved = false; + internal string pszKeywords = null; + internal string pszUrl = null; + internal string pszMsgText = null; + internal string pszMsgTitle = null; + internal string pszWindow = null; + internal bool fIndexOnFail = false; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class HH_POPUP { + internal int cbStruct=Marshal.SizeOf(typeof(HH_POPUP)); + internal IntPtr hinst = IntPtr.Zero; + internal int idString = 0; + internal IntPtr pszText; + internal POINT pt; + internal int clrForeground = -1; + internal int clrBackground = -1; + internal RECT rcMargins = RECT.FromXYWH(-1, -1, -1, -1); // amount of space between edges of window and text, -1 for each member to ignore + internal string pszFont = null; + } + + [SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")] + public const int HH_FTS_DEFAULT_PROXIMITY = -1; + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class HH_FTS_QUERY { + internal int cbStruct = Marshal.SizeOf(typeof(HH_FTS_QUERY)); + internal bool fUniCodeStrings = false; + [MarshalAs(UnmanagedType.LPStr)] + internal string pszSearchQuery = null; + internal int iProximity = NativeMethods.HH_FTS_DEFAULT_PROXIMITY; + internal bool fStemmedSearch = false; + internal bool fTitleOnly = false; + internal bool fExecute = true; + [MarshalAs(UnmanagedType.LPStr)] + internal string pszWindow = null; + } + + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto, Pack=4)] + public class MONITORINFOEX { + internal int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX)); + internal RECT rcMonitor = new RECT(); + internal RECT rcWork = new RECT(); + internal int dwFlags = 0; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + internal char[] szDevice = new char[32]; + } + + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto, Pack=4)] + public class MONITORINFO { + internal int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); + internal RECT rcMonitor = new RECT(); + internal RECT rcWork = new RECT(); + internal int dwFlags = 0; + } + + public delegate int EditStreamCallback(IntPtr dwCookie, IntPtr buf, int cb, out int transferred); + + [StructLayout(LayoutKind.Sequential)] + public class EDITSTREAM { + public IntPtr dwCookie = IntPtr.Zero; + public int dwError = 0; + public EditStreamCallback pfnCallback = null; + } + + [StructLayout(LayoutKind.Sequential)] + public class EDITSTREAM64 { + [MarshalAs(UnmanagedType.ByValArray, SizeConst=20)] + public byte[] contents = new byte[20]; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct DEVMODE + { + private const int CCHDEVICENAME = 32; + private const int CCHFORMNAME = 32; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] + public string dmDeviceName; + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + public int dmPositionX; + public int dmPositionY; + public ScreenOrientation dmDisplayOrientation; + public int dmDisplayFixedOutput; + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] + public string dmFormName; + public short dmLogPixels; + public int dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmReserved1; + public int dmReserved2; + public int dmPanningWidth; + public int dmPanningHeight; + } + + [ComImport(), Guid("0FF510A3-5FA5-49F1-8CCC-190D71083F3E"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVsPerPropertyBrowsing { + // hides the property at the given dispid from the properties window + // implmentors should can return E_NOTIMPL to show all properties that + // are otherwise browsable. + + [PreserveSig] + int HideProperty(int dispid,ref bool pfHide); + + // will have the "+" expandable glyph next to them and can be expanded or collapsed by the user + // Returning a non-S_OK return code or false for pfDisplay will suppress this feature + + [PreserveSig] + int DisplayChildProperties(int dispid, + ref bool pfDisplay); + + // retrieves the localized name and description for a property. + // returning a non-S_OK return code will display the default values + + [PreserveSig] + int GetLocalizedPropertyInfo(int dispid, int localeID, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pbstrLocalizedName, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pbstrLocalizeDescription); + + // determines if the given (usually current) value for a property is the default. If it is not default, + // the property will be shown as bold in the browser to indcate that it has been modified from the default. + + [PreserveSig] + int HasDefaultValue(int dispid, + ref bool fDefault); + + // determines if a property should be made read only. This only applies to properties that are writeable, + [PreserveSig] + int IsPropertyReadOnly(int dispid, + ref bool fReadOnly); + + + // returns the classname for this object. The class name is the non-bolded text that appears in the + // properties window selection combo. If this method returns a non-S_OK return code, the default + // will be used. The default is the name string from a call to ITypeInfo::GetDocumentation(MEMID_NIL, ...); + [PreserveSig] + int GetClassName([In, Out]ref string pbstrClassName); + + // checks whether the given property can be reset to some default value. If return value is non-S_OK or *pfCanReset is + // + [PreserveSig] + int CanResetPropertyValue(int dispid, [In, Out]ref bool pfCanReset); + + // given property. If the return value is S_OK, the property's value will then be refreshed to the new default + // values. + [PreserveSig] + int ResetPropertyValue(int dispid); + } + + [ComImport(), Guid("7494683C-37A0-11d2-A273-00C04F8EF4FF"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IManagedPerPropertyBrowsing { + + + [PreserveSig] + int GetPropertyAttributes(int dispid, + ref int pcAttributes, + ref IntPtr pbstrAttrNames, + ref IntPtr pvariantInitValues); + } + + [ComImport(), Guid("33C0C1D8-33CF-11d3-BFF2-00C04F990235"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IProvidePropertyBuilder { + + [PreserveSig] + int MapPropertyToBuilder( + int dispid, + [In, Out, MarshalAs(UnmanagedType.LPArray)] + int[] pdwCtlBldType, + [In, Out, MarshalAs(UnmanagedType.LPArray)] + string[] pbstrGuidBldr, + + [In, Out, MarshalAs(UnmanagedType.Bool)] + ref bool builderAvailable); + + [PreserveSig] + int ExecuteBuilder( + int dispid, + [In, MarshalAs(UnmanagedType.BStr)] + string bstrGuidBldr, + [In, MarshalAs(UnmanagedType.Interface)] + object pdispApp, + + HandleRef hwndBldrOwner, + [Out, In, MarshalAs(UnmanagedType.Struct)] + ref object pvarValue, + [In, Out, MarshalAs(UnmanagedType.Bool)] + ref bool actionCommitted); + } + + [StructLayout(LayoutKind.Sequential)] + public class INITCOMMONCONTROLSEX { + public int dwSize = 8; //ndirect.DllLib.sizeOf(this); + public int dwICC; + } + + [StructLayout(LayoutKind.Sequential)] + public class IMAGELISTDRAWPARAMS { + public int cbSize = Marshal.SizeOf(typeof(IMAGELISTDRAWPARAMS)); + public IntPtr himl = IntPtr.Zero; + public int i = 0; + public IntPtr hdcDst = IntPtr.Zero; + public int x = 0; + public int y = 0; + public int cx = 0; + public int cy = 0; + public int xBitmap = 0; + public int yBitmap = 0; + public int rgbBk = 0; + public int rgbFg = 0; + public int fStyle = 0; + public int dwRop = 0; + public int fState = 0; + public int Frame = 0; + public int crEffect = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class IMAGEINFO { + public IntPtr hbmImage = IntPtr.Zero; + public IntPtr hbmMask = IntPtr.Zero; + public int Unused1 = 0; + public int Unused2 = 0; + // rcImage was a by-value RECT structure + public int rcImage_left = 0; + public int rcImage_top = 0; + public int rcImage_right = 0; + public int rcImage_bottom = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class TRACKMOUSEEVENT { + public int cbSize = Marshal.SizeOf(typeof(TRACKMOUSEEVENT)); + public int dwFlags; + public IntPtr hwndTrack; + public int dwHoverTime = 100; // Never set this to field ZERO, or to HOVER_DEFAULT, ever! + } + + [StructLayout(LayoutKind.Sequential)] + public class POINT { + public int x; + public int y; + + public POINT() { + } + + public POINT(int x, int y) { + this.x = x; + this.y = y; + } + +#if DEBUG + public override string ToString() { + return "{x=" + x + ", y=" + y + "}"; + } +#endif + } + + // use this in cases where the Native API takes a POINT not a POINT* + // classes marshal by ref. + [StructLayout(LayoutKind.Sequential)] + public struct POINTSTRUCT { + public int x; + public int y; + public POINTSTRUCT(int x, int y) { + this.x = x; + this.y = y; + } + } + + public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT { + public int left; + public int top; + public int right; + public int bottom; + + public RECT(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) { + this.left = r.Left; + this.top = r.Top; + this.right = r.Right; + this.bottom = r.Bottom; + } + + public static RECT FromXYWH(int x, int y, int width, int height) { + return new RECT(x, y, x + width, y + height); + } + + public System.Drawing.Size Size { + get { + return new System.Drawing.Size(this.right - this.left, this.bottom - this.top); + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct MARGINS { + public int cxLeftWidth; + public int cxRightWidth; + public int cyTopHeight; + public int cyBottomHeight; + } + + public delegate int ListViewCompareCallback(IntPtr lParam1, IntPtr lParam2, IntPtr lParamSort); + + public delegate int TreeViewCompareCallback(IntPtr lParam1, IntPtr lParam2, IntPtr lParamSort); + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class WNDCLASS_I { + public int style = 0; + public IntPtr lpfnWndProc = IntPtr.Zero; + public int cbClsExtra = 0; + public int cbWndExtra = 0; + public IntPtr hInstance = IntPtr.Zero; + public IntPtr hIcon = IntPtr.Zero; + public IntPtr hCursor = IntPtr.Zero; + public IntPtr hbrBackground = IntPtr.Zero; + public IntPtr lpszMenuName = IntPtr.Zero; + public IntPtr lpszClassName = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class NONCLIENTMETRICS { + public int cbSize = Marshal.SizeOf(typeof(NONCLIENTMETRICS)); + public int iBorderWidth = 0; + public int iScrollWidth = 0; + public int iScrollHeight = 0; + public int iCaptionWidth = 0; + public int iCaptionHeight = 0; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfCaptionFont = null; + public int iSmCaptionWidth = 0; + public int iSmCaptionHeight = 0; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfSmCaptionFont = null; + public int iMenuWidth = 0; + public int iMenuHeight = 0; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfMenuFont = null; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfStatusFont = null; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfMessageFont = null; + // Added for Windows Vista.Since we are supporting >= Windows 7, + // this is safe to add. + public int iPaddedBorderWidth = 0; + } + /* + [StructLayout(LayoutKind.Sequential)] + public class ICONMETRICS { + public int cbSize = Marshal.SizeOf(typeof(ICONMETRICS)); + public int iHorzSpacing; + public int iVertSpacing; + public int iTitleWrap; + [MarshalAs(UnmanagedType.Struct)] + public LOGFONT lfFont; + } + */ + [StructLayout(LayoutKind.Sequential)] + [Serializable] + public struct MSG { + public IntPtr hwnd; + public int message; + public IntPtr wParam; + public IntPtr lParam; + public int time; + // pt was a by-value POINT structure + public int pt_x; + public int pt_y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PAINTSTRUCT { + public IntPtr hdc; + public bool fErase; + // rcPaint was a by-value RECT structure + public int rcPaint_left; + public int rcPaint_top; + public int rcPaint_right; + public int rcPaint_bottom; + public bool fRestore; + public bool fIncUpdate; + public int reserved1; + public int reserved2; + public int reserved3; + public int reserved4; + public int reserved5; + public int reserved6; + public int reserved7; + public int reserved8; + } + + [StructLayout(LayoutKind.Sequential)] + public class SCROLLINFO { + public int cbSize = Marshal.SizeOf(typeof(SCROLLINFO)); + public int fMask; + public int nMin; + public int nMax; + public int nPage; + public int nPos; + public int nTrackPos; + + public SCROLLINFO() { + } + + public SCROLLINFO(int mask, int min, int max, int page, int pos) { + fMask = mask; + nMin = min; + nMax = max; + nPage = page; + nPos = pos; + } + } + + [StructLayout(LayoutKind.Sequential)] + public class TPMPARAMS { + public int cbSize = Marshal.SizeOf(typeof(TPMPARAMS)); + // rcExclude was a by-value RECT structure + public int rcExclude_left; + public int rcExclude_top; + public int rcExclude_right; + public int rcExclude_bottom; + } + + [StructLayout(LayoutKind.Sequential)] + public class SIZE { + public int cx; + public int cy; + + public SIZE() { + } + + public SIZE(int cx, int cy) { + this.cx = cx; + this.cy = cy; + } + + /* Unused + public System.Drawing.Size ToSize() { + return new System.Drawing.Size(cx, cy); + } + */ + } + + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPLACEMENT { + public int length; + public int flags; + public int showCmd; + // ptMinPosition was a by-value POINT structure + public int ptMinPosition_x; + public int ptMinPosition_y; + // ptMaxPosition was a by-value POINT structure + public int ptMaxPosition_x; + public int ptMaxPosition_y; + // rcNormalPosition was a by-value RECT structure + public int rcNormalPosition_left; + public int rcNormalPosition_top; + public int rcNormalPosition_right; + public int rcNormalPosition_bottom; + } + + + + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] + public class STARTUPINFO_I { + public int cb = 0; + public IntPtr lpReserved = IntPtr.Zero; + public IntPtr lpDesktop = IntPtr.Zero; + public IntPtr lpTitle = IntPtr.Zero; + public int dwX = 0; + public int dwY = 0; + public int dwXSize = 0; + public int dwYSize = 0; + public int dwXCountChars = 0; + public int dwYCountChars = 0; + public int dwFillAttribute = 0; + public int dwFlags = 0; + public short wShowWindow = 0; + public short cbReserved2 = 0; + public IntPtr lpReserved2 = IntPtr.Zero; + public IntPtr hStdInput = IntPtr.Zero; + public IntPtr hStdOutput = IntPtr.Zero; + public IntPtr hStdError = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class PAGESETUPDLG { + public int lStructSize; + public IntPtr hwndOwner; + public IntPtr hDevMode; + public IntPtr hDevNames; + public int Flags; + + //POINT ptPaperSize; + public int paperSizeX = 0; + public int paperSizeY = 0; + + // RECT rtMinMargin; + public int minMarginLeft; + public int minMarginTop; + public int minMarginRight; + public int minMarginBottom; + + // RECT rtMargin; + public int marginLeft; + public int marginTop; + public int marginRight; + public int marginBottom; + + public IntPtr hInstance = IntPtr.Zero; + public IntPtr lCustData = IntPtr.Zero; + public WndProc lpfnPageSetupHook = null; + public WndProc lpfnPagePaintHook = null; + public string lpPageSetupTemplateName = null; + public IntPtr hPageSetupTemplate = IntPtr.Zero; + } + + // Any change in PRINTDLG, should also be in PRINTDLG_32 and PRINTDLG_64 + public interface PRINTDLG { + int lStructSize { get; set; } + + IntPtr hwndOwner { get; set; } + IntPtr hDevMode { get; set; } + IntPtr hDevNames { get; set; } + IntPtr hDC { get; set; } + + int Flags { get; set; } + + short nFromPage { get; set; } + short nToPage { get; set; } + short nMinPage { get; set; } + short nMaxPage { get; set; } + short nCopies { get; set; } + + IntPtr hInstance { get; set; } + IntPtr lCustData { get; set; } + + WndProc lpfnPrintHook { get; set; } + WndProc lpfnSetupHook { get; set; } + + string lpPrintTemplateName { get; set; } + string lpSetupTemplateName { get; set; } + + IntPtr hPrintTemplate { get; set; } + IntPtr hSetupTemplate { get; set; } + } + + // Any change in PRINTDLG_32, should also be in PRINTDLG and PRINTDLG_64 + // x86 requires EXPLICIT packing of 1. + [StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Auto)] + public class PRINTDLG_32 : PRINTDLG { + int m_lStructSize; + + IntPtr m_hwndOwner; + IntPtr m_hDevMode; + IntPtr m_hDevNames; + IntPtr m_hDC; + + int m_Flags; + + short m_nFromPage; + short m_nToPage; + short m_nMinPage; + short m_nMaxPage; + short m_nCopies; + + IntPtr m_hInstance; + IntPtr m_lCustData; + + WndProc m_lpfnPrintHook; + WndProc m_lpfnSetupHook; + + string m_lpPrintTemplateName; + string m_lpSetupTemplateName; + + IntPtr m_hPrintTemplate; + IntPtr m_hSetupTemplate; + + public int lStructSize { get { return m_lStructSize; } set { m_lStructSize = value; } } + + public IntPtr hwndOwner { get { return m_hwndOwner; } set { m_hwndOwner = value; } } + public IntPtr hDevMode { get { return m_hDevMode; } set { m_hDevMode = value; } } + public IntPtr hDevNames { get { return m_hDevNames; } set { m_hDevNames = value; } } + public IntPtr hDC { get { return m_hDC; } set { m_hDC = value; } } + + public int Flags { get { return m_Flags; } set { m_Flags = value; } } + + public short nFromPage { get { return m_nFromPage; } set { m_nFromPage = value; } } + public short nToPage { get { return m_nToPage; } set { m_nToPage = value; } } + public short nMinPage { get { return m_nMinPage; } set { m_nMinPage = value; } } + public short nMaxPage { get { return m_nMaxPage; } set { m_nMaxPage = value; } } + public short nCopies { get { return m_nCopies; } set { m_nCopies = value; } } + + public IntPtr hInstance { get { return m_hInstance; } set { m_hInstance = value; } } + public IntPtr lCustData { get { return m_lCustData; } set { m_lCustData = value; } } + + public WndProc lpfnPrintHook { get { return m_lpfnPrintHook; } set { m_lpfnPrintHook = value; } } + public WndProc lpfnSetupHook { get { return m_lpfnSetupHook; } set { m_lpfnSetupHook = value; } } + + public string lpPrintTemplateName { get { return m_lpPrintTemplateName; } set { m_lpPrintTemplateName = value; } } + public string lpSetupTemplateName { get { return m_lpSetupTemplateName; } set { m_lpSetupTemplateName = value; } } + + public IntPtr hPrintTemplate { get { return m_hPrintTemplate; } set { m_hPrintTemplate = value; } } + public IntPtr hSetupTemplate { get { return m_hSetupTemplate; } set { m_hSetupTemplate = value; } } + } + + // Any change in PRINTDLG_64, should also be in PRINTDLG_32 and PRINTDLG + // x64 does not require EXPLICIT packing of 1. + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class PRINTDLG_64 : PRINTDLG { + int m_lStructSize; + + IntPtr m_hwndOwner; + IntPtr m_hDevMode; + IntPtr m_hDevNames; + IntPtr m_hDC; + + int m_Flags; + + short m_nFromPage; + short m_nToPage; + short m_nMinPage; + short m_nMaxPage; + short m_nCopies; + + IntPtr m_hInstance; + IntPtr m_lCustData; + + WndProc m_lpfnPrintHook; + WndProc m_lpfnSetupHook; + + string m_lpPrintTemplateName; + string m_lpSetupTemplateName; + + IntPtr m_hPrintTemplate; + IntPtr m_hSetupTemplate; + + public int lStructSize { get { return m_lStructSize; } set { m_lStructSize = value; } } + + public IntPtr hwndOwner { get { return m_hwndOwner; } set { m_hwndOwner = value; } } + public IntPtr hDevMode { get { return m_hDevMode; } set { m_hDevMode = value; } } + public IntPtr hDevNames { get { return m_hDevNames; } set { m_hDevNames = value; } } + public IntPtr hDC { get { return m_hDC; } set { m_hDC = value; } } + + public int Flags { get { return m_Flags; } set { m_Flags = value; } } + + public short nFromPage { get { return m_nFromPage; } set { m_nFromPage = value; } } + public short nToPage { get { return m_nToPage; } set { m_nToPage = value; } } + public short nMinPage { get { return m_nMinPage; } set { m_nMinPage = value; } } + public short nMaxPage { get { return m_nMaxPage; } set { m_nMaxPage = value; } } + public short nCopies { get { return m_nCopies; } set { m_nCopies = value; } } + + public IntPtr hInstance { get { return m_hInstance; } set { m_hInstance = value; } } + public IntPtr lCustData { get { return m_lCustData; } set { m_lCustData = value; } } + + public WndProc lpfnPrintHook { get { return m_lpfnPrintHook; } set { m_lpfnPrintHook = value; } } + public WndProc lpfnSetupHook { get { return m_lpfnSetupHook; } set { m_lpfnSetupHook = value; } } + + public string lpPrintTemplateName { get { return m_lpPrintTemplateName; } set { m_lpPrintTemplateName = value; } } + public string lpSetupTemplateName { get { return m_lpSetupTemplateName; } set { m_lpSetupTemplateName = value; } } + + public IntPtr hPrintTemplate { get { return m_hPrintTemplate; } set { m_hPrintTemplate = value; } } + public IntPtr hSetupTemplate { get { return m_hSetupTemplate; } set { m_hSetupTemplate = value; } } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class PRINTDLGEX { + public int lStructSize; + + public IntPtr hwndOwner; + public IntPtr hDevMode; + public IntPtr hDevNames; + public IntPtr hDC; + + public int Flags; + public int Flags2; + + public int ExclusionFlags; + + public int nPageRanges; + public int nMaxPageRanges; + + public IntPtr pageRanges; + + public int nMinPage; + public int nMaxPage; + public int nCopies; + + public IntPtr hInstance; + [MarshalAs(UnmanagedType.LPStr)] + public string lpPrintTemplateName; + + public WndProc lpCallback = null; + + public int nPropertyPages; + + public IntPtr lphPropertyPages; + + public int nStartPage; + public int dwResultAction; + + } + + // x86 requires EXPLICIT packing of 1. + [StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Auto)] + public class PRINTPAGERANGE { + public int nFromPage = 0; + public int nToPage = 0; + } + + + + [StructLayout(LayoutKind.Sequential)] + public class PICTDESC + { + internal int cbSizeOfStruct; + public int picType; + internal IntPtr union1; + internal int union2; + internal int union3; + + public static PICTDESC CreateBitmapPICTDESC(IntPtr hbitmap, IntPtr hpal) { + PICTDESC pictdesc = new PICTDESC(); + pictdesc.cbSizeOfStruct = 16; + pictdesc.picType = Ole.PICTYPE_BITMAP; + pictdesc.union1 = hbitmap; + pictdesc.union2 = (int)(((long)hpal) & 0xffffffff); + pictdesc.union3 = (int)(((long)hpal) >> 32); + return pictdesc; + } + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) { + PICTDESC pictdesc = new PICTDESC(); + pictdesc.cbSizeOfStruct = 12; + pictdesc.picType = Ole.PICTYPE_ICON; + pictdesc.union1 = hicon; + return pictdesc; + } + + /* Unused + public static PICTDESC CreateEnhMetafilePICTDESC(IntPtr hEMF) { + PICTDESC pictdesc = new PICTDESC(); + pictdesc.cbSizeOfStruct = 12; + pictdesc.picType = Ole.PICTYPE_ENHMETAFILE; + pictdesc.union1 = hEMF; + return pictdesc; + } + + public static PICTDESC CreateWinMetafilePICTDESC(IntPtr hmetafile, int x, int y) { + PICTDESC pictdesc = new PICTDESC(); + pictdesc.cbSizeOfStruct = 20; + pictdesc.picType = Ole.PICTYPE_METAFILE; + pictdesc.union1 = hmetafile; + pictdesc.union2 = x; + pictdesc.union3 = y; + return pictdesc; + } + */ + + public virtual IntPtr GetHandle() { + return union1; + } + + public virtual IntPtr GetHPal() { + if (picType == Ole.PICTYPE_BITMAP) + return (IntPtr)((uint)union2 | (((long)union3) << 32)); + else + return IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagFONTDESC { + public int cbSizeofstruct = Marshal.SizeOf(typeof(tagFONTDESC)); + + [MarshalAs(UnmanagedType.LPWStr)] + public string lpstrName; + + [MarshalAs(UnmanagedType.U8)] + public long cySize; + + [MarshalAs(UnmanagedType.U2)] + public short sWeight; + + [MarshalAs(UnmanagedType.U2)] + public short sCharset; + + [MarshalAs(UnmanagedType.Bool)] + public bool fItalic; + + [MarshalAs(UnmanagedType.Bool)] + public bool fUnderline; + + [MarshalAs(UnmanagedType.Bool)] + public bool fStrikethrough; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class CHOOSECOLOR { + public int lStructSize = Marshal.SizeOf(typeof(CHOOSECOLOR)); //ndirect.DllLib.sizeOf(this); + public IntPtr hwndOwner; + public IntPtr hInstance; + public int rgbResult; + public IntPtr lpCustColors; + public int Flags; + public IntPtr lCustData = IntPtr.Zero; + public WndProc lpfnHook; + public string lpTemplateName = null; + } + + public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); + + [StructLayout(LayoutKind.Sequential)] + // This is not our convention for managed resources. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + public class BITMAP { + public int bmType = 0; + public int bmWidth = 0; + public int bmHeight = 0; + public int bmWidthBytes = 0; + public short bmPlanes = 0; + public short bmBitsPixel = 0; + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr bmBits = IntPtr.Zero; + } + [StructLayout(LayoutKind.Sequential)] + public class ICONINFO { + public int fIcon = 0; + public int xHotspot = 0; + public int yHotspot = 0; + public IntPtr hbmMask = IntPtr.Zero; + public IntPtr hbmColor = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class LOGPEN { + public int lopnStyle = 0; + // lopnWidth was a by-value POINT structure + public int lopnWidth_x = 0; + public int lopnWidth_y = 0; + public int lopnColor = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class LOGBRUSH { + public int lbStyle; + public int lbColor; + public IntPtr lbHatch; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LOGFONT { + public LOGFONT() { + } + public LOGFONT( LOGFONT lf ) + { + Debug.Assert( lf != null, "lf is null" ); + + this.lfHeight = lf.lfHeight; + this.lfWidth = lf.lfWidth; + this.lfEscapement = lf.lfEscapement; + this.lfOrientation = lf.lfOrientation; + this.lfWeight = lf.lfWeight; + this.lfItalic = lf.lfItalic; + this.lfUnderline = lf.lfUnderline; + this.lfStrikeOut = lf.lfStrikeOut; + this.lfCharSet = lf.lfCharSet; + this.lfOutPrecision = lf.lfOutPrecision; + this.lfClipPrecision = lf.lfClipPrecision; + this.lfQuality = lf.lfQuality; + this.lfPitchAndFamily = lf.lfPitchAndFamily; + this.lfFaceName = lf.lfFaceName; + } + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] + public string lfFaceName; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct TEXTMETRIC + { + public int tmHeight; + public int tmAscent; + public int tmDescent; + public int tmInternalLeading; + public int tmExternalLeading; + public int tmAveCharWidth; + public int tmMaxCharWidth; + public int tmWeight; + public int tmOverhang; + public int tmDigitizedAspectX; + public int tmDigitizedAspectY; + public char tmFirstChar; + public char tmLastChar; + public char tmDefaultChar; + public char tmBreakChar; + public byte tmItalic; + public byte tmUnderlined; + public byte tmStruckOut; + public byte tmPitchAndFamily; + public byte tmCharSet; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + public struct TEXTMETRICA + { + public int tmHeight; + public int tmAscent; + public int tmDescent; + public int tmInternalLeading; + public int tmExternalLeading; + public int tmAveCharWidth; + public int tmMaxCharWidth; + public int tmWeight; + public int tmOverhang; + public int tmDigitizedAspectX; + public int tmDigitizedAspectY; + public byte tmFirstChar; + public byte tmLastChar; + public byte tmDefaultChar; + public byte tmBreakChar; + public byte tmItalic; + public byte tmUnderlined; + public byte tmStruckOut; + public byte tmPitchAndFamily; + public byte tmCharSet; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NOTIFYICONDATA { + public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)); + public IntPtr hWnd; + public int uID; + public int uFlags; + public int uCallbackMessage; + public IntPtr hIcon; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] + public string szTip; + public int dwState = 0; + public int dwStateMask = 0; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szInfo; + public int uTimeoutOrVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string szInfoTitle; + public int dwInfoFlags; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class MENUITEMINFO_T + { + public int cbSize = Marshal.SizeOf(typeof(MENUITEMINFO_T)); + public int fMask; + public int fType; + public int fState; + public int wID; + public IntPtr hSubMenu; + public IntPtr hbmpChecked; + public IntPtr hbmpUnchecked; + public IntPtr dwItemData; + public string dwTypeData; + public int cch; + } + + // This version allows you to read the string that's stuffed + // in the native menu item. You have to do the marshaling on + // your own though. + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class MENUITEMINFO_T_RW + { + public int cbSize = Marshal.SizeOf(typeof(MENUITEMINFO_T_RW)); + public int fMask = 0; + public int fType = 0; + public int fState = 0; + public int wID = 0; + public IntPtr hSubMenu = IntPtr.Zero; + public IntPtr hbmpChecked = IntPtr.Zero; + public IntPtr hbmpUnchecked = IntPtr.Zero; + public IntPtr dwItemData = IntPtr.Zero; + public IntPtr dwTypeData = IntPtr.Zero; + public int cch = 0; + public IntPtr hbmpItem = IntPtr.Zero; // requires WINVER > 5 + } + + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct MSAAMENUINFO + { + public int dwMSAASignature; + public int cchWText; + public string pszWText; + + public MSAAMENUINFO(string text) { + dwMSAASignature = unchecked((int) MSAA_MENU_SIG); + cchWText = text.Length; + pszWText = text; + } + } + + public delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam); + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class OPENFILENAME_I + { + public int lStructSize = Marshal.SizeOf(typeof(OPENFILENAME_I)); //ndirect.DllLib.sizeOf(this); + public IntPtr hwndOwner; + public IntPtr hInstance; + public string lpstrFilter; // use embedded nulls to separate filters + public IntPtr lpstrCustomFilter = IntPtr.Zero; + public int nMaxCustFilter = 0; + public int nFilterIndex; + public IntPtr lpstrFile; + public int nMaxFile = NativeMethods.MAX_PATH; + public IntPtr lpstrFileTitle = IntPtr.Zero; + public int nMaxFileTitle = NativeMethods.MAX_PATH; + public string lpstrInitialDir; + public string lpstrTitle; + public int Flags; + public short nFileOffset = 0; + public short nFileExtension = 0; + public string lpstrDefExt; + public IntPtr lCustData = IntPtr.Zero; + public WndProc lpfnHook; + public string lpTemplateName = null; + public IntPtr pvReserved = IntPtr.Zero; + public int dwReserved = 0; + public int FlagsEx; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto), CLSCompliantAttribute(false)] + public class CHOOSEFONT { + public int lStructSize = Marshal.SizeOf(typeof(CHOOSEFONT)); // ndirect.DllLib.sizeOf(this); + public IntPtr hwndOwner; + public IntPtr hDC; + public IntPtr lpLogFont; + public int iPointSize = 0; + public int Flags; + public int rgbColors; + public IntPtr lCustData = IntPtr.Zero; + public WndProc lpfnHook; + public string lpTemplateName = null; + public IntPtr hInstance; + public string lpszStyle = null; + public short nFontType = 0; + public short ___MISSING_ALIGNMENT__ = 0; + public int nSizeMin; + public int nSizeMax; + } + + + [StructLayout(LayoutKind.Sequential)] + public class BITMAPINFO { + // bmiHeader was a by-value BITMAPINFOHEADER structure + public int bmiHeader_biSize = 40; // ndirect.DllLib.sizeOf( BITMAPINFOHEADER.class ); + public int bmiHeader_biWidth = 0; + public int bmiHeader_biHeight = 0; + public short bmiHeader_biPlanes = 0; + public short bmiHeader_biBitCount = 0; + public int bmiHeader_biCompression = 0; + public int bmiHeader_biSizeImage = 0; + public int bmiHeader_biXPelsPerMeter = 0; + public int bmiHeader_biYPelsPerMeter = 0; + public int bmiHeader_biClrUsed = 0; + public int bmiHeader_biClrImportant = 0; + + // bmiColors was an embedded array of RGBQUAD structures + public byte bmiColors_rgbBlue = 0; + public byte bmiColors_rgbGreen = 0; + public byte bmiColors_rgbRed = 0; + public byte bmiColors_rgbReserved = 0; + + private BITMAPINFO() + { + //Added to make FxCop happy: doesn't really matter since it's internal... + } + } + + [StructLayout(LayoutKind.Sequential)] + public class BITMAPINFOHEADER { + public int biSize = 40; // ndirect.DllLib.sizeOf( this ); + public int biWidth; + public int biHeight; + public short biPlanes; + public short biBitCount; + public int biCompression; + public int biSizeImage = 0; + public int biXPelsPerMeter = 0; + public int biYPelsPerMeter = 0; + public int biClrUsed = 0; + public int biClrImportant = 0; + } + + public class Ole { + public const int PICTYPE_UNINITIALIZED = -1; + public const int PICTYPE_NONE = 0; + public const int PICTYPE_BITMAP = 1; + public const int PICTYPE_METAFILE = 2; + public const int PICTYPE_ICON = 3; + public const int PICTYPE_ENHMETAFILE = 4; + public const int STATFLAG_DEFAULT = 0; + public const int STATFLAG_NONAME = 1; + } + + [StructLayout(LayoutKind.Sequential)] + public class STATSTG { + + [MarshalAs(UnmanagedType.LPWStr)] + public string pwcsName = null; + + public int type; + [MarshalAs(UnmanagedType.I8)] + public long cbSize; + [MarshalAs(UnmanagedType.I8)] + public long mtime = 0; + [MarshalAs(UnmanagedType.I8)] + public long ctime = 0; + [MarshalAs(UnmanagedType.I8)] + public long atime = 0; + [MarshalAs(UnmanagedType.I4)] + public int grfMode = 0; + [MarshalAs(UnmanagedType.I4)] + public int grfLocksSupported; + + public int clsid_data1 = 0; + [MarshalAs(UnmanagedType.I2)] + public short clsid_data2 = 0; + [MarshalAs(UnmanagedType.I2)] + public short clsid_data3 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b0 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b1 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b2 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b3 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b4 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b5 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b6 = 0; + [MarshalAs(UnmanagedType.U1)] + public byte clsid_b7 = 0; + [MarshalAs(UnmanagedType.I4)] + public int grfStateBits = 0; + [MarshalAs(UnmanagedType.I4)] + public int reserved = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class FILETIME { + public uint dwLowDateTime = 0; + public uint dwHighDateTime = 0; + } + + + + + [StructLayout(LayoutKind.Sequential)] + public class SYSTEMTIME { + public short wYear; + public short wMonth; + public short wDayOfWeek; + public short wDay; + public short wHour; + public short wMinute; + public short wSecond; + public short wMilliseconds; + + public override string ToString() { + return "[SYSTEMTIME: " + + wDay.ToString(CultureInfo.InvariantCulture) +"/" + wMonth.ToString(CultureInfo.InvariantCulture) + "/" + wYear.ToString(CultureInfo.InvariantCulture) + + " " + wHour.ToString(CultureInfo.InvariantCulture) + ":" + wMinute.ToString(CultureInfo.InvariantCulture) + ":" + wSecond.ToString(CultureInfo.InvariantCulture) + + "]"; + } + } + + [ + StructLayout(LayoutKind.Sequential), + CLSCompliantAttribute(false) + ] + public sealed class _POINTL { + public int x; + public int y; + + } + + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagSIZE { + public int cx = 0; + public int cy = 0; + + } + + [StructLayout(LayoutKind.Sequential)] + public class COMRECT { + public int left; + public int top; + public int right; + public int bottom; + + public COMRECT() { + } + + public COMRECT(System.Drawing.Rectangle r) { + this.left = r.X; + this.top = r.Y; + this.right = r.Right; + this.bottom = r.Bottom; + } + + + public COMRECT(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /* Unused + public RECT ToRECT() { + return new RECT(left, top, right, bottom); + } + */ + + public static COMRECT FromXYWH(int x, int y, int width, int height) { + return new COMRECT(x, y, x + width, y + height); + } + + public override string ToString() { + return "Left = " + left + " Top " + top + " Right = " + right + " Bottom = " + bottom; + } + } + + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagOleMenuGroupWidths { + [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)/*leftover(offset=0, widths)*/] + public int[] widths = new int[6]; + } + + [StructLayout(LayoutKind.Sequential)] + [Serializable] + public class MSOCRINFOSTRUCT { + public int cbSize = Marshal.SizeOf(typeof(MSOCRINFOSTRUCT)); // size of MSOCRINFO structure in bytes. + public int uIdleTimeInterval; // If olecrfNeedPeriodicIdleTime is registered + // in grfcrf, component needs to perform + // periodic idle time tasks during an idle phase + // every uIdleTimeInterval milliseconds. + public int grfcrf; // bit flags taken from olecrf values (above) + public int grfcadvf; // bit flags taken from olecadvf values (above) + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMLISTVIEW + { + public NMHDR hdr; + public int iItem; + public int iSubItem; + public int uNewState; + public int uOldState; + public int uChanged; + public IntPtr lParam; + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagPOINTF + { + [MarshalAs(UnmanagedType.R4)/*leftover(offset=0, x)*/] + public float x; + + [MarshalAs(UnmanagedType.R4)/*leftover(offset=4, y)*/] + public float y; + + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagOIFI + { + [MarshalAs(UnmanagedType.U4)/*leftover(offset=0, cb)*/] + public int cb; + + public bool fMDIApp; + public IntPtr hwndFrame; + public IntPtr hAccel; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=16, cAccelEntries)*/] + public int cAccelEntries; + + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMLVFINDITEM + { + public NMHDR hdr; + public int iStart; + public LVFINDINFO lvfi; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMHDR + { + public IntPtr hwndFrom; + public IntPtr idFrom; //This is declared as UINT_PTR in winuser.h + public int code; + } + + [ComVisible(true),Guid("626FC520-A41E-11CF-A731-00A0C9082637"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLDocument { + + [return: MarshalAs(UnmanagedType.Interface)] + object GetScript(); + + } + + [ComImport(), Guid("376BD3AA-3845-101B-84ED-08002B2EC713"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPerPropertyBrowsing { + [PreserveSig] + int GetDisplayString( + int dispID, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstr); + + [PreserveSig] + int MapPropertyToPage( + int dispID, + [Out] + out Guid pGuid); + + [PreserveSig] + int GetPredefinedStrings( + int dispID, + [Out] + CA_STRUCT pCaStringsOut, + [Out] + CA_STRUCT pCaCookiesOut); + + [PreserveSig] + int GetPredefinedValue( + int dispID, + [In, MarshalAs(UnmanagedType.U4)] + int dwCookie, + [Out] + VARIANT pVarOut); + } + + [ComImport(), Guid("4D07FC10-F931-11CE-B001-00AA006884E5"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ICategorizeProperties { + + [PreserveSig] + int MapPropertyToCategory( + int dispID, + ref int categoryID); + + [PreserveSig] + int GetCategoryName( + int propcat, + [In, MarshalAs(UnmanagedType.U4)] + int lcid, + out string categoryName); + } + + + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagSIZEL + { + public int cx; + public int cy; + } + + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagOLEVERB + { + public int lVerb; + + [MarshalAs(UnmanagedType.LPWStr)/*leftover(offset=4, customMarshal="UniStringMarshaller", lpszVerbName)*/] + public string lpszVerbName; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=8, fuFlags)*/] + public int fuFlags; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=12, grfAttribs)*/] + public int grfAttribs; + } + + + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagLOGPALETTE + { + [MarshalAs(UnmanagedType.U2)/*leftover(offset=0, palVersion)*/] + public short palVersion = 0; + + [MarshalAs(UnmanagedType.U2)/*leftover(offset=2, palNumEntries)*/] + public short palNumEntries = 0; + } + + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagCONTROLINFO + { + [MarshalAs(UnmanagedType.U4)/*leftover(offset=0, cb)*/] + public int cb = Marshal.SizeOf(typeof(tagCONTROLINFO)); + + public IntPtr hAccel; + + [MarshalAs(UnmanagedType.U2)/*leftover(offset=8, cAccel)*/] + public short cAccel; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=10, dwFlags)*/] + public int dwFlags; + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class CA_STRUCT + { + public int cElems = 0; + public IntPtr pElems = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class VARIANT { + [MarshalAs(UnmanagedType.I2)] + public short vt; + [MarshalAs(UnmanagedType.I2)] + public short reserved1 = 0; + [MarshalAs(UnmanagedType.I2)] + public short reserved2 = 0; + [MarshalAs(UnmanagedType.I2)] + public short reserved3 = 0; + + public IntPtr data1; + + public IntPtr data2; + + + public bool Byref{ + get{ + return 0 != (vt & (int)tagVT.VT_BYREF); + } + } + + public void Clear() { + if ((this.vt == (int)tagVT.VT_UNKNOWN || this.vt == (int)tagVT.VT_DISPATCH) && this.data1 != IntPtr.Zero) { + Marshal.Release(this.data1); + } + + if (this.vt == (int)tagVT.VT_BSTR && this.data1 != IntPtr.Zero) { + SysFreeString(this.data1); + } + + this.data1 = this.data2 = IntPtr.Zero; + this.vt = (int)tagVT.VT_EMPTY; + } + + ~VARIANT() { + Clear(); + } + + public static VARIANT FromObject(Object var) { + VARIANT v = new VARIANT(); + + if (var == null) { + v.vt = (int)tagVT.VT_EMPTY; + } + else if (Convert.IsDBNull(var)) { + } + else { + Type t = var.GetType(); + + if (t == typeof(bool)) { + v.vt = (int)tagVT.VT_BOOL; + } + else if (t == typeof(byte)) { + v.vt = (int)tagVT.VT_UI1; + v.data1 = (IntPtr)Convert.ToByte(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(char)) { + v.vt = (int)tagVT.VT_UI2; + v.data1 = (IntPtr)Convert.ToChar(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(string)) { + v.vt = (int)tagVT.VT_BSTR; + v.data1 = SysAllocString(Convert.ToString(var, CultureInfo.InvariantCulture)); + } + else if (t == typeof(short)) { + v.vt = (int)tagVT.VT_I2; + v.data1 = (IntPtr)Convert.ToInt16(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(int)) { + v.vt = (int)tagVT.VT_I4; + v.data1 = (IntPtr)Convert.ToInt32(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(long)) { + v.vt = (int)tagVT.VT_I8; + v.SetLong(Convert.ToInt64(var, CultureInfo.InvariantCulture)); + } + else if (t == typeof(Decimal)) { + v.vt = (int)tagVT.VT_CY; + Decimal c = (Decimal)var; + // Microsoft, it's bizzare that we need to call this as a static! + v.SetLong(Decimal.ToInt64(c)); + } + else if (t == typeof(decimal)) { + v.vt = (int)tagVT.VT_DECIMAL; + Decimal d = Convert.ToDecimal(var, CultureInfo.InvariantCulture); + v.SetLong(Decimal.ToInt64(d)); + } + else if (t == typeof(double)) { + v.vt = (int)tagVT.VT_R8; + // how do we handle double? + } + else if (t == typeof(float) || t == typeof(Single)) { + v.vt = (int)tagVT.VT_R4; + // how do we handle float? + } + else if (t == typeof(DateTime)) { + v.vt = (int)tagVT.VT_DATE; + v.SetLong(Convert.ToDateTime(var, CultureInfo.InvariantCulture).ToFileTime()); + } + else if (t == typeof(SByte)) { + v.vt = (int)tagVT.VT_I1; + v.data1 = (IntPtr)Convert.ToSByte(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(UInt16)) { + v.vt = (int)tagVT.VT_UI2; + v.data1 = (IntPtr)Convert.ToUInt16(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(UInt32)) { + v.vt = (int)tagVT.VT_UI4; + v.data1 = (IntPtr)Convert.ToUInt32(var, CultureInfo.InvariantCulture); + } + else if (t == typeof(UInt64)) { + v.vt = (int)tagVT.VT_UI8; + v.SetLong((long)Convert.ToUInt64(var, CultureInfo.InvariantCulture)); + } + else if (t == typeof(object) || t == typeof(UnsafeNativeMethods.IDispatch) || t.IsCOMObject) { + v.vt = (t == typeof(UnsafeNativeMethods.IDispatch) ? (short)tagVT.VT_DISPATCH : (short)tagVT.VT_UNKNOWN); + v.data1 = Marshal.GetIUnknownForObject(var); + } + else { + throw new ArgumentException(SR.GetString(SR.ConnPointUnhandledType, t.Name)); + } + } + return v; + } + + [DllImport(ExternDll.Oleaut32,CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr SysAllocString([In, MarshalAs(UnmanagedType.LPWStr)]string s); + + [DllImport(ExternDll.Oleaut32,CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern void SysFreeString(IntPtr pbstr); + + public void SetLong(long lVal) { + data1 = (IntPtr)(lVal & 0xFFFFFFFF); + data2 = (IntPtr)((lVal >> 32) & 0xFFFFFFFF); + } + + public IntPtr ToCoTaskMemPtr() { + IntPtr mem = Marshal.AllocCoTaskMem(16); + Marshal.WriteInt16(mem, vt); + Marshal.WriteInt16(mem, 2, reserved1); + Marshal.WriteInt16(mem, 4, reserved2); + Marshal.WriteInt16(mem, 6, reserved3); + Marshal.WriteInt32(mem, 8, (int) data1); + Marshal.WriteInt32(mem, 12, (int) data2); + return mem; + } + + public object ToObject() { + IntPtr val = data1; + long longVal; + + int vtType = (int)(this.vt & (short)tagVT.VT_TYPEMASK); + + switch (vtType) { + case (int)tagVT.VT_EMPTY: + return null; + case (int)tagVT.VT_NULL: + return Convert.DBNull; + + case (int)tagVT.VT_I1: + if (Byref) { + val = (IntPtr) Marshal.ReadByte(val); + } + return (SByte) (0xFF & (SByte) val); + + case (int)tagVT.VT_UI1: + if (Byref) { + val = (IntPtr) Marshal.ReadByte(val); + } + + return (byte) (0xFF & (byte) val); + + case (int)tagVT.VT_I2: + if (Byref) { + val = (IntPtr) Marshal.ReadInt16(val); + } + return (short)(0xFFFF & (short) val); + + case (int)tagVT.VT_UI2: + if (Byref) { + val = (IntPtr) Marshal.ReadInt16(val); + } + return (UInt16)(0xFFFF & (UInt16) val); + + case (int)tagVT.VT_I4: + case (int)tagVT.VT_INT: + if (Byref) { + val = (IntPtr) Marshal.ReadInt32(val); + } + return (int)val; + + case (int)tagVT.VT_UI4: + case (int)tagVT.VT_UINT: + if (Byref) { + val = (IntPtr) Marshal.ReadInt32(val); + } + return (UInt32)val; + + case (int)tagVT.VT_I8: + case (int)tagVT.VT_UI8: + if (Byref) { + longVal = Marshal.ReadInt64(val); + } + else { + longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); + } + + if (vt == (int)tagVT.VT_I8) { + return (long)longVal; + } + else { + return (UInt64)longVal; + } + } + + if (Byref) { + val = GetRefInt(val); + } + + switch (vtType) { + case (int)tagVT.VT_R4: + case (int)tagVT.VT_R8: + + // can I use unsafe here? + throw new FormatException(SR.GetString(SR.CannotConvertIntToFloat)); + + case (int)tagVT.VT_CY: + // internally currency is 8-byte int scaled by 10,000 + longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); + return new Decimal(longVal); + case (int)tagVT.VT_DATE: + throw new FormatException(SR.GetString(SR.CannotConvertDoubleToDate)); + + case (int)tagVT.VT_BSTR: + case (int)tagVT.VT_LPWSTR: + return Marshal.PtrToStringUni(val); + + case (int)tagVT.VT_LPSTR: + return Marshal.PtrToStringAnsi(val); + + case (int)tagVT.VT_DISPATCH: + case (int)tagVT.VT_UNKNOWN: + { + return Marshal.GetObjectForIUnknown(val); + } + + case (int)tagVT.VT_HRESULT: + return val; + + case (int)tagVT.VT_DECIMAL: + longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); + return new Decimal(longVal); + + case (int)tagVT.VT_BOOL: + return (val != IntPtr.Zero); + + case (int)tagVT.VT_VARIANT: + VARIANT varStruct = (VARIANT)UnsafeNativeMethods.PtrToStructure(val, typeof(VARIANT)); + return varStruct.ToObject(); + case (int)tagVT.VT_CLSID: + //Debug.Fail("PtrToStructure will not work with System.Guid..."); + Guid guid =(Guid)UnsafeNativeMethods.PtrToStructure(val, typeof(Guid)); + return guid; + + case (int)tagVT.VT_FILETIME: + longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); + return new DateTime(longVal); + + case (int)tagVT.VT_USERDEFINED: + throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, "VT_USERDEFINED")); + + case (int)tagVT.VT_ARRAY: + //gSAFEARRAY sa = (tagSAFEARRAY)Marshal.PtrToStructure(val), typeof(tagSAFEARRAY)); + //return GetArrayFromSafeArray(sa); + + case (int)tagVT.VT_VOID: + case (int)tagVT.VT_PTR: + case (int)tagVT.VT_SAFEARRAY: + case (int)tagVT.VT_CARRAY: + + case (int)tagVT.VT_RECORD: + case (int)tagVT.VT_BLOB: + case (int)tagVT.VT_STREAM: + case (int)tagVT.VT_STORAGE: + case (int)tagVT.VT_STREAMED_OBJECT: + case (int)tagVT.VT_STORED_OBJECT: + case (int)tagVT.VT_BLOB_OBJECT: + case (int)tagVT.VT_CF: + case (int)tagVT.VT_BSTR_BLOB: + case (int)tagVT.VT_VECTOR: + case (int)tagVT.VT_BYREF: + //case (int)tagVT.VT_RESERVED: + default: + int iVt = this.vt; + throw new ArgumentException(SR.GetString(SR.COM2UnhandledVT, iVt.ToString(CultureInfo.InvariantCulture))); + } + } + + private static IntPtr GetRefInt(IntPtr value) { + return Marshal.ReadIntPtr(value); + } + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagLICINFO + { + [MarshalAs(UnmanagedType.U4)/*leftover(offset=0, cb)*/] + public int cbLicInfo = Marshal.SizeOf(typeof(tagLICINFO)); + + public int fRuntimeAvailable = 0; + public int fLicVerified = 0; + } + + public enum tagVT { + VT_EMPTY = 0, + VT_NULL = 1, + VT_I2 = 2, + VT_I4 = 3, + VT_R4 = 4, + VT_R8 = 5, + VT_CY = 6, + VT_DATE = 7, + VT_BSTR = 8, + VT_DISPATCH = 9, + VT_ERROR = 10, + VT_BOOL = 11, + VT_VARIANT = 12, + VT_UNKNOWN = 13, + VT_DECIMAL = 14, + VT_I1 = 16, + VT_UI1 = 17, + VT_UI2 = 18, + VT_UI4 = 19, + VT_I8 = 20, + VT_UI8 = 21, + VT_INT = 22, + VT_UINT = 23, + VT_VOID = 24, + VT_HRESULT = 25, + VT_PTR = 26, + VT_SAFEARRAY = 27, + VT_CARRAY = 28, + VT_USERDEFINED = 29, + VT_LPSTR = 30, + VT_LPWSTR = 31, + VT_RECORD = 36, + VT_FILETIME = 64, + VT_BLOB = 65, + VT_STREAM = 66, + VT_STORAGE = 67, + VT_STREAMED_OBJECT = 68, + VT_STORED_OBJECT = 69, + VT_BLOB_OBJECT = 70, + VT_CF = 71, + VT_CLSID = 72, + VT_BSTR_BLOB = 4095, + VT_VECTOR = 4096, + VT_ARRAY = 8192, + VT_BYREF = 16384, + VT_RESERVED = 32768, + VT_ILLEGAL = 65535, + VT_ILLEGALMASKED = 4095, + VT_TYPEMASK = 4095 + } + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class WNDCLASS_D { + public int style; + public WndProc lpfnWndProc; + public int cbClsExtra = 0; + public int cbWndExtra = 0; + public IntPtr hInstance = IntPtr.Zero; + public IntPtr hIcon = IntPtr.Zero; + public IntPtr hCursor = IntPtr.Zero; + public IntPtr hbrBackground = IntPtr.Zero; + public string lpszMenuName = null; + public string lpszClassName = null; + } + + public class MSOCM { + // MSO Component registration flags + public const int msocrfNeedIdleTime = 1; + public const int msocrfNeedPeriodicIdleTime = 2; + public const int msocrfPreTranslateKeys = 4; + public const int msocrfPreTranslateAll = 8; + public const int msocrfNeedSpecActiveNotifs = 16; + public const int msocrfNeedAllActiveNotifs = 32; + public const int msocrfExclusiveBorderSpace = 64; + public const int msocrfExclusiveActivation = 128; + public const int msocrfNeedAllMacEvents = 256; + public const int msocrfMaster = 512; + + // MSO Component registration advise flags (see msocstate enumeration) + public const int msocadvfModal = 1; + public const int msocadvfRedrawOff = 2; + public const int msocadvfWarningsOff = 4; + public const int msocadvfRecording = 8; + + // MSO Component Host flags + public const int msochostfExclusiveBorderSpace = 1; + + // MSO idle flags, passed to IMsoComponent::FDoIdle and + // IMsoStdComponentMgr::FDoIdle. + public const int msoidlefPeriodic = 1; + public const int msoidlefNonPeriodic = 2; + public const int msoidlefPriority = 4; + public const int msoidlefAll = -1; + + // MSO Reasons for pushing a message loop, passed to + // IMsoComponentManager::FPushMessageLoop and + // IMsoComponentHost::FPushMessageLoop. The host should remain in message + // loop until IMsoComponent::FContinueMessageLoop + public const int msoloopDoEventsModal = -2; // Note this is not an official MSO loop -- it just must be distinct. + public const int msoloopMain = -1; // Note this is not an official MSO loop -- it just must be distinct. + public const int msoloopFocusWait = 1; + public const int msoloopDoEvents = 2; + public const int msoloopDebug = 3; + public const int msoloopModalForm = 4; + public const int msoloopModalAlert = 5; + + + /* msocstate values: state IDs passed to + IMsoComponent::OnEnterState, + IMsoComponentManager::OnComponentEnterState/FOnComponentExitState/FInState, + IMsoComponentHost::OnComponentEnterState, + IMsoStdComponentMgr::OnHostEnterState/FOnHostExitState/FInState. + When the host or a component is notified through one of these methods that + another entity (component or host) is entering or exiting a state + identified by one of these state IDs, the host/component should take + appropriate action: + msocstateModal (modal state): + If app is entering modal state, host/component should disable + its toplevel windows, and reenable them when app exits this + state. Also, when this state is entered or exited, host/component + should notify approprate inplace objects via + IOleInPlaceActiveObject::EnableModeless. + msocstateRedrawOff (redrawOff state): + If app is entering redrawOff state, host/component should disable + repainting of its windows, and reenable repainting when app exits + this state. + msocstateWarningsOff (warningsOff state): + If app is entering warningsOff state, host/component should disable + the presentation of any user warnings, and reenable this when + app exits this state. + msocstateRecording (Recording state): + Used to notify host/component when Recording is turned on or off. */ + public const int msocstateModal = 1; + public const int msocstateRedrawOff = 2; + public const int msocstateWarningsOff = 3; + public const int msocstateRecording = 4; + + + /* ** Comments on State Contexts ** + IMsoComponentManager::FCreateSubComponentManager allows one to create a + hierarchical tree of component managers. This tree is used to maintain + multiple contexts with regard to msocstateXXX states. These contexts are + referred to as 'state contexts'. + Each component manager in the tree defines a state context. The + components registered with a particular component manager or any of its + descendents live within that component manager's state context. Calls + to IMsoComponentManager::OnComponentEnterState/FOnComponentExitState + can be used to affect all components, only components within the component + manager's state context, or only those components that are outside of the + component manager's state context. IMsoComponentManager::FInState is used + to query the state of the component manager's state context at its root. + + msoccontext values: context indicators passed to + IMsoComponentManager::OnComponentEnterState/FOnComponentExitState. + These values indicate the state context that is to be affected by the + state change. + In IMsoComponentManager::OnComponentEnterState/FOnComponentExitState, + the comp mgr informs only those components/host that are within the + specified state context. */ + public const int msoccontextAll = 0; + public const int msoccontextMine = 1; + public const int msoccontextOthers = 2; + + /* ** WM_MOUSEACTIVATE Note (for top level compoenents and host) ** + If the active (or tracking) comp's reg info indicates that it + wants mouse messages, then no MA_xxxANDEAT value should be returned + from WM_MOUSEACTIVATE, so that the active (or tracking) comp will be able + to process the resulting mouse message. If one does not want to examine + the reg info, no MA_xxxANDEAT value should be returned from + WM_MOUSEACTIVATE if any comp is active (or tracking). + One can query the reg info of the active (or tracking) component at any + time via IMsoComponentManager::FGetActiveComponent. */ + + /* msogac values: values passed to + IMsoComponentManager::FGetActiveComponent. */ + public const int msogacActive = 0; + public const int msogacTracking = 1; + public const int msogacTrackingOrActive = 2; + + /* msocWindow values: values passed to IMsoComponent::HwndGetWindow. */ + public const int msocWindowFrameToplevel = 0; + public const int msocWindowFrameOwner = 1; + public const int msocWindowComponent = 2; + public const int msocWindowDlgOwner = 3; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class TOOLINFO_T + { + public int cbSize = Marshal.SizeOf(typeof(TOOLINFO_T)); + public int uFlags; + public IntPtr hwnd; + public IntPtr uId; + public RECT rect; + public IntPtr hinst = IntPtr.Zero; + public string lpszText; + public IntPtr lParam = IntPtr.Zero; + } + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class TOOLINFO_TOOLTIP + { + public int cbSize = Marshal.SizeOf(typeof(TOOLINFO_TOOLTIP)); + public int uFlags; + public IntPtr hwnd; + public IntPtr uId; + public RECT rect; + public IntPtr hinst = IntPtr.Zero; + public IntPtr lpszText; + public IntPtr lParam = IntPtr.Zero; + } + + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagDVTARGETDEVICE { + [MarshalAs(UnmanagedType.U4)] + public int tdSize = 0; + [MarshalAs(UnmanagedType.U2)] + public short tdDriverNameOffset = 0; + [MarshalAs(UnmanagedType.U2)] + public short tdDeviceNameOffset = 0; + [MarshalAs(UnmanagedType.U2)] + public short tdPortNameOffset = 0; + [MarshalAs(UnmanagedType.U2)] + public short tdExtDevmodeOffset = 0; + } + + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct TV_ITEM { + public int mask; + public IntPtr hItem; + public int state; + public int stateMask; + public IntPtr /* LPTSTR */ pszText; + public int cchTextMax; + public int iImage; + public int iSelectedImage; + public int cChildren; + public IntPtr lParam; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct TVSORTCB { + public IntPtr hParent; + public NativeMethods.TreeViewCompareCallback lpfnCompare; + public IntPtr lParam; + } + + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct TV_INSERTSTRUCT { + public IntPtr hParent; + public IntPtr hInsertAfter; + public int item_mask; + public IntPtr item_hItem; + public int item_state; + public int item_stateMask; + public IntPtr /* LPTSTR */ item_pszText; + public int item_cchTextMax; + public int item_iImage; + public int item_iSelectedImage; + public int item_cChildren; + public IntPtr item_lParam; + public int item_iIntegral; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMTREEVIEW + { + public NMHDR nmhdr; + public int action; + public TV_ITEM itemOld; + public TV_ITEM itemNew; + public int ptDrag_X; // This should be declared as POINT + public int ptDrag_Y; // we use unsafe blocks to manipulate + // NMTREEVIEW quickly, and POINT is declared + // as a class. Too much churn to change POINT + // now. + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMTVGETINFOTIP + { + public NMHDR nmhdr; + public string pszText; + public int cchTextMax; + public IntPtr item; + public IntPtr lParam; + + } + + [StructLayout(LayoutKind.Sequential)] + public class NMTVDISPINFO + { + public NMHDR hdr; + public TV_ITEM item; + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class POINTL { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct HIGHCONTRAST { + public int cbSize; + public int dwFlags; + public string lpszDefaultScheme; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct HIGHCONTRAST_I { + public int cbSize; + public int dwFlags; + public IntPtr lpszDefaultScheme; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class TCITEM_T + { + public int mask; + public int dwState = 0; + public int dwStateMask = 0; + public string pszText; + public int cchTextMax; + public int iImage; + public IntPtr lParam; + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagDISPPARAMS + { + public IntPtr rgvarg; + public IntPtr rgdispidNamedArgs; + [MarshalAs(UnmanagedType.U4)/*leftover(offset=8, cArgs)*/] + public int cArgs; + [MarshalAs(UnmanagedType.U4)/*leftover(offset=12, cNamedArgs)*/] + public int cNamedArgs; + } + + public enum tagINVOKEKIND { + INVOKE_FUNC = 1, + INVOKE_PROPERTYGET = 2, + INVOKE_PROPERTYPUT = 4, + INVOKE_PROPERTYPUTREF = 8 + } + + [StructLayout(LayoutKind.Sequential)] + public class tagEXCEPINFO { + [MarshalAs(UnmanagedType.U2)] + public short wCode = 0; + [MarshalAs(UnmanagedType.U2)] + public short wReserved = 0; + [MarshalAs(UnmanagedType.BStr)] + public string bstrSource = null; + [MarshalAs(UnmanagedType.BStr)] + public string bstrDescription = null; + [MarshalAs(UnmanagedType.BStr)] + public string bstrHelpFile = null; + [MarshalAs(UnmanagedType.U4)] + public int dwHelpContext = 0; + + public IntPtr pvReserved = IntPtr.Zero; + + public IntPtr pfnDeferredFillIn = IntPtr.Zero; + [MarshalAs(UnmanagedType.U4)] + public int scode = 0; + } + + public enum tagDESCKIND { + DESCKIND_NONE = 0, + DESCKIND_FUNCDESC = 1, + DESCKIND_VARDESC = 2, + DESCKIND_TYPECOMP = 3, + DESCKIND_IMPLICITAPPOBJ = 4, + DESCKIND_MAX = 5 + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagFUNCDESC { + public int memid = 0; + + public IntPtr lprgscode = IntPtr.Zero; + + // This is marked as NATIVE_TYPE_PTR, + // but the EE doesn't look for that, tries to handle it as + // a ELEMENT_TYPE_VALUECLASS and fails because it + // isn't a NATIVE_TYPE_NESTEDSTRUCT + /*[MarshalAs(UnmanagedType.PTR)]*/ + + public /*NativeMethods.tagELEMDESC*/ IntPtr lprgelemdescParam = IntPtr.Zero; + + // cpb, Microsoft, the EE chokes on Enums in structs + + public /*NativeMethods.tagFUNCKIND*/ int funckind = 0; + + public /*NativeMethods.tagINVOKEKIND*/ int invkind = 0; + + public /*NativeMethods.tagCALLCONV*/ int callconv = 0; + [MarshalAs(UnmanagedType.I2)] + public short cParams = 0; + [MarshalAs(UnmanagedType.I2)] + public short cParamsOpt = 0; + [MarshalAs(UnmanagedType.I2)] + public short oVft = 0; + [MarshalAs(UnmanagedType.I2)] + public short cScodesi = 0; + public NativeMethods.value_tagELEMDESC elemdescFunc; + [MarshalAs(UnmanagedType.U2)] + public short wFuncFlags = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagVARDESC { + public int memid = 0; + public IntPtr lpstrSchema = IntPtr.Zero; + public IntPtr unionMember = IntPtr.Zero; + public NativeMethods.value_tagELEMDESC elemdescVar; + [MarshalAs(UnmanagedType.U2)] + public short wVarFlags = 0; + public /*NativeMethods.tagVARKIND*/ int varkind = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public struct value_tagELEMDESC { + public NativeMethods.tagTYPEDESC tdesc; + public NativeMethods.tagPARAMDESC paramdesc; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPOS { + public IntPtr hwnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public int flags; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct HDLAYOUT { + public IntPtr prc; // pointer to a RECT + public IntPtr pwpos; // pointer to a WINDOWPOS + } + + [StructLayout(LayoutKind.Sequential)] + public class DRAWITEMSTRUCT { + public int CtlType = 0; + public int CtlID = 0; + public int itemID = 0; + public int itemAction =0; + public int itemState = 0; + public IntPtr hwndItem = IntPtr.Zero; + public IntPtr hDC = IntPtr.Zero; + public RECT rcItem; + public IntPtr itemData = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class MEASUREITEMSTRUCT { + public int CtlType = 0; + public int CtlID = 0; + public int itemID = 0; + public int itemWidth = 0; + public int itemHeight = 0; + public IntPtr itemData = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class HELPINFO { + public int cbSize = Marshal.SizeOf(typeof(HELPINFO)); + public int iContextType = 0; + public int iCtrlId = 0; + public IntPtr hItemHandle = IntPtr.Zero; + public IntPtr dwContextId = IntPtr.Zero; + public POINT MousePos = null; + } + + [StructLayout(LayoutKind.Sequential)] + public class ACCEL { + public byte fVirt; + public short key; + public short cmd; + } + + [StructLayout(LayoutKind.Sequential)] + public class MINMAXINFO { + public POINT ptReserved = null; + public POINT ptMaxSize = null; + public POINT ptMaxPosition = null; + public POINT ptMinTrackSize = null; + public POINT ptMaxTrackSize = null; + } + + + + [ComImport(), Guid("B196B28B-BAB4-101A-B69C-00AA00341D07"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISpecifyPropertyPages { + void GetPages( + [Out] + NativeMethods.tagCAUUID pPages); + + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagCAUUID + { + [MarshalAs(UnmanagedType.U4)/*leftover(offset=0, cElems)*/] + public int cElems = 0; + public IntPtr pElems = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMTOOLBAR + { + public NMHDR hdr; + public int iItem; + public TBBUTTON tbButton; + public int cchText; + public IntPtr pszText; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TBBUTTON { + public int iBitmap; + public int idCommand; + public byte fsState; + public byte fsStyle; + public byte bReserved0; + public byte bReserved1; + public IntPtr dwData; + public IntPtr iString; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class TOOLTIPTEXT + { + public NMHDR hdr; + public string lpszText; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=80)] + public string szText = null; + + public IntPtr hinst; + public int uFlags; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + public class TOOLTIPTEXTA + { + public NMHDR hdr; + public string lpszText; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=80)] + public string szText = null; + + public IntPtr hinst; + public int uFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMTBHOTITEM + { + public NMHDR hdr; + public int idOld; + public int idNew; + public int dwFlags; + } + + public const int HICF_OTHER = 0x00000000; + public const int HICF_MOUSE = 0x00000001; // Triggered by mouse + public const int HICF_ARROWKEYS = 0x00000002; // Triggered by arrow keys + public const int HICF_ACCELERATOR = 0x00000004; // Triggered by accelerator + public const int HICF_DUPACCEL = 0x00000008; // This accelerator is not unique + public const int HICF_ENTERING = 0x00000010; // idOld is invalid + public const int HICF_LEAVING = 0x00000020; // idNew is invalid + public const int HICF_RESELECT = 0x00000040; // hot item reselected + public const int HICF_LMOUSE = 0x00000080; // left mouse button selected + public const int HICF_TOGGLEDROPDOWN = 0x00000100; // Toggle button's dropdown state + + + /* + Microsoft: dead code. Keep it in case we need an HDITEM class where the pszText is actually set. + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] + public class HDITEM + { + public int mask = 0; + public int cxy = 0; + public string pszText = null; + public IntPtr hbm = IntPtr.Zero; + public int cchTextMax = 0; + public int fmt = 0; + public IntPtr lParam = IntPtr.Zero; + public int iImage = 0; + public int iOrder = 0; + public int type = 0; + public IntPtr pvFilter = IntPtr.Zero; + } + */ + + // HDN_ITEMCHANGING will send us an HDITEM w/ pszText set to some random pointer. + // Marshal.PtrToStructure chokes when it has to convert a random pointer to a string. + // For HDN_ITEMCHANGING map pszText to an IntPtr + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] + public class HDITEM2 + { + public int mask = 0; + public int cxy = 0; + public IntPtr pszText_notUsed = IntPtr.Zero; + public IntPtr hbm = IntPtr.Zero; + public int cchTextMax = 0; + public int fmt = 0; + public IntPtr lParam = IntPtr.Zero; + public int iImage = 0; + public int iOrder = 0; + public int type = 0; + public IntPtr pvFilter = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] + public struct TBBUTTONINFO + { + public int cbSize; + public int dwMask; + public int idCommand; + public int iImage; + public byte fsState; + public byte fsStyle; + public short cx; + public IntPtr lParam; + public IntPtr pszText; + public int cchTest; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class TV_HITTESTINFO { + public int pt_x; + public int pt_y; + public int flags = 0; + public IntPtr hItem = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class NMTVCUSTOMDRAW + { + public NMCUSTOMDRAW nmcd; + public int clrText; + public int clrTextBk; + public int iLevel = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMCUSTOMDRAW { + public NMHDR nmcd; + public int dwDrawStage; + public IntPtr hdc; + public RECT rc; + public IntPtr dwItemSpec; + public int uItemState; + public IntPtr lItemlParam; + } + + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class MCHITTESTINFO { + public int cbSize = Marshal.SizeOf(typeof(MCHITTESTINFO)); + public int pt_x = 0; + public int pt_y = 0; + public int uHit = 0; + public short st_wYear = 0; + public short st_wMonth = 0; + public short st_wDayOfWeek = 0; + public short st_wDay = 0; + public short st_wHour = 0; + public short st_wMinute = 0; + public short st_wSecond = 0; + public short st_wMilliseconds = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMSELCHANGE + { + public NMHDR nmhdr; + public SYSTEMTIME stSelStart = null; + public SYSTEMTIME stSelEnd = null; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMDAYSTATE + { + public NMHDR nmhdr; + public SYSTEMTIME stStart = null; + public int cDayState = 0; + public IntPtr prgDayState; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class NMVIEWCHANGE + { + public NMHDR nmhdr; + public uint uOldView; + public uint uNewView; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMLVCUSTOMDRAW + { + public NMCUSTOMDRAW nmcd; + public int clrText; + public int clrTextBk; + public int iSubItem; + public int dwItemType; + // Item Custom Draw + public int clrFace; + public int iIconEffect; + public int iIconPhase; + public int iPartId; + public int iStateId; + // Group Custom Draw + public RECT rcText; + public uint uAlign; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMLVGETINFOTIP + { + public NMHDR nmhdr; + public int flags = 0; + public IntPtr lpszText = IntPtr.Zero; + public int cchTextMax = 0; + public int item = 0; + public int subItem = 0; + public IntPtr lParam = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class NMLVKEYDOWN + { + public NMHDR hdr; + public short wVKey = 0; + public uint flags = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVHITTESTINFO { + public int pt_x; + public int pt_y; + public int flags = 0; + public int iItem = 0; + public int iSubItem = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVBKIMAGE + { + public int ulFlags; + public IntPtr hBmp = IntPtr.Zero; // not used + public string pszImage; + public int cchImageMax; + public int xOffset; + public int yOffset; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVCOLUMN_T + { + public int mask = 0; + public int fmt = 0; + public int cx = 0; + public string pszText = null; + public int cchTextMax = 0; + public int iSubItem = 0; + public int iImage = 0; + public int iOrder = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct LVFINDINFO { + public int flags; + public string psz; + public IntPtr lParam; + public int ptX; // was POINT pt + public int ptY; + public int vkDirection; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct LVITEM { + public int mask; + public int iItem; + public int iSubItem; + public int state; + public int stateMask; + public string pszText; + public int cchTextMax; + public int iImage; + public IntPtr lParam; + public int iIndent; + public int iGroupId; + public int cColumns; // tile view columns + public IntPtr puColumns; + + public unsafe void Reset() { + pszText = null; + mask = 0; + iItem = 0; + iSubItem = 0; + stateMask = 0; + state = 0; + cchTextMax = 0; + iImage = 0; + lParam = IntPtr.Zero; + iIndent = 0; + iGroupId = 0; + cColumns = 0; + puColumns = IntPtr.Zero; + } + + public override string ToString() { + return "LVITEM: pszText = " + pszText + + ", iItem = " + iItem.ToString(CultureInfo.InvariantCulture) + + ", iSubItem = " + iSubItem.ToString(CultureInfo.InvariantCulture) + + ", state = " + state.ToString(CultureInfo.InvariantCulture) + + ", iGroupId = " + iGroupId.ToString(CultureInfo.InvariantCulture) + + ", cColumns = " + cColumns.ToString(CultureInfo.InvariantCulture); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct LVITEM_NOTEXT { + public int mask; + public int iItem; + public int iSubItem; + public int state; + public int stateMask; + public IntPtr /*string*/ pszText; + public int cchTextMax; + public int iImage; + public IntPtr lParam; + public int iIndent; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVCOLUMN { + public int mask; + public int fmt; + public int cx = 0; + public IntPtr /* LPWSTR */ pszText; + public int cchTextMax = 0; + public int iSubItem = 0; + public int iImage; + public int iOrder = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public class LVGROUP { + public uint cbSize = (uint)Marshal.SizeOf(typeof(LVGROUP)); + public uint mask; + public IntPtr pszHeader; + public int cchHeader; + public IntPtr pszFooter = IntPtr.Zero; + public int cchFooter = 0; + public int iGroupId; + public uint stateMask = 0; + public uint state = 0; + public uint uAlign; + + public override string ToString() { + return "LVGROUP: header = " + pszHeader.ToString() + ", iGroupId = " + iGroupId.ToString(CultureInfo.InvariantCulture); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVINSERTMARK { + public uint cbSize = (uint)Marshal.SizeOf(typeof(LVINSERTMARK)); + public int dwFlags; + public int iItem; + public int dwReserved = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class LVTILEVIEWINFO { + public uint cbSize = (uint)Marshal.SizeOf(typeof(LVTILEVIEWINFO)); + public int dwMask; + public int dwFlags; + public SIZE sizeTile; + public int cLines; + public RECT rcLabelMargin; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMLVCACHEHINT { + public NMHDR hdr; + public int iFrom = 0; + public int iTo = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMLVDISPINFO + { + public NMHDR hdr; + public LVITEM item; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMLVDISPINFO_NOTEXT + { + public NMHDR hdr; + public LVITEM_NOTEXT item; + } + + [StructLayout(LayoutKind.Sequential)] + public class NMLVODSTATECHANGE { + public NMHDR hdr; + public int iFrom = 0; + public int iTo = 0; + public int uNewState = 0; + public int uOldState = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class CLIENTCREATESTRUCT { + public IntPtr hWindowMenu; + public int idFirstChild; + + public CLIENTCREATESTRUCT(IntPtr hmenu, int idFirst) { + hWindowMenu = hmenu; + idFirstChild = idFirst; + } + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class NMDATETIMECHANGE + { + public NMHDR nmhdr; + public int dwFlags = 0; + public SYSTEMTIME st = null; + } + + + [StructLayout(LayoutKind.Sequential)] + public class COPYDATASTRUCT { + public int dwData = 0; + public int cbData = 0; + public IntPtr lpData = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public class NMHEADER { + public NMHDR nmhdr; + public int iItem = 0; + public int iButton = 0; + public IntPtr pItem = IntPtr.Zero; // HDITEM* + } + + [StructLayout(LayoutKind.Sequential)] + public class MOUSEHOOKSTRUCT { + // pt was a by-value POINT structure + public int pt_x = 0; + public int pt_y = 0; + public IntPtr hWnd = IntPtr.Zero; + public int wHitTestCode = 0; + public int dwExtraInfo = 0; + } + + #region SendKeys SendInput functionality + + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEINPUT + { + public int dx; + public int dy; + public int mouseData; + public int dwFlags; + public int time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT + { + public short wVk; + public short wScan; + public int dwFlags; + public int time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HARDWAREINPUT + { + public int uMsg; + public short wParamL; + public short wParamH; + } + + [StructLayout(LayoutKind.Sequential)] + public struct INPUT + { + public int type; + public INPUTUNION inputUnion; + } + + // We need to split the field offset out into a union struct to avoid + // silent problems in 64 bit + [StructLayout(LayoutKind.Explicit)] + public struct INPUTUNION + { + [FieldOffset(0)] + public MOUSEINPUT mi; + [FieldOffset(0)] + public KEYBDINPUT ki; + [FieldOffset(0)] + public HARDWAREINPUT hi; + } + + #endregion + + [StructLayout(LayoutKind.Sequential)] + public class CHARRANGE + { + public int cpMin; + public int cpMax; + } + + [StructLayout(LayoutKind.Sequential, Pack=4)] + public class CHARFORMATW + { + public int cbSize = Marshal.SizeOf(typeof(CHARFORMATW)); + public int dwMask; + public int dwEffects; + public int yHeight; + public int yOffset = 0; + public int crTextColor = 0; + public byte bCharSet; + public byte bPitchAndFamily; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=64)] + public byte[] szFaceName = new byte[64]; + } + + [StructLayout(LayoutKind.Sequential, Pack=4)] + public class CHARFORMATA + { + public int cbSize = Marshal.SizeOf(typeof(CHARFORMATA)); + public int dwMask; + public int dwEffects; + public int yHeight; + public int yOffset; + public int crTextColor; + public byte bCharSet; + public byte bPitchAndFamily; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + public byte[] szFaceName = new byte[32]; + } + + [StructLayout(LayoutKind.Sequential, Pack=4)] + public class CHARFORMAT2A + { + public int cbSize = Marshal.SizeOf(typeof(CHARFORMAT2A)); + public int dwMask = 0; + public int dwEffects = 0; + public int yHeight = 0; + public int yOffset = 0; + public int crTextColor = 0; + public byte bCharSet = 0; + public byte bPitchAndFamily = 0; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + public byte[] szFaceName = new byte[32]; + public short wWeight = 0; + public short sSpacing = 0; + public int crBackColor = 0; + public int lcid = 0; + public int dwReserved = 0; + public short sStyle = 0; + public short wKerning = 0; + public byte bUnderlineType = 0; + public byte bAnimation = 0; + public byte bRevAuthor = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class TEXTRANGE + { + public CHARRANGE chrg; + public IntPtr lpstrText; /* allocated by caller, zero terminated by RichEdit */ + } + + [StructLayout(LayoutKind.Sequential)] + public class GETTEXTLENGTHEX + { // Taken from richedit.h: + public uint flags; // Flags (see GTL_XXX defines) + public uint codepage; // Code page for translation (CP_ACP for default, 1200 for Unicode) + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=4)] + public class SELCHANGE { + public NMHDR nmhdr; + public CHARRANGE chrg = null; + public int seltyp = 0; + } + + [StructLayout(LayoutKind.Sequential)] + public class PARAFORMAT + { + public int cbSize = Marshal.SizeOf(typeof(PARAFORMAT)); + public int dwMask; + public short wNumbering; + public short wReserved = 0; + public int dxStartIndent; + public int dxRightIndent; + public int dxOffset; + public short wAlignment; + public short cTabCount; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + public int[] rgxTabs; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class FINDTEXT + { + public CHARRANGE chrg; + public string lpstrText; + } + + [StructLayout(LayoutKind.Sequential)] + public class REPASTESPECIAL + { + public int dwAspect; + public int dwParam; + } + + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + [StructLayout(LayoutKind.Sequential)] + public class ENLINK + { + public NMHDR nmhdr; + public int msg = 0; + public IntPtr wParam = IntPtr.Zero; + public IntPtr lParam = IntPtr.Zero; + public CHARRANGE charrange = null; + } + + [StructLayout(LayoutKind.Sequential)] + public class ENLINK64 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 56)] + public byte[] contents = new byte[56]; + } + + // GetRegionData structures + [StructLayout(LayoutKind.Sequential)] + public struct RGNDATAHEADER + { + public int cbSizeOfStruct; + public int iType; + public int nCount; + public int nRgnSize; + // public NativeMethods.RECT rcBound; // Note that we don't define this field as part of the marshaling + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class OCPFIPARAMS + { + public int cbSizeOfStruct = Marshal.SizeOf(typeof(OCPFIPARAMS)); + public IntPtr hwndOwner; + public int x = 0; + public int y = 0; + public string lpszCaption; + public int cObjects = 1; + public IntPtr ppUnk; + public int pageCount = 1; + public IntPtr uuid; + public int lcid = Application.CurrentCulture.LCID; + public int dispidInitial; + } + + [ComVisible(true), StructLayout(LayoutKind.Sequential)] + public class DOCHOSTUIINFO + { + [MarshalAs(UnmanagedType.U4)] + public int cbSize = Marshal.SizeOf(typeof(DOCHOSTUIINFO)); + [MarshalAs(UnmanagedType.I4)] + public int dwFlags; + [MarshalAs(UnmanagedType.I4)] + public int dwDoubleClick; + [MarshalAs(UnmanagedType.I4)] + public int dwReserved1 = 0; + [MarshalAs(UnmanagedType.I4)] + public int dwReserved2 = 0; + } + + public enum DOCHOSTUIFLAG + { + DIALOG = 0x1, + DISABLE_HELP_MENU = 0x2, + NO3DBORDER = 0x4, + SCROLL_NO = 0x8, + DISABLE_SCRIPT_INACTIVE = 0x10, + OPENNEWWIN = 0x20, + DISABLE_OFFSCREEN = 0x40, + FLAT_SCROLLBAR = 0x80, + DIV_BLOCKDEFAULT = 0x100, + ACTIVATE_CLIENTHIT_ONLY = 0x200, + NO3DOUTERBORDER = 0x00200000, + THEME = 0x00040000, + NOTHEME = 0x80000, + DISABLE_COOKIE = 0x400 + } + + public enum DOCHOSTUIDBLCLICK + { + DEFAULT = 0x0, + SHOWPROPERTIES = 0x1, + SHOWCODE = 0x2 + } + + public enum OLECMDID + { + OLECMDID_SAVEAS = 4, + OLECMDID_PRINT = 6, + OLECMDID_PRINTPREVIEW = 7, + OLECMDID_PAGESETUP = 8, + OLECMDID_PROPERTIES = 10 + } + + public enum OLECMDEXECOPT + { + OLECMDEXECOPT_DODEFAULT = 0, + OLECMDEXECOPT_PROMPTUSER = 1, + OLECMDEXECOPT_DONTPROMPTUSER = 2, + OLECMDEXECOPT_SHOWHELP = 3 + } + + public enum OLECMDF + { + OLECMDF_SUPPORTED = 0x00000001, + OLECMDF_ENABLED = 0x00000002, + OLECMDF_LATCHED = 0x00000004, + OLECMDF_NINCHED = 0x00000008, + OLECMDF_INVISIBLE = 0x00000010, + OLECMDF_DEFHIDEONCTXTMENU = 0x00000020 + } + + [StructLayout(LayoutKind.Sequential)] + public class ENDROPFILES + { + public NMHDR nmhdr; + public IntPtr hDrop = IntPtr.Zero; + public int cp = 0; + public bool fProtected = false; + } + + [StructLayout(LayoutKind.Sequential)] + public class REQRESIZE + { + public NMHDR nmhdr; + public RECT rc; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class ENPROTECTED + { + public NMHDR nmhdr; + public int msg; + public IntPtr wParam; + public IntPtr lParam; + public CHARRANGE chrg; + } + + [StructLayout(LayoutKind.Sequential)] + public class ENPROTECTED64 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst=56)] + public byte[] contents = new byte[56]; + } + + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + public class ActiveX + { + public const int OCM__BASE = 0x2000; + public const int DISPID_VALUE = unchecked((int)0x0); + public const int DISPID_UNKNOWN = unchecked((int)0xFFFFFFFF); + public const int DISPID_AUTOSIZE = unchecked((int)0xFFFFFE0C); + public const int DISPID_BACKCOLOR = unchecked((int)0xFFFFFE0B); + public const int DISPID_BACKSTYLE = unchecked((int)0xFFFFFE0A); + public const int DISPID_BORDERCOLOR = unchecked((int)0xFFFFFE09); + public const int DISPID_BORDERSTYLE = unchecked((int)0xFFFFFE08); + public const int DISPID_BORDERWIDTH = unchecked((int)0xFFFFFE07); + public const int DISPID_DRAWMODE = unchecked((int)0xFFFFFE05); + public const int DISPID_DRAWSTYLE = unchecked((int)0xFFFFFE04); + public const int DISPID_DRAWWIDTH = unchecked((int)0xFFFFFE03); + public const int DISPID_FILLCOLOR = unchecked((int)0xFFFFFE02); + public const int DISPID_FILLSTYLE = unchecked((int)0xFFFFFE01); + public const int DISPID_FONT = unchecked((int)0xFFFFFE00); + public const int DISPID_FORECOLOR = unchecked((int)0xFFFFFDFF); + public const int DISPID_ENABLED = unchecked((int)0xFFFFFDFE); + public const int DISPID_HWND = unchecked((int)0xFFFFFDFD); + public const int DISPID_TABSTOP = unchecked((int)0xFFFFFDFC); + public const int DISPID_TEXT = unchecked((int)0xFFFFFDFB); + public const int DISPID_CAPTION = unchecked((int)0xFFFFFDFA); + public const int DISPID_BORDERVISIBLE = unchecked((int)0xFFFFFDF9); + public const int DISPID_APPEARANCE = unchecked((int)0xFFFFFDF8); + public const int DISPID_MOUSEPOINTER = unchecked((int)0xFFFFFDF7); + public const int DISPID_MOUSEICON = unchecked((int)0xFFFFFDF6); + public const int DISPID_PICTURE = unchecked((int)0xFFFFFDF5); + public const int DISPID_VALID = unchecked((int)0xFFFFFDF4); + public const int DISPID_READYSTATE = unchecked((int)0xFFFFFDF3); + public const int DISPID_REFRESH = unchecked((int)0xFFFFFDDA); + public const int DISPID_DOCLICK = unchecked((int)0xFFFFFDD9); + public const int DISPID_ABOUTBOX = unchecked((int)0xFFFFFDD8); + public const int DISPID_CLICK = unchecked((int)0xFFFFFDA8); + public const int DISPID_DBLCLICK = unchecked((int)0xFFFFFDA7); + public const int DISPID_KEYDOWN = unchecked((int)0xFFFFFDA6); + public const int DISPID_KEYPRESS = unchecked((int)0xFFFFFDA5); + public const int DISPID_KEYUP = unchecked((int)0xFFFFFDA4); + public const int DISPID_MOUSEDOWN = unchecked((int)0xFFFFFDA3); + public const int DISPID_MOUSEMOVE = unchecked((int)0xFFFFFDA2); + public const int DISPID_MOUSEUP = unchecked((int)0xFFFFFDA1); + public const int DISPID_ERROREVENT = unchecked((int)0xFFFFFDA0); + public const int DISPID_RIGHTTOLEFT = unchecked((int)0xFFFFFD9D); + public const int DISPID_READYSTATECHANGE = unchecked((int)0xFFFFFD9F); + public const int DISPID_AMBIENT_BACKCOLOR = unchecked((int)0xFFFFFD43); + public const int DISPID_AMBIENT_DISPLAYNAME = unchecked((int)0xFFFFFD42); + public const int DISPID_AMBIENT_FONT = unchecked((int)0xFFFFFD41); + public const int DISPID_AMBIENT_FORECOLOR = unchecked((int)0xFFFFFD40); + public const int DISPID_AMBIENT_LOCALEID = unchecked((int)0xFFFFFD3F); + public const int DISPID_AMBIENT_MESSAGEREFLECT = unchecked((int)0xFFFFFD3E); + public const int DISPID_AMBIENT_SCALEUNITS = unchecked((int)0xFFFFFD3D); + public const int DISPID_AMBIENT_TEXTALIGN = unchecked((int)0xFFFFFD3C); + public const int DISPID_AMBIENT_USERMODE = unchecked((int)0xFFFFFD3B); + public const int DISPID_AMBIENT_UIDEAD = unchecked((int)0xFFFFFD3A); + public const int DISPID_AMBIENT_SHOWGRABHANDLES = unchecked((int)0xFFFFFD39); + public const int DISPID_AMBIENT_SHOWHATCHING = unchecked((int)0xFFFFFD38); + public const int DISPID_AMBIENT_DISPLAYASDEFAULT = unchecked((int)0xFFFFFD37); + public const int DISPID_AMBIENT_SUPPORTSMNEMONICS = unchecked((int)0xFFFFFD36); + public const int DISPID_AMBIENT_AUTOCLIP = unchecked((int)0xFFFFFD35); + public const int DISPID_AMBIENT_APPEARANCE = unchecked((int)0xFFFFFD34); + public const int DISPID_AMBIENT_PALETTE = unchecked((int)0xFFFFFD2A); + public const int DISPID_AMBIENT_TRANSFERPRIORITY = unchecked((int)0xFFFFFD28); + public const int DISPID_AMBIENT_RIGHTTOLEFT = unchecked((int)0xFFFFFD24); + public const int DISPID_Name = unchecked((int)0xFFFFFCE0); + public const int DISPID_Delete = unchecked((int)0xFFFFFCDF); + public const int DISPID_Object = unchecked((int)0xFFFFFCDE); + public const int DISPID_Parent = unchecked((int)0xFFFFFCDD); + public const int DVASPECT_CONTENT = 0x1; + public const int DVASPECT_THUMBNAIL = 0x2; + public const int DVASPECT_ICON = 0x4; + public const int DVASPECT_DOCPRINT = 0x8; + public const int OLEMISC_RECOMPOSEONRESIZE = 0x1; + public const int OLEMISC_ONLYICONIC = 0x2; + public const int OLEMISC_INSERTNOTREPLACE = 0x4; + public const int OLEMISC_STATIC = 0x8; + public const int OLEMISC_CANTLINKINSIDE = 0x10; + public const int OLEMISC_CANLINKBYOLE1 = 0x20; + public const int OLEMISC_ISLINKOBJECT = 0x40; + public const int OLEMISC_INSIDEOUT = 0x80; + public const int OLEMISC_ACTIVATEWHENVISIBLE = 0x100; + public const int OLEMISC_RENDERINGISDEVICEINDEPENDENT = 0x200; + public const int OLEMISC_INVISIBLEATRUNTIME = 0x400; + public const int OLEMISC_ALWAYSRUN = 0x800; + public const int OLEMISC_ACTSLIKEBUTTON = 0x1000; + public const int OLEMISC_ACTSLIKELABEL = 0x2000; + public const int OLEMISC_NOUIACTIVATE = 0x4000; + public const int OLEMISC_ALIGNABLE = 0x8000; + public const int OLEMISC_SIMPLEFRAME = 0x10000; + public const int OLEMISC_SETCLIENTSITEFIRST = 0x20000; + public const int OLEMISC_IMEMODE = 0x40000; + public const int OLEMISC_IGNOREACTIVATEWHENVISIBLE = 0x80000; + public const int OLEMISC_WANTSTOMENUMERGE = 0x100000; + public const int OLEMISC_SUPPORTSMULTILEVELUNDO = 0x200000; + public const int QACONTAINER_SHOWHATCHING = 0x1; + public const int QACONTAINER_SHOWGRABHANDLES = 0x2; + public const int QACONTAINER_USERMODE = 0x4; + public const int QACONTAINER_DISPLAYASDEFAULT = 0x8; + public const int QACONTAINER_UIDEAD = 0x10; + public const int QACONTAINER_AUTOCLIP = 0x20; + public const int QACONTAINER_MESSAGEREFLECT = 0x40; + public const int QACONTAINER_SUPPORTSMNEMONICS = 0x80; + public const int XFORMCOORDS_POSITION = 0x1; + public const int XFORMCOORDS_SIZE = 0x2; + public const int XFORMCOORDS_HIMETRICTOCONTAINER = 0x4; + public const int XFORMCOORDS_CONTAINERTOHIMETRIC = 0x8; + public const int PROPCAT_Nil = unchecked((int)0xFFFFFFFF); + public const int PROPCAT_Misc = unchecked((int)0xFFFFFFFE); + public const int PROPCAT_Font = unchecked((int)0xFFFFFFFD); + public const int PROPCAT_Position = unchecked((int)0xFFFFFFFC); + public const int PROPCAT_Appearance = unchecked((int)0xFFFFFFFB); + public const int PROPCAT_Behavior = unchecked((int)0xFFFFFFFA); + public const int PROPCAT_Data = unchecked((int)0xFFFFFFF9); + public const int PROPCAT_List = unchecked((int)0xFFFFFFF8); + public const int PROPCAT_Text = unchecked((int)0xFFFFFFF7); + public const int PROPCAT_Scale = unchecked((int)0xFFFFFFF6); + public const int PROPCAT_DDE = unchecked((int)0xFFFFFFF5); + public const int GC_WCH_SIBLING = 0x1; + public const int GC_WCH_CONTAINER = 0x2; + public const int GC_WCH_CONTAINED = 0x3; + public const int GC_WCH_ALL = 0x4; + public const int GC_WCH_FREVERSEDIR = 0x8000000; + public const int GC_WCH_FONLYNEXT = 0x10000000; + public const int GC_WCH_FONLYPREV = 0x20000000; + public const int GC_WCH_FSELECTED = 0x40000000; + public const int OLECONTF_EMBEDDINGS = 0x1; + public const int OLECONTF_LINKS = 0x2; + public const int OLECONTF_OTHERS = 0x4; + public const int OLECONTF_ONLYUSER = 0x8; + public const int OLECONTF_ONLYIFRUNNING = 0x10; + public const int ALIGN_MIN = 0x0; + public const int ALIGN_NO_CHANGE = 0x0; + public const int ALIGN_TOP = 0x1; + public const int ALIGN_BOTTOM = 0x2; + public const int ALIGN_LEFT = 0x3; + public const int ALIGN_RIGHT = 0x4; + public const int ALIGN_MAX = 0x4; + public const int OLEVERBATTRIB_NEVERDIRTIES = 0x1; + public const int OLEVERBATTRIB_ONCONTAINERMENU = 0x2; + + public static Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + + private ActiveX() + { + } + } + + public static class Util { + public static int MAKELONG(int low, int high) { + return (high << 16) | (low & 0xffff); + } + + public static IntPtr MAKELPARAM(int low, int high) { + return (IntPtr) ((high << 16) | (low & 0xffff)); + } + + public static int HIWORD(int n) { + return (n >> 16) & 0xffff; + } + + public static int HIWORD(IntPtr n) { + return HIWORD( unchecked((int)(long)n) ); + } + + public static int LOWORD(int n) { + return n & 0xffff; + } + + public static int LOWORD(IntPtr n) { + return LOWORD( unchecked((int)(long)n) ); + } + + public static int SignedHIWORD(IntPtr n) { + return SignedHIWORD( unchecked((int)(long)n) ); + } + public static int SignedLOWORD(IntPtr n) { + return SignedLOWORD( unchecked((int)(long)n) ); + } + + public static int SignedHIWORD(int n) { + int i = (int)(short)((n >> 16) & 0xffff); + + return i; + } + + public static int SignedLOWORD(int n) { + int i = (int)(short)(n & 0xFFFF); + + return i; + } + + /// + /// + /// Computes the string size that should be passed to a typical Win32 call. + /// This will be the character count under NT, and the ubyte count for Windows 95. + /// + public static int GetPInvokeStringLength(String s) { + if (s == null) { + return 0; + } + + if (Marshal.SystemDefaultCharSize == 2) { + return s.Length; + } + else { + if (s.Length == 0) { + return 0; + } + if (s.IndexOf('\0') > -1) { + return GetEmbeddedNullStringLengthAnsi(s); + } + else { + return lstrlen(s); + } + } + } + + private static int GetEmbeddedNullStringLengthAnsi(String s) { + int n = s.IndexOf('\0'); + if (n > -1) { + String left = s.Substring(0, n); + String right = s.Substring(n+1); + return GetPInvokeStringLength(left) + GetEmbeddedNullStringLengthAnsi(right) + 1; + } + else { + return GetPInvokeStringLength(s); + } + } + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern int lstrlen(String s); + + /* Unused + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + internal static extern int RegisterWindowMessage(string msg); + */ + } + + public enum tagTYPEKIND { + TKIND_ENUM = 0, + TKIND_RECORD = 1, + TKIND_MODULE = 2, + TKIND_INTERFACE = 3, + TKIND_DISPATCH = 4, + TKIND_COCLASS = 5, + TKIND_ALIAS = 6, + TKIND_UNION = 7, + TKIND_MAX = 8 + } + + + + [StructLayout(LayoutKind.Sequential)] + public class tagTYPEDESC { + public IntPtr unionMember; + public short vt; + } + + [StructLayout(LayoutKind.Sequential)] + public struct tagPARAMDESC { + public IntPtr pparamdescex; + + [MarshalAs(UnmanagedType.U2)] + public short wParamFlags; + } + + public sealed class CommonHandles { + static CommonHandles() { +#if DEBUG + // Setup the DebugHandleTracker + System.Internal.DebugHandleTracker.Initialize(); + AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_DomainUnload); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); +#endif + } + + /// + /// + /// Handle type for accelerator tables. + /// + public static readonly int Accelerator = System.Internal.HandleCollector.RegisterType("Accelerator", 80, 50); + + /// + /// + /// handle type for cursors. + /// + public static readonly int Cursor = System.Internal.HandleCollector.RegisterType("Cursor", 20, 500); + + /// + /// + /// Handle type for enhanced metafiles. + /// + public static readonly int EMF = System.Internal.HandleCollector.RegisterType("EnhancedMetaFile", 20, 500); + + /// + /// + /// Handle type for file find handles. + /// + public static readonly int Find = System.Internal.HandleCollector.RegisterType("Find", 0, 1000); + + /// + /// + /// Handle type for GDI objects. + /// + public static readonly int GDI = System.Internal.HandleCollector.RegisterType("GDI", 50, 500); + + /// + /// + /// Handle type for HDC's that count against the Win98 limit of five DC's. HDC's + /// which are not scarce, such as HDC's for bitmaps, are counted as GDIHANDLE's. + /// + public static readonly int HDC = System.Internal.HandleCollector.RegisterType("HDC", 100, 2); // wait for 2 dc's before collecting + + /// + /// + /// Handle type for Compatible HDC's used for ToolStrips + /// + public static readonly int CompatibleHDC = System.Internal.HandleCollector.RegisterType("ComptibleHDC", 50, 50); // wait for 2 dc's before collecting + + /// + /// + /// Handle type for icons. + /// + public static readonly int Icon = System.Internal.HandleCollector.RegisterType("Icon", 20, 500); + + /// + /// + /// Handle type for kernel objects. + /// + public static readonly int Kernel = System.Internal.HandleCollector.RegisterType("Kernel", 0, 1000); + + /// + /// + /// Handle type for files. + /// + public static readonly int Menu = System.Internal.HandleCollector.RegisterType("Menu", 30, 1000); + + /// + /// + /// Handle type for windows. + /// + public static readonly int Window = System.Internal.HandleCollector.RegisterType("Window", 5, 1000); + +#if DEBUG + private static void CurrentDomain_DomainUnload(object sender, EventArgs e) { + System.Internal.DebugHandleTracker.CheckLeaks(); + } + + private static void CurrentDomain_ProcessExit(object sender, EventArgs e) { + System.Internal.DebugHandleTracker.CheckLeaks(); + } +#endif + } + + public enum tagSYSKIND { + SYS_WIN16 = 0, + SYS_MAC = 2 + } + + public delegate bool MonitorEnumProc(IntPtr monitor, IntPtr hdc, IntPtr lprcMonitor, IntPtr lParam); + + [ComImport(), Guid("A7ABA9C1-8983-11cf-8F20-00805F2CD064"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IProvideMultipleClassInfo { + // since the inheritance doesn't seem to work... + // these are from IProvideClassInfo & IProvideClassInfo2 + [PreserveSig] + UnsafeNativeMethods.ITypeInfo GetClassInfo(); + + [PreserveSig] + int GetGUID(int dwGuidKind, [In, Out] ref Guid pGuid); + + [PreserveSig] + int GetMultiTypeInfoCount([In, Out] ref int pcti); + + // we use arrays for most of these since we never use them anyway. + [PreserveSig] + int GetInfoOfIndex(int iti, int dwFlags, + [In, Out] + ref UnsafeNativeMethods.ITypeInfo pTypeInfo, + int pTIFlags, + int pcdispidReserved, + IntPtr piidPrimary, + IntPtr piidSource); + } + + [StructLayout(LayoutKind.Sequential)] + public class EVENTMSG { + public int message; + public int paramL; + public int paramH; + public int time; + public IntPtr hwnd; + } + + [ComImport(), Guid("B196B283-BAB4-101A-B69C-00AA00341D07"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IProvideClassInfo { + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.ITypeInfo GetClassInfo(); + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagTYPEATTR { + public Guid guid; + [MarshalAs(UnmanagedType.U4)] + public int lcid = 0; + [MarshalAs(UnmanagedType.U4)] + public int dwReserved = 0; + public int memidConstructor = 0; + public int memidDestructor = 0; + public IntPtr lpstrSchema = IntPtr.Zero; + [MarshalAs(UnmanagedType.U4)] + public int cbSizeInstance = 0; + public /*NativeMethods.tagTYPEKIND*/ int typekind = 0; + [MarshalAs(UnmanagedType.U2)] + public short cFuncs = 0; + [MarshalAs(UnmanagedType.U2)] + public short cVars = 0; + [MarshalAs(UnmanagedType.U2)] + public short cImplTypes = 0; + [MarshalAs(UnmanagedType.U2)] + public short cbSizeVft = 0; + [MarshalAs(UnmanagedType.U2)] + public short cbAlignment = 0; + [MarshalAs(UnmanagedType.U2)] + public short wTypeFlags = 0; + [MarshalAs(UnmanagedType.U2)] + public short wMajorVerNum = 0; + [MarshalAs(UnmanagedType.U2)] + public short wMinorVerNum = 0; + + // Microsoft these are inline too + //public NativeMethods.tagTYPEDESC tdescAlias; + [MarshalAs(UnmanagedType.U4)] + public int tdescAlias_unionMember = 0; + + [MarshalAs(UnmanagedType.U2)] + public short tdescAlias_vt = 0; + + //public NativeMethods.tagIDLDESC idldescType; + [MarshalAs(UnmanagedType.U4)] + public int idldescType_dwReserved = 0; + + [MarshalAs(UnmanagedType.U2)] + public short idldescType_wIDLFlags = 0; + + + public tagTYPEDESC Get_tdescAlias(){ + tagTYPEDESC td = new tagTYPEDESC(); + td.unionMember = (IntPtr)this.tdescAlias_unionMember; + td.vt = this.tdescAlias_vt; + return td; + } + + public tagIDLDESC Get_idldescType(){ + tagIDLDESC id = new tagIDLDESC(); + id.dwReserved = this.idldescType_dwReserved; + id.wIDLFlags = this.idldescType_wIDLFlags; + return id; + } + } + + public enum tagVARFLAGS { + VARFLAG_FREADONLY = 1, + VARFLAG_FSOURCE = 0x2, + VARFLAG_FBINDABLE = 0x4, + VARFLAG_FREQUESTEDIT = 0x8, + VARFLAG_FDISPLAYBIND = 0x10, + VARFLAG_FDEFAULTBIND = 0x20, + VARFLAG_FHIDDEN = 0x40, + VARFLAG_FDEFAULTCOLLELEM = 0x100, + VARFLAG_FUIDEFAULT = 0x200, + VARFLAG_FNONBROWSABLE = 0x400, + VARFLAG_FREPLACEABLE = 0x800, + VARFLAG_FIMMEDIATEBIND = 0x1000 + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagELEMDESC { + public NativeMethods.tagTYPEDESC tdesc = null; + public NativeMethods.tagPARAMDESC paramdesc; + } + + public enum tagVARKIND { + VAR_PERINSTANCE = 0, + VAR_STATIC = 1, + VAR_CONST = 2, + VAR_DISPATCH = 3 + } + + [StructLayout(LayoutKind.Sequential)] + public struct tagIDLDESC { + [MarshalAs(UnmanagedType.U4)] + public int dwReserved; + [MarshalAs(UnmanagedType.U2)] + public short wIDLFlags; + } + + public struct RGBQUAD { + public byte rgbBlue; + public byte rgbGreen; + public byte rgbRed; + public byte rgbReserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PALETTEENTRY { + public byte peRed; + public byte peGreen; + public byte peBlue; + public byte peFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct BITMAPINFO_FLAT { + public int bmiHeader_biSize;// = Marshal.SizeOf(typeof(BITMAPINFOHEADER)); + public int bmiHeader_biWidth; + public int bmiHeader_biHeight; + public short bmiHeader_biPlanes; + public short bmiHeader_biBitCount; + public int bmiHeader_biCompression; + public int bmiHeader_biSizeImage; + public int bmiHeader_biXPelsPerMeter; + public int bmiHeader_biYPelsPerMeter; + public int bmiHeader_biClrUsed; + public int bmiHeader_biClrImportant; + + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=BITMAPINFO_MAX_COLORSIZE*4)] + public byte[] bmiColors; // RGBQUAD structs... Blue-Green-Red-Reserved, repeat... + } + + /// + /// This method takes a file URL and converts it to a local path. The trick here is that + /// if there is a '#' in the path, everything after this is treated as a fragment. So + /// we need to append the fragment to the end of the path. + /// + internal static string GetLocalPath(string fileName) { + System.Diagnostics.Debug.Assert(fileName != null && fileName.Length > 0, "Cannot get local path, fileName is not valid"); + + Uri uri = new Uri(fileName); + return uri.LocalPath + uri.Fragment; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SYSTEM_POWER_STATUS { + public byte ACLineStatus; + public byte BatteryFlag; + public byte BatteryLifePercent; + public byte Reserved1; + public int BatteryLifeTime; + public int BatteryFullLifeTime; + } + + [StructLayout(LayoutKind.Sequential)] + internal class DLLVERSIONINFO + { + internal uint cbSize = 0; + internal uint dwMajorVersion = 0; + internal uint dwMinorVersion = 0; + internal uint dwBuildNumber = 0; + internal uint dwPlatformID = 0; + } + + public enum OLERENDER + { + OLERENDER_NONE = 0, + OLERENDER_DRAW = 1, + OLERENDER_FORMAT = 2, + OLERENDER_ASIS = 3 + } + + public enum PROCESS_DPI_AWARENESS + { + PROCESS_DPI_UNINITIALIZED = -1, + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 + } + + public enum MONTCALENDAR_VIEW_MODE + { + MCMV_MONTH = 0, + MCMV_YEAR = 1, + MCMV_DECADE = 2, + MCMV_CENTURY = 3 + } + + public const int DPI_AWARENESS_CONTEXT_UNAWARE = -1; + public const int DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2; + public const int DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3; + public const int DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4; + + // Theming/Visual Styles stuff + public const int STAP_ALLOW_NONCLIENT = (1 << 0); + public const int STAP_ALLOW_CONTROLS = (1 << 1); + public const int STAP_ALLOW_WEBCONTENT = (1 << 2); + + public const int PS_NULL = 5; + public const int PS_INSIDEFRAME = 6; + + public const int PS_GEOMETRIC = 0x00010000; + public const int PS_ENDCAP_SQUARE = 0x00000100; + + public const int NULL_BRUSH = 5; + public const int MM_HIMETRIC = 3; + + // Threading stuff + public const uint STILL_ACTIVE = 259; + + [StructLayout(LayoutKind.Sequential)] + public struct UiaRect { + public double left; + public double top; + public double width; + public double height; + + public UiaRect(System.Drawing.Rectangle r) { + this.left = r.Left; + this.top = r.Top; + this.width = r.Width; + this.height = r.Height; + } + } + + // UIAutomation IDs + // obtained from uiautomationclient.idl and uiautomationcore.idl + + // UIA_PatternIds + internal const int UIA_InvokePatternId = 10000; + internal const int UIA_SelectionPatternId = 10001; + internal const int UIA_ValuePatternId = 10002; + internal const int UIA_RangeValuePatternId = 10003; + internal const int UIA_ScrollPatternId = 10004; + internal const int UIA_ExpandCollapsePatternId = 10005; + internal const int UIA_GridPatternId = 10006; + internal const int UIA_GridItemPatternId = 10007; + internal const int UIA_MultipleViewPatternId = 10008; + internal const int UIA_WindowPatternId = 10009; + internal const int UIA_SelectionItemPatternId = 10010; + internal const int UIA_DockPatternId = 10011; + internal const int UIA_TablePatternId = 10012; + internal const int UIA_TableItemPatternId = 10013; + internal const int UIA_TextPatternId = 10014; + internal const int UIA_TogglePatternId = 10015; + internal const int UIA_TransformPatternId = 10016; + internal const int UIA_ScrollItemPatternId = 10017; + internal const int UIA_LegacyIAccessiblePatternId = 10018; + internal const int UIA_ItemContainerPatternId = 10019; + internal const int UIA_VirtualizedItemPatternId = 10020; + internal const int UIA_SynchronizedInputPatternId = 10021; + internal const int UIA_ObjectModelPatternId = 10022; + internal const int UIA_AnnotationPatternId = 10023; + internal const int UIA_TextPattern2Id = 10024; + internal const int UIA_StylesPatternId = 10025; + internal const int UIA_SpreadsheetPatternId = 10026; + internal const int UIA_SpreadsheetItemPatternId = 10027; + internal const int UIA_TransformPattern2Id = 10028; + internal const int UIA_TextChildPatternId = 10029; + internal const int UIA_DragPatternId = 10030; + internal const int UIA_DropTargetPatternId = 10031; + internal const int UIA_TextEditPatternId = 10032; + internal const int UIA_CustomNavigationPatternId = 10033; + + // UIA_EventIds + internal const int UIA_ToolTipOpenedEventId = 20000; + internal const int UIA_ToolTipClosedEventId = 20001; + internal const int UIA_StructureChangedEventId = 20002; + internal const int UIA_MenuOpenedEventId = 20003; + internal const int UIA_AutomationPropertyChangedEventId = 20004; + internal const int UIA_AutomationFocusChangedEventId = 20005; + internal const int UIA_AsyncContentLoadedEventId = 20006; + internal const int UIA_MenuClosedEventId = 20007; + internal const int UIA_LayoutInvalidatedEventId = 20008; + internal const int UIA_Invoke_InvokedEventId = 20009; + internal const int UIA_SelectionItem_ElementAddedToSelectionEventId = 20010; + internal const int UIA_SelectionItem_ElementRemovedFromSelectionEventId = 20011; + internal const int UIA_SelectionItem_ElementSelectedEventId = 20012; + internal const int UIA_Selection_InvalidatedEventId = 20013; + internal const int UIA_Text_TextSelectionChangedEventId = 20014; + internal const int UIA_Text_TextChangedEventId = 20015; + internal const int UIA_Window_WindowOpenedEventId = 20016; + internal const int UIA_Window_WindowClosedEventId = 20017; + internal const int UIA_MenuModeStartEventId = 20018; + internal const int UIA_MenuModeEndEventId = 20019; + internal const int UIA_InputReachedTargetEventId = 20020; + internal const int UIA_InputReachedOtherElementEventId = 20021; + internal const int UIA_InputDiscardedEventId = 20022; + internal const int UIA_SystemAlertEventId = 20023; + internal const int UIA_LiveRegionChangedEventId = 20024; + internal const int UIA_HostedFragmentRootsInvalidatedEventId = 20025; + internal const int UIA_Drag_DragStartEventId = 20026; + internal const int UIA_Drag_DragCancelEventId = 20027; + internal const int UIA_Drag_DragCompleteEventId = 20028; + internal const int UIA_DropTarget_DragEnterEventId = 20029; + internal const int UIA_DropTarget_DragLeaveEventId = 20030; + internal const int UIA_DropTarget_DroppedEventId = 20031; + internal const int UIA_TextEdit_TextChangedEventId = 20032; + internal const int UIA_TextEdit_ConversionTargetChangedEventId = 20033; + internal const int UIA_ChangesEventId = 20034; + + // UIAutomation PropertyIds + internal const int UIA_RuntimeIdPropertyId = 30000; + internal const int UIA_BoundingRectanglePropertyId = 30001; + internal const int UIA_ProcessIdPropertyId = 30002; + internal const int UIA_ControlTypePropertyId = 30003; + internal const int UIA_LocalizedControlTypePropertyId = 30004; + internal const int UIA_NamePropertyId = 30005; + internal const int UIA_AcceleratorKeyPropertyId = 30006; + internal const int UIA_AccessKeyPropertyId = 30007; + internal const int UIA_HasKeyboardFocusPropertyId = 30008; + internal const int UIA_IsKeyboardFocusablePropertyId = 30009; + internal const int UIA_IsEnabledPropertyId = 30010; + internal const int UIA_AutomationIdPropertyId = 30011; + internal const int UIA_ClassNamePropertyId = 30012; + internal const int UIA_HelpTextPropertyId = 30013; + internal const int UIA_ClickablePointPropertyId = 30014; + internal const int UIA_CulturePropertyId = 30015; + internal const int UIA_IsControlElementPropertyId = 30016; + internal const int UIA_IsContentElementPropertyId = 30017; + internal const int UIA_LabeledByPropertyId = 30018; + internal const int UIA_IsPasswordPropertyId = 30019; + internal const int UIA_NativeWindowHandlePropertyId = 30020; + internal const int UIA_ItemTypePropertyId = 30021; + internal const int UIA_IsOffscreenPropertyId = 30022; + internal const int UIA_OrientationPropertyId = 30023; + internal const int UIA_FrameworkIdPropertyId = 30024; + internal const int UIA_IsRequiredForFormPropertyId = 30025; + internal const int UIA_ItemStatusPropertyId = 30026; + internal const int UIA_IsDockPatternAvailablePropertyId = 30027; + internal const int UIA_IsExpandCollapsePatternAvailablePropertyId = 30028; + internal const int UIA_IsGridItemPatternAvailablePropertyId = 30029; + internal const int UIA_IsGridPatternAvailablePropertyId = 30030; + internal const int UIA_IsInvokePatternAvailablePropertyId = 30031; + internal const int UIA_IsMultipleViewPatternAvailablePropertyId = 30032; + internal const int UIA_IsRangeValuePatternAvailablePropertyId = 30033; + internal const int UIA_IsScrollPatternAvailablePropertyId = 30034; + internal const int UIA_IsScrollItemPatternAvailablePropertyId = 30035; + internal const int UIA_IsSelectionItemPatternAvailablePropertyId = 30036; + internal const int UIA_IsSelectionPatternAvailablePropertyId = 30037; + internal const int UIA_IsTablePatternAvailablePropertyId = 30038; + internal const int UIA_IsTableItemPatternAvailablePropertyId = 30039; + internal const int UIA_IsTextPatternAvailablePropertyId = 30040; + internal const int UIA_IsTogglePatternAvailablePropertyId = 30041; + internal const int UIA_IsTransformPatternAvailablePropertyId = 30042; + internal const int UIA_IsValuePatternAvailablePropertyId = 30043; + internal const int UIA_IsWindowPatternAvailablePropertyId = 30044; + internal const int UIA_ValueValuePropertyId = 30045; + internal const int UIA_ValueIsReadOnlyPropertyId = 30046; + internal const int UIA_RangeValueValuePropertyId = 30047; + internal const int UIA_RangeValueIsReadOnlyPropertyId = 30048; + internal const int UIA_RangeValueMinimumPropertyId = 30049; + internal const int UIA_RangeValueMaximumPropertyId = 30050; + internal const int UIA_RangeValueLargeChangePropertyId = 30051; + internal const int UIA_RangeValueSmallChangePropertyId = 30052; + internal const int UIA_ScrollHorizontalScrollPercentPropertyId = 30053; + internal const int UIA_ScrollHorizontalViewSizePropertyId = 30054; + internal const int UIA_ScrollVerticalScrollPercentPropertyId = 30055; + internal const int UIA_ScrollVerticalViewSizePropertyId = 30056; + internal const int UIA_ScrollHorizontallyScrollablePropertyId = 30057; + internal const int UIA_ScrollVerticallyScrollablePropertyId = 30058; + internal const int UIA_SelectionSelectionPropertyId = 30059; + internal const int UIA_SelectionCanSelectMultiplePropertyId = 30060; + internal const int UIA_SelectionIsSelectionRequiredPropertyId = 30061; + internal const int UIA_GridRowCountPropertyId = 30062; + internal const int UIA_GridColumnCountPropertyId = 30063; + internal const int UIA_GridItemRowPropertyId = 30064; + internal const int UIA_GridItemColumnPropertyId = 30065; + internal const int UIA_GridItemRowSpanPropertyId = 30066; + internal const int UIA_GridItemColumnSpanPropertyId = 30067; + internal const int UIA_GridItemContainingGridPropertyId = 30068; + internal const int UIA_DockDockPositionPropertyId = 30069; + internal const int UIA_ExpandCollapseExpandCollapseStatePropertyId = 30070; + internal const int UIA_MultipleViewCurrentViewPropertyId = 30071; + internal const int UIA_MultipleViewSupportedViewsPropertyId = 30072; + internal const int UIA_WindowCanMaximizePropertyId = 30073; + internal const int UIA_WindowCanMinimizePropertyId = 30074; + internal const int UIA_WindowWindowVisualStatePropertyId = 30075; + internal const int UIA_WindowWindowInteractionStatePropertyId = 30076; + internal const int UIA_WindowIsModalPropertyId = 30077; + internal const int UIA_WindowIsTopmostPropertyId = 30078; + internal const int UIA_SelectionItemIsSelectedPropertyId = 30079; + internal const int UIA_SelectionItemSelectionContainerPropertyId = 30080; + internal const int UIA_TableRowHeadersPropertyId = 30081; + internal const int UIA_TableColumnHeadersPropertyId = 30082; + internal const int UIA_TableRowOrColumnMajorPropertyId = 30083; + internal const int UIA_TableItemRowHeaderItemsPropertyId = 30084; + internal const int UIA_TableItemColumnHeaderItemsPropertyId = 30085; + internal const int UIA_ToggleToggleStatePropertyId = 30086; + internal const int UIA_TransformCanMovePropertyId = 30087; + internal const int UIA_TransformCanResizePropertyId = 30088; + internal const int UIA_TransformCanRotatePropertyId = 30089; + internal const int UIA_IsLegacyIAccessiblePatternAvailablePropertyId = 30090; + internal const int UIA_LegacyIAccessibleChildIdPropertyId = 30091; + internal const int UIA_LegacyIAccessibleNamePropertyId = 30092; + internal const int UIA_LegacyIAccessibleValuePropertyId = 30093; + internal const int UIA_LegacyIAccessibleDescriptionPropertyId = 30094; + internal const int UIA_LegacyIAccessibleRolePropertyId = 30095; + internal const int UIA_LegacyIAccessibleStatePropertyId = 30096; + internal const int UIA_LegacyIAccessibleHelpPropertyId = 30097; + internal const int UIA_LegacyIAccessibleKeyboardShortcutPropertyId = 30098; + internal const int UIA_LegacyIAccessibleSelectionPropertyId = 30099; + internal const int UIA_LegacyIAccessibleDefaultActionPropertyId = 30100; + internal const int UIA_AriaRolePropertyId = 30101; + internal const int UIA_AriaPropertiesPropertyId = 30102; + internal const int UIA_IsDataValidForFormPropertyId = 30103; + internal const int UIA_ControllerForPropertyId = 30104; + internal const int UIA_DescribedByPropertyId = 30105; + internal const int UIA_FlowsToPropertyId = 30106; + internal const int UIA_ProviderDescriptionPropertyId = 30107; + internal const int UIA_IsItemContainerPatternAvailablePropertyId = 30108; + internal const int UIA_IsVirtualizedItemPatternAvailablePropertyId = 30109; + internal const int UIA_IsSynchronizedInputPatternAvailablePropertyId = 30110; + internal const int UIA_OptimizeForVisualContentPropertyId = 30111; + internal const int UIA_IsObjectModelPatternAvailablePropertyId = 30112; + internal const int UIA_AnnotationAnnotationTypeIdPropertyId = 30113; + internal const int UIA_AnnotationAnnotationTypeNamePropertyId = 30114; + internal const int UIA_AnnotationAuthorPropertyId = 30115; + internal const int UIA_AnnotationDateTimePropertyId = 30116; + internal const int UIA_AnnotationTargetPropertyId = 30117; + internal const int UIA_IsAnnotationPatternAvailablePropertyId = 30118; + internal const int UIA_IsTextPattern2AvailablePropertyId = 30119; + internal const int UIA_StylesStyleIdPropertyId = 30120; + internal const int UIA_StylesStyleNamePropertyId = 30121; + internal const int UIA_StylesFillColorPropertyId = 30122; + internal const int UIA_StylesFillPatternStylePropertyId = 30123; + internal const int UIA_StylesShapePropertyId = 30124; + internal const int UIA_StylesFillPatternColorPropertyId = 30125; + internal const int UIA_StylesExtendedPropertiesPropertyId = 30126; + internal const int UIA_IsStylesPatternAvailablePropertyId = 30127; + internal const int UIA_IsSpreadsheetPatternAvailablePropertyId = 30128; + internal const int UIA_SpreadsheetItemFormulaPropertyId = 30129; + internal const int UIA_SpreadsheetItemAnnotationObjectsPropertyId = 30130; + internal const int UIA_SpreadsheetItemAnnotationTypesPropertyId = 30131; + internal const int UIA_IsSpreadsheetItemPatternAvailablePropertyId = 30132; + internal const int UIA_Transform2CanZoomPropertyId = 30133; + internal const int UIA_IsTransformPattern2AvailablePropertyId = 30134; + internal const int UIA_LiveSettingPropertyId = 30135; + internal const int UIA_IsTextChildPatternAvailablePropertyId = 30136; + internal const int UIA_IsDragPatternAvailablePropertyId = 30137; + internal const int UIA_DragIsGrabbedPropertyId = 30138; + internal const int UIA_DragDropEffectPropertyId = 30139; + internal const int UIA_DragDropEffectsPropertyId = 30140; + internal const int UIA_IsDropTargetPatternAvailablePropertyId = 30141; + internal const int UIA_DropTargetDropTargetEffectPropertyId = 30142; + internal const int UIA_DropTargetDropTargetEffectsPropertyId = 30143; + internal const int UIA_DragGrabbedItemsPropertyId = 30144; + internal const int UIA_Transform2ZoomLevelPropertyId = 30145; + internal const int UIA_Transform2ZoomMinimumPropertyId = 30146; + internal const int UIA_Transform2ZoomMaximumPropertyId = 30147; + internal const int UIA_FlowsFromPropertyId = 30148; + internal const int UIA_IsTextEditPatternAvailablePropertyId = 30149; + internal const int UIA_IsPeripheralPropertyId = 30150; + internal const int UIA_IsCustomNavigationPatternAvailablePropertyId = 30151; + internal const int UIA_PositionInSetPropertyId = 30152; + internal const int UIA_SizeOfSetPropertyId = 30153; + internal const int UIA_LevelPropertyId = 30154; + internal const int UIA_AnnotationTypesPropertyId = 30155; + internal const int UIA_AnnotationObjectsPropertyId = 30156; + internal const int UIA_LandmarkTypePropertyId = 30157; + internal const int UIA_LocalizedLandmarkTypePropertyId = 30158; + internal const int UIA_FullDescriptionPropertyId = 30159; + internal const int UIA_FillColorPropertyId = 30160; + internal const int UIA_OutlineColorPropertyId = 30161; + internal const int UIA_FillTypePropertyId = 30162; + internal const int UIA_VisualEffectsPropertyId = 30163; + internal const int UIA_OutlineThicknessPropertyId = 30164; + internal const int UIA_CenterPointPropertyId = 30165; + internal const int UIA_RotationPropertyId = 30166; + internal const int UIA_SizePropertyId = 30167; + + // UIA_ControlTypeIds + internal const int UIA_ButtonControlTypeId = 50000; + internal const int UIA_CalendarControlTypeId = 50001; + internal const int UIA_CheckBoxControlTypeId = 50002; + internal const int UIA_ComboBoxControlTypeId = 50003; + internal const int UIA_EditControlTypeId = 50004; + internal const int UIA_HyperlinkControlTypeId = 50005; + internal const int UIA_ImageControlTypeId = 50006; + internal const int UIA_ListItemControlTypeId = 50007; + internal const int UIA_ListControlTypeId = 50008; + internal const int UIA_MenuControlTypeId = 50009; + internal const int UIA_MenuBarControlTypeId = 50010; + internal const int UIA_MenuItemControlTypeId = 50011; + internal const int UIA_ProgressBarControlTypeId = 50012; + internal const int UIA_RadioButtonControlTypeId = 50013; + internal const int UIA_ScrollBarControlTypeId = 50014; + internal const int UIA_SliderControlTypeId = 50015; + internal const int UIA_SpinnerControlTypeId = 50016; + internal const int UIA_StatusBarControlTypeId = 50017; + internal const int UIA_TabControlTypeId = 50018; + internal const int UIA_TabItemControlTypeId = 50019; + internal const int UIA_TextControlTypeId = 50020; + internal const int UIA_ToolBarControlTypeId = 50021; + internal const int UIA_ToolTipControlTypeId = 50022; + internal const int UIA_TreeControlTypeId = 50023; + internal const int UIA_TreeItemControlTypeId = 50024; + internal const int UIA_CustomControlTypeId = 50025; + internal const int UIA_GroupControlTypeId = 50026; + internal const int UIA_ThumbControlTypeId = 50027; + internal const int UIA_DataGridControlTypeId = 50028; + internal const int UIA_DataItemControlTypeId = 50029; + internal const int UIA_DocumentControlTypeId = 50030; + internal const int UIA_SplitButtonControlTypeId = 50031; + internal const int UIA_WindowControlTypeId = 50032; + internal const int UIA_PaneControlTypeId = 50033; + internal const int UIA_HeaderControlTypeId = 50034; + internal const int UIA_HeaderItemControlTypeId = 50035; + internal const int UIA_TableControlTypeId = 50036; + internal const int UIA_TitleBarControlTypeId = 50037; + internal const int UIA_SeparatorControlTypeId = 50038; + internal const int UIA_SemanticZoomControlTypeId = 50039; + internal const int UIA_AppBarControlTypeId = 50040; + + // If this value is used, %windows%\system32 is searched for the DLL + // and its dependencies. Directories in the standard search path are not searched. + // Windows7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: + // This value requires KB2533623 to be installed. + // Windows Server 2003 and Windows XP: This value is not supported. + internal const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/NativeWindow.cs b/WindowsForms/Managed/System/WinForms/NativeWindow.cs new file mode 100644 index 000000000..37f95e656 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NativeWindow.cs @@ -0,0 +1,1697 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Threading; + using System.Configuration.Assemblies; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Runtime.ConstrainedExecution; + using System.Runtime.CompilerServices; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + using System.Diagnostics; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using Microsoft.Win32; + using System.Text; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Versioning; + + /// + /// + /// + /// Provides a low-level encapsulation of a window handle + /// and a window procedure. The class automatically manages window class creation and registration. + /// + /// + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) + ] + public class NativeWindow : MarshalByRefObject, IWin32Window { +#if DEBUG + private static readonly BooleanSwitch AlwaysUseNormalWndProc = new BooleanSwitch("AlwaysUseNormalWndProc", "Skips checking for the debugger when choosing the debuggable WndProc handler"); + private static readonly TraceSwitch WndProcChoice = new TraceSwitch("WndProcChoice", "Info about choice of WndProc"); +#else + private static readonly TraceSwitch WndProcChoice; +#endif + + /** + * Table of prime numbers to use as hash table sizes. Each entry is the + * smallest prime number larger than twice the previous entry. + */ + private readonly static int[] primes = { + 11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919, + 1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591, + 17519,21023,25229,30293,36353,43627,52361,62851,75431,90523, 108631, 130363, + 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, + 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, + 4999559, 5999471, 7199369 + }; + + const int InitializedFlags = 0x01; + const int DebuggerPresent = 0x02; + const int UseDebuggableWndProc = 0x04; + const int LoadConfigSettings = 0x08; + const int AssemblyIsDebuggable = 0x10; + + // do we have any active HWNDs? + // + [ThreadStatic] + static bool anyHandleCreated; + static bool anyHandleCreatedInApp; + + const float hashLoadFactor = .72F; + + private static int handleCount; + private static int hashLoadSize; + private static HandleBucket[] hashBuckets; + private static IntPtr userDefWindowProc; + [ThreadStatic] + private static byte wndProcFlags = 0; + [ThreadStatic] + private static byte userSetProcFlags = 0; + private static byte userSetProcFlagsForApp; + + //nned to Store Table of Ids and Handles + private static short globalID = 1; + private static Dictionary hashForIdHandle; + private static Dictionary hashForHandleId; + private static object internalSyncObject = new object(); + private static object createWindowSyncObject = new object(); + +#if DEBUG + AppDomain handleCreatedIn = null; + string subclassStatus = "None"; +#endif + IntPtr handle; + NativeMethods.WndProc windowProc; + IntPtr windowProcPtr; + IntPtr defWindowProc; + bool suppressedGC; + bool ownHandle; + NativeWindow previousWindow; // doubly linked list of subclasses. + NativeWindow nextWindow; + WeakReference weakThisPtr; + private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.EnableDpiChangedHighDpiImprovements ? + CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : + DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + + static NativeWindow() { + EventHandler shutdownHandler = new EventHandler(OnShutdown); + AppDomain.CurrentDomain.ProcessExit += shutdownHandler; + AppDomain.CurrentDomain.DomainUnload += shutdownHandler; + + // Initialize our static hash of handles. I have chosen + // a prime bucket based on a typical number of window handles + // needed to start up a decent sized app. + int hashSize = primes[4]; + hashBuckets = new HandleBucket[hashSize]; + + hashLoadSize = (int)(hashLoadFactor * hashSize); + if (hashLoadSize >= hashSize) { + hashLoadSize = hashSize - 1; + } + + //Intilialize the Hashtable for Id... + hashForIdHandle = new Dictionary(); + hashForHandleId = new Dictionary(); + } + + public NativeWindow() { + weakThisPtr = new WeakReference(this); + } + + /// + /// Cache window DpiContext awareness information that helps to create handle with right context at the later time. + /// + internal DpiAwarenessContext DpiAwarenessContext { + get { + return windowDpiAwarenessContext; + } + } + + /// + /// + /// Override's the base object's finalize method. + /// + ~NativeWindow() { + ForceExitMessageLoop(); + } + + /// + /// This was factored into another function so the finalizer in control that releases the window + /// can perform the exact same code without further changes. If you make changes to the finalizer, + /// change this method -- try not to change NativeWindow's finalizer. + /// + internal void ForceExitMessageLoop() { + IntPtr h; + bool ownedHandle; + + // + lock (this) { + h = handle; + ownedHandle = ownHandle; + } + + if (handle != IntPtr.Zero) { + //now, before we set handle to zero and finish the finalizer, let's send + // a WM_NULL to the window. Why? Because if the main ui thread is INSIDE + // the wndproc for this control during our unsubclass, then we could AV + // when control finally reaches us. + if (UnsafeNativeMethods.IsWindow(new HandleRef(null, handle))) { + int lpdwProcessId; //unused + int id = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, handle), out lpdwProcessId); + Application.ThreadContext ctx = Application.ThreadContext.FromId(id); + IntPtr threadHandle = (ctx == null ? IntPtr.Zero : ctx.GetHandle()); + + if (threadHandle != IntPtr.Zero) { + int exitCode = 0; + SafeNativeMethods.GetExitCodeThread(new HandleRef(null, threadHandle), out exitCode); + if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && exitCode == NativeMethods.STATUS_PENDING) { + IntPtr result; + if (UnsafeNativeMethods.SendMessageTimeout(new HandleRef(null, handle), + NativeMethods.WM_UIUNSUBCLASS, IntPtr.Zero, IntPtr.Zero, + UnsafeNativeMethods.SMTO_ABORTIFHUNG, 100, out result) == IntPtr.Zero) { + + //Debug.Fail("unable to ping HWND:" + handle.ToString() + " during finalization"); + } + } + } + } + if (handle != IntPtr.Zero) { + //if the dest thread is gone, it should be safe to unsubclass here. + ReleaseHandle(true); + } + } + + if (h != IntPtr.Zero && ownedHandle) { + // If we owned the handle, post a + // WM_CLOSE to get rid of it. + // + UnsafeNativeMethods.PostMessage(new HandleRef(this, h), NativeMethods.WM_CLOSE, 0, 0); + } + } + + /// + /// Indicates whether a window handle was created & is being tracked. + /// + internal static bool AnyHandleCreated { + get { + return anyHandleCreated; + } + } + + /// + /// + /// + /// Gets the handle for this window. + /// + /// + public IntPtr Handle { + get { +#if DEBUG + Debug.Assert(handle == IntPtr.Zero || (handleCreatedIn != null && handleCreatedIn == AppDomain.CurrentDomain), + "Attempt to access a handle created in a different domain"); + + // We cannot assert this here. Consider the case where someone has created an HWND on a thread but + // never started a message pump. When the thread is cleaned up by the OS USER will destroy all window + // handles. This happens long before we finalize, so when finalization does come along and we + // try to check to see if we should PostMessage(WM_CLOSE, this assert would fire. + // + // Debug.Assert(handle == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(this, handle)), + // "Attempt to access a non-valid handle "); +#endif + return handle; + } + } + + /// + /// This returns the previous NativeWindow in the chain of subclasses. + /// Generally it returns null, but if someone has subclassed a control + /// through the use of a NativeWindow class, this will return the + /// previous NativeWindow subclass. + /// + /// This should be public, but it is way too late for that. + /// + internal NativeWindow PreviousWindow { + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + get { + return previousWindow; + } + } + + /// + /// Helper method that returns a static value for the + /// unmanaged DefWindowProc call. + /// + internal static IntPtr UserDefindowProc { + get { + return userDefWindowProc; + } + } + + private static int WndProcFlags { + get { + // upcast for easy bit masking... + // + int intWndProcFlags = wndProcFlags; + + // Check to see if a debugger is installed. If there is, then use + // DebuggableCallback instead; this callback has no try/catch around it + // so exceptions go to the debugger. + // + if (intWndProcFlags == 0) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Init wndProcFlags"); + Debug.Indent(); + + if (userSetProcFlags != 0) { + intWndProcFlags = userSetProcFlags; + } + else if (userSetProcFlagsForApp != 0) { + intWndProcFlags = userSetProcFlagsForApp; + } + else if (!Application.CustomThreadExceptionHandlerAttached) { + if (Debugger.IsAttached) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger is attached, using debuggable WndProc"); + intWndProcFlags |= UseDebuggableWndProc; + } + else { + intWndProcFlags = AdjustWndProcFlagsFromRegistry(intWndProcFlags); + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After reg check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture)); + if ((intWndProcFlags & DebuggerPresent) != 0) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger present"); + + intWndProcFlags = AdjustWndProcFlagsFromMetadata(intWndProcFlags); + + if ((intWndProcFlags & AssemblyIsDebuggable) != 0) { + if ((intWndProcFlags & LoadConfigSettings) != 0) { + intWndProcFlags = AdjustWndProcFlagsFromConfig(intWndProcFlags); + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After config check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture)); + } + else { + intWndProcFlags |= UseDebuggableWndProc; + } + } + } + + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After config & debugger check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture)); + } + } + +#if DEBUG + if (AlwaysUseNormalWndProc.Enabled) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Stripping debuggablewndproc due to AlwaysUseNormalWndProc switch"); + intWndProcFlags &= ~UseDebuggableWndProc; + } +#endif + intWndProcFlags |= InitializedFlags; + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Final 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture)); + wndProcFlags = (byte)intWndProcFlags; + Debug.Unindent(); + } + + return intWndProcFlags; + } + } + + internal static bool WndProcShouldBeDebuggable { + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + get { + return (WndProcFlags & UseDebuggableWndProc) != 0; + } + } + + /// + /// Inserts an entry into this hashtable. + /// + private static void AddWindowToTable(IntPtr handle, NativeWindow window) { + + Debug.Assert(handle != IntPtr.Zero, "Should never insert a zero handle into the hash"); + + lock (internalSyncObject) { + + if (handleCount >= hashLoadSize) { + ExpandTable(); + } + + // set a flag that this thread is tracking an HWND + // + anyHandleCreated = true; + // ...same for the application + anyHandleCreatedInApp = true; + + uint seed; + uint incr; + // Assume we only have one thread writing concurrently. Modify + // buckets to contain new data, as long as we insert in the right order. + uint hashcode = InitHash(handle, hashBuckets.Length, out seed, out incr); + + int ntry = 0; + int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots + // create by remove that have the collision bit set over using up new slots. + + GCHandle root = GCHandle.Alloc(window, GCHandleType.Weak); + + do { + int bucketNumber = (int)(seed % (uint)hashBuckets.Length); + + if (emptySlotNumber == -1 && (hashBuckets[bucketNumber].handle == new IntPtr(-1)) && (hashBuckets[bucketNumber].hash_coll < 0)) + emptySlotNumber = bucketNumber; + + //We need to check if the collision bit is set because we have the possibility where the first + //item in the hash-chain has been deleted. + if ((hashBuckets[bucketNumber].handle == IntPtr.Zero) || + (hashBuckets[bucketNumber].handle == new IntPtr(-1) && ((hashBuckets[bucketNumber].hash_coll & unchecked(0x80000000)) == 0))) { + + if (emptySlotNumber != -1) { // Reuse slot + bucketNumber = emptySlotNumber; + } + + // Always set the hash_coll last because there may be readers + // reading the table right now on other threads. + hashBuckets[bucketNumber].window = root; + hashBuckets[bucketNumber].handle = handle; +#if DEBUG + hashBuckets[bucketNumber].owner = window.ToString(); +#endif + hashBuckets[bucketNumber].hash_coll |= (int)hashcode; + handleCount++; + return; + } + + // If there is an existing window in this slot, reuse it. Be sure to hook up the previous and next + // window pointers so we can get back to the right window. + // + if (((hashBuckets[bucketNumber].hash_coll & 0x7FFFFFFF) == hashcode) && handle == hashBuckets[bucketNumber].handle) { + GCHandle prevWindow = hashBuckets[bucketNumber].window; + if (prevWindow.IsAllocated) { + if (prevWindow.Target != null) { + window.previousWindow = ((NativeWindow)prevWindow.Target); + Debug.Assert(window.previousWindow.nextWindow == null, "Last window in chain should have null next ptr"); + window.previousWindow.nextWindow = window; + } + prevWindow.Free(); + } + hashBuckets[bucketNumber].window = root; +#if DEBUG + string ownerString = string.Empty; + NativeWindow w = window; + while(w != null) { + ownerString += ("->" + w.ToString()); + w = w.previousWindow; + } + hashBuckets[bucketNumber].owner = ownerString; +#endif + return; + } + + if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot + hashBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000); + } + + seed += incr; + + } while (++ntry < hashBuckets.Length); + + if (emptySlotNumber != -1) { + // Always set the hash_coll last because there may be readers + // reading the table right now on other threads. + hashBuckets[emptySlotNumber].window = root; + hashBuckets[emptySlotNumber].handle = handle; +#if DEBUG + hashBuckets[emptySlotNumber].owner = window.ToString(); +#endif + hashBuckets[emptySlotNumber].hash_coll |= (int)hashcode; + handleCount++; + return; + } + } + + // If you see this assert, make sure load factor & count are reasonable. + // Then verify that our double hash function (h2, described at top of file) + // meets the requirements described above. You should never see this assert. + Debug.Fail("native window hash table insert failed! Load factor too high, or our double hashing function is incorrect."); + } + + /// + /// Inserts an entry into this ID hashtable. + /// + internal static void AddWindowToIDTable(object wrapper, IntPtr handle) { + NativeWindow.hashForIdHandle[NativeWindow.globalID] = handle; + NativeWindow.hashForHandleId[handle] = NativeWindow.globalID; + UnsafeNativeMethods.SetWindowLong(new HandleRef(wrapper, handle), NativeMethods.GWL_ID, new HandleRef(wrapper, (IntPtr)globalID)); + globalID++; + + } + + // disable inlining optimization - VSW 454165 + // This method introduces a dependency on System.Configuration.dll + // due to its usage of the type WindowsFormsSection + // + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private static int AdjustWndProcFlagsFromConfig(int wndProcFlags) { + if (WindowsFormsSection.GetSection().JitDebugging) { + wndProcFlags |= UseDebuggableWndProc; + } + return wndProcFlags; + } + + private static int AdjustWndProcFlagsFromRegistry(int wndProcFlags) { + // This is the enum used to define the meaning of the debug flag... + // + + /* + // + // We split the value of DbgJITDebugLaunchSetting between the value for whether or not to ask the user and between a + // mask of places to ask. The places to ask are specified in the UnhandledExceptionLocation enum in excep.h. + // + enum DebuggerLaunchSetting + { + DLS_ASK_USER = 0x00000000, + DLS_TERMINATE_APP = 0x00000001, + DLS_ATTACH_DEBUGGER = 0x00000002, + DLS_QUESTION_MASK = 0x0000000F, + DLS_ASK_WHEN_SERVICE = 0x00000010, + DLS_MODIFIER_MASK = 0x000000F0, + DLS_LOCATION_MASK = 0xFFFFFF00, + DLS_LOCATION_SHIFT = 8 // Shift right 8 bits to get a UnhandledExceptionLocation value from the location part. + }; + */ + + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try { + + Debug.Assert(wndProcFlags == 0x00, "Re-entrancy into IsDebuggerInstalled()"); + + RegistryKey debugKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\.NETFramework"); + if (debugKey == null) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, ".NETFramework key not found"); + return wndProcFlags; + } + try { + object value = debugKey.GetValue("DbgJITDebugLaunchSetting"); + if (value != null) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "DbgJITDebugLaunchSetting value found, debugger is installed"); + int dbgJit = 0; + try { + dbgJit = (int)value; + } + catch (InvalidCastException) { + // If the value isn't a DWORD, then we will + // continue to use the non-debuggable wndproc + // + dbgJit = 1; + } + + // From the enum above, 0x01 == "Terminate App"... for + // anything else, we should flag that the debugger + // will catch unhandled exceptions + // + if (dbgJit != 1) { + wndProcFlags |= DebuggerPresent; + wndProcFlags |= LoadConfigSettings; + } + } + else if (debugKey.GetValue("DbgManagedDebugger") != null) { + //if there is a debugger installed, check the config files to decide + //whether to allow JIT debugging. + wndProcFlags |= DebuggerPresent; + wndProcFlags |= LoadConfigSettings; + } + } + finally { + debugKey.Close(); + } + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + return wndProcFlags; + } + + static int AdjustWndProcFlagsFromMetadata(int wndProcFlags) { + if ((wndProcFlags & DebuggerPresent) != 0) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger present, checking for debuggable entry assembly"); + Assembly entry = Assembly.GetEntryAssembly(); + if (entry != null) { + if (Attribute.IsDefined(entry, typeof(DebuggableAttribute))) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debuggable attribute on assembly"); + Attribute[] attr = Attribute.GetCustomAttributes(entry, typeof(DebuggableAttribute)); + if (attr.Length > 0) { + DebuggableAttribute dbg = (DebuggableAttribute)attr[0]; + if (dbg.IsJITTrackingEnabled) { + // Only if the assembly is really setup for debugging + // does it really make sense to enable the jitDebugging + // + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Entry assembly is debuggable"); + wndProcFlags |= AssemblyIsDebuggable; + } + } + } + } + } + return wndProcFlags; + } + + /// + /// + /// + /// Assigns a handle to this + /// window. + /// + /// + public void AssignHandle(IntPtr handle) { + AssignHandle(handle, true); + } + + // + + + + + internal void AssignHandle(IntPtr handle, bool assignUniqueID) { + // + lock (this) { + CheckReleased(); + Debug.Assert(handle != IntPtr.Zero, "handle is 0"); +#if DEBUG + handleCreatedIn = AppDomain.CurrentDomain; +#endif + this.handle = handle; + + // Synchronizing the handle DPiAwarenessContext with control DpiAwarenessContext. They can be out of sync in case control has a parent set before the handle was created. + if (DpiHelper.EnableDpiChangedHighDpiImprovements && windowDpiAwarenessContext != DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { + var handleDpiContext = CommonUnsafeNativeMethods.TryGetDpiAwarenessContextForWindow(this.handle); + if (handleDpiContext!= DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && !CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(windowDpiAwarenessContext, handleDpiContext)) { + windowDpiAwarenessContext = handleDpiContext; + } + } + + if (userDefWindowProc == IntPtr.Zero) { + string defproc = (Marshal.SystemDefaultCharSize == 1 ? "DefWindowProcA" : "DefWindowProcW"); + + userDefWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle("user32.dll")), defproc); + if (userDefWindowProc == IntPtr.Zero) { + throw new Win32Exception(); + } + } + + defWindowProc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); + Debug.Assert(defWindowProc != IntPtr.Zero, "defWindowProc is 0"); + + + if (WndProcShouldBeDebuggable) { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Using debuggable wndproc"); + windowProc = new NativeMethods.WndProc(this.DebuggableCallback); + } + else { + Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Using normal wndproc"); + windowProc = new NativeMethods.WndProc(this.Callback); + } + + AddWindowToTable(handle, this); + + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC, windowProc); + windowProcPtr = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); + Debug.Assert(defWindowProc != windowProcPtr, "Uh oh! Subclassed ourselves!!!"); + if (assignUniqueID && + (unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_STYLE))) & NativeMethods.WS_CHILD) != 0 && + unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_ID))) == 0) { + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_ID, new HandleRef(this, handle)); + } + + + if (suppressedGC) { + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + try { + GC.ReRegisterForFinalize(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + suppressedGC = false; + } + + OnHandleChange(); + } + } + + /// + /// + /// Window message callback method. Control arrives here when a window + /// message is sent to this Window. This method packages the window message + /// in a Message object and invokes the wndProc() method. A WM_NCDESTROY + /// message automatically causes the releaseHandle() method to be called. + /// + /// + private IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + + // Note: if you change this code be sure to change the + // corresponding code in DebuggableCallback below! + + Message m = Message.Create(hWnd, msg, wparam, lparam); + + try { + if (weakThisPtr.IsAlive && weakThisPtr.Target != null) { + WndProc(ref m); + } + else { + DefWndProc(ref m); + } + } + catch (Exception e) { + OnThreadException(e); + } + finally { + if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false); + if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true); + } + + return m.Result; + } + + /// + /// + /// Raises an exception if the window handle is not zero. + /// + /// + private void CheckReleased() { + if (handle != IntPtr.Zero) { + throw new InvalidOperationException(SR.GetString(SR.HandleAlreadyExists)); + } + } + + /// + /// + /// + /// Creates a window handle for this + /// window. + /// + /// + [ + // The call to UnsafeNativeMethods.CreateWindowEx is actually an interop call... + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke") + ] + public virtual void CreateHandle(CreateParams cp) { + + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "CreateAnyWindow Demanded"); + IntSecurity.CreateAnyWindow.Demand(); + + if ((cp.Style & NativeMethods.WS_CHILD) != NativeMethods.WS_CHILD + || cp.Parent == IntPtr.Zero) { + + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "TopLevelWindow Demanded"); + IntSecurity.TopLevelWindow.Demand(); + } + + // + lock (this) { + CheckReleased(); + WindowClass windowClass = WindowClass.Create(cp.ClassName, cp.ClassStyle); + lock (createWindowSyncObject) { + + // VSWhidbey 528778 + // Why are we checking the handle again after calling CheckReleased()? It turns + // out the CLR will sometimes pump messages while we're waiting on the lock. If + // a message comes through (say a WM_ACTIVATE for the parent) which causes the + // handle to be created, we can try to create the handle twice for the same + // NativeWindow object. See bug for more details. + + if (this.handle != IntPtr.Zero) { + return; + } + windowClass.targetWindow = this; + IntPtr createResult = IntPtr.Zero; + int lastWin32Error = 0; + + // Parking window dpi awarness context need to match with dpi awarenss context of control being + // parented to this parkign window. Otherwise, reparenting of control will fail. + using (DpiHelper.EnterDpiAwarenessScope(this.windowDpiAwarenessContext)) { + IntPtr modHandle = UnsafeNativeMethods.GetModuleHandle(null); + + // Win98 apparently doesn't believe in returning E_OUTOFMEMORY. They'd much + // rather just AV. So we catch this and then we re-throw an out of memory error. + // + try { + + //(bug 109840) + //CreateWindowEx() is throwing because we're passing the WindowText arg with a string of length > 32767. + //It looks like the Windows call (CreateWindowEx) will only work + //for string lengths no greater than the max length of a 16 bit int (32767). + + //We need to check the length of the string we're passing into CreateWindowEx(). + //If it exceeds the max, we should take the substring.... + + if (cp.Caption != null && cp.Caption.Length > Int16.MaxValue) { + cp.Caption = cp.Caption.Substring(0, Int16.MaxValue); + } + + createResult = UnsafeNativeMethods.CreateWindowEx(cp.ExStyle, windowClass.windowClassName, + cp.Caption, cp.Style, cp.X, cp.Y, cp.Width, cp.Height, new HandleRef(cp, cp.Parent), NativeMethods.NullHandleRef, + new HandleRef(null, modHandle), cp.Param); + + lastWin32Error = Marshal.GetLastWin32Error(); + } + catch (NullReferenceException e) { + throw new OutOfMemoryException(SR.GetString(SR.ErrorCreatingHandle), e); + } + } + windowClass.targetWindow = null; + + Debug.WriteLineIf(CoreSwitches.PerfTrack.Enabled, "Handle created of type '" + cp.ClassName + "' with caption '" + cp.Caption + "' from NativeWindow of type '" + GetType().FullName + "'"); + + if (createResult == IntPtr.Zero) { + throw new Win32Exception(lastWin32Error, SR.GetString(SR.ErrorCreatingHandle)); + } + ownHandle = true; + System.Internal.HandleCollector.Add(createResult, NativeMethods.CommonHandles.Window); + } + } + } + + /// + /// + /// Window message callback method. Control arrives here when a window + /// message is sent to this Window. This method packages the window message + /// in a Message object and invokes the wndProc() method. A WM_NCDESTROY + /// message automatically causes the releaseHandle() method to be called. + /// + /// + private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + + // Note: if you change this code be sure to change the + // corresponding code in Callback above! + + Message m = Message.Create(hWnd, msg, wparam, lparam); + + try { + if (weakThisPtr.IsAlive && weakThisPtr.Target != null) { + WndProc(ref m); + } + else { + DefWndProc(ref m); + } + } + finally { + if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false); + if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true); + } + + return m.Result; + } + + /// + /// + /// Invokes the default window procedure associated with this Window. It is + /// an error to call this method when the Handle property is zero. + /// + public void DefWndProc(ref Message m) { + if (previousWindow == null) { + if (defWindowProc == IntPtr.Zero) { + +#if DEBUG + Debug.Fail("Can't find a default window procedure for message " + m.ToString() + " on class " + GetType().Name + " subclass status: " + subclassStatus); +#endif + + // At this point, there isn't much we can do. There's a + // small chance the following line will allow the rest of + // the program to run, but don't get your hopes up. + m.Result = UnsafeNativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); + return; + } + m.Result = UnsafeNativeMethods.CallWindowProc(defWindowProc, m.HWnd, m.Msg, m.WParam, m.LParam); + } + else { + Debug.Assert(previousWindow != this, "Looping in our linked list"); + m.Result = previousWindow.Callback(m.HWnd, m.Msg, m.WParam, m.LParam); + } + } + + /// + /// + /// + /// Destroys the + /// handle associated with this window. + /// + /// + public virtual void DestroyHandle() { + // + lock (this) { + if (handle != IntPtr.Zero) { + if (!UnsafeNativeMethods.DestroyWindow(new HandleRef(this, handle))) { + UnSubclass(); + //then post a close and let it do whatever it needs to do on its own. + UnsafeNativeMethods.PostMessage(new HandleRef(this, handle), NativeMethods.WM_CLOSE, 0, 0); + } + handle = IntPtr.Zero; + ownHandle = false; + } + + // Now that we have disposed, there is no need to finalize us any more. So + // Mark to the garbage collector that we no longer need finalization. + // + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + try { + GC.SuppressFinalize(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + suppressedGC = true; + } + } + + /// + /// Increases the bucket count of this hashtable. This method is called from + /// the Insert method when the actual load factor of the hashtable reaches + /// the upper limit specified when the hashtable was constructed. The number + /// of buckets in the hashtable is increased to the smallest prime number + /// that is larger than twice the current number of buckets, and the entries + /// in the hashtable are redistributed into the new buckets using the cached + /// hashcodes. + /// + private static void ExpandTable() { + // Allocate new Array + int oldhashsize = hashBuckets.Length; + + int hashsize = GetPrime(1 + oldhashsize * 2); + + // Don't replace any internal state until we've finished adding to the + // new bucket[]. This serves two purposes: 1) Allow concurrent readers + // to see valid hashtable contents at all times and 2) Protect against + // an OutOfMemoryException while allocating this new bucket[]. + HandleBucket[] newBuckets = new HandleBucket[hashsize]; + + // rehash table into new buckets + int nb; + for (nb = 0; nb < oldhashsize; nb++) { + HandleBucket oldb = hashBuckets[nb]; + if ((oldb.handle != IntPtr.Zero) && (oldb.handle != new IntPtr(-1))) { + + // Now re-fit this entry into the table + // + uint seed = (uint)oldb.hash_coll & 0x7FFFFFFF; + uint incr = (uint)(1 + (((seed >> 5) + 1) % ((uint)newBuckets.Length - 1))); + + do { + int bucketNumber = (int)(seed % (uint)newBuckets.Length); + + if ((newBuckets[bucketNumber].handle == IntPtr.Zero) || (newBuckets[bucketNumber].handle == new IntPtr(-1))) { + newBuckets[bucketNumber].window = oldb.window; + newBuckets[bucketNumber].handle = oldb.handle; + newBuckets[bucketNumber].hash_coll |= oldb.hash_coll & 0x7FFFFFFF; + break; + } + newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000); + seed += incr; + } while (true); + } + } + + // New bucket[] is good to go - replace buckets and other internal state. + hashBuckets = newBuckets; + + hashLoadSize = (int)(hashLoadFactor * hashsize); + if (hashLoadSize >= hashsize) { + hashLoadSize = hashsize - 1; + } + } + + /// + /// + /// + /// Retrieves the window associated with the specified + /// . + /// + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public static NativeWindow FromHandle(IntPtr handle) { + if (handle != IntPtr.Zero && handleCount > 0) { + return GetWindowFromTable(handle); + } + return null; + } + + /// + /// Calculates a prime number of at least minSize using a static table, and + /// if we overflow it, we calculate directly. + /// + private static int GetPrime(int minSize) { + if (minSize < 0) { + Debug.Fail("NativeWindow hashtable capacity overflow"); + throw new OutOfMemoryException(); + } + for (int i = 0; i < primes.Length; i++) { + int size = primes[i]; + if (size >= minSize) return size; + } + //outside of our predefined table. + //compute the hard way. + for (int j = ((minSize - 2) | 1); j < Int32.MaxValue; j += 2) { + bool prime = true; + + if ((j & 1) != 0) { + int target = (int)Math.Sqrt(j); + for (int divisor = 3; divisor < target; divisor += 2) { + if ((j % divisor) == 0) { + prime = false; + break; + } + } + if (prime) return j; + } + else { + if (j == 2) { + return j; + } + } + } + return minSize; + } + + /// + /// Returns the native window for the given handle, or null if + /// the handle is not in our hash table. + /// + private static NativeWindow GetWindowFromTable(IntPtr handle) { + + Debug.Assert(handle != IntPtr.Zero, "Zero handles cannot be stored in the table"); + + // Take a snapshot of buckets, in case another thread does a resize + HandleBucket[] buckets = hashBuckets; + uint seed; + uint incr; + int ntry = 0; + uint hashcode = InitHash(handle, buckets.Length, out seed, out incr); + + HandleBucket b; + do { + int bucketNumber = (int)(seed % (uint)buckets.Length); + b = buckets[bucketNumber]; + if (b.handle == IntPtr.Zero) { + return null; + } + if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && handle == b.handle) { + if (b.window.IsAllocated) { + return (NativeWindow)b.window.Target; + } + } + seed += incr; + } + while (b.hash_coll < 0 && ++ntry < buckets.Length); + return null; + } + + internal IntPtr GetHandleFromID(short id) { + IntPtr handle; + if (NativeWindow.hashForIdHandle == null || !NativeWindow.hashForIdHandle.TryGetValue(id, out handle)) { + handle = IntPtr.Zero; + } + + return handle; + } + + /// + /// Computes the hash function: H(key, i) = h1(key) + i*h2(key, hashSize). + /// The out parameter 'seed' is h1(key), while the out parameter + /// 'incr' is h2(key, hashSize). Callers of this function should + /// add 'incr' each time through a loop. + /// + private static uint InitHash(IntPtr handle, int hashsize, out uint seed, out uint incr) { + // Hashcode must be positive. Also, we must not use the sign bit, since + // that is used for the collision bit. + uint hashcode = ((uint)handle.GetHashCode()) & 0x7FFFFFFF; + seed = (uint)hashcode; + // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for + // the modular arithmetic to work correctly. This guarantees you'll + // visit every bucket in the table exactly once within hashsize + // iterations. Violate this and it'll cause obscure bugs forever. + // If you change this calculation for h2(key), update putEntry too! + incr = (uint)(1 + (((seed >> 5) + 1) % ((uint)hashsize - 1))); + return hashcode; + } + + /// + /// + /// + /// Specifies a notification method that is called when the handle for a + /// window is changed. + /// + /// + protected virtual void OnHandleChange() { + } + + /// + /// + /// On class load, we connect an event to Application to let us know when + /// the process or domain terminates. When this happens, we attempt to + /// clear our window class cache. We cannot destroy windows (because we don't + /// have access to their thread), and we cannot unregister window classes + /// (because the classes are in use by the windows we can't destroy). Instead, + /// we move the class and window procs to DefWndProc + /// + [PrePrepareMethod] + private static void OnShutdown(object sender, EventArgs e) { + + // If we still have windows allocated, we must sling them to userDefWindowProc + // or else they will AV if they get a message after the managed code has been + // removed. In debug builds, we assert and give the "ToString" of the native + // window. In retail we just detatch the window proc and let it go. Note that + // we cannot call DestroyWindow because this API will fail if called from + // an incorrect thread. + // + if (handleCount > 0) { + + Debug.Assert(userDefWindowProc != IntPtr.Zero, "We have active windows but no user window proc?"); + + lock (internalSyncObject) { + for (int i = 0; i < hashBuckets.Length; i++) { + HandleBucket b = hashBuckets[i]; + if (b.handle != IntPtr.Zero && b.handle != new IntPtr(-1)) { + HandleRef href = new HandleRef(b, b.handle); + UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(null, userDefWindowProc)); + UnsafeNativeMethods.SetClassLong(href, NativeMethods.GCL_WNDPROC, userDefWindowProc); + UnsafeNativeMethods.PostMessage(href, NativeMethods.WM_CLOSE, 0, 0); + + // Fish out the Window object, if it is valid, and NULL the handle pointer. This + // way the rest of Microsoft won't think the handle is still valid here. + if (b.window.IsAllocated) { + NativeWindow w = (NativeWindow)b.window.Target; + if (w != null) { + w.handle = IntPtr.Zero; + } + } + +#if DEBUG && FINALIZATION_WATCH + Debug.Fail("Window did not clean itself up: " + b.owner); +#endif + + b.window.Free(); + } + hashBuckets[i].handle = IntPtr.Zero; + hashBuckets[i].hash_coll = 0; + } + + handleCount = 0; + } + } + + WindowClass.DisposeCache(); + } + + /// + /// + /// + /// When overridden in a derived class, + /// manages an unhandled thread + /// exception. + /// + /// + protected virtual void OnThreadException(Exception e) { + } + + /// + /// + /// + /// Releases the handle associated with this window. + /// + /// + public virtual void ReleaseHandle() { + ReleaseHandle(true); + } + + /// + /// Releases the handle associated with this window. If handleValid + /// is true, this will unsubclass the window as well. HandleValid + /// should be false if we are releasing in response to a + /// WM_DESTROY. Unsubclassing during this message can cause problems + /// with XP's theme manager and it's not needed anyway. + /// + private void ReleaseHandle(bool handleValid) { + if (handle != IntPtr.Zero) { + // + lock (this) { + if (handle != IntPtr.Zero) { + if (handleValid) { + UnSubclass(); + } + + RemoveWindowFromTable(handle, this); + + if (ownHandle) { + System.Internal.HandleCollector.Remove(handle, NativeMethods.CommonHandles.Window); + ownHandle = false; + } + + handle = IntPtr.Zero; + //if not finalizing already. + if (weakThisPtr.IsAlive && weakThisPtr.Target != null) { + OnHandleChange(); + + // Now that we have disposed, there is no need to finalize us any more. So + // Mark to the garbage collector that we no longer need finalization. + // + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + try { + GC.SuppressFinalize(this); + } + finally { + CodeAccessPermission.RevertAssert(); + } + suppressedGC = true; + } + } + } + } + } + + /// + /// Removes an entry from this hashtable. If an entry with the specified + /// key exists in the hashtable, it is removed. + /// + private static void RemoveWindowFromTable(IntPtr handle, NativeWindow window) { + + Debug.Assert(handle != IntPtr.Zero, "Incorrect handle"); + + lock (internalSyncObject) { + + uint seed; + uint incr; + // Assuming only one concurrent writer, write directly into buckets. + uint hashcode = InitHash(handle, hashBuckets.Length, out seed, out incr); + int ntry = 0; + NativeWindow prevWindow = window.PreviousWindow; + HandleBucket b; + + int bn; // bucketNumber + do { + bn = (int)(seed % (uint)hashBuckets.Length); // bucketNumber + b = hashBuckets[bn]; + if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && handle == b.handle) { + + bool shouldRemoveBucket = (window.nextWindow == null); + bool shouldReplaceBucket = IsRootWindowInListWithChildren(window); + + // We need to fixup the link pointers of window here. + // + if (window.previousWindow != null) { + window.previousWindow.nextWindow = window.nextWindow; + } + if (window.nextWindow != null) { + window.nextWindow.defWindowProc = window.defWindowProc; + window.nextWindow.previousWindow = window.previousWindow; + } + + window.nextWindow = null; + window.previousWindow = null; + + if (shouldReplaceBucket) { + // Free the existing GC handle + if (hashBuckets[bn].window.IsAllocated) { + hashBuckets[bn].window.Free(); + } + + hashBuckets[bn].window = GCHandle.Alloc(prevWindow, GCHandleType.Weak); + } + else if (shouldRemoveBucket) { + + // Clear hash_coll field, then key, then value + hashBuckets[bn].hash_coll &= unchecked((int)0x80000000); + if (hashBuckets[bn].hash_coll != 0) { + hashBuckets[bn].handle = new IntPtr(-1); + } + else { + hashBuckets[bn].handle = IntPtr.Zero; + } + + if (hashBuckets[bn].window.IsAllocated) { + hashBuckets[bn].window.Free(); + } + + Debug.Assert(handleCount > 0, "Underflow on handle count"); + handleCount--; + } + return; + } + seed += incr; + } while (hashBuckets[bn].hash_coll < 0 && ++ntry < hashBuckets.Length); + } + } + + + /// + /// Determines if the given window is the first member of the linked list + /// + private static bool IsRootWindowInListWithChildren(NativeWindow window) { + // This seems backwards, but it isn't. When a new subclass comes in, + // it's previousWindow field is set to the previous subclass. Therefore, + // the top of the subclass chain has nextWindow == null and previousWindow + // == the first child subclass. + return ((window.PreviousWindow != null) && (window.nextWindow == null)); + } + + /// + /// Determines if the given window is the first member of the linked list + /// and has no children + /// + + /* No one is calling this private method, so it is safe to comment it out. + private static bool IsRootWindowInListWithNoChildren(NativeWindow window) + { + return ((window.PreviousWindow == null) && (window.nextWindow == null)); + } + */ + + + /// + /// Inserts an entry into this ID hashtable. + /// + internal static void RemoveWindowFromIDTable(IntPtr handle) { + short id = (short)NativeWindow.hashForHandleId[handle]; + NativeWindow.hashForHandleId.Remove(handle); + NativeWindow.hashForIdHandle.Remove(id); + } + + /// + /// This method can be used to modify the exception handling behavior of + /// NativeWindow. By default, NativeWindow will detect if an application + /// is running under a debugger, or is running on a machine with a debugger + /// installed. In this case, an unhandled exception in the NativeWindow's + /// WndProc method will remain unhandled so the debugger can trap it. If + /// there is no debugger installed NativeWindow will trap the exception + /// and route it to the Application class's unhandled exception filter. + /// + /// You can control this behavior via a config file, or directly through + /// code using this method. Setting the unhandled exception mode does + /// not change the behavior of any NativeWindow objects that are currently + /// connected to window handles; it only affects new handle connections. + /// + /// When threadScope is false, the application exception mode is set. The + /// application exception mode is used for all threads that have the Automatic mode. + /// Setting the application exception mode does not affect the setting of the current thread. + /// + /// When threadScope is true, the thread exception mode is set. The thread + /// exception mode overrides the application exception mode if it's not Automatic. + /// + internal static void SetUnhandledExceptionModeInternal(UnhandledExceptionMode mode, bool threadScope) { + + if (!threadScope && anyHandleCreatedInApp) { + throw new InvalidOperationException(SR.GetString(SR.ApplicationCannotChangeApplicationExceptionMode)); + } + if (threadScope && anyHandleCreated) { + throw new InvalidOperationException(SR.GetString(SR.ApplicationCannotChangeThreadExceptionMode)); + } + + switch (mode) { + case UnhandledExceptionMode.Automatic: + if (threadScope) { + userSetProcFlags = 0; + } + else { + userSetProcFlagsForApp = 0; + } + break; + case UnhandledExceptionMode.ThrowException: + if (threadScope) { + userSetProcFlags = UseDebuggableWndProc | InitializedFlags; + } + else { + userSetProcFlagsForApp = UseDebuggableWndProc | InitializedFlags; + } + break; + case UnhandledExceptionMode.CatchException: + if (threadScope) { + userSetProcFlags = InitializedFlags; + } + else { + userSetProcFlagsForApp = InitializedFlags; + } + break; + default: + throw new InvalidEnumArgumentException("mode", (int)mode, typeof(UnhandledExceptionMode)); + } + } + + /// + /// Unsubclassing is a tricky business. We need to account for + /// some border cases: + /// + /// 1) User has done multiple subclasses but has un-subclassed out of order. + /// 2) User has done multiple subclasses but now our defWindowProc points to + /// a NativeWindow that has GC'd + /// 3) User releasing this handle but this NativeWindow is not the current + /// window proc. + /// + private void UnSubclass() { + bool finalizing = (!weakThisPtr.IsAlive || weakThisPtr.Target == null); + HandleRef href = new HandleRef(this, handle); + + // Don't touch if the current window proc is not ours. + // + IntPtr currentWinPrc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); + if (windowProcPtr == currentWinPrc) { + if (previousWindow == null) { + +#if DEBUG + subclassStatus = "Unsubclassing back to native defWindowProc"; +#endif + + // If the defWindowProc points to a native window proc, previousWindow will + // be null. In this case, it is completely safe to assign defWindowProc + // to the current wndproc. + // + UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, defWindowProc)); + } + else { + if (finalizing) { + +#if DEBUG + subclassStatus = "Setting back to userDefWindowProc -- next chain is managed"; +#endif + + // Here, we are finalizing and defWindowProc is pointing to a managed object. We must assume + // that the object defWindowProc is pointing to is also finalizing. Why? Because we're + // holding a ref to it, and it is holding a ref to us. The only way this cycle will + // finalize is if no one else is hanging onto it. So, we re-assign the window proc to + // userDefWindowProc. + UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, userDefWindowProc)); + } + else { + +#if DEBUG + subclassStatus = "Setting back to next managed subclass object"; +#endif + + // Here we are not finalizing so we use the windowProc for our previous window. This may + // DIFFER from the value we are currently storing in defWindowProc because someone may + // have re-subclassed. + UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, previousWindow.windowProc); + } + } + } + else { + // cutting the subclass chain anyway, even if we're not the last one in the chain + // if the whole chain is all managed NativeWindow it doesnt matter, + // if the chain is not, then someone has been dirty and didn't clean up properly, too bad for them... + + //We will cut off the chain if we cannot unsubclass. + //If we find previouswindow pointing to us, then we can let RemoveWindowFromTable reassign the + //defwndproc pointers properly when this guy gets removed (thereby unsubclassing ourselves) + + if (nextWindow == null || nextWindow.defWindowProc != windowProcPtr) { + // we didn't find it... let's unhook anyway and cut the chain... this prevents crashes + UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, userDefWindowProc)); + } +#if DEBUG + subclassStatus = "FORCE unsubclass -- we do not own the subclass"; +#endif + } + } + + /// + /// + /// + /// Invokes the default window procedure associated with + /// this window. + /// + /// + + protected virtual void WndProc(ref Message m) { + DefWndProc(ref m); + } + + /// + /// A struct that contains a single bucket for our handle / GCHandle hash table. + /// The hash table algorithm we use here was stolen selfishly from the framework's + /// Hashtable class. We don't use Hashtable directly, however, because of boxing + /// concerns. It's algorithm is perfect for our needs, however: Multiple + /// reader, single writer without the need for locks and constant lookup time. + /// + /// Differences between this implementation and Hashtable: + /// + /// Keys are IntPtrs; their hash code is their value. Collision is still + /// marked with the high bit. + /// + /// Reclaimed buckets store -1 in their handle, not the hash table reference. + /// + private struct HandleBucket { + public IntPtr handle; // Win32 window handle + public GCHandle window; // a weak GC handle to the NativeWindow class + public int hash_coll; // Store hash code; sign bit means there was a collision. +#if DEBUG + public string owner; // owner of this handle +#endif + } + + /// + /// + /// WindowClass encapsulates a window class. + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + private class WindowClass { + internal static WindowClass cache; + + internal WindowClass next; + internal string className; + internal int classStyle; + internal string windowClassName; + internal int hashCode; + internal IntPtr defWindowProc; + internal NativeMethods.WndProc windowProc; + internal bool registered; + internal NativeWindow targetWindow; + + private static object wcInternalSyncObject = new object(); + private static int domainQualifier = 0; + + internal WindowClass(string className, int classStyle) { + this.className = className; + this.classStyle = classStyle; + RegisterClass(); + } + + public IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + Debug.Assert(hWnd != IntPtr.Zero, "Windows called us with an HWND of 0"); + UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), NativeMethods.GWL_WNDPROC, new HandleRef(this, defWindowProc)); + targetWindow.AssignHandle(hWnd); + return targetWindow.Callback(hWnd, msg, wparam, lparam); + } + + /// + /// + /// Retrieves a WindowClass object for use. This will create a new + /// object if there is no such class/style available, or retrun a + /// cached object if one exists. + /// + internal static WindowClass Create(string className, int classStyle) { + lock (wcInternalSyncObject) { + WindowClass wc = cache; + if (className == null) { + while (wc != null && (wc.className != null || + wc.classStyle != classStyle)) wc = wc.next; + } + else { + while (wc != null && + !className.Equals(wc.className)) { + wc = wc.next; + } + } + if (wc == null) { + wc = new WindowClass(className, classStyle); + wc.next = cache; + cache = wc; + } + else { + if (!wc.registered) { + wc.RegisterClass(); + } + } + return wc; + } + } + + /// + /// + /// Disposes our window class cache. This doesn't free anything + /// from the actual cache; it merely attempts to unregister + /// the classes of everything in the cache. This allows the unused + /// classes to be unrooted. They can later be re-rooted and reused. + /// + internal static void DisposeCache() { + lock (wcInternalSyncObject) { + WindowClass wc = cache; + + while (wc != null) { + wc.UnregisterClass(); + wc = wc.next; + } + } + } + + /// + /// Fabricates a full class name from a partial. + /// + private string GetFullClassName(string className) { + StringBuilder b = new StringBuilder(50); + b.Append(Application.WindowsFormsVersion); + b.Append('.'); + b.Append(className); + b.Append(".app."); + b.Append(domainQualifier); + b.Append('.'); + String appDomain = Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 16); + b.Append(VersioningHelper.MakeVersionSafeName(appDomain, ResourceScope.Process, ResourceScope.AppDomain)); + return b.ToString(); + } + + /// + /// + /// Once the classname and style bits have been set, this can + /// be called to register the class. + /// + private void RegisterClass() { + NativeMethods.WNDCLASS_D wndclass = new NativeMethods.WNDCLASS_D(); + + if (userDefWindowProc == IntPtr.Zero) { + string defproc = (Marshal.SystemDefaultCharSize == 1 ? "DefWindowProcA" : "DefWindowProcW"); + + userDefWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle("user32.dll")), defproc); + if (userDefWindowProc == IntPtr.Zero) { + throw new Win32Exception(); + } + } + + string localClassName = className; + + if (localClassName == null) { + + // If we don't use a hollow brush here, Windows will "pre paint" us with COLOR_WINDOW which + // creates a little bit if flicker. This happens even though we are overriding wm_erasebackgnd. + // Make this hollow to avoid all flicker. + // + wndclass.hbrBackground = UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH); //(IntPtr)(NativeMethods.COLOR_WINDOW + 1); + wndclass.style = classStyle; + + defWindowProc = userDefWindowProc; + localClassName = "Window." + Convert.ToString(classStyle, 16); + hashCode = 0; + } + else { + NativeMethods.WNDCLASS_I wcls = new NativeMethods.WNDCLASS_I(); + bool ok = UnsafeNativeMethods.GetClassInfo(NativeMethods.NullHandleRef, className, wcls); + int error = Marshal.GetLastWin32Error(); + if (!ok) { + throw new Win32Exception(error, SR.GetString(SR.InvalidWndClsName)); + } + wndclass.style = wcls.style; + wndclass.cbClsExtra = wcls.cbClsExtra; + wndclass.cbWndExtra = wcls.cbWndExtra; + wndclass.hIcon = wcls.hIcon; + wndclass.hCursor = wcls.hCursor; + wndclass.hbrBackground = wcls.hbrBackground; + wndclass.lpszMenuName = Marshal.PtrToStringAuto(wcls.lpszMenuName); + localClassName = className; + defWindowProc = wcls.lpfnWndProc; + hashCode = className.GetHashCode(); + } + + // Our static data is different for different app domains, so we include the app domain in with + // our window class name. This way our static table always matches what Win32 thinks. + // + windowClassName = GetFullClassName(localClassName); + windowProc = new NativeMethods.WndProc(this.Callback); + wndclass.lpfnWndProc = windowProc; + wndclass.hInstance = UnsafeNativeMethods.GetModuleHandle(null); + wndclass.lpszClassName = windowClassName; + + short atom = UnsafeNativeMethods.RegisterClass(wndclass); + if (atom == 0) { + + int err = Marshal.GetLastWin32Error(); + if (err == NativeMethods.ERROR_CLASS_ALREADY_EXISTS) { + // Check to see if the window class window + // proc points to DefWndProc. If it does, then + // this is a class from a rudely-terminated app domain + // and we can safely reuse it. If not, we've got + // to throw. + NativeMethods.WNDCLASS_I wcls = new NativeMethods.WNDCLASS_I(); + bool ok = UnsafeNativeMethods.GetClassInfo(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), windowClassName, wcls); + if (ok && wcls.lpfnWndProc == NativeWindow.UserDefindowProc) { + + // We can just reuse this class because we have marked it + // as being a nop in another domain. All we need to do is call SetClassLong. + // Only one problem: SetClassLong takes an HWND, which we don't have. That leaves + // us with some tricky business. First, try this the easy way and see + // if we can simply unregister and re-register the class. This might + // work because the other domain shutdown would have posted WM_CLOSE to all + // the windows of the class. + if (UnsafeNativeMethods.UnregisterClass(windowClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) { + atom = UnsafeNativeMethods.RegisterClass(wndclass); + // If this fails, we will always raise the first err above. No sense exposing our twiddling. + } + else { + // This is a little harder. We cannot reuse the class because it is + // already in use. We must create a new class. We bump our domain qualifier + // here to account for this, so we only do this expensive search once for the + // domain. + do { + domainQualifier++; + windowClassName = GetFullClassName(localClassName); + wndclass.lpszClassName = windowClassName; + atom = UnsafeNativeMethods.RegisterClass(wndclass); + } while (atom == 0 && Marshal.GetLastWin32Error() == NativeMethods.ERROR_CLASS_ALREADY_EXISTS); + } + } + } + + if (atom == 0) { + windowProc = null; + throw new Win32Exception(err); + } + } + registered = true; + } + + /// + /// + /// Unregisters this window class. Unregistration is not a + /// last resort; the window class may be re-registered through + /// a call to registerClass. + /// + private void UnregisterClass() { + if (registered && UnsafeNativeMethods.UnregisterClass(windowClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) { + windowProc = null; + registered = false; + } + } + } + } + + /// + /// Determines the exception mode of NativeWindow's WndProc method. Pass + /// a value of this enum into SetUnhandledExceptionMode to control how + /// new NativeWindow objects handle exceptions. + /// + public enum UnhandledExceptionMode { + Automatic, + ThrowException, + CatchException + } +} diff --git a/WindowsForms/Managed/System/WinForms/NavigateEvent.cs b/WindowsForms/Managed/System/WinForms/NavigateEvent.cs new file mode 100644 index 000000000..7c9b08c95 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NavigateEvent.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class NavigateEventArgs : EventArgs { + private bool isForward = true; + + /// + /// + /// [To be supplied.] + /// + public bool Forward { + get { + return isForward; + } + } + + /// + /// + /// [To be supplied.] + /// + public NavigateEventArgs(bool isForward) { + this.isForward = isForward; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/NavigateEventHandler.cs b/WindowsForms/Managed/System/WinForms/NavigateEventHandler.cs new file mode 100644 index 000000000..4e71b7195 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NavigateEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// + public delegate void NavigateEventHandler(object sender, NavigateEventArgs ne); +} diff --git a/WindowsForms/Managed/System/WinForms/NodeLabelEditEvent.cs b/WindowsForms/Managed/System/WinForms/NodeLabelEditEvent.cs new file mode 100644 index 000000000..be3247cb4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NodeLabelEditEvent.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the + /// or event. + /// + /// + public class NodeLabelEditEventArgs : EventArgs { + private readonly string label; + private readonly TreeNode node; + private bool cancelEdit = false; + + /// + /// + /// [To be supplied.] + /// + public NodeLabelEditEventArgs(TreeNode node) { + this.node = node; + this.label = null; + } + + /// + /// + /// [To be supplied.] + /// + public NodeLabelEditEventArgs(TreeNode node, string label) { + this.node = node; + this.label = label; + } + + /// + /// + /// [To be supplied.] + /// + public bool CancelEdit { + get { + return cancelEdit; + } + set { + cancelEdit = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public string Label { + get { + return label; + } + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode Node { + get { + return node; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/NodeLabelEditEventHandler.cs b/WindowsForms/Managed/System/WinForms/NodeLabelEditEventHandler.cs new file mode 100644 index 000000000..1446a0a6e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NodeLabelEditEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the or + /// + /// event of a . + /// + /// + /// + public delegate void NodeLabelEditEventHandler(object sender, NodeLabelEditEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/NoneExcludedImageIndexConverter.cs b/WindowsForms/Managed/System/WinForms/NoneExcludedImageIndexConverter.cs new file mode 100644 index 000000000..824e4ccb6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NoneExcludedImageIndexConverter.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + + /// + /// + /// Just returns false for IncludeNoneAsStandardValue + /// + internal sealed class NoneExcludedImageIndexConverter : ImageIndexConverter { + + /// + protected override bool IncludeNoneAsStandardValue { + get { + return false; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/NotifyIcon.cs b/WindowsForms/Managed/System/WinForms/NotifyIcon.cs new file mode 100644 index 000000000..8b2c482ac --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NotifyIcon.cs @@ -0,0 +1,1041 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Runtime.InteropServices; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using Microsoft.Win32; + using System.Drawing; + using System.Globalization; + + /// + /// + /// + /// Specifies a component that creates + /// an icon in the Windows System Tray. This class cannot be inherited. + /// + /// + [ + DefaultProperty("Text"), + DefaultEvent("MouseDoubleClick"), + Designer("System.Windows.Forms.Design.NotifyIconDesigner, " + AssemblyRef.SystemDesign), + ToolboxItemFilter("System.Windows.Forms"), + SRDescription(SR.DescriptionNotifyIcon) + ] + public sealed class NotifyIcon : Component { + private static readonly object EVENT_MOUSEDOWN = new object(); + private static readonly object EVENT_MOUSEMOVE = new object(); + private static readonly object EVENT_MOUSEUP = new object(); + private static readonly object EVENT_CLICK = new object(); + private static readonly object EVENT_DOUBLECLICK = new object(); + private static readonly object EVENT_MOUSECLICK = new object(); + private static readonly object EVENT_MOUSEDOUBLECLICK = new object(); + private static readonly object EVENT_BALLOONTIPSHOWN = new object(); + private static readonly object EVENT_BALLOONTIPCLICKED = new object(); + private static readonly object EVENT_BALLOONTIPCLOSED = new object(); + + private const int WM_TRAYMOUSEMESSAGE = NativeMethods.WM_USER + 1024; + private static int WM_TASKBARCREATED = SafeNativeMethods.RegisterWindowMessage("TaskbarCreated"); + + private object syncObj = new object(); + + private Icon icon = null; + private string text = ""; + private int id = 0; + private bool added = false; + private NotifyIconNativeWindow window = null; + private ContextMenu contextMenu = null; + private ContextMenuStrip contextMenuStrip = null; + private ToolTipIcon balloonTipIcon; + private string balloonTipText = ""; + private string balloonTipTitle = ""; + private static int nextId = 0; + private object userData; + private bool doubleClick = false; // checks if doubleclick is fired + + // Visible defaults to false, but the NotifyIconDesigner makes it seem like the default is + // true. We do this because while visible is the more common case, if it was a true default, + // there would be no way to create a hidden NotifyIcon without being visible for a moment. + private bool visible = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public NotifyIcon() { + id = ++nextId; + window = new NotifyIconNativeWindow(this); + UpdateIcon(visible); + } + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public NotifyIcon(IContainer container) : this() { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + /// + /// + /// + /// Gets or sets the BalloonTip text displayed when + /// the mouse hovers over a system tray icon. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(""), + SRDescription(SR.NotifyIconBalloonTipTextDescr), + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string BalloonTipText { + get { + return balloonTipText; + } + set { + if (value != balloonTipText) { + balloonTipText = value; + } + } + } + + /// + /// + /// + /// Gets or sets the BalloonTip icon displayed when + /// the mouse hovers over a system tray icon. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ToolTipIcon.None), + SRDescription(SR.NotifyIconBalloonTipIconDescr) + ] + public ToolTipIcon BalloonTipIcon { + get { + return balloonTipIcon; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolTipIcon.None, (int)ToolTipIcon.Error)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolTipIcon)); + } + if (value != balloonTipIcon) { + balloonTipIcon = value; + } + } + } + + /// + /// + /// + /// Gets or sets the BalloonTip title displayed when + /// the mouse hovers over a system tray icon. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(""), + SRDescription(SR.NotifyIconBalloonTipTitleDescr) + ] + public string BalloonTipTitle { + get { + return balloonTipTitle; + } + set { + if (value != balloonTipTitle) { + balloonTipTitle = value; + } + } + } + + /// + /// + /// [This event is raised on the NIN_BALLOONUSERCLICK message.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.NotifyIconOnBalloonTipClickedDescr)] + public event EventHandler BalloonTipClicked { + add { + Events.AddHandler(EVENT_BALLOONTIPCLICKED, value); + } + + remove { + Events.RemoveHandler(EVENT_BALLOONTIPCLICKED, value); + } + } + + /// + /// + /// [This event is raised on the NIN_BALLOONTIMEOUT message.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.NotifyIconOnBalloonTipClosedDescr)] + public event EventHandler BalloonTipClosed { + add { + Events.AddHandler(EVENT_BALLOONTIPCLOSED, value); + } + + remove { + Events.RemoveHandler(EVENT_BALLOONTIPCLOSED, value); + } + } + + /// + /// + /// [This event is raised on the NIN_BALLOONSHOW or NIN_BALLOONHIDE message.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.NotifyIconOnBalloonTipShownDescr)] + public event EventHandler BalloonTipShown { + add { + Events.AddHandler(EVENT_BALLOONTIPSHOWN, value); + } + remove { + Events.RemoveHandler(EVENT_BALLOONTIPSHOWN, value); + } + } + + /// + /// + /// + /// Gets or sets context menu + /// for the tray icon. + /// + /// + [ + Browsable(false), + DefaultValue(null), + SRCategory(SR.CatBehavior), + SRDescription(SR.NotifyIconMenuDescr) + ] + public ContextMenu ContextMenu { + get { + return contextMenu; + } + + set { + this.contextMenu = value; + } + } + + [ + DefaultValue(null), + SRCategory(SR.CatBehavior), + SRDescription(SR.NotifyIconMenuDescr) + ] + public ContextMenuStrip ContextMenuStrip { + get { + return contextMenuStrip; + } + + set { + this.contextMenuStrip = value; + } + } + + /// + /// + /// + /// Gets or sets the current + /// icon. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(null), + SRDescription(SR.NotifyIconIconDescr) + ] + public Icon Icon { + get { + return icon; + } + set { + if (icon != value) { + this.icon = value; + UpdateIcon(visible); + } + } + } + + /// + /// + /// + /// Gets or sets the ToolTip text displayed when + /// the mouse hovers over a system tray icon. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(""), + SRDescription(SR.NotifyIconTextDescr), + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string Text { + get { + return text; + } + set { + if (value == null) value = ""; + if (value != null && !value.Equals(this.text)) { + if (value != null && value.Length > 63) { + throw new ArgumentOutOfRangeException("Text", value, SR.GetString(SR.TrayIcon_TextTooLong)); + } + this.text = value; + if (added) { + UpdateIcon(true); + } + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the icon is visible in the Windows System Tray. + /// + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(false), + SRDescription(SR.NotifyIconVisDescr) + ] + public bool Visible { + get { + return visible; + } + set { + if (visible != value) { + UpdateIcon(value); + visible = value; + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Occurs when the user clicks the icon in the system tray. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnClickDescr)] + public event EventHandler Click { + add { + Events.AddHandler(EVENT_CLICK, value); + } + remove { + Events.RemoveHandler(EVENT_CLICK, value); + } + } + + /// + /// + /// Occurs when the user double-clicks the icon in the system tray. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnDoubleClickDescr)] + public event EventHandler DoubleClick { + add { + Events.AddHandler(EVENT_DOUBLECLICK, value); + } + remove { + Events.RemoveHandler(EVENT_DOUBLECLICK, value); + } + } + + /// + /// + /// Occurs when the user clicks the icon in the system tray. + /// + [SRCategory(SR.CatAction), SRDescription(SR.NotifyIconMouseClickDescr)] + public event MouseEventHandler MouseClick { + add { + Events.AddHandler(EVENT_MOUSECLICK, value); + } + remove { + Events.RemoveHandler(EVENT_MOUSECLICK, value); + } + } + + /// + /// + /// Occurs when the user mouse double clicks the icon in the system tray. + /// + [SRCategory(SR.CatAction), SRDescription(SR.NotifyIconMouseDoubleClickDescr)] + public event MouseEventHandler MouseDoubleClick { + add { + Events.AddHandler(EVENT_MOUSEDOUBLECLICK, value); + } + remove { + Events.RemoveHandler(EVENT_MOUSEDOUBLECLICK, value); + } + } + + /// + /// + /// + /// Occurs when the + /// user presses a mouse button while the pointer is over the icon in the system tray. + /// + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseDownDescr)] + public event MouseEventHandler MouseDown { + add { + Events.AddHandler(EVENT_MOUSEDOWN, value); + } + remove { + Events.RemoveHandler(EVENT_MOUSEDOWN, value); + } + } + + /// + /// + /// + /// Occurs + /// when the user moves the mouse pointer over the icon in the system tray. + /// + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseMoveDescr)] + public event MouseEventHandler MouseMove { + add { + Events.AddHandler(EVENT_MOUSEMOVE, value); + } + remove { + Events.RemoveHandler(EVENT_MOUSEMOVE, value); + } + } + + /// + /// + /// + /// Occurs when the + /// user releases the mouse button while the pointer + /// is over the icon in the system tray. + /// + /// + [SRCategory(SR.CatMouse), SRDescription(SR.ControlOnMouseUpDescr)] + public event MouseEventHandler MouseUp { + add { + Events.AddHandler(EVENT_MOUSEUP, value); + } + remove { + Events.RemoveHandler(EVENT_MOUSEUP, value); + } + } + + /// + /// + /// + /// Disposes of the resources (other than memory) used by the + /// . + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (window != null) { + this.icon = null; + this.Text = String.Empty; + UpdateIcon(false); + window.DestroyHandle(); + window = null; + contextMenu = null; + contextMenuStrip = null; + } + } + else { + // This same post is done in ControlNativeWindow's finalize method, so if you change + // it, change it there too. + // + if (window != null && window.Handle != IntPtr.Zero) { + UnsafeNativeMethods.PostMessage(new HandleRef(window, window.Handle), NativeMethods.WM_CLOSE, 0, 0); + window.ReleaseHandle(); + } + } + base.Dispose(disposing); + } + + /// + /// + /// + /// This method raised the BalloonTipClicked event. + /// + /// + private void OnBalloonTipClicked() { + EventHandler handler = (EventHandler)Events[EVENT_BALLOONTIPCLICKED]; + if (handler != null) { + handler(this, EventArgs.Empty); + } + } + + /// + /// + /// + /// This method raised the BalloonTipClosed event. + /// + /// + private void OnBalloonTipClosed() { + EventHandler handler = (EventHandler)Events[EVENT_BALLOONTIPCLOSED]; + if (handler != null) { + handler(this, EventArgs.Empty); + } + } + + /// + /// + /// + /// This method raised the BalloonTipShown event. + /// + /// + private void OnBalloonTipShown() { + EventHandler handler = (EventHandler)Events[EVENT_BALLOONTIPSHOWN]; + if (handler != null) { + handler(this, EventArgs.Empty); + } + } + + /// + /// + /// + /// This method actually raises the Click event. Inheriting classes should + /// override this if they wish to be notified of a Click event. (This is far + /// preferable to actually adding an event handler.) They should not, + /// however, forget to call base.onClick(e); before exiting, to ensure that + /// other recipients do actually get the event. + /// + /// + private void OnClick(EventArgs e) { + EventHandler handler = (EventHandler) Events[ EVENT_CLICK ]; + if (handler != null) + handler( this, e ); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDoubleClick to send this event to any registered event listeners. + /// + private void OnDoubleClick(EventArgs e) { + EventHandler handler = (EventHandler) Events[ EVENT_DOUBLECLICK ]; + if (handler != null) + handler( this, e ); + } + + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.OnMouseClick to send this event to any registered event listeners. + /// + private void OnMouseClick(MouseEventArgs mea) { + MouseEventHandler handler = (MouseEventHandler) Events[ EVENT_MOUSECLICK ]; + if (handler != null) + handler( this, mea ); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.OnMouseDoubleClick to send this event to any registered event listeners. + /// + private void OnMouseDoubleClick(MouseEventArgs mea) { + MouseEventHandler handler = (MouseEventHandler) Events[ EVENT_MOUSEDOUBLECLICK ]; + if (handler != null) + handler( this, mea ); + } + + /// + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseDown to send this event to any registered event listeners. + /// + /// + /// + private void OnMouseDown(MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[EVENT_MOUSEDOWN]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseMove to send this event to any registered event listeners. + /// + /// + /// + private void OnMouseMove(MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[EVENT_MOUSEMOVE]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onMouseUp to send this event to any registered event listeners. + /// + /// + private void OnMouseUp(MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[EVENT_MOUSEUP]; + if (handler != null) + handler(this, e); + } + + /// + /// + /// + /// Displays a balloon tooltip in the taskbar. + /// + /// The system enforces minimum and maximum timeout values. Timeout + /// values that are too large are set to the maximum value and values + /// that are too small default to the minimum value. The operating system's + /// default minimum and maximum timeout values are 10 seconds and 30 seconds, + /// respectively. + /// + /// No more than one balloon ToolTip at at time is displayed for the taskbar. + /// If an application attempts to display a ToolTip when one is already being displayed, + /// the ToolTip will not appear until the existing balloon ToolTip has been visible for at + /// least the system minimum timeout value. For example, a balloon ToolTip with timeout + /// set to 30 seconds has been visible for seven seconds when another application attempts + /// to display a balloon ToolTip. If the system minimum timeout is ten seconds, the first + /// ToolTip displays for an additional three seconds before being replaced by the second ToolTip. + /// + /// + public void ShowBalloonTip(int timeout) { + ShowBalloonTip(timeout, this.balloonTipTitle, this.balloonTipText, this.balloonTipIcon); + } + + + /// + /// + /// + /// Displays a balloon tooltip in the taskbar with the specified title, + /// text, and icon for a duration of the specified timeout value. + /// + /// The system enforces minimum and maximum timeout values. Timeout + /// values that are too large are set to the maximum value and values + /// that are too small default to the minimum value. The operating system's + /// default minimum and maximum timeout values are 10 seconds and 30 seconds, + /// respectively. + /// + /// No more than one balloon ToolTip at at time is displayed for the taskbar. + /// If an application attempts to display a ToolTip when one is already being displayed, + /// the ToolTip will not appear until the existing balloon ToolTip has been visible for at + /// least the system minimum timeout value. For example, a balloon ToolTip with timeout + /// set to 30 seconds has been visible for seven seconds when another application attempts + /// to display a balloon ToolTip. If the system minimum timeout is ten seconds, the first + /// ToolTip displays for an additional three seconds before being replaced by the second ToolTip. + /// + /// + public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon) { + + if (timeout < 0) { + throw new ArgumentOutOfRangeException("timeout", SR.GetString(SR.InvalidArgument, "timeout", (timeout).ToString(CultureInfo.CurrentCulture))); + } + + if (string.IsNullOrEmpty(tipText)) + { + throw new ArgumentException(SR.GetString(SR.NotifyIconEmptyOrNullTipText)); + } + + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(tipIcon, (int)tipIcon, (int)ToolTipIcon.None, (int)ToolTipIcon.Error)){ + throw new InvalidEnumArgumentException("tipIcon", (int)tipIcon, typeof(ToolTipIcon)); + } + + + if (added ) { + // Bail if in design mode... + if (DesignMode) { + return; + } + IntSecurity.UnrestrictedWindows.Demand(); + + NativeMethods.NOTIFYICONDATA data = new NativeMethods.NOTIFYICONDATA(); + if (window.Handle == IntPtr.Zero) { + window.CreateHandle(new CreateParams()); + } + data.hWnd = window.Handle; + data.uID = id; + data.uFlags = NativeMethods.NIF_INFO; + data.uTimeoutOrVersion = timeout; + data.szInfoTitle = tipTitle; + data.szInfo = tipText; + switch (tipIcon) { + case ToolTipIcon.Info: data.dwInfoFlags = NativeMethods.NIIF_INFO; break; + case ToolTipIcon.Warning: data.dwInfoFlags = NativeMethods.NIIF_WARNING; break; + case ToolTipIcon.Error: data.dwInfoFlags = NativeMethods.NIIF_ERROR; break; + case ToolTipIcon.None: data.dwInfoFlags = NativeMethods.NIIF_NONE; break; + } + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data); + } + } + + /// + /// + /// Shows the context menu for the tray icon. + /// + /// + private void ShowContextMenu() { + + if (contextMenu != null || contextMenuStrip != null) { + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(pt); + + // VS7 #38994 + // The solution to this problem was found in MSDN Article ID: Q135788. + // Summary: the current window must be made the foreground window + // before calling TrackPopupMenuEx, and a task switch must be + // forced after the call. + UnsafeNativeMethods.SetForegroundWindow(new HandleRef(window, window.Handle)); + + if (contextMenu != null) { + contextMenu.OnPopup( EventArgs.Empty ); + + SafeNativeMethods.TrackPopupMenuEx(new HandleRef(contextMenu, contextMenu.Handle), + NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTALIGN, + pt.x, + pt.y, + new HandleRef(window, window.Handle), + null); + + // Force task switch (see above) + UnsafeNativeMethods.PostMessage(new HandleRef(window, window.Handle), NativeMethods.WM_NULL, IntPtr.Zero, IntPtr.Zero); + } + else if (contextMenuStrip != null) { + // this will set the context menu strip to be toplevel + // and will allow us to overlap the system tray + contextMenuStrip.ShowInTaskbar(pt.x, pt.y); + } + } + } + + /// + /// + /// Updates the icon in the system tray. + /// + /// + private void UpdateIcon(bool showIconInTray) { + lock(syncObj) { + + // Bail if in design mode... + // + if (DesignMode) { + return; + } + + IntSecurity.UnrestrictedWindows.Demand(); + + window.LockReference(showIconInTray); + + NativeMethods.NOTIFYICONDATA data = new NativeMethods.NOTIFYICONDATA(); + data.uCallbackMessage = WM_TRAYMOUSEMESSAGE; + data.uFlags = NativeMethods.NIF_MESSAGE; + if (showIconInTray) { + if (window.Handle == IntPtr.Zero) { + window.CreateHandle(new CreateParams()); + } + } + data.hWnd = window.Handle; + data.uID = id; + data.hIcon = IntPtr.Zero; + data.szTip = null; + if (icon != null) { + data.uFlags |= NativeMethods.NIF_ICON; + data.hIcon = icon.Handle; + } + data.uFlags |= NativeMethods.NIF_TIP; + data.szTip = text; + + if (showIconInTray && icon != null) { + if (!added) { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data); + added = true; + } + else { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data); + } + } + else if (added) { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data); + added = false; + } + } + } + + /// + /// + /// Handles the mouse-down event + /// + /// + private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { + if (clicks == 2) { + OnDoubleClick(new MouseEventArgs(button, 2, 0, 0, 0)); + OnMouseDoubleClick(new MouseEventArgs(button, 2, 0, 0, 0)); + doubleClick = true; + } + OnMouseDown(new MouseEventArgs(button, clicks, 0, 0, 0)); + } + + /// + /// + /// Handles the mouse-move event + /// + /// + private void WmMouseMove(ref Message m) { + OnMouseMove(new MouseEventArgs(Control.MouseButtons, 0, 0, 0, 0)); + } + + /// + /// + /// Handles the mouse-up event + /// + /// + private void WmMouseUp(ref Message m, MouseButtons button) { + OnMouseUp(new MouseEventArgs(button, 0, 0, 0, 0)); + //subhag + if(!doubleClick) { + OnClick(new MouseEventArgs(button, 0, 0, 0, 0)); + OnMouseClick(new MouseEventArgs(button, 0, 0, 0, 0)); + } + doubleClick = false; + } + + private void WmTaskbarCreated(ref Message m) { + added = false; + UpdateIcon(visible); + } + + private void WndProc(ref Message msg) { + + switch (msg.Msg) { + case WM_TRAYMOUSEMESSAGE: + switch ((int)msg.LParam) { + case NativeMethods.WM_LBUTTONDBLCLK: + WmMouseDown(ref msg, MouseButtons.Left, 2); + break; + case NativeMethods.WM_LBUTTONDOWN: + WmMouseDown(ref msg, MouseButtons.Left, 1); + break; + case NativeMethods.WM_LBUTTONUP: + WmMouseUp(ref msg, MouseButtons.Left); + break; + case NativeMethods.WM_MBUTTONDBLCLK: + WmMouseDown(ref msg, MouseButtons.Middle, 2); + break; + case NativeMethods.WM_MBUTTONDOWN: + WmMouseDown(ref msg, MouseButtons.Middle, 1); + break; + case NativeMethods.WM_MBUTTONUP: + WmMouseUp(ref msg, MouseButtons.Middle); + break; + case NativeMethods.WM_MOUSEMOVE: + WmMouseMove(ref msg); + break; + case NativeMethods.WM_RBUTTONDBLCLK: + WmMouseDown(ref msg, MouseButtons.Right, 2); + break; + case NativeMethods.WM_RBUTTONDOWN: + WmMouseDown(ref msg, MouseButtons.Right, 1); + break; + case NativeMethods.WM_RBUTTONUP: + if (contextMenu != null || contextMenuStrip != null) { + ShowContextMenu(); + } + WmMouseUp(ref msg, MouseButtons.Right); + break; + case NativeMethods.NIN_BALLOONSHOW: + OnBalloonTipShown(); + break; + case NativeMethods.NIN_BALLOONHIDE: + OnBalloonTipClosed(); + break; + case NativeMethods.NIN_BALLOONTIMEOUT: + OnBalloonTipClosed(); + break; + case NativeMethods.NIN_BALLOONUSERCLICK: + OnBalloonTipClicked(); + break; + } + break; + case NativeMethods.WM_COMMAND: + if (IntPtr.Zero == msg.LParam) { + if (Command.DispatchID((int)msg.WParam & 0xFFFF)) return; + } + else { + window.DefWndProc(ref msg); + } + break; + case NativeMethods.WM_DRAWITEM: + // If the wparam is zero, then the message was sent by a menu. + // See WM_DRAWITEM in MSDN. + if (msg.WParam == IntPtr.Zero) { + WmDrawItemMenuItem(ref msg); + } + break; + case NativeMethods.WM_MEASUREITEM: + // If the wparam is zero, then the message was sent by a menu. + if (msg.WParam == IntPtr.Zero) { + WmMeasureMenuItem(ref msg); + } + break; + + case NativeMethods.WM_INITMENUPOPUP: + WmInitMenuPopup(ref msg); + break; + + case NativeMethods.WM_DESTROY: + // Remove the icon from the taskbar + UpdateIcon(false); + break; + + default: + if (msg.Msg == WM_TASKBARCREATED) { + WmTaskbarCreated(ref msg); + } + window.DefWndProc(ref msg); + break; + } + } + + private void WmInitMenuPopup(ref Message m) { + if (contextMenu != null) { + if (contextMenu.ProcessInitMenuPopup(m.WParam)) { + return; + } + } + + window.DefWndProc(ref m); + } + + private void WmMeasureMenuItem(ref Message m) { + // Obtain the menu item object + NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); + + Debug.Assert(m.LParam != IntPtr.Zero, "m.lparam is null"); + + // A pointer to the correct MenuItem is stored in the measure item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(mis.itemData); + Debug.Assert(menuItem != null, "UniqueID is not associated with a menu item"); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmMeasureItem(ref m); + } + } + + private void WmDrawItemMenuItem(ref Message m) { + // Obtain the menu item object + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + + // A pointer to the correct MenuItem is stored in the draw item + // information sent with the message. + // (See MenuItem.CreateMenuItemInfo) + MenuItem menuItem = MenuItem.GetMenuItemFromItemData(dis.itemData); + + // Delegate this message to the menu item + if (menuItem != null) { + menuItem.WmDrawItem(ref m); + } + } + + /// + /// + /// Defines a placeholder window that the NotifyIcon is attached to. + /// + /// + private class NotifyIconNativeWindow : NativeWindow { + internal NotifyIcon reference; + private GCHandle rootRef; // We will root the control when we do not want to be elligible for garbage collection. + + /// + /// + /// Create a new NotifyIcon, and bind the window to the NotifyIcon component. + /// + /// + internal NotifyIconNativeWindow(NotifyIcon component) { + reference = component; + } + + ~NotifyIconNativeWindow() { + // This same post is done in Control's Dispose method, so if you change + // it, change it there too. + // + if (Handle != IntPtr.Zero) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_CLOSE, 0, 0); + } + + // This releases the handle from our window proc, re-routing it back to + // the system. + } + + public void LockReference(bool locked) { + if (locked) { + if (!rootRef.IsAllocated) { + rootRef = GCHandle.Alloc(reference, GCHandleType.Normal); + } + } + else { + if (rootRef.IsAllocated) { + rootRef.Free(); + } + } + } + + protected override void OnThreadException(Exception e) { + Application.OnThreadException(e); + } + + /// + /// + /// Pass messages on to the NotifyIcon object's wndproc handler. + /// + /// + protected override void WndProc(ref Message m) { + Debug.Assert(reference != null, "NotifyIcon was garbage collected while it was still visible. How did we let that happen?"); + reference.WndProc(ref m); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/NumericUpDown.cs b/WindowsForms/Managed/System/WinForms/NumericUpDown.cs new file mode 100644 index 000000000..08116a2a0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NumericUpDown.cs @@ -0,0 +1,960 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Globalization; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + /// + /// Represents a Windows up-down control that displays numeric values. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultEvent("ValueChanged"), + DefaultBindingProperty("Value"), + SRDescription(SR.DescriptionNumericUpDown) + ] + public class NumericUpDown : UpDownBase, ISupportInitialize { + + private static readonly Decimal DefaultValue = Decimal.Zero; + private static readonly Decimal DefaultMinimum = Decimal.Zero; + private static readonly Decimal DefaultMaximum = (Decimal)100.0; + private const int DefaultDecimalPlaces = 0; + private static readonly Decimal DefaultIncrement = Decimal.One; + private const bool DefaultThousandsSeparator = false; + private const bool DefaultHexadecimal = false; + private const int InvalidValue = -1; + + ////////////////////////////////////////////////////////////// + // Member variables + // + ////////////////////////////////////////////////////////////// + + /// + /// The number of decimal places to display. + /// + private int decimalPlaces = DefaultDecimalPlaces; + + /// + /// The amount to increment by. + /// + private Decimal increment = DefaultIncrement; + + // Display the thousands separator? + private bool thousandsSeparator = DefaultThousandsSeparator; + + // Minimum and maximum values + private Decimal minimum = DefaultMinimum; + private Decimal maximum = DefaultMaximum; + + // Hexadecimal + private bool hexadecimal = DefaultHexadecimal; + + // Internal storage of the current value + private Decimal currentValue = DefaultValue; + private bool currentValueChanged; + + // Event handler for the onValueChanged event + private EventHandler onValueChanged = null; + + // Disable value range checking while initializing the control + private bool initializing = false; + + // Provides for finer acceleration behavior. + private NumericUpDownAccelerationCollection accelerations; + + // the current NumericUpDownAcceleration object. + private int accelerationsCurrentIndex; + + // Used to calculate the time elapsed since the up/down button was pressed, + // to know when to get the next entry in the accelaration table. + private long buttonPressedStartTime; + + /// + /// + /// [To be supplied.] + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "0" is the default value for numeric up down. + // So we don't have to localize it. + ] + public NumericUpDown() : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + Text = "0"; + StopAcceleration(); + } + + ////////////////////////////////////////////////////////////// + // Properties + // + ////////////////////////////////////////////////////////////// + + + /// + /// Specifies the acceleration information. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public NumericUpDownAccelerationCollection Accelerations { + get { + if( this.accelerations == null ){ + this.accelerations = new NumericUpDownAccelerationCollection(); + } + return this.accelerations; + } + } + + /// + /// + /// Gets or sets the number of decimal places to display in the up-down control. + /// + [ + SRCategory(SR.CatData), + DefaultValue(NumericUpDown.DefaultDecimalPlaces), + SRDescription(SR.NumericUpDownDecimalPlacesDescr) + ] + public int DecimalPlaces { + + get { + return decimalPlaces; + } + + set { + if (value < 0 || value > 99) { + throw new ArgumentOutOfRangeException("DecimalPlaces", SR.GetString(SR.InvalidBoundArgument, "DecimalPlaces", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "99")); + } + decimalPlaces = value; + UpdateEditText(); + } + } + + /// + /// + /// Gets or + /// sets a value indicating whether the up-down control should + /// display the value it contains in hexadecimal format. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(NumericUpDown.DefaultHexadecimal), + SRDescription(SR.NumericUpDownHexadecimalDescr) + ] + public bool Hexadecimal { + + get { + return hexadecimal; + } + + set { + hexadecimal = value; + UpdateEditText(); + } + } + + /// + /// + /// Gets or sets the value + /// to increment or + /// decrement the up-down control when the up or down buttons are clicked. + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.NumericUpDownIncrementDescr) + ] + public Decimal Increment { + + get { + if (this.accelerationsCurrentIndex != InvalidValue) { + return this.Accelerations[this.accelerationsCurrentIndex].Increment; + } + + return this.increment; + } + + set { + if (value < (Decimal)0.0) { + throw new ArgumentOutOfRangeException("Increment", SR.GetString(SR.InvalidArgument, "Increment", value.ToString(CultureInfo.CurrentCulture))); + } + else { + this.increment = value; + } + } + } + + + /// + /// + /// Gets or sets the maximum value for the up-down control. + /// + [ + SRCategory(SR.CatData), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.NumericUpDownMaximumDescr) + ] + public Decimal Maximum { + + get { + return maximum; + } + + set { + maximum = value; + if (minimum > maximum) { + minimum = maximum; + } + + Value = Constrain(currentValue); + + Debug.Assert(maximum == value, "Maximum != what we just set it to!"); + } + } + + /// + /// + /// Gets or sets the minimum allowed value for the up-down control. + /// + [ + SRCategory(SR.CatData), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.NumericUpDownMinimumDescr) + ] + public Decimal Minimum { + + get { + return minimum; + } + + set { + minimum = value; + if (minimum > maximum) { + maximum = value; + } + + Value = Constrain(currentValue); + + Debug.Assert(minimum.Equals(value), "Minimum != what we just set it to!"); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// Determines whether the UpDownButtons have been pressed for enough time to activate acceleration. + /// + private bool Spinning { + get{ + return this.accelerations != null && this.buttonPressedStartTime != InvalidValue; + } + } + + /// + /// + /// + /// + /// The text displayed in the control. + /// + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + // We're just overriding this to make it non-browsable. + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// Gets or sets a value indicating whether a thousands + /// separator is displayed in the up-down control when appropriate. + /// + [ + SRCategory(SR.CatData), + DefaultValue(NumericUpDown.DefaultThousandsSeparator), + Localizable(true), + SRDescription(SR.NumericUpDownThousandsSeparatorDescr) + ] + public bool ThousandsSeparator { + + get { + return thousandsSeparator; + } + + set { + thousandsSeparator = value; + UpdateEditText(); + } + } + + /* + * The current value of the control + */ + /// + /// + /// Gets or sets the value + /// assigned to the up-down control. + /// + [ + SRCategory(SR.CatAppearance), + Bindable(true), + SRDescription(SR.NumericUpDownValueDescr) + ] + public Decimal Value { + + get { + if (UserEdit) { + ValidateEditText(); + } + return currentValue; + } + + set { + if (value != currentValue) { + + if (!initializing && ((value < minimum) || (value > maximum))) { + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", value.ToString(CultureInfo.CurrentCulture), "'Minimum'", "'Maximum'")); + } + else { + currentValue = value; + + OnValueChanged(EventArgs.Empty); + currentValueChanged = true; + UpdateEditText(); + } + } + } + } + + + ////////////////////////////////////////////////////////////// + // Methods + // + ////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Occurs when the property has been changed in some way. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.NumericUpDownOnValueChangedDescr)] + public event EventHandler ValueChanged { + add { + onValueChanged += value; + } + remove { + onValueChanged -= value; + } + } + + /// + /// + /// + /// Handles tasks required when the control is being initialized. + /// + public void BeginInit() { + initializing = true; + } + + // + // Returns the provided value constrained to be within the min and max. + // + private Decimal Constrain(Decimal value) { + + Debug.Assert(minimum <= maximum, + "minimum > maximum"); + + if (value < minimum) { + value = minimum; + } + + if (value > maximum) { + value = maximum; + } + + return value; + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new NumericUpDownAccessibleObject(this); + } + + /// + /// + /// + /// Decrements the value of the up-down control. + /// + /// + public override void DownButton() { + SetNextAcceleration(); + + if (UserEdit) { + ParseEditText(); + } + + Decimal newValue = currentValue; + + // Operations on Decimals can throw OverflowException. + // + try{ + newValue -= this.Increment; + + if (newValue < minimum){ + newValue = minimum; + if( this.Spinning){ + StopAcceleration(); + } + } + } + catch( OverflowException ){ + newValue = minimum; + } + + Value = newValue; + } + + /// + /// + /// + /// + /// Called when initialization of the control is complete. + /// + /// + public void EndInit() { + initializing = false; + Value = Constrain(currentValue); + UpdateEditText(); + } + + /// + /// Overridden to set/reset acceleration variables. + /// + protected override void OnKeyDown(KeyEventArgs e) { + if (base.InterceptArrowKeys && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) && !this.Spinning) { + StartAcceleration(); + } + + base.OnKeyDown(e); + } + + /// + /// Overridden to set/reset acceleration variables. + /// + protected override void OnKeyUp(KeyEventArgs e) { + if (base.InterceptArrowKeys && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)) { + StopAcceleration(); + } + + base.OnKeyUp(e); + } + + /// + /// + /// + /// + /// Restricts the entry of characters to digits (including hex), the negative sign, + /// the decimal point, and editing keystrokes (backspace). + /// + /// + protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) { + + base.OnTextBoxKeyPress(source, e); + + NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat; + string decimalSeparator = numberFormatInfo.NumberDecimalSeparator; + string groupSeparator = numberFormatInfo.NumberGroupSeparator; + string negativeSign = numberFormatInfo.NegativeSign; + + string keyInput = e.KeyChar.ToString(); + + if (Char.IsDigit(e.KeyChar)) { + // Digits are OK + } + else if (keyInput.Equals(decimalSeparator) || keyInput.Equals(groupSeparator) || + keyInput.Equals(negativeSign)) { + // Decimal separator is OK + } + else if (e.KeyChar == '\b') { + // Backspace key is OK + } + else if (Hexadecimal && ((e.KeyChar >= 'a' && e.KeyChar <= 'f') || e.KeyChar >= 'A' && e.KeyChar <= 'F')) { + // Hexadecimal digits are OK + } + else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0) { + // Let the edit control handle control and alt key combinations + } + else { + // Eat this invalid key and beep + e.Handled = true; + SafeNativeMethods.MessageBeep(0); + } + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnValueChanged(EventArgs e) { + + // Call the event handler + if (onValueChanged != null) { + onValueChanged(this, e); + } + } + + /// + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + if (UserEdit) + { + UpdateEditText(); + } + } + + /// + /// Overridden to start/end acceleration. + /// + internal override void OnStartTimer() { + StartAcceleration(); + } + + /// + /// Overridden to start/end acceleration. + /// + internal override void OnStopTimer() { + StopAcceleration(); + } + + /// + /// + /// + /// Converts the text displayed in the up-down control to a + /// numeric value and evaluates it. + /// + /// + protected void ParseEditText() { + + Debug.Assert(UserEdit == true, "ParseEditText() - UserEdit == false"); + + try { + // VSWhidbey 173332: Verify that the user is not starting the string with a "-" + // before attempting to set the Value property since a "-" is a valid character with + // which to start a string representing a negative number. + if (!string.IsNullOrEmpty(Text) && + !(Text.Length == 1 && Text == "-")) { + if (Hexadecimal) { + Value = Constrain(Convert.ToDecimal(Convert.ToInt32(Text, 16))); + } + else { + Value = Constrain(Decimal.Parse(Text, CultureInfo.CurrentCulture)); + } + } + } + catch { + // Leave value as it is + } + finally { + UserEdit = false; + } + } + + /// + /// Updates the index of the UpDownNumericAcceleration entry to use (if needed). + /// + private void SetNextAcceleration() { + // Spinning will check if accelerations is null. + if(this.Spinning && this.accelerationsCurrentIndex < (this.accelerations.Count - 1)) { // if index not the last entry ... + // Ticks are in 100-nanoseconds (1E-7 seconds). + long nowTicks = DateTime.Now.Ticks; + long buttonPressedElapsedTime = nowTicks - this.buttonPressedStartTime; + long accelerationInterval = 10000000L * this.accelerations[this.accelerationsCurrentIndex + 1].Seconds; // next entry. + + // If Up/Down button pressed for more than the current acceleration entry interval, get next entry in the accel table. + if( buttonPressedElapsedTime > accelerationInterval ) + { + this.buttonPressedStartTime = nowTicks; + this.accelerationsCurrentIndex++; + } + } + } + + private void ResetIncrement() { + Increment = DefaultIncrement; + } + + private void ResetMaximum() { + Maximum = DefaultMaximum; + } + + private void ResetMinimum() { + Minimum = DefaultMinimum; + } + + private void ResetValue() { + Value = DefaultValue; + } + + /// + /// Indicates whether the property should be + /// persisted. + /// + private bool ShouldSerializeIncrement() { + return !Increment.Equals(NumericUpDown.DefaultIncrement); + } + + /// + /// Indicates whether the property should be persisted. + /// + private bool ShouldSerializeMaximum() { + return !Maximum.Equals(NumericUpDown.DefaultMaximum); + } + + /// + /// Indicates whether the property should be persisted. + /// + private bool ShouldSerializeMinimum() { + return !Minimum.Equals(NumericUpDown.DefaultMinimum); + } + + /// + /// Indicates whether the property should be persisted. + /// + private bool ShouldSerializeValue() { + return !Value.Equals(NumericUpDown.DefaultValue); + } + + + /// + /// Records when UpDownButtons are pressed to enable acceleration. + /// + private void StartAcceleration() { + this.buttonPressedStartTime = DateTime.Now.Ticks; + } + + /// + /// Reset when UpDownButtons are pressed. + /// + private void StopAcceleration() { + this.accelerationsCurrentIndex = InvalidValue; + this.buttonPressedStartTime = InvalidValue; + } + + /// + /// + /// Provides some interesting info about this control in String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + s += ", Minimum = " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum = " + Maximum.ToString(CultureInfo.CurrentCulture); + return s; + } + + /// + /// + /// + /// Increments the value of the up-down control. + /// + /// + public override void UpButton() { + SetNextAcceleration(); + + if (UserEdit) { + ParseEditText(); + } + + Decimal newValue = currentValue; + + // Operations on Decimals can throw OverflowException. + // + try{ + newValue += this.Increment; + + if (newValue > maximum){ + newValue = maximum; + if( this.Spinning){ + StopAcceleration(); + } + } + } + catch( OverflowException ){ + newValue = maximum; + } + + Value = newValue; + } + + private string GetNumberText(decimal num) { + string text; + + if (Hexadecimal) { + text = ((Int64)num).ToString("X", CultureInfo.InvariantCulture); + Debug.Assert(text == text.ToUpper(CultureInfo.InvariantCulture), "GetPreferredSize assumes hex digits to be uppercase."); + } + else { + text = num.ToString((ThousandsSeparator ? "N" : "F") + DecimalPlaces.ToString(CultureInfo.CurrentCulture), CultureInfo.CurrentCulture); + } + return text; + } + + /// + /// + /// + /// Displays the current value of the up-down control in the appropriate format. + /// + /// + protected override void UpdateEditText() { + // If we're initializing, we don't want to update the edit text yet, + // just in case the value is invalid. + if (initializing) { + return; + } + + // If the current value is user-edited, then parse this value before reformatting + if (UserEdit) { + ParseEditText(); + } + + // VSWhidbey 173332: Verify that the user is not starting the string with a "-" + // before attempting to set the Value property since a "-" is a valid character with + // which to start a string representing a negative number. + if (currentValueChanged || (!string.IsNullOrEmpty(Text) && + !(Text.Length == 1 && Text == "-"))) { + + currentValueChanged = false; + ChangingText = true; + + // Make sure the current value is within the min/max + Debug.Assert(minimum <= currentValue && currentValue <= maximum, + "DecimalValue lies outside of [minimum, maximum]"); + + Text = GetNumberText(currentValue); + Debug.Assert(ChangingText == false, "ChangingText should have been set to false"); + } + } + + /// + /// + /// + /// Validates and updates + /// the text displayed in the up-down control. + /// + /// + protected override void ValidateEditText() { + + // See if the edit text parses to a valid decimal + ParseEditText(); + UpdateEditText(); + } + + // This is not a breaking change -- Even though this control previously autosized to hieght, + // it didn't actually have an AutoSize property. The new AutoSize property enables the + // smarter behavior. + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + int height = PreferredHeight; + + int baseSize = Hexadecimal ? 16 : 10; + int digit = GetLargestDigit(0, baseSize); + // The floor of log is intentionally 1 less than the number of digits. We initialize + // testNumber to account for the missing digit. + int numDigits = (int)Math.Floor(Math.Log(Math.Max(-(double)Minimum, (double)Maximum), baseSize)); + int maxDigits; + if (this.Hexadecimal) { + maxDigits = (int)Math.Floor(Math.Log(Int64.MaxValue, baseSize)); + } + else { + maxDigits = (int)Math.Floor(Math.Log((double)decimal.MaxValue, baseSize)); + } + bool maxDigitsReached = numDigits >= maxDigits; + decimal testNumber; + + // preinitialize testNumber with the leading digit + if(digit != 0 || numDigits == 1) { + testNumber = digit; + } else { + // zero can not be the leading digit if we need more than + // one digit. (0*baseSize = 0 in the loop below) + testNumber = GetLargestDigit(1, baseSize); + } + + if (maxDigitsReached) { + // Prevent bug VSWhidbey 555288. + // decimal.MaxValue is 79228162514264337593543950335 + // but 99999999999999999999999999999 is not a valid value for testNumber. + numDigits = maxDigits - 1; + } + + // e.g., if the lagest digit is 7, and we can have 3 digits, the widest string would be "777" + for(int i = 0; i < numDigits; i++) { + testNumber = testNumber * baseSize + digit; + } + + int textWidth = TextRenderer.MeasureText(GetNumberText(testNumber), this.Font).Width; + + if (maxDigitsReached) { + string shortText; + if (this.Hexadecimal) { + shortText = ((Int64) testNumber).ToString("X", CultureInfo.InvariantCulture); + } + else { + shortText = testNumber.ToString(CultureInfo.CurrentCulture); + } + int shortTextWidth = TextRenderer.MeasureText(shortText, this.Font).Width; + // Adding the width of the one digit that was dropped earlier. + // This assumes that no additional thousand separator is added by that digit which is correct. + textWidth += shortTextWidth / (numDigits+1); + } + + // Call AdjuctWindowRect to add space for the borders + int width = SizeFromClientSize(textWidth, height).Width + upDownButtons.Width; + return new Size(width, height) + Padding.Size; + } + + private int GetLargestDigit(int start, int end) { + int largestDigit = -1; + int digitWidth = -1; + + for(int i = start; i < end; i++) { + char ch; + if(i < 10) { + ch = i.ToString(CultureInfo.InvariantCulture)[0]; + } else { + ch = (char)('A' + (i - 10)); + } + + Size digitSize = TextRenderer.MeasureText(ch.ToString(), this.Font); + + if(digitSize.Width >= digitWidth) { + digitWidth = digitSize.Width; + largestDigit = i; + } + } + Debug.Assert(largestDigit != -1 && digitWidth != -1, "Failed to find largest digit."); + return largestDigit; + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class NumericUpDownAccessibleObject : ControlAccessibleObject { + + public NumericUpDownAccessibleObject(NumericUpDown owner) : base(owner) { + } + + /// + /// Gets or sets the accessible name. + /// + public override string Name { + get { + string baseName = base.Name; + return ((NumericUpDown)Owner).GetAccessibleName(baseName); + } + set { + base.Name = value; + } + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + if (AccessibilityImprovements.Level1) { + return AccessibleRole.SpinButton; + } + else { + return AccessibleRole.ComboBox; + } + } + } + } + + public override AccessibleObject GetChild(int index) { + if (index >= 0 && index < GetChildCount()) { + + // TextBox child + // + if (index == 0) { + return ((UpDownBase)Owner).TextBox.AccessibilityObject.Parent; + } + + // Up/down buttons + // + if (index == 1) { + return ((UpDownBase)Owner).UpDownButtonsInternal.AccessibilityObject.Parent; + } + } + + return null; + } + + public override int GetChildCount() { + return 2; + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/NumericUpDownAcceleration.cs b/WindowsForms/Managed/System/WinForms/NumericUpDownAcceleration.cs new file mode 100644 index 000000000..c3f12f806 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NumericUpDownAcceleration.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + + /// + /// Comprises the information specifying how acceleration should be performed + /// on a Windows up-down control when the up/down button is pressed for certain + /// amount of time. + /// + public class NumericUpDownAcceleration + { + private Int32 seconds; // Ideally we would use UInt32 but it is not CLS-compliant. + private Decimal increment; // Ideally we would use UInt32 but NumericUpDown uses Decimal values. + + public NumericUpDownAcceleration(Int32 seconds, Decimal increment) + { + if (seconds < 0) + { + throw new ArgumentOutOfRangeException("seconds", seconds, SR.GetString(SR.NumericUpDownLessThanZeroError)); + } + + if (increment < Decimal.Zero) + { + throw new ArgumentOutOfRangeException("increment", increment, SR.GetString(SR.NumericUpDownLessThanZeroError)); + } + + this.seconds = seconds; + this.increment = increment; + } + + /// + /// Determines the amount of time for the UpDown control to wait to set the increment + /// step when holding the up/down button. + /// + public Int32 Seconds + { + get + { + return this.seconds; + } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException("seconds", value, SR.GetString(SR.NumericUpDownLessThanZeroError)); + } + this.seconds = value; + } + } + + /// + /// Determines the amount to increment by. + /// + public Decimal Increment { + + get + { + return this.increment; + } + + set + { + if (value < Decimal.Zero) + { + throw new ArgumentOutOfRangeException("increment", value, SR.GetString(SR.NumericUpDownLessThanZeroError)); + } + this.increment = value; + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/NumericUpDownAccelerationCollection.cs b/WindowsForms/Managed/System/WinForms/NumericUpDownAccelerationCollection.cs new file mode 100644 index 000000000..2e56ffbea --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/NumericUpDownAccelerationCollection.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + + /// + /// Represents a SORTED collection of NumericUpDownAcceleration objects in the NumericUpDown Control. + /// The elements in the collection are sorted by the NumericUpDownAcceleration.Seconds property. + /// + [ListBindable(false)] + public class NumericUpDownAccelerationCollection : MarshalByRefObject, ICollection, IEnumerable + { + List items; + + + /// ICollection implementation. + + /// + /// Adds an item (NumericUpDownAcceleration object) to the ICollection. + /// The item is added preserving the collection sorted. + /// + public void Add(NumericUpDownAcceleration acceleration) + { + if( acceleration == null ) + { + throw new ArgumentNullException("acceleration"); + } + + // Keep the array sorted, insert in the right spot. + int index = 0; + + while( index < this.items.Count ) + { + if( acceleration.Seconds < this.items[index].Seconds ) + { + break; + } + index++; + } + this.items.Insert(index, acceleration); + } + + /// + /// Removes all items from the ICollection. + /// + public void Clear() + { + this.items.Clear(); + } + + /// + /// Determines whether the IList contains a specific value. + /// + public bool Contains(NumericUpDownAcceleration acceleration) + { + return this.items.Contains(acceleration); + } + + /// + /// Copies the elements of the ICollection to an Array, starting at a particular Array index. + /// + public void CopyTo(NumericUpDownAcceleration[] array, int index) + { + this.items.CopyTo(array, index); + } + + /// + /// Gets the number of elements contained in the ICollection. + /// + public int Count + { + get {return this.items.Count;} + } + + /// + /// Gets a value indicating whether the ICollection is read-only. + /// This collection property returns false always. + /// + public bool IsReadOnly + { + get {return false;} + } + + /// + /// Removes the specified item from the ICollection. + /// + public bool Remove(NumericUpDownAcceleration acceleration) + { + return this.items.Remove(acceleration); + } + + /// IEnumerable implementation. + + + /// + /// Returns an enumerator that can iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return ((IEnumerable)items).GetEnumerator(); + } + + /// NumericUpDownAccelerationCollection methods. + + /// + /// Class constructor. + /// + public NumericUpDownAccelerationCollection() + { + this.items = new List(); + } + + /// + /// Adds the elements of specified array to the collection, keeping the collection sorted. + /// + public void AddRange(params NumericUpDownAcceleration[] accelerations) + { + if (accelerations == null) + { + throw new ArgumentNullException("accelerations"); + } + + // Accept the range only if ALL elements in the array are not null. + foreach (NumericUpDownAcceleration acceleration in accelerations) + { + if (acceleration == null) + { + throw new ArgumentNullException(SR.GetString(SR.NumericUpDownAccelerationCollectionAtLeastOneEntryIsNull)); + } + } + + // The expected array size is typically small (5 items?), so we don't need to try to be smarter about the + // way we add the elements to the collection, just call Add. + foreach (NumericUpDownAcceleration acceleration in accelerations) + { + this.Add(acceleration); + } + } + + /// + /// Gets (ReadOnly) the element at the specified index. In C#, this property is the indexer for + /// the IList class. + /// + public NumericUpDownAcceleration this[int index] + { + get { return this.items[index]; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/OSFeature.cs b/WindowsForms/Managed/System/WinForms/OSFeature.cs new file mode 100644 index 000000000..bd6e13667 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/OSFeature.cs @@ -0,0 +1,167 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Configuration.Assemblies; + using System.Diagnostics; + using System; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// Provides operating-system specific feature queries. + /// + public class OSFeature : FeatureSupport { + + /// + /// + /// Represents the layered, top-level windows feature. This + /// field is read-only. + /// + public static readonly object LayeredWindows = new object(); + + /// + /// + /// Determines if the OS supports themes + /// + public static readonly object Themes = new object(); + + private static OSFeature feature = null; + + private static bool themeSupportTested = false; + private static bool themeSupport = false; + + /// + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + protected OSFeature() { + } + + /// + /// + /// Represents the instance of to use for feature queries. This property is read-only. + /// + public static OSFeature Feature { + get { + if (feature == null) + feature = new OSFeature(); + + return feature; + } + } + + /// + /// + /// Retrieves the version of the specified feature currently available on the system. + /// + public override Version GetVersionPresent(object feature) { + Version featureVersion = null; + if (feature == LayeredWindows) { + if (Environment.OSVersion.Platform == System.PlatformID.Win32NT + && Environment.OSVersion.Version.CompareTo(new Version(5, 0, 0, 0)) >= 0) { + + featureVersion = new Version(0, 0, 0, 0); + } + } + else if (feature == Themes) { + if (!themeSupportTested) { + try { + SafeNativeMethods.IsAppThemed(); + themeSupport = true; + } + catch { + themeSupport = false; + } + themeSupportTested = true; + } + + if (themeSupport) { + featureVersion = new Version(0, 0, 0, 0); + } + } + return featureVersion; + } + + internal bool OnXp { + get { + bool onXp = false; + if (Environment.OSVersion.Platform == System.PlatformID.Win32NT) { + onXp = Environment.OSVersion.Version.CompareTo(new Version(5, 1, 0, 0)) >= 0; + } + return onXp; + } + } + + internal bool OnWin2k { + get { + bool onWin2k = false; + if (Environment.OSVersion.Platform == System.PlatformID.Win32NT) { + onWin2k = Environment.OSVersion.Version.CompareTo(new Version(5, 0, 0, 0)) >= 0; + } + return onWin2k; + } + } + + /// + /// + /// Retrieves whether SystemParameterType is supported on the Current OS version. + /// + public static bool IsPresent(SystemParameter enumVal) { + switch (enumVal) { + case SystemParameter.DropShadow: + return Feature.OnXp; + + + case SystemParameter.FlatMenu: + return Feature.OnXp; + + + case SystemParameter.FontSmoothingContrastMetric: + return Feature.OnXp; + + + case SystemParameter.FontSmoothingTypeMetric: + return Feature.OnXp; + + + case SystemParameter.MenuFadeEnabled: + return Feature.OnWin2k; + + + case SystemParameter.SelectionFade: + return Feature.OnWin2k; + + + case SystemParameter.ToolTipAnimationMetric: + return Feature.OnWin2k; + + + case SystemParameter.UIEffects: + return Feature.OnWin2k; + + + case SystemParameter.CaretWidthMetric: + return Feature.OnWin2k; + + + case SystemParameter.VerticalFocusThicknessMetric: + return Feature.OnXp; + + + case SystemParameter.HorizontalFocusThicknessMetric: + return Feature.OnXp; + } + return false; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/OpacityConverter.cs b/WindowsForms/Managed/System/WinForms/OpacityConverter.cs new file mode 100644 index 000000000..55f7e7c0b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/OpacityConverter.cs @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// OpacityConverter is a class that can be used to convert + /// opacity values from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class OpacityConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is string) { + + string text = ((string)value).Replace('%', ' ').Trim(); + double val = Double.Parse(text, CultureInfo.CurrentCulture); + int indexOfPercent = ((string)value).IndexOf("%"); + if (indexOfPercent > 0 && (val >= 0.0 && val <= 1.0)) { + val /= 100.0; + text = val.ToString(CultureInfo.CurrentCulture); + } + double percent = 1.0; + + try { + percent = (double)TypeDescriptor.GetConverter(typeof(double)).ConvertFrom(context, culture, text); + + // assume they meant a percentage if it is > 1.0, else + // they actually typed the correct double... + // + if (percent > 1.0) { + percent /= 100.0; + } + } + catch (FormatException e) { + throw new FormatException(SR.GetString(SR.InvalidBoundArgument, + "Opacity", + text, + "0%", + "100%"), e); + } + + // Now check to see if it is within our bounds. + // + if (percent < 0.0 || percent > 1.0) { + throw new FormatException(SR.GetString(SR.InvalidBoundArgument, + "Opacity", + text, + "0%", + "100%")); + } + + return percent; + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string)) { + double val = (double)value; + int perc = (int)(val * 100.0); + return perc.ToString(CultureInfo.CurrentCulture) + "%"; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/OpenFileDialog.cs b/WindowsForms/Managed/System/WinForms/OpenFileDialog.cs new file mode 100644 index 000000000..49648374e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/OpenFileDialog.cs @@ -0,0 +1,310 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + + using System; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using CodeAccessPermission = System.Security.CodeAccessPermission; + using System.Security.Permissions; + using System.IO; + using System.ComponentModel; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// + /// Represents a common dialog box + /// that displays the control that allows the user to open a file. This class + /// cannot be inherited. + /// + /// + [SRDescription(SR.DescriptionOpenFileDialog)] + public sealed class OpenFileDialog : FileDialog + { + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box displays a + /// warning if the user specifies a file name that does not exist. + /// + /// + [ + DefaultValue(true), + SRDescription(SR.OFDcheckFileExistsDescr) + ] + public override bool CheckFileExists + { + get + { + return base.CheckFileExists; + } + set + { + base.CheckFileExists = value; + } + } + + /// + /// + /// + /// Gets or sets a value + /// indicating whether the dialog box allows multiple files to be selected. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.OFDmultiSelectDescr) + ] + public bool Multiselect + { + get + { + return GetOption(NativeMethods.OFN_ALLOWMULTISELECT); + } + set + { + SetOption(NativeMethods.OFN_ALLOWMULTISELECT, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether + /// the read-only check box is selected. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.OFDreadOnlyCheckedDescr) + ] + public bool ReadOnlyChecked + { + get + { + return GetOption(NativeMethods.OFN_READONLY); + } + set + { + SetOption(NativeMethods.OFN_READONLY, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog contains a read-only check box. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.OFDshowReadOnlyDescr) + ] + public bool ShowReadOnly + { + get + { + return !GetOption(NativeMethods.OFN_HIDEREADONLY); + } + set + { + SetOption(NativeMethods.OFN_HIDEREADONLY, !value); + } + } + + /// + /// + /// + /// Opens the file selected by the user with read-only permission. The file + /// attempted is specified by the property. + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: filename is snapped directly at the beginning of the function. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public Stream OpenFile() + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded"); + IntSecurity.FileDialogOpenFile.Demand(); + + string filename = FileNamesInternal[0]; + + if (filename == null || (filename.Length == 0)) + throw new ArgumentNullException("FileName"); + + Stream s = null; + + // SECREVIEW : We demanded the FileDialog permission above, so it is safe + // : to assert this here. Since the user picked the file, it + // : is OK to give them readonly access to the stream. + // + new FileIOPermission(FileIOPermissionAccess.Read, IntSecurity.UnsafeGetFullPath(filename)).Assert(); + try + { + s = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + return s; + } + + /// + /// + /// + /// Resets all properties to their default values. + /// + /// + public override void Reset() + { + base.Reset(); + SetOption(NativeMethods.OFN_FILEMUSTEXIST, true); + } + + internal override void EnsureFileDialogPermission() + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded in OpenFileDialog.RunFileDialog"); + IntSecurity.FileDialogOpenFile.Demand(); + } + + /// + /// + /// Displays a file open dialog. + /// + /// + internal override bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn) + { + //We have already done the demand in EnsureFileDialogPermission but it doesn't hurt to do it again + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded in OpenFileDialog.RunFileDialog"); + IntSecurity.FileDialogOpenFile.Demand(); + + bool result = UnsafeNativeMethods.GetOpenFileName(ofn); + if (!result) + { + // Something may have gone wrong - check for error condition + // + int errorCode = SafeNativeMethods.CommDlgExtendedError(); + switch (errorCode) + { + case NativeMethods.FNERR_INVALIDFILENAME: + throw new InvalidOperationException(SR.GetString(SR.FileDialogInvalidFileName, FileName)); + + case NativeMethods.FNERR_SUBCLASSFAILURE: + throw new InvalidOperationException(SR.GetString(SR.FileDialogSubLassFailure)); + + case NativeMethods.FNERR_BUFFERTOOSMALL: + throw new InvalidOperationException(SR.GetString(SR.FileDialogBufferTooSmall)); + } + } + return result; + } + + internal override string[] ProcessVistaFiles(FileDialogNative.IFileDialog dialog) + { + FileDialogNative.IFileOpenDialog openDialog = (FileDialogNative.IFileOpenDialog)dialog; + if (Multiselect) + { + FileDialogNative.IShellItemArray results; + openDialog.GetResults(out results); + uint count; + results.GetCount(out count); + string[] files = new string[count]; + for (uint i = 0; i < count; ++i) + { + FileDialogNative.IShellItem item; + results.GetItemAt(i, out item); + files[unchecked((int)i)] = GetFilePathFromShellItem(item); + } + return files; + } + else + { + FileDialogNative.IShellItem item; + openDialog.GetResult(out item); + return new string[] { GetFilePathFromShellItem(item) }; + } + } + + internal override FileDialogNative.IFileDialog CreateVistaDialog() + { + return new FileDialogNative.NativeFileOpenDialog(); + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts") + ] + public string SafeFileName + { + get + { + new FileIOPermission(PermissionState.Unrestricted).Assert(); + string fullPath = FileName; + CodeAccessPermission.RevertAssert(); + if (string.IsNullOrEmpty(fullPath)) + { + return ""; + } + + string safePath = RemoveSensitivePathInformation(fullPath); + return safePath; + } + } + + private static string RemoveSensitivePathInformation(string fullPath) + { + return System.IO.Path.GetFileName(fullPath); + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays"), + SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts") + ] + public string[] SafeFileNames + { + get + { + new FileIOPermission(PermissionState.Unrestricted).Assert(); + string[] fullPaths = FileNames; + CodeAccessPermission.RevertAssert(); + if (null == fullPaths || 0 == fullPaths.Length) + { return new string[0]; } + string[] safePaths = new string[fullPaths.Length]; + for (int i = 0; i < safePaths.Length; ++i) + { + safePaths[i] = RemoveSensitivePathInformation(fullPaths[i]); + } + return safePaths; + } + } + + internal override bool SettingsSupportVistaDialog + { + get + { + return base.SettingsSupportVistaDialog && !this.ShowReadOnly; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Orientation.cs b/WindowsForms/Managed/System/WinForms/Orientation.cs new file mode 100644 index 000000000..3b190dd3c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Orientation.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies the orientation of controls or elements of controls. + /// + /// + /// + public enum Orientation { + /// + /// + /// + /// The control or element is oriented horizontally. + /// + /// + Horizontal = 0, + + /// + /// + /// + /// The control or element is oriented vertically. + /// + /// + Vertical = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/OwnerDrawPropertyBag.cs b/WindowsForms/Managed/System/WinForms/OwnerDrawPropertyBag.cs new file mode 100644 index 000000000..5a9e68071 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/OwnerDrawPropertyBag.cs @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Internal; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + using System.Security.Permissions; + + + /// + /// + /// + /// Class used to pass new font/color information around for "partial" ownerdraw list/treeview items. + /// + /// + // + [SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly")] + [Serializable] + public class OwnerDrawPropertyBag : MarshalByRefObject, ISerializable { + Font font = null; + Color foreColor = Color.Empty; + Color backColor = Color.Empty; + Control.FontHandleWrapper fontWrapper = null; + private static object internalSyncObject = new object(); + /** + * Constructor used in deserialization + * Has to be protected because OwnerDrawPropertyBag is not sealed. FxCop Rule CA2229. + */ + protected OwnerDrawPropertyBag(SerializationInfo info, StreamingContext context) { + foreach (SerializationEntry entry in info) { + if (entry.Name == "Font") { + // SEC + font = (Font) entry.Value; + } + else if (entry.Name =="ForeColor") { + // SEC + foreColor =(Color)entry.Value; + } + else if (entry.Name =="BackColor") { + // SEC + backColor = (Color)entry.Value; + } + } + } + + internal OwnerDrawPropertyBag(){ + } + + /// + /// + /// [To be supplied.] + /// + public Font Font { + get { + return font; + } + set { + font = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public Color ForeColor { + get { + return foreColor; + } + set { + foreColor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public Color BackColor { + get { + return backColor; + } + set { + backColor = value; + } + } + + internal IntPtr FontHandle { + get { + if (fontWrapper == null) { + fontWrapper = new Control.FontHandleWrapper(Font); + } + return fontWrapper.Handle; + } + } + + /// + /// + /// Returns whether or not this property bag contains all default values (is empty) + /// + public virtual bool IsEmpty() { + return (Font == null && foreColor.IsEmpty && backColor.IsEmpty); + } + + /// + /// + /// Copies the bag. Always returns a valid ODPB object + /// + public static OwnerDrawPropertyBag Copy(OwnerDrawPropertyBag value) { + lock(internalSyncObject) { + OwnerDrawPropertyBag ret = new OwnerDrawPropertyBag(); + if (value == null) return ret; + ret.backColor = value.backColor; + ret.foreColor = value.foreColor; + ret.Font = value.font; + return ret; + } + } + + /// + /// + /// ISerializable private implementation + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + si.AddValue("BackColor", BackColor); + si.AddValue("ForeColor", ForeColor); + si.AddValue("Font", Font); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Padding.cs b/WindowsForms/Managed/System/WinForms/Padding.cs new file mode 100644 index 000000000..a30492813 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Padding.cs @@ -0,0 +1,421 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Globalization; + + /// + [TypeConverterAttribute(typeof(PaddingConverter))] + [Serializable] + public struct Padding { + private bool _all; + private int _top; + private int _left; + private int _right; + private int _bottom; + + /// + public static readonly Padding Empty = new Padding(0); + + /// + public Padding(int all) { + _all = true; + _top = _left = _right = _bottom = all; + Debug_SanityCheck(); + } + + /// + public Padding(int left, int top, int right, int bottom) { + _top = top; + _left = left; + _right = right; + _bottom = bottom; + _all = _top == _left && _top == _right && _top == _bottom; + Debug_SanityCheck(); + } + + /// + [RefreshProperties(RefreshProperties.All)] + public int All { + get { + return _all ? _top : -1; + } + set { + if (_all != true || _top != value) { + _all = true; + _top = _left = _right = _bottom = value; + } + Debug_SanityCheck(); + } + } + + /// + [RefreshProperties(RefreshProperties.All)] + public int Bottom { + get { + if (_all) { + return _top; + } + return _bottom; + } + set { + if (_all || _bottom != value) { + _all = false; + _bottom = value; + } + Debug_SanityCheck(); + } + } + + /// + [RefreshProperties(RefreshProperties.All)] + public int Left { + get { + if (_all) { + return _top; + } + return _left; + } + set { + if (_all || _left != value) { + _all = false; + _left = value; + } + Debug_SanityCheck(); + } + } + + /// + [RefreshProperties(RefreshProperties.All)] + public int Right { + get { + if (_all) { + return _top; + } + return _right; + } + set { + if (_all || _right != value) { + _all = false; + _right = value; + } + Debug_SanityCheck(); + } + } + + /// + [RefreshProperties(RefreshProperties.All)] + public int Top { + get { + return _top; + } + set { + if (_all || _top != value) { + _all = false; + _top = value; + } + Debug_SanityCheck(); + } + } + + /// + [Browsable(false)] + public int Horizontal { + get { + return Left + Right; + } + } + + /// + [Browsable(false)] + public int Vertical { + get { + return Top + Bottom; + } + } + + /// + [Browsable(false)] + public Size Size { + get { + return new Size(Horizontal, Vertical); + } + } + + public static Padding Add(Padding p1, Padding p2) { + // added for FXCop rule: Provide a friendly-name version of the Addition operator + return p1 + p2; + } + + public static Padding Subtract(Padding p1, Padding p2) { + // added for FXCop rule: Provide a friendly-name version of the Subtraction operator + return p1 - p2; + } + + /// + public override bool Equals(object other) { + if(other is Padding) { + return ((Padding)other) == this; + } + return false; + } + + + + /// + /// + /// + /// Performs vector addition of two objects. + /// + /// + public static Padding operator +(Padding p1, Padding p2) { + return new Padding(p1.Left + p2.Left, p1.Top + p2.Top, p1.Right + p2.Right, p1.Bottom + p2.Bottom ); + + } + + /// + /// + /// + /// Contracts a by another + /// . + /// + /// + public static Padding operator -(Padding p1, Padding p2) { + return new Padding(p1.Left - p2.Left, p1.Top - p2.Top, p1.Right - p2.Right, p1.Bottom - p2.Bottom ); + } + + /// + /// + /// Tests whether two objects + /// are identical. + /// + public static bool operator ==(Padding p1, Padding p2) { + return p1.Left == p2.Left && p1.Top == p2.Top && p1.Right == p2.Right && p1.Bottom == p2.Bottom; + } + + /// + /// + /// + /// Tests whether two objects are different. + /// + /// + public static bool operator !=(Padding p1, Padding p2) { + return !(p1 == p2); + } + + /// + public override int GetHashCode() { + // VSWhidbey #130081: Padding class should implement GetHashCode for perf + return Left + ^ WindowsFormsUtils.RotateLeft(Top, 8) + ^ WindowsFormsUtils.RotateLeft(Right, 16) + ^ WindowsFormsUtils.RotateLeft(Bottom, 24); + } + + /// + public override string ToString() { + return "{Left=" + Left.ToString(CultureInfo.CurrentCulture) + ",Top=" + Top.ToString(CultureInfo.CurrentCulture) + ",Right=" + Right.ToString(CultureInfo.CurrentCulture) + ",Bottom=" + Bottom.ToString(CultureInfo.CurrentCulture) + "}"; + } + + private void ResetAll() { + All = 0; + } + + private void ResetBottom() { + Bottom = 0; + } + + private void ResetLeft() { + Left = 0; + } + + private void ResetRight() { + Right = 0; + } + + private void ResetTop() { + Top = 0; + } + + internal void Scale(float dx, float dy) { + _top = (int)((float)_top * dy); + _left = (int)((float)_left * dx); + _right = (int)((float)_right * dx); + _bottom = (int)((float)_bottom * dy); + } + + internal bool ShouldSerializeAll() { + return _all; + } + + [Conditional("DEBUG")] + private void Debug_SanityCheck() { + if(_all) { + Debug.Assert(ShouldSerializeAll(), "_all is true, but ShouldSerializeAll() is false."); + Debug.Assert(All == Left && Left == Top && Top == Right && Right == Bottom, "_all is true, but All/Left/Top/Right/Bottom inconsistent."); + } else { + Debug.Assert(All == -1, "_all is false, but All != -1."); + Debug.Assert(!ShouldSerializeAll(), "ShouldSerializeAll() should not be true when all flag is not set."); + + // The below assert is not true with the current implementation of DockPaddingEdges. + // Debug.Assert(Left != Top || Top != Right || Right != Bottom, "_all is not set, but Left/Top/Right/Bottom are all the same"); + } + } + } + + public class PaddingConverter : TypeConverter { + + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// Converts the given object to the converter's native type. + /// + [ + SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes") // ConvertFromString returns an object + ] + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string valueStr = value as string; + if (valueStr != null) { + valueStr = valueStr.Trim(); + + if (valueStr.Length == 0) { + return null; + } + else { + // Parse 4 integer values. + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = valueStr.Split(new char[] { sep }); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + if (values.Length == 4) { + return new Padding(values[0], values[1], values[2], values[3]); + } + else { + throw new ArgumentException(SR.GetString(SR.TextParseFailedFormat, + "value", + valueStr, + "left, top, right, bottom")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (value is Padding) { + if (destinationType == typeof(string)) { + Padding padding = (Padding)value; + + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args = new string[4]; + int nArg = 0; + + // Note: ConvertToString will raise exception if value cannot be converted. + args[nArg++] = intConverter.ConvertToString(context, culture, padding.Left); + args[nArg++] = intConverter.ConvertToString(context, culture, padding.Top); + args[nArg++] = intConverter.ConvertToString(context, culture, padding.Right); + args[nArg++] = intConverter.ConvertToString(context, culture, padding.Bottom); + + return string.Join(sep, args); + } + else if (destinationType == typeof(InstanceDescriptor)) { + Padding padding = (Padding) value; + + if(padding.ShouldSerializeAll()) { + return new InstanceDescriptor( + typeof(Padding).GetConstructor(new Type[] {typeof(int)}), + new object[] {padding.All}); + } + else { + return new InstanceDescriptor( + typeof(Padding).GetConstructor(new Type[] {typeof(int), typeof(int), typeof(int), typeof(int)}), + new object[] {padding.Left, padding.Top, padding.Right, padding.Bottom}); + } + } + } + return base.ConvertTo(context, culture, value, destinationType); + } + + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + if (context == null) { + throw new ArgumentNullException("context"); + } + if (propertyValues == null) { + throw new ArgumentNullException("propertyValues"); + } + + Padding original = (Padding) context.PropertyDescriptor.GetValue(context.Instance); + + int all = (int)propertyValues["All"]; + if(original.All != all) { + return new Padding(all); + } + else { + return new Padding( + (int)propertyValues["Left"], + (int)propertyValues["Top"], + (int)propertyValues["Right"], + (int)propertyValues["Bottom"]); + } + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Padding), attributes); + return props.Sort(new string[] {"All", "Left", "Top", "Right", "Bottom"}); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PaintEvent.cs b/WindowsForms/Managed/System/WinForms/PaintEvent.cs new file mode 100644 index 000000000..c3cd51dfe --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PaintEvent.cs @@ -0,0 +1,190 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Drawing2D; + using System.Runtime.InteropServices; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + // NOTE: Please keep this class consistent with PrintPageEventArgs. + public class PaintEventArgs : EventArgs, IDisposable { + /// + /// Graphics object with which painting should be done. + /// + private Graphics graphics = null; + + // See ResetGraphics() + private GraphicsState savedGraphicsState = null; + + /// + /// DC (Display context) for obtaining the graphics object. Used to delay getting the graphics + /// object until absolutely necessary (for perf reasons) + /// + private readonly IntPtr dc = IntPtr.Zero; + IntPtr oldPal = IntPtr.Zero; + + /// + /// Rectangle into which all painting should be done. + /// + private readonly Rectangle clipRect; + //private Control paletteSource; + +#if DEBUG + static readonly TraceSwitch PaintEventFinalizationSwitch = new TraceSwitch("PaintEventFinalization", "Tracks the creation and finalization of PaintEvent objects"); + internal static string GetAllocationStack() + { + if (PaintEventFinalizationSwitch.TraceVerbose) + { + return Environment.StackTrace; + } + else + { + return "Enabled 'PaintEventFinalization' trace switch to see stack of allocation"; + } + } + private string AllocationSite = PaintEventArgs.GetAllocationStack(); +#endif + + /// + /// + /// + /// Initializes a new instance of the class with the specified graphics and + /// clipping rectangle. + /// + /// + public PaintEventArgs(Graphics graphics, Rectangle clipRect) { + if( graphics == null ){ + throw new ArgumentNullException("graphics"); + } + + this.graphics = graphics; + this.clipRect = clipRect; + } + + // Internal version of constructor for performance + // We try to avoid getting the graphics object until needed + internal PaintEventArgs(IntPtr dc, Rectangle clipRect) { + Debug.Assert(dc != IntPtr.Zero, "dc is not initialized."); + + this.dc = dc; + this.clipRect = clipRect; + } + + /// + ~PaintEventArgs() { + Dispose(false); + } + + /// + /// + /// + /// Gets the + /// rectangle in which to paint. + /// + /// + public Rectangle ClipRectangle { + get { + return clipRect; + } + } + + /// + /// Gets the HDC this paint event is connected to. If there is no associated + /// HDC, or the GDI+ Graphics object has been created (meaning GDI+ now owns the + /// HDC), 0 is returned. + /// + /// + internal IntPtr HDC { + get { + if (graphics == null) + return dc; + else + return IntPtr.Zero; + } + } + + /// + /// + /// + /// Gets the + /// object used to + /// paint. + /// + /// + public System.Drawing.Graphics Graphics { + get { + if (graphics == null && dc != IntPtr.Zero) { + oldPal = Control.SetUpPalette(dc, false /*force*/, false /*realize*/); + graphics = Graphics.FromHdcInternal(dc); + graphics.PageUnit = GraphicsUnit.Pixel; + savedGraphicsState = graphics.Save(); // See ResetGraphics() below + } + return graphics; + } + } + + // We want a way to dispose the GDI+ Graphics, but we don't want to create one + // simply to dispose it + // cpr: should be internal + /// + /// + /// + /// Disposes + /// of the resources (other than memory) used by the . + /// + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) { +#if DEBUG + Debug.Assert(disposing, "PaintEvent object should be explicitly disposed. Potential GDI multithreading lock issue. Allocation stack:\r\n" + AllocationSite); +#endif + if (disposing) { + //only dispose the graphics object if we created it via the dc. + if (graphics != null && dc != IntPtr.Zero) { + graphics.Dispose(); + } + } + + if (oldPal != IntPtr.Zero && dc != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(this, dc), new HandleRef(this, oldPal), 0); + oldPal = IntPtr.Zero; + } + } + + // If ControlStyles.AllPaintingInWmPaint, we call this method + // after OnPaintBackground so it appears to OnPaint that it's getting a fresh + // Graphics. We want to make sure AllPaintingInWmPaint is purely an optimization, + // and doesn't change behavior, so we need to make sure any clipping regions established + // in OnPaintBackground don't apply to OnPaint. See ASURT 44682. + internal void ResetGraphics() { + if (graphics != null) { + Debug.Assert(dc == IntPtr.Zero || savedGraphicsState != null, "Called ResetGraphics more than once?"); + if (savedGraphicsState != null) { + graphics.Restore(savedGraphicsState); + savedGraphicsState = null; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PaintEventHandler.cs b/WindowsForms/Managed/System/WinForms/PaintEventHandler.cs new file mode 100644 index 000000000..b1259c669 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PaintEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents the method that will handle the event of a class. + /// + /// + public delegate void PaintEventHandler(object sender, PaintEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/Panel.cs b/WindowsForms/Managed/System/WinForms/Panel.cs new file mode 100644 index 000000000..c68d7467a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Panel.cs @@ -0,0 +1,322 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms.Layout; + + /// + /// + /// + /// Represents a + /// control. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("BorderStyle"), + DefaultEvent("Paint"), + Docking(DockingBehavior.Ask), + Designer("System.Windows.Forms.Design.PanelDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionPanel) + ] + public class Panel : ScrollableControl { + + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + + /// + /// + /// Initializes a new instance of the class. + /// + public Panel() + : base() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + TabStop = false; + SetStyle(ControlStyles.Selectable | + ControlStyles.AllPaintingInWmPaint, false); + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + } + + /// + /// + /// Override to re-expose AutoSize. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRDescription(SR.ControlAutoSizeModeDescr), + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true) + ] + public virtual AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + if(ParentInternal != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(ParentInternal.LayoutEngine == DefaultLayout.Instance) { + ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize); + } + } + } + } + + /// + /// + /// Indicates the + /// border style for the control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.None), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.PanelBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + if (borderStyle != value) { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + borderStyle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + + cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); + cp.Style &= (~NativeMethods.WS_BORDER); + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(200, 100); + } + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + // Translating 0,0 from ClientSize to actual Size tells us how much space + // is required for the borders. + Size borderSize = SizeFromClientSize(Size.Empty); + Size totalPadding = borderSize + Padding.Size; + + return LayoutEngine.GetPreferredSize(this, proposedSize - totalPadding) + totalPadding; + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + /// + [DefaultValue(false)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Fires the event indicating that the panel has been resized. + /// Inheriting controls should use this in favour of actually listening to + /// the event, but should not forget to call base.onResize() to + /// ensure that the event is still fired for external listeners. + /// + protected override void OnResize(EventArgs eventargs) { + if (DesignMode && borderStyle == BorderStyle.None) { + Invalidate(); + } + base.OnResize(eventargs); + } + + + internal override void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + base.PrintToMetaFileRecursive(hDC, lParam, bounds); + + using (WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, bounds)) { + using(Graphics g = Graphics.FromHdcInternal(hDC.Handle)) { + ControlPaint.PrintBorder(g, new Rectangle(Point.Empty, Size), BorderStyle, Border3DStyle.Sunken); + } + } + } + + /// + /// + /// + /// + private static string StringFromBorderStyle(BorderStyle value) { + Type borderStyleType = typeof(BorderStyle); + return (ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) ? (borderStyleType.ToString() + "." + value.ToString()) : "[Invalid BorderStyle]"; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", BorderStyle: " + StringFromBorderStyle(borderStyle); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PictureBox.cs b/WindowsForms/Managed/System/WinForms/PictureBox.cs new file mode 100644 index 000000000..cc7b28f1e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PictureBox.cs @@ -0,0 +1,1441 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.IO; + using System.Security.Permissions; + using System.Drawing; + using System.Net; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Threading; + using System.Windows.Forms.Layout; + using Microsoft.Win32; + + /// + /// + /// Displays an image that can be a graphic from a bitmap, + /// icon, or metafile, as well as from + /// an enhanced metafile, JPEG, or GIF files. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Image"), + DefaultBindingProperty("Image"), + Docking(DockingBehavior.Ask), + Designer("System.Windows.Forms.Design.PictureBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionPictureBox) + ] + public class PictureBox : Control, ISupportInitialize { + + /// + /// + /// The type of border this control will have. + /// + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + + /// + /// + /// The image being displayed. + /// + private Image image; + + /// + /// + /// Controls how the image is placed within our bounds, or how we are + /// sized to fit said image. + /// + private PictureBoxSizeMode sizeMode = PictureBoxSizeMode.Normal; + private Size savedSize; + + bool currentlyAnimating; + + // Instance members for asynchronous behavior + private AsyncOperation currentAsyncLoadOperation = null; + private string imageLocation; + private Image initialImage; + private Image errorImage; + private int contentLength; + private int totalBytesRead; + private MemoryStream tempDownloadStream; + private const int readBlockSize = 4096; + private byte[] readBuffer = null; + private ImageInstallationType imageInstallationType; + private SendOrPostCallback loadCompletedDelegate = null; + private SendOrPostCallback loadProgressDelegate = null; + private bool handleValid = false; + private object internalSyncObject = new object(); + + // These default images will be demand loaded. + private Image defaultInitialImage = null; + private Image defaultErrorImage = null; + + [ ThreadStatic ] + private static Image defaultInitialImageForThread = null; + + [ ThreadStatic ] + private static Image defaultErrorImageForThread = null; + + + private static readonly object defaultInitialImageKey = new object(); + private static readonly object defaultErrorImageKey = new object(); + private static readonly object loadCompletedKey = new object(); + private static readonly object loadProgressChangedKey = new object(); + + + private const int PICTUREBOXSTATE_asyncOperationInProgress = 0x00000001; + private const int PICTUREBOXSTATE_cancellationPending = 0x00000002; + private const int PICTUREBOXSTATE_useDefaultInitialImage = 0x00000004; + private const int PICTUREBOXSTATE_useDefaultErrorImage = 0x00000008; + private const int PICTUREBOXSTATE_waitOnLoad = 0x00000010; + private const int PICTUREBOXSTATE_needToLoadImageLocation = 0x00000020; + private const int PICTUREBOXSTATE_inInitialization = 0x00000040; + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 pictureBoxState; // see PICTUREBOXSTATE_ consts above + + // http://msdn.microsoft.com/en-us/library/93z9ee4x(v=VS.100).aspx + // if we load an image from a stream, + // we must keep the stream open for the lifetime of the Image + StreamReader localImageStreamReader = null; + Stream uriImageStream = null; + + /// + /// + /// Creates a new picture with all default properties and no + /// Image. The default PictureBox.SizeMode will be PictureBoxSizeMode.NORMAL. + /// + /// + public PictureBox() { + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + pictureBoxState = new System.Collections.Specialized.BitVector32(PICTUREBOXSTATE_useDefaultErrorImage | + PICTUREBOXSTATE_useDefaultInitialImage); + + SetStyle(ControlStyles.Opaque |ControlStyles.Selectable , false); + SetStyle(ControlStyles.OptimizedDoubleBuffer|ControlStyles.SupportsTransparentBackColor, true); + + + TabStop = false; + savedSize = Size; + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + + /// + /// + /// Indicates the + /// border style for the control. + /// + [ + DefaultValue(BorderStyle.None), + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.PictureBoxBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + RecreateHandle(); + AdjustSize(); + } + } + } + + // Try to build a URI, but if that fails, that means it's a relative + // path, and we treat it as relative to the working directory (which is + // what GetFullPath uses). + private Uri CalculateUri(string path) + { + Uri uri; + try + { + uri = new Uri(path); + } + catch (UriFormatException) + { + // It's a relative pathname, get its full path as a file. + path = Path.GetFullPath(path); + uri = new Uri(path); + } + return uri; + } + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxCancelAsyncDescr) + ] + public void CancelAsync() + { + pictureBoxState[PICTUREBOXSTATE_cancellationPending] = true; + } + + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool CausesValidation { + get { + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, 50); + } + } + + /// + [ + SRCategory(SR.CatAsynchronous), + Localizable(true), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PictureBoxErrorImageDescr) + ] + public Image ErrorImage { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + // Strange pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] approach used + // here to avoid statically loading the default bitmaps from resources at + // runtime when they're never used. + + if (errorImage == null && pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage]) + { + if (defaultErrorImage == null) + { + // Can't share images across threads. + if (defaultErrorImageForThread == null) + { + defaultErrorImageForThread = + new Bitmap(typeof(PictureBox), + "ImageInError.bmp"); + } + defaultErrorImage = defaultErrorImageForThread; + } + errorImage = defaultErrorImage; + } + return errorImage; + } + set { + if (ErrorImage != value) + { + pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] = false; + + } + errorImage = value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + /// + /// Retrieves the Image that the is currently displaying. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + Bindable(true), + SRDescription(SR.PictureBoxImageDescr) + ] + public Image Image { + [ResourceExposure(ResourceScope.Machine)] + get { + return image; + } + set { + InstallNewImage(value, ImageInstallationType.DirectlySpecified); + } + } + + // The area occupied by the image + /// + [ + SRCategory(SR.CatAsynchronous), + Localizable(true), + DefaultValue(null), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PictureBoxImageLocationDescr) + ] + public string ImageLocation + { + get + { + return imageLocation; + } + set + { + // Reload even if value hasn't changed, since Image itself may + // have changed. + imageLocation = value; + + pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = !string.IsNullOrEmpty(imageLocation); + + // Reset main image if it hasn't been directly specified. + if (string.IsNullOrEmpty(imageLocation) && + imageInstallationType != ImageInstallationType.DirectlySpecified) + { + InstallNewImage(null, ImageInstallationType.DirectlySpecified); + } + + if (WaitOnLoad && !pictureBoxState[PICTUREBOXSTATE_inInitialization] && !string.IsNullOrEmpty(imageLocation)) + { + // Load immediately, so any error will occur synchronously + Load(); + } + + Invalidate(); + } + } + + private Rectangle ImageRectangle { + get { + return ImageRectangleFromSizeMode(sizeMode); + } + } + + private Rectangle ImageRectangleFromSizeMode(PictureBoxSizeMode mode) + { + Rectangle result = LayoutUtils.DeflateRect(ClientRectangle, Padding); + + if (image != null) { + switch (mode) { + case PictureBoxSizeMode.Normal: + case PictureBoxSizeMode.AutoSize: + // Use image's size rather than client size. + result.Size = image.Size; + break; + + case PictureBoxSizeMode.StretchImage: + // Do nothing, was initialized to the available dimensions. + break; + + case PictureBoxSizeMode.CenterImage: + // Center within the available space. + result.X += (result.Width - image.Width) / 2; + result.Y += (result.Height - image.Height) / 2; + result.Size = image.Size; + break; + + case PictureBoxSizeMode.Zoom: + Size imageSize = image.Size; + float ratio = Math.Min((float)ClientRectangle.Width / (float)imageSize.Width, (float)ClientRectangle.Height / (float)imageSize.Height); + result.Width = (int)(imageSize.Width * ratio); + result.Height = (int) (imageSize.Height * ratio); + result.X = (ClientRectangle.Width - result.Width) /2; + result.Y = (ClientRectangle.Height - result.Height) /2; + break; + + default: + Debug.Fail("Unsupported PictureBoxSizeMode value: " + mode); + break; + } + } + + return result; + } + + /// + [ + SRCategory(SR.CatAsynchronous), + Localizable(true), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PictureBoxInitialImageDescr) + ] + public Image InitialImage { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + // Strange pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] approach + // used here to avoid statically loading the default bitmaps from resources at + // runtime when they're never used. + + if (initialImage == null && pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage]) + { + if (defaultInitialImage == null) + { + // Can't share images across threads. + if (defaultInitialImageForThread == null) + { + defaultInitialImageForThread = + new Bitmap(typeof(PictureBox), + "PictureBox.Loading.bmp"); + } + defaultInitialImage = defaultInitialImageForThread; + } + initialImage = defaultInitialImage; + } + return initialImage; + } + set { + if (InitialImage != value) + { + pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] = false; + } + initialImage = value; + } + } + + private void InstallNewImage(Image value, + ImageInstallationType installationType) + { + StopAnimate(); + this.image = value; + + LayoutTransaction.DoLayoutIf(AutoSize, this, this, PropertyNames.Image); + + Animate(); + if (installationType != ImageInstallationType.ErrorOrInitial) + { + AdjustSize(); + } + this.imageInstallationType = installationType; + + Invalidate(); + CommonProperties.xClearPreferredSizeCache(this); + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + // Synchronous load + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoad0Descr) + ] + public void Load() + { + if (imageLocation == null || imageLocation.Length == 0) + { + throw new + InvalidOperationException(SR.GetString(SR.PictureBoxNoImageLocation)); + } + + // If the load and install fails, pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] will be + // false to prevent subsequent attempts. + pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = false; + + Image img; + ImageInstallationType installType = ImageInstallationType.FromUrl; + try + { + DisposeImageStream(); + + Uri uri = CalculateUri(imageLocation); + if (uri.IsFile) + { + localImageStreamReader = new StreamReader(uri.LocalPath); + img = Image.FromStream(localImageStreamReader.BaseStream); + } + else + { + using (WebClient wc = new WebClient()) + { + uriImageStream = wc.OpenRead(uri.ToString()); + img = Image.FromStream(uriImageStream); + } + } + } + catch + { + if (!DesignMode) + { + throw; + } + else + { + // In design mode, just replace with Error bitmap. + img = ErrorImage; + installType = ImageInstallationType.ErrorOrInitial; + } + } + + InstallNewImage(img, installType); + } + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoad1Descr), + SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings") // PM review done + ] + public void Load(String url) + { + this.ImageLocation = url; + this.Load(); + } + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoadAsync0Descr) + ] + public void LoadAsync() + { + if (imageLocation == null || imageLocation.Length == 0) + { + throw new + InvalidOperationException(SR.GetString(SR.PictureBoxNoImageLocation)); + } + + if (pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress]) + { + //We shouldn't throw here: just return (VSWhidbey 160308). + return; + } + + pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress] = true; + + if ((Image == null || + (imageInstallationType == ImageInstallationType.ErrorOrInitial)) + && InitialImage != null) + { + InstallNewImage(InitialImage, ImageInstallationType.ErrorOrInitial); + } + + currentAsyncLoadOperation = AsyncOperationManager.CreateOperation(null); + + if (loadCompletedDelegate == null) + { + loadCompletedDelegate = new SendOrPostCallback(LoadCompletedDelegate); + loadProgressDelegate = new SendOrPostCallback(LoadProgressDelegate); + readBuffer = new byte[readBlockSize]; + } + + pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation] = false; + pictureBoxState[PICTUREBOXSTATE_cancellationPending] = false; + contentLength = -1; + tempDownloadStream = new MemoryStream(); + + WebRequest req = WebRequest.Create(CalculateUri(imageLocation)); + + // Invoke BeginGetResponse on a threadpool thread, as it has + // unpredictable latency, since, on first call, it may load in the + // configuration system (this is NCL bug 20605) + (new WaitCallback(BeginGetResponseDelegate)).BeginInvoke(req, null, null); + } + + // Solely for calling BeginGetResponse itself asynchronously. + private void BeginGetResponseDelegate(object arg) + { + WebRequest req = (WebRequest)arg; + req.BeginGetResponse(new AsyncCallback(GetResponseCallback), req); + } + + private void PostCompleted(Exception error, bool cancelled) + { + AsyncOperation temp = currentAsyncLoadOperation; + currentAsyncLoadOperation = null; + if (temp != null) + { + temp.PostOperationCompleted( + loadCompletedDelegate, + new AsyncCompletedEventArgs(error, cancelled, null)); + } + } + + private void LoadCompletedDelegate(object arg) + { + AsyncCompletedEventArgs e = (AsyncCompletedEventArgs)arg; + + Image img = ErrorImage; + ImageInstallationType installType = ImageInstallationType.ErrorOrInitial; + + if (!e.Cancelled && e.Error == null) + { + // successful completion + try + { + img = Image.FromStream(tempDownloadStream); + installType = ImageInstallationType.FromUrl; + } + catch (Exception error) + { + e = new AsyncCompletedEventArgs(error, false, null); + } + } + + // If cancelled, don't change the image + if (!e.Cancelled) + { + InstallNewImage(img, installType); + } + + tempDownloadStream = null; + pictureBoxState[PICTUREBOXSTATE_cancellationPending] = false; + pictureBoxState[PICTUREBOXSTATE_asyncOperationInProgress] = false; + OnLoadCompleted(e); + } + + private void LoadProgressDelegate(object arg) + { + OnLoadProgressChanged((ProgressChangedEventArgs)arg); + } + + private void GetResponseCallback(IAsyncResult result) + { + if (pictureBoxState[PICTUREBOXSTATE_cancellationPending]) + { + PostCompleted(null, true); + } + else + { + try + { + WebRequest req = (WebRequest)result.AsyncState; + WebResponse webResponse = req.EndGetResponse(result); + + contentLength = (int)webResponse.ContentLength; + totalBytesRead = 0; + + Stream responseStream = webResponse.GetResponseStream(); + + // Continue on with asynchronous reading. + responseStream.BeginRead( + readBuffer, + 0, + readBlockSize, + new AsyncCallback(ReadCallBack), + responseStream); + + } + catch (Exception error) + { + // Since this is on a non-UI thread, we catch any exceptions and + // pass them back as data to the UI-thread. + PostCompleted(error, false); + } + } + } + + private void ReadCallBack(IAsyncResult result) + { + if (pictureBoxState[PICTUREBOXSTATE_cancellationPending]) + { + PostCompleted(null, true); + } + else + { + Stream responseStream = (Stream)result.AsyncState; + try + { + int bytesRead = responseStream.EndRead(result); + + if (bytesRead > 0) + { + totalBytesRead += bytesRead; + tempDownloadStream.Write(readBuffer, 0, bytesRead); + + responseStream.BeginRead( + readBuffer, + 0, + readBlockSize, + new AsyncCallback(ReadCallBack), + responseStream); + + // Report progress thus far, but only if we know total length. + if (contentLength != -1) + { + int progress = (int)(100 * (((float)totalBytesRead) / ((float)contentLength))); + if (currentAsyncLoadOperation != null) + { + currentAsyncLoadOperation.Post(loadProgressDelegate, + new ProgressChangedEventArgs(progress, null)); + } + } + } + else + { + tempDownloadStream.Seek(0, SeekOrigin.Begin); + if (currentAsyncLoadOperation != null) + { + currentAsyncLoadOperation.Post(loadProgressDelegate, + new ProgressChangedEventArgs(100, null)); + } + PostCompleted(null, false); + + // Do this so any exception that Close() throws will be + // dealt with ok. + Stream rs = responseStream; + responseStream = null; + rs.Close(); + } + } + catch (Exception error) + { + // Since this is on a non-UI thread, we catch any exceptions and + // pass them back as data to the UI-thread. + PostCompleted(error, false); + + if (responseStream != null) + { + responseStream.Close(); + } + } + } + } + + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoadAsync1Descr), + SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings") // PM review done + ] + public void LoadAsync(String url) + { + this.ImageLocation = url; + this.LoadAsync(); + } + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoadCompletedDescr) + ] + public event AsyncCompletedEventHandler LoadCompleted + { + add + { + this.Events.AddHandler(loadCompletedKey, value); + } + remove + { + this.Events.RemoveHandler(loadCompletedKey, value); + } + } + + /// + [ + SRCategory(SR.CatAsynchronous), + SRDescription(SR.PictureBoxLoadProgressChangedDescr) + ] + public event ProgressChangedEventHandler LoadProgressChanged + { + add + { + this.Events.AddHandler(loadProgressChangedKey, value); + } + remove + { + this.Events.RemoveHandler(loadProgressChangedKey, value); + } + } + + private void ResetInitialImage() + { + pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage] = true; + initialImage = defaultInitialImage; + } + + private void ResetErrorImage() + { + pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage] = true; + errorImage = defaultErrorImage; + } + + private void ResetImage() + { + InstallNewImage(null, ImageInstallationType.DirectlySpecified); + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override RightToLeft RightToLeft { + get { + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftChanged { + add { + base.RightToLeftChanged += value; + } + remove { + base.RightToLeftChanged -= value; + } + } + + // Be sure not to re-serialized initial image if it's the default. + private bool ShouldSerializeInitialImage() + { + return !pictureBoxState[PICTUREBOXSTATE_useDefaultInitialImage]; + } + + // Be sure not to re-serialized error image if it's the default. + private bool ShouldSerializeErrorImage() + { + return !pictureBoxState[PICTUREBOXSTATE_useDefaultErrorImage]; + } + + // Be sure not to serialize image if it wasn't directly specified + // through the Image property (e.g., if it's a download, or an initial + // or error image) + private bool ShouldSerializeImage() + { + return (imageInstallationType == ImageInstallationType.DirectlySpecified) + && (Image != null); + } + + /// + /// + /// Indicates how the image is displayed. + /// + [ + DefaultValue(PictureBoxSizeMode.Normal), + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.PictureBoxSizeModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public PictureBoxSizeMode SizeMode { + get { + return sizeMode; + } + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)PictureBoxSizeMode.Normal, (int)PictureBoxSizeMode.Zoom)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(PictureBoxSizeMode)); + } + if (this.sizeMode != value) { + if (value == PictureBoxSizeMode.AutoSize) { + this.AutoSize = true; + SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, true); + } + if (value != PictureBoxSizeMode.AutoSize) { + this.AutoSize = false; + SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, false); + savedSize = Size; + } + sizeMode = value; + AdjustSize(); + Invalidate(); + OnSizeModeChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_SIZEMODECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PictureBoxOnSizeModeChangedDescr)] + public event EventHandler SizeModeChanged { + add { + Events.AddHandler(EVENT_SIZEMODECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_SIZEMODECHANGED, value); + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Enter { + add { + base.Enter += value; + } + remove { + base.Enter -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Leave { + add { + base.Leave += value; + } + remove { + base.Leave -= value; + } + } + + /// + /// + /// If the PictureBox has the SizeMode property set to AutoSize, this makes + /// sure that the picturebox is large enough to hold the image. + /// + /// + private void AdjustSize() { + if (sizeMode == PictureBoxSizeMode.AutoSize) { + Size = PreferredSize; + } + else { + Size = savedSize; + } + } + + private void Animate() { + Animate(!DesignMode && Visible && Enabled && ParentInternal != null); + } + + private void StopAnimate() { + Animate(false); + } + + private void Animate(bool animate) { + if (animate != this.currentlyAnimating) { + if (animate) { + if (this.image != null) { + ImageAnimator.Animate(this.image, new EventHandler(this.OnFrameChanged)); + this.currentlyAnimating = animate; + } + } + else { + if (this.image != null) { + ImageAnimator.StopAnimate(this.image, new EventHandler(this.OnFrameChanged)); + this.currentlyAnimating = animate; + } + } + } + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + StopAnimate(); + } + + DisposeImageStream(); + + base.Dispose(disposing); + } + + private void DisposeImageStream() { + if (localImageStreamReader != null) { + localImageStreamReader.Dispose(); + localImageStreamReader = null; + } + if (uriImageStream != null) { + uriImageStream.Dispose(); + localImageStreamReader = null; + } + } + + // Overriding this method allows us to get the caching and clamping the proposedSize/output to + // MinimumSize / MaximumSize from GetPreferredSize for free. + internal override Size GetPreferredSizeCore(Size proposedSize) { + if (image == null) { + return CommonProperties.GetSpecifiedBounds(this).Size; + } + else { + Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; + return image.Size + bordersAndPadding; + } + } + + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + Animate(); + } + + private void OnFrameChanged(object o, EventArgs e) { + if (Disposing || IsDisposed) { + return; + } + // Handle should be created, before calling the BeginInvoke. + // refer VsWhidbey : 315136. + if (InvokeRequired && IsHandleCreated) { + lock (internalSyncObject) { + if (handleValid) { + BeginInvoke(new EventHandler(this.OnFrameChanged), o, e); + } + return; + } + } + + Invalidate(); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleDestroyed(EventArgs e) { + lock (internalSyncObject) { + handleValid = false; + } + base.OnHandleDestroyed(e); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleCreated(EventArgs e) { + lock (internalSyncObject) { + handleValid = true; + } + base.OnHandleCreated(e); + } + + /// + protected virtual void OnLoadCompleted(AsyncCompletedEventArgs e) + { + AsyncCompletedEventHandler handler = (AsyncCompletedEventHandler)(Events[loadCompletedKey]); + if (handler != null) + { + handler(this, e); + } + } + + /// + protected virtual void OnLoadProgressChanged(ProgressChangedEventArgs e) + { + ProgressChangedEventHandler handler = (ProgressChangedEventHandler)(Events[loadProgressChangedKey]); + if (handler != null) + { + handler(this, e); + } + } + + /// + /// + /// Overridden onPaint to make sure that the image is painted correctly. + /// + /// + protected override void OnPaint(PaintEventArgs pe) { + + if (pictureBoxState[PICTUREBOXSTATE_needToLoadImageLocation]) + { + try + { + if (WaitOnLoad) + { + Load(); + } + else + { + LoadAsync(); + } + } + catch (Exception ex) + { //Dont throw but paint error image LoadAsync fails.... + // Microsoft FXCOP + + + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + image = ErrorImage; + } + } + + if (image != null) { + Animate(); + ImageAnimator.UpdateFrames(this.Image); + + // Error and initial image are drawn centered, non-stretched. + Rectangle drawingRect = + (imageInstallationType == ImageInstallationType.ErrorOrInitial) + ? ImageRectangleFromSizeMode(PictureBoxSizeMode.CenterImage) + : ImageRectangle; + + pe.Graphics.DrawImage(image, drawingRect); + + } + + // Windows draws the border for us (see CreateParams) + base.OnPaint(pe); // raise Paint event + } + + + /// + /// + /// [To be supplied.] + /// + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + Animate(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnParentChanged(EventArgs e) { + base.OnParentChanged(e); + Animate(); + } + + /// + /// + /// OnResize override to invalidate entire control in Stetch mode + /// + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (sizeMode == PictureBoxSizeMode.Zoom || sizeMode == PictureBoxSizeMode.StretchImage || sizeMode == PictureBoxSizeMode.CenterImage || BackgroundImage != null) { + Invalidate(); + } + savedSize = Size; + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnSizeModeChanged(EventArgs e) { + EventHandler eh = Events[EVENT_SIZEMODECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", SizeMode: " + sizeMode.ToString("G"); + } + + /// + [ + SRCategory(SR.CatAsynchronous), + Localizable(true), + DefaultValue(false), + SRDescription(SR.PictureBoxWaitOnLoadDescr) + ] + public bool WaitOnLoad { + get { + return pictureBoxState[PICTUREBOXSTATE_waitOnLoad]; + } + set { + pictureBoxState[PICTUREBOXSTATE_waitOnLoad] = value; + } + } + + private enum ImageInstallationType + { + DirectlySpecified, + ErrorOrInitial, + FromUrl + } + + /// + /// + void ISupportInitialize.BeginInit() + { + pictureBoxState[PICTUREBOXSTATE_inInitialization] = true; + } + + /// + /// + void ISupportInitialize.EndInit() + { + Debug.Assert(pictureBoxState[PICTUREBOXSTATE_inInitialization]); + + // Need to do this in EndInit since there's no guarantee of the + // order in which ImageLocation and WaitOnLoad will be set. + if (ImageLocation != null && ImageLocation.Length != 0 && WaitOnLoad) + { + // Load when initialization completes, so any error will occur synchronously + Load(); + } + + pictureBoxState[PICTUREBOXSTATE_inInitialization] = false; + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/PictureBoxSizeMode.cs b/WindowsForms/Managed/System/WinForms/PictureBoxSizeMode.cs new file mode 100644 index 000000000..c09f2aa7e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PictureBoxSizeMode.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how an image is positioned within a . + /// + /// + public enum PictureBoxSizeMode { + + /// + /// + /// + /// The image is placed in the top-left corner of the + /// . The image is clipped if + /// the + /// is to small. + /// + /// + Normal = 0, + + /// + /// + /// + /// The image within the is stretched or shrunk to fit the + /// current size of the . + /// + /// + StretchImage = 1, + + /// + /// + /// The is sized to fit the + /// size of the image that is displayed. + /// + AutoSize = 2, + + /// + /// + /// The image is displayed in the center if the is larger than the image. If the image + /// is larger than the , the center of the picture is placed in the + /// center of the and the outside edges are clipped. + /// + CenterImage = 3, + + /// + /// + /// The size of image is increased or decresed maintaining the aspect ratio. + /// + Zoom = 4 + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PopupEventArgs.cs b/WindowsForms/Managed/System/WinForms/PopupEventArgs.cs new file mode 100644 index 000000000..1f18b37ca --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PopupEventArgs.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// This class contains the information a user needs to paint the ToolTip. + /// + public class PopupEventArgs : CancelEventArgs + { + + private IWin32Window associatedWindow; + private Size size; + private Control associatedControl; + private bool isBalloon; + + + /// + /// + /// Creates a new PopupEventArgs with the given parameters. + /// + public PopupEventArgs(IWin32Window associatedWindow, Control associatedControl, bool isBalloon, Size size) + { + this.associatedWindow = associatedWindow; + this.size = size; + this.associatedControl = associatedControl; + this.isBalloon = isBalloon; + + } + + /// + /// + /// The Associated Window for which the tooltip is being painted. + /// + public IWin32Window AssociatedWindow { + get { + return associatedWindow; + } + } + + /// + /// + /// The control for which the tooltip is being painted. + /// + public Control AssociatedControl { + get { + return associatedControl; + } + + } + + /// + /// + /// Whether the tooltip is Ballooned. + /// + public bool IsBalloon { + get { + return isBalloon; + } + + } + + /// + /// + /// The rectangle outlining the area in which the painting should be done. + /// + public Size ToolTipSize { + get { + return size; + } + set { + size = value; + } + } + } +} + + + diff --git a/WindowsForms/Managed/System/WinForms/PopupEventHandler.cs b/WindowsForms/Managed/System/WinForms/PopupEventHandler.cs new file mode 100644 index 000000000..6d575abb2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PopupEventHandler.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + + /// + /// + /// Handler for the Popup event of the ToolTip control. + /// + public delegate void PopupEventHandler(object sender, PopupEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/PowerStatus.cs b/WindowsForms/Managed/System/WinForms/PowerStatus.cs new file mode 100644 index 000000000..ac399f4be --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PowerStatus.cs @@ -0,0 +1,186 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// To be supplied. + /// + /// + public enum PowerLineStatus + { + /// + /// + /// To be supplied. + /// + Offline = 0, + + /// + /// + /// To be supplied. + /// + Online = 1, + + /// + /// + /// To be supplied. + /// + Unknown = 255 + } + + /// + /// + /// + /// To be supplied. + /// + /// + [Flags] + public enum BatteryChargeStatus + { + /// + /// + /// To be supplied. + /// + High = 1, + + /// + /// + /// To be supplied. + /// + Low = 2, + + /// + /// + /// To be supplied. + /// + Critical = 4, + + /// + /// + /// To be supplied. + /// + Charging = 8, + + /// + /// + /// To be supplied. + /// + NoSystemBattery = 128, + + /// + /// + /// To be supplied. + /// + Unknown = 255 + } + + /// + /// + /// + /// To be supplied. + /// + /// + public enum PowerState + { + /// + /// + /// To be supplied. + /// + Suspend = 0, + + /// + /// + /// To be supplied. + /// + Hibernate = 1 + } + + /// + /// + /// + /// To be supplied. + /// + /// + public class PowerStatus + { + private NativeMethods.SYSTEM_POWER_STATUS systemPowerStatus; + + internal PowerStatus() { + } + + /// + /// + /// [To be supplied.] + /// + public PowerLineStatus PowerLineStatus + { + get + { + UpdateSystemPowerStatus(); + return (PowerLineStatus)systemPowerStatus.ACLineStatus; + } + } + + /// + /// + /// [To be supplied.] + /// + public BatteryChargeStatus BatteryChargeStatus + { + get + { + UpdateSystemPowerStatus(); + return (BatteryChargeStatus)systemPowerStatus.BatteryFlag; + } + } + + /// + /// + /// [To be supplied.] + /// + public int BatteryFullLifetime + { + get + { + UpdateSystemPowerStatus(); + return systemPowerStatus.BatteryFullLifeTime; + } + } + + /// + /// + /// [To be supplied.] + /// + public float BatteryLifePercent + { + get + { + UpdateSystemPowerStatus(); + float lifePercent = systemPowerStatus.BatteryLifePercent / 100f; + return lifePercent > 1f ? 1f : lifePercent; + } + } + + /// + /// + /// [To be supplied.] + /// + public int BatteryLifeRemaining + { + get + { + UpdateSystemPowerStatus(); + return systemPowerStatus.BatteryLifeTime; + } + } + + private void UpdateSystemPowerStatus() { + UnsafeNativeMethods.GetSystemPowerStatus(ref systemPowerStatus); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PreProcessControlState.cs b/WindowsForms/Managed/System/WinForms/PreProcessControlState.cs new file mode 100644 index 000000000..505dcbd22 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PreProcessControlState.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + + /// + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public enum PreProcessControlState { + /// + /// + /// Indicates the message has been processed, and no further processing is necessary + /// + MessageProcessed = 0x00, + /// + /// + /// Indicates the control wants the message and processing should continue + /// + MessageNeeded = 0x01, + /// + /// + /// Indicates the control doesn't care about the message + /// + MessageNotNeeded = 0x02 + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventArgs.cs b/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventArgs.cs new file mode 100644 index 000000000..d71699561 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventArgs.cs @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + + /// + /// + /// + /// + /// Provides data for the PreviewKeyDownEvent + /// + /// + public class PreviewKeyDownEventArgs : EventArgs { + + private readonly Keys _keyData; + private bool _isInputKey; + + /// + /// + /// [To be supplied.] + /// + public PreviewKeyDownEventArgs(Keys keyData) { + _keyData = keyData; + } + + /// + /// + /// + /// Gets a value indicating whether the ALT key was pressed. + /// + /// + public bool Alt { + get { + return (_keyData & Keys.Alt) == Keys.Alt; + } + } + + /// + /// + /// + /// Gets a value indicating whether the CTRL key was pressed. + /// + /// + public bool Control { + get { + return (_keyData & Keys.Control) == Keys.Control; + } + } + + /// + /// + /// + /// Gets the keyboard code for a or + /// event. + /// + /// + //subhag : changed the behaviour of the KeyCode as per the new requirements. + public Keys KeyCode { + [ + // Keys is discontiguous so we have to use Enum.IsDefined. + SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible") + ] + get { + Keys keyGenerated = _keyData & Keys.KeyCode; + + // since Keys can be discontiguous, keeping Enum.IsDefined. + if (!Enum.IsDefined(typeof(Keys),(int)keyGenerated)) + return Keys.None; + else + return keyGenerated; + } + } + + /// + /// + /// + /// Gets the keyboard value for a or + /// event. + /// + /// + //subhag : added the KeyValue as per the new requirements. + public int KeyValue { + get { + return (int)(_keyData & Keys.KeyCode); + } + } + + /// + /// + /// + /// Gets the key data for a or + /// event. + /// + /// + public Keys KeyData { + get { + return _keyData; + } + } + + /// + /// + /// + /// Gets the modifier flags for a or event. + /// This indicates which modifier keys (CTRL, SHIFT, and/or ALT) were pressed. + /// + /// + public Keys Modifiers { + get { + return _keyData & Keys.Modifiers; + } + } + + /// + /// + /// + /// Gets + /// a value indicating whether the SHIFT key was pressed. + /// + /// + public bool Shift { + get { + return (_keyData & Keys.Shift) == Keys.Shift; + } + } + + public bool IsInputKey { + get { + return _isInputKey; + } + set { + _isInputKey = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventHandler.cs b/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventHandler.cs new file mode 100644 index 000000000..21d3ca35c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PreviewKeyDownEventHandler.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle PreviewKeyDown events. + /// + /// + public delegate void PreviewKeyDownEventHandler(object sender, PreviewKeyDownEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/Printing/PageSetupDialog.cs b/WindowsForms/Managed/System/WinForms/Printing/PageSetupDialog.cs new file mode 100644 index 000000000..3f77c4275 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Printing/PageSetupDialog.cs @@ -0,0 +1,434 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Printing; + using System.Runtime.InteropServices; + using System.Security; + using System.Text; + using System.Globalization; + + /// + /// + /// Represents + /// a dialog box that allows users to manipulate page settings, including margins and paper orientation. + /// + [DefaultProperty("Document")] + [SRDescription(SR.DescriptionPageSetupDialog)] + // The only event this dialog has is HelpRequested, which isn't very useful + public sealed class PageSetupDialog : CommonDialog { + // If PrintDocument != null, pageSettings == printDocument.PageSettings + private PrintDocument printDocument = null; + private PageSettings pageSettings = null; + private PrinterSettings printerSettings = null; + + private bool allowMargins; + private bool allowOrientation; + private bool allowPaper; + private bool allowPrinter; + private Margins minMargins; + private bool showHelp; + private bool showNetwork; + private bool enableMetric; + + /// + /// + /// Initializes a new instance of the class. + /// + public PageSetupDialog() { + Reset(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the margins section of the dialog box is enabled. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PSDallowMarginsDescr) + ] + public bool AllowMargins { + get { + return allowMargins; + } + set { + allowMargins = value; + } + } + + /// + /// + /// Gets or sets a value indicating whether the orientation section of the dialog box (landscape vs. portrait) + /// is enabled. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PSDallowOrientationDescr) + ] + public bool AllowOrientation { + get { return allowOrientation;} + set { allowOrientation = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the paper section of the dialog box (paper size and paper source) + /// is enabled. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PSDallowPaperDescr) + ] + public bool AllowPaper { + get { return allowPaper;} + set { allowPaper = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Printer button is enabled. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PSDallowPrinterDescr) + ] + public bool AllowPrinter { + get { return allowPrinter;} + set { allowPrinter = value;} + } + + /// + /// + /// Gets or sets a value indicating the + /// to get page settings from. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + SRDescription(SR.PDdocumentDescr) + ] + public PrintDocument Document { + get { return printDocument;} + set { + printDocument = value; + if (printDocument != null) { + pageSettings = printDocument.DefaultPageSettings; + printerSettings = printDocument.PrinterSettings; + } + } + } + + /// + /// + /// This allows the user to override the current behavior where the Metric is converted to ThousandOfInch even for METRIC MEASUREMENTSYSTEM + /// which returns a HUNDREDSOFMILLIMETER value. + /// + [ + DefaultValue(false), + SRDescription(SR.PSDenableMetricDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public bool EnableMetric { + get { return enableMetric;} + set { enableMetric = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating the minimum margins the + /// user is allowed to select, in hundredths of an inch. + /// + /// + /// + [ + SRCategory(SR.CatData), + SRDescription(SR.PSDminMarginsDescr) + ] + public Margins MinMargins { + get { return minMargins;} + set { + if (value == null) + value = new Margins(0, 0, 0, 0); + minMargins = value; + } + } + + /// + /// + /// + /// Gets + /// or sets + /// a value indicating + /// the page settings modified by the dialog box. + /// + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.PSDpageSettingsDescr) + ] + public PageSettings PageSettings { + get { return pageSettings;} + set { + pageSettings = value; + printDocument = null; + } + } + + /// + /// + /// + /// Gets + /// or sets the printer + /// settings the dialog box will modify if the user clicks the Printer button. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.PSDprinterSettingsDescr) + ] + public PrinterSettings PrinterSettings { + get { return printerSettings;} + set { + printerSettings = value; + printDocument = null; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Help button is visible. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PSDshowHelpDescr) + ] + public bool ShowHelp { + get { return showHelp;} + set { showHelp = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Network button is visible. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PSDshowNetworkDescr) + ] + public bool ShowNetwork { + get { return showNetwork;} + set { showNetwork = value;} + } + + private int GetFlags() { + int flags = 0; + flags |= NativeMethods.PSD_ENABLEPAGESETUPHOOK; + + if (!allowMargins) flags |= NativeMethods.PSD_DISABLEMARGINS; + if (!allowOrientation) flags |= NativeMethods.PSD_DISABLEORIENTATION; + if (!allowPaper) flags |= NativeMethods.PSD_DISABLEPAPER; + if (!allowPrinter || printerSettings == null) flags |= NativeMethods.PSD_DISABLEPRINTER; + + if (showHelp) flags |= NativeMethods.PSD_SHOWHELP; + if (!showNetwork) flags |= NativeMethods.PSD_NONETWORKBUTTON; + if (minMargins != null) flags |= NativeMethods.PSD_MINMARGINS; + if (pageSettings.Margins != null) flags |= NativeMethods.PSD_MARGINS; + + // + return flags; + } + + /// + /// + /// + /// Resets all options to their default values. + /// + /// + public override void Reset() { + allowMargins = true; + allowOrientation = true; + allowPaper = true; + allowPrinter = true; + MinMargins = null; // turns into Margin with all zeros + pageSettings = null; + printDocument = null; + printerSettings = null; + showHelp = false; + showNetwork = true; + } + + private void ResetMinMargins() { + MinMargins = null; + } + + /// + /// + /// + /// Indicates whether the + /// property should be + /// persisted. + /// + /// + private bool ShouldSerializeMinMargins() { + return minMargins.Left != 0 + || minMargins.Right != 0 + || minMargins.Top != 0 + || minMargins.Bottom != 0; + } + + private static void UpdateSettings(NativeMethods.PAGESETUPDLG data, PageSettings pageSettings, + PrinterSettings printerSettings) { + // SetHDevMode demands AllPrintingAndUnmanagedCode Permission : Since we are calling that function we should Assert the permision, + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + try + { + pageSettings.SetHdevmode(data.hDevMode); + if (printerSettings != null) { + printerSettings.SetHdevmode(data.hDevMode); + printerSettings.SetHdevnames(data.hDevNames); + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + + Margins newMargins = new Margins(); + newMargins.Left = data.marginLeft; + newMargins.Top = data.marginTop; + newMargins.Right = data.marginRight; + newMargins.Bottom = data.marginBottom; + + PrinterUnit fromUnit = ((data.Flags & NativeMethods.PSD_INHUNDREDTHSOFMILLIMETERS) != 0) + ? PrinterUnit.HundredthsOfAMillimeter + : PrinterUnit.ThousandthsOfAnInch; + + pageSettings.Margins = PrinterUnitConvert.Convert(newMargins, fromUnit, PrinterUnit.Display); + } + + + /// + /// + /// + /// + protected override bool RunDialog(IntPtr hwndOwner) { + IntSecurity.SafePrinting.Demand(); + + NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); + if (pageSettings == null) + throw new ArgumentException(SR.GetString(SR.PSDcantShowWithoutPage)); + + NativeMethods.PAGESETUPDLG data = new NativeMethods.PAGESETUPDLG(); + data.lStructSize = Marshal.SizeOf(data); + data.Flags = GetFlags(); + data.hwndOwner = hwndOwner; + data.lpfnPageSetupHook = hookProcPtr; + + PrinterUnit toUnit = PrinterUnit.ThousandthsOfAnInch; + + // Refer VSWhidbey: 331160. Below was a breaking change from RTM and EVERETT even though this was a correct FIX. + // EnableMetric is a new Whidbey property which we allow the users to choose between the AutoConversion or not. + if (EnableMetric) + { + //take the Units of Measurement while determining the PrinterUnits... + //bug (121347)... + StringBuilder sb = new StringBuilder(2); + int result = UnsafeNativeMethods.GetLocaleInfo(NativeMethods.LOCALE_USER_DEFAULT,NativeMethods.LOCALE_IMEASURE, sb,sb.Capacity); + + if (result > 0 && Int32.Parse(sb.ToString(), CultureInfo.InvariantCulture) == 0) { + toUnit = PrinterUnit.HundredthsOfAMillimeter; + } + } + + if (MinMargins != null) { + Margins margins = PrinterUnitConvert.Convert(MinMargins, PrinterUnit.Display, toUnit); + data.minMarginLeft = margins.Left; + data.minMarginTop = margins.Top; + data.minMarginRight = margins.Right; + data.minMarginBottom = margins.Bottom; + } + + if (pageSettings.Margins != null) { + Margins margins = PrinterUnitConvert.Convert(pageSettings.Margins, PrinterUnit.Display, toUnit); + data.marginLeft = margins.Left; + data.marginTop = margins.Top; + data.marginRight = margins.Right; + data.marginBottom = margins.Bottom; + } + + // Ensure that the margins are >= minMargins. + // This is a requirement of the PAGESETUPDLG structure. + // + data.marginLeft = Math.Max(data.marginLeft, data.minMarginLeft); + data.marginTop = Math.Max(data.marginTop, data.minMarginTop); + data.marginRight = Math.Max(data.marginRight, data.minMarginRight); + data.marginBottom = Math.Max(data.marginBottom, data.minMarginBottom); + + PrinterSettings printer = (printerSettings == null) ? pageSettings.PrinterSettings : printerSettings; + + // GetHDevmode demands AllPrintingAndUnmanagedCode Permission : Since we are calling that function we should Assert the permision, + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + try { + data.hDevMode = printer.GetHdevmode(pageSettings); + data.hDevNames = printer.GetHdevnames(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + try { + bool status = UnsafeNativeMethods.PageSetupDlg(data); + if (!status) { + // Debug.WriteLine(Windows.CommonDialogErrorToString(Windows.CommDlgExtendedError())); + return false; + } + + UpdateSettings(data, pageSettings, printerSettings); // yes, printerSettings, not printer + return true; + } + finally { + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode)); + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames)); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Printing/PrintControllerWithStatusDialog.cs b/WindowsForms/Managed/System/WinForms/Printing/PrintControllerWithStatusDialog.cs new file mode 100644 index 000000000..65b23277e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Printing/PrintControllerWithStatusDialog.cs @@ -0,0 +1,318 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Diagnostics; + using System; + using System.Threading; + using System.Drawing; + using Microsoft.Win32; + using System.ComponentModel; + using System.Drawing.Printing; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// [To be supplied.] + /// + public class PrintControllerWithStatusDialog : PrintController { + private PrintController underlyingController; + private PrintDocument document; + private BackgroundThread backgroundThread; + private int pageNumber; + private string dialogTitle; + + /// + /// + /// [To be supplied.] + /// + public PrintControllerWithStatusDialog(PrintController underlyingController) + : this(underlyingController, SR.GetString(SR.PrintControllerWithStatusDialog_DialogTitlePrint)) { + } + + /// + /// + /// [To be supplied.] + /// + public PrintControllerWithStatusDialog(PrintController underlyingController, string dialogTitle) { + this.underlyingController = underlyingController; + this.dialogTitle = dialogTitle; + } + /// + /// + /// + /// This is new public property which notifies if this controller is used for PrintPreview.. so get the underlying Controller + /// and return its IsPreview Property. + /// + /// + public override bool IsPreview { + get { + if (underlyingController != null) + { + return underlyingController.IsPreview; + } + return false; + } + } + + /// + /// + /// + /// + /// Implements StartPrint by delegating to the underlying controller. + /// + /// + public override void OnStartPrint(PrintDocument document, PrintEventArgs e) { + base.OnStartPrint(document, e); + + this.document = document; + pageNumber = 1; + + if (SystemInformation.UserInteractive) { + backgroundThread = new BackgroundThread(this); // starts running & shows dialog automatically + } + + // OnStartPrint does the security check... lots of + // extra setup to make sure that we tear down + // correctly... + // + try { + underlyingController.OnStartPrint(document, e); + } + catch { + if (backgroundThread != null) { + backgroundThread.Stop(); + } + throw; + } + finally { + if (backgroundThread != null && backgroundThread.canceled) { + e.Cancel = true; + } + } + } + + /// + /// + /// + /// + /// Implements StartPage by delegating to the underlying controller. + /// + /// + public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) { + base.OnStartPage(document, e); + + if (backgroundThread != null) { + backgroundThread.UpdateLabel(); + } + Graphics result = underlyingController.OnStartPage(document, e); + if (backgroundThread != null && backgroundThread.canceled){ + e.Cancel = true; + } + return result; + } + + /// + /// + /// + /// + /// Implements EndPage by delegating to the underlying controller. + /// + /// + public override void OnEndPage(PrintDocument document, PrintPageEventArgs e) { + underlyingController.OnEndPage(document, e); + if (backgroundThread != null && backgroundThread.canceled) { + e.Cancel = true; + } + pageNumber++; + + base.OnEndPage(document, e); + } + + /// + /// + /// + /// + /// Implements EndPrint by delegating to the underlying controller. + /// + /// + public override void OnEndPrint(PrintDocument document, PrintEventArgs e) { + underlyingController.OnEndPrint(document, e); + if (backgroundThread != null && backgroundThread.canceled) { + e.Cancel = true; + } + + if (backgroundThread != null) { + backgroundThread.Stop(); + } + + base.OnEndPrint(document, e); + } + + private class BackgroundThread { + private PrintControllerWithStatusDialog parent; + private StatusDialog dialog; + private Thread thread; + internal bool canceled = false; + private bool alreadyStopped = false; + + // Called from any thread + internal BackgroundThread(PrintControllerWithStatusDialog parent) { + this.parent = parent; + + // Calling Application.DoEvents() from within a paint event causes all sorts of problems, + // so we need to put the dialog on its own thread. + thread = new Thread(new ThreadStart(Run)); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + } + + // on correct thread + [ + UIPermission(SecurityAction.Assert, Window=UIPermissionWindow.AllWindows), + SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode), + ] + private void Run() { + // SECREVIEW : need all permissions to make the window not get adorned... + // + try { + lock (this) { + if (alreadyStopped) { + return; + } + + dialog = new StatusDialog(this, parent.dialogTitle); + ThreadUnsafeUpdateLabel(); + dialog.Visible = true; + } + + if (!alreadyStopped) { + Application.Run(dialog); + } + } + finally { + lock (this) { + if (dialog != null) { + dialog.Dispose(); + dialog = null; + } + } + } + } + + // Called from any thread + internal void Stop() { + lock (this) { + if (dialog != null && dialog.IsHandleCreated) { + dialog.BeginInvoke(new MethodInvoker(dialog.Close)); + return; + } + alreadyStopped = true; + } + } + + // on correct thread + private void ThreadUnsafeUpdateLabel() { + // "page {0} of {1}" + dialog.label1.Text = SR.GetString(SR.PrintControllerWithStatusDialog_NowPrinting, + parent.pageNumber, parent.document.DocumentName); + } + + // Called from any thread + internal void UpdateLabel() { + if (dialog != null && dialog.IsHandleCreated) { + dialog.BeginInvoke(new MethodInvoker(ThreadUnsafeUpdateLabel)); + // Don't wait for a response + } + } + } + + private class StatusDialog : Form { + internal Label label1; + private Button button1; + private TableLayoutPanel tableLayoutPanel1; + private BackgroundThread backgroundThread; + + internal StatusDialog(BackgroundThread backgroundThread, string dialogTitle) { + + InitializeComponent(); + this.backgroundThread = backgroundThread; + this.Text = dialogTitle; + this.MinimumSize = Size; + } + + /// + /// Tells whether the current resources for this dll have been + /// localized for a RTL language. + /// + private static bool IsRTLResources { + get { + return SR.GetString(SR.RTL) != "RTL_False"; + } + } + + private void InitializeComponent() { + if (IsRTLResources) + { + this.RightToLeft = RightToLeft.Yes; + } + + this.tableLayoutPanel1 = new TableLayoutPanel(); + this.label1 = new Label(); + this.button1 = new Button(); + + label1.AutoSize = true; + label1.Location = new Point(8, 16); + label1.TextAlign = ContentAlignment.MiddleCenter; + label1.Size = new Size(240, 64); + label1.TabIndex = 1; + label1.Anchor = AnchorStyles.None; + + button1.AutoSize = true; + button1.Size = new Size(75, 23); + button1.TabIndex = 0; + button1.Text = SR.GetString(SR.PrintControllerWithStatusDialog_Cancel); + button1.Location = new Point(88, 88); + button1.Anchor = AnchorStyles.None; + button1.Click += new EventHandler(button1_Click); + + tableLayoutPanel1.AutoSize = true; + tableLayoutPanel1.ColumnCount = 1; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(SizeType.Percent, 100F)); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(SizeType.Percent, 50F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(SizeType.Percent, 50F)); + tableLayoutPanel1.TabIndex = 0; + tableLayoutPanel1.Controls.Add(label1, 0, 0); + tableLayoutPanel1.Controls.Add(button1, 0, 1); + + this.AutoScaleDimensions = new Size(6, 13); + this.AutoScaleMode = AutoScaleMode.Font; + this.MaximizeBox = false; + this.ControlBox = false; + this.MinimizeBox = false; + Size clientSize = new Size(256, 122); + if (DpiHelper.IsScalingRequired) { + this.ClientSize = DpiHelper.LogicalToDeviceUnits(clientSize); + } + else { + this.ClientSize = clientSize; + } + this.CancelButton = button1; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Controls.Add(tableLayoutPanel1); + } + private void button1_Click(object sender, System.EventArgs e) { + button1.Enabled = false; + label1.Text = SR.GetString(SR.PrintControllerWithStatusDialog_Canceling); + backgroundThread.canceled = true; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Printing/PrintDialog.cs b/WindowsForms/Managed/System/WinForms/Printing/PrintDialog.cs new file mode 100644 index 000000000..a908a9f2b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Printing/PrintDialog.cs @@ -0,0 +1,580 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Printing; + using System.Runtime.InteropServices; + using System.Security; + + /// + /// + /// Allows users to select a printer and choose which + /// portions of the document to print. + /// + [DefaultProperty("Document")] + [SRDescription(SR.DescriptionPrintDialog)] + [Designer("System.Windows.Forms.Design.PrintDialogDesigner, " + AssemblyRef.SystemDesign)] + // The only event this dialog has is HelpRequested, which isn't very useful + public sealed class PrintDialog : CommonDialog { + private const int printRangeMask = (int) (PrintRange.AllPages | PrintRange.SomePages + | PrintRange.Selection | PrintRange.CurrentPage); + + // If PrintDocument != null, settings == printDocument.PrinterSettings + private PrinterSettings settings = null; + private PrintDocument printDocument = null; + + // Implementing "current page" would require switching to PrintDlgEx, which is windows 2000 and later only + private bool allowCurrentPage; + + private bool allowPages; + private bool allowPrintToFile; + private bool allowSelection; + private bool printToFile; + private bool showHelp; + private bool showNetwork; + + private bool useEXDialog = false; + + /// + /// + /// Initializes a new instance of the class. + /// + public PrintDialog() { + Reset(); + } + + + /// + /// + /// Gets or sets a value indicating whether the Current Page option button is enabled. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.PDallowCurrentPageDescr) + ] + public bool AllowCurrentPage { + get { return allowCurrentPage;} + set { allowCurrentPage = value;} + } + + + /// + /// + /// + /// Gets or sets a value indicating whether the Pages option button is enabled. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PDallowPagesDescr) + ] + public bool AllowSomePages { + get { return allowPages;} + set { allowPages = value;} + } + + /// + /// + /// Gets or sets a value indicating whether the Print to file check box is enabled. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PDallowPrintToFileDescr) + ] + public bool AllowPrintToFile { + get { return allowPrintToFile;} + set { allowPrintToFile = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the From... To... Page option button is enabled. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PDallowSelectionDescr) + ] + public bool AllowSelection { + get { return allowSelection;} + set { allowSelection = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating the used to obtain . + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + SRDescription(SR.PDdocumentDescr) + ] + public PrintDocument Document { + get { return printDocument;} + set { + printDocument = value; + if (printDocument == null) + settings = new PrinterSettings(); + else + settings = printDocument.PrinterSettings; + } + } + + private PageSettings PageSettings { + get { + if (Document == null) + return PrinterSettings.DefaultPageSettings; + else + return Document.DefaultPageSettings; + } + } + + /// + /// + /// + /// Gets or sets the the + /// dialog box will be modifying. + /// + /// + [ + SRCategory(SR.CatData), + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.PDprinterSettingsDescr) + ] + public PrinterSettings PrinterSettings { + get { + + if (settings == null) + { + settings = new PrinterSettings(); + } + return settings; + } + set { + if (value != PrinterSettings) + { + settings = value; + printDocument = null; + } + } + } + + /// + /// + /// Gets or sets a value indicating whether the Print to file check box is checked. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PDprintToFileDescr) + ] + public bool PrintToFile { + get { return printToFile;} + set { printToFile = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Help button is displayed. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PDshowHelpDescr) + ] + public bool ShowHelp { + get { return showHelp;} + set { showHelp = value;} + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Network button is displayed. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PDshowNetworkDescr) + ] + public bool ShowNetwork { + get { return showNetwork;} + set { showNetwork = value;} + } + + + /// + /// + /// UseEXDialog = true means to use the EX versions of the dialogs when running on XP or above, and to ignore the ShowHelp & ShowNetwork properties. + /// If running below XP then UseEXDialog is ignored and the non-EX dialogs are used & ShowHelp & ShowNetwork are respected. + /// UseEXDialog = false means to never use the EX versions of the dialog regardless of which O/S app is running on. ShowHelp & ShowNetwork will work in this case. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.PDuseEXDialog) + ] + public bool UseEXDialog { + get { return useEXDialog;} + set { useEXDialog = value;} + } + + private int GetFlags() { + int flags = 0; + + // VSWhidbey 93449: Only set this flag when using PRINTDLG and PrintDlg, + // and not when using PrintDlgEx and PRINTDLGEX. + if (!UseEXDialog || (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5)) { + flags |= NativeMethods.PD_ENABLEPRINTHOOK; + } + + if (!allowCurrentPage) flags |= NativeMethods.PD_NOCURRENTPAGE; + if (!allowPages) flags |= NativeMethods.PD_NOPAGENUMS; + if (!allowPrintToFile) flags |= NativeMethods.PD_DISABLEPRINTTOFILE; + if (!allowSelection) flags |= NativeMethods.PD_NOSELECTION; + + flags |= (int) PrinterSettings.PrintRange; + + if (printToFile) flags |= NativeMethods.PD_PRINTTOFILE; + if (showHelp) flags |= NativeMethods.PD_SHOWHELP; + if (!showNetwork) flags |= NativeMethods.PD_NONETWORKBUTTON; + if (PrinterSettings.Collate) flags |= NativeMethods.PD_COLLATE; + return flags; + } + + /// + /// + /// + /// Resets all options, the last selected printer, and the page + /// settings to their default values. + /// + /// + public override void Reset() { + allowCurrentPage = false; + allowPages = false; + allowPrintToFile = true; + allowSelection = false; + printDocument = null; + printToFile = false; + settings = null; + showHelp = false; + showNetwork = true; + } + + // Create a PRINTDLG with a few useful defaults. + internal static NativeMethods.PRINTDLG CreatePRINTDLG() { + NativeMethods.PRINTDLG data = null; + if (IntPtr.Size == 4) { + data = new NativeMethods.PRINTDLG_32(); + } + else { + data = new NativeMethods.PRINTDLG_64(); + } + data.lStructSize = Marshal.SizeOf(data); + data.hwndOwner = IntPtr.Zero; + data.hDevMode = IntPtr.Zero; + data.hDevNames = IntPtr.Zero; + data.Flags = 0; + data.hDC = IntPtr.Zero; + data.nFromPage = 1; + data.nToPage = 1; + data.nMinPage = 0; + data.nMaxPage = 9999; + data.nCopies = 1; + data.hInstance = IntPtr.Zero; + data.lCustData = IntPtr.Zero; + data.lpfnPrintHook = null; + data.lpfnSetupHook = null; + data.lpPrintTemplateName = null; + data.lpSetupTemplateName = null; + data.hPrintTemplate = IntPtr.Zero; + data.hSetupTemplate = IntPtr.Zero; + return data; + } + + // VSWhidbey 93449: Use PRINTDLGEX on Win2k and newer OS'. Note that at the time of this + // fix, PrinterSettings did not support multiple page ranges. (See VSWhidbey 193829.) + // Create a PRINTDLGEX with a few useful defaults. + internal static NativeMethods.PRINTDLGEX CreatePRINTDLGEX() { + NativeMethods.PRINTDLGEX data = new NativeMethods.PRINTDLGEX(); + data.lStructSize = Marshal.SizeOf(data); + data.hwndOwner = IntPtr.Zero; + data.hDevMode = IntPtr.Zero; + data.hDevNames = IntPtr.Zero; + data.hDC = IntPtr.Zero; + data.Flags = 0; + data.Flags2 = 0; + data.ExclusionFlags = 0; + data.nPageRanges = 0; + data.nMaxPageRanges = 1; + data.pageRanges = UnsafeNativeMethods.GlobalAlloc(NativeMethods.GPTR, + data.nMaxPageRanges * Marshal.SizeOf(typeof(NativeMethods.PRINTPAGERANGE))); + data.nMinPage = 0; + data.nMaxPage = 9999; + data.nCopies = 1; + data.hInstance = IntPtr.Zero; + data.lpPrintTemplateName = null; + data.nPropertyPages = 0; + data.lphPropertyPages = IntPtr.Zero; + data.nStartPage = NativeMethods.START_PAGE_GENERAL; + data.dwResultAction = 0; + return data; + } + + /// + /// + /// + /// + // VSWhidbey 93449: Use PrintDlgEx and PRINTDLGEX on Win2k and newer OS'. + protected override bool RunDialog(IntPtr hwndOwner) { + bool returnValue = false; + + IntSecurity.SafePrinting.Demand(); + + NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); + + if (!UseEXDialog || (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5)) { + NativeMethods.PRINTDLG data = CreatePRINTDLG(); + returnValue = ShowPrintDialog(hwndOwner, hookProcPtr, data); + } + else { + NativeMethods.PRINTDLGEX data = CreatePRINTDLGEX(); + returnValue = ShowPrintDialog(hwndOwner, data); + } + + return returnValue; + } + + // VSWhidbey 93449: Due to the nature of PRINTDLGEX vs PRINTDLG, separate but similar methods + // are required for showing the print dialog on Win2k and newer OS'. + private bool ShowPrintDialog(IntPtr hwndOwner, NativeMethods.WndProc hookProcPtr, NativeMethods.PRINTDLG data) { + + data.Flags = GetFlags(); + data.nCopies = (short) PrinterSettings.Copies; + data.hwndOwner = hwndOwner; + data.lpfnPrintHook = hookProcPtr; + + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + + try { + if (PageSettings == null) + data.hDevMode = PrinterSettings.GetHdevmode(); + else + data.hDevMode = PrinterSettings.GetHdevmode(PageSettings); + + data.hDevNames = PrinterSettings.GetHdevnames(); + } + catch (InvalidPrinterException) { + data.hDevMode = IntPtr.Zero; + data.hDevNames = IntPtr.Zero; + // Leave those fields null; Windows will fill them in + } + finally { + CodeAccessPermission.RevertAssert(); + } + + try { + // Windows doesn't like it if page numbers are invalid + if (AllowSomePages) { + if (PrinterSettings.FromPage < PrinterSettings.MinimumPage + || PrinterSettings.FromPage > PrinterSettings.MaximumPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "FromPage")); + if (PrinterSettings.ToPage < PrinterSettings.MinimumPage + || PrinterSettings.ToPage > PrinterSettings.MaximumPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "ToPage")); + if (PrinterSettings.ToPage < PrinterSettings.FromPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "FromPage")); + + data.nFromPage = (short) PrinterSettings.FromPage; + data.nToPage = (short) PrinterSettings.ToPage; + data.nMinPage = (short) PrinterSettings.MinimumPage; + data.nMaxPage = (short) PrinterSettings.MaximumPage; + } + + if (!UnsafeNativeMethods.PrintDlg(data)) + return false; + + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + try { + UpdatePrinterSettings(data.hDevMode, data.hDevNames, data.nCopies, data.Flags, settings, PageSettings); + } + finally { + CodeAccessPermission.RevertAssert(); + } + PrintToFile = ((data.Flags & NativeMethods.PD_PRINTTOFILE) != 0); + PrinterSettings.PrintToFile = PrintToFile; + + if (AllowSomePages) { + PrinterSettings.FromPage = data.nFromPage; + PrinterSettings.ToPage = data.nToPage; + } + + // Fix Dev10 #575399, when the flag PD_USEDEVMODECOPIESANDCOLLATE is not set, + // PRINTDLG.nCopies or PRINTDLG.nCopies indicates the number of copies the user wants + // to print, and the PD_COLLATE flag in the Flags member indicates + // whether the user wants to print them collated. + // Due to a Windows OS Bug 558734, we don't need to consider Windows XP and before + if ((data.Flags & NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE) == 0) { + if (Environment.OSVersion.Version.Major >= 6) { + PrinterSettings.Copies = data.nCopies; + PrinterSettings.Collate = ((data.Flags & NativeMethods.PD_COLLATE) == NativeMethods.PD_COLLATE); + } + } + + return true; + } + finally { + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode)); + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames)); + } + } + + // VSWhidbey 93449: Due to the nature of PRINTDLGEX vs PRINTDLG, separate but similar methods + // are required for showing the print dialog on Win2k and newer OS'. + private bool ShowPrintDialog(IntPtr hwndOwner, NativeMethods.PRINTDLGEX data) { + + data.Flags = GetFlags(); + data.nCopies = PrinterSettings.Copies; + data.hwndOwner = hwndOwner; + + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + try { + if (PageSettings == null) + data.hDevMode = PrinterSettings.GetHdevmode(); + else + data.hDevMode = PrinterSettings.GetHdevmode(PageSettings); + + data.hDevNames = PrinterSettings.GetHdevnames(); + } + catch (InvalidPrinterException) { + data.hDevMode = IntPtr.Zero; + data.hDevNames = IntPtr.Zero; + // Leave those fields null; Windows will fill them in + } + finally { + CodeAccessPermission.RevertAssert(); + } + + try { + // Windows doesn't like it if page numbers are invalid + if (AllowSomePages) { + if (PrinterSettings.FromPage < PrinterSettings.MinimumPage + || PrinterSettings.FromPage > PrinterSettings.MaximumPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "FromPage")); + if (PrinterSettings.ToPage < PrinterSettings.MinimumPage + || PrinterSettings.ToPage > PrinterSettings.MaximumPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "ToPage")); + if (PrinterSettings.ToPage < PrinterSettings.FromPage) + throw new ArgumentException(SR.GetString(SR.PDpageOutOfRange, "FromPage")); + + unsafe { + int* pageRangeField = (int*)data.pageRanges; + *pageRangeField = PrinterSettings.FromPage; + pageRangeField += 1; + *pageRangeField = PrinterSettings.ToPage; + } + data.nPageRanges = 1; + + data.nMinPage = PrinterSettings.MinimumPage; + data.nMaxPage = PrinterSettings.MaximumPage; + } + + // + // The flags NativeMethods.PD_SHOWHELP and NativeMethods.PD_NONETWORKBUTTON don't work with + // PrintDlgEx. So we have to strip them out. + data.Flags &= ~(NativeMethods.PD_SHOWHELP | NativeMethods.PD_NONETWORKBUTTON); + + int hr = UnsafeNativeMethods.PrintDlgEx(data); + if (NativeMethods.Failed(hr) || data.dwResultAction == NativeMethods.PD_RESULT_CANCEL) { + return false; + } + + IntSecurity.AllPrintingAndUnmanagedCode.Assert(); + try { + UpdatePrinterSettings(data.hDevMode, data.hDevNames, (short)data.nCopies, data.Flags, PrinterSettings, PageSettings); + } + finally { + CodeAccessPermission.RevertAssert(); + } + PrintToFile = ((data.Flags & NativeMethods.PD_PRINTTOFILE) != 0); + PrinterSettings.PrintToFile = PrintToFile; + if (AllowSomePages) { + unsafe { + int* pageRangeField = (int*)data.pageRanges; + PrinterSettings.FromPage = *pageRangeField; + pageRangeField += 1; + PrinterSettings.ToPage = *pageRangeField; + } + } + + // Fix Dev10 #575399, when the flag PD_USEDEVMODECOPIESANDCOLLATE is not set, + // PRINTDLG.nCopies or PRINTDLG.nCopies indicates the number of copies the user wants + // to print, and the PD_COLLATE flag in the Flags member indicates + // whether the user wants to print them collated. + // Due to a Windows OS Bug 558734, we don't need to consider Windows XP and before + if ((data.Flags & NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE) == 0) { + if(Environment.OSVersion.Version.Major >= 6) { + PrinterSettings.Copies = (short)(data.nCopies); + PrinterSettings.Collate = ((data.Flags & NativeMethods.PD_COLLATE) == NativeMethods.PD_COLLATE); + } + } + + // We should return true only if the user pressed the "Print" button while dismissing the dialog. + // Please refer to VsW: 403124 for more details. + return (data.dwResultAction == NativeMethods.PD_RESULT_PRINT); + } + finally { + if (data.hDevMode != IntPtr.Zero) + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode)); + if (data.hDevNames != IntPtr.Zero) + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames)); + if (data.pageRanges != IntPtr.Zero) + UnsafeNativeMethods.GlobalFree(new HandleRef(data, data.pageRanges)); + } + } + + // VSWhidbey 93449: Due to the nature of PRINTDLGEX vs PRINTDLG, separate but similar methods + // are required for updating the settings from the structure utilized by the dialog. + // Take information from print dialog and put in PrinterSettings + private static void UpdatePrinterSettings(IntPtr hDevMode, IntPtr hDevNames, short copies, int flags, PrinterSettings settings, PageSettings pageSettings) { + // Mode + settings.SetHdevmode(hDevMode); + settings.SetHdevnames(hDevNames); + + if (pageSettings!= null) + pageSettings.SetHdevmode(hDevMode); + + //Check for Copies == 1 since we might get the Right number of Copies from hdevMode.dmCopies... + //this is Native PrintDialogs + if (settings.Copies == 1) + settings.Copies = copies; + + settings.PrintRange = (PrintRange) (flags & printRangeMask); + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewControl.cs b/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewControl.cs new file mode 100644 index 000000000..c57fedfad --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewControl.cs @@ -0,0 +1,961 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.PhysicalToPixels(System.Drawing.Size,System.Drawing.Point):System.Drawing.Size")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.PixelsToPhysical(System.Drawing.Size,System.Drawing.Point):System.Drawing.Size")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.PrintPreviewControl.set_VirtualSize(System.Drawing.Size):System.Void")] + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Drawing; + using Microsoft.Win32; + using System.ComponentModel; + using System.Drawing.Printing; + using CodeAccessPermission = System.Security.CodeAccessPermission; + using System.Globalization; + + /// + /// + /// + /// The raw "preview" part of print previewing, without any dialogs or buttons. + /// Most PrintPreviewControl's are found on PrintPreviewDialog's, + /// but they don't have to be. + /// + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [DefaultProperty("Document")] + [SRDescription(SR.DescriptionPrintPreviewControl)] + public class PrintPreviewControl : Control { + Size virtualSize = new Size(1,1); + Point position = new Point(0,0); + Point lastOffset; + bool antiAlias; + + private const int SCROLL_PAGE = 100; + private const int SCROLL_LINE = 5; + private const double DefaultZoom = .3; + + private const int border = 10; // spacing per page, in mm + + private PrintDocument document; + private PreviewPageInfo[] pageInfo; // null if needs refreshing + private int startPage; // 0-based + private int rows = 1; + private int columns = 1; + private bool autoZoom = true; + + // The following are all computed by ComputeLayout + private bool layoutOk; + private Size imageSize = System.Drawing.Size.Empty; // 100ths of inch, not pixels + private Point screendpi = Point.Empty; + private double zoom = DefaultZoom; + bool pageInfoCalcPending; + bool exceptionPrinting; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public PrintPreviewControl() { + ResetBackColor(); + ResetForeColor(); + Size = new Size(100, 100); + SetStyle(ControlStyles.ResizeRedraw, false); + SetStyle(ControlStyles.Opaque | ControlStyles.OptimizedDoubleBuffer, true); + + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PrintPreviewAntiAliasDescr) + ] + public bool UseAntiAlias { + get { + return antiAlias; + } + set { + antiAlias = value; + } + } + + /// + /// + /// + /// Gets or sets a value If true (the default), resizing the control or changing the number of pages shown + /// will automatically adjust Zoom to make everything visible. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.PrintPreviewAutoZoomDescr) + ] + public bool AutoZoom { + get { return autoZoom;} + set { + if (autoZoom != value) { + autoZoom = value; + InvalidateLayout(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating the document to preview. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.PrintPreviewDocumentDescr) + ] + public PrintDocument Document { + get { return document;} + set { + document = value; + InvalidatePreview(); + } + } + + /// + /// + /// + /// Gets or sets the number of pages + /// displayed horizontally across the screen. + /// + /// + [ + DefaultValue(1), + SRCategory(SR.CatLayout), + SRDescription(SR.PrintPreviewColumnsDescr) + ] + public int Columns { + get { return columns;} + set { + if (value < 1 ) { + throw new ArgumentOutOfRangeException("Columns", SR.GetString(SR.InvalidLowBoundArgumentEx, "Columns", value.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + + columns = value; + InvalidateLayout(); + } + } + + /// + /// + /// + /// + /// Gets the CreateParams used to create the window. + /// If a subclass overrides this function, it must call the base implementation. + /// + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.Style |= NativeMethods.WS_HSCROLL; + cp.Style |= NativeMethods.WS_VSCROLL; + return cp; + } + } + + /// + /// + /// The virtual coordinate of the upper left visible pixel. + /// + + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWithScrollbarsPositionDescr) + ] + private Point Position { + get { return position;} + set { + SetPositionNoInvalidate(value); + } + } + + /// + /// + /// + /// Gets or sets the number of pages + /// displayed vertically down the screen. + /// + /// + [ + DefaultValue(1), + SRDescription(SR.PrintPreviewRowsDescr), + SRCategory(SR.CatBehavior) + ] + public int Rows { + get { return rows;} + set { + + if (value < 1 ) { + throw new ArgumentOutOfRangeException("Rows", SR.GetString(SR.InvalidLowBoundArgumentEx, "Rows", value.ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + + rows = value; + InvalidateLayout(); + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(RightToLeft.Inherit), + SRDescription(SR.ControlRightToLeftDescr) + ] + public override RightToLeft RightToLeft { + get { + return base.RightToLeft; + } + set { + base.RightToLeft = value; + InvalidatePreview(); + } + } + + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the page number of the upper left page. + /// + /// + /// + [ + DefaultValue(0), + SRDescription(SR.PrintPreviewStartPageDescr), + SRCategory(SR.CatBehavior) + ] + public int StartPage { + get { + int value = startPage; + if (pageInfo != null) { + value = Math.Min(value, pageInfo.Length - (rows * columns)); + } + value = Math.Max(value, 0); + + return value; + } + set { + if (value < 0 ) { + throw new ArgumentOutOfRangeException("StartPage", SR.GetString(SR.InvalidLowBoundArgumentEx, "StartPage", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + int oldValue = StartPage; + startPage = value; + if (oldValue != startPage) { + InvalidateLayout(); + OnStartPageChanged(EventArgs.Empty); + } + } + } + + private static readonly object EVENT_STARTPAGECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnStartPageChangedDescr)] + public event EventHandler StartPageChanged { + add { + Events.AddHandler(EVENT_STARTPAGECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_STARTPAGECHANGED, value); + } + } + + /// + /// + /// How big the control would be if the screen was infinitely large. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWithScrollbarsVirtualSizeDescr) + ] + private Size VirtualSize { + get { return virtualSize;} + set { + SetVirtualSizeNoInvalidate(value); + Invalidate(); + } + } + + /// + /// + /// + /// Gets or sets a value indicating how large the pages will appear. + /// + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.PrintPreviewZoomDescr), + DefaultValue(DefaultZoom) + ] + public double Zoom { + get { return zoom;} + set { + if (value <= 0) + throw new ArgumentException(SR.GetString(SR.PrintPreviewControlZoomNegative)); + autoZoom = false; + zoom = value; + InvalidateLayout(); + } + } + + private int AdjustScroll(Message m, int pos, int maxPos, bool horizontal) { + switch (NativeMethods.Util.LOWORD(m.WParam)) { + case NativeMethods.SB_THUMBPOSITION: + case NativeMethods.SB_THUMBTRACK: + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_TRACKPOS; + int direction = horizontal ? NativeMethods.SB_HORZ : NativeMethods.SB_VERT; + if (SafeNativeMethods.GetScrollInfo(new HandleRef(this, m.HWnd), direction, si)) + { + pos = si.nTrackPos; + } + else + { + pos = NativeMethods.Util.HIWORD(m.WParam); + } + break; + case NativeMethods.SB_LINEUP: + if (pos > SCROLL_LINE) { + pos-=SCROLL_LINE; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_LINEDOWN: + if (pos < maxPos-SCROLL_LINE) { + pos+=SCROLL_LINE; + } + else { + pos = maxPos; + } + break; + case NativeMethods.SB_PAGEUP: + if (pos > SCROLL_PAGE) { + pos-=SCROLL_PAGE; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_PAGEDOWN: + if (pos < maxPos-SCROLL_PAGE) { + pos+=SCROLL_PAGE; + } + else { + pos = maxPos; + } + break; + } + return pos; + } + + + // This function computes everything in terms of physical size (millimeters), not pixels + // + private void ComputeLayout() { + Debug.Assert(pageInfo != null, "Must call ComputePreview first"); + layoutOk = true; + if (pageInfo.Length == 0) { + ClientSize = Size; + return; + } + + Graphics tempGraphics = CreateGraphicsInternal(); + IntPtr dc = tempGraphics.GetHdc(); + screendpi = new Point(UnsafeNativeMethods.GetDeviceCaps(new HandleRef(tempGraphics, dc), NativeMethods.LOGPIXELSX), + UnsafeNativeMethods.GetDeviceCaps(new HandleRef(tempGraphics, dc), NativeMethods.LOGPIXELSY)); + tempGraphics.ReleaseHdcInternal(dc); + tempGraphics.Dispose(); + + Size pageSize = pageInfo[StartPage].PhysicalSize; + Size controlPhysicalSize = new Size(PixelsToPhysical(new Point(Size), screendpi)); + + if (autoZoom) { + double zoomX = ((double) controlPhysicalSize.Width - border*(columns + 1)) / (columns*pageSize.Width); + double zoomY = ((double) controlPhysicalSize.Height - border*(rows + 1)) / (rows*pageSize.Height); + zoom = Math.Min(zoomX, zoomY); + } + + imageSize = new Size((int) (zoom*pageSize.Width), (int) (zoom*pageSize.Height)); + int virtualX = (imageSize.Width * columns) + border * (columns +1); + int virtualY = (imageSize.Height * rows) + border * (rows +1); + SetVirtualSizeNoInvalidate(new Size(PhysicalToPixels(new Point(virtualX, virtualY), screendpi))); + } + + // "Prints" the document to memory + private void ComputePreview() { + int oldStart = StartPage; + + if (document == null) + pageInfo = new PreviewPageInfo[0]; + else { + IntSecurity.SafePrinting.Demand(); + + PrintController oldController = document.PrintController; + PreviewPrintController previewController = new PreviewPrintController(); + previewController.UseAntiAlias = UseAntiAlias; + document.PrintController = new PrintControllerWithStatusDialog(previewController, + SR.GetString(SR.PrintControllerWithStatusDialog_DialogTitlePreview)); + + // Want to make sure we've reverted any security asserts before we call Print -- that calls into user code + document.Print(); + pageInfo = previewController.GetPreviewPageInfo(); + Debug.Assert(pageInfo != null, "ReviewPrintController did not give us preview info"); + + document.PrintController = oldController; + } + + if (oldStart != StartPage) { + OnStartPageChanged(EventArgs.Empty); + } + } + + // Recomputes the sizes and positions of pages without forcing a new "preview print" + private void InvalidateLayout() { + layoutOk = false; + Invalidate(); + } + + /// + /// + /// + /// Refreshes the preview of the document. + /// + /// + public void InvalidatePreview() { + pageInfo = null; + InvalidateLayout(); + } + + /// + /// + /// + /// + /// Invalidate the layout, if necessary. + /// + /// + protected override void OnResize(EventArgs eventargs) { + InvalidateLayout(); + base.OnResize(eventargs); + } + + void CalculatePageInfo() { + if (pageInfoCalcPending) { + return; + } + + pageInfoCalcPending = true; + try { + if (pageInfo == null) { + try { + ComputePreview(); + } + catch { + exceptionPrinting = true; + throw; + } + finally { + Invalidate(); + } + } + } + finally { + pageInfoCalcPending = false; + } + } + + + /// + /// + /// + /// + /// Paints the control. + /// + /// + protected override void OnPaint(PaintEventArgs pevent) { + Brush backBrush = new SolidBrush(BackColor); + + try { + if (pageInfo == null || pageInfo.Length == 0) { + pevent.Graphics.FillRectangle(backBrush, ClientRectangle); + + if (pageInfo != null || exceptionPrinting) { + // Calculate formats + StringFormat format = new StringFormat(); + format.Alignment = ControlPaint.TranslateAlignment(ContentAlignment.MiddleCenter); + format.LineAlignment = ControlPaint.TranslateLineAlignment(ContentAlignment.MiddleCenter); + + // Do actual drawing + SolidBrush brush = new SolidBrush(ForeColor); + try { + if (exceptionPrinting) { + pevent.Graphics.DrawString(SR.GetString(SR.PrintPreviewExceptionPrinting), Font, brush, ClientRectangle, format); + } + else { + pevent.Graphics.DrawString(SR.GetString(SR.PrintPreviewNoPages), Font, brush, ClientRectangle, format); + } + } + finally { + brush.Dispose(); + format.Dispose(); + } + } + else { + BeginInvoke(new MethodInvoker(CalculatePageInfo)); + } + } + else { + if (!layoutOk) + ComputeLayout(); + + Size controlPhysicalSize = new Size(PixelsToPhysical(new Point(Size), screendpi)); + + //Point imagePixels = PhysicalToPixels(new Point(imageSize), screendpi); + Point virtualPixels = new Point(VirtualSize); + + // center pages on screen if small enough + Point offset = new Point(Math.Max(0, (Size.Width - virtualPixels.X) / 2), + Math.Max(0, (Size.Height - virtualPixels.Y) / 2)); + offset.X -= Position.X; + offset.Y -= Position.Y; + lastOffset = offset; + + int borderPixelsX = PhysicalToPixels(border, screendpi.X); + int borderPixelsY = PhysicalToPixels(border, screendpi.Y); + + Region originalClip = pevent.Graphics.Clip; + Rectangle[] pageRenderArea = new Rectangle[rows * columns]; + Point lastImageSize = Point.Empty; + int maxImageHeight = 0; + + try { + for (int row = 0; row < rows; row++) { + //Initialize our LastImageSize variable... + lastImageSize.X = 0; + lastImageSize.Y = maxImageHeight * row; + + for (int column = 0; column < columns; column++) { + int imageIndex = StartPage + column + row*columns; + if (imageIndex < pageInfo.Length) { + + Size pageSize = pageInfo[imageIndex].PhysicalSize; + if (autoZoom) { + double zoomX = ((double) controlPhysicalSize.Width - border*(columns + 1)) / (columns*pageSize.Width); + double zoomY = ((double) controlPhysicalSize.Height - border*(rows + 1)) / (rows*pageSize.Height); + zoom = Math.Min(zoomX, zoomY); + } + + imageSize = new Size((int) (zoom*pageSize.Width), (int) (zoom*pageSize.Height)); + Point imagePixels = PhysicalToPixels(new Point(imageSize), screendpi); + + + int x = offset.X + borderPixelsX * (column + 1) + lastImageSize.X; + int y = offset.Y + borderPixelsY * (row + 1) + lastImageSize.Y; + + lastImageSize.X += imagePixels.X; + //The Height is the Max of any PageHeight.. + maxImageHeight = Math.Max(maxImageHeight, imagePixels.Y); + + pageRenderArea[imageIndex - StartPage] = new Rectangle(x, y, imagePixels.X, imagePixels.Y); + pevent.Graphics.ExcludeClip(pageRenderArea[imageIndex - StartPage]); + } + } + } + + pevent.Graphics.FillRectangle(backBrush, ClientRectangle); + } + finally { + pevent.Graphics.Clip = originalClip; + } + + for (int i=0; i + /// + /// [To be supplied.] + /// + protected virtual void OnStartPageChanged(EventArgs e) { + EventHandler eh = Events[EVENT_STARTPAGECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + private static int PhysicalToPixels(int physicalSize, int dpi) { + return(int) (physicalSize * dpi / 100.0); + } + + private static Point PhysicalToPixels(Point physical, Point dpi) { + return new Point(PhysicalToPixels(physical.X, dpi.X), + PhysicalToPixels(physical.Y, dpi.Y)); + } + + private static Size PhysicalToPixels(Size physicalSize, Point dpi) { + return new Size(PhysicalToPixels(physicalSize.Width, dpi.X), + PhysicalToPixels(physicalSize.Height, dpi.Y)); + } + + private static int PixelsToPhysical(int pixels, int dpi) { + return(int) (pixels * 100.0 / dpi); + } + + private static Point PixelsToPhysical(Point pixels, Point dpi) { + return new Point(PixelsToPhysical(pixels.X, dpi.X), + PixelsToPhysical(pixels.Y, dpi.Y)); + } + + private static Size PixelsToPhysical(Size pixels, Point dpi) { + return new Size(PixelsToPhysical(pixels.Width, dpi.X), + PixelsToPhysical(pixels.Height, dpi.Y)); + } + + /// + /// + /// Resets the back color to the defaults for the PrintPreviewControl. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetBackColor() { + BackColor = SystemColors.AppWorkspace; + } + + /// + /// + /// Resets the back color to the defaults for the PrintPreviewControl. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetForeColor() { + ForeColor = Color.White; + } + + /// + /// WM_HSCROLL handler + /// + /// + /// + + private void WmHScroll(ref Message m) { + + // The lparam is handle of the sending scrollbar, or NULL when + // the scrollbar sending the message is the "form" scrollbar... + // + if (m.LParam != IntPtr.Zero) { + base.WndProc(ref m); + return; + } + + Point locPos = position; + int pos = locPos.X; + int maxPos = Math.Max(Width, virtualSize.Width /*- Width*/); + + locPos.X = AdjustScroll(m, pos, maxPos, true); + Position = locPos; + } + + private void SetPositionNoInvalidate(Point value) { + Point current = position; + + position = value; + position.X = Math.Min(position.X, virtualSize.Width - Width); + position.Y = Math.Min(position.Y, virtualSize.Height - Height); + if (position.X < 0) position.X = 0; + if (position.Y < 0) position.Y = 0; + + Rectangle rect = ClientRectangle; + NativeMethods.RECT scroll = NativeMethods.RECT.FromXYWH(rect.X, rect.Y, rect.Width, rect.Height); + SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), + current.X - position.X, + current.Y - position.Y, + ref scroll, + ref scroll); + + UnsafeNativeMethods.SetScrollPos(new HandleRef(this, Handle), NativeMethods.SB_HORZ, position.X, true); + UnsafeNativeMethods.SetScrollPos(new HandleRef(this, Handle), NativeMethods.SB_VERT, position.Y, true); + } + + internal void SetVirtualSizeNoInvalidate(Size value) { + virtualSize = value; + SetPositionNoInvalidate(position); // Make sure it's within range + + NativeMethods.SCROLLINFO info = new NativeMethods.SCROLLINFO(); + info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE; + info.nMin = 0; + info.nMax = Math.Max(Height, virtualSize.Height) - 1; + info.nPage = Height; + UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_VERT, info, true); + + info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE; + info.nMin = 0; + info.nMax = Math.Max(Width, virtualSize.Width) - 1; + info.nPage = Width; + UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ, info, true); + } + + /// + /// WM_VSCROLL handler + /// + /// + /// + + private void WmVScroll(ref Message m) { + + // The lparam is handle of the sending scrollbar, or NULL when + // the scrollbar sending the message is the "form" scrollbar... + // + if (m.LParam != IntPtr.Zero) { + base.WndProc(ref m); + return; + } + + Point locPos = Position; + int pos = locPos.Y; + int maxPos = Math.Max(Height, virtualSize.Height/* - Height*/); + + locPos.Y = AdjustScroll(m, pos, maxPos, false); + Position = locPos; + } + + /// + /// + /// Handles the WM_KEYDOWN message. + /// + /// + //added to handle keyboard events(71009)subhag + // + private void WmKeyDown(ref Message msg) { + + Keys keyData = (Keys)((int)msg.WParam | (int)ModifierKeys); + Point locPos = Position; + int pos = 0; + int maxPos = 0; + + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + if ((keyData & Keys.Modifiers) == Keys.Control) + { + pos = locPos.X; + if (pos > SCROLL_PAGE) { + pos-=SCROLL_PAGE; + } + else { + pos = 0; + } + locPos.X = pos; + Position = locPos; + } + else if (StartPage > 0) + { + StartPage--; + } + break; + case Keys.PageDown: + if ((keyData & Keys.Modifiers) == Keys.Control) + { + pos = locPos.X; + maxPos = Math.Max(Width, virtualSize.Width /*- Width*/); + if (pos < maxPos-SCROLL_PAGE) { + pos+=SCROLL_PAGE; + } + else { + pos = maxPos; + } + locPos.X = pos; + Position = locPos; + } + else if (StartPage < pageInfo.Length) + { + StartPage++; + } + break; + case Keys.Home: + if ((keyData & Keys.Modifiers) == Keys.Control) + StartPage=0; + break; + case Keys.End: + if ((keyData & Keys.Modifiers) == Keys.Control) + StartPage=pageInfo.Length; + break; + + case Keys.Up: + pos = locPos.Y; + if (pos > SCROLL_LINE) + { + pos-=SCROLL_LINE; + } + else { + pos = 0; + } + locPos.Y = pos; + Position = locPos; + break; + case Keys.Down: + + pos = locPos.Y; + maxPos = Math.Max(Height, virtualSize.Height/* - Height*/); + + if (pos < maxPos-SCROLL_LINE) { + pos+=SCROLL_LINE; + } + else { + pos = maxPos; + } + locPos.Y = pos; + Position = locPos; + break; + case Keys.Left: + + pos = locPos.X; + if (pos > SCROLL_LINE) { + pos-=SCROLL_LINE; + } + else { + pos = 0; + } + locPos.X = pos; + Position = locPos; + break; + case Keys.Right: + pos = locPos.X; + maxPos = Math.Max(Width, virtualSize.Width /*- Width*/); + if (pos < maxPos-SCROLL_LINE) { + pos+=SCROLL_LINE; + } + else { + pos = maxPos; + } + locPos.X = pos; + Position = locPos; + break; + } + } + + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_VSCROLL: + WmVScroll(ref m); + break; + case NativeMethods.WM_HSCROLL: + WmHScroll(ref m); + break; + //added case to handle keyboard events(71009)subhag + // + case NativeMethods.WM_KEYDOWN: + WmKeyDown(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + internal override bool ShouldSerializeBackColor() { + return !BackColor.Equals(SystemColors.AppWorkspace); + } + + /// + /// + /// + /// Indicates whether the property should be + /// persisted. + /// + /// + internal override bool ShouldSerializeForeColor() { + return !ForeColor.Equals(Color.White); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewDialog.cs b/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewDialog.cs new file mode 100644 index 000000000..3ddb50919 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Printing/PrintPreviewDialog.cs @@ -0,0 +1,1797 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Design; + using System.Drawing.Printing; + using System.Runtime.Remoting; + using System.Windows.Forms.Design; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Runtime.Versioning; + + /// + /// + /// Represents a + /// dialog box form that contains a . + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.ComponentModel.Design.ComponentDesigner, " + AssemblyRef.SystemDesign), + DesignTimeVisible(true), + DefaultProperty("Document"), + ToolboxItemFilter("System.Windows.Forms.Control.TopLevel"), + ToolboxItem(true), + SRDescription(SR.DescriptionPrintPreviewDialog) + ] + public class PrintPreviewDialog : Form { + PrintPreviewControl previewControl; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.NumericUpDown pageCounter; + private System.Windows.Forms.ToolStripButton printToolStripButton; + private System.Windows.Forms.ToolStripSplitButton zoomToolStripSplitButton; + private System.Windows.Forms.ToolStripMenuItem autoToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem4; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem5; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem6; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem8; + private System.Windows.Forms.ToolStripSeparator separatorToolStripSeparator; + private System.Windows.Forms.ToolStripButton onepageToolStripButton; + private System.Windows.Forms.ToolStripButton twopagesToolStripButton; + private System.Windows.Forms.ToolStripButton threepagesToolStripButton; + private System.Windows.Forms.ToolStripButton fourpagesToolStripButton; + private System.Windows.Forms.ToolStripButton sixpagesToolStripButton; + private System.Windows.Forms.ToolStripSeparator separatorToolStripSeparator1; + private System.Windows.Forms.ToolStripButton closeToolStripButton; + private System.Windows.Forms.ToolStripLabel pageToolStripLabel; + ImageList imageList; + + /// + /// + /// Initializes a new instance of the class. + /// + public PrintPreviewDialog() { + + + #pragma warning disable 618 + base.AutoScaleBaseSize = new Size(5, 13); + #pragma warning restore 618 + + + + this.previewControl = new PrintPreviewControl(); + this.imageList = new ImageList(); + + Bitmap bitmaps = new Bitmap(typeof(PrintPreviewDialog), "PrintPreviewStrip.bmp"); + bitmaps.MakeTransparent(); + imageList.Images.AddStrip(bitmaps); + + InitForm(); + + + } + + //subhag addition + //------------------------------------------------------------------------------------------------------------- + /// + /// + /// Indicates the control on the form that is clicked when + /// the user presses the ENTER key. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public IButtonControl AcceptButton { + get { + return base.AcceptButton; + } + set { + base.AcceptButton = value; + } + } + /// + /// + /// + /// Gets or sets a value indicating whether the form will adjust its size + /// to fit the height of the font used on the form and scale + /// its controls. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool AutoScale { + get { + #pragma warning disable 618 + return base.AutoScale; + #pragma warning restore 618 + } + set { + #pragma warning disable 618 + base.AutoScale = value; + #pragma warning restore 618 + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the form implements + /// autoscrolling. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AutoScroll { + get { + + return base.AutoScroll; + } + set { + base.AutoScroll = value; + } + } + + /// + /// + /// + /// Hide the property + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// + /// Hide the property + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override AutoValidate AutoValidate { + get { + return base.AutoValidate; + } + set { + base.AutoValidate = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler AutoValidateChanged { + add { + base.AutoValidateChanged += value; + } + remove { + base.AutoValidateChanged -= value; + } + } + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + /// + /// + /// Gets + /// or + /// sets the button control that will be clicked when the + /// user presses the ESC key. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public IButtonControl CancelButton { + get { + return base.CancelButton; + } + set { + base.CancelButton = value; + } + } + /// + /// + /// Gets or sets a value indicating whether a control box is displayed in the + /// caption bar of the form. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool ControlBox { + get { + return base.ControlBox; + } + set { + base.ControlBox = value; + } + } + + /// + /// + /// Hide the property + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ContextMenuStrip ContextMenuStrip { + get { + return base.ContextMenuStrip; + } + set { + base.ContextMenuStrip = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ContextMenuStripChanged { + add { + base.ContextMenuStripChanged += value; + } + remove { + base.ContextMenuStripChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the border style of the form. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public FormBorderStyle FormBorderStyle { + get { + return base.FormBorderStyle; + } + set { + base.FormBorderStyle = value; + } + } + /// + /// + /// + /// Gets or sets a value indicating whether a + /// help button should be displayed in the caption box of the form. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool HelpButton { + get { + return base.HelpButton; + } + set { + base.HelpButton = value; + } + } + /// + /// + /// + /// Gets or sets the icon for the form. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Icon Icon { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return base.Icon; + } + set { + base.Icon = value; + } + } + /// + /// + /// + /// Gets or sets a value indicating whether the form is a container for multiple document interface + /// (MDI) child forms. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool IsMdiContainer { + get { + return base.IsMdiContainer; + } + set { + base.IsMdiContainer = value; + } + } + /// + /// + /// + /// Gets or sets a value + /// indicating whether the form will receive key events + /// before the event is passed to the control that has focus. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool KeyPreview { + get { + return base.KeyPreview; + } + set { + base.KeyPreview = value; + } + } + /// + /// + /// + /// Gets or Sets the maximum size the dialog can be resized to. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Size MaximumSize { + get { + return base.MaximumSize; + } + set { + base.MaximumSize = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MaximumSizeChanged { + add { + base.MaximumSizeChanged += value; + } + remove { + base.MaximumSizeChanged -= value; + } + } + /// + /// + /// Gets or sets a value indicating whether the maximize button is + /// displayed in the caption bar of the form. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool MaximizeBox { + get { + return base.MaximizeBox; + } + set { + base.MaximizeBox = value; + } + } + + /// + /// + /// Hide the value + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Padding Margin { + get { + return base.Margin; + } + set { + base.Margin = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MarginChanged { + add { + base.MarginChanged += value; + } + remove { + base.MarginChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the + /// that is displayed in the form. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public MainMenu Menu { + get { + return base.Menu; + } + set { + base.Menu = value; + } + } + + /// + /// + /// + /// Gets the minimum size the form can be resized to. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] + new public Size MinimumSize { + get { + return base.MinimumSize; + } + set { + base.MinimumSize = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MinimumSizeChanged { + add { + base.MinimumSizeChanged += value; + } + remove { + base.MinimumSizeChanged -= value; + } + } + + /// + /// + /// + /// Hide the value + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Padding Padding { + get { + return base.Padding; + } + set { + base.Padding = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler PaddingChanged { + add { + base.PaddingChanged += value; + } + remove { + base.PaddingChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the size of the form. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Size Size { + get { + return base.Size; + } + set { + base.Size = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler SizeChanged { + add { + base.SizeChanged += value; + } + remove { + base.SizeChanged -= value; + } + } + /// + /// + /// + /// Gets or sets the + /// starting position of the form at run time. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public FormStartPosition StartPosition { + get { + return base.StartPosition; + } + set { + base.StartPosition = value; + } + } + /// + /// + /// Gets or sets a value indicating whether the form should be displayed as the top-most + /// form of your application. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TopMost { + get { + return base.TopMost; + } + set { + base.TopMost = value; + } + } + + /// + /// + /// Gets or sets the color that will represent transparent areas of the form. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Color TransparencyKey { + get { + return base.TransparencyKey; + } + set { + base.TransparencyKey = value; + } + } + + /// + /// + /// Hide the value + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool UseWaitCursor{ + get { + return base.UseWaitCursor; + } + set { + base.UseWaitCursor = value; + } + } + + /// + /// + /// Gets or sets the form's window state. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public FormWindowState WindowState { + get { + return base.WindowState; + } + set { + base.WindowState = value; + } + } + /// + /// + /// The accessible role of the control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public AccessibleRole AccessibleRole { + get { + return base.AccessibleRole; + } + set { + base.AccessibleRole = value; + } + } + /// + /// + /// The accessible description of the control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public string AccessibleDescription { + get { + return base.AccessibleDescription; + } + set { + base.AccessibleDescription = value; + } + } + /// + /// + /// The accessible name of the control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public string AccessibleName { + get { + return base.AccessibleName; + } + set { + base.AccessibleName = value; + } + } + /// + /// + /// + /// Indicates whether entering the control causes validation on the controls requiring validation. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool CausesValidation { + get { + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + /// + /// + /// Retrieves the bindings for this control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ControlBindingsCollection DataBindings { + get { + return base.DataBindings; + } + } + + + /// + protected override Size DefaultMinimumSize { + get { return new Size(375, 250); } + } + + + /// + /// + /// Indicates whether the control is currently enabled. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool Enabled { + get { + return base.Enabled; + } + set { + base.Enabled = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler EnabledChanged { + add { + base.EnabledChanged += value; + } + remove { + base.EnabledChanged -= value; + } + } + /// + /// + /// The location of this control. + /// + [Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler LocationChanged { + add { + base.LocationChanged += value; + } + remove { + base.LocationChanged -= value; + } + } + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public object Tag { + get { + return base.Tag; + } + set { + base.Tag = value; + } + } + /// + /// + /// The AllowDrop property. If AllowDrop is set to true then + /// this control will allow drag and drop operations and events to be used. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + /// + /// + /// Retrieves the cursor that will be displayed when the mouse is over this + /// control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Cursor Cursor { + get { + return base.Cursor; + } + set { + base.Cursor = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CursorChanged { + add { + base.CursorChanged += value; + } + remove { + base.CursorChanged -= value; + } + } + + /// + /// + /// The background image of the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// The background image layout of the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + /// + /// + /// Specifies a value that determines the IME (Input Method Editor) status of the + /// object when that object is selected. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// + /// Gets or + /// sets the size of the auto-scroll + /// margin. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Size AutoScrollMargin { + get { + return base.AutoScrollMargin; + } + set { + base.AutoScrollMargin = value; + } + } + /// + /// + /// Gets or sets the mimimum size of the auto-scroll. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public Size AutoScrollMinSize { + get { + return base.AutoScrollMinSize; + } + set { + base.AutoScrollMinSize = value; + } + } + /// + /// + /// The current value of the anchor property. The anchor property + /// determines which edges of the control are anchored to the container's + /// edges. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + base.Anchor = value; + } + } + /// + /// + /// Indicates whether the control is visible. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool Visible { + get { + return base.Visible; + } + set { + base.Visible = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler VisibleChanged { + add { + base.VisibleChanged += value; + } + remove { + base.VisibleChanged -= value; + } + } + /// + /// + /// The foreground color of the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override RightToLeft RightToLeft { + get { + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool RightToLeftLayout { + get { + + return base.RightToLeftLayout; + } + + set { + base.RightToLeftLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftChanged { + add { + base.RightToLeftChanged += value; + } + remove { + base.RightToLeftChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftLayoutChanged { + add { + base.RightToLeftLayoutChanged += value; + } + remove { + base.RightToLeftLayoutChanged -= value; + } + } + + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// The current text associated with this control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The dock property. The dock property controls to which edge + /// of the container this control is docked to. For example, when docked to + /// the top of the container, the control will be displayed flush at the + /// top of the container, extending the length of the container. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DockChanged { + add { + base.DockChanged += value; + } + remove { + base.DockChanged -= value; + } + } + + /// + /// + /// Retrieves the current font for this control. This will be the font used + /// by default for painting and text in the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + /// + /// The contextMenu associated with this control. The contextMenu + /// will be shown when the user right clicks the mouse on the control. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ContextMenu ContextMenu { + get { + return base.ContextMenu; + } + set { + base.ContextMenu = value; + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ContextMenuChanged { + add { + base.ContextMenuChanged += value; + } + remove { + base.ContextMenuChanged -= value; + } + } + + // DockPadding is not relevant to UpDownBase + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public DockPaddingEdges DockPadding { + get { + return base.DockPadding; + } + } + //------------------------------------------------------------------------------------------------------------- + //end addition + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.PrintPreviewAntiAliasDescr) + ] + public bool UseAntiAlias { + get { + return PrintPreviewControl.UseAntiAlias; + } + set { + PrintPreviewControl.UseAntiAlias = value; + } + } + + /// + /// + /// + /// PrintPreviewDialog does not support AutoScaleBaseSize. + /// + /// + /// Keeping implementation of obsoleted AutoScaleBaseSize API + #pragma warning disable 618 + // disable csharp compiler warning #0809: obsolete member overrides non-obsolete member + #pragma warning disable 0809 + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This property has been deprecated. Use the AutoScaleDimensions property instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public override Size AutoScaleBaseSize { + get { + return base.AutoScaleBaseSize; + } + + set { + // No-op + } + } + #pragma warning restore 0809 + #pragma warning restore 618 + + /// + /// + /// + /// Gets or sets the document to preview. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.PrintPreviewDocumentDescr) + ] + public PrintDocument Document { + get { + return previewControl.Document; + } + set { + previewControl.Document = value; + } + } + + /// + [Browsable(false), DefaultValue(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool MinimizeBox { + get { + return base.MinimizeBox; + } + set { + base.MinimizeBox = value; + } + } + + /// + /// + /// Gets or sets a value indicating the + /// contained in this form. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.PrintPreviewPrintPreviewControlDescr), + Browsable(false) + ] + public PrintPreviewControl PrintPreviewControl { + get { return previewControl;} + } + + /// + /// + /// + /// + /// Opacity does not apply to PrintPreviewDialogs. + /// + /// + [Browsable(false),EditorBrowsable(EditorBrowsableState.Advanced)] + public new double Opacity { + get { + return base.Opacity; + } + set { + base.Opacity = value; + } + } + + /// + [Browsable(false), DefaultValue(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool ShowInTaskbar { + get { + return base.ShowInTaskbar; + } + set { + base.ShowInTaskbar = value; + } + } + + /// + [Browsable(false), DefaultValue(SizeGripStyle.Hide), EditorBrowsable(EditorBrowsableState.Never)] + public new SizeGripStyle SizeGripStyle { + get { + return base.SizeGripStyle; + } + set { + base.SizeGripStyle = value; + } + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // The default page count is 1. + // So we don't have to localize it. + ] + void InitForm() { + + + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PrintPreviewDialog)); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.printToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.zoomToolStripSplitButton = new System.Windows.Forms.ToolStripSplitButton(); + this.autoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripMenuItem(); + this.separatorToolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); + this.onepageToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.twopagesToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.threepagesToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.fourpagesToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.sixpagesToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.separatorToolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.closeToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.pageCounter = new System.Windows.Forms.NumericUpDown(); + this.pageToolStripLabel = new System.Windows.Forms.ToolStripLabel(); + this.toolStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pageCounter)).BeginInit(); + this.SuspendLayout(); + + // + // toolStrip1 + // + resources.ApplyResources(this.toolStrip1, "toolStrip1"); + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.printToolStripButton, + this.zoomToolStripSplitButton, + this.separatorToolStripSeparator, + this.onepageToolStripButton, + this.twopagesToolStripButton, + this.threepagesToolStripButton, + this.fourpagesToolStripButton, + this.sixpagesToolStripButton, + this.separatorToolStripSeparator1, + this.closeToolStripButton}); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; + this.toolStrip1.GripStyle = ToolStripGripStyle.Hidden; + + // + // printToolStripButton + // + this.printToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.printToolStripButton.Name = "printToolStripButton"; + resources.ApplyResources(this.printToolStripButton, "printToolStripButton"); + + // + // zoomToolStripSplitButton + // + this.zoomToolStripSplitButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.zoomToolStripSplitButton.DoubleClickEnabled = true; + this.zoomToolStripSplitButton.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.autoToolStripMenuItem, + this.toolStripMenuItem1, + this.toolStripMenuItem2, + this.toolStripMenuItem3, + this.toolStripMenuItem4, + this.toolStripMenuItem5, + this.toolStripMenuItem6, + this.toolStripMenuItem7, + this.toolStripMenuItem8}); + this.zoomToolStripSplitButton.Name = "zoomToolStripSplitButton"; + this.zoomToolStripSplitButton.SplitterWidth = 1; + resources.ApplyResources(this.zoomToolStripSplitButton, "zoomToolStripSplitButton"); + + + // + // autoToolStripMenuItem + // + this.autoToolStripMenuItem.CheckOnClick = true; + this.autoToolStripMenuItem.DoubleClickEnabled = true; + this.autoToolStripMenuItem.Checked = true; + this.autoToolStripMenuItem.Name = "autoToolStripMenuItem"; + resources.ApplyResources(this.autoToolStripMenuItem, "autoToolStripMenuItem"); + + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.CheckOnClick = true; + this.toolStripMenuItem1.DoubleClickEnabled = true; + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + resources.ApplyResources(this.toolStripMenuItem1, "toolStripMenuItem1"); + + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.CheckOnClick = true; + this.toolStripMenuItem2.DoubleClickEnabled = true; + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + resources.ApplyResources(this.toolStripMenuItem2, "toolStripMenuItem2"); + + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.CheckOnClick = true; + this.toolStripMenuItem3.DoubleClickEnabled = true; + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + resources.ApplyResources(this.toolStripMenuItem3, "toolStripMenuItem3"); + + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.CheckOnClick = true; + this.toolStripMenuItem4.DoubleClickEnabled = true; + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + resources.ApplyResources(this.toolStripMenuItem4, "toolStripMenuItem4"); + + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.CheckOnClick = true; + this.toolStripMenuItem5.DoubleClickEnabled = true; + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + resources.ApplyResources(this.toolStripMenuItem5, "toolStripMenuItem5"); + + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.CheckOnClick = true; + this.toolStripMenuItem6.DoubleClickEnabled = true; + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + resources.ApplyResources(this.toolStripMenuItem6, "toolStripMenuItem6"); + + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.CheckOnClick = true; + this.toolStripMenuItem7.DoubleClickEnabled = true; + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + resources.ApplyResources(this.toolStripMenuItem7, "toolStripMenuItem7"); + + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.CheckOnClick = true; + this.toolStripMenuItem8.DoubleClickEnabled = true; + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + resources.ApplyResources(this.toolStripMenuItem8, "toolStripMenuItem8"); + + // + // separatorToolStripSeparator + // + this.separatorToolStripSeparator.Name = "separatorToolStripSeparator"; + + // + // onepageToolStripButton + // + this.onepageToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.onepageToolStripButton.Name = "onepageToolStripButton"; + resources.ApplyResources(this.onepageToolStripButton, "onepageToolStripButton"); + + // + // twopagesToolStripButton + // + this.twopagesToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.twopagesToolStripButton.Name = "twopagesToolStripButton"; + resources.ApplyResources(this.twopagesToolStripButton, "twopagesToolStripButton"); + + // + // threepagesToolStripButton + // + this.threepagesToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.threepagesToolStripButton.Name = "threepagesToolStripButton"; + resources.ApplyResources(this.threepagesToolStripButton, "threepagesToolStripButton"); + + // + // fourpagesToolStripButton + // + this.fourpagesToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.fourpagesToolStripButton.Name = "fourpagesToolStripButton"; + resources.ApplyResources(this.fourpagesToolStripButton, "fourpagesToolStripButton"); + + // + // sixpagesToolStripButton + // + this.sixpagesToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.sixpagesToolStripButton.Name = "sixpagesToolStripButton"; + resources.ApplyResources(this.sixpagesToolStripButton, "sixpagesToolStripButton"); + + // + // separatorToolStripSeparator1 + // + this.separatorToolStripSeparator1.Name = "separatorToolStripSeparator1"; + + // + // closeToolStripButton + // + this.closeToolStripButton.DisplayStyle = ToolStripItemDisplayStyle.Text; + this.closeToolStripButton.Name = "closeToolStripButton"; + resources.ApplyResources(this.closeToolStripButton, "closeToolStripButton"); + + // + // pageCounter + // + resources.ApplyResources(this.pageCounter, "pageCounter"); + pageCounter.Text = "1"; + pageCounter.TextAlign = HorizontalAlignment.Right; + pageCounter.DecimalPlaces = 0; + pageCounter.Minimum = new Decimal(0d); + pageCounter.Maximum = new Decimal(1000d); + pageCounter.ValueChanged += new EventHandler(UpdownMove); + this.pageCounter.Name = "pageCounter"; + + // + // pageToolStripLabel + // + this.pageToolStripLabel.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.pageToolStripLabel.Name = "pageToolStripLabel"; + resources.ApplyResources(this.pageToolStripLabel, "pageToolStripLabel"); + + previewControl.Size = new Size(792, 610); + previewControl.Location = new Point(0, 43); + previewControl.Dock = DockStyle.Fill; + previewControl.StartPageChanged += new EventHandler(previewControl_StartPageChanged); + + //EVENTS and Images ... + this.printToolStripButton.Click += new System.EventHandler(this.OnprintToolStripButtonClick); + this.autoToolStripMenuItem.Click += new System.EventHandler(ZoomAuto); + this.toolStripMenuItem1.Click += new System.EventHandler(Zoom500); + this.toolStripMenuItem2.Click += new System.EventHandler(Zoom250); + this.toolStripMenuItem3.Click += new System.EventHandler(Zoom150); + this.toolStripMenuItem4.Click += new System.EventHandler(Zoom100); + this.toolStripMenuItem5.Click += new System.EventHandler(Zoom75); + this.toolStripMenuItem6.Click += new System.EventHandler(Zoom50); + this.toolStripMenuItem7.Click += new System.EventHandler(Zoom25); + this.toolStripMenuItem8.Click += new System.EventHandler(Zoom10); + this.onepageToolStripButton.Click += new System.EventHandler(this.OnonepageToolStripButtonClick); + this.twopagesToolStripButton.Click += new System.EventHandler(this.OntwopagesToolStripButtonClick); + this.threepagesToolStripButton.Click += new System.EventHandler(this.OnthreepagesToolStripButtonClick); + this.fourpagesToolStripButton.Click += new System.EventHandler(this.OnfourpagesToolStripButtonClick); + this.sixpagesToolStripButton.Click += new System.EventHandler(this.OnsixpagesToolStripButtonClick); + this.closeToolStripButton.Click += new System.EventHandler(this.OncloseToolStripButtonClick); + this.closeToolStripButton.Paint += new PaintEventHandler(this.OncloseToolStripButtonPaint); + //Images + this.toolStrip1.ImageList = imageList; + this.printToolStripButton.ImageIndex = 0; + this.zoomToolStripSplitButton.ImageIndex = 1; + this.onepageToolStripButton.ImageIndex = 2; + this.twopagesToolStripButton.ImageIndex = 3; + this.threepagesToolStripButton.ImageIndex = 4; + this.fourpagesToolStripButton.ImageIndex = 5; + this.sixpagesToolStripButton.ImageIndex = 6; + + //tabIndex + previewControl.TabIndex = 0; + toolStrip1.TabIndex = 1; + + //DefaultItem on the Zoom SplitButton + zoomToolStripSplitButton.DefaultItem = autoToolStripMenuItem; + + //ShowCheckMargin + ToolStripDropDownMenu menu = this.zoomToolStripSplitButton.DropDown as ToolStripDropDownMenu; + if (menu != null) + { + menu.ShowCheckMargin = true; + menu.ShowImageMargin = false; + menu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; + + } + + //Create the ToolStripControlHost + ToolStripControlHost pageCounterItem = new ToolStripControlHost(pageCounter); + pageCounterItem.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + + this.toolStrip1.Items.Add(pageCounterItem); + this.toolStrip1.Items.Add(this.pageToolStripLabel); + + // + // Form1 + // + resources.ApplyResources(this, "$this"); + + this.Controls.Add(previewControl); + this.Controls.Add(this.toolStrip1); + + this.ClientSize = new Size(400, 300); + this.MinimizeBox = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = SizeGripStyle.Hide; + this.toolStrip1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pageCounter)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + + } + + + /// + /// + /// + /// + /// Forces the preview to be regenerated every time the dialog comes up + /// + /// + protected override void OnClosing(CancelEventArgs e) { + base.OnClosing(e); + previewControl.InvalidatePreview(); + } + + /// + /// + /// Creates the handle for the PrintPreviewDialog. If a + /// subclass overrides this function, + /// it must call the base implementation. + /// + protected override void CreateHandle() { + // We want to check printer settings before we push the modal message loop, + // so the user has a chance to catch the exception instead of letting go to + // the windows forms exception dialog. + if (Document != null && !Document.PrinterSettings.IsValid) + throw new InvalidPrinterException(Document.PrinterSettings); + + base.CreateHandle(); + } + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + return false; + } + } + return base.ProcessDialogKey(keyData); + } + + /// + /// + /// In Everett we used to TAB around the PrintPreviewDialog. Now since the PageCounter is added into the ToolStrip we dont + /// This is breaking from Everett. + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessTabKey(bool forward) { + if (this.ActiveControl == this.previewControl) + { + this.pageCounter.FocusInternal(); + return true; + } + return false; + } + + /// + /// + /// + /// + /// AutoScaleBaseSize should never be persisted for PrintPreviewDialogs. + /// + /// + internal override bool ShouldSerializeAutoScaleBaseSize() { + // This method is called when the dialog is "contained" on another form. + // We should use our own base size, not the base size of our container. + return false; + } + + /// + internal override bool ShouldSerializeText() { + return !Text.Equals(SR.GetString(SR.PrintPreviewDialog_PrintPreview)); + } + + void OncloseToolStripButtonClick(object sender, System.EventArgs e) { + this.Close(); + } + + void previewControl_StartPageChanged(object sender, EventArgs e) { + pageCounter.Value = previewControl.StartPage + 1; + } + + + void CheckZoomMenu(ToolStripMenuItem toChecked) { + foreach (ToolStripMenuItem item in zoomToolStripSplitButton.DropDownItems) { + item.Checked = toChecked == item; + } + } + + + void ZoomAuto(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.AutoZoom = true; + } + + void Zoom500(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = 5.00; + } + + void Zoom250(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = 2.50; + } + + void Zoom150(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = 1.50; + } + + void Zoom100(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = 1.00; + } + + void Zoom75(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = .75; + } + + void Zoom50(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = .50; + } + + void Zoom25(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = .25; + } + + void Zoom10(object sender, EventArgs eventargs) { + ToolStripMenuItem item = sender as ToolStripMenuItem; + CheckZoomMenu(item); + previewControl.Zoom = .10; + } + + void OncloseToolStripButtonPaint(object sender, PaintEventArgs e) + { + ToolStripItem item = sender as ToolStripItem; + if (item != null && !item.Selected) + { + Rectangle rect = new Rectangle (0, 0 , item.Bounds.Width - 1, item.Bounds.Height - 1); + using (Pen pen = new Pen(SystemColors.ControlDark)) + { + e.Graphics.DrawRectangle(pen, rect); + } + } + } + + + void OnprintToolStripButtonClick(object sender, System.EventArgs e) { + if (previewControl.Document != null) + { + previewControl.Document.Print(); + } + } + + void OnzoomToolStripSplitButtonClick(object sender, System.EventArgs e) { + ZoomAuto(null, EventArgs.Empty); + } + + //-------- + void OnonepageToolStripButtonClick(object sender, System.EventArgs e) { + previewControl.Rows = 1; + previewControl.Columns = 1; + } + + void OntwopagesToolStripButtonClick(object sender, System.EventArgs e) { + previewControl.Rows = 1; + previewControl.Columns = 2; + } + + void OnthreepagesToolStripButtonClick(object sender, System.EventArgs e) { + previewControl.Rows = 1; + previewControl.Columns = 3; + } + + void OnfourpagesToolStripButtonClick(object sender, System.EventArgs e) { + previewControl.Rows = 2; + previewControl.Columns = 2; + } + + void OnsixpagesToolStripButtonClick(object sender, System.EventArgs e) { + previewControl.Rows = 2; + previewControl.Columns = 3; + } + //---------------------- + + + void UpdownMove(object sender, EventArgs eventargs) { + int pageNum = ((int)pageCounter.Value) - 1; + if (pageNum >= 0) { + // -1 because users like to count from one, and programmers from 0 + previewControl.StartPage = pageNum; + + // And previewControl_PropertyChanged will change it again, + // ensuring it stays within legal bounds. + } + else { + pageCounter.Value = previewControl.StartPage + 1; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ProfessionalColorTable.cs b/WindowsForms/Managed/System/WinForms/ProfessionalColorTable.cs new file mode 100644 index 000000000..6d0b6a4bc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProfessionalColorTable.cs @@ -0,0 +1,2053 @@ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + using System.Drawing; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + + public class ProfessionalColorTable { + + private Dictionary professionalRGB = null; + private bool usingSystemColors = false; + private bool useSystemColors = false; + private string lastKnownColorScheme = String.Empty; + + + private const string oliveColorScheme = "HomeStead"; + private const string normalColorScheme = "NormalColor"; + private const string silverColorScheme = "Metallic"; + private const string royaleColorScheme = "Royale"; // sometimes returns NormalColor, sometimes returns Royale. + + private const string lunaFileName = "luna.msstyles"; + private const string royaleFileName = "royale.msstyles"; + private const string aeroFileName = "aero.msstyles"; + + + private object colorFreshnessKey = null; + + public ProfessionalColorTable() { + } + + private Dictionary ColorTable { + get { + if (UseSystemColors) { + // someone has turned off theme support for the color table. + if (!usingSystemColors || professionalRGB == null) { + if (professionalRGB == null) { + professionalRGB= new Dictionary((int)KnownColors.lastKnownColor); + } + InitSystemColors(ref professionalRGB); + } + } + else if (ToolStripManager.VisualStylesEnabled) { + // themes are on and enabled in the manager + if (usingSystemColors || professionalRGB == null) { + if (professionalRGB == null) { + professionalRGB= new Dictionary((int)KnownColors.lastKnownColor); + } + InitThemedColors(ref professionalRGB); + } + } + else { + // themes are off. + if (!usingSystemColors || professionalRGB == null) { + if (professionalRGB == null) { + professionalRGB= new Dictionary((int)KnownColors.lastKnownColor); + } + InitSystemColors(ref professionalRGB); + } + } + return professionalRGB; + } + } + + + /// when this is specified, professional colors picks from SystemColors rather than colors + /// that match the current theme. If theming is not turned on, we'll fall back to SystemColors. + /// + public bool UseSystemColors { + get { + return useSystemColors; + } + set{ + if (useSystemColors != value) { + useSystemColors = value; + ResetRGBTable(); + } + } + } + + internal Color FromKnownColor(ProfessionalColorTable.KnownColors color) { + if (ProfessionalColors.ColorFreshnessKey != colorFreshnessKey || ProfessionalColors.ColorScheme != lastKnownColorScheme) { + ResetRGBTable(); + } + colorFreshnessKey = ProfessionalColors.ColorFreshnessKey; + lastKnownColorScheme = ProfessionalColors.ColorScheme; + + return (Color)ColorTable[color]; + } + + + + private void ResetRGBTable() { + if (professionalRGB != null) { + professionalRGB.Clear(); + } + professionalRGB = null; + } + + +#region Colors + + [SRDescription(SR.ProfessionalColorsButtonSelectedHighlightDescr)] + public virtual Color ButtonSelectedHighlight { + get { return FromKnownColor(KnownColors.ButtonSelectedHighlight); } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedHighlightBorderDescr)] + public virtual Color ButtonSelectedHighlightBorder { + get { return ButtonPressedBorder; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedHighlightDescr)] + public virtual Color ButtonPressedHighlight { + get { return FromKnownColor(KnownColors.ButtonPressedHighlight); } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedHighlightBorderDescr)] + public virtual Color ButtonPressedHighlightBorder { + get { return SystemColors.Highlight; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedHighlightDescr)] + public virtual Color ButtonCheckedHighlight { + get { return FromKnownColor(KnownColors.ButtonCheckedHighlight); } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedHighlightBorderDescr)] + public virtual Color ButtonCheckedHighlightBorder { + get { return SystemColors.Highlight; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedBorderDescr)] + public virtual Color ButtonPressedBorder { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBdrMouseOver); } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedBorderDescr)] + public virtual Color ButtonSelectedBorder { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBdrMouseOver); } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientBeginDescr)] + public virtual Color ButtonCheckedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradSelectedBegin); } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientMiddleDescr)] + public virtual Color ButtonCheckedGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradSelectedMiddle); } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientEndDescr)] + public virtual Color ButtonCheckedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradSelectedEnd); } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientBeginDescr)] + public virtual Color ButtonSelectedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseOverBegin); } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientMiddleDescr)] + public virtual Color ButtonSelectedGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseOverMiddle); } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientEndDescr)] + public virtual Color ButtonSelectedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseOverEnd); } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientBeginDescr)] + public virtual Color ButtonPressedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseDownBegin); } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientMiddleDescr)] + public virtual Color ButtonPressedGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseDownMiddle); } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientEndDescr)] + public virtual Color ButtonPressedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseDownEnd); } + } + [SRDescription(SR.ProfessionalColorsCheckBackgroundDescr)] + public virtual Color CheckBackground { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdSelected); } + } + + [SRDescription(SR.ProfessionalColorsCheckSelectedBackgroundDescr)] + public virtual Color CheckSelectedBackground { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver); } + } + + [SRDescription(SR.ProfessionalColorsCheckPressedBackgroundDescr)] + public virtual Color CheckPressedBackground { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver); } + } + + [SRDescription(SR.ProfessionalColorsGripDarkDescr)] + public virtual Color GripDark { + get { return FromKnownColor(KnownColors.msocbvcrCBDragHandle); } + } + + [SRDescription(SR.ProfessionalColorsGripLightDescr)] + public virtual Color GripLight { + get { return FromKnownColor(KnownColors.msocbvcrCBDragHandleShadow); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginGradientBeginDescr)] + public virtual Color ImageMarginGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradVertBegin); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginGradientMiddleDescr)] + public virtual Color ImageMarginGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradVertMiddle); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginGradientEndDescr)] + public virtual Color ImageMarginGradientEnd { + get { return (usingSystemColors) ? SystemColors.Control : FromKnownColor(KnownColors.msocbvcrCBGradVertEnd); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientBeginDescr)] + public virtual Color ImageMarginRevealedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientMiddleDescr)] + public virtual Color ImageMarginRevealedGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle); } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientEndDescr)] + public virtual Color ImageMarginRevealedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd); } + } + + [SRDescription(SR.ProfessionalColorsMenuStripGradientBeginDescr)] + public virtual Color MenuStripGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzBegin); } + } + + [SRDescription(SR.ProfessionalColorsMenuStripGradientEndDescr)] + public virtual Color MenuStripGradientEnd{ + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzEnd); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedDescr)] + public virtual Color MenuItemSelected { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdMouseOver); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemBorderDescr)] + public virtual Color MenuItemBorder { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBdrSelected); } + } + + [SRDescription(SR.ProfessionalColorsMenuBorderDescr)] + public virtual Color MenuBorder { + get { return FromKnownColor(KnownColors.msocbvcrCBMenuBdrOuter); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedGradientBeginDescr)] + public virtual Color MenuItemSelectedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseOverBegin); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedGradientEndDescr)] + public virtual Color MenuItemSelectedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMouseOverEnd); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientBeginDescr)] + public virtual Color MenuItemPressedGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuTitleBkgdBegin); } + } + + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientMiddleDescr)] + public virtual Color MenuItemPressedGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle); } + } + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientEndDescr)] + public virtual Color MenuItemPressedGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMenuTitleBkgdEnd); } + } + + [SRDescription(SR.ProfessionalColorsRaftingContainerGradientBeginDescr)] + public virtual Color RaftingContainerGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzBegin); } + } + + [SRDescription(SR.ProfessionalColorsRaftingContainerGradientEndDescr)] + public virtual Color RaftingContainerGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzEnd); } + } + + [SRDescription(SR.ProfessionalColorsSeparatorDarkDescr)] + public virtual Color SeparatorDark { + get { return FromKnownColor(KnownColors.msocbvcrCBSplitterLine); } + } + + [SRDescription(SR.ProfessionalColorsSeparatorLightDescr)] + public virtual Color SeparatorLight { + get { return FromKnownColor(KnownColors.msocbvcrCBSplitterLineLight); } + } + + [SRDescription(SR.ProfessionalColorsStatusStripGradientBeginDescr)] + public virtual Color StatusStripGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzBegin); } + } + + [SRDescription(SR.ProfessionalColorsStatusStripGradientEndDescr)] + public virtual Color StatusStripGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzEnd); } + } + + [SRDescription(SR.ProfessionalColorsToolStripBorderDescr)] + public virtual Color ToolStripBorder { + get { return FromKnownColor(KnownColors.msocbvcrCBShadow); } + } + + [SRDescription(SR.ProfessionalColorsToolStripDropDownBackgroundDescr)] + public virtual Color ToolStripDropDownBackground { + get { return FromKnownColor(KnownColors.msocbvcrCBMenuBkgd); } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientBeginDescr)] + public virtual Color ToolStripGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradVertBegin); } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientMiddleDescr)] + public virtual Color ToolStripGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradVertMiddle); } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientEndDescr)] + public virtual Color ToolStripGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradVertEnd); } + } + + [SRDescription(SR.ProfessionalColorsToolStripContentPanelGradientBeginDescr)] + public virtual Color ToolStripContentPanelGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzBegin); } + } + + [SRDescription(SR.ProfessionalColorsToolStripContentPanelGradientEndDescr)] + public virtual Color ToolStripContentPanelGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzEnd); } + } + + [SRDescription(SR.ProfessionalColorsToolStripPanelGradientBeginDescr)] + public virtual Color ToolStripPanelGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzBegin); } + } + + [SRDescription(SR.ProfessionalColorsToolStripPanelGradientEndDescr)] + public virtual Color ToolStripPanelGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradMainMenuHorzEnd); } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientBeginDescr)] + public virtual Color OverflowButtonGradientBegin { + get { return FromKnownColor(KnownColors.msocbvcrCBGradOptionsBegin); } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientMiddleDescr)] + public virtual Color OverflowButtonGradientMiddle { + get { return FromKnownColor(KnownColors.msocbvcrCBGradOptionsMiddle); } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientEndDescr)] + public virtual Color OverflowButtonGradientEnd { + get { return FromKnownColor(KnownColors.msocbvcrCBGradOptionsEnd); } + } +#endregion Colors + + +#region NotDirectlyExposed + // + + + + internal Color ComboBoxButtonGradientBegin { + get { return MenuItemPressedGradientBegin; } + } + internal Color ComboBoxButtonGradientEnd { + get { return MenuItemPressedGradientEnd; } + } + internal Color ComboBoxButtonSelectedGradientBegin { + get { return MenuItemSelectedGradientBegin; } + } + internal Color ComboBoxButtonSelectedGradientEnd { + get { return MenuItemSelectedGradientEnd;} + } + internal Color ComboBoxButtonPressedGradientBegin { + get { return ButtonPressedGradientBegin; } + } + internal Color ComboBoxButtonPressedGradientEnd { + get { return ButtonPressedGradientEnd; } + } + internal Color ComboBoxButtonOnOverflow { + get { return ToolStripDropDownBackground; } + } + internal Color ComboBoxBorder { + get { return ButtonSelectedHighlightBorder; } + } + internal Color TextBoxBorder { + get { return ButtonSelectedHighlightBorder; } + } +#endregion + + + /* public virtual Color ControlLight { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdLight); } + } */ + + private static Color GetAlphaBlendedColor(Graphics g, Color src, Color dest, int alpha) { + int red = (src.R * alpha + (255 - alpha) * dest.R) / 255; + int green = (src.G * alpha + (255 - alpha) * dest.G) / 255; + int blue = (src.B * alpha + (255 - alpha) * dest.B) / 255; + int newAlpha = (src.A * alpha + (255 - alpha) * dest.A) / 255; + if (g == null) { + return Color.FromArgb(newAlpha, red, green, blue); + } + else { + return g.GetNearestColor(Color.FromArgb(newAlpha, red, green, blue)); + } + } + + + // this particular method gets us closer to office by increasing the resolution... + + private static Color GetAlphaBlendedColorHighRes(Graphics graphics, Color src, Color dest, int alpha) { + + int sum; + int nPart2; + int r, g, b; + + int nPart1 = alpha; + + if (nPart1 < 100) { + nPart2 = 100 - nPart1; + sum = 100; + } + else { + nPart2 = 1000 - nPart1; + sum = 1000; + } + + // By adding on sum/2 before dividing by sum, we properly round the value, + // rather than truncating it, while doing integer math. + + r = (nPart1 * src.R + nPart2 * dest.R + sum / 2) / sum; + g = (nPart1 * src.G + nPart2 * dest.G + sum / 2) / sum; + b = (nPart1 * src.B + nPart2 * dest.B + sum / 2) / sum; + + if (graphics == null) { + return Color.FromArgb(r, g, b); + } + else { + return graphics.GetNearestColor(Color.FromArgb(r, g, b)); + } + + } + + private void InitCommonColors(ref Dictionary rgbTable) { + /// we need to calculate our own alpha blended color based on the Higlight and Window + /// colors on the system. Since terminalserver + alphablending doesnt work we cant just do a + /// FromARGB here. So we have a simple function which calculates the blending for us. + + if (!DisplayInformation.LowResolution) { + using (Graphics g = WindowsFormsUtils.CreateMeasurementGraphics()) { + rgbTable[ProfessionalColorTable.KnownColors.ButtonPressedHighlight] = GetAlphaBlendedColor(g, SystemColors.Window, GetAlphaBlendedColor(g, SystemColors.Highlight, SystemColors.Window, 160), 50); + rgbTable[ProfessionalColorTable.KnownColors.ButtonCheckedHighlight] = GetAlphaBlendedColor(g, SystemColors.Window, GetAlphaBlendedColor(g, SystemColors.Highlight, SystemColors.Window, 80), 20); + rgbTable[ProfessionalColorTable.KnownColors.ButtonSelectedHighlight] = rgbTable[ProfessionalColorTable.KnownColors.ButtonCheckedHighlight]; + } + } + else { + rgbTable[ProfessionalColorTable.KnownColors.ButtonPressedHighlight] = SystemColors.Highlight; + rgbTable[ProfessionalColorTable.KnownColors.ButtonCheckedHighlight] = SystemColors.ControlLight; + rgbTable[ProfessionalColorTable.KnownColors.ButtonSelectedHighlight] = SystemColors.ControlLight; + } + + } + internal void InitSystemColors(ref Dictionary rgbTable) { + usingSystemColors = true; + + InitCommonColors(ref rgbTable); + + + // use locals so we arent fetching again and again. + Color buttonFace = SystemColors.ButtonFace; + Color buttonShadow = SystemColors.ButtonShadow; + Color highlight = SystemColors.Highlight; + Color window = SystemColors.Window; + Color empty = Color.Empty; + Color controlText = SystemColors.ControlText; + Color buttonHighlight = SystemColors.ButtonHighlight; + Color grayText = SystemColors.GrayText; + Color highlightText = SystemColors.HighlightText; + Color windowText = SystemColors.WindowText; + + // initialize to high contrast + Color gradientBegin = buttonFace; + Color gradientMiddle = buttonFace; + Color gradientEnd = buttonFace; + Color msocbvcrCBCtlBkgdMouseOver = highlight; + Color msocbvcrCBCtlBkgdMouseDown = highlight; + + bool lowResolution = DisplayInformation.LowResolution; + bool highContrast = DisplayInformation.HighContrast; + + if (lowResolution) { + msocbvcrCBCtlBkgdMouseOver = window; + } + else if (!highContrast) { + gradientBegin = GetAlphaBlendedColorHighRes(null, buttonFace, window, 23); + gradientMiddle = GetAlphaBlendedColorHighRes(null, buttonFace, window, 50 ); + gradientEnd = SystemColors.ButtonFace; + + + msocbvcrCBCtlBkgdMouseOver = GetAlphaBlendedColorHighRes(null, highlight, window, 30); + msocbvcrCBCtlBkgdMouseDown = GetAlphaBlendedColorHighRes(null, highlight, window, 50); + + + } + + + + if (lowResolution || highContrast) { + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = SystemColors.ControlLight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = buttonShadow; + } + else { + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = GetAlphaBlendedColorHighRes(null, window, buttonFace, 165); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = GetAlphaBlendedColorHighRes(null, highlight, window, 50); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = GetAlphaBlendedColorHighRes(null, buttonShadow, window, 75); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 205); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 70); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 90); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 40); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 70); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 90); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = GetAlphaBlendedColorHighRes(null, controlText, buttonShadow, 20); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = GetAlphaBlendedColorHighRes(null, buttonFace, window, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = GetAlphaBlendedColorHighRes(null, buttonShadow, window, 70); + + } + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = (lowResolution) ? SystemColors.ControlLight : highlight; + + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterFloating] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseDown] = highlight; + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelected] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelectedMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdLight] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseDown] = highlight; +// rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = (lowResolution) ? SystemColors.ControlLight : highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextLight] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseDown] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDockSeparatorLine] = empty; + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandleShadow] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDropDownArrow] = empty; + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzBegin] = buttonFace; + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverEnd] = msocbvcrCBCtlBkgdMouseOver; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverBegin] = msocbvcrCBCtlBkgdMouseOver; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverMiddle] = msocbvcrCBCtlBkgdMouseOver; + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsEnd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverBegin] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverEnd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverMiddle] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedBegin] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedEnd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedMiddle] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedBegin] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedEnd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedMiddle] = empty; + + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertBegin] = gradientBegin; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertMiddle] = gradientMiddle; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertEnd] = gradientEnd; + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownBegin] = msocbvcrCBCtlBkgdMouseDown; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownMiddle] = msocbvcrCBCtlBkgdMouseDown; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownEnd] = msocbvcrCBCtlBkgdMouseDown; + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdBegin] = gradientBegin; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdEnd] = gradientMiddle; + + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledDark] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLowColorIconDisabled] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMainMenuBkgd] = buttonFace; + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlTextDisabled] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuShadow] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuSplitArrow] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBOptionsButtonShadow] = empty; + + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBShadow] = rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd]; + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = SystemColors.InactiveCaption; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = SystemColors.InactiveCaptionText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = SystemColors.Info; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = SystemColors.InfoText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = buttonFace; + + + } + + internal void InitOliveLunaColors(ref Dictionary rgbTable) { + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(81, 94, 51); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(81, 94, 51); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterFloating] = Color.FromArgb(116, 134, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = Color.FromArgb(209, 222, 173); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseDown] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelected] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelectedMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgd] = Color.FromArgb(209, 222, 173); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextLight] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDockSeparatorLine] = Color.FromArgb(96, 119, 66); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = Color.FromArgb(81, 94, 51); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandleShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDropDownArrow] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzBegin] = Color.FromArgb(217, 217, 167); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = Color.FromArgb(242, 241, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = Color.FromArgb(230, 230, 209); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = Color.FromArgb(160, 177, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = Color.FromArgb(186, 201, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdBegin] = Color.FromArgb(237, 240, 214); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdEnd] = Color.FromArgb(181, 196, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownBegin] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownEnd] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownMiddle] = Color.FromArgb(255, 177, 109); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverEnd] = Color.FromArgb(255, 203, 136); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = Color.FromArgb(186, 204, 150); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsEnd] = Color.FromArgb(96, 119, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = Color.FromArgb(141, 160, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverEnd] = Color.FromArgb(255, 193, 118); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedBegin] = Color.FromArgb(254, 140, 73); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedEnd] = Color.FromArgb(255, 221, 152); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedMiddle] = Color.FromArgb(255, 184, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedBegin] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedEnd] = Color.FromArgb(255, 166, 76); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedMiddle] = Color.FromArgb(255, 195, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertBegin] = Color.FromArgb(255, 255, 237); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertEnd] = Color.FromArgb(181, 196, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertMiddle] = Color.FromArgb(206, 220, 167); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledDark] = Color.FromArgb(131, 144, 113); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledLight] = Color.FromArgb(243, 244, 240); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLowColorIconDisabled] = Color.FromArgb(159, 174, 122); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMainMenuBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = Color.FromArgb(117, 141, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = Color.FromArgb(244, 244, 238); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgd] = Color.FromArgb(216, 227, 182); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(173, 181, 157); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(173, 181, 157); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuShadow] = Color.FromArgb(134, 148, 108); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuSplitArrow] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBOptionsButtonShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBShadow] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = Color.FromArgb(244, 247, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = Color.FromArgb(197, 212, 159); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = Color.FromArgb(116, 134, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = Color.FromArgb(220, 224, 208); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = Color.FromArgb(153, 84, 10); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = Color.FromArgb(96, 119, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = Color.FromArgb(176, 194, 140); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(63, 93, 56); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(183, 198, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(183, 198, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = Color.FromArgb(191, 191, 223); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = Color.FromArgb(239, 235, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = Color.FromArgb(126, 125, 104); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = Color.FromArgb(239, 235, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(159, 171, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(159, 171, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = Color.FromArgb(217, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = Color.FromArgb(230, 234, 208); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = Color.FromArgb(150, 145, 133); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = Color.FromArgb(161, 176, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = Color.FromArgb(210, 223, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(90, 107, 70); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(90, 107, 70); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = Color.FromArgb(243, 242, 231); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = Color.FromArgb(170, 0, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(217, 217, 167); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(217, 217, 167); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(242, 241, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(242, 241, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = Color.FromArgb(255, 255, 237); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = Color.FromArgb(211, 211, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = Color.FromArgb(151, 160, 123); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = Color.FromArgb(226, 231, 191); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = Color.FromArgb(171, 192, 138); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = Color.FromArgb(117, 141, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = Color.FromArgb(218, 227, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = Color.FromArgb(151, 160, 123); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = Color.FromArgb(242, 240, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = Color.FromArgb(96, 119, 66); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = Color.FromArgb(175, 192, 130); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = Color.FromArgb(234, 233, 225); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = Color.FromArgb(181, 196, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = Color.FromArgb(253, 238, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = Color.FromArgb(175, 186, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = Color.FromArgb(115, 137, 84); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = Color.FromArgb(253, 247, 233); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = Color.FromArgb(151, 160, 123); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = Color.FromArgb(151, 160, 123); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = Color.FromArgb(187, 85, 3); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = Color.FromArgb(251, 200, 79); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = Color.FromArgb(200, 212, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = Color.FromArgb(176, 191, 138); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(234, 240, 207); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(234, 240, 207); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = Color.FromArgb(247, 190, 87); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = Color.FromArgb(255, 255, 220); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = Color.FromArgb(50, 69, 105); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = Color.FromArgb(248, 222, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = Color.FromArgb(232, 127, 8); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = Color.FromArgb(238, 147, 17); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = Color.FromArgb(251, 230, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = Color.FromArgb(64, 81, 59); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(120, 142, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(120, 142, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = Color.FromArgb(242, 240, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = Color.FromArgb(96, 128, 88); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = Color.FromArgb(206, 220, 167); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = Color.FromArgb(107, 129, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = Color.FromArgb(107, 129, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = Color.FromArgb(107, 129, 107); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = Color.FromArgb(151, 160, 123); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = Color.FromArgb(193, 198, 176); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = Color.FromArgb(211, 211, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = Color.FromArgb(249, 249, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = Color.FromArgb(237, 242, 212); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = Color.FromArgb(191, 206, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = Color.FromArgb(242, 241, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = Color.FromArgb(116, 134, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = Color.FromArgb(243, 242, 231); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = Color.FromArgb(164, 185, 127); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = Color.FromArgb(197, 212, 159); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = Color.FromArgb(222, 222, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = Color.FromArgb(188, 187, 177); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = Color.FromArgb(255, 255, 204); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = Color.FromArgb(116, 134, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = Color.FromArgb(216, 227, 182); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = Color.FromArgb(188, 205, 131); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = Color.FromArgb(217, 217, 167); + } + + internal void InitSilverLunaColors(ref Dictionary rgbTable) { + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(173, 174, 193); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterFloating] = Color.FromArgb(122, 121, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = Color.FromArgb(219, 218, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseDown] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelected] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelectedMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgd] = Color.FromArgb(219, 218, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextLight] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDockSeparatorLine] = Color.FromArgb(110, 109, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = Color.FromArgb(84, 84, 117); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandleShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDropDownArrow] = Color.FromArgb(224, 223, 227); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzBegin] = Color.FromArgb(215, 215, 229); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = Color.FromArgb(243, 243, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = Color.FromArgb(215, 215, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = Color.FromArgb(118, 116, 151); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = Color.FromArgb(184, 185, 202); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdBegin] = Color.FromArgb(232, 233, 242); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdEnd] = Color.FromArgb(172, 170, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownBegin] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownEnd] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownMiddle] = Color.FromArgb(255, 177, 109); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverEnd] = Color.FromArgb(255, 203, 136); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = Color.FromArgb(186, 185, 206); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsEnd] = Color.FromArgb(118, 116, 146); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = Color.FromArgb(156, 155, 180); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverEnd] = Color.FromArgb(255, 193, 118); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedBegin] = Color.FromArgb(254, 140, 73); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedEnd] = Color.FromArgb(255, 221, 152); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedMiddle] = Color.FromArgb(255, 184, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedBegin] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedEnd] = Color.FromArgb(255, 166, 76); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedMiddle] = Color.FromArgb(255, 195, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertBegin] = Color.FromArgb(249, 249, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertEnd] = Color.FromArgb(147, 145, 176); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertMiddle] = Color.FromArgb(225, 226, 236); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledDark] = Color.FromArgb(122, 121, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledLight] = Color.FromArgb(247, 245, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLowColorIconDisabled] = Color.FromArgb(168, 167, 190); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMainMenuBkgd] = Color.FromArgb(198, 200, 215); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = Color.FromArgb(253, 250, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgd] = Color.FromArgb(214, 211, 231); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(185, 187, 200); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(185, 187, 200); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuShadow] = Color.FromArgb(154, 140, 176); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuSplitArrow] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBOptionsButtonShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBShadow] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = Color.FromArgb(110, 109, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = Color.FromArgb(192, 192, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = Color.FromArgb(122, 121, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = Color.FromArgb(59, 59, 63); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = Color.FromArgb(7, 70, 213); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = Color.FromArgb(118, 116, 146); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = Color.FromArgb(186, 185, 206); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(75, 75, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(148, 148, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(148, 148, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(171, 169, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(171, 169, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(224, 223, 227); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(224, 223, 227); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = Color.FromArgb(191, 191, 223); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = Color.FromArgb(239, 235, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = Color.FromArgb(126, 125, 104); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = Color.FromArgb(223, 223, 234); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(162, 162, 181); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(162, 162, 181); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = Color.FromArgb(212, 213, 229); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = Color.FromArgb(227, 227, 236); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = Color.FromArgb(150, 145, 133); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = Color.FromArgb(169, 168, 191); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = Color.FromArgb(208, 208, 223); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(92, 91, 121); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(92, 91, 121); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = Color.FromArgb(238, 238, 244); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = Color.FromArgb(170, 0, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(215, 215, 229); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(215, 215, 229); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(243, 243, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(243, 243, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = Color.FromArgb(249, 249, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = Color.FromArgb(211, 211, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = Color.FromArgb(155, 154, 179); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = Color.FromArgb(223, 223, 234); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = Color.FromArgb(177, 176, 195); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = Color.FromArgb(212, 212, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = Color.FromArgb(155, 154, 179); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = Color.FromArgb(239, 239, 244); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = Color.FromArgb(110, 109, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = Color.FromArgb(168, 167, 191); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = Color.FromArgb(234, 233, 225); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = Color.FromArgb(165, 164, 189); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = Color.FromArgb(253, 238, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = Color.FromArgb(229, 229, 235); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = Color.FromArgb(112, 111, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = Color.FromArgb(253, 247, 233); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = Color.FromArgb(155, 154, 179); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = Color.FromArgb(155, 154, 179); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = Color.FromArgb(187, 85, 3); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = Color.FromArgb(251, 200, 79); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = Color.FromArgb(204, 206, 219); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = Color.FromArgb(147, 145, 176); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(225, 226, 236); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(225, 226, 236); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = Color.FromArgb(247, 190, 87); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = Color.FromArgb(255, 255, 220); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = Color.FromArgb(50, 69, 105); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = Color.FromArgb(248, 222, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = Color.FromArgb(232, 127, 8); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = Color.FromArgb(238, 147, 17); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = Color.FromArgb(251, 230, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = Color.FromArgb(110, 109, 143); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(168, 167, 191); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(168, 167, 191); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = Color.FromArgb(224, 223, 227); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = Color.FromArgb(243, 243, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = Color.FromArgb(124, 124, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = Color.FromArgb(215, 215, 229); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = Color.FromArgb(142, 142, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = Color.FromArgb(142, 142, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = Color.FromArgb(142, 142, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = Color.FromArgb(155, 154, 179); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = Color.FromArgb(195, 195, 210); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = Color.FromArgb(236, 234, 218); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = Color.FromArgb(247, 247, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = Color.FromArgb(239, 239, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = Color.FromArgb(179, 178, 204); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = Color.FromArgb(243, 243, 247); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = Color.FromArgb(122, 121, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = Color.FromArgb(238, 238, 244); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = Color.FromArgb(165, 172, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = Color.FromArgb(192, 192, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = Color.FromArgb(222, 222, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = Color.FromArgb(161, 160, 187); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = Color.FromArgb(255, 255, 204); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = Color.FromArgb(122, 121, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = Color.FromArgb(184, 188, 234); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = Color.FromArgb(198, 198, 217); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = Color.FromArgb(215, 215, 229); + } + + private void InitRoyaleColors(ref Dictionary rgbTable) { + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = Color.FromArgb(238,237,240); // msocbvcrCBBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle ] = Color.FromArgb(189,188,191); // msocbvcrCBDragHandle -> Needs to equal VSCOLOR_COMMANDBAR_DRAGHANDLE in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = Color.FromArgb(193,193,196); // msocbvcrCBSplitterLine + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = Color.FromArgb(167,166,170); // msocbvcrCBTitleBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = Color.FromArgb(255,255,255); // msocbvcrCBTitleText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterFloating] = Color.FromArgb(142,141,145); // msocbvcrCBBdrOuterFloating + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(235,233,237); // msocbvcrCBBdrOuterDocked + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = Color.FromArgb(238,237,240); // msocbvcrCBTearOffHandle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = Color.FromArgb(194,207,229); // msocbvcrCBTearOffHandleMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgd] = Color.FromArgb(238,237,240); // msocbvcrCBCtlBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlText] = Color.FromArgb(000,000,000); // msocbvcrCBCtlText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextDisabled] = Color.FromArgb(176,175,179); // msocbvcrCBCtlTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(194,207,229); // msocbvcrCBCtlBkgdMouseOver -> Needs to equal VSCOLOR_COMMANDBAR_HOVER in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseOver] = Color.FromArgb(51,94,168); // msocbvcrCBCtlBdrMouseOver -> Needs to equal VSCOLOR_COMMANDBAR_BORDER in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(000,000,000); // msocbvcrCBCtlTextMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseDown] = Color.FromArgb(153,175,212); // msocbvcrCBCtlBkgdMouseDown -> Needs to equal VSCOLOR_COMMANDBAR_HOVEROVERSELECTED in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseDown] = Color.FromArgb(51,94,168); // msocbvcrCBCtlBdrMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseDown] = Color.FromArgb(255,255,255); // msocbvcrCBCtlTextMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = Color.FromArgb(226,229,238); // msocbvcrCBCtlBkgdSelected -> Needs to equal VSCOLOR_COMMANDBAR_SELECTED in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelected] = Color.FromArgb(51,94,168); // msocbvcrCBCtlBdrSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = Color.FromArgb(51,94,168); // msocbvcrCBCtlBkgdSelectedMouseOver -> Needs to equal VSCOLOR_COMMANDBAR_HOVEROVERSELECTEDICON in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelectedMouseOver] = Color.FromArgb(51,94,168); // msocbvcrCBCtlBdrSelectedMouseOver -> Needs to equal VSCOLOR_COMMANDBAR_HOVEROVERSELECTEDICON_BORDER in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdLight] = Color.FromArgb(255,255,255); // msocbvcrCBCtlBkgdLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextLight] = Color.FromArgb(167,166,170); // msocbvcrCBCtlTextLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMainMenuBkgd] = Color.FromArgb(235,233,237); // msocbvcrCBMainMenuBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = Color.FromArgb(252,252,252); // msocbvcrCBMenuBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlText] = Color.FromArgb(0,0,0); // msocbvcrCBMenuCtlText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlTextDisabled] = Color.FromArgb(193,193,196); // msocbvcrCBMenuCtlTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = Color.FromArgb(134,133,136); // msocbvcrCBMenuBdrOuter + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgd] = Color.FromArgb(238,237,240); // msocbvcrCBMenuIconBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(228,226,230); // msocbvcrCBMenuIconBkgdDropped + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuSplitArrow] = Color.FromArgb(167,166,170); // msocbvcrCBMenuSplitArrow + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = Color.FromArgb(245,244,246); // msocbvcrWPBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(255,51,153); // msocbvcrWPText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = Color.FromArgb(255,51,153); // msocbvcrWPTitleBkgdActive + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = Color.FromArgb(255,51,153); // msocbvcrWPTitleBkgdInactive + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = Color.FromArgb(255,51,153); // msocbvcrWPTitleTextActive + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = Color.FromArgb(255,51,153); // msocbvcrWPTitleTextInactive + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = Color.FromArgb(255,51,153); // msocbvcrWPBdrOuterFloating + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = Color.FromArgb(255,51,153); // msocbvcrWPBdrOuterDocked + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = Color.FromArgb(255,51,153); // msocbvcrWPCtlBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = Color.FromArgb(255,51,153); // msocbvcrWPCtlText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = Color.FromArgb(255,51,153); // msocbvcrWPCtlBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = Color.FromArgb(255,51,153); // msocbvcrWPCtlBdrDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = Color.FromArgb(255,51,153); // msocbvcrWPCtlTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = Color.FromArgb(255,51,153); // msocbvcrWPCtlBkgdDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(255,51,153); // msocbvcrWPCtlBdrDefault + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = Color.FromArgb(255,51,153); // msocbvcrWPGroupline + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = Color.FromArgb(255,51,153); // msocbvcrSBBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = Color.FromArgb(255,51,153); // msocbvcrOBBkgdBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = Color.FromArgb(255,51,153); // msocbvcrOBBkgdBdrContrast + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = Color.FromArgb(255,51,153); // msocbvcrOABBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderCellBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderSeeThroughSelection + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderCellBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = Color.FromArgb(255,51,153); // msocbvcrGDHeaderCellBkgdSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = Color.FromArgb(255,255,255); // msocbvcrCBSplitterLineLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBShadow] = Color.FromArgb(238,237,240); // msocbvcrCBShadow -> Needs to equal VSCOLOR_COMMANDBAR_SHADOW in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBOptionsButtonShadow] = Color.FromArgb(245,244,246); // msocbvcrCBOptionsButtonShadow + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = Color.FromArgb(193,193,196); // msocbvcrWPNavBarBkgnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = Color.FromArgb(245,244,246); // msocbvcrWPBdrInnerDocked + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(235,233,237); // msocbvcrCBLabelBkgnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledLight] = Color.FromArgb(235,233,237); // msocbvcrCBIconDisabledLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledDark] = Color.FromArgb(167,166,170); // msocbvcrCBIconDisabledDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLowColorIconDisabled] = Color.FromArgb(176,175,179); // msocbvcrCBLowColorIconDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzBegin] = Color.FromArgb(235,233,237); // msocbvcrCBGradMainMenuHorzBegin -> Needs to equal VSCOLOR_ENVIRONMENT_BACKGROUND and VSCOLOR_ENVIRONMENT_BACKGROUND_GRADIENTBEGIN in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = Color.FromArgb(251,250,251); // msocbvcrCBGradMainMenuHorzEnd -> Needs to equal VSCOLOR_ENVIRONMENT_BACKGROUND_GRADIENTEND in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertBegin] = Color.FromArgb(252,252,252); // msocbvcrCBGradVertBegin -> Needs to equal VSCOLOR_COMMANDBAR_GRADIENT_BEGIN in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertMiddle] = Color.FromArgb(245,244,246); // msocbvcrCBGradVertMiddle -> Needs to equal VSCOLOR_COMMANDBAR_GRADIENT_MIDDLE in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertEnd] = Color.FromArgb(235,233,237); // msocbvcrCBGradVertEnd -> Needs to equal VSCOLOR_COMMANDBAR_GRADIENT_END in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = Color.FromArgb(242,242,242); // msocbvcrCBGradOptionsBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = Color.FromArgb(224,224,225); // msocbvcrCBGradOptionsMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsEnd] = Color.FromArgb(167,166,170); // msocbvcrCBGradOptionsEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdBegin] = Color.FromArgb(252,252,252); // msocbvcrCBGradMenuTitleBkgdBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdEnd] = Color.FromArgb(245,244,246); // msocbvcrCBGradMenuTitleBkgdEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = Color.FromArgb(247,246,248); // msocbvcrCBGradMenuIconBkgdDroppedBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = Color.FromArgb(241,240,242); // msocbvcrCBGradMenuIconBkgdDroppedMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = Color.FromArgb(228,226,230); // msocbvcrCBGradMenuIconBkgdDroppedEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedBegin] = Color.FromArgb(226,229,238); // msocbvcrCBGradOptionsSelectedBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedMiddle] = Color.FromArgb(226,229,238); // msocbvcrCBGradOptionsSelectedMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedEnd] = Color.FromArgb(226,229,238); // msocbvcrCBGradOptionsSelectedEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverBegin] = Color.FromArgb(194,207,229); // msocbvcrCBGradOptionsMouseOverBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverMiddle] = Color.FromArgb(194,207,229); // msocbvcrCBGradOptionsMouseOverMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverEnd] = Color.FromArgb(194,207,229); // msocbvcrCBGradOptionsMouseOverEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedBegin] = Color.FromArgb(226,229,238); // msocbvcrCBGradSelectedBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedMiddle] = Color.FromArgb(226,229,238); // msocbvcrCBGradSelectedMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedEnd] = Color.FromArgb(226,229,238); // msocbvcrCBGradSelectedEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverBegin] = Color.FromArgb(194,207,229); // msocbvcrCBGradMouseOverBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverMiddle] = Color.FromArgb(194,207,229); // msocbvcrCBGradMouseOverMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverEnd] = Color.FromArgb(194,207,229); // msocbvcrCBGradMouseOverEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownBegin] = Color.FromArgb(153,175,212); // msocbvcrCBGradMouseDownBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownMiddle] = Color.FromArgb(153,175,212); // msocbvcrCBGradMouseDownMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownEnd] = Color.FromArgb(153,175,212); // msocbvcrCBGradMouseDownEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = Color.FromArgb(235,233,237); // msocbvcrNetLookBkgnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuShadow] = Color.FromArgb(000,000,000); // msocbvcrCBMenuShadow + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDockSeparatorLine] = Color.FromArgb(51,94,168); // msocbvcrCBDockSeparatorLine + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDropDownArrow] = Color.FromArgb(235,233,237); // msocbvcrCBDropDownArrow + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = Color.FromArgb(255,51,153); // msocbvcrOLKGridlines + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = Color.FromArgb(255,51,153); // msocbvcrOLKGroupText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = Color.FromArgb(255,51,153); // msocbvcrOLKGroupLine + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = Color.FromArgb(255,51,153); // msocbvcrOLKGroupShaded + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = Color.FromArgb(255,51,153); // msocbvcrOLKGroupNested + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = Color.FromArgb(255,51,153); // msocbvcrOLKIconBar + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = Color.FromArgb(255,51,153); // msocbvcrOLKFlagNone + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = Color.FromArgb(255,51,153); // msocbvcrOLKFolderbarLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = Color.FromArgb(255,51,153); // msocbvcrOLKFolderbarDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = Color.FromArgb(255,51,153); // msocbvcrOLKFolderbarText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(255,51,153); // msocbvcrOLKWBButtonLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = Color.FromArgb(255,51,153); // msocbvcrOLKWBButtonDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = Color.FromArgb(255,51,153); // msocbvcrOLKWBSelectedButtonLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = Color.FromArgb(255,51,153); // msocbvcrOLKWBSelectedButtonDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = Color.FromArgb(255,51,153); // msocbvcrOLKWBHoverButtonLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = Color.FromArgb(255,51,153); // msocbvcrOLKWBHoverButtonDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = Color.FromArgb(255,51,153); // msocbvcrOLKWBPressedButtonLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = Color.FromArgb(255,51,153); // msocbvcrOLKWBPressedButtonDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = Color.FromArgb(255,51,153); // msocbvcrOLKWBDarkOutline + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(255,51,153); // msocbvcrOLKWBSplitterLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = Color.FromArgb(255,51,153); // msocbvcrOLKWBSplitterDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = Color.FromArgb(255,51,153); // msocbvcrOLKWBActionDividerLine + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = Color.FromArgb(255,51,153); // msocbvcrOLKWBLabelText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = Color.FromArgb(255,51,153); // msocbvcrOLKWBFoldersBackground + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = Color.FromArgb(255,51,153); // msocbvcrOLKTodayIndicatorLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = Color.FromArgb(255,51,153); // msocbvcrOLKTodayIndicatorDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = Color.FromArgb(255,51,153); // msocbvcrOLKInfoBarBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = Color.FromArgb(255,51,153); // msocbvcrOLKInfoBarText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = Color.FromArgb(255,51,153); // msocbvcrOLKPreviewPaneLabelText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = Color.FromArgb(0,61,178); // msocbvcrHyperlink + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = Color.FromArgb(170,0,170); // msocbvcrHyperlinkFollowed + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGWorkspaceBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGMDIParentWorkspaceBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGRulerBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGRulerActiveBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGRulerInactiveBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = Color.FromArgb(255,51,153); // msocbvcrOGRulerText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = Color.FromArgb(255,51,153); // msocbvcrOGRulerTabStopTicks + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = Color.FromArgb(255,51,153); // msocbvcrOGRulerBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = Color.FromArgb(255,51,153); // msocbvcrOGRulerTabBoxBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = Color.FromArgb(255,51,153); // msocbvcrOGRulerTabBoxBdrHighlight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = Color.FromArgb(255,51,153); // msocbvcrXLFormulaBarBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandleShadow] = Color.FromArgb(255,255,255); // msocbvcrCBDragHandleShadow -> Needs to equal VSCOLOR_COMMANDBAR_DRAGHANDLE_SHADOW in vscolors.cpp + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = Color.FromArgb(255,51,153); // msocbvcrOGTaskPaneGroupBoxHeaderBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = Color.FromArgb(255,51,153); // msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = Color.FromArgb(255,51,153); // msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = Color.FromArgb(255,51,153); // msocbvcrPPOutlineThumbnailsPaneTabBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = Color.FromArgb(255,51,153); // msocbvcrPPOutlineThumbnailsPaneTabText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = Color.FromArgb(255,51,153); // msocbvcrPPSlideBdrActiveSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = Color.FromArgb(255,51,153); // msocbvcrPPSlideBdrInactiveSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = Color.FromArgb(255,51,153); // msocbvcrPPSlideBdrMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = Color.FromArgb(255,51,153); // msocbvcrPPSlideBdrActiveSelectedMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = Color.FromArgb(0,0,0); // msocbvcrDlgGroupBoxText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = Color.FromArgb(237,235,239); // msocbvcrScrollbarBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = Color.FromArgb(155,154,156); // msocbvcrListHeaderArrow + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = Color.FromArgb(188,202,226); // msocbvcrDisabledHighlightedText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(235,233,237); // msocbvcrFocuslessHighlightedBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(000,000,000); // msocbvcrFocuslessHighlightedText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = Color.FromArgb(167,166,170); // msocbvcrDisabledFocuslessHighlightedText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = Color.FromArgb(255,51,153); // msocbvcrWPCtlTextMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = Color.FromArgb(255,51,153); // msocbvcrWPTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = Color.FromArgb(255,51,153); // msocbvcrWPInfoTipBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = Color.FromArgb(255,51,153); // msocbvcrWPInfoTipText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(255,51,153); // msocbvcrDWActiveTabBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(255,51,153); // msocbvcrDWActiveTabText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(255,51,153); // msocbvcrDWActiveTabTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(255,51,153); // msocbvcrDWInactiveTabBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255,51,153); // msocbvcrDWInactiveTabText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = Color.FromArgb(255,51,153); // msocbvcrDWTabBkgdMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = Color.FromArgb(255,51,153); // msocbvcrDWTabTextMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = Color.FromArgb(255,51,153); // msocbvcrDWTabBkgdMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = Color.FromArgb(255,51,153); // msocbvcrDWTabTextMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPLightBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPDarkBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupHeaderLightBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupHeaderDarkBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupHeaderText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupContentLightBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupContentDarkBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupContentText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupContentTextDisabled + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255,51,153); // msocbvcrGSPGroupline + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = Color.FromArgb(255,51,153); // msocbvcrGSPHyperlink + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = Color.FromArgb(212,212,226); // msocbvcrDocTabBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = Color.FromArgb(000,000,000); // msocbvcrDocTabText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = Color.FromArgb(118,116,146); // msocbvcrDocTabBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = Color.FromArgb(255,255,255); // msocbvcrDocTabBdrLight + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = Color.FromArgb(186,185,206); // msocbvcrDocTabBdrDark + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = Color.FromArgb(255,255,255); // msocbvcrDocTabBkgdSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = Color.FromArgb(000,000,000); // msocbvcrDocTabTextSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = Color.FromArgb(124,124,148); // msocbvcrDocTabBdrSelected + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(193,210,238); // msocbvcrDocTabBkgdMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(49,106,197); // msocbvcrDocTabTextMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(49,106,197); // msocbvcrDocTabBdrMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(49,106,197); // msocbvcrDocTabBdrLightMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(49,106,197); // msocbvcrDocTabBdrDarkMouseOver + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = Color.FromArgb(154,183,228); // msocbvcrDocTabBkgdMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = Color.FromArgb(000,000,000); // msocbvcrDocTabTextMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = Color.FromArgb(75,75,111); // msocbvcrDocTabBdrMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = Color.FromArgb(75,75,111); // msocbvcrDocTabBdrLightMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = Color.FromArgb(75,75,111); // msocbvcrDocTabBdrDarkMouseDown + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = Color.FromArgb(246,244,236); // msocbvcrToastGradBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = Color.FromArgb(179,178,204); // msocbvcrToastGradEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(236,233,216); // msocbvcrJotNavUIGradBegin + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(236,233,216); // msocbvcrJotNavUIGradMiddle + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = Color.FromArgb(255,255,255); // msocbvcrJotNavUIGradEnd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = Color.FromArgb(000,000,000); // msocbvcrJotNavUIText + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(172,168,153); // msocbvcrJotNavUIBdr + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = Color.FromArgb(224,223,227); // msocbvcrPlacesBarBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = Color.FromArgb(152,181,226); // msocbvcrPubPrintDocScratchPageBkgd + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = Color.FromArgb(193,210,238); // msocbvcrPubWebDocScratchPageBkgd + + } + + + + + internal void InitThemedColors(ref Dictionary rgbTable) { + + string colorScheme = VisualStyleInformation.ColorScheme; + string themeFileName = System.IO.Path.GetFileName(VisualStyleInformation.ThemeFilename); + bool initializedTable = false; + + // VS compares the filename of the theme to determine luna v. royale. + if (string.Equals(lunaFileName, themeFileName, StringComparison.OrdinalIgnoreCase)) { + // once we know it's luna we've got to pick between + // normal (blue) homestead (olive) and metallic (silver) + if (colorScheme == normalColorScheme) { + InitBlueLunaColors(ref rgbTable); + usingSystemColors = false; + initializedTable = true; + } + else if (colorScheme == oliveColorScheme) { + InitOliveLunaColors(ref rgbTable); + usingSystemColors = false; + initializedTable = true; + } + else if (colorScheme == silverColorScheme) { + InitSilverLunaColors(ref rgbTable); + usingSystemColors = false; + initializedTable = true; + } + + } + else if (string.Equals(aeroFileName, themeFileName, StringComparison.OrdinalIgnoreCase)) { + // On Vista running Aero theme, Office looks like it's using SystemColors + // With the exception of the MenuItemSelected Color for MenuStrip items that + // are contained in DropDowns. We're going to copy their behavior + InitSystemColors(ref rgbTable); + usingSystemColors = true; + initializedTable = true; + + // VSWhidbey 533213 + // Exception to SystemColors, use the ButtonSelectedHighlight color otherwise + // the background for DropDown MenuStrip items will have no contrast + rgbTable[KnownColors.msocbvcrCBCtlBkgdMouseOver] = rgbTable[KnownColors.ButtonSelectedHighlight]; + + // DevDiv Bugs 83608, CheckedBackground of ToolStripMenuItem + rgbTable[KnownColors.msocbvcrCBCtlBkgdSelected] = rgbTable[KnownColors.msocbvcrCBCtlBkgdMouseOver]; + } + else if (string.Equals(royaleFileName, themeFileName,StringComparison.OrdinalIgnoreCase)) { + + // once we know it's royale (TabletPC/MCE) we know about two color scheme names + // which should do exactly the same thing + if (colorScheme == normalColorScheme || colorScheme == royaleColorScheme ) { + InitRoyaleColors(ref rgbTable); + usingSystemColors = false; + initializedTable = true; + } + + } + + + if (!initializedTable) { + // unknown color scheme - bailing + + InitSystemColors(ref rgbTable); + usingSystemColors = true; + } + + InitCommonColors(ref rgbTable); + + } + + internal void InitBlueLunaColors(ref Dictionary rgbTable) { + + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(196, 205, 218); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = Color.FromArgb(196, 205, 218); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterFloating] = Color.FromArgb(42, 102, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = Color.FromArgb(196, 219, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseDown] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelected] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBdrSelectedMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgd] = Color.FromArgb(196, 219, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextLight] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDockSeparatorLine] = Color.FromArgb(0, 53, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = Color.FromArgb(39, 65, 118); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandleShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDropDownArrow] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzBegin] = Color.FromArgb(158, 190, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = Color.FromArgb(196, 218, 250); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = Color.FromArgb(203, 221, 246); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = Color.FromArgb(114, 155, 215); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = Color.FromArgb(161, 197, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdBegin] = Color.FromArgb(227, 239, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuTitleBkgdEnd] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownBegin] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownEnd] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseDownMiddle] = Color.FromArgb(255, 177, 109); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverEnd] = Color.FromArgb(255, 203, 136); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = Color.FromArgb(127, 177, 250); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsEnd] = Color.FromArgb(0, 53, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = Color.FromArgb(82, 127, 208); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverBegin] = Color.FromArgb(255, 255, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverEnd] = Color.FromArgb(255, 193, 118); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMouseOverMiddle] = Color.FromArgb(255, 225, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedBegin] = Color.FromArgb(254, 140, 73); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedEnd] = Color.FromArgb(255, 221, 152); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsSelectedMiddle] = Color.FromArgb(255, 184, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedBegin] = Color.FromArgb(255, 223, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedEnd] = Color.FromArgb(255, 166, 76); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradSelectedMiddle] = Color.FromArgb(255, 195, 116); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertBegin] = Color.FromArgb(227, 239, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertEnd] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradVertMiddle] = Color.FromArgb(203, 225, 252); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledDark] = Color.FromArgb(97, 122, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBIconDisabledLight] = Color.FromArgb(233, 236, 242); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLabelBkgnd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBLowColorIconDisabled] = Color.FromArgb(109, 150, 208); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMainMenuBkgd] = Color.FromArgb(153, 204, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = Color.FromArgb(0, 45, 150); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = Color.FromArgb(246, 246, 246); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuCtlTextDisabled] = Color.FromArgb(141, 141, 141); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgd] = Color.FromArgb(203, 225, 252); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(172, 183, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuIconBkgdDropped] = Color.FromArgb(172, 183, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuShadow] = Color.FromArgb(95, 130, 234); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuSplitArrow] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBOptionsButtonShadow] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBShadow] = Color.FromArgb(59, 97, 156); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = Color.FromArgb(106, 140, 203); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = Color.FromArgb(241, 249, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = Color.FromArgb(169, 199, 240); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = Color.FromArgb(42, 102, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = Color.FromArgb(187, 206, 236); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = Color.FromArgb(0, 70, 213); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = Color.FromArgb(0, 53, 154); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = Color.FromArgb(117, 166, 241); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = Color.FromArgb(0, 0, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = Color.FromArgb(59, 97, 156); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(94, 94, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = Color.FromArgb(94, 94, 94); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(129, 169, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = Color.FromArgb(129, 169, 226); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = Color.FromArgb(254, 128, 62); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = Color.FromArgb(255, 238, 194); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = Color.FromArgb(89, 89, 172); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = Color.FromArgb(239, 235, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = Color.FromArgb(126, 125, 104); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = Color.FromArgb(239, 235, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = Color.FromArgb(255, 192, 111); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = Color.FromArgb(191, 191, 223); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(74, 122, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = Color.FromArgb(74, 122, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = Color.FromArgb(185, 208, 241); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = Color.FromArgb(221, 236, 254); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = Color.FromArgb(150, 145, 133); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = Color.FromArgb(101, 143, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = Color.FromArgb(196, 219, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(0, 45, 134); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = Color.FromArgb(0, 45, 134); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = Color.FromArgb(221, 236, 254); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = Color.FromArgb(0, 61, 178); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = Color.FromArgb(170, 0, 170); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(59, 97, 156); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = Color.FromArgb(59, 97, 156); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(158, 190, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = Color.FromArgb(158, 190, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(196, 218, 250); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = Color.FromArgb(196, 218, 250); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = Color.FromArgb(227, 239, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = Color.FromArgb(144, 153, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = Color.FromArgb(216, 231, 252); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = Color.FromArgb(158, 190, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = Color.FromArgb(75, 120, 202); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = Color.FromArgb(186, 211, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = Color.FromArgb(144, 153, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = Color.FromArgb(242, 240, 228); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = Color.FromArgb(0, 53, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = Color.FromArgb(89, 135, 214); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = Color.FromArgb(234, 233, 225); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = Color.FromArgb(253, 238, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = Color.FromArgb(190, 218, 251); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = Color.FromArgb(55, 104, 185); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = Color.FromArgb(253, 247, 233); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = Color.FromArgb(144, 153, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = Color.FromArgb(144, 153, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = Color.FromArgb(187, 85, 3); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = Color.FromArgb(251, 200, 79); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = Color.FromArgb(215, 228, 251); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(203, 225, 252); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = Color.FromArgb(203, 225, 252); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = Color.FromArgb(0, 45, 150); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = Color.FromArgb(255, 255, 255); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = Color.FromArgb(247, 190, 87); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = Color.FromArgb(255, 255, 220); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = Color.FromArgb(50, 69, 105); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = Color.FromArgb(248, 222, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = Color.FromArgb(232, 127, 8); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = Color.FromArgb(238, 147, 17); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = Color.FromArgb(251, 230, 148); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = Color.FromArgb(0, 53, 145); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(89, 135, 214); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = Color.FromArgb(89, 135, 214); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = Color.FromArgb(236, 233, 216); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = Color.FromArgb(195, 218, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = Color.FromArgb(59, 97, 156); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = Color.FromArgb(158, 190, 245); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = Color.FromArgb(61, 108, 192); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = Color.FromArgb(61, 108, 192); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = Color.FromArgb(61, 108, 192); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = Color.FromArgb(144, 153, 174); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = Color.FromArgb(189, 194, 207); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = Color.FromArgb(211, 211, 211); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = Color.FromArgb(251, 251, 248); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = Color.FromArgb(220, 236, 254); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = Color.FromArgb(167, 197, 238); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = Color.FromArgb(185, 212, 249); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = Color.FromArgb(196, 218, 250); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = Color.FromArgb(42, 102, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = Color.FromArgb(221, 236, 254); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = Color.FromArgb(127, 157, 185); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = Color.FromArgb(128, 128, 128); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = Color.FromArgb(169, 199, 240); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = Color.FromArgb(222, 222, 222); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = Color.FromArgb(255, 255, 204); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = Color.FromArgb(74, 122, 201); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = Color.FromArgb(172, 168, 153); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = Color.FromArgb(123, 164, 224); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = Color.FromArgb(148, 187, 239); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = Color.FromArgb(0, 0, 0); + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = Color.FromArgb(158, 190, 245); + } + + + + internal enum KnownColors { + msocbvcrCBBdrOuterDocked, + msocbvcrCBBdrOuterFloating, + msocbvcrCBBkgd, + msocbvcrCBCtlBdrMouseDown, + msocbvcrCBCtlBdrMouseOver, + msocbvcrCBCtlBdrSelected, + msocbvcrCBCtlBdrSelectedMouseOver, + msocbvcrCBCtlBkgd, + msocbvcrCBCtlBkgdLight, + msocbvcrCBCtlBkgdMouseDown, + msocbvcrCBCtlBkgdMouseOver, + msocbvcrCBCtlBkgdSelected, + msocbvcrCBCtlBkgdSelectedMouseOver, + msocbvcrCBCtlText, + msocbvcrCBCtlTextDisabled, + msocbvcrCBCtlTextLight, + msocbvcrCBCtlTextMouseDown, + msocbvcrCBCtlTextMouseOver, + msocbvcrCBDockSeparatorLine, + msocbvcrCBDragHandle, + msocbvcrCBDragHandleShadow, + msocbvcrCBDropDownArrow, + msocbvcrCBGradMainMenuHorzBegin, + msocbvcrCBGradMainMenuHorzEnd, + msocbvcrCBGradMenuIconBkgdDroppedBegin, + msocbvcrCBGradMenuIconBkgdDroppedEnd, + msocbvcrCBGradMenuIconBkgdDroppedMiddle, + msocbvcrCBGradMenuTitleBkgdBegin, + msocbvcrCBGradMenuTitleBkgdEnd, + msocbvcrCBGradMouseDownBegin, + msocbvcrCBGradMouseDownEnd, + msocbvcrCBGradMouseDownMiddle, + msocbvcrCBGradMouseOverBegin, + msocbvcrCBGradMouseOverEnd, + msocbvcrCBGradMouseOverMiddle, + msocbvcrCBGradOptionsBegin, + msocbvcrCBGradOptionsEnd, + msocbvcrCBGradOptionsMiddle, + msocbvcrCBGradOptionsMouseOverBegin, + msocbvcrCBGradOptionsMouseOverEnd, + msocbvcrCBGradOptionsMouseOverMiddle, + msocbvcrCBGradOptionsSelectedBegin, + msocbvcrCBGradOptionsSelectedEnd, + msocbvcrCBGradOptionsSelectedMiddle, + msocbvcrCBGradSelectedBegin, + msocbvcrCBGradSelectedEnd, + msocbvcrCBGradSelectedMiddle, + msocbvcrCBGradVertBegin, + msocbvcrCBGradVertEnd, + msocbvcrCBGradVertMiddle, + msocbvcrCBIconDisabledDark, + msocbvcrCBIconDisabledLight, + msocbvcrCBLabelBkgnd, + msocbvcrCBLowColorIconDisabled, + msocbvcrCBMainMenuBkgd, + msocbvcrCBMenuBdrOuter, + msocbvcrCBMenuBkgd, + msocbvcrCBMenuCtlText, + msocbvcrCBMenuCtlTextDisabled, + msocbvcrCBMenuIconBkgd, + msocbvcrCBMenuIconBkgdDropped, + msocbvcrCBMenuShadow, + msocbvcrCBMenuSplitArrow, + msocbvcrCBOptionsButtonShadow, + msocbvcrCBShadow, + msocbvcrCBSplitterLine, + msocbvcrCBSplitterLineLight, + msocbvcrCBTearOffHandle, + msocbvcrCBTearOffHandleMouseOver, + msocbvcrCBTitleBkgd, + msocbvcrCBTitleText, + msocbvcrDisabledFocuslessHighlightedText, + msocbvcrDisabledHighlightedText, + msocbvcrDlgGroupBoxText, + msocbvcrDocTabBdr, + msocbvcrDocTabBdrDark, + msocbvcrDocTabBdrDarkMouseDown, + msocbvcrDocTabBdrDarkMouseOver, + msocbvcrDocTabBdrLight, + msocbvcrDocTabBdrLightMouseDown, + msocbvcrDocTabBdrLightMouseOver, + msocbvcrDocTabBdrMouseDown, + msocbvcrDocTabBdrMouseOver, + msocbvcrDocTabBdrSelected, + msocbvcrDocTabBkgd, + msocbvcrDocTabBkgdMouseDown, + msocbvcrDocTabBkgdMouseOver, + msocbvcrDocTabBkgdSelected, + msocbvcrDocTabText, + msocbvcrDocTabTextMouseDown, + msocbvcrDocTabTextMouseOver, + msocbvcrDocTabTextSelected, + msocbvcrDWActiveTabBkgd, + msocbvcrDWActiveTabText, + msocbvcrDWActiveTabTextDisabled, + msocbvcrDWInactiveTabBkgd, + msocbvcrDWInactiveTabText, + msocbvcrDWTabBkgdMouseDown, + msocbvcrDWTabBkgdMouseOver, + msocbvcrDWTabTextMouseDown, + msocbvcrDWTabTextMouseOver, + msocbvcrFocuslessHighlightedBkgd, + msocbvcrFocuslessHighlightedText, + msocbvcrGDHeaderBdr, + msocbvcrGDHeaderBkgd, + msocbvcrGDHeaderCellBdr, + msocbvcrGDHeaderCellBkgd, + msocbvcrGDHeaderCellBkgdSelected, + msocbvcrGDHeaderSeeThroughSelection, + msocbvcrGSPDarkBkgd, + msocbvcrGSPGroupContentDarkBkgd, + msocbvcrGSPGroupContentLightBkgd, + msocbvcrGSPGroupContentText, + msocbvcrGSPGroupContentTextDisabled, + msocbvcrGSPGroupHeaderDarkBkgd, + msocbvcrGSPGroupHeaderLightBkgd, + msocbvcrGSPGroupHeaderText, + msocbvcrGSPGroupline, + msocbvcrGSPHyperlink, + msocbvcrGSPLightBkgd, + msocbvcrHyperlink, + msocbvcrHyperlinkFollowed, + msocbvcrJotNavUIBdr, + msocbvcrJotNavUIGradBegin, + msocbvcrJotNavUIGradEnd, + msocbvcrJotNavUIGradMiddle, + msocbvcrJotNavUIText, + msocbvcrListHeaderArrow, + msocbvcrNetLookBkgnd, + msocbvcrOABBkgd, + msocbvcrOBBkgdBdr, + msocbvcrOBBkgdBdrContrast, + msocbvcrOGMDIParentWorkspaceBkgd, + msocbvcrOGRulerActiveBkgd, + msocbvcrOGRulerBdr, + msocbvcrOGRulerBkgd, + msocbvcrOGRulerInactiveBkgd, + msocbvcrOGRulerTabBoxBdr, + msocbvcrOGRulerTabBoxBdrHighlight, + msocbvcrOGRulerTabStopTicks, + msocbvcrOGRulerText, + msocbvcrOGTaskPaneGroupBoxHeaderBkgd, + msocbvcrOGWorkspaceBkgd, + msocbvcrOLKFlagNone, + msocbvcrOLKFolderbarDark, + msocbvcrOLKFolderbarLight, + msocbvcrOLKFolderbarText, + msocbvcrOLKGridlines, + msocbvcrOLKGroupLine, + msocbvcrOLKGroupNested, + msocbvcrOLKGroupShaded, + msocbvcrOLKGroupText, + msocbvcrOLKIconBar, + msocbvcrOLKInfoBarBkgd, + msocbvcrOLKInfoBarText, + msocbvcrOLKPreviewPaneLabelText, + msocbvcrOLKTodayIndicatorDark, + msocbvcrOLKTodayIndicatorLight, + msocbvcrOLKWBActionDividerLine, + msocbvcrOLKWBButtonDark, + msocbvcrOLKWBButtonLight, + msocbvcrOLKWBDarkOutline, + msocbvcrOLKWBFoldersBackground, + msocbvcrOLKWBHoverButtonDark, + msocbvcrOLKWBHoverButtonLight, + msocbvcrOLKWBLabelText, + msocbvcrOLKWBPressedButtonDark, + msocbvcrOLKWBPressedButtonLight, + msocbvcrOLKWBSelectedButtonDark, + msocbvcrOLKWBSelectedButtonLight, + msocbvcrOLKWBSplitterDark, + msocbvcrOLKWBSplitterLight, + msocbvcrPlacesBarBkgd, + msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd, + msocbvcrPPOutlineThumbnailsPaneTabBdr, + msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd, + msocbvcrPPOutlineThumbnailsPaneTabText, + msocbvcrPPSlideBdrActiveSelected, + msocbvcrPPSlideBdrActiveSelectedMouseOver, + msocbvcrPPSlideBdrInactiveSelected, + msocbvcrPPSlideBdrMouseOver, + msocbvcrPubPrintDocScratchPageBkgd, + msocbvcrPubWebDocScratchPageBkgd, + msocbvcrSBBdr, + msocbvcrScrollbarBkgd, + msocbvcrToastGradBegin, + msocbvcrToastGradEnd, + msocbvcrWPBdrInnerDocked, + msocbvcrWPBdrOuterDocked, + msocbvcrWPBdrOuterFloating, + msocbvcrWPBkgd, + msocbvcrWPCtlBdr, + msocbvcrWPCtlBdrDefault, + msocbvcrWPCtlBdrDisabled, + msocbvcrWPCtlBkgd, + msocbvcrWPCtlBkgdDisabled, + msocbvcrWPCtlText, + msocbvcrWPCtlTextDisabled, + msocbvcrWPCtlTextMouseDown, + msocbvcrWPGroupline, + msocbvcrWPInfoTipBkgd, + msocbvcrWPInfoTipText, + msocbvcrWPNavBarBkgnd, + msocbvcrWPText, + msocbvcrWPTextDisabled, + msocbvcrWPTitleBkgdActive, + msocbvcrWPTitleBkgdInactive, + msocbvcrWPTitleTextActive, + msocbvcrWPTitleTextInactive, + msocbvcrXLFormulaBarBkgd, + ButtonSelectedHighlight, // not actually from MSO tables + ButtonPressedHighlight,// not actually from MSO tables + ButtonCheckedHighlight,// not actually from MSO tables + lastKnownColor = ButtonCheckedHighlight + } + + } + + +} + diff --git a/WindowsForms/Managed/System/WinForms/ProfessionalColors.cs b/WindowsForms/Managed/System/WinForms/ProfessionalColors.cs new file mode 100644 index 000000000..5fff816d1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProfessionalColors.cs @@ -0,0 +1,380 @@ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + using System.Drawing; + using System.Collections; + using System.Diagnostics; + + public sealed class ProfessionalColors { + [ThreadStatic] + private static ProfessionalColorTable professionalColorTable = null; + + [ThreadStatic] + private static string colorScheme = null; + + [ThreadStatic] + private static object colorFreshnessKey = null; + + + internal static ProfessionalColorTable ColorTable { + get { + if (professionalColorTable == null) { + professionalColorTable = new ProfessionalColorTable(); + } + return professionalColorTable; + } + } + + static ProfessionalColors() { + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + SetScheme(); + } + + private ProfessionalColors() { + } + + internal static string ColorScheme { + get { return colorScheme; } + } + + // internal object used between professional color tables + // to identify when a userpreferencechanged has occurred + internal static object ColorFreshnessKey { + get { return colorFreshnessKey; } + } + +#region Colors + + [SRDescription(SR.ProfessionalColorsButtonSelectedHighlightDescr)] + public static Color ButtonSelectedHighlight { + get { return ColorTable.ButtonSelectedHighlight; } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedHighlightBorderDescr)] + public static Color ButtonSelectedHighlightBorder { + get { return ColorTable.ButtonSelectedHighlightBorder; } + } + + + [SRDescription(SR.ProfessionalColorsButtonPressedHighlightDescr)] + public static Color ButtonPressedHighlight { + get { return ColorTable.ButtonPressedHighlight; } + } + + + [SRDescription(SR.ProfessionalColorsButtonPressedHighlightBorderDescr)] + public static Color ButtonPressedHighlightBorder { + get { return ColorTable.ButtonPressedHighlightBorder; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedHighlightDescr)] + public static Color ButtonCheckedHighlight { + get { return ColorTable.ButtonCheckedHighlight; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedHighlightBorderDescr)] + public static Color ButtonCheckedHighlightBorder { + get { return ColorTable.ButtonCheckedHighlightBorder; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedBorderDescr)] + public static Color ButtonPressedBorder { + get { return ColorTable.ButtonPressedBorder; } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedBorderDescr)] + public static Color ButtonSelectedBorder { + get { return ColorTable.ButtonSelectedBorder; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientBeginDescr)] + public static Color ButtonCheckedGradientBegin { + get { return ColorTable.ButtonCheckedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientMiddleDescr)] + public static Color ButtonCheckedGradientMiddle { + get { return ColorTable.ButtonCheckedGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsButtonCheckedGradientEndDescr)] + public static Color ButtonCheckedGradientEnd { + get { return ColorTable.ButtonCheckedGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientBeginDescr)] + public static Color ButtonSelectedGradientBegin { + get { return ColorTable.ButtonSelectedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientMiddleDescr)] + public static Color ButtonSelectedGradientMiddle { + get { return ColorTable.ButtonSelectedGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsButtonSelectedGradientEndDescr)] + public static Color ButtonSelectedGradientEnd { + get { return ColorTable.ButtonSelectedGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientBeginDescr)] + public static Color ButtonPressedGradientBegin { + get { return ColorTable.ButtonPressedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientMiddleDescr)] + public static Color ButtonPressedGradientMiddle { + get { return ColorTable.ButtonPressedGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsButtonPressedGradientEndDescr)] + public static Color ButtonPressedGradientEnd { + get { return ColorTable.ButtonPressedGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsCheckBackgroundDescr)] + public static Color CheckBackground { + get { return ColorTable.CheckBackground; } + } + + [SRDescription(SR.ProfessionalColorsCheckSelectedBackgroundDescr)] + public static Color CheckSelectedBackground { + get { return ColorTable.CheckSelectedBackground; } + } + + [SRDescription(SR.ProfessionalColorsCheckPressedBackgroundDescr)] + public static Color CheckPressedBackground { + get { return ColorTable.CheckPressedBackground; } + } + + [SRDescription(SR.ProfessionalColorsGripDarkDescr)] + public static Color GripDark { + get { return ColorTable.GripDark; } + } + + [SRDescription(SR.ProfessionalColorsGripLightDescr)] + public static Color GripLight { + get { return ColorTable.GripLight; } + } + + + + + + + + + + + + + + + + + + + + + + + + [SRDescription(SR.ProfessionalColorsImageMarginGradientBeginDescr)] + public static Color ImageMarginGradientBegin { + get { return ColorTable.ImageMarginGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsImageMarginGradientMiddleDescr)] + public static Color ImageMarginGradientMiddle { + get { return ColorTable.ImageMarginGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsImageMarginGradientEndDescr)] + public static Color ImageMarginGradientEnd { + get { return ColorTable.ImageMarginGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientBeginDescr)] + public static Color ImageMarginRevealedGradientBegin { + get { return ColorTable.ImageMarginRevealedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientMiddleDescr)] + public static Color ImageMarginRevealedGradientMiddle { + get { return ColorTable.ImageMarginRevealedGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsImageMarginRevealedGradientEndDescr)] + public static Color ImageMarginRevealedGradientEnd { + get { return ColorTable.ImageMarginRevealedGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsMenuStripGradientBeginDescr)] + public static Color MenuStripGradientBegin { + get { return ColorTable.MenuStripGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsMenuStripGradientEndDescr)] + public static Color MenuStripGradientEnd{ + get { return ColorTable.MenuStripGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsMenuBorderDescr)] + public static Color MenuBorder { + get { return ColorTable.MenuBorder; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedDescr)] + public static Color MenuItemSelected { + get { return ColorTable.MenuItemSelected; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemBorderDescr)] + public static Color MenuItemBorder { + get { return ColorTable.MenuItemBorder; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedGradientBeginDescr)] + public static Color MenuItemSelectedGradientBegin { + get { return ColorTable.MenuItemSelectedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemSelectedGradientEndDescr)] + public static Color MenuItemSelectedGradientEnd { + get { return ColorTable.MenuItemSelectedGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientBeginDescr)] + public static Color MenuItemPressedGradientBegin { + get { return ColorTable.MenuItemPressedGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientMiddleDescr)] + public static Color MenuItemPressedGradientMiddle { + get { return ColorTable.MenuItemPressedGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsMenuItemPressedGradientEndDescr)] + public static Color MenuItemPressedGradientEnd { + get { return ColorTable.MenuItemPressedGradientEnd; } + } + + + [SRDescription(SR.ProfessionalColorsRaftingContainerGradientBeginDescr)] + public static Color RaftingContainerGradientBegin { + get { return ColorTable.RaftingContainerGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsRaftingContainerGradientEndDescr)] + public static Color RaftingContainerGradientEnd { + get { return ColorTable.RaftingContainerGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsSeparatorDarkDescr)] + public static Color SeparatorDark { + get { return ColorTable.SeparatorDark; } + } + + [SRDescription(SR.ProfessionalColorsSeparatorLightDescr)] + public static Color SeparatorLight { + get { return ColorTable.SeparatorLight; } + } + [SRDescription(SR.ProfessionalColorsStatusStripGradientBeginDescr)] + public static Color StatusStripGradientBegin { + get { return ColorTable.StatusStripGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsStatusStripGradientEndDescr)] + public static Color StatusStripGradientEnd { + get { return ColorTable.StatusStripGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsToolStripBorderDescr)] + public static Color ToolStripBorder { + get { return ColorTable.ToolStripBorder; } + } + + [SRDescription(SR.ProfessionalColorsToolStripDropDownBackgroundDescr)] + public static Color ToolStripDropDownBackground { + get { return ColorTable.ToolStripDropDownBackground; } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientBeginDescr)] + public static Color ToolStripGradientBegin { + get { return ColorTable.ToolStripGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientMiddleDescr)] + public static Color ToolStripGradientMiddle { + get { return ColorTable.ToolStripGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsToolStripGradientEndDescr)] + public static Color ToolStripGradientEnd { + get { return ColorTable.ToolStripGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsToolStripContentPanelGradientBeginDescr)] + public static Color ToolStripContentPanelGradientBegin { + get { return ColorTable.ToolStripContentPanelGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsToolStripContentPanelGradientEndDescr)] + public static Color ToolStripContentPanelGradientEnd { + get { return ColorTable.ToolStripContentPanelGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsToolStripPanelGradientBeginDescr)] + public static Color ToolStripPanelGradientBegin { + get { return ColorTable.ToolStripPanelGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsToolStripPanelGradientEndDescr)] + public static Color ToolStripPanelGradientEnd { + get { return ColorTable.ToolStripPanelGradientEnd; } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientBeginDescr)] + public static Color OverflowButtonGradientBegin { + get { return ColorTable.OverflowButtonGradientBegin; } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientMiddleDescr)] + public static Color OverflowButtonGradientMiddle { + get { return ColorTable.OverflowButtonGradientMiddle; } + } + + [SRDescription(SR.ProfessionalColorsOverflowButtonGradientEndDescr)] + public static Color OverflowButtonGradientEnd { + get { return ColorTable.OverflowButtonGradientEnd; } + } +#endregion Colors + + /* public static Color ControlLight { + get { return FromKnownColor(KnownColors.msocbvcrCBCtlBkgdLight); } + } */ + + + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { + SetScheme(); + if (e.Category == UserPreferenceCategory.Color) { + colorFreshnessKey = new object(); + } + } + + private static void SetScheme() { + if (VisualStyleRenderer.IsSupported) { + colorScheme = VisualStyleInformation.ColorScheme; + } + else { + colorScheme = null; + } + } + + } + + +} + diff --git a/WindowsForms/Managed/System/WinForms/ProgressBar.cs b/WindowsForms/Managed/System/WinForms/ProgressBar.cs new file mode 100644 index 000000000..ebab0228f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProgressBar.cs @@ -0,0 +1,1010 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security.Permissions; + using System.Drawing; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Runtime.InteropServices; + using System.Windows.Forms.Layout; + using System.Globalization; + using static UnsafeNativeMethods; + + /// + /// + /// + /// Represents a Windows progress bar control. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultBindingProperty("Value"), + SRDescription(SR.DescriptionProgressBar) + ] + public class ProgressBar : Control { + + + //# VS7 205: simcooke + //REMOVED: AddOnValueChanged, RemoveOnValueChanged, OnValueChanged and all designer plumbing associated with it. + // OnValueChanged event no longer exists. + + // these four values define the range of possible values, how to navigate + // through them, and the current position + // + private int minimum = 0; + private int maximum = 100; + private int step = 10; + private int value = 0; + + //this defines marquee animation speed + private int marqueeSpeed = 100; + + private Color defaultForeColor = SystemColors.Highlight; + + private ProgressBarStyle style = ProgressBarStyle.Blocks; + + private EventHandler onRightToLeftLayoutChanged; + private bool rightToLeftLayout = false; + + + /// + /// + /// + /// Initializes a new instance of the class in its default + /// state. + /// + /// + public ProgressBar() + : base() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.UseTextForAccessibility | + ControlStyles.Selectable, false); + ForeColor = defaultForeColor; + } + + /// + /// + /// + /// + /// This is called when creating a window. Inheriting classes can ovveride + /// this to add extra functionality, but should not forget to first call + /// base.getCreateParams() to make sure the control continues to work + /// correctly. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_PROGRESS; + if (this.Style == ProgressBarStyle.Continuous) { + cp.Style |= NativeMethods.PBS_SMOOTH; + } + else if (this.Style == ProgressBarStyle.Marquee && !DesignMode) { + cp.Style |= NativeMethods.PBS_MARQUEE; + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + + /// + /// + /// + /// Gets or sets the style of the ProgressBar. This is can be either Blocks or Continuous. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(ProgressBarStyle.Blocks), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStyleDescr) + ] + public ProgressBarStyle Style { + get { + return style; + } + set { + if (style != value) { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ProgressBarStyle.Blocks, (int)ProgressBarStyle.Marquee)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ProgressBarStyle)); + } + style = value; + if (IsHandleCreated) + RecreateHandle(); + if (style == ProgressBarStyle.Marquee) + { + StartMarquee(); + } + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool CausesValidation { + get { + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, 23); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// + /// Gets or sets the font of text in the . + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// Gets or sets the marquee animation speed of the . + /// Sets the value to a positive number causes the progressBar to move, while setting it to 0 + /// stops the progressBar. + /// + /// + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarMarqueeAnimationSpeed)] + public int MarqueeAnimationSpeed { + get { + return marqueeSpeed; + } + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("MarqueeAnimationSpeed must be non-negative"); + } + marqueeSpeed = value; + if (!DesignMode) { + StartMarquee(); + } + } + } + + /// + /// + /// + /// Start the Marquee rolling (or stop it, if the speed = 0) + /// + /// + private void StartMarquee() + { + if (IsHandleCreated && style == ProgressBarStyle.Marquee) + { + if (marqueeSpeed == 0) + { + SendMessage(NativeMethods.PBM_SETMARQUEE, 0, marqueeSpeed); + } + else + { + SendMessage(NativeMethods.PBM_SETMARQUEE, 1, marqueeSpeed); + } + } + } + + /// + /// + /// + /// Gets or sets the maximum value of the . + /// Gets or sets the maximum value of the . + /// + /// + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMaximumDescr) + ] + public int Maximum { + get { + return maximum; + } + set { + if (maximum != value) { + // Ensure that value is in the Win32 control's acceptable range + // Message: '%1' is not a valid value for '%0'. '%0' must be greater than %2. + // Should this set a boundary for the top end too? + if (value < 0) + throw new ArgumentOutOfRangeException("Maximum", SR.GetString(SR.InvalidLowBoundArgumentEx, "Maximum", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + + if (minimum > value) minimum = value; + + maximum = value; + + if (this.value > maximum) this.value = maximum; + + if (IsHandleCreated) { + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + UpdatePos() ; + } + } + } + } + + /// + /// + /// + /// Gets or sets the minimum value of the . + /// + /// + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMinimumDescr) + ] + public int Minimum { + get { + return minimum; + } + set { + if (minimum != value) { + // Ensure that value is in the Win32 control's acceptable range + // Message: '%1' is not a valid value for '%0'. '%0' must be greater than %2. + // Should this set a boundary for the top end too? + if (value < 0) + throw new ArgumentOutOfRangeException("Minimum", SR.GetString(SR.InvalidLowBoundArgumentEx, "Minimum", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + if (maximum < value) maximum = value; + + minimum = value; + + if (this.value < minimum) this.value = minimum; + + if (IsHandleCreated) { + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + UpdatePos() ; + } + } + } + } + + protected override void OnBackColorChanged(EventArgs e) + { + base.OnBackColorChanged(e); + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + } + + protected override void OnForeColorChanged(EventArgs e) + { + base.OnForeColorChanged(e); + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + onRightToLeftLayoutChanged += value; + } + remove { + onRightToLeftLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the amount that a call to + /// increases the progress bar's current position. + /// + /// + [ + DefaultValue(10), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStepDescr) + ] + public int Step { + get { + return step; + } + set { + step = value; + if (IsHandleCreated) SendMessage(NativeMethods.PBM_SETSTEP, step, 0); + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the current position of the . + /// + /// + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + Bindable(true), + SRDescription(SR.ProgressBarValueDescr) + ] + public int Value { + get { + return value; + } + set { + if (this.value != value) { + if ((value < minimum) || (value > maximum)) + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", value.ToString(CultureInfo.CurrentCulture), "'minimum'", "'maximum'")); + this.value = value; + UpdatePos() ; + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Enter { + add { + base.Enter += value; + } + remove { + base.Enter -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Leave { + add { + base.Leave += value; + } + remove { + base.Leave -= value; + } + } + + /// + /// + /// ProgressBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_PROGRESS_CLASS; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// + /// Advances the current position of the by the + /// specified increment and redraws the control to reflect the new position. + /// + /// + public void Increment(int value) { + if (this.Style == ProgressBarStyle.Marquee) { + throw new InvalidOperationException(SR.GetString(SR.ProgressBarIncrementMarqueeException)); + } + this.value += value; + + // Enforce that value is within the range (minimum, maximum) + if (this.value < minimum) { + this.value = minimum; + } + if (this.value > maximum) { + this.value = maximum; + } + + UpdatePos(); + } + + /// + /// + /// Overridden to set up our properties. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + SendMessage(NativeMethods.PBM_SETSTEP, step, 0); + SendMessage(NativeMethods.PBM_SETPOS, value, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + StartMarquee(); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + } + + /// + /// + /// Overridden to remove event handler. + /// + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + if (onRightToLeftLayoutChanged != null) { + onRightToLeftLayoutChanged(this, e); + } + } + + + + /// + /// + /// + /// Advances the current position of the + /// by the amount of the + /// property, and redraws the control to reflect the new position. + /// + /// + public void PerformStep() { + if (this.Style == ProgressBarStyle.Marquee) { + throw new InvalidOperationException(SR.GetString(SR.ProgressBarPerformStepMarqueeException)); + } + Increment(step); + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetForeColor() { + ForeColor = defaultForeColor; + } + + + /// + /// + /// Returns true if the ForeColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeForeColor() { + return ForeColor != defaultForeColor; + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Minimum: " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum: " + Maximum.ToString(CultureInfo.CurrentCulture) + ", Value: " + Value.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// Sends the underlying window a PBM_SETPOS message to update + /// the current value of the progressbar. + /// + /// + private void UpdatePos() { + if (IsHandleCreated) SendMessage(NativeMethods.PBM_SETPOS, value, 0); + } + + //Note: ProgressBar doesn't work like other controls as far as setting ForeColor/ + //BackColor -- you need to send messages to update the colors + private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e) + { + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + } + + /// + /// Creates a new AccessibleObject for this ProgressBar instance. + /// The AccessibleObject instance returned by this method supports ControlType UIA property. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.8 or opt-in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this ProgressBar instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ProgressBarAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + [ComVisible(true)] + internal class ProgressBarAccessibleObject : ControlAccessibleObject { + + internal ProgressBarAccessibleObject(ProgressBar owner) : base(owner) { + } + + private ProgressBar OwningProgressBar + { + get + { + return Owner as ProgressBar; + } + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ValuePatternId || + patternId == NativeMethods.UIA_RangeValuePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ProgressBarControlTypeId; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + // This is necessary for compatibility with MSAA proxy: + // IsKeyboardFocusable = true regardless the control is enabled/disabled. + return true; + case NativeMethods.UIA_IsRangeValuePatternAvailablePropertyId: + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + case NativeMethods.UIA_RangeValueIsReadOnlyPropertyId: + return true; + case NativeMethods.UIA_RangeValueLargeChangePropertyId: + case NativeMethods.UIA_RangeValueSmallChangePropertyId: + return double.NaN; + } + + return base.GetPropertyValue(propertyID); + } + + internal override void SetValue(double newValue) { + throw new InvalidOperationException("Progress Bar is read-only."); + } + + internal override double LargeChange { + get { + return Double.NaN; + } + } + + internal override double Maximum { + get { + return this.OwningProgressBar?.Maximum ?? Double.NaN; + } + } + + internal override double Minimum { + get { + return this.OwningProgressBar?.Minimum ?? Double.NaN; + } + } + + internal override double SmallChange { + get { + return Double.NaN; + } + } + + internal override double RangeValue { + get { + return this.OwningProgressBar?.Value ?? Double.NaN; + } + } + + internal override bool IsReadOnly { + get { + return true; + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ProgressBar.cs.back b/WindowsForms/Managed/System/WinForms/ProgressBar.cs.back new file mode 100644 index 000000000..ebab0228f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProgressBar.cs.back @@ -0,0 +1,1010 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security.Permissions; + using System.Drawing; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Runtime.InteropServices; + using System.Windows.Forms.Layout; + using System.Globalization; + using static UnsafeNativeMethods; + + /// + /// + /// + /// Represents a Windows progress bar control. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultBindingProperty("Value"), + SRDescription(SR.DescriptionProgressBar) + ] + public class ProgressBar : Control { + + + //# VS7 205: simcooke + //REMOVED: AddOnValueChanged, RemoveOnValueChanged, OnValueChanged and all designer plumbing associated with it. + // OnValueChanged event no longer exists. + + // these four values define the range of possible values, how to navigate + // through them, and the current position + // + private int minimum = 0; + private int maximum = 100; + private int step = 10; + private int value = 0; + + //this defines marquee animation speed + private int marqueeSpeed = 100; + + private Color defaultForeColor = SystemColors.Highlight; + + private ProgressBarStyle style = ProgressBarStyle.Blocks; + + private EventHandler onRightToLeftLayoutChanged; + private bool rightToLeftLayout = false; + + + /// + /// + /// + /// Initializes a new instance of the class in its default + /// state. + /// + /// + public ProgressBar() + : base() { + SetStyle(ControlStyles.UserPaint | + ControlStyles.UseTextForAccessibility | + ControlStyles.Selectable, false); + ForeColor = defaultForeColor; + } + + /// + /// + /// + /// + /// This is called when creating a window. Inheriting classes can ovveride + /// this to add extra functionality, but should not forget to first call + /// base.getCreateParams() to make sure the control continues to work + /// correctly. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_PROGRESS; + if (this.Style == ProgressBarStyle.Continuous) { + cp.Style |= NativeMethods.PBS_SMOOTH; + } + else if (this.Style == ProgressBarStyle.Marquee && !DesignMode) { + cp.Style |= NativeMethods.PBS_MARQUEE; + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + + /// + /// + /// + /// Gets or sets the style of the ProgressBar. This is can be either Blocks or Continuous. + /// + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(ProgressBarStyle.Blocks), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStyleDescr) + ] + public ProgressBarStyle Style { + get { + return style; + } + set { + if (style != value) { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ProgressBarStyle.Blocks, (int)ProgressBarStyle.Marquee)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ProgressBarStyle)); + } + style = value; + if (IsHandleCreated) + RecreateHandle(); + if (style == ProgressBarStyle.Marquee) + { + StartMarquee(); + } + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool CausesValidation { + get { + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, 23); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// + /// Gets or sets the font of text in the . + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// Gets or sets the marquee animation speed of the . + /// Sets the value to a positive number causes the progressBar to move, while setting it to 0 + /// stops the progressBar. + /// + /// + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarMarqueeAnimationSpeed)] + public int MarqueeAnimationSpeed { + get { + return marqueeSpeed; + } + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("MarqueeAnimationSpeed must be non-negative"); + } + marqueeSpeed = value; + if (!DesignMode) { + StartMarquee(); + } + } + } + + /// + /// + /// + /// Start the Marquee rolling (or stop it, if the speed = 0) + /// + /// + private void StartMarquee() + { + if (IsHandleCreated && style == ProgressBarStyle.Marquee) + { + if (marqueeSpeed == 0) + { + SendMessage(NativeMethods.PBM_SETMARQUEE, 0, marqueeSpeed); + } + else + { + SendMessage(NativeMethods.PBM_SETMARQUEE, 1, marqueeSpeed); + } + } + } + + /// + /// + /// + /// Gets or sets the maximum value of the . + /// Gets or sets the maximum value of the . + /// + /// + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMaximumDescr) + ] + public int Maximum { + get { + return maximum; + } + set { + if (maximum != value) { + // Ensure that value is in the Win32 control's acceptable range + // Message: '%1' is not a valid value for '%0'. '%0' must be greater than %2. + // Should this set a boundary for the top end too? + if (value < 0) + throw new ArgumentOutOfRangeException("Maximum", SR.GetString(SR.InvalidLowBoundArgumentEx, "Maximum", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + + if (minimum > value) minimum = value; + + maximum = value; + + if (this.value > maximum) this.value = maximum; + + if (IsHandleCreated) { + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + UpdatePos() ; + } + } + } + } + + /// + /// + /// + /// Gets or sets the minimum value of the . + /// + /// + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMinimumDescr) + ] + public int Minimum { + get { + return minimum; + } + set { + if (minimum != value) { + // Ensure that value is in the Win32 control's acceptable range + // Message: '%1' is not a valid value for '%0'. '%0' must be greater than %2. + // Should this set a boundary for the top end too? + if (value < 0) + throw new ArgumentOutOfRangeException("Minimum", SR.GetString(SR.InvalidLowBoundArgumentEx, "Minimum", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + if (maximum < value) maximum = value; + + minimum = value; + + if (this.value < minimum) this.value = minimum; + + if (IsHandleCreated) { + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + UpdatePos() ; + } + } + } + } + + protected override void OnBackColorChanged(EventArgs e) + { + base.OnBackColorChanged(e); + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + } + + protected override void OnForeColorChanged(EventArgs e) + { + base.OnForeColorChanged(e); + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + onRightToLeftLayoutChanged += value; + } + remove { + onRightToLeftLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the amount that a call to + /// increases the progress bar's current position. + /// + /// + [ + DefaultValue(10), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStepDescr) + ] + public int Step { + get { + return step; + } + set { + step = value; + if (IsHandleCreated) SendMessage(NativeMethods.PBM_SETSTEP, step, 0); + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the current position of the . + /// + /// + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + Bindable(true), + SRDescription(SR.ProgressBarValueDescr) + ] + public int Value { + get { + return value; + } + set { + if (this.value != value) { + if ((value < minimum) || (value > maximum)) + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", value.ToString(CultureInfo.CurrentCulture), "'minimum'", "'maximum'")); + this.value = value; + UpdatePos() ; + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Enter { + add { + base.Enter += value; + } + remove { + base.Enter -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Leave { + add { + base.Leave += value; + } + remove { + base.Leave -= value; + } + } + + /// + /// + /// ProgressBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_PROGRESS_CLASS; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// + /// Advances the current position of the by the + /// specified increment and redraws the control to reflect the new position. + /// + /// + public void Increment(int value) { + if (this.Style == ProgressBarStyle.Marquee) { + throw new InvalidOperationException(SR.GetString(SR.ProgressBarIncrementMarqueeException)); + } + this.value += value; + + // Enforce that value is within the range (minimum, maximum) + if (this.value < minimum) { + this.value = minimum; + } + if (this.value > maximum) { + this.value = maximum; + } + + UpdatePos(); + } + + /// + /// + /// Overridden to set up our properties. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SendMessage(NativeMethods.PBM_SETRANGE32, minimum, maximum); + SendMessage(NativeMethods.PBM_SETSTEP, step, 0); + SendMessage(NativeMethods.PBM_SETPOS, value, 0); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + StartMarquee(); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + } + + /// + /// + /// Overridden to remove event handler. + /// + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + if (onRightToLeftLayoutChanged != null) { + onRightToLeftLayoutChanged(this, e); + } + } + + + + /// + /// + /// + /// Advances the current position of the + /// by the amount of the + /// property, and redraws the control to reflect the new position. + /// + /// + public void PerformStep() { + if (this.Style == ProgressBarStyle.Marquee) { + throw new InvalidOperationException(SR.GetString(SR.ProgressBarPerformStepMarqueeException)); + } + Increment(step); + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetForeColor() { + ForeColor = defaultForeColor; + } + + + /// + /// + /// Returns true if the ForeColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeForeColor() { + return ForeColor != defaultForeColor; + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Minimum: " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum: " + Maximum.ToString(CultureInfo.CurrentCulture) + ", Value: " + Value.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// Sends the underlying window a PBM_SETPOS message to update + /// the current value of the progressbar. + /// + /// + private void UpdatePos() { + if (IsHandleCreated) SendMessage(NativeMethods.PBM_SETPOS, value, 0); + } + + //Note: ProgressBar doesn't work like other controls as far as setting ForeColor/ + //BackColor -- you need to send messages to update the colors + private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e) + { + if (IsHandleCreated) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + } + + /// + /// Creates a new AccessibleObject for this ProgressBar instance. + /// The AccessibleObject instance returned by this method supports ControlType UIA property. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.8 or opt-in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this ProgressBar instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ProgressBarAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + [ComVisible(true)] + internal class ProgressBarAccessibleObject : ControlAccessibleObject { + + internal ProgressBarAccessibleObject(ProgressBar owner) : base(owner) { + } + + private ProgressBar OwningProgressBar + { + get + { + return Owner as ProgressBar; + } + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3) { + return true; + } + + return base.IsIAccessibleExSupported(); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ValuePatternId || + patternId == NativeMethods.UIA_RangeValuePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_NamePropertyId: + return this.Name; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ProgressBarControlTypeId; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + // This is necessary for compatibility with MSAA proxy: + // IsKeyboardFocusable = true regardless the control is enabled/disabled. + return true; + case NativeMethods.UIA_IsRangeValuePatternAvailablePropertyId: + case NativeMethods.UIA_IsValuePatternAvailablePropertyId: + case NativeMethods.UIA_RangeValueIsReadOnlyPropertyId: + return true; + case NativeMethods.UIA_RangeValueLargeChangePropertyId: + case NativeMethods.UIA_RangeValueSmallChangePropertyId: + return double.NaN; + } + + return base.GetPropertyValue(propertyID); + } + + internal override void SetValue(double newValue) { + throw new InvalidOperationException("Progress Bar is read-only."); + } + + internal override double LargeChange { + get { + return Double.NaN; + } + } + + internal override double Maximum { + get { + return this.OwningProgressBar?.Maximum ?? Double.NaN; + } + } + + internal override double Minimum { + get { + return this.OwningProgressBar?.Minimum ?? Double.NaN; + } + } + + internal override double SmallChange { + get { + return Double.NaN; + } + } + + internal override double RangeValue { + get { + return this.OwningProgressBar?.Value ?? Double.NaN; + } + } + + internal override bool IsReadOnly { + get { + return true; + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ProgressBarRenderer.cs b/WindowsForms/Managed/System/WinForms/ProgressBarRenderer.cs new file mode 100644 index 000000000..c4e179f57 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProgressBarRenderer.cs @@ -0,0 +1,140 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the ProgressBar control. + /// + /// + public sealed class ProgressBarRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + + //cannot instantiate + private ProgressBarRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + /// + /// + /// + /// Renders a horizontal bar. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawHorizontalBar(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.ProgressBar.Bar.Normal); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical bar. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawVerticalBar(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.ProgressBar.BarVertical.Normal); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a number of constant size horizontal chunks in the given bounds. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawHorizontalChunks(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.ProgressBar.Chunk.Normal); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a number of constant size vertical chunks in the given bounds. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawVerticalChunks(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.ProgressBar.ChunkVertical.Normal); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Returns the width/height of a single horizontal/vertical progress bar chunk. + /// + /// + public static int ChunkThickness { + get { + InitializeRenderer(VisualStyleElement.ProgressBar.Chunk.Normal); + + return (visualStyleRenderer.GetInteger(IntegerProperty.ProgressChunkSize)); + } + } + + /// + /// + /// + /// Returns the width/height of the space between horizontal/vertical progress bar chunks. + /// + /// + public static int ChunkSpaceThickness { + get { + InitializeRenderer(VisualStyleElement.ProgressBar.Chunk.Normal); + + return (visualStyleRenderer.GetInteger(IntegerProperty.ProgressSpaceSize)); + } + } + + private static void InitializeRenderer(VisualStyleElement element) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(element); + } + else { + visualStyleRenderer.SetParameters(element); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ProgressBarStyle.cs b/WindowsForms/Managed/System/WinForms/ProgressBarStyle.cs new file mode 100644 index 000000000..8034dc202 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ProgressBarStyle.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// This Enumeration represents the styles the ProgressBar can take. + /// Blocks and Continuous. + /// + /// + public enum ProgressBarStyle + { + /// + /// + /// + /// The progress bar displays the progress status as a segmented bar. + /// + /// + Blocks, + + /// + /// + /// + /// The progress bar displays the progress status in a smooth scrolling bar. + /// + /// + Continuous, + + /// + /// + /// + /// The progress bar displays the progress status in the marquee style. + /// + /// + Marquee + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGrid.cs b/WindowsForms/Managed/System/WinForms/PropertyGrid.cs new file mode 100644 index 000000000..9e38f6f70 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGrid.cs @@ -0,0 +1,5693 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.IO; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Windows.Forms.ComponentModel.Com2Interop; + using System.Windows.Forms.Design; + using System.Windows.Forms.PropertyGridInternal; + using Microsoft.Win32; + + /// + /// + /// [To be supplied.] + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [Designer("System.Windows.Forms.Design.PropertyGridDesigner, " + AssemblyRef.SystemDesign)] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + [SRDescription(SR.DescriptionPropertyGrid)] + public class PropertyGrid : ContainerControl, IComPropertyBrowser, UnsafeNativeMethods.IPropertyNotifySink { + + private DocComment doccomment; + private int dcSizeRatio = -1; + private int hcSizeRatio = -1; + private HotCommands hotcommands; + private ToolStrip toolStrip; + + private bool helpVisible = true; + private bool toolbarVisible = true; + + private ImageList[] imageList = new ImageList[2]; + private Bitmap bmpAlpha; + private Bitmap bmpCategory; + private Bitmap bmpPropPage; + + // our array of viewTabs + private bool viewTabsDirty = true; + private bool drawFlatToolBar = false; + private PropertyTab[] viewTabs = new PropertyTab[0]; + private PropertyTabScope[] viewTabScopes = new PropertyTabScope[0]; + private Hashtable viewTabProps; + + // the tab view buttons + private ToolStripButton[] viewTabButtons; + // the index of the currently selected tab view + private int selectedViewTab; + + + // our view type buttons (Alpha vs. categorized) + private ToolStripButton[] viewSortButtons; + private int selectedViewSort; + private PropertySort propertySortValue; + + // this guy's kind of an odd one...he gets special treatment + private ToolStripButton btnViewPropertyPages; + private ToolStripSeparator separator1; + private ToolStripSeparator separator2; + private int buttonType = NORMAL_BUTTONS; + + // our main baby + private PropertyGridView gridView; + + + private IDesignerHost designerHost; + private IDesignerEventService designerEventService; + + private Hashtable designerSelections; + + private GridEntry peDefault; + private GridEntry peMain; + private GridEntryCollection currentPropEntries; + private Object[] currentObjects; + + private int paintFrozen; + private Color lineColor = SystemInformation.HighContrast ? (AccessibilityImprovements.Level1 ? SystemColors.ControlDarkDark : SystemColors.ControlDark ) + : SystemColors.InactiveBorder; + internal bool developerOverride = false; + internal Brush lineBrush = null; + private Color categoryForeColor = SystemColors.ControlText; + private Color categorySplitterColor = SystemColors.Control; + private Color viewBorderColor = SystemColors.ControlDark; + private Color selectedItemWithFocusForeColor = SystemColors.HighlightText; + private Color selectedItemWithFocusBackColor = SystemColors.Highlight; + internal Brush selectedItemWithFocusBackBrush = null; + private bool canShowVisualStyleGlyphs = true; + + private AttributeCollection browsableAttributes; + + private SnappableControl targetMove = null; + private int dividerMoveY = -1; + private const int CYDIVIDER = 3; + private static int cyDivider = CYDIVIDER; + private const int CXINDENT = 0; + private const int CYINDENT = 2; + private const int MIN_GRID_HEIGHT = 20; + + private const int PROPERTIES = 0; + private const int EVENTS = 1; + private const int ALPHA = 1; + private const int CATEGORIES = 0; + private const int NO_SORT = 2; + + private const int NORMAL_BUTTONS = 0; + private const int LARGE_BUTTONS = 1; + + private const int TOOLSTRIP_BUTTON_PADDING_Y = 9; + private int toolStripButtonPaddingY = TOOLSTRIP_BUTTON_PADDING_Y; + private static readonly Size DEFAULT_LARGE_BUTTON_SIZE = new Size(32, 32); + private static readonly Size DEFAULT_NORMAL_BUTTON_SIZE = new Size(16, 16); + private static Size largeButtonSize = DEFAULT_LARGE_BUTTON_SIZE; + private static Size normalButtonSize = DEFAULT_NORMAL_BUTTON_SIZE; + private static bool isScalingInitialized = false; + + private const ushort PropertiesChanged = 0x0001; + private const ushort GotDesignerEventService = 0x0002; + private const ushort InternalChange = 0x0004; + private const ushort TabsChanging = 0x0008; + private const ushort BatchMode = 0x0010; + private const ushort ReInitTab = 0x0020; + private const ushort SysColorChangeRefresh = 0x0040; + private const ushort FullRefreshAfterBatch = 0x0080; + private const ushort BatchModeChange = 0x0100; + private const ushort RefreshingProperties = 0x0200; + + private ushort flags; + + private bool GetFlag(ushort flag) { + return (flags & flag) != (ushort)0; + } + + private void SetFlag(ushort flag, bool value) { + if (value) { + flags |= flag; + } + else { + flags &= (ushort)~flag; + } + } + + + private readonly ComponentEventHandler onComponentAdd; + private readonly ComponentEventHandler onComponentRemove; + private readonly ComponentChangedEventHandler onComponentChanged; + + // the cookies for our connection points on objects that support IPropertyNotifySink + // + private AxHost.ConnectionPointCookie[] connectionPointCookies = null; + + private static object EventPropertyValueChanged = new object(); + private static object EventComComponentNameChanged = new object(); + private static object EventPropertyTabChanged = new object(); + private static object EventSelectedGridItemChanged = new object(); + private static object EventPropertySortChanged = new object(); + private static object EventSelectedObjectsChanged = new object(); + + /// + /// + /// [To be supplied.] + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // the "PropertyGridToolBar" caption is for testing. + // So we don't have to localize it. + ] + public PropertyGrid() { + + onComponentAdd = new ComponentEventHandler(OnComponentAdd); + onComponentRemove = new ComponentEventHandler(OnComponentRemove); + onComponentChanged = new ComponentChangedEventHandler(OnComponentChanged); + + SuspendLayout(); + AutoScaleMode = AutoScaleMode.None; + + // static variables are problem in a child level mixed mode scenario. Changing static variables cause compatibility issue. + // So, conditioning on new quirk to recalculate static variables everytime property grid initialized. + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + RescaleConstants(); + } + else { + if (!isScalingInitialized ) { + if (DpiHelper.IsScalingRequired) { + normalButtonSize = LogicalToDeviceUnits(DEFAULT_NORMAL_BUTTON_SIZE); + largeButtonSize = LogicalToDeviceUnits(DEFAULT_LARGE_BUTTON_SIZE); + } + + isScalingInitialized = true; + } + } + + try + { + gridView = CreateGridView(null); + gridView.TabStop = true; + gridView.MouseMove += new MouseEventHandler(this.OnChildMouseMove); + gridView.MouseDown += new MouseEventHandler(this.OnChildMouseDown); + gridView.TabIndex = 2; + + separator1 = CreateSeparatorButton(); + separator2 = CreateSeparatorButton(); + + toolStrip = AccessibilityImprovements.Level3 ? new PropertyGridToolStrip(this) : new ToolStrip(); + toolStrip.SuspendLayout(); + toolStrip.ShowItemToolTips = true; + toolStrip.AccessibleName = SR.GetString(SR.PropertyGridToolbarAccessibleName); + toolStrip.AccessibleRole = AccessibleRole.ToolBar; + toolStrip.TabStop = true; + toolStrip.AllowMerge = false; + + // This caption is for testing. + toolStrip.Text = "PropertyGridToolBar"; + + // LayoutInternal handles positioning, and for perf reasons, we manually size. + toolStrip.Dock = DockStyle.None; + toolStrip.AutoSize = false; + toolStrip.TabIndex = 1; + toolStrip.ImageScalingSize = normalButtonSize; + + // parity with the old... + toolStrip.CanOverflow = false; + + + // hide the grip but add in a few more pixels of padding. + toolStrip.GripStyle = ToolStripGripStyle.Hidden; + Padding toolStripPadding = toolStrip.Padding; + toolStripPadding.Left = 2; + toolStrip.Padding = toolStripPadding; + SetToolStripRenderer(); + + + // always add the property tab here + AddRefTab(DefaultTabType, null, PropertyTabScope.Static, true); + + doccomment = new DocComment(this); + doccomment.SuspendLayout(); + doccomment.TabStop = false; + doccomment.Dock = DockStyle.None; + doccomment.BackColor = SystemColors.Control; + doccomment.ForeColor = SystemColors.ControlText; + doccomment.MouseMove += new MouseEventHandler(this.OnChildMouseMove); + doccomment.MouseDown += new MouseEventHandler(this.OnChildMouseDown); + + + + hotcommands = new HotCommands(this); + hotcommands.SuspendLayout(); + hotcommands.TabIndex = 3; + hotcommands.Dock = DockStyle.None; + SetHotCommandColors(false); + hotcommands.Visible = false; + hotcommands.MouseMove += new MouseEventHandler(this.OnChildMouseMove); + hotcommands.MouseDown += new MouseEventHandler(this.OnChildMouseDown); + + Controls.AddRange(new Control[] { doccomment, hotcommands, gridView, toolStrip }); + + SetActiveControlInternal(gridView); + toolStrip.ResumeLayout(false); // SetupToolbar should perform the layout + SetupToolbar(); + this.PropertySort = PropertySort.Categorized | PropertySort.Alphabetical; + this.Text = "PropertyGrid"; + SetSelectState(0); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + finally { + if (doccomment != null) { + doccomment.ResumeLayout(false); + } + if (hotcommands != null) { + hotcommands.ResumeLayout(false); + } + ResumeLayout(true); + } + } + + internal IDesignerHost ActiveDesigner { + get{ + if (this.designerHost == null) { + designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); + } + return this.designerHost; + } + set{ + if (value != designerHost) { + SetFlag(ReInitTab, true); + if (this.designerHost != null) { + IComponentChangeService cs = (IComponentChangeService)designerHost.GetService(typeof(IComponentChangeService)); + if (cs != null) { + cs.ComponentAdded -= onComponentAdd; + cs.ComponentRemoved -= onComponentRemove; + cs.ComponentChanged -= onComponentChanged; + } + + IPropertyValueUIService pvSvc = (IPropertyValueUIService)designerHost.GetService(typeof(IPropertyValueUIService)); + if (pvSvc != null) { + pvSvc.PropertyUIValueItemsChanged -= new EventHandler(this.OnNotifyPropertyValueUIItemsChanged); + } + + designerHost.TransactionOpened -= new EventHandler(this.OnTransactionOpened); + designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); + SetFlag(BatchMode, false); + RemoveTabs(PropertyTabScope.Document, true); + this.designerHost = null; + } + + + + if (value != null) { + IComponentChangeService cs = (IComponentChangeService)value.GetService(typeof(IComponentChangeService)); + if (cs != null) { + cs.ComponentAdded += onComponentAdd; + cs.ComponentRemoved += onComponentRemove; + cs.ComponentChanged += onComponentChanged; + } + + value.TransactionOpened += new EventHandler(this.OnTransactionOpened); + value.TransactionClosed += new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); + SetFlag(BatchMode, false); + + IPropertyValueUIService pvSvc = (IPropertyValueUIService)value.GetService(typeof(IPropertyValueUIService)); + if (pvSvc != null) { + pvSvc.PropertyUIValueItemsChanged += new EventHandler(this.OnNotifyPropertyValueUIItemsChanged); + } + } + + designerHost = value; + if (peMain != null) { + peMain.DesignerHost = value; + } + RefreshTabs(PropertyTabScope.Document); + } + } + } + + /// + /// + /// [To be supplied.] + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AutoScroll { + get { + return base.AutoScroll; + } + set { + base.AutoScroll = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + toolStrip.BackColor = value; + toolStrip.Invalidate(true); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public AttributeCollection BrowsableAttributes { + set { + if (value == null || value == AttributeCollection.Empty) { + browsableAttributes = new AttributeCollection(new Attribute[]{BrowsableAttribute.Yes}); + } + else { + Attribute[] attributes = new Attribute[value.Count]; + value.CopyTo(attributes, 0); + browsableAttributes = new AttributeCollection(attributes); + } + if (currentObjects != null && currentObjects.Length > 0) { + if (peMain != null) { + peMain.BrowsableAttributes = BrowsableAttributes; + Refresh(true); + } + } + } + get { + if (browsableAttributes == null) { + browsableAttributes = new AttributeCollection(new Attribute[]{new BrowsableAttribute(true)}); + } + return browsableAttributes; + } + } + + private bool CanCopy { + get { + return gridView.CanCopy; + } + } + + private bool CanCut { + get { + return gridView.CanCut; + } + } + + private bool CanPaste { + get { + return gridView.CanPaste; + } + } + + private bool CanUndo { + get { + return gridView.CanUndo; + } + } + + /// + /// + /// true if the commands pane will be can be made visible + /// for the currently selected objects. Objects that + /// expose verbs can show commands. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + SRDescription(SR.PropertyGridCanShowCommandsDesc)] + public virtual bool CanShowCommands { + get { + return hotcommands.WouldBeVisible; + } + } + + /// + /// + /// The text used color for category headings. The background color is determined by the LineColor property. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCategoryForeColorDesc), + DefaultValue(typeof(Color), "ControlText") + ] + public Color CategoryForeColor { + get { + return categoryForeColor; + } + set { + if (categoryForeColor != value) { + categoryForeColor = value; + gridView.Invalidate(); + } + } + } + + /// + /// + /// The background color for the hot commands region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsBackColorDesc) + ] + public Color CommandsBackColor { + get { + return hotcommands.BackColor; + } + set { + hotcommands.BackColor = value; + hotcommands.Label.BackColor = value; + } + } + + /// + /// + /// The forground color for the hot commands region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsForeColorDesc) + ] + public Color CommandsForeColor { + get { + return hotcommands.ForeColor; + } + set { + hotcommands.ForeColor = value; + hotcommands.Label.ForeColor = value; + } + } + + /// + /// + /// The link color for the hot commands region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsLinkColorDesc) + ] + public Color CommandsLinkColor { + get { + return hotcommands.Label.LinkColor; + } + set { + hotcommands.Label.LinkColor = value; + } + } + + /// + /// + /// The active link color for the hot commands region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsActiveLinkColorDesc) + ] + public Color CommandsActiveLinkColor { + get { + return hotcommands.Label.ActiveLinkColor; + } + set { + hotcommands.Label.ActiveLinkColor = value; + } + } + + /// + /// + /// The color for the hot commands region when the link is disabled. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsDisabledLinkColorDesc) + ] + public Color CommandsDisabledLinkColor { + get { + return hotcommands.Label.DisabledLinkColor; + } + set { + hotcommands.Label.DisabledLinkColor = value; + } + } + + /// + /// + /// The border color for the hot commands region + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCommandsBorderColorDesc), + DefaultValue(typeof(Color), "ControlDark") + ] + public Color CommandsBorderColor { + get { + return hotcommands.BorderColor; + } + set { + hotcommands.BorderColor = value; + } + } + + /// + /// + /// Returns true if the commands pane is currently shown. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual bool CommandsVisible { + get { + return hotcommands.Visible; + } + } + + /// + /// + /// Returns true if the commands pane will be shown for objects + /// that expose verbs. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.PropertyGridCommandsVisibleIfAvailable) + ] + public virtual bool CommandsVisibleIfAvailable { + get { + return hotcommands.AllowVisible; + } + set { + bool hotcommandsVisible = hotcommands.Visible; + hotcommands.AllowVisible = value; + //PerformLayout(); + if (hotcommandsVisible != hotcommands.Visible) { + OnLayoutInternal(false); + hotcommands.Invalidate(); + } + } + } + + /// + /// + /// Returns a default location for showing the context menu. This + /// location is the center of the active property label in the grid, and + /// is used useful to position the context menu when the menu is invoked + /// via the keyboard. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public Point ContextMenuDefaultLocation { + get { + return GetPropertyGridView().ContextMenuDefaultLocation; + } + } + + /// + /// + /// Collection of child controls. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ControlCollection Controls { + get { + return base.Controls; + } + } + + + /// + protected override Size DefaultSize { + get { + return new Size(130, 130); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + protected virtual Type DefaultTabType { + get { + return typeof(PropertiesTab); + } + } + + /// + /// + protected bool DrawFlatToolbar { + get { + return drawFlatToolBar; + } + set { + if (drawFlatToolBar != value) { + drawFlatToolBar = value; + SetToolStripRenderer(); + } + + SetHotCommandColors(value && !AccessibilityImprovements.Level2); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + private bool FreezePainting { + get { + return paintFrozen > 0; + } + set { + + if (value && IsHandleCreated && this.Visible) { + if (0 == paintFrozen++) { + SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); + } + } + if (!value) { + if (paintFrozen == 0) { + return; + } + + if (0 == --paintFrozen) { + SendMessage(NativeMethods.WM_SETREDRAW, 1, 0); + Invalidate(true); + } + + } + } + } + + /// + /// Gets the help control accessibility object. + /// + internal AccessibleObject HelpAccessibleObject { + get { + return doccomment.AccessibilityObject; + } + } + + /// + /// + /// The background color for the help region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridHelpBackColorDesc), + DefaultValue(typeof(Color), "Control") + ] + public Color HelpBackColor { + get { + return doccomment.BackColor; + } + set { + doccomment.BackColor = value; + } + } + + /// + /// + /// The forground color for the help region. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridHelpForeColorDesc), + DefaultValue(typeof(Color), "ControlText") + ] + public Color HelpForeColor { + get { + return doccomment.ForeColor; + } + set { + doccomment.ForeColor = value; + } + } + + /// + /// + /// The border color for the help region + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridHelpBorderColorDesc), + DefaultValue(typeof(Color), "ControlDark") + ] + public Color HelpBorderColor { + get { + return doccomment.BorderColor; + } + set { + doccomment.BorderColor = value; + } + } + + /// + /// + /// Sets or gets the visiblity state of the help pane. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + Localizable(true), + SRDescription(SR.PropertyGridHelpVisibleDesc) + ] + public virtual bool HelpVisible { + get { + return this.helpVisible; + } + set { + this.helpVisible = value; + + doccomment.Visible = value; + OnLayoutInternal(false); + Invalidate(); + doccomment.Invalidate(); + } + } + + /// + /// Gets the hot commands control accessible object. + /// + internal AccessibleObject HotCommandsAccessibleObject { + get { + return hotcommands.AccessibilityObject; + } + } + + /// + /// Gets the main entry accessible object. + /// + internal AccessibleObject GridViewAccessibleObject { + get { + return gridView.AccessibilityObject; + } + } + + /// + /// Gets the value indicating whether the main entry is visible. + /// + internal bool GridViewVisible { + get { + return gridView != null && gridView.Visible; + } + } + + /// + /// + /// Background color for Highlighted text. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridSelectedItemWithFocusBackColorDesc), + DefaultValue(typeof(Color), "Highlight") + ] + public Color SelectedItemWithFocusBackColor { + get { + return selectedItemWithFocusBackColor; + } + set { + if (selectedItemWithFocusBackColor != value) + { + selectedItemWithFocusBackColor = value; + gridView.Invalidate(); + } + } + } + + /// + /// + /// Foreground color for Highlighted (selected) text. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridSelectedItemWithFocusForeColorDesc), + DefaultValue(typeof(Color), "HighlightText") + ] + public Color SelectedItemWithFocusForeColor { + get { + return selectedItemWithFocusForeColor; + } + set { + if (selectedItemWithFocusForeColor != value) + { + selectedItemWithFocusForeColor = value; + gridView.Invalidate(); + } + } + } + + /// + /// + /// Foreground color for disabled text in the Grid View + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridDisabledItemForeColorDesc), + DefaultValue(typeof(Color), "GrayText") + ] + public Color DisabledItemForeColor { + get { + return gridView.GrayTextColor; + } + set { + gridView.GrayTextColor = value; + gridView.Invalidate(); + } + } + + /// + /// + /// Color for the horizontal splitter line separating property categories. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCategorySplitterColorDesc), + DefaultValue(typeof(Color), "Control") + ] + public Color CategorySplitterColor { + get { + return categorySplitterColor; + } + set { + if (categorySplitterColor != value) { + categorySplitterColor = value; + gridView.Invalidate(); + } + } + } + + /// + /// + /// Enable/Disable use of VisualStyle glyph for PropertyGrid node expansion. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridCanShowVisualStyleGlyphsDesc), + DefaultValue(true) + ] + public bool CanShowVisualStyleGlyphs { + get { + return canShowVisualStyleGlyphs; + } + set { + if (canShowVisualStyleGlyphs != value) { + canShowVisualStyleGlyphs = value; + gridView.Invalidate(); + } + } + } + + /// + /// + bool IComPropertyBrowser.InPropertySet { + get { + return GetPropertyGridView().GetInPropertySet(); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridLineColorDesc), + DefaultValue(typeof(Color), "InactiveBorder") + ] + public Color LineColor { + get { + return lineColor; + } + set { + if (lineColor != value) { + lineColor = value; + developerOverride = true; + if (lineBrush != null) { + lineBrush.Dispose(); + lineBrush = null; + } + gridView.Invalidate(); + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// Sets or gets the current property sort type, which can be + /// PropertySort.Categorized or PropertySort.Alphabetical. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(PropertySort.CategorizedAlphabetical), + SRDescription(SR.PropertyGridPropertySortDesc) + ] + public PropertySort PropertySort { + get { + return propertySortValue; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)PropertySort.NoSort, (int)PropertySort.CategorizedAlphabetical)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(PropertySort)); + } + ToolStripButton newButton; + + if ((value & PropertySort.Categorized) != 0) { + newButton = viewSortButtons[CATEGORIES]; + } + else if ((value & PropertySort.Alphabetical) != 0) { + newButton = viewSortButtons[ALPHA]; + } + else { + newButton = viewSortButtons[NO_SORT]; + } + + GridItem selectedGridItem = SelectedGridItem; + + + OnViewSortButtonClick(newButton, EventArgs.Empty); + + this.propertySortValue = value; + + if (selectedGridItem != null) { + try { + SelectedGridItem = selectedGridItem; + } + catch (System.ArgumentException) { + // VSWhidbey#158036. When no row is selected, SelectedGridItem returns grid entry for root + // object. But this is not a selectable item. So don't worry if setting SelectedGridItem + // cause an argument exception whe ntrying to re-select the root object. Just leave the + // the grid with no selected row. + } + } + + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public PropertyTabCollection PropertyTabs { + get { + return new PropertyTabCollection(this); + } + } + + /// + /// + /// Sets a single Object into the grid to be browsed. If multiple + /// objects are being browsed, this property will return the first + /// one in the list. If no objects are selected, null is returned. + /// + [ + DefaultValue(null), + SRDescription(SR.PropertyGridSelectedObjectDesc), + SRCategory(SR.CatBehavior), + TypeConverter(typeof(SelectedObjectConverter)) + ] + public Object SelectedObject { + get { + if (currentObjects == null || currentObjects.Length == 0) { + return null; + } + return currentObjects[0]; + } + set { + if (value == null) { + SelectedObjects = new object[0]; + } + else { + SelectedObjects = new Object[]{value}; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public object[] SelectedObjects { + set { + try { + this.FreezePainting = true; + + SetFlag(FullRefreshAfterBatch, false); + if (GetFlag(BatchMode)) { + SetFlag(BatchModeChange, false); + } + + gridView.EnsurePendingChangesCommitted(); + + bool isSame = false; + bool classesSame = false; + bool showEvents = true; + + // validate the array coming in + if (value != null && value.Length > 0) { + for (int count = 0; count < value.Length; count++) { + if (value[count] == null) { + throw new ArgumentException(SR.GetString(SR.PropertyGridSetNull, count.ToString(CultureInfo.CurrentCulture), value.Length.ToString(CultureInfo.CurrentCulture))); + } + else if (value[count] is IUnimplemented) { + throw new NotSupportedException(SR.GetString(SR.PropertyGridRemotedObject, value[count].GetType().FullName)); + } + } + } + else { + showEvents = false; + } + + // make sure we actually changed something before we inspect tabs + if (currentObjects != null && value != null && + currentObjects.Length == value.Length) { + isSame = true; + classesSame = true; + for (int i = 0; i < value.Length && (isSame || classesSame); i++) { + if (isSame && currentObjects[i] != value[i]) { + isSame = false; + } + + Type oldType = GetUnwrappedObject(i).GetType(); + + Object objTemp = value[i]; + + if (objTemp is ICustomTypeDescriptor) { + objTemp = ((ICustomTypeDescriptor)objTemp).GetPropertyOwner(null); + } + Type newType = objTemp.GetType(); + + // check if the types are the same. If they are, and they + // are COM objects, check their GUID's. If they are different + // or Guid.Emtpy, assume the classes are different. + // + if (classesSame && + (oldType != newType || oldType.IsCOMObject && newType.IsCOMObject)) { + classesSame = false; + } + } + } + + if (!isSame) { + + EnsureDesignerEventService(); + + showEvents = showEvents && GetFlag(GotDesignerEventService); + + SetStatusBox("", ""); + + ClearCachedProps(); + + // The default selected entry might still reference the previous selected + // objects. Set it to null to avoid leaks. + peDefault = null; + + if (value == null) { + currentObjects = new Object[0]; + } + else { + currentObjects = (object[])value.Clone(); + } + + SinkPropertyNotifyEvents(); + SetFlag(PropertiesChanged, true); + + + // Since we are changing the selection, we need to make sure that the + // keywords for the currently selected grid entry gets removed + if (gridView != null) { + // TypeResolutionService is needed to access the HelpKeyword. However, + // TypeResolutionService might be disposed when project is closing. We + // need swallow the exception in this case. + try { + gridView.RemoveSelectedEntryHelpAttributes(); + } + catch (COMException) {} + } + + if (peMain != null) { + peMain.Dispose(); + } + + // throw away any extra component only tabs + if (!classesSame && !GetFlag(TabsChanging) && selectedViewTab < viewTabButtons.Length) { + + Type tabType = selectedViewTab == -1 ? null : viewTabs[selectedViewTab].GetType(); + ToolStripButton viewTabButton = null; + RefreshTabs(PropertyTabScope.Component); + EnableTabs(); + if (tabType != null) { + for (int i = 0; i < viewTabs.Length;i++) { + if (viewTabs[i].GetType() == tabType && viewTabButtons[i].Visible) { + viewTabButton = viewTabButtons[i]; + break; + } + } + } + SelectViewTabButtonDefault(viewTabButton); + } + + // make sure we've also got events on all the objects + if (showEvents && viewTabs != null && viewTabs.Length > EVENTS && (viewTabs[EVENTS] is EventsTab)) { + showEvents = viewTabButtons[EVENTS].Visible; + Object tempObj; + PropertyDescriptorCollection events; + Attribute[] attrs = new Attribute[BrowsableAttributes.Count]; + BrowsableAttributes.CopyTo(attrs, 0); + + Hashtable eventTypes = null; + + if (currentObjects.Length > 10) { + eventTypes = new Hashtable(); + } + + for (int i = 0; i < currentObjects.Length && showEvents; i++) { + tempObj = currentObjects[i]; + + if (tempObj is ICustomTypeDescriptor) { + tempObj = ((ICustomTypeDescriptor)tempObj).GetPropertyOwner(null); + } + + Type objType = tempObj.GetType(); + + if (eventTypes != null && eventTypes.Contains(objType)) { + continue; + } + + // make sure these things are sited components as well + showEvents = showEvents && (tempObj is IComponent && ((IComponent)tempObj).Site != null); + + // make sure we've also got events on all the objects + events = ((EventsTab)viewTabs[EVENTS]).GetProperties(tempObj, attrs); + showEvents = showEvents && events != null && events.Count > 0; + + if (showEvents && eventTypes != null) { + eventTypes[objType] = objType; + } + } + } + ShowEventsButton(showEvents && currentObjects.Length > 0); + DisplayHotCommands(); + + if (currentObjects.Length == 1) { + EnablePropPageButton(currentObjects[0]); + } + else { + EnablePropPageButton(null); + } + OnSelectedObjectsChanged(EventArgs.Empty); + } + + + /* + + Microsoft, hopefully this won't be a big perf problem, but it looks like we + need to refresh even if we didn't change the selected objects. + + if (propertiesChanged) {*/ + if (!GetFlag(TabsChanging)) { + + // ReInitTab means that we should set the tab back to what is used to be for a given designer. + // Basically, if you select an events tab for your designer and double click to go to code, it should + // be the events tab when you get back to the designer. + // + // so we set that bit when designers get switched, and makes sure we select and refresh that tab + // when we load. + // + if (currentObjects.Length > 0 && GetFlag(ReInitTab)) { + object designerKey = ActiveDesigner; + + // get the active designer, see if we've stashed away state for it. + // + if (designerKey != null && designerSelections != null && designerSelections.ContainsKey(designerKey.GetHashCode())) { + int nButton = (int)designerSelections[designerKey.GetHashCode()]; + + // yep, we know this one. Make sure it's selected. + // + if (nButton < viewTabs.Length && (nButton == PROPERTIES || viewTabButtons[nButton].Visible)) { + SelectViewTabButton(viewTabButtons[nButton], true); + } + } + else { + Refresh(false); + } + SetFlag(ReInitTab, false); + } + else { + Refresh(true); + } + + if (currentObjects.Length > 0) { + SaveTabSelection(); + } + } + /*}else { + Invalidate(); + gridView.Invalidate(); + //}*/ + } + finally { + this.FreezePainting = false; + } + } + + get + { + if (currentObjects == null) { + return new object[0]; + } + return (object[])currentObjects.Clone(); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public PropertyTab SelectedTab { + get { + Debug.Assert(selectedViewTab < viewTabs.Length && selectedViewTab >= 0, "Invalid tab selection!"); + return viewTabs[selectedViewTab]; + } + } + + + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public GridItem SelectedGridItem { + get { + GridItem g = gridView.SelectedGridEntry; + if (g == null) { + return this.peMain; + } + return g; + } + set { + gridView.SelectedGridEntry = (GridEntry)value; + } + } + + /// + /// + protected internal override bool ShowFocusCues { + get { + return true; + } + } + + /// + public override ISite Site { + get { + return base.Site; + } + set { + + // Perf - the base class is possibly going to change the font via ambient properties service + SuspendAllLayout(this); + + base.Site = value; + gridView.ServiceProvider = value; + + if (value == null) { + this.ActiveDesigner = null; + } + else { + this.ActiveDesigner = (IDesignerHost)value.GetService(typeof(IDesignerHost)); + } + + ResumeAllLayout(this,true); + + } + } + + /// + /// Gets the value indicating whether the Property grid is sorted by categories. + /// + internal bool SortedByCategories { + get { + return (PropertySort & PropertySort.Categorized) != 0; + } + } + + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + [Browsable(false)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridLargeButtonsDesc), + DefaultValue(false) + ] + public bool LargeButtons { + get { + return buttonType == LARGE_BUTTONS; + } + set { + if (value == (buttonType == LARGE_BUTTONS)) { + return; + } + + this.buttonType = (value ? LARGE_BUTTONS : NORMAL_BUTTONS); + if (value) { + EnsureLargeButtons(); + if (this.imageList != null && this.imageList[LARGE_BUTTONS] != null) { + toolStrip.ImageScalingSize = this.imageList[LARGE_BUTTONS].ImageSize; + } + } + else { + if (this.imageList != null && this.imageList[NORMAL_BUTTONS] != null) { + toolStrip.ImageScalingSize = this.imageList[NORMAL_BUTTONS].ImageSize; + } + } + + toolStrip.ImageList = imageList[this.buttonType]; + OnLayoutInternal(false); + Invalidate(); + toolStrip.Invalidate(); + } + } + + /// + /// Gets the toolbar control accessibility object. + /// + internal AccessibleObject ToolbarAccessibleObject { + get { + return toolStrip.AccessibilityObject; + } + } + + /// + /// + /// Sets or gets the visiblity state of the toolStrip. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.PropertyGridToolbarVisibleDesc) + ] + public virtual bool ToolbarVisible { + get { + return this.toolbarVisible; + } + set { + this.toolbarVisible = value; + + toolStrip.Visible = value; + OnLayoutInternal(false); + if (value) { + SetupToolbar(this.viewTabsDirty); + } + Invalidate(); + toolStrip.Invalidate(); + } + } + + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected ToolStripRenderer ToolStripRenderer { + get { + if (toolStrip != null) { + return toolStrip.Renderer; + } + return null; + } + set { + if (toolStrip != null) { + toolStrip.Renderer = value; + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridViewBackColorDesc), + DefaultValue(typeof(Color), "Window") + ] + public Color ViewBackColor { + get { + return gridView.BackColor; + } + set { + gridView.BackColor = value; + gridView.Invalidate(); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridViewForeColorDesc), + DefaultValue(typeof(Color), "WindowText") + ] + public Color ViewForeColor { + get { + return gridView.ForeColor; + } + set { + gridView.ForeColor = value; + gridView.Invalidate(); + + } + } + + /// + /// + /// Border color for the property grid view. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.PropertyGridViewBorderColorDesc), + DefaultValue(typeof(Color), "ControlDark") + ] + public Color ViewBorderColor { + get { + return viewBorderColor; + } + set { + if (viewBorderColor != value) { + viewBorderColor = value; + gridView.Invalidate(); + } + } + } + + private int AddImage(Bitmap image) { + + image.MakeTransparent(); + // Resize bitmap only if resizing is needed in order to avoid image distortion. + if (DpiHelper.IsScalingRequired && (image.Size.Width != normalButtonSize.Width || image.Size.Height != normalButtonSize.Height)) { + image = DpiHelper.CreateResizedBitmap(image, normalButtonSize); + } + int result = imageList[NORMAL_BUTTONS].Images.Count; + imageList[NORMAL_BUTTONS].Images.Add(image); + return result; + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseDown { + add { + base.MouseDown += value; + } + remove { + base.MouseDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseUp { + add { + base.MouseUp += value; + } + remove { + base.MouseUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event MouseEventHandler MouseMove { + add { + base.MouseMove += value; + } + remove { + base.MouseMove -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler MouseEnter { + add { + base.MouseEnter += value; + } + remove { + base.MouseEnter -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public new event EventHandler MouseLeave { + add { + base.MouseLeave += value; + } + remove { + base.MouseLeave -= value; + } + } + + /// + /// Event that is fired when a property value is modified. + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PropertyGridPropertyValueChangedDescr)] + public event PropertyValueChangedEventHandler PropertyValueChanged { + add { + Events.AddHandler(EventPropertyValueChanged, value); + } + remove { + Events.RemoveHandler(EventPropertyValueChanged, value); + } + } + + /// + /// + event ComponentRenameEventHandler IComPropertyBrowser.ComComponentNameChanged { + add { + Events.AddHandler(EventComComponentNameChanged, value); + } + remove { + Events.RemoveHandler(EventComComponentNameChanged, value); + } + } + + /// + /// Event that is fired when the current view tab is changed, such as changing from Properties to Events + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PropertyGridPropertyTabchangedDescr)] + public event PropertyTabChangedEventHandler PropertyTabChanged { + add { + Events.AddHandler(EventPropertyTabChanged, value); + } + remove { + Events.RemoveHandler(EventPropertyTabChanged, value); + } + } + + /// + /// Event that is fired when the sort mode is changed. + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PropertyGridPropertySortChangedDescr)] + public event EventHandler PropertySortChanged { + add { + Events.AddHandler(EventPropertySortChanged, value); + } + remove { + Events.RemoveHandler(EventPropertySortChanged, value); + } + } + + /// + /// Event that is fired when the selected GridItem is changed + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PropertyGridSelectedGridItemChangedDescr)] + public event SelectedGridItemChangedEventHandler SelectedGridItemChanged { + add { + Events.AddHandler(EventSelectedGridItemChanged, value); + } + remove { + Events.RemoveHandler(EventSelectedGridItemChanged, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.PropertyGridSelectedObjectsChangedDescr)] + public event EventHandler SelectedObjectsChanged { + add { + Events.AddHandler(EventSelectedObjectsChanged, value); + } + remove { + Events.RemoveHandler(EventSelectedObjectsChanged, value); + } + } + + + internal void AddTab(Type tabType, PropertyTabScope scope) { + AddRefTab(tabType, null, scope, true); + } + + + internal void AddRefTab(Type tabType, Object component, PropertyTabScope type, bool setupToolbar) { + PropertyTab tab = null; + int tabIndex = -1; + + if (viewTabs != null) { + // check to see if we've already got a tab of this type + for (int i = 0; i < viewTabs.Length; i++) { + Debug.Assert(viewTabs[i] != null, "Null item in tab array!"); + if (tabType == viewTabs[i].GetType()) { + tab = viewTabs[i]; + tabIndex = i; + break; + } + } + } + else { + tabIndex = 0; + } + + if (tab == null) { + // the tabs need service providers. The one we hold onto is not good enough, + // so try to get the one off of the component's site. + IDesignerHost host = null; + if (component != null && component is IComponent && ((IComponent) component).Site != null) + host = (IDesignerHost) ((IComponent) component).Site.GetService(typeof(IDesignerHost)); + + try + { + tab = CreateTab(tabType, host); + } + catch (Exception e) + { + Debug.Fail("Bad Tab. We're not going to show it. ", e.ToString()); + return; + } + + // add it at the end of the array + if (viewTabs != null) { + tabIndex = viewTabs.Length; + + // find the insertion position...special case for event's and properties + if (tabType == DefaultTabType) { + tabIndex = PROPERTIES; + } + else if (typeof(EventsTab).IsAssignableFrom(tabType)) { + tabIndex = EVENTS; + } + else { + // order tabs alphabetically, we've always got a property tab, so + // start after that + for (int i = 1; i < viewTabs.Length; i++) { + + // skip the event tab + if (viewTabs[i] is EventsTab) { + continue; + } + + if (String.Compare(tab.TabName, viewTabs[i].TabName, false, CultureInfo.InvariantCulture) < 0) { + tabIndex = i; + break; + } + } + } + } + + // now add the tab to the tabs array + PropertyTab[] newTabs = new PropertyTab[viewTabs.Length + 1]; + Array.Copy(viewTabs, 0, newTabs, 0, tabIndex); + Array.Copy(viewTabs, tabIndex, newTabs, tabIndex + 1, viewTabs.Length - tabIndex); + newTabs[tabIndex] = tab; + viewTabs = newTabs; + + viewTabsDirty = true; + + PropertyTabScope[] newTabScopes = new PropertyTabScope[viewTabScopes.Length + 1]; + Array.Copy(viewTabScopes, 0, newTabScopes, 0, tabIndex); + Array.Copy(viewTabScopes, tabIndex, newTabScopes, tabIndex + 1, viewTabScopes.Length - tabIndex); + newTabScopes[tabIndex] = type; + viewTabScopes = newTabScopes; + + Debug.Assert(viewTabs != null, "Tab array destroyed!"); + } + + if (tab != null && component != null) { + try { + Object[] tabComps = tab.Components; + int oldArraySize = tabComps == null ? 0 : tabComps.Length; + + Object[] newComps = new Object[oldArraySize + 1]; + if (oldArraySize > 0) { + Array.Copy(tabComps, newComps, oldArraySize); + } + newComps[oldArraySize] = component; + tab.Components = newComps; + } + catch (Exception e) { + Debug.Fail("Bad tab. We're going to remove it.", e.ToString()); + RemoveTab(tabIndex, false); + } + } + + if (setupToolbar) { + SetupToolbar(); + ShowEventsButton(false); + } + } + + /// + /// Collapses all the nodes in the PropertyGrid + public void CollapseAllGridItems() { + gridView.RecursivelyExpand(peMain, false, false, -1); + } + + private void ClearCachedProps() { + if (viewTabProps != null) { + viewTabProps.Clear(); + } + } + + internal void ClearValueCaches() { + if (peMain != null) { + peMain.ClearCachedValues(); + } + } + + + /// + /// Clears the tabs of the given scope or smaller. + /// tabScope must be PropertyTabScope.Component or PropertyTabScope.Document. + /// + internal void ClearTabs(PropertyTabScope tabScope) { + if (tabScope < PropertyTabScope.Document) { + throw new ArgumentException(SR.GetString(SR.PropertyGridTabScope)); + } + RemoveTabs(tabScope, true); + } + + #if DEBUG + internal bool inGridViewCreate = false; + #endif + + /// + /// Constructs the new instance of the accessibility object for current PropertyGrid control. + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new PropertyGridAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + private /*protected virtual*/ PropertyGridView CreateGridView(IServiceProvider sp) { +#if DEBUG + try { + inGridViewCreate = true; +#endif + return new PropertyGridView(sp, this); +#if DEBUG + } + finally { + inGridViewCreate = false; + } +#endif + } + + private ToolStripSeparator CreateSeparatorButton() { + ToolStripSeparator button = new ToolStripSeparator(); + + // On DpiChanged in PerMonV2 scenarios, we need to delegate the new DPI value to + // the affected ToolStripItems, which become newly created. DpiHelper.DeviceDpi does + // not have the changed DPI value at this point. + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + button.DeviceDpi = DeviceDpi; + } + + return button; + } + + /// + protected virtual PropertyTab CreatePropertyTab(Type tabType) { + return null; + } + + private PropertyTab CreateTab(Type tabType, IDesignerHost host) { + PropertyTab tab = CreatePropertyTab(tabType); + + if (tab == null) { + ConstructorInfo constructor = tabType.GetConstructor(new Type[] {typeof(IServiceProvider)}); + Object param = null; + if (constructor == null) { + + // try a IDesignerHost ctor + constructor = tabType.GetConstructor(new Type[] {typeof(IDesignerHost)}); + + if (constructor != null) { + param = host; + } + } + else { + param = this.Site; + } + + + if (param != null && constructor != null) { + tab = (PropertyTab) constructor.Invoke(new Object[] {param}); + } + else { + // just call the default ctor + // SECREVIEW: 332064 this is ok because the PropertyGrid requires FullTrust anyways. + tab = (PropertyTab)Activator.CreateInstance(tabType); + } + } + + Debug.Assert(tab != null, "Failed to create tab!"); + + if (tab != null) { + // ensure it's a valid tab + Bitmap bitmap = tab.Bitmap; + + if (bitmap == null) + throw new ArgumentException(SR.GetString(SR.PropertyGridNoBitmap, tab.GetType().FullName)); + + Size size = bitmap.Size; + if (size.Width != 16 || size.Height != 16) { + // resize it to 16x16 if it isn't already. + // + bitmap = new Bitmap(bitmap, new Size(16,16)); + } + + string name = tab.TabName; + if (name == null || name.Length == 0) + throw new ArgumentException(SR.GetString(SR.PropertyGridTabName, tab.GetType().FullName)); + + // we're good to go! + } + return tab; + } + + /* + private ToolStripButton CreateToggleButton(string toolTipText, int imageIndex, EventHandler eventHandler) { + ToolStripButton button = new ToolStripButton(); + button.Text = toolTipText; + button.AutoToolTip = true; + button.DisplayStyle = ToolStripItemDisplayStyle.Image; + button.ImageIndex = imageIndex; + button.Click += eventHandler; + button.CheckOnClick = true; + button.ImageScaling = ToolStripItemImageScaling.None; + return button; + } + */ + + private ToolStripButton CreatePushButton(string toolTipText, int imageIndex, EventHandler eventHandler, bool useCheckButtonRole = false) { + ToolStripButton button = new ToolStripButton(); + + // On DpiChanged in PerMonV2 scenarios, we need to delegate the new DPI value to + // the affected ToolStripItems, which become newly created. DpiHelper.DeviceDpi does + // not have the changed DPI value at this point. + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + button.DeviceDpi = DeviceDpi; + } + button.Text = toolTipText; + button.AutoToolTip = true; + button.DisplayStyle = ToolStripItemDisplayStyle.Image; + button.ImageIndex = imageIndex; + button.Click += eventHandler; + button.ImageScaling = ToolStripItemImageScaling.SizeToFit; + + if (AccessibilityImprovements.Level1) { + if (useCheckButtonRole) { + button.AccessibleRole = AccessibleRole.CheckButton; + } + } + + return button; + } + + /// + internal void DumpPropsToConsole() { + gridView.DumpPropsToConsole(peMain, ""); + } + + private void DisplayHotCommands() { + bool hotCommandsDisplayed = hotcommands.Visible; + + IComponent component = null; + DesignerVerb[] verbs = null; + + // We favor the menu command service, since it can give us + // verbs. If we fail that, we will go straight to the + // designer. + // + if (currentObjects != null && currentObjects.Length > 0) { + for (int i = 0; i < currentObjects.Length; i++) { + object obj = GetUnwrappedObject(i); + if (obj is IComponent) { + component = (IComponent)obj; + break; + } + } + + if (component != null) { + ISite site = component.Site; + + if (site != null) { + + IMenuCommandService mcs = (IMenuCommandService)site.GetService(typeof(IMenuCommandService)); + if (mcs != null) { + + // Got the menu command service. Let it deal with the set of verbs for + // this component. + // + verbs = new DesignerVerb[mcs.Verbs.Count]; + mcs.Verbs.CopyTo(verbs, 0); + } + else { + + // No menu command service. Go straight to the component's designer. We + // can only do this if the Object count is 1, because desginers do not + // support verbs across a multi-selection. + // + if (currentObjects.Length == 1 && GetUnwrappedObject(0) is IComponent) { + + IDesignerHost designerHost = (IDesignerHost) site.GetService(typeof(IDesignerHost)); + if (designerHost != null) { + IDesigner designer = designerHost.GetDesigner(component); + if (designer != null) { + verbs = new DesignerVerb[designer.Verbs.Count]; + designer.Verbs.CopyTo(verbs, 0); + } + } + } + } + } + } + } + + // VSWhidbey 122645 -- don't show verbs if a prop grid is on the form at design time. + //s + if (!DesignMode) { + + + if (verbs != null && verbs.Length > 0) { + hotcommands.SetVerbs(component, verbs); + } + else { + hotcommands.SetVerbs(null, null); + } + + if (hotCommandsDisplayed != hotcommands.Visible) { + OnLayoutInternal(false); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void Dispose(bool disposing) { + + if (disposing) { + // Unhook IDesignerEventService.ActiveDesignerChanged event + // + if (GetFlag(GotDesignerEventService)) { + Debug.Assert(designerEventService != null, "GetFlag(GotDesignerEventService) inconsistent with designerEventService == null"); + if (designerEventService != null) { + designerEventService.ActiveDesignerChanged -= new ActiveDesignerEventHandler(this.OnActiveDesignerChanged); + } + designerEventService = null; + SetFlag(GotDesignerEventService, false); + } + this.ActiveDesigner = null; + + if (viewTabs != null) { + for (int i = 0; i < viewTabs.Length; i++) { + viewTabs[i].Dispose(); + } + viewTabs = null; + } + + if (imageList != null) { + for (int i = 0; i < imageList.Length; i++) { + if(imageList[i] != null) { + imageList[i].Dispose(); + } + } + imageList = null; + } + + if (bmpAlpha != null) { + bmpAlpha.Dispose(); + bmpAlpha = null; + } + + if (bmpCategory != null) { + bmpCategory.Dispose(); + bmpCategory = null; + } + + if (bmpPropPage != null) { + bmpPropPage.Dispose(); + bmpPropPage = null; + } + + if (lineBrush != null) { + lineBrush.Dispose(); + lineBrush = null; + } + + if (peMain != null) { + peMain.Dispose(); + peMain = null; + } + + if (currentObjects != null) { + currentObjects = null; + SinkPropertyNotifyEvents(); + } + + ClearCachedProps(); + currentPropEntries = null; + } + + base.Dispose(disposing); + } + + private void DividerDraw(int y) { + if (y == -1) + return; + + Rectangle rectangle = gridView.Bounds; + rectangle.Y = y - cyDivider; + rectangle.Height = cyDivider; + + DrawXorBar(this,rectangle); + } + + private SnappableControl DividerInside(int x, int y) { + + int useGrid = -1; + + if (hotcommands.Visible) { + Point locDoc = hotcommands.Location; + if (y >= (locDoc.Y - cyDivider) && + y <= (locDoc.Y + 1)) { + return hotcommands; + } + useGrid = 0; + } + + if (doccomment.Visible) { + Point locDoc = doccomment.Location; + if (y >= (locDoc.Y - cyDivider) && + y <= (locDoc.Y+1)) { + return doccomment; + } + + if (useGrid == -1) { + useGrid = 1; + } + } + + // also the bottom line of the grid + if (useGrid != -1) { + int gridTop = gridView.Location.Y; + int gridBottom = gridTop + gridView.Size.Height; + + if (Math.Abs(gridBottom - y) <= 1 && y > gridTop) { + switch (useGrid) { + case 0: + return hotcommands; + case 1: + return doccomment; + } + } + } + return null; + } + + private int DividerLimitHigh(SnappableControl target) { + int high = gridView.Location.Y + MIN_GRID_HEIGHT; + if (target == doccomment && hotcommands.Visible) + high += hotcommands.Size.Height + 2; + return high; + } + + private int DividerLimitMove(SnappableControl target, int y) { + Rectangle rectTarget = target.Bounds; + + int cyNew = y; + + // make sure we're not going to make ourselves zero height -- make 15 the min size + cyNew = Math.Min((rectTarget.Y + rectTarget.Height - 15),cyNew); + + // make sure we're not going to make ourselves cover up the grid + cyNew = Math.Max(DividerLimitHigh(target), cyNew); + + // just return what we got here + return(cyNew); + } + + private static void DrawXorBar(Control ctlDrawTo, Rectangle rcFrame) { + Rectangle rc = ctlDrawTo.RectangleToScreen(rcFrame); + + if (rc.Width < rc.Height) { + for (int i = 0; i < rc.Width; i++) { + ControlPaint.DrawReversibleLine(new Point(rc.X+i, rc.Y), new Point(rc.X+i, rc.Y+rc.Height), ctlDrawTo.BackColor); + } + } + else { + for (int i = 0; i < rc.Height; i++) { + ControlPaint.DrawReversibleLine(new Point(rc.X, rc.Y+i), new Point(rc.X+rc.Width, rc.Y+i), ctlDrawTo.BackColor); + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + void IComPropertyBrowser.DropDownDone() { + GetPropertyGridView().DropDownDone(); + } + + private bool EnablePropPageButton(Object obj) { + if (obj == null) { + btnViewPropertyPages.Enabled = false; + return false; + } + + IUIService uiSvc = (IUIService)GetService(typeof(IUIService)); + bool enable = false; + + if (uiSvc != null) { + enable = uiSvc.CanShowComponentEditor(obj); + } + else { + enable = (TypeDescriptor.GetEditor(obj, typeof(ComponentEditor)) != null); + } + + btnViewPropertyPages.Enabled = enable; + return enable; + } + + // walk through the current tabs to see if they're all valid for this Object + private void EnableTabs() { + if (currentObjects != null) { + // make sure our toolbars is okay + SetupToolbar(); + + Debug.Assert(viewTabs != null, "Invalid tab array"); + Debug.Assert(viewTabs.Length == viewTabScopes.Length && viewTabScopes.Length == viewTabButtons.Length,"Uh oh, tab arrays aren't all the same length! tabs=" + viewTabs.Length.ToString(CultureInfo.InvariantCulture) + ", scopes=" + viewTabScopes.Length.ToString(CultureInfo.InvariantCulture) + ", buttons=" + viewTabButtons.Length.ToString(CultureInfo.InvariantCulture)); + + + + // skip the property tab since it's always valid + for (int i = 1; i < viewTabs.Length; i++) { + Debug.Assert(viewTabs[i] != null, "Invalid tab array entry"); + + bool canExtend = true; + // make sure the tab is valid for all objects + for (int j = 0; j < currentObjects.Length; j++) { + try + { + if (!viewTabs[i].CanExtend(GetUnwrappedObject(j))) + { + canExtend = false; + break; + } + } + catch (Exception e) + { + Debug.Fail("Bad Tab. Disable for now.", e.ToString()); + canExtend = false; + break; + } + } + + if (canExtend != viewTabButtons[i].Visible) { + viewTabButtons[i].Visible = canExtend; + if (!canExtend && i == selectedViewTab) { + SelectViewTabButton(viewTabButtons[PROPERTIES], true); + } + } + } + } + } + + private void EnsureDesignerEventService() { + if (GetFlag(GotDesignerEventService)) { + return; + } + designerEventService = (IDesignerEventService)GetService(typeof(IDesignerEventService)); + if (designerEventService != null) { + SetFlag(GotDesignerEventService, true); + designerEventService.ActiveDesignerChanged += new ActiveDesignerEventHandler(this.OnActiveDesignerChanged); + OnActiveDesignerChanged(null, new ActiveDesignerEventArgs(null, designerEventService.ActiveDesigner)); + } + } + + private void EnsureLargeButtons() { + if (this.imageList[LARGE_BUTTONS] == null) { + + this.imageList[LARGE_BUTTONS] = new ImageList(); + this.imageList[LARGE_BUTTONS].ImageSize = largeButtonSize; + + if (DpiHelper.IsScalingRequired) { + AddLargeImage(bmpAlpha); + AddLargeImage(bmpCategory); + + foreach (PropertyTab tab in viewTabs) { + AddLargeImage(tab.Bitmap); + } + + AddLargeImage(bmpPropPage); + } + else { + ImageList.ImageCollection images = imageList[NORMAL_BUTTONS].Images; + + for (int i = 0; i < images.Count; i++) { + if (images[i] is Bitmap) { + this.imageList[LARGE_BUTTONS].Images.Add(new Bitmap((Bitmap)images[i], largeButtonSize.Width, largeButtonSize.Height)); + } + } + } + } + } + + // this method should be called only inside a if (DpiHelper.IsScalingRequired) clause + private void AddLargeImage(Bitmap originalBitmap) { + if (originalBitmap == null) { + return; + } + + Bitmap largeBitmap = null; + try { + Bitmap transparentBitmap = new Bitmap(originalBitmap); + transparentBitmap.MakeTransparent(); + largeBitmap = DpiHelper.CreateResizedBitmap(transparentBitmap, largeButtonSize); + transparentBitmap.Dispose(); + + this.imageList[LARGE_BUTTONS].Images.Add(largeBitmap); + } + catch (Exception ex) { + Debug.Fail("Failed to add a large property grid toolstrip button, " + ex.ToString()); + } + } + + /// + /// + bool IComPropertyBrowser.EnsurePendingChangesCommitted() { + + // The commits sometimes cause transactions to open + // and close, which will cause refreshes, which we want to ignore. + // See ASURT 71390. + // + try { + + if (this.designerHost != null) { + designerHost.TransactionOpened -= new EventHandler(this.OnTransactionOpened); + designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); + } + + return GetPropertyGridView().EnsurePendingChangesCommitted(); + } + finally { + if (this.designerHost != null) { + designerHost.TransactionOpened += new EventHandler(this.OnTransactionOpened); + designerHost.TransactionClosed += new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void ExpandAllGridItems() { + gridView.RecursivelyExpand(peMain, false, true, PropertyGridView.MaxRecurseExpand); + } + + private static Type[] GetCommonTabs(Object[] objs, PropertyTabScope tabScope) { + + if (objs == null || objs.Length == 0) { + return new Type[0]; + } + + Type[] tabTypes = new Type[5]; + int types = 0; + int i,j,k; + PropertyTabAttribute tabAttr = (PropertyTabAttribute) TypeDescriptor.GetAttributes(objs[0])[typeof(PropertyTabAttribute)]; + + if (tabAttr == null) { + return new Type[0]; + } + + // filter out all the types of the current scope + for (i = 0; i < tabAttr.TabScopes.Length; i++) { + PropertyTabScope item = tabAttr.TabScopes[i]; + + if (item == tabScope) { + if (types == tabTypes.Length) { + Type[] newTabs = new Type[types * 2]; + Array.Copy(tabTypes, 0, newTabs, 0, types); + tabTypes = newTabs; + } + tabTypes[types++] = tabAttr.TabClasses[i]; + } + } + + if (types == 0) { + return new Type[0]; + } + + bool found; + + for (i = 1; i < objs.Length && types > 0; i++) { + + // get the tab attribute + tabAttr = (PropertyTabAttribute) TypeDescriptor.GetAttributes(objs[i])[typeof(PropertyTabAttribute)]; + + if (tabAttr == null) { + // if this guy has no tabs at all, we can fail right now + return new Type[0]; + } + + // make sure this guy has all the items in the array, + // if not, remove the items he doesn't have + for (j = 0; j < types; j++) { + found = false; + for (k = 0; k < tabAttr.TabClasses.Length; k++) { + if (tabAttr.TabClasses[k] == tabTypes[j]) { + found = true; + break; + } + } + + // if we didn't find an item, remove it from the list + if (!found) { + // swap in with the last item and decrement + tabTypes[j] = tabTypes[types-1]; + tabTypes[types-1] = null; + types--; + + // recheck this item since we'll be ending sooner + j--; + } + } + } + + Type[] returnTypes = new Type[types]; + if (types > 0) { + Array.Copy(tabTypes, 0, returnTypes, 0, types); + } + return returnTypes; + } + + internal GridEntry GetDefaultGridEntry() { + if (peDefault == null && currentPropEntries != null) { + peDefault = (GridEntry)currentPropEntries[0]; + } + return peDefault; + } + + /// + /// Gets the element from point. + /// + /// The point where to search the element. + /// The element found in the current point. + internal Control GetElementFromPoint(Point point) { + if (ToolbarAccessibleObject.Bounds.Contains(point)) { + return toolStrip; + } + + if (GridViewAccessibleObject.Bounds.Contains(point)) { + return gridView; + } + + if (HotCommandsAccessibleObject.Bounds.Contains(point)) { + return hotcommands; + } + + if (HelpAccessibleObject.Bounds.Contains(point)) { + return doccomment; + } + + return null; + } + + private object GetUnwrappedObject(int index) { + if (currentObjects == null || index < 0 || index > currentObjects.Length) { + return null; + } + + Object obj = currentObjects[index]; + if (obj is ICustomTypeDescriptor) { + obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(null); + } + return obj; + } + + internal GridEntryCollection GetPropEntries() { + + if (currentPropEntries == null) { + UpdateSelection(); + } + SetFlag(PropertiesChanged, false); + return currentPropEntries; + } + + + private PropertyGridView GetPropertyGridView() { + return gridView; + } + + + /// + /// + void IComPropertyBrowser.HandleF4() { + + if (gridView.ContainsFocus) { + return; + } + + if (this.ActiveControl != gridView) { + this.SetActiveControlInternal(gridView); + } + gridView.FocusInternal(); + } + + internal bool HavePropEntriesChanged() { + return GetFlag(PropertiesChanged); + } + + + /// + /// + void IComPropertyBrowser.LoadState(RegistryKey optRoot) { + if (optRoot != null) { + Object val = optRoot.GetValue("PbrsAlpha", "0"); + + if (val != null && val.ToString().Equals("1")) { + this.PropertySort = PropertySort.Alphabetical; + } + else { + this.PropertySort = PropertySort.Categorized | PropertySort.Alphabetical; + } + + val = optRoot.GetValue("PbrsShowDesc", "1"); + this.HelpVisible = (val != null && val.ToString().Equals("1")); + + val = optRoot.GetValue("PbrsShowCommands", "0"); + this.CommandsVisibleIfAvailable = (val != null && val.ToString().Equals("1")); + + + val = optRoot.GetValue("PbrsDescHeightRatio", "-1"); + + bool update = false; + if (val is string) { + int ratio = Int32.Parse((string)val, CultureInfo.InvariantCulture); + if (ratio > 0) { + dcSizeRatio = ratio; + update = true; + } + } + + val = optRoot.GetValue("PbrsHotCommandHeightRatio", "-1"); + if (val is string) { + int ratio = Int32.Parse((string)val, CultureInfo.InvariantCulture); + if (ratio > 0) { + dcSizeRatio = ratio; + update = true; + } + } + + if (update) { + OnLayoutInternal(false); + } + } + else { + // apply the same defaults from above. + // + this.PropertySort = PropertySort.Categorized | PropertySort.Alphabetical; + this.HelpVisible = true; + this.CommandsVisibleIfAvailable = false; + } + } + + // when the active document is changed, check all the components so see if they + // are offering up any new tabs + private void OnActiveDesignerChanged(Object sender, ActiveDesignerEventArgs e) { + + if (e.OldDesigner != null && e.OldDesigner == designerHost) { + this.ActiveDesigner = null; + } + + if (e.NewDesigner != null && e.NewDesigner != designerHost) { + this.ActiveDesigner = e.NewDesigner; + } + } + + /// + /// + /// + /// Called when a property on an Ole32 Object changes. + /// See IPropertyNotifySink::OnChanged + /// + void UnsafeNativeMethods.IPropertyNotifySink.OnChanged(int dispID) { + // we don't want the grid's own property sets doing this, but if we're getting + // an OnChanged that isn't the DispID of the property we're currently changing, + // we need to cause a refresh. + // + // + bool fullRefresh = false; + PropertyDescriptorGridEntry selectedEntry = gridView.SelectedGridEntry as PropertyDescriptorGridEntry; + if (selectedEntry != null && selectedEntry.PropertyDescriptor != null && selectedEntry.PropertyDescriptor.Attributes != null) { + + // fish out the DispIdAttribute which will tell us the DispId of the + // property that we're changing. + // + DispIdAttribute dispIdAttr = (DispIdAttribute)selectedEntry.PropertyDescriptor.Attributes[(typeof(DispIdAttribute))]; + if (dispIdAttr != null && !dispIdAttr.IsDefaultAttribute()) { + fullRefresh = (dispID != dispIdAttr.Value); + } + } + + if (!GetFlag(RefreshingProperties)) { + if (!gridView.GetInPropertySet() || fullRefresh) { + Refresh(fullRefresh); + } + + // this is so changes to names of native + // objects will be reflected in the combo box + Object obj = GetUnwrappedObject(0); + if (ComNativeDescriptor.Instance.IsNameDispId(obj, dispID) || dispID == NativeMethods.ActiveX.DISPID_Name) { + OnComComponentNameChanged(new ComponentRenameEventArgs(obj, null, TypeDescriptor.GetClassName(obj))); + } + } + } + + /// + /// We forward messages from several of our children + /// to our mouse move so we can put up the spliter over their borders + /// + private void OnChildMouseMove(Object sender, MouseEventArgs me) { + Point newPt = Point.Empty; + if (ShouldForwardChildMouseMessage((Control)sender, me, ref newPt)) { + // forward the message + this.OnMouseMove(new MouseEventArgs(me.Button, me.Clicks, newPt.X, newPt.Y, me.Delta)); + return; + } + } + + /// + /// We forward messages from several of our children + /// to our mouse move so we can put up the spliter over their borders + /// + private void OnChildMouseDown(Object sender, MouseEventArgs me) { + Point newPt = Point.Empty; + + if (ShouldForwardChildMouseMessage((Control)sender, me, ref newPt)) { + // forward the message + this.OnMouseDown(new MouseEventArgs(me.Button, me.Clicks, newPt.X, newPt.Y, me.Delta)); + return; + } + } + + private void OnComponentAdd(Object sender, ComponentEventArgs e) { + + PropertyTabAttribute attribute = (PropertyTabAttribute) TypeDescriptor.GetAttributes(e.Component.GetType())[typeof(PropertyTabAttribute)]; + + if (attribute == null) { + return; + } + + // add all the document items + for (int i=0; i < attribute.TabClasses.Length; i++) { + if (attribute.TabScopes[i] == PropertyTabScope.Document) { + AddRefTab(attribute.TabClasses[i], e.Component, PropertyTabScope.Document, true); + } + } + } + + private void OnComponentChanged(Object sender, ComponentChangedEventArgs e) { + bool batchMode = GetFlag(BatchMode); + if (batchMode || GetFlag(InternalChange) || gridView.GetInPropertySet() || + (currentObjects == null) || (currentObjects.Length == 0)) { + + if (batchMode && !gridView.GetInPropertySet()) { + SetFlag(BatchModeChange, true); + } + return; + } + + int objectCount = currentObjects.Length; + for (int i = 0; i < objectCount; i++) { + if (currentObjects[i] == e.Component) { + Refresh(false); + break; + } + } + } + + private void OnComponentRemove(Object sender, ComponentEventArgs e) { + + PropertyTabAttribute attribute = (PropertyTabAttribute) TypeDescriptor.GetAttributes(e.Component.GetType())[typeof(PropertyTabAttribute)]; + + if (attribute == null) { + return; + } + + // remove all the document items + for (int i=0; i < attribute.TabClasses.Length; i++) { + if (attribute.TabScopes[i] == PropertyTabScope.Document) { + ReleaseTab(attribute.TabClasses[i], e.Component); + } + } + + for (int i = 0; i < currentObjects.Length; i++) { + if (e.Component == currentObjects[i]) { + + object[] newObjects = new object[currentObjects.Length - 1]; + Array.Copy(currentObjects, 0, newObjects, 0, i); + if (i < newObjects.Length) { + // Dev10 Bug 462203: Array.Copy throws Argument Exception when deleting + // multiple controls with PropertyTabs in designer. + Array.Copy(currentObjects, i + 1, newObjects, i, newObjects.Length - i); + } + + if (!GetFlag(BatchMode)) { + this.SelectedObjects = newObjects; + } + else { + // otherwise, just dump the selection + // + gridView.ClearProps(); + this.currentObjects = newObjects; + SetFlag(FullRefreshAfterBatch, true); + } + } + } + + SetupToolbar(); + + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + Refresh(); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + Refresh(); + } + + /// + /// + internal void OnGridViewMouseWheel(MouseEventArgs e) { + this.OnMouseWheel(e); + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + OnLayoutInternal(false); + TypeDescriptor.Refreshed += new RefreshEventHandler(this.OnTypeDescriptorRefreshed); + if (currentObjects != null && currentObjects.Length > 0) { + Refresh(true); + } + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnHandleDestroyed(EventArgs e) { + TypeDescriptor.Refreshed -= new RefreshEventHandler(this.OnTypeDescriptorRefreshed); + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnGotFocus(EventArgs e) { + + base.OnGotFocus(e); + + if (this.ActiveControl == null) { + this.SetActiveControlInternal(gridView); + } + else { + // sometimes the edit is still the active control + // when it's hidden or disabled... + if (!this.ActiveControl.FocusInternal()) { + this.SetActiveControlInternal(gridView); + } + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + int sx = (int)Math.Round(Left * dx); + int sy = (int)Math.Round(Top * dy); + int sw = Width; + sw = (int)Math.Round((Left + Width) * dx - sx); + int sh = Height; + sh = (int)Math.Round((Top + Height) * dy - sy); + SetBounds(sx, sy, sw, sh, BoundsSpecified.All); + } + + private void OnLayoutInternal(bool dividerOnly) { + + if (!IsHandleCreated || !this.Visible) { + return; + } + + try { + + this.FreezePainting = true; + + if (!dividerOnly) { + // no toolbar or doc comment or commands, just + // fill the whole thing with the grid + if (!toolStrip.Visible && !doccomment.Visible && !hotcommands.Visible) { + gridView.Location = new Point(0,0); + gridView.Size = Size; + return; + } + + if (toolStrip.Visible) { + + int toolStripWidth = this.Width; + int toolStripHeight = ((LargeButtons) ? largeButtonSize : normalButtonSize).Height + toolStripButtonPaddingY; + Rectangle toolStripBounds = new Rectangle(0,1,toolStripWidth, toolStripHeight); + toolStrip.Bounds = toolStripBounds; + + int oldY = gridView.Location.Y; + gridView.Location = new Point(0, toolStrip.Height + toolStrip.Top); + /*if (oldY < gridView.Location.Y) { + // since the toolbar doesn't erase it's + // background, we'll have to force it to happen here. + Brush b = new SolidBrush(BackColor); + Graphics g = toolbar.CreateGraphicsInternal(); + g.FillRectangle(b, toolbar.ClientRectangle); + b.Dispose(); + g.Dispose(); + toolbar.Invalidate(); + }*/ + } + else { + gridView.Location = new Point(0, 0); + } + } + + // now work up from the bottom + int endSize = Size.Height; + + if (endSize < MIN_GRID_HEIGHT) { + return; + } + + int maxSpace = endSize - (gridView.Location.Y + MIN_GRID_HEIGHT); + int height; + + // if we're just moving the divider, set the requested heights + int dcRequestedHeight = 0; + int hcRequestedHeight = 0; + int dcOptHeight = 0; + int hcOptHeight = 0; + + if (dividerOnly) { + dcRequestedHeight = doccomment.Visible ? doccomment.Size.Height : 0; + hcRequestedHeight = hotcommands.Visible ? hotcommands.Size.Height : 0; + } + else { + if (doccomment.Visible) { + dcOptHeight = doccomment.GetOptimalHeight(Size.Width - cyDivider); + if (doccomment.userSized) { + dcRequestedHeight = doccomment.Size.Height; + } + else if (dcSizeRatio != -1) { + dcRequestedHeight = (this.Height * dcSizeRatio) / 100; + } + else { + dcRequestedHeight = dcOptHeight; + } + } + + if (hotcommands.Visible) { + hcOptHeight = hotcommands.GetOptimalHeight(Size.Width - cyDivider); + if (hotcommands.userSized) { + hcRequestedHeight = hotcommands.Size.Height; + } + else if (hcSizeRatio != -1) { + hcRequestedHeight = (this.Height * hcSizeRatio) / 100; + } + else { + hcRequestedHeight = hcOptHeight; + } + } + } + + // place the help comment window + if (dcRequestedHeight > 0) { + + maxSpace -= cyDivider; + + if (hcRequestedHeight == 0 || (dcRequestedHeight + hcRequestedHeight) < maxSpace) { + // full size + height = Math.Min(dcRequestedHeight, maxSpace); + } + else if (hcRequestedHeight > 0 && hcRequestedHeight < maxSpace) { + // give most of the space to the hot commands + height = maxSpace - hcRequestedHeight; + } + else { + // split the difference + height = Math.Min(dcRequestedHeight, maxSpace / 2 - 1); + } + + height = Math.Max(height, cyDivider * 2); + + doccomment.SetBounds(0, endSize - height, Size.Width, height); + + // if we've modified the height to less than the optimal, clear the userSized item + if (height <= dcOptHeight && height < dcRequestedHeight) { + doccomment.userSized = false; + } + else if (dcSizeRatio != -1 || doccomment.userSized) { + dcSizeRatio = (doccomment.Height * 100) / this.Height; + } + + doccomment.Invalidate(); + endSize = doccomment.Location.Y - cyDivider; + maxSpace -= height; + } + + // place the hot commands + if (hcRequestedHeight > 0) { + maxSpace -= cyDivider; + + + if (maxSpace > hcRequestedHeight) { + // full size + height = Math.Min(hcRequestedHeight, maxSpace); + } + else { + // what's left + height = maxSpace; + } + + height = Math.Max(height, cyDivider * 2); + + // if we've modified the height, clear the userSized item + if (height <= hcOptHeight && height < hcRequestedHeight) { + hotcommands.userSized = false; + } + else if (hcSizeRatio != -1 || hotcommands.userSized) { + hcSizeRatio = (hotcommands.Height * 100) / this.Height; + } + + hotcommands.SetBounds(0, endSize - height, Size.Width, height); + hotcommands.Invalidate(); + endSize = hotcommands.Location.Y - cyDivider; + } + + gridView.Size = new Size(Size.Width, endSize - gridView.Location.Y); + } + finally { + this.FreezePainting = false; + } + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnMouseDown(MouseEventArgs me) { + SnappableControl target = DividerInside(me.X,me.Y); + if (target != null && me.Button == MouseButtons.Left) { + // capture mouse. + CaptureInternal = true; + targetMove = target; + dividerMoveY = me.Y; + DividerDraw(dividerMoveY); + } + base.OnMouseDown(me); + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnMouseMove(MouseEventArgs me) { + + if (dividerMoveY == -1) { + if (DividerInside(me.X,me.Y) != null) { + Cursor = Cursors.HSplit; + } + else { + Cursor = null; + } + return; + } + + int yNew = DividerLimitMove(targetMove, me.Y); + + if (yNew != dividerMoveY) { + DividerDraw(dividerMoveY); + dividerMoveY = yNew; + DividerDraw(dividerMoveY); + } + base.OnMouseMove(me); + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnMouseUp(MouseEventArgs me) { + if (dividerMoveY == -1) + return; + + Cursor = null; + + DividerDraw(dividerMoveY); + dividerMoveY = DividerLimitMove(targetMove, me.Y); + Rectangle rectDoc = targetMove.Bounds; + if (dividerMoveY != rectDoc.Y) { + int yNew = rectDoc.Height + rectDoc.Y - dividerMoveY - (cyDivider / 2); // we subtract two so the mouse is still over the divider + Size size = targetMove.Size; + size.Height = Math.Max(0,yNew); + targetMove.Size = size; + targetMove.userSized = true; + OnLayoutInternal(true); + // invalidate the divider area so we cleanup anything + // left by the xor + Invalidate(new Rectangle(0, me.Y - cyDivider, Size.Width, me.Y + cyDivider)); + + // in case we're doing the top one, we might have wrecked stuff + // on the grid + gridView.Invalidate(new Rectangle(0, gridView.Size.Height - cyDivider, Size.Width, cyDivider)); + } + + // end the move + CaptureInternal = false; + dividerMoveY = -1; + targetMove = null; + base.OnMouseUp(me); + } + + /// + /// + /// + /// Called when a property on an Ole32 Object that is tagged + /// with "requestedit" is about to be edited. + /// See IPropertyNotifySink::OnRequestEdit + /// + int UnsafeNativeMethods.IPropertyNotifySink.OnRequestEdit(int dispID) { + // we don't do anything here... + return NativeMethods.S_OK; + } + + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnResize(EventArgs e) { + if (IsHandleCreated && this.Visible) { + OnLayoutInternal(false); + } + base.OnResize(e); + } + + + + private void OnButtonClick(Object sender, EventArgs e) { + // we don't want to steal focus from the property pages... + if (sender != btnViewPropertyPages) { + gridView.FocusInternal(); + } + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected void OnComComponentNameChanged(ComponentRenameEventArgs e) { + ComponentRenameEventHandler handler = (ComponentRenameEventHandler)Events[EventComComponentNameChanged]; + if (handler != null) handler(this,e); + } + + + /// + /// + /// [To be supplied.] + /// + // Seems safe - doesn't do anything interesting + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected void OnNotifyPropertyValueUIItemsChanged(object sender, EventArgs e) { + gridView.LabelPaintMargin = 0; + gridView.Invalidate(true); + } + + /// + /// + /// [To be supplied.] + /// + // Seems safe - doesn't do anything interesting + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnPaint(PaintEventArgs pevent) { + + // just erase the stuff above and below the properties window + // so we don't flicker. + Point psheetLoc = gridView.Location; + int width = Size.Width; + + Brush background; + if (BackColor.IsSystemColor) { + background = SystemBrushes.FromSystemColor(BackColor); + } + else { + background = new SolidBrush(BackColor); + } + pevent.Graphics.FillRectangle(background, new Rectangle(0,0,width, psheetLoc.Y)); + + int yLast = psheetLoc.Y + gridView.Size.Height; + + // fill above hotcommands + if (hotcommands.Visible) { + pevent.Graphics.FillRectangle(background, new Rectangle(0, yLast, width, hotcommands.Location.Y - yLast)); + yLast += hotcommands.Size.Height; + } + + // fill above doccomment + if (doccomment.Visible) { + pevent.Graphics.FillRectangle(background, new Rectangle(0, yLast, width, doccomment.Location.Y - yLast)); + yLast += doccomment.Size.Height; + } + + // anything that might be left + pevent.Graphics.FillRectangle(background, new Rectangle(0, yLast, width, Size.Height - yLast)); + + if (!BackColor.IsSystemColor) { + background.Dispose(); + } + base.OnPaint(pevent); + + if (lineBrush != null) { + lineBrush.Dispose(); + lineBrush = null; + } + } + + /// + /// + /// [To be supplied.] + /// + // Seems safe - just fires an event + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected virtual void OnPropertySortChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventPropertySortChanged]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + // Seems safe - just fires an event + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected virtual void OnPropertyTabChanged (PropertyTabChangedEventArgs e) { + PropertyTabChangedEventHandler handler = (PropertyTabChangedEventHandler)Events[EventPropertyTabChanged]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + // Seems safe - just fires an event + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected virtual void OnPropertyValueChanged(PropertyValueChangedEventArgs e) { + PropertyValueChangedEventHandler handler = (PropertyValueChangedEventHandler)Events[EventPropertyValueChanged]; + if (handler != null) handler(this,e); + } + + internal void OnPropertyValueSet(GridItem changedItem, object oldValue) { + OnPropertyValueChanged(new PropertyValueChangedEventArgs(changedItem, oldValue)); + + // In Level 3 announce the property value change like standalone combobox control do: "[something] selected". + if (AccessibilityImprovements.Level3 && changedItem != null) { + bool dropDown = false; + Type propertyType = changedItem.PropertyDescriptor.PropertyType; + UITypeEditor editor = (UITypeEditor)TypeDescriptor.GetEditor(propertyType, typeof(UITypeEditor)); + if (editor != null) { + dropDown = editor.GetEditStyle() == UITypeEditorEditStyle.DropDown; + } + else { + var gridEntry = changedItem as GridEntry; + if (gridEntry != null && gridEntry.Enumerable) { + dropDown = true; + } + } + + if (dropDown && !gridView.DropDownVisible) { + this.AccessibilityObject.RaiseAutomationNotification( + Automation.AutomationNotificationKind.ActionCompleted, + Automation.AutomationNotificationProcessing.All, + SR.GetString(SR.PropertyGridPropertyValueSelectedFormat, changedItem.Value)); + } + } + } + + internal void OnSelectedGridItemChanged(GridEntry oldEntry, GridEntry newEntry) { + OnSelectedGridItemChanged(new SelectedGridItemChangedEventArgs(oldEntry, newEntry)); + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnSelectedGridItemChanged(SelectedGridItemChangedEventArgs e) { + SelectedGridItemChangedEventHandler handler = (SelectedGridItemChangedEventHandler)Events[EventSelectedGridItemChanged]; + + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnSelectedObjectsChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventSelectedObjectsChanged]; + if (handler != null) { + handler(this, e); + } + } + + private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) { + if (e.LastTransaction) { + // We should not refresh the grid if the selectedObject is no longer sited. + IComponent currentSelection = SelectedObject as IComponent; + if (currentSelection != null) + { + if (currentSelection.Site == null) //The component is not logically sited...so clear the PropertyGrid Selection.. + { + //Setting to null... actually will clear off the state information so that ProperyGrid is in sane State. + this.SelectedObject = null; + return; + } + } + SetFlag(BatchMode, false); + if (GetFlag(FullRefreshAfterBatch)) { + this.SelectedObjects = currentObjects; + SetFlag(FullRefreshAfterBatch, false); + } + else if (GetFlag(BatchModeChange)){ + Refresh(false); + } + SetFlag(BatchModeChange, false); + } + } + + private void OnTransactionOpened(object sender, EventArgs e) { + SetFlag(BatchMode, true); + } + + private void OnTypeDescriptorRefreshed(RefreshEventArgs e) { + if (InvokeRequired) { + BeginInvoke(new RefreshEventHandler(this.OnTypeDescriptorRefreshedInvoke), new object[] { e }); + } + else { + OnTypeDescriptorRefreshedInvoke(e); + } + } + + private void OnTypeDescriptorRefreshedInvoke(RefreshEventArgs e) { + if (currentObjects != null) { + for (int i = 0; i < currentObjects.Length; i++) { + Type typeChanged = e.TypeChanged; + if (currentObjects[i] == e.ComponentChanged || typeChanged != null && typeChanged.IsAssignableFrom(currentObjects[i].GetType())) { + // clear our property hashes + ClearCachedProps(); + Refresh(true); + return; + } + } + } + } + + private void OnViewSortButtonClick(Object sender, EventArgs e) { + try { + + this.FreezePainting = true; + + // is this tab selected? If so, do nothing. + if (sender == viewSortButtons[selectedViewSort]) { + viewSortButtons[selectedViewSort].Checked = true; + return; + } + + // check new button and uncheck old button. + viewSortButtons[selectedViewSort].Checked = false; + + // find the new button in the list + int index = 0; + for (index = 0; index < viewSortButtons.Length; index++) { + if (viewSortButtons[index] == sender) { + break; + } + } + + selectedViewSort = index; + viewSortButtons[selectedViewSort].Checked = true; + + switch (selectedViewSort) { + case ALPHA: + propertySortValue = PropertySort.Alphabetical; + break; + case CATEGORIES: + propertySortValue = PropertySort.Alphabetical | PropertySort.Categorized; + break; + case NO_SORT: + propertySortValue = PropertySort.NoSort; + break; + } + + OnPropertySortChanged(EventArgs.Empty); + + Refresh(false); + OnLayoutInternal(false); + } + finally { + this.FreezePainting = false; + } + OnButtonClick(sender, e); + + } + + private void OnViewTabButtonClick(Object sender, EventArgs e) { + try { + + this.FreezePainting = true; + SelectViewTabButton((ToolStripButton)sender, true); + OnLayoutInternal(false); + SaveTabSelection(); + } + finally { + this.FreezePainting = false; + } + OnButtonClick(sender, e); + + } + + private void OnViewButtonClickPP(Object sender, EventArgs e) { + + if (btnViewPropertyPages.Enabled && + currentObjects != null && + currentObjects.Length > 0) { + Object baseObject = currentObjects[0]; + Object obj = baseObject; + + bool success = false; + + IUIService uiSvc = (IUIService)GetService(typeof(IUIService)); + + try { + if (uiSvc != null) { + success = uiSvc.ShowComponentEditor(obj, this); + } + else { + try { + ComponentEditor editor = (ComponentEditor)TypeDescriptor.GetEditor(obj, typeof(ComponentEditor)); + if (editor != null) { + if (editor is WindowsFormsComponentEditor) { + success = ((WindowsFormsComponentEditor)editor).EditComponent(null, obj, (IWin32Window)this); + } + else { + success = editor.EditComponent(obj); + } + } + } + catch { + } + } + + if (success) { + + if (baseObject is IComponent && + connectionPointCookies[0] == null) { + + ISite site = ((IComponent)baseObject).Site; + if (site != null) { + IComponentChangeService changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + + if (changeService != null) { + try { + changeService.OnComponentChanging(baseObject, null); + } + catch (CheckoutException coEx) { + if (coEx == CheckoutException.Canceled) { + return; + } + throw coEx; + } + + try { + // Now notify the change service that the change was successful. + // + SetFlag(InternalChange, true); + changeService.OnComponentChanged(baseObject, null, null, null); + } + finally { + SetFlag(InternalChange, false); + } + + } + } + } + gridView.Refresh(); + + } + } + catch (Exception ex) + { + String errString = SR.GetString(SR.ErrorPropertyPageFailed); + if (uiSvc != null) + { + uiSvc.ShowError(ex, errString); + } + else + { + RTLAwareMessageBox.Show(null, errString, SR.GetString(SR.PropertyGridTitle), MessageBoxButtons.OK, MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1, 0); + } + } + } + OnButtonClick(sender, e); + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + if (Visible && IsHandleCreated) { + OnLayoutInternal(false); + SetupToolbar(); + } + } + + /* + + /// + /// Returns the first child control that can take focus + /// + /// + /// Returns null if no control is able to take focus + /// + private Control FirstFocusableChild { + get { + if (toolbar.Visible) { + return toolbar; + } + else if (peMain != null) { + return gridView; + } + else if (hotcommands.Visible) { + return hotcommands; + } + else if (doccomment.Visible) { + return doccomment; + } + return null; + } + } + + + private Control LastFocusableChild { + get { + if (doccomment.Visible) { + return doccomment; + } + else if (hotcommands.Visible) { + return hotcommands; + } + else if (peMain != null) { + return gridView; + } + else if (toolbar.Visible) { + return toolbar; + } + return null; + } + } + + // SECREVIEW: Technically full trust != unmanaged code, therefore this WndProc is at + // lesser permission that all the other members in the class. Practically speaking though + // they are the same - keeping UnmanagedCode to match the base class. + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + protected override bool ProcessDialogKey(Keys keyData) { + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + // are we going forward? + if ((keyData & Keys.Shift) != 0) { + // this is backward + if (!this.ContainsFocus) { + Control lastFocusable = this.LastFocusableChild; + + if (lastFocusable != null) { + lastFocusable.Focus(); + return true; + } + } + } + else { + + // this is going forward + + if (!this.ContainsFocus) { + Control firstFocusable = this.FirstFocusableChild; + + if (firstFocusable != null) { + firstFocusable.Focus(); + return true; + } + } + } + // properties window is already selected + // pass on to parent + bool result = base.ProcessDialogKey(keyData); + + // if we're not hosted in a windows forms thing, just give the parent the focus + if (!result && this.Parent == null) { + int hWndParent = Windows.GetParent(this.Handle); + if (hWndParent != 0) { + Windows.SetFocus(hWndParent); + } + } + return result; + + } + } + */ + + /// + /// + /// Returns the last child control that can take focus + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] + [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")] + protected override bool ProcessDialogKey(Keys keyData) + { + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + if (((keyData & Keys.Control) != 0) || + ((keyData & Keys.Alt) != 0)) { + break; + } + + // are we going forward? + if ((keyData & Keys.Shift) != 0) { + // this is backward + if (hotcommands.Visible && hotcommands.ContainsFocus) { + gridView.ReverseFocus(); + } + else if (gridView.FocusInside) { + if (toolStrip.Visible) { + toolStrip.FocusInternal(); + if (AccessibilityImprovements.Level1) { + // we need to select first ToolStrip item, otherwise, ToolStrip container has the focus + if (toolStrip.Items.Count > 0) { + toolStrip.SelectNextToolStripItem(null, /*forward =*/ true); + } + } + } + else { + return base.ProcessDialogKey(keyData); + } + } + else { + // if we get here and the toolbar has focus, + // it means we're processing normally, so + // pass the focus to the parent + if (toolStrip.Focused || !toolStrip.Visible) { + return base.ProcessDialogKey(keyData); + } + else { + // otherwise, we're processing a message from elsewhere, + // wo we select our bottom guy. + if (hotcommands.Visible) { + hotcommands.Select(false); + } + else if (peMain != null) { + gridView.ReverseFocus(); + } + else if (toolStrip.Visible) { + toolStrip.FocusInternal(); + } + else { + return base.ProcessDialogKey(keyData); + } + } + } + return true; + } + else { + + bool passToParent = false; + + // this is forward + if (toolStrip.Focused) { + // normal stuff, just do the propsheet + if (peMain != null) { + gridView.FocusInternal(); + } + else { + base.ProcessDialogKey(keyData); + } + return true; + } + else if (gridView.FocusInside) { + if (hotcommands.Visible) { + hotcommands.Select(true); + return true; + } + else { + passToParent = true; + } + + } + else if (hotcommands.ContainsFocus) { + passToParent = true; + } + else { + // coming from out side, start with the toolStrip + if (toolStrip.Visible) { + toolStrip.FocusInternal(); + } + else { + gridView.FocusInternal(); + } + } + + // nobody's claimed the focus, pass it on... + if (passToParent) { + // properties window is already selected + // pass on to parent + bool result = base.ProcessDialogKey(keyData); + + // if we're not hosted in a windows forms thing, just give the parent the focus + if (!result && this.Parent == null) { + IntPtr hWndParent = UnsafeNativeMethods.GetParent(new HandleRef(this, Handle)); + if (hWndParent != IntPtr.Zero) { + UnsafeNativeMethods.SetFocus(new HandleRef(null, hWndParent)); + } + } + return result; + } + } + return true; + /* This conflicts with VS tab linking (ASURT # 31433) + case Keys.Prior: // PAGE_UP + if ((keyData & Keys.Control) != 0) { + SelectPriorView(); + return true; + } + break; + case Keys.Next: //PAGE_DOWN + if ((keyData & Keys.Control) != 0) { + SelectNextView(); + return true; + } + break; + */ + + } + return base.ProcessDialogKey(keyData); + } + + /// + /// + /// [To be supplied.] + /// + public override void Refresh() { + if (GetFlag(RefreshingProperties)) { + return; + } + + Refresh(true); + base.Refresh(); + } + + + private void Refresh(bool clearCached) { + + if (Disposing) { + return; + } + + if (GetFlag(RefreshingProperties)) { + return; + } + + try { + this.FreezePainting = true; + SetFlag(RefreshingProperties, true); + + if (clearCached) { + ClearCachedProps(); + } + RefreshProperties(clearCached); + gridView.Refresh(); + DisplayHotCommands(); + } + finally { + this.FreezePainting = false; + SetFlag(RefreshingProperties, false); + } + } + + internal void RefreshProperties(bool clearCached) { + + // Clear our current cache so we can do a full refresh. + if (clearCached && selectedViewTab != -1 && viewTabs != null) { + PropertyTab tab = viewTabs[selectedViewTab]; + if (tab != null && viewTabProps != null) { + string tabName = tab.TabName + propertySortValue.ToString(); + viewTabProps.Remove(tabName); + } + } + + SetFlag(PropertiesChanged, true); + UpdateSelection(); + } + + + /// + /// + /// Refreshes the tabs of the given scope by deleting them and requerying objects and documents + /// for them. + /// + public void RefreshTabs(PropertyTabScope tabScope) { + + if (tabScope < PropertyTabScope.Document) { + throw new ArgumentException(SR.GetString(SR.PropertyGridTabScope)); + } + + RemoveTabs(tabScope, false); + + // check the component level tabs + if (tabScope <= PropertyTabScope.Component) { + if (currentObjects != null && currentObjects.Length > 0) { + // get the subset of PropertyTabs that's common to all objects + Type[] tabTypes = GetCommonTabs(currentObjects, PropertyTabScope.Component); + + for (int i = 0; i < tabTypes.Length; i++) { + for (int j = 0; j < currentObjects.Length; j++) { + AddRefTab(tabTypes[i], currentObjects[j], PropertyTabScope.Component, false); + } + } + } + } + + // check the document level tabs + if (tabScope <= PropertyTabScope.Document && designerHost != null) { + IContainer container = designerHost.Container; + if (container != null) { + ComponentCollection components = container.Components; + if (components != null) { + foreach (IComponent comp in components) { + PropertyTabAttribute attribute = (PropertyTabAttribute) TypeDescriptor.GetAttributes(comp.GetType())[typeof(PropertyTabAttribute)]; + + if (attribute != null) { + for (int j = 0; j < attribute.TabClasses.Length; j++) { + if (attribute.TabScopes[j] == PropertyTabScope.Document) { + AddRefTab(attribute.TabClasses[j], comp, PropertyTabScope.Document, false); + } + } + } + } + } + } + } + + SetupToolbar(); + } + + internal void ReleaseTab(Type tabType, Object component) { + PropertyTab tab = null; + int tabIndex = -1; + for (int i = 0; i < viewTabs.Length; i++) { + if (tabType == viewTabs[i].GetType()) { + tab = viewTabs[i]; + tabIndex = i; + break; + } + } + + if (tab == null) { + //Debug.Fail("How can we release a tab when it isn't here."); + return; + } + + Object[] components = tab.Components; + bool killTab = false; + + try { + int index = -1; + if (components != null) + index = Array.IndexOf(components, component); + + if (index >= 0) { + object[] newComponents = new object[components.Length - 1]; + Array.Copy(components, 0, newComponents, 0, index); + Array.Copy(components, index + 1, newComponents, index, components.Length - index - 1); + components = newComponents; + tab.Components = components; + } + killTab = (components.Length == 0); + } + catch (Exception e) + { + Debug.Fail("Bad Tab. It's going away.", e.ToString()); + killTab = true; + } + + // we don't remove PropertyTabScope.Global tabs here. Our owner has to do that. + if (killTab && viewTabScopes[tabIndex] > PropertyTabScope.Global) { + RemoveTab(tabIndex, false); + } + } + + private void RemoveImage(int index) { + imageList[NORMAL_BUTTONS].Images.RemoveAt(index); + if (imageList[LARGE_BUTTONS] != null) { + imageList[LARGE_BUTTONS].Images.RemoveAt(index); + } + } + + // removes all the tabs with a classification greater than or equal to the specified classification. + // for example, removing PropertyTabScope.Document will remove PropertyTabScope.Document and PropertyTabScope.Component tabs + internal void RemoveTabs(PropertyTabScope classification, bool setupToolbar) { + if (classification == PropertyTabScope.Static) { + throw new ArgumentException(SR.GetString(SR.PropertyGridRemoveStaticTabs)); + } + + // in case we've been disposed + if (viewTabButtons == null || viewTabs == null || viewTabScopes == null) { + return; + } + + ToolStripButton selectedButton = (selectedViewTab >=0 && selectedViewTab < viewTabButtons.Length ? viewTabButtons[selectedViewTab] : null); + + for (int i = viewTabs.Length-1; i >= 0; i--) { + if (viewTabScopes[i] >= classification) { + + // adjust the selected view tab because we're deleting. + if (selectedViewTab == i) { + selectedViewTab = -1; + } + else if (selectedViewTab > i) { + selectedViewTab--; + } + + PropertyTab[] newTabs = new PropertyTab[viewTabs.Length - 1]; + Array.Copy(viewTabs, 0, newTabs, 0, i); + Array.Copy(viewTabs, i + 1, newTabs, i, viewTabs.Length - i - 1); + viewTabs = newTabs; + + PropertyTabScope[] newTabScopes = new PropertyTabScope[viewTabScopes.Length - 1]; + Array.Copy(viewTabScopes, 0, newTabScopes, 0, i); + Array.Copy(viewTabScopes, i + 1, newTabScopes, i, viewTabScopes.Length - i - 1); + viewTabScopes = newTabScopes; + + viewTabsDirty = true; + } + } + + if (setupToolbar && viewTabsDirty) { + SetupToolbar(); + + Debug.Assert(viewTabs != null && viewTabs.Length > 0, "Holy Moly! We don't have any tabs left!"); + + selectedViewTab = -1; + SelectViewTabButtonDefault(selectedButton); + + // clear the component refs of the tabs + for (int i = 0; i < viewTabs.Length; i++) { + viewTabs[i].Components = new Object[0]; + } + } + } + + internal void RemoveTab(int tabIndex, bool setupToolbar) { + Debug.Assert(viewTabs != null, "Tab array destroyed!"); + + if (tabIndex >= viewTabs.Length || tabIndex < 0) { + throw new ArgumentOutOfRangeException("tabIndex", SR.GetString(SR.PropertyGridBadTabIndex)); + } + + if (viewTabScopes[tabIndex] == PropertyTabScope.Static) { + throw new ArgumentException(SR.GetString(SR.PropertyGridRemoveStaticTabs)); + } + + + if (selectedViewTab == tabIndex) { + selectedViewTab = PROPERTIES; + } + + // Remove this tab from our "last selected" group + // + if (!GetFlag(ReInitTab) && ActiveDesigner != null) { + int hashCode = ActiveDesigner.GetHashCode(); + if (designerSelections != null && designerSelections.ContainsKey(hashCode) && (int)designerSelections[hashCode] == tabIndex) { + designerSelections.Remove(hashCode); + } + } + + ToolStripButton selectedButton = viewTabButtons[selectedViewTab]; + + PropertyTab[] newTabs = new PropertyTab[viewTabs.Length - 1]; + Array.Copy(viewTabs, 0, newTabs, 0, tabIndex); + Array.Copy(viewTabs, tabIndex + 1, newTabs, tabIndex, viewTabs.Length - tabIndex - 1); + viewTabs = newTabs; + + PropertyTabScope[] newTabScopes = new PropertyTabScope[viewTabScopes.Length - 1]; + Array.Copy(viewTabScopes, 0, newTabScopes, 0, tabIndex); + Array.Copy(viewTabScopes, tabIndex + 1, newTabScopes, tabIndex, viewTabScopes.Length - tabIndex - 1); + viewTabScopes = newTabScopes; + + viewTabsDirty = true; + + if (setupToolbar) { + SetupToolbar(); + selectedViewTab = -1; + SelectViewTabButtonDefault(selectedButton); + } + } + + internal void RemoveTab(Type tabType) { + PropertyTab tab = null; + int tabIndex = -1; + for (int i = 0; i < viewTabs.Length; i++) { + if (tabType == viewTabs[i].GetType()) { + tab = viewTabs[i]; + tabIndex = i; + break; + } + } + + // just quit if the tab isn't present. + if (tabIndex == -1) { + return; + } + + PropertyTab[] newTabs = new PropertyTab[viewTabs.Length - 1]; + Array.Copy(viewTabs, 0, newTabs, 0, tabIndex); + Array.Copy(viewTabs, tabIndex + 1, newTabs, tabIndex, viewTabs.Length - tabIndex - 1); + viewTabs = newTabs; + + PropertyTabScope[] newTabScopes = new PropertyTabScope[viewTabScopes.Length - 1]; + Array.Copy(viewTabScopes, 0, newTabScopes, 0, tabIndex); + Array.Copy(viewTabScopes, tabIndex + 1, newTabScopes, tabIndex, viewTabScopes.Length - tabIndex - 1); + viewTabScopes = newTabScopes; + + viewTabsDirty = true; + SetupToolbar(); + } + + private void ResetCommandsBackColor() { + hotcommands.ResetBackColor(); + } + + private void ResetCommandsForeColor() { + hotcommands.ResetForeColor(); + } + + private void ResetCommandsLinkColor() { + hotcommands.Label.ResetLinkColor(); + } + + private void ResetCommandsActiveLinkColor() { + hotcommands.Label.ResetActiveLinkColor(); + } + + private void ResetCommandsDisabledLinkColor() { + hotcommands.Label.ResetDisabledLinkColor(); + } + + private void ResetHelpBackColor() { + doccomment.ResetBackColor(); + } + + private void ResetHelpForeColor() { + doccomment.ResetBackColor(); + } + + // This method is intended for use in replacing a specific selected root object with + // another object of the same exact type. Scenario: An immutable root object being + // replaced with a new instance because one of its properties was changed by the user. + // + internal void ReplaceSelectedObject(object oldObject, object newObject) { + Debug.Assert(oldObject != null && newObject != null && oldObject.GetType() == newObject.GetType()); + + for (int i = 0; i < currentObjects.Length; ++i) { + if (currentObjects[i] == oldObject) { + currentObjects[i] = newObject; + Refresh(true); + break; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void ResetSelectedProperty() { + GetPropertyGridView().Reset(); + } + + private void SaveTabSelection() { + if (designerHost != null) { + if (designerSelections == null) { + designerSelections = new Hashtable(); + } + designerSelections[designerHost.GetHashCode()] = selectedViewTab; + } + } + + /// + /// + void IComPropertyBrowser.SaveState(RegistryKey optRoot) { + + if (optRoot == null) { + return; + } + + optRoot.SetValue("PbrsAlpha", (this.PropertySort == PropertySort.Alphabetical ? "1" : "0")); + optRoot.SetValue("PbrsShowDesc", (this.HelpVisible ? "1" : "0")); + optRoot.SetValue("PbrsShowCommands", (this.CommandsVisibleIfAvailable ? "1" : "0")); + optRoot.SetValue("PbrsDescHeightRatio", dcSizeRatio.ToString(CultureInfo.InvariantCulture)); + optRoot.SetValue("PbrsHotCommandHeightRatio", hcSizeRatio.ToString(CultureInfo.InvariantCulture)); + } + + void SetHotCommandColors(bool vscompat) { + if (vscompat) { + hotcommands.SetColors(SystemColors.Control, SystemColors.ControlText, SystemColors.ActiveCaption, SystemColors.ActiveCaption, SystemColors.ActiveCaption, SystemColors.ControlDark); + } + else { + hotcommands.SetColors(SystemColors.Control, SystemColors.ControlText, Color.Empty, Color.Empty, Color.Empty, Color.Empty); + } + } + + internal void SetStatusBox(string title,string desc) { + doccomment.SetComment(title,desc); + } + + private void SelectViewTabButton(ToolStripButton button, bool updateSelection) { + + Debug.Assert(viewTabButtons != null, "No view tab buttons to select!"); + + int oldTab = selectedViewTab; + + if (!SelectViewTabButtonDefault(button)) { + Debug.Fail("Failed to find the tab!"); + } + + if (updateSelection) { + Refresh(false); + } + } + + private bool SelectViewTabButtonDefault(ToolStripButton button) { + // make sure our selection number is valid + if (selectedViewTab >= 0 && selectedViewTab >= viewTabButtons.Length) { + selectedViewTab = -1; + } + + // is this tab button checked? If so, do nothing. + if (selectedViewTab >=0 && selectedViewTab < viewTabButtons.Length && + button == viewTabButtons[selectedViewTab]) { + viewTabButtons[selectedViewTab].Checked = true; + return true; + } + + PropertyTab oldTab = null; + + // unselect what's selected + if (selectedViewTab != -1) { + viewTabButtons[selectedViewTab].Checked = false; + oldTab = viewTabs[selectedViewTab]; + } + + // get the new index of the button + for (int i = 0; i < viewTabButtons.Length; i++) { + if (viewTabButtons[i] == button) { + selectedViewTab = i; + viewTabButtons[i].Checked = true; + try { + SetFlag(TabsChanging, true); + OnPropertyTabChanged(new PropertyTabChangedEventArgs(oldTab, viewTabs[i])); + } + finally { + SetFlag(TabsChanging, false); + } + return true; + } + } + + // select the first tab if we didn't find that one. + selectedViewTab = PROPERTIES; + Debug.Assert(viewTabs[PROPERTIES].GetType() == DefaultTabType, "First item is not property tab!"); + SelectViewTabButton(viewTabButtons[PROPERTIES], false); + return false; + } + + + + private void SetSelectState(int state) { + + + if (state >= (viewTabs.Length * viewSortButtons.Length)) { + state = 0; + } + else if (state < 0) { + state = (viewTabs.Length * viewSortButtons.Length) - 1; + } + + + // NOTE: See GetSelectState for the full description + // of the state transitions + + // views == 2 (Alpha || Categories) + // viewTabs = viewTabs.length + + // state -> tab = state / views + // state -> view = state % views + + int viewTypes = viewSortButtons.Length; + + if (viewTypes > 0) { + + int tab = state / viewTypes; + int view = state % viewTypes; + + Debug.Assert(tab < viewTabs.Length, "Trying to select invalid tab!"); + Debug.Assert(view < viewSortButtons.Length, "Can't select view type > 1"); + + OnViewTabButtonClick(viewTabButtons[tab], EventArgs.Empty); + OnViewSortButtonClick(viewSortButtons[view], EventArgs.Empty); + } + } + + private void SetToolStripRenderer() { + if (DrawFlatToolbar || (SystemInformation.HighContrast && AccessibilityImprovements.Level1)) { + // use an office look and feel with system colors + ProfessionalColorTable colorTable = new ProfessionalColorTable(); + colorTable.UseSystemColors = true; + ToolStripRenderer = new ToolStripProfessionalRenderer(colorTable); + } + else { + ToolStripRenderer = new ToolStripSystemRenderer(); + } + } + + + + private void SetupToolbar() { + SetupToolbar(false); + } + + private void SetupToolbar(bool fullRebuild) { + + // if the tab array hasn't changed, don't bother to do all + // this work. + // + if (!viewTabsDirty && !fullRebuild) { + return; + } + + try { + this.FreezePainting = true; + + + if (imageList[NORMAL_BUTTONS] == null || fullRebuild) { + imageList[NORMAL_BUTTONS] = new ImageList(); + if (DpiHelper.IsScalingRequired) { + imageList[NORMAL_BUTTONS].ImageSize = normalButtonSize; + } + } + + // setup our event handlers + EventHandler ehViewTab = new EventHandler(this.OnViewTabButtonClick); + EventHandler ehViewType = new EventHandler(this.OnViewSortButtonClick); + EventHandler ehPP = new EventHandler(this.OnViewButtonClickPP); + + Bitmap b; + int i; + + + // we manange the buttons as a seperate list so the toobar doesn't flash + ArrayList buttonList; + + if (fullRebuild) { + buttonList = new ArrayList(); + } + else { + buttonList = new ArrayList(toolStrip.Items); + } + + // setup the view type buttons. We only need to do this once + if (viewSortButtons == null || fullRebuild) { + viewSortButtons = new ToolStripButton[3]; + + int alphaIndex = -1; + int categoryIndex = -1; + + try { + if (bmpAlpha == null) { + bmpAlpha = SortByPropertyImage; + } + alphaIndex = AddImage(bmpAlpha); + } + catch (Exception e) { + Debug.Fail("Failed to load Alpha bitmap", e.ToString()); + } + + try { + if (bmpCategory == null) { + bmpCategory = SortByCategoryImage; + } + categoryIndex = AddImage(bmpCategory); + } + catch (Exception e) { + Debug.Fail("Failed to load category bitmap", e.ToString()); + } + + viewSortButtons[ALPHA] = CreatePushButton(SR.GetString(SR.PBRSToolTipAlphabetic), alphaIndex, ehViewType, true); + viewSortButtons[CATEGORIES] = CreatePushButton(SR.GetString(SR.PBRSToolTipCategorized), categoryIndex, ehViewType, true); + + // we create a dummy hidden button for view sort + viewSortButtons[NO_SORT] = CreatePushButton("", 0, ehViewType, true); + viewSortButtons[NO_SORT].Visible = false; + + // add the viewType buttons and a separator + for (i = 0; i < viewSortButtons.Length; i++) { + buttonList.Add(viewSortButtons[i]); + } + } + else { + // clear all the items from the toolStrip and image list after the first two + int items = buttonList.Count; + + for (i = items-1; i >= 2; i--) { + buttonList.RemoveAt(i); + } + + items = imageList[NORMAL_BUTTONS].Images.Count; + + for (i = items-1; i >= 2; i--) { + RemoveImage(i); + } + } + + buttonList.Add(separator1); + + // here's our buttons array + viewTabButtons = new ToolStripButton[viewTabs.Length]; + bool doAdd = viewTabs.Length > 1; + + // if we've only got the properties tab, don't add + // the button (or we'll just have a properties button that you can't do anything with) + // setup the view tab buttons + for (i = 0; i < viewTabs.Length; i++) { + try { + b = viewTabs[i].Bitmap; + viewTabButtons[i] = CreatePushButton(viewTabs[i].TabName, AddImage(b), ehViewTab, true); + if (doAdd) { + buttonList.Add(viewTabButtons[i]); + } + } + catch (Exception ex) { + Debug.Fail(ex.ToString()); + } + } + + // if we didn't add anything, we don't need another separator either. + if (doAdd) { + buttonList.Add(separator2); + } + + // add the design page button + int designpg = 0; + + try { + if (bmpPropPage == null) { + bmpPropPage = ShowPropertyPageImage; + } + designpg = AddImage(bmpPropPage); + } + catch (Exception e) { + Debug.Fail(e.ToString()); + } + + // we recreate this every time to ensure it's at the end + // + btnViewPropertyPages = CreatePushButton(SR.GetString(SR.PBRSToolTipPropertyPages), designpg, ehPP, false); + btnViewPropertyPages.Enabled = false; + buttonList.Add(btnViewPropertyPages); + + // Dispose this so it will get recreated for any new buttons. + if (imageList[LARGE_BUTTONS] != null) { + imageList[LARGE_BUTTONS].Dispose(); + imageList[LARGE_BUTTONS] = null; + } + + if (buttonType != NORMAL_BUTTONS) { + EnsureLargeButtons(); + } + + toolStrip.ImageList = imageList[this.buttonType]; + + toolStrip.SuspendLayout(); + toolStrip.Items.Clear(); + for (int j = 0; j < buttonList.Count; j++) { + toolStrip.Items.Add(buttonList[j] as ToolStripItem); + } + toolStrip.ResumeLayout(); + + if (viewTabsDirty) { + // if we're redoing our tabs make sure + // we setup the toolbar area correctly. + // + OnLayoutInternal(false); + } + + viewTabsDirty = false; + } + finally { + this.FreezePainting = false; + } + } + + /// + /// + /// [To be supplied.] + /// + protected void ShowEventsButton(bool value) { + if (viewTabs != null && viewTabs.Length > EVENTS && (viewTabs[EVENTS] is EventsTab)) { + + Debug.Assert(viewTabButtons != null && viewTabButtons.Length > EVENTS && viewTabButtons[EVENTS] != null, "Events button is not at EVENTS position"); + viewTabButtons[EVENTS].Visible = value; + if (!value && selectedViewTab == EVENTS) { + SelectViewTabButton(viewTabButtons[PROPERTIES], true); + } + } + + UpdatePropertiesViewTabVisibility(); + } + + /// + /// + /// This 16x16 Bitmap is applied to the button which orders properties alphabetically. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + protected virtual Bitmap SortByPropertyImage { + get { + return new Bitmap(typeof(PropertyGrid), "PBAlpha.bmp"); + } + } + + /// + /// + /// This 16x16 Bitmap is applied to the button which displays properties under the assigned categories. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + protected virtual Bitmap SortByCategoryImage { + get { + return new Bitmap(typeof(PropertyGrid), "PBCatego.bmp"); + } + } + + /// + /// + /// This 16x16 Bitmap is applied to the button which displays property page in the designer pane. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + protected virtual Bitmap ShowPropertyPageImage { + get { + return new Bitmap(typeof(PropertyGrid), "PBPPage.bmp"); + } + } + + private bool ShouldSerializeCommandsBackColor() { + return hotcommands.ShouldSerializeBackColor(); + } + + private bool ShouldSerializeCommandsForeColor() { + return hotcommands.ShouldSerializeForeColor(); + } + + private bool ShouldSerializeCommandsLinkColor() { + return hotcommands.Label.ShouldSerializeLinkColor(); + } + + private bool ShouldSerializeCommandsActiveLinkColor() { + return hotcommands.Label.ShouldSerializeActiveLinkColor(); + } + + private bool ShouldSerializeCommandsDisabledLinkColor() { + return hotcommands.Label.ShouldSerializeDisabledLinkColor(); + } + + /// + /// Sinks the property notify events on all the objects we are currently + /// browsing. + /// + /// See IPropertyNotifySink + /// + private void SinkPropertyNotifyEvents() { + // first clear any existing sinks. + for (int i = 0;connectionPointCookies != null && i < connectionPointCookies.Length; i++) { + if (connectionPointCookies[i] != null) { + connectionPointCookies[i].Disconnect(); + connectionPointCookies[i] = null; + } + } + + if (currentObjects == null || currentObjects.Length == 0) { + connectionPointCookies = null; + return; + } + + // it's okay if our array is too big...we'll just reuse it and ignore the empty slots. + if (connectionPointCookies == null || (currentObjects.Length > connectionPointCookies.Length)) { + connectionPointCookies = new AxHost.ConnectionPointCookie[currentObjects.Length]; + } + + for (int i = 0; i < currentObjects.Length; i++) { + try { + Object obj = GetUnwrappedObject(i); + + if (!Marshal.IsComObject(obj)) { + continue; + } + connectionPointCookies[i] = new AxHost.ConnectionPointCookie(obj, this, typeof(UnsafeNativeMethods.IPropertyNotifySink), /*throwException*/ false); + } + catch { + // guess we failed eh? + } + } + } + + private bool ShouldForwardChildMouseMessage(Control child, MouseEventArgs me, ref Point pt) { + + Size size = child.Size; + + // are we within two pixels of the edge? + if (me.Y <= 1 || (size.Height - me.Y) <= 1) { + // convert the coordinates to + NativeMethods.POINT temp = new NativeMethods.POINT(); + temp.x = me.X; + temp.y = me.Y; + UnsafeNativeMethods.MapWindowPoints(new HandleRef(child, child.Handle), new HandleRef(this, Handle), temp, 1); + + // forward the message + pt.X = temp.x; + pt.Y = temp.y; + return true; + } + return false; + } + + private void UpdatePropertiesViewTabVisibility() { + // If the only view available is properties-view, there's no need to show the button. + // + if (viewTabButtons != null) { + int nOtherViewsVisible = 0; + for(int i=1; i < viewTabButtons.Length; i++) { // Starts at index 1, since index 0 is properties-view + if (viewTabButtons[i].Visible) { + nOtherViewsVisible++; + } + } + if (nOtherViewsVisible > 0) { + viewTabButtons[PROPERTIES].Visible = true; + separator2.Visible = true; + } + else { + viewTabButtons[PROPERTIES].Visible = false; + separator2.Visible = false; + } + } + } + + internal void UpdateSelection() { + + if (!GetFlag(PropertiesChanged)) { + return; + } + + if (viewTabs == null) { + return; + } + + string tabName = viewTabs[selectedViewTab].TabName + propertySortValue.ToString(); + + if (viewTabProps != null && viewTabProps.ContainsKey(tabName)) { + peMain = (GridEntry)viewTabProps[tabName]; + if (peMain != null) { + peMain.Refresh(); + } + } + else { + if (currentObjects != null && currentObjects.Length > 0) { + peMain = (GridEntry)GridEntry.Create(gridView, currentObjects, new PropertyGridServiceProvider(this), designerHost, this.SelectedTab, propertySortValue); + } + else { + peMain = null; + } + + if (peMain == null) { + currentPropEntries = new GridEntryCollection(null, new GridEntry[0]); + gridView.ClearProps(); + return; + } + + if (BrowsableAttributes != null) { + peMain.BrowsableAttributes = BrowsableAttributes; + } + + if (viewTabProps == null) { + viewTabProps = new Hashtable(); + } + + viewTabProps[tabName] = peMain; + } + + // get entries. + currentPropEntries = peMain.Children; + peDefault = peMain.DefaultChild; + gridView.Invalidate(); + } + + /// + /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.UseCompatibleTextRenderingDescr) + ] + public bool UseCompatibleTextRendering { + get{ + return base.UseCompatibleTextRenderingInt; + } + set{ + base.UseCompatibleTextRenderingInt = value; + doccomment.UpdateTextRenderingEngine(); + gridView.Invalidate(); + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; + } + } + + /// + /// Determines whether the control supports rendering text using GDI+ and GDI. + /// This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same + /// value if the child control supports it. + /// + internal override bool SupportsUseCompatibleTextRendering { + get { + return true; + } + } + + internal override bool AllowsKeyboardToolTip() { + return false; + } + + // a mini version of process dialog key + // for responding to WM_GETDLGCODE + internal bool WantsTab(bool forward) { + if (forward) { + return toolStrip.Visible && toolStrip.Focused; + } + else { + return gridView.ContainsFocus && toolStrip.Visible; + } + } + + private string propName; + private int dwMsg; + + private void GetDataFromCopyData(IntPtr lparam) { + NativeMethods.COPYDATASTRUCT cds = (NativeMethods.COPYDATASTRUCT)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.COPYDATASTRUCT)); + + if (cds != null && cds.lpData != IntPtr.Zero) { + propName = Marshal.PtrToStringAuto(cds.lpData); + dwMsg = cds.dwData; + } + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected override void OnSystemColorsChanged(EventArgs e) { + // refresh the toolbar buttons + SetupToolbar(true); + + // this doesn't stick the first time we do it... + // either probably a toolbar issue, maybe GDI+, so we call it again + // fortunately this doesn't happen very often. + // + if (!GetFlag(SysColorChangeRefresh)) { + SetupToolbar(true); + SetFlag(SysColorChangeRefresh, true); + } + base.OnSystemColorsChanged(e); + } + + /// + /// Rescaling constants. + /// + private void RescaleConstants() { + normalButtonSize = LogicalToDeviceUnits(DEFAULT_NORMAL_BUTTON_SIZE); + largeButtonSize = LogicalToDeviceUnits(DEFAULT_LARGE_BUTTON_SIZE); + cyDivider = LogicalToDeviceUnits(CYDIVIDER); + toolStripButtonPaddingY = LogicalToDeviceUnits(TOOLSTRIP_BUTTON_PADDING_Y); + + if (hotcommands != null && hotcommands.Visible) { + // Require a fontchange notification + this.Controls.Remove(hotcommands); + this.Controls.Add(hotcommands); + } + } + + /// + /// Rescale constants when DPI changed + /// + /// old dpi + /// new dpi + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + RescaleConstants(); + SetupToolbar(true); + } + } + + /// + /// + /// [To be supplied.] + /// + // SECREVIEW: Technically full trust != unmanaged code, therefore this WndProc is at + // lesser permission that all the other members in the class. Practically speaking though + // they are the same - keeping UnmanagedCode to match the base class. + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_UNDO: + if ((long)m.LParam == 0) { + gridView.DoUndoCommand(); + } + else { + m.Result = CanUndo ? (IntPtr)1 : (IntPtr)0; + } + return; + case NativeMethods.WM_CUT: + if ((long)m.LParam == 0) { + gridView.DoCutCommand(); + } + else { + m.Result = CanCut ? (IntPtr)1 : (IntPtr)0; + } + return; + + case NativeMethods.WM_COPY: + if ((long)m.LParam == 0) { + gridView.DoCopyCommand(); + } + else { + m.Result = CanCopy ? (IntPtr)1 : (IntPtr)0; + } + return; + + case NativeMethods.WM_PASTE: + if ((long)m.LParam == 0) { + gridView.DoPasteCommand(); + } + else { + m.Result = CanPaste ? (IntPtr)1 : (IntPtr)0; + } + return; + + case NativeMethods.WM_COPYDATA: + GetDataFromCopyData(m.LParam); + m.Result = (IntPtr)1; + return; + case AutomationMessages.PGM_GETBUTTONCOUNT: + if (toolStrip != null) { + m.Result = (IntPtr)toolStrip.Items.Count; + return; + } + break; + case AutomationMessages.PGM_GETBUTTONSTATE: + if (toolStrip != null) { + int index = unchecked((int)(long)m.WParam); + if( index >= 0 && index < toolStrip.Items.Count ) { + ToolStripButton button = toolStrip.Items[index] as ToolStripButton; + if (button != null) { + m.Result = (IntPtr)(button.Checked ? 1 : 0); + } + else { + m.Result = IntPtr.Zero; + } + } + return; + } + break; + case AutomationMessages.PGM_SETBUTTONSTATE: + if (toolStrip != null) { + int index = unchecked((int)(long)m.WParam); + if( index >= 0 && index < toolStrip.Items.Count ) { + ToolStripButton button = toolStrip.Items[index] as ToolStripButton; + + if (button != null) { + button.Checked = !button.Checked; + // special treatment for the properies page button + if (button == btnViewPropertyPages) { + OnViewButtonClickPP(button, EventArgs.Empty); + } + else { + switch (unchecked((int)(long)m.WParam)) { + case ALPHA: + case CATEGORIES: + OnViewSortButtonClick(button, EventArgs.Empty); + break; + default: + SelectViewTabButton(button, true); + break; + } + } + } + } + return; + } + break; + + case AutomationMessages.PGM_GETBUTTONTEXT: + case AutomationMessages.PGM_GETBUTTONTOOLTIPTEXT: + if (toolStrip != null) { + int index = unchecked((int)(long)m.WParam); + if( index >= 0 && index < toolStrip.Items.Count ) { + string text = ""; + if (m.Msg == AutomationMessages.PGM_GETBUTTONTEXT) { + text = toolStrip.Items[index].Text; + } + else { + text = toolStrip.Items[index].ToolTipText; + } + + // write text into test file. + m.Result = AutomationMessages.WriteAutomationText(text); + } + return; + } + break; + + case AutomationMessages.PGM_GETTESTINGINFO: { + // Get "testing info" string for Nth grid entry (or active entry if N < 0) + string testingInfo = gridView.GetTestingInfo(unchecked((int) (long) m.WParam)); + m.Result = AutomationMessages.WriteAutomationText(testingInfo); + return; + } + + case AutomationMessages.PGM_GETROWCOORDS: + if (m.Msg == this.dwMsg) { + m.Result = (IntPtr) gridView.GetPropertyLocation(propName, m.LParam == IntPtr.Zero, m.WParam == IntPtr.Zero); + return; + } + break; + case AutomationMessages.PGM_GETSELECTEDROW: + case AutomationMessages.PGM_GETVISIBLEROWCOUNT: + m.Result = gridView.SendMessage(m.Msg, m.WParam, m.LParam); + return; + case AutomationMessages.PGM_SETSELECTEDTAB: + if( m.LParam != IntPtr.Zero ) { + string tabTypeName = AutomationMessages.ReadAutomationText(m.LParam); + + for (int i = 0; i < viewTabs.Length;i++) { + if (viewTabs[i].GetType().FullName == tabTypeName && viewTabButtons[i].Visible) { + SelectViewTabButtonDefault(viewTabButtons[i]); + m.Result = (IntPtr)1; + break; + } + } + } + m.Result = (IntPtr)0; + return; + } + + base.WndProc(ref m); + } + + internal abstract class SnappableControl : Control { + private Color borderColor = SystemColors.ControlDark; + protected PropertyGrid ownerGrid; + internal bool userSized = false; + + public abstract int GetOptimalHeight(int width); + public abstract int SnapHeightRequest(int request); + + public SnappableControl(PropertyGrid ownerGrid) { + this.ownerGrid = ownerGrid; + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + } + + public override Cursor Cursor { + get { + return Cursors.Default; + } + set { + base.Cursor = value; + } + } + + + protected override void OnControlAdded(ControlEventArgs ce) { + //ce.Control.MouseEnter += new EventHandler(this.OnChildMouseEnter); + } + + /* + private void OnChildMouseEnter(object sender, EventArgs e) { + if (sender is Control) { + ((Control)sender).Cursor = Cursors.Default; + } + } + */ + + public Color BorderColor { + get { + return borderColor; + } + set { + borderColor = value; + } + } + + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + Rectangle r = this.ClientRectangle; + r.Width --; + r.Height--; + using (Pen borderPen = new Pen(BorderColor, 1)) { + e.Graphics.DrawRectangle(borderPen, r); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class PropertyTabCollection : ICollection { + + /// + /// [To be supplied.] + /// + internal static PropertyTabCollection Empty = new PropertyTabCollection(null); + + private PropertyGrid owner; + + internal PropertyTabCollection(PropertyGrid owner) { + this.owner = owner; + } + + /// + /// + /// Retrieves the number of member attributes. + /// + public int Count { + get { + if (owner == null) { + return 0; + } + return owner.viewTabs.Length; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + /// Retrieves the member attribute with the specified index. + /// + public PropertyTab this[int index] { + get { + if (owner == null) { + throw new InvalidOperationException(SR.GetString(SR.PropertyGridPropertyTabCollectionReadOnly)); + } + return owner.viewTabs[index]; + } + } + + /// + /// + /// [To be supplied.] + /// + public void AddTabType(Type propertyTabType) { + if (owner == null) { + throw new InvalidOperationException(SR.GetString(SR.PropertyGridPropertyTabCollectionReadOnly)); + } + owner.AddTab(propertyTabType, PropertyTabScope.Global); + } + + /// + /// + /// [To be supplied.] + /// + public void AddTabType(Type propertyTabType, PropertyTabScope tabScope) { + if (owner == null) { + throw new InvalidOperationException(SR.GetString(SR.PropertyGridPropertyTabCollectionReadOnly)); + } + owner.AddTab(propertyTabType, tabScope); + } + + /// + /// + /// Clears the tabs of the given scope or smaller. + /// tabScope must be PropertyTabScope.Component or PropertyTabScope.Document. + /// + public void Clear(PropertyTabScope tabScope) { + if (owner == null) { + throw new InvalidOperationException(SR.GetString(SR.PropertyGridPropertyTabCollectionReadOnly)); + } + owner.ClearTabs(tabScope); + } + + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (owner == null) { + return; + } + if (owner.viewTabs.Length > 0) { + System.Array.Copy(owner.viewTabs, 0, dest, index, owner.viewTabs.Length); + } + } + /// + /// + /// Creates and retrieves a new enumerator for this collection. + /// + public IEnumerator GetEnumerator() { + if (owner == null) { + return new PropertyTab[0].GetEnumerator(); + } + + return owner.viewTabs.GetEnumerator(); + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveTabType(Type propertyTabType) { + if (owner == null) { + throw new InvalidOperationException(SR.GetString(SR.PropertyGridPropertyTabCollectionReadOnly)); + } + owner.RemoveTab(propertyTabType); + } + + } + + /// + /// An unimplemented interface. What is this? It is an interface that nobody ever + /// implements, of course? Where and why would it be used? Why, to find cross-process + /// remoted objects, of course! If a well-known object comes in from a cross process + /// connection, the remoting layer does contain enough type information to determine + /// if an object implements an interface. It assumes that if you are going to cast + /// an object to an interface that you know what you're doing, and allows the cast, + /// even for objects that DON'T actually implement the interface. The error here + /// is raised later when you make your first call on that interface pointer: you + /// get a remoting exception. + /// + /// This is a big problem for code that does "is" and "as" checks to detect the + /// presence of an interface. We do that all over the place here, so we do a check + /// during parameter validation to see if an object implements IUnimplemented. If it + /// does, we know that what we really have is a lying remoting proxy, and we bail. + /// + private interface IUnimplemented {} + + + internal class SelectedObjectConverter : ReferenceConverter { + public SelectedObjectConverter() : base(typeof(IComponent)) { + } + } + + private class PropertyGridServiceProvider : IServiceProvider { + PropertyGrid owner; + + public PropertyGridServiceProvider(PropertyGrid owner) { + this.owner = owner; + } + + public object GetService(Type serviceType) { + object s = null; + + if (owner.ActiveDesigner != null) { + s = owner.ActiveDesigner.GetService(serviceType); + } + + if (s == null) { + s = owner.gridView.GetService(serviceType); + } + + if (s == null && owner.Site != null) { + s = owner.Site.GetService(serviceType); + } + return s; + } + } + + /// + /// Helper class to support rendering text using either GDI or GDI+. + /// + internal static class MeasureTextHelper{ + public static SizeF MeasureText(PropertyGrid owner, Graphics g, string text, Font font ){ + return MeasureTextSimple(owner, g, text, font, new SizeF(0,0)); + } + + public static SizeF MeasureText(PropertyGrid owner, Graphics g, string text, Font font, int width ){ + return MeasureText(owner, g, text, font, new SizeF(width,999999)); + } + + public static SizeF MeasureTextSimple(PropertyGrid owner, Graphics g, string text, Font font, SizeF size ){ + SizeF bindingSize; + if( owner.UseCompatibleTextRendering ){ + bindingSize = g.MeasureString(text, font, size ); + } + else{ + bindingSize = (SizeF) TextRenderer.MeasureText(g, text, font, Size.Ceiling(size), GetTextRendererFlags() ); + } + + return bindingSize; + } + + public static SizeF MeasureText(PropertyGrid owner, Graphics g, string text, Font font, SizeF size ){ + SizeF bindingSize; + if( owner.UseCompatibleTextRendering ){ + bindingSize = g.MeasureString(text, font, size ); + } + else{ + TextFormatFlags flags = + GetTextRendererFlags() | + TextFormatFlags.LeftAndRightPadding | + TextFormatFlags.WordBreak | + TextFormatFlags.NoFullWidthCharacterBreak; + + bindingSize = (SizeF) TextRenderer.MeasureText(g, text, font, Size.Ceiling(size), flags ); + } + + return bindingSize; + } + + public static TextFormatFlags GetTextRendererFlags(){ + return TextFormatFlags.PreserveGraphicsClipping | + TextFormatFlags.PreserveGraphicsTranslateTransform; + } + } + } + + internal static class AutomationMessages { + private const int WM_USER = NativeMethods.WM_USER; + internal const int PGM_GETBUTTONCOUNT = WM_USER + 0x50; + internal const int PGM_GETBUTTONSTATE = WM_USER + 0x52; + internal const int PGM_SETBUTTONSTATE = WM_USER + 0x51; + internal const int PGM_GETBUTTONTEXT = WM_USER + 0x53; + internal const int PGM_GETBUTTONTOOLTIPTEXT = WM_USER + 0x54; + internal const int PGM_GETROWCOORDS = WM_USER + 0x55; + internal const int PGM_GETVISIBLEROWCOUNT = WM_USER + 0x56; + internal const int PGM_GETSELECTEDROW = WM_USER + 0x57; + internal const int PGM_SETSELECTEDTAB = WM_USER + 0x58; // DO NOT CHANGE THIS : VC uses it! + internal const int PGM_GETTESTINGINFO = WM_USER + 0x59; + + /// + /// Writes the specified text into a temporary file of the form %TEMP%\"Maui.[file id].log", where + /// 'file id' is a unique id that is return by this method. + /// This is to support MAUI interaction with the PropertyGrid control and MAUI should remove the + /// file after used. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static IntPtr WriteAutomationText(string text) + { + IntPtr fileId = IntPtr.Zero; + string fullFileName = GenerateLogFileName(ref fileId); + + if (fullFileName != null) + { + try + { + FileStream fs = new FileStream(fullFileName, FileMode.Create, FileAccess.Write); + StreamWriter sw = new StreamWriter(fs); + sw.WriteLine(text); + sw.Dispose(); + fs.Dispose(); + } + catch + { + fileId = IntPtr.Zero; + } + } + + return fileId; + } + + /// + /// Writes the contents of a test file as text. This file needs to have the following naming convention: + /// %TEMP%\"Maui.[file id].log", where 'file id' is a unique id sent to this window. + /// This is to support MAUI interaction with the PropertyGrid control and MAUI should create/delete this file. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static string ReadAutomationText(IntPtr fileId) + { + Debug.Assert(fileId != IntPtr.Zero, "Invalid file Id"); + + string text = null; + + if (fileId != IntPtr.Zero) + { + string fullFileName = GenerateLogFileName(ref fileId); + Debug.Assert(File.Exists(fullFileName), "Automation log file does not exist"); + + if (File.Exists(fullFileName)) + { + try + { + FileStream fs = new FileStream(fullFileName, FileMode.Open, FileAccess.Read); + StreamReader sr = new StreamReader(fs); + text = sr.ReadToEnd(); + sr.Dispose(); + fs.Dispose(); + } + catch + { + text = null; + } + } + } + + return text; + } + + /// + /// Generate log file from id. + /// + private static string GenerateLogFileName(ref IntPtr fileId) + { + string fullFileName = null; + + string filePath = System.Environment.GetEnvironmentVariable("TEMP"); + Debug.Assert(filePath != null, "Could not get value of the TEMP environment variable"); + + if (filePath != null) + { + if (fileId == IntPtr.Zero) // Create id + { + Random rnd = new Random(DateTime.Now.Millisecond); + fileId = new IntPtr(rnd.Next()); + } + + fullFileName = filePath + "\\Maui" + fileId + ".log"; + } + + return fullFileName; + } + } + + /// + /// Represents the PropertyGrid accessibility object. + /// Is used only in Accessibility Improvements of level3 to show correct accessible hierarchy. + /// + [ComVisible(true)] + internal class PropertyGridAccessibleObject : Control.ControlAccessibleObject { + + private PropertyGrid _owningPropertyGrid; + + /// + /// Initializes new instance of PropertyGridAccessibleObject + /// + /// The PropertyGrid owning control. + public PropertyGridAccessibleObject(PropertyGrid owningPropertyGrid) : base(owningPropertyGrid) { + _owningPropertyGrid = owningPropertyGrid; + } + + /// + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + /// x coordinate of point to check + /// y coordinate of point to check + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + Point clientPoint = _owningPropertyGrid.PointToClient(new Point((int)x, (int)y)); + + var element = _owningPropertyGrid.GetElementFromPoint(clientPoint); + if (element != null) { + return element.AccessibilityObject; + } + + return base.ElementProviderFromPoint(x, y); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch(direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return null; + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetChildFragment(0); + case UnsafeNativeMethods.NavigateDirection.LastChild: + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + break; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Request to return the element in the specified direction regarding the provided child element. + /// + /// The child element regarding which the target element is searched. + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal UnsafeNativeMethods.IRawElementProviderFragment ChildFragmentNavigate(AccessibleObject childFragment, UnsafeNativeMethods.NavigateDirection direction) { + switch(direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return this; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + int fragmentCount = GetChildFragmentCount(); + int childFragmentIndex = GetChildFragmentIndex(childFragment); + int nextChildFragmentIndex = childFragmentIndex + 1; + if (fragmentCount > nextChildFragmentIndex) { + return GetChildFragment(nextChildFragmentIndex); + } + + return null; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + fragmentCount = GetChildFragmentCount(); + childFragmentIndex = GetChildFragmentIndex(childFragment); + if (childFragmentIndex > 0) { + return GetChildFragment(childFragmentIndex - 1); + } + + return null; + } + + return null; + } + + /// + /// Return the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this; + } + } + + /// + /// Gets the accessible child corresponding to the specified index. + /// + /// The child index. + /// The accessible child. + internal AccessibleObject GetChildFragment(int index) { + if (index < 0) { + return null; + } + + if (_owningPropertyGrid.ToolbarVisible) { + if (index == 0) { + return _owningPropertyGrid.ToolbarAccessibleObject; + } + + index--; + } + + if (_owningPropertyGrid.GridViewVisible) { + if (index == 0) { + return _owningPropertyGrid.GridViewAccessibleObject; + } + + index--; + } + + if (_owningPropertyGrid.CommandsVisible) { + if (index == 0) { + return _owningPropertyGrid.HotCommandsAccessibleObject; + } + + index--; + } + + if (_owningPropertyGrid.HelpVisible) { + if (index == 0) { + return _owningPropertyGrid.HelpAccessibleObject; + } + } + + return null; + } + + /// + /// Gets the number of children belonging to an accessible object. + /// + /// The number of children. + internal int GetChildFragmentCount() { + int childCount = 0; + + if (_owningPropertyGrid.ToolbarVisible) { + childCount++; + } + + if (_owningPropertyGrid.GridViewVisible) { + childCount++; + } + + if (_owningPropertyGrid.CommandsVisible) { + childCount++; + } + + if (_owningPropertyGrid.HelpVisible) { + childCount++; + } + + return childCount; + } + + /// + /// Return the element in this fragment which has the keyboard focus, + /// + /// Return the element in this fragment which has the keyboard focus, + /// if any; otherwise return null. + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return GetFocused(); + } + + /// + /// Gets the child control index. + /// + /// The control accessible object which index should be found. + /// The child accessible index or -1 if not found. + internal int GetChildFragmentIndex(AccessibleObject controlAccessibleObject) { + int childFragmentCount = GetChildFragmentCount(); + for (int i = 0; i < childFragmentCount; i++) { + var childFragment = GetChildFragment(i); + if (childFragment == controlAccessibleObject) { + return i; + } + } + + return -1; + } + } + + /// + /// Represents the PropertyGrid inner ToolStrip control. + /// Is used starting with Accessibility Improvements of level 3. + /// + internal class PropertyGridToolStrip : ToolStrip { + + private PropertyGrid _parentPropertyGrid; + + /// + /// Initializes new instance of PropertyGridToolStrip control. + /// + /// The parent PropertyGrid control. + public PropertyGridToolStrip(PropertyGrid parentPropertyGrid) { + _parentPropertyGrid = parentPropertyGrid; + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return true; + } + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object for this control. + protected override AccessibleObject CreateAccessibilityInstance() { + return new PropertyGridToolStripAccessibleObject(this, _parentPropertyGrid); + } + } + + /// + /// Represents the PropertyGridToolStrip control accessibility object. + /// + [ComVisible(true)] + internal class PropertyGridToolStripAccessibleObject : ToolStrip.ToolStripAccessibleObject { + + private PropertyGrid _parentPropertyGrid; + + /// + /// Constructs new instance of PropertyGridToolStripAccessibleObject + /// + /// The PropertyGridToolStrip owning control. + /// The parent PropertyGrid control. + public PropertyGridToolStripAccessibleObject(PropertyGridToolStrip owningPropertyGridToolStrip, PropertyGrid parentPropertyGrid) : base(owningPropertyGridToolStrip) { + _parentPropertyGrid = parentPropertyGrid; + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + var propertyGridAccessibleObject = _parentPropertyGrid.AccessibilityObject as PropertyGridAccessibleObject; + if (propertyGridAccessibleObject != null) { + var navigationTarget = propertyGridAccessibleObject.ChildFragmentNavigate(this, direction); + if (navigationTarget != null) { + return navigationTarget; + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ToolBarControlTypeId; + } else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + + return base.GetPropertyValue(propertyID); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ArrayElementGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ArrayElementGridEntry.cs new file mode 100644 index 000000000..8496220a4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ArrayElementGridEntry.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + using System.Globalization; + + internal class ArrayElementGridEntry : GridEntry { + + protected int index; + + + public ArrayElementGridEntry(PropertyGrid ownerGrid, GridEntry peParent, int index) + : base(ownerGrid, peParent) { + this.index = index; + this.SetFlag(FLAG_RENDER_READONLY, (peParent.Flags & FLAG_RENDER_READONLY) != 0 || peParent.ForceReadOnly); + } + + + public override GridItemType GridItemType { + get { + return GridItemType.ArrayValue; + } + } + + + public override bool IsValueEditable { + get{ + return ParentGridEntry.IsValueEditable; + } + } + + public override string PropertyLabel { + get { + return "[" + index.ToString(CultureInfo.CurrentCulture) + "]"; + } + } + + + + public override Type PropertyType { + get { + return parentPE.PropertyType.GetElementType(); + } + } + + public override object PropertyValue { + get { + object owner = GetValueOwner(); + Debug.Assert(owner is Array, "Owner is not array type!"); + return((Array)owner).GetValue(index); + } + set { + object owner = GetValueOwner(); + Debug.Assert(owner is Array, "Owner is not array type!"); + ((Array)owner).SetValue(value,index); + } + } + + public override bool ShouldRenderReadOnly { + get { + return ParentGridEntry.ShouldRenderReadOnly; + } + } + + /* + /// + /// Checks if the value of the current item can be modified by the user. + /// + /// + /// True if the value can be modified + /// + public override bool CanSetPropertyValue() { + return this.ParentGridEntry.CanSetPropertyValue(); + } + */ + + /* + /// + /// Returns if it's an editable item. An example of a readonly + /// editable item is a collection property -- the property itself + /// can not be modified, but it's value (e.g. it's children) can, so + /// we don't want to draw it as readonly. + /// + /// + /// True if the value associated with this property (e.g. it's children) can be modified even if it's readonly. + /// + public override bool CanSetReadOnlyPropertyValue() { + return this.ParentGridEntry.CanSetReadOnlyPropertyValue(); + }*/ + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/CategoryGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/CategoryGridEntry.cs new file mode 100644 index 000000000..400520150 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/CategoryGridEntry.cs @@ -0,0 +1,296 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +//#define PAINT_CATEGORY_TRIANGLE +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Collections; + using System.Reflection; + + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + internal class CategoryGridEntry : GridEntry { + + internal string name; + private Brush backBrush = null; + private static Hashtable categoryStates = null; + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + public CategoryGridEntry(PropertyGrid ownerGrid, GridEntry peParent,string name, GridEntry[] childGridEntries) + : base(ownerGrid, peParent) { + this.name = name; + +#if DEBUG + for (int n = 0;n < childGridEntries.Length; n++) { + Debug.Assert(childGridEntries[n] != null, "Null item in category subproperty list"); + } +#endif + if (categoryStates == null) { + categoryStates = new Hashtable(); + } + + lock (categoryStates) { + if (!categoryStates.ContainsKey(name)) { + categoryStates.Add(name, true); + } + } + + this.IsExpandable = true; + + for (int i = 0; i < childGridEntries.Length; i++) { + childGridEntries[i].ParentGridEntry = this; + } + + this.ChildCollection = new GridEntryCollection(this, childGridEntries); + + lock (categoryStates) { + this.InternalExpanded = (bool)categoryStates[name]; + } + + this.SetFlag(GridEntry.FLAG_LABEL_BOLD,true); + } + + + /// + /// + /// Returns true if this GridEntry has a value field in the right hand column. + /// + internal override bool HasValue { + get { + return false; + } + } + + protected override void Dispose(bool disposing) { + if (disposing) { + if (backBrush != null) { + backBrush.Dispose(); + backBrush = null; + } + + if (ChildCollection != null) { + ChildCollection = null; + } + } + base.Dispose(disposing); + } + + public override void DisposeChildren() { + + // categories should never dispose + // + return; + } + + + // we don't want this guy participating in property depth. + public override int PropertyDepth { + get { + return base.PropertyDepth - 1; + } + } + + /// + /// Gets the accessibility object for the current category grid entry. + /// + /// + protected override GridEntryAccessibleObject GetAccessibilityObject() { + if (AccessibilityImprovements.Level3) { + return new CategoryGridEntryAccessibleObject(this); + } + + return base.GetAccessibilityObject(); + } + + + protected override Brush GetBackgroundBrush(Graphics g) { + return this.GridEntryHost.GetLineBrush(g); + } + + protected override Color LabelTextColor { + get { + return ownerGrid.CategoryForeColor; + } + } + + public override bool Expandable { + get { + return !GetFlagSet(FL_EXPANDABLE_FAILED); + } + } + + internal override bool InternalExpanded { + set { + base.InternalExpanded = value; + lock (categoryStates) { + categoryStates[this.name] = value; + } + } + } + + public override GridItemType GridItemType { + get { + return GridItemType.Category; + } + } + public override string HelpKeyword { + get { + return null; + } + } + + public override string PropertyLabel { + get { + return name; + } + } + + internal override int PropertyLabelIndent { + get { + // we give an extra pixel for breathing room + // we want to make sure that we return 0 for property depth here instead of + PropertyGridView gridHost = this.GridEntryHost; + + // we call base.PropertyDepth here because we don't want the subratction to happen. + return 1+gridHost.GetOutlineIconSize()+OUTLINE_ICON_PADDING + (base.PropertyDepth * gridHost.GetDefaultOutlineIndent()); + } + } + + public override string GetPropertyTextValue(object o) { + return ""; + } + + public override Type PropertyType { + get { + return typeof(void); + } + } + + /// + /// + /// Gets the owner of the current value. This is usually the value of the + /// root entry, which is the object being browsed + /// + public override object GetChildValueOwner(GridEntry childEntry) { + return ParentGridEntry.GetChildValueOwner(childEntry); + } + + protected override bool CreateChildren(bool diffOldChildren) { + return true; + } + + public override string GetTestingInfo() { + string str = "object = ("; + str += FullLabel; + str += "), Category = (" + this.PropertyLabel + ")"; + return str; + } + + public override void PaintLabel(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, bool selected, bool paintFullLabel) { + + base.PaintLabel(g, rect, clipRect, false, true); + + // now draw the focus rect + if (selected && hasFocus) { + bool bold = ((this.Flags & GridEntry.FLAG_LABEL_BOLD) != 0); + Font font = GetFont(bold); + int labelWidth = GetLabelTextWidth(this.PropertyLabel, g, font); + + int indent = PropertyLabelIndent-2; + Rectangle focusRect = new Rectangle(indent, rect.Y, labelWidth+3, rect.Height-1); + if (SystemInformation.HighContrast && !OwnerGrid.developerOverride && AccessibilityImprovements.Level1) { + // we changed line color to SystemColors.ControlDarkDark in high contrast mode + ControlPaint.DrawFocusRectangle(g, focusRect, SystemColors.ControlText, OwnerGrid.LineColor); + } + else { + ControlPaint.DrawFocusRectangle(g, focusRect); + } + } + + // draw the line along the top + if (parentPE.GetChildIndex(this) > 0) { + using (Pen topLinePen = new System.Drawing.Pen(ownerGrid.CategorySplitterColor, 1)) { + g.DrawLine(topLinePen, rect.X - 1, rect.Y - 1, rect.Width + 2, rect.Y - 1); + } + } + } + + public override void PaintValue(object val, System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, PaintValueFlags paintFlags) { + base.PaintValue(val, g, rect, clipRect, paintFlags & ~PaintValueFlags.DrawSelected); + + // draw the line along the top + if (parentPE.GetChildIndex(this) > 0) { + using (Pen topLinePen = new System.Drawing.Pen(ownerGrid.CategorySplitterColor, 1)) { + g.DrawLine(topLinePen, rect.X - 2, rect.Y - 1, rect.Width + 1, rect.Y - 1); + } + } + } + + internal override bool NotifyChildValue(GridEntry pe, int type) { + return parentPE.NotifyChildValue(pe, type); + } + + /// + /// Defines the Category Grid Entry accessible object that is derived from Grid Entry accessible object. + /// + [Runtime.InteropServices.ComVisible(true)] + internal class CategoryGridEntryAccessibleObject : GridEntryAccessibleObject { + + private CategoryGridEntry _owningCategoryGridEntry; + + /// + /// Initializes new instance of CategoryGridEntryAccessibleObject. + /// + /// The owning Category Grid Entry object. + public CategoryGridEntryAccessibleObject(CategoryGridEntry owningCategoryGridEntry) : base(owningCategoryGridEntry) { + _owningCategoryGridEntry = owningCategoryGridEntry; + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + PropertyGridView.PropertyGridViewAccessibleObject parent = (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + return parent.GetNextCategory(_owningCategoryGridEntry); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return parent.GetPreviousCategory(_owningCategoryGridEntry); + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return parent.GetFirstChildProperty(_owningCategoryGridEntry); + case UnsafeNativeMethods.NavigateDirection.LastChild: + return parent.GetLastChildProperty(_owningCategoryGridEntry); + } + + return base.FragmentNavigate(direction); + } + + public override AccessibleRole Role { + get { + return AccessibleRole.ButtonDropDownGrid; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DocComment.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DocComment.cs new file mode 100644 index 000000000..7d91e8b6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DocComment.cs @@ -0,0 +1,293 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Windows.Forms; + + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms.Layout; + + internal class DocComment : PropertyGrid.SnappableControl { + + private Label m_labelTitle; + private Label m_labelDesc; + private string fullDesc; + + protected int lineHeight; + private bool needUpdateUIWithFont = true; + + protected const int CBORDER = 3; + protected const int CXDEF = 0; + protected const int CYDEF = 59; + protected const int MIN_LINES = 2; + + private int cydef = CYDEF; + private int cBorder = CBORDER; + + internal Rectangle rect = Rectangle.Empty; + + internal DocComment(PropertyGrid owner) : base(owner) { + SuspendLayout(); + m_labelTitle = new Label(); + m_labelTitle.UseMnemonic = false; + m_labelTitle.Cursor = Cursors.Default; + m_labelDesc = new Label(); + m_labelDesc.AutoEllipsis = true; + m_labelDesc.Cursor = Cursors.Default; + + UpdateTextRenderingEngine(); + + Controls.Add(m_labelTitle); + Controls.Add(m_labelDesc); + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + cBorder = LogicalToDeviceUnits(CBORDER); + cydef = LogicalToDeviceUnits(CYDEF); + } + + Size = new Size(CXDEF, cydef); + + this.Text = SR.GetString(SR.PBRSDocCommentPaneTitle); + SetStyle(ControlStyles.Selectable, false); + ResumeLayout(false); + } + + public virtual int Lines { + get { + UpdateUIWithFont(); + return Height/lineHeight; + } + set { + UpdateUIWithFont(); + Size = new Size(Width, 1 + value * lineHeight); + } + } + + public override int GetOptimalHeight(int width) { + UpdateUIWithFont(); + // compute optimal label height as one line only. + int height = m_labelTitle.Size.Height; + + // do this to avoid getting parented to the Parking window. + // + if (this.ownerGrid.IsHandleCreated && !IsHandleCreated) { + CreateControl(); + } + + // compute optimal text height + Graphics g = m_labelDesc.CreateGraphicsInternal(); + SizeF sizef = PropertyGrid.MeasureTextHelper.MeasureText( this.ownerGrid, g, m_labelTitle.Text, Font, width); + Size sz = Size.Ceiling(sizef); + g.Dispose(); + var padding = DpiHelper.EnableDpiChangedHighDpiImprovements ? LogicalToDeviceUnits(2) : 2; + height += (sz.Height * 2) + padding; + return Math.Max(height + 2 * padding, DpiHelper.EnableDpiChangedHighDpiImprovements ? LogicalToDeviceUnits(CYDEF) : CYDEF); + } + + internal virtual void LayoutWindow() { + } + + protected override void OnFontChanged(EventArgs e) { + needUpdateUIWithFont = true; + PerformLayout(); + base.OnFontChanged(e); + } + + protected override void OnLayout(LayoutEventArgs e) { + UpdateUIWithFont(); + SetChildLabelsBounds(); + m_labelDesc.Text = this.fullDesc; + m_labelDesc.AccessibleName = this.fullDesc; // Don't crop the description for accessibility clients + base.OnLayout(e); + } + + protected override void OnResize(EventArgs e) { + Rectangle newRect = ClientRectangle; + if (!rect.IsEmpty && newRect.Width > rect.Width) { + Rectangle rectInvalidate = new Rectangle(rect.Width-1,0,newRect.Width-rect.Width+1,rect.Height); + Invalidate(rectInvalidate); + } + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + lineHeight = (int)Font.Height + LogicalToDeviceUnits(2); + if (ClientRectangle.Width != rect.Width || ClientRectangle.Height != rect.Height) { + m_labelTitle.Location = new Point(cBorder, cBorder); + m_labelDesc.Location = new Point(cBorder, cBorder + lineHeight); + // Labels were explicitly set bounds. resize of parent is not rescaling labels. + SetChildLabelsBounds(); + } + } + + rect = newRect; + base.OnResize(e); + } + + /// + /// Setting child label bounds + /// + private void SetChildLabelsBounds() { + Size size = ClientSize; + + // if the client size is 0, setting this to a negative number + // will force an extra layout. + size.Width = Math.Max(0, size.Width - 2 * cBorder); + size.Height = Math.Max(0, size.Height - 2 * cBorder); + + m_labelTitle.SetBounds(m_labelTitle.Top, + m_labelTitle.Left, + size.Width, + Math.Min(lineHeight, size.Height), + BoundsSpecified.Size); + + m_labelDesc.SetBounds(m_labelDesc.Top, + m_labelDesc.Left, + size.Width, + Math.Max(0, size.Height - lineHeight - (DpiHelper.EnableDpiChangedHighDpiImprovements ? LogicalToDeviceUnits(1) : 1)), + BoundsSpecified.Size); + } + + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + UpdateUIWithFont(); + } + + /// + /// Rescaling constants when DPi of the window changed. + /// + /// old dpi + /// new dpi + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + cBorder = LogicalToDeviceUnits(CBORDER); + cydef = LogicalToDeviceUnits(CYDEF); + } + } + + public virtual void SetComment(string title, string desc) { + if (m_labelDesc.Text != title) { + m_labelTitle.Text = title; + } + + if (desc != fullDesc) { + this.fullDesc = desc; + m_labelDesc.Text = fullDesc; + m_labelDesc.AccessibleName = this.fullDesc; // Don't crop the description for accessibility clients + } + } + + public override int SnapHeightRequest(int cyNew) { + UpdateUIWithFont(); + int lines = Math.Max(MIN_LINES, cyNew/lineHeight); + return 1 + lines*lineHeight; + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object for this control. + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new DocCommentAccessibleObject(this, ownerGrid); + } + + return base.CreateAccessibilityInstance(); + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + internal void UpdateTextRenderingEngine() { + m_labelTitle.UseCompatibleTextRendering = this.ownerGrid.UseCompatibleTextRendering; + m_labelDesc.UseCompatibleTextRendering = this.ownerGrid.UseCompatibleTextRendering; + } + + private void UpdateUIWithFont() { + if (IsHandleCreated && needUpdateUIWithFont) { + + // Some fonts throw because Bold is not a valid option + // for them. Fail gracefully. + try { + m_labelTitle.Font = new Font(Font, FontStyle.Bold); + } + catch { + } + + lineHeight = (int)Font.Height + 2; + m_labelTitle.Location = new Point(cBorder, cBorder); + m_labelDesc.Location = new Point(cBorder, cBorder + lineHeight); + + needUpdateUIWithFont = false; + PerformLayout(); + } + } + } + + /// + /// Represents the DocComment control accessible object. + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class DocCommentAccessibleObject : Control.ControlAccessibleObject { + + private PropertyGrid _parentPropertyGrid; + + /// + /// Initializes new instance of DocCommentAccessibleObject. + /// + /// The owning DocComment control. + /// The parent PropertyGrid control. + public DocCommentAccessibleObject(DocComment owningDocComment, PropertyGrid parentPropertyGrid) : base(owningDocComment) { + _parentPropertyGrid = parentPropertyGrid; + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + var propertyGridAccessibleObject = _parentPropertyGrid.AccessibilityObject as PropertyGridAccessibleObject; + if (propertyGridAccessibleObject != null) { + var navigationTarget = propertyGridAccessibleObject.ChildFragmentNavigate(this, direction); + if (navigationTarget != null) { + return navigationTarget; + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_PaneControlTypeId; + } else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + + return base.GetPropertyValue(propertyID); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs new file mode 100644 index 000000000..3c24e22e0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs @@ -0,0 +1,330 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + using System; + using System.Drawing; + + using System.ComponentModel; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.ButtonInternal; + using Microsoft.Win32; + + internal sealed class DropDownButton : Button { + + private bool useComboBoxTheme = false; + + private bool ignoreMouse; + + public DropDownButton() { + SetStyle(ControlStyles.Selectable, true); + SetAccessibleName(); + } + + + // VSWhidbey 375220 - when the holder is open, we don't fire clicks + // + public bool IgnoreMouse { + + get { + return ignoreMouse; + } + set { + ignoreMouse = value; + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + public bool UseComboBoxTheme { + set { + if (useComboBoxTheme != value) { + useComboBoxTheme = value; + if (AccessibilityImprovements.Level1) { + SetAccessibleName(); + } + Invalidate(); + } + } + } + + protected override void OnClick(EventArgs e) { + if (!IgnoreMouse) { + base.OnClick(e); + } + } + + protected override void OnMouseUp(MouseEventArgs e) { + if (!IgnoreMouse) { + base.OnMouseUp(e); + } + } + + protected override void OnMouseDown(MouseEventArgs e) { + if (!IgnoreMouse) { + base.OnMouseDown(e); + } + } + + + protected override void OnPaint(PaintEventArgs pevent) { + base.OnPaint(pevent); + + if (Application.RenderWithVisualStyles & useComboBoxTheme) { + ComboBoxState cbState = ComboBoxState.Normal; + + if (base.MouseIsDown) { + cbState = ComboBoxState.Pressed; + } + else if (base.MouseIsOver) { + cbState = ComboBoxState.Hot; + } + + Rectangle dropDownButtonRect = new Rectangle(0, 0, Width, Height); + if (cbState == ComboBoxState.Normal) { + pevent.Graphics.FillRectangle(SystemBrushes.Window, dropDownButtonRect); + } + if (!DpiHelper.EnableDpiChangedHighDpiImprovements) { + ComboBoxRenderer.DrawDropDownButton(pevent.Graphics, dropDownButtonRect, cbState); + } + else { + ComboBoxRenderer.DrawDropDownButtonForHandle(pevent.Graphics, dropDownButtonRect, cbState, this.HandleInternal); + } + + if (AccessibilityImprovements.Level1) { + // Redraw focus cues + // For consistency with other PropertyGrid buttons, i.e. those opening system dialogs ("..."), that always show visual cues when focused, + // we need to do the same for this custom button, painted as ComboBox control part (drop-down). + if (Focused) { + dropDownButtonRect.Inflate(-1, -1); + ControlPaint.DrawFocusRectangle(pevent.Graphics, dropDownButtonRect, ForeColor, BackColor); + } + } + } + } + + internal void PerformButtonClick() { + if (Visible && Enabled) { + OnClick(EventArgs.Empty); + } + } + + private void SetAccessibleName() { + if (AccessibilityImprovements.Level1 && useComboBoxTheme) { + this.AccessibleName = SR.GetString(SR.PropertyGridDropDownButtonComboBoxAccessibleName); + } + else { + this.AccessibleName = SR.GetString(SR.PropertyGridDropDownButtonAccessibleName); + } + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object for this control. + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new DropDownButtonAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + internal override ButtonBaseAdapter CreateStandardAdapter() { + return new DropDownButtonAdapter(this); + } + } + + internal class DropDownButtonAdapter : ButtonStandardAdapter { + + internal DropDownButtonAdapter(ButtonBase control) : base(control) {} + + private void DDB_Draw3DBorder(System.Drawing.Graphics g, Rectangle r, bool raised) { + if (Control.BackColor != SystemColors.Control && SystemInformation.HighContrast) { + if (raised) { + Color c = ControlPaint.LightLight(Control.BackColor); + ControlPaint.DrawBorder(g, r, + c, 1, ButtonBorderStyle.Outset, + c, 1, ButtonBorderStyle.Outset, + c, 2, ButtonBorderStyle.Inset, + c, 2, ButtonBorderStyle.Inset); + } + else { + ControlPaint.DrawBorder(g, r, ControlPaint.Dark(Control.BackColor), ButtonBorderStyle.Solid); + } + } + else { + if (raised) { + Color c = ControlPaint.Light(Control.BackColor); + ControlPaint.DrawBorder(g, r, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.Solid, + Control.BackColor, 2, ButtonBorderStyle.Outset, + Control.BackColor, 2, ButtonBorderStyle.Outset); + + Rectangle inside = r; + inside.Offset(1,1); + inside.Width -= 3; + inside.Height -= 3; + c = ControlPaint.LightLight(Control.BackColor); + ControlPaint.DrawBorder(g, inside, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.None); + } + else { + ControlPaint.DrawBorder(g, r, ControlPaint.Dark(Control.BackColor), ButtonBorderStyle.Solid); + } + } + } + + internal override void PaintUp(PaintEventArgs pevent, CheckState state) { + base.PaintUp(pevent, state); + if (!Application.RenderWithVisualStyles) { + DDB_Draw3DBorder(pevent.Graphics, Control.ClientRectangle, true); + } + else { + Color c = SystemColors.Window; + Rectangle rect = Control.ClientRectangle; + rect.Inflate(0, -1); + ControlPaint.DrawBorder(pevent.Graphics, rect, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.None); + } + } + + internal override void DrawImageCore(Graphics graphics, Image image, Rectangle imageBounds, Point imageStart, ButtonBaseAdapter.LayoutData layout) { + if (AccessibilityImprovements.Level3 && IsFilledWithHighlightColor && (Control.MouseIsOver || Control.Focused)) { + ControlPaint.DrawImageReplaceColor(graphics, image, imageBounds, Color.Black, SystemColors.HighlightText); + } + else { + ControlPaint.DrawImageReplaceColor(graphics, image, imageBounds, Color.Black, Control.ForeColor); + } + } + } + + /// + /// Represents the accessibility object for the PropertyGrid DropDown button. + /// This DropDownButtonAccessibleObject is available in Level3 only. + /// + [Runtime.InteropServices.ComVisible(true)] + internal class DropDownButtonAccessibleObject : Control.ControlAccessibleObject { + + private DropDownButton _owningDropDownButton; + private PropertyGridView _owningPropertyGrid; + + /// + /// Constructs the new instance of DropDownButtonAccessibleObject. + /// + /// + public DropDownButtonAccessibleObject(DropDownButton owningDropDownButton) : base(owningDropDownButton) { + _owningDropDownButton = owningDropDownButton; + _owningPropertyGrid = owningDropDownButton.Parent as PropertyGridView; + + UseStdAccessibleObjects(owningDropDownButton.Handle); + } + + public override void DoDefaultAction() { + _owningDropDownButton.PerformButtonClick(); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent && + _owningPropertyGrid.SelectedGridEntry != null && + _owningDropDownButton.Visible) { + return _owningPropertyGrid.SelectedGridEntry?.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.PreviousSibling) { + return _owningPropertyGrid.EditAccessibleObject; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Returns the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningPropertyGrid.AccessibilityObject; + } + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ButtonControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId: + return true; + case NativeMethods.UIA_LegacyIAccessibleRolePropertyId: + return Role; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Indicates whether the specified pattern is supported. + /// + /// The pattern ID. + /// True if specified pattern is supported, otherwise false. + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + return AccessibleRole.PushButton; + } + } + + /// + /// Request that focus is set to this item. + /// The UIAutomation framework will ensure that the UI hosting this fragment is already + /// focused before calling this method, so this method should only update its internal + /// focus state; it should not attempt to give its own HWND the focus, for example. + /// + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs.back b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs.back new file mode 100644 index 000000000..3c24e22e0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/DropDownButton.cs.back @@ -0,0 +1,330 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + using System; + using System.Drawing; + + using System.ComponentModel; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.ButtonInternal; + using Microsoft.Win32; + + internal sealed class DropDownButton : Button { + + private bool useComboBoxTheme = false; + + private bool ignoreMouse; + + public DropDownButton() { + SetStyle(ControlStyles.Selectable, true); + SetAccessibleName(); + } + + + // VSWhidbey 375220 - when the holder is open, we don't fire clicks + // + public bool IgnoreMouse { + + get { + return ignoreMouse; + } + set { + ignoreMouse = value; + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + public bool UseComboBoxTheme { + set { + if (useComboBoxTheme != value) { + useComboBoxTheme = value; + if (AccessibilityImprovements.Level1) { + SetAccessibleName(); + } + Invalidate(); + } + } + } + + protected override void OnClick(EventArgs e) { + if (!IgnoreMouse) { + base.OnClick(e); + } + } + + protected override void OnMouseUp(MouseEventArgs e) { + if (!IgnoreMouse) { + base.OnMouseUp(e); + } + } + + protected override void OnMouseDown(MouseEventArgs e) { + if (!IgnoreMouse) { + base.OnMouseDown(e); + } + } + + + protected override void OnPaint(PaintEventArgs pevent) { + base.OnPaint(pevent); + + if (Application.RenderWithVisualStyles & useComboBoxTheme) { + ComboBoxState cbState = ComboBoxState.Normal; + + if (base.MouseIsDown) { + cbState = ComboBoxState.Pressed; + } + else if (base.MouseIsOver) { + cbState = ComboBoxState.Hot; + } + + Rectangle dropDownButtonRect = new Rectangle(0, 0, Width, Height); + if (cbState == ComboBoxState.Normal) { + pevent.Graphics.FillRectangle(SystemBrushes.Window, dropDownButtonRect); + } + if (!DpiHelper.EnableDpiChangedHighDpiImprovements) { + ComboBoxRenderer.DrawDropDownButton(pevent.Graphics, dropDownButtonRect, cbState); + } + else { + ComboBoxRenderer.DrawDropDownButtonForHandle(pevent.Graphics, dropDownButtonRect, cbState, this.HandleInternal); + } + + if (AccessibilityImprovements.Level1) { + // Redraw focus cues + // For consistency with other PropertyGrid buttons, i.e. those opening system dialogs ("..."), that always show visual cues when focused, + // we need to do the same for this custom button, painted as ComboBox control part (drop-down). + if (Focused) { + dropDownButtonRect.Inflate(-1, -1); + ControlPaint.DrawFocusRectangle(pevent.Graphics, dropDownButtonRect, ForeColor, BackColor); + } + } + } + } + + internal void PerformButtonClick() { + if (Visible && Enabled) { + OnClick(EventArgs.Empty); + } + } + + private void SetAccessibleName() { + if (AccessibilityImprovements.Level1 && useComboBoxTheme) { + this.AccessibleName = SR.GetString(SR.PropertyGridDropDownButtonComboBoxAccessibleName); + } + else { + this.AccessibleName = SR.GetString(SR.PropertyGridDropDownButtonAccessibleName); + } + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object for this control. + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new DropDownButtonAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + internal override ButtonBaseAdapter CreateStandardAdapter() { + return new DropDownButtonAdapter(this); + } + } + + internal class DropDownButtonAdapter : ButtonStandardAdapter { + + internal DropDownButtonAdapter(ButtonBase control) : base(control) {} + + private void DDB_Draw3DBorder(System.Drawing.Graphics g, Rectangle r, bool raised) { + if (Control.BackColor != SystemColors.Control && SystemInformation.HighContrast) { + if (raised) { + Color c = ControlPaint.LightLight(Control.BackColor); + ControlPaint.DrawBorder(g, r, + c, 1, ButtonBorderStyle.Outset, + c, 1, ButtonBorderStyle.Outset, + c, 2, ButtonBorderStyle.Inset, + c, 2, ButtonBorderStyle.Inset); + } + else { + ControlPaint.DrawBorder(g, r, ControlPaint.Dark(Control.BackColor), ButtonBorderStyle.Solid); + } + } + else { + if (raised) { + Color c = ControlPaint.Light(Control.BackColor); + ControlPaint.DrawBorder(g, r, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.Solid, + Control.BackColor, 2, ButtonBorderStyle.Outset, + Control.BackColor, 2, ButtonBorderStyle.Outset); + + Rectangle inside = r; + inside.Offset(1,1); + inside.Width -= 3; + inside.Height -= 3; + c = ControlPaint.LightLight(Control.BackColor); + ControlPaint.DrawBorder(g, inside, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.None); + } + else { + ControlPaint.DrawBorder(g, r, ControlPaint.Dark(Control.BackColor), ButtonBorderStyle.Solid); + } + } + } + + internal override void PaintUp(PaintEventArgs pevent, CheckState state) { + base.PaintUp(pevent, state); + if (!Application.RenderWithVisualStyles) { + DDB_Draw3DBorder(pevent.Graphics, Control.ClientRectangle, true); + } + else { + Color c = SystemColors.Window; + Rectangle rect = Control.ClientRectangle; + rect.Inflate(0, -1); + ControlPaint.DrawBorder(pevent.Graphics, rect, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.None, + c, 1, ButtonBorderStyle.Solid, + c, 1, ButtonBorderStyle.None); + } + } + + internal override void DrawImageCore(Graphics graphics, Image image, Rectangle imageBounds, Point imageStart, ButtonBaseAdapter.LayoutData layout) { + if (AccessibilityImprovements.Level3 && IsFilledWithHighlightColor && (Control.MouseIsOver || Control.Focused)) { + ControlPaint.DrawImageReplaceColor(graphics, image, imageBounds, Color.Black, SystemColors.HighlightText); + } + else { + ControlPaint.DrawImageReplaceColor(graphics, image, imageBounds, Color.Black, Control.ForeColor); + } + } + } + + /// + /// Represents the accessibility object for the PropertyGrid DropDown button. + /// This DropDownButtonAccessibleObject is available in Level3 only. + /// + [Runtime.InteropServices.ComVisible(true)] + internal class DropDownButtonAccessibleObject : Control.ControlAccessibleObject { + + private DropDownButton _owningDropDownButton; + private PropertyGridView _owningPropertyGrid; + + /// + /// Constructs the new instance of DropDownButtonAccessibleObject. + /// + /// + public DropDownButtonAccessibleObject(DropDownButton owningDropDownButton) : base(owningDropDownButton) { + _owningDropDownButton = owningDropDownButton; + _owningPropertyGrid = owningDropDownButton.Parent as PropertyGridView; + + UseStdAccessibleObjects(owningDropDownButton.Handle); + } + + public override void DoDefaultAction() { + _owningDropDownButton.PerformButtonClick(); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent && + _owningPropertyGrid.SelectedGridEntry != null && + _owningDropDownButton.Visible) { + return _owningPropertyGrid.SelectedGridEntry?.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.PreviousSibling) { + return _owningPropertyGrid.EditAccessibleObject; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Returns the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningPropertyGrid.AccessibilityObject; + } + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ButtonControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId: + return true; + case NativeMethods.UIA_LegacyIAccessibleRolePropertyId: + return Role; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Indicates whether the specified pattern is supported. + /// + /// The pattern ID. + /// True if specified pattern is supported, otherwise false. + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + return AccessibleRole.PushButton; + } + } + + /// + /// Request that focus is set to this item. + /// The UIAutomation framework will ensure that the UI hosting this fragment is already + /// focused before calling this method, so this method should only update its internal + /// focus state; it should not attempt to give its own HWND the focus, for example. + /// + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntry.cs new file mode 100644 index 000000000..0e0736d52 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntry.cs @@ -0,0 +1,3185 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +//#define PBRS_PAINT_DEBUG +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Security.Permissions; + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Collections; + using System.Reflection; + using System.Globalization; + + using System.Drawing.Design; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Windows.Forms.Internal; + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Drawing.Drawing2D; + using Microsoft.Win32; + + /// + /// + /// Base Entry for properties to be displayed in properties window. + /// + internal abstract class GridEntry : GridItem, ITypeDescriptorContext { + + protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue); + private static BooleanSwitch PbrsAssertPropsSwitch = new BooleanSwitch("PbrsAssertProps", "PropertyBrowser : Assert on broken properties"); + + internal static AttributeTypeSorter AttributeTypeSorter = new AttributeTypeSorter(); + + // Type flags + internal const int FLAG_TEXT_EDITABLE = 0x0001; + internal const int FLAG_ENUMERABLE = 0x0002; + internal const int FLAG_CUSTOM_PAINT = 0x0004; + internal const int FLAG_IMMEDIATELY_EDITABLE = 0x0008; + internal const int FLAG_CUSTOM_EDITABLE = 0x0010; + internal const int FLAG_DROPDOWN_EDITABLE = 0x0020; + internal const int FLAG_LABEL_BOLD = 0x0040; + internal const int FLAG_READONLY_EDITABLE = 0x0080; + internal const int FLAG_RENDER_READONLY = 0x0100; + internal const int FLAG_IMMUTABLE = 0x0200; + internal const int FLAG_FORCE_READONLY = 0x0400; + internal const int FLAG_RENDER_PASSWORD = 0x1000; + + internal const int FLAG_DISPOSED = 0x2000; + + internal const int FL_EXPAND = 0x00010000; + internal const int FL_EXPANDABLE = 0x00020000; + //protected const int FL_EXPANDABLE_VALID = 0x00040000; + internal const int FL_EXPANDABLE_FAILED = 0x00080000; + internal const int FL_NO_CUSTOM_PAINT = 0x00100000; + internal const int FL_CATEGORIES = 0x00200000; + internal const int FL_CHECKED = unchecked((int)0x80000000); + + // rest are GridEntry constants. + + protected const int NOTIFY_RESET = 1; + protected const int NOTIFY_CAN_RESET = 2; + protected const int NOTIFY_DBL_CLICK = 3; + protected const int NOTIFY_SHOULD_PERSIST = 4; + protected const int NOTIFY_RETURN = 5; + + protected const int OUTLINE_ICON_PADDING = 5; + + protected static IComparer DisplayNameComparer = new DisplayNameSortComparer(); + + private static char passwordReplaceChar; + //Maximum number of characters we'll show in the property grid. Too many characters leads + //to bad performance. + private const int maximumLengthOfPropertyString = 1000; + + [Flags] + internal enum PaintValueFlags{ + None = 0, + DrawSelected = 0x1, + FetchValue = 0x2, + CheckShouldSerialize = 0x4, + PaintInPlace = 0x8 + } + + private class CacheItems { + public string lastLabel; + public Font lastLabelFont; + public int lastLabelWidth; + public string lastValueString; + public Font lastValueFont; + public int lastValueTextWidth; + public object lastValue; + public bool useValueString; + public bool lastShouldSerialize; + public bool useShouldSerialize; + public bool useCompatTextRendering; + } + + private CacheItems cacheItems; + + + // instance variables. + protected TypeConverter converter = null; + protected UITypeEditor editor = null; + internal GridEntry parentPE = null; + private GridEntryCollection childCollection = null; + internal int flags = 0; + private int propertyDepth = 0; + protected bool hasFocus = false; + private Rectangle outlineRect = Rectangle.Empty; + protected PropertySort PropertySort; + + protected Point labelTipPoint = InvalidPoint; + protected Point valueTipPoint = InvalidPoint; + + protected PropertyGrid ownerGrid; + + private static object EVENT_VALUE_CLICK = new object(); + private static object EVENT_LABEL_CLICK = new object(); + private static object EVENT_OUTLINE_CLICK = new object(); + private static object EVENT_VALUE_DBLCLICK = new object(); + private static object EVENT_LABEL_DBLCLICK = new object(); + private static object EVENT_OUTLINE_DBLCLICK = new object(); + private static object EVENT_RECREATE_CHILDREN = new object(); + + private GridEntryAccessibleObject accessibleObject = null; + + private bool lastPaintWithExplorerStyle = false; + + private static Color InvertColor(Color color) { + return Color.FromArgb(color.A, (byte)~color.R, (byte)~color.G, (byte)~color.B); + } + + protected GridEntry(PropertyGrid owner, GridEntry peParent) { + parentPE = peParent; + ownerGrid = owner; + + Debug.Assert( this.ownerGrid != null, "GridEntry w/o PropertyGrid owner, text rendering will fail." ); + + if (peParent != null) { + propertyDepth = peParent.PropertyDepth + 1; + this.PropertySort = peParent.PropertySort; + + if (peParent.ForceReadOnly) { + flags |= FLAG_FORCE_READONLY; + } + + } + else { + propertyDepth = -1; + } + } + + /// + /// Outline Icon padding + /// + private int OutlineIconPadding { + get { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + if (this.GridEntryHost != null) { + return this.GridEntryHost.LogicalToDeviceUnits(OUTLINE_ICON_PADDING); + } + } + + return OUTLINE_ICON_PADDING; + } + } + + private bool colorInversionNeededInHC { + get { + return SystemInformation.HighContrast && !OwnerGrid.developerOverride && AccessibilityImprovements.Level1; + } + } + + public AccessibleObject AccessibilityObject { + + get { + if (accessibleObject == null) { + accessibleObject = GetAccessibilityObject(); + } + return accessibleObject; + } + } + + protected virtual GridEntryAccessibleObject GetAccessibilityObject() { + return new GridEntryAccessibleObject(this); + } + + /// + /// + /// specify that this grid entry should be allowed to be merged for. + /// multi-select. + /// + public virtual bool AllowMerge { + get { + return true; + } + } + + internal virtual bool AlwaysAllowExpand { + get { + return false; + } + } + + internal virtual AttributeCollection Attributes { + get { + return TypeDescriptor.GetAttributes(PropertyType); + } + } + + /// + /// + /// Gets the value of the background brush to use. Override + /// this member to cause the entry to paint it's background in a different color. + /// The base implementation returns null. + /// + protected virtual Brush GetBackgroundBrush(Graphics g) { + return GridEntryHost.GetBackgroundBrush(g); + } + + /// + /// + /// + protected virtual Color LabelTextColor { + get { + if (this.ShouldRenderReadOnly) { + return GridEntryHost.GrayTextColor; + } + else { + return GridEntryHost.GetTextColor(); + } + } + } + + /// + /// + /// The set of attributes that will be used for browse filtering + /// + public virtual AttributeCollection BrowsableAttributes { + get{ + if (parentPE != null) { + return parentPE.BrowsableAttributes; + } + return null; + } + set{ + parentPE.BrowsableAttributes = value; + } + } + + /// + /// + /// Retrieves the component that is invoking the + /// method on the formatter object. This may + /// return null if there is no component + /// responsible for the call. + /// + public virtual IComponent Component { + get { + object owner = GetValueOwner(); + if (owner is IComponent) { + return(IComponent) owner; + } + if (parentPE != null) { + return parentPE.Component; + } + return null; + } + } + + protected virtual IComponentChangeService ComponentChangeService { + get { + return parentPE.ComponentChangeService; + } + + } + + /// + /// + /// Retrieves the container that contains the + /// set of objects this formatter may work + /// with. It may return null if there is no + /// container, or of the formatter should not + /// use any outside objects. + /// + public virtual IContainer Container { + get { + IComponent component = Component; + if (component != null) { + ISite site = component.Site; + if (site != null) { + return site.Container; + } + } + return null; + } + } + + protected GridEntryCollection ChildCollection{ + get { + if (childCollection == null) { + childCollection = new GridEntryCollection(this, null); + } + return childCollection; + } + set { + Debug.Assert(value == null || !Disposed, "Why are we putting new children in after we are disposed?"); + if (this.childCollection != value) { + if (this.childCollection != null) { + this.childCollection.Dispose(); + this.childCollection = null; + } + this.childCollection = value; + } + } + } + + public int ChildCount { + get { + if (Children != null) { + return Children.Count; + } + return 0; + } + } + + public virtual GridEntryCollection Children { + get { + if (childCollection == null && !Disposed) { + CreateChildren(); + } + return childCollection; + } + } + + public virtual PropertyTab CurrentTab{ + get{ + if (parentPE != null) { + return parentPE.CurrentTab; + } + return null; + } + set{ + if (parentPE != null) { + parentPE.CurrentTab = value; + } + } + } + + /// + /// + /// Returns the default child GridEntry of this item. Usually the default property + /// of the target object. + /// + internal virtual GridEntry DefaultChild { + get { + return null; + } + set{} + } + + internal virtual IDesignerHost DesignerHost{ + get{ + if (parentPE != null) { + return parentPE.DesignerHost; + } + return null; + } + set { + if (parentPE != null) { + parentPE.DesignerHost = value; + } + } + } + + internal bool Disposed{ + get { + return GetFlagSet(FLAG_DISPOSED); + } + } + + internal virtual bool Enumerable { + get { + return(this.Flags & GridEntry.FLAG_ENUMERABLE) != 0; + } + } + + + public override bool Expandable { + get { + bool fExpandable = GetFlagSet(FL_EXPANDABLE); + + if (fExpandable && childCollection != null && childCollection.Count > 0) { + return true; + } + + if (GetFlagSet(FL_EXPANDABLE_FAILED)) { + return false; + } + + if (fExpandable && (cacheItems == null || cacheItems.lastValue == null) && this.PropertyValue == null) { + fExpandable = false; + } + + return fExpandable; + } + } + + public override bool Expanded { + get{ + return InternalExpanded; + } + set { + GridEntryHost.SetExpand(this, value); + } + } + + internal virtual bool ForceReadOnly { + get { + return (flags & FLAG_FORCE_READONLY) != 0; + } + } + + internal virtual bool InternalExpanded { + get{ + // short circuit if we don't have children + if (childCollection == null || childCollection.Count == 0) { + return false; + } + return GetFlagSet(FL_EXPAND); + } + set { + if (!this.Expandable || value == this.InternalExpanded) { + return; + } + + if (childCollection != null && childCollection.Count > 0) { + SetFlag(FL_EXPAND,value); + } + else { + SetFlag(FL_EXPAND,false); + if (value) { + bool fMakeSure = CreateChildren(); + SetFlag(FL_EXPAND,fMakeSure); + } + } + + if (AccessibilityImprovements.Level1) { + // Notify accessibility clients of expanded state change + // StateChange requires NameChange, too - accessible clients won't see this, unless both events are raised + + // Root item is hidden and should not raise events + if (GridItemType != GridItemType.Root) { + int id = ((PropertyGridView)GridEntryHost).AccessibilityGetGridEntryChildID(this); + if (id >= 0) { + PropertyGridView.PropertyGridViewAccessibleObject gridAccObj = + (PropertyGridView.PropertyGridViewAccessibleObject)((PropertyGridView)GridEntryHost).AccessibilityObject; + + gridAccObj.NotifyClients(AccessibleEvents.StateChange, id); + gridAccObj.NotifyClients(AccessibleEvents.NameChange, id); + } + } + } + } + } + + internal virtual int Flags { + get { + if ((flags & FL_CHECKED) != 0) { + return flags; + } + + flags |= FL_CHECKED; + + TypeConverter converter = TypeConverter; + UITypeEditor uiEditor = UITypeEditor; + object value = Instance; + bool forceReadOnly = this.ForceReadOnly; + + if (value != null) { + forceReadOnly |= TypeDescriptor.GetAttributes(value).Contains(InheritanceAttribute.InheritedReadOnly); + } + + if (converter.GetStandardValuesSupported(this)) { + flags |= GridEntry.FLAG_ENUMERABLE; + } + + if (!forceReadOnly && converter.CanConvertFrom(this, typeof(string)) && + !converter.GetStandardValuesExclusive(this)) { + flags |= GridEntry.FLAG_TEXT_EDITABLE; + } + + bool isImmutableReadOnly = TypeDescriptor.GetAttributes(this.PropertyType)[typeof(ImmutableObjectAttribute)].Equals(ImmutableObjectAttribute.Yes); + bool isImmutable = isImmutableReadOnly || converter.GetCreateInstanceSupported(this); + + if (isImmutable) { + flags |= GridEntry.FLAG_IMMUTABLE; + } + + if (converter.GetPropertiesSupported(this)) { + flags |= GridEntry.FL_EXPANDABLE; + + // If we're exapndable, but we don't support editing, + // make us read only editable so we don't paint grey. + // + if (!forceReadOnly && (flags & GridEntry.FLAG_TEXT_EDITABLE) == 0 && !isImmutableReadOnly) { + flags |= GridEntry.FLAG_READONLY_EDITABLE; + } + } + + if (Attributes.Contains(PasswordPropertyTextAttribute.Yes)) { + flags |= GridEntry.FLAG_RENDER_PASSWORD; + } + + if (uiEditor != null) { + if (uiEditor.GetPaintValueSupported(this)) { + flags |= GridEntry.FLAG_CUSTOM_PAINT; + } + + // We only allow drop-downs if the object is NOT being inherited + // I would really rather this not be here, but we have other places where + // we make read-only properties editable if they have drop downs. Not + // sure this is the right thing...is it? + + bool allowButtons = !forceReadOnly; + + if (allowButtons) { + switch (uiEditor.GetEditStyle(this)) { + case UITypeEditorEditStyle.Modal: + flags |= GridEntry.FLAG_CUSTOM_EDITABLE; + if (!isImmutable && !PropertyType.IsValueType) { + flags |= GridEntry.FLAG_READONLY_EDITABLE; + } + break; + case UITypeEditorEditStyle.DropDown: + flags |= GridEntry.FLAG_DROPDOWN_EDITABLE; + break; + } + } + } + + return flags; + + } + set { + flags = value; + } + } + + /// + /// + /// Checks if the entry is currently expanded + /// + public bool Focus { + get{ + return this.hasFocus; + } + set{ + + if (Disposed) { + return; + } + + if (cacheItems != null) { + cacheItems.lastValueString = null; + cacheItems.useValueString = false; + cacheItems.useShouldSerialize = false; + } + + if (this.hasFocus != value) { + this.hasFocus = value; + + // Notify accessibility applications that keyboard focus has changed. + // + if (value == true) { + int id = ((PropertyGridView)GridEntryHost).AccessibilityGetGridEntryChildID(this); + if (id >= 0) { + PropertyGridView.PropertyGridViewAccessibleObject gridAccObj = + (PropertyGridView.PropertyGridViewAccessibleObject)((PropertyGridView)GridEntryHost).AccessibilityObject; + + gridAccObj.NotifyClients(AccessibleEvents.Focus, id); + gridAccObj.NotifyClients(AccessibleEvents.Selection, id); + + if (AccessibilityImprovements.Level3) { + AccessibilityObject.SetFocus(); + } + } + } + } + } + } + + /// + /// + /// Returns the label including the object name, and properties. For example, the value + /// of the Font size property on a Button called Button1 would be "Button1.Font.Size" + /// + public string FullLabel { + get { + string str = null; + if (parentPE != null) { + str = parentPE.FullLabel; + } + + if (str != null) { + str += "."; + } + else { + str = ""; + } + str += this.PropertyLabel; + + return str; + } + } + + public override GridItemCollection GridItems { + get { + if (Disposed) { + throw new ObjectDisposedException(SR.GetString(SR.GridItemDisposed)); + } + + if (IsExpandable && childCollection != null && childCollection.Count == 0) { + CreateChildren(); + } + + return this.Children; + } + } + + internal virtual PropertyGridView GridEntryHost{ + get{ // ACCESSOR: virtual was missing from this get + if (parentPE != null) { + return parentPE.GridEntryHost; + } + return null; + } + set { + throw new NotSupportedException(); + } + } + + public override GridItemType GridItemType { + get { + return GridItemType.Property; + } + } + + /// + /// + /// Returns true if this GridEntry has a value field in the right hand column. + /// + internal virtual bool HasValue { + get { + return true; + } + } + + /// + /// + /// Retrieves the keyword that the VS help dynamic help window will + /// use when this IPE is selected. + /// + public virtual string HelpKeyword { + get { + string keyWord = null; + + if (parentPE != null) { + keyWord = parentPE.HelpKeyword; + } + if (keyWord == null) { + keyWord = String.Empty; + } + + return keyWord; + } + } + + internal virtual string HelpKeywordInternal{ + get { + return this.HelpKeyword; + } + } + + public virtual bool IsCustomPaint { + get { + // prevent full flag population if possible. + if ((flags & FL_CHECKED) == 0) { + UITypeEditor typeEd = this.UITypeEditor; + if (typeEd != null) { + if ((this.flags & GridEntry.FLAG_CUSTOM_PAINT) != 0 || + (this.flags & GridEntry.FL_NO_CUSTOM_PAINT) != 0) { + return(this.flags & GridEntry.FLAG_CUSTOM_PAINT) != 0; + } + + + if (typeEd.GetPaintValueSupported(this)) { + flags |= GridEntry.FLAG_CUSTOM_PAINT; + return true; + } + else { + flags |= GridEntry.FL_NO_CUSTOM_PAINT; + return false; + } + } + } + return(this.Flags & GridEntry.FLAG_CUSTOM_PAINT) != 0; + } + } + + public virtual bool IsExpandable { + get { + return this.Expandable; + } + set { + if (value != GetFlagSet(FL_EXPANDABLE)) { + SetFlag(FL_EXPANDABLE_FAILED, false); + SetFlag(FL_EXPANDABLE, value); + } + } + } + + public virtual bool IsTextEditable { + get { + return this.IsValueEditable && (this.Flags & GridEntry.FLAG_TEXT_EDITABLE) != 0; + } + } + + public virtual bool IsValueEditable { + get { + return !ForceReadOnly && 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE)); + } + } + + /// + /// + /// Retrieves the component that is invoking the + /// method on the formatter object. This may + /// return null if there is no component + /// responsible for the call. + /// + public virtual object Instance { + get { + object owner = GetValueOwner(); + + if (parentPE != null && owner == null) { + return parentPE.Instance; + } + return owner; + } + } + + public override string Label { + get { + return this.PropertyLabel; + } + } + + /// + /// + /// Retrieves the PropertyDescriptor that is surfacing the given object/ + /// + public override PropertyDescriptor PropertyDescriptor { + get { + return null; + } + } + + + + /// + /// + /// Returns the pixel indent of the current GridEntry's label. + /// + internal virtual int PropertyLabelIndent { + get { + int borderWidth = this.GridEntryHost.GetOutlineIconSize() + OUTLINE_ICON_PADDING; + return((propertyDepth + 1) * borderWidth) + 1; + } + } + + internal virtual Point GetLabelToolTipLocation(int mouseX, int mouseY) { + return labelTipPoint; + } + + internal virtual string LabelToolTipText { + get { + return this.PropertyLabel; + } + } + + public virtual bool NeedsDropDownButton{ + get { + return(this.Flags & GridEntry.FLAG_DROPDOWN_EDITABLE) != 0; + } + } + + public virtual bool NeedsCustomEditorButton{ + get { + return(this.Flags & GridEntry.FLAG_CUSTOM_EDITABLE) != 0 && (IsValueEditable || (Flags & GridEntry.FLAG_READONLY_EDITABLE) !=0); + } + } + + public PropertyGrid OwnerGrid{ + get{ + return this.ownerGrid; + } + } + + /// + /// + /// Returns rect that the outline icon (+ or - or arrow) will be drawn into, relative + /// to the upper left corner of the GridEntry. + /// + public Rectangle OutlineRect { + get { + if (!outlineRect.IsEmpty) { + return outlineRect; + } + PropertyGridView gridHost = this.GridEntryHost; + Debug.Assert(gridHost != null, "No propEntryHost!"); + int outlineSize = gridHost.GetOutlineIconSize(); + int borderWidth = outlineSize + OutlineIconPadding; + int left = (propertyDepth * borderWidth) + (OutlineIconPadding) / 2; + int top = (gridHost.GetGridEntryHeight() - outlineSize) / 2; + outlineRect = new Rectangle(left, top, outlineSize, outlineSize); + return outlineRect; + } + set { + // set property is required to reset cached value when dpi changed. + if (value != outlineRect) { + outlineRect = value; + } + } + } + + public virtual GridEntry ParentGridEntry{ + get { + return this.parentPE; + } + set { + Debug.Assert(value != this, "how can we be our own parent?"); + this.parentPE = value; + if (value != null) { + propertyDepth = value.PropertyDepth+1; + + // Microsoft, why do we do this? + if (this.childCollection != null) { + for (int i = 0; i < childCollection.Count; i++) { + childCollection.GetEntry(i).ParentGridEntry = this; + } + } + } + } + } + + public override GridItem Parent { + get { + if (Disposed) { + throw new ObjectDisposedException(SR.GetString(SR.GridItemDisposed)); + } + + GridItem parent = this.ParentGridEntry; + + // don't allow walking all the way up to the parent. + // + //if (parent is IRootGridEntry) { + // return null; + //} + return parent; + } + } + + /// + /// + /// Returns category name of the current property + /// + public virtual string PropertyCategory { + get { + return CategoryAttribute.Default.Category; + } + } + + /// + /// + /// Returns "depth" of this property. That is, how many parent's between + /// this property and the root property. The root property has a depth of -1. + /// + public virtual int PropertyDepth { + get { + return propertyDepth; + } + } + + + /// + /// + /// Returns the description helpstring for this GridEntry. + /// + public virtual string PropertyDescription { + get { + return null; + } + } + + /// + /// + /// Returns the label of this property. Usually + /// this is the property name. + /// + public virtual string PropertyLabel { + get { + return null; + } + } + + /// + /// + /// Returns non-localized name of this property. + /// + public virtual string PropertyName { + get { + return this.PropertyLabel; + } + } + + /// + /// + /// Returns the Type of the value of this GridEntry, or null if the value is null. + /// + public virtual Type PropertyType { + get { + object obj = this.PropertyValue; + if (obj != null) { + return obj.GetType(); + } + else { + return null; + } + } + } + + /// + /// + /// Gets or sets the value for the property that is represented + /// by this GridEntry. + /// + public virtual object PropertyValue{ + get { + if (cacheItems != null) { + return cacheItems.lastValue; + } + return null; + } + set { + } + } + + public virtual bool ShouldRenderPassword { + get { + return (this.Flags & GridEntry.FLAG_RENDER_PASSWORD) != 0; + } + } + + public virtual bool ShouldRenderReadOnly { + get { + return ForceReadOnly || (0 != (this.Flags & GridEntry.FLAG_RENDER_READONLY) || (!this.IsValueEditable && (0 == (this.Flags & GridEntry.FLAG_READONLY_EDITABLE)))); + } + } + + /// + /// + /// Returns the type converter for this entry. + /// + internal virtual TypeConverter TypeConverter { + get { + if (converter == null) { + object value = this.PropertyValue; + if (value == null) { + converter = TypeDescriptor.GetConverter(this.PropertyType); + } + else { + converter = TypeDescriptor.GetConverter(value); + } + } + return converter; + } + } + + /// + /// + /// Returns the type editor for this entry. This may return null if there + /// is no type editor. + /// + internal virtual UITypeEditor UITypeEditor { + get { + if (editor == null && this.PropertyType != null) { + editor = (UITypeEditor)TypeDescriptor.GetEditor(this.PropertyType, typeof(System.Drawing.Design.UITypeEditor)); + } + + return editor; + } + } + + public override object Value { + get { + return this.PropertyValue; + } + // note: we don't do set because of the value class semantics, etc. + } + + internal Point ValueToolTipLocation { + get { + return ShouldRenderPassword ? InvalidPoint : valueTipPoint; + } + set{ + valueTipPoint = value; + } + } + + internal int VisibleChildCount { + get{ + if (!Expanded) { + return 0; + } + int count = ChildCount; + int totalCount = count; + for (int i = 0; i < count; i++) { + totalCount += ChildCollection.GetEntry(i).VisibleChildCount; + } + return totalCount; + } + } + + + /// + /// + /// Add an event handler to be invoked when the label portion of + /// the prop entry is clicked + /// + public virtual void AddOnLabelClick(EventHandler h) { + AddEventHandler(EVENT_LABEL_CLICK, h); + } + + /// + /// + /// Add an event handler to be invoked when the label portion of + /// the prop entry is double + /// + public virtual void AddOnLabelDoubleClick(EventHandler h) { + AddEventHandler(EVENT_LABEL_DBLCLICK, h); + } + + /// + /// + /// Add an event handler to be invoked when the value portion of + /// the prop entry is clicked + /// + public virtual void AddOnValueClick(EventHandler h) { + AddEventHandler(EVENT_VALUE_CLICK, h); + } + + + /// + /// + /// Add an event handler to be invoked when the value portion of + /// the prop entry is double-clicked + /// + public virtual void AddOnValueDoubleClick(EventHandler h) { + AddEventHandler(EVENT_VALUE_DBLCLICK, h); + } + + /// + /// + /// Add an event handler to be invoked when the outline icone portion of + /// the prop entry is clicked + /// + public virtual void AddOnOutlineClick(EventHandler h) { + AddEventHandler(EVENT_OUTLINE_CLICK, h); + } + + /// + /// + /// Add an event handler to be invoked when the outline icone portion of + /// the prop entry is double clicked + /// + public virtual void AddOnOutlineDoubleClick(EventHandler h) { + AddEventHandler(EVENT_OUTLINE_DBLCLICK, h); + } + + /// + /// + /// Add an event handler to be invoked when the children grid entries are re-created. + /// + public virtual void AddOnRecreateChildren(GridEntryRecreateChildrenEventHandler h) { + AddEventHandler(EVENT_RECREATE_CHILDREN, h); + } + + internal void ClearCachedValues() { + ClearCachedValues(true); + } + + internal void ClearCachedValues(bool clearChildren) { + if (cacheItems != null) { + cacheItems.useValueString = false; + cacheItems.lastValue = null; + cacheItems.useShouldSerialize = false; + } + if (clearChildren) { + for (int i = 0; i < ChildCollection.Count; i++) { + ChildCollection.GetEntry(i).ClearCachedValues(); + } + } + } + + /// + /// + /// Converts the given string of text to a value. + /// + public object ConvertTextToValue(string text) { + if (TypeConverter.CanConvertFrom(this, typeof(string))) { + return TypeConverter.ConvertFromString(this, text); + } + return text; + } + + /// + /// + /// Create the base prop entries given an object or set of objects + /// + internal static IRootGridEntry Create(PropertyGridView view, object[] rgobjs, IServiceProvider baseProvider, IDesignerHost currentHost, PropertyTab tab, PropertySort initialSortType) { + IRootGridEntry pe = null; + + if (rgobjs == null || rgobjs.Length == 0) { + return null; + } + + try + { + if (rgobjs.Length == 1) + { + pe = new SingleSelectRootGridEntry(view, rgobjs[0], baseProvider, currentHost, tab, initialSortType); + } + else + { + pe = new MultiSelectRootGridEntry(view, rgobjs, baseProvider, currentHost, tab, initialSortType); + } + } + catch (Exception e) + { + //Debug.fail("Couldn't create a top-level GridEntry"); + Debug.Fail(e.ToString()); + throw; + } + return pe; + } + + /// + /// + /// Populates the children of this grid entry + /// + protected virtual bool CreateChildren() { + return CreateChildren(false); + } + + /// + /// + /// Populates the children of this grid entry + /// + protected virtual bool CreateChildren(bool diffOldChildren) { + + Debug.Assert(!Disposed, "Why are we creating children after we are disposed?"); + + if (!GetFlagSet(FL_EXPANDABLE)) { + if (this.childCollection != null) { + this.childCollection.Clear(); + } + else { + this.childCollection = new GridEntryCollection(this, new GridEntry[0]); + } + return false; + } + + + if (!diffOldChildren && childCollection != null && childCollection.Count > 0) { + return true; + } + + + GridEntry [] childProps = GetPropEntries(this, + this.PropertyValue, + this.PropertyType); + + + bool fExpandable = (childProps != null && childProps.Length > 0); + + if (diffOldChildren && childCollection != null && childCollection.Count > 0) { + bool same = true; + if (childProps.Length == childCollection.Count) { + for (int i = 0; i < childProps.Length; i++) { + if (!childProps[i].NonParentEquals(childCollection[i])) { + same = false; + break; + } + } + } + else { + same = false; + } + + if (same) { + return true; + } + } + + + + if (!fExpandable) { + SetFlag(FL_EXPANDABLE_FAILED,true); + if (this.childCollection != null) { + this.childCollection.Clear(); + } + else { + this.childCollection = new GridEntryCollection(this, new GridEntry[0]); + } + + if (this.InternalExpanded) { + this.InternalExpanded = false; + } + + } + else { + if (this.childCollection != null) { + this.childCollection.Clear(); + this.childCollection.AddRange(childProps); + } + else { + this.childCollection = new GridEntryCollection(this, childProps); + } + } + return fExpandable; + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + protected virtual void Dispose(bool disposing) { + // make sure we don't accidentally + // check flags in this state... + flags |= FL_CHECKED; + + SetFlag(FLAG_DISPOSED, true); + + cacheItems = null; + converter = null; + editor = null; + accessibleObject = null; + + if (disposing) { + DisposeChildren(); + } + } + + + /// + /// + /// Disposes the array of children + /// + public virtual void DisposeChildren() { + if (childCollection != null) { + childCollection.Dispose(); + childCollection = null; + } + } + + + ~GridEntry() { + Dispose(false); + } + + + /// + /// + /// Invokes the type editor for editing this item. + /// + internal virtual void EditPropertyValue(PropertyGridView iva) { + if (UITypeEditor != null) { + try { + // this is another icky part. since edit value can push a modal loop + // there is a chance that this gridentry will be zombied before + // it returns. make sure we're not disposed. + // + object originalValue = this.PropertyValue; + object value = UITypeEditor.EditValue(this, (IServiceProvider)(ITypeDescriptorContext)this, originalValue); + + if (Disposed) { + return; + } + + // Push the new value back into the property + if (value != originalValue && this.IsValueEditable) { + iva.CommitValue(this, value); + } + + if (this.InternalExpanded) { + // QFE#3299: If edited property is expanded to show sub-properties, then we want to + // preserve the expanded states of it and all of its descendants. RecreateChildren() + // has logic that is supposed to do this, but which is fundamentally flawed. + PropertyGridView.GridPositionData positionData = GridEntryHost.CaptureGridPositionData(); + this.InternalExpanded = false; + RecreateChildren(); + positionData.Restore(GridEntryHost); + } + else { + // If edited property has no children or is collapsed, don't need to preserve expanded states. + // This limits the scope of the above QFE fix to just those cases where it is actually required. + RecreateChildren(); + } + } + catch (Exception e) + { + IUIService uiSvc = (IUIService)GetService(typeof(IUIService)); + if (uiSvc != null) + { + uiSvc.ShowError(e); + } + else + { + RTLAwareMessageBox.Show(GridEntryHost, e.Message, SR.GetString(SR.PBRSErrorTitle), MessageBoxButtons.OK, + MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + } + } + } + } + + /// + /// + /// Tests two GridEntries for equality + /// + public override bool Equals(object obj) { + + if (NonParentEquals(obj)) { + return((GridEntry)obj).ParentGridEntry == this.ParentGridEntry; + } + return false; + } + + /// + /// + /// Searches for a value of a given property for a value editor user + /// + public virtual object FindPropertyValue(string propertyName, Type propertyType) { + object owner = GetValueOwner(); + PropertyDescriptor property = TypeDescriptor.GetProperties(owner)[propertyName]; + if (property != null && property.PropertyType == propertyType) { + return property.GetValue(owner); + } + + if (parentPE != null) + return parentPE.FindPropertyValue(propertyName, propertyType); + + return null; + } + + /// + /// + /// Returns the index of a child GridEntry + /// + internal virtual int GetChildIndex(GridEntry pe) { + return this.Children.GetEntry(pe); + } + + /// + /// + /// Gets the components that own the current value. This is usually the value of the + /// root entry, which is the object being browsed. Walks up the GridEntry tree + /// looking for an owner that is an IComponent + /// + public virtual IComponent[] GetComponents() { + IComponent component = Component; + if (component != null) { + return new IComponent[] { component}; + } + return null; + } + + protected int GetLabelTextWidth(string labelText, Graphics g, Font f) { + + if (cacheItems == null) { + cacheItems = new CacheItems(); + } + else if (cacheItems.useCompatTextRendering == ownerGrid.UseCompatibleTextRendering && cacheItems.lastLabel == labelText && f.Equals(cacheItems.lastLabelFont)) { + return cacheItems.lastLabelWidth; + } + + SizeF textSize = PropertyGrid.MeasureTextHelper.MeasureText( this.ownerGrid, g, labelText, f); + + cacheItems.lastLabelWidth = (int) textSize.Width; + cacheItems.lastLabel = labelText; + cacheItems.lastLabelFont = f; + cacheItems.useCompatTextRendering = ownerGrid.UseCompatibleTextRendering; + + return cacheItems.lastLabelWidth; + } + + internal int GetValueTextWidth(string valueString, Graphics g, Font f) { + + if (cacheItems == null) { + cacheItems = new CacheItems(); + } + else if (cacheItems.lastValueTextWidth != -1 && cacheItems.lastValueString == valueString && f.Equals(cacheItems.lastValueFont)) { + return cacheItems.lastValueTextWidth; + } + + // Value text is rendered using GDI directly (No TextRenderer) but measured/adjusted using GDI+ (since previous releases), so don't use MeasureTextHelper. + cacheItems.lastValueTextWidth = (int) g.MeasureString(valueString, f).Width; + cacheItems.lastValueString = valueString; + cacheItems.lastValueFont = f; + return cacheItems.lastValueTextWidth; + } + //subhag (66503) To check if text contains multiple lines + // + internal bool GetMultipleLines(string valueString) { + + if (valueString.IndexOf('\n') > 0 || valueString.IndexOf('\r') > 0 ) + return true; + else + return false; + } + /// + /// + /// Gets the owner of the current value. This is usually the value of the + /// root entry, which is the object being browsed + /// + public virtual object GetValueOwner() { + if (parentPE == null) { + return this.PropertyValue; + } + + return parentPE.GetChildValueOwner(this); + } + + /// + /// + /// Gets the owners of the current value. This is usually the value of the + /// root entry, which is the objects being browsed for a multiselect item + /// + public virtual object[] GetValueOwners() { + object owner = GetValueOwner(); + if (owner != null) { + return new object[] { owner}; + } + return null; + } + + /// + /// + /// Gets the owner of the current value. This is usually the value of the + /// root entry, which is the object being browsed + /// + public virtual object GetChildValueOwner(GridEntry childEntry) { + + /*// make sure this is one of our children + int index = GetChildIndex(childEntry); + + if (index != -1){ + return this.PropertyValue; + } + + + Debug.Fail(childEntry.PropertyLabel + " is not a child of " + this.PropertyLabel); + return null;*/ + return this.PropertyValue; + } + + /// + /// + /// Returns a string with info about the currently selected GridEntry + /// + public virtual string GetTestingInfo() { + string str = "object = ("; + string textVal = GetPropertyTextValue(); + if (textVal == null) { + textVal = "(null)"; + } + else { + // make sure we clear any embedded nulls + textVal = textVal.Replace((char)0, ' '); + } + Type type = this.PropertyType; + if (type==null) { + type = typeof(object); + } + str += this.FullLabel; + str += "), property = (" + this.PropertyLabel + "," + type.AssemblyQualifiedName + "), value = " + "[" + textVal + "], expandable = " + this.Expandable.ToString() + ", readOnly = " + ShouldRenderReadOnly;; + return str; + } + + /// + /// + /// Retrieves the type of the value for this GridEntry + /// + public virtual Type GetValueType() { + return this.PropertyType; + } + + /// + /// + /// Returns the child GridEntries for this item. + /// + protected virtual GridEntry[] GetPropEntries(GridEntry peParent, object obj, Type objType) { + + + // we don't want to create subprops for null objects. + if (obj == null) { + return null; + } + + GridEntry[] entries = null; + + Attribute[] attributes = new Attribute[this.BrowsableAttributes.Count]; + this.BrowsableAttributes.CopyTo(attributes, 0); + + PropertyTab tab = this.CurrentTab; + Debug.Assert(tab != null, "No current tab!"); + + try + { + + bool forceReadOnly = this.ForceReadOnly; + + if (!forceReadOnly) + { + ReadOnlyAttribute readOnlyAttr = (ReadOnlyAttribute)TypeDescriptor.GetAttributes(obj)[typeof(ReadOnlyAttribute)]; + forceReadOnly = (readOnlyAttr != null && !readOnlyAttr.IsDefaultAttribute()); + } + + // do we want to expose sub properties? + // + if (TypeConverter.GetPropertiesSupported(this) || AlwaysAllowExpand) + { + + // ask the tab if we have one. + // + PropertyDescriptorCollection props = null; + PropertyDescriptor defProp = null; + if (tab != null) + { + props = tab.GetProperties(this, obj, attributes); + defProp = tab.GetDefaultProperty(obj); + } + else + { + props = TypeConverter.GetProperties(this, obj, attributes); + defProp = TypeDescriptor.GetDefaultProperty(obj); + } + + if (props == null) + { + return null; + } + + if ((this.PropertySort & PropertySort.Alphabetical) != 0) + { + if (objType == null || !objType.IsArray) + { + props = props.Sort(GridEntry.DisplayNameComparer); + } + + PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[props.Count]; + props.CopyTo(propertyDescriptors, 0); + + props = new PropertyDescriptorCollection(SortParenProperties(propertyDescriptors)); + } + + if (defProp == null && props.Count > 0) + { + defProp = props[0]; + } + + // if the target object is an array and nothing else has provided a set of + // properties to use, then expand the array. + // + if ((props == null || props.Count == 0) && objType != null && objType.IsArray && obj != null) + { + + Array objArray = (Array)obj; + + entries = new GridEntry[objArray.Length]; + + for (int i = 0; i < entries.Length; i++) + { + entries[i] = new ArrayElementGridEntry(this.ownerGrid, peParent, i); + } + } + else + { + // otherwise, create the proper GridEntries. + // + bool createInstanceSupported = TypeConverter.GetCreateInstanceSupported(this); + entries = new GridEntry[props.Count]; + int index = 0; + + // loop through all the props we got and create property descriptors. + // + foreach (PropertyDescriptor pd in props) + { + GridEntry newEntry; + + // make sure we've got a valid property, otherwise hide it + // + bool hide = false; + try + { + object owner = obj; + if (obj is ICustomTypeDescriptor) + { + owner = ((ICustomTypeDescriptor)obj).GetPropertyOwner(pd); + } + pd.GetValue(owner); + } + catch (Exception w) + { + if (PbrsAssertPropsSwitch.Enabled) + { + Debug.Fail("Bad property '" + peParent.PropertyLabel + "." + pd.Name + "': " + w.ToString()); + } + hide = true; + } + + if (createInstanceSupported) + { + newEntry = new ImmutablePropertyDescriptorGridEntry(this.ownerGrid, peParent, pd, hide); + } + else + { + newEntry = new PropertyDescriptorGridEntry(this.ownerGrid, peParent, pd, hide); + } + + if (forceReadOnly) + { + newEntry.flags |= FLAG_FORCE_READONLY; + } + + // check to see if we've come across the default item. + // + if (pd.Equals(defProp)) + { + this.DefaultChild = newEntry; + } + + // add it to the array. + // + entries[index++] = newEntry; + } + } + } + } + catch (Exception e) + { +#if DEBUG + if (PbrsAssertPropsSwitch.Enabled) { + // Checked builds are not giving us enough information here. So, output as much stuff as + // we can. + System.Text.StringBuilder b = new System.Text.StringBuilder(); + b.Append(string.Format(CultureInfo.CurrentCulture, "********* Debug log written on {0} ************\r\n", DateTime.Now)); + b.Append(string.Format(CultureInfo.CurrentCulture, "Exception '{0}' reading properties for object {1}.\r\n", e.GetType().Name, obj)); + b.Append(string.Format(CultureInfo.CurrentCulture, "Exception Text: \r\n{0}", e.ToString())); + b.Append(string.Format(CultureInfo.CurrentCulture, "Exception stack: \r\n{0}", e.StackTrace)); + string path = string.Format(CultureInfo.CurrentCulture, "{0}\\PropertyGrid.log", Environment.GetEnvironmentVariable("SYSTEMDRIVE")); + System.IO.FileStream s = new System.IO.FileStream(path, System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.None); + System.IO.StreamWriter w = new System.IO.StreamWriter(s); + w.Write(b.ToString()); + w.Close(); + s.Close(); + RTLAwareMessageBox.Show(null, b.ToString(), SR.GetString(SR.PropertyGridInternalNoProp, path), + MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + } +#endif + Debug.Fail("Failed to get properties: " + e.GetType().Name + "," + e.Message + "\n" + e.StackTrace); + } + return entries; + } + + /// + /// + /// Resets the current item + /// + public virtual void ResetPropertyValue() { + NotifyValue(NOTIFY_RESET); + Refresh(); + } + + /* + /// + /// Checks if the value of the current item can be modified by the user. + /// + /// + /// True if the value can be modified + /// + public virtual bool CanSetPropertyValue() { + return 0 != (Flags & (GridEntry.FLAG_DROPDOWN_EDITABLE | GridEntry.FLAG_TEXT_EDITABLE | GridEntry.FLAG_CUSTOM_EDITABLE | GridEntry.FLAG_ENUMERABLE)); + } + */ + + /* + /// + /// Returns if it's an editable item. An example of a readonly + /// editable item is a collection property -- the property itself + /// can not be modified, but it's value (e.g. it's children) can, so + /// we don't want to draw it as readonly. + /// + /// + /// True if the value associated with this property (e.g. it's children) can be modified even if it's readonly. + /// + public virtual bool CanSetReadOnlyPropertyValue() { + return GetFlagSet(GridEntry.FLAG_READONLY_EDITABLE); + } + */ + + /// + /// + /// Returns if the property can be reset + /// + public virtual bool CanResetPropertyValue() { + return NotifyValue(NOTIFY_CAN_RESET); + } + + /// + /// + /// Called when the item is double clicked. + /// + public virtual bool DoubleClickPropertyValue() { + return NotifyValue(NOTIFY_DBL_CLICK); + } + + + /// + /// + /// Returns the text value of this property. + /// + public virtual string GetPropertyTextValue() { + return GetPropertyTextValue(this.PropertyValue); + } + + /// + /// + /// Returns the text value of this property. + /// + public virtual string GetPropertyTextValue(object value) { + string str = null; + + TypeConverter converter = TypeConverter; + try + { + str = converter.ConvertToString(this, value); + } + catch (Exception t) + { + Debug.Fail("Bad Type Converter! " + t.GetType().Name + ", " + t.Message + "," + converter.ToString(), t.ToString()); + } + + if (str == null) { + str = String.Empty; + } + return str; + } + + /// + /// + /// Returns the text values of this property. + /// + public virtual object[] GetPropertyValueList() { + ICollection values = TypeConverter.GetStandardValues(this); + if (values != null) { + object[] valueArray = new object[values.Count]; + values.CopyTo(valueArray, 0); + return valueArray; + } + return new object[0]; + } + + public override int GetHashCode() { + // These can be null, so workaround giving hashcode = 0 for null objects. + object label = this.PropertyLabel; + object type = this.PropertyType; + UInt32 h1 = (UInt32)((label == null) ? 0 : label.GetHashCode()); + UInt32 h2 = (UInt32)((type == null) ? 0 : type.GetHashCode()); + UInt32 h3 = (UInt32)GetType().GetHashCode(); + + return(int)(h1 ^ ((h2 << 13) | (h2 >> 19)) ^ ((h3 << 26) | (h3 >> 6))); + } + + /// + /// + /// Checks if a given flag is set + /// + protected virtual bool GetFlagSet(int flag) { + return((flag & Flags) != 0); + } + + protected Font GetFont(bool boldFont) { + if (boldFont) + return GridEntryHost.GetBoldFont(); + else + return GridEntryHost.GetBaseFont(); + } + + protected IntPtr GetHfont(bool boldFont) { + if (boldFont) + return GridEntryHost.GetBoldHfont(); + else + return GridEntryHost.GetBaseHfont(); + } + + /// + /// + /// Retrieves the requested service. This may + /// return null if the requested service is not + /// available. + /// + public virtual object GetService(Type serviceType) { + + if (serviceType == typeof(GridItem)) { + return (GridItem)this; + } + + if (parentPE != null) { + return parentPE.GetService(serviceType); + } + return null; + } + + internal virtual bool NonParentEquals(object obj) { + if (obj == this) return true; + if (obj == null) return false; + if (!(obj is GridEntry)) return false; + GridEntry pe = (GridEntry)obj; + + return pe.PropertyLabel.Equals(this.PropertyLabel) && + pe.PropertyType.Equals(this.PropertyType) && pe.PropertyDepth == this.PropertyDepth; + } + + + /// + /// + /// Paints the label portion of this GridEntry into the given Graphics object. This + /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is + /// to be painted. + /// + public virtual void PaintLabel(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, bool selected, bool paintFullLabel) { + PropertyGridView gridHost = this.GridEntryHost; + Debug.Assert(gridHost != null, "No propEntryHost"); + string strLabel = this.PropertyLabel; + + int borderWidth = gridHost.GetOutlineIconSize()+OUTLINE_ICON_PADDING; + + // fill the background if necessary + Brush bkBrush = selected ? gridHost.GetSelectedItemWithFocusBackBrush(g) : this.GetBackgroundBrush(g); + // if we don't have focus, paint with the line color + if (selected && !hasFocus) { + bkBrush = gridHost.GetLineBrush(g); + } + + bool fBold = ((this.Flags & GridEntry.FLAG_LABEL_BOLD) != 0); + Font font = GetFont(fBold); + + int labelWidth = GetLabelTextWidth(strLabel, g, font); + + int neededWidth = paintFullLabel ? labelWidth : 0; + int stringX = rect.X + this.PropertyLabelIndent; + Brush blank = bkBrush; + + if (paintFullLabel && (neededWidth >= (rect.Width-(stringX+2)))) { + int totalWidth = stringX + neededWidth + PropertyGridView.GDIPLUS_SPACE; // 5 = extra needed to ensure text draws completely and isn't clipped. +#if PBRS_PAINT_DEBUG + blank = Brushes.Green; +#endif + + // blank out the space we're going to use + g.FillRectangle(blank, borderWidth-1, rect.Y, totalWidth-borderWidth+3, rect.Height); + + // draw an end line + Pen linePen = new Pen(gridHost.GetLineColor()); + g.DrawLine(linePen, totalWidth, rect.Y, totalWidth, rect.Height); + linePen.Dispose(); + + // set the new width that we can draw into + rect.Width = totalWidth - rect.X; + } + else { // Normal case -- no pseudo-tooltip for the label + +#if PBRS_PAINT_DEBUG + blank = Brushes.Red; +#endif + // Debug.WriteLine(rect.X.ToString() +" "+ rect.Y.ToString() +" "+ rect.Width.ToString() +" "+ rect.Height.ToString()); + g.FillRectangle(blank, rect.X, rect.Y, rect.Width, rect.Height); + } + + // draw the border stripe on the left + Brush stripeBrush = gridHost.GetLineBrush(g); + g.FillRectangle(stripeBrush, rect.X, rect.Y, borderWidth, rect.Height); + + if (selected && hasFocus) { + g.FillRectangle(gridHost.GetSelectedItemWithFocusBackBrush(g), stringX, rect.Y, rect.Width - stringX - 1, rect.Height); + } + + int maxSpace = Math.Min(rect.Width-stringX-1, labelWidth + PropertyGridView.GDIPLUS_SPACE); + Rectangle textRect = new Rectangle(stringX, rect.Y + 1, maxSpace, rect.Height - 1); + + + if (!Rectangle.Intersect(textRect, clipRect).IsEmpty) { + Region oldClip = g.Clip; + g.SetClip(textRect); + + //We need to Invert color only if in Highcontrast mode, targeting 4.7.1 and above, Gridcategory and not a developer override. This is required to achieve required contrast ratio. + var shouldInvertForHC = colorInversionNeededInHC && (fBold || (selected && !hasFocus)); + + // Do actual drawing + // A brush is needed if using GDI+ only (UseCompatibleTextRendering); if using GDI, only the color is needed. + Color textColor = selected && hasFocus ? gridHost.GetSelectedItemWithFocusForeColor() : shouldInvertForHC ? InvertColor(ownerGrid.LineColor) : g.GetNearestColor(this.LabelTextColor); + + if( this.ownerGrid.UseCompatibleTextRendering ) { + using( Brush textBrush = new SolidBrush(textColor)){ + StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap); + stringFormat.Trimming = StringTrimming.None; + g.DrawString(strLabel, font, textBrush, textRect, stringFormat); + } + } + else{ + TextRenderer.DrawText( g, strLabel, font, textRect, textColor, PropertyGrid.MeasureTextHelper.GetTextRendererFlags() ); + } + #if PBRS_PAINT_DEBUG + textRect.Width --; + textRect.Height--; + g.DrawRectangle(new Pen(Color.Blue), textRect); + #endif + g.SetClip(oldClip, CombineMode.Replace); + oldClip.Dispose(); // clip is actually copied out. + + if (maxSpace <= labelWidth) { + this.labelTipPoint = new Point(stringX+2, rect.Y+1); + } + else { + this.labelTipPoint = InvalidPoint; + } + } + + rect.Y -= 1; + rect.Height += 2; + + PaintOutline(g, rect); + } + + /// + /// + /// Paints the outline portion of this GridEntry into the given Graphics object. This + /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is + /// to be painted. + /// + public virtual void PaintOutline(System.Drawing.Graphics g, Rectangle r) { + // draw tree-view glyphs as triangles on Vista and Windows afterword + // when Vistual style is enabled + if (GridEntryHost.IsExplorerTreeSupported) { + + // size of Explorer Tree style glyph (triangle) is different from +/- glyph, + // so when we change the visual style (such as changing Windows theme), + // we need to recaculate outlineRect + if(!lastPaintWithExplorerStyle) { + outlineRect = Rectangle.Empty; + lastPaintWithExplorerStyle = true; + } + + PaintOutlineWithExplorerTreeStyle(g, r, (DpiHelper.EnableDpiChangedHighDpiImprovements && GridEntryHost!=null) ? this.GridEntryHost.HandleInternal: IntPtr.Zero); + } + // draw tree-view glyphs as +/- + else { + + // size of Explorer Tree style glyph (triangle) is different from +/- glyph, + // so when we change the visual style (such as changing Windows theme), + // we need to recaculate outlineRect + if (lastPaintWithExplorerStyle) { + outlineRect = Rectangle.Empty; + lastPaintWithExplorerStyle = false; + } + + PaintOutlineWithClassicStyle(g, r); + } + } + + private void PaintOutlineWithExplorerTreeStyle(System.Drawing.Graphics g, Rectangle r, IntPtr handle) { + if (this.Expandable) { + bool fExpanded = this.InternalExpanded; + Rectangle outline = this.OutlineRect; + + // make sure we're in our bounds + outline = Rectangle.Intersect(r, outline); + if (outline.IsEmpty) return; + + VisualStyleElement element = null; + if (fExpanded) + element = VisualStyleElement.ExplorerTreeView.Glyph.Opened; + else + element = VisualStyleElement.ExplorerTreeView.Glyph.Closed; + + // Invert color if it is not overriden by developer. + if (colorInversionNeededInHC) { + Color textColor = InvertColor(ownerGrid.LineColor); + if (g != null) { + Brush b = new SolidBrush(textColor); + g.FillRectangle(b, outline); + b.Dispose(); + } + } + + VisualStyleRenderer explorerTreeRenderer = new VisualStyleRenderer(element); + explorerTreeRenderer.DrawBackground(g, outline, handle); + } + } + + private void PaintOutlineWithClassicStyle(System.Drawing.Graphics g, Rectangle r) { + // draw outline box. + if (this.Expandable) { + bool fExpanded = this.InternalExpanded; + Rectangle outline = this.OutlineRect; + + // make sure we're in our bounds + outline = Rectangle.Intersect(r, outline); + if (outline.IsEmpty) return; + + // draw border area box + Brush b = this.GetBackgroundBrush(g); + Pen p; + Color penColor = GridEntryHost.GetTextColor(); + + // inverting text color to back ground to get required contrast ratio + if (colorInversionNeededInHC) { + penColor = InvertColor(ownerGrid.LineColor); + } + else { + // Filling rectangle as it was in all cases where we do not invert colors. + g.FillRectangle(b, outline); + } + + + if (penColor.IsSystemColor) { + p = SystemPens.FromSystemColor(penColor); + } + else { + p = new Pen(penColor); + } + + g.DrawRectangle(p, outline.X, outline.Y, outline.Width - 1, outline.Height - 1); + + // draw horizontal line for +/- + int indent = 2; + g.DrawLine(p, outline.X + indent,outline.Y + outline.Height / 2, + outline.X + outline.Width - indent - 1,outline.Y + outline.Height/2); + + // draw vertical line to make a + + if (!fExpanded) { + g.DrawLine(p, outline.X + outline.Width/2, outline.Y + indent, + outline.X + outline.Width/2, outline.Y + outline.Height - indent - 1); + } + + if (!penColor.IsSystemColor) { + p.Dispose(); + } + } + } + + /// + /// + /// Paints the value portion of this GridEntry into the given Graphics object. This + /// is called by the GridEntry host (the PropertyGridView) when this GridEntry is + /// to be painted. + /// + public virtual void PaintValue(object val, System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, PaintValueFlags paintFlags) { + PropertyGridView gridHost = this.GridEntryHost; + Debug.Assert(gridHost != null, "No propEntryHost"); + int cPaint = 0; + + Color textColor = gridHost.GetTextColor(); + if (this.ShouldRenderReadOnly) { + textColor = GridEntryHost.GrayTextColor; + } + + string strValue; + + if ((paintFlags & PaintValueFlags.FetchValue) != PaintValueFlags.None) { + if (cacheItems != null && cacheItems.useValueString) { + strValue = cacheItems.lastValueString; + val = cacheItems.lastValue; + } + else { + val = this.PropertyValue; + strValue = GetPropertyTextValue(val); + if (cacheItems == null) { + cacheItems = new CacheItems(); + } + cacheItems.lastValueString = strValue; + cacheItems.useValueString = true; + cacheItems.lastValueTextWidth = -1; + cacheItems.lastValueFont = null; + cacheItems.lastValue = val; + } + } + else { + strValue = GetPropertyTextValue(val); + } + + // paint out the main rect using the appropriate brush + Brush bkBrush = this.GetBackgroundBrush(g); + Debug.Assert(bkBrush != null, "We didn't find a good background brush for PaintValue"); + + if ((paintFlags & PaintValueFlags.DrawSelected) != PaintValueFlags.None) { + bkBrush = gridHost.GetSelectedItemWithFocusBackBrush(g); + textColor = gridHost.GetSelectedItemWithFocusForeColor(); + } + + Brush blank = bkBrush; +#if PBRS_PAINT_DEBUG + blank = Brushes.Yellow; +#endif + //g.FillRectangle(blank, rect.X-1, rect.Y, rect.Width+1, rect.Height); + g.FillRectangle(blank, clipRect); + + if (IsCustomPaint) { + cPaint = gridHost.GetValuePaintIndent(); + Rectangle rectPaint = new Rectangle(rect.X + 1, rect.Y + 1, gridHost.GetValuePaintWidth(), gridHost.GetGridEntryHeight() - 2); + + if (!Rectangle.Intersect(rectPaint, clipRect).IsEmpty) { + UITypeEditor uie = UITypeEditor; + if (uie != null) { + uie.PaintValue(new PaintValueEventArgs(this, val, g, rectPaint)); + } + + // paint a border around the area + rectPaint.Width --; + rectPaint.Height--; + g.DrawRectangle(SystemPens.WindowText, rectPaint); + } + } + + rect.X += cPaint + gridHost.GetValueStringIndent(); + rect.Width -= cPaint + 2 * gridHost.GetValueStringIndent(); + + // bold the property if we need to persist it (e.g. it's not the default value) + bool valueModified = ((paintFlags & PaintValueFlags.CheckShouldSerialize) != PaintValueFlags.None) && ShouldSerializePropertyValue(); + + // If we have text to paint, paint it + if (strValue != null && strValue.Length > 0) { + + Font f = GetFont(valueModified); + + if (strValue.Length > maximumLengthOfPropertyString) + { + strValue = strValue.Substring(0, maximumLengthOfPropertyString); + } + int textWidth = GetValueTextWidth(strValue, g, f); + bool doToolTip = false; + + //subhag (66503) To check if text contains multiple lines + // + if (textWidth >= rect.Width || GetMultipleLines(strValue)) + doToolTip = true; + + if (Rectangle.Intersect(rect, clipRect).IsEmpty) { + return; + } + + // Do actual drawing + + //strValue = ReplaceCRLF(strValue); + + + // AS/URT 55015 + // bump the text down 2 pixels and over 1 pixel. + if ((paintFlags & PaintValueFlags.PaintInPlace) != PaintValueFlags.None) { + rect.Offset(1, 2); + } + else { + // only go down one pixel when we're painting in the listbox + rect.Offset(1, 1); + } + + Matrix m = g.Transform; + IntPtr hdc = g.GetHdc(); + IntNativeMethods.RECT lpRect = IntNativeMethods.RECT.FromXYWH(rect.X + (int)m.OffsetX + 2, rect.Y + (int)m.OffsetY - 1, rect.Width - 4, rect.Height); + IntPtr hfont = GetHfont(valueModified); + + int oldTextColor = 0; + int oldBkColor = 0; + + Color bkColor = ((paintFlags & PaintValueFlags.DrawSelected) != PaintValueFlags.None) ? GridEntryHost.GetSelectedItemWithFocusBackColor() : GridEntryHost.BackColor; + + try { + oldTextColor = SafeNativeMethods.SetTextColor(new HandleRef(g, hdc), SafeNativeMethods.RGBToCOLORREF(textColor.ToArgb())); + oldBkColor = SafeNativeMethods.SetBkColor(new HandleRef(g, hdc), SafeNativeMethods.RGBToCOLORREF(bkColor.ToArgb())); + hfont = SafeNativeMethods.SelectObject(new HandleRef(g, hdc), new HandleRef(null, hfont)); + int format = IntNativeMethods.DT_EDITCONTROL | IntNativeMethods.DT_EXPANDTABS | IntNativeMethods.DT_NOCLIP | IntNativeMethods.DT_SINGLELINE | IntNativeMethods.DT_NOPREFIX; + if (gridHost.DrawValuesRightToLeft) { + format |= IntNativeMethods.DT_RIGHT | IntNativeMethods.DT_RTLREADING; + } + + // For password mode, Replace the string value either with * or a bullet, depending on the OS platform + if (ShouldRenderPassword) { + + if (passwordReplaceChar == (char)0) { + if (Environment.OSVersion.Version.Major > 4) { + passwordReplaceChar = (char)0x25CF; // Bullet is 2022, but edit box uses round circle 25CF + } + else { + passwordReplaceChar = '*'; + } + } + + strValue = new string(passwordReplaceChar, strValue.Length); + } + + IntUnsafeNativeMethods.DrawText(new HandleRef(g, hdc), strValue, ref lpRect, format); + } + finally { + SafeNativeMethods.SetTextColor(new HandleRef(g, hdc), oldTextColor); + SafeNativeMethods.SetBkColor(new HandleRef(g, hdc), oldBkColor); + hfont = SafeNativeMethods.SelectObject(new HandleRef(g, hdc), new HandleRef(null, hfont)); + g.ReleaseHdcInternal(hdc); + } + + + #if PBRS_PAINT_DEBUG + rect.Width --; + rect.Height--; + g.DrawRectangle(new Pen(Color.Purple), rect); + #endif + + if (doToolTip) { + this.ValueToolTipLocation = new Point(rect.X+2, rect.Y-1); + } + else { + this.ValueToolTipLocation = InvalidPoint; + } + } + + return; + } + + public virtual bool OnComponentChanging() { + if (ComponentChangeService != null) { + try { + ComponentChangeService.OnComponentChanging(GetValueOwner(), PropertyDescriptor); + } + catch (CheckoutException coEx) { + if (coEx == CheckoutException.Canceled) { + return false; + } + throw coEx; + } + } + return true; + } + + public virtual void OnComponentChanged() { + if (ComponentChangeService != null) { + ComponentChangeService.OnComponentChanged(GetValueOwner(), PropertyDescriptor, null, null); + } + } + + /// + /// + /// Called when the label portion of this GridEntry is clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnLabelClick(e) if this is overrideen. + /// + protected virtual void OnLabelClick(EventArgs e) { + RaiseEvent(EVENT_LABEL_CLICK, e); + } + + /// + /// + /// Called when the label portion of this GridEntry is double-clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnLabelDoubleClick(e) if this is overrideen. + /// + protected virtual void OnLabelDoubleClick(EventArgs e) { + RaiseEvent(EVENT_LABEL_DBLCLICK, e); + } + + /// + /// + /// Called when the GridEntry is clicked. + /// + public virtual bool OnMouseClick(int x, int y, int count, MouseButtons button) { + // where are we at? + PropertyGridView gridHost = this.GridEntryHost; + Debug.Assert(gridHost != null, "No prop entry host!"); + + // make sure it's the left button + if ((button & MouseButtons.Left) != MouseButtons.Left) { + return false; + } + + + int labelWidth = gridHost.GetLabelWidth(); + + // are we in the label? + if (x >= 0 && x <= labelWidth) { + + // are we on the outline? + if (Expandable) { + Rectangle outlineRect = OutlineRect; + if (outlineRect.Contains(x, y)) { + if (count % 2 == 0) { + OnOutlineDoubleClick(EventArgs.Empty); + } + else { + OnOutlineClick(EventArgs.Empty); + } + return true; + } + } + + if (count % 2 == 0) { + OnLabelDoubleClick(EventArgs.Empty); + } + else { + OnLabelClick(EventArgs.Empty); + } + return true; + } + + // are we in the value? + labelWidth += gridHost.GetSplitterWidth(); + if (x >= labelWidth && x <= labelWidth + gridHost.GetValueWidth()) { + if (count % 2 == 0) { + OnValueDoubleClick(EventArgs.Empty); + } + else { + OnValueClick(EventArgs.Empty); + } + return true; + } + return false; + } + + /// + /// + /// Called when the outline icon portion of this GridEntry is clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnOutlineClick(e) if this is overrideen. + /// + protected virtual void OnOutlineClick(EventArgs e) { + RaiseEvent(EVENT_OUTLINE_CLICK, e); + } + + /// + /// + /// Called when the outline icon portion of this GridEntry is double-clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnOutlineDoubleClick(e) if this is overrideen. + /// + protected virtual void OnOutlineDoubleClick(EventArgs e) { + RaiseEvent(EVENT_OUTLINE_DBLCLICK, e); + } + + /// + /// + /// Called when RecreateChildren is called. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnOutlineDoubleClick(e) if this is overrideen. + /// + protected virtual void OnRecreateChildren(GridEntryRecreateChildrenEventArgs e) { + Delegate handler = GetEventHandler(EVENT_RECREATE_CHILDREN); + if (handler != null) ((GridEntryRecreateChildrenEventHandler)handler)(this, e); + } + + /// + /// + /// Called when the value portion of this GridEntry is clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnValueClick(e) if this is overrideen. + /// + protected virtual void OnValueClick(EventArgs e) { + RaiseEvent(EVENT_VALUE_CLICK, e); + } + + /// + /// + /// Called when the value portion of this GridEntry is clicked. + /// Default implmentation fired the event to any listeners, so be sure + /// to call base.OnValueDoubleClick(e) if this is overrideen. + /// + protected virtual void OnValueDoubleClick(EventArgs e) { + RaiseEvent(EVENT_VALUE_DBLCLICK, e); + } + + + + internal bool OnValueReturnKey() { + return NotifyValue(NOTIFY_RETURN); + } + + /// + /// + /// Sets the specified flag + /// + protected virtual void SetFlag(int flag, bool fVal) { + SetFlag(flag, (fVal ? flag : 0)); + } + + /// + /// + /// Sets the default child of this entry, given a valid value mask. + /// + protected virtual void SetFlag(int flag_valid, int flag, bool fVal) { + SetFlag(flag_valid | flag, + flag_valid | (fVal ? flag : 0)); + } + + /// + /// + /// Sets the value of a flag + /// + protected virtual void SetFlag(int flag, int val) { + Flags = (Flags & ~(flag)) | val; + } + + public override bool Select() { + if (Disposed) { + return false; + } + + try { + GridEntryHost.SelectedGridEntry = this; + return true; + } + catch { + } + return false; + } + + /// + /// + /// Checks if this value should be persisited. + /// + internal virtual bool ShouldSerializePropertyValue() { + + if (cacheItems != null) { + if (cacheItems.useShouldSerialize) { + return cacheItems.lastShouldSerialize; + } + else { + cacheItems.lastShouldSerialize = NotifyValue(NOTIFY_SHOULD_PERSIST); + cacheItems.useShouldSerialize = true; + } + } + else { + cacheItems = new CacheItems(); + cacheItems.lastShouldSerialize = NotifyValue(NOTIFY_SHOULD_PERSIST); + cacheItems.useShouldSerialize = true; + } + return cacheItems.lastShouldSerialize; + } + + private PropertyDescriptor[] SortParenProperties(PropertyDescriptor[] props) { + + PropertyDescriptor[] newProps = null; + int newPos = 0; + + + // first scan the list and move any parentesized properties to the front. + for (int i = 0; i < props.Length; i++) { + if (((ParenthesizePropertyNameAttribute)props[i].Attributes[typeof(ParenthesizePropertyNameAttribute)]).NeedParenthesis) { + if (newProps == null) { + newProps = new PropertyDescriptor[props.Length]; + } + newProps[newPos++] = props[i]; + props[i] = null; + } + } + + + // second pass, copy any that didn't have the parens. + if (newPos > 0) { + for (int i = 0; i < props.Length; i++) { + if (props[i] != null) { + newProps[newPos++] = props[i]; + } + } + props = newProps; + } + return props; + } + + /// + /// + /// Sends a notify message to this GridEntry, and returns the success result + /// + internal virtual bool NotifyValueGivenParent(object obj, int type) { + return false; + } + + /// + /// + /// Sends a notify message to the child GridEntry, and returns the success result + /// + internal virtual bool NotifyChildValue(GridEntry pe, int type) { + + return pe.NotifyValueGivenParent(pe.GetValueOwner(),type); + } + + internal virtual bool NotifyValue(int type) { + // KILLME, Microsoft, more spagetti + if (parentPE == null) { + return true; + } + else { + return parentPE.NotifyChildValue(this, type); + } + } + + internal void RecreateChildren() { + RecreateChildren(-1); + } + + internal void RecreateChildren(int oldCount) { + + // cause the flags to be rebuilt as well... + bool wasExpanded = this.InternalExpanded || oldCount > 0; + + if (oldCount == -1) { + oldCount = this.VisibleChildCount; + } + + ResetState(); + if (oldCount == 0) { + return; + } + + foreach(GridEntry child in ChildCollection) { + child.RecreateChildren(); + } + + DisposeChildren(); + this.InternalExpanded = wasExpanded; + OnRecreateChildren(new GridEntryRecreateChildrenEventArgs(oldCount, VisibleChildCount)); + } + + /// + /// + /// Refresh the current GridEntry's value and it's children + /// + public virtual void Refresh() { + + Type type = this.PropertyType; + if (type != null && type.IsArray) { + CreateChildren(true); + } + + if (this.childCollection != null) { + + // check to see if the value has changed. + // + if (this.InternalExpanded && cacheItems != null && cacheItems.lastValue != null && cacheItems.lastValue != this.PropertyValue) { + ClearCachedValues(); + RecreateChildren(); + return; + } + else if (this.InternalExpanded) { + // otherwise just do a refresh. + IEnumerator childEnum = childCollection.GetEnumerator(); + while (childEnum.MoveNext()) { + object o = childEnum.Current; + Debug.Assert(o != null, "Collection contains a null element. But how? Garbage collector hole? GDI+ corrupting memory?"); + GridEntry e = (GridEntry) o; + e.Refresh(); + } + } + else { + DisposeChildren(); + } + } + + ClearCachedValues(); + } + + public virtual void RemoveOnLabelClick(EventHandler h) { + RemoveEventHandler(EVENT_LABEL_CLICK, h); + } + public virtual void RemoveOnLabelDoubleClick(EventHandler h) { + RemoveEventHandler(EVENT_LABEL_DBLCLICK, h); + } + + public virtual void RemoveOnValueClick(EventHandler h) { + RemoveEventHandler(EVENT_VALUE_CLICK, h); + } + + public virtual void RemoveOnValueDoubleClick(EventHandler h) { + RemoveEventHandler(EVENT_VALUE_DBLCLICK, h); + } + + public virtual void RemoveOnOutlineClick(EventHandler h) { + RemoveEventHandler(EVENT_OUTLINE_CLICK, h); + } + + public virtual void RemoveOnOutlineDoubleClick(EventHandler h) { + RemoveEventHandler(EVENT_OUTLINE_DBLCLICK, h); + } + + public virtual void RemoveOnRecreateChildren(GridEntryRecreateChildrenEventHandler h) { + RemoveEventHandler(EVENT_RECREATE_CHILDREN, h); + } + + /* + private string ReplaceCRLF(string str) { + str = str.Replace('\r', (char)1); + str = str.Replace('\n', (char)1); + return str; + } + */ + + protected void ResetState() { + this.Flags = 0; + ClearCachedValues(); + } + + /// + /// + /// Sets the value of this GridEntry from text + /// + public virtual bool SetPropertyTextValue(string str) { + bool fChildrenPrior = (childCollection != null && childCollection.Count > 0); + this.PropertyValue = ConvertTextToValue(str); + CreateChildren(); + bool fChildrenAfter = (childCollection != null && childCollection.Count > 0); + return(fChildrenPrior != fChildrenAfter); + } + + public override string ToString() { + return GetType().FullName + " " + this.PropertyLabel; + } + + + +#if !DONT_SUPPORT_ADD_EVENT_HANDLER + private EventEntry eventList; + + protected virtual void AddEventHandler(object key, Delegate handler) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (handler == null) return; + for (EventEntry e = eventList; e != null; e = e.next) { + if (e.key == key) { + e.handler = Delegate.Combine(e.handler, handler); + return; + } + } + eventList = new EventEntry(eventList, key, handler); + } + } + + protected virtual void RaiseEvent(object key, EventArgs e) { + Delegate handler = GetEventHandler(key); + if (handler != null) ((EventHandler)handler)(this, e); + } + + protected virtual Delegate GetEventHandler(object key) { + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + for (EventEntry e = eventList; e != null; e = e.next) { + if (e.key == key) return e.handler; + } + return null; + } + } + + protected virtual void RemoveEventHandler(object key, Delegate handler) { + // Locking this here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (handler == null) return; + for (EventEntry e = eventList, prev = null; e != null; prev = e, e = e.next) { + if (e.key == key) { + e.handler = Delegate.Remove(e.handler, handler); + if (e.handler == null) { + if (prev == null) { + eventList = e.next; + } + else { + prev.next = e.next; + } + } + return; + } + } + } + } + + protected virtual void RemoveEventHandlers() { + eventList = null; + } + + /// + /// + /// + private sealed class EventEntry { + internal EventEntry next; + internal object key; + internal Delegate handler; + + internal EventEntry(EventEntry next, object key, Delegate handler) { + this.next = next; + this.key = key; + this.handler = handler; + } + } +#endif + + [ComVisible(true)] + public class GridEntryAccessibleObject : AccessibleObject { + + protected GridEntry owner = null; + private delegate void SelectDelegate(AccessibleSelection flags); + private int[] runtimeId = null; // Used by UIAutomation + + public GridEntryAccessibleObject(GridEntry owner) : base() { + Debug.Assert(owner != null, "GridEntryAccessibleObject must have a valid owner GridEntry"); + this.owner = owner; + } + + public override Rectangle Bounds { + get { + return PropertyGridView.AccessibilityGetGridEntryBounds(owner); + } + } + + public override string DefaultAction { + get { + if (!owner.Expandable) { + return base.DefaultAction; + } + else if (owner.Expanded) { + return SR.GetString(SR.AccessibleActionCollapse); + } + else { + return SR.GetString(SR.AccessibleActionExpand); + } + } + } + + public override string Description { + get { + return owner.PropertyDescription; + } + } + + public override string Help { + get { + if (AccessibilityImprovements.Level1) { + return owner.PropertyDescription; + } + else { + return base.Help; + } + } + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + switch(direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + var parentGridEntry = owner.ParentGridEntry; + if (parentGridEntry != null) { + if (parentGridEntry is SingleSelectRootGridEntry) { + return owner.OwnerGrid.GridViewAccessibleObject; + } + else { + return parentGridEntry.AccessibilityObject; + } + } + + return Parent; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + return Navigate(AccessibleNavigation.Previous); + case UnsafeNativeMethods.NavigateDirection.NextSibling: + return Navigate(AccessibleNavigation.Next); + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Return the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + if (AccessibilityImprovements.Level3) { + return (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + } + + return base.FragmentRoot; + } + } + + #region IAccessibleEx - patterns and properties + + internal override bool IsIAccessibleExSupported() { + if (owner.Expandable && AccessibilityImprovements.Level1) { + return true; + } + else { + return false; + } + } + + internal override int[] RuntimeId { + get { + if (runtimeId == null) { + // we need to provide a unique ID + // others are implementing this in the same manner + // first item is static - 0x2a + // second item can be anything, but it's good to supply HWND + // third and others are optional, but in case of GridItem we need it, to make it unique + // grid items are not controls, they don't have hwnd - we use hwnd of PropertyGridView + + runtimeId = new int[3]; + runtimeId[0] = 0x2a; + runtimeId[1] = (int)(long)owner.GridEntryHost.Handle; + runtimeId[2] = this.GetHashCode(); + } + + return runtimeId; + } + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_ControlTypePropertyId: + if (AccessibilityImprovements.Level3) { + // In Level 3 the accessible hierarchy is changed so we cannot use Button type + // for the grid items to not break automation logic that searches for the first + // button in the PropertyGridView to show dialog/drop-down. In Level < 3 action + // button is one of the first children of PropertyGridView. + return NativeMethods.UIA_DataItemControlTypeId; + } + + return NativeMethods.UIA_ButtonControlTypeId; + case NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId: + return (Object)IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + + if (AccessibilityImprovements.Level3) { + switch (propertyID) { + case NativeMethods.UIA_AccessKeyPropertyId: + return string.Empty; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return owner.hasFocus; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (this.State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return true; + case NativeMethods.UIA_AutomationIdPropertyId: + return GetHashCode().ToString(); + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (this.State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + case NativeMethods.UIA_LegacyIAccessibleRolePropertyId: + return Role; + case NativeMethods.UIA_LegacyIAccessibleDefaultActionPropertyId: + return DefaultAction; + default: + return base.GetPropertyValue(propertyID); + } + } + + return null; + } + + internal override bool IsPatternSupported(int patternId) { + if (owner.Expandable && + patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + return true; + } + + if (AccessibilityImprovements.Level3 && ( + patternId == NativeMethods.UIA_InvokePatternId || + patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)) { + return true; + } + + return false; + } + + internal override void Expand() { + if (owner.Expandable && owner.Expanded == false) { + owner.Expanded = true; + } + } + + internal override void Collapse() { + if (owner.Expandable && owner.Expanded == true) { + owner.Expanded = false; + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + if (owner.Expandable) { + return owner.Expanded ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + else { + return UnsafeNativeMethods.ExpandCollapseState.LeafNode; + } + } + } + + #endregion + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + owner.OnOutlineClick(EventArgs.Empty); + } + + public override string Name { + get { + return owner.PropertyLabel; + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return((Control)this.owner.GridEntryHost).AccessibilityObject; + } + } + + private PropertyGridView PropertyGridView { + get { + return(PropertyGridView)((PropertyGridView.PropertyGridViewAccessibleObject)Parent).Owner; + } + } + + public override AccessibleRole Role { + get { + if (AccessibilityImprovements.Level3) { + return AccessibleRole.Cell; + } + else if (AccessibilityImprovements.Level1) { + if (owner.Expandable) { + return AccessibleRole.ButtonDropDownGrid; + } + else { + return AccessibleRole.Cell; + } + } + + return AccessibleRole.Row; + } + } + + public override AccessibleStates State { + get { + AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable; + + // Determine focus + // + if (owner.Focus) { + state |= AccessibleStates.Focused; + } + + // Determine selected + // + Debug.Assert(Parent != null, "GridEntry AO does not have a parent AO"); + PropertyGridView.PropertyGridViewAccessibleObject parent = (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + if (parent.GetSelected() == this) { + state |= AccessibleStates.Selected; + } + + // Determine expanded/collapsed state + // + if (owner.Expandable) { + if (owner.Expanded) { + state |= AccessibleStates.Expanded; + } + else { + state |= AccessibleStates.Collapsed; + } + } + + // Determine readonly/editable state + // + if (owner.ShouldRenderReadOnly) { + state |= AccessibleStates.ReadOnly; + } + + // Determine password state + // + if (owner.ShouldRenderPassword) { + state |= AccessibleStates.Protected; + } + + return state; + } + } + + public override string Value { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return owner.GetPropertyTextValue(); + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + set { + owner.SetPropertyTextValue(value); + } + } + + /// + /// + /// Returns the currently focused child, if any. + /// Returns this if the object itself is focused. + /// + public override AccessibleObject GetFocused() { + + if (owner.Focus) { + return this; + } + else { + return null; + } + } + + + /// + /// + /// Navigate to the next or previous grid entry. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + + PropertyGridView.PropertyGridViewAccessibleObject parent = + (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + + switch (navdir) { + case AccessibleNavigation.Down: + case AccessibleNavigation.Right: + case AccessibleNavigation.Next: + return parent.Next(this.owner); + + case AccessibleNavigation.Up: + case AccessibleNavigation.Left: + case AccessibleNavigation.Previous: + return parent.Previous(this.owner); + + case AccessibleNavigation.FirstChild: + case AccessibleNavigation.LastChild: + // Fall through and return null, + // as this object has no children. + break; + } + + return null; + + } + + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void Select(AccessibleSelection flags) { + + // vs 77963 -- make sure we're on the right thread. + // + if (PropertyGridView.InvokeRequired) { + PropertyGridView.Invoke(new SelectDelegate(this.Select), new object[]{flags}); + return; + } + + // Focus the PropertyGridView window + // + if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) { + bool focused = PropertyGridView.FocusInternal(); + } + + // Select the grid entry + // + if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) { + PropertyGridView.AccessibilitySelect(this.owner); + } + } + + internal override void SetFocus() { + base.SetFocus(); + + if (AccessibilityImprovements.Level3) { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + } + + public class DisplayNameSortComparer : IComparer { + public int Compare(object left, object right) { + // review: (Microsoft) Is CurrentCulture correct here? This was already reviewed as invariant... + return String.Compare(((PropertyDescriptor)left).DisplayName, ((PropertyDescriptor)right).DisplayName, true, CultureInfo.CurrentCulture); + } + } + } + + internal class AttributeTypeSorter : IComparer{ + + private static IDictionary typeIds; + + private static string GetTypeIdString(Attribute a) { + + string result; + object typeId = a.TypeId; + + + if (typeId == null) { + Debug.Fail("Attribute '" + a.GetType().FullName + "' does not have a typeid."); + return ""; + } + + if (typeIds == null) { + typeIds = new Hashtable(); + result = null; + } + else { + result = typeIds[typeId] as string; + } + + if (result == null) { + result = typeId.ToString(); + typeIds[typeId] = result; + } + return result; + } + + public int Compare(object obj1, object obj2) { + Attribute a1 = obj1 as Attribute; + Attribute a2 = obj2 as Attribute; + + if (a1 == null && a2 == null) { + return 0; + } + else if (a1 == null) { + return -1; + } + else if (a2 == null) { + return 1; + } + return String.Compare(AttributeTypeSorter.GetTypeIdString(a1), AttributeTypeSorter.GetTypeIdString(a2), false, CultureInfo.InvariantCulture); + } + } + + internal delegate void GridEntryRecreateChildrenEventHandler(object sender, GridEntryRecreateChildrenEventArgs rce); + + internal class GridEntryRecreateChildrenEventArgs : EventArgs { + public readonly int OldChildCount; + public readonly int NewChildCount; + + public GridEntryRecreateChildrenEventArgs(int oldCount, int newCount) { + this.OldChildCount = oldCount; + this.NewChildCount = newCount; + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntryCollection.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntryCollection.cs new file mode 100644 index 000000000..911ba36d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridEntryCollection.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Collections; + using System.Reflection; + + using System.Drawing.Design; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Drawing; + using Microsoft.Win32; + + internal class GridEntryCollection : GridItemCollection { + + private GridEntry owner; + + public GridEntryCollection(GridEntry owner, GridEntry[] entries) : base(entries) { + this.owner = owner; + } + + public void AddRange(GridEntry[] value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (entries != null) { + GridEntry[] newArray = new GridEntry[entries.Length + value.Length]; + entries.CopyTo(newArray, 0); + value.CopyTo(newArray, entries.Length); + entries = newArray; + } + else { + entries = (GridEntry[])value.Clone(); + } + } + + public void Clear() { + entries = new GridEntry[0]; + } + + public void CopyTo(Array dest, int index) { + entries.CopyTo(dest, index); + } + + internal GridEntry GetEntry(int index) { + return (GridEntry)entries[index]; + } + + internal int GetEntry(GridEntry child) { + return Array.IndexOf(entries, child); + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (disposing) { + if (owner != null && entries != null) { + for (int i = 0; i < entries.Length; i++) { + if (entries[i] != null) { + ((GridEntry)entries[i]).Dispose(); + entries[i] = null; + } + } + entries = new GridEntry[0]; + } + } + } + + ~GridEntryCollection() { + Dispose(false); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridErrorDlg.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridErrorDlg.cs new file mode 100644 index 000000000..3582ef3bd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridErrorDlg.cs @@ -0,0 +1,422 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.IO; + using System.Drawing; + using Microsoft.Win32; + using Message = System.Windows.Forms.Message; + using System.Drawing.Drawing2D; + + /// + /// + /// Implements a dialog that is displayed when an unhandled exception occurs in + /// a thread. This dialog's width is defined by the summary message + /// in the top pane. We don't restrict dialog width in any way. + /// Use caution and check at all DPI scaling factors if adding a new message + /// to be displayed in the top pane. + /// + internal class GridErrorDlg : Form { + private TableLayoutPanel overarchingTableLayoutPanel; + private TableLayoutPanel buttonTableLayoutPanel; + private PictureBox pictureBox; + private Label lblMessage; + private DetailsButton detailsBtn; + private Button cancelBtn; + private Button okBtn; + private TableLayoutPanel pictureLabelTableLayoutPanel; + private TextBox details; + + private Bitmap expandImage = null; + private Bitmap collapseImage = null; + private PropertyGrid ownerGrid; + + private bool detailsButtonExpanded = false; + + public bool DetailsButtonExpanded { + get { + return detailsButtonExpanded; + } + } + + public string Details { + set { + this.details.Text = value; + } + } + + + public string Message { + set { + this.lblMessage.Text = value; + } + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We use " " for the text so we leave a small amount of test. + // So we don't have to localize it. + ] + public GridErrorDlg(PropertyGrid owner) { + ownerGrid = owner; + expandImage = new Bitmap(typeof(ThreadExceptionDialog), "down.bmp"); + expandImage.MakeTransparent(); + if (DpiHelper.IsScalingRequired) { + DpiHelper.ScaleBitmapLogicalToDevice(ref expandImage); + } + collapseImage = new Bitmap(typeof(ThreadExceptionDialog), "up.bmp"); + collapseImage.MakeTransparent(); + if (DpiHelper.IsScalingRequired) { + DpiHelper.ScaleBitmapLogicalToDevice(ref collapseImage); + } + + InitializeComponent(); + + foreach( Control c in this.Controls ){ + if( c.SupportsUseCompatibleTextRendering ){ + c.UseCompatibleTextRenderingInt = ownerGrid.UseCompatibleTextRendering; + } + } + + pictureBox.Image = SystemIcons.Warning.ToBitmap(); + detailsBtn.Text = " " + SR.GetString(SR.ExDlgShowDetails); + + details.AccessibleName = SR.GetString(SR.ExDlgDetailsText); + + okBtn.Text = SR.GetString(SR.ExDlgOk); + cancelBtn.Text = SR.GetString(SR.ExDlgCancel); + detailsBtn.Image = expandImage; + } + + /// + /// + /// Called when the details button is clicked. + /// + private void DetailsClick(object sender, EventArgs devent) { + int delta = details.Height + 8; + + if (details.Visible) { + detailsBtn.Image = expandImage; + detailsButtonExpanded = false; + Height -= delta; + } + else { + detailsBtn.Image = collapseImage; + detailsButtonExpanded = true; + details.Width = overarchingTableLayoutPanel.Width - details.Margin.Horizontal; + Height += delta; + } + + details.Visible = !details.Visible; + + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + details.TabStop = !details.TabStop; + + if (details.Visible) { + details.Focus(); + } + } + } + + /// + /// Tells whether the current resources for this dll have been + /// localized for a RTL language. + /// + private static bool IsRTLResources { + get { + return SR.GetString(SR.RTL) != "RTL_False"; + } + } + + private void InitializeComponent() { + if (IsRTLResources) { + this.RightToLeft = RightToLeft.Yes; + } + + this.detailsBtn = new DetailsButton(this); + this.overarchingTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.buttonTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.okBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.pictureLabelTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.lblMessage = new System.Windows.Forms.Label(); + this.pictureBox = new System.Windows.Forms.PictureBox(); + this.details = new System.Windows.Forms.TextBox(); + this.overarchingTableLayoutPanel.SuspendLayout(); + this.buttonTableLayoutPanel.SuspendLayout(); + this.pictureLabelTableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit(); + this.SuspendLayout(); + // + // lblMessage + // + this.lblMessage.AutoSize = true; + this.lblMessage.Location = new System.Drawing.Point(73, 30); + this.lblMessage.Margin = new System.Windows.Forms.Padding(3, 30, 3, 0); + this.lblMessage.Name = "lblMessage"; + this.lblMessage.Size = new System.Drawing.Size(208, 43); + this.lblMessage.TabIndex = 0; + // + // pictureBox + // + this.pictureBox.Location = new System.Drawing.Point(3, 3); + this.pictureBox.Name = "pictureBox"; + this.pictureBox.Size = new System.Drawing.Size(64, 64); + this.pictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.pictureBox.TabIndex = 5; + this.pictureBox.TabStop = false; + this.pictureBox.AutoSize = true; + // + // detailsBtn + // + this.detailsBtn.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.detailsBtn.Location = new System.Drawing.Point(3, 3); + this.detailsBtn.Margin = new System.Windows.Forms.Padding(12, 3, 29, 3); + this.detailsBtn.Name = "detailsBtn"; + this.detailsBtn.Size = new System.Drawing.Size(100, 23); + this.detailsBtn.TabIndex = 4; + this.detailsBtn.Click += new System.EventHandler(this.DetailsClick); + // + // overarchingTableLayoutPanel + // + this.overarchingTableLayoutPanel.AutoSize = true; + this.overarchingTableLayoutPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.overarchingTableLayoutPanel.ColumnCount = 1; + this.overarchingTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.overarchingTableLayoutPanel.Controls.Add(this.buttonTableLayoutPanel, 0, 1); + this.overarchingTableLayoutPanel.Controls.Add(this.pictureLabelTableLayoutPanel, 0, 0); + this.overarchingTableLayoutPanel.Location = new System.Drawing.Point(1, 0); + this.overarchingTableLayoutPanel.MinimumSize = new System.Drawing.Size(279, 50); + this.overarchingTableLayoutPanel.Name = "overarchingTableLayoutPanel"; + this.overarchingTableLayoutPanel.RowCount = 2; + this.overarchingTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.overarchingTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.overarchingTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.overarchingTableLayoutPanel.Size = new System.Drawing.Size(290, 108); + this.overarchingTableLayoutPanel.TabIndex = 6; + // + // buttonTableLayoutPanel + // + this.buttonTableLayoutPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.buttonTableLayoutPanel.AutoSize = true; + this.buttonTableLayoutPanel.ColumnCount = 3; + this.overarchingTableLayoutPanel.SetColumnSpan(this.buttonTableLayoutPanel, 2); + this.buttonTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.buttonTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.buttonTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.buttonTableLayoutPanel.Controls.Add(this.cancelBtn, 2, 0); + this.buttonTableLayoutPanel.Controls.Add(this.okBtn, 1, 0); + this.buttonTableLayoutPanel.Controls.Add(this.detailsBtn, 0, 0); + this.buttonTableLayoutPanel.Location = new System.Drawing.Point(0, 79); + this.buttonTableLayoutPanel.Name = "buttonTableLayoutPanel"; + this.buttonTableLayoutPanel.RowCount = 1; + this.buttonTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.buttonTableLayoutPanel.Size = new System.Drawing.Size(290, 29); + this.buttonTableLayoutPanel.TabIndex = 8; + // + // okBtn + // + this.okBtn.AutoSize = true; + this.okBtn.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okBtn.Location = new System.Drawing.Point(131, 3); + this.okBtn.Name = "okBtn"; + this.okBtn.Size = new System.Drawing.Size(75, 23); + this.okBtn.TabIndex = 1; + this.okBtn.Click += new System.EventHandler(this.OnButtonClick); + // + // cancelBtn + // + this.cancelBtn.AutoSize = true; + this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelBtn.Location = new System.Drawing.Point(212, 3); + this.cancelBtn.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(75, 23); + this.cancelBtn.TabIndex = 2; + this.cancelBtn.Click += new System.EventHandler(this.OnButtonClick); + // + // pictureLabelTableLayoutPanel + // + this.pictureLabelTableLayoutPanel.AutoSize = true; + this.pictureLabelTableLayoutPanel.AutoSizeMode = Forms.AutoSizeMode.GrowOnly; + this.pictureLabelTableLayoutPanel.ColumnCount = 2; + this.pictureLabelTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.pictureLabelTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.pictureLabelTableLayoutPanel.Controls.Add(this.lblMessage, 1, 0); + this.pictureLabelTableLayoutPanel.Controls.Add(this.pictureBox, 0, 0); + this.pictureLabelTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.pictureLabelTableLayoutPanel.Location = new System.Drawing.Point(3, 3); + this.pictureLabelTableLayoutPanel.Name = "pictureLabelTableLayoutPanel"; + this.pictureLabelTableLayoutPanel.RowCount = 1; + this.pictureLabelTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); + this.pictureLabelTableLayoutPanel.Size = new System.Drawing.Size(284, 73); + this.pictureLabelTableLayoutPanel.TabIndex = 4; + // + // details + // + this.details.Location = new System.Drawing.Point(4, 114); + this.details.Multiline = true; + this.details.Name = "details"; + this.details.ReadOnly = true; + this.details.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.details.Size = new System.Drawing.Size(273, 100); + this.details.TabIndex = 3; + this.details.TabStop = false; + this.details.Visible = false; + // + // Form1 + // + this.AcceptButton = this.okBtn; + this.AutoSize = true; + this.AutoScaleMode = Forms.AutoScaleMode.Font; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.CancelButton = this.cancelBtn; + this.ClientSize = new System.Drawing.Size(299, 113); + this.Controls.Add(this.details); + this.Controls.Add(this.overarchingTableLayoutPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Form1"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.overarchingTableLayoutPanel.ResumeLayout(false); + this.overarchingTableLayoutPanel.PerformLayout(); + this.buttonTableLayoutPanel.ResumeLayout(false); + this.buttonTableLayoutPanel.PerformLayout(); + this.pictureLabelTableLayoutPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + } + + private void OnButtonClick(object s, EventArgs e) { + DialogResult = ((Button)s).DialogResult; + Close(); + } + + protected override void OnVisibleChanged(EventArgs e) { + if (this.Visible) { + // make sure the details button is sized properly + // + using (Graphics g = CreateGraphics()) { + SizeF sizef = PropertyGrid.MeasureTextHelper.MeasureText( this.ownerGrid, g, detailsBtn.Text, detailsBtn.Font); + int detailsWidth = (int) Math.Ceiling(sizef.Width); + detailsWidth += detailsBtn.Image.Width; + detailsBtn.Width = (int) Math.Ceiling(detailsWidth * (ownerGrid.UseCompatibleTextRendering ? 1.15f : 1.4f)); + detailsBtn.Height = okBtn.Height; + } + + // Update the location of the TextBox details + int x = details.Location.X; + int y = detailsBtn.Location.Y + detailsBtn.Height + detailsBtn.Margin.Bottom; + + // Location is relative to its parent, + // therefore, we need to take its parent into consideration + Control parent = detailsBtn.Parent; + while(parent != null && !(parent is Form)) { + y += parent.Location.Y; + parent = parent.Parent; + } + + details.Location = new System.Drawing.Point(x, y); + + if (details.Visible) { + DetailsClick(details, EventArgs.Empty); + } + } + okBtn.Focus(); + } + } + + internal class DetailsButton: Button { + private GridErrorDlg parent; + public DetailsButton(GridErrorDlg form){ + parent = form; + } + + public bool Expanded { + get { + return parent.DetailsButtonExpanded; + } + } + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level1) { + return new DetailsButtonAccessibleObject(this); + } + else { + return base.CreateAccessibilityInstance(); + } + } + } + + internal class DetailsButtonAccessibleObject: Control.ControlAccessibleObject { + + private DetailsButton ownerItem = null; + + public DetailsButtonAccessibleObject(DetailsButton owner): base(owner) { + ownerItem = owner; + } + + internal override bool IsIAccessibleExSupported() { + Debug.Assert(ownerItem != null, "AccessibleObject owner cannot be null"); + return true; + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ButtonControlTypeId; + } + else { + return base.GetPropertyValue(propertyID); + } + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return ownerItem.Expanded ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + internal override void Expand() { + if (ownerItem !=null && !ownerItem.Expanded) { + DoDefaultAction(); + } + } + + internal override void Collapse() { + if (ownerItem != null && ownerItem.Expanded) { + DoDefaultAction(); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridToolTip.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridToolTip.cs new file mode 100644 index 000000000..65fdca7dd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/GridToolTip.cs @@ -0,0 +1,259 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + + using System; + using System.Collections; + + using System.Windows.Forms; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.IO; + using System.Drawing; + using Microsoft.Win32; + using Message = System.Windows.Forms.Message; + + internal class GridToolTip : Control { + + Control[] controls; + string toolTipText; + NativeMethods.TOOLINFO_T[] toolInfos; + bool dontShow; + Point lastMouseMove = Point.Empty; + private int maximumToolTipLength = 1000; + private bool _positioned = false; + + internal GridToolTip(Control[] controls) { + this.controls = controls; + SetStyle(ControlStyles.UserPaint, false); + this.Font = controls[0].Font; + this.toolInfos = new NativeMethods.TOOLINFO_T[controls.Length]; + + for (int i = 0; i < controls.Length; i++) { + controls[i].HandleCreated += new EventHandler(this.OnControlCreateHandle); + controls[i].HandleDestroyed += new EventHandler(this.OnControlDestroyHandle); + + if (controls[i].IsHandleCreated) { + SetupToolTip(controls[i]); + } + } + } + + public string ToolTip{ + get { + return toolTipText; + } + set { + if (this.IsHandleCreated || !String.IsNullOrEmpty(value)) { + this.Reset(); + } + + if (value != null && value.Length > maximumToolTipLength) + { + //Let the user know the text was truncated by throwing on an ellipsis + value = value.Substring(0, maximumToolTipLength) + "..."; + } + this.toolTipText = value; + + if (this.IsHandleCreated) { + + bool visible = this.Visible; + + if (visible){ + this.Visible = false; + } + + // here's a hack. if we give + // the tooltip an empty string, it won't come back + // so we just force it hidden instead + // + if (value == null || value.Length == 0){ + dontShow = true; + value = ""; + } + else{ + dontShow = false; + } + + for (int i = 0; i < controls.Length; i++) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATETIPTEXT, 0, GetTOOLINFO(controls[i])); + } + + if (visible && !dontShow){ + this.Visible = true; + } + + } + } + } + + + /// + /// + /// The createParams to create the window. + /// + /// + protected override CreateParams CreateParams { + get { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + CreateParams cp = new CreateParams(); + cp.Parent = IntPtr.Zero; + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + cp.Style |= (NativeMethods.TTS_ALWAYSTIP | NativeMethods.TTS_NOPREFIX); + cp.ExStyle = 0; + cp.Caption = this.ToolTip; + return cp; + } + } + + private NativeMethods.TOOLINFO_T GetTOOLINFO(Control c) { + + int index = Array.IndexOf(controls, c); + + Debug.Assert(index != -1, "Failed to find control in tooltip array"); + + if (toolInfos[index] == null){ + toolInfos[index] = new NativeMethods.TOOLINFO_T(); + toolInfos[index].cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_T)); + toolInfos[index].uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + } + toolInfos[index].lpszText = this.toolTipText; + toolInfos[index].hwnd = c.Handle; + toolInfos[index].uId = c.Handle; + return toolInfos[index]; + } + + /* + private bool MouseMoved(Message msg){ + bool moved = true; + + Point newMove = new Point(NativeMethods.Util.LOWORD(msg.LParam), NativeMethods.Util.HIWORD(msg.LParam)); + + // check if the mouse has actually moved... + if (lastMouseMove == newMove){ + moved = false; + } + + lastMouseMove = newMove; + return moved; + } + */ + + private void OnControlCreateHandle(object sender, EventArgs e){ + SetupToolTip((Control)sender); + } + + private void OnControlDestroyHandle(object sender, EventArgs e){ + if (IsHandleCreated) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_DELTOOL, 0, GetTOOLINFO((Control)sender)); + } + } + + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + for (int i = 0; i < controls.Length; i++) { + if (controls[i].IsHandleCreated) { + SetupToolTip(controls[i]); + } + } + } + + internal void PositionToolTip(Control parent, Rectangle itemRect) { + if (_positioned && DpiHelper.EnableDpiChangedHighDpiImprovements) { + return; + } + + Visible = false; + + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(itemRect.X, itemRect.Y, itemRect.Width, itemRect.Height); + + SendMessage(NativeMethods.TTM_ADJUSTRECT, 1, ref rect); + + // now offset it back to screen coords + Point locPoint = parent.PointToScreen(new Point(rect.left, rect.top)); + + Location = locPoint; // set the position once so it updates it's size with it's real width. + + int overHang = (Location.X + Size.Width) - SystemInformation.VirtualScreen.Width; + if (overHang > 0) { + locPoint.X -= overHang; + Location = locPoint; + } + + // tell the control we've repositioned it. + Visible = true; + + // _positioned flag is reset to false on mouse move before showing ToolTip with new text. + _positioned = true; + } + + private void SetupToolTip(Control c) { + if (this.IsHandleCreated) { + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOACTIVATE); + + if (0 == (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO(c))) { + Debug.Fail("TTM_ADDTOOL failed for " + c.GetType().Name); + } + + // Setting the max width has the added benefit of enabling multiline + // tool tips! subhag 66503) + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + } + } + + public void Reset(){ + // okay, this resets the tooltip state, + // which can get broken when we leave the window + // then reenter. So we set the tooltip to null, + // update the text, then it back to what it was, so the tooltip + // thinks it's back in the regular state again + // + string oldText = this.ToolTip; + this.toolTipText = ""; + for (int i = 0; i < controls.Length; i++) { + if (0 == (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATETIPTEXT, 0, GetTOOLINFO(controls[i]))) { + //Debug.Fail("TTM_UPDATETIPTEXT failed for " + controls[i].GetType().Name); + } + } + this.toolTipText = oldText; + this.SendMessage(NativeMethods.TTM_UPDATE, 0, 0); + this._positioned = false; + } + + protected override void WndProc(ref Message msg) { + switch (msg.Msg) { + case NativeMethods.WM_SHOWWINDOW: + if (unchecked( (int) (long)msg.WParam) != 0 && dontShow){ + msg.WParam = IntPtr.Zero; + } + break; + case NativeMethods.WM_NCHITTEST: + // Fix for VSWhidbey#93985 and VSWhidbey#172903. When using v6 common controls, the native + // tooltip does not end up returning HTTRANSPARENT all the time, so its TTF_TRANSPARENT + // behavior does not work, ie. mouse events do not fall thru to controls underneath. This + // is due to a combination of old app-specific hacks in comctl32, functional changes between + // v5 and v6, and the specfic way the property grid drives its tooltip. Workaround is to just + // force HTTRANSPARENT all the time. + msg.Result = (IntPtr) NativeMethods.HTTRANSPARENT; + return; + } + base.WndProc(ref msg); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/HotCommands.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/HotCommands.cs new file mode 100644 index 000000000..28579892c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/HotCommands.cs @@ -0,0 +1,280 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Text; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Windows.Forms; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + + internal class HotCommands : PropertyGrid.SnappableControl { + + private object component; + private DesignerVerb[] verbs; + private LinkLabel label; + private bool allowVisible = true; + private int optimalHeight = -1; + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // HotCommands window is not visible + // So we don't have to localize its text. + ] + internal HotCommands(PropertyGrid owner) : base(owner) { + this.Text = "Command Pane"; + } + + public virtual bool AllowVisible { + get { + return allowVisible; + } + set { + if (this.allowVisible != value) { + this.allowVisible = value; + if (value && WouldBeVisible) + this.Visible = true; + else + this.Visible = false; + } + } + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object for this control. + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new HotCommandsAccessibleObject(this, ownerGrid); + } + + return base.CreateAccessibilityInstance(); + } + + public override Rectangle DisplayRectangle { + get { + Size sz = ClientSize; + return new Rectangle(4, 4, sz.Width - 8, sz.Height - 8); + } + } + + public LinkLabel Label { + get { + if (label == null) { + label = new LinkLabel(); + label.Dock = DockStyle.Fill; + label.LinkBehavior = LinkBehavior.AlwaysUnderline; + + // use default LinkLabel colors for regular, active, and visited + label.DisabledLinkColor = SystemColors.ControlDark; + label.LinkClicked += new LinkLabelLinkClickedEventHandler(this.LinkClicked); + this.Controls.Add(label); + } + return label; + } + } + + + public virtual bool WouldBeVisible { + get { + return (component != null); + } + } + + public override int GetOptimalHeight(int width) { + if (optimalHeight == -1) { + int lineHeight = (int)(1.5 * Font.Height); + int verbCount = 0; + if (verbs != null) { + verbCount = verbs.Length; + } + optimalHeight = verbCount * lineHeight + 8; + } + return optimalHeight; + } + public override int SnapHeightRequest(int request) { + return request; + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + private void LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { + try + { + if (!e.Link.Enabled) + { + return; + } + + ((DesignerVerb)e.Link.LinkData).Invoke(); + } + catch (Exception ex) + { + RTLAwareMessageBox.Show(this, ex.Message, SR.GetString(SR.PBRSErrorTitle), MessageBoxButtons.OK, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1, 0); + } + } + + private void OnCommandChanged(object sender, EventArgs e) { + SetupLabel(); + } + + protected override void OnGotFocus(EventArgs e) { + Label.FocusInternal(); + Label.Invalidate(); + } + + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + optimalHeight = -1; + } + + internal void SetColors(Color background, Color normalText, Color link, Color activeLink, Color visitedLink, Color disabledLink) { + Label.BackColor = background; + Label.ForeColor = normalText; + Label.LinkColor = link; + Label.ActiveLinkColor = activeLink; + Label.VisitedLinkColor = visitedLink; + Label.DisabledLinkColor = disabledLink; + } + + public void Select(bool forward) { + Label.FocusInternal(); + } + + public virtual void SetVerbs(object component, DesignerVerb[] verbs) { + if (this.verbs != null) { + for (int i = 0; i < this.verbs.Length; i++){ + this.verbs[i].CommandChanged -= new EventHandler(this.OnCommandChanged); + } + this.component = null; + this.verbs = null; + } + + if (component == null || verbs == null || verbs.Length == 0) { + Visible = false; + Label.Links.Clear(); + Label.Text = null; + } + else { + this.component = component; + this.verbs = verbs; + + for (int i = 0; i < verbs.Length; i++){ + verbs[i].CommandChanged += new EventHandler(this.OnCommandChanged); + } + + if (allowVisible) { + Visible = true; + } + SetupLabel(); + } + + optimalHeight = -1; + } + + private void SetupLabel() { + Label.Links.Clear(); + StringBuilder sb = new StringBuilder(); + Point[] links = new Point[verbs.Length]; + int charLoc = 0; + bool firstVerb = true; + + for (int i=0; i + /// Represents the hot commands control accessible object. + /// + [Runtime.InteropServices.ComVisible(true)] + internal class HotCommandsAccessibleObject : Control.ControlAccessibleObject { + + private PropertyGrid _parentPropertyGrid; + + /// + /// Initializes new instance of DocCommentAccessibleObject. + /// + /// The owning HotCommands control. + /// The parent PropertyGrid control. + public HotCommandsAccessibleObject(HotCommands owningHotCommands, PropertyGrid parentPropertyGrid) : base(owningHotCommands) { + _parentPropertyGrid = parentPropertyGrid; + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + var propertyGridAccessibleObject = _parentPropertyGrid.AccessibilityObject as PropertyGridAccessibleObject; + if (propertyGridAccessibleObject != null) { + var navigationTarget = propertyGridAccessibleObject.ChildFragmentNavigate(this, direction); + if (navigationTarget != null) { + return navigationTarget; + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_PaneControlTypeId; + } else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + + return base.GetPropertyValue(propertyID); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/IRootGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/IRootGridEntry.cs new file mode 100644 index 000000000..b12383479 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/IRootGridEntry.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// [To be supplied.] + /// + public interface IRootGridEntry{ + /// + /// + /// [To be supplied.] + /// + AttributeCollection BrowsableAttributes { + get; + set; + } + /// + /// + /// [To be supplied.] + /// + + void ResetBrowsableAttributes(); + /// + /// + /// [To be supplied.] + /// + void ShowCategories(bool showCategories); + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ImmutablePropertyDescriptorGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ImmutablePropertyDescriptorGridEntry.cs new file mode 100644 index 000000000..9fb32fcc0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/ImmutablePropertyDescriptorGridEntry.cs @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Runtime.Serialization.Formatters; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Drawing; + using System.Drawing.Design; + using Microsoft.Win32; + + + // This grid entry is used for immutable objects. An immutable object is identified + // through it's TypeConverter, which returns TRUE to ShouldCreateInstance. For this case, + // we never go through the property descriptor to change the value, but recreate each + // time. + // + internal class ImmutablePropertyDescriptorGridEntry : PropertyDescriptorGridEntry { + + internal ImmutablePropertyDescriptorGridEntry(PropertyGrid ownerGrid, GridEntry peParent, PropertyDescriptor propInfo, bool hide) + : base(ownerGrid, peParent, propInfo, hide) { + } + + internal override bool IsPropertyReadOnly { + get { + return ShouldRenderReadOnly; + } + } + + public override object PropertyValue + { + get { + return base.PropertyValue; + } + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + set + { + // Create a new instance of the value and set it into the parent grid entry. + // + object owner = GetValueOwner(); + GridEntry parentEntry = InstanceParentGridEntry; + TypeConverter parentConverter = parentEntry.TypeConverter; + + PropertyDescriptorCollection props = parentConverter.GetProperties(parentEntry, owner); + IDictionary values = new Hashtable(props.Count); + object newObject = null; + + for (int i = 0; i < props.Count; i++) { + if (propertyInfo.Name != null && propertyInfo.Name.Equals(props[i].Name)) { + values[props[i].Name] = value; + } + else { + values[props[i].Name] = props[i].GetValue(owner); + } + } + + try { + newObject = parentConverter.CreateInstance(parentEntry, values); + } + catch (Exception e) { + if (string.IsNullOrEmpty(e.Message)) { + throw new TargetInvocationException(SR.GetString(SR.ExceptionCreatingObject, + InstanceParentGridEntry.PropertyType.FullName, + e.ToString()), e); + } + else + throw; // rethrow the same exception + } + + if (newObject != null) { + parentEntry.PropertyValue = newObject; + } + } + } + + internal override bool NotifyValueGivenParent(object obj, int type) { + return ParentGridEntry.NotifyValue(type); + } + + public override bool ShouldRenderReadOnly { + get { + return InstanceParentGridEntry.ShouldRenderReadOnly; + } + } + + private GridEntry InstanceParentGridEntry { + get { + GridEntry parent = this.ParentGridEntry; + + if (parent is CategoryGridEntry) { + parent = parent.ParentGridEntry; + } + + return parent; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MergePropertyDescriptor.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MergePropertyDescriptor.cs new file mode 100644 index 000000000..b51d2048e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MergePropertyDescriptor.cs @@ -0,0 +1,665 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Serialization.Formatters.Binary; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.IO; + using System.Collections; + using System.Globalization; + using System.Reflection; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + internal class MergePropertyDescriptor : PropertyDescriptor { + + private PropertyDescriptor[] descriptors; + + private enum TriState { + Unknown, + Yes, + No + } + + private TriState localizable = TriState.Unknown; + private TriState readOnly = TriState.Unknown; + private TriState canReset = TriState.Unknown; + + private MultiMergeCollection collection; + + + public MergePropertyDescriptor(PropertyDescriptor[] descriptors) : base(descriptors[0].Name, null) { + this.descriptors = descriptors; + } + + + /// + /// + /// + /// When overridden in a derived class, gets the type of the + /// component this property + /// is bound to. + /// + /// + public override Type ComponentType { + get { + return descriptors[0].ComponentType; + } + } + + /// + /// + /// + /// Gets the type converter for this property. + /// + /// + public override TypeConverter Converter { + get { + return descriptors[0].Converter; + } + } + + public override string DisplayName { + get { + return descriptors[0].DisplayName; + } + } + + /// + /// + /// + /// Gets a value + /// indicating whether this property should be localized, as + /// specified in the . + /// + /// + public override bool IsLocalizable { + get { + if (localizable == TriState.Unknown) { + localizable = TriState.Yes; + foreach (PropertyDescriptor pd in descriptors) { + if (!pd.IsLocalizable) { + localizable = TriState.No; + break; + } + } + } + return (localizable == TriState.Yes); + } + } + + /// + /// + /// + /// When overridden in + /// a derived class, gets a value + /// indicating whether this property is read-only. + /// + /// + public override bool IsReadOnly { + get { + if (readOnly == TriState.Unknown) { + readOnly = TriState.No; + foreach (PropertyDescriptor pd in descriptors) { + if (pd.IsReadOnly) { + readOnly = TriState.Yes; + break; + } + } + } + return (readOnly == TriState.Yes); + } + } + + + /// + /// + /// + /// When overridden in a derived class, + /// gets the type of the property. + /// + /// + public override Type PropertyType { + get { + return descriptors[0].PropertyType; + } + } + + public PropertyDescriptor this[int index] { + get { + return descriptors[index]; + } + } + + /// + /// + /// + /// When overridden in a derived class, indicates whether + /// resetting the will change the value of the + /// . + /// + /// + public override bool CanResetValue(object component) { + Debug.Assert(component is Array, "MergePropertyDescriptor::CanResetValue called with non-array value"); + if (canReset == TriState.Unknown) { + canReset = TriState.Yes; + Array a = (Array)component; + for (int i = 0; i < descriptors.Length; i++) { + if (!descriptors[i].CanResetValue(GetPropertyOwnerForComponent(a, i))) { + canReset = TriState.No; + break; + } + } + + } + return (canReset == TriState.Yes); + } + + /// + /// This method attempts to copy the given value so unique values are + /// always passed to each object. If the object cannot be copied it + /// will be returned. + /// + private object CopyValue(object value) { + + // null is always OK + if (value == null) { + return value; + } + + Type type = value.GetType(); + + // value types are always copies + if (type.IsValueType) { + return value; + } + + object clonedValue = null; + + // ICloneable is the next easiest thing + ICloneable clone = value as ICloneable; + if (clone != null) { + clonedValue = clone.Clone(); + } + + // Next, access the type converter + if (clonedValue == null) { + TypeConverter converter = TypeDescriptor.GetConverter(value); + if (converter.CanConvertTo(typeof(InstanceDescriptor))) { + // Instance descriptors provide full fidelity unless + // they are marked as incomplete. + InstanceDescriptor desc = (InstanceDescriptor)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(InstanceDescriptor)); + if (desc != null && desc.IsComplete) { + clonedValue = desc.Invoke(); + } + } + + // If that didn't work, try conversion to/from string + if (clonedValue == null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string))) { + object stringRep = converter.ConvertToInvariantString(value); + clonedValue = converter.ConvertFromInvariantString((string)stringRep); + } + } + + + + // How about serialization? + if (clonedValue == null && type.IsSerializable) { + BinaryFormatter f = new BinaryFormatter(); + MemoryStream ms = new MemoryStream(); + f.Serialize(ms, value); + ms.Position = 0; + clonedValue = f.Deserialize(ms); + } + + if (clonedValue != null) { + return clonedValue; + } + + // we failed. This object's reference will be set on each property. + return value; + } + + /// + /// + /// + /// Creates a collection of attributes using the + /// array of attributes that you passed to the constructor. + /// + /// + protected override AttributeCollection CreateAttributeCollection() { + return new MergedAttributeCollection(this); + } + + + private object GetPropertyOwnerForComponent(Array a, int i) { + object propertyOwner = a.GetValue(i); + if (propertyOwner is ICustomTypeDescriptor) { + propertyOwner = ((ICustomTypeDescriptor) propertyOwner).GetPropertyOwner(descriptors[i]); + } + return propertyOwner; + } + + /// + /// + /// + /// Gets an editor of the specified type. + /// + /// + public override object GetEditor(Type editorBaseType) { + return descriptors[0].GetEditor(editorBaseType); + } + + + /// + /// + /// + /// When overridden in a derived class, gets the current + /// value + /// of the + /// property on a component. + /// + /// + public override object GetValue(object component) { + Debug.Assert(component is Array, "MergePropertyDescriptor::GetValue called with non-array value"); + bool temp; + return GetValue((Array)component, out temp); + } + + public object GetValue(Array components, out bool allEqual) { + allEqual = true; + object obj = descriptors[0].GetValue(GetPropertyOwnerForComponent(components, 0)); + + if (obj is ICollection) { + if (collection == null) { + collection = new MultiMergeCollection((ICollection)obj); + } + else if (collection.Locked) { + return collection; + } + else { + collection.SetItems((ICollection)obj); + } + } + + for (int i = 1; i < descriptors.Length; i++) { + object objCur = descriptors[i].GetValue(GetPropertyOwnerForComponent(components, i)); + + if (collection != null) { + if (!collection.MergeCollection((ICollection)objCur)){ + allEqual = false; + return null; + } + } + else if ((obj == null && objCur == null) || + (obj != null && obj.Equals(objCur))) { + + continue; + } + else { + allEqual = false; + return null; + } + } + + if (allEqual && collection != null && collection.Count == 0) { + return null; + } + + return (collection != null ? collection : obj); + } + + internal object[] GetValues(Array components) { + object[] values = new object[components.Length]; + + for (int i = 0; i < components.Length; i++) { + values[i] = descriptors[i].GetValue(GetPropertyOwnerForComponent(components, i)); + } + return values; + } + + /// + /// + /// + /// When overridden in a derived class, resets the + /// value + /// for this property + /// of the component. + /// + /// + public override void ResetValue(object component) { + + Debug.Assert(component is Array, "MergePropertyDescriptor::ResetValue called with non-array value"); + Array a = (Array)component; + for (int i = 0; i < descriptors.Length; i++) { + descriptors[i].ResetValue(GetPropertyOwnerForComponent(a, i)); + } + } + + private void SetCollectionValues(Array a, IList listValue) { + + try { + if (collection != null) { + collection.Locked = true; + } + + // now we have to copy the value into each property. + object[] values = new object[listValue.Count]; + + listValue.CopyTo(values, 0); + + for (int i = 0; i < descriptors.Length; i++) { + IList propList = descriptors[i].GetValue(GetPropertyOwnerForComponent(a, i)) as IList; + + if (propList == null) { + continue; + } + + propList.Clear(); + foreach (object val in values) { + propList.Add(val); + } + } + } + finally { + if (collection != null) { + collection.Locked = false; + } + } + + } + + /// + /// + /// + /// When overridden in a derived class, sets the value of + /// the component to a different value. + /// + /// + public override void SetValue(object component, object value) { + Debug.Assert(component is Array, "MergePropertyDescriptor::SetValue called with non-array value"); + Array a = (Array)component; + if (value is IList && typeof(IList).IsAssignableFrom(PropertyType)) { + SetCollectionValues(a, (IList)value); + } + else { + for (int i = 0; i < descriptors.Length; i++) { + object clonedValue = CopyValue(value); + descriptors[i].SetValue(GetPropertyOwnerForComponent(a, i), clonedValue); + } + } + } + + /// + /// + /// + /// When overridden in a derived class, indicates whether the + /// value of + /// this property needs to be persisted. + /// + /// + + public override bool ShouldSerializeValue(object component) { + Debug.Assert(component is Array, "MergePropertyDescriptor::ShouldSerializeValue called with non-array value"); + Array a = (Array)component; + for (int i = 0; i < descriptors.Length; i++) { + if (!descriptors[i].ShouldSerializeValue(GetPropertyOwnerForComponent(a, i))) { + return false; + } + } + return true; + } + + private class MultiMergeCollection : ICollection { + + private object[] items; + private bool locked; + + public MultiMergeCollection(ICollection original) { + SetItems(original); + } + + /// + /// + /// Retrieves the number of items. + /// + public int Count { + get { + if (items != null) { + return items.Length; + } + else { + return 0; + } + } + } + + + /// + /// + /// Prevents the contents of the collection from being re-initialized; + /// + public bool Locked { + get { + return locked; + } + set { + this.locked = value; + } + } + + object ICollection.SyncRoot { + get { + return this; + } + } + + bool ICollection.IsSynchronized { + get { + return false; + } + } + + public void CopyTo(Array array, int index) { + if (items == null) return; + + Array.Copy(items, 0, array, index, items.Length); + } + + public IEnumerator GetEnumerator(){ + if (items != null) { + return items.GetEnumerator(); + } + else { + return new object[0].GetEnumerator(); + } + } + + /// + /// + /// Ensures that the new collection equals the exisitng one. + /// Otherwise, it wipes out the contents of the new collection. + /// + public bool MergeCollection(ICollection newCollection) { + + if (locked) { + return true; + } + + if (items.Length != newCollection.Count) { + items = new object[0]; + return false; + } + + object[] newItems = new object[newCollection.Count]; + newCollection.CopyTo(newItems, 0); + for (int i = 0;i < newItems.Length; i++) { + if (((newItems[i] == null) != (items[i] == null)) || + (items[i] != null && !items[i].Equals(newItems[i]))){ + items = new object[0]; + return false; + } + + } + return true; + } + + public void SetItems(ICollection collection) { + if (locked) { + return; + } + items = new object[collection.Count]; + collection.CopyTo(items, 0); + } + + } + + private class MergedAttributeCollection : AttributeCollection { + private MergePropertyDescriptor owner; + + private AttributeCollection[] attributeCollections = null; + private IDictionary foundAttributes = null; + + public MergedAttributeCollection(MergePropertyDescriptor owner) : base((Attribute[])null) { + this.owner = owner; + } + + public override Attribute this[Type attributeType] { + get { + return GetCommonAttribute(attributeType); + } + } + + #if false + private void FullMerge() { + Attribute[][] collections = new Attribute[owner.descriptors.Length][]; + for (int i = 0; i < owner.descriptors.Length; i++) { + AttributeCollection attrCollection = owner.descriptors[i].Attributes; + collections[i] = new Attribute[attrCollection.Count]; + attrCollection.CopyTo(collections[i], 0); + Array.Sort(collections[i], GridEntry.AttributeTypeSorter); + } + + ArrayList mergedList = new ArrayList(); + + // merge the sorted lists -- note that lists aren't fully sorted just by + // Attribute.TypeId + // + int[] posArray = new int[collections.Length]; + for (int i = 0; i < collections[0].Length; i++) { + Attribute pivotAttr = collections[0][i]; + bool match = true; + for (int j = 1; j < collections.Length; j++) { + + if (posArray[j] >= collections[j].Length) { + match = false; + break; + } + + // check to see if we're on a match + // + if (pivotAttr.Equals(collections[j][posArray[j]])) { + posArray[j] += 1; + continue; + } + + int jPos = posArray[j]; + Attribute jAttr = collections[j][jPos]; + + match = false; + + // if we aren't on a match, check all the items until we're past + // where the matching item would be + while (GridEntry.AttributeTypeSorter.Compare(jAttr, pivotAttr) <= 0) { + + // got a match! + if (pivotAttr.Equals(jAttr)) { + posArray[j] = jPos + 1; + match = true; + break; + } + + // try again + jPos++; + if (jPos < collections[j].Length) { + jAttr = collections[j][jPos]; + } + else { + break; + } + } + + // if we got here, there is no match, quit for this guy + if (!match) { + posArray[j] = jPos; + break; + } + } + + // do we have a match? + if (match) { + mergedList.Add(pivotAttr); + } + } + + // create our merged array + Attribute[] mergedAttrs = new Attribute[mergedList.Count]; + mergedList.CopyTo(mergedAttrs, 0); + } + + #endif + + private Attribute GetCommonAttribute(Type attributeType) { + if (attributeCollections == null) { + attributeCollections = new AttributeCollection[owner.descriptors.Length]; + for (int i = 0; i < owner.descriptors.Length; i++) { + attributeCollections[i] = owner.descriptors[i].Attributes; + } + } + + if (attributeCollections.Length == 0) { + return GetDefaultAttribute(attributeType); + } + + Attribute value; + if (foundAttributes != null) { + value = foundAttributes[attributeType] as Attribute; + if (value != null) { + return value; + } + } + + value = attributeCollections[0][attributeType]; + + if (value == null) { + return null; + } + + for (int i = 1; i < attributeCollections.Length; i++) { + Attribute newValue = attributeCollections[i][attributeType]; + if (!value.Equals(newValue)) { + value = GetDefaultAttribute(attributeType); + break; + } + } + + if (foundAttributes == null) { + foundAttributes = new Hashtable(); + } + foundAttributes[attributeType] = value; + return value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiPropertyDescriptorGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiPropertyDescriptorGridEntry.cs new file mode 100644 index 000000000..62a415326 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiPropertyDescriptorGridEntry.cs @@ -0,0 +1,356 @@ +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + internal class MultiPropertyDescriptorGridEntry : PropertyDescriptorGridEntry { + + + private MergePropertyDescriptor mergedPd; + private object[] objs; + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + public MultiPropertyDescriptorGridEntry(PropertyGrid ownerGrid, GridEntry peParent, object[] objectArray, PropertyDescriptor[] propInfo, bool hide) + : base (ownerGrid, peParent, hide) { + mergedPd = new MergePropertyDescriptor(propInfo); + this.objs = objectArray; + base.Initialize(mergedPd); + } + + public override IContainer Container { + get { + IContainer c = null; + + foreach (object o in objs) { + IComponent comp = o as IComponent; + if (comp == null) { + c = null; + break; + } + + if (comp.Site != null) { + if (c == null) { + c = comp.Site.Container; + continue; + } + else if (c == comp.Site.Container) { + continue; + } + } + c = null; + break; + } + return c; + } + } + + public override bool Expandable { + get { + bool fExpandable = GetFlagSet(FL_EXPANDABLE); + + if (fExpandable && ChildCollection.Count > 0) { + return true; + } + + if (GetFlagSet(FL_EXPANDABLE_FAILED)) { + return false; + } + + try { + foreach (object o in mergedPd.GetValues(objs)) { + if (o == null) { + fExpandable = false; + break; + } + } + } + catch { + fExpandable = false; + } + + return fExpandable; + } + } + + public override object PropertyValue{ + set { + base.PropertyValue = value; + + RecreateChildren(); + if (Expanded) + GridEntryHost.Refresh(false); + + } + } + + protected override bool CreateChildren() { + return CreateChildren(false); + } + + protected override bool CreateChildren(bool diffOldChildren) { + try { + + if (mergedPd.PropertyType.IsValueType || (Flags & GridEntry.FLAG_IMMUTABLE) != 0) { + return base.CreateChildren(diffOldChildren); + } + + ChildCollection.Clear(); + + MultiPropertyDescriptorGridEntry[] mergedProps = MultiSelectRootGridEntry.PropertyMerger.GetMergedProperties(mergedPd.GetValues(objs), this, this.PropertySort, this.CurrentTab); + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose && mergedProps == null, "PropertyGridView: MergedProps returned null!"); + + if (mergedProps != null) { + ChildCollection.AddRange(mergedProps); + } + bool fExpandable = this.Children.Count > 0; + if (!fExpandable) { + SetFlag(GridEntry.FL_EXPANDABLE_FAILED,true); + } + return fExpandable; + } + catch { + return false; + } + } + + public override object GetChildValueOwner(GridEntry childEntry) { + if (mergedPd.PropertyType.IsValueType || (Flags & GridEntry.FLAG_IMMUTABLE) != 0) { + return base.GetChildValueOwner(childEntry); + } + return mergedPd.GetValues(objs); + } + + public override IComponent[] GetComponents() { + IComponent[] temp = new IComponent[objs.Length]; + Array.Copy(objs, 0, temp, 0, objs.Length); + return temp; + } + + /// + /// + /// Returns the text value of this property. + /// + public override string GetPropertyTextValue(object value) { + + bool allEqual = true; + try{ + if (value == null && mergedPd.GetValue(objs, out allEqual) == null) { + if (!allEqual) { + return ""; + } + } + } + catch{ + return ""; + } + return base.GetPropertyTextValue(value); + } + + internal override bool NotifyChildValue(GridEntry pe, int type) { + bool success = false; + + IDesignerHost host = DesignerHost; + DesignerTransaction trans = null; + + if (host != null) { + trans = host.CreateTransaction(); + } + try { + success = base.NotifyChildValue(pe, type); + } + finally { + if (trans != null) { + trans.Commit(); + } + } + return success; + } + + protected override void NotifyParentChange(GridEntry ge) { + // now see if we need to notify the parent(s) up the chain + while (ge != null && + ge is PropertyDescriptorGridEntry && + ((PropertyDescriptorGridEntry)ge).propertyInfo.Attributes.Contains(NotifyParentPropertyAttribute.Yes)) { + + // find the next parent property with a differnet value owner + object owner = ge.GetValueOwner(); + + // find the next property descriptor with a different parent + while (!(ge is PropertyDescriptorGridEntry) || OwnersEqual(owner, ge.GetValueOwner())) { + ge = ge.ParentGridEntry; + if (ge == null) { + break; + } + } + + // fire the change on that owner + if (ge != null) { + owner = ge.GetValueOwner(); + + IComponentChangeService changeService = ComponentChangeService; + + if (changeService != null) { + Array ownerArray = owner as Array; + if (ownerArray != null) { + for (int i = 0; i < ownerArray.Length; i++) { + PropertyDescriptor pd = ((PropertyDescriptorGridEntry)ge).propertyInfo;; + + if (pd is MergePropertyDescriptor) { + pd = ((MergePropertyDescriptor)pd)[i]; + } + + if (pd != null) { + changeService.OnComponentChanging(ownerArray.GetValue(i), pd); + changeService.OnComponentChanged(ownerArray.GetValue(i), pd, null, null); + } + } + } + else { + changeService.OnComponentChanging(owner, ((PropertyDescriptorGridEntry)ge).propertyInfo); + changeService.OnComponentChanged(owner, ((PropertyDescriptorGridEntry)ge).propertyInfo, null, null); + } + } + } + } + } + + internal override bool NotifyValueGivenParent(object obj, int type) { + if (obj is ICustomTypeDescriptor) { + obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(propertyInfo); + } + + switch (type) { + case NOTIFY_RESET: + + object[] objects = (object[])obj; + + if (objects != null && objects.Length > 0) { + + IDesignerHost host = DesignerHost; + DesignerTransaction trans = null; + + if (host != null) { + trans = host.CreateTransaction(SR.GetString(SR.PropertyGridResetValue, this.PropertyName)); + } + try { + + bool needChangeNotify = !(objects[0] is IComponent) || ((IComponent)objects[0]).Site == null; + if (needChangeNotify) { + if (!OnComponentChanging()) { + if (trans != null) { + trans.Cancel(); + trans = null; + } + return false; + } + } + + this.mergedPd.ResetValue(obj); + + if (needChangeNotify) { + OnComponentChanged(); + } + NotifyParentChange(this); + } + finally { + if (trans != null) { + trans.Commit(); + } + } + } + return false; + case NOTIFY_DBL_CLICK: + case NOTIFY_RETURN: + Debug.Assert(propertyInfo is MergePropertyDescriptor, "Did not get a MergePropertyDescriptor!!!"); + Debug.Assert(obj is object[], "Did not get an array of objects!!"); + + MergePropertyDescriptor mpd = propertyInfo as MergePropertyDescriptor; + + if (mpd != null) { + object[] objs = (object[])obj; + + if (eventBindings == null) { + eventBindings = (IEventBindingService)GetService(typeof(IEventBindingService)); + } + + if (eventBindings != null) { + EventDescriptor descriptor = eventBindings.GetEvent(mpd[0]); + if (descriptor != null) { + return ViewEvent(obj, null, descriptor, true); + } + } + + return false; + } + else { + return base.NotifyValueGivenParent(obj, type); + } + } + + return base.NotifyValueGivenParent(obj, type); + } + + private bool OwnersEqual(object owner1, object owner2) { + if (!(owner1 is Array)) { + return owner1 == owner2; + } + else { + Array a1 = owner1 as Array; + Array a2 = owner2 as Array; + + if (a1 != null && a2 != null && a1.Length == a2.Length) { + for (int i = 0; i < a1.Length; i++) { + if (a1.GetValue(i) != a2.GetValue(i)) { + return false; + } + } + return true; + } + return false; + } + } + + + public override bool OnComponentChanging() { + if (ComponentChangeService != null) { + int cLength = objs.Length; + for (int i = 0; i < cLength; i++) { + try { + ComponentChangeService.OnComponentChanging(objs[i], mergedPd[i]); + } + catch (CheckoutException co) { + if (co == CheckoutException.Canceled) { + return false; + } + throw co; + } + } + } + return true; + } + + public override void OnComponentChanged() { + if (ComponentChangeService != null) { + int cLength = objs.Length; + for (int i = 0; i < cLength; i++) { + ComponentChangeService.OnComponentChanged(objs[i], mergedPd[i], null, null); + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiSelectRootGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiSelectRootGridEntry.cs new file mode 100644 index 000000000..b93b1b35b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/MultiSelectRootGridEntry.cs @@ -0,0 +1,348 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Drawing; + using Microsoft.Win32; + using System.Globalization; + + internal class MultiSelectRootGridEntry : SingleSelectRootGridEntry { + + private static PDComparer PropertyComparer = new PDComparer(); + + + internal MultiSelectRootGridEntry(PropertyGridView view, object obj, IServiceProvider baseProvider, IDesignerHost host, PropertyTab tab, PropertySort sortType) + : base(view,obj, baseProvider, host, tab, sortType) { + } + + internal override bool ForceReadOnly { + get { + if (!forceReadOnlyChecked) { + bool anyRO = false; + foreach (object obj in (Array)objValue) { + ReadOnlyAttribute readOnlyAttr = (ReadOnlyAttribute)TypeDescriptor.GetAttributes(obj)[typeof(ReadOnlyAttribute)]; + if ((readOnlyAttr != null && !readOnlyAttr.IsDefaultAttribute()) || TypeDescriptor.GetAttributes(obj).Contains(InheritanceAttribute.InheritedReadOnly)) { + anyRO = true; + break; + } + } + if (anyRO) { + flags |= FLAG_FORCE_READONLY; + } + forceReadOnlyChecked = true; + } + return base.ForceReadOnly; + } + } + + protected override bool CreateChildren() { + return CreateChildren(false); + } + + protected override bool CreateChildren(bool diffOldChildren) { + try { + object[] rgobjs = (object[])objValue; + + + ChildCollection.Clear(); + + MultiPropertyDescriptorGridEntry[] mergedProps = PropertyMerger.GetMergedProperties(rgobjs, this, this.PropertySort, CurrentTab); + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose && mergedProps == null, "PropertyGridView: MergedProps returned null!"); + + if (mergedProps != null) { + ChildCollection.AddRange(mergedProps); + } + bool fExpandable = this.Children.Count > 0; + if (!fExpandable) { + //Debug.WriteLine("Object " + rgobjs[0].GetType().FullName + " is not expandable because it has no children"); + SetFlag(GridEntry.FL_EXPANDABLE_FAILED,true); + } + CategorizePropEntries(); + return fExpandable; + } + catch { + return false; + } + } + + + internal static class PropertyMerger { + + public static MultiPropertyDescriptorGridEntry[] GetMergedProperties(object[] rgobjs, GridEntry parentEntry, PropertySort sort, PropertyTab tab) { + + MultiPropertyDescriptorGridEntry[] result = null; + try { + int cLength = rgobjs.Length; + object[] rgobjArgs = new object[1]; + + if((sort & PropertySort.Alphabetical) != 0) { + ArrayList props = GetCommonProperties(rgobjs, true, tab, parentEntry); + + MultiPropertyDescriptorGridEntry[] entries = new MultiPropertyDescriptorGridEntry[props.Count]; + for (int i=0; i < entries.Length; i++) { + entries[i] = new MultiPropertyDescriptorGridEntry(parentEntry.OwnerGrid, parentEntry, rgobjs, (PropertyDescriptor[])props[i], false); + } + + result = SortParenEntries(entries); + } + else { + object[] sortObjs = new object[cLength - 1]; + Array.Copy(rgobjs, 1, sortObjs, 0, cLength - 1); + + ArrayList props = GetCommonProperties(sortObjs, true, tab, parentEntry); + + // this'll work for just one as well + ArrayList firstProps = GetCommonProperties(new object[]{rgobjs[0]}, false, tab, parentEntry); + + PropertyDescriptor[] firstPds = new PropertyDescriptor[firstProps.Count]; + for (int i = 0; i < firstProps.Count; i++) { + firstPds[i] = ((PropertyDescriptor[])firstProps[i])[0]; + } + + props = UnsortedMerge(firstPds, props); + + MultiPropertyDescriptorGridEntry[] entries = new MultiPropertyDescriptorGridEntry[props.Count]; + + for (int i=0; i < entries.Length; i++) { + entries[i] = new MultiPropertyDescriptorGridEntry(parentEntry.OwnerGrid, parentEntry, rgobjs, (PropertyDescriptor[])props[i], false); + } + + result = SortParenEntries(entries); + } + } + catch { + } + + return result; + + } + + // this returns an array list of the propertydescriptor arrays, one for each + // component + // + private static ArrayList GetCommonProperties(object[] objs, bool presort, PropertyTab tab, GridEntry parentEntry) { + PropertyDescriptorCollection[] propCollections = new PropertyDescriptorCollection[objs.Length]; + + Attribute[] attrs = new Attribute[parentEntry.BrowsableAttributes.Count]; + parentEntry.BrowsableAttributes.CopyTo(attrs, 0); + + for (int i = 0; i < objs.Length; i++) { + PropertyDescriptorCollection pdc = tab.GetProperties(parentEntry, objs[i], attrs); + if (presort) { + pdc = pdc.Sort(PropertyComparer); + } + propCollections[i] = pdc; + } + + ArrayList mergedList = new ArrayList(); + PropertyDescriptor[] matchArray = new PropertyDescriptor[objs.Length]; + + // + // Merge the property descriptors + // + int[] posArray = new int[propCollections.Length]; + for (int i = 0; i < propCollections[0].Count; i++) { + PropertyDescriptor pivotDesc = propCollections[0][i]; + + bool match = pivotDesc.Attributes[typeof(MergablePropertyAttribute)].IsDefaultAttribute(); + + for (int j = 1; match && j < propCollections.Length; j++) { + + if (posArray[j] >= propCollections[j].Count) { + match = false; + break; + } + + // check to see if we're on a match + // + PropertyDescriptor jProp = propCollections[j][posArray[j]]; + if (pivotDesc.Equals(jProp)) { + posArray[j] += 1; + + if (!jProp.Attributes[typeof(MergablePropertyAttribute)].IsDefaultAttribute()) { + match = false; + break; + } + matchArray[j] = jProp; + continue; + } + + int jPos = posArray[j]; + jProp = propCollections[j][jPos]; + + match = false; + + // if we aren't on a match, check all the items until we're past + // where the matching item would be + while (PropertyComparer.Compare(jProp, pivotDesc) <= 0) { + + // got a match! + if (pivotDesc.Equals(jProp)) { + + if (!jProp.Attributes[typeof(MergablePropertyAttribute)].IsDefaultAttribute()) { + match = false; + jPos++; + } + else { + match = true; + matchArray[j] = jProp; + posArray[j] = jPos + 1; + } + break; + } + + // try again + jPos++; + if (jPos < propCollections[j].Count) { + jProp = propCollections[j][jPos]; + } + else { + break; + } + } + + // if we got here, there is no match, quit for this guy + if (!match) { + posArray[j] = jPos; + break; + } + } + + // do we have a match? + if (match) { + matchArray[0] = pivotDesc; + mergedList.Add(matchArray.Clone()); + } + } + + return mergedList; + } + + + + private static MultiPropertyDescriptorGridEntry[] SortParenEntries(MultiPropertyDescriptorGridEntry[] entries) { + + MultiPropertyDescriptorGridEntry[] newEntries = null; + int newPos = 0; + + // first scan the list and move any parentesized properties to the front. + for (int i = 0; i < entries.Length; i++) { + if (entries[i].ParensAroundName) { + if (newEntries == null) { + newEntries = new MultiPropertyDescriptorGridEntry[entries.Length]; + } + newEntries[newPos++] = entries[i]; + entries[i] = null; + } + } + + // second pass, copy any that didn't have the parens. + if (newPos > 0) { + for (int i = 0; i < entries.Length; i++) { + if (entries[i] != null) { + newEntries[newPos++] = entries[i]; + } + } + entries = newEntries; + } + return entries; + } + + /// + /// + /// merges an unsorted array of grid entries with a sorted array of grid entries that + /// have already been merged. The resulting array is the intersection of entries between the two, + /// but in the order of baseEntries. + /// + private static ArrayList UnsortedMerge(PropertyDescriptor[] baseEntries, ArrayList sortedMergedEntries) { + + ArrayList mergedEntries = new ArrayList(); + PropertyDescriptor[] mergeArray = new PropertyDescriptor[((PropertyDescriptor[])sortedMergedEntries[0]).Length + 1]; + + for (int i = 0; i < baseEntries.Length; i++) { + + PropertyDescriptor basePd = baseEntries[i]; + + // first, do a binary search for a matching item + PropertyDescriptor[] mergedEntryList = null; + string entryName = basePd.Name + " " + basePd.PropertyType.FullName; + + int len = sortedMergedEntries.Count; + // perform a binary search + int offset = len / 2; + int start = 0; + + while (len > 0) { + PropertyDescriptor[] pdList = (PropertyDescriptor[])sortedMergedEntries[start + offset]; + PropertyDescriptor pd = pdList[0]; + string sortString = pd.Name + " " + pd.PropertyType.FullName; + int result = String.Compare(entryName, sortString, false, CultureInfo.InvariantCulture); + if (result == 0) { + mergedEntryList = pdList; + break; + } + else if (result < 0) { + len = offset; + } + else { + int delta = offset + 1; + start += delta; + len -= delta; + } + offset = len / 2; + } + + if (mergedEntryList != null) { + mergeArray[0] = basePd; + Array.Copy(mergedEntryList, 0, mergeArray, 1, mergedEntryList.Length); + mergedEntries.Add(mergeArray.Clone()); + } + } + return mergedEntries; + } + + } + + private class PDComparer : IComparer { + public int Compare(object obj1, object obj2) { + PropertyDescriptor a1 = obj1 as PropertyDescriptor; + PropertyDescriptor a2 = obj2 as PropertyDescriptor; + + if (a1 == null && a2 == null) { + return 0; + } + else if (a1 == null) { + return -1; + } + else if (a2 == null) { + return 1; + } + + int result = String.Compare(a1.Name, a2.Name, false, CultureInfo.InvariantCulture); + + if (result == 0) { + // + result = String.Compare(a1.PropertyType.FullName, a2.PropertyType.FullName, true, System.Globalization.CultureInfo.CurrentCulture); + } + return result; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertiesTab.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertiesTab.cs new file mode 100644 index 000000000..4cafe1828 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertiesTab.cs @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.ComponentModel.Design; + using System.ComponentModel; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.Design; + using System.Collections; + using Microsoft.Win32; + + /// + /// + /// [To be supplied.] + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class PropertiesTab : PropertyTab { + + + /// + /// + /// [To be supplied.] + /// + public override string TabName { + get { + return SR.GetString(SR.PBRSToolTipProperties); + } + } + + /// + /// + /// [To be supplied.] + /// + public override string HelpKeyword { + get { + return "vs.properties"; // do not localize. + } + } + + /// + /// + /// [To be supplied.] + /// + public override PropertyDescriptor GetDefaultProperty(object obj) { + PropertyDescriptor def = base.GetDefaultProperty(obj); + + if (def == null) { + PropertyDescriptorCollection props = GetProperties(obj); + if (props != null) { + for (int i = 0; i < props.Count; i++) { + if ("Name".Equals(props[i].Name)) { + def = props[i]; + break; + } + } + } + } + return def; + } + + /// + /// + /// [To be supplied.] + /// + public override PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes) { + return GetProperties(null, component, attributes); + } + + /// + /// + /// [To be supplied.] + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attributes) { + if (attributes == null) { + attributes = new Attribute[]{BrowsableAttribute.Yes}; + } + + if (context == null) { + return TypeDescriptor.GetProperties(component, attributes); + } + else { + TypeConverter tc = (context.PropertyDescriptor == null ? TypeDescriptor.GetConverter(component) : context.PropertyDescriptor.Converter); + if (tc == null || !tc.GetPropertiesSupported(context)) { + return TypeDescriptor.GetProperties(component, attributes); + } + else { + return tc.GetProperties(context, component, attributes); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyDescriptorGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyDescriptorGridEntry.cs new file mode 100644 index 000000000..49d9b58e1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyDescriptorGridEntry.cs @@ -0,0 +1,1270 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Drawing; + using System.Drawing.Design; + using Microsoft.Win32; + using System.Globalization; + + + internal class PropertyDescriptorGridEntry : GridEntry { + internal PropertyDescriptor propertyInfo; + + private TypeConverter exceptionConverter = null; + private UITypeEditor exceptionEditor = null; + private bool isSerializeContentsProp = false; + private byte parensAroundName = ParensAroundNameUnknown; + private IPropertyValueUIService pvSvc; + protected IEventBindingService eventBindings = null; + private bool pvSvcChecked; + private PropertyValueUIItem[] pvUIItems = null; + private Rectangle [] uiItemRects; + private bool readOnlyVerified = false; + private bool forceRenderReadOnly = false; + private string helpKeyword; + private string toolTipText = null; + private bool activeXHide = false; + private static int scaledImageSizeX = IMAGE_SIZE; + private static int scaledImageSizeY = IMAGE_SIZE; + private static bool isScalingInitialized = false; + + + private const int IMAGE_SIZE = 8; + private const byte ParensAroundNameUnknown = (byte)0xFF; + private const byte ParensAroundNameNo = (byte)0; + private const byte ParensAroundNameYes = (byte)1; + + static IEventBindingService targetBindingService; + static IComponent targetComponent; + static EventDescriptor targetEventdesc; + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + internal PropertyDescriptorGridEntry(PropertyGrid ownerGrid, GridEntry peParent, bool hide) + : base(ownerGrid, peParent){ + this.activeXHide = hide; + } + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + internal PropertyDescriptorGridEntry(PropertyGrid ownerGrid, GridEntry peParent, PropertyDescriptor propInfo, bool hide) + : base(ownerGrid, peParent) { + + this.activeXHide = hide; + Initialize(propInfo); + } + + + /// + /// + /// specify that this grid entry should be allowed to be merged for. + /// multi-select. + /// + public override bool AllowMerge { + get { + MergablePropertyAttribute mpa = (MergablePropertyAttribute)propertyInfo.Attributes[typeof(MergablePropertyAttribute)]; + return mpa == null || mpa.IsDefaultAttribute(); + } + } + + internal override AttributeCollection Attributes { + get { + return propertyInfo.Attributes; + } + } + + /// + /// + /// Retrieves the keyword that the VS help dynamic help window will + /// use when this IPE is selected. + /// + public override string HelpKeyword { + get { + if (this.helpKeyword == null) { + + object owner = GetValueOwner(); + if (owner == null) + return null; //null exception protection. + + HelpKeywordAttribute helpAttribute = (HelpKeywordAttribute)propertyInfo.Attributes[typeof(HelpKeywordAttribute)]; + + if (helpAttribute != null && !helpAttribute.IsDefaultAttribute()) { + return helpAttribute.HelpKeyword; + } + else if (this is ImmutablePropertyDescriptorGridEntry) { + helpKeyword = this.PropertyName; + + GridEntry ge = this; + + while (ge.ParentGridEntry != null) { + + ge = ge.ParentGridEntry; + + // for value classes, the equality will never work, so + // just try the type equality + if (ge.PropertyValue == owner || (owner.GetType().IsValueType && owner.GetType() == ge.PropertyValue.GetType())) { + helpKeyword = ge.PropertyName + "." + helpKeyword; + break; + } + } + } + else { + + string typeName = ""; + + Type componentType = propertyInfo.ComponentType; + + if (componentType.IsCOMObject) { + typeName = TypeDescriptor.GetClassName(owner); + } + else { + + // make sure this property is declared on a class that + // is related to the component we're looking at. + // if it's not, it could be a shadow property so we need + // to try to find the real property. + // + Type ownerType = owner.GetType(); + if (!componentType.IsPublic || !componentType.IsAssignableFrom(ownerType)) { + PropertyDescriptor componentProperty = TypeDescriptor.GetProperties(ownerType)[this.PropertyName]; + if (componentProperty != null) { + componentType = componentProperty.ComponentType; + } + else { + componentType = null; + } + } + + if (componentType == null) { + typeName = TypeDescriptor.GetClassName(owner); + } + else { + + // + + + + + + //if (helpAttribute != null && !helpAttribute.IsDefaultAttribute()) { + // typeName = helpAttribute.HelpKeyword; + //} + //else { + typeName = componentType.FullName; + //} + } + } + helpKeyword = typeName + "." + propertyInfo.Name; + } + } + return this.helpKeyword; + } + } + + internal override string LabelToolTipText { + get { + return (toolTipText != null ? toolTipText : base.LabelToolTipText); + } + } + + internal override string HelpKeywordInternal{ + get { + return this.PropertyLabel; + } + } + + internal override bool Enumerable { + get { + return base.Enumerable && !IsPropertyReadOnly; + } + } + + + internal virtual bool IsPropertyReadOnly { + get { + return propertyInfo.IsReadOnly; + } + } + + public override bool IsValueEditable { + get { + return this.exceptionConverter == null && !IsPropertyReadOnly && base.IsValueEditable; + } + } + + public override bool NeedsDropDownButton{ + get { + return base.NeedsDropDownButton && !IsPropertyReadOnly; + } + } + + internal bool ParensAroundName { + get { + if (ParensAroundNameUnknown == this.parensAroundName) { + if (((ParenthesizePropertyNameAttribute)propertyInfo.Attributes[typeof(ParenthesizePropertyNameAttribute)]).NeedParenthesis) { + this.parensAroundName = ParensAroundNameYes; + } + else { + this.parensAroundName = ParensAroundNameNo; + } + } + return (this.parensAroundName == ParensAroundNameYes); + } + } + + + public override string PropertyCategory { + get { + string category = propertyInfo.Category; + if (category == null || category.Length == 0) { + category = base.PropertyCategory; + } + return category; + } + } + + /// + /// + /// Retrieves the PropertyDescriptor that is surfacing the given object/ + /// + public override PropertyDescriptor PropertyDescriptor { + get { + return propertyInfo; + } + } + + + public override string PropertyDescription { + get { + return propertyInfo.Description; + } + } + + public override string PropertyLabel { + get { + string label = propertyInfo.DisplayName; + if (ParensAroundName) { + label = "(" + label + ")"; + } + return label; + } + } + + /// + /// + /// Returns non-localized name of this property. + /// + public override string PropertyName { + get { + if (propertyInfo != null) { + return propertyInfo.Name; + } + return null; + } + } + + + + public override Type PropertyType { + get { + return propertyInfo.PropertyType; + } + } + + + /// + /// + /// Gets or sets the value for the property that is represented + /// by this GridEntry. + /// + public override object PropertyValue{ + get { + try + { + object objRet = GetPropertyValueCore(GetValueOwner()); + + if (this.exceptionConverter != null) + { + // undo the exception converter + SetFlagsAndExceptionInfo(0, null, null); + } + + return objRet; + } + catch (Exception e) + { + if (this.exceptionConverter == null) + { + // clear the flags + SetFlagsAndExceptionInfo(0, new ExceptionConverter(), new ExceptionEditor()); + } + return e; + } + } + set { + SetPropertyValue(GetValueOwner(), value, false, null); + } + } + + private IPropertyValueUIService PropertyValueUIService { + get { + if (!pvSvcChecked && this.pvSvc == null) { + this.pvSvc = (IPropertyValueUIService)GetService(typeof(IPropertyValueUIService)); + pvSvcChecked = true; + } + return this.pvSvc; + } + } + + private void SetFlagsAndExceptionInfo(int flags, ExceptionConverter converter, ExceptionEditor editor) + { + this.Flags = flags; + this.exceptionConverter = converter; + this.exceptionEditor = editor; + } + + public override bool ShouldRenderReadOnly + { + get { + if (base.ForceReadOnly || forceRenderReadOnly) { + return true; + } + + // if read only editable is set, make sure it's valid + // + if (propertyInfo.IsReadOnly && !readOnlyVerified && GetFlagSet(GridEntry.FLAG_READONLY_EDITABLE)) { + Type propType = this.PropertyType; + + if (propType != null && (propType.IsArray || propType.IsValueType || propType.IsPrimitive)) { + SetFlag(FLAG_READONLY_EDITABLE,false); + SetFlag(FLAG_RENDER_READONLY, true); + forceRenderReadOnly = true; + } + } + readOnlyVerified = true; + + if (base.ShouldRenderReadOnly){ + if (!this.isSerializeContentsProp && !base.NeedsCustomEditorButton) { + return true; + } + } + return false; + } + } + + /// + /// + /// Returns the type converter for this entry. + /// + internal override TypeConverter TypeConverter { + get { + if (exceptionConverter != null) { + return exceptionConverter; + } + + if (converter == null) { + converter = propertyInfo.Converter; + } + return base.TypeConverter; + } + } + + /// + /// + /// Returns the type editor for this entry. This may return null if there + /// is no type editor. + /// + internal override UITypeEditor UITypeEditor { + get { + if (exceptionEditor != null) { + return exceptionEditor; + } + + editor = (UITypeEditor)propertyInfo.GetEditor(typeof(System.Drawing.Design.UITypeEditor)); + + return base.UITypeEditor; + } + } + + + /// + /// + /// Invokes the type editor for editing this item. + /// + internal override void EditPropertyValue(PropertyGridView iva) { + base.EditPropertyValue(iva); + + if (!IsValueEditable) { + RefreshPropertiesAttribute refreshAttr = (RefreshPropertiesAttribute)propertyInfo.Attributes[typeof(RefreshPropertiesAttribute)]; + if ((refreshAttr != null && !refreshAttr.RefreshProperties.Equals(RefreshProperties.None))) { + this.GridEntryHost.Refresh(refreshAttr != null && refreshAttr.Equals(RefreshPropertiesAttribute.All)); + } + } + } + + + internal override Point GetLabelToolTipLocation(int mouseX, int mouseY){ + if (pvUIItems != null) { + for (int i = 0; i < pvUIItems.Length; i++) { + if (uiItemRects[i].Contains(mouseX, GridEntryHost.GetGridEntryHeight() / 2)) { + this.toolTipText = pvUIItems[i].ToolTip; + return new Point(mouseX, mouseY); + } + } + } + this.toolTipText = null; + return base.GetLabelToolTipLocation(mouseX, mouseY); + } + + protected object GetPropertyValueCore(object target) { + if (propertyInfo == null) { + return null; + } + + if (target is ICustomTypeDescriptor) { + target = ((ICustomTypeDescriptor)target).GetPropertyOwner(propertyInfo); + } + try { + return propertyInfo.GetValue(target); + } + catch { + throw; + } + } + + protected void Initialize(PropertyDescriptor propInfo) { + propertyInfo = propInfo; + + this.isSerializeContentsProp = (propertyInfo.SerializationVisibility == DesignerSerializationVisibility.Content); + + + Debug.Assert(propInfo != null, "Can't create propEntry because of null prop info"); + + if (!this.activeXHide && IsPropertyReadOnly) { + SetFlag(FLAG_TEXT_EDITABLE, false); + } + + if (isSerializeContentsProp && TypeConverter.GetPropertiesSupported()) { + SetFlag(FL_EXPANDABLE, true); + } + } + + protected virtual void NotifyParentChange(GridEntry ge) { + // now see if we need to notify the parent(s) up the chain + while (ge != null && + ge is PropertyDescriptorGridEntry && + ((PropertyDescriptorGridEntry)ge).propertyInfo.Attributes.Contains(NotifyParentPropertyAttribute.Yes)) { + + // find the next parent property with a differnet value owner + object owner = ge.GetValueOwner(); + + // Fix for Dev10 bug 584323: + // when owner is an instance of a value type, + // we can't just use == in the following while condition testing + bool isValueType = owner.GetType().IsValueType; + + // find the next property descriptor with a different parent + while (!(ge is PropertyDescriptorGridEntry) + || isValueType ? owner.Equals(ge.GetValueOwner()) : owner == ge.GetValueOwner()) { + ge = ge.ParentGridEntry; + if (ge == null) { + break; + } + } + + // fire the change on that owner + if (ge != null) { + owner = ge.GetValueOwner(); + + IComponentChangeService changeService = ComponentChangeService; + + if (changeService != null) { + changeService.OnComponentChanging(owner, ((PropertyDescriptorGridEntry)ge).propertyInfo); + changeService.OnComponentChanged(owner, ((PropertyDescriptorGridEntry)ge).propertyInfo, null, null); + } + + ge.ClearCachedValues(false); //clear the value so it paints correctly next time. + PropertyGridView gv = this.GridEntryHost; + if (gv != null) { + gv.InvalidateGridEntryValue(ge); + } + } + } + } + + internal override bool NotifyValueGivenParent(object obj, int type) { + if (obj is ICustomTypeDescriptor) { + obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(propertyInfo); + } + + switch (type) { + case NOTIFY_RESET: + + SetPropertyValue(obj, null, true, SR.GetString(SR.PropertyGridResetValue, this.PropertyName)); + if (pvUIItems != null) { + for (int i = 0; i < pvUIItems.Length; i++) { + pvUIItems[i].Reset(); + } + } + pvUIItems = null; + return false; + case NOTIFY_CAN_RESET: + try { + return propertyInfo.CanResetValue(obj) || (pvUIItems != null && pvUIItems.Length > 0); + } + catch { + + if (this.exceptionConverter == null) { + // clear the flags + this.Flags = 0; + this.exceptionConverter = new ExceptionConverter(); + this.exceptionEditor = new ExceptionEditor(); + } + return false; + } + case NOTIFY_SHOULD_PERSIST: + try{ + return propertyInfo.ShouldSerializeValue(obj); + } + catch { + + if (this.exceptionConverter == null) { + // clear the flags + this.Flags = 0; + this.exceptionConverter = new ExceptionConverter(); + this.exceptionEditor = new ExceptionEditor(); + } + return false; + } + + case NOTIFY_DBL_CLICK: + case NOTIFY_RETURN: + if (eventBindings == null) { + eventBindings = (IEventBindingService)GetService(typeof(IEventBindingService)); + } + if (eventBindings != null) { + EventDescriptor descriptor = eventBindings.GetEvent(propertyInfo); + if (descriptor != null) { + return ViewEvent(obj, null, null, true); + } + } + break; + } + return false; + } + + public override void OnComponentChanged() { + base.OnComponentChanged(); + // If we got this it means someone called ITypeDescriptorContext.OnCompnentChanged. + // so we need to echo that change up the inheritance change in case the owner object isn't a sited component. + NotifyParentChange(this); + } + + + public override bool OnMouseClick(int x, int y, int count, MouseButtons button) { + if (pvUIItems != null && count == 2 && ((button & MouseButtons.Left) == MouseButtons.Left)) { + for (int i = 0; i < pvUIItems.Length; i++) { + if (uiItemRects[i].Contains(x, GridEntryHost.GetGridEntryHeight() / 2)) { + pvUIItems[i].InvokeHandler(this, propertyInfo, pvUIItems[i]); + return true; + } + } + } + return base.OnMouseClick(x, y, count, button); + } + + public override void PaintLabel(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, bool selected, bool paintFullLabel) { + base.PaintLabel(g, rect, clipRect, selected, paintFullLabel); + + IPropertyValueUIService propValSvc = this.PropertyValueUIService; + + if (propValSvc == null) { + return; + } + + pvUIItems = propValSvc.GetPropertyUIValueItems(this, propertyInfo); + + if (pvUIItems != null) { + if (uiItemRects == null || uiItemRects.Length != pvUIItems.Length) { + uiItemRects = new Rectangle[pvUIItems.Length]; + } + + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + scaledImageSizeX = DpiHelper.LogicalToDeviceUnitsX(IMAGE_SIZE); + scaledImageSizeY = DpiHelper.LogicalToDeviceUnitsY(IMAGE_SIZE); + } + isScalingInitialized = true; + } + + for (int i = 0; i < pvUIItems.Length; i++) { + uiItemRects[i] = new Rectangle(rect.Right - ((scaledImageSizeX + 1) * (i + 1)), (rect.Height - scaledImageSizeY) / 2, scaledImageSizeX, scaledImageSizeY); + g.DrawImage(pvUIItems[i].Image, uiItemRects[i]); + } + GridEntryHost.LabelPaintMargin = (scaledImageSizeX + 1) * pvUIItems.Length; + } + } + + + + private object SetPropertyValue(object obj,object objVal, bool reset, string undoText) { + DesignerTransaction trans = null; + try + { + + object oldValue = GetPropertyValueCore(obj); + + if (objVal != null && objVal.Equals(oldValue)) + { + return objVal; + } + + ClearCachedValues(); + + IDesignerHost host = DesignerHost; + + + if (host != null) + { + string text = (undoText == null ? SR.GetString(SR.PropertyGridSetValue, propertyInfo.Name) : undoText); + trans = host.CreateTransaction(text); + } + + // Usually IComponent things are sited and this notification will be + // fired automatically by the PropertyDescriptor. However, for non-IComponent sub objects + // or sub objects that are non-sited sub components, we need to manuall fire + // the notification. + // + bool needChangeNotify = !(obj is IComponent) || ((IComponent)obj).Site == null; + + if (needChangeNotify) + { + try + { + if (ComponentChangeService != null) + { + ComponentChangeService.OnComponentChanging(obj, propertyInfo); + } + } + catch (CheckoutException coEx) + { + if (coEx == CheckoutException.Canceled) + { + return oldValue; + } + throw coEx; + } + + } + + + bool wasExpanded = this.InternalExpanded; + int childCount = -1; + if (wasExpanded) + { + childCount = this.ChildCount; + } + + RefreshPropertiesAttribute refreshAttr = (RefreshPropertiesAttribute)propertyInfo.Attributes[typeof(RefreshPropertiesAttribute)]; + bool needsRefresh = wasExpanded || (refreshAttr != null && !refreshAttr.RefreshProperties.Equals(RefreshProperties.None)); + + if (needsRefresh) + { + DisposeChildren(); + } + + // Determine if this is an event being created, and if so, navigate to the event code + // + + EventDescriptor eventDesc = null; + + // This is possibly an event. Check it out. + // + if (obj != null && objVal is string) + { + + if (eventBindings == null) + { + eventBindings = (IEventBindingService)GetService(typeof(IEventBindingService)); + } + if (eventBindings != null) + { + eventDesc = eventBindings.GetEvent(propertyInfo); + } + + // For a merged set of properties, the event binding service won't + // find an event. So, we ask type descriptor directly. + // + if (eventDesc == null) + { + // If we have a merged property descriptor, pull out one of + // the elements. + // + object eventObj = obj; + + if (propertyInfo is MergePropertyDescriptor && obj is Array) + { + Array objArray = obj as Array; + if (objArray.Length > 0) + { + eventObj = objArray.GetValue(0); + } + } + eventDesc = TypeDescriptor.GetEvents(eventObj)[propertyInfo.Name]; + } + } + + bool setSuccessful = false; + try + { + if (reset) + { + propertyInfo.ResetValue(obj); + } + else if (eventDesc != null) + { + ViewEvent(obj, (string)objVal, eventDesc, false); + } + else + { // Not an event + SetPropertyValueCore(obj, objVal, true); + } + + setSuccessful = true; + + // Now notify the change service that the change was successful. + // + if (needChangeNotify && ComponentChangeService != null) + { + ComponentChangeService.OnComponentChanged(obj, propertyInfo, null, objVal); + } + + NotifyParentChange(this); + } + finally + { + // see if we need to refresh the property browser + // 1) if this property has the refreshpropertiesattribute, or + // 2) it's got expanded sub properties + // + if (needsRefresh && this.GridEntryHost != null) + { + RecreateChildren(childCount); + if (setSuccessful) { + this.GridEntryHost.Refresh(refreshAttr != null && refreshAttr.Equals(RefreshPropertiesAttribute.All)); + } + } + } + } + catch (CheckoutException checkoutEx) + { + if (trans != null) + { + trans.Cancel(); + trans = null; + } + + if (checkoutEx == CheckoutException.Canceled) + { + return null; + } + throw; + } + catch + { + if (trans != null) + { + trans.Cancel(); + trans = null; + } + + throw; + } + finally { + if (trans != null) { + trans.Commit(); + } + } + return obj; + } + + protected void SetPropertyValueCore(object obj, object value, bool doUndo) { + + if (propertyInfo == null) { + return; + } + + // Store the current cursor and set it to the HourGlass. + // + Cursor oldCursor = Cursor.Current; + try { + Cursor.Current = Cursors.WaitCursor; + + object target = obj; + + if (target is ICustomTypeDescriptor) { + target = ((ICustomTypeDescriptor)target).GetPropertyOwner(propertyInfo); + } + + // check the type of the object we are modifying. If it's a value type or an array, + // we need to modify the object and push the value back up to the parent. + // + bool treatAsValueType = false; + + if (ParentGridEntry != null) { + Type propType = ParentGridEntry.PropertyType; + treatAsValueType = propType.IsValueType || propType.IsArray; + } + + if (target != null) { + + propertyInfo.SetValue(target, value); + + // Microsoft, okay, since the value that we modified may not + // be stored by the parent property, we need to push this + // value back into the parent. An example here is Size or + // Location, which return Point objects that are unconnected + // to the object they relate to. So we modify the Point object and + // push it back into the object we got it from. + // + if (treatAsValueType) { + GridEntry parent = this.ParentGridEntry; + if (parent != null && parent.IsValueEditable) { + parent.PropertyValue = obj; + } + } + } + } + finally { + // Flip back to the old cursor. + // + Cursor.Current = oldCursor; + } + } + + /// + /// + /// Navigates code to the given event. + /// + protected bool ViewEvent(object obj, string newHandler, EventDescriptor eventdesc, bool alwaysNavigate) { + + object value = GetPropertyValueCore(obj); + + string handler = value as string; + + if (handler == null && value != null && TypeConverter != null && TypeConverter.CanConvertTo(typeof(string))) { + handler = TypeConverter.ConvertToString(value); + } + + if (newHandler == null && !String.IsNullOrEmpty(handler)) { + newHandler = handler; + } + else if (handler == newHandler && !String.IsNullOrEmpty(newHandler)) { + return true; + } + + IComponent component = obj as IComponent; + + if (component == null && propertyInfo is MergePropertyDescriptor) { + + // It's possible that multiple objects are selected, and we're trying to create an event for each of them + // + Array objArray = obj as Array; + if (objArray != null && objArray.Length > 0) { + component = objArray.GetValue(0) as IComponent; + } + } + + if (component == null) { + return false; + } + + if (propertyInfo.IsReadOnly) { + return false; + } + + if (eventdesc == null) { + if (eventBindings == null) { + eventBindings = (IEventBindingService)GetService(typeof(IEventBindingService)); + } + if (eventBindings != null) { + eventdesc = eventBindings.GetEvent(propertyInfo); + } + } + + IDesignerHost host = this.DesignerHost; + DesignerTransaction trans = null; + + try { + // This check can cause exceptions if the event has unreferenced dependencies, which we want to cath. + // This must be done before the transaction is started or the commit/cancel will also throw. + if (eventdesc.EventType == null) { + return false; + } + + if (host != null) { + string compName = component.Site != null ? component.Site.Name : component.GetType().Name; + trans = DesignerHost.CreateTransaction(SR.GetString(SR.WindowsFormsSetEvent, compName + "." + this.PropertyName)); + } + + if (eventBindings == null) { + ISite site = component.Site; + if (site != null) { + eventBindings = (IEventBindingService)site.GetService(typeof(IEventBindingService)); + } + } + + if (newHandler == null && eventBindings != null) { + newHandler = eventBindings.CreateUniqueMethodName(component, eventdesc); + } + + + if (newHandler != null) { + + // now walk through all the matching methods to see if this one exists. + // if it doesn't we'll wanna show code. + // + if (eventBindings != null) { + bool methodExists = false; + foreach (string methodName in eventBindings.GetCompatibleMethods(eventdesc)) { + if (newHandler == methodName) { + methodExists = true; + break; + } + } + if (!methodExists) { + alwaysNavigate = true; + } + } + + try { + propertyInfo.SetValue(obj, newHandler); + } catch (InvalidOperationException ex) { + if (trans != null) { + trans.Cancel(); + trans = null; + } + + if (this.GridEntryHost != null && this.GridEntryHost is PropertyGridView) { + PropertyGridView pgv = this.GridEntryHost as PropertyGridView; + pgv.ShowInvalidMessage(newHandler, obj, ex); + } + + return false; + } + } + + if (alwaysNavigate && eventBindings != null) { + targetBindingService = eventBindings; + targetComponent = component; + targetEventdesc = eventdesc; + Application.Idle += new EventHandler(PropertyDescriptorGridEntry.ShowCodeIdle); + } + } + catch { + if (trans != null) { + trans.Cancel(); + trans = null; + } + throw; + } + finally { + if (trans != null) { + trans.Commit(); + } + } + return true; + } + + /// + /// + /// Displays the user code for the given event. This will return true if the user + /// code could be displayed, or false otherwise. + /// + static private void ShowCodeIdle(object sender, EventArgs e) { + Application.Idle -= new EventHandler(PropertyDescriptorGridEntry.ShowCodeIdle); + if (targetBindingService != null) { + targetBindingService.ShowCode(targetComponent, targetEventdesc); + targetBindingService = null; + targetComponent = null; + targetEventdesc = null; + } + } + + /// + /// Creates a new AccessibleObject for this PropertyDescriptorGridEntry instance. + /// The AccessibleObject instance returned by this method supports IsEnabled UIA property. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.7.2 or opt in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this PropertyDescriptorGridEntry instance. + /// + protected override GridEntryAccessibleObject GetAccessibilityObject() { + if (AccessibilityImprovements.Level2) { + return new PropertyDescriptorGridEntryAccessibleObject(this); + } + + return base.GetAccessibilityObject(); + } + + [ComVisible(true)] + protected class PropertyDescriptorGridEntryAccessibleObject : GridEntryAccessibleObject { + + private PropertyDescriptorGridEntry _owningPropertyDescriptorGridEntry; + + public PropertyDescriptorGridEntryAccessibleObject(PropertyDescriptorGridEntry owner) : base(owner) { + _owningPropertyDescriptorGridEntry = owner; + } + + internal override bool IsIAccessibleExSupported() { + return true; + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var propertyGridViewAccessibleObject = (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + var propertyGridView = propertyGridViewAccessibleObject.Owner as PropertyGridView; + bool currentGridEntryFound = false; + return propertyGridViewAccessibleObject.GetNextGridEntry(_owningPropertyDescriptorGridEntry, propertyGridView.TopLevelGridEntries, out currentGridEntryFound); + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + propertyGridViewAccessibleObject = (PropertyGridView.PropertyGridViewAccessibleObject)Parent; + propertyGridView = propertyGridViewAccessibleObject.Owner as PropertyGridView; + currentGridEntryFound = false; + return propertyGridViewAccessibleObject.GetPreviousGridEntry(_owningPropertyDescriptorGridEntry, propertyGridView.TopLevelGridEntries, out currentGridEntryFound); + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetFirstChild(); + case UnsafeNativeMethods.NavigateDirection.LastChild: + return GetLastChild(); + } + } + + return base.FragmentNavigate(direction); + } + + private UnsafeNativeMethods.IRawElementProviderFragment GetFirstChild() { + if (_owningPropertyDescriptorGridEntry.ChildCount > 0) { + return _owningPropertyDescriptorGridEntry.Children.GetEntry(0).AccessibilityObject; + } + + var propertyGridView = GetPropertyGridView(); + if (propertyGridView == null) { + return null; + } + + if (_owningPropertyDescriptorGridEntry == propertyGridView.SelectedGridEntry) { + if (propertyGridView.SelectedGridEntry.Enumerable) { + return propertyGridView.DropDownListBoxAccessibleObject; + } + + return propertyGridView.EditAccessibleObject; + } + + return null; + } + + private UnsafeNativeMethods.IRawElementProviderFragment GetLastChild() { + if (_owningPropertyDescriptorGridEntry.ChildCount > 0) { + return _owningPropertyDescriptorGridEntry.Children + .GetEntry(_owningPropertyDescriptorGridEntry.ChildCount - 1).AccessibilityObject; + } + + var propertyGridView = GetPropertyGridView(); + if (propertyGridView == null) { + return null; + } + + if (_owningPropertyDescriptorGridEntry == propertyGridView.SelectedGridEntry) { + if (propertyGridView.SelectedGridEntry.Enumerable && propertyGridView.DropDownButton.Visible) { + return propertyGridView.DropDownButton.AccessibilityObject; + } + + return propertyGridView.EditAccessibleObject; + } + + return null; + } + + private PropertyGridView GetPropertyGridView() { + var propertyGridViewAccessibleObject = Parent as PropertyGridView.PropertyGridViewAccessibleObject; + if (propertyGridViewAccessibleObject == null) { + return null; + } + + return propertyGridViewAccessibleObject.Owner as PropertyGridView; + } + + internal override bool IsPatternSupported(int patternId) { + if (AccessibilityImprovements.Level3 && + patternId == NativeMethods.UIA_ValuePatternId || + (patternId == NativeMethods.UIA_ExpandCollapsePatternId && owner.Enumerable)) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + internal override void Expand() { + if (ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) { + ExpandOrCollapse(); + } + } + + internal override void Collapse() { + if (ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Expanded) { + ExpandOrCollapse(); + } + } + + private void ExpandOrCollapse() { + var propertyGridView = GetPropertyGridView(); + if (propertyGridView == null) { + return; + } + + int row = propertyGridView.GetRowFromGridEntryInternal(_owningPropertyDescriptorGridEntry); + if (row != -1) { + propertyGridView.PopupDialog(row); + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + var propertyGridView = GetPropertyGridView(); + if (propertyGridView == null) { + return UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + + if (_owningPropertyDescriptorGridEntry == propertyGridView.SelectedGridEntry && propertyGridView.DropDownVisible) { + return UnsafeNativeMethods.ExpandCollapseState.Expanded; + } + else { + return UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_IsEnabledPropertyId) { + return !((PropertyDescriptorGridEntry)owner).IsPropertyReadOnly; + } + + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_LegacyIAccessibleDefaultActionPropertyId) { + return string.Empty; + } + else if (propertyID == NativeMethods.UIA_IsValuePatternAvailablePropertyId) { + return true; + } + } + + return base.GetPropertyValue(propertyID); + } + } + + /// + /// + /// The exception converter is a type converter that displays an exception to the user. + /// + private class ExceptionConverter : TypeConverter { + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == typeof(string)) { + if (value is Exception) { + Exception ex = (Exception)value; + if (ex.InnerException != null) { + ex = ex.InnerException; + } + return ex.Message; + } + return null; + } + throw GetConvertToException(value, destinationType); + } + } + + /// + /// + /// The exception editor displays a message to the user. + /// + private class ExceptionEditor : UITypeEditor { + + /// + /// + /// Edits the given object value using the editor style provided by + /// GetEditorStyle. A service provider is provided so that any + /// required editing services can be obtained. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { + Exception except = value as Exception; + + if (except != null) { + IUIService uis = null; + + if (context != null) { + uis = (IUIService)context.GetService(typeof(IUIService)); + } + + if (uis != null) { + uis.ShowError(except); + } + else { + string message = except.Message; + if (message == null || message.Length == 0) message = except.ToString(); + + RTLAwareMessageBox.Show(null, message, SR.GetString(SR.PropertyGridExceptionInfo), + MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0); + } + } + return value; + } + + /// + /// + /// Retrieves the editing style of the Edit method. If the method + /// is not supported, this will return None. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { + return UITypeEditorEditStyle.Modal; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridCommands.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridCommands.cs new file mode 100644 index 000000000..e9ee35bee --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridCommands.cs @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + + using System.Diagnostics; + using System; + using System.ComponentModel.Design; + using Microsoft.Win32; + + /// + /// + /// This class contains the set of menu commands our property browser + /// uses. + /// + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] + [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] + public class PropertyGridCommands{ + + /// + /// + /// This guid corresponds to the menu grouping windows forms will use for its menus. This is + /// defined in the windows forms menu CTC file, but we need it here so we can define what + /// context menus to use. + /// + protected static readonly Guid wfcMenuGroup = new Guid("{a72bd644-1979-4cbc-a620-ea4112198a66}"); + + /// + /// + /// This guid corresponds to the windows forms command set. + /// + protected static readonly Guid wfcMenuCommand = new Guid("{5a51cf82-7619-4a5d-b054-47f438425aa7}"); + + /// + /// + /// [To be supplied.] + /// + public static readonly CommandID Reset = new CommandID(wfcMenuCommand, 0x3000); + /// + /// + /// [To be supplied.] + /// + public static readonly CommandID Description = new CommandID(wfcMenuCommand, 0x3001); + /// + /// + /// [To be supplied.] + /// + public static readonly CommandID Hide = new CommandID(wfcMenuCommand, 0x3002); + /// + /// + /// [To be supplied.] + /// + public static readonly CommandID Commands = new CommandID(wfcMenuCommand, 0x3010); + } + +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridView.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridView.cs new file mode 100644 index 000000000..b15d22d99 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/PropertyGridView.cs @@ -0,0 +1,7822 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Security.Permissions; + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Versioning; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System; + using System.Collections; + using System.Windows.Forms; + using System.Windows.Forms.Internal; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.Design; + using System.ComponentModel.Design; + using System.Windows.Forms.VisualStyles; + using System.IO; + using System.Drawing; + using System.Drawing.Design; + using Microsoft.Win32; + using Message = System.Windows.Forms.Message; + using System.Drawing.Drawing2D; + + internal class PropertyGridView : + Control, + IWin32Window, + IWindowsFormsEditorService, + IServiceProvider { + + protected static readonly Point InvalidPoint = new Point(int.MinValue, int.MinValue); + +#if true // RENDERMODE + public const int RENDERMODE_LEFTDOT = 2; + public const int RENDERMODE_BOLD = 3; + public const int RENDERMODE_TRIANGLE = 4; + + public static int inheritRenderMode = RENDERMODE_BOLD; +#endif + + + public static TraceSwitch GridViewDebugPaint = new TraceSwitch("GridViewDebugPaint", "PropertyGridView: Debug property painting"); + + + private PropertyGrid ownerGrid; // the properties window host. + + +#if true // RENDERMODE + private const int LEFTDOT_SIZE = 4; +#endif + // constants + private const int EDIT_INDENT = 0; + private const int OUTLINE_INDENT = 10; + private const int OUTLINE_SIZE = 9; + private const int OUTLINE_SIZE_EXPLORER_TREE_STYLE = 16; + private int outlineSize = OUTLINE_SIZE; + private int outlineSizeExplorerTreeStyle = OUTLINE_SIZE_EXPLORER_TREE_STYLE; + private const int PAINT_WIDTH = 20; + private int paintWidth = PAINT_WIDTH; + private const int PAINT_INDENT = 26; + private int paintIndent = PAINT_INDENT; + private const int ROWLABEL = 1; + private const int ROWVALUE = 2; + private const int MAX_LISTBOX_HEIGHT = 200; + private int maxListBoxHeight = MAX_LISTBOX_HEIGHT; + + private const short ERROR_NONE = 0; + private const short ERROR_THROWN = 1; + private const short ERROR_MSGBOX_UP = 2; + internal const short GDIPLUS_SPACE = 2; + internal const int MaxRecurseExpand = 10; + + private const int DOTDOTDOT_ICONWIDTH = 7; + private const int DOTDOTDOT_ICONHEIGHT = 8; + private const int DOWNARROW_ICONWIDTH = 16; + private const int DOWNARROW_ICONHEIGHT = 16; + + private static int OFFSET_2PIXELS = 2; + private int offset_2Units = OFFSET_2PIXELS; + + protected static readonly Point InvalidPosition = new Point(int.MinValue, int.MinValue); + + + // colors and fonts + private Brush backgroundBrush = null; + private Font fontBold = null; + private Color grayTextColor; + + // for backwards compatibility of default colors + private bool grayTextColorModified = false; // true if someone has set the grayTextColor property + + // property collections + private GridEntryCollection topLevelGridEntries = null; // top level props + private GridEntryCollection allGridEntries = null; // cache of viewable props + + // row information + internal int totalProps = -1; // # of viewable props + private int visibleRows = -1; // # of visible rows + private int labelWidth = -1; + public double labelRatio = 2; // ratio of whole row to label width + + private short requiredLabelPaintMargin = GDIPLUS_SPACE; + + // current selected row and tooltip. + private int selectedRow = -1; + private GridEntry selectedGridEntry = null; + private int tipInfo = -1; + + // editors & controls + private GridViewEdit edit = null; + private DropDownButton btnDropDown = null; + private DropDownButton btnDialog = null; + private GridViewListBox listBox = null; + private DropDownHolder dropDownHolder = null; + private Rectangle lastClientRect = Rectangle.Empty; + private Control currentEditor = null; + private ScrollBar scrollBar = null; + internal GridToolTip toolTip = null; + private GridErrorDlg errorDlg = null; + + + // flags + private const short FlagNeedsRefresh = 0x0001; + private const short FlagIsNewSelection = 0x0002; + private const short FlagIsSplitterMove = 0x0004; + private const short FlagIsSpecialKey = 0x0008; + private const short FlagInPropertySet = 0x0010; + private const short FlagDropDownClosing = 0x0020; + private const short FlagDropDownCommit = 0x0040; + private const short FlagNeedUpdateUIBasedOnFont = 0x0080; + private const short FlagBtnLaunchedEditor = 0x0100; + private const short FlagNoDefault = 0x0200; + private const short FlagResizableDropDown = 0x0400; + + + private short flags = FlagNeedsRefresh | FlagIsNewSelection | FlagNeedUpdateUIBasedOnFont; + private short errorState = ERROR_NONE; + + private Point ptOurLocation = new Point(1,1); + + private string originalTextValue = null; // original text, in case of ESC + private int cumulativeVerticalWheelDelta = 0; + private long rowSelectTime = 0; + private Point rowSelectPos = Point.Empty; // the position that we clicked on a row to test for double clicks + private Point lastMouseDown = InvalidPosition; + private int lastMouseMove; + private GridEntry lastClickedEntry; + + private IServiceProvider serviceProvider; + private IHelpService topHelpService; + private IHelpService helpService; + + private EventHandler ehValueClick; + private EventHandler ehLabelClick; + private EventHandler ehOutlineClick; + private EventHandler ehValueDblClick; + private EventHandler ehLabelDblClick; + private GridEntryRecreateChildrenEventHandler ehRecreateChildren; + + private int cachedRowHeight = -1; + IntPtr baseHfont; + IntPtr boldHfont; + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // PropertyGridView's caption is not visible. + // So we don't have to localize its text. + ] + public PropertyGridView(IServiceProvider serviceProvider, PropertyGrid propertyGrid) + : base() { + if (DpiHelper.IsScalingRequired) { + paintWidth = DpiHelper.LogicalToDeviceUnitsX(PAINT_WIDTH); + paintIndent = DpiHelper.LogicalToDeviceUnitsX(PAINT_INDENT); + outlineSizeExplorerTreeStyle = DpiHelper.LogicalToDeviceUnitsX(OUTLINE_SIZE_EXPLORER_TREE_STYLE); + outlineSize = DpiHelper.LogicalToDeviceUnitsX(OUTLINE_SIZE); + maxListBoxHeight = DpiHelper.LogicalToDeviceUnitsY(MAX_LISTBOX_HEIGHT); + } + + this.ehValueClick = new EventHandler(this.OnGridEntryValueClick); + this.ehLabelClick = new EventHandler(this.OnGridEntryLabelClick); + this.ehOutlineClick = new EventHandler(this.OnGridEntryOutlineClick); + this.ehValueDblClick = new EventHandler(this.OnGridEntryValueDoubleClick); + this.ehLabelDblClick = new EventHandler(this.OnGridEntryLabelDoubleClick); + this.ehRecreateChildren = new GridEntryRecreateChildrenEventHandler(this.OnRecreateChildren); + + ownerGrid = propertyGrid; + this.serviceProvider = serviceProvider; + + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.ResizeRedraw, false); + SetStyle(ControlStyles.UserMouse, true); + + + // properties + BackColor = SystemColors.Window; + ForeColor = SystemColors.WindowText; + grayTextColor = SystemColors.GrayText; + backgroundBrush = SystemBrushes.Window; + TabStop = true; + + this.Text = "PropertyGridView"; + + CreateUI(); + LayoutWindow(true); + } + + public override Color BackColor { + get { + return base.BackColor; + } + set { + this.backgroundBrush = new SolidBrush(value); + base.BackColor = value; + } + } + + internal Brush GetBackgroundBrush(Graphics g) { + return backgroundBrush; + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool CanCopy { + get { + if (selectedGridEntry == null) { + return false; + } + + if (!Edit.Focused) { + string val = selectedGridEntry.GetPropertyTextValue(); + + return val != null && val.Length > 0; + } + + return true; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool CanCut { + get { + return CanCopy && selectedGridEntry.IsTextEditable; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool CanPaste { + get { + return selectedGridEntry != null && selectedGridEntry.IsTextEditable; // return gridView.CanPaste; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool CanUndo { + get { + if (!Edit.Visible || !Edit.Focused) { + return false; + } + return (0 != (int)Edit.SendMessage(NativeMethods.EM_CANUNDO, 0, 0)); + } + } + + internal DropDownButton DropDownButton { + get { + if (btnDropDown == null) { + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + + btnDropDown = new DropDownButton(); + btnDropDown.UseComboBoxTheme = true; + Bitmap bitmap = CreateResizedBitmap("Arrow.ico", DOWNARROW_ICONWIDTH, DOWNARROW_ICONHEIGHT); + btnDropDown.Image = bitmap; + btnDropDown.BackColor = SystemColors.Control; + btnDropDown.ForeColor = SystemColors.ControlText; + btnDropDown.Click += new EventHandler(this.OnBtnClick); + btnDropDown.GotFocus += new EventHandler(OnDropDownButtonGotFocus); + btnDropDown.LostFocus += new EventHandler(this.OnChildLostFocus); + btnDropDown.TabIndex = 2; + CommonEditorSetup(btnDropDown); + btnDropDown.Size = DpiHelper.EnableDpiChangedHighDpiImprovements ? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight) : new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight); + } + return btnDropDown; + } + } + + private Button DialogButton { + get { + if (btnDialog == null) { + + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + btnDialog = new DropDownButton(); + btnDialog.BackColor = SystemColors.Control; + btnDialog.ForeColor = SystemColors.ControlText; + btnDialog.TabIndex = 3; + btnDialog.Image = CreateResizedBitmap("dotdotdot.ico", DOTDOTDOT_ICONWIDTH, DOTDOTDOT_ICONHEIGHT); + btnDialog.Click += new EventHandler(this.OnBtnClick); + btnDialog.KeyDown += new KeyEventHandler(this.OnBtnKeyDown); + btnDialog.GotFocus += new EventHandler(OnDropDownButtonGotFocus); + btnDialog.LostFocus += new EventHandler(this.OnChildLostFocus); + btnDialog.Size = DpiHelper.EnableDpiChangedHighDpiImprovements ? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight) : new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight); + CommonEditorSetup(btnDialog); + } + return btnDialog; + } + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static Bitmap GetBitmapFromIcon(string iconName, int iconsWidth, int iconsHeight) { + Size desiredSize = new Size(iconsWidth, iconsHeight); + Icon icon = new Icon(BitmapSelector.GetResourceStream(typeof(PropertyGrid), iconName), desiredSize); + Bitmap b = icon.ToBitmap(); + icon.Dispose(); + + if ((DpiHelper.IsScalingRequired || DpiHelper.EnableDpiChangedHighDpiImprovements) && (b.Size.Width != iconsWidth || b.Size.Height != iconsHeight)) { + Bitmap scaledBitmap = DpiHelper.CreateResizedBitmap(b, desiredSize); + if (scaledBitmap != null) { + b.Dispose(); + b = scaledBitmap; + } + } + return b; + } + + private GridViewEdit Edit { + get{ + if (edit == null) { + + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + + edit = new GridViewEdit(this); + edit.BorderStyle = BorderStyle.None; + edit.AutoSize = false; + edit.TabStop = false; + edit.AcceptsReturn = true; + edit.BackColor = BackColor; + edit.ForeColor = ForeColor; + edit.KeyDown += new KeyEventHandler(this.OnEditKeyDown); + edit.KeyPress += new KeyPressEventHandler(this.OnEditKeyPress); + edit.GotFocus += new EventHandler(this.OnEditGotFocus); + edit.LostFocus += new EventHandler(this.OnEditLostFocus); + edit.MouseDown += new MouseEventHandler(this.OnEditMouseDown); + edit.TextChanged += new EventHandler(this.OnEditChange); + //edit.ImeModeChanged += new EventHandler(this.OnEditImeModeChanged); + edit.TabIndex = 1; + CommonEditorSetup(edit); + } + return edit; + } + } + + /// + /// Represents the Editor's control accessible object. + /// + internal AccessibleObject EditAccessibleObject { + get { + return Edit.AccessibilityObject; + } + } + + private GridViewListBox DropDownListBox { + get { + if (listBox == null) { + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + + listBox = new GridViewListBox(this); + listBox.DrawMode = DrawMode.OwnerDrawFixed; + //listBox.Click += new EventHandler(this.OnListClick); + listBox.MouseUp += new MouseEventHandler(this.OnListMouseUp); + listBox.DrawItem += new DrawItemEventHandler(this.OnListDrawItem); + listBox.SelectedIndexChanged += new EventHandler(this.OnListChange); + listBox.KeyDown += new KeyEventHandler(this.OnListKeyDown); + listBox.LostFocus += new EventHandler(this.OnChildLostFocus); + listBox.Visible = true; + listBox.ItemHeight = RowHeight; + } + return listBox; + } + } + + /// + /// Represents the DropDownListBox accessible object. + /// + internal AccessibleObject DropDownListBoxAccessibleObject { + get { + if (DropDownListBox.Visible) { + return DropDownListBox.AccessibilityObject; + } + + return null; + } + } + + internal bool DrawValuesRightToLeft { + get { + if (edit != null && edit.IsHandleCreated) { + int exStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(edit, edit.Handle), NativeMethods.GWL_EXSTYLE))); + return ((exStyle & NativeMethods.WS_EX_RTLREADING) != 0); + } + else { + return false; + } + } + } + + internal bool DropDownVisible { + get { + return dropDownHolder != null && dropDownHolder.Visible; + } + } + + public bool FocusInside { + get { + return(this.ContainsFocus || (dropDownHolder != null && dropDownHolder.ContainsFocus)); + } + } + + internal Color GrayTextColor{ + get { + // if changed from the default, then the set value is returned + if (grayTextColorModified) { + return grayTextColor; + } + + if (this.ForeColor.ToArgb() == SystemColors.WindowText.ToArgb()) { + return SystemColors.GrayText; + } + + // compute the new color by halving the value of the old one. + // + int colorRGB = this.ForeColor.ToArgb(); + + int alphaValue = (colorRGB >> 24) & 0xff; + if (alphaValue != 0) { + alphaValue /= 2; + colorRGB &= 0xFFFFFF; + colorRGB |= (int)((alphaValue << 24) & 0xFF000000); + } + else { + colorRGB /= 2; + } + return Color.FromArgb(colorRGB); + } + set { + grayTextColor = value; + grayTextColorModified = true; + } + } + + // This dialog's width is defined by the summary message + // in the top pane. We don't restrict dialog width in any way. + // Use caution and check at all DPI scaling factors if adding a new message + // to be displayed in the top pane. + private GridErrorDlg ErrorDialog { + get { + if (this.errorDlg == null) { + using (DpiHelper.EnterDpiAwarenessScope(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) { + errorDlg = new GridErrorDlg(this.ownerGrid); + } + } + return errorDlg; + } + } + + private bool HasEntries { + get{ + return topLevelGridEntries != null && topLevelGridEntries.Count > 0; + } + } + + protected int InternalLabelWidth { + get { + if (GetFlag(FlagNeedUpdateUIBasedOnFont)) { + UpdateUIBasedOnFont(true); + } + if (labelWidth == -1) { + SetConstants(); + } + return labelWidth; + } + } + + internal int LabelPaintMargin { + + set { + requiredLabelPaintMargin = (short)Math.Max(Math.Max(value, requiredLabelPaintMargin), GDIPLUS_SPACE); + } + } + + protected bool NeedsCommit{ + get { + string text; + + if (edit==null || !Edit.Visible) { + return false; + } + + text = Edit.Text; + + if (((text == null || text.Length == 0) && (originalTextValue == null || originalTextValue.Length == 0)) || + (text != null && originalTextValue != null && text.Equals(originalTextValue))) { + return false; + } + return true; + } + } + + public PropertyGrid OwnerGrid{ + get{ + return this.ownerGrid; + } + } + + protected int RowHeight { + get { + if (cachedRowHeight == -1) { + cachedRowHeight = (int)Font.Height + 2; + } + return cachedRowHeight; + } + } + + /// + /// + /// Returns a default location for showing the context menu. This + /// location is the center of the active property label in the grid, and + /// is used useful to position the context menu when the menu is invoked + /// via the keyboard. + /// + public Point ContextMenuDefaultLocation { + get { + // get the rect for the currently selected prop name, find the middle + Rectangle rect = GetRectangle( selectedRow, ROWLABEL ); + Point pt = PointToScreen( new Point( rect.X, rect.Y ) ); + return new Point(pt.X + (rect.Width / 2), pt.Y + (rect.Height / 2)); + } + } + + private ScrollBar ScrollBar { + get { + if (scrollBar == null) { + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + scrollBar = new VScrollBar(); + scrollBar.Scroll += new ScrollEventHandler(this.OnScroll); + Controls.Add(scrollBar); + } + return scrollBar; + } + } + + internal GridEntry SelectedGridEntry { + get { + return selectedGridEntry; + } + set { + if (allGridEntries != null) { + foreach (GridEntry e in allGridEntries) { + if (e == value) { + SelectGridEntry(value, true); + return; + } + } + } + + GridEntry gr = FindEquivalentGridEntry(new GridEntryCollection(null, new GridEntry[]{value})); + + if (gr != null) { + SelectGridEntry(gr, true); + return; + } + + throw new ArgumentException(SR.GetString(SR.PropertyGridInvalidGridEntry)); + } + } + + /* + public PropertyDescriptor SelectedPropertyDescriptor { + get { + if (selectedGridEntry != null && (selectedGridEntry is PropertyDescriptorGridEntry)) { + return ((PropertyDescriptorGridEntry) selectedGridEntry).PropertyDescriptor; + } + else { + return null; + } + } + } + */ + + /* + /// + /// + /// Returns the currently selected property name. + /// If no property or a category name is selected, "" is returned. + /// If the category is a sub property, it is concatenated onto its + /// parent property name with a ".". + /// + public string SelectedPropertyName { + get { + if (selectedGridEntry == null) { + return ""; + } + GridEntry gridEntry = selectedGridEntry; + string name = ""; + while (gridEntry != null && gridEntry.PropertyDepth >= 0) { + if (name.Length > 0) { + name = gridEntry.PropertyName + "." + name; + } + else { + name = gridEntry.PropertyName; + } + gridEntry = gridEntry.ParentGridEntry; + } + return name; + } + set{ + if (value==null){ + return; + } + if (value.Equals(selectedGridEntry.PropertyLabel)){ + return; + } + + string curName; + string remain = value; + + int dot = remain.IndexOf('.'); + GridEntry[] ipes = GetAllGridEntries(); + int pos = 0; + + while (dot != -1){ + curName = remain.Substring(0, dot); + Debug.WriteLine("Looking for: " + curName); + for (int i = pos; i < ipes.Length ; i++){ + Debug.WriteLine("Checking : " + ipes[i].PropertyLabel); + if (ipes[i].PropertyLabel.Equals(curName)){ + if (ipes[i].Expandable){ + pos = i; + remain = remain.Substring(dot + 1); + dot = remain.IndexOf('.'); + if (dot != -1){ + Debug.WriteLine("Expanding: " + ipes[i].PropertyLabel); + ipes[i].SetPropertyExpand(true); + ipes = GetAllGridEntries(); + break; + } + else{ + SelectGridEntry(ipes[i], true); + return; + } + } + } + } + // uh oh + dot = -1; + } + // oops, didn't find it + SelectRow(0); + return; + + } + } + */ + + /// + /// + /// Returns or sets the IServiceProvider the PropertyGridView will use to obtain + /// services. This may be null. + /// + public IServiceProvider ServiceProvider { + get { + return serviceProvider; + } + set { + if (value != serviceProvider) { + this.serviceProvider = value; + + topHelpService = null; + + if (helpService != null && helpService is IDisposable) + ((IDisposable)helpService).Dispose(); + + helpService = null; + } + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces. + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + private int TipColumn { + get{ + return(tipInfo & unchecked((int)0xFFFF0000)) >> 16; + } + set{ + + // clear the column + tipInfo &= 0xFFFF; + + // set the row + tipInfo |= ((value & 0xFFFF) << 16); + } + } + + private int TipRow { + get{ + return tipInfo & 0xFFFF; + } + set{ + + // clear the row + tipInfo &= unchecked((int)0xFFFF0000); + + // set the row + tipInfo |= (value & 0xFFFF); + } + } + + private GridToolTip ToolTip { + get { + if (toolTip == null) { + #if DEBUG + if (ownerGrid.inGridViewCreate) { + throw new Exception("PERF REGRESSION - Creating item in grid view create"); + } + #endif + toolTip = new GridToolTip(new Control[]{this, Edit}); + toolTip.ToolTip = ""; + toolTip.Font = this.Font; + } + return toolTip; + } + } + + /// + /// Gets the top level grid entries. + /// + internal GridEntryCollection TopLevelGridEntries { + get { + return topLevelGridEntries; + } + } + + internal GridEntryCollection AccessibilityGetGridEntries() { + return GetAllGridEntries(); + } + + internal Rectangle AccessibilityGetGridEntryBounds(GridEntry gridEntry) { + int row = GetRowFromGridEntry(gridEntry); + if (row == -1) { + return new Rectangle(0, 0, 0, 0); + } + Rectangle rect = GetRectangle(row, ROWVALUE | ROWLABEL); + + // Translate rect to screen coordinates + // + NativeMethods.POINT pt = new NativeMethods.POINT(rect.X, rect.Y); + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, Handle), pt); + + return new Rectangle(pt.x, pt.y, rect.Width, rect.Height); + } + + internal int AccessibilityGetGridEntryChildID(GridEntry gridEntry) { + + GridEntryCollection ipes = GetAllGridEntries(); + + if (ipes == null) { + return -1; + } + + // Find the grid entry and return its ID + // + for(int index = 0; index < ipes.Count; ++index) { + if (ipes[index].Equals(gridEntry)) { + return index; + } + } + + return -1; + } + + internal void AccessibilitySelect(GridEntry entry) { + SelectGridEntry(entry, true); + FocusInternal(); + } + + private void AddGridEntryEvents(GridEntryCollection ipeArray, int startIndex, int count) { + if (ipeArray == null) { + return; + } + + if (count == -1) { + count = ipeArray.Count - startIndex; + } + + for (int i= startIndex; i < (startIndex + count); i++) { + if (ipeArray[i] != null) { + GridEntry ge = ipeArray.GetEntry(i); + ge.AddOnValueClick(ehValueClick); + ge.AddOnLabelClick(ehLabelClick); + ge.AddOnOutlineClick(ehOutlineClick); + ge.AddOnOutlineDoubleClick(ehOutlineClick); + ge.AddOnValueDoubleClick(ehValueDblClick); + ge.AddOnLabelDoubleClick(ehLabelDblClick); + ge.AddOnRecreateChildren(ehRecreateChildren); + } + } + } + + protected virtual void AdjustOrigin(System.Drawing.Graphics g, Point newOrigin, ref Rectangle r) { + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Adjusting paint origin to (" + newOrigin.X.ToString(CultureInfo.InvariantCulture) + "," + newOrigin.Y.ToString(CultureInfo.InvariantCulture) + ")"); + + g.ResetTransform(); + g.TranslateTransform(newOrigin.X, newOrigin.Y); + r.Offset(-newOrigin.X, -newOrigin.Y); + } + + private void CancelSplitterMove() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CancelSplitterMove"); + if (GetFlag(FlagIsSplitterMove)) { + SetFlag(FlagIsSplitterMove, false); + CaptureInternal = false; + + if (selectedRow != -1) { + SelectRow(selectedRow); + } + } + } + + internal GridPositionData CaptureGridPositionData() { + return new GridPositionData(this); + } + + private void ClearGridEntryEvents(GridEntryCollection ipeArray, int startIndex, int count) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ClearGridEntryEvents"); + if (ipeArray == null) { + return; + } + + if (count == -1) { + count = ipeArray.Count - startIndex; + } + + for (int i = startIndex ; i < (startIndex + count); i++) { + if (ipeArray[i] != null) { + GridEntry ge = ipeArray.GetEntry(i); + ge.RemoveOnValueClick(ehValueClick); + ge.RemoveOnLabelClick(ehLabelClick); + ge.RemoveOnOutlineClick(ehOutlineClick); + ge.RemoveOnOutlineDoubleClick(ehOutlineClick); + ge.RemoveOnValueDoubleClick(ehValueDblClick); + ge.RemoveOnLabelDoubleClick(ehLabelDblClick); + ge.RemoveOnRecreateChildren(ehRecreateChildren); + } + } + } + + public void ClearProps() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ClearProps"); + + + if (!HasEntries) { + return; + } + + CommonEditorHide(); + topLevelGridEntries = null; + ClearGridEntryEvents(allGridEntries, 0, -1); + allGridEntries = null; + selectedRow = -1; + //selectedGridEntry = null; // we don't wanna clear this because then we can't save where we were on a Refresh() + tipInfo = -1; + } + + /// + /// + /// Closes a previously opened drop down. This should be called by the + /// drop down when the user does something that should close it. + /// + public void /* IWindowsFormsEditorService. */ CloseDropDown() { + CloseDropDownInternal(true); + } + + private void CloseDropDownInternal(bool resetFocus) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CloseDropDown"); + + // the activation code in the DropDownHolder can cause this to recurse... + + if (GetFlag(FlagDropDownClosing)) { + return; + } + try { + SetFlag(FlagDropDownClosing, true); + if (dropDownHolder != null && dropDownHolder.Visible) { + + if (dropDownHolder.Component == DropDownListBox && GetFlag(FlagDropDownCommit)) { + OnListClick(null, null); + } + + Edit.Filter = false; + + // disable the ddh so it wont' steal the focus back + // + dropDownHolder.SetComponent(null, false); + dropDownHolder.Visible = false; + + // when we disable the dropdown holder, focus will be lost, + // so put it onto one of our children first. + if (resetFocus) { + if (DialogButton.Visible) { + DialogButton.FocusInternal(); + } + else if (DropDownButton.Visible) { + DropDownButton.FocusInternal(); + } + else if (Edit.Visible) { + Edit.FocusInternal(); + } + else { + FocusInternal(); + } + + if (selectedRow != -1) { + SelectRow(selectedRow); + } + } + + if (AccessibilityImprovements.Level3 && selectedRow != -1) { + var gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry != null) { + gridEntry.AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + gridEntry.AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Expanded, + UnsafeNativeMethods.ExpandCollapseState.Collapsed); + } + } + } + } + finally { + SetFlag(FlagDropDownClosing, false); + } + } + + private void CommonEditorHide() { + CommonEditorHide(false); + } + + private void CommonEditorHide(bool always) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CommonEditorHide"); + + if (!always && !HasEntries) { + return; + } + + CloseDropDown(); + + bool gotfocus = false; + + if (Edit.Focused || DialogButton.Focused || DropDownButton.Focused) { + + if (IsHandleCreated && Visible && Enabled) { + + gotfocus = IntPtr.Zero != UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle)); + } + } + + try { + // We do this becuase the Focus call above doesn't always stick, so + // we make the Edit think that it doesn't have focus. this prevents + // ActiveControl code on the containercontrol from moving focus elsewhere + // when the dropdown closes. + Edit.DontFocus = true; + if (Edit.Focused && !gotfocus) { + gotfocus = this.FocusInternal(); + } + Edit.Visible = false; + + Edit.SelectionStart = 0; + Edit.SelectionLength = 0; + + if (DialogButton.Focused && !gotfocus) { + gotfocus = this.FocusInternal(); + } + DialogButton.Visible = false; + + if (DropDownButton.Focused && !gotfocus) { + gotfocus = this.FocusInternal(); + } + DropDownButton.Visible = false; + currentEditor = null; + } + finally { + Edit.DontFocus = false; + } + } + + protected virtual void CommonEditorSetup(Control ctl) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CommonEditorSetup"); + ctl.Visible = false; + Controls.Add(ctl); + } + + protected virtual void CommonEditorUse(Control ctl, Rectangle rectTarget) { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CommonEditorUse"); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Showing common editors"); + + Debug.Assert(ctl != null, "Null control passed to CommonEditorUse"); + + Rectangle rectCur = ctl.Bounds; + + // the client rect minus the border line + Rectangle clientRect = this.ClientRectangle; + + clientRect.Inflate(-1,-1); + + try { + rectTarget = Rectangle.Intersect(clientRect, rectTarget); + //if (ctl is Button) + // Debug.WriteStackTrace(); + + + if (!rectTarget.IsEmpty) { + if (!rectTarget.Equals(rectCur)) { + ctl.SetBounds(rectTarget.X,rectTarget.Y, + rectTarget.Width,rectTarget.Height); + } + ctl.Visible = true; + } + } + catch { + rectTarget = Rectangle.Empty; + } + + if (rectTarget.IsEmpty) { + + ctl.Visible = false; + } + + currentEditor = ctl; + + } + + private /*protected virtual*/ int CountPropsFromOutline(GridEntryCollection rgipes) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CountPropsFromOutLine"); + if (rgipes == null) return 0; + int cProps = rgipes.Count; + for (int i = 0; i < rgipes.Count; i++) { + if (((GridEntry)rgipes[i]).InternalExpanded) + cProps += CountPropsFromOutline(((GridEntry)rgipes[i]).Children); + } + return cProps; + } + + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new PropertyGridViewAccessibleObject(this, ownerGrid); + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap CreateResizedBitmap(string icon, int width, int height) { + Bitmap bitmap = null; + var scaledIconWidth = width; + var scaledIconHeight = height; + try { + //scale for per-monitor DPI. + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + scaledIconWidth = LogicalToDeviceUnits(width); + scaledIconHeight = LogicalToDeviceUnits(height); + } + else if (DpiHelper.IsScalingRequired ) { + // only primary monitor scaling. + scaledIconWidth = DpiHelper.LogicalToDeviceUnitsX(width); + scaledIconHeight = DpiHelper.LogicalToDeviceUnitsY(height); + } + + bitmap = GetBitmapFromIcon(icon, scaledIconWidth, scaledIconHeight); + } + catch (Exception e) { + Debug.Fail(e.ToString()); + bitmap = new Bitmap(scaledIconWidth, scaledIconHeight); + } + return bitmap; + } + + + protected virtual void CreateUI() { + UpdateUIBasedOnFont(false); + } + + protected override void Dispose(bool disposing) { + if (disposing) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Dispose"); + if (scrollBar != null) scrollBar.Dispose(); + if (listBox != null) listBox.Dispose(); + if (dropDownHolder != null) dropDownHolder.Dispose(); + scrollBar = null; + listBox = null; + dropDownHolder = null; + + ownerGrid = null; + topLevelGridEntries = null; + allGridEntries = null; + serviceProvider = null; + + topHelpService = null; + + if (helpService != null && helpService is IDisposable) + ((IDisposable)helpService).Dispose(); + + helpService = null; + + if (edit != null) { + edit.Dispose(); + edit = null; + } + + if (fontBold != null) { + fontBold.Dispose(); + fontBold = null; + + } + + if (btnDropDown != null) { + btnDropDown.Dispose(); + btnDropDown = null; + } + + if (btnDialog != null) { + btnDialog.Dispose(); + btnDialog = null; + } + + if (toolTip != null) { + toolTip.Dispose(); + toolTip = null; + } + } + + base.Dispose(disposing); + } + + public void DoCopyCommand() { + if (this.CanCopy) { + if (Edit.Focused) { + Edit.Copy(); + } + else { + Clipboard.SetDataObject(selectedGridEntry.GetPropertyTextValue()); + } + } + } + + public void DoCutCommand() { + if (this.CanCut) { + DoCopyCommand(); + if (Edit.Visible) { + Edit.Cut(); + } + } + } + + + public void DoPasteCommand() { + if (this.CanPaste && Edit.Visible) { + if (Edit.Focused) { + Edit.Paste(); + } + else { + IDataObject dataObj = Clipboard.GetDataObject(); + if (dataObj != null) { + string data = (string)dataObj.GetData(typeof(string)); + if (data != null) { + Edit.FocusInternal(); + Edit.Text = data; + SetCommitError(ERROR_NONE, true); + } + } + } + } + } + + public void DoUndoCommand() { + if (this.CanUndo && Edit.Visible) { + Edit.SendMessage(NativeMethods.WM_UNDO, 0, 0); + } + } + + internal void DumpPropsToConsole(GridEntry entry, string prefix) { + + Type propType = entry.PropertyType; + + if (entry.PropertyValue != null) { + propType = entry.PropertyValue.GetType(); + } + + System.Console.WriteLine(prefix + entry.PropertyLabel + ", value type=" + (propType == null ? "(null)" : propType.FullName) + ", value=" + (entry.PropertyValue == null ? "(null)" : entry.PropertyValue.ToString()) + + ", flags=" + entry.Flags.ToString(CultureInfo.InvariantCulture) + + ", TypeConverter=" + (entry.TypeConverter == null ? "(null)" : entry.TypeConverter.GetType().FullName) + ", UITypeEditor=" + ((entry.UITypeEditor == null ? "(null)" : entry.UITypeEditor.GetType().FullName))); + GridEntryCollection children = entry.Children; + + if (children != null) { + foreach(GridEntry g in children) { + DumpPropsToConsole(g, prefix + "\t"); + } + } + } + + private int GetIPELabelIndent(GridEntry gridEntry) { + //return OUTLINE_INDENT*(gridEntry.PropertyDepth + 1); + return gridEntry.PropertyLabelIndent + 1; + } + + private int GetIPELabelLength(System.Drawing.Graphics g, GridEntry gridEntry) { + SizeF sizeF = PropertyGrid.MeasureTextHelper.MeasureText(this.ownerGrid, g, gridEntry.PropertyLabel, Font); + Size size = Size.Ceiling(sizeF); + return ptOurLocation.X + GetIPELabelIndent(gridEntry) + size.Width; + } + + private bool IsIPELabelLong(System.Drawing.Graphics g,GridEntry gridEntry) { + if (gridEntry == null) return false; + int length = GetIPELabelLength(g,gridEntry); + return(length > ptOurLocation.X + InternalLabelWidth); + } + + protected virtual void DrawLabel(System.Drawing.Graphics g, int row, Rectangle rect, bool selected, bool fLongLabelRequest, ref Rectangle clipRect) { + + GridEntry gridEntry = GetGridEntryFromRow(row); + + if (gridEntry == null || rect.IsEmpty) + return; + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Drawing label for property " + gridEntry.PropertyLabel); + + Point newOrigin = new Point(rect.X, rect.Y); + Rectangle cr = Rectangle.Intersect(rect, clipRect); + + if (cr.IsEmpty) { + return; + } + + AdjustOrigin(g, newOrigin, ref rect); + cr.Offset(-newOrigin.X, -newOrigin.Y); + + try { + try + { + bool fLongLabel = false; + int labelEnd = 0; + int labelIndent = GetIPELabelIndent(gridEntry); + + if (fLongLabelRequest) + { + labelEnd = GetIPELabelLength(g, gridEntry); + fLongLabel = IsIPELabelLong(g, gridEntry); + } + + gridEntry.PaintLabel(g, rect, cr, selected, fLongLabel); + } + catch (Exception ex) + { + Debug.Fail(ex.ToString()); + } + } + finally { + ResetOrigin(g); + } + } + + protected virtual void DrawValueEntry(System.Drawing.Graphics g, int row, ref Rectangle clipRect) { + GridEntry gridEntry = GetGridEntryFromRow(row); + if (gridEntry == null) + return; + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Drawing value for property " + gridEntry.PropertyLabel); + + Rectangle r = GetRectangle(row,ROWVALUE); + Point newOrigin = new Point(r.X, r.Y); + Rectangle cr = Rectangle.Intersect(clipRect, r); + + if (cr.IsEmpty) { + return; + } + + AdjustOrigin(g, newOrigin, ref r); + cr.Offset(-newOrigin.X, -newOrigin.Y); + + try { + try { + DrawValueEntry(g,r, cr,gridEntry,null, true); + } + catch { + } + } + finally { + ResetOrigin(g); + } + } + + private /*protected virtual*/ void DrawValueEntry(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, GridEntry gridEntry, object value, bool fetchValue) { + DrawValue(g, rect, clipRect, gridEntry, value, false, true, fetchValue, true); + } + private void DrawValue(System.Drawing.Graphics g, Rectangle rect, Rectangle clipRect, GridEntry gridEntry, object value, bool drawSelected, bool checkShouldSerialize, bool fetchValue, bool paintInPlace) { + GridEntry.PaintValueFlags paintFlags = GridEntry.PaintValueFlags.None; + + if (drawSelected) { + paintFlags |= GridEntry.PaintValueFlags.DrawSelected; + } + + if (checkShouldSerialize) { + paintFlags |= GridEntry.PaintValueFlags.CheckShouldSerialize; + } + + if (fetchValue) { + paintFlags |= GridEntry.PaintValueFlags.FetchValue; + } + + if (paintInPlace) { + paintFlags |= GridEntry.PaintValueFlags.PaintInPlace; + } + + gridEntry.PaintValue(value, g, rect, clipRect, paintFlags); + } + + private void F4Selection(bool popupModalDialog) { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) return; + + // if we are in an errorState, just put the focus back on the Edit + if (errorState != ERROR_NONE && Edit.Visible) { + Edit.FocusInternal(); + return; + } + + if (DropDownButton.Visible) { + PopupDialog(selectedRow); + } + else if (DialogButton.Visible) { + if (popupModalDialog) { + PopupDialog(selectedRow); + } + else { + DialogButton.FocusInternal(); + } + } + else if (Edit.Visible) { + Edit.FocusInternal(); + SelectEdit(false); + } + return; + } + + //The following Suppress message calls are required so we can handle + //errors when attempting to generate an event handler when the document + //is read-only. This code is a duplicate of the error handling in + //CommitValue(). + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + public void DoubleClickRow(int row, bool toggleExpand, int type) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:DoubleClickRow"); + GridEntry gridEntry = GetGridEntryFromRow(row); + if (gridEntry == null) return; + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Property " + gridEntry.PropertyLabel + " double clicked"); + + if (!toggleExpand || type == ROWVALUE) { + try { + bool action = gridEntry.DoubleClickPropertyValue(); + if (action) { + SelectRow(row); + return; + } + } + catch (Exception ex) { + SetCommitError(ERROR_THROWN); + ShowInvalidMessage(gridEntry.PropertyLabel, null, ex); + return; + } + } + + SelectGridEntry(gridEntry, true); + + if (type == ROWLABEL && toggleExpand && gridEntry.Expandable) { + SetExpand(gridEntry,!gridEntry.InternalExpanded); + return; + } + + if (gridEntry.IsValueEditable && gridEntry.Enumerable) { + int index = GetCurrentValueIndex(gridEntry); + + if (index != -1) { + object[] values = gridEntry.GetPropertyValueList(); + + if (values == null || index >= (values.Length - 1)) { + index = 0; + } + else { + index++; + } + + CommitValue(values[index]); + SelectRow(selectedRow); + Refresh(); + return; + } + } + + if (Edit.Visible) { + Edit.FocusInternal(); + SelectEdit(false); + return; + } + } + + public Font GetBaseFont() { + return Font; + } + + public Font GetBoldFont() { + if (fontBold == null) { + fontBold = new Font(this.Font, FontStyle.Bold); + } + return fontBold; + } + + internal IntPtr GetBaseHfont() { + if (baseHfont == IntPtr.Zero) { + baseHfont = GetBaseFont().ToHfont(); + } + return baseHfont; + } + + /// + /// Gets the element from point. + /// + /// The point x coordinate. + /// The point y coordinate. + /// The found grid element. + internal GridEntry GetElementFromPoint(int x, int y) { + Point point = new Point(x, y); + var allGridEntries = GetAllGridEntries(); + GridEntry[] targetEntries = new GridEntry[allGridEntries.Count]; + try { + GetGridEntriesFromOutline(allGridEntries, 0, allGridEntries.Count - 1, targetEntries); + } + catch (Exception ex) { + Debug.Fail(ex.ToString()); + } + + foreach (GridEntry gridEntry in targetEntries) { + if (gridEntry.AccessibilityObject.Bounds.Contains(point)) { + return gridEntry; + } + } + + return null; + } + + internal IntPtr GetBoldHfont() { + if (boldHfont == IntPtr.Zero) { + boldHfont = GetBoldFont().ToHfont(); + } + return boldHfont; + } + + + private bool GetFlag(short flag) { + return (this.flags & flag) != 0; + } + + public virtual Color GetLineColor() { + return ownerGrid.LineColor; + } + + public virtual Brush GetLineBrush(Graphics g) { + if (ownerGrid.lineBrush == null) { + Color clr = g.GetNearestColor(ownerGrid.LineColor); + ownerGrid.lineBrush = new SolidBrush(clr); + } + return ownerGrid.lineBrush; + } + + public virtual Color GetSelectedItemWithFocusForeColor() + { + return ownerGrid.SelectedItemWithFocusForeColor; + } + + public virtual Color GetSelectedItemWithFocusBackColor() + { + return ownerGrid.SelectedItemWithFocusBackColor; + } + + public virtual Brush GetSelectedItemWithFocusBackBrush(Graphics g) + { + if (ownerGrid.selectedItemWithFocusBackBrush == null) { + Color clr = g.GetNearestColor(ownerGrid.SelectedItemWithFocusBackColor); + ownerGrid.selectedItemWithFocusBackBrush = new SolidBrush(clr); + } + return ownerGrid.selectedItemWithFocusBackBrush; + } + + public virtual IntPtr GetHostHandle() { + return Handle; + } + + public virtual int GetLabelWidth() { + return InternalLabelWidth; + } + + internal bool IsExplorerTreeSupported { + get { + if (ownerGrid.CanShowVisualStyleGlyphs && UnsafeNativeMethods.IsVista && VisualStyleRenderer.IsSupported) { + return true; + } + + return false; + } + } + + public virtual int GetOutlineIconSize() { + if (IsExplorerTreeSupported) { + return outlineSizeExplorerTreeStyle; + } + else { + return outlineSize; + } + } + + public virtual int GetGridEntryHeight() { + return RowHeight; + } + + // for qa automation + internal int GetPropertyLocation(string propName, bool getXY, bool rowValue) { + if (allGridEntries != null && allGridEntries.Count > 0) { + for (int i = 0; i < allGridEntries.Count; i++) { + if (0 == String.Compare(propName, allGridEntries.GetEntry(i).PropertyLabel, true, CultureInfo.InvariantCulture)) { + if (getXY) { + int row = GetRowFromGridEntry(allGridEntries.GetEntry(i)); + + if (row < 0 || row >= this.visibleRows) { + return -1; + } + else { + Rectangle r = GetRectangle(row, rowValue ? ROWVALUE : ROWLABEL); + return(r.Y << 16 | (r.X & 0xFFFF)); + } + } + else { + return i; + } + } + } + } + return -1; + } + + public new object GetService(Type classService) { + if (classService == typeof(IWindowsFormsEditorService)) { + return this; + } + if (ServiceProvider != null) { + return serviceProvider.GetService(classService); + } + return null; + } + + public virtual int GetSplitterWidth() { + return 1; + } + + public virtual int GetTotalWidth() { + return GetLabelWidth() + GetSplitterWidth() + GetValueWidth(); + } + + public virtual int GetValuePaintIndent() { + return paintIndent; + } + + public virtual int GetValuePaintWidth() { + return paintWidth; + } + + public virtual int GetValueStringIndent() { + return EDIT_INDENT; + } + + public virtual int GetValueWidth() { + return(int)(InternalLabelWidth * (labelRatio - 1)); + } + + private void SetDropDownWindowPosition(Rectangle rect, bool setBounds = false) { + Size size = dropDownHolder.Size; + + // Setting size to the width of selected row value field at the minimum. + size.Width = Math.Max(rect.Width + 1, size.Width); + + Point loc = PointToScreen(new Point(0, 0)); + Rectangle rectScreen = Screen.FromControl(Edit).WorkingArea; + + loc.X = Math.Min(rectScreen.X + rectScreen.Width - size.Width, + Math.Max(rectScreen.X, loc.X + rect.X + rect.Width - size.Width)); + loc.Y += rect.Y; + if (rectScreen.Y + rectScreen.Height < (size.Height + loc.Y + Edit.Height)) { + loc.Y -= size.Height; + dropDownHolder.ResizeUp = true; + } + else { + loc.Y += rect.Height + 1; + dropDownHolder.ResizeUp = false; + } + + int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE; + + if (loc.X == 0 && loc.Y == 0) { + flags |= NativeMethods.SWP_NOMOVE; + } + + if (this.Width == size.Width && this.Height == size.Height) { + flags |= NativeMethods.SWP_NOSIZE; + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this.dropDownHolder, this.dropDownHolder.Handle), NativeMethods.NullHandleRef, loc.X, loc.Y, size.Width, size.Height, flags); + if (setBounds) { + dropDownHolder.SetBounds(loc.X, loc.Y, size.Width, size.Height); + } + } + + /// + /// + /// Displays the provided control in a drop down. When possible, the + /// current dimensions of the control will be respected. If this is not possible + /// for the current screen layout the control may be resized, so it should + /// be implemented using appropriate docking and anchoring so it will resize + /// nicely. If the user performs an action that would cause the drop down + /// to prematurely disappear the control will be hidden. + /// + public void /* cpr IWindowsFormsEditorService. */ DropDownControl(Control ctl) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:DropDownControl"); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "DropDownControl(ctl = " + ctl.GetType().Name + ")"); + if (dropDownHolder == null) { + dropDownHolder = new DropDownHolder(this); + } + + dropDownHolder.Visible = false; + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + Rectangle rect = GetRectangle(selectedRow, ROWVALUE); + + dropDownHolder.SuspendAllLayout(dropDownHolder); + // Parenting the dropdown holder - may cause WM_DPI changed event + UnsafeNativeMethods.SetWindowLong(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(this, Handle)); + + // WMDPI changed events are raised when parent DPi changed or windows is positioned on a screen with different Dpi than primary monitor. + // So order of the parenting and positioning of the window need to be in synch to avoid repetitive WM_DPI changed messages. + SetDropDownWindowPosition(rect); + + dropDownHolder.SetComponent(ctl, GetFlag(FlagResizableDropDown)); + + //Set window position but not bounds to avoid moving window to primary monitor ( set bounds cause it ). + SetDropDownWindowPosition(rect); + dropDownHolder.ResumeAllLayout(dropDownHolder,true); + + // control is a top=level window. standard way of setparent on the control is prohibited for top-level controls. + // It is unknown why this control was created as a top-level control. Windows does not recommend this way of setting parent. + // We are not touching this for this release. We may revisit it in next release. + SafeNativeMethods.ShowWindow(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.SW_SHOWNA); + SetDropDownWindowPosition(rect, setBounds: true); + } + else { + dropDownHolder.SetComponent(ctl, GetFlag(FlagResizableDropDown)); + Rectangle rect = GetRectangle(selectedRow, ROWVALUE); + Size size = dropDownHolder.Size; + Point loc = PointToScreen(new Point(0, 0)); + Rectangle rectScreen = Screen.FromControl(Edit).WorkingArea; + size.Width = Math.Max(rect.Width + 1, size.Width); + + // Not needed... CYMAXDDLHEIGHT used to be 200, but why limit it??? + //size.Height = Math.Min(size.Height,CYMAXDDLHEIGHT); + + loc.X = Math.Min(rectScreen.X + rectScreen.Width - size.Width, + Math.Max(rectScreen.X, loc.X + rect.X + rect.Width - size.Width)); + loc.Y += rect.Y; + if (rectScreen.Y + rectScreen.Height < (size.Height + loc.Y + Edit.Height)) { + loc.Y -= size.Height; + dropDownHolder.ResizeUp = true; + } + else { + loc.Y += rect.Height + 1; + dropDownHolder.ResizeUp = false; + } + + // control is a top=level window. standard way of setparent on the control is prohibited for top-level controls. + // It is unknown why this control was created as a top-level control. Windows does not recommend this way of setting parent. + // We are not touching this for this relase. We may revisit it in next release. + UnsafeNativeMethods.SetWindowLong(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.GWL_HWNDPARENT, new HandleRef(this, Handle)); + dropDownHolder.SetBounds(loc.X, loc.Y, size.Width, size.Height); + SafeNativeMethods.ShowWindow(new HandleRef(dropDownHolder, dropDownHolder.Handle), NativeMethods.SW_SHOWNA); + } + + Edit.Filter = true; + dropDownHolder.Visible = true; + dropDownHolder.FocusComponent(); + SelectEdit(false); + + if (AccessibilityImprovements.Level3) { + var gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry != null) { + gridEntry.AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + gridEntry.AccessibilityObject.RaiseAutomationPropertyChangedEvent( + NativeMethods.UIA_ExpandCollapseExpandCollapseStatePropertyId, + UnsafeNativeMethods.ExpandCollapseState.Collapsed, + UnsafeNativeMethods.ExpandCollapseState.Expanded); + } + } + + try { + DropDownButton.IgnoreMouse = true; + dropDownHolder.DoModalLoop(); + } + finally { + DropDownButton.IgnoreMouse = false; + } + + if (selectedRow != -1) { + FocusInternal(); + SelectRow(selectedRow); + } + } + + public virtual void DropDownDone() { + CloseDropDown(); + } + + public virtual void DropDownUpdate() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:DropDownUpdate"); + if (dropDownHolder != null && dropDownHolder.GetUsed()) { + int row = selectedRow; + GridEntry gridEntry = GetGridEntryFromRow(row); + Edit.Text = gridEntry.GetPropertyTextValue(); + } + } + + public bool EnsurePendingChangesCommitted() { + this.CloseDropDown(); + return this.Commit(); + } + + private bool FilterEditWndProc(ref Message m) { + // if it's the TAB key, we keep it since we'll give them focus with it. + if (dropDownHolder != null && dropDownHolder.Visible && m.Msg == NativeMethods.WM_KEYDOWN && (int)m.WParam != (int)Keys.Tab) { + Control ctl = dropDownHolder.Component; + if (ctl != null) { + m.Result = ctl.SendMessage(m.Msg, m.WParam, m.LParam); + return true; + } + } + return false; + } + + private bool FilterReadOnlyEditKeyPress(char keyChar) { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry.Enumerable && gridEntry.IsValueEditable) { + int index = GetCurrentValueIndex(gridEntry); + + object[] values = gridEntry.GetPropertyValueList(); + string letter = new string(new char[] {keyChar}); + for (int i = 0; i < values.Length; i++) { + object valueCur = values[(i + index + 1) % values.Length]; + string text = gridEntry.GetPropertyTextValue(valueCur); + if (text != null && text.Length > 0 && String.Compare(text.Substring(0,1), letter, true, CultureInfo.InvariantCulture) == 0) { + CommitValue(valueCur); + if (Edit.Focused) { + SelectEdit(false); + } + return true; + } + } + + } + return false; + } + + public virtual bool WillFilterKeyPress(char charPressed) { + if (!Edit.Visible) { + return false; + } + + Keys modifiers = ModifierKeys; + if ((int)(modifiers & ~Keys.Shift) != 0) { + return false; + } + + // try to activate the Edit. + // we don't activate for +,-, or * on expandable items because they have special meaning + // for the tree. + // + + if (selectedGridEntry != null) { + switch (charPressed) { + case '+': + case '-': + case '*': + return !selectedGridEntry.Expandable; + case unchecked( (char)(int)(long)Keys.Tab): + return false; + } + } + + return true; + } + + public void FilterKeyPress(char keyChar) { + + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) + return; + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:FilterKeyPress()"); + + Edit.FilterKeyPress(keyChar); + } + + private /*protected virtual*/ GridEntry FindEquivalentGridEntry(GridEntryCollection ipeHier) { + if (ipeHier == null || ipeHier.Count == 0) + return null; + GridEntryCollection rgipes = GetAllGridEntries(); + + if (rgipes == null || rgipes.Count == 0) { + return null; + } + + GridEntry targetEntry = null; + int row = 0; + int count = rgipes.Count; + + for (int i = 0; i < ipeHier.Count; i++) { + + if (ipeHier[i] == null) { + continue; + } + + // if we've got one above, and it's expandable, + // expand it + if (targetEntry != null) { + + // how many do we have? + int items = rgipes.Count; + + // expand and get the new count + if (!targetEntry.InternalExpanded) { + SetExpand(targetEntry, true); + rgipes = GetAllGridEntries(); + } + count = targetEntry.VisibleChildCount; + } + + int start = row; + targetEntry = null; + + // now, we will only go as many as were expanded... + for (; row < rgipes.Count && ((row - start) <= count); row++) { + if (ipeHier.GetEntry(i).NonParentEquals(rgipes[row])) { + targetEntry = rgipes.GetEntry(row); + row++; + break; + } + } + + // didn't find it... + if (targetEntry == null) { + break; + } + } + + return targetEntry; + } + + protected virtual Point FindPosition(int x, int y) { + if (RowHeight == -1) + return InvalidPosition; + Size size = this.GetOurSize(); + + if (x < 0 || x > size.Width + ptOurLocation.X) + return InvalidPosition; + Point pt = new Point(ROWLABEL,0); + if (x > InternalLabelWidth + ptOurLocation.X) + pt.X = ROWVALUE; + pt.Y = (y-ptOurLocation.Y)/(1+RowHeight); + return pt; + } + + public virtual void Flush() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView::Flush()"); + if (Commit() && Edit.Focused) { + this.FocusInternal(); + } + } + + private GridEntryCollection GetAllGridEntries() { + return GetAllGridEntries(false); + } + + private GridEntryCollection GetAllGridEntries(bool fUpdateCache) { + if (visibleRows == -1 || totalProps == -1 || !HasEntries) { + return null; + } + + if (allGridEntries != null && !fUpdateCache) { + return allGridEntries; + } + + GridEntry[] rgipes = new GridEntry[totalProps]; + try + { + GetGridEntriesFromOutline(topLevelGridEntries, 0, 0, rgipes); + } + catch (Exception ex) + { + Debug.Fail(ex.ToString()); + } + allGridEntries = new GridEntryCollection(null, rgipes); + AddGridEntryEvents(allGridEntries, 0, -1); + return allGridEntries; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private int GetCurrentValueIndex(GridEntry gridEntry) { + + if (!gridEntry.Enumerable) { + return -1; + } + + try { + object[] values = gridEntry.GetPropertyValueList(); + object value = gridEntry.PropertyValue; + string textValue = gridEntry.TypeConverter.ConvertToString(gridEntry, value); + + if (values != null && values.Length > 0) { + string itemTextValue; + int stringMatch = -1; + int equalsMatch = -1; + for (int i = 0; i < values.Length; i++) { + + object curValue = values[i]; + + // check real values against string values. + itemTextValue = gridEntry.TypeConverter.ConvertToString(curValue); + if (value == curValue || 0 == String.Compare(textValue, itemTextValue, true, CultureInfo.InvariantCulture)) { + stringMatch = i; + } + // now try .equals if they are both non-null + if (value != null && curValue != null && curValue.Equals(value)) { + equalsMatch = i; + } + + if (stringMatch == equalsMatch && stringMatch != -1) { + return stringMatch; + } + } + + if (stringMatch != -1) { + return stringMatch; + } + + if (equalsMatch != -1) { + return equalsMatch; + } + } + } + catch (Exception e) { + Debug.Fail(e.ToString()); + } + return -1; + + } + + public virtual int GetDefaultOutlineIndent() { + return OUTLINE_INDENT; + } + + private IHelpService GetHelpService() { + if (helpService == null && ServiceProvider != null) { + topHelpService = (IHelpService)ServiceProvider.GetService(typeof(IHelpService)); + if (topHelpService != null) { + IHelpService localHelpService = topHelpService.CreateLocalContext(HelpContextType.ToolWindowSelection); + if (localHelpService != null) { + helpService = localHelpService; + } + } + } + return helpService; + } + + public virtual int GetScrollOffset() { + //Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:GetScrollOffset"); + if (scrollBar == null) { + return 0; + } + int pos = ScrollBar.Value; + return pos; + } + + /// + /// + /// returns an array of IPE specifying the current heirarchy of ipes from the given + /// gridEntry through its parents to the root. + /// + private GridEntryCollection GetGridEntryHierarchy(GridEntry gridEntry) { + if (gridEntry == null) { + return null; + } + + int depth = gridEntry.PropertyDepth; + if (depth > 0) { + GridEntry[] entries = new GridEntry[depth + 1]; + + while (gridEntry != null && depth >= 0) { + entries[depth] = gridEntry; + gridEntry = gridEntry.ParentGridEntry; + depth = gridEntry.PropertyDepth; + } + return new GridEntryCollection(null, entries); + } + return new GridEntryCollection(null, new GridEntry[]{gridEntry}); + } + + private /*protected virtual*/ GridEntry GetGridEntryFromRow(int row) { + return GetGridEntryFromOffset(row + GetScrollOffset()); + } + + private /*protected virtual*/ GridEntry GetGridEntryFromOffset(int offset) { + GridEntryCollection rgipesAll = GetAllGridEntries(); + if (rgipesAll != null) { + if (offset >= 0 && offset < rgipesAll.Count) + return rgipesAll.GetEntry(offset); + } + return null; + } + + private /*protected virtual*/ int GetGridEntriesFromOutline(GridEntryCollection rgipe, int cCur, + int cTarget, GridEntry[] rgipeTarget) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:GetGridEntriesFromOutline"); + if (rgipe == null || rgipe.Count == 0) + return cCur; + + cCur--; // want to account for each entry as we find it. + + for (int cLocal = 0; cLocal < rgipe.Count; cLocal++) { + cCur++; + if (cCur >= cTarget + rgipeTarget.Length) + break; + + GridEntry ipeCur = rgipe.GetEntry(cLocal); + + //Debug.Assert(ipeCur != null, "Null IPE at position " + cLocal.ToString()); + + + if (cCur >= cTarget) + rgipeTarget[cCur - cTarget] = ipeCur; + + if (ipeCur.InternalExpanded) { + GridEntryCollection subGridEntry = ipeCur.Children; + //Debug.Assert(subGridEntry != null && subGridEntry.Length > 0 && subGridEntry[0] != null, "Expanded property " + ipeCur.PropertyLabel + " has no children!"); + if (subGridEntry != null && subGridEntry.Count > 0) { + cCur = GetGridEntriesFromOutline(subGridEntry, + cCur+1,cTarget,rgipeTarget); + } + } + } + + return cCur; + } + + private Size GetOurSize() { + Size size = ClientSize; + if (size.Width == 0) { + Size sizeWindow = Size; + if (sizeWindow.Width > 10) { + Debug.Fail("We have a bad client width!"); + size.Width = sizeWindow.Width; + size.Height = sizeWindow.Height; + } + } + if (!GetScrollbarHidden()) { + Size sizeScroll = ScrollBar.Size; + size.Width -= sizeScroll.Width; + } + size.Width -= 2; + size.Height -= 2; + return size; + } + + public Rectangle GetRectangle(int row, int flRow) { + Rectangle rect = new Rectangle(0,0,0,0); + Size size = this.GetOurSize(); + + rect.X = ptOurLocation.X; + + bool fLabel = ((flRow & ROWLABEL) != 0); + bool fValue = ((flRow & ROWVALUE) != 0); + + if (fLabel && fValue) { + rect.X = 1; + rect.Width = size.Width - 1; + } + else if (fLabel) { + rect.X = 1; + rect.Width = InternalLabelWidth - 1; + } + else if (fValue) { + rect.X = ptOurLocation.X + InternalLabelWidth; + rect.Width = size.Width - InternalLabelWidth; + } + + rect.Y = (row)*(RowHeight+1)+1+ptOurLocation.Y; + rect.Height = RowHeight; + + return rect; + } + + private int GetRowFromGridEntry(GridEntry gridEntry) { + GridEntryCollection rgipesAll = GetAllGridEntries(); + if (gridEntry == null || rgipesAll == null) + return -1; + + int bestMatch = -1; + + for (int i = 0; i < rgipesAll.Count; i++) { + + // try for an exact match. semantics of equals are a bit loose here... + // + if (gridEntry == rgipesAll[i]) { + return i - GetScrollOffset(); + } + else if (bestMatch == -1 && gridEntry.Equals(rgipesAll[i])) { + bestMatch = i - GetScrollOffset(); + } + } + + if (bestMatch != -1) { + return bestMatch; + } + + return -1 - GetScrollOffset(); + } + + internal int GetRowFromGridEntryInternal(GridEntry gridEntry) { + return GetRowFromGridEntry(gridEntry); + } + + public virtual bool GetInPropertySet() { + return GetFlag(FlagInPropertySet); + } + + protected virtual bool GetScrollbarHidden() { + if (scrollBar == null) { + return true; + } + return !ScrollBar.Visible; + } + + /// + /// + /// Returns a string containing test info about a given GridEntry. Requires an offset into the top-level + /// entry collection (ie. nested entries are not accessible). Or specify -1 to get info for the current + /// selected entry (which can be any entry, top-level or nested). + /// + public virtual string GetTestingInfo(int entry) { + GridEntry gridEntry = (entry < 0) ? GetGridEntryFromRow(selectedRow) : GetGridEntryFromOffset(entry); + + if (gridEntry == null) + return ""; + else + return gridEntry.GetTestingInfo(); + } + + public Color GetTextColor() { + return this.ForeColor; + } + + private void LayoutWindow(bool invalidate) { + Rectangle rect = ClientRectangle; + Size sizeWindow = new Size(rect.Width,rect.Height); + + if (scrollBar != null) { + Rectangle boundsScroll = ScrollBar.Bounds; + boundsScroll.X = sizeWindow.Width - boundsScroll.Width - 1; + boundsScroll.Y = 1; + boundsScroll.Height = sizeWindow.Height - 2; + ScrollBar.Bounds = boundsScroll; + } + + if (invalidate) { + Invalidate(); + } + } + + internal void InvalidateGridEntryValue(GridEntry ge) { + int row = GetRowFromGridEntry(ge); + if (row != -1) { + InvalidateRows(row, row, ROWVALUE); + } + } + + private void InvalidateRow(int row) { + InvalidateRows(row, row, ROWVALUE | ROWLABEL); + } + + + private void InvalidateRows(int startRow, int endRow) { + InvalidateRows(startRow, endRow, ROWVALUE | ROWLABEL); + } + + private void InvalidateRows(int startRow, int endRow, int type) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:InvalidateRows"); + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Invalidating rows " + startRow.ToString(CultureInfo.InvariantCulture) + " through " + endRow.ToString(CultureInfo.InvariantCulture)); + Rectangle rect; + + // invalidate from the start row down + if (endRow == -1) { + rect = GetRectangle(startRow, type); + rect.Height = (Size.Height - rect.Y) - 1; + Invalidate(rect); + } + else { + for (int i = startRow; i <= endRow; i++) { + rect = GetRectangle(i, type); + Invalidate(rect); + } + } + } + + + /// + /// + /// Overridden to handle TAB key. + /// + protected override bool IsInputKey(Keys keyData) { + switch (keyData & Keys.KeyCode) { + case Keys.Escape: + case Keys.Tab: + case Keys.F4: + return false; + + case Keys.Return: + if (Edit.Focused) { + return false; + } + break; + } + return base.IsInputKey(keyData); + } + + private bool IsMyChild(Control c) { + + if (c == this || c == null) { + return false; + } + + Control cParent = c.ParentInternal; + + while (cParent != null) { + if (cParent == this) { + return true; + } + cParent = cParent.ParentInternal; + } + return false; + } + + private bool IsScrollValueValid(int newValue) { + /*Debug.WriteLine("se.newValue = " + se.newValue.ToString()); + Debug.WriteLine("ScrollBar.Value = " + ScrollBar.Value.ToString()); + Debug.WriteLine("visibleRows = " + visibleRows.ToString()); + Debug.WriteLine("totalProps = " + totalProps.ToString()); + Debug.WriteLine("ScrollBar.Max = " + ScrollBar.Maximum.ToString()); + Debug.WriteLine("ScrollBar.LargeChange = " + ScrollBar.LargeChange.ToString());*/ + + // is this move valid? + if (newValue == ScrollBar.Value || + newValue < 0 || + newValue > ScrollBar.Maximum || + (newValue + (ScrollBar.LargeChange-1) >= totalProps)) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView: move not needed, returning"); + return false; + } + return true; + } + + internal bool IsSiblingControl(Control c1, Control c2) { + + Control parent1 = c1.ParentInternal; + Control parent2 = c2.ParentInternal; + + while (parent2 != null) { + if (parent1 == parent2) { + return true; + } + parent2 = parent2.ParentInternal; + } + return false; + } + + private void MoveSplitterTo(int xpos) { + + int widthPS = GetOurSize().Width; + int startPS = ptOurLocation.X; + int pos = Math.Max(Math.Min(xpos,widthPS-10),GetOutlineIconSize() * 2); + + int oldLabelWidth = InternalLabelWidth; + + labelRatio = ((double)widthPS / (double) (pos - startPS)); + + SetConstants(); + + if (selectedRow != -1) { + // do this to move any editor we have + SelectRow(selectedRow); + } + + Rectangle r = ClientRectangle; + + // if we're moving to the left, just invalidate the values + if (oldLabelWidth > InternalLabelWidth) { + int left = InternalLabelWidth - requiredLabelPaintMargin; + Invalidate(new Rectangle(left, 0, Size.Width - left, Size.Height)); + } + else { + // to the right, just invalidate from where the splitter was + // to the right + r.X = oldLabelWidth - requiredLabelPaintMargin; + r.Width -= r.X; + Invalidate(r); + } + } + + private void OnBtnClick(object sender, EventArgs e) { + + if (GetFlag(FlagBtnLaunchedEditor)) { + return; + } + + if (sender == DialogButton && !Commit()) { + return; + } + SetCommitError(ERROR_NONE); + + try { + Commit(); + SetFlag(FlagBtnLaunchedEditor, true); + PopupDialog(selectedRow); + } + finally { + SetFlag(FlagBtnLaunchedEditor, false); + } + } + + private void OnBtnKeyDown(object sender, KeyEventArgs ke) { + OnKeyDown(sender,ke); + } + + + private void OnChildLostFocus(object sender, EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnChildLostFocus"); + this.InvokeLostFocus(this, e); + } + + private void OnDropDownButtonGotFocus(object sender, EventArgs e) { + if (AccessibilityImprovements.Level3) { + DropDownButton dropDownButton = sender as DropDownButton; + if (dropDownButton != null) { + dropDownButton.AccessibilityObject.SetFocus(); + } + } + } + + protected override void OnGotFocus(EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnGotFocus"); + + base.OnGotFocus(e); + + if (e != null && !GetInPropertySet()) { + if (!Commit()) { + Edit.FocusInternal(); + return; + } + } + + if (selectedGridEntry != null && GetRowFromGridEntry(selectedGridEntry) != -1) { + selectedGridEntry.Focus = true; + SelectGridEntry(selectedGridEntry, false); + } + else { + SelectRow(0); + } + + if (selectedGridEntry != null && selectedGridEntry.GetValueOwner() != null) { + UpdateHelpAttributes(null, selectedGridEntry); + } + + // For empty GridView, draw a focus-indicator rectangle, just inside GridView borders + if ((totalProps <= 0) && AccessibilityImprovements.Level1) { + int doubleOffset = 2 * offset_2Units; + + if ((Size.Width > doubleOffset) && (Size.Height > doubleOffset)) { + using (Graphics g = CreateGraphicsInternal()) { + ControlPaint.DrawFocusRectangle(g, new Rectangle(offset_2Units, offset_2Units, Size.Width - doubleOffset, Size.Height - doubleOffset)); + } + } + } + } + + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnSysColorChange); + } + + protected override void OnHandleDestroyed(EventArgs e) { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnSysColorChange); + // We can leak this if we aren't disposed. + // + if (toolTip != null && !RecreatingHandle) { + toolTip.Dispose(); + toolTip = null; + } + base.OnHandleDestroyed(e); + } + + /* + public bool OnHelp() { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null || !(Focused || Edit.Focused)) { + return false; + } + + string keyword = gridEntry.HelpKeyword; + if (keyword != null && keyword.Length != 0) { + try { + IHelpService hsvc = GetHelpService(); + if (hsvc != null) { + hsvc.ShowHelpFromKeyword(keyword); + } + } + catch (Exception) { + } + } + return true; + } + + // This has no effect, see VSW#470693. + protected override void OnImeModeChanged(EventArgs e) { + // VSW #375530 + // Only update edit box mode if actually out of sync with grid's mode (to avoid re-entrancy issues) + // + if (edit != null && edit.ImeMode != this.ImeMode) { + // URT #51190 + // Keep the ImeMode of the property grid and edit box in step + // + edit.ImeMode = this.ImeMode; + } + base.OnImeModeChanged(e); + } + */ + + private void OnListChange(object sender, EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnListChange"); + if (!DropDownListBox.InSetSelectedIndex()) { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + Edit.Text = gridEntry.GetPropertyTextValue(DropDownListBox.SelectedItem); + Edit.FocusInternal(); + SelectEdit(false); + } + SetFlag(FlagDropDownCommit, true); + } + + private void OnListMouseUp(object sender, MouseEventArgs me) { + OnListClick(sender, me); + } + + private void OnListClick(object sender, EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnListClick"); + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + + if (DropDownListBox.Items.Count == 0) { + CommonEditorHide(); + SetCommitError(ERROR_NONE); + SelectRow(selectedRow); + return; + } + else { + object value = DropDownListBox.SelectedItem; + + // don't need the commit becuase we're committing anyway. + // + SetFlag(FlagDropDownCommit, false); + if (value != null && !CommitText((string)value)) { + SetCommitError(ERROR_NONE); + SelectRow(selectedRow); + } + } + } + + private void OnListDrawItem(object sender, DrawItemEventArgs die) { + int index = die.Index; + + if (index < 0 || selectedGridEntry == null) { + return; + } + + string text = (string)DropDownListBox.Items[die.Index]; + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Drawing list item, value='" + text + "'"); + die.DrawBackground(); + die.DrawFocusRectangle(); + + Rectangle drawBounds = die.Bounds; + drawBounds.Y += 1; + drawBounds.X -= 1; + + + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + try { + DrawValue(die.Graphics, drawBounds, drawBounds, gridEntry, gridEntry.ConvertTextToValue(text), (int)(die.State & DrawItemState.Selected) != 0, false, false, false); + } + catch (FormatException ex) { + ShowFormatExceptionMessage(gridEntry.PropertyLabel, text, ex); + if (DropDownListBox.IsHandleCreated) + DropDownListBox.Visible = false; + } + } + + private void OnListKeyDown(object sender, KeyEventArgs ke) { + if (ke.KeyCode == Keys.Return) { + OnListClick(null, null); + if (selectedGridEntry != null) { + selectedGridEntry.OnValueReturnKey(); + } + } + + OnKeyDown(sender,ke); + } + + protected override void OnLostFocus(EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnLostFocus"); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "PropertyGridView lost focus"); + + if (e != null) { + base.OnLostFocus(e); + } + if (this.FocusInside) { + base.OnLostFocus(e); + return; + } + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry != null) { + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "removing gridEntry focus"); + gridEntry.Focus = false;; + CommonEditorHide(); + InvalidateRow(selectedRow); + } + base.OnLostFocus(e); + + // For empty GridView, clear the focus indicator that was painted in OnGotFocus() + if (totalProps <= 0 && AccessibilityImprovements.Level1) { + using (Graphics g = CreateGraphicsInternal()) { + Rectangle clearRect = new Rectangle(1, 1, Size.Width - 2, Size.Height - 2); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Filling empty gridview rect=" + clearRect.ToString()); + + g.FillRectangle(backgroundBrush, clearRect); + } + } + } + + private void OnEditChange(object sender, EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditChange"); + SetCommitError(ERROR_NONE, Edit.Focused); + + ToolTip.ToolTip = ""; + ToolTip.Visible = false; + + if (!Edit.InSetText()) { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry != null && (gridEntry.Flags & GridEntry.FLAG_IMMEDIATELY_EDITABLE) != 0) + Commit(); + } + } + + private void OnEditGotFocus(object sender, EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditGotFocus"); + + if (!Edit.Visible) { + this.FocusInternal(); + return; + } + + switch (errorState) { + case ERROR_MSGBOX_UP: + return; + case ERROR_THROWN: + if (Edit.Visible) { + Edit.HookMouseDown = true; + } + break; + default: + if (this.NeedsCommit) { + SetCommitError(ERROR_NONE, true); + } + break; + } + + if (selectedGridEntry != null && GetRowFromGridEntry(selectedGridEntry) != -1) { + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "adding gridEntry focus"); + selectedGridEntry.Focus = true; + InvalidateRow(selectedRow); + (Edit.AccessibilityObject as ControlAccessibleObject).NotifyClients(AccessibleEvents.Focus); + + if (AccessibilityImprovements.Level3) { + Edit.AccessibilityObject.SetFocus(); + } + } + else { + SelectRow(0); + } + } + /* + private void OnEditImeModeChanged(object sender, EventArgs e) { + // URT #51190 + // The property grid ImeMode tracks the ImeMode of the edit control. + // We require this because the first character the goes into the edit control + // is composed while the PropertyGrid still has focus - so the ImeMode + // of the grid and the edit need to be the same or we get inconsistent IME composition. + // + if (this.ImeMode != edit.ImeMode) { + this.ImeMode = edit.ImeMode; + } + } + */ + + private bool ProcessEnumUpAndDown(GridEntry gridEntry, Keys keyCode, bool closeDropDown = true) { + object value = gridEntry.PropertyValue; + object[] rgvalues = gridEntry.GetPropertyValueList(); + if (rgvalues != null) { + for (int i = 0; i < rgvalues.Length; i++) { + object rgvalue = rgvalues[i]; + if (value != null && rgvalue != null && value.GetType() != rgvalue.GetType() && gridEntry.TypeConverter.CanConvertTo(gridEntry, value.GetType())) { + rgvalue = gridEntry.TypeConverter.ConvertTo(gridEntry, CultureInfo.CurrentCulture, rgvalue, value.GetType()); + } + + bool equal = (value == rgvalue) || (value != null && value.Equals(rgvalue)); + + if (!equal && value is string && rgvalue != null) { + equal = 0 == String.Compare((string)value, rgvalue.ToString(), true, CultureInfo.CurrentCulture); + } + if (equal) { + object valueNew = null; + if (keyCode == Keys.Up) { + if (i == 0) return true; + valueNew = rgvalues[i - 1]; + } + else { + if (i == rgvalues.Length - 1) return true; + valueNew = rgvalues[i + 1]; + } + + CommitValue(gridEntry, valueNew, closeDropDown); + SelectEdit(false); + return true; + } + } + } + + return false; + } + + private void OnEditKeyDown(object sender, KeyEventArgs ke) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditKeyDown"); + bool fAlt = ke.Alt; + if (!fAlt && (ke.KeyCode == Keys.Up || ke.KeyCode == Keys.Down)) { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (!gridEntry.Enumerable || !gridEntry.IsValueEditable) { + return; + } + + ke.Handled = true; + bool processed = ProcessEnumUpAndDown(gridEntry, ke.KeyCode); + if (processed) { + return; + } + } + // VS7 # 13336: handle non-expand/collapse case of left & right as up & down + else if ((ke.KeyCode == Keys.Left || ke.KeyCode == Keys.Right) && + (ke.Modifiers & ~Keys.Shift) != 0) { + return; + } + OnKeyDown(sender,ke); + } + + private void OnEditKeyPress(object sender, KeyPressEventArgs ke) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditKeyPress"); + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) + return; + + if (!gridEntry.IsTextEditable) { + ke.Handled = FilterReadOnlyEditKeyPress(ke.KeyChar); + } + } + + + private void OnEditLostFocus(object sender, EventArgs e) { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditLostFocus"); + + // believe it or not, this can actually happen. + if (Edit.Focused || (errorState == ERROR_MSGBOX_UP) || (errorState == ERROR_THROWN)|| GetInPropertySet()) { + return; + } + + // check to see if the focus is on the drop down or one of it's children + // if so, return; + if (dropDownHolder != null && dropDownHolder.Visible) { + bool found = false; + for (IntPtr hwnd = UnsafeNativeMethods.GetForegroundWindow(); + hwnd != IntPtr.Zero; hwnd = UnsafeNativeMethods.GetParent(new HandleRef(null, hwnd))) { + if (hwnd == dropDownHolder.Handle) { + found = true; + } + } + if (found) + return; + } + + if (this.FocusInside) { + return; + } + + // if the focus isn't goint to a child of the view + if (!Commit()) { + Edit.FocusInternal(); + return; + } + // change our focus state. + this.InvokeLostFocus(this, EventArgs.Empty); + } + + private void OnEditMouseDown(object sender, MouseEventArgs me) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEditMouseDown"); + + if (!FocusInside) { + SelectGridEntry(selectedGridEntry, false); + } + + if (me.Clicks % 2 == 0) { + DoubleClickRow(selectedRow,false, ROWVALUE); + Edit.SelectAll(); + } + + if (rowSelectTime == 0) { + return; + } + + // check if the click happened within the double click time since the row was selected. + // this allows the edits to be selected with two clicks instead of 3 (select row, double click). + // + long timeStamp = DateTime.Now.Ticks; + int delta = (int)((timeStamp - rowSelectTime) / 10000); // make it milleseconds + + if (delta < SystemInformation.DoubleClickTime) { + + Point screenPoint = Edit.PointToScreen(new Point(me.X, me.Y)); + + if (Math.Abs(screenPoint.X - rowSelectPos.X) < SystemInformation.DoubleClickSize.Width && + Math.Abs(screenPoint.Y - rowSelectPos.Y) < SystemInformation.DoubleClickSize.Height) { + DoubleClickRow(selectedRow,false, ROWVALUE); + Edit.SendMessage(NativeMethods.WM_LBUTTONUP, 0, (int)(me.Y << 16 | (me.X & 0xFFFF))); + Edit.SelectAll(); + } + rowSelectPos = Point.Empty; + + rowSelectTime = 0; + } + } + + private bool OnF4(Control sender) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnF4"); + if (ModifierKeys != 0) { + return false; + } + if (sender == this || sender == this.ownerGrid) + F4Selection(true); + else + UnfocusSelection(); + return true; + } + + private bool OnEscape(Control sender) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnEscape"); + if ((ModifierKeys & (Keys.Alt | Keys.Control)) != 0) { + return false; + } + + SetFlag(FlagDropDownCommit, false); + + if (sender == Edit && Edit.Focused) { + + // if we aren't in an error state, just quit + if (errorState == ERROR_NONE) { + Edit.Text = originalTextValue; + FocusInternal(); + return true; + } + + if (this.NeedsCommit) { + bool success = false; + Edit.Text = originalTextValue; + bool needReset = true; + + if (selectedGridEntry != null) { + + string curTextValue = selectedGridEntry.GetPropertyTextValue(); + needReset = originalTextValue != curTextValue && !(string.IsNullOrEmpty(originalTextValue) && string.IsNullOrEmpty(curTextValue)); + } + + if (needReset) { + try { + success = CommitText(originalTextValue); + } + catch { + } + } + else { + success = true; + } + + // this would be an odd thing to happen, but... + if (!success) { + Edit.FocusInternal(); + SelectEdit(false); + return true; + } + } + + SetCommitError(ERROR_NONE); + FocusInternal(); + return true; + } + else if (sender != this) { + CloseDropDown(); + FocusInternal(); + } + return false; + } + + protected override void OnKeyDown(KeyEventArgs ke) { + OnKeyDown(this,ke); + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We want to commit empty text. + // So we don't have to localize it. + ] + private void OnKeyDown(object sender, KeyEventArgs ke) { + + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) return; + + ke.Handled = true; + bool fControl = ke.Control; + bool fShift = ke.Shift; + bool fBoth = fControl && fShift; + bool fAlt = ke.Alt; + Keys keyCode = ke.KeyCode; + bool fallingThorugh = false; + + // Microsoft, we have to do this here because if we are + // hosted in a non-windows forms dialog, we never get a chance to + // peek at the messages, we just get called, + // so we have to do this here... + // + if (keyCode == Keys.Tab) { + if (ProcessDialogKey(ke.KeyData)) { + ke.Handled = true; + return; + } + } + + // Alt-Arrow support... sigh... + if (keyCode == Keys.Down && fAlt && DropDownButton.Visible) { + F4Selection(false); + return; + } + + if (keyCode == Keys.Up && fAlt && DropDownButton.Visible && (dropDownHolder != null) && dropDownHolder.Visible) { + UnfocusSelection(); + return; + } + + if (ToolTip.Visible) { + ToolTip.ToolTip = ""; + } + + if (fBoth || sender == this || sender == this.ownerGrid) { + switch (keyCode) { + case Keys.Up: + case Keys.Down: + int pos = (keyCode == Keys.Up ? selectedRow - 1 : selectedRow + 1); + SelectGridEntry(GetGridEntryFromRow(pos),true); + SetFlag(FlagNoDefault, false); + return; + case Keys.Left: + if (fControl) { + // move the splitter 3 pixels to the left + MoveSplitterTo(InternalLabelWidth - 3); + return; + } + if (gridEntry.InternalExpanded) + SetExpand(gridEntry,false); + else { + // VS7 # 13336: handle non-expand/collapse case of left & right as up & down + SelectGridEntry( GetGridEntryFromRow( selectedRow - 1 ), true ); + } + return; + case Keys.Right: + if (fControl) { + // move the splitter 3 pixels to the right + MoveSplitterTo(InternalLabelWidth + 3); + return; + } + if (gridEntry.Expandable) { + if (gridEntry.InternalExpanded) { + GridEntryCollection rgipes2 = gridEntry.Children; + SelectGridEntry(rgipes2.GetEntry(0),true); + } + else + SetExpand(gridEntry,true); + } + else { + // VS7 # 13336: handle non-expand/collapse case of left & right as up & down + SelectGridEntry( GetGridEntryFromRow( selectedRow + 1 ), true ); + } + return; + case Keys.Return: + if (gridEntry.Expandable) { + SetExpand(gridEntry,!gridEntry.InternalExpanded); + } + else { + gridEntry.OnValueReturnKey(); + } + + return; + case Keys.Home: + case Keys.End: + GridEntryCollection rgipes = GetAllGridEntries(); + int pos2 = (keyCode == Keys.Home ? 0 : rgipes.Count-1); + SelectGridEntry(rgipes.GetEntry(pos2),true); + return; + case Keys.Add: + case Keys.Oemplus: + case Keys.OemMinus: + case Keys.Subtract: + + if (!gridEntry.Expandable) { + break; + } + + SetFlag(FlagIsSpecialKey, true); + bool expand = (keyCode == Keys.Add || keyCode == Keys.Oemplus); + SetExpand(gridEntry,expand); + Invalidate(); + ke.Handled = true; + return; + + + case Keys.D8: + if (fShift) { + goto case Keys.Multiply; + } + break; + case Keys.Multiply: + SetFlag(FlagIsSpecialKey, true); + RecursivelyExpand(gridEntry,true, true, MaxRecurseExpand); + ke.Handled = false; + return; + + + + case Keys.Prior: //PAGE_UP: + case Keys.Next: //PAGE_DOWN + + bool next = (keyCode == Keys.Next); + //int rowGoal = next ? visibleRows - 1 : 0; + int offset = next ? visibleRows - 1 : 1 - visibleRows; + + int row = selectedRow; + + if (fControl && !fShift) { + return; + } + if (selectedRow != -1) { // actual paging. + + int start = GetScrollOffset(); + SetScrollOffset(start + offset); + SetConstants(); + if (GetScrollOffset() != (start + offset)) { + // we didn't make a full page + if (next) { + row = visibleRows - 1; + } + else { + row = 0; + } + } + } + + SelectRow(row); + Refresh(); + return; + + // Copy/paste support... + + case Keys.Insert: + if (fShift && !fControl && !fAlt) { + fallingThorugh = true; + goto case Keys.V; + } + goto case Keys.C; + case Keys.C: + // copy text in current property + if (fControl && !fAlt && !fShift) { + DoCopyCommand(); + return; + } + break; + case Keys.Delete: + // cut text in current property + if (fShift && !fControl && !fAlt) { + fallingThorugh = true; + goto case Keys.X; + } + break; + case Keys.X: + // cut text in current property + if (fallingThorugh || (fControl && !fAlt && !fShift)) { + Clipboard.SetDataObject(gridEntry.GetPropertyTextValue()); + CommitText(""); + return; + } + break; + case Keys.V: + // paste the text + if (fallingThorugh || (fControl && !fAlt && !fShift)) { + DoPasteCommand(); + } + break; + case Keys.A: + if (fControl && !fAlt && !fShift && Edit.Visible) { + Edit.FocusInternal(); + Edit.SelectAll(); + } + break; + } + } + + if (gridEntry != null && ke.KeyData == (Keys.C | Keys.Alt | Keys.Shift | Keys.Control)) { + Clipboard.SetDataObject(gridEntry.GetTestingInfo()); + return; + } + + /* Microsoft, VS30371 -- Due to conflicts with other VS commands, + we are removing this functionality. + + // Ctrl + Shift + 'X' selects the property that starts with 'X' + if (fBoth) { + // now get the array to work with. + GridEntry[] rgipes = GetAllGridEntries(); + int cLength = rgipes.Length; + + // now get our char. + string strCh = (new string(new char[] {(char)ke.KeyCode})).ToLower(CultureInfo.InvariantCulture); + + int cCur = -1; + if (gridEntry != null) + for (int i = 0; i < cLength; i++) { + if (rgipes[i] == gridEntry) { + cCur = i; + break; + } + } + + cCur += 1; // this indicated where we start... + // find next label that starts with this letter. + for (int i = 0; i < cLength; i++) { + GridEntry ipeCur = rgipes[(i + cCur) % cLength]; + if (ipeCur.PropertyLabel.ToLower(CultureInfo.InvariantCulture).StartsWith(strCh)) { + if (gridEntry != ipeCur) { + SelectGridEntry(ipeCur,true); + return; + } + break; + } + } + } + */ + + if (AccessibilityImprovements.Level3 && selectedGridEntry.Enumerable && + dropDownHolder != null && dropDownHolder.Visible && + (keyCode == Keys.Up || keyCode == Keys.Down)) + { + ProcessEnumUpAndDown(selectedGridEntry, keyCode, false); + } + + ke.Handled = false; + return; + } + + protected override void OnKeyPress(KeyPressEventArgs ke) { + + bool fControl = false; //ke.getControl(); + bool fShift = false; //ke.getShift(); + bool fBoth = fControl && fShift; + if (!fBoth && WillFilterKeyPress(ke.KeyChar)) + // find next property with letter typed. + FilterKeyPress(ke.KeyChar); + SetFlag(FlagIsSpecialKey, false); + } + + protected override void OnMouseDown(MouseEventArgs me) { + // check for a splitter + if (me.Button == MouseButtons.Left && SplitterInside(me.X,me.Y) && totalProps != 0) { + if (!Commit()) { + return; + } + + if (me.Clicks == 2) { + MoveSplitterTo(this.Width / 2); + return; + } + + UnfocusSelection(); + SetFlag(FlagIsSplitterMove, true); + tipInfo = -1; + CaptureInternal = true; + return; + } + + // are ew on a propentry? + Point pos = FindPosition(me.X,me.Y); + + if (pos == InvalidPosition) { + return; + } + + // Notify that prop entry of the click...but normalize + // it's coords first...we really just need the x, y + GridEntry gridEntry = GetGridEntryFromRow(pos.Y); + + if (gridEntry != null) { + // get the origin of this pe + Rectangle r = GetRectangle(pos.Y, ROWLABEL); + + lastMouseDown = new Point(me.X, me.Y); + + // offset the mouse points + // notify the prop entry + if (me.Button == MouseButtons.Left) { + gridEntry.OnMouseClick(me.X - r.X, me.Y - r.Y, me.Clicks, me.Button); + } + else { + SelectGridEntry(gridEntry, false); + } + lastMouseDown = InvalidPosition; + gridEntry.Focus = true; + SetFlag(FlagNoDefault, false); + } + } + + // this will make tool tip go away. + protected override void OnMouseLeave(EventArgs e) { + if (!GetFlag(FlagIsSplitterMove)) + Cursor = Cursors.Default; // Cursor = null;; + + base.OnMouseLeave(e); + } + + protected override void OnMouseMove(MouseEventArgs me) { + int rowMoveCur; + Point pt = Point.Empty; + bool onLabel = false; + + if (me == null) { + rowMoveCur = -1; + pt = InvalidPosition; + } + else { + pt = FindPosition(me.X,me.Y); + if (pt == InvalidPosition || (pt.X != ROWLABEL && pt.X != ROWVALUE)) { + rowMoveCur = -1; + ToolTip.ToolTip = ""; + } + else { + rowMoveCur = pt.Y; + onLabel = pt.X == ROWLABEL; + } + + } + + if (pt == InvalidPosition || me == null) { + return; + } + + if (GetFlag(FlagIsSplitterMove)) { + MoveSplitterTo(me.X); + } + + if ((rowMoveCur != this.TipRow || pt.X != this.TipColumn) && !GetFlag(FlagIsSplitterMove)) { + GridEntry gridItem = GetGridEntryFromRow(rowMoveCur); + string tip = ""; + tipInfo = -1; + + if (gridItem != null) { + Rectangle itemRect = GetRectangle(pt.Y, pt.X); + if (onLabel && gridItem.GetLabelToolTipLocation(me.X - itemRect.X, me.Y - itemRect.Y) != InvalidPoint) { + tip = gridItem.LabelToolTipText; + this.TipRow = rowMoveCur; + this.TipColumn = pt.X; + } + else if (!onLabel && gridItem.ValueToolTipLocation != InvalidPoint && !Edit.Focused) { + if (!this.NeedsCommit) { + tip = gridItem.GetPropertyTextValue(); + } + this.TipRow = rowMoveCur; + this.TipColumn = pt.X; + } + } + + // VSWhidbey 94890: Ensure that tooltips don't display when host application is not foreground app. + // Assume that we don't want to display the tooltips + IntPtr foregroundWindow = UnsafeNativeMethods.GetForegroundWindow(); + if (UnsafeNativeMethods.IsChild(new HandleRef(null, foregroundWindow), new HandleRef(null, this.Handle))) { + // vs 75848 -- don't show the tips if a + // dropdown is showing + if ((dropDownHolder == null || dropDownHolder.Component == null) || rowMoveCur == selectedRow) { + ToolTip.ToolTip = tip; + } + } + else { + ToolTip.ToolTip = ""; + } + } + + if (totalProps != 0 && (SplitterInside(me.X,me.Y) || GetFlag(FlagIsSplitterMove))) { + Cursor = Cursors.VSplit; + } + else { + Cursor = Cursors.Default; // Cursor = null;; + } + base.OnMouseMove(me); + } + + protected override void OnMouseUp(MouseEventArgs me) { + CancelSplitterMove(); + } + + protected override void OnMouseWheel(MouseEventArgs me) { + + this.ownerGrid.OnGridViewMouseWheel(me); + + HandledMouseEventArgs e = me as HandledMouseEventArgs; + if (e != null) { + if (e.Handled) { + return; + } + e.Handled = true; + } + + if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) { + return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down. + } + + int wheelScrollLines = SystemInformation.MouseWheelScrollLines; + if (wheelScrollLines == 0) { + return; // Do not scroll when the user system setting is 0 lines per notch + } + + Debug.Assert(this.cumulativeVerticalWheelDelta > -NativeMethods.WHEEL_DELTA, "cumulativeVerticalWheelDelta is too small"); + Debug.Assert(this.cumulativeVerticalWheelDelta < NativeMethods.WHEEL_DELTA, "cumulativeVerticalWheelDelta is too big"); + + // Should this only work if the Edit has focus? anyway + // we use the mouse wheel to change the values in the dropdown if it's + // an enumerable value. + // + if (selectedGridEntry != null && selectedGridEntry.Enumerable && Edit.Focused && selectedGridEntry.IsValueEditable) { + int index = GetCurrentValueIndex(selectedGridEntry); + if (index != -1) { + int delta = me.Delta > 0 ? -1 : 1; + object[] values = selectedGridEntry.GetPropertyValueList(); + + if (delta > 0 && index >= (values.Length - 1)) { + index = 0; + } + else if (delta < 0 && index == 0) { + index = values.Length - 1; + } + else { + index += delta; + } + + CommitValue(values[index]); + SelectGridEntry(selectedGridEntry, true); + Edit.FocusInternal(); + return; + } + } + + + int initialOffset = GetScrollOffset(); + cumulativeVerticalWheelDelta += me.Delta; + float partialNotches = (float)cumulativeVerticalWheelDelta / (float)NativeMethods.WHEEL_DELTA; + int fullNotches = (int) partialNotches; + + if (wheelScrollLines == -1) { + + // Equivalent to large change scrolls + if (fullNotches != 0) { + + int originalOffset = initialOffset; + int large = fullNotches * this.scrollBar.LargeChange; + int newOffset = Math.Max(0,initialOffset - large); + newOffset = Math.Min(newOffset, totalProps - visibleRows+1); + + + initialOffset -= fullNotches * this.scrollBar.LargeChange; + if (Math.Abs(initialOffset - originalOffset) >= Math.Abs(fullNotches * this.scrollBar.LargeChange)) { + this.cumulativeVerticalWheelDelta -= fullNotches * NativeMethods.WHEEL_DELTA; + } + else { + this.cumulativeVerticalWheelDelta = 0; + } + + if (!ScrollRows(newOffset)) { + this.cumulativeVerticalWheelDelta = 0; + return; + } + } + } + else { + + + // SystemInformation.MouseWheelScrollLines doesn't work under terminal server, + // it default to the notches per scroll. + int scrollBands = (int) ((float) wheelScrollLines * partialNotches); + + if (scrollBands != 0) { + + if (ToolTip.Visible) { + ToolTip.ToolTip = ""; + } + + int newOffset = Math.Max(0,initialOffset - scrollBands); + newOffset = Math.Min(newOffset, totalProps - visibleRows+1); + + if (scrollBands > 0) { + + if (this.scrollBar.Value <= this.scrollBar.Minimum) { + this.cumulativeVerticalWheelDelta = 0; + } + else { + this.cumulativeVerticalWheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + } + else { + + if (this.scrollBar.Value > (scrollBar.Maximum-visibleRows+1)) { + this.cumulativeVerticalWheelDelta = 0; + } + else { + this.cumulativeVerticalWheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + } + + if (!ScrollRows(newOffset)) { + this.cumulativeVerticalWheelDelta = 0; + return; + } + } + else { + this.cumulativeVerticalWheelDelta = 0; + } + + } + } + + protected override void OnMove(EventArgs e) { + CloseDropDown(); + } + + protected override void OnPaintBackground(PaintEventArgs pe) { + } + + protected override void OnPaint(PaintEventArgs pe) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnPaint"); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "On paint called. Rect=" + pe.ClipRectangle.ToString()); + Graphics g = pe.Graphics; + + int yPos = 0; + int startRow = 0; + int endRow = visibleRows - 1; + + Rectangle clipRect = pe.ClipRectangle; + + // give ourselves a little breathing room to account for lines, etc., as well + // as the entries themselves. + // + clipRect.Inflate(0,2); + + try { + Size sizeWindow = this.Size; + + // figure out what rows we're painting + Point posStart = FindPosition(clipRect.X, clipRect.Y); + Point posEnd = FindPosition(clipRect.X, clipRect.Y + clipRect.Height); + if (posStart != InvalidPosition) { + startRow = Math.Max(0,posStart.Y); + } + + if (posEnd != InvalidPosition) { + endRow = posEnd.Y; + } + + int cPropsVisible = Math.Min(totalProps - GetScrollOffset(),1+visibleRows); + +#if DEBUG + GridEntry debugIPEStart = GetGridEntryFromRow(startRow); + GridEntry debugIPEEnd = GetGridEntryFromRow(endRow); + string startName = debugIPEStart == null ? null : debugIPEStart.PropertyLabel; + if (startName == null) { + startName = "(null)"; + } + string endName = debugIPEEnd == null ? null : debugIPEEnd.PropertyLabel; + if (endName == null) { + endName = "(null)"; + } +#endif + + SetFlag(FlagNeedsRefresh, false); + + //SetConstants(); + + Size size = this.GetOurSize(); + Point loc = this.ptOurLocation; + + if (GetGridEntryFromRow(cPropsVisible-1) == null) { + cPropsVisible--; + } + + + // if we actually have some properties, then start drawing the grid + // + if (totalProps > 0) { + + // draw splitter + cPropsVisible = Math.Min(cPropsVisible, endRow+1); + + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Drawing splitter"); + Pen splitterPen = new Pen(ownerGrid.LineColor, GetSplitterWidth()); + splitterPen.DashStyle = DashStyle.Solid; + g.DrawLine(splitterPen, labelWidth,loc.Y,labelWidth, (cPropsVisible)*(RowHeight+1)+loc.Y); + splitterPen.Dispose(); + + // draw lines. + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Drawing lines"); + Pen linePen = new Pen(g.GetNearestColor(ownerGrid.LineColor)); + + int cHeightCurRow = 0; + int cLineEnd = loc.X + size.Width; + int cLineStart = loc.X; + + // draw values. + int totalWidth = GetTotalWidth() + 1; + //g.TextColor = ownerGrid.TextColor; + + // draw labels. set clip rect. + for (int i = startRow; i < cPropsVisible; i++) { + try { + + // draw the line + cHeightCurRow = (i)*(RowHeight+1) + loc.Y; + g.DrawLine(linePen, cLineStart,cHeightCurRow,cLineEnd,cHeightCurRow); + + // draw the value + DrawValueEntry(g,i, ref clipRect); + + // draw the label + Rectangle rect = GetRectangle(i,ROWLABEL); + yPos = rect.Y + rect.Height; + DrawLabel(g,i, rect, (i==selectedRow),false, ref clipRect); + if (i == selectedRow) { + Edit.Invalidate(); + } + + } + catch { + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Exception thrown during painting property " + GetGridEntryFromRow(i).PropertyLabel); + } + } + + // draw the bottom line + cHeightCurRow = (cPropsVisible)*(RowHeight+1) + loc.Y; + g.DrawLine(linePen, cLineStart,cHeightCurRow,cLineEnd,cHeightCurRow); + + linePen.Dispose(); + } + + // fill anything left with window + if (yPos < Size.Height) { + yPos++; + Rectangle clearRect = new Rectangle(1, yPos, Size.Width - 2, Size.Height - yPos - 1); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Filling remaining area rect=" + clearRect.ToString()); + + g.FillRectangle(backgroundBrush, clearRect); + } + + // draw outside border + using (Pen borderPen = new Pen(ownerGrid.ViewBorderColor, 1)) { + g.DrawRectangle(borderPen, 0, 0, sizeWindow.Width - 1, sizeWindow.Height - 1); + } + + fontBold = null; + } + catch { + Debug.Fail("Caught exception in OnPaint"); + // Do nothing. + } + finally { + ClearCachedFontInfo(); + } + } + + private void OnGridEntryLabelDoubleClick(object s, EventArgs e) { + GridEntry gridEntry = (GridEntry)s; + + // if we've changed since the click (probably because we moved a row into view), bail + // + if (gridEntry != lastClickedEntry) { + return; + } + int row = GetRowFromGridEntry(gridEntry); + DoubleClickRow(row, gridEntry.Expandable, ROWLABEL); + } + + private void OnGridEntryValueDoubleClick(object s, EventArgs e) { + + GridEntry gridEntry = (GridEntry)s; + // if we've changed since the click (probably because we moved a row into view), bail + // + if (gridEntry != lastClickedEntry) { + return; + } + int row = GetRowFromGridEntry(gridEntry); + DoubleClickRow(row, gridEntry.Expandable, ROWVALUE); + } + + private void OnGridEntryLabelClick(object s, EventArgs e) { + this.lastClickedEntry = (GridEntry)s; + SelectGridEntry(lastClickedEntry, true); + } + + private void OnGridEntryOutlineClick(object s, EventArgs e) { + GridEntry gridEntry = (GridEntry)s; + Debug.Assert(gridEntry.Expandable, "non-expandable IPE firing outline click"); + + Cursor oldCursor = Cursor; + if (!ShouldSerializeCursor()) { + oldCursor = null; + } + Cursor = Cursors.WaitCursor; + + try { + SetExpand(gridEntry, !gridEntry.InternalExpanded); + SelectGridEntry(gridEntry, false); + } + finally { + Cursor = oldCursor; + } + } + + private void OnGridEntryValueClick(object s, EventArgs e) { + + this.lastClickedEntry = (GridEntry)s; + bool setSelectTime = s != selectedGridEntry; + SelectGridEntry(lastClickedEntry, true); + Edit.FocusInternal(); + + if (lastMouseDown != InvalidPosition) { + + // clear the row select time so we don't interpret this as a double click. + // + rowSelectTime = 0; + + Point editPoint = PointToScreen(lastMouseDown); + editPoint = Edit.PointToClientInternal(editPoint); + Edit.SendMessage(NativeMethods.WM_LBUTTONDOWN, 0, (int)(editPoint.Y << 16 | (editPoint.X & 0xFFFF))); + Edit.SendMessage(NativeMethods.WM_LBUTTONUP, 0, (int)(editPoint.Y << 16 | (editPoint.X & 0xFFFF))); + } + + if (setSelectTime) { + rowSelectTime = DateTime.Now.Ticks; + rowSelectPos = PointToScreen(lastMouseDown); + } + else { + rowSelectTime = 0; + rowSelectPos = Point.Empty; + } + } + + private void ClearCachedFontInfo() { + if (baseHfont != IntPtr.Zero) { + SafeNativeMethods.ExternalDeleteObject(new HandleRef(this, baseHfont)); + baseHfont = IntPtr.Zero; + } + if (boldHfont != IntPtr.Zero) { + SafeNativeMethods.ExternalDeleteObject(new HandleRef(this, boldHfont)); + boldHfont = IntPtr.Zero; + } + } + + protected override void OnFontChanged(EventArgs e) { + + ClearCachedFontInfo(); + cachedRowHeight = -1; + + if (this.Disposing || this.ParentInternal == null || this.ParentInternal.Disposing) { + return; + } + + fontBold = null; // URT #45662 - fontBold is cached based on Font + + ToolTip.Font = this.Font; + SetFlag(FlagNeedUpdateUIBasedOnFont, true); + UpdateUIBasedOnFont(true); + base.OnFontChanged(e); + + if (selectedGridEntry != null) { + SelectGridEntry(selectedGridEntry, true); + } + } + + protected override void OnVisibleChanged(EventArgs e) { + if (this.Disposing || this.ParentInternal == null || this.ParentInternal.Disposing) { + return; + } + + if (this.Visible && this.ParentInternal != null) { + SetConstants(); + if (selectedGridEntry != null) { + SelectGridEntry(selectedGridEntry, true); + } + if (toolTip != null) { + ToolTip.Font = this.Font; + } + + } + + base.OnVisibleChanged(e); + } + + // a GridEntry recreated its children + protected virtual void OnRecreateChildren(object s, GridEntryRecreateChildrenEventArgs e) { + GridEntry parent = (GridEntry) s; + + if (parent.Expanded) { + + GridEntry[] entries = new GridEntry[allGridEntries.Count]; + allGridEntries.CopyTo(entries, 0); + + // find the index of the gridEntry that fired the event in our main list. + int parentIndex = -1; + for (int i = 0; i < entries.Length; i++) { + if (entries[i] == parent) { + parentIndex = i; + break; + } + } + + Debug.Assert(parentIndex != -1, "parent GridEntry not found in allGridEntries"); + + // clear our existing handlers + ClearGridEntryEvents(allGridEntries, parentIndex + 1, e.OldChildCount); + + // resize the array if it's changed + if (e.OldChildCount != e.NewChildCount) { + int newArraySize = entries.Length + (e.NewChildCount - e.OldChildCount); + GridEntry[] newEntries = new GridEntry[newArraySize]; + + // copy the existing entries up to the parent + Array.Copy(entries, 0, newEntries, 0, parentIndex + 1); + + // copy the entries after the spot we'll be putting the new ones + Array.Copy(entries, parentIndex + e.OldChildCount+1, newEntries, parentIndex + e.NewChildCount+1, entries.Length - (parentIndex + e.OldChildCount + 1)); + + entries = newEntries; + } + + // from that point, replace the children with tne new children. + GridEntryCollection children = parent.Children; + int childCount = children.Count; + + Debug.Assert(childCount == e.NewChildCount, "parent reports " + childCount + " new children, event reports " + e.NewChildCount); + + // replace the changed items + for (int i = 0; i < childCount; i++) { + entries[parentIndex + i + 1] = children.GetEntry(i); + } + + // reset the array, rehook the handlers. + allGridEntries.Clear(); + allGridEntries.AddRange(entries); + AddGridEntryEvents(allGridEntries, parentIndex + 1, childCount); + + } + + if (e.OldChildCount != e.NewChildCount) { + totalProps = CountPropsFromOutline(topLevelGridEntries); + SetConstants(); + } + Invalidate(); + } + + protected override void OnResize(EventArgs e) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnResize"); + + Rectangle newRect = ClientRectangle; + int yDelta = lastClientRect == Rectangle.Empty ? 0 : newRect.Height - lastClientRect.Height; + bool lastRow = (selectedRow+1) == visibleRows; + + // if we are hiding or showing the scroll bar, update the selected row + // or if we are changing widths + // + bool sbVisible = ScrollBar.Visible; + + if (!lastClientRect.IsEmpty && newRect.Width > lastClientRect.Width) { + Rectangle rectInvalidate = new Rectangle(lastClientRect.Width-1,0,newRect.Width-lastClientRect.Width+1,lastClientRect.Height); + Invalidate(rectInvalidate); + } + + if (!lastClientRect.IsEmpty && yDelta > 0) { + Rectangle rectInvalidate = new Rectangle(0,lastClientRect.Height-1,lastClientRect.Width,newRect.Height-lastClientRect.Height+1); + Invalidate(rectInvalidate); + } + + int scroll = GetScrollOffset(); + SetScrollOffset(0); + SetConstants(); + SetScrollOffset(scroll); + + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + SetFlag(FlagNeedUpdateUIBasedOnFont, true); + UpdateUIBasedOnFont(true); + base.OnFontChanged(e); + } + + CommonEditorHide(); + + LayoutWindow(false); + // vs 69679 + bool selectionVisible = (selectedGridEntry != null && selectedRow >=0 && selectedRow <= visibleRows); + SelectGridEntry(selectedGridEntry, selectionVisible); + lastClientRect = newRect; + } + + private void OnScroll(object sender, ScrollEventArgs se) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:OnScroll(" + ScrollBar.Value.ToString(CultureInfo.InvariantCulture) + " -> " + se.NewValue.ToString(CultureInfo.InvariantCulture) +")"); + + if (!Commit() || !IsScrollValueValid(se.NewValue)) { + // cancel the move + se.NewValue = ScrollBar.Value; + return; + } + + int oldRow = -1; + GridEntry oldGridEntry = selectedGridEntry; + if (selectedGridEntry != null) { + oldRow = GetRowFromGridEntry(oldGridEntry); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "OnScroll: SelectedGridEntry=" + oldGridEntry.PropertyLabel); + } + + ScrollBar.Value = se.NewValue; + if (oldGridEntry != null) { + // we need to zero out the selected row so we don't try to commit again...since selectedRow is now bogus. + selectedRow = -1; + SelectGridEntry(oldGridEntry, (ScrollBar.Value == totalProps ? true : false)); + int newRow = GetRowFromGridEntry(oldGridEntry); + if (oldRow != newRow) { + Invalidate(); + } + } + else { + Invalidate(); + } + } + + private void OnSysColorChange(object sender, UserPreferenceChangedEventArgs e) { + if (e.Category == UserPreferenceCategory.Color || e.Category == UserPreferenceCategory.Accessibility) { + SetFlag(FlagNeedUpdateUIBasedOnFont, true); + } + } + + public virtual void PopupDialog(int row) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:PopupDialog"); + GridEntry gridEntry = GetGridEntryFromRow(row); + if (gridEntry != null) { + if (dropDownHolder != null && dropDownHolder.GetUsed()) { + CloseDropDown(); + return; + } + + bool fBtnDropDown = gridEntry.NeedsDropDownButton; + bool fEnum = gridEntry.Enumerable; + bool fBtnDialog = gridEntry.NeedsCustomEditorButton; + if (fEnum && !fBtnDropDown) { + DropDownListBox.Items.Clear(); + object value = gridEntry.PropertyValue; + object[] rgItems = gridEntry.GetPropertyValueList(); + int maxWidth = 0; + + // The listbox draws with GDI, not GDI+. So, we + // use a normal DC here. + // + IntPtr hdc = UnsafeNativeMethods.GetDC(new HandleRef(DropDownListBox, DropDownListBox.Handle)); + IntPtr hFont = Font.ToHfont(); + System.Internal.HandleCollector.Add(hFont, NativeMethods.CommonHandles.GDI); + NativeMethods.TEXTMETRIC tm = new NativeMethods.TEXTMETRIC(); + int iSel = -1; + try { + hFont = SafeNativeMethods.SelectObject(new HandleRef(DropDownListBox, hdc), new HandleRef(Font, hFont)); + + iSel = GetCurrentValueIndex(gridEntry); + if (rgItems != null && rgItems.Length > 0) { + string s; + IntNativeMethods.SIZE textSize = new IntNativeMethods.SIZE(); + + for (int i = 0; i < rgItems.Length; i++) { + s = gridEntry.GetPropertyTextValue(rgItems[i]); + DropDownListBox.Items.Add(s); + IntUnsafeNativeMethods.GetTextExtentPoint32(new HandleRef(DropDownListBox, hdc), s, textSize); + maxWidth = Math.Max((int) textSize.cx, maxWidth); + } + } + SafeNativeMethods.GetTextMetrics(new HandleRef(DropDownListBox, hdc), ref tm); + + // border + padding + scrollbar + maxWidth += 2 + tm.tmMaxCharWidth + SystemInformation.VerticalScrollBarWidth; + + hFont = SafeNativeMethods.SelectObject(new HandleRef(DropDownListBox, hdc), new HandleRef(Font, hFont)); + } + finally { + SafeNativeMethods.DeleteObject(new HandleRef(Font, hFont)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(DropDownListBox, DropDownListBox.Handle), new HandleRef(DropDownListBox, hdc)); + } + + // Microsoft, 4/25/1998 - must check for -1 and not call the set... + if (iSel != -1) { + DropDownListBox.SelectedIndex = iSel; + } + SetFlag(FlagDropDownCommit, false); + DropDownListBox.Height = Math.Max(tm.tmHeight + 2, Math.Min(maxListBoxHeight, DropDownListBox.PreferredHeight)); + DropDownListBox.Width = Math.Max(maxWidth, GetRectangle(row,ROWVALUE).Width); + try { + bool resizable = DropDownListBox.Items.Count > (DropDownListBox.Height / DropDownListBox.ItemHeight); + SetFlag(FlagResizableDropDown, resizable); + DropDownControl(DropDownListBox); + } + finally { + SetFlag(FlagResizableDropDown, false); + } + + Refresh(); + } + else if (fBtnDialog || fBtnDropDown) { + try { + SetFlag(FlagInPropertySet, true); + Edit.DisableMouseHook = true; + + try { + SetFlag(FlagResizableDropDown, gridEntry.UITypeEditor.IsDropDownResizable); + gridEntry.EditPropertyValue(this); + } + finally { + SetFlag(FlagResizableDropDown, false); + } + } + finally { + SetFlag(FlagInPropertySet, false); + Edit.DisableMouseHook = false; + } + Refresh(); + + // as/urt 31468 -- we can't do this because + // some dialogs are non-modal, and + // this will pull focus from them. + // See ASURT 31468. + // + //if (fBtnDialog) { + // this.Focus(); + //} + + if (FocusInside) { + SelectGridEntry(gridEntry, false); + } + } + } + } + + protected override bool ProcessDialogKey(Keys keyData) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ProcessDialogKey"); + if (HasEntries) { + Keys keyCode = keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.F4: + if (FocusInside) { + return OnF4(this); + } + break; + + case Keys.Tab: + + if (((keyData & Keys.Control) != 0) || + ((keyData & Keys.Alt) != 0)) { + break; + } + + bool forward = (keyData & Keys.Shift) == 0; + + Control focusedControl = Control.FromHandleInternal(UnsafeNativeMethods.GetFocus()); + + if (focusedControl == null || !IsMyChild(focusedControl)) { + if (forward) { + TabSelection(); + focusedControl = Control.FromHandleInternal(UnsafeNativeMethods.GetFocus()); + // make sure the value actually took the focus + if (IsMyChild(focusedControl)) { + return true; + } + else { + return base.ProcessDialogKey(keyData); + } + + } + else { + break; + } + } + else { + // one of our editors has focus + + if (Edit.Focused) { + if (forward) { + if (DropDownButton.Visible) { + DropDownButton.FocusInternal(); + return true; + } + else if (DialogButton.Visible) { + DialogButton.FocusInternal(); + return true; + } + // fall through + } + else { + SelectGridEntry(GetGridEntryFromRow(selectedRow), false); + return true; + } + } + else if (DialogButton.Focused || DropDownButton.Focused) { + if (!forward && Edit.Visible) { + Edit.FocusInternal(); + return true; + } + // fall through + } + } + break; + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + return false; + case Keys.Return: + if (DialogButton.Focused || DropDownButton.Focused) { + OnBtnClick((DialogButton.Focused ? DialogButton : DropDownButton), new EventArgs()); + return true; + } + else if (selectedGridEntry != null && selectedGridEntry.Expandable) { + SetExpand(selectedGridEntry, !selectedGridEntry.InternalExpanded); + return true; + } + break; + } + } + return base.ProcessDialogKey(keyData); + } + + protected virtual void RecalculateProps() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:RecalculateProps"); + int props = CountPropsFromOutline(topLevelGridEntries); + if (totalProps != props) { + totalProps = props; + ClearGridEntryEvents(allGridEntries, 0, -1); + allGridEntries = null; + } + } + + internal /*public virtual*/ void RecursivelyExpand(GridEntry gridEntry, bool fInit, bool expand, int maxExpands) { + + if (gridEntry == null || (expand && --maxExpands < 0)) { + return; + } + + SetExpand(gridEntry, expand); + + GridEntryCollection rgipes = gridEntry.Children; + if (rgipes != null) + for (int i = 0; i < rgipes.Count; i++) + RecursivelyExpand(rgipes.GetEntry(i),false, expand, maxExpands); + + if (fInit) { + GridEntry ipeSelect = selectedGridEntry; + Refresh(); + SelectGridEntry(ipeSelect,false); + Invalidate(); + } + + } + + public override void Refresh() { + Refresh(false, -1, -1); + + //resetting gridoutline rect to recalculate before repaint when viewsort property changed. This is necessary especially when user + // changes sort and move to a secondary monitor with different DPI and change view sort back to original. + if (topLevelGridEntries != null && DpiHelper.EnableDpiChangedHighDpiImprovements) { + var outlineRectIconSize = this.GetOutlineIconSize(); + foreach (GridEntry gridentry in topLevelGridEntries) { + if (gridentry.OutlineRect.Height != outlineRectIconSize || gridentry.OutlineRect.Width != outlineRectIconSize) { + ResetOutline(gridentry); + } + } + } + + // make sure we got everything + Invalidate(); + } + + public void Refresh(bool fullRefresh) { + Refresh(fullRefresh, -1, -1); + } + + + GridPositionData positionData; + + private void Refresh(bool fullRefresh, int rowStart, int rowEnd) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Refresh"); + Debug.WriteLineIf(GridViewDebugPaint.TraceVerbose, "Refresh called for rows " + rowStart.ToString(CultureInfo.InvariantCulture) + " through " + rowEnd.ToString(CultureInfo.InvariantCulture)); + SetFlag(FlagNeedsRefresh, true); + GridEntry gridEntry = null; + + // VSWhidbey 361345 -- there are cases here where the grid could get be disposed. + // so just bail. + if (this.IsDisposed) { + return; + } + + bool pageInGridEntry = true; + + if (rowStart == -1) { + rowStart = 0; + } + + if (fullRefresh || this.ownerGrid.HavePropEntriesChanged()) { + if (HasEntries && !GetInPropertySet() && !Commit()) { + OnEscape(this); + } + + int oldLength = totalProps; + object oldObject = topLevelGridEntries == null || topLevelGridEntries.Count == 0 ? null : ((GridEntry)topLevelGridEntries[0]).GetValueOwner(); + + // walk up to the main IPE and refresh it. + if (fullRefresh) { + this.ownerGrid.RefreshProperties(true); + } + + if (oldLength > 0 && !GetFlag(FlagNoDefault)) { + positionData = CaptureGridPositionData(); + CommonEditorHide(true); + } + + UpdateHelpAttributes(selectedGridEntry, null); + selectedGridEntry = null; + SetFlag(FlagIsNewSelection, true); + topLevelGridEntries = this.ownerGrid.GetPropEntries(); + + + ClearGridEntryEvents(allGridEntries, 0, -1); + allGridEntries = null; + RecalculateProps(); + + int newLength = totalProps; + if (newLength > 0) { + if (newLength < oldLength) { + SetScrollbarLength(); + SetScrollOffset(0); + } + + SetConstants(); + + if (positionData != null) { + gridEntry = positionData.Restore(this); + + // Upon restoring the grid entry position, we don't + // want to page it in + // + object newObject = topLevelGridEntries == null || topLevelGridEntries.Count == 0 ? null : ((GridEntry)topLevelGridEntries[0]).GetValueOwner(); + pageInGridEntry = (gridEntry == null) || oldLength != newLength || newObject != oldObject; + } + + if (gridEntry == null) { + gridEntry = this.ownerGrid.GetDefaultGridEntry(); + SetFlag(FlagNoDefault, gridEntry == null && totalProps > 0); + } + + InvalidateRows(rowStart, rowEnd); + if (gridEntry == null) { + selectedRow = 0; + selectedGridEntry = GetGridEntryFromRow(selectedRow); + } + } + else if (oldLength == 0) { + return; + } + else { + SetConstants(); + } + + + // Release the old positionData which contains reference to previous selected objects. + positionData = null; + lastClickedEntry = null; + } + + if (!HasEntries) { + CommonEditorHide(selectedRow != -1); + this.ownerGrid.SetStatusBox(null, null); + SetScrollOffset(0); + selectedRow = -1; + Invalidate(); + return; + } + // in case we added or removed properties + + ownerGrid.ClearValueCaches(); + + InvalidateRows(rowStart, rowEnd); + + if (gridEntry != null) { + SelectGridEntry(gridEntry, pageInGridEntry); + } + } + + public virtual void Reset() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Reset"); + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) return; + + gridEntry.ResetPropertyValue(); + SelectRow(selectedRow); + } + + protected virtual void ResetOrigin(System.Drawing.Graphics g) { + g.ResetTransform(); + } + + internal void RestoreHierarchyState(ArrayList expandedItems) { + if (expandedItems == null) { + return; + } + + foreach(GridEntryCollection gec in expandedItems) { + FindEquivalentGridEntry(gec); + } + } + + public virtual DialogResult RunDialog(Form dialog) { + return ShowDialog(dialog); + } + + internal ArrayList SaveHierarchyState(GridEntryCollection entries) { + return SaveHierarchyState(entries, null); + } + + private ArrayList SaveHierarchyState(GridEntryCollection entries, ArrayList expandedItems) { + if (entries == null) { + return new ArrayList(); + } + + if (expandedItems == null) { + expandedItems = new ArrayList(); + } + + for (int i = 0; i < entries.Count; i++) { + if (((GridEntry)entries[i]).InternalExpanded) { + GridEntry entry = entries.GetEntry(i); + expandedItems.Add(GetGridEntryHierarchy(entry.Children.GetEntry(0))); + SaveHierarchyState(entry.Children, expandedItems); + } + } + + return expandedItems; + } + + // Scroll to the new offset + private bool ScrollRows(int newOffset) { + + GridEntry ipeCur = selectedGridEntry; + + if (!IsScrollValueValid(newOffset) || !Commit()) { + return false; + } + + bool showEdit = Edit.Visible; + bool showBtnDropDown = DropDownButton.Visible; + bool showBtnEdit = DialogButton.Visible; + + Edit.Visible = false; + DialogButton.Visible = false; + DropDownButton.Visible = false; + + SetScrollOffset(newOffset); + + if (ipeCur != null) { + int curRow = GetRowFromGridEntry(ipeCur); + if (curRow >=0 && curRow < visibleRows-1) { + Edit.Visible = showEdit; + DialogButton.Visible = showBtnEdit; + DropDownButton.Visible = showBtnDropDown; + SelectGridEntry(ipeCur, true); + } + else { + CommonEditorHide(); + } + } + else { + CommonEditorHide(); + } + + Invalidate(); + return true; + } + + private void SelectEdit(bool caretAtEnd) { + if (edit != null) { + Edit.SelectAll(); + } + } + + // select functions... selectGridEntry and selectRow will select a Row + // and install the appropriate editors. + // + internal /*protected virtual*/ void SelectGridEntry(GridEntry gridEntry, bool fPageIn) { + + if (gridEntry == null) return; + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:SelectGridEntry(" + gridEntry.PropertyLabel + ")"); + + int row = GetRowFromGridEntry(gridEntry); + if (row + GetScrollOffset() < 0) { + // throw exception? return false? + return; + } + + int maxRows = (int)Math.Ceiling(((double)GetOurSize().Height)/(1+RowHeight)); + + // Determine whether or not we need to page-in this GridEntry + // + + if (!fPageIn || (row >= 0 && row < (maxRows-1))) { + // No need to page-in: either fPageIn is false or the row is already in view + // + SelectRow(row); + } + else { + // Page-in the selected GridEntry + // + + selectedRow = -1; // clear the selected row since it's no longer a valid number + + int cOffset = GetScrollOffset(); + if (row < 0) { + SetScrollOffset(row + cOffset); + Invalidate(); + SelectRow(0); + } + else { + // try to put it one row up from the bottom + int newOffset = row + cOffset - (maxRows - 2); + + if (newOffset >= ScrollBar.Minimum && newOffset < ScrollBar.Maximum) { + SetScrollOffset(newOffset); + } + Invalidate(); + SelectGridEntry(gridEntry, false); + } + } + } + + private void SelectRow(int row) { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:SelectRow(" + row.ToString(CultureInfo.InvariantCulture) + ")"); + + if (!GetFlag(FlagIsNewSelection)) { + if (this.FocusInside) { + // If we're in an error state, we want to bail out of this. + if (errorState != ERROR_NONE || (row != selectedRow && !Commit())) { + return; + } + } + else { + FocusInternal(); + } + } + + GridEntry gridEntry = GetGridEntryFromRow(row); + + // Update our reset command. + // + if (row != selectedRow) { + UpdateResetCommand(gridEntry); + } + + if (GetFlag(FlagIsNewSelection) && GetGridEntryFromRow(selectedRow) == null) { + CommonEditorHide(); + } + + UpdateHelpAttributes(selectedGridEntry, gridEntry); + + // tell the old selection it's not focused any more + if (selectedGridEntry != null) { + selectedGridEntry.Focus = false; + } + + // selection not visible. + if (row < 0 || row >= visibleRows) { + CommonEditorHide(); + selectedRow = row; + selectedGridEntry = gridEntry; + Refresh(); + return; + } + + // leave current selection. + if (gridEntry == null) + return; + + bool newRow = false; + int oldSel = selectedRow; + if (selectedRow != row || !gridEntry.Equals(selectedGridEntry)) { + CommonEditorHide(); + newRow = true; + } + + if (!newRow) + CloseDropDown(); + + Rectangle rect = GetRectangle(row,ROWVALUE); + string s = gridEntry.GetPropertyTextValue(); + + // what components are we using? + bool fBtnDropDown = gridEntry.NeedsDropDownButton | gridEntry.Enumerable; + bool fBtnDialog = gridEntry.NeedsCustomEditorButton; + bool fEdit = gridEntry.IsTextEditable; + bool fPaint = gridEntry.IsCustomPaint; + + rect.X += 1; + rect.Width -= 1; + + // we want to allow builders on read-only properties + if ((fBtnDialog || fBtnDropDown) && !gridEntry.ShouldRenderReadOnly && FocusInside) { + Control btn = fBtnDropDown ? (Control)DropDownButton : (Control)DialogButton; + Size sizeBtn = DpiHelper.EnableDpiChangedHighDpiImprovements? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight): + new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight); + Rectangle rectTarget = new Rectangle(rect.X+rect.Width-sizeBtn.Width, + rect.Y, + sizeBtn.Width,rect.Height); + CommonEditorUse(btn,rectTarget); + sizeBtn = btn.Size; + rect.Width -= (sizeBtn.Width); + btn.Invalidate(); + } + + // if we're painting the value, size the rect between the button and the painted value + if (fPaint) { + rect.X += paintIndent + 1; + rect.Width -= paintIndent + 1; + } + else { + rect.X += EDIT_INDENT + 1; // +1 to compensate for where GDI+ draws it's string relative to the rect. + rect.Width -= EDIT_INDENT + 1; + } + + if ((GetFlag(FlagIsNewSelection) || !Edit.Focused) && (s != null && !s.Equals(Edit.Text))) { + Edit.Text = s; + originalTextValue = s; + Edit.SelectionStart = 0; + Edit.SelectionLength = 0; + } + Edit.AccessibleName = gridEntry.Label; + +#if true // RENDERMODE + switch (inheritRenderMode) { + case RENDERMODE_BOLD: + if (gridEntry.ShouldSerializePropertyValue()) { + Edit.Font = GetBoldFont(); + } + else { + Edit.Font = Font; + } + break; + case RENDERMODE_LEFTDOT: + if (gridEntry.ShouldSerializePropertyValue()) { + rect.X += (LEFTDOT_SIZE * 2); + rect.Width -= (LEFTDOT_SIZE * 2); + } + // nothing + break; + case RENDERMODE_TRIANGLE: + // nothing + break; + } +#endif + + if (GetFlag(FlagIsSplitterMove) || !gridEntry.HasValue || !FocusInside) { + Edit.Visible = false; + } + else { + rect.Offset(1,1); + rect.Height -= 1; + rect.Width -= 1; + CommonEditorUse(Edit,rect); + bool drawReadOnly = gridEntry.ShouldRenderReadOnly; + Edit.ForeColor = drawReadOnly ? this.GrayTextColor : this.ForeColor; + Edit.BackColor = this.BackColor; + Edit.ReadOnly = drawReadOnly || !gridEntry.IsTextEditable; + Edit.UseSystemPasswordChar = gridEntry.ShouldRenderPassword; + } + + GridEntry oldSelectedGridEntry = selectedGridEntry; + selectedRow = row; + selectedGridEntry = gridEntry; + this.ownerGrid.SetStatusBox(gridEntry.PropertyLabel,gridEntry.PropertyDescription); + + // tell the new focused item that it now has focus + if (selectedGridEntry != null) { + selectedGridEntry.Focus = this.FocusInside; + } + + if (!GetFlag(FlagIsNewSelection)) { + FocusInternal(); + } + + // + + InvalidateRow(oldSel); + + InvalidateRow(row); + if (FocusInside) + { + SetFlag(FlagIsNewSelection, false); + } + + try { + if (selectedGridEntry != oldSelectedGridEntry) { + this.ownerGrid.OnSelectedGridItemChanged(oldSelectedGridEntry, selectedGridEntry); + } + } + catch { + } + } + + public virtual void SetConstants() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:SetConstants"); + Size size = this.GetOurSize(); + + visibleRows = (int)Math.Ceiling(((double)size.Height)/(1+RowHeight)); + + size = this.GetOurSize(); + + if (size.Width >= 0) { + labelRatio = Math.Max(Math.Min(labelRatio, 9), 1.1); + labelWidth = ptOurLocation.X + (int) ((double)size.Width / labelRatio); + } + + int oldWidth = labelWidth; + + + bool adjustWidth = SetScrollbarLength(); + GridEntryCollection rgipesAll = GetAllGridEntries(); + if (rgipesAll != null) { + int scroll = GetScrollOffset(); + if ((scroll + visibleRows) >= rgipesAll.Count) { + visibleRows = rgipesAll.Count - scroll; + } + } + + + if (adjustWidth && size.Width >= 0) { + labelRatio = ((double) GetOurSize().Width / (double) (oldWidth - ptOurLocation.X)); + //labelWidth = loc.X + (int) ((double)size.Width / labelRatio); + } + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tsize :" + size.ToString()); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tlocation :" + ptOurLocation.ToString()); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tvisibleRows:" + (visibleRows).ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tlabelWidth :" + (labelWidth).ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tlabelRatio :" + (labelRatio).ToString(CultureInfo.InvariantCulture)); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\trowHeight :" + (RowHeight).ToString(CultureInfo.InvariantCulture)); +#if DEBUG + if (rgipesAll == null) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tIPE Count :(null)"); + } + else { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "\tIPE Count :" + (rgipesAll.Count).ToString(CultureInfo.InvariantCulture)); + } +#endif + } + + private void SetCommitError(short error) { + SetCommitError(error, error == ERROR_THROWN); + } + + private void SetCommitError(short error, bool capture) { + #if DEBUG + if (CompModSwitches.DebugGridView.TraceVerbose) { + string err = "UNKNOWN!"; + switch (error) { + case ERROR_NONE: + err = "ERROR_NONE"; + break; + case ERROR_THROWN: + err = "ERROR_THROWN"; + break; + case ERROR_MSGBOX_UP: + err = "ERROR_MSGBOX_UP"; + break; + } + Debug.WriteLine( "PropertyGridView:SetCommitError(error=" + err + ", capture=" + capture.ToString() + ")"); + } + #endif + errorState = error; + if (error != ERROR_NONE) { + CancelSplitterMove(); + } + + Edit.HookMouseDown = capture; + + } + + internal /*public virtual*/ void SetExpand(GridEntry gridEntry, bool value) { + if (gridEntry != null && gridEntry.Expandable) { + + int row = GetRowFromGridEntry(gridEntry); + int countFromEnd = visibleRows - row; + int curRow = selectedRow; + + // if the currently selected row is below us, we need to commit now + // or the offsets will be wrong + if (selectedRow != -1 && row < selectedRow && Edit.Visible) { + // this will cause the commit + FocusInternal(); + + } + + int offset = GetScrollOffset(); + int items = totalProps; + + gridEntry.InternalExpanded = value; + RecalculateProps(); + GridEntry ipeSelect = selectedGridEntry; + if (!value) { + for (GridEntry ipeCur = ipeSelect; ipeCur != null; ipeCur = ipeCur.ParentGridEntry) { + if (ipeCur.Equals(gridEntry)) { + ipeSelect = gridEntry; + } + } + } + row = GetRowFromGridEntry(gridEntry); + + SetConstants(); + + int newItems = totalProps - items; + + if (value && newItems > 0 && newItems < visibleRows && (row + (newItems)) >= visibleRows && newItems < curRow) { + // scroll to show the newly opened items. + SetScrollOffset((totalProps - items) + offset); + } + + Invalidate(); + + SelectGridEntry(ipeSelect,false); + + int scroll = GetScrollOffset(); + SetScrollOffset(0); + SetConstants(); + SetScrollOffset(scroll); + } + } + + private void SetFlag(short flag, bool value) { + if (value) { + flags = (short)((ushort)flags|(ushort)flag); + } + else { + flags &= (short)~flag; + } + } + + public virtual void SetScrollOffset(int cOffset) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:SetScrollOffset(" + cOffset.ToString(CultureInfo.InvariantCulture) + ")"); + int posNew = Math.Max(0, Math.Min(totalProps - visibleRows + 1, cOffset)); + int posOld = ScrollBar.Value; + if (posNew != posOld && IsScrollValueValid(posNew) && visibleRows > 0) { + ScrollBar.Value = posNew; + Invalidate(); + selectedRow = GetRowFromGridEntry(selectedGridEntry); + } + } + + // C#r + internal virtual bool _Commit() { + return Commit(); + } + + private bool Commit() { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Commit()"); + + if (errorState == ERROR_MSGBOX_UP) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Commit() returning false because an error has been thrown or we are in a property set"); + return false; + } + + if (!this.NeedsCommit) { + SetCommitError(ERROR_NONE); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:Commit() returning true because no change has been made"); + return true; + } + + if (GetInPropertySet()) { + return false; + } + + GridEntry ipeCur = GetGridEntryFromRow(selectedRow); + if (ipeCur == null) { + return true; + } + bool success = false; + try { + success = CommitText(Edit.Text); + } + finally { + + if (!success) { + Edit.FocusInternal(); + SelectEdit(false); + } + else { + SetCommitError(ERROR_NONE); + } + } + return success; + } + + private bool CommitValue(object value) { + GridEntry ipeCur = selectedGridEntry; + + if (selectedGridEntry == null && selectedRow != -1) { + ipeCur = GetGridEntryFromRow(selectedRow); + } + + if (ipeCur == null) { + Debug.Fail("Committing with no selected row!"); + return true; + } + + return CommitValue(ipeCur, value); + } + + internal bool CommitValue(GridEntry ipeCur, object value, bool closeDropDown = true) { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CommitValue(" + (value==null ? "null" :value.ToString()) + ")"); + + int propCount = ipeCur.ChildCount; + bool capture = Edit.HookMouseDown; + object originalValue = null; + + try { + originalValue = ipeCur.PropertyValue; + } + catch { + // if the getter is failing, we still want to let + // the set happen. + } + + try { + try { + SetFlag(FlagInPropertySet, true); + + //if this propentry is enumerable, then once a value is selected from the editor, + //we'll want to close the drop down (like true/false). Otherwise, if we're + //working with Anchor for ex., then we should be able to select different values + //from the editor, without having it close every time. + if (ipeCur != null && + ipeCur.Enumerable && + closeDropDown) { + CloseDropDown(); + } + + try { + Edit.DisableMouseHook = true; + ipeCur.PropertyValue = value; + } + finally { + Edit.DisableMouseHook = false; + Edit.HookMouseDown = capture; + } + } + catch (Exception ex) { + SetCommitError(ERROR_THROWN); + ShowInvalidMessage(ipeCur.PropertyLabel, value, ex); + return false; + } + } + finally { + SetFlag(FlagInPropertySet, false); + } + + SetCommitError(ERROR_NONE); + + string text = ipeCur.GetPropertyTextValue(); + if (!String.Equals(text, Edit.Text)) { + Edit.Text = text; + Edit.SelectionStart = 0; + Edit.SelectionLength = 0; + } + originalTextValue = text; + + // Update our reset command. + // + UpdateResetCommand(ipeCur); + + if (ipeCur.ChildCount != propCount) { + ClearGridEntryEvents(allGridEntries, 0, -1); + allGridEntries = null; + SelectGridEntry(ipeCur, true); + } + + // in case this guy got disposed... + if (ipeCur.Disposed) { + bool editfocused = (edit != null && edit.Focused); + + // reselect the row to find the replacement. + // + SelectGridEntry(ipeCur, true); + ipeCur = selectedGridEntry; + + if (editfocused && edit != null) { + edit.Focus(); + } + } + + this.ownerGrid.OnPropertyValueSet(ipeCur, originalValue); + + return true; + } + + private bool CommitText(string text) { + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:CommitValue(" + (text==null ? "null" :text.ToString()) + ")"); + + object value = null; + + GridEntry ipeCur = selectedGridEntry; + + if (selectedGridEntry == null && selectedRow != -1) { + ipeCur = GetGridEntryFromRow(selectedRow); + } + + if (ipeCur == null) { + Debug.Fail("Committing with no selected row!"); + return true; + } + + try + { + value = ipeCur.ConvertTextToValue(text); + } + catch (Exception ex) + { + SetCommitError(ERROR_THROWN); + ShowInvalidMessage(ipeCur.PropertyLabel, text, ex); + return false; + } + + SetCommitError(ERROR_NONE); + + return CommitValue(value); + } + + internal void ReverseFocus() { + if (selectedGridEntry == null) { + FocusInternal(); + } + else { + SelectGridEntry(selectedGridEntry, true); + + if (DialogButton.Visible) { + DialogButton.FocusInternal(); + } + else if (DropDownButton.Visible) { + DropDownButton.FocusInternal(); + } + else if (Edit.Visible) { + Edit.SelectAll(); + Edit.FocusInternal(); + } + } + } + + private bool SetScrollbarLength() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:SetScrollBarLength"); + bool sbChange = false; + if (totalProps != -1) { + if (totalProps < visibleRows) { + SetScrollOffset(0); + } + else if (GetScrollOffset() > totalProps) { + SetScrollOffset((totalProps+1) - visibleRows); + } + + bool fHidden = !ScrollBar.Visible; + if (visibleRows > 0) { + ScrollBar.LargeChange = visibleRows-1; + } + ScrollBar.Maximum = Math.Max(0,totalProps - 1); + if (fHidden != (totalProps < visibleRows)) { + sbChange = true; + ScrollBar.Visible = fHidden; + Size size = GetOurSize(); + if (labelWidth != -1 && size.Width > 0) { + if (labelWidth > ptOurLocation.X + size.Width) { + labelWidth = ptOurLocation.X + (int) ((double)size.Width / labelRatio); + } + else { + labelRatio = ((double) GetOurSize().Width / (double) (labelWidth - ptOurLocation.X)); + } + } + Invalidate(); + } + } + return sbChange; + } + + /// + /// + /// Shows the given dialog, and returns its dialog result. You should always + /// use this method rather than showing the dialog directly, as this will + /// properly position the dialog and provide it a dialog owner. + /// + public DialogResult /* IWindowsFormsEditorService. */ ShowDialog(Form dialog) { + // try to shift down if sitting right on top of existing owner. + if (dialog.StartPosition == FormStartPosition.CenterScreen) { + Control topControl = this; + if (topControl != null) { + while (topControl.ParentInternal != null) { + topControl = topControl.ParentInternal; + } + if (topControl.Size.Equals(dialog.Size)) { + dialog.StartPosition = FormStartPosition.Manual; + Point location = topControl.Location; + // + location.Offset(25, 25); + dialog.Location = location; + } + } + } + + IntPtr priorFocus = UnsafeNativeMethods.GetFocus(); + + IUIService service = (IUIService)GetService(typeof(IUIService)); + DialogResult result; + if (service != null) { + result = service.ShowDialog(dialog); + } + else { + result = dialog.ShowDialog(this); + } + + if (priorFocus != IntPtr.Zero) { + UnsafeNativeMethods.SetFocus(new HandleRef(null, priorFocus)); + } + + return result; + } + + private void ShowFormatExceptionMessage(string propName, object value, Exception ex) + { + if (value == null) + { + value = "(null)"; + } + + if (propName == null) + { + propName = "(unknown)"; + } + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ShowFormatExceptionMessage(prop=" + propName + ")"); + + // we have to uninstall our hook so the user can push the button! + bool hooked = Edit.HookMouseDown; + Edit.DisableMouseHook = true; + SetCommitError(ERROR_MSGBOX_UP, false); + + // Fix for NdpWhidbey#28082: Before invoking the error dialog, flush all mouse messages in the message queue. + // Otherwise the click that triggered the error will still be in the queue, and will get eaten by the dialog, + // potentially causing an accidental button click. Problem occurs because we trap clicks using a system hook, + // which usually discards the message by returning 1 to GetMessage(). But this won't occur until after the + // error dialog gets closed, which is much too late. + NativeMethods.MSG mouseMsg = new NativeMethods.MSG(); + while (UnsafeNativeMethods.PeekMessage(ref mouseMsg, + NativeMethods.NullHandleRef, + NativeMethods.WM_MOUSEFIRST, + NativeMethods.WM_MOUSELAST, + NativeMethods.PM_REMOVE)) + ; + + // These things are just plain useless. + // + if (ex is System.Reflection.TargetInvocationException) + { + ex = ex.InnerException; + } + + // Try to find an exception message to display + // + string exMessage = ex.Message; + + bool revert = false; + + while (exMessage == null || exMessage.Length == 0) + { + ex = ex.InnerException; + if (ex == null) + { + break; + } + exMessage = ex.Message; + } + + IUIService uiSvc = (IUIService)GetService(typeof(IUIService)); + + ErrorDialog.Message = SR.GetString(SR.PBRSFormatExceptionMessage); + ErrorDialog.Text = SR.GetString(SR.PBRSErrorTitle); + ErrorDialog.Details = exMessage; + + + if (uiSvc != null) + { + revert = (DialogResult.Cancel == uiSvc.ShowDialog(ErrorDialog)); + } + else + { + revert = (DialogResult.Cancel == this.ShowDialog(ErrorDialog)); + } + + Edit.DisableMouseHook = false; + + if (hooked) + { + SelectGridEntry(selectedGridEntry, true); + } + SetCommitError(ERROR_THROWN, hooked); + + if (revert) + { + OnEscape(Edit); + } + } + + internal void ShowInvalidMessage(string propName, object value, Exception ex) { + + if (value == null) { + value = "(null)"; + } + + if (propName == null) { + propName = "(unknown)"; + } + + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:ShowInvalidMessage(prop=" + propName + ")"); + + // we have to uninstall our hook so the user can push the button! + bool hooked = Edit.HookMouseDown; + Edit.DisableMouseHook = true; + SetCommitError(ERROR_MSGBOX_UP, false); + + // Fix for NdpWhidbey#28082: Before invoking the error dialog, flush all mouse messages in the message queue. + // Otherwise the click that triggered the error will still be in the queue, and will get eaten by the dialog, + // potentially causing an accidental button click. Problem occurs because we trap clicks using a system hook, + // which usually discards the message by returning 1 to GetMessage(). But this won't occur until after the + // error dialog gets closed, which is much too late. + NativeMethods.MSG mouseMsg = new NativeMethods.MSG(); + while (UnsafeNativeMethods.PeekMessage(ref mouseMsg, + NativeMethods.NullHandleRef, + NativeMethods.WM_MOUSEFIRST, + NativeMethods.WM_MOUSELAST, + NativeMethods.PM_REMOVE)) + ; + + // These things are just plain useless. + // + if (ex is System.Reflection.TargetInvocationException) { + ex = ex.InnerException; + } + + // Try to find an exception message to display + // + string exMessage = ex.Message; + + bool revert = false; + + while (exMessage == null || exMessage.Length == 0) { + ex = ex.InnerException; + if (ex == null) { + break; + } + exMessage = ex.Message; + } + + IUIService uiSvc = (IUIService)GetService(typeof(IUIService)); + + ErrorDialog.Message = SR.GetString(SR.PBRSErrorInvalidPropertyValue); + ErrorDialog.Text = SR.GetString(SR.PBRSErrorTitle); + ErrorDialog.Details = exMessage; + + + if (uiSvc != null) { + revert = (DialogResult.Cancel == uiSvc.ShowDialog(ErrorDialog)); + } + else { + revert = (DialogResult.Cancel == this.ShowDialog(ErrorDialog)); + } + + Edit.DisableMouseHook = false; + + if (hooked) { + SelectGridEntry(selectedGridEntry, true); + } + SetCommitError(ERROR_THROWN, hooked); + + if (revert) { + OnEscape(Edit); + } + } + + private bool SplitterInside(int x, int y) { + return(Math.Abs(x - InternalLabelWidth) < 4); + } + + private void TabSelection() { + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) + return; + + if (Edit.Visible) { + Edit.FocusInternal(); + SelectEdit(false); + } + else if (dropDownHolder != null && dropDownHolder.Visible) { + dropDownHolder.FocusComponent(); + return; + } + else if (currentEditor != null) { + currentEditor.FocusInternal(); + } + + return; + } + + + internal void RemoveSelectedEntryHelpAttributes() { + UpdateHelpAttributes(selectedGridEntry, null); + } + + private void UpdateHelpAttributes(GridEntry oldEntry, GridEntry newEntry) { + // Update the help context with the current property + // + IHelpService hsvc = GetHelpService(); + if (hsvc == null || oldEntry == newEntry) { + return; + } + + GridEntry temp = oldEntry; + if (oldEntry != null && !oldEntry.Disposed) { + + + while (temp != null) { + hsvc.RemoveContextAttribute("Keyword", temp.HelpKeyword); + temp = temp.ParentGridEntry; + } + } + + if (newEntry != null) { + temp = newEntry; + + UpdateHelpAttributes(hsvc, temp, true); + } + } + + private void UpdateHelpAttributes(IHelpService helpSvc, GridEntry entry, bool addAsF1) { + if (entry == null) { + return; + } + + UpdateHelpAttributes(helpSvc, entry.ParentGridEntry, false); + string helpKeyword = entry.HelpKeyword; + if (helpKeyword != null) { + helpSvc.AddContextAttribute("Keyword", helpKeyword, addAsF1 ? HelpKeywordType.F1Keyword : HelpKeywordType.GeneralKeyword); + } + } + + + private void UpdateUIBasedOnFont(bool layoutRequired) { + if (IsHandleCreated && GetFlag(FlagNeedUpdateUIBasedOnFont)) { + try { + if (listBox != null) { + DropDownListBox.ItemHeight = RowHeight + 2; + } + if (btnDropDown != null) { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + btnDropDown.Size = new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(this.deviceDpi), RowHeight); + } + else { + btnDropDown.Size = new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight); + } + if (btnDialog != null) { + DialogButton.Size = DropDownButton.Size; + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + btnDialog.Image = CreateResizedBitmap("dotdotdot.ico", DOTDOTDOT_ICONWIDTH, DOTDOTDOT_ICONHEIGHT); + } + } + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + btnDropDown.Image = CreateResizedBitmap("Arrow.ico", DOWNARROW_ICONWIDTH, DOWNARROW_ICONHEIGHT); + } + } + + if (layoutRequired) { + LayoutWindow(true); + } + } + finally { + SetFlag(FlagNeedUpdateUIBasedOnFont, false); + } + } + } + + private bool UnfocusSelection() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "PropertyGridView:UnfocusSelection()"); + GridEntry gridEntry = GetGridEntryFromRow(selectedRow); + if (gridEntry == null) + return true; + + bool commit = Commit(); + + if (commit && this.FocusInside) { + FocusInternal(); + } + return commit; + } + + private void UpdateResetCommand(GridEntry gridEntry) { + if (totalProps > 0) { + IMenuCommandService mcs = (IMenuCommandService)GetService(typeof(IMenuCommandService)); + if (mcs != null) { + MenuCommand reset = mcs.FindCommand(PropertyGridCommands.Reset); + if (reset != null) { + reset.Enabled = gridEntry == null ? false : gridEntry.CanResetPropertyValue(); + } + } + } + } + + // a mini version of process dialog key + // for responding to WM_GETDLGCODE + internal bool WantsTab(bool forward) { + if (forward) { + if (this.Focused) { + // we want a tab if the grid has focus and + // we have a button or an Edit + if (DropDownButton.Visible || + DialogButton.Visible || + Edit.Visible) { + return true; + } + } + else if (Edit.Focused && (DropDownButton.Visible || DialogButton.Visible)) { + // if the Edit has focus, and we have a button, we want the tab as well + return true; + } + return ownerGrid.WantsTab(forward); + } + else { + if (Edit.Focused || DropDownButton.Focused || DialogButton.Focused) { + return true; + } + return ownerGrid.WantsTab(forward); + } + } + + private unsafe bool WmNotify(ref Message m) { + if (m.LParam != IntPtr.Zero) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + + if (nmhdr->hwndFrom == ToolTip.Handle) { + switch (nmhdr->code) { + case NativeMethods.TTN_POP: + break; + case NativeMethods.TTN_SHOW: + // we want to move the tooltip over where our text would be + Point mouseLoc = Cursor.Position; + + // convert to window coords + mouseLoc = this.PointToClientInternal(mouseLoc); + + // figure out where we are and apply the offset + mouseLoc = FindPosition(mouseLoc.X, mouseLoc.Y); + + if (mouseLoc == InvalidPosition) { + break; + } + + GridEntry curEntry = GetGridEntryFromRow(mouseLoc.Y); + + if (curEntry == null) { + break; + } + + // get the proper rectangle + Rectangle itemRect = GetRectangle(mouseLoc.Y, mouseLoc.X); + Point tipPt = Point.Empty; + + // and if we need a tooltip, move the tooltip control to that point. + if (mouseLoc.X == ROWLABEL) { + tipPt = curEntry.GetLabelToolTipLocation(mouseLoc.X - itemRect.X, mouseLoc.Y - itemRect.Y); + } + else if (mouseLoc.X == ROWVALUE) { + tipPt = curEntry.ValueToolTipLocation; + } + else { + break; + } + + if (tipPt != InvalidPoint) { + itemRect.Offset(tipPt); + ToolTip.PositionToolTip(this, itemRect); + m.Result = (IntPtr)1; + return true; + } + + break; + } + } + } + return false; + } + + protected override void WndProc(ref Message m) { + switch (m.Msg) { + + case NativeMethods.WM_SYSCOLORCHANGE: + Invalidate(); + break; + + // Microsoft -- if we get focus in the error + // state, make sure we push it back to the + // Edit or bad bad things can happen with + // our state... + // + case NativeMethods.WM_SETFOCUS: + if (!GetInPropertySet() && Edit.Visible && (errorState != ERROR_NONE || !Commit())) { + base.WndProc(ref m); + Edit.FocusInternal(); + return; + } + break; + + case NativeMethods.WM_IME_STARTCOMPOSITION: + Edit.FocusInternal(); + Edit.Clear(); + UnsafeNativeMethods.PostMessage(new HandleRef(Edit, Edit.Handle), NativeMethods.WM_IME_STARTCOMPOSITION, 0, 0); + return; + + case NativeMethods.WM_IME_COMPOSITION: + Edit.FocusInternal(); + UnsafeNativeMethods.PostMessage(new HandleRef(Edit, Edit.Handle), NativeMethods.WM_IME_COMPOSITION, m.WParam, m.LParam); + return; + + case NativeMethods.WM_GETDLGCODE: + + int flags = NativeMethods.DLGC_WANTCHARS | NativeMethods.DLGC_WANTARROWS; + + + if (selectedGridEntry != null) { + if ((ModifierKeys & Keys.Shift) == 0) { + // if we're going backwards, we don't want the tab. + // otherwise, we only want it if we have an edit... + // + if (edit.Visible) { + flags |= NativeMethods.DLGC_WANTTAB; + } + } + } + m.Result = (IntPtr)(flags); + return; + + case NativeMethods.WM_MOUSEMOVE: + + // check if it's the same position, of so eat the message + if (unchecked( (int) (long)m.LParam) == lastMouseMove) { + return; + } + lastMouseMove = unchecked( (int) (long)m.LParam); + break; + + case NativeMethods.WM_NOTIFY: + if (WmNotify(ref m)) + return; + break; + case AutomationMessages.PGM_GETSELECTEDROW: + m.Result = (IntPtr)GetRowFromGridEntry(selectedGridEntry); + return; + case AutomationMessages.PGM_GETVISIBLEROWCOUNT: + m.Result = (IntPtr)Math.Min(visibleRows, totalProps); + return; + } + + base.WndProc(ref m); + } + + /// + /// rescale constants for the DPI change + /// + /// + /// + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + RescaleConstants(); + } + + /// + /// Rescale constants on this object + /// + private void RescaleConstants() { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + ClearCachedFontInfo(); + cachedRowHeight = -1; + paintWidth = LogicalToDeviceUnits(PAINT_WIDTH); + paintIndent = LogicalToDeviceUnits(PAINT_INDENT); + outlineSizeExplorerTreeStyle = LogicalToDeviceUnits(OUTLINE_SIZE_EXPLORER_TREE_STYLE); + outlineSize = LogicalToDeviceUnits(OUTLINE_SIZE); + maxListBoxHeight = LogicalToDeviceUnits(MAX_LISTBOX_HEIGHT); + offset_2Units = LogicalToDeviceUnits(OFFSET_2PIXELS); + if (topLevelGridEntries != null) { + foreach (GridEntry t in topLevelGridEntries) { + ResetOutline(t); + } + } + } + } + + /// + /// private method to recursively reset outlinerect for grid entries ( both visible and invisible) + /// + private void ResetOutline(GridEntry entry) { + entry.OutlineRect = Rectangle.Empty; + if (entry.ChildCount > 0) { + foreach (GridEntry ent in entry.Children) { + ResetOutline(ent); + } + } + return; + } + + private class DropDownHolder : Form, IMouseHookClient { + + private Control currentControl = null; // the control that is hosted in the holder + private PropertyGridView gridView; // the owner gridview + private MouseHook mouseHook; // we use this to hook mouse downs, etc. to know when to close the dropdown. + + private LinkLabel createNewLink = null; + + // all the resizing goo... + // + private bool resizable = true; // true if we're showing the resize widget. + private bool resizing = false; // true if we're in the middle of a resize operation. + private bool resizeUp = false; // true if the dropdown is above the grid row, which means the resize widget is at the top. + private Point dragStart = Point.Empty; // the point at which the drag started to compute the delta + private Rectangle dragBaseRect = Rectangle.Empty; // the original bounds of our control. + private int currentMoveType = MoveTypeNone; // what type of move are we processing? left, bottom, or both? + + private int ResizeBarSize; // the thickness of the resize bar + private int ResizeBorderSize; // the thickness of the 2-way resize area along the outer edge of the resize bar + private int ResizeGripSize; // the size of the 4-way resize grip at outermost corner of the resize bar + private Size MinDropDownSize; // the minimum size for the control. + private Bitmap sizeGripGlyph; // our cached size grip glyph. Control paint only does right bottom glyphs, so we cache a mirrored one. See GetSizeGripGlyph + + private const int DropDownHolderBorder = 1; + private const int MoveTypeNone = 0x0; + private const int MoveTypeBottom = 0x1; + private const int MoveTypeLeft = 0x2; + private const int MoveTypeTop = 0x4; + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // DropDownHolder's caption is not visible. + // So we don't have to localize its text. + ] + internal DropDownHolder(PropertyGridView psheet) + : base() { + + MinDropDownSize = new Size(SystemInformation.VerticalScrollBarWidth * 4, SystemInformation.HorizontalScrollBarHeight * 4); + ResizeGripSize = SystemInformation.HorizontalScrollBarHeight; + ResizeBarSize = ResizeGripSize + 1; + ResizeBorderSize = ResizeBarSize / 2; + + this.ShowInTaskbar = false; + this.ControlBox = false; + this.MinimizeBox = false; + this.MaximizeBox = false; + this.Text = ""; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.AutoScaleMode = AutoScaleMode.None; // children may scale, but we won't interfere. + mouseHook = new MouseHook(this, this, psheet); + Visible = false; + gridView = psheet; + this.BackColor = gridView.BackColor; + } + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_TOOLWINDOW; + cp.Style |= NativeMethods.WS_POPUP | NativeMethods.WS_BORDER; + if (OSFeature.IsPresent(SystemParameter.DropShadow)) { + cp.ClassStyle |= NativeMethods.CS_DROPSHADOW; + } + if (gridView != null) { + cp.Parent = gridView.ParentInternal.Handle; + } + return cp; + } + } + + private LinkLabel CreateNewLink { + get { + if (this.createNewLink == null) { + this.createNewLink = new LinkLabel(); + this.createNewLink.LinkClicked += new LinkLabelLinkClickedEventHandler(OnNewLinkClicked); + } + return createNewLink; + } + } + + + public virtual bool HookMouseDown{ + get{ + return mouseHook.HookMouseDown; + } + set{ + mouseHook.HookMouseDown = value; + } + } + + /// + /// This gets set to true if there isn't enough space below the currently selected + /// row for the drop down, so it appears above the row. In this case, we make the resize + /// grip appear at the top left. + /// + public bool ResizeUp { + + set { + if (resizeUp != value) { + + // clear the glyph so we regenerate it. + // + sizeGripGlyph = null; + resizeUp = value; + + if (resizable) { + this.DockPadding.Bottom = 0; + this.DockPadding.Top = 0; + if (value) { + this.DockPadding.Top = ResizeBarSize; + } + else { + this.DockPadding.Bottom = ResizeBarSize; + } + } + } + } + } + + + protected override void DestroyHandle() { + mouseHook.HookMouseDown = false; + base.DestroyHandle(); + } + + protected override void Dispose(bool disposing) { + + if (disposing && createNewLink != null) { + createNewLink.Dispose(); + createNewLink = null; + } + base.Dispose(disposing); + } + + public void DoModalLoop() { + + // Push a modal loop. This seems expensive, but I think it is a + // better user model than returning from DropDownControl immediately. + // + while (this.Visible) { + Application.DoEventsModal(); + UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, NativeMethods.QS_ALLINPUT, NativeMethods.MWMO_INPUTAVAILABLE); + } + } + + public virtual Control Component { + get { + return currentControl; + } + } + + /// + /// Get an InstanceCreationEditor for this entry. First, we look on the property type, and if we + /// don't find that we'll go up to the editor type itself. That way people can associate the InstanceCreationEditor with + /// the type of DropDown UIType Editor. + /// + /// + private InstanceCreationEditor GetInstanceCreationEditor(PropertyDescriptorGridEntry entry) { + + if (entry == null) { + return null; + } + + InstanceCreationEditor editor = null; + + // check the property type itself. this is the default path. + // + PropertyDescriptor pd = entry.PropertyDescriptor; + if (pd != null) { + editor = pd.GetEditor(typeof(InstanceCreationEditor)) as InstanceCreationEditor; + } + + // now check if there is a dropdown UI type editor. If so, use that. + // + if (editor == null) { + UITypeEditor ute = entry.UITypeEditor; + if (ute != null && ute.GetEditStyle() == UITypeEditorEditStyle.DropDown) { + editor = (InstanceCreationEditor)TypeDescriptor.GetEditor(ute, typeof(InstanceCreationEditor)); + } + } + return editor; + } + + /// + /// Get a glyph for sizing the lower left hand grip. The code in ControlPaint only does lower-right glyphs + /// so we do some GDI+ magic to take that glyph and mirror it. That way we can still share the code (in case it changes for theming, etc), + /// not have any special cases, and possibly solve world hunger. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private Bitmap GetSizeGripGlyph(Graphics g) { + if (this.sizeGripGlyph != null) { + return sizeGripGlyph; + } + + // create our drawing surface based on the current graphics context. + // + sizeGripGlyph = new Bitmap(ResizeGripSize, ResizeGripSize, g); + + using (Graphics glyphGraphics = Graphics.FromImage(sizeGripGlyph)) + { + // mirror the image around the x-axis to get a gripper handle that works + // for the lower left. + System.Drawing.Drawing2D.Matrix m = new System.Drawing.Drawing2D.Matrix(); + + // basically, mirroring is just scaling by -1 on the X-axis. So any point that's like (10, 10) goes to (-10, 10). + // that mirrors it, but also moves everything to the negative axis, so we just bump the whole thing over by it's width. + // + // the +1 is because things at (0,0) stay at (0,0) since [0 * -1 = 0] so we want to get them over to the other side too. + // + // resizeUp causes the image to also be mirrored vertically so the grip can be used as a top-left grip instead of bottom-left. + // + m.Translate(ResizeGripSize + 1, (resizeUp ? ResizeGripSize + 1: 0)); + m.Scale(-1, (resizeUp ? -1 : 1)); + glyphGraphics.Transform = m; + ControlPaint.DrawSizeGrip(glyphGraphics, BackColor, 0, 0, ResizeGripSize, ResizeGripSize); + glyphGraphics.ResetTransform(); + + } + sizeGripGlyph.MakeTransparent(BackColor); + return sizeGripGlyph; + } + + public virtual bool GetUsed() { + return(currentControl != null); + } + + public virtual void FocusComponent() { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:FocusComponent()"); + if (currentControl != null && Visible) { + currentControl.FocusInternal(); + } + } + + /// + /// General purpose method, based on Control.Contains()... + /// + /// Determines whether a given window (specified using native window handle) + /// is a descendant of this control. This catches both contained descendants + /// and 'owned' windows such as modal dialogs. Using window handles rather + /// than Control objects allows it to catch un-managed windows as well. + /// + private bool OwnsWindow(IntPtr hWnd) { + while (hWnd != IntPtr.Zero) { + hWnd = UnsafeNativeMethods.GetWindowLong(new HandleRef(null, hWnd), NativeMethods.GWL_HWNDPARENT); + if (hWnd == IntPtr.Zero) { + return false; + } + if (hWnd == this.Handle) { + return true; + } + } + return false; + } + + public bool OnClickHooked() { + gridView.CloseDropDownInternal(false); + return false; + } + + private void OnCurrentControlResize(object o, EventArgs e) { + if (currentControl != null && !resizing) { + int oldWidth = this.Width; + Size newSize = new Size(2 * DropDownHolderBorder + currentControl.Width, 2 * DropDownHolderBorder + currentControl.Height); + if (resizable) { + newSize.Height += ResizeBarSize; + } + try { + resizing = true; + this.SuspendLayout(); + this.Size = newSize; + } + finally { + resizing = false; + this.ResumeLayout(false); + } + this.Left -= (this.Width - oldWidth); + } + } + + + protected override void OnLayout(LayoutEventArgs levent) { + try { + resizing = true; + base.OnLayout(levent); + } + finally { + resizing = false; + } + + } + + private void OnNewLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { + InstanceCreationEditor ice = e.Link.LinkData as InstanceCreationEditor; + + Debug.Assert(ice != null, "How do we have a link without the InstanceCreationEditor?"); + if (ice != null) { + Type createType = gridView.SelectedGridEntry.PropertyType; + if (createType != null) { + + gridView.CloseDropDown(); + + object newValue = ice.CreateInstance(gridView.SelectedGridEntry, createType); + + if (newValue != null) { + + // make sure we got what we asked for. + // + if (!createType.IsInstanceOfType(newValue)) { + throw new InvalidCastException(SR.GetString(SR.PropertyGridViewEditorCreatedInvalidObject, createType)); + } + + gridView.CommitValue(newValue); + } + } + } + } + + /// + /// Just figure out what kind of sizing we would do at a given drag location. + /// + private int MoveTypeFromPoint(int x, int y) + { + Rectangle bGripRect = new Rectangle(0, Height - ResizeGripSize, ResizeGripSize, ResizeGripSize); + Rectangle tGripRect = new Rectangle(0, 0, ResizeGripSize, ResizeGripSize); + + if (!resizeUp && bGripRect.Contains(x, y)) + { + return MoveTypeLeft | MoveTypeBottom; + } + else if (resizeUp && tGripRect.Contains(x, y)) { + return MoveTypeLeft | MoveTypeTop; + } + else if (!resizeUp && Math.Abs(Height - y) < ResizeBorderSize) + { + return MoveTypeBottom; + } + else if (resizeUp && Math.Abs(y) < ResizeBorderSize) + { + return MoveTypeTop; + } + return MoveTypeNone; + } + + /// + /// Decide if we're going to be sizing at the given point, and if so, Capture and safe our current state. + /// + protected override void OnMouseDown(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) { + this.currentMoveType = MoveTypeFromPoint(e.X, e.Y); + if (this.currentMoveType != MoveTypeNone) + { + dragStart = PointToScreen(new Point(e.X, e.Y)); + dragBaseRect = Bounds; + Capture = true; + } + else { + gridView.CloseDropDown(); + } + } + base.OnMouseDown (e); + } + + /// + /// Either set the cursor or do a move, depending on what our currentMoveType is/ + /// + protected override void OnMouseMove(MouseEventArgs e) + { + // not moving so just set the cursor. + // + if (this.currentMoveType == MoveTypeNone) + { + int cursorMoveType = MoveTypeFromPoint(e.X, e.Y); + switch (cursorMoveType) { + case (MoveTypeLeft | MoveTypeBottom): + Cursor = Cursors.SizeNESW; + break; + case MoveTypeBottom: + case MoveTypeTop: + Cursor = Cursors.SizeNS; + break; + case MoveTypeTop | MoveTypeLeft: + Cursor = Cursors.SizeNWSE; + break; + default: + Cursor = null; + break; + } + } + else + { + Point dragPoint = PointToScreen(new Point(e.X, e.Y)); + Rectangle newBounds = Bounds; + + // we're in a move operation, so do the resize. + // + if ((currentMoveType & MoveTypeBottom) == MoveTypeBottom) + { + newBounds.Height = Math.Max(MinDropDownSize.Height, dragBaseRect.Height + (dragPoint.Y - dragStart.Y)); + } + + // for left and top moves, we actually have to resize and move the form simultaneously. + // do to that, we compute the xdelta, and apply that to the base rectangle if it's not going to + // make the form smaller than the minimum. + // + if ((currentMoveType & MoveTypeTop) == MoveTypeTop) + { + int delta = dragPoint.Y - dragStart.Y; + + if ((dragBaseRect.Height - delta) > MinDropDownSize.Height) + { + newBounds.Y = dragBaseRect.Top + delta; + newBounds.Height = dragBaseRect.Height - delta; + } + } + + if ((currentMoveType & MoveTypeLeft) == MoveTypeLeft) + { + int delta = dragPoint.X - dragStart.X; + + if ((dragBaseRect.Width - delta) > MinDropDownSize.Width) + { + newBounds.X = dragBaseRect.Left + delta; + newBounds.Width = dragBaseRect.Width - delta; + } + } + + if (newBounds != Bounds) { + try { + resizing = true; + this.Bounds = newBounds; + } + finally { + resizing = false; + } + } + + // Redraw! + // + Invalidate(); + } + base.OnMouseMove (e); + } + + protected override void OnMouseLeave(EventArgs e) + { + // just clear the cursor back to the default. + // + Cursor = null; + base.OnMouseLeave (e); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp (e); + + if (e.Button == MouseButtons.Left) { + + // reset the world. + // + this.currentMoveType = MoveTypeNone; + this.dragStart = Point.Empty; + this.dragBaseRect = Rectangle.Empty; + this.Capture = false; + } + } + + /// + /// Just paint and draw our glyph. + /// + protected override void OnPaint(PaintEventArgs pe) + { + base.OnPaint(pe); + if (resizable) { + // Draw the grip + Rectangle lRect = new Rectangle(0, resizeUp ? 0 : Height - ResizeGripSize, ResizeGripSize, ResizeGripSize); + pe.Graphics.DrawImage(GetSizeGripGlyph(pe.Graphics), lRect); + + // Draw the divider + int y = resizeUp ? (ResizeBarSize - 1) : (Height - ResizeBarSize); + Pen pen = new Pen(SystemColors.ControlDark, 1); + pen.DashStyle = DashStyle.Solid; + pe.Graphics.DrawLine(pen, 0, y, Width, y); + pen.Dispose(); + } + } + + protected override bool ProcessDialogKey(Keys keyData) { + if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == 0) { + switch (keyData & Keys.KeyCode) { + case Keys.Escape: + gridView.OnEscape(this); + return true; + case Keys.F4: + gridView.F4Selection(true); + return true; + case Keys.Return: + // make sure the return gets forwarded to the control that + // is being displayed + if (gridView.UnfocusSelection() && gridView.SelectedGridEntry != null) { + gridView.SelectedGridEntry.OnValueReturnKey(); + } + return true; + } + } + + return base.ProcessDialogKey(keyData); + } + + public void SetComponent(Control ctl, bool resizable) { + + this.resizable = resizable; + this.Font = gridView.Font; + + // check to see if we're going to be adding an InstanceCreationEditor + // + InstanceCreationEditor editor = (ctl == null ? null : GetInstanceCreationEditor(gridView.SelectedGridEntry as PropertyDescriptorGridEntry)); + + // clear any existing control we have + // + if (currentControl != null) { + currentControl.Resize -= new EventHandler(this.OnCurrentControlResize); + Controls.Remove(currentControl); + currentControl = null; + } + + // remove the InstanceCreationEditor link + // + if (createNewLink != null && createNewLink.Parent == this) { + this.Controls.Remove(createNewLink); + } + + // now set up the new control, top to bottom + // + if (ctl != null) { + + currentControl = ctl; + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:SetComponent(" + (ctl.GetType().Name) + ")"); + + this.DockPadding.All = 0; + + // first handle the control. If it's a listbox, make sure it's got some height + // to it. + // + if (currentControl is GridViewListBox) { + ListBox lb = (ListBox)currentControl; + + if (lb.Items.Count == 0) { + lb.Height = Math.Max(lb.Height, lb.ItemHeight); + } + } + + // Parent the control now. That way it can inherit our + // font and scale itself if it wants to. + try { + SuspendLayout(); + Controls.Add(ctl); + + Size sz = new Size(2 * DropDownHolderBorder + ctl.Width, 2 * DropDownHolderBorder + ctl.Height); + + // now check for an editor, and show the link if there is one. + // + if (editor != null) { + + // set up the link. + // + CreateNewLink.Text = editor.Text; + CreateNewLink.Links.Clear(); + CreateNewLink.Links.Add(0, editor.Text.Length, editor); + + // size it as close to the size of the text as possible. + // + int linkHeight = CreateNewLink.Height; + using (Graphics g = gridView.CreateGraphics()) { + SizeF sizef = PropertyGrid.MeasureTextHelper.MeasureText(this.gridView.ownerGrid, g, editor.Text, gridView.GetBaseFont()); + linkHeight = (int)sizef.Height; + } + + CreateNewLink.Height = linkHeight + DropDownHolderBorder; + + // add the total height plus some border + sz.Height += (linkHeight + (DropDownHolderBorder * 2)); + } + + // finally, if we're resizable, add the space for the widget. + // + if (resizable) { + sz.Height += ResizeBarSize; + + // we use dockpadding to save space to draw the widget. + // + if (resizeUp) { + this.DockPadding.Top = ResizeBarSize; + } + else { + this.DockPadding.Bottom = ResizeBarSize; + } + } + + // set the size stuff. + // + Size = sz; + + // DockStyle property on child control is forcing to resize to Parent control size that is not yet scaled. + // Parent control's Preffered size, by this time, is calculated based on child controls scaled dimensions. + // Also, making sure we are not shrinking if we are already there. + if(DpiHelper.EnableDpiChangedHighDpiImprovements) { + ctl.Visible = true; + if (this.Size.Height < this.PreferredSize.Height) { + this.Size = new Size(this.Size.Width, this.PreferredSize.Height); + } + ctl.Dock = DockStyle.Fill; + } + else { + ctl.Dock = DockStyle.Fill; + ctl.Visible = true; + } + + if (editor != null) { + CreateNewLink.Dock = DockStyle.Bottom; + Controls.Add(CreateNewLink); + } + } + finally { + ResumeLayout(true); + } + + // hook the resize event. + // + currentControl.Resize += new EventHandler(this.OnCurrentControlResize); + } + Enabled = currentControl != null; + } + + protected override void WndProc(ref Message m) { + + if (m.Msg == NativeMethods.WM_ACTIVATE) { + SetState(STATE_MODAL, true); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:WM_ACTIVATE()"); + IntPtr activatedWindow = (IntPtr)m.LParam; + if (Visible && NativeMethods.Util.LOWORD(m.WParam) == NativeMethods.WA_INACTIVE && !this.OwnsWindow(activatedWindow)) { + gridView.CloseDropDownInternal(false); + return; + } + + // prevent the IMsoComponentManager active code from getting fired. + //Active = ((int)m.WParam & 0x0000FFFF) != NativeMethods.WA_INACTIVE; + //return; + } + else if (m.Msg == NativeMethods.WM_CLOSE) { + // don't let an ALT-F4 get you down + // + if (Visible) { + gridView.CloseDropDown(); + } + return; + } + else if (m.Msg == NativeMethods.WM_DPICHANGED) { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + // Dropdownholder in PropertyGridView is already scaled based on parent font and other properties that were already set for new DPI + // This case is to avoid rescaling(double scaling) of this form due to Font changes. + + var oldDpi = this.deviceDpi; + this.deviceDpi = + (int) UnsafeNativeMethods.GetDpiForWindow(new HandleRef(this, HandleInternal)); + if (oldDpi != deviceDpi) { + RescaleConstantsForDpi(oldDpi, this.deviceDpi); + this.PerformLayout(); + } + + m.Result = IntPtr.Zero; + return; + } + } + + base.WndProc(ref m); + } + + protected override void RescaleConstantsForDpi(int oldDpi, int newDpi) { + base.RescaleConstantsForDpi(oldDpi, newDpi); + if (!DpiHelper.EnableDpiChangedHighDpiImprovements) { + return; + } + + var scrollbarHeight = SystemInformation.GetHorizontalScrollBarHeightForDpi(newDpi); + MinDropDownSize = new Size(SystemInformation.GetVerticalScrollBarWidthForDpi(newDpi) * 4, scrollbarHeight * 4); + ResizeGripSize = scrollbarHeight; + + ResizeBarSize = ResizeGripSize + 1; + ResizeBorderSize = ResizeBarSize / 2; + + var factor = (double)newDpi / oldDpi; + this.Height = (int)Math.Round(factor * this.Height); + } + + } + + private class GridViewListBox : ListBox { + + internal bool fInSetSelectedIndex = false; + private PropertyGridView _owningPropertyGridView; + + public GridViewListBox(PropertyGridView gridView) { + base.IntegralHeight = false; + _owningPropertyGridView = gridView; + base.BackColor = gridView.BackColor; + } + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + cp.Style &= ~NativeMethods.WS_BORDER; + cp.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE; + return cp; + } + } + + /// + /// Gets the owning PropertyGridView. + /// + internal PropertyGridView OwningPropertyGridView { + get { + return _owningPropertyGridView; + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + /// + /// Constructs the new instance of the accessibility object for this control. + /// + /// The accessibility object instance. + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new GridViewListBoxAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + public virtual bool InSetSelectedIndex() { + return fInSetSelectedIndex; + } + + protected override void OnSelectedIndexChanged(EventArgs e) { + fInSetSelectedIndex = true; + base.OnSelectedIndexChanged(e); + fInSetSelectedIndex = false; + + var gridViewListBoxAccessibleObject = AccessibilityObject as GridViewListBoxAccessibleObject; + if (gridViewListBoxAccessibleObject != null) { + gridViewListBoxAccessibleObject.SetListBoxItemFocus(); + } + } + } + + [ComVisible(true)] + private class GridViewListBoxItemAccessibleObject : AccessibleObject { + + private GridViewListBox _owningGridViewListBox; + private object _owningItem; + + public GridViewListBoxItemAccessibleObject(GridViewListBox owningGridViewListBox, object owningItem) { + _owningGridViewListBox = owningGridViewListBox; + _owningItem = owningItem; + + UseStdAccessibleObjects(_owningGridViewListBox.Handle); + } + + /// + /// Gets the DropDown button bounds. + /// + public override Rectangle Bounds { + get { + int left; + int top; + int width; + int height; + var systemIAccessible = GetSystemIAccessibleInternal(); + systemIAccessible.accLocation(out left, out top, out width, out height, GetChildId()); + return new Rectangle(left, top, width, height); + } + } + + /// + /// Gets the DropDown button default action. + /// + public override string DefaultAction { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accDefaultAction[GetChildId()]; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return _owningGridViewListBox.AccessibilityObject; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + int currentIndex = GetCurrentIndex(); + var gridViewListBoxAccessibleObject = _owningGridViewListBox.AccessibilityObject as GridViewListBoxAccessibleObject; + if (gridViewListBoxAccessibleObject != null) { + int itemsCount = gridViewListBoxAccessibleObject.GetChildFragmentCount(); + int nextItemIndex = currentIndex + 1; + if (itemsCount > nextItemIndex) { + return gridViewListBoxAccessibleObject.GetChildFragment(nextItemIndex); + } + } + break; + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + currentIndex = GetCurrentIndex(); + gridViewListBoxAccessibleObject = _owningGridViewListBox.AccessibilityObject as GridViewListBoxAccessibleObject; + if (gridViewListBoxAccessibleObject != null) { + var itemsCount = gridViewListBoxAccessibleObject.GetChildFragmentCount(); + int previousItemIndex = currentIndex - 1; + if (previousItemIndex >= 0) { + return gridViewListBoxAccessibleObject.GetChildFragment(previousItemIndex); + } + } + + break; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningGridViewListBox.AccessibilityObject; + } + } + + private int GetCurrentIndex() { + return _owningGridViewListBox.Items.IndexOf(_owningItem); + } + + internal override int GetChildId() { + return GetCurrentIndex() + 1; // Index is zero-based, Child ID is 1-based. + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_RuntimeIdPropertyId: + return RuntimeId; + case NativeMethods.UIA_BoundingRectanglePropertyId: + return BoundingRectangle; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ListItemControlTypeId; + case NativeMethods.UIA_NamePropertyId: + return Name; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return _owningGridViewListBox.Focused; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable; + case NativeMethods.UIA_IsEnabledPropertyId: + return _owningGridViewListBox.Enabled; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + default: + return base.GetPropertyValue(propertyID); + } + } + + /// + /// Gets the help text. + /// + public override string Help { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.accHelp[GetChildId()]; + } + } + + /// + /// Gets the keyboard shortcut. + /// + public override string KeyboardShortcut { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return systemIAccessible.get_accKeyboardShortcut(GetChildId()); + } + } + + /// + /// Indicates whether specified pattern is supported. + /// + /// The pattern ID. + /// True if specified + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId || + patternId == NativeMethods.UIA_InvokePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + /// + /// Gets or sets the accessible name. + /// + public override string Name { + get { + if (_owningGridViewListBox != null) { + return _owningItem.ToString(); + } + + return base.Name; + } + + set { + base.Name = value; + } + } + + /// + /// Gets the accessible role. + /// + public override AccessibleRole Role { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleRole)systemIAccessible.get_accRole(GetChildId()); + } + } + + /// + /// Gets the runtime ID. + /// + internal override int[] RuntimeId { + get { + var runtimeId = new int[3]; + runtimeId[0] = RuntimeIDFirstItem; + runtimeId[1] = (int)(long)_owningGridViewListBox.Handle; + runtimeId[2] = _owningItem.GetHashCode(); + + return runtimeId; + } + } + + /// + /// Gets the accessible state. + /// + public override AccessibleStates State { + get { + var systemIAccessible = GetSystemIAccessibleInternal(); + return (AccessibleStates)systemIAccessible.get_accState(GetChildId()); + } + } + + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + } + + private class GridViewListBoxItemAccessibleObjectCollection : Hashtable { + + private GridViewListBox _owningGridViewListBox; + + public GridViewListBoxItemAccessibleObjectCollection(GridViewListBox owningGridViewListBox) { + _owningGridViewListBox = owningGridViewListBox; + } + + public override object this[object key] { + get { + if (!ContainsKey(key)) { + var itemAccessibleObject = new GridViewListBoxItemAccessibleObject(_owningGridViewListBox, key); + base[key] = itemAccessibleObject; + } + + return base[key]; + } + + set { + base[key] = value; + } + } + } + + /// + /// Represents the PropertyGridView ListBox accessibility object. + /// + [ComVisible(true)] + private class GridViewListBoxAccessibleObject : ControlAccessibleObject { + + private GridViewListBox _owningGridViewListBox; + private PropertyGridView _owningPropertyGridView; + private GridViewListBoxItemAccessibleObjectCollection _itemAccessibleObjects; + + /// + /// Constructs the new instance of GridViewListBoxAccessibleObject. + /// + /// The owning GridViewListBox. + public GridViewListBoxAccessibleObject(GridViewListBox owningGridViewListBox) : base(owningGridViewListBox) { + _owningGridViewListBox = owningGridViewListBox; + _owningPropertyGridView = owningGridViewListBox.OwningPropertyGridView; + _itemAccessibleObjects = new GridViewListBoxItemAccessibleObjectCollection(owningGridViewListBox); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent) { + return _owningPropertyGridView.SelectedGridEntry.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild) { + return GetChildFragment(0); + } + else if (direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + var childFragmentCount = GetChildFragmentCount(); + if (childFragmentCount > 0) { + return GetChildFragment(childFragmentCount - 1); + } + } + else if (direction == UnsafeNativeMethods.NavigateDirection.NextSibling) { + return _owningPropertyGridView.Edit.AccessibilityObject; + } + + return base.FragmentNavigate(direction); + } + + /// + /// Return the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return _owningPropertyGridView.AccessibilityObject; + } + } + + public AccessibleObject GetChildFragment(int index) { + if (index < 0 || index >= _owningGridViewListBox.Items.Count) { + return null; + } + + var item = _owningGridViewListBox.Items[index]; + return _itemAccessibleObjects[item] as AccessibleObject; + } + + + public int GetChildFragmentCount() { + return _owningGridViewListBox.Items.Count; + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ListControlTypeId; + } else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + + return base.GetPropertyValue(propertyID); + } + + internal override void SetFocus() { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + + base.SetFocus(); + } + + internal void SetListBoxItemFocus() { + var selectedItem = _owningGridViewListBox.SelectedItem; + var itemAccessibleObject = _itemAccessibleObjects[selectedItem] as AccessibleObject; + if (itemAccessibleObject != null) { + itemAccessibleObject.SetFocus(); + } + } + } + + private class GridViewEdit : TextBox , IMouseHookClient { + + internal bool fInSetText = false; + internal bool filter = false; + internal PropertyGridView psheet = null; + private bool dontFocusMe = false; + private int lastMove; + + private MouseHook mouseHook; + + // We do this becuase the Focus call above doesn't always stick, so + // we make the Edit think that it doesn't have focus. this prevents + // ActiveControl code on the containercontrol from moving focus elsewhere + // when the dropdown closes. + public bool DontFocus { + set { + dontFocusMe = value; + } + } + + public virtual bool Filter { + get { return filter;} + + set { + this.filter = value; + } + } + + /// + /// Indicates whether or not the control supports UIA Providers via + /// IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces + /// + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + public override bool Focused { + get { + if (dontFocusMe) { + return false; + } + return base.Focused; + } + } + + + public override string Text { + get { + return base.Text; + } + set { + fInSetText = true; + base.Text = value; + fInSetText = false; + } + } + + public bool DisableMouseHook { + + set { + mouseHook.DisableMouseHook = value; + } + } + + + public virtual bool HookMouseDown{ + get{ + return mouseHook.HookMouseDown; + } + set{ + mouseHook.HookMouseDown = value; + if (value) { + this.FocusInternal(); + } + } + } + + + public GridViewEdit(PropertyGridView psheet) { + this.psheet = psheet; + mouseHook = new MouseHook(this, this, psheet); + } + + /// + /// Creates a new AccessibleObject for this GridViewEdit instance. + /// The AccessibleObject instance returned by this method overrides several UIA properties. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.7.2 or opt in into this feature using a compatibility switch. + /// + /// + /// AccessibleObject for this GridViewEdit instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level2) { + return new GridViewEditAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + protected override void DestroyHandle() { + mouseHook.HookMouseDown = false; + base.DestroyHandle(); + } + + protected override void Dispose(bool disposing) { + if (disposing) { + mouseHook.Dispose(); + } + base.Dispose(disposing); + } + + public void FilterKeyPress(char keyChar) { + + if (IsInputChar(keyChar)) { + this.FocusInternal(); + this.SelectAll(); + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_CHAR, (IntPtr)keyChar, IntPtr.Zero); + } + } + + + + /// + /// + /// Overridden to handle TAB key. + /// + protected override bool IsInputKey(Keys keyData) { + switch (keyData & Keys.KeyCode) { + case Keys.Escape: + case Keys.Tab: + case Keys.F4: + case Keys.F1: + case Keys.Return: + return false; + } + if (psheet.NeedsCommit) { + return false; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// Overridden to handle TAB key. + /// + protected override bool IsInputChar(char keyChar) { + switch ((Keys)(int)keyChar) { + case Keys.Tab: + case Keys.Return: + return false; + } + return base.IsInputChar(keyChar); + } + + protected override void OnKeyDown(KeyEventArgs ke) { + + // this is because on a dialog we may + // not get a chance to pre-process + // + if (ProcessDialogKey(ke.KeyData)) { + ke.Handled = true; + return; + } + + base.OnKeyDown(ke); + } + + protected override void OnKeyPress(KeyPressEventArgs ke) { + if (!IsInputChar(ke.KeyChar)) { + ke.Handled = true; + return; + } + base.OnKeyPress(ke); + } + + public bool OnClickHooked() { + // can we commit this value? + // eat the value if we failed to commit. + return !psheet._Commit(); + } + + protected override void OnMouseEnter(EventArgs e) { + base.OnMouseEnter(e); + + if (!this.Focused) { + Graphics g = CreateGraphics(); + if (psheet.SelectedGridEntry != null && + ClientRectangle.Width <= psheet.SelectedGridEntry.GetValueTextWidth(this.Text, g, this.Font)) { + psheet.ToolTip.ToolTip = this.PasswordProtect ? "" : this.Text; + } + g.Dispose(); + } + + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + + // make sure we allow the Edit to handle ctrl-z + switch (keyData & Keys.KeyCode) { + case Keys.Z: + case Keys.C: + case Keys.X: + case Keys.V: + if( + ((keyData & Keys.Control) != 0) && + ((keyData & Keys.Shift) == 0) && + ((keyData & Keys.Alt) == 0)) { + return false; + } + break; + + case Keys.A: + if( + ((keyData & Keys.Control) != 0) && + ((keyData & Keys.Shift) == 0) && + ((keyData & Keys.Alt) == 0)) { + SelectAll(); + return true; + } + + break; + + case Keys.Insert: + if (((keyData & Keys.Alt) == 0)) { + if (((keyData & Keys.Control) != 0) ^ ((keyData & Keys.Shift) == 0)) { + return false; + } + } + break; + + case Keys.Delete: + if( + ((keyData & Keys.Control) == 0) && + ((keyData & Keys.Shift) != 0) && + ((keyData & Keys.Alt) == 0)) { + return false; + } + else if( + ((keyData & Keys.Control) == 0) && + ((keyData & Keys.Shift) == 0) && + ((keyData & Keys.Alt) == 0) + ) + { + // if this is just the delete key and we're on a non-text editable property that is resettable, + // reset it now. + // + if (psheet.SelectedGridEntry != null && !psheet.SelectedGridEntry.Enumerable && !psheet.SelectedGridEntry.IsTextEditable && psheet.SelectedGridEntry.CanResetPropertyValue()) { + object oldValue = psheet.SelectedGridEntry.PropertyValue; + psheet.SelectedGridEntry.ResetPropertyValue(); + psheet.UnfocusSelection(); + psheet.ownerGrid.OnPropertyValueSet(psheet.SelectedGridEntry, oldValue); + } + } + break; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + + /// + /// + /// Overrides Control.ProcessDialogKey to handle the Escape and Return + /// keys. + /// + /// + protected override bool ProcessDialogKey(Keys keyData) { + + // We don't do anything with modified keys here. + // + if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == 0) { + switch (keyData & Keys.KeyCode) { + case Keys.Return: + bool fwdReturn = !psheet.NeedsCommit; + if (psheet.UnfocusSelection() && fwdReturn) { + psheet.SelectedGridEntry.OnValueReturnKey(); + } + return true; + case Keys.Escape: + psheet.OnEscape(this); + return true; + case Keys.F4: + psheet.F4Selection(true); + return true; + } + } + + // for the tab key, we want to commit before we allow it to be processed. + if ((keyData & Keys.KeyCode) == Keys.Tab && ((keyData & (Keys.Control | Keys.Alt)) == 0)) { + return !psheet._Commit(); + } + + return base.ProcessDialogKey(keyData); + } + + protected override void SetVisibleCore(bool value) { + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:Visible(" + (value.ToString()) + ")"); + // make sure we dont' have the mouse captured if + // we're going invisible + if (value == false && this.HookMouseDown) { + mouseHook.HookMouseDown = false; + } + base.SetVisibleCore(value); + } + + + // a mini version of process dialog key + // for responding to WM_GETDLGCODE + internal bool WantsTab(bool forward) { + return psheet.WantsTab(forward); + } + + private unsafe bool WmNotify(ref Message m) { + + if (m.LParam != IntPtr.Zero) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + + if (nmhdr->hwndFrom == psheet.ToolTip.Handle) { + switch (nmhdr->code) { + case NativeMethods.TTN_SHOW: + psheet.ToolTip.PositionToolTip(this, ClientRectangle); + m.Result = (IntPtr)1; + return true; + default: + psheet.WndProc(ref m); + break; + } + } + } + return false; + } + + protected override void WndProc(ref Message m) { + + if (filter) { + if (psheet.FilterEditWndProc(ref m)) { + return; + } + } + + switch (m.Msg) { + case NativeMethods.WM_STYLECHANGED: + if ((unchecked( (int) (long)m.WParam) & NativeMethods.GWL_EXSTYLE) != 0) { + psheet.Invalidate(); + } + break; + case NativeMethods.WM_MOUSEMOVE: + if (unchecked( (int) (long)m.LParam) == lastMove) { + return; + } + lastMove = unchecked( (int) (long)m.LParam); + break; + case NativeMethods.WM_DESTROY: + mouseHook.HookMouseDown = false; + break; + case NativeMethods.WM_SHOWWINDOW: + if (IntPtr.Zero == m.WParam) { + mouseHook.HookMouseDown = false; + } + break; + case NativeMethods.WM_PASTE: + /*if (!this.ReadOnly) { + IDataObject dataObject = Clipboard.GetDataObject(); + Debug.Assert(dataObject != null, "Failed to get dataObject from clipboard"); + if (dataObject != null) { + object data = dataObject.GetData(typeof(string)); + if (data != null) { + string clipboardText = data.ToString(); + SelectedText = clipboardText; + m.result = 1; + return; + } + } + }*/ + if (this.ReadOnly) { + return; + } + break; + + case NativeMethods.WM_GETDLGCODE: + + m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTARROWS | NativeMethods.DLGC_WANTCHARS); + if (psheet.NeedsCommit || this.WantsTab((ModifierKeys & Keys.Shift) == 0)) { + m.Result = (IntPtr)((long)m.Result | NativeMethods.DLGC_WANTALLKEYS | NativeMethods.DLGC_WANTTAB); + } + return; + + case NativeMethods.WM_NOTIFY: + if (WmNotify(ref m)) + return; + break; + } + base.WndProc(ref m); + } + + public virtual bool InSetText() { + return fInSetText; + } + + [ComVisible(true)] + protected class GridViewEditAccessibleObject : ControlAccessibleObject { + + private PropertyGridView propertyGridView; + + public GridViewEditAccessibleObject(GridViewEdit owner) : base(owner) { + this.propertyGridView = owner.psheet; + } + + public override AccessibleStates State { + get { + AccessibleStates states = base.State; + if (this.IsReadOnly) { + states |= AccessibleStates.ReadOnly; + } + else { + states &= ~AccessibleStates.ReadOnly; + } + return states; + } + } + + internal override bool IsIAccessibleExSupported() { + return true; + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + if (direction == UnsafeNativeMethods.NavigateDirection.Parent) { + return propertyGridView.SelectedGridEntry.AccessibilityObject; + } + else if (direction == UnsafeNativeMethods.NavigateDirection.NextSibling) { + if (propertyGridView.DropDownButton.Visible) { + return propertyGridView.DropDownButton.AccessibilityObject; + } else if (propertyGridView.DialogButton.Visible) { + return propertyGridView.DialogButton.AccessibilityObject; + } + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + if (AccessibilityImprovements.Level3) { + return propertyGridView.AccessibilityObject; + } + + return base.FragmentRoot; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_IsEnabledPropertyId) { + return !this.IsReadOnly; + } + else if (propertyID == NativeMethods.UIA_IsValuePatternAvailablePropertyId) { + return IsPatternSupported(NativeMethods.UIA_ValuePatternId); + } + + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_EditControlTypeId; + } + else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ValuePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + + public override string Name { + get { + if (AccessibilityImprovements.Level3) { + string name = Owner.AccessibleName; + if (name != null) { + return name; + } + else { + var selectedGridEntry = propertyGridView.SelectedGridEntry; + if (selectedGridEntry != null) { + return selectedGridEntry.AccessibilityObject.Name; + } + } + } + + return base.Name; + } + + set { + base.Name = value; + } + } + + #region IValueProvider + + internal override bool IsReadOnly { + get { + PropertyDescriptorGridEntry propertyDescriptorGridEntry = this.propertyGridView.SelectedGridEntry as PropertyDescriptorGridEntry; + return propertyDescriptorGridEntry == null || propertyDescriptorGridEntry.IsPropertyReadOnly; + } + } + + #endregion + + internal override void SetFocus() { + if (AccessibilityImprovements.Level3) { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + + base.SetFocus(); + } + } + } + + internal interface IMouseHookClient { + + // return true if the click is handled, false + // to pass it on + bool OnClickHooked(); + } + + internal class MouseHook { + private PropertyGridView gridView; + private Control control; + private IMouseHookClient client; + + internal int thisProcessID = 0; + private GCHandle mouseHookRoot; + private IntPtr mouseHookHandle = IntPtr.Zero; + private bool hookDisable = false; + + private bool processing; + + public MouseHook(Control control, IMouseHookClient client, PropertyGridView gridView) { + this.control = control; + this.gridView = gridView; + this.client = client; + #if DEBUG + callingStack = Environment.StackTrace; + #endif + } + + #if DEBUG + string callingStack; + ~MouseHook() { + Debug.Assert(mouseHookHandle == IntPtr.Zero, "Finalizing an active mouse hook. This will crash the process. Calling stack: " + callingStack); + } + #endif + + + public bool DisableMouseHook { + + set { + hookDisable = value; + if (value) { + UnhookMouse(); + } + } + } + + + public virtual bool HookMouseDown{ + get{ + GC.KeepAlive(this); + return mouseHookHandle != IntPtr.Zero; + } + set{ + if (value && !hookDisable) { + HookMouse(); + } + else { + UnhookMouse(); + } + } + } + + + public void Dispose() { + UnhookMouse(); + } + + + /// + /// + /// Sets up the needed windows hooks to catch messages. + /// + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + private void HookMouse() { + GC.KeepAlive(this); + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (mouseHookHandle != IntPtr.Zero) { + return; + } + + if (thisProcessID == 0) { + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(control, control.Handle), out thisProcessID); + } + + + NativeMethods.HookProc hook = new NativeMethods.HookProc(new MouseHookObject(this).Callback); + mouseHookRoot = GCHandle.Alloc(hook); + + mouseHookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE, + hook, + NativeMethods.NullHandleRef, + SafeNativeMethods.GetCurrentThreadId()); + Debug.Assert(mouseHookHandle != IntPtr.Zero, "Failed to install mouse hook"); + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:HookMouse()"); + } + } + + /// + /// + /// HookProc used for catch mouse messages. + /// + /// + private IntPtr MouseHookProc(int nCode, IntPtr wparam, IntPtr lparam) { + GC.KeepAlive(this); + if (nCode == NativeMethods.HC_ACTION) { + NativeMethods.MOUSEHOOKSTRUCT mhs = (NativeMethods.MOUSEHOOKSTRUCT)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.MOUSEHOOKSTRUCT)); + if (mhs != null) { + switch (unchecked( (int) (long)wparam)) { + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_MOUSEACTIVATE: + if (ProcessMouseDown(mhs.hWnd, mhs.pt_x, mhs.pt_y)) { + return (IntPtr)1; + } + break; + } + + } + } + + return UnsafeNativeMethods.CallNextHookEx(new HandleRef(this, mouseHookHandle), nCode, wparam, lparam); + } + + /// + /// + /// Removes the windowshook that was installed. + /// + /// + private void UnhookMouse() { + GC.KeepAlive(this); + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + if (mouseHookHandle != IntPtr.Zero) { + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(this, mouseHookHandle)); + mouseHookRoot.Free(); + mouseHookHandle = IntPtr.Zero; + Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:UnhookMouse()"); + } + } + } + + /* + * Here is where we force validation on any clicks outside the + */ + private bool ProcessMouseDown(IntPtr hWnd, int x, int y) { + + + // com+ 12678 + // if we put up the "invalid" message box, it appears this + // method is getting called re-entrantly when it shouldn't be. + // this prevents us from recursing. + // + if (processing) { + return false; + } + + IntPtr hWndAtPoint = hWnd; + IntPtr handle = control.Handle; + Control ctrlAtPoint = Control.FromHandleInternal(hWndAtPoint); + + // if it's us or one of our children, just process as normal + // + if (hWndAtPoint != handle && !control.Contains(ctrlAtPoint)) { + Debug.Assert(thisProcessID != 0, "Didn't get our process id!"); + + // make sure the window is in our process + int pid; + SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWndAtPoint), out pid); + + // if this isn't our process, unhook the mouse. + if (pid != thisProcessID) { + this.HookMouseDown = false; + return false; + } + + bool needCommit = false; + + // if this a sibling control (e.g. the drop down or buttons), just forward the message and skip the commit + needCommit = ctrlAtPoint == null ? true : !gridView.IsSiblingControl(control, ctrlAtPoint); + + try { + processing = true; + + if (needCommit) { + if (client.OnClickHooked()) { + return true; // there was an error, so eat the mouse + } + /* This breaks all sorts of stuff. Need to find a better way to do this but we can't figure + out the scenario this addressed. + else { + // Returning false lets the message go to its destination. Only + // return false if there is still a mouse button down. That might not be the + // case if committing the entry opened a modal dialog. + // + MouseButtons state = Control.MouseButtons; + return (int)state == 0; + } + */ + } + } + finally { + processing = false; + } + + // cancel our hook at this point + HookMouseDown = false; + //gridView.UnfocusSelection(); + } + return false; + } + + /// + /// + /// Forwards messageHook calls to ToolTip.messageHookProc + /// + /// + private class MouseHookObject { + internal WeakReference reference; + + public MouseHookObject(MouseHook parent) { + this.reference = new WeakReference(parent, false); + } + + public virtual IntPtr Callback(int nCode, IntPtr wparam, IntPtr lparam) { + IntPtr ret = IntPtr.Zero; + try { + MouseHook control = (MouseHook)reference.Target; + if (control != null) { + ret = control.MouseHookProc(nCode, wparam, lparam); + } + } + catch { + // ignore + } + return ret; + } + } + } + + /// + /// + /// The accessible object class for a PropertyGridView. The child accessible objects + /// are accessible objects corresponding to the property grid entries. + /// + [ComVisible(true)] + internal class PropertyGridViewAccessibleObject : ControlAccessibleObject { + + private PropertyGridView _owningPropertyGridView; + private PropertyGrid _parentPropertyGrid; + + /// + /// + /// Construct a PropertyGridViewAccessibleObject + /// + public PropertyGridViewAccessibleObject(PropertyGridView owner, PropertyGrid parentPropertyGrid) : base(owner) { + _owningPropertyGridView = owner; + _parentPropertyGrid = parentPropertyGrid; + } + + /// + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + /// x coordinate of point to check + /// y coordinate of point to check + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + if (AccessibilityImprovements.Level3) { + return HitTest((int)x, (int)y); + } + + return base.ElementProviderFromPoint(x, y); + } + + /// + /// Request to return the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + var propertyGridAccessibleObject = _parentPropertyGrid.AccessibilityObject as PropertyGridAccessibleObject; + if (propertyGridAccessibleObject != null) { + var navigationTarget = propertyGridAccessibleObject.ChildFragmentNavigate(this, direction); + if (navigationTarget != null) { + return navigationTarget; + } + } + + if (_owningPropertyGridView.OwnerGrid.SortedByCategories) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetFirstCategory(); + case UnsafeNativeMethods.NavigateDirection.LastChild: + return GetLastCategory(); + } + } + else { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetChild(0); + case UnsafeNativeMethods.NavigateDirection.LastChild: + int childCount = GetChildCount(); + if (childCount > 0) { + return GetChild(childCount - 1); + } + + return null; + } + } + } + + return base.FragmentNavigate(direction); + } + + /// + /// Return the element that is the root node of this fragment of UI. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + if (AccessibilityImprovements.Level3) { + return _owningPropertyGridView.OwnerGrid.AccessibilityObject; + } + + return base.FragmentRoot; + } + } + + /// + /// Gets the accessible object for the currently focused grid entry. + /// + /// The accessible object for the currently focused grid entry. + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + if (AccessibilityImprovements.Level3) { + return GetFocused(); + } + + return base.FragmentRoot; + } + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_TableControlTypeId; + } else if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + } + + return base.GetPropertyValue(propertyID); + } + + public override string Name { + get { + string name = Owner.AccessibleName; + if (name != null) { + return name; + } + else { + return SR.GetString(SR.PropertyGridDefaultAccessibleName); + } + } + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return AccessibleRole.Table; + } + } + } + + public AccessibleObject Next(GridEntry current) { + int row = ((PropertyGridView)Owner).GetRowFromGridEntry(current); + GridEntry nextEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(++row); + if (nextEntry != null) { + return nextEntry.AccessibilityObject; + } + return null; + } + + internal AccessibleObject GetCategory(int categoryIndex) { + GridEntry[] targetEntries = new GridEntry[1]; + var topLevelGridEntries = _owningPropertyGridView.TopLevelGridEntries; + var topLevelGridEntriesCount = topLevelGridEntries.Count; + if (topLevelGridEntriesCount > 0) { + var targetEntry = topLevelGridEntries[categoryIndex]; + var categoryGridEntry = targetEntry as CategoryGridEntry; + if (categoryGridEntry != null) { + return categoryGridEntry.AccessibilityObject; + } + } + + return null; + } + + internal AccessibleObject GetFirstCategory() { + return GetCategory(0); + } + + internal AccessibleObject GetLastCategory() { + var topLevelGridEntries = _owningPropertyGridView.TopLevelGridEntries; + var topLevelGridEntriesCount = topLevelGridEntries.Count; + return GetCategory(topLevelGridEntries.Count - 1); + } + + /// + /// Gets the previous grid entry accessibility object. + /// + /// The current grid entry. + /// The grid entry collection. + /// Indicates whether the current grid entry is found. + /// The previous grid entry. + internal AccessibleObject GetPreviousGridEntry(GridEntry currentGridEntry, GridEntryCollection gridEntryCollection, out bool currentGridEntryFound) { + GridEntry previousGridEntry = null; + currentGridEntryFound = false; + + foreach (GridEntry gridEntry in gridEntryCollection) { + if (currentGridEntry == gridEntry) { + // Set to true to return the previous iterable element. + currentGridEntryFound = true; + if (previousGridEntry != null) { + // In the current iteration return previous entry if the current entry == iterated grid entry. + return previousGridEntry.AccessibilityObject; + } + else { + return null; + } + } + else { + previousGridEntry = gridEntry; + if (gridEntry.ChildCount > 0) { + var foundChild = GetPreviousGridEntry(currentGridEntry, gridEntry.Children, out currentGridEntryFound); + if (foundChild != null) { + // Return some down-level child if found. + return foundChild; + } + else if (currentGridEntryFound) { + // If the passed current is found but there is no next near this current. + return null; + } + } + } + } + + return null; + } + + /// + /// Gets the next grid entry. + /// + /// The current grid entry. + /// The grid entry collection. + /// Indicates whether the current grid entry is found. + /// The next grid entry. + internal AccessibleObject GetNextGridEntry(GridEntry currentGridEntry, GridEntryCollection gridEntryCollection, out bool currentGridEntryFound) { + currentGridEntryFound = false; + + foreach (GridEntry gridEntry in gridEntryCollection) { + if (currentGridEntryFound) { + // Return the next entry via IEnumerable.Next() if previous entry == passed current. + return gridEntry.AccessibilityObject; + } + + if (currentGridEntry == gridEntry) { + // Set to true to return the next iterable element. (see above) + currentGridEntryFound = true; + } + else if (gridEntry.ChildCount > 0) { + var foundChild = GetNextGridEntry(currentGridEntry, gridEntry.Children, out currentGridEntryFound); + if (foundChild != null) { + // Return some down-level child if found. + return foundChild; + } + else if (currentGridEntryFound) { + // If the passed current is found but there is no next near this current. + return null; + } + } + } + + return null; + } + + /// + /// Gets the first child property. + /// + /// The current grid entry. + /// The first child property. + internal AccessibleObject GetFirstChildProperty(CategoryGridEntry current) { + if (current.ChildCount > 0) { + GridEntryCollection subGridEntry = current.Children; + if (subGridEntry != null && subGridEntry.Count > 0) { + GridEntry[] targetEntries = new GridEntry[1]; + try { + _owningPropertyGridView.GetGridEntriesFromOutline(subGridEntry, 0, 0, targetEntries); + } + catch (Exception ex) { + Debug.Fail(ex.ToString()); + } + + return targetEntries[0].AccessibilityObject; + } + } + + return null; + } + + /// + /// Gets the last child property. + /// + /// The current grid entry. + /// The last child property. + internal AccessibleObject GetLastChildProperty(CategoryGridEntry current) { + if (current.ChildCount > 0) { + GridEntryCollection subGridEntry = current.Children; + if (subGridEntry != null && subGridEntry.Count > 0) { + GridEntry[] targetEntries = new GridEntry[1]; + try { + _owningPropertyGridView.GetGridEntriesFromOutline(subGridEntry, 0, subGridEntry.Count - 1, targetEntries); + } + catch (Exception ex) { + Debug.Fail(ex.ToString()); + } + + return targetEntries[0].AccessibilityObject; + } + } + + return null; + } + + /// + /// Gets the next category. + /// + /// The current grid entry. + /// The next category. + internal AccessibleObject GetNextCategory(CategoryGridEntry current) { + int row = _owningPropertyGridView.GetRowFromGridEntry(current); + + GridEntry nextEntry; + + do { + nextEntry = _owningPropertyGridView.GetGridEntryFromRow(++row); + if (nextEntry is CategoryGridEntry) { + return nextEntry.AccessibilityObject; + } + } + while (nextEntry != null); + + return null; + } + + public AccessibleObject Previous(GridEntry current) { + int row = ((PropertyGridView)Owner).GetRowFromGridEntry(current); + GridEntry prevEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(--row); + if (prevEntry != null) { + return prevEntry.AccessibilityObject; + } + return null; + } + + /// + /// Gets the previous category. + /// + /// The current grid entry. + /// The previous category. + internal AccessibleObject GetPreviousCategory(CategoryGridEntry current) { + int row = _owningPropertyGridView.GetRowFromGridEntry(current); + + GridEntry previousEntry; + + do { + previousEntry = _owningPropertyGridView.GetGridEntryFromRow(--row); + if (previousEntry is CategoryGridEntry) { + return previousEntry.AccessibilityObject; + } + } + while (previousEntry != null); + + return null; + } + + /// + /// + /// Get the accessible child at the given index. + /// The accessible children of a PropertyGridView are accessible objects + /// corresponding to the property grid entries. + /// + public override AccessibleObject GetChild(int index) { + + GridEntryCollection properties = ((PropertyGridView)Owner).AccessibilityGetGridEntries(); + if (properties != null && index >= 0 && index < properties.Count) { + return properties.GetEntry(index).AccessibilityObject; + } + else { + return null; + } + } + + /// + /// + /// Get the number of accessible children. + /// The accessible children of a PropertyGridView are accessible objects + /// corresponding to the property grid entries. + /// + public override int GetChildCount() { + GridEntryCollection properties = ((PropertyGridView)Owner).AccessibilityGetGridEntries(); + + if (properties != null) { + return properties.Count; + } + else { + return 0; + } + } + + /// + /// + /// Get the accessible object for the currently focused grid entry. + /// + public override AccessibleObject GetFocused() { + + GridEntry gridEntry = ((PropertyGridView)Owner).SelectedGridEntry; + if (gridEntry != null && gridEntry.Focus) { + return gridEntry.AccessibilityObject; + } + return null; + } + + /// + /// + /// Get the accessible object for the currently selected grid entry. + /// + public override AccessibleObject GetSelected() { + GridEntry gridEntry = ((PropertyGridView)Owner).SelectedGridEntry; + if (gridEntry != null) { + return gridEntry.AccessibilityObject; + } + return null; + } + + + + /// + /// + /// Get the accessible child at the given screen location. + /// The accessible children of a PropertyGridView are accessible objects + /// corresponding to the property grid entries. + /// + public override AccessibleObject HitTest(int x, int y) { + + // Convert to client coordinates + // + NativeMethods.POINT pt = new NativeMethods.POINT(x, y); + UnsafeNativeMethods.ScreenToClient(new HandleRef(Owner, Owner.Handle), pt); + + // Find the grid entry at the given client coordinates + // + Point pos = ((PropertyGridView)Owner).FindPosition(pt.x, pt.y); + if (pos != PropertyGridView.InvalidPosition) { + GridEntry gridEntry = ((PropertyGridView)Owner).GetGridEntryFromRow(pos.Y); + if (gridEntry != null) { + + // Return the accessible object for this grid entry + // + return gridEntry.AccessibilityObject; + } + } + + // No grid entry at this point + // + return null; + } + + /// + /// + /// Navigate to another object. + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navdir) { + + if (GetChildCount() > 0) { + // We're only handling FirstChild and LastChild here + switch(navdir) { + case AccessibleNavigation.FirstChild: + return GetChild(0); + case AccessibleNavigation.LastChild: + return GetChild(GetChildCount() - 1); + } + } + return null; // Perform default behavior + } + } + + internal class GridPositionData { + + ArrayList expandedState; + GridEntryCollection selectedItemTree; + int itemRow; + int itemCount; + + public GridPositionData(PropertyGridView gridView) { + selectedItemTree = gridView.GetGridEntryHierarchy(gridView.selectedGridEntry); + expandedState = gridView.SaveHierarchyState(gridView.topLevelGridEntries); + itemRow = gridView.selectedRow; + itemCount = gridView.totalProps; + } + + public GridEntry Restore(PropertyGridView gridView) { + gridView.RestoreHierarchyState(expandedState); + GridEntry entry = gridView.FindEquivalentGridEntry(selectedItemTree); + + if (entry != null) { + gridView.SelectGridEntry(entry, true); + + int delta = gridView.selectedRow - itemRow; + if (delta != 0 && gridView.ScrollBar.Visible) { + if (itemRow < gridView.visibleRows) { + delta += gridView.GetScrollOffset(); + + if (delta < 0) { + delta = 0; + } + else if (delta > gridView.ScrollBar.Maximum) { + delta = gridView.ScrollBar.Maximum - 1; + } + gridView.SetScrollOffset(delta); + } + + } + } + return entry; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/PropertyGridInternal/SingleSelectRootGridEntry.cs b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/SingleSelectRootGridEntry.cs new file mode 100644 index 000000000..7721edcd0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyGridInternal/SingleSelectRootGridEntry.cs @@ -0,0 +1,359 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms.PropertyGridInternal { + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Reflection; + using System.ComponentModel.Design; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using System.Drawing; + using Microsoft.Win32; + + + internal class SingleSelectRootGridEntry : GridEntry, IRootGridEntry { + protected object objValue; + protected String objValueClassName; + protected GridEntry propDefault; + protected IDesignerHost host; + protected IServiceProvider baseProvider = null; + protected PropertyTab tab = null; + protected PropertyGridView gridEntryHost = null; + protected AttributeCollection browsableAttributes = null; + private IComponentChangeService changeService; + protected bool forceReadOnlyChecked; + + // + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + internal SingleSelectRootGridEntry(PropertyGridView gridEntryHost, object value, GridEntry parent, IServiceProvider baseProvider, IDesignerHost host, PropertyTab tab, PropertySort sortType) + : base(gridEntryHost.OwnerGrid, parent) { + Debug.Assert(value != null,"Can't browse a null object!"); + this.host = host; + this.gridEntryHost = gridEntryHost; + this.baseProvider = baseProvider; + this.tab = tab; + this.objValue = value; + this.objValueClassName = TypeDescriptor.GetClassName(this.objValue); + + this.IsExpandable = true; + // default to categories + this.PropertySort = sortType; + this.InternalExpanded = true; + } + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // GridEntry classes are internal so we have complete + // control over who does what in the constructor. + ] + internal SingleSelectRootGridEntry(PropertyGridView view, object value, IServiceProvider baseProvider, IDesignerHost host, PropertyTab tab, PropertySort sortType) : this(view, value,null, baseProvider, host, tab, sortType) { + } + + /// + /// + /// The set of attributes that will be used for browse filtering + /// + public override AttributeCollection BrowsableAttributes { + get { + if (browsableAttributes == null) { + browsableAttributes = new AttributeCollection(new Attribute[]{BrowsableAttribute.Yes}); + } + return this.browsableAttributes; + } + set { + if (value == null) { + ResetBrowsableAttributes(); + return; + } + + bool same = true; + + if (this.browsableAttributes != null && value != null && this.browsableAttributes.Count == value.Count) { + Attribute[] attr1 = new Attribute[browsableAttributes.Count]; + Attribute[] attr2 = new Attribute[value.Count]; + + browsableAttributes.CopyTo(attr1, 0); + value.CopyTo(attr2, 0); + + Array.Sort(attr1, GridEntry.AttributeTypeSorter); + Array.Sort(attr2, GridEntry.AttributeTypeSorter); + for (int i = 0; i < attr1.Length; i++) { + if (!attr1[i].Equals(attr2[i])) { + same = false; + break; + } + } + } + else { + same = false; + } + + this.browsableAttributes = value; + + if (!same && Children != null && Children.Count > 0) { + DisposeChildren(); + } + } + } + + protected override IComponentChangeService ComponentChangeService { + get { + if (changeService == null) { + changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + } + return changeService; + } + } + + internal override bool AlwaysAllowExpand { + get { + return true; + } + } + + public override PropertyTab CurrentTab { + get { + return tab; + } + set { + this.tab = value; + } + } + + internal override GridEntry DefaultChild { + get { + return propDefault; + } + set { + this.propDefault = value; + } + } + + internal override IDesignerHost DesignerHost { + get { + return host; + } + set { + host = value; + } + } + + internal override bool ForceReadOnly { + get { + if (!forceReadOnlyChecked) { + ReadOnlyAttribute readOnlyAttr = (ReadOnlyAttribute)TypeDescriptor.GetAttributes(this.objValue)[typeof(ReadOnlyAttribute)]; + if ((readOnlyAttr != null && !readOnlyAttr.IsDefaultAttribute()) || TypeDescriptor.GetAttributes(this.objValue).Contains(InheritanceAttribute.InheritedReadOnly)) { + flags |= FLAG_FORCE_READONLY; + } + forceReadOnlyChecked = true; + } + return base.ForceReadOnly || (GridEntryHost != null && !GridEntryHost.Enabled); + } + } + + internal override PropertyGridView GridEntryHost { + get { + return this.gridEntryHost; + } + set { + this.gridEntryHost = value; + } + } + + public override GridItemType GridItemType { + get { + return GridItemType.Root; + } + } + + /// + /// + /// Retrieves the keyword that the VS help dynamic help window will + /// use when this IPE is selected. + /// + public override string HelpKeyword { + get { + + HelpKeywordAttribute helpAttribute = (HelpKeywordAttribute)TypeDescriptor.GetAttributes(objValue)[typeof(HelpKeywordAttribute)]; + + if (helpAttribute != null && !helpAttribute.IsDefaultAttribute()) { + return helpAttribute.HelpKeyword; + } + + return this.objValueClassName; + } + } + + public override string PropertyLabel { + get { + if (objValue is IComponent) { + ISite site = ((IComponent)objValue).Site; + if (site == null) { + return objValue.GetType().Name; + } + else { + return site.Name; + } + } + else if (objValue != null) { + return objValue.ToString(); + } + return null; + } + } + + /// + /// + /// Gets or sets the value for the property that is represented + /// by this GridEntry. + /// + public override object PropertyValue{ + get { + return objValue; + } + set { + object old = objValue; + objValue = value; + objValueClassName = TypeDescriptor.GetClassName(objValue); + ownerGrid.ReplaceSelectedObject(old, value); + } + } + + protected override bool CreateChildren() { + bool fReturn = base.CreateChildren(); + CategorizePropEntries(); + return fReturn; + } + + protected override void Dispose(bool disposing) { + if (disposing) { + host = null; + baseProvider = null; + tab = null; + gridEntryHost = null; + changeService = null; + } + this.objValue = null; + this.objValueClassName = null; + this.propDefault = null; + base.Dispose(disposing); + } + + public override object GetService(Type serviceType) { + object service = null; + + if (host != null) { + service = host.GetService(serviceType); + } + if (service == null && baseProvider != null) { + service = baseProvider.GetService(serviceType); + } + return service; + } + + /// + /// + /// Reset the Browsable attributes to the default (BrowsableAttribute.Yes) + /// + public void ResetBrowsableAttributes() { + this.browsableAttributes = new AttributeCollection(new Attribute[]{BrowsableAttribute.Yes}); + } + + + /// + /// + /// Sets the value of this GridEntry from text + /// + public virtual void ShowCategories(bool fCategories) { + if (((this.PropertySort &= PropertySort.Categorized) !=0) != fCategories) { + + if (fCategories) { + this.PropertySort |= PropertySort.Categorized; + } + else { + this.PropertySort &= ~PropertySort.Categorized; + } + + + // recreate the children + if (this.Expandable && this.ChildCollection != null) { + CreateChildren(); + } + } + } + + internal void CategorizePropEntries() { + if (Children.Count > 0) { + + GridEntry[] childEntries = new GridEntry[this.Children.Count]; + this.Children.CopyTo(childEntries, 0); + + if ((this.PropertySort & PropertySort.Categorized) != 0) { + + + // first, walk through all the entires and + // group them by their category by adding + // them to a hashtable of arraylists. + // + Hashtable bins = new Hashtable(); + for (int i = 0; i < childEntries.Length; i++) { + GridEntry pe = childEntries[i]; + Debug.Assert(pe != null); + if (pe != null) { + string category = pe.PropertyCategory; + ArrayList bin = (ArrayList)bins[category]; + if (bin == null) { + bin = new ArrayList(); + bins[category] = bin; + } + bin.Add(pe); + } + } + + // now walk through the hashtable + // and create a categorygridentry for each + // category that holds all the properties + // of that category. + // + ArrayList propList = new ArrayList(); + IDictionaryEnumerator enumBins = (IDictionaryEnumerator)bins.GetEnumerator(); + while (enumBins.MoveNext()) { + ArrayList bin = (ArrayList)enumBins.Value; + if (bin != null) { + string category = (string)enumBins.Key; + if (bin.Count > 0) { + GridEntry[] rgpes = new GridEntry[bin.Count]; + bin.CopyTo(rgpes, 0); + try { + propList.Add(new CategoryGridEntry(this.ownerGrid, this, category, rgpes)); + } + catch { + } + } + } + } + + childEntries = new GridEntry[propList.Count]; + propList.CopyTo(childEntries, 0); + StringSorter.Sort(childEntries); + + ChildCollection.Clear(); + ChildCollection.AddRange(childEntries); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyManager.cs b/WindowsForms/Managed/System/WinForms/PropertyManager.cs new file mode 100644 index 000000000..c06f196e3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyManager.cs @@ -0,0 +1,238 @@ +namespace System.Windows.Forms { + + using System; + using System.Windows.Forms; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using System.Collections; + + /// + /// + /// [To be supplied.] + /// + public class PropertyManager : BindingManagerBase { + + // PropertyManager class + // + + private object dataSource; + private string propName; + private PropertyDescriptor propInfo; + private bool bound; + + + /// + /// + /// [To be supplied.] + /// + public override Object Current { + get { + return this.dataSource; + } + } + + private void PropertyChanged(object sender, EventArgs ea) { + EndCurrentEdit(); + OnCurrentChanged(EventArgs.Empty); + } + + internal override void SetDataSource(Object dataSource) { + if (this.dataSource != null && !String.IsNullOrEmpty(this.propName)) { + propInfo.RemoveValueChanged(this.dataSource, new EventHandler(PropertyChanged)); + propInfo = null; + } + + this.dataSource = dataSource; + + if (this.dataSource != null && !String.IsNullOrEmpty(this.propName)) { + propInfo = TypeDescriptor.GetProperties(dataSource).Find(propName, true); + if (propInfo == null) + throw new ArgumentException(SR.GetString(SR.PropertyManagerPropDoesNotExist, propName, dataSource.ToString())); + propInfo.AddValueChanged(dataSource, new EventHandler(PropertyChanged)); + } + } + + /// + /// + /// [To be supplied.] + /// + public PropertyManager() {} + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the dataSource + // it would be a breaking change. + ] + internal PropertyManager(Object dataSource) : base(dataSource){} + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the dataSource + // it would be a breaking change. + ] + internal PropertyManager(Object dataSource, string propName) : base() { + this.propName = propName; + this.SetDataSource(dataSource); + } + + internal override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { + return ListBindingHelper.GetListItemProperties(dataSource, listAccessors); + } + + internal override Type BindType { + get { + return dataSource.GetType(); + } + } + + internal override String GetListName() { + return TypeDescriptor.GetClassName(dataSource) + "." + propName; + } + + /// + public override void SuspendBinding() { + EndCurrentEdit(); + if (bound) { + try { + bound = false; + UpdateIsBinding(); + } catch { + bound = true; + UpdateIsBinding(); + throw; + } + } + } + + /// + public override void ResumeBinding() { + OnCurrentChanged(new EventArgs()); + if (!bound) { + try { + bound = true; + UpdateIsBinding(); + } catch { + bound = false; + UpdateIsBinding(); + throw; + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected internal override String GetListName(ArrayList listAccessors) { + return ""; + } + + /// + /// + /// [To be supplied.] + /// + public override void CancelCurrentEdit() { + IEditableObject obj = this.Current as IEditableObject; + if (obj != null) + obj.CancelEdit(); + PushData(); + } + + /// + /// + /// [To be supplied.] + /// + public override void EndCurrentEdit() { + bool success; + PullData(out success); + + if (success) { + IEditableObject obj = this.Current as IEditableObject; + if (obj != null) + obj.EndEdit(); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void UpdateIsBinding() { + for (int i = 0; i < this.Bindings.Count; i++) + this.Bindings[i].UpdateIsBinding(); + } + + /// + /// + /// [To be supplied.] + /// + internal protected override void OnCurrentChanged(EventArgs ea) { + PushData(); + + if (this.onCurrentChangedHandler != null) + this.onCurrentChangedHandler(this, ea); + + if (this.onCurrentItemChangedHandler != null) + this.onCurrentItemChangedHandler(this, ea); + } + + /// + /// + /// [To be supplied.] + /// + internal protected override void OnCurrentItemChanged(EventArgs ea) { + PushData(); + + if (this.onCurrentItemChangedHandler != null) + this.onCurrentItemChangedHandler(this, ea); + } + + internal override object DataSource { + get { + return this.dataSource; + } + } + + internal override bool IsBinding { + get { + return (dataSource != null); + } + } + + // no op on the propertyManager + /// + /// + /// [To be supplied.] + /// + public override int Position { + get { + return 0; + } + set { + } + } + + /// + public override int Count { + get { + return 1; + } + } + + // no-op on the propertyManager + /// + /// + /// [To be supplied.] + /// + public override void AddNew() { + throw new NotSupportedException(SR.GetString(SR.DataBindingAddNewNotSupportedOnPropertyManager)); + } + + // no-op on the propertyManager + /// + /// + /// [To be supplied.] + /// + public override void RemoveAt(int index) { + throw new NotSupportedException(SR.GetString(SR.DataBindingRemoveAtNotSupportedOnPropertyManager)); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertySort.cs b/WindowsForms/Managed/System/WinForms/PropertySort.cs new file mode 100644 index 000000000..ba3589d5d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertySort.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + + /// + /// + /// Possible values for property grid sorting mode + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum PropertySort{ + + /// + /// + /// Properties will not be sorted, rather they + /// will be displayed in the order that they are retrieved + /// from the TypeDescriptor. + /// + NoSort = 0, + + /// + /// + /// Properties are sorted as a flat, alphabetical list. + /// + Alphabetical = 1, + + /// + /// + /// Properties are under category groups, which are defined + /// by the properties themselves. + /// + Categorized = 2, + + /// + /// + /// Properties are under category groups, which are defined + /// by the properties themselves, and are alphabetical + /// within those groups. + /// + + CategorizedAlphabetical = Alphabetical | Categorized, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyStore.cs b/WindowsForms/Managed/System/WinForms/PropertyStore.cs new file mode 100644 index 000000000..5530e9a5d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyStore.cs @@ -0,0 +1,948 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + + /// + /// This is a small class that can efficiently store property values. + /// It tries to optimize for size first, "get" access second, and + /// "set" access third. + /// + internal class PropertyStore { + + private static int currentKey; + + private IntegerEntry[] intEntries; + private ObjectEntry[] objEntries; + + /// + /// Retrieves an integer value from our property list. + /// This will set value to zero and return false if the + /// list does not contain the given key. + /// + public bool ContainsInteger(int key) { + bool found; + GetInteger(key, out found); + return found; + } + + /// + /// Retrieves an integer value from our property list. + /// This will set value to zero and return false if the + /// list does not contain the given key. + /// + public bool ContainsObject(int key) { + bool found; + GetObject(key, out found); + return found; + } + + /// + /// Creates a new key for this property store. This is NOT + /// guarded by any thread safety so if you are calling it on + /// multiple threads you should guard. For our purposes, + /// we're fine because this is designed to be called in a class + /// initializer, and we never have the same class hierarchy + /// initializing on multiple threads at once. + /// + public static int CreateKey() { + return currentKey++; + } + + public Color GetColor(int key) { + bool found; + return GetColor(key, out found); + } + + // this is a wrapper around GetObject designed to + // reduce the boxing hit + public Color GetColor(int key, out bool found) { + object storedObject = GetObject(key, out found); + + if (found) { + ColorWrapper wrapper = storedObject as ColorWrapper; + + if (wrapper != null) { + return wrapper.Color; + } +#if DEBUG + else if (storedObject != null) { + Debug.Fail("Have non-null object that isnt a color wrapper stored in a color entry!\r\nDid someone SetObject instead of SetColor?"); + } +#endif + } + // we didnt actually find a non-null color wrapper. + found = false; + return Color.Empty; + } + + + public Padding GetPadding(int key) { + bool found; + return GetPadding(key, out found); + } + + // this is a wrapper around GetObject designed to + // reduce the boxing hit + public Padding GetPadding(int key, out bool found) { + object storedObject = GetObject(key, out found); + + if (found) { + PaddingWrapper wrapper = storedObject as PaddingWrapper; + + + if (wrapper != null) { + return wrapper.Padding; + } +#if DEBUG + else if (storedObject != null) { + Debug.Fail("Have non-null object that isnt a padding wrapper stored in a padding entry!\r\nDid someone SetObject instead of SetPadding?"); + } +#endif + } + + // we didnt actually find a non-null padding wrapper. + found = false; + return Padding.Empty; + } +#if false // FXCOP currently not used + public Size GetSize(int key) { + bool found; + return GetSize(key, out found); + } +#endif + + // this is a wrapper around GetObject designed to + // reduce the boxing hit + public Size GetSize(int key, out bool found) { + object storedObject = GetObject(key, out found); + + if (found) { + SizeWrapper wrapper = storedObject as SizeWrapper; + if (wrapper != null) { + return wrapper.Size; + } +#if DEBUG + else if (storedObject != null) { + Debug.Fail("Have non-null object that isnt a padding wrapper stored in a padding entry!\r\nDid someone SetObject instead of SetPadding?"); + } +#endif + } + // we didnt actually find a non-null size wrapper. + found = false; + return Size.Empty; + } + + public Rectangle GetRectangle(int key) { + bool found; + return GetRectangle(key, out found); + } + + // this is a wrapper around GetObject designed to + // reduce the boxing hit + public Rectangle GetRectangle(int key, out bool found) { + object storedObject = GetObject(key, out found); + + if (found) { + RectangleWrapper wrapper = storedObject as RectangleWrapper; + if (wrapper != null) { + return wrapper.Rectangle; + } +#if DEBUG + else if (storedObject != null) { + Debug.Fail("Have non-null object that isnt a Rectangle wrapper stored in a Rectangle entry!\r\nDid someone SetObject instead of SetRectangle?"); + } +#endif + } + // we didnt actually find a non-null rectangle wrapper. + found = false; + return Rectangle.Empty; + } + + /// + /// Retrieves an integer value from our property list. + /// This will set value to zero and return false if the + /// list does not contain the given key. + /// + public int GetInteger(int key) { + bool found; + return GetInteger(key, out found); + } + + /// + /// Retrieves an integer value from our property list. + /// This will set value to zero and return false if the + /// list does not contain the given key. + /// + public int GetInteger(int key, out bool found) { + + int value = 0; + int index; + short element; + short keyIndex = SplitKey(key, out element); + + found = false; + + if (LocateIntegerEntry(keyIndex, out index)) { + // We have found the relevant entry. See if + // the bitmask indicates the value is used. + // + if (((1 << element) & intEntries[index].Mask) != 0) { + + found = true; + + switch(element) { + case 0: + value = intEntries[index].Value1; + break; + + case 1: + value = intEntries[index].Value2; + break; + + case 2: + value = intEntries[index].Value3; + break; + + case 3: + value = intEntries[index].Value4; + break; + + default: + Debug.Fail("Invalid element obtained from LocateIntegerEntry"); + break; + } + } + } + + return value; + } + + /// + /// Retrieves an object value from our property list. + /// This will set value to null and return false if the + /// list does not contain the given key. + /// + public object GetObject(int key) { + bool found; + return GetObject(key, out found); + } + + /// + /// Retrieves an object value from our property list. + /// This will set value to null and return false if the + /// list does not contain the given key. + /// + public object GetObject(int key, out bool found) { + + object value = null; + int index; + short element; + short keyIndex = SplitKey(key, out element); + + found = false; + + if (LocateObjectEntry(keyIndex, out index)) { + // We have found the relevant entry. See if + // the bitmask indicates the value is used. + // + if (((1 << element) & objEntries[index].Mask) != 0) { + + found = true; + + switch(element) { + case 0: + value = objEntries[index].Value1; + break; + + case 1: + value = objEntries[index].Value2; + break; + + case 2: + value = objEntries[index].Value3; + break; + + case 3: + value = objEntries[index].Value4; + break; + + default: + Debug.Fail("Invalid element obtained from LocateObjectEntry"); + break; + } + } + } + + return value; + } + + + /// + /// Locates the requested entry in our array if entries. This does + /// not do the mask check to see if the entry is currently being used, + /// but it does locate the entry. If the entry is found, this returns + /// true and fills in index and element. If the entry is not found, + /// this returns false. If the entry is not found, index will contain + /// the insert point at which one would add a new element. + /// + private bool LocateIntegerEntry(short entryKey, out int index) { + if (intEntries != null) { + int length = intEntries.Length; + if (length <= 16) { + //if the array is small enough, we unroll the binary search to be more efficient. + //usually the performance gain is around 10% to 20% + //DON'T change this code unless you are very confident! + index = 0; + int midPoint = length / 2; + if (intEntries[midPoint].Key <= entryKey) { + index = midPoint; + } + //we don't move this inside the previous if branch since this catches both the case + //index == 0 and index = midPoint + if (intEntries[index].Key == entryKey) { + return true; + } + + midPoint = (length + 1) / 4; + if (intEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (intEntries[index].Key == entryKey) { + return true; + } + } + + midPoint = (length + 3) / 8; + if (intEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (intEntries[index].Key == entryKey) { + return true; + } + } + + midPoint = (length + 7) / 16; + if (intEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (intEntries[index].Key == entryKey) { + return true; + } + } + + Debug.Assert(index < length); + if (entryKey > intEntries[index].Key) { + index++; + } + Debug_VerifyLocateIntegerEntry(index, entryKey, length); + return false; + } + else { + // Entries are stored in numerical order by key index so we can + // do a binary search on them. + // + int max = length - 1; + int min = 0; + int idx = 0; + + do { + idx = (max + min) / 2; + short currentKeyIndex = intEntries[idx].Key; + + if (currentKeyIndex == entryKey) { + index = idx; + return true; + } + else if (entryKey < currentKeyIndex) { + max = idx - 1; + } + else { + min = idx + 1; + } + } + while (max >= min); + + // Didn't find the index. Setup our output + // appropriately + // + index = idx; + if (entryKey > intEntries[idx].Key) { + index++; + } + return false; + } + } + else { + index = 0; + return false; + } + } + + /// + /// Locates the requested entry in our array if entries. This does + /// not do the mask check to see if the entry is currently being used, + /// but it does locate the entry. If the entry is found, this returns + /// true and fills in index and element. If the entry is not found, + /// this returns false. If the entry is not found, index will contain + /// the insert point at which one would add a new element. + /// + private bool LocateObjectEntry(short entryKey, out int index) { + if (objEntries != null) { + int length = objEntries.Length; + Debug.Assert(length > 0); + if (length <= 16) { + //if the array is small enough, we unroll the binary search to be more efficient. + //usually the performance gain is around 10% to 20% + //DON'T change this code unless you are very confident! + index = 0; + int midPoint = length / 2; + if (objEntries[midPoint].Key <= entryKey) { + index = midPoint; + } + //we don't move this inside the previous if branch since this catches both the case + //index == 0 and index = midPoint + if (objEntries[index].Key == entryKey) { + return true; + } + + midPoint = (length + 1) / 4; + if (objEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (objEntries[index].Key == entryKey) { + return true; + } + } + + midPoint = (length + 3) / 8; + if (objEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (objEntries[index].Key == entryKey) { + return true; + } + } + + midPoint = (length + 7) / 16; + if (objEntries[index + midPoint].Key <= entryKey) { + index += midPoint; + if (objEntries[index].Key == entryKey) { + return true; + } + } + + Debug.Assert(index < length); + if (entryKey > objEntries[index].Key) { + index++; + } + Debug_VerifyLocateObjectEntry(index, entryKey, length); + return false; + } + else { + // Entries are stored in numerical order by key index so we can + // do a binary search on them. + // + int max = length - 1; + int min = 0; + int idx = 0; + + do { + idx = (max + min) / 2; + short currentKeyIndex = objEntries[idx].Key; + + if (currentKeyIndex == entryKey) { + index = idx; + return true; + } + else if (entryKey < currentKeyIndex) { + max = idx - 1; + } + else { + min = idx + 1; + } + } + while (max >= min); + + // Didn't find the index. Setup our output + // appropriately + // + index = idx; + if (entryKey > objEntries[idx].Key) { + index++; + } + return false; + } + } + else { + index = 0; + return false; + } + } +/* + public Color RemoveColor(int key) { + RemoveObject(key); + } +*/ + /// + /// Removes the given key from the array + /// + public void RemoveInteger(int key) { + int index; + short element; + short entryKey = SplitKey(key, out element); + + if (LocateIntegerEntry(entryKey, out index)) { + if (((1 << element) & intEntries[index].Mask) == 0) { + // this element is not being used - return right away + return; + } + + // declare that the element is no longer used + intEntries[index].Mask &= (short) (~((short)(1 << element))); + + if (intEntries[index].Mask == 0) { + // this object entry is no longer in use - let's remove it all together + // not great for perf but very simple and we don't expect to remove much + IntegerEntry[] newEntries = new IntegerEntry[intEntries.Length - 1]; + if (index > 0) { + Array.Copy(intEntries, 0, newEntries, 0, index); + } + if (index < newEntries.Length) { + Debug.Assert(intEntries.Length - index - 1 > 0); + Array.Copy(intEntries, index + 1, newEntries, index, intEntries.Length - index - 1); + } + intEntries = newEntries; + } + else { + // this object entry is still in use - let's just clean up the deleted element + switch (element) + { + case 0: + intEntries[index].Value1 = 0; + break; + + case 1: + intEntries[index].Value2 = 0; + break; + + case 2: + intEntries[index].Value3 = 0; + break; + + case 3: + intEntries[index].Value4 = 0; + break; + + default: + Debug.Fail("Invalid element obtained from LocateIntegerEntry"); + break; + } + } + } + } + + /// + /// Removes the given key from the array + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public void RemoveObject(int key) + { + int index; + short element; + short entryKey = SplitKey(key, out element); + + if (LocateObjectEntry(entryKey, out index)) { + if (((1 << element) & objEntries[index].Mask) == 0) { + // this element is not being used - return right away + return; + } + + // declare that the element is no longer used + objEntries[index].Mask &= (short)(~((short)(1 << element))); + + if (objEntries[index].Mask == 0) { + // this object entry is no longer in use - let's remove it all together + // not great for perf but very simple and we don't expect to remove much + if (objEntries.Length == 1) + { + // instead of allocating an array of length 0, we simply reset the array to null. + objEntries = null; + } + else + { + ObjectEntry[] newEntries = new ObjectEntry[objEntries.Length - 1]; + if (index > 0) + { + Array.Copy(objEntries, 0, newEntries, 0, index); + } + if (index < newEntries.Length) + { + Debug.Assert(objEntries.Length - index - 1 > 0); + Array.Copy(objEntries, index + 1, newEntries, index, objEntries.Length - index - 1); + } + objEntries = newEntries; + } + } + else { + // this object entry is still in use - let's just clean up the deleted element + switch (element) + { + case 0: + objEntries[index].Value1 = null; + break; + + case 1: + objEntries[index].Value2 = null; + break; + + case 2: + objEntries[index].Value3 = null; + break; + + case 3: + objEntries[index].Value4 = null; + break; + + default: + Debug.Fail("Invalid element obtained from LocateObjectEntry"); + break; + } + } + } + } + + public void SetColor(int key, Color value) { + bool found; + object storedObject = GetObject(key, out found); + + if (!found) { + SetObject(key, new ColorWrapper(value)); + } + else { + ColorWrapper wrapper = storedObject as ColorWrapper; + if(wrapper != null) { + // re-using the wrapper reduces the boxing hit. + wrapper.Color = value; + } + else { + Debug.Assert(storedObject == null, "object should either be null or ColorWrapper"); // could someone have SetObject to this key behind our backs? + SetObject(key, new ColorWrapper(value)); + } + + } + } + public void SetPadding(int key, Padding value) { + bool found; + object storedObject = GetObject(key, out found); + + if (!found) { + SetObject(key, new PaddingWrapper(value)); + } + else { + PaddingWrapper wrapper = storedObject as PaddingWrapper; + if(wrapper != null) { + // re-using the wrapper reduces the boxing hit. + wrapper.Padding = value; + } + else { + Debug.Assert(storedObject == null, "object should either be null or PaddingWrapper"); // could someone have SetObject to this key behind our backs? + SetObject(key, new PaddingWrapper(value)); + } + + } + } + public void SetRectangle(int key, Rectangle value) { + + bool found; + object storedObject = GetObject(key, out found); + + if (!found) { + SetObject(key, new RectangleWrapper(value)); + } + else { + RectangleWrapper wrapper = storedObject as RectangleWrapper; + if(wrapper != null) { + // re-using the wrapper reduces the boxing hit. + wrapper.Rectangle = value; + } + else { + Debug.Assert(storedObject == null, "object should either be null or RectangleWrapper"); // could someone have SetObject to this key behind our backs? + SetObject(key, new RectangleWrapper(value)); + } + + } + + } + public void SetSize(int key, Size value) { + + bool found; + object storedObject = GetObject(key, out found); + + if (!found) { + SetObject(key, new SizeWrapper(value)); + } + else { + SizeWrapper wrapper = storedObject as SizeWrapper; + if(wrapper != null) { + // re-using the wrapper reduces the boxing hit. + wrapper.Size = value; + } + else { + Debug.Assert(storedObject == null, "object should either be null or SizeWrapper"); // could someone have SetObject to this key behind our backs? + SetObject(key, new SizeWrapper(value)); + } + + } + } + /// + /// Stores the given value in the key. + /// + public void SetInteger(int key, int value) { + int index; + short element; + short entryKey = SplitKey(key, out element); + + if (!LocateIntegerEntry(entryKey, out index)) { + + // We must allocate a new entry. + // + if (intEntries != null) { + IntegerEntry[] newEntries = new IntegerEntry[intEntries.Length + 1]; + + if (index > 0) { + Array.Copy(intEntries, 0, newEntries, 0, index); + } + + if (intEntries.Length - index > 0) { + Array.Copy(intEntries, index, newEntries, index + 1, intEntries.Length - index); + } + + intEntries = newEntries; + } + else { + intEntries = new IntegerEntry[1]; + Debug.Assert(index == 0, "LocateIntegerEntry should have given us a zero index."); + } + + intEntries[index].Key = entryKey; + } + + // Now determine which value to set. + // + switch(element) { + case 0: + intEntries[index].Value1 = value; + break; + + case 1: + intEntries[index].Value2 = value; + break; + + case 2: + intEntries[index].Value3 = value; + break; + + case 3: + intEntries[index].Value4 = value; + break; + + default: + Debug.Fail("Invalid element obtained from LocateIntegerEntry"); + break; + } + + intEntries[index].Mask = (short)((1 << element) | (ushort)(intEntries[index].Mask)); + } + + /// + /// Stores the given value in the key. + /// + public void SetObject(int key, object value) { + int index; + short element; + short entryKey = SplitKey(key, out element); + + if (!LocateObjectEntry(entryKey, out index)) { + + // We must allocate a new entry. + // + if (objEntries != null) { + ObjectEntry[] newEntries = new ObjectEntry[objEntries.Length + 1]; + + if (index > 0) { + Array.Copy(objEntries, 0, newEntries, 0, index); + } + + if (objEntries.Length - index > 0) { + Array.Copy(objEntries, index, newEntries, index + 1, objEntries.Length - index); + } + + objEntries = newEntries; + } + else { + objEntries = new ObjectEntry[1]; + Debug.Assert(index == 0, "LocateObjectEntry should have given us a zero index."); + } + + objEntries[index].Key = entryKey; + } + + // Now determine which value to set. + // + switch(element) { + case 0: + objEntries[index].Value1 = value; + break; + + case 1: + objEntries[index].Value2 = value; + break; + + case 2: + objEntries[index].Value3 = value; + break; + + case 3: + objEntries[index].Value4 = value; + break; + + default: + Debug.Fail("Invalid element obtained from LocateObjectEntry"); + break; + } + + objEntries[index].Mask = (short)((ushort)(objEntries[index].Mask)|(1 << element)); + } + + /// + /// Takes the given key and splits it into an index + /// and an element. + /// + private short SplitKey(int key, out short element) { + element = (short)(key & 0x00000003); + return (short)(key & 0xFFFFFFFC); + } + + [Conditional("DEBUG_PROPERTYSTORE")] + private void Debug_VerifyLocateIntegerEntry(int index, short entryKey, int length) { + int max = length - 1; + int min = 0; + int idx = 0; + + do { + idx = (max + min) / 2; + short currentKeyIndex = intEntries[idx].Key; + + if (currentKeyIndex == entryKey) { + Debug.Assert(index == idx, "GetIntegerEntry in property store broken. index is " + index + " while it should be " + idx + "length of the array is " + length); + } + else if (entryKey < currentKeyIndex) { + max = idx - 1; + } + else { + min = idx + 1; + } + } + while (max >= min); + + // shouldn't find the index if we run this debug code + if (entryKey > intEntries[idx].Key) { + idx++; + } + Debug.Assert(index == idx, "GetIntegerEntry in property store broken. index is " + index + " while it should be " + idx + "length of the array is " + length); + } + + [Conditional("DEBUG_PROPERTYSTORE")] + private void Debug_VerifyLocateObjectEntry(int index, short entryKey, int length) { + int max = length - 1; + int min = 0; + int idx = 0; + + do { + idx = (max + min) / 2; + short currentKeyIndex = objEntries[idx].Key; + + if (currentKeyIndex == entryKey) { + Debug.Assert(index == idx, "GetObjEntry in property store broken. index is " + index + " while is should be " + idx + "length of the array is " + length); + } + else if (entryKey < currentKeyIndex) { + max = idx - 1; + } + else { + min = idx + 1; + } + } + while (max >= min); + + if (entryKey > objEntries[idx].Key) { + idx++; + } + Debug.Assert(index == idx, "GetObjEntry in property store broken. index is " + index + " while is should be " + idx + "length of the array is " + length); + } + + /// + /// Stores the relationship between a key and a value. + /// We do not want to be so inefficient that we require + /// four bytes for each four byte property, so use an algorithm + /// that uses the bottom two bits of the key to identify + /// one of four elements in an entry. + /// + private struct IntegerEntry { + public short Key; + public short Mask; // only lower four bits are used; mask of used values. + public int Value1; + public int Value2; + public int Value3; + public int Value4; + } + + /// + /// Stores the relationship between a key and a value. + /// We do not want to be so inefficient that we require + /// four bytes for each four byte property, so use an algorithm + /// that uses the bottom two bits of the key to identify + /// one of four elements in an entry. + /// + private struct ObjectEntry { + public short Key; + public short Mask; // only lower four bits are used; mask of used values. + public object Value1; + public object Value2; + public object Value3; + public object Value4; + } + + + private sealed class ColorWrapper { + public Color Color; + public ColorWrapper(Color color){ + this.Color = color; + } + } + + + private sealed class PaddingWrapper{ + public Padding Padding; + public PaddingWrapper(Padding padding){ + this.Padding = padding; + } + } + private sealed class RectangleWrapper{ + public Rectangle Rectangle; + public RectangleWrapper(Rectangle rectangle){ + this.Rectangle = rectangle; + } + } + private sealed class SizeWrapper { + public Size Size; + public SizeWrapper(Size size){ + this.Size = size; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyTabChangedEvent.cs b/WindowsForms/Managed/System/WinForms/PropertyTabChangedEvent.cs new file mode 100644 index 000000000..b5383974c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyTabChangedEvent.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + using System.Windows.Forms.Design; + + /// + /// + /// [To be supplied.] + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class PropertyTabChangedEventArgs : EventArgs{ + + private PropertyTab oldTab; + private PropertyTab newTab; + + /// + /// + /// [To be supplied.] + /// + public PropertyTabChangedEventArgs(PropertyTab oldTab, PropertyTab newTab) { + this.oldTab = oldTab; + this.newTab = newTab; + } + + /// + /// + /// [To be supplied.] + /// + public PropertyTab OldTab { + get { + return oldTab; + } + } + + /// + /// + /// [To be supplied.] + /// + public PropertyTab NewTab { + get { + return newTab; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyTabChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/PropertyTabChangedEventHandler.cs new file mode 100644 index 000000000..52da03c57 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyTabChangedEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// [To be supplied.] + /// + public delegate void PropertyTabChangedEventHandler(object s, PropertyTabChangedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyValueChangedEvent.cs b/WindowsForms/Managed/System/WinForms/PropertyValueChangedEvent.cs new file mode 100644 index 000000000..d1247627b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyValueChangedEvent.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + + /// + /// + /// The event class that is created when a property + /// in the grid is modified by the user. + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class PropertyValueChangedEventArgs : EventArgs { + private readonly GridItem changedItem; + private object oldValue; + /// + /// + /// Constructor + /// + public PropertyValueChangedEventArgs(GridItem changedItem, object oldValue) { + this.changedItem = changedItem; + this.oldValue = oldValue; + } + + /// + /// + /// [To be supplied.] + /// + public GridItem ChangedItem { + get { + return changedItem; + } + } + + /// + /// + /// [To be supplied.] + /// + public object OldValue { + get { + return oldValue; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/PropertyValueChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/PropertyValueChangedEventHandler.cs new file mode 100644 index 000000000..751bf88d7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/PropertyValueChangedEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + + + + /// + /// + /// The event handler class that is invoked when a property + /// in the grid is modified by the user. + /// + public delegate void PropertyValueChangedEventHandler(object s, PropertyValueChangedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEvent.cs b/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEvent.cs new file mode 100644 index 000000000..2faa60686 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEvent.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms; + using Microsoft.Win32; + + /// + /// + /// The QueryAccessibilityHelpEventArgs is fired when AccessibleObject + /// is providing help to accessibility applications. + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class QueryAccessibilityHelpEventArgs : EventArgs { + + private string helpNamespace; + private string helpString; + private string helpKeyword; + + /// + /// + /// [To be supplied.] + /// + public QueryAccessibilityHelpEventArgs() { + } + + /// + /// + /// [To be supplied.] + /// + public QueryAccessibilityHelpEventArgs(string helpNamespace, string helpString, string helpKeyword) { + this.helpNamespace = helpNamespace; + this.helpString = helpString; + this.helpKeyword = helpKeyword; + } + + /// + /// + /// [To be supplied.] + /// + public string HelpNamespace { + get { + return helpNamespace; + } + set { + helpNamespace = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public string HelpString { + get { + return helpString; + } + set { + helpString = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public string HelpKeyword { + get { + return helpKeyword; + } + set { + helpKeyword = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEventHandler.cs b/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEventHandler.cs new file mode 100644 index 000000000..58cef1ca5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QueryAccessibilityHelpEventHandler.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + public delegate void QueryAccessibilityHelpEventHandler(object sender, QueryAccessibilityHelpEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/QueryContinueDragEvent.cs b/WindowsForms/Managed/System/WinForms/QueryContinueDragEvent.cs new file mode 100644 index 000000000..529b9defb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QueryContinueDragEvent.cs @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class QueryContinueDragEventArgs : EventArgs { + + private readonly int keyState; + private readonly bool escapePressed; + private DragAction action; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public QueryContinueDragEventArgs(int keyState, bool escapePressed, DragAction action) { + this.keyState = keyState; + this.escapePressed = escapePressed; + this.action = action; + } + + /// + /// + /// + /// Gets a value indicating + /// the current state of the SHIFT, CTRL, and ALT keys. + /// + /// + public int KeyState { + get { + return keyState; + } + } + + /// + /// + /// + /// Gets a value indicating whether the user pressed the ESC key. + /// + /// + public bool EscapePressed { + get { + return escapePressed; + } + } + + /// + /// + /// + /// Gets + /// or sets the status of a drag-and-drop operation. + /// + /// + public DragAction Action { + get { + return action; + } + set { + action = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/QueryContinueDragEventHandler.cs b/WindowsForms/Managed/System/WinForms/QueryContinueDragEventHandler.cs new file mode 100644 index 000000000..a1a3ef7c4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QueryContinueDragEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + + /// + /// + /// + /// Represents the method that will handle the + /// event of a + /// . + /// + /// + public delegate void QueryContinueDragEventHandler(object sender, QueryContinueDragEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/QuestionEventArgs.cs b/WindowsForms/Managed/System/WinForms/QuestionEventArgs.cs new file mode 100644 index 000000000..5d7036dc1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QuestionEventArgs.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + using System.ComponentModel; + + /// + /// + /// + public class QuestionEventArgs : EventArgs + { + private bool response; + + /// + /// + /// + public QuestionEventArgs() + { + this.response = false; + } + + /// + /// + /// + public QuestionEventArgs(bool response) + { + this.response = response; + } + + /// + /// + /// + public bool Response + { + get + { + return this.response; + } + set + { + this.response = value; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/QuestionEventHandler.cs b/WindowsForms/Managed/System/WinForms/QuestionEventHandler.cs new file mode 100644 index 000000000..a8846375e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/QuestionEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.Diagnostics; + + /// + /// + /// Describes a delegate for an event that has a QuestionEventArgs as + /// a parameter. + /// + public delegate void QuestionEventHandler(object sender, QuestionEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/RTLAwareMessageBox.cs b/WindowsForms/Managed/System/WinForms/RTLAwareMessageBox.cs new file mode 100644 index 000000000..a604528ed --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RTLAwareMessageBox.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + + /// + /// + /// + /// The Show method displays a message box that can contain text, buttons, and symbols that + /// inform and instruct the user. This MessageBox will be RTL, if the resources + /// for this dll have been localized to a RTL language. + /// + /// + internal sealed class RTLAwareMessageBox { + + /// + /// + /// + /// Displays a message box with specified text, caption, and style. + /// Makes the dialog RTL if the resources for this dll have been localized to a RTL language. + /// + /// + public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, MessageBoxOptions options) { + if (RTLAwareMessageBox.IsRTLResources) { + options |= (MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading); + } + return MessageBox.Show(owner, text, caption, buttons, icon, defaultButton, options); + } + + /// + /// Tells whether the current resources for this dll have been + /// localized for a RTL language. + /// + public static bool IsRTLResources { + get { + return SR.GetString(SR.RTL) != "RTL_False"; + } + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/RadioButton.cs b/WindowsForms/Managed/System/WinForms/RadioButton.cs new file mode 100644 index 000000000..885dbbcff --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RadioButton.cs @@ -0,0 +1,704 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Windows.Forms.ButtonInternal; + + using System.ComponentModel; + using System.ComponentModel.Design; + + using System.Drawing; + using System.Windows.Forms.Internal; + + using System.Drawing.Drawing2D; + using System.Windows.Forms.Layout; + using Microsoft.Win32; + + /// + /// + /// + /// Encapsulates a + /// standard + /// Windows radio button (option button). + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Checked"), + DefaultEvent("CheckedChanged"), + DefaultBindingProperty("Checked"), + ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign), + Designer("System.Windows.Forms.Design.RadioButtonDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionRadioButton) + ] + public class RadioButton : ButtonBase { + + private static readonly object EVENT_CHECKEDCHANGED = new object(); + private static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight; + + // Used to see if we need to iterate through the autochecked items and modify their tabstops. + private bool firstfocus = true; + private bool isChecked; + private bool autoCheck = true; + private ContentAlignment checkAlign = ContentAlignment.MiddleLeft; + private Appearance appearance = System.Windows.Forms.Appearance.Normal; + + private const int FlatSystemStylePaddingWidth = 24; + private const int FlatSystemStyleMinimumHeight = 13; + + internal int flatSystemStylePaddingWidth = FlatSystemStylePaddingWidth; + internal int flatSystemStyleMinimumHeight = FlatSystemStyleMinimumHeight; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public RadioButton() : base() { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth); + flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight); + } + + // Radiobuttons shouldn't respond to right clicks, so we need to do all our own click logic + SetStyle(ControlStyles.StandardClick, false); + + TextAlign = ContentAlignment.MiddleLeft; + TabStop = false; + SetAutoSizeMode(AutoSizeMode.GrowAndShrink); + } + + /// + /// + /// Gets or sets a value indicating whether the + /// value and the appearance of + /// the control automatically change when the control is clicked. + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.RadioButtonAutoCheckDescr) + ] + public bool AutoCheck { + get { + return autoCheck; + } + + set { + if (autoCheck != value) { + autoCheck = value; + PerformAutoUpdates(false); + } + } + } + + /// + /// + /// + /// Gets or sets the appearance of the radio + /// button + /// control is drawn. + /// + /// + [ + DefaultValue(Appearance.Normal), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.RadioButtonAppearanceDescr) + ] + public Appearance Appearance { + get { + return appearance; + } + + set { + if (appearance != value) { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)Appearance.Normal, (int)Appearance.Button)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(Appearance)); + } + + using (LayoutTransaction.CreateTransactionIf(AutoSize, this.ParentInternal, this, PropertyNames.Appearance)) { + appearance = value; + if (OwnerDraw) { + Refresh(); + } + else { + UpdateStyles(); + } + + OnAppearanceChanged(EventArgs.Empty); + } + } + } + } + + private static readonly object EVENT_APPEARANCECHANGED = new object(); + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnAppearanceChangedDescr)] + public event EventHandler AppearanceChanged { + add { + Events.AddHandler(EVENT_APPEARANCECHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_APPEARANCECHANGED, value); + } + } + + /// + /// + /// + /// Gets or + /// sets the location of the check box portion of the + /// radio button control. + /// + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(ContentAlignment.MiddleLeft), + SRDescription(SR.RadioButtonCheckAlignDescr) + ] + public ContentAlignment CheckAlign { + get { + return checkAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + + checkAlign = value; + if (OwnerDraw) { + Invalidate(); + } + else { + UpdateStyles(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// control is checked or not. + /// + /// + /// + [ + Bindable(true), + SettingsBindable(true), + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.RadioButtonCheckedDescr) + ] + public bool Checked { + get { + return isChecked; + } + + set { + if (isChecked != value) { + isChecked = value; + + if (IsHandleCreated) SendMessage(NativeMethods.BM_SETCHECK, value? 1: 0, 0); + Invalidate(); + Update(); + PerformAutoUpdates(false); + OnCheckedChanged(EventArgs.Empty); + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "BUTTON"; + if (OwnerDraw) { + cp.Style |= NativeMethods.BS_OWNERDRAW; + } + else { + cp.Style |= NativeMethods.BS_RADIOBUTTON; + if (Appearance == Appearance.Button) { + cp.Style |= NativeMethods.BS_PUSHLIKE; + } + + // Determine the alignment of the radio button + // + ContentAlignment align = RtlTranslateContent(CheckAlign); + if ((int)(align & anyRight) != 0) { + cp.Style |= NativeMethods.BS_RIGHTBUTTON; + } + } + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(104, 24); + } + } + + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// For RadioButton controls, scale the width of the system-style padding and height of the radio button image. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on. + /// + /// Old DPI value + /// New DPI value + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth); + flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight); + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + if(FlatStyle != FlatStyle.System) { + return base.GetPreferredSizeCore(proposedConstraints); + } + + Size textSize = TextRenderer.MeasureText(this.Text, this.Font); + Size size = SizeFromClientSize(textSize); + size.Width += flatSystemStylePaddingWidth; + size.Height = DpiHelper.EnableDpiChangedHighDpiImprovements ? Math.Max(size.Height + 5, flatSystemStyleMinimumHeight) : size.Height + 5; // ensure minimum height to avoid truncation of RadioButton circle or text + return size; + } + + internal override Rectangle OverChangeRectangle { + get { + if (Appearance == Appearance.Button) { + return base.OverChangeRectangle; + } + else { + if (FlatStyle == FlatStyle.Standard) { + // this Rectangle will cause no Invalidation + // can't use Rectangle.Empty because it will cause Invalidate(ClientRectangle) + return new Rectangle(-1, -1, 1, 1); + } + else { + return Adapter.CommonLayout().Layout().checkBounds; + } + } + } + } + + internal override Rectangle DownChangeRectangle { + get { + if (Appearance == Appearance.Button || FlatStyle == FlatStyle.System) { + return base.DownChangeRectangle; + } + else { + return Adapter.CommonLayout().Layout().checkBounds; + } + } + } + + /// + [DefaultValue(false)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + /// + /// + /// Gets or sets the value indicating whether the user can give the focus to this + /// control using the TAB key. + /// + /// + /// + [ + Localizable(true), + DefaultValue(ContentAlignment.MiddleLeft) + ] + public override ContentAlignment TextAlign { + get { + return base.TextAlign; + } + set { + base.TextAlign = value; + } + } + + /// + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + /// + [SRDescription(SR.RadioButtonOnCheckedChangedDescr)] + public event EventHandler CheckedChanged { + add { + Events.AddHandler(EVENT_CHECKEDCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_CHECKEDCHANGED, value); + } + } + + /// + /// + /// + /// + /// Constructs the new instance of the accessibility object for this control. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new RadioButtonAccessibleObject(this); + } + + /// + /// + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + //Since this is protected override, this can be called directly in a overriden class + //and the handle doesn't need to be created. + //So check for the handle to improve performance + if (IsHandleCreated) { + SendMessage(NativeMethods.BM_SETCHECK, isChecked? 1: 0, 0); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnCheckedChanged(EventArgs e) { + AccessibilityNotifyClients(AccessibleEvents.StateChange, -1); + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + EventHandler handler = (EventHandler)Events[EVENT_CHECKEDCHANGED]; + if (handler != null) handler(this, e); + } + + /// + /// + /// We override this to implement the autoCheck functionality. + /// + /// + protected override void OnClick(EventArgs e) { + if (autoCheck) { + Checked = true; + } + base.OnClick(e); + } + + /// + /// + /// + /// + protected override void OnEnter(EventArgs e) { + // Just like the Win32 RadioButton, fire a click if the + // user arrows onto the control.. + // + if (MouseButtons == MouseButtons.None) { + if (UnsafeNativeMethods.GetKeyState((int)Keys.Tab) >= 0) { + //We enter the radioButton by using arrow keys + //Paint in raised state... + // + ResetFlagsandPaint(); + if(!ValidationCancelled){ + OnClick(e); + } + } + else { + //we enter the radioButton by pressing Tab + PerformAutoUpdates(true); + //reset the TabStop so we can come back later + //notice that PerformAutoUpdates will set the + //TabStop of this button to false + TabStop = true; + } + } + base.OnEnter(e); + } + + /// + /// + /// + /// + private void PerformAutoUpdates(bool tabbedInto) { + if (autoCheck) { + if (firstfocus) { + WipeTabStops(tabbedInto); + } + TabStop = isChecked; + if (isChecked) { + Control parent = ParentInternal; + if (parent != null) { + Control.ControlCollection children = parent.Controls; + for (int i = 0; i < children.Count; i++) { + Control ctl = children[i]; + if (ctl != this && ctl is RadioButton) { + RadioButton button = (RadioButton)ctl; + if (button.autoCheck && button.Checked) { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(this)["Checked"]; + propDesc.SetValue(button, false); + } + } + } + } + } + } + } + + /// + /// + /// Removes tabstops from all radio buttons, other than the one that currently has the focus. + /// + /// + private void WipeTabStops(bool tabbedInto) { + Control parent = ParentInternal; + if (parent != null) { + Control.ControlCollection children = parent.Controls; + for (int i = 0; i < children.Count; i++) { + Control ctl = children[i]; + if (ctl is RadioButton) { + RadioButton button = (RadioButton) ctl; + if (!tabbedInto) { + button.firstfocus = false; + } + if (button.autoCheck) { + button.TabStop = false; + } + } + } + } + } + + internal override ButtonBaseAdapter CreateFlatAdapter() { + return new RadioButtonFlatAdapter(this); + } + + internal override ButtonBaseAdapter CreatePopupAdapter() { + return new RadioButtonPopupAdapter(this); + } + + internal override ButtonBaseAdapter CreateStandardAdapter() { + return new RadioButtonStandardAdapter(this); + } + + private void OnAppearanceChanged(EventArgs e) { + EventHandler eh = Events[EVENT_APPEARANCECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + if (mevent.Button == MouseButtons.Left && GetStyle(ControlStyles.UserPaint)) { + if (base.MouseIsDown) { + Point pt = PointToScreen(new Point(mevent.X, mevent.Y)); + if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + //Paint in raised state... + // + ResetFlagsandPaint(); + if (!ValidationCancelled) { + OnClick(mevent); + OnMouseClick(mevent); + } + + } + } + } + base.OnMouseUp(mevent); + } + + /// + /// + /// + /// Generates a event for the + /// button, simulating a click by a user. + /// + /// + public void PerformClick() { + if (CanSelect) { + //Paint in raised state... + // + ResetFlagsandPaint(); + if (!ValidationCancelled) { + OnClick(EventArgs.Empty); + } + } + + } + + /// + /// + /// + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (UseMnemonic && IsMnemonic(charCode, Text) && CanSelect) { + if (!Focused) { + FocusInternal(); // This will cause an OnEnter event, which in turn will fire the click event + } + else { + PerformClick(); // Generate a click if already focused + } + return true; + } + return false; + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Checked: " + Checked.ToString(); + } + + /// + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class RadioButtonAccessibleObject : ButtonBaseAccessibleObject { + + /// + /// + /// [To be supplied.] + /// + public RadioButtonAccessibleObject(RadioButton owner) : base(owner) { + } + + /// + /// + /// [To be supplied.] + /// + public override string DefaultAction { + get { + string defaultAction = Owner.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + + return SR.GetString(SR.AccessibleActionCheck); + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.RadioButton; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleStates State { + get { + if (((RadioButton)Owner).Checked) { + return AccessibleStates.Checked | base.State; + } + return base.State; + } + } + + /// + /// + /// [To be supplied.] + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ((RadioButton)Owner).PerformClick(); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RadioButtonRenderer.cs b/WindowsForms/Managed/System/WinForms/RadioButtonRenderer.cs new file mode 100644 index 000000000..13d161ea6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RadioButtonRenderer.cs @@ -0,0 +1,298 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.Internal; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the RadioButton control. It works downlevel too (obviously + /// without visual styles applied.) + /// + /// + public sealed class RadioButtonRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement RadioElement = VisualStyleElement.Button.RadioButton.UncheckedNormal; + private static bool renderMatchingApplicationState = true; + + //cannot instantiate + private RadioButtonRenderer() { + } + + /// + /// + /// + /// If this property is true, then the renderer will use the setting from Application.RenderWithVisualStyles to + /// determine how to render. + /// If this property is false, the renderer will always render with visualstyles. + /// + /// + public static bool RenderMatchingApplicationState { + get { + return renderMatchingApplicationState; + } + set { + renderMatchingApplicationState = value; + } + } + + private static bool RenderWithVisualStyles { + get { + return (!renderMatchingApplicationState || Application.RenderWithVisualStyles); + } + } + + /// + /// + /// + /// Returns true if the background corresponding to the given state is partially transparent, else false. + /// + /// + public static bool IsBackgroundPartiallyTransparent(RadioButtonState state) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + return visualStyleRenderer.IsBackgroundPartiallyTransparent(); + } + else { + return false; //for downlevel, this is false + } + } + + /// + /// + /// + /// This is just a convenience wrapper for VisualStyleRenderer.DrawThemeParentBackground. For downlevel, + /// this isn't required and does nothing. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawParentBackground(Graphics g, Rectangle bounds, Control childControl) { + if (RenderWithVisualStyles) { + InitializeRenderer(0); + + visualStyleRenderer.DrawParentBackground(g, bounds, childControl); + } + } + + /// + /// + /// + /// Renders a RadioButton control. + /// + /// + public static void DrawRadioButton(Graphics g, Point glyphLocation, RadioButtonState state) { + DrawRadioButton(g, glyphLocation, state, IntPtr.Zero); + } + + internal static void DrawRadioButton(Graphics g, Point glyphLocation, RadioButtonState state, IntPtr hWnd) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state, hWnd)); + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, glyphBounds, hWnd); + } + else { + ControlPaint.DrawRadioButton(g, glyphBounds, ConvertToButtonState(state)); + } + } + + /// + /// + /// + /// Renders a RadioButton control. + /// + /// + public static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, bool focused, RadioButtonState state) { + DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + focused, state); + } + + /// + /// + /// + /// Renders a RadioButton control. + /// + /// + public static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, bool focused, RadioButtonState state) { + DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font, flags, focused, state, IntPtr.Zero); + } + + internal static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, bool focused, RadioButtonState state, IntPtr hWnd) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state, hWnd)); + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + visualStyleRenderer.DrawBackground(g, glyphBounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + ControlPaint.DrawRadioButton(g, glyphBounds, ConvertToButtonState(state)); + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, radioButtonText, font, textBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, textBounds); + } + } + + /// + /// + /// + /// Renders a RadioButton control. + /// + /// + public static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, Image image, Rectangle imageBounds, bool focused, RadioButtonState state) { + DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + image, imageBounds, focused, state); + } + + + + /// + /// + /// + /// Renders a RadioButton control. + /// + /// + public static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, RadioButtonState state) { + DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font, flags, image, imageBounds, focused, state, IntPtr.Zero); + } + + internal static void DrawRadioButton(Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, RadioButtonState state, IntPtr hWnd) { + Rectangle glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(g, state, hWnd)); + Color textColor; + + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + //Keep this drawing order! It matches default drawing order. + visualStyleRenderer.DrawImage(g, imageBounds, image); + visualStyleRenderer.DrawBackground(g, glyphBounds); + textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + } + else { + g.DrawImage(image, imageBounds); + ControlPaint.DrawRadioButton(g, glyphBounds, ConvertToButtonState(state)); + textColor = SystemColors.ControlText; + } + + TextRenderer.DrawText(g, radioButtonText, font, textBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, textBounds); + } + } + + /// + /// + /// + /// Returns the size of the RadioButton glyph. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static Size GetGlyphSize(Graphics g, RadioButtonState state) { + return GetGlyphSize(g, state, IntPtr.Zero); + } + + internal static Size GetGlyphSize(Graphics g, RadioButtonState state, IntPtr hWnd) { + if (RenderWithVisualStyles) { + InitializeRenderer((int)state); + + return visualStyleRenderer.GetPartSize(g, ThemeSizeType.Draw, hWnd); + } + + return new Size(13, 13); + } + + internal static ButtonState ConvertToButtonState(RadioButtonState state) { + switch (state) { + case RadioButtonState.CheckedNormal: + case RadioButtonState.CheckedHot: + return ButtonState.Checked; + case RadioButtonState.CheckedPressed: + return (ButtonState.Checked | ButtonState.Pushed); + case RadioButtonState.CheckedDisabled: + return (ButtonState.Checked | ButtonState.Inactive); + + case RadioButtonState.UncheckedPressed: + return ButtonState.Pushed; + case RadioButtonState.UncheckedDisabled: + return ButtonState.Inactive; + + default: + return ButtonState.Normal; + } + } + + internal static RadioButtonState ConvertFromButtonState(ButtonState state, bool isHot) { + if ((state & ButtonState.Checked) == ButtonState.Checked) { + if ((state & ButtonState.Pushed) == ButtonState.Pushed) { + return RadioButtonState.CheckedPressed; + } + else if ((state & ButtonState.Inactive) == ButtonState.Inactive) { + return RadioButtonState.CheckedDisabled; + } + else if (isHot) { + return RadioButtonState.CheckedHot; + } + + return RadioButtonState.CheckedNormal; + } + else { //unchecked + if ((state & ButtonState.Pushed) == ButtonState.Pushed) { + return RadioButtonState.UncheckedPressed; + } + else if ((state & ButtonState.Inactive) == ButtonState.Inactive) { + return RadioButtonState.UncheckedDisabled; + } + else if (isHot) { + return RadioButtonState.UncheckedHot; + } + + return RadioButtonState.UncheckedNormal; + } + } + + private static void InitializeRenderer(int state) { + RadioButtonState radioButtonState = (RadioButtonState)state; + int part = RadioElement.Part; + if (AccessibilityImprovements.Level2 + && SystemInformation.HighContrast + && (radioButtonState == RadioButtonState.CheckedDisabled || radioButtonState == RadioButtonState.UncheckedDisabled) + && VisualStyleRenderer.IsCombinationDefined(RadioElement.ClassName, VisualStyleElement.Button.RadioButton.HighContrastDisabledPart)) { + part = VisualStyleElement.Button.RadioButton.HighContrastDisabledPart; + } + + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(RadioElement.ClassName, part, state); + } + else { + visualStyleRenderer.SetParameters(RadioElement.ClassName, part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/RelatedCurrencyManager.cs b/WindowsForms/Managed/System/WinForms/RelatedCurrencyManager.cs new file mode 100644 index 000000000..117812719 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RelatedCurrencyManager.cs @@ -0,0 +1,186 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using System.Collections; + using System.Collections.Generic; + + /// + /// + /// Represents the child version of the System.Windows.Forms.ListManager + /// that is used when a parent/child relationship exists in a System.Windows.Forms.DataSet. + /// + internal class RelatedCurrencyManager : CurrencyManager { + + BindingManagerBase parentManager; + string dataField; + PropertyDescriptor fieldInfo; + static List IgnoreItemChangedTable = new List(); + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the dataSource + // it would be a breaking change. + ] + internal RelatedCurrencyManager(BindingManagerBase parentManager, string dataField) : base(null) { + Bind(parentManager, dataField); + } + + internal void Bind(BindingManagerBase parentManager, string dataField) { + Debug.Assert(parentManager != null, "How could this be a null parentManager."); + + // Unwire previous BindingManagerBase + UnwireParentManager(this.parentManager); + + this.parentManager = parentManager; + this.dataField = dataField; + this.fieldInfo = parentManager.GetItemProperties().Find(dataField, true); + if (fieldInfo == null || !typeof(IList).IsAssignableFrom(fieldInfo.PropertyType)) { + throw new ArgumentException(SR.GetString(SR.RelatedListManagerChild, dataField)); + } + this.finalType = fieldInfo.PropertyType; + + // Wire new BindingManagerBase + WireParentManager(this.parentManager); + + ParentManager_CurrentItemChanged(parentManager, EventArgs.Empty); + } + + private void UnwireParentManager(BindingManagerBase bmb) { + if (bmb != null) { + bmb.CurrentItemChanged -= new EventHandler(ParentManager_CurrentItemChanged); + + if (bmb is CurrencyManager) { + (bmb as CurrencyManager).MetaDataChanged -= new EventHandler(ParentManager_MetaDataChanged); + } + } + } + + private void WireParentManager(BindingManagerBase bmb) { + if (bmb != null) { + bmb.CurrentItemChanged += new EventHandler(ParentManager_CurrentItemChanged); + + if (bmb is CurrencyManager) { + (bmb as CurrencyManager).MetaDataChanged += new EventHandler(ParentManager_MetaDataChanged); + } + } + } + + internal override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { + PropertyDescriptor[] accessors; + + if (listAccessors != null && listAccessors.Length > 0) { + accessors = new PropertyDescriptor[listAccessors.Length + 1]; + listAccessors.CopyTo(accessors, 1); + } + else { + accessors = new PropertyDescriptor[1]; + } + + // Set this accessor (add to the beginning) + accessors[0] = this.fieldInfo; + + // Get props + return parentManager.GetItemProperties(accessors); + } + + /// + /// + /// Gets the properties of the item. + /// + public override PropertyDescriptorCollection GetItemProperties() { + return GetItemProperties(null); + } + + // + // Gets the name of the list. + // + internal override string GetListName() { + string name = GetListName(new ArrayList()); + if (name.Length > 0) { + return name; + } + return base.GetListName(); + } + + /// + /// + /// Gets the name of the specified list. + /// + protected internal override string GetListName(ArrayList listAccessors) { + listAccessors.Insert(0, fieldInfo); + return parentManager.GetListName(listAccessors); + } + + private void ParentManager_MetaDataChanged(object sender, EventArgs e) { + // Propagate MetaDataChanged events from the parent manager + base.OnMetaDataChanged(e); + } + + private void ParentManager_CurrentItemChanged(object sender, EventArgs e) { + if (IgnoreItemChangedTable.Contains(parentManager)) { + return; + } + + int oldlistposition = this.listposition; + + // we only pull the data from the controls into the backEnd. we do not care about keeping the lastGoodKnownRow + // when we are about to change the entire list in this currencymanager. + try { + PullData(); + } + catch (Exception ex) { + OnDataError(ex); + } + + if (parentManager is CurrencyManager) { + CurrencyManager curManager = (CurrencyManager) parentManager; + if (curManager.Count > 0) { + // Parent list has a current row, so get the related list from the relevant property on that row. + SetDataSource(fieldInfo.GetValue(curManager.Current)); + listposition = (Count > 0 ? 0 : -1); + } else { + // APPCOMPAT: bring back the Everett behavior where the currency manager adds an item and + // then it cancels the addition. + // vsw 427731. + // + // really, really hocky. + // will throw if the list in the curManager is not IBindingList + // and this will fail if the IBindingList does not have list change notification. read on.... + // when a new item will get added to an empty parent table, + // the table will fire OnCurrentChanged and this method will get executed again + // allowing us to set the data source to an object with the right properties (so we can show + // metadata at design time). + // we then call CancelCurrentEdit to remove the dummy row, but making sure to ignore any + // OnCurrentItemChanged that results from this action (to avoid infinite recursion) + curManager.AddNew(); + try { + IgnoreItemChangedTable.Add(curManager); + curManager.CancelCurrentEdit(); + } finally { + if (IgnoreItemChangedTable.Contains(curManager)) { + IgnoreItemChangedTable.Remove(curManager); + } + } + } + } else { + // Case where the parent is not a list, but a single object + SetDataSource(fieldInfo.GetValue(parentManager.Current)); + listposition = (Count > 0 ? 0 : -1); + } + if (oldlistposition != listposition) + OnPositionChanged(EventArgs.Empty); + OnCurrentChanged(EventArgs.Empty); + OnCurrentItemChanged(EventArgs.Empty); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RelatedImageListAttribute.cs b/WindowsForms/Managed/System/WinForms/RelatedImageListAttribute.cs new file mode 100644 index 000000000..b220e4399 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RelatedImageListAttribute.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// Specifies which imagelist a property relates to. For example ImageListIndex must relate to a + /// specific ImageList property + /// + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)] + public sealed class RelatedImageListAttribute : Attribute { + private string relatedImageList=null; + + public RelatedImageListAttribute(string relatedImageList) { + this.relatedImageList = relatedImageList; + } + + public string RelatedImageList { + get { + return relatedImageList; + } + } + } // end of RelatedImageListAttribute class +} // end of namespace diff --git a/WindowsForms/Managed/System/WinForms/RelatedPropertyManager.cs b/WindowsForms/Managed/System/WinForms/RelatedPropertyManager.cs new file mode 100644 index 000000000..64ed9b708 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RelatedPropertyManager.cs @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using Microsoft.Win32; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using System.Collections; + + internal class RelatedPropertyManager : PropertyManager { + + BindingManagerBase parentManager; + string dataField; + PropertyDescriptor fieldInfo; + + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // If the constructor does not set the dataSource + // it would be a breaking change. + ] + internal RelatedPropertyManager(BindingManagerBase parentManager, string dataField) : base(GetCurrentOrNull(parentManager), dataField) { + Bind(parentManager, dataField); + } + + private void Bind(BindingManagerBase parentManager, string dataField) { + Debug.Assert(parentManager != null, "How could this be a null parentManager."); + this.parentManager = parentManager; + this.dataField = dataField; + this.fieldInfo = parentManager.GetItemProperties().Find(dataField, true); + if (fieldInfo == null) + throw new ArgumentException(SR.GetString(SR.RelatedListManagerChild, dataField)); + // this.finalType = fieldInfo.PropertyType; + parentManager.CurrentItemChanged += new EventHandler(ParentManager_CurrentItemChanged); + Refresh(); + } + + internal override string GetListName() { + string name = GetListName(new ArrayList()); + if (name.Length > 0) { + return name; + } + return base.GetListName(); + } + + protected internal override string GetListName(ArrayList listAccessors) { + listAccessors.Insert(0, fieldInfo); + return parentManager.GetListName(listAccessors); + } + + internal override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { + PropertyDescriptor[] accessors; + + if (listAccessors != null && listAccessors.Length > 0) { + accessors = new PropertyDescriptor[listAccessors.Length + 1]; + listAccessors.CopyTo(accessors, 1); + } + else { + accessors = new PropertyDescriptor[1]; + } + + // Set this accessor (add to the beginning) + accessors[0] = this.fieldInfo; + + // Get props + return parentManager.GetItemProperties(accessors); + } + + private void ParentManager_CurrentItemChanged(object sender, EventArgs e) { + Refresh(); + } + + private void Refresh() { + EndCurrentEdit(); + SetDataSource(GetCurrentOrNull(parentManager)); + OnCurrentChanged(EventArgs.Empty); + } + + + internal override Type BindType { + get { + return fieldInfo.PropertyType; + } + } + + public override object Current { + get { + return (this.DataSource != null) ? fieldInfo.GetValue(this.DataSource) : null; + } + } + + static private object GetCurrentOrNull(BindingManagerBase parentManager) { + bool anyCurrent = (parentManager.Position >= 0 && parentManager.Position < parentManager.Count); + return anyCurrent ? parentManager.Current : null; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RequestResizeEvent.cs b/WindowsForms/Managed/System/WinForms/RequestResizeEvent.cs new file mode 100644 index 000000000..630492b5c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RequestResizeEvent.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class ContentsResizedEventArgs : EventArgs { + readonly Rectangle newRectangle; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + + public ContentsResizedEventArgs(Rectangle newRectangle) { + this.newRectangle = newRectangle; + } + + /// + /// + /// + /// Represents the requested size of the control. + /// + /// + public Rectangle NewRectangle { + get { + return newRectangle; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/RequestResizeEventHandler.cs b/WindowsForms/Managed/System/WinForms/RequestResizeEventHandler.cs new file mode 100644 index 000000000..f5f202b7c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RequestResizeEventHandler.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// Represents the method that will handle the event of + /// a . + /// + public delegate void ContentsResizedEventHandler(object sender, ContentsResizedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventArgs.cs b/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventArgs.cs new file mode 100644 index 000000000..0800ccde6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventArgs.cs @@ -0,0 +1,32 @@ +using System; +using System.Windows.Forms; + +namespace System.Windows.Forms { + /// + public class RetrieveVirtualItemEventArgs : EventArgs { + private int itemIndex; + private ListViewItem item; + + /// + public RetrieveVirtualItemEventArgs(int itemIndex) { + this.itemIndex = itemIndex; + } + + /// + public int ItemIndex { + get { + return itemIndex; + } + } + + /// + public ListViewItem Item { + get { + return item; + } + set { + this.item = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventHandler.cs new file mode 100644 index 000000000..3ff45ab8e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RetrieveVirtualItemEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// Represents the method that will handle the RetrieveVirtualItem event of a ListView. + /// + /// + public delegate void RetrieveVirtualItemEventHandler(object sender, RetrieveVirtualItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBox.cs b/WindowsForms/Managed/System/WinForms/RichTextBox.cs new file mode 100644 index 000000000..d52a04b79 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBox.cs @@ -0,0 +1,4064 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using Microsoft.Win32; + using System; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.IO; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Serialization.Formatters; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Windows.Forms.ComponentModel; + using System.Windows.Forms.Design; + using System.Windows.Forms.Layout; + using System.Runtime.Versioning; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using Util = NativeMethods.Util; + + // + + + + + + + /// + /// + /// Rich Text control. The RichTextBox is a control that contains formatted text. + /// It supports font selection, boldface, and other type attributes. + /// + + [ClassInterface(ClassInterfaceType.AutoDispatch), + ComVisible(true), + Docking(DockingBehavior.Ask), + Designer("System.Windows.Forms.Design.RichTextBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionRichTextBox) + ] + public class RichTextBox : TextBoxBase { + static TraceSwitch richTextDbg; + static TraceSwitch RichTextDbg { + get { + if (richTextDbg == null) { + richTextDbg = new TraceSwitch("RichTextDbg", "Debug info about RichTextBox"); + } + return richTextDbg; + } + } + + /// + /// + /// Paste special flags. + /// + private const int DV_E_DVASPECT = unchecked((int)0x8004006B); + private const int DVASPECT_CONTENT = 1; + private const int DVASPECT_THUMBNAIL = 2; + private const int DVASPECT_ICON = 4; + private const int DVASPECT_DOCPRINT = 8; + + internal const int INPUT = 0x0001; + internal const int OUTPUT = 0x0002; + internal const int DIRECTIONMASK = INPUT | OUTPUT; + internal const int ANSI = 0x0004; + internal const int UNICODE = 0x0008; + internal const int FORMATMASK = ANSI | UNICODE; + internal const int TEXTLF = 0x0010; + internal const int TEXTCRLF = 0x0020; + internal const int RTF = 0x0040; + internal const int KINDMASK = TEXTLF | TEXTCRLF | RTF; + + // This is where we store the reched library. + private static IntPtr moduleHandle; + + private static readonly string SZ_RTF_TAG = "{\\rtf"; + private const int CHAR_BUFFER_LEN = 512; + + // Event objects + // + private static readonly object EVENT_HSCROLL = new object(); + private static readonly object EVENT_LINKACTIVATE = new object(); + private static readonly object EVENT_IMECHANGE = new object(); + private static readonly object EVENT_PROTECTED = new object(); + private static readonly object EVENT_REQUESTRESIZE = new object(); + private static readonly object EVENT_SELCHANGE = new object(); + private static readonly object EVENT_VSCROLL = new object(); + + + // Persistent state + // + private int bulletIndent; + private int rightMargin; + private string textRtf; // If not null, takes precedence over cached Text value + private string textPlain; + private Color selectionBackColorToSetOnHandleCreated; + RichTextBoxLanguageOptions languageOption = RichTextBoxLanguageOptions.AutoFont | RichTextBoxLanguageOptions.DualFont; + + // Non-persistent state + // + static int logPixelsX; + static int logPixelsY; + Stream editStream = null; + float zoomMultiplier = 1.0f; + + // used to decide when to fire the selectionChange event. + private int curSelStart; + private int curSelEnd; + private short curSelType; + object oleCallback; + + private static int[] shortcutsToDisable; + private static int richEditMajorVersion = 3; //Assume version 3: it'll only be version 2 on Win98, and we don't yet load version 4. + + private BitVector32 richTextBoxFlags = new BitVector32(); + private static readonly BitVector32.Section autoWordSelectionSection = BitVector32.CreateSection(1); + private static readonly BitVector32.Section showSelBarSection = BitVector32.CreateSection(1, autoWordSelectionSection); + private static readonly BitVector32.Section autoUrlDetectSection = BitVector32.CreateSection(1, showSelBarSection); + private static readonly BitVector32.Section fInCtorSection = BitVector32.CreateSection(1, autoUrlDetectSection); + private static readonly BitVector32.Section protectedErrorSection = BitVector32.CreateSection(1, fInCtorSection); + private static readonly BitVector32.Section linkcursorSection = BitVector32.CreateSection(1, protectedErrorSection); + private static readonly BitVector32.Section allowOleDropSection = BitVector32.CreateSection(1, linkcursorSection); + private static readonly BitVector32.Section suppressTextChangedEventSection = BitVector32.CreateSection(1, allowOleDropSection); + private static readonly BitVector32.Section callOnContentsResizedSection = BitVector32.CreateSection(1, suppressTextChangedEventSection); + private static readonly BitVector32.Section richTextShortcutsEnabledSection = BitVector32.CreateSection(1, callOnContentsResizedSection); + private static readonly BitVector32.Section allowOleObjectsSection = BitVector32.CreateSection(1, richTextShortcutsEnabledSection); + private static readonly BitVector32.Section scrollBarsSection = BitVector32.CreateSection((short) RichTextBoxScrollBars.ForcedBoth, allowOleObjectsSection); + private static readonly BitVector32.Section enableAutoDragDropSection = BitVector32.CreateSection(1, scrollBarsSection); + + /// + /// + /// Constructs a new RichTextBox. + /// + public RichTextBox() { + InConstructor = true; + richTextBoxFlags[autoWordSelectionSection] = 0;// This is false by default + DetectUrls = true; + ScrollBars = RichTextBoxScrollBars.Both; + RichTextShortcutsEnabled = true; + MaxLength = int.MaxValue; + Multiline = true; + AutoSize = false; + curSelStart = curSelEnd = curSelType = -1; + InConstructor = false; + } + + /// + /// + /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop + /// may still be used: this should be hidden in the property grid, but not in code + /// + [Browsable(false)] + public override bool AllowDrop { + get { + return richTextBoxFlags[allowOleDropSection] != 0; + } + set { + if (value) { + try + { + IntSecurity.ClipboardRead.Demand(); + } + catch (Exception e) + { + throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); + } + } + richTextBoxFlags[allowOleDropSection] = value ? 1 : 0; + UpdateOleCallback(); + } + } + + internal bool AllowOleObjects { + get { + return richTextBoxFlags[allowOleObjectsSection] != 0; + } + set { + richTextBoxFlags[allowOleObjectsSection] = value ? 1 : 0; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the size + /// of the control automatically adjusts when the font assigned to the control + /// is changed. + /// + /// Note: this works differently than other Controls' AutoSize, so we're hiding + /// it to avoid confusion. + /// + /// + [ + DefaultValue(false), + RefreshProperties(RefreshProperties.Repaint), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + base.AutoSize = value; + } + } + + /// + /// + /// Controls whether whether mouse selection snaps to whole words. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.RichTextBoxAutoWordSelection) + ] + public bool AutoWordSelection { + get { return richTextBoxFlags[autoWordSelectionSection] != 0; } + set { + richTextBoxFlags[autoWordSelectionSection] = value ? 1 : 0; + if (IsHandleCreated) { + SendMessage(RichTextBoxConstants.EM_SETOPTIONS, + value ? RichTextBoxConstants.ECOOP_OR : RichTextBoxConstants.ECOOP_XOR, + RichTextBoxConstants.ECO_AUTOWORDSELECTION); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Returns the amount of indent used in a RichTextBox control when + /// SelectionBullet is set to true. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.RichTextBoxBulletIndent) + ] + public int BulletIndent { + get { + return bulletIndent; + } + + set { + + if (value < 0) { + throw new ArgumentOutOfRangeException("BulletIndent", SR.GetString(SR.InvalidArgument, "BulletIndent", (value).ToString(CultureInfo.CurrentCulture))); + } + + this.bulletIndent = value; + + // Call to update the control only if the bullet is set. + if (IsHandleCreated && SelectionBullet) + SelectionBullet = true; + } + } + + private bool CallOnContentsResized { + get { return richTextBoxFlags[callOnContentsResizedSection] != 0; } + set { richTextBoxFlags[callOnContentsResizedSection] = value ? 1 : 0; } + } + + internal override bool CanRaiseTextChangedEvent { + get { + return !SuppressTextChangedEvent; + } + } + + /// + /// + /// Whether or not there are actions that can be Redone on the RichTextBox control. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxCanRedoDescr) + ] + public bool CanRedo { + get { + if (IsHandleCreated) { + bool b; + b = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_CANREDO, 0, 0)) != 0; + + return b; + } + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + // Check for library + if (moduleHandle == IntPtr.Zero) { + string richEditControlDllVersion = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.DLL_RICHEDIT : RichTextBoxConstants.DLL_RICHEDIT_41; + moduleHandle = UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(richEditControlDllVersion); + + int lastWin32Error = Marshal.GetLastWin32Error(); + + // This code has been here since the inception of the project, + // we can’t determine why we have to compare w/ 32 here. + // This fails on 3-GB mode, (once the dll is loaded above 3GB memory space) (see Dev10 bug#732388) + if ((ulong)moduleHandle < (ulong)32) { + throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError, richEditControlDllVersion)); + } + + //Determine whether we're Rich Edit 2.0 or 3.0: see + //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp + + StringBuilder pathBuilder = UnsafeNativeMethods.GetModuleFileNameLongPath(new HandleRef(null, moduleHandle)); + string path = pathBuilder.ToString(); + new FileIOPermission(FileIOPermissionAccess.Read, path).Assert(); + FileVersionInfo versionInfo; + try { + versionInfo = FileVersionInfo.GetVersionInfo(path); + } + finally { + CodeAccessPermission.RevertAssert(); + } + Debug.Assert(versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion), "Couldn't get the version info for the richedit dll"); + if (versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion)) { + //Note: this only allows for one digit version + int parsedValue; + if (int.TryParse(versionInfo.ProductVersion[0].ToString(), out parsedValue)) { + richEditMajorVersion = parsedValue; + } + } + } + + CreateParams cp = base.CreateParams; + if (Marshal.SystemDefaultCharSize == 1) { + cp.ClassName = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.WC_RICHEDITA : RichTextBoxConstants.WC_RICHEDITA_41; + } + else { + cp.ClassName = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.WC_RICHEDITW : RichTextBoxConstants.WC_RICHEDITW_41; + } + + if (Multiline) { + if (((int)ScrollBars & RichTextBoxConstants.RTB_HORIZ) != 0 && !WordWrap) { + // RichEd infers word wrap from the absence of horizontal scroll bars + cp.Style |= NativeMethods.WS_HSCROLL; + if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) + cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; + } + + if (((int)ScrollBars & RichTextBoxConstants.RTB_VERT) != 0) { + cp.Style |= NativeMethods.WS_VSCROLL; + if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) + cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; + } + } + + // VSWhidbey 94843: Remove the WS_BORDER style from the control, if we're trying to set it, + // to prevent the control from displaying the single point rectangle around the 3D border + if (BorderStyle.FixedSingle == BorderStyle && ((cp.Style & NativeMethods.WS_BORDER) != 0)) { + cp.Style &= (~NativeMethods.WS_BORDER); + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + } + + return cp; + } + } + + // public bool CanUndo {}; <-- inherited from TextBoxBase + + /// + /// + /// Controls whether or not the rich edit control will automatically highlight URLs. + /// By default, this is true. Note that changing this property will not update text that is + /// already present in the RichTextBox control; it only affects text which is entered after the + /// property is changed. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.RichTextBoxDetectURLs) + ] + public bool DetectUrls { + get { + return richTextBoxFlags[autoUrlDetectSection] != 0; + } + set { + if (value != DetectUrls) { + richTextBoxFlags[autoUrlDetectSection] = value ? 1 : 0; + if (IsHandleCreated) { + this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, value ? 1 : 0, 0); + RecreateHandle(); + } + } + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(100, 96); + } + } + + /// + /// + /// We can't just enable drag/drop of text by default: it's a breaking change (VSWhidbey + /// 375177). Should be false by default. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.RichTextBoxEnableAutoDragDrop) + ] + public bool EnableAutoDragDrop + { + get + { + return richTextBoxFlags[enableAutoDragDropSection] != 0; + } + set + { + if (value) + { + try + { + IntSecurity.ClipboardRead.Demand(); + } + catch (Exception e) + { + throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); + } + } + richTextBoxFlags[enableAutoDragDropSection] = value ? 1 : 0; + UpdateOleCallback(); + } + } + + /// + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + if (IsHandleCreated) { + if (InternalSetForeColor(value)) { + base.ForeColor = value; + } + } + else { + base.ForeColor = value; + } + } + } + + /// + public override Font Font { + get { + return base.Font; + } + set { + if (IsHandleCreated) + { + if (SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)) > 0) { + if (value == null) { + base.Font = null; + SetCharFormatFont(false, Font); + } + else { + try{ + Font f = GetCharFormatFont(false); + if (f == null || !f.Equals (value)) { + SetCharFormatFont(false, value); + // update controlfont from "resolved" font from the attempt + // to set the document font... + // + CallOnContentsResized = true; + base.Font = GetCharFormatFont(false); + } + } + finally{ + CallOnContentsResized = false; + } + } + } + else { + base.Font = value; + } + } + else { + base.Font = value; + } + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + Size scrollBarPadding = Size.Empty; + + //If the RTB is multiline, we won't have a horizontal scrollbar. + if (!WordWrap && Multiline && (ScrollBars & RichTextBoxScrollBars.Horizontal) != 0) { + scrollBarPadding.Height += SystemInformation.HorizontalScrollBarHeight; + } + if (Multiline && (ScrollBars & RichTextBoxScrollBars.Vertical) != 0) { + scrollBarPadding.Width += SystemInformation.VerticalScrollBarWidth; + } + + // Subtract the scroll bar padding before measuring + proposedConstraints -= scrollBarPadding; + + Size prefSize = base.GetPreferredSizeCore(proposedConstraints); + + return prefSize + scrollBarPadding; + } + + private bool InConstructor { + get { return richTextBoxFlags[fInCtorSection] != 0; } + set { richTextBoxFlags[fInCtorSection] = value ? 1 : 0; } + } + + /// + /// Sets or gets the rich text box control' language option. + /// The IMF_AUTOFONT flag is set by default. + /// The IMF_AUTOKEYBOARD and IMF_IMECANCELCOMPLETE flags are cleared by default. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public RichTextBoxLanguageOptions LanguageOption + { + get + { + RichTextBoxLanguageOptions opt; + if (IsHandleCreated) { + opt = (RichTextBoxLanguageOptions)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETLANGOPTIONS, 0, 0); + } + else { + opt = languageOption; + } + return opt; + } + set + { + if (this.LanguageOption != value) + { + this.languageOption = value; + if (IsHandleCreated) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETLANGOPTIONS, 0, (int) value); + } + } + } + } + + private bool LinkCursor { + get { return richTextBoxFlags[linkcursorSection] != 0; } + set { richTextBoxFlags[linkcursorSection] = value ? 1 : 0; } + } + + /// + [ + DefaultValue(int.MaxValue), + ] + public override int MaxLength { + get { + return base.MaxLength; + } + set { + base.MaxLength = value; + } + } + /// + /// + /// [To be supplied.] + /// + [DefaultValue(true)] + public override bool Multiline { + get { + return base.Multiline; + } + set { + base.Multiline = value; + } + } + + private bool ProtectedError { + get { return richTextBoxFlags[protectedErrorSection] != 0; } + set { richTextBoxFlags[protectedErrorSection] = value ? 1 : 0; } + } + + /// + /// + /// Returns the name of the action that will be performed if the user + /// Redo's their last Undone operation. If no operation can be redone, + /// an empty string ("") is returned. + /// + //NOTE: This is overridable, because we want people to be able to + // mess with the names if necessary...? + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxRedoActionNameDescr) + ] + public string RedoActionName { + get { + if (!CanRedo) return ""; + int n; + n = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_GETREDONAME, 0, 0)); + return GetEditorActionName(n); + } + } + + /// + //Description: Specifies whether rich text formatting keyboard shortcuts are enabled. + [ + DefaultValue(true), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public bool RichTextShortcutsEnabled { + get { return richTextBoxFlags[richTextShortcutsEnabledSection] != 0; } + set { + if (shortcutsToDisable == null) { + shortcutsToDisable = new int[] {(int)Shortcut.CtrlL, (int)Shortcut.CtrlR, (int)Shortcut.CtrlE, (int)Shortcut.CtrlJ}; + } + richTextBoxFlags[richTextShortcutsEnabledSection] = value ? 1 : 0; + } + } + + /// + /// + /// The right margin of a RichTextBox control. A nonzero margin implies WordWrap. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.RichTextBoxRightMargin) + ] + public int RightMargin { + get { + return rightMargin; + } + set { + if (this.rightMargin != value) { + if (value < 0) + throw new ArgumentOutOfRangeException("RightMargin", SR.GetString(SR.InvalidLowBoundArgumentEx, "RightMargin", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + this.rightMargin = value; + + if (value == 0) { + // Once you set EM_SETTARGETDEVICE to something nonzero, RichEd will assume + // word wrap forever and ever. + RecreateHandle(); + } + else if (IsHandleCreated) { + IntPtr hDC = UnsafeNativeMethods.CreateIC("DISPLAY", null, null, new HandleRef(null, IntPtr.Zero)); + try { + SendMessage(RichTextBoxConstants.EM_SETTARGETDEVICE, hDC, (IntPtr)Pixel2Twip(hDC, value, true)); + } + finally { + if (hDC != IntPtr.Zero) { + UnsafeNativeMethods.DeleteDC(new HandleRef(null, hDC)); + } + } + } + } + } + } + + /// + /// + /// The text of a RichTextBox control, including all Rtf codes. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxRTF), + RefreshProperties(RefreshProperties.All) + ] + public string Rtf { + get { + if (IsHandleCreated) + { + return StreamOut(RichTextBoxConstants.SF_RTF); + } + else if (textPlain != null) + { + ForceHandleCreate(); + return StreamOut(RichTextBoxConstants.SF_RTF); + } + else + { + return textRtf; + } + } + set { + if (value == null) value = ""; + + if (value.Equals(Rtf)) + return; + + ForceHandleCreate(); + textRtf = value; + StreamIn(value, RichTextBoxConstants.SF_RTF); + if (CanRaiseTextChangedEvent) { + OnTextChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// The current scrollbar settings for a multi-line rich edit control. + /// Possible return values are given by the RichTextBoxScrollBars enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(RichTextBoxScrollBars.Both), + Localizable(true), + SRDescription(SR.RichTextBoxScrollBars) + ] + public RichTextBoxScrollBars ScrollBars { + get { + return (RichTextBoxScrollBars) richTextBoxFlags[scrollBarsSection]; + } + set { + // we could be more clever here, but it doesnt seem like this would get set enough + // to warrant a clever bitmask. + if (!ClientUtils.IsEnumValid_NotSequential(value, + (int)value, + (int)RichTextBoxScrollBars.Both, + (int)RichTextBoxScrollBars.None, + (int)RichTextBoxScrollBars.Horizontal, + (int)RichTextBoxScrollBars.Vertical, + (int)RichTextBoxScrollBars.ForcedHorizontal, + (int)RichTextBoxScrollBars.ForcedVertical, + (int)RichTextBoxScrollBars.ForcedBoth)) { + + throw new InvalidEnumArgumentException("value", (int)value, typeof(RichTextBoxScrollBars)); + } + + if (value != ScrollBars) { + using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.ScrollBars)) { + richTextBoxFlags[scrollBarsSection] = (int) value; + RecreateHandle(); + } + } + } + } + + /// + /// + /// The alignment of the paragraphs in a RichTextBox control. + /// + [ + DefaultValue(HorizontalAlignment.Left), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelAlignment) + ] + public HorizontalAlignment SelectionAlignment { + get { + HorizontalAlignment selectionAlignment = HorizontalAlignment.Left; + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_ALIGNMENT & pf.dwMask) != 0) { + switch (pf.wAlignment) { + case RichTextBoxConstants.PFA_LEFT: + selectionAlignment = HorizontalAlignment.Left; + break; + + case RichTextBoxConstants.PFA_RIGHT: + selectionAlignment = HorizontalAlignment.Right; + break; + + case RichTextBoxConstants.PFA_CENTER: + selectionAlignment = HorizontalAlignment.Center; + break; + } + } + + return selectionAlignment; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.dwMask = RichTextBoxConstants.PFM_ALIGNMENT; + switch (value) { + + case HorizontalAlignment.Left: + pf.wAlignment = RichTextBoxConstants.PFA_LEFT; + break; + + case HorizontalAlignment.Right: + pf.wAlignment = RichTextBoxConstants.PFA_RIGHT; + break; + + case HorizontalAlignment.Center: + pf.wAlignment = RichTextBoxConstants.PFA_CENTER; + break; + } + + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// Determines if a paragraph in the RichTextBox control + /// contains the current selection or insertion point has the bullet style. + /// + [ + DefaultValue(false), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelBullet) + ] + public bool SelectionBullet { + get { + RichTextBoxSelectionAttribute selectionBullet = RichTextBoxSelectionAttribute.None; + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_NUMBERING & pf.dwMask) != 0) { + if (RichTextBoxConstants.PFN_BULLET == pf.wNumbering) { + selectionBullet = RichTextBoxSelectionAttribute.All; + } + } + else { + // For paragraphs with mixed SelectionBullets, we just return false + return false; + } + + return selectionBullet == RichTextBoxSelectionAttribute.All; + } + set { + ForceHandleCreate(); + + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.dwMask = RichTextBoxConstants.PFM_NUMBERING | RichTextBoxConstants.PFM_OFFSET; + + if (!value) { + pf.wNumbering = 0; + pf.dxOffset = 0; + } + else { + pf.wNumbering = RichTextBoxConstants.PFN_BULLET; + pf.dxOffset = Pixel2Twip(IntPtr.Zero, bulletIndent, true); + } + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// Determines whether text in the RichTextBox control + /// appears on the baseline (normal), as a superscript above the baseline, + /// or as a subscript below the baseline. + /// + [ + DefaultValue(0), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelCharOffset) + ] + public int SelectionCharOffset { + get { + int selCharOffset = 0; + + ForceHandleCreate(); + NativeMethods.CHARFORMATA cf = GetCharFormat(true); + // if the effects member contains valid info + if ((cf.dwMask & RichTextBoxConstants.CFM_OFFSET) != 0) { + selCharOffset = cf.yOffset; + } + else { + // The selection contains characters of different offsets, + // so we just return the offset of the first character. + selCharOffset = cf.yOffset; + } + + return Twip2Pixel(IntPtr.Zero,selCharOffset,false); + } + set { + if (value > 2000 || value < -2000) + throw new ArgumentOutOfRangeException("SelectionCharOffset", SR.GetString(SR.InvalidBoundArgument, "SelectionCharOffset", value, -2000, 2000)); + + ForceHandleCreate(); + NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); + cf.dwMask = RichTextBoxConstants.CFM_OFFSET; + cf.yOffset = Pixel2Twip(IntPtr.Zero, value, false); + + // Set the format information + // SendMessage will force the handle to be created if it hasn't already. Normally, + // we would cache property values until the handle is created - but for this property, + // it's far more simple to just create the handle. + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); + } + } + + /// + /// + /// The color of the currently selected text in the + /// RichTextBox control. + /// Returns Color.Empty if the selection has more than one color. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelColor) + ] + public Color SelectionColor { + get { + Color selColor = Color.Empty; + + ForceHandleCreate(); + NativeMethods.CHARFORMATA cf = GetCharFormat(true); + // if the effects member contains valid info + if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0) + selColor = ColorTranslator.FromOle(cf.crTextColor); + + return selColor; + } + set { + ForceHandleCreate(); + NativeMethods.CHARFORMATA cf = GetCharFormat(true); + cf.dwMask = RichTextBoxConstants.CFM_COLOR; + cf.dwEffects = 0; + cf.crTextColor = ColorTranslator.ToWin32(value); + + // set the format information + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); + } + } + + /// + /// + /// The background color of the currently selected text in the RichTextBox control. + /// Returns Color.Empty if the selection has more than one color. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelBackColor) + ] + public Color SelectionBackColor { + get { + Color selColor = Color.Empty; + + if (IsHandleCreated) { + NativeMethods.CHARFORMAT2A cf2 = GetCharFormat2(true); + // If the effects member contains valid info + if ((cf2.dwEffects & RichTextBoxConstants.CFE_AUTOBACKCOLOR) != 0) { + selColor = this.BackColor; + } + else if ((cf2.dwMask & RichTextBoxConstants.CFM_BACKCOLOR) != 0) { + selColor = ColorTranslator.FromOle(cf2.crBackColor); + } + } + else { + selColor = selectionBackColorToSetOnHandleCreated; + } + return selColor; + } + set + { + //Note: don't compare the value to the old value here: it's possible that + //you have a different range selected. + selectionBackColorToSetOnHandleCreated = value; + if (IsHandleCreated) + { + NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); + if (value == Color.Empty) + { + cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR; + } + else + { + cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR; + cf2.crBackColor = ColorTranslator.ToWin32(value); + } + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2); + } + } + } + + /// + /// + /// The font used to display the currently selected text + /// or the characters(s) immediately following the insertion point in the + /// RichTextBox control. Null if the selection has more than one font. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelFont) + ] + public Font SelectionFont { + get { + return GetCharFormatFont(true); + } + set { + SetCharFormatFont(true, value); + } + } + + + /// + /// + /// The distance (in pixels) between the left edge of the first line of text + /// in the selected paragraph(s) (as specified by the SelectionIndent property) + /// and the left edge of subsequent lines of text in the same paragraph(s). + /// + [ + DefaultValue(0), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelHangingIndent) + ] + public int SelectionHangingIndent { + get { + int selHangingIndent = 0; + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_OFFSET & pf.dwMask) != 0) + selHangingIndent = pf.dxOffset; + + return Twip2Pixel(IntPtr.Zero, selHangingIndent, true); + } + set { + ForceHandleCreate(); + + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.dwMask = RichTextBoxConstants.PFM_OFFSET; + pf.dxOffset = Pixel2Twip(IntPtr.Zero, value, true); + + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// The distance (in pixels) between the left edge of the RichTextBox control and + /// the left edge of the text that is selected or added at the current + /// insertion point. + /// + [ + DefaultValue(0), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelIndent) + ] + public int SelectionIndent { + get { + int selIndent = 0; + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_STARTINDENT & pf.dwMask) != 0) + selIndent = pf.dxStartIndent; + + return Twip2Pixel(IntPtr.Zero, selIndent, true); + } + set { + ForceHandleCreate(); + + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.dwMask = RichTextBoxConstants.PFM_STARTINDENT; + pf.dxStartIndent = Pixel2Twip(IntPtr.Zero, value, true); + + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// + /// Gets or sets the number of characters selected in the text + /// box. + /// + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectionLengthDescr) + ] + public override int SelectionLength { + get { + + if (!IsHandleCreated) { + return base.SelectionLength; + } + + // RichTextBox allows the user to select the EOF character, + // but we don't want to include this in the SelectionLength. + // So instead of sending EM_GETSEL, we just obtain the SelectedText and return + // the length of it. + // + return SelectedText.Length; + } + + set { + base.SelectionLength = value; + } + } + + /// + /// + /// true if the current selection prevents any changes to its contents. + /// + [ + DefaultValue(false), + SRDescription(SR.RichTextBoxSelProtected), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool SelectionProtected { + get { + ForceHandleCreate(); + return GetCharFormat(RichTextBoxConstants.CFM_PROTECTED, RichTextBoxConstants.CFM_PROTECTED) == RichTextBoxSelectionAttribute.All; + } + set { + ForceHandleCreate(); + SetCharFormat(RichTextBoxConstants.CFM_PROTECTED, value ? RichTextBoxConstants.CFE_PROTECTED : 0, RichTextBoxSelectionAttribute.All); + } + } + + /// + /// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) + /// in Win9x. + /// + internal override bool SelectionUsesDbcsOffsetsInWin9x { + get { + return false; // false for RichEdit, true for Edit. + } + } + + /// + /// + /// The currently selected text of a RichTextBox control, including + /// all Rtf codes. + /// + [ + DefaultValue(""), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelRTF) + ] + public string SelectedRtf { + get { + ForceHandleCreate(); + return StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); + } + set { + ForceHandleCreate(); + if (value == null) value = ""; + StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); + } + } + + /// + /// + /// The distance (in pixels) between the right edge of the RichTextBox control and + /// the right edge of the text that is selected or added at the current + /// insertion point. + /// + [ + DefaultValue(0), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelRightIndent) + ] + public int SelectionRightIndent { + get { + int selRightIndent = 0; + + ForceHandleCreate(); + + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_RIGHTINDENT & pf.dwMask) != 0) + selRightIndent = pf.dxRightIndent; + + return Twip2Pixel(IntPtr.Zero, selRightIndent, true); + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException("SelectionRightIndent", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionRightIndent", value, 0)); + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.dwMask = RichTextBoxConstants.PFM_RIGHTINDENT; + pf.dxRightIndent = Pixel2Twip(IntPtr.Zero, value, true); + + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// The absolute tab positions (in pixels) of text in a RichTextBox control. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelTabs) + ] + public int[] SelectionTabs { + get { + int[] selTabs = new int[0]; + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + // check if alignment has been set yet + if ((RichTextBoxConstants.PFM_TABSTOPS & pf.dwMask) != 0) { + selTabs = new int[pf.cTabCount]; + for (int x = 0; x < pf.cTabCount; x++) + selTabs[x] = Twip2Pixel(IntPtr.Zero, pf.rgxTabs[x], true); + } + + return selTabs; + } + set { + // Verify the argument, and throw an error if is bad + if (value != null && value.Length > RichTextBoxConstants.MAX_TAB_STOPS) + throw new ArgumentOutOfRangeException("SelectionTabs", SR.GetString(SR.SelTabCountRange)); + + ForceHandleCreate(); + NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); + pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; + + // get the format for our currently selected paragraph because + // we need to get the number of tabstops to copy + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); + + pf.cTabCount = (short)((value == null) ? 0 : value.Length); + pf.dwMask = RichTextBoxConstants.PFM_TABSTOPS; + for (int x = 0; x < pf.cTabCount; x++) + pf.rgxTabs[x] = Pixel2Twip(IntPtr.Zero, value[x], true); + + // set the format for our current paragraph or selection + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); + } + } + + /// + /// + /// The currently selected text of a RichTextBox control; consists of a + /// zero length string if no characters are selected. + /// + [ + DefaultValue(""), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelText) + ] + public override string SelectedText { + get { + ForceHandleCreate(); + + String text = StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); + return text; + } + set { + ForceHandleCreate(); + StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); + } + } + + /// + /// + /// The type of the current selection. The returned value is one + /// of the values enumerated in RichTextBoxSelectionType. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxSelTypeDescr) + ] + public RichTextBoxSelectionTypes SelectionType { + get { + ForceHandleCreate(); + if (SelectionLength > 0) { + int n; + n = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_SELECTIONTYPE, 0, 0)); + return (RichTextBoxSelectionTypes)n; + } + else { + return RichTextBoxSelectionTypes.Empty; + } + } + } + + /// + /// + /// Whether or not the left edge of the control will have a "selection margin" which + /// can be used to select entire lines + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.RichTextBoxSelMargin) + ] + public bool ShowSelectionMargin { + get { return richTextBoxFlags[showSelBarSection] != 0; } + set { + if (value != ShowSelectionMargin) { + richTextBoxFlags[showSelBarSection] = value ? 1 : 0; + if (IsHandleCreated) { + SendMessage(RichTextBoxConstants.EM_SETOPTIONS, + value ? RichTextBoxConstants.ECOOP_OR : + RichTextBoxConstants.ECOOP_XOR, + RichTextBoxConstants.ECO_SELECTIONBAR); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + RefreshProperties(RefreshProperties.All) + ] + public override string Text { + get { + if (IsDisposed) { + return base.Text; + } + + if (RecreatingHandle || GetAnyDisposingInHierarchy()) { + // We can return any old garbage if we're in the process of recreating the handle + return ""; + } + + if (!IsHandleCreated && textRtf == null) { + if (textPlain != null) { + return textPlain; + } + else { + return base.Text; + } + } + else { + // if the handle is created, we are golden, however + // if the handle isn't created, but textRtf was + // specified, we need the RichEdit to translate + // for us, so we must create the handle; + // + ForceHandleCreate(); + + return StreamOut(RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); + } + } + set { + using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { + textRtf = null; + if (!IsHandleCreated) { + textPlain = value; + } + else { + textPlain = null; + if (value == null) { + value = ""; + } + StreamIn(value, RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); + // reset Modified + SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); + } + } + } + } + + private bool SuppressTextChangedEvent { + get { return richTextBoxFlags[suppressTextChangedEventSection] != 0; } + set { + bool oldValue = SuppressTextChangedEvent; + if (value != oldValue) { + richTextBoxFlags[suppressTextChangedEventSection] = value ? 1 : 0; + CommonProperties.xClearPreferredSizeCache(this); + } + } + } + + /// + [Browsable(false)] + public override int TextLength { + get + { + NativeMethods.GETTEXTLENGTHEX gtl = new NativeMethods.GETTEXTLENGTHEX(); + + gtl.flags = RichTextBoxConstants.GTL_NUMCHARS; + + if (Marshal.SystemDefaultCharSize == 1 /*ANSI*/) + { + gtl.codepage = 0; /* CP_ANSI */; + } + else + { + gtl.codepage = 1200; /* CP_UNICODE */ + } + + return unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTLENGTHEX, gtl, 0 /*ignored*/)); + } + } + + /// + /// + /// Returns the name of the action that will be undone if the user + /// Undo's their last operation. If no operation can be undone, it will + /// return an empty string (""). + /// + //NOTE: This is overridable, because we want people to be able to + // mess with the names if necessary...? + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.RichTextBoxUndoActionNameDescr) + ] + public string UndoActionName { + get { + if (!CanUndo) return ""; + int n; + n = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_GETUNDONAME, 0, 0)); + return GetEditorActionName(n); + } + } + + private string GetEditorActionName(int actionID) { + switch (actionID) { + case 0: + return SR.GetString(SR.RichTextBox_IDUnknown); + case 1: + return SR.GetString(SR.RichTextBox_IDTyping); + case 2: + return SR.GetString(SR.RichTextBox_IDDelete); + case 3: + return SR.GetString(SR.RichTextBox_IDDragDrop); + case 4: + return SR.GetString(SR.RichTextBox_IDCut); + case 5: + return SR.GetString(SR.RichTextBox_IDPaste); + default: + goto + case 0; + } + } + + /// + /// + /// The current zoom level for the RichTextBox control. This may be between 1/64 and 64. 1.0 indicates + /// no zoom (i.e. normal viewing). Zoom works best with TrueType fonts; + /// for non-TrueType fonts, ZoomFactor will be treated as the nearest whole number. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(1.0f), + Localizable(true), + SRDescription(SR.RichTextBoxZoomFactor) + ] + public float ZoomFactor { + get { + if (IsHandleCreated) { + int numerator = 0; + int denominator = 0; + SendMessage(RichTextBoxConstants.EM_GETZOOM, ref numerator, ref denominator); + if ( (numerator != 0) && (denominator != 0) ) { + zoomMultiplier = ((float)numerator)/((float)denominator); + } + else { + zoomMultiplier = 1.0f; + } + return zoomMultiplier; + } + else return zoomMultiplier; + } + + set { + if (zoomMultiplier == value) return; + + if (value <= 0.015625f || value >= 64.0f) + throw new ArgumentOutOfRangeException("ZoomFactor", SR.GetString(SR.InvalidExBoundArgument, "ZoomFactor", (value).ToString(CultureInfo.CurrentCulture), (0.015625f).ToString(CultureInfo.CurrentCulture), (64.0f).ToString(CultureInfo.CurrentCulture))); + SendZoomFactor(value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxContentsResized)] + public event ContentsResizedEventHandler ContentsResized { + add { + Events.AddHandler(EVENT_REQUESTRESIZE, value); + } + remove { + Events.RemoveHandler(EVENT_REQUESTRESIZE, value); + } + } + + + /// + /// + /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop + /// may still be used: this should be hidden in the property grid, but not in code + /// + [Browsable(false)] + public new event DragEventHandler DragDrop { + add { + base.DragDrop += value; + } + remove { + base.DragDrop -= value; + } + } + + /// + /// + /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop + /// may still be used: this should be hidden in the property grid, but not in code + /// + [Browsable(false)] + public new event DragEventHandler DragEnter { + add { + base.DragEnter += value; + } + remove { + base.DragEnter -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DragLeave { + add { + base.DragLeave += value; + } + remove { + base.DragLeave -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event DragEventHandler DragOver { + add { + base.DragOver += value; + } + remove { + base.DragOver -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event GiveFeedbackEventHandler GiveFeedback { + add { + base.GiveFeedback += value; + } + remove { + base.GiveFeedback -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event QueryContinueDragEventHandler QueryContinueDrag { + add { + base.QueryContinueDrag += value; + } + remove { + base.QueryContinueDrag -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxHScroll)] + public event EventHandler HScroll { + add { + Events.AddHandler(EVENT_HSCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_HSCROLL, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxLinkClick)] + public event LinkClickedEventHandler LinkClicked { + add { + Events.AddHandler(EVENT_LINKACTIVATE, value); + } + remove { + Events.RemoveHandler(EVENT_LINKACTIVATE, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxIMEChange)] + public event EventHandler ImeChange { + add { + Events.AddHandler(EVENT_IMECHANGE, value); + } + remove { + Events.RemoveHandler(EVENT_IMECHANGE, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxProtected)] + public event EventHandler Protected { + add { + Events.AddHandler(EVENT_PROTECTED, value); + } + remove { + Events.RemoveHandler(EVENT_PROTECTED, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxSelChange)] + public event EventHandler SelectionChanged { + add { + Events.AddHandler(EVENT_SELCHANGE, value); + } + remove { + Events.RemoveHandler(EVENT_SELCHANGE, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxVScroll)] + public event EventHandler VScroll { + add { + Events.AddHandler(EVENT_VSCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_VSCROLL, value); + } + } + + /// + /// + /// Returns a boolean indicating whether the RichTextBoxConstants control can paste the + /// given clipboard format. + /// + public bool CanPaste(DataFormats.Format clipFormat) { + bool b = false; + b = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_CANPASTE, clipFormat.Id, 0)) != 0; + + return b; + } + + //DrawToBitmap doesn't work for this control, so we should hide it. We'll + //still call base so that this has a chance to work if it can. + [EditorBrowsable(EditorBrowsableState.Never)] + new public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) + { + base.DrawToBitmap(bitmap, targetBounds); + } + + private unsafe int EditStreamProc(IntPtr dwCookie, IntPtr buf, int cb, out int transferred) { + int ret = 0; // assume that everything is Okay + + byte[] bytes = new byte[cb]; + + int cookieVal = (int)dwCookie; + + transferred = 0; + try { + switch (cookieVal & DIRECTIONMASK) { + case RichTextBox.OUTPUT: { + if (editStream == null) { + editStream = new MemoryStream(); + } + + switch (cookieVal & KINDMASK) { + case RichTextBox.RTF: + case RichTextBox.TEXTCRLF: + Marshal.Copy(buf, bytes, 0, cb); + editStream.Write(bytes, 0, cb); + break; + case RichTextBox.TEXTLF: + // Strip out \r characters so that we consistently return + // \n for linefeeds. In a future version the RichEdit control + // may support a SF_NOXLATCRLF flag which would do this for + // us. Internally the RichEdit stores the text with only + // a \n, so we want to keep that the same here. + // + if ((cookieVal & UNICODE) != 0) { + Debug.Assert(cb % 2 == 0, "EditStreamProc call out of cycle. Expected to always get character boundary calls"); + int requestedCharCount = cb/2; + int consumedCharCount = 0; + + fixed (byte* pb = bytes) { + char* pChars = (char*)pb; + char* pBuffer = (char*)(long)buf; + + for (int i=0; i + /// + /// Searches the text in a RichTextBox control for a given string. + /// + public int Find(string str) { + return Find(str, 0, 0, RichTextBoxFinds.None); + } + + /// + /// + /// Searches the text in a RichTextBox control for a given string. + /// + public int Find(string str, RichTextBoxFinds options) { + return Find(str, 0, 0, options); + } + + /// + /// + /// Searches the text in a RichTextBox control for a given string. + /// + public int Find(string str, int start, RichTextBoxFinds options) { + return Find(str, start, -1, options); + } + + /// + /// + /// Searches the text in a RichTextBox control for a given string. + /// + public int Find(string str, int start, int end, RichTextBoxFinds options) { + + int textLen = TextLength; + + if (str == null) + throw new ArgumentNullException("str"); + if (start < 0 || start > textLen) + throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLen)); + if (end < -1) + throw new ArgumentOutOfRangeException("end", SR.GetString(SR.RichTextFindEndInvalid, end)); + + bool selectWord = true; + NativeMethods.FINDTEXT ft = new NativeMethods.FINDTEXT(); + ft.chrg = new NativeMethods.CHARRANGE(); + + // set up the default values for the FINDTEXT structure, that is + // the given string and the whole range of the text stream + ft.lpstrText = str; + if (end == -1) { + end = textLen; + } + + if (start > end) { + throw new ArgumentException(SR.GetString(SR.RichTextFindEndInvalid, end)); + } + + if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { + // normal + // + ft.chrg.cpMin = start; + ft.chrg.cpMax = end; + } + else { + // reverse + // + ft.chrg.cpMin = end; + ft.chrg.cpMax = start; + } + + // force complete search if we ended up with a zero length search + if (ft.chrg.cpMin == ft.chrg.cpMax) { + if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { + ft.chrg.cpMin = 0; + ft.chrg.cpMax = -1; + } + else { + ft.chrg.cpMin = textLen; + ft.chrg.cpMax = 0; + } + } + + // set up the options for the search + int findOptions = 0; + if ((options & RichTextBoxFinds.WholeWord) == RichTextBoxFinds.WholeWord) + findOptions |= RichTextBoxConstants.FR_WHOLEWORD; + if ((options & RichTextBoxFinds.MatchCase) == RichTextBoxFinds.MatchCase) + findOptions |= RichTextBoxConstants.FR_MATCHCASE; + if ((options & RichTextBoxFinds.NoHighlight) == RichTextBoxFinds.NoHighlight) + selectWord = false; + if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { + // The default for RichEdit 2.0 is to search in reverse + findOptions |= RichTextBoxConstants.FR_DOWN; + } + + // Perform the find, will return ubyte position + int position; + + position = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_FINDTEXT, findOptions, ft); + + + // if we didn't find anything, or we don't have to select what was found, + // we're done + if (position != -1 && selectWord) { + // Select the string found, this is done in ubyte units + NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); + chrg.cpMin = position; + //Look for kashidas in the string. A kashida is an arabic visual justification character + //that's not semantically meaningful. Searching for ABC might find AB_C (where A,B, and C + //represent Arabic characters and _ represents a kashida). We should highlight the text + //including the kashida (VSWhidbey 94809). + char kashida = (char)0x640; + string text = this.Text; + string foundString = text.Substring(position, str.Length); + int startIndex = foundString.IndexOf(kashida); + if (startIndex == -1) + { + //No kashida in the string + chrg.cpMax = position + str.Length; + } + else + { + //There's at least one kashida + int searchingCursor; //index into search string + int foundCursor; //index into Text + for (searchingCursor = startIndex, foundCursor = position + startIndex; searchingCursor < str.Length; + searchingCursor++, foundCursor++) + { + while (text[foundCursor] == kashida && str[searchingCursor] != kashida) + { + foundCursor++; + } + } + chrg.cpMax = foundCursor; + } + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, chrg); + SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); + + } + + return position; + } + + /// + /// + /// Searches the text in a RichTextBox control for the given characters. + /// + public int Find(char[] characterSet) { + return Find(characterSet, 0, -1); + } + + /// + /// + /// Searches the text in a RichTextBox control for the given characters. + /// + public int Find(char[] characterSet, int start) { + return Find(characterSet, start, -1); + } + + /// + /// + /// Searches the text in a RichTextBox control for the given characters. + /// + public int Find(char[] characterSet, int start, int end) { + // Code used to support ability to search backwards and negate character sets. + // The API no longer supports this, but in case we change our mind, I'm leaving + // the ability in here. + bool forward = true; + bool negate = false; + + int textLength = TextLength; + + if (characterSet == null) + throw new ArgumentNullException("characterSet"); + if (start < 0 || start > textLength) + throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLength)); + if (end < start && end != -1) + throw new ArgumentOutOfRangeException("end", SR.GetString(SR.InvalidLowBoundArgumentEx, "end", end, "start")); + + // Don't do anything if we get nothing to look for + if (characterSet.Length == 0) + return -1; + + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); + if (start == end) { + start = 0; + end = textLen; + } + if (end == -1) { + end = textLen; + } + + NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); // The range of characters we have searched + chrg.cpMax = chrg.cpMin = start; + + // Use the TEXTRANGE to move our text buffer forward + // or backwards within the main text + NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); // Characters we have slurped into memory in order to search + txrg.chrg = new NativeMethods.CHARRANGE(); + + txrg.chrg.cpMin = chrg.cpMin; + txrg.chrg.cpMax = chrg.cpMax; + UnsafeNativeMethods.CharBuffer charBuffer; + charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(CHAR_BUFFER_LEN + 1); + + txrg.lpstrText = charBuffer.AllocCoTaskMem(); + if (txrg.lpstrText == IntPtr.Zero) + throw new OutOfMemoryException(); + + try { + bool done = false; + + // We want to loop as long as it takes. This loop will grab a + // chunk of text out from the control as directed by txrg.chrg; + while (!done) { + if (forward) { + // Move forward by starting at the end of the + // previous text window and extending by the + // size of our buffer + txrg.chrg.cpMin = chrg.cpMax; + txrg.chrg.cpMax += CHAR_BUFFER_LEN; + } + else { + // Move backwards by anchoring at the start + // of the previous buffer window, and backing + // up by the desired size of our buffer + txrg.chrg.cpMax = chrg.cpMin; + txrg.chrg.cpMin -= CHAR_BUFFER_LEN; + + // We need to keep our request within the + // lower bound of zero + if (txrg.chrg.cpMin < 0) + txrg.chrg.cpMin = 0; + } + + if (end != -1) + txrg.chrg.cpMax = Math.Min(txrg.chrg.cpMax, end); + + // go get the text in this range, if we didn't get any text then punt + int len; + len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); + if (len == 0) { + chrg.cpMax = chrg.cpMin = -1; // Hit end of control without finding what we wanted + break; + } + + // get the data from RichTextBoxConstants into a string for us to use. + charBuffer.PutCoTaskMem(txrg.lpstrText); + string str = charBuffer.GetString(); + + // Loop through our text + if (forward) { + // Start at the begining of the buffer + for (int x = 0; x < len; x++) { + // Is it in char set? + bool found = GetCharInCharSet(str[x], characterSet, negate); + + if (found) { + done = true; + break; + } + + // Advance the buffer + chrg.cpMax++; + } + } + else { // Span reverse. + int x = len; + while (x-- != 0) { + // Is it in char set? + bool found = GetCharInCharSet(str[x], characterSet, negate); + + if (found) { + done = true; + break; + } + + // Bring the selection back while keeping it anchored + chrg.cpMin--; + } + } + } + } + finally { + // release the resources we got for our GETTEXTRANGE operation. + if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(txrg.lpstrText); + } + + int index = (forward) ? chrg.cpMax : chrg.cpMin; + return index; + } + + private void ForceHandleCreate() { + if (!IsHandleCreated) + { + CreateHandle(); + } + } + + // Sends set color message to HWND; doesn't call Control.SetForeColor + private bool InternalSetForeColor(Color value) { + NativeMethods.CHARFORMATA cf = GetCharFormat(false); + if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0 + && ColorTranslator.ToWin32(value) == cf.crTextColor) { + + return true; + } + + cf.dwMask = RichTextBoxConstants.CFM_COLOR; + cf.dwEffects = 0; + cf.crTextColor = ColorTranslator.ToWin32(value); + return SetCharFormat(RichTextBoxConstants.SCF_ALL, cf); + } + + + private NativeMethods.CHARFORMATA GetCharFormat(bool fSelection) { + NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf); + return cf; + } + + private NativeMethods.CHARFORMAT2A GetCharFormat2(bool fSelection) { + NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf2); + return cf2; + } + + /// + /// + /// + /// + private RichTextBoxSelectionAttribute GetCharFormat(int mask, int effect) { + RichTextBoxSelectionAttribute charFormat = RichTextBoxSelectionAttribute.None; + + // check to see if the control has been created + if (IsHandleCreated) { + NativeMethods.CHARFORMATA cf = GetCharFormat(true); + // if the effects member contains valid info + if ((cf.dwMask & mask) != 0) + // if the text has the desired effect + if ((cf.dwEffects & effect) != 0) + charFormat = RichTextBoxSelectionAttribute.All; + } + + return charFormat; + } + + Font GetCharFormatFont(bool selectionOnly) { + ForceHandleCreate(); + + NativeMethods.CHARFORMATA cf = GetCharFormat(selectionOnly); + if ((cf.dwMask & RichTextBoxConstants.CFM_FACE) == 0) { + return null; + } + + string fontName = Encoding.Default.GetString(cf.szFaceName); + int index = fontName.IndexOf('\0'); + if (index != -1) { + fontName = fontName.Substring(0, index); + } + + float fontSize = 13; + if ((cf.dwMask & RichTextBoxConstants.CFM_SIZE) != 0) { + fontSize = (float)cf.yHeight/(float)20.0; + if (fontSize == 0 && cf.yHeight > 0) { + fontSize = 1; + } + } + + FontStyle style = FontStyle.Regular; + if ((cf.dwMask & RichTextBoxConstants.CFM_BOLD) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_BOLD) != 0) + style |= FontStyle.Bold; + if ((cf.dwMask & RichTextBoxConstants.CFM_ITALIC) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_ITALIC) != 0) + style |= FontStyle.Italic; + if ((cf.dwMask & RichTextBoxConstants.CFM_STRIKEOUT) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_STRIKEOUT) != 0) + style |= FontStyle.Strikeout; + if ((cf.dwMask & RichTextBoxConstants.CFM_UNDERLINE) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_UNDERLINE) != 0) + style |= FontStyle.Underline; + + try { + return new Font(fontName, fontSize, style, GraphicsUnit.Point, cf.bCharSet); + } + catch { + } + + return null; + } + + /// + /// + /// Returns the index of the character nearest to the given point. + /// + public override int GetCharIndexFromPosition(Point pt) { + NativeMethods.POINT wpt = new NativeMethods.POINT(pt.X, pt.Y); + int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, wpt); + + string t = this.Text; + // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit + // is a newline. + // + if (index >= t.Length) { + index = Math.Max(t.Length - 1, 0); + } + return index; + } + + /// + /// + /// + /// + private bool GetCharInCharSet(char c, char[] charSet, bool negate) { + bool match = false; + int charSetLen = charSet.Length; + + // Loop through the given character set and compare for a match + for (int i = 0; !match && i < charSetLen; i++) + match = c == charSet[i]; + + return negate ? !match : match; + } + + /// + /// + /// Returns the number of the line containing a specified character position + /// in a RichTextBox control. Note that this returns the physical line number + /// and not the conceptual line number. For example, if the first conceptual + /// line (line number 0) word-wraps and extends to the second line, and if + /// you pass the index of a overflowed character, GetLineFromCharIndex would + /// return 1 and not 0. + /// + public override int GetLineFromCharIndex(int index) { + return unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_EXLINEFROMCHAR, 0, index)); + } + + /// + /// + /// Returns the location of the character at the given index. + /// + public override Point GetPositionFromCharIndex(int index) { + if (richEditMajorVersion == 2) { + return base.GetPositionFromCharIndex(index); + } + + if (index < 0 || index > Text.Length) { + return Point.Empty; + } + + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, pt, index); + return new Point(pt.x, pt.y); + } + + /// + /// + /// + /// + private bool GetProtectedError() { + if (ProtectedError) { + ProtectedError = false; + return true; + } + + return false; + } + + /// + /// + /// Loads the contents of the given RTF or text file into a RichTextBox control. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public void LoadFile(string path) { + LoadFile(path, RichTextBoxStreamType.RichText); + } + + /// + /// + /// Loads the contents of a RTF or text into a RichTextBox control. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public void LoadFile(string path, RichTextBoxStreamType fileType) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)){ + throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); + } + + Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + try { + LoadFile(file, fileType); + } + finally { + file.Close(); + } + } + + /// + /// + /// Loads the contents of a RTF or text into a RichTextBox control. + /// + public void LoadFile(Stream data, RichTextBoxStreamType fileType) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) + { + throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); + } + + int flags; + switch (fileType) { + case RichTextBoxStreamType.RichText: + flags = RichTextBoxConstants.SF_RTF; + break; + case RichTextBoxStreamType.PlainText: + this.Rtf = ""; + flags = RichTextBoxConstants.SF_TEXT; + break; + case RichTextBoxStreamType.UnicodePlainText: + flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; + break; + default: + throw new ArgumentException(SR.GetString(SR.InvalidFileType)); + } + + StreamIn(data, flags); + } + + + /// + /// + /// [To be supplied.] + /// + protected override void OnBackColorChanged(EventArgs e) { + if (IsHandleCreated) + { + SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + base.OnBackColorChanged(e); + } + + /// + protected override void OnContextMenuChanged(EventArgs e) + { + base.OnContextMenuChanged(e); + UpdateOleCallback(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + //VSWhidbey# 325345. + // When the RTL property is changed, here's what happens. Let's assume that we change from + // RTL.No to RTL.Yes. + + // 1. RecreateHandle is called. + // 2. In RTB.OnHandleDestroyed, we cache off any RTF that might have been set. + // The RTB has been set to the empty string, so we do get RTF back. The RTF + // contains formatting info, but doesn't contain any reading-order info, + // so RichEdit defaults to LTR reading order. + // 3. In RTB.OnHandleCreated, we check if we have any cached RTF, and if so, + // we want to set the RTF to that value. This is to ensure that the original + // text doesn't get lost. + // 4. In the RTF setter, we get the current RTF, compare it to the old RTF, and + // since those are not equal, we set the RichEdit content to the old RTF. + // 5. But... since the original RTF had no reading-order info, the reading-order + // will default to LTR - thus VSWhidbey #325345. + + // That's why in Everett we set the text back since that clears the RTF, thus restoring + // the reading order to that of the window style. The problem here is that when there's + // no initial text (the empty string), then WindowText would not actually set the text, + // and we were left with the LTR reading order. There's no longer any initial text (as in Everett, + // e.g richTextBox1), since we changed the designers to not set text. Sigh... + + // So the fix is to force windowtext, whether or not that window text is equal to what's already there. + // Note that in doing so we will lose any formatting info you might have set on the RTF. We are okay with that. + + // We use WindowText rather than Text because this way we can avoid + // spurious TextChanged events. + // + string oldText = WindowText; + ForceWindowText(null); + ForceWindowText(oldText); + } + + /// + /// + /// Fires an event when the user changes the control's contents + /// are either smaller or larger than the control's window size. + /// + protected virtual void OnContentsResized(ContentsResizedEventArgs e) { + ContentsResizedEventHandler handler = (ContentsResizedEventHandler)Events[EVENT_REQUESTRESIZE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnHandleCreated(EventArgs e) { + // base.OnHandleCreated is called somewhere in the middle of this + + curSelStart = curSelEnd = curSelType = -1; + + // We will always set the control to use the maximum text, it defaults to 32k.. + // This must be done before we start loading files, because some files may + // be larger than 32k. + // + UpdateMaxLength(); + + // This is needed so that the control will fire change and update events + // even if it is hidden + // + SendMessage(RichTextBoxConstants.EM_SETEVENTMASK, + 0, + RichTextBoxConstants.ENM_PROTECTED | RichTextBoxConstants.ENM_SELCHANGE | + RichTextBoxConstants.ENM_DROPFILES | RichTextBoxConstants.ENM_REQUESTRESIZE | + RichTextBoxConstants.ENM_IMECHANGE | RichTextBoxConstants.ENM_CHANGE | + RichTextBoxConstants.ENM_UPDATE | RichTextBoxConstants.ENM_SCROLL | + RichTextBoxConstants.ENM_KEYEVENTS | RichTextBoxConstants.ENM_MOUSEEVENTS | + RichTextBoxConstants.ENM_SCROLLEVENTS | RichTextBoxConstants.ENM_LINK); + + int rm = rightMargin; + rightMargin = 0; + RightMargin = rm; + + // + + + this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, DetectUrls ? 1 : 0, 0); + if (selectionBackColorToSetOnHandleCreated != Color.Empty) { + this.SelectionBackColor = selectionBackColorToSetOnHandleCreated; + } + + // Initialize colors before initializing RTF, otherwise CFE_AUTOCOLOR will be in effect + // and our text will all be Color.WindowText. + AutoWordSelection = AutoWordSelection; + SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + InternalSetForeColor(ForeColor); + + // base sets the Text property. It's important to do this *after* setting EM_AUTOUrlDETECT. + base.OnHandleCreated(e); + + // vsWhidbey 371584: for some reason, we need to set the OleCallback before setting the RTF property. + UpdateOleCallback(); + + // RTF property takes precedence over Text property + // + try { + SuppressTextChangedEvent = true; + if (textRtf != null) { + // setting RTF calls back on Text, which relies on textRTF being null + string text = textRtf; + textRtf = null; + Rtf = text; + } + else if (textPlain != null) { + string text = textPlain; + textPlain = null; + Text = text; + } + } + finally { + SuppressTextChangedEvent = false; + } + + // Since we can't send EM_SETSEL until RTF has been set, + // we can't rely on base to do it for us. + base.SetSelectionOnHandle(); + + if (ShowSelectionMargin) { + // If you call SendMessage instead of PostMessage, the control + // will resize itself to the size of the parent's client area. Don't know why... + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOPTIONS, (IntPtr)RichTextBoxConstants.ECOOP_OR, + (IntPtr)RichTextBoxConstants.ECO_SELECTIONBAR); + } + + if (languageOption != this.LanguageOption) { + this.LanguageOption = languageOption; + } + + ClearUndo(); + + SendZoomFactor(zoomMultiplier); + + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + + if (!InConstructor) { + textRtf = Rtf; + if (textRtf.Length == 0) + textRtf = null; + } + + oleCallback = null; + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); + } + + /// + /// + /// Fires an event when the user clicks a RichTextBox control's horizontal + /// scroll bar. + /// + protected virtual void OnHScroll(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_HSCROLL]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Fires an event when the user clicks on a link + /// in a rich-edit control. + /// + protected virtual void OnLinkClicked(LinkClickedEventArgs e) { + LinkClickedEventHandler handler = (LinkClickedEventHandler)Events[EVENT_LINKACTIVATE]; + if (handler != null) handler(this,e); + } + + + /// + /// + /// Fires an event when the user changes the control's IME conversion status. + /// + protected virtual void OnImeChange(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_IMECHANGE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Fires an event when the user is taking an action that would change + /// a protected range of text in the RichTextBox control. + /// + protected virtual void OnProtected(EventArgs e) { + ProtectedError = true; + EventHandler handler = (EventHandler)Events[EVENT_PROTECTED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Fires an event when the current selection of text in the RichTextBox + /// control has changed or the insertion point has moved. + /// + protected virtual void OnSelectionChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SELCHANGE]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Fires an event when the user clicks a RichTextBox control's vertical + /// scroll bar. + /// + protected virtual void OnVScroll(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_VSCROLL]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Pastes the contents of the clipboard in the given clipboard format. + /// + public void Paste(DataFormats.Format clipFormat) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); + IntSecurity.ClipboardRead.Demand(); + + PasteUnsafe(clipFormat, 0); + } + + /// + /// + /// Note that this doesn't make a security demand: functions that call this should. + /// + private void PasteUnsafe(DataFormats.Format clipFormat, int hIcon) { + NativeMethods.REPASTESPECIAL rps = null; + + if (hIcon != 0) { + rps = new NativeMethods.REPASTESPECIAL(); + rps.dwAspect = DVASPECT_ICON; + rps.dwParam = hIcon; + } + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_PASTESPECIAL, clipFormat.Id, rps); + } + + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message m, Keys keyData) { + if (this.RichTextShortcutsEnabled == false) { + foreach (int shortcutValue in shortcutsToDisable) { + if ((int)keyData == shortcutValue) { + return true; + } + } + } + return base.ProcessCmdKey(ref m, keyData); + } + + /// + /// + /// Redoes the last undone editing operation. + /// + public void Redo() { + SendMessage(RichTextBoxConstants.EM_REDO, 0, 0); + } + + //NOTE: Undo is implemented on TextBox + + /// + /// + /// Saves the contents of a RichTextBox control to a file. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public void SaveFile(string path) { + SaveFile(path, RichTextBoxStreamType.RichText); + } + + /// + /// + /// Saves the contents of a RichTextBox control to a file. + /// + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public void SaveFile(string path, RichTextBoxStreamType fileType) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) + { + throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); + } + + Stream file = File.Create(path); + try { + SaveFile(file, fileType); + } + finally { + file.Close(); + } + } + + /// + /// + /// Saves the contents of a RichTextBox control to a file. + /// + public void SaveFile(Stream data, RichTextBoxStreamType fileType) { + int flags; + switch (fileType) { + case RichTextBoxStreamType.RichText: + flags = RichTextBoxConstants.SF_RTF; + break; + case RichTextBoxStreamType.PlainText: + flags = RichTextBoxConstants.SF_TEXT; + break; + case RichTextBoxStreamType.UnicodePlainText: + flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; + break; + case RichTextBoxStreamType.RichNoOleObjs: + flags = RichTextBoxConstants.SF_RTFNOOBJS; + break; + case RichTextBoxStreamType.TextTextOleObjs: + flags = RichTextBoxConstants.SF_TEXTIZED; + break; + default: + throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); + } + + StreamOut(data, flags, true); + } + + /// + /// + /// Core Zoom calculation and message passing (used by ZoomFactor property and CreateHandle() + /// + /// + private void SendZoomFactor(float zoom) { + int numerator; + int denominator; + + if (zoom == 1.0f) { + denominator = 0; + numerator = 0; + } + else { + denominator = 1000; + float multiplier = 1000 * zoom; + numerator = (int)Math.Ceiling(multiplier); + if (numerator >= 64000) { + numerator = (int)Math.Floor(multiplier); + } + } + + if (IsHandleCreated) { + SendMessage(RichTextBoxConstants.EM_SETZOOM, numerator, denominator); + +#if DEBUG + + // DEBUG CODE: Verify that EM_SETZOOM actually set the zoom + int n = 0, d = 0; + SendMessage(RichTextBoxConstants.EM_GETZOOM, ref n, ref d); + Debug.Assert(n == numerator && d == denominator, "EM_SETZOOM failed"); + // END DEBUG CODE +#endif + } + + if (numerator != 0) { + zoomMultiplier = ((float)numerator)/((float)denominator); + } + else { + zoomMultiplier = 1.0f; + } + } + + /// + /// + /// + /// + private bool SetCharFormat(int mask, int effect, RichTextBoxSelectionAttribute charFormat) { + // check to see if the control has been created + if (IsHandleCreated) { + NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); + + cf.dwMask = mask; + + switch (charFormat) { + case RichTextBoxSelectionAttribute.All: + cf.dwEffects = effect; + break; + case RichTextBoxSelectionAttribute.None: + cf.dwEffects = 0; + break; + default: + throw new ArgumentException(SR.GetString(SR.UnknownAttr)); + } + + // set the format information + return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); + } + return false; + } + + private bool SetCharFormat(int charRange, NativeMethods.CHARFORMATA cf) { + return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, charRange, cf); + } + + private void SetCharFormatFont(bool selectionOnly, Font value) { + ForceHandleCreate(); + NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); + + FontToLogFont(value, logfont); + + byte[] bytesFaceName; + + int dwMask = RichTextBoxConstants.CFM_FACE | RichTextBoxConstants.CFM_SIZE | RichTextBoxConstants.CFM_BOLD | + RichTextBoxConstants.CFM_ITALIC | RichTextBoxConstants.CFM_STRIKEOUT | RichTextBoxConstants.CFM_UNDERLINE | + RichTextBoxConstants.CFM_CHARSET; + + int dwEffects = 0; + if (value.Bold) dwEffects |= RichTextBoxConstants.CFE_BOLD; + if (value.Italic) dwEffects |= RichTextBoxConstants.CFE_ITALIC; + if (value.Strikeout) dwEffects |= RichTextBoxConstants.CFE_STRIKEOUT; + if (value.Underline) dwEffects |= RichTextBoxConstants.CFE_UNDERLINE; + + if (Marshal.SystemDefaultCharSize == 1) + { + bytesFaceName = Encoding.Default.GetBytes(logfont.lfFaceName); + + NativeMethods.CHARFORMATA cfA = new NativeMethods.CHARFORMATA(); + for (int i=0; i + /// + /// This is just here as a minor perf improvement, so we don't have to call expensive RevertAssert. + /// When the method exits, we effectively revert the assert. + /// + [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + static private void FontToLogFont(Font value, NativeMethods.LOGFONT logfont) { + value.ToLogFont(logfont); + } + + + /// + /// + /// + private static void SetupLogPixels(IntPtr hDC) { + bool release = false; + if (hDC == IntPtr.Zero) { + hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + release = true; + } + if (hDC == IntPtr.Zero) return; + logPixelsX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSX); + logPixelsY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSY); + if (release) + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); + } + + private static int Pixel2Twip(IntPtr hDC, int v, bool xDirection) { + SetupLogPixels(hDC); + int logP = xDirection ? logPixelsX : logPixelsY; + return(int) ((((double)v) / logP) * 72.0 * 20.0); + } + + private static int Twip2Pixel(IntPtr hDC, int v, bool xDirection) { + SetupLogPixels(hDC); + int logP = xDirection ? logPixelsX : logPixelsY; + return(int) (((((double) v) / 20.0) / 72.0) * logP); + } + + /// + /// + /// + /// + private void StreamIn(string str, int flags) { + if (str.Length == 0 ) { + // Destroy the selection if callers was setting + // selection text + // + if ((RichTextBoxConstants.SFF_SELECTION & flags) != 0) { + SendMessage(NativeMethods.WM_CLEAR, 0, 0); + ProtectedError = false; + return; + } + // WM_SETTEXT is allowed even if we have protected text + // + SendMessage(NativeMethods.WM_SETTEXT, 0, ""); + return; + } + + // Rather than work only some of the time with null characters, + // we're going to be consistent and never work with them. + int nullTerminatedLength = str.IndexOf((char) 0); + if (nullTerminatedLength != -1) { + str = str.Substring(0, nullTerminatedLength); + } + + // get the string into a byte array + byte[] encodedBytes; + if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { + encodedBytes = Encoding.Unicode.GetBytes(str); + } + else { + encodedBytes = Encoding.Default.GetBytes(str); + } + editStream = new MemoryStream(encodedBytes.Length); + editStream.Write(encodedBytes, 0, encodedBytes.Length); + editStream.Position = 0; + StreamIn(editStream, flags); + } + + private void StreamIn(Stream data, int flags) { + // clear out the selection only if we are replacing all the text + // + if ((flags & RichTextBoxConstants.SFF_SELECTION) == 0) { + NativeMethods.CHARRANGE cr = new NativeMethods.CHARRANGE(); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, cr); + } + + try { + editStream = data; + Debug.Assert(data != null, "StreamIn passed a null stream"); + + // If SF_RTF is requested then check for the RTF tag at the start + // of the file. We don't load if the tag is not there + // + if ((flags & RichTextBoxConstants.SF_RTF) != 0) { + long streamStart = editStream.Position; + byte[] bytes = new byte[SZ_RTF_TAG.Length]; + editStream.Read(bytes, (int)streamStart, SZ_RTF_TAG.Length); + string str = Encoding.Default.GetString(bytes); + if (!SZ_RTF_TAG.Equals(str)) + throw new ArgumentException(SR.GetString(SR.InvalidFileFormat)); + + // put us back at the start of the file + editStream.Position = streamStart; + } + + int cookieVal = 0; + // set up structure to do stream operation + NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); + if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { + cookieVal = INPUT | UNICODE; + } + else { + cookieVal = INPUT | ANSI; + } + if ((flags & RichTextBoxConstants.SF_RTF) != 0) { + cookieVal |= RTF; + } + else { + cookieVal |= TEXTLF; + } + es.dwCookie = (IntPtr) cookieVal; + es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); + + // gives us TextBox compatible behavior, programatic text change shouldn't + // be limited... + // + SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, Int32.MaxValue); + + + + // go get the text for the control + //Weird hack needed for 64-bit + if (IntPtr.Size == 8) { + NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es64); + + //Assign back dwError value + es.dwError = GetErrorValue64(es64); + } + else { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es); + } + + UpdateMaxLength(); + + // If we failed to load because of protected + // text then return protect event was fired so no + // exception is required for the the error + if (GetProtectedError()) + return; + + if (es.dwError != 0) + throw new InvalidOperationException(SR.GetString(SR.LoadTextError)); + + // set the modify tag on the control + SendMessage(NativeMethods.EM_SETMODIFY, -1, 0); + + // EM_GETLINECOUNT will cause the RichTextBoxConstants to recalculate its line indexes + SendMessage(NativeMethods.EM_GETLINECOUNT, 0, 0); + + + } + finally { + // release any storage space held. + editStream = null; + } + } + + /// + /// + /// + /// + private string StreamOut(int flags) { + Stream stream = new MemoryStream(); + StreamOut(stream, flags, false); + stream.Position = 0; + int streamLength = (int)stream.Length; + string result = string.Empty; + + if (streamLength > 0) { + byte[] bytes = new byte[streamLength]; + stream.Read(bytes, 0, streamLength); + + if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { + result = Encoding.Unicode.GetString(bytes,0 , bytes.Length); + } + else { + result = Encoding.Default.GetString(bytes, 0, bytes.Length); + } + // workaround ??? for bug 117325. When the string is modified is can return + // with an extra null termination (always?). If it does, get rid of it. + if(!String.IsNullOrEmpty(result) && (result[result.Length-1] == '\0')) { + result = result.Substring(0, result.Length-1); + } + } + + return result; + } + + private void StreamOut(Stream data, int flags, bool includeCrLfs) { + // set up the EDITSTREAM structure for the callback. + Debug.Assert(data != null, "StreamOut passed a null stream"); + editStream = data; + + try { + int cookieVal = 0; + NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); + if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { + cookieVal = OUTPUT | UNICODE; + } + else { + cookieVal = OUTPUT | ANSI; + } + if ((flags & RichTextBoxConstants.SF_RTF) != 0) { + cookieVal |= RTF; + } + else { + if (includeCrLfs) { + cookieVal |= TEXTCRLF; + } + else { + cookieVal |= TEXTLF; + } + } + es.dwCookie = (IntPtr) cookieVal; + es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); + + //Get Text + //Weird hack needed for 64-bit + if (IntPtr.Size == 8) { + NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es64); + + //Assign back dwError value + es.dwError = GetErrorValue64(es64); + } + else { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es); + } + + // check to make sure things went well + if (es.dwError != 0) + throw new InvalidOperationException(SR.GetString(SR.SaveTextError)); + } + finally { + // release any storage space held. + editStream = null; + } + } + + private unsafe NativeMethods.EDITSTREAM64 ConvertToEDITSTREAM64(NativeMethods.EDITSTREAM es) { + NativeMethods.EDITSTREAM64 es64 = new NativeMethods.EDITSTREAM64(); + + fixed (byte* es64p = &es64.contents[0]) { + byte *bp; + long l; + + /* + l = (long) es.dwCookie; + bp = (byte *) &l; + for (int i=0; i < sizeof(long); i++) { + es64.contents[i] = bp[i]; + }*/ + *((long *)es64p) = (long) es.dwCookie; + /* + int il = es.dwError; + bp = (byte *) &il; + for (int i=0; i < sizeof(int); i++) { + es64.contents[i+8] = bp[i]; + }*/ + *((int *)(es64p + 8)) = es.dwError; + + l = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); + bp = (byte *) &l; + for (int i=0; i < sizeof(long); i++) { + es64.contents[i+12] = bp[i]; + } + //*((long *)(es64p + 12)) = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); + } + + return es64; + } + + private unsafe int GetErrorValue64(NativeMethods.EDITSTREAM64 es64) { + int errorVal; + + fixed (byte* es64p = &es64.contents[0]) { + errorVal = *((int *)(es64p + 8)); + } + + return errorVal; + } + +/* FOR 64 BIT DEBUGGING + private unsafe string PrintBytes(NativeMethods.EDITSTREAM es) { + StringBuilder sb = new StringBuilder(); + fixed (IntPtr *ip = &es.dwCookie) { + byte *bytep = (byte *) ip; + for (int i=0; i < Marshal.SizeOf(es); i++) { + sb.Append(bytep[i].ToString() + " "); + } + } + return sb.ToString(); + } + + private unsafe string PrintBytes(NativeMethods.EDITSTREAM64 es64) { + StringBuilder sb = new StringBuilder(); + fixed (byte *bytep = &es64.contents[0]) { + for (int i=0; i < Marshal.SizeOf(es64); i++) { + sb.Append(bytep[i].ToString() + " "); + } + } + return sb.ToString(); + } +*/ + + + private void UpdateOleCallback() { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "update ole callback (" + AllowDrop + ")"); + if (IsHandleCreated) { + if (oleCallback == null) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "binding ole callback"); + bool unrestricted = false; + try { + IntSecurity.UnmanagedCode.Demand(); + unrestricted = true; + } + catch (SecurityException) { + unrestricted = false; + } + if (unrestricted) { + this.AllowOleObjects = true; + } + else { + this.AllowOleObjects = (0 != unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_SETQUERYRTFOBJ, 0, 1))); + } + + oleCallback = CreateRichEditOleCallback(); + + // Forcibly QI (through IUnknown::QueryInterface) to handle multiple + // definitions of the interface. + // + IntPtr punk = Marshal.GetIUnknownForObject(oleCallback); + try { + IntPtr pRichEditOleCallback; + Guid iidRichEditOleCallback = typeof(UnsafeNativeMethods.IRichEditOleCallback).GUID; + Marshal.QueryInterface(punk, ref iidRichEditOleCallback, out pRichEditOleCallback); + try { + UnsafeNativeMethods.SendCallbackMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOLECALLBACK, IntPtr.Zero, pRichEditOleCallback); + } + finally { + Marshal.Release(pRichEditOleCallback); + } + } finally { + Marshal.Release(punk); + } + } + UnsafeNativeMethods.DragAcceptFiles(new HandleRef(this, Handle), false); + } + } + + //Note: RichTextBox doesn't work like other controls as far as setting ForeColor/ + //BackColor -- you need to send messages to update the colors + private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e) + { + if (IsHandleCreated) + { + if (this.BackColor.IsSystemColor) + { + SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + } + if (this.ForeColor.IsSystemColor) + { + InternalSetForeColor(ForeColor); + } + } + } + + /// + /// + /// Creates the IRichEditOleCallback compatible object for handling RichEdit callbacks. For more + /// information look up the MSDN info on this interface. This is designed to be a back door of + /// sorts, which is why it is fairly obscure, and uses the RichEdit name instead of RichTextBox. + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected virtual object CreateRichEditOleCallback() { + return new OleCallback(this); + } + + /// + /// + /// Handles link messages (mouse move, down, up, dblclk, etc) + /// + /// + private void EnLinkMsgHandler(ref Message m) { + NativeMethods.ENLINK enlink; + //On 64-bit, we do some custom marshalling to get this to work. The richedit control + //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 & 504502. + if (IntPtr.Size == 8) { + enlink = ConvertFromENLINK64((NativeMethods.ENLINK64)m.GetLParam(typeof(NativeMethods.ENLINK64))); + } + else { + enlink = (NativeMethods.ENLINK)m.GetLParam(typeof(NativeMethods.ENLINK)); + } + + switch (enlink.msg) { + case NativeMethods.WM_SETCURSOR: + LinkCursor = true; + m.Result = (IntPtr)1; + return; + // Mouse-down triggers Url; this matches Outlook 2000's behavior. + case NativeMethods.WM_LBUTTONDOWN: + string linktext = CharRangeToString(enlink.charrange); + if (!string.IsNullOrEmpty(linktext)) + { + OnLinkClicked(new LinkClickedEventArgs(linktext)); + } + m.Result = (IntPtr)1; + return; + } + m.Result = IntPtr.Zero; + return; + } + + /// + /// + /// Converts a CHARRANGE to a string. Note: The behavior of this is dependent on the current window + /// class name being used. We have to create a CharBuffer of the type of RichTextBox DLL we're using, + /// not based on the SystemCharWidth. + /// + /// + private string CharRangeToString(NativeMethods.CHARRANGE c) { + NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); + txrg.chrg = c; + Debug.Assert((c.cpMax-c.cpMin)>0, "CHARRANGE was null or negative - can't do it!"); + + //Windows bug: 64-bit windows returns a bad range for us. VSWhidbey 504502. + //Putting in a hack to avoid an unhandled exception. + if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) { + return string.Empty; + } + + int characters = (c.cpMax-c.cpMin) + 1; // +1 for null termination + UnsafeNativeMethods.CharBuffer charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(characters); + IntPtr unmanagedBuffer = charBuffer.AllocCoTaskMem(); + if (unmanagedBuffer == IntPtr.Zero) + throw new OutOfMemoryException(SR.GetString(SR.OutOfMemory)); + + txrg.lpstrText = unmanagedBuffer; + int len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); + Debug.Assert(len != 0, "CHARRANGE from RichTextBox was bad! - impossible?"); + charBuffer.PutCoTaskMem(unmanagedBuffer); + if (txrg.lpstrText != IntPtr.Zero) + Marshal.FreeCoTaskMem(unmanagedBuffer); + + string result = charBuffer.GetString(); + return result; + } + + internal override void UpdateMaxLength() { + if (IsHandleCreated) { + SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, MaxLength); + } + } + + /// + /// + /// + /// + private void WmReflectCommand(ref Message m) { + + // We check if we're in the middle of handle creation because + // the rich edit control fires spurious events during this time. + // + if (m.LParam == Handle && !GetState(STATE_CREATINGHANDLE)) + { + switch (Util.HIWORD(m.WParam)) { + + case NativeMethods.EN_HSCROLL: + OnHScroll(EventArgs.Empty); + break; + + case NativeMethods.EN_VSCROLL: + OnVScroll(EventArgs.Empty); + break; + + default: + base.WndProc(ref m); + break; + } + } + else { + base.WndProc(ref m); + } + } + + /// + /// + /// + /// + internal void WmReflectNotify(ref Message m) { + if (m.HWnd == Handle) { + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (nmhdr.code) { + case RichTextBoxConstants.EN_LINK: + EnLinkMsgHandler(ref m); + break; + case RichTextBoxConstants.EN_DROPFILES: + NativeMethods.ENDROPFILES endropfiles = (NativeMethods.ENDROPFILES)m.GetLParam(typeof(NativeMethods.ENDROPFILES)); + + // Only look at the first file. + StringBuilder path = new StringBuilder(NativeMethods.MAX_PATH); + if (UnsafeNativeMethods.DragQueryFileLongPath(new HandleRef(endropfiles, endropfiles.hDrop), 0, path) != 0) + { + // Try to load the file as an RTF + try + { + LoadFile(path.ToString(), RichTextBoxStreamType.RichText); + } + catch + { + // we failed to load as rich text so try it as plain text + try + { + LoadFile(path.ToString(), RichTextBoxStreamType.PlainText); + } + catch + { + // ignore any problems we have + } + } + } + m.Result = (IntPtr)1; // tell them we did the drop + break; + + case RichTextBoxConstants.EN_REQUESTRESIZE: + if (!CallOnContentsResized) { + NativeMethods.REQRESIZE reqResize = (NativeMethods.REQRESIZE)m.GetLParam(typeof(NativeMethods.REQRESIZE)); + if (BorderStyle == System.Windows.Forms.BorderStyle.Fixed3D) { + reqResize.rc.bottom++; + } + OnContentsResized(new ContentsResizedEventArgs(Rectangle.FromLTRB(reqResize.rc.left, reqResize.rc.top, reqResize.rc.right, reqResize.rc.bottom))); + } + break; + + case RichTextBoxConstants.EN_SELCHANGE: + NativeMethods.SELCHANGE selChange = (NativeMethods.SELCHANGE)m.GetLParam(typeof(NativeMethods.SELCHANGE)); + WmSelectionChange(selChange); + break; + + case RichTextBoxConstants.EN_PROTECTED: { + NativeMethods.ENPROTECTED enprotected; + + //On 64-bit, we do some custom marshalling to get this to work. The richedit control + //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 + if (IntPtr.Size == 8) { + enprotected = ConvertFromENPROTECTED64((NativeMethods.ENPROTECTED64)m.GetLParam(typeof(NativeMethods.ENPROTECTED64))); + } + else { + enprotected = (NativeMethods.ENPROTECTED)m.GetLParam(typeof(NativeMethods.ENPROTECTED)); + } + + + switch (enprotected.msg) { + case RichTextBoxConstants.EM_SETCHARFORMAT: + // Allow change of protected style + // + NativeMethods.CHARFORMATA charFormat = (NativeMethods.CHARFORMATA)UnsafeNativeMethods.PtrToStructure(enprotected.lParam, typeof(NativeMethods.CHARFORMATA)); + if ((charFormat.dwMask & RichTextBoxConstants.CFM_PROTECTED) != 0) { + m.Result = IntPtr.Zero; + return; + } + break; + + // Throw an exception for the following + // + case RichTextBoxConstants.EM_SETPARAFORMAT: + case NativeMethods.EM_REPLACESEL: + break; + + case RichTextBoxConstants.EM_STREAMIN: + // Don't allow STREAMIN to replace protected selection + // + if ((unchecked( (int) (long)enprotected.wParam) & RichTextBoxConstants.SFF_SELECTION) != 0) + break; + m.Result = IntPtr.Zero; + return; + + // Allow the following + // + case NativeMethods.WM_COPY: + case NativeMethods.WM_SETTEXT: + case RichTextBoxConstants.EM_EXLIMITTEXT: + m.Result = IntPtr.Zero; + return; + + // Beep and disallow change for all other messages + // + default: + SafeNativeMethods.MessageBeep(0); + break; + } + + OnProtected(EventArgs.Empty); + m.Result = (IntPtr)1; + break; + } + + default: + base.WndProc(ref m); + break; + } + } + else { + base.WndProc(ref m); + } + } + + private unsafe NativeMethods.ENPROTECTED ConvertFromENPROTECTED64(NativeMethods.ENPROTECTED64 es64) { + NativeMethods.ENPROTECTED es = new NativeMethods.ENPROTECTED(); + + fixed (byte* es64p = &es64.contents[0]) { + es.nmhdr = new NativeMethods.NMHDR(); + es.chrg = new NativeMethods.CHARRANGE(); + + es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); + es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); + es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); + es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); + es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); + es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); + es.chrg.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); + es.chrg.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); + } + + return es; + } + + private static unsafe NativeMethods.ENLINK ConvertFromENLINK64(NativeMethods.ENLINK64 es64) { + NativeMethods.ENLINK es = new NativeMethods.ENLINK(); + + fixed (byte* es64p = &es64.contents[0]) { + es.nmhdr = new NativeMethods.NMHDR(); + es.charrange = new NativeMethods.CHARRANGE(); + + es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); + es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); + es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); + es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); + es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); + es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); + es.charrange.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); + es.charrange.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); + } + + return es; + } + + /// + /// + /// + /// + private void WmSelectionChange(NativeMethods.SELCHANGE selChange) { + int selStart = selChange.chrg.cpMin; + int selEnd = selChange.chrg.cpMax; + short selType = (short)selChange.seltyp; + + // VSWhidbey 94804: The IME retains characters in the composition window even after MaxLength + // has been reached in the rich edit control. So, if the Hangul or HangulFull IME is in use, and the + // number of characters in the control is equal to MaxLength, and the selection start equals the + // selection end (nothing is currently selected), then kill and restore focus to the control. Then, + // to prevent any further partial composition from occurring, post a message back to myself to select + // the last character being composed so that any further composition will occur within the context of + // the string contained within the control. + // + // Since the IME window completes the composition string when the control loses focus and the + // EIMES_COMPLETECOMPSTRKILLFOCUS status type is set in the control by the EM_SETIMESTATUS message, + // simply killing focus and resetting focus to the control will force the contents of the composition + // window to be removed. This forces the undo buffer to be emptied and the backspace key will properly + // remove the last completed character typed. + + // Is either the Hangul or HangulFull IME currently in use? + if( ImeMode == ImeMode.Hangul || ImeMode == ImeMode.HangulFull ) { + + // Is the IME CompositionWindow open? + int compMode = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_GETIMECOMPMODE, 0, 0)); + if (RichTextBoxConstants.ICM_NOTOPEN != compMode) { + + int textLength = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); + if (selStart == selEnd && textLength == MaxLength) { + + SendMessage(NativeMethods.WM_KILLFOCUS, 0, 0); + SendMessage(NativeMethods.WM_SETFOCUS, 0, 0); + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.EM_SETSEL, selEnd - 1, selEnd); + } + } + } + + if (selStart != curSelStart || selEnd != curSelEnd || selType != curSelType) { + curSelStart = selStart; + curSelEnd = selEnd; + curSelType = selType; + OnSelectionChanged(EventArgs.Empty); + } + } + + /// + /// + /// + /// + private void WmSetFont(ref Message m) { + + // This function would normally cause two TextChanged events to be fired, one + // from the base.WndProc, and another from InternalSetForeColor. + // To prevent this, we suppress the first event fire. + // + try { + SuppressTextChangedEvent = true; + base.WndProc(ref m); + } + finally { + SuppressTextChangedEvent = false; + } + + InternalSetForeColor(ForeColor); + } + + // + // + // + /// + /// + /// [To be supplied.] + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + WmReflectNotify(ref m); + break; + + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + + case NativeMethods.WM_SETCURSOR: + //NOTE: RichTextBox uses the WM_SETCURSOR message over links to allow us to + // change the cursor to a hand. It does this through a synchronous notification + // message. So we have to pass the message to the DefWndProc first, and + // then, if we receive a notification message in the meantime (indicated by + // changing "LinkCursor", we set it to a hand. Otherwise, we call the + // WM_SETCURSOR implementation on Control to set it to the user's selection for + // the RichTextBox's cursor. + // + // Similarly, + LinkCursor = false; + DefWndProc(ref m); + if (LinkCursor && !Cursor.Equals(Cursors.WaitCursor)) { + UnsafeNativeMethods.SetCursor(new HandleRef(Cursors.Hand, Cursors.Hand.Handle)); + m.Result = (IntPtr)1; + } + else { + base.WndProc(ref m); + } + break; + + case NativeMethods.WM_SETFONT: + WmSetFont(ref m); + break; + + case NativeMethods.WM_IME_NOTIFY: + OnImeChange(EventArgs.Empty); + base.WndProc(ref m); + break; + + case NativeMethods.WM_GETDLGCODE: + base.WndProc(ref m); + m.Result = (IntPtr)((AcceptsTab) ? unchecked( (int) (long)m.Result) | NativeMethods.DLGC_WANTTAB : unchecked( (int) (long)m.Result) & ~NativeMethods.DLGC_WANTTAB); + break; + + case NativeMethods.WM_GETOBJECT: + base.WndProc(ref m); + + // OLEACC.DLL uses window class names to identify standard control types. But WinForm controls use app-specific window + // classes. Usually this doesn't matter, because system controls always identify their window class explicitly through + // the WM_GETOBJECT+OBJID_QUERYCLASSNAMEIDX message. But RICHEDIT20 doesn't do that - so we must do it ourselves. + // Otherwise OLEACC will treat rich edit controls as custom controls, so the accessible Role and Value will be wrong. + if (unchecked((int)(long)m.LParam) == NativeMethods.OBJID_QUERYCLASSNAMEIDX) { + m.Result = (IntPtr) ((Marshal.SystemDefaultCharSize == 1) ? (65536+29) : (65536+30)); + } + break; + + case NativeMethods.WM_RBUTTONUP: + //Whidbey 317086 + //since RichEdit eats up the WM_CONTEXTMENU message, we need to force DefWndProc + //to spit out this message again on receiving WM_RBUTTONUP message. By setting UserMouse + //style to true, we effectily let the WmMouseUp method in Control.cs to generate + //the WM_CONTEXTMENU message for us. + bool oldStyle = GetStyle(ControlStyles.UserMouse); + SetStyle(ControlStyles.UserMouse, true); + base.WndProc(ref m); + SetStyle(ControlStyles.UserMouse, oldStyle); + break; + + case NativeMethods.WM_VSCROLL: + base.WndProc(ref m); + int loWord = Util.LOWORD(m.WParam); + if (loWord == NativeMethods.SB_THUMBTRACK) + { + OnVScroll(EventArgs.Empty); + } else + if (loWord == NativeMethods.SB_THUMBPOSITION) + { + OnVScroll(EventArgs.Empty); + } + break; + + case NativeMethods.WM_HSCROLL: + base.WndProc(ref m); + loWord = Util.LOWORD(m.WParam); + if (loWord == NativeMethods.SB_THUMBTRACK) + { + OnHScroll(EventArgs.Empty); + } + if (loWord == NativeMethods.SB_THUMBPOSITION) + { + OnHScroll(EventArgs.Empty); + } + break; + + default: + base.WndProc(ref m); + break; + } + } + + // I used the visual basic 6 RichText (REOleCB.CPP) as a guide for this + private class OleCallback : UnsafeNativeMethods.IRichEditOleCallback { + + private RichTextBox owner; + IDataObject lastDataObject; + DragDropEffects lastEffect; + + internal OleCallback(RichTextBox owner) { + this.owner = owner; + } + + + public int GetNewStorage(out UnsafeNativeMethods.IStorage storage) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetNewStorage"); + if (!this.owner.AllowOleObjects) { + storage = null; + return NativeMethods.E_FAIL; + } + // Debug.WriteLine("get new storage"); + UnsafeNativeMethods.ILockBytes pLockBytes = UnsafeNativeMethods.CreateILockBytesOnHGlobal(NativeMethods.NullHandleRef, true); + + Debug.Assert(pLockBytes != null, "pLockBytes is NULL!"); + + storage = UnsafeNativeMethods.StgCreateDocfileOnILockBytes(pLockBytes, + NativeMethods.STGM_SHARE_EXCLUSIVE | NativeMethods.STGM_CREATE | NativeMethods.STGM_READWRITE, + 0); + Debug.Assert(storage != null, "storage is NULL!"); + + return NativeMethods.S_OK; + } + + public int GetInPlaceContext(IntPtr lplpFrame, + IntPtr lplpDoc, + IntPtr lpFrameInfo) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetInPlaceContext"); + return NativeMethods.E_NOTIMPL; + } + + public int ShowContainerUI(int fShow) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ShowContainerUI"); + // Do nothing + return NativeMethods.S_OK; + } + + public int QueryInsertObject(ref Guid lpclsid, IntPtr lpstg, int cp) + { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryInsertObject(" + lpclsid.ToString() + ")"); + + try + { + IntSecurity.UnmanagedCode.Demand(); + return NativeMethods.S_OK; + } + catch (SecurityException) + { + // We do not have unmanaged code access, so + // we need to restrict what we allow to be loaded + } + Guid realClsid = new Guid(); + + + int hr = UnsafeNativeMethods.ReadClassStg(new HandleRef(null, lpstg), ref realClsid); + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "real clsid:" + realClsid.ToString() + " (hr=" + hr.ToString("X", CultureInfo.InvariantCulture) + ")"); + + if (!NativeMethods.Succeeded(hr)) + { + return NativeMethods.S_FALSE; + } + + if (realClsid == Guid.Empty) + { + realClsid = lpclsid; + } + + switch (realClsid.ToString().ToUpper(CultureInfo.InvariantCulture)) + { + case "00000315-0000-0000-C000-000000000046": // Metafile + case "00000316-0000-0000-C000-000000000046": // DIB + case "00000319-0000-0000-C000-000000000046": // EMF + case "0003000A-0000-0000-C000-000000000046": //BMP + return NativeMethods.S_OK; + default: + Debug.WriteLineIf(RichTextDbg.TraceVerbose, " denying '" + lpclsid.ToString() + "' from being inserted due to security restrictions"); + return NativeMethods.S_FALSE; + } + } + + public int DeleteObject(IntPtr lpoleobj) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::DeleteObject"); + // Do nothing + return NativeMethods.S_OK; + } + + public int QueryAcceptData(IComDataObject lpdataobj, + /* CLIPFORMAT* */ IntPtr lpcfFormat, int reco, + int fReally, IntPtr hMetaPict) { + + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryAcceptData(reco=" + reco + ")"); + + if (reco == NativeMethods.RECO_DROP) { + if (owner.AllowDrop || owner.EnableAutoDragDrop) { + + MouseButtons b = Control.MouseButtons; + Keys k = Control.ModifierKeys; + + int keyState = 0; + + // Due to the order in which we get called, we have to set up the keystate here. + // First GetDragDropEffect is called with grfKeyState == 0, and then + // QueryAcceptData is called. Since this is the time we want to fire + // OnDragEnter, but we have yet to get the keystate, we set it up ourselves. + + if ((b & MouseButtons.Left) == MouseButtons.Left) { + keyState |= NativeMethods.MK_LBUTTON; + } + + if ((b & MouseButtons.Right) == MouseButtons.Right) { + keyState |= NativeMethods.MK_RBUTTON; + } + + if ((b & MouseButtons.Middle) == MouseButtons.Middle) { + keyState |= NativeMethods.MK_MBUTTON; + } + + if ((k & Keys.Control) == Keys.Control) { + keyState |= NativeMethods.MK_CONTROL; + } + + if ((k & Keys.Shift) == Keys.Shift) { + keyState |= NativeMethods.MK_SHIFT; + } + + lastDataObject = new DataObject(lpdataobj); + + if (!owner.EnableAutoDragDrop) { + lastEffect = DragDropEffects.None; + } + + DragEventArgs e = new DragEventArgs(lastDataObject, + keyState, + Control.MousePosition.X, + Control.MousePosition.Y, + DragDropEffects.All, + lastEffect); + if (fReally == 0) { + // we are just querying + + // We can get here without GetDragDropEffects actually being called first. + // This happens when you drag/drop between two rtb's. Say you drag from rtb1 to rtb2. + // GetDragDropEffects will first be called for rtb1, then QueryAcceptData for rtb1 just + // like in the local drag case. Then you drag into rtb2. rtb2 will first be called in this method, + // and not GetDragDropEffects. Now lastEffect is initialized to None for rtb2, so we would not allow + // the drag. Thus we need to set the effect here as well. + e.Effect = ((keyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; + owner.OnDragEnter(e); + } + else { + owner.OnDragDrop(e); + lastDataObject = null; + } + + lastEffect = e.Effect; + if (e.Effect == DragDropEffects.None) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data"); + return NativeMethods.E_FAIL; + } + else { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tAccept data"); + return NativeMethods.S_OK; + } + } + else { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data, allowdrop == false"); + lastDataObject = null; + return NativeMethods.E_FAIL; + } + } + else { + return NativeMethods.E_NOTIMPL; + } + } + + public int ContextSensitiveHelp(int fEnterMode) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ContextSensitiveHelp"); + return NativeMethods.E_NOTIMPL; + } + + public int GetClipboardData(NativeMethods.CHARRANGE lpchrg, int reco, + IntPtr lplpdataobj) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetClipboardData"); + return NativeMethods.E_NOTIMPL; + } + + public int GetDragDropEffect(bool fDrag, int grfKeyState, ref int pdwEffect) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetDragDropEffect"); + + if (owner.AllowDrop || owner.EnableAutoDragDrop) { + + if (fDrag && grfKeyState == 0) { + // This is the very first call we receive in a Drag-Drop operation, + // so we will let the control know what we support. + + // Note that we haven't gotten any data yet, so we will let QueryAcceptData + // do the OnDragEnter. Note too, that grfKeyState does not yet reflect the + // current keystate + if (owner.EnableAutoDragDrop) { + lastEffect = (DragDropEffects.All | DragDropEffects.None); + } + else + lastEffect = DragDropEffects.None; + + } + else { + // We are either dragging over or dropping + + + // The below is the complete reverse of what the docs on MSDN suggest, + // but if we follow the docs, we would be firing OnDragDrop all the + // time instead of OnDragOver (see bug 99294). MSDN seems to be wrong here. + + // drag - fDrag = false, grfKeyState != 0 + // drop - fDrag = false, grfKeyState = 0 + // We only care about the drag. + // + // When we drop, lastEffect will have the right state + if (!fDrag && lastDataObject != null && grfKeyState != 0) { + + DragEventArgs e = new DragEventArgs(lastDataObject, + grfKeyState, + Control.MousePosition.X, + Control.MousePosition.Y, + DragDropEffects.All, + lastEffect); + + // Now tell which of the allowable effects we want to use, but only if we are not already none + if (lastEffect != DragDropEffects.None) { + e.Effect = ((grfKeyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; + } + + owner.OnDragOver(e); + lastEffect = e.Effect; + } + } + + pdwEffect = (int)lastEffect; + + } + else { + pdwEffect = (int)DragDropEffects.None; + } + return NativeMethods.S_OK; + } + + public int GetContextMenu(short seltype, IntPtr lpoleobj, NativeMethods.CHARRANGE lpchrg, out IntPtr hmenu) { + Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetContextMenu"); + ContextMenu cm = owner.ContextMenu; + if (cm == null || owner.ShortcutsEnabled == false) + hmenu = IntPtr.Zero; + else { + cm.sourceControl = owner; + cm.OnPopup(EventArgs.Empty); + // RichEd calls DestroyMenu after displaying the context menu + IntPtr handle = cm.Handle; + // if another control shares the same context menu + // then we have to mark the context menu's handles empty because + // RichTextBox will delete the menu handles once the popup menu is dismissed. + Menu menu = cm; + while (true) { + int i = 0; + int count = menu.ItemCount; + for (; i< count; i++) { + if (menu.items[i].handle != IntPtr.Zero) { + menu = menu.items[i]; + break; + } + } + if (i == count) { + menu.handle = IntPtr.Zero; + menu.created = false; + if (menu == cm) + break; + else + menu = ((MenuItem) menu).Menu; + } + } + + hmenu = handle; + } + + return NativeMethods.S_OK; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxConstants.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxConstants.cs new file mode 100644 index 000000000..d6faca5c8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxConstants.cs @@ -0,0 +1,650 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +//=--------------------------------------------------------------------------= +// RichTextBoxConstants.cs +//=--------------------------------------------------------------------------= +// Copyright (c) 1997 Microsoft Corporation. All Rights Reserved. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +//=--------------------------------------------------------------------------= + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + internal static class RichTextBoxConstants { + + // flags for enum that we don't want public + // + internal const int RTB_HORIZ = 0x0001; + internal const int RTB_VERT = 0x0002; + internal const int RTB_FORCE = 0x0010; + + /* Richedit dll name */ + internal const string RICHEDIT_DLL10 = "RichEd32.DLL"; + internal const string RICHEDIT_DLL20 = "RichEd20.DLL"; + internal const string RICHEDIT_DLL30 = RICHEDIT_DLL20; + internal const string RICHEDIT_DLL41 = "MsftEdit.DLL"; + + /* Richedit1.0 Window Class */ + internal const string RICHEDIT_CLASS10A = "RICHEDIT"; + + /* Richedit2.0 Window Class. */ + internal const string RICHEDIT_CLASS20A = "RichEdit20A"; + internal const string RICHEDIT_CLASS20W = "RichEdit20W"; + + /* Richedit3.0 Window Class */ + internal const string RICHEDIT_CLASS30A = RICHEDIT_CLASS20A; + internal const string RICHEDIT_CLASS30W = RICHEDIT_CLASS20W; + + internal const string DLL_RICHEDIT = RICHEDIT_DLL30; + internal const string WC_RICHEDITA = RICHEDIT_CLASS30A; + internal const string WC_RICHEDITW = RICHEDIT_CLASS30W; + + /* Richedit4.1 Window Class */ + internal const string RICHEDIT_CLASS41A = "RICHEDIT50A"; + internal const string RICHEDIT_CLASS41W = "RICHEDIT50W"; + + internal const string DLL_RICHEDIT_41 = RICHEDIT_DLL41; + internal const string WC_RICHEDITA_41 = RICHEDIT_CLASS41A; + internal const string WC_RICHEDITW_41 = RICHEDIT_CLASS41W; + + /* RichTextBox messages */ + internal const int WM_CONTEXTMENU = 0x007B; + + internal const int WM_PRINTCLIENT = 0x0318; + + internal const int EM_GETLIMITTEXT = (NativeMethods.WM_USER + 37); + + internal const int EM_POSFROMCHAR = (NativeMethods.WM_USER + 38); + internal const int EM_CHARFROMPOS = (NativeMethods.WM_USER + 39); + + internal const int EM_SCROLLCARET = (NativeMethods.WM_USER + 49); + internal const int EM_CANPASTE = (NativeMethods.WM_USER + 50); + internal const int EM_DISPLAYBAND = (NativeMethods.WM_USER + 51); + internal const int EM_EXGETSEL = (NativeMethods.WM_USER + 52); + internal const int EM_EXLIMITTEXT = (NativeMethods.WM_USER + 53); + internal const int EM_EXLINEFROMCHAR = (NativeMethods.WM_USER + 54); + internal const int EM_EXSETSEL = (NativeMethods.WM_USER + 55); + internal const int EM_FINDTEXT = (NativeMethods.WM_USER + 56); + internal const int EM_FORMATRANGE = (NativeMethods.WM_USER + 57); + internal const int EM_GETCHARFORMAT = (NativeMethods.WM_USER + 58); + internal const int EM_GETEVENTMASK = (NativeMethods.WM_USER + 59); + internal const int EM_GETOLEINTERFACE = (NativeMethods.WM_USER + 60); + internal const int EM_GETPARAFORMAT = (NativeMethods.WM_USER + 61); + internal const int EM_GETSELTEXT = (NativeMethods.WM_USER + 62); + internal const int EM_HIDESELECTION = (NativeMethods.WM_USER + 63); + internal const int EM_PASTESPECIAL = (NativeMethods.WM_USER + 64); + internal const int EM_REQUESTRESIZE = (NativeMethods.WM_USER + 65); + internal const int EM_SELECTIONTYPE = (NativeMethods.WM_USER + 66); + internal const int EM_SETBKGNDCOLOR = (NativeMethods.WM_USER + 67); + internal const int EM_SETCHARFORMAT = (NativeMethods.WM_USER + 68); + internal const int EM_SETEVENTMASK = (NativeMethods.WM_USER + 69); + internal const int EM_SETOLECALLBACK = (NativeMethods.WM_USER + 70); + internal const int EM_SETPARAFORMAT = (NativeMethods.WM_USER + 71); + internal const int EM_SETTARGETDEVICE = (NativeMethods.WM_USER + 72); + internal const int EM_STREAMIN = (NativeMethods.WM_USER + 73); + internal const int EM_STREAMOUT = (NativeMethods.WM_USER + 74); + internal const int EM_GETTEXTRANGE = (NativeMethods.WM_USER + 75); + internal const int EM_FINDWORDBREAK = (NativeMethods.WM_USER + 76); + internal const int EM_SETOPTIONS = (NativeMethods.WM_USER + 77); + internal const int EM_GETOPTIONS = (NativeMethods.WM_USER + 78); + internal const int EM_FINDTEXTEX = (NativeMethods.WM_USER + 79); + internal const int EM_GETWORDBREAKPROCEX = (NativeMethods.WM_USER + 80); + internal const int EM_SETWORDBREAKPROCEX = (NativeMethods.WM_USER + 81); + + // Richedit v2.0 messages + internal const int EM_SETUNDOLIMIT = (NativeMethods.WM_USER + 82); + internal const int EM_REDO = (NativeMethods.WM_USER + 84); + internal const int EM_CANREDO = (NativeMethods.WM_USER + 85); + internal const int EM_GETUNDONAME = (NativeMethods.WM_USER + 86); + internal const int EM_GETREDONAME = (NativeMethods.WM_USER + 87); + internal const int EM_STOPGROUPTYPING = (NativeMethods.WM_USER + 88); + + internal const int EM_SETTEXTMODE = (NativeMethods.WM_USER + 89); + internal const int EM_GETTEXTMODE = (NativeMethods.WM_USER + 90); + + internal const int EM_AUTOURLDETECT = (NativeMethods.WM_USER + 91); + internal const int EM_GETAUTOURLDETECT = (NativeMethods.WM_USER + 92); + internal const int EM_SETPALETTE = (NativeMethods.WM_USER + 93); + internal const int EM_GETTEXTEX = (NativeMethods.WM_USER + 94); + internal const int EM_GETTEXTLENGTHEX = (NativeMethods.WM_USER + 95); + + // Asia specific messages + internal const int EM_SETPUNCTUATION = (NativeMethods.WM_USER + 100); + internal const int EM_GETPUNCTUATION = (NativeMethods.WM_USER + 101); + internal const int EM_SETWORDWRAPMODE = (NativeMethods.WM_USER + 102); + internal const int EM_GETWORDWRAPMODE = (NativeMethods.WM_USER + 103); + internal const int EM_SETIMECOLOR = (NativeMethods.WM_USER + 104); + internal const int EM_GETIMECOLOR = (NativeMethods.WM_USER + 105); + internal const int EM_SETIMEOPTIONS = (NativeMethods.WM_USER + 106); + internal const int EM_GETIMEOPTIONS = (NativeMethods.WM_USER + 107); + internal const int EM_CONVPOSITION = (NativeMethods.WM_USER + 108); + + internal const int EM_SETLANGOPTIONS = (NativeMethods.WM_USER + 120); + internal const int EM_GETLANGOPTIONS = (NativeMethods.WM_USER + 121); + internal const int EM_GETIMECOMPMODE = (NativeMethods.WM_USER + 122); + + internal const int EM_FINDTEXTW = (NativeMethods.WM_USER + 123); + internal const int EM_FINDTEXTEXW = (NativeMethods.WM_USER + 124); + + //Rich TextBox 3.0 Asia msgs + internal const int EM_RECONVERSION = (NativeMethods.WM_USER + 125); + internal const int EM_SETIMEMODEBIAS = (NativeMethods.WM_USER + 126); + internal const int EM_GETIMEMODEBIAS = (NativeMethods.WM_USER + 127); + + // BiDi Specific messages + internal const int EM_SETBIDIOPTIONS = (NativeMethods.WM_USER + 200); + internal const int EM_GETBIDIOPTIONS = (NativeMethods.WM_USER + 201); + + internal const int EM_SETTYPOGRAPHYOPTIONS = (NativeMethods.WM_USER + 202); + internal const int EM_GETTYPOGRAPHYOPTIONS = (NativeMethods.WM_USER + 203); + + // Extended TextBox style specific messages + internal const int EM_SETEDITSTYLE = (NativeMethods.WM_USER + 204); + internal const int EM_GETEDITSTYLE = (NativeMethods.WM_USER + 205); + + // Ole Objects Disabling message + internal const int EM_SETQUERYRTFOBJ = (NativeMethods.WM_USER + 270); + + // Extended edit style masks + internal const int SES_EMULATESYSEDIT = 1; + internal const int SES_BEEPONMAXTEXT = 2; + internal const int SES_EXTENDBACKCOLOR = 4; + internal const int SES_MAPCPS = 8; + internal const int SES_EMULATE10 = 16; + internal const int SES_USECRLF = 32; + internal const int SES_USEAIMM = 64; + internal const int SES_NOIME = 128; + internal const int SES_ALLOWBEEPS = 256; + internal const int SES_UPPERCASE = 512; + internal const int SES_LOWERCASE = 1024; + internal const int SES_NOINPUTSEQUENCECHK = 2048; + internal const int SES_BIDI = 4096; + internal const int SES_SCROLLONKILLFOCUS = 8192; + internal const int SES_XLTCRCRLFTOCR = 16384; + + // Options for EM_SETLANGOPTIONS and EM_GETLANGOPTIONS + internal const int IMF_AUTOKEYBOARD = 0x0001; + internal const int IMF_AUTOFONT = 0x0002; + internal const int IMF_IMECANCELCOMPLETE = 0x0004; // high completes the comp string when aborting, low cancels. + internal const int IMF_IMEALWAYSSENDNOTIFY = 0x0008; + internal const int IMF_AUTOFONTSIZEADJUST = 0x0010; + internal const int IMF_UIFONTS = 0x0020; + internal const int IMF_DUALFONT = 0x0080; + + // Values for EM_GETIMECOMPMODE + internal const int ICM_NOTOPEN = 0x0000; + internal const int ICM_LEVEL3 = 0x0001; + internal const int ICM_LEVEL2 = 0x0002; + internal const int ICM_LEVEL2_5 = 0x0003; + internal const int ICM_LEVEL2_SUI = 0x0004; + + // Pegasus outline mode messages (RE 3.0) + + // Outline mode message + internal const int EM_OUTLINE = NativeMethods.WM_USER + 220; + + // Message for getting and restoring scroll pos + internal const int EM_GETSCROLLPOS = NativeMethods.WM_USER + 221; + internal const int EM_SETSCROLLPOS = NativeMethods.WM_USER + 222; + + // Change fontsize in current selection by wparam + internal const int EM_SETFONTSIZE = NativeMethods.WM_USER + 223; + internal const int EM_GETZOOM = NativeMethods.WM_USER + 224; + internal const int EM_SETZOOM = NativeMethods.WM_USER + 225; + + // Outline mode wparam values + internal const int EMO_EXIT = 0; // enter normal mode, lparam ignored + internal const int EMO_ENTER = 1; // enter outline mode, lparam ignored + internal const int EMO_PROMOTE = 2; // LOWORD(lparam) == 0 ==> + // promote to body-text + // LOWORD(lparam) != 0 ==> + // promote/demote current selection + // by indicated number of levels + internal const int EMO_EXPAND = 3; // HIWORD(lparam) = EMO_EXPANDSELECTION + // -> expands selection to level + // indicated in LOWORD(lparam) + // LOWORD(lparam) = -1/+1 corresponds + // to collapse/expand button presses + // in winword (other values are + // equivalent to having pressed these + // buttons more than once) + // HIWORD(lparam) = EMO_EXPANDDOCUMENT + // -> expands whole document to + // indicated level + internal const int EMO_MOVESELECTION = 4; // LOWORD(lparam) != 0 -> move current + // selection up/down by indicated + // amount + internal const int EMO_GETVIEWMODE = 5; // Returns VM_NORMAL or VM_OUTLINE + + // EMO_EXPAND options + + internal const int EMO_EXPANDSELECTION = 0; + internal const int EMO_EXPANDDOCUMENT = 1; + internal const int VM_NORMAL = 4; // Agrees with RTF \viewkindN + internal const int VM_OUTLINE = 2; + + // New notifications + internal const int EN_MSGFILTER = 0x0700; + internal const int EN_REQUESTRESIZE = 0x0701; + internal const int EN_SELCHANGE = 0x0702; + internal const int EN_DROPFILES = 0x0703; + internal const int EN_PROTECTED = 0x0704; + internal const int EN_CORRECTTEXT = 0x0705; /* PenWin specific */ + internal const int EN_STOPNOUNDO = 0x0706; + internal const int EN_IMECHANGE = 0x0707; /* Asia specific */ + internal const int EN_SAVECLIPBOARD = 0x0708; + internal const int EN_OLEOPFAILED = 0x0709; + internal const int EN_OBJECTPOSITIONS = 0x070a; + internal const int EN_LINK = 0x070b; + internal const int EN_DRAGDROPDONE = 0x070c; + internal const int EN_PARAGRAPHEXPANDED = 0x070d; + + // BiDi specific notifications + internal const int EN_ALIGNLTR = 0x0710; + internal const int EN_ALIGNRTL = 0x0711; + + // Event notification masks */ + internal const int ENM_NONE = 0x00000000; + internal const int ENM_CHANGE = 0x00000001; + internal const int ENM_UPDATE = 0x00000002; + internal const int ENM_SCROLL = 0x00000004; + internal const int ENM_KEYEVENTS = 0x00010000; + internal const int ENM_MOUSEEVENTS = 0x00020000; + internal const int ENM_REQUESTRESIZE = 0x00040000; + internal const int ENM_SELCHANGE = 0x00080000; + internal const int ENM_DROPFILES = 0x00100000; + internal const int ENM_PROTECTED = 0x00200000; + internal const int ENM_CORRECTTEXT = 0x00400000; /* PenWin specific */ + internal const int ENM_SCROLLEVENTS = 0x00000008; + internal const int ENM_DRAGDROPDONE = 0x00000010; + internal const int ENM_PARAGRAPHEXPANDED = 0x00000020; + + /* Asia specific notification mask */ + internal const int ENM_IMECHANGE = 0x00800000; /* unused by RE2.0 */ + internal const int ENM_LANGCHANGE = 0x01000000; + internal const int ENM_OBJECTPOSITIONS = 0x02000000; + internal const int ENM_LINK = 0x04000000; + + /* New edit control styles */ + internal const int ES_SAVESEL = 0x00008000; + internal const int ES_SUNKEN = 0x00004000; + internal const int ES_DISABLENOSCROLL = 0x00002000; + /* same as WS_MAXIMIZE, but that doesn't make sense so we re-use the value */ + internal const int ES_SELECTIONBAR = 0x01000000; + /* same as ES_UPPERCASE, but re-used to completely disable OLE drag'n'drop */ + internal const int ES_NOOLEDRAGDROP = 0x00000008; + + /* New edit control extended style */ + internal const int ES_EX_NOCALLOLEINIT = 0x01000000; + + /* These flags are used in FE Windows */ + internal const int ES_VERTICAL = 0x00400000; // NOT IN RE3.0/2.0 + internal const int ES_NOIME = 0x00080000; + internal const int ES_SELFIME = 0x00040000; + + /* TextBox control options */ + internal const int ECO_AUTOWORDSELECTION = 0x00000001; + internal const int ECO_AUTOVSCROLL = 0x00000040; + internal const int ECO_AUTOHSCROLL = 0x00000080; + internal const int ECO_NOHIDESEL = 0x00000100; + internal const int ECO_READONLY = 0x00000800; + internal const int ECO_WANTRETURN = 0x00001000; + internal const int ECO_SAVESEL = 0x00008000; + internal const int ECO_SELECTIONBAR = 0x01000000; // guessing this is selection margin + internal const int ECO_VERTICAL = 0x00400000; /* FE specific */ + + + /* ECO operations */ + internal const int ECOOP_SET = 0x0001; + internal const int ECOOP_OR = 0x0002; + internal const int ECOOP_AND = 0x0003; + internal const int ECOOP_XOR = 0x0004; + + /* new word break function actions */ + internal const int WB_CLASSIFY = 3; + internal const int WB_MOVEWORDLEFT = 4; + internal const int WB_MOVEWORDRIGHT = 5; + internal const int WB_LEFTBREAK = 6; + internal const int WB_RIGHTBREAK = 7; + + /* Asia specific flags */ + internal const int WB_MOVEWORDPREV = 4; + internal const int WB_MOVEWORDNEXT = 5; + internal const int WB_PREVBREAK = 6; + internal const int WB_NEXTBREAK = 7; + + internal const int PC_FOLLOWING = 1; + internal const int PC_LEADING = 2; + internal const int PC_OVERFLOW = 3; + internal const int PC_DELIMITER = 4; + + internal const int WBF_WORDWRAP = 0x010; + internal const int WBF_WORDBREAK = 0x020; + internal const int WBF_OVERFLOW = 0x040; + internal const int WBF_LEVEL1 = 0x080; + internal const int WBF_LEVEL2 = 0x100; + internal const int WBF_CUSTOM = 0x200; + + /* for use with EM_GET/SETTEXTMODE */ + internal const int TM_PLAINTEXT = 1; + internal const int TM_RICHTEXT = 2; /* default behavior */ + internal const int TM_SINGLELEVELUNDO = 4; + internal const int TM_MULTILEVELUNDO = 8; /* default behavior */ + internal const int TM_SINGLECODEPAGE = 16; + internal const int TM_MULTICODEPAGE = 32; /* default behavior */ + + /* Asia specific flags */ + internal const int IMF_FORCENONE = 0x0001; + internal const int IMF_FORCEENABLE = 0x0002; + internal const int IMF_FORCEDISABLE = 0x0004; + internal const int IMF_CLOSESTATUSWINDOW = 0x0008; + internal const int IMF_VERTICAL = 0x0020; + internal const int IMF_FORCEACTIVE = 0x0040; + internal const int IMF_FORCEINACTIVE = 0x0080; + internal const int IMF_FORCEREMEMBER = 0x0100; + internal const int IMF_MULTIPLEEDIT = 0x0400; + + /* Word break flags (used with WB_CLASSIFY) */ + internal const int WBF_CLASS = 0x0F; + internal const int WBF_ISWHITE = 0x10; + internal const int WBF_BREAKLINE = 0x20; + internal const int WBF_BREAKAFTER = 0x40; + + internal const int cchTextLimitDefault = 32767; + + /* CHARFORMAT masks */ + internal const int CFM_BOLD = 0x00000001; + internal const int CFM_ITALIC = 0x00000002; + internal const int CFM_UNDERLINE = 0x00000004; + internal const int CFM_STRIKEOUT = 0x00000008; + internal const int CFM_PROTECTED = 0x00000010; + internal const int CFM_LINK = 0x00000020; /* Exchange hyperlink extension */ + internal const int CFM_SIZE = unchecked((int)0x80000000); + internal const int CFM_COLOR = 0x40000000; + internal const int CFM_FACE = 0x20000000; + internal const int CFM_OFFSET = 0x10000000; + internal const int CFM_CHARSET = 0x08000000; + + /* CHARFORMAT effects */ + internal const int CFE_BOLD = 0x0001; + internal const int CFE_ITALIC = 0x0002; + internal const int CFE_UNDERLINE = 0x0004; + internal const int CFE_STRIKEOUT = 0x0008; + internal const int CFE_PROTECTED = 0x0010; + internal const int CFE_LINK = 0x0020; + internal const int CFE_AUTOCOLOR = 0x40000000; /* NOTE: this corresponds to */ + /* CFM_COLOR, which controls it */ + internal const int yHeightCharPtsMost = 1638; + + /* EM_SETCHARFORMAT wparam masks */ + internal const int SCF_SELECTION = 0x0001; + internal const int SCF_WORD = 0x0002; + internal const int SCF_DEFAULT = 0x0000; // set the default charformat or paraformat + internal const int SCF_ALL = 0x0004; // not valid with SCF_SELECTION or SCF_WORD + internal const int SCF_USEUIRULES = 0x0008; // modifier for SCF_SELECTION; says that + // the format came from a toolbar, etc. and + // therefore UI formatting rules should be + // used instead of strictly formatting the + // selection. + + /* stream formats */ + internal const int SF_TEXT = 0x0001; + internal const int SF_RTF = 0x0002; + internal const int SF_RTFNOOBJS = 0x0003; /* outbound only */ + internal const int SF_TEXTIZED = 0x0004; /* outbound only */ + internal const int SF_UNICODE = 0x0010; /* Unicode file of some kind */ + + /* Flag telling stream operations to operate on the selection only */ + /* EM_STREAMIN will replace the current selection */ + /* EM_STREAMOUT will stream out the current selection */ + internal const int SFF_SELECTION = 0x8000; + + /* Flag telling stream operations to operate on the common RTF keyword only */ + /* EM_STREAMIN will accept the only common RTF keyword */ + /* EM_STREAMOUT will stream out the only common RTF keyword */ + internal const int SFF_PLAINRTF = 0x4000; + + /* all paragraph measurements are in twips */ + + internal const int MAX_TAB_STOPS = 32; + internal const int lDefaultTab = 720; + + /* PARAFORMAT mask values */ + internal const int PFM_STARTINDENT = 0x00000001; + internal const int PFM_RIGHTINDENT = 0x00000002; + internal const int PFM_OFFSET = 0x00000004; + internal const int PFM_ALIGNMENT = 0x00000008; + internal const int PFM_TABSTOPS = 0x00000010; + internal const int PFM_NUMBERING = 0x00000020; + internal const int PFM_OFFSETINDENT = unchecked((int)0x80000000); + + /* PARAFORMAT numbering options */ + internal const int PFN_BULLET = 0x0001; + + /* PARAFORMAT alignment options */ + internal const int PFA_LEFT = 0x0001; + internal const int PFA_RIGHT = 0x0002; + internal const int PFA_CENTER = 0x0003; + + /* CHARFORMAT and PARAFORMAT "ALL" masks + CFM_COLOR mirrors CFE_AUTOCOLOR, a little + code to easily deal with autocolor */ + internal const int CFM_EFFECTS = (CFM_BOLD | CFM_ITALIC | + CFM_UNDERLINE | CFM_COLOR | + CFM_STRIKEOUT | CFE_PROTECTED | + CFM_LINK); + internal const int CFM_ALL = (CFM_EFFECTS | CFM_SIZE | + CFM_FACE | CFM_OFFSET | CFM_CHARSET); + internal const int PFM_ALL = (PFM_STARTINDENT | PFM_RIGHTINDENT | + PFM_OFFSET | PFM_ALIGNMENT | + PFM_TABSTOPS | PFM_NUMBERING | + PFM_OFFSETINDENT); + + /* New masks and effects -- a parenthesized asterisk indicates that + the data is stored by RichEdit2.0, but not displayed */ + + internal const int CFM_SMALLCAPS = 0x0040; /* (*) */ + internal const int CFM_ALLCAPS = 0x0080; /* (*) */ + internal const int CFM_HIDDEN = 0x0100; /* (*) */ + internal const int CFM_OUTLINE = 0x0200; /* (*) */ + internal const int CFM_SHADOW = 0x0400; /* (*) */ + internal const int CFM_EMBOSS = 0x0800; /* (*) */ + internal const int CFM_IMPRINT = 0x1000; /* (*) */ + internal const int CFM_DISABLED = 0x2000; + internal const int CFM_REVISED = 0x4000; + + internal const int CFM_BACKCOLOR = 0x04000000; + internal const int CFM_LCID = 0x02000000; + internal const int CFM_UNDERLINETYPE = 0x00800000; /* (*) */ + internal const int CFM_WEIGHT = 0x00400000; + internal const int CFM_SPACING = 0x00200000; /* (*) */ + internal const int CFM_KERNING = 0x00100000; /* (*) */ + internal const int CFM_STYLE = 0x00080000; /* (*) */ + internal const int CFM_ANIMATION = 0x00040000; /* (*) */ + internal const int CFM_REVAUTHOR = 0x00008000; + + internal const int CFE_SUBSCRIPT = 0x00010000; /* Superscript and subscript are */ + internal const int CFE_SUPERSCRIPT = 0x00020000; /* mutually exclusive */ + + internal const int CFM_SUBSCRIPT = (CFE_SUBSCRIPT | CFE_SUPERSCRIPT); + internal const int CFM_SUPERSCRIPT = CFM_SUBSCRIPT; + + internal const int CFM_EFFECTS2 = (CFM_EFFECTS | CFM_DISABLED | + CFM_SMALLCAPS | CFM_ALLCAPS | + CFM_HIDDEN | CFM_OUTLINE | + CFM_SHADOW | CFM_EMBOSS | + CFM_IMPRINT | CFM_DISABLED | + CFM_REVISED | CFM_SUBSCRIPT | + CFM_SUPERSCRIPT | CFM_BACKCOLOR); + + internal const int CFM_ALL2 = (CFM_ALL | CFM_EFFECTS2 | + CFM_BACKCOLOR | CFM_LCID | + CFM_UNDERLINETYPE | CFM_WEIGHT | + CFM_REVAUTHOR | CFM_SPACING | + CFM_KERNING | CFM_STYLE | + CFM_ANIMATION); + + internal const int CFE_SMALLCAPS = CFM_SMALLCAPS; + internal const int CFE_ALLCAPS = CFM_ALLCAPS; + internal const int CFE_HIDDEN = CFM_HIDDEN; + internal const int CFE_OUTLINE = CFM_OUTLINE; + internal const int CFE_SHADOW = CFM_SHADOW; + internal const int CFE_EMBOSS = CFM_EMBOSS; + internal const int CFE_IMPRINT = CFM_IMPRINT; + internal const int CFE_DISABLED = CFM_DISABLED; + internal const int CFE_REVISED = CFM_REVISED; + + /* NOTE: CFE_AUTOCOLOR and CFE_AUTOBACKCOLOR correspond to CFM_COLOR and + CFM_BACKCOLOR, respectively, which control them */ + internal const int CFE_AUTOBACKCOLOR = CFM_BACKCOLOR; + + /* Underline types */ + internal const int CFU_CF1UNDERLINE = 0xFF; /* map charformat's bit underline to CF2.*/ + internal const int CFU_INVERT = 0xFE; /* For IME composition fake a selection.*/ + internal const int CFU_UNDERLINEDOTTED = 0x4; /* (*) displayed as ordinary underline */ + internal const int CFU_UNDERLINEDOUBLE = 0x3; /* (*) displayed as ordinary underline */ + internal const int CFU_UNDERLINEWORD = 0x2; /* (*) displayed as ordinary underline */ + internal const int CFU_UNDERLINE = 0x1; + internal const int CFU_UNDERLINENONE = 0; + + /* PARAFORMAT 2.0 masks and effects */ + + internal const int PFM_SPACEBEFORE = 0x00000040; + internal const int PFM_SPACEAFTER = 0x00000080; + internal const int PFM_LINESPACING = 0x00000100; + internal const int PFM_STYLE = 0x00000400; + internal const int PFM_BORDER = 0x00000800; /* (*) */ + internal const int PFM_SHADING = 0x00001000; /* (*) */ + internal const int PFM_NUMBERINGSTYLE = 0x00002000; /* (*) */ + internal const int PFM_NUMBERINGTAB = 0x00004000; /* (*) */ + internal const int PFM_NUMBERINGSTART = 0x00008000; /* (*) */ + + internal const int PFM_RTLPARA = 0x00010000; + internal const int PFM_KEEP = 0x00020000; /* (*) */ + internal const int PFM_KEEPNEXT = 0x00040000; /* (*) */ + internal const int PFM_PAGEBREAKBEFORE = 0x00080000; /* (*) */ + internal const int PFM_NOLINENUMBER = 0x00100000; /* (*) */ + internal const int PFM_NOWIDOWCONTROL = 0x00200000; /* (*) */ + internal const int PFM_DONOTHYPHEN = 0x00400000; /* (*) */ + internal const int PFM_SIDEBYSIDE = 0x00800000; /* (*) */ + + internal const int PFM_TABLE = unchecked((int)0xc0000000); /* (*) */ + + /* Note: PARAFORMAT has no effects */ + internal const int PFM_EFFECTS = (PFM_RTLPARA | PFM_KEEP | + PFM_KEEPNEXT | PFM_TABLE | + PFM_PAGEBREAKBEFORE | PFM_NOLINENUMBER | + PFM_NOWIDOWCONTROL | PFM_DONOTHYPHEN | + PFM_SIDEBYSIDE | PFM_TABLE); + + internal const int PFM_ALL2 = (PFM_ALL | PFM_EFFECTS | + PFM_SPACEBEFORE | PFM_SPACEAFTER | + PFM_LINESPACING | PFM_STYLE | + PFM_SHADING | PFM_BORDER | + PFM_NUMBERINGTAB | PFM_NUMBERINGSTART | + PFM_NUMBERINGSTYLE); + + internal const int PFE_RTLPARA = (PFM_RTLPARA >> 16); + internal const int PFE_KEEP = (PFM_KEEP >> 16); /* (*) */ + internal const int PFE_KEEPNEXT = (PFM_KEEPNEXT >> 16); /* (*) */ + internal const int PFE_PAGEBREAKBEFORE = (PFM_PAGEBREAKBEFORE >> 16); /* (*) */ + internal const int PFE_NOLINENUMBER = (PFM_NOLINENUMBER >> 16); /* (*) */ + internal const int PFE_NOWIDOWCONTROL = (PFM_NOWIDOWCONTROL >> 16); /* (*) */ + internal const int PFE_DONOTHYPHEN = (PFM_DONOTHYPHEN >> 16); /* (*) */ + internal const int PFE_SIDEBYSIDE = (PFM_SIDEBYSIDE >> 16); /* (*) */ + + internal const int PFE_TABLEROW = 0xc000; /* These 3 options are mutually */ + internal const int PFE_TABLECELLEND = 0x8000; /* exclusive and each imply */ + internal const int PFE_TABLECELL = 0x4000; /* that para is part of a table*/ + + /* + * PARAFORMAT numbering options (values for wNumbering): + * + * Numbering Type Value Meaning + * tomNoNumbering 0 Turn off paragraph numbering + * tomNumberAsLCLetter 1 a, b, c, ... + * tomNumberAsUCLetter 2 A, B, C, ... + * tomNumberAsLCRoman 3 i, ii, iii, ... + * tomNumberAsUCRoman 4 I, II, III, ... + * tomNumberAsSymbols 5 default is bullet + * tomNumberAsNumber 6 0, 1, 2, ... + * tomNumberAsSequence 7 tomNumberingStart is first Unicode to use + * + * Other valid Unicode chars are Unicodes for bullets. + */ + internal const int PFA_JUSTIFY = 4; /* New paragraph-alignment option 2.0 (*) */ + + internal const int SEL_EMPTY = 0x0000; + internal const int SEL_TEXT = 0x0001; + internal const int SEL_OBJECT = 0x0002; + internal const int SEL_MULTICHAR = 0x0004; + internal const int SEL_MULTIOBJECT = 0x0008; + + internal const int tomTrue = -1, + tomFalse = 0, + tomNone = 0, + tomUndefined = -9999999, + tomAutoColor = -9999997; + + /* used with IRichEditOleCallback::GetContextMenu, this flag will be + passed as a "selection type". It indicates that a context menu for + a right-mouse drag drop should be generated. The IOleObject parameter + will really be the IDataObject for the drop + */ + internal const int GCM_RIGHTMOUSEDROP = 0x8000; + + internal const int OLEOP_DOVERB = 1; + + /* clipboard formats - use as parameter to RegisterClipboardFormat() */ + internal const string CF_RTF = "Rich Text Format"; + internal const string CF_RTFNOOBJS = "Rich Text Format Without Objects"; + internal const string CF_RETEXTOBJ = "RichEdit Text and Objects"; + + /* UndoName info */ + internal const int UID_UNKNOWN = 0; + internal const int UID_TYPING = 1; + internal const int UID_DELETE = 2; + internal const int UID_DRAGDROP = 3; + internal const int UID_CUT = 4; + internal const int UID_PASTE = 5; + + /* flags for the GETEXTEX data structure */ + internal const int GT_DEFAULT = 0; + internal const int GT_USECRLF = 1; + + /* flags for the GETTEXTLENGTHEX data structure */ + internal const int GTL_DEFAULT = 0; /* do the default (return # of chars) */ + internal const int GTL_USECRLF = 1; /* compute answer using CRLFs for paragraphs*/ + internal const int GTL_PRECISE = 2; /* compute a precise answer */ + internal const int GTL_CLOSE = 4; /* fast computation of a "close" answer */ + internal const int GTL_NUMCHARS = 8; /* return the number of characters */ + internal const int GTL_NUMBYTES = 16; /* return the number of _bytes_ */ + + /* UNICODE embedding character */ +// disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal static readonly char WCH_EMBEDDING = (char)0xFFFC; +#pragma warning restore 0414 + + /* flags for the find text options */ + internal const int FR_DOWN = 0x00000001; + internal const int FR_WHOLEWORD = 0x00000002; + internal const int FR_MATCHCASE = 0x00000004; + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxFinds.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxFinds.cs new file mode 100644 index 000000000..4c01e93d2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxFinds.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies how the method works. + /// + /// + /// + [Flags] + public enum RichTextBoxFinds { + + /// + /// + /// + /// Find the text without any special characteristics. + /// + /// + /// + None = 0x00000000, + + /// + /// + /// + /// Match only a whole word. + /// + /// + WholeWord = 0x00000002, + + /// + /// + /// Match the case exactly. + /// + MatchCase = 0x00000004, + + /// + /// + /// + /// If the text is found, do not highlight it. + /// + /// + NoHighlight = 0x00000008, + + /// + /// + /// + /// Search from the end of the current selection to the beginning of the document. + /// + /// + Reverse = 0x00000010, + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxLanguageOptions.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxLanguageOptions.cs new file mode 100644 index 000000000..2649b4bc5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxLanguageOptions.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms +{ + /// + /// Rich edit control's option settings for Input Method Editor (IME) and + /// Asian language support + /// + [Flags] + public enum RichTextBoxLanguageOptions + { + /// + /// + /// If this flag is set, the control automatically changes fonts when the user + /// explicitly changes to a different keyboard layout. + /// + /// + AutoFont = 0x0002, + + /// + /// + /// Font-bound font sizes are scaled from insertion point size according to a script. + /// For example, Asian fonts are slightly larger than Western. This is the default. + /// + /// + AutoFontSizeAdjust = 0x0010, + + /// + /// + /// If this flag is set, the control automatically changes the keyboard layout when the + /// user explicitly changes to a different font, or when the user explicitly changes the insertion point to a new location in the text. + /// + /// + AutoKeyboard = 0x0001, + + /// + /// + /// Sets the control to dual-font mode. Used for Asian language text. The control + /// uses an English font for ASCII text and an Asian font for Asian text. + /// + /// + DualFont = 0x0080, + + /// + /// + /// Controls how Rich Edit notifies the client during IME composition: + /// 0: No EN_CHANGE or EN_SELCHANGE notifications during undetermined state. + /// Send notification when final string comes in. (default) + /// 1: Send EN_CHANGE and EN_SELCHANGE events during undetermined state. + /// + /// + ImeAlwaysSendNotify = 0x0008, + + /// + /// + /// This flag determines how the control uses the composition string of an IME + /// if the user cancels it. If this flag is set, the control discards the composition string. + /// If this flag is not set, the control uses the composition string as the result string. + /// + /// + ImeCancelComplete = 0x0004, + + /// + /// + /// Use UI default fonts. This option is turned off by default. + /// + /// + UIFonts = 0x0020 + } +} + diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxScrollBars.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxScrollBars.cs new file mode 100644 index 000000000..cc4d335e1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxScrollBars.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how a control displays scroll bars. + /// + /// + /// + public enum RichTextBoxScrollBars { + + /// + /// + /// + /// Never display scroll bars. + /// + /// + None = 0, + + /// + /// + /// + /// Display only a + /// horizontal scroll bar when needed. + /// + /// + /// + Horizontal = RichTextBoxConstants.RTB_HORIZ, + + /// + /// + /// + /// Display only a + /// vertical scroll bar when needed. + /// + /// + /// + Vertical = RichTextBoxConstants.RTB_VERT, + + /// + /// + /// + /// Display both a horizontal and a vertical scroll bar when needed. + /// + /// + Both = Horizontal | Vertical, + + /// + /// + /// + /// Always + /// display only a horizontal scroll bar. + /// + /// + /// + ForcedHorizontal = RichTextBoxConstants.RTB_FORCE | Horizontal, + + /// + /// + /// + /// Always display only a vertical scroll bar. + /// + /// + ForcedVertical = RichTextBoxConstants.RTB_FORCE | Vertical, + + /// + /// + /// + /// Always display both a horizontal and a vertical scroll bar. + /// + /// + ForcedBoth = ForcedHorizontal | ForcedVertical, + + // Be careful when adding new members -- this enum is part normal, part flags + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionAttribute.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionAttribute.cs new file mode 100644 index 000000000..5ed22eef0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionAttribute.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies whether any characters in the + /// current selection have the style or attribute. + /// + /// + /// + public enum RichTextBoxSelectionAttribute { + /// + /// + /// + /// Some but not all characters. + /// + /// + Mixed = -1, + + /// + /// + /// + /// No characters. + /// + /// + None = 0, + + /// + /// + /// + /// All characters. + /// + /// + All = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionTypes.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionTypes.cs new file mode 100644 index 000000000..121ca08e8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxSelectionTypes.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + + /// + /// + /// Defines the possible kinds selection types in a RichTextBox control. + /// The actual vale returned by RichTextBox.getSelType() is a combination + /// of any of the below options. + /// + /// + [Flags] + public enum RichTextBoxSelectionTypes { + /// + /// + /// The current selection is empty. + /// + Empty = 0, + + /// + /// + /// The current selection is text only. + /// + Text = 1, + + /// + /// + /// The current selection contains atleast one OLE object. + /// + Object = 2, + + /// + /// + /// The current selection contains more than one character. + /// + MultiChar = 4, + + /// + /// + /// The current selection contains more than one OLE object. + /// + MultiObject = 8, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxStreamType.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxStreamType.cs new file mode 100644 index 000000000..e7eef31d5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxStreamType.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// Defines the possible kinds of input/output streams used by RichTextBox + /// control's load/save mechansim. These stream options are also used the + /// control's text modification methods. + /// + /// + public enum RichTextBoxStreamType { + /// + /// + /// Rich Text Format (RTF). + /// + RichText = 0, + + /// + /// + /// Text with spaces in place of OLE objects. + /// + PlainText = 1, + + /// + /// + /// RTF with spaces in place of OLE object (valid only for saveFile). + /// + RichNoOleObjs = 2, + + /// + /// + /// Text with a text representation of OLE objects (valid only for saveFile). + /// + TextTextOleObjs = 3, + + /// + /// + /// Text with spaces in place of OLE objects, encoded in Unicode. + /// + UnicodePlainText = 4, + } +} diff --git a/WindowsForms/Managed/System/WinForms/RichTextBoxWordPunctuations.cs b/WindowsForms/Managed/System/WinForms/RichTextBoxWordPunctuations.cs new file mode 100644 index 000000000..9786e36ae --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RichTextBoxWordPunctuations.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// This class defines the possible kinds of punctuation tables that + /// can be used with the RichTextBox word wrapping and word breaking features. + /// + /* + CONSIDER: This should be marked with the Flags attribute, but we can't because our + code generator cannot emit code for OR'd combinations of flags. + + Flags */ + + + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum RichTextBoxWordPunctuations { + /// + /// + /// Use pre-defined Level 1 punctuation table as default. + /// + Level1 = 0x080, + + /// + /// + /// Use pre-defined Level 2 punctuation table as default. + /// + Level2 = 0x100, + + /// + /// + /// Use a custom defined punctuation table. + /// + Custom = 0x200, + + /// + /// + /// Used as a mask. + /// + All = Level1 | Level2 | Custom, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/RightToLeft.cs b/WindowsForms/Managed/System/WinForms/RightToLeft.cs new file mode 100644 index 000000000..b416b30a2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/RightToLeft.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System.Drawing; + + /// + /// + /// Specifies a value indicating whether the text appears + /// from right to + /// left, as when using Hebrew or Arabic fonts. + /// + public enum RightToLeft { + + /// + /// + /// + /// + /// The + /// + /// text reads + /// + /// from left to right. This is the default. + /// + /// + /// + No = 0, + + /// + /// + /// + /// The text reads from + /// right to left. + /// + /// + /// + Yes = 1, + + /// + /// + /// + /// The direction the + /// text appears in is inherited from the parent control. + /// + /// + /// + Inherit = 2 + } +} diff --git a/WindowsForms/Managed/System/WinForms/SafeNativeMethods.cs b/WindowsForms/Managed/System/WinForms/SafeNativeMethods.cs new file mode 100644 index 000000000..96bd48ac5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SafeNativeMethods.cs @@ -0,0 +1,922 @@ +///------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System; + using System.Security; + using System.Security.Permissions; + using System.Collections; + using System.IO; + using System.Text; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Versioning; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + + [SuppressUnmanagedCodeSecurity] + internal static class SafeNativeMethods { + + [DllImport("shlwapi.dll")] + [ResourceExposure(ResourceScope.None)] + public static extern int SHAutoComplete(HandleRef hwndEdit, int flags); + +/* +#if DEBUG + [DllImport(ExternDll.Shell32, EntryPoint="SHGetFileInfo")] + private static extern IntPtr IntSHGetFileInfo([MarshalAs(UnmanagedType.LPWStr)]string pszPath, int dwFileAttributes, NativeMethods.SHFILEINFO info, int cbFileInfo, int flags); + public static IntPtr SHGetFileInfo(string pszPath, int dwFileAttributes, NativeMethods.SHFILEINFO info, int cbFileInfo, int flags) { + IntPtr newHandle = IntSHGetFileInfo(pszPath, dwFileAttributes, info, cbFileInfo, flags); + validImageListHandles.Add(newHandle); + return newHandle; + } +#else + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + public static extern IntPtr SHGetFileInfo(string pszPath, int dwFileAttributes, NativeMethods.SHFILEINFO info, int cbFileInfo, int flags); +#endif +*/ + [DllImport(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern int OemKeyScan(short wAsciiVal); + + + [DllImport(ExternDll.Gdi32)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetSystemPaletteEntries(HandleRef hdc, int iStartIndex, int nEntries, byte[] lppe); + [DllImport(ExternDll.Gdi32)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetDIBits(HandleRef hdc, HandleRef hbm, int uStartScan, int cScanLines, byte[] lpvBits, ref NativeMethods.BITMAPINFO_FLAT bmi, int uUsage); + [DllImport(ExternDll.Gdi32)] + [ResourceExposure(ResourceScope.None)] + public static extern int StretchDIBits(HandleRef hdc, int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth, int nSrcHeight, byte[] lpBits, ref NativeMethods.BITMAPINFO_FLAT lpBitsInfo, int iUsage, int dwRop); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateCompatibleBitmap", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr IntCreateCompatibleBitmap(HandleRef hDC, int width, int height); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateCompatibleBitmap(HandleRef hDC, int width, int height) { + return System.Internal.HandleCollector.Add(IntCreateCompatibleBitmap(hDC, width, height), NativeMethods.CommonHandles.GDI); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetScrollInfo(HandleRef hWnd, int fnBar, [In, Out] NativeMethods.SCROLLINFO si); + + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsAccelerator(HandleRef hAccel, int cAccelEntries, [In] ref NativeMethods.MSG lpMsg, short[] lpwCmd); + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ChooseFont([In, Out] NativeMethods.CHOOSEFONT cf); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetBitmapBits(HandleRef hbmp, int cbBuffer, byte[] lpvBits); + [DllImport(ExternDll.Comdlg32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int CommDlgExtendedError(); + [DllImport(ExternDll.Oleaut32, ExactSpelling=true, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern void SysFreeString(HandleRef bstr); + + [DllImport(ExternDll.Oleaut32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern void OleCreatePropertyFrame(HandleRef hwndOwner, int x, int y, [MarshalAs(UnmanagedType.LPWStr)]string caption, int objects, [MarshalAs(UnmanagedType.Interface)] ref object pobjs, int pages, HandleRef pClsid, int locale, int reserved1, IntPtr reserved2); + [DllImport(ExternDll.Oleaut32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern void OleCreatePropertyFrame(HandleRef hwndOwner, int x, int y, [MarshalAs(UnmanagedType.LPWStr)]string caption, int objects, [MarshalAs(UnmanagedType.Interface)] ref object pobjs, int pages, Guid[] pClsid, int locale, int reserved1, IntPtr reserved2); + [DllImport(ExternDll.Oleaut32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern void OleCreatePropertyFrame(HandleRef hwndOwner, int x, int y, [MarshalAs(UnmanagedType.LPWStr)]string caption, int objects, HandleRef lplpobjs, int pages, HandleRef pClsid, int locale, int reserved1, IntPtr reserved2); + [DllImport(ExternDll.Hhctrl, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HtmlHelp(HandleRef hwndCaller, [MarshalAs(UnmanagedType.LPTStr)]string pszFile, int uCommand, int dwData); + [DllImport(ExternDll.Hhctrl, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HtmlHelp(HandleRef hwndCaller, [MarshalAs(UnmanagedType.LPTStr)]string pszFile, int uCommand, string dwData); + [DllImport(ExternDll.Hhctrl, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HtmlHelp(HandleRef hwndCaller, [MarshalAs(UnmanagedType.LPTStr)]string pszFile, int uCommand, [MarshalAs(UnmanagedType.LPStruct)]NativeMethods.HH_POPUP dwData); + [DllImport(ExternDll.Hhctrl, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HtmlHelp(HandleRef hwndCaller, [MarshalAs(UnmanagedType.LPTStr)]string pszFile, int uCommand, [MarshalAs(UnmanagedType.LPStruct)]NativeMethods.HH_FTS_QUERY dwData); + [DllImport(ExternDll.Hhctrl, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HtmlHelp(HandleRef hwndCaller, [MarshalAs(UnmanagedType.LPTStr)]string pszFile, int uCommand, [MarshalAs(UnmanagedType.LPStruct)]NativeMethods.HH_AKLINK dwData); + [DllImport(ExternDll.Oleaut32)] + [ResourceExposure(ResourceScope.None)] + public static extern void VariantInit(HandleRef pObject); + [ DllImport(ExternDll.Oleaut32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern void VariantClear(HandleRef pObject); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool LineTo(HandleRef hdc, int x, int y); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool MoveToEx(HandleRef hdc, int x, int y, NativeMethods.POINT pt); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool Rectangle( + HandleRef hdc, int left, int top, int right, int bottom); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PatBlt(HandleRef hdc, int left, int top, int width, int height, int rop); + + [DllImport(ExternDll.Kernel32, EntryPoint="GetThreadLocale", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern int GetThreadLCID(); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetMessagePos(); + + + + [DllImport(ExternDll.User32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RegisterClipboardFormat(string format); + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); + + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ChooseColor([In, Out] NativeMethods.CHOOSECOLOR cc); + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RegisterWindowMessage(string msg); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ExternalDeleteObject(HandleRef hObject); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteObject", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + internal static extern bool IntDeleteObject(HandleRef hObject); + public static bool DeleteObject(HandleRef hObject) { + System.Internal.HandleCollector.Remove((IntPtr)hObject, NativeMethods.CommonHandles.GDI); + return IntDeleteObject(hObject); + } + + [DllImport(ExternDll.Oleaut32, EntryPoint="OleCreateFontIndirect", ExactSpelling=true, PreserveSig=false)] + [ResourceExposure(ResourceScope.Process)] + public static extern SafeNativeMethods.IFontDisp OleCreateIFontDispIndirect(NativeMethods.FONTDESC fd, ref Guid iid); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateSolidBrush", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateSolidBrush(int crColor); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateSolidBrush(int crColor) { + return System.Internal.HandleCollector.Add(IntCreateSolidBrush(crColor), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetWindowExtEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.SIZE size); + + [DllImport(ExternDll.Kernel32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, + int dwLanguageId, StringBuilder lpBuffer, int nSize, HandleRef arguments); + + + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern void InitCommonControls(); + + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool InitCommonControlsEx(NativeMethods.INITCOMMONCONTROLSEX icc); + +#if DEBUG + private static System.Collections.ArrayList validImageListHandles = ArrayList.Synchronized(new System.Collections.ArrayList()); +#endif + + // + +#if DEBUG + [DllImport(ExternDll.Comctl32, EntryPoint="ImageList_Create")] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntImageList_Create(int cx, int cy, int flags, int cInitial, int cGrow); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr ImageList_Create(int cx, int cy, int flags, int cInitial, int cGrow) { + IntPtr newHandle = IntImageList_Create(cx, cy, flags, cInitial, cGrow); + validImageListHandles.Add(newHandle); + return newHandle; + } +#else + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr ImageList_Create(int cx, int cy, int flags, int cInitial, int cGrow); +#endif + +#if DEBUG + [DllImport(ExternDll.Comctl32, EntryPoint="ImageList_Destroy")] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntImageList_Destroy(HandleRef himl); + public static bool ImageList_Destroy(HandleRef himl) { + System.Diagnostics.Debug.Assert(validImageListHandles.Contains(himl.Handle), "Invalid ImageList handle"); + validImageListHandles.Remove(himl.Handle); + return IntImageList_Destroy(himl); + } +#else + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Destroy(HandleRef himl); +#endif + // unfortunately, the neat wrapper to Assert for DEBUG assumes that this was created by + // our version of ImageList_Create, which is not always the case for the TreeView's internal + // native state image list. Use separate EntryPoint thunk to skip this check: + [DllImport(ExternDll.Comctl32, EntryPoint = "ImageList_Destroy")] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Destroy_Native(HandleRef himl); + + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ImageList_GetImageCount(HandleRef himl); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ImageList_Add(HandleRef himl, HandleRef hbmImage, HandleRef hbmMask); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ImageList_ReplaceIcon(HandleRef himl, int index, HandleRef hicon); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ImageList_SetBkColor(HandleRef himl, int clrBk); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Draw(HandleRef himl, int i, HandleRef hdcDst, int x, int y, int fStyle); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Replace(HandleRef himl, int i, HandleRef hbmImage, HandleRef hbmMask); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_DrawEx(HandleRef himl, int i, HandleRef hdcDst, int x, int y, int dx, int dy, int rgbBk, int rgbFg, int fStyle); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_GetIconSize(HandleRef himl, out int x, out int y); + +#if DEBUG + [DllImport(ExternDll.Comctl32, EntryPoint="ImageList_Duplicate")] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntImageList_Duplicate(HandleRef himl); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr ImageList_Duplicate(HandleRef himl) { + IntPtr newHandle = IntImageList_Duplicate(himl); + validImageListHandles.Add(newHandle); + return newHandle; + } +#else + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr ImageList_Duplicate(HandleRef himl); +#endif + + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Remove(HandleRef himl, int i); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_GetImageInfo(HandleRef himl, int i, NativeMethods.IMAGEINFO pImageInfo); + +#if DEBUG + [DllImport(ExternDll.Comctl32, EntryPoint="ImageList_Read")] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr IntImageList_Read(UnsafeNativeMethods.IStream pstm); + public static IntPtr ImageList_Read(UnsafeNativeMethods.IStream pstm) { + IntPtr newHandle = IntImageList_Read(pstm); + validImageListHandles.Add(newHandle); + return newHandle; + } +#else + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ImageList_Read(UnsafeNativeMethods.IStream pstm); +#endif + + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImageList_Write(HandleRef himl, UnsafeNativeMethods.IStream pstm); + [DllImport(ExternDll.Comctl32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ImageList_WriteEx(HandleRef himl, int dwFlags, UnsafeNativeMethods.IStream pstm); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool TrackPopupMenuEx(HandleRef hmenu, int fuFlags, int x, int y, HandleRef hwnd, NativeMethods.TPMPARAMS tpm); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetKeyboardLayout(int dwLayout); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ActivateKeyboardLayout(HandleRef hkl, int uFlags); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetKeyboardLayoutList(int size, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] hkls); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref NativeMethods.DEVMODE lpDevMode); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out]NativeMethods.MONITORINFOEX info); + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr MonitorFromPoint(NativeMethods.POINTSTRUCT pt, int flags); + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr MonitorFromRect(ref NativeMethods.RECT rect, int flags); + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags); + [DllImport(ExternDll.User32, ExactSpelling = true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnumDisplayMonitors(HandleRef hdc, NativeMethods.COMRECT rcClip, NativeMethods.MonitorEnumProc lpfnEnum, IntPtr dwData); + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, EntryPoint = "CreateHalftonePalette", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr /*HPALETTE*/ IntCreateHalftonePalette(HandleRef hdc); + public static IntPtr /*HPALETTE*/ CreateHalftonePalette(HandleRef hdc) { + return System.Internal.HandleCollector.Add(IntCreateHalftonePalette(hdc), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetPaletteEntries(HandleRef hpal, int iStartIndex, int nEntries, int[] lppe); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetTextMetricsW(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRIC lptm); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetTextMetricsA(HandleRef hDC, [In, Out] ref NativeMethods.TEXTMETRICA lptm); + + public static int GetTextMetrics(HandleRef hDC, ref NativeMethods.TEXTMETRIC lptm) { + if (Marshal.SystemDefaultCharSize == 1) + { + // ANSI + NativeMethods.TEXTMETRICA lptmA = new NativeMethods.TEXTMETRICA(); + int retVal = SafeNativeMethods.GetTextMetricsA(hDC, ref lptmA); + + lptm.tmHeight = lptmA.tmHeight; + lptm.tmAscent = lptmA.tmAscent; + lptm.tmDescent = lptmA.tmDescent; + lptm.tmInternalLeading = lptmA.tmInternalLeading; + lptm.tmExternalLeading = lptmA.tmExternalLeading; + lptm.tmAveCharWidth = lptmA.tmAveCharWidth; + lptm.tmMaxCharWidth = lptmA.tmMaxCharWidth; + lptm.tmWeight = lptmA.tmWeight; + lptm.tmOverhang = lptmA.tmOverhang; + lptm.tmDigitizedAspectX = lptmA.tmDigitizedAspectX; + lptm.tmDigitizedAspectY = lptmA.tmDigitizedAspectY; + lptm.tmFirstChar = (char) lptmA.tmFirstChar; + lptm.tmLastChar = (char) lptmA.tmLastChar; + lptm.tmDefaultChar = (char) lptmA.tmDefaultChar; + lptm.tmBreakChar = (char) lptmA.tmBreakChar; + lptm.tmItalic = lptmA.tmItalic; + lptm.tmUnderlined = lptmA.tmUnderlined; + lptm.tmStruckOut = lptmA.tmStruckOut; + lptm.tmPitchAndFamily = lptmA.tmPitchAndFamily; + lptm.tmCharSet = lptmA.tmCharSet; + + return retVal; + } + else + { + // Unicode + return SafeNativeMethods.GetTextMetricsW(hDC, ref lptm); + } + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateDIBSection", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr IntCreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, byte[] ppvBits, IntPtr hSection, int dwOffset); + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr CreateDIBSection(HandleRef hdc, HandleRef pbmi, int iUsage, byte[] ppvBits, IntPtr hSection, int dwOffset) { + return System.Internal.HandleCollector.Add(IntCreateDIBSection(hdc, pbmi, iUsage, ppvBits, hSection, dwOffset), NativeMethods.CommonHandles.GDI); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr /*HBITMAP*/ IntCreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, IntPtr lpvBits); + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr /*HBITMAP*/ CreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, IntPtr lpvBits) { + return System.Internal.HandleCollector.Add(IntCreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr /*HBITMAP*/ IntCreateBitmapShort(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, short[] lpvBits); + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr /*HBITMAP*/ CreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, short[] lpvBits) { + return System.Internal.HandleCollector.Add(IntCreateBitmapShort(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBitmap", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr /*HBITMAP*/ IntCreateBitmapByte(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, byte[] lpvBits); + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public static IntPtr /*HBITMAP*/ CreateBitmap(int nWidth, int nHeight, int nPlanes, int nBitsPerPixel, byte[] lpvBits) { + return System.Internal.HandleCollector.Add(IntCreateBitmapByte(nWidth, nHeight, nPlanes, nBitsPerPixel, lpvBits), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePatternBrush", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr /*HBRUSH*/ IntCreatePatternBrush(HandleRef hbmp); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr /*HBRUSH*/ CreatePatternBrush(HandleRef hbmp) { + return System.Internal.HandleCollector.Add(IntCreatePatternBrush(hbmp), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateBrushIndirect", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateBrushIndirect(NativeMethods.LOGBRUSH lb); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateBrushIndirect(NativeMethods.LOGBRUSH lb) { + return System.Internal.HandleCollector.Add(IntCreateBrushIndirect(lb), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreatePen", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreatePen(int nStyle, int nWidth, int crColor); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreatePen(int nStyle, int nWidth, int crColor) { + return System.Internal.HandleCollector.Add(IntCreatePen(nStyle, nWidth, crColor), NativeMethods.CommonHandles.GDI); + } + + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetViewportExtEx(HandleRef hDC, int x, int y, NativeMethods.SIZE size); + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr LoadCursor(HandleRef hInst, int iconId); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool GetClipCursor([In, Out] ref NativeMethods.RECT lpRect); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetCursor(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetIconInfo(HandleRef hIcon, [In, Out] NativeMethods.ICONINFO info); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int IntersectClipRect(HandleRef hDC, int x1, int y1, int x2, int y2); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="CopyImage", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr IntCopyImage(HandleRef hImage, int uType, int cxDesired, int cyDesired, int fuFlags); + public static IntPtr CopyImage(HandleRef hImage, int uType, int cxDesired, int cyDesired, int fuFlags) { + return System.Internal.HandleCollector.Add(IntCopyImage(hImage, uType, cxDesired, cyDesired, fuFlags), NativeMethods.CommonHandles.GDI); + } + public static IntPtr CopyImageAsCursor(HandleRef hImage, int uType, int cxDesired, int cyDesired, int fuFlags) { + return System.Internal.HandleCollector.Add(IntCopyImage(hImage, uType, cxDesired, cyDesired, fuFlags), NativeMethods.CommonHandles.Cursor); + } + + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool AdjustWindowRectEx(ref NativeMethods.RECT lpRect, int dwStyle, bool bMenu, int dwExStyle); + + // This API is available only starting Windows 10 RS1 + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool AdjustWindowRectExForDpi(ref NativeMethods.RECT lpRect, int dwStyle, bool bMenu, int dwExStyle, uint dpi); + + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DoDragDrop(IComDataObject dataObject, UnsafeNativeMethods.IOleDropSource dropSource, int allowedEffects, int[] finalEffect); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetSysColorBrush(int nIndex); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnableWindow(HandleRef hWnd, bool enable); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetClientRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetDoubleClickTime(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetUpdateRgn(HandleRef hwnd, HandleRef hrgn, bool fErase); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ValidateRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); + + // + // WARNING: Don't uncomment this code unless you absolutelly need it. Use instead Marshal.GetLastWin32Error + // and mark your PInvoke [DllImport(..., SetLastError=true)] + // From MSDN: + // GetLastWin32Error exposes the Win32 GetLastError API method from Kernel32.DLL. This method exists because + // it is not safe to make a direct platform invoke call to GetLastError to obtain this information. If you + // want to access this error code, you must call GetLastWin32Error rather than writing your own platform invoke + // definition for GetLastError and calling it. The common language runtime can make internal calls to APIs that + // overwrite the operating system maintained GetLastError. + // + //[DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + //public extern static int GetLastError(); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int FillRect(HandleRef hdc, [In] ref NativeMethods.RECT rect, HandleRef hbrush); + + [DllImport(ExternDll.Gdi32,ExactSpelling=true,CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int /*COLORREF*/ GetTextColor(HandleRef hDC); + + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetBkColor(HandleRef hDC); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int /*COLORREF*/ SetTextColor(HandleRef hDC, int crColor); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetBkColor(HandleRef hDC, int clr); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr /* HPALETTE */SelectPalette(HandleRef hdc, HandleRef hpal, int bForceBackground); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetViewportOrgEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.POINT point); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateRectRgn", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateRectRgn(int x1, int y1, int x2, int y2); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateRectRgn(int x1, int y1, int x2, int y2) { + return System.Internal.HandleCollector.Add(IntCreateRectRgn(x1, y1, x2, y2), NativeMethods.CommonHandles.GDI); + } + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int CombineRgn(HandleRef hRgn, HandleRef hRgn1, HandleRef hRgn2, int nCombineMode); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RealizePalette(HandleRef hDC); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool LPtoDP(HandleRef hDC, [In, Out] ref NativeMethods.RECT lpRect, int nCount); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetWindowOrgEx(HandleRef hDC, int x, int y, [In, Out] NativeMethods.POINT point); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetViewportOrgEx(HandleRef hDC, [In, Out] NativeMethods.POINT point); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetMapMode(HandleRef hDC, int nMapMode); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsWindowEnabled(HandleRef hWnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsWindowVisible(HandleRef hWnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ReleaseCapture(); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern int GetCurrentThreadId(); + + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData); + internal delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId); + [return:MarshalAs(UnmanagedType.Bool)] + [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetExitCodeThread(HandleRef hWnd, out int lpdwExitCode); + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ShowWindow(HandleRef hWnd, int nCmdShow); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter, + int x, int y, int cx, int cy, int flags); + + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetWindowTextLength(HandleRef hWnd); + // this is a wrapper that comctl exposes for the NT function since it doesn't exist natively on 95. + [DllImport(ExternDll.Comctl32, ExactSpelling=true), CLSCompliantAttribute(false)] + [ResourceExposure(ResourceScope.None)] + private static extern bool _TrackMouseEvent(NativeMethods.TRACKMOUSEEVENT tme); + public static bool TrackMouseEvent(NativeMethods.TRACKMOUSEEVENT tme) { + // only on NT - not on 95 - comctl32 has a wrapper for 95 and NT. + return _TrackMouseEvent(tme); + } + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool RedrawWindow(HandleRef hwnd, ref NativeMethods.RECT rcUpdate, HandleRef hrgnUpdate, int flags); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool RedrawWindow(HandleRef hwnd, NativeMethods.COMRECT rcUpdate, HandleRef hrgnUpdate, int flags); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool InvalidateRect(HandleRef hWnd, ref NativeMethods.RECT rect, bool erase); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool InvalidateRect(HandleRef hWnd, NativeMethods.COMRECT rect, bool erase); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool InvalidateRgn(HandleRef hWnd, HandleRef hrgn, bool erase); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool UpdateWindow(HandleRef hWnd); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern int GetCurrentProcessId(); + [DllImport(ExternDll.User32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int ScrollWindowEx(HandleRef hWnd, int nXAmount, int nYAmount, NativeMethods.COMRECT rectScrollRegion, ref NativeMethods.RECT rectClip, HandleRef hrgnUpdate, ref NativeMethods.RECT prcUpdate, int flags); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.AppDomain)] + public static extern int GetThreadLocale(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool MessageBeep(int type); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool DrawMenuBar(HandleRef hWnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool IsChild(HandleRef parent, HandleRef child); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, IntPtr lpTimerFunc); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool KillTimer(HandleRef hwnd, int idEvent); + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto), + SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api")] + [ResourceExposure(ResourceScope.None)] + public static extern int MessageBox(HandleRef hWnd, string text, string caption, int type); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr SelectObject(HandleRef hDC, HandleRef hObject); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetTickCount(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ScrollWindow(HandleRef hWnd, int nXAmount, int nYAmount, ref NativeMethods.RECT rectScrollRegion, ref NativeMethods.RECT rectClip); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetCurrentProcess(); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetCurrentThread(); + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [DllImport(ExternDll.Kernel32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.AppDomain)] + public extern static bool SetThreadLocale(int Locale); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsWindowUnicode(HandleRef hWnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool DrawEdge(HandleRef hDC, ref NativeMethods.RECT rect, int edge, int flags); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool DrawFrameControl(HandleRef hDC, ref NativeMethods.RECT rect, int type, int state); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetClipRgn(HandleRef hDC, HandleRef hRgn); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetRgnBox(HandleRef hRegion, ref NativeMethods.RECT clipRect); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SelectClipRgn(HandleRef hDC, HandleRef hRgn); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetROP2(HandleRef hDC, int nDrawMode); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool DrawIcon(HandleRef hDC, int x, int y, HandleRef hIcon); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool DrawIconEx(HandleRef hDC, int x, int y, HandleRef hIcon, int width, int height, int iStepIfAniCursor, HandleRef hBrushFlickerFree, int diFlags); + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetBkMode(HandleRef hDC, int nBkMode); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool BitBlt(HandleRef hDC, int x, int y, int nWidth, int nHeight, + HandleRef hSrcDC, int xSrc, int ySrc, int dwRop); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ShowCaret(HandleRef hWnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool HideCaret(HandleRef hWnd); + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern uint GetCaretBlinkTime(); + + + // Theming/Visual Styles + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsAppThemed(); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeAppProperties(); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void SetThemeAppProperties(int Flags); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr OpenThemeData(HandleRef hwnd, [MarshalAs(UnmanagedType.LPWStr)] string pszClassList); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int CloseThemeData(HandleRef hTheme); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetCurrentThemeName(StringBuilder pszThemeFileName, int dwMaxNameChars, StringBuilder pszColorBuff, int dwMaxColorChars, StringBuilder pszSizeBuff, int cchMaxSizeChars); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsThemePartDefined(HandleRef hTheme, int iPartId, int iStateId); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DrawThemeBackground(HandleRef hTheme, HandleRef hdc, int partId, int stateId, [In] NativeMethods.COMRECT pRect, [In] NativeMethods.COMRECT pClipRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DrawThemeEdge(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [In] NativeMethods.COMRECT pDestRect, int uEdge, int uFlags, [Out] NativeMethods.COMRECT pContentRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DrawThemeParentBackground(HandleRef hwnd, HandleRef hdc, [In] NativeMethods.COMRECT prc); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DrawThemeText(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [MarshalAs(UnmanagedType.LPWStr)] string pszText, int iCharCount, int dwTextFlags, int dwTextFlags2, [In] NativeMethods.COMRECT pRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeBackgroundContentRect(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [In] NativeMethods.COMRECT pBoundingRect, [Out] NativeMethods.COMRECT pContentRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeBackgroundExtent(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [In] NativeMethods.COMRECT pContentRect, [Out] NativeMethods.COMRECT pExtentRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeBackgroundRegion(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [In] NativeMethods.COMRECT pRect, ref IntPtr pRegion); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeBool(HandleRef hTheme, int iPartId, int iStateId, int iPropId, ref bool pfVal); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeColor(HandleRef hTheme, int iPartId, int iStateId, int iPropId, ref int pColor); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeEnumValue(HandleRef hTheme, int iPartId, int iStateId, int iPropId, ref int piVal); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeFilename(HandleRef hTheme, int iPartId, int iStateId, int iPropId, StringBuilder pszThemeFilename, int cchMaxBuffChars); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeFont(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, int iPropId, NativeMethods.LOGFONT pFont); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeInt(HandleRef hTheme, int iPartId, int iStateId, int iPropId, ref int piVal); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemePartSize(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [In] NativeMethods.COMRECT prc, System.Windows.Forms.VisualStyles.ThemeSizeType eSize, [Out] NativeMethods.SIZE psz); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemePosition(HandleRef hTheme, int iPartId, int iStateId, int iPropId, [Out] NativeMethods.POINT pPoint); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeMargins(HandleRef hTheme, HandleRef hDC, int iPartId, int iStateId, int iPropId, ref NativeMethods.MARGINS margins); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeString(HandleRef hTheme, int iPartId, int iStateId, int iPropId, StringBuilder pszBuff, int cchMaxBuffChars); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeDocumentationProperty([MarshalAs(UnmanagedType.LPWStr)] string pszThemeName, [MarshalAs(UnmanagedType.LPWStr)] string pszPropertyName, StringBuilder pszValueBuff, int cchMaxValChars); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeTextExtent(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, [MarshalAs(UnmanagedType.LPWStr)] string pszText, int iCharCount, int dwTextFlags, [In] NativeMethods.COMRECT pBoundingRect, [Out] NativeMethods.COMRECT pExtentRect); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeTextMetrics(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, ref System.Windows.Forms.VisualStyles.TextMetrics ptm); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int HitTestThemeBackground(HandleRef hTheme, HandleRef hdc, int iPartId, int iStateId, int dwOptions, [In] NativeMethods.COMRECT pRect, HandleRef hrgn, [In] NativeMethods.POINTSTRUCT ptTest, ref int pwHitTestCode); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsThemeBackgroundPartiallyTransparent(HandleRef hTheme, int iPartId, int iStateId); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetThemeSysBool(HandleRef hTheme, int iBoolId); + [DllImport(ExternDll.Uxtheme, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThemeSysInt(HandleRef hTheme, int iIntId, ref int piValue); + + [DllImportAttribute(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr OpenInputDesktop(int dwFlags, [MarshalAs(UnmanagedType.Bool)] bool fInherit, int dwDesiredAccess); + + [DllImportAttribute(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool CloseDesktop(IntPtr hDesktop); + + // for Windows vista to windows 8. + [DllImport(ExternDll.User32, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetProcessDPIAware(); + + // for windows 8.1 and above + [DllImport(ExternDll.ShCore, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetProcessDpiAwareness(NativeMethods.PROCESS_DPI_AWARENESS awareness); + + // for Windows 10 version RS2 and above + [DllImport(ExternDll.User32, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetProcessDpiAwarenessContext(int dpiFlag); + + // Available in Windows 10 version RS1 and above. + [DllImport(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetThreadDpiAwarenessContext(); + + // Available in Windows 10 version RS1 and above. + [DllImport(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern bool AreDpiAwarenessContextsEqual(int dpiContextA, int dpiContextB); + + // Color conversion + // + public static int RGBToCOLORREF(int rgbValue) { + + // clear the A value, swap R & B values + int bValue = (rgbValue & 0xFF) << 16; + + rgbValue &= 0xFFFF00; + rgbValue |= ((rgbValue >> 16) & 0xFF); + rgbValue &= 0x00FFFF; + rgbValue |= bValue; + return rgbValue; + } + + public static Color ColorFromCOLORREF(int colorref) { + int r = colorref & 0xFF; + int g = (colorref >> 8) & 0xFF; + int b = (colorref >> 16) & 0xFF; + return Color.FromArgb(r, g, b); + } + + public static int ColorToCOLORREF(Color color) { + return (int)color.R | ((int)color.G << 8) | ((int)color.B << 16); + } + + [ComImport(), Guid("BEF6E003-A874-101A-8BBA-00AA00300CAB"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)] + public interface IFontDisp { + + string Name {get; set;} + + long Size {get;set;} + + bool Bold {get;set;} + + bool Italic {get;set;} + + bool Underline {get;set;} + + bool Strikethrough {get;set;} + + short Weight {get;set;} + + short Charset {get;set;} + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SaveFileDialog.cs b/WindowsForms/Managed/System/WinForms/SaveFileDialog.cs new file mode 100644 index 000000000..b060ac274 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SaveFileDialog.cs @@ -0,0 +1,229 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using CodeAccessPermission = System.Security.CodeAccessPermission; + using System.IO; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Security.Permissions; + using System.ComponentModel; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// + /// Represents + /// a common dialog box that allows the user to specify options for saving a + /// file. This class cannot be inherited. + /// + /// + [ + Designer("System.Windows.Forms.Design.SaveFileDialogDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionSaveFileDialog) + ] + public sealed class SaveFileDialog : FileDialog { + + /// + /// + /// + /// Gets or sets a value indicating whether the dialog box prompts the user for + /// permission to create a file if the user specifies a file that does not exist. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.SaveFileDialogCreatePrompt) + ] + public bool CreatePrompt { + get { + return GetOption(NativeMethods.OFN_CREATEPROMPT); + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(NativeMethods.OFN_CREATEPROMPT, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the Save As dialog box displays a warning if the user specifies + /// a file name that already exists. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.SaveFileDialogOverWritePrompt) + ] + public bool OverwritePrompt { + get { + return GetOption(NativeMethods.OFN_OVERWRITEPROMPT); + } + set { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded"); + IntSecurity.FileDialogCustomization.Demand(); + SetOption(NativeMethods.OFN_OVERWRITEPROMPT, value); + } + } + + /// + /// + /// + /// Opens the file with read/write permission selected by the user. + /// + /// + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: filename is a local variable and not subject to race conditions. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + public Stream OpenFile() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogSaveFile Demanded"); + IntSecurity.FileDialogSaveFile.Demand(); + + string filename = FileNamesInternal[0]; + + if (string.IsNullOrEmpty(filename)) + throw new ArgumentNullException( "FileName" ); + + Stream s = null; + + // SECREVIEW : We demanded the FileDialog permission above, so it is safe + // : to assert this here. Since the user picked the file, it + // : is OK to give them read/write access to the stream. + // + new FileIOPermission(FileIOPermissionAccess.AllAccess, IntSecurity.UnsafeGetFullPath(filename)).Assert(); + try { + s = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite); + } + finally { + CodeAccessPermission.RevertAssert(); + } + return s; + } + + /// + /// + /// + /// Prompts the user with a + /// when a file is about to be created. This method is + /// invoked when the CreatePrompt property is true and the specified file + /// does not exist. A return value of false prevents the dialog from + /// closing. + /// + /// + private bool PromptFileCreate(string fileName) + { + return MessageBoxWithFocusRestore(SR.GetString(SR.FileDialogCreatePrompt, fileName), + DialogCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + } + + /// + /// + /// + /// Prompts the user when a file is about to be overwritten. This method is + /// invoked when the "overwritePrompt" property is true and the specified + /// file already exists. A return value of false prevents the dialog from + /// closing. + /// + /// + /// + private bool PromptFileOverwrite(string fileName) { + return MessageBoxWithFocusRestore(SR.GetString(SR.FileDialogOverwritePrompt, fileName), + DialogCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + } + + // If it's necessary to throw up a "This file exists, are you sure?" kind of + // MessageBox, here's where we do it. + // Return value is whether or not the user hit "okay". + internal override bool PromptUserIfAppropriate(string fileName) { + if (!base.PromptUserIfAppropriate(fileName)) { + return false; + } + + //Note: When we are using the Vista dialog mode we get two prompts (one from us and one from the OS) if we do this + if ((options & NativeMethods.OFN_OVERWRITEPROMPT) != 0 && FileExists(fileName) && !this.UseVistaDialogInternal) { + if (!PromptFileOverwrite(fileName)) { + return false; + } + } + + if ((options & NativeMethods.OFN_CREATEPROMPT) != 0 && !FileExists(fileName)) { + if (!PromptFileCreate(fileName)) { + return false; + } + } + + return true; + } + + /// + /// + /// + /// Resets all dialog box options to their default + /// values. + /// + /// + public override void Reset() { + base.Reset(); + SetOption(NativeMethods.OFN_OVERWRITEPROMPT, true); + } + + internal override void EnsureFileDialogPermission() + { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogSaveFile Demanded in SaveFileDialog.RunFileDialog"); + IntSecurity.FileDialogSaveFile.Demand(); + } + + /// + /// + /// + /// + internal override bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn) { + //We have already done the demand in EnsureFileDialogPermission but it doesn't hurt to do it again + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogSaveFile Demanded in SaveFileDialog.RunFileDialog"); + IntSecurity.FileDialogSaveFile.Demand(); + + bool result = UnsafeNativeMethods.GetSaveFileName(ofn); + + if (!result) { + // Something may have gone wrong - check for error condition + // + int errorCode = SafeNativeMethods.CommDlgExtendedError(); + switch(errorCode) { + case NativeMethods.FNERR_INVALIDFILENAME: + throw new InvalidOperationException(SR.GetString(SR.FileDialogInvalidFileName, FileName)); + } + } + + return result; + } + internal override string[] ProcessVistaFiles(FileDialogNative.IFileDialog dialog) + { + FileDialogNative.IFileSaveDialog saveDialog = (FileDialogNative.IFileSaveDialog)dialog; + FileDialogNative.IShellItem item; + dialog.GetResult(out item); + return new string[] { GetFilePathFromShellItem(item) }; + } + internal override FileDialogNative.IFileDialog CreateVistaDialog() + { return new FileDialogNative.NativeFileSaveDialog(); } + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Screen.cs b/WindowsForms/Managed/System/WinForms/Screen.cs new file mode 100644 index 000000000..3d73ec97d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Screen.cs @@ -0,0 +1,498 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using System.Threading; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Collections; + using Microsoft.Win32; + using Internal; + + /// + /// + /// + /// Represents a display device or + /// multiple display devices on a single system. + /// + /// + public class Screen { + + readonly IntPtr hmonitor; + /// + /// Bounds of the screen + /// + readonly Rectangle bounds; + /// + /// Available working area on the screen. This excludes taskbars and other + /// docked windows. + /// + private Rectangle workingArea = Rectangle.Empty; + /// + /// Set to true if this screen is the primary monitor + /// + readonly bool primary; + /// + /// Device name associated with this monitor + /// + readonly string deviceName; + + readonly int bitDepth; + + private static object syncLock = new object();//used to lock this class before sync'ing to SystemEvents + + private static int desktopChangedCount = -1;//static counter of desktop size changes + + private int currentDesktopChangedCount = -1;//instance-based counter used to invalidate WorkingArea + + // This identifier is just for us, so that we don't try to call the multimon + // functions if we just need the primary monitor... this is safer for + // non-multimon OSes. + // + private const int PRIMARY_MONITOR = unchecked((int)0xBAADF00D); + + private const int MONITOR_DEFAULTTONULL = 0x00000000; + private const int MONITOR_DEFAULTTOPRIMARY = 0x00000001; + private const int MONITOR_DEFAULTTONEAREST = 0x00000002; + private const int MONITORINFOF_PRIMARY = 0x00000001; + + private static bool multiMonitorSupport = (UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CMONITORS) != 0); + private static Screen[] screens; + + internal Screen(IntPtr monitor) : this(monitor, IntPtr.Zero) { + } + + internal Screen(IntPtr monitor, IntPtr hdc) { + + IntPtr screenDC = hdc; + + if (!multiMonitorSupport || monitor == (IntPtr)PRIMARY_MONITOR) { + // Single monitor system + // + bounds = SystemInformation.VirtualScreen; + primary = true; + deviceName = "DISPLAY"; + } + else { + // MultiMonitor System + // We call the 'A' version of GetMonitorInfoA() because + // the 'W' version just never fills out the struct properly on Win2K. + // + NativeMethods.MONITORINFOEX info = new NativeMethods.MONITORINFOEX(); + SafeNativeMethods.GetMonitorInfo(new HandleRef(null, monitor), info); + bounds = Rectangle.FromLTRB(info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom); + primary = ((info.dwFlags & MONITORINFOF_PRIMARY) != 0); + + deviceName = new string(info.szDevice); + deviceName = deviceName.TrimEnd((char)0); + + if (hdc == IntPtr.Zero) { + screenDC = UnsafeNativeMethods.CreateDC(deviceName); + } + } + hmonitor = monitor; + + this.bitDepth = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, screenDC), NativeMethods.BITSPIXEL); + this.bitDepth *= UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, screenDC), NativeMethods.PLANES); + + if (hdc != screenDC) { + UnsafeNativeMethods.DeleteDC(new HandleRef(null, screenDC)); + } + } + + /// + /// + /// + /// Gets an array of all of the displays on the system. + /// + /// + public static Screen[] AllScreens { + get { + if (screens == null) { + if (multiMonitorSupport) { + MonitorEnumCallback closure = new MonitorEnumCallback(); + NativeMethods.MonitorEnumProc proc = new NativeMethods.MonitorEnumProc(closure.Callback); + SafeNativeMethods.EnumDisplayMonitors(NativeMethods.NullHandleRef, null, proc, IntPtr.Zero); + + if (closure.screens.Count > 0) { + Screen[] temp = new Screen[closure.screens.Count]; + closure.screens.CopyTo(temp, 0); + screens = temp; + } + else { + screens = new Screen[] {new Screen((IntPtr)PRIMARY_MONITOR)}; + } + } + else { + screens = new Screen[] {PrimaryScreen}; + } + + // Now that we have our screens, attach a display setting changed + // event so that we know when to invalidate them. + // + SystemEvents.DisplaySettingsChanging += new EventHandler(OnDisplaySettingsChanging); + } + + return screens; + } + } + + /// + /// + /// + /// Gets Bits per Pixel value. + /// + /// + public int BitsPerPixel { + get { + return bitDepth; + } + } + + /// + /// + /// + /// Gets the bounds of the display. + /// + /// + public Rectangle Bounds { + get { + return bounds; + } + } + + /// + /// + /// + /// Gets the device name associated with a display. + /// + /// + public string DeviceName { + get { + return deviceName; + } + } + + /// + /// + /// + /// Gets a value indicating whether a particular display is + /// the primary device. + /// + /// + public bool Primary { + get { + return primary; + } + } + + /// + /// + /// + /// Gets the + /// primary display. + /// + /// + public static Screen PrimaryScreen { + get { + if (multiMonitorSupport) { + Screen[] screens = AllScreens; + for (int i=0; i + /// + /// + /// Gets the working area of the screen. + /// + /// + public Rectangle WorkingArea { + get { + + //if the static Screen class has a different desktop change count + //than this instance then update the count and recalculate our working area + if (currentDesktopChangedCount != Screen.DesktopChangedCount) { + + Interlocked.Exchange(ref currentDesktopChangedCount, Screen.DesktopChangedCount); + + if (!multiMonitorSupport ||hmonitor == (IntPtr)PRIMARY_MONITOR) { + // Single monitor system + // + workingArea = SystemInformation.WorkingArea; + } + else { + // MultiMonitor System + // We call the 'A' version of GetMonitorInfoA() because + // the 'W' version just never fills out the struct properly on Win2K. + // + NativeMethods.MONITORINFOEX info = new NativeMethods.MONITORINFOEX(); + SafeNativeMethods.GetMonitorInfo(new HandleRef(null, hmonitor), info); + workingArea = Rectangle.FromLTRB(info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom); + } + } + + return workingArea; + } + } + + /// + /// Screen instances call this property to determine + /// if their WorkingArea cache needs to be invalidated. + /// + private static int DesktopChangedCount { + get { + if (desktopChangedCount == -1) { + + lock (syncLock) { + + //now that we have a lock, verify (again) our changecount... + if (desktopChangedCount == -1) { + //sync the UserPreference.Desktop change event. We'll keep count + //of desktop changes so that the WorkingArea property on Screen + //instances know when to invalidate their cache. + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + + desktopChangedCount = 0; + } + } + } + return desktopChangedCount; + } + } + + /// + /// + /// + /// Specifies a value that indicates whether the specified object is equal to + /// this one. + /// + /// + public override bool Equals(object obj) { + if (obj is Screen) { + Screen comp = (Screen)obj; + if (hmonitor == comp.hmonitor) { + return true; + } + } + return false; + } + + /// + /// + /// + /// Retrieves a + /// for the monitor that contains the specified point. + /// + /// + /// + public static Screen FromPoint(Point point) { + if (multiMonitorSupport) { + NativeMethods.POINTSTRUCT pt = new NativeMethods.POINTSTRUCT(point.X, point.Y); + return new Screen(SafeNativeMethods.MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST)); + } + else { + return new Screen((IntPtr)PRIMARY_MONITOR); + } + } + + /// + /// + /// + /// Retrieves a + /// for the monitor that contains the + /// largest region of the rectangle. + /// + /// + /// + public static Screen FromRectangle(Rectangle rect) { + if (multiMonitorSupport) { + NativeMethods.RECT rc = NativeMethods.RECT.FromXYWH(rect.X, rect.Y, rect.Width, rect.Height); + return new Screen(SafeNativeMethods.MonitorFromRect(ref rc, MONITOR_DEFAULTTONEAREST)); + } + else { + return new Screen((IntPtr)PRIMARY_MONITOR, IntPtr.Zero); + } + } + + /// + /// + /// + /// Retrieves a + /// for the monitor that contains the largest + /// region of the window of the control. + /// + /// + public static Screen FromControl(Control control) { + return FromHandleInternal(control.Handle); + } + + /// + /// + /// + /// Retrieves a + /// for the monitor that + /// contains the largest region of the window. + /// + /// + public static Screen FromHandle(IntPtr hwnd) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ObjectFromWin32Handle Demanded"); + IntSecurity.ObjectFromWin32Handle.Demand(); + return FromHandleInternal(hwnd); + } + + internal static Screen FromHandleInternal(IntPtr hwnd) { + if (multiMonitorSupport) { + return new Screen(SafeNativeMethods.MonitorFromWindow(new HandleRef(null, hwnd), MONITOR_DEFAULTTONEAREST)); + } + else { + return new Screen((IntPtr)PRIMARY_MONITOR, IntPtr.Zero); + } + } + + /// + /// + /// + /// Retrieves the working area for the monitor that is closest to the + /// specified point. + /// + /// + /// + public static Rectangle GetWorkingArea(Point pt) { + return Screen.FromPoint(pt).WorkingArea; + } + /// + /// + /// + /// Retrieves the working area for the monitor that contains the largest region + /// of the specified rectangle. + /// + /// + /// + public static Rectangle GetWorkingArea(Rectangle rect) { + return Screen.FromRectangle(rect).WorkingArea; + } + /// + /// + /// + /// Retrieves the working area for the monitor that contains the largest + /// region of the specified control. + /// + /// + /// + public static Rectangle GetWorkingArea(Control ctl) { + return Screen.FromControl(ctl).WorkingArea; + } + + /// + /// + /// + /// Retrieves the bounds of the monitor that is closest to the specified + /// point. + /// + /// + public static Rectangle GetBounds(Point pt) { + return Screen.FromPoint(pt).Bounds; + } + /// + /// + /// + /// Retrieves the bounds of the monitor that contains the largest region of the + /// specified rectangle. + /// + /// + public static Rectangle GetBounds(Rectangle rect) { + return Screen.FromRectangle(rect).Bounds; + } + /// + /// + /// + /// Retrieves the bounds of the monitor + /// that contains the largest region of the specified control. + /// + /// + public static Rectangle GetBounds(Control ctl) { + return Screen.FromControl(ctl).Bounds; + } + + /// + /// + /// + /// Computes and retrieves a hash code for an object. + /// + /// + public override int GetHashCode() { + return(int)hmonitor; + } + + /// + /// Called by the SystemEvents class when our display settings are + /// changing. We cache screen information and at this point we must + /// invalidate our cache. + /// + private static void OnDisplaySettingsChanging(object sender, EventArgs e) { + + // Now that we've responded to this event, we don't need it again until + // someone re-queries. We will re-add the event at that time. + // + SystemEvents.DisplaySettingsChanging -= new EventHandler(OnDisplaySettingsChanging); + + // Display settings changed, so the set of screens we have is invalid. + // + screens = null; + } + + /// + /// Called by the SystemEvents class when our display settings have + /// changed. Here, we increment a static counter that Screen instances + /// can check against to invalidate their cache. + /// + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { + + if (e.Category == UserPreferenceCategory.Desktop) { + Interlocked.Increment(ref desktopChangedCount); + } + } + + /// + /// + /// + /// Retrieves a string representing this object. + /// + /// + public override string ToString() { + return GetType().Name + "[Bounds=" + bounds.ToString() + " WorkingArea=" + WorkingArea.ToString() + " Primary=" + primary.ToString() + " DeviceName=" + deviceName; + } + + + /// + /// + /// + private class MonitorEnumCallback { + public ArrayList screens = new ArrayList(); + + public virtual bool Callback(IntPtr monitor, IntPtr hdc, IntPtr lprcMonitor, IntPtr lparam) { + screens.Add(new Screen(monitor, hdc)); + return true; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScreenOrientation.cs b/WindowsForms/Managed/System/WinForms/ScreenOrientation.cs new file mode 100644 index 000000000..0c9e3edd6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScreenOrientation.cs @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the angle of screen orientation + /// + /// + public enum ScreenOrientation { + /// + /// + /// + /// The screen is oriented at 0 degrees + /// + /// + Angle0 = 0, + + /// + /// + /// + /// The screen is oriented at 90 degrees + /// + /// + Angle90 = 1, + + /// + /// + /// + /// The screen is oriented at 180 degrees. + /// + /// + Angle180 = 2, + + /// + /// + /// + /// The screen is oriented at 270 degrees. + /// + /// + Angle270 = 3, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ScrollBar.cs b/WindowsForms/Managed/System/WinForms/ScrollBar.cs new file mode 100644 index 000000000..0fe38fc1a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollBar.cs @@ -0,0 +1,941 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + + using System; + using System.Security.Permissions; + + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Globalization; + + + /// + /// + /// + /// Implements the basic functionality of a scroll bar control. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultEvent("Scroll"), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett + ] + public abstract class ScrollBar : Control { + + private static readonly object EVENT_SCROLL = new object(); + private static readonly object EVENT_VALUECHANGED = new object(); + + private int minimum = 0; + private int maximum = 100; + private int smallChange = 1; + private int largeChange = 10; + private int value = 0; + private ScrollOrientation scrollOrientation; + private int wheelDelta = 0; + private bool scaleScrollBarForDpiChange = true; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// + public ScrollBar() + : base() { + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.StandardClick, false); + SetStyle(ControlStyles.UseTextForAccessibility, false); + + TabStop = false; + + if ((this.CreateParams.Style & NativeMethods.SBS_VERT) != 0) + { + scrollOrientation = ScrollOrientation.VerticalScroll; + } + else + { + scrollOrientation = ScrollOrientation.HorizontalScroll; + } + } + + /// + /// Hide AutoSize: it doesn't make sense for this control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Retrieves the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "SCROLLBAR"; + cp.Style &= (~NativeMethods.WS_BORDER); + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + protected override Padding DefaultMargin { + get { + return Padding.Empty; + } + } + + /// + /// rescale constants for the DPI change + /// + /// ols dpi + /// new dpi + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (DpiHelper.EnableDpiChangedHighDpiImprovements && ScaleScrollBarForDpiChange) { + Scale((float)deviceDpiNew / deviceDpiOld); + } + } + + /// + /// + /// + /// + /// Gets or sets the foreground color of the scroll bar control. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets a value to be added or subtracted to the + /// property when the scroll box is moved a large distance. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(10), + SRDescription(SR.ScrollBarLargeChangeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int LargeChange { + get { + // We preserve the actual large change value that has been set, but when we come to + // get the value of this property, make sure it's within the maximum allowable value. + // This way we ensure that we don't depend on the order of property sets when + // code is generated at design-time. + // + return Math.Min(largeChange, maximum - minimum + 1); + } + set { + if (largeChange != value) { + + if (value < 0) { + throw new ArgumentOutOfRangeException("LargeChange", SR.GetString(SR.InvalidLowBoundArgumentEx, "LargeChange", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + largeChange = value; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets the upper limit of values of the scrollable range. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(100), + SRDescription(SR.ScrollBarMaximumDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int Maximum { + get { + return maximum; + } + set { + if (maximum != value) { + if (minimum > value) + minimum = value; + // bring this.value in line. + if (value < this.value) + Value = value; + maximum = value; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets the lower limit of values of the scrollable range. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + SRDescription(SR.ScrollBarMinimumDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int Minimum { + get { + return minimum; + } + set { + if (minimum != value) { + if (maximum < value) + maximum = value; + // bring this.value in line. + if (value > this.value) + this.value = value; + minimum = value; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets the value to be added or subtracted to the + /// + /// property when the scroll box is + /// moved a small distance. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(1), + SRDescription(SR.ScrollBarSmallChangeDescr) + ] + public int SmallChange { + get { + // We can't have SmallChange > LargeChange, but we shouldn't manipulate + // the set values for these properties, so we just return the smaller + // value here. + // + return Math.Min(smallChange, LargeChange); + } + set { + if (smallChange != value) { + + if (value < 0) { + throw new ArgumentOutOfRangeException("SmallChange", SR.GetString(SR.InvalidLowBoundArgumentEx, "SmallChange", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + smallChange = value; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// + [DefaultValue(false)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + /// + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets a numeric value that represents the current + /// position of the scroll box + /// on + /// the scroll bar control. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Bindable(true), + SRDescription(SR.ScrollBarValueDescr) + ] + public int Value { + get { + return value; + } + set { + if (this.value != value) { + if (value < minimum || value > maximum) { + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", (value).ToString(CultureInfo.CurrentCulture), "'minimum'", "'maximum'")); + } + this.value = value; + UpdateScrollInfo(); + OnValueChanged(EventArgs.Empty); + } + } + } + + /// + /// Get/Set flag to let scrollbar scale according to the DPI of the window. + /// + [SRCategory(SR.CatBehavior), + DefaultValue(true), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + SRDescription(SR.ControlDpiChangeScale)] + public bool ScaleScrollBarForDpiChange { + get { + return scaleScrollBarForDpiChange; + } + set { + scaleScrollBarForDpiChange = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + /// ScrollBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDown { + add { + base.MouseDown += value; + } + remove { + base.MouseDown -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseUp { + add { + base.MouseUp += value; + } + remove { + base.MouseUp -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseMove { + add { + base.MouseMove += value; + } + remove { + base.MouseMove -= value; + } + } + + /// + /// + /// + /// Occurs when the scroll box has + /// been + /// moved by either a mouse or keyboard action. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.ScrollBarOnScrollDescr)] + public event ScrollEventHandler Scroll { + add { + Events.AddHandler(EVENT_SCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_SCROLL, value); + } + } + + /// + /// + /// + /// Occurs when the property has changed, either by a + /// event or programatically. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.valueChangedEventDescr)] + public event EventHandler ValueChanged { + add { + Events.AddHandler(EVENT_VALUECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_VALUECHANGED, value); + } + } + + + /// + /// This is a helper method that is called by ScaleControl to retrieve the bounds + /// that the control should be scaled by. You may override this method if you + /// wish to reuse ScaleControl's scaling logic but you need to supply your own + /// bounds. The default implementation returns scaled bounds that take into + /// account the BoundsSpecified, whether the control is top level, and whether + /// the control is fixed width or auto size, and any adornments the control may have. + /// + protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { + + // Adjust Specified for vertical or horizontal scaling + if (scrollOrientation == ScrollOrientation.VerticalScroll) { + specified &= ~BoundsSpecified.Width; + } + else { + specified &= ~BoundsSpecified.Height; + } + + return base.GetScaledBounds(bounds, factor, specified); + } + + internal override IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) + { + return IntPtr.Zero; + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnEnabledChanged(EventArgs e) { + if (Enabled) { + UpdateScrollInfo(); + } + base.OnEnabledChanged(e); + } + + /// + /// + /// Creates the handle. overridden to help set up scrollbar information. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + UpdateScrollInfo(); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnScroll(ScrollEventArgs se) { + ScrollEventHandler handler = (ScrollEventHandler)Events[EVENT_SCROLL]; + if (handler != null) handler(this,se); + } + + /// + /// + /// Converts mouse wheel movements into scrolling, when scrollbar has the focus. + /// Typically one wheel step will cause one small scroll increment, in either + /// direction. A single wheel message could represent either one wheel step, multiple + /// wheel steps (fast wheeling), or even a fraction of a step (smooth-wheeled mice). + /// So we accumulate the total wheel delta, and consume it in whole numbers of steps. + /// + protected override void OnMouseWheel(MouseEventArgs e) { + wheelDelta += e.Delta; + + bool scrolled = false; + + while (Math.Abs(wheelDelta) >= NativeMethods.WHEEL_DELTA) { + if (wheelDelta > 0) { + wheelDelta -= NativeMethods.WHEEL_DELTA; + DoScroll(ScrollEventType.SmallDecrement); + scrolled = true; + } + else { + wheelDelta += NativeMethods.WHEEL_DELTA; + DoScroll(ScrollEventType.SmallIncrement); + scrolled = true; + } + } + + if (scrolled) { + DoScroll(ScrollEventType.EndScroll); + } + + if (e is HandledMouseEventArgs) { + ((HandledMouseEventArgs) e).Handled = true; + } + + // The base implementation should be called before the implementation above, + // but changing the order in Whidbey would be too much of a breaking change + // for this particular class. + base.OnMouseWheel(e); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnValueChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_VALUECHANGED]; + if (handler != null) handler(this,e); + } + + // Reflects the position of the scrollbar + private int ReflectPosition(int position) { + if (this is HScrollBar) { + return minimum + (maximum - LargeChange + 1) - position; + } + return position; + } + + /// + /// + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", Minimum: " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum: " + Maximum.ToString(CultureInfo.CurrentCulture) + ", Value: " + Value.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// Internal helper method + /// + /// + protected void UpdateScrollInfo() { + if (IsHandleCreated && Enabled) { + + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_ALL; + si.nMin = minimum; + si.nMax = maximum; + si.nPage = LargeChange; + + if (RightToLeft == RightToLeft.Yes) { + // Reflect the scrollbar position horizontally on an Rtl system + si.nPos = ReflectPosition(value); + } + else { + si.nPos = value; + } + + si.nTrackPos = 0; + + UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_CTL, si, true); + } + } + + /// + /// + /// + /// + private void WmReflectScroll(ref Message m) { + ScrollEventType type = (ScrollEventType)NativeMethods.Util.LOWORD(m.WParam); + DoScroll(type); + } + + /// + /// + /// + /// + private void DoScroll(ScrollEventType type) { + + // For Rtl systems we need to swap increment and decrement + // + if (RightToLeft == RightToLeft.Yes) { + switch (type) { + case ScrollEventType.First: + type = ScrollEventType.Last; + break; + + case ScrollEventType.Last: + type = ScrollEventType.First; + break; + + case ScrollEventType.SmallDecrement: + type = ScrollEventType.SmallIncrement; + break; + + case ScrollEventType.SmallIncrement: + type = ScrollEventType.SmallDecrement; + break; + + case ScrollEventType.LargeDecrement: + type = ScrollEventType.LargeIncrement; + break; + + case ScrollEventType.LargeIncrement: + type = ScrollEventType.LargeDecrement; + break; + } + } + + int newValue = value; + int oldValue = value; + + // The ScrollEventArgs constants are defined in terms of the windows + // messages.. this eliminates confusion between the VSCROLL and + // HSCROLL constants, which are identical. + // + switch (type) { + case ScrollEventType.First: + newValue = minimum; + break; + + case ScrollEventType.Last: + newValue = maximum - LargeChange + 1; // si.nMax - si.nPage + 1; + break; + + case ScrollEventType.SmallDecrement: + newValue = Math.Max(value - SmallChange, minimum); + break; + + case ScrollEventType.SmallIncrement: + newValue = Math.Min(value + SmallChange, maximum - LargeChange + 1); // max - lChange + 1); + break; + + case ScrollEventType.LargeDecrement: + newValue = Math.Max(value - LargeChange, minimum); + break; + + case ScrollEventType.LargeIncrement: + newValue = Math.Min(value + LargeChange, maximum - LargeChange + 1); // si.nPos + si.nPage,si.nMax - si.nPage + 1); + break; + + case ScrollEventType.ThumbPosition: + case ScrollEventType.ThumbTrack: + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.fMask = NativeMethods.SIF_TRACKPOS; + SafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_CTL, si); + + if (RightToLeft == RightToLeft.Yes) { + newValue = ReflectPosition(si.nTrackPos); + } + else { + newValue = si.nTrackPos; + } + + break; + } + + ScrollEventArgs se = new ScrollEventArgs(type, oldValue, newValue, this.scrollOrientation); + OnScroll(se); + Value = se.NewValue; + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_HSCROLL: + case NativeMethods.WM_REFLECT + NativeMethods.WM_VSCROLL: + WmReflectScroll(ref m); + break; + case NativeMethods.WM_ERASEBKGND: + break; + + case NativeMethods.WM_SIZE: + //VS7#13707 : Microsoft, 4/26/1999 - Fixes the scrollbar focus rect + if (UnsafeNativeMethods.GetFocus() == this.Handle) { + DefWndProc(ref m); + SendMessage(NativeMethods.WM_KILLFOCUS, 0, 0); + SendMessage(NativeMethods.WM_SETFOCUS, 0, 0); + } + break; + + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollBarRenderer.cs b/WindowsForms/Managed/System/WinForms/ScrollBarRenderer.cs new file mode 100644 index 000000000..d3af7ad50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollBarRenderer.cs @@ -0,0 +1,208 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Windows.Forms.VisualStyles; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the ScrollBar control. + /// + /// + public sealed class ScrollBarRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + + //cannot instantiate + private ScrollBarRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + /// + /// + /// + /// Renders a ScrollBar arrow button. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawArrowButton(Graphics g, Rectangle bounds, ScrollBarArrowButtonState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.ArrowButton.LeftNormal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal ScrollBar thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawHorizontalThumb(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical ScrollBar thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawVerticalThumb(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.ThumbButtonVertical.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal ScrollBar thumb grip. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawHorizontalThumbGrip(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.GripperHorizontal.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical ScrollBar thumb grip. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawVerticalThumbGrip(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.GripperVertical.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal ScrollBar thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawRightHorizontalTrack(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.RightTrackHorizontal.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal ScrollBar thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawLeftHorizontalTrack(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.LeftTrackHorizontal.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical ScrollBar thumb in the center of the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawUpperVerticalTrack(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.UpperTrackVertical.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical ScrollBar thumb in the center of the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawLowerVerticalTrack(Graphics g, Rectangle bounds, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.LowerTrackVertical.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a ScrollBar size box in the center of the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawSizeBox(Graphics g, Rectangle bounds, ScrollBarSizeBoxState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.SizeBox.LeftAlign, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Returns the size of the ScrollBar thumb grip. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetThumbGripSize(Graphics g, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.GripperHorizontal.Normal, (int)state); + + return visualStyleRenderer.GetPartSize(g, ThemeSizeType.True); + } + + /// + /// + /// + /// Returns the size of the ScrollBar size box. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetSizeBoxSize(Graphics g, ScrollBarState state) { + InitializeRenderer(VisualStyleElement.ScrollBar.SizeBox.LeftAlign, (int)state); + + return visualStyleRenderer.GetPartSize(g, ThemeSizeType.True); + } + + private static void InitializeRenderer(VisualStyleElement element, int state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(element.ClassName, element.Part, state); + } + else { + visualStyleRenderer.SetParameters(element.ClassName, element.Part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollBars.cs b/WindowsForms/Managed/System/WinForms/ScrollBars.cs new file mode 100644 index 000000000..3853da023 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollBars.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies + /// which scroll bars will be visible on a control. + /// + /// + /// + public enum ScrollBars { + + /// + /// + /// + /// No scroll bars are shown. + /// + /// + /// + None = 0, + + /// + /// + /// + /// Only horizontal scroll bars are shown. + /// + /// + /// + Horizontal = 1, + + /// + /// + /// + /// Only vertical scroll bars are shown. + /// + /// + /// + Vertical = 2, + + /// + /// + /// + /// Both horizontal and vertical scroll bars are shown. + /// + /// + /// + Both = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollButton.cs b/WindowsForms/Managed/System/WinForms/ScrollButton.cs new file mode 100644 index 000000000..778781702 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollButton.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies the type of + /// scroll arrow to create on a scroll bar. + /// + /// + /// + public enum ScrollButton { + + /// + /// + /// + /// A down-scroll arrow. + /// + /// + /// + Down = NativeMethods.DFCS_SCROLLDOWN, + + /// + /// + /// + /// A left-scroll arrow. + /// + /// + /// + Left = NativeMethods.DFCS_SCROLLLEFT, + + /// + /// + /// + /// A right-scroll arrow. + /// + /// + /// + Right = NativeMethods.DFCS_SCROLLRIGHT, + + /// + /// + /// + /// An up-scroll arrow. + /// + /// + /// + Up = NativeMethods.DFCS_SCROLLUP, + + /// + /// + /// + Min = NativeMethods.DFCS_SCROLLUP, + + /// + Max = NativeMethods.DFCS_SCROLLRIGHT, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollEvent.cs b/WindowsForms/Managed/System/WinForms/ScrollEvent.cs new file mode 100644 index 000000000..13254f75a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollEvent.cs @@ -0,0 +1,131 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Provides data for the + /// event. + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ScrollEventArgs : EventArgs { + + readonly ScrollEventType type; + int newValue; + private ScrollOrientation scrollOrientation; + int oldValue = -1; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ScrollEventArgs(ScrollEventType type, int newValue) { + this.type = type; + this.newValue = newValue; + } + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ScrollEventArgs(ScrollEventType type, int newValue, ScrollOrientation scroll) { + this.type = type; + this.newValue = newValue; + this.scrollOrientation = scroll; + } + + /// + public ScrollEventArgs(ScrollEventType type, int oldValue, int newValue) { + this.type = type; + this.newValue = newValue; + this.oldValue = oldValue; + + } + + + /// + public ScrollEventArgs(ScrollEventType type, int oldValue, int newValue, ScrollOrientation scroll) { + this.type = type; + this.newValue = newValue; + this.scrollOrientation = scroll; + this.oldValue = oldValue; + } + + /// + /// + /// + /// Specifies the type of scroll event that occurred. + /// + /// + /// + public ScrollOrientation ScrollOrientation { + get { + return scrollOrientation; + } + } + + /// + /// + /// + /// Specifies the type of scroll event that occurred. + /// + /// + /// + public ScrollEventType Type { + get { + return type; + } + } + + /// + /// + /// + /// Specifies the new location of the scroll box + /// within the + /// scroll bar. + /// + /// + /// + public int NewValue { + get { + return newValue; + } + set { + newValue = value; + } + } + + /// + /// + /// + /// Specifies the last position + /// within the + /// scroll bar. + /// + /// + public int OldValue { + get { + return oldValue; + } + } + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollEventHandler.cs b/WindowsForms/Managed/System/WinForms/ScrollEventHandler.cs new file mode 100644 index 000000000..6c18e45aa --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that handles the + /// event of a , , or + /// . + /// + /// + /// + public delegate void ScrollEventHandler(object sender, ScrollEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollEventType.cs b/WindowsForms/Managed/System/WinForms/ScrollEventType.cs new file mode 100644 index 000000000..f64207c3d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollEventType.cs @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the type of action used to raise the event. + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum ScrollEventType { + + /// + /// + /// + /// The + /// scroll box was + /// moved a small + /// distance. The user clicked the left(horizontal) or top(vertical) scroll arrow or pressed the UP ARROW + /// key. + /// + /// + /// + SmallDecrement = NativeMethods.SB_LINELEFT, + + /// + /// + /// + /// The + /// scroll box was + /// moved a small distance. The user clicked the right(horizontal) or bottom(vertical) scroll arrow or + /// pressed the DOWN ARROW key. + /// + /// + /// + SmallIncrement = NativeMethods.SB_LINERIGHT, + + /// + /// + /// + /// The scroll box + /// moved a large distance. The user clicked the scroll bar to the left(horizontal) or above(vertical) + /// the scroll box, or pressed the PAGE UP key. + /// + /// + /// + LargeDecrement = NativeMethods.SB_PAGELEFT, + + /// + /// + /// + /// The scroll box moved a large distance. The user clicked the scroll bar to + /// the right(horizontal) or below(vertical) the scroll box, or pressed the PAGE DOWN key. + /// + /// + /// + LargeIncrement = NativeMethods.SB_PAGERIGHT, + + /// + /// + /// + /// The scroll box was moved. + /// + /// + /// + ThumbPosition = NativeMethods.SB_THUMBPOSITION, + + /// + /// + /// + /// The scroll box + /// is currently being moved. + /// + /// + /// + ThumbTrack = NativeMethods.SB_THUMBTRACK, + + /// + /// + /// + /// The + /// scroll box was moved to the + /// position. + /// + /// + /// + First = NativeMethods.SB_LEFT, + + /// + /// + /// + /// The + /// scroll box was moved to the + /// position. + /// + /// + /// + Last = NativeMethods.SB_RIGHT, + + /// + /// + /// + /// The scroll box has stopped moving. + /// + /// + /// + EndScroll = NativeMethods.SB_ENDSCROLL, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollOrientation.cs b/WindowsForms/Managed/System/WinForms/ScrollOrientation.cs new file mode 100644 index 000000000..f167aed43 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollOrientation.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// Provides data for the + /// event. This enumeration gives the orientation of the scroll that took place + /// + public enum ScrollOrientation { + + /// + /// + /// + /// Denotes that horizontal scrolling took place. + /// + /// + HorizontalScroll, + + /// + /// + /// + /// Denotes that vertical scrolling took place. + /// + /// + VerticalScroll + } +} diff --git a/WindowsForms/Managed/System/WinForms/ScrollProperties.cs b/WindowsForms/Managed/System/WinForms/ScrollProperties.cs new file mode 100644 index 000000000..6ae58b770 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollProperties.cs @@ -0,0 +1,343 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms; + using System.Globalization; + + /// + /// + /// + /// Basic Properties for Scrollbars. + /// + /// + public abstract class ScrollProperties { + + internal int minimum = 0; + internal int maximum = 100; + internal int smallChange = 1; + internal int largeChange = 10; + internal int value = 0; + internal bool maximumSetExternally; + internal bool smallChangeSetExternally; + internal bool largeChangeSetExternally; + + /// + private ScrollableControl parent; + + protected ScrollableControl ParentControl { + get + { + return parent; + } + } + + /// + /// Number of pixels to scroll the client region as a "line" for autoscroll. + /// + /// + private const int SCROLL_LINE = 5; + + internal bool visible = false; + + //Always Enabled + private bool enabled = true; + + + /// + protected ScrollProperties(ScrollableControl container) { + this.parent = container; + } + + /// + /// + /// + /// Gets or sets a bool value controlling whether the scrollbar is enabled. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.ScrollBarEnableDescr) + ] + public bool Enabled { + get { + return enabled; + } + set { + if (parent.AutoScroll) { + return; + } + if (value != enabled) { + enabled = value; + EnableScroll(value); + } + } + } + + + /// + /// + /// + /// Gets or sets a value to be added or subtracted to the + /// property when the scroll box is moved a large distance. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(10), + SRDescription(SR.ScrollBarLargeChangeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int LargeChange { + get { + // We preserve the actual large change value that has been set, but when we come to + // get the value of this property, make sure it's within the maximum allowable value. + // This way we ensure that we don't depend on the order of property sets when + // code is generated at design-time. + // + return Math.Min(largeChange, maximum - minimum + 1); + } + set { + if (largeChange != value ) { + if (value < 0) { + throw new ArgumentOutOfRangeException("LargeChange", SR.GetString(SR.InvalidLowBoundArgumentEx, "LargeChange", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + largeChange = value; + largeChangeSetExternally = true; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets the upper limit of values of the scrollable range. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(100), + SRDescription(SR.ScrollBarMaximumDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int Maximum { + get { + return maximum; + } + set { + if (parent.AutoScroll) { + return; + } + if (maximum != value) { + if (minimum > value) + minimum = value; + // bring this.value in line. + if (value < this.value) + Value = value; + maximum = value; + maximumSetExternally = true; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets the lower limit of values of the scrollable range. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + SRDescription(SR.ScrollBarMinimumDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public int Minimum { + get { + return minimum; + } + + set { + if (parent.AutoScroll) { + return; + } + if (minimum != value) { + if (value < 0) { + throw new ArgumentOutOfRangeException("Minimum", SR.GetString(SR.InvalidLowBoundArgumentEx, "Minimum", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (maximum < value) + maximum = value; + // bring this.value in line. + if (value > this.value) + this.value = value; + minimum = value; + UpdateScrollInfo(); + } + } + } + + internal abstract int PageSize { + get; + } + + internal abstract int Orientation { + get; + } + + internal abstract int HorizontalDisplayPosition { + get; + } + + + internal abstract int VerticalDisplayPosition { + get; + } + + /// + /// + /// + /// Gets or sets the value to be added or subtracted to the + /// + /// property when the scroll box is + /// moved a small distance. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(1), + SRDescription(SR.ScrollBarSmallChangeDescr) + ] + public int SmallChange { + get { + // We can't have SmallChange > LargeChange, but we shouldn't manipulate + // the set values for these properties, so we just return the smaller + // value here. + // + return Math.Min(smallChange, LargeChange); + } + set { + if (smallChange != value) { + + if (value < 0) { + throw new ArgumentOutOfRangeException("SmallChange", SR.GetString(SR.InvalidLowBoundArgumentEx, "SmallChange", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + smallChange = value; + smallChangeSetExternally = true; + UpdateScrollInfo(); + } + } + } + + /// + /// + /// + /// Gets or sets a numeric value that represents the current + /// position of the scroll box + /// on + /// the scroll bar control. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Bindable(true), + SRDescription(SR.ScrollBarValueDescr) + ] + public int Value { + get { + return value; + } + set { + if (this.value != value) { + if (value < minimum || value > maximum) { + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", (value).ToString(CultureInfo.CurrentCulture), "'minimum'", "'maximum'")); + } + this.value = value; + UpdateScrollInfo(); + parent.SetDisplayFromScrollProps(HorizontalDisplayPosition, VerticalDisplayPosition); + } + } + } + + + /// + /// + /// + /// Gets or sets a bool value controlling whether the scrollbar is showing. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ScrollBarVisibleDescr) + ] + public bool Visible { + get { + return visible; + } + set { + if (parent.AutoScroll) { + return; + } + if (value != visible) { + visible = value; + parent.UpdateStylesCore (); + UpdateScrollInfo(); + parent.SetDisplayFromScrollProps(HorizontalDisplayPosition, VerticalDisplayPosition); + } + } + } + + /// + /// + /// Internal helper method + /// + /// + internal void UpdateScrollInfo() { + if (parent.IsHandleCreated && visible) { + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_ALL; + si.nMin = minimum; + si.nMax = maximum; + si.nPage = (parent.AutoScroll) ? PageSize : LargeChange; + si.nPos = value; + si.nTrackPos = 0; + UnsafeNativeMethods.SetScrollInfo(new HandleRef(parent, parent.Handle), Orientation, si, true); + } + } + + /// + /// + /// Internal helper method for enabling or disabling the Vertical Scroll bar. + /// + /// + private void EnableScroll(bool enable) + { + if (enable) { + UnsafeNativeMethods.EnableScrollBar(new HandleRef(parent, parent.Handle), Orientation, NativeMethods.ESB_ENABLE_BOTH); + } + else { + UnsafeNativeMethods.EnableScrollBar(new HandleRef(parent, parent.Handle), Orientation, NativeMethods.ESB_DISABLE_BOTH); + } + + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ScrollableControl.cs b/WindowsForms/Managed/System/WinForms/ScrollableControl.cs new file mode 100644 index 000000000..dcf9ad3b3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ScrollableControl.cs @@ -0,0 +1,1777 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using System.Security.Permissions; + using System.Reflection; + using System.Windows.Forms.Layout; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Defines a base class for controls that support auto-scrolling behavior. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.ScrollableControlDesigner, " + AssemblyRef.SystemDesign) + ] + public class ScrollableControl : Control, IArrangedElement { +#if DEBUG + internal static readonly TraceSwitch AutoScrolling = new TraceSwitch("AutoScrolling", "Debug autoscrolling logic"); +#else + internal static readonly TraceSwitch AutoScrolling; +#endif + + /// + /// + protected const int ScrollStateAutoScrolling = 0x0001; + /// + /// + /// + /// + protected const int ScrollStateHScrollVisible = 0x0002; + /// + /// + /// + /// + protected const int ScrollStateVScrollVisible = 0x0004; + /// + /// + /// + /// + protected const int ScrollStateUserHasScrolled = 0x0008; + /// + /// + /// + /// + protected const int ScrollStateFullDrag = 0x0010; + + + private Size userAutoScrollMinSize = System.Drawing.Size.Empty; + /// + /// Current size of the displayRect. + /// + /// + private Rectangle displayRect = Rectangle.Empty; + /// + /// Current margins for autoscrolling. + /// + /// + private Size scrollMargin = System.Drawing.Size.Empty; + /// + /// User requested margins for autoscrolling. + /// + /// + private Size requestedScrollMargin = System.Drawing.Size.Empty; + /// + /// User requested autoscroll position - used for form creation only. + /// + /// + internal Point scrollPosition = Point.Empty; + + private DockPaddingEdges dockPadding = null; + + private int scrollState; + + VScrollProperties verticalScroll = null; + HScrollProperties horizontalScroll = null; + private static readonly object EVENT_SCROLL = new object(); + + + // Used to figure out what the horizontal scroll value should be set to when + // the horizontal scrollbar is first shown + private bool resetRTLHScrollValue = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public ScrollableControl() + : base() { + SetStyle(ControlStyles.ContainerControl, true); + SetStyle(ControlStyles.AllPaintingInWmPaint, false); + SetScrollState(ScrollStateAutoScrolling, false); + } + + /// + /// + /// + /// Gets + /// or sets a value + /// indicating whether the container will allow the user to scroll to any + /// controls placed outside of its visible boundaries. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + DefaultValue(false), + SRDescription(SR.FormAutoScrollDescr) + ] + public virtual bool AutoScroll { + get { + return GetScrollState(ScrollStateAutoScrolling); + } + + set { + if (value) { + UpdateFullDrag(); + } + + SetScrollState(ScrollStateAutoScrolling, value); + LayoutTransaction.DoLayout(this, this, PropertyNames.AutoScroll); + } + } + + /// + /// + /// + /// Gets or + /// sets the size of the auto-scroll + /// margin. + /// + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormAutoScrollMarginDescr) + ] + public Size AutoScrollMargin { + get { + return requestedScrollMargin; + } + + set { + if (value.Width < 0 || value.Height < 0) { + throw new ArgumentOutOfRangeException("AutoScrollMargin", SR.GetString(SR.InvalidArgument, "AutoScrollMargin", value.ToString())); + } + SetAutoScrollMargin(value.Width, value.Height); + } + } + + /// + /// + /// Gets or sets the location of the auto-scroll position. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormAutoScrollPositionDescr) + ] + public Point AutoScrollPosition { + get { + Rectangle rect = GetDisplayRectInternal(); + return new Point(rect.X, rect.Y); + } + + set { + if (Created) { + SetDisplayRectLocation(-value.X, -value.Y); + SyncScrollbars(true); + } + + scrollPosition = value; + } + } + + /// + /// + /// Gets or sets the mimimum size of the auto-scroll. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.FormAutoScrollMinSizeDescr) + ] + public Size AutoScrollMinSize { + get { + return userAutoScrollMinSize; + } + + set { + if (value != userAutoScrollMinSize) { + userAutoScrollMinSize = value; + AutoScroll = true; + PerformLayout(); + } + } + } + + /// + /// + /// + /// + /// Retrieves the CreateParams used to create the window. + /// If a subclass overrides this function, it must call the base implementation. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + if (HScroll || HorizontalScroll.Visible) { + cp.Style |= NativeMethods.WS_HSCROLL; + } + else { + cp.Style &= (~NativeMethods.WS_HSCROLL); + } + if (VScroll || VerticalScroll.Visible) { + cp.Style |= NativeMethods.WS_VSCROLL; + } + else { + cp.Style &= (~NativeMethods.WS_VSCROLL); + } + + return cp; + } + } + + /// + /// + /// + /// + /// Retreives the current display rectangle. The display rectangle + /// is the virtual display area that is used to layout components. + /// The position and dimensions of the Form's display rectangle + /// change during autoScroll. + /// + /// + public override Rectangle DisplayRectangle { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + Rectangle rect = base.ClientRectangle; + if (!displayRect.IsEmpty) { + rect.X = displayRect.X; + rect.Y = displayRect.Y; + if (HScroll) { + rect.Width = displayRect.Width; + } + if (VScroll) { + rect.Height = displayRect.Height; + } + } + return LayoutUtils.DeflateRect(rect, Padding); + } + } + + /// + /// + Rectangle IArrangedElement.DisplayRectangle { + get { + Rectangle displayRectangle = this.DisplayRectangle; + // V7#79 : Controls anchored the bottom of their container may disappear (be scrunched) + // when scrolling is used. + if(AutoScrollMinSize.Width != 0 && AutoScrollMinSize.Height != 0) { + displayRectangle.Width = Math.Max(displayRectangle.Width, AutoScrollMinSize.Width); + displayRectangle.Height = Math.Max(displayRectangle.Height, AutoScrollMinSize.Height); + } + return displayRectangle; + } + } + + /// + /// + /// + /// Gets or + /// sets a value indicating whether the horizontal scroll bar is visible. + /// + /// + protected bool HScroll { + get { + return GetScrollState(ScrollStateHScrollVisible); + } + set { + SetScrollState(ScrollStateHScrollVisible, value); + } + } + + /// + /// + /// Gets the Horizontal Scroll bar for this ScrollableControl. + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.ScrollableControlHorizontalScrollDescr), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always) + ] + public HScrollProperties HorizontalScroll { + get { + if (horizontalScroll == null) { + horizontalScroll = new HScrollProperties(this); + } + return horizontalScroll; + } + } + + + + + + /// + /// + /// + /// Gets or + /// sets a value indicating whether the vertical scroll bar is visible. + /// + /// + protected bool VScroll { + get { + return GetScrollState(ScrollStateVScrollVisible); + } + set { + SetScrollState(ScrollStateVScrollVisible, value); + } + } + + + /// + /// + /// Gets the Veritcal Scroll bar for this ScrollableControl. + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.ScrollableControlVerticalScrollDescr), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always) + ] + public VScrollProperties VerticalScroll { + get { + if (verticalScroll == null) { + verticalScroll = new VScrollProperties(this); + } + return verticalScroll; + } + } + + /// + /// + /// Gets the dock padding settings for all + /// edges of the control. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public DockPaddingEdges DockPadding { + get { + if (dockPadding == null) { + dockPadding = new DockPaddingEdges(this); + } + return dockPadding; + } + } + + /// + /// + /// + /// Adjusts + /// the auto-scroll bars on the container based on the current control + /// positions and the control currently selected. + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void AdjustFormScrollbars(bool displayScrollbars) { + bool needLayout = false; + + Rectangle display = GetDisplayRectInternal(); + + if (!displayScrollbars && (HScroll || VScroll)) { + needLayout = SetVisibleScrollbars(false, false); + } + + if (!displayScrollbars) { + Rectangle client = ClientRectangle; + display.Width = client.Width; + display.Height = client.Height; + } + else { + needLayout |= ApplyScrollbarChanges(display); + } + + if (needLayout) { + LayoutTransaction.DoLayout(this, this, PropertyNames.DisplayRectangle); + } + } + + private bool ApplyScrollbarChanges(Rectangle display) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ApplyScrollbarChanges(" + display + ") {"); + Debug.Indent(); + + bool needLayout = false; + bool needHscroll = false; + bool needVscroll = false; + Rectangle currentClient = ClientRectangle; + Rectangle fullClient = currentClient; + Rectangle minClient = fullClient; + if (HScroll) { + fullClient.Height += SystemInformation.HorizontalScrollBarHeight; + } + else { + minClient.Height -= SystemInformation.HorizontalScrollBarHeight; + } + + if (VScroll) { + fullClient.Width += SystemInformation.VerticalScrollBarWidth; + } + else { + minClient.Width -= SystemInformation.VerticalScrollBarWidth; + } + + int maxX = minClient.Width; + int maxY = minClient.Height; + + if (Controls.Count != 0) { + + // Compute the actual scroll margins (take into account docked + // things.) + // + scrollMargin = requestedScrollMargin; + + if (dockPadding != null) { + scrollMargin.Height += Padding.Bottom; + scrollMargin.Width += Padding.Right; + } + + for (int i=0; i maxX) { + needHscroll = true; + maxX = layoutBounds.Width; + } + if (layoutBounds.Height > maxY) { + needVscroll = true; + maxY = layoutBounds.Height; + } + } + else if (Controls.Count != 0) { + + // Compute the dimensions of the display rect + // + for (int i=0; i < Controls.Count; i++) { + bool watchHoriz = true; + bool watchVert = true; + + Control current = Controls[i]; + + // Same logic as the margin calc - you need to see if the + // control *will* be visible... + // + if (current != null && current.GetState(STATE_VISIBLE)) { + if (defaultLayoutEngine) { + Control richCurrent = (Control)current; + + switch (richCurrent.Dock) { + case DockStyle.Top: + watchHoriz = false; + break; + case DockStyle.Left: + watchVert = false; + break; + case DockStyle.Bottom: + case DockStyle.Fill: + case DockStyle.Right: + watchHoriz = false; + watchVert = false; + break; + default: + AnchorStyles anchor = richCurrent.Anchor; + if ((anchor & AnchorStyles.Right) == AnchorStyles.Right) { + watchHoriz = false; + } + if ((anchor & AnchorStyles.Left) != AnchorStyles.Left) { + watchHoriz = false; + } + if ((anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom) { + watchVert = false; + } + if ((anchor & AnchorStyles.Top) != AnchorStyles.Top) { + watchVert = false; + } + break; + } + } + + if (watchHoriz || watchVert) { + Rectangle bounds = current.Bounds; + int ctlRight = -display.X + bounds.X + bounds.Width + scrollMargin.Width; + int ctlBottom = -display.Y + bounds.Y + bounds.Height + scrollMargin.Height; + + if (!defaultLayoutEngine) + { + ctlRight += current.Margin.Right; + ctlBottom += current.Margin.Bottom; + } + + if (ctlRight > maxX && watchHoriz) { + needHscroll = true; + maxX = ctlRight; + } + if (ctlBottom > maxY && watchVert) { + needVscroll = true; + maxY = ctlBottom; + } + } + } + } + } + + // Check maxX/maxY against the clientRect, we must compare it to the + // clientRect without any scrollbars, and then we can check it against + // the clientRect with the "new" scrollbars. This will make the + // scrollbars show and hide themselves correctly at the boundaries. + // + if (maxX <= fullClient.Width) { + needHscroll = false; + } + if (maxY <= fullClient.Height) { + needVscroll = false; + } + Rectangle clientToBe = fullClient; + if (needHscroll) { + clientToBe.Height -= SystemInformation.HorizontalScrollBarHeight; + } + if (needVscroll) { + clientToBe.Width -= SystemInformation.VerticalScrollBarWidth; + } + if (needHscroll && maxY > clientToBe.Height) { + needVscroll = true; + } + if (needVscroll && maxX > clientToBe.Width) { + needHscroll = true; + } + if (!needHscroll) { + maxX = clientToBe.Width; + } + if (!needVscroll) { + maxY = clientToBe.Height; + } + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Current scrollbars(" + HScroll + ", " + VScroll + ")"); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Needed scrollbars(" + needHscroll + ", " + needVscroll + ")"); + + // Show the needed scrollbars + // + needLayout = (SetVisibleScrollbars(needHscroll, needVscroll) || needLayout); + + // If needed, adjust the size... + // + if (HScroll || VScroll) { + needLayout = (SetDisplayRectangleSize(maxX, maxY) || needLayout); + } + // Else just update the display rect size... this keeps it as big as the client + // area in a resize scenario + // + else { + SetDisplayRectangleSize(maxX, maxY); + } + + // Sync up the scrollbars + // + SyncScrollbars(true); + + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, needLayout ? "Need layout" : "No layout changes"); + Debug.Unindent(); + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "}"); + return needLayout; + } + + private Rectangle GetDisplayRectInternal() { + if (displayRect.IsEmpty) { + displayRect = ClientRectangle; + } + if (!AutoScroll && this.HorizontalScroll.visible == true) { + displayRect = new Rectangle(displayRect.X, displayRect.Y, this.HorizontalScroll.Maximum, this.displayRect.Height); + } + if (!AutoScroll && this.VerticalScroll.visible == true) { + displayRect = new Rectangle(displayRect.X, displayRect.Y, this.displayRect.Width, this.VerticalScroll.Maximum); + } + return displayRect; + } + + + /// + /// + /// + /// Tests a given scroll state bit to determine if it is set. + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected bool GetScrollState(int bit) { + return(bit & scrollState) == bit; + } + + + /// + /// + /// + /// Forces the layout of any docked or anchored child controls. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLayout(LayoutEventArgs levent) { + + // V#33515 - Microsoft, 3/18/1998 + // We get into a problem when you change the docking of a control + // with autosizing on. Since the control (affectedControl) has + // already had the dock property changed, adjustFormScrollbars + // treats it as a docked control. However, since base.onLayout + // hasn't been called yet, the bounds of the control haven't been + // changed. + // + // We can't just call base.onLayout() once in this case, since + // adjusting the scrollbars COULD adjust the display area, and + // thus require a new layout. The result is that when you + // affect a control's layout, we are forced to layout twice. There + // isn't any noticible flicker, but this could be a perf problem... + // + if (levent.AffectedControl != null && AutoScroll) { + base.OnLayout(levent); + } + + AdjustFormScrollbars(AutoScroll); + base.OnLayout(levent); + + } + + /// + /// + /// + /// Handles mouse wheel processing for our scrollbars. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnMouseWheel(MouseEventArgs e) { + + // Favor the vertical scroll bar, since it's the most + // common use. However, if there isn't a vertical + // scroll and the horizontal is on, then wheel it around. + // + if (VScroll) { + Rectangle client = ClientRectangle; + int pos = -displayRect.Y; + int maxPos = -(client.Height - displayRect.Height); + + pos = Math.Max(pos - e.Delta, 0); + pos = Math.Min(pos, maxPos); + + SetDisplayRectLocation(displayRect.X, -pos); + SyncScrollbars(AutoScroll); + if (e is HandledMouseEventArgs) + { + ((HandledMouseEventArgs)e).Handled = true; + } + } + else if (HScroll) { + Rectangle client = ClientRectangle; + int pos = -displayRect.X; + int maxPos = -(client.Width - displayRect.Width); + + pos = Math.Max(pos - e.Delta, 0); + pos = Math.Min(pos, maxPos); + + SetDisplayRectLocation(-pos, displayRect.Y); + SyncScrollbars(AutoScroll); + if (e is HandledMouseEventArgs) + { + ((HandledMouseEventArgs)e).Handled = true; + } + } + + // The base implementation should be called before the implementation above, + // but changing the order in Whidbey would be too much of a breaking change + // for this particular class. + base.OnMouseWheel(e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + resetRTLHScrollValue = true; + // When the page becomes visible, we need to call OnLayout to adjust the scrollbars. + LayoutTransaction.DoLayout(this, this, PropertyNames.RightToLeft); + } + + /// + protected override void OnPaintBackground (PaintEventArgs e) { + if ((HScroll || VScroll) && + BackgroundImage != null && + (BackgroundImageLayout == ImageLayout.Zoom || BackgroundImageLayout == ImageLayout.Stretch || BackgroundImageLayout == ImageLayout.Center)) { + if (ControlPaint.IsImageTransparent(BackgroundImage)) { + PaintTransparentBackground(e, displayRect); + } + ControlPaint.DrawBackgroundImage(e.Graphics, BackgroundImage, BackColor, BackgroundImageLayout, displayRect, displayRect, displayRect.Location); + } + else { + base.OnPaintBackground(e); + } + } + + protected override void OnPaddingChanged(EventArgs e) { + // VSWhidbey 340011: DockPaddingEdges compat. + // dont call base in this instance - for App compat we should not fire Invalidate when + // the padding has changed. + EventHandler handler = (EventHandler)Events[Control.EventPaddingChanged]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnVisibleChanged(EventArgs e) { + if (Visible) { + // When the page becomes visible, we need to call OnLayout to adjust the scrollbars. + LayoutTransaction.DoLayout(this, this, PropertyNames.Visible); + } + base.OnVisibleChanged(e); + } + + // internal for Form to call + // + internal void ScaleDockPadding(float dx, float dy) { + if (dockPadding != null) { + dockPadding.Scale(dx, dy); + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + ScaleDockPadding(dx, dy); + base.ScaleCore(dx, dy); + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + ScaleDockPadding(factor.Width, factor.Height); + base.ScaleControl(factor, specified); + } + + // internal for the respective scroll bars to call so that the Display Rectangle is set to + // enable the visual scroll effect. + // + internal void SetDisplayFromScrollProps(int x, int y) + { + Rectangle display = GetDisplayRectInternal(); + ApplyScrollbarChanges(display); + SetDisplayRectLocation(x, y); + } + + /// + /// + /// Adjusts the displayRect to be at the offset x, y. The contents of the + /// Form is scrolled using Windows.ScrollWindowEx. + /// + // + // + + protected void SetDisplayRectLocation(int x, int y) { + int xDelta = 0; + int yDelta = 0; + + + Rectangle client = ClientRectangle; + //VSWhidbey 141644 - The DisplayRect property modifies + // the returned rect to include padding. We don't want to + // include this padding in our adjustment of the DisplayRect + // because it interferes with the scrolling. + Rectangle displayRectangle = displayRect; + int minX = Math.Min(client.Width - displayRectangle.Width, 0); + int minY = Math.Min(client.Height - displayRectangle.Height, 0); + + if (x > 0) { + x = 0; + } + if (y > 0) { + y = 0; + } + if (x < minX) { + x = minX; + } + if (y < minY) { + y = minY; + } + + if (displayRectangle.X != x) + { + xDelta = x - displayRectangle.X; + } + if (displayRectangle.Y != y) + { + yDelta = y - displayRectangle.Y; + } + displayRect.X = x; + displayRect.Y = y; + + if (xDelta != 0 || yDelta != 0 && IsHandleCreated) { + Rectangle cr = ClientRectangle; + NativeMethods.RECT rcClip = NativeMethods.RECT.FromXYWH(cr.X, cr.Y, cr.Width, cr.Height); + NativeMethods.RECT rcUpdate = NativeMethods.RECT.FromXYWH(cr.X, cr.Y, cr.Width, cr.Height); + SafeNativeMethods.ScrollWindowEx(new HandleRef(this, Handle), xDelta, yDelta, + null, + ref rcClip, + NativeMethods.NullHandleRef, + ref rcUpdate, + NativeMethods.SW_INVALIDATE + | NativeMethods.SW_ERASE + | NativeMethods.SW_SCROLLCHILDREN); + } + + + + // Force child controls to update bounds. + // + for (int i=0; i + /// + /// Scrolls the currently active control into view if we are an AutoScroll + /// Form that has the Horiz or Vert scrollbar displayed... + /// + public void ScrollControlIntoView(Control activeControl) { + Debug.WriteLineIf(ScrollableControl.AutoScrolling.TraceVerbose, "ScrollControlIntoView(" + activeControl.GetType().FullName + ")"); + Debug.Indent(); + + Rectangle client = ClientRectangle; + + if (IsDescendant(activeControl) + && AutoScroll + && (HScroll || VScroll) + && activeControl != null + && (client.Width > 0 && client.Height > 0)) { + + Debug.WriteLineIf(ScrollableControl.AutoScrolling.TraceVerbose, "Calculating..."); + + + Point scrollLocation = ScrollToControl(activeControl); + + + SetScrollState(ScrollStateUserHasScrolled, false); + SetDisplayRectLocation(scrollLocation.X, scrollLocation.Y); + SyncScrollbars(true); + } + + Debug.Unindent(); + } + + + /// DCR 194760: allow containers to tweak autoscrolling. when you tab between controls contained in the scrollable control + /// this allows you to set the scroll location. This would allow you to scroll to the middle of a control, where as the default is + /// the top of the control. + + /// + /// Additionally there is a new AutoScrollOffset property on the child controls themselves. This lets them control where they want to + /// be scrolled to. E.g. In SelectedIndexChanged for a ListBox, you could do: + /// + /// listBox1.AutoScrollOffset = parent.AutoScrollPosition; + /// + /// + protected virtual Point ScrollToControl(Control activeControl) { + + Rectangle client = ClientRectangle; + int xCalc = displayRect.X; + int yCalc = displayRect.Y; + int xMargin = scrollMargin.Width; + int yMargin = scrollMargin.Height; + + Rectangle bounds = activeControl.Bounds; + if (activeControl.ParentInternal != this) { + Debug.WriteLineIf(ScrollableControl.AutoScrolling.TraceVerbose, "not direct child, original bounds: " + bounds); + bounds = this.RectangleToClient(activeControl.ParentInternal.RectangleToScreen(bounds)); + } + Debug.WriteLineIf(ScrollableControl.AutoScrolling.TraceVerbose, "adjusted bounds: " + bounds); + + if (bounds.X < xMargin) { + xCalc = displayRect.X + xMargin - bounds.X; + } + else if (bounds.X + bounds.Width + xMargin > client.Width) { + + xCalc = client.Width - (bounds.X + bounds.Width + xMargin - displayRect.X); + + if (bounds.X + xCalc - displayRect.X < xMargin) { + xCalc = displayRect.X + xMargin - bounds.X; + } + } + + if (bounds.Y < yMargin) { + yCalc = displayRect.Y + yMargin - bounds.Y; + } + else if (bounds.Y + bounds.Height + yMargin > client.Height) { + + yCalc = client.Height - (bounds.Y + bounds.Height + yMargin - displayRect.Y); + + if (bounds.Y + yCalc - displayRect.Y < yMargin) { + yCalc = displayRect.Y + yMargin - bounds.Y; + } + } + + xCalc += activeControl.AutoScrollOffset.X; + yCalc += activeControl.AutoScrollOffset.Y; + + return new Point(xCalc, yCalc); + + } + private int ScrollThumbPosition(int fnBar) { + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.fMask = NativeMethods.SIF_TRACKPOS; + SafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), fnBar, si); + return si.nTrackPos; + } + + + /// + /// + /// + /// Occurs when the scroll box has been moved by either a mouse or keyboard action. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.ScrollBarOnScrollDescr)] + public event ScrollEventHandler Scroll { + add { + Events.AddHandler(EVENT_SCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_SCROLL, value); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnScroll(ScrollEventArgs se) { + ScrollEventHandler handler = (ScrollEventHandler)Events[EVENT_SCROLL]; + if (handler != null) handler(this,se); + } + + private void ResetAutoScrollMargin() { + AutoScrollMargin = Size.Empty; + } + + private void ResetAutoScrollMinSize() { + AutoScrollMinSize = Size.Empty; + } + + private void ResetScrollProperties(ScrollProperties scrollProperties) { + // Set only these two values as when the ScrollBars are not visible ... + // there is no meaning of the "value" property. + scrollProperties.visible = false; + scrollProperties.value = 0; + } + + /// + /// + /// + /// Sets the size + /// of the auto-scroll margins. + /// + /// + public void SetAutoScrollMargin(int x, int y) { + // Make sure we're not setting the margins to negative numbers + if (x < 0) { + x = 0; + } + if (y < 0) { + y = 0; + } + + if (x != requestedScrollMargin.Width + || y != requestedScrollMargin.Height) { + + requestedScrollMargin = new Size(x, y); + if (AutoScroll) { + PerformLayout(); + } + } + } + + + /// + /// Actually displays or hides the horiz and vert autoscrollbars. This will + /// also adjust the values of formState to reflect the new state + /// + private bool SetVisibleScrollbars(bool horiz, bool vert) { + bool needLayout = false; + + if (!horiz && HScroll + || horiz && !HScroll + || !vert && VScroll + || vert && !VScroll) { + + needLayout = true; + } + + // If we are about to show the horizontal scrollbar, then + // set this flag, so that we can set the right initial value + // based on whether we are right to left. + if (horiz && !HScroll && (RightToLeft == RightToLeft.Yes)) { + resetRTLHScrollValue = true; + } + + + if (needLayout) { + int x = displayRect.X; + int y = displayRect.Y; + if (!horiz) { + x = 0; + } + if (!vert) { + y = 0; + } + SetDisplayRectLocation(x, y); + SetScrollState(ScrollStateUserHasScrolled, false); + HScroll = horiz; + VScroll = vert; + //Update the visible member of ScrollBars.... + if (horiz) + { + HorizontalScroll.visible = true; + } + else + { + ResetScrollProperties(HorizontalScroll); + } + if (vert) + { + VerticalScroll.visible = true; + } + else + { + ResetScrollProperties(VerticalScroll); + } + UpdateStyles(); + } + return needLayout; + } + + /// + /// Sets the width and height of the virtual client area used in + /// autoscrolling. This will also adjust the x and y location of the + /// virtual client area if the new size forces it. + /// + /// + private bool SetDisplayRectangleSize(int width, int height) { + bool needLayout = false; + + if (displayRect.Width != width + || displayRect.Height != height) { + + displayRect.Width = width; + displayRect.Height = height; + needLayout = true; + } + + int minX = ClientRectangle.Width - width; + int minY = ClientRectangle.Height - height; + + if (minX > 0) minX = 0; + if (minY > 0) minY = 0; + + int x = displayRect.X; + int y = displayRect.Y; + + if (!HScroll) { + x = 0; + } + if (!VScroll) { + y = 0; + } + + if (x < minX) { + x = minX; + } + if (y < minY) { + y = minY; + } + SetDisplayRectLocation(x, y); + + return needLayout; + } + + /// + /// + /// + /// Sets a given scroll state bit. + /// + /// + protected void SetScrollState(int bit, bool value) { + if (value) { + scrollState |= bit; + } + else { + scrollState &= (~bit); + } + } + + /// + /// + /// Indicates whether the + /// property should be persisted. + /// + /// + private bool ShouldSerializeAutoScrollPosition() { + if (AutoScroll) { + Point pt = AutoScrollPosition; + if (pt.X != 0 || pt.Y != 0) { + return true; + } + } + return false; + } + + /// + /// + /// Indicates whether the property should be persisted. + /// + /// + private bool ShouldSerializeAutoScrollMargin() { + return !AutoScrollMargin.Equals(new Size(0,0)); + } + + /// + /// + /// Indicates whether the + /// property should be persisted. + /// + /// + private bool ShouldSerializeAutoScrollMinSize() { + return !AutoScrollMinSize.Equals(new Size(0,0)); + } + + + /// + /// Updates the value of the autoscroll scrollbars based on the current form + /// state. This is a one-way sync, updating the scrollbars only. + /// + /// + private void SyncScrollbars(bool autoScroll) { + Rectangle displayRect = this.displayRect; + + if (autoScroll) { + if (!IsHandleCreated) { + return; + } + + if (HScroll) { + if (!HorizontalScroll.maximumSetExternally) { + HorizontalScroll.maximum = displayRect.Width-1; + } + if (!HorizontalScroll.largeChangeSetExternally) { + HorizontalScroll.largeChange = ClientRectangle.Width; + } + if (!HorizontalScroll.smallChangeSetExternally) { + HorizontalScroll.smallChange = 5; + } + if (resetRTLHScrollValue && !IsMirrored) { + resetRTLHScrollValue = false; + BeginInvoke(new EventHandler(this.OnSetScrollPosition)); + } + else if(-displayRect.X >= HorizontalScroll.minimum && -displayRect.X < HorizontalScroll.maximum) { + HorizontalScroll.value = -displayRect.X; + } + HorizontalScroll.UpdateScrollInfo (); + } + if (VScroll) { + if (!VerticalScroll.maximumSetExternally) { + VerticalScroll.maximum = displayRect.Height-1; + } + if (!VerticalScroll.largeChangeSetExternally) { + VerticalScroll.largeChange = ClientRectangle.Height; + } + if (!VerticalScroll.smallChangeSetExternally) { + VerticalScroll.smallChange = 5; + } + if (-displayRect.Y >= VerticalScroll.minimum && -displayRect.Y < VerticalScroll.maximum) { + VerticalScroll.value = -displayRect.Y; + } + VerticalScroll.UpdateScrollInfo (); + } + } + else { + if (this.HorizontalScroll.Visible) { + HorizontalScroll.Value = -displayRect.X; + } + else { + ResetScrollProperties(HorizontalScroll); + } + if (this.VerticalScroll.Visible) { + VerticalScroll.Value = -displayRect.Y; + } + else { + ResetScrollProperties(VerticalScroll); + } + } + } + + private void OnSetScrollPosition(object sender, EventArgs e) { + if (!IsMirrored) { + SendMessage(NativeMethods.WM_HSCROLL, + NativeMethods.Util.MAKELPARAM((RightToLeft == RightToLeft.Yes) ? NativeMethods.SB_RIGHT : NativeMethods.SB_LEFT,0), 0); + } + } + + + /// + /// Queries the system to determine the users preference for full drag + /// of windows. + /// + private void UpdateFullDrag() { + SetScrollState(ScrollStateFullDrag, SystemInformation.DragFullWindows); + } + + /// + /// WM_VSCROLL handler + /// + /// + private void WmVScroll(ref Message m) { + + // The lparam is handle of the sending scrollbar, or NULL when + // the scrollbar sending the message is the "form" scrollbar... + // + if (m.LParam != IntPtr.Zero) { + base.WndProc(ref m); + return; + } + + Rectangle client = ClientRectangle; + bool thumbTrack = NativeMethods.Util.LOWORD(m.WParam) != NativeMethods.SB_THUMBTRACK; + int pos = -displayRect.Y; + int oldValue = pos; + + int maxPos = -(client.Height - displayRect.Height); + if (!AutoScroll) { + maxPos = this.VerticalScroll.Maximum; + } + + switch (NativeMethods.Util.LOWORD(m.WParam)) { + case NativeMethods.SB_THUMBPOSITION: + case NativeMethods.SB_THUMBTRACK: + pos = ScrollThumbPosition(NativeMethods.SB_VERT); + break; + case NativeMethods.SB_LINEUP: + if (pos > 0) { + pos-=VerticalScroll.SmallChange; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_LINEDOWN: + if (pos < maxPos-VerticalScroll.SmallChange) { + pos+=VerticalScroll.SmallChange; + } + else { + pos = maxPos; + } + break; + case NativeMethods.SB_PAGEUP: + if (pos > VerticalScroll.LargeChange) { + pos-=VerticalScroll.LargeChange; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_PAGEDOWN: + if (pos < maxPos-VerticalScroll.LargeChange) { + pos+=VerticalScroll.LargeChange; + } + else { + pos = maxPos; + } + break; + case NativeMethods.SB_TOP: + pos = 0; + break; + case NativeMethods.SB_BOTTOM: + pos = maxPos; + break; + } + // VsW: 178279: This bugs reproes on all those machine which have SystemInformation.DragFullWindows set to false + // "thumbTrack" was incorrectly used... the usage should be identical to WnHScroll which follows. + if (GetScrollState(ScrollStateFullDrag) || thumbTrack ) { + SetScrollState(ScrollStateUserHasScrolled, true); + SetDisplayRectLocation(displayRect.X, -pos); + SyncScrollbars(AutoScroll); + } + WmOnScroll(ref m, oldValue, pos, ScrollOrientation.VerticalScroll); + } + + /// + /// WM_HSCROLL handler + /// + /// + private void WmHScroll(ref Message m) { + + // The lparam is handle of the sending scrollbar, or NULL when + // the scrollbar sending the message is the "form" scrollbar... + // + if (m.LParam != IntPtr.Zero) { + base.WndProc(ref m); + return; + } + + Rectangle client = ClientRectangle; + + int pos = -displayRect.X; + int oldValue = pos; + int maxPos = -(client.Width - displayRect.Width); + if (!AutoScroll) { + maxPos = this.HorizontalScroll.Maximum; + } + + switch (NativeMethods.Util.LOWORD(m.WParam)) { + case NativeMethods.SB_THUMBPOSITION: + case NativeMethods.SB_THUMBTRACK: + pos = ScrollThumbPosition(NativeMethods.SB_HORZ); + break; + case NativeMethods.SB_LINEUP: + if (pos > HorizontalScroll.SmallChange) { + pos-=HorizontalScroll.SmallChange; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_LINEDOWN: + if (pos < maxPos-HorizontalScroll.SmallChange) { + pos+=HorizontalScroll.SmallChange; + } + else { + pos = maxPos; + } + break; + case NativeMethods.SB_PAGEUP: + if (pos > HorizontalScroll.LargeChange) { + pos-=HorizontalScroll.LargeChange; + } + else { + pos = 0; + } + break; + case NativeMethods.SB_PAGEDOWN: + if (pos < maxPos-HorizontalScroll.LargeChange) { + pos+=HorizontalScroll.LargeChange; + } + else { + pos = maxPos; + } + break; + case NativeMethods.SB_LEFT: + pos = 0; + break; + case NativeMethods.SB_RIGHT: + pos = maxPos; + break; + } + if (GetScrollState(ScrollStateFullDrag) || NativeMethods.Util.LOWORD(m.WParam) != NativeMethods.SB_THUMBTRACK) { + SetScrollState(ScrollStateUserHasScrolled, true); + SetDisplayRectLocation(-pos, displayRect.Y); + SyncScrollbars(AutoScroll); + } + WmOnScroll(ref m, oldValue, pos, ScrollOrientation.HorizontalScroll); + } + + /// + /// This function gets called which populates the eventArgs and fires the OnScroll( ) event passing + /// the appropriate scroll event and scroll bar. + /// + /// + private void WmOnScroll(ref Message m, int oldValue, int value, ScrollOrientation scrollOrientation) { + ScrollEventType type = (ScrollEventType)NativeMethods.Util.LOWORD(m.WParam); + if (type != ScrollEventType.EndScroll) { + ScrollEventArgs se = new ScrollEventArgs(type, oldValue, value, scrollOrientation); + OnScroll(se); + } + } + + private void WmSettingChange(ref Message m) { + base.WndProc(ref m); + UpdateFullDrag(); + } + + /// + /// + /// The button's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the button continues to function properly. + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_VSCROLL: + WmVScroll(ref m); + break; + case NativeMethods.WM_HSCROLL: + WmHScroll(ref m); + break; + case NativeMethods.WM_SETTINGCHANGE: + WmSettingChange(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + /// + /// Determines the border padding for + /// docked controls. + /// + [TypeConverterAttribute(typeof(DockPaddingEdgesConverter))] + public class DockPaddingEdges : ICloneable { + private ScrollableControl owner; + private int left; + private int right; + private int top; + private int bottom; + + /// + /// Creates a new DockPaddingEdges. The specified owner will + /// be notified when the values are changed. + /// + internal DockPaddingEdges(ScrollableControl owner) { + this.owner = owner; + } + + internal DockPaddingEdges(int left, int right, int top, int bottom) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + } + + /// + /// + /// Gets + /// or + /// sets the padding width for all edges of a docked control. + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PaddingAllDescr) + ] + public int All { + get { + if (owner == null) { + if (left == right && top == bottom && left == top) { + return left; + } + else { + return 0; + } + } + else { + // The Padding struct uses -1 to indicate that LRTB are in disagreement. + // For backwards compatibility, we need to remap -1 to 0, but we need + // to be careful because it could be that they explicitly set All to -1. + if (owner.Padding.All == -1 + && + (owner.Padding.Left != -1 + || owner.Padding.Top != -1 + || owner.Padding.Right != -1 + || owner.Padding.Bottom != -1)) { + return 0; + } + return owner.Padding.All; + } + } + set { + if (owner == null) { + left = value; + top = value; + right = value; + bottom = value; + } + else { + owner.Padding = new Padding(value); + } + } + } + + /// + /// + /// Gets + /// or + /// sets the padding width for the bottom edge of a docked control. + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PaddingBottomDescr) + ] + public int Bottom { + get { + if (owner == null) { + return bottom; + } + else { + return owner.Padding.Bottom; + } + } + set { + if (owner == null) { + bottom = value; + } + else { + Padding padding = owner.Padding; + padding.Bottom = value; + owner.Padding = padding; + } + } + } + + /// + /// + /// Gets + /// or sets the padding width for the left edge of a docked control. + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PaddingLeftDescr) + ] + public int Left { + get { + if (owner == null) { + return left; + } + else { + return owner.Padding.Left; + } + } + set { + if (owner == null) { + left = value; + } + else { + Padding padding = owner.Padding; + padding.Left = value; + owner.Padding = padding; + } + } + } + + /// + /// + /// Gets + /// or sets the padding width for the right edge of a docked control. + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PaddingRightDescr) + ] + public int Right { + get { + if (owner == null) { + return right; + } + else { + return owner.Padding.Right; + } + } + set { + if (owner == null) { + right = value; + } + else { + Padding padding = owner.Padding; + padding.Right = value; + owner.Padding = padding; + } + } + } + + /// + /// + /// Gets + /// or sets the padding width for the top edge of a docked control. + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.PaddingTopDescr) + ] + public int Top { + get { + if (owner == null) { + return bottom; + } + else { + return owner.Padding.Top; + } + } + set { + if (owner == null) { + top = value; + } + else { + Padding padding = owner.Padding; + padding.Top = value; + owner.Padding = padding; + } + } + } + + /// + /// + public override bool Equals(object other) { + DockPaddingEdges dpeOther = other as DockPaddingEdges; + + if (dpeOther != null) { + return this.owner.Padding.Equals(dpeOther.owner.Padding); + } + return false; + } + + /// + /// + public override int GetHashCode() { + return base.GetHashCode(); + } + + + /// + private void ResetAll() { + All = 0; + } + + /// + private void ResetBottom() { + Bottom = 0; + } + + /// + private void ResetLeft() { + Left = 0; + } + + /// + private void ResetRight() { + Right = 0; + } + + /// + private void ResetTop() { + Top = 0; + } + + internal void Scale(float dx, float dy) { + this.owner.Padding.Scale(dx, dy); + } + + /// + /// + public override string ToString() { + return ""; // used to say "(DockPadding)" but that's useless + } + + /// + /// + object ICloneable.Clone() { + DockPaddingEdges dpe = new DockPaddingEdges(Left, Right, Top, Bottom); + return dpe; + } + } + + + /// + public class DockPaddingEdgesConverter : TypeConverter { + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes); + return props.Sort(new string[] {"All", "Left", "Top", "Right", "Bottom"}); + } + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/SearchDirectionHint.cs b/WindowsForms/Managed/System/WinForms/SearchDirectionHint.cs new file mode 100644 index 000000000..04daa5d60 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SearchDirectionHint.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace System.Windows.Forms { + /// + /// + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum SearchDirectionHint { + /// + /// + /// + /// + Up = NativeMethods.VK_UP, + /// + /// + /// + /// + Down = NativeMethods.VK_DOWN, + /// + /// + /// + /// + Left = NativeMethods.VK_LEFT, + /// + /// + /// + /// + Right = NativeMethods.VK_RIGHT + } +} diff --git a/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventArgs.cs b/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventArgs.cs new file mode 100644 index 000000000..8b700e4d3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventArgs.cs @@ -0,0 +1,87 @@ +using System; +using System.Drawing; + +namespace System.Windows.Forms { + /// + public class SearchForVirtualItemEventArgs : EventArgs { + private bool isTextSearch; + private bool isPrefixSearch; + private bool includeSubItemsInSearch; + private string text; + private Point startingPoint; + private SearchDirectionHint direction; + private int startIndex; + private int index = -1; + + /// + public SearchForVirtualItemEventArgs(bool isTextSearch, bool isPrefixSearch, bool includeSubItemsInSearch, string text, Point startingPoint, SearchDirectionHint direction, int startIndex) { + this.isTextSearch = isTextSearch; + this.isPrefixSearch = isPrefixSearch; + this.includeSubItemsInSearch = includeSubItemsInSearch; + this.text = text; + this.startingPoint = startingPoint; + this.direction = direction; + this.startIndex = startIndex; + } + + /// + public bool IsTextSearch { + get { + return isTextSearch; + } + } + + /// + public bool IncludeSubItemsInSearch { + get { + return includeSubItemsInSearch; + } + } + + /// + public int Index { + get { + return this.index; + } + set + { + this.index = value; + } + } + + /// + public bool IsPrefixSearch { + get { + return isPrefixSearch; + } + } + + /// + public string Text{ + get { + return text; + } + } + + /// + public Point StartingPoint { + get { + return startingPoint; + } + } + + /// + public SearchDirectionHint Direction { + get { + return direction; + } + } + + /// + public int StartIndex { + get { + return startIndex; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventHandler.cs new file mode 100644 index 000000000..39097b1f7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SearchForVirtualItemEventHandler.cs @@ -0,0 +1,15 @@ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + /// + /// + /// + /// Represents the method that will handle the SearchForVirtualItem event of a ListView. + /// + /// + public delegate void SearchForVirtualItemEventHandler(object sender, SearchForVirtualItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/SecurityIDType.cs b/WindowsForms/Managed/System/WinForms/SecurityIDType.cs new file mode 100644 index 000000000..80e098db7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SecurityIDType.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// + public enum SecurityIDType { + /// + /// + /// [To be supplied.] + /// + User = 1, + /// + /// + /// [To be supplied.] + /// + Group = 2, + /// + /// + /// [To be supplied.] + /// + Domain = 3, + /// + /// + /// [To be supplied.] + /// + Alias = 4, + /// + /// + /// [To be supplied.] + /// + WellKnownGroup = 5, + /// + /// + /// [To be supplied.] + /// + DeletedAccount = 6, + /// + /// + /// [To be supplied.] + /// + Invalid = 7, + /// + /// + /// [To be supplied.] + /// + Unknown = 8, + /// + /// + /// [To be supplied.] + /// + Computer = 9, + } +} diff --git a/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEvent.cs b/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEvent.cs new file mode 100644 index 000000000..75d8ec196 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEvent.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + + /// + /// + /// The event class that is created when the selected GridItem in the PropertyGrid is changed by the user. + /// + public class SelectedGridItemChangedEventArgs : EventArgs { + private GridItem oldSelection; + private GridItem newSelection; + + /// + /// + /// Constructs a SelectedGridItemChangedEventArgs object. + /// + public SelectedGridItemChangedEventArgs(GridItem oldSel, GridItem newSel) { + this.oldSelection = oldSel; + this.newSelection = newSel; + } + + + + /// + /// + /// The newly selected GridItem object + /// + public GridItem NewSelection { + get { + return this.newSelection; + } + } + + /// + /// + /// The previously selected GridItem object. This can be null. + /// + public GridItem OldSelection { + get { + return this.oldSelection; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEventHandler.cs new file mode 100644 index 000000000..3aee24ee0 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SelectedGridItemChangedEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + + /// + /// + /// [To be supplied.] + /// + public delegate void SelectedGridItemChangedEventHandler(object sender, SelectedGridItemChangedEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/SelectionMode.cs b/WindowsForms/Managed/System/WinForms/SelectionMode.cs new file mode 100644 index 000000000..b0fa18ee6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SelectionMode.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum SelectionMode { + + /// + /// + /// indicates that no items can be selected. + /// + None = 0, + + /// + /// + /// indicates that only one item at a time can be selected. + /// + One = 1, + + /// + /// + /// indicates that more than one item at a time can be selected. + /// + MultiSimple = 2, + + /// + /// + /// Indicates that more than one item at a time can be selected, and + /// keyboard combinations, such as SHIFT and CTRL can be used to help + /// in selection. + /// + MultiExtended = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/SelectionRange.cs b/WindowsForms/Managed/System/WinForms/SelectionRange.cs new file mode 100644 index 000000000..62b50ba12 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SelectionRange.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + + using System.Diagnostics; + + using System; + using System.Globalization; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using System.IO; + using Microsoft.Win32; + + /// + /// + /// This is a class that represents the date selection range of a MonthCalendar control. + /// + [ + TypeConverterAttribute(typeof(SelectionRangeConverter)) + ] + public sealed class SelectionRange { + /// + /// + /// The lower limit of the selection range. + /// + private DateTime start = DateTime.MinValue.Date; + + /// + /// + /// The upper limit of the selection range. + /// + private DateTime end = DateTime.MaxValue.Date; + + /// + /// + /// Create a new SelectionRange object with the range [null, null]. + /// + public SelectionRange() { + } + + /// + /// + /// Create a new SelectionRange object with the given range. + /// + public SelectionRange(DateTime lower, DateTime upper) { + //NOTE: simcooke: we explicitly DO NOT want to throw an exception here - just silently + // swap them around. This is because the win32 control can return non- + // normalized ranges. + + // We use lower.Date and upper.Date to remove any time component + // + if (lower < upper) { + start = lower.Date; + end = upper.Date; + } + else { + start = upper.Date; + end = lower.Date; + } + } + + /// + /// + /// Create a new SelectionRange object given an existing SelectionRange object. + /// + public SelectionRange(SelectionRange range) { + this.start = range.start; + this.end = range.end; + } + + /// + /// + /// Returns the ending time of this range. + /// + public DateTime End { + get { + return end; + } + set { + end = value.Date; + } + } + + /// + /// + /// Starting time of this range + /// + public DateTime Start { + get { + return start; + } + set { + start = value.Date; + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + return "SelectionRange: Start: " + start.ToString() + ", End: " + end.ToString(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SelectionRangeConverter.cs b/WindowsForms/Managed/System/WinForms/SelectionRangeConverter.cs new file mode 100644 index 000000000..edad6ef84 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SelectionRangeConverter.cs @@ -0,0 +1,191 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// SelectionRangeConverter is a class that can be used to convert + /// SelectionRange objects from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class SelectionRangeConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string) || sourceType == typeof(DateTime)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(DateTime)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + if (value is string) { + string text = ((string)value).Trim(); + if (text.Length == 0) { + return new SelectionRange(DateTime.Now.Date, DateTime.Now.Date); + } + + // Separate the string into the two dates, and parse each one + // + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + char separator = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(new char[] {separator}); + + if (tokens.Length == 2) { + TypeConverter dateTimeConverter = TypeDescriptor.GetConverter(typeof(DateTime)); + DateTime start = (DateTime)dateTimeConverter.ConvertFromString(context, culture, tokens[0]); + DateTime end = (DateTime)dateTimeConverter.ConvertFromString(context, culture, tokens[1]); + return new SelectionRange(start, end); + } + else { + throw new ArgumentException(SR.GetString(SR.TextParseFailedFormat, + text, + "Start" + separator + " End")); + } + } + else if (value is DateTime) { + DateTime dt = (DateTime)value; + return new SelectionRange(dt, dt); + } + + return base.ConvertFrom(context, culture, value); + } + + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + SelectionRange range = value as SelectionRange; + if (range != null) { + if (destinationType == typeof(string)) { + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + string sep = culture.TextInfo.ListSeparator + " "; + PropertyDescriptorCollection props = GetProperties(value); + string[] args = new string[props.Count]; + + for (int i = 0; i < props.Count; i++) { + object propValue = props[i].GetValue(value); + args[i] = TypeDescriptor.GetConverter(propValue).ConvertToString(context, culture, propValue); + } + + return string.Join(sep, args); + } + if (destinationType == typeof(DateTime)) { + return range.Start; + } + if (destinationType == typeof(InstanceDescriptor)) { + ConstructorInfo ctor = typeof(SelectionRange).GetConstructor(new Type[] { + typeof(DateTime), typeof(DateTime)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {range.Start, range.End}); + } + } + } + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + try + { + return new SelectionRange((DateTime)propertyValues["Start"], + (DateTime)propertyValues["End"]); + } + catch (InvalidCastException invalidCast) + { + throw new ArgumentException(SR.GetString(SR.PropertyValueInvalidEntry), invalidCast); + } + catch (NullReferenceException nullRef) + { + throw new ArgumentException(SR.GetString(SR.PropertyValueInvalidEntry), nullRef); + } + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(SelectionRange), attributes); + return props.Sort(new string[] {"Start", "End"}); + } + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SendKeys.cs b/WindowsForms/Managed/System/WinForms/SendKeys.cs new file mode 100644 index 000000000..37e1b3a89 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SendKeys.cs @@ -0,0 +1,1113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Security; + + using System.Diagnostics; + + using System; + + using System.Drawing; + using System.Collections; + using System.ComponentModel; + using System.Globalization; + + /// + /// + /// Provides methods for sending keystrokes to an application. + /// + public class SendKeys { + private const int HAVESHIFT = 0; + private const int HAVECTRL = 1; + private const int HAVEALT = 2; + + // I'm unsure what significance the value 10 has, but it seems to make sense + // to make this a constant rather than have 10 sprinkled throughout the code. + // It appears to be a sentinel value of some sort - indicating an unknown + // grouping level. + // + private const int UNKNOWN_GROUPING = 10; + + private static KeywordVk [] keywords = new KeywordVk[] { + new KeywordVk("ENTER", (int)Keys.Return), + new KeywordVk("TAB", (int)Keys.Tab), + new KeywordVk("ESC", (int)Keys.Escape), + new KeywordVk("ESCAPE", (int)Keys.Escape), + new KeywordVk("HOME", (int)Keys.Home), + new KeywordVk("END", (int)Keys.End), + new KeywordVk("LEFT", (int)Keys.Left), + new KeywordVk("RIGHT", (int)Keys.Right), + new KeywordVk("UP", (int)Keys.Up), + new KeywordVk("DOWN", (int)Keys.Down), + new KeywordVk("PGUP", (int)Keys.Prior), + new KeywordVk("PGDN", (int)Keys.Next), + new KeywordVk("NUMLOCK", (int)Keys.NumLock), + new KeywordVk("SCROLLLOCK", (int)Keys.Scroll), + new KeywordVk("PRTSC", (int)Keys.PrintScreen), + new KeywordVk("BREAK", (int)Keys.Cancel), + new KeywordVk("BACKSPACE", (int)Keys.Back), + new KeywordVk("BKSP", (int)Keys.Back), + new KeywordVk("BS", (int)Keys.Back), + new KeywordVk("CLEAR", (int)Keys.Clear), + new KeywordVk("CAPSLOCK", (int)Keys.Capital), + new KeywordVk("INS", (int)Keys.Insert), + new KeywordVk("INSERT", (int)Keys.Insert), + new KeywordVk("DEL", (int)Keys.Delete), + new KeywordVk("DELETE", (int)Keys.Delete), + new KeywordVk("HELP", (int)Keys.Help), + new KeywordVk("F1", (int)Keys.F1), + new KeywordVk("F2", (int)Keys.F2), + new KeywordVk("F3", (int)Keys.F3), + new KeywordVk("F4", (int)Keys.F4), + new KeywordVk("F5", (int)Keys.F5), + new KeywordVk("F6", (int)Keys.F6), + new KeywordVk("F7", (int)Keys.F7), + new KeywordVk("F8", (int)Keys.F8), + new KeywordVk("F9", (int)Keys.F9), + new KeywordVk("F10", (int)Keys.F10), + new KeywordVk("F11", (int)Keys.F11), + new KeywordVk("F12", (int)Keys.F12), + new KeywordVk("F13", (int)Keys.F13), + new KeywordVk("F14", (int)Keys.F14), + new KeywordVk("F15", (int)Keys.F15), + new KeywordVk("F16", (int)Keys.F16), + new KeywordVk("MULTIPLY", (int)Keys.Multiply), + new KeywordVk("ADD", (int)Keys.Add), + new KeywordVk("SUBTRACT", (int)Keys.Subtract), + new KeywordVk("DIVIDE", (int)Keys.Divide), + new KeywordVk("+", (int)Keys.Add), + new KeywordVk("%", (int)(Keys.D5 | Keys.Shift)), + new KeywordVk("^", (int)(Keys.D6 | Keys.Shift)), + }; + + private const int SHIFTKEYSCAN = 0x0100; + private const int CTRLKEYSCAN = 0x0200; + private const int ALTKEYSCAN = 0x0400; + + /// + /// + /// should we stop using the hook? + /// + private static bool stopHook; + + /// + /// + /// HHOOK + /// + private static IntPtr hhook; + + private static NativeMethods.HookProc hook; + + /// + /// + /// vector of events that we have yet to post to the journaling hook. + /// + private static Queue events; + + private static bool fStartNewChar; + + private static SKWindow messageWindow; + + private enum SendMethodTypes + { + Default = 1, + JournalHook = 2, + SendInput = 3 + } + + private static SendMethodTypes? sendMethod = null; + + private static bool? hookSupported = null; + + // Used only for SendInput because SendInput alters the global state of the keyboard + private static bool capslockChanged; + private static bool numlockChanged; + private static bool scrollLockChanged; + private static bool kanaChanged; + + static SendKeys() { + Application.ThreadExit += new EventHandler(OnThreadExit); + messageWindow = new SKWindow(); + messageWindow.CreateControl(); + } + + /// + /// + /// private constructor to prevent people from creating one of these. they + /// should use public static methods + /// + private SendKeys() { + } + + /// + /// + /// adds an event to our list of events for the hook + /// + private static void AddEvent(SKEvent skevent) { + + if (events == null) { + events = new Queue(); + } + events.Enqueue(skevent); + } + + // Helper function for ParseKeys for doing simple, self-describing characters. + private static bool AddSimpleKey(char character, int repeat, IntPtr hwnd, int[] haveKeys, bool fStartNewChar, int cGrp) { + int vk = UnsafeNativeMethods.VkKeyScan(character); + + if (vk != -1) { + if (haveKeys[HAVESHIFT] == 0 && (vk & SHIFTKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVECTRL] == 0 && (vk & CTRLKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVEALT] == 0 && (vk & ALTKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + } + + AddMsgsForVK(vk & 0xff, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + else { + int oemVal = SafeNativeMethods.OemKeyScan((short)(0xFF & (int)character)); + for (int i = 0; i < repeat; i++) { + AddEvent(new SKEvent(NativeMethods.WM_CHAR, character, (int)(oemVal & 0xFFFF), hwnd)); + } + } + + if (cGrp != 0) fStartNewChar = true; + return fStartNewChar; + } + + /// + /// + /// given the vk, add the appropriate messages for it + /// + private static void AddMsgsForVK(int vk, int repeat, bool altnoctrldown, IntPtr hwnd) { + for (int i = 0; i < repeat; i++) { + AddEvent(new SKEvent(altnoctrldown ? NativeMethods.WM_SYSKEYDOWN : NativeMethods.WM_KEYDOWN, vk, fStartNewChar, hwnd)); + // fStartNewChar = false; + AddEvent(new SKEvent(altnoctrldown ? NativeMethods.WM_SYSKEYUP : NativeMethods.WM_KEYUP, vk, fStartNewChar, hwnd)); + } + } + + /// + /// + /// called whenever there is a closing parenthesis, or the end of a + /// character. This generates events for the end of a modifier. + /// + private static void CancelMods(int [] haveKeys, int level, IntPtr hwnd) { + if (haveKeys[HAVESHIFT] == level) { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ShiftKey, false, hwnd)); + haveKeys[HAVESHIFT] = 0; + } + if (haveKeys[HAVECTRL] == level) { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ControlKey, false, hwnd)); + haveKeys[HAVECTRL] = 0; + } + if (haveKeys[HAVEALT] == level) { + AddEvent(new SKEvent(NativeMethods.WM_SYSKEYUP, (int)Keys.Menu, false, hwnd)); + haveKeys[HAVEALT] = 0; + } + } + + /// + /// + /// install the hook. quite easy + /// + private static void InstallHook() { + if (hhook == IntPtr.Zero) { + hook = new NativeMethods.HookProc(new SendKeysHookProc().Callback); + stopHook = false; + hhook = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_JOURNALPLAYBACK, + hook, + new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), + 0); + if (hhook == IntPtr.Zero) + throw new SecurityException(SR.GetString(SR.SendKeysHookFailed)); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void TestHook() + { + hookSupported = false; + try + { + + NativeMethods.HookProc hookProc = new NativeMethods.HookProc(EmptyHookCallback); + IntPtr hookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_JOURNALPLAYBACK, + hookProc, + new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), + 0); + + hookSupported = (hookHandle != IntPtr.Zero); + + if (hookHandle != IntPtr.Zero) + { + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(null, hookHandle)); + } + } + catch {} // ignore any exceptions to keep existing SendKeys behavior + } + + private static IntPtr EmptyHookCallback(int code, IntPtr wparam, IntPtr lparam) + { + return IntPtr.Zero; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void LoadSendMethodFromConfig() + { + if (!sendMethod.HasValue) + { + sendMethod = SendMethodTypes.Default; + + try + { + // read SendKeys value from config file, not case sensitive + string value = System.Configuration.ConfigurationManager.AppSettings.Get("SendKeys"); + + if (string.IsNullOrEmpty(value)) + return; + + if (value.Equals("JournalHook", StringComparison.OrdinalIgnoreCase)) + sendMethod = SendMethodTypes.JournalHook; + else if (value.Equals("SendInput", StringComparison.OrdinalIgnoreCase)) + sendMethod = SendMethodTypes.SendInput; + } + catch {} // ignore any exceptions to keep existing SendKeys behavior + } + } + + /// + /// + /// tells us to shut down the server, perhaps if we're shutting down and the + /// hook is still running + /// + private static void JournalCancel() { + if (hhook != IntPtr.Zero) { + stopHook = false; + if (events != null) { + events.Clear(); + } + hhook = IntPtr.Zero; + } + } + + private static byte[] GetKeyboardState() { + byte [] keystate = new byte[256]; + UnsafeNativeMethods.GetKeyboardState(keystate); + return keystate; + } + + private static void SetKeyboardState(byte[] keystate) { + UnsafeNativeMethods.SetKeyboardState(keystate); + } + + /// + /// + /// before we do a sendkeys, we want to clear the state + /// of a couple of keys [capslock, numlock, scrolllock] so they don't + /// interfere. + /// + private static void ClearKeyboardState() { + + byte [] keystate = GetKeyboardState(); + + keystate[(int)Keys.Capital] = 0; + keystate[(int)Keys.NumLock] = 0; + keystate[(int)Keys.Scroll] = 0; + + SetKeyboardState(keystate); + } + + /// + /// + /// given the string, match the keyword to a VK. return -1 if it don't match + /// nuthin' + /// + private static int MatchKeyword(string keyword) { + for (int i = 0; i < keywords.Length; i++) + if (String.Equals(keywords[i].keyword, keyword, StringComparison.OrdinalIgnoreCase)) + return keywords[i].vk; + + return -1; + } + + /// + /// + /// This event is raised from Application when each window thread + /// termiantes. It gives us a chance to uninstall our journal + /// hook if we had one installed. + /// + private static void OnThreadExit(object sender, EventArgs e) { + try { + UninstallJournalingHook(); + } + catch { + } + } + + /// + /// + /// parse the string the user has given us, and generate the appropriate + /// events for the journaling hook + /// + private static void ParseKeys(string keys, IntPtr hwnd) { + + int i = 0; + + // these four variables are used for grouping + int [] haveKeys = new int[] { 0, 0, 0}; // shift, ctrl, alt + int cGrp = 0; + + // fStartNewChar indicates that the next msg will be the first + // of a char or char group. This is needed for IntraApp Nesting + // of SendKeys. + // + fStartNewChar = true; + + // okay, start whipping through the characters one at a time. + // + int keysLen = keys.Length; + while (i < keysLen) { + int repeat = 1; + char ch = keys[i]; + int vk = 0; + + switch (ch) { + case '}': + // if these appear at this point they are out of + // context, so return an error. KeyStart processes + // ochKeys up to the appropriate KeyEnd. + // + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + case '{': + int j = i + 1; + + // There's a unique class of strings of the form "{} n}" where + // n is an integer - in this case we want to send n copies of the '}' character. + // Here we test for the possibility of this class of problems, and skip the + // first '}' in the string if necessary. + // + if (j + 1 < keysLen && keys[j] == '}') { + // Scan for the final '}' character + int final = j + 1; + while (final < keysLen && keys[final] != '}') { + final++; + } + if (final < keysLen) { + // Found the special case, so skip the first '}' in the string. + // The remainder of the code will attempt to find the repeat count. + j++; + } + } + + // okay, we're in a {...} situation. look for the keyword + // + while (j < keysLen && keys[j] != '}' + && !Char.IsWhiteSpace(keys[j])) { + j++; + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + + // okay, have our KEYWORD. verify it's one we know about + // + string keyName = keys.Substring(i + 1, j - (i + 1)); + + // see if we have a space, which would mean a repeat count. + // + if (Char.IsWhiteSpace(keys[j])) { + int digit; + while (j < keysLen && Char.IsWhiteSpace(keys[j])) { + j++; + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + + if (Char.IsDigit(keys[j])) { + digit = j; + while (j < keysLen && Char.IsDigit(keys[j])) { + j++; + } + repeat = Int32.Parse(keys.Substring(digit, j - digit), CultureInfo.InvariantCulture); + } + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + if (keys[j] != '}') { + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysRepeat)); + } + + vk = MatchKeyword(keyName); + if (vk != -1) { + // Unlike AddSimpleKey, the bit mask uses Keys, rather than scan keys + if (haveKeys[HAVESHIFT] == 0 && (vk & (int)Keys.Shift) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVECTRL] == 0 && (vk & (int)Keys.Control) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVEALT] == 0 && (vk & (int)Keys.Alt) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + } + AddMsgsForVK(vk, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + else if (keyName.Length == 1) { + fStartNewChar = AddSimpleKey(keyName[0], repeat, hwnd, haveKeys, fStartNewChar, cGrp); + } + else { + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysKeyword, keys.Substring(i + 1, j - (i + 1)))); + } + + // don't forget to position ourselves at the end of the {...} group + i = j; + break; + + case '+': + if (haveKeys[HAVESHIFT] != 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + break; + + case '^': + if (haveKeys[HAVECTRL]!= 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + break; + + case '%': + if (haveKeys[HAVEALT] != 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent((haveKeys[HAVECTRL] != 0) ? NativeMethods.WM_KEYDOWN : NativeMethods.WM_SYSKEYDOWN, + (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + break; + + case '(': + // convert all immediate mode states to group mode + // Allows multiple keys with the same shift, etc. state. + // Nests three deep. + // + cGrp++; + if (cGrp > 3) throw new ArgumentException(SR.GetString(SR.SendKeysNestingError)); + + if (haveKeys[HAVESHIFT] == UNKNOWN_GROUPING) haveKeys[HAVESHIFT] = cGrp; + if (haveKeys[HAVECTRL] == UNKNOWN_GROUPING) haveKeys[HAVECTRL] = cGrp; + if (haveKeys[HAVEALT] == UNKNOWN_GROUPING) haveKeys[HAVEALT] = cGrp; + break; + + case ')': + if (cGrp < 1) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + CancelMods(haveKeys, cGrp, hwnd); + cGrp--; + if (cGrp == 0) fStartNewChar = true; + break; + + case '~': + vk = (int)Keys.Return; + AddMsgsForVK(vk, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + break; + + default: + fStartNewChar = AddSimpleKey(keys[i], repeat, hwnd, haveKeys, fStartNewChar, cGrp); + break; + } + + + // next element in the string + // + i++; + } + + if (cGrp != 0) + throw new ArgumentException(SR.GetString(SR.SendKeysGroupDelimError)); + + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + + // Uses User32 SendInput to send keystrokes + private static void SendInput(byte[] oldKeyboardState, Queue previousEvents) + { + // Should be a No-Opt most of the time + AddCancelModifiersForPreviousEvents(previousEvents); + + // SKEvents are sent as sent as 1 or 2 inputs + // currentInput[0] represents the SKEvent + // currentInput[1] is a KeyUp to prevent all identical WM_CHARs to be sent as one message + NativeMethods.INPUT[] currentInput = new NativeMethods.INPUT[2]; + + // all events are Keyboard events + currentInput[0].type = NativeMethods.INPUT_KEYBOARD; + currentInput[1].type = NativeMethods.INPUT_KEYBOARD; + + // set KeyUp values for currentInput[1] + currentInput[1].inputUnion.ki.wVk = (short) 0; + currentInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE | NativeMethods.KEYEVENTF_KEYUP; + + // initialize unused members + currentInput[0].inputUnion.ki.dwExtraInfo = IntPtr.Zero; + currentInput[0].inputUnion.ki.time = 0; + currentInput[1].inputUnion.ki.dwExtraInfo = IntPtr.Zero; + currentInput[1].inputUnion.ki.time = 0; + + // send each of our SKEvents using SendInput + int INPUTSize = Marshal.SizeOf(typeof(NativeMethods.INPUT)); + + // need these outside the lock below + uint eventsSent=0; + int eventsTotal; + + // A lock here will allow multiple threads to SendInput at the same time. + // This mimics the JournalHook method of using the message loop to mitigate + // threading issues. There is still a theoretical thread issue with adding + // to the events Queue (both JournalHook and SendInput), but we do not want + // to alter the timings of the existing shipped behavior. I did not run into + // problems with 2 threads on a multiproc machine + lock (events.SyncRoot) + { + // block keyboard and mouse input events from reaching applications. + bool blockInputSuccess = UnsafeNativeMethods.BlockInput(true); + + try + { + eventsTotal = events.Count; + ClearGlobalKeys(); + + for (int i = 0; i < eventsTotal; i++) + { + SKEvent skEvent = (SKEvent)events.Dequeue(); + + currentInput[0].inputUnion.ki.dwFlags = 0; + + if (skEvent.wm == NativeMethods.WM_CHAR) + { + // for WM_CHAR, send a KEYEVENTF_UNICODE instead of a Keyboard event + // to support extended ascii characters with no keyboard equivalent. + // send currentInput[1] in this case + currentInput[0].inputUnion.ki.wVk = (short)0; + currentInput[0].inputUnion.ki.wScan = (short)skEvent.paramL; + currentInput[0].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE; + currentInput[1].inputUnion.ki.wScan = (short)skEvent.paramL; + + // call SendInput, increment the eventsSent but subtract 1 for the extra one sent + eventsSent += UnsafeNativeMethods.SendInput(2, currentInput, INPUTSize) - 1; + } + else + { + // just need to send currentInput[0] for skEvent + currentInput[0].inputUnion.ki.wScan = 0; + + // add KeyUp flag if we have a KeyUp + if (skEvent.wm == NativeMethods.WM_KEYUP || skEvent.wm == NativeMethods.WM_SYSKEYUP) + { + currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_KEYUP; + } + + // Sets KEYEVENTF_EXTENDEDKEY flag if necessary + if (IsExtendedKey(skEvent)) + { + currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_EXTENDEDKEY; + } + + currentInput[0].inputUnion.ki.wVk = (short)skEvent.paramL; + + // send only currentInput[0] + eventsSent += UnsafeNativeMethods.SendInput(1, currentInput, INPUTSize); + + CheckGlobalKeys(skEvent); + } + + // We need this slight delay here for Alt-Tab to work on Vista when the Aero theme + // is running. See DevDiv bugs 23355. Although this does not look good, a delay + // here actually more closely resembles the old JournalHook that processes each + // event individually in the hook callback. + System.Threading.Thread.Sleep(1); + } + + // reset the keyboard back to what it was before inputs were sent, SendInupt modifies + // the global lights on the keyboard (caps, scroll..) so we need to call it again to + // undo those changes + ResetKeyboardUsingSendInput(INPUTSize); + } + finally + { + SetKeyboardState(oldKeyboardState); + + // unblock input if it was previously blocked + if (blockInputSuccess) + { + UnsafeNativeMethods.BlockInput(false); + } + } + } + + // check to see if we sent the number of events we're supposed to + if (eventsSent != eventsTotal) + { + // calls Marshal.GetLastWin32Error and sets it in the exception + throw new Win32Exception(); + } + } + + // For SendInput, these previous events that stick around if an Exception was + // thrown could modify the state of the keyboard modifiers (alt, ctrl, shift). + // We must send a KeyUp for those, JournalHook doesn't permanently set the state + // so it's ok + private static void AddCancelModifiersForPreviousEvents(Queue previousEvents) + { + if (previousEvents == null) + { + return; + } + + bool shift = false; + bool ctrl = false; + bool alt = false; + while (previousEvents.Count > 0) + { + SKEvent skEvent = (SKEvent)previousEvents.Dequeue(); + + bool isOn; + if ((skEvent.wm == NativeMethods.WM_KEYUP) || + (skEvent.wm == NativeMethods.WM_SYSKEYUP)) + { + isOn = false; + } + else if ((skEvent.wm == NativeMethods.WM_KEYDOWN) || + (skEvent.wm == NativeMethods.WM_SYSKEYDOWN)) + { + isOn = true; + } + else + { + continue; + } + + if (skEvent.paramL == (int)Keys.ShiftKey) + { + shift = isOn; + } + else if (skEvent.paramL == (int)Keys.ControlKey) + { + ctrl = isOn; + } + else if (skEvent.paramL == (int)Keys.Menu) + { + alt = isOn; + } + } + + if (shift) + { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ShiftKey, false, IntPtr.Zero)); + } + else if (ctrl) + { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ControlKey, false, IntPtr.Zero)); + } + else if (alt) + { + AddEvent(new SKEvent(NativeMethods.WM_SYSKEYUP, (int)Keys.Menu, false, IntPtr.Zero)); + } + } + + private static bool IsExtendedKey(SKEvent skEvent) + { + return (skEvent.paramL == NativeMethods.VK_UP) || + (skEvent.paramL == NativeMethods.VK_DOWN) || + (skEvent.paramL == NativeMethods.VK_LEFT) || + (skEvent.paramL == NativeMethods.VK_RIGHT) || + (skEvent.paramL == NativeMethods.VK_PRIOR) || + (skEvent.paramL == NativeMethods.VK_NEXT) || + (skEvent.paramL == NativeMethods.VK_HOME) || + (skEvent.paramL == NativeMethods.VK_END) || + (skEvent.paramL == NativeMethods.VK_INSERT) || + (skEvent.paramL == NativeMethods.VK_DELETE); + } + + private static void ClearGlobalKeys() + { + capslockChanged = false; + numlockChanged = false; + scrollLockChanged = false; + kanaChanged = false; + } + + private static void CheckGlobalKeys(SKEvent skEvent) + { + if (skEvent.wm == NativeMethods.WM_KEYDOWN) + { + switch (skEvent.paramL) + { + case (int)Keys.CapsLock: + capslockChanged = !capslockChanged; + break; + case (int)Keys.NumLock: + numlockChanged = !numlockChanged; + break; + case (int)Keys.Scroll: + scrollLockChanged = !scrollLockChanged; + break; + case (int)Keys.KanaMode: + kanaChanged = !kanaChanged; + break; + } + } + } + + private static void ResetKeyboardUsingSendInput(int INPUTSize) + { + // if the new state is the same, we don't need to do anything + if (!(capslockChanged || numlockChanged || scrollLockChanged || kanaChanged)) + { + return; + } + + // INPUT struct for resetting the keyboard + NativeMethods.INPUT[] keyboardInput = new NativeMethods.INPUT[2]; + + keyboardInput[0].type = NativeMethods.INPUT_KEYBOARD; + keyboardInput[0].inputUnion.ki.dwFlags = 0; + + keyboardInput[1].type = NativeMethods.INPUT_KEYBOARD; + keyboardInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_KEYUP; + + // SendInputs to turn on or off these keys. Inputs are pairs because KeyUp is sent for each one + if (capslockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (numlockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (scrollLockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (kanaChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_KANA; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_KANA; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + } + + /// + /// + /// Sends keystrokes to the active application. + /// + public static void Send(string keys) { + Send(keys, null, false); + } + + /// + /// Sends keystrokes to the active application. + /// + + + // WARNING: this method will never work if control != null, because while + // Windows journaling *looks* like it can be directed to a specific HWND, + // it can't. + // + + // No one is calling this method so it is safe to comment it out + + //private static void Send(string keys, /*bogus*/ Control control) { + // Send(keys, control, false); + //} + + + private static void Send(string keys, Control control, bool wait) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "UnmanagedCode Demanded"); + IntSecurity.UnmanagedCode.Demand(); + + if (keys == null || keys.Length == 0) return; + + // If we're not going to wait, make sure there is a pump. + // + if (!wait && !Application.MessageLoop) { + throw new InvalidOperationException(SR.GetString(SR.SendKeysNoMessageLoop)); + } + + // For SendInput only, see AddCancelModifiersForPreviousEvents for details + Queue previousEvents = null; + if ((events != null) && (events.Count != 0)) + { + previousEvents = (Queue) events.Clone(); + } + + // generate the list of events that we're going to fire off with the hook + // + ParseKeys(keys, (control != null) ? control.Handle : IntPtr.Zero); + + + // if there weren't any events posted as a result, we're done! + // + if (events == null) return; + + LoadSendMethodFromConfig(); + + byte[] oldstate = GetKeyboardState(); + + if (sendMethod.Value != SendMethodTypes.SendInput) + { + if (!hookSupported.HasValue && + sendMethod.Value == SendMethodTypes.Default) + { + // We don't know if the JournalHook will work, test it out + // so we know whether or not to call ClearKeyboardState. ClearKeyboardState + // does nothing for JournalHooks but inversely affects SendInput + TestHook(); + } + if (sendMethod.Value == SendMethodTypes.JournalHook || + hookSupported.Value) + { + ClearKeyboardState(); + InstallHook(); + SetKeyboardState(oldstate); + } + } + + if (sendMethod.Value == SendMethodTypes.SendInput || + (sendMethod.Value == SendMethodTypes.Default && !hookSupported.Value)) + { + // either SendInput is configured or JournalHooks failed by default, call SendInput + SendInput(oldstate, previousEvents); + } + + if (wait) + { + Flush(); + } + } + + /// + /// + /// Sends the given keys to the active application, and then waits for + /// the messages to be processed. + /// + public static void SendWait(string keys) { + SendWait(keys, null); + } + + /// + /// + /// Sends the given keys to the active application, and then waits for + /// the messages to be processed. + /// + + + // WARNING: this method will never work if control != null, because while + // Windows journaling *looks* like it can be directed to a specific HWND, + // it can't. + // + private static void SendWait(string keys, Control control) { + Send(keys, control, true); + } + + /// + /// + /// Processes all the Windows messages currently in the message queue. + /// + public static void Flush() { + Application.DoEvents(); + while (events != null && events.Count > 0) { + Application.DoEvents(); + } + } + + /// + /// + /// cleans up and uninstalls the hook + /// + private static void UninstallJournalingHook() { + if (hhook != IntPtr.Zero) { + stopHook = false; + if (events != null) { + events.Clear(); + } + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(null, hhook)); + hhook = IntPtr.Zero; + } + } + + /// + /// SendKeys creates a window to monitor WM_CANCELJOURNAL messages. + /// + private class SKWindow : Control { + + public SKWindow() { + SetState(STATE_TOPLEVEL, true); + SetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED, false); + SetBounds(-1, -1, 0, 0); + Visible = false; + } + + protected override void WndProc(ref Message m) { + if (m.Msg == NativeMethods.WM_CANCELJOURNAL) { + try { + SendKeys.JournalCancel(); + } + catch { + } + } + } + } + + /// + /// + /// helps us hold information about the various events we're going to journal + /// + private class SKEvent { + internal int wm; + internal int paramL; + internal int paramH; + internal IntPtr hwnd; + + public SKEvent(int a, int b, bool c, IntPtr hwnd) { + wm = a; + paramL = b; + paramH = c ? 1 : 0; + this.hwnd = hwnd; + } + + public SKEvent(int a, int b, int c, IntPtr hwnd) { + wm = a; + paramL = b; + paramH = c; + this.hwnd = hwnd; + } + } + + /// + /// + /// holds a keyword and the associated VK_ for it + /// + private class KeywordVk { + internal string keyword; + internal int vk; + + public KeywordVk(string key, int v) { + keyword = key; + vk = v; + } + } + + /// + /// + /// this class is our callback for the journaling hook we install + /// + private class SendKeysHookProc { + + // Microsoft: There appears to be a timing issue where setting and removing and then setting + // these hooks via SetWindowsHookEx / UnhookWindowsHookEx can cause messages to be left + // in the queue and sent after the re hookup happens. This puts us in a bad state as we + // get an HC_SKIP before an HC_GETNEXT. So in that case, we just ignore the HC_SKIP calls + // until we get an HC_GETNEXT. We also sleep a bit in the Unhook... + // + private bool gotNextEvent = false; + + public virtual IntPtr Callback(int code, IntPtr wparam, IntPtr lparam) { + NativeMethods.EVENTMSG eventmsg = (NativeMethods.EVENTMSG)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.EVENTMSG)); + + + if (UnsafeNativeMethods.GetAsyncKeyState((int)Keys.Pause) != 0) { + SendKeys.stopHook = true; + } + + // + switch (code) { + case NativeMethods.HC_SKIP: + + if (!gotNextEvent) { + break; + } + + if (SendKeys.events != null && SendKeys.events.Count > 0) { + SendKeys.events.Dequeue(); + } + SendKeys.stopHook = SendKeys.events == null || SendKeys.events.Count == 0; + break; + + case NativeMethods.HC_GETNEXT: + + gotNextEvent = true; + + #if DEBUG + Debug.Assert(SendKeys.events != null && SendKeys.events.Count > 0 && !SendKeys.stopHook, "HC_GETNEXT when queue is empty!"); + #endif + + SKEvent evt = (SKEvent)SendKeys.events.Peek(); + eventmsg.message = evt.wm; + eventmsg.paramL = evt.paramL; + eventmsg.paramH = evt.paramH; + eventmsg.hwnd = evt.hwnd; + eventmsg.time = SafeNativeMethods.GetTickCount(); + Marshal.StructureToPtr(eventmsg, lparam, true); + break; + + default: + if (code < 0) + UnsafeNativeMethods.CallNextHookEx(new HandleRef(null, SendKeys.hhook), code, wparam, lparam); + break; + } + + if (SendKeys.stopHook) { + SendKeys.UninstallJournalingHook(); + gotNextEvent = false; + } + return IntPtr.Zero; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/SendKeys.cs.back b/WindowsForms/Managed/System/WinForms/SendKeys.cs.back new file mode 100644 index 000000000..bf698c795 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SendKeys.cs.back @@ -0,0 +1,1113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Security; + + using System.Diagnostics; + + using System; + + using System.Drawing; + using System.Collections; + using System.ComponentModel; + using System.Globalization; + + /// + /// + /// Provides methods for sending keystrokes to an application. + /// + public class SendKeys { + private const int HAVESHIFT = 0; + private const int HAVECTRL = 1; + private const int HAVEALT = 2; + + // I'm unsure what significance the value 10 has, but it seems to make sense + // to make this a constant rather than have 10 sprinkled throughout the code. + // It appears to be a sentinel value of some sort - indicating an unknown + // grouping level. + // + private const int UNKNOWN_GROUPING = 10; + + private static KeywordVk [] keywords = new KeywordVk[] { + new KeywordVk("ENTER", (int)Keys.Return), + new KeywordVk("TAB", (int)Keys.Tab), + new KeywordVk("ESC", (int)Keys.Escape), + new KeywordVk("ESCAPE", (int)Keys.Escape), + new KeywordVk("HOME", (int)Keys.Home), + new KeywordVk("END", (int)Keys.End), + new KeywordVk("LEFT", (int)Keys.Left), + new KeywordVk("RIGHT", (int)Keys.Right), + new KeywordVk("UP", (int)Keys.Up), + new KeywordVk("DOWN", (int)Keys.Down), + new KeywordVk("PGUP", (int)Keys.Prior), + new KeywordVk("PGDN", (int)Keys.Next), + new KeywordVk("NUMLOCK", (int)Keys.NumLock), + new KeywordVk("SCROLLLOCK", (int)Keys.Scroll), + new KeywordVk("PRTSC", (int)Keys.PrintScreen), + new KeywordVk("BREAK", (int)Keys.Cancel), + new KeywordVk("BACKSPACE", (int)Keys.Back), + new KeywordVk("BKSP", (int)Keys.Back), + new KeywordVk("BS", (int)Keys.Back), + new KeywordVk("CLEAR", (int)Keys.Clear), + new KeywordVk("CAPSLOCK", (int)Keys.----), + new KeywordVk("INS", (int)Keys.Insert), + new KeywordVk("INSERT", (int)Keys.Insert), + new KeywordVk("DEL", (int)Keys.Delete), + new KeywordVk("DELETE", (int)Keys.Delete), + new KeywordVk("HELP", (int)Keys.Help), + new KeywordVk("F1", (int)Keys.F1), + new KeywordVk("F2", (int)Keys.F2), + new KeywordVk("F3", (int)Keys.F3), + new KeywordVk("F4", (int)Keys.F4), + new KeywordVk("F5", (int)Keys.F5), + new KeywordVk("F6", (int)Keys.F6), + new KeywordVk("F7", (int)Keys.F7), + new KeywordVk("F8", (int)Keys.F8), + new KeywordVk("F9", (int)Keys.F9), + new KeywordVk("F10", (int)Keys.F10), + new KeywordVk("F11", (int)Keys.F11), + new KeywordVk("F12", (int)Keys.F12), + new KeywordVk("F13", (int)Keys.F13), + new KeywordVk("F14", (int)Keys.F14), + new KeywordVk("F15", (int)Keys.F15), + new KeywordVk("F16", (int)Keys.F16), + new KeywordVk("MULTIPLY", (int)Keys.Multiply), + new KeywordVk("ADD", (int)Keys.Add), + new KeywordVk("SUBTRACT", (int)Keys.Subtract), + new KeywordVk("DIVIDE", (int)Keys.Divide), + new KeywordVk("+", (int)Keys.Add), + new KeywordVk("%", (int)(Keys.D5 | Keys.Shift)), + new KeywordVk("^", (int)(Keys.D6 | Keys.Shift)), + }; + + private const int SHIFTKEYSCAN = 0x0100; + private const int CTRLKEYSCAN = 0x0200; + private const int ALTKEYSCAN = 0x0400; + + /// + /// + /// should we stop using the hook? + /// + private static bool stopHook; + + /// + /// + /// HHOOK + /// + private static IntPtr hhook; + + private static NativeMethods.HookProc hook; + + /// + /// + /// vector of events that we have yet to post to the journaling hook. + /// + private static Queue events; + + private static bool fStartNewChar; + + private static SKWindow messageWindow; + + private enum SendMethodTypes + { + Default = 1, + JournalHook = 2, + SendInput = 3 + } + + private static SendMethodTypes? sendMethod = null; + + private static bool? hookSupported = null; + + // Used only for SendInput because SendInput alters the global state of the keyboard + private static bool capslockChanged; + private static bool numlockChanged; + private static bool scrollLockChanged; + private static bool kanaChanged; + + static SendKeys() { + Application.ThreadExit += new EventHandler(OnThreadExit); + messageWindow = new SKWindow(); + messageWindow.CreateControl(); + } + + /// + /// + /// private constructor to prevent people from creating one of these. they + /// should use public static methods + /// + private SendKeys() { + } + + /// + /// + /// adds an event to our list of events for the hook + /// + private static void AddEvent(SKEvent skevent) { + + if (events == null) { + events = new Queue(); + } + events.Enqueue(skevent); + } + + // Helper function for ParseKeys for doing simple, self-describing characters. + private static bool AddSimpleKey(char character, int repeat, IntPtr hwnd, int[] haveKeys, bool fStartNewChar, int cGrp) { + int vk = UnsafeNativeMethods.VkKeyScan(character); + + if (vk != -1) { + if (haveKeys[HAVESHIFT] == 0 && (vk & SHIFTKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVECTRL] == 0 && (vk & CTRLKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVEALT] == 0 && (vk & ALTKEYSCAN) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + } + + AddMsgsForVK(vk & 0xff, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + else { + int oemVal = SafeNativeMethods.OemKeyScan((short)(0xFF & (int)character)); + for (int i = 0; i < repeat; i++) { + AddEvent(new SKEvent(NativeMethods.WM_CHAR, character, (int)(oemVal & 0xFFFF), hwnd)); + } + } + + if (cGrp != 0) fStartNewChar = true; + return fStartNewChar; + } + + /// + /// + /// given the vk, add the appropriate messages for it + /// + private static void AddMsgsForVK(int vk, int repeat, bool altnoctrldown, IntPtr hwnd) { + for (int i = 0; i < repeat; i++) { + AddEvent(new SKEvent(altnoctrldown ? NativeMethods.WM_SYSKEYDOWN : NativeMethods.WM_KEYDOWN, vk, fStartNewChar, hwnd)); + // fStartNewChar = false; + AddEvent(new SKEvent(altnoctrldown ? NativeMethods.WM_SYSKEYUP : NativeMethods.WM_KEYUP, vk, fStartNewChar, hwnd)); + } + } + + /// + /// + /// called whenever there is a closing parenthesis, or the end of a + /// character. This generates events for the end of a modifier. + /// + private static void CancelMods(int [] haveKeys, int level, IntPtr hwnd) { + if (haveKeys[HAVESHIFT] == level) { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ShiftKey, false, hwnd)); + haveKeys[HAVESHIFT] = 0; + } + if (haveKeys[HAVECTRL] == level) { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ControlKey, false, hwnd)); + haveKeys[HAVECTRL] = 0; + } + if (haveKeys[HAVEALT] == level) { + AddEvent(new SKEvent(NativeMethods.WM_SYSKEYUP, (int)Keys.Menu, false, hwnd)); + haveKeys[HAVEALT] = 0; + } + } + + /// + /// + /// install the hook. quite easy + /// + private static void InstallHook() { + if (hhook == IntPtr.Zero) { + hook = new NativeMethods.HookProc(new SendKeysHookProc().Callback); + stopHook = false; + hhook = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_JOURNALPLAYBACK, + hook, + new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), + 0); + if (hhook == IntPtr.Zero) + throw new SecurityException(SR.GetString(SR.SendKeysHookFailed)); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void TestHook() + { + hookSupported = false; + try + { + + NativeMethods.HookProc hookProc = new NativeMethods.HookProc(EmptyHookCallback); + IntPtr hookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_JOURNALPLAYBACK, + hookProc, + new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), + 0); + + hookSupported = (hookHandle != IntPtr.Zero); + + if (hookHandle != IntPtr.Zero) + { + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(null, hookHandle)); + } + } + catch {} // ignore any exceptions to keep existing SendKeys behavior + } + + private static IntPtr EmptyHookCallback(int code, IntPtr wparam, IntPtr lparam) + { + return IntPtr.Zero; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void LoadSendMethodFromConfig() + { + if (!sendMethod.HasValue) + { + sendMethod = SendMethodTypes.Default; + + try + { + // read SendKeys value from config file, not case sensitive + string value = System.Configuration.ConfigurationManager.AppSettings.Get("SendKeys"); + + if (string.IsNullOrEmpty(value)) + return; + + if (value.Equals("JournalHook", StringComparison.OrdinalIgnoreCase)) + sendMethod = SendMethodTypes.JournalHook; + else if (value.Equals("SendInput", StringComparison.OrdinalIgnoreCase)) + sendMethod = SendMethodTypes.SendInput; + } + catch {} // ignore any exceptions to keep existing SendKeys behavior + } + } + + /// + /// + /// tells us to shut down the server, perhaps if we're shutting down and the + /// hook is still running + /// + private static void JournalCancel() { + if (hhook != IntPtr.Zero) { + stopHook = false; + if (events != null) { + events.Clear(); + } + hhook = IntPtr.Zero; + } + } + + private static byte[] GetKeyboardState() { + byte [] keystate = new byte[256]; + UnsafeNativeMethods.GetKeyboardState(keystate); + return keystate; + } + + private static void SetKeyboardState(byte[] keystate) { + UnsafeNativeMethods.SetKeyboardState(keystate); + } + + /// + /// + /// before we do a sendkeys, we want to clear the state + /// of a couple of keys [capslock, numlock, scrolllock] so they don't + /// interfere. + /// + private static void ClearKeyboardState() { + + byte [] keystate = GetKeyboardState(); + + keystate[(int)Keys.----] = 0; + keystate[(int)Keys.NumLock] = 0; + keystate[(int)Keys.Scroll] = 0; + + SetKeyboardState(keystate); + } + + /// + /// + /// given the string, match the keyword to a VK. return -1 if it don't match + /// nuthin' + /// + private static int MatchKeyword(string keyword) { + for (int i = 0; i < keywords.Length; i++) + if (String.Equals(keywords[i].keyword, keyword, StringComparison.OrdinalIgnoreCase)) + return keywords[i].vk; + + return -1; + } + + /// + /// + /// This event is raised from Application when each window thread + /// termiantes. It gives us a chance to uninstall our journal + /// hook if we had one installed. + /// + private static void OnThreadExit(object sender, EventArgs e) { + try { + UninstallJournalingHook(); + } + catch { + } + } + + /// + /// + /// parse the string the user has given us, and generate the appropriate + /// events for the journaling hook + /// + private static void ParseKeys(string keys, IntPtr hwnd) { + + int i = 0; + + // these four variables are used for grouping + int [] haveKeys = new int[] { 0, 0, 0}; // shift, ctrl, alt + int cGrp = 0; + + // fStartNewChar indicates that the next msg will be the first + // of a char or char group. This is needed for IntraApp Nesting + // of SendKeys. + // + fStartNewChar = true; + + // okay, start whipping through the characters one at a time. + // + int keysLen = keys.Length; + while (i < keysLen) { + int repeat = 1; + char ch = keys[i]; + int vk = 0; + + switch (ch) { + case '}': + // if these appear at this point they are out of + // context, so return an error. KeyStart processes + // ochKeys up to the appropriate KeyEnd. + // + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + case '{': + int j = i + 1; + + // There's a unique class of strings of the form "{} n}" where + // n is an integer - in this case we want to send n copies of the '}' character. + // Here we test for the possibility of this class of problems, and skip the + // first '}' in the string if necessary. + // + if (j + 1 < keysLen && keys[j] == '}') { + // Scan for the final '}' character + int final = j + 1; + while (final < keysLen && keys[final] != '}') { + final++; + } + if (final < keysLen) { + // Found the special case, so skip the first '}' in the string. + // The remainder of the code will attempt to find the repeat count. + j++; + } + } + + // okay, we're in a {...} situation. look for the keyword + // + while (j < keysLen && keys[j] != '}' + && !Char.IsWhiteSpace(keys[j])) { + j++; + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + + // okay, have our KEYWORD. verify it's one we know about + // + string keyName = keys.Substring(i + 1, j - (i + 1)); + + // see if we have a space, which would mean a repeat count. + // + if (Char.IsWhiteSpace(keys[j])) { + int digit; + while (j < keysLen && Char.IsWhiteSpace(keys[j])) { + j++; + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + + if (Char.IsDigit(keys[j])) { + digit = j; + while (j < keysLen && Char.IsDigit(keys[j])) { + j++; + } + repeat = Int32.Parse(keys.Substring(digit, j - digit), CultureInfo.InvariantCulture); + } + } + + if (j >= keysLen) { + throw new ArgumentException(SR.GetString(SR.SendKeysKeywordDelimError)); + } + if (keys[j] != '}') { + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysRepeat)); + } + + vk = MatchKeyword(keyName); + if (vk != -1) { + // Unlike AddSimpleKey, the bit mask uses Keys, rather than scan keys + if (haveKeys[HAVESHIFT] == 0 && (vk & (int)Keys.Shift) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVECTRL] == 0 && (vk & (int)Keys.Control) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + } + + if (haveKeys[HAVEALT] == 0 && (vk & (int)Keys.Alt) != 0) { + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + } + AddMsgsForVK(vk, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + else if (keyName.Length == 1) { + fStartNewChar = AddSimpleKey(keyName[0], repeat, hwnd, haveKeys, fStartNewChar, cGrp); + } + else { + throw new ArgumentException(SR.GetString(SR.InvalidSendKeysKeyword, keys.Substring(i + 1, j - (i + 1)))); + } + + // don't forget to position ourselves at the end of the {...} group + i = j; + break; + + case '+': + if (haveKeys[HAVESHIFT] != 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ShiftKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVESHIFT] = UNKNOWN_GROUPING; + break; + + case '^': + if (haveKeys[HAVECTRL]!= 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent(NativeMethods.WM_KEYDOWN, (int)Keys.ControlKey, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVECTRL] = UNKNOWN_GROUPING; + break; + + case '%': + if (haveKeys[HAVEALT] != 0) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + + AddEvent(new SKEvent((haveKeys[HAVECTRL] != 0) ? NativeMethods.WM_KEYDOWN : NativeMethods.WM_SYSKEYDOWN, + (int)Keys.Menu, fStartNewChar, hwnd)); + fStartNewChar = false; + haveKeys[HAVEALT] = UNKNOWN_GROUPING; + break; + + case '(': + // convert all immediate mode states to group mode + // Allows multiple keys with the same shift, etc. state. + // Nests three deep. + // + cGrp++; + if (cGrp > 3) throw new ArgumentException(SR.GetString(SR.SendKeysNestingError)); + + if (haveKeys[HAVESHIFT] == UNKNOWN_GROUPING) haveKeys[HAVESHIFT] = cGrp; + if (haveKeys[HAVECTRL] == UNKNOWN_GROUPING) haveKeys[HAVECTRL] = cGrp; + if (haveKeys[HAVEALT] == UNKNOWN_GROUPING) haveKeys[HAVEALT] = cGrp; + break; + + case ')': + if (cGrp < 1) throw new ArgumentException(SR.GetString(SR.InvalidSendKeysString, keys)); + CancelMods(haveKeys, cGrp, hwnd); + cGrp--; + if (cGrp == 0) fStartNewChar = true; + break; + + case '~': + vk = (int)Keys.Return; + AddMsgsForVK(vk, repeat, haveKeys[HAVEALT] > 0 && haveKeys[HAVECTRL] == 0, hwnd); + break; + + default: + fStartNewChar = AddSimpleKey(keys[i], repeat, hwnd, haveKeys, fStartNewChar, cGrp); + break; + } + + + // next element in the string + // + i++; + } + + if (cGrp != 0) + throw new ArgumentException(SR.GetString(SR.SendKeysGroupDelimError)); + + CancelMods(haveKeys, UNKNOWN_GROUPING, hwnd); + } + + // Uses User32 SendInput to send keystrokes + private static void SendInput(byte[] oldKeyboardState, Queue previousEvents) + { + // Should be a No-Opt most of the time + AddCancelModifiersForPreviousEvents(previousEvents); + + // SKEvents are sent as sent as 1 or 2 inputs + // currentInput[0] represents the SKEvent + // currentInput[1] is a KeyUp to prevent all identical WM_CHARs to be sent as one message + NativeMethods.INPUT[] currentInput = new NativeMethods.INPUT[2]; + + // all events are Keyboard events + currentInput[0].type = NativeMethods.INPUT_KEYBOARD; + currentInput[1].type = NativeMethods.INPUT_KEYBOARD; + + // set KeyUp values for currentInput[1] + currentInput[1].inputUnion.ki.wVk = (short) 0; + currentInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE | NativeMethods.KEYEVENTF_KEYUP; + + // initialize unused members + currentInput[0].inputUnion.ki.dwExtraInfo = IntPtr.Zero; + currentInput[0].inputUnion.ki.time = 0; + currentInput[1].inputUnion.ki.dwExtraInfo = IntPtr.Zero; + currentInput[1].inputUnion.ki.time = 0; + + // send each of our SKEvents using SendInput + int INPUTSize = Marshal.SizeOf(typeof(NativeMethods.INPUT)); + + // need these outside the lock below + uint eventsSent=0; + int eventsTotal; + + // A lock here will allow multiple threads to SendInput at the same time. + // This mimics the JournalHook method of using the message loop to mitigate + // threading issues. There is still a theoretical thread issue with adding + // to the events Queue (both JournalHook and SendInput), but we do not want + // to alter the timings of the existing shipped behavior. I did not run into + // problems with 2 threads on a multiproc machine + lock (events.SyncRoot) + { + // block keyboard and mouse input events from reaching applications. + bool blockInputSuccess = UnsafeNativeMethods.BlockInput(true); + + try + { + eventsTotal = events.Count; + ClearGlobalKeys(); + + for (int i = 0; i < eventsTotal; i++) + { + SKEvent skEvent = (SKEvent)events.Dequeue(); + + currentInput[0].inputUnion.ki.dwFlags = 0; + + if (skEvent.wm == NativeMethods.WM_CHAR) + { + // for WM_CHAR, send a KEYEVENTF_UNICODE instead of a Keyboard event + // to support extended ascii characters with no keyboard equivalent. + // send currentInput[1] in this case + currentInput[0].inputUnion.ki.wVk = (short)0; + currentInput[0].inputUnion.ki.wScan = (short)skEvent.paramL; + currentInput[0].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE; + currentInput[1].inputUnion.ki.wScan = (short)skEvent.paramL; + + // call SendInput, increment the eventsSent but subtract 1 for the extra one sent + eventsSent += UnsafeNativeMethods.SendInput(2, currentInput, INPUTSize) - 1; + } + else + { + // just need to send currentInput[0] for skEvent + currentInput[0].inputUnion.ki.wScan = 0; + + // add KeyUp flag if we have a KeyUp + if (skEvent.wm == NativeMethods.WM_KEYUP || skEvent.wm == NativeMethods.WM_SYSKEYUP) + { + currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_KEYUP; + } + + // Sets KEYEVENTF_EXTENDEDKEY flag if necessary + if (IsExtendedKey(skEvent)) + { + currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_EXTENDEDKEY; + } + + currentInput[0].inputUnion.ki.wVk = (short)skEvent.paramL; + + // send only currentInput[0] + eventsSent += UnsafeNativeMethods.SendInput(1, currentInput, INPUTSize); + + CheckGlobalKeys(skEvent); + } + + // We need this slight delay here for Alt-Tab to work on Vista when the Aero theme + // is running. See DevDiv bugs 23355. Although this does not look good, a delay + // here actually more closely resembles the old JournalHook that processes each + // event individually in the hook callback. + System.Threading.Thread.Sleep(1); + } + + // reset the keyboard back to what it was before inputs were sent, SendInupt modifies + // the global lights on the keyboard (caps, scroll..) so we need to call it again to + // undo those changes + ResetKeyboardUsingSendInput(INPUTSize); + } + finally + { + SetKeyboardState(oldKeyboardState); + + // unblock input if it was previously blocked + if (blockInputSuccess) + { + UnsafeNativeMethods.BlockInput(false); + } + } + } + + // check to see if we sent the number of events we're supposed to + if (eventsSent != eventsTotal) + { + // calls Marshal.GetLastWin32Error and sets it in the exception + throw new Win32Exception(); + } + } + + // For SendInput, these previous events that stick around if an Exception was + // thrown could modify the state of the keyboard modifiers (alt, ctrl, shift). + // We must send a KeyUp for those, JournalHook doesn't permanently set the state + // so it's ok + private static void AddCancelModifiersForPreviousEvents(Queue previousEvents) + { + if (previousEvents == null) + { + return; + } + + bool shift = false; + bool ctrl = false; + bool alt = false; + while (previousEvents.Count > 0) + { + SKEvent skEvent = (SKEvent)previousEvents.Dequeue(); + + bool isOn; + if ((skEvent.wm == NativeMethods.WM_KEYUP) || + (skEvent.wm == NativeMethods.WM_SYSKEYUP)) + { + isOn = false; + } + else if ((skEvent.wm == NativeMethods.WM_KEYDOWN) || + (skEvent.wm == NativeMethods.WM_SYSKEYDOWN)) + { + isOn = true; + } + else + { + continue; + } + + if (skEvent.paramL == (int)Keys.ShiftKey) + { + shift = isOn; + } + else if (skEvent.paramL == (int)Keys.ControlKey) + { + ctrl = isOn; + } + else if (skEvent.paramL == (int)Keys.Menu) + { + alt = isOn; + } + } + + if (shift) + { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ShiftKey, false, IntPtr.Zero)); + } + else if (ctrl) + { + AddEvent(new SKEvent(NativeMethods.WM_KEYUP, (int)Keys.ControlKey, false, IntPtr.Zero)); + } + else if (alt) + { + AddEvent(new SKEvent(NativeMethods.WM_SYSKEYUP, (int)Keys.Menu, false, IntPtr.Zero)); + } + } + + private static bool IsExtendedKey(SKEvent skEvent) + { + return (skEvent.paramL == NativeMethods.VK_UP) || + (skEvent.paramL == NativeMethods.VK_DOWN) || + (skEvent.paramL == NativeMethods.VK_LEFT) || + (skEvent.paramL == NativeMethods.VK_RIGHT) || + (skEvent.paramL == NativeMethods.VK_PRIOR) || + (skEvent.paramL == NativeMethods.VK_NEXT) || + (skEvent.paramL == NativeMethods.VK_HOME) || + (skEvent.paramL == NativeMethods.VK_END) || + (skEvent.paramL == NativeMethods.VK_INSERT) || + (skEvent.paramL == NativeMethods.VK_DELETE); + } + + private static void ClearGlobalKeys() + { + capslockChanged = false; + numlockChanged = false; + scrollLockChanged = false; + kanaChanged = false; + } + + private static void CheckGlobalKeys(SKEvent skEvent) + { + if (skEvent.wm == NativeMethods.WM_KEYDOWN) + { + switch (skEvent.paramL) + { + case (int)Keys.CapsLock: + capslockChanged = !capslockChanged; + break; + case (int)Keys.NumLock: + numlockChanged = !numlockChanged; + break; + case (int)Keys.Scroll: + scrollLockChanged = !scrollLockChanged; + break; + case (int)Keys.KanaMode: + kanaChanged = !kanaChanged; + break; + } + } + } + + private static void ResetKeyboardUsingSendInput(int INPUTSize) + { + // if the new state is the same, we don't need to do anything + if (!(capslockChanged || numlockChanged || scrollLockChanged || kanaChanged)) + { + return; + } + + // INPUT struct for resetting the keyboard + NativeMethods.INPUT[] keyboardInput = new NativeMethods.INPUT[2]; + + keyboardInput[0].type = NativeMethods.INPUT_KEYBOARD; + keyboardInput[0].inputUnion.ki.dwFlags = 0; + + keyboardInput[1].type = NativeMethods.INPUT_KEYBOARD; + keyboardInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_KEYUP; + + // SendInputs to turn on or off these keys. Inputs are pairs because KeyUp is sent for each one + if (capslockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (numlockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (scrollLockChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + + if (kanaChanged) + { + keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_KANA; + keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_KANA; + UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); + } + } + + /// + /// + /// Sends keystrokes to the active application. + /// + public static void Send(string keys) { + Send(keys, null, false); + } + + /// + /// Sends keystrokes to the active application. + /// + + + // WARNING: this method will never work if control != null, because while + // Windows journaling *looks* like it can be directed to a specific HWND, + // it can't. + // + + // No one is calling this method so it is safe to comment it out + + //private static void Send(string keys, /*bogus*/ Control control) { + // Send(keys, control, false); + //} + + + private static void Send(string keys, Control control, bool wait) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "UnmanagedCode Demanded"); + IntSecurity.UnmanagedCode.Demand(); + + if (keys == null || keys.Length == 0) return; + + // If we're not going to wait, make sure there is a pump. + // + if (!wait && !Application.MessageLoop) { + throw new InvalidOperationException(SR.GetString(SR.SendKeysNoMessageLoop)); + } + + // For SendInput only, see AddCancelModifiersForPreviousEvents for details + Queue previousEvents = null; + if ((events != null) && (events.Count != 0)) + { + previousEvents = (Queue) events.Clone(); + } + + // generate the list of events that we're going to fire off with the hook + // + ParseKeys(keys, (control != null) ? control.Handle : IntPtr.Zero); + + + // if there weren't any events posted as a result, we're done! + // + if (events == null) return; + + LoadSendMethodFromConfig(); + + byte[] oldstate = GetKeyboardState(); + + if (sendMethod.Value != SendMethodTypes.SendInput) + { + if (!hookSupported.HasValue && + sendMethod.Value == SendMethodTypes.Default) + { + // We don't know if the JournalHook will work, test it out + // so we know whether or not to call ClearKeyboardState. ClearKeyboardState + // does nothing for JournalHooks but inversely affects SendInput + TestHook(); + } + if (sendMethod.Value == SendMethodTypes.JournalHook || + hookSupported.Value) + { + ClearKeyboardState(); + InstallHook(); + SetKeyboardState(oldstate); + } + } + + if (sendMethod.Value == SendMethodTypes.SendInput || + (sendMethod.Value == SendMethodTypes.Default && !hookSupported.Value)) + { + // either SendInput is configured or JournalHooks failed by default, call SendInput + SendInput(oldstate, previousEvents); + } + + if (wait) + { + Flush(); + } + } + + /// + /// + /// Sends the given keys to the active application, and then waits for + /// the messages to be processed. + /// + public static void SendWait(string keys) { + SendWait(keys, null); + } + + /// + /// + /// Sends the given keys to the active application, and then waits for + /// the messages to be processed. + /// + + + // WARNING: this method will never work if control != null, because while + // Windows journaling *looks* like it can be directed to a specific HWND, + // it can't. + // + private static void SendWait(string keys, Control control) { + Send(keys, control, true); + } + + /// + /// + /// Processes all the Windows messages currently in the message queue. + /// + public static void Flush() { + Application.DoEvents(); + while (events != null && events.Count > 0) { + Application.DoEvents(); + } + } + + /// + /// + /// cleans up and uninstalls the hook + /// + private static void UninstallJournalingHook() { + if (hhook != IntPtr.Zero) { + stopHook = false; + if (events != null) { + events.Clear(); + } + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(null, hhook)); + hhook = IntPtr.Zero; + } + } + + /// + /// SendKeys creates a window to monitor WM_CANCELJOURNAL messages. + /// + private class SKWindow : Control { + + public SKWindow() { + SetState(STATE_TOPLEVEL, true); + SetState2(STATE2_INTERESTEDINUSERPREFERENCECHANGED, false); + SetBounds(-1, -1, 0, 0); + Visible = false; + } + + protected override void WndProc(ref Message m) { + if (m.Msg == NativeMethods.WM_CANCELJOURNAL) { + try { + SendKeys.JournalCancel(); + } + catch { + } + } + } + } + + /// + /// + /// helps us hold information about the various events we're going to journal + /// + private class SKEvent { + internal int wm; + internal int paramL; + internal int paramH; + internal IntPtr hwnd; + + public SKEvent(int a, int b, bool c, IntPtr hwnd) { + wm = a; + paramL = b; + paramH = c ? 1 : 0; + this.hwnd = hwnd; + } + + public SKEvent(int a, int b, int c, IntPtr hwnd) { + wm = a; + paramL = b; + paramH = c; + this.hwnd = hwnd; + } + } + + /// + /// + /// holds a keyword and the associated VK_ for it + /// + private class KeywordVk { + internal string keyword; + internal int vk; + + public KeywordVk(string key, int v) { + keyword = key; + vk = v; + } + } + + /// + /// + /// this class is our callback for the journaling hook we install + /// + private class SendKeysHookProc { + + // Microsoft: There appears to be a timing issue where setting and removing and then setting + // these hooks via SetWindowsHookEx / UnhookWindowsHookEx can cause messages to be left + // in the queue and sent after the re hookup happens. This puts us in a bad state as we + // get an HC_SKIP before an HC_GETNEXT. So in that case, we just ignore the HC_SKIP calls + // until we get an HC_GETNEXT. We also sleep a bit in the Unhook... + // + private bool gotNextEvent = false; + + public virtual IntPtr Callback(int code, IntPtr wparam, IntPtr lparam) { + NativeMethods.EVENTMSG eventmsg = (NativeMethods.EVENTMSG)UnsafeNativeMethods.PtrToStructure(lparam, typeof(NativeMethods.EVENTMSG)); + + + if (UnsafeNativeMethods.GetAsyncKeyState((int)Keys.Pause) != 0) { + SendKeys.stopHook = true; + } + + // + switch (code) { + case NativeMethods.HC_SKIP: + + if (!gotNextEvent) { + break; + } + + if (SendKeys.events != null && SendKeys.events.Count > 0) { + SendKeys.events.Dequeue(); + } + SendKeys.stopHook = SendKeys.events == null || SendKeys.events.Count == 0; + break; + + case NativeMethods.HC_GETNEXT: + + gotNextEvent = true; + + #if DEBUG + Debug.Assert(SendKeys.events != null && SendKeys.events.Count > 0 && !SendKeys.stopHook, "HC_GETNEXT when queue is empty!"); + #endif + + SKEvent evt = (SKEvent)SendKeys.events.Peek(); + eventmsg.message = evt.wm; + eventmsg.paramL = evt.paramL; + eventmsg.paramH = evt.paramH; + eventmsg.hwnd = evt.hwnd; + eventmsg.time = SafeNativeMethods.GetTickCount(); + Marshal.StructureToPtr(eventmsg, lparam, true); + break; + + default: + if (code < 0) + UnsafeNativeMethods.CallNextHookEx(new HandleRef(null, SendKeys.hhook), code, wparam, lparam); + break; + } + + if (SendKeys.stopHook) { + SendKeys.UninstallJournalingHook(); + gotNextEvent = false; + } + return IntPtr.Zero; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/Shortcut.cs b/WindowsForms/Managed/System/WinForms/Shortcut.cs new file mode 100644 index 000000000..b751583de --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Shortcut.cs @@ -0,0 +1,1131 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies shortcut keys that can be used by menu items. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum Shortcut { + + /// + /// + /// + /// No shortcut key is associated with the menu item. + /// + /// + None = 0, + /// + /// + /// + /// The shorcut keys CTRL+A. + /// + /// + CtrlA = Keys.Control + Keys.A, + /// + /// + /// + /// The shorcut keys CTRL+B. + /// + /// + CtrlB = Keys.Control + Keys.B, + /// + /// + /// + /// The shorcut keys CTRL+C. + /// + /// + CtrlC = Keys.Control + Keys.C, + /// + /// + /// + /// The shorcut keys CTRL+D. + /// + /// + CtrlD = Keys.Control + Keys.D, + /// + /// + /// + /// The shorcut keys CTRL+E. + /// + /// + CtrlE = Keys.Control + Keys.E, + /// + /// + /// + /// The shorcut keys CTRL+F. + /// + /// + CtrlF = Keys.Control + Keys.F, + /// + /// + /// + /// The shorcut keys CTRL+G. + /// + /// + CtrlG = Keys.Control + Keys.G, + /// + /// + /// + /// The shorcut keys CTRL+H. + /// + /// + CtrlH = Keys.Control + Keys.H, + /// + /// + /// + /// The shorcut keys CTRL+I. + /// + /// + CtrlI = Keys.Control + Keys.I, + /// + /// + /// + /// The shorcut keys CTRL+J. + /// + /// + CtrlJ = Keys.Control + Keys.J, + /// + /// + /// + /// The shorcut keys CTRL+K. + /// + /// + CtrlK = Keys.Control + Keys.K, + /// + /// + /// + /// The shorcut keys CTRL+L. + /// + /// + CtrlL = Keys.Control + Keys.L, + /// + /// + /// + /// The shorcut keys CTRL+M. + /// + /// + CtrlM = Keys.Control + Keys.M, + /// + /// + /// + /// The shorcut keys CTRL+N. + /// + /// + CtrlN = Keys.Control + Keys.N, + /// + /// + /// + /// The shorcut keys CTRL+O. + /// + /// + CtrlO = Keys.Control + Keys.O, + /// + /// + /// + /// The shorcut keys CTRL+P. + /// + /// + CtrlP = Keys.Control + Keys.P, + /// + /// + /// + /// The shorcut keys CTRL+Q. + /// + /// + CtrlQ = Keys.Control + Keys.Q, + /// + /// + /// + /// The shorcut keys CTRL+R. + /// + /// + CtrlR = Keys.Control + Keys.R, + /// + /// + /// + /// The shorcut keys CTRL+S. + /// + /// + CtrlS = Keys.Control + Keys.S, + /// + /// + /// + /// The shorcut keys CTRL+T. + /// + /// + CtrlT = Keys.Control + Keys.T, + /// + /// + /// + /// The shorcut keys CTRL+U + /// + /// + CtrlU = Keys.Control + Keys.U, + /// + /// + /// + /// The shorcut keys CTRL+V. + /// + /// + CtrlV = Keys.Control + Keys.V, + /// + /// + /// + /// The shorcut keys CTRL+W. + /// + /// + CtrlW = Keys.Control + Keys.W, + /// + /// + /// + /// The shorcut keys CTRL+X. + /// + /// + CtrlX = Keys.Control + Keys.X, + /// + /// + /// + /// The shorcut keys CTRL+Y. + /// + /// + CtrlY = Keys.Control + Keys.Y, + /// + /// + /// + /// The shorcut keys CTRL+Z. + /// + /// + CtrlZ = Keys.Control + Keys.Z, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+A. + /// + /// + CtrlShiftA = Keys.Control + Keys.Shift + Keys.A, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+B. + /// + /// + CtrlShiftB = Keys.Control + Keys.Shift + Keys.B, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+C. + /// + /// + CtrlShiftC = Keys.Control + Keys.Shift + Keys.C, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+D. + /// + /// + CtrlShiftD = Keys.Control + Keys.Shift + Keys.D, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+E. + /// + /// + CtrlShiftE = Keys.Control + Keys.Shift + Keys.E, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F. + /// + /// + CtrlShiftF = Keys.Control + Keys.Shift + Keys.F, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+G. + /// + /// + CtrlShiftG = Keys.Control + Keys.Shift + Keys.G, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+H. + /// + /// + CtrlShiftH = Keys.Control + Keys.Shift + Keys.H, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+I. + /// + /// + CtrlShiftI = Keys.Control + Keys.Shift + Keys.I, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+J. + /// + /// + CtrlShiftJ = Keys.Control + Keys.Shift + Keys.J, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+K. + /// + /// + CtrlShiftK = Keys.Control + Keys.Shift + Keys.K, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+L. + /// + /// + CtrlShiftL = Keys.Control + Keys.Shift + Keys.L, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+M. + /// + /// + CtrlShiftM = Keys.Control + Keys.Shift + Keys.M, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+N. + /// + /// + CtrlShiftN = Keys.Control + Keys.Shift + Keys.N, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+O. + /// + /// + CtrlShiftO = Keys.Control + Keys.Shift + Keys.O, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+P. + /// + /// + CtrlShiftP = Keys.Control + Keys.Shift + Keys.P, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+Q. + /// + /// + CtrlShiftQ = Keys.Control + Keys.Shift + Keys.Q, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+R. + /// + /// + CtrlShiftR = Keys.Control + Keys.Shift + Keys.R, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+S. + /// + /// + CtrlShiftS = Keys.Control + Keys.Shift + Keys.S, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+T. + /// + /// + CtrlShiftT = Keys.Control + Keys.Shift + Keys.T, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+U. + /// + /// + CtrlShiftU = Keys.Control + Keys.Shift + Keys.U, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+V. + /// + /// + CtrlShiftV = Keys.Control + Keys.Shift + Keys.V, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+W. + /// + /// + CtrlShiftW = Keys.Control + Keys.Shift + Keys.W, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+X. + /// + /// + CtrlShiftX = Keys.Control + Keys.Shift + Keys.X, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+Y. + /// + /// + CtrlShiftY = Keys.Control + Keys.Shift + Keys.Y, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+Z. + /// + /// + CtrlShiftZ = Keys.Control + Keys.Shift + Keys.Z, + /// + /// + /// + /// The shortcut key F1. + /// + /// + F1 = Keys.F1, + /// + /// + /// + /// The shortcut key F2. + /// + /// + F2 = Keys.F2, + /// + /// + /// + /// The shortcut key F3. + /// + /// + F3 = Keys.F3, + /// + /// + /// + /// The shortcut key F4. + /// + /// + F4 = Keys.F4, + /// + /// + /// + /// The shortcut key F5. + /// + /// + F5 = Keys.F5, + /// + /// + /// + /// The shortcut key F6. + /// + /// + F6 = Keys.F6, + /// + /// + /// + /// The shortcut key F7. + /// + /// + F7 = Keys.F7, + /// + /// + /// [To be supplied.] + /// + F8 = Keys.F8, + /// + /// + /// + /// The shortcut key F9. + /// + /// + F9 = Keys.F9, + /// + /// + /// + /// The shortcut key F10. + /// + /// + F10 = Keys.F10, + /// + /// + /// + /// The shortcut key F11. + /// + /// + F11 = Keys.F11, + /// + /// + /// + /// The shortcut key F12. + /// + /// + F12 = Keys.F12, + /// + /// + /// + /// The shortcut keys SHIFT+F1. + /// + /// + ShiftF1 = Keys.Shift + Keys.F1, + /// + /// + /// + /// The shortcut keys SHIFT+F2. + /// + /// + ShiftF2 = Keys.Shift + Keys.F2, + /// + /// + /// + /// The shortcut keys SHIFT+F3. + /// + /// + ShiftF3 = Keys.Shift + Keys.F3, + /// + /// + /// + /// The shortcut keys SHIFT+F4. + /// + /// + ShiftF4 = Keys.Shift + Keys.F4, + /// + /// + /// + /// The shortcut keys SHIFT+F5. + /// + /// + ShiftF5 = Keys.Shift + Keys.F5, + /// + /// + /// + /// The shortcut keys SHIFT+F6. + /// + /// + ShiftF6 = Keys.Shift + Keys.F6, + /// + /// + /// + /// The shortcut keys SHIFT+F7. + /// + /// + ShiftF7 = Keys.Shift + Keys.F7, + /// + /// + /// + /// The shortcut keys SHIFT+F8. + /// + /// + ShiftF8 = Keys.Shift + Keys.F8, + /// + /// + /// + /// The shortcut keys SHIFT+F9. + /// + /// + ShiftF9 = Keys.Shift + Keys.F9, + /// + /// + /// + /// The shortcut keys SHIFT+F10. + /// + /// + ShiftF10 = Keys.Shift + Keys.F10, + /// + /// + /// + /// The shortcut keys SHIFT+F11. + /// + /// + ShiftF11 = Keys.Shift + Keys.F11, + /// + /// + /// + /// The shortcut keys SHIFT+F12. + /// + /// + ShiftF12 = Keys.Shift + Keys.F12, + /// + /// + /// + /// The shortcut keys CTRL+F1. + /// + /// + CtrlF1 = Keys.Control + Keys.F1, + /// + /// + /// + /// The shortcut keys CTRL+F2. + /// + /// + CtrlF2 = Keys.Control + Keys.F2, + /// + /// + /// + /// The shortcut keys CTRL+F3. + /// + /// + CtrlF3 = Keys.Control + Keys.F3, + /// + /// + /// + /// The shortcut keys CTRL+F4. + /// + /// + CtrlF4 = Keys.Control + Keys.F4, + /// + /// + /// + /// The shortcut keys CTRL+F5. + /// + /// + CtrlF5 = Keys.Control + Keys.F5, + /// + /// + /// + /// The shortcut keys CTRL+F6. + /// + /// + CtrlF6 = Keys.Control + Keys.F6, + /// + /// + /// + /// The shortcut keys CTRL+F7. + /// + /// + CtrlF7 = Keys.Control + Keys.F7, + /// + /// + /// + /// The shortcut keys CTRL+F8. + /// + /// + CtrlF8 = Keys.Control + Keys.F8, + /// + /// + /// + /// The shortcut keys CTRL+F9. + /// + /// + CtrlF9 = Keys.Control + Keys.F9, + /// + /// + /// + /// The shortcut keys CTRL+F10. + /// + /// + CtrlF10 = Keys.Control + Keys.F10, + /// + /// + /// + /// The shortcut keys CTRL+F11. + /// + /// + CtrlF11 = Keys.Control + Keys.F11, + /// + /// + /// + /// The shortcut keys CTRL+F12. + /// + /// + CtrlF12 = Keys.Control + Keys.F12, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F1. + /// + /// + CtrlShiftF1 = Keys.Control + Keys.Shift + Keys.F1, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F2. + /// + /// + CtrlShiftF2 = Keys.Control + Keys.Shift + Keys.F2, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F3. + /// + /// + CtrlShiftF3 = Keys.Control + Keys.Shift + Keys.F3, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F4. + /// + /// + CtrlShiftF4 = Keys.Control + Keys.Shift + Keys.F4, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F5. + /// + /// + CtrlShiftF5 = Keys.Control + Keys.Shift + Keys.F5, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F6. + /// + /// + CtrlShiftF6 = Keys.Control + Keys.Shift + Keys.F6, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F7. + /// + /// + CtrlShiftF7 = Keys.Control + Keys.Shift + Keys.F7, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F8. + /// + /// + CtrlShiftF8 = Keys.Control + Keys.Shift + Keys.F8, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F9. + /// + /// + CtrlShiftF9 = Keys.Control + Keys.Shift + Keys.F9, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F10. + /// + /// + CtrlShiftF10 = Keys.Control + Keys.Shift + Keys.F10, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F11. + /// + /// + CtrlShiftF11 = Keys.Control + Keys.Shift + Keys.F11, + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+F12. + /// + /// + CtrlShiftF12 = Keys.Control + Keys.Shift + Keys.F12, + /// + /// + /// + /// The shortcut key INSERT. + /// + /// + Ins = Keys.Insert, + /// + /// + /// + /// The shortcut keys CTRL+INSERT. + /// + /// + CtrlIns = Keys.Control + Keys.Insert, + /// + /// + /// + /// The shortcut keys SHIFT+INSERT. + /// + /// + ShiftIns = Keys.Shift + Keys.Insert, + /// + /// + /// + /// The shortcut key DELETE. + /// + /// + Del = Keys.Delete, + /// + /// + /// + /// The shortcut keys CTRL+DELETE. + /// + /// + CtrlDel = Keys.Control + Keys.Delete, + /// + /// + /// + /// The shortcut keys SHIFT+DELETE. + /// + /// + ShiftDel = Keys.Shift + Keys.Delete, + /// + /// + /// + /// The shortcut keys Alt + RightArrow. + /// + /// + AltRightArrow = Keys.Alt + Keys.Right, + /// + /// + /// + /// The shortcut keys ALT+LeftArrow. + /// + /// + AltLeftArrow = Keys.Alt + Keys.Left, + /// + /// + /// + /// The shortcut keys ALT+UpArrow. + /// + /// + AltUpArrow = Keys.Alt + Keys.Up, + /// + /// + /// + /// The shortcut keys Alt + DownArrow. + /// + /// + AltDownArrow = Keys.Alt + Keys.Down, + /// + /// + /// + /// The shortcut keys ALT+BACKSPACE. + /// + /// + AltBksp = Keys.Alt + Keys.Back, + /// + /// + /// + /// The shortcut keys ALT+F1. + /// + /// + AltF1 = Keys.Alt + Keys.F1, + /// + /// + /// + /// The shortcut keys ALT+F2. + /// + /// + AltF2 = Keys.Alt + Keys.F2, + /// + /// + /// + /// The shortcut keys ALT+F3. + /// + /// + AltF3 = Keys.Alt + Keys.F3, + /// + /// + /// + /// The shortcut keys ALT+F4. + /// + /// + AltF4 = Keys.Alt + Keys.F4, + /// + /// + /// + /// The shortcut keys ALT+F5. + /// + /// + AltF5 = Keys.Alt + Keys.F5, + /// + /// + /// + /// The shortcut keys ALT+F6. + /// + /// + AltF6 = Keys.Alt + Keys.F6, + /// + /// + /// + /// The shortcut keys ALT+F7. + /// + /// + AltF7 = Keys.Alt + Keys.F7, + /// + /// + /// + /// The shortcut keys ALT+F8. + /// + /// + AltF8 = Keys.Alt + Keys.F8, + /// + /// + /// + /// The shortcut keys ALT+F9. + /// + /// + AltF9 = Keys.Alt + Keys.F9, + /// + /// + /// + /// The shortcut keys ALT+F10. + /// + /// + AltF10 = Keys.Alt + Keys.F10, + /// + /// + /// + /// The shortcut keys ALT+F11. + /// + /// + AltF11 = Keys.Alt + Keys.F11, + /// + /// + /// + /// The shortcut keys ALT+F12. + /// + /// + AltF12 = Keys.Alt + Keys.F12, + + /// + /// + /// + /// The shortcut keys ALT+0. + /// + /// + Alt0 = Keys.Alt + Keys.D0, + + /// + /// + /// + /// The shortcut keys ALT+1. + /// + /// + Alt1 = Keys.Alt + Keys.D1, + + /// + /// + /// + /// The shortcut keys ALT+2. + /// + /// + Alt2 = Keys.Alt + Keys.D2, + + /// + /// + /// + /// The shortcut keys ALT+3. + /// + /// + Alt3 = Keys.Alt + Keys.D3, + + /// + /// + /// + /// The shortcut keys ALT+4. + /// + /// + Alt4 = Keys.Alt + Keys.D4, + + /// + /// + /// + /// The shortcut keys ALT+5. + /// + /// + Alt5 = Keys.Alt + Keys.D5, + + /// + /// + /// + /// The shortcut keys ALT+6. + /// + /// + Alt6 = Keys.Alt + Keys.D6, + + /// + /// + /// + /// The shortcut keys ALT+7. + /// + /// + Alt7 = Keys.Alt + Keys.D7, + + /// + /// + /// + /// The shortcut keys ALT+8. + /// + /// + Alt8 = Keys.Alt + Keys.D8, + + /// + /// + /// + /// The shortcut keys ALT+9. + /// + /// + Alt9 = Keys.Alt + Keys.D9, + + /// + /// + /// + /// The shortcut keys CTRL+0. + /// + /// + Ctrl0 = Keys.Control + Keys.D0, + + /// + /// + /// + /// The shortcut keys CTRL+1. + /// + /// + Ctrl1 = Keys.Control + Keys.D1, + + /// + /// + /// + /// The shortcut keys CTRL+2. + /// + /// + Ctrl2 = Keys.Control + Keys.D2, + + /// + /// + /// + /// The shortcut keys CTRL+3. + /// + /// + Ctrl3 = Keys.Control + Keys.D3, + + /// + /// + /// + /// The shortcut keys CTRL+4. + /// + /// + Ctrl4 = Keys.Control + Keys.D4, + + /// + /// + /// + /// The shortcut keys CTRL+5. + /// + /// + Ctrl5 = Keys.Control + Keys.D5, + + /// + /// + /// + /// The shortcut keys CTRL+6. + /// + /// + Ctrl6 = Keys.Control + Keys.D6, + + /// + /// + /// + /// The shortcut keys CTRL+7. + /// + /// + Ctrl7 = Keys.Control + Keys.D7, + + /// + /// + /// + /// The shortcut keys CTRL+8. + /// + /// + Ctrl8 = Keys.Control + Keys.D8, + + /// + /// + /// + /// The shortcut keys CTRL+9. + /// + /// + Ctrl9 = Keys.Control + Keys.D9, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+0. + /// + /// + CtrlShift0 = Keys.Control + Keys.Shift + Keys.D0, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+1. + /// + /// + CtrlShift1 = Keys.Control + Keys.Shift + Keys.D1, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+2. + /// + /// + CtrlShift2 = Keys.Control + Keys.Shift + Keys.D2, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+3. + /// + /// + CtrlShift3 = Keys.Control + Keys.Shift + Keys.D3, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+4. + /// + /// + CtrlShift4 = Keys.Control + Keys.Shift + Keys.D4, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+5. + /// + /// + CtrlShift5 = Keys.Control + Keys.Shift + Keys.D5, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+6. + /// + /// + CtrlShift6 = Keys.Control + Keys.Shift + Keys.D6, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+7. + /// + /// + CtrlShift7 = Keys.Control + Keys.Shift + Keys.D7, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+8. + /// + /// + CtrlShift8 = Keys.Control + Keys.Shift + Keys.D8, + + /// + /// + /// + /// The shortcut keys CTRL+SHIFT+9. + /// + /// + CtrlShift9 = Keys.Control + Keys.Shift + Keys.D9, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/SizeGripStyle.cs b/WindowsForms/Managed/System/WinForms/SizeGripStyle.cs new file mode 100644 index 000000000..b7976c379 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SizeGripStyle.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// Specifies the style of the sizing grip on a . + /// + /// + public enum SizeGripStyle { + /// + /// + /// + /// The size grip is automatically display when needed. + /// + /// + Auto = 0, + /// + /// + /// + /// The sizing grip is always shown on the form. + /// + /// + Show = 1, + /// + /// + /// + /// The sizing grip is hidden. + /// + /// + Hide = 2, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SortOrder.cs b/WindowsForms/Managed/System/WinForms/SortOrder.cs new file mode 100644 index 000000000..fa057491a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SortOrder.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies how items in + /// a list are sorted. + /// + /// + public enum SortOrder { + + /// + /// + /// + /// The items are + /// not sorted. + /// + /// + None = 0, + + /// + /// + /// + /// The items + /// are sorted in ascending order. + /// + /// + Ascending = 1, + + /// + /// + /// + /// The items are + /// sorted in descending order. + /// + /// + Descending = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/SpecialFolderEnumConverter.cs b/WindowsForms/Managed/System/WinForms/SpecialFolderEnumConverter.cs new file mode 100644 index 000000000..1fb3ac070 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SpecialFolderEnumConverter.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Collections; + + internal class SpecialFolderEnumConverter : AlphaSortedEnumConverter { + public SpecialFolderEnumConverter(Type type) : base(type) { + } + + /// + /// See VSWhidbey #376570. Personal appears twice in type editor because its numeric value matches with MyDocuments. + /// This code filters out the duplicate value. + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + StandardValuesCollection values = base.GetStandardValues(context); + ArrayList list = new ArrayList(); + int count = values.Count; + bool personalSeen = false; + for (int i = 0; i < count; i++) { + if (values[i] is System.Environment.SpecialFolder && + values[i].Equals(System.Environment.SpecialFolder.Personal)) { + if (!personalSeen) { + personalSeen = true; + list.Add(values[i]); + } + } + else { + list.Add(values[i]); + } + } + return new StandardValuesCollection(list); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/SplitContainer.cs b/WindowsForms/Managed/System/WinForms/SplitContainer.cs new file mode 100644 index 000000000..e0a4ffeeb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitContainer.cs @@ -0,0 +1,2605 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Collections; + using System.Drawing.Drawing2D; + using System.Globalization; + + /// + /// + /// A SplitContainer is a ContainerControl with 2 panels separated with a splitter + /// in the middle. This is a composite control. The user can drag and drop this control from Toolbox. + /// Controls can be added to the right panel and the left panel. The Orientation can be either Horizontal or Vertical. + /// The Controls inside the Panels would be redrawn with the new Orientation. + /// With this control the user need be aware of docking, z-order of the controls. The controls get parented when thry are + /// dropped on the SpitContainer. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("SplitterMoved"), + Docking(DockingBehavior.AutoDock), + Designer("System.Windows.Forms.Design.SplitContainerDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionSplitContainer) + ] + public class SplitContainer : ContainerControl, ISupportInitialize + { + + // + // CONTANTS USED DURING DRAWING SPLITTER MOOVEMENTS + // + private const int DRAW_START = 1; + private const int DRAW_MOVE = 2; + private const int DRAW_END = 3; + private const int rightBorder = 5; + private const int leftBorder = 2; + + private int BORDERSIZE = 0; + + + // + // SplitContainer private Cached copies of public properties... + // + private Orientation orientation = Orientation.Vertical; + private SplitterPanel panel1 = null; + private SplitterPanel panel2 = null; + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + private FixedPanel fixedPanel = FixedPanel.None; + + private int panel1MinSize = 25; //Panel1 Minimum Size + private int newPanel1MinSize = 25; //New panel1 Minimum Size used for ISupportInitialize + private int panel2MinSize = 25; //Panel2 Minimum Size + private int newPanel2MinSize = 25; //New panel2 Minimum Size used for ISupportInitialize + private bool tabStop = true; + // + //Fixed panel width or height; + // + private int panelSize; + + // + //SPLITTER PROPERTIES + // + private Rectangle splitterRect; + private int splitterInc = 1; + private bool splitterFixed; + private int splitterDistance = 50; //default splitter distance; + private int splitterWidth = 4; + private int newSplitterWidth = 4; //New splitter width used for ISupportInitialize + private int splitDistance = 50; + + // + //Properties used using DRAWING a MOVING SPLITTER + // + private int lastDrawSplit =1; + private int initialSplitterDistance; + private Rectangle initialSplitterRectangle; + private Point anchor = Point.Empty; + private bool splitBegin = false; + private bool splitMove = false; + private bool splitBreak = false; + + // + // Split Cursor + // + Cursor overrideCursor = null; + + // + //Needed For Tabbing + // + Control nextActiveControl = null; + private bool callBaseVersion; + private bool splitterFocused; + + // + //Required to keep track of Splitter movements.... + // + private bool splitterClick; + private bool splitterDrag; + + // + // FixedPanel.None require us + // to keep the Width/Height Ratio Depending on SplitContainer.Orientation + // + + double ratioWidth = 0.0f; + double ratioHeight = 0.0f; + bool resizeCalled = false; + bool splitContainerScaling = false; + bool setSplitterDistance = false; + + // + //Events + // + private static readonly object EVENT_MOVING = new object(); + private static readonly object EVENT_MOVED = new object(); + + // private IMessageFilter implementation + SplitContainerMessageFilter splitContainerMessageFilter = null; + + //This would avoid re-entrant code into SelectNextControl. + private bool selectNextControl = false; + + // Initialization flag for ISupportInitialize + private bool initializing = false; + + // + // Constructor + // + /// + public SplitContainer() + { + // either the left or top panel - LTR + // either the right or top panel - RTL + panel1 = new SplitterPanel(this); + // either the right or bottom panel - LTR + // either the left or bottom panel - RTL + panel2 = new SplitterPanel(this); + splitterRect = new Rectangle(); + + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + + ((WindowsFormsUtils.TypedControlCollection)this.Controls).AddInternal(panel1); + ((WindowsFormsUtils.TypedControlCollection)this.Controls).AddInternal(panel2); + UpdateSplitter(); + + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //PROPERTIES START IN ALPHABETICAL ORDER // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + + + + /// + /// + /// This property is overridden to allow the AutoScroll to be set on all the panels when + /// The autoScroll on SplitContainer is shown. + /// Here we dont set the base value ... but set autoscroll for panels. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + DefaultValue(false), + SRDescription(SR.FormAutoScrollDescr), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public override bool AutoScroll { + get { + //Always return false ... as Splitcontainer doesnt support AutoScroll + return false; + } + + set { + base.AutoScroll = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DefaultValue(typeof(Point), "0, 0") + ] + public override Point AutoScrollOffset { + get { + return base.AutoScrollOffset; + } + set { + base.AutoScrollOffset = value; + } + } + + /// + /// + /// Override AutoScrollMinSize to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Size AutoScrollMinSize { + get { + return base.AutoScrollMinSize; + } + set { + base.AutoScrollMinSize = value; + } + } + + + /// + /// + /// Override AutoScrollMargin to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Size AutoScrollMargin { + get { + return base.AutoScrollMargin; + } + set { + base.AutoScrollMargin = value; + } + } + + + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.FormAutoScrollPositionDescr) + ] + public new Point AutoScrollPosition { + get { + return base.AutoScrollPosition; + } + + set { + base.AutoScrollPosition = value; + } + } + + /// + /// Hide AutoSize, as it can mean more than one thing and might confuse users + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + + /// + /// + /// + /// + /// The binding manager for the container control. + /// + /// + [ + Browsable(false), + SRDescription(SR.ContainerControlBindingContextDescr), + SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly") + ] + public override BindingContext BindingContext { + get { + return BindingContextInternal; + } + set { + BindingContextInternal = value; + } + } + + /// + /// + /// Indicates what type of border the Splitter control has. This value + /// comes from the System.Windows.Forms.BorderStyle enumeration. + /// + [ + DefaultValue(BorderStyle.None), + SRCategory(SR.CatAppearance), + System.Runtime.InteropServices.DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.SplitterBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + Invalidate(); + SetInnerMostBorder(this); + if (this.ParentInternal != null) { + if (this.ParentInternal is SplitterPanel) { + SplitContainer sc = (SplitContainer)((SplitterPanel)this.ParentInternal).Owner; + sc.SetInnerMostBorder(sc); + } + } + } + + switch (BorderStyle) + { + case BorderStyle.None: + BORDERSIZE = 0; + break; + case BorderStyle.FixedSingle: + BORDERSIZE = 1; + break; + case BorderStyle.Fixed3D: + BORDERSIZE = 4; + break; + } + } + } + + /// + /// + /// Controls Collection... + /// This is overriden so that the Controls.Add ( ) is not Code Gened... + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Control.ControlCollection Controls { + get { + return base.Controls; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlAdded { + add { + base.ControlAdded += value; + } + remove { + base.ControlAdded -= value; + } + } + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlRemoved { + add { + base.ControlRemoved += value; + } + remove { + base.ControlRemoved -= value; + } + } + /// + /// + /// The dock property. The dock property controls to which edge + /// of the container this control is docked to. For example, when docked to + /// the top of the container, the control will be displayed flush at the + /// top of the container, extending the length of the container. + /// + public new DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + if (this.ParentInternal != null) { + if (this.ParentInternal is SplitterPanel) { + SplitContainer sc = (SplitContainer)((SplitterPanel)this.ParentInternal).Owner; + sc.SetInnerMostBorder(sc); + } + } + ResizeSplitContainer(); + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(150, 100); + } + } + + /// + /// + /// Indicates what type of border the Splitter control has. This value + /// comes from the System.Windows.Forms.BorderStyle enumeration. + /// + [ + DefaultValue(FixedPanel.None), + SRCategory(SR.CatLayout), + SRDescription(SR.SplitContainerFixedPanelDescr) + ] + public FixedPanel FixedPanel { + get { + return fixedPanel; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)FixedPanel.None, (int)FixedPanel.Panel2)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(FixedPanel)); + } + + if (fixedPanel != value) { + fixedPanel = value; + // UpdatePanelSize !! + switch (fixedPanel) { + case FixedPanel.Panel2: + if (Orientation == Orientation.Vertical) { + panelSize = Width - SplitterDistanceInternal - SplitterWidthInternal; + } + else { + panelSize = Height - SplitterDistanceInternal - SplitterWidthInternal; + } + break; + default: + panelSize = SplitterDistanceInternal; + break; + } + } + } + } + + /// + /// + /// This property determines whether the the splitter can move. + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(false), + Localizable(true), + SRDescription(SR.SplitContainerIsSplitterFixedDescr) + ] + + public bool IsSplitterFixed { + get { + return splitterFixed; + } + set { + splitterFixed = value; + } + } + + //Private property used to check whether the splitter can be moved by the user. + private bool IsSplitterMovable { + get { + if (Orientation == Orientation.Vertical) { + return (Width >= Panel1MinSize + SplitterWidthInternal + Panel2MinSize); + } + else { + return (Height >= Panel1MinSize + SplitterWidthInternal + Panel2MinSize); + } + + } + } + + // VsWhidbey 434959 : Refer to IsContainerControl property on Control for more details. + internal override bool IsContainerControl + { + get + { + return true; + } + } + + /// + /// + /// This Property sets or gets if the splitter is vertical or horizontal. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(Orientation.Vertical), + Localizable(true), + SRDescription(SR.SplitContainerOrientationDescr) + ] + public Orientation Orientation { + get { return orientation; } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)Orientation.Horizontal, (int)Orientation.Vertical)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(Orientation)); + } + if (orientation != value) { + orientation = value; + //update the splitterDistance to validate it w.r.t the new Orientation. + splitDistance = 0; + SplitterDistance = SplitterDistanceInternal; + UpdateSplitter(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + private Cursor OverrideCursor { + get { + return overrideCursor; + } + set { + if (overrideCursor != value) { + overrideCursor = value; + + if (IsHandleCreated) { + // We want to instantly change the cursor if the mouse is within our bounds. + NativeMethods.POINT p = new NativeMethods.POINT(); + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetCursorPos(p); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + if ((r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom) || UnsafeNativeMethods.GetCapture() == Handle) + SendMessage(NativeMethods.WM_SETCURSOR, Handle, NativeMethods.HTCLIENT); + } + } + } + } + + /// + /// Indicates if either panel is collapsed + /// + private bool CollapsedMode { + get { + return Panel1Collapsed || Panel2Collapsed; + } + } + + /// + /// + /// The Left or Top panel in the SplitContainer. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.SplitContainerPanel1Descr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public SplitterPanel Panel1 { + get { + return this.panel1; + } + } + + /// + /// Collapses or restores the given panel + /// + private void CollapsePanel(SplitterPanel p, bool collapsing) { + p.Collapsed = collapsing; + if (collapsing) { + p.Visible = false; + + } + else { + // restore panel + p.Visible = true; + } + UpdateSplitter(); + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// Collapses or restores panel1 + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(false), + SRDescription(SR.SplitContainerPanel1CollapsedDescr) + ] + public bool Panel1Collapsed { + get { + return panel1.Collapsed; + } + set { + if (value != panel1.Collapsed) { + if (value && panel2.Collapsed) { + CollapsePanel(panel2, false); + } + CollapsePanel(panel1, value); + } + } + } + + + /// + /// + /// Collapses or restores panel2 + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(false), + SRDescription(SR.SplitContainerPanel2CollapsedDescr) + ] + public bool Panel2Collapsed { + get { + return panel2.Collapsed; + } + set { + if (value != panel2.Collapsed) { + if (value && panel1.Collapsed) { + CollapsePanel(panel1, false); + } + CollapsePanel(panel2, value); + } + } + } + + /// + /// + /// This property determines the minimum distance of pixels of the splitter from the left or the top edge of Panel1. + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(25), + Localizable(true), + SRDescription(SR.SplitContainerPanel1MinSizeDescr), + RefreshProperties(RefreshProperties.All) + ] + public int Panel1MinSize { + get { + return panel1MinSize; + } + set { + newPanel1MinSize = value; + if (value != Panel1MinSize && !initializing) { + ApplyPanel1MinSize(value); + } + } + } + + /// + /// + /// This is the Right or Bottom panel in the SplitContainer. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.SplitContainerPanel2Descr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public SplitterPanel Panel2 { + get { + return this.panel2; + } + } + + /// + /// + /// This property determines the minimum distance of pixels of the splitter from the right or the bottom edge of Panel2 + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(25), + Localizable(true), + SRDescription(SR.SplitContainerPanel2MinSizeDescr), + RefreshProperties(RefreshProperties.All) + ] + public int Panel2MinSize { + get { + return panel2MinSize; + } + set { + newPanel2MinSize = value; + if (value != Panel2MinSize && !initializing) { + ApplyPanel2MinSize(value); + } + } + } + + /// + /// + /// This property determines pixel distance of the splitter from the left or top edge. + /// + [ + SRCategory(SR.CatLayout), + Localizable(true), + SettingsBindable(true), + SRDescription(SR.SplitContainerSplitterDistanceDescr), + DefaultValue(50) + ] + public int SplitterDistance + { + get + { + return splitDistance; + } + set + { + if (value != SplitterDistance) + { + if (value < 0) { + throw new ArgumentOutOfRangeException("SplitterDistance", SR.GetString(SR.InvalidLowBoundArgument, "SplitterDistance", (value).ToString(CultureInfo.CurrentCulture), "0")); + } + + + try + { + setSplitterDistance = true; + + if (Orientation == Orientation.Vertical) { + + if (value < Panel1MinSize) + { + value = Panel1MinSize; + } + if (value + SplitterWidthInternal > this.Width - Panel2MinSize) + { + value = this.Width - Panel2MinSize - SplitterWidthInternal; + } + if (value < 0 ) { + throw new InvalidOperationException(SR.GetString(SR.SplitterDistanceNotAllowed)); + } + splitDistance = value; + splitterDistance = value; + panel1.WidthInternal = SplitterDistance; + + } + else { + + if (value < Panel1MinSize) + { + value = Panel1MinSize; + } + + if (value + SplitterWidthInternal > this.Height - Panel2MinSize) + { + value = this.Height - Panel2MinSize - SplitterWidthInternal; + } + if (value < 0 ) { + throw new InvalidOperationException(SR.GetString(SR.SplitterDistanceNotAllowed)); + } + splitDistance = value; + splitterDistance = value; + panel1.HeightInternal = SplitterDistance; + } + + switch (fixedPanel) { + case FixedPanel.Panel1: + panelSize = SplitterDistance; + break; + case FixedPanel.Panel2: + if (Orientation == Orientation.Vertical) { + panelSize = Width - SplitterDistance - SplitterWidthInternal; + } + else { + panelSize = Height - SplitterDistance - SplitterWidthInternal; + } + break; + } + UpdateSplitter(); + } + finally + { + setSplitterDistance = false; + } + OnSplitterMoved(new SplitterEventArgs(SplitterRectangle.X + SplitterRectangle.Width/2, SplitterRectangle.Y + SplitterRectangle.Height/2, SplitterRectangle.X, SplitterRectangle.Y)); + } + } + } + + private int SplitterDistanceInternal { + + get { + return splitterDistance; + } + set { + SplitterDistance = value; + } + } + + /// + /// + /// This determines the number of pixels the splitter moves in increments.This is defaulted to 1. + /// + [ + SRCategory(SR.CatLayout), + DefaultValue(1), + Localizable(true), + SRDescription(SR.SplitContainerSplitterIncrementDescr) + ] + public int SplitterIncrement { + get { + return splitterInc; + } + set { + + if (value < 1 ) { + throw new ArgumentOutOfRangeException("SplitterIncrement", SR.GetString(SR.InvalidLowBoundArgumentEx, "SplitterIncrement", (value).ToString(CultureInfo.CurrentCulture), "1")); + } + + splitterInc = value; + } + } + + + /// + /// + /// This property determines the rectangle bounds of the splitter. + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.SplitContainerSplitterRectangleDescr), + Browsable(false) + ] + public Rectangle SplitterRectangle { + get { + Rectangle r = splitterRect; + r.X = splitterRect.X - Left; + r.Y = splitterRect.Y - Top; + return r; + } + } + + /// + /// + /// This property determines the thickness of the splitter. + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.SplitContainerSplitterWidthDescr), + Localizable(true), + DefaultValue(4) + ] + public int SplitterWidth { + get { + return splitterWidth; + } + set { + newSplitterWidth = value; + if (value != SplitterWidth && !initializing) { + ApplySplitterWidth(value); + } + } + } + + /// + /// We need to have a internal Property for the SplitterWidth which returns zero if we are in collapased mode. + /// This property is used to Layout SplitContainer. + /// + private int SplitterWidthInternal { + get { + // if CollapsedMode then splitterwidth == 0; + return (CollapsedMode) ? 0 : splitterWidth; + } + } + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public new bool TabStop { + get { + return tabStop; + } + set { + if (TabStop != value) { + tabStop = value; + OnTabStopChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //END PROPERTIES // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //Start PUBLIC FUNCTIONS // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + /// ISupportInitialize support. Disables splitter panel min size and splitter width + /// validation during initialization. + /// + public void BeginInit() { + initializing = true; + } + + /// + /// + /// ISupportInitialize support. Enables splitter panel min size and splitter width + /// validation after initialization. + /// + public void EndInit() { + initializing = false; + + // validate and apply new value + if (newPanel1MinSize != panel1MinSize) { + ApplyPanel1MinSize(newPanel1MinSize); + } + if (newPanel2MinSize != panel2MinSize) { + ApplyPanel2MinSize(newPanel2MinSize); + } + if (newSplitterWidth != splitterWidth) { + ApplySplitterWidth(newSplitterWidth); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //End PUBLIC FUNCTIONS // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //Start EVENT HANDLERS // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovingDescr)] + public event SplitterCancelEventHandler SplitterMoving { + add { + Events.AddHandler(EVENT_MOVING, value); + } + remove { + Events.RemoveHandler(EVENT_MOVING, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovedDescr)] + public event SplitterEventHandler SplitterMoved { + add { + Events.AddHandler(EVENT_MOVED, value); + } + remove { + Events.RemoveHandler(EVENT_MOVED, value); + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //End EVENT HANDLERS // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // // + //start EVENT Delegates // + // // + ///////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + /// Overides the Control.OnGotFocus to Invalidate... + /// + protected override void OnGotFocus(EventArgs e) { + base.OnGotFocus(e); + Invalidate(); + + } + + /// + /// + /// Overrides the Control.OnKeydown for implementing splitter movements. + /// + protected override void OnKeyDown(KeyEventArgs e) { + Debug.Assert(Enabled, "SplitContainer.OnKeyDown should not be called if the button is disabled"); + base.OnKeyDown(e); + //If the Panel1MinSize + Panel2MinSize < SplitContainer.Size then carry on the splitter move... + if (IsSplitterMovable && !IsSplitterFixed) { + if (e.KeyData == Keys.Escape && splitBegin) { + splitBegin = false; + splitBreak = true; + return; + } + //valid Keys that move the splitter... + if (e.KeyData == Keys.Right || e.KeyData == Keys.Down || + e.KeyData == Keys.Left || e.KeyData == Keys.Up + && splitterFocused) { + if (splitBegin) { + splitMove = true; + } + + //left OR up + if (e.KeyData == Keys.Left || e.KeyData == Keys.Up && splitterFocused) { + splitterDistance -= SplitterIncrement; + splitterDistance = (splitterDistance < Panel1MinSize) ? splitterDistance + SplitterIncrement : Math.Max(splitterDistance, BORDERSIZE); + } + //right OR down + if (e.KeyData == Keys.Right || e.KeyData == Keys.Down && splitterFocused) { + splitterDistance += SplitterIncrement; + if (Orientation == Orientation.Vertical) { + splitterDistance = (splitterDistance + SplitterWidth > Width - Panel2MinSize -BORDERSIZE) ? splitterDistance - SplitterIncrement : splitterDistance; + } + else { + splitterDistance = (splitterDistance + SplitterWidth > Height - Panel2MinSize - BORDERSIZE) ? splitterDistance - SplitterIncrement : splitterDistance; + } + + } + + if (!splitBegin) { + splitBegin = true; + } + //draw Helper start + if (splitBegin && !splitMove) { + initialSplitterDistance = SplitterDistanceInternal; + DrawSplitBar(DRAW_START); + } + else { //draw helper move + DrawSplitBar(DRAW_MOVE); + //Moving by mouse .....gives the origin of the splitter.. + // + Rectangle r = CalcSplitLine(splitterDistance, 0); + int xSplit = r.X; + int ySplit = r.Y; + SplitterCancelEventArgs se = new SplitterCancelEventArgs(this.Left + SplitterRectangle.X + SplitterRectangle.Width/2, this.Top + SplitterRectangle.Y + SplitterRectangle.Height/2, xSplit, ySplit); + OnSplitterMoving(se); + if (se.Cancel) { + SplitEnd(false); + } + } + } //End Valid Keys.... + } //End SplitterFixed Check... + } + + /// + /// + /// Overrides the Control.OnKeydown for implementing splitter movements. + /// + protected override void OnKeyUp(KeyEventArgs e) { + base.OnKeyUp(e); + if (splitBegin && IsSplitterMovable) { + if (e.KeyData == Keys.Right || e.KeyData == Keys.Down || + e.KeyData == Keys.Left || e.KeyData == Keys.Up + && splitterFocused) { + DrawSplitBar(DRAW_END); + ApplySplitterDistance(); + splitBegin = false; + splitMove = false; + } + } + if (splitBreak) { + splitBreak = false; + SplitEnd(false); + } + //problem with the Focus rect after Keyup .... + //Focus rect and reverible lines leave a trace behind on the splitter... + using (Graphics g = CreateGraphicsInternal()) { + if (BackgroundImage == null) { + using (SolidBrush brush = new SolidBrush(this.BackColor)) { + g.FillRectangle(brush, SplitterRectangle); + } + } + DrawFocus(g, SplitterRectangle); + } + + } + + /// + /// + /// Overrides the Control.OnLayout. + /// + protected override void OnLayout(LayoutEventArgs e) { + SetInnerMostBorder(this); + + if (IsSplitterMovable && !setSplitterDistance) { + ResizeSplitContainer(); + } + base.OnLayout(e); + } + + /// + /// + /// Overrides the Control.OnLostFocus to Invalidate. + /// + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + Invalidate(); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + if (!IsSplitterFixed && IsSplitterMovable) { + + //change cursor if default and user hasnt changed the cursor. + if (Cursor == DefaultCursor && SplitterRectangle.Contains(e.Location)) + { + if (Orientation == Orientation.Vertical) { + OverrideCursor = Cursors.VSplit; + } + else { + OverrideCursor = Cursors.HSplit; + } + } + else { + OverrideCursor = null;; + } + + if (splitterClick) { + int x = e.X ; + int y = e.Y ; + splitterDrag = true; + SplitMove(x, y); + if (Orientation == Orientation.Vertical) { + x = Math.Max(Math.Min(x, Width - Panel2MinSize), Panel1MinSize); + y = Math.Max(y, 0); + } + else { + y = Math.Max(Math.Min(y, Height - Panel2MinSize), Panel1MinSize); + x = Math.Max(x, 0); + } + Rectangle r = CalcSplitLine(GetSplitterDistance(e.X, e.Y), 0); + int xSplit = r.X; + int ySplit = r.Y; + SplitterCancelEventArgs se = new SplitterCancelEventArgs(x, y, xSplit, ySplit); + OnSplitterMoving(se); + if (se.Cancel) { + SplitEnd(false); + + } + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + if (!Enabled) { + return; + } + OverrideCursor = null; + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown(e); + //If the Panel1MinSize + Panel2MinSize < SplitContainer.Size then carry on the splitter move... + if (IsSplitterMovable && SplitterRectangle.Contains(e.Location)) { + if (!Enabled) { + return; + } + if (e.Button == MouseButtons.Left && e.Clicks == 1 && !IsSplitterFixed) { + // Focus the current splitter OnMouseDown. + splitterFocused = true; + IContainerControl c = this.ParentInternal.GetContainerControlInternal(); + if (c != null) { + ContainerControl cc = c as ContainerControl; + if (cc == null) { + c.ActiveControl = this; + } + else { + cc.SetActiveControlInternal(this); + } + } + SetActiveControlInternal(null); + nextActiveControl = panel2; + + SplitBegin(e.X, e.Y); + splitterClick = true; + } + } + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseUp(MouseEventArgs e) { + base.OnMouseUp(e); + if (!Enabled) { + return; + } + if (!IsSplitterFixed && IsSplitterMovable && splitterClick) { + CaptureInternal = false; + + if (splitterDrag) { + CalcSplitLine(GetSplitterDistance(e.X, e.Y), 0); + SplitEnd(true); + } + else { + SplitEnd(false); + + } + splitterClick = false; + splitterDrag = false; + } + } + + /// + /// Overrides the Control.OnMove() to synchronize the + /// splitterRect with the position of the SplitContainer. + /// + protected override void OnMove(EventArgs e) + { + base.OnMove(e); + SetSplitterRect(this.Orientation == Orientation.Vertical); + } + + + /// + /// + /// Overrides the Control.OnPaint() to focus the Splitter. + /// + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + if (Focused) { + DrawFocus(e.Graphics,SplitterRectangle); + } + } + + /// + /// + /// Inherriting classes should override this method to respond to the + /// splitterMoving event. This event occurs while the splitter is + /// being moved by the user. + /// + public void OnSplitterMoving(SplitterCancelEventArgs e) { + SplitterCancelEventHandler handler = (SplitterCancelEventHandler)Events[EVENT_MOVING]; + if (handler != null) handler(this, e); + + } + + /// + /// + /// Inherriting classes should override this method to respond to the + /// splitterMoved event. This event occurs when the user finishes + /// moving the splitter. + /// + public void OnSplitterMoved(SplitterEventArgs e) { + SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVED]; + if (handler != null) handler(this, e); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // // + ///END DELEGATES // + // // + //////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + // pass the RightToLeft value to the Parent. + this.panel1.RightToLeft = this.RightToLeft; + this.panel2.RightToLeft = this.RightToLeft; + UpdateSplitter(); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // // + ///START PRIVATE FUNCTIONS // + // // + //////////////////////////////////////////////////////////////////////////////////////////////// + + + /// + /// Validate and set the minimum size for Panel1. + /// + private void ApplyPanel1MinSize(int value) { + if (value < 0) { + throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidLowBoundArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture), "0")); + } + + if (Orientation== Orientation.Vertical) { + if (DesignMode && Width != DefaultSize.Width && value + Panel2MinSize + SplitterWidth > Width) { + throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture))); + } + } + else if (Orientation == Orientation.Horizontal) { + if (DesignMode && Height != DefaultSize.Height && value + Panel2MinSize + SplitterWidth > Height) { + throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture))); + } + } + + panel1MinSize = value; + if (value > SplitterDistanceInternal) { + SplitterDistanceInternal = value; //Set the Splitter Distance to the end of Panel1 + } + } + + /// + /// Validate and set the minimum size for Panel2. + /// + private void ApplyPanel2MinSize(int value) { + if (value < 0) { + throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidLowBoundArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture), "0")); + } + if (Orientation == Orientation.Vertical) { + if (DesignMode && Width != DefaultSize.Width && value + Panel1MinSize + SplitterWidth > Width) { + throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture))); + } + + } + else if (Orientation == Orientation.Horizontal) { + if (DesignMode && Height != DefaultSize.Height && value + Panel1MinSize + SplitterWidth > Height) { + throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture))); + } + } + panel2MinSize = value; + if (value > Panel2.Width) { + SplitterDistanceInternal = Panel2.Width + SplitterWidthInternal; //Set the Splitter Distance to the start of Panel2 + } + } + + /// + /// Validate and set the splitter width. + /// + private void ApplySplitterWidth(int value) { + if (value < 1) { + throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture), "1")); + } + if (Orientation == Orientation.Vertical) { + if (DesignMode && value + Panel1MinSize + Panel2MinSize > Width) { + throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidArgument, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture))); + } + + } + else if (Orientation == Orientation.Horizontal) { + if (DesignMode && value + Panel1MinSize + Panel2MinSize > Height) { + throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidArgument, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture))); + } + } + splitterWidth = value; + UpdateSplitter(); + } + + /// + /// + /// Sets the split position to be the current split size. This is called + /// by splitEdit + /// + /// + private void ApplySplitterDistance() { + + using (new System.Windows.Forms.Layout.LayoutTransaction(this, this, "SplitterDistance", false)) { + SplitterDistanceInternal = splitterDistance; + } + + // Refer to VsWhidbey : 467815: We need to invalidate when we have transparent backgournd. + if (this.BackColor == Color.Transparent) { + // the panel1 retains the focus rect... so Invalidate the rect ... + Invalidate(); + } + + if (Orientation == Orientation.Vertical) { + + if (RightToLeft == RightToLeft.No) { + splitterRect.X = this.Location.X + SplitterDistanceInternal; + } + else { + splitterRect.X = this.Right - SplitterDistanceInternal - SplitterWidthInternal; + } + } + else { + splitterRect.Y = this.Location.Y + SplitterDistanceInternal; + } + } + + /// + /// + /// Calculates the bounding rect of the split line. minWeight refers + /// to the minimum height or width of the splitline. + /// + private Rectangle CalcSplitLine(int splitSize, int minWeight) { + + Rectangle r = new Rectangle(); + switch (Orientation) { + case Orientation.Vertical: + r.Width = SplitterWidthInternal ; + r.Height = Height; + if (r.Width < minWeight) { + r.Width = minWeight; + } + + if (RightToLeft == RightToLeft.No) { + r.X = panel1.Location.X + splitSize; + } + else { + r.X = Width - splitSize - SplitterWidthInternal; + } + + break; + + + case Orientation.Horizontal: + r.Width = Width; + r.Height = SplitterWidthInternal; + if (r.Width < minWeight) { + r.Width = minWeight; + } + r.Y = panel1.Location.Y + splitSize; + break; + } + return r; + } + + /// + /// + /// Draws the splitter bar at the current location. Will automatically + /// cleanup anyplace the splitter was drawn previously. + /// + /// + private void DrawSplitBar(int mode) { + if (mode != DRAW_START && lastDrawSplit != -1) { + DrawSplitHelper(lastDrawSplit); + lastDrawSplit = -1; + } + // Bail if drawing with no old point... + // + else if (mode != DRAW_START && lastDrawSplit == -1) { + return; + } + + if (mode != DRAW_END) { + if (splitMove || splitBegin) { // Splitter is moved by keys and not by mouse + DrawSplitHelper(splitterDistance); + lastDrawSplit = splitterDistance; + } + else { + DrawSplitHelper(splitterDistance); + lastDrawSplit = splitterDistance; + } + + } + else { + if (lastDrawSplit != -1) { + DrawSplitHelper(lastDrawSplit); + } + lastDrawSplit = -1; + } + } + + /// + /// + /// Draws the focus rectangle if the control has focus. + /// + /// + /// + private void DrawFocus(Graphics g, Rectangle r) { + r.Inflate (-1, -1); + ControlPaint.DrawFocusRectangle(g, r, this.ForeColor, this.BackColor); + } + + /// + /// + /// Draws the splitter line at the requested location. Should only be called + /// by drawSpltBar. + /// + /// + private void DrawSplitHelper(int splitSize) { + + Rectangle r = CalcSplitLine(splitSize, 3); + IntPtr parentHandle = this.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone)); + SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT); + SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(null, dc)); + } + + /// + /// + /// Calculates the split size based on the mouse position (x, y). + /// + /// + private int GetSplitterDistance(int x, int y) { + int delta; + if (Orientation == Orientation.Vertical) { + delta = x - anchor.X; + } + else { + delta = y - anchor.Y; + } + + // Negative delta - moving to the left + // Positive delta - moving to the right + + int size = 0; + switch (Orientation) { + case Orientation.Vertical: + if (RightToLeft == RightToLeft.No) { + size = Math.Max(panel1.Width + delta, BORDERSIZE); + } + else { + // In RTL negative delta actually means increasing the size.... + size = Math.Max(panel1.Width - delta, BORDERSIZE); + } + break; + case Orientation.Horizontal: + size = Math.Max(panel1.Height + delta, BORDERSIZE); + break; + } + if (Orientation == Orientation.Vertical) { + return Math.Max(Math.Min(size, Width - Panel2MinSize), Panel1MinSize); + } + else { + return Math.Max(Math.Min(size, Height - Panel2MinSize), Panel1MinSize); + } + } + + /// + /// Process an arrowKey press by selecting the next control in the group + /// that the activeControl belongs to. + /// + /// + private bool ProcessArrowKey(bool forward) { + Control group = this; + if (ActiveControl != null) { + group = ActiveControl.ParentInternal; + } + return group.SelectNextControl(ActiveControl, forward, false, false, true); + } + + /// + /// + /// Re paint SplitterRect for SplitContainer + /// + /// + private void RepaintSplitterRect() + { + if (IsHandleCreated) { + Graphics g = this.CreateGraphicsInternal(); + if (BackgroundImage != null) { + + using (TextureBrush textureBrush = new TextureBrush(BackgroundImage,WrapMode.Tile)) { + g.FillRectangle(textureBrush, ClientRectangle); + } + } + else{ + using (SolidBrush solidBrush = new SolidBrush(this.BackColor)) { + g.FillRectangle(solidBrush, splitterRect); + } + } + g.Dispose(); + } + } + + + private void SetSplitterRect(bool vertical) { + if (vertical) + { + splitterRect.X = ((RightToLeft == RightToLeft.Yes) ? this.Width - splitterDistance - SplitterWidthInternal : this.Location.X + splitterDistance); + splitterRect.Y = this.Location.Y; + splitterRect.Width = SplitterWidthInternal; + splitterRect.Height = this.Height; + } + else + { + splitterRect.X = this.Location.X; + splitterRect.Y = this.Location.Y + SplitterDistanceInternal; + splitterRect.Width = this.Width; + splitterRect.Height = SplitterWidthInternal; + } + } + + /// + /// + /// Reize SplitContainer + /// + /// + private void ResizeSplitContainer() + { + if (splitContainerScaling) + { + return; + } + + panel1.SuspendLayout(); + panel2.SuspendLayout(); + + if (this.Width == 0) { // Set the correct Width iif the WIDTH has changed to ZERO. + panel1.Size = new Size(0, panel1.Height); + panel2.Size = new Size(0, panel2.Height); + } + else if (this.Height == 0) { // Set the correct Height iif the HEIGHT has changed to ZERO. + panel1.Size = new Size(panel1.Width, 0); + panel2.Size = new Size(panel2.Width, 0); + } + else + { + if (Orientation == Orientation.Vertical) + { + // If no panel is collapsed then do the default ... + if (!CollapsedMode) + { + if (this.FixedPanel == FixedPanel.Panel1) { + panel1.Size = new Size(panelSize, Height); + panel2.Size = new Size(Math.Max(Width - panelSize - SplitterWidthInternal, Panel2MinSize), Height); + } + if (this.FixedPanel == FixedPanel.Panel2) { + panel2.Size = new Size(panelSize, Height); + splitterDistance = Math.Max(Width - panelSize - SplitterWidthInternal, Panel1MinSize); + panel1.WidthInternal = splitterDistance; + panel1.HeightInternal = Height; + } + if (this.FixedPanel == FixedPanel.None) { + if (ratioWidth != 0.0) { + splitterDistance = Math.Max((int)(Math.Floor(this.Width / ratioWidth)), Panel1MinSize); + } + panel1.WidthInternal = splitterDistance; //Default splitter distance from left or top. + panel1.HeightInternal = Height; + panel2.Size = new Size(Math.Max(Width - splitterDistance - SplitterWidthInternal, Panel2MinSize), Height); + } + if (RightToLeft == RightToLeft.No) { + panel2.Location = new Point(panel1.WidthInternal + SplitterWidthInternal, 0); + } + else { + panel1.Location = new Point(Width - panel1.WidthInternal, 0); + } + RepaintSplitterRect(); + SetSplitterRect(true); + } + else + { + if (Panel1Collapsed) { + panel2.Size = this.Size; + panel2.Location = new Point(0,0); + } + else if (Panel2Collapsed) { + panel1.Size = this.Size; + panel1.Location = new Point(0,0); + } + } + + } + else if (Orientation == Orientation.Horizontal) { + // If no panel is collapsed then do the default ... + if (!CollapsedMode) + { + if (this.FixedPanel == FixedPanel.Panel1) { + + //Default splitter distance from left or top. + panel1.Size = new Size(Width, panelSize); + int panel2Start = panelSize + SplitterWidthInternal; + panel2.Size = new Size(Width, Math.Max(Height - panel2Start, Panel2MinSize)); + panel2.Location = new Point(0,panel2Start); + } + if (this.FixedPanel == FixedPanel.Panel2) { + + panel2.Size = new Size(Width, panelSize); + splitterDistance = Math.Max(Height - Panel2.Height - SplitterWidthInternal, Panel1MinSize); + panel1.HeightInternal = splitterDistance; + panel1.WidthInternal = Width; + int panel2Start = splitterDistance + SplitterWidthInternal; + panel2.Location = new Point(0, panel2Start); + } + if (this.FixedPanel == FixedPanel.None) { + //NO PANEL FIXED !! + if (ratioHeight != 0.0) + { + splitterDistance = Math.Max((int)(Math.Floor(this.Height / ratioHeight )), Panel1MinSize); + } + panel1.HeightInternal = splitterDistance; //Default splitter distance from left or top. + panel1.WidthInternal = Width; + int panel2Start = splitterDistance + SplitterWidthInternal; + panel2.Size = new Size(Width,Math.Max(Height - panel2Start, Panel2MinSize)); + panel2.Location = new Point(0,panel2Start); + + + } + RepaintSplitterRect(); + SetSplitterRect(false); + } + else + { + if (Panel1Collapsed) { + panel2.Size = this.Size; + panel2.Location = new Point(0,0); + } + else if (Panel2Collapsed) { + panel1.Size = this.Size; + panel1.Location = new Point(0,0); + } + } + } + try { + resizeCalled = true; + ApplySplitterDistance(); + } + finally { + resizeCalled = false; + } + } + panel1.ResumeLayout(); + panel2.ResumeLayout(); + } + + + /// + /// Scales an individual control's location, size, padding and margin. + /// If the control is top level, this will not scale the control's location. + /// This does not scale children or the size of auto sized controls. You can + /// omit scaling in any direction by changing BoundsSpecified. + /// + /// After the control is scaled the RequiredScaling property is set to + /// BoundsSpecified.None. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + try + { + splitContainerScaling = true; + base.ScaleControl(factor, specified); + + float scale; + if (orientation == Orientation.Vertical) { + scale = factor.Width; + } + else { + scale = factor.Height; + } + SplitterWidth = (int)Math.Round((float)SplitterWidth * scale); + } + finally + { + splitContainerScaling = false; + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void Select(bool directed, bool forward) { + // avoid re-entrant code. + // SelectNextControl can call back on us.. and we might end up infinitely recursing. + if (selectNextControl) + { + return; + } + // continue selection iff panels have controls or tabstop is true. + if ((this.Panel1.Controls.Count > 0 || this.Panel2.Controls.Count > 0) || TabStop) { + SelectNextControlInContainer(this, forward, true, true, false); + } + else { //If this SplitContainer cannot be selected let the parent select the next in line + try { + Control parent = this.ParentInternal; + selectNextControl = true; + while (parent != null) { + if (parent.SelectNextControl(this, forward, true, true, parent.ParentInternal == null)) { + break; + } + parent = parent.ParentInternal; + } + } + finally { + selectNextControl = false; + } + } + } + + /// + /// + /// Selects the next control following ctl. + /// + private bool SelectNextControlInContainer(Control ctl, bool forward, bool tabStopOnly, + bool nested, bool wrap) { + if (!Contains(ctl) || !nested && ctl.ParentInternal != this) ctl = null; + Control start = ctl; + SplitterPanel firstPanel = null; + do { + ctl = GetNextControl(ctl, forward); + + SplitterPanel panel = ctl as SplitterPanel; + if (panel != null && panel.Visible) { + //We have crossed over to the second Panel... + if (firstPanel != null) { + break; + } + firstPanel = panel; + } + if (!forward && firstPanel != null && ctl.ParentInternal != firstPanel) { + //goback to start correct re-ordering .... + ctl = firstPanel; + break; + } + if (ctl == null) { + break; + } + else { + if (ctl.CanSelect && ctl.TabStop) { + if (ctl is SplitContainer) + { + ((SplitContainer)ctl).Select(forward, forward); + } + else + { + SelectNextActiveControl(ctl, forward, tabStopOnly, nested, wrap); + } + return true; + } + } + } while (ctl != null); + if (ctl != null && this.TabStop) { + //we are on Splitter.....Focus it + splitterFocused = true; + IContainerControl c = this.ParentInternal.GetContainerControlInternal(); + if (c != null) { + ContainerControl cc = c as ContainerControl; + if (cc == null) { + c.ActiveControl = this; + } + else { + IntSecurity.ModifyFocus.Demand(); + cc.SetActiveControlInternal(this); + } + } + SetActiveControlInternal(null); + nextActiveControl = ctl; + return true; + } + else + { + // If the splitter cannot be selected select the next control in the splitter + bool selected = SelectNextControlInPanel(ctl, forward, tabStopOnly, nested, wrap); + if (!selected) + { + Control parent = this.ParentInternal; + if (parent != null) + { + try + { + selectNextControl = true; + parent.SelectNextControl(this, forward, true, true, true); + } + finally + { + selectNextControl = false; + } + } + } + } + return false; + } + + /// + /// + /// Selects the next control following ctl. + /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + private bool SelectNextControlInPanel(Control ctl, bool forward, bool tabStopOnly, + bool nested, bool wrap) { + + if (!Contains(ctl) || !nested && ctl.ParentInternal != this) ctl = null; + Control start = ctl; + do { + ctl = GetNextControl(ctl, forward); + if (ctl == null || (ctl is SplitterPanel && ctl.Visible)) { + break; + } + else { + if (ctl.CanSelect && (!tabStopOnly || ctl.TabStop)) { + if (ctl is SplitContainer) + { + ((SplitContainer)ctl).Select(forward, forward); + } + else + { + SelectNextActiveControl(ctl, forward, tabStopOnly, nested, wrap); + } + return true; + } + } + } while (ctl != null); + + //If CTL == null .. we r out of the Current SplitContainer... + if (ctl == null || (ctl is SplitterPanel && !ctl.Visible)) { + callBaseVersion = true; + + } + //IF the CTL == typeof(SpliterPanel) find the NEXT Control... so that we know + // we can focus the NEXT control within this SPLITCONTAINER.... + else + { + ctl = GetNextControl(ctl, forward); + if (forward) { + nextActiveControl = panel2; + } + else { + if (ctl == null || !(ctl.ParentInternal.Visible)) { + callBaseVersion = true; + } + else + nextActiveControl = panel2; + } + } + return false; + } + + // This will select the correct active control in the containerControl (if the passed in control is a containerControl) + private static void SelectNextActiveControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap) + { + ContainerControl container = ctl as ContainerControl; + if (container != null) + { + bool correctParentActiveControl = true; + if (container.ParentInternal != null) + { + IContainerControl c = container.ParentInternal.GetContainerControlInternal(); + if (c != null) + { + c.ActiveControl = container; + correctParentActiveControl = (c.ActiveControl == container); + } + } + if (correctParentActiveControl) + { + ctl.SelectNextControl(null, forward, tabStopOnly, nested, wrap); + } + } + else + { + ctl.Select(); + } + } + + /// + /// + /// Selects the innermost PANEL. + /// + private void SetInnerMostBorder(SplitContainer sc) { + foreach(Control ctl in sc.Controls) { + bool foundChildSplitContainer = false; + if (ctl is SplitterPanel) { + foreach (Control c in ctl.Controls) { + SplitContainer c1 = c as SplitContainer; + if (c1 != null && c1.Dock == DockStyle.Fill) { + // We need to Overlay borders + // if the Children have matching BorderStyles ... + if (c1.BorderStyle != BorderStyle) { + break; + } + ((SplitterPanel)ctl).BorderStyle = BorderStyle.None; + SetInnerMostBorder(c1); + foundChildSplitContainer = true; + } + } + if (!foundChildSplitContainer) { + ((SplitterPanel)ctl).BorderStyle = BorderStyle; + } + } + } + } + + + /// + /// + /// This protected override allows us to check is an unvalid value is set for Width and Height. + /// The SplitContainer would not throw on invalid Size (i.e Width and Height) settings, but would correct the error like Form + /// Say, the Panel1MinSize == 150 , Panel2MinSize == 50 and SplitterWidth == 4 and the user tries + /// to set SplitContainer.Width = 50 ... then this function would try to correct the value to 204.. instead of throwing. + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + // If we are changing Height, check if its greater than minimun else ... make it equal to the minimum + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None && Orientation == Orientation.Horizontal) { + if (height < Panel1MinSize + SplitterWidthInternal + Panel2MinSize) + { + height = Panel1MinSize + SplitterWidthInternal + Panel2MinSize; + } + } + + // If we are changing Width, check if its greater than minimun else ... make it equal to the minimum + if ((specified & BoundsSpecified.Width) != BoundsSpecified.None && Orientation == Orientation.Vertical) { + if (width < Panel1MinSize + SplitterWidthInternal + Panel2MinSize) + { + width = Panel1MinSize + SplitterWidthInternal + Panel2MinSize; + } + } + + base.SetBoundsCore(x, y, width, height, specified); + + SetSplitterRect(this.Orientation == Orientation.Vertical); + } + + /// + /// + /// Begins the splitter moving. + /// + /// + private void SplitBegin(int x, int y) { + anchor = new Point(x, y); + splitterDistance = GetSplitterDistance(x, y); + initialSplitterDistance = splitterDistance; + initialSplitterRectangle = SplitterRectangle; + + // SECREVIEW : We need a message filter to capture the ESC key + // to cancel the split action. + // The method PreFilterMessage is adorned with a LinkDemand. + // But this is not enough since this is a public unsealed class. + // We should have private implementation of the IMessageFilter + // So that we dont expose this to the classed deriving from this public class + // Refer to VsWhidbey : 423553 for more information. + IntSecurity.UnmanagedCode.Assert(); + try { + if (splitContainerMessageFilter == null) + { + splitContainerMessageFilter = new SplitContainerMessageFilter(this); + } + Application.AddMessageFilter(splitContainerMessageFilter); + } + finally { + CodeAccessPermission.RevertAssert(); + } + CaptureInternal = true; + DrawSplitBar(DRAW_START); + } + + /// + /// + /// The split movement. + /// + /// + private void SplitMove(int x, int y) { + int size = GetSplitterDistance(x, y); + int delta = size - initialSplitterDistance; + int mod = delta % SplitterIncrement; + if (splitterDistance != size) { + if (Orientation == Orientation.Vertical) + { + if (size + SplitterWidthInternal <= this.Width - Panel2MinSize - BORDERSIZE) + { + splitterDistance = size - mod; + } + } + else + { + if (size + SplitterWidthInternal <= this.Height - Panel2MinSize - BORDERSIZE) + { + splitterDistance = size - mod; + } + } + } + DrawSplitBar(DRAW_MOVE); + } + + /// + /// + /// Finishes the split movement. + /// + /// + private void SplitEnd(bool accept) { + DrawSplitBar(DRAW_END); + if (splitContainerMessageFilter != null) + { + Application.RemoveMessageFilter(splitContainerMessageFilter); + splitContainerMessageFilter = null; + } + + if (accept) { + ApplySplitterDistance(); + + } + else if (splitterDistance != initialSplitterDistance) { + splitterClick = false; + splitterDistance = SplitterDistanceInternal = initialSplitterDistance; + } + anchor = Point.Empty; + + } + + /// + /// + /// Update Splitter + /// + /// + private void UpdateSplitter() { + if (splitContainerScaling) + { + return; + } + panel1.SuspendLayout(); + panel2.SuspendLayout(); + if (Orientation == Orientation.Vertical) { + bool isRTL = RightToLeft == RightToLeft.Yes; + + //NO PANEL FIXED !! + if (!CollapsedMode) { + + panel1.HeightInternal = Height; + panel1.WidthInternal = splitterDistance; //Default splitter distance from left or top. + panel2.Size = new Size(Width - splitterDistance - SplitterWidthInternal, Height); + + if (!isRTL) { + panel1.Location = new Point(0,0); + panel2.Location = new Point(splitterDistance + SplitterWidthInternal, 0); + } + else { + panel1.Location = new Point(Width - splitterDistance, 0); + panel2.Location = new Point(0, 0); + } + + RepaintSplitterRect(); + SetSplitterRect(true /*Vertical*/); + if (!resizeCalled) { + ratioWidth = ((double)(this.Width) / (double)(panel1.Width) > 0) ? (double)(this.Width) / (double)(panel1.Width) : ratioWidth; + } + + + } + else { + if (Panel1Collapsed) { + panel2.Size = this.Size; + panel2.Location = new Point(0,0); + } + else if (Panel2Collapsed) { + panel1.Size = this.Size; + panel1.Location = new Point(0,0); + } + // Update Ratio when the splitContainer is in CollapsedMode. + if (!resizeCalled) + { + ratioWidth = ((double)(this.Width) / (double)(splitterDistance) > 0) ? (double)(this.Width) / (double)(splitterDistance) : ratioWidth; + } + } + } + else { + //NO PANEL FIXED !! + if (!CollapsedMode) { + panel1.Location = new Point(0,0); + panel1.WidthInternal = Width; + + panel1.HeightInternal = SplitterDistanceInternal; //Default splitter distance from left or top. + int panel2Start = splitterDistance + SplitterWidthInternal; + panel2.Size = new Size(Width, Height - panel2Start); + panel2.Location = new Point(0,panel2Start); + + RepaintSplitterRect(); + SetSplitterRect(false/*Horizontal*/); + + if (!resizeCalled) { + ratioHeight = ((double)(this.Height) / (double)(panel1.Height) > 0) ? (double)(this.Height) / (double)(panel1.Height) : ratioHeight; + } + } + else { + if (Panel1Collapsed) { + panel2.Size = this.Size; + panel2.Location = new Point(0,0); + } + else if (Panel2Collapsed) { + panel1.Size = this.Size; + panel1.Location = new Point(0,0); + } + + // Update Ratio when the splitContainer is in CollapsedMode. + if (!resizeCalled) + { + ratioHeight = ((double)(this.Height) / (double)(splitterDistance) > 0) ? (double)(this.Height) / (double)(splitterDistance) : ratioHeight; + } + } + } + panel1.ResumeLayout(); + panel2.ResumeLayout(); + } + + /// + /// + /// Handles the WM_SETCURSOR message + /// + /// + private void WmSetCursor(ref Message m) { + + // Accessing through the Handle property has side effects that break this + // logic. You must use InternalHandle. + // + if (m.WParam == InternalHandle && ((int)m.LParam & 0x0000FFFF) == NativeMethods.HTCLIENT) { + if (OverrideCursor != null) { + Cursor.CurrentInternal = OverrideCursor; + } + else { + Cursor.CurrentInternal = Cursor; + } + } + else { + DefWndProc(ref m); + } + + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // END PRIVATE FUNCTIONS ... // + // // + /////////////////////////////////////////////////////////////////////////////////////////////////// + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // Start PROTECTED OVERRIDE FUNCTIONS // + // // + /////////////////////////////////////////////////////////////////////////////////////////////////// + + internal override Rectangle GetToolNativeScreenRectangle() { + // Return splitter rectangle instead of the whole container rectangle to be consistent with the mouse ToolTip + Rectangle containerRectangle = base.GetToolNativeScreenRectangle(); + Rectangle splitterRectangle = this.SplitterRectangle; + return new Rectangle(containerRectangle.X + splitterRectangle.X, containerRectangle.Y + splitterRectangle.Y, splitterRectangle.Width, splitterRectangle.Height); + } + + internal override void AfterControlRemoved(Control control, Control oldParent) { + base.AfterControlRemoved(control, oldParent); + if (control is SplitContainer && control.Dock == DockStyle.Fill) + { + SetInnerMostBorder(this); + } + + } + + /// + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN + /// keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. For the TAB key, the method selects the next control + /// on the form. For the arrow keys, + /// !!! + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { +#if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessDialogKey [" + keyData.ToString() + "]"); +#endif + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Tab: + if (ProcessTabKey((keyData & Keys.Shift) == Keys.None)) return true; + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + if (!splitterFocused) { + if (ProcessArrowKey(keyCode == Keys.Right || + keyCode == Keys.Down)) return true; + } + else + return false; + break; + + } + } + return base.ProcessDialogKey(keyData); + } + + /// + /// /// + /// This will process the TabKey for the SplitContainer. The Focus needs to Shift from controls to the Left of the Splitter + /// to the splitter and then to the controls on the right of the splitter. This override implements this Logic. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessTabKey(bool forward) { + //Dont Focus the Splitter if TabStop == False or if the Splitter is Fixed !! + if (!TabStop || IsSplitterFixed) { + return base.ProcessTabKey(forward); + } + + if (nextActiveControl != null) { + SetActiveControlInternal(nextActiveControl); + nextActiveControl = null; + } + + if (SelectNextControlInPanel(ActiveControl, forward, true, true, true)) { + nextActiveControl = null; + splitterFocused = false; + return true; + } + else { + if (callBaseVersion) { + callBaseVersion = false; + return base.ProcessTabKey(forward); + } + else { + //We are om Splitter ...... + splitterFocused = true; + IContainerControl c = this.ParentInternal.GetContainerControlInternal(); + if (c != null) { + ContainerControl cc = c as ContainerControl; + if (cc == null) { + c.ActiveControl = this; + } + else { + cc.SetActiveControlInternal(this); + } + } + SetActiveControlInternal(null); + return true; + } + } + } + + protected override void OnMouseCaptureChanged(EventArgs e) { + base.OnMouseCaptureChanged(e); + if (splitContainerMessageFilter != null) + { + Application.RemoveMessageFilter(splitContainerMessageFilter); + splitContainerMessageFilter = null; + } + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message msg) { + switch (msg.Msg) { + case NativeMethods.WM_SETCURSOR: + WmSetCursor(ref msg); + break; + case NativeMethods.WM_SETFOCUS: + splitterFocused = true; + base.WndProc(ref msg); + break; + case NativeMethods.WM_KILLFOCUS: + splitterFocused = false; + base.WndProc(ref msg); + break; + + default: + base.WndProc(ref msg); + break; + } + } + + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Control.ControlCollection CreateControlsInstance() { + return new SplitContainerTypedControlCollection(this, typeof(SplitterPanel), /*isReadOnly*/true); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // End PROTECTED OVERRIDE FUNCTIONS // + // // + /////////////////////////////////////////////////////////////////////////////////////////////////// + + + private class SplitContainerMessageFilter : IMessageFilter + { + private SplitContainer owner = null; + + public SplitContainerMessageFilter(SplitContainer splitContainer) + { + this.owner = splitContainer; + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) + ] + bool IMessageFilter.PreFilterMessage(ref Message m) { + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + if ((m.Msg == NativeMethods.WM_KEYDOWN && (int)m.WParam == (int)Keys.Escape) + || (m.Msg == NativeMethods.WM_SYSKEYDOWN)) { + //Notify that splitMOVE was reverted .. + //this is used in ONKEYUP!! + owner.splitBegin = false; + owner.SplitEnd(false); + owner.splitterClick = false; + owner.splitterDrag = false; + } + return true; + } + return false; + } + } + + /// + /// This control collection only allows a specific type of control + /// into the controls collection. It optionally supports readonlyness. + /// + internal class SplitContainerTypedControlCollection : WindowsFormsUtils.TypedControlCollection { + SplitContainer owner; + + public SplitContainerTypedControlCollection(Control c, Type type, bool isReadOnly): base(c, type, isReadOnly) + { + this.owner = c as SplitContainer; + } + + public override void Remove(Control value) { + if (value is SplitterPanel) { + if (!owner.DesignMode) { + if (IsReadOnly) throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + } + base.Remove(value); + } + + internal override void SetChildIndexInternal(Control child, int newIndex) + { + if (child is SplitterPanel) { + if (!owner.DesignMode) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + } + else { + // just no-op it at DT. + return; + } + } + base.SetChildIndexInternal(child, newIndex); + } + + } + + } + } diff --git a/WindowsForms/Managed/System/WinForms/Splitter.cs b/WindowsForms/Managed/System/WinForms/Splitter.cs new file mode 100644 index 000000000..e2759ec28 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Splitter.cs @@ -0,0 +1,1096 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Globalization; + + /// + /// + /// Provides user resizing of docked elements at run time. To use a Splitter you can + /// dock any control to an edge of a container, and then dock the splitter to the same + /// edge. The splitter will then resize the control that is previous in the docking + /// order. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("SplitterMoved"), + DefaultProperty("Dock"), + SRDescription(SR.DescriptionSplitter), + Designer("System.Windows.Forms.Design.SplitterDesigner, " + AssemblyRef.SystemDesign) + ] + public class Splitter : Control { + private const int DRAW_START = 1; + private const int DRAW_MOVE = 2; + private const int DRAW_END = 3; + + private const int defaultWidth = 3; + + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + private int minSize = 25; + private int minExtra = 25; + private Point anchor = Point.Empty; + private Control splitTarget; + private int splitSize = -1; + private int splitterThickness = 3; + private int initTargetSize; + private int lastDrawSplit = -1; + private int maxSize; + private static readonly object EVENT_MOVING = new object(); + private static readonly object EVENT_MOVED = new object(); + + // refer to VsWhidbey : 423553 (Cannot expose IMessageFilter.PreFilterMessage through this unsealed class) + private SplitterMessageFilter splitterMessageFilter = null; + + /// + /// + /// Creates a new Splitter. + /// + public Splitter() + : base() { + SetStyle(ControlStyles.Selectable, false); + TabStop = false; + minSize = 25; + minExtra = 25; + + Dock = DockStyle.Left; + } + + /// + /// + /// The current value of the anchor property. The anchor property + /// determines which edges of the control are anchored to the container's + /// edges. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DefaultValue(AnchorStyles.None)] + public override AnchorStyles Anchor { + get { + return AnchorStyles.None; + } + set { + // do nothing! + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + base.AllowDrop = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(defaultWidth, defaultWidth); + } + } + + /// + protected override Cursor DefaultCursor { + get { + switch (Dock) { + case DockStyle.Top: + case DockStyle.Bottom: + return Cursors.HSplit; + case DockStyle.Left: + case DockStyle.Right: + return Cursors.VSplit; + } + return base.DefaultCursor; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + /// + /// Indicates what type of border the Splitter control has. This value + /// comes from the System.Windows.Forms.BorderStyle enumeration. + /// + [ + DefaultValue(BorderStyle.None), + SRCategory(SR.CatAppearance), + System.Runtime.InteropServices.DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.SplitterBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); + cp.Style &= (~NativeMethods.WS_BORDER); + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// + /// + [ + Localizable(true), + DefaultValue(DockStyle.Left) + ] + public override DockStyle Dock { + get { return base.Dock;} + + set { + + if (!(value == DockStyle.Top || value == DockStyle.Bottom || value == DockStyle.Left || value == DockStyle.Right)) { + throw new ArgumentException(SR.GetString(SR.SplitterInvalidDockEnum)); + } + + int requestedSize = splitterThickness; + + base.Dock = value; + switch (Dock) { + case DockStyle.Top: + case DockStyle.Bottom: + if (splitterThickness != -1) { + Height = requestedSize; + } + break; + case DockStyle.Left: + case DockStyle.Right: + if (splitterThickness != -1) { + Width = requestedSize; + } + break; + } + } + } + + /// + /// + /// Determines if the splitter is horizontal. + /// + /// + private bool Horizontal { + get { + DockStyle dock = Dock; + return dock == DockStyle.Left || dock == DockStyle.Right; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// The minExtra is this minimum size (in pixels) of the remaining + /// area of the container. This area is center of the container that + /// is not occupied by edge docked controls, this is the are that + /// would be used for any fill docked control. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(25), + SRDescription(SR.SplitterMinExtraDescr) + ] + public int MinExtra { + get { + return minExtra; + } + set { + if (value < 0) value = 0; + minExtra = value; + } + } + + /// + /// + /// The minSize is the minimum size (in pixels) of the target of the + /// splitter. The target of a splitter is always the control adjacent + /// to the splitter, just prior in the dock order. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(25), + SRDescription(SR.SplitterMinSizeDescr) + ] + public int MinSize { + get { + return minSize; + } + set { + if (value < 0) value = 0; + minSize = value; + } + } + + /// + /// + /// The position of the splitter. If the splitter is not bound + /// to a control, SplitPosition will be -1. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.SplitterSplitPositionDescr) + ] + public int SplitPosition { + get { + if (splitSize == -1) splitSize = CalcSplitSize(); + return splitSize; + } + set { + // calculate maxSize and other bounding conditions + SplitData spd = CalcSplitBounds(); + + // this is not an else-if to handle the maxSize < minSize case... + // ie. we give minSize priority over maxSize... + if (value > maxSize) value = maxSize; + if (value < minSize) value = minSize; + + // if (value == splitSize) return; -- do we need this check? + + splitSize = value; + DrawSplitBar(DRAW_END); + + if (spd.target == null) { + splitSize = -1; + return; + } + + Rectangle bounds = spd.target.Bounds; + switch (Dock) { + case DockStyle.Top: + bounds.Height = value; + break; + case DockStyle.Bottom: + bounds.Y += bounds.Height - splitSize; + bounds.Height = value; + break; + case DockStyle.Left: + bounds.Width = value; + break; + case DockStyle.Right: + bounds.X += bounds.Width - splitSize; + bounds.Width = value; + break; + } + spd.target.Bounds = bounds; + Application.DoEvents(); + OnSplitterMoved(new SplitterEventArgs(Left, Top, (Left + bounds.Width / 2), (Top + bounds.Height / 2))); + + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Enter { + add { + base.Enter += value; + } + remove { + base.Enter -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { + base.KeyUp += value; + } + remove { + base.KeyUp -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { + base.KeyDown += value; + } + remove { + base.KeyDown -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { + base.KeyPress += value; + } + remove { + base.KeyPress -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Leave { + add { + base.Leave += value; + } + remove { + base.Leave -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovingDescr)] + public event SplitterEventHandler SplitterMoving { + add { + Events.AddHandler(EVENT_MOVING, value); + } + remove { + Events.RemoveHandler(EVENT_MOVING, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovedDescr)] + public event SplitterEventHandler SplitterMoved { + add { + Events.AddHandler(EVENT_MOVED, value); + } + remove { + Events.RemoveHandler(EVENT_MOVED, value); + } + } + + /// + /// + /// Draws the splitter bar at the current location. Will automatically + /// cleanup anyplace the splitter was drawn previously. + /// + /// + private void DrawSplitBar(int mode) { + if (mode != DRAW_START && lastDrawSplit != -1) { + DrawSplitHelper(lastDrawSplit); + lastDrawSplit = -1; + } + // Bail if drawing with no old point... + // + else if (mode != DRAW_START && lastDrawSplit == -1) { + return; + } + + if (mode != DRAW_END) { + DrawSplitHelper(splitSize); + lastDrawSplit = splitSize; + } + else { + if (lastDrawSplit != -1) { + DrawSplitHelper(lastDrawSplit); + } + lastDrawSplit = -1; + } + } + + /// + /// + /// Calculates the bounding rect of the split line. minWeight refers + /// to the minimum height or width of the splitline. + /// + private Rectangle CalcSplitLine(int splitSize, int minWeight) { + Rectangle r = Bounds; + Rectangle bounds = splitTarget.Bounds; + switch (Dock) { + case DockStyle.Top: + if (r.Height < minWeight) r.Height = minWeight; + r.Y = bounds.Y + splitSize; + break; + case DockStyle.Bottom: + if (r.Height < minWeight) r.Height = minWeight; + r.Y = bounds.Y + bounds.Height - splitSize - r.Height; + break; + case DockStyle.Left: + if (r.Width < minWeight) r.Width = minWeight; + r.X = bounds.X + splitSize; + break; + case DockStyle.Right: + if (r.Width < minWeight) r.Width = minWeight; + r.X = bounds.X + bounds.Width - splitSize - r.Width; + break; + } + return r; + } + + /// + /// + /// Calculates the current size of the splitter-target. + /// + /// + private int CalcSplitSize() { + Control target = FindTarget(); + if (target == null) return -1; + Rectangle r = target.Bounds; + switch (Dock) { + case DockStyle.Top: + case DockStyle.Bottom: + return r.Height; + case DockStyle.Left: + case DockStyle.Right: + return r.Width; + default: + return -1; // belts & braces + } + } + + /// + /// + /// Calculates the bounding criteria for the splitter. + /// + /// + private SplitData CalcSplitBounds() { + SplitData spd = new SplitData(); + Control target = FindTarget(); + spd.target = target; + if (target != null) { + switch (target.Dock) { + case DockStyle.Left: + case DockStyle.Right: + initTargetSize = target.Bounds.Width; + break; + case DockStyle.Top: + case DockStyle.Bottom: + initTargetSize = target.Bounds.Height; + break; + } + Control parent = ParentInternal; + Control.ControlCollection children = parent.Controls; + int count = children.Count; + int dockWidth = 0, dockHeight = 0; + for (int i = 0; i < count; i++) { + Control ctl = children[i]; + if (ctl != target) { + switch (((Control)ctl).Dock) { + case DockStyle.Left: + case DockStyle.Right: + dockWidth += ctl.Width; + break; + case DockStyle.Top: + case DockStyle.Bottom: + dockHeight += ctl.Height; + break; + } + } + } + Size clientSize = parent.ClientSize; + if (Horizontal) { + maxSize = clientSize.Width - dockWidth - minExtra; + } + else { + maxSize = clientSize.Height - dockHeight - minExtra; + } + spd.dockWidth = dockWidth; + spd.dockHeight = dockHeight; + } + return spd; + } + + /// + /// + /// Draws the splitter line at the requested location. Should only be called + /// by drawSpltBar. + /// + /// + private void DrawSplitHelper(int splitSize) { + if (splitTarget == null) { + return; + } + + Rectangle r = CalcSplitLine(splitSize, 3); + IntPtr parentHandle = ParentInternal.Handle; + IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(ParentInternal, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE); + IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH(); + IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(ParentInternal, dc), new HandleRef(null, halftone)); + SafeNativeMethods.PatBlt(new HandleRef(ParentInternal, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT); + SafeNativeMethods.SelectObject(new HandleRef(ParentInternal, dc), new HandleRef(null, saveBrush)); + SafeNativeMethods.DeleteObject(new HandleRef(null, halftone)); + UnsafeNativeMethods.ReleaseDC(new HandleRef(ParentInternal, parentHandle), new HandleRef(null, dc)); + } + + + /// + /// Raises a splitter event + /// + /// + + /* No one seems to be calling this, so it is okay to comment it out + private void RaiseSplitterEvent(object key, SplitterEventArgs spevent) { + SplitterEventHandler handler = (SplitterEventHandler)Events[key]; + if (handler != null) handler(this, spevent); + } + */ + + /// + /// + /// Finds the target of the splitter. The target of the splitter is the + /// control that is "outside" or the splitter. For example, if the splitter + /// is docked left, the target is the control that is just to the left + /// of the splitter. + /// + /// + private Control FindTarget() { + Control parent = ParentInternal; + if (parent == null) return null; + Control.ControlCollection children = parent.Controls; + int count = children.Count; + DockStyle dock = Dock; + for (int i = 0; i < count; i++) { + Control target = children[i]; + if (target != this) { + switch (dock) { + case DockStyle.Top: + if (target.Bottom == Top) return(Control)target; + break; + case DockStyle.Bottom: + if (target.Top == Bottom) return(Control)target; + break; + case DockStyle.Left: + if (target.Right == Left) return(Control)target; + break; + case DockStyle.Right: + if (target.Left == Right) return(Control)target; + break; + } + } + } + return null; + } + + /// + /// + /// Calculates the split size based on the mouse position (x, y). + /// + /// + private int GetSplitSize(int x, int y) { + int delta; + if (Horizontal) { + delta = x - anchor.X; + } + else { + delta = y - anchor.Y; + } + int size = 0; + switch (Dock) { + case DockStyle.Top: + size = splitTarget.Height + delta; + break; + case DockStyle.Bottom: + size = splitTarget.Height - delta; + break; + case DockStyle.Left: + size = splitTarget.Width + delta; + break; + case DockStyle.Right: + size = splitTarget.Width - delta; + break; + } + return Math.Max(Math.Min(size, maxSize), minSize); + } + + /// + /// + /// + /// + protected override void OnKeyDown(KeyEventArgs e) { + base.OnKeyDown(e); + if (splitTarget != null && e.KeyCode == Keys.Escape) { + SplitEnd(false); + } + } + + /// + /// + /// + /// + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown(e); + if (e.Button == MouseButtons.Left && e.Clicks == 1) { + SplitBegin(e.X, e.Y); + } + } + + /// + /// + /// + /// + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + if (splitTarget != null) { + int x = e.X + Left; + int y = e.Y + Top; + Rectangle r = CalcSplitLine(GetSplitSize(e.X, e.Y), 0); + int xSplit = r.X; + int ySplit = r.Y; + OnSplitterMoving(new SplitterEventArgs(x, y, xSplit, ySplit)); + } + } + + /// + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs e) { + base.OnMouseUp(e); + if (splitTarget != null) { + int x = e.X + Left; + int y = e.Y + Top; + Rectangle r = CalcSplitLine(GetSplitSize(e.X, e.Y), 0); + int xSplit = r.X; + int ySplit = r.Y; + SplitEnd(true); + } + } + + /// + /// + /// Inherriting classes should override this method to respond to the + /// splitterMoving event. This event occurs while the splitter is + /// being moved by the user. + /// + protected virtual void OnSplitterMoving(SplitterEventArgs sevent) { + SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVING]; + if (handler != null) handler(this,sevent); + if (splitTarget != null) { + SplitMove(sevent.SplitX, sevent.SplitY); + } + } + + /// + /// + /// Inherriting classes should override this method to respond to the + /// splitterMoved event. This event occurs when the user finishes + /// moving the splitter. + /// + protected virtual void OnSplitterMoved(SplitterEventArgs sevent) { + SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVED]; + if (handler != null) handler(this,sevent); + if (splitTarget != null) { + SplitMove(sevent.SplitX, sevent.SplitY); + } + } + + + + /// + /// + /// [To be supplied.] + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + if (Horizontal) { + if (width < 1) { + width = 3; + } + splitterThickness = width; + } + else { + if (height < 1) { + height = 3; + } + splitterThickness = height; + } + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Begins the splitter moving. + /// + /// + private void SplitBegin(int x, int y) { + SplitData spd = CalcSplitBounds(); + if (spd.target != null && (minSize < maxSize)) { + anchor = new Point(x, y); + splitTarget = spd.target; + splitSize = GetSplitSize(x, y); + + // SECREVIEW : We need a message filter to capture the ESC key + // : to cancel the split action. + // : The method PreFilterMessage is adorned with a LinkDemand. + // + IntSecurity.UnmanagedCode.Assert(); + try { + if (splitterMessageFilter != null) + { + splitterMessageFilter = new SplitterMessageFilter(this); + } + Application.AddMessageFilter(splitterMessageFilter); + } + finally { + CodeAccessPermission.RevertAssert(); + } + CaptureInternal = true; + DrawSplitBar(DRAW_START); + } + } + + /// + /// + /// Finishes the split movement. + /// + /// + private void SplitEnd(bool accept) { + DrawSplitBar(DRAW_END); + splitTarget = null; + CaptureInternal = false; + if (splitterMessageFilter != null) + { + Application.RemoveMessageFilter(splitterMessageFilter); + splitterMessageFilter = null; + } + + if (accept) { + ApplySplitPosition(); + } + else if (splitSize != initTargetSize) { + SplitPosition = initTargetSize; + } + anchor = Point.Empty; + } + + /// + /// + /// Sets the split position to be the current split size. This is called + /// by splitEdit + /// + /// + private void ApplySplitPosition() { + SplitPosition = splitSize; + } + + /// + /// + /// Moves the splitter line to the splitSize for the mouse position + /// (x, y). + /// + /// + private void SplitMove(int x, int y) { + int size = GetSplitSize(x-Left+anchor.X, y-Top+anchor.Y); + if (splitSize != size) { + splitSize = size; + DrawSplitBar(DRAW_MOVE); + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", MinExtra: " + MinExtra.ToString(CultureInfo.CurrentCulture) + ", MinSize: " + MinSize.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// Return value holder... + /// + private class SplitData { + public int dockWidth = -1; + public int dockHeight = -1; + internal Control target; + } + + + private class SplitterMessageFilter : IMessageFilter + { + private Splitter owner = null; + + public SplitterMessageFilter(Splitter splitter) + { + this.owner = splitter; + } + + /// + /// + /// + /// + [ + System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), + ] + public bool PreFilterMessage(ref Message m) { + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + if (m.Msg == NativeMethods.WM_KEYDOWN && unchecked((int)(long)m.WParam) == (int)Keys.Escape) { + owner.SplitEnd(false); + } + return true; + } + return false; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SplitterCancelEvent.cs b/WindowsForms/Managed/System/WinForms/SplitterCancelEvent.cs new file mode 100644 index 000000000..7b0e3b8b6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitterCancelEvent.cs @@ -0,0 +1,104 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for splitter events. + /// + /// + public class SplitterCancelEventArgs : CancelEventArgs { + + private readonly int mouseCursorX; + private readonly int mouseCursorY; + private int splitX; + private int splitY; + + /// + /// + /// + /// Initializes an instance of the class with the specified coordinates + /// of the mouse pointer and the upper-left corner of the . + /// + /// + public SplitterCancelEventArgs(int mouseCursorX, int mouseCursorY, int splitX, int splitY) + : base (false) { + this.mouseCursorX = mouseCursorX; + this.mouseCursorY = mouseCursorY; + this.splitX = splitX; + this.splitY = splitY; + } + + /// + /// + /// + /// Gets the x-coordinate of the + /// mouse pointer (in client coordinates). + /// + /// + public int MouseCursorX { + get { + return mouseCursorX; + } + } + + /// + /// + /// + /// Gets the y-coordinate of the mouse pointer (in + /// client coordinates). + /// + /// + public int MouseCursorY { + get { + return mouseCursorY; + } + } + + /// + /// + /// + /// Gets the x-coordinate of the + /// upper-left corner of the (in client coordinates). + /// + /// + public int SplitX { + get { + return splitX; + } + set { + splitX = value; + } + } + + /// + /// + /// + /// Gets the y-coordinate of the upper-left corner of the (in client coordinates). + /// + /// + public int SplitY { + get { + return splitY; + } + set { + splitY = value; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SplitterCancelEventHandler.cs b/WindowsForms/Managed/System/WinForms/SplitterCancelEventHandler.cs new file mode 100644 index 000000000..d573a4321 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitterCancelEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents a method that will handle splitter events. + /// + /// + public delegate void SplitterCancelEventHandler(object sender, SplitterCancelEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/SplitterEvent.cs b/WindowsForms/Managed/System/WinForms/SplitterEvent.cs new file mode 100644 index 000000000..22490e484 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitterEvent.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for splitter events. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class SplitterEventArgs : EventArgs { + + private readonly int x; + private readonly int y; + private int splitX; + private int splitY; + + /// + /// + /// + /// Initializes an instance of the class with the specified coordinates + /// of the mouse pointer and the upper-left corner of the . + /// + /// + public SplitterEventArgs(int x, int y, int splitX, int splitY) { + this.x = x; + this.y = y; + this.splitX = splitX; + this.splitY = splitY; + } + + /// + /// + /// + /// Gets the x-coordinate of the + /// mouse pointer (in client coordinates). + /// + /// + public int X { + get { + return x; + } + } + + /// + /// + /// + /// Gets the y-coordinate of the mouse pointer (in + /// client coordinates). + /// + /// + public int Y { + get { + return y; + } + } + + /// + /// + /// + /// Gets the x-coordinate of the + /// upper-left corner of the (in client coordinates). + /// + /// + public int SplitX { + get { + return splitX; + } + set { + splitX = value; + } + } + + /// + /// + /// + /// Gets the y-coordinate of the upper-left corner of the (in client coordinates). + /// + /// + public int SplitY { + get { + return splitY; + } + set { + splitY = value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/SplitterEventHandler.cs b/WindowsForms/Managed/System/WinForms/SplitterEventHandler.cs new file mode 100644 index 000000000..de0870093 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitterEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents a method that will handle splitter events. + /// + /// + public delegate void SplitterEventHandler(object sender, SplitterEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/SplitterPanel.cs b/WindowsForms/Managed/System/WinForms/SplitterPanel.cs new file mode 100644 index 000000000..8fd04606d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SplitterPanel.cs @@ -0,0 +1,523 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Drawing; + using System.Drawing.Design; + using System.Drawing.Imaging; + using System.Runtime.InteropServices; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + + /// + /// + /// TBD. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Docking(DockingBehavior.Never), + Designer("System.Windows.Forms.Design.SplitterPanelDesigner, " + AssemblyRef.SystemDesign), + ToolboxItem(false) + ] + public sealed class SplitterPanel : Panel { + + SplitContainer owner = null; + private bool collapsed = false; + + /// + public SplitterPanel(SplitContainer owner) + : base() { + this.owner = owner; + SetStyle(ControlStyles.ResizeRedraw, true); + + } + + internal bool Collapsed { + get { + return collapsed; + } + set { + collapsed = value; + } + } + + + /// + /// + /// Override AutoSize to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new bool AutoSize { + get { + return base.AutoSize; + } + set { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + Localizable(false) + ] + public override AutoSizeMode AutoSizeMode { + get { + return AutoSizeMode.GrowOnly; + } + set { + } + } + + /// + /// + /// Override Anchor to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + base.Anchor = value; + } + } + + /// + /// + /// Indicates what type of border the Splitter control has. This value + /// comes from the System.Windows.Forms.BorderStyle enumeration. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new BorderStyle BorderStyle { + get { + return base.BorderStyle; + } + set { + base.BorderStyle = value; + } + } + + /// + /// + /// The dock property. The dock property controls to which edge + /// of the container this control is docked to. For example, when docked to + /// the top of the container, the control will be displayed flush at the + /// top of the container, extending the length of the container. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + } + } + + /// + /// + /// Override DockPadding to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + new public DockPaddingEdges DockPadding { + get { + return base.DockPadding; + } + } + + /// + /// + /// The height of this SplitterPanel + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlHeightDescr) + ] + public new int Height { + get { + if (Collapsed) { + return 0; + } + return base.Height; + } + set { + throw new NotSupportedException(SR.GetString(SR.SplitContainerPanelHeight)); + } + } + + internal int HeightInternal { + get { + return ((Panel)this).Height; + } + set { + ((Panel)this).Height = value; + } + } + + /// + /// + /// Override Location to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Padding DefaultMargin { + get { + return new Padding(0, 0, 0, 0); + } + } + + + /// + /// + /// Override AutoSize to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Size MinimumSize { + get { + return base.MinimumSize; + } + set { + base.MinimumSize = value; + } + } + + + /// + /// + /// Override AutoSize to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Size MaximumSize { + get { + return base.MaximumSize; + } + set { + base.MaximumSize = value; + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control. The name can be + /// used as a key into the ControlCollection. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new string Name { + get { + return base.Name; + } + set { + base.Name = value; + } + } + + /// + /// + /// The parent of this control. + /// + internal SplitContainer Owner { + get { + return owner; + } + } + + /// + /// + /// The parent of this control. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Control Parent { + get { + return base.Parent; + } + set { + base.Parent = value; + } + } + + /// + /// + /// Override Size to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Size Size { + get { + if (Collapsed) { + return Size.Empty; + } + return base.Size; + } + set { + base.Size = value; + } + } + + /// + /// + /// Override TabIndex to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + + /// + /// + /// Override TabStop to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + /// Override Visible to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new bool Visible { + get { + return base.Visible; + } + set { + base.Visible = value; + } + } + + /// + /// + /// The width of this control. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlWidthDescr) + ] + public new int Width { + get { + if (Collapsed) { + return 0; + } + return base.Width; + } + set { + throw new NotSupportedException(SR.GetString(SR.SplitContainerPanelWidth)); + } + } + + internal int WidthInternal { + get { + return ((Panel)this).Width; + } + set { + ((Panel)this).Width = value; + } + } + + /// + /// + /// Override VisibleChanged to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new event EventHandler VisibleChanged { + add { + base.VisibleChanged += value; + } + remove { + base.VisibleChanged -= value; + } + } + + /// + /// + /// Override DockChanged to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new event EventHandler DockChanged { + add { + base.DockChanged += value; + } + remove { + base.DockChanged -= value; + } + } + + /// + /// + /// Override LocationChanged to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new event EventHandler LocationChanged { + add { + base.LocationChanged += value; + } + remove { + base.LocationChanged -= value; + } + } + + /// + /// + /// Override TabIndexChanged to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + /// + /// Override TabStopChanged to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/StatusBar.cs b/WindowsForms/Managed/System/WinForms/StatusBar.cs new file mode 100644 index 000000000..29479e080 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBar.cs @@ -0,0 +1,1940 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Drawing; + using System.Collections; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + using System.Globalization; + + + /// + /// + /// + /// Represents a Windows status bar control. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("PanelClick"), + DefaultProperty("Text"), + Designer("System.Windows.Forms.Design.StatusBarDesigner, " + AssemblyRef.SystemDesign), + ] + public class StatusBar : Control { + + private int sizeGripWidth = 0; + private const int SIMPLE_INDEX = 0xFF; + + private static readonly object EVENT_PANELCLICK = new object(); + private static readonly object EVENT_SBDRAWITEM = new object(); + + private bool showPanels; + private bool layoutDirty; + private int panelsRealized; + private bool sizeGrip = true; + private string simpleText; + private Point lastClick = new Point(0, 0); + private IList panels = new ArrayList(); + private StatusBarPanelCollection panelsCollection; + private ControlToolTip tooltips; + + private ToolTip mainToolTip = null; + private bool toolTipSet = false; + + /// + /// + /// + /// Initializes a new default instance of the class. + /// + /// + public StatusBar() + : base() { + base.SetStyle(ControlStyles.UserPaint | ControlStyles.Selectable, false); + + Dock = DockStyle.Bottom; + TabStop = false; + } + + private static VisualStyleRenderer renderer = null; + + /// + /// A VisualStyleRenderer we can use to get information about the current UI theme + /// + private static VisualStyleRenderer VisualStyleRenderer { + get { + if (VisualStyleRenderer.IsSupported) { + if (renderer == null) { + renderer = new VisualStyleRenderer(VisualStyleElement.ToolBar.Button.Normal); + } + } + else { + renderer = null; + } + return renderer; + } + + } + + private int SizeGripWidth { + get { + if (sizeGripWidth == 0) { + if (Application.RenderWithVisualStyles && VisualStyleRenderer != null){ + // VSWhidbey 207045: need to build up accurate gripper width to avoid cutting off other panes. + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + VisualStyleElement thisElement; + Size elementSize; + + // gripper pane width... + thisElement = VisualStyleElement.Status.GripperPane.Normal; + vsRenderer.SetParameters(thisElement); + elementSize = vsRenderer.GetPartSize(Graphics.FromHwndInternal(this.Handle), ThemeSizeType.True); + sizeGripWidth = elementSize.Width; + + // ...plus gripper width + thisElement = VisualStyleElement.Status.Gripper.Normal; + vsRenderer.SetParameters(thisElement); + elementSize = vsRenderer.GetPartSize(Graphics.FromHwndInternal(this.Handle), ThemeSizeType.True); + sizeGripWidth += elementSize.Width; + + // Either GetPartSize could have returned a width of zero, so make sure we have a reasonable number: + sizeGripWidth = Math.Max(sizeGripWidth, 16); + } + else { + sizeGripWidth = 16; + } + } + return sizeGripWidth; + } + } + + /// + /// + /// + /// The background color of this control. This is an ambient property and will + /// always return a non-null value. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + // not supported, always return CONTROL + return SystemColors.Control; + } + + set { + // no op, not supported. + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the image rendered on the background of the + /// + /// control. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// + /// Returns the CreateParams used to create the handle for this control. + /// Inheriting classes should call base.getCreateParams in the manor below: + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_STATUSBAR; + + if (this.sizeGrip) { + cp.Style |= NativeMethods.SBARS_SIZEGRIP; + } + else { + cp.Style &= (~NativeMethods.SBARS_SIZEGRIP); + } + cp.Style |= NativeMethods.CCS_NOPARENTALIGN | NativeMethods.CCS_NORESIZE; + + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, 22); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// + /// Gets or sets the docking behavior of the control. + /// + /// + [ + Localizable(true), + DefaultValue(DockStyle.Bottom) + ] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + } + } + + /// + /// + /// + /// Gets or sets the font the + /// control will use to display + /// information. + /// + /// + [ + Localizable(true) + ] + public override Font Font { + get { return base.Font;} + set { + base.Font = value; + SetPanelContentsWidths(false); + } + } + + /// + /// + /// + /// Gets or sets + /// the forecolor for the control. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// + /// Gets the collection of + /// panels contained within the + /// control. + /// + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.StatusBarPanelsDescr), + Localizable(true), + SRCategory(SR.CatAppearance), + MergableProperty(false) + ] + public StatusBarPanelCollection Panels { + get { + if (panelsCollection == null) { + panelsCollection = new StatusBarPanelCollection(this); + } + + return panelsCollection; + } + } + + /// + /// + /// + /// The status bar text. + /// + /// + [ + Localizable(true) + ] + public override string Text { + get { + if (simpleText == null) { + return ""; + } + else { + return simpleText; + } + } + set { + SetSimpleText(value); + if (simpleText != value) { + simpleText = value; + OnTextChanged(EventArgs.Empty); + } + } + } + + /* No one is calling this, so it is ok to comment it out + private IntPtr ToolTipHandle { + get { + EnumChildren c = new EnumChildren( this ); + UnsafeNativeMethods.EnumChildWindows(new HandleRef(null, UnsafeNativeMethods.GetDesktopWindow()), new NativeMethods.EnumChildrenProc(c.Callback), NativeMethods.NullHandleRef); + return c.hWndFound; + } + } + */ + + /// + /// + /// + /// Gets or sets a value indicating whether panels should be shown. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.StatusBarShowPanelsDescr) + ] + public bool ShowPanels { + get { + return showPanels; + } + set { + if (showPanels != value) { + showPanels = value; + + layoutDirty = true; + if (IsHandleCreated) { + int bShowPanels = (!showPanels) ? 1 : 0; + + SendMessage(NativeMethods.SB_SIMPLE, bShowPanels, 0); + + if (showPanels) { + PerformLayout(); + RealizePanels(); + } + else if (tooltips != null) { + for (int i=0; i + /// + /// + /// Gets or sets a value indicating whether a sizing grip + /// will be rendered on the corner of the + /// control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.StatusBarSizingGripDescr) + ] + public bool SizingGrip { + get { + return sizeGrip; + } + set { + if (value != sizeGrip) { + sizeGrip = value; + RecreateHandle(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the user will be able to tab to the + /// . + /// + /// + [DefaultValue(false)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + internal bool ToolTipSet { + get { + return toolTipSet; + } + } + + internal ToolTip MainToolTip { + get { + return mainToolTip; + } + } + + /// + /// + /// + /// Occurs when a visual aspect of an owner-drawn status bar changes. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.StatusBarDrawItem)] + public event StatusBarDrawItemEventHandler DrawItem { + add { + Events.AddHandler(EVENT_SBDRAWITEM, value); + } + remove { + Events.RemoveHandler(EVENT_SBDRAWITEM, value); + } + } + + /// + /// + /// + /// Occurs when a panel on the status bar is clicked. + /// + /// + [SRCategory(SR.CatMouse), SRDescription(SR.StatusBarOnPanelClickDescr)] + public event StatusBarPanelClickEventHandler PanelClick { + add { + Events.AddHandler(EVENT_PANELCLICK, value); + } + remove { + Events.RemoveHandler(EVENT_PANELCLICK, value); + } + } + + /// + /// + /// StatusBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// Tells whether the panels have been realized. + /// + /// + internal bool ArePanelsRealized() { + return this.showPanels && IsHandleCreated; + } + + /// + /// + /// + /// + internal void DirtyLayout() { + layoutDirty = true; + } + + /// + /// + /// Makes the panel according to the sizes in the panel list. + /// + /// + private void ApplyPanelWidths() { + // This forces handle creation every time any time the StatusBar + // has to be re-laidout. + // + if (!IsHandleCreated) + return; + + StatusBarPanel panel = null; + int length = this.panels.Count; + + if (length == 0) { + Size sz = Size; + int[] offsets = new int[1]; + offsets[0] = sz.Width; + if (sizeGrip) { + offsets[0] -= SizeGripWidth; + } + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.SB_SETPARTS, 1, offsets); + SendMessage(NativeMethods.SB_SETICON, 0, IntPtr.Zero); + + return; + } + + int[] offsets2 = new int[length]; + int currentOffset = 0; + for (int i = 0; i < length; i++) { + panel = (StatusBarPanel) this.panels[i]; + currentOffset += panel.Width; + offsets2[i] = currentOffset; + panel.Right = offsets2[i]; + } + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.SB_SETPARTS, length, offsets2); + + // Tooltip setup... + // + for (int i=0; i + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_BAR_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + + base.CreateHandle(); + } + + /// + /// + /// Disposes this control + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (panelsCollection != null) { + StatusBarPanel[] panelCopy = new StatusBarPanel[panelsCollection.Count]; + ((ICollection)panelsCollection).CopyTo(panelCopy, 0); + panelsCollection.Clear(); + + foreach(StatusBarPanel p in panelCopy) { + p.Dispose(); + } + } + } + base.Dispose(disposing); + } + + /// + /// + /// Forces the panels to be updated, location, repainting, etc. + /// + /// + private void ForcePanelUpdate() { + if (ArePanelsRealized()) { + layoutDirty = true; + SetPanelContentsWidths(true); + PerformLayout(); + RealizePanels(); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + if (!DesignMode) { + tooltips = new ControlToolTip(this); + } + + if (!this.showPanels) { + SendMessage(NativeMethods.SB_SIMPLE, 1, 0); + SetSimpleText(simpleText); + } + else { + ForcePanelUpdate(); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + base.OnHandleDestroyed(e); + if (tooltips != null) { + tooltips.Dispose(); + tooltips = null; + } + } + + /* Not used + /// + /// + /// + private sealed class EnumChildren { + + public IntPtr hWndFound = IntPtr.Zero; + + private StatusBar peer; + + public EnumChildren( StatusBar peer ) { + if (peer == null) + throw new ArgumentNullException("peer"); + this.peer = peer; + } + + public bool Callback(IntPtr hWnd, IntPtr lparam) { + if (UnsafeNativeMethods.GetParent(new HandleRef(null, hWnd)) == peer.Handle) { + hWndFound = hWnd; + return false; + } + + return true; + } + }*/ + + /// + /// + /// + /// Raises the event. + /// + /// + protected override void OnMouseDown(MouseEventArgs e) { + lastClick.X = e.X; + lastClick.Y = e.Y; + base.OnMouseDown(e); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnPanelClick(StatusBarPanelClickEventArgs e) { + StatusBarPanelClickEventHandler handler = (StatusBarPanelClickEventHandler)Events[EVENT_PANELCLICK]; + if (handler != null) handler(this,e); + } + + /// + /// + /// + /// Raises the Layout event. + /// + /// + protected override void OnLayout(LayoutEventArgs levent) { + if (this.showPanels) { + LayoutPanels(); + if (IsHandleCreated && panelsRealized != panels.Count) { + RealizePanels(); + } + } + base.OnLayout(levent); + } + + /// + /// + /// This function sets up all the panel on the status bar according to + /// the internal this.panels List. + /// + /// + internal void RealizePanels() { + StatusBarPanel panel = null; + int length = this.panels.Count; + int old = panelsRealized; + + panelsRealized = 0; + + if (length == 0) { + SendMessage(NativeMethods.SB_SETTEXT, 0, ""); + } + + int i; + for (i = 0; i < length; i++) { + panel = (StatusBarPanel) this.panels[i]; + try { + panel.Realize(); + panelsRealized++; + } + catch { + } + } + for (; i + /// + /// Remove the internal list of panels without updating the control. + /// + /// + internal void RemoveAllPanelsWithoutUpdate() { + int size = this.panels.Count; + // remove the parent reference + for (int i = 0; i < size; i++) { + StatusBarPanel sbp = (StatusBarPanel) this.panels[i]; + sbp.ParentInternal = null; + } + + this.panels.Clear(); + if (this.showPanels == true) { + ApplyPanelWidths(); + ForcePanelUpdate(); + } + + } + + /// + /// + /// Sets the widths of any panels that have the + /// StatusBarPanelAutoSize.CONTENTS property set. + /// + /// + internal void SetPanelContentsWidths(bool newPanels) { + int size = panels.Count; + bool changed = false; + for (int i = 0; i < size; i++) { + StatusBarPanel sbp = (StatusBarPanel) panels[i]; + if (sbp.AutoSize == StatusBarPanelAutoSize.Contents) { + int newWidth = sbp.GetContentsWidth(newPanels); + if (sbp.Width != newWidth) { + sbp.Width = newWidth; + changed = true; + } + } + } + if (changed) { + DirtyLayout(); + PerformLayout(); + } + } + + private void SetSimpleText(string simpleText) { + if (!showPanels && IsHandleCreated) { + + int wparam = SIMPLE_INDEX + NativeMethods.SBT_NOBORDERS; + if (RightToLeft == RightToLeft.Yes) { + wparam |= NativeMethods.SBT_RTLREADING; + } + + SendMessage(NativeMethods.SB_SETTEXT, wparam, simpleText); + } + } + + /// + /// + /// Sizes the the panels appropriately. It looks at the SPRING AutoSize + /// property. + /// + /// + private void LayoutPanels() { + StatusBarPanel panel = null; + int barPanelWidth = 0; + int springNum = 0; + StatusBarPanel[] pArray = new StatusBarPanel[this.panels.Count]; + bool changed = false; + + for (int i = 0; i < pArray.Length; i++) { + panel = (StatusBarPanel) this.panels[i]; + if (panel.AutoSize == StatusBarPanelAutoSize.Spring) { + pArray[springNum] = panel; + springNum++; + } + else + barPanelWidth += panel.Width; + } + + + if (springNum > 0) { + Rectangle rect = Bounds; + int springPanelsLeft = springNum; + int leftoverWidth = rect.Width - barPanelWidth; + if (sizeGrip) { + leftoverWidth -= SizeGripWidth; + } + int copyOfLeftoverWidth = unchecked((int)0x80000000); + while (springPanelsLeft > 0) { + + int widthOfSpringPanel = (leftoverWidth) / springPanelsLeft; + if (leftoverWidth == copyOfLeftoverWidth) + break; + copyOfLeftoverWidth = leftoverWidth; + + for (int i = 0; i < springNum; i++) { + panel = pArray[i]; + if (panel == null) + continue; + + if (widthOfSpringPanel < panel.MinWidth) { + if (panel.Width != panel.MinWidth) { + changed = true; + } + panel.Width = panel.MinWidth; + pArray[i] = null; + springPanelsLeft --; + leftoverWidth -= panel.MinWidth; + } + else { + if (panel.Width != widthOfSpringPanel) { + changed = true; + } + panel.Width = widthOfSpringPanel; + } + } + } + } + + if (changed || layoutDirty) { + ApplyPanelWidths(); + } + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected virtual void OnDrawItem(StatusBarDrawItemEventArgs sbdievent) { + StatusBarDrawItemEventHandler handler = (StatusBarDrawItemEventHandler)Events[EVENT_SBDRAWITEM]; + if (handler != null) handler(this,sbdievent); + } + + /// + /// + /// + /// Raises the + /// event. + /// + /// + protected override void OnResize(EventArgs e) { + Invalidate(); + base.OnResize(e); + } + + /// + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + if (Panels != null) { + s += ", Panels.Count: " + Panels.Count.ToString(CultureInfo.CurrentCulture); + if (Panels.Count > 0) + s += ", Panels[0]: " + Panels[0].ToString(); + } + return s; + } + + //call this when System.Windows.forms.toolTip is Associated with Statusbar.... + internal void SetToolTip(ToolTip t) { + this.mainToolTip = t; + toolTipSet = true; + + } + + internal void UpdateTooltip(StatusBarPanel panel) { + if (tooltips == null) { + if (IsHandleCreated && !DesignMode) { + //This shouldn't happen: tooltips should've already been set. The best we can + //do here is reset it. + tooltips = new ControlToolTip(this); + } + else { + return; + } + } + + if (panel.Parent == this && panel.ToolTipText.Length > 0) { + int border = SystemInformation.Border3DSize.Width; + ControlToolTip.Tool t = tooltips.GetTool(panel); + if (t == null) { + t = new ControlToolTip.Tool(); + } + t.text = panel.ToolTipText; + t.rect = new Rectangle(panel.Right-panel.Width + border, 0, panel.Width - border, Height); + tooltips.SetTool(panel, t); + } + else { + tooltips.SetTool(panel, null); + } + } + + private void UpdatePanelIndex() { + int length = panels.Count; + for (int i=0; i + /// + /// + /// Processes messages for ownerdraw panels. + /// + private void WmDrawItem(ref Message m) { + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + + int length = this.panels.Count; + if (dis.itemID < 0 || dis.itemID >= length) + Debug.Fail("OwnerDraw item out of range"); + + StatusBarPanel panel = (StatusBarPanel) + this.panels[dis.itemID]; + + Graphics g = Graphics.FromHdcInternal(dis.hDC); + Rectangle r = Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); + + //The itemstate is not defined for a statusbar control + OnDrawItem(new StatusBarDrawItemEventArgs(g, Font, r, dis.itemID, DrawItemState.None, panel, this.ForeColor, this.BackColor)); + g.Dispose(); + } + + /// + /// + /// + /// + private void WmNotifyNMClick(NativeMethods.NMHDR note) { + + if (!showPanels) { + return; + } + + int size = panels.Count; + int currentOffset = 0; + int index = -1; + for (int i = 0; i < size; i++) { + StatusBarPanel panel = (StatusBarPanel) panels[i]; + currentOffset += panel.Width; + if (lastClick.X < currentOffset) { + // this is where the mouse was clicked. + index = i; + break; + } + } + if (index != -1) { + MouseButtons button = MouseButtons.Left; + int clicks = 0; + switch (note.code) { + case NativeMethods.NM_CLICK: + button = MouseButtons.Left; + clicks = 1; + break; + case NativeMethods.NM_RCLICK: + button = MouseButtons.Right; + clicks = 1; + break; + case NativeMethods.NM_DBLCLK: + button = MouseButtons.Left; + clicks = 2; + break; + case NativeMethods.NM_RDBLCLK: + button = MouseButtons.Right; + clicks = 2; + break; + } + + Point pt = lastClick; + StatusBarPanel panel = (StatusBarPanel) panels[index]; + + StatusBarPanelClickEventArgs sbpce = new StatusBarPanelClickEventArgs(panel, + button, clicks, pt.X, pt.Y); + OnPanelClick(sbpce); + } + } + + private void WmNCHitTest(ref Message m) { + int x = NativeMethods.Util.LOWORD(m.LParam); + Rectangle bounds = Bounds; + bool callSuper = true; + + // The default implementation of the statusbar + // : will let you size the form when it is docked on the bottom, + // : but when it is anywhere else, the statusbar will be resized. + // : to prevent that we provide a little bit a sanity to only + // : allow resizing, when it would resize the form. + // + if (x > bounds.X + bounds.Width - SizeGripWidth) { + Control parent = ParentInternal; + if (parent != null && parent is Form) { + FormBorderStyle bs = ((Form)parent).FormBorderStyle; + + if (bs != FormBorderStyle.Sizable + && bs != FormBorderStyle.SizableToolWindow) { + callSuper = false; + } + + if (!((Form)parent).TopLevel + || Dock != DockStyle.Bottom) { + + callSuper = false; + } + + if (callSuper) { + Control.ControlCollection children = parent.Controls; + int c = children.Count; + for (int i=0; i Top) { + callSuper = false; + break; + } + } + } + } + } + else { + callSuper = false; + } + } + + if (callSuper) { + base.WndProc(ref m); + } + else { + m.Result = (IntPtr)NativeMethods.HTCLIENT; + } + } + + /// + /// + /// + /// Base wndProc. All messages are sent to wndProc after getting filtered through + /// the preProcessMessage function. Inheriting controls should call base.wndProc + /// for any messages that they don't handle. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_NCHITTEST: + WmNCHitTest(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmDrawItem(ref m); + break; + case NativeMethods.WM_NOTIFY: + case NativeMethods.WM_NOTIFY + NativeMethods.WM_REFLECT: + NativeMethods.NMHDR note = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (note.code) { + case NativeMethods.NM_CLICK: + case NativeMethods.NM_RCLICK: + case NativeMethods.NM_DBLCLK: + case NativeMethods.NM_RDBLCLK: + WmNotifyNMClick(note); + break; + default: + base.WndProc(ref m); + break; + } + break; + + default: + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + /// The collection of StatusBarPanels that the StatusBar manages. + /// event. + /// + /// + [ + ListBindable(false) + ] + public class StatusBarPanelCollection : IList { + private StatusBar owner; + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + + /// + /// + /// + /// Constructor for the StatusBarPanelCollection class + /// + /// + public StatusBarPanelCollection(StatusBar owner) { + this.owner = owner; + } + + /// + /// + /// This method will return an individual StatusBarPanel with the appropriate index. + /// + public virtual StatusBarPanel this[int index] { + get { + return(StatusBarPanel)owner.panels[index]; + } + set { + + if (value == null) + throw new ArgumentNullException("StatusBarPanel"); + + owner.layoutDirty = true; + + if (value.Parent != null) { + throw new ArgumentException(SR.GetString(SR.ObjectHasParent), "value"); + } + + int length = owner.panels.Count; + + if (index < 0|| index >= length) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + StatusBarPanel oldPanel = (StatusBarPanel) owner.panels[index]; + oldPanel.ParentInternal = null; + value.ParentInternal = owner; + if (value.AutoSize == StatusBarPanelAutoSize.Contents) { + value.Width = value.GetContentsWidth(true); + } + owner.panels[index] = value; + value.Index = index; + + if (owner.ArePanelsRealized()) { + owner.PerformLayout(); + value.Realize(); + } + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is StatusBarPanel) { + this[index] = (StatusBarPanel)value; + } + else { + throw new ArgumentException(SR.GetString(SR.StatusBarBadStatusBarPanel), "value"); + } + } + } + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual StatusBarPanel this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + /// + /// + /// + /// Returns an integer representing the number of StatusBarPanels + /// in this collection. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public int Count { + get { + return owner.panels.Count; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// + /// Adds a StatusBarPanel to the collection. + /// + /// + public virtual StatusBarPanel Add(string text) { + StatusBarPanel panel = new StatusBarPanel(); + panel.Text = text; + Add(panel); + return panel; + } + + /// + /// + /// + /// Adds a StatusBarPanel to the collection. + /// + /// + public virtual int Add(StatusBarPanel value) { + int index = owner.panels.Count; + Insert(index, value); + return index; + } + + /// + /// + int IList.Add(object value) { + if (value is StatusBarPanel) { + return Add((StatusBarPanel)value); + } + else { + throw new ArgumentException(SR.GetString(SR.StatusBarBadStatusBarPanel), "value"); + } + } + + /// + /// + /// [To be supplied.] + /// + public virtual void AddRange(StatusBarPanel[] panels) { + if (panels == null) { + throw new ArgumentNullException("panels"); + } + foreach(StatusBarPanel panel in panels) { + Add(panel); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(StatusBarPanel panel) { + return IndexOf(panel) != -1; + } + + /// + /// + bool IList.Contains(object panel) { + if (panel is StatusBarPanel) { + return Contains((StatusBarPanel)panel); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(StatusBarPanel panel) { + for(int index=0; index < Count; ++index) { + if (this[index] == panel) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object panel) { + if (panel is StatusBarPanel) { + return IndexOf((StatusBarPanel)panel); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + /// + /// + /// + /// Inserts a StatusBarPanel in the collection. + /// + /// + public virtual void Insert(int index, StatusBarPanel value) { + + //check for the value not to be null + if (value == null) + throw new ArgumentNullException("value"); + //end check + + + owner.layoutDirty = true; + if (value.Parent != owner && value.Parent != null) + throw new ArgumentException(SR.GetString(SR.ObjectHasParent), "value"); + + int length = owner.panels.Count; + + if (index < 0 || index > length) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + value.ParentInternal = owner; + + switch (value.AutoSize) { + case StatusBarPanelAutoSize.None: + case StatusBarPanelAutoSize.Spring: + break; + case StatusBarPanelAutoSize.Contents: + value.Width = value.GetContentsWidth(true); + break; + } + + owner.panels.Insert(index, value); + owner.UpdatePanelIndex(); + + owner.ForcePanelUpdate(); + } + + /// + /// + void IList.Insert(int index, object value) { + if (value is StatusBarPanel) { + Insert(index, (StatusBarPanel)value); + } + else { + throw new ArgumentException(SR.GetString(SR.StatusBarBadStatusBarPanel), "value"); + } + } + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// + /// Removes all the StatusBarPanels in the collection. + /// + /// + public virtual void Clear() { + owner.RemoveAllPanelsWithoutUpdate(); + owner.PerformLayout(); + + } + + /// + /// + /// + /// Removes an individual StatusBarPanel in the collection. + /// + /// + public virtual void Remove(StatusBarPanel value) { + + //check for the value not to be null + if (value == null) + throw new ArgumentNullException("StatusBarPanel"); + //end check + + if (value.Parent != owner) { + return; + } + RemoveAt(value.Index); + } + + /// + /// + void IList.Remove(object value) { + if (value is StatusBarPanel) { + Remove((StatusBarPanel)value); + } + } + + + /// + /// + /// + /// Removes an individual StatusBarPanel in the collection at the given index. + /// + /// + public virtual void RemoveAt(int index) { + int length = Count; + if (index < 0 || index >= length) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + // clear any tooltip + // + StatusBarPanel panel = (StatusBarPanel)owner.panels[index]; + + owner.panels.RemoveAt(index); + panel.ParentInternal = null; + + // this will cause the panels tooltip to be removed since it's no longer a child + // of this StatusBar. + // + owner.UpdateTooltip(panel); + + // V#41207 - Microsoft, 4/1/1998 - We must reindex the panels after a removal... + owner.UpdatePanelIndex(); + owner.ForcePanelUpdate(); + } + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + owner.panels.CopyTo(dest, index); + } + + /// + /// + /// + /// Returns the Enumerator for this collection. + /// + /// + public IEnumerator GetEnumerator() { + if (owner.panels != null) { + return owner.panels.GetEnumerator(); + } + else { + return new StatusBarPanel[0].GetEnumerator(); + } + } + } + /// + /// + /// This is a tooltip control that provides tips for a single + /// control. Each "tool" region is defined by a rectangle and + /// the string that should be displayed. This implementation + /// is based on System.Windows.Forms.ToolTip, but this control + /// is lighter weight and provides less functionality... however + /// this control binds to rectangular regions, instead of + /// full controls. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + private class ControlToolTip { + + /// + public class Tool { + /// + public Rectangle rect = Rectangle.Empty; + /// + public string text; + internal IntPtr id = new IntPtr(-1); + } + + private Hashtable tools = new Hashtable(); + private ToolTipNativeWindow window = null; + private Control parent = null; + private int nextId = 0; + + /// + /// + /// Creates a new ControlToolTip. + /// + public ControlToolTip(Control parent) { + window = new ToolTipNativeWindow(this); + this.parent = parent; + } + + /// + /// + /// Returns the createParams to create the window. + /// + protected CreateParams CreateParams { + get { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + CreateParams cp = new CreateParams(); + cp.Parent = IntPtr.Zero; + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + cp.Style |= NativeMethods.TTS_ALWAYSTIP; + cp.ExStyle = 0; + cp.Caption = null; + return cp; + } + } + + /// + /// + /// + public IntPtr Handle { + get { + if (window.Handle == IntPtr.Zero) { + CreateHandle(); + } + return window.Handle; + } + } + + private bool IsHandleCreated { + get { return window.Handle != IntPtr.Zero;} + } + + private void AssignId(Tool tool) { + tool.id = (IntPtr)nextId; + nextId++; + } + + /// + /// + /// Sets the tool for the specified key. Keep in mind + /// that as soon as setTool is called, the handle for + /// the ControlToolTip is created, and the handle for + /// the parent control is also created. If the parent + /// handle is recreated in the future, all tools must + /// be re-added. The old tool for the specified key + /// will be removed. Passing null in for the + /// tool parameter will result in the tool + /// region being removed. + /// + public void SetTool(object key, Tool tool) { + bool remove = false; + bool add = false; + bool update = false; + + Tool toRemove = null; + if (tools.ContainsKey(key)) { + toRemove = (Tool)tools[key]; + } + + if (toRemove != null) { + remove = true; + } + if (tool != null) { + add = true; + } + if (tool != null && toRemove != null + && tool.id == toRemove.id) { + update = true; + } + + if (update) { + UpdateTool(tool); + } + else { + if (remove) { + RemoveTool(toRemove); + } + if (add) { + AddTool(tool); + } + } + + if (tool != null) { + tools[key] = tool; + } + else { + tools.Remove(key); + } + + } + + /// + /// + /// Returns the tool associated with the specified key, + /// or null if there is no area. + /// + public Tool GetTool(object key) { + return(Tool)tools[key]; + } + + + private void AddTool(Tool tool) { + if (tool != null && tool.text != null && tool.text.Length > 0) { + int ret; + StatusBar p = (StatusBar)parent; + + if (p.ToolTipSet) { + ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(p.MainToolTip, p.MainToolTip.Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO(tool)); + } + else { + ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ADDTOOL, 0, GetTOOLINFO(tool)); + } + if (ret == 0) { + throw new InvalidOperationException(SR.GetString(SR.StatusBarAddFailed)); + } + } + } + private void RemoveTool(Tool tool) { + if (tool != null && tool.text != null && tool.text.Length > 0 && (int)tool.id >= 0) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_DELTOOL, 0, GetMinTOOLINFO(tool)); + } + } + private void UpdateTool(Tool tool) { + if (tool != null && tool.text != null && tool.text.Length > 0 && (int)tool.id >= 0) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTOOLINFO, 0, GetTOOLINFO(tool)); + } + } + + + /// + /// + /// Creates the handle for the control. + /// + protected void CreateHandle() { + if (IsHandleCreated) { + return; + } + + window.CreateHandle(CreateParams); + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOACTIVATE); + + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + } + + /// + /// + /// Destroys the handle for this control. + /// + protected void DestroyHandle() { + if (IsHandleCreated) { + window.DestroyHandle(); + tools.Clear(); + } + } + + /// + /// + /// Disposes of the component. Call dispose when the component is no longer needed. + /// This method removes the component from its container (if the component has a site) + /// and triggers the dispose event. + /// + public void Dispose() { + DestroyHandle(); + } + + /// + /// + /// Returns a new instance of the TOOLINFO_T structure with the minimum + /// required data to uniquely identify a region. This is used primarily + /// for delete operations. NOTE: This cannot force the creation of a handle. + /// + private NativeMethods.TOOLINFO_T GetMinTOOLINFO(Tool tool) { + NativeMethods.TOOLINFO_T ti = new NativeMethods.TOOLINFO_T(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_T)); + ti.hwnd = parent.Handle; + if ((int)tool.id < 0) { + AssignId(tool); + } + StatusBar p = (StatusBar)parent; + if (p != null && p.ToolTipSet) { + ti.uId = parent.Handle; + } + else { + ti.uId = tool.id; + } + return ti; + } + + /// + /// + /// Returns a detailed TOOLINFO_T structure that represents the specified + /// region. NOTE: This may force the creation of a handle. + /// + private NativeMethods.TOOLINFO_T GetTOOLINFO(Tool tool) { + NativeMethods.TOOLINFO_T ti = GetMinTOOLINFO(tool); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_T)); + ti.uFlags |= NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + + // RightToLeft reading order + // + Control richParent = parent; + if (richParent != null && richParent.RightToLeft == RightToLeft.Yes) { + ti.uFlags |= NativeMethods.TTF_RTLREADING; + } + + ti.lpszText = tool.text; + ti.rect = NativeMethods.RECT.FromXYWH(tool.rect.X, tool.rect.Y, tool.rect.Width, tool.rect.Height); + return ti; + } + + + /// + /// + /// + ~ControlToolTip() { + DestroyHandle(); + } + + /// + /// + /// WNDPROC + /// + + protected void WndProc(ref Message msg) { + switch (msg.Msg) { + case NativeMethods.WM_SETFOCUS: + // bug 120872, the COMCTL StatusBar passes WM_SETFOCUS on to the DefWndProc, so + // it will take keyboard focus. We don't want it doing this, so we eat + // the message. + // + return; + default: + window.DefWndProc(ref msg); + break; + } + } + + /// + /// + /// + private class ToolTipNativeWindow : NativeWindow { + ControlToolTip control; + + internal ToolTipNativeWindow(ControlToolTip control) { + this.control = control; + } + + protected override void WndProc(ref Message m) { + if (control != null) { + control.WndProc(ref m); + } + } + } + } + + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEvent.cs b/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEvent.cs new file mode 100644 index 000000000..5f2ce20b1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEvent.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class StatusBarDrawItemEventArgs : DrawItemEventArgs { + readonly StatusBarPanel panel; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public StatusBarDrawItemEventArgs(System.Drawing.Graphics g, Font font, Rectangle r, int itemId, + DrawItemState itemState, StatusBarPanel panel) : base(g, font, r, itemId, itemState) { + this.panel = panel; + } + + /// + /// + /// + /// Initializes a new instance of the + /// class using the Forecolor and Backcolor. + /// + /// + public StatusBarDrawItemEventArgs(System.Drawing.Graphics g, Font font, Rectangle r, int itemId, + DrawItemState itemState, StatusBarPanel panel, Color foreColor, Color backColor) : base(g, font, r, itemId, itemState, foreColor, backColor) { + this.panel = panel; + } + + /// + /// + /// + /// Specifies the + /// to + /// draw. + /// + /// + public StatusBarPanel Panel { + get { + return panel; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEventHandler.cs new file mode 100644 index 000000000..1c72ae8cc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarDrawItemEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// event of a . + /// + /// + public delegate void StatusBarDrawItemEventHandler(object sender, StatusBarDrawItemEventArgs sbdevent); +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanel.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanel.cs new file mode 100644 index 000000000..9c816f4d9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanel.cs @@ -0,0 +1,703 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// + /// Stores the + /// control panel's information. + /// + /// + [ + ToolboxItem(false), + DesignTimeVisible(false), + DefaultProperty("Text") + ] + public class StatusBarPanel : Component, ISupportInitialize { + + private const int DEFAULTWIDTH = 100; + private const int DEFAULTMINWIDTH = 10; + private const int PANELTEXTINSET = 3; + private const int PANELGAP = 2; + + private string text = ""; + private string name = ""; + private string toolTipText = ""; + private Icon icon = null; + + private HorizontalAlignment alignment = HorizontalAlignment.Left; + private System.Windows.Forms.StatusBarPanelBorderStyle borderStyle = System.Windows.Forms.StatusBarPanelBorderStyle.Sunken; + private StatusBarPanelStyle style = StatusBarPanelStyle.Text; + + // these are package scope so the parent can get at them. + // + private StatusBar parent = null; + private int width = DEFAULTWIDTH; + private int right = 0; + private int minWidth = DEFAULTMINWIDTH; + private int index = 0; + private StatusBarPanelAutoSize autoSize = StatusBarPanelAutoSize.None; + + private bool initializing = false; + + private object userData; + + /// + /// + /// + /// Initializes a new default instance of the class. + /// + /// + public StatusBarPanel() { + } + + /// + /// + /// + /// Gets or sets the + /// property. + /// + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(HorizontalAlignment.Left), + Localizable(true), + SRDescription(SR.StatusBarPanelAlignmentDescr) + ] + public HorizontalAlignment Alignment { + get { + return alignment; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + if (alignment != value) { + alignment = value; + Realize(); + } + } + } + + /// + /// + /// + /// Gets or sets the + /// property. + /// + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(StatusBarPanelAutoSize.None), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.StatusBarPanelAutoSizeDescr) + ] + public StatusBarPanelAutoSize AutoSize { + get { + return this.autoSize; + } + + set { + //valid values are 0x1 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)StatusBarPanelAutoSize.None, (int)StatusBarPanelAutoSize.Contents)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(StatusBarPanelAutoSize)); + } + if (this.autoSize != value) { + this.autoSize = value; + UpdateSize(); + } + } + } + + + /// + /// + /// + /// Gets or sets the + /// + /// property. + /// + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(System.Windows.Forms.StatusBarPanelBorderStyle.Sunken), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.StatusBarPanelBorderStyleDescr) + ] + public StatusBarPanelBorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x1 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)StatusBarPanelBorderStyle.None, (int)StatusBarPanelBorderStyle.Sunken)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(StatusBarPanelBorderStyle)); + } + if (this.borderStyle != value) { + this.borderStyle = value; + Realize(); + if (Created) + this.parent.Invalidate(); + } + } + } + + /// + /// + /// + /// + internal bool Created { + get { + return this.parent != null && this.parent.ArePanelsRealized(); + } + } + + /// + /// + /// + /// Gets or sets the + /// property. + /// + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(null), + Localizable(true), + SRDescription(SR.StatusBarPanelIconDescr) + ] + public Icon Icon { + [ResourceExposure(ResourceScope.Machine)] + get { + // unfortunately we have no way of getting the icon from the control. + return this.icon; + } + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + set { + + if (value != null && (((Icon)value).Height > SystemInformation.SmallIconSize.Height || ((Icon)value).Width > SystemInformation.SmallIconSize.Width)) { + this.icon = new Icon(value, SystemInformation.SmallIconSize); + } + else { + this.icon = value; + } + + if (Created) { + IntPtr handle = (this.icon == null) ? IntPtr.Zero : this.icon.Handle; + this.parent.SendMessage(NativeMethods.SB_SETICON, (IntPtr)GetIndex(), handle); + + } + UpdateSize(); + if (Created) { + this.parent.Invalidate(); + } + } + } + + /// + /// + /// + /// Expose index internally + /// + /// + internal int Index + { + get + { + return index; + } + set + { + index = value; + } + } + /// + /// + /// + /// Gets or sets the minimum width the can be within the + /// control. + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(DEFAULTMINWIDTH), + Localizable(true), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.StatusBarPanelMinWidthDescr) + ] + public int MinWidth { + get { + return this.minWidth; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("MinWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "MinWidth", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (value != this.minWidth) { + this.minWidth = value; + + UpdateSize(); + if (this.minWidth > this.Width) { + Width = value; + } + } + } + } + + /// + /// + /// + /// Gets or sets the name of the panel. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.StatusBarPanelNameDescr) + ] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this, name); + } + set { + name = value; + if(Site!= null) { + Site.Name = name; + } + } + } + + /// + /// + /// + /// Represents the + /// control which hosts the + /// panel. + /// + /// + /// + [Browsable(false)] + public StatusBar Parent { + get { + return parent; + } + } + + /// + /// + /// + /// Expose a direct setter for parent internally + /// + /// + internal StatusBar ParentInternal + { + set + { + parent = value; + } + } + + /// + /// + /// + /// Expose right internally + /// + /// + internal int Right + { + get + { + return right; + } + set + { + right = value; + } + } + + /// + /// + /// + /// Gets or sets the style of the panel. + /// + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(StatusBarPanelStyle.Text), + SRDescription(SR.StatusBarPanelStyleDescr) + ] + public StatusBarPanelStyle Style { + get { return style;} + set { + //valid values are 0x1 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)StatusBarPanelStyle.Text, (int)StatusBarPanelStyle.OwnerDraw)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(StatusBarPanelStyle)); + } + if (this.style != value) { + this.style = value; + Realize(); + if (Created) { + this.parent.Invalidate(); + } + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// + /// Gets or sets the text of the panel. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(""), + SRDescription(SR.StatusBarPanelTextDescr) + ] + public string Text { + get { + if (text == null) { + return ""; + } + else { + return text; + } + } + set { + if (value == null) { + value = ""; + } + + if (!Text.Equals(value)) { + + if (value.Length == 0) { + this.text = null; + } + else { + this.text = value; + } + Realize(); + UpdateSize(); + } + } + } + + /// + /// + /// + /// Gets + /// or sets the panel's tool tip text. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(""), + SRDescription(SR.StatusBarPanelToolTipTextDescr) + ] + public string ToolTipText { + get { + if (this.toolTipText == null) { + return ""; + } + else { + return this.toolTipText; + } + } + set { + if (value == null) { + value = ""; + } + + if (!ToolTipText.Equals(value)) { + + if (value.Length == 0) { + this.toolTipText = null; + } + else { + this.toolTipText = value; + } + + if (Created) { + parent.UpdateTooltip(this); + } + } + } + } + + /// + /// + /// + /// Gets or sets the width of the within the + /// control. + /// + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(DEFAULTWIDTH), + SRDescription(SR.StatusBarPanelWidthDescr) + ] + public int Width { + get { + return this.width; + } + set { + if (!initializing && value < this.minWidth) + throw new ArgumentOutOfRangeException("Width", SR.GetString(SR.WidthGreaterThanMinWidth)); + + this.width = value; + UpdateSize(); + } + } + + /// + /// + /// Handles tasks required when the control is being initialized. + /// + public void BeginInit() { + initializing = true; + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (parent != null) { + int index = GetIndex(); + if (index != -1) { + parent.Panels.RemoveAt(index); + } + } + } + base.Dispose(disposing); + } + + /// + /// + /// Called when initialization of the control is complete. + /// + public void EndInit() { + initializing = false; + + if (Width < MinWidth) { + Width = MinWidth; + } + } + + /// + /// + /// + /// Gets the width of the contents of the panel + /// + internal int GetContentsWidth(bool newPanel) { + string text; + if (newPanel) { + if (this.text == null) + text = ""; + else + text = this.text; + } + else + text = Text; + + Graphics g = this.parent.CreateGraphicsInternal(); + Size sz = Size.Ceiling(g.MeasureString(text, parent.Font)); + if (this.icon != null) { + sz.Width += this.icon.Size.Width + 5; + } + g.Dispose(); + + int width = sz.Width + SystemInformation.BorderSize.Width*2 + PANELTEXTINSET*2 + PANELGAP; + return Math.Max(width, minWidth); + } + + /// + /// + /// + /// Returns the index of the panel by making the parent control search + /// for it within its list. + /// + private int GetIndex() { + return index; + } + + /// + /// + /// + /// Sets all the properties for this panel. + /// + internal void Realize() { + if (Created) { + string text; + string sendText; + int border = 0; + + if (this.text == null) { + text = ""; + } + else { + text = this.text; + } + + HorizontalAlignment align = alignment; + // Translate the alignment for Rtl apps + // + if (parent.RightToLeft == RightToLeft.Yes) { + switch (align) { + case HorizontalAlignment.Left: + align = HorizontalAlignment.Right; + break; + case HorizontalAlignment.Right: + align = HorizontalAlignment.Left; + break; + } + } + + switch (align) { + case HorizontalAlignment.Center: + sendText = "\t" + text; + break; + case HorizontalAlignment.Right: + sendText = "\t\t" + text; + break; + default: + sendText = text; + break; + } + switch (borderStyle) { + case StatusBarPanelBorderStyle.None: + border |= NativeMethods.SBT_NOBORDERS; + break; + case StatusBarPanelBorderStyle.Sunken: + break; + case StatusBarPanelBorderStyle.Raised: + border |= NativeMethods.SBT_POPOUT; + break; + } + switch (style) { + case StatusBarPanelStyle.Text: + break; + case StatusBarPanelStyle.OwnerDraw: + border |= NativeMethods.SBT_OWNERDRAW; + break; + } + + + int wparam = GetIndex() | border; + if (parent.RightToLeft == RightToLeft.Yes) { + wparam |= NativeMethods.SBT_RTLREADING; + } + + int result = (int) UnsafeNativeMethods.SendMessage(new HandleRef(parent, parent.Handle), NativeMethods.SB_SETTEXT, (IntPtr)wparam, sendText); + + if (result == 0) + throw new InvalidOperationException(SR.GetString(SR.UnableToSetPanelText)); + + if (this.icon != null && style != StatusBarPanelStyle.OwnerDraw) { + this.parent.SendMessage(NativeMethods.SB_SETICON, (IntPtr)GetIndex(), this.icon.Handle); + } + else { + this.parent.SendMessage(NativeMethods.SB_SETICON, (IntPtr)GetIndex(), IntPtr.Zero); + } + + if (style == StatusBarPanelStyle.OwnerDraw) { + NativeMethods.RECT rect = new NativeMethods.RECT(); + result = (int) UnsafeNativeMethods.SendMessage(new HandleRef(parent, parent.Handle), NativeMethods.SB_GETRECT, (IntPtr)GetIndex(), ref rect); + + if (result != 0) { + this.parent.Invalidate(Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom)); + } + } + } + } + + private void UpdateSize() { + if (this.autoSize == StatusBarPanelAutoSize.Contents) { + ApplyContentSizing(); + } + else { + if (Created) { + parent.DirtyLayout(); + parent.PerformLayout(); + } + } + } + + private void ApplyContentSizing() { + if (this.autoSize == StatusBarPanelAutoSize.Contents && + parent != null) { + int newWidth = GetContentsWidth(false); + if (newWidth != this.Width) { + this.Width = newWidth; + if (Created) { + parent.DirtyLayout(); + parent.PerformLayout(); + } + } + } + } + + /// + /// + /// + /// Retrieves a string that contains information about the + /// panel. + /// + /// + public override string ToString() { + return "StatusBarPanel: {" + Text + "}"; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanelAutoSize.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanelAutoSize.cs new file mode 100644 index 000000000..4c30c7dcb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanelAutoSize.cs @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies how a panel on a status bar changes when the + /// status bar resizes. + /// + /// + public enum StatusBarPanelAutoSize { + + /// + /// + /// + /// The panel does not change + /// its size when the status bar resizes. + /// + /// + None = 1, + + /// + /// + /// + /// The panel shares the available status bar space (the + /// space not taken up by panels with the and + /// settings) with other panels that have the + /// + /// setting. + /// + /// + Spring = 2, + + /// + /// + /// + /// The width of the panel is determined by its contents. + /// + /// + Contents = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanelBorderStyle.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanelBorderStyle.cs new file mode 100644 index 000000000..3afff79e7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanelBorderStyle.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the border style of a panel on the . + /// + /// + public enum StatusBarPanelBorderStyle { + /// + /// + /// No border. + /// + None = 1, + + /// + /// + /// + /// A raised border. + /// + /// + Raised = 2, + + /// + /// + /// + /// A sunken border. + /// + /// + Sunken = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEvent.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEvent.cs new file mode 100644 index 000000000..7556ced50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEvent.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class StatusBarPanelClickEventArgs : MouseEventArgs { + + readonly StatusBarPanel statusBarPanel; + + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public StatusBarPanelClickEventArgs(StatusBarPanel statusBarPanel, MouseButtons button, int clicks, int x, int y) + : base(button, clicks, x, y, 0) { + this.statusBarPanel = statusBarPanel; + } + + /// + /// + /// + /// Specifies the that represents the clicked panel. + /// + /// + public StatusBarPanel StatusBarPanel { + get { + return statusBarPanel; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEventHandler.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEventHandler.cs new file mode 100644 index 000000000..4a92d9f45 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanelClickEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// event of a . + /// + /// + public delegate void StatusBarPanelClickEventHandler(object sender, StatusBarPanelClickEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/StatusBarPanelStyle.cs b/WindowsForms/Managed/System/WinForms/StatusBarPanelStyle.cs new file mode 100644 index 000000000..778119951 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusBarPanelStyle.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies whether a panel on + /// a status bar is owner drawn or system drawn. + /// + /// + public enum StatusBarPanelStyle { + + /// + /// + /// + /// The panel is + /// drawn by the system. + /// + /// + Text = 1, + + /// + /// + /// + /// The panel is + /// drawn by the owner. + /// + /// + OwnerDraw = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/StatusStrip.cs b/WindowsForms/Managed/System/WinForms/StatusStrip.cs new file mode 100644 index 000000000..46718307b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StatusStrip.cs @@ -0,0 +1,696 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Collections.Specialized; + using System.Runtime.InteropServices; + using System.Windows.Forms.Layout; + using System.Security.Permissions; + using System.Security; + + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + SRDescription(SR.DescriptionStatusStrip) + ] + public class StatusStrip : ToolStrip { + + private const AnchorStyles AllAnchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom | AnchorStyles.Top; + private const AnchorStyles HorizontalAnchor = AnchorStyles.Left | AnchorStyles.Right; + private const AnchorStyles VerticalAnchor = AnchorStyles.Top | AnchorStyles.Bottom; + + private BitVector32 state = new BitVector32(); + + private static readonly int stateSizingGrip = BitVector32.CreateMask(); + private static readonly int stateCalledSpringTableLayout = BitVector32.CreateMask(stateSizingGrip); + + + private const int gripWidth = 12; + private RightToLeftLayoutGrip rtlLayoutGrip; + private Orientation lastOrientation = Orientation.Horizontal; + + /// + public StatusStrip() { + SuspendLayout(); + this.CanOverflow = false; + this.LayoutStyle = ToolStripLayoutStyle.Table; + this.RenderMode = ToolStripRenderMode.System; + this.GripStyle = ToolStripGripStyle.Hidden; + + SetStyle(ControlStyles.ResizeRedraw, true); + this.Stretch = true; + state[stateSizingGrip] = true; + ResumeLayout(true); + + } + + [ + DefaultValue(false), + SRDescription(SR.ToolStripCanOverflowDescr), + SRCategory(SR.CatLayout), + Browsable(false) + ] + public new bool CanOverflow { + get { + return base.CanOverflow; + } + set { + base.CanOverflow = value; + } + } + + + protected override bool DefaultShowItemToolTips { + get { + return false; + } + } + + + protected override Size DefaultSize { + get { + return new Size(200, 22); + } + } + + + protected override Padding DefaultPadding { + get { + if (Orientation == Orientation.Horizontal) { + if (RightToLeft == RightToLeft.No) { + return new Padding(1, 0, 14, 0); + } + else { + return new Padding(14, 0, 1, 0); + } + } + else { + // vertical + // the difference in symmetry here is that the grip does not actually rotate, it remains the same height it + // was before, so the DisplayRectangle needs to shrink up by its height. + return new Padding(1, 3, 1, DefaultSize.Height); + } + + } + } + + protected override DockStyle DefaultDock { + get { + return DockStyle.Bottom; + } + } + + + /// + [DefaultValue(DockStyle.Bottom)] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + } + } + + [DefaultValue(ToolStripGripStyle.Hidden)] + public new ToolStripGripStyle GripStyle { + get { + return base.GripStyle; + } + set { + base.GripStyle = value; + } + } + + [DefaultValue(ToolStripLayoutStyle.Table)] + public new ToolStripLayoutStyle LayoutStyle { + get { return base.LayoutStyle; } + set { base.LayoutStyle = value; } + } + + // we do some custom stuff with padding to accomodate size grip. + // changing this is not supported at DT + [Browsable(false)] + public new Padding Padding { + get { + return base.Padding; + } + set { + base.Padding = value; + } + } + + [Browsable(false)] + public new event EventHandler PaddingChanged { + add { + base.PaddingChanged += value; + } + remove { + base.PaddingChanged -= value; + } + } + + private Control RTLGrip { + get{ + if (rtlLayoutGrip == null) { + rtlLayoutGrip = new RightToLeftLayoutGrip(); + } + return rtlLayoutGrip; + } + } + + + [DefaultValue(false)] + [SRDescription(SR.ToolStripShowItemToolTipsDescr)] + [SRCategory(SR.CatBehavior)] + public new bool ShowItemToolTips { + get { + return base.ShowItemToolTips; + } + set { + base.ShowItemToolTips = value; + } + } + + // VSWhidbey 430251 + // return whether we should paint the sizing grip. + private bool ShowSizingGrip { + get{ + if (SizingGrip && IsHandleCreated) { + if (DesignMode) { + return true; // we dont care about the state of VS. + } + else { + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this); + if (rootHwnd.Handle != IntPtr.Zero) { + return !UnsafeNativeMethods.IsZoomed(rootHwnd); + } + } + } + return false; + } + } + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.StatusStripSizingGripDescr) + ] + public bool SizingGrip { + get { + return state[stateSizingGrip]; + } + set { + if (value != state[stateSizingGrip]) { + state[stateSizingGrip] = value; + EnsureRightToLeftGrip(); + Invalidate(true); + } + } + } + + [Browsable(false)] + public Rectangle SizeGripBounds { + get { + if (SizingGrip) { + Size statusStripSize = this.Size; + // we cant necessarily make this the height of the status strip, as + // the orientation could change. + int gripHeight = Math.Min(DefaultSize.Height, statusStripSize.Height); + + if (RightToLeft == RightToLeft.Yes) { + return new Rectangle(0, statusStripSize.Height - gripHeight, gripWidth, gripHeight); + } + else { + return new Rectangle(statusStripSize.Width - gripWidth, statusStripSize.Height - gripHeight, gripWidth, gripHeight); + } + } + return Rectangle.Empty; + } + } + + + [DefaultValue(true)] + [SRCategory(SR.CatLayout)] + [SRDescription(SR.ToolStripStretchDescr)] + public new bool Stretch { + get { + return base.Stretch; + } + set { + base.Stretch = value; + } + } + + + private TableLayoutSettings TableLayoutSettings { + get { return this.LayoutSettings as TableLayoutSettings; } + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new StatusStripAccessibleObject(this); + } + + protected internal override ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) { + return new ToolStripStatusLabel(text,image,onClick); + } + + protected override void Dispose( bool disposing ) { + if (disposing) { + if (rtlLayoutGrip != null) { + rtlLayoutGrip.Dispose(); + rtlLayoutGrip = null; + } + } + base.Dispose(disposing); + } + + // in RTL, we parent a transparent control over the grip to support mirroring. + private void EnsureRightToLeftGrip() { + if (SizingGrip && RightToLeft == RightToLeft.Yes) { + RTLGrip.Bounds = SizeGripBounds; + if (!this.Controls.Contains(RTLGrip)) { + WindowsFormsUtils.ReadOnlyControlCollection controlCollection = this.Controls as WindowsFormsUtils.ReadOnlyControlCollection; + + if (controlCollection != null) { + controlCollection.AddInternal(RTLGrip); + } + } + } + else if (rtlLayoutGrip != null) { + if (this.Controls.Contains(rtlLayoutGrip)) { + WindowsFormsUtils.ReadOnlyControlCollection controlCollection = this.Controls as WindowsFormsUtils.ReadOnlyControlCollection; + + if (controlCollection != null) { + controlCollection.RemoveInternal(rtlLayoutGrip); + } + rtlLayoutGrip.Dispose(); + rtlLayoutGrip = null; + } + + } + + } + + internal override Size GetPreferredSizeCore(Size proposedSize) { + if (LayoutStyle == ToolStripLayoutStyle.Table) { + + if (proposedSize.Width == 1) { + proposedSize.Width = Int32.MaxValue; + } + if (proposedSize.Height == 1) { + proposedSize.Height = Int32.MaxValue; + } + if (Orientation == Orientation.Horizontal) { + return GetPreferredSizeHorizontal(this, proposedSize) + Padding.Size; + } + else { + return GetPreferredSizeVertical(this, proposedSize) + Padding.Size; + } + } + return base.GetPreferredSizeCore(proposedSize); + } + + protected override void OnPaintBackground(PaintEventArgs e) { + base.OnPaintBackground(e); + + if (ShowSizingGrip) { + Renderer.DrawStatusStripSizingGrip(new ToolStripRenderEventArgs(e.Graphics, this)); + } + } + + + protected override void OnLayout(LayoutEventArgs levent) { + state[stateCalledSpringTableLayout] = false; + bool inDisplayedItemCollecton = false; + ToolStripItem item = levent.AffectedComponent as ToolStripItem; + int itemCount = DisplayedItems.Count; + if (item != null) { + inDisplayedItemCollecton = DisplayedItems.Contains(item); + } + + + if (this.LayoutStyle == ToolStripLayoutStyle.Table) { + OnSpringTableLayoutCore(); + } + base.OnLayout(levent); + + if (itemCount != DisplayedItems.Count || (item != null && (inDisplayedItemCollecton != DisplayedItems.Contains(item)))) { + // calling OnLayout has changed the displayed items collection + // the SpringTableLayoutCore requires the count of displayed items to + // be accurate. + // - so we need to perform layout again. + if (this.LayoutStyle == ToolStripLayoutStyle.Table) { + OnSpringTableLayoutCore(); + base.OnLayout(levent); + } + + } + + EnsureRightToLeftGrip(); + + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode; ; + } + } + + + protected override void SetDisplayedItems() { + if (state[stateCalledSpringTableLayout]) { + bool rightToLeft = ((Orientation == Orientation.Horizontal) && (RightToLeft == RightToLeft.Yes)); + + // shove all items that dont fit one pixel outside the displayed region + Rectangle displayRect = DisplayRectangle; + Point noMansLand = displayRect.Location; + noMansLand.X += ClientSize.Width + 1; + noMansLand.Y += ClientSize.Height + 1; + bool overflow = false; + Rectangle lastItemBounds = Rectangle.Empty; + + ToolStripItem lastItem = null; + for (int i = 0; i < Items.Count; i++) { + ToolStripItem item = Items[i]; + + // using spring layout we can get into a situation where there's extra items which arent + // visible. + if (overflow || ((IArrangedElement)item).ParticipatesInLayout) { + + if (overflow || (SizingGrip && item.Bounds.IntersectsWith(SizeGripBounds))) { + // if the item collides with the size grip, set the location to nomansland. + SetItemLocation(item, noMansLand); + item.SetPlacement(ToolStripItemPlacement.None); + } + } + else if (lastItem != null && (lastItemBounds.IntersectsWith(item.Bounds))) { + // if it overlaps the previous element, set the location to nomansland. + SetItemLocation(item, noMansLand); + item.SetPlacement(ToolStripItemPlacement.None); + } + else if (item.Bounds.Width == 1) { + ToolStripStatusLabel panel = item as ToolStripStatusLabel; + if (panel != null && panel.Spring) { + // once we get down to one pixel, there can always be a one pixel + // distribution problem with the TLP - there's usually a spare hanging around. + // so set this off to nomansland as well. + SetItemLocation(item, noMansLand); + item.SetPlacement(ToolStripItemPlacement.None); + } + } + + + if (item.Bounds.Location != noMansLand){ + // set the next item to inspect for collisions + lastItem = item; + lastItemBounds = lastItem.Bounds; + } + else { + // we cant fit an item, everything else after it should not be displayed + if (((IArrangedElement)item).ParticipatesInLayout) { + overflow = true; + } + } + } + } + base.SetDisplayedItems(); + } + + internal override void ResetRenderMode() { + RenderMode = ToolStripRenderMode.System; + } + internal override bool ShouldSerializeRenderMode() { + // We should NEVER serialize custom. + return (RenderMode != ToolStripRenderMode.System && RenderMode != ToolStripRenderMode.Custom); + } + + + + /// + /// Override this function if you want to do custom table layouts for the + /// StatusStrip. The default layoutstyle is tablelayout, and we need to play + /// with the row/column styles + /// + protected virtual void OnSpringTableLayoutCore() { + if (this.LayoutStyle == ToolStripLayoutStyle.Table) { + state[stateCalledSpringTableLayout]= true; + + this.SuspendLayout(); + + if (lastOrientation != Orientation) { + TableLayoutSettings settings = this.TableLayoutSettings; + settings.RowCount = 0; + settings.ColumnCount = 0; + settings.ColumnStyles.Clear(); + settings.RowStyles.Clear(); + } + lastOrientation = Orientation; + + if (Orientation == Orientation.Horizontal) { + + // + // Horizontal layout + // + TableLayoutSettings.GrowStyle = TableLayoutPanelGrowStyle.AddColumns; + + int originalColumnCount = this.TableLayoutSettings.ColumnStyles.Count; + + // iterate through the elements which are going to be displayed. + for (int i = 0; i < this.DisplayedItems.Count; i++) { + if (i >= originalColumnCount) { + // add if it's necessary. + this.TableLayoutSettings.ColumnStyles.Add(new ColumnStyle()); + } + + // determine if we "spring" or "autosize" the column + ToolStripStatusLabel panel = DisplayedItems[i] as ToolStripStatusLabel; + bool spring = (panel != null && panel.Spring); + DisplayedItems[i].Anchor = (spring) ? AllAnchor : VerticalAnchor; + + // spring is achieved by using 100% as the column style + ColumnStyle colStyle = this.TableLayoutSettings.ColumnStyles[i]; + colStyle.Width = 100; // this width is ignored in AutoSize. + colStyle.SizeType = (spring) ? SizeType.Percent : SizeType.AutoSize; + } + + if (TableLayoutSettings.RowStyles.Count > 1 || TableLayoutSettings.RowStyles.Count == 0) { + TableLayoutSettings.RowStyles.Clear(); + TableLayoutSettings.RowStyles.Add(new RowStyle()); + } + TableLayoutSettings.RowCount = 1; + + TableLayoutSettings.RowStyles[0].SizeType = SizeType.Absolute; + TableLayoutSettings.RowStyles[0].Height = Math.Max(0,this.DisplayRectangle.Height); + TableLayoutSettings.ColumnCount = DisplayedItems.Count+1; // add an extra cell so it fills the remaining space + + // dont remove the extra column styles, just set them back to autosize. + for (int i = DisplayedItems.Count; i < TableLayoutSettings.ColumnStyles.Count; i++) { + this.TableLayoutSettings.ColumnStyles[i].SizeType = SizeType.AutoSize; + } + } + else { + // + // Vertical layout + // + + TableLayoutSettings.GrowStyle = TableLayoutPanelGrowStyle.AddRows; + + int originalRowCount = this.TableLayoutSettings.RowStyles.Count; + + // iterate through the elements which are going to be displayed. + for (int i = 0; i < this.DisplayedItems.Count; i++) { + if (i >= originalRowCount) { + // add if it's necessary. + this.TableLayoutSettings.RowStyles.Add(new RowStyle()); + } + + // determine if we "spring" or "autosize" the row + ToolStripStatusLabel panel = DisplayedItems[i] as ToolStripStatusLabel; + bool spring = (panel != null && panel.Spring); + DisplayedItems[i].Anchor = (spring) ? AllAnchor : HorizontalAnchor; + + // spring is achieved by using 100% as the row style + RowStyle rowStyle = this.TableLayoutSettings.RowStyles[i]; + rowStyle.Height = 100; // this width is ignored in AutoSize. + rowStyle.SizeType = (spring) ? SizeType.Percent : SizeType.AutoSize; + } + TableLayoutSettings.ColumnCount = 1; + + if (TableLayoutSettings.ColumnStyles.Count > 1 || TableLayoutSettings.ColumnStyles.Count == 0) { + TableLayoutSettings.ColumnStyles.Clear(); + TableLayoutSettings.ColumnStyles.Add(new ColumnStyle()); + } + + TableLayoutSettings.ColumnCount = 1; + TableLayoutSettings.ColumnStyles[0].SizeType = SizeType.Absolute; + TableLayoutSettings.ColumnStyles[0].Width = Math.Max(0,this.DisplayRectangle.Width); + + TableLayoutSettings.RowCount = DisplayedItems.Count+1; // add an extra cell so it fills the remaining space + + // dont remove the extra column styles, just set them back to autosize. + for (int i = DisplayedItems.Count; i < TableLayoutSettings.RowStyles.Count; i++) { + this.TableLayoutSettings.RowStyles[i].SizeType = SizeType.AutoSize; + } + + } + + this.ResumeLayout(false); + } + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + if ((m.Msg == NativeMethods.WM_NCHITTEST) && SizingGrip) { + // if we're within the grip bounds tell windows + // that we're the bottom right of the window. + Rectangle sizeGripBounds = SizeGripBounds; + int x = NativeMethods.Util.LOWORD(m.LParam); + int y = NativeMethods.Util.HIWORD(m.LParam); + + + if (sizeGripBounds.Contains(PointToClient(new Point(x, y)))) { + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this); + + // if the main window isnt maximized - we should paint a resize grip. + // double check that we're at the bottom right hand corner of the window. + if (rootHwnd.Handle != IntPtr.Zero && !UnsafeNativeMethods.IsZoomed(rootHwnd)) { + // get the client area of the topmost window. If we're next to the edge then + // the sizing grip is valid. + NativeMethods.RECT rootHwndClientArea = new NativeMethods.RECT(); + UnsafeNativeMethods.GetClientRect(rootHwnd, ref rootHwndClientArea); + + // map the size grip FROM statusStrip coords TO the toplevel window coords. + NativeMethods.POINT gripLocation; + if (RightToLeft == RightToLeft.Yes) { + gripLocation = new NativeMethods.POINT(SizeGripBounds.Left, SizeGripBounds.Bottom); + } + else { + gripLocation = new NativeMethods.POINT(SizeGripBounds.Right, SizeGripBounds.Bottom); + } + UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, this.Handle), rootHwnd, gripLocation, 1); + + int deltaBottomEdge = Math.Abs(rootHwndClientArea.bottom - gripLocation.y); + int deltaRightEdge = Math.Abs(rootHwndClientArea.right - gripLocation.x); + + if (RightToLeft != RightToLeft.Yes) { + if ((deltaRightEdge + deltaBottomEdge) < 2) { + m.Result = (IntPtr)NativeMethods.HTBOTTOMRIGHT; + return; + } + } + + + } + + } + } + base.WndProc(ref m); + } + + // special transparent mirrored window which says it's the bottom left of the form. + private class RightToLeftLayoutGrip : Control { + public RightToLeftLayoutGrip() { + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + this.BackColor = Color.Transparent; + } + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + return cp; + } + } + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + if (m.Msg == NativeMethods.WM_NCHITTEST) { + int x = NativeMethods.Util.LOWORD(m.LParam); + int y = NativeMethods.Util.HIWORD(m.LParam); + + if (ClientRectangle.Contains(PointToClient(new Point(x, y)))) { + m.Result = (IntPtr)NativeMethods.HTBOTTOMLEFT; + return; + } + + } + base.WndProc(ref m); + } + } + + + [System.Runtime.InteropServices.ComVisible(true)] + internal class StatusStripAccessibleObject : ToolStripAccessibleObject { + public StatusStripAccessibleObject(StatusStrip owner) : base(owner) { + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.StatusBar; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_StatusBarControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + StatusStrip statusStrip = Owner as StatusStrip; + if (statusStrip == null || statusStrip.Items.Count == 0) { + return null; + } + + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + AccessibleObject firstChild = null; + for (int i = 0; i < GetChildCount(); i++) { + firstChild = GetChild(i); + if (firstChild != null && !(firstChild is ControlAccessibleObject)) { + return firstChild; + } + } + return null; + + case UnsafeNativeMethods.NavigateDirection.LastChild: + AccessibleObject lastChild = null; + for (int i = GetChildCount() - 1; i >= 0; i--) { + lastChild = GetChild(i); + if (lastChild != null && !(lastChild is ControlAccessibleObject)) { + return lastChild; + } + } + return null; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { + return HitTest((int)x, (int)y); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() { + return GetFocused(); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/StringSorter.cs b/WindowsForms/Managed/System/WinForms/StringSorter.cs new file mode 100644 index 000000000..b3c698ec8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StringSorter.cs @@ -0,0 +1,389 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Threading; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Globalization; + + /// + /// + /// + /// + /// This class provides methods to perform locale based comparison of strings + /// and sorting of arrays. + /// + /// + internal sealed class StringSorter + { + /// + /// + /// Ignore case when comparing two strings. When this flag is specified in + /// calls to compare() and sort(), two strings are considered equal if they + /// differ only in casing. + /// + public const int IgnoreCase = 0x00000001; + + /// + /// + /// Do not differentiate between Hiragana and Katakana characters. When this + /// flag is specified in calls to compare() and sort(), corresponding + /// Hiragana and Katakana characters compare as equal. + /// + public const int IgnoreKanaType = 0x00010000; + + /// + /// + /// Ignore nonspacing marks (accents, diacritics, and vowel marks). When + /// this flag is specified in calls to compare() and sort(), strings compare + /// as equal if they differ only in how characters are accented. + /// + public const int IgnoreNonSpace = 0x00000002; + + /// + /// + /// Ignore symbols. When this flag is specified in calls to compare() and + /// sort(), strings compare as equal if they differ only in what symbol + /// characters they contain. + /// + public const int IgnoreSymbols = 0x00000004; + + /// + /// + /// Ignore character widths. When this flag is specified in calls to + /// compare() and sort(), string comparisons do not differentiate between a + /// single-ubyte character and the same character as a double-ubyte character. + /// + public const int IgnoreWidth = 0x00020000; + + /// + /// + /// Treat punctuation the same as symbols. Typically, strings are compared + /// using what is called a "word sort" technique. In a word sort, all + /// punctuation marks and other nonalphanumeric characters, except for the + /// hyphen and the apostrophe, come before any alphanumeric character. The + /// hyphen and the apostrophe are treated differently than the other + /// nonalphanumeric symbols, in order to ensure that words such as "coop" + /// and "co-op" stay together within a sorted list. If the STRINGSORT flag + /// is specified, strings are compared using what is called a "string sort" + /// technique. In a string sort, the hyphen and apostrophe are treated just + /// like any other nonalphanumeric symbols: they come before the + /// alphanumeric symbols. + /// + public const int StringSort = 0x00001000; + + /// + /// + /// Descending sort. When this flag is specified in a call to sort(), the + /// strings are sorted in descending order. This flag should not be used in + /// calls to compare(). + /// + public const int Descending = unchecked((int)0x80000000); + + private const int CompareOptions = IgnoreCase | IgnoreKanaType | + IgnoreNonSpace | IgnoreSymbols | IgnoreWidth | StringSort; + + private string[] keys; + private object[] items; + private int lcid; + private int options; + private bool descending; + + /// + /// + /// Constructs a StringSorter. Instances are created by the sort() routines, + /// but never by the user. + /// + private StringSorter(CultureInfo culture, string[] keys, object[] items, int options) { + if (keys == null) { + if (items is string[]) { + keys = (string[])items; + items = null; + } + else { + keys = new string[items.Length]; + for (int i = 0; i < items.Length; i++) { + object item = items[i]; + if (item != null) keys[i] = item.ToString(); + } + } + } + this.keys = keys; + this.items = items; + this.lcid = culture == null? SafeNativeMethods.GetThreadLocale(): culture.LCID; + this.options = options & CompareOptions; + this.descending = (options & Descending) != 0; + } + + /// + /// + /// + /// + internal static int ArrayLength(object[] array) { + if (array == null) + return 0; + else + return array.Length; + } + + /// + /// + /// Compares two strings using the default locale and no additional string + /// comparison flags. + /// + public static int Compare(string s1, string s2) { + return Compare(SafeNativeMethods.GetThreadLocale(), s1, s2, 0); + } + + /// + /// + /// Compares two strings using the default locale with the given set of + /// string comparison flags. + /// + public static int Compare(string s1, string s2, int options) { + return Compare(SafeNativeMethods.GetThreadLocale(), s1, s2, options); + } + + /// + /// + /// Compares two strings using a given locale and a given set of comparison + /// flags. If the two strings are of different lengths, they are compared up + /// to the length of the shortest one. If they are equal to that point, then + /// the return value will indicate that the longer string is greater. Notice + /// that if the return value is 0, the two strings are "equal" in the + /// collation sense, though not necessarily identical. + /// A string always sorts before a non-null string. Two + /// strings are considered equal. + /// The parameter is a combination of zero or more of + /// the following flags: IGNORECASE, IGNOREKANATYPE, + /// IGNORENONSPACE, IGNORESYMBOLS, + /// IGNOREWIDTH, and STRINGSORT. + /// + public static int Compare(CultureInfo culture, string s1, string s2, int options) { + return Compare(culture.LCID, s1, s2, options); + } + + /// + /// + /// + /// + private static int Compare(int lcid, string s1, string s2, int options) { + if (s1 == null) return s2 == null? 0: -1; + if (s2 == null) return 1; + return String.Compare(s1, s2, false, CultureInfo.CurrentCulture); + } + + /// + /// + /// + /// + private int CompareKeys(string s1, string s2) { + int result = Compare(lcid, s1, s2, options); + return descending? -result: result; + } + + /// + /// + /// Implementation of Quicksort algorithm. Within the outer do + /// loop, the method recurses on the shorter side and loops on the longer + /// side. This bounds the recursive depth by log2(n) in the worst case. + /// Otherwise, worst case recursive depth would be n. + /// + /// + private void QuickSort(int left, int right) { + do { + int i = left; + int j = right; + string s = keys[(i + j) >> 1]; + do { + while (CompareKeys(keys[i], s) < 0) i++; + while (CompareKeys(s, keys[j]) < 0) j--; + if (i > j) break; + if (i < j) { + string key = keys[i]; + keys[i] = keys[j]; + keys[j] = key; + if (items != null) { + object item = items[i]; + items[i] = items[j]; + items[j] = item; + } + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + + /// + /// + /// Sorts an object array based on the string representation of the + /// elements. If the items parameter is not a string array, the + /// toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the default locale. + /// + public static void Sort(object[] items) { + Sort(null, null, items, 0, ArrayLength(items), 0); + } + + /// + /// + /// Sorts a range in an object array based on the string representation of + /// the elements. If the items parameter is not a string array, + /// the toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the default locale. + /// + public static void Sort(object[] items, int index, int count) { + Sort(null, null, items, index, count, 0); + } + + /// + /// + /// Sorts a string array and an object array based on the elements of the + /// string array. The arrays are sorted using the default locale. + /// + public static void Sort(string[] keys, object[] items) { + Sort(null, keys, items, 0, ArrayLength(items), 0); + } + + /// + /// + /// Sorts a range in a string array and a range in an object array based on + /// the elements of the string array. The arrays are sorted using the + /// default locale. + /// + public static void Sort(string[] keys, object[] items, int index, int count) { + Sort(null, keys, items, index, count, 0); + } + + /// + /// + /// Sorts an object array based on the string representation of the + /// elements. If the items parameter is not a string array, the + /// toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the default locale and the given sorting + /// options. + /// + public static void Sort(object[] items, int options) { + Sort(null, null, items, 0, ArrayLength(items), options); + } + + /// + /// + /// Sorts a range in an object array based on the string representation of + /// the elements. If the items parameter is not a string array, + /// the toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the default locale and the given sorting + /// options. + /// + public static void Sort(object[] items, int index, int count, int options) { + Sort(null, null, items, index, count, options); + } + + /// + /// + /// Sorts a string array and an object array based on the elements of the + /// string array. The arrays are sorted using the default locale and the + /// given sorting options. + /// + public static void Sort(string[] keys, object[] items, int options) { + Sort(null, keys, items, 0, ArrayLength(items), options); + } + + /// + /// + /// Sorts a range in a string array and a range in an object array based on + /// the elements of the string array. The arrays are sorted using the + /// default locale and the given sorting options. + /// + public static void Sort(string[] keys, object[] items, int index, int count, int options) { + Sort(null, keys, items, index, count, options); + } + + /// + /// + /// Sorts an object array based on the string representation of the + /// elements. If the items parameter is not a string array, the + /// toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the given locale and the given sorting + /// options. + /// + public static void Sort(CultureInfo culture, object[] items, int options) { + Sort(culture, null, items, 0, ArrayLength(items), options); + } + + /// + /// + /// Sorts a range in an object array based on the string representation of + /// the elements. If the items parameter is not a string array, + /// the toString method of each of the elements is called to + /// produce the string representation. The objects are then sorted by their + /// string representations using the given locale and the given sorting + /// options. + /// + public static void Sort(CultureInfo culture, object[] items, int index, int count, int options) { + Sort(culture, null, items, index, count, options); + } + + /// + /// + /// Sorts a string array and an object array based on the elements of the + /// string array. The arrays are sorted using the given locale and the + /// given sorting options. + /// + public static void Sort(CultureInfo culture, string[] keys, object[] items, int options) { + Sort(culture, keys, items, 0, ArrayLength(items), options); + } + + /// + /// + /// Sorts a range in a string array and a range in an object array based on + /// the elements of the string array. Elements in the keys + /// array specify the sort keys for corresponding elements in the + /// items array. The range of elements given by the + /// index and count parameters is sorted in both + /// arrays according to the given locale and sorting options. + /// If the keys parameter is null, the sort keys + /// are instead computed by calling the toString method of each + /// element in the items array. + /// null keys always sort before a non-null keys. + /// The options parameter is a combination of zero or more of + /// the following flags: IGNORECASE, IGNOREKANATYPE, + /// IGNORENONSPACE, IGNORESYMBOLS, + /// IGNOREWIDTH, STRINGSORT, and + /// DESCENDING. + /// + public static void Sort(CultureInfo culture, string[] keys, object[] items, int index, int count, int options) { + // keys and items have to be the same length + // + if ((items == null) + || ((keys != null) && (keys.Length != items.Length))) + throw new ArgumentException(SR.GetString(SR.ArraysNotSameSize, + "keys", + "items")); + if (count > 1) { + StringSorter sorter = new StringSorter(culture, keys, items, options); + sorter.QuickSort(index, index + count - 1); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/StringSource.cs b/WindowsForms/Managed/System/WinForms/StringSource.cs new file mode 100644 index 000000000..1215a2891 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StringSource.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.InteropServices; + using System.Collections; + using System.Diagnostics.CodeAnalysis; + + + /// + /// + /// + /// Represents an internal class that is used bu ComboBox and TextBox AutoCompleteCustomSoucr property. + /// This class is reponsible for initializing the SHAutoComplete COM object and setting options in it. + /// The StringSource contains an array of Strings which is passed to the COM object as the custom source. + /// + /// + internal class StringSource : IEnumString { + + private string[] strings; + private int current; + private int size; + private UnsafeNativeMethods.IAutoComplete2 autoCompleteObject2; + + /// + /// + /// + /// SHAutoComplete COM object CLSID. + /// + /// + private static Guid autoCompleteClsid = new Guid("{00BB2763-6A77-11D0-A535-00C04FD7D062}"); + + /// + /// + /// + /// Constructor. + /// + /// + public StringSource(string[] strings) { + Array.Clear(strings,0, size); + + if (strings != null) { + this.strings = strings; + } + current = 0; + size = (strings == null ) ? 0 : strings.Length; + + Guid iid_iunknown = typeof(UnsafeNativeMethods.IAutoComplete2).GUID; + object obj = UnsafeNativeMethods.CoCreateInstance(ref autoCompleteClsid, null, NativeMethods.CLSCTX_INPROC_SERVER, ref iid_iunknown); + + autoCompleteObject2 = (UnsafeNativeMethods.IAutoComplete2)obj; + } + + /// + /// + /// + /// This is the method that binds the custom source with the IAutoComplete interface.The "hWndEdit" is the handle + /// to the edit Control and the "options' are the options that need to be set in the AUTOCOMPLETE mode. + /// + /// + public bool Bind(HandleRef edit, int options) { + + bool retVal = false; + + if (autoCompleteObject2 != null) { + try + { + autoCompleteObject2.SetOptions(options); + autoCompleteObject2.Init(edit, (IEnumString)this, null, null); + retVal = true; + } + catch + { + retVal = false; + } + } + return retVal; + } + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public void ReleaseAutoComplete() + { + if (autoCompleteObject2 != null) + { + Marshal.ReleaseComObject(autoCompleteObject2); + autoCompleteObject2 = null; + } + } + + public void RefreshList(string[] newSource) + { + Array.Clear(strings,0, size); + + if (strings != null) { + this.strings = newSource; + } + current = 0; + size = (strings == null ) ? 0 : strings.Length; + } + + #region IEnumString Members + + void IEnumString.Clone(out IEnumString ppenum) + { + ppenum = new StringSource(strings); + } + + int IEnumString.Next(int celt, string[] rgelt, IntPtr pceltFetched) + { + if (celt < 0) + { + return NativeMethods.E_INVALIDARG; + } + int fetched = 0; + + while (current < size && celt > 0) + { + rgelt[fetched] = strings[current]; + current++; + fetched++; + celt--; + } + + if (pceltFetched != IntPtr.Zero) + { + Marshal.WriteInt32(pceltFetched, fetched); + } + return celt == 0 ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + + void IEnumString.Reset() + { + current = 0; + } + + int IEnumString.Skip(int celt) + { + current += celt; + if (current >= size) + { + return (NativeMethods.S_FALSE); + } + return NativeMethods.S_OK; + } + + #endregion + } +} + diff --git a/WindowsForms/Managed/System/WinForms/StructFormat.cs b/WindowsForms/Managed/System/WinForms/StructFormat.cs new file mode 100644 index 000000000..434e60225 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/StructFormat.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + /// + /// + /// + /// + public enum StructFormat { + /// + /// + /// [To be supplied.] + /// + Ansi = 1, + /// + /// + /// [To be supplied.] + /// + Unicode = 2, + /// + /// + /// [To be supplied.] + /// + Auto = 3, + } +} diff --git a/WindowsForms/Managed/System/WinForms/SysInfoForm.cs b/WindowsForms/Managed/System/WinForms/SysInfoForm.cs new file mode 100644 index 000000000..8b6d97f67 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SysInfoForm.cs @@ -0,0 +1,697 @@ +#if SECURITY_DIALOG +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm.InitializeComponent():System.Void")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_AppBase():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_CodeBase():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_CommandLine():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_CommandLineArgs():System.String[]")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_CommonUserAppDataPath():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_CompanyName():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_ConfigurationFile():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_ExecutablePath():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_LocalUserAppDataPath():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_MyDocuments():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_Name():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_ProductName():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_ProductVersion():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_StartupPath():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_UserAppDataPath():System.String")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm+AppInfo.get_Version():System.Version")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.SysInfoForm..ctor(System.Boolean)")] + +namespace System.Windows.Forms { + + using System; + using System.Diagnostics; + using System.Drawing; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.IO; + using System.Xml; + using System.Windows.Forms; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + using System.Runtime.Versioning; + + /// + /// + /// Summary description for SysInfoForm. + /// + internal class SysInfoForm : Form { + static Switch[] switches; + + /// + /// + /// Required by the Win Forms designer + /// + Container components; + ColumnHeader codeBaseColumn; + ColumnHeader versionColumn; + ColumnHeader fileVersionColumn; + ColumnHeader asmNameColumn; + ColumnHeader switchNameColumn; + ColumnHeader displayNameColumn; + ListView loadedAssemblyList; + ListView switchesList; + Button closeButton; + TabPage appInfo; + TabPage sysInfo; + TabPage secInfo; + TabPage switchInfo; + TabPage bugReportInfo; + TabControl tabControl1; + PropertyGrid appProps; + Label bugReportLabel; + Label securityLabel; + Label switchLabel; + TextBox bugReportDescription; + CheckBox includeSystemInformation; + CheckBox includeApplicationInformation; + Panel bugReportPanel; + Button saveBugReport; + Button submitBugReport; + bool windowIsRestricted = false; + + private static Switch[] GetSwitchesFromLoadedAssemblies() { + ArrayList list = new ArrayList(); + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + new ReflectionPermission(PermissionState.Unrestricted).Assert(); + try { + foreach (Assembly assembly in assemblies) { + Module[] modules = assembly.GetLoadedModules(); + + foreach (Module module in modules) { + + if (module != null) { + foreach (Type type in module.GetTypes()) { + + if (type != null) { + MemberInfo[] members = type.FindMembers(MemberTypes.Field, + BindingFlags.Static + | BindingFlags.Public + | BindingFlags.NonPublic, + new MemberFilter(Filter), + null); + + foreach (MemberInfo member in members) { + + if (member != null) { + FieldInfo field = member as FieldInfo; + if (field != null) { + object value = field.GetValue(null); + if (value != null) + list.Add(value); + } + } + } + } + } + } + } + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + return(Switch[]) list.ToArray(typeof(Switch)); + } + + private static bool Filter(MemberInfo member, object criteria) { + if (member == null) { + return false; + } + FieldInfo field = (FieldInfo)member; + if (field.FieldType == null) { + return false; + } + return(field.FieldType.IsSubclassOf(typeof(Switch))); + } + + public SysInfoForm(bool windowIsRestricted) { + this.windowIsRestricted = windowIsRestricted; + + // Required for Win Form Designer support + InitializeComponent(); + + MinimumSize = Size; + if (windowIsRestricted) { + securityLabel.Text = SR.GetString(SR.SecurityRestrictedText); + } + else { + securityLabel.Text = SR.GetString(SR.SecurityUnrestrictedText); + } + } + + /// + /// + /// Clean up any resources being used + /// + protected override void Dispose(bool disposing) { + if (disposing) { + components.Dispose(); + } + base.Dispose(disposing); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void PopulateAssemblyInfo() { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { + AssemblyName name = asm.GetName(); + try { + string fileVer = "n/a"; + if (name.EscapedCodeBase != null && name.EscapedCodeBase.Length > 0) { + Uri codeBase = new Uri(name.EscapedCodeBase); + if (codeBase.Scheme == "file") { + fileVer = FileVersionInfo.GetVersionInfo(NativeMethods.GetLocalPath(name.EscapedCodeBase)).FileVersion; + } + } + ListViewItem item = new ListViewItem(new string[] { + name.Name, + fileVer, + name.Version.ToString(), + name.EscapedCodeBase}); + loadedAssemblyList.Items.Add(item); + } + catch { + // ignore any exceptions... this dialog should never cause a fault... + // + loadedAssemblyList.Items.Add(new ListViewItem(new string[] {name.Name, "Exception loading information"})); + } + } + } + + private void PopulateApplicationInfo() { + appProps.SelectedObject = new AppInfo(); + } + + private void WriteBugReport(TextWriter writer) { + XmlTextWriter xml = new XmlTextWriter(writer); + xml.Formatting = Formatting.Indented; + + xml.WriteStartElement("bug"); { + xml.WriteAttributeString("product", ".NET Frameworks SDK"); + + xml.WriteStartElement("problem"); { + xml.WriteString(bugReportDescription.Text); + } + xml.WriteEndElement(); + + if (includeApplicationInformation.Checked) { + xml.WriteStartElement("assemblies"); { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { + AssemblyName name = asm.GetName(); + xml.WriteStartElement("assembly"); { + xml.WriteAttributeString("name", name.Name); + xml.WriteAttributeString("codebase", name.CodeBase); + xml.WriteAttributeString("version", name.Version.ToString()); + + string fileVer = "n/a"; + if (name.EscapedCodeBase != null && name.EscapedCodeBase.Length > 0) { + Uri codeBase = new Uri(name.EscapedCodeBase); + if (codeBase.Scheme == "file") { + fileVer = FileVersionInfo.GetVersionInfo(NativeMethods.GetLocalPath(name.EscapedCodeBase)).FileVersion; + } + } + xml.WriteAttributeString("win32version", fileVer); + } + xml.WriteEndElement(); + } + } + xml.WriteEndElement(); + } + + if (includeSystemInformation.Checked) { + xml.WriteStartElement("system"); { + xml.WriteAttributeString("os", Environment.OSVersion.Platform.ToString("G") + " " + Environment.OSVersion.Version.ToString()); + } + xml.WriteEndElement(); + } + } + xml.WriteEndElement(); + } + + private void SubmitBugReport(object sender, EventArgs e) { + UnsafeNativeMethods.ShellExecute(NativeMethods.NullHandleRef, null, SR.GetString(SR.SecuritySubmitBugUrl), null, null, NativeMethods.SW_NORMAL); + } + + private void SaveBugReport(object sender, EventArgs e) { + SaveFileDialog dialog = new SaveFileDialog(); + dialog.Filter = SR.GetString(SR.SecuritySaveFilter); + if (dialog.ShowDialog() == DialogResult.OK) { + Stream output = dialog.OpenFile(); + StreamWriter writer = new StreamWriter(output); + try { + WriteBugReport(writer); + } + finally { + writer.Flush(); + writer.Close(); + } + } + } + + private void PopulateSwitchesInfo() { + + // grab it in a local in case assembly load causes static to null out + // Until we start listening to assembly load events, we should not cache! + Switch[] switchesLocal = null; // switches + if (switchesLocal == null) { + switchesLocal = GetSwitchesFromLoadedAssemblies(); + switches = switchesLocal; + Array.Sort(switchesLocal, new SwitchSorter()); + } + + foreach (Switch sw in switchesLocal) { + bool value; + TraceSwitch tsw = sw as TraceSwitch; + if (tsw != null) + value = tsw.Level > TraceLevel.Off; + else { + BooleanSwitch bsw = sw as BooleanSwitch; + if (bsw != null) + value = bsw.Enabled; + else + continue; + } + + ListViewItem item = new SwitchItem(sw); + item.Checked = value; + switchesList.Items.Add(item); + } + } + + private void SwitchChecked(object sender, ItemCheckEventArgs e) { + SwitchItem item = (SwitchItem)switchesList.Items[e.Index]; + Switch sw = item.Value; + bool value = e.NewValue == CheckState.Checked; + TraceSwitch tsw = sw as TraceSwitch; + if (tsw != null) { + tsw.Level = (value ? TraceLevel.Verbose : TraceLevel.Off); + } + else { + BooleanSwitch bsw = sw as BooleanSwitch; + if (bsw != null) + bsw.Enabled = value; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + void TabSelectionChanged(object sender, EventArgs e) { + if (tabControl1.SelectedTab == switchInfo) { + if (switchesList.Items.Count == 0) { + try { + PopulateSwitchesInfo(); + } + catch { + } + } + } + else if (tabControl1.SelectedTab == sysInfo) { + if (loadedAssemblyList.Items.Count == 0) { + try { + PopulateAssemblyInfo(); + } + catch { + } + } + } + else if (tabControl1.SelectedTab == appInfo) { + if (appProps.SelectedObject == null) { + try { + PopulateApplicationInfo(); + } + catch { + } + } + } + } + + /// + /// + /// Required method for Designer support - do not modify + /// the contents of this method with an editor + /// + private void InitializeComponent() { + this.components = new Container(); + this.sysInfo = new TabPage(); + this.versionColumn = new ColumnHeader(); + this.fileVersionColumn = new ColumnHeader(); + this.loadedAssemblyList = new ListView(); + this.switchesList = new ListView(); + this.appInfo = new TabPage(); + this.tabControl1 = new TabControl(); + this.asmNameColumn = new ColumnHeader(); + this.codeBaseColumn = new ColumnHeader(); + this.switchNameColumn = new ColumnHeader(); + this.displayNameColumn = new ColumnHeader(); + this.secInfo = new TabPage(); + this.bugReportInfo = new TabPage(); + this.bugReportLabel = new Label(); + this.switchInfo = new TabPage(); + this.closeButton = new Button(); + this.securityLabel = new Label(); + this.bugReportDescription = new TextBox(); + this.includeSystemInformation = new CheckBox(); + this.includeApplicationInformation = new CheckBox(); + this.saveBugReport = new Button(); + this.submitBugReport = new Button(); + this.switchLabel = new Label(); + this.bugReportPanel = new Panel(); + + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + this.appProps = new PropertyGrid(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + //@design this.TrayLargeIcon = false; + //@design this.TrayAutoArrange = true; + //@design this.TrayHeight = 0; + //@design this.GridSize = new System.Drawing.Size(4, 4); + this.Text = SR.GetString(SR.SecurityAboutDialog); + this.AutoScaleDimensions = new System.Drawing.Size(6, 13); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(442, 273); + this.MinimizeBox = false; + this.MaximizeBox = false; + this.ControlBox = false; + this.CancelButton = closeButton; + this.Font = new Font("Tahoma", 8); + + sysInfo.Size = new System.Drawing.Size(428, 210); + sysInfo.TabIndex = 1; + sysInfo.Text = SR.GetString(SR.SecurityAssembliesTab); + + versionColumn.Text = SR.GetString(SR.SecurityVersionColumn); + versionColumn.Width = 100; + versionColumn.TextAlign = HorizontalAlignment.Left; + + fileVersionColumn.Text = SR.GetString(SR.SecurityFileVersionColumn); + fileVersionColumn.Width = 100; + fileVersionColumn.TextAlign = HorizontalAlignment.Left; + + loadedAssemblyList.Size = new System.Drawing.Size(428, 210); + loadedAssemblyList.Dock = DockStyle.Fill; + loadedAssemblyList.TabIndex = 0; + loadedAssemblyList.FullRowSelect = true; + loadedAssemblyList.View = View.Details; + loadedAssemblyList.Columns.Clear(); + loadedAssemblyList.Columns.AddRange(new ColumnHeader[] {asmNameColumn, + fileVersionColumn, + versionColumn, + codeBaseColumn}); + + switchLabel.Size = new Size(428, 25); + switchLabel.Dock = DockStyle.Bottom; + switchLabel.Text = SR.GetString(SR.SecuritySwitchLabel); + + switchesList.Size = new System.Drawing.Size(428, 210); + switchesList.Dock = DockStyle.Fill; + switchesList.ItemCheck += new ItemCheckEventHandler(SwitchChecked); + switchesList.TabIndex = 0; + switchesList.CheckBoxes = true; + switchesList.FullRowSelect = true; + switchesList.View = View.Details; + switchesList.Columns.Clear(); + switchesList.Columns.AddRange(new ColumnHeader[] {switchNameColumn, + displayNameColumn}); + + appInfo.Size = new System.Drawing.Size(428, 210); + appInfo.TabIndex = 2; + appInfo.Text = SR.GetString(SR.SecurityApplication); + appInfo.DockPadding.All = 2; + + switchInfo.Size = new System.Drawing.Size(428, 210); + switchInfo.TabIndex = 4; + switchInfo.Text = SR.GetString(SR.SecuritySwitchesTab); + + bugReportDescription.Multiline = true; + bugReportDescription.Dock = DockStyle.Fill; + bugReportDescription.WordWrap = true; + + bugReportLabel.Dock = DockStyle.Top; + bugReportLabel.Size = new System.Drawing.Size(428, 36); + bugReportLabel.Text = SR.GetString(SR.SecurityBugReportLabel); + + includeSystemInformation.Checked = true; + includeSystemInformation.Dock = DockStyle.Bottom; + includeSystemInformation.FlatStyle = FlatStyle.System; + includeSystemInformation.Text = SR.GetString(SR.SecurityIncludeSysInfo); + + includeApplicationInformation.Checked = true; + includeApplicationInformation.Dock = DockStyle.Bottom; + includeApplicationInformation.FlatStyle = FlatStyle.System; + includeApplicationInformation.Text = SR.GetString(SR.SecurityIncludeAppInfo); + + saveBugReport.Text = SR.GetString(SR.SecuritySaveBug); + saveBugReport.Location = new Point(2, 2); + saveBugReport.FlatStyle = FlatStyle.System; + saveBugReport.Size = new Size(75, 23); + saveBugReport.Click += new EventHandler(SaveBugReport); + + submitBugReport.Text = SR.GetString(SR.SecuritySubmitBug); + submitBugReport.Location = new Point(79, 2); + submitBugReport.FlatStyle = FlatStyle.System; + submitBugReport.Size = new Size(75, 23); + submitBugReport.Click += new EventHandler(SubmitBugReport); + + bugReportPanel.Dock = DockStyle.Bottom; + bugReportPanel.Size = new Size(428, 27); + + appProps.Dock = DockStyle.Fill; + appProps.ToolbarVisible = false; + + tabControl1.Location = new System.Drawing.Point(4, 4); + tabControl1.Size = new System.Drawing.Size(436, 236); + tabControl1.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom; + tabControl1.SelectedIndex = 0; + tabControl1.TabIndex = 0; + tabControl1.SelectedIndexChanged += new EventHandler(TabSelectionChanged); + + asmNameColumn.Text = SR.GetString(SR.SecurityAsmNameColumn); + asmNameColumn.Width = 150; + asmNameColumn.TextAlign = HorizontalAlignment.Left; + + switchNameColumn.Text = SR.GetString(SR.SecuritySwitchNameColumn); + switchNameColumn.Width = 150; + switchNameColumn.TextAlign = HorizontalAlignment.Left; + + displayNameColumn.Text = SR.GetString(SR.SecuritySwitchDescrColumn); + displayNameColumn.Width = 300; + displayNameColumn.TextAlign = HorizontalAlignment.Left; + + codeBaseColumn.Text = SR.GetString(SR.SecurityCodeBaseColumn); + codeBaseColumn.Width = 400; + codeBaseColumn.TextAlign = HorizontalAlignment.Left; + + secInfo.Size = new System.Drawing.Size(428, 210); + secInfo.TabIndex = 0; + secInfo.Text = SR.GetString(SR.SecurityInfoTab); + + bugReportInfo.Size = new System.Drawing.Size(428, 210); + bugReportInfo.TabIndex = 0; + bugReportInfo.Text = SR.GetString(SR.SecurityBugReportTab); + + securityLabel.Dock = DockStyle.Fill; + + closeButton.Size = new System.Drawing.Size(75, 23); + closeButton.FlatStyle = FlatStyle.System; + closeButton.TabIndex = 1; + closeButton.Location = new System.Drawing.Point(344, 248); + closeButton.Text = SR.GetString(SR.SecurityClose); + closeButton.Anchor = AnchorStyles.Right | AnchorStyles.Bottom; + closeButton.DialogResult = DialogResult.OK; + + this.Controls.Add(closeButton); + this.Controls.Add(tabControl1); + switchInfo.Controls.Add(switchesList); + switchInfo.Controls.Add(switchLabel); + sysInfo.Controls.Add(loadedAssemblyList); + secInfo.Controls.Add(securityLabel); + appInfo.Controls.Add(appProps); + bugReportPanel.Controls.Add(saveBugReport); + bugReportPanel.Controls.Add(submitBugReport); + bugReportInfo.Controls.Add(bugReportDescription); + bugReportInfo.Controls.Add(bugReportLabel); + bugReportInfo.Controls.Add(includeSystemInformation); + bugReportInfo.Controls.Add(includeApplicationInformation); + bugReportInfo.Controls.Add(bugReportPanel); + tabControl1.Controls.Add(secInfo); + tabControl1.Controls.Add(appInfo); + tabControl1.Controls.Add(sysInfo); + tabControl1.Controls.Add(switchInfo); + tabControl1.Controls.Add(bugReportInfo); + } + + [DefaultProperty("CompanyName")] + public class AppInfo { + AssemblyName assemblyName; + + public AppInfo() { + assemblyName = Assembly.GetEntryAssembly().GetName(); + } + + [Category("Entry Assembly")] + public Version Version { + get { + return assemblyName.Version; + } + } + + [Category("Entry Assembly")] + public string Name { + get { + return assemblyName.Name; + } + } + + [Category("Entry Assembly")] + public string CodeBase { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return assemblyName.CodeBase; + } + } + + [Category("Directories")] + public string MyDocuments { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Environment.GetFolderPath(Environment.SpecialFolder.Personal); + } + } + + [Category("Directories")] + public string UserAppDataPath { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.UserAppDataPath; + } + } + + [Category("Directories")] + public string CommonUserAppDataPath { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.CommonAppDataPath; + } + } + + [Category("Directories")] + public string LocalUserAppDataPath { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.LocalUserAppDataPath; + } + } + + [Category("Application")] + public string CompanyName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.CompanyName; + } + } + + [Category("Directories")] + public string AppBase { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return AppDomain.CurrentDomain.SetupInformation.ApplicationBase; + } + } + + [Category("Application")] + public string ConfigurationFile { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; + } + } + + [Category("Application")] + public string ProductName { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.ProductName; + } + } + + [Category("Application")] + public string ProductVersion { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.ProductVersion; + } + } + + [Category("Startup")] + public string CommandLine { + get { + return Environment.CommandLine; + } + } + + [Category("Startup")] + public string[] CommandLineArgs { + get { + return Environment.GetCommandLineArgs(); + } + } + + [Category("Startup")] + public string StartupPath { + get { + return Application.StartupPath; + } + } + + [Category("Application")] + public string ExecutablePath { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return Application.ExecutablePath; + } + } + } + + + class SwitchSorter : IComparer { + public int Compare(object x, object y) { + return String.Compare(((Switch)x).DisplayName, ((Switch)y).DisplayName, false, CultureInfo.InvariantCulture); + } + } + + class SwitchItem : ListViewItem { + Switch value; + + internal SwitchItem(Switch value) : base(new string[] {value.DisplayName, value.Description}) { + this.value = value; + } + + public Switch Value { + get { + return value; + } + } + + } + } +} +#endif diff --git a/WindowsForms/Managed/System/WinForms/SystemInformation.cs b/WindowsForms/Managed/System/WinForms/SystemInformation.cs new file mode 100644 index 000000000..d394a680f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SystemInformation.cs @@ -0,0 +1,1986 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Text; + using System.Configuration.Assemblies; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using System; + using Microsoft.Win32; + using System.IO; + using System.Security; + using System.Security.Permissions; + using System.Drawing; + using System.ComponentModel; + using System.Runtime.Versioning; + + /// + /// + /// Provides information about the operating system. + /// + public class SystemInformation { + + // private constructor to prevent creation + // + private SystemInformation() { + } + + // Figure out if all the multimon stuff is supported on the OS + // + private static bool checkMultiMonitorSupport = false; + private static bool multiMonitorSupport = false; + private static bool checkNativeMouseWheelSupport = false; + private static bool nativeMouseWheelSupport = true; + private static bool highContrast = false; + private static bool systemEventsAttached = false; + private static bool systemEventsDirty = true; + + private static IntPtr processWinStation = IntPtr.Zero; + private static bool isUserInteractive = false; + + private static PowerStatus powerStatus = null; + + private const int DefaultMouseWheelScrollLines = 3; + + //////////////////////////////////////////////////////////////////////////// + // SystemParametersInfo + // + + /// + /// + /// + /// Gets a value indicating whether the user has enabled full window drag. + /// + /// + public static bool DragFullWindows { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETDRAGFULLWINDOWS, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Gets a value indicating whether the user has selected to run in high contrast + /// mode. + /// + /// + public static bool HighContrast { + get { + EnsureSystemEvents(); + if (systemEventsDirty) { + NativeMethods.HIGHCONTRAST_I data = new NativeMethods.HIGHCONTRAST_I(); + data.cbSize = Marshal.SizeOf(data); + data.dwFlags = 0; + data.lpszDefaultScheme = IntPtr.Zero; + + bool b = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETHIGHCONTRAST, data.cbSize, ref data, 0); + + // NT4 does not support this parameter, so we always force + // it to false if we fail to get the parameter. + // + if (b) { + highContrast = (data.dwFlags & NativeMethods.HCF_HIGHCONTRASTON) != 0; + } + else { + highContrast = false; + } + systemEventsDirty = false; + } + + return highContrast; + } + } + + /// + /// + /// + /// Gets the number of lines to scroll when the mouse wheel is rotated. + /// + /// + public static int MouseWheelScrollLines { + get { + if (NativeMouseWheelSupport) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETWHEELSCROLLLINES, 0, ref data, 0); + return data; + } + else { + IntPtr hWndMouseWheel = IntPtr.Zero; + + // Check for the MouseZ "service". This is a little app that generated the + // MSH_MOUSEWHEEL messages by monitoring the hardware. If this app isn't + // found, then there is no support for MouseWheels on the system. + // + hWndMouseWheel = UnsafeNativeMethods.FindWindow(NativeMethods.MOUSEZ_CLASSNAME, NativeMethods.MOUSEZ_TITLE); + + if (hWndMouseWheel != IntPtr.Zero) { + + // Register the MSH_SCROLL_LINES message... + // + int message = SafeNativeMethods.RegisterWindowMessage(NativeMethods.MSH_SCROLL_LINES); + + + int lines = (int)UnsafeNativeMethods.SendMessage(new HandleRef(null, hWndMouseWheel), message, 0, 0); + + // this fails under terminal server, so we default to 3, which is the windows + // default. Nobody seems to pay attention to this value anyways... + if (lines != 0) { + return lines; + } + } + } + + return DefaultMouseWheelScrollLines; + } + } + + //////////////////////////////////////////////////////////////////////////// + // SystemMetrics + // + + /// + /// + /// + /// Gets the dimensions of the primary display monitor in pixels. + /// + /// + public static Size PrimaryMonitorSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSCREEN), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSCREEN)); + } + } + + /// + /// + /// + /// Gets the width of the vertical scroll bar in pixels. + /// + /// + public static int VerticalScrollBarWidth { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXVSCROLL); + } + } + + /// + /// + /// + /// Gets the width of the vertical scroll bar in pixels. + /// + /// + public static int GetVerticalScrollBarWidthForDpi(int dpi) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CXVSCROLL, (uint)dpi); + } else { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXVSCROLL); + } + } + + /// + /// + /// + /// Gets the height of the horizontal scroll bar in pixels. + /// + /// + public static int HorizontalScrollBarHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYHSCROLL); + } + } + + /// + /// + /// + /// Gets the height of the horizontal scroll bar in pixels. + /// + /// + public static int GetHorizontalScrollBarHeightForDpi(int dpi) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CYHSCROLL, (uint)dpi); + } else { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYHSCROLL); + } + } + + /// + /// + /// + /// Gets the height of the normal caption area of a window in pixels. + /// + /// + public static int CaptionHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYCAPTION); + } + } + + /// + /// + /// + /// Gets the width and + /// height of a window border in pixels. + /// + /// + public static Size BorderSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXBORDER), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYBORDER)); + } + } + + /// + /// + /// + /// Gets the width and + /// height of a window border in pixels. + /// + /// + public static Size GetBorderSizeForDpi(int dpi) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return new Size(UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CXBORDER, (uint)dpi), + UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CYBORDER, (uint)dpi)); + } else { + return BorderSize; + } + } + + /// + /// + /// + /// Gets the thickness in pixels, of the border for a window that has a caption + /// and is not resizable. + /// + /// + public static Size FixedFrameBorderSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXFIXEDFRAME), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYFIXEDFRAME)); + } + } + + /// + /// + /// Gets the height of the scroll box in a vertical scroll bar in pixels. + /// + public static int VerticalScrollBarThumbHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYVTHUMB); + } + } + + /// + /// + /// Gets the width of the scroll box in a horizontal scroll bar in pixels. + /// + public static int HorizontalScrollBarThumbWidth { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXHTHUMB); + } + } +/* + /// + public static Font IconFont { + get { + Font iconFont = IconFontInternal; + return iconFont != null ? iconFont : Control.DefaultFont; + } + } + + // IconFontInternal is the same as IconFont, only it does not fall back to Control.DefaultFont + // if the icon font can not be retrieved. It returns null instead. + internal static Font IconFontInternal { + get { + Font iconFont = null; + + NativeMethods.ICONMETRICS data = new NativeMethods.ICONMETRICS(); + bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETICONMETRICS, data.cbSize, data, 0); + + Debug.Assert(!result || data.iHorzSpacing == IconHorizontalSpacing, "Spacing in ICONMETRICS does not match IconHorizontalSpacing."); + Debug.Assert(!result || data.iVertSpacing == IconVerticalSpacing, "Spacing in ICONMETRICS does not match IconVerticalSpacing."); + + if (result && data.lfFont != null) { + try { + iconFont = Font.FromLogFont(data.lfFont); + } + catch {} + } + return iconFont; + } + } +*/ + + /// + /// + /// + /// Gets the default dimensions of an icon in pixels. + /// + /// + public static Size IconSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXICON), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYICON)); + } + } + + /// + /// + /// + /// Gets the dimensions of a cursor in pixels. + /// + /// + public static Size CursorSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXCURSOR), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYCURSOR)); + } + } + + /// + /// + /// + /// Gets the system's font for menus. + /// + /// + public static Font MenuFont { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + return GetMenuFontHelper(0, false); + } + } + + /// + /// + /// + /// Gets the system's font for menus, scaled accordingly to an arbitrary DPI you provide. + /// + /// + public static Font GetMenuFontForDpi(int dpi) { + return GetMenuFontHelper((uint)dpi, DpiHelper.EnableDpiChangedMessageHandling); + } + + private static Font GetMenuFontHelper(uint dpi, bool useDpi) { + Font menuFont = null; + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + bool result; + if (useDpi) { + result = UnsafeNativeMethods.TrySystemParametersInfoForDpi(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0, dpi); + } else { + result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + } + + if (result && data.lfMenuFont != null) { + // SECREVIEW : This assert is safe since we created the NONCLIENTMETRICS struct. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + menuFont = Font.FromLogFont(data.lfMenuFont); + } + catch { + // menu font is not true type. Default to standard control font. + // + menuFont = Control.DefaultFont; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + return menuFont; + } + + /// + /// + /// + /// Gets the height of a one line of a menu in pixels. + /// + /// + public static int MenuHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMENU); + } + } + + + /// + /// + /// + /// Returns the current system power status. + /// + /// + public static PowerStatus PowerStatus + { + get + { + if (powerStatus == null) { + powerStatus = new PowerStatus(); + } + return powerStatus; + } + } + + + /// + /// + /// + /// Gets the size of the working area in pixels. + /// + /// + public static Rectangle WorkingArea { + get { + NativeMethods.RECT rc = new NativeMethods.RECT(); + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETWORKAREA, 0, ref rc, 0); + return Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + } + + /// + /// + /// + /// Gets + /// the height, in pixels, of the Kanji window at the bottom + /// of the screen for double-byte (DBCS) character set versions of Windows. + /// + /// + public static int KanjiWindowHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYKANJIWINDOW); + } + } + + /// + /// + /// + /// Gets a value indicating whether the system has a mouse installed. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool MousePresent { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_MOUSEPRESENT) != 0; + } + } + + /// + /// + /// + /// Gets the height in pixels, of the arrow bitmap on the vertical scroll bar. + /// + /// + public static int VerticalScrollBarArrowHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYVSCROLL); + } + } + + /// + /// Gets the height of the vertical scroll bar arrow bitmap in pixels. + /// + /// + /// + public static int VerticalScrollBarArrowHeightForDpi(int dpi) { + return UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CXHSCROLL, (uint)dpi); + } + /// + /// + /// + /// Gets the width, in pixels, of the arrow bitmap on the horizontal scrollbar. + /// + /// + public static int HorizontalScrollBarArrowWidth { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXHSCROLL); + } + } + + /// + /// + /// + /// Gets the width of the horizontal scroll bar arrow bitmap in pixels. + /// + /// + public static int GetHorizontalScrollBarArrowWidthForDpi(int dpi) { + if (DpiHelper.EnableDpiChangedMessageHandling) { + return UnsafeNativeMethods.TryGetSystemMetricsForDpi(NativeMethods.SM_CXHSCROLL, (uint)dpi); + } + else { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXHSCROLL); + } + } + + /// + /// + /// + /// Gets a value indicating whether this is a debug version of the operating + /// system. + /// + /// + public static bool DebugOS { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SensitiveSystemInformation Demanded"); + IntSecurity.SensitiveSystemInformation.Demand(); + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_DEBUG) != 0; + } + } + + /// + /// + /// + /// Gets a value indicating whether the functions of the left and right mouse + /// buttons have been swapped. + /// + /// + public static bool MouseButtonsSwapped { + get { + return(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SWAPBUTTON) != 0); + } + } + + /// + /// + /// + /// Gets the minimum allowable dimensions of a window in pixels. + /// + /// + public static Size MinimumWindowSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMIN), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMIN)); + } + } + + /// + /// + /// + /// Gets the dimensions in pixels, of a caption bar or title bar + /// button. + /// + /// + public static Size CaptionButtonSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSIZE), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSIZE)); + } + } + + /// + /// + /// + /// Gets the thickness in pixels, of the border for a window that can be resized. + /// + /// + public static Size FrameBorderSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXFRAME), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYFRAME)); + } + } + + /// + /// + /// + /// Gets the system's default + /// minimum tracking dimensions of a window in pixels. + /// + /// + public static Size MinWindowTrackSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMINTRACK), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMINTRACK)); + } + } + + /// + /// + /// + /// Gets the dimensions in pixels, of the area that the user must click within + /// for the system to consider the two clicks a double-click. The rectangle is + /// centered around the first click. + /// + /// + public static Size DoubleClickSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXDOUBLECLK), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYDOUBLECLK)); + } + } + + /// + /// + /// + /// Gets the maximum number of milliseconds allowed between mouse clicks for a + /// double-click. + /// + /// + public static int DoubleClickTime { + get { + return SafeNativeMethods.GetDoubleClickTime(); + } + } + + /// + /// + /// + /// Gets + /// the dimensions in pixels, of the grid used + /// to arrange icons in a large icon view. + /// + /// + public static Size IconSpacingSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXICONSPACING), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYICONSPACING)); + } + } + + /// + /// + /// + /// Gets a value indicating whether drop down menus should be right-aligned with + /// the corresponding menu bar item. + /// + /// + public static bool RightAlignedMenus { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_MENUDROPALIGNMENT) != 0; + } + } + + /// + /// + /// + /// Gets a value indicating whether the Microsoft Windows for Pen computing + /// extensions are installed. + /// + /// + public static bool PenWindows { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_PENWINDOWS) != 0; + } + } + + /// + /// + /// + /// Gets a value indicating whether the operating system is capable of handling + /// double-byte (DBCS) characters. + /// + /// + public static bool DbcsEnabled { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_DBCSENABLED) != 0; + } + } + + /// + /// + /// + /// Gets the number of buttons on mouse. + /// + /// + public static int MouseButtons { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CMOUSEBUTTONS); + } + } + + /// + /// + /// + /// Gets a value indicating whether security is present on this operating system. + /// + /// + public static bool Secure { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SensitiveSystemInformation Demanded"); + IntSecurity.SensitiveSystemInformation.Demand(); + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SECURE) != 0; + } + } + + /// + /// + /// + /// Gets the dimensions in pixels, of a 3-D + /// border. + /// + /// + public static Size Border3DSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXEDGE), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYEDGE)); + } + } + + /// + /// + /// Gets the dimensions + /// in pixels, of + /// the grid into which minimized windows will be placed. + /// + public static Size MinimizedWindowSpacingSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMINSPACING), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMINSPACING)); + } + } + + /// + /// + /// + /// Gets + /// the recommended dimensions of a small icon in pixels. + /// + /// + public static Size SmallIconSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSMICON), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSMICON)); + } + } + + /// + /// + /// + /// Gets the height of + /// a small caption in pixels. + /// + /// + public static int ToolWindowCaptionHeight { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSMCAPTION); + } + } + + /// + /// + /// + /// Gets the + /// dimensions of small caption buttons in pixels. + /// + /// + public static Size ToolWindowCaptionButtonSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSMSIZE), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSMSIZE)); + } + } + + /// + /// + /// + /// Gets + /// the dimensions in pixels, of menu bar buttons. + /// + /// + public static Size MenuButtonSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMENUSIZE), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMENUSIZE)); + } + } + + /// + /// + /// Gets flags specifying how the system arranges minimized windows. + /// + public static ArrangeStartingPosition ArrangeStartingPosition { + get { + ArrangeStartingPosition mask = ArrangeStartingPosition.BottomLeft | ArrangeStartingPosition.BottomRight | ArrangeStartingPosition.Hide | ArrangeStartingPosition.TopLeft | ArrangeStartingPosition.TopRight; + int compoundValue = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_ARRANGE); + return mask & (ArrangeStartingPosition) compoundValue; + } + } + + /// + /// + /// + /// Gets flags specifying how the system arranges minimized windows. + /// + /// + public static ArrangeDirection ArrangeDirection { + get { + ArrangeDirection mask = ArrangeDirection.Down | ArrangeDirection.Left | ArrangeDirection.Right | ArrangeDirection.Up; + int compoundValue = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_ARRANGE); + return mask & (ArrangeDirection) compoundValue; + } + } + + /// + /// + /// + /// Gets the dimensions in pixels, of a normal minimized window. + /// + /// + public static Size MinimizedWindowSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMINIMIZED), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMINIMIZED)); + } + } + + /// + /// + /// + /// Gets the default maximum dimensions in pixels, of a + /// window that has a caption and sizing borders. + /// + /// + public static Size MaxWindowTrackSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMAXTRACK), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMAXTRACK)); + } + } + + /// + /// + /// + /// Gets the default dimensions, in pixels, of a maximized top-left window on the + /// primary monitor. + /// + /// + public static Size PrimaryMonitorMaximizedWindowSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMAXIMIZED), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMAXIMIZED)); + } + } + + /// + /// + /// + /// Gets a value indicating whether this computer is connected to a network. + /// + /// + public static bool Network { + get { + return(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_NETWORK) & 0x00000001) != 0; + } + } + + /// + /// + /// + /// To be supplied. + /// + /// + public static bool TerminalServerSession { + get { + return(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_REMOTESESSION) & 0x00000001) != 0; + } + } + + + + /// + /// + /// + /// Gets a value that specifies how the system was started. + /// + /// + public static BootMode BootMode { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SensitiveSystemInformation Demanded"); + IntSecurity.SensitiveSystemInformation.Demand(); + return(BootMode) UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CLEANBOOT); + } + } + + /// + /// + /// + /// Gets the dimensions in pixels, of the rectangle that a drag operation + /// must extend to be considered a drag. The rectangle is centered on a drag + /// point. + /// + /// + public static Size DragSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXDRAG), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYDRAG)); + } + } + + /// + /// + /// + /// Gets a value indicating whether the user requires an application to present + /// information visually in situations where it would otherwise present the + /// information in audible form. + /// + /// + public static bool ShowSounds { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SHOWSOUNDS) != 0; + } + } + + /// + /// + /// + /// Gets the + /// dimensions of the default size of a menu checkmark in pixels. + /// + /// + public static Size MenuCheckSize { + get { + return new Size(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXMENUCHECK), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYMENUCHECK)); + } + } + + /// + /// + /// + /// Gets a value + /// indicating whether the system is enabled for Hebrew and Arabic languages. + /// + /// + public static bool MidEastEnabled { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_MIDEASTENABLED) != 0; + } + } + + private static bool MultiMonitorSupport { + get { + if (!checkMultiMonitorSupport) { + multiMonitorSupport = (UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CMONITORS) != 0); + checkMultiMonitorSupport = true; + } + return multiMonitorSupport; + } + } + + /// + /// + /// + /// Gets a value indicating whether the system natively supports the mouse wheel + /// in newer mice. + /// + /// + public static bool NativeMouseWheelSupport { + get { + if (!checkNativeMouseWheelSupport) { + nativeMouseWheelSupport = (UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_MOUSEWHEELPRESENT) != 0); + checkNativeMouseWheelSupport = true; + } + return nativeMouseWheelSupport; + } + } + + + /// + /// + /// + /// Gets a value indicating whether there is a mouse with a mouse wheel + /// installed on this machine. + /// + /// + public static bool MouseWheelPresent { + get { + + bool mouseWheelPresent = false; + + if (!NativeMouseWheelSupport) { + IntPtr hWndMouseWheel = IntPtr.Zero; + + // Check for the MouseZ "service". This is a little app that generated the + // MSH_MOUSEWHEEL messages by monitoring the hardware. If this app isn't + // found, then there is no support for MouseWheels on the system. + // + hWndMouseWheel = UnsafeNativeMethods.FindWindow(NativeMethods.MOUSEZ_CLASSNAME, NativeMethods.MOUSEZ_TITLE); + + if (hWndMouseWheel != IntPtr.Zero) { + mouseWheelPresent = true; + } + } + else { + mouseWheelPresent = (UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_MOUSEWHEELPRESENT) != 0); + } + return mouseWheelPresent; + } + } + + + /// + /// + /// + /// Gets the + /// bounds of the virtual screen. + /// + /// + public static Rectangle VirtualScreen { + get { + if (MultiMonitorSupport) { + return new Rectangle(UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_XVIRTUALSCREEN), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_YVIRTUALSCREEN), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXVIRTUALSCREEN), + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYVIRTUALSCREEN)); + } + else { + Size size = PrimaryMonitorSize; + return new Rectangle(0, 0, size.Width, size.Height); + } + } + } + + + /// + /// + /// + /// Gets the number of display monitors on the desktop. + /// + /// + public static int MonitorCount { + get { + if (MultiMonitorSupport) { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CMONITORS); + } + else { + return 1; + } + } + } + + + /// + /// + /// + /// Gets a value indicating whether all the display monitors have the + /// same color format. + /// + /// + public static bool MonitorsSameDisplayFormat { + get { + if (MultiMonitorSupport) { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SAMEDISPLAYFORMAT) != 0; + } + else { + return true; + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // Misc + // + + + /// + /// + /// + /// Gets the computer name of the current system. + /// + /// + public static string ComputerName { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SensitiveSystemInformation Demanded"); + IntSecurity.SensitiveSystemInformation.Demand(); + + StringBuilder sb = new StringBuilder(256); + UnsafeNativeMethods.GetComputerName(sb, new int[] {sb.Capacity}); + return sb.ToString(); + } + } + + + /// + /// + /// Gets the user's domain name. + /// + public static string UserDomainName { + get { + return Environment.UserDomainName; + } + } + + /// + /// + /// + /// Gets a value indicating whether the current process is running in user + /// interactive mode. + /// + /// + public static bool UserInteractive { + get { + if (Environment.OSVersion.Platform == System.PlatformID.Win32NT) { + IntPtr hwinsta = IntPtr.Zero; + + hwinsta = UnsafeNativeMethods.GetProcessWindowStation(); + if (hwinsta != IntPtr.Zero && processWinStation != hwinsta) { + isUserInteractive = true; + + int lengthNeeded = 0; + NativeMethods.USEROBJECTFLAGS flags = new NativeMethods.USEROBJECTFLAGS(); + + if (UnsafeNativeMethods.GetUserObjectInformation(new HandleRef(null, hwinsta), NativeMethods.UOI_FLAGS, flags, Marshal.SizeOf(flags), ref lengthNeeded)) { + if ((flags.dwFlags & NativeMethods.WSF_VISIBLE) == 0) { + isUserInteractive = false; + } + } + processWinStation = hwinsta; + } + } + else { + isUserInteractive = true; + } + return isUserInteractive; + } + } + + /// + /// + /// + /// Gets the user name for the current thread, that is, the name of the + /// user currently logged onto the system. + /// + /// + public static string UserName { + get { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "SensitiveSystemInformation Demanded"); + IntSecurity.SensitiveSystemInformation.Demand(); + + StringBuilder sb = new StringBuilder(256); + UnsafeNativeMethods.GetUserName(sb, new int[] {sb.Capacity}); + return sb.ToString(); + } + } + + private static void EnsureSystemEvents() { + if (!systemEventsAttached) { + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(SystemInformation.OnUserPreferenceChanged); + systemEventsAttached = true; + } + } + + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + systemEventsDirty = true; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // NEW ADDITIONS FOR WHIDBEY // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Gets a value indicating whether the drop shadow effect in enabled. Defaults to false + /// downlevel. + /// + /// + public static bool IsDropShadowEnabled { + get { + if (OSFeature.Feature.OnXp) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETDROPSHADOW, 0, ref data, 0); + return data != 0; + } + return false; + } + } + + /// + /// + /// + /// Gets a value indicating whether the native user menus have a flat menu appearance. Defaults to false + /// downlevel. + /// + /// + public static bool IsFlatMenuEnabled { + get { + if (OSFeature.Feature.OnXp) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETFLATMENU, 0, ref data, 0); + return data != 0; + } + return false; + } + } + + /// + /// + /// + /// Gets a value indicating whether the Font Smoothing OSFeature.Feature is enabled. + /// + /// + public static bool IsFontSmoothingEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETFONTSMOOTHING, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Returns a contrast value that is ClearType smoothing. + /// + /// + public static int FontSmoothingContrast { + get { + if (OSFeature.Feature.OnXp) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETFONTSMOOTHINGCONTRAST, 0, ref data, 0); + return data; + } + else { + throw new NotSupportedException(SR.GetString(SR.SystemInformationFeatureNotSupported)); + } + } + } + + /// + /// + /// + /// Returns a type of Font smoothing. + /// + /// + public static int FontSmoothingType { + get { + if (OSFeature.Feature.OnXp) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETFONTSMOOTHINGTYPE, 0, ref data, 0); + return data; + } + else { + throw new NotSupportedException(SR.GetString(SR.SystemInformationFeatureNotSupported)); + } + } + } + + /// + /// + /// + /// Retrieves the width in pixels of an icon cell. + /// + /// + public static int IconHorizontalSpacing { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_ICONHORIZONTALSPACING, 0, ref data, 0); + return data; + } + } + + /// + /// + /// + /// Retrieves the height in pixels of an icon cell. + /// + /// + public static int IconVerticalSpacing { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_ICONVERTICALSPACING, 0, ref data, 0); + return data; + } + } + + /// + /// + /// + /// Gets a value indicating whether the Icon title wrapping is enabled. + /// + /// + public static bool IsIconTitleWrappingEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETICONTITLEWRAP, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Gets a value indicating whether the menu access keys are always underlined. + /// + /// + public static bool MenuAccessKeysUnderlined { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETKEYBOARDCUES, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Retrieves the Keyboard repeat delay setting, which is a value in the + /// range from 0 through 3. The Actual Delay Associated with each value may vary depending on the + /// hardware. + /// + /// + public static int KeyboardDelay { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETKEYBOARDDELAY, 0, ref data, 0); + return data; + } + } + + /// + /// + /// + /// Gets a value indicating whether the user relies on Keyboard instead of mouse and wants + /// applications to display keyboard interfaces that would be otherwise hidden. + /// + /// + public static bool IsKeyboardPreferred { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETKEYBOARDPREF, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Retrieves the Keyboard repeat speed setting, which is a value in the + /// range from 0 through 31. The actual rate may vary depending on the + /// hardware. + /// + /// + public static int KeyboardSpeed { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETKEYBOARDSPEED, 0, ref data, 0); + return data; + } + } + + /// + /// + /// + /// Gets the Size in pixels of the rectangle within which the mouse pointer has to stay. + /// + /// + public static Size MouseHoverSize { + get { + int height = 0; + int width = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMOUSEHOVERHEIGHT, 0, ref height, 0); + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMOUSEHOVERWIDTH, 0, ref width, 0); + return new Size(width, height); + } + } + + /// + /// + /// + /// Gets the time, in milliseconds, that the mouse pointer has to stay in the hover rectangle. + /// + /// + public static int MouseHoverTime { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMOUSEHOVERTIME, 0, ref data, 0); + return data; + } + + } + + /// + /// + /// + /// Gets the current mouse speed. + /// + /// + public static int MouseSpeed { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMOUSESPEED, 0, ref data, 0); + return data; + } + + } + + + /// + /// + /// + /// Determines whether the snap-to-default-button feature is enabled. + /// + /// + public static bool IsSnapToDefaultEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETSNAPTODEFBUTTON, 0, ref data, 0); + return data != 0; + } + } + + + /// + /// + /// + /// Determines whether the Popup Menus are left Aligned or Right Aligned. + /// + /// + public static LeftRightAlignment PopupMenuAlignment { + get { + bool data = false; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMENUDROPALIGNMENT, 0, ref data, 0); + if (data) { + return LeftRightAlignment.Left; + } + else { + return LeftRightAlignment.Right; + } + + } + } + + /// + /// + /// + /// Determines whether the maenu fade animation feature is enabled. Defaults to false + /// downlevel. + /// + /// + public static bool IsMenuFadeEnabled { + get { + if (OSFeature.Feature.OnXp || OSFeature.Feature.OnWin2k) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMENUFADE, 0, ref data, 0); + return data != 0; + } + return false; + } + } + + /// + /// + /// + /// Indicates the time, in milliseconds, that the system waits before displaying a shortcut menu. + /// + /// + public static int MenuShowDelay { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMENUSHOWDELAY, 0, ref data, 0); + return data; + } + + } + + /// + /// + /// + /// Indicates whether the slide open effect for combo boxes is enabled. + /// + /// + public static bool IsComboBoxAnimationEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETCOMBOBOXANIMATION, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Indicates whether the gradient effect for windows title bars is enabled. + /// + /// + public static bool IsTitleBarGradientEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETGRADIENTCAPTIONS, 0, ref data, 0); + return data != 0; + } + } + + + /// + /// + /// + /// Indicates whether the hot tracking of user interface elements is enabled. + /// + /// + public static bool IsHotTrackingEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETHOTTRACKING, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Indicates whether the smooth scrolling effect for listbox is enabled. + /// + /// + public static bool IsListBoxSmoothScrollingEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETLISTBOXSMOOTHSCROLLING, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Indicates whether the menu animation feature is enabled. + /// + /// + public static bool IsMenuAnimationEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETMENUANIMATION, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Indicates whether the selection fade effect is enabled. Defaults to false + /// downlevel. + /// + /// + public static bool IsSelectionFadeEnabled { + get { + if (OSFeature.Feature.OnXp || OSFeature.Feature.OnWin2k) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETSELECTIONFADE, 0, ref data, 0); + return data != 0; + } + return false; + } + } + + /// + /// + /// + /// Indicates whether the tool tip animation is enabled. Defaults to false + /// downlevel. + /// + /// + public static bool IsToolTipAnimationEnabled { + get { + if (OSFeature.Feature.OnXp || OSFeature.Feature.OnWin2k) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETTOOLTIPANIMATION, 0, ref data, 0); + return data != 0; + } + return false; + + } + } + + /// + /// + /// + /// Indicates whether all the UI Effects are enabled. Defaults to false + /// downlevel. + /// + /// + public static bool UIEffectsEnabled { + get { + if (OSFeature.Feature.OnXp || OSFeature.Feature.OnWin2k) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETUIEFFECTS, 0, ref data, 0); + return data != 0; + } + return false; + } + } + + + /// + /// + /// + /// Indicates whether the active windows tracking (activating the window the mouse in on) is ON or OFF. + /// + /// + public static bool IsActiveWindowTrackingEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETACTIVEWINDOWTRACKING, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Retrieves the active window tracking delay, in milliseconds. + /// + /// + public static int ActiveWindowTrackingDelay { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETACTIVEWNDTRKTIMEOUT, 0, ref data, 0); + return data; + } + + } + + + /// + /// + /// + /// Indicates whether the active windows tracking (activating the window the mouse in on) is ON or OFF. + /// + /// + public static bool IsMinimizeRestoreAnimationEnabled { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETANIMATION, 0, ref data, 0); + return data != 0; + } + } + + /// + /// + /// + /// Retrieves the border multiplier factor that determines the width of a windo's sizing border. + /// + /// + public static int BorderMultiplierFactor { + get { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETBORDER, 0, ref data, 0); + return data; + } + + } + + /// + /// + /// + /// Indicates the caret blink time. + /// + /// + public static int CaretBlinkTime { + get { + return unchecked((int)SafeNativeMethods.GetCaretBlinkTime()); + } + + } + + /// + /// + /// + /// Indicates the caret width in edit controls. + /// + /// + public static int CaretWidth { + get { + if (OSFeature.Feature.OnXp || OSFeature.Feature.OnWin2k) { + int data = 0; + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETCARETWIDTH, 0, ref data, 0); + return data; + } + else { + throw new NotSupportedException(SR.GetString(SR.SystemInformationFeatureNotSupported)); + } + } + + } + + /// + /// + /// + /// None. + /// + /// + public static int MouseWheelScrollDelta { + get { + return NativeMethods.WHEEL_DELTA; + } + + } + + + /// + /// + /// + /// The width of the left and right edges of the focus rectangle. + /// + /// + public static int VerticalFocusThickness { + get { + if (OSFeature.Feature.OnXp) { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYFOCUSBORDER); + } + else { + throw new NotSupportedException(SR.GetString(SR.SystemInformationFeatureNotSupported)); + } + } + + } + + /// + /// + /// + /// The width of the top and bottom edges of the focus rectangle. + /// + /// + public static int HorizontalFocusThickness { + get { + if (OSFeature.Feature.OnXp) { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXFOCUSBORDER); + } + else { + throw new NotSupportedException(SR.GetString(SR.SystemInformationFeatureNotSupported)); + } + } + + } + + /// + /// + /// + /// The height of the vertical sizing border around the perimeter of the window that can be resized. + /// + /// + public static int VerticalResizeBorderThickness { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSIZEFRAME); + } + + } + + /// + /// + /// + /// The width of the horizontal sizing border around the perimeter of the window that can be resized. + /// + /// + public static int HorizontalResizeBorderThickness { + get { + return UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSIZEFRAME); + } + + } + + /// + /// + /// + /// The orientation of the screen in degrees. + /// + /// + public static ScreenOrientation ScreenOrientation { + get { + ScreenOrientation so = ScreenOrientation.Angle0; + NativeMethods.DEVMODE dm = new NativeMethods.DEVMODE(); + dm.dmSize = (short) Marshal.SizeOf(typeof(NativeMethods.DEVMODE)); + dm.dmDriverExtra = 0; + try { + SafeNativeMethods.EnumDisplaySettings(null, -1 /*ENUM_CURRENT_SETTINGS*/, ref dm); + if ( (dm.dmFields & NativeMethods.DM_DISPLAYORIENTATION) > 0) { + so = dm.dmDisplayOrientation; + } + } + catch { + // empty catch, we'll just return the default if the call to EnumDisplaySettings fails. + } + + return so; + } + } + + /// + /// + /// + /// Specifies the thikness, in pixels, of the Sizing Border. + /// + /// + public static int SizingBorderWidth { + get { + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iBorderWidth > 0) { + return data.iBorderWidth; + } + else { + return 0; + } + } + } + + /* + /// + /// + /// + /// Specified the width, in pixels, of a standard vertical scrollbar. + /// + /// + public static int VerticalScrollBarWidth { + get { + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iScrollWidth > 0) { + return data.iScrollWidth; + } + else { + return 0; + } + + + } + } + + /// + /// + /// + /// Specified the height, in pixels, of a standard horizontal scrollbar. + /// + /// + public static int HorizontalScrollBarWidth { + get { + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iScrollHeight > 0) { + return data.iScrollHeight; + } + else { + return 0; + } + } + } + + + /// + /// + /// + /// Specified the Size, in pixels, of the caption buttons. + /// + /// + public static Size CaptionButtonSize { + get { + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iCaptionHeight > 0 && data.iCaptionWidth > 0) { + return new Size(data.iCaptionWidth, data.iCaptionHeight); + } + else { + return return new Size(0, 0); + } + + + } + } + */ + + /// + /// + /// + /// Specified the Size, in pixels, of the small caption buttons. + /// + /// + public static Size SmallCaptionButtonSize { + get { + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iSmCaptionHeight > 0 && data.iSmCaptionWidth > 0) { + return new Size(data.iSmCaptionWidth, data.iSmCaptionHeight); + } + else { + return Size.Empty; + } + + + } + } + + /// + /// + /// + /// Specified the Size, in pixels, of the menu bar buttons. + /// + /// + public static Size MenuBarButtonSize { + get { + + //we can get the system's menu font through the NONCLIENTMETRICS structure via SystemParametersInfo + // + NativeMethods.NONCLIENTMETRICS data = new NativeMethods.NONCLIENTMETRICS(); + bool result = UnsafeNativeMethods.SystemParametersInfo(NativeMethods.SPI_GETNONCLIENTMETRICS, data.cbSize, data, 0); + if (result && data.iMenuHeight > 0 && data.iMenuWidth > 0) { + return new Size(data.iMenuWidth, data.iMenuHeight); + } + else { + return Size.Empty; + } + + + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // End ADDITIONS FOR WHIDBEY // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// + /// Checks whether the current Microsoft app is running on a secure desktop under a terminal + /// server session. This is the case when the TS session has been locked. + /// This method is useful when calling into GDI+ Graphics methods that modify the object's + /// state, these methods fail under a locked terminal session. + /// + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + internal static bool InLockedTerminalSession() { + bool retVal = false; + + if (SystemInformation.TerminalServerSession) { + // Let's try to open the input desktop, it it fails with access denied assume + // the app is running on a secure desktop. + IntPtr hDsk = SafeNativeMethods.OpenInputDesktop(0, false, NativeMethods.DESKTOP_SWITCHDESKTOP); + + if (hDsk == IntPtr.Zero) { + int error = Marshal.GetLastWin32Error(); + retVal = error == NativeMethods.ERROR_ACCESS_DENIED; + } + + if (hDsk != IntPtr.Zero) { + SafeNativeMethods.CloseDesktop(hDsk); + } + } + + return retVal; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/SystemParameter.cs b/WindowsForms/Managed/System/WinForms/SystemParameter.cs new file mode 100644 index 000000000..de5aee3c6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/SystemParameter.cs @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the + /// SystemParameterType. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public enum SystemParameter { + + /// + /// + /// + /// DropShadow. + /// + /// + DropShadow = 0, + + /// + /// + /// + /// Flat Menu. + /// + /// + FlatMenu = 1, + + /// + /// + /// + /// FontSmoothingContrastMetric. + /// + /// + FontSmoothingContrastMetric = 2, + + /// + /// + /// + /// FontSmoothingTypeMetric. + /// + /// + FontSmoothingTypeMetric = 3, + + /// + /// + /// + /// MenuFadeEnabled. + /// + /// + MenuFadeEnabled = 4, + + /// + /// + /// + /// SelectionFade. + /// + /// + SelectionFade = 5, + + /// + /// + /// + /// ToolTipAnimationMetric. + /// + /// + ToolTipAnimationMetric = 6, + + /// + /// + /// + /// UIEffects. + /// + /// + UIEffects = 7, + + /// + /// + /// + /// CaretWidthMetric. + /// + /// + CaretWidthMetric = 8, + + /// + /// + /// + /// VerticalFocusThicknessMetric. + /// + /// + VerticalFocusThicknessMetric = 9, + + /// + /// + /// + /// HorizontalFocusThicknessMetric. + /// + /// + HorizontalFocusThicknessMetric = 10, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TabAlignment.cs b/WindowsForms/Managed/System/WinForms/TabAlignment.cs new file mode 100644 index 000000000..abee19c83 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabAlignment.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// Controls where the tabs will be located in a Tab Control. + /// + public enum TabAlignment { + + /// + /// + /// Tabs will be located across the top of the control. + /// + Top = 0, + + /// + /// + /// Tabs will be located across the bottom of the control. + /// + Bottom = 1, + + /// + /// + /// Tabs will be located along the left edge of the control. + /// + Left = 2, + + /// + /// + /// Tabs will be located along the right edge of the control. + /// + Right = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabAppearance.cs b/WindowsForms/Managed/System/WinForms/TabAppearance.cs new file mode 100644 index 000000000..03ec568c3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabAppearance.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// The various tab controls will let you configure their appearance. This + /// enumeration contains the possible values. + /// + public enum TabAppearance { + + /// + /// + /// Indicates that the tabs look like normal tabs typically seen in Property + /// page type situations. + /// + Normal = 0, + + /// + /// + /// Indicates that the tabs look like buttons as seen on the taskbar found + /// in Windows 95 or Windows NT. + /// + Buttons = 1, + + /// + /// + /// Indicates that buttons should be draw flat instead of like regular + /// windows pushbuttons. + /// + FlatButtons = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabControl.cs b/WindowsForms/Managed/System/WinForms/TabControl.cs new file mode 100644 index 000000000..9d14fc12f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControl.cs @@ -0,0 +1,2804 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + + + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Configuration.Assemblies; + using Microsoft.Win32; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Design; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.Serialization.Formatters; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + using System.Globalization; + + /// + /// + /// The TabControl. This control has a lot of the functionality of a TabStrip + /// but manages a list of TabPages which are the 'pages' that appear on each tab. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("TabPages"), + DefaultEvent("SelectedIndexChanged"), + Designer("System.Windows.Forms.Design.TabControlDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionTabControl) + ] + public class TabControl : Control { + + private static readonly Size DEFAULT_ITEMSIZE = Size.Empty; + private static readonly Point DEFAULT_PADDING = new Point(6, 3); + + //properties + private TabPageCollection tabCollection; + private TabAlignment alignment = TabAlignment.Top; + private TabDrawMode drawMode = TabDrawMode.Normal; + private ImageList imageList = null; + private Size itemSize = DEFAULT_ITEMSIZE; + private Point padding = DEFAULT_PADDING; + private TabSizeMode sizeMode = TabSizeMode.Normal; + private TabAppearance appearance = TabAppearance.Normal; + private Rectangle cachedDisplayRect = Rectangle.Empty; + private bool currentlyScaling = false; + private int selectedIndex = -1; + private Size cachedSize = Size.Empty; + private string controlTipText = String.Empty; + private bool handleInTable; + + //events + private EventHandler onSelectedIndexChanged; + private DrawItemEventHandler onDrawItem; + + //events + private static readonly object EVENT_DESELECTING = new object(); + private static readonly object EVENT_DESELECTED = new object(); + private static readonly object EVENT_SELECTING = new object(); + private static readonly object EVENT_SELECTED = new object(); + private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object(); + + + private const int TABCONTROLSTATE_hotTrack = 0x00000001; + private const int TABCONTROLSTATE_multiline = 0x00000002; + private const int TABCONTROLSTATE_showToolTips = 0x00000004; + private const int TABCONTROLSTATE_getTabRectfromItemSize = 0x00000008; + private const int TABCONTROLSTATE_fromCreateHandles = 0x00000010; + private const int TABCONTROLSTATE_UISelection = 0x00000020; + private const int TABCONTROLSTATE_selectFirstControl = 0x00000040; + private const int TABCONTROLSTATE_insertingItem = 0x00000080; + private const int TABCONTROLSTATE_autoSize = 0x00000100; + + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 tabControlState; // see TABCONTROLSTATE_ consts above + + + + /// + /// + /// This message is posted by the control to itself after a TabPage is + /// added to it. On certain occasions, after items are added to a + /// TabControl in quick succession, TCM_ADJUSTRECT calls return the wrong + /// display rectangle. When the message is received, the control calls + /// updateTabSelection() to layout the TabPages correctly. + /// + /// + private readonly int tabBaseReLayoutMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_TabBaseReLayout"); + + //state + private TabPage[] tabPages; + private int tabPageCount; + private int lastSelection; + + private bool rightToLeftLayout = false; + private bool skipUpdateSize; + + /// + /// + /// Constructs a TabBase object, usually as the base class for a TabStrip or TabControl. + /// + public TabControl() + : base() { + + tabControlState = new System.Collections.Specialized.BitVector32(0x00000000); + + tabCollection = new TabPageCollection(this); + SetStyle(ControlStyles.UserPaint, false); + } + + /// + /// + /// Returns on what area of the control the tabs reside on (A TabAlignment value). + /// The possibilities are Top (the default), Bottom, Left, and Right. When alignment + /// is left or right, the Multiline property is ignored and Multiline is implicitly on. + /// If the alignment is anything other than top, TabAppearance.FlatButtons degenerates + /// to TabAppearance.Buttons. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(TabAlignment.Top), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.TabBaseAlignmentDescr) + ] + public TabAlignment Alignment { + get { + return alignment; + } + + set { + if (this.alignment != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabAlignment.Top, (int)TabAlignment.Right)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(TabAlignment)); + } + + this.alignment = value; + if (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right) + Multiline = true; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates whether the tabs in the tabstrip look like regular tabs, or if they look + /// like buttons as seen in the Windows 95 taskbar. + /// If the alignment is anything other than top, TabAppearance.FlatButtons degenerates + /// to TabAppearance.Buttons. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(TabAppearance.Normal), + SRDescription(SR.TabBaseAppearanceDescr) + ] + public TabAppearance Appearance { + get { + if (appearance == TabAppearance.FlatButtons && alignment != TabAlignment.Top) { + return TabAppearance.Buttons; + } + else { + return appearance; + } + } + + set { + if (this.appearance != value) { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabAppearance.Normal, (int)TabAppearance.FlatButtons)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(TabAppearance)); + } + + this.appearance = value; + RecreateHandle(); + + //Fire OnStyleChanged(EventArgs.Empty) here since we are no longer calling UpdateStyles( ) but always reCreating the Handle. + OnStyleChanged(EventArgs.Empty); + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + //The tab control can only be rendered in 1 color: System's Control color. + //So, always return this value... otherwise, we're inheriting the forms backcolor + //and passing it on to the pab pages. + return SystemColors.Control; + } + set { + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(200, 100); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_TABCONTROL; + + // set up window styles + // + if (Multiline == true) cp.Style |= NativeMethods.TCS_MULTILINE; + if (drawMode == TabDrawMode.OwnerDrawFixed) cp.Style |= NativeMethods.TCS_OWNERDRAWFIXED; + if (ShowToolTips && !DesignMode) { + cp.Style |= NativeMethods.TCS_TOOLTIPS; + } + + if (alignment == TabAlignment.Bottom || + alignment == TabAlignment.Right) + cp.Style |= NativeMethods.TCS_BOTTOM; + + if (alignment == TabAlignment.Left || + alignment == TabAlignment.Right) + cp.Style |= NativeMethods.TCS_VERTICAL | NativeMethods.TCS_MULTILINE; + + if (tabControlState[TABCONTROLSTATE_hotTrack]) { + cp.Style |= NativeMethods.TCS_HOTTRACK; + } + + if (appearance == TabAppearance.Normal) + cp.Style |= NativeMethods.TCS_TABS; + else { + cp.Style |= NativeMethods.TCS_BUTTONS; + if (appearance == TabAppearance.FlatButtons && alignment == TabAlignment.Top) + cp.Style |= NativeMethods.TCS_FLATBUTTONS; + } + + switch (sizeMode) { + case TabSizeMode.Normal: + cp.Style |= NativeMethods.TCS_RAGGEDRIGHT; + break; + case TabSizeMode.FillToRight: + cp.Style |= NativeMethods.TCS_RIGHTJUSTIFY; + break; + case TabSizeMode.Fixed: + cp.Style |= NativeMethods.TCS_FIXEDWIDTH; + break; + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Form explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + + return cp; + } + } + + /// + /// + /// The rectangle that represents the Area of the tab strip not + /// taken up by the tabs, borders, or anything else owned by the Tab. This + /// is typically the rectangle you want to use to place the individual + /// children of the tab strip. + /// + public override Rectangle DisplayRectangle { + get { + + + // Null out cachedDisplayRect whenever we do anything to change it... + // + if (!cachedDisplayRect.IsEmpty) + return cachedDisplayRect; + + Rectangle bounds = Bounds; + NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(bounds.X, bounds.Y, bounds.Width, bounds.Height); + + + + // We force a handle creation here, because otherwise the DisplayRectangle will be wildly inaccurate + // + if (!IsDisposed) + { + // Since this is called thru the OnResize (and Layout) which is triggered by SetExtent if the TabControl is hosted as + // a ActiveX control, so check if this is ActiveX and dont force Handle Creation here as the native code breaks in this case. + // please Refer to VsWhidbey :: 288940 + if (!IsActiveX) + { + if (!IsHandleCreated) { + CreateHandle(); + } + } + if (IsHandleCreated) + { + SendMessage(NativeMethods.TCM_ADJUSTRECT, 0, ref rect); + } + } + + Rectangle r = Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + + Point p = this.Location; + r.X -= p.X; + r.Y -= p.Y; + + cachedDisplayRect = r; + return r; + } + } + + + /// + /// + /// The drawing mode of the tabs in the tab strip. This will indicate + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(TabDrawMode.Normal), + SRDescription(SR.TabBaseDrawModeDescr) + ] + public TabDrawMode DrawMode { + get { + return drawMode; + } + + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabDrawMode.Normal, (int)TabDrawMode.OwnerDrawFixed)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(TabDrawMode)); + } + + if (drawMode != value) { + drawMode = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates whether the tabs visually change when the mouse passes over them. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TabBaseHotTrackDescr) + ] + public bool HotTrack { + get { + return tabControlState[TABCONTROLSTATE_hotTrack]; + } + + set { + if (HotTrack != value) { + tabControlState[TABCONTROLSTATE_hotTrack] = value; + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Returns the imageList the control points at. This is where tabs that have imageIndex + /// set will get there images from. + /// + [ + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(null), + SRDescription(SR.TabBaseImageListDescr) + ] + public ImageList ImageList { + get { + return imageList; + } + set { + if (this.imageList != value) { + EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + if (imageList != null) { + imageList.RecreateHandle -= recreateHandler; + imageList.Disposed -= disposedHandler; + } + + this.imageList = value; + IntPtr handle = (value != null) ? value.Handle : IntPtr.Zero; + if (IsHandleCreated) + SendMessage(NativeMethods.TCM_SETIMAGELIST, IntPtr.Zero, handle); + + // Update the image list in the tab pages. + foreach (TabPage tabPage in TabPages) { + tabPage.ImageIndexer.ImageList = value; + } + + + if (value != null) { + value.RecreateHandle += recreateHandler; + value.Disposed += disposedHandler; + } + } + } + } + + /// + /// + /// By default, tabs will automatically size themselves to fit their icon, if any, and their label. + /// However, the tab size can be explicity set by setting this property. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.TabBaseItemSizeDescr) + ] + public Size ItemSize { + get { + if (itemSize.IsEmpty) { + + // Obtain the current itemsize of the first tab from the winctl control + // + if (IsHandleCreated) { + tabControlState[TABCONTROLSTATE_getTabRectfromItemSize] = true; + return GetTabRect(0).Size; + } + else { + return DEFAULT_ITEMSIZE; + } + } + else { + return itemSize; + } + } + + set { + if (value.Width < 0 || value.Height < 0) { + throw new ArgumentOutOfRangeException("ItemSize", SR.GetString(SR.InvalidArgument, "ItemSize", value.ToString())); + } + itemSize = value; + ApplyItemSize(); + UpdateSize(); + Invalidate(); + } + } + + /// + /// This private property is set by the TabPageCollection when the user calls "InsertItem". + /// The problem is when InsertItem is called then we add this item to the ControlsCollection (in addition to the TabPageCollection) + /// to keep both the collections is sync. But the controlCollection.Add is overriden to again ADD the item to the TabPageCollection. + /// So we keep this flag in order to aviod repeatd addition (only during insert) + /// When the Add ends ... we reset this flag. + /// + private bool InsertingItem { + get { + return (bool)tabControlState[TABCONTROLSTATE_insertingItem]; + } + set { + tabControlState[TABCONTROLSTATE_insertingItem] = value; + } + } + + /// + /// + /// Indicates if there can be more than one row of tabs. By default [when + /// this property is false], if there are more tabs than available display + /// space, arrows are shown to let the user navigate between the extra + /// tabs, but only one row is shown. If this property is set to true, then + /// Windows spills extra tabs over on to second rows. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TabBaseMultilineDescr) + ] + public bool Multiline { + get { + return tabControlState[TABCONTROLSTATE_multiline]; + } + set { + if (Multiline != value) { + tabControlState[TABCONTROLSTATE_multiline] = value; + if (Multiline == false && (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right)) + this.alignment = TabAlignment.Top; + RecreateHandle(); + } + } + } + + /// + /// + /// The amount of padding around the items in the individual tabs. + /// You can specify both horizontal and vertical padding. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.TabBasePaddingDescr) + ] + public new Point Padding { + get { + return padding; + } + set { + //do some validation checking here, against min & max GridSize + // + if ( value.X < 0 || value.Y < 0 ) + throw new ArgumentOutOfRangeException("Padding", SR.GetString(SR.InvalidArgument, "Padding", value.ToString())); + + if (padding != value) { + padding = value; + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + + + /// + /// + /// The number of rows currently being displayed in + /// the tab strip. This is most commonly used when the Multline property + /// is 'true' and you want to know how many rows the tabs are currently + /// taking up. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TabBaseRowCountDescr) + ] + public int RowCount { + get { + int n; + n = unchecked( (int) (long)SendMessage(NativeMethods.TCM_GETROWCOUNT, 0, 0)); + return n; + } + } + + /// + /// + /// The index of the currently selected tab in the strip, if there + /// is one. If the value is -1, there is currently no selection. If the + /// value is 0 or greater, than the value is the index of the currently + /// selected tab. + /// + [ + Browsable(false), + SRCategory(SR.CatBehavior), + DefaultValue(-1), + SRDescription(SR.selectedIndexDescr) + ] + public int SelectedIndex { + get { + if (IsHandleCreated) { + int n; + n = unchecked( (int) (long)SendMessage(NativeMethods.TCM_GETCURSEL, 0, 0)); + return n; + } + else { + return selectedIndex; + } + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectedIndex", value.ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + if (SelectedIndex != value) { + if (IsHandleCreated) { + // Guard Against CreateHandle .. + // And also if we are setting SelectedIndex ourselves from SelectNextTab.. + if (!tabControlState[TABCONTROLSTATE_fromCreateHandles] && !tabControlState[TABCONTROLSTATE_selectFirstControl]) { + tabControlState[TABCONTROLSTATE_UISelection] = true; + // Fire Deselecting .. Deselected on currently selected TabPage... + if (WmSelChanging()) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + if(ValidationCancelled) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + } + + SendMessage(NativeMethods.TCM_SETCURSEL, value, 0); + + if (!tabControlState[TABCONTROLSTATE_fromCreateHandles] && !tabControlState[TABCONTROLSTATE_selectFirstControl]) { + // Fire Selecting & Selected .. Also if Selecting is Canceled.. + // then retuern as we do not change the SelectedIndex... + tabControlState[TABCONTROLSTATE_selectFirstControl] = true; + if (WmSelChange ()) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + tabControlState[TABCONTROLSTATE_selectFirstControl] = false; + return; + } + else + { + tabControlState[TABCONTROLSTATE_selectFirstControl] = false; + } + } + } + else { + selectedIndex = value; + } + } + } + } + + /// + /// + /// The selection to the given tab, provided it .equals a tab in the + /// list. The return value is the index of the tab that was selected, + /// or -1 if no tab was selected. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TabControlSelectedTabDescr) + ] + public TabPage SelectedTab { + get { + return SelectedTabInternal; + } + set { + SelectedTabInternal = value; + } + } + + internal TabPage SelectedTabInternal { + get { + int index = SelectedIndex; + if (index == -1) { + return null; + } + else { + Debug.Assert(0 <= index && index < tabPages.Length, "SelectedIndex returned an invalid index"); + return tabPages[index]; + } + } + set { + int index = FindTabPage(value); + SelectedIndex = index; + } + } + + /// + /// + /// By default, tabs are big enough to display their text, and any space + /// on the right of the strip is left as such. However, you can also + /// set it such that the tabs are stretched to fill out the right extent + /// of the strip, if necessary, or you can set it such that all tabs + /// the same width. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(TabSizeMode.Normal), + SRDescription(SR.TabBaseSizeModeDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public TabSizeMode SizeMode { + get { + return sizeMode; + } + set { + if (sizeMode == value) return; + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabSizeMode.Normal, (int)TabSizeMode.Fixed)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(TabSizeMode)); + } + + sizeMode = value; + RecreateHandle(); + } + } + + /// + /// + /// Indicates whether tooltips are being shown for tabs that have tooltips set on + /// them. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.TabBaseShowToolTipsDescr) + ] + public bool ShowToolTips { + get { + return tabControlState[TABCONTROLSTATE_showToolTips]; + } + set { + if (ShowToolTips != value) { + tabControlState[TABCONTROLSTATE_showToolTips] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Returns the number of tabs in the strip + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TabBaseTabCountDescr) + ] + public int TabCount { + get { return tabPageCount;} + } + + /// + /// + /// Returns the Collection of TabPages. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.TabControlTabsDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Editor("System.Windows.Forms.Design.TabPageCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + MergableProperty(false) + ] + public TabPageCollection TabPages { + get { + return tabCollection; + } + } + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] + public event DrawItemEventHandler DrawItem { + add { + onDrawItem += value; + } + remove { + onDrawItem -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + onSelectedIndexChanged += value; + } + remove { + onSelectedIndexChanged -= value; + } + } + + /// + /// + /// + /// Occurs before a tabpage is selected as the top tabPage. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.TabControlSelectingEventDescr) + ] + public event TabControlCancelEventHandler Selecting { + add { + Events.AddHandler(EVENT_SELECTING, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTING, value); + } + } + + /// + /// + /// + /// Occurs after a tabpage is selected as the top tabPage. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.TabControlSelectedEventDescr) + ] + public event TabControlEventHandler Selected { + add { + Events.AddHandler(EVENT_SELECTED, value); + } + remove { + Events.RemoveHandler(EVENT_SELECTED, value); + } + } + + /// + /// + /// + /// Occurs before the visible property of the top tabpage is set to false. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.TabControlDeselectingEventDescr) + ] + public event TabControlCancelEventHandler Deselecting { + add { + Events.AddHandler(EVENT_DESELECTING, value); + } + remove { + Events.RemoveHandler(EVENT_DESELECTING, value); + } + } + + /// + /// + /// + /// Occurs after the visible property of the top tabpage is set to false. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.TabControlDeselectedEventDescr) + ] + public event TabControlEventHandler Deselected { + add { + Events.AddHandler(EVENT_DESELECTED, value); + } + remove { + Events.RemoveHandler(EVENT_DESELECTED, value); + } + } + + /// + /// + /// TabControl Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// + /// + internal int AddTabPage(TabPage tabPage, NativeMethods.TCITEM_T tcitem) { + int index = AddNativeTabPage(tcitem); + if (index >= 0) { + Insert(index, tabPage); + + } + return index; + } + + internal int AddNativeTabPage(NativeMethods.TCITEM_T tcitem) { + int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_INSERTITEM, tabPageCount + 1, tcitem); + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), tabBaseReLayoutMessage, IntPtr.Zero, IntPtr.Zero); + return index; + } + + /// + /// + /// + /// + internal void ApplyItemSize() { + if (IsHandleCreated && ShouldSerializeItemSize()) { + SendMessage(NativeMethods.TCM_SETITEMSIZE, 0, (int)NativeMethods.Util.MAKELPARAM(itemSize.Width, itemSize.Height)); + } + cachedDisplayRect = Rectangle.Empty; + } + + /// + /// + /// + /// + internal void BeginUpdate() { + BeginUpdateInternal(); + } + + /// + /// + /// + /// + protected override Control.ControlCollection CreateControlsInstance() { + return new ControlCollection(this); + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + private void DetachImageList(object sender, EventArgs e) { + ImageList = null; + } + + + /// + /// + /// Allows the user to specify the index in Tabcontrol.TabPageCollection of the tabpage to be hidden. + /// + public void DeselectTab(int index) { + TabPage t = GetTabPage(index); + if (SelectedTab == t) { + if (0 <= index && index < TabPages.Count -1) { + SelectedTab = GetTabPage(++index); + } + else { + SelectedTab = GetTabPage(0); + } + } + } + + /// + /// + /// Allows the user to specify the tabpage in Tabcontrol.TabPageCollection to be hidden. + /// + public void DeselectTab(TabPage tabPage) { + if (tabPage == null) { + throw new ArgumentNullException("tabPage"); + + } + int index = FindTabPage(tabPage); + DeselectTab(index); + } + + /// + /// + /// Allows the user to specify the name of the tabpage in Tabcontrol.TabPageCollection to be hidden. + /// + public void DeselectTab(string tabPageName) { + if (tabPageName == null) { + throw new ArgumentNullException("tabPageName"); + + } + TabPage tabPage = TabPages[tabPageName]; + DeselectTab(tabPage); + } + + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (imageList != null) { + imageList.Disposed -= new EventHandler(this.DetachImageList); + } + } + base.Dispose(disposing); + } + + internal void EndUpdate() { + EndUpdate(true); + } + + internal void EndUpdate(bool invalidate) { + EndUpdateInternal(invalidate); + } + + internal int FindTabPage(TabPage tabPage) { + if (tabPages != null) { + for (int i = 0; i < tabPageCount; i++) { + if (tabPages[i].Equals(tabPage)) { + return i; + } + } + } + return -1; + } + + /// + /// + /// + /// + public Control GetControl(int index) { + return(Control) GetTabPage(index); + } + + internal TabPage GetTabPage(int index) { + + if (index < 0 || index >= tabPageCount) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + } + return tabPages[index]; + } + + /// + /// + /// This has package scope so that TabStrip and TabControl can call it. + /// + /// + protected virtual object[] GetItems() { + TabPage[] result = new TabPage[tabPageCount]; + if (tabPageCount > 0) Array.Copy(tabPages, 0, result, 0, tabPageCount); + return result; + } + + /// + /// + /// This has package scope so that TabStrip and TabControl can call it. + /// + /// + protected virtual object[] GetItems(Type baseType) { + object[] result = (object[]) Array.CreateInstance(baseType, tabPageCount); + if (tabPageCount > 0) Array.Copy(tabPages, 0, result, 0, tabPageCount); + return result; + } + + internal TabPage[] GetTabPages() { + return (TabPage[])GetItems(); + } + + /// + /// + /// Retrieves the bounding rectangle for the given tab in the tab strip. + /// + public Rectangle GetTabRect(int index) { + if (index < 0 || (index >= tabPageCount && !tabControlState[TABCONTROLSTATE_getTabRectfromItemSize])) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + } + tabControlState[TABCONTROLSTATE_getTabRectfromItemSize] = false ; + NativeMethods.RECT rect = new NativeMethods.RECT(); + + // normally, we would not want to create the handle for this, but since + // it is dependent on the actual physical display, we simply must. + if (!IsHandleCreated) + CreateHandle(); + + SendMessage(NativeMethods.TCM_GETITEMRECT, index, ref rect); + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// + protected string GetToolTipText(object item) { + return((TabPage)item).ToolTipText; + } + + private void ImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) + SendMessage(NativeMethods.TCM_SETIMAGELIST, 0, ImageList.Handle); + } + + /// + /// + /// + /// + internal void Insert(int index, TabPage tabPage) { + if (tabPages == null) { + tabPages = new TabPage[4]; + } + else if (tabPages.Length == tabPageCount) { + TabPage[] newTabPages = new TabPage[tabPageCount * 2]; + Array.Copy(tabPages, 0, newTabPages, 0, tabPageCount); + tabPages = newTabPages; + } + if (index < tabPageCount) { + Array.Copy(tabPages, index, tabPages, index + 1, tabPageCount - index); + } + tabPages[index] = tabPage; + tabPageCount++; + cachedDisplayRect = Rectangle.Empty; + ApplyItemSize(); + if (Appearance == TabAppearance.FlatButtons) { + Invalidate(); + } + } + + /// + /// + /// This function is used by the Insert Logic to insert a tabPage in the current TabPage in the TabPageCollection. + /// + /// + private void InsertItem(int index, TabPage tabPage) { + + if (index < 0 || ((tabPages != null) && index > tabPageCount)) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + if (tabPage == null) + throw new ArgumentNullException("tabPage"); + + + int retIndex; + if (IsHandleCreated) { + NativeMethods.TCITEM_T tcitem = tabPage.GetTCITEM(); + retIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_INSERTITEM, index, tcitem); + if (retIndex >= 0) Insert(retIndex, tabPage); + } + + } + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// This is a notification that the handle has been created. + /// We do some work here to configure the handle. + /// Overriders should call base.OnHandleCreated() + /// + /// + protected override void OnHandleCreated(EventArgs e) { + + //Add the handle to hashtable for Ids .. + NativeWindow.AddWindowToIDTable(this, this.Handle); + handleInTable = true; + + // VSWhidbey 123853: Set the padding BEFORE setting the control's font (as done + // in base.OnHandleCreated()) so that the tab control will honor both the + // horizontal and vertical dimensions of the padding rectangle. + if (!padding.IsEmpty) { + SendMessage(NativeMethods.TCM_SETPADDING, 0, NativeMethods.Util.MAKELPARAM(padding.X, padding.Y)); + } + + base.OnHandleCreated(e); + cachedDisplayRect = Rectangle.Empty; + ApplyItemSize(); + if (imageList != null) { + SendMessage(NativeMethods.TCM_SETIMAGELIST, 0, imageList.Handle); + } + + if (ShowToolTips) { + IntPtr tooltipHwnd; + tooltipHwnd = SendMessage(NativeMethods.TCM_GETTOOLTIPS, 0, 0); + if (tooltipHwnd != IntPtr.Zero) { + SafeNativeMethods.SetWindowPos(new HandleRef(this, tooltipHwnd), + NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | + NativeMethods.SWP_NOACTIVATE); + } + } + + // Add the pages + // + foreach(TabPage page in TabPages) { + AddNativeTabPage(page.GetTCITEM()); + } + + // Resize the pages + // + ResizePages(); + + if (selectedIndex != -1) { + try { + tabControlState[TABCONTROLSTATE_fromCreateHandles] = true; + SelectedIndex = selectedIndex; + } + finally { + tabControlState[TABCONTROLSTATE_fromCreateHandles] = false; + } + selectedIndex = -1; + } + UpdateTabSelection(false); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnHandleDestroyed(EventArgs e) { + if (!Disposing) { + selectedIndex = SelectedIndex; + } + //Remove the Handle from NativewIndow.... + + // VSWhidbey 539185, Don't try to remove the Handle if we've already done so + if (handleInTable) + { + handleInTable = false; + NativeWindow.RemoveWindowFromIDTable(this.Handle); + } + + base.OnHandleDestroyed(e); + } + + /// + /// + /// Actually goes and fires the OnDrawItem event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.onDrawItem(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnDrawItem(DrawItemEventArgs e) { + if (onDrawItem != null) onDrawItem(this, e); + } + + /// + /// + /// Actually goes and fires the OnLeave event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnLeave(e); to ensure the event is + /// still fired to external listeners + /// This listener is overidden so that we can fire SAME ENTER and LEAVE + /// events on the TabPage. + /// TabPage should fire enter when the focus is on the TABPAGE and not when the control + /// within the TabPage gets Focused. + /// Similary the Leave event should fire when the TabControl (and hence the TabPage) looses + /// Focus. To be Backward compatible we have added new bool which can be set to true + /// to the get the NEW SANE ENTER-LEAVE EVENTS ON THE TABPAGE. + /// + protected override void OnEnter(EventArgs e) { + base.OnEnter (e); + if (SelectedTab != null) { + SelectedTab.FireEnter(e); + } + + } + + /// + /// + /// Actually goes and fires the OnLeave event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnLeave(e); to ensure the event is + /// still fired to external listeners + /// This listener is overidden so that we can fire SAME ENTER and LEAVE + /// events on the TabPage. + /// TabPage should fire enter when the focus is on the TABPAGE and not when the control + /// within the TabPage gets Focused. + /// Similary the Leave event should fire when the TabControl (and hence the TabPage) looses + /// Focus. To be Backward compatible we have added new bool which can be set to true + /// to the get the NEW SANE ENTER-LEAVE EVENTS ON THE TABPAGE. + /// + protected override void OnLeave(EventArgs e) { + if (SelectedTab != null) { + SelectedTab.FireLeave(e); + } + base.OnLeave(e); + } + + /// + /// + /// We override this to get tabbing functionality. + /// If overriding this, remember to call base.onKeyDown. + /// + /// + protected override void OnKeyDown(KeyEventArgs ke) { + if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Control) !=0) { + bool forward = (ke.KeyData & Keys.Shift) == 0; + SelectNextTab(ke, forward); + } + if (ke.KeyCode == Keys.PageDown && (ke.KeyData & Keys.Control) !=0) { + SelectNextTab(ke, true); + } + if (ke.KeyCode == Keys.PageUp && (ke.KeyData & Keys.Control) !=0) { + SelectNextTab(ke, false); + } + + base.OnKeyDown(ke); + } + + internal override void OnParentHandleRecreated() + { + // Avoid temporarily resizing the TabControl while the parent + // recreates its handle to avoid bug VSWhidbey 543390. + this.skipUpdateSize = true; + try { + base.OnParentHandleRecreated(); + } + finally { + this.skipUpdateSize = false; + } + } + + /// + /// + /// + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + cachedDisplayRect = Rectangle.Empty; + UpdateTabSelection(false); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Actually goes and fires the onSelectedIndexChanged event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.onSelectedIndexChanged(e); to ensure the event is + /// still fired to external listeners + /// + protected virtual void OnSelectedIndexChanged(EventArgs e) { + int index = SelectedIndex; + cachedDisplayRect = Rectangle.Empty; + UpdateTabSelection(tabControlState[TABCONTROLSTATE_UISelection]); + tabControlState[TABCONTROLSTATE_UISelection] = false; + if (onSelectedIndexChanged != null) onSelectedIndexChanged(this, e); + + } + + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnSelecting(TabControlCancelEventArgs e) { + TabControlCancelEventHandler handler = (TabControlCancelEventHandler)Events[EVENT_SELECTING]; + if (handler != null) handler(this, e); + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnSelected(TabControlEventArgs e) { + TabControlEventHandler handler = (TabControlEventHandler)Events[EVENT_SELECTED]; + if (handler != null) handler(this, e); + + // Raise the enter event for this tab. + if (SelectedTab != null) { + SelectedTab.FireEnter(EventArgs.Empty); + } + + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnDeselecting(TabControlCancelEventArgs e) { + TabControlCancelEventHandler handler = (TabControlCancelEventHandler)Events[EVENT_DESELECTING]; + if (handler != null) handler(this, e); + + + } + + /// + /// + /// + /// Raises the event. + /// + /// + protected virtual void OnDeselected(TabControlEventArgs e) { + TabControlEventHandler handler = (TabControlEventHandler)Events[EVENT_DESELECTED]; + if (handler != null) handler(this, e); + + // Raise the Leave event for this tab. + if (SelectedTab != null) { + SelectedTab.FireLeave(EventArgs.Empty); + } + } + + /// + /// + /// We override this to get the Ctrl and Ctrl-Shift Tab functionality. + /// + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected override bool ProcessKeyPreview(ref Message m) { + if (ProcessKeyEventArgs(ref m)) return true; + return base.ProcessKeyPreview(ref m); + } + + /// + /// + /// + /// + internal void UpdateSize() { + if (this.skipUpdateSize) { + return; + } + // the spin control (left right arrows) won't update without resizing. + // the most correct thing would be to recreate the handle, but this works + // and is cheaper. + // + BeginUpdate(); + Size size = Size; + Size = new Size(size.Width + 1, size.Height); + Size = size; + EndUpdate(); + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + cachedDisplayRect = Rectangle.Empty; + UpdateSize(); + } + + internal override void RecreateHandleCore() { + // + TabPage[] tabPages = GetTabPages(); + + int index = ((tabPages.Length > 0) && (SelectedIndex == -1)) ? 0: SelectedIndex; + + // We don't actually want to remove the windows forms Tabpages - we only + // want to remove the corresponding TCITEM structs. + // So, no RemoveAll() + if (IsHandleCreated) { + SendMessage(NativeMethods.TCM_DELETEALLITEMS, 0, 0); + } + this.tabPages = null; + tabPageCount = 0; + + base.RecreateHandleCore(); + + for (int i = 0; i < tabPages.Length; i++) { + TabPages.Add(tabPages[i]); + } + try { + tabControlState[TABCONTROLSTATE_fromCreateHandles] = true; + SelectedIndex = index; + } + finally { + tabControlState[TABCONTROLSTATE_fromCreateHandles] = false; + } + + // The comctl32 TabControl seems to have some painting glitches. Briefly + // resizing the control seems to fix these. + // + UpdateSize(); + } + + /// + /// + /// [To be supplied.] + /// + protected void RemoveAll() { + this.Controls.Clear(); + + SendMessage(NativeMethods.TCM_DELETEALLITEMS, 0, 0); + tabPages = null; + tabPageCount = 0; + } + + /// + /// + /// + /// + internal void RemoveTabPage(int index) { + if (index < 0 || index >= tabPageCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + tabPageCount--; + if (index < tabPageCount) { + Array.Copy(tabPages, index + 1, tabPages, index, tabPageCount - index); + } + tabPages[tabPageCount] = null; + if (IsHandleCreated) { + SendMessage(NativeMethods.TCM_DELETEITEM, index, 0); + } + cachedDisplayRect = Rectangle.Empty; + } + + private void ResetItemSize() { + ItemSize = DEFAULT_ITEMSIZE; + } + + private void ResetPadding() { + Padding = DEFAULT_PADDING; + + } + + private void ResizePages() { + Rectangle rect = DisplayRectangle; + TabPage[] pages = GetTabPages(); + for (int i = 0; i < pages.Length; i++) { + pages[i].Bounds = rect; + } + } + + /// + /// + /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. + /// + /// + internal void SetToolTip(ToolTip toolTip, string controlToolTipText) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TCM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); + controlTipText = controlToolTipText; + + } + + /// + /// + /// + /// + internal void SetTabPage(int index, TabPage tabPage, NativeMethods.TCITEM_T tcitem) { + if (index < 0 || index >= tabPageCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + if (IsHandleCreated) + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_SETITEM, index, tcitem); + // Make the Updated tab page the currently selected tab page + if (DesignMode && IsHandleCreated) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_SETCURSEL, (IntPtr)index, IntPtr.Zero); + } + tabPages[index] = tabPage; + } + + /// + /// + /// Allows the user to specify the index in Tabcontrol.TabPageCollection of the tabpage to be shown. + /// + public void SelectTab(int index) { + TabPage t = GetTabPage(index); + if(t != null) + { + SelectedTab = t; + } + } + + /// + /// + /// Allows the user to specify the tabpage in Tabcontrol.TabPageCollection to be shown. + /// + public void SelectTab(TabPage tabPage) { + if (tabPage == null) { + throw new ArgumentNullException("tabPage"); + + } + int index = FindTabPage(tabPage); + SelectTab(index); + } + + /// + /// + /// Allows the user to specify the name of the tabpage in Tabcontrol.TabPageCollection to be shown. + /// + public void SelectTab(string tabPageName) { + if (tabPageName == null) { + throw new ArgumentNullException("tabPageName"); + + } + TabPage tabPage = TabPages[tabPageName]; + SelectTab(tabPage); + } + + /// + /// + /// This is called by TabControl in response to the KeyDown event to override the selection of tabpages + /// for different key combinations. + /// Control + Tab selects the next tabpage. + /// Control + Shift + Tab selects the previous tabpage. + /// Control + PageDown selects the next tabpage. + /// Control + PageUp selects the previous tabpage. + /// + /// + private void SelectNextTab(KeyEventArgs ke, bool forward) { + // WmSelChanging actually changes focus to cause validations. + // So cache in the Focused value so that we can reuse it later + bool focused = Focused; + + // Fire Deselecting .. Deselected on currently selected TabPage... + if (WmSelChanging()) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + if(ValidationCancelled) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + else { + + int sel = SelectedIndex; + if (sel != -1) { + int count = TabCount; + if (forward) + sel = (sel + 1) % count; + else + sel = (sel + count - 1) % count; + + + // this is special casing.. + // this function is called from OnKeyDown( ) which selects the NEXT TABPAGE + // But now we call the WmSelChanging( ) to Focus the tab page + // This breaks the logic in UpdateTabSelection (which is called + // thru SET of SelectedIndex) to Select the First control + // So adding this new Flag to select the first control. + try + { + tabControlState[TABCONTROLSTATE_UISelection] = true; + tabControlState[TABCONTROLSTATE_selectFirstControl] = true; + SelectedIndex = sel; + // This is required so that we select the first control if the TabControl is not current focused. + tabControlState[TABCONTROLSTATE_selectFirstControl] = !focused; + // Fire Selecting .. Selected on newly selected TabPage... + WmSelChange(); + + } + finally + { + // tabControlState[TABCONTROLSTATE_selectFirstControl] can be true if the TabControl is not focussed + // But at the end of this function reset the state !! + tabControlState[TABCONTROLSTATE_selectFirstControl] = false; + ke.Handled = true; + } + + } + } + } + + // Refer vsW: 543074: TabControl overrides this method to return true. + internal override bool ShouldPerformContainerValidation() { + return true; + } + + private bool ShouldSerializeItemSize() { + return !itemSize.Equals(DEFAULT_ITEMSIZE); + } + + private new bool ShouldSerializePadding() { + return !padding.Equals(DEFAULT_PADDING); + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + if (TabPages != null) { + s += ", TabPages.Count: " + TabPages.Count.ToString(CultureInfo.CurrentCulture); + if (TabPages.Count > 0) + s += ", TabPages[0]: " + TabPages[0].ToString(); + } + return s; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + currentlyScaling = true; + base.ScaleCore(dx, dy); + currentlyScaling = false; + } + + /// + /// + /// + /// + /// + /// Set the panel selections appropriately + /// + /// + protected void UpdateTabSelection(bool updateFocus) { + if (IsHandleCreated) { + int index = SelectedIndex; + + // make current panel invisible + TabPage[] tabPages = GetTabPages(); + if (index != -1) { + // VSWhidbey #163724 + // Changing the bounds of the tabPage during scaling + // will force a layout to occur. After this layout + // the tabpage will then be scaled again resulting + // in incorrect sizes. Suspend Layout in this case. + if (currentlyScaling) { + tabPages[index].SuspendLayout(); + } + tabPages[index].Bounds = DisplayRectangle; + + // After changing the Bounds of TabPages, we need to + // make TabPages Redraw. + // Use Invalidate directly here has no performance + // issue, since ReSize is calling low frequence. + tabPages[index].Invalidate(); + + if (currentlyScaling) { + tabPages[index].ResumeLayout(false); + } + + tabPages[index].Visible = true; + if (updateFocus) { + if (!Focused || tabControlState[TABCONTROLSTATE_selectFirstControl]) { + tabControlState[TABCONTROLSTATE_UISelection] = false; + bool selectNext = false; + // SECREVIEW : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + selectNext = tabPages[index].SelectNextControl(null, true, true, false, false); + } + finally { + CodeAccessPermission.RevertAssert(); + } + + if (selectNext) { + if (!ContainsFocus) { + IContainerControl c = GetContainerControlInternal(); + if (c != null) { + while (c.ActiveControl is ContainerControl) { + c = (IContainerControl) c.ActiveControl; + } + if (c.ActiveControl != null) + { + c.ActiveControl.FocusInternal(); + } + } + } + } + else { + IContainerControl c = GetContainerControlInternal(); + if (c != null && !DesignMode) { + if (c is ContainerControl) { + ((ContainerControl)c).SetActiveControlInternal(this); + } + else { + // SECREVIEW : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + c.ActiveControl = this; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + } + } + } + } + for (int i = 0; i < tabPages.Length; i++) { + if (i != SelectedIndex) { + tabPages[i].Visible = false; + } + } + } + } + + /// + /// + /// + /// + protected override void OnStyleChanged(EventArgs e) { + base.OnStyleChanged(e); + cachedDisplayRect = Rectangle.Empty; + UpdateTabSelection(false); + } + + + + /// + /// + /// + /// + internal void UpdateTab(TabPage tabPage) { + int index = FindTabPage(tabPage); + SetTabPage(index, tabPage, tabPage.GetTCITEM()); + + // It's possible that changes to this TabPage will change the DisplayRectangle of the + // TabControl (e.g. ASURT 99087), so invalidate and resize the size of this page. + // + cachedDisplayRect = Rectangle.Empty; + UpdateTabSelection(false); + } + + + /// + /// + /// + /// + private void WmNeedText(ref Message m) { + + NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + + int commandID = (int)ttt.hdr.idFrom; + + string tipText = GetToolTipText(GetTabPage(commandID)); + if (!string.IsNullOrEmpty(tipText)) + ttt.lpszText = tipText; + else + ttt.lpszText = controlTipText; + + ttt.hinst = IntPtr.Zero; + + // RightToLeft reading order + // + if (RightToLeft == RightToLeft.Yes) { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + + Marshal.StructureToPtr(ttt, m.LParam, false); + + } + + /// + /// + /// + /// + private void WmReflectDrawItem(ref Message m) { + + NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); + IntPtr oldPal = SetUpPalette(dis.hDC, false /*force*/, false /*realize*/); + using (Graphics g = Graphics.FromHdcInternal(dis.hDC)) { + OnDrawItem(new DrawItemEventArgs(g, Font, Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom), dis.itemID, (DrawItemState)dis.itemState)); + } + if (oldPal != IntPtr.Zero) { + SafeNativeMethods.SelectPalette(new HandleRef(null, dis.hDC), new HandleRef(null, oldPal), 0); + } + m.Result = (IntPtr)1; + } + + /// + /// + /// + /// + private bool WmSelChange() { + TabControlCancelEventArgs tcc = new TabControlCancelEventArgs(this.SelectedTab, this.SelectedIndex, false, TabControlAction.Selecting); + OnSelecting(tcc); + if (!tcc.Cancel) { + OnSelected (new TabControlEventArgs (this.SelectedTab, this.SelectedIndex, TabControlAction.Selected)); + OnSelectedIndexChanged(EventArgs.Empty); + } + else { + // user Cancelled the Selection of the new Tab. + SendMessage(NativeMethods.TCM_SETCURSEL, lastSelection, 0); + UpdateTabSelection(true); + } + return tcc.Cancel; + } + + /// + /// + /// + /// + private bool WmSelChanging() { + IContainerControl c = GetContainerControlInternal(); + if (c != null && !DesignMode) { + if (c is ContainerControl) { + ((ContainerControl)c).SetActiveControlInternal(this); + } + else { + // SECREVIEW : Taking focus and activating a control in response + // : to a user gesture (WM_SETFOCUS) is OK. + // + IntSecurity.ModifyFocus.Assert(); + try { + c.ActiveControl = this; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + // Fire DeSelecting .... on the current Selected Index... + // Set the return value to a global + // if 'cancelled' return from here else.. + // fire Deselected. + lastSelection = SelectedIndex; + TabControlCancelEventArgs tcc = new TabControlCancelEventArgs(this.SelectedTab, this.SelectedIndex, false, TabControlAction.Deselecting); + OnDeselecting(tcc); + if (!tcc.Cancel) { + OnDeselected(new TabControlEventArgs(this.SelectedTab, this.SelectedIndex, TabControlAction.Deselected)); + } + return tcc.Cancel; + + } + + /// + /// + /// + /// + private void WmTabBaseReLayout(ref Message m) { + BeginUpdate(); + cachedDisplayRect = Rectangle.Empty; + UpdateTabSelection(false); + EndUpdate(); + Invalidate(true); + + // Remove other TabBaseReLayout messages from the message queue + NativeMethods.MSG msg = new NativeMethods.MSG(); + IntPtr hwnd = Handle; + while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, hwnd), + tabBaseReLayoutMessage, + tabBaseReLayoutMessage, + NativeMethods.PM_REMOVE)) { + ; // NULL loop + } + } + + /// + /// + /// The tab's window procedure. Inheritng classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the tab continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + switch (m.Msg) { + case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: + WmReflectDrawItem(ref m); + break; + + case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: + // We use TCM_SETITEMSIZE instead + break; + + case NativeMethods.WM_NOTIFY: + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (nmhdr.code) { + // new switch added to prevent the TabControl from changing to next TabPage ... + //in case of validation cancelled... + //Turn tabControlState[TABCONTROLSTATE_UISelection] = false and Return So that no WmSelChange() gets fired. + //If validation not cancelled then tabControlState[TABCONTROLSTATE_UISelection] is turned ON to set the focus on to the ... + //next TabPage.. + + case NativeMethods.TCN_SELCHANGING: + if (WmSelChanging()) { + m.Result = (IntPtr)1; + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + + if(ValidationCancelled) { + m.Result = (IntPtr)1; + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + else { + tabControlState[TABCONTROLSTATE_UISelection] = true; + } + break; + case NativeMethods.TCN_SELCHANGE: + if (WmSelChange ()) { + m.Result = (IntPtr)1; + tabControlState[TABCONTROLSTATE_UISelection] = false; + return; + } + else { + tabControlState[TABCONTROLSTATE_UISelection] = true; + } + break; + case NativeMethods.TTN_GETDISPINFOA: + case NativeMethods.TTN_GETDISPINFOW: + // MSDN: + // Setting the max width has the added benefit of enabling Multiline + // tool tips! + // + UnsafeNativeMethods.SendMessage(new HandleRef(nmhdr, nmhdr.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + WmNeedText(ref m); + m.Result = (IntPtr)1; + return; + } + break; + } + if (m.Msg == tabBaseReLayoutMessage) { + WmTabBaseReLayout(ref m); + return; + } + base.WndProc(ref m); + } + + + /// + /// + /// [To be supplied.] + /// + public class TabPageCollection : IList { + private TabControl owner; + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public TabPageCollection( TabControl owner ) { + if (owner == null) { + throw new ArgumentNullException("owner"); + } + this.owner = owner; + } + + /// + /// + /// [To be supplied.] + /// + public virtual TabPage this[int index] { + get { + return owner.GetTabPage(index); + } + set { + owner.SetTabPage(index, value, value.GetTCITEM()); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // value is the name of the param passed in. + // So we don't have to localize it. + ] + set { + if (value is TabPage) { + this[index] = (TabPage)value; + } + else { + throw new ArgumentException("value"); + } + } + } + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual TabPage this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false)] + public int Count { + get { + return owner.tabPageCount; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public void Add(TabPage value) { + + if (value == null) { + throw new ArgumentNullException("value"); + } + + owner.Controls.Add(value); + } + + /// + /// + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // value is the name of the param passed in. + // So we don't have to localize it. + ] + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + int IList.Add(object value) { + if (value is TabPage) { + Add((TabPage)value); + return IndexOf((TabPage)value); + } + else { + throw new ArgumentException("value"); + } + } + + // <-- NEW ADD OVERLOADS FOR WHIDBEY + + /// + /// + /// [To be supplied.] + /// + public void Add(string text) { + TabPage page = new TabPage(); + page.Text = text; + Add(page); + } + + /// + /// + /// [To be supplied.] + /// + public void Add(string key, string text) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + Add(page); + } + + /// + /// + /// [To be supplied.] + /// + public void Add(string key, string text, int imageIndex) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + page.ImageIndex = imageIndex; + Add(page); + } + + /// + /// + /// [To be supplied.] + /// + public void Add(string key, string text, string imageKey) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + page.ImageKey = imageKey; + Add(page); + } + + // END - NEW ADD OVERLOADS FOR WHIDBEY --> + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public void AddRange(TabPage[] pages) { + if (pages == null) { + throw new ArgumentNullException("pages"); + } + foreach(TabPage page in pages) { + Add(page); + } + } + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public bool Contains(TabPage page) { + + //check for the page not to be null + if (page == null) + throw new ArgumentNullException("value"); + //end check + + return IndexOf(page) != -1; + } + + /// + /// + bool IList.Contains(object page) { + if (page is TabPage) { + return Contains((TabPage)page); + } + else { + return false; + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + + /// + /// + /// [To be supplied.] + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public int IndexOf(TabPage page) { + + //check for the page not to be null + if (page == null) + throw new ArgumentNullException("value"); + //end check + + for(int index=0; index < Count; ++index) { + if (this[index] == page) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object page) { + if (page is TabPage) { + return IndexOf((TabPage)page); + } + else { + return -1; + } + } + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + /// + /// + /// Inserts the supplied Tabpage at the given index. + /// + public void Insert(int index, TabPage tabPage) { + owner.InsertItem(index, tabPage); + try { + // 247078 : See InsertingItem property + owner.InsertingItem = true; + owner.Controls.Add(tabPage); + } + finally { + owner.InsertingItem = false; + } + owner.Controls.SetChildIndex(tabPage, index); + } + + /// + /// + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // tabPage is the name of the param passed in. + // So we don't have to localize it. + ] + void IList.Insert(int index, object tabPage) { + if (tabPage is TabPage) { + Insert(index, (TabPage)tabPage); + } + else { + throw new ArgumentException("tabPage"); + } + } + + // <-- NEW INSERT OVERLOADS FOR WHIDBEY + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string text) { + TabPage page = new TabPage(); + page.Text = text; + Insert(index, page); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + Insert(index, page); + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text, int imageIndex) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + Insert(index,page); + // ImageKey and ImageIndex require parenting... + page.ImageIndex = imageIndex; + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, string key, string text, string imageKey) { + TabPage page = new TabPage(); + page.Name = key; + page.Text = text; + Insert(index, page); + // ImageKey and ImageIndex require parenting... + page.ImageKey = imageKey; + } + + // END - NEW INSERT OVERLOADS FOR WHIDBEY --> + + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { + owner.RemoveAll(); + } + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (Count > 0) { + System.Array.Copy(owner.GetTabPages(), 0, dest, index, Count); + } + } + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + TabPage[] tabPages = owner.GetTabPages(); + if (tabPages != null) { + return tabPages.GetEnumerator(); + } + else { + return new TabPage[0].GetEnumerator(); + } + } + + /// + /// + /// [To be supplied.] + /// + + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] + public void Remove(TabPage value) { + + //check for the value not to be null + if (value == null) + throw new ArgumentNullException("value"); + //end check + owner.Controls.Remove(value); + } + + /// + /// + void IList.Remove(object value) { + if (value is TabPage) { + Remove((TabPage)value); + } + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + owner.Controls.RemoveAt(index); + } + + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + } + + + /// + /// + /// Collection of controls... + /// + [ComVisible(false)] + public new class ControlCollection : Control.ControlCollection { + + private TabControl owner; + + /*C#r: protected*/ + + /// + /// + /// + /// [To be supplied.] + /// + public ControlCollection(TabControl owner) + : base(owner) { + this.owner = owner; + } + + /// + /// + /// [To be supplied.] + /// + public override void Add(Control value) { + if (!(value is TabPage)) { + throw new ArgumentException(SR.GetString(SR.TabControlInvalidTabPageType, value.GetType().Name)); + } + + TabPage tabPage = (TabPage)value; + + // 247078 : See InsertingItem property + if (!owner.InsertingItem) + { + if (owner.IsHandleCreated) { + owner.AddTabPage(tabPage, tabPage.GetTCITEM()); + } + else { + owner.Insert(owner.TabCount, tabPage); + } + } + + base.Add(tabPage); + tabPage.Visible = false; + + // Without this check, we force handle creation on the tabcontrol + // which is not good at all of there are any OCXs on it. + // + if (owner.IsHandleCreated) { + tabPage.Bounds = owner.DisplayRectangle; + } + + // site the tabPage if necessary. + ISite site = owner.Site; + if (site != null) { + ISite siteTab = tabPage.Site; + if (siteTab == null) { + IContainer container = site.Container; + if (container != null) + { + container.Add(tabPage); + } + } + } + owner.ApplyItemSize(); + owner.UpdateTabSelection(false); + + } + + /// + /// + /// [To be supplied.] + /// + public override void Remove(Control value) { + base.Remove(value); + if (!(value is TabPage)) { + return; + } + int index = owner.FindTabPage((TabPage)value); + int curSelectedIndex = owner.SelectedIndex; + + if (index != -1) { + owner.RemoveTabPage(index); + if (index == curSelectedIndex) + { + owner.SelectedIndex = 0; //Always select the first tabPage is the Selected TabPage is removed. + } + } + owner.UpdateTabSelection(false); + } + + } + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabControlAction.cs b/WindowsForms/Managed/System/WinForms/TabControlAction.cs new file mode 100644 index 000000000..2a125f0b7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControlAction.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// This enum is used to specify the action that caused a TreeViewEventArgs. + /// + public enum TabControlAction { + + /// + Selecting, + /// + Selected, + /// + Deselecting, + /// + Deselected + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabControlCancelEvent.cs b/WindowsForms/Managed/System/WinForms/TabControlCancelEvent.cs new file mode 100644 index 000000000..87f2a9122 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControlCancelEvent.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the , + /// + /// event. + /// + /// + /// + public class TabControlCancelEventArgs : CancelEventArgs { + private TabPage tabPage; + private int tabPageIndex; + private TabControlAction action; + + + /// + public TabControlCancelEventArgs(TabPage tabPage, int tabPageIndex, bool cancel, TabControlAction action) + : base(cancel) { + this.tabPage = tabPage; + this.tabPageIndex = tabPageIndex; + this.action = action; + } + + /// + /// + /// + /// Stores the referemce to the tabpage that is undergoing the TabControl event. + /// + /// + /// + public TabPage TabPage { + get { + return tabPage; + } + } + + /// + /// + /// + /// Stores the index to the tabpage that is undergoing the TabControl event. + /// + /// + /// + public int TabPageIndex { + get { + return tabPageIndex; + } + } + + /// + /// + /// + /// Stores the TabControl action which instigated the TabControl event. + /// + /// + /// + public TabControlAction Action { + get { + return action; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabControlCancelEventHandler.cs b/WindowsForms/Managed/System/WinForms/TabControlCancelEventHandler.cs new file mode 100644 index 000000000..2d78d2755 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControlCancelEventHandler.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that handles the + /// , event of a + /// + /// + /// + public delegate void TabControlCancelEventHandler(object sender, TabControlCancelEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/TabControlEvent.cs b/WindowsForms/Managed/System/WinForms/TabControlEvent.cs new file mode 100644 index 000000000..2e73b1c07 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControlEvent.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Provides data for the , + /// event. + /// + public class TabControlEventArgs : EventArgs { + + private TabPage tabPage; + private int tabPageIndex; + private TabControlAction action; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + public TabControlEventArgs(TabPage tabPage, int tabPageIndex, TabControlAction action) { + this.tabPage = tabPage; + this.tabPageIndex = tabPageIndex; + this.action = action; + } + + /// + /// + /// + /// Stores the referemce to the tabpage that is undergoing the TabControl event. + /// + /// + /// + public TabPage TabPage { + get { + return tabPage; + } + } + + /// + /// + /// + /// Stores the index to the tabpage that is undergoing the TabControl event. + /// + /// + /// + public int TabPageIndex { + get { + return tabPageIndex; + } + } + + /// + /// + /// + /// Stores the TabControl action which instigated the TabControl event. + /// + /// + /// + public TabControlAction Action{ + get { + return action; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TabControlEventHandler.cs b/WindowsForms/Managed/System/WinForms/TabControlEventHandler.cs new file mode 100644 index 000000000..699c4c4ba --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabControlEventHandler.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that handles the + /// , event of a + /// + /// + /// + public delegate void TabControlEventHandler(object sender, TabControlEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/TabDrawMode.cs b/WindowsForms/Managed/System/WinForms/TabDrawMode.cs new file mode 100644 index 000000000..d42eb8fec --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabDrawMode.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// The TabStrip and TabControl both support ownerdraw functionality, but + /// only one type, in which you can paint the tabs individually. This + /// enumeration contains the valid values for it's drawMode property. + /// + public enum TabDrawMode { + + /// + /// + /// All the items in the control are painted by the system and are of the + /// same size + /// + Normal = 0, + + /// + /// + /// The user paints the items in the control manually + /// + OwnerDrawFixed = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabPage.cs b/WindowsForms/Managed/System/WinForms/TabPage.cs new file mode 100644 index 000000000..84eed9540 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabPage.cs @@ -0,0 +1,814 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Drawing.Design; + using System.ComponentModel.Design; + using System.Text; + using System.Windows.Forms; + using System.Security.Permissions; + using Microsoft.Win32; + using System.Windows.Forms.VisualStyles; + using System.Runtime.InteropServices; + using System.Globalization; + using System.Windows.Forms.Layout; + + /// + /// + /// TabPage implements a single page of a tab control. It is essentially + /// a Panel with TabItem properties. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.TabPageDesigner, " + AssemblyRef.SystemDesign), + ToolboxItem (false), + DesignTimeVisible (false), + DefaultEvent ("Click"), + DefaultProperty ("Text") + ] + public class TabPage : Panel { + private ImageList.Indexer imageIndexer; + private string toolTipText = ""; + private bool enterFired = false; + private bool leaveFired = false; + private bool useVisualStyleBackColor = false; + + + /// + /// + /// Constructs an empty TabPage. + /// + public TabPage () + : base() { + SetStyle (ControlStyles.CacheText, true); + Text = null; + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + Localizable(false) + ] + public override AutoSizeMode AutoSizeMode { + get { + return AutoSizeMode.GrowOnly; + } + set { + } + } + + /// + /// Hide AutoSize: it doesn't make sense for this control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + /// + /// + /// The background color of this control. This is an ambient property and + /// will always return a non-null value. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ControlBackColorDescr) + ] + public override Color BackColor { + get { + Color color = base.BackColor; + // If some color is Set by the user return that... + if (color != DefaultBackColor) + { + return color; + } + // If user has not set a color and if XP theming ON and Parent's appearance is Normal, then return the Transparent Color.... + TabControl parent = ParentInternal as TabControl; + if (Application.RenderWithVisualStyles && UseVisualStyleBackColor && (parent != null && parent.Appearance == TabAppearance.Normal)) { + return Color.Transparent; + } + // return base.Color by default... + return color; + } + set { + if (DesignMode) { + if (value != Color.Empty) { + PropertyDescriptor pd = TypeDescriptor.GetProperties(this)["UseVisualStyleBackColor"]; + Debug.Assert(pd != null); + if (pd != null) { + pd.SetValue(this, false); + } + } + } + else { + UseVisualStyleBackColor = false; + } + + base.BackColor = value; + } + } + + /// + /// + /// Constructs the new instance of the Controls collection objects. Subclasses + /// should not call base.CreateControlsInstance. Our version creates a control + /// collection that does not support + /// + protected override Control.ControlCollection CreateControlsInstance () { + return new TabPageControlCollection (this); + } + + + /// + /// + internal ImageList.Indexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ImageList.Indexer (); + } + + return imageIndexer; + } + } + + /// + /// + /// Returns the imageIndex for the tabPage. This should point to an image + /// in the TabControl's associated imageList that will appear on the tab, or be -1. + /// + [ + TypeConverterAttribute (typeof(ImageIndexConverter)), + Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable (true), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue (-1), + SRDescription (SR.TabItemImageIndexDescr) + ] + public int ImageIndex { + get { + return ImageIndexer.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException ("ImageIndex", SR.GetString (SR.InvalidLowBoundArgumentEx, "imageIndex", (value).ToString (CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + TabControl parent = ParentInternal as TabControl; + + if (parent != null) { + this.ImageIndexer.ImageList = parent.ImageList; + } + + this.ImageIndexer.Index = value; + UpdateParent (); + } + } + + /// + /// + /// Returns the imageIndex for the tabPage. This should point to an image + /// in the TabControl's associated imageList that will appear on the tab, or be -1. + /// + [ + TypeConverterAttribute (typeof(ImageKeyConverter)), + Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Localizable (true), + DefaultValue (""), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.TabItemImageIndexDescr) + ] + public string ImageKey { + get { + return ImageIndexer.Key; + } + set { + this.ImageIndexer.Key = value; + + TabControl parent = ParentInternal as TabControl; + + if (parent != null) { + this.ImageIndexer.ImageList = parent.ImageList; + } + + UpdateParent (); + } + } + + /// + /// + /// Constructs a TabPage with text for the tab. + /// + public TabPage (string text) : this() { + Text = text; + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + base.Anchor = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler DockChanged { + add { + base.DockChanged += value; + } + remove { + base.DockChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public bool Enabled { + get { + return base.Enabled; + } + set { + base.Enabled = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler EnabledChanged { + add { + base.EnabledChanged += value; + } + remove { + base.EnabledChanged -= value; + } + } + + /// + /// + [ + DefaultValue (false), + SRCategory(SR.CatAppearance), + SRDescription (SR.TabItemUseVisualStyleBackColorDescr) + ] + public bool UseVisualStyleBackColor { + get { + return useVisualStyleBackColor; + } + set { + useVisualStyleBackColor = value; + this.Invalidate(true); + } + } + + /// + /// + // VSWhidbey 94772: Make the Location property non-browsable for the TabPages. + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler LocationChanged { + add { + base.LocationChanged += value; + } + remove { + base.LocationChanged -= value; + } + } + + /// + [DefaultValue(typeof(Size), "0, 0")] + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + public override Size MaximumSize { + get { return base.MaximumSize; } + + set { + base.MaximumSize = value; + } + } + + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + public override Size MinimumSize { + get { return base.MinimumSize; } + + set { + base.MinimumSize = value; + } + } + + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + public new Size PreferredSize { + get { return base.PreferredSize; } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + + /// + /// Refer VsWhidbey : 440669: This property is required by certain controls (TabPage) to render its transparency using theming API. + /// We dont want all controls (that are have transparent BackColor) to use theming API to render its background because it has HUGE PERF cost. + /// + /// + internal override bool RenderTransparencyWithVisualStyles { + get { + return true; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable (true), + Browsable (true), + EditorBrowsable (EditorBrowsableState.Always) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + UpdateParent (); + } + } + + /// + /// + [Browsable (true), EditorBrowsable (EditorBrowsableState.Always)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The toolTipText for the tab, that will appear when the mouse hovers + /// over the tab and the TabControl's showToolTips property is true. + /// + [ + DefaultValue (""), + Localizable (true), + SRDescription (SR.TabItemToolTipTextDescr) + ] + public string ToolTipText { + get { + return toolTipText; + } + set { + if (value == null) { + value = ""; + } + + if (value == toolTipText) + return; + + toolTipText = value; + UpdateParent (); + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public bool Visible { + get { + return base.Visible; + } + set { + base.Visible = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler VisibleChanged { + add { + base.VisibleChanged += value; + } + remove { + base.VisibleChanged -= value; + } + } + + /// + /// Assigns a new parent control. Sends out the appropriate property change + /// notifications for properties that are affected by the change of parent. + /// + internal override void AssignParent (Control value) { + if (value != null && !(value is TabControl)) { + throw new ArgumentException (SR.GetString (SR.TABCONTROLTabPageNotOnTabControl, value.GetType ().FullName)); + } + + base.AssignParent (value); + } + + /// + /// + /// Given a component, this retrieves the tab page that it's parented to, or + /// null if it's not parented to any tab page. + /// + public static TabPage GetTabPageOfComponent (Object comp) { + if (!(comp is Control)) { + return null; + } + + Control c = (Control)comp; + + while (c != null && !(c is TabPage)) { + c = c.ParentInternal; + } + + return (TabPage)c; + } + + /// + /// + /// + /// + internal NativeMethods.TCITEM_T GetTCITEM () { + NativeMethods.TCITEM_T tcitem = new NativeMethods.TCITEM_T (); + + tcitem.mask = 0; + tcitem.pszText = null; + tcitem.cchTextMax = 0; + tcitem.lParam = IntPtr.Zero; + + string text = Text; + + PrefixAmpersands (ref text); + if (text != null) { + tcitem.mask |= NativeMethods.TCIF_TEXT; + tcitem.pszText = text; + tcitem.cchTextMax = text.Length; + } + + int imageIndex = ImageIndex; + + tcitem.mask |= NativeMethods.TCIF_IMAGE; + tcitem.iImage = ImageIndexer.ActualIndex; + return tcitem; + } + + private void PrefixAmpersands (ref string value) { + // Due to a comctl32 problem, ampersands underline the next letter in the + // text string, but the accelerators don't work. + // So in this function, we prefix ampersands with another ampersand + // so that they actually appear as ampersands. + // + // Sanity check parameter + // + if (value == null || value.Length == 0) { + return; + } + + // If there are no ampersands, we don't need to do anything here + // + if (value.IndexOf ('&') < 0) { + return; + } + + // Insert extra ampersands + // + StringBuilder newString = new StringBuilder (); + + for (int i = 0; i < value.Length; ++i) { + if (value[i] == '&') { + if (i < value.Length - 1 && value[i + 1] == '&') { + ++i; // Skip the second ampersand + } + + newString.Append ("&&"); + } + else { + newString.Append (value[i]); + } + } + + value = newString.ToString (); + } + + /// + /// This is an internal method called by the TabControl to fire the Leave event when TabControl leave occurs. + /// + internal void FireLeave (EventArgs e) { + leaveFired = true; + OnLeave (e); + } + + /// + /// This is an internal method called by the TabControl to fire the Enter event when TabControl leave occurs. + /// + internal void FireEnter (EventArgs e) { + enterFired = true; + OnEnter (e); + } + + /// + /// + /// Actually goes and fires the OnEnter event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnEnter(e); to ensure the event is + /// still fired to external listeners + /// This listener is overidden so that we can fire SAME ENTER and LEAVE + /// events on the TabPage. + /// TabPage should fire enter when the focus is on the TABPAGE and not when the control + /// within the TabPage gets Focused. + /// + protected override void OnEnter (EventArgs e) { + TabControl parent = ParentInternal as TabControl; + + if (parent != null) { + if (enterFired) { + base.OnEnter (e); + } + + enterFired = false; + } + } + + /// + /// + /// Actually goes and fires the OnLeave event. Inheriting controls + /// should use this to know when the event is fired [this is preferable to + /// adding an event handler on yourself for this event]. They should, + /// however, remember to call base.OnLeave(e); to ensure the event is + /// still fired to external listeners + /// This listener is overidden so that we can fire SAME ENTER and LEAVE + /// events on the TabPage. + /// TabPage should fire enter when the focus is on the TABPAGE and not when the control + /// within the TabPage gets Focused. + /// Similary the Leave should fire when the TabControl (and hence the TabPage) looses + /// Focus. + /// + protected override void OnLeave (EventArgs e) { + TabControl parent = ParentInternal as TabControl; + + if (parent != null) { + if (leaveFired) { + base.OnLeave (e); + } + + leaveFired = false; + } + } + + /// + protected override void OnPaintBackground (PaintEventArgs e) { + + // VSWhidbey 94759: Utilize the TabRenderer new to Whidbey to draw the tab pages so that the + // panels are drawn using the correct visual styles when the application supports using visual + // styles. + + // Does this application utilize Visual Styles? + // VSWhidbey 167291: Utilize the UseVisualStyleBackColor property to determine whether or + // not the themed background should be utilized. + TabControl parent = ParentInternal as TabControl; + if (Application.RenderWithVisualStyles && UseVisualStyleBackColor && (parent != null && parent.Appearance == TabAppearance.Normal)) { + + Color bkcolor = UseVisualStyleBackColor ? Color.Transparent : this.BackColor; + Rectangle inflateRect = LayoutUtils.InflateRect(DisplayRectangle, Padding); + + + + //HACK: to ensure that the tabpage draws correctly (the border will get clipped and + // and gradient fill will match correctly with the tabcontrol). Unfortunately, there is no good way to determine + // the padding used on the tabpage. + // I would like to use the following below, but GetMargins is busted in the theming API: + //VisualStyleRenderer visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Tab.Pane.Normal); + //Padding themePadding = visualStyleRenderer.GetMargins(e.Graphics, MarginProperty.ContentMargins); + //Rectangle rectWithBorder = new Rectangle(inflateRect.X - themePadding.Left, + // inflateRect.Y - themePadding.Top, + // inflateRect.Width + themePadding.Right + themePadding.Left, + // inflateRect.Height + themePadding.Bottom + themePadding.Top); + Rectangle rectWithBorder = new Rectangle(inflateRect.X - 4, inflateRect.Y - 2, inflateRect.Width + 8, inflateRect.Height + 6); + + TabRenderer.DrawTabPage(e.Graphics, rectWithBorder); + + // Is there a background image to paint? The TabRenderer does not currently support + // painting the background image on the panel, so we need to draw it ourselves. + if (this.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(e.Graphics, BackgroundImage, bkcolor, BackgroundImageLayout, inflateRect, inflateRect, DisplayRectangle.Location); + } + } + else { + base.OnPaintBackground (e); + } + } + + /// + /// + /// overrides main setting of our bounds so that we can control our size and that of our + /// TabPages... + /// + /// + protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified) { + Control parent = ParentInternal; + + if (parent is TabControl && parent.IsHandleCreated) { + Rectangle r = parent.DisplayRectangle; + + // LayoutEngines send BoundsSpecified.None so they can know they are the ones causing the size change + // in the subsequent InitLayout. We need to be careful preserve a None. + base.SetBoundsCore (r.X, r.Y, r.Width, r.Height, specified == BoundsSpecified.None ? BoundsSpecified.None : BoundsSpecified.All); + } + else { + base.SetBoundsCore (x, y, width, height, specified); + } + } + + /// + /// Determines if the Location property needs to be persisted. + /// + [EditorBrowsable (EditorBrowsableState.Never)] + private bool ShouldSerializeLocation () { + return Left != 0 || Top != 0; + } + + /// + /// + /// The text property is what is returned for the TabPages default printing. + /// + public override string ToString () { + return "TabPage: {" + Text + "}"; + } + + /// + /// + /// + /// + internal void UpdateParent () { + TabControl parent = ParentInternal as TabControl; + + if (parent != null) { + parent.UpdateTab (this); + } + } + + /// + /// + /// Our control collection will throw an exception if you try to add other tab pages. + /// + [ComVisible(false)] + public class TabPageControlCollection : Control.ControlCollection { + /// + /// + /// + /// Creates a new TabPageControlCollection. + /// + public TabPageControlCollection (TabPage owner) : base(owner) { + } + + /// + /// + /// Adds a child control to this control. The control becomes the last control + /// in the child control list. If the control is already a child of another + /// control it is first removed from that control. The tab page overrides + /// this method to ensure that child tab pages are not added to it, as these + /// are illegal. + /// + public override void Add (Control value) { + if (value is TabPage) { + throw new ArgumentException (SR.GetString (SR.TABCONTROLTabPageOnTabPage)); + } + + base.Add (value); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TabRenderer.cs b/WindowsForms/Managed/System/WinForms/TabRenderer.cs new file mode 100644 index 000000000..ef2df7b9c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabRenderer.cs @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.Internal; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the Tab control. + /// + /// + public sealed class TabRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + + //cannot instantiate + private TabRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTabItem(Graphics g, Rectangle bounds, TabItemState state) { + InitializeRenderer(VisualStyleElement.Tab.TabItem.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, bool focused, TabItemState state) { + InitializeRenderer(VisualStyleElement.Tab.TabItem.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + + // I need this hack since GetBackgroundContentRectangle() returns same rectangle + // as bounds for this control! + Rectangle contentBounds = Rectangle.Inflate(bounds, -3, -3); + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, string tabItemText, Font font, TabItemState state) { + DrawTabItem(g, bounds, tabItemText, font, false, state); + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, string tabItemText, Font font, bool focused, TabItemState state) { + DrawTabItem(g, bounds, tabItemText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + focused, state); + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, string tabItemText, Font font, TextFormatFlags flags, bool focused, TabItemState state) { + InitializeRenderer(VisualStyleElement.Tab.TabItem.Normal, (int)state); + visualStyleRenderer.DrawBackground(g, bounds); + + // I need this hack since GetBackgroundContentRectangle() returns same rectangle + // as bounds for this control! + Rectangle contentBounds = Rectangle.Inflate(bounds, -3, -3); + Color textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + TextRenderer.DrawText(g, tabItemText, font, contentBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, Image image, Rectangle imageRectangle, bool focused, TabItemState state) { + InitializeRenderer(VisualStyleElement.Tab.TabItem.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + + // I need this hack since GetBackgroundContentRectangle() returns same rectangle + // as bounds for this control! + Rectangle contentBounds = Rectangle.Inflate(bounds, -3, -3); + + visualStyleRenderer.DrawImage(g, imageRectangle, image); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, string tabItemText, Font font, Image image, Rectangle imageRectangle, bool focused, TabItemState state) { + DrawTabItem(g, bounds, tabItemText, font, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine, + image, imageRectangle, focused, state); + } + + /// + /// + /// + /// Renders a Tab item. + /// + /// + public static void DrawTabItem(Graphics g, Rectangle bounds, string tabItemText, Font font, TextFormatFlags flags, Image image, Rectangle imageRectangle, bool focused, TabItemState state) { + InitializeRenderer(VisualStyleElement.Tab.TabItem.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + + // I need this hack since GetBackgroundContentRectangle() returns same rectangle + // as bounds for this control! + Rectangle contentBounds = Rectangle.Inflate(bounds, -3, -3); + visualStyleRenderer.DrawImage(g, imageRectangle, image); + Color textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + TextRenderer.DrawText(g, tabItemText, font, contentBounds, textColor, flags); + + if (focused) { + ControlPaint.DrawFocusRectangle(g, contentBounds); + } + } + + /// + /// + /// + /// Renders a TabPage. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTabPage(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.Tab.Pane.Normal, 0); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + private static void InitializeRenderer(VisualStyleElement element, int state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(element.ClassName, element.Part, state); + } + else { + visualStyleRenderer.SetParameters(element.ClassName, element.Part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TabSizeMode.cs b/WindowsForms/Managed/System/WinForms/TabSizeMode.cs new file mode 100644 index 000000000..a6cb6f77f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TabSizeMode.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// Controls the automatic sizing of certain objects. This is typically + /// used for the sizing of Tabs in a TabStrip control. + /// + public enum TabSizeMode { + + /// + /// + /// Indicates that items are only as wide as they need to be to display + /// their information. Empty space on the right is left as such + /// + Normal = 0, + + /// + /// + /// indicates that the tags are stretched to ensure they reach the far + /// right of the strip, if necesary. This is only applicable to tab + /// strips with more than one row. + /// + FillToRight = 1, + + /// + /// + /// Indicates that all tabs are the same width. period. + /// + Fixed = 2, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventArgs.cs b/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventArgs.cs new file mode 100644 index 000000000..658fd74b6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventArgs.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + + /// + /// + /// This is the overrided PaintEventArgs for painting the cell of the table + /// It contains additional information indicating the row/column of the cell + /// as well as the bound of the cell + /// + public class TableLayoutCellPaintEventArgs : PaintEventArgs { + private Rectangle bounds; + private int row; + private int column; + + /// + public TableLayoutCellPaintEventArgs(Graphics g, Rectangle clipRectangle, Rectangle cellBounds, int column, int row) : base(g, clipRectangle) { + this.bounds = cellBounds; + this.row = row; + this.column = column; + } + + //the bounds of the cell + /// + public Rectangle CellBounds { + get { return bounds; } + } + + //the row index of the cell + /// + public int Row { + get { return row; } + } + + //the column index of the cell + /// + public int Column { + get { return column; } + } + } + } + diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventHandler.cs b/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventHandler.cs new file mode 100644 index 000000000..742953f1b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutCellPaintEventHandler.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + + public delegate void TableLayoutCellPaintEventHandler(object sender, TableLayoutCellPaintEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutColumnStyleCollection.cs b/WindowsForms/Managed/System/WinForms/TableLayoutColumnStyleCollection.cs new file mode 100644 index 000000000..f3618d4a6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutColumnStyleCollection.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + /// + public class TableLayoutColumnStyleCollection : TableLayoutStyleCollection { + + + internal TableLayoutColumnStyleCollection(IArrangedElement Owner) : base(Owner) {} + internal TableLayoutColumnStyleCollection() : base(null) {} + + internal override string PropertyName { + get { return PropertyNames.ColumnStyles; } + } + + /// + public int Add(ColumnStyle columnStyle) { return ((IList)this).Add(columnStyle); } + + /// + public void Insert(int index, ColumnStyle columnStyle) { ((IList)this).Insert(index, columnStyle); } + + /// + public new ColumnStyle this[int index] { + get { return (ColumnStyle)((IList)this)[index]; } + set { ((IList)this)[index] = value; } + } + + /// + public void Remove(ColumnStyle columnStyle) { ((IList)this).Remove(columnStyle); } + + /// + public bool Contains(ColumnStyle columnStyle) { return ((IList)this).Contains(columnStyle); } + + /// + public int IndexOf(ColumnStyle columnStyle) { return ((IList)this).IndexOf(columnStyle); } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutPanel.cs b/WindowsForms/Managed/System/WinForms/TableLayoutPanel.cs new file mode 100644 index 000000000..8dded8814 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutPanel.cs @@ -0,0 +1,587 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + using System.Runtime.InteropServices; + + /// + [ProvideProperty("ColumnSpan", typeof(Control))] + [ProvideProperty("RowSpan", typeof(Control))] + [ProvideProperty("Row", typeof(Control))] + [ProvideProperty("Column", typeof(Control))] + [ProvideProperty("CellPosition", typeof(Control))] + [DefaultProperty("ColumnCount")] + [DesignerSerializer("System.Windows.Forms.Design.TableLayoutPanelCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)] + [Docking(DockingBehavior.Never)] + [Designer("System.Windows.Forms.Design.TableLayoutPanelDesigner, " + AssemblyRef.SystemDesign)] + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [SRDescription(SR.DescriptionTableLayoutPanel)] + public class TableLayoutPanel : Panel, IExtenderProvider { + private TableLayoutSettings _tableLayoutSettings; + private static readonly object EventCellPaint = new object(); + + /// + public TableLayoutPanel() { + _tableLayoutSettings = TableLayout.CreateSettings(this); + } + + /// + public override LayoutEngine LayoutEngine { + get { return TableLayout.Instance; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public TableLayoutSettings LayoutSettings { + get { + return _tableLayoutSettings; + } + set { + if (value != null && value.IsStub) { + // WINRES only scenario. + // we only support table layout settings that have been created from a type converter. + // this is here for localization (WinRes) support. + using (new LayoutTransaction(this, this, PropertyNames.LayoutSettings)) { + // apply RowStyles, ColumnStyles, Row & Column assignments. + _tableLayoutSettings.ApplySettings(value); + } + } + else { + throw new NotSupportedException(SR.GetString(SR.TableLayoutSettingSettingsIsNotSupported)); + } + + } + } + + + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + Localizable(true) + ] + public new BorderStyle BorderStyle { + get { return base.BorderStyle; } + set { + base.BorderStyle = value; + Debug.Assert(BorderStyle == value, "BorderStyle should be the same as we set it"); + } + } + + + /// + [ + DefaultValue(TableLayoutPanelCellBorderStyle.None), + SRCategory(SR.CatAppearance), + SRDescription(SR.TableLayoutPanelCellBorderStyleDescr), + Localizable(true) + ] + public TableLayoutPanelCellBorderStyle CellBorderStyle { + get { return _tableLayoutSettings.CellBorderStyle; } + set { + _tableLayoutSettings.CellBorderStyle = value; + + // PERF: dont turn on ResizeRedraw unless we know we need it. + if (value != TableLayoutPanelCellBorderStyle.None) { + SetStyle(ControlStyles.ResizeRedraw, true); + } + this.Invalidate(); + Debug.Assert(CellBorderStyle == value, "CellBorderStyle should be the same as we set it"); + } + } + + private int CellBorderWidth { + get { return _tableLayoutSettings.CellBorderWidth; } + } + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [SRDescription(SR.ControlControlsDescr)] + public new TableLayoutControlCollection Controls { + get { return (TableLayoutControlCollection)base.Controls; } + } + + /// + /// + /// This sets the maximum number of columns allowed on this table instead of allocating + /// actual spaces for these columns. So it is OK to set ColumnCount to Int32.MaxValue without + /// causing out of memory exception + /// + [SRDescription(SR.GridPanelColumnsDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(0)] + [Localizable(true)] + public int ColumnCount { + get { return _tableLayoutSettings.ColumnCount; } + set { + _tableLayoutSettings.ColumnCount = value; + Debug.Assert(ColumnCount == value, "ColumnCount should be the same as we set it"); + } + } + + /// + /// + /// Specifies if a TableLayoutPanel will gain additional rows or columns once its existing cells + /// become full. If the value is 'FixedSize' then the TableLayoutPanel will throw an exception + /// when the TableLayoutPanel is over-filled. + /// + [SRDescription(SR.TableLayoutPanelGrowStyleDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(TableLayoutPanelGrowStyle.AddRows)] + public TableLayoutPanelGrowStyle GrowStyle { + get { + return _tableLayoutSettings.GrowStyle; + } + set { + _tableLayoutSettings.GrowStyle = value; + } + } + + /// + /// + /// This sets the maximum number of rows allowed on this table instead of allocating + /// actual spaces for these rows. So it is OK to set RowCount to Int32.MaxValue without + /// causing out of memory exception + /// + [SRDescription(SR.GridPanelRowsDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(0)] + [Localizable(true)] + public int RowCount { + get { return _tableLayoutSettings.RowCount; } + set { _tableLayoutSettings.RowCount = value; } + } + + /// + [SRDescription(SR.GridPanelRowStylesDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [SRCategory(SR.CatLayout)] + [DisplayName("Rows")] + [MergableProperty(false)] + [Browsable(false)] + public TableLayoutRowStyleCollection RowStyles { + get { return _tableLayoutSettings.RowStyles; } + } + + /// + [SRDescription(SR.GridPanelColumnStylesDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [SRCategory(SR.CatLayout)] + [DisplayName("Columns")] + [Browsable(false)] + [MergableProperty(false)] + public TableLayoutColumnStyleCollection ColumnStyles { + get { return _tableLayoutSettings.ColumnStyles; } + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Control.ControlCollection CreateControlsInstance() { + return new TableLayoutControlCollection(this); + } + + private bool ShouldSerializeControls() { + TableLayoutControlCollection collection = this.Controls; + return collection != null && collection.Count > 0; + } + + #region Extended Properties + /// + /// + bool IExtenderProvider.CanExtend(object obj) { + Control control = obj as Control; + return control != null && control.Parent == this; + } + + /// + [SRDescription(SR.GridPanelGetColumnSpanDescr)] + [DefaultValue(1)] + [SRCategory(SR.CatLayout)] + [DisplayName("ColumnSpan")] + public int GetColumnSpan(Control control) { + return _tableLayoutSettings.GetColumnSpan(control); + } + + /// + public void SetColumnSpan(Control control, int value) { + // layout.SetColumnSpan() throws ArgumentException if out of range. + _tableLayoutSettings.SetColumnSpan(control, value); + Debug.Assert(GetColumnSpan(control) == value, "GetColumnSpan should be the same as we set it"); + } + + /// + [SRDescription(SR.GridPanelGetRowSpanDescr)] + [DefaultValue(1)] + [SRCategory(SR.CatLayout)] + [DisplayName("RowSpan")] + public int GetRowSpan(Control control) { + return _tableLayoutSettings.GetRowSpan(control); + } + + /// + public void SetRowSpan(Control control, int value) { + // layout.SetRowSpan() throws ArgumentException if out of range. + _tableLayoutSettings.SetRowSpan(control, value); + Debug.Assert(GetRowSpan(control) == value, "GetRowSpan should be the same as we set it"); + } + + //get the row position of the control + /// + [DefaultValue(-1)] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer + [SRDescription(SR.GridPanelRowDescr)] + [SRCategory(SR.CatLayout)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [DisplayName("Row")] + public int GetRow(Control control) { + return _tableLayoutSettings.GetRow(control); + } + + //set the row position of the control + /// + public void SetRow(Control control, int row) { + _tableLayoutSettings.SetRow(control, row); + Debug.Assert(GetRow(control) == row, "GetRow should be the same as we set it"); + } + + //get the row and column position of the control + /// + [DefaultValue(typeof(TableLayoutPanelCellPosition), "-1,-1")] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer + [SRDescription(SR.GridPanelCellPositionDescr)] + [SRCategory(SR.CatLayout)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [DisplayName("Cell")] + public TableLayoutPanelCellPosition GetCellPosition(Control control) { + return _tableLayoutSettings.GetCellPosition(control); + } + + //set the row and column of the control + /// + public void SetCellPosition(Control control, TableLayoutPanelCellPosition position) { + _tableLayoutSettings.SetCellPosition(control, position); + } + + + + //get the column position of the control + /// + [DefaultValue(-1)] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer + [SRDescription(SR.GridPanelColumnDescr)] + [SRCategory(SR.CatLayout)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [DisplayName("Column")] + public int GetColumn(Control control) { + return _tableLayoutSettings.GetColumn(control); + } + + //set the column position of the control + /// + public void SetColumn(Control control, int column) { + _tableLayoutSettings.SetColumn(control, column); + Debug.Assert(GetColumn(control) == column, "GetColumn should be the same as we set it"); + } + + /// + /// + /// get the control which covers the specified row and column. return null if we can't find one + /// + public Control GetControlFromPosition (int column, int row) { + return (Control)_tableLayoutSettings.GetControlFromPosition(column, row); + } + + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Control instead of IArrangedElement intentionally + public TableLayoutPanelCellPosition GetPositionFromControl (Control control) { + return _tableLayoutSettings.GetPositionFromControl(control); + } + + /// + /// + /// This returns an array representing the widths (in pixels) of the columns in the TableLayoutPanel. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public int[] GetColumnWidths() { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this); + if (containerInfo.Columns == null) { + return new int[0]; + } + + int[] cw = new int[containerInfo.Columns.Length]; + for(int i = 0; i < containerInfo.Columns.Length; i++) { + cw[i] = containerInfo.Columns[i].MinSize; + } + return cw; + } + + /// + /// + /// This returns an array representing the heights (in pixels) of the rows in the TableLayoutPanel. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public int[] GetRowHeights() { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this); + if (containerInfo.Rows == null) { + return new int[0]; + } + + int[] rh = new int[containerInfo.Rows.Length]; + for(int i = 0; i < containerInfo.Rows.Length; i++) { + rh[i] = containerInfo.Rows[i].MinSize; + } + return rh; + } + + + #endregion + + #region PaintCode + + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.TableLayoutPanelOnPaintCellDescr)] + public event TableLayoutCellPaintEventHandler CellPaint { + add { + Events.AddHandler(EventCellPaint, value); + } + remove { + Events.RemoveHandler(EventCellPaint, value); + } + } + + /// + /// + /// + /// When a layout fires, make sure we're painting all of our + /// cell borders. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLayout(LayoutEventArgs levent) { + base.OnLayout(levent); + this.Invalidate(); + } + + + /// + protected virtual void OnCellPaint(TableLayoutCellPaintEventArgs e) { + TableLayoutCellPaintEventHandler handler = (TableLayoutCellPaintEventHandler)Events[EventCellPaint]; + if (handler != null) { + handler(this, e); + } + } + + /// + protected override void OnPaintBackground(PaintEventArgs e) { + base.OnPaintBackground(e); + + + + // paint borderstyles on top of the background image in WM_ERASEBKGND + + int cellBorderWidth = this.CellBorderWidth; + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this); + TableLayout.Strip[] colStrips = containerInfo.Columns; + TableLayout.Strip[] rowStrips = containerInfo.Rows; + TableLayoutPanelCellBorderStyle cellBorderStyle = this.CellBorderStyle; + + + + if (colStrips == null || rowStrips == null) { + return; + } + int cols = colStrips.Length; + int rows = rowStrips.Length; + + int totalColumnWidths = 0, totalColumnHeights = 0; + + Graphics g = e.Graphics; + Rectangle displayRect = DisplayRectangle; + Rectangle clipRect = e.ClipRectangle; + + //leave the space for the border + int startx; + bool isRTL = (RightToLeft == RightToLeft.Yes); + if (isRTL) { + startx = displayRect.Right - (cellBorderWidth / 2); + } + else { + startx = displayRect.X + (cellBorderWidth / 2); + } + + for (int i = 0; i < cols; i++) { + int starty = displayRect.Y + (cellBorderWidth / 2); + + if (isRTL) { + startx -= colStrips[i].MinSize; + } + + for (int j = 0; j < rows; j++) { + Rectangle outsideCellBounds = new Rectangle(startx, starty, ((TableLayout.Strip)colStrips[i]).MinSize, ((TableLayout.Strip)rowStrips[j]).MinSize); + + Rectangle insideCellBounds = new Rectangle(outsideCellBounds.X + (cellBorderWidth + 1) / 2, outsideCellBounds.Y + (cellBorderWidth + 1)/ 2, outsideCellBounds.Width - (cellBorderWidth + 1) / 2, outsideCellBounds.Height - (cellBorderWidth + 1) / 2); + + if (clipRect.IntersectsWith(insideCellBounds)) { + //first, call user's painting code + using (TableLayoutCellPaintEventArgs pcea = new TableLayoutCellPaintEventArgs(g, clipRect, insideCellBounds, i, j)) { + OnCellPaint(pcea); + } + // paint the table border on top. + ControlPaint.PaintTableCellBorder(cellBorderStyle, g, outsideCellBounds); + } + starty += rowStrips[j].MinSize; + // Only sum this up once... + if (i == 0) { + totalColumnHeights += rowStrips[j].MinSize; + } + } + + if (!isRTL) { + startx += colStrips[i].MinSize; + } + totalColumnWidths += colStrips[i].MinSize; + } + + + if (!HScroll && !VScroll && cellBorderStyle != TableLayoutPanelCellBorderStyle.None) { + Rectangle tableBounds = new Rectangle(cellBorderWidth/2 + displayRect.X, cellBorderWidth/2 + displayRect.Y, displayRect.Width - cellBorderWidth, displayRect.Height - cellBorderWidth); + // paint the border of the table if we are not auto scrolling. + // if the borderStyle is Inset or Outset, we can only paint the lower bottom half since otherwise we will have 1 pixel loss at the border. + if (cellBorderStyle == TableLayoutPanelCellBorderStyle.Inset) { + g.DrawLine(SystemPens.ControlDark, tableBounds.Right, tableBounds.Y, tableBounds.Right, tableBounds.Bottom); + g.DrawLine(SystemPens.ControlDark, tableBounds.X, tableBounds.Y + tableBounds.Height - 1, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1); + } + else if (cellBorderStyle == TableLayoutPanelCellBorderStyle.Outset) { + using (Pen pen = new Pen(SystemColors.Window)) { + g.DrawLine(pen, tableBounds.X + tableBounds.Width - 1, tableBounds.Y, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1); + g.DrawLine(pen, tableBounds.X, tableBounds.Y + tableBounds.Height - 1, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1); + } + } + else { + ControlPaint.PaintTableCellBorder(cellBorderStyle, g, tableBounds); + } + ControlPaint.PaintTableControlBorder(cellBorderStyle, g, displayRect); + } + else { + ControlPaint.PaintTableControlBorder(cellBorderStyle, g, displayRect); + } + + } + + + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + + base.ScaleCore(dx, dy); + ScaleAbsoluteStyles(new SizeF(dx,dy)); + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + base.ScaleControl(factor, specified); + ScaleAbsoluteStyles(factor); + } + + private void ScaleAbsoluteStyles(SizeF factor) { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this); + int i = 0; + + // VSWhidbey 432427: the last row/column can be larger than the + // absolutely styled column width. + int lastRowHeight = -1; + int lastRow = containerInfo.Rows.Length -1; + if (containerInfo.Rows.Length > 0) { + lastRowHeight = containerInfo.Rows[lastRow].MinSize; + } + + int lastColumnHeight = -1; + int lastColumn = containerInfo.Columns.Length -1; + if (containerInfo.Columns.Length > 0) { + lastColumnHeight = containerInfo.Columns[containerInfo.Columns.Length -1].MinSize; + } + + foreach(ColumnStyle cs in ColumnStyles) { + if (cs.SizeType == SizeType.Absolute){ + if (i == lastColumn && lastColumnHeight > 0) { + // the last column is typically expanded to fill the table. use the actual + // width in this case. + cs.Width = (float)Math.Round(lastColumnHeight * factor.Width); + } + else { + cs.Width = (float)Math.Round(cs.Width * factor.Width); + } + } + i++; + } + + i = 0; + + foreach(RowStyle rs in RowStyles) { + if (rs.SizeType == SizeType.Absolute) { + if (i == lastRow && lastRowHeight > 0) { + // the last row is typically expanded to fill the table. use the actual + // width in this case. + rs.Height = (float)Math.Round(lastRowHeight * factor.Height); + } + else { + rs.Height = (float)Math.Round(rs.Height * factor.Height); + } + } + } + + } + + #endregion + } + + #region ControlCollection + /// + /// + /// Represents a collection of controls on the TableLayoutPanel. + /// + [ListBindable(false)] + [DesignerSerializer("System.Windows.Forms.Design.TableLayoutControlCollectionCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)] + public class TableLayoutControlCollection : Control.ControlCollection { + private TableLayoutPanel _container; + + /// + public TableLayoutControlCollection(TableLayoutPanel container) : base(container) { + _container = (TableLayoutPanel)container; + } + + //the container of this TableLayoutControlCollection + /// + public TableLayoutPanel Container { + get { return _container; } + } + + //Add control to cell (x, y) on the table. The control becomes absolutely positioned if neither x nor y is equal to -1 + /// + public virtual void Add(Control control, int column, int row) { + base.Add(control); + _container.SetColumn(control, column); + _container.SetRow(control, row); + } + } + #endregion +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellBorderStyle.cs b/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellBorderStyle.cs new file mode 100644 index 000000000..f16446070 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellBorderStyle.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + using System.ComponentModel; + + /// + /// + /// + /// + public enum TableLayoutPanelCellBorderStyle + { + /// + None = 0, + + /// + Single = 1, + + /// + Inset = 2, + + /// + InsetDouble = 3, + + /// + Outset = 4, + + /// + OutsetDouble = 5, + + /// + OutsetPartial = 6 + } +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellPosition.cs b/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellPosition.cs new file mode 100644 index 000000000..86bb70219 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutPanelCellPosition.cs @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + + [TypeConverterAttribute(typeof(TableLayoutPanelCellPositionTypeConverter))] + public struct TableLayoutPanelCellPosition { + + private int row; + private int column; + + public TableLayoutPanelCellPosition(int column, int row) { + if (row < -1) { + throw new ArgumentOutOfRangeException("row", SR.GetString(SR.InvalidArgument, "row", (row).ToString(CultureInfo.CurrentCulture))); + } + if (column < -1) { + throw new ArgumentOutOfRangeException("column", SR.GetString(SR.InvalidArgument, "column", (column).ToString(CultureInfo.CurrentCulture))); + } + this.row = row; + this.column = column; + } + + public int Row { + get { + return row; + } + set { + row = value; + } + } + + public int Column { + get { + return column; + } + set { + column = value; + } + } + public override bool Equals(object other) { + if(other is TableLayoutPanelCellPosition) { + TableLayoutPanelCellPosition dpeOther = (TableLayoutPanelCellPosition) other; + + return (dpeOther.row == row && + dpeOther.column == column); + } + return false; + } + public static bool operator ==(TableLayoutPanelCellPosition p1, TableLayoutPanelCellPosition p2) { + return p1.Row == p2.Row && p1.Column == p2.Column; + } + public static bool operator !=(TableLayoutPanelCellPosition p1, TableLayoutPanelCellPosition p2) { + return !(p1 == p2); + } + + + public override string ToString() { + return Column.ToString(CultureInfo.CurrentCulture) + "," + Row.ToString(CultureInfo.CurrentCulture); + } + + public override int GetHashCode() { + // VSWhidbey #130081: structs should implement GetHashCode for perf + return WindowsFormsUtils.GetCombinedHashCodes( + this.row, + this.column); + } + } + + internal class TableLayoutPanelCellPositionTypeConverter : TypeConverter { + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + + string text = ((string)value).Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 2 integer values. + // + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(new char[] {sep}); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 2) { + return new TableLayoutPanelCellPosition(values[0], values[1]); + } + else { + throw new ArgumentException(SR.GetString(SR.TextParseFailedFormat, + text, + "column, row")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is TableLayoutPanelCellPosition) { + TableLayoutPanelCellPosition cellPosition = (TableLayoutPanelCellPosition) value; + + + return new InstanceDescriptor( + typeof(TableLayoutPanelCellPosition).GetConstructor(new Type[] {typeof(int), typeof(int)}), + new object[] {cellPosition.Column, cellPosition.Row}); + + } + return base.ConvertTo(context, culture, value, destinationType); + } + + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + + return new TableLayoutPanelCellPosition( + (int)propertyValues["Column"], + (int)propertyValues["Row"] + ); + + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(TableLayoutPanelCellPosition), attributes); + return props.Sort(new string[] {"Column","Row"}); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutPanelGrowStyle.cs b/WindowsForms/Managed/System/WinForms/TableLayoutPanelGrowStyle.cs new file mode 100644 index 000000000..80c2bb888 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutPanelGrowStyle.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies if a TableLayoutPanel will gain additional rows or columns once its existing cells + /// become full. If the value is 'None' then the TableLayoutPanel will throw an exception + /// when the TableLayoutPanel is over-filled. + /// + /// + public enum TableLayoutPanelGrowStyle { + + /// + /// + /// + /// The TableLayoutPanel will not allow additional rows or columns once it is full. + /// + /// + FixedSize = 0, + + /// + /// + /// + /// The TableLayoutPanel will gain additional rows once it becomes full. + /// + /// + AddRows = 1, + + /// + /// + /// + /// The TableLayoutPanel will gain additional columns once it becomes full. + /// + /// + AddColumns = 2 + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutRowStyleCollection.cs b/WindowsForms/Managed/System/WinForms/TableLayoutRowStyleCollection.cs new file mode 100644 index 000000000..1893cc7f5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutRowStyleCollection.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + + /// + public class TableLayoutRowStyleCollection : TableLayoutStyleCollection { + + internal TableLayoutRowStyleCollection(IArrangedElement Owner) : base(Owner) {} + internal TableLayoutRowStyleCollection() : base(null) {} + + internal override string PropertyName { + get { return PropertyNames.RowStyles; } + } + + /// + public int Add(RowStyle rowStyle) { return ((IList)this).Add(rowStyle); } + + /// + public void Insert(int index, RowStyle rowStyle) { ((IList)this).Insert(index, rowStyle); } + + /// + public new RowStyle this[int index] { + get { return (RowStyle)((IList)this)[index]; } + set { ((IList)this)[index] = value; } + } + + /// + public void Remove(RowStyle rowStyle) { ((IList)this).Remove(rowStyle); } + + /// + public bool Contains(RowStyle rowStyle) { return ((IList)this).Contains(rowStyle); } + + /// + public int IndexOf(RowStyle rowStyle) { return ((IList)this).IndexOf(rowStyle); } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutSettings.cs b/WindowsForms/Managed/System/WinForms/TableLayoutSettings.cs new file mode 100644 index 000000000..0537b731f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutSettings.cs @@ -0,0 +1,777 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + using System.Runtime.Serialization; + using System.Security.Permissions; + + /// + /// this is a wrapper class to expose interesting properties of TableLayout + [ + TypeConverter(typeof(TableLayoutSettingsTypeConverter)), + Serializable + ] + public sealed class TableLayoutSettings : LayoutSettings, ISerializable { + + + static private int[] borderStyleToOffset = { + /*None = */ 0, + /*Single = */ 1, + /*Inset = */ 2, + /*InsetDouble = */ 3, + /*Outset = */ 2, + /*OutsetDouble = */ 3, + /*OutsetPartial = */ 3 + }; + private TableLayoutPanelCellBorderStyle _borderStyle; + private TableLayoutSettingsStub _stub; + + // used by TableLayoutSettingsTypeConverter + internal TableLayoutSettings() : base(null){ + _stub = new TableLayoutSettingsStub(); + } + + internal TableLayoutSettings(IArrangedElement owner) : base(owner) {} + + [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] + internal TableLayoutSettings(SerializationInfo serializationInfo, StreamingContext context) : this() { + TypeConverter converter = TypeDescriptor.GetConverter(this); + string stringVal = serializationInfo.GetString("SerializedString"); + + if (!String.IsNullOrEmpty(stringVal) && converter != null) { + TableLayoutSettings tls = converter.ConvertFromInvariantString(stringVal) as TableLayoutSettings; + if (tls != null) { + this.ApplySettings(tls); + } + } + } + + /// + public override LayoutEngine LayoutEngine { + get { return TableLayout.Instance; } + } + + private TableLayout TableLayout { + get { return (TableLayout) this.LayoutEngine; } + } + + + /// + /// internal as this is a TableLayoutPanel feature only + [DefaultValue(TableLayoutPanelCellBorderStyle.None), SRCategory(SR.CatAppearance), SRDescription(SR.TableLayoutPanelCellBorderStyleDescr)] + internal TableLayoutPanelCellBorderStyle CellBorderStyle { + get { return _borderStyle; } + set { + //valid values are 0x0 to 0x6 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TableLayoutPanelCellBorderStyle.None, (int)TableLayoutPanelCellBorderStyle.OutsetPartial)){ + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "CellBorderStyle", value.ToString())); + } + _borderStyle = value; + //set the CellBorderWidth according to the current CellBorderStyle. + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + containerInfo.CellBorderWidth = borderStyleToOffset[(int)value]; + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.CellBorderStyle); + Debug.Assert(CellBorderStyle == value, "CellBorderStyle should be the same as we set"); + } + } + + [DefaultValue(0)] + internal int CellBorderWidth { + get { return TableLayout.GetContainerInfo(Owner).CellBorderWidth; } + } + + /// + /// + /// This sets the maximum number of columns allowed on this table instead of allocating + /// actual spaces for these columns. So it is OK to set ColumnCount to Int32.MaxValue without + /// causing out of memory exception + /// + [SRDescription(SR.GridPanelColumnsDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(0)] + public int ColumnCount { + get { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + return containerInfo.MaxColumns; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("ColumnCount", value, SR.GetString (SR.InvalidLowBoundArgumentEx, "ColumnCount", value.ToString (CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + containerInfo.MaxColumns = value; + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Columns); + Debug.Assert(ColumnCount == value, "the max columns should equal to the value we set it to"); + + } + } + + /// + /// + /// This sets the maximum number of rows allowed on this table instead of allocating + /// actual spaces for these rows. So it is OK to set RowCount to Int32.MaxValue without + /// causing out of memory exception + /// + [SRDescription(SR.GridPanelRowsDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(0)] + public int RowCount { + get { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + return containerInfo.MaxRows; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("RowCount", value, SR.GetString (SR.InvalidLowBoundArgumentEx, "RowCount", value.ToString (CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + containerInfo.MaxRows = value; + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Rows); + Debug.Assert(RowCount == value, "the max rows should equal to the value we set it to"); + + } + } + + /// + [SRDescription(SR.GridPanelRowStylesDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [SRCategory(SR.CatLayout)] + public TableLayoutRowStyleCollection RowStyles { + get { + if (IsStub) { + return _stub.RowStyles; + } + else { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + return containerInfo.RowStyles; + } + } + } + + /// + [SRDescription(SR.GridPanelColumnStylesDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [SRCategory(SR.CatLayout)] + public TableLayoutColumnStyleCollection ColumnStyles { + get { + if (IsStub) { + return _stub.ColumnStyles; + } + else { + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + return containerInfo.ColumnStyles; + } + } + } + + /// + /// + /// Specifies if a TableLayoutPanel will gain additional rows or columns once its existing cells + /// become full. If the value is 'FixedSize' then the TableLayoutPanel will throw an exception + /// when the TableLayoutPanel is over-filled. + /// + [SRDescription(SR.TableLayoutPanelGrowStyleDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(TableLayoutPanelGrowStyle.AddRows)] + public TableLayoutPanelGrowStyle GrowStyle { + get { + return TableLayout.GetContainerInfo(Owner).GrowStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TableLayoutPanelGrowStyle.FixedSize, (int)TableLayoutPanelGrowStyle.AddColumns)){ + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "GrowStyle", value.ToString())); + } + + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner); + if (containerInfo.GrowStyle != value) { + containerInfo.GrowStyle = value; + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.GrowStyle); + } + } + } + + internal bool IsStub { + get { + if ( _stub != null) { + Debug.Assert( _stub.IsValid, "seems like we're still partying on an object that's given over its rows and columns, that's a nono."); + return true; + } + return false; + } + } + + internal void ApplySettings(TableLayoutSettings settings) { + if (settings.IsStub) { + if (!IsStub) { + // we're the real-live thing here, gotta walk through and touch controls + settings._stub.ApplySettings(this); + } + else { + // we're just copying another stub into us, just replace the member + _stub = settings._stub; + } + } + + } + #region Extended Properties + public int GetColumnSpan(object control) { + if (control == null) { + throw new ArgumentNullException("control"); + } + if (IsStub) { + return _stub.GetColumnSpan(control); + } + else { + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + return TableLayout.GetLayoutInfo(element).ColumnSpan; + } + } + + public void SetColumnSpan(object control, int value) { + if(value < 1) { + throw new ArgumentOutOfRangeException("ColumnSpan", SR.GetString(SR.InvalidArgument, "ColumnSpan", (value).ToString(CultureInfo.CurrentCulture))); + } + if (IsStub) { + _stub.SetColumnSpan(control, value); + } + else { + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + // LayoutInfo.SetColumnSpan() throws ArgumentException if out of range. + if (element.Container != null) { + TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container)); + } + TableLayout.GetLayoutInfo(element).ColumnSpan = value; + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.ColumnSpan); + Debug.Assert(GetColumnSpan(element) == value, "column span should equal to the value we set"); + } + + } + + public int GetRowSpan(object control) { + if (IsStub) { + return _stub.GetRowSpan(control); + } + else { + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + return TableLayout.GetLayoutInfo(element).RowSpan; + } + } + + public void SetRowSpan(object control, int value) { + if(value < 1) { + throw new ArgumentOutOfRangeException("RowSpan", SR.GetString(SR.InvalidArgument, "RowSpan", (value).ToString(CultureInfo.CurrentCulture))); + } + if (control == null) { + throw new ArgumentNullException("control"); + } + + if (IsStub) { + _stub.SetRowSpan(control, value); + } + else { + + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + // LayoutInfo.SetColumnSpan() throws ArgumentException if out of range. + if (element.Container != null) { + TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container)); + } + TableLayout.GetLayoutInfo(element).RowSpan = value; + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.RowSpan); + Debug.Assert(GetRowSpan(element) == value, "row span should equal to the value we set"); + } + + } + + //get the row position of the element + [SRDescription(SR.GridPanelRowDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(-1)] + public int GetRow(object control) { + if (control == null) { + throw new ArgumentNullException("control"); + } + if (IsStub) { + return _stub.GetRow(control); + } + else { + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element); + return layoutInfo.RowPosition; + } + } + + //set the row position of the element + //if we set the row position to -1, it will automatically switch the control from + //absolutely positioned to non-absolutely positioned + public void SetRow(object control, int row) { + if (control == null) { + throw new ArgumentNullException("control"); + } + if (row < -1) { + throw new ArgumentOutOfRangeException("Row", SR.GetString(SR.InvalidArgument, "Row", (row).ToString(CultureInfo.CurrentCulture))); + } + SetCellPosition(control, row, -1, /*rowSpecified=*/true, /*colSpecified=*/false); + + } + + //get the column position of the element + [SRDescription(SR.TableLayoutSettingsGetCellPositionDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(-1)] + public TableLayoutPanelCellPosition GetCellPosition(object control) { + if (control == null) { + throw new ArgumentNullException("control"); + } + return new TableLayoutPanelCellPosition(GetColumn(control), GetRow(control)); + } + + //get the column position of the element + [SRDescription(SR.TableLayoutSettingsSetCellPositionDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(-1)] + public void SetCellPosition(object control, TableLayoutPanelCellPosition cellPosition) { + if (control == null) { + throw new ArgumentNullException("control"); + } + SetCellPosition(control, cellPosition.Row, cellPosition.Column, /*rowSpecified=*/true, /*colSpecified=*/true); + + } + + //get the column position of the element + [SRDescription(SR.GridPanelColumnDescr)] + [SRCategory(SR.CatLayout)] + [DefaultValue(-1)] + public int GetColumn(object control) { + if (control == null) { + throw new ArgumentNullException("control"); + } + if (IsStub) { + return _stub.GetColumn(control); + } + else { + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element); + return layoutInfo.ColumnPosition; + } + } + + //set the column position of the element + //if we set the column position to -1, it will automatically switch the control from + //absolutely positioned to non-absolutely positioned + public void SetColumn(object control, int column) { + if (column < -1) { + throw new ArgumentException(SR.GetString(SR.InvalidArgument, "Column", (column).ToString(CultureInfo.CurrentCulture))); + } + if (IsStub) { + _stub.SetColumn(control, column); + } + else { + SetCellPosition(control, -1, column, /*rowSpecified=*/false, /*colSpecified=*/true); + } + } + + private void SetCellPosition(object control, int row, int column, bool rowSpecified, bool colSpecified) { + + if (IsStub) { + if (colSpecified) { + _stub.SetColumn(control, column); + } + if (rowSpecified) { + _stub.SetRow(control, row); + } + } + else { + + IArrangedElement element = LayoutEngine.CastToArrangedElement(control); + if (element.Container != null) { + TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container)); + } + TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element); + if (colSpecified) { + layoutInfo.ColumnPosition = column; + } + if (rowSpecified) { + layoutInfo.RowPosition = row; + } + LayoutTransaction.DoLayout(element.Container, element, PropertyNames.TableIndex); + Debug.Assert(!colSpecified || GetColumn(element) == column, "column position shoule equal to what we set"); + Debug.Assert(!rowSpecified || GetRow(element) == row, "row position shoule equal to what we set"); + } + } + + /// + ///get the element which covers the specified row and column. return null if we can't find one + /// + internal IArrangedElement GetControlFromPosition (int column, int row) { + return TableLayout.GetControlFromPosition(Owner, column, row); + } + + internal TableLayoutPanelCellPosition GetPositionFromControl (IArrangedElement element) { + return TableLayout.GetPositionFromControl(Owner, element); + } + + #endregion + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + TypeConverter converter = TypeDescriptor.GetConverter(this); + string stringVal = (converter != null) ? converter.ConvertToInvariantString(this) : null; + + if (!String.IsNullOrEmpty(stringVal)) { + si.AddValue("SerializedString", stringVal); + } + } + + internal List GetControlsInformation() { + if (IsStub) { + return _stub.GetControlsInformation(); + } + else { + List controlsInfo = new List(Owner.Children.Count); + + foreach (IArrangedElement element in Owner.Children) { + Control c = element as Control; + if (c != null) { + ControlInformation controlInfo = new ControlInformation(); + + // We need to go through the PropertyDescriptor for the Name property + // since it is shadowed. + PropertyDescriptor prop = TypeDescriptor.GetProperties(c)["Name"]; + if (prop != null && prop.PropertyType == typeof(string)) { + controlInfo.Name = prop.GetValue(c); + } + else { + Debug.Fail("Name property missing on control"); + } + + controlInfo.Row = GetRow(c); + controlInfo.RowSpan = GetRowSpan(c); + controlInfo.Column = GetColumn(c); + controlInfo.ColumnSpan = GetColumnSpan(c); + controlsInfo.Add(controlInfo); + } + + } + return controlsInfo; + } + + } + + internal struct ControlInformation { + internal object Name; + internal int Row; + internal int Column; + internal int RowSpan; + internal int ColumnSpan; + + internal ControlInformation(object name, int row, int column, int rowSpan, int columnSpan) { + Name = name; + Row = row; + Column = column; + RowSpan = rowSpan; + ColumnSpan = columnSpan; + } + } + + /// TableLayoutSettingsStub + /// contains information about + /// + private class TableLayoutSettingsStub { + + private static ControlInformation DefaultControlInfo = new ControlInformation(null, -1, -1, 1, 1); + private TableLayoutColumnStyleCollection columnStyles; + private TableLayoutRowStyleCollection rowStyles; + private Dictionary controlsInfo; + private bool isValid = true; + + public TableLayoutSettingsStub() { + } + + /// ApplySettings - applies settings from the stub into a full-fledged + /// TableLayoutSettings. + /// + /// NOTE: this is a one-time only operation - there is data loss to the stub + /// as a result of calling this function. we hand as much over to the other guy + /// so we dont have to reallocate anything + /// + internal void ApplySettings(TableLayoutSettings settings) { + // + // apply row,column,rowspan,colspan + // + TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(settings.Owner); + Control appliedControl = containerInfo.Container as Control; + if (appliedControl != null && controlsInfo != null) { + + // we store the control names, look up the controls + // in the appliedControl's control collection and apply the row,column settings. + foreach (object controlName in controlsInfo.Keys){ + ControlInformation controlInfo = controlsInfo[controlName]; + + // Look for the control in our table, we have to go through + // PropertyDescriptor rather than just going using appliedControl.Controls[controlName] + // because the Name property is shadowed at design time + foreach (Control tableControl in appliedControl.Controls) { + if (tableControl != null) { + string name = null; + PropertyDescriptor prop = TypeDescriptor.GetProperties(tableControl)["Name"]; + if (prop != null && prop.PropertyType == typeof(string)) { + name = prop.GetValue(tableControl) as string; + } + else { + Debug.Fail("Name property missing on control"); + } + if (WindowsFormsUtils.SafeCompareStrings(name, controlName as string, /* ignoreCase = */ false)) { + settings.SetRow(tableControl, controlInfo.Row); + settings.SetColumn(tableControl, controlInfo.Column); + settings.SetRowSpan(tableControl, controlInfo.RowSpan); + settings.SetColumnSpan(tableControl, controlInfo.ColumnSpan); + break; + } + } + } + } + } + + // + // assign over the row and column styles + // + containerInfo.RowStyles = rowStyles; + containerInfo.ColumnStyles = columnStyles; + + // since we've given over the styles to the other guy, null out. + columnStyles = null; + rowStyles = null; + + // set a flag for assertion detection. + isValid = false; + + } + + + + public TableLayoutColumnStyleCollection ColumnStyles { + get { + if (columnStyles == null) { + columnStyles = new TableLayoutColumnStyleCollection(); + } + return columnStyles; + } + } + + public bool IsValid { + get { return isValid; } + } + + public TableLayoutRowStyleCollection RowStyles { + get { + if (rowStyles == null) { + rowStyles = new TableLayoutRowStyleCollection(); + } + return rowStyles; + } + } + + internal List GetControlsInformation() { + + if (controlsInfo == null) { + return new List(); + } + + List listOfControlInfo = new List(controlsInfo.Count); + foreach (object name in controlsInfo.Keys ) { + ControlInformation ci = controlsInfo[name]; + ci.Name = name; + listOfControlInfo.Add(ci); + } + return listOfControlInfo; + } + + private ControlInformation GetControlInformation(object controlName) { + + if (controlsInfo == null) { + return DefaultControlInfo; + } + if (!controlsInfo.ContainsKey(controlName)) { + return DefaultControlInfo; + } + return controlsInfo[controlName]; + + } + + public int GetColumn(object controlName) { + return GetControlInformation(controlName).Column; + } + public int GetColumnSpan(object controlName) { + return GetControlInformation(controlName).ColumnSpan; + } + public int GetRow(object controlName) { + return GetControlInformation(controlName).Row; + } + public int GetRowSpan(object controlName) { + return GetControlInformation(controlName).RowSpan; + } + + private void SetControlInformation(object controlName, ControlInformation info) { + if (controlsInfo == null) { + controlsInfo = new Dictionary(); + } + controlsInfo[controlName] = info; + } + + public void SetColumn(object controlName, int column) { + if (GetColumn(controlName) != column) { + ControlInformation info = GetControlInformation(controlName); + info.Column = column; + SetControlInformation(controlName, info); + } + + } + public void SetColumnSpan(object controlName, int value) { + if (GetColumnSpan(controlName) != value) { + ControlInformation info = GetControlInformation(controlName); + info.ColumnSpan = value; + SetControlInformation(controlName, info); + } + } + public void SetRow(object controlName, int row) { + if (GetRow(controlName) != row) { + ControlInformation info = GetControlInformation(controlName); + info.Row = row; + SetControlInformation(controlName, info); + } + } + public void SetRowSpan(object controlName, int value) { + if (GetRowSpan(controlName) != value) { + ControlInformation info = GetControlInformation(controlName); + info.RowSpan = value; + SetControlInformation(controlName, info); + } + } + + + } // end of System.Windows.Forms.TableLayoutSettings + + internal class StyleConverter : TypeConverter { + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is TableLayoutStyle) { + TableLayoutStyle style = (TableLayoutStyle) value; + + switch(style.SizeType) { + case SizeType.AutoSize: + return new InstanceDescriptor( + style.GetType().GetConstructor(new Type[] {}), + new object[] {}); + case SizeType.Absolute: + case SizeType.Percent: + return new InstanceDescriptor( + style.GetType().GetConstructor(new Type[] {typeof(SizeType), typeof(int)}), + new object[] {style.SizeType, style.Size}); + default: + Debug.Fail("Unsupported SizeType."); + break; + } + } + return base.ConvertTo(context, culture, value, destinationType); + } + } + } + + + /// + public enum SizeType { + + /// + AutoSize, + + /// + Absolute, + + /// + Percent + } + + + /// + public class ColumnStyle : TableLayoutStyle { + + + /// + public ColumnStyle() {} + + + /// + public ColumnStyle(SizeType sizeType) { + this.SizeType = sizeType; + } + + + /// + public ColumnStyle(SizeType sizeType, float width) { + this.SizeType = sizeType; + this.Width = width; + } + + + /// + public float Width { + get { return base.Size; } + set { base.Size = value; } + } + } + + + /// + public class RowStyle : TableLayoutStyle { + + + /// + public RowStyle() {} + + /// + public RowStyle(SizeType sizeType) { + this.SizeType = sizeType; + } + + + /// + public RowStyle(SizeType sizeType, float height) { + this.SizeType = sizeType; + this.Height = height; + } + + + /// + public float Height { + get { return base.Size; } + set { base.Size = value; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutSettingsTypeConverter.cs b/WindowsForms/Managed/System/WinForms/TableLayoutSettingsTypeConverter.cs new file mode 100644 index 000000000..20d79c420 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutSettingsTypeConverter.cs @@ -0,0 +1,255 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.Layout")] +namespace System.Windows.Forms.Layout { + + using System; + using System.ComponentModel; + using System.Xml; + using System.Text; + using System.Globalization; + using System.Diagnostics; + using System.ComponentModel.Design.Serialization; + + public class TableLayoutSettingsTypeConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + if (value is string) { + XmlDocument tableLayoutSettingsXml = new XmlDocument(); + tableLayoutSettingsXml.LoadXml(value as string); + + TableLayoutSettings settings = new TableLayoutSettings(); + + ParseControls(settings, tableLayoutSettingsXml.GetElementsByTagName("Control")); + ParseStyles(settings, tableLayoutSettingsXml.GetElementsByTagName("Columns"), /*isColumn=*/true); + ParseStyles(settings, tableLayoutSettingsXml.GetElementsByTagName("Rows"), /*isColumn=*/false); + return settings; + } + return base.ConvertFrom(context, culture, value); + } + + + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (value is TableLayoutSettings && (destinationType == typeof(string))) { + TableLayoutSettings tableLayoutSettings = value as TableLayoutSettings; + + StringBuilder xmlStringBuilder = new StringBuilder(); + XmlWriter xmlWriter = XmlWriter.Create(xmlStringBuilder); + xmlWriter.WriteStartElement("TableLayoutSettings"); + + // + // write controls + // + xmlWriter.WriteStartElement("Controls"); + + foreach (TableLayoutSettings.ControlInformation c in tableLayoutSettings.GetControlsInformation()) { + + + xmlWriter.WriteStartElement("Control"); + xmlWriter.WriteAttributeString("Name", c.Name.ToString()); + xmlWriter.WriteAttributeString("Row",c.Row.ToString(CultureInfo.CurrentCulture)); + xmlWriter.WriteAttributeString("RowSpan", c.RowSpan.ToString(CultureInfo.CurrentCulture)); + + xmlWriter.WriteAttributeString("Column", c.Column.ToString(CultureInfo.CurrentCulture)); + xmlWriter.WriteAttributeString("ColumnSpan", c.ColumnSpan.ToString(CultureInfo.CurrentCulture)); + + xmlWriter.WriteEndElement(); + + } + xmlWriter.WriteEndElement(); // end Controls + + // + // write columns + // + xmlWriter.WriteStartElement("Columns"); + StringBuilder columnStyles = new StringBuilder(); + foreach (ColumnStyle colStyle in tableLayoutSettings.ColumnStyles) { + columnStyles.AppendFormat("{0},{1},", colStyle.SizeType, colStyle.Width); + } + if (columnStyles.Length > 0) { + columnStyles.Remove(columnStyles.Length - 1, 1); + } + xmlWriter.WriteAttributeString("Styles", columnStyles.ToString()); + xmlWriter.WriteEndElement(); // end columns + + // + // write rows + // + xmlWriter.WriteStartElement("Rows"); + StringBuilder rowStyles = new StringBuilder(); + foreach (RowStyle rowStyle in tableLayoutSettings.RowStyles) { + rowStyles.AppendFormat("{0},{1},", rowStyle.SizeType, rowStyle.Height); + } + if (rowStyles.Length > 0) { + rowStyles.Remove(rowStyles.Length - 1, 1); + } + xmlWriter.WriteAttributeString("Styles", rowStyles.ToString()); + xmlWriter.WriteEndElement(); // end Rows + + + xmlWriter.WriteEndElement(); // end TableLayoutSettings + + xmlWriter.Close(); + return xmlStringBuilder.ToString(); + + } + return base.ConvertTo(context, culture, value, destinationType); + } + + private string GetAttributeValue(XmlNode node, string attribute) { + XmlAttribute attr = node.Attributes[attribute]; + if (attr != null) { + return attr.Value; + } + return null; + } + + private int GetAttributeValue(XmlNode node, string attribute, int valueIfNotFound) { + string attributeValue = GetAttributeValue(node, attribute); + if (!string.IsNullOrEmpty(attributeValue)) { + int result; + if (Int32.TryParse(attributeValue, out result)) { + return result; + } + // dont throw because we should just gracefully fall back to valueIfNotFound; + Debug.Fail(string.Format(CultureInfo.CurrentCulture, "Failed to parse int: {0}", attributeValue)); + } + return valueIfNotFound; + } + + + + private void ParseControls(TableLayoutSettings settings, XmlNodeList controlXmlFragments) { + foreach (XmlNode controlXmlNode in controlXmlFragments) { + string name = GetAttributeValue(controlXmlNode, "Name"); + + if (!string.IsNullOrEmpty(name)) { + int row = GetAttributeValue(controlXmlNode, "Row", /*default*/-1); + int rowSpan = GetAttributeValue(controlXmlNode, "RowSpan", /*default*/1); + int column = GetAttributeValue(controlXmlNode, "Column", /*default*/-1); + int columnSpan = GetAttributeValue(controlXmlNode, "ColumnSpan",/*default*/1); + + settings.SetRow(name, row); + settings.SetColumn(name, column); + settings.SetRowSpan(name, rowSpan); + settings.SetColumnSpan(name, columnSpan); + } + + } + + + } + + + private void ParseStyles(TableLayoutSettings settings, XmlNodeList controlXmlFragments, bool columns) { + foreach (XmlNode styleXmlNode in controlXmlFragments) { + string styleString = GetAttributeValue(styleXmlNode, "Styles"); + Type sizeTypeType = typeof(SizeType); + + // styleString will consist of N Column/Row styles serialized in the following format + // (Percent | Absolute | AutoSize), (24 | 24.4 | 24,4) + // Two examples: + // Percent,23.3,Percent,46.7,Percent,30 + // Percent,23,3,Percent,46,7,Percent,30 + // Note we get either . or , based on the culture the TableLayoutSettings were serialized in + + if (!string.IsNullOrEmpty(styleString)) { + int currentIndex = 0; + int nextIndex; + while (currentIndex < styleString.Length) { + + // ---- SizeType Parsing ----------------- + nextIndex = currentIndex; + while (Char.IsLetter(styleString[nextIndex])) { + nextIndex++; + } + SizeType type = (SizeType)Enum.Parse(sizeTypeType, styleString.Substring(currentIndex, nextIndex - currentIndex), true); + + // ----- Float Parsing -------------- + // Find the next Digit (start of the float) + while (!char.IsDigit(styleString[nextIndex])) { + nextIndex++; + } + // Append digits left of the decimal delimiter(s) + StringBuilder floatStringBuilder = new StringBuilder(); + while ((nextIndex < styleString.Length) && (char.IsDigit(styleString[nextIndex]))) { + floatStringBuilder.Append(styleString[nextIndex]); + nextIndex++; + } + // Append culture invariant delimiter + floatStringBuilder.Append('.'); + // Append digits right of the decimal delimiter(s) + while ((nextIndex < styleString.Length) && (!char.IsLetter(styleString[nextIndex]))) { + if (char.IsDigit(styleString[nextIndex])) { + floatStringBuilder.Append(styleString[nextIndex]); + } + nextIndex++; + } + string floatString = floatStringBuilder.ToString(); + float width; + if (!float.TryParse(floatString, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out width)) { + Debug.Fail(string.Format(CultureInfo.CurrentCulture, "Failed to parse float for style: {0}", floatString)); + width = 0F; + } + + // Add new Column/Row Style + if (columns) { + settings.ColumnStyles.Add(new ColumnStyle(type, width)); + } + else { + settings.RowStyles.Add(new RowStyle(type, width)); + } + + // Go to the next Column/Row Style + currentIndex = nextIndex; + } + } + } + } + + + } + +} + + + + diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutStyle.cs b/WindowsForms/Managed/System/WinForms/TableLayoutStyle.cs new file mode 100644 index 000000000..982c2c39e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutStyle.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + + /// + [TypeConverterAttribute(typeof(TableLayoutSettings.StyleConverter))] + public abstract class TableLayoutStyle { + private IArrangedElement _owner; + private SizeType _sizeType = SizeType.AutoSize; + private float _size; + + /// + [DefaultValue(SizeType.AutoSize)] + public SizeType SizeType { + get { return _sizeType; } + set { + if (_sizeType != value) { + _sizeType = value; + if(Owner != null) { + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Style); + Control owner = Owner as Control; + if (owner != null) { + owner.Invalidate(); + } + } + } + } + } + + internal float Size { + get { return _size; } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("Size", SR.GetString(SR.InvalidLowBoundArgumentEx, "Size", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (_size != value) { + _size = value; + if(Owner != null) { + LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Style); + Control owner = Owner as Control; + if (owner != null) { + owner.Invalidate(); + } + } + } + } + } + + private bool ShouldSerializeSize() { + return SizeType != SizeType.AutoSize; + } + + internal IArrangedElement Owner { + get { return _owner; } + set { _owner = value; } + } + + //set the size without doing a layout + internal void SetSize(float size) { + Debug.Assert(size >= 0); + _size = size; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TableLayoutStyleCollection.cs b/WindowsForms/Managed/System/WinForms/TableLayoutStyleCollection.cs new file mode 100644 index 000000000..43587cfc6 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TableLayoutStyleCollection.cs @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Drawing.Design; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + + /// + [Editor("System.Windows.Forms.Design.StyleCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))] + public abstract class TableLayoutStyleCollection : IList { + private IArrangedElement _owner; + private ArrayList _innerList = new ArrayList(); + + internal TableLayoutStyleCollection(IArrangedElement owner) { + _owner = owner; + } + + internal IArrangedElement Owner { + get { return _owner; } + } + internal virtual string PropertyName { + get { return null; } + } + + /// + /// + int IList.Add(object style) { + EnsureNotOwned((TableLayoutStyle)style); + ((TableLayoutStyle)style).Owner = this.Owner; + int index = _innerList.Add(style); + PerformLayoutIfOwned(); + return index; + } + + /// + /// + public int Add(TableLayoutStyle style) { + return ((IList)this).Add(style); + } + + + /// + /// + void IList.Insert(int index, object style) { + EnsureNotOwned((TableLayoutStyle)style); + ((TableLayoutStyle)style).Owner = this.Owner; + _innerList.Insert(index, style); + PerformLayoutIfOwned(); + + } + + /// + /// + object IList.this[int index] { + get { return _innerList[index]; } + set { + TableLayoutStyle style = (TableLayoutStyle) value; + EnsureNotOwned(style); + style.Owner = this.Owner; + _innerList[index] = style; + PerformLayoutIfOwned(); + } + } + + /// + /// + public TableLayoutStyle this[int index] { + get { return (TableLayoutStyle)((IList)this)[index]; } + set { ((IList)this)[index] = value; } + } + + /// + /// + void IList.Remove(object style) { + ((TableLayoutStyle)style).Owner = null; + _innerList.Remove(style); + PerformLayoutIfOwned(); + } + + /// + public void Clear() { + foreach(TableLayoutStyle style in _innerList) { + style.Owner = null; + } + _innerList.Clear(); + PerformLayoutIfOwned(); + } + + /// + public void RemoveAt(int index) { + TableLayoutStyle style = (TableLayoutStyle) _innerList[index]; + style.Owner = null; + _innerList.RemoveAt(index); + PerformLayoutIfOwned(); + } + + // These methods just forward to _innerList. + bool IList.Contains(object style) { return _innerList.Contains(style); } + int IList.IndexOf(object style) { return _innerList.IndexOf(style); } + + // These properties / methods just forward to _innerList and are item-type agnostic. + bool IList.IsFixedSize { get {return _innerList.IsFixedSize;} } + bool IList.IsReadOnly { get {return _innerList.IsReadOnly;} } + void ICollection.CopyTo(System.Array array, int startIndex) { _innerList.CopyTo(array, startIndex); } + + /// + public int Count { get { return _innerList.Count; }} + bool ICollection.IsSynchronized { get{ return _innerList.IsSynchronized; }} + object ICollection.SyncRoot { get { return _innerList.SyncRoot; }} + IEnumerator IEnumerable.GetEnumerator() { return _innerList.GetEnumerator(); } + + private void EnsureNotOwned(TableLayoutStyle style) { + if(style.Owner != null) { + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, style.GetType().Name), "style"); + } + } + internal void EnsureOwnership(IArrangedElement owner) { + _owner = owner; + for (int i = 0; i < Count; i++) { + this[i].Owner = owner; + } + } + private void PerformLayoutIfOwned() { + if (this.Owner != null) { + LayoutTransaction.DoLayout(this.Owner, this.Owner, PropertyName); + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TextBox.cs b/WindowsForms/Managed/System/WinForms/TextBox.cs new file mode 100644 index 000000000..f9a455735 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TextBox.cs @@ -0,0 +1,901 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Windows.Forms; + using System.ComponentModel.Design; + using System.Drawing; + using Microsoft.Win32; + using System.Reflection; + using System.Text; + using System.Runtime.InteropServices; + using System.Collections.Specialized; + using System.Drawing.Design; + using System.Security; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// + /// Represents a Windows text box control. + /// + /// + + + [ + ClassInterface(ClassInterfaceType.AutoDispatch), + ComVisible(true), + Designer("System.Windows.Forms.Design.TextBoxDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionTextBox) + ] + public class TextBox : TextBoxBase { + + private static readonly object EVENT_TEXTALIGNCHANGED = new object(); + + /// + /// Controls whether or not the edit box consumes/respects ENTER key + /// presses. While this is typically desired by multiline edits, this + /// can interfere with normal key processing in a dialog. + /// + private bool acceptsReturn = false; + + /// + /// Indicates what the current special password character is. This is + /// displayed instead of any other text the user might enter. + /// + private char passwordChar = (char)0; + + private bool useSystemPasswordChar; + + /// + /// Controls whether or not the case of characters entered into the edit + /// box is forced to a specific case. + /// + private CharacterCasing characterCasing = System.Windows.Forms.CharacterCasing.Normal; + + /// + /// Controls which scrollbars appear by default. + /// + private ScrollBars scrollBars = System.Windows.Forms.ScrollBars.None; + + /// + /// Controls text alignment in the edit box. + /// + private HorizontalAlignment textAlign = HorizontalAlignment.Left; + + /// + /// True if the selection has been set by the user. If the selection has + /// never been set and we get focus, we focus all the text in the control + /// so we mimic the Windows dialog manager. + /// + private bool selectionSet = false; + + /// + /// + /// This stores the value for the autocomplete mode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// + private AutoCompleteMode autoCompleteMode = AutoCompleteMode.None; + + /// + /// + /// This stores the value for the autoCompleteSource mode which can be one of the values + /// from AutoCompleteSource enum. + /// + private AutoCompleteSource autoCompleteSource = AutoCompleteSource.None; + + /// + /// + /// This stores the custom StringCollection required for the autoCompleteSource when its set to CustomSource. + /// + private AutoCompleteStringCollection autoCompleteCustomSource; + private bool fromHandleCreate = false; + private StringSource stringSource = null; + + /// + public TextBox(){ + } + + + /// + /// + /// + /// Gets or sets a value indicating whether pressing ENTER + /// in a multiline + /// control creates a new line of text in the control or activates the default button + /// for the form. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxAcceptsReturnDescr) + ] + public bool AcceptsReturn { + get { + return acceptsReturn; + } + + set { + acceptsReturn = value; + } + } + + + /// + /// + /// This is the AutoCompleteMode which can be either + /// None, AutoSuggest, AutoAppend or AutoSuggestAppend. + /// This property in conjunction with AutoCompleteSource enables the AutoComplete feature for TextBox. + /// + [ + DefaultValue(AutoCompleteMode.None), + SRDescription(SR.TextBoxAutoCompleteModeDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteMode AutoCompleteMode { + get { + return autoCompleteMode; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoCompleteMode.None, (int)AutoCompleteMode.SuggestAppend)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteMode)); + } + bool resetAutoComplete = false; + if (autoCompleteMode != AutoCompleteMode.None && value == AutoCompleteMode.None) { + resetAutoComplete = true; + } + autoCompleteMode = value; + SetAutoComplete(resetAutoComplete); + } + } + + /// + /// + /// This is the AutoCompleteSource which can be one of the + /// values from AutoCompleteSource enumeration. + /// This property in conjunction with AutoCompleteMode enables the AutoComplete feature for TextBox. + /// + [ + DefaultValue(AutoCompleteSource.None), + SRDescription(SR.TextBoxAutoCompleteSourceDescr), + TypeConverterAttribute(typeof(TextBoxAutoCompleteSourceConverter)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteSource AutoCompleteSource { + get { + return autoCompleteSource; + } + set { + // FxCop: Avoid usage of Enum.IsDefined - this looks like an enum that could grow + if (!ClientUtils.IsEnumValid_NotSequential(value, + (int)value, + (int)AutoCompleteSource.None, + (int)AutoCompleteSource.AllSystemSources, + (int)AutoCompleteSource.AllUrl, + (int)AutoCompleteSource.CustomSource, + (int)AutoCompleteSource.FileSystem, + (int)AutoCompleteSource.FileSystemDirectories, + (int)AutoCompleteSource.HistoryList, + (int)AutoCompleteSource.ListItems, + (int)AutoCompleteSource.RecentlyUsedList)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoCompleteSource)); + } + if (value == AutoCompleteSource.ListItems) { + throw new NotSupportedException(SR.GetString(SR.TextBoxAutoCompleteSourceNoItems)); + } + + // VSWhidbey 466300 + if (value != AutoCompleteSource.None && value != AutoCompleteSource.CustomSource) + { + FileIOPermission fiop = new FileIOPermission(PermissionState.Unrestricted); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Demand(); + } + + autoCompleteSource = value; + SetAutoComplete(false); + } + } + + /// + /// + /// This is the AutoCompleteCustomSource which is custom StringCollection used when the + /// AutoCompleteSource is CustomSource. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.TextBoxAutoCompleteCustomSourceDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteStringCollection AutoCompleteCustomSource { + get { + if (autoCompleteCustomSource == null) { + autoCompleteCustomSource = new AutoCompleteStringCollection(); + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + return autoCompleteCustomSource; + } + set { + if (autoCompleteCustomSource != value) { + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + + autoCompleteCustomSource = value; + + if (value != null) { + autoCompleteCustomSource.CollectionChanged += new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + SetAutoComplete(false); + } + + } + } + + /// + /// + /// + /// Gets or sets whether the TextBox control + /// modifies the case of characters as they are typed. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(CharacterCasing.Normal), + SRDescription(SR.TextBoxCharacterCasingDescr) + ] + public CharacterCasing CharacterCasing { + get { + return characterCasing; + } + set { + if (characterCasing != value) { + //verify that 'value' is a valid enum type... + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)CharacterCasing.Normal, (int)CharacterCasing.Lower)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(CharacterCasing)); + } + + characterCasing = value; + RecreateHandle(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override bool Multiline { + get { + return base.Multiline; + } + set { + + if (Multiline != value) + { + base.Multiline = value; + if (value && AutoCompleteMode != AutoCompleteMode.None) + { + RecreateHandle(); + } + } + } + } + + /// + /// Determines if the control is in password protect mode. + /// + internal override bool PasswordProtect { + get { + return this.PasswordChar != '\0'; + } + } + + + /// + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + switch (characterCasing) { + case CharacterCasing.Lower: + cp.Style |= NativeMethods.ES_LOWERCASE; + break; + case CharacterCasing.Upper: + cp.Style |= NativeMethods.ES_UPPERCASE; + break; + } + + // Translate for Rtl if necessary + // + HorizontalAlignment align = RtlTranslateHorizontal(textAlign); + cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the ES_XXXX alignment styles + switch (align) { + case HorizontalAlignment.Left: + cp.Style |= NativeMethods.ES_LEFT; + break; + case HorizontalAlignment.Center: + cp.Style |= NativeMethods.ES_CENTER; + break; + case HorizontalAlignment.Right: + cp.Style |= NativeMethods.ES_RIGHT; + break; + } + + if (Multiline) { + // vs 59731: Don't show horizontal scroll bars which won't do anything + if ((scrollBars & ScrollBars.Horizontal) == ScrollBars.Horizontal + && textAlign == HorizontalAlignment.Left + && !WordWrap) { + cp.Style |= NativeMethods.WS_HSCROLL; + } + if ((scrollBars & ScrollBars.Vertical) == ScrollBars.Vertical) { + cp.Style |= NativeMethods.WS_VSCROLL; + } + } + + if (useSystemPasswordChar) { + cp.Style |= NativeMethods.ES_PASSWORD; + } + + return cp; + } + } + + /// + /// + /// + /// Gets or sets the character used to mask characters in a single-line text box + /// control used to enter passwords. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue((char)0), + Localizable(true), + SRDescription(SR.TextBoxPasswordCharDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public char PasswordChar { + get { + if (!IsHandleCreated) { + CreateHandle(); + } + return (char)SendMessage(NativeMethods.EM_GETPASSWORDCHAR, 0, 0); + } + set { + passwordChar = value; + if (!useSystemPasswordChar) { + if (IsHandleCreated) { + if (PasswordChar != value) { + // Set the password mode. + SendMessage(NativeMethods.EM_SETPASSWORDCHAR, value, 0); + + // Disable IME if setting the control to password mode. + VerifyImeRestrictedModeChanged(); + + ResetAutoComplete(false); + Invalidate(); + } + } + } + } + } + + /// + /// + /// + /// Gets or sets which scroll bars should + /// appear in a multiline + /// control. + /// + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(ScrollBars.None), + SRDescription(SR.TextBoxScrollBarsDescr) + ] + public ScrollBars ScrollBars { + get { + return scrollBars; + } + set { + if (scrollBars != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ScrollBars.None, (int)ScrollBars.Both)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ScrollBars)); + } + + scrollBars = value; + RecreateHandle(); + } + } + } + + internal override Size GetPreferredSizeCore(Size proposedConstraints) { + Size scrollBarPadding = Size.Empty; + + if(Multiline && !WordWrap && (ScrollBars & ScrollBars.Horizontal) != 0) { + scrollBarPadding.Height += SystemInformation.GetHorizontalScrollBarHeightForDpi(deviceDpi); + } + if(Multiline && (ScrollBars & ScrollBars.Vertical) != 0) { + scrollBarPadding.Width += SystemInformation.GetVerticalScrollBarWidthForDpi(deviceDpi); + } + + // Subtract the scroll bar padding before measuring + proposedConstraints -= scrollBarPadding; + + Size prefSize = base.GetPreferredSizeCore(proposedConstraints); + + return prefSize + scrollBarPadding; + } + + /// + /// + /// + /// Gets or sets + /// the current text in the text box. + /// + /// + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + selectionSet = false; + } + } + + /// + /// + /// + /// Gets or sets how text is + /// aligned in a + /// control. + /// Note: This code is duplicated in MaskedTextBox for simplicity. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(HorizontalAlignment.Left), + SRDescription(SR.TextBoxTextAlignDescr) + ] + public HorizontalAlignment TextAlign { + get { + return textAlign; + } + set { + if (textAlign != value) { + //verify that 'value' is a valid enum type... + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + + textAlign = value; + RecreateHandle(); + OnTextAlignChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// + /// Indicates if the text in the edit control should appear as + /// the default password character. This property has precedence + /// over the PasswordChar property. Whenever the UseSystemPasswordChar + /// is set to true, the default system password character is used, + /// any character set into PasswordChar is ignored. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxUseSystemPasswordCharDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public bool UseSystemPasswordChar { + get { + return useSystemPasswordChar; + } + set { + if (value != useSystemPasswordChar) { + useSystemPasswordChar = value; + + // RecreateHandle will update IME restricted mode. + RecreateHandle(); + + if (value) { + ResetAutoComplete(false); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnTextAlignChangedDescr)] + public event EventHandler TextAlignChanged { + add { + Events.AddHandler(EVENT_TEXTALIGNCHANGED, value); + } + + remove { + Events.RemoveHandler(EVENT_TEXTALIGNCHANGED, value); + } + } + + /// + protected override void Dispose(bool disposing) { + if (disposing) { + // VSWhidbey 281668 - Reset this just in case, because the SHAutoComplete stuff + // will subclass this guys wndproc (and nativewindow can't know about it). + // so this will undo it, but on a dispose we'll be Destroying the window anyay. + // + ResetAutoComplete(true); + if (autoCompleteCustomSource != null) { + autoCompleteCustomSource.CollectionChanged -= new CollectionChangeEventHandler(this.OnAutoCompleteCustomSourceChanged); + } + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + } + base.Dispose(disposing); + } + + /// + /// + /// + /// + /// Overridden to handle RETURN key. + /// + /// + protected override bool IsInputKey(Keys keyData) { + if (Multiline && (keyData & Keys.Alt) == 0) { + switch (keyData & Keys.KeyCode) { + case Keys.Return: + return acceptsReturn; + } + } + return base.IsInputKey(keyData); + } + + + private void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) { + if (AutoCompleteSource == AutoCompleteSource.CustomSource) + { + SetAutoComplete(true); + } + } + + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + // VSWhidbey 465708. Force repainting of the entire window frame + if (Application.RenderWithVisualStyles && this.IsHandleCreated && this.BorderStyle == BorderStyle.Fixed3D) { + SafeNativeMethods.RedrawWindow(new HandleRef(this, this.Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + if (this.AutoCompleteMode != AutoCompleteMode.None) { + //we always will recreate the handle when autocomplete mode is on + RecreateHandle(); + } + } + + /// + /// + /// + /// Overrideen to focus the text on first focus. + /// + protected override void OnGotFocus(EventArgs e) { + base.OnGotFocus(e); + if (!selectionSet) { + // We get one shot at selecting when we first get focus. If we don't + // do it, we still want to act like the selection was set. + selectionSet = true; + + // If the user didn't provide a selection, force one in. + if (SelectionLength == 0 && Control.MouseButtons == MouseButtons.None) { + SelectAll(); + } + } + } + + /// + /// + /// + /// Overridden to update the newly created handle with the settings of the + /// PasswordChar properties. + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + base.SetSelectionOnHandle(); + + if (passwordChar != 0) { + if (!useSystemPasswordChar) { + SendMessage(NativeMethods.EM_SETPASSWORDCHAR, passwordChar, 0); + } + } + + VerifyImeRestrictedModeChanged(); + + if (AutoCompleteMode != AutoCompleteMode.None) { + try + { + fromHandleCreate = true; + SetAutoComplete(false); + } + finally + { + fromHandleCreate = false; + } + } + } + + protected override void OnHandleDestroyed(EventArgs e) + { + if (stringSource != null) + { + stringSource.ReleaseAutoComplete(); + stringSource = null; + } + base.OnHandleDestroyed(e); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnTextAlignChanged(EventArgs e) { + EventHandler eh = Events[EVENT_TEXTALIGNCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Process a command key. + /// Native "EDIT" control does not support "Select All" shorcut represented by Ctrl-A keys, when in multiline mode, + /// and historically Microsoft TextBox did not support it either. + /// We are adding support for this shortcut for application targeting 4.6.1 and newer and for applications targeting 4.0 and newer + /// versions of the .NET Framework if they opt into this feature by adding the following config switch to the 'runtime' section of the app.config file: + /// + /// + /// + /// To opt out of this feature, when targeting 4.6.1 and newer, please set the above mentioned switch to true. + /// + /// m - the current windows message + /// keyData - bitmask containing one or more keys + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message m, Keys keyData) { + bool returnValue = base.ProcessCmdKey(ref m, keyData); + if (!returnValue && this.Multiline && !LocalAppContextSwitches.DoNotSupportSelectAllShortcutInMultilineTextBox + && this.ShortcutsEnabled && (keyData == (Keys.Control | Keys.A))) { + SelectAll(); + return true; + } + + return returnValue; + } + + /// + /// Replaces the portion of the text specified by startPos and length with the one passed in, + /// without resetting the undo buffer (if any). + /// This method is provided as an alternative to SelectedText which clears the undo buffer. + /// Observe that this method does not honor the MaxLength property as the parameter-less base's + /// Paste does, see VSW#487664 for more info. + /// + public void Paste(string text){ + base.SetSelectedTextInternal(text, false); + } + + /// + /// Performs the actual select without doing arg checking. + /// + internal override void SelectInternal(int start, int length, int textLen) { + // If user set selection into text box, mark it so we don't + // clobber it when we get focus. + selectionSet = true; + base.SelectInternal( start, length, textLen ); + } + + private string[] GetStringsForAutoComplete() + { + string[] strings = new string[AutoCompleteCustomSource.Count]; + for (int i = 0; i < AutoCompleteCustomSource.Count; i++) { + strings[i] = AutoCompleteCustomSource[i]; + } + return strings; + } + + + + /// + /// + /// Sets the AutoComplete mode in TextBox. + /// + internal void SetAutoComplete(bool reset) + { + //Autocomplete Not Enabled for Password enabled and MultiLine Textboxes. + if (Multiline || passwordChar != 0 || useSystemPasswordChar || AutoCompleteSource == AutoCompleteSource.None) { + return; + } + + if (AutoCompleteMode != AutoCompleteMode.None) { + if (!fromHandleCreate) + { + //RecreateHandle to avoid Leak. + // notice the use of member variable to avoid re-entrancy + AutoCompleteMode backUpMode = this.AutoCompleteMode; + autoCompleteMode = AutoCompleteMode.None; + RecreateHandle(); + autoCompleteMode = backUpMode; + } + + if (AutoCompleteSource == AutoCompleteSource.CustomSource) { + if (IsHandleCreated && AutoCompleteCustomSource != null) { + if (AutoCompleteCustomSource.Count == 0) { + ResetAutoComplete(true); + } + else { + if (stringSource == null) + { + stringSource = new StringSource(GetStringsForAutoComplete()); + if (!stringSource.Bind(new HandleRef(this, Handle), (int)AutoCompleteMode)) + { + throw new ArgumentException(SR.GetString(SR.AutoCompleteFailure)); + } + } + else + { + stringSource.RefreshList(GetStringsForAutoComplete()); + } + } + } + + } + else { + try { + if (IsHandleCreated) { + int mode = 0; + if (AutoCompleteMode == AutoCompleteMode.Suggest) { + mode |= NativeMethods.AUTOSUGGEST | NativeMethods.AUTOAPPEND_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.Append) { + mode |= NativeMethods.AUTOAPPEND | NativeMethods.AUTOSUGGEST_OFF; + } + if (AutoCompleteMode == AutoCompleteMode.SuggestAppend) { + mode |= NativeMethods.AUTOSUGGEST; + mode |= NativeMethods.AUTOAPPEND; + } + int ret = SafeNativeMethods.SHAutoComplete(new HandleRef(this, Handle) , (int)AutoCompleteSource | mode); + } + } + catch (SecurityException) { + // If we don't have full trust, degrade gracefully. Allow the control to + // function without auto-complete. Allow the app to continue running. + } + } + } + else if (reset) { + ResetAutoComplete(true); + } + } + + + // + /// + /// Resets the AutoComplete mode in TextBox. + /// + private void ResetAutoComplete(bool force) { + if ((AutoCompleteMode != AutoCompleteMode.None || force) && IsHandleCreated) { + int mode = (int)AutoCompleteSource.AllSystemSources | NativeMethods.AUTOSUGGEST_OFF | NativeMethods.AUTOAPPEND_OFF; + SafeNativeMethods.SHAutoComplete(new HandleRef(this, Handle) , mode); + } + } + + private void ResetAutoCompleteCustomSource() { + AutoCompleteCustomSource = null; + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + using (Pen pen = new Pen(VisualStyleInformation.TextControlBorder)) { + g.DrawRectangle(pen, rect); + } + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + //------------------------------------------------------------------------------------------------- + + /// + /// + /// + /// The edits window procedure. Inheritng classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the combo continues to function properly. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + // Work around a very obscure Windows issue. See ASURT 45255. + case NativeMethods.WM_LBUTTONDOWN: + MouseButtons realState = MouseButtons; + bool wasValidationCancelled = ValidationCancelled; + FocusInternal(); + if (realState == MouseButtons && + (!ValidationCancelled || wasValidationCancelled)) { + base.WndProc(ref m); + } + break; + //for readability ... so that we know whats happening ... + // case WM_LBUTTONUP is included here eventhough it just calls the base. + case NativeMethods.WM_LBUTTONUP: + base.WndProc(ref m); + break; + case NativeMethods.WM_PRINT: + WmPrint(ref m); + break; + default: + base.WndProc(ref m); + break; + } + + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TextBoxAutoCompleteSourceConverter.cs b/WindowsForms/Managed/System/WinForms/TextBoxAutoCompleteSourceConverter.cs new file mode 100644 index 000000000..6c4345a0a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TextBoxAutoCompleteSourceConverter.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Collections; + + internal class TextBoxAutoCompleteSourceConverter : EnumConverter { + public TextBoxAutoCompleteSourceConverter(Type type) : base(type) { + } + + /// + /// + /// + /// Gets a collection of standard values for the data type this validator is + /// designed for. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + StandardValuesCollection values = base.GetStandardValues(context); + ArrayList list = new ArrayList(); + int count = values.Count; + for (int i=0; i +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Collections; + using System.Collections.Specialized; + using System.Security.Permissions; + using System.Windows.Forms.Design; + using System.Windows.Forms.Layout; + using System.ComponentModel.Design; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using Microsoft.Win32; + using System.Reflection; + using System.Globalization; + + /// + /// + /// + /// Implements the basic functionality required by text + /// controls. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("TextChanged"), + DefaultBindingProperty("Text"), + Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + AssemblyRef.SystemDesign) + ] + public abstract class TextBoxBase : Control { + + // The boolean properties for this control are contained in the textBoxFlags bit + // vector. We can store up to 32 boolean values in this one vector. Here we + // create the bitmasks for each bit in the vector. + // + private static readonly int autoSize = BitVector32.CreateMask(); + private static readonly int hideSelection = BitVector32.CreateMask(autoSize); + private static readonly int multiline = BitVector32.CreateMask(hideSelection); + private static readonly int modified = BitVector32.CreateMask(multiline); + private static readonly int readOnly = BitVector32.CreateMask(modified); + private static readonly int acceptsTab = BitVector32.CreateMask(readOnly); + private static readonly int wordWrap = BitVector32.CreateMask(acceptsTab); + private static readonly int creatingHandle = BitVector32.CreateMask(wordWrap); + private static readonly int codeUpdateText = BitVector32.CreateMask(creatingHandle); + private static readonly int shortcutsEnabled = BitVector32.CreateMask(codeUpdateText); + private static readonly int scrollToCaretOnHandleCreated = BitVector32.CreateMask(shortcutsEnabled); + private static readonly int setSelectionOnHandleCreated = BitVector32.CreateMask(scrollToCaretOnHandleCreated); + + private static readonly object EVENT_ACCEPTSTABCHANGED = new object(); + private static readonly object EVENT_BORDERSTYLECHANGED = new object(); + private static readonly object EVENT_HIDESELECTIONCHANGED = new object(); + private static readonly object EVENT_MODIFIEDCHANGED = new object(); + private static readonly object EVENT_MULTILINECHANGED = new object(); + private static readonly object EVENT_READONLYCHANGED = new object(); + + /// + /// + /// The current border for this edit control. + /// + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + + /// + /// + /// Controls the maximum length of text in the edit control. + /// + private int maxLength = 32767; // Win9X default, used for consistency + + /// + /// + /// Used by the autoSizing code to help figure out the desired height of + /// the edit box. + /// + private int requestedHeight; + bool integralHeightAdjust = false; + + //these indices are used to cache the values of the selection, by doing this + //if the handle isn't created yet, we don't force a creation. + private int selectionStart = 0; + private int selectionLength = 0; + + /// + /// Controls firing of click event (Left click). + /// This is used by TextBox, RichTextBox and MaskedTextBox, code was moved down from TextBox/RichTextBox + /// but cannot make it as default behavior to avoid introducing breaking changes. + /// + private bool doubleClickFired = false; + + private static int[] shortcutsToDisable; + + // We store all boolean properties in here. + // + private BitVector32 textBoxFlags = new BitVector32(); + + /// + /// + /// Creates a new TextBox control. Uses the parent's current font and color + /// set. + /// + internal TextBoxBase() : base() { + + // this class overrides GetPreferredSizeCore, let Control automatically cache the result + SetState2(STATE2_USEPREFERREDSIZECACHE, true); + + textBoxFlags[autoSize | hideSelection | wordWrap | shortcutsEnabled] = true; + SetStyle(ControlStyles.FixedHeight, textBoxFlags[autoSize]); + SetStyle(ControlStyles.StandardClick + | ControlStyles.StandardDoubleClick + | ControlStyles.UseTextForAccessibility + | ControlStyles.UserPaint, false); + + + // cache requestedHeight. Note: Control calls DefaultSize (overridable) in the constructor + // to set the control's cached height that is returned when calling Height, so we just + // need to get the cached height here. + requestedHeight = Height; + } + + /// + /// + /// + /// Gets or sets + /// a value indicating whether pressing the TAB key + /// in a multiline text box control types + /// a TAB character in the control instead of moving the focus to the next control + /// in the tab order. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxAcceptsTabDescr) + ] + public bool AcceptsTab { + get { + return textBoxFlags[acceptsTab]; + } + set { + if (textBoxFlags[acceptsTab] != value) { + textBoxFlags[acceptsTab] = value; + OnAcceptsTabChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnAcceptsTabChangedDescr)] + public event EventHandler AcceptsTabChanged { + add { + Events.AddHandler(EVENT_ACCEPTSTABCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_ACCEPTSTABCHANGED, value); + } + } + + + /// + /// + /// + /// Gets or sets a value indicating whether the following shortcuts should be enabled or not: + /// Ctrl-Z, Ctrl-C, Ctrl-X, Ctrl-V, Ctrl-A, Ctrl-L, Ctrl-R, Ctrl-E, Ctrl-I, Ctrl-Y, + /// Ctrl-BackSpace, Ctrl-Del, Shift-Del, Shift-Ins. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TextBoxShortcutsEnabledDescr) + ] + public virtual bool ShortcutsEnabled { + get { + return textBoxFlags[shortcutsEnabled]; + } + set { + if (shortcutsToDisable == null) { + shortcutsToDisable = new int[] {(int)Shortcut.CtrlZ, (int)Shortcut.CtrlC, (int)Shortcut.CtrlX, + (int)Shortcut.CtrlV, (int)Shortcut.CtrlA, (int)Shortcut.CtrlL, (int)Shortcut.CtrlR, + (int)Shortcut.CtrlE, (int)Shortcut.CtrlY, (int)Keys.Control + (int)Keys.Back, + (int)Shortcut.CtrlDel, (int)Shortcut.ShiftDel, (int)Shortcut.ShiftIns, (int)Shortcut.CtrlJ}; + } + textBoxFlags[shortcutsEnabled] = value; + } + } + + /// + /// + /// Implements the property. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + // First call parent's ProcessCmdKey, since we don't to eat up + // the shortcut key we are not supported in TextBox. + bool returnedValue = base.ProcessCmdKey(ref msg, keyData); + + if (this.ShortcutsEnabled == false) { + foreach (int shortcutValue in shortcutsToDisable) { + if ((int)keyData == shortcutValue || + (int)keyData == (shortcutValue | (int)Keys.Shift)) { + return true; + } + } + } + // + // There are a few keys that change the alignment of the text, but that + // are not ignored by the native control when the ReadOnly property is set. + // We need to workaround that. + if (textBoxFlags[readOnly]) { + int k = (int)keyData; + if (k == (int)Shortcut.CtrlL // align left + || k == (int)Shortcut.CtrlR // align right + || k == (int)Shortcut.CtrlE // align centre + || k == (int)Shortcut.CtrlJ) { // align justified + return true; + } + } + + return returnedValue; + } + + /// + /// + /// + /// Gets or sets a value indicating whether the size + /// of the control automatically adjusts when the font assigned to the control + /// is changed. + /// + /// Note: this works differently than other Controls' AutoSize, so we're hiding + /// it to avoid confusion. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.TextBoxAutoSizeDescr), + RefreshProperties(RefreshProperties.Repaint), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + + public override bool AutoSize { + get { + return textBoxFlags[autoSize]; + } + set { + // Note that we intentionally do not call base. TextBoxes size themselves by + // overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize + // continue to return false to keep our LayoutEngines from messing with TextBoxes. + // This is done for backwards compatibility since the new AutoSize behavior differs. + if (textBoxFlags[autoSize] != value) { + textBoxFlags[autoSize] = value; + + // AutoSize's effects are ignored for a multi-line textbox + // + if (!Multiline) { + SetStyle(ControlStyles.FixedHeight, value); + AdjustHeight(false); + } + + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// + /// Gets or sets + /// the background color of the control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_BACKCOLOR), + SRDescription(SR.ControlBackColorDescr) + ] + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else if (this.ReadOnly) { + return SystemColors.Control; + } else { + return SystemColors.Window; + } + } + set { + base.BackColor = value; + } + } + + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the border type + /// of the text box control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.TextBoxBorderDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + if (borderStyle != value) { + //verify that 'value' is a valid enum type... + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + borderStyle = value; + UpdateStyles(); + RecreateHandle(); + + // PreferredSize depends on BorderStyle : thru CreateParams.ExStyle in User32!AdjustRectEx. + // So when the BorderStyle changes let the parent of this control know about it. + using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.BorderStyle)) { + OnBorderStyleChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnBorderStyleChangedDescr)] + public event EventHandler BorderStyleChanged { + add { + Events.AddHandler(EVENT_BORDERSTYLECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_BORDERSTYLECHANGED, value); + } + } + + internal virtual bool CanRaiseTextChangedEvent { + get { + return true; + } + } + + /// + /// Specifies whether the ImeMode can be enabled - See also ImeModeBase. + /// + protected override bool CanEnableIme { + get { + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CanEnableIme(), this = " + this ); + Debug.Indent(); + + bool canEnable = !( this.ReadOnly || this.PasswordProtect ) && base.CanEnableIme; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + canEnable ); + Debug.Unindent(); + + return canEnable; + } + } + + /// + /// + /// + /// Gets a value + /// indicating whether the user can undo the previous operation in a text box control. + /// + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxCanUndoDescr) + ] + public bool CanUndo { + get { + if (IsHandleCreated) { + bool b; + b = unchecked( (int) (long)SendMessage(NativeMethods.EM_CANUNDO, 0, 0)) != 0; + + return b; + } + return false; + } + } + + /// + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = "EDIT"; + cp.Style |= NativeMethods.ES_AUTOHSCROLL | NativeMethods.ES_AUTOVSCROLL; + if (!textBoxFlags[hideSelection]) cp.Style |= NativeMethods.ES_NOHIDESEL; + if (textBoxFlags[readOnly]) cp.Style |= NativeMethods.ES_READONLY; + cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); + cp.Style &= (~NativeMethods.WS_BORDER); + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + if (textBoxFlags[multiline]) { + cp.Style |= NativeMethods.ES_MULTILINE; + if (textBoxFlags[wordWrap]) cp.Style &= ~NativeMethods.ES_AUTOHSCROLL; + } + + return cp; + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + protected override Cursor DefaultCursor { + get { + return Cursors.IBeam; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, PreferredHeight); + } + } + + /// + /// + /// + /// Gets or sets the foreground color of the control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DispId(NativeMethods.ActiveX.DISPID_FORECOLOR), + SRDescription(SR.ControlForeColorDescr) + ] + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the selected + /// text in the text box control remains highlighted when the control loses focus. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TextBoxHideSelectionDescr) + ] + public bool HideSelection { + get { + return textBoxFlags[hideSelection]; + } + + set { + if (textBoxFlags[hideSelection] != value) { + textBoxFlags[hideSelection] = value; + RecreateHandle(); + OnHideSelectionChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnHideSelectionChangedDescr)] + public event EventHandler HideSelectionChanged { + add { + Events.AddHandler(EVENT_HIDESELECTIONCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_HIDESELECTIONCHANGED, value); + } + } + + /// + /// Internal version of ImeMode property. The ImeMode of TextBoxBase controls depend on its IME restricted + /// mode which is determined by the CanEnableIme property which checks whether the control is in Password or + /// ReadOnly mode. + /// + protected override ImeMode ImeModeBase { + get { + if( DesignMode ) { + return base.ImeModeBase; + } + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_ImeModeInternal(), this = " + this ); + Debug.Indent(); + + ImeMode imeMode = CanEnableIme ? base.ImeModeBase : ImeMode.Disable; + + Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + imeMode ); + Debug.Unindent(); + + return imeMode; + } + set { + base.ImeModeBase = value; + } + } + + /// + /// + /// + /// Gets or + /// sets the lines of text in an text box control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + MergableProperty(false), + Localizable(true), + SRDescription(SR.TextBoxLinesDescr), + Editor("System.Windows.Forms.Design.StringArrayEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public string[] Lines { + get { + string text = Text; + ArrayList list = new ArrayList(); + + int lineStart = 0; + while (lineStart < text.Length) { + int lineEnd = lineStart; + for (; lineEnd < text.Length; lineEnd++) { + char c = text[lineEnd]; + if (c == '\r' || c == '\n') + break; + } + + string line = text.Substring(lineStart, lineEnd - lineStart); + list.Add(line); + + // Treat "\r", "\r\n", and "\n" as new lines + if (lineEnd < text.Length && text[lineEnd] == '\r') + lineEnd++; + if (lineEnd < text.Length && text[lineEnd] == '\n') + lineEnd++; + + lineStart = lineEnd; + } + + // Corner case -- last character in Text is a new line; need to add blank line to list + if (text.Length > 0 && (text[text.Length - 1] == '\r' || text[text.Length - 1] == '\n')) + list.Add(""); + + return(string[]) list.ToArray(typeof(string)); + } + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We append a new line to the Text property. + // Don't localize the new line character. + ] + set { + //unparse this string list... + if (value != null && value.Length > 0) { + + // Work Item #40689: + // Using a StringBuilder instead of a String + // speeds things up approx 150 times + StringBuilder text = new StringBuilder(value[0]); + for (int i=1; i < value.Length; ++i) { + text.Append("\r\n"); + text.Append(value[i]); + } + Text = text.ToString(); + } + else { + Text = ""; + } + } + } + + /// + /// + /// + /// Gets or sets the maximum number of + /// characters the user can type into the text box control. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(32767), + Localizable(true), + SRDescription(SR.TextBoxMaxLengthDescr) + ] + public virtual int MaxLength { + get { + return maxLength; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("MaxLength", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxLength", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (maxLength != value) { + maxLength = value; + UpdateMaxLength(); + } + } + } + + /// + /// + /// + /// Gets or sets a value that indicates that the text box control has been modified by the user since + /// the control was created or its contents were last set. + /// + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxModifiedDescr) + ] + public bool Modified { + get { + if (IsHandleCreated) { + bool curState = (0 != unchecked( (int) (long)SendMessage(NativeMethods.EM_GETMODIFY, 0, 0))); + if (textBoxFlags[modified] != curState) { + // VSWhidbey 94719 - raise ModifiedChanged event. See WmReflectCommand for more info. + textBoxFlags[modified] = curState; + OnModifiedChanged(EventArgs.Empty); + } + return curState; + } + else{ + return textBoxFlags[modified]; + } + + } + + set { + if (Modified != value) { + if (IsHandleCreated) { + SendMessage(NativeMethods.EM_SETMODIFY, value ? 1 : 0, 0); + // VSWhidbey 94719 - must maintain this state always in order for the + // test in the Get method to work properly. + } + + textBoxFlags[modified] = value; + OnModifiedChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnModifiedChangedDescr)] + public event EventHandler ModifiedChanged { + add { + Events.AddHandler(EVENT_MODIFIEDCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_MODIFIEDCHANGED, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether this + /// is a multiline text box control. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.TextBoxMultilineDescr), + RefreshProperties(RefreshProperties.All) + ] + public virtual bool Multiline { + get { + return textBoxFlags[multiline]; + } + set { + if (textBoxFlags[multiline] != value) { + using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Multiline)) { + textBoxFlags[multiline] = value; + + if (value) { + // Multi-line textboxes do not have fixed height + // + SetStyle(ControlStyles.FixedHeight, false); + } + else { + // Single-line textboxes may have fixed height, depending on AutoSize + SetStyle(ControlStyles.FixedHeight, AutoSize); + } + + RecreateHandle(); + AdjustHeight(false); + OnMultilineChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnMultilineChangedDescr)] + public event EventHandler MultilineChanged { + add { + Events.AddHandler(EVENT_MULTILINECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_MULTILINECHANGED, value); + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr) + ] + public new event EventHandler PaddingChanged { + add { + base.PaddingChanged += value; + } + remove { + base.PaddingChanged -= value; + } + } + + /// + /// Determines if the control is in password protect mode. This is overridden in TextBox and + /// MaskedTextBox and is false by default so RichTextBox that doesn't support Password doesn't + /// have to care about this. + /// + virtual internal bool PasswordProtect + { + get { + return false; + } + } + + /// + /// + /// + /// Returns the preferred + /// height for a single-line text box. + /// + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxPreferredHeightDescr) + ] + public int PreferredHeight { + get { + // VSWhidbey 523205: COMPAT we must return the same busted height we did in Everett, even + // if it doesnt take multiline and word wrap into account. For better accuracy and/or wrapping use + // GetPreferredSize instead. + int height = FontHeight; + if (borderStyle != BorderStyle.None) { + height += SystemInformation.GetBorderSizeForDpi(deviceDpi).Height * 4 + 3; + } + return height; + } + } + + // GetPreferredSizeCore + // VSWhidbey 523205: This method can return a different value than PreferredHeight! It properly handles + // border style + multiline and wordwrap. + + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + // 3px vertical space is required between the text and the border to keep the last + // line from being clipped. + // This 3 pixel size was added in everett and we do this to maintain compat. + // old everett behavior was FontHeight + [SystemInformation.BorderSize.Height * 4 + 3] + // however the [ ] was only added if borderstyle was not none. + Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; + + if (BorderStyle != BorderStyle.None) { + bordersAndPadding += new Size(0, 3); + } + + if (BorderStyle == BorderStyle.FixedSingle) { + // VSWhidbey 321520: bump these by 2px to match BorderStyle.Fixed3D - they'll be omitted from the SizeFromClientSize call. + bordersAndPadding.Width +=2; + bordersAndPadding.Height +=2; + } + // Reduce constraints by border/padding size + proposedConstraints -= bordersAndPadding; + + // Fit the text to the remaining space + // Fix for Dev10 bug 590621: + // in text box, we don't interpret ampersand (&) as a directive to underscore the character that follows. + TextFormatFlags format = TextFormatFlags.NoPrefix; + if(!Multiline) { + format |= TextFormatFlags.SingleLine; + } else if(WordWrap) { + format |= TextFormatFlags.WordBreak; + } + Size textSize = TextRenderer.MeasureText(this.Text, this.Font, proposedConstraints, format); + + // We use this old computation as a lower bound to ensure backwards compatibility. + textSize.Height = Math.Max(textSize.Height, FontHeight); + Size preferredSize = textSize + bordersAndPadding; + return preferredSize; + } + + /// + /// Get the currently selected text start position and length. Use this method internally + /// to avoid calling SelectionStart + SelectionLength each of which does essentially the + /// same (save one message round trip). + /// + internal void GetSelectionStartAndLength( out int start, out int length ){ + int end = 0; + + if( !IsHandleCreated ) { + // It is possible that the cached values are no longer valid if the Text has been changed + // while the control does not have a handle. We need to return valid values. We also need + // to keep the old cached values in case the Text is changed again making the cached values + // valid again. See VSW#495181. + AdjustSelectionStartAndEnd( this.selectionStart, this.selectionLength, out start, out end, -1 ); + length = end - start; + } + else { + start = 0; + UnsafeNativeMethods.SendMessage( new HandleRef( this, Handle ), NativeMethods.EM_GETSEL, ref start, ref end ); + + //Here, we return the max of either 0 or the # returned by + //the windows call. This eliminates a problem on nt4 where + // a huge negative # is being returned. + // + start = Math.Max( 0, start ); + // ditto for end + end = Math.Max( 0, end ); + + if( this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1 ) { + // When processing EM_GETSEL, EDIT control returns byte offsets instead of character offsets, this + // makes a difference in unicode code pages like Japanese. We need to adjust the offsets. + ToUnicodeOffsets( WindowText, ref start, ref end ); + } + + length = end - start; + } + +#if DEBUG + { + string t = WindowText; + int len; + + end = start + length - 1; + + if (t == null) { + len = 0; + } + else { + len = t.Length; + } + + Debug.Assert(end <= len, "SelectionEnd is outside the set of valid caret positions for the current WindowText (end =" + + end + ", WindowText.Length =" + len +")"); + } +#endif + } + + /// + /// + /// + /// Gets or sets a value indicating whether text in the text box is read-only. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.TextBoxReadOnlyDescr) + ] + public bool ReadOnly { + get { + return textBoxFlags[readOnly]; + } + set { + if (textBoxFlags[readOnly] != value) { + textBoxFlags[readOnly] = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.EM_SETREADONLY, value? -1: 0, 0); + } + + OnReadOnlyChanged(EventArgs.Empty); + + VerifyImeRestrictedModeChanged(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnReadOnlyChangedDescr)] + public event EventHandler ReadOnlyChanged { + add { + Events.AddHandler(EVENT_READONLYCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_READONLYCHANGED, value); + } + } + + /// + /// + /// + /// The currently selected text in the control. + /// + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectedTextDescr) + ] + public virtual string SelectedText { + get { + int selStart, selLength; + GetSelectionStartAndLength( out selStart, out selLength ); + return Text.Substring(selStart, selLength); + } + set { + SetSelectedTextInternal(value, true); + } + } + + /// + /// Replaces the selected text with the one passed in. + /// + internal virtual void SetSelectedTextInternal(string text, bool clearUndo){ + if (!IsHandleCreated) { + CreateHandle(); + } + + if( text == null ){ + text = ""; + } + + // The EM_LIMITTEXT message limits only the text the user can enter. It does not affect any text + // already in the edit control when the message is sent, nor does it affect the length of the text + // copied to the edit control by the WM_SETTEXT message. + SendMessage(NativeMethods.EM_LIMITTEXT, 0, 0); + + if( clearUndo ){ + SendMessage(NativeMethods.EM_REPLACESEL, 0, text); + // For consistency with Text, we clear the modified flag + SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); + ClearUndo(); + } + else{ + SendMessage(NativeMethods.EM_REPLACESEL, /*undoable*/ -1, text); + } + + // Re-enable user input. + SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); + } + + + /// + /// + /// + /// Gets or sets the number of characters selected in the text + /// box. + /// + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectionLengthDescr) + ] + public virtual int SelectionLength { + get { + int start, length; + + GetSelectionStartAndLength( out start, out length ); + + return length; + } + + set { + if (value < 0){ + throw new ArgumentOutOfRangeException("SelectionLength", SR.GetString(SR.InvalidArgument, "SelectionLength", value.ToString(CultureInfo.CurrentCulture))); + } + + int selStart, selLength; + GetSelectionStartAndLength( out selStart, out selLength ); + + if (value != selLength) { + Select(selStart, value); + } + } + } + + /// + /// + /// + /// Gets or sets the starting + /// point of text selected in the text + /// box. + /// + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectionStartDescr) + ] + public int SelectionStart { + get { + int selStart, selLength; + + GetSelectionStartAndLength( out selStart, out selLength ); + + return selStart; + } + set { + if (value < 0){ + throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidArgument, "SelectionStart", value.ToString(CultureInfo.CurrentCulture))); + } + Select(value, SelectionLength); + } + } + + // Call SetSelectionOnHandle inside CreateHandle() + internal virtual bool SetSelectionInCreateHandle { + get { + return true; + } + } + + /// + /// + /// + /// Gets or sets + /// the current text in the text box. + /// + /// + [ + Localizable(true), + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public override string Text { + get { + return base.Text; + } + set { + if (value != base.Text) { + base.Text = value; + if (IsHandleCreated) { + // clear the modified flag + SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); + } + } + } + } + + /// + [Browsable(false)] + public virtual int TextLength { + get { + // Note: Currently Microsoft does not fully support surrogates - VSW#327396. If + // the text contains surrogate characters this property may return incorrect values. + + if (IsHandleCreated && Marshal.SystemDefaultCharSize == 2) { + return SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); + } + else { + return Text.Length; + } + } + } + + /// + /// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) + /// in Win9x. + /// + internal virtual bool SelectionUsesDbcsOffsetsInWin9x { + get { + return true; + } + } + + // Since setting the WindowText while the handle is created + // generates a WM_COMMAND message, we must trap that case + // and prevent the event from getting fired, or we get + // double "TextChanged" events. + // + /// + /// + /// + /// + internal override string WindowText { + get { + return base.WindowText; + } + + set { + if (value == null) value = ""; + if (!WindowText.Equals(value)) { + textBoxFlags[codeUpdateText] = true; + try { + base.WindowText = value; + } + finally { + textBoxFlags[codeUpdateText] = false; + } + } + } + } + + /// + /// For VSWhidbey 325345. In certain circumstances we might have to force + /// text into the window whether or not the text is the same. + /// Make this a method on TextBoxBase rather than RichTextBox (which is the only + /// control that needs this at this point), since we need to set codeUpdateText. + /// + internal void ForceWindowText(string value) { + if (value == null) { + value = ""; + } + + textBoxFlags[codeUpdateText] = true; + try { + if (IsHandleCreated) { + UnsafeNativeMethods.SetWindowText(new HandleRef(this, Handle), value); + } + else { + if (value.Length == 0) { + Text = null; + } + else { + Text = value; + } + } + } + finally { + textBoxFlags[codeUpdateText] = false; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether a + /// multiline text box control automatically wraps words to the beginning of the next + /// line when necessary. + /// + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(true), + SRDescription(SR.TextBoxWordWrapDescr) + ] + public bool WordWrap { + get { + return textBoxFlags[wordWrap]; + } + set { + using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.WordWrap)) { + if (textBoxFlags[wordWrap] != value) { + textBoxFlags[wordWrap] = value; + RecreateHandle(); + } + } + } + } + + /// + /// + /// Adjusts the height of a single-line edit control to match the height of + /// the control's font. + /// + /// + private void AdjustHeight(bool returnIfAnchored) { + + // If we're anchored to two opposite sides of the form, don't adjust the size because + // we'll lose our anchored size by resetting to the requested width. + // + if (returnIfAnchored && (this.Anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) { + return; + } + + int saveHeight = requestedHeight; + try { + if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) { + Height = PreferredHeight; + } + else { + + int curHeight = Height; + + // Changing the font of a multi-line textbox can sometimes cause a painting problem + // The only workaround I can find is to size the textbox big enough for the font, and + // then restore its correct size. + // + if (textBoxFlags[multiline]) { + Height = Math.Max(saveHeight, PreferredHeight + 2); // 2 = fudge factor + } + + integralHeightAdjust = true; + try { + Height = saveHeight; + } + finally { + integralHeightAdjust = false; + } + } + } + finally { + requestedHeight = saveHeight; + } + } + + /// + /// + /// + /// Append text to the current text of text box. + /// + /// + public void AppendText( string text ) { + if (text.Length > 0) { + int selStart, selLength; + + GetSelectionStartAndLength( out selStart, out selLength ); + + try { + // This enables you to use SelectionColor to AppendText in color. + int endOfText = GetEndPosition(); + + SelectInternal(endOfText, endOfText, endOfText); + SelectedText = text; + } + finally { + // If AppendText is called when the control is docked and the form is minimized, + // all the text will scroll to the top and the control will look empty when the + // form is restored. We work around this by selecting back whatever was originally + // selected when AppendText was called. + if (this.Width == 0 || this.Height == 0) { + this.Select(selStart, selLength); + } + } + } + } + + /// + /// + /// + /// Clears all text from the text box control. + /// + /// + public void Clear() { + Text = null; + } + + /// + /// + /// + /// Clears information about the most recent operation + /// from the undo buffer of the text box. + /// + /// + public void ClearUndo() { + if (IsHandleCreated) { + SendMessage(NativeMethods.EM_EMPTYUNDOBUFFER, 0, 0); + } + } + + /// + /// + /// + /// Copies the current selection in the text box to the Clipboard. + /// + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + public void Copy() { + SendMessage(NativeMethods.WM_COPY, 0, 0); + } + + /// + /// + /// + /// + protected override void CreateHandle() { + // This "creatingHandle" stuff is to avoid property change events + // when we set the Text property. + textBoxFlags[creatingHandle] = true; + try { + base.CreateHandle(); + + if (SetSelectionInCreateHandle) { + // send EM_SETSEL message + SetSelectionOnHandle(); + } + + } + finally { + textBoxFlags[creatingHandle] = false; + } + } + + /// + /// + /// + /// Moves the current selection in the text box to the Clipboard. + /// + /// + public void Cut() { + SendMessage(NativeMethods.WM_CUT, 0, 0); + } + + /// + /// Returns the text end position (one past the last input character). This property is virtual to allow MaskedTextBox + /// to set the last input char position as opposed to the last char position which may be a mask character. + /// + internal virtual int GetEndPosition(){ + // +1 because RichTextBox has this funny EOF pseudo-character after all the text. + return IsHandleCreated ? TextLength + 1 : TextLength; + } + + /// + /// + /// + /// Overridden to handle TAB key. + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) != Keys.Alt) { + switch (keyData & Keys.KeyCode) { + case Keys.Tab: + // Single-line RichEd's want tab characters (see WM_GETDLGCODE), + // so we don't ask it + return Multiline && textBoxFlags[acceptsTab] && ((keyData & Keys.Control) == 0); + case Keys.Escape: + if (Multiline) + return false; + break; + case Keys.Back: + if (!this.ReadOnly) + return true; + break; + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + // else fall through to base + } + } + return base.IsInputKey(keyData); + } + + /// + /// + /// + /// Overridden to update the newly created handle with the settings of the + /// MaxLength and PasswordChar properties. + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + // it's likely here that the create params could have changed + // the border size/etc. + CommonProperties.xClearPreferredSizeCache(this); + AdjustHeight(true); + + UpdateMaxLength(); + if (textBoxFlags[modified]){ + SendMessage(NativeMethods.EM_SETMODIFY, 1, 0); + } + if (textBoxFlags[scrollToCaretOnHandleCreated]) { + ScrollToCaret(); + textBoxFlags[scrollToCaretOnHandleCreated] = false; + } + } + + /// + /// + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + textBoxFlags[modified] = Modified; + textBoxFlags[setSelectionOnHandleCreated] = true; + // Update text selection cached values to be restored when recreating the handle. + GetSelectionStartAndLength( out this.selectionStart, out this.selectionLength ); + base.OnHandleDestroyed(e); + } + + /// + /// + /// + /// Replaces the current selection in the text box with the contents of the Clipboard. + /// + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + public void Paste() { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); + IntSecurity.ClipboardRead.Demand(); + + SendMessage(NativeMethods.WM_PASTE, 0, 0); + } + + /// + /// + /// [To be supplied.] + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "TextBoxBase.ProcessDialogKey [" + keyData.ToString() + "]"); + Keys keyCode = (Keys)keyData & Keys.KeyCode; + + if (keyCode == Keys.Tab && this.AcceptsTab && (keyData & Keys.Control) != 0) { + // When this control accepts Tabs, Ctrl-Tab is treated exactly like Tab. + keyData &= ~Keys.Control; + } + + return base.ProcessDialogKey(keyData); + } + + /// + /// + /// TextBox / RichTextBox Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnAcceptsTabChanged(EventArgs e) { + EventHandler eh = Events[EVENT_ACCEPTSTABCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnBorderStyleChanged(EventArgs e) { + EventHandler eh = Events[EVENT_BORDERSTYLECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + AdjustHeight(false); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnHideSelectionChanged(EventArgs e) { + EventHandler eh = Events[EVENT_HIDESELECTIONCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnModifiedChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MODIFIEDCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// Raises the MouseUp event. + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + Point pt = PointToScreen(mevent.Location); + + if (mevent.Button == MouseButtons.Left) { + if (!ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + if (!this.doubleClickFired) { + OnClick(mevent); + OnMouseClick(mevent); + } + else { + this.doubleClickFired = false; + OnDoubleClick(mevent); + OnMouseDoubleClick(mevent); + } + } + this.doubleClickFired = false; + } + base.OnMouseUp(mevent); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnMultilineChanged(EventArgs e) { + EventHandler eh = Events[EVENT_MULTILINECHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + + protected override void OnPaddingChanged(EventArgs e) { + base.OnPaddingChanged(e); + AdjustHeight(false); + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void OnReadOnlyChanged(EventArgs e) { + EventHandler eh = Events[EVENT_READONLYCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + protected override void OnTextChanged(EventArgs e) { + // since AutoSize existed in Everett, (and is the default) we can't + // relayout the parent when the "preferredsize" of the control changes. + // this means a multiline = true textbox wont natrually grow in height when + // the text changes. + CommonProperties.xClearPreferredSizeCache(this); + base.OnTextChanged(e); + } + + /// + /// + /// Returns the character nearest to the given point. + /// + public virtual char GetCharFromPosition(Point pt) { + string t = this.Text; + int index = GetCharIndexFromPosition(pt); + return (index < 0 || index >= t.Length) ? (char)0 : t[index]; + } + + /// + /// + /// Returns the index of the character nearest to the given point. + /// + public virtual int GetCharIndexFromPosition(Point pt) { + int longPoint = NativeMethods.Util.MAKELONG(pt.X, pt.Y); + int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, longPoint); + index = NativeMethods.Util.LOWORD(index); + + if (index < 0) { + index = 0; + } + else { + string t = this.Text; + // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit + // is a newline. + // + if (index >= t.Length) { + index = Math.Max(t.Length - 1, 0); + } + } + return index; + } + + /// + /// + /// Returns the number of the line containing a specified character position + /// in a textbox. Note that this returns the physical line number + /// and not the conceptual line number. For example, if the first conceptual + /// line (line number 0) word-wraps and extends to the second line, and if + /// you pass the index of a overflowed character, GetLineFromCharIndex would + /// return 1 and not 0. + /// + public virtual int GetLineFromCharIndex(int index) { + return unchecked( (int) (long)SendMessage(NativeMethods.EM_LINEFROMCHAR, index, 0)); + } + + /// + /// + /// Returns the location of the character at the given index. + /// + public virtual Point GetPositionFromCharIndex(int index) { + if (index < 0 || index >= Text.Length) + return Point.Empty; + + int i = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, index, 0); + return new Point(NativeMethods.Util.SignedLOWORD(i), NativeMethods.Util.SignedHIWORD(i)); + } + + /// + /// + /// Returns the index of the first character of a given line. Returns -1 of lineNumber is invalid. + /// + public int GetFirstCharIndexFromLine(int lineNumber) { + if (lineNumber < 0) { + throw new ArgumentOutOfRangeException("lineNumber", SR.GetString(SR.InvalidArgument, "lineNumber", lineNumber.ToString(CultureInfo.CurrentCulture))); + } + return unchecked( (int) (long)SendMessage(NativeMethods.EM_LINEINDEX, lineNumber, 0)); + } + + /// + /// + /// Returns the index of the first character of the line where the caret is. + /// + public int GetFirstCharIndexOfCurrentLine() { + return unchecked( (int) (long)SendMessage(NativeMethods.EM_LINEINDEX, -1, 0)); + } + + + + /// + /// + /// Ensures that the caret is visible in the TextBox window, by scrolling the + /// TextBox control surface if necessary. + /// + public void ScrollToCaret() { + if (IsHandleCreated) { + if (String.IsNullOrEmpty(this.WindowText)) { + // If there is no text, then there is no place to go. + return; + } + + bool scrolled = false; + object editOle = null; + IntPtr editOlePtr = IntPtr.Zero; + try { + if (UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETOLEINTERFACE, 0, out editOle) != 0) { + + editOlePtr = Marshal.GetIUnknownForObject(editOle); + + if (editOlePtr != IntPtr.Zero) { + IntPtr iTextDocument = IntPtr.Zero; + Guid iiTextDocumentGuid = typeof(UnsafeNativeMethods.ITextDocument).GUID; + + try { + Marshal.QueryInterface(editOlePtr, ref iiTextDocumentGuid, out iTextDocument); + + UnsafeNativeMethods.ITextDocument textDocument = Marshal.GetObjectForIUnknown(iTextDocument) as UnsafeNativeMethods.ITextDocument; + + if (textDocument != null) { + int selStart, selLength; + + // vsWhidbey 372764: when the user calls RichTextBox::ScrollToCaret we want the RichTextBox to show as + // much text as possible. + // Here is how we do that: + // 1. We scroll the RichTextBox all the way to the bottom so the last line of text is the last visible line. + // 2. We get the first visible line. + // 3. If the first visible line is smaller than the start of the selection, then we are done: + // The selection fits inside the RichTextBox display rectangle. + // 4. Otherwise, scroll the selection to the top of the RichTextBox. + GetSelectionStartAndLength( out selStart, out selLength ); + int selStartLine = GetLineFromCharIndex(selStart); + + // 1. Scroll the RichTextBox all the way to the bottom + UnsafeNativeMethods.ITextRange textRange = textDocument.Range(this.WindowText.Length - 1, this.WindowText.Length - 1); + textRange.ScrollIntoView(0); // 0 ==> tomEnd + + // 2. Get the first visible line. + int firstVisibleLine = unchecked( (int) (long)SendMessage(NativeMethods.EM_GETFIRSTVISIBLELINE, 0, 0)); + + // 3. If the first visible line is smaller than the start of the selection, we are done; + if (firstVisibleLine <= selStartLine) { + // we are done + } else { + // 4. Scroll the selection to the top of the RichTextBox + textRange = textDocument.Range(selStart, selStart + selLength); + textRange.ScrollIntoView(32); // 32 ==> tomStart + } + + scrolled = true; + } + } finally { + if (iTextDocument != IntPtr.Zero) { + Marshal.Release(iTextDocument); + } + } + } + } + } finally { + if (editOlePtr != IntPtr.Zero) { + Marshal.Release(editOlePtr); + } + } + + if (!scrolled) { + SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); + } + } + else { + textBoxFlags[scrollToCaretOnHandleCreated] = true; + } + } + + /// + /// + /// + /// Sets the SelectionLength to 0. + /// + /// + public void DeselectAll() { + this.SelectionLength = 0; + } + + /// + /// + /// + /// Selects a range of text in the text box. + /// + /// + public void Select(int start, int length) { + if (start < 0){ + throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidArgument, "start", start.ToString(CultureInfo.CurrentCulture))); + } + + int textLen = TextLength; + + if (start > textLen) { + //We shouldn't allow positive length if you're starting at the end, but + //should allow negative length. + long longLength = Math.Min(0, (long)length + start - textLen); + if (longLength < int.MinValue) { + length = int.MinValue; + } + else { + length = (int)longLength; + } + start = textLen; + } + + SelectInternal(start, length, textLen); + } + + /// + /// Performs the actual select without doing arg checking. + /// + /// Send in -1 for the textLen parameter if you don't have the text + /// length cached when calling this method. It will be computed. + /// But if you do have it cached, please pass it in. This will avoid + /// the expensive call to the TextLength property. + /// + internal virtual void SelectInternal(int start, int length, int textLen) { + //if our handle is created - send message... + if (IsHandleCreated) { + int s, e; + AdjustSelectionStartAndEnd(start, length, out s, out e, textLen); + + SendMessage(NativeMethods.EM_SETSEL, s, e); + // + + } + else { + //otherwise, wait until handle is created to send this message. + //Store the indices until then... + this.selectionStart = start; + this.selectionLength = length; + textBoxFlags[setSelectionOnHandleCreated] = true; + } + } + + /// + /// + /// + /// Selects all text in the text box. + /// + /// + public void SelectAll() { + int textLen = TextLength; + SelectInternal(0, textLen, textLen); + } + + /// + /// + /// Overrides Control.setBoundsCore to enforce autoSize. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + if (!integralHeightAdjust && height != Height) + requestedHeight = height; + + if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) + height = PreferredHeight; + + base.SetBoundsCore(x, y, width, height, specified); + } + + private static void Swap(ref int n1, ref int n2) { + int temp = n2; + n2 = n1; + n1 = temp; + } + + // + // Send in -1 if you don't have the text length cached + // when calling this method. It will be computed. If not, + // please pass in the text length as the last parameter. + // This will avoid the expensive call to the TextLength + // property. + internal void AdjustSelectionStartAndEnd(int selStart, int selLength, out int start, out int end, int textLen) { + start = selStart; + end = 0; + + if (start <= -1) { + start = -1; + } + else { + int textLength; + + if (textLen >= 0) { + textLength = textLen; + } + else { + textLength = this.TextLength; + } + + if (start > textLength) { + start = textLength; + } + + checked { + try { + end = start + selLength; + } + catch (OverflowException) { + //Since we overflowed, cap at the max/min value: we'll correct the value below + end = start > 0 ? int.MaxValue : int.MinValue; + } + } + + // Make sure end is in range + if (end < 0) { + end = 0; + } + else if (end > textLength) { + end = textLength; + } + + if (this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1) { + // EDIT control expects selection values to be byte offsets instead of character offsets, + // this makes a difference in unicode code pages like Japanese. + // We need to adjust the offsets. + ToDbcsOffsets(WindowText, ref start, ref end); + } + } + } + + // Called by CreateHandle or OnHandleCreated + internal void SetSelectionOnHandle() { + Debug.Assert(IsHandleCreated, "Don't call this method until the handle is created."); + if (textBoxFlags[setSelectionOnHandleCreated]) { + textBoxFlags[setSelectionOnHandleCreated] = false; + int start, end; + AdjustSelectionStartAndEnd(this.selectionStart, this.selectionLength, out start, out end, -1); + SendMessage(NativeMethods.EM_SETSEL, start, end); + } + } + + /// + /// Converts byte offsset to unicode offsets. + /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions + /// as opposed to RICHEDIT which does it always as character positions. + /// This method is used when handling the WM_GETSEL message. + /// + static void ToUnicodeOffsets(string str, ref int start, ref int end) { + Encoding e = Encoding.Default; + + // Acutally, we may get away with this call if we can get the bytes from Win9x. Dont know if it is possible. + // This can be expensive since start/end could be small, but str.Length can be quite big. + byte[] bytes = e.GetBytes(str); + + bool swap = start > end; + if (swap) { + Swap(ref start, ref end); + } + + // Make sure start and end are within the string + // + if (start < 0){ + start = 0; + } + if (start > bytes.Length){ + start = bytes.Length; + } + if (end > bytes.Length){ + end = bytes.Length; + } + + // IMPORTANT: Avoid off-by-1 errors! + // The end value passed in is the character immediately after the last character selected. + + int newStart = start == 0 ? 0 : e.GetCharCount(bytes, 0, start); + end = newStart + e.GetCharCount(bytes, start, end - start); + start = newStart; + + if (swap){ + Swap(ref start, ref end); + } + } + + /// + /// Converts unicode offsset to byte offsets. + /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions + /// as opposed to RICHEDIT which does it always as character positions. + /// This method is used when handling the WM_SETSEL message. + /// + static internal void ToDbcsOffsets(string str, ref int start, ref int end) { + Encoding e = Encoding.Default; + + bool swap = start > end; + if (swap) { + Swap(ref start, ref end); + } + + // Make sure start and end are within the string + // + if (start < 0) { + start = 0; + } + if (start > str.Length) { + start = str.Length; + } + if (end < start) { + end = start; + } + if (end > str.Length) { + end = str.Length; + } + + // IMPORTANT: Avoid off-by-1 errors! + // The end value passed in is the character immediately after the last character selected. + + int newStart = start == 0 ? 0 : e.GetByteCount(str.Substring(0, start)); + end = newStart + e.GetByteCount(str.Substring(start, end - start)); + start = newStart; + + if (swap) { + Swap(ref start, ref end); + } + } + + + + /// + /// + /// Provides some interesting information for the TextBox control in + /// String form. + /// + /// + public override string ToString() { + + string s = base.ToString(); + + string txt = Text; + if (txt.Length > 40) txt = txt.Substring(0, 40) + "..."; + return s + ", Text: " + txt.ToString(); + } + + /// + /// + /// + /// Undoes the last edit operation in the text box. + /// + /// + public void Undo() { + SendMessage(NativeMethods.EM_UNDO, 0, 0); + } + + internal virtual void UpdateMaxLength() { + if (IsHandleCreated) { + SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); + } + } + + + internal override IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) + { + if ((msg == NativeMethods.WM_CTLCOLORSTATIC) && !ShouldSerializeBackColor()) { + // Let the Win32 Edit control handle background colors itself. + // This is necessary because a disabled edit control will display a different + // BackColor than when enabled. + return IntPtr.Zero; + } + else { + return base.InitializeDCForWmCtlColor(dc, msg); + } + } + + /// + /// + /// + /// + private void WmReflectCommand(ref Message m) { + if (!textBoxFlags[codeUpdateText] && !textBoxFlags[creatingHandle]) { + if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_CHANGE && CanRaiseTextChangedEvent) { + OnTextChanged(EventArgs.Empty); + } + else if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_UPDATE) { + // VSWhidbey 94719: force update to the Modified property, which will trigger + // ModifiedChanged event handlers + bool force = this.Modified; + } + } + } + + /// + /// + /// + /// + void WmSetFont(ref Message m) { + base.WndProc(ref m); + if (!textBoxFlags[multiline]) { + SendMessage(NativeMethods.EM_SETMARGINS, NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); + } + } + + void WmGetDlgCode(ref Message m) { + base.WndProc(ref m); + if (AcceptsTab) { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox wants tabs"); + m.Result = (IntPtr)(unchecked( (int) (long)m.Result) | NativeMethods.DLGC_WANTTAB); + } + else { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox doesn't want tabs"); + m.Result = (IntPtr)(unchecked( (int) (long)m.Result) & ~(NativeMethods.DLGC_WANTTAB | NativeMethods.DLGC_WANTALLKEYS)); + } + } + + /// + /// Handles the WM_CONTEXTMENU message based on this + /// table: + /// ShortcutsEnabled #1 #2 #3 + /// Yes strip context system + /// No strip context N/A + /// + /// + private void WmTextBoxContextMenu(ref Message m) { + + if (ContextMenu != null || ContextMenuStrip != null) { + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + Point client; + bool keyboardActivated = false; + // lparam will be exactly -1 when the user invokes the context menu + // with the keyboard. + // + if (unchecked((int)(long)m.LParam) == -1) { + keyboardActivated = true; + client = new Point(Width/2, Height/2); + } + else { + client = PointToClientInternal(new Point(x, y)); + } + + // + + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (ClientRectangle.Contains( client )) { + if (ContextMenu != null) { + ContextMenu.Show(this, client); + } + else if (ContextMenuStrip != null) { + ContextMenuStrip.ShowInternal(this, client, keyboardActivated); + } + else { + Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?"); + DefWndProc( ref m ); + } + } + } + } + + /// + /// + /// + /// The control's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the control continues to function properly. + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_LBUTTONDBLCLK: + this.doubleClickFired = true; + base.WndProc(ref m); + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: + WmReflectCommand(ref m); + break; + case NativeMethods.WM_GETDLGCODE: + WmGetDlgCode(ref m); + break; + case NativeMethods.WM_SETFONT: + WmSetFont(ref m); + break; + case NativeMethods.WM_CONTEXTMENU: + if (ShortcutsEnabled) { + //calling base will find ContextMenus in this order: + // 1) ContextMenu 2) ContextMenuStrip 3) SystemMenu + base.WndProc(ref m); + } + else { + // we'll handle this message so we can hide the + // SystemMenu if Context and Strip menus are null + WmTextBoxContextMenu(ref m); + } + break; + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TextBoxRenderer.cs b/WindowsForms/Managed/System/WinForms/TextBoxRenderer.cs new file mode 100644 index 000000000..8df0ac2a2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TextBoxRenderer.cs @@ -0,0 +1,134 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + + using System; + using System.Drawing; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Internal; + using System.Windows.Forms.VisualStyles; + using Microsoft.Win32; + + + /// + /// + /// + /// This is a rendering class for the TextBox control. + /// + /// + public sealed class TextBoxRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + private static readonly VisualStyleElement TextBoxElement = VisualStyleElement.TextBox.TextEdit.Normal; + + //cannot instantiate + private TextBoxRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + private static void DrawBackground(Graphics g, Rectangle bounds, TextBoxState state) { + visualStyleRenderer.DrawBackground(g, bounds); + if (state != TextBoxState.Disabled) { + Color windowColor = visualStyleRenderer.GetColor(ColorProperty.FillColor); + if (windowColor != SystemColors.Window) { + Rectangle fillRect = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + //then we need to re-fill the background. + using(SolidBrush brush = new SolidBrush(SystemColors.Window)) { + g.FillRectangle(brush, fillRect); + } + } + } + } + + /// + /// + /// + /// Renders a TextBox control. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTextBox(Graphics g, Rectangle bounds, TextBoxState state) { + InitializeRenderer((int)state); + DrawBackground(g, bounds, state); + } + + /// + /// + /// + /// Renders a TextBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string textBoxText, Font font, TextBoxState state) { + DrawTextBox(g, bounds, textBoxText, font, TextFormatFlags.TextBoxControl, state); + } + + /// + /// + /// + /// Renders a TextBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string textBoxText, Font font, Rectangle textBounds, TextBoxState state) { + DrawTextBox(g, bounds, textBoxText, font, textBounds, TextFormatFlags.TextBoxControl, state); + } + + /// + /// + /// + /// Renders a TextBox control. + /// + /// + public static void DrawTextBox(Graphics g, Rectangle bounds, string textBoxText, Font font, TextFormatFlags flags, TextBoxState state) { + InitializeRenderer((int)state); + Rectangle textBounds = visualStyleRenderer.GetBackgroundContentRectangle(g, bounds); + textBounds.Inflate(-2, -2); + DrawTextBox(g, bounds, textBoxText, font, textBounds, flags, state); + } + + /// + /// + /// + /// Renders a TextBox control. + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using Graphics instead of IDeviceContext intentionally + ] + public static void DrawTextBox(Graphics g, Rectangle bounds, string textBoxText, Font font, Rectangle textBounds, TextFormatFlags flags, TextBoxState state) { + InitializeRenderer((int)state); + + DrawBackground(g, bounds, state); + Color textColor = visualStyleRenderer.GetColor(ColorProperty.TextColor); + TextRenderer.DrawText(g, textBoxText, font, textBounds, textColor, flags); + } + + + private static void InitializeRenderer(int state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(TextBoxElement.ClassName, TextBoxElement.Part, state); + } + else { + visualStyleRenderer.SetParameters(TextBoxElement.ClassName, TextBoxElement.Part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TextDataFormat.cs b/WindowsForms/Managed/System/WinForms/TextDataFormat.cs new file mode 100644 index 000000000..42c43c771 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TextDataFormat.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// Specifies the formats that can be used with Clipboard.GetText and Clipboard.SetText methods + /// + public enum TextDataFormat { + /// + /// + /// [To be supplied.] + /// + Text, + /// + /// + /// [To be supplied.] + /// + UnicodeText, + /// + /// + /// [To be supplied.] + /// + Rtf, + /// + /// + /// [To be supplied.] + /// + Html, + /// + /// + /// [To be supplied.] + /// + CommaSeparatedValue + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TextImageRelation.cs b/WindowsForms/Managed/System/WinForms/TextImageRelation.cs new file mode 100644 index 000000000..cbccca12f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TextImageRelation.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + // Defined in such a way that you can cast the relation to an AnchorStyle and the direction of + // the AnchorStyle points to where the image goes. (e.g., (AnchorStyle)ImageBeforeText -> Left)) + /// + public enum TextImageRelation { + /// + Overlay = AnchorStyles.None, + /// + ImageBeforeText = AnchorStyles.Left, + /// + TextBeforeImage = AnchorStyles.Right, + /// + ImageAboveText = AnchorStyles.Top, + /// + TextAboveImage = AnchorStyles.Bottom + }; +} diff --git a/WindowsForms/Managed/System/WinForms/ThreadExceptionDialog.cs b/WindowsForms/Managed/System/WinForms/ThreadExceptionDialog.cs new file mode 100644 index 000000000..cb7f97afd --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ThreadExceptionDialog.cs @@ -0,0 +1,435 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Text; + using System.ComponentModel; + using System.Drawing; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices; + using System.Globalization; + + /// + /// + /// + /// + /// Implements a dialog box that is displayed when an unhandled exception occurs in + /// a thread. + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + UIPermission(SecurityAction.Assert, Window=UIPermissionWindow.AllWindows)] + public class ThreadExceptionDialog : Form { + + private const string DownBitmapName = "down.bmp"; + private const string UpBitmapName = "up.bmp"; + + private const int MAXWIDTH = 440; + private const int MAXHEIGHT = 325; + private const int PADDINGWIDTH = 84; + private const int PADDINGHEIGHT = 26; + private const int MAXTEXTWIDTH = 180; + private const int MAXTEXTHEIGHT = 40; + private const int BUTTONTOPPADDING = 31; + private const int BUTTONDETAILS_LEFTPADDING = 8; + private const int MESSAGE_TOPPADDING = 8; + private const int HEIGHTPADDING = 8; + private const int BUTTONWIDTH = 100; + private const int BUTTONHEIGHT = 23; + private const int BUTTONALIGNMENTWIDTH = 105; + private const int BUTTONALIGNMENTPADDING = 5; + private const int DETAILSWIDTHPADDING = 16; + private const int DETAILSHEIGHT = 154; + private const int PICTUREWIDTH = 64; + private const int PICTUREHEIGHT = 64; + private const int EXCEPTIONMESSAGEVERTICALPADDING = 4; + + private int scaledMaxWidth = MAXWIDTH; + private int scaledMaxHeight = MAXHEIGHT; + private int scaledPaddingWidth = PADDINGWIDTH; + private int scaledPaddingHeight = PADDINGHEIGHT; + private int scaledMaxTextWidth = MAXTEXTWIDTH; + private int scaledMaxTextHeight = MAXTEXTHEIGHT; + private int scaledButtonTopPadding = BUTTONTOPPADDING; + private int scaledButtonDetailsLeftPadding = BUTTONDETAILS_LEFTPADDING; + private int scaledMessageTopPadding = MESSAGE_TOPPADDING; + private int scaledHeightPadding = HEIGHTPADDING; + private int scaledButtonWidth = BUTTONWIDTH; + private int scaledButtonHeight = BUTTONHEIGHT; + private int scaledButtonAlignmentWidth = BUTTONALIGNMENTWIDTH; + private int scaledButtonAlignmentPadding = BUTTONALIGNMENTPADDING; + private int scaledDetailsWidthPadding = DETAILSWIDTHPADDING; + private int scaledDetailsHeight = DETAILSHEIGHT; + private int scaledPictureWidth = PICTUREWIDTH; + private int scaledPictureHeight = PICTUREHEIGHT; + private int scaledExceptionMessageVerticalPadding = EXCEPTIONMESSAGEVERTICALPADDING; + + private PictureBox pictureBox = new PictureBox(); + private Label message = new Label(); + private Button continueButton = new Button(); + private Button quitButton = new Button(); + private Button detailsButton = new Button(); + private Button helpButton = new Button(); + private TextBox details = new TextBox(); + private Bitmap expandImage = null; + private Bitmap collapseImage = null; + private bool detailsVisible = false; + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ThreadExceptionDialog(Exception t) { + + if (DpiHelper.EnableThreadExceptionDialogHighDpiImprovements) { + scaledMaxWidth = LogicalToDeviceUnits(MAXWIDTH); + scaledMaxHeight = LogicalToDeviceUnits(MAXHEIGHT); + scaledPaddingWidth = LogicalToDeviceUnits(PADDINGWIDTH); + scaledPaddingHeight = LogicalToDeviceUnits(PADDINGHEIGHT); + scaledMaxTextWidth = LogicalToDeviceUnits(MAXTEXTWIDTH); + scaledMaxTextHeight = LogicalToDeviceUnits(MAXTEXTHEIGHT); + scaledButtonTopPadding = LogicalToDeviceUnits(BUTTONTOPPADDING); + scaledButtonDetailsLeftPadding = LogicalToDeviceUnits(BUTTONDETAILS_LEFTPADDING); + scaledMessageTopPadding = LogicalToDeviceUnits(MESSAGE_TOPPADDING); + scaledHeightPadding = LogicalToDeviceUnits(HEIGHTPADDING); + scaledButtonWidth = LogicalToDeviceUnits(BUTTONWIDTH); + scaledButtonHeight = LogicalToDeviceUnits(BUTTONHEIGHT); + scaledButtonAlignmentWidth = LogicalToDeviceUnits(BUTTONALIGNMENTWIDTH); + scaledButtonAlignmentPadding = LogicalToDeviceUnits(BUTTONALIGNMENTPADDING); + scaledDetailsWidthPadding = LogicalToDeviceUnits(DETAILSWIDTHPADDING); + scaledDetailsHeight = LogicalToDeviceUnits(DETAILSHEIGHT); + scaledPictureWidth = LogicalToDeviceUnits(PICTUREWIDTH); + scaledPictureHeight = LogicalToDeviceUnits(PICTUREHEIGHT); + scaledExceptionMessageVerticalPadding = LogicalToDeviceUnits(EXCEPTIONMESSAGEVERTICALPADDING); + } + + string messageRes; + string messageText; + Button[] buttons; + bool detailAnchor = false; + + WarningException w = t as WarningException; + if (w != null) { + messageRes = SR.ExDlgWarningText; + messageText = w.Message; + if (w.HelpUrl == null) { + buttons = new Button[] {continueButton}; + } + else { + buttons = new Button[] {continueButton, helpButton}; + } + } + else { + messageText = t.Message; + + detailAnchor = true; + + if (Application.AllowQuit) { + if (t is SecurityException) { + messageRes = "ExDlgSecurityErrorText"; + } + else { + messageRes = "ExDlgErrorText"; + } + buttons = new Button[] {detailsButton, continueButton, quitButton}; + } + else { + if (t is SecurityException) { + messageRes = "ExDlgSecurityContinueErrorText"; + } + else { + messageRes = "ExDlgContinueErrorText"; + } + buttons = new Button[] {detailsButton, continueButton}; + } + } + + if (messageText.Length == 0) { + messageText = t.GetType().Name; + } + if (t is SecurityException) { + messageText = SR.GetString(messageRes, t.GetType().Name, Trim(messageText)); + } + else { + messageText = SR.GetString(messageRes, Trim(messageText)); + } + + StringBuilder detailsTextBuilder = new StringBuilder(); + string newline = "\r\n"; + string separator = SR.GetString(SR.ExDlgMsgSeperator); + string sectionseparator = SR.GetString(SR.ExDlgMsgSectionSeperator); + if (Application.CustomThreadExceptionHandlerAttached) { + detailsTextBuilder.Append(SR.GetString(SR.ExDlgMsgHeaderNonSwitchable)); + } + else { + detailsTextBuilder.Append(SR.GetString(SR.ExDlgMsgHeaderSwitchable)); + } + detailsTextBuilder.Append(string.Format(CultureInfo.CurrentCulture, sectionseparator, SR.GetString(SR.ExDlgMsgExceptionSection))); + detailsTextBuilder.Append(t.ToString()); + detailsTextBuilder.Append(newline); + detailsTextBuilder.Append(newline); + detailsTextBuilder.Append(string.Format(CultureInfo.CurrentCulture, sectionseparator, SR.GetString(SR.ExDlgMsgLoadedAssembliesSection))); + new FileIOPermission(PermissionState.Unrestricted).Assert(); + try { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { + AssemblyName name = asm.GetName(); + string fileVer = SR.GetString(SR.NotAvailable); + + try { + + // bug 113573 -- if there's a path with an escaped value in it + // like c:\temp\foo%2fbar, the AssemblyName call will unescape it to + // c:\temp\foo\bar, which is wrong, and this will fail. It doesn't look like the + // assembly name class handles this properly -- even the "CodeBase" property is un-escaped + // so we can't circumvent this. + // + if (name.EscapedCodeBase != null && name.EscapedCodeBase.Length > 0) { + Uri codeBase = new Uri(name.EscapedCodeBase); + if (codeBase.Scheme == "file") { + fileVer = FileVersionInfo.GetVersionInfo(NativeMethods.GetLocalPath(name.EscapedCodeBase)).FileVersion; + } + } + } + catch(System.IO.FileNotFoundException){ + } + detailsTextBuilder.Append(SR.GetString(SR.ExDlgMsgLoadedAssembliesEntry, name.Name, name.Version, fileVer, name.EscapedCodeBase)); + detailsTextBuilder.Append(separator); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + + detailsTextBuilder.Append(string.Format(CultureInfo.CurrentCulture, sectionseparator, SR.GetString(SR.ExDlgMsgJITDebuggingSection))); + if (Application.CustomThreadExceptionHandlerAttached) { + detailsTextBuilder.Append(SR.GetString(SR.ExDlgMsgFooterNonSwitchable)); + } + else { + detailsTextBuilder.Append(SR.GetString(SR.ExDlgMsgFooterSwitchable)); + } + + detailsTextBuilder.Append(newline); + detailsTextBuilder.Append(newline); + + string detailsText = detailsTextBuilder.ToString(); + + Graphics g = message.CreateGraphicsInternal(); + + Size textSize = new Size(scaledMaxWidth - scaledPaddingWidth, int.MaxValue); + + if (DpiHelper.EnableThreadExceptionDialogHighDpiImprovements && (Label.UseCompatibleTextRenderingDefault == false)) { + // we need to measure string using API that matches the rendering engine - TextRenderer.MeasureText for GDI + textSize = Size.Ceiling(TextRenderer.MeasureText(messageText, Font, textSize, TextFormatFlags.WordBreak)); + } + else { + // if HighDpi improvements are not enabled, or rendering mode is GDI+, use Graphics.MeasureString + textSize = Size.Ceiling(g.MeasureString(messageText, Font, textSize.Width)); + } + + textSize.Height += scaledExceptionMessageVerticalPadding; + g.Dispose(); + + if (textSize.Width < scaledMaxTextWidth) textSize.Width = scaledMaxTextWidth; + if (textSize.Height > scaledMaxHeight) textSize.Height = scaledMaxHeight; + + int width = textSize.Width + scaledPaddingWidth; + int buttonTop = Math.Max(textSize.Height, scaledMaxTextHeight) + scaledPaddingHeight; + + // SECREVIEW : We must get a hold of the parent to get at it's text + // : to make this dialog look like the parent. + // + IntSecurity.GetParent.Assert(); + try { + Form activeForm = Form.ActiveForm; + if (activeForm == null || activeForm.Text.Length == 0) { + Text = SR.GetString(SR.ExDlgCaption); + } + else { + Text = SR.GetString(SR.ExDlgCaption2, activeForm.Text); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + AcceptButton = continueButton; + CancelButton = continueButton; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + StartPosition = FormStartPosition.CenterScreen; + Icon = null; + ClientSize = new Size(width, buttonTop + scaledButtonTopPadding); + TopMost = true; + + pictureBox.Location = new Point(scaledPictureWidth/8, scaledPictureHeight/8); + pictureBox.Size = new Size(scaledPictureWidth*3/4, scaledPictureHeight*3/4); + pictureBox.SizeMode = PictureBoxSizeMode.StretchImage; + if (t is SecurityException) { + pictureBox.Image = SystemIcons.Information.ToBitmap(); + } + else { + pictureBox.Image = SystemIcons.Error.ToBitmap(); + } + Controls.Add(pictureBox); + message.SetBounds(scaledPictureWidth, + scaledMessageTopPadding + (scaledMaxTextHeight - Math.Min(textSize.Height, scaledMaxTextHeight)) / 2, + textSize.Width, textSize.Height); + message.Text = messageText; + Controls.Add(message); + + continueButton.Text = SR.GetString(SR.ExDlgContinue); + continueButton.FlatStyle = FlatStyle.Standard; + continueButton.DialogResult = DialogResult.Cancel; + + quitButton.Text = SR.GetString(SR.ExDlgQuit); + quitButton.FlatStyle = FlatStyle.Standard; + quitButton.DialogResult = DialogResult.Abort; + + helpButton.Text = SR.GetString(SR.ExDlgHelp); + helpButton.FlatStyle = FlatStyle.Standard; + helpButton.DialogResult = DialogResult.Yes; + + detailsButton.Text = SR.GetString(SR.ExDlgShowDetails); + detailsButton.FlatStyle = FlatStyle.Standard; + detailsButton.Click += new EventHandler(DetailsClick); + + Button b = null; + int startIndex = 0; + + if (detailAnchor) { + b = detailsButton; + + expandImage = new Bitmap(this.GetType(), DownBitmapName); + expandImage.MakeTransparent(); + collapseImage = new Bitmap(this.GetType(), UpBitmapName); + collapseImage.MakeTransparent(); + + if (DpiHelper.EnableThreadExceptionDialogHighDpiImprovements) { + ScaleBitmapLogicalToDevice(ref expandImage); + ScaleBitmapLogicalToDevice(ref collapseImage); + } + + b.SetBounds(scaledButtonDetailsLeftPadding, buttonTop, scaledButtonWidth, scaledButtonHeight); + b.Image = expandImage; + b.ImageAlign = ContentAlignment.MiddleLeft; + Controls.Add(b); + startIndex = 1; + } + + int buttonLeft = (width - scaledButtonDetailsLeftPadding - ((buttons.Length - startIndex) * scaledButtonAlignmentWidth - scaledButtonAlignmentPadding)); + + for (int i = startIndex; i < buttons.Length; i++) { + b = buttons[i]; + b.SetBounds(buttonLeft, buttonTop, scaledButtonWidth, scaledButtonHeight); + Controls.Add(b); + buttonLeft += scaledButtonAlignmentWidth; + } + + details.Text = detailsText; + details.ScrollBars = ScrollBars.Both; + details.Multiline = true; + details.ReadOnly = true; + details.WordWrap = false; + details.TabStop = false; + details.AcceptsReturn = false; + + details.SetBounds(scaledButtonDetailsLeftPadding, buttonTop + scaledButtonTopPadding, width - scaledDetailsWidthPadding, scaledDetailsHeight); + details.Visible = detailsVisible; + Controls.Add(details); + if (DpiHelper.EnableThreadExceptionDialogHighDpiImprovements) { + DpiChanged += ThreadExceptionDialog_DpiChanged; + } + } + + private void ThreadExceptionDialog_DpiChanged(object sender, DpiChangedEventArgs e) { + if (expandImage != null) { + expandImage.Dispose(); + } + expandImage = new Bitmap(this.GetType(), DownBitmapName); + expandImage.MakeTransparent(); + + if (collapseImage != null) { + collapseImage.Dispose(); + } + collapseImage = new Bitmap(this.GetType(), UpBitmapName); + collapseImage.MakeTransparent(); + + ScaleBitmapLogicalToDevice(ref expandImage); + ScaleBitmapLogicalToDevice(ref collapseImage); + + detailsButton.Image = detailsVisible ? collapseImage : expandImage; + } + + /// + /// + /// + /// Hide the property + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoSize + { + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + get + { + return base.AutoSize; + } + [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] + set + { + base.AutoSize = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// Called when the details button is clicked. + /// + private void DetailsClick(object sender, EventArgs eventargs) { + int delta = details.Height + scaledHeightPadding; + if (detailsVisible) delta = -delta; + Height = Height + delta; + detailsVisible = !detailsVisible; + details.Visible = detailsVisible; + detailsButton.Image = detailsVisible ? collapseImage : expandImage; + } + + private static string Trim(string s) { + if (s == null) return s; + int i = s.Length; + while (i > 0 && s[i - 1] == '.') i--; + return s.Substring(0, i); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TickStyle.cs b/WindowsForms/Managed/System/WinForms/TickStyle.cs new file mode 100644 index 000000000..ecb177853 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TickStyle.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the + /// location of tick marks in a + /// control. + /// + /// + public enum TickStyle { + + /// + /// + /// + /// No tick marks appear in the control. + /// + /// + None = 0, + + /// + /// + /// + /// Tick + /// marks are located on the top of horizontal control or on the left of a vertical control. + /// + /// + TopLeft = 1, + + /// + /// + /// + /// Tick marks are + /// located on the bottom of a horizontal control or on the right side of a vertical control. + /// + /// + BottomRight = 2, + + /// + /// + /// + /// Tick + /// marks are located on both sides of the control. + /// + /// + Both = 3, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/Timer.cs b/WindowsForms/Managed/System/WinForms/Timer.cs new file mode 100644 index 000000000..75aed1dbc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Timer.cs @@ -0,0 +1,473 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Threading; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Windows.Forms.Design; + using System; + using System.Globalization; + + /// + /// + /// Implements a Windows-based timer that raises an event at user-defined intervals. This timer is optimized for + /// use in Win Forms + /// applications and must be used in a window. + /// + [ + DefaultProperty("Interval"), + DefaultEvent("Tick"), + ToolboxItemFilter("System.Windows.Forms"), + SRDescription(SR.DescriptionTimer) + ] + public class Timer : Component { + + /// + /// + /// + private int interval; + + /// + /// + /// + private bool enabled; + + /// + /// + /// + internal EventHandler onTimer; + + /// + /// + /// + private GCHandle timerRoot; + + + // our holder for the HWND that handles our Timer messages. + // + private TimerNativeWindow timerWindow; + + /// + /// + /// + private object userData; + + private object syncObj = new object(); + + /// + /// + /// Initializes a new instance of the + /// class. + /// + public Timer() + : base() { + interval = 100; + } + + /// + /// + /// Initializes a new instance of the class with the specified container. + /// + public Timer(IContainer container) : this() { + if (container == null) { + throw new ArgumentNullException("container"); + } + + container.Add(this); + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Occurs when the specified timer + /// interval has elapsed and the timer is enabled. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TimerTimerDescr)] + public event EventHandler Tick { + add { + onTimer += value; + } + remove { + onTimer -= value; + } + } + + /// + /// + /// + /// Disposes of the resources (other than memory) used by the timer. + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (timerWindow != null) { + timerWindow.StopTimer(); + } + + Enabled = false; + } + timerWindow = null; + base.Dispose(disposing); + } + + /// + /// + /// Indicates whether the timer is + /// running. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TimerEnabledDescr) + ] + public virtual bool Enabled { + get { + if (timerWindow == null) { + return enabled; + } + return timerWindow.IsTimerRunning; + } + + set { + lock(syncObj) { + + if (enabled != value) { + + enabled = value; + + // At runtime, enable or disable the corresponding Windows timer + // + if (!DesignMode) { + if (value) { + + // create the timer window if needed. + // + if (timerWindow == null) { + + timerWindow = new TimerNativeWindow(this); + } + + timerRoot = GCHandle.Alloc(this); + + timerWindow.StartTimer(interval); + } + else{ + if (timerWindow != null){ + timerWindow.StopTimer(); + } + + if (timerRoot.IsAllocated) { + timerRoot.Free(); + } + + } + } + + + } + } + + } + } + + /// + /// + /// + /// Indicates the time, in milliseconds, between timer ticks. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(100), + SRDescription(SR.TimerIntervalDescr) + ] + public int Interval { + get { + return interval; + } + set { + lock(syncObj) { + if (value < 1) + throw new ArgumentOutOfRangeException("Interval", SR.GetString(SR.TimerInvalidInterval, value, (0).ToString(CultureInfo.CurrentCulture))); + + if (interval != value) { + interval = value; + if (Enabled) { + + Debug.Assert(DesignMode || timerWindow != null, "Why don't we have a timer HWND?"); + // just change the timer value, don't tear down the timer + // itself. + // + if (!DesignMode && timerWindow != null) { + timerWindow.RestartTimer(value); + } + } + } + } + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnTick(EventArgs e) { + if (onTimer != null) onTimer(this, e); + } + + /// + /// + /// Starts the + /// timer. + /// + public void Start() { + Enabled = true; + } + + /// + /// + /// Stops the + /// timer. + /// + public void Stop() { + Enabled = false; + } + + /// + /// + /// returns us as a string. + /// + /// + public override string ToString() { + string s = base.ToString(); + return s + ", Interval: " + Interval.ToString(CultureInfo.CurrentCulture); + } + + + private class TimerNativeWindow : NativeWindow { + + + // the timer that owns us + // + private Timer _owner; + + // our current id -- this is usally the same as TimerID but we also + // use it as a flag of when our timer is running. + // + private int _timerID; + + // arbitrary timer ID. + // + private static int TimerID = 1; + + // setting this when we are stopping the timer so someone can't restart it in the process. + // + private bool _stoppingTimer; + + internal TimerNativeWindow(Timer owner) { + + this._owner = owner; + } + + ~TimerNativeWindow() { + + + + + // note this call will work form the finalizer thread. + // + StopTimer(); + } + + public bool IsTimerRunning { + get { + + return _timerID != 0 && Handle != IntPtr.Zero; + } + } + + + // Ensures that our HWND has been created. + // + private bool EnsureHandle() { + if (Handle == IntPtr.Zero) { + + + // we create a totally vanilla invisible window just for WM_TIMER messages. + // + CreateParams cp = new CreateParams(); + cp.Style = 0; + cp.ExStyle = 0; + cp.ClassStyle = 0; + cp.Caption = GetType().Name; + + // Message only windows are cheaper and have fewer issues than + // full blown invisible windows. But, they are only supported + // on NT. + if (Environment.OSVersion.Platform == PlatformID.Win32NT) { + cp.Parent = (IntPtr)NativeMethods.HWND_MESSAGE; + } + + CreateHandle(cp); + } + Debug.Assert(Handle != IntPtr.Zero, "Timer HWND creation failed!"); + return Handle != IntPtr.Zero; + } + + // Returns true if we need to marshal across threads to access this timer's HWND. + // + private bool GetInvokeRequired(IntPtr hWnd) { + if (hWnd != IntPtr.Zero) { + int pid; + int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, hWnd), out pid); + int currentThread = SafeNativeMethods.GetCurrentThreadId(); + return(hwndThread != currentThread); + } + return false; + } + + // change the interval of the timer without destroying the HWND. + // + public void RestartTimer(int newInterval) { + StopTimer(false, IntPtr.Zero); + StartTimer(newInterval); + } + + // Start the timer with the specified interval. + // + public void StartTimer(int interval) { + + if (_timerID == 0 && !_stoppingTimer) { + if (EnsureHandle()) { + _timerID = (int) SafeNativeMethods.SetTimer(new HandleRef(this, Handle), TimerID++, interval, IntPtr.Zero); + } + } + } + + // stop the timer. + // + public void StopTimer() { + + StopTimer(true, IntPtr.Zero); + } + + // stop the timer and optionally destroy the HWND. + // + public void StopTimer(bool destroyHwnd, IntPtr hWnd) { + + + if (hWnd == IntPtr.Zero) { + + hWnd = Handle; + } + + // Fire a message across threads to destroy the timer and HWND on the thread that created it. + // + if (GetInvokeRequired(hWnd)) { + UnsafeNativeMethods.PostMessage(new HandleRef(this, hWnd), NativeMethods.WM_CLOSE, 0, 0); + return; + } + + // Locking 'this' here is ok since this is an internal class. See VSW#464499. + lock(this) { + + if (_stoppingTimer || hWnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(new HandleRef(this, hWnd))) { + + return; + } + + if (_timerID != 0) { + + + try { + _stoppingTimer = true; + SafeNativeMethods.KillTimer(new HandleRef(this, hWnd), _timerID); + } + finally { + _timerID = 0; + _stoppingTimer = false; + } + + } + + if (destroyHwnd) { + base.DestroyHandle(); + } + } + } + + // Destroy the handle, stopping the timer first. + // + public override void DestroyHandle() { + // don't recurse! + // + StopTimer(false, IntPtr.Zero); + Debug.Assert(_timerID == 0, "Destroying handle with timerID still set."); + base.DestroyHandle(); + } + + protected override void OnThreadException(Exception e) { + Application.OnThreadException(e); + } + + public override void ReleaseHandle() { + // don't recurse! + // + StopTimer(false, IntPtr.Zero); + Debug.Assert(_timerID == 0, "Destroying handle with timerID still set."); + + base.ReleaseHandle(); + } + + protected override void WndProc(ref Message m) { + + Debug.Assert(m.HWnd == Handle && Handle != IntPtr.Zero, "Timer getting messages for other windows?"); + + // for timer messages, make sure they're ours (it'll be wierd if they aren't) + // and call the timer event. + // + if (m.Msg == NativeMethods.WM_TIMER) { + //Debug.Assert((int)m.WParam == _timerID, "Why are we getting a timer message that isn't ours?"); + if (unchecked( (int) (long)m.WParam) == _timerID) { + _owner.OnTick(EventArgs.Empty); + return; + } + } + else if (m.Msg == NativeMethods.WM_CLOSE) { + // this is a posted method from another thread that tells us we need + // to kill the timer. The handle may already be gone, so we specify it here. + // + StopTimer(true, m.HWnd); + return; + } + base.WndProc(ref m); + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBar.cs b/WindowsForms/Managed/System/WinForms/ToolBar.cs new file mode 100644 index 000000000..3e9bb6308 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBar.cs @@ -0,0 +1,2178 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Drawing; + using System.Collections; + using System.ComponentModel.Design; + using Microsoft.Win32; + using System.Globalization; + + /// + /// + /// Represents a Windows toolbar. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultEvent("ButtonClick"), + Designer("System.Windows.Forms.Design.ToolBarDesigner, " + AssemblyRef.SystemDesign), + DefaultProperty("Buttons") + ] + public class ToolBar : Control { + + private ToolBarButtonCollection buttonsCollection; + + /// + /// + /// The size of a button in the ToolBar + /// + internal Size buttonSize = System.Drawing.Size.Empty; + + /// + /// + /// This is used by our autoSizing support. + /// + private int requestedSize; + /// + /// + /// This represents the width of the drop down arrow we have if the + /// DropDownArrows property is true. this value is used by the ToolBarButton + /// objects to compute their size + /// + internal const int DDARROW_WIDTH = 15; + + /// + /// + /// Indicates what our appearance will be. This will either be normal + /// or flat. + /// + private ToolBarAppearance appearance = ToolBarAppearance.Normal; + + /// + /// + /// Indicates whether or not we have a border + /// + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + + /// + /// + /// The array of buttons we're working with. + /// + private ToolBarButton [] buttons; + + /// + /// + /// The number of buttons we're working with + /// + private int buttonCount = 0; + + /// + /// + /// Indicates if text captions should go underneath images in buttons or + /// to the right of them + /// + private ToolBarTextAlign textAlign = ToolBarTextAlign.Underneath; + + /// + /// + /// The ImageList object that contains the main images for our control. + /// + private ImageList imageList = null; + + /// + /// + /// The maximum width of buttons currently being displayed. This is needed + /// by our autoSizing code. If this value is -1, it needs to be recomputed. + /// + private int maxWidth = -1; + /// + /// + /// To be supplied. + /// + private int hotItem = -1; + + // VSWhidbey 219660: Track the current scale factor so we can scale our buttons + private float currentScaleDX = 1.0F; + private float currentScaleDY = 1.0F; + + private const int TOOLBARSTATE_wrappable = 0x00000001; + private const int TOOLBARSTATE_dropDownArrows = 0x00000002; + private const int TOOLBARSTATE_divider = 0x00000004; + private const int TOOLBARSTATE_showToolTips = 0x00000008; + private const int TOOLBARSTATE_autoSize = 0x00000010; + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 toolBarState; // see TOOLBARSTATE_ consts above + + // event handlers + // + private ToolBarButtonClickEventHandler onButtonClick = null; + private ToolBarButtonClickEventHandler onButtonDropDown = null; + + + /// + /// + /// Initializes a new instance of the class. + /// + public ToolBar() + : base() { + + // Set this BEFORE calling any other methods so that these defaults will be propagated + toolBarState = new System.Collections.Specialized.BitVector32(TOOLBARSTATE_autoSize | + TOOLBARSTATE_showToolTips | + TOOLBARSTATE_divider | + TOOLBARSTATE_dropDownArrows | + TOOLBARSTATE_wrappable); + + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.FixedHeight, AutoSize); + SetStyle(ControlStyles.FixedWidth, false); + TabStop = false; + Dock = DockStyle.Top; + buttonsCollection = new ToolBarButtonCollection(this); + } + + /// + /// + /// Gets or sets the appearance of the toolbar + /// control and its buttons. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(ToolBarAppearance.Normal), + Localizable(true), + SRDescription(SR.ToolBarAppearanceDescr) + ] + public ToolBarAppearance Appearance { + get { + return appearance; + } + + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolBarAppearance.Normal, (int)ToolBarAppearance.Flat)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolBarAppearance)); + } + + if (value != appearance) { + appearance = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates whether the toolbar + /// adjusts its size automatically based on the size of the buttons and the + /// dock style. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ToolBarAutoSizeDescr), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return toolBarState[TOOLBARSTATE_autoSize]; + } + + set { + // Note that we intentionally do not call base. Toolbars size themselves by + // overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize + // continue to return false to keep our LayoutEngines from messing with TextBoxes. + // This is done for backwards compatibility since the new AutoSize behavior differs. + if (AutoSize != value) { + toolBarState[TOOLBARSTATE_autoSize] = value; + if (Dock == DockStyle.Left || Dock == DockStyle.Right) { + SetStyle(ControlStyles.FixedWidth, AutoSize); + SetStyle(ControlStyles.FixedHeight, false); + } + else { + SetStyle(ControlStyles.FixedHeight, AutoSize); + SetStyle(ControlStyles.FixedWidth, false); + } + AdjustSize(Dock); + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// Gets or sets + /// the border style of the toolbar control. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.None), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.ToolBarBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + + if (borderStyle != value) { + borderStyle = value; + + //UpdateStyles(); + RecreateHandle(); // Looks like we need to recreate the handle to avoid painting glitches + } + } + } + + /// + /// + /// A collection of controls assigned to the + /// toolbar control. The property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ToolBarButtonsDescr), + MergableProperty(false) + ] + public ToolBarButtonCollection Buttons { + get { + return buttonsCollection; + } + } + + /// + /// + /// Gets or sets + /// the size of the buttons on the toolbar control. + /// + [ + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.All), + Localizable(true), + SRDescription(SR.ToolBarButtonSizeDescr) + ] + public Size ButtonSize { + get { + if (buttonSize.IsEmpty) { + + // Obtain the current buttonsize of the first button from the winctl control + // + if (IsHandleCreated && buttons != null && buttonCount > 0) { + int result = unchecked( (int) (long)SendMessage(NativeMethods.TB_GETBUTTONSIZE, 0, 0)); + if (result > 0) { + return new Size(NativeMethods.Util.LOWORD(result), NativeMethods.Util.HIWORD(result)); + } + } + if (TextAlign == ToolBarTextAlign.Underneath) { + return new Size(39, 36); // Default button size + } + else { + return new Size(23, 22); // Default button size + } + } + else { + return buttonSize; + } + } + + set { + + if (value.Width < 0 || value.Height < 0) + throw new ArgumentOutOfRangeException("ButtonSize", SR.GetString(SR.InvalidArgument, "ButtonSize", value.ToString())); + + if (buttonSize != value) { + buttonSize = value; + maxWidth = -1; // Force recompute of maxWidth + RecreateHandle(); + AdjustSize(Dock); + } + } + } + + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to get base.CreateParams first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_TOOLBAR; + + // windows forms has it's own docking code. + // + cp.Style |= NativeMethods.CCS_NOPARENTALIGN + | NativeMethods.CCS_NORESIZE; + // | NativeMethods.WS_CHILD was commented out since setTopLevel should be able to work. + + if (!Divider) cp.Style |= NativeMethods.CCS_NODIVIDER ; + if (Wrappable) cp.Style |= NativeMethods.TBSTYLE_WRAPPABLE; + if (ShowToolTips && !DesignMode) cp.Style |= NativeMethods.TBSTYLE_TOOLTIPS; + + cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); + cp.Style &= (~NativeMethods.WS_BORDER); + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + switch (appearance) { + case ToolBarAppearance.Normal: + break; + case ToolBarAppearance.Flat: + cp.Style |= NativeMethods.TBSTYLE_FLAT; + break; + } + + switch (textAlign) { + case ToolBarTextAlign.Underneath: + break; + case ToolBarTextAlign.Right: + cp.Style |= NativeMethods.TBSTYLE_LIST; + break; + } + + return cp; + } + } + + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(100, 22); + } + } + + /// + /// + /// Gets or sets a value indicating + /// whether the toolbar displays a divider. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(true), + SRDescription(SR.ToolBarDividerDescr) + ] + public bool Divider { + get { + return toolBarState[TOOLBARSTATE_divider]; + } + + set { + if (Divider != value) { + + toolBarState[TOOLBARSTATE_divider] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// + /// + /// Sets the way in which this ToolBar is docked to its parent. We need to + /// override this to ensure autoSizing works correctly + /// + /// + [ + Localizable(true), + DefaultValue(DockStyle.Top) + ] + public override DockStyle Dock { + get { return base.Dock;} + + set { + //valid values are 0x0 to 0x5 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle)); + } + + if (Dock != value) { + if (value == DockStyle.Left || value == DockStyle.Right) { + SetStyle(ControlStyles.FixedWidth, AutoSize); + SetStyle(ControlStyles.FixedHeight, false); + } + else { + SetStyle(ControlStyles.FixedHeight, AutoSize); + SetStyle(ControlStyles.FixedWidth, false); + } + AdjustSize(value); + base.Dock = value; + } + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// Gets or sets a value indicating whether drop-down buttons on a + /// toolbar display down arrows. + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolBarDropDownArrowsDescr) + ] + public bool DropDownArrows { + get { + return toolBarState[TOOLBARSTATE_dropDownArrows]; + } + + set { + + if (DropDownArrows != value) { + toolBarState[TOOLBARSTATE_dropDownArrows] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + /// Gets or sets the collection of images available to the toolbar button + /// controls. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ToolBarImageListDescr) + ] + public ImageList ImageList { + get { + return this.imageList; + } + set { + if (value != imageList) { + EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle); + EventHandler disposedHandler = new EventHandler(DetachImageList); + + if (imageList != null) { + imageList.Disposed -= disposedHandler; + imageList.RecreateHandle -= recreateHandler; + } + + imageList = value; + + if (value != null) { + value.Disposed += disposedHandler; + value.RecreateHandle += recreateHandler; + } + + if (IsHandleCreated) + RecreateHandle(); + } + } + } + + /// + /// + /// Gets the size of the images in the image list assigned to the + /// toolbar. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolBarImageSizeDescr) + ] + public Size ImageSize { + get { + if (this.imageList != null) { + return this.imageList.ImageSize; + } + else { + return new Size(0, 0); + } + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// The preferred height for this ToolBar control. This is + /// used by the AutoSizing code. + /// + internal int PreferredHeight { + get { + int height = 0; + + if (buttons == null || buttonCount == 0 || !IsHandleCreated) { + height = ButtonSize.Height; + } + else { + // get the first visible button and get it's height + // + NativeMethods.RECT rect = new NativeMethods.RECT(); + int firstVisible; + + for (firstVisible = 0; firstVisible < buttons.Length; firstVisible++) { + if (buttons[firstVisible] != null && buttons[firstVisible].Visible) { + break; + } + } + if (firstVisible == buttons.Length) { + firstVisible = 0; + } + + SendMessage(NativeMethods.TB_GETRECT, firstVisible, ref rect); + + // height is the button's height plus some extra goo + // + height = rect.bottom - rect.top; + } + + // if the ToolBar is wrappable, and there is more than one row, make + // sure the height is correctly adjusted + // + if (Wrappable && IsHandleCreated) { + height = height * unchecked( (int) (long)SendMessage(NativeMethods.TB_GETROWS, 0, 0)); + } + + height = (height > 0) ? height : 1; + + switch(borderStyle) { + case BorderStyle.FixedSingle: + height += SystemInformation.BorderSize.Height; + break; + case BorderStyle.Fixed3D: + height += SystemInformation.Border3DSize.Height; + break; + } + + if (Divider) height += 2; + + height += 4; + + return height; + } + + } + + /// + /// + /// The preferred width for this ToolBar control. This is + /// used by AutoSizing code. + /// NOTE!!!!!!!!! This function assumes it's only going to get called + /// if the control is docked left or right [ie, it really + /// just returns a max width] + /// + internal int PreferredWidth { + get { + int width; + + // fortunately, we compute this value sometimes, so we can just + // use it if we have it. + // + if (maxWidth == -1) { + // don't have it, have to recompute + // + if (!IsHandleCreated || buttons == null) + maxWidth = ButtonSize.Width; + else { + + NativeMethods.RECT rect = new NativeMethods.RECT(); + + for (int x = 0; x < buttonCount; x++) { + SendMessage(NativeMethods.TB_GETRECT, 0, ref rect); + if ((rect.right - rect.left) > maxWidth) + maxWidth = rect.right - rect.left; + } + } + } + + width = maxWidth; + + if (borderStyle != BorderStyle.None) { + width += SystemInformation.BorderSize.Height * 4 + 3; + } + + return width; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override RightToLeft RightToLeft { + get { + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftChanged { + add { + base.RightToLeftChanged += value; + } + remove { + base.RightToLeftChanged -= value; + } + } + + /// + /// + /// VSWhidbey 219660: We need to track the current scale factor so that we can tell the + /// unmanaged control how to scale its buttons. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + currentScaleDX = dx; + currentScaleDY = dy; + base.ScaleCore(dx, dy); + UpdateButtons(); + } + + /// + /// + /// VSWhidbey 219660: We need to track the current scale factor so that we can tell the + /// unmanaged control how to scale its buttons. + /// + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + currentScaleDX = factor.Width; + currentScaleDY = factor.Height; + base.ScaleControl(factor, specified); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the toolbar displays a + /// tool tip for each button. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.ToolBarShowToolTipsDescr) + ] + public bool ShowToolTips { + get { + return toolBarState[TOOLBARSTATE_showToolTips]; + } + set { + if (ShowToolTips != value) { + + toolBarState[TOOLBARSTATE_showToolTips] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// + /// + [DefaultValue(false)] + new public bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + /// + /// + /// + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// Gets or sets the alignment of text in relation to each + /// image displayed on + /// the toolbar button controls. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ToolBarTextAlign.Underneath), + Localizable(true), + SRDescription(SR.ToolBarTextAlignDescr) + ] + public ToolBarTextAlign TextAlign { + get { + return textAlign; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolBarTextAlign.Underneath, (int)ToolBarTextAlign.Right)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolBarTextAlign)); + } + + if (textAlign == value) return; + textAlign = value; + RecreateHandle(); + } + } + + /// + /// + /// Gets + /// or sets a value + /// indicating whether the toolbar buttons wrap to the next line if the + /// toolbar becomes too small to display all the buttons + /// on the same line. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ToolBarWrappableDescr) + ] + public bool Wrappable { + get { + return toolBarState[TOOLBARSTATE_wrappable]; + } + set { + if (Wrappable != value) { + toolBarState[TOOLBARSTATE_wrappable] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Occurs when a on the is clicked. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolBarButtonClickDescr)] + public event ToolBarButtonClickEventHandler ButtonClick { + add { + onButtonClick += value; + } + remove { + onButtonClick -= value; + } + } + + /// + /// + /// Occurs when a drop-down style or its down arrow is clicked. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolBarButtonDropDownDescr)] + public event ToolBarButtonClickEventHandler ButtonDropDown { + add { + onButtonDropDown += value; + } + remove { + onButtonDropDown -= value; + } + } + + /// + /// + /// ToolBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// Adjusts the height or width of the ToolBar to make sure we have enough + /// room to show the buttons. + /// + /// + // VS 30082 -- we pass in a value for dock rather than calling Dock ourselves + // because we can't change Dock until the size has been properly adjusted. + private void AdjustSize(DockStyle dock) { + int saveSize = requestedSize; + try { + if (dock == DockStyle.Left || dock == DockStyle.Right) { + Width = AutoSize ? PreferredWidth : saveSize; + } + else { + Height = AutoSize ? PreferredHeight : saveSize; + } + } + finally { + requestedSize = saveSize; + } + } + + /// + /// + /// This routine lets us change a bunch of things about the toolbar without + /// having each operation wait for the paint to complete. This must be + /// matched up with a call to endUpdate(). + /// + internal void BeginUpdate() { + BeginUpdateInternal(); + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_BAR_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// Resets the imageList to null. We wire this method up to the imageList's + /// Dispose event, so that we don't hang onto an imageList that's gone away. + /// + private void DetachImageList(object sender, EventArgs e) { + ImageList = null; + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + // + lock(this) { + // We need to mark the Disposing state here so buttonsCollection won't attempt to update + // the buttons. VSW#340606. + bool currentDisposing = GetState(STATE_DISPOSING); + + try { + SetState(STATE_DISPOSING, true); + + if (imageList != null) { + imageList.Disposed -= new EventHandler(DetachImageList); + imageList = null; + } + + if (buttonsCollection != null) { + ToolBarButton[] buttonCopy = new ToolBarButton[buttonsCollection.Count]; + ((ICollection)buttonsCollection).CopyTo(buttonCopy, 0); + buttonsCollection.Clear(); + + foreach (ToolBarButton b in buttonCopy) { + b.Dispose(); + } + } + } + finally { + SetState(STATE_DISPOSING, currentDisposing); + } + } + } + + base.Dispose(disposing); + } + + /// + /// + /// This routine lets us change a bunch of things about the toolbar without + /// having each operation wait for the paint to complete. This must be + /// matched up with a call to beginUpdate(). + /// + internal void EndUpdate() { + EndUpdateInternal(); + } + + /// + /// + /// Forces the button sizes based on various different things. The default + /// ToolBar button sizing rules are pretty primitive and this tends to be + /// a little better, and lets us actually show things like DropDown Arrows + /// for ToolBars + /// + /// + private void ForceButtonWidths() { + + if (buttons != null && buttonSize.IsEmpty && IsHandleCreated) { + + // force ourselves to re-compute this each time + // + maxWidth = -1; + + for (int x = 0; x < buttonCount; x++) { + + NativeMethods.TBBUTTONINFO tbbi = new NativeMethods.TBBUTTONINFO(); + tbbi.cbSize = Marshal.SizeOf(typeof(NativeMethods.TBBUTTONINFO)); + tbbi.cx = buttons[x].Width; + + if (tbbi.cx > maxWidth) { + maxWidth = tbbi.cx; + } + + tbbi.dwMask = NativeMethods.TBIF_SIZE; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONINFO, x, ref tbbi); + } + } + } + + private void ImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) + RecreateHandle(); + } + + /// + /// + /// + /// + private void Insert(int index, ToolBarButton button) { + + button.parent = this; + + if (buttons == null) { + buttons = new ToolBarButton[4]; + } + else if (buttons.Length == buttonCount) { + ToolBarButton[] newButtons = new ToolBarButton[buttonCount + 4]; + System.Array.Copy(buttons, 0, newButtons, 0, buttonCount); + buttons = newButtons; + } + + if (index < buttonCount) + System.Array.Copy(buttons, index, buttons, index + 1, buttonCount - index); + + buttons[index] = button; + buttonCount++; + } + + /// + /// + /// Inserts a button at a given location on the toolbar control. + /// + private void InsertButton(int index, ToolBarButton value) { + + if (value == null) + throw new ArgumentNullException("value"); + if (index < 0 || ((buttons != null) && (index > buttonCount))) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + // insert the button into our local array, and then into the + // real windows ToolBar control + // + Insert(index, value); + if (IsHandleCreated) { + NativeMethods.TBBUTTON tbbutton = value.GetTBBUTTON(index); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_INSERTBUTTON, index, ref tbbutton); + } + UpdateButtons(); + } + + /// + /// + /// Adds a button to the ToolBar + /// + /// + private int InternalAddButton(ToolBarButton button) { + if (button == null) + throw new ArgumentNullException("button"); + int index = buttonCount; + Insert(index, button); + return index; + } + + /// + /// + /// Changes the data for a button in the ToolBar, and then does the appropriate + /// work to update the ToolBar control. + /// + /// + internal void InternalSetButton(int index, ToolBarButton value, bool recreate, bool updateText) { + + // tragically, there doesn't appear to be a way to remove the + // string for the button if it has one, so we just have to leave + // it in there. + // + buttons[index].parent = null; + buttons[index].stringIndex = (IntPtr)(-1); + buttons[index] = value; + buttons[index].parent = this; + + if (IsHandleCreated) { + NativeMethods.TBBUTTONINFO tbbi = value.GetTBBUTTONINFO(updateText, index); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONINFO, index, ref tbbi); + + if (tbbi.pszText != IntPtr.Zero) { + Marshal.FreeHGlobal(tbbi.pszText); + } + + if (recreate) { + UpdateButtons(); + } + else { + // after doing anything with the comctl ToolBar control, this + // appears to be a good idea. + // + SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + + ForceButtonWidths(); + this.Invalidate(); + } + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnButtonClick(ToolBarButtonClickEventArgs e) { + if (onButtonClick != null) onButtonClick(this, e); + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnButtonDropDown(ToolBarButtonClickEventArgs e) { + if (onButtonDropDown != null) onButtonDropDown(this, e); + } + + /// + /// + /// + /// + /// Overridden from the control class so we can add all the buttons + /// and do whatever work needs to be done. + /// Don't forget to call base.OnHandleCreated. + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + + // we have to set the button struct size, because they don't. + // + SendMessage(NativeMethods.TB_BUTTONSTRUCTSIZE, Marshal.SizeOf(typeof(NativeMethods.TBBUTTON)), 0); + + // set up some extra goo + // + if (DropDownArrows) + SendMessage(NativeMethods.TB_SETEXTENDEDSTYLE, 0, NativeMethods.TBSTYLE_EX_DRAWDDARROWS); + + // if we have an imagelist, add it in now. + // + if (imageList != null) + SendMessage(NativeMethods.TB_SETIMAGELIST, 0, imageList.Handle); + + RealizeButtons(); + + // Force a repaint, as occasionally the ToolBar border does not paint properly + // (comctl ToolBar is flaky) + // + BeginUpdate(); + try { + Size size = Size; + Size = new Size(size.Width + 1, size.Height); + Size = size; + } + finally { + EndUpdate(); + } + } + + /// + /// + /// + /// + /// The control is being resized. Make sure the width/height are correct. + /// + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (Wrappable) AdjustSize(Dock); + } + + /// + /// + /// + /// + /// Overridden to ensure that the buttons and the control resize properly + /// whenever the font changes. + /// + /// + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + if (IsHandleCreated) { + if (!buttonSize.IsEmpty) { + SendToolbarButtonSizeMessage(); + } + else { + AdjustSize(Dock); + ForceButtonWidths(); + } + } + } + + /// + /// + /// Sets all the button data into the ToolBar control + /// + /// + private void RealizeButtons() { + if (buttons != null) { + IntPtr ptbbuttons = IntPtr.Zero; + try { + BeginUpdate(); + // go and add in all the strings for all of our buttons + // + for (int x = 0; x < buttonCount; x++) { + if (buttons[x].Text.Length > 0) { + string addString = buttons[x].Text + '\0'.ToString(); + buttons[x].stringIndex = SendMessage(NativeMethods.TB_ADDSTRING, 0, addString); + } + else { + buttons[x].stringIndex = (IntPtr)(-1); + } + } + + // insert the buttons and set their parent pointers + // + int cb = Marshal.SizeOf(typeof(NativeMethods.TBBUTTON)); + int count = buttonCount; + ptbbuttons = Marshal.AllocHGlobal(checked(cb * count)); + + for (int x = 0; x < count; x++) { + + NativeMethods.TBBUTTON tbbutton = buttons[x].GetTBBUTTON(x); + Marshal.StructureToPtr(tbbutton, (IntPtr)(checked((long)ptbbuttons + (cb * x))), true); + buttons[x].parent = this; + } + + SendMessage(NativeMethods.TB_ADDBUTTONS, count, ptbbuttons); + + // after doing anything with the comctl ToolBar control, this + // appears to be a good idea. + // + SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + + // The win32 ToolBar control is somewhat unpredictable here. We + // have to set the button size after we've created all the + // buttons. Otherwise, we need to manually set the width of all + // the buttons so they look reasonable + // + if (!buttonSize.IsEmpty) { + SendToolbarButtonSizeMessage(); + } + else { + ForceButtonWidths(); + } + AdjustSize(Dock); + } + finally { + Marshal.FreeHGlobal(ptbbuttons); + EndUpdate(); + } + } + } + + /// + /// + /// + /// + private void RemoveAt(int index) { + buttons[index].parent = null; + buttons[index].stringIndex = (IntPtr)(-1); + buttonCount--; + + if (index < buttonCount) + System.Array.Copy(buttons, index + 1, buttons, index, buttonCount - index); + + buttons[buttonCount] = null; + } + + /// + /// + /// Resets the toolbar buttons to the minimum + /// size. + /// + private void ResetButtonSize() { + buttonSize = Size.Empty; + RecreateHandle(); + } + + /// Sends a TB_SETBUTTONSIZE message to the unmanaged control, with size arguments properly scaled. + private void SendToolbarButtonSizeMessage() { + SendMessage(NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM((int)(buttonSize.Width*currentScaleDX), (int)(buttonSize.Height*currentScaleDY))); + } + + + /// + /// + /// Overrides Control.setBoundsCore to enforce AutoSize. + /// + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + int originalHeight = height; + int originalWidth = width; + + base.SetBoundsCore(x, y, width, height, specified); + + Rectangle bounds = Bounds; + if (Dock == DockStyle.Left || Dock == DockStyle.Right) { + if ((specified & BoundsSpecified.Width) != BoundsSpecified.None) requestedSize = width; + if (AutoSize) width = PreferredWidth; + + if (width != originalWidth && Dock == DockStyle.Right) { + int deltaWidth = originalWidth - width; + x += deltaWidth; + } + + } + else { + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) requestedSize = height; + if (AutoSize) height = PreferredHeight; + + if (height != originalHeight && Dock == DockStyle.Bottom) { + int deltaHeight = originalHeight - height; + y += deltaHeight; + } + + } + + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Determines if the property needs to be persisted. + /// + private bool ShouldSerializeButtonSize() { + return !buttonSize.IsEmpty; + } + + /// + /// + /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. + /// + /// + internal void SetToolTip(ToolTip toolTip) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TB_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); + + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + s += ", Buttons.Count: " + buttonCount.ToString(CultureInfo.CurrentCulture); + if (buttonCount > 0) + s += ", Buttons[0]: " + buttons[0].ToString(); + return s; + } + + /// + /// + /// Updates all the information in the ToolBar. Tragically, the win32 + /// control is pretty flakey, and the only real choice here is to recreate + /// the handle and re-realize all the buttons. + /// + /// + internal void UpdateButtons() { + if (IsHandleCreated) { + RecreateHandle(); + } + } + + /// + /// + /// The button clicked was a dropdown button. If it has a menu specified, + /// show it now. Otherwise, fire an onButtonDropDown event. + /// + /// + private void WmNotifyDropDown(ref Message m) { + NativeMethods.NMTOOLBAR nmTB = (NativeMethods.NMTOOLBAR)m.GetLParam(typeof(NativeMethods.NMTOOLBAR)); + + ToolBarButton tbb = (ToolBarButton)buttons[nmTB.iItem]; + if (tbb == null) + throw new InvalidOperationException(SR.GetString(SR.ToolBarButtonNotFound)); + + OnButtonDropDown(new ToolBarButtonClickEventArgs(tbb)); + + Menu menu = tbb.DropDownMenu; + if (menu != null) { + NativeMethods.RECT rc = new NativeMethods.RECT(); + NativeMethods.TPMPARAMS tpm = new NativeMethods.TPMPARAMS(); + + SendMessage(NativeMethods.TB_GETRECT, nmTB.iItem, ref rc); + + if ((menu.GetType()).IsAssignableFrom(typeof(ContextMenu))) { + ((ContextMenu)menu).Show(this, new Point(rc.left, rc.bottom)); + } + else { + Menu main = menu.GetMainMenu(); + if (main != null) { + main.ProcessInitMenuPopup(menu.Handle); + } + + UnsafeNativeMethods.MapWindowPoints(new HandleRef(nmTB.hdr, nmTB.hdr.hwndFrom), NativeMethods.NullHandleRef, ref rc, 2); + + tpm.rcExclude_left = rc.left; + tpm.rcExclude_top = rc.top; + tpm.rcExclude_right = rc.right; + tpm.rcExclude_bottom = rc.bottom; + + SafeNativeMethods.TrackPopupMenuEx( + new HandleRef(menu, menu.Handle), + NativeMethods.TPM_LEFTALIGN | + NativeMethods.TPM_LEFTBUTTON | + NativeMethods.TPM_VERTICAL, + rc.left, rc.bottom, + new HandleRef(this, Handle), tpm); + } + } + } + + private void WmNotifyNeedText(ref Message m) { + NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + int commandID = (int)ttt.hdr.idFrom; + ToolBarButton tbb = (ToolBarButton) buttons[commandID]; + + if (tbb != null && tbb.ToolTipText != null) + ttt.lpszText = tbb.ToolTipText; + else + ttt.lpszText = null; + + ttt.hinst = IntPtr.Zero; + + // RightToLeft reading order + // + if (RightToLeft == RightToLeft.Yes) { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + + Marshal.StructureToPtr(ttt, m.LParam, false); + } + + private void WmNotifyNeedTextA(ref Message m) { + + NativeMethods.TOOLTIPTEXTA ttt = (NativeMethods.TOOLTIPTEXTA) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXTA)); + int commandID = (int)ttt.hdr.idFrom; + ToolBarButton tbb = (ToolBarButton) buttons[commandID]; + + if (tbb != null && tbb.ToolTipText != null) + ttt.lpszText = tbb.ToolTipText; + else + ttt.lpszText = null; + + ttt.hinst = IntPtr.Zero; + + // RightToLeft reading order + // + if (RightToLeft == RightToLeft.Yes) { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + + Marshal.StructureToPtr(ttt, m.LParam, false); + } + + // VSWhidbey 177016: Track the currently hot item since the user might be using the tab and + // arrow keys to navigate the toolbar and if that's the case, we'll need to know where to re- + // position the tooltip window when the underlying toolbar control attempts to display it. + private void WmNotifyHotItemChange(ref Message m) { + // Should we set the hot item? + NativeMethods.NMTBHOTITEM nmTbHotItem = (NativeMethods.NMTBHOTITEM)m.GetLParam(typeof(NativeMethods.NMTBHOTITEM)); + if (NativeMethods.HICF_ENTERING == (nmTbHotItem.dwFlags & NativeMethods.HICF_ENTERING)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_LEAVING == (nmTbHotItem.dwFlags & NativeMethods.HICF_LEAVING)) + this.hotItem = -1; + else if (NativeMethods.HICF_MOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_MOUSE)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_ARROWKEYS == (nmTbHotItem.dwFlags & NativeMethods.HICF_ARROWKEYS)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_ACCELERATOR == (nmTbHotItem.dwFlags & NativeMethods.HICF_ACCELERATOR)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_DUPACCEL == (nmTbHotItem.dwFlags & NativeMethods.HICF_DUPACCEL)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_RESELECT == (nmTbHotItem.dwFlags & NativeMethods.HICF_RESELECT)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_LMOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_LMOUSE)) + this.hotItem = nmTbHotItem.idNew; + else if (NativeMethods.HICF_TOGGLEDROPDOWN == (nmTbHotItem.dwFlags & NativeMethods.HICF_TOGGLEDROPDOWN)) + this.hotItem = nmTbHotItem.idNew; + } + + + /// + /// + /// + /// + private void WmReflectCommand(ref Message m) { + + int id = NativeMethods.Util.LOWORD(m.WParam); + ToolBarButton tbb = buttons[id]; + + if (tbb != null) { + ToolBarButtonClickEventArgs e = new ToolBarButtonClickEventArgs(tbb); + OnButtonClick(e); + } + + base.WndProc(ref m); + + ResetMouseEventArgs(); + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_COMMAND + NativeMethods.WM_REFLECT: + WmReflectCommand(ref m); + break; + + case NativeMethods.WM_NOTIFY: + case NativeMethods.WM_NOTIFY + NativeMethods.WM_REFLECT: + NativeMethods.NMHDR note = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (note.code) { + case NativeMethods.TTN_NEEDTEXTA: + // MSDN: + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + // Microsoft: temporarily commenting out this code to fix Pri0 bug 152233 + // UnsafeNativeMethods.SendMessage(new HandleRef(note, note.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + WmNotifyNeedTextA(ref m); + m.Result = (IntPtr)1; + return; + + case NativeMethods.TTN_NEEDTEXTW: + // On Win 98/IE 5,we still get W messages. If we ignore them, it will send the A version. + if (Marshal.SystemDefaultCharSize == 2) { + // MSDN: + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + // Microsoft: temporarily commenting out this code to fix Pri0 bug 152233 + // UnsafeNativeMethods.SendMessage(new HandleRef(note, note.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + WmNotifyNeedText(ref m); + m.Result = (IntPtr)1; + return; + } + break; + case NativeMethods.TTN_SHOW: + // VSWhidbey 177016: Prevent the tooltip from displaying in the upper left corner of the + // desktop when the control is nowhere near that location. + NativeMethods.WINDOWPLACEMENT wndPlacement = new NativeMethods.WINDOWPLACEMENT(); + int nRet = UnsafeNativeMethods.GetWindowPlacement(new HandleRef(null, note.hwndFrom), ref wndPlacement); + + // Is this tooltip going to be positioned in the upper left corner of the display, + // but nowhere near the toolbar button? + if (wndPlacement.rcNormalPosition_left == 0 && + wndPlacement.rcNormalPosition_top == 0 && + this.hotItem != -1) { + + // Assume that we're going to vertically center the tooltip on the right edge of the current + // hot item. + + // Where is the right edge of the current hot item? + int buttonRight = 0; + for(int idx = 0; idx <= this.hotItem; idx++) { + // How wide is the item at this index? (It could be a separator, and therefore a different width.) + buttonRight += buttonsCollection[idx].GetButtonWidth(); + } + + // Where can we place this tooltip so that it will be completely visible on the current display? + int tooltipWidth = wndPlacement.rcNormalPosition_right - wndPlacement.rcNormalPosition_left; + int tooltipHeight = wndPlacement.rcNormalPosition_bottom - wndPlacement.rcNormalPosition_top; + + // We'll need screen coordinates of this position for setting the tooltip's position + int x = this.Location.X + buttonRight + 1; + int y = this.Location.Y + (this.ButtonSize.Height / 2); + NativeMethods.POINT leftTop = new NativeMethods.POINT(x, y); + UnsafeNativeMethods.ClientToScreen(new HandleRef(this, this.Handle), leftTop); + + // Will the tooltip bleed off the top? + if (leftTop.y < SystemInformation.WorkingArea.Y) { + // Reposition the tooltip to be displayed below the button + leftTop.y += (this.ButtonSize.Height / 2) + 1; + } + + // Will the tooltip bleed off the bottom? + if (leftTop.y + tooltipHeight > SystemInformation.WorkingArea.Height) { + // Reposition the tooltip to be displayed above the button + leftTop.y -= ((this.ButtonSize.Height / 2) + tooltipHeight + 1); + } + + // Will the tooltip bleed off the right edge? + if (leftTop.x + tooltipWidth > SystemInformation.WorkingArea.Right) { + // Move the tooltip far enough left that it will display in the working area + leftTop.x -= (this.ButtonSize.Width + tooltipWidth + 2); + } + + SafeNativeMethods.SetWindowPos(new HandleRef(null, note.hwndFrom), NativeMethods.NullHandleRef, leftTop.x, leftTop.y, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE); + m.Result = (IntPtr)1; + return; + } + break; + + case NativeMethods.TBN_HOTITEMCHANGE: + WmNotifyHotItemChange(ref m); + break; + + + case NativeMethods.TBN_QUERYINSERT: + m.Result = (IntPtr)1; + break; + + case NativeMethods.TBN_DROPDOWN: + WmNotifyDropDown(ref m); + break; + } + break; + + } + base.WndProc(ref m); + } + + /// + /// + /// Encapsulates a collection of controls for use by the + /// class. + /// + public class ToolBarButtonCollection : IList { + + private ToolBar owner; + private bool suspendUpdate; + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + + /// + /// + /// Initializes a new instance of the class and assigns it to the specified toolbar. + /// + public ToolBarButtonCollection(ToolBar owner) { + this.owner = owner; + } + + /// + /// + /// Gets or sets the toolbar button at the specified indexed location in the + /// toolbar button collection. + /// + public virtual ToolBarButton this[int index] { + get { + if (index < 0 || ((owner.buttons != null) && (index >= owner.buttonCount))) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + return owner.buttons[index]; + } + set { + + // Sanity check parameters + // + if (index < 0 || ((owner.buttons != null) && index >= owner.buttonCount)) { + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + } + if (value == null) { + throw new ArgumentNullException("value"); + } + + owner.InternalSetButton(index, value, true, true); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is ToolBarButton) { + this[index] = (ToolBarButton)value; + } + else { + throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"value"); + } + } + } + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ToolBarButton this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + /// + /// + /// Gets the number of buttons in the toolbar button collection. + /// + [Browsable(false)] + public int Count { + get { + return owner.buttonCount; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Adds a new toolbar button to + /// the end of the toolbar button collection. + /// + public int Add(ToolBarButton button) { + + int index = owner.InternalAddButton(button); + + if (!suspendUpdate) { + owner.UpdateButtons(); + } + + return index; + } + + /// + /// + /// [To be supplied.] + /// + public int Add(string text) { + ToolBarButton button = new ToolBarButton(text); + return Add(button); + } + + /// + /// + int IList.Add(object button) { + if (button is ToolBarButton) { + return Add((ToolBarButton)button); + } + else { + throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"button"); + } + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ToolBarButton[] buttons) { + if (buttons == null) { + throw new ArgumentNullException("buttons"); + } + try { + suspendUpdate = true; + foreach(ToolBarButton button in buttons) { + Add(button); + } + } + finally { + suspendUpdate = false; + owner.UpdateButtons(); + } + } + + + /// + /// + /// Removes + /// all buttons from the toolbar button collection. + /// + public void Clear() { + + if (owner.buttons == null) { + return; + } + + for (int x = owner.buttonCount; x > 0; x--) { + if (owner.IsHandleCreated) { + owner.SendMessage(NativeMethods.TB_DELETEBUTTON, x - 1, 0); + } + owner.RemoveAt(x - 1); + } + + owner.buttons = null; + owner.buttonCount = 0; + if (!owner.Disposing) { + owner.UpdateButtons(); + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ToolBarButton button) { + return IndexOf(button) != -1; + } + + /// + /// + bool IList.Contains(object button) { + if (button is ToolBarButton) { + return Contains((ToolBarButton)button); + } + else { + return false; + } + } + + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + + /// + /// + void ICollection.CopyTo(Array dest, int index) { + if (owner.buttonCount > 0) { + System.Array.Copy(owner.buttons, 0, dest, index, owner.buttonCount); + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ToolBarButton button) { + for(int index=0; index < Count; ++index) { + if (this[index] == button) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object button) { + if (button is ToolBarButton) { + return IndexOf((ToolBarButton)button); + } + else { + return -1; + } + } + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, ToolBarButton button) { + owner.InsertButton(index, button); + } + + /// + /// + void IList.Insert(int index, object button) { + if (button is ToolBarButton) { + Insert(index, (ToolBarButton)button); + } + else { + throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"button"); + } + } + + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + /// + /// + /// Removes + /// a given button from the toolbar button collection. + /// + public void RemoveAt(int index) { + int count = (owner.buttons == null) ? 0 : owner.buttonCount; + + if (index < 0 || index >= count) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture))); + + if (owner.IsHandleCreated) { + owner.SendMessage(NativeMethods.TB_DELETEBUTTON, index, 0); + } + + owner.RemoveAt(index); + owner.UpdateButtons(); + + } + + + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(ToolBarButton button) { + int index = IndexOf(button); + if (index != -1) { + RemoveAt(index); + } + } + + /// + /// + void IList.Remove(object button) { + if (button is ToolBarButton) { + Remove((ToolBarButton)button); + } + } + + /// + /// + /// Returns an enumerator that can be used to iterate + /// through the toolbar button collection. + /// + public IEnumerator GetEnumerator() { + return new WindowsFormsUtils.ArraySubsetEnumerator(owner.buttons, owner.buttonCount); + } + } + + } +} + + + + diff --git a/WindowsForms/Managed/System/WinForms/ToolBarAppearance.cs b/WindowsForms/Managed/System/WinForms/ToolBarAppearance.cs new file mode 100644 index 000000000..8a241f5a7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarAppearance.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Specifies the type of toolbar to display. + /// + /// + public enum ToolBarAppearance { + + /// + /// + /// + /// The + /// toolbar and buttons appear as standard three dimensional controls. + /// + /// + Normal = 0, + + /// + /// + /// + /// The toolbar and buttons appear flat, but the buttons change to three + /// dimensional as the mouse pointer moves over them. + /// + /// + Flat = 1, + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBarButton.cs b/WindowsForms/Managed/System/WinForms/ToolBarButton.cs new file mode 100644 index 000000000..3aa0dc155 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarButton.cs @@ -0,0 +1,770 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Drawing; + using System.Text; + using System.Drawing.Design; + using Marshal = System.Runtime.InteropServices.Marshal; + using System.Windows.Forms; + using Microsoft.Win32; + using System.Globalization; + + + /// + /// + /// + /// Represents a Windows toolbar button. + /// + [ + Designer("System.Windows.Forms.Design.ToolBarButtonDesigner, " + AssemblyRef.SystemDesign), + DefaultProperty("Text"), + ToolboxItem(false), + DesignTimeVisible(false), + ] + public class ToolBarButton : Component { + + string text; + string name = null; + string tooltipText; + bool enabled = true; + bool visible = true; + bool pushed = false; + bool partialPush = false; + private int commandId = -1; // the cached command id of the button. + private ToolBarButtonImageIndexer imageIndexer; + + ToolBarButtonStyle style = ToolBarButtonStyle.PushButton; + + object userData; + + // These variables below are used by the ToolBar control to help + // it manage some information about us. + + /// + /// + /// If this button has a string, what it's index is in the ToolBar's + /// internal list of strings. Needs to be package protected. + /// + /// + internal IntPtr stringIndex = (IntPtr)(-1); + + /// + /// + /// Our parent ToolBar control. + /// + /// + internal ToolBar parent; + + /// + /// + /// For DropDown buttons, we can optionally show a + /// context menu when the button is dropped down. + /// + internal Menu dropDownMenu = null; + + /// + /// + /// Initializes a new instance of the class. + /// + public ToolBarButton() { + } + + /// + /// + /// [To be supplied.] + /// + public ToolBarButton(string text) : base() { + this.Text = text; + } + + + + // We need a special way to defer to the ToolBar's image + // list for indexing purposes. + internal class ToolBarButtonImageIndexer : ImageList.Indexer { + private ToolBarButton owner; + + + + public ToolBarButtonImageIndexer(ToolBarButton button) { + owner = button; + } + + public override ImageList ImageList { + get { + if ((owner != null) && (owner.parent != null)) { + return owner.parent.ImageList; + } + return null; + } + set { Debug.Assert(false, "We should never set the image list"); } + } + } + + internal ToolBarButtonImageIndexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ToolBarButtonImageIndexer(this); + } + + return imageIndexer; + } + } + + /// + /// + /// + /// Indicates the menu to be displayed in + /// the drop-down toolbar button. + /// + [ + DefaultValue(null), + TypeConverterAttribute(typeof(ReferenceConverter)), + SRDescription(SR.ToolBarButtonMenuDescr) + ] + public Menu DropDownMenu { + get { + return dropDownMenu; + } + + set { + //The dropdownmenu must be of type ContextMenu, Main & Items are invalid. + // + if (value != null && !(value is ContextMenu)) { + throw new ArgumentException(SR.GetString(SR.ToolBarButtonInvalidDropDownMenuType)); + } + dropDownMenu = value; + } + } + + /// + /// + /// Indicates whether the button is enabled or not. + /// + [ + DefaultValue(true), + Localizable(true), + SRDescription(SR.ToolBarButtonEnabledDescr) + ] + public bool Enabled { + get { + return enabled; + } + + set { + if (enabled != value) { + + enabled = value; + + if (parent != null && parent.IsHandleCreated) { + parent.SendMessage(NativeMethods.TB_ENABLEBUTTON, FindButtonIndex(), + enabled ? 1 : 0); + } + } + } + } + + /// + /// + /// Indicates the index + /// value of the image assigned to the button. + /// + [ + TypeConverterAttribute(typeof(ImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(-1), + RefreshProperties(RefreshProperties.Repaint), + Localizable(true), + SRDescription(SR.ToolBarButtonImageIndexDescr) + ] + public int ImageIndex { + get { + return ImageIndexer.Index; + } + set { + if (ImageIndexer.Index != value) { + if (value < -1) + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", (value).ToString(CultureInfo.CurrentCulture), -1)); + + ImageIndexer.Index = value; + UpdateButton(false); + } + } + } + + /// + /// + /// Indicates the index + /// value of the image assigned to the button. + /// + [ + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(""), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ToolBarButtonImageIndexDescr) + ] + public string ImageKey { + get { + return ImageIndexer.Key; + } + set { + if (ImageIndexer.Key != value) { + ImageIndexer.Key = value; + UpdateButton(false); + } + } + } + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control - however this + /// property has no bearing on the runtime aspects of this control. + /// + [Browsable(false)] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this, name); + } + set { + if (value == null || value.Length == 0) { + name = null; + } + else { + name = value; + } + if(Site!= null) { + Site.Name = name; + } + } + } + + + /// + /// + /// Indicates the toolbar control that the toolbar button is assigned to. This property is + /// read-only. + /// + [ + Browsable(false), + ] + public ToolBar Parent { + get { + return parent; + } + } + + /// + /// + /// + /// Indicates whether a toggle-style toolbar button + /// is partially pushed. + /// + [ + DefaultValue(false), + SRDescription(SR.ToolBarButtonPartialPushDescr) + ] + public bool PartialPush { + get { + if (parent == null || !parent.IsHandleCreated) + return partialPush; + else { + if ((int)parent.SendMessage(NativeMethods.TB_ISBUTTONINDETERMINATE, FindButtonIndex(), 0) != 0) + partialPush = true; + else + partialPush = false; + + return partialPush; + } + } + set { + if (partialPush != value) { + partialPush = value; + UpdateButton(false); + } + } + } + + /// + /// + /// Indicates whether a toggle-style toolbar button is currently in the pushed state. + /// + [ + DefaultValue(false), + SRDescription(SR.ToolBarButtonPushedDescr) + ] + public bool Pushed { + get { + if (parent == null || !parent.IsHandleCreated) + return pushed; + else { + return GetPushedState(); + } + } + set { + if (value != Pushed) { // Getting property Pushed updates pushed member variable + pushed = value; + UpdateButton(false, false, false); + } + } + } + + /// + /// + /// Indicates the bounding rectangle for a toolbar button. This property is + /// read-only. + /// + public Rectangle Rectangle { + get { + if (parent != null) { + NativeMethods.RECT rc = new NativeMethods.RECT(); + UnsafeNativeMethods.SendMessage(new HandleRef(parent, parent.Handle), NativeMethods.TB_GETRECT, FindButtonIndex(), ref rc); + return Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + return Rectangle.Empty; + } + } + + /// + /// + /// Indicates the style of the + /// toolbar button. + /// + [ + DefaultValue(ToolBarButtonStyle.PushButton), + SRDescription(SR.ToolBarButtonStyleDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public ToolBarButtonStyle Style { + get { + return style; + } + set { + //valid values are 0x1 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolBarButtonStyle.PushButton, (int)ToolBarButtonStyle.DropDownButton)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolBarButtonStyle)); + } + if (style == value) return; + style = value; + UpdateButton(true); + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// Indicates the text that is displayed on the toolbar button. + /// + [ + Localizable(true), + DefaultValue(""), + SRDescription(SR.ToolBarButtonTextDescr) + ] + public string Text { + get { + return(text == null) ? "" : text; + } + set { + if (String.IsNullOrEmpty(value)) { + value = null; + } + + if ( (value == null && text != null) || + (value != null && (text == null || !text.Equals(value)))) { + text = value; + // 94943 VSWhidbey Adding a mnemonic requires a handle recreate. + UpdateButton(WindowsFormsUtils.ContainsMnemonic(text), true, true); + } + } + } + + + + /// + /// + /// + /// Indicates + /// the text that appears as a tool tip for a control. + /// + [ + Localizable(true), + DefaultValue(""), + SRDescription(SR.ToolBarButtonToolTipTextDescr) + ] + public string ToolTipText { + get { + return tooltipText == null ? "" : tooltipText; + } + set { + tooltipText = value; + } + } + + /// + /// + /// + /// Indicates whether the toolbar button + /// is visible. + /// + [ + DefaultValue(true), + Localizable(true), + SRDescription(SR.ToolBarButtonVisibleDescr) + ] + public bool Visible { + get { + return visible; + } + set { + if (visible != value) { + visible = value; + UpdateButton(false); + } + } + } + + /// + /// + /// This is somewhat nasty -- by default, the windows ToolBar isn't very + /// clever about setting the width of buttons, and has a very primitive + /// algorithm that doesn't include for things like drop down arrows, etc. + /// We need to do a bunch of work here to get all the widths correct. Ugh. + /// + /// + internal short Width { + get { + Debug.Assert(parent != null, "Parent should be non-null when button width is requested"); + + int width = 0; + ToolBarButtonStyle style = Style; + + Size edge = SystemInformation.Border3DSize; + if (style != ToolBarButtonStyle.Separator) { + + // COMPAT: this will force handle creation. + // we could use the measurement graphics, but it looks like this has been like this since Everett. + using (Graphics g = this.parent.CreateGraphicsInternal()) { + + Size buttonSize = this.parent.buttonSize; + if (!(buttonSize.IsEmpty)) { + width = buttonSize.Width; + } + else { + if (this.parent.ImageList != null || !String.IsNullOrEmpty(Text)) { + Size imageSize = this.parent.ImageSize; + Size textSize = Size.Ceiling(g.MeasureString(Text, parent.Font)); + if (this.parent.TextAlign == ToolBarTextAlign.Right) { + if (textSize.Width == 0) + width = imageSize.Width + edge.Width * 4; + else + width = imageSize.Width + textSize.Width + edge.Width * 6; + } + else { + if (imageSize.Width > textSize.Width) + width = imageSize.Width + edge.Width * 4; + else + width = textSize.Width + edge.Width * 4; + } + if (style == ToolBarButtonStyle.DropDownButton && this.parent.DropDownArrows) { + width += ToolBar.DDARROW_WIDTH; + } + } + else + width = this.parent.ButtonSize.Width; + } + } + } + else { + width = edge.Width * 2; + } + + return(short)width; + } + + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (parent != null) { + int index = FindButtonIndex(); + if (index != -1) { + parent.Buttons.RemoveAt(index); + } + } + } + base.Dispose(disposing); + } + + /// + /// + /// Finds out index in the parent. + /// + /// + private int FindButtonIndex() { + for (int x = 0; x < parent.Buttons.Count; x++) { + if (parent.Buttons[x] == this) { + return x; + } + } + return -1; + } + + // VSWhidbey 177016: This is necessary to get the width of the buttons in the toolbar, + // including the width of separators, so that we can accurately position the tooltip adjacent + // to the currently hot button when the user uses keyboard navigation to access the toolbar. + internal int GetButtonWidth() { + + // Assume that this button is the same width as the parent's ButtonSize's Width + int buttonWidth = Parent.ButtonSize.Width; + + NativeMethods.TBBUTTONINFO button = new NativeMethods.TBBUTTONINFO(); + button.cbSize = Marshal.SizeOf(typeof(NativeMethods.TBBUTTONINFO)); + button.dwMask = NativeMethods.TBIF_SIZE; + + int buttonID = (int)UnsafeNativeMethods.SendMessage(new HandleRef(Parent, Parent.Handle), NativeMethods.TB_GETBUTTONINFO, commandId, ref button); + if (buttonID != -1) { + buttonWidth = button.cx; + } + + return buttonWidth; + } + + private bool GetPushedState() + { + if ((int)parent.SendMessage(NativeMethods.TB_ISBUTTONCHECKED, FindButtonIndex(), 0) != 0) { + pushed = true; + } + else { + pushed = false; + } + + return pushed; + } + + /// + /// + /// Returns a TBBUTTON object that represents this ToolBarButton. + /// + internal NativeMethods.TBBUTTON GetTBBUTTON(int commandId) { + + NativeMethods.TBBUTTON button = new NativeMethods.TBBUTTON(); + + button.iBitmap = ImageIndexer.ActualIndex; + + // set up the state of the button + // + button.fsState = 0; + if (enabled) button.fsState |= NativeMethods.TBSTATE_ENABLED; + if (partialPush && style == ToolBarButtonStyle.ToggleButton) button.fsState |= NativeMethods.TBSTATE_INDETERMINATE; + if (pushed) button.fsState |= NativeMethods.TBSTATE_CHECKED; + if (!visible) button.fsState |= NativeMethods.TBSTATE_HIDDEN; + + // set the button style + // + switch (style) { + case ToolBarButtonStyle.PushButton: + button.fsStyle = NativeMethods.TBSTYLE_BUTTON; + break; + case ToolBarButtonStyle.ToggleButton: + button.fsStyle = NativeMethods.TBSTYLE_CHECK; + break; + case ToolBarButtonStyle.Separator: + button.fsStyle = NativeMethods.TBSTYLE_SEP; + break; + case ToolBarButtonStyle.DropDownButton: + button.fsStyle = NativeMethods.TBSTYLE_DROPDOWN; + break; + + } + + button.dwData = (IntPtr)0; + button.iString = this.stringIndex; + this.commandId = commandId; + button.idCommand = commandId; + + return button; + } + + /// + /// + /// Returns a TBBUTTONINFO object that represents this ToolBarButton. + /// + internal NativeMethods.TBBUTTONINFO GetTBBUTTONINFO(bool updateText, int newCommandId) { + + NativeMethods.TBBUTTONINFO button = new NativeMethods.TBBUTTONINFO(); + button.cbSize = Marshal.SizeOf(typeof(NativeMethods.TBBUTTONINFO)); + button.dwMask = NativeMethods.TBIF_IMAGE + | NativeMethods.TBIF_STATE | NativeMethods.TBIF_STYLE; + + // Comctl on Win98 interprets null strings as empty strings, which forces + // the button to leave space for text. The only workaround is to avoid having comctl + // update the text. + if (updateText) { + button.dwMask |= NativeMethods.TBIF_TEXT; + } + + button.iImage = ImageIndexer.ActualIndex; + + if (newCommandId != commandId) { + commandId = newCommandId; + button.idCommand = newCommandId; + button.dwMask |= NativeMethods.TBIF_COMMAND; + } + + // set up the state of the button + // + button.fsState = 0; + if (enabled) button.fsState |= NativeMethods.TBSTATE_ENABLED; + if (partialPush && style == ToolBarButtonStyle.ToggleButton) button.fsState |= NativeMethods.TBSTATE_INDETERMINATE; + if (pushed) button.fsState |= NativeMethods.TBSTATE_CHECKED; + if (!visible) button.fsState |= NativeMethods.TBSTATE_HIDDEN; + + // set the button style + // + switch (style) { + case ToolBarButtonStyle.PushButton: + button.fsStyle = NativeMethods.TBSTYLE_BUTTON; + break; + case ToolBarButtonStyle.ToggleButton: + button.fsStyle = NativeMethods.TBSTYLE_CHECK; + break; + case ToolBarButtonStyle.Separator: + button.fsStyle = NativeMethods.TBSTYLE_SEP; + break; + } + + + if (text == null) { + button.pszText = Marshal.StringToHGlobalAuto("\0\0"); + } + else { + string textValue = this.text; + PrefixAmpersands(ref textValue); + button.pszText = Marshal.StringToHGlobalAuto(textValue); + } + + return button; + } + + private void PrefixAmpersands(ref string value) { + // Due to a comctl32 problem, ampersands underline the next letter in the + // text string, but the accelerators don't work. + // So in this function, we prefix ampersands with another ampersand + // so that they actually appear as ampersands. + // + + // Sanity check parameter + // + if (value == null || value.Length == 0) { + return; + } + + // If there are no ampersands, we don't need to do anything here + // + if (value.IndexOf('&') < 0) { + return; + } + + // Insert extra ampersands + // + StringBuilder newString = new StringBuilder(); + for(int i=0; i < value.Length; ++i) { + if (value[i] == '&') { + if (i < value.Length - 1 && value[i+1] == '&') { + ++i; // Skip the second ampersand + } + newString.Append("&&"); + } + else { + newString.Append(value[i]); + } + } + + value = newString.ToString(); + } + + /// + /// + /// + /// + public override string ToString() { + return "ToolBarButton: " + Text + ", Style: " + Style.ToString("G"); + } + + /// + /// + /// When a button property changes and the parent control is created, + /// we need to make sure it gets the new button information. + /// If Text was changed, call the next overload. + /// + internal void UpdateButton(bool recreate) { + UpdateButton(recreate, false, true); + } + + /// + /// + /// When a button property changes and the parent control is created, + /// we need to make sure it gets the new button information. + /// + private void UpdateButton(bool recreate, bool updateText, bool updatePushedState) { + // It looks like ToolBarButtons with a DropDownButton tend to + // lose the DropDownButton very easily - so we need to recreate + // the button each time it changes just to be sure. + // + if (style == ToolBarButtonStyle.DropDownButton && parent != null && parent.DropDownArrows) { + recreate = true; + } + + // we just need to get the Pushed state : this asks the Button its states and sets + // the private member "pushed" to right value.. + + // this member is used in "InternalSetButton" which calls GetTBBUTTONINFO(bool updateText) + // the GetButtonInfo method uses the "pushed" variable.. + + //rather than setting it ourselves .... we asks the button to set it for us.. + if (updatePushedState && parent != null && parent.IsHandleCreated) { + GetPushedState(); + } + if (parent != null) { + int index = FindButtonIndex(); + if (index != -1) + parent.InternalSetButton(index, this, recreate, updateText); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEvent.cs b/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEvent.cs new file mode 100644 index 000000000..46f17db08 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEvent.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Drawing; + using System.ComponentModel; + using Microsoft.Win32; + + /// + /// + /// + /// Provides data for the + /// event. + /// + /// + public class ToolBarButtonClickEventArgs : EventArgs { + + private ToolBarButton button; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public ToolBarButtonClickEventArgs(ToolBarButton button) { + this.button = button; + } + + /// + /// + /// + /// Specifies the + /// that was clicked. + /// + /// + public ToolBarButton Button { + get { return button;} + set { button = value;} + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEventHandler.cs new file mode 100644 index 000000000..3b968438e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarButtonClickEventHandler.cs @@ -0,0 +1,16 @@ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the event of a + /// . + /// + /// + public delegate void ToolBarButtonClickEventHandler(object sender, ToolBarButtonClickEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBarButtonStyle.cs b/WindowsForms/Managed/System/WinForms/ToolBarButtonStyle.cs new file mode 100644 index 000000000..99f5e3596 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarButtonStyle.cs @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// Specifies the button style within a toolbar. + /// + public enum ToolBarButtonStyle { + + /// + /// + /// + /// A + /// standard, three-dimensional + /// button. + /// + /// + PushButton = 1, + + /// + /// + /// + /// A toggle button that appears sunken when clicked + /// and retains the sunken appearance until + /// clicked again. + /// + /// + ToggleButton = 2, + + /// + /// + /// + /// A space or + /// line between toolbar buttons. The appearance depends on the + /// value of the + /// property. + /// + /// + Separator = 3, + + /// + /// + /// + /// A drop down control that displays a menu or other window + /// when + /// clicked. + /// + /// + DropDownButton = 4, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolBarTextAlign.cs b/WindowsForms/Managed/System/WinForms/ToolBarTextAlign.cs new file mode 100644 index 000000000..c8af03db3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolBarTextAlign.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies + /// the alignment of text on the toolbar button control. + /// + /// + public enum ToolBarTextAlign { + + /// + /// + /// + /// The text + /// is aligned underneath the toolbar button image. + /// + /// + Underneath = 0, + + /// + /// + /// + /// The text + /// is aligned to the right of the toolbar button image. + /// + /// + Right = 1, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStrip.cs b/WindowsForms/Managed/System/WinForms/ToolStrip.cs new file mode 100644 index 000000000..43c1af19d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStrip.cs @@ -0,0 +1,5574 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Configuration; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Threading; + using System.Windows.Forms.Layout; + using System.ComponentModel.Design.Serialization; + using System.Drawing.Drawing2D; + using System.Text.RegularExpressions; + using System.Text; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// Summary of ToolStrip. + /// + + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [DesignerSerializer("System.Windows.Forms.Design.ToolStripCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)] + [Designer("System.Windows.Forms.Design.ToolStripDesigner, " + AssemblyRef.SystemDesign)] + [DefaultProperty("Items")] + [SRDescription(SR.DescriptionToolStrip)] + [DefaultEvent("ItemClicked")] + + + public class ToolStrip : System.Windows.Forms.ScrollableControl, + IArrangedElement, + ISupportToolStripPanel + { + + + private static Size onePixel = new Size(1,1); + internal static Point InvalidMouseEnter = new Point(Int32.MaxValue, Int32.MaxValue); + + private ToolStripItemCollection toolStripItemCollection = null; + private ToolStripOverflowButton toolStripOverflowButton = null; + private ToolStripGrip toolStripGrip = null; + private ToolStripItemCollection displayedItems = null; + private ToolStripItemCollection overflowItems = null; + private ToolStripDropTargetManager dropTargetManager = null; + private IntPtr hwndThatLostFocus = IntPtr.Zero; + private ToolStripItem lastMouseActiveItem = null; + private ToolStripItem lastMouseDownedItem = null; + private LayoutEngine layoutEngine = null; + private ToolStripLayoutStyle layoutStyle = ToolStripLayoutStyle.StackWithOverflow; + private LayoutSettings layoutSettings = null; + private Rectangle lastInsertionMarkRect = Rectangle.Empty; + private ImageList imageList = null; + private ToolStripGripStyle toolStripGripStyle = ToolStripGripStyle.Visible; + private ISupportOleDropSource itemReorderDropSource = null; + private IDropTarget itemReorderDropTarget = null; + private int toolStripState = 0; + private bool showItemToolTips = false; + private MouseHoverTimer mouseHoverTimer = null; + private ToolStripItem currentlyActiveTooltipItem; + private NativeWindow dropDownOwnerWindow; + private byte mouseDownID = 0; // NEVER use this directly from another class, 0 should never be returned to another class. + + private Orientation orientation = Orientation.Horizontal; + + private ArrayList activeDropDowns = new ArrayList(1); + private ToolStripRenderer renderer = null; + private Type currentRendererType = typeof(System.Type); + private Hashtable shortcuts = null; + private Stack mergeHistoryStack = null; + private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default; + private Size largestDisplayedItemSize = Size.Empty; + private CachedItemHdcInfo cachedItemHdcInfo = null; + private bool alreadyHooked = false; + + private Size imageScalingSize; + private const int ICON_DIMENSION = 16; + private static int iconWidth = ICON_DIMENSION; + private static int iconHeight = ICON_DIMENSION; + + private Font defaultFont = null; + private RestoreFocusMessageFilter restoreFocusFilter; + + private bool layoutRequired = false; + + private static readonly Padding defaultPadding = new Padding(0, 0, 1, 0); + private static readonly Padding defaultGripMargin = new Padding(2); + private Padding scaledDefaultPadding = defaultPadding; + private Padding scaledDefaultGripMargin = defaultGripMargin; + + + private Point mouseEnterWhenShown = InvalidMouseEnter; + + private const int INSERTION_BEAM_WIDTH = 6; + + internal static int insertionBeamWidth = INSERTION_BEAM_WIDTH; + + private static readonly object EventPaintGrip = new object(); + private static readonly object EventLayoutCompleted = new object(); + private static readonly object EventItemAdded = new object(); + private static readonly object EventItemRemoved = new object(); + private static readonly object EventLayoutStyleChanged = new object(); + private static readonly object EventRendererChanged = new object(); + private static readonly object EventItemClicked = new object(); + private static readonly object EventLocationChanging = new object(); + private static readonly object EventBeginDrag = new object(); + private static readonly object EventEndDrag = new object(); + + private static readonly int PropBindingContext = PropertyStore.CreateKey(); + private static readonly int PropTextDirection = PropertyStore.CreateKey(); + private static readonly int PropToolTip = PropertyStore.CreateKey(); + private static readonly int PropToolStripPanelCell = PropertyStore.CreateKey(); + + internal const int STATE_CANOVERFLOW = 0x00000001; + internal const int STATE_ALLOWITEMREORDER = 0x00000002; + internal const int STATE_DISPOSINGITEMS = 0x00000004; + internal const int STATE_MENUAUTOEXPAND = 0x00000008; + internal const int STATE_MENUAUTOEXPANDDEFAULT = 0x00000010; + internal const int STATE_SCROLLBUTTONS = 0x00000020; + internal const int STATE_USEDEFAULTRENDERER = 0x00000040; + internal const int STATE_ALLOWMERGE = 0x00000080; + internal const int STATE_RAFTING = 0x00000100; + internal const int STATE_STRETCH = 0x00000200; + internal const int STATE_LOCATIONCHANGING = 0x00000400; + internal const int STATE_DRAGGING = 0x00000800; + internal const int STATE_HASVISIBLEITEMS = 0x00001000; + internal const int STATE_SUSPENDCAPTURE = 0x00002000; + internal const int STATE_LASTMOUSEDOWNEDITEMCAPTURE = 0x00004000; + internal const int STATE_MENUACTIVE = 0x00008000; + + +#if DEBUG + internal static readonly TraceSwitch SelectionDebug = new TraceSwitch("SelectionDebug", "Debug ToolStrip Selection code"); + internal static readonly TraceSwitch DropTargetDebug = new TraceSwitch("DropTargetDebug", "Debug ToolStrip Drop code"); + internal static readonly TraceSwitch LayoutDebugSwitch = new TraceSwitch("Layout debug", "Debug ToolStrip layout code"); + internal static readonly TraceSwitch MouseActivateDebug = new TraceSwitch("ToolStripMouseActivate", "Debug ToolStrip WM_MOUSEACTIVATE code"); + internal static readonly TraceSwitch MergeDebug = new TraceSwitch("ToolStripMergeDebug", "Debug toolstrip merging"); + internal static readonly TraceSwitch SnapFocusDebug = new TraceSwitch("SnapFocus", "Debug snapping/restoration of focus"); + internal static readonly TraceSwitch FlickerDebug = new TraceSwitch("FlickerDebug", "Debug excessive calls to Invalidate()"); + internal static readonly TraceSwitch ItemReorderDebug = new TraceSwitch("ItemReorderDebug", "Debug excessive calls to Invalidate()"); + internal static readonly TraceSwitch MDIMergeDebug = new TraceSwitch("MDIMergeDebug", "Debug toolstrip MDI merging"); + internal static readonly TraceSwitch MenuAutoExpandDebug = new TraceSwitch("MenuAutoExpand", "Debug menu auto expand"); + internal static readonly TraceSwitch ControlTabDebug = new TraceSwitch("ControlTab", "Debug ToolStrip Control+Tab selection"); +#else + internal static readonly TraceSwitch SelectionDebug; + internal static readonly TraceSwitch DropTargetDebug; + internal static readonly TraceSwitch LayoutDebugSwitch; + internal static readonly TraceSwitch MouseActivateDebug; + internal static readonly TraceSwitch MergeDebug; + internal static readonly TraceSwitch SnapFocusDebug; + internal static readonly TraceSwitch FlickerDebug; + internal static readonly TraceSwitch ItemReorderDebug; + internal static readonly TraceSwitch MDIMergeDebug; + internal static readonly TraceSwitch MenuAutoExpandDebug; + internal static readonly TraceSwitch ControlTabDebug; +#endif + + private delegate void BooleanMethodInvoker(bool arg); + internal Action rescaleConstsCallbackDelegate; + + /// + /// + /// Summary of ToolStrip. + /// + public ToolStrip() { + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + ToolStripManager.CurrentDpi = DeviceDpi; + defaultFont = ToolStripManager.DefaultFont; + iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi); + iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi); + insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, DeviceDpi); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, DeviceDpi); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, DeviceDpi); + } + else if (DpiHelper.IsScalingRequired) { + iconWidth = DpiHelper.LogicalToDeviceUnitsX(ICON_DIMENSION); + iconHeight = DpiHelper.LogicalToDeviceUnitsY(ICON_DIMENSION); + if (DpiHelper.EnableToolStripHighDpiImprovements) { + insertionBeamWidth = DpiHelper.LogicalToDeviceUnitsX(INSERTION_BEAM_WIDTH); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin); + } + } + + imageScalingSize = new Size(iconWidth, iconHeight); + + SuspendLayout(); + this.CanOverflow = true; + this.TabStop = false; + this.MenuAutoExpand = false; + SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.SupportsTransparentBackColor, true); + + SetStyle(ControlStyles.Selectable, false); + SetToolStripState(STATE_USEDEFAULTRENDERER | STATE_ALLOWMERGE, true); + + SetState2(STATE2_MAINTAINSOWNCAPTUREMODE // VSWhidbey 458967: a toolstrip does not take capture on MouseDown. + | STATE2_USEPREFERREDSIZECACHE, // this class overrides GetPreferredSizeCore, let Control automatically cache the result + true); + + //add a weak ref link in ToolstripManager + ToolStripManager.ToolStrips.Add(this); + + layoutEngine = new ToolStripSplitStackLayout(this); + this.Dock = DefaultDock; + this.AutoSize = true; + this.CausesValidation = false; + Size defaultSize = DefaultSize; + SetAutoSizeMode(AutoSizeMode.GrowAndShrink); + this.ShowItemToolTips = DefaultShowItemToolTips; + ResumeLayout(true); + } + + public ToolStrip(params ToolStripItem[] items) : this() { + Items.AddRange(items); + } + + internal ArrayList ActiveDropDowns { + get { return activeDropDowns; } + } + + // returns true when entered into menu mode through this toolstrip/menustrip + // this is only really supported for menustrip active event, but to prevent casting everywhere... + internal virtual bool KeyboardActive { + get { return GetToolStripState(STATE_MENUACTIVE); } + set { SetToolStripState(STATE_MENUACTIVE, value);} + } + + // This is only for use in determining whether to show scroll bars on + // ToolStripDropDownMenus. No one else should be using it for anything. + internal virtual bool AllItemsVisible { + get { + return true; + } + set { + // we do nothing in repsonse to a set, since we calculate the value above. + } + } + + [DefaultValue(true), Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + if (IsInToolStripPanel && base.AutoSize && !value) { + // VSWhidbey 351717 - restoring the bounds can change the location of the toolstrip - + // which would join it to a new row. Set the specified bounds to the new location to + // prevent this. + Rectangle bounds = CommonProperties.GetSpecifiedBounds(this); + bounds.Location = this.Location; + CommonProperties.UpdateSpecifiedBounds(this, bounds.X, bounds.Y, bounds.Width, bounds.Height, BoundsSpecified.Location); + + } + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool AutoScroll { + get { + return base.AutoScroll; + } + set { + throw new NotSupportedException(SR.GetString(SR.ToolStripDoesntSupportAutoScroll)); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMargin { + get { + return base.AutoScrollMargin; + } + set { + base.AutoScrollMargin = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMinSize { + get { + return base.AutoScrollMinSize; + } + set { + base.AutoScrollMinSize = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Point AutoScrollPosition { + get { + return base.AutoScrollPosition; + } + set { + base.AutoScrollPosition = value; + } + } + + /// + /// + /// Summary of AllowDrop. + /// + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + if (value && AllowItemReorder) { + throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue)); + } + + base.AllowDrop = value; + + // SECREVIEW: If we toggle between AllowDrop and AllowItemReorder + // make sure that we're demanding the Clipboard permission in + // ToolStripDropTargetManager.SetAcceptDrops + if (value) { + this.DropTargetManager.EnsureRegistered(this); + } + else { + this.DropTargetManager.EnsureUnRegistered(this); + } + + } + } + /// + /// + /// + /// + [ + DefaultValue(false), + SRDescription(SR.ToolStripAllowItemReorderDescr), + SRCategory(SR.CatBehavior) + ] + public bool AllowItemReorder { + get { return GetToolStripState(STATE_ALLOWITEMREORDER); } + set { + if (GetToolStripState(STATE_ALLOWITEMREORDER) != value) { + if (AllowDrop && value) { + throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue)); + } + SetToolStripState(STATE_ALLOWITEMREORDER, value); + + // SECREVIEW: If we toggle between AllowDrop and AllowItemReorder + // make sure that we're demanding the Clipboard permission in + // ToolStripDropTargetManager.SetAcceptDrops + if (value) { + ToolStripSplitStackDragDropHandler dragDropHandler = new ToolStripSplitStackDragDropHandler(this); + this.ItemReorderDropSource = dragDropHandler; + this.ItemReorderDropTarget = dragDropHandler; + + this.DropTargetManager.EnsureRegistered(this); + } + else { + this.DropTargetManager.EnsureUnRegistered(this); + } + + } + + + + } + } + + /// + /// + /// + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripAllowMergeDescr), + SRCategory(SR.CatBehavior) + ] + public bool AllowMerge { + get { return GetToolStripState(STATE_ALLOWMERGE); } + set { + if (GetToolStripState(STATE_ALLOWMERGE) != value) { + SetToolStripState(STATE_ALLOWMERGE, value); + } + } + } + + + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + // the base calls SetDock, which causes an OnDockChanged to be called + // which forces two layouts of the parent. + using (new LayoutTransaction(this, this, PropertyNames.Anchor)) { + base.Anchor = value; + } + } + } + + /// + /// + /// + /// Just here so we can implement ShouldSerializeBackColor + /// + [ + SRDescription(SR.ToolStripBackColorDescr), + SRCategory(SR.CatAppearance) + ] + public new Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnBeginDrag)] + public event EventHandler BeginDrag { + add { + Events.AddHandler(EventBeginDrag, value); + } + remove { + Events.RemoveHandler(EventBeginDrag, value); + } + } + + /// + public override BindingContext BindingContext { + get { + BindingContext bc = (BindingContext) this.Properties.GetObject(PropBindingContext); + if (bc != null) + return bc; + + // try the parent + // + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) + return p.BindingContext; + + // we don't have a binding context + return null; + } + set { + if (this.Properties.GetObject(PropBindingContext) != value) { + this.Properties.SetObject(PropBindingContext, value); + + // re-wire the bindings + OnBindingContextChanged(EventArgs.Empty); + } + } + } + + + + + /// + /// + /// Summary of CanOverflow. + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripCanOverflowDescr), + SRCategory(SR.CatLayout) + ] + public bool CanOverflow { + get { + return GetToolStripState(STATE_CANOVERFLOW); + } + set { + if (GetToolStripState(STATE_CANOVERFLOW) != value) { + SetToolStripState(STATE_CANOVERFLOW, value); + InvalidateLayout(); + } + } + } + + /// we can only shift selection when we're not focused (someone mousing over us) + /// or we are focused and one of our toolstripcontrolhosts do not have focus. + /// SCENARIO: put focus in combo box, move the mouse over another item... selectioni + /// should not shift until the combobox relinquishes its focus. + /// + internal bool CanHotTrack { + get { + if (!Focused) { + // if ContainsFocus in one of the children = false, someone is just mousing by, we can hot track + return (ContainsFocus == false); + } + else { + // if the toolstrip itself contains focus we can definately hottrack. + return true; + } + } + } + + + [ + Browsable(false), + DefaultValue(false), + ] + public new bool CausesValidation { + get { + // By default: CausesValidation is false for a ToolStrip + // we want people to be able to use menus without validating + // their controls. + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + + [Browsable(false)] + public new event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Control.ControlCollection Controls { + get { return base.Controls; } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlAdded { + add { + base.ControlAdded += value; + } + remove { + base.ControlAdded -= value; + } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Cursor Cursor { + get { return base.Cursor; } + set { base.Cursor = value; } + } + + /// + /// Hide browsable property + /// + [Browsable(false)] + public new event EventHandler CursorChanged { + add { + base.CursorChanged += value; + } + remove { + base.CursorChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlRemoved { + add { + base.ControlRemoved += value; + } + remove { + base.ControlRemoved -= value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnEndDrag)] + public event EventHandler EndDrag { + add { + Events.AddHandler(EventEndDrag, value); + } + remove { + Events.RemoveHandler(EventEndDrag, value); + } + } + + /// + public override Font Font { + get { + if (this.IsFontSet()) { + return base.Font; + } + if (defaultFont == null) { + // since toolstrip manager default font is thread static, hold onto a copy of the + // pointer in an instance variable for perf so we dont have to keep fishing into + // thread local storage for it. + defaultFont = ToolStripManager.DefaultFont; + } + return defaultFont; + } + set { + base.Font = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(100, 25), DeviceDpi) : + new Size(100, 25); + } + } + + protected override Padding DefaultPadding { + get { + // one pixel from the right edge to prevent the right border from painting over the + // aligned-right toolstrip item. + return scaledDefaultPadding; + } + } + + protected override Padding DefaultMargin { + get { return Padding.Empty; } + } + + protected virtual DockStyle DefaultDock { + get { + return DockStyle.Top; + } + } + + protected virtual Padding DefaultGripMargin { + get { + if (toolStripGrip != null) { + return toolStripGrip.DefaultMargin; + } + else { + return scaledDefaultGripMargin; + } + } + } + + protected virtual bool DefaultShowItemToolTips { + get { + return true; + } + } + + [Browsable(false)] + [SRDescription(SR.ToolStripDefaultDropDownDirectionDescr)] + [SRCategory(SR.CatBehavior)] + public virtual ToolStripDropDownDirection DefaultDropDownDirection { + get { + ToolStripDropDownDirection direction = toolStripDropDownDirection; + if (direction == ToolStripDropDownDirection.Default) { + if (Orientation == Orientation.Vertical) { + if (IsInToolStripPanel) { + // parent can be null when we're swapping between ToolStripPanels. + DockStyle actualDock = (ParentInternal != null) ? ParentInternal.Dock : DockStyle.Left; + direction = (actualDock == DockStyle.Right) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + if (DesignMode && actualDock == DockStyle.Left) + { + direction = ToolStripDropDownDirection.Right ; + } + + } + else { + direction = ((Dock == DockStyle.Right) && (RightToLeft == RightToLeft.No)) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + if (DesignMode && Dock == DockStyle.Left) + { + direction = ToolStripDropDownDirection.Right ; + } + } + } + else { // horizontal + DockStyle dock = this.Dock; + if (IsInToolStripPanel && ParentInternal != null) { + dock = ParentInternal.Dock; // we want the orientation of the ToolStripPanel; + } + + if (dock == DockStyle.Bottom) { + direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.AboveLeft : ToolStripDropDownDirection.AboveRight; + } + else { + // assume Dock.Top + direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight; + } + } + } + return direction; + } + set { + // cant use Enum.IsValid as its not sequential + switch (value) { + case ToolStripDropDownDirection.AboveLeft: + case ToolStripDropDownDirection.AboveRight: + case ToolStripDropDownDirection.BelowLeft: + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.Left: + case ToolStripDropDownDirection.Right: + case ToolStripDropDownDirection.Default: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection)); + } + + toolStripDropDownDirection = value; + } + } + /// + /// + /// + /// Just here so we can add the default value attribute + /// + [DefaultValue(DockStyle.Top)] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + if (value != Dock) { + using (new LayoutTransaction(this, this, PropertyNames.Dock)) + using (new LayoutTransaction(this.ParentInternal, this, PropertyNames.Dock)) { + // We don't call base.Dock = value, because that would cause us to get 2 LocationChanged events. + // The first is when the parent gets a Layout due to the DockChange, and the second comes from when we + // change the orientation. Instead we've duplicated the logic of Control.Dock.set here, but with a + // LayoutTransaction on the Parent as well. + // See VSWhidbey:489688 and VSWhidbey:474781 for more details. + DefaultLayout.SetDock(this, value); + UpdateLayoutStyle(Dock); + } + // This will cause the DockChanged event to fire. + OnDockChanged(EventArgs.Empty); + } + } + } + + /// + /// Returns an owner window that can be used to + /// own a drop down. + /// + internal virtual NativeWindow DropDownOwnerWindow { + get { + if (dropDownOwnerWindow == null) { + dropDownOwnerWindow = new NativeWindow(); + } + + if (dropDownOwnerWindow.Handle == IntPtr.Zero) { + CreateParams cp = new CreateParams(); + cp.ExStyle = NativeMethods.WS_EX_TOOLWINDOW; + dropDownOwnerWindow.CreateHandle(cp); + } + + return dropDownOwnerWindow; + } + } + + /// + /// Returns the drop target manager that all the hwndless + /// items and this winbar share. this is necessary as + /// RegisterDragDrop requires an HWND. + /// + internal ToolStripDropTargetManager DropTargetManager { + get { + if (dropTargetManager == null) { + dropTargetManager = new ToolStripDropTargetManager(this); + } + return dropTargetManager; + + } + set { + dropTargetManager = value; + } + + } + /// + /// + /// + /// Just here so we can add the default value attribute + /// + protected internal virtual ToolStripItemCollection DisplayedItems { + get { + if (displayedItems == null) { + displayedItems = new ToolStripItemCollection(this, false); + } + return displayedItems; + } + } + + + /// + /// + /// + /// + /// Retreives the current display rectangle. The display rectangle + /// is the virtual display area that is used to layout components. + /// The position and dimensions of the Form's display rectangle + /// change during autoScroll. + /// + /// + public override Rectangle DisplayRectangle { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + Rectangle rect = base.DisplayRectangle; + + if ((LayoutEngine is ToolStripSplitStackLayout) && (GripStyle == ToolStripGripStyle.Visible)){ + + if (Orientation == Orientation.Horizontal) { + int gripwidth = Grip.GripThickness + Grip.Margin.Horizontal; + rect.Width -= gripwidth; + // in RTL.No we need to shift the rectangle + rect.X += (RightToLeft == RightToLeft.No) ? gripwidth : 0; + } + else { // Vertical Grip placement + int gripheight = Grip.GripThickness + Grip.Margin.Vertical; + rect.Y += gripheight; + rect.Height -= gripheight; + } + + } + return rect; + } + } + + /// + /// + /// + /// Forecolor really has no meaning for winbars - so lets hide it + /// + [Browsable(false)] + public new Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// [ToolStrip ForeColorChanged event, overriden to turn browsing off.] + /// + [ + Browsable(false) + ] + public new event EventHandler ForeColorChanged + { + add + { + base.ForeColorChanged += value; + } + remove + { + base.ForeColorChanged -= value; + } + } + + private bool HasKeyboardInput { + get { + return (ContainsFocus || (ToolStripManager.ModalMenuFilter.InMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this)); + } + } + + /// + /// Summary of ToolStripGrip. + /// + /// + internal ToolStripGrip Grip { + get { + if (toolStripGrip == null) { + toolStripGrip = new ToolStripGrip(); + toolStripGrip.Overflow = ToolStripItemOverflow.Never; + toolStripGrip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible; + toolStripGrip.AutoSize = false; + toolStripGrip.ParentInternal = this; + toolStripGrip.Margin = DefaultGripMargin; + } + return toolStripGrip; + } + } + /// + /// + /// Summary of GripStyle. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripGripStyleDescr), + DefaultValue(ToolStripGripStyle.Visible) + ] + public ToolStripGripStyle GripStyle { + get { + return toolStripGripStyle; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripGripStyle.Hidden, (int)ToolStripGripStyle.Visible)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripGripStyle)); + } + if (toolStripGripStyle != value) { + toolStripGripStyle = value; + Grip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible; + LayoutTransaction.DoLayout(this, this, PropertyNames.GripStyle); + } + } + + } + + /// + /// + /// Summary of GripStyle. + /// + [ + Browsable(false) + ] + public ToolStripGripDisplayStyle GripDisplayStyle { + get { + return (LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow) ? ToolStripGripDisplayStyle.Vertical + : ToolStripGripDisplayStyle.Horizontal; + } + } + + /// + /// + /// The external spacing between the grip and the padding of the winbar and the first item in the collection + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.ToolStripGripDisplayStyleDescr) + ] + public Padding GripMargin { + get { + return Grip.Margin; + } + set { + Grip.Margin = value; + } + } + + /// + /// + /// The boundaries of the grip on the winbar. If it is invisible - returns Rectangle.Empty. + /// + [ + Browsable(false) + ] + public Rectangle GripRectangle { + get { + return (GripStyle == ToolStripGripStyle.Visible) ? Grip.Bounds : Rectangle.Empty; + } + } + + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool HasChildren { + get { + return base.HasChildren; + } + } + + internal bool HasVisibleItems { + get { + if (!IsHandleCreated) { + foreach(ToolStripItem item in Items) { + if (((IArrangedElement)item).ParticipatesInLayout) { + // set in the state so that when the handle is created, we're accurate. + SetToolStripState(STATE_HASVISIBLEITEMS, true); + return true; + } + } + SetToolStripState(STATE_HASVISIBLEITEMS, false); + return false; + } + // after the handle is created, we start layout... so this state is cached. + return GetToolStripState(STATE_HASVISIBLEITEMS); + } + set { + SetToolStripState(STATE_HASVISIBLEITEMS, value); + } + } + + /// + /// + /// Gets the Horizontal Scroll bar for this ScrollableControl. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + new public HScrollProperties HorizontalScroll + { + get + { + return base.HorizontalScroll; + } + } + + [ + DefaultValue(typeof(Size), "16,16"), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripImageScalingSizeDescr), + ] + public Size ImageScalingSize { + get { + return ImageScalingSizeInternal; + } + set { + ImageScalingSizeInternal = value; + } + } + + internal virtual Size ImageScalingSizeInternal { + get { + return imageScalingSize; + } + set { + if (imageScalingSize != value) { + imageScalingSize = value; + + LayoutTransaction.DoLayoutIf((Items.Count > 0), this, this, PropertyNames.ImageScalingSize); + foreach (ToolStripItem item in this.Items) { + item.OnImageScalingSizeChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// + /// Gets or sets the that contains the displayed on a label control. + /// + /// + [ + DefaultValue(null), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripImageListDescr), + Browsable(false) + ] + public ImageList ImageList { + get { + return imageList; + } + set { + if (imageList != value) { + EventHandler handler = new EventHandler(ImageListRecreateHandle); + + // Remove the previous imagelist handle recreate handler + // + if (imageList != null) { + imageList.RecreateHandle -= handler; + } + + imageList = value; + + // Add the new imagelist handle recreate handler + // + if (value != null) { + value.RecreateHandle += handler; + } + + foreach (ToolStripItem item in Items) { + item.InvalidateImageListImage(); + } + Invalidate(); + } + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal override bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + internal bool IsInToolStripPanel { + get { + return ToolStripPanelRow != null; + + } + } + + /// indicates whether the user is currently + /// moving the toolstrip from one toolstrip container + /// to another + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsCurrentlyDragging { + get { + return GetToolStripState(STATE_DRAGGING); + } + } + + + /// + /// indicates if the SetBoundsCore is called thru Locationchanging. + /// + private bool IsLocationChanging { + get { + return GetToolStripState(STATE_LOCATIONCHANGING); + } + } + + + /// + /// + /// The items that belong to this ToolStrip. + /// Note - depending on space and layout preferences, not all items + /// in this collection will be displayed. They may not even be displayed + /// on this winbar (say in the case where we're overflowing the item). + /// The collection of _Displayed_ items is the DisplayedItems collection. + /// The displayed items collection also includes things like the OverflowButton + /// and the Grip. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripItemsDescr), + MergableProperty(false) + ] + public virtual ToolStripItemCollection Items { + get { + if (toolStripItemCollection == null) { + toolStripItemCollection = new ToolStripItemCollection(this, true); + } + return toolStripItemCollection; + } + } + + + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemAddedDescr)] + public event ToolStripItemEventHandler ItemAdded { + add { + Events.AddHandler(EventItemAdded, value); + } + remove { + Events.RemoveHandler(EventItemAdded, value); + } + } + + + /// + /// + /// Occurs when the control is clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripItemOnClickDescr)] + public event ToolStripItemClickedEventHandler ItemClicked { + add { + Events.AddHandler(EventItemClicked, value); + } + remove { + Events.RemoveHandler(EventItemClicked, value); + } + } + + + /// + /// we have a backbuffer for painting items... this is cached to be the size of the largest + /// item in the collection - and is cached in OnPaint, and disposed when the toolstrip + /// is no longer visible. + /// + /// [: toolstrip - main hdc ] <-- visible to user + /// [ toolstrip double buffer hdc ] <-- onpaint hands us this buffer, after we're done DBuf is copied to "main hdc"/ + /// [tsi dc] <-- we copy the background from the DBuf, then paint the item into this DC, then BitBlt back up to DBuf + /// + /// This is done because GDI wont honor GDI+ TranslateTransform. We used to use DCMapping to change the viewport + /// origin and clipping rect of the toolstrip double buffer hdc to paint each item, but this proves costly + /// because you need to allocate GDI+ Graphics objects for every single item. This method allows us to only + /// allocate 1 Graphics object and share it between all the items in OnPaint. + /// + private CachedItemHdcInfo ItemHdcInfo { + get { + if (cachedItemHdcInfo == null) { + cachedItemHdcInfo = new CachedItemHdcInfo(); + } + return cachedItemHdcInfo; + } + } + + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemRemovedDescr)] + public event ToolStripItemEventHandler ItemRemoved { + add { + Events.AddHandler(EventItemRemoved, value); + } + remove { + Events.RemoveHandler(EventItemRemoved, value); + } + } + /// + /// handy check for painting and sizing + [Browsable(false)] + public bool IsDropDown { + get { return (this is ToolStripDropDown); } + } + + internal bool IsDisposingItems { + get { + return GetToolStripState(STATE_DISPOSINGITEMS); + } + } + /// + /// The OnDrag[blah] methods that will be called if AllowItemReorder is true. + /// + /// This allows us to have methods that handle drag/drop of the winbar items + /// without calling back on the user's code + /// + internal IDropTarget ItemReorderDropTarget { + get { + return itemReorderDropTarget; + } + set { + itemReorderDropTarget = value; + } + } + + /// + /// The OnQueryContinueDrag and OnGiveFeedback methods that will be called if + /// AllowItemReorder is true. + /// + /// This allows us to have methods that handle drag/drop of the winbar items + /// without calling back on the user's code + /// + internal ISupportOleDropSource ItemReorderDropSource { + get { + return itemReorderDropSource; + } + set { + itemReorderDropSource = value; + } + } + + internal bool IsInDesignMode { + get { + return DesignMode; + } + } + + internal bool IsTopInDesignMode { + get { + var topLevelToolStrip = GetToplevelOwnerToolStrip(); + return topLevelToolStrip != null && topLevelToolStrip.IsInDesignMode; + } + } + + internal bool IsSelectionSuspended { + get { return GetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE); } + } + + internal ToolStripItem LastMouseDownedItem { + get { + if (lastMouseDownedItem != null && + (lastMouseDownedItem.IsDisposed || lastMouseDownedItem.ParentInternal != this)){ + // handle disposal, parent changed since we last mouse downed. + lastMouseDownedItem = null; + } + return lastMouseDownedItem; + + } + } + + [ + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public LayoutSettings LayoutSettings { + get { + return layoutSettings; + } + set { + layoutSettings = value; + } + } + + + /// + /// + /// Specifies whether we're horizontal or vertical + /// + [ + SRDescription(SR.ToolStripLayoutStyle), + SRCategory(SR.CatLayout), + AmbientValue(ToolStripLayoutStyle.StackWithOverflow) + ] + public ToolStripLayoutStyle LayoutStyle { + get { + if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow) { + switch (this.Orientation) { + case Orientation.Horizontal: + return ToolStripLayoutStyle.HorizontalStackWithOverflow; + case Orientation.Vertical: + return ToolStripLayoutStyle.VerticalStackWithOverflow; + } + } + return layoutStyle; + } + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripLayoutStyle.StackWithOverflow, (int)ToolStripLayoutStyle.Table)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripLayoutStyle)); + } + if (layoutStyle != value) { + layoutStyle = value; + + switch (value) { + case ToolStripLayoutStyle.Flow: + if (!(layoutEngine is FlowLayout)) { + layoutEngine = FlowLayout.Instance; + } + // Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location) + UpdateOrientation(Orientation.Horizontal); + break; + case ToolStripLayoutStyle.Table: + + if (!(layoutEngine is TableLayout)) { + layoutEngine = TableLayout.Instance; + } + // Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location) + UpdateOrientation(Orientation.Horizontal); + break; + case ToolStripLayoutStyle.StackWithOverflow: + case ToolStripLayoutStyle.HorizontalStackWithOverflow: + case ToolStripLayoutStyle.VerticalStackWithOverflow: + default: + + if (value != ToolStripLayoutStyle.StackWithOverflow) { + UpdateOrientation((value == ToolStripLayoutStyle.VerticalStackWithOverflow) ? Orientation.Vertical : Orientation.Horizontal); + } + else { + if (IsInToolStripPanel) { + UpdateLayoutStyle(ToolStripPanelRow.Orientation); + } + else { + UpdateLayoutStyle(this.Dock); + } + } + if (!(layoutEngine is ToolStripSplitStackLayout)) { + layoutEngine = new ToolStripSplitStackLayout(this); + } + break; + } + + using (LayoutTransaction.CreateTransactionIf(IsHandleCreated, this, this, PropertyNames.LayoutStyle)) { + LayoutSettings = CreateLayoutSettings(layoutStyle); + } + OnLayoutStyleChanged(EventArgs.Empty); + } + } + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutCompleteDescr)] + public event EventHandler LayoutCompleted { + add { + Events.AddHandler(EventLayoutCompleted, value); + } + remove { + Events.RemoveHandler(EventLayoutCompleted, value); + } + } + + internal bool LayoutRequired { + get { + return this.layoutRequired; + } + set { + this.layoutRequired = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutStyleChangedDescr)] + public event EventHandler LayoutStyleChanged { + add { + Events.AddHandler(EventLayoutStyleChanged, value); + } + remove { + Events.RemoveHandler(EventLayoutStyleChanged, value); + } + } + + /// + public override LayoutEngine LayoutEngine { + get { + // + return layoutEngine; + } + } + + + + /// + /// + /// [To be supplied.] + /// + internal event ToolStripLocationCancelEventHandler LocationChanging { + add { + Events.AddHandler(EventLocationChanging, value); + } + remove { + Events.RemoveHandler(EventLocationChanging, value); + } + } + + /// + protected internal virtual Size MaxItemSize { + get { + return this.DisplayRectangle.Size; + } + } + + internal bool MenuAutoExpand { + get { + if (!DesignMode) { + if (GetToolStripState(STATE_MENUAUTOEXPAND)) { + if (!IsDropDown && !ToolStripManager.ModalMenuFilter.InMenuMode) { + SetToolStripState(STATE_MENUAUTOEXPAND, false); + return false; + } + return true; + } + } + return false; + + } + set { + if (!DesignMode) { + SetToolStripState(STATE_MENUAUTOEXPAND, value); + } + + } + } + + internal Stack MergeHistoryStack { + get { + if(mergeHistoryStack == null) { + mergeHistoryStack = new Stack(); + } + return mergeHistoryStack; + } + } + + + private MouseHoverTimer MouseHoverTimer { + get { + if (mouseHoverTimer == null) { + mouseHoverTimer = new MouseHoverTimer(); + } + return mouseHoverTimer; + } + } + + + /// + /// + /// Summary of OverflowButton. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public ToolStripOverflowButton OverflowButton { + get { + if (toolStripOverflowButton == null) { + toolStripOverflowButton = new ToolStripOverflowButton(this); + toolStripOverflowButton.Overflow = ToolStripItemOverflow.Never; + toolStripOverflowButton.ParentInternal = this; + toolStripOverflowButton.Alignment = ToolStripItemAlignment.Right; + toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size); + } + return toolStripOverflowButton; + } + } + + // + // SECREVIEW VSWhidbey 436973: adding control host items to this collection in + // the internet zone will throw security exceptions + // + internal ToolStripItemCollection OverflowItems { + get { + if (overflowItems == null) { + overflowItems = new ToolStripItemCollection(this, false); + } + return overflowItems; + } + } + + [Browsable(false)] + public Orientation Orientation { + get { + return orientation; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripPaintGripDescr)] + public event PaintEventHandler PaintGrip { + add { + Events.AddHandler(EventPaintGrip, value); + } + remove { + Events.RemoveHandler(EventPaintGrip, value); + } + } + + internal RestoreFocusMessageFilter RestoreFocusFilter { + get { + if (restoreFocusFilter == null) { + restoreFocusFilter = new RestoreFocusMessageFilter(this); + } + return restoreFocusFilter; + } + } + + internal ToolStripPanelCell ToolStripPanelCell { + get { return ((ISupportToolStripPanel)this).ToolStripPanelCell; } + } + + + internal ToolStripPanelRow ToolStripPanelRow { + get { return ((ISupportToolStripPanel)this).ToolStripPanelRow; } + } + + // fetches the Cell associated with this toolstrip. + ToolStripPanelCell ISupportToolStripPanel.ToolStripPanelCell { + get { + ToolStripPanelCell toolStripPanelCell = null; + if (!IsDropDown && !IsDisposed) { + if (Properties.ContainsObject(ToolStrip.PropToolStripPanelCell)) { + toolStripPanelCell = (ToolStripPanelCell)Properties.GetObject(ToolStrip.PropToolStripPanelCell); + } + else { + toolStripPanelCell = new ToolStripPanelCell(this); + Properties.SetObject(ToolStrip.PropToolStripPanelCell, toolStripPanelCell); + } + } + return toolStripPanelCell; + } + } + + + ToolStripPanelRow ISupportToolStripPanel.ToolStripPanelRow { + get { + ToolStripPanelCell cell = ToolStripPanelCell; + if (cell == null) { + return null; + } + return ToolStripPanelCell.ToolStripPanelRow; + } + set { + ToolStripPanelRow oldToolStripPanelRow = ToolStripPanelRow; + + if (oldToolStripPanelRow != value) { + ToolStripPanelCell cell = ToolStripPanelCell; + if (cell == null) { + return; + } + cell.ToolStripPanelRow = value; + + if (value != null) { + if (oldToolStripPanelRow == null || oldToolStripPanelRow.Orientation != value.Orientation) { + if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow) + { + UpdateLayoutStyle(value.Orientation); + } + else + { + UpdateOrientation(value.Orientation); + } + + } + } + else { + if (oldToolStripPanelRow != null && oldToolStripPanelRow.ControlsInternal.Contains(this)) { + oldToolStripPanelRow.ControlsInternal.Remove(this); + } + UpdateLayoutStyle(Dock); + } + } + + } + + } + + + [DefaultValue(false)] + [SRCategory(SR.CatLayout)] + [SRDescription(SR.ToolStripStretchDescr)] + public bool Stretch { + get { + return GetToolStripState(STATE_STRETCH); + } + set { + if (Stretch != value) { + SetToolStripState(STATE_STRETCH,value); + } + } + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode && !IsTopInDesignMode; + } + } + + /// + /// + /// The renderer is used to paint the hwndless winbar items. If someone wanted to + /// change the "Hot" look of all of their buttons to be a green triangle, they should + /// create a class that derives from ToolStripRenderer, assign it to this property and call + /// invalidate. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public ToolStripRenderer Renderer { + get { + + if (IsDropDown) { + // PERF: since this is called a lot we dont want to make it virtual + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown is ToolStripOverflow || dropDown.IsAutoGenerated) { + if (dropDown.OwnerToolStrip != null) { + return dropDown.OwnerToolStrip.Renderer; + } + } + } + if (RenderMode == ToolStripRenderMode.ManagerRenderMode) { + return ToolStripManager.Renderer; + } + // always return a valid renderer so our paint code + // doesn't have to be bogged down by checks for null. + + SetToolStripState(STATE_USEDEFAULTRENDERER, false); + if (renderer == null) { + Renderer = ToolStripManager.CreateRenderer(RenderMode); + } + return renderer; + + } + set { + // if the value happens to be null, the next get + // will autogenerate a new ToolStripRenderer. + if (renderer != value) { + SetToolStripState(STATE_USEDEFAULTRENDERER, (value == null)); + renderer = value; + currentRendererType = (renderer != null) ? renderer.GetType() : typeof(System.Type); + OnRendererChanged(EventArgs.Empty); + } + } + } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public event EventHandler RendererChanged { + add { + Events.AddHandler(EventRendererChanged, value); + } + remove { + Events.RemoveHandler(EventRendererChanged, value); + } + } + + /// + [ + SRDescription(SR.ToolStripRenderModeDescr), + SRCategory(SR.CatAppearance), + ] + public ToolStripRenderMode RenderMode { + get { + if (GetToolStripState(STATE_USEDEFAULTRENDERER)) { + return ToolStripRenderMode.ManagerRenderMode; + } + if (renderer != null && !renderer.IsAutoGenerated) { + return ToolStripRenderMode.Custom; + } + // check the type of the currently set renderer. + // types are cached as this may be called frequently. + if (currentRendererType == ToolStripManager.ProfessionalRendererType) { + return ToolStripRenderMode.Professional; + } + if (currentRendererType == ToolStripManager.SystemRendererType) { + return ToolStripRenderMode.System; + } + return ToolStripRenderMode.Custom; + + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripRenderMode.Custom, (int)ToolStripRenderMode.ManagerRenderMode)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripRenderMode)); + } + if (value == ToolStripRenderMode.Custom) { + throw new NotSupportedException(SR.GetString(SR.ToolStripRenderModeUseRendererPropertyInstead)); + } + + if (value == ToolStripRenderMode.ManagerRenderMode) { + if (!GetToolStripState(STATE_USEDEFAULTRENDERER)) { + SetToolStripState(STATE_USEDEFAULTRENDERER, true); + OnRendererChanged(EventArgs.Empty); + } + } + else { + SetToolStripState(STATE_USEDEFAULTRENDERER, false); + Renderer = ToolStripManager.CreateRenderer(value); + } + } + } + + + /// + /// ToolStripItems need to access this to determine if they should be showing underlines + /// for their accelerators. Since they are not HWNDs, and this method is protected on control + /// we need a way for them to get at it. + /// + internal bool ShowKeyboardCuesInternal { + get { + return this.ShowKeyboardCues; + + } + } + + + /// + [DefaultValue(true)] + [SRDescription(SR.ToolStripShowItemToolTipsDescr)] + [SRCategory(SR.CatBehavior)] + public bool ShowItemToolTips { + get { + return showItemToolTips; + } + set { + if (showItemToolTips != value) { + showItemToolTips = value; + if (!showItemToolTips) { + UpdateToolTip(null); + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip internalToolTip = this.ToolTip; + foreach (ToolStripItem item in this.Items) { + if (showItemToolTips) { + KeyboardToolTipStateMachine.Instance.Hook(item, internalToolTip); + } + else { + KeyboardToolTipStateMachine.Instance.Unhook(item, internalToolTip); + } + } + } + + // Fix for Dev10 889523 + // If the overflow button has not been created, don't check its properties + // since this will force its creating and cause a re-layout of the control + if (toolStripOverflowButton != null && this.OverflowButton.HasDropDownItems) { + this.OverflowButton.DropDown.ShowItemToolTips = value; + } + } + } + } + + /// internal lookup table for shortcuts... intended to speed search time + internal Hashtable Shortcuts { + get { + if (shortcuts == null) { + shortcuts = new Hashtable(1); + } + return shortcuts; + } + } + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + + /// this is the ToolTip used for the individual items + /// it only works if ShowItemToolTips = true + /// + internal ToolTip ToolTip { + get { + ToolTip toolTip; + if (!Properties.ContainsObject(ToolStrip.PropToolTip)) { + toolTip = new ToolTip(); + Properties.SetObject(ToolStrip.PropToolTip,toolTip ); + } + else { + toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip); + } + return toolTip; + } + } + + /// + [ + DefaultValue(ToolStripTextDirection.Horizontal), + SRDescription(SR.ToolStripTextDirectionDescr), + SRCategory(SR.CatAppearance) + ] + public virtual ToolStripTextDirection TextDirection { + get { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStrip.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStrip.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + textDirection = ToolStripTextDirection.Horizontal; + } + + return textDirection; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripTextDirection.Inherit, (int)ToolStripTextDirection.Vertical270)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripTextDirection)); + } + Properties.SetObject(ToolStrip.PropTextDirection, value); + + using(new LayoutTransaction(this, this, "TextDirection")) { + for (int i = 0; i < Items.Count; i++) { + Items[i].OnOwnerTextDirectionChanged(); + } + } + + } + } + + /// + /// + /// Gets the Vertical Scroll bar for this ScrollableControl. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + new public VScrollProperties VerticalScroll + { + get + { + return base.VerticalScroll; + } + } + + void ISupportToolStripPanel.BeginDrag() { + OnBeginDrag(EventArgs.Empty); + } + + // Internal so that it's not a public API. + internal virtual void ChangeSelection(ToolStripItem nextItem) { + + if (nextItem != null) { + ToolStripControlHost controlHost = nextItem as ToolStripControlHost; + // if we contain focus, we should set focus to ourselves + // so we get the focus off the thing that's currently focused + // e.g. go from a text box to a toolstrip button + if (ContainsFocus && !Focused) { + this.FocusInternal(); + if (controlHost == null) { + // if nextItem IS a toolstripcontrolhost, we're going to focus it anyways + // we only fire KeyboardActive when "focusing" a non-hwnd backed item + KeyboardActive = true; + } + } + if (controlHost != null) { + if (hwndThatLostFocus == IntPtr.Zero) { + SnapFocus(UnsafeNativeMethods.GetFocus()); + } + controlHost.Control.Select(); + controlHost.Control.FocusInternal(); + } + + + nextItem.Select(); + + ToolStripMenuItem tsNextItem = nextItem as ToolStripMenuItem; + if (tsNextItem != null && !IsDropDown) { + // only toplevel menus auto expand when the selection changes. + tsNextItem.HandleAutoExpansion(); + } + + } + + } + + protected virtual LayoutSettings CreateLayoutSettings(ToolStripLayoutStyle layoutStyle) { + switch (layoutStyle) { + case ToolStripLayoutStyle.Flow: + return new FlowLayoutSettings(this); + case ToolStripLayoutStyle.Table: + return new TableLayoutSettings(this); + default: + return null; + } + } + + protected internal virtual ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) { + if (text == "-") { + return new ToolStripSeparator(); + } + else { + return new ToolStripButton(text,image,onClick); + } + } + + + /// + /// Summary of ClearAllSelections. + /// + private void ClearAllSelections() { + ClearAllSelectionsExcept(null); + } + + /// + /// Summary of ClearAllSelectionsExcept. + /// + /// + private void ClearAllSelectionsExcept(ToolStripItem item) { + Rectangle regionRect = (item == null) ? Rectangle.Empty : item.Bounds; + Region region = null; + + try { + + for (int i = 0; i < DisplayedItems.Count; i++) { + if (DisplayedItems[i] == item) { + continue; + } + else if (item != null && DisplayedItems[i].Pressed) { + // + ToolStripDropDownItem dropDownItem = DisplayedItems[i] as ToolStripDropDownItem; + + if (dropDownItem != null && dropDownItem.HasDropDownItems) { + dropDownItem.AutoHide(item); + } + } + bool invalidate = false; + if (DisplayedItems[i].Selected) { + DisplayedItems[i].Unselect(); + Debug.WriteLineIf(SelectionDebug.TraceVerbose,"[SelectDBG ClearAllSelectionsExcept] Unselecting " + DisplayedItems[i].Text); + invalidate = true; + } + + + if (invalidate) { + // since regions are heavy weight - only use if we need it. + if (region == null) { + region = new Region(regionRect); + } + region.Union(DisplayedItems[i].Bounds); + } + } + + // force an WM_PAINT to happen now to instantly reflect the selection change. + if (region != null) { + Invalidate(region, true); + Update(); + } + else if (regionRect != Rectangle.Empty) { + Invalidate(regionRect, true); + Update(); + } + + } + finally { + if (region != null) { + region.Dispose(); + } + } + // fire accessibility + if (IsHandleCreated && item != null) { + int focusIndex = DisplayedItems.IndexOf(item); + AccessibilityNotifyClients(AccessibleEvents.Focus, focusIndex); + } + } + + internal void ClearInsertionMark() { + if (lastInsertionMarkRect != Rectangle.Empty) { + // stuff away the lastInsertionMarkRect + // and clear it out _before_ we call paint OW + // the call to invalidate wont help as it will get + // repainted. + Rectangle invalidate = lastInsertionMarkRect; + lastInsertionMarkRect = Rectangle.Empty; + + this.Invalidate(invalidate); + } + + } + private void ClearLastMouseDownedItem() { + ToolStripItem lastItem = lastMouseDownedItem; + lastMouseDownedItem = null; + if (IsSelectionSuspended) { + SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, false); + if (lastItem != null) { + lastItem.Invalidate(); + } + } + } + + /// + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if(disposing) { + ToolStripOverflow overflow = GetOverflow(); + + try { + + this.SuspendLayout(); + if (overflow != null) { + overflow.SuspendLayout(); + } + // if there's a problem in config, dont be a leaker. + SetToolStripState(STATE_DISPOSINGITEMS, true); + lastMouseDownedItem = null; + + HookStaticEvents(/*hook=*/false); + + ToolStripPanelCell toolStripPanelCell = Properties.GetObject(ToolStrip.PropToolStripPanelCell) as ToolStripPanelCell; + if (toolStripPanelCell != null) { + toolStripPanelCell.Dispose(); + } + + if (cachedItemHdcInfo != null) { + cachedItemHdcInfo.Dispose(); + } + + if (mouseHoverTimer != null) { + mouseHoverTimer.Dispose(); + } + + ToolTip toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip); + if (toolTip != null) { + toolTip.Dispose (); + } + + if (!Items.IsReadOnly) { + // only dispose the items we actually own. + for (int i = Items.Count - 1; i >= 0; i--) { + Items[i].Dispose(); + } + Items.Clear(); + } + // clean up items not in the Items list + if (toolStripGrip != null) { + toolStripGrip.Dispose(); + } + if (toolStripOverflowButton != null) { + toolStripOverflowButton.Dispose(); + } + + // remove the restore focus filter + if (restoreFocusFilter != null) { + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(restoreFocusFilter); + restoreFocusFilter = null; + } + + + // exit menu mode if necessary. + bool exitMenuMode = false; + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) { + exitMenuMode = true; + } + ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this); + // if we were the last toolstrip in the queue, exit menu mode. + if (exitMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == null) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "Exiting menu mode because we're the last toolstrip in the queue, and we've disposed."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + + ToolStripManager.ToolStrips.Remove(this); + } + finally { + + this.ResumeLayout(false); + if (overflow != null) { + overflow.ResumeLayout(false); + } + SetToolStripState(STATE_DISPOSINGITEMS, false); + } + } + base.Dispose( disposing ); + + } + + internal void DoLayoutIfHandleCreated(ToolStripItemEventArgs e) { + if (this.IsHandleCreated) { + LayoutTransaction.DoLayout(this, e.Item, PropertyNames.Items); + this.Invalidate(); + // Adding this item may have added it to the overflow + // However, we can't check if it's in OverflowItems, because + // it gets added there in Layout, and layout might be suspended. + if (this.CanOverflow && this.OverflowButton.HasDropDown) { + if (DeferOverflowDropDownLayout()) { + CommonProperties.xClearPreferredSizeCache(this.OverflowButton.DropDown); + this.OverflowButton.DropDown.LayoutRequired = true; + } + else { + LayoutTransaction.DoLayout(this.OverflowButton.DropDown, e.Item, PropertyNames.Items); + this.OverflowButton.DropDown.Invalidate(); + } + } + } + else { + // next time we fetch the preferred size, recalc it. + CommonProperties.xClearPreferredSizeCache(this); + this.LayoutRequired = true; + if (this.CanOverflow && this.OverflowButton.HasDropDown) { + this.OverflowButton.DropDown.LayoutRequired = true; + } + } + } + + private bool DeferOverflowDropDownLayout() { + return this.IsLayoutSuspended + ||!this.OverflowButton.DropDown.Visible + || !this.OverflowButton.DropDown.IsHandleCreated; + } + + void ISupportToolStripPanel.EndDrag() { + ToolStripPanel.ClearDragFeedback(); + OnEndDrag(EventArgs.Empty); + } + + internal ToolStripOverflow GetOverflow() { + return (toolStripOverflowButton == null || !toolStripOverflowButton.HasDropDown) ? null : toolStripOverflowButton.DropDown as ToolStripOverflow; + } + internal byte GetMouseId() { + // never return 0 as the mousedown ID, this is the "reset" value. + if (mouseDownID == 0) { + mouseDownID++; + } + return mouseDownID; + } + internal virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) { + + if (rtlAware && RightToLeft == RightToLeft.Yes) { + if (direction == ArrowDirection.Right) { + direction = ArrowDirection.Left; + } + else if (direction == ArrowDirection.Left) { + direction = ArrowDirection.Right; + } + } + return GetNextItem(start, direction); + } + + /// + /// + /// Gets the next item from the given start item in the direction specified. + /// - This function wraps if at the end + /// - This function will only surf the items in the current container + /// - Overriding this function will change the tab ordering and accessible child ordering. + /// + public virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction) + { + if (!WindowsFormsUtils.EnumValidator.IsValidArrowDirection(direction)) { + throw new InvalidEnumArgumentException("direction", (int)direction, typeof(ArrowDirection)); + } + + switch (direction) { + case ArrowDirection.Right: + return GetNextItemHorizontal(start, /*forward = */true); + case ArrowDirection.Left: + return GetNextItemHorizontal(start, /*forward = */false); + case ArrowDirection.Down: + return GetNextItemVertical(start, /*forward = */true); + case ArrowDirection.Up: + return GetNextItemVertical(start, /*forward = */false); + } + + return null; + } + + + // + // Helper function for GetNextItem - do not directly call this. + // + private ToolStripItem GetNextItemHorizontal(ToolStripItem start, bool forward) { + + if (DisplayedItems.Count <= 0) + return null; + + if (start == null) { + // The navigation should be consistent when navigating in forward and + // backward direction entering the toolstrip, it means that for AI.Level3 + // the first toolstrip item should be selected irrespectively TAB or SHIFT+TAB + // is pressed. + start = GetStartItem(forward); + } + + int current = DisplayedItems.IndexOf(start); + if (current == -1) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "Started from a visible = false item"); + return null; + } + + Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current != -1), "[SelectDBG GetNextToolStripItem] Last selected item was " + ((current != -1) ? DisplayedItems[current].Text : "")); + Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current == -1), "[SelectDBG GetNextToolStripItem] Last selected item was null"); + + int count = DisplayedItems.Count; + + do { + + if (forward) { + current = ++current % count; + } + else { // provide negative wrap if necessary + current = (--current < 0) ? count + current : current; + } + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown!= null) + { + if (dropDown.OwnerItem != null && dropDown.OwnerItem.IsInDesignMode) { + return DisplayedItems[current]; + } + } + if (DisplayedItems[current].CanKeyboardSelect) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG GetNextToolStripItem] selecting " + DisplayedItems[current].Text); + //ClearAllSelectionsExcept(Items[current]); + return DisplayedItems[current]; + } + + } while (DisplayedItems[current] != start); + return null; + } + + private ToolStripItem GetStartItem(bool forward) { + if (forward) { + return DisplayedItems[DisplayedItems.Count - 1]; + } + else if (AccessibilityImprovements.Level3 && !(this is ToolStripDropDown)) { + // For the drop-down up-directed loop should be preserved. + // So if the current item is topmost, then the bottom item should be selected on up-key press. + return DisplayedItems[DisplayedItems.Count > 1 ? 1 : 0]; + } + + return DisplayedItems[0]; + } + + + // + // Helper function for GetNextItem - do not directly call this. + // + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + private ToolStripItem GetNextItemVertical(ToolStripItem selectedItem, bool down) { + + ToolStripItem tanWinner = null; + ToolStripItem hypotenuseWinner = null; + + double minHypotenuse = Double.MaxValue; + double minTan = Double.MaxValue; + double hypotenuseOfTanWinner = Double.MaxValue; + double tanOfHypotenuseWinner = Double.MaxValue; + + if (selectedItem == null) { + ToolStripItem item = GetNextItemHorizontal(selectedItem, down); + return item; + } + + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown != null) + { + if (dropDown.OwnerItem != null && (dropDown.OwnerItem.IsInDesignMode || (dropDown.OwnerItem.Owner != null && dropDown.OwnerItem.Owner.IsInDesignMode))) { + ToolStripItem item = GetNextItemHorizontal(selectedItem, down); + return item; + } + } + + + Point midPointOfCurrent = new Point(selectedItem.Bounds.X + selectedItem.Width / 2, + selectedItem.Bounds.Y + selectedItem.Height / 2); + + + + for(int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem otherItem = DisplayedItems[i]; + if (otherItem == selectedItem || !otherItem.CanKeyboardSelect) { + continue; + } + if (!down && otherItem.Bounds.Bottom > selectedItem.Bounds.Top) { + // if we are going up the other control has to be above + continue; + } + else if (down && otherItem.Bounds.Top < selectedItem.Bounds.Bottom) { + // if we are going down the other control has to be below + continue; + } + + //[ otherControl ] + // * + Point otherItemMidLocation = new Point(otherItem.Bounds.X + otherItem.Width/2, (down)? otherItem.Bounds.Top : otherItem.Bounds.Bottom); + #if DEBUG_UPDOWN + Graphics g = Graphics.FromHwnd(this.Handle); + + using (Pen p = new Pen(Color.FromKnownColor((KnownColor)i))) { + g.DrawLine(p,otherItemMidLocation, midPointOfCurrent); + } + System.Threading.Thread.Sleep(100); + g.Dispose(); + #endif + int oppositeSide = otherItemMidLocation.X - midPointOfCurrent.X; + int adjacentSide = otherItemMidLocation.Y - midPointOfCurrent.Y; + + // use pythagrian theorem to calculate the length of the distance + // between the middle of the current control in question and it's adjacent + // objects. + double hypotenuse = Math.Sqrt(adjacentSide*adjacentSide + oppositeSide*oppositeSide); + + if (adjacentSide != 0) { // avoid divide by zero - we dont do layered controls + // _[o] + // |/ + // [s] + // get the angle between s and o by taking the arctan. + // PERF consider using approximation instead + double tan = Math.Abs(Math.Atan(oppositeSide/adjacentSide)); + + // we want the thing with the smallest angle and smallest distance between midpoints + minTan = Math.Min(minTan, tan); + minHypotenuse = Math.Min(minHypotenuse, hypotenuse); + + if (minTan == tan && minTan != Double.NaN) { + tanWinner = otherItem; + hypotenuseOfTanWinner = hypotenuse; + } + + if (minHypotenuse == hypotenuse) { + hypotenuseWinner = otherItem; + tanOfHypotenuseWinner = tan; + } + } + } + + + #if DEBUG_UPDOWN + string tanWinnerString = (tanWinner == null) ? "null" : tanWinner.ToString(); + string hypWinnerString = (hypotenuseWinner == null) ? "null": hypotenuseWinner.ToString(); + Debug.WriteLine(String.Format("Tangent winner is {0} Hyp winner is {1}", tanWinnerString, hypWinnerString)); + + #endif + if ((tanWinner == null) || (hypotenuseWinner == null)) { + return (GetNextItemHorizontal(null,down)); + } + else { + // often times the guy with the best angle will be the guy with the closest hypotenuse. + // however in layouts where things are more randomly spaced, this is not necessarily the case. + if (tanOfHypotenuseWinner == minTan) { + // if the angles match up, such as in the case of items of the same width in vertical flow + // then pick the closest one. + return hypotenuseWinner; + } + else if ((!down && tanWinner.Bounds.Bottom <= hypotenuseWinner.Bounds.Top) + ||(down && tanWinner.Bounds.Top > hypotenuseWinner.Bounds.Bottom)) { + // we prefer the case where the angle is smaller than + // the case where the hypotenuse is smaller. The only + // scenarios where that is not the case is when the hypoteneuse + // winner is clearly closer than the angle winner. + + // [a.winner] | [s] + // | [h.winner] + // [h.winner] | + // [s] | [a.winner] + return hypotenuseWinner; + } + else { + return tanWinner; + } + } + } + + + internal override Size GetPreferredSizeCore(Size proposedSize) { + // We act like a container control + + // Translating 0,0 from ClientSize to actual Size tells us how much space + // is required for the borders. + if (proposedSize.Width == 1) { + proposedSize.Width = Int32.MaxValue; + } + if (proposedSize.Height == 1) { + proposedSize.Height = Int32.MaxValue; + } + + Padding padding = Padding; + Size prefSize = LayoutEngine.GetPreferredSize(this, proposedSize - padding.Size); + Padding newPadding = Padding; + + // VSWhidbey 471860: + // as a side effect of some of the layouts, we can change the padding. + // if this happens, we need to clear the cache. + if (padding != newPadding) { + CommonProperties.xClearPreferredSizeCache(this); + } + return prefSize + newPadding.Size; + + } + +#region GetPreferredSizeHelpers + + // + // These are here so they can be shared between splitstack layout and StatusStrip + // + internal static Size GetPreferredSizeHorizontal(IArrangedElement container, Size proposedConstraints) { + Size maxSize = Size.Empty; + ToolStrip toolStrip = container as ToolStrip; + + // ensure preferred size respects default size as a minimum. + Size defaultSize = toolStrip.DefaultSize - toolStrip.Padding.Size; + maxSize.Height = Math.Max(0, defaultSize.Height); + + bool requiresOverflow = false; + bool foundItemParticipatingInLayout = false; + + for (int j = 0; j < toolStrip.Items.Count; j++) { + ToolStripItem item = toolStrip.Items[j]; + + if (((IArrangedElement)item).ParticipatesInLayout) { + foundItemParticipatingInLayout =true; + if (item.Overflow != ToolStripItemOverflow.Always) { + Padding itemMargin = item.Margin; + Size prefItemSize = GetPreferredItemSize(item); + maxSize.Width += itemMargin.Horizontal + prefItemSize.Width; + maxSize.Height = Math.Max(maxSize.Height, itemMargin.Vertical + prefItemSize.Height); + } + else { + requiresOverflow = true; + } + } + } + + if (toolStrip.Items.Count == 0 || (!foundItemParticipatingInLayout)) { + // if there are no items there, create something anyways. + maxSize = defaultSize; + } + + + if (requiresOverflow) { + // add in the width of the overflow button + ToolStripOverflowButton overflowItem = toolStrip.OverflowButton; + Padding overflowItemMargin = overflowItem.Margin; + + maxSize.Width += overflowItemMargin.Horizontal + overflowItem.Bounds.Width; + } + else { + maxSize.Width += 2; //add Padding of 2 Pixels to the right if not Overflow. + } + + if (toolStrip.GripStyle == ToolStripGripStyle.Visible) { + // add in the grip width + Padding gripMargin = toolStrip.GripMargin; + maxSize.Width += gripMargin.Horizontal + toolStrip.Grip.GripThickness; + } + + maxSize = LayoutUtils.IntersectSizes(maxSize, proposedConstraints); + return maxSize; + } + + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + + internal static Size GetPreferredSizeVertical(IArrangedElement container, Size proposedConstraints) { + Size maxSize = Size.Empty; + bool requiresOverflow = false; + ToolStrip toolStrip = container as ToolStrip; + + bool foundItemParticipatingInLayout = false; + + + for (int j = 0; j < toolStrip.Items.Count; j++) { + ToolStripItem item = toolStrip.Items[j]; + + if (((IArrangedElement)item).ParticipatesInLayout) { + foundItemParticipatingInLayout = true; + if (item.Overflow != ToolStripItemOverflow.Always) { + Size preferredSize = GetPreferredItemSize(item); + Padding itemMargin = item.Margin; + maxSize.Height += itemMargin.Vertical + preferredSize.Height; + maxSize.Width = Math.Max(maxSize.Width, itemMargin.Horizontal + preferredSize.Width); + } + else { + requiresOverflow = true; + } + } + } + + + if (toolStrip.Items.Count == 0 || !foundItemParticipatingInLayout) { + // if there are no items there, create something anyways. + maxSize = LayoutUtils.FlipSize( toolStrip.DefaultSize); + } + + if (requiresOverflow) { + // add in the width of the overflow button + ToolStripOverflowButton overflowItem = toolStrip.OverflowButton; + Padding overflowItemMargin = overflowItem.Margin; + maxSize.Height += overflowItemMargin.Vertical + overflowItem.Bounds.Height; + } + else { + maxSize.Height += 2; //add Padding to the bottom if not Overflow. + } + + if (toolStrip.GripStyle == ToolStripGripStyle.Visible) { + // add in the grip width + Padding gripMargin = toolStrip.GripMargin; + maxSize.Height += gripMargin.Vertical + toolStrip.Grip.GripThickness; + } + + // note here the difference in vertical - we want the strings to fit perfectly so we're not going to constrain by the specified size. + if (toolStrip.Size != maxSize) + { + CommonProperties.xClearPreferredSizeCache(toolStrip); + } + return maxSize; + } + + private static Size GetPreferredItemSize(ToolStripItem item) { + return item.AutoSize ? item.GetPreferredSize(Size.Empty) : item.Size; + } +#endregion +#region MeasurementGraphics + // + internal static Graphics GetMeasurementGraphics() { + return WindowsFormsUtils.CreateMeasurementGraphics(); + } +#endregion + /// + /// Summary of GetSelectedItem. + /// + internal ToolStripItem GetSelectedItem() { + ToolStripItem selectedItem = null; + + for (int i = 0; i < DisplayedItems.Count; i++) { + if (DisplayedItems[i].Selected) { + selectedItem = DisplayedItems[i]; + } + } + + return selectedItem; + } + /// + /// Retrieves the current value of the specified bit in the control's state. + /// + internal bool GetToolStripState(int flag) { + return (toolStripState & flag) != 0; + } + + internal virtual ToolStrip GetToplevelOwnerToolStrip() { + return this; + } + + /// In the case of a + /// toolstrip -> toolstrip + /// contextmenustrip -> the control that is showing it + /// toolstripdropdown -> top most toolstrip + internal virtual Control GetOwnerControl() { + return this; + } + + + private void HandleMouseLeave() { + // If we had a particular item that was "entered" + // notify it that we have left. + if (lastMouseActiveItem != null) { + if (!DesignMode) { + MouseHoverTimer.Cancel(lastMouseActiveItem); + } + try { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "firing mouse leave on " + lastMouseActiveItem.ToString()); + lastMouseActiveItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseLeave); + } + finally { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "setting last active item to null"); + lastMouseActiveItem = null; + } + } + ToolStripMenuItem.MenuTimer.HandleToolStripMouseLeave(this); + } + + /// + /// Summary of HandleItemClick. + /// + internal void HandleItemClick(ToolStripItem dismissingItem) { + ToolStripItemClickedEventArgs e= new ToolStripItemClickedEventArgs(dismissingItem); + OnItemClicked(e); + // VSWhidbey 395136 - ensure both the overflow and the main toolstrip fire ItemClick event + // otherwise the overflow wont dismiss. + if (!IsDropDown && dismissingItem.IsOnOverflow) { + OverflowButton.DropDown.HandleItemClick(dismissingItem); + } + } + + internal virtual void HandleItemClicked(ToolStripItem dismissingItem) { + // post processing after the click has happened. + /*if (ContainsFocus && !Focused) { + RestoreFocusInternal(); + }*/ + ToolStripDropDownItem item = dismissingItem as ToolStripDropDownItem; + if (item != null && !item.HasDropDownItems) + { + KeyboardActive = false; + } + + } + + private void HookStaticEvents(bool hook) { + if (hook) { + if (!alreadyHooked) { + try { + ToolStripManager.RendererChanged += new EventHandler(OnDefaultRendererChanged); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally{ + alreadyHooked = true; + } + } + } + else if (alreadyHooked) { + try { + ToolStripManager.RendererChanged -= new EventHandler(OnDefaultRendererChanged); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally { + alreadyHooked = false; + } + + } + } + + //initialize winbar + private void InitializeRenderer(ToolStripRenderer renderer) { + // wrap this in a LayoutTransaction so that if they change sizes + // in this method we've suspended layout. + using(LayoutTransaction.CreateTransactionIf(AutoSize, this, this, PropertyNames.Renderer)) { + renderer.Initialize(this); + for (int i = 0; i < this.Items.Count; i++) { + renderer.InitializeItem(this.Items[i]); + } + } + Invalidate( this.Controls.Count > 0); + } + + + // sometimes you only want to force a layout if the winbar is visible. + private void InvalidateLayout() { + if (IsHandleCreated) { + LayoutTransaction.DoLayout(this, this, null); + } + } + internal void InvalidateTextItems() { + using (new LayoutTransaction(this, this, "ShowKeyboardFocusCues", /*PerformLayout=*/Visible)) { + for (int j = 0; j < DisplayedItems.Count; j++) { + if (((DisplayedItems[j].DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text)){ + DisplayedItems[j].InvalidateItemLayout("ShowKeyboardFocusCues"); + } + } + } + } + /// + /// + /// Summary of IsInputKey. + /// + /// + protected override bool IsInputKey(Keys keyData) { + ToolStripItem item = this.GetSelectedItem(); + if ((item != null) && item.IsInputKey(keyData)) { + return true; + } + return base.IsInputKey(keyData); + } + /// + /// + /// Summary of IsInputChar. + /// + /// + protected override bool IsInputChar(char charCode) { + ToolStripItem item = this.GetSelectedItem(); + if ((item != null) && item.IsInputChar(charCode)) { + return true; + } + return base.IsInputChar(charCode); + } + + private static bool IsPseudoMnemonic(char charCode, string text) { + if (!String.IsNullOrEmpty(text)) { + if (!WindowsFormsUtils.ContainsMnemonic(text)) { + char charToCompare = Char.ToUpper(charCode, CultureInfo.CurrentCulture); + char firstLetter = Char.ToUpper(text[0], CultureInfo.CurrentCulture); + if (firstLetter == charToCompare ||(Char.ToLower(charCode, CultureInfo.CurrentCulture) == Char.ToLower(text[0], CultureInfo.CurrentCulture)) ) { + return true; + } + } + } + return false; + } + + /// Force an item to be painted immediately, rather than waiting for WM_PAINT to occur. + internal void InvokePaintItem(ToolStripItem item) { + // Force a WM_PAINT to happen NOW. + Invalidate(item.Bounds); + Update(); + } + /// + /// Gets or sets the that contains the displayed on a label control + /// + private void ImageListRecreateHandle(object sender, EventArgs e) { + Invalidate(); + } + + /// + /// This override fires the LocationChanging event if + /// 1) We are not currently Rafting .. since this cause this infinite times... + /// 2) If we havent been called once .. Since the "LocationChanging" is listened to by the RaftingCell and calls "JOIN" which may call us back. + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + Point location = this.Location; + + if (!IsCurrentlyDragging && !IsLocationChanging && IsInToolStripPanel) + { + ToolStripLocationCancelEventArgs cae = new ToolStripLocationCancelEventArgs(new Point(x, y), false); + try { + if (location.X != x || location.Y != y) { + SetToolStripState(STATE_LOCATIONCHANGING, true); + OnLocationChanging(cae); + } + + if (!cae.Cancel) { + base.SetBoundsCore(x, y, width, height, specified); + } + } + finally { + SetToolStripState(STATE_LOCATIONCHANGING, false); + } + } + else { + if (IsCurrentlyDragging) { + Region transparentRegion = Renderer.GetTransparentRegion(this); + if (transparentRegion != null && (location.X != x || location.Y != y)) { + try { + Invalidate(transparentRegion); + Update(); + } + finally { + transparentRegion.Dispose(); + } + } + } + SetToolStripState(STATE_LOCATIONCHANGING, false); + base.SetBoundsCore(x, y, width, height, specified); + } + + } + + internal void PaintParentRegion(Graphics g, Region region) { + + } + + internal bool ProcessCmdKeyInternal(ref Message m, Keys keyData) { + return ProcessCmdKey(ref m, keyData); + } + + // This function will print to the PrinterDC. ToolStrip have there own buffered painting and doesnt play very well + // with the DC translations done by base Control class. Hence we do our own Painting and the BitBLT the DC into the printerDc. + // Refer to VsWhidbey : 400683. + internal override void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + using (Bitmap image = new Bitmap(bounds.Width, bounds.Height)) + using (Graphics g = Graphics.FromImage(image)) { + IntPtr imageHdc = g.GetHdc(); + //send the actual wm_print message + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.WM_PRINT, (IntPtr)imageHdc, + (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT)); + + //now BLT the result to the destination bitmap. + IntPtr desthDC = hDC.Handle; + SafeNativeMethods.BitBlt(new HandleRef(this, desthDC), bounds.X, bounds.Y, bounds.Width, bounds.Height, + new HandleRef(g, imageHdc), 0, 0, NativeMethods.SRCCOPY); + g.ReleaseHdcInternal(imageHdc); + } + } + + /// + /// + /// Summary of ProcessCmdKey. + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message m, Keys keyData) { + + if (ToolStripManager.IsMenuKey(keyData)) { + if (!IsDropDown && ToolStripManager.ModalMenuFilter.InMenuMode) { + ClearAllSelections(); + ToolStripManager.ModalMenuFilter.MenuKeyToggle = true; + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.ProcessCmdKey] Detected a second ALT keypress while in Menu Mode."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + + } + + // Give the ToolStripItem very first chance at + // processing keys (except for ALT handling) + ToolStripItem selectedItem = this.GetSelectedItem(); + if (selectedItem != null){ + if (selectedItem.ProcessCmdKey(ref m, keyData)) { + return true; + } + } + + foreach (ToolStripItem item in this.Items) { + if (item == selectedItem) { + continue; + } + if (item.ProcessCmdKey(ref m, keyData)) { + return true; + } + } + + + if (!IsDropDown) { + bool isControlTab = + (keyData & Keys.Control) == Keys.Control && (keyData & Keys.KeyCode) == Keys.Tab; + + if (isControlTab && !TabStop && HasKeyboardInput) { + bool handled = false; + if ((keyData & Keys.Shift) == Keys.None) { + handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/true); + } + else { + handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/false); + } + if (handled) { + return true; + } + } + } + return base.ProcessCmdKey(ref m, keyData); + } + + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN + /// keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. For the TAB key, the method selects the next control + /// on the form. For the arrow keys, + /// !!! + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + bool retVal = false; + + // Give the ToolStripItem first dibs + ToolStripItem item = this.GetSelectedItem(); + if (item != null){ + if(item.ProcessDialogKey(keyData)) { + return true; + } + } + + // if the ToolStrip receives an escape, then we + // should send the focus back to the last item that + // had focus. + bool hasModifiers = ((keyData & (Keys.Alt | Keys.Control)) != Keys.None); + + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Back: + // if it's focused itself, process. if it's not focused, make sure a child control + // doesnt have focus before processing + if (!ContainsFocus) { + // shift backspace/backspace work as backspace, which is the same as shift+tab + retVal = ProcessTabKey(false); + } + break; + case Keys.Tab: + // ctrl+tab does nothing + if (!hasModifiers){ + retVal = ProcessTabKey((keyData & Keys.Shift) == Keys.None); + } + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + retVal = ProcessArrowKey(keyCode); + break; + case Keys.Home: + SelectNextToolStripItem(null, /*forward =*/ true ); + retVal = true; + break; + case Keys.End: + SelectNextToolStripItem(null, /*forward =*/ false ); + retVal = true; + break; + case Keys.Escape: // escape and menu key should restore focus + // ctrl+esc does nothing + if (!hasModifiers && !TabStop){ + RestoreFocusInternal(); + retVal = true; + } + break; + + } + + + if (retVal) { + return retVal; + } + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] calling base"); + return base.ProcessDialogKey(keyData); + } + + internal virtual void ProcessDuplicateMnemonic(ToolStripItem item, char charCode) { + if (!CanProcessMnemonic()) { // Checking again for security... + return; + } + // SECREVIEW see toolstrip dropdown ProcessDuplicateMnemonic for special security implications + if (item != null) { + // SECREVIEW: SetFocusUnsafe as ProcessMnemonic has link demand for AWP. + SetFocusUnsafe(); + item.Select(); + } + } + /// + /// + /// + /// Rules for parsing mnemonics + /// PASS 1: Real mnemonics + /// Check items for the character after the &. If it matches, perform the click event or open the dropdown (in the case that it has dropdown items) + /// PASS 2: Fake mnemonics + /// Begin with the current selection and parse through the first character in the items in the menu. + /// If there is only one item that matches + /// perform the click event or open the dropdown (in the case that it has dropdown items) + /// Else + /// change the selection from the current selected item to the first item that matched. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // menus and toolbars only take focus on ALT + if (!CanProcessMnemonic()) { + return false; + } + if (Focused || ContainsFocus) { + return ProcessMnemonicInternal(charCode); + } + bool inMenuMode = ToolStripManager.ModalMenuFilter.InMenuMode; + if (!inMenuMode && Control.ModifierKeys == Keys.Alt) { + // This is the case where someone hasnt released the ALT key yet, but has pushed another letter. + // In some cases we can activate the menu that is not the MainMenuStrip... + // See VSWhidbey 501382 for more details. + return ProcessMnemonicInternal(charCode); + } + else if (inMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) { + return ProcessMnemonicInternal(charCode); + } + + // do not call base, as we dont want to walk through the controls collection and reprocess everything + // we should have processed in the displayed items collection. + return false; + + } + private bool ProcessMnemonicInternal(char charCode) { + if (!CanProcessMnemonic()) { // Checking again for security... + return false; + } + // at this point we assume we can process mnemonics as process mnemonic has filtered for use. + ToolStripItem startingItem = GetSelectedItem(); + int startIndex = 0; + if (startingItem != null) { + startIndex = DisplayedItems.IndexOf(startingItem); + } + startIndex = Math.Max(0, startIndex); + + ToolStripItem firstMatch = null; + bool foundMenuItem = false; + int index = startIndex; + + // PASS1, iterate through the real mnemonics + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem currentItem = DisplayedItems[index]; + + index = (index +1)%DisplayedItems.Count; + if (string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) { + continue; + } + // VSWhidbey 429513 - only items which display text should be processed + if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) { + continue; + } + // keep track whether we've found a menu item - we'll have to do a + // second pass for fake mnemonics in that case. + foundMenuItem = (foundMenuItem || (currentItem is ToolStripMenuItem)); + + if (Control.IsMnemonic(charCode,currentItem.Text)) { + if (firstMatch == null) { + firstMatch = currentItem; + } + else { + // we've found a second match - we should only change selection. + if (firstMatch == startingItem) { + // change the selection to be the second match as the first is already selected + ProcessDuplicateMnemonic(currentItem, charCode); + } + else { + ProcessDuplicateMnemonic(firstMatch, charCode); + } + // we've found two mnemonics, just return. + return true; + } + } + } + // We've found a singular match. + if (firstMatch != null) { + return firstMatch.ProcessMnemonic(charCode); + } + + if (!foundMenuItem) { + return false; + } + + index = startIndex; + + // 242501 MenuStrip parity: key presses should change selection if mnemonic not present + // if we havent found a mnemonic, cycle through the menu items and + // checbbbMk if we match. + + // PASS2, iterate through the pseudo mnemonics + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem currentItem = DisplayedItems[index]; + index = (index +1)%DisplayedItems.Count; + + // Menu items only + if (!(currentItem is ToolStripMenuItem) || string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) { + continue; + } + // VSWhidbey 429513 - only items which display text should be processed + if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) { + continue; + } + + + if (ToolStrip.IsPseudoMnemonic(charCode,currentItem.Text)) { + if (firstMatch == null) { + firstMatch = currentItem; + } + else { + // we've found a second match - we should only change selection. + if (firstMatch == startingItem) { + // change the selection to be the second match as the first is already selected + ProcessDuplicateMnemonic(currentItem, charCode); + } + else { + ProcessDuplicateMnemonic(firstMatch, charCode); + } + // we've found two mnemonics, just return. + return true; + } + } + } + + if (firstMatch != null) { + return firstMatch.ProcessMnemonic(charCode); + } + + // do not call base, as we dont want to walk through the controls collection and reprocess everything + // we should have processed in the displayed items collection. + return false; + } + + /// + /// + /// Summary of ProcessTabKey. + /// + /// + private bool ProcessTabKey(bool forward) { + if (TabStop) { + // ToolBar in tab-order parity + // this means we want the toolstrip in the normal tab order - which means it shouldnt wrap. + // First tab gets you into the toolstrip, second tab moves you on your way outside the container. + // arrow keys would continue to wrap. + return false; + } + else { + // TabStop = false + // this means we dont want the toolstrip in the normal tab order (default). + // We got focus to the toolstrip by putting focus into a control contained on the toolstrip or + // via a mnemonic e.g. Bold. In this case we want to wrap. + // arrow keys would continue to wrap + if (RightToLeft == RightToLeft.Yes) { + forward = !forward; + } + SelectNextToolStripItem(GetSelectedItem(), forward); + return true; + } + } + + /// + /// + /// Summary of ProcessArrowKey: this is more useful than overriding ProcessDialogKey because usually + /// the difference between ToolStrip/ToolStripDropDown is arrow key handling. ProcessDialogKey first gives + /// the selected ToolStripItem the chance to process the message... so really a proper inheritor would + /// call down to the base first. Unfortunately doing this would cause the the arrow keys would be eaten + /// in the base class. Instead we're providing a separate place to override all arrow key handling. + /// + internal virtual bool ProcessArrowKey(Keys keyCode) { + + bool retVal = false; + Debug.WriteLineIf(MenuAutoExpandDebug.TraceVerbose, "[ToolStrip.ProcessArrowKey] MenuTimer.Cancel called"); + ToolStripMenuItem.MenuTimer.Cancel(); + + switch (keyCode) { + case Keys.Left: + case Keys.Right: + retVal = ProcessLeftRightArrowKey(keyCode == Keys.Right); + break; + case Keys.Up: + case Keys.Down: + if (IsDropDown || Orientation != Orientation.Horizontal) { + ToolStripItem currentSel = GetSelectedItem(); + if (keyCode == Keys.Down) { + ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Down); + if (nextItem != null) { + ChangeSelection(nextItem); + retVal = true; + } + } + else { + ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Up); + if (nextItem != null){ + ChangeSelection(nextItem); + retVal = true; + } + } + } + break; + } + return retVal; + } + + /// + /// Process an arrowKey press by selecting the next control in the group + /// that the activeControl belongs to. + /// + /// + private bool ProcessLeftRightArrowKey(bool right) { + ToolStripItem selectedItem = GetSelectedItem(); + ToolStripItem nextItem = SelectNextToolStripItem(GetSelectedItem(), right); + return true; + } + + /// + /// Summary of NotifySelectionChange. + /// + /// + internal void NotifySelectionChange(ToolStripItem item) { + if (item == null) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] none should be selected"); + ClearAllSelections(); + } + else if (item.Selected) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] Notify selection change: " + item.ToString() + ": " + item.Selected.ToString()); + ClearAllSelectionsExcept(item); + } + } + + private void OnDefaultRendererChanged(object sender, EventArgs e) { + // callback from static event + if (GetToolStripState(STATE_USEDEFAULTRENDERER)) { + OnRendererChanged(e); + } + } + + protected virtual void OnBeginDrag(EventArgs e) { + SetToolStripState(STATE_DRAGGING, true); + Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?"); + Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?"); + + ClearAllSelections(); + UpdateToolTip(null); // supress the tooltip. + EventHandler handler = (EventHandler)Events[EventBeginDrag]; + if (handler != null) handler(this,e); + } + + protected virtual void OnEndDrag(EventArgs e) { + SetToolStripState(STATE_DRAGGING, false); + Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?"); + Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?"); + Debug.Assert(ToolStripPanelRow == null || ToolStripPanelRow.ToolStripPanel.RowsInternal.Contains(ToolStripPanelRow), "Why are we in an orphaned row?"); + + EventHandler handler = (EventHandler)Events[EventEndDrag]; + if (handler != null) handler(this,e); + + } + + + + protected override void OnDockChanged(EventArgs e){ + base.OnDockChanged(e); + } + + /// + protected virtual void OnRendererChanged(EventArgs e) { + InitializeRenderer(Renderer); + + EventHandler handler = (EventHandler)Events[EventRendererChanged]; + if (handler != null) handler(this,e); + + } + /// + /// + /// Summary of OnEnabledChanged. + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + + // notify items that the parent has changed + for (int i = 0; i < this.Items.Count; i++) { + if (Items[i] != null && Items[i].ParentInternal == this) { + Items[i].OnParentEnabledChanged(e); + } + } + + } + + + internal void OnDefaultFontChanged() { + defaultFont = null; + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + ToolStripManager.CurrentDpi = DeviceDpi; + defaultFont = ToolStripManager.DefaultFont; + } + if (!IsFontSet()) { + OnFontChanged(EventArgs.Empty); + } + } + + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + for (int i = 0; i < this.Items.Count; i++) { + Items[i].OnOwnerFontChanged(e); + } + } + + protected override void OnInvalidated(InvalidateEventArgs e) { + base.OnInvalidated(e); +#if false +// DEBUG code which is helpful for FlickerFest debugging. + if (FlickerDebug.TraceVerbose) { + string name = this.Name; + if (string.IsNullOrEmpty(name)) { + if (IsDropDown) { + ToolStripItem item = ((ToolStripDropDown)this).OwnerItem; + if (item != null && item.Name != null) { + name = item.Name = ".DropDown"; + } + } + if (string.IsNullOrEmpty(name)) { + name = this.GetType().Name; + } + } + // for debugging VS we want to filter out the propgrid toolstrip + Debug.WriteLineIf(!(this.ParentInternal is PropertyGrid), "Invalidate called on: " + name + new StackTrace().ToString()); + } +#endif + } + /// + /// + /// Summary of OnHandleCreated. + /// + protected override void OnHandleCreated(EventArgs e) { + if ((this.AllowDrop || this.AllowItemReorder) && (DropTargetManager != null)) { + this.DropTargetManager.EnsureRegistered(this); + } + + // calling control's (in base) version AFTER we register our DropTarget, so it will + // listen to us instead of control's implementation + base.OnHandleCreated(e); + } + + /// + /// + /// Summary of OnHandleDestroyed. + /// + protected override void OnHandleDestroyed(EventArgs e) { + if (DropTargetManager != null) { + // Make sure we unregister ourselves as a drop target + this.DropTargetManager.EnsureUnRegistered(this); + } + base.OnHandleDestroyed(e); + } + + /// + protected internal virtual void OnItemAdded(ToolStripItemEventArgs e) { + DoLayoutIfHandleCreated(e); + + if (!HasVisibleItems && e.Item != null && ((IArrangedElement)e.Item).ParticipatesInLayout) { + // VSWhidbey 441403 + // in certain cases, we may not have laid out yet (e.g. a dropdown may not layout until + // it becomes visible.) We will recalculate this in SetDisplayedItems, but for the moment + // if we find an item that ParticipatesInLayout, mark us as having visible items. + HasVisibleItems = true; + } + + ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemAdded]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Called when an item has been clicked on the winbar. + /// + protected virtual void OnItemClicked(ToolStripItemClickedEventArgs e) { + ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventItemClicked]; + if (handler != null) handler(this, e); + + } + + /// + protected internal virtual void OnItemRemoved(ToolStripItemEventArgs e) { + + // clear cached item states. + OnItemVisibleChanged(e, /*performlayout*/true); + + ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemRemoved]; + if (handler != null) handler(this, e); + } + + + internal void OnItemVisibleChanged(ToolStripItemEventArgs e, bool performLayout) { + + // clear cached item states. + if (e.Item == lastMouseActiveItem) { + lastMouseActiveItem = null; + } + if (e.Item == LastMouseDownedItem) { + lastMouseDownedItem = null; + } + if (e.Item == currentlyActiveTooltipItem) { + UpdateToolTip(null); + } + if (performLayout) { + DoLayoutIfHandleCreated(e); + } + } + /// + protected override void OnLayout(LayoutEventArgs e) { + this.LayoutRequired = false; + + // we need to do this to prevent autosizing to happen while we're reparenting. + ToolStripOverflow overflow = GetOverflow(); + if (overflow != null) { + overflow.SuspendLayout(); + toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size); + } + + for (int j = 0; j < Items.Count; j++) { + Items[j].OnLayout(e); + } + + base.OnLayout(e); + SetDisplayedItems(); + OnLayoutCompleted(EventArgs.Empty); + Invalidate(); + + if (overflow != null) { + overflow.ResumeLayout(); + } + } + + /// + protected virtual void OnLayoutCompleted(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventLayoutCompleted]; + if (handler != null) handler(this, e); + } + + /// + protected virtual void OnLayoutStyleChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventLayoutStyleChanged]; + if (handler != null) handler(this, e); + } + + /// + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + ClearAllSelections(); + } + + protected override void OnLeave(EventArgs e) { + base.OnLeave(e); + if (!IsDropDown) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "uninstalling RestoreFocusFilter"); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter); + } + } + /// + internal virtual void OnLocationChanging(ToolStripLocationCancelEventArgs e) { + ToolStripLocationCancelEventHandler handler = (ToolStripLocationCancelEventHandler)Events[EventLocationChanging]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Delegate mouse down to the winbar and its affected items + /// + protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) { + + // NEVER use this directly from another class. Always use GetMouseID so that + // 0 is not returned to another class. + mouseDownID++; + + ToolStripItem item = GetItemAt(mea.X, mea.Y); + if (item != null) { + if (!IsDropDown && (!(item is ToolStripDropDownItem))){ + // set capture only when we know we're not on a dropdown (already effectively have capture due to modal menufilter) + // and the item in question requires the mouse to be in the same item to be clicked. + SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, true); + this.CaptureInternal = true; + } + MenuAutoExpand = true; + + if (mea != null) { + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + } + lastMouseDownedItem = item; + item.FireEvent(mea, ToolStripItemEventType.MouseDown); + } + else { + base.OnMouseDown(mea); + } + + } + + + /// + /// + /// Delegate mouse moves to the winbar and its affected items + /// + protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose,"OnMouseMove called"); + + ToolStripItem item = GetItemAt(mea.X, mea.Y); + + if (!Grip.MovingToolStrip) { + + // If we had a particular item that was "entered" + // notify it that we have entered. It's fair to put + // this in the MouseMove event, as MouseEnter is fired during + // control's WM_MOUSEMOVE. Waiting until this event gives us + // the actual coordinates. + + + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Item to get mouse move: {0}", (item == null) ? "null" : item.ToString())); + if (item != lastMouseActiveItem) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "This is a new item - last item to get was {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString())); + + // notify the item that we've moved on + HandleMouseLeave(); + + // track only items that dont get mouse events themselves. + lastMouseActiveItem = (item is ToolStripControlHost) ? null : item; + + if (lastMouseActiveItem != null) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseEnter on: {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString())); + item.FireEvent(new System.EventArgs(), ToolStripItemEventType.MouseEnter); + } + // + + if (!DesignMode) { + MouseHoverTimer.Start(lastMouseActiveItem); + } + + + + } + } + else { + item = this.Grip; + } + if (item != null) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (item == null) ? "null" : item.ToString())); + + // Fire mouse move on the item + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + item.FireEvent(mea, ToolStripItemEventType.MouseMove); + } + else { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (this == null) ? "null" : this.ToString())); + + base.OnMouseMove(mea); + } + } + + + + /// + /// + /// Delegate mouse leave to the winbar and its affected items + /// + protected override void OnMouseLeave(System.EventArgs e) { + HandleMouseLeave(); + base.OnMouseLeave(e); + } + + + /// + protected override void OnMouseCaptureChanged(System.EventArgs e) { + if (!GetToolStripState(STATE_SUSPENDCAPTURE)) { + // while we're showing a feedback rect, dont cancel moving the toolstrip. + Grip.MovingToolStrip = false; + } + ClearLastMouseDownedItem(); + + + + base.OnMouseCaptureChanged(e); + } + + /// + /// + /// Delegate mouse up to the winbar and its affected items + /// + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) { + + ToolStripItem item = (Grip.MovingToolStrip) ? Grip : GetItemAt(mea.X, mea.Y); + + if (item != null) { + if (mea != null) { + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + } + item.FireEvent(mea, ToolStripItemEventType.MouseUp); + } + else { + base.OnMouseUp(mea); + } + ClearLastMouseDownedItem(); + + } + + + + + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + + Graphics toolstripGraphics = e.Graphics; + Size bitmapSize = this.largestDisplayedItemSize; + bool excludedTransparentRegion = false; + + Rectangle viewableArea = this.DisplayRectangle; + Region transparentRegion = Renderer.GetTransparentRegion(this); + + try { + + // Paint the items + // The idea here is to let items pretend they are controls. + // they should get paint events at 0,0 and have proper clipping regions + // set up for them. We cannot use g.TranslateTransform as that does + // not translate the GDI world, and things like XP Visual Styles and the + // TextRenderer only know how to speak GDI. + // + // The previous appropach was to set up the GDI clipping region and allocate a graphics + // from that, but that meant we were allocating graphics objects left and right, which + // turned out to be slow. + // + // So now we allocate an offscreen bitmap of size == MaxItemSize, copy the background + // of the toolstrip into that bitmap, then paint the item on top of the bitmap, then copy + // the contents of the bitmap back onto the toolstrip. This gives us our paint event starting + // at 0,0. Combine this with double buffering of the toolstrip and the entire toolstrip is updated + // after returning from this function. + if (!LayoutUtils.IsZeroWidthOrHeight(bitmapSize)) { + // cant create a 0x0 bmp. + + // Supporting RoundedEdges... + // we've got a concept of a region that we shouldnt paint (the TransparentRegion as specified in the Renderer). + // in order to support this we're going to intersect that region with the clipping region. + // this new region will be excluded during the guts of OnPaint, and restored at the end of OnPaint. + if (transparentRegion != null) { + // only use the intersection so we can easily add back in the bits we took out at the end. + transparentRegion.Intersect(toolstripGraphics.Clip); + toolstripGraphics.ExcludeClip(transparentRegion); + excludedTransparentRegion = true; + } + + // Preparing for painting the individual items... + // using WindowsGraphics here because we want to preserve the clipping information. + + // calling GetHdc by itself does not set up the clipping info. + using(WindowsGraphics toolStripWindowsGraphics = WindowsGraphics.FromGraphics(toolstripGraphics, ApplyGraphicsProperties.Clipping)){ + // get the cached item HDC. + HandleRef toolStripHDC = new HandleRef(this, toolStripWindowsGraphics.GetHdc()); + HandleRef itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize); + + Graphics itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle); + try { + // Painting the individual items... + // iterate through all the items, painting them + // one by one into the compatible offscreen DC, and then copying + // them back onto the main toolstrip. + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem item = DisplayedItems[i]; + if (item != null) { // CONSIDER ! item is ToolStripControlHost + Rectangle clippingRect = e.ClipRectangle; + Rectangle bounds = item.Bounds; + + if (!IsDropDown && item.Owner == this) { + // owned items should not paint outside the client + // area. (this is mainly to prevent obscuring the grip + // and overflowbutton - ToolStripDropDownMenu places items + // outside of the display rectangle - so we need to allow for this + // in dropdoowns). + clippingRect.Intersect(viewableArea); + } + + // get the intersection of these two. + clippingRect.Intersect(bounds); + + if (LayoutUtils.IsZeroWidthOrHeight(clippingRect)) { + continue; // no point newing up a graphics object if there's nothing to paint. + } + + Size itemSize = item.Size; + + // check if our item buffer is large enough to handle. + if (!LayoutUtils.AreWidthAndHeightLarger(bitmapSize, itemSize)) { + // the cached HDC isnt big enough for this item. make it bigger. + this.largestDisplayedItemSize = itemSize; + bitmapSize = itemSize; + // dispose the old graphics - create a new, bigger one. + itemGraphics.Dispose(); + + // calling this should take the existing DC and select in a bigger bitmap. + itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize); + + // allocate a new graphics. + itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle); + + } + // since the item graphics object will have 0,0 at the + // corner we need to actually shift the origin of the rect over + // so it will be 0,0 too. + clippingRect.Offset(-bounds.X, -bounds.Y); + + // PERF - consider - we only actually need to copy the clipping rect. + // copy the background from the toolstrip onto the offscreen bitmap + SafeNativeMethods.BitBlt(itemHDC, 0, 0, item.Size.Width, item.Size.Height, toolStripHDC, item.Bounds.X, item.Bounds.Y, NativeMethods.SRCCOPY); + + // paint the item into the offscreen bitmap + using (PaintEventArgs itemPaintEventArgs = new PaintEventArgs(itemGraphics, clippingRect)) { + item.FireEvent(itemPaintEventArgs, ToolStripItemEventType.Paint); + } + + // copy the item back onto the toolstrip + SafeNativeMethods.BitBlt(toolStripHDC, item.Bounds.X, item.Bounds.Y, item.Size.Width, item.Size.Height, itemHDC, 0, 0, NativeMethods.SRCCOPY); + + } + } + } + finally { + if (itemGraphics != null) { + itemGraphics.Dispose(); + } + } + } + + } + + // Painting the edge effects... + // These would include things like (shadow line on the bottom, some overflow effects) + Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(toolstripGraphics, this)); + + // Restoring the clip region to its original state... + // the transparent region should be added back in as the insertion mark should paint over it. + if (excludedTransparentRegion) { + toolstripGraphics.SetClip(transparentRegion,CombineMode.Union); + } + + // Paint the item re-order insertion mark... + // This should ignore the transparent region and paint + // over the entire area. + PaintInsertionMark(toolstripGraphics); + } + finally { + if (transparentRegion != null) { + transparentRegion.Dispose(); + } + } + } + + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + + // normally controls just need to do handle recreation, but ToolStrip does it based on layout of items. + using(new LayoutTransaction(this, this, PropertyNames.RightToLeft)) { + for (int i = 0; i < Items.Count; i++) { + Items[i].OnParentRightToLeftChanged(e); + } + if (toolStripOverflowButton != null) { + toolStripOverflowButton.OnParentRightToLeftChanged(e); + } + if (toolStripGrip != null) { + toolStripGrip.OnParentRightToLeftChanged(e); + } + } + + + } + + /// + /// + /// Inheriting classes should override this method to handle the erase + /// background request from windows. It is not necessary to call + /// base.onPaintBackground, however if you do not want the default + /// Windows behavior you must set event.handled to true. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaintBackground(PaintEventArgs e) { + base.OnPaintBackground(e); + + Graphics g = e.Graphics; + GraphicsState graphicsState = g.Save(); + try { + using (Region transparentRegion = Renderer.GetTransparentRegion(this)) { + if (transparentRegion != null) { + EraseCorners(e, transparentRegion); + g.ExcludeClip(transparentRegion); + } + } + Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(g, this)); + + } + finally { + if (graphicsState != null) { + g.Restore(graphicsState); + } + } + } + + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + if (!Disposing && !IsDisposed) { + HookStaticEvents(Visible); + } + } + + + + private void EraseCorners(PaintEventArgs e, Region transparentRegion) { + if (transparentRegion != null) { + PaintTransparentBackground(e, ClientRectangle, transparentRegion); + } + } + + /// + /// + /// Summary of OnPaint. + /// + internal protected virtual void OnPaintGrip(System.Windows.Forms.PaintEventArgs e) { + + Renderer.DrawGrip(new ToolStripGripRenderEventArgs(e.Graphics, this)); + + PaintEventHandler handler = (PaintEventHandler)Events[EventPaintGrip]; + if (handler != null) handler(this,e); + + + } + + /// + protected override void OnScroll(ScrollEventArgs se) { + + if (se.Type != ScrollEventType.ThumbTrack && se.NewValue != se.OldValue) { + ScrollInternal(se.OldValue - se.NewValue); + } + base.OnScroll(se); + + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { + switch (e.Category) { + case UserPreferenceCategory.Window: + OnDefaultFontChanged(); + break; + case UserPreferenceCategory.General: + InvalidateTextItems(); + break; + } + + } + + protected override void OnTabStopChanged(EventArgs e) { + // VSWhidbey 442518 - SelectNextControl can select non-tabstop things. + // we need to prevent this by changing the value of "CanSelect" + SetStyle(ControlStyles.Selectable, TabStop); + base.OnTabStopChanged(e); + } + + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on. + /// + /// Old DPI value + /// New DPI value + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + if (deviceDpiOld != deviceDpiNew) { + ToolStripManager.CurrentDpi = deviceDpiNew; + defaultFont = ToolStripManager.DefaultFont; + + // We need to take care of this control. + ResetScaling(deviceDpiNew); + + // We need to scale the one Grip per ToolStrip as well (if present). + if (toolStripGrip != null) { + toolStripGrip.ToolStrip_RescaleConstants(deviceDpiOld, deviceDpiNew); + } + + // We need to delegate this "event" to the Controls/Components, which are + // not directly affected by this, but need to consume. + rescaleConstsCallbackDelegate?.Invoke(deviceDpiOld, deviceDpiNew); + } + } + } + + /// + /// Resets the scaling (only in PerMonitorV2 scenarios). + /// Do only call from code which is quirked with PerMonitorV2 quirks for the ToolStrip. + /// + /// The new DPI passed by WmDpiChangedBeforeParent. + internal virtual void ResetScaling(int newDpi) { + iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi); + iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi); + insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, newDpi); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, newDpi); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, newDpi); + imageScalingSize = new Size(iconWidth, iconHeight); + } + + /// + /// Paints the I beam when items are being reordered + /// + internal void PaintInsertionMark(Graphics g) { + if (lastInsertionMarkRect != Rectangle.Empty) { + int widthOfBeam = insertionBeamWidth; + if (Orientation == Orientation.Horizontal) { + int start = lastInsertionMarkRect.X; + int verticalBeamStart = start + 2; + + // draw two vertical lines + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(verticalBeamStart, lastInsertionMarkRect.Y), new Point(verticalBeamStart, lastInsertionMarkRect.Bottom-1), // first vertical line + new Point(verticalBeamStart+1, lastInsertionMarkRect.Y), new Point(verticalBeamStart+1, lastInsertionMarkRect.Bottom-1), //second vertical line + }); + // then two top horizontal + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(start, lastInsertionMarkRect.Bottom-1), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Bottom-1), //bottom line + new Point(start+1, lastInsertionMarkRect.Bottom -2), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Bottom-2),//bottom second line + }); + // then two bottom horizontal + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(start, lastInsertionMarkRect.Y), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Y), //top line + new Point(start+1, lastInsertionMarkRect.Y+1), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Y+1)//top second line + }); + } + else { + + widthOfBeam = insertionBeamWidth; + int start = lastInsertionMarkRect.Y; + int horizontalBeamStart = start + 2; + + // draw two horizontal lines + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.X, horizontalBeamStart), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart), // first vertical line + new Point(lastInsertionMarkRect.X, horizontalBeamStart+1), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart+1), //second vertical line + }); + // then two left vertical + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.X, start), new Point(lastInsertionMarkRect.X, start + widthOfBeam-1), //left line + new Point(lastInsertionMarkRect.X+1, start+1), new Point(lastInsertionMarkRect.X+1, start + widthOfBeam-2), //second left line + }); + // then two right vertical + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.Right-1, start), new Point(lastInsertionMarkRect.Right-1, start + widthOfBeam-1), //right line + new Point(lastInsertionMarkRect.Right-2, start+1), new Point(lastInsertionMarkRect.Right-2, start + widthOfBeam-2), //second right line + }); + } + + } + } + + /// + /// Paints the I beam when items are being reordered + /// + internal void PaintInsertionMark(Rectangle insertionRect) { + if (lastInsertionMarkRect != insertionRect) { + ClearInsertionMark(); + lastInsertionMarkRect = insertionRect; + this.Invalidate(insertionRect); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public new Control GetChildAtPoint(Point point) { + return base.GetChildAtPoint(point); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public new Control GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue) { + return base.GetChildAtPoint(pt, skipValue); + } + + // GetNextControl for ToolStrip should always return null + // we do our own tabbing/etc - this allows us to pretend + // we dont have child controls. + internal override Control GetFirstChildControlInTabOrder(bool forward) { + return null; + } + /// + /// + /// Finds the ToolStripItem contained within a specified client coordinate point + /// If item not found - returns null + /// + public ToolStripItem GetItemAt(int x, int y) { + return GetItemAt(new Point(x,y)); + } + + + /// + /// + /// Finds the ToolStripItem contained within a specified client coordinate point + /// If item not found - returns null + /// + public ToolStripItem GetItemAt(Point point) { + + Rectangle comparisonRect = new Rectangle(point, onePixel); + Rectangle bounds; + + // Check the last item we had the mouse over + if (lastMouseActiveItem != null) { + bounds = lastMouseActiveItem.Bounds; + + if (bounds.IntersectsWith(comparisonRect) && lastMouseActiveItem.ParentInternal == this) { + return this.lastMouseActiveItem; + } + } + + + // Walk the ToolStripItem collection + for (int i = 0; i < this.DisplayedItems.Count; i++) { + if (DisplayedItems[i] == null || DisplayedItems[i].ParentInternal != this) { + continue; + } + + bounds = DisplayedItems[i].Bounds; + + // inflate the grip so it is easier to access + if (toolStripGrip != null && DisplayedItems[i] == toolStripGrip) { + bounds = LayoutUtils.InflateRect(bounds, GripMargin); + } + if (bounds.IntersectsWith(comparisonRect)) { + return this.DisplayedItems[i]; + } + } + + return null; + + } + + private void RestoreFocusInternal(bool wasInMenuMode) { + // VSWhidbey 503500 + // This is called from the RestoreFocusFilter. If the state of MenuMode has changed + // since we posted this message, we do not know enough information about whether + // we should exit menu mode. + if (wasInMenuMode == ToolStripManager.ModalMenuFilter.InMenuMode) { + RestoreFocusInternal(); + } + } + + /// RestoreFocus - returns focus to the control who activated us + /// See comment on SnapFocus + /// + internal void RestoreFocusInternal() { + ToolStripManager.ModalMenuFilter.MenuKeyToggle = false; + ClearAllSelections(); + lastMouseDownedItem = null; + + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Someone has called RestoreFocus, exiting MenuMode."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + + if (!IsDropDown) { + // reset menu auto expansion. + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Setting menu auto expand to false"); + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] uninstalling RestoreFocusFilter"); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter); + + MenuAutoExpand = false; + + if (!DesignMode && !TabStop && (Focused || ContainsFocus)) { + RestoreFocus(); + } + + } + + // this matches the case where you click on a toolstrip control host + // then tab off of it, then hit ESC. ESC would "restore focus" and + // we should cancel keyboard activation if this method has cancelled focus. + if (KeyboardActive && !Focused && !ContainsFocus) { + KeyboardActive = false; + } + + } + + // override if you want to control (when TabStop = false) where the focus returns to + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void RestoreFocus() { + + bool focusSuccess = false; + + if ((hwndThatLostFocus != IntPtr.Zero) && (hwndThatLostFocus != this.Handle)) { + Control c = Control.FromHandleInternal(hwndThatLostFocus); + + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip RestoreFocus]: Will Restore Focus to: " + WindowsFormsUtils.GetControlInformation(hwndThatLostFocus)); + hwndThatLostFocus = IntPtr.Zero; + + if ((c != null) && c.Visible) { + focusSuccess = c.FocusInternal(); + } + } + hwndThatLostFocus = IntPtr.Zero; + + if (!focusSuccess) { + // clear out the focus, we have focus, we're not supposed to anymore. + UnsafeNativeMethods.SetFocus(NativeMethods.NullHandleRef); + } + } + + internal virtual void ResetRenderMode() { + RenderMode = ToolStripRenderMode.ManagerRenderMode; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetMinimumSize() { + CommonProperties.SetMinimumSize(this, new Size(-1,-1)); + } + + private void ResetGripMargin() { + GripMargin = Grip.DefaultMargin; + } + + internal void ResumeCaputureMode() { + SetToolStripState(STATE_SUSPENDCAPTURE, false); + } + + internal void SuspendCaputureMode() { + SetToolStripState(STATE_SUSPENDCAPTURE, true); + } + + internal virtual void ScrollInternal(int delta) { + SuspendLayout(); + foreach (ToolStripItem item in this.Items) { + Point newLocation = item.Bounds.Location; + + newLocation.Y -= delta; + + SetItemLocation(item, newLocation); + } + + ResumeLayout(false); + Invalidate(); + } + + /// + /// + /// Summary of SetItemLocation + /// + /// + protected internal void SetItemLocation(ToolStripItem item, Point location) { + if (item == null) { + throw new ArgumentNullException("item"); + } + if (item.Owner != this) { + throw new NotSupportedException(SR.GetString(SR.ToolStripCanOnlyPositionItsOwnItems)); + } + + item.SetBounds(new Rectangle(location, item.Size)); + } + /// + /// + /// This is needed so that people doing custom layout engines can change the "Parent" property of the item. + /// + protected static void SetItemParent(ToolStripItem item, ToolStrip parent) { + item.Parent = parent; + } + + protected override void SetVisibleCore(bool visible) { + if (visible) { + SnapMouseLocation(); + } + else { + // make sure we reset selection - this is critical for close/reopen dropdowns. + if (!Disposing && !IsDisposed) { + ClearAllSelections(); + } + + // when we're not visible, clear off old item HDC. + CachedItemHdcInfo lastInfo = cachedItemHdcInfo; + cachedItemHdcInfo = null; + + lastMouseDownedItem = null; + + if (lastInfo != null) { + lastInfo.Dispose(); + } + } + base.SetVisibleCore(visible); + } + + internal bool ShouldSelectItem() { + // we only want to select the item if the cursor position has + // actually moved from when the window became visible. + + // We ALWAYS get a WM_MOUSEMOVE when the window is shown, + // which could accidentally change selection. + if (mouseEnterWhenShown == InvalidMouseEnter) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] MouseEnter already reset."); + return true; + } + + Point mousePosition = WindowsFormsUtils.LastCursorPoint; + if (mouseEnterWhenShown != mousePosition) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse position has changed - call Select()."); + mouseEnterWhenShown = InvalidMouseEnter; + return true; + } + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse hasnt actually moved yet."); + + return false; + } + /// + /// + /// Summary of Select. + /// + /// + /// + protected override void Select(bool directed, bool forward) { + bool correctParentActiveControl = true; + if (ParentInternal != null) { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + + if (c != null) { + c.ActiveControl = this; + correctParentActiveControl = (c.ActiveControl == this); + } + } + if (directed && correctParentActiveControl) { + SelectNextToolStripItem(null, forward); + } + } + + + /// + /// Summary of SelectNextToolStripItem. + /// + + + + internal ToolStripItem SelectNextToolStripItem(ToolStripItem start, bool forward) { + + ToolStripItem nextItem = GetNextItem(start, (forward) ? ArrowDirection.Right : ArrowDirection.Left, /*RTLAware=*/true); + ChangeSelection(nextItem); + return nextItem; + } + + // + // SECREVIEW: only call from places protected by a link demand for AllWindowsPermission. + // + internal void SetFocusUnsafe() { + if (TabStop) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Focusing toolstrip."); + FocusInternal(); + } + else { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Entering menu mode."); + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this, /*menuKeyPressed=*/false); + } + } + + private void SetupGrip() { + Rectangle gripRectangle = Rectangle.Empty; + Rectangle displayRect = DisplayRectangle; + + + if (Orientation == Orientation.Horizontal) { + // the display rectangle already knows about the padding and the grip rectangle width + // so place it relative to that. + gripRectangle.X = Math.Max(0, displayRect.X - Grip.GripThickness); + gripRectangle.Y = Math.Max(0,displayRect.Top - Grip.Margin.Top); + gripRectangle.Width = Grip.GripThickness; + gripRectangle.Height = displayRect.Height; + if (RightToLeft == RightToLeft.Yes) { + gripRectangle.X = ClientRectangle.Right - gripRectangle.Width - Grip.Margin.Horizontal; + gripRectangle.X += Grip.Margin.Left; + } + else { + gripRectangle.X -= Grip.Margin.Right; + } + } + else { + // vertical split stack mode + gripRectangle.X = displayRect.Left; + gripRectangle.Y = displayRect.Top - (Grip.GripThickness + Grip.Margin.Bottom); + gripRectangle.Width = displayRect.Width; + gripRectangle.Height = Grip.GripThickness; + } + + if (Grip.Bounds !=gripRectangle) { + Grip.SetBounds(gripRectangle); + } + + } + + /// + /// + /// + /// Sets the size of the auto-scroll margins. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + new public void SetAutoScrollMargin(int x, int y) { + base.SetAutoScrollMargin(x, y); + } + + internal void SetLargestItemSize(Size size) { + + if (toolStripOverflowButton != null && toolStripOverflowButton.Visible) { + size = LayoutUtils.UnionSizes(size, toolStripOverflowButton.Bounds.Size); + } + if (toolStripGrip != null && toolStripGrip.Visible) { + size = LayoutUtils.UnionSizes(size, toolStripGrip.Bounds.Size); + + } + largestDisplayedItemSize = size; + + } + + /// + /// + /// Afer we've performed a layout we need to reset the DisplayedItems and the OverflowItems collection. + /// OverflowItems are not supported in layouts other than ToolStripSplitStack + /// + protected virtual void SetDisplayedItems() { + this.DisplayedItems.Clear(); + this.OverflowItems.Clear(); + HasVisibleItems = false; + + Size biggestItemSize = Size.Empty; // used in determining OnPaint caching. + + + if (this.LayoutEngine is ToolStripSplitStackLayout) { + if (ToolStripGripStyle.Visible == GripStyle) { + this.DisplayedItems.Add(Grip); + SetupGrip(); + } + + // VSWhidbey 468104 + // for splitstack layout we re-arrange the items in the displayed items + // collection so that we can easily tab through them in natural order + Rectangle displayRect = this.DisplayRectangle; + int lastRightAlignedItem = -1; + + for (int pass=0; pass < 2; pass++) { + int j = 0; + + if (pass == 1 /*add right aligned items*/) { + j = lastRightAlignedItem; + } + + // add items to the DisplayedItem collection. + // in pass 0, we go forward adding the head (left) aligned items + // in pass 1, we go backward starting from the last (right) aligned item we found + + for (; j >= 0 && j < Items.Count; j = (pass == 0) ? j+1 : j-1){ + + ToolStripItem item = Items[j]; + ToolStripItemPlacement placement = item.Placement; + if (((IArrangedElement)item).ParticipatesInLayout) { + if (placement == ToolStripItemPlacement.Main) { + bool addItem = false; + if (pass == 0) { // Align.Left items + addItem = (item.Alignment == ToolStripItemAlignment.Left); + if (!addItem) { + // stash away this index so we dont have to iterate through the whole list again. + lastRightAlignedItem = j; + } + } + else if (pass == 1) { // Align.Right items + addItem = (item.Alignment == ToolStripItemAlignment.Right); + } + if (addItem) { + HasVisibleItems = true; + biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size); + this.DisplayedItems.Add(item); + } + } + else if (placement == ToolStripItemPlacement.Overflow && !(item is ToolStripSeparator)) { + if (item is ToolStripControlHost && this.OverflowButton.DropDown.IsRestrictedWindow) { + // VSWhidbey 436973: control hosts cannot be added to the overflow in the Internet + // just set the placement to None. + item.SetPlacement(ToolStripItemPlacement.None); + } + else { + this.OverflowItems.Add(item); + } + } + } + else { + item.SetPlacement(ToolStripItemPlacement.None); + } + } + + } + ToolStripOverflow overflow = GetOverflow(); + if (overflow != null) { + overflow.LayoutRequired = true; + } + if (OverflowItems.Count ==0) { + this.OverflowButton.Visible = false; + } + else if (CanOverflow){ + this.DisplayedItems.Add(OverflowButton); + } + + } + else { + // NOT a SplitStack layout. We dont change the order of the displayed items collection + // for custom keyboard handling override GetNextItem. + Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, "Setting Displayed Items: Current bounds: " + this.Bounds.ToString()); + Rectangle clientBounds = this.ClientRectangle; + + // for all other layout managers, we ignore overflow placement + bool allContained = true; + for (int j = 0; j < Items.Count; j++) { + ToolStripItem item = Items[j]; + if (((IArrangedElement)item).ParticipatesInLayout) + { + item.ParentInternal = this; + + bool boundsCheck = !IsDropDown; + bool intersects = item.Bounds.IntersectsWith(clientBounds); + + bool verticallyContained = clientBounds.Contains(clientBounds.X, item.Bounds.Top) && + clientBounds.Contains(clientBounds.X, item.Bounds.Bottom); + if (!verticallyContained) { + allContained = false; + } + + if (!boundsCheck || intersects) { + HasVisibleItems = true; + biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size); + this.DisplayedItems.Add(item); + item.SetPlacement(ToolStripItemPlacement.Main); + } + } + else { + item.SetPlacement(ToolStripItemPlacement.None); + } + + Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, item.ToString() + Items[j].Bounds); + } + + // For performance we calculate this here, since we're already iterating over the items. + // the only one who cares about it is ToolStripDropDownMenu (to see if it needs scroll buttons). + this.AllItemsVisible = allContained; + } + + SetLargestItemSize(biggestItemSize); + } + + + /// + /// Sets the current value of the specified bit in the control's state. + /// + internal void SetToolStripState(int flag, bool value) { + toolStripState = value? toolStripState | flag: toolStripState & ~flag; + } + + // remembers the current mouse location so we can determine + // later if we need to shift selection. + internal void SnapMouseLocation() { + mouseEnterWhenShown = WindowsFormsUtils.LastCursorPoint; + } + + + /// SnapFocus + /// When get focus to the toolstrip (and we're not participating in the tab order) + /// it's probably cause someone hit the ALT key. We need to remember who that was + /// so when we're done here we can RestoreFocus back to it. + /// + /// We're called from WM_SETFOCUS, and otherHwnd is the HWND losing focus. + /// + /// Required checks + /// - make sure it's not a dropdown + /// - make sure it's not a child control of this control. + /// - make sure the control is on this window + /// + private void SnapFocus(IntPtr otherHwnd) { +#if DEBUG + if (SnapFocusDebug.TraceVerbose) { + string stackTrace = new StackTrace().ToString(); + Regex regex = new Regex("FocusInternal"); + Debug.WriteLine(!regex.IsMatch(stackTrace), "who is setting focus to us?"); + } +#endif + // we need to know who sent us focus so we know who to send it back to later. + + if (!TabStop && !IsDropDown) { + bool snapFocus = false; + if (Focused && (otherHwnd != this.Handle)) { + // the case here is a label before a combo box calling FocusInternal in ProcessMnemonic. + // we'll filter out children later. + snapFocus = true; + } + else if (!ContainsFocus && !Focused) { + snapFocus =true; + } + + if (snapFocus) { + // remember the current mouse position so that we can check later if it actually moved + // otherwise we'd unexpectedly change selection to whatever the cursor was over at this moment. + SnapMouseLocation(); + + // start auto expanding for keyboard and mouse. + // MenuAutoExpand = true; + + HandleRef thisHandle = new HandleRef(this, this.Handle); + HandleRef otherHandle = new HandleRef(null, otherHwnd); + + // make sure the otherHandle is not a child of thisHandle + if ((thisHandle.Handle != otherHandle.Handle) && + !UnsafeNativeMethods.IsChild(thisHandle, otherHandle)) { + + // make sure the root window of the otherHwnd is the same as + // the root window of thisHwnd. + HandleRef thisHwndRoot = WindowsFormsUtils.GetRootHWnd(this); + HandleRef otherHwndRoot = WindowsFormsUtils.GetRootHWnd(otherHandle); + + if (thisHwndRoot.Handle == otherHwndRoot.Handle && (thisHwndRoot.Handle != IntPtr.Zero)) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip SnapFocus]: Caching for return focus:" + WindowsFormsUtils.GetControlInformation(otherHandle.Handle)); + // we know we're in the same window heirarchy. + hwndThatLostFocus = otherHandle.Handle; + } + } + } + } + + } + + // when we're control tabbing around we need to remember the original + // thing that lost focus. + internal void SnapFocusChange(ToolStrip otherToolStrip) { + otherToolStrip.hwndThatLostFocus = this.hwndThatLostFocus; + } + + private bool ShouldSerializeDefaultDropDownDirection() { + return (toolStripDropDownDirection != ToolStripDropDownDirection.Default); + } + + internal virtual bool ShouldSerializeLayoutStyle() { + return layoutStyle != ToolStripLayoutStyle.StackWithOverflow; + } + + internal override bool ShouldSerializeMinimumSize() { + Size invalidDefaultSize = new Size(-1,-1); + return (CommonProperties.GetMinimumSize(this, invalidDefaultSize) != invalidDefaultSize); + } + + private bool ShouldSerializeGripMargin() { + return GripMargin != DefaultGripMargin; + } + + internal virtual bool ShouldSerializeRenderMode() { + // We should NEVER serialize custom. + return (RenderMode != ToolStripRenderMode.ManagerRenderMode && RenderMode != ToolStripRenderMode.Custom); + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(base.ToString()); + sb.Append(", Name: "); + sb.Append(this.Name); + sb.Append(", Items: ").Append(this.Items.Count); + return sb.ToString(); + } + + internal void UpdateToolTip(ToolStripItem item) { + if (ShowItemToolTips) { + + if (item != currentlyActiveTooltipItem && ToolTip != null) { + + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + ToolTip.Hide(this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + if (AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip.Active = false; + } + + currentlyActiveTooltipItem = item; + + + if (currentlyActiveTooltipItem != null && !GetToolStripState(STATE_DRAGGING)) { + Cursor currentCursor = Cursor.CurrentInternal; + + if (currentCursor != null) { + if (AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip.Active = true; + } + + Point cursorLocation = Cursor.Position; + cursorLocation.Y += Cursor.Size.Height - currentCursor.HotSpot.Y; + + cursorLocation = WindowsFormsUtils.ConstrainToScreenBounds(new Rectangle(cursorLocation, onePixel)).Location; + + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + ToolTip.Show(currentlyActiveTooltipItem.ToolTipText, + this, + PointToClient(cursorLocation), + ToolTip.AutoPopDelay); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + } + } + + } + + private void UpdateLayoutStyle(DockStyle newDock) { + if (!IsInToolStripPanel && layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { + using (new LayoutTransaction(this, this, PropertyNames.Orientation)) { + // + // We want the ToolStrip to size appropriately when the dock has switched. + // + if (newDock == DockStyle.Left || newDock == DockStyle.Right) { + UpdateOrientation(Orientation.Vertical); + } + else { + UpdateOrientation(Orientation.Horizontal); + } + } + + OnLayoutStyleChanged(EventArgs.Empty); + + if (this.ParentInternal != null) { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Orientation); + } + } + } + + private void UpdateLayoutStyle(Orientation newRaftingRowOrientation) { + if (layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { + using (new LayoutTransaction(this, this, PropertyNames.Orientation)) { + + // + // We want the ToolStrip to size appropriately when the rafting container orientation has switched. + // + /* if (newRaftingRowOrientation != orientation) { + int oldHeight = this.Height; + this.Height = this.Width; + this.Width = oldHeight; + }*/ + + UpdateOrientation(newRaftingRowOrientation); + if (LayoutEngine is ToolStripSplitStackLayout && layoutStyle == ToolStripLayoutStyle.StackWithOverflow) { + OnLayoutStyleChanged(EventArgs.Empty); + } + + } + } + else { + // update the orientation but dont force a layout. + UpdateOrientation(newRaftingRowOrientation); + } + + } + + + private void UpdateOrientation(Orientation newOrientation) { + if (newOrientation != orientation) { + // snap our last dimensions before switching over. + // use specifed bounds so that if something is docked or anchored we dont take the extra stretching + // effects into account. + Size size = CommonProperties.GetSpecifiedBounds(this).Size; + orientation = newOrientation; + // since the Grip affects the DisplayRectangle, we need to re-adjust the size + SetupGrip(); + } + } + + /// + /// + /// Summary of WndProc. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (m.Msg == NativeMethods.WM_SETFOCUS) { + SnapFocus(m.WParam); + } + if (m.Msg == NativeMethods.WM_MOUSEACTIVATE) { + // we want to prevent taking focus if someone clicks on the toolstrip dropdown + // itself. the mouse message will still go through, but focus wont be taken. + // if someone clicks on a child control (combobox, textbox, etc) focus will + // be taken - but we'll handle that in WM_NCACTIVATE handler. + Point pt = PointToClient(WindowsFormsUtils.LastCursorPoint); + IntPtr hwndClicked = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, Handle), pt.X, pt.Y,(int)(GetChildAtPointSkip.Invisible | GetChildAtPointSkip.Disabled | GetChildAtPointSkip.Transparent)); + // if we click on the toolstrip itself, eat the activation. + // if we click on a child control, allow the toolstrip to activate. + if (hwndClicked == this.Handle) { + lastMouseDownedItem = null; + m.Result = (IntPtr)NativeMethods.MA_NOACTIVATE; + + if (!IsDropDown && !IsInDesignMode) { + + // VSWhidbey 473357: if our root HWND is not the active hwnd, + // eat the mouse message and bring the form to the front. + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this); + if (rootHwnd.Handle != IntPtr.Zero) { + + // snap the active window and compare to our root window. + IntPtr hwndActive = UnsafeNativeMethods.GetActiveWindow(); + if (hwndActive != rootHwnd.Handle) { + // Activate the window, and discard the mouse message. + // this appears to be the same behavior as office. + m.Result = (IntPtr)NativeMethods.MA_ACTIVATEANDEAT; + } + } + } + return; + } + else { + // we're setting focus to a child control - remember who gave it to us + // so we can restore it on ESC. + SnapFocus(UnsafeNativeMethods.GetFocus()); + if (!IsDropDown && !TabStop) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "Installing restoreFocusFilter"); + // PERF, SECREVIEW: dont call Application.AddMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().AddMessageFilter(RestoreFocusFilter); + } + } + } + + + base.WndProc(ref m); + + if (m.Msg == NativeMethods.WM_NCDESTROY) { + // Destroy the owner window, if we created one. We + // cannot do this in OnHandleDestroyed, because at + // that point our handle is not actually destroyed so + // destroying our parent actually causes a recursive + // WM_DESTROY. + if (dropDownOwnerWindow != null) { + dropDownOwnerWindow.DestroyHandle(); + } + } + } + + // Overriden to return Items instead of Controls. + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { return Items; } + } + + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + } + + /// + /// + bool IArrangedElement.ParticipatesInLayout { + get { return GetState(STATE_VISIBLE);} + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripAccessibleObject(this); + } + + /// + protected override Control.ControlCollection CreateControlsInstance() { + return new WindowsFormsUtils.ReadOnlyControlCollection(this, /* isReadOnly = */ !DesignMode); + } + + + internal void OnItemAddedInternal(ToolStripItem item) { + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + if (this.ShowItemToolTips) { + KeyboardToolTipStateMachine.Instance.Hook(item, this.ToolTip); + } + } + } + + internal void OnItemRemovedInternal(ToolStripItem item) { + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Unhook(item, this.ToolTip); + } + } + + internal override bool AllowsChildrenToShowToolTips() { + return base.AllowsChildrenToShowToolTips() && this.ShowItemToolTips; + } + + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripAccessibleObject : ControlAccessibleObject { + + private ToolStrip owner; + + /// + public ToolStripAccessibleObject(ToolStrip owner) : base(owner) { + this.owner = owner; + } + + /// + /// + /// Return the child object at the given screen coordinates. + /// + public override AccessibleObject HitTest(int x, int y) { + + Point clientHit = owner.PointToClient(new Point(x,y)); + ToolStripItem item = owner.GetItemAt(clientHit); + return ((item != null) && (item.AccessibilityObject != null)) ? + item.AccessibilityObject : + base.HitTest(x,y); + } + + + /// + /// + /// When overridden in a derived class, gets the accessible child corresponding to the specified + /// index. + /// + // + public override AccessibleObject GetChild(int index) { + if ((owner == null) || (owner.Items == null)) + return null; + + if (index == 0 && owner.Grip.Visible) { + return owner.Grip.AccessibilityObject; + } + else if (owner.Grip.Visible && index > 0) { + index--; + } + + if (index < owner.Items.Count) { + ToolStripItem item = null; + int myIndex = 0; + + // First we walk through the head aligned items. + for (int i = 0; i < owner.Items.Count; ++i) + { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) { + if (myIndex == index) { + item = owner.Items[i]; + break; + } + myIndex++; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + if (item == null) { + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) { + if (myIndex == index) { + item = owner.Items[i]; + break; + } + myIndex++; + } + } + } + + if (item == null) { + Debug.Fail("No item matched the index??"); + return null; + } + + if (item.Placement == ToolStripItemPlacement.Overflow) { + return new ToolStripAccessibleObjectWrapperForItemsOnOverflow(item); + } + return item.AccessibilityObject; + } + + if (owner.CanOverflow && owner.OverflowButton.Visible && index == owner.Items.Count) { + return owner.OverflowButton.AccessibilityObject; + } + return null; + } + + /// + /// + /// When overridden in a derived class, gets the number of children + /// belonging to an accessible object. + /// + public override int GetChildCount() { + if ((owner == null) || (owner.Items == null)) + return -1; + + int count = 0; + for (int i = 0; i < owner.Items.Count; i++) { + if (owner.Items[i].Available) { + count++; + } + } + if (owner.Grip.Visible) { + count++; + } + if (owner.CanOverflow && owner.OverflowButton.Visible) { + count++; + } + return count; + } + + internal AccessibleObject GetChildFragment(int fragmentIndex, bool getOverflowItem = false) { + + var items = getOverflowItem ? owner.OverflowItems : owner.DisplayedItems; + int childFragmentCount = items.Count; + + if (!getOverflowItem && owner.CanOverflow && owner.OverflowButton.Visible && fragmentIndex == childFragmentCount - 1) { + return owner.OverflowButton.AccessibilityObject; + } + + for (int index = 0; index < childFragmentCount; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Left && fragmentIndex == index) { + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + } + + for (int index = 0; index < childFragmentCount; index++) { + var item = owner.Items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Right && fragmentIndex == index) { + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + } + + return null; + } + + internal int GetChildOverflowFragmentCount() { + if (owner == null || owner.OverflowItems == null) { + return -1; + } + + return owner.OverflowItems.Count; + } + + internal int GetChildFragmentCount() { + if (owner == null || owner.DisplayedItems == null) { + return -1; + } + + return owner.DisplayedItems.Count; + } + + internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if (owner == null || owner.Items == null) { + return -1; + } + + if (child.Owner == owner.Grip) { + return 0; + } + + ToolStripItemCollection items; + var placement = child.Owner.Placement; + + if (owner is ToolStripOverflow) { + // Overflow items in ToolStripOverflow host are in DisplayedItems collection. + items = owner.DisplayedItems; + } + else { + if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) { + return GetChildFragmentCount() - 1; + } + + // Items can be either in DisplayedItems or in OverflowItems (if overflow) + items = (placement == ToolStripItemPlacement.Main) ? owner.DisplayedItems : owner.OverflowItems; + } + + // First we walk through the head aligned items. + for (int index = 0; index < items.Count; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Left && child.Owner == items[index]) { + return index; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + for (int index = 0; index < items.Count; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Right && child.Owner == items[index]) { + return index; + } + } + + return -1; + } + + internal int GetChildIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if ((owner == null) || (owner.Items == null)) { + return -1; + } + + int index = 0; + if (owner.Grip.Visible) { + if (child.Owner == owner.Grip) { + return 0; + } + index = 1; + } + + if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) { + return owner.Items.Count + index; + } + + // First we walk through the head aligned items. + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) { + if (child.Owner == owner.Items[i]) { + return index; + } + index++; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) { + if (child.Owner == owner.Items[i]) { + return index; + } + index++; + } + } + + return -1; + } + + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.ToolBar; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildFragmentCount(); + if (childCount > 0) { + return this.GetChildFragment(0); + } + break; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildFragmentCount(); + if (childCount > 0) { + return this.GetChildFragment(childCount - 1); + } + break; + } + } + + return base.FragmentNavigate(direction); + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ToolBarControlTypeId; + } + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsIAccessibleExSupported() + { + return AccessibilityImprovements.Level3 && !owner.IsInDesignMode && !owner.IsTopInDesignMode; + } + + } + + private class ToolStripAccessibleObjectWrapperForItemsOnOverflow : ToolStripItem.ToolStripItemAccessibleObject { + public ToolStripAccessibleObjectWrapperForItemsOnOverflow(ToolStripItem item) + : base(item) { + } + public override AccessibleStates State { + get { + AccessibleStates state = base.State; + state |= AccessibleStates.Offscreen; + state |= AccessibleStates.Invisible; + return state; + } + } + } + + // When we click somewhere outside of the toolstrip it should be as if we hit esc. + + internal class RestoreFocusMessageFilter : IMessageFilter { + private ToolStrip ownerToolStrip; + + public RestoreFocusMessageFilter(ToolStrip ownerToolStrip) { + this.ownerToolStrip = ownerToolStrip; + } + + public bool PreFilterMessage(ref Message m) { + + if (ownerToolStrip.Disposing || ownerToolStrip.IsDisposed || ownerToolStrip.IsDropDown) { + return false; + } + // if the app has changed activation, restore focus + + switch (m.Msg) { + + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + if (ownerToolStrip.ContainsFocus) { + // if we've clicked on something that's not a child of the toolstrip and we + // currently have focus, restore it. + if (!UnsafeNativeMethods.IsChild(new HandleRef(this, ownerToolStrip.Handle), new HandleRef(this,m.HWnd))) { + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(ownerToolStrip); + if (rootHwnd.Handle == m.HWnd || UnsafeNativeMethods.IsChild(rootHwnd, new HandleRef(this,m.HWnd))) { + // Only RestoreFocus if the hwnd is a child of the root window and isnt on the toolstrip. + RestoreFocusInternal(); + } + } + } + return false; + + default: + return false; + } + } + private void RestoreFocusInternal() { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocusFilter] Detected a click, restoring focus."); + + + ownerToolStrip.BeginInvoke(new BooleanMethodInvoker(ownerToolStrip.RestoreFocusInternal), new object[]{ ToolStripManager.ModalMenuFilter.InMenuMode } ); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(this); + } + } + + internal override bool ShowsOwnKeyboardToolTip() { + bool hasVisibleSelectableItems = false; + int i = this.Items.Count; + while (i-- != 0 && !hasVisibleSelectableItems) { + ToolStripItem item = this.Items[i]; + if (item.CanKeyboardSelect && item.Visible) { + hasVisibleSelectableItems = true; + } + } + + return !hasVisibleSelectableItems; + } + } + + + + + + internal class CachedItemHdcInfo : IDisposable { + + internal CachedItemHdcInfo() { + } + + ~CachedItemHdcInfo() { + Dispose(); + } + + private HandleRef cachedItemHDC = NativeMethods.NullHandleRef; + private Size cachedHDCSize = Size.Empty; + private HandleRef cachedItemBitmap = NativeMethods.NullHandleRef; + // this DC is cached and should only be deleted on Dispose or when the size changes. + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public HandleRef GetCachedItemDC(HandleRef toolStripHDC, Size bitmapSize) { + + if ((cachedHDCSize.Width < bitmapSize.Width) + || (cachedHDCSize.Height < bitmapSize.Height)) { + + if (cachedItemHDC.Handle == IntPtr.Zero) { + // create a new DC - we dont have one yet. + IntPtr compatibleHDC = UnsafeNativeMethods.CreateCompatibleDC(toolStripHDC); + cachedItemHDC = new HandleRef(this, compatibleHDC); + } + + // create compatible bitmap with the correct size. + cachedItemBitmap = new HandleRef(this, SafeNativeMethods.CreateCompatibleBitmap(toolStripHDC, bitmapSize.Width, bitmapSize.Height)); + IntPtr oldBitmap = SafeNativeMethods.SelectObject(cachedItemHDC,cachedItemBitmap); + + // delete the old bitmap + if (oldBitmap != IntPtr.Zero) { + // ExternalDelete to prevent Handle underflow + SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, oldBitmap)); + oldBitmap = IntPtr.Zero; + } + + + // remember what size we created. + cachedHDCSize = bitmapSize; + + } + return cachedItemHDC; + } + + + private void DeleteCachedItemHDC() { + + if (cachedItemHDC.Handle != IntPtr.Zero) { + // delete the bitmap + if (cachedItemBitmap.Handle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(cachedItemBitmap); + cachedItemBitmap = NativeMethods.NullHandleRef; + } + // delete the DC itself. + UnsafeNativeMethods.DeleteCompatibleDC(cachedItemHDC); + } + + cachedItemHDC = NativeMethods.NullHandleRef; + cachedItemBitmap = NativeMethods.NullHandleRef; + cachedHDCSize = Size.Empty; + } + + public void Dispose() { + DeleteCachedItemHDC(); + GC.SuppressFinalize(this); + } + + } + + + + internal class MouseHoverTimer : IDisposable { + + private System.Windows.Forms.Timer mouseHoverTimer = new System.Windows.Forms.Timer(); + private const int SPI_GETMOUSEHOVERTIME_WIN9X = 400; // in Win9x this is not supported so lets use the default from a more modern OS. + + // consider - weak reference? + private ToolStripItem currentItem = null; + + public MouseHoverTimer() { + int interval = SystemInformation.MouseHoverTime; + if (interval == 0) { + interval = SPI_GETMOUSEHOVERTIME_WIN9X; + } + + mouseHoverTimer.Interval = interval; + mouseHoverTimer.Tick += new EventHandler(OnTick); + } + + public void Start(ToolStripItem item) { + if (item != currentItem) { + Cancel(currentItem); + } + currentItem = item; + if (currentItem != null) { + mouseHoverTimer.Enabled = true; + } + } + + + public void Cancel() { + mouseHoverTimer.Enabled = false; + currentItem = null; + } + /// cancels if and only if this item was the one that + /// requested the timer + /// + public void Cancel(ToolStripItem item) { + if (item == currentItem) { + Cancel(); + } + } + + public void Dispose() { + if (mouseHoverTimer != null) { + Cancel(); + mouseHoverTimer.Dispose(); + mouseHoverTimer = null; + } + } + + + private void OnTick(object sender, EventArgs e) { + mouseHoverTimer.Enabled = false; + if (currentItem != null && !currentItem.IsDisposed) { + currentItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseHover); + } + } + + + } + + /// + /// This class supports the AllowItemReorder feature. + /// When reordering items ToolStrip and ToolStripItem drag/drop events + /// are routed here. + /// + internal sealed class ToolStripSplitStackDragDropHandler : IDropTarget, ISupportOleDropSource { + + private ToolStrip owner; + + public ToolStripSplitStackDragDropHandler(ToolStrip owner) { + + if (owner == null) { + // + throw new ArgumentNullException("owner"); + } + this.owner = owner; + } + + public void OnDragEnter(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragEnter: " + e.ToString()); + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + e.Effect = DragDropEffects.Move; + this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y))); + + } + } + + public void OnDragLeave(System.EventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragLeave: " + e.ToString()); + owner.ClearInsertionMark(); + } + + public void OnDragDrop(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragDrop: " + e.ToString()); + + + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + ToolStripItem item = (ToolStripItem)e.Data.GetData(typeof(ToolStripItem)); + OnDropItem(item, owner.PointToClient(new Point(e.X, e.Y))); + } + + } + public void OnDragOver(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragOver: " + e.ToString()); + + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + if (this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y)))) { + e.Effect = DragDropEffects.Move; + } + else { + if (owner != null) { + owner.ClearInsertionMark(); + } + e.Effect = DragDropEffects.None; + } + } + + + } + + public void OnGiveFeedback(GiveFeedbackEventArgs e) { + } + + public void OnQueryContinueDrag(QueryContinueDragEventArgs e) { + } + + private void OnDropItem(ToolStripItem droppedItem, Point ownerClientAreaRelativeDropPoint) { + Point start = Point.Empty; + + int toolStripItemIndex = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint); + if (toolStripItemIndex >= 0) { + ToolStripItem item = owner.Items[toolStripItemIndex]; + if (item == droppedItem) { + owner.ClearInsertionMark(); + return; // optimization + } + + RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint); + droppedItem.Alignment = item.Alignment; + + // Protect against negative indicies + int insertIndex = Math.Max(0, toolStripItemIndex); + + if (relativeLocation == RelativeLocation.Above) { + insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex + 1; + } + else if (relativeLocation == RelativeLocation.Below) { + insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex-1; + } + else if (((item.Alignment == ToolStripItemAlignment.Left) && (relativeLocation == RelativeLocation.Left)) || + ((item.Alignment == ToolStripItemAlignment.Right) && (relativeLocation == RelativeLocation.Right))) { + + // the item alignment is Tail & dropped to right of the center of the item + // or the item alignment is Head & dropped to the left of the center of the item + + // Normally, insert the new item after the item, however in RTL insert before the item + insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.Yes) ? insertIndex + 1 : insertIndex); + } + else { + // the item alignment is Tail & dropped to left of the center of the item + // or the item alignment is Head & dropped to the right of the center of the item + + + // Normally, insert the new item before the item, however in RTL insert after the item + insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.No) ? insertIndex + 1 : insertIndex); + } + + // VSWhidbey 517774 + // If the control is moving from a lower to higher index, you actually want to set it one less than its position. + // This is because it is being removed from its original position, which lowers the index of every control before + // its new drop point by 1. + if (owner.Items.IndexOf(droppedItem) < insertIndex) { + insertIndex--; + } + + owner.Items.MoveItem(Math.Max(0,insertIndex), droppedItem); + owner.ClearInsertionMark(); + + } + else if (toolStripItemIndex == -1 && owner.Items.Count == 0) { + owner.Items.Add(droppedItem); + owner.ClearInsertionMark(); + } + } + + + + private bool ShowItemDropPoint(Point ownerClientAreaRelativeDropPoint) { + + int i = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint); + if (i >= 0) { + ToolStripItem item = owner.Items[i]; + RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint); + + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Drop relative loc " + relativeLocation); + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Index " + i); + + Rectangle insertionRect = Rectangle.Empty; + switch (relativeLocation) { + case RelativeLocation.Above: + insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Top, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth); + break; + case RelativeLocation.Below: + insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Bottom, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth); + break; + case RelativeLocation.Right: + insertionRect = new Rectangle(item.Bounds.Right, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height- (owner.Margin.Vertical)-1); + break; + case RelativeLocation.Left: + insertionRect = new Rectangle(item.Bounds.Left, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height - (owner.Margin.Vertical) -1); + break; + } + + owner.PaintInsertionMark(insertionRect); + return true; + } + else if (owner.Items.Count == 0) { + Rectangle insertionRect = owner.DisplayRectangle; + insertionRect.Width = ToolStrip.insertionBeamWidth; + owner.PaintInsertionMark(insertionRect); + return true; + } + return false; + } + + + private int GetItemInsertionIndex(Point ownerClientAreaRelativeDropPoint) { + for(int i = 0; i< owner.DisplayedItems.Count; i++) { + Rectangle bounds = owner.DisplayedItems[i].Bounds; + bounds.Inflate(owner.DisplayedItems[i].Margin.Size); + if (bounds.Contains(ownerClientAreaRelativeDropPoint)) { + Debug.WriteLineIf(ToolStrip.DropTargetDebug.TraceVerbose, "MATCH " + owner.DisplayedItems[i].Text + " Bounds: " + owner.DisplayedItems[i].Bounds.ToString()); + + // consider what to do about items not in the display + return owner.Items.IndexOf(owner.DisplayedItems[i]); + } + } + + if (owner.DisplayedItems.Count > 0) { + for (int i = 0; i < owner.DisplayedItems.Count; i++) { + if (owner.DisplayedItems[i].Alignment == ToolStripItemAlignment.Right) { + if (i > 0) { + return owner.Items.IndexOf(owner.DisplayedItems[i - 1]); + } + return owner.Items.IndexOf(owner.DisplayedItems[i]); + } + } + return owner.Items.IndexOf(owner.DisplayedItems[owner.DisplayedItems.Count - 1]); + } + return -1; + } + + private enum RelativeLocation { + Above, + Below, + Right, + Left + } + + private RelativeLocation ComparePositions(Rectangle orig, Point check) { + + if (owner.Orientation == Orientation.Horizontal) { + int widthUnit = orig.Width / 2; + RelativeLocation relativeLocation = RelativeLocation.Left; + + // we can return here if we are checking abovebelowleftright, because + // the left right calculation is more picky than the above/below calculation + // and the above below calculation will just override this one. + if ((orig.Left + widthUnit) >= check.X) { + relativeLocation = RelativeLocation.Left; + return relativeLocation; + } + else if ((orig.Right - widthUnit) <= check.X) { + relativeLocation = RelativeLocation.Right; + return relativeLocation; + } + } + + if (owner.Orientation == Orientation.Vertical) { + int heightUnit = orig.Height/ 2; + RelativeLocation relativeLocation = (check.Y <= (orig.Top + heightUnit)) ? + RelativeLocation.Above + : RelativeLocation.Below; + + return relativeLocation; + } + + Debug.Fail("Could not calculate the relative position for AllowItemReorder"); + return RelativeLocation.Left; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStrip.cs.back b/WindowsForms/Managed/System/WinForms/ToolStrip.cs.back new file mode 100644 index 000000000..26e05607c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStrip.cs.back @@ -0,0 +1,5574 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Configuration; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security.Permissions; + using System.Threading; + using System.Windows.Forms.Layout; + using System.ComponentModel.Design.Serialization; + using System.Drawing.Drawing2D; + using System.Text.RegularExpressions; + using System.Text; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Runtime.Versioning; + + /// + /// + /// Summary of ToolStrip. + /// + + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [DesignerSerializer("System.Windows.Forms.Design.ToolStripCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)] + [Designer("System.Windows.Forms.Design.ToolStripDesigner, " + AssemblyRef.SystemDesign)] + [DefaultProperty("Items")] + [SRDescription(SR.DescriptionToolStrip)] + [DefaultEvent("ItemClicked")] + + + public class ToolStrip : System.Windows.Forms.ScrollableControl, + IArrangedElement, + ISupportToolStripPanel + { + + + private static Size onePixel = new Size(1,1); + internal static Point InvalidMouseEnter = new Point(Int32.MaxValue, Int32.MaxValue); + + private ToolStripItemCollection toolStripItemCollection = null; + private ToolStripOverflowButton toolStripOverflowButton = null; + private ToolStripGrip toolStripGrip = null; + private ToolStripItemCollection displayedItems = null; + private ToolStripItemCollection overflowItems = null; + private ToolStripDropTargetManager dropTargetManager = null; + private IntPtr hwndThatLostFocus = IntPtr.Zero; + private ToolStripItem lastMouseActiveItem = null; + private ToolStripItem lastMouseDownedItem = null; + private LayoutEngine layoutEngine = null; + private ToolStripLayoutStyle layoutStyle = ToolStripLayoutStyle.StackWithOverflow; + private LayoutSettings layoutSettings = null; + private Rectangle lastInsertionMarkRect = Rectangle.Empty; + private ImageList imageList = null; + private ToolStripGripStyle toolStripGripStyle = ToolStripGripStyle.Visible; + private ISupportOleDropSource itemReorderDropSource = null; + private IDropTarget itemReorderDropTarget = null; + private int toolStripState = 0; + private bool showItemToolTips = false; + private MouseHoverTimer mouseHoverTimer = null; + private ToolStripItem currentlyActiveTooltipItem; + private NativeWindow dropDownOwnerWindow; + private byte mouseDownID = 0; // NEVER use this directly from another class, 0 should never be returned to another class. + + private Orientation orientation = Orientation.Horizontal; + + private ArrayList activeDropDowns = new ArrayList(1); + private ToolStripRenderer renderer = null; + private Type currentRendererType = typeof(System.Type); + private Hashtable shortcuts = null; + private Stack mergeHistoryStack = null; + private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default; + private Size largestDisplayedItemSize = Size.Empty; + private CachedItemHdcInfo cachedItemHdcInfo = null; + private bool alreadyHooked = false; + + private Size imageScalingSize; + private const int ICON_DIMENSION = 16; + private static int iconWidth = ICON_DIMENSION; + private static int iconHeight = ICON_DIMENSION; + + private Font defaultFont = null; + private RestoreFocusMessageFilter restoreFocusFilter; + + private bool layoutRequired = false; + + private static readonly Padding defaultPadding = new Padding(0, 0, 1, 0); + private static readonly Padding defaultGripMargin = new Padding(2); + private Padding scaledDefaultPadding = defaultPadding; + private Padding scaledDefaultGripMargin = defaultGripMargin; + + + private Point mouseEnterWhenShown = InvalidMouseEnter; + + private const int INSERTION_BEAM_WIDTH = 6; + + internal static int insertionBeamWidth = INSERTION_BEAM_WIDTH; + + private static readonly object EventPaintGrip = new object(); + private static readonly object EventLayoutCompleted = new object(); + private static readonly object EventItemAdded = new object(); + private static readonly object EventItemRemoved = new object(); + private static readonly object EventLayoutStyleChanged = new object(); + private static readonly object EventRendererChanged = new object(); + private static readonly object EventItemClicked = new object(); + private static readonly object EventLocationChanging = new object(); + private static readonly object EventBeginDrag = new object(); + private static readonly object EventEndDrag = new object(); + + private static readonly int PropBindingContext = PropertyStore.CreateKey(); + private static readonly int PropTextDirection = PropertyStore.CreateKey(); + private static readonly int PropToolTip = PropertyStore.CreateKey(); + private static readonly int PropToolStripPanelCell = PropertyStore.CreateKey(); + + internal const int STATE_CANOVERFLOW = 0x00000001; + internal const int STATE_ALLOWITEMREORDER = 0x00000002; + internal const int STATE_DISPOSINGITEMS = 0x00000004; + internal const int STATE_MENUAUTOEXPAND = 0x00000008; + internal const int STATE_MENUAUTOEXPANDDEFAULT = 0x00000010; + internal const int STATE_SCROLLBUTTONS = 0x00000020; + internal const int STATE_USEDEFAULTRENDERER = 0x00000040; + internal const int STATE_ALLOWMERGE = 0x00000080; + internal const int STATE_RAFTING = 0x00000100; + internal const int STATE_STRETCH = 0x00000200; + internal const int STATE_LOCATIONCHANGING = 0x00000400; + internal const int STATE_DRAGGING = 0x00000800; + internal const int STATE_HASVISIBLEITEMS = 0x00001000; + internal const int STATE_SUSPENDCAPTURE = 0x00002000; + internal const int STATE_LASTMOUSEDOWNEDITEMCAPTURE = 0x00004000; + internal const int STATE_MENUACTIVE = 0x00008000; + + +#if DEBUG + internal static readonly TraceSwitch SelectionDebug = new TraceSwitch("SelectionDebug", "Debug ToolStrip Selection code"); + internal static readonly TraceSwitch DropTargetDebug = new TraceSwitch("DropTargetDebug", "Debug ToolStrip Drop code"); + internal static readonly TraceSwitch LayoutDebugSwitch = new TraceSwitch("Layout debug", "Debug ToolStrip layout code"); + internal static readonly TraceSwitch MouseActivateDebug = new TraceSwitch("ToolStripMouseActivate", "Debug ToolStrip WM_MOUSEACTIVATE code"); + internal static readonly TraceSwitch MergeDebug = new TraceSwitch("ToolStripMergeDebug", "Debug toolstrip merging"); + internal static readonly TraceSwitch SnapFocusDebug = new TraceSwitch("SnapFocus", "Debug snapping/restoration of focus"); + internal static readonly TraceSwitch FlickerDebug = new TraceSwitch("FlickerDebug", "Debug excessive calls to Invalidate()"); + internal static readonly TraceSwitch ItemReorderDebug = new TraceSwitch("ItemReorderDebug", "Debug excessive calls to Invalidate()"); + internal static readonly TraceSwitch MDIMergeDebug = new TraceSwitch("MDIMergeDebug", "Debug toolstrip MDI merging"); + internal static readonly TraceSwitch MenuAutoExpandDebug = new TraceSwitch("MenuAutoExpand", "Debug menu auto expand"); + internal static readonly TraceSwitch ControlTabDebug = new TraceSwitch("ControlTab", "Debug ToolStrip Control+Tab selection"); +#else + internal static readonly TraceSwitch SelectionDebug; + internal static readonly TraceSwitch DropTargetDebug; + internal static readonly TraceSwitch LayoutDebugSwitch; + internal static readonly TraceSwitch MouseActivateDebug; + internal static readonly TraceSwitch MergeDebug; + internal static readonly TraceSwitch SnapFocusDebug; + internal static readonly TraceSwitch FlickerDebug; + internal static readonly TraceSwitch ItemReorderDebug; + internal static readonly TraceSwitch MDIMergeDebug; + internal static readonly TraceSwitch MenuAutoExpandDebug; + internal static readonly TraceSwitch ControlTabDebug; +#endif + + private delegate void BooleanMethodInvoker(bool arg); + internal Action rescaleConstsCallbackDelegate; + + /// + /// + /// Summary of ToolStrip. + /// + public ToolStrip() { + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + ToolStripManager.CurrentDpi = DeviceDpi; + defaultFont = ToolStripManager.DefaultFont; + iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi); + iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi); + insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, DeviceDpi); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, DeviceDpi); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, DeviceDpi); + } + else if (DpiHelper.IsScalingRequired) { + iconWidth = DpiHelper.LogicalToDeviceUnitsX(ICON_DIMENSION); + iconHeight = DpiHelper.LogicalToDeviceUnitsY(ICON_DIMENSION); + if (DpiHelper.EnableToolStripHighDpiImprovements) { + insertionBeamWidth = DpiHelper.LogicalToDeviceUnitsX(INSERTION_BEAM_WIDTH); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin); + } + } + + imageScalingSize = new Size(iconWidth, iconHeight); + + SuspendLayout(); + this.CanOverflow = true; + this.TabStop = false; + this.MenuAutoExpand = false; + SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.SupportsTransparentBackColor, true); + + SetStyle(ControlStyles.Selectable, false); + SetToolStripState(STATE_USEDEFAULTRENDERER | STATE_ALLOWMERGE, true); + + SetState2(STATE2_MAINTAINSOWNCAPTUREMODE // VSWhidbey 458967: a toolstrip does not take capture on MouseDown. + | STATE2_USEPREFERREDSIZECACHE, // this class overrides GetPreferredSizeCore, let Control automatically cache the result + true); + + //add a weak ref link in ToolstripManager + ToolStripManager.ToolStrips.Add(this); + + layoutEngine = new ToolStripSplitStackLayout(this); + this.Dock = DefaultDock; + this.AutoSize = true; + this.CausesValidation = false; + Size defaultSize = DefaultSize; + SetAutoSizeMode(AutoSizeMode.GrowAndShrink); + this.ShowItemToolTips = DefaultShowItemToolTips; + ResumeLayout(true); + } + + public ToolStrip(params ToolStripItem[] items) : this() { + Items.AddRange(items); + } + + internal ArrayList ActiveDropDowns { + get { return activeDropDowns; } + } + + // returns true when entered into menu mode through this toolstrip/menustrip + // this is only really supported for menustrip active event, but to prevent casting everywhere... + internal virtual bool KeyboardActive { + get { return GetToolStripState(STATE_MENUACTIVE); } + set { SetToolStripState(STATE_MENUACTIVE, value);} + } + + // This is only for use in determining whether to show scroll bars on + // ToolStripDropDownMenus. No one else should be using it for anything. + internal virtual bool AllItemsVisible { + get { + return true; + } + set { + // we do nothing in repsonse to a set, since we calculate the value above. + } + } + + [DefaultValue(true), Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + if (IsInToolStripPanel && base.AutoSize && !value) { + // VSWhidbey 351717 - restoring the bounds can change the location of the toolstrip - + // which would join it to a new row. Set the specified bounds to the new location to + // prevent this. + Rectangle bounds = CommonProperties.GetSpecifiedBounds(this); + bounds.Location = this.Location; + CommonProperties.UpdateSpecifiedBounds(this, bounds.X, bounds.Y, bounds.Width, bounds.Height, BoundsSpecified.Location); + + } + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool AutoScroll { + get { + return base.AutoScroll; + } + set { + throw new NotSupportedException(SR.GetString(SR.ToolStripDoesntSupportAutoScroll)); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMargin { + get { + return base.AutoScrollMargin; + } + set { + base.AutoScrollMargin = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMinSize { + get { + return base.AutoScrollMinSize; + } + set { + base.AutoScrollMinSize = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Point AutoScrollPosition { + get { + return base.AutoScrollPosition; + } + set { + base.AutoScrollPosition = value; + } + } + + /// + /// + /// Summary of AllowDrop. + /// + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + if (value && AllowItemReorder) { + throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue)); + } + + base.AllowDrop = value; + + // SECREVIEW: If we toggle between AllowDrop and AllowItemReorder + // make sure that we're demanding the Clipboard permission in + // ToolStripDropTargetManager.SetAcceptDrops + if (value) { + this.DropTargetManager.EnsureRegistered(this); + } + else { + this.DropTargetManager.EnsureUnRegistered(this); + } + + } + } + /// + /// + /// + /// + [ + DefaultValue(false), + SRDescription(SR.ToolStripAllowItemReorderDescr), + SRCategory(SR.CatBehavior) + ] + public bool AllowItemReorder { + get { return GetToolStripState(STATE_ALLOWITEMREORDER); } + set { + if (GetToolStripState(STATE_ALLOWITEMREORDER) != value) { + if (AllowDrop && value) { + throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue)); + } + SetToolStripState(STATE_ALLOWITEMREORDER, value); + + // SECREVIEW: If we toggle between AllowDrop and AllowItemReorder + // make sure that we're demanding the Clipboard permission in + // ToolStripDropTargetManager.SetAcceptDrops + if (value) { + ToolStripSplitStackDragDropHandler dragDropHandler = new ToolStripSplitStackDragDropHandler(this); + this.ItemReorderDropSource = dragDropHandler; + this.ItemReorderDropTarget = dragDropHandler; + + this.DropTargetManager.EnsureRegistered(this); + } + else { + this.DropTargetManager.EnsureUnRegistered(this); + } + + } + + + + } + } + + /// + /// + /// + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripAllowMergeDescr), + SRCategory(SR.CatBehavior) + ] + public bool AllowMerge { + get { return GetToolStripState(STATE_ALLOWMERGE); } + set { + if (GetToolStripState(STATE_ALLOWMERGE) != value) { + SetToolStripState(STATE_ALLOWMERGE, value); + } + } + } + + + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + // the base calls SetDock, which causes an OnDockChanged to be called + // which forces two layouts of the parent. + using (new LayoutTransaction(this, this, PropertyNames.Anchor)) { + base.Anchor = value; + } + } + } + + /// + /// + /// + /// Just here so we can implement ShouldSerializeBackColor + /// + [ + SRDescription(SR.ToolStripBackColorDescr), + SRCategory(SR.CatAppearance) + ] + public new Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnBeginDrag)] + public event EventHandler BeginDrag { + add { + Events.AddHandler(EventBeginDrag, value); + } + remove { + Events.RemoveHandler(EventBeginDrag, value); + } + } + + /// + public override BindingContext BindingContext { + get { + BindingContext bc = (BindingContext) this.Properties.GetObject(PropBindingContext); + if (bc != null) + return bc; + + // try the parent + // + Control p = ParentInternal; + if (p != null && p.CanAccessProperties) + return p.BindingContext; + + // we don't have a binding context + return null; + } + set { + if (this.Properties.GetObject(PropBindingContext) != value) { + this.Properties.SetObject(PropBindingContext, value); + + // re-wire the bindings + OnBindingContextChanged(EventArgs.Empty); + } + } + } + + + + + /// + /// + /// Summary of CanOverflow. + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripCanOverflowDescr), + SRCategory(SR.CatLayout) + ] + public bool CanOverflow { + get { + return GetToolStripState(STATE_CANOVERFLOW); + } + set { + if (GetToolStripState(STATE_CANOVERFLOW) != value) { + SetToolStripState(STATE_CANOVERFLOW, value); + InvalidateLayout(); + } + } + } + + /// we can only shift selection when we're not focused (someone mousing over us) + /// or we are focused and one of our toolstripcontrolhosts do not have focus. + /// SCENARIO: put focus in combo box, move the mouse over another item... selectioni + /// should not shift until the combobox relinquishes its focus. + /// + internal bool CanHotTrack { + get { + if (!Focused) { + // if ContainsFocus in one of the children = false, someone is just mousing by, we can hot track + return (ContainsFocus == false); + } + else { + // if the toolstrip itself contains focus we can definately hottrack. + return true; + } + } + } + + + [ + Browsable(false), + DefaultValue(false), + ] + public new bool CausesValidation { + get { + // By default: CausesValidation is false for a ToolStrip + // we want people to be able to use menus without validating + // their controls. + return base.CausesValidation; + } + set { + base.CausesValidation = value; + } + } + + [Browsable(false)] + public new event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Control.ControlCollection Controls { + get { return base.Controls; } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlAdded { + add { + base.ControlAdded += value; + } + remove { + base.ControlAdded -= value; + } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Cursor Cursor { + get { return base.Cursor; } + set { base.Cursor = value; } + } + + /// + /// Hide browsable property + /// + [Browsable(false)] + public new event EventHandler CursorChanged { + add { + base.CursorChanged += value; + } + remove { + base.CursorChanged -= value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event ControlEventHandler ControlRemoved { + add { + base.ControlRemoved += value; + } + remove { + base.ControlRemoved -= value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnEndDrag)] + public event EventHandler EndDrag { + add { + Events.AddHandler(EventEndDrag, value); + } + remove { + Events.RemoveHandler(EventEndDrag, value); + } + } + + /// + public override Font Font { + get { + if (this.IsFontSet()) { + return base.Font; + } + if (defaultFont == null) { + // since toolstrip manager default font is thread static, hold onto a copy of the + // pointer in an instance variable for perf so we dont have to keep fishing into + // thread local storage for it. + defaultFont = ToolStripManager.DefaultFont; + } + return defaultFont; + } + set { + base.Font = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(100, 25), DeviceDpi) : + new Size(100, 25); + } + } + + protected override Padding DefaultPadding { + get { + // one pixel from the right edge to prevent the right border from painting over the + // aligned-right toolstrip item. + return scaledDefaultPadding; + } + } + + protected override Padding DefaultMargin { + get { return Padding.Empty; } + } + + protected virtual DockStyle DefaultDock { + get { + return DockStyle.Top; + } + } + + protected virtual Padding DefaultGripMargin { + get { + if (toolStripGrip != null) { + return toolStripGrip.DefaultMargin; + } + else { + return scaledDefaultGripMargin; + } + } + } + + protected virtual bool DefaultShowItemToolTips { + get { + return true; + } + } + + [Browsable(false)] + [SRDescription(SR.ToolStripDefaultDropDownDirectionDescr)] + [SRCategory(SR.CatBehavior)] + public virtual ToolStripDropDownDirection DefaultDropDownDirection { + get { + ToolStripDropDownDirection direction = toolStripDropDownDirection; + if (direction == ToolStripDropDownDirection.Default) { + if (Orientation == Orientation.Vertical) { + if (IsInToolStripPanel) { + // parent can be null when we're swapping between ToolStripPanels. + DockStyle actualDock = (ParentInternal != null) ? ParentInternal.Dock : DockStyle.Left; + direction = (actualDock == DockStyle.Right) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + if (DesignMode && actualDock == DockStyle.Left) + { + direction = ToolStripDropDownDirection.Right ; + } + + } + else { + direction = ((Dock == DockStyle.Right) && (RightToLeft == RightToLeft.No)) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + if (DesignMode && Dock == DockStyle.Left) + { + direction = ToolStripDropDownDirection.Right ; + } + } + } + else { // horizontal + DockStyle dock = this.Dock; + if (IsInToolStripPanel && ParentInternal != null) { + dock = ParentInternal.Dock; // we want the orientation of the ToolStripPanel; + } + + if (dock == DockStyle.Bottom) { + direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.AboveLeft : ToolStripDropDownDirection.AboveRight; + } + else { + // assume Dock.Top + direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight; + } + } + } + return direction; + } + set { + // cant use Enum.IsValid as its not sequential + switch (value) { + case ToolStripDropDownDirection.AboveLeft: + case ToolStripDropDownDirection.AboveRight: + case ToolStripDropDownDirection.BelowLeft: + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.Left: + case ToolStripDropDownDirection.Right: + case ToolStripDropDownDirection.Default: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection)); + } + + toolStripDropDownDirection = value; + } + } + /// + /// + /// + /// Just here so we can add the default value attribute + /// + [DefaultValue(DockStyle.Top)] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + if (value != Dock) { + using (new LayoutTransaction(this, this, PropertyNames.Dock)) + using (new LayoutTransaction(this.ParentInternal, this, PropertyNames.Dock)) { + // We don't call base.Dock = value, because that would cause us to get 2 LocationChanged events. + // The first is when the parent gets a Layout due to the DockChange, and the second comes from when we + // change the orientation. Instead we've duplicated the logic of Control.Dock.set here, but with a + // LayoutTransaction on the Parent as well. + // See VSWhidbey:489688 and VSWhidbey:474781 for more details. + DefaultLayout.SetDock(this, value); + UpdateLayoutStyle(Dock); + } + // This will cause the DockChanged event to fire. + OnDockChanged(EventArgs.Empty); + } + } + } + + /// + /// Returns an owner window that can be used to + /// own a drop down. + /// + internal virtual NativeWindow DropDownOwnerWindow { + get { + if (dropDownOwnerWindow == null) { + dropDownOwnerWindow = new NativeWindow(); + } + + if (dropDownOwnerWindow.Handle == IntPtr.Zero) { + CreateParams cp = new CreateParams(); + cp.ExStyle = NativeMethods.WS_EX_TOOLWINDOW; + dropDownOwnerWindow.CreateHandle(cp); + } + + return dropDownOwnerWindow; + } + } + + /// + /// Returns the drop target manager that all the hwndless + /// items and this winbar share. this is necessary as + /// RegisterDragDrop requires an HWND. + /// + internal ToolStripDropTargetManager DropTargetManager { + get { + if (dropTargetManager == null) { + dropTargetManager = new ToolStripDropTargetManager(this); + } + return dropTargetManager; + + } + set { + dropTargetManager = value; + } + + } + /// + /// + /// + /// Just here so we can add the default value attribute + /// + protected internal virtual ToolStripItemCollection DisplayedItems { + get { + if (displayedItems == null) { + displayedItems = new ToolStripItemCollection(this, false); + } + return displayedItems; + } + } + + + /// + /// + /// + /// + /// Retreives the current display rectangle. The display rectangle + /// is the virtual display area that is used to layout components. + /// The position and dimensions of the Form's display rectangle + /// change during autoScroll. + /// + /// + public override Rectangle DisplayRectangle { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + Rectangle rect = base.DisplayRectangle; + + if ((LayoutEngine is ToolStripSplitStackLayout) && (GripStyle == ToolStripGripStyle.Visible)){ + + if (Orientation == Orientation.Horizontal) { + int gripwidth = Grip.GripThickness + Grip.Margin.Horizontal; + rect.Width -= gripwidth; + // in RTL.No we need to shift the rectangle + rect.X += (RightToLeft == RightToLeft.No) ? gripwidth : 0; + } + else { // Vertical Grip placement + int gripheight = Grip.GripThickness + Grip.Margin.Vertical; + rect.Y += gripheight; + rect.Height -= gripheight; + } + + } + return rect; + } + } + + /// + /// + /// + /// Forecolor really has no meaning for winbars - so lets hide it + /// + [Browsable(false)] + public new Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// + /// [ToolStrip ForeColorChanged event, overriden to turn browsing off.] + /// + [ + Browsable(false) + ] + public new event EventHandler ForeColorChanged + { + add + { + base.ForeColorChanged += value; + } + remove + { + base.ForeColorChanged -= value; + } + } + + private bool HasKeyboardInput { + get { + return (ContainsFocus || (ToolStripManager.ModalMenuFilter.InMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this)); + } + } + + /// + /// Summary of ToolStripGrip. + /// + /// + internal ToolStripGrip Grip { + get { + if (toolStripGrip == null) { + toolStripGrip = new ToolStripGrip(); + toolStripGrip.Overflow = ToolStripItemOverflow.Never; + toolStripGrip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible; + toolStripGrip.AutoSize = false; + toolStripGrip.ParentInternal = this; + toolStripGrip.Margin = DefaultGripMargin; + } + return toolStripGrip; + } + } + /// + /// + /// Summary of GripStyle. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripGripStyleDescr), + DefaultValue(ToolStripGripStyle.Visible) + ] + public ToolStripGripStyle GripStyle { + get { + return toolStripGripStyle; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripGripStyle.Hidden, (int)ToolStripGripStyle.Visible)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripGripStyle)); + } + if (toolStripGripStyle != value) { + toolStripGripStyle = value; + Grip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible; + LayoutTransaction.DoLayout(this, this, PropertyNames.GripStyle); + } + } + + } + + /// + /// + /// Summary of GripStyle. + /// + [ + Browsable(false) + ] + public ToolStripGripDisplayStyle GripDisplayStyle { + get { + return (LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow) ? ToolStripGripDisplayStyle.Vertical + : ToolStripGripDisplayStyle.Horizontal; + } + } + + /// + /// + /// The external spacing between the grip and the padding of the winbar and the first item in the collection + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.ToolStripGripDisplayStyleDescr) + ] + public Padding GripMargin { + get { + return Grip.Margin; + } + set { + Grip.Margin = value; + } + } + + /// + /// + /// The boundaries of the grip on the winbar. If it is invisible - returns Rectangle.Empty. + /// + [ + Browsable(false) + ] + public Rectangle GripRectangle { + get { + return (GripStyle == ToolStripGripStyle.Visible) ? Grip.Bounds : Rectangle.Empty; + } + } + + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool HasChildren { + get { + return base.HasChildren; + } + } + + internal bool HasVisibleItems { + get { + if (!IsHandleCreated) { + foreach(ToolStripItem item in Items) { + if (((IArrangedElement)item).ParticipatesInLayout) { + // set in the state so that when the handle is created, we're accurate. + SetToolStripState(STATE_HASVISIBLEITEMS, true); + return true; + } + } + SetToolStripState(STATE_HASVISIBLEITEMS, false); + return false; + } + // after the handle is created, we start layout... so this state is cached. + return GetToolStripState(STATE_HASVISIBLEITEMS); + } + set { + SetToolStripState(STATE_HASVISIBLEITEMS, value); + } + } + + /// + /// + /// Gets the Horizontal Scroll bar for this ScrollableControl. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + new public HScrollProperties HorizontalScroll + { + get + { + return base.HorizontalScroll; + } + } + + [ + DefaultValue(typeof(Size), "16,16"), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripImageScalingSizeDescr), + ] + public Size ImageScalingSize { + get { + return ImageScalingSizeInternal; + } + set { + ImageScalingSizeInternal = value; + } + } + + internal virtual Size ImageScalingSizeInternal { + get { + return imageScalingSize; + } + set { + if (imageScalingSize != value) { + imageScalingSize = value; + + LayoutTransaction.DoLayoutIf((Items.Count > 0), this, this, PropertyNames.ImageScalingSize); + foreach (ToolStripItem item in this.Items) { + item.OnImageScalingSizeChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// + /// Gets or sets the that contains the displayed on a label control. + /// + /// + [ + DefaultValue(null), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripImageListDescr), + Browsable(false) + ] + public ImageList ImageList { + get { + return imageList; + } + set { + if (imageList != value) { + EventHandler handler = new EventHandler(ImageListRecreateHandle); + + // Remove the previous imagelist handle recreate handler + // + if (imageList != null) { + imageList.RecreateHandle -= handler; + } + + imageList = value; + + // Add the new imagelist handle recreate handler + // + if (value != null) { + value.RecreateHandle += handler; + } + + foreach (ToolStripItem item in Items) { + item.InvalidateImageListImage(); + } + Invalidate(); + } + } + } + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal override bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + internal bool IsInToolStripPanel { + get { + return ToolStripPanelRow != null; + + } + } + + /// indicates whether the user is currently + /// moving the toolstrip from one toolstrip container + /// to another + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsCurrentlyDragging { + get { + return GetToolStripState(STATE_DRAGGING); + } + } + + + /// + /// indicates if the SetBoundsCore is called thru Locationchanging. + /// + private bool IsLocationChanging { + get { + return GetToolStripState(STATE_LOCATIONCHANGING); + } + } + + + /// + /// + /// The items that belong to this ToolStrip. + /// Note - depending on space and layout preferences, not all items + /// in this collection will be displayed. They may not even be displayed + /// on this winbar (say in the case where we're overflowing the item). + /// The collection of _Displayed_ items is the DisplayedItems collection. + /// The displayed items collection also includes things like the OverflowButton + /// and the Grip. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripItemsDescr), + MergableProperty(false) + ] + public virtual ToolStripItemCollection Items { + get { + if (toolStripItemCollection == null) { + toolStripItemCollection = new ToolStripItemCollection(this, true); + } + return toolStripItemCollection; + } + } + + + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemAddedDescr)] + public event ToolStripItemEventHandler ItemAdded { + add { + Events.AddHandler(EventItemAdded, value); + } + remove { + Events.RemoveHandler(EventItemAdded, value); + } + } + + + /// + /// + /// Occurs when the control is clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripItemOnClickDescr)] + public event ToolStripItemClickedEventHandler ItemClicked { + add { + Events.AddHandler(EventItemClicked, value); + } + remove { + Events.RemoveHandler(EventItemClicked, value); + } + } + + + /// + /// we have a backbuffer for painting items... this is cached to be the size of the largest + /// item in the collection - and is cached in OnPaint, and disposed when the toolstrip + /// is no longer visible. + /// + /// [: toolstrip - main hdc ] <-- visible to user + /// [ toolstrip double buffer hdc ] <-- onpaint hands us this buffer, after we're done DBuf is copied to "main hdc"/ + /// [tsi dc] <-- we copy the background from the DBuf, then paint the item into this DC, then BitBlt back up to DBuf + /// + /// This is done because GDI wont honor GDI+ TranslateTransform. We used to use DCMapping to change the viewport + /// origin and clipping rect of the toolstrip double buffer hdc to paint each item, but this proves costly + /// because you need to allocate GDI+ Graphics objects for every single item. This method allows us to only + /// allocate 1 Graphics object and share it between all the items in OnPaint. + /// + private CachedItemHdcInfo ItemHdcInfo { + get { + if (cachedItemHdcInfo == null) { + cachedItemHdcInfo = new CachedItemHdcInfo(); + } + return cachedItemHdcInfo; + } + } + + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemRemovedDescr)] + public event ToolStripItemEventHandler ItemRemoved { + add { + Events.AddHandler(EventItemRemoved, value); + } + remove { + Events.RemoveHandler(EventItemRemoved, value); + } + } + /// + /// handy check for painting and sizing + [Browsable(false)] + public bool IsDropDown { + get { return (this is ToolStripDropDown); } + } + + internal bool IsDisposingItems { + get { + return GetToolStripState(STATE_DISPOSINGITEMS); + } + } + /// + /// The OnDrag[blah] methods that will be called if AllowItemReorder is true. + /// + /// This allows us to have methods that handle drag/drop of the winbar items + /// without calling back on the user's code + /// + internal IDropTarget ItemReorderDropTarget { + get { + return itemReorderDropTarget; + } + set { + itemReorderDropTarget = value; + } + } + + /// + /// The OnQueryContinueDrag and OnGiveFeedback methods that will be called if + /// AllowItemReorder is true. + /// + /// This allows us to have methods that handle drag/drop of the winbar items + /// without calling back on the user's code + /// + internal ISupportOleDropSource ItemReorderDropSource { + get { + return itemReorderDropSource; + } + set { + itemReorderDropSource = value; + } + } + + internal bool IsInDesignMode { + get { + return DesignMode; + } + } + + internal bool IsTopInDesignMode { + get { + var topLevelToolStrip = GetToplevelOwnerToolStrip(); + return topLevelToolStrip != null && topLevelToolStrip.IsInDesignMode; + } + } + + internal bool IsSelectionSuspended { + get { return GetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE); } + } + + internal ToolStripItem LastMouseDownedItem { + get { + if (lastMouseDownedItem != null && + (lastMouseDownedItem.IsDisposed || lastMouseDownedItem.ParentInternal != this)){ + // handle disposal, parent changed since we last mouse downed. + lastMouseDownedItem = null; + } + return lastMouseDownedItem; + + } + } + + [ + DefaultValue(null), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public LayoutSettings LayoutSettings { + get { + return layoutSettings; + } + set { + layoutSettings = value; + } + } + + + /// + /// + /// Specifies whether we're horizontal or vertical + /// + [ + SRDescription(SR.ToolStripLayoutStyle), + SRCategory(SR.CatLayout), + AmbientValue(ToolStripLayoutStyle.StackWithOverflow) + ] + public ToolStripLayoutStyle LayoutStyle { + get { + if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow) { + switch (this.Orientation) { + case Orientation.Horizontal: + return ToolStripLayoutStyle.HorizontalStackWithOverflow; + case Orientation.Vertical: + return ToolStripLayoutStyle.VerticalStackWithOverflow; + } + } + return layoutStyle; + } + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripLayoutStyle.StackWithOverflow, (int)ToolStripLayoutStyle.Table)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripLayoutStyle)); + } + if (layoutStyle != value) { + layoutStyle = value; + + switch (value) { + case ToolStripLayoutStyle.Flow: + if (!(layoutEngine is FlowLayout)) { + layoutEngine = FlowLayout.Instance; + } + // Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location) + UpdateOrientation(Orientation.Horizontal); + break; + case ToolStripLayoutStyle.Table: + + if (!(layoutEngine is TableLayout)) { + layoutEngine = TableLayout.Instance; + } + // Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location) + UpdateOrientation(Orientation.Horizontal); + break; + case ToolStripLayoutStyle.StackWithOverflow: + case ToolStripLayoutStyle.HorizontalStackWithOverflow: + case ToolStripLayoutStyle.VerticalStackWithOverflow: + default: + + if (value != ToolStripLayoutStyle.StackWithOverflow) { + UpdateOrientation((value == ToolStripLayoutStyle.VerticalStackWithOverflow) ? Orientation.Vertical : Orientation.Horizontal); + } + else { + if (IsInToolStripPanel) { + UpdateLayoutStyle(ToolStripPanelRow.Orientation); + } + else { + UpdateLayoutStyle(this.Dock); + } + } + if (!(layoutEngine is ToolStripSplitStackLayout)) { + layoutEngine = new ToolStripSplitStackLayout(this); + } + break; + } + + using (LayoutTransaction.CreateTransactionIf(IsHandleCreated, this, this, PropertyNames.LayoutStyle)) { + LayoutSettings = CreateLayoutSettings(layoutStyle); + } + OnLayoutStyleChanged(EventArgs.Empty); + } + } + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutCompleteDescr)] + public event EventHandler LayoutCompleted { + add { + Events.AddHandler(EventLayoutCompleted, value); + } + remove { + Events.RemoveHandler(EventLayoutCompleted, value); + } + } + + internal bool LayoutRequired { + get { + return this.layoutRequired; + } + set { + this.layoutRequired = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutStyleChangedDescr)] + public event EventHandler LayoutStyleChanged { + add { + Events.AddHandler(EventLayoutStyleChanged, value); + } + remove { + Events.RemoveHandler(EventLayoutStyleChanged, value); + } + } + + /// + public override LayoutEngine LayoutEngine { + get { + // + return layoutEngine; + } + } + + + + /// + /// + /// [To be supplied.] + /// + internal event ToolStripLocationCancelEventHandler LocationChanging { + add { + Events.AddHandler(EventLocationChanging, value); + } + remove { + Events.RemoveHandler(EventLocationChanging, value); + } + } + + /// + protected internal virtual Size MaxItemSize { + get { + return this.DisplayRectangle.Size; + } + } + + internal bool MenuAutoExpand { + get { + if (!DesignMode) { + if (GetToolStripState(STATE_MENUAUTOEXPAND)) { + if (!IsDropDown && !ToolStripManager.ModalMenuFilter.InMenuMode) { + SetToolStripState(STATE_MENUAUTOEXPAND, false); + return false; + } + return true; + } + } + return false; + + } + set { + if (!DesignMode) { + SetToolStripState(STATE_MENUAUTOEXPAND, value); + } + + } + } + + internal Stack MergeHistoryStack { + get { + if(mergeHistoryStack == null) { + mergeHistoryStack = new Stack(); + } + return mergeHistoryStack; + } + } + + + private MouseHoverTimer MouseHoverTimer { + get { + if (mouseHoverTimer == null) { + mouseHoverTimer = new MouseHoverTimer(); + } + return mouseHoverTimer; + } + } + + + /// + /// + /// Summary of OverflowButton. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + public ToolStripOverflowButton OverflowButton { + get { + if (toolStripOverflowButton == null) { + toolStripOverflowButton = new ToolStripOverflowButton(this); + toolStripOverflowButton.Overflow = ToolStripItemOverflow.Never; + toolStripOverflowButton.ParentInternal = this; + toolStripOverflowButton.Alignment = ToolStripItemAlignment.Right; + toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size); + } + return toolStripOverflowButton; + } + } + + // + // SECREVIEW VSWhidbey 436973: adding control host items to this collection in + // the internet zone will throw security exceptions + // + internal ToolStripItemCollection OverflowItems { + get { + if (overflowItems == null) { + overflowItems = new ToolStripItemCollection(this, false); + } + return overflowItems; + } + } + + [Browsable(false)] + public Orientation Orientation { + get { + return orientation; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripPaintGripDescr)] + public event PaintEventHandler PaintGrip { + add { + Events.AddHandler(EventPaintGrip, value); + } + remove { + Events.RemoveHandler(EventPaintGrip, value); + } + } + + internal RestoreFocusMessageFilter RestoreFocusFilter { + get { + if (restoreFocusFilter == null) { + restoreFocusFilter = new RestoreFocusMessageFilter(this); + } + return restoreFocusFilter; + } + } + + internal ToolStripPanelCell ToolStripPanelCell { + get { return ((ISupportToolStripPanel)this).ToolStripPanelCell; } + } + + + internal ToolStripPanelRow ToolStripPanelRow { + get { return ((ISupportToolStripPanel)this).ToolStripPanelRow; } + } + + // fetches the Cell associated with this toolstrip. + ToolStripPanelCell ISupportToolStripPanel.ToolStripPanelCell { + get { + ToolStripPanelCell toolStripPanelCell = null; + if (!IsDropDown && !IsDisposed) { + if (Properties.ContainsObject(ToolStrip.PropToolStripPanelCell)) { + toolStripPanelCell = (ToolStripPanelCell)Properties.GetObject(ToolStrip.PropToolStripPanelCell); + } + else { + toolStripPanelCell = new ToolStripPanelCell(this); + Properties.SetObject(ToolStrip.PropToolStripPanelCell, toolStripPanelCell); + } + } + return toolStripPanelCell; + } + } + + + ToolStripPanelRow ISupportToolStripPanel.ToolStripPanelRow { + get { + ToolStripPanelCell cell = ToolStripPanelCell; + if (cell == null) { + return null; + } + return ToolStripPanelCell.ToolStripPanelRow; + } + set { + ToolStripPanelRow oldToolStripPanelRow = ToolStripPanelRow; + + if (oldToolStripPanelRow != value) { + ToolStripPanelCell cell = ToolStripPanelCell; + if (cell == null) { + return; + } + cell.ToolStripPanelRow = value; + + if (value != null) { + if (oldToolStripPanelRow == null || oldToolStripPanelRow.Orientation != value.Orientation) { + if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow) + { + UpdateLayoutStyle(value.Orientation); + } + else + { + UpdateOrientation(value.Orientation); + } + + } + } + else { + if (oldToolStripPanelRow != null && oldToolStripPanelRow.ControlsInternal.Contains(this)) { + oldToolStripPanelRow.ControlsInternal.Remove(this); + } + UpdateLayoutStyle(Dock); + } + } + + } + + } + + + [DefaultValue(false)] + [SRCategory(SR.CatLayout)] + [SRDescription(SR.ToolStripStretchDescr)] + public bool Stretch { + get { + return GetToolStripState(STATE_STRETCH); + } + set { + if (Stretch != value) { + SetToolStripState(STATE_STRETCH,value); + } + } + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3 && !DesignMode && !IsTopInDesignMode; + } + } + + /// + /// + /// The renderer is used to paint the hwndless winbar items. If someone wanted to + /// change the "Hot" look of all of their buttons to be a green triangle, they should + /// create a class that derives from ToolStripRenderer, assign it to this property and call + /// invalidate. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public ToolStripRenderer Renderer { + get { + + if (IsDropDown) { + // PERF: since this is called a lot we dont want to make it virtual + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown is ToolStripOverflow || dropDown.IsAutoGenerated) { + if (dropDown.OwnerToolStrip != null) { + return dropDown.OwnerToolStrip.Renderer; + } + } + } + if (RenderMode == ToolStripRenderMode.ManagerRenderMode) { + return ToolStripManager.Renderer; + } + // always return a valid renderer so our paint code + // doesn't have to be bogged down by checks for null. + + SetToolStripState(STATE_USEDEFAULTRENDERER, false); + if (renderer == null) { + Renderer = ToolStripManager.CreateRenderer(RenderMode); + } + return renderer; + + } + set { + // if the value happens to be null, the next get + // will autogenerate a new ToolStripRenderer. + if (renderer != value) { + SetToolStripState(STATE_USEDEFAULTRENDERER, (value == null)); + renderer = value; + currentRendererType = (renderer != null) ? renderer.GetType() : typeof(System.Type); + OnRendererChanged(EventArgs.Empty); + } + } + } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public event EventHandler RendererChanged { + add { + Events.AddHandler(EventRendererChanged, value); + } + remove { + Events.RemoveHandler(EventRendererChanged, value); + } + } + + /// + [ + SRDescription(SR.ToolStripRenderModeDescr), + SRCategory(SR.CatAppearance), + ] + public ToolStripRenderMode RenderMode { + get { + if (GetToolStripState(STATE_USEDEFAULTRENDERER)) { + return ToolStripRenderMode.ManagerRenderMode; + } + if (renderer != null && !renderer.IsAutoGenerated) { + return ToolStripRenderMode.Custom; + } + // check the type of the currently set renderer. + // types are cached as this may be called frequently. + if (currentRendererType == ToolStripManager.ProfessionalRendererType) { + return ToolStripRenderMode.Professional; + } + if (currentRendererType == ToolStripManager.SystemRendererType) { + return ToolStripRenderMode.System; + } + return ToolStripRenderMode.Custom; + + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripRenderMode.Custom, (int)ToolStripRenderMode.ManagerRenderMode)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripRenderMode)); + } + if (value == ToolStripRenderMode.Custom) { + throw new NotSupportedException(SR.GetString(SR.ToolStripRenderModeUseRendererPropertyInstead)); + } + + if (value == ToolStripRenderMode.ManagerRenderMode) { + if (!GetToolStripState(STATE_USEDEFAULTRENDERER)) { + SetToolStripState(STATE_USEDEFAULTRENDERER, true); + OnRendererChanged(EventArgs.Empty); + } + } + else { + SetToolStripState(STATE_USEDEFAULTRENDERER, false); + Renderer = ToolStripManager.CreateRenderer(value); + } + } + } + + + /// + /// ToolStripItems need to access this to determine if they should be showing underlines + /// for their accelerators. Since they are not HWNDs, and this method is protected on control + /// we need a way for them to get at it. + /// + internal bool ShowKeyboardCuesInternal { + get { + return this.ShowKeyboardCues; + + } + } + + + /// + [DefaultValue(true)] + [SRDescription(SR.ToolStripShowItemToolTipsDescr)] + [SRCategory(SR.CatBehavior)] + public bool ShowItemToolTips { + get { + return showItemToolTips; + } + set { + if (showItemToolTips != value) { + showItemToolTips = value; + if (!showItemToolTips) { + UpdateToolTip(null); + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip internalToolTip = this.ToolTip; + foreach (ToolStripItem item in this.Items) { + if (showItemToolTips) { + KeyboardToolTipStateMachine.Instance.Hook(item, internalToolTip); + } + else { + KeyboardToolTipStateMachine.Instance.Unhook(item, internalToolTip); + } + } + } + + // Fix for Dev10 889523 + // If the overflow button has not been created, don't check its properties + // since this will force its creating and cause a re-layout of the control + if (toolStripOverflowButton != null && this.OverflowButton.HasDropDownItems) { + this.OverflowButton.DropDown.ShowItemToolTips = value; + } + } + } + } + + /// internal lookup table for shortcuts... intended to speed search time + internal Hashtable Shortcuts { + get { + if (shortcuts == null) { + shortcuts = new Hashtable(1); + } + return shortcuts; + } + } + + /// + /// + /// Indicates whether the user can give the focus to this control using the TAB + /// key. This property is read-only. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + DispId(NativeMethods.ActiveX.DISPID_TABSTOP), + SRDescription(SR.ControlTabStopDescr) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + + /// this is the ToolTip used for the individual items + /// it only works if ShowItemToolTips = true + /// + internal ToolTip ToolTip { + get { + ToolTip toolTip; + if (!Properties.ContainsObject(ToolStrip.PropToolTip)) { + toolTip = new ToolTip(); + Properties.SetObject(ToolStrip.PropToolTip,toolTip ); + } + else { + toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip); + } + return toolTip; + } + } + + /// + [ + DefaultValue(ToolStripTextDirection.Horizontal), + SRDescription(SR.ToolStripTextDirectionDescr), + SRCategory(SR.CatAppearance) + ] + public virtual ToolStripTextDirection TextDirection { + get { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStrip.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStrip.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + textDirection = ToolStripTextDirection.Horizontal; + } + + return textDirection; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripTextDirection.Inherit, (int)ToolStripTextDirection.Vertical270)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripTextDirection)); + } + Properties.SetObject(ToolStrip.PropTextDirection, value); + + using(new LayoutTransaction(this, this, "TextDirection")) { + for (int i = 0; i < Items.Count; i++) { + Items[i].OnOwnerTextDirectionChanged(); + } + } + + } + } + + /// + /// + /// Gets the Vertical Scroll bar for this ScrollableControl. + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + new public VScrollProperties VerticalScroll + { + get + { + return base.VerticalScroll; + } + } + + void ISupportToolStripPanel.BeginDrag() { + OnBeginDrag(EventArgs.Empty); + } + + // Internal so that it's not a public API. + internal virtual void ChangeSelection(ToolStripItem nextItem) { + + if (nextItem != null) { + ToolStripControlHost controlHost = nextItem as ToolStripControlHost; + // if we contain focus, we should set focus to ourselves + // so we get the focus off the thing that's currently focused + // e.g. go from a text box to a toolstrip button + if (ContainsFocus && !Focused) { + this.FocusInternal(); + if (controlHost == null) { + // if nextItem IS a toolstripcontrolhost, we're going to focus it anyways + // we only fire KeyboardActive when "focusing" a non-hwnd backed item + KeyboardActive = true; + } + } + if (controlHost != null) { + if (hwndThatLostFocus == IntPtr.Zero) { + SnapFocus(UnsafeNativeMethods.GetFocus()); + } + controlHost.Control.Select(); + controlHost.Control.FocusInternal(); + } + + + nextItem.Select(); + + ToolStripMenuItem tsNextItem = nextItem as ToolStripMenuItem; + if (tsNextItem != null && !IsDropDown) { + // only toplevel menus auto expand when the selection changes. + tsNextItem.HandleAutoExpansion(); + } + + } + + } + + protected virtual LayoutSettings CreateLayoutSettings(ToolStripLayoutStyle layoutStyle) { + switch (layoutStyle) { + case ToolStripLayoutStyle.Flow: + return new FlowLayoutSettings(this); + case ToolStripLayoutStyle.Table: + return new TableLayoutSettings(this); + default: + return null; + } + } + + protected internal virtual ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) { + if (text == "-") { + return new ToolStripSeparator(); + } + else { + return new ToolStripButton(text,image,onClick); + } + } + + + /// + /// Summary of ClearAllSelections. + /// + private void ClearAllSelections() { + ClearAllSelectionsExcept(null); + } + + /// + /// Summary of ClearAllSelectionsExcept. + /// + /// + private void ClearAllSelectionsExcept(ToolStripItem item) { + Rectangle regionRect = (item == null) ? Rectangle.Empty : item.Bounds; + Region region = null; + + try { + + for (int i = 0; i < DisplayedItems.Count; i++) { + if (DisplayedItems[i] == item) { + continue; + } + else if (item != null && DisplayedItems[i].Pressed) { + // + ToolStripDropDownItem dropDownItem = DisplayedItems[i] as ToolStripDropDownItem; + + if (dropDownItem != null && dropDownItem.HasDropDownItems) { + dropDownItem.AutoHide(item); + } + } + bool invalidate = false; + if (DisplayedItems[i].Selected) { + DisplayedItems[i].Unselect(); + Debug.WriteLineIf(SelectionDebug.TraceVerbose,"[SelectDBG ClearAllSelectionsExcept] Unselecting " + DisplayedItems[i].Text); + invalidate = true; + } + + + if (invalidate) { + // since regions are heavy weight - only use if we need it. + if (region == null) { + region = new Region(regionRect); + } + region.Union(DisplayedItems[i].Bounds); + } + } + + // force an WM_PAINT to happen now to instantly reflect the selection change. + if (region != null) { + Invalidate(region, true); + Update(); + } + else if (regionRect != Rectangle.Empty) { + Invalidate(regionRect, true); + Update(); + } + + } + finally { + if (region != null) { + region.Dispose(); + } + } + // fire accessibility + if (IsHandleCreated && item != null) { + int focusIndex = DisplayedItems.IndexOf(item); + AccessibilityNotifyClients(AccessibleEvents.Focus, focusIndex); + } + } + + internal void ClearInsertionMark() { + if (lastInsertionMarkRect != Rectangle.Empty) { + // stuff away the lastInsertionMarkRect + // and clear it out _before_ we call paint OW + // the call to invalidate wont help as it will get + // repainted. + Rectangle invalidate = lastInsertionMarkRect; + lastInsertionMarkRect = Rectangle.Empty; + + this.Invalidate(invalidate); + } + + } + private void ClearLastMouseDownedItem() { + ToolStripItem lastItem = lastMouseDownedItem; + lastMouseDownedItem = null; + if (IsSelectionSuspended) { + SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, false); + if (lastItem != null) { + lastItem.Invalidate(); + } + } + } + + /// + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) { + if(disposing) { + ToolStripOverflow overflow = GetOverflow(); + + try { + + this.SuspendLayout(); + if (overflow != null) { + overflow.SuspendLayout(); + } + // if there's a problem in config, dont be a leaker. + SetToolStripState(STATE_DISPOSINGITEMS, true); + lastMouseDownedItem = null; + + HookStaticEvents(/*hook=*/false); + + ToolStripPanelCell toolStripPanelCell = Properties.GetObject(ToolStrip.PropToolStripPanelCell) as ToolStripPanelCell; + if (toolStripPanelCell != null) { + toolStripPanelCell.Dispose(); + } + + if (cachedItemHdcInfo != null) { + cachedItemHdcInfo.Dispose(); + } + + if (mouseHoverTimer != null) { + mouseHoverTimer.Dispose(); + } + + ToolTip toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip); + if (toolTip != null) { + toolTip.Dispose (); + } + + if (!Items.IsReadOnly) { + // only dispose the items we actually own. + for (int i = Items.Count - 1; i >= 0; i--) { + Items[i].Dispose(); + } + Items.Clear(); + } + // clean up items not in the Items list + if (toolStripGrip != null) { + toolStripGrip.Dispose(); + } + if (toolStripOverflowButton != null) { + toolStripOverflowButton.Dispose(); + } + + // remove the restore focus filter + if (restoreFocusFilter != null) { + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(restoreFocusFilter); + restoreFocusFilter = null; + } + + + // exit menu mode if necessary. + bool exitMenuMode = false; + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) { + exitMenuMode = true; + } + ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this); + // if we were the last toolstrip in the queue, exit menu mode. + if (exitMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == null) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "Exiting menu mode because we're the last toolstrip in the queue, and we've disposed."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + + ToolStripManager.ToolStrips.Remove(this); + } + finally { + + this.ResumeLayout(false); + if (overflow != null) { + overflow.ResumeLayout(false); + } + SetToolStripState(STATE_DISPOSINGITEMS, false); + } + } + base.Dispose( disposing ); + + } + + internal void DoLayoutIfHandleCreated(ToolStripItemEventArgs e) { + if (this.IsHandleCreated) { + LayoutTransaction.DoLayout(this, e.Item, PropertyNames.Items); + this.Invalidate(); + // Adding this item may have added it to the overflow + // However, we can't check if it's in OverflowItems, because + // it gets added there in Layout, and layout might be suspended. + if (this.CanOverflow && this.OverflowButton.HasDropDown) { + if (DeferOverflowDropDownLayout()) { + CommonProperties.xClearPreferredSizeCache(this.OverflowButton.DropDown); + this.OverflowButton.DropDown.LayoutRequired = true; + } + else { + LayoutTransaction.DoLayout(this.OverflowButton.DropDown, e.Item, PropertyNames.Items); + this.OverflowButton.DropDown.Invalidate(); + } + } + } + else { + // next time we fetch the preferred size, recalc it. + CommonProperties.xClearPreferredSizeCache(this); + this.LayoutRequired = true; + if (this.CanOverflow && this.OverflowButton.HasDropDown) { + this.OverflowButton.DropDown.LayoutRequired = true; + } + } + } + + private bool DeferOverflowDropDownLayout() { + return this.IsLayoutSuspended + ||!this.OverflowButton.DropDown.Visible + || !this.OverflowButton.DropDown.IsHandleCreated; + } + + void ISupportToolStripPanel.EndDrag() { + ToolStripPanel.ClearDragFeedback(); + OnEndDrag(EventArgs.Empty); + } + + internal ToolStripOverflow GetOverflow() { + return (toolStripOverflowButton == null || !toolStripOverflowButton.HasDropDown) ? null : toolStripOverflowButton.DropDown as ToolStripOverflow; + } + internal byte GetMouseId() { + // never return 0 as the mousedown ID, this is the "reset" value. + if (mouseDownID == 0) { + mouseDownID++; + } + return mouseDownID; + } + internal virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) { + + if (rtlAware && RightToLeft == RightToLeft.Yes) { + if (direction == ArrowDirection.Right) { + direction = ArrowDirection.Left; + } + else if (direction == ArrowDirection.Left) { + direction = ArrowDirection.Right; + } + } + return GetNextItem(start, direction); + } + + /// + /// + /// Gets the next item from the given start item in the direction specified. + /// - This function wraps if at the end + /// - This function will only surf the items in the current container + /// - Overriding this function will change the tab ordering and accessible child ordering. + /// + public virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction) + { + if (!WindowsFormsUtils.EnumValidator.IsValidArrowDirection(direction)) { + throw new InvalidEnumArgumentException("direction", (int)direction, typeof(ArrowDirection)); + } + + switch (direction) { + case ArrowDirection.Right: + return GetNextItemHorizontal(start, /*forward = */true); + case ArrowDirection.Left: + return GetNextItemHorizontal(start, /*forward = */false); + case ArrowDirection.Down: + return GetNextItemVertical(start, /*forward = */true); + case ArrowDirection.Up: + return GetNextItemVertical(start, /*forward = */false); + } + + return null; + } + + + // + // Helper function for GetNextItem - do not directly call this. + // + private ToolStripItem GetNextItemHorizontal(ToolStripItem start, bool forward) { + + if (DisplayedItems.Count <= 0) + return null; + + if (start == null) { + // The navigation should be consistent when navigating in forward and + // backward direction entering the toolstrip, it means that for AI.Level3 + // the first toolstrip item should be selected irrespectively TAB or SHIFT+TAB + // is pressed. + start = GetStartItem(forward); + } + + int current = DisplayedItems.IndexOf(start); + if (current == -1) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "Started from a visible = false item"); + return null; + } + + Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current != -1), "[SelectDBG GetNextToolStripItem] Last selected item was " + ((current != -1) ? DisplayedItems[current].Text : "")); + Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current == -1), "[SelectDBG GetNextToolStripItem] Last selected item was null"); + + int count = DisplayedItems.Count; + + do { + + if (forward) { + current = ++current % count; + } + else { // provide negative wrap if necessary + current = (--current < 0) ? count + current : current; + } + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown!= null) + { + if (dropDown.OwnerItem != null && dropDown.OwnerItem.IsInDesignMode) { + return DisplayedItems[current]; + } + } + if (DisplayedItems[current].CanKeyboardSelect) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG GetNextToolStripItem] selecting " + DisplayedItems[current].Text); + //ClearAllSelectionsExcept(Items[current]); + return DisplayedItems[current]; + } + + } while (DisplayedItems[current] != start); + return null; + } + + private ToolStripItem GetStartItem(bool forward) { + if (forward) { + return DisplayedItems[DisplayedItems.Count - 1]; + } + else if (AccessibilityImprovements.Level3 && !(this is ToolStripDropDown)) { + // For the drop-down up-directed loop should be preserved. + // So if the current item is topmost, then the bottom item should be selected on up-key press. + return DisplayedItems[DisplayedItems.Count > 1 ? 1 : 0]; + } + + return DisplayedItems[0]; + } + + + // + // Helper function for GetNextItem - do not directly call this. + // + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + private ToolStripItem GetNextItemVertical(ToolStripItem selectedItem, bool down) { + + ToolStripItem tanWinner = null; + ToolStripItem hypotenuseWinner = null; + + double minHypotenuse = Double.MaxValue; + double minTan = Double.MaxValue; + double hypotenuseOfTanWinner = Double.MaxValue; + double tanOfHypotenuseWinner = Double.MaxValue; + + if (selectedItem == null) { + ToolStripItem item = GetNextItemHorizontal(selectedItem, down); + return item; + } + + ToolStripDropDown dropDown = this as ToolStripDropDown; + if (dropDown != null) + { + if (dropDown.OwnerItem != null && (dropDown.OwnerItem.IsInDesignMode || (dropDown.OwnerItem.Owner != null && dropDown.OwnerItem.Owner.IsInDesignMode))) { + ToolStripItem item = GetNextItemHorizontal(selectedItem, down); + return item; + } + } + + + Point midPointOfCurrent = new Point(selectedItem.Bounds.X + selectedItem.Width / 2, + selectedItem.Bounds.Y + selectedItem.Height / 2); + + + + for(int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem otherItem = DisplayedItems[i]; + if (otherItem == selectedItem || !otherItem.CanKeyboardSelect) { + continue; + } + if (!down && otherItem.Bounds.Bottom > selectedItem.Bounds.Top) { + // if we are going up the other control has to be above + continue; + } + else if (down && otherItem.Bounds.Top < selectedItem.Bounds.Bottom) { + // if we are going down the other control has to be below + continue; + } + + //[ otherControl ] + // * + Point otherItemMidLocation = new Point(otherItem.Bounds.X + otherItem.Width/2, (down)? otherItem.Bounds.Top : otherItem.Bounds.Bottom); + #if DEBUG_UPDOWN + Graphics g = Graphics.FromHwnd(this.Handle); + + using (Pen p = new Pen(Color.FromKnownColor((KnownColor)i))) { + g.DrawLine(p,otherItemMidLocation, midPointOfCurrent); + } + System.Threading.Thread.Sleep(100); + g.Dispose(); + #endif + int oppositeSide = otherItemMidLocation.X - midPointOfCurrent.X; + int adjacentSide = otherItemMidLocation.Y - midPointOfCurrent.Y; + + // use pythagrian theorem to calculate the length of the distance + // between the middle of the current control in question and it's adjacent + // objects. + double hypotenuse = Math.Sqrt(adjacentSide*adjacentSide + oppositeSide*oppositeSide); + + if (adjacentSide != 0) { // avoid divide by zero - we dont do layered controls + // _[o] + // |/ + // [s] + // get the angle between s and o by taking the arctan. + // PERF consider using approximation instead + double tan = Math.Abs(Math.Atan(oppositeSide/adjacentSide)); + + // we want the thing with the smallest angle and smallest distance between midpoints + minTan = Math.Min(minTan, tan); + minHypotenuse = Math.Min(minHypotenuse, hypotenuse); + + if (minTan == tan && minTan != Double.NaN) { + tanWinner = otherItem; + hypotenuseOfTanWinner = hypotenuse; + } + + if (minHypotenuse == hypotenuse) { + hypotenuseWinner = otherItem; + tanOfHypotenuseWinner = tan; + } + } + } + + + #if DEBUG_UPDOWN + string tanWinnerString = (tanWinner == null) ? "null" : tanWinner.ToString(); + string hypWinnerString = (hypotenuseWinner == null) ? "null": hypotenuseWinner.ToString(); + Debug.WriteLine(String.Format("Tangent winner is {0} Hyp winner is {1}", tanWinnerString, hypWinnerString)); + + #endif + if ((tanWinner == null) || (hypotenuseWinner == null)) { + return (GetNextItemHorizontal(null,down)); + } + else { + // often times the guy with the best angle will be the guy with the closest hypotenuse. + // however in layouts where things are more randomly spaced, this is not necessarily the case. + if (tanOfHypotenuseWinner == minTan) { + // if the angles match up, such as in the case of items of the same width in vertical flow + // then pick the closest one. + return hypotenuseWinner; + } + else if ((!down && tanWinner.Bounds.Bottom <= hypotenuseWinner.Bounds.Top) + ||(down && tanWinner.Bounds.Top > hypotenuseWinner.Bounds.Bottom)) { + // we prefer the case where the angle is smaller than + // the case where the hypotenuse is smaller. The only + // scenarios where that is not the case is when the hypoteneuse + // winner is clearly closer than the angle winner. + + // [a.winner] | [s] + // | [h.winner] + // [h.winner] | + // [s] | [a.winner] + return hypotenuseWinner; + } + else { + return tanWinner; + } + } + } + + + internal override Size GetPreferredSizeCore(Size proposedSize) { + // We act like a container control + + // Translating 0,0 from ClientSize to actual Size tells us how much space + // is required for the borders. + if (proposedSize.Width == 1) { + proposedSize.Width = Int32.MaxValue; + } + if (proposedSize.Height == 1) { + proposedSize.Height = Int32.MaxValue; + } + + Padding padding = Padding; + Size prefSize = LayoutEngine.GetPreferredSize(this, proposedSize - padding.Size); + Padding newPadding = Padding; + + // VSWhidbey 471860: + // as a side effect of some of the layouts, we can change the padding. + // if this happens, we need to clear the cache. + if (padding != newPadding) { + CommonProperties.xClearPreferredSizeCache(this); + } + return prefSize + newPadding.Size; + + } + +#region GetPreferredSizeHelpers + + // + // These are here so they can be shared between splitstack layout and StatusStrip + // + internal static Size GetPreferredSizeHorizontal(IArrangedElement container, Size proposedConstraints) { + Size maxSize = Size.Empty; + ToolStrip toolStrip = container as ToolStrip; + + // ensure preferred size respects default size as a minimum. + Size defaultSize = toolStrip.DefaultSize - toolStrip.Padding.Size; + maxSize.Height = Math.Max(0, defaultSize.Height); + + bool requiresOverflow = false; + bool foundItemParticipatingInLayout = false; + + for (int j = 0; j < toolStrip.Items.Count; j++) { + ToolStripItem item = toolStrip.Items[j]; + + if (((IArrangedElement)item).ParticipatesInLayout) { + foundItemParticipatingInLayout =true; + if (item.Overflow != ToolStripItemOverflow.Always) { + Padding itemMargin = item.Margin; + Size prefItemSize = GetPreferredItemSize(item); + maxSize.Width += itemMargin.Horizontal + prefItemSize.Width; + maxSize.Height = Math.Max(maxSize.Height, itemMargin.Vertical + prefItemSize.Height); + } + else { + requiresOverflow = true; + } + } + } + + if (toolStrip.Items.Count == 0 || (!foundItemParticipatingInLayout)) { + // if there are no items there, create something anyways. + maxSize = defaultSize; + } + + + if (requiresOverflow) { + // add in the width of the overflow button + ToolStripOverflowButton overflowItem = toolStrip.OverflowButton; + Padding overflowItemMargin = overflowItem.Margin; + + maxSize.Width += overflowItemMargin.Horizontal + overflowItem.Bounds.Width; + } + else { + maxSize.Width += 2; //add Padding of 2 Pixels to the right if not Overflow. + } + + if (toolStrip.GripStyle == ToolStripGripStyle.Visible) { + // add in the grip width + Padding gripMargin = toolStrip.GripMargin; + maxSize.Width += gripMargin.Horizontal + toolStrip.Grip.GripThickness; + } + + maxSize = LayoutUtils.IntersectSizes(maxSize, proposedConstraints); + return maxSize; + } + + + [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] + + internal static Size GetPreferredSizeVertical(IArrangedElement container, Size proposedConstraints) { + Size maxSize = Size.Empty; + bool requiresOverflow = false; + ToolStrip toolStrip = container as ToolStrip; + + bool foundItemParticipatingInLayout = false; + + + for (int j = 0; j < toolStrip.Items.Count; j++) { + ToolStripItem item = toolStrip.Items[j]; + + if (((IArrangedElement)item).ParticipatesInLayout) { + foundItemParticipatingInLayout = true; + if (item.Overflow != ToolStripItemOverflow.Always) { + Size preferredSize = GetPreferredItemSize(item); + Padding itemMargin = item.Margin; + maxSize.Height += itemMargin.Vertical + preferredSize.Height; + maxSize.Width = Math.Max(maxSize.Width, itemMargin.Horizontal + preferredSize.Width); + } + else { + requiresOverflow = true; + } + } + } + + + if (toolStrip.Items.Count == 0 || !foundItemParticipatingInLayout) { + // if there are no items there, create something anyways. + maxSize = LayoutUtils.FlipSize( toolStrip.DefaultSize); + } + + if (requiresOverflow) { + // add in the width of the overflow button + ToolStripOverflowButton overflowItem = toolStrip.OverflowButton; + Padding overflowItemMargin = overflowItem.Margin; + maxSize.Height += overflowItemMargin.Vertical + overflowItem.Bounds.Height; + } + else { + maxSize.Height += 2; //add Padding to the bottom if not Overflow. + } + + if (toolStrip.GripStyle == ToolStripGripStyle.Visible) { + // add in the grip width + Padding gripMargin = toolStrip.GripMargin; + maxSize.Height += gripMargin.Vertical + toolStrip.Grip.GripThickness; + } + + // note here the difference in vertical - we want the strings to fit perfectly so we're not going to constrain by the specified size. + if (toolStrip.Size != maxSize) + { + CommonProperties.xClearPreferredSizeCache(toolStrip); + } + return maxSize; + } + + private static Size GetPreferredItemSize(ToolStripItem item) { + return item.AutoSize ? item.GetPreferredSize(Size.Empty) : item.Size; + } +#endregion +#region MeasurementGraphics + // + internal static Graphics GetMeasurementGraphics() { + return WindowsFormsUtils.CreateMeasurementGraphics(); + } +#endregion + /// + /// Summary of GetSelectedItem. + /// + internal ToolStripItem GetSelectedItem() { + ToolStripItem selectedItem = null; + + for (int i = 0; i < DisplayedItems.Count; i++) { + if (DisplayedItems[i].Selected) { + selectedItem = DisplayedItems[i]; + } + } + + return selectedItem; + } + /// + /// Retrieves the current value of the specified bit in the control's state. + /// + internal bool GetToolStripState(int flag) { + return (toolStripState & flag) != 0; + } + + internal virtual ToolStrip GetToplevelOwnerToolStrip() { + return this; + } + + /// In the case of a + /// toolstrip -> toolstrip + /// contextmenustrip -> the control that is showing it + /// toolstripdropdown -> top most toolstrip + internal virtual Control GetOwnerControl() { + return this; + } + + + private void HandleMouseLeave() { + // If we had a particular item that was "entered" + // notify it that we have left. + if (lastMouseActiveItem != null) { + if (!DesignMode) { + MouseHoverTimer.Cancel(lastMouseActiveItem); + } + try { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "firing mouse leave on " + lastMouseActiveItem.ToString()); + lastMouseActiveItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseLeave); + } + finally { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "setting last active item to null"); + lastMouseActiveItem = null; + } + } + ToolStripMenuItem.MenuTimer.HandleToolStripMouseLeave(this); + } + + /// + /// Summary of HandleItemClick. + /// + internal void HandleItemClick(ToolStripItem dismissingItem) { + ToolStripItemClickedEventArgs e= new ToolStripItemClickedEventArgs(dismissingItem); + OnItemClicked(e); + // VSWhidbey 395136 - ensure both the overflow and the main toolstrip fire ItemClick event + // otherwise the overflow wont dismiss. + if (!IsDropDown && dismissingItem.IsOnOverflow) { + OverflowButton.DropDown.HandleItemClick(dismissingItem); + } + } + + internal virtual void HandleItemClicked(ToolStripItem dismissingItem) { + // post processing after the click has happened. + /*if (ContainsFocus && !Focused) { + RestoreFocusInternal(); + }*/ + ToolStripDropDownItem item = dismissingItem as ToolStripDropDownItem; + if (item != null && !item.HasDropDownItems) + { + KeyboardActive = false; + } + + } + + private void HookStaticEvents(bool hook) { + if (hook) { + if (!alreadyHooked) { + try { + ToolStripManager.RendererChanged += new EventHandler(OnDefaultRendererChanged); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally{ + alreadyHooked = true; + } + } + } + else if (alreadyHooked) { + try { + ToolStripManager.RendererChanged -= new EventHandler(OnDefaultRendererChanged); + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally { + alreadyHooked = false; + } + + } + } + + //initialize winbar + private void InitializeRenderer(ToolStripRenderer renderer) { + // wrap this in a LayoutTransaction so that if they change sizes + // in this method we've suspended layout. + using(LayoutTransaction.CreateTransactionIf(AutoSize, this, this, PropertyNames.Renderer)) { + renderer.Initialize(this); + for (int i = 0; i < this.Items.Count; i++) { + renderer.InitializeItem(this.Items[i]); + } + } + Invalidate( this.Controls.Count > 0); + } + + + // sometimes you only want to force a layout if the winbar is visible. + private void InvalidateLayout() { + if (IsHandleCreated) { + LayoutTransaction.DoLayout(this, this, null); + } + } + internal void InvalidateTextItems() { + using (new LayoutTransaction(this, this, "ShowKeyboardFocusCues", /*PerformLayout=*/Visible)) { + for (int j = 0; j < DisplayedItems.Count; j++) { + if (((DisplayedItems[j].DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text)){ + DisplayedItems[j].InvalidateItemLayout("ShowKeyboardFocusCues"); + } + } + } + } + /// + /// + /// Summary of IsInputKey. + /// + /// + protected override bool IsInputKey(Keys keyData) { + ToolStripItem item = this.GetSelectedItem(); + if ((item != null) && item.IsInputKey(keyData)) { + return true; + } + return base.IsInputKey(keyData); + } + /// + /// + /// Summary of IsInputChar. + /// + /// + protected override bool IsInputChar(char charCode) { + ToolStripItem item = this.GetSelectedItem(); + if ((item != null) && item.IsInputChar(charCode)) { + return true; + } + return base.IsInputChar(charCode); + } + + private static bool IsPseudoMnemonic(char charCode, string text) { + if (!String.IsNullOrEmpty(text)) { + if (!WindowsFormsUtils.ContainsMnemonic(text)) { + char charToCompare = Char.ToUpper(charCode, CultureInfo.CurrentCulture); + char firstLetter = Char.ToUpper(text[0], CultureInfo.CurrentCulture); + if (firstLetter == charToCompare ||(Char.ToLower(charCode, CultureInfo.CurrentCulture) == Char.ToLower(text[0], CultureInfo.CurrentCulture)) ) { + return true; + } + } + } + return false; + } + + /// Force an item to be painted immediately, rather than waiting for WM_PAINT to occur. + internal void InvokePaintItem(ToolStripItem item) { + // Force a WM_PAINT to happen NOW. + Invalidate(item.Bounds); + Update(); + } + /// + /// Gets or sets the that contains the displayed on a label control + /// + private void ImageListRecreateHandle(object sender, EventArgs e) { + Invalidate(); + } + + /// + /// This override fires the LocationChanging event if + /// 1) We are not currently Rafting .. since this cause this infinite times... + /// 2) If we havent been called once .. Since the "LocationChanging" is listened to by the RaftingCell and calls "JOIN" which may call us back. + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + Point location = this.Location; + + if (!IsCurrentlyDragging && !IsLocationChanging && IsInToolStripPanel) + { + ToolStripLocationCancelEventArgs cae = new ToolStripLocationCancelEventArgs(new Point(x, y), false); + try { + if (location.X != x || location.Y != y) { + SetToolStripState(STATE_LOCATIONCHANGING, true); + OnLocationChanging(cae); + } + + if (!cae.Cancel) { + base.SetBoundsCore(x, y, width, height, specified); + } + } + finally { + SetToolStripState(STATE_LOCATIONCHANGING, false); + } + } + else { + if (IsCurrentlyDragging) { + Region transparentRegion = Renderer.GetTransparentRegion(this); + if (transparentRegion != null && (location.X != x || location.Y != y)) { + try { + Invalidate(transparentRegion); + Update(); + } + finally { + transparentRegion.Dispose(); + } + } + } + SetToolStripState(STATE_LOCATIONCHANGING, false); + base.SetBoundsCore(x, y, width, height, specified); + } + + } + + internal void PaintParentRegion(Graphics g, Region region) { + + } + + internal bool ProcessCmdKeyInternal(ref Message m, Keys keyData) { + return ProcessCmdKey(ref m, keyData); + } + + // This function will print to the PrinterDC. ToolStrip have there own buffered painting and doesnt play very well + // with the DC translations done by base Control class. Hence we do our own Painting and the BitBLT the DC into the printerDc. + // Refer to VsWhidbey : 400683. + internal override void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) { + using (Bitmap image = new Bitmap(bounds.Width, bounds.Height)) + using (Graphics g = Graphics.FromImage(image)) { + IntPtr imageHdc = g.GetHdc(); + //send the actual wm_print message + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.WM_PRINT, (IntPtr)imageHdc, + (IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT)); + + //now BLT the result to the destination bitmap. + IntPtr desthDC = hDC.Handle; + SafeNativeMethods.BitBlt(new HandleRef(this, desthDC), bounds.X, bounds.Y, bounds.Width, bounds.Height, + new HandleRef(g, imageHdc), 0, 0, NativeMethods.SRCCOPY); + g.ReleaseHdcInternal(imageHdc); + } + } + + /// + /// + /// Summary of ProcessCmdKey. + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message m, Keys keyData) { + + if (ToolStripManager.IsMenuKey(keyData)) { + if (!IsDropDown && ToolStripManager.ModalMenuFilter.InMenuMode) { + ClearAllSelections(); + ToolStripManager.ModalMenuFilter.MenuKeyToggle = true; + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.ProcessCmdKey] Detected a second ALT keypress while in Menu Mode."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + + } + + // Give the ToolStripItem very first chance at + // processing keys (except for ALT handling) + ToolStripItem selectedItem = this.GetSelectedItem(); + if (selectedItem != null){ + if (selectedItem.ProcessCmdKey(ref m, keyData)) { + return true; + } + } + + foreach (ToolStripItem item in this.Items) { + if (item == selectedItem) { + continue; + } + if (item.ProcessCmdKey(ref m, keyData)) { + return true; + } + } + + + if (!IsDropDown) { + bool isControlTab = + (keyData & Keys.Control) == Keys.Control && (keyData & Keys.KeyCode) == Keys.Tab; + + if (isControlTab && !TabStop && HasKeyboardInput) { + bool handled = false; + if ((keyData & Keys.Shift) == Keys.None) { + handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/true); + } + else { + handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/false); + } + if (handled) { + return true; + } + } + } + return base.ProcessCmdKey(ref m, keyData); + } + + /// + /// + /// Processes a dialog key. Overrides Control.processDialogKey(). This + /// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN + /// keys in dialogs. + /// The method performs no processing on keys that include the ALT or + /// CONTROL modifiers. For the TAB key, the method selects the next control + /// on the form. For the arrow keys, + /// !!! + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + bool retVal = false; + + // Give the ToolStripItem first dibs + ToolStripItem item = this.GetSelectedItem(); + if (item != null){ + if(item.ProcessDialogKey(keyData)) { + return true; + } + } + + // if the ToolStrip receives an escape, then we + // should send the focus back to the last item that + // had focus. + bool hasModifiers = ((keyData & (Keys.Alt | Keys.Control)) != Keys.None); + + Keys keyCode = (Keys)keyData & Keys.KeyCode; + switch (keyCode) { + case Keys.Back: + // if it's focused itself, process. if it's not focused, make sure a child control + // doesnt have focus before processing + if (!ContainsFocus) { + // shift backspace/backspace work as backspace, which is the same as shift+tab + retVal = ProcessTabKey(false); + } + break; + case Keys.Tab: + // ctrl+tab does nothing + if (!hasModifiers){ + retVal = ProcessTabKey((keyData & Keys.Shift) == Keys.None); + } + break; + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + retVal = ProcessArrowKey(keyCode); + break; + case Keys.Home: + SelectNextToolStripItem(null, /*forward =*/ true ); + retVal = true; + break; + case Keys.End: + SelectNextToolStripItem(null, /*forward =*/ false ); + retVal = true; + break; + case Keys.Escape: // escape and menu key should restore focus + // ctrl+esc does nothing + if (!hasModifiers && !TabStop){ + RestoreFocusInternal(); + retVal = true; + } + break; + + } + + + if (retVal) { + return retVal; + } + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] calling base"); + return base.ProcessDialogKey(keyData); + } + + internal virtual void ProcessDuplicateMnemonic(ToolStripItem item, char charCode) { + if (!CanProcessMnemonic()) { // Checking again for security... + return; + } + // SECREVIEW see toolstrip dropdown ProcessDuplicateMnemonic for special security implications + if (item != null) { + // SECREVIEW: SetFocusUnsafe as ProcessMnemonic has link demand for AWP. + SetFocusUnsafe(); + item.Select(); + } + } + /// + /// + /// + /// Rules for parsing mnemonics + /// PASS 1: Real mnemonics + /// Check items for the character after the &. If it matches, perform the click event or open the dropdown (in the case that it has dropdown items) + /// PASS 2: Fake mnemonics + /// Begin with the current selection and parse through the first character in the items in the menu. + /// If there is only one item that matches + /// perform the click event or open the dropdown (in the case that it has dropdown items) + /// Else + /// change the selection from the current selected item to the first item that matched. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // menus and toolbars only take focus on ALT + if (!CanProcessMnemonic()) { + return false; + } + if (Focused || ContainsFocus) { + return ProcessMnemonicInternal(charCode); + } + bool inMenuMode = ToolStripManager.ModalMenuFilter.InMenuMode; + if (!inMenuMode && Control.ModifierKeys == Keys.Alt) { + // This is the case where someone hasnt released the ALT key yet, but has pushed another letter. + // In some cases we can activate the menu that is not the MainMenuStrip... + // See VSWhidbey 501382 for more details. + return ProcessMnemonicInternal(charCode); + } + else if (inMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) { + return ProcessMnemonicInternal(charCode); + } + + // do not call base, as we dont want to walk through the controls collection and reprocess everything + // we should have processed in the displayed items collection. + return false; + + } + private bool ProcessMnemonicInternal(char charCode) { + if (!CanProcessMnemonic()) { // Checking again for security... + return false; + } + // at this point we assume we can process mnemonics as process mnemonic has filtered for use. + ToolStripItem startingItem = GetSelectedItem(); + int startIndex = 0; + if (startingItem != null) { + startIndex = DisplayedItems.IndexOf(startingItem); + } + startIndex = Math.Max(0, startIndex); + + ToolStripItem firstMatch = null; + bool foundMenuItem = false; + int index = startIndex; + + // PASS1, iterate through the real mnemonics + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem currentItem = DisplayedItems[index]; + + index = (index +1)%DisplayedItems.Count; + if (string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) { + continue; + } + // VSWhidbey 429513 - only items which display text should be processed + if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) { + continue; + } + // keep track whether we've found a menu item - we'll have to do a + // second pass for fake mnemonics in that case. + foundMenuItem = (foundMenuItem || (currentItem is ToolStripMenuItem)); + + if (Control.IsMnemonic(charCode,currentItem.Text)) { + if (firstMatch == null) { + firstMatch = currentItem; + } + else { + // we've found a second match - we should only change selection. + if (firstMatch == startingItem) { + // change the selection to be the second match as the first is already selected + ProcessDuplicateMnemonic(currentItem, charCode); + } + else { + ProcessDuplicateMnemonic(firstMatch, charCode); + } + // we've found two mnemonics, just return. + return true; + } + } + } + // We've found a singular match. + if (firstMatch != null) { + return firstMatch.ProcessMnemonic(charCode); + } + + if (!foundMenuItem) { + return false; + } + + index = startIndex; + + // 242501 MenuStrip parity: key presses should change selection if mnemonic not present + // if we havent found a mnemonic, cycle through the menu items and + // checbbbMk if we match. + + // PASS2, iterate through the pseudo mnemonics + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem currentItem = DisplayedItems[index]; + index = (index +1)%DisplayedItems.Count; + + // Menu items only + if (!(currentItem is ToolStripMenuItem) || string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) { + continue; + } + // VSWhidbey 429513 - only items which display text should be processed + if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) { + continue; + } + + + if (ToolStrip.IsPseudoMnemonic(charCode,currentItem.Text)) { + if (firstMatch == null) { + firstMatch = currentItem; + } + else { + // we've found a second match - we should only change selection. + if (firstMatch == startingItem) { + // change the selection to be the second match as the first is already selected + ProcessDuplicateMnemonic(currentItem, charCode); + } + else { + ProcessDuplicateMnemonic(firstMatch, charCode); + } + // we've found two mnemonics, just return. + return true; + } + } + } + + if (firstMatch != null) { + return firstMatch.ProcessMnemonic(charCode); + } + + // do not call base, as we dont want to walk through the controls collection and reprocess everything + // we should have processed in the displayed items collection. + return false; + } + + /// + /// + /// Summary of ProcessTabKey. + /// + /// + private bool ProcessTabKey(bool forward) { + if (TabStop) { + // ToolBar in tab-order parity + // this means we want the toolstrip in the normal tab order - which means it shouldnt wrap. + // First tab gets you into the toolstrip, second tab moves you on your way outside the container. + // arrow keys would continue to wrap. + return false; + } + else { + // TabStop = false + // this means we dont want the toolstrip in the normal tab order (default). + // We got focus to the toolstrip by putting focus into a control contained on the toolstrip or + // via a mnemonic e.g. Bold. In this case we want to wrap. + // arrow keys would continue to wrap + if (RightToLeft == RightToLeft.Yes) { + forward = !forward; + } + SelectNextToolStripItem(GetSelectedItem(), forward); + return true; + } + } + + /// + /// + /// Summary of ProcessArrowKey: this is more useful than overriding ProcessDialogKey because usually + /// the difference between ToolStrip/ToolStripDropDown is arrow key handling. ProcessDialogKey first gives + /// the selected ToolStripItem the chance to process the message... so really a proper inheritor would + /// call down to the base first. Unfortunately doing this would cause the the arrow keys would be eaten + /// in the base class. Instead we're providing a separate place to override all arrow key handling. + /// + internal virtual bool ProcessArrowKey(Keys keyCode) { + + bool retVal = false; + Debug.WriteLineIf(MenuAutoExpandDebug.TraceVerbose, "[ToolStrip.ProcessArrowKey] MenuTimer.Cancel called"); + ToolStripMenuItem.MenuTimer.Cancel(); + + switch (keyCode) { + case Keys.Left: + case Keys.Right: + retVal = ProcessLeftRightArrowKey(keyCode == Keys.Right); + break; + case Keys.Up: + case Keys.Down: + if (IsDropDown || Orientation != Orientation.Horizontal) { + ToolStripItem currentSel = GetSelectedItem(); + if (keyCode == Keys.Down) { + ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Down); + if (nextItem != null) { + ChangeSelection(nextItem); + retVal = true; + } + } + else { + ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Up); + if (nextItem != null){ + ChangeSelection(nextItem); + retVal = true; + } + } + } + break; + } + return retVal; + } + + /// + /// Process an arrowKey press by selecting the next control in the group + /// that the activeControl belongs to. + /// + /// + private bool ProcessLeftRightArrowKey(bool right) { + ToolStripItem selectedItem = GetSelectedItem(); + ToolStripItem nextItem = SelectNextToolStripItem(GetSelectedItem(), right); + return true; + } + + /// + /// Summary of NotifySelectionChange. + /// + /// + internal void NotifySelectionChange(ToolStripItem item) { + if (item == null) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] none should be selected"); + ClearAllSelections(); + } + else if (item.Selected) { + Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] Notify selection change: " + item.ToString() + ": " + item.Selected.ToString()); + ClearAllSelectionsExcept(item); + } + } + + private void OnDefaultRendererChanged(object sender, EventArgs e) { + // callback from static event + if (GetToolStripState(STATE_USEDEFAULTRENDERER)) { + OnRendererChanged(e); + } + } + + protected virtual void OnBeginDrag(EventArgs e) { + SetToolStripState(STATE_DRAGGING, true); + Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?"); + Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?"); + + ClearAllSelections(); + UpdateToolTip(null); // supress the tooltip. + EventHandler handler = (EventHandler)Events[EventBeginDrag]; + if (handler != null) handler(this,e); + } + + protected virtual void OnEndDrag(EventArgs e) { + SetToolStripState(STATE_DRAGGING, false); + Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?"); + Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?"); + Debug.Assert(ToolStripPanelRow == null || ToolStripPanelRow.ToolStripPanel.RowsInternal.Contains(ToolStripPanelRow), "Why are we in an orphaned row?"); + + EventHandler handler = (EventHandler)Events[EventEndDrag]; + if (handler != null) handler(this,e); + + } + + + + protected override void OnDockChanged(EventArgs e){ + base.OnDockChanged(e); + } + + /// + protected virtual void OnRendererChanged(EventArgs e) { + InitializeRenderer(Renderer); + + EventHandler handler = (EventHandler)Events[EventRendererChanged]; + if (handler != null) handler(this,e); + + } + /// + /// + /// Summary of OnEnabledChanged. + /// + protected override void OnEnabledChanged(EventArgs e) { + base.OnEnabledChanged(e); + + // notify items that the parent has changed + for (int i = 0; i < this.Items.Count; i++) { + if (Items[i] != null && Items[i].ParentInternal == this) { + Items[i].OnParentEnabledChanged(e); + } + } + + } + + + internal void OnDefaultFontChanged() { + defaultFont = null; + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + ToolStripManager.CurrentDpi = DeviceDpi; + defaultFont = ToolStripManager.DefaultFont; + } + if (!IsFontSet()) { + OnFontChanged(EventArgs.Empty); + } + } + + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + for (int i = 0; i < this.Items.Count; i++) { + Items[i].OnOwnerFontChanged(e); + } + } + + protected override void OnInvalidated(InvalidateEventArgs e) { + base.OnInvalidated(e); +#if false +// DEBUG code which is helpful for FlickerFest debugging. + if (FlickerDebug.TraceVerbose) { + string name = this.Name; + if (string.IsNullOrEmpty(name)) { + if (IsDropDown) { + ToolStripItem item = ((ToolStripDropDown)this).OwnerItem; + if (item != null && item.Name != null) { + name = item.Name = ".DropDown"; + } + } + if (string.IsNullOrEmpty(name)) { + name = this.GetType().Name; + } + } + // for debugging VS we want to filter out the propgrid toolstrip + Debug.WriteLineIf(!(this.ParentInternal is PropertyGrid), "Invalidate called on: " + name + new StackTrace().ToString()); + } +#endif + } + /// + /// + /// Summary of OnHandleCreated. + /// + protected override void OnHandleCreated(EventArgs e) { + if ((this.AllowDrop || this.AllowItemReorder) && (DropTargetManager != null)) { + this.DropTargetManager.EnsureRegistered(this); + } + + // calling control's (in base) version AFTER we register our DropTarget, so it will + // listen to us instead of control's implementation + base.OnHandleCreated(e); + } + + /// + /// + /// Summary of OnHandleDestroyed. + /// + protected override void OnHandleDestroyed(EventArgs e) { + if (DropTargetManager != null) { + // Make sure we unregister ourselves as a drop target + this.DropTargetManager.EnsureUnRegistered(this); + } + base.OnHandleDestroyed(e); + } + + /// + protected internal virtual void OnItemAdded(ToolStripItemEventArgs e) { + DoLayoutIfHandleCreated(e); + + if (!HasVisibleItems && e.Item != null && ((IArrangedElement)e.Item).ParticipatesInLayout) { + // VSWhidbey 441403 + // in certain cases, we may not have laid out yet (e.g. a dropdown may not layout until + // it becomes visible.) We will recalculate this in SetDisplayedItems, but for the moment + // if we find an item that ParticipatesInLayout, mark us as having visible items. + HasVisibleItems = true; + } + + ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemAdded]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Called when an item has been clicked on the winbar. + /// + protected virtual void OnItemClicked(ToolStripItemClickedEventArgs e) { + ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventItemClicked]; + if (handler != null) handler(this, e); + + } + + /// + protected internal virtual void OnItemRemoved(ToolStripItemEventArgs e) { + + // clear cached item states. + OnItemVisibleChanged(e, /*performlayout*/true); + + ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemRemoved]; + if (handler != null) handler(this, e); + } + + + internal void OnItemVisibleChanged(ToolStripItemEventArgs e, bool performLayout) { + + // clear cached item states. + if (e.Item == lastMouseActiveItem) { + lastMouseActiveItem = null; + } + if (e.Item == LastMouseDownedItem) { + lastMouseDownedItem = null; + } + if (e.Item == currentlyActiveTooltipItem) { + UpdateToolTip(null); + } + if (performLayout) { + DoLayoutIfHandleCreated(e); + } + } + /// + protected override void OnLayout(LayoutEventArgs e) { + this.LayoutRequired = false; + + // we need to do this to prevent autosizing to happen while we're reparenting. + ToolStripOverflow overflow = GetOverflow(); + if (overflow != null) { + overflow.SuspendLayout(); + toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size); + } + + for (int j = 0; j < Items.Count; j++) { + Items[j].OnLayout(e); + } + + base.OnLayout(e); + SetDisplayedItems(); + OnLayoutCompleted(EventArgs.Empty); + Invalidate(); + + if (overflow != null) { + overflow.ResumeLayout(); + } + } + + /// + protected virtual void OnLayoutCompleted(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventLayoutCompleted]; + if (handler != null) handler(this, e); + } + + /// + protected virtual void OnLayoutStyleChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventLayoutStyleChanged]; + if (handler != null) handler(this, e); + } + + /// + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + ClearAllSelections(); + } + + protected override void OnLeave(EventArgs e) { + base.OnLeave(e); + if (!IsDropDown) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "uninstalling RestoreFocusFilter"); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter); + } + } + /// + internal virtual void OnLocationChanging(ToolStripLocationCancelEventArgs e) { + ToolStripLocationCancelEventHandler handler = (ToolStripLocationCancelEventHandler)Events[EventLocationChanging]; + if (handler != null) handler(this, e); + } + + /// + /// + /// Delegate mouse down to the winbar and its affected items + /// + protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) { + + // NEVER use this directly from another class. Always use GetMouseID so that + // 0 is not returned to another class. + mouseDownID++; + + ToolStripItem item = GetItemAt(mea.X, mea.Y); + if (item != null) { + if (!IsDropDown && (!(item is ToolStripDropDownItem))){ + // set capture only when we know we're not on a dropdown (already effectively have capture due to modal menufilter) + // and the item in question requires the mouse to be in the same item to be clicked. + SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, true); + this.CaptureInternal = true; + } + MenuAutoExpand = true; + + if (mea != null) { + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + } + lastMouseDownedItem = item; + item.FireEvent(mea, ToolStripItemEventType.MouseDown); + } + else { + base.OnMouseDown(mea); + } + + } + + + /// + /// + /// Delegate mouse moves to the winbar and its affected items + /// + protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose,"OnMouseMove called"); + + ToolStripItem item = GetItemAt(mea.X, mea.Y); + + if (!Grip.MovingToolStrip) { + + // If we had a particular item that was "entered" + // notify it that we have entered. It's fair to put + // this in the MouseMove event, as MouseEnter is fired during + // control's WM_MOUSEMOVE. Waiting until this event gives us + // the actual coordinates. + + + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Item to get mouse move: {0}", (item == null) ? "null" : item.ToString())); + if (item != lastMouseActiveItem) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "This is a new item - last item to get was {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString())); + + // notify the item that we've moved on + HandleMouseLeave(); + + // track only items that dont get mouse events themselves. + lastMouseActiveItem = (item is ToolStripControlHost) ? null : item; + + if (lastMouseActiveItem != null) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseEnter on: {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString())); + item.FireEvent(new System.EventArgs(), ToolStripItemEventType.MouseEnter); + } + // + + if (!DesignMode) { + MouseHoverTimer.Start(lastMouseActiveItem); + } + + + + } + } + else { + item = this.Grip; + } + if (item != null) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (item == null) ? "null" : item.ToString())); + + // Fire mouse move on the item + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + item.FireEvent(mea, ToolStripItemEventType.MouseMove); + } + else { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (this == null) ? "null" : this.ToString())); + + base.OnMouseMove(mea); + } + } + + + + /// + /// + /// Delegate mouse leave to the winbar and its affected items + /// + protected override void OnMouseLeave(System.EventArgs e) { + HandleMouseLeave(); + base.OnMouseLeave(e); + } + + + /// + protected override void OnMouseCaptureChanged(System.EventArgs e) { + if (!GetToolStripState(STATE_SUSPENDCAPTURE)) { + // while we're showing a feedback rect, dont cancel moving the toolstrip. + Grip.MovingToolStrip = false; + } + ClearLastMouseDownedItem(); + + + + base.OnMouseCaptureChanged(e); + } + + /// + /// + /// Delegate mouse up to the winbar and its affected items + /// + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) { + + ToolStripItem item = (Grip.MovingToolStrip) ? Grip : GetItemAt(mea.X, mea.Y); + + if (item != null) { + if (mea != null) { + // Transpose this to "client coordinates" of the ToolStripItem. + Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords); + mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta); + } + item.FireEvent(mea, ToolStripItemEventType.MouseUp); + } + else { + base.OnMouseUp(mea); + } + ClearLastMouseDownedItem(); + + } + + + + + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + + Graphics toolstripGraphics = e.Graphics; + Size bitmapSize = this.largestDisplayedItemSize; + bool excludedTransparentRegion = false; + + Rectangle viewableArea = this.DisplayRectangle; + Region transparentRegion = Renderer.GetTransparentRegion(this); + + try { + + // Paint the items + // The idea here is to let items pretend they are controls. + // they should get paint events at 0,0 and have proper clipping regions + // set up for them. We cannot use g.TranslateTransform as that does + // not translate the GDI world, and things like XP Visual Styles and the + // TextRenderer only know how to speak GDI. + // + // The previous appropach was to set up the GDI clipping region and allocate a graphics + // from that, but that meant we were allocating graphics objects left and right, which + // turned out to be slow. + // + // So now we allocate an offscreen bitmap of size == MaxItemSize, copy the background + // of the toolstrip into that bitmap, then paint the item on top of the bitmap, then copy + // the contents of the bitmap back onto the toolstrip. This gives us our paint event starting + // at 0,0. Combine this with double buffering of the toolstrip and the entire toolstrip is updated + // after returning from this function. + if (!LayoutUtils.IsZeroWidthOrHeight(bitmapSize)) { + // cant create a 0x0 bmp. + + // Supporting RoundedEdges... + // we've got a concept of a region that we shouldnt paint (the TransparentRegion as specified in the Renderer). + // in order to support this we're going to intersect that region with the clipping region. + // this new region will be excluded during the guts of OnPaint, and restored at the end of OnPaint. + if (transparentRegion != null) { + // only use the intersection so we can easily add back in the bits we took out at the end. + transparentRegion.Intersect(toolstripGraphics.Clip); + toolstripGraphics.ExcludeClip(transparentRegion); + excludedTransparentRegion = true; + } + + // Preparing for painting the individual items... + // using WindowsGraphics here because we want to preserve the clipping information. + + // calling GetHdc by itself does not set up the clipping info. + using(WindowsGraphics toolStripWindowsGraphics = WindowsGraphics.FromGraphics(toolstripGraphics, ApplyGraphicsProperties.Clipping)){ + // get the cached item HDC. + HandleRef toolStripHDC = new HandleRef(this, toolStripWindowsGraphics.GetHdc()); + HandleRef itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize); + + Graphics itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle); + try { + // Painting the individual items... + // iterate through all the items, painting them + // one by one into the compatible offscreen DC, and then copying + // them back onto the main toolstrip. + for (int i = 0; i < DisplayedItems.Count; i++) { + ToolStripItem item = DisplayedItems[i]; + if (item != null) { // + Rectangle clippingRect = e.ClipRectangle; + Rectangle bounds = item.Bounds; + + if (!IsDropDown && item.Owner == this) { + // owned items should not paint outside the client + // area. (this is mainly to prevent obscuring the grip + // and overflowbutton - ToolStripDropDownMenu places items + // outside of the display rectangle - so we need to allow for this + // in dropdoowns). + clippingRect.Intersect(viewableArea); + } + + // get the intersection of these two. + clippingRect.Intersect(bounds); + + if (LayoutUtils.IsZeroWidthOrHeight(clippingRect)) { + continue; // no point newing up a graphics object if there's nothing to paint. + } + + Size itemSize = item.Size; + + // check if our item buffer is large enough to handle. + if (!LayoutUtils.AreWidthAndHeightLarger(bitmapSize, itemSize)) { + // the cached HDC isnt big enough for this item. make it bigger. + this.largestDisplayedItemSize = itemSize; + bitmapSize = itemSize; + // dispose the old graphics - create a new, bigger one. + itemGraphics.Dispose(); + + // calling this should take the existing DC and select in a bigger bitmap. + itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize); + + // allocate a new graphics. + itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle); + + } + // since the item graphics object will have 0,0 at the + // corner we need to actually shift the origin of the rect over + // so it will be 0,0 too. + clippingRect.Offset(-bounds.X, -bounds.Y); + + // PERF - consider - we only actually need to copy the clipping rect. + // copy the background from the toolstrip onto the offscreen bitmap + SafeNativeMethods.BitBlt(itemHDC, 0, 0, item.Size.Width, item.Size.Height, toolStripHDC, item.Bounds.X, item.Bounds.Y, NativeMethods.SRCCOPY); + + // paint the item into the offscreen bitmap + using (PaintEventArgs itemPaintEventArgs = new PaintEventArgs(itemGraphics, clippingRect)) { + item.FireEvent(itemPaintEventArgs, ToolStripItemEventType.Paint); + } + + // copy the item back onto the toolstrip + SafeNativeMethods.BitBlt(toolStripHDC, item.Bounds.X, item.Bounds.Y, item.Size.Width, item.Size.Height, itemHDC, 0, 0, NativeMethods.SRCCOPY); + + } + } + } + finally { + if (itemGraphics != null) { + itemGraphics.Dispose(); + } + } + } + + } + + // Painting the edge effects... + // These would include things like (shadow line on the bottom, some overflow effects) + Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(toolstripGraphics, this)); + + // Restoring the clip region to its original state... + // the transparent region should be added back in as the insertion mark should paint over it. + if (excludedTransparentRegion) { + toolstripGraphics.SetClip(transparentRegion,CombineMode.Union); + } + + // Paint the item re-order insertion mark... + // This should ignore the transparent region and paint + // over the entire area. + PaintInsertionMark(toolstripGraphics); + } + finally { + if (transparentRegion != null) { + transparentRegion.Dispose(); + } + } + } + + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + + // normally controls just need to do handle recreation, but ToolStrip does it based on layout of items. + using(new LayoutTransaction(this, this, PropertyNames.RightToLeft)) { + for (int i = 0; i < Items.Count; i++) { + Items[i].OnParentRightToLeftChanged(e); + } + if (toolStripOverflowButton != null) { + toolStripOverflowButton.OnParentRightToLeftChanged(e); + } + if (toolStripGrip != null) { + toolStripGrip.OnParentRightToLeftChanged(e); + } + } + + + } + + /// + /// + /// Inheriting classes should override this method to handle the erase + /// background request from windows. It is not necessary to call + /// base.onPaintBackground, however if you do not want the default + /// Windows behavior you must set event.handled to true. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaintBackground(PaintEventArgs e) { + base.OnPaintBackground(e); + + Graphics g = e.Graphics; + GraphicsState graphicsState = g.Save(); + try { + using (Region transparentRegion = Renderer.GetTransparentRegion(this)) { + if (transparentRegion != null) { + EraseCorners(e, transparentRegion); + g.ExcludeClip(transparentRegion); + } + } + Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(g, this)); + + } + finally { + if (graphicsState != null) { + g.Restore(graphicsState); + } + } + } + + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + if (!Disposing && !IsDisposed) { + HookStaticEvents(Visible); + } + } + + + + private void EraseCorners(PaintEventArgs e, Region transparentRegion) { + if (transparentRegion != null) { + PaintTransparentBackground(e, ClientRectangle, transparentRegion); + } + } + + /// + /// + /// Summary of OnPaint. + /// + internal protected virtual void OnPaintGrip(System.Windows.Forms.PaintEventArgs e) { + + Renderer.DrawGrip(new ToolStripGripRenderEventArgs(e.Graphics, this)); + + PaintEventHandler handler = (PaintEventHandler)Events[EventPaintGrip]; + if (handler != null) handler(this,e); + + + } + + /// + protected override void OnScroll(ScrollEventArgs se) { + + if (se.Type != ScrollEventType.ThumbTrack && se.NewValue != se.OldValue) { + ScrollInternal(se.OldValue - se.NewValue); + } + base.OnScroll(se); + + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { + switch (e.Category) { + case UserPreferenceCategory.Window: + OnDefaultFontChanged(); + break; + case UserPreferenceCategory.General: + InvalidateTextItems(); + break; + } + + } + + protected override void OnTabStopChanged(EventArgs e) { + // VSWhidbey 442518 - SelectNextControl can select non-tabstop things. + // we need to prevent this by changing the value of "CanSelect" + SetStyle(ControlStyles.Selectable, TabStop); + base.OnTabStopChanged(e); + } + + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on. + /// + /// Old DPI value + /// New DPI value + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + if (deviceDpiOld != deviceDpiNew) { + ToolStripManager.CurrentDpi = deviceDpiNew; + defaultFont = ToolStripManager.DefaultFont; + + // We need to take care of this control. + ResetScaling(deviceDpiNew); + + // We need to scale the one Grip per ToolStrip as well (if present). + if (toolStripGrip != null) { + toolStripGrip.ToolStrip_RescaleConstants(deviceDpiOld, deviceDpiNew); + } + + // We need to delegate this "event" to the Controls/Components, which are + // not directly affected by this, but need to consume. + rescaleConstsCallbackDelegate?.Invoke(deviceDpiOld, deviceDpiNew); + } + } + } + + /// + /// Resets the scaling (only in PerMonitorV2 scenarios). + /// Do only call from code which is quirked with PerMonitorV2 quirks for the ToolStrip. + /// + /// The new DPI passed by WmDpiChangedBeforeParent. + internal virtual void ResetScaling(int newDpi) { + iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi); + iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi); + insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, newDpi); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, newDpi); + scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, newDpi); + imageScalingSize = new Size(iconWidth, iconHeight); + } + + /// + /// Paints the I beam when items are being reordered + /// + internal void PaintInsertionMark(Graphics g) { + if (lastInsertionMarkRect != Rectangle.Empty) { + int widthOfBeam = insertionBeamWidth; + if (Orientation == Orientation.Horizontal) { + int start = lastInsertionMarkRect.X; + int verticalBeamStart = start + 2; + + // draw two vertical lines + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(verticalBeamStart, lastInsertionMarkRect.Y), new Point(verticalBeamStart, lastInsertionMarkRect.Bottom-1), // first vertical line + new Point(verticalBeamStart+1, lastInsertionMarkRect.Y), new Point(verticalBeamStart+1, lastInsertionMarkRect.Bottom-1), //second vertical line + }); + // then two top horizontal + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(start, lastInsertionMarkRect.Bottom-1), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Bottom-1), //bottom line + new Point(start+1, lastInsertionMarkRect.Bottom -2), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Bottom-2),//bottom second line + }); + // then two bottom horizontal + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(start, lastInsertionMarkRect.Y), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Y), //top line + new Point(start+1, lastInsertionMarkRect.Y+1), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Y+1)//top second line + }); + } + else { + + widthOfBeam = insertionBeamWidth; + int start = lastInsertionMarkRect.Y; + int horizontalBeamStart = start + 2; + + // draw two horizontal lines + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.X, horizontalBeamStart), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart), // first vertical line + new Point(lastInsertionMarkRect.X, horizontalBeamStart+1), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart+1), //second vertical line + }); + // then two left vertical + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.X, start), new Point(lastInsertionMarkRect.X, start + widthOfBeam-1), //left line + new Point(lastInsertionMarkRect.X+1, start+1), new Point(lastInsertionMarkRect.X+1, start + widthOfBeam-2), //second left line + }); + // then two right vertical + g.DrawLines(SystemPens.ControlText, + new Point[] { new Point(lastInsertionMarkRect.Right-1, start), new Point(lastInsertionMarkRect.Right-1, start + widthOfBeam-1), //right line + new Point(lastInsertionMarkRect.Right-2, start+1), new Point(lastInsertionMarkRect.Right-2, start + widthOfBeam-2), //second right line + }); + } + + } + } + + /// + /// Paints the I beam when items are being reordered + /// + internal void PaintInsertionMark(Rectangle insertionRect) { + if (lastInsertionMarkRect != insertionRect) { + ClearInsertionMark(); + lastInsertionMarkRect = insertionRect; + this.Invalidate(insertionRect); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public new Control GetChildAtPoint(Point point) { + return base.GetChildAtPoint(point); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public new Control GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue) { + return base.GetChildAtPoint(pt, skipValue); + } + + // GetNextControl for ToolStrip should always return null + // we do our own tabbing/etc - this allows us to pretend + // we dont have child controls. + internal override Control GetFirstChildControlInTabOrder(bool forward) { + return null; + } + /// + /// + /// Finds the ToolStripItem contained within a specified client coordinate point + /// If item not found - returns null + /// + public ToolStripItem GetItemAt(int x, int y) { + return GetItemAt(new Point(x,y)); + } + + + /// + /// + /// Finds the ToolStripItem contained within a specified client coordinate point + /// If item not found - returns null + /// + public ToolStripItem GetItemAt(Point point) { + + Rectangle comparisonRect = new Rectangle(point, onePixel); + Rectangle bounds; + + // Check the last item we had the mouse over + if (lastMouseActiveItem != null) { + bounds = lastMouseActiveItem.Bounds; + + if (bounds.IntersectsWith(comparisonRect) && lastMouseActiveItem.ParentInternal == this) { + return this.lastMouseActiveItem; + } + } + + + // Walk the ToolStripItem collection + for (int i = 0; i < this.DisplayedItems.Count; i++) { + if (DisplayedItems[i] == null || DisplayedItems[i].ParentInternal != this) { + continue; + } + + bounds = DisplayedItems[i].Bounds; + + // inflate the grip so it is easier to access + if (toolStripGrip != null && DisplayedItems[i] == toolStripGrip) { + bounds = LayoutUtils.InflateRect(bounds, GripMargin); + } + if (bounds.IntersectsWith(comparisonRect)) { + return this.DisplayedItems[i]; + } + } + + return null; + + } + + private void RestoreFocusInternal(bool wasInMenuMode) { + // VSWhidbey 503500 + // This is called from the RestoreFocusFilter. If the state of MenuMode has changed + // since we posted this message, we do not know enough information about whether + // we should exit menu mode. + if (wasInMenuMode == ToolStripManager.ModalMenuFilter.InMenuMode) { + RestoreFocusInternal(); + } + } + + /// RestoreFocus - returns focus to the control who activated us + /// See comment on SnapFocus + /// + internal void RestoreFocusInternal() { + ToolStripManager.ModalMenuFilter.MenuKeyToggle = false; + ClearAllSelections(); + lastMouseDownedItem = null; + + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Someone has called RestoreFocus, exiting MenuMode."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + + if (!IsDropDown) { + // reset menu auto expansion. + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Setting menu auto expand to false"); + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] uninstalling RestoreFocusFilter"); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter); + + MenuAutoExpand = false; + + if (!DesignMode && !TabStop && (Focused || ContainsFocus)) { + RestoreFocus(); + } + + } + + // this matches the case where you click on a toolstrip control host + // then tab off of it, then hit ESC. ESC would "restore focus" and + // we should cancel keyboard activation if this method has cancelled focus. + if (KeyboardActive && !Focused && !ContainsFocus) { + KeyboardActive = false; + } + + } + + // override if you want to control (when TabStop = false) where the focus returns to + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void RestoreFocus() { + + bool focusSuccess = false; + + if ((hwndThatLostFocus != IntPtr.Zero) && (hwndThatLostFocus != this.Handle)) { + Control c = Control.FromHandleInternal(hwndThatLostFocus); + + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip RestoreFocus]: Will Restore Focus to: " + WindowsFormsUtils.GetControlInformation(hwndThatLostFocus)); + hwndThatLostFocus = IntPtr.Zero; + + if ((c != null) && c.Visible) { + focusSuccess = c.FocusInternal(); + } + } + hwndThatLostFocus = IntPtr.Zero; + + if (!focusSuccess) { + // clear out the focus, we have focus, we're not supposed to anymore. + UnsafeNativeMethods.SetFocus(NativeMethods.NullHandleRef); + } + } + + internal virtual void ResetRenderMode() { + RenderMode = ToolStripRenderMode.ManagerRenderMode; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetMinimumSize() { + CommonProperties.SetMinimumSize(this, new Size(-1,-1)); + } + + private void ResetGripMargin() { + GripMargin = Grip.DefaultMargin; + } + + internal void ResumeCaputureMode() { + SetToolStripState(STATE_SUSPENDCAPTURE, false); + } + + internal void SuspendCaputureMode() { + SetToolStripState(STATE_SUSPENDCAPTURE, true); + } + + internal virtual void ScrollInternal(int delta) { + SuspendLayout(); + foreach (ToolStripItem item in this.Items) { + Point newLocation = item.Bounds.Location; + + newLocation.Y -= delta; + + SetItemLocation(item, newLocation); + } + + ResumeLayout(false); + Invalidate(); + } + + /// + /// + /// Summary of SetItemLocation + /// + /// + protected internal void SetItemLocation(ToolStripItem item, Point location) { + if (item == null) { + throw new ArgumentNullException("item"); + } + if (item.Owner != this) { + throw new NotSupportedException(SR.GetString(SR.ToolStripCanOnlyPositionItsOwnItems)); + } + + item.SetBounds(new Rectangle(location, item.Size)); + } + /// + /// + /// This is needed so that people doing custom layout engines can change the "Parent" property of the item. + /// + protected static void SetItemParent(ToolStripItem item, ToolStrip parent) { + item.Parent = parent; + } + + protected override void SetVisibleCore(bool visible) { + if (visible) { + SnapMouseLocation(); + } + else { + // make sure we reset selection - this is critical for close/reopen dropdowns. + if (!Disposing && !IsDisposed) { + ClearAllSelections(); + } + + // when we're not visible, clear off old item HDC. + CachedItemHdcInfo lastInfo = cachedItemHdcInfo; + cachedItemHdcInfo = null; + + lastMouseDownedItem = null; + + if (lastInfo != null) { + lastInfo.Dispose(); + } + } + base.SetVisibleCore(visible); + } + + internal bool ShouldSelectItem() { + // we only want to select the item if the cursor position has + // actually moved from when the window became visible. + + // We ALWAYS get a WM_MOUSEMOVE when the window is shown, + // which could accidentally change selection. + if (mouseEnterWhenShown == InvalidMouseEnter) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] MouseEnter already reset."); + return true; + } + + Point mousePosition = WindowsFormsUtils.LastCursorPoint; + if (mouseEnterWhenShown != mousePosition) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse position has changed - call Select()."); + mouseEnterWhenShown = InvalidMouseEnter; + return true; + } + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse hasnt actually moved yet."); + + return false; + } + /// + /// + /// Summary of Select. + /// + /// + /// + protected override void Select(bool directed, bool forward) { + bool correctParentActiveControl = true; + if (ParentInternal != null) { + IContainerControl c = ParentInternal.GetContainerControlInternal(); + + if (c != null) { + c.ActiveControl = this; + correctParentActiveControl = (c.ActiveControl == this); + } + } + if (directed && correctParentActiveControl) { + SelectNextToolStripItem(null, forward); + } + } + + + /// + /// Summary of SelectNextToolStripItem. + /// + + + + internal ToolStripItem SelectNextToolStripItem(ToolStripItem start, bool forward) { + + ToolStripItem nextItem = GetNextItem(start, (forward) ? ArrowDirection.Right : ArrowDirection.Left, /*RTLAware=*/true); + ChangeSelection(nextItem); + return nextItem; + } + + // + // SECREVIEW: only call from places protected by a link demand for AllWindowsPermission. + // + internal void SetFocusUnsafe() { + if (TabStop) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Focusing toolstrip."); + FocusInternal(); + } + else { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Entering menu mode."); + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this, /*menuKeyPressed=*/false); + } + } + + private void SetupGrip() { + Rectangle gripRectangle = Rectangle.Empty; + Rectangle displayRect = DisplayRectangle; + + + if (Orientation == Orientation.Horizontal) { + // the display rectangle already knows about the padding and the grip rectangle width + // so place it relative to that. + gripRectangle.X = Math.Max(0, displayRect.X - Grip.GripThickness); + gripRectangle.Y = Math.Max(0,displayRect.Top - Grip.Margin.Top); + gripRectangle.Width = Grip.GripThickness; + gripRectangle.Height = displayRect.Height; + if (RightToLeft == RightToLeft.Yes) { + gripRectangle.X = ClientRectangle.Right - gripRectangle.Width - Grip.Margin.Horizontal; + gripRectangle.X += Grip.Margin.Left; + } + else { + gripRectangle.X -= Grip.Margin.Right; + } + } + else { + // vertical split stack mode + gripRectangle.X = displayRect.Left; + gripRectangle.Y = displayRect.Top - (Grip.GripThickness + Grip.Margin.Bottom); + gripRectangle.Width = displayRect.Width; + gripRectangle.Height = Grip.GripThickness; + } + + if (Grip.Bounds !=gripRectangle) { + Grip.SetBounds(gripRectangle); + } + + } + + /// + /// + /// + /// Sets the size of the auto-scroll margins. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + new public void SetAutoScrollMargin(int x, int y) { + base.SetAutoScrollMargin(x, y); + } + + internal void SetLargestItemSize(Size size) { + + if (toolStripOverflowButton != null && toolStripOverflowButton.Visible) { + size = LayoutUtils.UnionSizes(size, toolStripOverflowButton.Bounds.Size); + } + if (toolStripGrip != null && toolStripGrip.Visible) { + size = LayoutUtils.UnionSizes(size, toolStripGrip.Bounds.Size); + + } + largestDisplayedItemSize = size; + + } + + /// + /// + /// Afer we've performed a layout we need to reset the DisplayedItems and the OverflowItems collection. + /// OverflowItems are not supported in layouts other than ToolStripSplitStack + /// + protected virtual void SetDisplayedItems() { + this.DisplayedItems.Clear(); + this.OverflowItems.Clear(); + HasVisibleItems = false; + + Size biggestItemSize = Size.Empty; // used in determining OnPaint caching. + + + if (this.LayoutEngine is ToolStripSplitStackLayout) { + if (ToolStripGripStyle.Visible == GripStyle) { + this.DisplayedItems.Add(Grip); + SetupGrip(); + } + + // VSWhidbey 468104 + // for splitstack layout we re-arrange the items in the displayed items + // collection so that we can easily tab through them in natural order + Rectangle displayRect = this.DisplayRectangle; + int lastRightAlignedItem = -1; + + for (int pass=0; pass < 2; pass++) { + int j = 0; + + if (pass == 1 /*add right aligned items*/) { + j = lastRightAlignedItem; + } + + // add items to the DisplayedItem collection. + // in pass 0, we go forward adding the head (left) aligned items + // in pass 1, we go backward starting from the last (right) aligned item we found + + for (; j >= 0 && j < Items.Count; j = (pass == 0) ? j+1 : j-1){ + + ToolStripItem item = Items[j]; + ToolStripItemPlacement placement = item.Placement; + if (((IArrangedElement)item).ParticipatesInLayout) { + if (placement == ToolStripItemPlacement.Main) { + bool addItem = false; + if (pass == 0) { // Align.Left items + addItem = (item.Alignment == ToolStripItemAlignment.Left); + if (!addItem) { + // stash away this index so we dont have to iterate through the whole list again. + lastRightAlignedItem = j; + } + } + else if (pass == 1) { // Align.Right items + addItem = (item.Alignment == ToolStripItemAlignment.Right); + } + if (addItem) { + HasVisibleItems = true; + biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size); + this.DisplayedItems.Add(item); + } + } + else if (placement == ToolStripItemPlacement.Overflow && !(item is ToolStripSeparator)) { + if (item is ToolStripControlHost && this.OverflowButton.DropDown.IsRestrictedWindow) { + // VSWhidbey 436973: control hosts cannot be added to the overflow in the Internet + // just set the placement to None. + item.SetPlacement(ToolStripItemPlacement.None); + } + else { + this.OverflowItems.Add(item); + } + } + } + else { + item.SetPlacement(ToolStripItemPlacement.None); + } + } + + } + ToolStripOverflow overflow = GetOverflow(); + if (overflow != null) { + overflow.LayoutRequired = true; + } + if (OverflowItems.Count ==0) { + this.OverflowButton.Visible = false; + } + else if (CanOverflow){ + this.DisplayedItems.Add(OverflowButton); + } + + } + else { + // NOT a SplitStack layout. We dont change the order of the displayed items collection + // for custom keyboard handling override GetNextItem. + Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, "Setting Displayed Items: Current bounds: " + this.Bounds.ToString()); + Rectangle clientBounds = this.ClientRectangle; + + // for all other layout managers, we ignore overflow placement + bool allContained = true; + for (int j = 0; j < Items.Count; j++) { + ToolStripItem item = Items[j]; + if (((IArrangedElement)item).ParticipatesInLayout) + { + item.ParentInternal = this; + + bool boundsCheck = !IsDropDown; + bool intersects = item.Bounds.IntersectsWith(clientBounds); + + bool verticallyContained = clientBounds.Contains(clientBounds.X, item.Bounds.Top) && + clientBounds.Contains(clientBounds.X, item.Bounds.Bottom); + if (!verticallyContained) { + allContained = false; + } + + if (!boundsCheck || intersects) { + HasVisibleItems = true; + biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size); + this.DisplayedItems.Add(item); + item.SetPlacement(ToolStripItemPlacement.Main); + } + } + else { + item.SetPlacement(ToolStripItemPlacement.None); + } + + Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, item.ToString() + Items[j].Bounds); + } + + // For performance we calculate this here, since we're already iterating over the items. + // the only one who cares about it is ToolStripDropDownMenu (to see if it needs scroll buttons). + this.AllItemsVisible = allContained; + } + + SetLargestItemSize(biggestItemSize); + } + + + /// + /// Sets the current value of the specified bit in the control's state. + /// + internal void SetToolStripState(int flag, bool value) { + toolStripState = value? toolStripState | flag: toolStripState & ~flag; + } + + // remembers the current mouse location so we can determine + // later if we need to shift selection. + internal void SnapMouseLocation() { + mouseEnterWhenShown = WindowsFormsUtils.LastCursorPoint; + } + + + /// SnapFocus + /// When get focus to the toolstrip (and we're not participating in the tab order) + /// it's probably cause someone hit the ALT key. We need to remember who that was + /// so when we're done here we can RestoreFocus back to it. + /// + /// We're called from WM_SETFOCUS, and otherHwnd is the HWND losing focus. + /// + /// Required checks + /// - make sure it's not a dropdown + /// - make sure it's not a child control of this control. + /// - make sure the control is on this window + /// + private void SnapFocus(IntPtr otherHwnd) { +#if DEBUG + if (SnapFocusDebug.TraceVerbose) { + string stackTrace = new StackTrace().ToString(); + Regex regex = new Regex("FocusInternal"); + Debug.WriteLine(!regex.IsMatch(stackTrace), "who is setting focus to us?"); + } +#endif + // we need to know who sent us focus so we know who to send it back to later. + + if (!TabStop && !IsDropDown) { + bool snapFocus = false; + if (Focused && (otherHwnd != this.Handle)) { + // the case here is a label before a combo box calling FocusInternal in ProcessMnemonic. + // we'll filter out children later. + snapFocus = true; + } + else if (!ContainsFocus && !Focused) { + snapFocus =true; + } + + if (snapFocus) { + // remember the current mouse position so that we can check later if it actually moved + // otherwise we'd unexpectedly change selection to whatever the cursor was over at this moment. + SnapMouseLocation(); + + // start auto expanding for keyboard and mouse. + // MenuAutoExpand = true; + + HandleRef thisHandle = new HandleRef(this, this.Handle); + HandleRef otherHandle = new HandleRef(null, otherHwnd); + + // make sure the otherHandle is not a child of thisHandle + if ((thisHandle.Handle != otherHandle.Handle) && + !UnsafeNativeMethods.IsChild(thisHandle, otherHandle)) { + + // make sure the root window of the otherHwnd is the same as + // the root window of thisHwnd. + HandleRef thisHwndRoot = WindowsFormsUtils.GetRootHWnd(this); + HandleRef otherHwndRoot = WindowsFormsUtils.GetRootHWnd(otherHandle); + + if (thisHwndRoot.Handle == otherHwndRoot.Handle && (thisHwndRoot.Handle != IntPtr.Zero)) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip SnapFocus]: Caching for return focus:" + WindowsFormsUtils.GetControlInformation(otherHandle.Handle)); + // we know we're in the same window heirarchy. + hwndThatLostFocus = otherHandle.Handle; + } + } + } + } + + } + + // when we're control tabbing around we need to remember the original + // thing that lost focus. + internal void SnapFocusChange(ToolStrip otherToolStrip) { + otherToolStrip.hwndThatLostFocus = this.hwndThatLostFocus; + } + + private bool ShouldSerializeDefaultDropDownDirection() { + return (toolStripDropDownDirection != ToolStripDropDownDirection.Default); + } + + internal virtual bool ShouldSerializeLayoutStyle() { + return layoutStyle != ToolStripLayoutStyle.StackWithOverflow; + } + + internal override bool ShouldSerializeMinimumSize() { + Size invalidDefaultSize = new Size(-1,-1); + return (CommonProperties.GetMinimumSize(this, invalidDefaultSize) != invalidDefaultSize); + } + + private bool ShouldSerializeGripMargin() { + return GripMargin != DefaultGripMargin; + } + + internal virtual bool ShouldSerializeRenderMode() { + // We should NEVER serialize custom. + return (RenderMode != ToolStripRenderMode.ManagerRenderMode && RenderMode != ToolStripRenderMode.Custom); + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(base.ToString()); + sb.Append(", Name: "); + sb.Append(this.Name); + sb.Append(", Items: ").Append(this.Items.Count); + return sb.ToString(); + } + + internal void UpdateToolTip(ToolStripItem item) { + if (ShowItemToolTips) { + + if (item != currentlyActiveTooltipItem && ToolTip != null) { + + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + ToolTip.Hide(this); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + + if (AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip.Active = false; + } + + currentlyActiveTooltipItem = item; + + + if (currentlyActiveTooltipItem != null && !GetToolStripState(STATE_DRAGGING)) { + Cursor currentCursor = Cursor.CurrentInternal; + + if (currentCursor != null) { + if (AccessibilityImprovements.UseLegacyToolTipDisplay) { + ToolTip.Active = true; + } + + Point cursorLocation = Cursor.Position; + cursorLocation.Y += Cursor.Size.Height - currentCursor.HotSpot.Y; + + cursorLocation = WindowsFormsUtils.ConstrainToScreenBounds(new Rectangle(cursorLocation, onePixel)).Location; + + // SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone + IntSecurity.AllWindows.Assert(); + try { + ToolTip.Show(currentlyActiveTooltipItem.ToolTipText, + this, + PointToClient(cursorLocation), + ToolTip.AutoPopDelay); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + } + } + + } + + private void UpdateLayoutStyle(DockStyle newDock) { + if (!IsInToolStripPanel && layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { + using (new LayoutTransaction(this, this, PropertyNames.Orientation)) { + // + // We want the ToolStrip to size appropriately when the dock has switched. + // + if (newDock == DockStyle.Left || newDock == DockStyle.Right) { + UpdateOrientation(Orientation.Vertical); + } + else { + UpdateOrientation(Orientation.Horizontal); + } + } + + OnLayoutStyleChanged(EventArgs.Empty); + + if (this.ParentInternal != null) { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Orientation); + } + } + } + + private void UpdateLayoutStyle(Orientation newRaftingRowOrientation) { + if (layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { + using (new LayoutTransaction(this, this, PropertyNames.Orientation)) { + + // + // We want the ToolStrip to size appropriately when the rafting container orientation has switched. + // + /* if (newRaftingRowOrientation != orientation) { + int oldHeight = this.Height; + this.Height = this.Width; + this.Width = oldHeight; + }*/ + + UpdateOrientation(newRaftingRowOrientation); + if (LayoutEngine is ToolStripSplitStackLayout && layoutStyle == ToolStripLayoutStyle.StackWithOverflow) { + OnLayoutStyleChanged(EventArgs.Empty); + } + + } + } + else { + // update the orientation but dont force a layout. + UpdateOrientation(newRaftingRowOrientation); + } + + } + + + private void UpdateOrientation(Orientation newOrientation) { + if (newOrientation != orientation) { + // snap our last dimensions before switching over. + // use specifed bounds so that if something is docked or anchored we dont take the extra stretching + // effects into account. + Size size = CommonProperties.GetSpecifiedBounds(this).Size; + orientation = newOrientation; + // since the Grip affects the DisplayRectangle, we need to re-adjust the size + SetupGrip(); + } + } + + /// + /// + /// Summary of WndProc. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (m.Msg == NativeMethods.WM_SETFOCUS) { + SnapFocus(m.WParam); + } + if (m.Msg == NativeMethods.WM_MOUSEACTIVATE) { + // we want to prevent taking focus if someone clicks on the toolstrip dropdown + // itself. the mouse message will still go through, but focus wont be taken. + // if someone clicks on a child control (combobox, textbox, etc) focus will + // be taken - but we'll handle that in WM_NCACTIVATE handler. + Point pt = PointToClient(WindowsFormsUtils.LastCursorPoint); + IntPtr hwndClicked = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, Handle), pt.X, pt.Y,(int)(GetChildAtPointSkip.Invisible | GetChildAtPointSkip.Disabled | GetChildAtPointSkip.Transparent)); + // if we click on the toolstrip itself, eat the activation. + // if we click on a child control, allow the toolstrip to activate. + if (hwndClicked == this.Handle) { + lastMouseDownedItem = null; + m.Result = (IntPtr)NativeMethods.MA_NOACTIVATE; + + if (!IsDropDown && !IsInDesignMode) { + + // VSWhidbey 473357: if our root HWND is not the active hwnd, + // eat the mouse message and bring the form to the front. + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this); + if (rootHwnd.Handle != IntPtr.Zero) { + + // snap the active window and compare to our root window. + IntPtr hwndActive = UnsafeNativeMethods.GetActiveWindow(); + if (hwndActive != rootHwnd.Handle) { + // Activate the window, and discard the mouse message. + // this appears to be the same behavior as office. + m.Result = (IntPtr)NativeMethods.MA_ACTIVATEANDEAT; + } + } + } + return; + } + else { + // we're setting focus to a child control - remember who gave it to us + // so we can restore it on ESC. + SnapFocus(UnsafeNativeMethods.GetFocus()); + if (!IsDropDown && !TabStop) { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "Installing restoreFocusFilter"); + // PERF, SECREVIEW: dont call Application.AddMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().AddMessageFilter(RestoreFocusFilter); + } + } + } + + + base.WndProc(ref m); + + if (m.Msg == NativeMethods.WM_NCDESTROY) { + // Destroy the owner window, if we created one. We + // cannot do this in OnHandleDestroyed, because at + // that point our handle is not actually destroyed so + // destroying our parent actually causes a recursive + // WM_DESTROY. + if (dropDownOwnerWindow != null) { + dropDownOwnerWindow.DestroyHandle(); + } + } + } + + // Overriden to return Items instead of Controls. + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { return Items; } + } + + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + } + + /// + /// + bool IArrangedElement.ParticipatesInLayout { + get { return GetState(STATE_VISIBLE);} + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripAccessibleObject(this); + } + + /// + protected override Control.ControlCollection CreateControlsInstance() { + return new WindowsFormsUtils.ReadOnlyControlCollection(this, /* isReadOnly = */ !DesignMode); + } + + + internal void OnItemAddedInternal(ToolStripItem item) { + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + if (this.ShowItemToolTips) { + KeyboardToolTipStateMachine.Instance.Hook(item, this.ToolTip); + } + } + } + + internal void OnItemRemovedInternal(ToolStripItem item) { + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Unhook(item, this.ToolTip); + } + } + + internal override bool AllowsChildrenToShowToolTips() { + return base.AllowsChildrenToShowToolTips() && this.ShowItemToolTips; + } + + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripAccessibleObject : ControlAccessibleObject { + + private ToolStrip owner; + + /// + public ToolStripAccessibleObject(ToolStrip owner) : base(owner) { + this.owner = owner; + } + + /// + /// + /// Return the child object at the given screen coordinates. + /// + public override AccessibleObject HitTest(int x, int y) { + + Point clientHit = owner.PointToClient(new Point(x,y)); + ToolStripItem item = owner.GetItemAt(clientHit); + return ((item != null) && (item.AccessibilityObject != null)) ? + item.AccessibilityObject : + base.HitTest(x,y); + } + + + /// + /// + /// When overridden in a derived class, gets the accessible child corresponding to the specified + /// index. + /// + // + public override AccessibleObject GetChild(int index) { + if ((owner == null) || (owner.Items == null)) + return null; + + if (index == 0 && owner.Grip.Visible) { + return owner.Grip.AccessibilityObject; + } + else if (owner.Grip.Visible && index > 0) { + index--; + } + + if (index < owner.Items.Count) { + ToolStripItem item = null; + int myIndex = 0; + + // First we walk through the head aligned items. + for (int i = 0; i < owner.Items.Count; ++i) + { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) { + if (myIndex == index) { + item = owner.Items[i]; + break; + } + myIndex++; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + if (item == null) { + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) { + if (myIndex == index) { + item = owner.Items[i]; + break; + } + myIndex++; + } + } + } + + if (item == null) { + Debug.Fail("No item matched the index??"); + return null; + } + + if (item.Placement == ToolStripItemPlacement.Overflow) { + return new ToolStripAccessibleObjectWrapperForItemsOnOverflow(item); + } + return item.AccessibilityObject; + } + + if (owner.CanOverflow && owner.OverflowButton.Visible && index == owner.Items.Count) { + return owner.OverflowButton.AccessibilityObject; + } + return null; + } + + /// + /// + /// When overridden in a derived class, gets the number of children + /// belonging to an accessible object. + /// + public override int GetChildCount() { + if ((owner == null) || (owner.Items == null)) + return -1; + + int count = 0; + for (int i = 0; i < owner.Items.Count; i++) { + if (owner.Items[i].Available) { + count++; + } + } + if (owner.Grip.Visible) { + count++; + } + if (owner.CanOverflow && owner.OverflowButton.Visible) { + count++; + } + return count; + } + + internal AccessibleObject GetChildFragment(int fragmentIndex, bool getOverflowItem = false) { + + var items = getOverflowItem ? owner.OverflowItems : owner.DisplayedItems; + int childFragmentCount = items.Count; + + if (!getOverflowItem && owner.CanOverflow && owner.OverflowButton.Visible && fragmentIndex == childFragmentCount - 1) { + return owner.OverflowButton.AccessibilityObject; + } + + for (int index = 0; index < childFragmentCount; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Left && fragmentIndex == index) { + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + } + + for (int index = 0; index < childFragmentCount; index++) { + var item = owner.Items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Right && fragmentIndex == index) { + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + } + + return null; + } + + internal int GetChildOverflowFragmentCount() { + if (owner == null || owner.OverflowItems == null) { + return -1; + } + + return owner.OverflowItems.Count; + } + + internal int GetChildFragmentCount() { + if (owner == null || owner.DisplayedItems == null) { + return -1; + } + + return owner.DisplayedItems.Count; + } + + internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if (owner == null || owner.Items == null) { + return -1; + } + + if (child.Owner == owner.Grip) { + return 0; + } + + ToolStripItemCollection items; + var placement = child.Owner.Placement; + + if (owner is ToolStripOverflow) { + // Overflow items in ToolStripOverflow host are in DisplayedItems collection. + items = owner.DisplayedItems; + } + else { + if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) { + return GetChildFragmentCount() - 1; + } + + // Items can be either in DisplayedItems or in OverflowItems (if overflow) + items = (placement == ToolStripItemPlacement.Main) ? owner.DisplayedItems : owner.OverflowItems; + } + + // First we walk through the head aligned items. + for (int index = 0; index < items.Count; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Left && child.Owner == items[index]) { + return index; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + for (int index = 0; index < items.Count; index++) { + var item = items[index]; + if (item.Available && item.Alignment == ToolStripItemAlignment.Right && child.Owner == items[index]) { + return index; + } + } + + return -1; + } + + internal int GetChildIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if ((owner == null) || (owner.Items == null)) { + return -1; + } + + int index = 0; + if (owner.Grip.Visible) { + if (child.Owner == owner.Grip) { + return 0; + } + index = 1; + } + + if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) { + return owner.Items.Count + index; + } + + // First we walk through the head aligned items. + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) { + if (child.Owner == owner.Items[i]) { + return index; + } + index++; + } + } + + // If we didn't find it, then we walk through the tail aligned items. + for (int i = 0; i < owner.Items.Count; ++i) { + if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) { + if (child.Owner == owner.Items[i]) { + return index; + } + index++; + } + } + + return -1; + } + + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.ToolBar; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (AccessibilityImprovements.Level3) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildFragmentCount(); + if (childCount > 0) { + return this.GetChildFragment(0); + } + break; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildFragmentCount(); + if (childCount > 0) { + return this.GetChildFragment(childCount - 1); + } + break; + } + } + + return base.FragmentNavigate(direction); + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ToolBarControlTypeId; + } + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsIAccessibleExSupported() + { + return AccessibilityImprovements.Level3 && !owner.IsInDesignMode && !owner.IsTopInDesignMode; + } + + } + + private class ToolStripAccessibleObjectWrapperForItemsOnOverflow : ToolStripItem.ToolStripItemAccessibleObject { + public ToolStripAccessibleObjectWrapperForItemsOnOverflow(ToolStripItem item) + : base(item) { + } + public override AccessibleStates State { + get { + AccessibleStates state = base.State; + state |= AccessibleStates.Offscreen; + state |= AccessibleStates.Invisible; + return state; + } + } + } + + // When we click somewhere outside of the toolstrip it should be as if we hit esc. + + internal class RestoreFocusMessageFilter : IMessageFilter { + private ToolStrip ownerToolStrip; + + public RestoreFocusMessageFilter(ToolStrip ownerToolStrip) { + this.ownerToolStrip = ownerToolStrip; + } + + public bool PreFilterMessage(ref Message m) { + + if (ownerToolStrip.Disposing || ownerToolStrip.IsDisposed || ownerToolStrip.IsDropDown) { + return false; + } + // if the app has changed activation, restore focus + + switch (m.Msg) { + + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + if (ownerToolStrip.ContainsFocus) { + // if we've clicked on something that's not a child of the toolstrip and we + // currently have focus, restore it. + if (!UnsafeNativeMethods.IsChild(new HandleRef(this, ownerToolStrip.Handle), new HandleRef(this,m.HWnd))) { + HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(ownerToolStrip); + if (rootHwnd.Handle == m.HWnd || UnsafeNativeMethods.IsChild(rootHwnd, new HandleRef(this,m.HWnd))) { + // Only RestoreFocus if the hwnd is a child of the root window and isnt on the toolstrip. + RestoreFocusInternal(); + } + } + } + return false; + + default: + return false; + } + } + private void RestoreFocusInternal() { + Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocusFilter] Detected a click, restoring focus."); + + + ownerToolStrip.BeginInvoke(new BooleanMethodInvoker(ownerToolStrip.RestoreFocusInternal), new object[]{ ToolStripManager.ModalMenuFilter.InMenuMode } ); + + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(this); + } + } + + internal override bool ShowsOwnKeyboardToolTip() { + bool hasVisibleSelectableItems = false; + int i = this.Items.Count; + while (i-- != 0 && !hasVisibleSelectableItems) { + ToolStripItem item = this.Items[i]; + if (item.CanKeyboardSelect && item.Visible) { + hasVisibleSelectableItems = true; + } + } + + return !hasVisibleSelectableItems; + } + } + + + + + + internal class CachedItemHdcInfo : IDisposable { + + internal CachedItemHdcInfo() { + } + + ~CachedItemHdcInfo() { + Dispose(); + } + + private HandleRef cachedItemHDC = NativeMethods.NullHandleRef; + private Size cachedHDCSize = Size.Empty; + private HandleRef cachedItemBitmap = NativeMethods.NullHandleRef; + // this DC is cached and should only be deleted on Dispose or when the size changes. + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public HandleRef GetCachedItemDC(HandleRef toolStripHDC, Size bitmapSize) { + + if ((cachedHDCSize.Width < bitmapSize.Width) + || (cachedHDCSize.Height < bitmapSize.Height)) { + + if (cachedItemHDC.Handle == IntPtr.Zero) { + // create a new DC - we dont have one yet. + IntPtr compatibleHDC = UnsafeNativeMethods.CreateCompatibleDC(toolStripHDC); + cachedItemHDC = new HandleRef(this, compatibleHDC); + } + + // create compatible bitmap with the correct size. + cachedItemBitmap = new HandleRef(this, SafeNativeMethods.CreateCompatibleBitmap(toolStripHDC, bitmapSize.Width, bitmapSize.Height)); + IntPtr oldBitmap = SafeNativeMethods.SelectObject(cachedItemHDC,cachedItemBitmap); + + // delete the old bitmap + if (oldBitmap != IntPtr.Zero) { + // ExternalDelete to prevent Handle underflow + SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, oldBitmap)); + oldBitmap = IntPtr.Zero; + } + + + // remember what size we created. + cachedHDCSize = bitmapSize; + + } + return cachedItemHDC; + } + + + private void DeleteCachedItemHDC() { + + if (cachedItemHDC.Handle != IntPtr.Zero) { + // delete the bitmap + if (cachedItemBitmap.Handle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(cachedItemBitmap); + cachedItemBitmap = NativeMethods.NullHandleRef; + } + // delete the DC itself. + UnsafeNativeMethods.DeleteCompatibleDC(cachedItemHDC); + } + + cachedItemHDC = NativeMethods.NullHandleRef; + cachedItemBitmap = NativeMethods.NullHandleRef; + cachedHDCSize = Size.Empty; + } + + public void Dispose() { + DeleteCachedItemHDC(); + GC.SuppressFinalize(this); + } + + } + + + + internal class MouseHoverTimer : IDisposable { + + private System.Windows.Forms.Timer mouseHoverTimer = new System.Windows.Forms.Timer(); + private const int SPI_GETMOUSEHOVERTIME_WIN9X = 400; // in Win9x this is not supported so lets use the default from a more modern OS. + + // consider - weak reference? + private ToolStripItem currentItem = null; + + public MouseHoverTimer() { + int interval = SystemInformation.MouseHoverTime; + if (interval == 0) { + interval = SPI_GETMOUSEHOVERTIME_WIN9X; + } + + mouseHoverTimer.Interval = interval; + mouseHoverTimer.Tick += new EventHandler(OnTick); + } + + public void Start(ToolStripItem item) { + if (item != currentItem) { + Cancel(currentItem); + } + currentItem = item; + if (currentItem != null) { + mouseHoverTimer.Enabled = true; + } + } + + + public void Cancel() { + mouseHoverTimer.Enabled = false; + currentItem = null; + } + /// cancels if and only if this item was the one that + /// requested the timer + /// + public void Cancel(ToolStripItem item) { + if (item == currentItem) { + Cancel(); + } + } + + public void Dispose() { + if (mouseHoverTimer != null) { + Cancel(); + mouseHoverTimer.Dispose(); + mouseHoverTimer = null; + } + } + + + private void OnTick(object sender, EventArgs e) { + mouseHoverTimer.Enabled = false; + if (currentItem != null && !currentItem.IsDisposed) { + currentItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseHover); + } + } + + + } + + /// + /// This class supports the AllowItemReorder feature. + /// When reordering items ToolStrip and ToolStripItem drag/drop events + /// are routed here. + /// + internal sealed class ToolStripSplitStackDragDropHandler : IDropTarget, ISupportOleDropSource { + + private ToolStrip owner; + + public ToolStripSplitStackDragDropHandler(ToolStrip owner) { + + if (owner == null) { + // + throw new ArgumentNullException("owner"); + } + this.owner = owner; + } + + public void OnDragEnter(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragEnter: " + e.ToString()); + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + e.Effect = DragDropEffects.Move; + this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y))); + + } + } + + public void OnDragLeave(System.EventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragLeave: " + e.ToString()); + owner.ClearInsertionMark(); + } + + public void OnDragDrop(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragDrop: " + e.ToString()); + + + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + ToolStripItem item = (ToolStripItem)e.Data.GetData(typeof(ToolStripItem)); + OnDropItem(item, owner.PointToClient(new Point(e.X, e.Y))); + } + + } + public void OnDragOver(DragEventArgs e){ + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragOver: " + e.ToString()); + + if (e.Data.GetDataPresent(typeof(ToolStripItem))) { + if (this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y)))) { + e.Effect = DragDropEffects.Move; + } + else { + if (owner != null) { + owner.ClearInsertionMark(); + } + e.Effect = DragDropEffects.None; + } + } + + + } + + public void OnGiveFeedback(GiveFeedbackEventArgs e) { + } + + public void OnQueryContinueDrag(QueryContinueDragEventArgs e) { + } + + private void OnDropItem(ToolStripItem droppedItem, Point ownerClientAreaRelativeDropPoint) { + Point start = Point.Empty; + + int toolStripItemIndex = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint); + if (toolStripItemIndex >= 0) { + ToolStripItem item = owner.Items[toolStripItemIndex]; + if (item == droppedItem) { + owner.ClearInsertionMark(); + return; // optimization + } + + RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint); + droppedItem.Alignment = item.Alignment; + + // Protect against negative indicies + int insertIndex = Math.Max(0, toolStripItemIndex); + + if (relativeLocation == RelativeLocation.Above) { + insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex + 1; + } + else if (relativeLocation == RelativeLocation.Below) { + insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex-1; + } + else if (((item.Alignment == ToolStripItemAlignment.Left) && (relativeLocation == RelativeLocation.Left)) || + ((item.Alignment == ToolStripItemAlignment.Right) && (relativeLocation == RelativeLocation.Right))) { + + // the item alignment is Tail & dropped to right of the center of the item + // or the item alignment is Head & dropped to the left of the center of the item + + // Normally, insert the new item after the item, however in RTL insert before the item + insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.Yes) ? insertIndex + 1 : insertIndex); + } + else { + // the item alignment is Tail & dropped to left of the center of the item + // or the item alignment is Head & dropped to the right of the center of the item + + + // Normally, insert the new item before the item, however in RTL insert after the item + insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.No) ? insertIndex + 1 : insertIndex); + } + + // VSWhidbey 517774 + // If the control is moving from a lower to higher index, you actually want to set it one less than its position. + // This is because it is being removed from its original position, which lowers the index of every control before + // its new drop point by 1. + if (owner.Items.IndexOf(droppedItem) < insertIndex) { + insertIndex--; + } + + owner.Items.MoveItem(Math.Max(0,insertIndex), droppedItem); + owner.ClearInsertionMark(); + + } + else if (toolStripItemIndex == -1 && owner.Items.Count == 0) { + owner.Items.Add(droppedItem); + owner.ClearInsertionMark(); + } + } + + + + private bool ShowItemDropPoint(Point ownerClientAreaRelativeDropPoint) { + + int i = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint); + if (i >= 0) { + ToolStripItem item = owner.Items[i]; + RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint); + + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Drop relative loc " + relativeLocation); + Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Index " + i); + + Rectangle insertionRect = Rectangle.Empty; + switch (relativeLocation) { + case RelativeLocation.Above: + insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Top, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth); + break; + case RelativeLocation.Below: + insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Bottom, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth); + break; + case RelativeLocation.Right: + insertionRect = new Rectangle(item.Bounds.Right, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height- (owner.Margin.Vertical)-1); + break; + case RelativeLocation.Left: + insertionRect = new Rectangle(item.Bounds.Left, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height - (owner.Margin.Vertical) -1); + break; + } + + owner.PaintInsertionMark(insertionRect); + return true; + } + else if (owner.Items.Count == 0) { + Rectangle insertionRect = owner.DisplayRectangle; + insertionRect.Width = ToolStrip.insertionBeamWidth; + owner.PaintInsertionMark(insertionRect); + return true; + } + return false; + } + + + private int GetItemInsertionIndex(Point ownerClientAreaRelativeDropPoint) { + for(int i = 0; i< owner.DisplayedItems.Count; i++) { + Rectangle bounds = owner.DisplayedItems[i].Bounds; + bounds.Inflate(owner.DisplayedItems[i].Margin.Size); + if (bounds.Contains(ownerClientAreaRelativeDropPoint)) { + Debug.WriteLineIf(ToolStrip.DropTargetDebug.TraceVerbose, "MATCH " + owner.DisplayedItems[i].Text + " Bounds: " + owner.DisplayedItems[i].Bounds.ToString()); + + // consider what to do about items not in the display + return owner.Items.IndexOf(owner.DisplayedItems[i]); + } + } + + if (owner.DisplayedItems.Count > 0) { + for (int i = 0; i < owner.DisplayedItems.Count; i++) { + if (owner.DisplayedItems[i].Alignment == ToolStripItemAlignment.Right) { + if (i > 0) { + return owner.Items.IndexOf(owner.DisplayedItems[i - 1]); + } + return owner.Items.IndexOf(owner.DisplayedItems[i]); + } + } + return owner.Items.IndexOf(owner.DisplayedItems[owner.DisplayedItems.Count - 1]); + } + return -1; + } + + private enum RelativeLocation { + Above, + Below, + Right, + Left + } + + private RelativeLocation ComparePositions(Rectangle orig, Point check) { + + if (owner.Orientation == Orientation.Horizontal) { + int widthUnit = orig.Width / 2; + RelativeLocation relativeLocation = RelativeLocation.Left; + + // we can return here if we are checking abovebelowleftright, because + // the left right calculation is more picky than the above/below calculation + // and the above below calculation will just override this one. + if ((orig.Left + widthUnit) >= check.X) { + relativeLocation = RelativeLocation.Left; + return relativeLocation; + } + else if ((orig.Right - widthUnit) <= check.X) { + relativeLocation = RelativeLocation.Right; + return relativeLocation; + } + } + + if (owner.Orientation == Orientation.Vertical) { + int heightUnit = orig.Height/ 2; + RelativeLocation relativeLocation = (check.Y <= (orig.Top + heightUnit)) ? + RelativeLocation.Above + : RelativeLocation.Below; + + return relativeLocation; + } + + Debug.Fail("Could not calculate the relative position for AllowItemReorder"); + return RelativeLocation.Left; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventArgs.cs new file mode 100644 index 000000000..2c463938d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventArgs.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + /// + public class ToolStripArrowRenderEventArgs : EventArgs { + + private Graphics graphics = null; + private Rectangle arrowRect = Rectangle.Empty; + private Color arrowColor = Color.Empty; + private Color defaultArrowColor = Color.Empty; + private ArrowDirection arrowDirection = ArrowDirection.Down; + private ToolStripItem item = null; + private bool arrowColorChanged = false; + + /// + public ToolStripArrowRenderEventArgs(Graphics g, ToolStripItem toolStripItem, Rectangle arrowRectangle, Color arrowColor, ArrowDirection arrowDirection) { + this.item = toolStripItem; + this.graphics = g; + this.arrowRect = arrowRectangle; + this.defaultArrowColor = arrowColor; + this.arrowDirection = arrowDirection; + } + + + /// + public Rectangle ArrowRectangle { + get { + return arrowRect; + } + set { + arrowRect = value; + } + } + + /// + public Color ArrowColor { + get { + if (arrowColorChanged) { + return arrowColor; + } + return DefaultArrowColor; + } + set { + arrowColor = value; + arrowColorChanged = true; + } + } + + internal Color DefaultArrowColor { + get { + return defaultArrowColor; + } + set { + defaultArrowColor = value; + } + } + + /// + public ArrowDirection Direction { + get { + return arrowDirection; + } + set { + arrowDirection = value; + } + } + /// + public Graphics Graphics { + get { + return graphics; + } + } + + + /// + public ToolStripItem Item { + get { + return item; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventHandler.cs new file mode 100644 index 000000000..ada9aac00 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripArrowRenderEventHandler.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + + /// + public delegate void ToolStripArrowRenderEventHandler(object sender, ToolStripArrowRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripButton.cs b/WindowsForms/Managed/System/WinForms/ToolStripButton.cs new file mode 100644 index 000000000..b66f3c5dc --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripButton.cs @@ -0,0 +1,329 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Drawing.Imaging; + using System.ComponentModel; + using System.Windows.Forms.Design; + + /// + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip)] + public class ToolStripButton : ToolStripItem { + + private CheckState checkState = CheckState.Unchecked; + private bool checkOnClick = false; + private const int STANDARD_BUTTON_WIDTH = 23; + private int standardButtonWidth = STANDARD_BUTTON_WIDTH; + + private static readonly object EventCheckedChanged = new object(); + private static readonly object EventCheckStateChanged = new object(); + + /// + /// + /// Summary of ToolStripButton. + /// + public ToolStripButton() { + Initialize(); + } + public ToolStripButton(string text):base(text,null,null) { + Initialize(); + } + public ToolStripButton(Image image):base(null,image,null) { + Initialize(); + } + public ToolStripButton(string text, Image image):base(text,image,null) { + Initialize(); + } + public ToolStripButton(string text, Image image, EventHandler onClick):base(text,image,onClick) { + Initialize(); + } + public ToolStripButton(string text, Image image, EventHandler onClick, string name):base(text,image,onClick,name) { + Initialize(); + } + + + [DefaultValue(true)] + public new bool AutoToolTip { + get { + return base.AutoToolTip; + } + set { + base.AutoToolTip = value; + } + } + + + /// + /// + /// Summary of CanSelect. + /// + public override bool CanSelect { + get { + return true; + } + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripButtonCheckOnClickDescr) + ] + public bool CheckOnClick { + get { + return checkOnClick; + } + set { + checkOnClick = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the item is checked. + /// + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripButtonCheckedDescr) + ] + public bool Checked { + get { + return checkState != CheckState.Unchecked; + } + + set { + if (value != Checked) { + CheckState = value ? CheckState.Checked : CheckState.Unchecked; + InvokePaint(); + + } + } + } + + /// + /// + /// Gets + /// or sets a value indicating whether the check box is checked. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(CheckState.Unchecked), + SRDescription(SR.CheckBoxCheckStateDescr) + ] + public CheckState CheckState { + get { + return checkState; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)CheckState.Unchecked, (int)CheckState.Indeterminate)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(CheckState)); + } + + if (value != checkState) { + checkState = value; + Invalidate(); + OnCheckedChanged(EventArgs.Empty); + OnCheckStateChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckedChangedDescr)] + public event EventHandler CheckedChanged { + add { + Events.AddHandler(EventCheckedChanged, value); + } + remove { + Events.RemoveHandler(EventCheckedChanged, value); + } + } + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckStateChangedDescr)] + public event EventHandler CheckStateChanged { + add { + Events.AddHandler(EventCheckStateChanged, value); + } + remove { + Events.RemoveHandler(EventCheckStateChanged, value); + } + } + + + protected override bool DefaultAutoToolTip { + get { + return true; + } + } + + internal override int DeviceDpi { + get { + return base.DeviceDpi; + } + + // This gets called via ToolStripItem.RescaleConstantsForDpi. + // It's practically calling Initialize on DpiChanging with the new Dpi value. + // ToolStripItem.RescaleConstantsForDpi is already behind quirks. + set { + if (base.DeviceDpi != value) { + base.DeviceDpi = value; + standardButtonWidth = DpiHelper.LogicalToDeviceUnits(STANDARD_BUTTON_WIDTH, DeviceDpi); + } + } + } + + /// + /// + /// constructs the new instance of the accessibility object for this ToolStripItem. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripButtonAccessibleObject(this); + } + + public override Size GetPreferredSize(Size constrainingSize) { + Size prefSize = base.GetPreferredSize(constrainingSize); + prefSize.Width = Math.Max(prefSize.Width, standardButtonWidth); + return prefSize; + } + + /// + /// Called by all constructors of ToolStripButton. + /// + private void Initialize() { + SupportsSpaceKey = true; + if (DpiHelper.EnableToolStripHighDpiImprovements) { + standardButtonWidth = DpiHelper.LogicalToDeviceUnitsX(STANDARD_BUTTON_WIDTH); + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnCheckedChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventCheckedChanged]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnCheckStateChanged(EventArgs e) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + EventHandler handler = (EventHandler)Events[EventCheckStateChanged]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + if (this.Owner != null) { + ToolStripRenderer renderer = this.Renderer; + + renderer.DrawButtonBackground(new ToolStripItemRenderEventArgs(e.Graphics, this)); + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { + ToolStripItemImageRenderEventArgs rea = new ToolStripItemImageRenderEventArgs(e.Graphics, this, InternalLayout.ImageRectangle); + rea.ShiftOnPress = true; + renderer.DrawItemImage(rea); + } + + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(e.Graphics, this, this.Text, InternalLayout.TextRectangle, this.ForeColor, this.Font, InternalLayout.TextFormat)); + } + } + } + + /// + protected override void OnClick(EventArgs e) { + if (checkOnClick) { + this.Checked = !this.Checked; + } + base.OnClick(e); + } + + /// + /// An implementation of AccessibleChild for use with ToolStripItems + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripButtonAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripButton ownerItem = null; + + public ToolStripButtonAccessibleObject(ToolStripButton ownerItem): base(ownerItem) { + this.ownerItem = ownerItem; + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ButtonControlTypeId; + } + } + + return base.GetPropertyValue(propertyID); + } + + public override AccessibleRole Role { + get { + if (ownerItem.CheckOnClick && AccessibilityImprovements.Level1) { + return AccessibleRole.CheckButton; + } + else { + return base.Role; + } + } + } + + public override AccessibleStates State { + get { + if (ownerItem.Enabled && ownerItem.Checked) { + return base.State | AccessibleStates.Checked; + } + + if (AccessibilityImprovements.Level1) { + // Disabled ToolStripButton, that is selected, must have focus state so that Narrator can announce it + if (!ownerItem.Enabled && ownerItem.Selected) { + return base.State | AccessibleStates.Focused; + } + } + + return base.State; + } + } + + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripComboBox.cs b/WindowsForms/Managed/System/WinForms/ToolStripComboBox.cs new file mode 100644 index 000000000..140a86e50 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripComboBox.cs @@ -0,0 +1,830 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing.Design; + using System.Collections.Specialized; + using System.Drawing.Drawing2D; + using System.Windows.Forms.Design; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices; + using System.Windows.Forms.Internal; + using System.Globalization; + + /// + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.MenuStrip | ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)] + [DefaultProperty("Items")] + public class ToolStripComboBox : ToolStripControlHost { + + + internal static readonly object EventDropDown = new object(); + internal static readonly object EventDropDownClosed = new object(); + internal static readonly object EventDropDownStyleChanged = new object(); + internal static readonly object EventSelectedIndexChanged = new object(); + internal static readonly object EventSelectionChangeCommitted = new object(); + internal static readonly object EventTextUpdate = new object(); + + private static readonly Padding dropDownPadding = new Padding(2); + private static readonly Padding padding = new Padding(1, 0, 1, 0); + + private Padding scaledDropDownPadding = dropDownPadding; + private Padding scaledPadding = padding; + + /// + public ToolStripComboBox() : base(CreateControlInstance()) { + ToolStripComboBoxControl combo = Control as ToolStripComboBoxControl; + combo.Owner = this; + + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledPadding = DpiHelper.LogicalToDeviceUnits(padding); + scaledDropDownPadding = DpiHelper.LogicalToDeviceUnits(dropDownPadding); + } + } + public ToolStripComboBox(string name) : this() { + this.Name = name; + } + + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ToolStripComboBox(Control c) : base(c) { + throw new NotSupportedException(SR.GetString(SR.ToolStripMustSupplyItsOwnComboBox)); + } + + /// + /// Constructs the new instance of the accessibility object for this ToolStripComboBox ToolStrip item. + /// + /// + /// The new instance of the accessibility object for this ToolStripComboBox ToolStrip item + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripComboBoxAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripComboBoxAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripComboBox ownerItem = null; + + public ToolStripComboBoxAccessibleObject(ToolStripComboBox ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + public override string DefaultAction { + get { + // Note: empty value is provided due to this Accessible object + // represents the control container but not the contained ComboBox + // control itself. + return string.Empty; + } + } + + public override void DoDefaultAction() { + // Do nothing. + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + + return AccessibleRole.ComboBox; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild || + direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + return this.ownerItem.ComboBox.AccessibilityObject; + } + + // Handle Parent and other directions in base ToolStripItem.FragmentNavigate() method. + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return this.ownerItem.RootToolStrip.AccessibilityObject; + } + } + } + + private static Control CreateControlInstance() { + ComboBox comboBox = new ToolStripComboBoxControl(); + comboBox.FlatStyle = FlatStyle.Popup; + comboBox.Font = ToolStripManager.DefaultFont; + return comboBox; + } + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxAutoCompleteCustomSourceDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public System.Windows.Forms.AutoCompleteStringCollection AutoCompleteCustomSource { + get { return ComboBox.AutoCompleteCustomSource; } + set { ComboBox.AutoCompleteCustomSource = value;} + } + + /// + [ + DefaultValue(AutoCompleteMode.None), + SRDescription(SR.ComboBoxAutoCompleteModeDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteMode AutoCompleteMode { + get { return ComboBox.AutoCompleteMode; } + set { ComboBox.AutoCompleteMode = value;} + } + + /// + [ + DefaultValue(AutoCompleteSource.None), + SRDescription(SR.ComboBoxAutoCompleteSourceDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteSource AutoCompleteSource { + get { return ComboBox.AutoCompleteSource; } + set { ComboBox.AutoCompleteSource = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ComboBox ComboBox { + get{ + return Control as ComboBox; + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(100,22); + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal override Padding DefaultMargin { + get { + if (IsOnDropDown) { + return scaledDropDownPadding; + } + else { + return scaledPadding; + } + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler DoubleClick { + add { + base.DoubleClick+=value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownDescr)] + public event EventHandler DropDown { + add { + Events.AddHandler(EventDropDown, value); + } + remove { + Events.RemoveHandler(EventDropDown, value); + } + } + + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnDropDownClosedDescr)] + public event EventHandler DropDownClosed { + add { + Events.AddHandler(EventDropDownClosed, value); + } + remove { + Events.RemoveHandler(EventDropDownClosed, value); + } + } + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxDropDownStyleChangedDescr)] + public event EventHandler DropDownStyleChanged { + add { + Events.AddHandler(EventDropDownStyleChanged, value); + } + remove { + Events.RemoveHandler(EventDropDownStyleChanged, value); + } + } + + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownHeightDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DefaultValue(106) + ] + public int DropDownHeight { + get { return ComboBox.DropDownHeight; } + set { ComboBox.DropDownHeight = value;} + + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ComboBoxStyle.DropDown), + SRDescription(SR.ComboBoxStyleDescr), + RefreshPropertiesAttribute(RefreshProperties.Repaint) + ] + public ComboBoxStyle DropDownStyle { + get { return ComboBox.DropDownStyle; } + set { ComboBox.DropDownStyle = value;} + } + + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ComboBoxDropDownWidthDescr) + ] + public int DropDownWidth { + get { return ComboBox.DropDownWidth; } + set { ComboBox.DropDownWidth = value;} + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxDroppedDownDescr) + ] + public bool DroppedDown { + get { return ComboBox.DroppedDown; } + set { ComboBox.DroppedDown = value;} + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(FlatStyle.Popup), + Localizable(true), + SRDescription(SR.ComboBoxFlatStyleDescr) + ] + public FlatStyle FlatStyle { + get { return ComboBox.FlatStyle; } + set { ComboBox.FlatStyle = value;} + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + Localizable(true), + SRDescription(SR.ComboBoxIntegralHeightDescr) + ] + public bool IntegralHeight { + get { return ComboBox.IntegralHeight; } + set { ComboBox.IntegralHeight = value;} + } + /// + /// + /// Collection of the items contained in this ComboBox. + /// + [ + SRCategory(SR.CatData), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.ComboBoxItemsDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public ComboBox.ObjectCollection Items { + get{ + return ComboBox.Items; + } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(8), + Localizable(true), + SRDescription(SR.ComboBoxMaxDropDownItemsDescr) + ] + public int MaxDropDownItems { + get { return ComboBox.MaxDropDownItems; } + set { ComboBox.MaxDropDownItems = value;} + } + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Localizable(true), + SRDescription(SR.ComboBoxMaxLengthDescr) + ] + public int MaxLength { + get { return ComboBox.MaxLength; } + set { ComboBox.MaxLength = value; } + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedIndexDescr) + ] + public int SelectedIndex { + get { return ComboBox.SelectedIndex; } + set { ComboBox.SelectedIndex = value;} + } + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] + public event EventHandler SelectedIndexChanged { + add { + Events.AddHandler(EventSelectedIndexChanged, value); + } + remove { + Events.RemoveHandler(EventSelectedIndexChanged, value); + } + } + /// + [ + Browsable(false), + Bindable(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedItemDescr) + ] + public object SelectedItem { + get { return ComboBox.SelectedItem; } + set { ComboBox.SelectedItem = value;} + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectedTextDescr) + ] + public string SelectedText { + get { return ComboBox.SelectedText; } + set { ComboBox.SelectedText = value;} + } + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionLengthDescr) + ] + public int SelectionLength { + get { return ComboBox.SelectionLength; } + set { ComboBox.SelectionLength = value;} + } + + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ComboBoxSelectionStartDescr) + ] + public int SelectionStart { + get { return ComboBox.SelectionStart; } + set { ComboBox.SelectionStart = value;} + } + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ComboBoxSortedDescr) + ] + public bool Sorted { + get { return ComboBox.Sorted; } + set {ComboBox.Sorted = value;} + } + + + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ComboBoxOnTextUpdateDescr)] + public event EventHandler TextUpdate { + add { + Events.AddHandler(EventTextUpdate, value); + } + remove { + Events.RemoveHandler(EventTextUpdate, value); + } + } + +#region WrappedMethods + + /// + public void BeginUpdate() { ComboBox.BeginUpdate(); } + /// + public void EndUpdate() { ComboBox.EndUpdate(); } + /// + public int FindString(string s) { return ComboBox.FindString(s); } + /// + public int FindString(string s, int startIndex) { return ComboBox.FindString(s, startIndex); } + /// + public int FindStringExact(string s) { return ComboBox.FindStringExact(s); } + /// + public int FindStringExact(string s, int startIndex) { return ComboBox.FindStringExact(s, startIndex); } + /// + public int GetItemHeight(int index) { return ComboBox.GetItemHeight(index); } + /// + public void Select(int start, int length) { ComboBox.Select(start, length); } + /// + public void SelectAll() { ComboBox.SelectAll(); } + +#endregion WrappedMethods + + /// + public override Size GetPreferredSize(Size constrainingSize) { + + // + Size preferredSize = base.GetPreferredSize(constrainingSize); + preferredSize.Width = Math.Max(preferredSize.Width, 75); + + return preferredSize; + } + private void HandleDropDown(object sender, System.EventArgs e) { + OnDropDown(e); + } + private void HandleDropDownClosed(object sender, System.EventArgs e) { + OnDropDownClosed(e); + } + + private void HandleDropDownStyleChanged(object sender, System.EventArgs e) { + OnDropDownStyleChanged(e); + } + private void HandleSelectedIndexChanged(object sender, System.EventArgs e) { + OnSelectedIndexChanged(e); + } + private void HandleSelectionChangeCommitted(object sender, System.EventArgs e) { + OnSelectionChangeCommitted(e); + } + private void HandleTextUpdate(object sender, System.EventArgs e) { + OnTextUpdate(e); + } + + /// + protected virtual void OnDropDown(EventArgs e) { + if (ParentInternal != null) { + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(ParentInternal.RestoreFocusFilter); + ToolStripManager.ModalMenuFilter.SuspendMenuMode(); + } + RaiseEvent(EventDropDown, e); + } + /// + protected virtual void OnDropDownClosed(EventArgs e) { + if (ParentInternal != null) { + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(ParentInternal.RestoreFocusFilter); + ToolStripManager.ModalMenuFilter.ResumeMenuMode(); + } + RaiseEvent(EventDropDownClosed, e); + } + /// + protected virtual void OnDropDownStyleChanged(EventArgs e) { + RaiseEvent(EventDropDownStyleChanged, e); + } + /// + protected virtual void OnSelectedIndexChanged(EventArgs e) { + RaiseEvent(EventSelectedIndexChanged, e); + } + /// + protected virtual void OnSelectionChangeCommitted(EventArgs e) { + RaiseEvent(EventSelectionChangeCommitted, e); + } + /// + protected virtual void OnTextUpdate(EventArgs e) { + RaiseEvent(EventTextUpdate, e); + } + + /// + protected override void OnSubscribeControlEvents(Control control) { + ComboBox comboBox = control as ComboBox; + if (comboBox != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + comboBox.DropDown += new EventHandler(HandleDropDown); + comboBox.DropDownClosed += new EventHandler(HandleDropDownClosed); + comboBox.DropDownStyleChanged += new EventHandler(HandleDropDownStyleChanged); + comboBox.SelectedIndexChanged += new EventHandler(HandleSelectedIndexChanged); + comboBox.SelectionChangeCommitted += new EventHandler(HandleSelectionChangeCommitted); + comboBox.TextUpdate += new EventHandler(HandleTextUpdate); + } + + base.OnSubscribeControlEvents(control); + } + + /// + protected override void OnUnsubscribeControlEvents(Control control) { + ComboBox comboBox = control as ComboBox; + if (comboBox != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + comboBox.DropDown -= new EventHandler(HandleDropDown); + comboBox.DropDownClosed -= new EventHandler(HandleDropDownClosed); + comboBox.DropDownStyleChanged -= new EventHandler(HandleDropDownStyleChanged); + comboBox.SelectedIndexChanged -= new EventHandler(HandleSelectedIndexChanged); + comboBox.SelectionChangeCommitted -= new EventHandler(HandleSelectionChangeCommitted); + comboBox.TextUpdate -= new EventHandler(HandleTextUpdate); + } + base.OnUnsubscribeControlEvents(control); + } + + private bool ShouldSerializeDropDownWidth() { + return ComboBox.ShouldSerializeDropDownWidth(); + } + + internal override bool ShouldSerializeFont() { + return !object.Equals(this.Font, ToolStripManager.DefaultFont); + } + + public override string ToString() { + return base.ToString() + ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); + } + + + internal class ToolStripComboBoxControl : ComboBox { + private ToolStripComboBox owner = null; + + public ToolStripComboBoxControl() { + this.FlatStyle = FlatStyle.Popup; + SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true); + } + + public ToolStripComboBox Owner { + get { return owner; } + set { owner = value; } + } + + private ProfessionalColorTable ColorTable { + get { + if (Owner != null) { + ToolStripProfessionalRenderer renderer = Owner.Renderer as ToolStripProfessionalRenderer; + if (renderer != null) { + return renderer.ColorTable; + } + } + return ProfessionalColors.ColorTable; + } + } + + /// + /// Constructs the new instance of the accessibility object for this ToolStripComboBoxControl. + /// + /// + /// The new instance of the accessibility object for this ToolStripComboBoxControl item + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripComboBoxControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + internal override FlatComboAdapter CreateFlatComboAdapterInstance() { + return new ToolStripComboBoxFlatComboAdapter(this); + } + + internal class ToolStripComboBoxFlatComboAdapter : FlatComboAdapter { + + public ToolStripComboBoxFlatComboAdapter(ComboBox comboBox) : base(comboBox, /*smallButton=*/true) { + } + + private static bool UseBaseAdapter(ComboBox comboBox) { + ToolStripComboBoxControl toolStripComboBox = comboBox as ToolStripComboBoxControl; + if (toolStripComboBox == null || !(toolStripComboBox.Owner.Renderer is ToolStripProfessionalRenderer)) { + Debug.Assert(toolStripComboBox != null, "Why are we here and not a toolstrip combo?"); + return true; + } + return false; + } + + private static ProfessionalColorTable GetColorTable(ToolStripComboBoxControl toolStripComboBoxControl) { + if (toolStripComboBoxControl != null) { + return toolStripComboBoxControl.ColorTable; + } + return ProfessionalColors.ColorTable; + } + + protected override Color GetOuterBorderColor(ComboBox comboBox) { + if (UseBaseAdapter(comboBox)) { + return base.GetOuterBorderColor(comboBox); + } + return (comboBox.Enabled) ? SystemColors.Window : GetColorTable(comboBox as ToolStripComboBoxControl).ComboBoxBorder; + } + + protected override Color GetPopupOuterBorderColor(ComboBox comboBox, bool focused) { + if (UseBaseAdapter(comboBox)) { + return base.GetPopupOuterBorderColor(comboBox, focused); + } + if (!comboBox.Enabled) { + return SystemColors.ControlDark; + } + return (focused) ? GetColorTable(comboBox as ToolStripComboBoxControl).ComboBoxBorder : SystemColors.Window; + } + + protected override void DrawFlatComboDropDown(ComboBox comboBox, Graphics g, Rectangle dropDownRect) { + if (UseBaseAdapter(comboBox)) { + base.DrawFlatComboDropDown(comboBox, g, dropDownRect); + return; + } + + + if (!comboBox.Enabled || !ToolStripManager.VisualStylesEnabled) { + g.FillRectangle(SystemBrushes.Control, dropDownRect); + } + else { + + ToolStripComboBoxControl toolStripComboBox = comboBox as ToolStripComboBoxControl; + ProfessionalColorTable colorTable = GetColorTable(toolStripComboBox); + + if (!comboBox.DroppedDown) { + bool focused = comboBox.ContainsFocus || comboBox.MouseIsOver; + if (focused) { + using (Brush b = new LinearGradientBrush(dropDownRect, colorTable.ComboBoxButtonSelectedGradientBegin, colorTable.ComboBoxButtonSelectedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, dropDownRect); + } + } + else if (toolStripComboBox.Owner.IsOnOverflow) { + using (Brush b = new SolidBrush(colorTable.ComboBoxButtonOnOverflow)) { + g.FillRectangle(b, dropDownRect); + } + } + else { + using (Brush b = new LinearGradientBrush(dropDownRect, colorTable.ComboBoxButtonGradientBegin, colorTable.ComboBoxButtonGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, dropDownRect); + } + } + } + else { + using (Brush b = new LinearGradientBrush(dropDownRect, colorTable.ComboBoxButtonPressedGradientBegin, colorTable.ComboBoxButtonPressedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, dropDownRect); + } + } + } + + Brush brush; + if (comboBox.Enabled) { + if (AccessibilityImprovements.Level2 && SystemInformation.HighContrast && (comboBox.ContainsFocus || comboBox.MouseIsOver) && ToolStripManager.VisualStylesEnabled) { + brush = SystemBrushes.HighlightText; + } + else { + brush = SystemBrushes.ControlText; + } + } + else { + brush = SystemBrushes.GrayText; + } + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + + // if the width is odd - favor pushing it over one pixel right. + middle.X += (dropDownRect.Width % 2); + g.FillPolygon(brush, new Point[] { + new Point(middle.X - FlatComboAdapter.Offset2Pixels, middle.Y - 1), + new Point(middle.X + FlatComboAdapter.Offset2Pixels + 1, middle.Y - 1), + new Point(middle.X, middle.Y + FlatComboAdapter.Offset2Pixels) + }); + + } + } + + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) { + if ((keyData & Keys.Down) == Keys.Down || (keyData & Keys.Up) == Keys.Up) { + return true; + } + } + return base.IsInputKey(keyData); + } + + protected override void OnDropDownClosed(EventArgs e) { + base.OnDropDownClosed(e); + Invalidate(); + Update(); + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + internal class ToolStripComboBoxControlAccessibleObject : ComboBoxUiaProvider { + + private ComboBox.ChildAccessibleObject childAccessibleObject; + + public ToolStripComboBoxControlAccessibleObject(ToolStripComboBoxControl toolStripComboBoxControl) : base(toolStripComboBoxControl) { + childAccessibleObject = new ChildAccessibleObject(toolStripComboBoxControl, toolStripComboBoxControl.Handle); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var toolStripComboBoxControl = Owner as ToolStripComboBoxControl; + if (toolStripComboBoxControl != null) { + return toolStripComboBoxControl.Owner.AccessibilityObject.FragmentNavigate(direction); + } + break; + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + var toolStripComboBoxControl = this.Owner as ToolStripComboBoxControl; + if (toolStripComboBoxControl != null) { + return toolStripComboBoxControl.Owner.Owner.AccessibilityObject; + } + + return base.FragmentRoot; + } + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ComboBoxControlTypeId; + case NativeMethods.UIA_IsOffscreenPropertyId: + return (this.State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) + { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId || + patternId == NativeMethods.UIA_ValuePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + } + + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripContainer.cs b/WindowsForms/Managed/System/WinForms/ToolStripContainer.cs new file mode 100644 index 000000000..6a7fa6997 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripContainer.cs @@ -0,0 +1,498 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/// this is the UBER container for ToolStripPanels. +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Windows.Forms; + using System.Collections; + using System.Globalization; + using System.Windows.Forms.Layout; + + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [Designer("System.Windows.Forms.Design.ToolStripContainerDesigner, " + AssemblyRef.SystemDesign)] + [SRDescription(SR.ToolStripContainerDesc)] + public class ToolStripContainer : ContainerControl { + + private ToolStripPanel topPanel; + private ToolStripPanel bottomPanel; + private ToolStripPanel leftPanel; + private ToolStripPanel rightPanel; + private ToolStripContentPanel contentPanel; + + + public ToolStripContainer() { + + SetStyle(ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); + + SuspendLayout(); + try { + // undone - smart demand creation + topPanel = new ToolStripPanel(this); + bottomPanel = new ToolStripPanel(this); + leftPanel = new ToolStripPanel(this); + rightPanel = new ToolStripPanel(this); + contentPanel = new ToolStripContentPanel(); + contentPanel.Dock = DockStyle.Fill; + topPanel.Dock = DockStyle.Top; + bottomPanel.Dock = DockStyle.Bottom; + rightPanel.Dock = DockStyle.Right; + leftPanel.Dock = DockStyle.Left; + + ToolStripContainerTypedControlCollection controlCollection = this.Controls as ToolStripContainerTypedControlCollection; + if (controlCollection != null) { + controlCollection.AddInternal(contentPanel); + controlCollection.AddInternal(leftPanel); + controlCollection.AddInternal(rightPanel); + controlCollection.AddInternal(topPanel); + controlCollection.AddInternal(bottomPanel); + } + // else consider throw new exception + + } + finally { + ResumeLayout(true); + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool AutoScroll { + get { return base.AutoScroll; } + set { base.AutoScroll = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMargin { + get { return base.AutoScrollMargin; } + set { base.AutoScrollMargin = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMinSize { + get { return base.AutoScrollMinSize; } + set { base.AutoScrollMinSize = value; } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Color BackColor { + get { return base.BackColor; } + set { base.BackColor = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler BackColorChanged { + add { + base.BackColorChanged += value; + } + remove { + base.BackColorChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Image BackgroundImage { + get { return base.BackgroundImage; } + set { base.BackgroundImage = value; } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override ImageLayout BackgroundImageLayout { + get { return base.BackgroundImageLayout; } + set { base.BackgroundImageLayout = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged += value; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerBottomToolStripPanelDescr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public ToolStripPanel BottomToolStripPanel { + get { + return bottomPanel; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerBottomToolStripPanelVisibleDescr), + DefaultValue(true) + ] + public bool BottomToolStripPanelVisible { + get { + return BottomToolStripPanel.Visible; + } + set { + BottomToolStripPanel.Visible = value; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerContentPanelDescr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public ToolStripContentPanel ContentPanel { + get { + return contentPanel; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new bool CausesValidation { + get { return base.CausesValidation; } + set { base.CausesValidation = value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new ContextMenuStrip ContextMenuStrip { + get { return base.ContextMenuStrip; } + set { base.ContextMenuStrip = value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ContextMenuStripChanged { + add { + base.ContextMenuStripChanged += value; + } + remove { + base.ContextMenuStripChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override Cursor Cursor { + get { return base.Cursor; } + set { base.Cursor = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new event EventHandler CursorChanged { + add { + base.CursorChanged += value; + } + remove { + base.CursorChanged -= value; + } + } + + + protected override Size DefaultSize { + get { + return new Size(150, 175); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new Color ForeColor { + get { return base.ForeColor; } + set { base.ForeColor = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerLeftToolStripPanelDescr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public ToolStripPanel LeftToolStripPanel { + get { + return leftPanel; + } + } + + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerLeftToolStripPanelVisibleDescr), + DefaultValue(true) + ] + public bool LeftToolStripPanelVisible { + get { + return LeftToolStripPanel.Visible; + } + set { + LeftToolStripPanel.Visible = value; + } + } + + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerRightToolStripPanelDescr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public ToolStripPanel RightToolStripPanel { + get { + return rightPanel; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerRightToolStripPanelVisibleDescr), + DefaultValue(true) + ] + public bool RightToolStripPanelVisible { + get { + return RightToolStripPanel.Visible; + } + set { + RightToolStripPanel.Visible = value; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerTopToolStripPanelDescr), + Localizable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public ToolStripPanel TopToolStripPanel { + get { + return topPanel; + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripContainerTopToolStripPanelVisibleDescr), + DefaultValue(true) + ] + public bool TopToolStripPanelVisible { + get { + return TopToolStripPanel.Visible; + } + set { + TopToolStripPanel.Visible = value; + } + } + + /// + /// + /// Controls Collection... + /// This is overriden so that the Controls.Add ( ) is not Code Gened... + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Control.ControlCollection Controls { + get { + return base.Controls; + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override Control.ControlCollection CreateControlsInstance() { + return new ToolStripContainerTypedControlCollection(this, /*isReadOnly*/true); + } + + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + RightToLeft rightToLeft = this.RightToLeft; + + // no need to suspend layout - we're already in a layout transaction. + if (rightToLeft == RightToLeft.Yes) { + RightToolStripPanel.Dock = DockStyle.Left; + LeftToolStripPanel.Dock = DockStyle.Right; + } + else { + RightToolStripPanel.Dock = DockStyle.Right; + LeftToolStripPanel.Dock = DockStyle.Left; + } + } + + protected override void OnSizeChanged(EventArgs e) { + foreach (Control c in this.Controls) { + c.SuspendLayout(); + } + base.OnSizeChanged(e); + foreach (Control c in this.Controls) { + c.ResumeLayout(); + } + + } + + internal override void RecreateHandleCore() { + //If ToolStripContainer's Handle is getting created demand create the childControl handle's + // Refer vsw : 517556 for more details. + if (IsHandleCreated) { + foreach(Control c in Controls) { + c.CreateControl(true); + } + } + base.RecreateHandleCore(); + + } + + internal override bool AllowsKeyboardToolTip() { + return false; + } + + + internal class ToolStripContainerTypedControlCollection : WindowsFormsUtils.ReadOnlyControlCollection { + ToolStripContainer owner; + Type contentPanelType = typeof(ToolStripContentPanel); + Type panelType = typeof(ToolStripPanel); + + public ToolStripContainerTypedControlCollection(Control c, bool isReadOnly) + : base(c, isReadOnly) { + this.owner = c as ToolStripContainer; + } + + public override void Add(Control value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripContainerUseContentPanel)); + } + + Type controlType = value.GetType(); + if (!contentPanelType.IsAssignableFrom(controlType) && !panelType.IsAssignableFrom(controlType)) { + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TypedControlCollectionShouldBeOfTypes, contentPanelType.Name, panelType.Name)), value.GetType().Name); + } + base.Add(value); + } + public override void Remove(Control value) { + if (value is ToolStripPanel || value is ToolStripContentPanel) { + if (!owner.DesignMode) { + if (IsReadOnly) throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + } + base.Remove(value); + } + + internal override void SetChildIndexInternal(Control child, int newIndex) { + if (child is ToolStripPanel || child is ToolStripContentPanel) { + if (!owner.DesignMode) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + } + else { + // just no-op it at DT. + return; + } + } + base.SetChildIndexInternal(child, newIndex); + } + + } + + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripContentPanel.cs b/WindowsForms/Managed/System/WinForms/ToolStripContentPanel.cs new file mode 100644 index 000000000..09541c6e3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripContentPanel.cs @@ -0,0 +1,411 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using System.Collections.Specialized; + using System.Runtime.InteropServices; + using System.Diagnostics.CodeAnalysis; + using System.ComponentModel.Design; + + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.ToolStripContentPanelDesigner, " + AssemblyRef.SystemDesign), + DefaultEvent("Load"), + Docking(DockingBehavior.Never), + InitializationEvent("Load"), + ToolboxItem(false) + ] + public class ToolStripContentPanel : Panel { + private ToolStripRendererSwitcher rendererSwitcher = null; + private BitVector32 state = new BitVector32(); + private static readonly int stateLastDoubleBuffer = BitVector32.CreateMask(); + + private static readonly object EventRendererChanged = new object(); + private static readonly object EventLoad = new object(); + + public ToolStripContentPanel() { + + // Consider: OptimizedDoubleBuffer + SetStyle(ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + Localizable(false) + ] + public override AutoSizeMode AutoSizeMode { + get { + return AutoSizeMode.GrowOnly; + } + set { + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + base.Anchor = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public override bool AutoScroll { + get { return base.AutoScroll; } + set { base.AutoScroll = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMargin { + get { return base.AutoScrollMargin; } + set { base.AutoScrollMargin = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Size AutoScrollMinSize { + get { return base.AutoScrollMinSize; } + set { base.AutoScrollMinSize = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool AutoSize { + get { return base.AutoSize; } + set { base.AutoSize = value; } + } + + + public override Color BackColor { + get { + return base.BackColor; + } + + set { + + // To support transparency on ToolStripContainer, we need this check + // to ensure that background color of the container reflects the + // ContentPanel + if (this.ParentInternal is ToolStripContainer && value == Color.Transparent) { + this.ParentInternal.BackColor = Color.Transparent; + } + base.BackColor = value; + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler AutoSizeChanged{ + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool CausesValidation { + get { return base.CausesValidation; } + set { base.CausesValidation = value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler CausesValidationChanged { + add { + base.CausesValidationChanged += value; + } + remove { + base.CausesValidationChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock=value; + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler DockChanged { + add { + base.DockChanged += value; + } + remove { + base.DockChanged -= value; + } + } + + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripContentPanelOnLoadDescr)] + public event EventHandler Load { + add { + Events.AddHandler(EventLoad, value); + } + remove { + Events.RemoveHandler(EventLoad, value); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Point Location { + get { return base.Location; } + set { base.Location = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new event EventHandler LocationChanged { + add { + base.LocationChanged += value; + } + remove { + base.LocationChanged -= value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Size MinimumSize { + get { return base.MinimumSize; } + set { base.MinimumSize = value; } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Size MaximumSize { + get { return base.MaximumSize; } + set { base.MaximumSize = value; } + } + + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new string Name { + get { + return base.Name; + } + set { + base.Name = value; + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + base.TabStop = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + private ToolStripRendererSwitcher RendererSwitcher { + get { + if (rendererSwitcher == null) { + rendererSwitcher = new ToolStripRendererSwitcher(this, ToolStripRenderMode.System); + HandleRendererChanged(this, EventArgs.Empty); + rendererSwitcher.RendererChanged += new EventHandler(HandleRendererChanged); + } + return rendererSwitcher; + } + } + + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public ToolStripRenderer Renderer { + get { + return RendererSwitcher.Renderer; + } + set { + RendererSwitcher.Renderer = value; + } + } + + + /// + [ + SRDescription(SR.ToolStripRenderModeDescr), + SRCategory(SR.CatAppearance), + ] + public ToolStripRenderMode RenderMode { + get { + return RendererSwitcher.RenderMode; + } + set { + RendererSwitcher.RenderMode = value; + } + } + + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripRendererChanged)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public event EventHandler RendererChanged { + add { + Events.AddHandler(EventRendererChanged, value); + } + remove { + Events.RemoveHandler(EventRendererChanged, value); + } + } + + private void HandleRendererChanged(object sender, EventArgs e) { + OnRendererChanged(e); + } + + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + if (!RecreatingHandle) { + OnLoad(EventArgs.Empty); + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLoad(EventArgs e) { + // There is no good way to explain this event except to say + // that it's just another name for OnControlCreated. + EventHandler handler = (EventHandler)Events[EventLoad]; + if (handler != null) handler(this,e); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaintBackground(PaintEventArgs e) { + ToolStripContentPanelRenderEventArgs rea = new ToolStripContentPanelRenderEventArgs(e.Graphics, this); + + Renderer.DrawToolStripContentPanelBackground(rea); + + if (!rea.Handled) { + base.OnPaintBackground(e); + } + + } + + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected virtual void OnRendererChanged(EventArgs e) { + + // we dont want to be greedy.... if we're using TSProfessionalRenderer go DBuf, else dont. + if (Renderer is ToolStripProfessionalRenderer) { + state[stateLastDoubleBuffer] = this.DoubleBuffered; + //this.DoubleBuffered = true; + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + } + else { + // restore DBuf + this.DoubleBuffered = state[stateLastDoubleBuffer]; + } + + Renderer.InitializeContentPanel(this); + + this.Invalidate(); + + EventHandler handler = (EventHandler)Events[EventRendererChanged]; + if (handler != null) handler(this,e); + + } + + private void ResetRenderMode() { + this.RendererSwitcher.ResetRenderMode(); + } + + private bool ShouldSerializeRenderMode() { + return this.RendererSwitcher.ShouldSerializeRenderMode(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventArgs.cs new file mode 100644 index 000000000..771a7cb28 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventArgs.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + + /// + /// ToolStripContentPanelRenderEventArgs + /// + public class ToolStripContentPanelRenderEventArgs : EventArgs { + + private ToolStripContentPanel contentPanel = null; + private Graphics graphics = null; + private bool handled = false; + + /// + /// This class represents all the information to render the toolStrip + /// + public ToolStripContentPanelRenderEventArgs(Graphics g, ToolStripContentPanel contentPanel) { + this.contentPanel = contentPanel; + this.graphics = g; + } + + + /// + /// the graphics object to draw with + /// + public Graphics Graphics { + get { + return graphics; + } + } + + public bool Handled { + get { return handled; } + set { handled = value; } + } + + /// + /// Represents which toolStrip was affected by the click + /// + public ToolStripContentPanel ToolStripContentPanel { + get { + return contentPanel; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventHandler.cs new file mode 100644 index 000000000..c8651cf2c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripContentPanelRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of the ToolStripPanel is being rendered + /// + /// + public delegate void ToolStripContentPanelRenderEventHandler(object sender, ToolStripContentPanelRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs b/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs new file mode 100644 index 000000000..03be1f4c5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs @@ -0,0 +1,1225 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Runtime.Versioning; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + /// + /// ToolStripItem that can host Controls. + /// + public class ToolStripControlHost : ToolStripItem { + + private Control control; + private int suspendSyncSizeCount = 0; + private ContentAlignment controlAlign = ContentAlignment.MiddleCenter; + private bool inSetVisibleCore = false; + + internal static readonly object EventGotFocus = new object(); + internal static readonly object EventLostFocus = new object(); + internal static readonly object EventKeyDown = new object(); + internal static readonly object EventKeyPress = new object(); + internal static readonly object EventKeyUp = new object(); + internal static readonly object EventEnter = new object(); + internal static readonly object EventLeave = new object(); + internal static readonly object EventValidated = new object(); + internal static readonly object EventValidating = new object(); + + + /// + /// + /// Constructs a ToolStripControlHost + /// + + public ToolStripControlHost(Control c) { + if (c == null) { + throw new ArgumentNullException("c", SR.ControlCannotBeNull); + } + control = c; + SyncControlParent(); + c.Visible = true; + SetBounds(c.Bounds); + + // now that we have a control set in, update the bounds. + Rectangle bounds = this.Bounds; + CommonProperties.UpdateSpecifiedBounds(c, bounds.X, bounds.Y, bounds.Width, bounds.Height); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + c.ToolStripControlHost = this; + } + + OnSubscribeControlEvents(c); + } + public ToolStripControlHost(Control c, string name) : this (c){ + this.Name = name; + } + + /// + public override Color BackColor { + get { + return Control.BackColor; + } + set { + Control.BackColor = value; + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr), + DefaultValue(null) + ] + public override Image BackgroundImage { + get { + return Control.BackgroundImage; + } + set { + Control.BackgroundImage = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public override ImageLayout BackgroundImageLayout { + get { + return Control.BackgroundImageLayout; + } + set { + Control.BackgroundImageLayout = value; + } + } + /// + /// + /// Overriden to return value from Control.CanSelect. + /// + public override bool CanSelect { + get { + if (control != null) { + return (DesignMode || this.Control.CanSelect); + } + return false; + } + } + + [ + SRCategory(SR.CatFocus), + DefaultValue(true), + SRDescription(SR.ControlCausesValidationDescr) + ] + public bool CausesValidation { + get { return Control.CausesValidation; } + set { Control.CausesValidation = value; } + } + + /// + [DefaultValue(ContentAlignment.MiddleCenter), Browsable(false)] + public ContentAlignment ControlAlign { + get { return controlAlign; } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (controlAlign != value) { + controlAlign = value; + OnBoundsChanged(); + } + } + } + + /// + /// + /// The control that this item is hosting. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Control Control { + get { + return control; + } + } + + internal AccessibleObject ControlAccessibilityObject { + get { + return Control?.AccessibilityObject; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + if (Control != null) { + // When you create the control - it sets up its size as its default size. + // Since the property is protected we dont know for sure, but this is a pretty good guess. + return Control.Size; + } + return base.DefaultSize; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new ToolStripItemDisplayStyle DisplayStyle { + get { + return base.DisplayStyle; + } + set { + base.DisplayStyle = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DisplayStyleChanged { + add { + Events.AddHandler(EventDisplayStyleChanged, value); + } + remove { + Events.RemoveHandler(EventDisplayStyleChanged, value); + } + } + + /// + // For control hosts, this property has no effect + /// as they get their own clicks. Use ControlStyles.StandardClick + /// instead. + /// + [DefaultValue(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool DoubleClickEnabled { + get { + return base.DoubleClickEnabled; + } + set { + base.DoubleClickEnabled = value; + } + } + /// + public override Font Font { + get { + return Control.Font; + } + set { + Control.Font = value; + } + } + + /// + public override bool Enabled { + get { + return Control.Enabled; + } + set { + Control.Enabled = value; + } + } + + + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnEnterDescr)] + public event EventHandler Enter { + add { + Events.AddHandler(EventEnter, value); + } + remove { + Events.RemoveHandler(EventEnter, value); + } + } + + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Always) + ] + public virtual bool Focused { + get { + return Control.Focused; + } + } + + /// + public override Color ForeColor { + get { + return Control.ForeColor; + } + set { + Control.ForeColor = value; + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.ToolStripItemOnGotFocusDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler GotFocus { + add { + Events.AddHandler(EventGotFocus, value); + } + remove { + Events.RemoveHandler(EventGotFocus, value); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return base.Image; + } + set { + base.Image = value; + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ToolStripItemImageScaling ImageScaling { + get { + return base.ImageScaling; + } + set { + base.ImageScaling = value; + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Color ImageTransparentColor { + get { + return base.ImageTransparentColor; + } + set { + base.ImageTransparentColor = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ContentAlignment ImageAlign { + get { + return base.ImageAlign; + } + set { + base.ImageAlign = value; + + } + } + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLeaveDescr)] + public event EventHandler Leave { + add { + Events.AddHandler(EventLeave, value); + } + remove { + Events.RemoveHandler(EventLeave, value); + } + } + + /// + /// + /// Occurs when the control loses focus. + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.ToolStripItemOnLostFocusDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler LostFocus { + add { + Events.AddHandler(EventLostFocus, value); + } + remove { + Events.RemoveHandler(EventLostFocus, value); + } + } + + + /// + /// + /// Occurs when a key is pressed down while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyDownDescr)] + public event KeyEventHandler KeyDown { + add { + Events.AddHandler(EventKeyDown, value); + } + remove { + Events.RemoveHandler(EventKeyDown, value); + } + } + + /// + /// + /// Occurs when a key is pressed while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyPressDescr)] + public event KeyPressEventHandler KeyPress { + add { + Events.AddHandler(EventKeyPress, value); + } + remove { + Events.RemoveHandler(EventKeyPress, value); + } + } + + /// + /// + /// Occurs when a key is released while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyUpDescr)] + public event KeyEventHandler KeyUp { + add { + Events.AddHandler(EventKeyUp, value); + } + remove { + Events.RemoveHandler(EventKeyUp, value); + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + public override RightToLeft RightToLeft { + get { + if (control != null) { + return control.RightToLeft; + } + return base.RightToLeft; + } + set { + if (control != null) { + control.RightToLeft = value; + } + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool RightToLeftAutoMirrorImage { + get { + return base.RightToLeftAutoMirrorImage; + } + set { + base.RightToLeftAutoMirrorImage = value; + } + } + + /// + public override bool Selected { + get { + if (Control != null) + { + return Control.Focused; + } + return false; + } + } + + public override Size Size { + get { + return base.Size; + } + set { + Rectangle specifiedBounds = Rectangle.Empty; + if (control != null) { + // we dont normally update the specified bounds, but if someone explicitly sets + // the size we should. + specifiedBounds = control.Bounds; + specifiedBounds.Size = value; + CommonProperties.UpdateSpecifiedBounds(control, specifiedBounds.X, specifiedBounds.Y, specifiedBounds.Width, specifiedBounds.Height); + } + + + base.Size = value; + + if (control != null) { + // checking again in case the control has adjusted the size. + Rectangle bounds = control.Bounds; + if (bounds != specifiedBounds) { + CommonProperties.UpdateSpecifiedBounds(control, bounds.X, bounds.Y, bounds.Width, bounds.Height); + } + } + } + } + + /// + /// Overriden to set the Site for the control hosted. This is set at DesignTime when the component is added to the Container. + /// Refer to VsWhidbey : 390573. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override ISite Site { + get { + return base.Site; + } + set { + base.Site = value; + if (value != null) + { + Control.Site = new StubSite(Control, this); + } + else + { + Control.Site = null; + } + } + } + + /// + /// + /// Overriden to modify hosted control's text. + /// + [DefaultValue("")] + public override string Text { + get { + return Control.Text; + } + set { + Control.Text = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new ContentAlignment TextAlign { + get { + return base.TextAlign; + } + set { + base.TextAlign = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(ToolStripTextDirection.Horizontal)] + public override ToolStripTextDirection TextDirection { + get { + return base.TextDirection; + } + set { + base.TextDirection = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new TextImageRelation TextImageRelation { + get { + return base.TextImageRelation; + } + set { + base.TextImageRelation = value; + } + } + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatingDescr)] + public event CancelEventHandler Validating { + add { + Events.AddHandler(EventValidating, value); + } + remove { + Events.RemoveHandler(EventValidating, value); + } + } + + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatedDescr)] + public event EventHandler Validated { + add { + Events.AddHandler(EventValidated, value); + } + remove { + Events.RemoveHandler(EventValidated, value); + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return Control.AccessibilityObject; + } + + /// + /// + /// Cleans up and destroys the hosted control. + /// + protected override void Dispose(bool disposing) { + // Call base first so other things stop trying to talk to the control. This will + // unparent the host item which will cause a SyncControlParent, so the control + // will be correctly unparented before being disposed. + base.Dispose (disposing); + + if (disposing && Control != null) { + + OnUnsubscribeControlEvents(Control); + + // we only call control.Dispose if we are NOT being disposed in the finalizer. + Control.Dispose(); + control = null; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void Focus() + { + Control.Focus(); + } + + + /// + public override Size GetPreferredSize(Size constrainingSize) { + if (control != null) { + return Control.GetPreferredSize(constrainingSize - Padding.Size) + Padding.Size; + } + return base.GetPreferredSize(constrainingSize); + } + + /// + /// Handle* wrappers: + /// We sync the event from the hosted control and call resurface it on ToolStripItem. + /// + + private void HandleClick(object sender, System.EventArgs e) { + OnClick(e); + } + private void HandleBackColorChanged(object sender, System.EventArgs e) { + OnBackColorChanged(e); + } + private void HandleDoubleClick(object sender, System.EventArgs e) { + OnDoubleClick(e); + } + private void HandleDragDrop(object sender, DragEventArgs e) { + OnDragDrop(e); + } + private void HandleDragEnter(object sender, DragEventArgs e) { + OnDragEnter(e); + } + private void HandleDragLeave(object sender, EventArgs e) { + OnDragLeave(e); + } + private void HandleDragOver(object sender, DragEventArgs e) { + OnDragOver(e); + } + private void HandleEnter(object sender, System.EventArgs e) { + OnEnter(e); + } + private void HandleEnabledChanged(object sender, System.EventArgs e) { + OnEnabledChanged(e); + } + private void HandleForeColorChanged(object sender, System.EventArgs e) { + OnForeColorChanged(e); + } + private void HandleGiveFeedback(object sender, GiveFeedbackEventArgs e) { + OnGiveFeedback(e); + } + private void HandleGotFocus(object sender, EventArgs e) { + OnGotFocus(e); + } + private void HandleLocationChanged(object sender, EventArgs e) { + OnLocationChanged(e); + } + private void HandleLostFocus(object sender, EventArgs e) { + OnLostFocus(e); + } + private void HandleKeyDown(object sender, KeyEventArgs e) { + OnKeyDown(e); + } + private void HandleKeyPress(object sender, KeyPressEventArgs e) { + OnKeyPress(e); + } + private void HandleKeyUp(object sender, KeyEventArgs e) { + OnKeyUp(e); + } + private void HandleLeave(object sender, System.EventArgs e) { + OnLeave(e); + } + private void HandleMouseDown(object sender, MouseEventArgs e) { + OnMouseDown(e); + RaiseMouseEvent(ToolStripItem.EventMouseDown, e); + } + private void HandleMouseEnter(object sender, EventArgs e) { + OnMouseEnter(e); + RaiseEvent(ToolStripItem.EventMouseEnter, e); + } + private void HandleMouseLeave(object sender, EventArgs e) { + OnMouseLeave(e); + RaiseEvent(ToolStripItem.EventMouseLeave, e); + } + private void HandleMouseHover(object sender, EventArgs e) { + OnMouseHover(e); + RaiseEvent(ToolStripItem.EventMouseHover, e); + } + private void HandleMouseMove(object sender, MouseEventArgs e) { + OnMouseMove(e); + RaiseMouseEvent(ToolStripItem.EventMouseMove, e); + } + private void HandleMouseUp(object sender, MouseEventArgs e) { + OnMouseUp(e); + RaiseMouseEvent(ToolStripItem.EventMouseUp, e); + } + private void HandlePaint(object sender, PaintEventArgs e) { + OnPaint(e); + RaisePaintEvent(ToolStripItem.EventPaint, e); + } + private void HandleQueryAccessibilityHelp(object sender, QueryAccessibilityHelpEventArgs e) { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Events[ToolStripItem.EventQueryAccessibilityHelp]; + if (handler != null) handler(this, e); + } + private void HandleQueryContinueDrag(object sender, QueryContinueDragEventArgs e) { + OnQueryContinueDrag(e); + } + private void HandleRightToLeftChanged(object sender, EventArgs e) { + OnRightToLeftChanged(e); + } + private void HandleResize(object sender, EventArgs e) { + if (suspendSyncSizeCount == 0) { + OnHostedControlResize(e); + } + } + + private void HandleTextChanged(object sender, EventArgs e) { + OnTextChanged(e); + } + private void HandleControlVisibleChanged(object sender, EventArgs e) { + // check the STATE_VISIBLE flag rather than using Control.Visible. + // if we check while it's unparented it will return visible false. + // the easiest way to do this is to use ParticipatesInLayout. + bool controlVisibleStateFlag = ((IArrangedElement)Control).ParticipatesInLayout; + bool itemVisibleStateFlag = ((IArrangedElement)(this)).ParticipatesInLayout; + + if (itemVisibleStateFlag != controlVisibleStateFlag) { + this.Visible = Control.Visible; + // this should fire the OnVisibleChanged and raise events appropriately. + }; + } + + private void HandleValidating(object sender, CancelEventArgs e) { + OnValidating(e); + } + + private void HandleValidated(object sender, System.EventArgs e) { + OnValidated(e); + } + + internal override void OnAccessibleDescriptionChanged(EventArgs e) { + Control.AccessibleDescription = AccessibleDescription; + } + internal override void OnAccessibleNameChanged(EventArgs e) { + Control.AccessibleName = AccessibleName; + } + internal override void OnAccessibleDefaultActionDescriptionChanged(EventArgs e) { + Control.AccessibleDefaultActionDescription = AccessibleDefaultActionDescription; + } + internal override void OnAccessibleRoleChanged(EventArgs e) { + Control.AccessibleRole = AccessibleRole; + } + + + protected virtual void OnEnter(EventArgs e) { + RaiseEvent(EventEnter, e); + } + + + /// + /// + /// called when the control has lost focus + /// + protected virtual void OnGotFocus(EventArgs e) { + RaiseEvent(EventGotFocus, e); + } + + protected virtual void OnLeave(EventArgs e) { + RaiseEvent(EventLeave, e); + } + + /// + /// + /// called when the control has lost focus + /// + protected virtual void OnLostFocus(EventArgs e) { + RaiseEvent(EventLostFocus, e); + } + /// + protected virtual void OnKeyDown(KeyEventArgs e) { + RaiseKeyEvent(EventKeyDown, e); + } + /// + protected virtual void OnKeyPress(KeyPressEventArgs e) { + RaiseKeyPressEvent(EventKeyPress, e); + } + /// + protected virtual void OnKeyUp(KeyEventArgs e) { + RaiseKeyEvent(EventKeyUp, e); + } + /// + /// + /// Called when the items bounds are changed. Here, we update the Control's bounds. + /// + protected override void OnBoundsChanged() { + if (control != null) { + SuspendSizeSync(); + IArrangedElement element = control as IArrangedElement; + if (element == null) { + Debug.Fail("why are we here? control should not be null"); + return; + } + + Size size = LayoutUtils.DeflateRect(this.Bounds, this.Padding).Size; + Rectangle bounds = LayoutUtils.Align(size, this.Bounds, ControlAlign); + + // use BoundsSpecified.None so we dont deal w/specified bounds - this way we can tell what someone has set the size to. + element.SetBounds(bounds, BoundsSpecified.None); + + // sometimes a control can ignore the size passed in, use the adjustment + // to re-align. + if (bounds != control.Bounds) { + bounds = LayoutUtils.Align(control.Size, this.Bounds, ControlAlign); + element.SetBounds(bounds, BoundsSpecified.None); + } + ResumeSizeSync(); + } + } + + /// + /// + /// Called when the control fires its Paint event. + /// + protected override void OnPaint(PaintEventArgs e) { + // do nothing.... + } + + + /// + protected internal override void OnLayout(LayoutEventArgs e) { + // do nothing... called via the controls collection + } + + /// + /// + /// Called when the item's parent has been changed. + /// + protected override void OnParentChanged(ToolStrip oldParent, ToolStrip newParent) { + if (oldParent != null && Owner == null && newParent == null && Control != null) { + // if we've really been removed from the item collection, + // politely remove ourselves from the control collection + WindowsFormsUtils.ReadOnlyControlCollection oldControlCollection + = GetControlCollection(Control.ParentInternal as ToolStrip); + if (oldControlCollection != null) { + oldControlCollection.RemoveInternal(Control); + } + } + else { + SyncControlParent(); + } + + base.OnParentChanged(oldParent, newParent); + } + + + + + /// + /// + /// The events from the hosted control are subscribed here. + /// Override to add/prevent syncing of control events. + /// NOTE: if you override and hook up events here, you should unhook in OnUnsubscribeControlEvents. + /// + protected virtual void OnSubscribeControlEvents(Control control) { + + if (control != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + control.Click += new EventHandler(HandleClick); + control.BackColorChanged += new EventHandler(HandleBackColorChanged); + control.DoubleClick += new EventHandler(HandleDoubleClick); + control.DragDrop += new DragEventHandler(HandleDragDrop); + control.DragEnter += new DragEventHandler(HandleDragEnter); + control.DragLeave += new EventHandler(HandleDragLeave); + control.DragOver += new DragEventHandler(HandleDragOver); + control.Enter += new EventHandler(HandleEnter); + control.EnabledChanged += new EventHandler(HandleEnabledChanged); + control.ForeColorChanged += new EventHandler(HandleForeColorChanged); + control.GiveFeedback += new GiveFeedbackEventHandler(HandleGiveFeedback); + control.GotFocus += new EventHandler(HandleGotFocus); + control.Leave += new EventHandler(HandleLeave); + control.LocationChanged += new EventHandler(HandleLocationChanged); + control.LostFocus += new EventHandler(HandleLostFocus); + control.KeyDown += new KeyEventHandler(HandleKeyDown); + control.KeyPress += new KeyPressEventHandler(HandleKeyPress); + control.KeyUp += new KeyEventHandler(HandleKeyUp); + control.MouseDown += new MouseEventHandler(HandleMouseDown); + control.MouseEnter += new EventHandler(HandleMouseEnter); + control.MouseHover += new EventHandler(HandleMouseHover); + control.MouseLeave += new EventHandler(HandleMouseLeave); + control.MouseMove += new MouseEventHandler(HandleMouseMove); + control.MouseUp += new MouseEventHandler(HandleMouseUp); + control.Paint += new PaintEventHandler(HandlePaint); + control.QueryAccessibilityHelp += new QueryAccessibilityHelpEventHandler(HandleQueryAccessibilityHelp); + control.QueryContinueDrag += new QueryContinueDragEventHandler(HandleQueryContinueDrag); + control.Resize += new EventHandler(HandleResize); + control.RightToLeftChanged += new EventHandler(HandleRightToLeftChanged); + control.TextChanged += new EventHandler(HandleTextChanged); + control.VisibleChanged += new EventHandler(HandleControlVisibleChanged); + control.Validating += new CancelEventHandler(HandleValidating); + control.Validated += new EventHandler(HandleValidated); + + } + + } + + + /// + /// + /// The events from the hosted control are unsubscribed here. + /// Override to unhook events subscribed in OnSubscribeControlEvents. + /// + protected virtual void OnUnsubscribeControlEvents(Control control) { + if (control != null) { + // Please keep this alphabetized and in sync with Subscribe + // + control.Click -= new EventHandler(HandleClick); + control.BackColorChanged -= new EventHandler(HandleBackColorChanged); + control.DoubleClick -= new EventHandler(HandleDoubleClick); + control.DragDrop -= new DragEventHandler(HandleDragDrop); + control.DragEnter -= new DragEventHandler(HandleDragEnter); + control.DragLeave -= new EventHandler(HandleDragLeave); + control.DragOver -= new DragEventHandler(HandleDragOver); + control.Enter -= new EventHandler(HandleEnter); + control.EnabledChanged -= new EventHandler(HandleEnabledChanged); + control.ForeColorChanged -= new EventHandler(HandleForeColorChanged); + control.GiveFeedback -= new GiveFeedbackEventHandler(HandleGiveFeedback); + control.GotFocus -= new EventHandler(HandleGotFocus); + control.Leave -= new EventHandler(HandleLeave); + control.LocationChanged -= new EventHandler(HandleLocationChanged); + control.LostFocus -= new EventHandler(HandleLostFocus); + control.KeyDown -= new KeyEventHandler(HandleKeyDown); + control.KeyPress -= new KeyPressEventHandler(HandleKeyPress); + control.KeyUp -= new KeyEventHandler(HandleKeyUp); + control.MouseDown -= new MouseEventHandler(HandleMouseDown); + control.MouseEnter -= new EventHandler(HandleMouseEnter); + control.MouseHover -= new EventHandler(HandleMouseHover); + control.MouseLeave -= new EventHandler(HandleMouseLeave); + control.MouseMove -= new MouseEventHandler(HandleMouseMove); + control.MouseUp -= new MouseEventHandler(HandleMouseUp); + control.Paint -= new PaintEventHandler(HandlePaint); + control.QueryAccessibilityHelp -= new QueryAccessibilityHelpEventHandler(HandleQueryAccessibilityHelp); + control.QueryContinueDrag -= new QueryContinueDragEventHandler(HandleQueryContinueDrag); + control.Resize -= new EventHandler(HandleResize); + control.RightToLeftChanged -= new EventHandler(HandleRightToLeftChanged); + control.TextChanged -= new EventHandler(HandleTextChanged); + control.VisibleChanged -= new EventHandler(HandleControlVisibleChanged); + control.Validating -= new CancelEventHandler(HandleValidating); + control.Validated -= new EventHandler(HandleValidated); + + } + + } + + protected virtual void OnValidating(CancelEventArgs e) { + RaiseCancelEvent(EventValidating, e); + } + + + protected virtual void OnValidated(EventArgs e) { + RaiseEvent(EventValidated, e); + } + + private static WindowsFormsUtils.ReadOnlyControlCollection GetControlCollection(ToolStrip toolStrip) { + WindowsFormsUtils.ReadOnlyControlCollection newControls = + toolStrip != null ? (WindowsFormsUtils.ReadOnlyControlCollection) toolStrip.Controls : null; + return newControls; + } + + // Ensures the hosted Control is parented to the ToolStrip hosting this ToolStripItem. + private void SyncControlParent() { + WindowsFormsUtils.ReadOnlyControlCollection newControls = GetControlCollection(ParentInternal); + if (newControls != null) { + newControls.AddInternal(Control); + } + } + + /// + protected virtual void OnHostedControlResize(EventArgs e) { + // support for syncing the wrapper when the control size has changed + this.Size = Control.Size; + } + + /// + // SECREVIEW: Technically full trust != unmanaged code, therefore this WndProc is at + // lesser permission that all the other members in the class. Practically speaking though + // they are the same - keeping UnmanagedCode to match the base class. + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + // Control will get this from being in the control collection. + return false; + } + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal override bool ProcessMnemonic(char charCode) { + if (control != null) { + return control.ProcessMnemonic( charCode ); + } + return base.ProcessMnemonic( charCode ); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessDialogKey(Keys keyData) { + // Control will get this from being in the control collection. + return false; + } + + protected override void SetVisibleCore(bool visible) { + // This is needed, because if you try and set set visible to true before the parent is visible, + // we will get called back into here, and set it back to false, since the parent is not visible. + if (inSetVisibleCore) { + return; + } + + inSetVisibleCore = true; + Control.SuspendLayout(); + try { + Control.Visible = visible; + } + finally { + Control.ResumeLayout(false); + // this will go ahead and perform layout. + base.SetVisibleCore(visible); + inSetVisibleCore = false; + } + + + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetBackColor() { + Control.ResetBackColor(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetForeColor() { + Control.ResetForeColor(); + } + + private void SuspendSizeSync() { + this.suspendSyncSizeCount++; + } + + private void ResumeSizeSync() { + this.suspendSyncSizeCount--; + } + internal override bool ShouldSerializeBackColor() { + if (control != null) { + return control.ShouldSerializeBackColor(); + } + return base.ShouldSerializeBackColor(); + } + internal override bool ShouldSerializeForeColor() { + if (control != null) { + return control.ShouldSerializeForeColor(); + } + return base.ShouldSerializeForeColor(); + } + + internal override bool ShouldSerializeFont() { + if (control != null) { + return control.ShouldSerializeFont(); + } + return base.ShouldSerializeFont(); + } + + internal override bool ShouldSerializeRightToLeft() { + if (control != null) { + return control.ShouldSerializeRightToLeft(); + } + return base.ShouldSerializeRightToLeft(); + } + + internal override void OnKeyboardToolTipHook(ToolTip toolTip) { + base.OnKeyboardToolTipHook(toolTip); + + KeyboardToolTipStateMachine.Instance.Hook(this.Control, toolTip); + } + + internal override void OnKeyboardToolTipUnhook(ToolTip toolTip) { + base.OnKeyboardToolTipUnhook(toolTip); + + KeyboardToolTipStateMachine.Instance.Unhook(this.Control, toolTip); + } + + // Our implementation of ISite: + // Since the Control which is wrapped by ToolStripControlHost is a runtime instance, there is no way of knowing + // whether the control is in runtime or designtime. + // This implementation of ISite would be set to Control.Site when ToolStripControlHost.Site is set at DesignTime. (Refer to Site property on ToolStripControlHost) + // This implementation just returns the DesigMode property to be ToolStripControlHost's DesignMode property. + // Everything else is pretty much default implementation. + private class StubSite : ISite, IDictionaryService + { + private Hashtable _dictionary = null; + IComponent comp = null; + IComponent owner = null; + + public StubSite(Component control, Component host) + { + comp = control as IComponent; + owner = host as IComponent; + } + // The component sited by this component site. + /// + /// When implemented by a class, gets the component associated with the . + /// + IComponent ISite.Component { + get + { + return comp; + } + } + + // The container in which the component is sited. + /// + /// When implemented by a class, gets the container associated with the . + /// + IContainer ISite.Container { + get { + return owner.Site.Container; + } + } + + // Indicates whether the component is in design mode. + /// + /// When implemented by a class, determines whether the component is in design mode. + /// + bool ISite.DesignMode { + get { + return owner.Site.DesignMode; + } + } + + // The name of the component. + // + /// + /// When implemented by a class, gets or sets the name of + /// the component associated with the . + /// + String ISite.Name { + get { + return owner.Site.Name; + } + set { + owner.Site.Name = value; + } + } + + /// + /// Returns the requested service. + /// + object IServiceProvider.GetService(Type service) { + if (service == null) { + throw new ArgumentNullException("service"); + } + + // We have to implement our own dictionary service. If we don't, + // the properties of the underlying component will end up being + // overwritten by our own properties when GetProperties is called + if (service == typeof(IDictionaryService)) { + return this; + } + + if (owner.Site != null) { + return owner.Site.GetService(service); + } + return null; + } + + /// + /// Retrieves the key corresponding to the given value. + /// + object IDictionaryService.GetKey(object value) { + if (_dictionary != null) { + foreach (DictionaryEntry de in _dictionary) { + object o = de.Value; + if (value != null && value.Equals(o)) { + return de.Key; + } + } + } + return null; + } + + /// + /// Retrieves the value corresponding to the given key. + /// + object IDictionaryService.GetValue(object key) { + if (_dictionary != null) { + return _dictionary[key]; + } + return null; + } + + /// + /// Stores the given key-value pair in an object's site. This key-value + /// pair is stored on a per-object basis, and is a handy place to save + /// additional information about a component. + /// + void IDictionaryService.SetValue(object key, object value) { + if (_dictionary == null) { + _dictionary = new Hashtable(); + } + if (value == null) { + _dictionary.Remove(key); + } else { + _dictionary[key] = value; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs.back b/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs.back new file mode 100644 index 000000000..03be1f4c5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripControlHost.cs.back @@ -0,0 +1,1225 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Runtime.Versioning; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + /// + /// ToolStripItem that can host Controls. + /// + public class ToolStripControlHost : ToolStripItem { + + private Control control; + private int suspendSyncSizeCount = 0; + private ContentAlignment controlAlign = ContentAlignment.MiddleCenter; + private bool inSetVisibleCore = false; + + internal static readonly object EventGotFocus = new object(); + internal static readonly object EventLostFocus = new object(); + internal static readonly object EventKeyDown = new object(); + internal static readonly object EventKeyPress = new object(); + internal static readonly object EventKeyUp = new object(); + internal static readonly object EventEnter = new object(); + internal static readonly object EventLeave = new object(); + internal static readonly object EventValidated = new object(); + internal static readonly object EventValidating = new object(); + + + /// + /// + /// Constructs a ToolStripControlHost + /// + + public ToolStripControlHost(Control c) { + if (c == null) { + throw new ArgumentNullException("c", SR.ControlCannotBeNull); + } + control = c; + SyncControlParent(); + c.Visible = true; + SetBounds(c.Bounds); + + // now that we have a control set in, update the bounds. + Rectangle bounds = this.Bounds; + CommonProperties.UpdateSpecifiedBounds(c, bounds.X, bounds.Y, bounds.Width, bounds.Height); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + c.ToolStripControlHost = this; + } + + OnSubscribeControlEvents(c); + } + public ToolStripControlHost(Control c, string name) : this (c){ + this.Name = name; + } + + /// + public override Color BackColor { + get { + return Control.BackColor; + } + set { + Control.BackColor = value; + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr), + DefaultValue(null) + ] + public override Image BackgroundImage { + get { + return Control.BackgroundImage; + } + set { + Control.BackgroundImage = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public override ImageLayout BackgroundImageLayout { + get { + return Control.BackgroundImageLayout; + } + set { + Control.BackgroundImageLayout = value; + } + } + /// + /// + /// Overriden to return value from Control.CanSelect. + /// + public override bool CanSelect { + get { + if (control != null) { + return (DesignMode || this.Control.CanSelect); + } + return false; + } + } + + [ + SRCategory(SR.CatFocus), + DefaultValue(true), + SRDescription(SR.ControlCausesValidationDescr) + ] + public bool CausesValidation { + get { return Control.CausesValidation; } + set { Control.CausesValidation = value; } + } + + /// + [DefaultValue(ContentAlignment.MiddleCenter), Browsable(false)] + public ContentAlignment ControlAlign { + get { return controlAlign; } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (controlAlign != value) { + controlAlign = value; + OnBoundsChanged(); + } + } + } + + /// + /// + /// The control that this item is hosting. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Control Control { + get { + return control; + } + } + + internal AccessibleObject ControlAccessibilityObject { + get { + return Control?.AccessibilityObject; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + if (Control != null) { + // When you create the control - it sets up its size as its default size. + // Since the property is protected we dont know for sure, but this is a pretty good guess. + return Control.Size; + } + return base.DefaultSize; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new ToolStripItemDisplayStyle DisplayStyle { + get { + return base.DisplayStyle; + } + set { + base.DisplayStyle = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DisplayStyleChanged { + add { + Events.AddHandler(EventDisplayStyleChanged, value); + } + remove { + Events.RemoveHandler(EventDisplayStyleChanged, value); + } + } + + /// + // For control hosts, this property has no effect + /// as they get their own clicks. Use ControlStyles.StandardClick + /// instead. + /// + [DefaultValue(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool DoubleClickEnabled { + get { + return base.DoubleClickEnabled; + } + set { + base.DoubleClickEnabled = value; + } + } + /// + public override Font Font { + get { + return Control.Font; + } + set { + Control.Font = value; + } + } + + /// + public override bool Enabled { + get { + return Control.Enabled; + } + set { + Control.Enabled = value; + } + } + + + /// + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnEnterDescr)] + public event EventHandler Enter { + add { + Events.AddHandler(EventEnter, value); + } + remove { + Events.RemoveHandler(EventEnter, value); + } + } + + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Always) + ] + public virtual bool Focused { + get { + return Control.Focused; + } + } + + /// + public override Color ForeColor { + get { + return Control.ForeColor; + } + set { + Control.ForeColor = value; + } + } + + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.ToolStripItemOnGotFocusDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler GotFocus { + add { + Events.AddHandler(EventGotFocus, value); + } + remove { + Events.RemoveHandler(EventGotFocus, value); + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return base.Image; + } + set { + base.Image = value; + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ToolStripItemImageScaling ImageScaling { + get { + return base.ImageScaling; + } + set { + base.ImageScaling = value; + } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Color ImageTransparentColor { + get { + return base.ImageTransparentColor; + } + set { + base.ImageTransparentColor = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ContentAlignment ImageAlign { + get { + return base.ImageAlign; + } + set { + base.ImageAlign = value; + + } + } + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnLeaveDescr)] + public event EventHandler Leave { + add { + Events.AddHandler(EventLeave, value); + } + remove { + Events.RemoveHandler(EventLeave, value); + } + } + + /// + /// + /// Occurs when the control loses focus. + /// + [ + SRCategory(SR.CatFocus), + SRDescription(SR.ToolStripItemOnLostFocusDescr), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public event EventHandler LostFocus { + add { + Events.AddHandler(EventLostFocus, value); + } + remove { + Events.RemoveHandler(EventLostFocus, value); + } + } + + + /// + /// + /// Occurs when a key is pressed down while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyDownDescr)] + public event KeyEventHandler KeyDown { + add { + Events.AddHandler(EventKeyDown, value); + } + remove { + Events.RemoveHandler(EventKeyDown, value); + } + } + + /// + /// + /// Occurs when a key is pressed while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyPressDescr)] + public event KeyPressEventHandler KeyPress { + add { + Events.AddHandler(EventKeyPress, value); + } + remove { + Events.RemoveHandler(EventKeyPress, value); + } + } + + /// + /// + /// Occurs when a key is released while the control has focus. + /// + [SRCategory(SR.CatKey), SRDescription(SR.ControlOnKeyUpDescr)] + public event KeyEventHandler KeyUp { + add { + Events.AddHandler(EventKeyUp, value); + } + remove { + Events.RemoveHandler(EventKeyUp, value); + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + public override RightToLeft RightToLeft { + get { + if (control != null) { + return control.RightToLeft; + } + return base.RightToLeft; + } + set { + if (control != null) { + control.RightToLeft = value; + } + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool RightToLeftAutoMirrorImage { + get { + return base.RightToLeftAutoMirrorImage; + } + set { + base.RightToLeftAutoMirrorImage = value; + } + } + + /// + public override bool Selected { + get { + if (Control != null) + { + return Control.Focused; + } + return false; + } + } + + public override Size Size { + get { + return base.Size; + } + set { + Rectangle specifiedBounds = Rectangle.Empty; + if (control != null) { + // we dont normally update the specified bounds, but if someone explicitly sets + // the size we should. + specifiedBounds = control.Bounds; + specifiedBounds.Size = value; + CommonProperties.UpdateSpecifiedBounds(control, specifiedBounds.X, specifiedBounds.Y, specifiedBounds.Width, specifiedBounds.Height); + } + + + base.Size = value; + + if (control != null) { + // checking again in case the control has adjusted the size. + Rectangle bounds = control.Bounds; + if (bounds != specifiedBounds) { + CommonProperties.UpdateSpecifiedBounds(control, bounds.X, bounds.Y, bounds.Width, bounds.Height); + } + } + } + } + + /// + /// Overriden to set the Site for the control hosted. This is set at DesignTime when the component is added to the Container. + /// Refer to VsWhidbey : 390573. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override ISite Site { + get { + return base.Site; + } + set { + base.Site = value; + if (value != null) + { + Control.Site = new StubSite(Control, this); + } + else + { + Control.Site = null; + } + } + } + + /// + /// + /// Overriden to modify hosted control's text. + /// + [DefaultValue("")] + public override string Text { + get { + return Control.Text; + } + set { + Control.Text = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new ContentAlignment TextAlign { + get { + return base.TextAlign; + } + set { + base.TextAlign = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(ToolStripTextDirection.Horizontal)] + public override ToolStripTextDirection TextDirection { + get { + return base.TextDirection; + } + set { + base.TextDirection = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new TextImageRelation TextImageRelation { + get { + return base.TextImageRelation; + } + set { + base.TextImageRelation = value; + } + } + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatingDescr)] + public event CancelEventHandler Validating { + add { + Events.AddHandler(EventValidating, value); + } + remove { + Events.RemoveHandler(EventValidating, value); + } + } + + + [SRCategory(SR.CatFocus), SRDescription(SR.ControlOnValidatedDescr)] + public event EventHandler Validated { + add { + Events.AddHandler(EventValidated, value); + } + remove { + Events.RemoveHandler(EventValidated, value); + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return Control.AccessibilityObject; + } + + /// + /// + /// Cleans up and destroys the hosted control. + /// + protected override void Dispose(bool disposing) { + // Call base first so other things stop trying to talk to the control. This will + // unparent the host item which will cause a SyncControlParent, so the control + // will be correctly unparented before being disposed. + base.Dispose (disposing); + + if (disposing && Control != null) { + + OnUnsubscribeControlEvents(Control); + + // we only call control.Dispose if we are NOT being disposed in the finalizer. + Control.Dispose(); + control = null; + } + } + + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void Focus() + { + Control.Focus(); + } + + + /// + public override Size GetPreferredSize(Size constrainingSize) { + if (control != null) { + return Control.GetPreferredSize(constrainingSize - Padding.Size) + Padding.Size; + } + return base.GetPreferredSize(constrainingSize); + } + + /// + /// Handle* wrappers: + /// We sync the event from the hosted control and call resurface it on ToolStripItem. + /// + + private void HandleClick(object sender, System.EventArgs e) { + OnClick(e); + } + private void HandleBackColorChanged(object sender, System.EventArgs e) { + OnBackColorChanged(e); + } + private void HandleDoubleClick(object sender, System.EventArgs e) { + OnDoubleClick(e); + } + private void HandleDragDrop(object sender, DragEventArgs e) { + OnDragDrop(e); + } + private void HandleDragEnter(object sender, DragEventArgs e) { + OnDragEnter(e); + } + private void HandleDragLeave(object sender, EventArgs e) { + OnDragLeave(e); + } + private void HandleDragOver(object sender, DragEventArgs e) { + OnDragOver(e); + } + private void HandleEnter(object sender, System.EventArgs e) { + OnEnter(e); + } + private void HandleEnabledChanged(object sender, System.EventArgs e) { + OnEnabledChanged(e); + } + private void HandleForeColorChanged(object sender, System.EventArgs e) { + OnForeColorChanged(e); + } + private void HandleGiveFeedback(object sender, GiveFeedbackEventArgs e) { + OnGiveFeedback(e); + } + private void HandleGotFocus(object sender, EventArgs e) { + OnGotFocus(e); + } + private void HandleLocationChanged(object sender, EventArgs e) { + OnLocationChanged(e); + } + private void HandleLostFocus(object sender, EventArgs e) { + OnLostFocus(e); + } + private void HandleKeyDown(object sender, KeyEventArgs e) { + OnKeyDown(e); + } + private void HandleKeyPress(object sender, KeyPressEventArgs e) { + OnKeyPress(e); + } + private void HandleKeyUp(object sender, KeyEventArgs e) { + OnKeyUp(e); + } + private void HandleLeave(object sender, System.EventArgs e) { + OnLeave(e); + } + private void HandleMouseDown(object sender, MouseEventArgs e) { + OnMouseDown(e); + RaiseMouseEvent(ToolStripItem.EventMouseDown, e); + } + private void HandleMouseEnter(object sender, EventArgs e) { + OnMouseEnter(e); + RaiseEvent(ToolStripItem.EventMouseEnter, e); + } + private void HandleMouseLeave(object sender, EventArgs e) { + OnMouseLeave(e); + RaiseEvent(ToolStripItem.EventMouseLeave, e); + } + private void HandleMouseHover(object sender, EventArgs e) { + OnMouseHover(e); + RaiseEvent(ToolStripItem.EventMouseHover, e); + } + private void HandleMouseMove(object sender, MouseEventArgs e) { + OnMouseMove(e); + RaiseMouseEvent(ToolStripItem.EventMouseMove, e); + } + private void HandleMouseUp(object sender, MouseEventArgs e) { + OnMouseUp(e); + RaiseMouseEvent(ToolStripItem.EventMouseUp, e); + } + private void HandlePaint(object sender, PaintEventArgs e) { + OnPaint(e); + RaisePaintEvent(ToolStripItem.EventPaint, e); + } + private void HandleQueryAccessibilityHelp(object sender, QueryAccessibilityHelpEventArgs e) { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Events[ToolStripItem.EventQueryAccessibilityHelp]; + if (handler != null) handler(this, e); + } + private void HandleQueryContinueDrag(object sender, QueryContinueDragEventArgs e) { + OnQueryContinueDrag(e); + } + private void HandleRightToLeftChanged(object sender, EventArgs e) { + OnRightToLeftChanged(e); + } + private void HandleResize(object sender, EventArgs e) { + if (suspendSyncSizeCount == 0) { + OnHostedControlResize(e); + } + } + + private void HandleTextChanged(object sender, EventArgs e) { + OnTextChanged(e); + } + private void HandleControlVisibleChanged(object sender, EventArgs e) { + // check the STATE_VISIBLE flag rather than using Control.Visible. + // if we check while it's unparented it will return visible false. + // the easiest way to do this is to use ParticipatesInLayout. + bool controlVisibleStateFlag = ((IArrangedElement)Control).ParticipatesInLayout; + bool itemVisibleStateFlag = ((IArrangedElement)(this)).ParticipatesInLayout; + + if (itemVisibleStateFlag != controlVisibleStateFlag) { + this.Visible = Control.Visible; + // this should fire the OnVisibleChanged and raise events appropriately. + }; + } + + private void HandleValidating(object sender, CancelEventArgs e) { + OnValidating(e); + } + + private void HandleValidated(object sender, System.EventArgs e) { + OnValidated(e); + } + + internal override void OnAccessibleDescriptionChanged(EventArgs e) { + Control.AccessibleDescription = AccessibleDescription; + } + internal override void OnAccessibleNameChanged(EventArgs e) { + Control.AccessibleName = AccessibleName; + } + internal override void OnAccessibleDefaultActionDescriptionChanged(EventArgs e) { + Control.AccessibleDefaultActionDescription = AccessibleDefaultActionDescription; + } + internal override void OnAccessibleRoleChanged(EventArgs e) { + Control.AccessibleRole = AccessibleRole; + } + + + protected virtual void OnEnter(EventArgs e) { + RaiseEvent(EventEnter, e); + } + + + /// + /// + /// called when the control has lost focus + /// + protected virtual void OnGotFocus(EventArgs e) { + RaiseEvent(EventGotFocus, e); + } + + protected virtual void OnLeave(EventArgs e) { + RaiseEvent(EventLeave, e); + } + + /// + /// + /// called when the control has lost focus + /// + protected virtual void OnLostFocus(EventArgs e) { + RaiseEvent(EventLostFocus, e); + } + /// + protected virtual void OnKeyDown(KeyEventArgs e) { + RaiseKeyEvent(EventKeyDown, e); + } + /// + protected virtual void OnKeyPress(KeyPressEventArgs e) { + RaiseKeyPressEvent(EventKeyPress, e); + } + /// + protected virtual void OnKeyUp(KeyEventArgs e) { + RaiseKeyEvent(EventKeyUp, e); + } + /// + /// + /// Called when the items bounds are changed. Here, we update the Control's bounds. + /// + protected override void OnBoundsChanged() { + if (control != null) { + SuspendSizeSync(); + IArrangedElement element = control as IArrangedElement; + if (element == null) { + Debug.Fail("why are we here? control should not be null"); + return; + } + + Size size = LayoutUtils.DeflateRect(this.Bounds, this.Padding).Size; + Rectangle bounds = LayoutUtils.Align(size, this.Bounds, ControlAlign); + + // use BoundsSpecified.None so we dont deal w/specified bounds - this way we can tell what someone has set the size to. + element.SetBounds(bounds, BoundsSpecified.None); + + // sometimes a control can ignore the size passed in, use the adjustment + // to re-align. + if (bounds != control.Bounds) { + bounds = LayoutUtils.Align(control.Size, this.Bounds, ControlAlign); + element.SetBounds(bounds, BoundsSpecified.None); + } + ResumeSizeSync(); + } + } + + /// + /// + /// Called when the control fires its Paint event. + /// + protected override void OnPaint(PaintEventArgs e) { + // do nothing.... + } + + + /// + protected internal override void OnLayout(LayoutEventArgs e) { + // do nothing... called via the controls collection + } + + /// + /// + /// Called when the item's parent has been changed. + /// + protected override void OnParentChanged(ToolStrip oldParent, ToolStrip newParent) { + if (oldParent != null && Owner == null && newParent == null && Control != null) { + // if we've really been removed from the item collection, + // politely remove ourselves from the control collection + WindowsFormsUtils.ReadOnlyControlCollection oldControlCollection + = GetControlCollection(Control.ParentInternal as ToolStrip); + if (oldControlCollection != null) { + oldControlCollection.RemoveInternal(Control); + } + } + else { + SyncControlParent(); + } + + base.OnParentChanged(oldParent, newParent); + } + + + + + /// + /// + /// The events from the hosted control are subscribed here. + /// Override to add/prevent syncing of control events. + /// NOTE: if you override and hook up events here, you should unhook in OnUnsubscribeControlEvents. + /// + protected virtual void OnSubscribeControlEvents(Control control) { + + if (control != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + control.Click += new EventHandler(HandleClick); + control.BackColorChanged += new EventHandler(HandleBackColorChanged); + control.DoubleClick += new EventHandler(HandleDoubleClick); + control.DragDrop += new DragEventHandler(HandleDragDrop); + control.DragEnter += new DragEventHandler(HandleDragEnter); + control.DragLeave += new EventHandler(HandleDragLeave); + control.DragOver += new DragEventHandler(HandleDragOver); + control.Enter += new EventHandler(HandleEnter); + control.EnabledChanged += new EventHandler(HandleEnabledChanged); + control.ForeColorChanged += new EventHandler(HandleForeColorChanged); + control.GiveFeedback += new GiveFeedbackEventHandler(HandleGiveFeedback); + control.GotFocus += new EventHandler(HandleGotFocus); + control.Leave += new EventHandler(HandleLeave); + control.LocationChanged += new EventHandler(HandleLocationChanged); + control.LostFocus += new EventHandler(HandleLostFocus); + control.KeyDown += new KeyEventHandler(HandleKeyDown); + control.KeyPress += new KeyPressEventHandler(HandleKeyPress); + control.KeyUp += new KeyEventHandler(HandleKeyUp); + control.MouseDown += new MouseEventHandler(HandleMouseDown); + control.MouseEnter += new EventHandler(HandleMouseEnter); + control.MouseHover += new EventHandler(HandleMouseHover); + control.MouseLeave += new EventHandler(HandleMouseLeave); + control.MouseMove += new MouseEventHandler(HandleMouseMove); + control.MouseUp += new MouseEventHandler(HandleMouseUp); + control.Paint += new PaintEventHandler(HandlePaint); + control.QueryAccessibilityHelp += new QueryAccessibilityHelpEventHandler(HandleQueryAccessibilityHelp); + control.QueryContinueDrag += new QueryContinueDragEventHandler(HandleQueryContinueDrag); + control.Resize += new EventHandler(HandleResize); + control.RightToLeftChanged += new EventHandler(HandleRightToLeftChanged); + control.TextChanged += new EventHandler(HandleTextChanged); + control.VisibleChanged += new EventHandler(HandleControlVisibleChanged); + control.Validating += new CancelEventHandler(HandleValidating); + control.Validated += new EventHandler(HandleValidated); + + } + + } + + + /// + /// + /// The events from the hosted control are unsubscribed here. + /// Override to unhook events subscribed in OnSubscribeControlEvents. + /// + protected virtual void OnUnsubscribeControlEvents(Control control) { + if (control != null) { + // Please keep this alphabetized and in sync with Subscribe + // + control.Click -= new EventHandler(HandleClick); + control.BackColorChanged -= new EventHandler(HandleBackColorChanged); + control.DoubleClick -= new EventHandler(HandleDoubleClick); + control.DragDrop -= new DragEventHandler(HandleDragDrop); + control.DragEnter -= new DragEventHandler(HandleDragEnter); + control.DragLeave -= new EventHandler(HandleDragLeave); + control.DragOver -= new DragEventHandler(HandleDragOver); + control.Enter -= new EventHandler(HandleEnter); + control.EnabledChanged -= new EventHandler(HandleEnabledChanged); + control.ForeColorChanged -= new EventHandler(HandleForeColorChanged); + control.GiveFeedback -= new GiveFeedbackEventHandler(HandleGiveFeedback); + control.GotFocus -= new EventHandler(HandleGotFocus); + control.Leave -= new EventHandler(HandleLeave); + control.LocationChanged -= new EventHandler(HandleLocationChanged); + control.LostFocus -= new EventHandler(HandleLostFocus); + control.KeyDown -= new KeyEventHandler(HandleKeyDown); + control.KeyPress -= new KeyPressEventHandler(HandleKeyPress); + control.KeyUp -= new KeyEventHandler(HandleKeyUp); + control.MouseDown -= new MouseEventHandler(HandleMouseDown); + control.MouseEnter -= new EventHandler(HandleMouseEnter); + control.MouseHover -= new EventHandler(HandleMouseHover); + control.MouseLeave -= new EventHandler(HandleMouseLeave); + control.MouseMove -= new MouseEventHandler(HandleMouseMove); + control.MouseUp -= new MouseEventHandler(HandleMouseUp); + control.Paint -= new PaintEventHandler(HandlePaint); + control.QueryAccessibilityHelp -= new QueryAccessibilityHelpEventHandler(HandleQueryAccessibilityHelp); + control.QueryContinueDrag -= new QueryContinueDragEventHandler(HandleQueryContinueDrag); + control.Resize -= new EventHandler(HandleResize); + control.RightToLeftChanged -= new EventHandler(HandleRightToLeftChanged); + control.TextChanged -= new EventHandler(HandleTextChanged); + control.VisibleChanged -= new EventHandler(HandleControlVisibleChanged); + control.Validating -= new CancelEventHandler(HandleValidating); + control.Validated -= new EventHandler(HandleValidated); + + } + + } + + protected virtual void OnValidating(CancelEventArgs e) { + RaiseCancelEvent(EventValidating, e); + } + + + protected virtual void OnValidated(EventArgs e) { + RaiseEvent(EventValidated, e); + } + + private static WindowsFormsUtils.ReadOnlyControlCollection GetControlCollection(ToolStrip toolStrip) { + WindowsFormsUtils.ReadOnlyControlCollection newControls = + toolStrip != null ? (WindowsFormsUtils.ReadOnlyControlCollection) toolStrip.Controls : null; + return newControls; + } + + // Ensures the hosted Control is parented to the ToolStrip hosting this ToolStripItem. + private void SyncControlParent() { + WindowsFormsUtils.ReadOnlyControlCollection newControls = GetControlCollection(ParentInternal); + if (newControls != null) { + newControls.AddInternal(Control); + } + } + + /// + protected virtual void OnHostedControlResize(EventArgs e) { + // support for syncing the wrapper when the control size has changed + this.Size = Control.Size; + } + + /// + // SECREVIEW: Technically full trust != unmanaged code, therefore this WndProc is at + // lesser permission that all the other members in the class. Practically speaking though + // they are the same - keeping UnmanagedCode to match the base class. + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + // Control will get this from being in the control collection. + return false; + } + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal override bool ProcessMnemonic(char charCode) { + if (control != null) { + return control.ProcessMnemonic( charCode ); + } + return base.ProcessMnemonic( charCode ); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessDialogKey(Keys keyData) { + // Control will get this from being in the control collection. + return false; + } + + protected override void SetVisibleCore(bool visible) { + // This is needed, because if you try and set set visible to true before the parent is visible, + // we will get called back into here, and set it back to false, since the parent is not visible. + if (inSetVisibleCore) { + return; + } + + inSetVisibleCore = true; + Control.SuspendLayout(); + try { + Control.Visible = visible; + } + finally { + Control.ResumeLayout(false); + // this will go ahead and perform layout. + base.SetVisibleCore(visible); + inSetVisibleCore = false; + } + + + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetBackColor() { + Control.ResetBackColor(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ResetForeColor() { + Control.ResetForeColor(); + } + + private void SuspendSizeSync() { + this.suspendSyncSizeCount++; + } + + private void ResumeSizeSync() { + this.suspendSyncSizeCount--; + } + internal override bool ShouldSerializeBackColor() { + if (control != null) { + return control.ShouldSerializeBackColor(); + } + return base.ShouldSerializeBackColor(); + } + internal override bool ShouldSerializeForeColor() { + if (control != null) { + return control.ShouldSerializeForeColor(); + } + return base.ShouldSerializeForeColor(); + } + + internal override bool ShouldSerializeFont() { + if (control != null) { + return control.ShouldSerializeFont(); + } + return base.ShouldSerializeFont(); + } + + internal override bool ShouldSerializeRightToLeft() { + if (control != null) { + return control.ShouldSerializeRightToLeft(); + } + return base.ShouldSerializeRightToLeft(); + } + + internal override void OnKeyboardToolTipHook(ToolTip toolTip) { + base.OnKeyboardToolTipHook(toolTip); + + KeyboardToolTipStateMachine.Instance.Hook(this.Control, toolTip); + } + + internal override void OnKeyboardToolTipUnhook(ToolTip toolTip) { + base.OnKeyboardToolTipUnhook(toolTip); + + KeyboardToolTipStateMachine.Instance.Unhook(this.Control, toolTip); + } + + // Our implementation of ISite: + // Since the Control which is wrapped by ToolStripControlHost is a runtime instance, there is no way of knowing + // whether the control is in runtime or designtime. + // This implementation of ISite would be set to Control.Site when ToolStripControlHost.Site is set at DesignTime. (Refer to Site property on ToolStripControlHost) + // This implementation just returns the DesigMode property to be ToolStripControlHost's DesignMode property. + // Everything else is pretty much default implementation. + private class StubSite : ISite, IDictionaryService + { + private Hashtable _dictionary = null; + IComponent comp = null; + IComponent owner = null; + + public StubSite(Component control, Component host) + { + comp = control as IComponent; + owner = host as IComponent; + } + // The component sited by this component site. + /// + /// When implemented by a class, gets the component associated with the . + /// + IComponent ISite.Component { + get + { + return comp; + } + } + + // The container in which the component is sited. + /// + /// When implemented by a class, gets the container associated with the . + /// + IContainer ISite.Container { + get { + return owner.Site.Container; + } + } + + // Indicates whether the component is in design mode. + /// + /// When implemented by a class, determines whether the component is in design mode. + /// + bool ISite.DesignMode { + get { + return owner.Site.DesignMode; + } + } + + // The name of the component. + // + /// + /// When implemented by a class, gets or sets the name of + /// the component associated with the . + /// + String ISite.Name { + get { + return owner.Site.Name; + } + set { + owner.Site.Name = value; + } + } + + /// + /// Returns the requested service. + /// + object IServiceProvider.GetService(Type service) { + if (service == null) { + throw new ArgumentNullException("service"); + } + + // We have to implement our own dictionary service. If we don't, + // the properties of the underlying component will end up being + // overwritten by our own properties when GetProperties is called + if (service == typeof(IDictionaryService)) { + return this; + } + + if (owner.Site != null) { + return owner.Site.GetService(service); + } + return null; + } + + /// + /// Retrieves the key corresponding to the given value. + /// + object IDictionaryService.GetKey(object value) { + if (_dictionary != null) { + foreach (DictionaryEntry de in _dictionary) { + object o = de.Value; + if (value != null && value.Equals(o)) { + return de.Key; + } + } + } + return null; + } + + /// + /// Retrieves the value corresponding to the given key. + /// + object IDictionaryService.GetValue(object key) { + if (_dictionary != null) { + return _dictionary[key]; + } + return null; + } + + /// + /// Stores the given key-value pair in an object's site. This key-value + /// pair is stored on a per-object basis, and is a handy place to save + /// additional information about a component. + /// + void IDictionaryService.SetValue(object key, object value) { + if (_dictionary == null) { + _dictionary = new Hashtable(); + } + if (value == null) { + _dictionary.Remove(key); + } else { + _dictionary[key] = value; + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDown.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDown.cs new file mode 100644 index 000000000..bb8e3bc53 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDown.cs @@ -0,0 +1,2337 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms; + using System.Windows.Forms.Layout; + using System.Runtime.InteropServices; + using System.Collections.Specialized; + using System.Collections.Generic; + using System.Globalization; + + + + /// + /// + /// Summary of ToolStripDropDown. + /// + [Designer("System.Windows.Forms.Design.ToolStripDropDownDesigner, " + AssemblyRef.SystemDesign)] + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + public class ToolStripDropDown : ToolStrip { + + private ToolStripItem ownerItem = null; + private bool isAutoGenerated = false; + private Point dropDownLocation = Point.Empty; + private bool dropShadowEnabled = true; + private bool autoClose = true; + private bool autoSize = true; + private int countDropDownItemsAssignedTo = 0; // the number of dropdown items using this as their dropdown.. + private BitVector32 state = new BitVector32(); + private Point displayLocation = new Point(0,0); + private bool saveSourceControl = false; + + private ToolStripDropDownDirection childDropDownDirection = ToolStripDropDownDirection.Default; + private ToolStripDropDownCloseReason closeReason = ToolStripDropDownCloseReason.AppFocusChange; + + + private static readonly int PropOpacity = PropertyStore.CreateKey(); + private static readonly int PropDDRightToLeft = PropertyStore.CreateKey(); + private static readonly int PropSourceControl = PropertyStore.CreateKey(); + + private static readonly object EventOpening = new object(); + private static readonly object EventOpened = new object(); + private static readonly object EventClosed = new object(); + private static readonly object EventClosing = new object(); + + private static readonly object ToolStripParkingWindowKey = new object(); + + private static readonly Padding defaultPadding = new Padding(1, 2, 1, 2); + private Padding scaledDefaultPadding = defaultPadding; + +#if DEBUG + internal static TraceSwitch DropDownActivateDebug = new TraceSwitch("DropDownActivateDebug", "Debug activation code for dropDown controls"); + internal static TraceSwitch DropDownDebugBounds = new TraceSwitch("DropDownDebugBounds", "Debug GetDropDownBounds"); + static readonly BooleanSwitch AlwaysRestrictWindows = new BooleanSwitch("AlwaysRestrictWindows", "Always make Form classes behave as though they are restricted"); +#else + internal static TraceSwitch DropDownActivateDebug; + internal static TraceSwitch DropDownDebugBounds; +#endif + + private static readonly int stateLayered = BitVector32.CreateMask(); + private static readonly int stateAllowTransparency = BitVector32.CreateMask(stateLayered); + private static readonly int stateIsRestrictedWindow = BitVector32.CreateMask(stateAllowTransparency); + private static readonly int stateIsRestrictedWindowChecked = BitVector32.CreateMask(stateIsRestrictedWindow); + private static readonly int stateNotWorkingAreaConstrained = BitVector32.CreateMask(stateIsRestrictedWindowChecked); + private static readonly int stateInSetVisibleCore = BitVector32.CreateMask(stateNotWorkingAreaConstrained); + + + + /// SECREVIEW + /// ToolStripDropDown security notes + /// Key ideas: + /// AllWindowsPermission is required to get keystrokes, obscure the taskbar, prevent the dropdown from closing. + /// + /// A. Disallow ControlHost items, both shipped and user created via UiPermission.AWP. We'd simply not display these items in the internet / dropdown case. + /// B. Disallow AutoClose == false and ignore cancelable closing event param. This makes it impossible to enter more than one keystroke and not cause dropdown dismissal. + /// C. Restrict keystroke handling events. We would route WM_KKEY* to DefWndProc, and because Control.WndProc would be skipped, the following events would not be raised: + /// On/KeyUp/On/KeyDown/On/KeyPress + /// D. Ensure proper link and inheritancedemands on + /// ProcessKeyPreview/ProcessDialogKey/ProcessCmdKey/ProcessKeyMessage/PreProcessMessage/WndProc + /// E. Ensure proper inheritance demands on + /// IsInputKey/IsInputChar + /// F. Prevent double mnemonic evaluation on DropDowns (another way of getting keyboard input by having hidden items) + /// Protect ProcessMnemonic. + + + + /// + /// + /// Summary of ToolStripDropDown. + /// + public ToolStripDropDown() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding); + } + + // SECURITY NOTE: The IsRestrictedWindow check is done once and cached. We force it to happen here + // since we want to ensure the check is done on the code that constructs the ToolStripDropDown. + bool temp = IsRestrictedWindow; + + this.SuspendLayout(); + Initialize(); + this.ResumeLayout(false); + } + + /// + /// + /// Summary of ToolStripDropDown. + /// + /// + internal ToolStripDropDown(ToolStripItem ownerItem): this() { + this.ownerItem = ownerItem; + } + internal ToolStripDropDown(ToolStripItem ownerItem, bool isAutoGenerated) : this(ownerItem) { + this.isAutoGenerated = isAutoGenerated; + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool AllowItemReorder { + get { + return base.AllowItemReorder; + } + set { + base.AllowItemReorder = value; + } + } + + /// + /// + /// + /// + /// Gets or sets + /// a value indicating whether the opacity of the form can be + /// adjusted. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlAllowTransparencyDescr) + ] + public bool AllowTransparency { + get { + return state[stateAllowTransparency]; + } + set { + if (value != (state[stateAllowTransparency]) && + OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + state[stateAllowTransparency] = value; + + state[stateLayered] = state[stateAllowTransparency]; + + UpdateStyles(); + + if (!value) { + if (Properties.ContainsObject(PropOpacity)) { + Properties.SetObject(PropOpacity, (object)1.0f); + } + UpdateLayered(); + } + } + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override AnchorStyles Anchor { + get { + return base.Anchor; + } + set { + base.Anchor = value; + } + } + + /// + [DefaultValue(true)] + public override bool AutoSize { + get { + return autoSize; + } + set { + if (autoSize != value) { + autoSize = value; + // we shadow CommonProperties + LayoutTransaction.DoLayout(this,this,PropertyNames.AutoSize); + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// specifies whether the dropdown should automatically close when the dropdown has lost + /// activation. If you want a dropdown that always stays open, specify AutoClose = false; + /// + [ + DefaultValue(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripDropDownAutoCloseDescr) + ] + public bool AutoClose { + get { + return autoClose; + } + set { + // SECREVIEW - to be honored, you must have AllWindowsPermission. See SetVisibleCore + if (autoClose != value) { + autoClose = value; + ApplyTopMost(/*topMost=*/!autoClose); + } + } + } + + + [Browsable(false)] + public new event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + [Browsable(false)] + public new event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + [Browsable(false)] + public new event EventHandler BindingContextChanged { + add { + base.BindingContextChanged += value; + } + remove { + base.BindingContextChanged -= value; + } + } + + /// + [DefaultValue(false), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never)] + public new bool CanOverflow { + get { + return base.CanOverflow; + } + set { + base.CanOverflow = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event UICuesEventHandler ChangeUICues { + add { base.ChangeUICues += value; } + remove { base.ChangeUICues -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new ContextMenu ContextMenu { + get { return base.ContextMenu; } + set { base.ContextMenu = value; } + } + + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ContextMenuChanged { + add { base.ContextMenuChanged += value; } + remove { base.ContextMenuChanged -= value; } + } + + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new ContextMenuStrip ContextMenuStrip { + get { return base.ContextMenuStrip; } + set { base.ContextMenuStrip = value; } + } + + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler ContextMenuStripChanged { + add { base.ContextMenuStripChanged += value; } + remove { base.ContextMenuStripChanged -= value; } + } + + /// + /// + /// + /// + /// This is called when creating a window. Inheriting classes can overide + /// this to add extra functionality, but should not forget to first call + /// base.CreateParams() to make sure the control continues to work + /// correctly. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + // If we're using themes then go ahead + if (DropShadowEnabled) { + cp.ClassStyle |= NativeMethods.CS_DROPSHADOW; + } + // we're a borderless menuless control with no min/max boxes + // we dont want to show in the taskbar either + + //Q205158 HOWTO: Prevent a Window from Appearing on the Taskbar + //Give the window the WS_EX_TOOLWINDOW extended style, and remove the WS_EX_APPWINDOW style. As a side effect, the window will have a smaller caption than a normal window. + //Give the window the WS_POPUP style and make it owned by a hidden window. (Form) + + cp.Style &= ~(NativeMethods.WS_CAPTION | NativeMethods.WS_CLIPSIBLINGS); /* no caption, no siblings */ + cp.ExStyle &= ~(NativeMethods.WS_EX_APPWINDOW); /* show in taskbar = false */ + // | NativeMethods.WS_EX_TOOLWINDOW + cp.Style |= (TopLevel) ? NativeMethods.WS_POPUP : NativeMethods.WS_CHILD; + cp.ExStyle |= (NativeMethods.WS_EX_CONTROLPARENT); /* show in taskbar = false */ + + bool topLevel = TopLevel; + + // opacity + if (topLevel && (state[stateLayered])) { + cp.ExStyle |= NativeMethods.WS_EX_LAYERED; + } + else if (topLevel) { + //From MSDN: Menus, dialog boxes, and combo list boxes have the CS_SAVEBITS style. When you use this style for a window, + //Windows saves a bitmap copy of the screen image that the window obscures. First, Windows asks the display driver to save the bits. + //If the display driver has enough memory, it saves the bits for Windows. If the display driver does not have enough memory, Window + //saves the bits itself as a bitmap in global memory and also uses some of User's local heap for housekeeping structures for each window. + //When the application removes the window, Windows can restore the screen image quickly by using the stored bits. + cp.ClassStyle |= NativeMethods.CS_SAVEBITS; + } + else if (!topLevel) { + cp.Style |= NativeMethods.WS_CLIPSIBLINGS; + } + + // We're turning off CLIPSIBLINGS because in the designer the elements of the form beneath + // are actually sibling controls. We want to paint right over them as if we were a toplevel window. + + return cp; + } + } + + /// + protected override Padding DefaultPadding { + get { return scaledDefaultPadding; } + } + + + /// We want this to default to true... This way tooltips on overflows and custom dropdowns will show. + /// Since menu items don't show tooltips by default we can savely leave it on + protected override bool DefaultShowItemToolTips { + get { + return true; + } + } + + protected override DockStyle DefaultDock { + get { + return DockStyle.None; + } + } + + public override ToolStripDropDownDirection DefaultDropDownDirection { + get { + if (childDropDownDirection == ToolStripDropDownDirection.Default) { + return (RightToLeft == RightToLeft.Yes)? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + } + return childDropDownDirection; + } + set { + childDropDownDirection = value; + base.DefaultDropDownDirection = value; + } + } + + /// + [ + DefaultValue(DockStyle.None), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Always)] + public override DockStyle Dock { + get { + return base.Dock; + } + set{ + base.Dock = value; + } + } + + /// changed the browsable attribute + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler DockChanged { + add { + base.DockChanged += value; + } + remove { + base.DockChanged -= value; + } + } + + /// + /// Returns an owner window that can be used to + /// own a drop down. + /// + internal override NativeWindow DropDownOwnerWindow { + get { + // Re-use the drop down owner from our parenting + // tool strip if we can. + if (ownerItem != null && ownerItem.Owner != null) { + return ownerItem.Owner.DropDownOwnerWindow; + } + + return base.DropDownOwnerWindow; + } + } + + /// + public bool DropShadowEnabled { + get { + // VSWhidbey 338272 - DropShadows are only supported on TopMost windows + // due to the flakeyness of the way it's implemented in the OS. (Non toplevel + // windows can have parts of the shadow disappear because another window can get + // sandwiched between the SysShadow window and the dropdown.) + return dropShadowEnabled && TopMost && DisplayInformation.IsDropShadowEnabled; + } + set { + if (dropShadowEnabled != value) { + dropShadowEnabled = value; + if (IsHandleCreated && !DesignMode) { + RecreateHandle(); + } + } + } + } + + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripDropDownClosedDecr)] + public event ToolStripDropDownClosedEventHandler Closed { + add { + Events.AddHandler(EventClosed, value); + } + remove { + Events.RemoveHandler(EventClosed, value); + } + } + + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripDropDownClosingDecr)] + public event ToolStripDropDownClosingEventHandler Closing { + add { + Events.AddHandler(EventClosing, value); + } + remove { + Events.RemoveHandler(EventClosing, value); + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler Enter { + add { base.Enter += value; } + remove { base.Enter -= value; } + } + + public override Font Font { + get { + if (this.IsFontSet()) { + return base.Font; + } + // if the FONT isnt set, then return our owner item's font. + if (IsAutoGenerated && OwnerItem != null) { + return OwnerItem.Font; + } + + return base.Font; + } + set { + base.Font = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler FontChanged { + add { base.FontChanged += value; } + remove { base.FontChanged -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { base.ForeColorChanged += value; } + remove { base.ForeColorChanged -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event GiveFeedbackEventHandler GiveFeedback { + add { base.GiveFeedback += value; } + remove { base.GiveFeedback -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new ToolStripGripDisplayStyle GripDisplayStyle { + get { + return base.GripDisplayStyle; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new Rectangle GripRectangle { + get { + return base.GripRectangle; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Padding GripMargin{ + get { + return base.GripMargin; + } + set { + base.GripMargin = value; + } + } + + /// + [DefaultValue(ToolStripGripStyle.Hidden), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never)] + public new ToolStripGripStyle GripStyle { + get { + return base.GripStyle; + } + set { + base.GripStyle = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + [SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")] //Everett + new public event HelpEventHandler HelpRequested { + add { base.HelpRequested += value; } + remove { base.HelpRequested -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler ImeModeChanged { + add { base.ImeModeChanged += value; } + remove { base.ImeModeChanged -= value; } + } + + /// + /// + /// determines whether this dropdown was autogenerated. + /// + [Browsable(false)] + public bool IsAutoGenerated { + get { + return isAutoGenerated; + } + } + + internal bool IsAssignedToDropDownItem { + get { + return IsAutoGenerated || (countDropDownItemsAssignedTo > 0); + } + } + + /// + /// + /// + /// Determines if this form should display a warning banner + /// when the form is displayed in an unsecure mode. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + internal bool IsRestrictedWindow { + get { + /// SECREVIEW: make sure to keep changes here in sync with Form.IsRestrictedWindow + if (!state[stateIsRestrictedWindowChecked]) { + state[stateIsRestrictedWindowChecked] = true; + state[stateIsRestrictedWindow] = false; + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Checking for restricted window..."); + Debug.Indent(); +#if DEBUG + if (AlwaysRestrictWindows.Enabled) { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Always restricted switch is on..."); + state[stateIsRestrictedWindow] = true; + Debug.Unindent(); + return true; + } +#endif + + try { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "WindowAdornmentModification Demanded"); + IntSecurity.WindowAdornmentModification.Demand(); + } + catch { + Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "Caught exception, we are restricted..."); + state[stateIsRestrictedWindow] = true; + } + Debug.Unindent(); + } + + return state[stateIsRestrictedWindow]; + } + } + + + internal override Size ImageScalingSizeInternal { + get { + if (IsAutoGenerated && OwnerToolStrip != null) { + return OwnerToolStrip.ImageScalingSizeInternal; + } + return base.ImageScalingSizeInternal; + } + set { + base.ImageScalingSizeInternal = value; + } + } + + internal override bool KeyboardActive { + get { + // ripple up the chain until we get the topmost toolstrip (usually the main menu strip is the one we care about) + ToolStrip ownerToolStrip = OwnerToolStrip; + if (ownerToolStrip != null) { + return ownerToolStrip.KeyboardActive; + } + return base.KeyboardActive; + } + set { + base.KeyboardActive = value; + + // ripple up the chain until we get the topmost toolstrip (usually the main menu strip is the one we care about) + ToolStrip ownerToolStrip = OwnerToolStrip; + if (ownerToolStrip != null) { + ownerToolStrip.KeyboardActive = value; + } + + + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event KeyEventHandler KeyDown { + add { base.KeyDown += value; } + remove { base.KeyDown -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event KeyPressEventHandler KeyPress { + add { base.KeyPress += value; } + remove { base.KeyPress -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event KeyEventHandler KeyUp { + add { base.KeyUp += value; } + remove { base.KeyUp -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler Leave { + add { base.Leave += value; } + remove { base.Leave -= value; } + } + + /// + /// Override Location to make it hidden from the user in the designer + /// + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new Point Location { + get { + return base.Location; + } + set { + base.Location = value; + } + } + + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripDropDownOpeningDescr)] + public event CancelEventHandler Opening { + add { + Events.AddHandler(EventOpening, value); + } + remove { + Events.RemoveHandler(EventOpening, value); + } + } + + + /// + /// + /// Occurs when the control is clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ToolStripDropDownOpenedDescr)] + public event EventHandler Opened { + add { + Events.AddHandler(EventOpened, value); + } + remove { + Events.RemoveHandler(EventOpened, value); + } + } + + /// + protected internal override Size MaxItemSize { + get { + return Screen.GetWorkingArea(this.Bounds).Size - this.Padding.Size; + } + } + + /// + /// + /// Determines the opacity of the form. This can only be set on top level + /// controls. Opacity requires Windows 2000 or later, and is ignored on earlier + /// operating systems. + /// + [ + SRCategory(SR.CatWindowStyle), + TypeConverterAttribute(typeof(OpacityConverter)), + SRDescription(SR.FormOpacityDescr), + DefaultValue(1.0), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) + ] + public double Opacity { + get { + object opacity = Properties.GetObject(PropOpacity); + if (opacity != null) { + return Convert.ToDouble(opacity, CultureInfo.InvariantCulture); + } + else { + return 1.0f; + } + } + set { + if (IsRestrictedWindow) { + value = Math.Max(value, .50f); + } + + if (value > 1.0) { + value = 1.0f; + } + else if (value < 0.0) { + value = 0.0f; + } + + Properties.SetObject(PropOpacity, value); + + bool oldLayered = (state[stateLayered]); + + if (OpacityAsByte < 255 && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) + { + AllowTransparency = true; + state[stateLayered] = true; + } + else + { + state[stateLayered] = false; + } + + if (oldLayered != (state[stateLayered])) + { + UpdateStyles(); + } + UpdateLayered(); + } + } + + private byte OpacityAsByte { + get { + return (byte)(Opacity * 255.0f); + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new ToolStripOverflowButton OverflowButton { + get { + return base.OverflowButton; + } + } + + + /// + /// + /// Summary of OwnerItem. + /// + [DefaultValue(null), Browsable(false)] + public ToolStripItem OwnerItem { + get { return ownerItem; } + set { + if (ownerItem != value) { + Font originalFont = this.Font; + RightToLeft startRightToLeft = this.RightToLeft; + ownerItem = value; + // RESET ambient properties + if (!originalFont.Equals(Font)) { + OnOwnerItemFontChanged(EventArgs.Empty); + } + if (ownerItem != null && RightToLeftInherited && startRightToLeft != this.RightToLeft) { + using (new LayoutTransaction(this,this,PropertyNames.RightToLeft)) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + } + } + + internal ToolStripDropDownItem OwnerDropDownItem { + get { return OwnerItem as ToolStripDropDownItem; } + } + + internal ToolStrip OwnerToolStrip { + get { + if (ownerItem != null) { + ToolStrip owner = ownerItem.ParentInternal; + if (owner != null) { + return owner; + } + + // might not actually be placed on the overflow, just check for sure. + if (ownerItem.Placement == ToolStripItemPlacement.Overflow && ownerItem.Owner != null) { + return ownerItem.Owner.OverflowButton.DropDown; + } + if (owner == null) { + return ownerItem.Owner; + } + } + return null; + } + } + + /// + /// + /// Hide the Region Property + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public Region Region { + get { + return base.Region; + } + set { + base.Region = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler RegionChanged { + add { base.RegionChanged += value; } + remove { base.RegionChanged -= value; } + } + + + internal virtual bool RequiresScrollButtons { + get { + return false; + } + set { + Debug.Fail("You can't set this except on ToolStripDropDownMenu"); + } + } + + + [ + SRCategory(SR.CatAppearance), + Localizable(true), + AmbientValue(RightToLeft.Inherit), + SRDescription(SR.ControlRightToLeftDescr) + ] + public override RightToLeft RightToLeft { + get { + // our inheritance is from our owner item. + if (RightToLeftInherited) { + if (SourceControlInternal != null) { + return SourceControlInternal.RightToLeft; + } + if (OwnerItem != null) { + return OwnerItem.RightToLeft; + } + } + return base.RightToLeft; + } + set { + base.RightToLeft = value; + } + } + + private bool RightToLeftInherited { + get { + // fish out of control's property store whether or not RTL was set, if it's set to inherit. + return !ShouldSerializeRightToLeft(); + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event ScrollEventHandler Scroll { + add { base.Scroll += value; } + remove { base.Scroll -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new bool Stretch { + get { + return base.Stretch; + } + set { + base.Stretch = value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler StyleChanged { + add { base.StyleChanged += value; } + remove { base.StyleChanged -= value; } + } + + + internal Control SourceControlInternal { + get { + return Properties.GetObject(PropSourceControl) as Control; + } + set{ + Properties.SetObject(PropSourceControl, value); + } + } + + internal override int ShowParams { + get { + return NativeMethods.SW_SHOWNOACTIVATE; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TabStopChanged { + add { base.TabStopChanged += value; } + remove { base.TabStopChanged -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { base.TextChanged += value; } + remove { base.TextChanged -= value; } + } + + [ + Browsable(false), + DefaultValue(ToolStripTextDirection.Horizontal), + SRDescription(SR.ToolStripTextDirectionDescr), + SRCategory(SR.CatAppearance) + ] + public override ToolStripTextDirection TextDirection { + get { + return base.TextDirection; + } + set { + base.TextDirection = value; + } + } + + + //Consistency: match casing of Form.TopMost, which shipped in Everett, even though it's wrong + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + protected virtual bool TopMost + { + get { return true; } + } + + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool TopLevel { + get { + return GetTopLevel(); + } + set { + // + if (value != GetTopLevel()) { + SetTopLevelInternal(value); + SetTopLevelStyle(value); + } + } + } + + +// public Color TransparencyKey { +// This property intentionally not available for ToolStripDropDown +// it's not robust enough for our needs +// } + + /// + /// VSWhidbey 233498: override base TabIndex property in order to avoid serialization + /// (since a dropdown shouldn't participate in the taborder...) + [ + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false) + ] + public new int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ControlOnTabIndexChangedDescr) + ] + public new event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler Validated { + add { base.Validated += value; } + remove { base.Validated -= value; } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event CancelEventHandler Validating { + add { base.Validating += value; } + remove { base.Validating -= value; } + } + + /// + /// VSWhidbey 233498: override base Visible property in order to control serialization by setting default value + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ControlVisibleDescr), + DefaultValue(false), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool Visible { + get { + return base.Visible; + } + set { + base.Visible = value; + } + } + + // internally we use not so we dont have to initialize it. + internal bool WorkingAreaConstrained { + get { + if (state[stateNotWorkingAreaConstrained]) { + // SECREVIEW: + // We are constrained to the working area when we dont have AllWindows permission + return IsRestrictedWindow; + } + return true; + } + set { + bool notConstrained = !value; + state[stateNotWorkingAreaConstrained] = notConstrained; + } + } + + internal void AssignToDropDownItem() { + countDropDownItemsAssignedTo++; + } + + internal void AdjustSize() { + Size size = GetSuggestedSize(); + if (size != this.Size) { + this.Size = size; + } + } + + private void ApplyTopMost(bool topMost) { + if (TopMost) { + HandleRef topMostFlag = (topMost) ? NativeMethods.HWND_TOPMOST : NativeMethods.HWND_NOTOPMOST; + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), topMostFlag, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOACTIVATE); + } + } + + /// + protected override void Dispose(bool disposing) { + if (disposing) { + SourceControlInternal = null; + } + base.Dispose(disposing); + } + + + private void CancelAutoExpand() { + ToolStrip toplevelOwnerToolStrip = GetToplevelOwnerToolStrip(); + if (toplevelOwnerToolStrip != null) { + toplevelOwnerToolStrip.MenuAutoExpand = false; + } + } + + internal override bool CanProcessMnemonic() { + // VSWhidbey 515812: Dont let mnemonics act as keyboard input in IE in the internet. + if (IsRestrictedWindow && !Application.MessageLoop) { + return false; + } + return base.CanProcessMnemonic(); + } + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripDropDownAccessibleObject(this); + } + + protected override LayoutSettings CreateLayoutSettings(ToolStripLayoutStyle style) { + LayoutSettings layoutSettings = base.CreateLayoutSettings(style); + + if (style == ToolStripLayoutStyle.Flow) { + FlowLayoutSettings flowLayoutSettings = layoutSettings as FlowLayoutSettings; + flowLayoutSettings.FlowDirection = FlowDirection.TopDown; + flowLayoutSettings.WrapContents = false; + return flowLayoutSettings; + } + return layoutSettings; + } + + protected override void CreateHandle() { + base.CreateHandle(); + + if (TopLevel) { + ReparentToDropDownOwnerWindow(); + + if (!AutoClose || !WorkingAreaConstrained) { + ApplyTopMost(true); + } + + } + if (DesignMode) { + SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, false); + } + + } + + public void Close() { + SetCloseReason(ToolStripDropDownCloseReason.CloseCalled); + this.Visible = false; + // we were the last one in the chain, roll out of menu mode. + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == null) { + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + } + + private void ResetCloseReason() { + closeReason = ToolStripDropDownCloseReason.AppFocusChange; + } + internal void SetCloseReason(ToolStripDropDownCloseReason reason) { + closeReason = reason; + } + + + public void Close(ToolStripDropDownCloseReason reason) { + SetCloseReason(reason); + this.Visible = false; + } + + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + Rectangle adjustedBounds = base.ApplyBoundsConstraints(suggestedX, suggestedY, proposedWidth, proposedHeight); + + // SECREVIEW VSWhidbey 430541 - place security size restrictions in non public/protected function + // to prevent custom IL from skipping restrictions in SetBoundsCore. + if (IsRestrictedWindow && TopMost) { + adjustedBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(adjustedBounds); + } + return adjustedBounds; + } + /// + /// Summary of GetBaseDropDownBounds. + /// + // called by ToolStripDropDownMenu, + internal Rectangle GetDropDownBounds(Rectangle suggestedBounds) { + + Rectangle dropDownBounds; + if (TopLevel) { + Point screenPoint = Point.Empty; + + if ((ownerItem != null) && (ownerItem is ToolStripDropDownItem)) { + screenPoint = ((ToolStripDropDownItem)ownerItem).DropDownLocation; + } + else { + screenPoint = suggestedBounds.Location; + } + + Rectangle suggestedScreenBounds = new Rectangle(screenPoint, suggestedBounds.Size); + if (WorkingAreaConstrained) { + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(suggestedScreenBounds); + } + else { + dropDownBounds = WindowsFormsUtils.ConstrainToScreenBounds(suggestedScreenBounds); + } + } + else { + Point parentClientPoint = Point.Empty; + if ((ownerItem != null) && (ownerItem is ToolStripDropDownItem) && (ParentInternal != null)) { + parentClientPoint = ParentInternal.PointToClient(((ToolStripDropDownItem)ownerItem).DropDownLocation); + } + else { + parentClientPoint = suggestedBounds.Location; + } + dropDownBounds = new Rectangle(parentClientPoint, suggestedBounds.Size); + + } + Debug.WriteLineIf(DropDownDebugBounds.TraceVerbose, "DropDownBounds for " + suggestedBounds + "is" +dropDownBounds); + + return dropDownBounds; + + } + + internal Rectangle CalculateDropDownLocation(Point start, ToolStripDropDownDirection dropDownDirection) { + Point offset = Point.Empty; + if (!IsHandleCreated) { + // PERF: + // if the handle isnt created yet, then we likely havent performed layout + // yet. force a layout here so that we get the correct size. + LayoutTransaction.DoLayout(this, this, PropertyNames.PreferredSize); + } + Rectangle dropDownBounds = new Rectangle(Point.Empty, GetSuggestedSize()); + // calculate the offset from the upper left hand corner of the item. + switch (dropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + offset.X = - dropDownBounds.Width; + offset.Y = - dropDownBounds.Height; + break; + case ToolStripDropDownDirection.AboveRight: + offset.Y = - dropDownBounds.Height; + break; + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.Right: + break; + case ToolStripDropDownDirection.BelowLeft: + case ToolStripDropDownDirection.Left: + offset.X = - dropDownBounds.Width; + break; + } + dropDownBounds.Location = new Point(start.X + offset.X, start.Y + offset.Y); + if (WorkingAreaConstrained) { + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds); + } + return dropDownBounds; + + } + + internal Size GetSuggestedSize() { + if (AutoSize) { + return GetPreferredSize(Size.Empty); + } + return this.Size; + } + + /// + /// Returns the ToolStrip from which all the dropdowns started from. This can be null. + /// + internal override ToolStrip GetToplevelOwnerToolStrip() { + ToolStripDropDown topmost = GetFirstDropDown(); + return (topmost.OwnerItem == null) ? null : topmost.OwnerToolStrip; + } + + internal ToolStripItem GetToplevelOwnerItem() { + ToolStripDropDown topmost = GetFirstDropDown(); + return topmost.OwnerItem; + } + + internal override void HandleItemClicked(ToolStripItem dismissingItem) { + // Only clear the SourceControl if this is the last click. + if (!LocalAppContextSwitches.UseLegacyContextMenuStripSourceControlValue && + this.ActiveDropDowns.Count == 0) { + // post processing after the click has happened. + SourceControlInternal = null; + } + base.HandleItemClicked(dismissingItem); + } + /// + /// Set some common properties + /// + internal virtual void Initialize() { + SetState(STATE_VISIBLE, false); + SetTopLevelInternal(true); + + // VSWhidbey 82103 -- marking this as a modal form prevents it from being activated + // by the IMsoComponentManager, which will break keyboard routing in VS. + // + SetState(STATE_MODAL, true); + + SetStyle(ControlStyles.ResizeRedraw, true); + this.UpdateStyles(); + this.GripStyle = ToolStripGripStyle.Hidden; + this.CanOverflow = false; + this.LayoutStyle = ToolStripLayoutStyle.Flow; + this.MenuAutoExpand = true; + this.AutoSize = true; + } + + + /// + /// + /// Summary of OnLayout. + /// + /// + protected virtual void OnClosed(ToolStripDropDownClosedEventArgs e){ + if (IsHandleCreated) { + if (ownerItem == null || !ownerItem.IsInDesignMode) + { + AccessibilityNotifyClients(AccessibleEvents.SystemMenuPopupEnd, -1); + } + } + ToolStripDropDownClosedEventHandler handler = (ToolStripDropDownClosedEventHandler)Events[EventClosed]; + if (handler != null) handler(this, e); + } + + // SECREVIEW - for the "Cancel" to be honored, you must have AllWindowsPermission + // i.e. ToolStripDropDown.IsRestrictedWindow MUST be false. + // for more details see ToolStripDropDown.SetVisibleCore + protected virtual void OnClosing(ToolStripDropDownClosingEventArgs e) { + ToolStripDropDownClosingEventHandler handler = (ToolStripDropDownClosingEventHandler)Events[EventClosing]; + if (handler != null) handler(this, e); + } + /// + /// + /// When our handle is being created, suspend the deactivation + /// portion of the WndProc, as we'll never be shown. + /// + protected override void OnHandleCreated(EventArgs e) { + UpdateStyles(); // get rid of WS_CAPTION style + base.OnHandleCreated(e); + UpdateLayered(); // update transparency + } + + + + protected override void OnItemClicked(ToolStripItemClickedEventArgs e) { + try { + base.OnItemClicked(e); + } + finally { + ToolStripDropDownItem dismissingItem = e.ClickedItem as ToolStripDropDownItem; + if (AutoClose) { + if ((dismissingItem == null) // it's not a dropdownitem + || (dismissingItem is ToolStripSplitButton && !dismissingItem.DropDown.Visible) // clicking on the split button button dismisses + || !(dismissingItem.HasDropDownItems)) { // clicking on a item w/dropdown does not dismiss window + Close(ToolStripDropDownCloseReason.ItemClicked); + } + } + } + + } + + + /// + protected override void OnLayout(LayoutEventArgs e) { + // VSWhidbey 480533 it's important to size the dropdown first, then layout so that + // the layout engine and SetDisplayedItems know how big the container is. + AdjustSize(); + base.OnLayout(e); + + } + + protected virtual void OnOpening(CancelEventArgs e){ + CancelEventHandler handler = (CancelEventHandler)Events[EventOpening]; + if (handler != null) handler(this, e); + } + + + /// + /// + /// Summary of OnLayout. + /// + /// + protected virtual void OnOpened(System.EventArgs e) { + if (IsHandleCreated) { + if (ownerItem == null || !ownerItem.IsInDesignMode) + { + AccessibilityNotifyClients(AccessibleEvents.SystemMenuPopupStart, -1); + } + } + + EventHandler handler = (EventHandler)Events[EventOpened]; + if (handler != null) handler(this, e); + } + + protected override void OnVisibleChanged(System.EventArgs e) { + if (this.Location != this.displayLocation) { + // If we adjusted the position from where the user wanted it, + // see if we can put it in the right location now that they've changed + // the items collection, and store where we actually have it. + + // Just because this is the case doesn't mean that we need to do another + // another layout however. + this.Location = this.displayLocation; + this.displayLocation = this.Location; + } + if (AutoScroll || this.LayoutRequired) { + // the base here forces a layout... we really only want to do this the first + // time we pop the window... the subsequent times should be snappy. + base.OnVisibleChanged(e); + } + else { + this.SuspendLayout(); + try { + // scrollable control forces a layout here for scrollbar reasons only + // since we toggle visibility a lot this is expensive. Let's be clever and + // not do it. + base.OnVisibleChanged(e); + } + finally { + this.ResumeLayout(false); + } + } + } + + /// + protected override void OnParentChanged(System.EventArgs e) { + base.OnParentChanged(e); + Rectangle bounds = this.Bounds; + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, BoundsSpecified.Location); + } + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) { + base.OnMouseUp(mea); + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown.OnMouseUp] mouse up outside of the toolstrip - this should dismiss the entire chain"); + + //VSWhidbey 384068 Menus should dismiss when you drag off + if (!ClientRectangle.Contains(mea.Location)) { + bool dismiss = true; + if (OwnerToolStrip != null && OwnerItem != null) { + if (OwnerItem.Bounds.Contains(WindowsFormsUtils.TranslatePoint(mea.Location, this, OwnerToolStrip))) { + dismiss = false; // dont dismiss if we clicked on our owner item + } + } + if (dismiss) { + DismissAll(); + CancelAutoExpand(); + } + } + } + + internal void OnOwnerItemFontChanged(EventArgs e) { + if (this.IsAutoGenerated) { + using (new LayoutTransaction(this, OwnerItem, PropertyNames.Font)) { + OnFontChanged(e); + } + } + } + + internal void SelectPreviousToolStrip() { + // snap the owner item before calling hide as non-auto created dropdowns will + // exit menu mode if there's no OwnerItem. + ToolStripItem itemOnPreviousMenuToSelect = this.OwnerItem; + this.Hide(); + + if (itemOnPreviousMenuToSelect != null) { + itemOnPreviousMenuToSelect.Select(); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(itemOnPreviousMenuToSelect); + } + + if (OwnerToolStrip != null) { + // make sure we send keyboard handling where we've just + // sent selection + if (!OwnerToolStrip.IsDropDown) { + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != OwnerToolStrip) { + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(OwnerToolStrip); + } + // escape should cancel auto expansion + OwnerToolStrip.MenuAutoExpand = false; + // When the control cannot be select (TabStop), we can press "Tab" to + // navigate inside the owner toolstip. Otherwise, press "Tab" will leave + // the owner toolstrip so it should exit the menu mode. + if (OwnerToolStrip.CanSelect) { + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + } + } + } + else { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown.SelectPreviousToolStrip] No previous toolstrip to select - exiting menu mode."); + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + + } + + + /// + /// this is where we handle navigation between the File,Edit,View dropdowns + /// if you have one of these dropdowns showing and you hit the arrow key + /// and it's not processed by the menu item + /// + internal override bool ProcessArrowKey(Keys keyCode) { + + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripDropDown.ProcessArrowKey] MenuTimer.Cancel called"); + + ToolStripMenuItem.MenuTimer.Cancel(); + + if (keyCode == Keys.Left || keyCode == Keys.Right) { + + bool rightAligned = SystemInformation.RightAlignedMenus; + bool forward = (keyCode == Keys.Left && rightAligned) || (keyCode == Keys.Right && !rightAligned); + + if (!IsFirstDropDown && !forward) { + // this is the case where you've casecaded out to a second level dropdown and you hit the back arrow + // key. In this case we want to just hide the current dropdown + this.Visible = false; + return true; + } + else { + bool closeOnHorizontalKey = false; + if (LayoutStyle == ToolStripLayoutStyle.Flow) { + closeOnHorizontalKey = FlowLayout.GetFlowDirection(this) == FlowDirection.TopDown && !FlowLayout.GetWrapContents(this); + } + + if (closeOnHorizontalKey) { + + ToolStrip toplevelToolStrip = GetToplevelOwnerToolStrip(); + ToolStripItem rootItem = GetToplevelOwnerItem(); + + // This is the case where you need to open up the adjacent DropDown (File->Edit) menus because: + // - this is the toplevel ToolStripDropDown and you hit left or right + // - this is a non-toplevel ToolStripDropDown and you hit an arrow key in a direction + // of the cascade AND the current item has no cascading menu associated with it. + + bool isOnOverflow = (OwnerItem != null && OwnerItem.IsOnOverflow); + if (forward || !isOnOverflow) { + SetCloseReason(ToolStripDropDownCloseReason.Keyboard); + DismissAll(); + } + else if (isOnOverflow) { + // VSWhidbey 478068: going backwards should roll us up and our children but not the overflow. + Visible = false; + } + + if (toplevelToolStrip != null && rootItem != null) { + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != toplevelToolStrip) { + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(toplevelToolStrip); + } + toplevelToolStrip.SelectNextToolStripItem(rootItem,forward); + } + return true; + } + } + } + // get base behavior like up/down navigation. + return base.ProcessArrowKey(keyCode); + } + + + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) { + if (this.OwnerItem != null && this.OwnerItem.IsInDesignMode) { + return false; + } + + if (AutoClose && Visible) { + if (ToolStripManager.IsMenuKey(keyData)) { + SetCloseReason(ToolStripDropDownCloseReason.Keyboard); + DismissAll(); + ToolStrip toplevel = GetToplevelOwnerToolStrip(); + if (toplevel != null) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown ProcessDialogKey]: Got Menu Key, finding toplevel toolstrip, calling RestoreFocus."); + toplevel.RestoreFocusInternal(); + ToolStripManager.ModalMenuFilter.MenuKeyToggle = true; + } + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + return true; + } + else if ((keyData & Keys.KeyCode) == Keys.Escape) { + SetCloseReason(ToolStripDropDownCloseReason.Keyboard); + SelectPreviousToolStrip(); + return true; + + } + } + return base.ProcessDialogKey(keyData); + } + + + + [EditorBrowsable(EditorBrowsableState.Advanced)] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogChar(char charCode) { + #if DEBUG + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ToolStripDropDown.ProcessDialogChar [" + charCode.ToString() + "]"); + #endif + // since we're toplevel and arent a container control, we've got to do our own mnemonic handling. + // + if ((OwnerItem == null || OwnerItem.Pressed) && charCode != ' ') { + if (ProcessMnemonic(charCode)){ + return true; + } + } + return base.ProcessDialogChar(charCode); + } + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + if (!CanProcessMnemonic()) { + return false; // here for security see CanProcessMnemonic. + } + return base.ProcessMnemonic(charCode); + } + internal override void ProcessDuplicateMnemonic(ToolStripItem item, char charCode) { + if (!CanProcessMnemonic()) { + return; // here for security see CanProcessMnemonic. + } + if (item != null) { + // SECREVIEW + // We want to make sure that Mnemonics arent another way of getting keyboard + // input. Allowing double mnemonics to switch selection could effectively let you + // figure out what someone was typing into the window by checking the selected state + // of all of your items. Instead of switching selection, we'll just perform the click. + // ===> Assumes ToolStripItem.ProcessMnemonic is protected by AWP + + if (IsRestrictedWindow) { + item.ProcessMnemonic(charCode); // execute the first click + } + else { + base.ProcessDuplicateMnemonic(item,charCode); + } + } + } + + internal override void RecreateHandleCore() { + // If we're visible, then we'll have set our parent hwnd to the active control. + // That means that re-create handle will set it as our parent, but that's not what + // we want, since that means that from now on we'll be displayed in that controls + // client co-ordinates. To fix this, we first re-parent ourselves back to the + // hidden window, do the re-create, then set the parent again. + if (Visible) { + ReparentToDropDownOwnerWindow(); + } + base.RecreateHandleCore(); + if (Visible) { + ReparentToActiveToolStripWindow(); + } + } + + private void ResetDropShadowEnabled() { + DropShadowEnabled = true; + } + + private void ReparentToActiveToolStripWindow() { + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, ToolStripManager.ModalMenuFilter.ActiveHwnd); + } + + private void ReparentToDropDownOwnerWindow() { + // when we're toplevel we need to parent ourselves to a hidden window + // this prevents a taskbar entry. + NativeWindow ownerWindow = DropDownOwnerWindow; + HandleRef ownerHandle = new HandleRef(ownerWindow, ownerWindow.Handle); + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_HWNDPARENT, ownerHandle); + } + + internal override void ResetScaling(int newDpi) { + base.ResetScaling(newDpi); + CommonProperties.xClearPreferredSizeCache(this); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, newDpi); + } + + /// + /// VERY similar to Form.ScaleCore + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, GetType().Name + "::ScaleCore(" + dx + ", " + dy + ")"); + SuspendLayout(); + try { + //Get size values in advance to prevent one change from affecting another. + Size clientSize = ClientSize; + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + ClientSize = ScaleSize(clientSize, dx, dy); + if (!MinimumSize.IsEmpty) { + MinimumSize = ScaleSize(minSize, dx, dy); + } + if (!MaximumSize.IsEmpty) { + MaximumSize = ScaleSize(maxSize, dx, dy); + } + + ScaleDockPadding(dx, dy); + + foreach(Control control in Controls) { + if (control != null) { +#pragma warning disable 618 + control.Scale(dx, dy); +#pragma warning restore 618 + } + } + } + finally { + ResumeLayout(); + } + } + + /// + /// + /// Scale this form. Form overrides this to enforce a maximum / minimum size. + /// + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { + + base.ScaleControl(factor, specified); + + Size minSize = MinimumSize; + Size maxSize = MaximumSize; + if (!MinimumSize.IsEmpty) { + MinimumSize = ScaleSize(minSize, factor.Width, factor.Height); + } + if (!MaximumSize.IsEmpty) { + MaximumSize = ScaleSize(maxSize, factor.Width, factor.Height); + } + } + + /// + /// This is called when the ToolStripDropDownItem sets the DropDown property using CreateDefaultDropDown. + /// In this case, the IsAutoGenerated should return true. + /// + internal void SetAutoGeneratedInternal(bool autoGenerated) + { + this.isAutoGenerated = autoGenerated; + } + + + /// + /// + /// Sync sizes with the ToolStripDropDown + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + + Rectangle bounds = new Rectangle(x, y, width, height); + bounds = GetDropDownBounds(bounds); + + base.SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + } + + + private void SetTopLevelStyle(bool value) { + if (IsHandleCreated) { + // We need to swap they style bits on the window handle + // we could recreate the handle, but that seems rather expensive. + int styleFlags = WindowStyle; + + if (value) { + // setting toplevel = true + styleFlags &= ~NativeMethods.WS_CHILD; + styleFlags |= NativeMethods.WS_POPUP; + } + else { + // this is a child window + styleFlags &= ~NativeMethods.WS_POPUP; + styleFlags |= NativeMethods.WS_CHILD; + } + + WindowStyle = styleFlags; + + } + } + /// + /// + /// Summary of SetVisibleCore. + /// + /// + protected override void SetVisibleCore(bool visible) { + + if (state[stateInSetVisibleCore]) { + return; + } + state[stateInSetVisibleCore] = true; + try { + if (visible) { + if (LayoutRequired) { + LayoutTransaction.DoLayout(this, this, PropertyNames.Visible); + } + bool openingEventCancelled = true; // assume that it's been cancelled so that if we throw we do nothing. + + try { + // VSWhidbey 313920 - add opening event. + // Snap the foreground window BEFORE calling any user events so they dont have a + // chance to activate something else. This covers the case where someone handles the + // opening event and throws up a messagebox. + IntPtr foregroundWindow = UnsafeNativeMethods.GetForegroundWindow(); + + // Fire Opening event + // Cancellable event in which default value of e.Cancel depends on the number of displayed items >0. + // + CancelEventArgs openEventArgs = new CancelEventArgs(/*cancel=*/(DisplayedItems.Count == 0)); + OnOpening(openEventArgs); + + openingEventCancelled = openEventArgs.Cancel; + + if (!openingEventCancelled) { + // do the actual work to open the window. + if (TopLevel) { + ReparentToActiveToolStripWindow(); + } + // review: what is this used for? + if (OwnerToolStrip != null) { + OwnerToolStrip.ActiveDropDowns.Add(this); + // the act of showing this window can cause a spurious mouse move + // in the parent, make sure it retains where the mouse really was. + OwnerToolStrip.SnapMouseLocation(); + + // VSWhidbey 458967: make sure that mouse capture + // transitions between the owner and dropdown. + if (OwnerToolStrip.CaptureInternal) { + CaptureInternal = true; + } + } + base.SetVisibleCore(visible); + if (TopLevel){ + ApplyTopMost(true); + } + else if (IsHandleCreated && SafeNativeMethods.IsWindowEnabled(new HandleRef(this, Handle))) { + SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOP, 0, 0, 0, 0, + NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOACTIVATE); + } + + } + + } + finally { + // Fire Opened event + // fire the opened event only if we actually opened the control. + // + if (!openingEventCancelled) { + OnOpened(new EventArgs()); + } + } + + } + else { + if (GetVisibleCore()) { + ToolStripDropDownCloseReason reason = closeReason; + ResetCloseReason(); + + ToolStripDropDownClosingEventArgs e = new ToolStripDropDownClosingEventArgs(reason); + + // Fire Closing Event + // Cancel is prepopulated based on AutoClose feature. + // + if (e.CloseReason != ToolStripDropDownCloseReason.CloseCalled) { + e.Cancel = !AutoClose; + } + else { + e.Cancel = false; + } + + try { + OnClosing(e); + } + finally { + // SECREVIEW - When IsRestrictedWindow is true, you cannot cancel the closing of the ToolStripDropDown. + // You can set AutoClose = false and OnClosing(e.Cancel=true), but we will ignore it. + + if (!e.Cancel || IsRestrictedWindow /*delay evaluate only in the case we need it*/) { + + // setting to not visible. Dismiss our child drop downs, reset, set ourselves visible false. + DismissActiveDropDowns(); + + // VSWhidbey 480392 and Dev10 490127: make sure we cancel auto expansion on the root + ToolStrip topLevelToolStrip = GetToplevelOwnerToolStrip(); + ToolStrip parentToolStrip = null; + if (this.OwnerItem != null) { + parentToolStrip = this.OwnerItem.ParentInternal; + } + // We don't consider reason == ToolStripDropDownCloseReason.Keyboard here. + // DropDown needs to be closed when Alt or ESC is pressed, + // but these two keys are handled in ToolStrip.RestoreFocusInternal() + // and ToolStripDropDown.SelectPreviousToolStrip() respectively, + // and ToolStrip.MenuAutoExpand of top level tool strip will be set false there. + // Left and Right keys may also close dropdown, but we don't need to + // set ToolStrip.MenuAutoExpand of top level tool strip to be false in such cases. + if ((reason == ToolStripDropDownCloseReason.AppClicked) || + (reason == ToolStripDropDownCloseReason.ItemClicked) || + (reason == ToolStripDropDownCloseReason.CloseCalled && topLevelToolStrip == parentToolStrip) || + (reason == ToolStripDropDownCloseReason.AppFocusChange && topLevelToolStrip == parentToolStrip)) { + CancelAutoExpand(); + } + + // if this came through via a click event we should actually + // dismiss everyone in the chain. Other windows will recieve a + // close, closing event with reason AppFocusChange. This is by + // design since the item wasnt clicked on that window. + if (reason == ToolStripDropDownCloseReason.ItemClicked) { + // Preserve the SourceControl value up the chain. + this.saveSourceControl = true; + DismissAll(); + // make sure that when we roll up, our owner item's selection is cleared. + ToolStripItem rootOwnerItem = GetToplevelOwnerItem(); + if (rootOwnerItem != null) { + rootOwnerItem.Unselect(); + } + + ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this); + + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose,"[ToolStripDropDown.SetVisibleCore] Exiting menu mode because item clicked"); + + ToolStripManager.ModalMenuFilter.ExitMenuMode(); + } + else { + ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this); + } + + + // snap our bounds, we'll need it for some invalidation later. + Rectangle bounds = this.Bounds; + + + try { + base.SetVisibleCore(visible); + } + finally { + + // remove ourselves from the active dropdown list. + if (OwnerToolStrip != null) { + OwnerToolStrip.ActiveDropDowns.Remove(this); + } + ActiveDropDowns.Clear(); + // VSWhidbey 389244: if the user traps the click event and starts + // pumping their own messages by calling Application.DoEvents, we + // should release mouse capture. + if (CaptureInternal) { + CaptureInternal = false; + } + } + + // Fire OnClosed. + // if you make VisibleChanged throw you dont get closed. Sorry. + + ToolStripDropDownClosedEventArgs closedEventArgs = new ToolStripDropDownClosedEventArgs(reason); + OnClosed(closedEventArgs); + + if (TopLevel && (!IsDisposed || !Disposing)) { + // Parent back up to our DropDownOwnerWindow. + ReparentToDropDownOwnerWindow(); + } + + + if (!this.saveSourceControl) { + Debug.Assert(reason != ToolStripDropDownCloseReason.ItemClicked, + "Why are we resetting SourceControl on a click event?"); + + // VSWhidbey 475650: If we're not about to fire a Click event, reset SourceControl. + SourceControlInternal = null; + } + + // Making ourselves look presentable: + // We may be about to invoke a click event here... + // if we're the topmost dropdown then invalidate our + // intersection with the toplevel toolstrip + if (!DesignMode && IsFirstDropDown && OwnerItem != null && TopLevel) { + ToolStrip toolStrip = GetToplevelOwnerToolStrip(); + if (toolStrip != null && !(toolStrip.IsDisposed || toolStrip.Disposing)) { + // translate the bounds (already in screen coords) to toolstrip. + bounds.Location = toolStrip.PointToClient(bounds.Location); + + // find the intersection with the client and use that to invalidate + bounds.Intersect(toolStrip.ClientRectangle); + + if (bounds.Width > 0 && bounds.Height >0) { + toolStrip.Invalidate(bounds); + toolStrip.Update(); + } + } + } + + } + } + } + else { + ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this); + } + } + } + finally { + state[stateInSetVisibleCore] = false; + this.saveSourceControl = false; + } + + } + + private bool ShouldSerializeDefaultDropDownDirection() { + return (childDropDownDirection != ToolStripDropDownDirection.Default); + } + + /// + /// Updates the layered window attributes if the control + /// is in layered mode. + /// + private void UpdateLayered() { + if (state[stateLayered] && IsHandleCreated && TopLevel && OSFeature.Feature.IsPresent(OSFeature.LayeredWindows)) { + bool result; + + result = UnsafeNativeMethods.SetLayeredWindowAttributes(new HandleRef(this, Handle), 0, OpacityAsByte, NativeMethods.LWA_ALPHA); + + if (!result) { + throw new Win32Exception(); + } + } + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new void Show() { + // don't set displayLocation here, since all the other Show methods call this. + base.Show(); + } + + /// + /// show relative to control coordinates + public void Show(Control control, Point position) { + if (control == null) { + throw new ArgumentNullException("control"); + } + SourceControlInternal = control; + // VSWhidbey 310328 - when we have no owner item and we're set to RTL.Inherit, translate the coordinates + // so that the menu looks like it's swooping from the other side + if (this.OwnerItem == null && control.RightToLeft == RightToLeft.Yes) { + AdjustSize(); + position.Offset(control.IsMirrored ? Width : -Width, 0); + } + + this.displayLocation = control.PointToScreen(position); + this.Location = this.displayLocation; + ShowCore(); + + } + + public void Show(Control control, Point position, ToolStripDropDownDirection direction) { + if (control == null) { + throw new ArgumentNullException("control"); + } + SourceControlInternal = control; + this.displayLocation = CalculateDropDownLocation(control.PointToScreen(position), direction).Location; + this.Location = this.displayLocation; + ShowCore(); + } + + /// + /// show relative to control coordinates + public void Show(Control control, int x, int y) { + if (control == null) { + throw new ArgumentNullException("control"); + } + SourceControlInternal = control; + Show(control, new Point(x,y)); + } + + + /// + /// show relative to screen coordinates + public void Show(Point screenLocation) { + this.displayLocation = screenLocation; + this.Location = this.displayLocation; + ShowCore(); + } + public void Show(Point position, ToolStripDropDownDirection direction) { + this.displayLocation = CalculateDropDownLocation(position, direction).Location; + this.Location = this.displayLocation; + ShowCore(); + } + + + /// + /// show relative to screen coordinates + public void Show(int x, int y) { + this.displayLocation = new Point(x,y); + this.Location = this.displayLocation; + ShowCore(); + } + + private void ShowCore() { + Show(); + } + + private bool ShouldSerializeDropShadowEnabled() { + return dropShadowEnabled == false; + } + internal override bool ShouldSerializeLayoutStyle() { + return LayoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow; + } + internal void UnassignDropDownItem() { + Debug.Assert(countDropDownItemsAssignedTo > 0, "dropdown assignment underflow"); + countDropDownItemsAssignedTo = Math.Max(--countDropDownItemsAssignedTo, 0); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST && IsRestrictedWindow) { + // SECREVIEW: + // To Receive a OnKey* event, the ToolStripDropDown must have AllWindows permission + // directly calling DefWndProc will prevent these events from executing. + // All PreProcess keyboard messages are protected by link demands. + + // NOTE the same kind of fix needs to go into ToolStripScrollButton.StickyLabel (the only alloewed ControlHost) + DefWndProc(ref m); + return; + } + switch (m.Msg) { + case NativeMethods.WM_NCACTIVATE: + // if someone clicks on a child control of the toolstrip dropdown, we want + // the title bar to continue appearing active. Normally we just show without + // taking window activation (ShowWindow(SHOWNOACTIVATE)) but we cant stop + // child controls from taking focus. + WmNCActivate(ref m); + return; + case NativeMethods.WM_ACTIVATE: + // This is the Chrome Panel collection editor scenario + // we had focus, then the Chrome panel was activated and we never went away + // when we get focus again, we should reactivate our message filter. + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown.WndProc] got a WM_ACTIVATE " + (((int)m.WParam == NativeMethods.WA_ACTIVE) ? "WA_ACTIVE" : "WA_INACTIVE") + " - checkin if we need to set the active toolstrip"); + + if((int)m.WParam == NativeMethods.WA_ACTIVE) { + if (this.Visible) { + if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != this) { + // if we were inactive and now we are, we should enter menu mode + ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this); + } + } + else { + Debug.Fail("Why are we being activated when we're not visible? Deactivating thingee is " + WindowsFormsUtils.GetControlInformation(m.LParam)); + } + } + else { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown.WndProc] activating thingee is " + WindowsFormsUtils.GetControlInformation(m.LParam)); + } + + base.WndProc(ref m); + return; +/* case NativeMethods.WM_SYSKEYUP: + Keys keyData = (Keys)(int)m.WParam; + base.WndProc(ref m); + // VSW 423760: handle the case where the ALT key has been pressed down while a dropdown + // was open. We need to clear off the MenuKeyToggle so the next ALT will activate + // the menu. + if (IsMenuKey(keyData)) { + ToolStrip toplevel = GetToplevelOwnerToolStrip(); + if (toplevel != null && toplevel.MenuKeyToggle) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ToolStripDropDown WM_SYSKEYUP]: Got Menu Key, restoring MenuKeyToggle."); + toplevel.MenuKeyToggle = false; + } + } + return;*/ + default: + base.WndProc(ref m); + return; + } + + } + + +#region DropDownSpecific + internal void DismissAll() { + ToolStripDropDown toplevel = this.GetFirstDropDown(); + toplevel.closeReason = this.closeReason; + toplevel.DismissActiveDropDowns(); + if (!LocalAppContextSwitches.UseLegacyContextMenuStripSourceControlValue) { + toplevel.saveSourceControl = this.saveSourceControl; + } + toplevel.Visible = false; + } + private void DismissActiveDropDowns() { + Debug.WriteLineIf((DropDownActivateDebug.TraceVerbose && ActiveDropDowns.Count > 0), "Dismiss children called - COUNT " + ActiveDropDowns.Count + " \r\n" + new StackTrace().ToString()); + int count = ActiveDropDowns.Count; + if (count == 1) { + // this is the most common case + ToolStripDropDown dropDown = ActiveDropDowns[0] as ToolStripDropDown; + if (dropDown != null) { + dropDown.Visible = false; + } + + } + else { + ArrayList dropDowns = ActiveDropDowns.Clone() as ArrayList; + // We cant iterate through the active dropdown collection + // here as changing visibility changes the collection. + for (int i = 0; i < dropDowns.Count; i++) { + ToolStripDropDown dropDown = dropDowns[i] as ToolStripDropDown; + if (dropDown != null) { + dropDown.Visible = false; + } + } + } + } + +#region WMNCACTIVATE + private bool sendingActivateMessage = false; + + // WmNcActivate + // if someone clicks on a child control of the toolstrip dropdown, we want + // the title bar to continue appearing active. Normally we just show without + // taking window activation (ShowWindow(SHOWNOACTIVATE)) but we cant stop + // child controls from taking focus. + private void WmNCActivate(ref Message m) { + + if (m.WParam != IntPtr.Zero /*activating*/) { + + if (!sendingActivateMessage) { + sendingActivateMessage = true; + try { + Debug.WriteLineIf(DropDownActivateDebug.TraceVerbose, "Sending WM_NCACTIVATE to toplevel hwnd" + ToolStripManager.ModalMenuFilter.ActiveHwnd); + // we're activating - notify the previous guy that we're activating. + HandleRef activeHwndHandleRef = ToolStripManager.ModalMenuFilter.ActiveHwnd; + + UnsafeNativeMethods.SendMessage(activeHwndHandleRef, NativeMethods.WM_NCACTIVATE, (IntPtr)1, NativeMethods.InvalidIntPtr); + SafeNativeMethods.RedrawWindow(activeHwndHandleRef, null, NativeMethods.NullHandleRef, NativeMethods.RDW_FRAME | NativeMethods.RDW_INVALIDATE); + m.WParam = (IntPtr)1; + } + finally { + sendingActivateMessage = false; + } + } + DefWndProc(ref m); + return; + + } + else { + base.WndProc(ref m); + } + + } +#endregion + /// + /// Determines if this is the first dropDown in the dropDown chain + /// + internal bool IsFirstDropDown { + get { + return ((OwnerToolStrip as ToolStripDropDown) == null); + } + } + + /// + /// returns the root dropdown in the chain. + /// + internal ToolStripDropDown GetFirstDropDown() { + + ToolStripDropDown topmost = this; + // walk back up the chain of windows to get the topmost + ToolStripDropDown ownerDropDown = topmost.OwnerToolStrip as ToolStripDropDown; + while (ownerDropDown != null) { + topmost = ownerDropDown; + ownerDropDown = topmost.OwnerToolStrip as ToolStripDropDown; + } + return topmost; + } + + internal static ToolStripDropDown GetFirstDropDown(ToolStrip start) { + Debug.Assert(start != null, "Who is passing null to GetFirstDropDown?"); + if ((start == null) || (!start.IsDropDown)) { + return null; + } + ToolStripDropDown startDropDown = (start as ToolStripDropDown); + return startDropDown.GetFirstDropDown(); + } + + +#endregion DropDownSpecific + + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripDropDownAccessibleObject : ToolStripAccessibleObject { + private ToolStripDropDown owner; + + /// + public ToolStripDropDownAccessibleObject(ToolStripDropDown owner) : base(owner) { + this.owner = owner; + } + + public override string Name { + get { + // Special case: If an explicit name has been set in the AccessibleName property, use that. + // Note: Any non-null value in AccessibleName overrides the default accessible name logic, + // even an empty string (this is the only way to *force* the accessible name to be blank). + string name = owner.AccessibleName; + if (name != null) { + return name; + } + + // NOT localized for testing purposes. Localizers can use AccessibleName. + name = "DropDown"; + + if (owner.OwnerItem != null && owner.OwnerItem.AccessibilityObject.Name != null) { + name = owner.OwnerItem.AccessibilityObject.Name + name; + } + + return name; + } + + set { + // If anyone tries to set the accessible name, just cache the value in the control's + // AccessibleName property. This value will then end up overriding the normal accessible + // name logic, until such time as AccessibleName is set back to null. + owner.AccessibleName = value; + } + } + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.MenuPopup; + } + } + + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownButton.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownButton.cs new file mode 100644 index 000000000..f1dc77c26 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownButton.cs @@ -0,0 +1,320 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Drawing.Imaging; + using System.Threading; + using System.Diagnostics; + using System.Windows.Forms.ButtonInternal; + using System.Windows.Forms.Layout; + using System.Security.Permissions; + using System.Security; + using System.Windows.Forms.Design; + /// + /// + /// A ToolStripButton that can display a popup. + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)] + public class ToolStripDropDownButton : ToolStripDropDownItem { + + private bool showDropDownArrow = true; + private byte openMouseId = 0; + + /// + /// + /// Constructs a ToolStripButton that can display a popup. + /// + public ToolStripDropDownButton() { + Initialize(); + } + public ToolStripDropDownButton(string text):base(text,null,(EventHandler)null) { + Initialize(); + } + public ToolStripDropDownButton(Image image):base(null,image,(EventHandler)null) { + Initialize(); + } + public ToolStripDropDownButton(string text, Image image):base(text,image,(EventHandler)null) { + Initialize(); + } + public ToolStripDropDownButton(string text, Image image, EventHandler onClick):base(text,image,onClick) { + Initialize(); + } + public ToolStripDropDownButton(string text, Image image, EventHandler onClick, string name) :base(text,image,onClick,name){ + Initialize(); + } + public ToolStripDropDownButton(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) { + Initialize(); + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level1) { + return new ToolStripDropDownButtonAccessibleObject(this); + } + else { + return base.CreateAccessibilityInstance(); + } + } + + [DefaultValue(true)] + public new bool AutoToolTip { + get { + return base.AutoToolTip; + } + set { + base.AutoToolTip = value; + } + } + + + protected override bool DefaultAutoToolTip { + get { + return true; + } + } + + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripDropDownButtonShowDropDownArrowDescr), + SRCategory(SR.CatAppearance) + ] + public bool ShowDropDownArrow { + get { + return showDropDownArrow; + } + set { + if (showDropDownArrow != value) { + showDropDownArrow = value; + this.InvalidateItemLayout(PropertyNames.ShowDropDownArrow); + } + } + } + /// + /// Creates an instance of the object that defines how image and text + /// gets laid out in the ToolStripItem + /// + internal override ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripDropDownButtonInternalLayout(this); + } + + + protected override ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true); + } + + /// + /// Called by all constructors of ToolStripButton. + /// + private void Initialize() { + SupportsSpaceKey = true; + } + + /// + /// + /// Overriden to invoke displaying the popup. + /// + protected override void OnMouseDown(MouseEventArgs e) { + if ((Control.ModifierKeys != Keys.Alt) && + (e.Button == MouseButtons.Left)) { + if (DropDown.Visible) { + ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); + } + else { + // opening should happen on mouse down. + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + this.ShowDropDown(/*mousePush =*/true); + } + } + base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseEventArgs e) { + if ((Control.ModifierKeys != Keys.Alt) && + (e.Button == MouseButtons.Left)) { + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + if (closeMouseId != openMouseId) { + openMouseId = 0; // reset the mouse id, we should never get this value from toolstrip. + ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); + Select(); + } + } + base.OnMouseUp(e); + } + + protected override void OnMouseLeave(EventArgs e) { + openMouseId = 0; // reset the mouse id, we should never get this value from toolstrip. + base.OnMouseLeave(e); + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + if (this.Owner != null) { + ToolStripRenderer renderer = this.Renderer; + Graphics g = e.Graphics; + + renderer.DrawDropDownButtonBackground(new ToolStripItemRenderEventArgs(e.Graphics, this)); + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, InternalLayout.ImageRectangle)); + } + + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, this.Text, InternalLayout.TextRectangle, this.ForeColor, this.Font, InternalLayout.TextFormat)); + } + if (ShowDropDownArrow) { + ToolStripDropDownButton.ToolStripDropDownButtonInternalLayout layout = InternalLayout as ToolStripDropDownButtonInternalLayout; + Rectangle dropDownArrowRect = (layout != null) ? layout.DropDownArrowRect :Rectangle.Empty; + + Color arrowColor; + if (Selected && !Pressed && AccessibilityImprovements.Level2 && SystemInformation.HighContrast) { + arrowColor = Enabled ? SystemColors.HighlightText : SystemColors.ControlDark; + } + else { + arrowColor = Enabled ? SystemColors.ControlText : SystemColors.ControlDark; + } + renderer.DrawArrow(new ToolStripArrowRenderEventArgs(g, this,dropDownArrowRect, arrowColor, ArrowDirection.Down)); + } + } + } + + + /// + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal override bool ProcessMnemonic(char charCode) { + // checking IsMnemonic is not necesssary - toolstrip does this for us. + if (HasDropDownItems) { + Select(); + ShowDropDown(); + return true; + } + return false; + } + + /// + /// An implementation of Accessibleobject for use with ToolStripDropDownButton + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripDropDownButtonAccessibleObject : ToolStripDropDownItemAccessibleObject { + private ToolStripDropDownButton ownerItem = null; + + public ToolStripDropDownButtonAccessibleObject(ToolStripDropDownButton ownerItem) + : base(ownerItem) { + this.ownerItem = ownerItem; + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ButtonControlTypeId; + } + else { + return base.GetPropertyValue(propertyID); + } + } + } + + internal class ToolStripDropDownButtonInternalLayout : ToolStripItemInternalLayout { + private ToolStripDropDownButton ownerItem; + private static readonly Size dropDownArrowSizeUnscaled = new Size(5, 3); + private static Size dropDownArrowSize = dropDownArrowSizeUnscaled; + private const int DROP_DOWN_ARROW_PADDING = 2; + private static Padding dropDownArrowPadding = new Padding(DROP_DOWN_ARROW_PADDING); + private Padding scaledDropDownArrowPadding = dropDownArrowPadding; + private Rectangle dropDownArrowRect = Rectangle.Empty; + + public ToolStripDropDownButtonInternalLayout(ToolStripDropDownButton ownerItem) : base(ownerItem) { + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) + { + dropDownArrowSize = DpiHelper.LogicalToDeviceUnits(dropDownArrowSizeUnscaled, ownerItem.DeviceDpi); + scaledDropDownArrowPadding = DpiHelper.LogicalToDeviceUnits(dropDownArrowPadding, ownerItem.DeviceDpi); + } + else if (DpiHelper.IsScalingRequired) { + // these 2 values are used to calculate size of the clickable drop down button + // on the right of the image/text + dropDownArrowSize = DpiHelper.LogicalToDeviceUnits(dropDownArrowSizeUnscaled); + scaledDropDownArrowPadding = DpiHelper.LogicalToDeviceUnits(dropDownArrowPadding); + } + this.ownerItem = ownerItem; + } + + public override Size GetPreferredSize(Size constrainingSize) { + + Size preferredSize = base.GetPreferredSize(constrainingSize); + if (ownerItem.ShowDropDownArrow) { + if (ownerItem.TextDirection == ToolStripTextDirection.Horizontal) { + preferredSize.Width += DropDownArrowRect.Width + scaledDropDownArrowPadding.Horizontal; + } + else { + preferredSize.Height += DropDownArrowRect.Height + scaledDropDownArrowPadding.Vertical; + } + } + return preferredSize; + } + + protected override ToolStripItemLayoutOptions CommonLayoutOptions() { + ToolStripItemLayoutOptions options = base.CommonLayoutOptions(); + + if (ownerItem.ShowDropDownArrow) { + + if (ownerItem.TextDirection == ToolStripTextDirection.Horizontal) { + + // We're rendering horizontal.... make sure to take care of RTL issues. + + int widthOfDropDown = dropDownArrowSize.Width + scaledDropDownArrowPadding.Horizontal; + options.client.Width -= widthOfDropDown; + + if (ownerItem.RightToLeft == RightToLeft.Yes) { + + // if RightToLeft.Yes: [ v | rest of drop down button ] + options.client.Offset(widthOfDropDown, 0); + dropDownArrowRect = new Rectangle(scaledDropDownArrowPadding.Left,0, dropDownArrowSize.Width, ownerItem.Bounds.Height); + } + else { + // if RightToLeft.No [ rest of drop down button | v ] + dropDownArrowRect = new Rectangle(options.client.Right,0, dropDownArrowSize.Width, ownerItem.Bounds.Height); + + } + } + else { + // else we're rendering vertically. + int heightOfDropDown = dropDownArrowSize.Height + scaledDropDownArrowPadding.Vertical; + + options.client.Height -= heightOfDropDown; + + // [ rest of button / v] + dropDownArrowRect = new Rectangle(0,options.client.Bottom + scaledDropDownArrowPadding.Top, ownerItem.Bounds.Width-1, dropDownArrowSize.Height); + + } + + } + return options; + } + + public Rectangle DropDownArrowRect { + get { + return dropDownArrowRect; + } + } + + } + + } +} + + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownCloseReason.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownCloseReason.cs new file mode 100644 index 000000000..dfb1adb9f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownCloseReason.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + public enum ToolStripDropDownCloseReason { + AppFocusChange, + AppClicked, + ItemClicked, + Keyboard, + CloseCalled + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventArgs.cs new file mode 100644 index 000000000..3a80a63f2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventArgs.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + + public class ToolStripDropDownClosedEventArgs : EventArgs { + + ToolStripDropDownCloseReason closeReason; + + public ToolStripDropDownClosedEventArgs(ToolStripDropDownCloseReason reason) { + closeReason = reason; + } + + public ToolStripDropDownCloseReason CloseReason { + get { return closeReason; } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventHandler.cs new file mode 100644 index 000000000..dc1f67dc7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosedEventHandler.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + + + public delegate void ToolStripDropDownClosedEventHandler(object sender, ToolStripDropDownClosedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventArgs.cs new file mode 100644 index 000000000..957d1f243 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventArgs.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + + // SECREVIEW - for the "Cancel" to be honored, you must have AllWindowsPermission + // i.e. ToolStripDropDown.IsRestrictedWindow MUST be false. + // for more details see ToolStripDropDown.SetVisibleCore + + public class ToolStripDropDownClosingEventArgs : CancelEventArgs { + + ToolStripDropDownCloseReason closeReason; + + public ToolStripDropDownClosingEventArgs(ToolStripDropDownCloseReason reason) { + closeReason = reason; + } + + + public ToolStripDropDownCloseReason CloseReason { + get { return closeReason; } + } + // TBD: CloseReason + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventHandler.cs new file mode 100644 index 000000000..24801eedb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownClosingEventHandler.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + + + public delegate void ToolStripDropDownClosingEventHandler(object sender, ToolStripDropDownClosingEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownDirection.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownDirection.cs new file mode 100644 index 000000000..b5b02d00e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownDirection.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + public enum ToolStripDropDownDirection { + + // Dir L=0 + AboveLeft = 0x0000, // 0 0 0 Above =00 + AboveRight = 0x0001, // 0 0 1 Below =01 + BelowLeft = 0x0002, // 0 1 0 Side =10 + BelowRight = 0x0003, // 0 1 1 Last bit indicates left or right. + Left = 0x0004, // 1 0 0 + Right = 0x0005, // 1 0 1 + Default = 0x0007 // 1 1 1 + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs new file mode 100644 index 000000000..559d32f89 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs @@ -0,0 +1,973 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using System.Diagnostics; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + /// + /// Base class for ToolStripItems that display DropDown windows. + /// + [Designer("System.Windows.Forms.Design.ToolStripMenuItemDesigner, " + AssemblyRef.SystemDesign)] + [DefaultProperty("DropDownItems")] + public abstract class ToolStripDropDownItem : ToolStripItem { + + private ToolStripDropDown dropDown = null; + private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default; + private static readonly object EventDropDownShow = new object(); + private static readonly object EventDropDownHide = new object(); + private static readonly object EventDropDownOpened = new object(); + private static readonly object EventDropDownClosed = new object(); + private static readonly object EventDropDownItemClicked = new object(); + + /// + /// + /// Protected ctor so you can't create one of these without deriving from it. + /// + protected ToolStripDropDownItem() { + } + + protected ToolStripDropDownItem(string text, Image image, EventHandler onClick) : base(text, image, onClick) { + } + + protected ToolStripDropDownItem(string text, Image image, EventHandler onClick, string name) : base(text, image, onClick, name) { + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripDropDownItem(string text, Image image, params ToolStripItem[] dropDownItems) : this(text, image, (EventHandler)null) { + if (dropDownItems != null) { + this.DropDownItems.AddRange(dropDownItems); + } + } + + + /// + /// + /// The ToolStripDropDown that will be displayed when this item is clicked. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripDropDownDescr) + ] + public ToolStripDropDown DropDown { + get { + if (dropDown == null) { + DropDown = CreateDefaultDropDown(); + if (!(this is ToolStripOverflowButton)) + { + dropDown.SetAutoGeneratedInternal(true); + } + + if (ParentInternal != null) { + dropDown.ShowItemToolTips = ParentInternal.ShowItemToolTips; + } + } + return dropDown; + } + set { + if (dropDown != value) { + + if (dropDown != null) { + dropDown.Opened -= new EventHandler(DropDown_Opened); + dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + dropDown.UnassignDropDownItem(); + } + + dropDown = value; + if (dropDown != null) { + dropDown.Opened += new EventHandler(DropDown_Opened); + dropDown.Closed += new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked += new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + dropDown.AssignToDropDownItem(); + } + + } + + + } + } + + // the area which activates the dropdown. + internal virtual Rectangle DropDownButtonArea { + get { return this.Bounds; } + } + + [Browsable(false)] + [SRDescription(SR.ToolStripDropDownItemDropDownDirectionDescr)] + [SRCategory(SR.CatBehavior)] + public ToolStripDropDownDirection DropDownDirection { + get { + if (toolStripDropDownDirection == ToolStripDropDownDirection.Default) { + ToolStrip parent = ParentInternal; + if (parent != null) { + ToolStripDropDownDirection dropDownDirection = parent.DefaultDropDownDirection; + if (OppositeDropDownAlign || this.RightToLeft != parent.RightToLeft && (this.RightToLeft != RightToLeft.Inherit)) { + dropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, RightToLeft); + } + + if (IsOnDropDown) { + // we gotta make sure that we dont collide with the existing menu. + Rectangle bounds = GetDropDownBounds(dropDownDirection); + Rectangle ownerItemBounds = new Rectangle(TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), Size); + Rectangle intersectionBetweenChildAndParent = Rectangle.Intersect(bounds, ownerItemBounds); + + // grab the intersection + if (intersectionBetweenChildAndParent.Width >= 2) { + RightToLeft toggledRightToLeft = (RightToLeft == RightToLeft.Yes) ? RightToLeft.No : RightToLeft.Yes; + ToolStripDropDownDirection newDropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, toggledRightToLeft); + + // verify that changing the dropdown direction actually causes less intersection. + int newIntersectionWidth = Rectangle.Intersect(GetDropDownBounds(newDropDownDirection), ownerItemBounds).Width; + if (newIntersectionWidth < intersectionBetweenChildAndParent.Width) { + dropDownDirection = newDropDownDirection; + } + } + + } + return dropDownDirection; + + } + } + + // someone has set a custom override + return toolStripDropDownDirection; + + } + set { + // cant use Enum.IsValid as its not sequential + switch (value) { + case ToolStripDropDownDirection.AboveLeft: + case ToolStripDropDownDirection.AboveRight: + case ToolStripDropDownDirection.BelowLeft: + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.Left: + case ToolStripDropDownDirection.Right: + case ToolStripDropDownDirection.Default: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection)); + } + + if (toolStripDropDownDirection != value) { + toolStripDropDownDirection = value; + if (HasDropDownItems && DropDown.Visible) { + DropDown.Location = DropDownLocation; + } + } + } + } + + + /// + /// + /// Occurs when the dropdown is closed + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownClosedDecr) + ] + public event EventHandler DropDownClosed { + add { + Events.AddHandler(EventDropDownClosed, value); + } + remove { + Events.RemoveHandler(EventDropDownClosed, value); + } + } + + /// + internal protected virtual Point DropDownLocation + { + get { + + if (ParentInternal == null || !HasDropDownItems) { + return Point.Empty; + } + ToolStripDropDownDirection dropDownDirection = DropDownDirection; + return GetDropDownBounds(dropDownDirection).Location; + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownOpeningDescr) + ] + public event EventHandler DropDownOpening { + add { + Events.AddHandler(EventDropDownShow, value); + } + remove { + Events.RemoveHandler(EventDropDownShow, value); + } + } + /// + /// + /// Occurs when the dropdown is opened + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownOpenedDescr) + ] + public event EventHandler DropDownOpened { + add { + Events.AddHandler(EventDropDownOpened, value); + } + remove { + Events.RemoveHandler(EventDropDownOpened, value); + } + } + + /// + /// + /// Returns the DropDown's items collection. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripDropDownItemsDescr) + ] + public ToolStripItemCollection DropDownItems { + get { + return DropDown.Items; + } + } + + /// + /// + /// Occurs when the dropdown is opened + /// + [SRCategory(SR.CatAction)] + public event ToolStripItemClickedEventHandler DropDownItemClicked { + add { + Events.AddHandler(EventDropDownItemClicked, value); + } + remove { + Events.RemoveHandler(EventDropDownItemClicked, value); + } + } + + /// + [Browsable(false)] + public virtual bool HasDropDownItems { + get { + //VSWhidbey 354665: use count of visible DisplayedItems instead so that we take into account things that arent visible + return (dropDown != null) && dropDown.HasVisibleItems; + } + } + + /// + [Browsable(false)] + public bool HasDropDown { + get { return dropDown != null; } + } + + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool Pressed { + get { + // + if (dropDown != null) { + if (DropDown.AutoClose || !IsInDesignMode || (IsInDesignMode && !IsOnDropDown)) { + return DropDown.OwnerItem == this && DropDown.Visible; + } + } + return base.Pressed; + } + } + + internal virtual bool OppositeDropDownAlign { + get { return false; } + } + + + internal virtual void AutoHide(ToolStripItem otherItemBeingSelected) { + HideDropDown(); + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripDropDownItemAccessibleObject(this); + } + + /// + protected virtual ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripDropDown(this, true); + } + + private Rectangle DropDownDirectionToDropDownBounds(ToolStripDropDownDirection dropDownDirection, Rectangle dropDownBounds) { + Point offset = Point.Empty; + + switch (dropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + offset.X = -dropDownBounds.Width + this.Width; + offset.Y = -dropDownBounds.Height + 1; + break; + case ToolStripDropDownDirection.AboveRight: + offset.Y = -dropDownBounds.Height + 1; + break; + case ToolStripDropDownDirection.BelowRight: + offset.Y = this.Height - 1; + break; + case ToolStripDropDownDirection.BelowLeft: + offset.X = -dropDownBounds.Width + this.Width; + offset.Y = this.Height - 1; + break; + case ToolStripDropDownDirection.Right: + offset.X = this.Width; + if (!IsOnDropDown) { + // overlap the toplevel toolstrip + offset.X -= 1; + } + break; + + case ToolStripDropDownDirection.Left: + offset.X = -dropDownBounds.Width; + break; + } + + Point itemScreenLocation = this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + dropDownBounds.Location = new Point(itemScreenLocation.X + offset.X, itemScreenLocation.Y + offset.Y); + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds); + return dropDownBounds; + } + + + private void DropDown_Closed(object sender, ToolStripDropDownClosedEventArgs e) { + OnDropDownClosed(EventArgs.Empty); + } + + private void DropDown_Opened(object sender, EventArgs e) { + OnDropDownOpened(EventArgs.Empty); + } + + private void DropDown_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { + OnDropDownItemClicked(e); + } + /// + /// + /// Make sure we unhook dropdown events. + /// + protected override void Dispose(bool disposing) + { + if (this.dropDown != null) { + dropDown.Opened -= new EventHandler(DropDown_Opened); + dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + + if (disposing && dropDown.IsAutoGenerated) { + // if we created the dropdown, dispose it and its children. + dropDown.Dispose(); + dropDown = null; + } + } + base.Dispose(disposing); + } + + + private Rectangle GetDropDownBounds(ToolStripDropDownDirection dropDownDirection) { + + Rectangle dropDownBounds = new Rectangle(Point.Empty, DropDown.GetSuggestedSize()); + // calculate the offset from the upper left hand corner of the item. + dropDownBounds = DropDownDirectionToDropDownBounds(dropDownDirection, dropDownBounds); + + // we should make sure we dont obscure the owner item. + Rectangle itemScreenBounds = new Rectangle(this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), this.Size); + + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Height > 1) { + + bool rtl = (RightToLeft == RightToLeft.Yes); + + // try positioning to the left + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) { + dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left, dropDownBounds); + } + + // try positioning to the right + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) { + dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right, dropDownBounds); + } + } + + return dropDownBounds; + + } + + + + /// + /// + /// Hides the DropDown, if it is visible. + /// + public void HideDropDown() { + // consider - CloseEventArgs to prevent shutting down. + OnDropDownHide(EventArgs.Empty); + + if (this.dropDown != null && this.dropDown.Visible) { + DropDown.Visible = false; + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + AccessibilityNotifyClients(AccessibleEvents.NameChange); + } + } + } + + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + if (dropDown != null) { + dropDown.OnOwnerItemFontChanged(EventArgs.Empty); + } + } + + + /// + protected override void OnBoundsChanged() { + base.OnBoundsChanged(); + //Reset the Bounds... + if (this.dropDown != null && this.dropDown.Visible) + { + this.dropDown.Bounds = GetDropDownBounds(DropDownDirection); + } + } + + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + if (HasDropDownItems) { + // only perform a layout on a visible dropdown - otherwise clear the preferred size cache. + if (DropDown.Visible) { + LayoutTransaction.DoLayout(DropDown, this, PropertyNames.RightToLeft); + } + else { + CommonProperties.xClearPreferredSizeCache(DropDown); + DropDown.LayoutRequired = true; + } + } + } + + + internal override void OnImageScalingSizeChanged(EventArgs e) { + base.OnImageScalingSizeChanged(e); + if (HasDropDown && DropDown.IsAutoGenerated) { + DropDown.DoLayoutIfHandleCreated(new ToolStripItemEventArgs(this)); + } + } + /// + /// + /// Called as a response to HideDropDown + /// + protected virtual void OnDropDownHide(EventArgs e) { + this.Invalidate(); + + EventHandler handler = (EventHandler)Events[EventDropDownHide]; + if (handler != null) handler(this, e); + } + /// + /// + /// Last chance to stick in the DropDown before it is shown. + /// + protected virtual void OnDropDownShow(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventDropDownShow]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownOpened(System.EventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + + if (DropDown.OwnerItem == this) { + EventHandler handler = (EventHandler)Events[EventDropDownOpened]; + if (handler != null) handler(this, e); + } + } + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownClosed(System.EventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + this.Invalidate(); + + if (DropDown.OwnerItem == this) { + EventHandler handler = (EventHandler)Events[EventDropDownClosed]; + if (handler != null) handler(this, e); + + if (!DropDown.IsAutoGenerated) { + DropDown.OwnerItem = null; + } + } + + } + + + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + + if (DropDown.OwnerItem == this) { + ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventDropDownItemClicked]; + if (handler != null) handler(this, e); + } + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + if (HasDropDownItems) { + return DropDown.ProcessCmdKeyInternal(ref m, keyData); + } + return base.ProcessCmdKey(ref m, keyData); + } + + + /// + [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] + protected internal override bool ProcessDialogKey(Keys keyData) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + + if (HasDropDownItems) { + + // VSWhidbey 478068: items on the overflow should have the same kind of keyboard handling as a toplevel + bool isToplevel = (!IsOnDropDown || IsOnOverflow); + + + if (isToplevel && (keyCode == Keys.Down || keyCode == Keys.Up || keyCode == Keys.Enter || (SupportsSpaceKey && keyCode == Keys.Space))) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from toplevel item"); + + if (Enabled || DesignMode) { + // |__[ * File ]_____| * is where you are. Up or down arrow hit should expand menu + this.ShowDropDown(); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + this.DropDown.SelectNextToolStripItem(null, true); + }// else eat the key + return true; + + } + else if (!isToplevel) { + + + // if we're on a DropDown - then cascade out. + bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0); + bool forward = ((keyCode == Keys.Enter) || (SupportsSpaceKey && keyCode == Keys.Space)); + forward = (forward || (menusCascadeRight && keyCode == Keys.Left) || (!menusCascadeRight && keyCode == Keys.Right)); + + + if (forward) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from NON-toplevel item"); + + if (Enabled || DesignMode) { + this.ShowDropDown(); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + this.DropDown.SelectNextToolStripItem(null, true); + } // else eat the key + return true; + } + + } + } + + + if (IsOnDropDown) { + + bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0); + bool backward = ((menusCascadeRight && keyCode == Keys.Right) || (!menusCascadeRight && keyCode == Keys.Left)); + + if (backward) { + + + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] close submenu from NON-toplevel item"); + + // we're on a drop down but we're heading back up the chain. + // remember to select the item that displayed this dropdown. + ToolStripDropDown parent = GetCurrentParentDropDown(); + if (parent != null && !parent.IsFirstDropDown) { + // we're walking back up the dropdown chain. + parent.SetCloseReason(ToolStripDropDownCloseReason.Keyboard); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + parent.SelectPreviousToolStrip(); + return true; + } + // else if (parent.IsFirstDropDown) + // the base handling (ToolStripDropDown.ProcessArrowKey) will perform auto-expansion of + // the previous item in the menu. + + } + } + + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] ddi calling base"); + return base.ProcessDialogKey(keyData); + + } + + private ToolStripDropDownDirection RTLTranslateDropDownDirection(ToolStripDropDownDirection dropDownDirection, RightToLeft rightToLeft) { + switch (dropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + return ToolStripDropDownDirection.AboveRight; + case ToolStripDropDownDirection.AboveRight: + return ToolStripDropDownDirection.AboveLeft; + case ToolStripDropDownDirection.BelowRight: + return ToolStripDropDownDirection.BelowLeft; + case ToolStripDropDownDirection.BelowLeft: + return ToolStripDropDownDirection.BelowRight; + case ToolStripDropDownDirection.Right: + return ToolStripDropDownDirection.Left; + case ToolStripDropDownDirection.Left: + return ToolStripDropDownDirection.Right; + } + Debug.Fail("Why are we here"); + + // dont expect it to come to this but just in case here are the real defaults. + if (IsOnDropDown) { + return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + } + else { + return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight; + } + + + } + + /// + /// + /// Shows the DropDown, if one is set. + /// + public void ShowDropDown() { + this.ShowDropDown(false); + } + + internal void ShowDropDown(bool mousePush) { + this.ShowDropDownInternal(); + ToolStripDropDownMenu menu = this.dropDown as ToolStripDropDownMenu; + if (menu != null) { + if (!mousePush) { + menu.ResetScrollPosition(); + } + menu.RestoreScrollPosition(); + } + } + + private void ShowDropDownInternal() { + + if (this.dropDown == null || (!this.dropDown.Visible)) { + // VSWhidbey 469145 we want to show if there's no dropdown + // or if the dropdown is not visible. + OnDropDownShow(EventArgs.Empty); + } + + // the act of setting the drop down visible the first time sets the parent + // it seems that GetVisibleCore returns true if your parent is null. + + if (this.dropDown != null && !this.dropDown.Visible) { + + if (this.dropDown.IsAutoGenerated && this.DropDownItems.Count <= 0) { + return; // this is a no-op for autogenerated drop downs. + } + + if (this.DropDown == this.ParentInternal) { + throw new InvalidOperationException(SR.GetString(SR.ToolStripShowDropDownInvalidOperation)); + } + + this.dropDown.OwnerItem = this; + this.dropDown.Location = DropDownLocation; + this.dropDown.Show(); + this.Invalidate(); + + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + AccessibilityNotifyClients(AccessibleEvents.NameChange); + } + } + } + + private bool ShouldSerializeDropDown() { + return dropDown != null && !dropDown.IsAutoGenerated; + } + + private bool ShouldSerializeDropDownDirection() { + return (toolStripDropDownDirection != ToolStripDropDownDirection.Default); + } + + private bool ShouldSerializeDropDownItems() { + return (dropDown != null && dropDown.IsAutoGenerated); + } + + internal override void OnKeyboardToolTipHook(ToolTip toolTip) { + base.OnKeyboardToolTipHook(toolTip); + KeyboardToolTipStateMachine.Instance.Hook(this.DropDown, toolTip); + } + + internal override void OnKeyboardToolTipUnhook(ToolTip toolTip) { + base.OnKeyboardToolTipUnhook(toolTip); + KeyboardToolTipStateMachine.Instance.Unhook(this.DropDown, toolTip); + } + + // This is de facto behind the 4.8/EnableDpiMessaging/ToolStrip HighDpi quirks. + internal override void ToolStrip_RescaleConstants(int oldDpi, int newDpi) { + RescaleConstantsInternal(newDpi); + + // Traversing the tree of DropDownMenuItems non-recursively to set new + // Font (where necessary because not inherited from parent), DeviceDpi and reset the scaling. + var itemsStack = new System.Collections.Generic.Stack(); + itemsStack.Push(this); + while(itemsStack.Count > 0) { + var item = itemsStack.Pop(); + + if (item.dropDown != null) { + // The following does not get set, since dropDown has no parent/is not part of the + // controls collection, so this gets never called through the normal inheritance chain. + item.dropDown.deviceDpi = newDpi; + item.dropDown.ResetScaling(newDpi); + + foreach (ToolStripItem childItem in item.DropDown.Items) { + if (childItem == null) continue; + + // Checking if font was inherited from parent. + Font local = childItem.Font; + if (!local.Equals(childItem.OwnerItem?.Font)) { + var factor = (float)newDpi / oldDpi; + childItem.Font = new Font(local.FontFamily, local.Size * factor, local.Style, + local.Unit, local.GdiCharSet, local.GdiVerticalFont); + } + + childItem.DeviceDpi = newDpi; + + if (typeof(ToolStripDropDownItem).IsAssignableFrom(childItem.GetType())) { + if (((ToolStripDropDownItem)childItem).dropDown != null) { + itemsStack.Push((ToolStripDropDownItem)childItem); + } + } + } + } + } + + // It's important to call the base class method only AFTER we processed all DropDown items, + // because we need the new DPI in place, before a Font change triggers new layout calc. + base.ToolStrip_RescaleConstants(oldDpi, newDpi); + } + } + + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripDropDownItemAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject { + private ToolStripDropDownItem owner; + /// + public ToolStripDropDownItemAccessibleObject(ToolStripDropDownItem item) : base(item) { + owner = item; + } + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.MenuItem; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ToolStripDropDownItem item = Owner as ToolStripDropDownItem; + if (item != null && item.HasDropDownItems) { + item.ShowDropDown(); + } + else { + base.DoDefaultAction(); + } + + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3 && owner.Parent != null && (owner.Parent.IsInDesignMode || owner.Parent.IsTopInDesignMode)) { + return false; + } + + if (owner!= null && AccessibilityImprovements.Level1 ) { + return true; + } + else { + return base.IsIAccessibleExSupported(); + } + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId && owner.HasDropDownItems) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsOffscreenPropertyId && owner != null && owner.Owner is ToolStripDropDown) { + return !((ToolStripDropDown)owner.Owner).Visible; + } + + return base.GetPropertyValue(propertyID); + } + + internal override void Expand() { + DoDefaultAction(); + } + + internal override void Collapse() { + if (owner != null && owner.DropDown != null && owner.DropDown.Visible) { + owner.DropDown.Close(); + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return owner.DropDown.Visible ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + public override AccessibleObject GetChild(int index) { + if ((owner == null) || !owner.HasDropDownItems) { + return null; + } + return owner.DropDown.AccessibilityObject.GetChild(index); + + } + public override int GetChildCount() { + if ((owner == null) || !owner.HasDropDownItems) { + return -1; + } + + // Do not expose child items when the submenu is collapsed to prevent Narrator from announcing + // invisible menu items when Narrator is in item's mode (CAPSLOCK + Arrow Left/Right) or + // in scan mode (CAPSLOCK + Space) + if (AccessibilityImprovements.Level3 && ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) { + return 0; + } + + if (owner.DropDown.LayoutRequired) { + LayoutTransaction.DoLayout(owner.DropDown, owner.DropDown, PropertyNames.Items); + } + return owner.DropDown.AccessibilityObject.GetChildCount(); + + } + + internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if ((owner == null) || (owner.DropDownItems == null)) { + return -1; + } + + for (int i = 0; i < owner.DropDownItems.Count; i++) { + if (owner.DropDownItems[i].Available && child.Owner == owner.DropDownItems[i]) { + return i; + } + } + + return -1; + } + + /// + /// Gets the number of children belonging to an accessible object. + /// + /// The number of children. + internal int GetChildFragmentCount() { + if ((owner == null) || (owner.DropDownItems == null)) { + return -1; + } + + int count = 0; + for (int i = 0; i < owner.DropDownItems.Count; i++) { + if (owner.DropDownItems[i].Available) { + count++; + } + } + + return count; + } + + internal AccessibleObject GetChildFragment(int index) { + var toolStripAccessibleObject = owner.DropDown.AccessibilityObject as ToolStrip.ToolStripAccessibleObject; + if (toolStripAccessibleObject != null) { + return toolStripAccessibleObject.GetChildFragment(index); + } + + return null; + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (owner == null || owner.DropDown == null) { + return null; + } + + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildCount(); + if (childCount > 0) { + return GetChildFragment(0); + } + + return null; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildCount(); + if (childCount > 0) { + return GetChildFragment(childCount - 1); + } + + return null; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + ToolStripDropDown dropDown = owner.Owner as ToolStripDropDown; + + if (dropDown == null) { + break; + } + int index = dropDown.Items.IndexOf(owner); + + if (index == -1) { + Debug.Fail("No item matched the index?"); + return null; + } + + index += direction == UnsafeNativeMethods.NavigateDirection.NextSibling ? 1 : -1; + + if (index >= 0 && index < dropDown.Items.Count) { + var item = dropDown.Items[index]; + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs.back b/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs.back new file mode 100644 index 000000000..559d32f89 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownItem.cs.back @@ -0,0 +1,973 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel; + using System.Diagnostics; + using System.Security.Permissions; + using System.Windows.Forms.Layout; + + /// + /// + /// Base class for ToolStripItems that display DropDown windows. + /// + [Designer("System.Windows.Forms.Design.ToolStripMenuItemDesigner, " + AssemblyRef.SystemDesign)] + [DefaultProperty("DropDownItems")] + public abstract class ToolStripDropDownItem : ToolStripItem { + + private ToolStripDropDown dropDown = null; + private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default; + private static readonly object EventDropDownShow = new object(); + private static readonly object EventDropDownHide = new object(); + private static readonly object EventDropDownOpened = new object(); + private static readonly object EventDropDownClosed = new object(); + private static readonly object EventDropDownItemClicked = new object(); + + /// + /// + /// Protected ctor so you can't create one of these without deriving from it. + /// + protected ToolStripDropDownItem() { + } + + protected ToolStripDropDownItem(string text, Image image, EventHandler onClick) : base(text, image, onClick) { + } + + protected ToolStripDropDownItem(string text, Image image, EventHandler onClick, string name) : base(text, image, onClick, name) { + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripDropDownItem(string text, Image image, params ToolStripItem[] dropDownItems) : this(text, image, (EventHandler)null) { + if (dropDownItems != null) { + this.DropDownItems.AddRange(dropDownItems); + } + } + + + /// + /// + /// The ToolStripDropDown that will be displayed when this item is clicked. + /// + [ + TypeConverter(typeof(ReferenceConverter)), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripDropDownDescr) + ] + public ToolStripDropDown DropDown { + get { + if (dropDown == null) { + DropDown = CreateDefaultDropDown(); + if (!(this is ToolStripOverflowButton)) + { + dropDown.SetAutoGeneratedInternal(true); + } + + if (ParentInternal != null) { + dropDown.ShowItemToolTips = ParentInternal.ShowItemToolTips; + } + } + return dropDown; + } + set { + if (dropDown != value) { + + if (dropDown != null) { + dropDown.Opened -= new EventHandler(DropDown_Opened); + dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + dropDown.UnassignDropDownItem(); + } + + dropDown = value; + if (dropDown != null) { + dropDown.Opened += new EventHandler(DropDown_Opened); + dropDown.Closed += new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked += new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + dropDown.AssignToDropDownItem(); + } + + } + + + } + } + + // the area which activates the dropdown. + internal virtual Rectangle DropDownButtonArea { + get { return this.Bounds; } + } + + [Browsable(false)] + [SRDescription(SR.ToolStripDropDownItemDropDownDirectionDescr)] + [SRCategory(SR.CatBehavior)] + public ToolStripDropDownDirection DropDownDirection { + get { + if (toolStripDropDownDirection == ToolStripDropDownDirection.Default) { + ToolStrip parent = ParentInternal; + if (parent != null) { + ToolStripDropDownDirection dropDownDirection = parent.DefaultDropDownDirection; + if (OppositeDropDownAlign || this.RightToLeft != parent.RightToLeft && (this.RightToLeft != RightToLeft.Inherit)) { + dropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, RightToLeft); + } + + if (IsOnDropDown) { + // we gotta make sure that we dont collide with the existing menu. + Rectangle bounds = GetDropDownBounds(dropDownDirection); + Rectangle ownerItemBounds = new Rectangle(TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), Size); + Rectangle intersectionBetweenChildAndParent = Rectangle.Intersect(bounds, ownerItemBounds); + + // grab the intersection + if (intersectionBetweenChildAndParent.Width >= 2) { + RightToLeft toggledRightToLeft = (RightToLeft == RightToLeft.Yes) ? RightToLeft.No : RightToLeft.Yes; + ToolStripDropDownDirection newDropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, toggledRightToLeft); + + // verify that changing the dropdown direction actually causes less intersection. + int newIntersectionWidth = Rectangle.Intersect(GetDropDownBounds(newDropDownDirection), ownerItemBounds).Width; + if (newIntersectionWidth < intersectionBetweenChildAndParent.Width) { + dropDownDirection = newDropDownDirection; + } + } + + } + return dropDownDirection; + + } + } + + // someone has set a custom override + return toolStripDropDownDirection; + + } + set { + // cant use Enum.IsValid as its not sequential + switch (value) { + case ToolStripDropDownDirection.AboveLeft: + case ToolStripDropDownDirection.AboveRight: + case ToolStripDropDownDirection.BelowLeft: + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.Left: + case ToolStripDropDownDirection.Right: + case ToolStripDropDownDirection.Default: + break; + default: + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection)); + } + + if (toolStripDropDownDirection != value) { + toolStripDropDownDirection = value; + if (HasDropDownItems && DropDown.Visible) { + DropDown.Location = DropDownLocation; + } + } + } + } + + + /// + /// + /// Occurs when the dropdown is closed + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownClosedDecr) + ] + public event EventHandler DropDownClosed { + add { + Events.AddHandler(EventDropDownClosed, value); + } + remove { + Events.RemoveHandler(EventDropDownClosed, value); + } + } + + /// + internal protected virtual Point DropDownLocation + { + get { + + if (ParentInternal == null || !HasDropDownItems) { + return Point.Empty; + } + ToolStripDropDownDirection dropDownDirection = DropDownDirection; + return GetDropDownBounds(dropDownDirection).Location; + } + } + + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownOpeningDescr) + ] + public event EventHandler DropDownOpening { + add { + Events.AddHandler(EventDropDownShow, value); + } + remove { + Events.RemoveHandler(EventDropDownShow, value); + } + } + /// + /// + /// Occurs when the dropdown is opened + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripDropDownOpenedDescr) + ] + public event EventHandler DropDownOpened { + add { + Events.AddHandler(EventDropDownOpened, value); + } + remove { + Events.RemoveHandler(EventDropDownOpened, value); + } + } + + /// + /// + /// Returns the DropDown's items collection. + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRCategory(SR.CatData), + SRDescription(SR.ToolStripDropDownItemsDescr) + ] + public ToolStripItemCollection DropDownItems { + get { + return DropDown.Items; + } + } + + /// + /// + /// Occurs when the dropdown is opened + /// + [SRCategory(SR.CatAction)] + public event ToolStripItemClickedEventHandler DropDownItemClicked { + add { + Events.AddHandler(EventDropDownItemClicked, value); + } + remove { + Events.RemoveHandler(EventDropDownItemClicked, value); + } + } + + /// + [Browsable(false)] + public virtual bool HasDropDownItems { + get { + //VSWhidbey 354665: use count of visible DisplayedItems instead so that we take into account things that arent visible + return (dropDown != null) && dropDown.HasVisibleItems; + } + } + + /// + [Browsable(false)] + public bool HasDropDown { + get { return dropDown != null; } + } + + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool Pressed { + get { + // + if (dropDown != null) { + if (DropDown.AutoClose || !IsInDesignMode || (IsInDesignMode && !IsOnDropDown)) { + return DropDown.OwnerItem == this && DropDown.Visible; + } + } + return base.Pressed; + } + } + + internal virtual bool OppositeDropDownAlign { + get { return false; } + } + + + internal virtual void AutoHide(ToolStripItem otherItemBeingSelected) { + HideDropDown(); + } + + /// + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripDropDownItemAccessibleObject(this); + } + + /// + protected virtual ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripDropDown(this, true); + } + + private Rectangle DropDownDirectionToDropDownBounds(ToolStripDropDownDirection dropDownDirection, Rectangle dropDownBounds) { + Point offset = Point.Empty; + + switch (dropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + offset.X = -dropDownBounds.Width + this.Width; + offset.Y = -dropDownBounds.Height + 1; + break; + case ToolStripDropDownDirection.AboveRight: + offset.Y = -dropDownBounds.Height + 1; + break; + case ToolStripDropDownDirection.BelowRight: + offset.Y = this.Height - 1; + break; + case ToolStripDropDownDirection.BelowLeft: + offset.X = -dropDownBounds.Width + this.Width; + offset.Y = this.Height - 1; + break; + case ToolStripDropDownDirection.Right: + offset.X = this.Width; + if (!IsOnDropDown) { + // overlap the toplevel toolstrip + offset.X -= 1; + } + break; + + case ToolStripDropDownDirection.Left: + offset.X = -dropDownBounds.Width; + break; + } + + Point itemScreenLocation = this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + dropDownBounds.Location = new Point(itemScreenLocation.X + offset.X, itemScreenLocation.Y + offset.Y); + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds); + return dropDownBounds; + } + + + private void DropDown_Closed(object sender, ToolStripDropDownClosedEventArgs e) { + OnDropDownClosed(EventArgs.Empty); + } + + private void DropDown_Opened(object sender, EventArgs e) { + OnDropDownOpened(EventArgs.Empty); + } + + private void DropDown_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { + OnDropDownItemClicked(e); + } + /// + /// + /// Make sure we unhook dropdown events. + /// + protected override void Dispose(bool disposing) + { + if (this.dropDown != null) { + dropDown.Opened -= new EventHandler(DropDown_Opened); + dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed); + dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked); + + if (disposing && dropDown.IsAutoGenerated) { + // if we created the dropdown, dispose it and its children. + dropDown.Dispose(); + dropDown = null; + } + } + base.Dispose(disposing); + } + + + private Rectangle GetDropDownBounds(ToolStripDropDownDirection dropDownDirection) { + + Rectangle dropDownBounds = new Rectangle(Point.Empty, DropDown.GetSuggestedSize()); + // calculate the offset from the upper left hand corner of the item. + dropDownBounds = DropDownDirectionToDropDownBounds(dropDownDirection, dropDownBounds); + + // we should make sure we dont obscure the owner item. + Rectangle itemScreenBounds = new Rectangle(this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), this.Size); + + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Height > 1) { + + bool rtl = (RightToLeft == RightToLeft.Yes); + + // try positioning to the left + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) { + dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left, dropDownBounds); + } + + // try positioning to the right + if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) { + dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right, dropDownBounds); + } + } + + return dropDownBounds; + + } + + + + /// + /// + /// Hides the DropDown, if it is visible. + /// + public void HideDropDown() { + // consider - CloseEventArgs to prevent shutting down. + OnDropDownHide(EventArgs.Empty); + + if (this.dropDown != null && this.dropDown.Visible) { + DropDown.Visible = false; + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + AccessibilityNotifyClients(AccessibleEvents.NameChange); + } + } + } + + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + if (dropDown != null) { + dropDown.OnOwnerItemFontChanged(EventArgs.Empty); + } + } + + + /// + protected override void OnBoundsChanged() { + base.OnBoundsChanged(); + //Reset the Bounds... + if (this.dropDown != null && this.dropDown.Visible) + { + this.dropDown.Bounds = GetDropDownBounds(DropDownDirection); + } + } + + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + if (HasDropDownItems) { + // only perform a layout on a visible dropdown - otherwise clear the preferred size cache. + if (DropDown.Visible) { + LayoutTransaction.DoLayout(DropDown, this, PropertyNames.RightToLeft); + } + else { + CommonProperties.xClearPreferredSizeCache(DropDown); + DropDown.LayoutRequired = true; + } + } + } + + + internal override void OnImageScalingSizeChanged(EventArgs e) { + base.OnImageScalingSizeChanged(e); + if (HasDropDown && DropDown.IsAutoGenerated) { + DropDown.DoLayoutIfHandleCreated(new ToolStripItemEventArgs(this)); + } + } + /// + /// + /// Called as a response to HideDropDown + /// + protected virtual void OnDropDownHide(EventArgs e) { + this.Invalidate(); + + EventHandler handler = (EventHandler)Events[EventDropDownHide]; + if (handler != null) handler(this, e); + } + /// + /// + /// Last chance to stick in the DropDown before it is shown. + /// + protected virtual void OnDropDownShow(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventDropDownShow]; + if (handler != null) { + handler(this, e); + } + } + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownOpened(System.EventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + + if (DropDown.OwnerItem == this) { + EventHandler handler = (EventHandler)Events[EventDropDownOpened]; + if (handler != null) handler(this, e); + } + } + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownClosed(System.EventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + this.Invalidate(); + + if (DropDown.OwnerItem == this) { + EventHandler handler = (EventHandler)Events[EventDropDownClosed]; + if (handler != null) handler(this, e); + + if (!DropDown.IsAutoGenerated) { + DropDown.OwnerItem = null; + } + } + + } + + + + /// + /// + /// called when the default item is clicked + /// + protected internal virtual void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) { + // only send the event if we're the thing that currently owns the DropDown. + + if (DropDown.OwnerItem == this) { + ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventDropDownItemClicked]; + if (handler != null) handler(this, e); + } + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + if (HasDropDownItems) { + return DropDown.ProcessCmdKeyInternal(ref m, keyData); + } + return base.ProcessCmdKey(ref m, keyData); + } + + + /// + [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] + protected internal override bool ProcessDialogKey(Keys keyData) { + Keys keyCode = (Keys)keyData & Keys.KeyCode; + + if (HasDropDownItems) { + + // VSWhidbey 478068: items on the overflow should have the same kind of keyboard handling as a toplevel + bool isToplevel = (!IsOnDropDown || IsOnOverflow); + + + if (isToplevel && (keyCode == Keys.Down || keyCode == Keys.Up || keyCode == Keys.Enter || (SupportsSpaceKey && keyCode == Keys.Space))) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from toplevel item"); + + if (Enabled || DesignMode) { + // |__[ * File ]_____| * is where you are. Up or down arrow hit should expand menu + this.ShowDropDown(); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + this.DropDown.SelectNextToolStripItem(null, true); + }// else eat the key + return true; + + } + else if (!isToplevel) { + + + // if we're on a DropDown - then cascade out. + bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0); + bool forward = ((keyCode == Keys.Enter) || (SupportsSpaceKey && keyCode == Keys.Space)); + forward = (forward || (menusCascadeRight && keyCode == Keys.Left) || (!menusCascadeRight && keyCode == Keys.Right)); + + + if (forward) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from NON-toplevel item"); + + if (Enabled || DesignMode) { + this.ShowDropDown(); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + this.DropDown.SelectNextToolStripItem(null, true); + } // else eat the key + return true; + } + + } + } + + + if (IsOnDropDown) { + + bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0); + bool backward = ((menusCascadeRight && keyCode == Keys.Right) || (!menusCascadeRight && keyCode == Keys.Left)); + + if (backward) { + + + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] close submenu from NON-toplevel item"); + + // we're on a drop down but we're heading back up the chain. + // remember to select the item that displayed this dropdown. + ToolStripDropDown parent = GetCurrentParentDropDown(); + if (parent != null && !parent.IsFirstDropDown) { + // we're walking back up the dropdown chain. + parent.SetCloseReason(ToolStripDropDownCloseReason.Keyboard); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + parent.SelectPreviousToolStrip(); + return true; + } + // else if (parent.IsFirstDropDown) + // the base handling (ToolStripDropDown.ProcessArrowKey) will perform auto-expansion of + // the previous item in the menu. + + } + } + + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] ddi calling base"); + return base.ProcessDialogKey(keyData); + + } + + private ToolStripDropDownDirection RTLTranslateDropDownDirection(ToolStripDropDownDirection dropDownDirection, RightToLeft rightToLeft) { + switch (dropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + return ToolStripDropDownDirection.AboveRight; + case ToolStripDropDownDirection.AboveRight: + return ToolStripDropDownDirection.AboveLeft; + case ToolStripDropDownDirection.BelowRight: + return ToolStripDropDownDirection.BelowLeft; + case ToolStripDropDownDirection.BelowLeft: + return ToolStripDropDownDirection.BelowRight; + case ToolStripDropDownDirection.Right: + return ToolStripDropDownDirection.Left; + case ToolStripDropDownDirection.Left: + return ToolStripDropDownDirection.Right; + } + Debug.Fail("Why are we here"); + + // dont expect it to come to this but just in case here are the real defaults. + if (IsOnDropDown) { + return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right; + } + else { + return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight; + } + + + } + + /// + /// + /// Shows the DropDown, if one is set. + /// + public void ShowDropDown() { + this.ShowDropDown(false); + } + + internal void ShowDropDown(bool mousePush) { + this.ShowDropDownInternal(); + ToolStripDropDownMenu menu = this.dropDown as ToolStripDropDownMenu; + if (menu != null) { + if (!mousePush) { + menu.ResetScrollPosition(); + } + menu.RestoreScrollPosition(); + } + } + + private void ShowDropDownInternal() { + + if (this.dropDown == null || (!this.dropDown.Visible)) { + // VSWhidbey 469145 we want to show if there's no dropdown + // or if the dropdown is not visible. + OnDropDownShow(EventArgs.Empty); + } + + // the act of setting the drop down visible the first time sets the parent + // it seems that GetVisibleCore returns true if your parent is null. + + if (this.dropDown != null && !this.dropDown.Visible) { + + if (this.dropDown.IsAutoGenerated && this.DropDownItems.Count <= 0) { + return; // this is a no-op for autogenerated drop downs. + } + + if (this.DropDown == this.ParentInternal) { + throw new InvalidOperationException(SR.GetString(SR.ToolStripShowDropDownInvalidOperation)); + } + + this.dropDown.OwnerItem = this; + this.dropDown.Location = DropDownLocation; + this.dropDown.Show(); + this.Invalidate(); + + if (AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + AccessibilityNotifyClients(AccessibleEvents.NameChange); + } + } + } + + private bool ShouldSerializeDropDown() { + return dropDown != null && !dropDown.IsAutoGenerated; + } + + private bool ShouldSerializeDropDownDirection() { + return (toolStripDropDownDirection != ToolStripDropDownDirection.Default); + } + + private bool ShouldSerializeDropDownItems() { + return (dropDown != null && dropDown.IsAutoGenerated); + } + + internal override void OnKeyboardToolTipHook(ToolTip toolTip) { + base.OnKeyboardToolTipHook(toolTip); + KeyboardToolTipStateMachine.Instance.Hook(this.DropDown, toolTip); + } + + internal override void OnKeyboardToolTipUnhook(ToolTip toolTip) { + base.OnKeyboardToolTipUnhook(toolTip); + KeyboardToolTipStateMachine.Instance.Unhook(this.DropDown, toolTip); + } + + // This is de facto behind the 4.8/EnableDpiMessaging/ToolStrip HighDpi quirks. + internal override void ToolStrip_RescaleConstants(int oldDpi, int newDpi) { + RescaleConstantsInternal(newDpi); + + // Traversing the tree of DropDownMenuItems non-recursively to set new + // Font (where necessary because not inherited from parent), DeviceDpi and reset the scaling. + var itemsStack = new System.Collections.Generic.Stack(); + itemsStack.Push(this); + while(itemsStack.Count > 0) { + var item = itemsStack.Pop(); + + if (item.dropDown != null) { + // The following does not get set, since dropDown has no parent/is not part of the + // controls collection, so this gets never called through the normal inheritance chain. + item.dropDown.deviceDpi = newDpi; + item.dropDown.ResetScaling(newDpi); + + foreach (ToolStripItem childItem in item.DropDown.Items) { + if (childItem == null) continue; + + // Checking if font was inherited from parent. + Font local = childItem.Font; + if (!local.Equals(childItem.OwnerItem?.Font)) { + var factor = (float)newDpi / oldDpi; + childItem.Font = new Font(local.FontFamily, local.Size * factor, local.Style, + local.Unit, local.GdiCharSet, local.GdiVerticalFont); + } + + childItem.DeviceDpi = newDpi; + + if (typeof(ToolStripDropDownItem).IsAssignableFrom(childItem.GetType())) { + if (((ToolStripDropDownItem)childItem).dropDown != null) { + itemsStack.Push((ToolStripDropDownItem)childItem); + } + } + } + } + } + + // It's important to call the base class method only AFTER we processed all DropDown items, + // because we need the new DPI in place, before a Font change triggers new layout calc. + base.ToolStrip_RescaleConstants(oldDpi, newDpi); + } + } + + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripDropDownItemAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject { + private ToolStripDropDownItem owner; + /// + public ToolStripDropDownItemAccessibleObject(ToolStripDropDownItem item) : base(item) { + owner = item; + } + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.MenuItem; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + ToolStripDropDownItem item = Owner as ToolStripDropDownItem; + if (item != null && item.HasDropDownItems) { + item.ShowDropDown(); + } + else { + base.DoDefaultAction(); + } + + } + + internal override bool IsIAccessibleExSupported() { + if (AccessibilityImprovements.Level3 && owner.Parent != null && (owner.Parent.IsInDesignMode || owner.Parent.IsTopInDesignMode)) { + return false; + } + + if (owner!= null && AccessibilityImprovements.Level1 ) { + return true; + } + else { + return base.IsIAccessibleExSupported(); + } + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId && owner.HasDropDownItems) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsOffscreenPropertyId && owner != null && owner.Owner is ToolStripDropDown) { + return !((ToolStripDropDown)owner.Owner).Visible; + } + + return base.GetPropertyValue(propertyID); + } + + internal override void Expand() { + DoDefaultAction(); + } + + internal override void Collapse() { + if (owner != null && owner.DropDown != null && owner.DropDown.Visible) { + owner.DropDown.Close(); + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return owner.DropDown.Visible ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + public override AccessibleObject GetChild(int index) { + if ((owner == null) || !owner.HasDropDownItems) { + return null; + } + return owner.DropDown.AccessibilityObject.GetChild(index); + + } + public override int GetChildCount() { + if ((owner == null) || !owner.HasDropDownItems) { + return -1; + } + + // Do not expose child items when the submenu is collapsed to prevent Narrator from announcing + // invisible menu items when Narrator is in item's mode (CAPSLOCK + Arrow Left/Right) or + // in scan mode (CAPSLOCK + Space) + if (AccessibilityImprovements.Level3 && ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) { + return 0; + } + + if (owner.DropDown.LayoutRequired) { + LayoutTransaction.DoLayout(owner.DropDown, owner.DropDown, PropertyNames.Items); + } + return owner.DropDown.AccessibilityObject.GetChildCount(); + + } + + internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) { + if ((owner == null) || (owner.DropDownItems == null)) { + return -1; + } + + for (int i = 0; i < owner.DropDownItems.Count; i++) { + if (owner.DropDownItems[i].Available && child.Owner == owner.DropDownItems[i]) { + return i; + } + } + + return -1; + } + + /// + /// Gets the number of children belonging to an accessible object. + /// + /// The number of children. + internal int GetChildFragmentCount() { + if ((owner == null) || (owner.DropDownItems == null)) { + return -1; + } + + int count = 0; + for (int i = 0; i < owner.DropDownItems.Count; i++) { + if (owner.DropDownItems[i].Available) { + count++; + } + } + + return count; + } + + internal AccessibleObject GetChildFragment(int index) { + var toolStripAccessibleObject = owner.DropDown.AccessibilityObject as ToolStrip.ToolStripAccessibleObject; + if (toolStripAccessibleObject != null) { + return toolStripAccessibleObject.GetChildFragment(index); + } + + return null; + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (owner == null || owner.DropDown == null) { + return null; + } + + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + int childCount = GetChildCount(); + if (childCount > 0) { + return GetChildFragment(0); + } + + return null; + case UnsafeNativeMethods.NavigateDirection.LastChild: + childCount = GetChildCount(); + if (childCount > 0) { + return GetChildFragment(childCount - 1); + } + + return null; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + ToolStripDropDown dropDown = owner.Owner as ToolStripDropDown; + + if (dropDown == null) { + break; + } + int index = dropDown.Items.IndexOf(owner); + + if (index == -1) { + Debug.Fail("No item matched the index?"); + return null; + } + + index += direction == UnsafeNativeMethods.NavigateDirection.NextSibling ? 1 : -1; + + if (index >= 0 && index < dropDown.Items.Count) { + var item = dropDown.Items[index]; + var controlHostItem = item as ToolStripControlHost; + if (controlHostItem != null) { + return controlHostItem.ControlAccessibilityObject; + } + + return item.AccessibilityObject; + } + + return null; + } + + return base.FragmentNavigate(direction); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropDownMenu.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropDownMenu.cs new file mode 100644 index 000000000..799713618 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropDownMenu.cs @@ -0,0 +1,841 @@ + +namespace System.Windows.Forms { + + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Globalization; + using System.Windows.Forms.Layout; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Security; + using System.Security.Permissions; + using System.Windows.Forms.Internal; + using System.Collections.Specialized; + using System.Diagnostics.CodeAnalysis; + + /// + [Designer("System.Windows.Forms.Design.ToolStripDropDownDesigner, " + AssemblyRef.SystemDesign)] + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + public class ToolStripDropDownMenu : ToolStripDropDown { + + private static readonly Padding ImagePadding = new Padding(2); + private static readonly Padding TextPadding = new Padding(8,1,9,1); + private static readonly Padding CheckPadding = new Padding(5,2,2,2); + private static readonly Padding ArrowPadding = new Padding(0,0,8,0); + + // This is totally a UI Fudge - if we have an image or check margin with + // no image or checks in it use this - which is consistent with office + // and an image margin with a 16x16 icon in it. + private static int DefaultImageMarginWidth = 24; // 24+1px border - with scaling we add this 1px to new, scaled, field value + private static int DefaultImageAndCheckMarginWidth = 46; // 46+1px border - with scaling we add this 1px to new, scaled, field value + + private static int ArrowSize = 10; + + private Size maxItemSize = Size.Empty; + private Rectangle checkRectangle = Rectangle.Empty; + private Rectangle imageRectangle = Rectangle.Empty; + private Rectangle arrowRectangle = Rectangle.Empty; + private Rectangle textRectangle = Rectangle.Empty; + private Rectangle imageMarginBounds = Rectangle.Empty; + private int paddingToTrim = 0; + private int tabWidth = -1; + + private ToolStripScrollButton upScrollButton = null; + private ToolStripScrollButton downScrollButton = null; + private int scrollAmount = 0; + private int indexOfFirstDisplayedItem = -1; + + private BitVector32 state = new BitVector32(); + + private static readonly int stateShowImageMargin = BitVector32.CreateMask(); + private static readonly int stateShowCheckMargin = BitVector32.CreateMask(stateShowImageMargin); + private static readonly int stateMaxItemSizeValid = BitVector32.CreateMask(stateShowCheckMargin); + + private static readonly Size DefaultImageSize = new Size(16, 16); + + private Size scaledDefaultImageSize = DefaultImageSize; + private int scaledDefaultImageMarginWidth = DefaultImageMarginWidth + 1; // 1px for border + private int scaledDefaultImageAndCheckMarginWidth = DefaultImageAndCheckMarginWidth + 1; // 1px for border + private Padding scaledImagePadding = ImagePadding; + private Padding scaledTextPadding = TextPadding; + private Padding scaledCheckPadding = CheckPadding; + private Padding scaledArrowPadding = ArrowPadding; + private int scaledArrowSize = ArrowSize; + + /// + /// + /// Summary of ToolStripDropDown. + /// + public ToolStripDropDownMenu(){ + } + + /// + /// Constructor to autogenerate + /// + internal ToolStripDropDownMenu(ToolStripItem ownerItem, bool isAutoGenerated) : base(ownerItem, isAutoGenerated) { + if (DpiHelper.IsScalingRequired) { + scaledDefaultImageSize = DpiHelper.LogicalToDeviceUnits(DefaultImageSize); + scaledDefaultImageMarginWidth = DpiHelper.LogicalToDeviceUnitsX(DefaultImageMarginWidth) + 1; // 1px for border + scaledDefaultImageAndCheckMarginWidth = DpiHelper.LogicalToDeviceUnitsX(DefaultImageAndCheckMarginWidth) + 1; // 1px for border + + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledImagePadding = DpiHelper.LogicalToDeviceUnits(ImagePadding); + scaledTextPadding = DpiHelper.LogicalToDeviceUnits(TextPadding); + scaledCheckPadding = DpiHelper.LogicalToDeviceUnits(CheckPadding); + scaledArrowPadding = DpiHelper.LogicalToDeviceUnits(ArrowPadding); + scaledArrowSize = DpiHelper.LogicalToDeviceUnitsX(ArrowSize); + } + } + } + + internal override bool AllItemsVisible { + get { + return !RequiresScrollButtons; + } + set { + RequiresScrollButtons = !value; + } + } + + internal Rectangle ArrowRectangle { + get { + return arrowRectangle; + } + } + + internal Rectangle CheckRectangle { + get { + return checkRectangle; + } + } + + /// + protected override Padding DefaultPadding { + get { + RightToLeft rightToLeft = RightToLeft; + + int textPadding = (rightToLeft == RightToLeft.Yes) ? scaledTextPadding.Right : scaledTextPadding.Left; + int padding = (ShowCheckMargin || ShowImageMargin) ? textPadding + ImageMargin.Width : textPadding; + + // scooch in all the items by the margin. + if (rightToLeft == RightToLeft.Yes) { + return new Padding(1,2,padding,2); + } + return new Padding(padding,2,1,2); + } + } + + /// + public override Rectangle DisplayRectangle { + get { + Rectangle rect = base.DisplayRectangle; + if (GetToolStripState(STATE_SCROLLBUTTONS)) { + + rect.Y += UpScrollButton.Height + UpScrollButton.Margin.Vertical; + rect.Height -= UpScrollButton.Height + UpScrollButton.Margin.Vertical + DownScrollButton.Height + DownScrollButton.Margin.Vertical; + // Because we're going to draw the scroll buttons on top of the padding, we need to add it back in here. + rect = LayoutUtils.InflateRect(rect, new Padding(0, this.Padding.Top, 0, this.Padding.Bottom)); + } + return rect; + } + } + + private ToolStripScrollButton DownScrollButton { + get { + if (downScrollButton == null) { + downScrollButton = new ToolStripScrollButton(false); + downScrollButton.ParentInternal = this; + } + return downScrollButton; + } + } + /// + /// the rectangle representing + /// + internal Rectangle ImageRectangle { + get { + return imageRectangle; + } + } + + internal int PaddingToTrim { + get { + return paddingToTrim; + } + set { + if (paddingToTrim != value) { + paddingToTrim = value; + AdjustSize(); + } + + } + } + + /// + /// the rectangle representing the color stripe in the menu - this will appear as AffectedBounds + /// in the ToolStripRenderEventArgs + /// + internal Rectangle ImageMargin { + get { + imageMarginBounds.Height =this.Height; + return imageMarginBounds; + } + } + + /// + public override LayoutEngine LayoutEngine { + get { + return ToolStripDropDownLayoutEngine.LayoutInstance; + } + } + + [ + DefaultValue(ToolStripLayoutStyle.Flow) + ] + public new ToolStripLayoutStyle LayoutStyle { + get { return base.LayoutStyle; } + set { base.LayoutStyle = value; } + } + + /// + protected internal override Size MaxItemSize { + get { + if (!state[stateMaxItemSizeValid]) { + CalculateInternalLayoutMetrics(); + } + return maxItemSize; + } + } + + /// + [ + DefaultValue(true), + SRDescription(SR.ToolStripDropDownMenuShowImageMarginDescr), + SRCategory(SR.CatAppearance) + ] + public bool ShowImageMargin { + get { + return state[stateShowImageMargin]; + } + set { + if (value != state[stateShowImageMargin]) { + state[stateShowImageMargin] = value; + LayoutTransaction.DoLayout(this, this, PropertyNames.ShowImageMargin); + } + } + } + + /// + [ + DefaultValue(false), + SRDescription(SR.ToolStripDropDownMenuShowCheckMarginDescr), + SRCategory(SR.CatAppearance) + ] + public bool ShowCheckMargin { + get { + return state[stateShowCheckMargin]; + } + set { + if (value != state[stateShowCheckMargin]) { + state[stateShowCheckMargin] = value; + LayoutTransaction.DoLayout(this, this, PropertyNames.ShowCheckMargin); + } + } + } + + + internal Rectangle TextRectangle { + get { + return textRectangle; + } + } + + private ToolStripScrollButton UpScrollButton { + get { + if (upScrollButton == null) { + upScrollButton = new ToolStripScrollButton(true); + upScrollButton.ParentInternal = this; + } + return upScrollButton; + } + } + + /// this takes a native menu and builds up a managed toolstrip around it. + /// Scenario: showing the items from the SystemMenu. + /// targetWindow is the window to send WM_COMMAND, WM_SYSCOMMAND to + /// hmenu is a handle to the native menu + /// + /// + + internal static ToolStripDropDownMenu FromHMenu(IntPtr hmenu, IWin32Window targetWindow) { + ToolStripDropDownMenu managedDropDown = new ToolStripDropDownMenu(); + managedDropDown.SuspendLayout(); + + + HandleRef menuHandle = new HandleRef(null, hmenu); + int count = UnsafeNativeMethods.GetMenuItemCount(menuHandle); + + ToolStripItem itemToAdd; + + // surf through the items in the collection, building up TSMenuItems and TSSeparators + // corresponding to the native menu. + for (int i = 0; i < count; i++) { + // peek at the i'th item. + NativeMethods.MENUITEMINFO_T_RW info = new NativeMethods.MENUITEMINFO_T_RW(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW)); + info.fMask = NativeMethods.MIIM_FTYPE; + info.fType = NativeMethods.MIIM_FTYPE; + UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info); + + if (info.fType == NativeMethods.MFT_SEPARATOR){ + // its a separator. + itemToAdd = new ToolStripSeparator(); + } + else { + // its a menu item... lets fish out the command id + info = new NativeMethods.MENUITEMINFO_T_RW(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW)); + info.fMask = NativeMethods.MIIM_ID; + info.fType = NativeMethods.MIIM_ID; + UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info); + + // create the managed object - toolstripmenu item knows how to grok hmenu for information. + itemToAdd = new ToolStripMenuItem(hmenu, info.wID, targetWindow); + + + // if there is a submenu fetch it. + info = new NativeMethods.MENUITEMINFO_T_RW(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW)); + info.fMask = NativeMethods.MIIM_SUBMENU; + info.fType = NativeMethods.MIIM_SUBMENU; + UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info); + + if (info.hSubMenu != IntPtr.Zero) { + // set the dropdown to be the items from the submenu + ((ToolStripMenuItem)itemToAdd).DropDown = FromHMenu(info.hSubMenu, targetWindow); + } + } + + managedDropDown.Items.Add(itemToAdd); + } + managedDropDown.ResumeLayout(); + return managedDropDown; + } + + [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // using "\t" to figure out the width of tab + private void CalculateInternalLayoutMetrics() { + + + Size maxTextSize = Size.Empty; + Size maxImageSize = Size.Empty; + Size maxCheckSize = scaledDefaultImageSize; + Size maxArrowSize = Size.Empty; + Size maxNonMenuItemSize = Size.Empty; + + // determine Text Metrics + for (int i = 0; i < Items.Count; i++) { + ToolStripItem item = Items[i]; + ToolStripMenuItem menuItem = item as ToolStripMenuItem; + + if (menuItem != null) { + + Size menuItemTextSize = menuItem.GetTextSize(); + + if (menuItem.ShowShortcutKeys) { + Size shortcutTextSize = menuItem.GetShortcutTextSize(); + if (tabWidth == -1) { + tabWidth = TextRenderer.MeasureText("\t", this.Font).Width; + } + menuItemTextSize.Width += tabWidth + shortcutTextSize.Width; + menuItemTextSize.Height = Math.Max(menuItemTextSize.Height, shortcutTextSize.Height); + } + + // we truly only care about the maximum size we find. + maxTextSize.Width = Math.Max(maxTextSize.Width, menuItemTextSize.Width); + maxTextSize.Height = Math.Max(maxTextSize.Height, menuItemTextSize.Height); + + // determine Image Metrics + Size imageSize = Size.Empty; + if (menuItem.Image != null) { + imageSize = (menuItem.ImageScaling == ToolStripItemImageScaling.SizeToFit) ? ImageScalingSize : menuItem.Image.Size; + } + + maxImageSize.Width = Math.Max(maxImageSize.Width, imageSize.Width); + maxImageSize.Height = Math.Max(maxImageSize.Height, imageSize.Height); + + if (menuItem.CheckedImage != null) { + Size checkedImageSize = menuItem.CheckedImage.Size; + maxCheckSize.Width = Math.Max(checkedImageSize.Width, maxCheckSize.Width); + maxCheckSize.Height = Math.Max(checkedImageSize.Height, maxCheckSize.Height); + } + } + else if (!(item is ToolStripSeparator)) { + maxNonMenuItemSize.Height = Math.Max(item.Bounds.Height, maxNonMenuItemSize.Height); + maxNonMenuItemSize.Width = Math.Max(item.Bounds.Width, maxNonMenuItemSize.Width); + } + } + + this.maxItemSize.Height = Math.Max(maxTextSize.Height + scaledTextPadding.Vertical, Math.Max(maxCheckSize.Height + scaledCheckPadding.Vertical, maxArrowSize.Height + scaledArrowPadding.Vertical)); + + if (ShowImageMargin) { + // only add in the image into the calculation if we're going to render it. + this.maxItemSize.Height = Math.Max(maxImageSize.Height + scaledImagePadding.Vertical, maxItemSize.Height); + } + + bool useDefaultCheckMarginWidth = (ShowCheckMargin && (maxCheckSize.Width == 0)); + bool useDefaultImageMarginWidth = (ShowImageMargin && (maxImageSize.Width == 0)); + // Always save space for an arrow + maxArrowSize = new Size(scaledArrowSize, maxItemSize.Height); + + maxTextSize.Height = maxItemSize.Height - scaledTextPadding.Vertical; + maxImageSize.Height = maxItemSize.Height - scaledImagePadding.Vertical; + maxCheckSize.Height = maxItemSize.Height - scaledCheckPadding.Vertical; + + // fixup if there are non-menu items that are larger than our normal menu items + maxTextSize.Width = Math.Max(maxTextSize.Width, maxNonMenuItemSize.Width); + + Point nextPoint = Point.Empty; + int checkAndImageMarginWidth = 0; + + int extraImageWidth = Math.Max(0, maxImageSize.Width - scaledDefaultImageSize.Width); + + if (ShowCheckMargin && ShowImageMargin) { + // double column - check margin then image margin + // default to 46px - grow if necessary. + checkAndImageMarginWidth = scaledDefaultImageAndCheckMarginWidth; + + // add in the extra space for the image... since the check size is locked down to 16x16. + checkAndImageMarginWidth += extraImageWidth; + + // align the checkmark + nextPoint = new Point(scaledCheckPadding.Left, scaledCheckPadding.Top); + checkRectangle = LayoutUtils.Align(maxCheckSize, new Rectangle(nextPoint.X, nextPoint.Y, maxCheckSize.Width, maxItemSize.Height), ContentAlignment.MiddleCenter); + + // align the image rectangle + nextPoint.X = checkRectangle.Right + scaledCheckPadding.Right + scaledImagePadding.Left; + nextPoint.Y = scaledImagePadding.Top; + imageRectangle = LayoutUtils.Align(maxImageSize, new Rectangle(nextPoint.X, nextPoint.Y, maxImageSize.Width, maxItemSize.Height), ContentAlignment.MiddleCenter); + + } + else if (ShowCheckMargin) { + // no images should be shown in a ShowCheckMargin only scenario. + // default to 24px - grow if necessary. + checkAndImageMarginWidth = scaledDefaultImageMarginWidth; + + // align the checkmark + nextPoint = new Point(1,scaledCheckPadding.Top); + // nextPoint = new Point(scaledCheckPadding.Left, scaledCheckPadding.Top); + checkRectangle = LayoutUtils.Align(maxCheckSize, new Rectangle(nextPoint.X, nextPoint.Y, checkAndImageMarginWidth, maxItemSize.Height), ContentAlignment.MiddleCenter); + + imageRectangle = Rectangle.Empty; + + } + else if (ShowImageMargin) { + // checks and images render in the same area. + + // default to 24px - grow if necessary. + checkAndImageMarginWidth = scaledDefaultImageMarginWidth; + + // add in the extra space for the image... since the check size is locked down to 16x16. + checkAndImageMarginWidth += extraImageWidth; + + // NOTE due to the Padding property, we're going to have to recalc the vertical alignment in ToolStripMenuItemInternalLayout. + // Dont fuss here over the Y, X is what's critical. + + // check and image rect are the same - take the max of the image size and the check size and align + nextPoint = new Point(1, scaledCheckPadding.Top); + checkRectangle = LayoutUtils.Align(LayoutUtils.UnionSizes(maxCheckSize,maxImageSize), new Rectangle(nextPoint.X, nextPoint.Y, checkAndImageMarginWidth-1, maxItemSize.Height), ContentAlignment.MiddleCenter); + + // align the image + imageRectangle = checkRectangle; + + } + else { + checkAndImageMarginWidth = 0; + } + nextPoint.X = checkAndImageMarginWidth+1; + + + // calculate space for image + // if we didnt have a check - make sure to ignore check padding + + // consider: should we constrain to a reasonable width? + //imageMarginBounds = new Rectangle(0, 0, Math.Max(imageMarginWidth,DefaultImageMarginWidth), this.Height); + imageMarginBounds = new Rectangle(0,0,checkAndImageMarginWidth, this.Height); + + // calculate space for shortcut and text + nextPoint.X = imageMarginBounds.Right+ scaledTextPadding.Left; + nextPoint.Y = scaledTextPadding.Top; + textRectangle = new Rectangle(nextPoint, maxTextSize); + + // calculate space for arrow + nextPoint.X = textRectangle.Right+ scaledTextPadding.Right + scaledArrowPadding.Left; + nextPoint.Y = scaledArrowPadding.Top; + arrowRectangle = new Rectangle(nextPoint, maxArrowSize); + + // calculate space required for all of these pieces + this.maxItemSize.Width = (arrowRectangle.Right + scaledArrowPadding.Right) - imageMarginBounds.Left; + + this.Padding = DefaultPadding; + int trimPadding = imageMarginBounds.Width; + + if (RightToLeft == RightToLeft.Yes) { + // reverse the rectangle alignment in RightToLeft.Yes + trimPadding += scaledTextPadding.Right; + int width = maxItemSize.Width; + checkRectangle.X = width - checkRectangle.Right; + imageRectangle.X = width - imageRectangle.Right; + textRectangle.X = width - textRectangle.Right; + arrowRectangle.X = width - arrowRectangle.Right; + imageMarginBounds.X = width - imageMarginBounds.Right; + + } + else { + trimPadding += scaledTextPadding.Left; + } + + + + // VSWhidbey 339274 - we need to make sure that the text really appears vertically centered - this can be a problem in + // systems which force the text rectangle to be odd. + + // force this to be an even height. + this.maxItemSize.Height += this.maxItemSize.Height%2; + + textRectangle.Y = LayoutUtils.VAlign(textRectangle.Size, new Rectangle(Point.Empty, maxItemSize), ContentAlignment.MiddleCenter).Y; + textRectangle.Y += (textRectangle.Height %2); // if the height is odd, push down by one px, see 339274 for picture + state[stateMaxItemSizeValid]=true; + this.PaddingToTrim = trimPadding; + + } + + internal override void ChangeSelection(ToolStripItem nextItem) { + if (nextItem != null) { + Rectangle displayRect = DisplayRectangle; + if (!displayRect.Contains(displayRect.X, nextItem.Bounds.Top) + || !displayRect.Contains(displayRect.X, nextItem.Bounds.Bottom)) { + + int delta; + if (displayRect.Y > nextItem.Bounds.Top) { + delta = nextItem.Bounds.Top - displayRect.Y; + } + else { + delta = nextItem.Bounds.Bottom - (displayRect.Y + displayRect.Height); + // Now adjust so that the item at the top isn't truncated. + int index = this.Items.IndexOf(nextItem); + while (index >= 0) { + // we need to roll back to the index which is visible + if ( (this.Items[index].Visible && displayRect.Contains(displayRect.X, this.Items[index].Bounds.Top - delta)) + || !this.Items[index].Visible) { + --index; + } + else { + break; + } + } + + if (index >= 0) { + if (displayRect.Contains(displayRect.X, this.Items[index].Bounds.Bottom - delta)) { + // We found an item which is truncated at the top. + delta += (this.Items[index].Bounds.Bottom - delta) - displayRect.Top; + } + } + } + this.ScrollInternal(delta); + this.UpdateScrollButtonStatus(); + } + } + base.ChangeSelection(nextItem); + } + + protected internal override ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) { + if (text == "-") { + return new ToolStripSeparator(); + } + else { + return new ToolStripMenuItem(text,image,onClick); + } + } + + internal override ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) { + // for up/down we dont care about flipping left/right tab should still take you down. + return GetNextItem(start, direction); + } + + internal override void Initialize() { + base.Initialize(); + this.Padding = DefaultPadding; + FlowLayoutSettings settings = FlowLayout.CreateSettings(this); + settings.FlowDirection = FlowDirection.TopDown; + state[stateShowImageMargin] = true; + } + /// + protected override void OnLayout(LayoutEventArgs e) { + if (!this.IsDisposed) + { + // We always layout as if we don't need scroll buttons. + // If we do, then we'll adjust the positions to match. + this.RequiresScrollButtons = false; + CalculateInternalLayoutMetrics(); + base.OnLayout(e); + if (!this.RequiresScrollButtons) { + this.ResetScrollPosition(); + } + } + } + + protected override void OnFontChanged(EventArgs e) { + tabWidth = -1; + base.OnFontChanged(e); + + } + /// + protected override void OnPaintBackground(PaintEventArgs e) { + base.OnPaintBackground(e); + if (ShowCheckMargin || ShowImageMargin) { + Renderer.DrawImageMargin(new ToolStripRenderEventArgs(e.Graphics, this, this.ImageMargin, SystemColors.Control)); + } + } + + internal override void ResetScaling(int newDpi) { + base.ResetScaling(newDpi); + CommonProperties.xClearPreferredSizeCache(this); + scaledDefaultImageSize = DpiHelper.LogicalToDeviceUnits(DefaultImageSize, newDpi); + scaledDefaultImageMarginWidth = DpiHelper.LogicalToDeviceUnits(DefaultImageMarginWidth, newDpi) + 1; // 1px for border + scaledDefaultImageAndCheckMarginWidth = DpiHelper.LogicalToDeviceUnits(DefaultImageAndCheckMarginWidth, newDpi) + 1; // 1px for border + + scaledImagePadding = DpiHelper.LogicalToDeviceUnits(ImagePadding, newDpi); + scaledTextPadding = DpiHelper.LogicalToDeviceUnits(TextPadding, newDpi); + scaledCheckPadding = DpiHelper.LogicalToDeviceUnits(CheckPadding, newDpi); + scaledArrowPadding = DpiHelper.LogicalToDeviceUnits(ArrowPadding, newDpi); + scaledArrowSize = DpiHelper.LogicalToDeviceUnits(ArrowSize, newDpi); + } + + internal override bool RequiresScrollButtons { + get { + return GetToolStripState(STATE_SCROLLBUTTONS); + } + set { + bool changed = (RequiresScrollButtons != value); + SetToolStripState(STATE_SCROLLBUTTONS, value); + if (changed) { + UpdateScrollButtonLocations(); + if (this.Items.Count > 0) { + int delta = this.Items[0].Bounds.Top - this.DisplayRectangle.Top; + this.ScrollInternal(delta); + this.scrollAmount -= delta; + if (value) { + RestoreScrollPosition(); + } + } + else { + this.scrollAmount = 0; + } + } + } + } + + internal void ResetScrollPosition() { + this.scrollAmount = 0; + } + + internal void RestoreScrollPosition() { + if (!RequiresScrollButtons || this.Items.Count == 0) { + return; + } + + // We don't just scroll by the amount, because that might + // cause the bottom of the menu to be blank if some items have + // been removed/hidden since the last time we were displayed. + // This also deals with items of different height, so that we don't truncate + // and items under the top scrollbar. + + Rectangle displayRectangle = this.DisplayRectangle; + int alreadyScrolled = displayRectangle.Top - this.Items[0].Bounds.Top; + + int requiredScrollAmount = this.scrollAmount - alreadyScrolled; + + int deltaToScroll = 0; + if (requiredScrollAmount > 0) { + for (int i = 0; i < this.Items.Count && deltaToScroll < requiredScrollAmount; ++i) { + if (this.Items[i].Available) { + Rectangle adjustedLastItemBounds = this.Items[this.Items.Count - 1].Bounds; + adjustedLastItemBounds.Y -= deltaToScroll; + if (displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Top) + && displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Bottom)) { + // Scrolling this amount would make the last item visible, so don't scroll any more. + break; + } + + // We use a delta between the tops, since it takes margin's and padding into account. + if (i < this.Items.Count - 1) { + deltaToScroll += this.Items[i + 1].Bounds.Top - this.Items[i].Bounds.Top; + } + else { + deltaToScroll += this.Items[i].Bounds.Height; + } + } + } + } + else { + for (int i = this.Items.Count - 1; i >= 0 && deltaToScroll > requiredScrollAmount; --i) { + if (this.Items[i].Available) { + Rectangle adjustedLastItemBounds = this.Items[0].Bounds; + adjustedLastItemBounds.Y -= deltaToScroll; + if (displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Top) + && displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Bottom)) { + // Scrolling this amount would make the last item visible, so don't scroll any more. + break; + } + + // We use a delta between the tops, since it takes margin's and padding into account. + if (i > 0) { + deltaToScroll -= this.Items[i].Bounds.Top - this.Items[i - 1].Bounds.Top; + } + else { + deltaToScroll -= this.Items[i].Bounds.Height; + } + } + } + } + this.ScrollInternal(deltaToScroll); + this.scrollAmount = this.DisplayRectangle.Top - this.Items[0].Bounds.Top; + UpdateScrollButtonLocations(); + } + + + internal override void ScrollInternal(int delta) { + base.ScrollInternal(delta); + this.scrollAmount += delta; + } + + internal void ScrollInternal(bool up) { + UpdateScrollButtonStatus(); + + // calling this to get ScrollWindowEx. In actuality it does nothing + // to change the display rect! + int delta; + if (this.indexOfFirstDisplayedItem == -1 || this.indexOfFirstDisplayedItem >= this.Items.Count) { + Debug.Fail("Why wasn't 'UpdateScrollButtonStatus called'? We don't have the item to scroll by"); + int menuHeight = SystemInformation.MenuHeight; + + delta = up ? -menuHeight : menuHeight; + } + else { + if (up) { + if (this.indexOfFirstDisplayedItem == 0) { + Debug.Fail("We're trying to scroll up, but the top item is displayed!!!"); + delta = 0; + } + else { + ToolStripItem itemTop = this.Items[this.indexOfFirstDisplayedItem - 1]; + ToolStripItem itemBottom = this.Items[this.indexOfFirstDisplayedItem]; + // We use a delta between the tops, since it takes margin's and padding into account. + delta = itemTop.Bounds.Top - itemBottom.Bounds.Top; + } + } + else { + if (this.indexOfFirstDisplayedItem == this.Items.Count - 1) { + Debug.Fail("We're trying to scroll down, but the top item is displayed!!!"); + delta = 0; + } + + ToolStripItem itemTop = this.Items[this.indexOfFirstDisplayedItem]; + ToolStripItem itemBottom = this.Items[this.indexOfFirstDisplayedItem + 1]; + // We use a delta between the tops, since it takes margin's and padding into account. + delta = itemBottom.Bounds.Top - itemTop.Bounds.Top; + } + } + ScrollInternal(delta); + UpdateScrollButtonLocations(); + } + + /// + protected override void SetDisplayedItems() { + + base.SetDisplayedItems(); + if (RequiresScrollButtons) { + + this.DisplayedItems.Add(UpScrollButton); + this.DisplayedItems.Add(DownScrollButton); + UpdateScrollButtonLocations(); + UpScrollButton.Visible = true; + DownScrollButton.Visible = true; + } + else { + UpScrollButton.Visible = false; + DownScrollButton.Visible = false; + } + } + + private void UpdateScrollButtonLocations() { + + if (GetToolStripState(STATE_SCROLLBUTTONS)) { + Size upSize = UpScrollButton.GetPreferredSize(Size.Empty); + // + Point upLocation = new Point(1, 0); + + UpScrollButton.SetBounds(new Rectangle(upLocation, upSize)); + + Size downSize = DownScrollButton.GetPreferredSize(Size.Empty); + int height = GetDropDownBounds(this.Bounds).Height; + + Point downLocation = new Point(1, height - downSize.Height); + DownScrollButton.SetBounds(new Rectangle(downLocation, downSize)); + UpdateScrollButtonStatus(); + } + + } + + private void UpdateScrollButtonStatus() { + Rectangle displayRectangle = this.DisplayRectangle; + + this.indexOfFirstDisplayedItem = -1; + int minY = int.MaxValue, maxY = 0; + + for (int i = 0; i < this.Items.Count; ++i) { + ToolStripItem item = this.Items[i]; + if (UpScrollButton == item) { + continue; + } + if (DownScrollButton == item) { + continue; + } + + if (!item.Available) { + continue; + } + + if (this.indexOfFirstDisplayedItem == -1 && displayRectangle.Contains(displayRectangle.X, item.Bounds.Top)) { + this.indexOfFirstDisplayedItem = i; + } + + minY = Math.Min(minY, item.Bounds.Top); + maxY = Math.Max(maxY, item.Bounds.Bottom); + } + + UpScrollButton.Enabled = !displayRectangle.Contains(displayRectangle.X, minY); + DownScrollButton.Enabled = !displayRectangle.Contains(displayRectangle.X, maxY); + } + + internal sealed class ToolStripDropDownLayoutEngine : FlowLayout { + + public static ToolStripDropDownLayoutEngine LayoutInstance = new ToolStripDropDownLayoutEngine(); + + internal override Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) { + Size preferredSize = base.GetPreferredSize(container, proposedConstraints); + ToolStripDropDownMenu dropDownMenu = container as ToolStripDropDownMenu; + if (dropDownMenu != null) { + preferredSize.Width = dropDownMenu.MaxItemSize.Width - dropDownMenu.PaddingToTrim; + } + return preferredSize; + } + + } + } + } diff --git a/WindowsForms/Managed/System/WinForms/ToolStripDropTargetManager.cs b/WindowsForms/Managed/System/WinForms/ToolStripDropTargetManager.cs new file mode 100644 index 000000000..5af6ef549 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripDropTargetManager.cs @@ -0,0 +1,328 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Threading; + using System.Runtime.InteropServices; + using System.Security; + using System.Globalization; + + /// + /// Why do we have this class? Well it seems that RegisterDropTarget + /// requires an HWND to back it's IDropTargets. Since some ToolStripItems + /// do not have HWNDS, this guy's got to figure out who the event was + /// really supposed to go to and pass it on to it. + /// + internal class ToolStripDropTargetManager : IDropTarget { + + private IDropTarget lastDropTarget; + private ToolStrip owner; + +#if DEBUG + private bool dropTargetIsEntered; +#endif + + +#if DEBUG + internal static readonly TraceSwitch DragDropDebug = new TraceSwitch("DragDropDebug", "Debug ToolStrip DragDrop code"); +#else + internal static readonly TraceSwitch DragDropDebug; +#endif + + /// + /// Summary of ToolStripDropTargetManager. + /// + /// + public ToolStripDropTargetManager(ToolStrip owner) { + this.owner = owner; + if (owner == null) { + throw new ArgumentNullException("owner"); + } + } + + /// + /// Summary of EnsureRegistered. + /// + /// + public void EnsureRegistered(IDropTarget dropTarget) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Ensuring drop target registered"); + SetAcceptDrops(true); + } + + /// + /// Summary of EnsureUnRegistered. + /// + /// + public void EnsureUnRegistered(IDropTarget dropTarget) { + + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Attempting to unregister droptarget"); + for (int i = 0; i < owner.Items.Count; i++) { + if (owner.Items[i].AllowDrop) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "An item still has allowdrop set to true - cant unregister"); + return; // can't unregister this as a drop target unless everyone is done. + } + } + if (owner.AllowDrop || owner.AllowItemReorder) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "The ToolStrip has AllowDrop or AllowItemReorder set to true - cant unregister"); + return; // can't unregister this as a drop target if ToolStrip is still accepting drops + } + + SetAcceptDrops(false); + owner.DropTargetManager = null; + } + + /// + /// Takes a screen point and converts it into an item. May return null. + /// + /// + /// + private ToolStripItem FindItemAtPoint(int x, int y) { + return owner.GetItemAt(owner.PointToClient(new Point(x,y))); + } + /// + /// Summary of OnDragEnter. + /// + /// + public void OnDragEnter(DragEventArgs e) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "[DRAG ENTER] =============="); + + // If we are supporting Item Reordering + // and this is a ToolStripItem - snitch it. + if (owner.AllowItemReorder && e.Data.GetDataPresent(typeof(ToolStripItem))) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ItemReorderTarget taking this..."); + lastDropTarget = owner.ItemReorderDropTarget; + + } + else { + ToolStripItem item = FindItemAtPoint(e.X, e.Y); + + if ((item != null) && (item.AllowDrop)) { + // the item wants this event + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStripItem taking this: " + item.ToString()); + + lastDropTarget = ((IDropTarget)item); + } + else if (owner.AllowDrop) { + // the winbar wants this event + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStrip taking this because AllowDrop set to true."); + lastDropTarget = ((IDropTarget)owner); + + } + else { + // There could be one item that says "AllowDrop == true" which would turn + // on this drop target manager. If we're not over that particular item - then + // just null out the last drop target manager. + + // the other valid reason for being here is that we've done an AllowItemReorder + // and we dont have a ToolStripItem contain within the data (say someone drags a link + // from IE over the ToolStrip) + + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "No one wanted it."); + + lastDropTarget = null; + + } + } + if (lastDropTarget != null) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Calling OnDragEnter on target..."); +#if DEBUG + dropTargetIsEntered=true; +#endif + lastDropTarget.OnDragEnter(e); + } + } + + /// + /// Summary of OnDragOver. + /// + /// + public void OnDragOver(DragEventArgs e) { + + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "[DRAG OVER] =============="); + + IDropTarget newDropTarget = null; + + // If we are supporting Item Reordering + // and this is a ToolStripItem - snitch it. + if (owner.AllowItemReorder && e.Data.GetDataPresent(typeof(ToolStripItem))) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ItemReorderTarget taking this..."); + newDropTarget = owner.ItemReorderDropTarget; + } + else { + ToolStripItem item = FindItemAtPoint(e.X, e.Y); + + if ((item != null) && (item.AllowDrop)) { + // the item wants this event + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStripItem taking this: " + item.ToString()); + newDropTarget = ((IDropTarget)item); + } + else if (owner.AllowDrop) { + // the winbar wants this event + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "ToolStrip taking this because AllowDrop set to true."); + newDropTarget = ((IDropTarget)owner); + } + else { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "No one wanted it."); + newDropTarget = null; + } + } + + // if we've switched drop targets - then + // we need to create drag enter and leave events + if (newDropTarget != lastDropTarget) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "NEW DROP TARGET!"); + UpdateDropTarget(newDropTarget, e); + } + + // now call drag over + if (lastDropTarget != null) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Calling OnDragOver on target..."); + lastDropTarget.OnDragOver(e); + } + + } + + /// + /// Summary of OnDragLeave. + /// + /// + public void OnDragLeave(System.EventArgs e) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "[DRAG LEAVE] =============="); + + if (lastDropTarget != null) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Calling OnDragLeave on current target..."); +#if DEBUG + dropTargetIsEntered=false; +#endif + lastDropTarget.OnDragLeave(e); + + } +#if DEBUG + else { + Debug.Assert(!dropTargetIsEntered, "Why do we have an entered droptarget and NO lastDropTarget?"); + } +#endif + lastDropTarget = null; + } + + /// + /// Summary of OnDragDrop. + /// + /// + public void OnDragDrop(DragEventArgs e) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "[DRAG DROP] =============="); + + if (lastDropTarget != null) { + Debug.WriteLineIf(DragDropDebug.TraceVerbose, "Calling OnDragDrop on current target..."); + + lastDropTarget.OnDragDrop(e); + } + else { + Debug.Assert(false, "Why is lastDropTarget null?"); + } + + lastDropTarget = null; + } + + + /// + /// Used to actually register the control as a drop target. + /// + /// + /// + private void SetAcceptDrops(bool accept) { + if (owner.AllowDrop && accept) { + // if the owner has set AllowDrop to true then demand Clipboard permissions. + // else its us, and we can assert them. + IntSecurity.ClipboardRead.Demand(); + } + + + if (accept && owner.IsHandleCreated) { + try + { + if (Application.OleRequired() != System.Threading.ApartmentState.STA) + { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + if (accept) + { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Registering as drop target: " + owner.Handle.ToString()); + // Register + int n = UnsafeNativeMethods.RegisterDragDrop(new HandleRef(owner, owner.Handle), (UnsafeNativeMethods.IOleDropTarget)(new DropTarget(this))); + + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.InvariantCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_ALREADYREGISTERED) + { + throw new Win32Exception(n); + } + } + else + { + IntSecurity.ClipboardRead.Assert(); + try + { + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, "Revoking drop target: " + owner.Handle.ToString()); + + // Revoke + int n = UnsafeNativeMethods.RevokeDragDrop(new HandleRef(owner, owner.Handle)); + Debug.WriteLineIf(CompModSwitches.DragDrop.TraceInfo, " ret:" + n.ToString(CultureInfo.InvariantCulture)); + if (n != 0 && n != NativeMethods.DRAGDROP_E_NOTREGISTERED) + { + throw new Win32Exception(n); + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + } + } + catch (Exception e) + { + throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); + } + } + } + + /// + /// if we have a new active item, fire drag leave and + /// enter. This corresponds to the case where you + /// are dragging between items and havent actually + /// left the ToolStrip's client area. + /// + /// + /// + private void UpdateDropTarget(IDropTarget newTarget, DragEventArgs e) { + + if (newTarget != lastDropTarget) { + + // tell the last drag target you've left + if (lastDropTarget != null) { + OnDragLeave(new EventArgs()); + } + lastDropTarget = newTarget; + if (newTarget != null) { + DragEventArgs dragEnterArgs = new DragEventArgs(e.Data, e.KeyState, e.X, e.Y, e.AllowedEffect, e.Effect); + dragEnterArgs.Effect = DragDropEffects.None; + + // tell the next drag target you've entered + OnDragEnter(dragEnterArgs); + } + } + + } + + + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripGrip.cs b/WindowsForms/Managed/System/WinForms/ToolStripGrip.cs new file mode 100644 index 000000000..6c833115c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripGrip.cs @@ -0,0 +1,308 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.Windows.Forms.Layout; + using System.Diagnostics; + + /// + /// + + internal class ToolStripGrip : ToolStripButton { + + private Cursor oldCursor; + private int gripThickness = 0; + Point startLocation = Point.Empty; + private bool movingToolStrip = false; + private Point lastEndLocation = ToolStrip.InvalidMouseEnter; + private static Size DragSize = LayoutUtils.MaxSize; + + private static readonly Padding defaultPadding = new Padding(2); + private static readonly int gripThicknessDefault = 3; + private static readonly int gripThicknessVisualStylesEnabled = 5; + private Padding scaledDefaultPadding = defaultPadding; + private int scaledGripThickness = gripThicknessDefault; + private int scaledGripThicknessVisualStylesEnabled = gripThicknessVisualStylesEnabled; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + internal ToolStripGrip() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding); + scaledGripThickness = DpiHelper.LogicalToDeviceUnitsX(gripThicknessDefault); + scaledGripThicknessVisualStylesEnabled = DpiHelper.LogicalToDeviceUnitsX(gripThicknessVisualStylesEnabled); + } + + // if we're using XP themes we've got to be a bit thicker. + gripThickness = ToolStripManager.VisualStylesEnabled ? scaledGripThicknessVisualStylesEnabled : scaledGripThickness; + SupportsItemClick = false; + } + + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal override Padding DefaultMargin { + get { + return scaledDefaultPadding; + } + } + + public override bool CanSelect { + get { + return false; + } + } + + internal int GripThickness { + get { + return gripThickness; + } + } + + internal bool MovingToolStrip { + get { + return ((ToolStripPanelRow != null) && movingToolStrip); + } + set { + if ((movingToolStrip != value) && ParentInternal != null) { + if (value) { + // dont let grips move the toolstrip + if (ParentInternal.ToolStripPanelRow == null) { + return; + } + } + movingToolStrip = value; + lastEndLocation = ToolStrip.InvalidMouseEnter; + if (movingToolStrip) { + ((ISupportToolStripPanel)this.ParentInternal).BeginDrag(); + } + else { + ((ISupportToolStripPanel)this.ParentInternal).EndDrag(); + } + } + } + } + + private ToolStripPanelRow ToolStripPanelRow { + get { + return (ParentInternal == null) ? null : ((ISupportToolStripPanel)ParentInternal).ToolStripPanelRow; + } + } + + + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripGripAccessibleObject(this); + } + + public override Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = Size.Empty; + if (this.ParentInternal != null) { + if (this.ParentInternal.LayoutStyle == ToolStripLayoutStyle.VerticalStackWithOverflow) { + preferredSize = new Size(this.ParentInternal.Width, gripThickness); + } + else { + preferredSize = new Size(gripThickness, this.ParentInternal.Height); + + } + + } + // Constrain ourselves + if (preferredSize.Width > constrainingSize.Width) { + preferredSize.Width = constrainingSize.Width; + } + + if (preferredSize.Height > constrainingSize.Height) { + preferredSize.Height = constrainingSize.Height; + } + + return preferredSize; + } + + private bool LeftMouseButtonIsDown() { + return (Control.MouseButtons == MouseButtons.Left) && (Control.ModifierKeys == Keys.None); + } + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + // all the grip painting should be on the ToolStrip itself. + if (ParentInternal != null) { + ParentInternal.OnPaintGrip(e); + } + } + + + /// + /// + /// + /// + protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) { + startLocation = TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + base.OnMouseDown(mea); + } + + protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { + bool leftMouseButtonDown = LeftMouseButtonIsDown(); + if (!MovingToolStrip && leftMouseButtonDown) { + + // determine if we've moved far enough such that the toolstrip + // can be considered as moving. + Point currentLocation = TranslatePoint(mea.Location, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + int deltaX = currentLocation.X - startLocation.X; + deltaX = (deltaX < 0) ? deltaX *-1 : deltaX; + + if (DragSize == LayoutUtils.MaxSize) { + DragSize = SystemInformation.DragSize; + } + + if (deltaX >= DragSize.Width) { + MovingToolStrip = true; + } + else { + int deltaY = currentLocation.Y - startLocation.Y; + deltaY = (deltaY < 0) ? deltaY *-1 : deltaY; + + if (deltaY >= DragSize.Height) { + MovingToolStrip = true; + } + } + + } + if (MovingToolStrip) { + if (leftMouseButtonDown) { + Point endLocation = TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + // protect against calling when the mouse hasnt really moved. moving the toolstrip/creating the feedback rect + // can cause extra mousemove events, we want to make sure we're not doing all this work + // for nothing. + if (endLocation != lastEndLocation) { + ToolStripPanelRow.ToolStripPanel.MoveControl(ParentInternal, /*startLocation,*/endLocation ); + lastEndLocation = endLocation; + } + startLocation = endLocation; + } + else { + // sometimes we dont get mouseup in DT. Release now. + MovingToolStrip = false; + } + } + + base.OnMouseMove(mea); + } + + protected override void OnMouseEnter(System.EventArgs e) { + + // only switch the cursor if we've got a rafting row. + if ((ParentInternal != null) && (ToolStripPanelRow != null) && (!ParentInternal.IsInDesignMode)) { + oldCursor = ParentInternal.Cursor; + SetCursor(ParentInternal, Cursors.SizeAll); + } + else { + oldCursor = null; + } + base.OnMouseEnter(e); + + } + + /// + /// + /// + /// + protected override void OnMouseLeave(System.EventArgs e) { + if (oldCursor != null && !ParentInternal.IsInDesignMode) { + SetCursor(ParentInternal,oldCursor); + } + if (!MovingToolStrip && LeftMouseButtonIsDown()) { + MovingToolStrip = true; + } + base.OnMouseLeave(e); + } + + + + protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) { + if (MovingToolStrip) { + Point endLocation = TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); + ToolStripPanelRow.ToolStripPanel.MoveControl(ParentInternal, /*startLocation,*/endLocation ); + } + + if (!ParentInternal.IsInDesignMode) { + SetCursor(ParentInternal, oldCursor); + } + ToolStripPanel.ClearDragFeedback(); + MovingToolStrip = false; + base.OnMouseUp(mea); + } + + internal override void ToolStrip_RescaleConstants(int oldDpi, int newDpi) { + base.RescaleConstantsInternal(newDpi); + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, newDpi); + scaledGripThickness = DpiHelper.LogicalToDeviceUnits(gripThicknessDefault, newDpi); + scaledGripThicknessVisualStylesEnabled = DpiHelper.LogicalToDeviceUnits(gripThicknessVisualStylesEnabled, newDpi); + this.Margin = DefaultMargin; + + gripThickness = ToolStripManager.VisualStylesEnabled ? scaledGripThicknessVisualStylesEnabled : scaledGripThickness; + + OnFontChanged(EventArgs.Empty); + } + + private static void SetCursor(Control control, Cursor cursor) { + IntSecurity.ModifyCursor.Assert(); + control.Cursor = cursor; + // CodeAccessPermission.RevertAssert automatically called + } + + internal class ToolStripGripAccessibleObject : ToolStripButtonAccessibleObject { + private string stockName; + + public ToolStripGripAccessibleObject(ToolStripGrip owner) : base(owner){ + } + + public override string Name { + get { + string name = Owner.AccessibleName; + if (name != null) { + return name; + } + if (string.IsNullOrEmpty(stockName)) { + stockName = SR.GetString(SR.ToolStripGripAccessibleName); + } + return stockName; + } + set { + base.Name = value; + } + } + + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.Grip; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + switch (propertyID) { + case NativeMethods.UIA_IsOffscreenPropertyId: + return false; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_ThumbControlTypeId; + } + } + + return base.GetPropertyValue(propertyID); + } + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripGripDisplayStyle.cs b/WindowsForms/Managed/System/WinForms/ToolStripGripDisplayStyle.cs new file mode 100644 index 000000000..97c5046d3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripGripDisplayStyle.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + /// + public enum ToolStripGripDisplayStyle { + /// + Horizontal, + /// + Vertical + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventArgs.cs new file mode 100644 index 000000000..06b4a5be7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventArgs.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + /// + /// + public class ToolStripGripRenderEventArgs : ToolStripRenderEventArgs { + + /// + /// + /// This class represents all the information to render the toolStrip + /// + public ToolStripGripRenderEventArgs(Graphics g, ToolStrip toolStrip) : base(g, toolStrip) { + + } + + /// + /// + /// the graphics object to draw with + /// + public Rectangle GripBounds { + get { + return ToolStrip.GripRectangle; + } + } + + + /// + /// + /// vertical or horizontal + /// + public ToolStripGripDisplayStyle GripDisplayStyle { + get { + return ToolStrip.GripDisplayStyle; + } + } + + /// + /// + /// visible or not + /// + public ToolStripGripStyle GripStyle { + get { + return ToolStrip.GripStyle; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventHandler.cs new file mode 100644 index 000000000..e79d6e11a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripGripRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of a winbar item is being rendered + /// + /// + public delegate void ToolStripGripRenderEventHandler(object sender, ToolStripGripRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripGripStyle.cs b/WindowsForms/Managed/System/WinForms/ToolStripGripStyle.cs new file mode 100644 index 000000000..b0285db37 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripGripStyle.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + /// + /// + public enum ToolStripGripStyle { + /// + Hidden = 0x00000000, + /// + Visible = 0x00000001 + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripHighContrastRenderer.cs b/WindowsForms/Managed/System/WinForms/ToolStripHighContrastRenderer.cs new file mode 100644 index 000000000..db1cc30db --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripHighContrastRenderer.cs @@ -0,0 +1,440 @@ +#region Using directives + +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Collections.Specialized; + + +#endregion + +namespace System.Windows.Forms { + + // this renderer supports high contrast for ToolStripProfessional and ToolStripSystemRenderer. + internal class ToolStripHighContrastRenderer : ToolStripSystemRenderer { + + private const int GRIP_PADDING = 4; + + BitVector32 options = new BitVector32(); + private static readonly int optionsDottedBorder = BitVector32.CreateMask(); + private static readonly int optionsDottedGrip = BitVector32.CreateMask(optionsDottedBorder); + private static readonly int optionsFillWhenSelected = BitVector32.CreateMask(optionsDottedGrip); + + public ToolStripHighContrastRenderer(bool systemRenderMode) { + options[optionsDottedBorder | optionsDottedGrip | optionsFillWhenSelected] = !systemRenderMode; + } + + public bool DottedBorder { + get { return options[optionsDottedBorder]; } + } + public bool DottedGrip { + get { return options[optionsDottedGrip]; } + } + public bool FillWhenSelected{ + get { return options[optionsFillWhenSelected]; } + } + + // this is a renderer override, so return null so we dont get into an infinite loop. + internal override ToolStripRenderer RendererOverride { + get { return null; } + } + + protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) { + base.OnRenderArrow(e); + } + + protected override void OnRenderGrip(ToolStripGripRenderEventArgs e) { + + if (DottedGrip) { + Graphics g = e.Graphics; + Rectangle bounds = e.GripBounds; + ToolStrip toolStrip = e.ToolStrip; + + int height = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Height : bounds.Width; + int width = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Width : bounds.Height; + + int numRectangles = (height - (GRIP_PADDING * 2)) / 4; + + if (numRectangles > 0) { + Rectangle[] shadowRects = new Rectangle[numRectangles]; + int startY = GRIP_PADDING; + int startX = (width / 2); + + for (int i = 0; i < numRectangles; i++) { + shadowRects[i] = (toolStrip.Orientation == Orientation.Horizontal) ? + new Rectangle(startX, startY, 2, 2) : + new Rectangle(startY, startX, 2, 2); + + startY += 4; + } + + g.FillRectangles(SystemBrushes.ControlLight, shadowRects); + + + + } + } + else { + base.OnRenderGrip(e); + } + } + + + protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { + + if (FillWhenSelected) { + RenderItemInternalFilled(e, false); + } + else { + base.OnRenderDropDownButtonBackground(e); + if (e.Item.Pressed) { + e.Graphics.DrawRectangle(SystemPens.ButtonHighlight, new Rectangle(0, 0, e.Item.Width - 1, e.Item.Height - 1)); + } + } + + } + + protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { + if (AccessibilityImprovements.Level1) { + // The ARGB values came directly from the bitmap. (See + // NetFXDev1\src\NDP\fx\src\Microsoft\Managed\Resources\System\Microsoft\checked.bmp). + // If the bitmap colors change this code will no longer work and will + // need to be updated. + Color checkColor = Color.FromArgb(255, 4, 2, 4); + + // Create a color map to remap the check color to either the theme + // color for highlighted text or menu text, depending on whether + // the menu item is selected. + ColorMap[] checkColorMap = new ColorMap[1]; + checkColorMap[0] = new ColorMap(); + checkColorMap[0].OldColor = checkColor; + + checkColorMap[0].NewColor = ((e.Item.Selected || e.Item.Pressed) && e.Item.Enabled) ? + SystemColors.HighlightText : SystemColors.MenuText; + + // If we already have an image attributes associated with the event, + // just add the color map. Otherwise, create a new one. + ImageAttributes imageAttr = e.ImageAttributes ?? new ImageAttributes(); + imageAttr.SetRemapTable(checkColorMap, ColorAdjustType.Bitmap); + e.ImageAttributes = imageAttr; + } + + base.OnRenderItemCheck(e); + } + + protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { + // do nothing + } + protected override void OnRenderItemBackground(ToolStripItemRenderEventArgs e) { + base.OnRenderItemBackground(e); + } + + protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { + ToolStripSplitButton item = e.Item as ToolStripSplitButton; + Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); + Graphics g = e.Graphics; + + if (item != null) { + Rectangle dropDownRect = item.DropDownButtonBounds; + + if (item.Pressed) { + g.DrawRectangle(SystemPens.ButtonHighlight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + + } + else if (item.Selected) { + g.FillRectangle(SystemBrushes.Highlight, bounds); + g.DrawRectangle(SystemPens.ButtonHighlight, bounds.X, bounds.Y, bounds.Width-1, bounds.Height -1); + g.DrawRectangle(SystemPens.ButtonHighlight, dropDownRect); + } + + Color arrowColor = AccessibilityImprovements.Level2 && item.Selected && !item.Pressed ? SystemColors.HighlightText : SystemColors.ControlText; + DrawArrow(new ToolStripArrowRenderEventArgs(g, item, dropDownRect, arrowColor, ArrowDirection.Down)); + } + } + + protected override void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e) { + base.OnRenderStatusStripSizingGrip(e); + } + + protected override void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) { + if (FillWhenSelected) { + RenderItemInternalFilled(e); + } + else { + base.OnRenderLabelBackground(e); + } + } + protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { + + base.OnRenderMenuItemBackground(e); + if (!e.Item.IsOnDropDown && e.Item.Pressed) { + e.Graphics.DrawRectangle(SystemPens.ButtonHighlight, 0, 0, e.Item.Width - 1, e.Item.Height - 1); + } + } + protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) { + if (FillWhenSelected) { + + RenderItemInternalFilled(e, /*pressFill = */false); + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + Color arrowColor = item.Enabled ? SystemColors.ControlText : SystemColors.ControlDark; + DrawArrow(new ToolStripArrowRenderEventArgs(g, item, new Rectangle(Point.Empty, item.Size), arrowColor, ArrowDirection.Down)); + + + } + else { + base.OnRenderOverflowButtonBackground(e); + } + } + + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { + + if (AccessibilityImprovements.Level2 && e.Item.Selected && (!e.Item.Pressed || e.Item is ToolStripButton)) { + e.DefaultTextColor = SystemColors.HighlightText; + } + else if (e.TextColor != SystemColors.HighlightText && e.TextColor != SystemColors.ControlText) { + // we'll change the DefaultTextColor, if someone wants to change this,manually set the TextColor property. + if (e.Item.Selected || e.Item.Pressed) { + e.DefaultTextColor = SystemColors.HighlightText; + } + else { + e.DefaultTextColor = SystemColors.ControlText; + } + } + + + // VSO-399067 ToolstripButtons that are checked are rendered with a highlight + // background. In that case, set the text color to highlight as well. + if (AccessibilityImprovements.Level1 && + typeof(ToolStripButton).IsAssignableFrom(e.Item.GetType()) && + ((ToolStripButton)e.Item).DisplayStyle != ToolStripItemDisplayStyle.Image && + ((ToolStripButton)e.Item).Checked) { + e.TextColor = SystemColors.HighlightText; + } + + base.OnRenderItemText(e); + } + + protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + // do nothing. LOGO requirements ask us not to paint background effects behind + } + + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + Graphics g = e.Graphics; + + + if (e.ToolStrip is ToolStripDropDown) { + + g.DrawRectangle(SystemPens.ButtonHighlight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + + if (!(e.ToolStrip is ToolStripOverflow)) { + // make the neck connected. + g.FillRectangle(SystemBrushes.Control, e.ConnectedArea); + + } + } + else if (e.ToolStrip is MenuStrip) { + // do nothing + } + else if (e.ToolStrip is StatusStrip) { + g.DrawRectangle(SystemPens.ButtonShadow, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + + } + else { + RenderToolStripBackgroundInternal(e); + } + } + + private void RenderToolStripBackgroundInternal(ToolStripRenderEventArgs e) { + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + Graphics g = e.Graphics; + + if (DottedBorder) { + + using (Pen p = new Pen(SystemColors.ButtonShadow)) { + p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; + + bool oddWidth = ((bounds.Width & 0x1) == 0x1); + bool oddHeight = ((bounds.Height & 0x1) == 0x1); + int indent = 2; + + // top + g.DrawLine(p, bounds.X + indent, bounds.Y, bounds.Width - 1, bounds.Y); + // bottom + g.DrawLine(p, bounds.X + indent, bounds.Height - 1, bounds.Width - 1, bounds.Height - 1); + + // left + g.DrawLine(p, bounds.X, bounds.Y + indent, bounds.X, bounds.Height - 1); + // right + g.DrawLine(p, bounds.Width - 1, bounds.Y + indent, bounds.Width - 1, bounds.Height - 1); + + // connecting pixels + + // top left conntecting pixel - always drawn + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(1, 1, 1, 1)); + + if (oddWidth) { + // top right pixel + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(bounds.Width - 2, 1, 1, 1)); + } + // bottom conntecting pixels - drawn only if height is odd + if (oddHeight) { + // bottom left + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(1, bounds.Height - 2, 1, 1)); + + } + + // top and bottom right conntecting pixel - drawn only if height and width are odd + if (oddHeight && oddWidth) { + // bottom right + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(bounds.Width - 2, bounds.Height - 2, 1, 1)); + } + + } + } + else { + // draw solid border + bounds.Width -= 1; + bounds.Height -= 1; + g.DrawRectangle(SystemPens.ButtonShadow, bounds); + } + + } + protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { + + Pen foreColorPen = SystemPens.ButtonShadow; + + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); + + + if (e.Vertical) { + if (bounds.Height >= 8) { + bounds.Inflate(0, -4); // scoot down 4PX and start drawing + } + // Draw dark line + int startX = bounds.Width / 2; + + g.DrawLine(foreColorPen, startX, bounds.Top, startX, bounds.Bottom - 1); + + } + else { + // + // horizontal separator + if (bounds.Width >= 4) { + bounds.Inflate(-2, 0); // scoot over 2PX and start drawing + } + + // Draw dark line + int startY = bounds.Height / 2; + + g.DrawLine(foreColorPen, bounds.Left, startY, bounds.Right - 1, startY); + + } + + } + + // Indicates whether system is currently set to high contrast 'white on black' mode + internal static bool IsHighContrastWhiteOnBlack() + { + return SystemColors.Control.ToArgb() == Color.Black.ToArgb(); + } + + protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { + Image image = e.Image; + if (image != null) { + if (Image.GetPixelFormatSize(image.PixelFormat) > 16) { + // for 24, 32 bit images, just paint normally - mapping the color table is not + // going to work when you can have full color. + base.OnRenderItemImage(e); + return; + } + Graphics g = e.Graphics; + + ToolStripItem item = e.Item; + Rectangle imageRect = e.ImageRectangle; + using (ImageAttributes attrs = new ImageAttributes()) { + if (IsHighContrastWhiteOnBlack() && !(FillWhenSelected && (e.Item.Pressed || e.Item.Selected))) { + // translate white, black and blue to colors visible in high contrast mode. + ColorMap cm1 = new ColorMap(); + ColorMap cm2 = new ColorMap(); + ColorMap cm3 = new ColorMap(); + + cm1.OldColor = Color.Black; + cm1.NewColor = Color.White; + cm2.OldColor = Color.White; + cm2.NewColor = Color.Black; + cm3.OldColor = Color.FromArgb(0, 0, 128); + cm3.NewColor = Color.White; + + + attrs.SetRemapTable(new ColorMap[] { cm1, cm2, cm3 }, ColorAdjustType.Bitmap); + } + if (item.ImageScaling == ToolStripItemImageScaling.None) { + g.DrawImage(image, imageRect, 0, 0, imageRect.Width, imageRect.Height, GraphicsUnit.Pixel, attrs); + } + else { + g.DrawImage(image, imageRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attrs); + } + } + } + } + + protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { + if (FillWhenSelected) { + ToolStripButton button = e.Item as ToolStripButton; + if (button != null && button.Checked) { + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); + + if (button.CheckState == CheckState.Checked) { + g.FillRectangle(SystemBrushes.Highlight, bounds); + } + if (button.Selected && AccessibilityImprovements.Level1) { + g.DrawRectangle(SystemPens.Highlight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + else { + g.DrawRectangle(SystemPens.ControlLight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + else { + RenderItemInternalFilled(e); + } + } + else { + base.OnRenderButtonBackground(e); + } + } + + private void RenderItemInternalFilled(ToolStripItemRenderEventArgs e) { + RenderItemInternalFilled(e, /*pressFill=*/true); + } + + private void RenderItemInternalFilled(ToolStripItemRenderEventArgs e, bool pressFill) { + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size); + + if (e.Item.Pressed) { + if (pressFill) { + g.FillRectangle(SystemBrushes.Highlight, bounds); + } + else { + g.DrawRectangle(SystemPens.ControlLight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + else if (e.Item.Selected) { + g.FillRectangle(SystemBrushes.Highlight, bounds); + g.DrawRectangle(SystemPens.ControlLight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + + + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItem.cs b/WindowsForms/Managed/System/WinForms/ToolStripItem.cs new file mode 100644 index 000000000..240a15254 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItem.cs @@ -0,0 +1,4821 @@ +//#define SELECTEDCHANGED +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms.ButtonInternal; + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using System.Runtime.InteropServices; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Drawing.Design; + using System.Security.Permissions; + using System.Security; + using System.Configuration; + using System.Drawing.Imaging; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Runtime.Versioning; + using Collections.Generic; + + /// + /// + [System.ComponentModel.DesignTimeVisible(false)] + [Designer("System.Windows.Forms.Design.ToolStripItemDesigner, " + AssemblyRef.SystemDesign)] + [DefaultEvent("Click")] + [ToolboxItem(false)] + [DefaultProperty("Text")] + public abstract class ToolStripItem : Component, + IDropTarget, + ISupportOleDropSource, + IArrangedElement, + IKeyboardToolTip { + + + + +#if DEBUG + internal static readonly TraceSwitch MouseDebugging = new TraceSwitch("MouseDebugging", "Debug ToolStripItem mouse debugging code"); +#else + internal static readonly TraceSwitch MouseDebugging; +#endif + + private Rectangle bounds = Rectangle.Empty; + private PropertyStore propertyStore; + private ToolStripItemAlignment alignment = ToolStripItemAlignment.Left; + private ToolStrip parent = null; + private ToolStrip owner = null; + private ToolStripItemOverflow overflow = ToolStripItemOverflow.AsNeeded; + private ToolStripItemPlacement placement = ToolStripItemPlacement.None; + private ContentAlignment imageAlign = ContentAlignment.MiddleCenter; + private ContentAlignment textAlign = ContentAlignment.MiddleCenter; + private TextImageRelation textImageRelation = TextImageRelation.ImageBeforeText; + private ToolStripItemImageIndexer imageIndexer = null; + private ToolStripItemInternalLayout toolStripItemInternalLayout = null; + private BitVector32 state = new BitVector32(); + private string toolTipText = null; + private Color imageTransparentColor = Color.Empty; + private ToolStripItemImageScaling imageScaling = ToolStripItemImageScaling.SizeToFit; + private Size cachedTextSize = Size.Empty; + + private static readonly Padding defaultMargin = new Padding(0, 1, 0, 2); + private static readonly Padding defaultStatusStripMargin = new Padding(0, 2, 0, 0); + private Padding scaledDefaultMargin = defaultMargin; + private Padding scaledDefaultStatusStripMargin = defaultStatusStripMargin; + + private ToolStripItemDisplayStyle displayStyle = ToolStripItemDisplayStyle.ImageAndText; + + private static readonly ArrangedElementCollection EmptyChildCollection = new ArrangedElementCollection(); + + + /// + /// Adding a new event?? Make sure you dont need to add to ToolStripControlHost.cs + /// + internal static readonly object EventMouseDown = new object(); + internal static readonly object EventMouseEnter = new object(); + internal static readonly object EventMouseLeave = new object(); + internal static readonly object EventMouseHover = new object(); + internal static readonly object EventMouseMove = new object(); + internal static readonly object EventMouseUp = new object(); + internal static readonly object EventMouseWheel = new object(); + internal static readonly object EventClick = new object(); + internal static readonly object EventDoubleClick = new object(); + internal static readonly object EventDragDrop = new object(); + internal static readonly object EventDragEnter = new object(); + internal static readonly object EventDragLeave = new object(); + internal static readonly object EventDragOver = new object(); + internal static readonly object EventDisplayStyleChanged = new object(); + internal static readonly object EventEnabledChanged = new object(); + internal static readonly object EventInternalEnabledChanged = new object(); + internal static readonly object EventFontChanged = new object(); + internal static readonly object EventForeColorChanged = new object(); + internal static readonly object EventBackColorChanged = new object(); + internal static readonly object EventGiveFeedback = new object(); + internal static readonly object EventQueryContinueDrag = new object(); + internal static readonly object EventQueryAccessibilityHelp = new object(); + internal static readonly object EventMove = new object(); + internal static readonly object EventResize = new object(); + internal static readonly object EventLayout = new object(); + internal static readonly object EventLocationChanged = new object(); + internal static readonly object EventRightToLeft = new object(); + internal static readonly object EventVisibleChanged = new object(); + internal static readonly object EventAvailableChanged = new object(); + internal static readonly object EventOwnerChanged = new object(); + internal static readonly object EventPaint = new object(); + internal static readonly object EventText = new object(); + internal static readonly object EventSelectedChanged = new object(); + + /// + /// Adding a new event?? Make sure you dont need to add to ToolStripControlHost.cs + /// + + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + + private static readonly int PropName = PropertyStore.CreateKey(); + private static readonly int PropText = PropertyStore.CreateKey(); + private static readonly int PropBackColor = PropertyStore.CreateKey(); + private static readonly int PropForeColor = PropertyStore.CreateKey(); + + private static readonly int PropImage = PropertyStore.CreateKey(); + private static readonly int PropFont = PropertyStore.CreateKey(); + private static readonly int PropRightToLeft = PropertyStore.CreateKey(); + private static readonly int PropTag = PropertyStore.CreateKey(); + + private static readonly int PropAccessibility = PropertyStore.CreateKey(); + private static readonly int PropAccessibleName = PropertyStore.CreateKey(); + private static readonly int PropAccessibleRole = PropertyStore.CreateKey(); + private static readonly int PropAccessibleHelpProvider = PropertyStore.CreateKey(); + + private static readonly int PropAccessibleDefaultActionDescription = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDescription = PropertyStore.CreateKey(); + private static readonly int PropTextDirection = PropertyStore.CreateKey(); + private static readonly int PropMirroredImage = PropertyStore.CreateKey(); + + + private static readonly int PropBackgroundImage = PropertyStore.CreateKey(); + private static readonly int PropBackgroundImageLayout = PropertyStore.CreateKey(); + + private static readonly int PropMergeAction = PropertyStore.CreateKey(); + private static readonly int PropMergeIndex = PropertyStore.CreateKey(); + + + private static readonly int stateAllowDrop = BitVector32.CreateMask(); + private static readonly int stateVisible = BitVector32.CreateMask(stateAllowDrop); + private static readonly int stateEnabled = BitVector32.CreateMask(stateVisible); + private static readonly int stateMouseDownAndNoDrag = BitVector32.CreateMask(stateEnabled); + private static readonly int stateAutoSize = BitVector32.CreateMask(stateMouseDownAndNoDrag); + private static readonly int statePressed = BitVector32.CreateMask(stateAutoSize); + private static readonly int stateSelected = BitVector32.CreateMask(statePressed); + private static readonly int stateContstructing = BitVector32.CreateMask(stateSelected); + private static readonly int stateDisposed = BitVector32.CreateMask(stateContstructing); + private static readonly int stateCurrentlyAnimatingImage = BitVector32.CreateMask(stateDisposed); + private static readonly int stateDoubleClickEnabled = BitVector32.CreateMask(stateCurrentlyAnimatingImage); + private static readonly int stateAutoToolTip = BitVector32.CreateMask(stateDoubleClickEnabled); + private static readonly int stateSupportsRightClick = BitVector32.CreateMask(stateAutoToolTip); + private static readonly int stateSupportsItemClick = BitVector32.CreateMask(stateSupportsRightClick); + private static readonly int stateRightToLeftAutoMirrorImage = BitVector32.CreateMask(stateSupportsItemClick); + private static readonly int stateInvalidMirroredImage = BitVector32.CreateMask(stateRightToLeftAutoMirrorImage); + private static readonly int stateSupportsSpaceKey = BitVector32.CreateMask(stateInvalidMirroredImage); + private static readonly int stateMouseDownAndUpMustBeInSameItem = BitVector32.CreateMask(stateSupportsSpaceKey); + private static readonly int stateSupportsDisabledHotTracking = BitVector32.CreateMask(stateMouseDownAndUpMustBeInSameItem); + private static readonly int stateUseAmbientMargin = BitVector32.CreateMask(stateSupportsDisabledHotTracking); + private static readonly int stateDisposing = BitVector32.CreateMask(stateUseAmbientMargin); + + + private long lastClickTime = 0; + private int deviceDpi = DpiHelper.DeviceDpi; + internal Font defaultFont = ToolStripManager.DefaultFont; + + /// + /// + /// Constructor + /// + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin); + scaledDefaultStatusStripMargin = DpiHelper.LogicalToDeviceUnits(defaultStatusStripMargin); + } + + state[stateEnabled | stateAutoSize | stateVisible | stateContstructing | stateSupportsItemClick | stateInvalidMirroredImage | stateMouseDownAndUpMustBeInSameItem | stateUseAmbientMargin] = true; + state[stateAllowDrop | stateMouseDownAndNoDrag | stateSupportsRightClick | statePressed | stateSelected | stateDisposed | stateDoubleClickEnabled | stateRightToLeftAutoMirrorImage | stateSupportsSpaceKey] = false; + SetAmbientMargin(); + this.Size = DefaultSize; + this.DisplayStyle = DefaultDisplayStyle; + CommonProperties.SetAutoSize(this,true); + state[stateContstructing] = false; + this.AutoToolTip = DefaultAutoToolTip; + + } + + + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem(string text, Image image, EventHandler onClick) : this(text, image, onClick, null) { + } + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem(string text, Image image, EventHandler onClick, string name) : this() { + this.Text = text; + this.Image = image; + if (onClick != null) { + Click += onClick; + } + this.Name = name; + } + + /// + /// + /// The Accessibility Object for this Control + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolStripItemAccessibilityObjectDescr) + ] + public AccessibleObject AccessibilityObject { + get { + AccessibleObject accessibleObject = (AccessibleObject)Properties.GetObject(PropAccessibility); + if (accessibleObject == null) { + accessibleObject = CreateAccessibilityInstance(); + Properties.SetObject(PropAccessibility, accessibleObject); + } + + Debug.Assert(accessibleObject != null, "Failed to create accessibility object"); + return accessibleObject; + } + } + + /// + /// + /// The default action description of the control + /// + [ + SRCategory(SR.CatAccessibility), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolStripItemAccessibleDefaultActionDescr) + ] + public string AccessibleDefaultActionDescription { + get { + return (string)Properties.GetObject(PropAccessibleDefaultActionDescription); + } + set { + Properties.SetObject(PropAccessibleDefaultActionDescription, value); + OnAccessibleDefaultActionDescriptionChanged(EventArgs.Empty); + } + } + /// + /// + /// The accessible description of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ToolStripItemAccessibleDescriptionDescr) + ] + public string AccessibleDescription { + get { + return (string)Properties.GetObject(PropAccessibleDescription); + } + set { + Properties.SetObject(PropAccessibleDescription, value); + OnAccessibleDescriptionChanged(EventArgs.Empty); + } + } + + /// + /// + /// The accessible name of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ToolStripItemAccessibleNameDescr) + ] + public string AccessibleName { + get { + return (string)Properties.GetObject(PropAccessibleName); + } + + set { + Properties.SetObject(PropAccessibleName, value); + OnAccessibleNameChanged(EventArgs.Empty); + } + } + + /// + /// + /// The accessible role of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AccessibleRole.Default), + SRDescription(SR.ToolStripItemAccessibleRoleDescr) + ] + public AccessibleRole AccessibleRole { + + get { + bool found; + int role = Properties.GetInteger(PropAccessibleRole, out found); + if (found) { + return (AccessibleRole)role; + } + else { + return AccessibleRole.Default; + } + } + + set { + //valid values are 0xffffffff to 0x40 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AccessibleRole.Default, (int)AccessibleRole.OutlineButton)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AccessibleRole)); + } + Properties.SetInteger(PropAccessibleRole, (int)value); + OnAccessibleRoleChanged(EventArgs.Empty); + } + + + } + + + /// + /// + /// Determines if the item aligns towards the beginning or end of the winbar. + /// + [ + DefaultValue(ToolStripItemAlignment.Left), + SRCategory(SR.CatLayout), + SRDescription(SR.ToolStripItemAlignmentDescr) + ] + public ToolStripItemAlignment Alignment { + get { + return alignment; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemAlignment.Left, (int)ToolStripItemAlignment.Right)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemAlignment)); + } + if (alignment != value) { + alignment = value; + + if ((this.ParentInternal != null) && this.ParentInternal.IsHandleCreated) { + this.ParentInternal.PerformLayout(); + } + } + } + } + + + /// + /// + /// Determines if this item can be dragged. + /// This is EXACTLY like Control.AllowDrop - setting this to true WILL call + /// the droptarget handlers. The ToolStripDropTargetManager is the one that + /// handles the routing of DropTarget events to the ToolStripItem's IDropTarget + /// methods. + /// + [ + SRCategory(SR.CatDragDrop), + DefaultValue(false), + SRDescription(SR.ToolStripItemAllowDropDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public virtual bool AllowDrop { + get { + return state[stateAllowDrop]; + } + set { + if (value != state[stateAllowDrop]) { + EnsureParentDropTargetRegistered(); + state[stateAllowDrop] = value; + } + } + } + + /// + /// + /// Determines whether we set the ToolStripItem to its preferred size + /// + [DefaultValue(true)] + [SRCategory(SR.CatBehavior)] + [RefreshProperties(RefreshProperties.All)] + [Localizable(true)] + [SRDescription(SR.ToolStripItemAutoSizeDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public bool AutoSize { + get { + return state[stateAutoSize]; + } + set { + if (state[stateAutoSize] != value) { + state[stateAutoSize] = value; + CommonProperties.SetAutoSize(this,value); + InvalidateItemLayout(PropertyNames.AutoSize); + } + } + } + + /// + /// + /// !!!!This property ONLY works when toolStrip.ShowItemToolTips = true!!!! + /// if AutoToolTip is set to true we use the Text, if false, we use ToolTipText. + /// + [DefaultValue(false)] + [SRDescription(SR.ToolStripItemAutoToolTipDescr)] + [SRCategory(SR.CatBehavior)] + public bool AutoToolTip { + get { + return state[stateAutoToolTip]; + } + set { + state[stateAutoToolTip] = value; + } + } + + /// + /// as opposed to Visible, which returns whether or not the item and its parent are Visible + /// Available returns whether or not the item will be shown. Setting Available sets Visible and Vice/Versa + /// + [ + Browsable(false), + SRDescription(SR.ToolStripItemAvailableDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool Available{ + get { + // MainMenu compat: + // the only real diff is the getter - this returns what the item really thinks, + // as opposed to whether or not the parent is also Visible. Visible behavior is the same + // so that it matches Control behavior (we dont have to do special things for control hosts, etc etc). + return state[stateVisible]; + } + set { + SetVisibleCore(value); + } + } + + + [ + Browsable(false), + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ToolStripItemOnAvailableChangedDescr) + ] + public event EventHandler AvailableChanged { + add { + Events.AddHandler(EventAvailableChanged, value); + } + remove { + Events.RemoveHandler(EventAvailableChanged, value); + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr), + DefaultValue(null) + ] + public virtual Image BackgroundImage { + get { + return Properties.GetObject(PropBackgroundImage) as Image; + + } + set { + if (BackgroundImage != value) { + Properties.SetObject(PropBackgroundImage, value); + Invalidate(); + } + } + } + + // Every ToolStripItem needs to cache its last/current Parent's DeviceDpi + // for PerMonitorV2 scaling purposes. + internal virtual int DeviceDpi { + get { + return deviceDpi; + } + set { + deviceDpi = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public virtual ImageLayout BackgroundImageLayout { + get { + bool found = Properties.ContainsObject(PropBackgroundImageLayout); + if (!found) { + return ImageLayout.Tile; + } + else { + return ((ImageLayout)Properties.GetObject(PropBackgroundImageLayout)); + } + } + set { + if (BackgroundImageLayout != value) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ImageLayout.None, (int)ImageLayout.Zoom)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ImageLayout)); + } + Properties.SetObject(PropBackgroundImageLayout, value); + Invalidate(); + } + } + } + + /// + /// + /// The BackColor of the item + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemBackColorDescr) + ] + public virtual Color BackColor { + get { + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) + return c; + + Control p = ParentInternal; + if (p != null) { + return p.BackColor; + } + return Control.DefaultBackColor; + } + set { + Color c = BackColor; + if (!value.IsEmpty || Properties.ContainsObject(PropBackColor)) { + Properties.SetColor(PropBackColor, value); + } + + if (!c.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnBackColorChangedDescr)] + public event EventHandler BackColorChanged { + add { + Events.AddHandler(EventBackColorChanged, value); + } + remove { + Events.RemoveHandler(EventBackColorChanged, value); + } + } + + /// + /// + /// The bounds of the item + /// + [Browsable(false)] + public virtual Rectangle Bounds { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + return bounds; + } + } + + // Zero-based rectangle, same concept as ClientRect + internal Rectangle ClientBounds { + get { + Rectangle client = bounds; + client.Location = Point.Empty; + return client; + } + } + /// + [Browsable(false)] + public Rectangle ContentRectangle { + get { + Rectangle content = LayoutUtils.InflateRect(InternalLayout.ContentRectangle, this.Padding); + content.Size = LayoutUtils.UnionSizes(Size.Empty, content.Size); + return content; + } + } + + /// + /// + /// Determines whether or not the item can be selected. + /// + [Browsable(false)] + public virtual bool CanSelect { + get { + return true; + } + } + + // usually the same as can select, but things like the control box in an MDI window are exceptions + internal virtual bool CanKeyboardSelect { + get { + return CanSelect; + } + } + + /// + /// + /// Occurs when the control is clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripItemOnClickDescr) + ] + public event EventHandler Click { + add { + Events.AddHandler(EventClick, value); + } + remove { + Events.RemoveHandler(EventClick, value); + } + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DefaultValue(CommonProperties.DefaultAnchor) + ] + public AnchorStyles Anchor { + get { + // since we dont support DefaultLayout go directly against the CommonProperties + return CommonProperties.xGetAnchor(this); + } + set { + // flags enum - dont check for validity.... + + if(value != Anchor) { + // since we dont support DefaultLayout go directly against the CommonProperties + CommonProperties.xSetAnchor(this, value); + if (ParentInternal != null) { + LayoutTransaction.DoLayout(this, ParentInternal, PropertyNames.Anchor); + } + } + } + } + + + /// This does not show up in the property grid because it only applies to flow and table layouts + [ + Browsable(false), + DefaultValue(CommonProperties.DefaultDock) + ] + public DockStyle Dock { + get { + + // since we dont support DefaultLayout go directly against the CommonProperties + return CommonProperties.xGetDock(this); + } + set { + //valid values are 0x0 to 0x5 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle)); + } + if(value != Dock) { + // since we dont support DefaultLayout go directly against the CommonProperties + CommonProperties.xSetDock(this, value); + if (ParentInternal != null) { + LayoutTransaction.DoLayout(this, ParentInternal, PropertyNames.Dock); + } + } + } + } + + /// default setting of auto tooltip when this object is created + protected virtual bool DefaultAutoToolTip { + get { + return false; + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal virtual Padding DefaultMargin { + get { + if (this.Owner != null && this.Owner is StatusStrip) { + return scaledDefaultStatusStripMargin; + } + else { + return scaledDefaultMargin; + } + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Padding DefaultPadding { + get { + return Padding.Empty; + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(23, 23), DeviceDpi) : + new Size(23, 23); + } + } + + protected virtual ToolStripItemDisplayStyle DefaultDisplayStyle { + get { + return ToolStripItemDisplayStyle.ImageAndText; + } + } + + /// + /// + /// specifies the default behavior of these items on ToolStripDropDowns when clicked. + /// + internal protected virtual bool DismissWhenClicked { + get { + return true; + } + } + + /// + /// + /// DisplayStyle specifies whether the image and text are rendered. This is not on the base + /// item class because different derived things will have different enumeration needs. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemDisplayStyleDescr) + ] + public virtual ToolStripItemDisplayStyle DisplayStyle { + get { + return displayStyle; + } + set { + if (displayStyle != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemDisplayStyle.None, (int)ToolStripItemDisplayStyle.ImageAndText)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemDisplayStyle)); + } + displayStyle = value; + if (!state[stateContstructing]) { + InvalidateItemLayout(PropertyNames.DisplayStyle); + OnDisplayStyleChanged(new EventArgs()); + } + } + + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event EventHandler DisplayStyleChanged { + add { + Events.AddHandler(EventDisplayStyleChanged, value); + } + remove { + Events.RemoveHandler(EventDisplayStyleChanged, value); + } + } + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + private RightToLeft DefaultRightToLeft { + get { + return RightToLeft.Inherit; + } + } + /// + /// + /// Occurs when the control is double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnDoubleClickDescr)] + public event EventHandler DoubleClick { + add { + Events.AddHandler(EventDoubleClick, value); + } + remove { + Events.RemoveHandler(EventDoubleClick, value); + } + } + + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripItemDoubleClickedEnabledDescr) + ] + public bool DoubleClickEnabled { + get { + return state[stateDoubleClickEnabled]; + } + set { + state[stateDoubleClickEnabled] = value; + } + } + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragDropDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragDrop { + add { + Events.AddHandler(EventDragDrop, value); + } + remove { + Events.RemoveHandler(EventDragDrop, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragEnterDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragEnter { + add { + Events.AddHandler(EventDragEnter, value); + } + remove { + Events.RemoveHandler(EventDragEnter, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragOverDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragOver { + add { + Events.AddHandler(EventDragOver, value); + } + remove { + Events.RemoveHandler(EventDragOver, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragLeaveDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event EventHandler DragLeave { + add { + Events.AddHandler(EventDragLeave, value); + } + remove { + Events.RemoveHandler(EventDragLeave, value); + } + } + + /// + /// ToolStripItem.DropSource + /// + /// This represents what we're actually going to drag. If the parent has set AllowItemReorder to true, + /// then the item should call back on the private OnQueryContinueDrag/OnGiveFeedback that is implemented + /// in the parent winbar. + /// + /// Else if the parent does not support reordering of items (Parent.AllowItemReorder = false) - + /// then call back on the ToolStripItem's OnQueryContinueDrag/OnGiveFeedback methods. + /// + private DropSource DropSource { + get { + if ((ParentInternal != null) && (ParentInternal.AllowItemReorder) && (ParentInternal.ItemReorderDropSource != null)) { + return new DropSource(ParentInternal.ItemReorderDropSource); + } + return new DropSource(this); + } + } + + /// + /// + /// Occurs when the control is clicked. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ToolStripItemEnabledDescr), + DefaultValue(true) + ] + public virtual bool Enabled { + get { + bool parentEnabled = true; + + if (this.Owner != null) { + parentEnabled = this.Owner.Enabled; + } + + return state[stateEnabled] && parentEnabled; + } + set { + // flip disabled bit. + if (state[stateEnabled] != value) { + state[stateEnabled] = value; + if (!state[stateEnabled]) { + bool wasSelected = state[stateSelected]; + // clear all the other states. + state[stateSelected | statePressed] = false; + + if (wasSelected && !AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + + } + OnEnabledChanged(EventArgs.Empty); + Invalidate(); + } + OnInternalEnabledChanged(EventArgs.Empty); + } + + } + + + + /// + /// + /// [To be supplied.] + /// + [ + SRDescription(SR.ToolStripItemEnabledChangedDescr) + ] + public event EventHandler EnabledChanged { + add { + Events.AddHandler(EventEnabledChanged, value); + } + remove { + Events.RemoveHandler(EventEnabledChanged, value); + } + } + + internal event EventHandler InternalEnabledChanged { + add { + Events.AddHandler(EventInternalEnabledChanged, value); + } + remove { + Events.RemoveHandler(EventInternalEnabledChanged, value); + } + } + + private void EnsureParentDropTargetRegistered() { + if (ParentInternal != null) { + IntSecurity.ClipboardRead.Demand(); + ParentInternal.DropTargetManager.EnsureRegistered(this); + } + } + + /// + /// + /// Retrieves the current font for this item. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemForeColorDescr) + ] + public virtual Color ForeColor { + get { + Color foreColor = Properties.GetColor(PropForeColor); + if (!foreColor.IsEmpty){ + return foreColor; + } + + Control p = ParentInternal; + if (p != null) { + return p.ForeColor; + } + return Control.DefaultForeColor; + } + + set { + Color c = ForeColor; + if (!value.IsEmpty || Properties.ContainsObject(PropForeColor)) { + Properties.SetColor(PropForeColor, value); + } + if (!c.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnForeColorChangedDescr)] + public event EventHandler ForeColorChanged { + add { + Events.AddHandler(EventForeColorChanged, value); + } + remove { + Events.RemoveHandler(EventForeColorChanged, value); + } + } + + /// + /// + /// Retrieves the current font for this control. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemFontDescr) + ] + public virtual Font Font { + get { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return font; + } + + Font f = GetOwnerFont(); + if (f != null) { + return f; + } + + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + defaultFont : + ToolStripManager.DefaultFont; + } + set { + + Font local = (Font)Properties.GetObject(PropFont); + if ((local != value)){ + Properties.SetObject(PropFont, value); + OnFontChanged(EventArgs.Empty); + } + } + } + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnGiveFeedbackDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event GiveFeedbackEventHandler GiveFeedback { + add { + Events.AddHandler(EventGiveFeedback, value); + } + remove { + Events.RemoveHandler(EventGiveFeedback, value); + } + } + + /// + /// + /// The height of this control + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int Height { + get { + return Bounds.Height; + } + set{ + Rectangle currentBounds = this.Bounds; + SetBounds(currentBounds.X, currentBounds.Y, currentBounds.Width, value); + } + } + + /// + /// + /// ToolStripItems do not have children. For perf reasons always return a static empty collection. + /// Consider creating readonly collection. + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + return ToolStripItem.EmptyChildCollection; + } + } + /// + /// + /// Should not be exposed as this returns an unexposed type. + /// + /// + IArrangedElement IArrangedElement.Container { + get { + if (this.ParentInternal == null) { + return this.Owner; + } + return this.ParentInternal; + } + } + /// + /// + /// + + + Rectangle IArrangedElement.DisplayRectangle { + get { + return this.Bounds; + } + } + + /// + /// + /// + + + bool IArrangedElement.ParticipatesInLayout { + get { + // this can be different than "Visible" property as "Visible" takes into account whether or not you + // are parented and your parent is visible. + return state[stateVisible]; + } + } + + + /// + /// + PropertyStore IArrangedElement.Properties { + get { + return this.Properties; + } + } + + + // Sets the bounds for an element. + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + // in this case the parent is telling us to refresh our bounds - dont + // call PerformLayout + SetBounds(bounds); + } + + /// + /// + /// + + + void IArrangedElement.PerformLayout(IArrangedElement container, string propertyName) { + return; + } + + /// + /// + /// + /// Gets or sets the alignment of the image on the label control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageAlignDescr) + ] + public ContentAlignment ImageAlign { + get { + return imageAlign; + } + set { + + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (imageAlign != value) { + imageAlign = value; + InvalidateItemLayout(PropertyNames.ImageAlign); + } + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr) + ] + public virtual Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + Image image = (Image)Properties.GetObject(PropImage); + + if (image == null && (Owner != null) && (Owner.ImageList != null) && ImageIndexer.ActualIndex >= 0) { + if (ImageIndexer.ActualIndex < Owner.ImageList.Images.Count) { + // CACHE (by design). If we fetched out of the image list every time it would dramatically hit perf. + image = Owner.ImageList.Images[ImageIndexer.ActualIndex]; + state[stateInvalidMirroredImage] = true; + Properties.SetObject(PropImage, image); + return image; + } + } + else { + return image; + } + return null; + } + set { + if (Image != value) { + StopAnimate(); + Bitmap bmp = value as Bitmap; + if (bmp != null && ImageTransparentColor != Color.Empty) { + if (bmp.RawFormat.Guid != ImageFormat.Icon.Guid && !ImageAnimator.CanAnimate(bmp)) { + bmp.MakeTransparent(ImageTransparentColor); + } + value = bmp; + } + if (value != null) { + ImageIndex = -1; + } + Properties.SetObject(PropImage, value); + state[stateInvalidMirroredImage] = true; + Animate(); + InvalidateItemLayout(PropertyNames.Image); + } + } + } + + + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageTransparentColorDescr) + ] + public Color ImageTransparentColor { + get { + return imageTransparentColor; + } + set { + if (imageTransparentColor != value) { + imageTransparentColor = value; + Bitmap currentImage = Image as Bitmap; + if (currentImage != null && value != Color.Empty) { + if (currentImage.RawFormat.Guid != ImageFormat.Icon.Guid && !ImageAnimator.CanAnimate(currentImage)) { + currentImage.MakeTransparent(imageTransparentColor); + } + } + Invalidate(); + } + + } + } + + /// + /// + /// Returns the ToolStripItem's currently set image index + /// Here for compat only - this is NOT to be visible at DT. + /// + [ + SRDescription(SR.ToolStripItemImageIndexDescr), + Localizable(true), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + Editor("System.Windows.Forms.Design.ToolStripImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(false), + RelatedImageList("Owner.ImageList") + ] + public int ImageIndex { + get { + if ((Owner != null) && ImageIndexer.Index != -1 && Owner.ImageList != null && ImageIndexer.Index >= Owner.ImageList.Images.Count) { + return Owner.ImageList.Images.Count - 1; + } + return this.ImageIndexer.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + + ImageIndexer.Index = value; + state[stateInvalidMirroredImage] = true; + // Set the Image Property to null + Properties.SetObject(PropImage, null); + + + InvalidateItemLayout(PropertyNames.ImageIndex); + } + } + + internal ToolStripItemImageIndexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ToolStripItemImageIndexer(this); + } + return imageIndexer; + } + } + + /// + /// + /// Returns the ToolStripItem's currently set image index + /// Here for compat only - this is NOT to be visible at DT. + /// + [ + SRDescription(SR.ToolStripItemImageKeyDescr), + SRCategory(SR.CatBehavior), + Localizable(true), + TypeConverterAttribute(typeof(ImageKeyConverter)), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.ToolStripImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(false), + RelatedImageList("Owner.ImageList") + ] + public string ImageKey { + get { + return this.ImageIndexer.Key; + } + set { + ImageIndexer.Key = value; + state[stateInvalidMirroredImage] = true; + Properties.SetObject(PropImage, null); + + InvalidateItemLayout(PropertyNames.ImageKey); + + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ToolStripItemImageScaling.SizeToFit), + Localizable(true), + SRDescription(SR.ToolStripItemImageScalingDescr) + ] + public ToolStripItemImageScaling ImageScaling { + get { + return imageScaling; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemImageScaling.None, (int)ToolStripItemImageScaling.SizeToFit)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemImageScaling)); + } + if (imageScaling != value) { + imageScaling = value; + + InvalidateItemLayout(PropertyNames.ImageScaling); + } + } + } + + /// + /// + /// This object helps determine where the image and text should be drawn. + /// + /// + /// + internal ToolStripItemInternalLayout InternalLayout { + get { + if (toolStripItemInternalLayout == null) { + toolStripItemInternalLayout = CreateInternalLayout(); + } + return toolStripItemInternalLayout; + } + } + + internal bool IsForeColorSet { + get { + Color color = Properties.GetColor(PropForeColor); + if (!color.IsEmpty) { + return true; + } + else { + Control parent = ParentInternal; + if (parent != null) { + return parent.ShouldSerializeForeColor(); + } + } + return false; + } + } + + /// + /// + /// This is used by ToolStrip to pass on the mouseMessages for ActiveDropDown. + /// + /// + /// + internal bool IsInDesignMode { + get { + return DesignMode; + } + } + + + /// + [Browsable(false)] + public bool IsDisposed { + get { + return state[stateDisposed]; + } + + } + + /// + [Browsable(false)] + public bool IsOnDropDown { + get { + + if (ParentInternal != null) { + return ParentInternal.IsDropDown; + } + else if (Owner != null && Owner.IsDropDown) { + return true; + } + + return false; + } + } + + /// returns whether the item placement is set to overflow. + [Browsable(false)] + public bool IsOnOverflow { + get { + return (this.Placement == ToolStripItemPlacement.Overflow); + } + } + + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal virtual bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + /// + /// + /// Occurs when the location of the ToolStripItem has been updated -- usually by layout by its + /// owner of ToolStrips + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ToolStripItemOnLocationChangedDescr)] + public event EventHandler LocationChanged { + add { + Events.AddHandler(EventLocationChanged, value); + } + remove { + Events.RemoveHandler(EventLocationChanged, value); + } + } + + /// + /// + /// Specifies the external spacing between this item and any other item or the ToolStrip. + /// + [ + SRDescription(SR.ToolStripItemMarginDescr), + SRCategory(SR.CatLayout) + ] + public Padding Margin { + get { return CommonProperties.GetMargin(this); } + set { + if (Margin != value) { + state[stateUseAmbientMargin] = false; + CommonProperties.SetMargin(this, value); + } + } + } + + + /// + /// + /// Specifies the merge action when merging two ToolStrip. + /// + [ + SRDescription(SR.ToolStripMergeActionDescr), + DefaultValue(MergeAction.Append), + SRCategory(SR.CatLayout) + ] + public MergeAction MergeAction { + get { + bool found; + int action = Properties.GetInteger(PropMergeAction, out found); + if (found) { + return (MergeAction)action; + } + else { + // default value + return MergeAction.Append; + } + } + + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)MergeAction.Append, (int)MergeAction.MatchOnly)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(MergeAction)); + } + Properties.SetInteger(PropMergeAction, (int)value); + } + } + + /// + /// + /// Specifies the merge action when merging two ToolStrip. + /// + [ + SRDescription(SR.ToolStripMergeIndexDescr), + DefaultValue(-1), + SRCategory(SR.CatLayout) + ] + public int MergeIndex { + get { + bool found; + int index = Properties.GetInteger(PropMergeIndex, out found); + if (found) { + return index; + } + else { + // default value + return -1; + } + } + + set { + Properties.SetInteger(PropMergeIndex, value); + } + } + + // required for menus - see VSWhidbey 458967. + internal bool MouseDownAndUpMustBeInSameItem { + get { return state[stateMouseDownAndUpMustBeInSameItem]; } + set { state[stateMouseDownAndUpMustBeInSameItem] = value; } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is + /// pressed. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseDownDescr) + ] + public event MouseEventHandler MouseDown { + add { + Events.AddHandler(EventMouseDown, value); + } + remove { + Events.RemoveHandler(EventMouseDown, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer enters the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseEnterDescr) + ] + public event EventHandler MouseEnter { + add { + Events.AddHandler(EventMouseEnter, value); + } + remove { + Events.RemoveHandler(EventMouseEnter, value); + } + } + + /// + /// + /// Occurs when the mouse pointer leaves the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseLeaveDescr) + ] + public event EventHandler MouseLeave { + add { + Events.AddHandler(EventMouseLeave, value); + } + remove { + Events.RemoveHandler(EventMouseLeave, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer hovers over the contro. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseHoverDescr) + ] + public event EventHandler MouseHover { + add { + Events.AddHandler(EventMouseHover, value); + } + remove { + Events.RemoveHandler(EventMouseHover, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is moved over the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseMoveDescr) + ] + public event MouseEventHandler MouseMove { + add { + Events.AddHandler(EventMouseMove, value); + } + remove { + Events.RemoveHandler(EventMouseMove, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is released. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseUpDescr) + ] + public event MouseEventHandler MouseUp { + add { + Events.AddHandler(EventMouseUp, value); + } + remove { + Events.RemoveHandler(EventMouseUp, value); + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control. The name can be + /// used as a key into the ControlCollection. + /// + [ + Browsable(false), + DefaultValue(null) + ] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this, (string)Properties.GetObject(ToolStripItem.PropName)); + } + set { + if (DesignMode) //InDesignMode the Extender property will set it to the right value. + { + return; + } + Properties.SetObject(ToolStripItem.PropName, value); + } + } + + /// + /// + /// The owner of this ToolStripItem. The owner is essentially a backpointer to + /// the ToolStrip who contains this item in it's item collection. Handy for getting + /// to things such as the ImageList, which would be defined on the ToolStrip. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ToolStrip Owner { + get { + return this.owner; + } + set { + if (owner != value) { + if (owner != null) { + owner.Items.Remove(this); + } + if (value != null) { + value.Items.Add(this); + } + } + } + } + + + /// returns the "parent" item on the preceeding menu which has spawned this item. + /// e.g. File->Open the OwnerItem of Open is File. + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ToolStripItem OwnerItem { + get { + ToolStripDropDown currentParent = null; + + if (ParentInternal != null) { + currentParent = ParentInternal as ToolStripDropDown; + } + else if (Owner != null) { + // parent may be null, but we may be "owned" by a collection. + currentParent = Owner as ToolStripDropDown; + } + + if (currentParent != null) { + return currentParent.OwnerItem; + } + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripItemOwnerChangedDescr) + ] + public event EventHandler OwnerChanged { + add { + Events.AddHandler(EventOwnerChanged, value); + } + remove { + Events.RemoveHandler(EventOwnerChanged, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemOnPaintDescr) + ] + public event PaintEventHandler Paint { + add { + Events.AddHandler(EventPaint, value); + } + remove { + Events.RemoveHandler(EventPaint, value); + } + } + /// + /// + /// The parent of this ToolStripItem. This can be distinct from the owner because + /// the item can fall onto another window (overflow). In this case the overflow + /// would be the parent but the original winbar would be the Owner. The "parent" + /// winbar will be firing things like paint events - where as the "owner" winbar + /// will be containing shared data like image lists. Typically the only one who should + /// set the parent property is the layout manager on the ToolStrip. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + internal protected ToolStrip Parent { + get { + // we decided that there is no "parent" protection for toolstripitems. + // since toolstrip and toolstripitem are tightly coupled. + return ParentInternal; + } + set { + ParentInternal = value; + } + } + + + /// + /// + /// Specifies whether or not the item is glued to the winbar or overflow or + /// can float between the two. + /// + [ + DefaultValue(ToolStripItemOverflow.AsNeeded), + SRDescription(SR.ToolStripItemOverflowDescr), + SRCategory(SR.CatLayout) + ] + public ToolStripItemOverflow Overflow { + get { + return overflow; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemOverflow.Never, (int)ToolStripItemOverflow.AsNeeded)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripGripStyle)); + } + if (overflow != value) { + overflow = value; + if (Owner != null) { + LayoutTransaction.DoLayout(Owner, this.Owner, "Overflow"); + } + } + } + } + + /// + /// + /// Specifies the internal spacing between the contents and the edges of the item + /// + [ + SRDescription(SR.ToolStripItemPaddingDescr), + SRCategory(SR.CatLayout) + ] + public virtual Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + if (Padding != value) { + CommonProperties.SetPadding(this, value); + InvalidateItemLayout(PropertyNames.Padding); + } + } + } + + /// + /// This is explicitly a winbar, because only winbars know how to manage winbaritems + /// + internal ToolStrip ParentInternal { + get { + return parent; + } + set { + if (parent != value) { + ToolStrip oldParent = parent; + parent = value; + OnParentChanged(oldParent, value); + } + } + } + + /// + /// + /// Where the item actually ended up. + /// + [Browsable(false)] + public ToolStripItemPlacement Placement { + get { + return placement; + } + } + + internal Size PreferredImageSize { + get { + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.Image) { + return Size.Empty; + } + + Image image = (Image)Properties.GetObject(PropImage); + bool usingImageList = ((Owner != null) && (Owner.ImageList != null) && (ImageIndexer.ActualIndex >= 0)); + + if (ImageScaling == ToolStripItemImageScaling.SizeToFit) { + ToolStrip ownerToolStrip = Owner; + if (ownerToolStrip != null && (image != null || usingImageList)) { + return ownerToolStrip.ImageScalingSize; + } + } + + + Size imageSize = Size.Empty; + if (usingImageList) { + imageSize = Owner.ImageList.ImageSize; + } + else { + imageSize = (image == null) ? Size.Empty : image.Size; + } + + return imageSize; + + } + + } + /// + /// Retrieves our internal property storage object. If you have a property + /// whose value is not always set, you should store it in here to save + /// space. + /// + internal PropertyStore Properties { + get { + if (propertyStore == null) { + propertyStore = new PropertyStore(); + } + return propertyStore; + } + } + + + /// + /// + /// Returns true if the state of the item is pushed + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool Pressed { + get { + return CanSelect && state[statePressed]; + + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnQueryContinueDragDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event QueryContinueDragEventHandler QueryContinueDrag { + add { + Events.AddHandler(EventQueryContinueDrag, value); + } + remove { + Events.RemoveHandler(EventQueryContinueDrag, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripItemOnQueryAccessibilityHelpDescr)] + public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + Events.AddHandler(EventQueryAccessibilityHelp, value); + } + remove { + Events.RemoveHandler(EventQueryAccessibilityHelp, value); + } + } + + + // Returns the value of the backColor field -- no asking the parent with its color is, etc. + internal Color RawBackColor { + get { + return Properties.GetColor(PropBackColor); + } + } + + internal ToolStripRenderer Renderer { + get { + if (Owner != null) { + return Owner.Renderer; + } + return (ParentInternal != null) ? ParentInternal.Renderer : null; + + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + bool found; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + + if (((RightToLeft)rightToLeft) == RightToLeft.Inherit) { + if (Owner != null) { + rightToLeft = (int)Owner.RightToLeft; + } + else if (ParentInternal != null) { + // case for Overflow & Grip + rightToLeft = (int)ParentInternal.RightToLeft; + } + else { + rightToLeft = (int)DefaultRightToLeft; + } + } + return (RightToLeft)rightToLeft; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)) + { + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + + RightToLeft oldValue = RightToLeft; + + if (Properties.ContainsInteger(PropRightToLeft) || value != RightToLeft.Inherit) { + Properties.SetInteger(PropRightToLeft, (int)value); + } + + if (oldValue != RightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + + + /// + /// Mirrors the image when RTL.Yes. + /// Note we do not change what is returned back from the Image property as this would cause problems with serialization. + /// Instead we only change what is painted - there's an internal MirroredImage property which fills in as + /// e.Image in the ToolStripItemImageRenderEventArgs if the item is RTL.Yes and AutoMirrorImage is turned on. + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemRightToLeftAutoMirrorImageDescr) + ] + public bool RightToLeftAutoMirrorImage { + get { + return state[stateRightToLeftAutoMirrorImage]; + } + set { + if (state[stateRightToLeftAutoMirrorImage] != value) { + state[stateRightToLeftAutoMirrorImage] = value; + Invalidate(); + } + } + } + + internal Image MirroredImage { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (state[stateInvalidMirroredImage]) { + Image image = Image; + if (image != null) { + Image mirroredImage = image.Clone() as Image; + mirroredImage.RotateFlip(RotateFlipType.RotateNoneFlipX); + + Properties.SetObject(PropMirroredImage, mirroredImage); + state[stateInvalidMirroredImage] = false; + return mirroredImage; + } + else { + return null; + } + } + return Properties.GetObject(PropMirroredImage) as Image; + } + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnRightToLeftChangedDescr)] + public event EventHandler RightToLeftChanged { + add { + Events.AddHandler(EventRightToLeft, value); + } + remove { + Events.RemoveHandler(EventRightToLeft, value); + } + } + /// + /// + /// if the item is selected we return true. + /// + /// FAQ: Why dont we have a Hot or MouseIsOver property? + /// After going through the scenarios, we've decided NOT to add a separate MouseIsOver or Hot flag to ToolStripItem. The thing to use is 'Selected'. + /// Why? While the selected thing can be different than the moused over item, the selected item is ALWAYS the one you want to paint differently + /// + /// Scenario 1: Keyboard select an item then select a different item with the mouse. + /// - Do Alt+F to expand your File menu, keyboard down several items. + /// - Mouse over a different item + /// - Notice how two things are never painted hot at the same time, and how the selection changes from the keyboard selected item to the one selected with the mouse. In this case the selection should move with the mouse selection. + /// - Notice how if you hit enter when the mouse is over it, it executes the item. That's selection. + /// Scenario 2: Put focus into a combo box, then mouse over a different item + /// - Notice how all the other items you mouse over do not change the way they are painted, if you hit enter, that goes to the combobox, rather than executing the current item. + /// + /// At first look "MouseIsOver" or "Hot" seems to be the thing people want, but its almost never the desired behavior. A unified selection model is simpler and seems to meet the scenarios. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool Selected { + get { + return CanSelect && !DesignMode && (state[stateSelected] || + (ParentInternal != null && ParentInternal.IsSelectionSuspended && + ParentInternal.LastMouseDownedItem == this)); + } + } + +#if SELECTEDCHANGED + public event EventHandler SelectedChanged { + add { + Events.AddHandler(EventSelectedChanged, value); + } + remove { + Events.RemoveHandler(EventSelectedChanged, value); + } + } +#endif + + /// + /// + internal protected virtual bool ShowKeyboardCues { + get { + if (!DesignMode) { + return ToolStripManager.ShowMenuFocusCues; + } + // default to true. + return true; + } + } + + /// + /// The size of the item + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ToolStripItemSizeDescr) + ] + public virtual Size Size { + get { + return Bounds.Size; + } + set { + Rectangle currentBounds = Bounds; + currentBounds.Size = value; + SetBounds(currentBounds); + } + } + + internal bool SupportsRightClick { + get { + return state[stateSupportsRightClick]; + } + set { + state[stateSupportsRightClick] = value; + } + } + + internal bool SupportsItemClick { + get { + return state[stateSupportsItemClick]; + } + set { + state[stateSupportsItemClick] = value; + } + } + + internal bool SupportsSpaceKey { + get { + return state[stateSupportsSpaceKey]; + } + set { + state[stateSupportsSpaceKey] = value; + } + } + + + internal bool SupportsDisabledHotTracking { + get { + return state[stateSupportsDisabledHotTracking]; + } + set { + state[stateSupportsDisabledHotTracking] = value; + } + } + + /// + /// Summary for Tag + [DefaultValue(null), + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ToolStripItemTagDescr), + TypeConverter(typeof(StringConverter)) + ] + public object Tag { + get { + if (Properties.ContainsObject(ToolStripItem.PropTag)) { + return propertyStore.GetObject(ToolStripItem.PropTag); + } + return null; + + } + set { + Properties.SetObject(ToolStripItem.PropTag, value); + } + } + + /// + /// The text of the item + [ + DefaultValue(""), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemTextDescr) + ] + public virtual string Text { + get { + if (Properties.ContainsObject(ToolStripItem.PropText)) { + return (string)Properties.GetObject(ToolStripItem.PropText); + } + return ""; + + } + set { + if (value != Text) { + Properties.SetObject(ToolStripItem.PropText, value); + OnTextChanged(EventArgs.Empty); + + } + } + + } + + /// + /// + /// + /// Gets or sets the alignment of the text on the label control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemTextAlignDescr) + ] + public virtual ContentAlignment TextAlign { + get { + return textAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (textAlign != value) { + textAlign = value; + InvalidateItemLayout(PropertyNames.TextAlign); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnTextChangedDescr)] + public event EventHandler TextChanged { + add { + Events.AddHandler(EventText, value); + } + remove { + Events.RemoveHandler(EventText, value); + } + } + /// + [ + SRDescription(SR.ToolStripTextDirectionDescr), + SRCategory(SR.CatAppearance) + ] + public virtual ToolStripTextDirection TextDirection { + get { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + if (this.ParentInternal != null) { + // in the case we're on a ToolStripOverflow + textDirection = ParentInternal.TextDirection; + } + else { + textDirection = (Owner == null) ? ToolStripTextDirection.Horizontal : Owner.TextDirection; + } + } + + return textDirection; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripTextDirection.Inherit, (int)ToolStripTextDirection.Vertical270)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripTextDirection)); + } + Properties.SetObject(ToolStripItem.PropTextDirection, value); + InvalidateItemLayout("TextDirection"); + } + } + + /// + [DefaultValue(TextImageRelation.ImageBeforeText), + Localizable(true), + SRDescription(SR.ToolStripItemTextImageRelationDescr), + SRCategory(SR.CatAppearance)] + public TextImageRelation TextImageRelation { + get { + return textImageRelation; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidTextImageRelation(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(TextImageRelation)); + } + if(value != TextImageRelation) { + textImageRelation = value; + InvalidateItemLayout(PropertyNames.TextImageRelation); + } + } + } + + + /// + /// + /// !!!!This property ONLY works when toolStrip.ShowItemToolTips = true!!!! + /// if AutoToolTip is set to true we return the Text as the ToolTipText. + /// + [SRDescription(SR.ToolStripItemToolTipTextDescr)] + [SRCategory(SR.CatBehavior)] + [Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor))] + [Localizable(true)] + public string ToolTipText { + get { + if (AutoToolTip && string.IsNullOrEmpty(toolTipText)) { + string toolText = Text; + if (WindowsFormsUtils.ContainsMnemonic(toolText)) { + // this shouldnt be called a lot so we can take the perf hit here. + toolText = String.Join("", toolText.Split('&')); + } + return toolText; + } + return toolTipText; + } + set { + toolTipText = value; + } + } + + /// + /// Whether or not the item is visible + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ToolStripItemVisibleDescr) + ] + public bool Visible { + get { + return (ParentInternal!=null) && (ParentInternal.Visible) && Available; + } + set { + SetVisibleCore(value); + } + + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnVisibleChangedDescr)] + public event EventHandler VisibleChanged { + add { + Events.AddHandler(EventVisibleChanged, value); + } + remove { + Events.RemoveHandler(EventVisibleChanged, value); + } + } + + /// + /// + /// The width of this ToolStripItem. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int Width { + get { + return Bounds.Width; + } + set { + Rectangle currentBounds = this.Bounds; + SetBounds(currentBounds.X, currentBounds.Y, value, currentBounds.Height); + } + } + + // + // Methods for ToolStripItem + // + + internal void AccessibilityNotifyClients(AccessibleEvents accEvent) { + if (ParentInternal != null) { + int index = ParentInternal.DisplayedItems.IndexOf(this); + ParentInternal.AccessibilityNotifyClients(accEvent, index); + } + } + + private void Animate() { + Animate(!DesignMode && Visible && Enabled && ParentInternal != null); + } + + private void StopAnimate() { + Animate(false); + } + + private void Animate(bool animate) { + if (animate != state[stateCurrentlyAnimatingImage]) { + if (animate) { + if (Image != null) { + ImageAnimator.Animate(Image, new EventHandler(this.OnAnimationFrameChanged)); + state[stateCurrentlyAnimatingImage] = animate; + } + } + else { + if (Image != null) { + ImageAnimator.StopAnimate(Image, new EventHandler(this.OnAnimationFrameChanged)); + state[stateCurrentlyAnimatingImage] = animate; + } + } + } + } + + + internal bool BeginDragForItemReorder() { + if (Control.ModifierKeys == Keys.Alt) { + + if (this.ParentInternal.Items.Contains(this) && this.ParentInternal.AllowItemReorder) { + // we only drag + ToolStripItem item = this as ToolStripItem; + DoDragDrop(item,DragDropEffects.Move); + return true; + } + + } + return false; + } + + + /// + /// + /// constructs the new instance of the accessibility object for this ToolStripItem. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual AccessibleObject CreateAccessibilityInstance() { + return new ToolStripItemAccessibleObject(this); + } + + /// + /// Creates an instance of the object that defines how image and text + /// gets laid out in the ToolStripItem + /// + + internal virtual ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripItemInternalLayout(this); + } + /// + /// + /// Disposes this winbar item... + /// + protected override void Dispose(bool disposing) + { + if (disposing) { + state[stateDisposing] = true; + + if (this.Owner != null) { + StopAnimate(); + Debug.Assert(this.Owner.Items.Contains(this), "How can there be a owner and not be in the collection?"); + this.Owner.Items.Remove(this); + toolStripItemInternalLayout = null; + state[stateDisposed] = true; + } + } + base.Dispose (disposing); + + if (disposing) { + // need to call base() first since the Component.Dispose(_) is listened to by the ComponentChangeService + // which Serializes the object in Undo-Redo transactions. + Properties.SetObject(PropMirroredImage, null); + Properties.SetObject(PropImage, null); + state[stateDisposing] = false; + } + + } + + internal static long DoubleClickTicks { + // (DoubleClickTime in ms) * 1,000,000 ns/1ms * 1 Tick / 100ns = XXX in Ticks. + // Therefore: (DoubleClickTime) * 1,000,000/100 = xxx in Ticks. + get { return SystemInformation.DoubleClickTime * 10000; } + } + + /// + /// + /// Begins a drag operation. The allowedEffects determine which + /// drag operations can occur. If the drag operation needs to interop + /// with applications in another process, data should either be + /// a base managed class (String, Bitmap, or Metafile) or some Object + /// that implements System.Runtime.Serialization.ISerializable. data can also be any Object that + /// implements System.Windows.Forms.IDataObject. + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public DragDropEffects DoDragDrop(Object data, DragDropEffects allowedEffects) { + int[] finalEffect = new int[] {(int)DragDropEffects.None}; + UnsafeNativeMethods.IOleDropSource dropSource = this.DropSource; + IComDataObject dataObject = null; + + dataObject = data as IComDataObject; + if (dataObject == null) { + DataObject iwdata = null; + IDataObject idataObject = data as IDataObject; + if (idataObject != null) { + iwdata = new DataObject(idataObject); + } + else if (data is ToolStripItem) { + + // it seems that the DataObject does string comparison + // on the type, so you can't ask for GetDataPresent of + // a base type (e.g. ToolStripItem) when you are really + // looking at a ToolStripButton. The alternative is + // to set the format string expressly to a string matching + // the type of ToolStripItem + iwdata = new DataObject(); + iwdata.SetData(typeof(ToolStripItem).ToString(), data); + } + else { + iwdata = new DataObject(); + iwdata.SetData(data); + } + dataObject = (IComDataObject)iwdata; + } + + try { + SafeNativeMethods.DoDragDrop(dataObject, dropSource, (int)allowedEffects, finalEffect); + } + catch { + } + return(DragDropEffects)finalEffect[0]; + } + + internal void FireEvent(ToolStripItemEventType met) { + FireEvent(new System.EventArgs(), met); + } + internal void FireEvent(EventArgs e, ToolStripItemEventType met) { + + switch (met) { + case ToolStripItemEventType.LocationChanged: + OnLocationChanged(e); + break; + case ToolStripItemEventType.Paint: + HandlePaint(e as PaintEventArgs); + break; + case ToolStripItemEventType.MouseHover: + // disabled toolstrip items should show tooltips. + // we wont raise mouse events though. + if (!Enabled && ParentInternal != null && !string.IsNullOrEmpty(ToolTipText)) { + ParentInternal.UpdateToolTip(this); + } + else { + FireEventInteractive(e, met); + } + break; + case ToolStripItemEventType.MouseEnter: + HandleMouseEnter(e); + break; + case ToolStripItemEventType.MouseLeave: + // disabled toolstrip items should also clear tooltips. + // we wont raise mouse events though. + if (!Enabled && ParentInternal != null) { + ParentInternal.UpdateToolTip(null); + } + else { + HandleMouseLeave(e); + } + break; + case ToolStripItemEventType.MouseMove: + // VSWhidbey 266970: disabled items typically dont get mouse move + // but they should be allowed to re-order if the ALT key is pressed + if (!Enabled && ParentInternal != null) { + BeginDragForItemReorder(); + } + else { + FireEventInteractive(e, met); + } + break; + default: + FireEventInteractive(e, met); + break; + } + + } + + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + internal void FireEventInteractive(EventArgs e, ToolStripItemEventType met) { + if (Enabled) { + switch (met) { + case ToolStripItemEventType.MouseMove: + HandleMouseMove(e as MouseEventArgs); + break; + case ToolStripItemEventType.MouseHover: + HandleMouseHover(e as EventArgs); + break; + case ToolStripItemEventType.MouseUp: + HandleMouseUp(e as MouseEventArgs); + break; + case ToolStripItemEventType.MouseDown: + HandleMouseDown(e as MouseEventArgs); + break; + case ToolStripItemEventType.Click: + HandleClick(e); + break; + case ToolStripItemEventType.DoubleClick: + HandleDoubleClick(e); + break; + default: + Debug.Assert(false, "Invalid event type."); + break; + } + } + } + + private Font GetOwnerFont() { + if (Owner != null) { + return Owner.Font; + } + return null; + } + + /// we dont want a public settable property... and usually owner will work + /// except for things like the overflow button + public ToolStrip GetCurrentParent() { + return this.Parent; + } + internal ToolStripDropDown GetCurrentParentDropDown() { + if (ParentInternal != null) { + return ParentInternal as ToolStripDropDown; + } + else { + return Owner as ToolStripDropDown; + } + } + /// + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + public virtual Size GetPreferredSize(Size constrainingSize) { + + // Switch Size.Empty to maximum possible values + constrainingSize = LayoutUtils.ConvertZeroToUnbounded(constrainingSize); + return InternalLayout.GetPreferredSize(constrainingSize - Padding.Size) + Padding.Size; + } + + internal Size GetTextSize() { + if (string.IsNullOrEmpty(Text)) { + return Size.Empty; + } + else if (cachedTextSize == Size.Empty) { + cachedTextSize = TextRenderer.MeasureText(Text, Font); + } + return cachedTextSize; + } + + + /// + /// + /// Invalidates the ToolStripItem + /// + public void Invalidate() { + if (this.ParentInternal != null) { + ParentInternal.Invalidate(this.Bounds, true); + } + } + + + /// + /// + /// invalidates a rectangle within the ToolStripItem's bounds + /// + public void Invalidate(Rectangle r) { + // the only value add to this over calling invalidate on the ToolStrip is that + // you can specify the rectangle with coordinates relative to the upper left hand + // corner of the ToolStripItem. + Point rectangleLocation = this.TranslatePoint(r.Location, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ToolStripCoords); + + if (this.ParentInternal != null) { + ParentInternal.Invalidate(new Rectangle(rectangleLocation, r.Size), true); + } + } + + internal void InvalidateItemLayout(string affectedProperty, bool invalidatePainting) { + this.toolStripItemInternalLayout = null; + + if (Owner != null) { + LayoutTransaction.DoLayout(Owner, this, affectedProperty); + } + + if (invalidatePainting && Owner != null) { + Owner.Invalidate(); + } + } + internal void InvalidateItemLayout(string affectedProperty) { + InvalidateItemLayout(affectedProperty, /*invalidatePainting*/true); + } + + internal void InvalidateImageListImage() { + // invalidate the cache. + if (ImageIndexer.ActualIndex >= 0) { + Properties.SetObject(PropImage, null); + InvalidateItemLayout(PropertyNames.Image); + } + } + + internal void InvokePaint() { + if (this.ParentInternal != null) { + this.ParentInternal.InvokePaintItem(this); + } + } + + /// + protected internal virtual bool IsInputKey(Keys keyData) { + return false; + } + + /// + protected internal virtual bool IsInputChar(char charCode) { + return false; + } + + // + // Private handlers which are the equivilant of Control.Wm + // + + private void HandleClick(System.EventArgs e) { + Debug.Assert(Enabled, "Who called me when I am disabled?"); + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleClick"); + + try { + if (!DesignMode) { + state[statePressed] = true; + } + // force painting w/o using message loop here because it may be quite a long + // time before it gets pumped again. + InvokePaint(); + + if (SupportsItemClick && Owner != null) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleItemClick"); + Owner.HandleItemClick(this); + } + + OnClick(e); + + if (SupportsItemClick && Owner != null) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleItemClicked"); + Owner.HandleItemClicked(this); + } + } + finally { + state[statePressed] = false; + } + // when we get around to it, paint unpressed. + Invalidate(); + + } + private void HandleDoubleClick(System.EventArgs e) { + OnDoubleClick(e); + } + private void HandlePaint(PaintEventArgs e) { + Animate(); + ImageAnimator.UpdateFrames(this.Image); + + OnPaint(e); + RaisePaintEvent(EventPaint, e); + } + private void HandleMouseEnter(System.EventArgs e) { + + if (!DesignMode) { + if (ParentInternal != null + && ParentInternal.CanHotTrack + && ParentInternal.ShouldSelectItem()) { + + if (Enabled) { + // calling select can dismiss a child dropdown which would break auto-expansion. + // save off auto expand and restore it. + bool autoExpand = ParentInternal.MenuAutoExpand; + + if (ParentInternal.LastMouseDownedItem == this) { + // Same as Control.MouseButtons == MouseButtons.Left, but slightly more efficient. + if (UnsafeNativeMethods.GetKeyState((int)Keys.LButton) < 0) { + this.Push(true); + } + } + + Select(); + ParentInternal.MenuAutoExpand = autoExpand; + } + else if (SupportsDisabledHotTracking) { + Select(); + } + } + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutMouseEnter(this); + } + + if (Enabled) { + OnMouseEnter(e); + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseEnter"); + RaiseEvent(EventMouseEnter, e); + } + + } + private void HandleMouseMove(System.Windows.Forms.MouseEventArgs mea) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseMove"); + + if (Enabled && CanSelect && !Selected) { + if (ParentInternal != null + && ParentInternal.CanHotTrack + && ParentInternal.ShouldSelectItem()) { + // this is the case where we got a mouse enter, but ShouldSelectItem + // returned false. + // typically occus when a window first opens - we get a mouse enter on the item + // the cursor is hovering over - but we dont actually want to change selection to it. + Select(); + } + } + OnMouseMove(mea); + RaiseMouseEvent(EventMouseMove, mea); + + } + private void HandleMouseHover(EventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseHover"); + OnMouseHover(e); + RaiseEvent(EventMouseHover, e); + } + private void HandleLeave() { + if (state[stateMouseDownAndNoDrag] || state[statePressed] || state[stateSelected]) { + state[stateMouseDownAndNoDrag | statePressed | stateSelected] = false; + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + + Invalidate(); + } + } + private void HandleMouseLeave(System.EventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseLeave"); + HandleLeave(); + if (Enabled) { + OnMouseLeave(e); + RaiseEvent(EventMouseLeave, e); + } + } + private void HandleMouseDown(MouseEventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseDown"); + + state[stateMouseDownAndNoDrag] = !BeginDragForItemReorder(); + if (state[stateMouseDownAndNoDrag]) { + if (e.Button == MouseButtons.Left) { + this.Push(true); + } + // + OnMouseDown(e); + RaiseMouseEvent(EventMouseDown, e); + } + } + private void HandleMouseUp(MouseEventArgs e) { + + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseUp"); + + bool fireMouseUp = (ParentInternal.LastMouseDownedItem == this); + + if (!fireMouseUp && !MouseDownAndUpMustBeInSameItem) { + // in the case of menus, you can mouse down on one item and mouse up + // on another. We do need to be careful (as in the case of VSWhidbey 482156) + // that the mouse has actually moved from when a dropdown has been opened - + // otherwise we may accidentally click what's underneath the mouse at the time + // the dropdown is opened. + fireMouseUp = ParentInternal.ShouldSelectItem(); + } + + + if (state[stateMouseDownAndNoDrag] || fireMouseUp) { + this.Push(false); + + if (e.Button == MouseButtons.Left || (e.Button == MouseButtons.Right && state[stateSupportsRightClick])) { + bool shouldFireDoubleClick = false; + if (DoubleClickEnabled) { + long newTime = DateTime.Now.Ticks; + long deltaTicks = newTime - lastClickTime; + lastClickTime = newTime; + // use >= for cases where the delta is so fast DateTime cannot pick up the delta. + Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking"); + // if we've seen a mouse up less than the double click time ago, we should fire. + if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) { + shouldFireDoubleClick = true; + } + } + if (shouldFireDoubleClick) { + HandleDoubleClick(new System.EventArgs()); + // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime. + lastClickTime = 0; + } + else { + HandleClick(new System.EventArgs()); + } + } + + // + OnMouseUp(e); + RaiseMouseEvent(EventMouseUp, e); + + + } + } + + + internal virtual void OnAccessibleDescriptionChanged(EventArgs e) { + } + internal virtual void OnAccessibleNameChanged(EventArgs e) { + } + internal virtual void OnAccessibleDefaultActionDescriptionChanged(EventArgs e) { + } + internal virtual void OnAccessibleRoleChanged(EventArgs e) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackColorChanged(EventArgs e) { + this.Invalidate(); + RaiseEvent(EventBackColorChanged, e); + } + + /// + protected virtual void OnBoundsChanged() { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Bounds); + InternalLayout.PerformLayout(); + } + + + + /// + protected virtual void OnClick(System.EventArgs e) { + RaiseEvent(EventClick, e); + } + + /// + protected internal virtual void OnLayout(LayoutEventArgs e) { + } + + /// + /// Explicit support of DropTarget + /// + /// + /// + void IDropTarget.OnDragEnter(DragEventArgs dragEvent) { + OnDragEnter(dragEvent); + } + /// + /// + void IDropTarget.OnDragOver(DragEventArgs dragEvent) { + OnDragOver(dragEvent); + } + /// + /// + void IDropTarget.OnDragLeave(EventArgs e) { + OnDragLeave(e); + } + /// + /// + void IDropTarget.OnDragDrop(DragEventArgs dragEvent) { + OnDragDrop(dragEvent); + } + /// + /// Explicit support of DropSource + /// + /// + /// + void ISupportOleDropSource.OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEventArgs) { + OnGiveFeedback(giveFeedbackEventArgs); + } + /// + /// + void ISupportOleDropSource.OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEventArgs) { + OnQueryContinueDrag(queryContinueDragEventArgs); + } + + + private void OnAnimationFrameChanged(object o, EventArgs e) { + + ToolStrip parent = ParentInternal; + if (parent != null) { + if (parent.Disposing || parent.IsDisposed) { + return; + } + + if (parent.IsHandleCreated && parent.InvokeRequired) { + parent.BeginInvoke(new EventHandler(this.OnAnimationFrameChanged), new object[]{o,e}); + return; + } + + Invalidate(); + } + } + + protected virtual void OnAvailableChanged(System.EventArgs e) { + RaiseEvent(EventAvailableChanged, e); + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.onEnter to send this event to any registered event listeners. + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragEnter to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragEnter(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragEnter, dragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragOver to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragOver(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragOver, dragEvent); + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragLeave to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragLeave(EventArgs e) { + RaiseEvent(EventDragLeave, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragDrop to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragDrop(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragDrop, dragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDisplayStyleChanged(EventArgs e) { + RaiseEvent(EventDisplayStyleChanged, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onGiveFeedback to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] // PM review done + protected virtual void OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEvent) { + GiveFeedbackEventHandler handler = (GiveFeedbackEventHandler)Events[EventGiveFeedback]; + if (handler != null) handler(this,giveFeedbackEvent); + } + + internal virtual void OnImageScalingSizeChanged(EventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onQueryContinueDrag to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] // PM review done + protected virtual void OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEvent) { + RaiseQueryContinueDragEvent(EventQueryContinueDrag, queryContinueDragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnDoubleClick(System.EventArgs e) { + RaiseEvent(EventDoubleClick, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnEnabledChanged(System.EventArgs e) { + RaiseEvent(EventEnabledChanged, e); + Animate(); + } + + internal void OnInternalEnabledChanged(System.EventArgs e) { + RaiseEvent(EventInternalEnabledChanged, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnForeColorChanged(EventArgs e) { + this.Invalidate(); + RaiseEvent(EventForeColorChanged, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFontChanged(EventArgs e) { + cachedTextSize = Size.Empty; + // PERF - only invalidate if we actually care about the font + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + this.InvalidateItemLayout(PropertyNames.Font); + } + else { + this.toolStripItemInternalLayout = null; + } + RaiseEvent(EventFontChanged, e); + } + + /// + protected virtual void OnLocationChanged (System.EventArgs e) { + RaiseEvent(EventLocationChanged, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseEnter(EventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseHover(EventArgs e) { + if (ParentInternal != null && !string.IsNullOrEmpty(ToolTipText)) { + ParentInternal.UpdateToolTip(this); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseLeave(EventArgs e) { + if (ParentInternal != null) { + ParentInternal.UpdateToolTip(null); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseDown(MouseEventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseUp(MouseEventArgs e) { + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnPaint(System.Windows.Forms.PaintEventArgs e) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackColorChanged(EventArgs e) { + Color backColor = Properties.GetColor(PropBackColor); + if (backColor.IsEmpty) { + OnBackColorChanged(e); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnParentChanged(ToolStrip oldParent, ToolStrip newParent) { + SetAmbientMargin(); + if ((oldParent != null) && (oldParent.DropTargetManager != null)) { + oldParent.DropTargetManager.EnsureUnRegistered(this); + } + if (AllowDrop && (newParent != null)) { + EnsureParentDropTargetRegistered(); + } + Animate(); + } + + /// + /// + /// Occurs when this.Parent.Enabled changes. + /// + protected internal virtual void OnParentEnabledChanged(System.EventArgs e) { + OnEnabledChanged(EventArgs.Empty); + } + + /// + /// + /// Occurs when the font property has changed on the parent - used to notify inheritors of the font property that + /// the font has changed + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal protected virtual void OnOwnerFontChanged(EventArgs e) { + if (Properties.GetObject(PropFont) == null) { + OnFontChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentForeColorChanged(EventArgs e) { + Color foreColor = Properties.GetColor(PropForeColor); + if (foreColor.IsEmpty) { + OnForeColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal virtual void OnParentRightToLeftChanged(EventArgs e) { + if (!Properties.ContainsInteger(PropRightToLeft) || ((RightToLeft)Properties.GetInteger(PropRightToLeft)) == RightToLeft.Inherit) { + OnRightToLeftChanged(e); + } + } + + + /// + /// + /// Occurs when the owner of an item changes. + /// + protected virtual void OnOwnerChanged(EventArgs e) { + RaiseEvent(EventOwnerChanged,e); + SetAmbientMargin(); + if (Owner != null) { + // check if we need to fire OnRightToLeftChanged + bool found = false; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + if ((rightToLeft == (int)RightToLeft.Inherit) && RightToLeft != DefaultRightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal void OnOwnerTextDirectionChanged() { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + InvalidateItemLayout("TextDirection"); + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnRightToLeftChanged(EventArgs e) { + InvalidateItemLayout(PropertyNames.RightToLeft); + RaiseEvent(EventRightToLeft, e); + + } +#if SELECTEDCHANGED + protected virtual void OnSelectedChanged(EventArgs e) { + RaiseEvent(EventSelectedChanged, e); + } +#endif + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTextChanged(EventArgs e) { + cachedTextSize = Size.Empty; + // VSWhidbey 399025: make sure we clear the cache before we perform the layout. + InvalidateItemLayout(PropertyNames.Text); + RaiseEvent(EventText, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnVisibleChanged(System.EventArgs e) { + if (Owner != null && !(Owner.IsDisposed || Owner.Disposing)) { + Owner.OnItemVisibleChanged(new ToolStripItemEventArgs(this), /*performLayout*/true); + } + RaiseEvent(EventVisibleChanged, e); + Animate(); + } + + public void PerformClick() { + if (Enabled && Available) { + FireEvent(ToolStripItemEventType.Click); + } + } + + /// + /// Pushes the button. + /// + /// + internal void Push(bool push) { + if (!CanSelect || !Enabled || DesignMode) { + return; + } + + if (state[statePressed] != push) { + state[statePressed] = push; + if (Available) { + this.Invalidate(); + } + } + } + /// + /// + /// See Control.ProcessDialogKey for more info. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected internal virtual bool ProcessDialogKey(Keys keyData) { + // + if (keyData == Keys.Enter || (state[stateSupportsSpaceKey] && keyData == Keys.Space)) { + FireEvent(ToolStripItemEventType.Click); + if (ParentInternal != null && !ParentInternal.IsDropDown && !(AccessibilityImprovements.Level2 && !Enabled)) { + ParentInternal.RestoreFocusInternal(); + } + return true; + } + return false; + } + /// + /// + /// See Control.ProcessCmdKey for more info. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessCmdKey(ref Message m, Keys keyData) { + return false; + } + + /// + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal virtual bool ProcessMnemonic(char charCode) { + // checking IsMnemonic is not necessary - control does this for us. + FireEvent(ToolStripItemEventType.Click); + return true; + } + + + internal void RaiseCancelEvent(object key, CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + internal void RaiseDragEvent(object key, DragEventArgs e) { + DragEventHandler handler = (DragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseEvent(object key, EventArgs e) { + EventHandler handler = (EventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseKeyEvent(object key, KeyEventArgs e) { + KeyEventHandler handler = (KeyEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseKeyPressEvent(object key, KeyPressEventArgs e) { + KeyPressEventHandler handler = (KeyPressEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseMouseEvent(object key, MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaisePaintEvent(object key, PaintEventArgs e) { + PaintEventHandler handler = (PaintEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + internal void RaiseQueryContinueDragEvent(object key, QueryContinueDragEventArgs e) { + QueryContinueDragEventHandler handler = (QueryContinueDragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + private void ResetToolTipText() { + toolTipText = null; + } + + // This is de facto behind the 4.8/EnableDpiMessaging/ToolStrip HighDpi quirks. + internal virtual void ToolStrip_RescaleConstants(int oldDpi, int newDpi) { + DeviceDpi = newDpi; + RescaleConstantsInternal(newDpi); + + OnFontChanged(EventArgs.Empty); + } + + internal void RescaleConstantsInternal(int newDpi) { + ToolStripManager.CurrentDpi = newDpi; + defaultFont = ToolStripManager.DefaultFont; + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin, deviceDpi); + scaledDefaultStatusStripMargin = DpiHelper.LogicalToDeviceUnits(defaultStatusStripMargin, deviceDpi); + } + + /// + /// + /// Selects the item + /// + public void Select() { + +#if DEBUG + // let's not snap the stack trace unless we're debugging selection. + if (ToolStrip.SelectionDebug.TraceVerbose) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "[Selection DBG] WBI.Select: {0} \r\n{1}\r\n", this.ToString(), new StackTrace().ToString().Substring(0,200))); + } +#endif + if (!CanSelect) { + return; + } + + if (Owner != null && Owner.IsCurrentlyDragging) { + // make sure we dont select during a drag operation. + return; + } + if (ParentInternal != null && ParentInternal.IsSelectionSuspended) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[Selection DBG] BAILING, selection is currently suspended"); + return; + } + + if (!Selected) { + state[stateSelected] = true; + if (ParentInternal != null) { + ParentInternal.NotifySelectionChange(this); + Debug.Assert(state[stateSelected], "calling notify selection change changed the selection state of this item"); + } + if (IsOnDropDown) { + if (OwnerItem != null && OwnerItem.IsOnDropDown) { + // ensure the selection is moved back to our owner item. + OwnerItem.Select(); + } + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(this); + } + + if (AccessibilityImprovements.Level3 && AccessibilityObject is ToolStripItemAccessibleObject) { + ((ToolStripItemAccessibleObject)AccessibilityObject).RaiseFocusChanged(); + } + +#if SELECTEDCHANGED + OnSelectedChanged(EventArgs.Empty); +#endif + } + } + + internal void SetOwner(ToolStrip newOwner) { + if (owner != newOwner) { + Font f = this.Font; + + if (owner != null) { + owner.rescaleConstsCallbackDelegate -= ToolStrip_RescaleConstants; + } + owner = newOwner; + + if (owner != null) { + owner.rescaleConstsCallbackDelegate += ToolStrip_RescaleConstants; + } + + // clear the parent if the owner is null... + // + if (newOwner == null) { + this.ParentInternal = null; + } + if (!state[stateDisposing] && !IsDisposed) { + OnOwnerChanged(EventArgs.Empty); + if (f != Font) { + OnFontChanged(EventArgs.Empty); + } + } + } + + } + + + + protected virtual void SetVisibleCore(bool visible) { + if (state[stateVisible] != visible) { + state[stateVisible]= visible; + Unselect(); + Push(false); + + OnAvailableChanged(EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); + } + } + + /// + /// + /// Sets the bounds of the item + /// + internal protected virtual void SetBounds(Rectangle bounds) { + Rectangle oldBounds = this.bounds; + this.bounds = bounds; + + if (! state[stateContstructing]) { + // Dont fire while we're in the base constructor as the inherited + // class may not have had a chance to initialize yet. + + if (this.bounds != oldBounds) { + OnBoundsChanged(); + } + + if (this.bounds.Location != oldBounds.Location) { + OnLocationChanged(EventArgs.Empty); + } + } + } + + /// + /// Sets the bounds of the item + /// + internal void SetBounds(int x, int y, int width, int height) { + SetBounds(new Rectangle(x,y,width,height)); + } + /// + /// Sets the placement of the item + /// + internal void SetPlacement(ToolStripItemPlacement placement) { + this.placement = placement; + } + + // Some implementations of DefaultMargin check which container they + // are on. They need to be re-evaluated when the containership changes. + // DefaultMargin will stop being honored the moment someone sets the Margin property. + internal void SetAmbientMargin() { + if (state[stateUseAmbientMargin] && Margin != DefaultMargin) { + CommonProperties.SetMargin(this, DefaultMargin); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageTransparentColor() { + return ImageTransparentColor != Color.Empty; + } + + /// + /// Returns true if the backColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeBackColor() { + Color backColor = Properties.GetColor(PropBackColor); + return !backColor.IsEmpty; + } + + private bool ShouldSerializeDisplayStyle() { + return DisplayStyle != DefaultDisplayStyle; + } + + private bool ShouldSerializeToolTipText() { + return !string.IsNullOrEmpty(toolTipText); + } + /// + /// Returns true if the foreColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeForeColor() { + Color foreColor = Properties.GetColor(PropForeColor); + return !foreColor.IsEmpty; + } + + /// + /// Returns true if the font should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeFont() { + bool found; + object font = Properties.GetObject(PropFont, out found); + return (found && font != null); + } + + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializePadding() { + return (Padding != DefaultPadding); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeMargin() { + return (Margin != DefaultMargin); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeVisible() { + return !state[stateVisible]; // only serialize if someone turned off visiblilty + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImage() { + return (Image != null) && (ImageIndexer.ActualIndex < 0); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageKey() { + return (Image != null) && (ImageIndexer.ActualIndex >= 0) && (ImageIndexer.Key != null && ImageIndexer.Key.Length != 0); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageIndex() { + return (Image != null) && (ImageIndexer.ActualIndex >= 0) && (ImageIndexer.Index != -1); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeRightToLeft() { + bool found = false; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + return false; + } + return (rightToLeft != (int)DefaultRightToLeft); + } + + private bool ShouldSerializeTextDirection() { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + return textDirection != ToolStripTextDirection.Inherit; + } + + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetBackColor() { + BackColor = Color.Empty; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetDisplayStyle() { + DisplayStyle = DefaultDisplayStyle; + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetForeColor() { + ForeColor = Color.Empty; + } + + + /// + /// + /// Resets the Font to be based on the parent's Font. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetFont() { + Font = null; + } + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetImage() { + Image = null; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private void ResetImageTransparentColor() { + ImageTransparentColor = Color.Empty; + } + + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetMargin() { + state[stateUseAmbientMargin] = true; + SetAmbientMargin(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetPadding() { + CommonProperties.ResetPadding(this); + } + /// + /// + /// Resets the RightToLeft to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetRightToLeft() { + RightToLeft = RightToLeft.Inherit; + } + + /// + /// + /// Resets the TextDirection to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetTextDirection() { + TextDirection = ToolStripTextDirection.Inherit; + } + + /// + /// Translates a point from one coordinate system to another + /// + internal Point TranslatePoint(Point fromPoint, ToolStripPointType fromPointType, ToolStripPointType toPointType) { + ToolStrip parent = ParentInternal; + + if (parent == null) { + parent = (IsOnOverflow && Owner != null)? Owner.OverflowButton.DropDown : Owner; + } + if (parent == null) { + // should not throw here as it's an internal function call. + Debug.Fail("could not determine current parent"); + return fromPoint; + } + + if (fromPointType == toPointType) { + return fromPoint; + } + + Point toPoint = Point.Empty; + Point currentToolStripItemLocation = Bounds.Location; + + // From: Screen + // To: ToolStrip or ToolStripItem + if (fromPointType == ToolStripPointType.ScreenCoords) { + // Convert ScreenCoords --> ToolStripCoords + toPoint = parent.PointToClient(fromPoint); + + // Convert ToolStripCoords --> ToolStripItemCoords + if (toPointType == ToolStripPointType.ToolStripItemCoords) { + toPoint.X += currentToolStripItemLocation.X; + toPoint.Y += currentToolStripItemLocation.Y; + } + } + // From: ToolStrip or ToolStripItem + // To: Screen or ToolStripItem + else { + // Convert "fromPoint" ToolStripItemCoords --> ToolStripCoords + if (fromPointType == ToolStripPointType.ToolStripItemCoords) { + fromPoint.X += currentToolStripItemLocation.X; + fromPoint.Y += currentToolStripItemLocation.Y; + } + + // At this point, fromPoint is now in ToolStrip coordinates. + + // Convert ToolStripCoords --> ScreenCoords + if (toPointType == ToolStripPointType.ScreenCoords) { + toPoint = parent.PointToScreen(fromPoint); + } + // Convert ToolStripCoords --> ToolStripItemCoords + else if (toPointType == ToolStripPointType.ToolStripItemCoords) { + fromPoint.X -= currentToolStripItemLocation.X; + fromPoint.Y -= currentToolStripItemLocation.Y; + toPoint = fromPoint; + } + else { + Debug.Assert((toPointType == ToolStripPointType.ToolStripCoords), "why are we here! - investigate"); + toPoint = fromPoint; + } + + } + return toPoint; + + } + + internal ToolStrip RootToolStrip { + get { + ToolStripItem item = this; + while (item.OwnerItem != null) { + item = item.OwnerItem; + } + return item.ParentInternal; + } + } + + /// + /// + /// ToString support + /// + public override string ToString() { + if (Text != null && this.Text.Length != 0) { + return this.Text; + } + return base.ToString(); + } + + /// + /// removes selection bits from item state + /// + internal void Unselect() { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "[Selection DBG] WBI.Unselect: {0}", this.ToString())); + if (state[stateSelected]) { + state[stateSelected] = false; + if (Available) { + Invalidate(); + if (ParentInternal != null) { + ParentInternal.NotifySelectionChange(this); + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } +#if SELECTEDCHANGED + OnSelectedChanged(EventArgs.Empty); +#endif + } + } + } + + #region IKeyboardToolTip implementation + + bool IKeyboardToolTip.CanShowToolTipsNow() { + return this.Visible && this.parent != null && ((IKeyboardToolTip)this.parent).AllowsChildrenToShowToolTips(); + } + + Rectangle IKeyboardToolTip.GetNativeScreenRectangle() { + return this.AccessibilityObject.Bounds; + } + + IList IKeyboardToolTip.GetNeighboringToolsRectangles() { + List neighbors = new List(3); + if (this.parent != null) { + ToolStripItemCollection items = this.parent.DisplayedItems; + int i = 0, count = items.Count; + bool found = false; + while (!found && i < count) { + found = Object.ReferenceEquals(items[i], this); + if (found) { + int previousIndex = i - 1; + if (previousIndex >= 0) { + neighbors.Add(((IKeyboardToolTip)items[previousIndex]).GetNativeScreenRectangle()); + } + + int nextIndex = i + 1; + if (nextIndex < count) { + neighbors.Add(((IKeyboardToolTip)items[nextIndex]).GetNativeScreenRectangle()); + } + } + else { + i++; + } + } + Debug.Assert(i < count, "Item has a parent set but the parent doesn't own the item"); + } + + ToolStripDropDown dropDown = this.parent as ToolStripDropDown; + if (dropDown != null && dropDown.OwnerItem != null) { + neighbors.Add(((IKeyboardToolTip)dropDown.OwnerItem).GetNativeScreenRectangle()); + } + + return neighbors; + } + + bool IKeyboardToolTip.IsHoveredWithMouse() { + return ((IKeyboardToolTip)this).GetNativeScreenRectangle().Contains(Control.MousePosition); + } + + bool IKeyboardToolTip.HasRtlModeEnabled() { + return this.parent != null && ((IKeyboardToolTip)this.parent).HasRtlModeEnabled(); + } + + bool IKeyboardToolTip.AllowsToolTip() { + return true; + } + + IWin32Window IKeyboardToolTip.GetOwnerWindow() { + Debug.Assert(this.ParentInternal != null, "Tool Strip Item Parent is null"); + return this.ParentInternal; + } + + void IKeyboardToolTip.OnHooked(ToolTip toolTip) { + this.OnKeyboardToolTipHook(toolTip); + } + + void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) { + this.OnKeyboardToolTipUnhook(toolTip); + } + + string IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) { + return this.ToolTipText; + } + + bool IKeyboardToolTip.ShowsOwnToolTip() { + return true; + } + + bool IKeyboardToolTip.IsBeingTabbedTo() { + return this.IsBeingTabbedTo(); + } + + bool IKeyboardToolTip.AllowsChildrenToShowToolTips() { + return true; + } + + #endregion + + internal virtual void OnKeyboardToolTipHook(ToolTip toolTip) { + } + + internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip) { + } + + internal virtual bool IsBeingTabbedTo() { + return ToolStrip.AreCommonNavigationalKeysDown(); + } + + /// + /// + /// An implementation of AccessibleChild for use with ToolStripItems + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripItemAccessibleObject : AccessibleObject { + + // Member variables + + private ToolStripItem ownerItem = null; // The associated ToolStripItem for this AccessibleChild (if any) + + private AccessibleStates additionalState = AccessibleStates.None; // Test hook for the designer + + private int[] runtimeId = null; // Used by UIAutomation + // constructors + + /// + /// + /// [To be supplied.] + /// + public ToolStripItemAccessibleObject(ToolStripItem ownerItem) { + + Debug.Assert(ownerItem != null, "Cannot construct a ToolStripItemAccessibleObject with a null ownerItem"); + if (ownerItem == null) { + throw new ArgumentNullException("ownerItem"); + } + + this.ownerItem = ownerItem; + } + + /// + public override string DefaultAction { + get { + string defaultAction = ownerItem.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + else { + return SR.GetString(SR.AccessibleActionPress); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Description { + get { + string description = ownerItem.AccessibleDescription; + if (description != null) { + return description; + } + else { + return base.Description; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Help { + get { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[ToolStripItem.EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + return args.HelpString; + } + else { + return base.Help; + } + } + } + + public override string KeyboardShortcut { + get { + // This really is the Mnemonic - NOT the shortcut. E.g. in notepad Edit->Replace is Control+H + // but the KeyboardShortcut comes up as the mnemonic 'r'. + char mnemonic = WindowsFormsUtils.GetMnemonic(ownerItem.Text, false); + if (ownerItem.IsOnDropDown) { + // no ALT on dropdown + return (mnemonic == (char) 0) ? string.Empty : mnemonic.ToString(); + } + return (mnemonic == (char) 0) ? string.Empty : ("Alt+" + mnemonic); + + } + } + + internal override int[] RuntimeId { + get { + if (AccessibilityImprovements.Level1) { + if (runtimeId == null) { + // we need to provide a unique ID + // others are implementing this in the same manner + // first item is static - 0x2a + // [AccessibilityImprovements.Level3] first item should be UiaAppendRuntimeId since this is not a top-level element of the fragment. + // second item can be anything, but here it is a hash. For toolstrip hash is unique even with child controls. Hwnd is not. + + runtimeId = new int[2]; + runtimeId[0] = AccessibilityImprovements.Level3 ? NativeMethods.UiaAppendRuntimeId : 0x2a; + runtimeId[1] = ownerItem.GetHashCode(); + } + return runtimeId; + } + else { + return base.RuntimeId; + } + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + + if (AccessibilityImprovements.Level1) { + if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + else if (propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) { + return (Object)this.IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + } + + if (AccessibilityImprovements.Level3) { + switch (propertyID) { + case NativeMethods.UIA_IsEnabledPropertyId: + return ownerItem.Enabled; + case NativeMethods.UIA_IsOffscreenPropertyId: + return ownerItem.Placement != ToolStripItemPlacement.Main; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return ownerItem.CanSelect; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return ownerItem.Selected; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + } + } + + return base.GetPropertyValue(propertyID); + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + string name = ownerItem.AccessibleName; + if (name != null) { + return name; + } + + string baseName = base.Name; + + if (baseName == null || baseName.Length == 0) { + return WindowsFormsUtils.TextWithoutMnemonics(ownerItem.Text); + } + + return baseName; + } + set { + ownerItem.AccessibleName = value; + } + } + + /// + /// + /// [To be supplied.] + /// + internal ToolStripItem Owner { + get { + return ownerItem; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = ownerItem.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return AccessibleRole.PushButton; + } + + } + } + + public override AccessibleStates State { + get { + if (!ownerItem.CanSelect) { + return base.State | additionalState; + } + + + if (!ownerItem.Enabled) { + + if (AccessibilityImprovements.Level2) { + if (ownerItem.Selected && ownerItem is ToolStripMenuItem) { + return AccessibleStates.Unavailable | additionalState | AccessibleStates.Focused; + } + } + + // VSO 436154 - Disabled menu items that are selected must have focus + // state so that Narrator can announce them. + if (AccessibilityImprovements.Level1) { + if (ownerItem.Selected && ownerItem is ToolStripMenuItem) { + return AccessibleStates.Focused; + } + } + + return AccessibleStates.Unavailable | additionalState; + } + + AccessibleStates accState = AccessibleStates.Focusable | additionalState; + + // + /*if (HasDropDownItems) { + accState |= AccessibleState.HasPopup; + }*/ + + if (ownerItem.Selected || ownerItem.Pressed) { + accState |= AccessibleStates.Focused | AccessibleStates.HotTracked; + } + if (ownerItem.Pressed) { + accState |= AccessibleStates.Pressed; + } + return accState; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + if (Owner != null) { + ((ToolStripItem)Owner).PerformClick(); + } + } + /// + /// + /// [To be supplied.] + /// + public override int GetHelpTopic(out string fileName) { + int topic = 0; + + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[ToolStripItem.EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + + fileName = args.HelpNamespace; + if (fileName != null && fileName.Length > 0) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + + try { + topic = Int32.Parse(args.HelpKeyword, CultureInfo.InvariantCulture); + } + catch { + } + + return topic; + } + else { + return base.GetHelpTopic(out fileName); + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) { + ToolStripItem nextItem = null; + + if (Owner != null) { + ToolStrip parent = Owner.ParentInternal; + if (parent == null) { + return null; + } + + bool forwardInCollection = (parent.RightToLeft == RightToLeft.No); + switch(navigationDirection) { + case AccessibleNavigation.FirstChild: + nextItem = parent.GetNextItem(null, ArrowDirection.Right, /*RTLAware=*/true); + break; + case AccessibleNavigation.LastChild: + nextItem = parent.GetNextItem(null, ArrowDirection.Left,/*RTLAware=*/true); + break; + case AccessibleNavigation.Previous: + case AccessibleNavigation.Left: + nextItem = parent.GetNextItem(Owner, ArrowDirection.Left, /*RTLAware=*/true); + break; + case AccessibleNavigation.Next: + case AccessibleNavigation.Right: + nextItem = parent.GetNextItem(Owner, ArrowDirection.Right, /*RTLAware=*/true); + break; + case AccessibleNavigation.Up: + nextItem = (Owner.IsOnDropDown) ? parent.GetNextItem(Owner, ArrowDirection.Up): + parent.GetNextItem(Owner, ArrowDirection.Left, /*RTLAware=*/true); + break; + case AccessibleNavigation.Down: + nextItem = (Owner.IsOnDropDown) ? parent.GetNextItem(Owner, ArrowDirection.Down): + parent.GetNextItem(Owner, ArrowDirection.Right, /*RTLAware=*/true); + break; + + } + + + } + if (nextItem != null) { + return nextItem.AccessibilityObject; + } + return null; + } + + // + public void AddState(AccessibleStates state) { + if (state == AccessibleStates.None) { + additionalState = state; + } + else { + additionalState |= state; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + if (Owner != null) { + return "ToolStripItemAccessibleObject: Owner = " + Owner.ToString(); + } + else { + return "ToolStripItemAccessibleObject: Owner = null"; + } + + } + + /// + /// + /// Gets the bounds of the accessible object, in screen coordinates. + /// + public override Rectangle Bounds { + get { + Rectangle bounds = Owner.Bounds; + if (Owner.ParentInternal != null && + Owner.ParentInternal.Visible) { + return new Rectangle(Owner.ParentInternal.PointToScreen(bounds.Location), bounds.Size); + } + return Rectangle.Empty; + } + } + + /// + /// + /// When overridden in a derived class, gets or sets the parent of an accessible object. + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (Owner.IsOnDropDown) { + // VSWhidbey 477274: return the owner item as the accessible parent. + ToolStripDropDown dropDown = Owner.GetCurrentParentDropDown(); + if (dropDown.OwnerItem != null) { + return dropDown.OwnerItem.AccessibilityObject; + } + return dropDown.AccessibilityObject; + } + return (Owner.Parent != null) ? Owner.Parent.AccessibilityObject : base.Parent; + + + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return ownerItem.RootToolStrip?.AccessibilityObject; + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + int index = GetChildFragmentIndex(); + if (index == -1) { + Debug.Fail("No item matched the index?"); + return null; + } + + int increment = direction == UnsafeNativeMethods.NavigateDirection.NextSibling ? 1 : -1; + AccessibleObject sibling = null; + + if (AccessibilityImprovements.Level3) { + index += increment; + int itemsCount = GetChildFragmentCount(); + if (index >= 0 && index < itemsCount) { + sibling = GetChildFragment(index); + } + } + else { + // Skipping contol host items, as they are provided by system + do { + index += increment; + sibling = index >= 0 && index < Parent.GetChildCount() ? Parent.GetChild(index) : null; + } while (sibling != null && sibling is Control.ControlAccessibleObject); + } + + return sibling; + } + + return base.FragmentNavigate(direction); + } + + private AccessibleObject GetChildFragment(int index) { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragment(index); + } + + // ToolStripOverflowButtonAccessibleObject is derived from ToolStripDropDownItemAccessibleObject + // and we should not process ToolStripOverflowButton as a ToolStripDropDownItem here so check for + // the ToolStripOverflowButton firstly as more specific condition. + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildFragment(index, true); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildFragment(index); + } + + return null; + } + + private int GetChildFragmentCount() { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragmentCount(); + } + + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildOverflowFragmentCount(); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildCount(); + } + + return -1; + } + + private int GetChildFragmentIndex() { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragmentIndex(this); + } + + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildFragmentIndex(this); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildFragmentIndex(this); + } + + return -1; + } + + internal override void SetFocus() { + Owner.Select(); + } + + internal void RaiseFocusChanged() { + ToolStrip root = ownerItem.RootToolStrip; + if (root != null && root.SupportsUiaProviders) { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + } + + } + + // We need a special way to defer to the ToolStripItem's image + // list for indexing purposes. + internal class ToolStripItemImageIndexer : ImageList.Indexer { + private ToolStripItem item; + + public ToolStripItemImageIndexer(ToolStripItem item) { + this.item = item; + } + + public override ImageList ImageList { + get { + if ((item != null) && (item.Owner != null)) { + return item.Owner.ImageList; + } + return null; + } + set { Debug.Assert(false, "We should never set the image list"); } + } + } + + + + + /// + /// + /// This class helps determine where the image and text should be drawn. + /// + /// + /// + internal class ToolStripItemInternalLayout { + + private ToolStripItemLayoutOptions currentLayoutOptions; + private ToolStripItem ownerItem; + private ButtonBaseAdapter.LayoutData layoutData; + private const int BORDER_WIDTH = 2; + private const int BORDER_HEIGHT = 3; + private readonly static Size INVALID_SIZE = new Size(Int32.MinValue,Int32.MinValue); + + private Size lastPreferredSize = INVALID_SIZE; + private ToolStripLayoutData parentLayoutData = null; + + + public ToolStripItemInternalLayout(ToolStripItem ownerItem) { + if (ownerItem == null) { + throw new ArgumentNullException("ownerItem"); + } + this.ownerItem = ownerItem; + } + + + // the thing that we fetch properties off of -- this can be different than ownerItem - e.g. case of split button. + protected virtual ToolStripItem Owner { + get { return ownerItem; } + } + + public virtual Rectangle ImageRectangle { + get { + Rectangle imageRect = LayoutData.imageBounds; + imageRect.Intersect(layoutData.field); + return imageRect; + } + } + + + + internal ButtonBaseAdapter.LayoutData LayoutData { + get { + EnsureLayout(); + return layoutData; + } + } + + public Size PreferredImageSize { + get { + return Owner.PreferredImageSize; + } + } + + + protected virtual ToolStrip ParentInternal { + get { + return (ownerItem != null) ? ownerItem.ParentInternal : null; + } + } + + public virtual Rectangle TextRectangle { + get { + Rectangle textRect = LayoutData.textBounds; + textRect.Intersect(layoutData.field); + return textRect; + } + } + + public virtual Rectangle ContentRectangle { + get { + return LayoutData.field; + } + } + + public virtual TextFormatFlags TextFormat { + get { + if (currentLayoutOptions != null) { + return currentLayoutOptions.gdiTextFormatFlags; + } + return CommonLayoutOptions().gdiTextFormatFlags; + } + } + + + internal static TextFormatFlags ContentAlignToTextFormat(ContentAlignment alignment, bool rightToLeft) { + TextFormatFlags textFormat = TextFormatFlags.Default; + if (rightToLeft) { + //We specifically do not want to turn on TextFormatFlags.Right. + textFormat |= TextFormatFlags.RightToLeft; + } + + // Calculate Text Positioning + textFormat |= ControlPaint.TranslateAlignmentForGDI(alignment); + textFormat |= ControlPaint.TranslateLineAlignmentForGDI(alignment); + return textFormat; + + } + + + protected virtual ToolStripItemLayoutOptions CommonLayoutOptions() { + ToolStripItemLayoutOptions layoutOptions = new ToolStripItemLayoutOptions(); + Rectangle bounds = new Rectangle(Point.Empty, ownerItem.Size); + + layoutOptions.client = bounds; + + layoutOptions.growBorderBy1PxWhenDefault = false; + + layoutOptions.borderSize = BORDER_WIDTH; + layoutOptions.paddingSize = 0; + layoutOptions.maxFocus = true; + layoutOptions.focusOddEvenFixup = false; + layoutOptions.font = ownerItem.Font; + layoutOptions.text = ((Owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) ? Owner.Text : String.Empty; + layoutOptions.imageSize = PreferredImageSize; + layoutOptions.checkSize = 0; + layoutOptions.checkPaddingSize = 0; + layoutOptions.checkAlign = ContentAlignment.TopLeft; + layoutOptions.imageAlign = Owner.ImageAlign; + layoutOptions.textAlign = Owner.TextAlign; + layoutOptions.hintTextUp = false; + layoutOptions.shadowedText = !ownerItem.Enabled; + layoutOptions.layoutRTL = RightToLeft.Yes == Owner.RightToLeft; + layoutOptions.textImageRelation = Owner.TextImageRelation; + //set textImageInset to 0 since we don't draw 3D border for ToolStripItems. + layoutOptions.textImageInset = 0; + layoutOptions.everettButtonCompat = false; + + + // Support RTL + layoutOptions.gdiTextFormatFlags = ContentAlignToTextFormat(Owner.TextAlign, Owner.RightToLeft == RightToLeft.Yes); + + + // in 2K and XP++ hide underlined &File unless ALT is pressed + layoutOptions.gdiTextFormatFlags = (Owner.ShowKeyboardCues) ? layoutOptions.gdiTextFormatFlags : layoutOptions.gdiTextFormatFlags | TextFormatFlags.HidePrefix; + + return layoutOptions; + } + + private bool EnsureLayout() { + + if (layoutData == null || parentLayoutData == null || !parentLayoutData.IsCurrent(ParentInternal)) { + PerformLayout(); + return true; + } + return false; + } + + private ButtonBaseAdapter.LayoutData GetLayoutData() { + + currentLayoutOptions = CommonLayoutOptions(); + + if (Owner.TextDirection != ToolStripTextDirection.Horizontal) { + currentLayoutOptions.verticalText = true; + } + + ButtonBaseAdapter.LayoutData data = currentLayoutOptions.Layout(); + return data; + + } + public virtual Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = Size.Empty; + EnsureLayout(); + // we would prefer not to be larger than the winbar itself. + // so we'll ask the ButtonAdapter layout guy what it thinks + // its preferred size should be - and we'll tell it to be no + // bigger than the ToolStrip itself. Note this is "Parent" not + // "Owner" because we care in this instance what we're currently displayed on. + + if (ownerItem != null) { + lastPreferredSize = currentLayoutOptions.GetPreferredSizeCore(constrainingSize); + return lastPreferredSize; + } + Debug.Fail("Why are we here without an owner?"); + return Size.Empty; + } + + + internal void PerformLayout() { + layoutData = GetLayoutData(); + ToolStrip parent = ParentInternal; + if (parent != null) { + parentLayoutData = new ToolStripLayoutData(parent); + } + else { + parentLayoutData = null; + } + } + + internal class ToolStripItemLayoutOptions : ButtonBaseAdapter.LayoutOptions { + Size cachedSize = LayoutUtils.InvalidSize; + Size cachedProposedConstraints = LayoutUtils.InvalidSize; + + // override GetTextSize to provide simple text caching. + protected override Size GetTextSize(Size proposedConstraints) { + if (cachedSize != LayoutUtils.InvalidSize + && (cachedProposedConstraints == proposedConstraints + || cachedSize.Width <= proposedConstraints.Width)) { + return cachedSize; + } + else { + cachedSize = base.GetTextSize(proposedConstraints); + cachedProposedConstraints = proposedConstraints; + } + return cachedSize; + + } + + } + private class ToolStripLayoutData { + private ToolStripLayoutStyle layoutStyle; + private bool autoSize; + private Size size; + + public ToolStripLayoutData(ToolStrip toolStrip) { + this.layoutStyle = toolStrip.LayoutStyle; + this.autoSize = toolStrip.AutoSize; + this.size = toolStrip.Size; + } + public bool IsCurrent(ToolStrip toolStrip) { + if (toolStrip == null) { + return false; + } + return (toolStrip.Size == size && toolStrip.LayoutStyle == layoutStyle && toolStrip.AutoSize == autoSize); + } + } + } + + +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItem.cs.back b/WindowsForms/Managed/System/WinForms/ToolStripItem.cs.back new file mode 100644 index 000000000..240a15254 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItem.cs.back @@ -0,0 +1,4821 @@ +//#define SELECTEDCHANGED +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms.ButtonInternal; + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + using System.Runtime.InteropServices; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Drawing.Design; + using System.Security.Permissions; + using System.Security; + using System.Configuration; + using System.Drawing.Imaging; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Runtime.Versioning; + using Collections.Generic; + + /// + /// + [System.ComponentModel.DesignTimeVisible(false)] + [Designer("System.Windows.Forms.Design.ToolStripItemDesigner, " + AssemblyRef.SystemDesign)] + [DefaultEvent("Click")] + [ToolboxItem(false)] + [DefaultProperty("Text")] + public abstract class ToolStripItem : Component, + IDropTarget, + ISupportOleDropSource, + IArrangedElement, + IKeyboardToolTip { + + + + +#if DEBUG + internal static readonly TraceSwitch MouseDebugging = new TraceSwitch("MouseDebugging", "Debug ToolStripItem mouse debugging code"); +#else + internal static readonly TraceSwitch MouseDebugging; +#endif + + private Rectangle bounds = Rectangle.Empty; + private PropertyStore propertyStore; + private ToolStripItemAlignment alignment = ToolStripItemAlignment.Left; + private ToolStrip parent = null; + private ToolStrip owner = null; + private ToolStripItemOverflow overflow = ToolStripItemOverflow.AsNeeded; + private ToolStripItemPlacement placement = ToolStripItemPlacement.None; + private ContentAlignment imageAlign = ContentAlignment.MiddleCenter; + private ContentAlignment textAlign = ContentAlignment.MiddleCenter; + private TextImageRelation textImageRelation = TextImageRelation.ImageBeforeText; + private ToolStripItemImageIndexer imageIndexer = null; + private ToolStripItemInternalLayout toolStripItemInternalLayout = null; + private BitVector32 state = new BitVector32(); + private string toolTipText = null; + private Color imageTransparentColor = Color.Empty; + private ToolStripItemImageScaling imageScaling = ToolStripItemImageScaling.SizeToFit; + private Size cachedTextSize = Size.Empty; + + private static readonly Padding defaultMargin = new Padding(0, 1, 0, 2); + private static readonly Padding defaultStatusStripMargin = new Padding(0, 2, 0, 0); + private Padding scaledDefaultMargin = defaultMargin; + private Padding scaledDefaultStatusStripMargin = defaultStatusStripMargin; + + private ToolStripItemDisplayStyle displayStyle = ToolStripItemDisplayStyle.ImageAndText; + + private static readonly ArrangedElementCollection EmptyChildCollection = new ArrangedElementCollection(); + + + /// + /// Adding a new event?? Make sure you dont need to add to ToolStripControlHost.cs + /// + internal static readonly object EventMouseDown = new object(); + internal static readonly object EventMouseEnter = new object(); + internal static readonly object EventMouseLeave = new object(); + internal static readonly object EventMouseHover = new object(); + internal static readonly object EventMouseMove = new object(); + internal static readonly object EventMouseUp = new object(); + internal static readonly object EventMouseWheel = new object(); + internal static readonly object EventClick = new object(); + internal static readonly object EventDoubleClick = new object(); + internal static readonly object EventDragDrop = new object(); + internal static readonly object EventDragEnter = new object(); + internal static readonly object EventDragLeave = new object(); + internal static readonly object EventDragOver = new object(); + internal static readonly object EventDisplayStyleChanged = new object(); + internal static readonly object EventEnabledChanged = new object(); + internal static readonly object EventInternalEnabledChanged = new object(); + internal static readonly object EventFontChanged = new object(); + internal static readonly object EventForeColorChanged = new object(); + internal static readonly object EventBackColorChanged = new object(); + internal static readonly object EventGiveFeedback = new object(); + internal static readonly object EventQueryContinueDrag = new object(); + internal static readonly object EventQueryAccessibilityHelp = new object(); + internal static readonly object EventMove = new object(); + internal static readonly object EventResize = new object(); + internal static readonly object EventLayout = new object(); + internal static readonly object EventLocationChanged = new object(); + internal static readonly object EventRightToLeft = new object(); + internal static readonly object EventVisibleChanged = new object(); + internal static readonly object EventAvailableChanged = new object(); + internal static readonly object EventOwnerChanged = new object(); + internal static readonly object EventPaint = new object(); + internal static readonly object EventText = new object(); + internal static readonly object EventSelectedChanged = new object(); + + /// + /// Adding a new event?? Make sure you dont need to add to ToolStripControlHost.cs + /// + + + // Property store keys for properties. The property store allocates most efficiently + // in groups of four, so we try to lump properties in groups of four based on how + // likely they are going to be used in a group. + + private static readonly int PropName = PropertyStore.CreateKey(); + private static readonly int PropText = PropertyStore.CreateKey(); + private static readonly int PropBackColor = PropertyStore.CreateKey(); + private static readonly int PropForeColor = PropertyStore.CreateKey(); + + private static readonly int PropImage = PropertyStore.CreateKey(); + private static readonly int PropFont = PropertyStore.CreateKey(); + private static readonly int PropRightToLeft = PropertyStore.CreateKey(); + private static readonly int PropTag = PropertyStore.CreateKey(); + + private static readonly int PropAccessibility = PropertyStore.CreateKey(); + private static readonly int PropAccessibleName = PropertyStore.CreateKey(); + private static readonly int PropAccessibleRole = PropertyStore.CreateKey(); + private static readonly int PropAccessibleHelpProvider = PropertyStore.CreateKey(); + + private static readonly int PropAccessibleDefaultActionDescription = PropertyStore.CreateKey(); + private static readonly int PropAccessibleDescription = PropertyStore.CreateKey(); + private static readonly int PropTextDirection = PropertyStore.CreateKey(); + private static readonly int PropMirroredImage = PropertyStore.CreateKey(); + + + private static readonly int PropBackgroundImage = PropertyStore.CreateKey(); + private static readonly int PropBackgroundImageLayout = PropertyStore.CreateKey(); + + private static readonly int PropMergeAction = PropertyStore.CreateKey(); + private static readonly int PropMergeIndex = PropertyStore.CreateKey(); + + + private static readonly int stateAllowDrop = BitVector32.CreateMask(); + private static readonly int stateVisible = BitVector32.CreateMask(stateAllowDrop); + private static readonly int stateEnabled = BitVector32.CreateMask(stateVisible); + private static readonly int stateMouseDownAndNoDrag = BitVector32.CreateMask(stateEnabled); + private static readonly int stateAutoSize = BitVector32.CreateMask(stateMouseDownAndNoDrag); + private static readonly int statePressed = BitVector32.CreateMask(stateAutoSize); + private static readonly int stateSelected = BitVector32.CreateMask(statePressed); + private static readonly int stateContstructing = BitVector32.CreateMask(stateSelected); + private static readonly int stateDisposed = BitVector32.CreateMask(stateContstructing); + private static readonly int stateCurrentlyAnimatingImage = BitVector32.CreateMask(stateDisposed); + private static readonly int stateDoubleClickEnabled = BitVector32.CreateMask(stateCurrentlyAnimatingImage); + private static readonly int stateAutoToolTip = BitVector32.CreateMask(stateDoubleClickEnabled); + private static readonly int stateSupportsRightClick = BitVector32.CreateMask(stateAutoToolTip); + private static readonly int stateSupportsItemClick = BitVector32.CreateMask(stateSupportsRightClick); + private static readonly int stateRightToLeftAutoMirrorImage = BitVector32.CreateMask(stateSupportsItemClick); + private static readonly int stateInvalidMirroredImage = BitVector32.CreateMask(stateRightToLeftAutoMirrorImage); + private static readonly int stateSupportsSpaceKey = BitVector32.CreateMask(stateInvalidMirroredImage); + private static readonly int stateMouseDownAndUpMustBeInSameItem = BitVector32.CreateMask(stateSupportsSpaceKey); + private static readonly int stateSupportsDisabledHotTracking = BitVector32.CreateMask(stateMouseDownAndUpMustBeInSameItem); + private static readonly int stateUseAmbientMargin = BitVector32.CreateMask(stateSupportsDisabledHotTracking); + private static readonly int stateDisposing = BitVector32.CreateMask(stateUseAmbientMargin); + + + private long lastClickTime = 0; + private int deviceDpi = DpiHelper.DeviceDpi; + internal Font defaultFont = ToolStripManager.DefaultFont; + + /// + /// + /// Constructor + /// + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin); + scaledDefaultStatusStripMargin = DpiHelper.LogicalToDeviceUnits(defaultStatusStripMargin); + } + + state[stateEnabled | stateAutoSize | stateVisible | stateContstructing | stateSupportsItemClick | stateInvalidMirroredImage | stateMouseDownAndUpMustBeInSameItem | stateUseAmbientMargin] = true; + state[stateAllowDrop | stateMouseDownAndNoDrag | stateSupportsRightClick | statePressed | stateSelected | stateDisposed | stateDoubleClickEnabled | stateRightToLeftAutoMirrorImage | stateSupportsSpaceKey] = false; + SetAmbientMargin(); + this.Size = DefaultSize; + this.DisplayStyle = DefaultDisplayStyle; + CommonProperties.SetAutoSize(this,true); + state[stateContstructing] = false; + this.AutoToolTip = DefaultAutoToolTip; + + } + + + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem(string text, Image image, EventHandler onClick) : this(text, image, onClick, null) { + } + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + protected ToolStripItem(string text, Image image, EventHandler onClick, string name) : this() { + this.Text = text; + this.Image = image; + if (onClick != null) { + Click += onClick; + } + this.Name = name; + } + + /// + /// + /// The Accessibility Object for this Control + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolStripItemAccessibilityObjectDescr) + ] + public AccessibleObject AccessibilityObject { + get { + AccessibleObject accessibleObject = (AccessibleObject)Properties.GetObject(PropAccessibility); + if (accessibleObject == null) { + accessibleObject = CreateAccessibilityInstance(); + Properties.SetObject(PropAccessibility, accessibleObject); + } + + Debug.Assert(accessibleObject != null, "Failed to create accessibility object"); + return accessibleObject; + } + } + + /// + /// + /// The default action description of the control + /// + [ + SRCategory(SR.CatAccessibility), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolStripItemAccessibleDefaultActionDescr) + ] + public string AccessibleDefaultActionDescription { + get { + return (string)Properties.GetObject(PropAccessibleDefaultActionDescription); + } + set { + Properties.SetObject(PropAccessibleDefaultActionDescription, value); + OnAccessibleDefaultActionDescriptionChanged(EventArgs.Empty); + } + } + /// + /// + /// The accessible description of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ToolStripItemAccessibleDescriptionDescr) + ] + public string AccessibleDescription { + get { + return (string)Properties.GetObject(PropAccessibleDescription); + } + set { + Properties.SetObject(PropAccessibleDescription, value); + OnAccessibleDescriptionChanged(EventArgs.Empty); + } + } + + /// + /// + /// The accessible name of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(null), + Localizable(true), + SRDescription(SR.ToolStripItemAccessibleNameDescr) + ] + public string AccessibleName { + get { + return (string)Properties.GetObject(PropAccessibleName); + } + + set { + Properties.SetObject(PropAccessibleName, value); + OnAccessibleNameChanged(EventArgs.Empty); + } + } + + /// + /// + /// The accessible role of the control + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AccessibleRole.Default), + SRDescription(SR.ToolStripItemAccessibleRoleDescr) + ] + public AccessibleRole AccessibleRole { + + get { + bool found; + int role = Properties.GetInteger(PropAccessibleRole, out found); + if (found) { + return (AccessibleRole)role; + } + else { + return AccessibleRole.Default; + } + } + + set { + //valid values are 0xffffffff to 0x40 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AccessibleRole.Default, (int)AccessibleRole.OutlineButton)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(AccessibleRole)); + } + Properties.SetInteger(PropAccessibleRole, (int)value); + OnAccessibleRoleChanged(EventArgs.Empty); + } + + + } + + + /// + /// + /// Determines if the item aligns towards the beginning or end of the winbar. + /// + [ + DefaultValue(ToolStripItemAlignment.Left), + SRCategory(SR.CatLayout), + SRDescription(SR.ToolStripItemAlignmentDescr) + ] + public ToolStripItemAlignment Alignment { + get { + return alignment; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemAlignment.Left, (int)ToolStripItemAlignment.Right)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemAlignment)); + } + if (alignment != value) { + alignment = value; + + if ((this.ParentInternal != null) && this.ParentInternal.IsHandleCreated) { + this.ParentInternal.PerformLayout(); + } + } + } + } + + + /// + /// + /// Determines if this item can be dragged. + /// This is EXACTLY like Control.AllowDrop - setting this to true WILL call + /// the droptarget handlers. The ToolStripDropTargetManager is the one that + /// handles the routing of DropTarget events to the ToolStripItem's IDropTarget + /// methods. + /// + [ + SRCategory(SR.CatDragDrop), + DefaultValue(false), + SRDescription(SR.ToolStripItemAllowDropDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public virtual bool AllowDrop { + get { + return state[stateAllowDrop]; + } + set { + if (value != state[stateAllowDrop]) { + EnsureParentDropTargetRegistered(); + state[stateAllowDrop] = value; + } + } + } + + /// + /// + /// Determines whether we set the ToolStripItem to its preferred size + /// + [DefaultValue(true)] + [SRCategory(SR.CatBehavior)] + [RefreshProperties(RefreshProperties.All)] + [Localizable(true)] + [SRDescription(SR.ToolStripItemAutoSizeDescr)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public bool AutoSize { + get { + return state[stateAutoSize]; + } + set { + if (state[stateAutoSize] != value) { + state[stateAutoSize] = value; + CommonProperties.SetAutoSize(this,value); + InvalidateItemLayout(PropertyNames.AutoSize); + } + } + } + + /// + /// + /// !!!!This property ONLY works when toolStrip.ShowItemToolTips = true!!!! + /// if AutoToolTip is set to true we use the Text, if false, we use ToolTipText. + /// + [DefaultValue(false)] + [SRDescription(SR.ToolStripItemAutoToolTipDescr)] + [SRCategory(SR.CatBehavior)] + public bool AutoToolTip { + get { + return state[stateAutoToolTip]; + } + set { + state[stateAutoToolTip] = value; + } + } + + /// + /// as opposed to Visible, which returns whether or not the item and its parent are Visible + /// Available returns whether or not the item will be shown. Setting Available sets Visible and Vice/Versa + /// + [ + Browsable(false), + SRDescription(SR.ToolStripItemAvailableDescr), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public bool Available{ + get { + // MainMenu compat: + // the only real diff is the getter - this returns what the item really thinks, + // as opposed to whether or not the parent is also Visible. Visible behavior is the same + // so that it matches Control behavior (we dont have to do special things for control hosts, etc etc). + return state[stateVisible]; + } + set { + SetVisibleCore(value); + } + } + + + [ + Browsable(false), + SRCategory(SR.CatPropertyChanged), + SRDescription(SR.ToolStripItemOnAvailableChangedDescr) + ] + public event EventHandler AvailableChanged { + add { + Events.AddHandler(EventAvailableChanged, value); + } + remove { + Events.RemoveHandler(EventAvailableChanged, value); + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr), + DefaultValue(null) + ] + public virtual Image BackgroundImage { + get { + return Properties.GetObject(PropBackgroundImage) as Image; + + } + set { + if (BackgroundImage != value) { + Properties.SetObject(PropBackgroundImage, value); + Invalidate(); + } + } + } + + // Every ToolStripItem needs to cache its last/current Parent's DeviceDpi + // for PerMonitorV2 scaling purposes. + internal virtual int DeviceDpi { + get { + return deviceDpi; + } + set { + deviceDpi = value; + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ImageLayout.Tile), + Localizable(true), + SRDescription(SR.ControlBackgroundImageLayoutDescr) + ] + public virtual ImageLayout BackgroundImageLayout { + get { + bool found = Properties.ContainsObject(PropBackgroundImageLayout); + if (!found) { + return ImageLayout.Tile; + } + else { + return ((ImageLayout)Properties.GetObject(PropBackgroundImageLayout)); + } + } + set { + if (BackgroundImageLayout != value) { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ImageLayout.None, (int)ImageLayout.Zoom)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ImageLayout)); + } + Properties.SetObject(PropBackgroundImageLayout, value); + Invalidate(); + } + } + } + + /// + /// + /// The BackColor of the item + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemBackColorDescr) + ] + public virtual Color BackColor { + get { + Color c = RawBackColor; // inheritedProperties.BackColor + if (!c.IsEmpty) + return c; + + Control p = ParentInternal; + if (p != null) { + return p.BackColor; + } + return Control.DefaultBackColor; + } + set { + Color c = BackColor; + if (!value.IsEmpty || Properties.ContainsObject(PropBackColor)) { + Properties.SetColor(PropBackColor, value); + } + + if (!c.Equals(BackColor)) { + OnBackColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnBackColorChangedDescr)] + public event EventHandler BackColorChanged { + add { + Events.AddHandler(EventBackColorChanged, value); + } + remove { + Events.RemoveHandler(EventBackColorChanged, value); + } + } + + /// + /// + /// The bounds of the item + /// + [Browsable(false)] + public virtual Rectangle Bounds { + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + get { + return bounds; + } + } + + // Zero-based rectangle, same concept as ClientRect + internal Rectangle ClientBounds { + get { + Rectangle client = bounds; + client.Location = Point.Empty; + return client; + } + } + /// + [Browsable(false)] + public Rectangle ContentRectangle { + get { + Rectangle content = LayoutUtils.InflateRect(InternalLayout.ContentRectangle, this.Padding); + content.Size = LayoutUtils.UnionSizes(Size.Empty, content.Size); + return content; + } + } + + /// + /// + /// Determines whether or not the item can be selected. + /// + [Browsable(false)] + public virtual bool CanSelect { + get { + return true; + } + } + + // usually the same as can select, but things like the control box in an MDI window are exceptions + internal virtual bool CanKeyboardSelect { + get { + return CanSelect; + } + } + + /// + /// + /// Occurs when the control is clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripItemOnClickDescr) + ] + public event EventHandler Click { + add { + Events.AddHandler(EventClick, value); + } + remove { + Events.RemoveHandler(EventClick, value); + } + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + DefaultValue(CommonProperties.DefaultAnchor) + ] + public AnchorStyles Anchor { + get { + // since we dont support DefaultLayout go directly against the CommonProperties + return CommonProperties.xGetAnchor(this); + } + set { + // flags enum - dont check for validity.... + + if(value != Anchor) { + // since we dont support DefaultLayout go directly against the CommonProperties + CommonProperties.xSetAnchor(this, value); + if (ParentInternal != null) { + LayoutTransaction.DoLayout(this, ParentInternal, PropertyNames.Anchor); + } + } + } + } + + + /// This does not show up in the property grid because it only applies to flow and table layouts + [ + Browsable(false), + DefaultValue(CommonProperties.DefaultDock) + ] + public DockStyle Dock { + get { + + // since we dont support DefaultLayout go directly against the CommonProperties + return CommonProperties.xGetDock(this); + } + set { + //valid values are 0x0 to 0x5 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){ + throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle)); + } + if(value != Dock) { + // since we dont support DefaultLayout go directly against the CommonProperties + CommonProperties.xSetDock(this, value); + if (ParentInternal != null) { + LayoutTransaction.DoLayout(this, ParentInternal, PropertyNames.Dock); + } + } + } + } + + /// default setting of auto tooltip when this object is created + protected virtual bool DefaultAutoToolTip { + get { + return false; + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal virtual Padding DefaultMargin { + get { + if (this.Owner != null && this.Owner is StatusStrip) { + return scaledDefaultStatusStripMargin; + } + else { + return scaledDefaultMargin; + } + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Padding DefaultPadding { + get { + return Padding.Empty; + } + } + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected virtual Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(23, 23), DeviceDpi) : + new Size(23, 23); + } + } + + protected virtual ToolStripItemDisplayStyle DefaultDisplayStyle { + get { + return ToolStripItemDisplayStyle.ImageAndText; + } + } + + /// + /// + /// specifies the default behavior of these items on ToolStripDropDowns when clicked. + /// + internal protected virtual bool DismissWhenClicked { + get { + return true; + } + } + + /// + /// + /// DisplayStyle specifies whether the image and text are rendered. This is not on the base + /// item class because different derived things will have different enumeration needs. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemDisplayStyleDescr) + ] + public virtual ToolStripItemDisplayStyle DisplayStyle { + get { + return displayStyle; + } + set { + if (displayStyle != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemDisplayStyle.None, (int)ToolStripItemDisplayStyle.ImageAndText)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemDisplayStyle)); + } + displayStyle = value; + if (!state[stateContstructing]) { + InvalidateItemLayout(PropertyNames.DisplayStyle); + OnDisplayStyleChanged(new EventArgs()); + } + } + + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event EventHandler DisplayStyleChanged { + add { + Events.AddHandler(EventDisplayStyleChanged, value); + } + remove { + Events.RemoveHandler(EventDisplayStyleChanged, value); + } + } + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + private RightToLeft DefaultRightToLeft { + get { + return RightToLeft.Inherit; + } + } + /// + /// + /// Occurs when the control is double clicked. + /// + [SRCategory(SR.CatAction), SRDescription(SR.ControlOnDoubleClickDescr)] + public event EventHandler DoubleClick { + add { + Events.AddHandler(EventDoubleClick, value); + } + remove { + Events.RemoveHandler(EventDoubleClick, value); + } + } + + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripItemDoubleClickedEnabledDescr) + ] + public bool DoubleClickEnabled { + get { + return state[stateDoubleClickEnabled]; + } + set { + state[stateDoubleClickEnabled] = value; + } + } + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragDropDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragDrop { + add { + Events.AddHandler(EventDragDrop, value); + } + remove { + Events.RemoveHandler(EventDragDrop, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragEnterDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragEnter { + add { + Events.AddHandler(EventDragEnter, value); + } + remove { + Events.RemoveHandler(EventDragEnter, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragOverDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event DragEventHandler DragOver { + add { + Events.AddHandler(EventDragOver, value); + } + remove { + Events.RemoveHandler(EventDragOver, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnDragLeaveDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event EventHandler DragLeave { + add { + Events.AddHandler(EventDragLeave, value); + } + remove { + Events.RemoveHandler(EventDragLeave, value); + } + } + + /// + /// ToolStripItem.DropSource + /// + /// This represents what we're actually going to drag. If the parent has set AllowItemReorder to true, + /// then the item should call back on the private OnQueryContinueDrag/OnGiveFeedback that is implemented + /// in the parent winbar. + /// + /// Else if the parent does not support reordering of items (Parent.AllowItemReorder = false) - + /// then call back on the ToolStripItem's OnQueryContinueDrag/OnGiveFeedback methods. + /// + private DropSource DropSource { + get { + if ((ParentInternal != null) && (ParentInternal.AllowItemReorder) && (ParentInternal.ItemReorderDropSource != null)) { + return new DropSource(ParentInternal.ItemReorderDropSource); + } + return new DropSource(this); + } + } + + /// + /// + /// Occurs when the control is clicked. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ToolStripItemEnabledDescr), + DefaultValue(true) + ] + public virtual bool Enabled { + get { + bool parentEnabled = true; + + if (this.Owner != null) { + parentEnabled = this.Owner.Enabled; + } + + return state[stateEnabled] && parentEnabled; + } + set { + // flip disabled bit. + if (state[stateEnabled] != value) { + state[stateEnabled] = value; + if (!state[stateEnabled]) { + bool wasSelected = state[stateSelected]; + // clear all the other states. + state[stateSelected | statePressed] = false; + + if (wasSelected && !AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + + } + OnEnabledChanged(EventArgs.Empty); + Invalidate(); + } + OnInternalEnabledChanged(EventArgs.Empty); + } + + } + + + + /// + /// + /// [To be supplied.] + /// + [ + SRDescription(SR.ToolStripItemEnabledChangedDescr) + ] + public event EventHandler EnabledChanged { + add { + Events.AddHandler(EventEnabledChanged, value); + } + remove { + Events.RemoveHandler(EventEnabledChanged, value); + } + } + + internal event EventHandler InternalEnabledChanged { + add { + Events.AddHandler(EventInternalEnabledChanged, value); + } + remove { + Events.RemoveHandler(EventInternalEnabledChanged, value); + } + } + + private void EnsureParentDropTargetRegistered() { + if (ParentInternal != null) { + IntSecurity.ClipboardRead.Demand(); + ParentInternal.DropTargetManager.EnsureRegistered(this); + } + } + + /// + /// + /// Retrieves the current font for this item. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemForeColorDescr) + ] + public virtual Color ForeColor { + get { + Color foreColor = Properties.GetColor(PropForeColor); + if (!foreColor.IsEmpty){ + return foreColor; + } + + Control p = ParentInternal; + if (p != null) { + return p.ForeColor; + } + return Control.DefaultForeColor; + } + + set { + Color c = ForeColor; + if (!value.IsEmpty || Properties.ContainsObject(PropForeColor)) { + Properties.SetColor(PropForeColor, value); + } + if (!c.Equals(ForeColor)) { + OnForeColorChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnForeColorChangedDescr)] + public event EventHandler ForeColorChanged { + add { + Events.AddHandler(EventForeColorChanged, value); + } + remove { + Events.RemoveHandler(EventForeColorChanged, value); + } + } + + /// + /// + /// Retrieves the current font for this control. This will be the font used + /// by default for painting and text in the control. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemFontDescr) + ] + public virtual Font Font { + get { + Font font = (Font)Properties.GetObject(PropFont); + if (font != null) { + return font; + } + + Font f = GetOwnerFont(); + if (f != null) { + return f; + } + + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + defaultFont : + ToolStripManager.DefaultFont; + } + set { + + Font local = (Font)Properties.GetObject(PropFont); + if ((local != value)){ + Properties.SetObject(PropFont, value); + OnFontChanged(EventArgs.Empty); + } + } + } + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnGiveFeedbackDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event GiveFeedbackEventHandler GiveFeedback { + add { + Events.AddHandler(EventGiveFeedback, value); + } + remove { + Events.RemoveHandler(EventGiveFeedback, value); + } + } + + /// + /// + /// The height of this control + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int Height { + get { + return Bounds.Height; + } + set{ + Rectangle currentBounds = this.Bounds; + SetBounds(currentBounds.X, currentBounds.Y, currentBounds.Width, value); + } + } + + /// + /// + /// ToolStripItems do not have children. For perf reasons always return a static empty collection. + /// Consider creating readonly collection. + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + return ToolStripItem.EmptyChildCollection; + } + } + /// + /// + /// Should not be exposed as this returns an unexposed type. + /// + /// + IArrangedElement IArrangedElement.Container { + get { + if (this.ParentInternal == null) { + return this.Owner; + } + return this.ParentInternal; + } + } + /// + /// + /// + + + Rectangle IArrangedElement.DisplayRectangle { + get { + return this.Bounds; + } + } + + /// + /// + /// + + + bool IArrangedElement.ParticipatesInLayout { + get { + // this can be different than "Visible" property as "Visible" takes into account whether or not you + // are parented and your parent is visible. + return state[stateVisible]; + } + } + + + /// + /// + PropertyStore IArrangedElement.Properties { + get { + return this.Properties; + } + } + + + // Sets the bounds for an element. + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + // in this case the parent is telling us to refresh our bounds - dont + // call PerformLayout + SetBounds(bounds); + } + + /// + /// + /// + + + void IArrangedElement.PerformLayout(IArrangedElement container, string propertyName) { + return; + } + + /// + /// + /// + /// Gets or sets the alignment of the image on the label control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageAlignDescr) + ] + public ContentAlignment ImageAlign { + get { + return imageAlign; + } + set { + + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (imageAlign != value) { + imageAlign = value; + InvalidateItemLayout(PropertyNames.ImageAlign); + } + } + } + + /// + /// + /// + /// Gets or sets the image that is displayed on a . + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageDescr) + ] + public virtual Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + Image image = (Image)Properties.GetObject(PropImage); + + if (image == null && (Owner != null) && (Owner.ImageList != null) && ImageIndexer.ActualIndex >= 0) { + if (ImageIndexer.ActualIndex < Owner.ImageList.Images.Count) { + // CACHE (by design). If we fetched out of the image list every time it would dramatically hit perf. + image = Owner.ImageList.Images[ImageIndexer.ActualIndex]; + state[stateInvalidMirroredImage] = true; + Properties.SetObject(PropImage, image); + return image; + } + } + else { + return image; + } + return null; + } + set { + if (Image != value) { + StopAnimate(); + Bitmap bmp = value as Bitmap; + if (bmp != null && ImageTransparentColor != Color.Empty) { + if (bmp.RawFormat.Guid != ImageFormat.Icon.Guid && !ImageAnimator.CanAnimate(bmp)) { + bmp.MakeTransparent(ImageTransparentColor); + } + value = bmp; + } + if (value != null) { + ImageIndex = -1; + } + Properties.SetObject(PropImage, value); + state[stateInvalidMirroredImage] = true; + Animate(); + InvalidateItemLayout(PropertyNames.Image); + } + } + } + + + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemImageTransparentColorDescr) + ] + public Color ImageTransparentColor { + get { + return imageTransparentColor; + } + set { + if (imageTransparentColor != value) { + imageTransparentColor = value; + Bitmap currentImage = Image as Bitmap; + if (currentImage != null && value != Color.Empty) { + if (currentImage.RawFormat.Guid != ImageFormat.Icon.Guid && !ImageAnimator.CanAnimate(currentImage)) { + currentImage.MakeTransparent(imageTransparentColor); + } + } + Invalidate(); + } + + } + } + + /// + /// + /// Returns the ToolStripItem's currently set image index + /// Here for compat only - this is NOT to be visible at DT. + /// + [ + SRDescription(SR.ToolStripItemImageIndexDescr), + Localizable(true), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + Editor("System.Windows.Forms.Design.ToolStripImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(false), + RelatedImageList("Owner.ImageList") + ] + public int ImageIndex { + get { + if ((Owner != null) && ImageIndexer.Index != -1 && Owner.ImageList != null && ImageIndexer.Index >= Owner.ImageList.Images.Count) { + return Owner.ImageList.Images.Count - 1; + } + return this.ImageIndexer.Index; + } + set { + if (value < -1) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture))); + } + + + ImageIndexer.Index = value; + state[stateInvalidMirroredImage] = true; + // Set the Image Property to null + Properties.SetObject(PropImage, null); + + + InvalidateItemLayout(PropertyNames.ImageIndex); + } + } + + internal ToolStripItemImageIndexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ToolStripItemImageIndexer(this); + } + return imageIndexer; + } + } + + /// + /// + /// Returns the ToolStripItem's currently set image index + /// Here for compat only - this is NOT to be visible at DT. + /// + [ + SRDescription(SR.ToolStripItemImageKeyDescr), + SRCategory(SR.CatBehavior), + Localizable(true), + TypeConverterAttribute(typeof(ImageKeyConverter)), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.ToolStripImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(false), + RelatedImageList("Owner.ImageList") + ] + public string ImageKey { + get { + return this.ImageIndexer.Key; + } + set { + ImageIndexer.Key = value; + state[stateInvalidMirroredImage] = true; + Properties.SetObject(PropImage, null); + + InvalidateItemLayout(PropertyNames.ImageKey); + + } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(ToolStripItemImageScaling.SizeToFit), + Localizable(true), + SRDescription(SR.ToolStripItemImageScalingDescr) + ] + public ToolStripItemImageScaling ImageScaling { + get { + return imageScaling; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemImageScaling.None, (int)ToolStripItemImageScaling.SizeToFit)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripItemImageScaling)); + } + if (imageScaling != value) { + imageScaling = value; + + InvalidateItemLayout(PropertyNames.ImageScaling); + } + } + } + + /// + /// + /// This object helps determine where the image and text should be drawn. + /// + /// + /// + internal ToolStripItemInternalLayout InternalLayout { + get { + if (toolStripItemInternalLayout == null) { + toolStripItemInternalLayout = CreateInternalLayout(); + } + return toolStripItemInternalLayout; + } + } + + internal bool IsForeColorSet { + get { + Color color = Properties.GetColor(PropForeColor); + if (!color.IsEmpty) { + return true; + } + else { + Control parent = ParentInternal; + if (parent != null) { + return parent.ShouldSerializeForeColor(); + } + } + return false; + } + } + + /// + /// + /// This is used by ToolStrip to pass on the mouseMessages for ActiveDropDown. + /// + /// + /// + internal bool IsInDesignMode { + get { + return DesignMode; + } + } + + + /// + [Browsable(false)] + public bool IsDisposed { + get { + return state[stateDisposed]; + } + + } + + /// + [Browsable(false)] + public bool IsOnDropDown { + get { + + if (ParentInternal != null) { + return ParentInternal.IsDropDown; + } + else if (Owner != null && Owner.IsDropDown) { + return true; + } + + return false; + } + } + + /// returns whether the item placement is set to overflow. + [Browsable(false)] + public bool IsOnOverflow { + get { + return (this.Placement == ToolStripItemPlacement.Overflow); + } + } + + + /// + /// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing). + /// + internal virtual bool IsMnemonicsListenerAxSourced + { + get{ + return true; + } + } + + /// + /// + /// Occurs when the location of the ToolStripItem has been updated -- usually by layout by its + /// owner of ToolStrips + /// + [SRCategory(SR.CatLayout), SRDescription(SR.ToolStripItemOnLocationChangedDescr)] + public event EventHandler LocationChanged { + add { + Events.AddHandler(EventLocationChanged, value); + } + remove { + Events.RemoveHandler(EventLocationChanged, value); + } + } + + /// + /// + /// Specifies the external spacing between this item and any other item or the ToolStrip. + /// + [ + SRDescription(SR.ToolStripItemMarginDescr), + SRCategory(SR.CatLayout) + ] + public Padding Margin { + get { return CommonProperties.GetMargin(this); } + set { + if (Margin != value) { + state[stateUseAmbientMargin] = false; + CommonProperties.SetMargin(this, value); + } + } + } + + + /// + /// + /// Specifies the merge action when merging two ToolStrip. + /// + [ + SRDescription(SR.ToolStripMergeActionDescr), + DefaultValue(MergeAction.Append), + SRCategory(SR.CatLayout) + ] + public MergeAction MergeAction { + get { + bool found; + int action = Properties.GetInteger(PropMergeAction, out found); + if (found) { + return (MergeAction)action; + } + else { + // default value + return MergeAction.Append; + } + } + + set { + //valid values are 0x0 to 0x4 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)MergeAction.Append, (int)MergeAction.MatchOnly)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(MergeAction)); + } + Properties.SetInteger(PropMergeAction, (int)value); + } + } + + /// + /// + /// Specifies the merge action when merging two ToolStrip. + /// + [ + SRDescription(SR.ToolStripMergeIndexDescr), + DefaultValue(-1), + SRCategory(SR.CatLayout) + ] + public int MergeIndex { + get { + bool found; + int index = Properties.GetInteger(PropMergeIndex, out found); + if (found) { + return index; + } + else { + // default value + return -1; + } + } + + set { + Properties.SetInteger(PropMergeIndex, value); + } + } + + // required for menus - see VSWhidbey 458967. + internal bool MouseDownAndUpMustBeInSameItem { + get { return state[stateMouseDownAndUpMustBeInSameItem]; } + set { state[stateMouseDownAndUpMustBeInSameItem] = value; } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is + /// pressed. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseDownDescr) + ] + public event MouseEventHandler MouseDown { + add { + Events.AddHandler(EventMouseDown, value); + } + remove { + Events.RemoveHandler(EventMouseDown, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer enters the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseEnterDescr) + ] + public event EventHandler MouseEnter { + add { + Events.AddHandler(EventMouseEnter, value); + } + remove { + Events.RemoveHandler(EventMouseEnter, value); + } + } + + /// + /// + /// Occurs when the mouse pointer leaves the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseLeaveDescr) + ] + public event EventHandler MouseLeave { + add { + Events.AddHandler(EventMouseLeave, value); + } + remove { + Events.RemoveHandler(EventMouseLeave, value); + } + } + + + /// + /// + /// Occurs when the mouse pointer hovers over the contro. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseHoverDescr) + ] + public event EventHandler MouseHover { + add { + Events.AddHandler(EventMouseHover, value); + } + remove { + Events.RemoveHandler(EventMouseHover, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is moved over the control. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseMoveDescr) + ] + public event MouseEventHandler MouseMove { + add { + Events.AddHandler(EventMouseMove, value); + } + remove { + Events.RemoveHandler(EventMouseMove, value); + } + } + + /// + /// + /// Occurs when the mouse pointer is over the control and a mouse button is released. + /// + [ + SRCategory(SR.CatMouse), + SRDescription(SR.ToolStripItemOnMouseUpDescr) + ] + public event MouseEventHandler MouseUp { + add { + Events.AddHandler(EventMouseUp, value); + } + remove { + Events.RemoveHandler(EventMouseUp, value); + } + } + + /// + /// + /// Name of this control. The designer will set this to the same + /// as the programatic Id "(name)" of the control. The name can be + /// used as a key into the ControlCollection. + /// + [ + Browsable(false), + DefaultValue(null) + ] + public string Name { + get { + return WindowsFormsUtils.GetComponentName(this, (string)Properties.GetObject(ToolStripItem.PropName)); + } + set { + if (DesignMode) //InDesignMode the Extender property will set it to the right value. + { + return; + } + Properties.SetObject(ToolStripItem.PropName, value); + } + } + + /// + /// + /// The owner of this ToolStripItem. The owner is essentially a backpointer to + /// the ToolStrip who contains this item in it's item collection. Handy for getting + /// to things such as the ImageList, which would be defined on the ToolStrip. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ToolStrip Owner { + get { + return this.owner; + } + set { + if (owner != value) { + if (owner != null) { + owner.Items.Remove(this); + } + if (value != null) { + value.Items.Add(this); + } + } + } + } + + + /// returns the "parent" item on the preceeding menu which has spawned this item. + /// e.g. File->Open the OwnerItem of Open is File. + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public ToolStripItem OwnerItem { + get { + ToolStripDropDown currentParent = null; + + if (ParentInternal != null) { + currentParent = ParentInternal as ToolStripDropDown; + } + else if (Owner != null) { + // parent may be null, but we may be "owned" by a collection. + currentParent = Owner as ToolStripDropDown; + } + + if (currentParent != null) { + return currentParent.OwnerItem; + } + return null; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripItemOwnerChangedDescr) + ] + public event EventHandler OwnerChanged { + add { + Events.AddHandler(EventOwnerChanged, value); + } + remove { + Events.RemoveHandler(EventOwnerChanged, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemOnPaintDescr) + ] + public event PaintEventHandler Paint { + add { + Events.AddHandler(EventPaint, value); + } + remove { + Events.RemoveHandler(EventPaint, value); + } + } + /// + /// + /// The parent of this ToolStripItem. This can be distinct from the owner because + /// the item can fall onto another window (overflow). In this case the overflow + /// would be the parent but the original winbar would be the Owner. The "parent" + /// winbar will be firing things like paint events - where as the "owner" winbar + /// will be containing shared data like image lists. Typically the only one who should + /// set the parent property is the layout manager on the ToolStrip. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + internal protected ToolStrip Parent { + get { + // we decided that there is no "parent" protection for toolstripitems. + // since toolstrip and toolstripitem are tightly coupled. + return ParentInternal; + } + set { + ParentInternal = value; + } + } + + + /// + /// + /// Specifies whether or not the item is glued to the winbar or overflow or + /// can float between the two. + /// + [ + DefaultValue(ToolStripItemOverflow.AsNeeded), + SRDescription(SR.ToolStripItemOverflowDescr), + SRCategory(SR.CatLayout) + ] + public ToolStripItemOverflow Overflow { + get { + return overflow; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripItemOverflow.Never, (int)ToolStripItemOverflow.AsNeeded)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripGripStyle)); + } + if (overflow != value) { + overflow = value; + if (Owner != null) { + LayoutTransaction.DoLayout(Owner, this.Owner, "Overflow"); + } + } + } + } + + /// + /// + /// Specifies the internal spacing between the contents and the edges of the item + /// + [ + SRDescription(SR.ToolStripItemPaddingDescr), + SRCategory(SR.CatLayout) + ] + public virtual Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + if (Padding != value) { + CommonProperties.SetPadding(this, value); + InvalidateItemLayout(PropertyNames.Padding); + } + } + } + + /// + /// This is explicitly a winbar, because only winbars know how to manage winbaritems + /// + internal ToolStrip ParentInternal { + get { + return parent; + } + set { + if (parent != value) { + ToolStrip oldParent = parent; + parent = value; + OnParentChanged(oldParent, value); + } + } + } + + /// + /// + /// Where the item actually ended up. + /// + [Browsable(false)] + public ToolStripItemPlacement Placement { + get { + return placement; + } + } + + internal Size PreferredImageSize { + get { + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.Image) { + return Size.Empty; + } + + Image image = (Image)Properties.GetObject(PropImage); + bool usingImageList = ((Owner != null) && (Owner.ImageList != null) && (ImageIndexer.ActualIndex >= 0)); + + if (ImageScaling == ToolStripItemImageScaling.SizeToFit) { + ToolStrip ownerToolStrip = Owner; + if (ownerToolStrip != null && (image != null || usingImageList)) { + return ownerToolStrip.ImageScalingSize; + } + } + + + Size imageSize = Size.Empty; + if (usingImageList) { + imageSize = Owner.ImageList.ImageSize; + } + else { + imageSize = (image == null) ? Size.Empty : image.Size; + } + + return imageSize; + + } + + } + /// + /// Retrieves our internal property storage object. If you have a property + /// whose value is not always set, you should store it in here to save + /// space. + /// + internal PropertyStore Properties { + get { + if (propertyStore == null) { + propertyStore = new PropertyStore(); + } + return propertyStore; + } + } + + + /// + /// + /// Returns true if the state of the item is pushed + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool Pressed { + get { + return CanSelect && state[statePressed]; + + } + } + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatDragDrop), + SRDescription(SR.ToolStripItemOnQueryContinueDragDescr), + EditorBrowsable(EditorBrowsableState.Advanced), + Browsable(false) + ] + public event QueryContinueDragEventHandler QueryContinueDrag { + add { + Events.AddHandler(EventQueryContinueDrag, value); + } + remove { + Events.RemoveHandler(EventQueryContinueDrag, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripItemOnQueryAccessibilityHelpDescr)] + public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + Events.AddHandler(EventQueryAccessibilityHelp, value); + } + remove { + Events.RemoveHandler(EventQueryAccessibilityHelp, value); + } + } + + + // Returns the value of the backColor field -- no asking the parent with its color is, etc. + internal Color RawBackColor { + get { + return Properties.GetColor(PropBackColor); + } + } + + internal ToolStripRenderer Renderer { + get { + if (Owner != null) { + return Owner.Renderer; + } + return (ParentInternal != null) ? ParentInternal.Renderer : null; + + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemRightToLeftDescr) + ] + public virtual RightToLeft RightToLeft { + get { + bool found; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + + if (((RightToLeft)rightToLeft) == RightToLeft.Inherit) { + if (Owner != null) { + rightToLeft = (int)Owner.RightToLeft; + } + else if (ParentInternal != null) { + // case for Overflow & Grip + rightToLeft = (int)ParentInternal.RightToLeft; + } + else { + rightToLeft = (int)DefaultRightToLeft; + } + } + return (RightToLeft)rightToLeft; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)RightToLeft.No, (int)RightToLeft.Inherit)) + { + throw new InvalidEnumArgumentException("RightToLeft", (int)value, typeof(RightToLeft)); + } + + RightToLeft oldValue = RightToLeft; + + if (Properties.ContainsInteger(PropRightToLeft) || value != RightToLeft.Inherit) { + Properties.SetInteger(PropRightToLeft, (int)value); + } + + if (oldValue != RightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + + + /// + /// Mirrors the image when RTL.Yes. + /// Note we do not change what is returned back from the Image property as this would cause problems with serialization. + /// Instead we only change what is painted - there's an internal MirroredImage property which fills in as + /// e.Image in the ToolStripItemImageRenderEventArgs if the item is RTL.Yes and AutoMirrorImage is turned on. + /// + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemRightToLeftAutoMirrorImageDescr) + ] + public bool RightToLeftAutoMirrorImage { + get { + return state[stateRightToLeftAutoMirrorImage]; + } + set { + if (state[stateRightToLeftAutoMirrorImage] != value) { + state[stateRightToLeftAutoMirrorImage] = value; + Invalidate(); + } + } + } + + internal Image MirroredImage { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if (state[stateInvalidMirroredImage]) { + Image image = Image; + if (image != null) { + Image mirroredImage = image.Clone() as Image; + mirroredImage.RotateFlip(RotateFlipType.RotateNoneFlipX); + + Properties.SetObject(PropMirroredImage, mirroredImage); + state[stateInvalidMirroredImage] = false; + return mirroredImage; + } + else { + return null; + } + } + return Properties.GetObject(PropMirroredImage) as Image; + } + } + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnRightToLeftChangedDescr)] + public event EventHandler RightToLeftChanged { + add { + Events.AddHandler(EventRightToLeft, value); + } + remove { + Events.RemoveHandler(EventRightToLeft, value); + } + } + /// + /// + /// if the item is selected we return true. + /// + /// FAQ: Why dont we have a Hot or MouseIsOver property? + /// After going through the scenarios, we've decided NOT to add a separate MouseIsOver or Hot flag to ToolStripItem. The thing to use is 'Selected'. + /// Why? While the selected thing can be different than the moused over item, the selected item is ALWAYS the one you want to paint differently + /// + /// Scenario 1: Keyboard select an item then select a different item with the mouse. + /// - Do Alt+F to expand your File menu, keyboard down several items. + /// - Mouse over a different item + /// - Notice how two things are never painted hot at the same time, and how the selection changes from the keyboard selected item to the one selected with the mouse. In this case the selection should move with the mouse selection. + /// - Notice how if you hit enter when the mouse is over it, it executes the item. That's selection. + /// Scenario 2: Put focus into a combo box, then mouse over a different item + /// - Notice how all the other items you mouse over do not change the way they are painted, if you hit enter, that goes to the combobox, rather than executing the current item. + /// + /// At first look "MouseIsOver" or "Hot" seems to be the thing people want, but its almost never the desired behavior. A unified selection model is simpler and seems to meet the scenarios. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual bool Selected { + get { + return CanSelect && !DesignMode && (state[stateSelected] || + (ParentInternal != null && ParentInternal.IsSelectionSuspended && + ParentInternal.LastMouseDownedItem == this)); + } + } + +#if SELECTEDCHANGED + public event EventHandler SelectedChanged { + add { + Events.AddHandler(EventSelectedChanged, value); + } + remove { + Events.RemoveHandler(EventSelectedChanged, value); + } + } +#endif + + /// + /// + internal protected virtual bool ShowKeyboardCues { + get { + if (!DesignMode) { + return ToolStripManager.ShowMenuFocusCues; + } + // default to true. + return true; + } + } + + /// + /// The size of the item + [ + SRCategory(SR.CatLayout), + Localizable(true), + SRDescription(SR.ToolStripItemSizeDescr) + ] + public virtual Size Size { + get { + return Bounds.Size; + } + set { + Rectangle currentBounds = Bounds; + currentBounds.Size = value; + SetBounds(currentBounds); + } + } + + internal bool SupportsRightClick { + get { + return state[stateSupportsRightClick]; + } + set { + state[stateSupportsRightClick] = value; + } + } + + internal bool SupportsItemClick { + get { + return state[stateSupportsItemClick]; + } + set { + state[stateSupportsItemClick] = value; + } + } + + internal bool SupportsSpaceKey { + get { + return state[stateSupportsSpaceKey]; + } + set { + state[stateSupportsSpaceKey] = value; + } + } + + + internal bool SupportsDisabledHotTracking { + get { + return state[stateSupportsDisabledHotTracking]; + } + set { + state[stateSupportsDisabledHotTracking] = value; + } + } + + /// + /// Summary for Tag + [DefaultValue(null), + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ToolStripItemTagDescr), + TypeConverter(typeof(StringConverter)) + ] + public object Tag { + get { + if (Properties.ContainsObject(ToolStripItem.PropTag)) { + return propertyStore.GetObject(ToolStripItem.PropTag); + } + return null; + + } + set { + Properties.SetObject(ToolStripItem.PropTag, value); + } + } + + /// + /// The text of the item + [ + DefaultValue(""), + SRCategory(SR.CatAppearance), + Localizable(true), + SRDescription(SR.ToolStripItemTextDescr) + ] + public virtual string Text { + get { + if (Properties.ContainsObject(ToolStripItem.PropText)) { + return (string)Properties.GetObject(ToolStripItem.PropText); + } + return ""; + + } + set { + if (value != Text) { + Properties.SetObject(ToolStripItem.PropText, value); + OnTextChanged(EventArgs.Empty); + + } + } + + } + + /// + /// + /// + /// Gets or sets the alignment of the text on the label control. + /// + /// + [ + DefaultValue(ContentAlignment.MiddleCenter), + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripItemTextAlignDescr) + ] + public virtual ContentAlignment TextAlign { + get { + return textAlign; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment)); + } + if (textAlign != value) { + textAlign = value; + InvalidateItemLayout(PropertyNames.TextAlign); + } + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnTextChangedDescr)] + public event EventHandler TextChanged { + add { + Events.AddHandler(EventText, value); + } + remove { + Events.RemoveHandler(EventText, value); + } + } + /// + [ + SRDescription(SR.ToolStripTextDirectionDescr), + SRCategory(SR.CatAppearance) + ] + public virtual ToolStripTextDirection TextDirection { + get { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + if (this.ParentInternal != null) { + // in the case we're on a ToolStripOverflow + textDirection = ParentInternal.TextDirection; + } + else { + textDirection = (Owner == null) ? ToolStripTextDirection.Horizontal : Owner.TextDirection; + } + } + + return textDirection; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripTextDirection.Inherit, (int)ToolStripTextDirection.Vertical270)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripTextDirection)); + } + Properties.SetObject(ToolStripItem.PropTextDirection, value); + InvalidateItemLayout("TextDirection"); + } + } + + /// + [DefaultValue(TextImageRelation.ImageBeforeText), + Localizable(true), + SRDescription(SR.ToolStripItemTextImageRelationDescr), + SRCategory(SR.CatAppearance)] + public TextImageRelation TextImageRelation { + get { + return textImageRelation; + } + set { + if (!WindowsFormsUtils.EnumValidator.IsValidTextImageRelation(value)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(TextImageRelation)); + } + if(value != TextImageRelation) { + textImageRelation = value; + InvalidateItemLayout(PropertyNames.TextImageRelation); + } + } + } + + + /// + /// + /// !!!!This property ONLY works when toolStrip.ShowItemToolTips = true!!!! + /// if AutoToolTip is set to true we return the Text as the ToolTipText. + /// + [SRDescription(SR.ToolStripItemToolTipTextDescr)] + [SRCategory(SR.CatBehavior)] + [Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor))] + [Localizable(true)] + public string ToolTipText { + get { + if (AutoToolTip && string.IsNullOrEmpty(toolTipText)) { + string toolText = Text; + if (WindowsFormsUtils.ContainsMnemonic(toolText)) { + // this shouldnt be called a lot so we can take the perf hit here. + toolText = String.Join("", toolText.Split('&')); + } + return toolText; + } + return toolTipText; + } + set { + toolTipText = value; + } + } + + /// + /// Whether or not the item is visible + [ + SRCategory(SR.CatBehavior), + Localizable(true), + SRDescription(SR.ToolStripItemVisibleDescr) + ] + public bool Visible { + get { + return (ParentInternal!=null) && (ParentInternal.Visible) && Available; + } + set { + SetVisibleCore(value); + } + + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripItemOnVisibleChangedDescr)] + public event EventHandler VisibleChanged { + add { + Events.AddHandler(EventVisibleChanged, value); + } + remove { + Events.RemoveHandler(EventVisibleChanged, value); + } + } + + /// + /// + /// The width of this ToolStripItem. + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public int Width { + get { + return Bounds.Width; + } + set { + Rectangle currentBounds = this.Bounds; + SetBounds(currentBounds.X, currentBounds.Y, value, currentBounds.Height); + } + } + + // + // Methods for ToolStripItem + // + + internal void AccessibilityNotifyClients(AccessibleEvents accEvent) { + if (ParentInternal != null) { + int index = ParentInternal.DisplayedItems.IndexOf(this); + ParentInternal.AccessibilityNotifyClients(accEvent, index); + } + } + + private void Animate() { + Animate(!DesignMode && Visible && Enabled && ParentInternal != null); + } + + private void StopAnimate() { + Animate(false); + } + + private void Animate(bool animate) { + if (animate != state[stateCurrentlyAnimatingImage]) { + if (animate) { + if (Image != null) { + ImageAnimator.Animate(Image, new EventHandler(this.OnAnimationFrameChanged)); + state[stateCurrentlyAnimatingImage] = animate; + } + } + else { + if (Image != null) { + ImageAnimator.StopAnimate(Image, new EventHandler(this.OnAnimationFrameChanged)); + state[stateCurrentlyAnimatingImage] = animate; + } + } + } + } + + + internal bool BeginDragForItemReorder() { + if (Control.ModifierKeys == Keys.Alt) { + + if (this.ParentInternal.Items.Contains(this) && this.ParentInternal.AllowItemReorder) { + // we only drag + ToolStripItem item = this as ToolStripItem; + DoDragDrop(item,DragDropEffects.Move); + return true; + } + + } + return false; + } + + + /// + /// + /// constructs the new instance of the accessibility object for this ToolStripItem. Subclasses + /// should not call base.CreateAccessibilityObject. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual AccessibleObject CreateAccessibilityInstance() { + return new ToolStripItemAccessibleObject(this); + } + + /// + /// Creates an instance of the object that defines how image and text + /// gets laid out in the ToolStripItem + /// + + internal virtual ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripItemInternalLayout(this); + } + /// + /// + /// Disposes this winbar item... + /// + protected override void Dispose(bool disposing) + { + if (disposing) { + state[stateDisposing] = true; + + if (this.Owner != null) { + StopAnimate(); + Debug.Assert(this.Owner.Items.Contains(this), "How can there be a owner and not be in the collection?"); + this.Owner.Items.Remove(this); + toolStripItemInternalLayout = null; + state[stateDisposed] = true; + } + } + base.Dispose (disposing); + + if (disposing) { + // need to call base() first since the Component.Dispose(_) is listened to by the ComponentChangeService + // which Serializes the object in Undo-Redo transactions. + Properties.SetObject(PropMirroredImage, null); + Properties.SetObject(PropImage, null); + state[stateDisposing] = false; + } + + } + + internal static long DoubleClickTicks { + // (DoubleClickTime in ms) * 1,000,000 ns/1ms * 1 Tick / 100ns = XXX in Ticks. + // Therefore: (DoubleClickTime) * 1,000,000/100 = xxx in Ticks. + get { return SystemInformation.DoubleClickTime * 10000; } + } + + /// + /// + /// Begins a drag operation. The allowedEffects determine which + /// drag operations can occur. If the drag operation needs to interop + /// with applications in another process, data should either be + /// a base managed class (String, Bitmap, or Metafile) or some Object + /// that implements System.Runtime.Serialization.ISerializable. data can also be any Object that + /// implements System.Windows.Forms.IDataObject. + /// + [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public DragDropEffects DoDragDrop(Object data, DragDropEffects allowedEffects) { + int[] finalEffect = new int[] {(int)DragDropEffects.None}; + UnsafeNativeMethods.IOleDropSource dropSource = this.DropSource; + IComDataObject dataObject = null; + + dataObject = data as IComDataObject; + if (dataObject == null) { + DataObject iwdata = null; + IDataObject idataObject = data as IDataObject; + if (idataObject != null) { + iwdata = new DataObject(idataObject); + } + else if (data is ToolStripItem) { + + // it seems that the DataObject does string comparison + // on the type, so you can't ask for GetDataPresent of + // a base type (e.g. ToolStripItem) when you are really + // looking at a ToolStripButton. The alternative is + // to set the format string expressly to a string matching + // the type of ToolStripItem + iwdata = new DataObject(); + iwdata.SetData(typeof(ToolStripItem).ToString(), data); + } + else { + iwdata = new DataObject(); + iwdata.SetData(data); + } + dataObject = (IComDataObject)iwdata; + } + + try { + SafeNativeMethods.DoDragDrop(dataObject, dropSource, (int)allowedEffects, finalEffect); + } + catch { + } + return(DragDropEffects)finalEffect[0]; + } + + internal void FireEvent(ToolStripItemEventType met) { + FireEvent(new System.EventArgs(), met); + } + internal void FireEvent(EventArgs e, ToolStripItemEventType met) { + + switch (met) { + case ToolStripItemEventType.LocationChanged: + OnLocationChanged(e); + break; + case ToolStripItemEventType.Paint: + HandlePaint(e as PaintEventArgs); + break; + case ToolStripItemEventType.MouseHover: + // disabled toolstrip items should show tooltips. + // we wont raise mouse events though. + if (!Enabled && ParentInternal != null && !string.IsNullOrEmpty(ToolTipText)) { + ParentInternal.UpdateToolTip(this); + } + else { + FireEventInteractive(e, met); + } + break; + case ToolStripItemEventType.MouseEnter: + HandleMouseEnter(e); + break; + case ToolStripItemEventType.MouseLeave: + // disabled toolstrip items should also clear tooltips. + // we wont raise mouse events though. + if (!Enabled && ParentInternal != null) { + ParentInternal.UpdateToolTip(null); + } + else { + HandleMouseLeave(e); + } + break; + case ToolStripItemEventType.MouseMove: + // VSWhidbey 266970: disabled items typically dont get mouse move + // but they should be allowed to re-order if the ALT key is pressed + if (!Enabled && ParentInternal != null) { + BeginDragForItemReorder(); + } + else { + FireEventInteractive(e, met); + } + break; + default: + FireEventInteractive(e, met); + break; + } + + } + + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + internal void FireEventInteractive(EventArgs e, ToolStripItemEventType met) { + if (Enabled) { + switch (met) { + case ToolStripItemEventType.MouseMove: + HandleMouseMove(e as MouseEventArgs); + break; + case ToolStripItemEventType.MouseHover: + HandleMouseHover(e as EventArgs); + break; + case ToolStripItemEventType.MouseUp: + HandleMouseUp(e as MouseEventArgs); + break; + case ToolStripItemEventType.MouseDown: + HandleMouseDown(e as MouseEventArgs); + break; + case ToolStripItemEventType.Click: + HandleClick(e); + break; + case ToolStripItemEventType.DoubleClick: + HandleDoubleClick(e); + break; + default: + Debug.Assert(false, "Invalid event type."); + break; + } + } + } + + private Font GetOwnerFont() { + if (Owner != null) { + return Owner.Font; + } + return null; + } + + /// we dont want a public settable property... and usually owner will work + /// except for things like the overflow button + public ToolStrip GetCurrentParent() { + return this.Parent; + } + internal ToolStripDropDown GetCurrentParentDropDown() { + if (ParentInternal != null) { + return ParentInternal as ToolStripDropDown; + } + else { + return Owner as ToolStripDropDown; + } + } + /// + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + public virtual Size GetPreferredSize(Size constrainingSize) { + + // Switch Size.Empty to maximum possible values + constrainingSize = LayoutUtils.ConvertZeroToUnbounded(constrainingSize); + return InternalLayout.GetPreferredSize(constrainingSize - Padding.Size) + Padding.Size; + } + + internal Size GetTextSize() { + if (string.IsNullOrEmpty(Text)) { + return Size.Empty; + } + else if (cachedTextSize == Size.Empty) { + cachedTextSize = TextRenderer.MeasureText(Text, Font); + } + return cachedTextSize; + } + + + /// + /// + /// Invalidates the ToolStripItem + /// + public void Invalidate() { + if (this.ParentInternal != null) { + ParentInternal.Invalidate(this.Bounds, true); + } + } + + + /// + /// + /// invalidates a rectangle within the ToolStripItem's bounds + /// + public void Invalidate(Rectangle r) { + // the only value add to this over calling invalidate on the ToolStrip is that + // you can specify the rectangle with coordinates relative to the upper left hand + // corner of the ToolStripItem. + Point rectangleLocation = this.TranslatePoint(r.Location, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ToolStripCoords); + + if (this.ParentInternal != null) { + ParentInternal.Invalidate(new Rectangle(rectangleLocation, r.Size), true); + } + } + + internal void InvalidateItemLayout(string affectedProperty, bool invalidatePainting) { + this.toolStripItemInternalLayout = null; + + if (Owner != null) { + LayoutTransaction.DoLayout(Owner, this, affectedProperty); + } + + if (invalidatePainting && Owner != null) { + Owner.Invalidate(); + } + } + internal void InvalidateItemLayout(string affectedProperty) { + InvalidateItemLayout(affectedProperty, /*invalidatePainting*/true); + } + + internal void InvalidateImageListImage() { + // invalidate the cache. + if (ImageIndexer.ActualIndex >= 0) { + Properties.SetObject(PropImage, null); + InvalidateItemLayout(PropertyNames.Image); + } + } + + internal void InvokePaint() { + if (this.ParentInternal != null) { + this.ParentInternal.InvokePaintItem(this); + } + } + + /// + protected internal virtual bool IsInputKey(Keys keyData) { + return false; + } + + /// + protected internal virtual bool IsInputChar(char charCode) { + return false; + } + + // + // Private handlers which are the equivilant of Control.Wm + // + + private void HandleClick(System.EventArgs e) { + Debug.Assert(Enabled, "Who called me when I am disabled?"); + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleClick"); + + try { + if (!DesignMode) { + state[statePressed] = true; + } + // force painting w/o using message loop here because it may be quite a long + // time before it gets pumped again. + InvokePaint(); + + if (SupportsItemClick && Owner != null) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleItemClick"); + Owner.HandleItemClick(this); + } + + OnClick(e); + + if (SupportsItemClick && Owner != null) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] HandleItemClicked"); + Owner.HandleItemClicked(this); + } + } + finally { + state[statePressed] = false; + } + // when we get around to it, paint unpressed. + Invalidate(); + + } + private void HandleDoubleClick(System.EventArgs e) { + OnDoubleClick(e); + } + private void HandlePaint(PaintEventArgs e) { + Animate(); + ImageAnimator.UpdateFrames(this.Image); + + OnPaint(e); + RaisePaintEvent(EventPaint, e); + } + private void HandleMouseEnter(System.EventArgs e) { + + if (!DesignMode) { + if (ParentInternal != null + && ParentInternal.CanHotTrack + && ParentInternal.ShouldSelectItem()) { + + if (Enabled) { + // calling select can dismiss a child dropdown which would break auto-expansion. + // save off auto expand and restore it. + bool autoExpand = ParentInternal.MenuAutoExpand; + + if (ParentInternal.LastMouseDownedItem == this) { + // Same as Control.MouseButtons == MouseButtons.Left, but slightly more efficient. + if (UnsafeNativeMethods.GetKeyState((int)Keys.LButton) < 0) { + this.Push(true); + } + } + + Select(); + ParentInternal.MenuAutoExpand = autoExpand; + } + else if (SupportsDisabledHotTracking) { + Select(); + } + } + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutMouseEnter(this); + } + + if (Enabled) { + OnMouseEnter(e); + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseEnter"); + RaiseEvent(EventMouseEnter, e); + } + + } + private void HandleMouseMove(System.Windows.Forms.MouseEventArgs mea) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseMove"); + + if (Enabled && CanSelect && !Selected) { + if (ParentInternal != null + && ParentInternal.CanHotTrack + && ParentInternal.ShouldSelectItem()) { + // this is the case where we got a mouse enter, but ShouldSelectItem + // returned false. + // typically occus when a window first opens - we get a mouse enter on the item + // the cursor is hovering over - but we dont actually want to change selection to it. + Select(); + } + } + OnMouseMove(mea); + RaiseMouseEvent(EventMouseMove, mea); + + } + private void HandleMouseHover(EventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseHover"); + OnMouseHover(e); + RaiseEvent(EventMouseHover, e); + } + private void HandleLeave() { + if (state[stateMouseDownAndNoDrag] || state[statePressed] || state[stateSelected]) { + state[stateMouseDownAndNoDrag | statePressed | stateSelected] = false; + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + + Invalidate(); + } + } + private void HandleMouseLeave(System.EventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseLeave"); + HandleLeave(); + if (Enabled) { + OnMouseLeave(e); + RaiseEvent(EventMouseLeave, e); + } + } + private void HandleMouseDown(MouseEventArgs e) { + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseDown"); + + state[stateMouseDownAndNoDrag] = !BeginDragForItemReorder(); + if (state[stateMouseDownAndNoDrag]) { + if (e.Button == MouseButtons.Left) { + this.Push(true); + } + // + OnMouseDown(e); + RaiseMouseEvent(EventMouseDown, e); + } + } + private void HandleMouseUp(MouseEventArgs e) { + + Debug.WriteLineIf(MouseDebugging.TraceVerbose, "[" + this.Text + "] MouseUp"); + + bool fireMouseUp = (ParentInternal.LastMouseDownedItem == this); + + if (!fireMouseUp && !MouseDownAndUpMustBeInSameItem) { + // in the case of menus, you can mouse down on one item and mouse up + // on another. We do need to be careful (as in the case of VSWhidbey 482156) + // that the mouse has actually moved from when a dropdown has been opened - + // otherwise we may accidentally click what's underneath the mouse at the time + // the dropdown is opened. + fireMouseUp = ParentInternal.ShouldSelectItem(); + } + + + if (state[stateMouseDownAndNoDrag] || fireMouseUp) { + this.Push(false); + + if (e.Button == MouseButtons.Left || (e.Button == MouseButtons.Right && state[stateSupportsRightClick])) { + bool shouldFireDoubleClick = false; + if (DoubleClickEnabled) { + long newTime = DateTime.Now.Ticks; + long deltaTicks = newTime - lastClickTime; + lastClickTime = newTime; + // use >= for cases where the delta is so fast DateTime cannot pick up the delta. + Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking"); + // if we've seen a mouse up less than the double click time ago, we should fire. + if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) { + shouldFireDoubleClick = true; + } + } + if (shouldFireDoubleClick) { + HandleDoubleClick(new System.EventArgs()); + // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime. + lastClickTime = 0; + } + else { + HandleClick(new System.EventArgs()); + } + } + + // + OnMouseUp(e); + RaiseMouseEvent(EventMouseUp, e); + + + } + } + + + internal virtual void OnAccessibleDescriptionChanged(EventArgs e) { + } + internal virtual void OnAccessibleNameChanged(EventArgs e) { + } + internal virtual void OnAccessibleDefaultActionDescriptionChanged(EventArgs e) { + } + internal virtual void OnAccessibleRoleChanged(EventArgs e) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnBackColorChanged(EventArgs e) { + this.Invalidate(); + RaiseEvent(EventBackColorChanged, e); + } + + /// + protected virtual void OnBoundsChanged() { + LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Bounds); + InternalLayout.PerformLayout(); + } + + + + /// + protected virtual void OnClick(System.EventArgs e) { + RaiseEvent(EventClick, e); + } + + /// + protected internal virtual void OnLayout(LayoutEventArgs e) { + } + + /// + /// Explicit support of DropTarget + /// + /// + /// + void IDropTarget.OnDragEnter(DragEventArgs dragEvent) { + OnDragEnter(dragEvent); + } + /// + /// + void IDropTarget.OnDragOver(DragEventArgs dragEvent) { + OnDragOver(dragEvent); + } + /// + /// + void IDropTarget.OnDragLeave(EventArgs e) { + OnDragLeave(e); + } + /// + /// + void IDropTarget.OnDragDrop(DragEventArgs dragEvent) { + OnDragDrop(dragEvent); + } + /// + /// Explicit support of DropSource + /// + /// + /// + void ISupportOleDropSource.OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEventArgs) { + OnGiveFeedback(giveFeedbackEventArgs); + } + /// + /// + void ISupportOleDropSource.OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEventArgs) { + OnQueryContinueDrag(queryContinueDragEventArgs); + } + + + private void OnAnimationFrameChanged(object o, EventArgs e) { + + ToolStrip parent = ParentInternal; + if (parent != null) { + if (parent.Disposing || parent.IsDisposed) { + return; + } + + if (parent.IsHandleCreated && parent.InvokeRequired) { + parent.BeginInvoke(new EventHandler(this.OnAnimationFrameChanged), new object[]{o,e}); + return; + } + + Invalidate(); + } + } + + protected virtual void OnAvailableChanged(System.EventArgs e) { + RaiseEvent(EventAvailableChanged, e); + } + + /// + /// + /// Raises the event. + /// Inheriting classes should override this method to handle this event. + /// Call base.onEnter to send this event to any registered event listeners. + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragEnter to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragEnter(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragEnter, dragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragOver to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragOver(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragOver, dragEvent); + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragLeave to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragLeave(EventArgs e) { + RaiseEvent(EventDragLeave, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onDragDrop to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDragDrop(DragEventArgs dragEvent) { + RaiseDragEvent(EventDragDrop, dragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDisplayStyleChanged(EventArgs e) { + RaiseEvent(EventDisplayStyleChanged, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onGiveFeedback to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] // PM review done + protected virtual void OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEvent) { + GiveFeedbackEventHandler handler = (GiveFeedbackEventHandler)Events[EventGiveFeedback]; + if (handler != null) handler(this,giveFeedbackEvent); + } + + internal virtual void OnImageScalingSizeChanged(EventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// Call base.onQueryContinueDrag to send this event to any registered event listeners. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] // PM review done + protected virtual void OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEvent) { + RaiseQueryContinueDragEvent(EventQueryContinueDrag, queryContinueDragEvent); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnDoubleClick(System.EventArgs e) { + RaiseEvent(EventDoubleClick, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnEnabledChanged(System.EventArgs e) { + RaiseEvent(EventEnabledChanged, e); + Animate(); + } + + internal void OnInternalEnabledChanged(System.EventArgs e) { + RaiseEvent(EventInternalEnabledChanged, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnForeColorChanged(EventArgs e) { + this.Invalidate(); + RaiseEvent(EventForeColorChanged, e); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFontChanged(EventArgs e) { + cachedTextSize = Size.Empty; + // PERF - only invalidate if we actually care about the font + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + this.InvalidateItemLayout(PropertyNames.Font); + } + else { + this.toolStripItemInternalLayout = null; + } + RaiseEvent(EventFontChanged, e); + } + + /// + protected virtual void OnLocationChanged (System.EventArgs e) { + RaiseEvent(EventLocationChanged, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseEnter(EventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseHover(EventArgs e) { + if (ParentInternal != null && !string.IsNullOrEmpty(ToolTipText)) { + ParentInternal.UpdateToolTip(this); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseLeave(EventArgs e) { + if (ParentInternal != null) { + ParentInternal.UpdateToolTip(null); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseDown(MouseEventArgs e) { + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnMouseUp(MouseEventArgs e) { + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnPaint(System.Windows.Forms.PaintEventArgs e) { + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentBackColorChanged(EventArgs e) { + Color backColor = Properties.GetColor(PropBackColor); + if (backColor.IsEmpty) { + OnBackColorChanged(e); + } + } + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnParentChanged(ToolStrip oldParent, ToolStrip newParent) { + SetAmbientMargin(); + if ((oldParent != null) && (oldParent.DropTargetManager != null)) { + oldParent.DropTargetManager.EnsureUnRegistered(this); + } + if (AllowDrop && (newParent != null)) { + EnsureParentDropTargetRegistered(); + } + Animate(); + } + + /// + /// + /// Occurs when this.Parent.Enabled changes. + /// + protected internal virtual void OnParentEnabledChanged(System.EventArgs e) { + OnEnabledChanged(EventArgs.Empty); + } + + /// + /// + /// Occurs when the font property has changed on the parent - used to notify inheritors of the font property that + /// the font has changed + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal protected virtual void OnOwnerFontChanged(EventArgs e) { + if (Properties.GetObject(PropFont) == null) { + OnFontChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnParentForeColorChanged(EventArgs e) { + Color foreColor = Properties.GetColor(PropForeColor); + if (foreColor.IsEmpty) { + OnForeColorChanged(e); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected internal virtual void OnParentRightToLeftChanged(EventArgs e) { + if (!Properties.ContainsInteger(PropRightToLeft) || ((RightToLeft)Properties.GetInteger(PropRightToLeft)) == RightToLeft.Inherit) { + OnRightToLeftChanged(e); + } + } + + + /// + /// + /// Occurs when the owner of an item changes. + /// + protected virtual void OnOwnerChanged(EventArgs e) { + RaiseEvent(EventOwnerChanged,e); + SetAmbientMargin(); + if (Owner != null) { + // check if we need to fire OnRightToLeftChanged + bool found = false; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + rightToLeft = (int)RightToLeft.Inherit; + } + if ((rightToLeft == (int)RightToLeft.Inherit) && RightToLeft != DefaultRightToLeft) { + OnRightToLeftChanged(EventArgs.Empty); + } + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal void OnOwnerTextDirectionChanged() { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + + if (textDirection == ToolStripTextDirection.Inherit) { + InvalidateItemLayout("TextDirection"); + } + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnRightToLeftChanged(EventArgs e) { + InvalidateItemLayout(PropertyNames.RightToLeft); + RaiseEvent(EventRightToLeft, e); + + } +#if SELECTEDCHANGED + protected virtual void OnSelectedChanged(EventArgs e) { + RaiseEvent(EventSelectedChanged, e); + } +#endif + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnTextChanged(EventArgs e) { + cachedTextSize = Size.Empty; + // VSWhidbey 399025: make sure we clear the cache before we perform the layout. + InvalidateItemLayout(PropertyNames.Text); + RaiseEvent(EventText, e); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnVisibleChanged(System.EventArgs e) { + if (Owner != null && !(Owner.IsDisposed || Owner.Disposing)) { + Owner.OnItemVisibleChanged(new ToolStripItemEventArgs(this), /*performLayout*/true); + } + RaiseEvent(EventVisibleChanged, e); + Animate(); + } + + public void PerformClick() { + if (Enabled && Available) { + FireEvent(ToolStripItemEventType.Click); + } + } + + /// + /// Pushes the button. + /// + /// + internal void Push(bool push) { + if (!CanSelect || !Enabled || DesignMode) { + return; + } + + if (state[statePressed] != push) { + state[statePressed] = push; + if (Available) { + this.Invalidate(); + } + } + } + /// + /// + /// See Control.ProcessDialogKey for more info. + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + protected internal virtual bool ProcessDialogKey(Keys keyData) { + // + if (keyData == Keys.Enter || (state[stateSupportsSpaceKey] && keyData == Keys.Space)) { + FireEvent(ToolStripItemEventType.Click); + if (ParentInternal != null && !ParentInternal.IsDropDown && !(AccessibilityImprovements.Level2 && !Enabled)) { + ParentInternal.RestoreFocusInternal(); + } + return true; + } + return false; + } + /// + /// + /// See Control.ProcessCmdKey for more info. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected internal virtual bool ProcessCmdKey(ref Message m, Keys keyData) { + return false; + } + + /// + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal virtual bool ProcessMnemonic(char charCode) { + // checking IsMnemonic is not necessary - control does this for us. + FireEvent(ToolStripItemEventType.Click); + return true; + } + + + internal void RaiseCancelEvent(object key, CancelEventArgs e) { + CancelEventHandler handler = (CancelEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + internal void RaiseDragEvent(object key, DragEventArgs e) { + DragEventHandler handler = (DragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseEvent(object key, EventArgs e) { + EventHandler handler = (EventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseKeyEvent(object key, KeyEventArgs e) { + KeyEventHandler handler = (KeyEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseKeyPressEvent(object key, KeyPressEventArgs e) { + KeyPressEventHandler handler = (KeyPressEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaiseMouseEvent(object key, MouseEventArgs e) { + MouseEventHandler handler = (MouseEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + internal void RaisePaintEvent(object key, PaintEventArgs e) { + PaintEventHandler handler = (PaintEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + internal void RaiseQueryContinueDragEvent(object key, QueryContinueDragEventArgs e) { + QueryContinueDragEventHandler handler = (QueryContinueDragEventHandler)Events[key]; + if (handler != null) handler(this, e); + } + + private void ResetToolTipText() { + toolTipText = null; + } + + // This is de facto behind the 4.8/EnableDpiMessaging/ToolStrip HighDpi quirks. + internal virtual void ToolStrip_RescaleConstants(int oldDpi, int newDpi) { + DeviceDpi = newDpi; + RescaleConstantsInternal(newDpi); + + OnFontChanged(EventArgs.Empty); + } + + internal void RescaleConstantsInternal(int newDpi) { + ToolStripManager.CurrentDpi = newDpi; + defaultFont = ToolStripManager.DefaultFont; + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin, deviceDpi); + scaledDefaultStatusStripMargin = DpiHelper.LogicalToDeviceUnits(defaultStatusStripMargin, deviceDpi); + } + + /// + /// + /// Selects the item + /// + public void Select() { + +#if DEBUG + // let's not snap the stack trace unless we're debugging selection. + if (ToolStrip.SelectionDebug.TraceVerbose) { + Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "[Selection DBG] WBI.Select: {0} \r\n{1}\r\n", this.ToString(), new StackTrace().ToString().Substring(0,200))); + } +#endif + if (!CanSelect) { + return; + } + + if (Owner != null && Owner.IsCurrentlyDragging) { + // make sure we dont select during a drag operation. + return; + } + if (ParentInternal != null && ParentInternal.IsSelectionSuspended) { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[Selection DBG] BAILING, selection is currently suspended"); + return; + } + + if (!Selected) { + state[stateSelected] = true; + if (ParentInternal != null) { + ParentInternal.NotifySelectionChange(this); + Debug.Assert(state[stateSelected], "calling notify selection change changed the selection state of this item"); + } + if (IsOnDropDown) { + if (OwnerItem != null && OwnerItem.IsOnDropDown) { + // ensure the selection is moved back to our owner item. + OwnerItem.Select(); + } + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(this); + } + + if (AccessibilityImprovements.Level3 && AccessibilityObject is ToolStripItemAccessibleObject) { + ((ToolStripItemAccessibleObject)AccessibilityObject).RaiseFocusChanged(); + } + +#if SELECTEDCHANGED + OnSelectedChanged(EventArgs.Empty); +#endif + } + } + + internal void SetOwner(ToolStrip newOwner) { + if (owner != newOwner) { + Font f = this.Font; + + if (owner != null) { + owner.rescaleConstsCallbackDelegate -= ToolStrip_RescaleConstants; + } + owner = newOwner; + + if (owner != null) { + owner.rescaleConstsCallbackDelegate += ToolStrip_RescaleConstants; + } + + // clear the parent if the owner is null... + // + if (newOwner == null) { + this.ParentInternal = null; + } + if (!state[stateDisposing] && !IsDisposed) { + OnOwnerChanged(EventArgs.Empty); + if (f != Font) { + OnFontChanged(EventArgs.Empty); + } + } + } + + } + + + + protected virtual void SetVisibleCore(bool visible) { + if (state[stateVisible] != visible) { + state[stateVisible]= visible; + Unselect(); + Push(false); + + OnAvailableChanged(EventArgs.Empty); + OnVisibleChanged(EventArgs.Empty); + } + } + + /// + /// + /// Sets the bounds of the item + /// + internal protected virtual void SetBounds(Rectangle bounds) { + Rectangle oldBounds = this.bounds; + this.bounds = bounds; + + if (! state[stateContstructing]) { + // Dont fire while we're in the base constructor as the inherited + // class may not have had a chance to initialize yet. + + if (this.bounds != oldBounds) { + OnBoundsChanged(); + } + + if (this.bounds.Location != oldBounds.Location) { + OnLocationChanged(EventArgs.Empty); + } + } + } + + /// + /// Sets the bounds of the item + /// + internal void SetBounds(int x, int y, int width, int height) { + SetBounds(new Rectangle(x,y,width,height)); + } + /// + /// Sets the placement of the item + /// + internal void SetPlacement(ToolStripItemPlacement placement) { + this.placement = placement; + } + + // Some implementations of DefaultMargin check which container they + // are on. They need to be re-evaluated when the containership changes. + // DefaultMargin will stop being honored the moment someone sets the Margin property. + internal void SetAmbientMargin() { + if (state[stateUseAmbientMargin] && Margin != DefaultMargin) { + CommonProperties.SetMargin(this, DefaultMargin); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageTransparentColor() { + return ImageTransparentColor != Color.Empty; + } + + /// + /// Returns true if the backColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeBackColor() { + Color backColor = Properties.GetColor(PropBackColor); + return !backColor.IsEmpty; + } + + private bool ShouldSerializeDisplayStyle() { + return DisplayStyle != DefaultDisplayStyle; + } + + private bool ShouldSerializeToolTipText() { + return !string.IsNullOrEmpty(toolTipText); + } + /// + /// Returns true if the foreColor should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeForeColor() { + Color foreColor = Properties.GetColor(PropForeColor); + return !foreColor.IsEmpty; + } + + /// + /// Returns true if the font should be persisted in code gen. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeFont() { + bool found; + object font = Properties.GetObject(PropFont, out found); + return (found && font != null); + } + + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializePadding() { + return (Padding != DefaultPadding); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeMargin() { + return (Margin != DefaultMargin); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeVisible() { + return !state[stateVisible]; // only serialize if someone turned off visiblilty + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImage() { + return (Image != null) && (ImageIndexer.ActualIndex < 0); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageKey() { + return (Image != null) && (ImageIndexer.ActualIndex >= 0) && (ImageIndexer.Key != null && ImageIndexer.Key.Length != 0); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeImageIndex() { + return (Image != null) && (ImageIndexer.ActualIndex >= 0) && (ImageIndexer.Index != -1); + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeRightToLeft() { + bool found = false; + int rightToLeft = Properties.GetInteger(PropRightToLeft, out found); + if (!found) { + return false; + } + return (rightToLeft != (int)DefaultRightToLeft); + } + + private bool ShouldSerializeTextDirection() { + ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit; + if (Properties.ContainsObject(ToolStripItem.PropTextDirection)) { + textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStripItem.PropTextDirection); + } + return textDirection != ToolStripTextDirection.Inherit; + } + + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetBackColor() { + BackColor = Color.Empty; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetDisplayStyle() { + DisplayStyle = DefaultDisplayStyle; + } + + /// + /// + /// Resets the fore color to be based on the parent's fore color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetForeColor() { + ForeColor = Color.Empty; + } + + + /// + /// + /// Resets the Font to be based on the parent's Font. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetFont() { + Font = null; + } + + /// + /// + /// Resets the back color to be based on the parent's back color. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetImage() { + Image = null; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private void ResetImageTransparentColor() { + ImageTransparentColor = Color.Empty; + } + + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetMargin() { + state[stateUseAmbientMargin] = true; + SetAmbientMargin(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ResetPadding() { + CommonProperties.ResetPadding(this); + } + /// + /// + /// Resets the RightToLeft to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetRightToLeft() { + RightToLeft = RightToLeft.Inherit; + } + + /// + /// + /// Resets the TextDirection to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetTextDirection() { + TextDirection = ToolStripTextDirection.Inherit; + } + + /// + /// Translates a point from one coordinate system to another + /// + internal Point TranslatePoint(Point fromPoint, ToolStripPointType fromPointType, ToolStripPointType toPointType) { + ToolStrip parent = ParentInternal; + + if (parent == null) { + parent = (IsOnOverflow && Owner != null)? Owner.OverflowButton.DropDown : Owner; + } + if (parent == null) { + // should not throw here as it's an internal function call. + Debug.Fail("could not determine current parent"); + return fromPoint; + } + + if (fromPointType == toPointType) { + return fromPoint; + } + + Point toPoint = Point.Empty; + Point currentToolStripItemLocation = Bounds.Location; + + // From: Screen + // To: ToolStrip or ToolStripItem + if (fromPointType == ToolStripPointType.ScreenCoords) { + // Convert ScreenCoords --> ToolStripCoords + toPoint = parent.PointToClient(fromPoint); + + // Convert ToolStripCoords --> ToolStripItemCoords + if (toPointType == ToolStripPointType.ToolStripItemCoords) { + toPoint.X += currentToolStripItemLocation.X; + toPoint.Y += currentToolStripItemLocation.Y; + } + } + // From: ToolStrip or ToolStripItem + // To: Screen or ToolStripItem + else { + // Convert "fromPoint" ToolStripItemCoords --> ToolStripCoords + if (fromPointType == ToolStripPointType.ToolStripItemCoords) { + fromPoint.X += currentToolStripItemLocation.X; + fromPoint.Y += currentToolStripItemLocation.Y; + } + + // At this point, fromPoint is now in ToolStrip coordinates. + + // Convert ToolStripCoords --> ScreenCoords + if (toPointType == ToolStripPointType.ScreenCoords) { + toPoint = parent.PointToScreen(fromPoint); + } + // Convert ToolStripCoords --> ToolStripItemCoords + else if (toPointType == ToolStripPointType.ToolStripItemCoords) { + fromPoint.X -= currentToolStripItemLocation.X; + fromPoint.Y -= currentToolStripItemLocation.Y; + toPoint = fromPoint; + } + else { + Debug.Assert((toPointType == ToolStripPointType.ToolStripCoords), "why are we here! - investigate"); + toPoint = fromPoint; + } + + } + return toPoint; + + } + + internal ToolStrip RootToolStrip { + get { + ToolStripItem item = this; + while (item.OwnerItem != null) { + item = item.OwnerItem; + } + return item.ParentInternal; + } + } + + /// + /// + /// ToString support + /// + public override string ToString() { + if (Text != null && this.Text.Length != 0) { + return this.Text; + } + return base.ToString(); + } + + /// + /// removes selection bits from item state + /// + internal void Unselect() { + Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "[Selection DBG] WBI.Unselect: {0}", this.ToString())); + if (state[stateSelected]) { + state[stateSelected] = false; + if (Available) { + Invalidate(); + if (ParentInternal != null) { + ParentInternal.NotifySelectionChange(this); + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } +#if SELECTEDCHANGED + OnSelectedChanged(EventArgs.Empty); +#endif + } + } + } + + #region IKeyboardToolTip implementation + + bool IKeyboardToolTip.CanShowToolTipsNow() { + return this.Visible && this.parent != null && ((IKeyboardToolTip)this.parent).AllowsChildrenToShowToolTips(); + } + + Rectangle IKeyboardToolTip.GetNativeScreenRectangle() { + return this.AccessibilityObject.Bounds; + } + + IList IKeyboardToolTip.GetNeighboringToolsRectangles() { + List neighbors = new List(3); + if (this.parent != null) { + ToolStripItemCollection items = this.parent.DisplayedItems; + int i = 0, count = items.Count; + bool found = false; + while (!found && i < count) { + found = Object.ReferenceEquals(items[i], this); + if (found) { + int previousIndex = i - 1; + if (previousIndex >= 0) { + neighbors.Add(((IKeyboardToolTip)items[previousIndex]).GetNativeScreenRectangle()); + } + + int nextIndex = i + 1; + if (nextIndex < count) { + neighbors.Add(((IKeyboardToolTip)items[nextIndex]).GetNativeScreenRectangle()); + } + } + else { + i++; + } + } + Debug.Assert(i < count, "Item has a parent set but the parent doesn't own the item"); + } + + ToolStripDropDown dropDown = this.parent as ToolStripDropDown; + if (dropDown != null && dropDown.OwnerItem != null) { + neighbors.Add(((IKeyboardToolTip)dropDown.OwnerItem).GetNativeScreenRectangle()); + } + + return neighbors; + } + + bool IKeyboardToolTip.IsHoveredWithMouse() { + return ((IKeyboardToolTip)this).GetNativeScreenRectangle().Contains(Control.MousePosition); + } + + bool IKeyboardToolTip.HasRtlModeEnabled() { + return this.parent != null && ((IKeyboardToolTip)this.parent).HasRtlModeEnabled(); + } + + bool IKeyboardToolTip.AllowsToolTip() { + return true; + } + + IWin32Window IKeyboardToolTip.GetOwnerWindow() { + Debug.Assert(this.ParentInternal != null, "Tool Strip Item Parent is null"); + return this.ParentInternal; + } + + void IKeyboardToolTip.OnHooked(ToolTip toolTip) { + this.OnKeyboardToolTipHook(toolTip); + } + + void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) { + this.OnKeyboardToolTipUnhook(toolTip); + } + + string IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) { + return this.ToolTipText; + } + + bool IKeyboardToolTip.ShowsOwnToolTip() { + return true; + } + + bool IKeyboardToolTip.IsBeingTabbedTo() { + return this.IsBeingTabbedTo(); + } + + bool IKeyboardToolTip.AllowsChildrenToShowToolTips() { + return true; + } + + #endregion + + internal virtual void OnKeyboardToolTipHook(ToolTip toolTip) { + } + + internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip) { + } + + internal virtual bool IsBeingTabbedTo() { + return ToolStrip.AreCommonNavigationalKeysDown(); + } + + /// + /// + /// An implementation of AccessibleChild for use with ToolStripItems + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class ToolStripItemAccessibleObject : AccessibleObject { + + // Member variables + + private ToolStripItem ownerItem = null; // The associated ToolStripItem for this AccessibleChild (if any) + + private AccessibleStates additionalState = AccessibleStates.None; // Test hook for the designer + + private int[] runtimeId = null; // Used by UIAutomation + // constructors + + /// + /// + /// [To be supplied.] + /// + public ToolStripItemAccessibleObject(ToolStripItem ownerItem) { + + Debug.Assert(ownerItem != null, "Cannot construct a ToolStripItemAccessibleObject with a null ownerItem"); + if (ownerItem == null) { + throw new ArgumentNullException("ownerItem"); + } + + this.ownerItem = ownerItem; + } + + /// + public override string DefaultAction { + get { + string defaultAction = ownerItem.AccessibleDefaultActionDescription; + if (defaultAction != null) { + return defaultAction; + } + else { + return SR.GetString(SR.AccessibleActionPress); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Description { + get { + string description = ownerItem.AccessibleDescription; + if (description != null) { + return description; + } + else { + return base.Description; + } + } + } + + /// + /// + /// [To be supplied.] + /// + public override string Help { + get { + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[ToolStripItem.EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + return args.HelpString; + } + else { + return base.Help; + } + } + } + + public override string KeyboardShortcut { + get { + // This really is the Mnemonic - NOT the shortcut. E.g. in notepad Edit->Replace is Control+H + // but the KeyboardShortcut comes up as the mnemonic 'r'. + char mnemonic = WindowsFormsUtils.GetMnemonic(ownerItem.Text, false); + if (ownerItem.IsOnDropDown) { + // no ALT on dropdown + return (mnemonic == (char) 0) ? string.Empty : mnemonic.ToString(); + } + return (mnemonic == (char) 0) ? string.Empty : ("Alt+" + mnemonic); + + } + } + + internal override int[] RuntimeId { + get { + if (AccessibilityImprovements.Level1) { + if (runtimeId == null) { + // we need to provide a unique ID + // others are implementing this in the same manner + // first item is static - 0x2a + // [AccessibilityImprovements.Level3] first item should be UiaAppendRuntimeId since this is not a top-level element of the fragment. + // second item can be anything, but here it is a hash. For toolstrip hash is unique even with child controls. Hwnd is not. + + runtimeId = new int[2]; + runtimeId[0] = AccessibilityImprovements.Level3 ? NativeMethods.UiaAppendRuntimeId : 0x2a; + runtimeId[1] = ownerItem.GetHashCode(); + } + return runtimeId; + } + else { + return base.RuntimeId; + } + } + } + + /// + /// Gets the accessible property value. + /// + /// The accessible property ID. + /// The accessible property value. + internal override object GetPropertyValue(int propertyID) { + + if (AccessibilityImprovements.Level1) { + if (propertyID == NativeMethods.UIA_NamePropertyId) { + return Name; + } + else if (propertyID == NativeMethods.UIA_IsExpandCollapsePatternAvailablePropertyId) { + return (Object)this.IsPatternSupported(NativeMethods.UIA_ExpandCollapsePatternId); + } + } + + if (AccessibilityImprovements.Level3) { + switch (propertyID) { + case NativeMethods.UIA_IsEnabledPropertyId: + return ownerItem.Enabled; + case NativeMethods.UIA_IsOffscreenPropertyId: + return ownerItem.Placement != ToolStripItemPlacement.Main; + case NativeMethods.UIA_IsKeyboardFocusablePropertyId: + return ownerItem.CanSelect; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return ownerItem.Selected; + case NativeMethods.UIA_AccessKeyPropertyId: + return KeyboardShortcut; + case NativeMethods.UIA_IsPasswordPropertyId: + return false; + case NativeMethods.UIA_HelpTextPropertyId: + return Help ?? string.Empty; + } + } + + return base.GetPropertyValue(propertyID); + } + + /// + /// + /// [To be supplied.] + /// + public override string Name { + get { + string name = ownerItem.AccessibleName; + if (name != null) { + return name; + } + + string baseName = base.Name; + + if (baseName == null || baseName.Length == 0) { + return WindowsFormsUtils.TextWithoutMnemonics(ownerItem.Text); + } + + return baseName; + } + set { + ownerItem.AccessibleName = value; + } + } + + /// + /// + /// [To be supplied.] + /// + internal ToolStripItem Owner { + get { + return ownerItem; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = ownerItem.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return AccessibleRole.PushButton; + } + + } + } + + public override AccessibleStates State { + get { + if (!ownerItem.CanSelect) { + return base.State | additionalState; + } + + + if (!ownerItem.Enabled) { + + if (AccessibilityImprovements.Level2) { + if (ownerItem.Selected && ownerItem is ToolStripMenuItem) { + return AccessibleStates.Unavailable | additionalState | AccessibleStates.Focused; + } + } + + // VSO 436154 - Disabled menu items that are selected must have focus + // state so that Narrator can announce them. + if (AccessibilityImprovements.Level1) { + if (ownerItem.Selected && ownerItem is ToolStripMenuItem) { + return AccessibleStates.Focused; + } + } + + return AccessibleStates.Unavailable | additionalState; + } + + AccessibleStates accState = AccessibleStates.Focusable | additionalState; + + // + /*if (HasDropDownItems) { + accState |= AccessibleState.HasPopup; + }*/ + + if (ownerItem.Selected || ownerItem.Pressed) { + accState |= AccessibleStates.Focused | AccessibleStates.HotTracked; + } + if (ownerItem.Pressed) { + accState |= AccessibleStates.Pressed; + } + return accState; + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + if (Owner != null) { + ((ToolStripItem)Owner).PerformClick(); + } + } + /// + /// + /// [To be supplied.] + /// + public override int GetHelpTopic(out string fileName) { + int topic = 0; + + QueryAccessibilityHelpEventHandler handler = (QueryAccessibilityHelpEventHandler)Owner.Events[ToolStripItem.EventQueryAccessibilityHelp]; + + if (handler != null) { + QueryAccessibilityHelpEventArgs args = new QueryAccessibilityHelpEventArgs(); + handler(Owner, args); + + fileName = args.HelpNamespace; + if (fileName != null && fileName.Length > 0) { + IntSecurity.DemandFileIO(FileIOPermissionAccess.PathDiscovery, fileName); + } + + try { + topic = Int32.Parse(args.HelpKeyword, CultureInfo.InvariantCulture); + } + catch { + } + + return topic; + } + else { + return base.GetHelpTopic(out fileName); + } + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override AccessibleObject Navigate(AccessibleNavigation navigationDirection) { + ToolStripItem nextItem = null; + + if (Owner != null) { + ToolStrip parent = Owner.ParentInternal; + if (parent == null) { + return null; + } + + bool forwardInCollection = (parent.RightToLeft == RightToLeft.No); + switch(navigationDirection) { + case AccessibleNavigation.FirstChild: + nextItem = parent.GetNextItem(null, ArrowDirection.Right, /*RTLAware=*/true); + break; + case AccessibleNavigation.LastChild: + nextItem = parent.GetNextItem(null, ArrowDirection.Left,/*RTLAware=*/true); + break; + case AccessibleNavigation.Previous: + case AccessibleNavigation.Left: + nextItem = parent.GetNextItem(Owner, ArrowDirection.Left, /*RTLAware=*/true); + break; + case AccessibleNavigation.Next: + case AccessibleNavigation.Right: + nextItem = parent.GetNextItem(Owner, ArrowDirection.Right, /*RTLAware=*/true); + break; + case AccessibleNavigation.Up: + nextItem = (Owner.IsOnDropDown) ? parent.GetNextItem(Owner, ArrowDirection.Up): + parent.GetNextItem(Owner, ArrowDirection.Left, /*RTLAware=*/true); + break; + case AccessibleNavigation.Down: + nextItem = (Owner.IsOnDropDown) ? parent.GetNextItem(Owner, ArrowDirection.Down): + parent.GetNextItem(Owner, ArrowDirection.Right, /*RTLAware=*/true); + break; + + } + + + } + if (nextItem != null) { + return nextItem.AccessibilityObject; + } + return null; + } + + // + public void AddState(AccessibleStates state) { + if (state == AccessibleStates.None) { + additionalState = state; + } + else { + additionalState |= state; + } + } + + /// + /// + /// [To be supplied.] + /// + public override string ToString() { + if (Owner != null) { + return "ToolStripItemAccessibleObject: Owner = " + Owner.ToString(); + } + else { + return "ToolStripItemAccessibleObject: Owner = null"; + } + + } + + /// + /// + /// Gets the bounds of the accessible object, in screen coordinates. + /// + public override Rectangle Bounds { + get { + Rectangle bounds = Owner.Bounds; + if (Owner.ParentInternal != null && + Owner.ParentInternal.Visible) { + return new Rectangle(Owner.ParentInternal.PointToScreen(bounds.Location), bounds.Size); + } + return Rectangle.Empty; + } + } + + /// + /// + /// When overridden in a derived class, gets or sets the parent of an accessible object. + /// + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + if (Owner.IsOnDropDown) { + // VSWhidbey 477274: return the owner item as the accessible parent. + ToolStripDropDown dropDown = Owner.GetCurrentParentDropDown(); + if (dropDown.OwnerItem != null) { + return dropDown.OwnerItem.AccessibilityObject; + } + return dropDown.AccessibilityObject; + } + return (Owner.Parent != null) ? Owner.Parent.AccessibilityObject : base.Parent; + + + } + } + + /// + /// Gets the top level element. + /// + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + return ownerItem.RootToolStrip?.AccessibilityObject; + } + } + + /// + /// Returns the element in the specified direction. + /// + /// Indicates the direction in which to navigate. + /// Returns the element in the specified direction. + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + return Parent; + case UnsafeNativeMethods.NavigateDirection.NextSibling: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + int index = GetChildFragmentIndex(); + if (index == -1) { + Debug.Fail("No item matched the index?"); + return null; + } + + int increment = direction == UnsafeNativeMethods.NavigateDirection.NextSibling ? 1 : -1; + AccessibleObject sibling = null; + + if (AccessibilityImprovements.Level3) { + index += increment; + int itemsCount = GetChildFragmentCount(); + if (index >= 0 && index < itemsCount) { + sibling = GetChildFragment(index); + } + } + else { + // Skipping contol host items, as they are provided by system + do { + index += increment; + sibling = index >= 0 && index < Parent.GetChildCount() ? Parent.GetChild(index) : null; + } while (sibling != null && sibling is Control.ControlAccessibleObject); + } + + return sibling; + } + + return base.FragmentNavigate(direction); + } + + private AccessibleObject GetChildFragment(int index) { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragment(index); + } + + // ToolStripOverflowButtonAccessibleObject is derived from ToolStripDropDownItemAccessibleObject + // and we should not process ToolStripOverflowButton as a ToolStripDropDownItem here so check for + // the ToolStripOverflowButton firstly as more specific condition. + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildFragment(index, true); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildFragment(index); + } + + return null; + } + + private int GetChildFragmentCount() { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragmentCount(); + } + + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildOverflowFragmentCount(); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildCount(); + } + + return -1; + } + + private int GetChildFragmentIndex() { + var toolStripParent = Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripParent != null) { + return toolStripParent.GetChildFragmentIndex(this); + } + + var toolStripOverflowButtonParent = Parent as ToolStripOverflowButton.ToolStripOverflowButtonAccessibleObject; + if (toolStripOverflowButtonParent != null) { + var toolStripGrandParent = toolStripOverflowButtonParent.Parent as ToolStrip.ToolStripAccessibleObject; + if (toolStripGrandParent != null) { + return toolStripGrandParent.GetChildFragmentIndex(this); + } + } + + var dropDownItemParent = Parent as ToolStripDropDownItemAccessibleObject; + if (dropDownItemParent != null) { + return dropDownItemParent.GetChildFragmentIndex(this); + } + + return -1; + } + + internal override void SetFocus() { + Owner.Select(); + } + + internal void RaiseFocusChanged() { + ToolStrip root = ownerItem.RootToolStrip; + if (root != null && root.SupportsUiaProviders) { + RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + } + } + + } + + // We need a special way to defer to the ToolStripItem's image + // list for indexing purposes. + internal class ToolStripItemImageIndexer : ImageList.Indexer { + private ToolStripItem item; + + public ToolStripItemImageIndexer(ToolStripItem item) { + this.item = item; + } + + public override ImageList ImageList { + get { + if ((item != null) && (item.Owner != null)) { + return item.Owner.ImageList; + } + return null; + } + set { Debug.Assert(false, "We should never set the image list"); } + } + } + + + + + /// + /// + /// This class helps determine where the image and text should be drawn. + /// + /// + /// + internal class ToolStripItemInternalLayout { + + private ToolStripItemLayoutOptions currentLayoutOptions; + private ToolStripItem ownerItem; + private ButtonBaseAdapter.LayoutData layoutData; + private const int BORDER_WIDTH = 2; + private const int BORDER_HEIGHT = 3; + private readonly static Size INVALID_SIZE = new Size(Int32.MinValue,Int32.MinValue); + + private Size lastPreferredSize = INVALID_SIZE; + private ToolStripLayoutData parentLayoutData = null; + + + public ToolStripItemInternalLayout(ToolStripItem ownerItem) { + if (ownerItem == null) { + throw new ArgumentNullException("ownerItem"); + } + this.ownerItem = ownerItem; + } + + + // the thing that we fetch properties off of -- this can be different than ownerItem - e.g. case of split button. + protected virtual ToolStripItem Owner { + get { return ownerItem; } + } + + public virtual Rectangle ImageRectangle { + get { + Rectangle imageRect = LayoutData.imageBounds; + imageRect.Intersect(layoutData.field); + return imageRect; + } + } + + + + internal ButtonBaseAdapter.LayoutData LayoutData { + get { + EnsureLayout(); + return layoutData; + } + } + + public Size PreferredImageSize { + get { + return Owner.PreferredImageSize; + } + } + + + protected virtual ToolStrip ParentInternal { + get { + return (ownerItem != null) ? ownerItem.ParentInternal : null; + } + } + + public virtual Rectangle TextRectangle { + get { + Rectangle textRect = LayoutData.textBounds; + textRect.Intersect(layoutData.field); + return textRect; + } + } + + public virtual Rectangle ContentRectangle { + get { + return LayoutData.field; + } + } + + public virtual TextFormatFlags TextFormat { + get { + if (currentLayoutOptions != null) { + return currentLayoutOptions.gdiTextFormatFlags; + } + return CommonLayoutOptions().gdiTextFormatFlags; + } + } + + + internal static TextFormatFlags ContentAlignToTextFormat(ContentAlignment alignment, bool rightToLeft) { + TextFormatFlags textFormat = TextFormatFlags.Default; + if (rightToLeft) { + //We specifically do not want to turn on TextFormatFlags.Right. + textFormat |= TextFormatFlags.RightToLeft; + } + + // Calculate Text Positioning + textFormat |= ControlPaint.TranslateAlignmentForGDI(alignment); + textFormat |= ControlPaint.TranslateLineAlignmentForGDI(alignment); + return textFormat; + + } + + + protected virtual ToolStripItemLayoutOptions CommonLayoutOptions() { + ToolStripItemLayoutOptions layoutOptions = new ToolStripItemLayoutOptions(); + Rectangle bounds = new Rectangle(Point.Empty, ownerItem.Size); + + layoutOptions.client = bounds; + + layoutOptions.growBorderBy1PxWhenDefault = false; + + layoutOptions.borderSize = BORDER_WIDTH; + layoutOptions.paddingSize = 0; + layoutOptions.maxFocus = true; + layoutOptions.focusOddEvenFixup = false; + layoutOptions.font = ownerItem.Font; + layoutOptions.text = ((Owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) ? Owner.Text : String.Empty; + layoutOptions.imageSize = PreferredImageSize; + layoutOptions.checkSize = 0; + layoutOptions.checkPaddingSize = 0; + layoutOptions.checkAlign = ContentAlignment.TopLeft; + layoutOptions.imageAlign = Owner.ImageAlign; + layoutOptions.textAlign = Owner.TextAlign; + layoutOptions.hintTextUp = false; + layoutOptions.shadowedText = !ownerItem.Enabled; + layoutOptions.layoutRTL = RightToLeft.Yes == Owner.RightToLeft; + layoutOptions.textImageRelation = Owner.TextImageRelation; + //set textImageInset to 0 since we don't draw 3D border for ToolStripItems. + layoutOptions.textImageInset = 0; + layoutOptions.everettButtonCompat = false; + + + // Support RTL + layoutOptions.gdiTextFormatFlags = ContentAlignToTextFormat(Owner.TextAlign, Owner.RightToLeft == RightToLeft.Yes); + + + // in 2K and XP++ hide underlined &File unless ALT is pressed + layoutOptions.gdiTextFormatFlags = (Owner.ShowKeyboardCues) ? layoutOptions.gdiTextFormatFlags : layoutOptions.gdiTextFormatFlags | TextFormatFlags.HidePrefix; + + return layoutOptions; + } + + private bool EnsureLayout() { + + if (layoutData == null || parentLayoutData == null || !parentLayoutData.IsCurrent(ParentInternal)) { + PerformLayout(); + return true; + } + return false; + } + + private ButtonBaseAdapter.LayoutData GetLayoutData() { + + currentLayoutOptions = CommonLayoutOptions(); + + if (Owner.TextDirection != ToolStripTextDirection.Horizontal) { + currentLayoutOptions.verticalText = true; + } + + ButtonBaseAdapter.LayoutData data = currentLayoutOptions.Layout(); + return data; + + } + public virtual Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = Size.Empty; + EnsureLayout(); + // we would prefer not to be larger than the winbar itself. + // so we'll ask the ButtonAdapter layout guy what it thinks + // its preferred size should be - and we'll tell it to be no + // bigger than the ToolStrip itself. Note this is "Parent" not + // "Owner" because we care in this instance what we're currently displayed on. + + if (ownerItem != null) { + lastPreferredSize = currentLayoutOptions.GetPreferredSizeCore(constrainingSize); + return lastPreferredSize; + } + Debug.Fail("Why are we here without an owner?"); + return Size.Empty; + } + + + internal void PerformLayout() { + layoutData = GetLayoutData(); + ToolStrip parent = ParentInternal; + if (parent != null) { + parentLayoutData = new ToolStripLayoutData(parent); + } + else { + parentLayoutData = null; + } + } + + internal class ToolStripItemLayoutOptions : ButtonBaseAdapter.LayoutOptions { + Size cachedSize = LayoutUtils.InvalidSize; + Size cachedProposedConstraints = LayoutUtils.InvalidSize; + + // override GetTextSize to provide simple text caching. + protected override Size GetTextSize(Size proposedConstraints) { + if (cachedSize != LayoutUtils.InvalidSize + && (cachedProposedConstraints == proposedConstraints + || cachedSize.Width <= proposedConstraints.Width)) { + return cachedSize; + } + else { + cachedSize = base.GetTextSize(proposedConstraints); + cachedProposedConstraints = proposedConstraints; + } + return cachedSize; + + } + + } + private class ToolStripLayoutData { + private ToolStripLayoutStyle layoutStyle; + private bool autoSize; + private Size size; + + public ToolStripLayoutData(ToolStrip toolStrip) { + this.layoutStyle = toolStrip.LayoutStyle; + this.autoSize = toolStrip.AutoSize; + this.size = toolStrip.Size; + } + public bool IsCurrent(ToolStrip toolStrip) { + if (toolStrip == null) { + return false; + } + return (toolStrip.Size == size && toolStrip.LayoutStyle == layoutStyle && toolStrip.AutoSize == autoSize); + } + } + } + + +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemAlignment.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemAlignment.cs new file mode 100644 index 000000000..3f375ba72 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemAlignment.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + + /// + /// + /// This enum is used to determine alignment of the ToolStripItem on the ToolStrip. + /// + public enum ToolStripItemAlignment { + Left=0x0, + Right=0x1, + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventArgs.cs new file mode 100644 index 000000000..11df9c89d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventArgs.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + public class ToolStripItemClickedEventArgs : EventArgs { + private ToolStripItem clickedItem = null; + + /// + /// + /// This class represents event args a ToolStrip can use when an item has been clicked. + /// + public ToolStripItemClickedEventArgs(ToolStripItem clickedItem) { + + this.clickedItem = clickedItem; + } + + + /// + /// + /// Represents the item that was clicked on the toolStrip. + /// + public ToolStripItem ClickedItem { + get { + return clickedItem; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventHandler.cs new file mode 100644 index 000000000..c25b423c8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemClickedEventHandler.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + + + /// + /// + /// + /// Represents the method that will handle the + /// , , or event of a form, control, or other component. + /// + /// + public delegate void ToolStripItemClickedEventHandler(object sender, ToolStripItemClickedEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemCollection.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemCollection.cs new file mode 100644 index 000000000..612ac034f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemCollection.cs @@ -0,0 +1,558 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Drawing.Design; + using System.Diagnostics; + using System.Windows.Forms.Layout; + using System.Drawing; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// Summary description for ToolStripItemCollection. + /// + [ + Editor("System.Windows.Forms.Design.ToolStripCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + ListBindable(false), + UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows) + ] + public class ToolStripItemCollection : ArrangedElementCollection, IList { + + private ToolStrip owner; + private bool itemsCollection; + private bool isReadOnly = false; + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways. + private int lastAccessedIndex = -1; + + + internal ToolStripItemCollection(ToolStrip owner, bool itemsCollection) : this(owner, itemsCollection, /*isReadOnly=*/false){ + } + + + /// + /// [To be supplied.] + /// + internal ToolStripItemCollection(ToolStrip owner, bool itemsCollection, bool isReadOnly) { + this.owner = owner; + this.itemsCollection = itemsCollection; + this.isReadOnly = isReadOnly; + } + + /// + /// + /// [To be supplied.] + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public ToolStripItemCollection(ToolStrip owner, ToolStripItem[] value) { + if (owner == null) { + throw new ArgumentNullException("owner"); + } + + this.owner = owner; + AddRange(value); + } + + /// + /// + /// + /// + public new virtual ToolStripItem this[int index] { + get { + return (ToolStripItem)(InnerList[index]); + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual ToolStripItem this[string key] { + get { + // We do not support null and empty string as valid keys. + if ((key == null) || (key.Length == 0)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return (ToolStripItem)InnerList[index]; + } + else { + return null; + } + + } + } + + + public ToolStripItem Add(string text) { + return Add(text,null,null); + } + public ToolStripItem Add(Image image) { + return Add(null,image,null); + } + public ToolStripItem Add(string text, Image image) { + return Add(text,image,null); + } + public ToolStripItem Add(string text, Image image, EventHandler onClick) { + ToolStripItem item = owner.CreateDefaultItem(text,image,onClick); + Add(item); + return item; + } + + /// + /// + /// [To be supplied.] + /// + public int Add(ToolStripItem value) { + CheckCanAddOrInsertItem(value); + + SetOwner(value); + int retVal = InnerList.Add(value); + if (itemsCollection && owner != null) { + owner.OnItemAddedInternal(value); + owner.OnItemAdded(new ToolStripItemEventArgs(value)); + } + return retVal; + + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ToolStripItem[] toolStripItems) { + if (toolStripItems == null) { + throw new ArgumentNullException("toolStripItems"); + } + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + + // ToolStripDropDown will look for PropertyNames.Items to determine if it needs + // to resize itself. + using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items)) { + for (int i = 0; i < toolStripItems.Length; i++) { + this.Add(toolStripItems[i]); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ToolStripItemCollection toolStripItems) { + if (toolStripItems == null) { + throw new ArgumentNullException("toolStripItems"); + } + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + + // ToolStripDropDown will look for PropertyNames.Items to determine if it needs + // to resize itself. + using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items)) { + int currentCount = toolStripItems.Count; + for (int i = 0; i < currentCount; i++) { + this.Add(toolStripItems[i]); + } + } + + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ToolStripItem value) { + return InnerList.Contains(value); + } + + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + if (Count == 0) { + return; + } + ToolStripOverflow overflow = null; + + if (owner != null && !owner.IsDisposingItems) { + owner.SuspendLayout(); + overflow = owner.GetOverflow(); + if (overflow != null) { + overflow.SuspendLayout(); + } + } + try { + while (Count != 0) { + RemoveAt(Count - 1); + } + } + finally { + if (overflow != null) { + overflow.ResumeLayout(false); + } + if (owner != null && !owner.IsDisposingItems) { + owner.ResumeLayout(); + } + } + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + private void CheckCanAddOrInsertItem(ToolStripItem value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + + + ToolStripDropDown dropDown = owner as ToolStripDropDown; + if (dropDown != null) { + // VSWhidbey 436973: if we're on a dropdown, we can only add non-control host items + // as we dont want anything on a dropdown to get keyboard messages in the Internet. + + if (dropDown.OwnerItem == value) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCircularReference)); + } + + // VSWhidbey 496526: ScrollButton is the only allowed control host as it correctly eats key messages. + if (value is ToolStripControlHost && !(value is System.Windows.Forms.ToolStripScrollButton)) { + if (dropDown.IsRestrictedWindow) { + IntSecurity.AllWindows.Demand(); + } + } + } + + } + + /// + /// + /// Searches for Items by their Name property, builds up an array + /// of all the controls that match. + /// + /// + public ToolStripItem[] Find(string key, bool searchAllChildren) { + if ((key == null) || (key.Length == 0)) { + throw new System.ArgumentNullException("key", SR.GetString(SR.FindKeyMayNotBeEmptyOrNull)); + } + + ArrayList foundItems = FindInternal(key, searchAllChildren, this, new ArrayList()); + + // Make this a stongly typed collection. + ToolStripItem[] stronglyTypedFoundItems = new ToolStripItem[foundItems.Count]; + foundItems.CopyTo(stronglyTypedFoundItems, 0); + + return stronglyTypedFoundItems; + } + + /// + /// Searches for Items by their Name property, builds up an array list + /// of all the items that match. + /// + /// + /// + private ArrayList FindInternal(string key, bool searchAllChildren, ToolStripItemCollection itemsToLookIn, ArrayList foundItems) + { + if ((itemsToLookIn == null) || (foundItems == null)) { + return null; // + } + + try + { + for (int i = 0; i < itemsToLookIn.Count; i++) + { + if (itemsToLookIn[i] == null) + { + continue; + } + + if (WindowsFormsUtils.SafeCompareStrings(itemsToLookIn[i].Name, key, /* ignoreCase = */ true)) + { + foundItems.Add(itemsToLookIn[i]); + } + } + + + // Optional recurive search for controls in child collections. + + if (searchAllChildren) + { + for (int j = 0; j < itemsToLookIn.Count; j++) + { + ToolStripDropDownItem item = itemsToLookIn[j] as ToolStripDropDownItem; + if (item == null) + { + continue; + } + if (item.HasDropDownItems) + { + // if it has a valid child collecion, append those results to our collection + foundItems = FindInternal(key, searchAllChildren, item.DropDownItems, foundItems); + } + } + } + } + catch (Exception e) + { + // VSWHIDBEY 80122 make sure we deal with non-critical failures gracefully. + if (ClientUtils.IsCriticalException(e)) + { + throw; + } + } + + return foundItems; + } + + + + public override bool IsReadOnly { get { return this.isReadOnly; }} + + void IList.Clear() { Clear(); } + bool IList.IsFixedSize { get { return InnerList.IsFixedSize; }} + bool IList.Contains(object value) { return InnerList.Contains(value); } + void IList.RemoveAt(int index) { RemoveAt(index); } + void IList.Remove(object value) { Remove(value as ToolStripItem); } + int IList.Add(object value) { return Add(value as ToolStripItem); } + int IList.IndexOf(object value) { return IndexOf(value as ToolStripItem); } + void IList.Insert(int index, object value) { Insert(index, value as ToolStripItem); } + + /// + /// + object IList.this[int index] { + get { return InnerList[index]; } + set { throw new NotSupportedException(SR.GetString(SR.ToolStripCollectionMustInsertAndRemove)); /* InnerList[index] = value; */ } + } + /// + /// + /// [To be supplied.] + /// + public void Insert(int index, ToolStripItem value) { + CheckCanAddOrInsertItem(value); + SetOwner(value); + InnerList.Insert(index, value); + if (itemsCollection && owner != null) { + if (owner.IsHandleCreated){ + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + } + else { + // next time we fetch the preferred size, recalc it. + CommonProperties.xClearPreferredSizeCache(owner); + } + owner.OnItemAddedInternal(value); + owner.OnItemAdded(new ToolStripItemEventArgs(value)); + } + + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ToolStripItem value) { + return InnerList.IndexOf(value); + } + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if ((key == null) || (key.Length == 0)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// Do proper cleanup of ownership, etc. + /// + private void OnAfterRemove(ToolStripItem item) { + if (itemsCollection) { + ToolStrip parent = null; + if (item != null) { + parent = item.ParentInternal; + item.SetOwner(null); + } + + if (owner != null) { + owner.OnItemRemovedInternal(item); + + if (!owner.IsDisposingItems) { + ToolStripItemEventArgs e = new ToolStripItemEventArgs(item); + owner.OnItemRemoved(e); + + // VSWhidbey 505129 + // dont fire the ItemRemoved event for Overflow + // it would fire constantly.... instead clear any state if the item + // is really being removed from the master collection. + if (parent != null && parent != owner) { + parent.OnItemVisibleChanged(e, /*performLayout*/false); + } + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(ToolStripItem value) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + InnerList.Remove(value); + OnAfterRemove(value); + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + ToolStripItem item = null; + if (index < Count && index >= 0) { + item = (ToolStripItem)(InnerList[index]); + } + InnerList.RemoveAt(index); + OnAfterRemove(item); + } + + /// + /// + /// Removes the child item with the specified key. + /// + public virtual void RemoveByKey(string key) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly)); + } + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(ToolStripItem[] array, int index) { + InnerList.CopyTo(array, index); + } + + // + internal void MoveItem(ToolStripItem value) { + if (value.ParentInternal != null) { + int indexOfItem = value.ParentInternal.Items.IndexOf(value); + if (indexOfItem >= 0) { + value.ParentInternal.Items.RemoveAt(indexOfItem); + } + } + Add(value); + + } + + internal void MoveItem(int index, ToolStripItem value) { + + // if moving to the end - call add instead. + if (index == this.Count) { + MoveItem(value); + return; + } + + if (value.ParentInternal != null) { + int indexOfItem = value.ParentInternal.Items.IndexOf(value); + + if (indexOfItem >= 0) { + value.ParentInternal.Items.RemoveAt(indexOfItem); + + if ((value.ParentInternal == owner) && (index > indexOfItem)) { + index--; + } + } + } + Insert(index,value); + + } + + private void SetOwner(ToolStripItem item) { + + if (itemsCollection) { + + if (item != null) { + if (item.Owner != null) { + item.Owner.Items.Remove(item); + } + + item.SetOwner(owner); + if (item.Renderer != null) { + item.Renderer.InitializeItem(item); + } + } + } + } + + } + + +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemDisplayStyle.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemDisplayStyle.cs new file mode 100644 index 000000000..f0866d617 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemDisplayStyle.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + /// + /// + /// Specifies what to render for the ToolStripItem + /// + public enum ToolStripItemDisplayStyle { + /// + None = 0x0000, + /// + Text = 0x0001, // 0001 + /// + Image = 0x0002, // 0010 + /// + ImageAndText = 0x0003, // 0011 + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemEventArgs.cs new file mode 100644 index 000000000..b10dfbc30 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemEventArgs.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + public class ToolStripItemEventArgs : EventArgs { + + private ToolStripItem item; + + /// + public ToolStripItemEventArgs(ToolStripItem item) { + this.item = item; + } + + /// + public ToolStripItem Item { + get { + return item; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemEventHandler.cs new file mode 100644 index 000000000..99502302a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemEventHandler.cs @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + public delegate void ToolStripItemEventHandler(object sender, ToolStripItemEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemEventType.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemEventType.cs new file mode 100644 index 000000000..286c6f693 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemEventType.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + + + /// + /// These methods allow the ToolStrip to route events + /// to the winbar item. Since a ToolStrip is not a ToolStripItem, + /// it cannot directly call OnPaint. + /// + internal enum ToolStripItemEventType { + Paint, + LocationChanged, + MouseUp, + MouseDown, + MouseMove, + MouseEnter, + MouseLeave, + MouseHover, + Click, + DoubleClick + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventArgs.cs new file mode 100644 index 000000000..8bb1fadc5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventArgs.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + using System.Drawing.Imaging; + + /// + /// + public class ToolStripItemImageRenderEventArgs : ToolStripItemRenderEventArgs { + + private Image image = null; + private Rectangle imageRectangle = Rectangle.Empty; + private bool shiftOnPress = false; + private ImageAttributes imageAttr = null; + + public ToolStripItemImageRenderEventArgs(Graphics g, ToolStripItem item, Rectangle imageRectangle) : base(g, item) { + this.image = (item.RightToLeftAutoMirrorImage && (item.RightToLeft == RightToLeft.Yes)) ? item.MirroredImage : item.Image; + this.imageRectangle = imageRectangle; + + } + /// + /// + /// This class represents all the information to render the winbar + /// + public ToolStripItemImageRenderEventArgs(Graphics g, ToolStripItem item, Image image, Rectangle imageRectangle) : base(g, item) { + this.image = image; + this.imageRectangle = imageRectangle; + } + + + /// + /// + /// the string to draw + /// + public Image Image { + get { + return image; + } + } + + /// + /// + /// the rectangle to draw the Image in + /// + public Rectangle ImageRectangle { + get { + return imageRectangle; + } + } + + // not public as it currently pertains to button & system renderer. + internal bool ShiftOnPress { + get { return shiftOnPress; } + set { shiftOnPress = value; } + } + + // not public as it currently pertains to ToolStripRenderer. + internal ImageAttributes ImageAttributes { + get { return imageAttr; } + set { imageAttr = value; } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventHandler.cs new file mode 100644 index 000000000..957d2ad32 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemImageRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of a winbar item is being rendered + /// + /// + public delegate void ToolStripItemImageRenderEventHandler(object sender, ToolStripItemImageRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemImageScaling.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemImageScaling.cs new file mode 100644 index 000000000..570abcaa2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemImageScaling.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + /// + public enum ToolStripItemImageScaling { + /// + None, + /// + SizeToFit + + }; +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemOverflow.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemOverflow.cs new file mode 100644 index 000000000..13f56c396 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemOverflow.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + + /// + /// + /// Summary of ToolStripItemOverflow. + /// This enum is used to determine placement of the ToolStripItem on the ToolStrip. + /// + public enum ToolStripItemOverflow { + /// + Never, // on the main winbar itself, + /// + Always, // on the overflow window + /// + AsNeeded // DEFAULT try for main, overflow as necessary + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemPlacement.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemPlacement.cs new file mode 100644 index 000000000..7d46d017d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemPlacement.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + + /// + /// + /// ToolStripItemPlacement + /// This enum represents the current layout of the ToolStripItem. + /// + public enum ToolStripItemPlacement { + /// + Main, // in the main winbar itself + /// + Overflow, // in the overflow window + /// + None // either offscreen or visible == false so we didn't lay it out + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventArgs.cs new file mode 100644 index 000000000..7e5477826 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventArgs.cs @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + /// + /// + public class ToolStripItemRenderEventArgs : EventArgs { + + private ToolStripItem item = null; + private Graphics graphics = null; + + /// + /// + /// This class represents all the information to render the winbar + /// + public ToolStripItemRenderEventArgs(Graphics g, ToolStripItem item) { + this.item = item; + this.graphics = g; + } + + + /// + /// + /// the graphics object to draw with + /// + public Graphics Graphics { + get { + return graphics; + } + } + + /// + /// + /// the item to draw + /// + public ToolStripItem Item { + get { + return item; + } + } + + /// + /// + /// The toolstrip the item is currently parented to + /// + public ToolStrip ToolStrip { + get { + return item.ParentInternal; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventHandler.cs new file mode 100644 index 000000000..e7dc3619a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of a winbar item is being rendered + /// + /// + public delegate void ToolStripItemRenderEventHandler(object sender, ToolStripItemRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemStates.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemStates.cs new file mode 100644 index 000000000..2cd24a0f1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemStates.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + + internal enum ToolStripItemStates { + None = 0x00000000, + Selected = 0x00000001, + Focused = 0x00000002, + Hot = 0x00000004, + Pressed = 0x00000008, + Disabled = 0x00000010 + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventArgs.cs new file mode 100644 index 000000000..363f63507 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventArgs.cs @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Windows.Forms.ButtonInternal; + + /// + /// + /// This class represents all the information to render the winbar + /// + public class ToolStripItemTextRenderEventArgs : ToolStripItemRenderEventArgs { + + private string text = null; + private Rectangle textRectangle = Rectangle.Empty; + private Color textColor = SystemColors.ControlText; + private Font textFont = null; + private ContentAlignment textAlignment; + private ToolStripTextDirection textDirection = ToolStripTextDirection.Horizontal; + private TextFormatFlags textFormat = TextFormatFlags.Default; + private Color defaultTextColor = SystemColors.ControlText; + private bool textColorChanged = false; + + /// + /// + /// This class represents all the information to render the winbar + /// + public ToolStripItemTextRenderEventArgs(Graphics g, ToolStripItem item, string text, Rectangle textRectangle, Color textColor, Font textFont, TextFormatFlags format) : base(g, item) { + this.text = text; + this.textRectangle = textRectangle; + this.defaultTextColor = textColor; + this.textFont = textFont; + this.textAlignment = item.TextAlign; + this.textFormat = format; + textDirection = item.TextDirection; + + } + + + /// + /// + /// This class represents all the information to render the winbar + /// + public ToolStripItemTextRenderEventArgs(Graphics g, ToolStripItem item, string text, Rectangle textRectangle, Color textColor, Font textFont, ContentAlignment textAlign) : base(g, item) { + this.text = text; + this.textRectangle = textRectangle; + this.defaultTextColor = textColor; + this.textFont = textFont; + this.textFormat = ToolStripItemInternalLayout.ContentAlignToTextFormat(textAlign, item.RightToLeft == RightToLeft.Yes); + + // in 2K and XP++ hide underlined &File unless ALT is pressed + this.textFormat = (item.ShowKeyboardCues) ? textFormat : textFormat | TextFormatFlags.HidePrefix; + textDirection = item.TextDirection; + } + + + /// + /// + /// the string to draw + /// + public string Text { + get { + return text; + } + set { + text = value; + } + } + + /// + /// + /// the color to draw the text + /// + public Color TextColor { + get { + if (textColorChanged) { + return textColor; + } + return DefaultTextColor; + } + set { + textColor = value; + textColorChanged=true; + } + } + + // + + + internal Color DefaultTextColor { + get { + return defaultTextColor; + } + set { + defaultTextColor = value; + } + } + + /// + /// + /// the font to draw the text + /// + public Font TextFont { + get { + return textFont; + } + set { + textFont = value; + } + } + + /// + /// + /// the rectangle to draw the text in + /// + public Rectangle TextRectangle { + get { + return textRectangle; + } + set { + textRectangle = value; + } + } + + /// + /// + /// the rectangle to draw the text in + /// + public TextFormatFlags TextFormat { + get { + return textFormat; + } + set { + textFormat = value; + } + } + + + /// + /// + /// the angle at which the text should be drawn in tenths of degrees. + /// + public ToolStripTextDirection TextDirection { + get { + return textDirection; + } + set { + textDirection = value; + } + } + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventHandler.cs new file mode 100644 index 000000000..880ddd18a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripItemTextRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of a winbar item is being rendered + /// + /// + public delegate void ToolStripItemTextRenderEventHandler(object sender, ToolStripItemTextRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripLabel.cs b/WindowsForms/Managed/System/WinForms/ToolStripLabel.cs new file mode 100644 index 000000000..1cd12c92b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripLabel.cs @@ -0,0 +1,439 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.ButtonInternal; + using System.Security.Permissions; + using System.Security; + using System.Windows.Forms.Design; + + /// + /// + /// A non selectable winbar item + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip)] + public class ToolStripLabel : ToolStripItem { + + private LinkBehavior linkBehavior = LinkBehavior.SystemDefault; + private bool isLink = false, linkVisited = false; + + private Color linkColor = Color.Empty; + private Color activeLinkColor = Color.Empty; + private Color visitedLinkColor = Color.Empty; + private Font hoverLinkFont, linkFont; + private Cursor lastCursor; + + + + + /// + /// + /// A non selectable winbar item + /// + public ToolStripLabel() { + } + public ToolStripLabel(string text):base(text,null,null) { + } + public ToolStripLabel(Image image):base(null,image,null) { + } + public ToolStripLabel(string text, Image image):base(text,image,null) { + } + public ToolStripLabel(string text, Image image, bool isLink):this(text,image,isLink, null) { + } + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public ToolStripLabel(string text, Image image, bool isLink, EventHandler onClick):this(text,image,isLink,onClick,null) { + } + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public ToolStripLabel(string text, Image image, bool isLink, EventHandler onClick, string name) : base(text,image,onClick,name) { + IsLink = isLink; + } + + /// + public override bool CanSelect { + get { return (IsLink || DesignMode); } + } + + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripLabelIsLinkDescr) + ] + public bool IsLink { + get { + return isLink; + } + set { + if (isLink != value) { + isLink = value; + Invalidate(); + } + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripLabelActiveLinkColorDescr) + ] + public Color ActiveLinkColor { + get { + if (activeLinkColor.IsEmpty) { + return IEActiveLinkColor; + } + else { + return activeLinkColor; + } + } + set { + if (activeLinkColor != value) { + activeLinkColor = value; + Invalidate(); + } + } + } + private Color IELinkColor { + get { + return LinkUtilities.IELinkColor; + } + } + + private Color IEActiveLinkColor { + get { + return LinkUtilities.IEActiveLinkColor; + } + } + private Color IEVisitedLinkColor { + get { + return LinkUtilities.IEVisitedLinkColor; + } + } + + [ + DefaultValue(LinkBehavior.SystemDefault), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripLabelLinkBehaviorDescr) + ] + public LinkBehavior LinkBehavior { + get { + return linkBehavior; + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)LinkBehavior.SystemDefault, (int)LinkBehavior.NeverUnderline)) + { + throw new InvalidEnumArgumentException("LinkBehavior", (int)value, typeof(LinkBehavior)); + } + if (linkBehavior != value) { + linkBehavior = value; + InvalidateLinkFonts(); + Invalidate(); + } + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripLabelLinkColorDescr) + ] + public Color LinkColor { + get { + if (linkColor.IsEmpty) { + return IELinkColor; + } + else { + return linkColor; + } + } + set { + if (linkColor != value) { + linkColor = value; + Invalidate(); + } + } + } + + [ + DefaultValue(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripLabelLinkVisitedDescr) + ] + public bool LinkVisited { + get { + return linkVisited; + } + set { + if (linkVisited != value) { + linkVisited = value; + Invalidate(); + } + } + } + + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.ToolStripLabelVisitedLinkColorDescr) + ] + public Color VisitedLinkColor { + get { + if (visitedLinkColor.IsEmpty) { + return IEVisitedLinkColor; + } + else { + return visitedLinkColor; + } + } + set { + if (visitedLinkColor != value) { + visitedLinkColor = value; + Invalidate(); + } + } + } + + /// + /// + /// Invalidates the current set of fonts we use when painting + /// links. The fonts will be recreated when needed. + /// + private void InvalidateLinkFonts() { + + if (linkFont != null) { + linkFont.Dispose(); + } + + if (hoverLinkFont != null && hoverLinkFont != linkFont) { + hoverLinkFont.Dispose(); + } + + linkFont = null; + hoverLinkFont = null; + } + + protected override void OnFontChanged(EventArgs e) { + InvalidateLinkFonts(); + base.OnFontChanged(e); + } + + + protected override void OnMouseEnter(EventArgs e) { + if (IsLink) { + ToolStrip parent = this.Parent; + if (parent != null) { + lastCursor = parent.Cursor; + parent.Cursor = Cursors.Hand; + } + } + base.OnMouseEnter(e); + + } + + protected override void OnMouseLeave(EventArgs e) { + if (IsLink) { + ToolStrip parent = this.Parent; + if (parent != null) { + parent.Cursor = lastCursor; + } + } + base.OnMouseLeave(e); + + } + + private void ResetActiveLinkColor() + { + ActiveLinkColor = IEActiveLinkColor; + } + + private void ResetLinkColor() + { + LinkColor = IELinkColor; + } + + private void ResetVisitedLinkColor() + { + VisitedLinkColor = IEVisitedLinkColor; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeActiveLinkColor() { + return !activeLinkColor.IsEmpty; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeLinkColor() { + return !linkColor.IsEmpty; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private bool ShouldSerializeVisitedLinkColor() { + return !visitedLinkColor.IsEmpty; + } + + + + /// + /// Creates an instance of the object that defines how image and text + /// gets laid out in the ToolStripItem + /// + internal override ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripLabelLayout(this); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripLabelAccessibleObject(this); + } + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + if (this.Owner != null) { + ToolStripRenderer renderer = this.Renderer; + + renderer.DrawLabelBackground(new ToolStripItemRenderEventArgs(e.Graphics, this)); + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(e.Graphics, this, InternalLayout.ImageRectangle)); + } + PaintText(e.Graphics); + } + } + + internal void PaintText(Graphics g) { + ToolStripRenderer renderer = this.Renderer; + + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + Font font = this.Font; + Color textColor = this.ForeColor; + if (IsLink) { + LinkUtilities.EnsureLinkFonts(font, this.LinkBehavior, ref this.linkFont, ref this.hoverLinkFont); + + if (this.Pressed) { + font = hoverLinkFont; + textColor = this.ActiveLinkColor; + } + else if (this.Selected) { + font = hoverLinkFont; + textColor = (this.LinkVisited) ? this.VisitedLinkColor : this.LinkColor; + } + else { + font = linkFont; + textColor = (this.LinkVisited) ? this.VisitedLinkColor : this.LinkColor; + } + } + Rectangle textRect = InternalLayout.TextRectangle; + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, this.Text, textRect, textColor, font, InternalLayout.TextFormat)); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // checking IsMnemonic is not necessary - toolstrip does this for us. + if (ParentInternal != null) { + if (!CanSelect) { + ParentInternal.SetFocusUnsafe(); + ParentInternal.SelectNextToolStripItem(this, /*forward=*/true); + } + else { + FireEvent(ToolStripItemEventType.Click); + } + return true; + + } + return false; + } + + + [System.Runtime.InteropServices.ComVisible(true)] + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class ToolStripLabelAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripLabel ownerItem = null; + + public ToolStripLabelAccessibleObject(ToolStripLabel ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + public override string DefaultAction { + get { + if (ownerItem.IsLink) { + return SR.GetString(SR.AccessibleActionClick); + } + else { + return string.Empty; + } + } + } + + public override void DoDefaultAction() { + if (ownerItem.IsLink) { + base.DoDefaultAction(); + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_TextControlTypeId; + } + else if (propertyID == NativeMethods.UIA_LegacyIAccessibleStatePropertyId) { + return this.State; + } + } + + return base.GetPropertyValue(propertyID); + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return (ownerItem.IsLink) ? AccessibleRole.Link : AccessibleRole.StaticText; + } + } + + public override AccessibleStates State { + get { + return base.State | AccessibleStates.ReadOnly; + } + } + } + /// + /// This class performs internal layout for the "split button button" portion of a split button. + /// Its main job is to make sure the inner button has the same parent as the split button, so + /// that layout can be performed using the correct graphics context. + /// + private class ToolStripLabelLayout : ToolStripItemInternalLayout { + + ToolStripLabel owner; + + public ToolStripLabelLayout(ToolStripLabel owner) : base(owner) { + this.owner = owner; + } + + protected override ToolStripItemLayoutOptions CommonLayoutOptions() { + ToolStripItemLayoutOptions layoutOptions = base.CommonLayoutOptions(); + layoutOptions.borderSize = 0; + return layoutOptions; + } + } + + } + +} + + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripLayoutStyle.cs b/WindowsForms/Managed/System/WinForms/ToolStripLayoutStyle.cs new file mode 100644 index 000000000..4acf2f3ed --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripLayoutStyle.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + + /// + public enum ToolStripLayoutStyle { + StackWithOverflow = 0x0, + HorizontalStackWithOverflow = 0x1, + VerticalStackWithOverflow = 0x2, + Flow = 0x3, + Table = 0x4 + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventArgs.cs new file mode 100644 index 000000000..f09934f2d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventArgs.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Drawing; + + /// + /// + /// + /// ToolStripLocationCancelEventArgs provides Arguments for the Cancelable LocationChanging Event. + /// event. + /// + /// + internal class ToolStripLocationCancelEventArgs : CancelEventArgs { + + private Point newLocation; + + + /// + /// + /// + /// Initializes a new instance of the ToolStripLocationCancelEventArgs with cancel value. + /// + /// + public ToolStripLocationCancelEventArgs(Point newLocation, bool value) : base(value) { + + this.newLocation = newLocation; + + } + + /// + /// + /// + /// Returns the New Location of the ToolStrip. + /// + /// + public Point NewLocation { + get { + return this.newLocation; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventHandler.cs new file mode 100644 index 000000000..43767290d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripLocationCancelEventHandler.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.ComponentModel; + using System.Diagnostics; + using System; + + /// + /// + /// Represents the method that will handle the event raised when canceling an + /// OnLocationChanging event for ToolStrips. + /// + internal delegate void ToolStripLocationCancelEventHandler(object sender, ToolStripLocationCancelEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripManager.cs b/WindowsForms/Managed/System/WinForms/ToolStripManager.cs new file mode 100644 index 000000000..973d96328 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripManager.cs @@ -0,0 +1,2293 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + using System.Windows.Forms.VisualStyles; + using System.ComponentModel; + using System.Collections; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + using System.Drawing; + using Microsoft.Win32; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + + /// + public sealed class ToolStripManager { + + // WARNING: ThreadStatic initialization happens only on the first thread at class CTOR time. + // use InitializeThread mechanism to initialize ThreadStatic members + [ThreadStatic] + private static ClientUtils.WeakRefCollection toolStripWeakArrayList; + [ThreadStatic] + private static ClientUtils.WeakRefCollection toolStripPanelWeakArrayList; + [ThreadStatic] + private static bool initialized; + + private static Font defaultFont; + private static ConcurrentDictionary defaultFontCache = new ConcurrentDictionary(); + + // WARNING: When subscribing to static event handlers - make sure you unhook from them + // otherwise you can leak USER objects on process shutdown. + // Consider: use WeakRefCollection + [ThreadStatic] + private static Delegate[] staticEventHandlers; + private const int staticEventDefaultRendererChanged = 0; + private const int staticEventCount = 1; + + private static object internalSyncObject = new object(); + + private static int currentDpi = DpiHelper.DeviceDpi; + + private static void InitalizeThread() { + if (!initialized) { + initialized = true; + currentRendererType = ProfessionalRendererType; + } + } + private ToolStripManager() { + } + + static ToolStripManager() { + SystemEvents.UserPreferenceChanging += new UserPreferenceChangingEventHandler(OnUserPreferenceChanging); + } + + internal static Font DefaultFont { + get { + Font sysFont = null; + + // We need to cache the default fonts for the different DPIs. + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + int dpi = CurrentDpi; + + Font retFont = null; + if (defaultFontCache.TryGetValue(dpi, out retFont) == false || retFont == null) { + // default to menu font + sysFont = SystemInformation.GetMenuFontForDpi(dpi); + if (sysFont != null) { + // ensure font is in pixels so it displays properly in the property grid at design time. + if (sysFont.Unit != GraphicsUnit.Point) { + retFont = ControlPaint.FontInPoints(sysFont); + sysFont.Dispose(); + } + else { + retFont = sysFont; + } + defaultFontCache[dpi] = retFont; + } + } + return retFont; + } + else { + Font retFont = defaultFont; // threadsafe local reference + + if (retFont == null) { + lock (internalSyncObject) { + // double check the defaultFont after the lock. + retFont = defaultFont; + + if (retFont == null) { + // default to menu font + sysFont = SystemFonts.MenuFont; + if (sysFont == null) { + // ...or to control font if menu font unavailable + sysFont = Control.DefaultFont; + } + if (sysFont != null) { + // ensure font is in pixels so it displays properly in the property grid at design time. + if (sysFont.Unit != GraphicsUnit.Point) { + defaultFont = ControlPaint.FontInPoints(sysFont); + retFont = defaultFont; + sysFont.Dispose(); + } + else { + defaultFont = sysFont; + retFont = defaultFont; + } + } + return retFont; + } + } + } + return retFont; + } + } + } + + internal static int CurrentDpi { + get { + return currentDpi; + } + set { + currentDpi = value; + } + } + + internal static ClientUtils.WeakRefCollection ToolStrips { + get { + if (toolStripWeakArrayList == null) { + toolStripWeakArrayList = new ClientUtils.WeakRefCollection(); + } + return toolStripWeakArrayList; + } + } + + + ///Static events only!!! + private static void AddEventHandler(int key, Delegate value) { + lock (internalSyncObject) { + if (staticEventHandlers == null) { + staticEventHandlers = new Delegate[staticEventCount]; + } + staticEventHandlers[key] = Delegate.Combine(staticEventHandlers[key], value); + } + } + + + /// + /// + /// Find a toolstrip in the weak ref arraylist, return null if nothing was found + /// + [UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)] + public static ToolStrip FindToolStrip(string toolStripName) { + ToolStrip result = null; + for (int i = 0; i < ToolStrips.Count; i++) { + // is this the right string comparaison? + if (ToolStrips[i] != null && String.Equals(((ToolStrip)ToolStrips[i]).Name, toolStripName, StringComparison.Ordinal)) { + result = (ToolStrip)ToolStrips[i]; + break; + } + } + return result; + } + + /// + /// + /// Find a toolstrip in the weak ref arraylist, return null if nothing was found + /// + [UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)] + internal static ToolStrip FindToolStrip(Form owningForm, string toolStripName) + { + ToolStrip result = null; + for (int i = 0; i < ToolStrips.Count; i++) + { + // is this the right string comparaison? + if (ToolStrips[i] != null && String.Equals(((ToolStrip)ToolStrips[i]).Name, toolStripName, StringComparison.Ordinal)) + { + result = (ToolStrip)ToolStrips[i]; + if (result.FindForm() == owningForm) { + break; + } + } + } + return result; + } + + + private static bool CanChangeSelection(ToolStrip start, ToolStrip toolStrip) { + if (toolStrip == null) { + Debug.Fail("passed in bogus toolstrip, why?"); + return false; + } + bool canChange = toolStrip.TabStop == false && + toolStrip.Enabled && + toolStrip.Visible && + !toolStrip.IsDisposed && + !toolStrip.Disposing && + !toolStrip.IsDropDown && + IsOnSameWindow(start, toolStrip); + + + if (canChange) { + foreach (ToolStripItem item in toolStrip.Items) { + if (item.CanSelect) { + return true; + } + } + } + return false; + } + + private static bool ChangeSelection(ToolStrip start, ToolStrip toolStrip) { + if (toolStrip == null || start == null) { + Debug.Assert(toolStrip != null, "passed in bogus toolstrip, why?"); + Debug.Assert(start != null, "passed in bogus start, why?"); + return false; + } + if (start == toolStrip) { + return false; + } + if (ModalMenuFilter.InMenuMode) { + if (ModalMenuFilter.GetActiveToolStrip() == start) { + ModalMenuFilter.RemoveActiveToolStrip(start); + start.NotifySelectionChange(null); + } + ModalMenuFilter.SetActiveToolStrip(toolStrip); + } + else { + toolStrip.FocusInternal(); + } + // copy over the hwnd that we want to restore focus to on ESC + start.SnapFocusChange(toolStrip); + + toolStrip.SelectNextToolStripItem(null, toolStrip.RightToLeft != RightToLeft.Yes); + return true; + } + + private static Delegate GetEventHandler(int key) { + lock (internalSyncObject) { + if (staticEventHandlers == null) + return null; + else + return (Delegate)staticEventHandlers[key]; + } + } + + + private static bool IsOnSameWindow(Control control1, Control control2) { + return (WindowsFormsUtils.GetRootHWnd(control1).Handle == WindowsFormsUtils.GetRootHWnd(control2).Handle); + } + + internal static bool IsThreadUsingToolStrips() { + return (toolStripWeakArrayList != null && (toolStripWeakArrayList.Count > 0)); + } + + + + + private static void OnUserPreferenceChanging(object sender, UserPreferenceChangingEventArgs e) { + // using changing here so that the cache will be cleared by the time the ToolStrip + // hooks onto the changed event. + + // SPI_SETNONCLIENTMETRICS is put up in WM_SETTINGCHANGE if the Menu font changes. + // this corresponds to UserPreferenceCategory.Window. + if (e.Category == UserPreferenceCategory.Window) { + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + defaultFontCache.Clear(); + } + else { + lock (internalSyncObject) { + defaultFont = null; + } + } + } + } + + internal static void NotifyMenuModeChange(bool invalidateText, bool activationChange) { + bool toolStripPruneNeeded = false; + + // If we've toggled the ShowUnderlines value, we'll need to invalidate + for (int i = 0; i < ToolStrips.Count; i++) { + ToolStrip toolStrip = ToolStrips[i] as ToolStrip; + if (toolStrip == null) { + toolStripPruneNeeded = true; + continue; + } + if (invalidateText) { + toolStrip.InvalidateTextItems(); + } + if (activationChange) { + toolStrip.KeyboardActive = false; + } + } + if (toolStripPruneNeeded) { + PruneToolStripList(); + } + + } + + /// removes dead entries from the toolstrip weak reference collection. + internal static void PruneToolStripList() { + if (toolStripWeakArrayList != null) { + if (toolStripWeakArrayList.Count > 0) { + for (int i = toolStripWeakArrayList.Count - 1; i >= 0; i--) { + if (toolStripWeakArrayList[i] == null) { + toolStripWeakArrayList.RemoveAt(i); + } + } + } + } + } + + /// static events only!!! + private static void RemoveEventHandler(int key, Delegate value) { + lock (internalSyncObject) { + if (staticEventHandlers != null) { + staticEventHandlers[key] = Delegate.Remove(staticEventHandlers[key], value); + } + } + } + + // this is a special version of SelectNextControl which looks for ToolStrips + // that are TabStop = false in TabOrder. This is used from Control+Tab + // handling to swap focus between ToolStrips. + internal static bool SelectNextToolStrip(ToolStrip start, bool forward) { + + if (start == null || start.ParentInternal == null) { + Debug.Assert(start != null, "why is null passed here?"); + return false; + } + + ToolStrip wrappedControl = null; + ToolStrip nextControl = null; + + int startTabIndex = start.TabIndex; + int index = ToolStrips.IndexOf(start); + int totalCount = ToolStrips.Count; + for (int i = 0; i < totalCount; i++) { + index = (forward) ? (index + 1) % totalCount + : (index + totalCount - 1) % totalCount; + + ToolStrip toolStrip = ToolStrips[index] as ToolStrip; + + if (toolStrip == null || + toolStrip == start) { + continue; + } + + int nextControlTabIndex = toolStrip.TabIndex; + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "SELECTNEXTTOOLSTRIP: start: " + startTabIndex.ToString(CultureInfo.CurrentCulture) + " " + start.Name); + // since CanChangeSelection can iterate through all the items in a toolstrip, + // defer the checking until we think we've got a viable TabIndex candidate. + // this brings it to O(n+m) instead of O(n*m) where n is # toolstrips & m is avg number + // items/toolstrip + if (forward) { + if (nextControlTabIndex >= startTabIndex && CanChangeSelection(start, toolStrip)) { + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "FORWARD considering selection " + toolStrip.Name + " " + toolStrip.TabIndex.ToString(CultureInfo.CurrentCulture)); + if (nextControl == null) { + nextControl = toolStrip; + } + else if (toolStrip.TabIndex < nextControl.TabIndex) { + // we want to pick a larger index, but one that's + // closest to the start tab index. + nextControl = toolStrip; + } + } + else if (((wrappedControl == null) || (toolStrip.TabIndex < wrappedControl.TabIndex)) + && CanChangeSelection(start, toolStrip)) { + // we've found a candidate for wrapping (the one with the smallest tab index in the collection) + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "\tFORWARD new wrap candidate " + toolStrip.Name); + wrappedControl = toolStrip; + } + } + else { + if (nextControlTabIndex <= startTabIndex && CanChangeSelection(start, toolStrip)) { + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "\tREVERSE selecting " + toolStrip.Name); + if (nextControl == null) { + nextControl = toolStrip; + } + else if (toolStrip.TabIndex > nextControl.TabIndex) { + // we want to pick a smaller index, but one that's + // closest to the start tab index. + nextControl = toolStrip; + } + } + else if (((wrappedControl == null) || (toolStrip.TabIndex > wrappedControl.TabIndex)) + && CanChangeSelection(start, toolStrip)) { + // we've found a candidate for wrapping (the one with the largest tab index in the collection) + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "\tREVERSE new wrap candidate " + toolStrip.Name); + + wrappedControl = toolStrip; + } + else { + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "\tREVERSE skipping wrap candidate " + toolStrip.Name + toolStrip.TabIndex.ToString(CultureInfo.CurrentCulture)); + + } + + } + if (nextControl != null + && Math.Abs(nextControl.TabIndex - startTabIndex) <= 1) { + // if we've found a valid candidate AND it's within 1 + // then bail, we've found something close enough. + break; + } + } + if (nextControl != null) { + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "SELECTING " + nextControl.Name); + return ChangeSelection(start, nextControl); + + } + else if (wrappedControl != null) { + Debug.WriteLineIf(ToolStrip.ControlTabDebug.TraceVerbose, "WRAPPING " + wrappedControl.Name); + + return ChangeSelection(start, wrappedControl); + } + return false; + } + + + /// ============================================================================ + /// BEGIN task specific functions. Since ToolStripManager is used + /// for Painting, Merging and Rafting, and who knows what else in the future + /// the following properties/methods/events are organized in regions + /// alphabetically by task + /// ---------------------------------------------------------------------------- + + /// + /// ToolStripManager Default Renderer + /// + #region DefaultRenderer + + /// These are thread static because we want separate instances + /// for each thread. We dont want to guarantee thread safety + /// and dont want to have to take locks in painting code. + [ThreadStatic] + private static ToolStripRenderer defaultRenderer; + + // types cached for perf. + internal static Type SystemRendererType = typeof(ToolStripSystemRenderer); + internal static Type ProfessionalRendererType = typeof(ToolStripProfessionalRenderer); + private static bool visualStylesEnabledIfPossible = true; + + [ThreadStatic] + private static Type currentRendererType; + + private static Type CurrentRendererType { + get { + InitalizeThread(); + return currentRendererType; + } + set { + currentRendererType = value; + } + } + + private static Type DefaultRendererType { + get { + return ProfessionalRendererType; + } + } + + + /// + /// the default renderer for the thread. When ToolStrip.RenderMode is set to manager - this + /// is the property used. + /// + + public static ToolStripRenderer Renderer { + get { + if (defaultRenderer == null) { + defaultRenderer = CreateRenderer(RenderMode); + } + return defaultRenderer; + } + [UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)] + set { + /// SECREVIEW in InternetZone, use individual ToolStrip.Renderer property rather than global one. + + if (defaultRenderer != value) { + CurrentRendererType = (value == null) ? DefaultRendererType : value.GetType(); + defaultRenderer = value; + + EventHandler handler = (EventHandler)GetEventHandler(staticEventDefaultRendererChanged); + + if (handler != null) { + handler(null, EventArgs.Empty); + } + + } + } + } + + // + // occurs when toolstripmanager.Renderer property has changed + // + // WARNING: When subscribing to static event handlers - make sure you unhook from them + // otherwise you can leak USER objects on process shutdown. + // + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public static event EventHandler RendererChanged { + add { + AddEventHandler(staticEventDefaultRendererChanged, value); + } + remove { + RemoveEventHandler(staticEventDefaultRendererChanged, value); + } + } + + + + /// + /// returns the default toolstrip RenderMode for the thread + public static ToolStripManagerRenderMode RenderMode { + get { + Type currentType = CurrentRendererType; + + if (defaultRenderer != null && !defaultRenderer.IsAutoGenerated) { + return ToolStripManagerRenderMode.Custom; + } + // check the type of the currently set renderer. + // types are cached as this may be called frequently. + if (currentType == ProfessionalRendererType) { + return ToolStripManagerRenderMode.Professional; + } + if (currentType == SystemRendererType) { + return ToolStripManagerRenderMode.System; + } + return ToolStripManagerRenderMode.Custom; + } + [UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)] + set { + + /// SECREVIEW in InternetZone, use individual ToolStrip.RenderMode property rather than global one. + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripManagerRenderMode.Custom, (int)ToolStripManagerRenderMode.Professional)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripManagerRenderMode)); + } + + switch (value) { + case ToolStripManagerRenderMode.System: + case ToolStripManagerRenderMode.Professional: + Renderer = CreateRenderer(value); + break; + case ToolStripManagerRenderMode.Custom: + throw new NotSupportedException(SR.GetString(SR.ToolStripRenderModeUseRendererPropertyInstead)); + } + } + } + + /// + /// an additional layering of control. this lets you pick whether your toolbars + /// should use visual style information (theming) to render itself. + /// potentially you could want a themed app but an unthemed toolstrip. (e.g. Whidbey VS). + /// + public static bool VisualStylesEnabled { + get { + return visualStylesEnabledIfPossible && Application.RenderWithVisualStyles; + } + [UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)] + set { + bool oldVis = VisualStylesEnabled; + visualStylesEnabledIfPossible = value; + + if (oldVis != VisualStylesEnabled) { + EventHandler handler = (EventHandler)GetEventHandler(staticEventDefaultRendererChanged); + + if (handler != null) { + handler(null, EventArgs.Empty); + } + } + } + } + + internal static ToolStripRenderer CreateRenderer(ToolStripManagerRenderMode renderMode) { + switch (renderMode) { + case ToolStripManagerRenderMode.System: + return new ToolStripSystemRenderer(/*isAutoGenerated=*/true); + case ToolStripManagerRenderMode.Professional: + return new ToolStripProfessionalRenderer(/*isAutoGenerated=*/true); + case ToolStripManagerRenderMode.Custom: + default: + return new ToolStripSystemRenderer(/*isAutoGenerated=*/true); + } + } + internal static ToolStripRenderer CreateRenderer(ToolStripRenderMode renderMode) { + switch (renderMode) { + case ToolStripRenderMode.System: + return new ToolStripSystemRenderer(/*isAutoGenerated=*/true); + case ToolStripRenderMode.Professional: + return new ToolStripProfessionalRenderer(/*isAutoGenerated=*/true); + case ToolStripRenderMode.Custom: + default: + return new ToolStripSystemRenderer(/*isAutoGenerated=*/true); + } + } + + #endregion DefaultRenderer + + #region ToolStripPanel + + internal static ClientUtils.WeakRefCollection ToolStripPanels { + get { + if (toolStripPanelWeakArrayList == null) { + toolStripPanelWeakArrayList = new ClientUtils.WeakRefCollection(); + } + return toolStripPanelWeakArrayList; + } + } + + + internal static ToolStripPanel ToolStripPanelFromPoint(Control draggedControl, Point screenLocation) { + + if (toolStripPanelWeakArrayList != null) { + ISupportToolStripPanel draggedItem = draggedControl as ISupportToolStripPanel; + bool rootWindowCheck = draggedItem.IsCurrentlyDragging; + + + + for (int i = 0; i < toolStripPanelWeakArrayList.Count; i++) { + ToolStripPanel toolStripPanel = toolStripPanelWeakArrayList[i] as ToolStripPanel; + + if (toolStripPanel != null && toolStripPanel.IsHandleCreated && toolStripPanel.Visible && + toolStripPanel.DragBounds.Contains(toolStripPanel.PointToClient(screenLocation))) { + // VSWhidbey 342496 - ensure that we cant drag off one window to another. + if (rootWindowCheck) { + if (IsOnSameWindow(draggedControl, toolStripPanel)) { + return toolStripPanel; + } + } + else { + return toolStripPanel; + } + } + } + } + return null; + } + + + + #endregion + + #region ToolStripSettings + + /// + /// Loads settings for the given Form using the form type's fullname as settings key. + /// + public static void LoadSettings(Form targetForm) { + if (targetForm == null) { + throw new ArgumentNullException("targetForm"); + } + + LoadSettings(targetForm, targetForm.GetType().FullName); + } + + /// + /// Loads settings for the given Form with the given settings key. + /// + public static void LoadSettings(Form targetForm, string key) { + if (targetForm == null) { + throw new ArgumentNullException("targetForm"); + } + + if (String.IsNullOrEmpty(key)) { + throw new ArgumentNullException("key"); + } + + ToolStripSettingsManager settingsManager = new ToolStripSettingsManager(targetForm, key); + + settingsManager.Load(); + } + + /// + /// Saves settings for the given form using the form type's fullname as settings key. + /// + public static void SaveSettings(Form sourceForm) { + if (sourceForm == null) { + throw new ArgumentNullException("sourceForm"); + } + + SaveSettings(sourceForm, sourceForm.GetType().FullName); + } + + /// + /// Saves settings for the given form with the given settings key. + /// + public static void SaveSettings(Form sourceForm, string key) { + if (sourceForm == null) { + throw new ArgumentNullException("sourceForm"); + } + + if (String.IsNullOrEmpty(key)) { + throw new ArgumentNullException("key"); + } + + ToolStripSettingsManager settingsManager = new ToolStripSettingsManager(sourceForm, key); ; + + settingsManager.Save(); + } + + #endregion + + /// + /// ToolStripManager ALT key PreProcessing + /// + + #region MenuKeyAndShortcutProcessing + + /// ModalMenuFilter + /// - this installs a message filter when a dropdown becomes active. + /// - the message filter + /// a. eats WM_MOUSEMOVEs so that the window that's underneath + /// doesnt get highlight processing/tooltips + /// b. detects mouse clicks. if the click is outside the dropdown, it + /// dismisses it. + /// c. detects when the active window has changed. If the active window + /// is unexpected, it dismisses all dropdowns. + /// d. detects keyboard messages, and redirects them to the active dropdown. + /// + /// - There should be 1 Message Filter per thread and it should be uninstalled once + /// the last dropdown has gone away + /// This is not part of ToolStripManager because it's DropDown specific and + /// we dont want to publicly expose this message filter. + internal class ModalMenuFilter : IMessageModifyAndFilter { + private HandleRef _activeHwnd = NativeMethods.NullHandleRef; // the window that was active when we showed the dropdown + private HandleRef _lastActiveWindow = NativeMethods.NullHandleRef; // the window that was last known to be active + private List _inputFilterQueue; + private bool _inMenuMode = false; + private bool _caretHidden = false; + private bool _showUnderlines = false; + private bool menuKeyToggle = false; + private bool _suspendMenuMode = false; + private HostedWindowsFormsMessageHook messageHook; + private System.Windows.Forms.Timer _ensureMessageProcessingTimer = null; + private const int MESSAGE_PROCESSING_INTERVAL = 500; + + private ToolStrip _toplevelToolStrip = null; + + private readonly WeakReference lastFocusedTool = new WeakReference(null); + +#if DEBUG + bool _justEnteredMenuMode = false; +#endif + [ThreadStatic] + private static ModalMenuFilter _instance; + + internal static ModalMenuFilter Instance { + get { + if (_instance == null) { + _instance = new ModalMenuFilter(); + } + return _instance; + } + } + + private ModalMenuFilter() { + } + + /// this is the HWnd that was active when we popped the first dropdown. + internal static HandleRef ActiveHwnd { + get { return Instance.ActiveHwndInternal; } + } + + // returns whether or not we should show focus cues for mnemonics. + public bool ShowUnderlines { + get { + return _showUnderlines; + } + set { + if (_showUnderlines != value) { + _showUnderlines = value; + ToolStripManager.NotifyMenuModeChange(/*textStyleChanged*/true, /*activationChanged*/false); + } + } + } + + private HandleRef ActiveHwndInternal { + get { + return _activeHwnd; + } + set { + if (_activeHwnd.Handle != value.Handle) { + Control control = null; + + // unsubscribe from handle recreate. + if (_activeHwnd.Handle != IntPtr.Zero) { + control = Control.FromHandleInternal(_activeHwnd.Handle); + if (control != null) { + control.HandleCreated -= new EventHandler(OnActiveHwndHandleCreated); + } + } + + _activeHwnd = value; + + // make sure we watch out for handle recreates. + control = Control.FromHandleInternal(_activeHwnd.Handle); + if (control != null) { + control.HandleCreated += new EventHandler(OnActiveHwndHandleCreated); + } + } + + } + } + + // returns whether or not someone has called EnterMenuMode. + internal static bool InMenuMode { + get { return Instance._inMenuMode; } + } + + + internal static bool MenuKeyToggle { + get { + return Instance.menuKeyToggle; + } + set { + if (Instance.menuKeyToggle != value) { + Instance.menuKeyToggle = value; + + } + } + } + + /// This is used in scenarios where windows forms + /// does not own the message pump, but needs access + /// to the message queue. + private HostedWindowsFormsMessageHook MessageHook { + get { + if (messageHook == null) { + messageHook = new HostedWindowsFormsMessageHook(); + } + return messageHook; + } + } + + // ToolStrip analog to WM_ENTERMENULOOP + private void EnterMenuModeCore() { + Debug.Assert(!InMenuMode, "How did we get here if we're already in menu mode?"); + + if (!InMenuMode) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "___________Entering MenuMode...."); +#if DEBUG + _justEnteredMenuMode = true; +#endif + IntPtr hwndActive = UnsafeNativeMethods.GetActiveWindow(); + if (hwndActive != IntPtr.Zero) { + ActiveHwndInternal = new HandleRef(this, hwndActive); + } + + // PERF, SECREVIEW: dont call Application.AddMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().AddMessageFilter(this); + Application.ThreadContext.FromCurrent().TrackInput(true); + + if (!Application.ThreadContext.FromCurrent().GetMessageLoop(true)) { + // message filter isnt going to help as we dont own the message pump + // switch over to a MessageHook + MessageHook.HookMessages = true; + } + _inMenuMode = true; + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + NotifyLastLastFocusedToolAboutFocusLoss(); + } + + // fire timer messages to force our filter to get evaluated. + ProcessMessages(true); + } + + } + + internal void NotifyLastLastFocusedToolAboutFocusLoss() { + IKeyboardToolTip lastFocusedTool = KeyboardToolTipStateMachine.Instance.LastFocusedTool; + if (lastFocusedTool != null) { + this.lastFocusedTool.SetTarget(lastFocusedTool); + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(lastFocusedTool); + } + } + + internal static void ExitMenuMode() { + Instance.ExitMenuModeCore(); + } + + // ToolStrip analog to WM_EXITMENULOOP + private void ExitMenuModeCore() { + + // ensure we've cleaned up the timer. + ProcessMessages(false); + + if (InMenuMode) { + try { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "___________Exiting MenuMode...."); + + if (messageHook != null) { + // message filter isnt going to help as we dont own the message pump + // switch over to a MessageHook + messageHook.HookMessages = false; + } + // PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could + // get called a lot and we want to have to assert AWP. + Application.ThreadContext.FromCurrent().RemoveMessageFilter(this); + Application.ThreadContext.FromCurrent().TrackInput(false); + +#if DEBUG + _justEnteredMenuMode = false; +#endif + if (ActiveHwnd.Handle != IntPtr.Zero) { + // unsubscribe from handle creates + Control control = Control.FromHandleInternal(ActiveHwnd.Handle); + if (control != null) { + control.HandleCreated -= new EventHandler(OnActiveHwndHandleCreated); + } + ActiveHwndInternal = NativeMethods.NullHandleRef; + } + if (_inputFilterQueue != null) { + _inputFilterQueue.Clear(); + } + if (_caretHidden) { + _caretHidden = false; + SafeNativeMethods.ShowCaret(NativeMethods.NullHandleRef); + } + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + IKeyboardToolTip tool; + if(this.lastFocusedTool.TryGetTarget(out tool) && tool != null) { + KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(tool); + } + } + + } + finally { + _inMenuMode = false; + + // skip the setter here so we only iterate through the toolstrips once. + bool textStyleChanged = _showUnderlines; + _showUnderlines = false; + ToolStripManager.NotifyMenuModeChange(/*textStyleChanged*/textStyleChanged, /*activationChanged*/true); + + } + + } + } + + + + internal static ToolStrip GetActiveToolStrip() { + return Instance.GetActiveToolStripInternal(); + } + + + internal ToolStrip GetActiveToolStripInternal() { + if (_inputFilterQueue != null && _inputFilterQueue.Count > 0) { + return _inputFilterQueue[_inputFilterQueue.Count - 1]; + } + return null; + } + + // return the toolstrip that is at the root. + private ToolStrip GetCurrentToplevelToolStrip() { + if (_toplevelToolStrip == null) { + ToolStrip activeToolStrip = GetActiveToolStripInternal(); + if (activeToolStrip != null) { + _toplevelToolStrip = activeToolStrip.GetToplevelOwnerToolStrip(); + } + } + return _toplevelToolStrip; + } + + + private void OnActiveHwndHandleCreated(object sender, EventArgs e) { + Control topLevel = sender as Control; + ActiveHwndInternal = new HandleRef(this, topLevel.Handle); + } + internal static void ProcessMenuKeyDown(ref Message m) { + Keys keyData = (Keys)(int)m.WParam; + + ToolStrip toolStrip = Control.FromHandleInternal(m.HWnd) as ToolStrip; + if (toolStrip != null && !toolStrip.IsDropDown) { + return; + } + + // VSW 423760: handle the case where the ALT key has been pressed down while a dropdown + // was open. We need to clear off the MenuKeyToggle so the next ALT will activate + // the menu. + + if (ToolStripManager.IsMenuKey(keyData)) { + if (!InMenuMode && MenuKeyToggle) { + MenuKeyToggle = false; + } + else if (!MenuKeyToggle) { + ModalMenuFilter.Instance.ShowUnderlines = true; + } + } + + } + + internal static void CloseActiveDropDown(ToolStripDropDown activeToolStripDropDown, ToolStripDropDownCloseReason reason) { + + activeToolStripDropDown.SetCloseReason(reason); + activeToolStripDropDown.Visible = false; + + // there's no more dropdowns left in the chain + if (GetActiveToolStrip() == null) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.CloseActiveDropDown] Calling exit because there are no more dropdowns left to activate."); + ExitMenuMode(); + + // make sure we roll selection off the toplevel toolstrip. + if (activeToolStripDropDown.OwnerItem != null) { + activeToolStripDropDown.OwnerItem.Unselect(); + } + } + + } + + // fire a timer event to ensure we have a message in the queue every 500ms + private void ProcessMessages(bool process) { + if (process) { + if (_ensureMessageProcessingTimer == null) { + _ensureMessageProcessingTimer = new System.Windows.Forms.Timer(); + } + _ensureMessageProcessingTimer.Interval = MESSAGE_PROCESSING_INTERVAL; + _ensureMessageProcessingTimer.Enabled = true; + } + else if (_ensureMessageProcessingTimer != null) { + _ensureMessageProcessingTimer.Enabled = false; + _ensureMessageProcessingTimer.Dispose(); + _ensureMessageProcessingTimer = null; + } + } + + + private void ProcessMouseButtonPressed(IntPtr hwndMouseMessageIsFrom, int x, int y) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.ProcessMouseButtonPressed] Found a mouse down."); + + int countDropDowns = _inputFilterQueue.Count; + for (int i = 0; i < countDropDowns; i++) { + ToolStrip activeToolStrip = GetActiveToolStripInternal(); + + if (activeToolStrip != null) { + NativeMethods.POINT pt = new NativeMethods.POINT(); + pt.x = x; + pt.y = y; + UnsafeNativeMethods.MapWindowPoints(new HandleRef(activeToolStrip, hwndMouseMessageIsFrom), new HandleRef(activeToolStrip, activeToolStrip.Handle), pt, 1); + if (!activeToolStrip.ClientRectangle.Contains(pt.x, pt.y)) { + ToolStripDropDown activeToolStripDropDown = activeToolStrip as ToolStripDropDown; + if (activeToolStripDropDown != null) { + + if (!(activeToolStripDropDown.OwnerToolStrip != null + && activeToolStripDropDown.OwnerToolStrip.Handle == hwndMouseMessageIsFrom + && activeToolStripDropDown.OwnerDropDownItem != null + && activeToolStripDropDown.OwnerDropDownItem.DropDownButtonArea.Contains(x, y))) { + // the owner item should handle closing the dropdown + // this allows code such as if (DropDown.Visible) { Hide, Show } etc. + CloseActiveDropDown(activeToolStripDropDown, ToolStripDropDownCloseReason.AppClicked); + } + } + else { + // make sure we clear the selection. + activeToolStrip.NotifySelectionChange(/*selectedItem=*/null); + // we're a toplevel toolstrip and we've clicked somewhere else. + // Exit menu mode + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.ProcessMouseButtonPressed] Calling exit because we're a toplevel toolstrip and we've clicked somewhere else."); + ExitMenuModeCore(); + } + } + else { + // we've found a dropdown that intersects with the mouse message + break; + } + } + else { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.ProcessMouseButtonPressed] active toolstrip is null."); + break; + } + } + + } + private bool ProcessActivationChange() { + int countDropDowns = _inputFilterQueue.Count; + for (int i = 0; i < countDropDowns; i++) { + ToolStripDropDown activeDropDown = this.GetActiveToolStripInternal() as ToolStripDropDown; + if (activeDropDown != null && activeDropDown.AutoClose) { + activeDropDown.Visible = false; + } + } + // if (_inputFilterQueue.Count == 0) { + ExitMenuModeCore(); + return true; + //} + //return false; + + } + + internal static void SetActiveToolStrip(ToolStrip toolStrip, bool menuKeyPressed) { + if (!InMenuMode && menuKeyPressed) { + Instance.ShowUnderlines = true; + } + + Instance.SetActiveToolStripCore(toolStrip); + } + + internal static void SetActiveToolStrip(ToolStrip toolStrip) { + Instance.SetActiveToolStripCore(toolStrip); + } + + private void SetActiveToolStripCore(ToolStrip toolStrip) { + + if (toolStrip == null) { + return; + } + if (toolStrip.IsDropDown) { + // for something that never closes, dont use menu mode. + ToolStripDropDown dropDown = toolStrip as ToolStripDropDown; + + if (dropDown.AutoClose == false) { + // store off the current active hwnd + IntPtr hwndActive = UnsafeNativeMethods.GetActiveWindow(); + if (hwndActive != IntPtr.Zero) { + ActiveHwndInternal = new HandleRef(this, hwndActive); + } + // dont actually enter menu mode... + return; + } + } + toolStrip.KeyboardActive = true; + + + if (_inputFilterQueue == null) { + // use list because we want to be able to remove at any point + _inputFilterQueue = new List(); + } + else { + ToolStrip currentActiveToolStrip = GetActiveToolStripInternal(); + + // toolstrip dropdowns push/pull their activation based on visibility. + // we have to account for the toolstrips that arent dropdowns + if (currentActiveToolStrip != null) { + if (!currentActiveToolStrip.IsDropDown) { + _inputFilterQueue.Remove(currentActiveToolStrip); + } + else if ((toolStrip.IsDropDown) + && (ToolStripDropDown.GetFirstDropDown(toolStrip) + != ToolStripDropDown.GetFirstDropDown(currentActiveToolStrip))) { + + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.SetActiveToolStripCore] Detected a new dropdown not in this chain opened, Dismissing everything in the old chain. "); + _inputFilterQueue.Remove(currentActiveToolStrip); + + ToolStripDropDown currentActiveToolStripDropDown = currentActiveToolStrip as ToolStripDropDown; + currentActiveToolStripDropDown.DismissAll(); + } + } + } + + // reset the toplevel toolstrip + _toplevelToolStrip = null; + + if (!_inputFilterQueue.Contains(toolStrip)) + _inputFilterQueue.Add(toolStrip); + if (!InMenuMode && _inputFilterQueue.Count > 0) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.SetActiveToolStripCore] Setting " + WindowsFormsUtils.GetControlInformation(toolStrip.Handle) + " active."); + + EnterMenuModeCore(); + } + // hide the caret if we're showing a toolstrip dropdown + if (!_caretHidden && toolStrip.IsDropDown && InMenuMode) { + _caretHidden = true; + SafeNativeMethods.HideCaret(NativeMethods.NullHandleRef); + } + + } + + internal static void SuspendMenuMode() { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter] SuspendMenuMode"); + + Instance._suspendMenuMode = true; + } + + internal static void ResumeMenuMode() { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter] ResumeMenuMode"); + Instance._suspendMenuMode = false; + } + internal static void RemoveActiveToolStrip(ToolStrip toolStrip) { + Instance.RemoveActiveToolStripCore(toolStrip); + } + + private void RemoveActiveToolStripCore(ToolStrip toolStrip) { + // precautionary - remove the active toplevel toolstrip. + _toplevelToolStrip = null; + + if (_inputFilterQueue != null) { + _inputFilterQueue.Remove(toolStrip); + } + } + + private static bool IsChildOrSameWindow(HandleRef hwndParent, HandleRef hwndChild) { + if (hwndParent.Handle == hwndChild.Handle) { + return true; + } + if (UnsafeNativeMethods.IsChild(hwndParent, hwndChild)) { + return true; + } + return false; + } + + private static bool IsKeyOrMouseMessage(Message m) { + bool filterMessage = false; + + if (m.Msg >= NativeMethods.WM_MOUSEFIRST && m.Msg <= NativeMethods.WM_MOUSELAST) { + filterMessage = true; + } + else if (m.Msg >= NativeMethods.WM_NCLBUTTONDOWN && m.Msg <= NativeMethods.WM_NCMBUTTONDBLCLK) { + filterMessage = true; + } + else if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + filterMessage = true; + } + return filterMessage; + } + + + + public bool PreFilterMessage(ref Message m) { +#if DEBUG + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose && _justEnteredMenuMode, "[ModalMenuFilter.PreFilterMessage] MenuMode MessageFilter installed and working."); + _justEnteredMenuMode = false; +#endif + + if (_suspendMenuMode) { + return false; + } + ToolStrip activeToolStrip = GetActiveToolStrip(); + if (activeToolStrip == null) { + return false; + } + if (activeToolStrip.IsDisposed) { + RemoveActiveToolStripCore(activeToolStrip); + return false; + } + HandleRef hwndActiveToolStrip = new HandleRef(activeToolStrip, activeToolStrip.Handle); + HandleRef hwndCurrentActiveWindow = new HandleRef(null, UnsafeNativeMethods.GetActiveWindow()); + + // if the active window has changed... + if (hwndCurrentActiveWindow.Handle != _lastActiveWindow.Handle) { + // if another window has gotten activation - we should dismiss. + if (hwndCurrentActiveWindow.Handle == IntPtr.Zero) { + // we dont know what it was cause it's on another thread or doesnt exist + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.PreFilterMessage] Dismissing because: " + WindowsFormsUtils.GetControlInformation(hwndCurrentActiveWindow.Handle) + " has gotten activation. "); + ProcessActivationChange(); + } + else if (!(Control.FromChildHandleInternal(hwndCurrentActiveWindow.Handle) is ToolStripDropDown) // its NOT a dropdown + && !IsChildOrSameWindow(hwndCurrentActiveWindow, hwndActiveToolStrip) // and NOT a child of the active toolstrip + && !IsChildOrSameWindow(hwndCurrentActiveWindow, ActiveHwnd)) { // and NOT a child of the active hwnd + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.PreFilterMessage] Calling ProcessActivationChange because: " + WindowsFormsUtils.GetControlInformation(hwndCurrentActiveWindow.Handle) + " has gotten activation. "); + ProcessActivationChange(); + } + } + + // store this off so we dont have to do activation processing next time + _lastActiveWindow = hwndCurrentActiveWindow; + + // PERF: skip over things like PAINT... + if (!IsKeyOrMouseMessage(m)) { + return false; + } + + DpiAwarenessContext context = CommonUnsafeNativeMethods.TryGetDpiAwarenessContextForWindow(m.HWnd); + + using (DpiHelper.EnterDpiAwarenessScope(context)) { + switch (m.Msg) { + + case NativeMethods.WM_MOUSEMOVE: + case NativeMethods.WM_NCMOUSEMOVE: + // Mouse move messages should be eaten if they arent for a dropdown. + // this prevents things like ToolTips and mouse over highlights from + // being processed. + Control control = Control.FromChildHandleInternal(m.HWnd); + if (control == null || !(control.TopLevelControlInternal is ToolStripDropDown)) { + // double check it's not a child control of the active toolstrip. + if (!IsChildOrSameWindow(hwndActiveToolStrip, new HandleRef(null, m.HWnd))) { + + // it is NOT a child of the current active toolstrip. + + ToolStrip toplevelToolStrip = GetCurrentToplevelToolStrip(); + if (toplevelToolStrip != null + && (IsChildOrSameWindow(new HandleRef(toplevelToolStrip, toplevelToolStrip.Handle), + new HandleRef(null, m.HWnd)))) { + // DONT EAT mouse message. + // The mouse message is from an HWND that is part of the toplevel toolstrip - let the mosue move through so + // when you have something like the file menu open and mouse over the edit menu + // the file menu will dismiss. + + return false; + } + else if (!IsChildOrSameWindow(ActiveHwnd, new HandleRef(null, m.HWnd))) { + // DONT EAT mouse message. + // the mouse message is from another toplevel HWND. + return false; + } + // EAT mouse message + // the HWND is + // not part of the active toolstrip + // not the toplevel toolstrip (e.g. MenuStrip). + // not parented to the toplevel toolstrip (e.g a combo box on a menu strip). + return true; + } + } + break; + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + // + // When a mouse button is pressed, we should determine if it is within the client coordinates + // of the active dropdown. If not, we should dismiss it. + // + ProcessMouseButtonPressed(m.HWnd, + /*x=*/NativeMethods.Util.SignedLOWORD(m.LParam), + /*y=*/NativeMethods.Util.SignedHIWORD(m.LParam)); + + break; + case NativeMethods.WM_NCLBUTTONDOWN: + case NativeMethods.WM_NCRBUTTONDOWN: + case NativeMethods.WM_NCMBUTTONDOWN: + // + // When a mouse button is pressed, we should determine if it is within the client coordinates + // of the active dropdown. If not, we should dismiss it. + // + ProcessMouseButtonPressed(/*nc messages are in screen coords*/IntPtr.Zero, + /*x=*/NativeMethods.Util.SignedLOWORD(m.LParam), + /*y=*/NativeMethods.Util.SignedHIWORD(m.LParam)); + break; + + case NativeMethods.WM_KEYDOWN: + case NativeMethods.WM_KEYUP: + case NativeMethods.WM_CHAR: + case NativeMethods.WM_DEADCHAR: + case NativeMethods.WM_SYSKEYDOWN: + case NativeMethods.WM_SYSKEYUP: + case NativeMethods.WM_SYSCHAR: + case NativeMethods.WM_SYSDEADCHAR: + + if (!activeToolStrip.ContainsFocus) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.PreFilterMessage] MODIFYING Keyboard message " + m.ToString()); + + // route all keyboard messages to the active dropdown. + m.HWnd = activeToolStrip.Handle; + } + else { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ModalMenuFilter.PreFilterMessage] got Keyboard message " + m.ToString()); + } + break; + + } + } + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] // since this has the lifetime of the thread, theres no great way to dispose. + private class HostedWindowsFormsMessageHook { + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private IntPtr messageHookHandle = IntPtr.Zero; + private bool isHooked = false; //VSWHIDBEY # 474112 + private NativeMethods.HookProc hookProc; + + public HostedWindowsFormsMessageHook() { + +#if DEBUG + try { + callingStack = Environment.StackTrace; + } + catch (SecurityException) { + } +#endif + } + +#if DEBUG + string callingStack; + ~HostedWindowsFormsMessageHook() { + Debug.Assert(messageHookHandle == IntPtr.Zero, "Finalizing an active mouse hook. This will crash the process. Calling stack: " + callingStack); + } +#endif + + public bool HookMessages { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get { + return messageHookHandle != IntPtr.Zero; + } + set { + if (value) { + InstallMessageHook(); + } + else { + UninstallMessageHook(); + } + } + } + + private void InstallMessageHook() { + lock (this) { + if (messageHookHandle != IntPtr.Zero) { + return; + } + + hookProc = new NativeMethods.HookProc(this.MessageHookProc); + + messageHookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_GETMESSAGE, + hookProc, + new HandleRef(null, IntPtr.Zero), + SafeNativeMethods.GetCurrentThreadId()); + + if (messageHookHandle != IntPtr.Zero) { + isHooked = true; + } + Debug.Assert(messageHookHandle != IntPtr.Zero, "Failed to install mouse hook"); + } + } + + private unsafe IntPtr MessageHookProc(int nCode, IntPtr wparam, IntPtr lparam) { + if (nCode == NativeMethods.HC_ACTION) { + if (isHooked && (int)wparam == NativeMethods.PM_REMOVE /*only process GetMessage, not PeekMessage*/) { + // only process messages we've pulled off the queue + NativeMethods.MSG* msg = (NativeMethods.MSG*)lparam; + if (msg != null) { + //Debug.WriteLine("Got " + Message.Create(msg->hwnd, msg->message, wparam, lparam).ToString()); + // call pretranslate on the message - this should execute + // the message filters and preprocess message. + if (Application.ThreadContext.FromCurrent().PreTranslateMessage(ref *msg)) { + msg->message = NativeMethods.WM_NULL; + } + } + } + } + + return UnsafeNativeMethods.CallNextHookEx(new HandleRef(this, messageHookHandle), nCode, wparam, lparam); + } + + private void UninstallMessageHook() { + lock (this) { + if (messageHookHandle != IntPtr.Zero) { + UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(this, messageHookHandle)); + hookProc = null; + messageHookHandle = IntPtr.Zero; + isHooked = false; + } + } + } + + + } + + + } + + + + internal static bool ShowMenuFocusCues { + get { + if (!DisplayInformation.MenuAccessKeysUnderlined) { + return ModalMenuFilter.Instance.ShowUnderlines; + } + return true; + } + } + + + /// determines if the key combination is valid for a shortcut. + /// must have a modifier key + a regular key. + /// + public static bool IsValidShortcut(Keys shortcut) { + // should have a key and one or more modifiers. + + Keys keyCode = (Keys)(shortcut & Keys.KeyCode); + Keys modifiers = (Keys)(shortcut & Keys.Modifiers); + + if (shortcut == Keys.None) { + return false; + } + else if ((keyCode == Keys.Delete) || (keyCode == Keys.Insert)) { + return true; + } + else if (((int)keyCode >= (int)Keys.F1) && ((int)keyCode <= (int)Keys.F24)) { + // function keys by themselves are valid + return true; + } + else if ((keyCode != Keys.None) && (modifiers != Keys.None)) { + switch (keyCode) { + case Keys.Menu: + case Keys.ControlKey: + case Keys.ShiftKey: + // shift, control and alt arent valid on their own. + return false; + default: + if (modifiers == Keys.Shift) { + // shift + somekey isnt a valid modifier either + return false; + } + return true; + } + } + // has to have a valid keycode and valid modifier. + return false; + } + + internal static bool IsMenuKey(Keys keyData) { + Keys keyCode = keyData & Keys.KeyCode; + return (Keys.Menu == keyCode || Keys.F10 == keyCode); + } + + public static bool IsShortcutDefined(Keys shortcut) { + for (int i = 0; i < ToolStrips.Count; i++) { + ToolStrip t = ToolStrips[i] as ToolStrip; + if ((t != null) && t.Shortcuts.Contains(shortcut)) { + return true; + } + } + return false; + } + + + /// this function is called for toplevel controls to process shortcuts. + /// this function should be called from the topmost container control only. + /// + internal static bool ProcessCmdKey(ref Message m, Keys keyData) { + + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessCmdKey - processing: [" + keyData.ToString() + "]"); + if (ToolStripManager.IsValidShortcut(keyData)) { + // if we're at the toplevel, check the toolstrips for matching shortcuts. + // Win32 menus are handled in Form.ProcessCmdKey, but we cant guarantee that + // toolstrips will be hosted in a form. ToolStrips have a hash of shortcuts + // per container, so this should hopefully be a quick search. + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessCmdKey - IsValidShortcut: [" + keyData.ToString() + "]"); + + return ToolStripManager.ProcessShortcut(ref m, keyData); + } + if (m.Msg == NativeMethods.WM_SYSKEYDOWN) { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessCmdKey - Checking if it's a menu key: [" + keyData.ToString() + "]"); + ToolStripManager.ModalMenuFilter.ProcessMenuKeyDown(ref m); + } + + return false; + } + + + /// we're halfway to an accellerator table system here. + /// each toolstrip maintains a hash of the current shortcuts its using. + /// this way the search only takes O(number of toolstrips in the thread) + /// ToolStripMenuItem pushes itself into this table as the owner is set or the shortcut changes. + /// + internal static bool ProcessShortcut(ref Message m, Keys shortcut) { + if (!IsThreadUsingToolStrips()) { + return false; + } + Control activeControl = Control.FromChildHandleInternal(m.HWnd); + Control activeControlInChain = activeControl; + + if (activeControlInChain != null && IsValidShortcut(shortcut)) { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessShortcut - processing: [" + shortcut.ToString() + "]"); + + // start from the focused control and work your way up the parent chain + do { + // check the context menu strip first. + if (activeControlInChain.ContextMenuStrip != null) { + if (activeControlInChain.ContextMenuStrip.Shortcuts.ContainsKey(shortcut)) { + ToolStripMenuItem item = activeControlInChain.ContextMenuStrip.Shortcuts[shortcut] as ToolStripMenuItem; + if (item.ProcessCmdKey(ref m, shortcut)) { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessShortcut - found item on context menu: [" + item.ToString() + "]"); + return true; + } + } + } + activeControlInChain = activeControlInChain.ParentInternal; + } while (activeControlInChain != null); + + if (activeControlInChain != null) { + // the keystroke may applies to one of our parents... + // a WM_CONTEXTMENU message bubbles up to the parent control + activeControl = activeControlInChain; + } + + bool retVal = false; + bool needsPrune = false; + + // now search the toolstrips + for (int i = 0; i < ToolStrips.Count; i++) { + ToolStrip toolStrip = ToolStrips[i] as ToolStrip; + bool isAssociatedContextMenu = false; + bool isDoublyAssignedContextMenuStrip = false; + + + if (toolStrip == null) { + // consider prune tree... + needsPrune = true; + continue; + } + else if (activeControl != null && toolStrip == activeControl.ContextMenuStrip) { + continue; + } + else if (toolStrip.Shortcuts.ContainsKey(shortcut)) { + + + if (toolStrip.IsDropDown) { + // we dont want to process someone else's context menu (e.g. button1 and button2 have context menus) + // button2's context menu should not be processed if button1 is the one we're processing. + + ToolStripDropDown dropDown = toolStrip as ToolStripDropDown; + ContextMenuStrip toplevelContextMenu = dropDown.GetFirstDropDown() as ContextMenuStrip; + + // VSWhidbey 433886: if a context menu is re-used between the main menu and the + // and some other control's context menu, we should go ahead and evaluate it. + + if (toplevelContextMenu != null) { + isDoublyAssignedContextMenuStrip = toplevelContextMenu.IsAssignedToDropDownItem; + if (!isDoublyAssignedContextMenuStrip) { + if (toplevelContextMenu != activeControl.ContextMenuStrip) { + // the toplevel context menu is NOT the same as the active control's context menu. + continue; + } + else { + isAssociatedContextMenu = true; + } + } + } + // else it's not a child of a context menu + } + + + bool rootWindowsMatch = false; + + if (!isAssociatedContextMenu) { + // make sure that were processing shortcuts for the correct window. + // since the shortcut lookup is faster than this check we've postponed this to the last + // possible moment. + ToolStrip topMostToolStrip = toolStrip.GetToplevelOwnerToolStrip(); + if (topMostToolStrip != null && activeControl != null) { + HandleRef rootWindowOfToolStrip = WindowsFormsUtils.GetRootHWnd(topMostToolStrip); + HandleRef rootWindowOfControl = WindowsFormsUtils.GetRootHWnd(activeControl); + rootWindowsMatch = (rootWindowOfToolStrip.Handle == rootWindowOfControl.Handle); + + if (rootWindowsMatch) { + // Double check this is not an MDIContainer type situation... + Form mainForm = Control.FromHandleInternal(rootWindowOfControl.Handle) as Form; + if (mainForm != null && mainForm.IsMdiContainer) { + Form toolStripForm = topMostToolStrip.FindFormInternal(); + if (toolStripForm != mainForm && toolStripForm != null) { + // VSWhidbey 530569 + // we should only process shortcuts of the ActiveMDIChild or the Main Form. + rootWindowsMatch = (toolStripForm == mainForm.ActiveMdiChildInternal); + } + } + + } + } + } + if (isAssociatedContextMenu || rootWindowsMatch || isDoublyAssignedContextMenuStrip) { + ToolStripMenuItem item = toolStrip.Shortcuts[shortcut] as ToolStripMenuItem; + if (item != null) { + if (item.ProcessCmdKey(ref m, shortcut)) { + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessShortcut - found item on toolstrip: [" + item.ToString() + "]"); + retVal = true; + break; + } + } + } + } + } + if (needsPrune) { + PruneToolStripList(); + } + return retVal; + + } + return false; + + } + + + /// this function handles when Alt is pressed. + /// if it finds a menustrip to select, it returns true, + /// if it doesnt it returns false. + /// if it finds a win32 menu is already associated with the control it bails, returning false. + /// + internal static bool ProcessMenuKey(ref Message m) { + + Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "ToolStripManager.ProcessMenuKey: [" + m.ToString() + "]"); + if (!IsThreadUsingToolStrips()) { + return false; + } + // recievedMenuKeyUp = true; + + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] Determining whether we should send focus to MenuStrip"); + + Keys keyData = (Keys)(int)m.LParam; + + // search for our menu to work with + Control intendedControl = Control.FromHandleInternal(m.HWnd); + Control toplevelControl = null; + + MenuStrip menuStripToActivate = null; + if (intendedControl != null) { + // search for a menustrip to select. + toplevelControl = intendedControl.TopLevelControlInternal; + if (toplevelControl != null) { + IntPtr hMenu = UnsafeNativeMethods.GetMenu(new HandleRef(toplevelControl, toplevelControl.Handle)); + if (hMenu == IntPtr.Zero) { + // only activate the menu if there's no win32 menu. Win32 menus trump menustrips. + menuStripToActivate = GetMainMenuStrip(toplevelControl); + } + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "[ProcessMenuKey] MenuStripToActivate is: {0}", menuStripToActivate)); + + } + } + // the data that comes into the LParam is the ASCII code, not the VK_* code. + // we need to compare against char instead. + if ((char)keyData == ' ') { // dont process system menu + ModalMenuFilter.MenuKeyToggle = false; + } + else if ((char)keyData == '-') { + // deal with MDI system menu + Form mdiChild = toplevelControl as Form; + if (mdiChild != null && mdiChild.IsMdiChild) { + if (mdiChild.WindowState == FormWindowState.Maximized) { + ModalMenuFilter.MenuKeyToggle = false; + } + } + } + else { + // this is the same as Control.ModifierKeys - but we save two p/invokes. + if (UnsafeNativeMethods.GetKeyState((int)Keys.ShiftKey) < 0 && (keyData == Keys.None)) { + // VSWhidbey 381933 if it's Shift+F10 and we're already InMenuMode, then we + // need to cancel this message, otherwise we'll enter the native modal menu loop. + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] DETECTED SHIFT+F10" + keyData.ToString()); + return ToolStripManager.ModalMenuFilter.InMenuMode; + } + else { + if (menuStripToActivate != null && !ModalMenuFilter.MenuKeyToggle) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] attempting to set focus to menustrip"); + + // if we've alt-tabbed away dont snap/restore focus. + HandleRef topmostParentOfMenu = WindowsFormsUtils.GetRootHWnd(menuStripToActivate); + IntPtr foregroundWindow = UnsafeNativeMethods.GetForegroundWindow(); + + if (topmostParentOfMenu.Handle == foregroundWindow) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] ToolStripManager call MenuStrip.OnMenuKey"); + return menuStripToActivate.OnMenuKey(); + } + } + else if (menuStripToActivate != null) { + Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "[ProcessMenuKey] Resetting MenuKeyToggle"); + ModalMenuFilter.MenuKeyToggle = false; + return true; + } + } + + } + return false; + } + + internal static MenuStrip GetMainMenuStrip(Control control) { + if (control == null) { + Debug.Fail("why are we passing null to GetMainMenuStrip?"); + return null; + } + + // look for a particular main menu strip to be set. + Form mainForm = control.FindFormInternal(); + if (mainForm != null && mainForm.MainMenuStrip != null) { + return mainForm.MainMenuStrip; + } + + // if not found go through the entire collection. + return GetFirstMenuStripRecursive(control.Controls); + } + + private static MenuStrip GetFirstMenuStripRecursive(Control.ControlCollection controlsToLookIn) { + try { + // Perform breadth first search - as it's likely people will want controls belonging + // to the same parent close to each other. + + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null) { + continue; + } + if (controlsToLookIn[i] is MenuStrip) { + return controlsToLookIn[i] as MenuStrip; + } + } + + // Recursive search for controls in child collections. + + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null) { + continue; + } + + if ((controlsToLookIn[i].Controls != null) && controlsToLookIn[i].Controls.Count > 0) { + // if it has a valid child collecion, append those results to our collection + MenuStrip menuStrip = GetFirstMenuStripRecursive(controlsToLookIn[i].Controls); + if (menuStrip != null) { + return menuStrip; + } + } + } + } + catch (Exception e) { + // VSWHIDBEY 80122 make sure we deal with non-critical failures gracefully. + if (ClientUtils.IsCriticalException(e)) { + throw; + } + } + return null; + } + + + #endregion MenuKeyAndShortcutProcessing + + /// + /// ToolStripManager MenuMerging functions + /// + + #region MenuMerging + + private static ToolStripItem FindMatch(ToolStripItem source, ToolStripItemCollection destinationItems) { + // based on MergeAction: + // Append, return the last sibling + ToolStripItem result = null; + if (source != null) { + for (int i = 0; i < destinationItems.Count; i++) { + ToolStripItem candidateItem = destinationItems[i]; + // using SafeCompareKeys so we use the same heuristics as keyed collections. + if (WindowsFormsUtils.SafeCompareStrings(source.Text, candidateItem.Text, true)) { + result = candidateItem; + break; // we found it + } + } + + if (result == null && source.MergeIndex > -1 && source.MergeIndex < destinationItems.Count) { + result = destinationItems[source.MergeIndex]; + } + } + return result; + } + + /// + /// + internal static ArrayList FindMergeableToolStrips(ContainerControl container) { + ArrayList result = new ArrayList(); + if (container != null) { + for (int i = 0; i < ToolStrips.Count; i++) { + ToolStrip candidateTS = (ToolStrip)ToolStrips[i]; + //if(candidateTS != null) { + // Debug.WriteLine("candidate TS: " + candidateTS.Name + " | " + candidateTS.AllowMerge + " | " + (candidateTS.Parent == null ? "null" : candidateTS.Parent.Name) +" | " + container.Name); + //} + //Debug.WriteLine(candidateTS == null ? "null" : "not null"); + if (candidateTS != null && candidateTS.AllowMerge && container == candidateTS.FindFormInternal()) { + //Debug.WriteLine("adding"); + result.Add(candidateTS); + } + } + } + result.Sort(new ToolStripCustomIComparer()); //we sort them from more specific to less specific + return result; + } + + private static bool IsSpecialMDIStrip(ToolStrip toolStrip) { + return (toolStrip is MdiControlStrip || toolStrip is MdiWindowListStrip); + } + + /// + /// + /// merge two toolstrips + /// + public static bool Merge(ToolStrip sourceToolStrip, ToolStrip targetToolStrip) { + // check arguments + if (sourceToolStrip == null) { + throw new ArgumentNullException("sourceToolStrip"); + } + if (targetToolStrip == null) { + throw new ArgumentNullException("targetToolStrip"); + } + if (targetToolStrip == sourceToolStrip) { + throw new ArgumentException(SR.GetString(SR.ToolStripMergeImpossibleIdentical)); + } + + // we only do this if the source and target toolstrips are the same + bool canMerge = IsSpecialMDIStrip(sourceToolStrip); + canMerge = (canMerge || (sourceToolStrip.AllowMerge && + targetToolStrip.AllowMerge && + (sourceToolStrip.GetType().IsAssignableFrom(targetToolStrip.GetType()) || targetToolStrip.GetType().IsAssignableFrom(sourceToolStrip.GetType())) + ) + ); + MergeHistory mergeHistory = null; + if (canMerge) { + //Debug.WriteLine("Begin merge between src: " + sourceToolStrip.Name + " and target: " + targetToolStrip.Name); + Debug.Indent(); + mergeHistory = new MergeHistory(sourceToolStrip); + + + int originalCount = sourceToolStrip.Items.Count; + + if (originalCount > 0) { + sourceToolStrip.SuspendLayout(); + targetToolStrip.SuspendLayout(); + try { + int lastCount = originalCount; + + // 2. do the actual merging logic + for (int i = 0, itemToLookAt = 0; i < originalCount; i++) { + ToolStripItem item = sourceToolStrip.Items[itemToLookAt]; + //Debug.WriteLine("doing the recursive merge for item " + item.Text); + MergeRecursive(item, targetToolStrip.Items, mergeHistory.MergeHistoryItemsStack); + + int numberOfItemsMerged = lastCount - sourceToolStrip.Items.Count; + itemToLookAt = (numberOfItemsMerged > 0) ? itemToLookAt : itemToLookAt + 1; + lastCount = sourceToolStrip.Items.Count; + } + } + finally { + Debug.Unindent(); + sourceToolStrip.ResumeLayout(); + targetToolStrip.ResumeLayout(); + } + //Debug.WriteLine("pusing mergehistory for toolstrip " + sourceToolStrip.Name + " in target toolstrip MergeHistoryStack property"); + if (mergeHistory.MergeHistoryItemsStack.Count > 0) { + // only push this on the stack if we actually did something + targetToolStrip.MergeHistoryStack.Push(mergeHistory); + } + } + } + bool result = false; + if (mergeHistory != null && mergeHistory.MergeHistoryItemsStack.Count > 0) { + result = true; // we did merge something + } + return result; + } + + + + private static void MergeRecursive(ToolStripItem source, ToolStripItemCollection destinationItems, Stack history) { + Debug.Indent(); + MergeHistoryItem maction; + switch (source.MergeAction) { + case MergeAction.MatchOnly: + case MergeAction.Replace: + case MergeAction.Remove: + ToolStripItem item = FindMatch(source, destinationItems); + if (item != null) { + switch (source.MergeAction) { + case MergeAction.MatchOnly: + //Debug.WriteLine("matchonly"); + ToolStripDropDownItem tsddownDest = item as ToolStripDropDownItem; + ToolStripDropDownItem tsddownSrc = source as ToolStripDropDownItem; + if (tsddownDest != null && tsddownSrc != null && tsddownSrc.DropDownItems.Count != 0) { + + int originalCount = tsddownSrc.DropDownItems.Count; + + if (originalCount > 0) { + int lastCount = originalCount; + tsddownSrc.DropDown.SuspendLayout(); + + try { + // the act of walking through this collection removes items from + // the dropdown. + for (int i = 0, itemToLookAt = 0; i < originalCount; i++) { + + MergeRecursive(tsddownSrc.DropDownItems[itemToLookAt], tsddownDest.DropDownItems, history); + + int numberOfItemsMerged = lastCount - tsddownSrc.DropDownItems.Count; + itemToLookAt = (numberOfItemsMerged > 0) ? itemToLookAt : itemToLookAt + 1; + lastCount = tsddownSrc.DropDownItems.Count; + } + } + finally { + tsddownSrc.DropDown.ResumeLayout(); + } + } + } + break; + case MergeAction.Replace: + case MergeAction.Remove: + //Debug.WriteLine("remove"); + maction = new MergeHistoryItem(MergeAction.Insert); + maction.TargetItem = item; + int indexOfDestinationItem = destinationItems.IndexOf(item); + destinationItems.RemoveAt(indexOfDestinationItem); + maction.Index = indexOfDestinationItem; + maction.IndexCollection = destinationItems; + maction.TargetItem = item; + history.Push(maction); + //Debug.WriteLine(maction.ToString()); + if (source.MergeAction == MergeAction.Replace) { + //Debug.WriteLine("replace"); + //ToolStripItem clonedItem = source.Clone(); + maction = new MergeHistoryItem(MergeAction.Remove); + maction.PreviousIndexCollection = source.Owner.Items; + maction.PreviousIndex = maction.PreviousIndexCollection.IndexOf(source); + maction.TargetItem = source; + destinationItems.Insert(indexOfDestinationItem, source); + maction.Index = indexOfDestinationItem; + maction.IndexCollection = destinationItems; + history.Push(maction); + //Debug.WriteLine(maction.ToString()); + } + break; + } + } + break; + case MergeAction.Insert: + if (source.MergeIndex > -1) { + maction = new MergeHistoryItem(MergeAction.Remove); + maction.PreviousIndexCollection = source.Owner.Items; + maction.PreviousIndex = maction.PreviousIndexCollection.IndexOf(source); + maction.TargetItem = source; + int insertIndex = Math.Min(destinationItems.Count, source.MergeIndex); + destinationItems.Insert(insertIndex, source); + maction.IndexCollection = destinationItems; + maction.Index = insertIndex; + history.Push(maction); + //Debug.WriteLine(maction.ToString()); + } + break; + case MergeAction.Append: + maction = new MergeHistoryItem(MergeAction.Remove); + maction.PreviousIndexCollection = source.Owner.Items; + maction.PreviousIndex = maction.PreviousIndexCollection.IndexOf(source); + maction.TargetItem = source; + int index = destinationItems.Add(source); + maction.Index = index; + maction.IndexCollection = destinationItems; + history.Push(maction); + //Debug.WriteLine(maction.ToString()); + break; + } + Debug.Unindent(); + } + + /// + /// + /// merge two toolstrips + /// + public static bool Merge(ToolStrip sourceToolStrip, string targetName) { + if (sourceToolStrip == null) { + throw new ArgumentNullException("sourceToolStrip"); + } + if (targetName == null) { + throw new ArgumentNullException("targetName"); + } + + ToolStrip target = FindToolStrip(targetName); + if (target == null) { + return false; + } + else { + return Merge(sourceToolStrip, target); + } + } + + /// + /// doesn't do a null check on source... if it's null we unmerge everything + /// + internal static bool RevertMergeInternal(ToolStrip targetToolStrip, ToolStrip sourceToolStrip, bool revertMDIControls) { + bool result = false; + if (targetToolStrip == null) { + throw new ArgumentNullException("targetToolStrip"); + } + if (targetToolStrip == sourceToolStrip) { + throw new ArgumentException(SR.GetString(SR.ToolStripMergeImpossibleIdentical)); + } + bool foundToolStrip = false; + + if (sourceToolStrip != null) { + // we have a specific toolstrip to pull out. + + + // make sure the sourceToolStrip is even merged into the targetToolStrip + foreach (MergeHistory history in targetToolStrip.MergeHistoryStack) { + foundToolStrip = (history.MergedToolStrip == sourceToolStrip); + if (foundToolStrip) { + break; + } + } + + // PERF: if we dont have the toolstrip in our merge history, bail. + if (!foundToolStrip) { + //Debug.WriteLine("source toolstrip not contained within target " + history.MergedToolStrip.Name); + return false; + } + } + + if( sourceToolStrip != null ) { + sourceToolStrip.SuspendLayout(); + } + targetToolStrip.SuspendLayout(); + + try { + //Debug.WriteLine("Reverting merge, playing back history for all merged toolstrip "); + Stack reApply = new Stack(); + foundToolStrip = false; + while (targetToolStrip.MergeHistoryStack.Count > 0 && !foundToolStrip) { + result = true; // we unmerge something... + MergeHistory history = targetToolStrip.MergeHistoryStack.Pop(); + if (history.MergedToolStrip == sourceToolStrip) { + foundToolStrip = true; + } + else if (!revertMDIControls && sourceToolStrip == null) { + // VSWhidbey 352431: Calling ToolStripManager.RevertMerge should not pull out MDIControlStrip && MDIWindowListStrip. + if (IsSpecialMDIStrip(history.MergedToolStrip)) { + reApply.Push(history.MergedToolStrip); + } + } + else { + reApply.Push(history.MergedToolStrip); + } + //Debug.WriteLine("unmerging " + history.MergedToolStrip.Name); + Debug.Indent(); + while (history.MergeHistoryItemsStack.Count > 0) { + MergeHistoryItem historyItem = history.MergeHistoryItemsStack.Pop(); + switch (historyItem.MergeAction) { + case MergeAction.Remove: + historyItem.IndexCollection.Remove(historyItem.TargetItem); + // put it back + historyItem.PreviousIndexCollection.Insert(Math.Min(historyItem.PreviousIndex, historyItem.PreviousIndexCollection.Count), historyItem.TargetItem); + break; + case MergeAction.Insert: + historyItem.IndexCollection.Insert(Math.Min(historyItem.Index, historyItem.IndexCollection.Count), historyItem.TargetItem); + // no need to put it back, inserting it in a new collection, moved it at the correct location + break; + } + } + Debug.Unindent(); + } + + // re-apply the merges of the toolstrips we had to unmerge first. + while (reApply.Count > 0) { + ToolStrip mergeAgain = reApply.Pop(); + Merge(mergeAgain, targetToolStrip); + } + } + finally { + if( sourceToolStrip != null ) { + sourceToolStrip.ResumeLayout(); + } + targetToolStrip.ResumeLayout(); + } + + return result; + //ToolStripMergeNode.SynchronizeFromToolStripMergeNode(targetToolStrip.Items, targetToolStrip.MergeItems); + } + + /// + /// + /// unmerge two toolstrips + /// + public static bool RevertMerge(ToolStrip targetToolStrip) { + return RevertMergeInternal(targetToolStrip, null, /*revertMDIControls*/false); + } + + /// + /// + /// unmerge two toolstrips + /// + public static bool RevertMerge(ToolStrip targetToolStrip, ToolStrip sourceToolStrip) { + if (sourceToolStrip == null) { + throw new ArgumentNullException("sourceToolStrip"); + } + return RevertMergeInternal(targetToolStrip, sourceToolStrip, /*revertMDIControls*/false); + } + + /// + /// + /// unmerge two toolstrips + /// + public static bool RevertMerge(string targetName) { + ToolStrip target = FindToolStrip(targetName); + if (target == null) { + return false; + } + else { + return RevertMerge(target); + } + } + + #endregion MenuMerging + + } + + internal class ToolStripCustomIComparer : IComparer { + int IComparer.Compare(object x, object y) { + if (x.GetType() == y.GetType()) { + return 0; // same type + } + if (x.GetType().IsAssignableFrom(y.GetType())) { + return 1; + } + if (y.GetType().IsAssignableFrom(x.GetType())) { + return -1; + } + return 0; // not the same type, not in each other inheritance chain + } + } + + internal class MergeHistory { + private Stack mergeHistoryItemsStack; + private ToolStrip mergedToolStrip; + + public MergeHistory(ToolStrip mergedToolStrip) { + this.mergedToolStrip = mergedToolStrip; + } + public Stack MergeHistoryItemsStack { + get { + if (mergeHistoryItemsStack == null) { + mergeHistoryItemsStack = new Stack(); + } + return mergeHistoryItemsStack; + } + } + public ToolStrip MergedToolStrip { + get { + return mergedToolStrip; + } + } + } + + internal class MergeHistoryItem { + private MergeAction mergeAction; + private ToolStripItem targetItem; + private int index = -1; + private int previousIndex = -1; + private ToolStripItemCollection previousIndexCollection; + private ToolStripItemCollection indexCollection; + + public MergeHistoryItem(MergeAction mergeAction) { + this.mergeAction = mergeAction; + } + public MergeAction MergeAction { + get { + return mergeAction; + } + } + public ToolStripItem TargetItem { + get { + return targetItem; + } + set { + targetItem = value; + } + } + public int Index { + get { + return index; + } + set { + index = value; + } + } + public int PreviousIndex { + get { + return previousIndex; + } + set { + previousIndex = value; + } + } + public ToolStripItemCollection PreviousIndexCollection { + get { + return previousIndexCollection; + } + set { + previousIndexCollection = value; + } + } + public ToolStripItemCollection IndexCollection { + get { + return indexCollection; + } + set { + indexCollection = value; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public override string ToString() { + return "MergeAction: " + mergeAction.ToString() + " | TargetItem: " + (TargetItem == null ? "null" : TargetItem.Text) + " Index: " + index.ToString(CultureInfo.CurrentCulture); + } + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripManagerRenderMode.cs b/WindowsForms/Managed/System/WinForms/ToolStripManagerRenderMode.cs new file mode 100644 index 000000000..f5c77dd98 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripManagerRenderMode.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + + /// + public enum ToolStripManagerRenderMode { + /// + [Browsable(false)] + Custom = ToolStripRenderMode.Custom, + /// + System = ToolStripRenderMode.System, + /// + Professional = ToolStripRenderMode.Professional + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripMenuItem.cs b/WindowsForms/Managed/System/WinForms/ToolStripMenuItem.cs new file mode 100644 index 000000000..fa1d5cc27 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripMenuItem.cs @@ -0,0 +1,1562 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Drawing.Imaging; + using System.Threading; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Layout; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices; + using System.ComponentModel.Design.Serialization; + using System.Windows.Forms.Design; + using System.Globalization; + using System.Windows.Forms.Internal; + using System.Runtime.Versioning; + + /// + /// + /// ToolStripMenuItem + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.MenuStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)] + [DesignerSerializer("System.Windows.Forms.Design.ToolStripMenuItemCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)] + public class ToolStripMenuItem : ToolStripDropDownItem { + + private static MenuTimer menuTimer = new MenuTimer(); + + private static readonly int PropShortcutKeys = PropertyStore.CreateKey(); + private static readonly int PropCheckState = PropertyStore.CreateKey(); + private static readonly int PropMdiForm = PropertyStore.CreateKey(); + + private bool checkOnClick = false; + private bool showShortcutKeys = true; + private ToolStrip lastOwner = null; + + // SUPPORT for mapping NATIVE menu commands to ToolStripMenuItems ----- + // corresponds to wID in MENUITEMINFO structure + private int nativeMenuCommandID = -1; + private IntPtr targetWindowHandle = IntPtr.Zero; + private IntPtr nativeMenuHandle = IntPtr.Zero; + + // Keep checked images shared between menu items, but per thread so we dont have locking issues in GDI+ + [ThreadStatic] + private static Image indeterminateCheckedImage; + + [ThreadStatic] + private static Image checkedImage; + + private string shortcutKeyDisplayString; + private string cachedShortcutText; + private Size cachedShortcutSize = Size.Empty; + + private static readonly Padding defaultPadding = new Padding(4, 0, 4, 0); + private static readonly Padding defaultDropDownPadding = new Padding(0, 1, 0, 1); + private static readonly Size checkMarkBitmapSize = new Size(16, 16); + private Padding scaledDefaultPadding = defaultPadding; + private Padding scaledDefaultDropDownPadding = defaultDropDownPadding; + private Size scaledCheckMarkBitmapSize = checkMarkBitmapSize; + + private byte openMouseId = 0; + + private static readonly object EventCheckedChanged = new object(); + private static readonly object EventCheckStateChanged = new object(); + + /// + public ToolStripMenuItem() : base() { + Initialize(); // all additional work should be done in Initialize + } + public ToolStripMenuItem(string text):base(text,null,(EventHandler)null) { + Initialize(); + } + public ToolStripMenuItem(Image image):base(null,image,(EventHandler)null) { + Initialize(); + } + public ToolStripMenuItem(string text, Image image):base(text,image,(EventHandler)null) { + Initialize(); + } + public ToolStripMenuItem(string text, Image image, EventHandler onClick):base(text,image,onClick) { + Initialize(); + } + public ToolStripMenuItem(string text, Image image, EventHandler onClick, string name) : base(text,image,onClick, name){ + Initialize(); + } + public ToolStripMenuItem(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) { + Initialize(); + } + public ToolStripMenuItem(string text, Image image, EventHandler onClick, Keys shortcutKeys):base(text,image,onClick) { + Initialize(); + this.ShortcutKeys = shortcutKeys; + } + internal ToolStripMenuItem(Form mdiForm) { + Initialize(); + Properties.SetObject(PropMdiForm,mdiForm); + } + + /// this constructor is only used when we're trying to + /// mimic a native menu like the system menu. In that case + /// we've got to go ahead and collect the command id and the + /// target window to send WM_COMMAND/WM_SYSCOMMAND messages to. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + internal ToolStripMenuItem(IntPtr hMenu, int nativeMenuCommandId, IWin32Window targetWindow) { + Initialize(); + this.Overflow = ToolStripItemOverflow.Never; + this.nativeMenuCommandID = nativeMenuCommandId; + this.targetWindowHandle = Control.GetSafeHandle(targetWindow); + this.nativeMenuHandle = hMenu; + + // Since fetching the image and the text is an awful lot of work + // we're going to just cache it and assume the native stuff + // doesnt update. + // we'll only live-update enabled. + // if this becomes a problem we can override Image and Text properties + // to live-return the results. + + // fetch image + this.Image = GetNativeMenuItemImage(); + this.ImageScaling = ToolStripItemImageScaling.None; + + // fetch text + string text = GetNativeMenuItemTextAndShortcut(); + + // the shortcut is tab separated from the item text. + if (text != null) { + // separate out the two fields. + string[] textFields = text.Split('\t'); + + if (textFields.Length >= 1){ + this.Text = textFields[0]; + } + + if (textFields.Length >= 2) { + // VSWhidbey 448242: We dont care about the shortcut here, the OS is going to + // handle it for us by sending a WM_(SYS)COMMAND during TranslateAcellerator + // Just display whatever the OS would have. + this.ShowShortcutKeys = true; + this.ShortcutKeyDisplayString = textFields[1]; + } + } + } + + internal override void AutoHide(ToolStripItem otherItemBeingSelected) { + if (IsOnDropDown) { + MenuTimer.Transition(this,otherItemBeingSelected as ToolStripMenuItem); + } + else { + base.AutoHide(otherItemBeingSelected); + } + } + private void ClearShortcutCache() { + cachedShortcutSize = Size.Empty; + cachedShortcutText = null; + } + + /// + protected override ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripDropDownMenu(this, true); + } + + internal override ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripMenuItemInternalLayout(this); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripMenuItemAccessibleObject(this); + } + + private void Initialize() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding); + scaledDefaultDropDownPadding = DpiHelper.LogicalToDeviceUnits(defaultDropDownPadding); + scaledCheckMarkBitmapSize = DpiHelper.LogicalToDeviceUnits(checkMarkBitmapSize); + } + + this.Overflow = ToolStripItemOverflow.Never; + this.MouseDownAndUpMustBeInSameItem = false; + this.SupportsDisabledHotTracking = true; + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ? + DpiHelper.LogicalToDeviceUnits(new Size(32, 19), DeviceDpi) : + new Size(32, 19); + } + } + + + /// + protected internal override Padding DefaultMargin { + get { + return Padding.Empty; + } + } + /// + protected override Padding DefaultPadding { + get { + if (IsOnDropDown) { + return scaledDefaultDropDownPadding; + } + else { + return scaledDefaultPadding; + } + } + } + + + public override bool Enabled { + get{ + if (nativeMenuCommandID != -1) { + // if we're based off a native menu item, + // we need to ask it if it's enabled. + if (base.Enabled && nativeMenuHandle != IntPtr.Zero && targetWindowHandle != IntPtr.Zero) { + return GetNativeMenuItemEnabled(); + } + return false; + } + else { + return base.Enabled; + } + } + set { + base.Enabled = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the item is checked. + /// + /// + [ + Bindable(true), + DefaultValue(false), + SRCategory(SR.CatAppearance), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.CheckBoxCheckedDescr) + ] + public bool Checked { + get { + return CheckState != CheckState.Unchecked; + } + + set { + if (value != Checked) { + CheckState = value ? CheckState.Checked : CheckState.Unchecked; + InvokePaint(); + } + } + } + + + /// + /// Keeps a shared copy of the checked image between all menu items + /// Fishes out the appropriate one based on CheckState. + /// + internal Image CheckedImage { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + CheckState checkedState = CheckState; + + if (checkedState == CheckState.Indeterminate) { + if (indeterminateCheckedImage == null) { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + indeterminateCheckedImage = GetBitmapFromIcon("IndeterminateChecked.ico", scaledCheckMarkBitmapSize); + } + else { + Bitmap indeterminateCheckedBmp = new Bitmap(typeof(ToolStripMenuItem), "IndeterminateChecked.bmp"); + if (indeterminateCheckedBmp != null) { + // + indeterminateCheckedBmp.MakeTransparent(indeterminateCheckedBmp.GetPixel(1, 1)); + if (DpiHelper.IsScalingRequired) { + DpiHelper.ScaleBitmapLogicalToDevice(ref indeterminateCheckedBmp); + } + indeterminateCheckedImage = indeterminateCheckedBmp; + } + } + } + return indeterminateCheckedImage; + } + else if (checkedState == CheckState.Checked) { + if (checkedImage == null) { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + checkedImage = GetBitmapFromIcon("Checked.ico", scaledCheckMarkBitmapSize); + } + else { + Bitmap checkedBmp = new Bitmap(typeof(ToolStripMenuItem), "Checked.bmp"); + if (checkedBmp != null) { + // + checkedBmp.MakeTransparent(checkedBmp.GetPixel(1, 1)); + if (DpiHelper.IsScalingRequired) { + DpiHelper.ScaleBitmapLogicalToDevice(ref checkedBmp); + } + checkedImage = checkedBmp; + } + } + } + return checkedImage; + } + return null; + + } + } + + private static Bitmap GetBitmapFromIcon(string iconName, Size desiredIconSize) + { + Bitmap b = null; + + Icon icon = new Icon(typeof(ToolStripMenuItem), iconName); + if (icon != null) + { + Icon desiredIcon = new Icon(icon, desiredIconSize); + if (desiredIcon != null) + { + try + { + b = desiredIcon.ToBitmap(); + + if (b != null) + { + b.MakeTransparent(b.GetPixel(1, 1)); + if (DpiHelper.IsScalingRequired && (b.Size.Width != desiredIconSize.Width || b.Size.Height != desiredIconSize.Height)) + { + Bitmap scaledBitmap = DpiHelper.CreateResizedBitmap(b, desiredIconSize); + if (scaledBitmap != null) + { + b.Dispose(); + b = scaledBitmap; + } + } + } + } + finally + { + icon.Dispose(); + desiredIcon.Dispose(); + } + } + } + + return b; + } + + /// + [ + DefaultValue(false), + SRCategory(SR.CatBehavior), + SRDescription(SR.ToolStripButtonCheckOnClickDescr) + ] + public bool CheckOnClick { + get { + return checkOnClick; + } + set { + checkOnClick = value; + } + } + + /// + /// + /// Gets + /// or sets a value indicating whether the check box is checked. + /// + [ + Bindable(true), + SRCategory(SR.CatAppearance), + DefaultValue(CheckState.Unchecked), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.CheckBoxCheckStateDescr) + ] + public CheckState CheckState { + get { + bool found = false; + object checkState = Properties.GetInteger(PropCheckState, out found); + return (found) ? (CheckState)checkState : CheckState.Unchecked; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)CheckState.Unchecked, (int)CheckState.Indeterminate)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(CheckState)); + } + + if (value != CheckState) { + Properties.SetInteger(PropCheckState, (int)value); + OnCheckedChanged(EventArgs.Empty); + OnCheckStateChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckedChangedDescr)] + public event EventHandler CheckedChanged { + add { + Events.AddHandler(EventCheckedChanged, value); + } + remove { + Events.RemoveHandler(EventCheckedChanged, value); + } + } + /// + /// + /// Occurs when the + /// value of the + /// property changes. + /// + [SRDescription(SR.CheckBoxOnCheckStateChangedDescr)] + public event EventHandler CheckStateChanged { + add { + Events.AddHandler(EventCheckStateChanged, value); + } + remove { + Events.RemoveHandler(EventCheckStateChanged, value); + } + } + + + /// + /// + /// Specifies whether or not the item is glued to the winbar or overflow or + /// can float between the two. + /// + [ + DefaultValue(ToolStripItemOverflow.Never), + SRDescription(SR.ToolStripItemOverflowDescr), + SRCategory(SR.CatLayout) + ] + public new ToolStripItemOverflow Overflow { + get { + return base.Overflow; + } + set { + base.Overflow = value; + } + } + + /// + /// + /// + /// Gets or sets the shortcut keys associated with the menu + /// item. + /// + /// + [ + Localizable(true), + DefaultValue(Keys.None), + SRDescription(SR.MenuItemShortCutDescr) + ] + public Keys ShortcutKeys { + get { + bool found = false; + object shortcutKeys = Properties.GetInteger(PropShortcutKeys, out found ); + return (found) ? (Keys)shortcutKeys : Keys.None; + } + set { + if ((value != Keys.None) && !ToolStripManager.IsValidShortcut(value)){ + // prevent use of alt, ctrl, shift modifiers with no key code. + throw new InvalidEnumArgumentException("value", (int)value, typeof(Keys)); + } + Keys originalShortcut = ShortcutKeys; + if (originalShortcut != value) { + ClearShortcutCache(); + ToolStrip owner = this.Owner; + if (owner != null) { + // add to the shortcut caching system. + if (originalShortcut != Keys.None) { + owner.Shortcuts.Remove(originalShortcut); + } + if (owner.Shortcuts.Contains(value)) { + // last one in wins. + owner.Shortcuts[value] = this; + } + else { + owner.Shortcuts.Add(value, this); + } + } + Properties.SetInteger(PropShortcutKeys, (int)value); + + if (ShowShortcutKeys && IsOnDropDown) { + ToolStripDropDownMenu parent = GetCurrentParentDropDown() as ToolStripDropDownMenu; + if (parent != null) { + LayoutTransaction.DoLayout(this.ParentInternal, this, "ShortcutKeys"); + parent.AdjustSize(); + } + } + } + } + + } + + [ + SRDescription(SR.ToolStripMenuItemShortcutKeyDisplayStringDescr), + SRCategory(SR.CatAppearance), + DefaultValue(null), + Localizable(true) + ] + public string ShortcutKeyDisplayString { + get { + return shortcutKeyDisplayString; + } + set { + if (shortcutKeyDisplayString != value) { + shortcutKeyDisplayString = value; + ClearShortcutCache(); + if (ShowShortcutKeys) { + ToolStripDropDown parent = this.ParentInternal as ToolStripDropDown; + if (parent != null) { + LayoutTransaction.DoLayout(parent, this, "ShortcutKeyDisplayString"); + parent.AdjustSize(); + } + } + } + } + } + + /// + /// + /// + /// Gets or sets a value that indicates whether the shortcut + /// keys that are assocaited + /// with the menu item are displayed next to the menu item + /// caption. + /// + /// + [ + DefaultValue(true), + Localizable(true), + SRDescription(SR.MenuItemShowShortCutDescr) + ] + public bool ShowShortcutKeys { + get { + return showShortcutKeys; + } + set { + if (value != showShortcutKeys) { + ClearShortcutCache(); + showShortcutKeys = value; + ToolStripDropDown parent = this.ParentInternal as ToolStripDropDown; + if (parent != null) { + LayoutTransaction.DoLayout(parent, this, "ShortcutKeys"); + parent.AdjustSize(); + + } + } + } + } + + + /// + /// An item is toplevel if it is parented to anything other than a ToolStripDropDownMenu + /// This implies that a ToolStripMenuItem in an overflow IS a toplevel item + /// + internal bool IsTopLevel { + get { + return (this.ParentInternal as ToolStripDropDown == null); + } + } + + [Browsable(false)] + public bool IsMdiWindowListEntry { + get{ + return MdiForm != null; + } + } + + internal static MenuTimer MenuTimer { + get { + return menuTimer; + } + } + + + /// Tag property for internal use + internal Form MdiForm { + get { + if (Properties.ContainsObject(PropMdiForm)) { + return Properties.GetObject(PropMdiForm) as Form; + } + return null; + + } + } + + internal ToolStripMenuItem Clone() { + + // dirt simple clone - just properties, no subitems + + ToolStripMenuItem menuItem = new ToolStripMenuItem(); + menuItem.Events.AddHandlers(this.Events); + + menuItem.AccessibleName = this.AccessibleName; + menuItem.AccessibleRole = this.AccessibleRole; + menuItem.Alignment = this.Alignment; + menuItem.AllowDrop = this.AllowDrop; + menuItem.Anchor = this.Anchor; + menuItem.AutoSize = this.AutoSize; + menuItem.AutoToolTip = this.AutoToolTip; + menuItem.BackColor = this.BackColor; + menuItem.BackgroundImage = this.BackgroundImage; + menuItem.BackgroundImageLayout = this.BackgroundImageLayout; + menuItem.Checked = this.Checked; + menuItem.CheckOnClick = this.CheckOnClick; + menuItem.CheckState = this.CheckState; + menuItem.DisplayStyle = this.DisplayStyle; + menuItem.Dock = this.Dock; + menuItem.DoubleClickEnabled = this.DoubleClickEnabled; + menuItem.Enabled = this.Enabled; + menuItem.Font = this.Font; + menuItem.ForeColor = this.ForeColor; + menuItem.Image = this.Image; + menuItem.ImageAlign = this.ImageAlign; + menuItem.ImageScaling = this.ImageScaling; + menuItem.ImageTransparentColor = this.ImageTransparentColor; + menuItem.Margin = this.Margin; + menuItem.MergeAction = this.MergeAction; + menuItem.MergeIndex = this.MergeIndex; + menuItem.Name = this.Name; + menuItem.Overflow = this.Overflow; + menuItem.Padding = this.Padding; + menuItem.RightToLeft= this.RightToLeft; + + // No settings support for cloned items. + // menuItem.SaveSettings= this.SaveSettings; + // menuItem.SettingsKey = this.SettingsKey; + + menuItem.ShortcutKeys = this.ShortcutKeys; + menuItem.ShowShortcutKeys = this.ShowShortcutKeys; + menuItem.Tag = this.Tag; + menuItem.Text = this.Text; + menuItem.TextAlign = this.TextAlign; + menuItem.TextDirection = this.TextDirection; + menuItem.TextImageRelation = this.TextImageRelation; + menuItem.ToolTipText = this.ToolTipText; + + // cant actually use "Visible" property as that returns whether or not the parent + // is visible too.. instead use ParticipatesInLayout as this queries the actual state. + menuItem.Visible = ((IArrangedElement)this).ParticipatesInLayout; + + if (!AutoSize) { + menuItem.Size = this.Size; + } + return menuItem; + } + + internal override int DeviceDpi { + get { + return base.DeviceDpi; + } + + // This gets called via ToolStripItem.RescaleConstantsForDpi. + // It's practically calling Initialize on DpiChanging with the new Dpi value. + // ToolStripItem.RescaleConstantsForDpi is already behind a quirk. + set { + base.DeviceDpi = value; + scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, value); + scaledDefaultDropDownPadding = DpiHelper.LogicalToDeviceUnits(defaultDropDownPadding, value); + scaledCheckMarkBitmapSize = DpiHelper.LogicalToDeviceUnits(checkMarkBitmapSize, value); + } + } + + protected override void Dispose(bool disposing) { + if (disposing) { + if (lastOwner != null) { + Keys shortcut = this.ShortcutKeys; + if (shortcut != Keys.None && lastOwner.Shortcuts.ContainsKey(shortcut)) { + lastOwner.Shortcuts.Remove(shortcut); + } + lastOwner = null; + if (MdiForm != null) { + Properties.SetObject(PropMdiForm,null); + } + + } + } + base.Dispose(disposing); + } + + private bool GetNativeMenuItemEnabled() { + if (nativeMenuCommandID == -1 || nativeMenuHandle == IntPtr.Zero) { + Debug.Fail("why were we called to fetch native menu item info with nothing assigned?"); + return false; + } + NativeMethods.MENUITEMINFO_T_RW info = new NativeMethods.MENUITEMINFO_T_RW(); + info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW)); + info.fMask = NativeMethods.MIIM_STATE; + info.fType = NativeMethods.MIIM_STATE; + info.wID = nativeMenuCommandID; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(this, nativeMenuHandle), nativeMenuCommandID, /*fByPosition instead of ID=*/ false, info); + + return ((info.fState & NativeMethods.MFS_DISABLED) == 0); + } + + // returns text and shortcut separated by tab. + private string GetNativeMenuItemTextAndShortcut() { + + if (nativeMenuCommandID == -1 || nativeMenuHandle == IntPtr.Zero) { + Debug.Fail("why were we called to fetch native menu item info with nothing assigned?"); + return null; + } + string text = null; + + // fetch the string length + NativeMethods.MENUITEMINFO_T_RW info = new NativeMethods.MENUITEMINFO_T_RW(); + info.fMask = NativeMethods.MIIM_STRING; + info.fType = NativeMethods.MIIM_STRING; + info.wID = nativeMenuCommandID; + info.dwTypeData = IntPtr.Zero; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(this, nativeMenuHandle), nativeMenuCommandID, /*fByPosition instead of ID=*/ false, info); + + if (info.cch > 0) { + // fetch the string + info.cch += 1; // according to MSDN we need to increment the count we receive by 1. + info.wID = nativeMenuCommandID; + IntPtr allocatedStringBuffer = Marshal.AllocCoTaskMem(info.cch * Marshal.SystemDefaultCharSize); + info.dwTypeData = allocatedStringBuffer; + + + try { + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(this, nativeMenuHandle), nativeMenuCommandID, /*fByPosition instead of ID=*/ false, info); + + // convert the string into managed data. + if (info.dwTypeData != IntPtr.Zero){ + // we have to use PtrToStringAuto as we can't use Marshal.SizeOf to determine + // the size of the struct with a StringBuilder member. + text = Marshal.PtrToStringAuto(info.dwTypeData, info.cch); + } + } + finally { + if (allocatedStringBuffer != IntPtr.Zero) { + // use our local instead of the info structure member *just* in case windows decides to clobber over it. + // we want to be sure to deallocate the memory we know we allocated. + Marshal.FreeCoTaskMem(allocatedStringBuffer); + } + } + } + return text; + } + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine | ResourceScope.Process, ResourceScope.Machine | ResourceScope.Process)] + private Image GetNativeMenuItemImage(){ + + if (nativeMenuCommandID == -1 || nativeMenuHandle == IntPtr.Zero) { + Debug.Fail("why were we called to fetch native menu item info with nothing assigned?"); + return null; + } + + NativeMethods.MENUITEMINFO_T_RW info = new NativeMethods.MENUITEMINFO_T_RW(); + info.fMask = NativeMethods.MIIM_BITMAP; + info.fType = NativeMethods.MIIM_BITMAP; + info.wID = nativeMenuCommandID; + UnsafeNativeMethods.GetMenuItemInfo(new HandleRef(this, nativeMenuHandle), nativeMenuCommandID, /*fByPosition instead of ID=*/ false, info); + + if (info.hbmpItem != IntPtr.Zero && info.hbmpItem.ToInt32() > NativeMethods.HBMMENU_POPUP_MINIMIZE) { + return Bitmap.FromHbitmap(info.hbmpItem); + } + else { + // its a system defined bitmap + int buttonToUse = -1; + + switch (info.hbmpItem.ToInt32()) { + case NativeMethods.HBMMENU_MBAR_CLOSE: + case NativeMethods.HBMMENU_MBAR_CLOSE_D: + case NativeMethods.HBMMENU_POPUP_CLOSE: + buttonToUse = (int)CaptionButton.Close; + break; + + case NativeMethods.HBMMENU_MBAR_MINIMIZE: + case NativeMethods.HBMMENU_MBAR_MINIMIZE_D: + case NativeMethods.HBMMENU_POPUP_MINIMIZE: + buttonToUse = (int)CaptionButton.Minimize; + break; + + case NativeMethods.HBMMENU_MBAR_RESTORE: + case NativeMethods.HBMMENU_POPUP_RESTORE: + buttonToUse = (int)CaptionButton.Restore; + break; + + case NativeMethods.HBMMENU_POPUP_MAXIMIZE: + buttonToUse = (int)CaptionButton.Maximize; + break; + + case NativeMethods.HBMMENU_SYSTEM: + // + case NativeMethods.HBMMENU_CALLBACK: + // owner draw not supported + default: + break; + } + if (buttonToUse > -1) { + + // we've mapped to a system defined bitmap we know how to draw + Bitmap image = new Bitmap(16, 16); + + using (Graphics g = Graphics.FromImage(image)) { + ControlPaint.DrawCaptionButton(g, new Rectangle(Point.Empty, image.Size), (CaptionButton)buttonToUse, ButtonState.Flat); + g.DrawRectangle(SystemPens.Control, 0, 0, image.Width - 1, image.Height - 1); + } + + image.MakeTransparent(SystemColors.Control); + return image; + } + } + return null; + } + + + internal Size GetShortcutTextSize() { + if (!ShowShortcutKeys) { + return Size.Empty; + } + string shortcutString = GetShortcutText(); + if (string.IsNullOrEmpty(shortcutString)) { + return Size.Empty; + } + else if (cachedShortcutSize == Size.Empty) { + cachedShortcutSize = TextRenderer.MeasureText(shortcutString, Font); + } + return cachedShortcutSize; + } + + internal string GetShortcutText() { + if (cachedShortcutText == null) { + cachedShortcutText = ShortcutToText(this.ShortcutKeys, this.ShortcutKeyDisplayString); + } + return cachedShortcutText; + } + + internal void HandleAutoExpansion() { + if (Enabled && ParentInternal != null && ParentInternal.MenuAutoExpand && HasDropDownItems) { + ShowDropDown(); + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + DropDown.SelectNextToolStripItem(null, /*forward=*/true); + } + } + + /// + protected override void OnClick(EventArgs e) { + if (checkOnClick) { + this.Checked = !this.Checked; + } + base.OnClick(e); + if (nativeMenuCommandID != -1) { + // fire off the appropriate native handler by posting a message to the window target. + if ((nativeMenuCommandID & 0xF000) != 0) { + // These are system menu items like Minimize, Maximize, Restore, Resize, Move, Close. + + // use PostMessage instead of SendMessage so that the DefWndProc can appropriately handle + // the system message... if we use SendMessage the dismissal of our window + // breaks things like the modal sizing loop. + UnsafeNativeMethods.PostMessage( new HandleRef(this, targetWindowHandle), NativeMethods.WM_SYSCOMMAND,nativeMenuCommandID, 0); + } + else { + // These are user added items like ".Net Window..." + + // be consistent with sending a WM_SYSCOMMAND, use POST not SEND. + UnsafeNativeMethods.PostMessage( new HandleRef(this, targetWindowHandle), NativeMethods.WM_COMMAND, nativeMenuCommandID, 0); + } + this.Invalidate(); + } + + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnCheckedChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EventCheckedChanged]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnCheckStateChanged(EventArgs e) { + AccessibilityNotifyClients(AccessibleEvents.StateChange); + EventHandler handler = (EventHandler)Events[EventCheckStateChanged]; + if (handler != null) handler(this,e); + } + + /// + protected override void OnDropDownHide(EventArgs e) { + + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripMenuItem.OnDropDownHide] MenuTimer.Cancel called"); + MenuTimer.Cancel(this); + base.OnDropDownHide(e); + } + + protected override void OnDropDownShow(EventArgs e) { + // if someone has beaten us to the punch by arrowing around + // cancel the current menu timer. + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripMenuItem.OnDropDownShow] MenuTimer.Cancel called"); + MenuTimer.Cancel(this); + if (ParentInternal != null) { + ParentInternal.MenuAutoExpand = true; + } + base.OnDropDownShow(e); + } + + protected override void OnFontChanged(EventArgs e) { + ClearShortcutCache(); + base.OnFontChanged(e); + } + /// + internal void OnMenuAutoExpand() { + this.ShowDropDown(); + } + + + /// + /// + protected override void OnMouseDown(MouseEventArgs e) { + + // Opening should happen on mouse down + // we use a mouse down ID to ensure that the reshow + + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripMenuItem.OnMouseDown] MenuTimer.Cancel called"); + MenuTimer.Cancel(this); + OnMouseButtonStateChange(e, /*isMouseDown=*/true); + + } + + protected override void OnMouseUp(MouseEventArgs e) { + OnMouseButtonStateChange(e, /*isMouseDown=*/false); + base.OnMouseUp(e); + } + + private void OnMouseButtonStateChange(MouseEventArgs e, bool isMouseDown) { + bool showDropDown = true; + if (IsOnDropDown) { + ToolStripDropDown dropDown = GetCurrentParentDropDown() as ToolStripDropDown; + + // VSWhidbey 260536 - right click support for context menus. + // used in ToolStripItem to determine whether to fire click OnMouseUp. + SupportsRightClick = (dropDown.GetFirstDropDown() is ContextMenuStrip); + } + else { + showDropDown = !DropDown.Visible; + SupportsRightClick = false; + } + + if (e.Button == MouseButtons.Left || + (e.Button == MouseButtons.Right && SupportsRightClick)) { + + if (isMouseDown && showDropDown) { + // opening should happen on mouse down. + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + this.ShowDropDown(/*mousePush =*/true); + + } + else if (!isMouseDown && !showDropDown) { + // closing should happen on mouse up. ensure it's not the mouse + // up for the mouse down we opened with. + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + int openedMouseID =openMouseId; + if (closeMouseId != openedMouseID) { + openMouseId = 0; // reset the mouse id, we should never get this value from toolstrip. + ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); + Select(); + } + + } + + } + } + + + + /// + /// + protected override void OnMouseEnter(EventArgs e) { + + Debug.Assert(this.ParentInternal != null, "Why is parent null"); + + // If we are in a submenu pop down the submenu. + if (this.ParentInternal != null && this.ParentInternal.MenuAutoExpand && Selected) { + Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "received mouse enter - calling drop down"); + + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripMenuItem.OnMouseEnter] MenuTimer.Cancel / MenuTimer.Start called"); + + MenuTimer.Cancel(this); + MenuTimer.Start(this); + + } + base.OnMouseEnter(e); + } + + + /// + /// + protected override void OnMouseLeave(EventArgs e) { + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[ToolStripMenuItem.OnMouseLeave] MenuTimer.Cancel called"); + MenuTimer.Cancel(this); + base.OnMouseLeave(e); + } + + protected override void OnOwnerChanged(EventArgs e) { + + Keys shortcut = this.ShortcutKeys; + if (shortcut != Keys.None) { + if (lastOwner != null) { + lastOwner.Shortcuts.Remove(shortcut); + } + + if (Owner != null) { + if (Owner.Shortcuts.Contains(shortcut)) { + // last one in wins + Owner.Shortcuts[shortcut] = this; + } + else { + Owner.Shortcuts.Add(shortcut, this); + } + lastOwner = Owner; + } + } + + base.OnOwnerChanged(e); + } + + /// + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + if (this.Owner != null) { + ToolStripRenderer renderer = this.Renderer; + Graphics g = e.Graphics; + renderer.DrawMenuItemBackground(new ToolStripItemRenderEventArgs(g, this)); + + Color textColor = SystemColors.MenuText; + if (IsForeColorSet) { + textColor = this.ForeColor; + } + else if (!this.IsTopLevel || (ToolStripManager.VisualStylesEnabled)) { + if (Selected || Pressed) { + textColor = SystemColors.HighlightText; + } + else { + textColor = SystemColors.MenuText; + } + } + + bool rightToLeft = (RightToLeft == RightToLeft.Yes); + + ToolStripMenuItemInternalLayout menuItemInternalLayout = this.InternalLayout as ToolStripMenuItemInternalLayout; + if (menuItemInternalLayout != null && menuItemInternalLayout.UseMenuLayout) { + + // Support for special DropDownMenu layout + #if DEBUG_PAINT + g.DrawRectangle(Pens.Green, menuItemInternalLayout.TextRectangle); + g.DrawRectangle(Pens.HotPink, menuItemInternalLayout.ImageRectangle); + g.DrawRectangle(Pens.Black, menuItemInternalLayout.CheckRectangle); + g.DrawRectangle(Pens.Red, menuItemInternalLayout.ArrowRectangle); + g.DrawRectangle(Pens.Blue, new Rectangle(Point.Empty, new Size(-1,-1) + this.Size)); + #endif + if (CheckState != CheckState.Unchecked && menuItemInternalLayout.PaintCheck) { + Rectangle checkRectangle = menuItemInternalLayout.CheckRectangle; + if (!menuItemInternalLayout.ShowCheckMargin) { + checkRectangle = menuItemInternalLayout.ImageRectangle; + } + if (checkRectangle.Width != 0) { + renderer.DrawItemCheck(new ToolStripItemImageRenderEventArgs(g, this, CheckedImage, checkRectangle)); + } + } + + + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + + // render text AND shortcut + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, this.Text, InternalLayout.TextRectangle, textColor, this.Font, (rightToLeft) ? ContentAlignment.MiddleRight : ContentAlignment.MiddleLeft)); + bool showShortCut = ShowShortcutKeys; + if (!DesignMode){ + showShortCut = showShortCut && !HasDropDownItems; + } + + if (showShortCut) { + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, GetShortcutText(), InternalLayout.TextRectangle, textColor, this.Font, (rightToLeft) ? ContentAlignment.MiddleLeft : ContentAlignment.MiddleRight)); + } + } + + if (HasDropDownItems) { + + ArrowDirection arrowDir = (rightToLeft) ? ArrowDirection.Left : ArrowDirection.Right; + Color arrowColor = (Selected ||Pressed) ? SystemColors.HighlightText : SystemColors.MenuText; + arrowColor = (Enabled) ? arrowColor : SystemColors.ControlDark; + renderer.DrawArrow(new ToolStripArrowRenderEventArgs(g, this, menuItemInternalLayout.ArrowRectangle, arrowColor, arrowDir)); + } + + if (menuItemInternalLayout.PaintImage && (DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image && Image != null) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, InternalLayout.ImageRectangle)); + } + + } + else { + + + // Toplevel item support, menu items hosted on a plain winbar dropdown + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, this.Text, InternalLayout.TextRectangle, textColor, this.Font, InternalLayout.TextFormat)); + } + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image && Image != null) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, InternalLayout.ImageRectangle)); + } + } + + + } + + } + + /// + /// + /// handle shortcut keys here. + /// + [ + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) { + + if (Enabled && ShortcutKeys == keyData && !HasDropDownItems) { + FireEvent(ToolStripItemEventType.Click); + return true; + } + + // call base here to get ESC, ALT, etc.. handling. + return base.ProcessCmdKey(ref m, keyData); + } + + + /// + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + protected internal override bool ProcessMnemonic(char charCode) { + // no need to check IsMnemonic, toolstrip.ProcessMnemonic checks this already. + if (HasDropDownItems) { + Select(); + ShowDropDown(); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this); + } + + DropDown.SelectNextToolStripItem(null, /*forward=*/true); + return true; + } + + return base.ProcessMnemonic(charCode); + } + + /// + /// overridden here so we scooch over when we're in the ToolStripDropDownMenu + internal protected override void SetBounds(Rectangle rect) { + + ToolStripMenuItemInternalLayout internalLayout = InternalLayout as ToolStripMenuItemInternalLayout; + if (internalLayout != null && internalLayout.UseMenuLayout) { + ToolStripDropDownMenu dropDownMenu = Owner as ToolStripDropDownMenu; + + // Scooch over by the padding amount. The padding is added to + // the ToolStripDropDownMenu to keep the non-menu item riffraff + // aligned to the text rectangle. When flow layout comes through to set our position + // via IArrangedElement DEFY IT! + if (dropDownMenu != null) { + rect.X -= dropDownMenu.Padding.Left; + rect.X = Math.Max(rect.X,0); + } + } + base.SetBounds(rect); + } + + /// this is to support routing to native menu commands + internal void SetNativeTargetWindow(IWin32Window window) { + targetWindowHandle = Control.GetSafeHandle(window); + } + + /// this is to support routing to native menu commands + internal void SetNativeTargetMenu(IntPtr hMenu) { + nativeMenuHandle = hMenu; + } + internal static string ShortcutToText(Keys shortcutKeys, string shortcutKeyDisplayString) { + if (!string.IsNullOrEmpty(shortcutKeyDisplayString)) { + return shortcutKeyDisplayString; + } + else if (shortcutKeys == Keys.None) { + return String.Empty; + } + else { + return TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(shortcutKeys); + } + } + + internal override bool IsBeingTabbedTo() { + if (base.IsBeingTabbedTo()) { + return true; + } + + if (ToolStripManager.ModalMenuFilter.InMenuMode) { + return true; + } + + return false; + } + + /// + /// An implementation of AccessibleChild for use with ToolStripItems + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripMenuItemAccessibleObject : ToolStripDropDownItemAccessibleObject { + private ToolStripMenuItem ownerItem = null; + + public ToolStripMenuItemAccessibleObject(ToolStripMenuItem ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + + public override AccessibleStates State { + get { + if (ownerItem.Enabled ) { + AccessibleStates state = base.State; + + if ((state & AccessibleStates.Pressed) == AccessibleStates.Pressed){ + // for some reason menu items are never "pressed". + state &= ~AccessibleStates.Pressed; + } + + if (ownerItem.Checked) { + state |= AccessibleStates.Checked; + } + return state; + } + return base.State; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_MenuItemControlTypeId; + } + else if (AccessibilityImprovements.Level2 && propertyID == NativeMethods.UIA_AcceleratorKeyPropertyId) { + return ownerItem.GetShortcutText(); + } + else { + return base.GetPropertyValue(propertyID); + } + } + } + } + + + internal class MenuTimer { + + private System.Windows.Forms.Timer autoMenuExpandTimer = new System.Windows.Forms.Timer(); + + // consider - weak reference? + private ToolStripMenuItem currentItem = null; + private ToolStripMenuItem fromItem = null; + private bool inTransition = false; + + private int quickShow = 1; + + private int slowShow; + + public MenuTimer() { + // MenuShowDelay can be set to 0. In this case, set to something low so it's inperceptable. + autoMenuExpandTimer.Tick += new EventHandler(OnTick); + + // since MenuShowDelay is registry tweakable we've gotta make sure we've got some sort + // of interval + slowShow = Math.Max(quickShow, SystemInformation.MenuShowDelay); + + } + // the current item to autoexpand. + private ToolStripMenuItem CurrentItem { + get { + return currentItem; + } + set{ + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose && currentItem != value, "[MenuTimer.CurrentItem] changed: " + ((value == null) ? "null" : value.ToString())); + currentItem = value; + } + } + public bool InTransition { + get { return inTransition; } + set { inTransition = value; } + } + + public void Start(ToolStripMenuItem item) { + if (InTransition) { + return; + } + StartCore(item); + } + + private void StartCore(ToolStripMenuItem item) { + if (item != CurrentItem) { + Cancel(CurrentItem); + } + CurrentItem = item; + if (item != null) { + CurrentItem = item; + autoMenuExpandTimer.Interval = item.IsOnDropDown ? slowShow : quickShow; + autoMenuExpandTimer.Enabled = true; + } + } + + public void Transition(ToolStripMenuItem fromItem, ToolStripMenuItem toItem) { + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[MenuTimer.Transition] transitioning items " + fromItem.ToString() + " " + toItem.ToString()); + + if (toItem == null && InTransition) { + Cancel(); + // in this case we're likely to have hit an item that's not a menu item + // or nothing is selected. + EndTransition(/*forceClose*/ true); + return; + } + + if (this.fromItem != fromItem) { + this.fromItem = fromItem; + CancelCore(); + StartCore(toItem); + } + // set up the current item to be the toItem so it will be auto expanded when complete. + CurrentItem = toItem; + InTransition = true; + + } + + + public void Cancel() { + if (InTransition) { + return; + } + CancelCore(); + + } + /// cancels if and only if this item was the one that + /// requested the timer + /// + public void Cancel(ToolStripMenuItem item) { + if (InTransition) { + return; + } + if (item == CurrentItem) { + CancelCore(); + } + } + + private void CancelCore() { + autoMenuExpandTimer.Enabled = false; + CurrentItem = null; + } + private void EndTransition(bool forceClose) { + ToolStripMenuItem lastSelected = fromItem; + fromItem = null; // immediately clear BEFORE we call user code. + if (InTransition) { + InTransition = false; + + // we should roolup if the current item has changed and is selected. + bool rollup = forceClose || (CurrentItem != null && CurrentItem != lastSelected && CurrentItem.Selected); + if (rollup && lastSelected != null && lastSelected.HasDropDownItems) { + lastSelected.HideDropDown(); + } + + } + + } + internal void HandleToolStripMouseLeave(ToolStrip toolStrip) { + if (InTransition && toolStrip == fromItem.ParentInternal) { + // restore the selection back to CurrentItem. + // we're about to fall off the edge of the toolstrip, something should be selected + // at all times while we're InTransition mode - otherwise it looks really funny + // to have an auto expanded item + if (CurrentItem != null) { + CurrentItem.Select(); + } + } + else { + + // because we've split up selected/pressed, we need to make sure + // that onmouseleave we make sure there's a selected menu item. + if (toolStrip.IsDropDown && toolStrip.ActiveDropDowns.Count > 0) { + ToolStripDropDown dropDown = toolStrip.ActiveDropDowns[0] as ToolStripDropDown; + + ToolStripMenuItem menuItem = (dropDown == null) ? null : dropDown.OwnerItem as ToolStripMenuItem; + if (menuItem != null && menuItem.Pressed) { + menuItem.Select(); + } + } + } + } + + private void OnTick(object sender, EventArgs e) { + autoMenuExpandTimer.Enabled = false; + + if (CurrentItem == null) { + return; + } + EndTransition(/*forceClose*/false); + if (CurrentItem != null && !CurrentItem.IsDisposed && CurrentItem.Selected && CurrentItem.Enabled && ToolStripManager.ModalMenuFilter.InMenuMode) { + Debug.WriteLineIf(ToolStrip.MenuAutoExpandDebug.TraceVerbose, "[MenuTimer.OnTick] calling OnMenuAutoExpand"); + CurrentItem.OnMenuAutoExpand(); + } + } + + + } + + internal class ToolStripMenuItemInternalLayout : ToolStripItemInternalLayout { + private ToolStripMenuItem ownerItem; + + public ToolStripMenuItemInternalLayout(ToolStripMenuItem ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + public bool ShowCheckMargin { + get{ + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + return menu.ShowCheckMargin; + } + return false; + } + } + public bool ShowImageMargin { + get{ + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + return menu.ShowImageMargin; + } + return false; + } + } + + public bool PaintCheck { + get { + return ShowCheckMargin || ShowImageMargin; + } + } + + public bool PaintImage { + get { + return ShowImageMargin; + } + } + public Rectangle ArrowRectangle { + get { + if (UseMenuLayout) { + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + // since menuItem.Padding isnt taken into consideration, we've got to recalc the centering of + // the arrow rect per item + Rectangle arrowRect = menu.ArrowRectangle; + arrowRect.Y = LayoutUtils.VAlign(arrowRect.Size, ownerItem.ClientBounds, ContentAlignment.MiddleCenter).Y; + return arrowRect; + } + } + return Rectangle.Empty; + } + } + public Rectangle CheckRectangle { + get { + if (UseMenuLayout) { + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + Rectangle checkRectangle = menu.CheckRectangle; + if (ownerItem.CheckedImage != null) { + int imageHeight = ownerItem.CheckedImage.Height; + // make sure we're vertically centered + checkRectangle.Y += (checkRectangle.Height - imageHeight)/2; + checkRectangle.Height = imageHeight; + return checkRectangle; + } + } + } + return Rectangle.Empty; + } + } + public override Rectangle ImageRectangle { + get { + if (UseMenuLayout) { + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + + // since menuItem.Padding isnt taken into consideration, we've got to recalc the centering of + // the image rect per item + Rectangle imageRect = menu.ImageRectangle; + if (ownerItem.ImageScaling == ToolStripItemImageScaling.SizeToFit) { + imageRect.Size = menu.ImageScalingSize; + } + else { + //If we don't have an image, use the CheckedImage + Image image = ownerItem.Image ?? ownerItem.CheckedImage; + imageRect.Size = image.Size; + } + imageRect.Y = LayoutUtils.VAlign(imageRect.Size, ownerItem.ClientBounds, ContentAlignment.MiddleCenter).Y; + return imageRect; + } + } + return base.ImageRectangle; + } + } + + public override Rectangle TextRectangle { + get { + if (UseMenuLayout) { + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + return menu.TextRectangle; + } + } + return base.TextRectangle; + } + } + + public bool UseMenuLayout { + get { + return ownerItem.Owner is ToolStripDropDownMenu; + } + } + + public override Size GetPreferredSize(Size constrainingSize) { + if (UseMenuLayout) { + ToolStripDropDownMenu menu = ownerItem.Owner as ToolStripDropDownMenu; + if (menu != null) { + return menu.MaxItemSize; + } + } + return base.GetPreferredSize(constrainingSize); + } + } + + } diff --git a/WindowsForms/Managed/System/WinForms/ToolStripOverflow.cs b/WindowsForms/Managed/System/WinForms/ToolStripOverflow.cs new file mode 100644 index 000000000..da9c1a8e1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripOverflow.cs @@ -0,0 +1,178 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Windows.Forms.Layout; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices; + + + /// + /// + /// ToolStripOverflow + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + public class ToolStripOverflow : ToolStripDropDown, IArrangedElement { + + + +#if DEBUG + internal static readonly TraceSwitch PopupLayoutDebug = new TraceSwitch("PopupLayoutDebug", "Debug ToolStripPopup Layout code"); +#else + internal static readonly TraceSwitch PopupLayoutDebug; +#endif + + private ToolStripOverflowButton ownerItem; + + /// + public ToolStripOverflow (ToolStripItem parentItem) : base(parentItem) { + if (parentItem == null) { + throw new ArgumentNullException("parentItem"); + } + ownerItem = parentItem as ToolStripOverflowButton; + } + + + /// + protected internal override ToolStripItemCollection DisplayedItems { + get { + if (ParentToolStrip != null) { + ToolStripItemCollection items = ParentToolStrip.OverflowItems; + return items; + } + return new ToolStripItemCollection(null, false); + } + } + + public override ToolStripItemCollection Items { + get { + return new ToolStripItemCollection(null, /*ownedCollection=*/false, /*readonly=*/true); + } + } + + private ToolStrip ParentToolStrip { + get { + if (ownerItem != null) { + return ownerItem.ParentToolStrip; + } + return null; + } + } + + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { return DisplayedItems; } + } + + /// + /// + IArrangedElement IArrangedElement.Container { + get { return ParentInternal; } + } + + /// + /// + bool IArrangedElement.ParticipatesInLayout { + get { return GetState(STATE_VISIBLE); } + } + + + /// + /// + PropertyStore IArrangedElement.Properties { + get { return Properties; } + } + + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified); + } + + /// + /// + /// Summary of CreateLayoutEngine. + /// + /// + public override LayoutEngine LayoutEngine { + get { + return FlowLayout.Instance; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripOverflowAccessibleObject(this); + } + + [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")] + public override Size GetPreferredSize(Size constrainingSize) { + constrainingSize.Width = 200; + return base.GetPreferredSize(constrainingSize); + } + protected override void OnLayout(LayoutEventArgs e) { + + if (ParentToolStrip != null && ParentToolStrip.IsInDesignMode) { + if (FlowLayout.GetFlowDirection(this) != FlowDirection.TopDown) { + FlowLayout.SetFlowDirection(this, FlowDirection.TopDown); + } + if (FlowLayout.GetWrapContents(this)) { + FlowLayout.SetWrapContents(this, false); + } + } + else { + if (FlowLayout.GetFlowDirection(this) != FlowDirection.LeftToRight) { + FlowLayout.SetFlowDirection(this, FlowDirection.LeftToRight); + } + if (!FlowLayout.GetWrapContents(this)) { + FlowLayout.SetWrapContents(this, true); + } + } + base.OnLayout(e); + + } + + /// + protected override void SetDisplayedItems() { + // do nothing here.... this is really for the setting the overflow/displayed items on the + // main winbar. Our working item collection is our displayed item collection... calling + // base would clear it out. + Size biggestItemSize = Size.Empty; + for (int j = 0; j < DisplayedItems.Count; j++) { + ToolStripItem item = DisplayedItems[j]; + if (((IArrangedElement)item).ParticipatesInLayout) { + HasVisibleItems = true; + biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size); + } + } + SetLargestItemSize(biggestItemSize); + } + + internal class ToolStripOverflowAccessibleObject : ToolStripAccessibleObject { + public ToolStripOverflowAccessibleObject(ToolStripOverflow owner) + : base(owner) { + } + + public override AccessibleObject GetChild(int index) { + return ((ToolStripOverflow)Owner).DisplayedItems[index].AccessibilityObject; + } + + public override int GetChildCount() { + return ((ToolStripOverflow)Owner).DisplayedItems.Count; + } + } + + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripOverflowButton.cs b/WindowsForms/Managed/System/WinForms/ToolStripOverflowButton.cs new file mode 100644 index 000000000..aab408026 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripOverflowButton.cs @@ -0,0 +1,176 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Windows.Forms.Design; + + + /// + /// + /// ToolStripOverflowButton + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.None)] + public class ToolStripOverflowButton : ToolStripDropDownButton { + + // we need to cache this away as the Parent property gets reset a lot. + private ToolStrip parentToolStrip; + + private static bool isScalingInitialized = false; + private const int MAX_WIDTH = 16; + private const int MAX_HEIGHT = 16; + private static int maxWidth = MAX_WIDTH; + private static int maxHeight = MAX_HEIGHT; + + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + internal ToolStripOverflowButton(ToolStrip parentToolStrip) { + if (!isScalingInitialized) + { + if (DpiHelper.IsScalingRequired) + { + maxWidth = DpiHelper.LogicalToDeviceUnitsX(MAX_WIDTH); + maxHeight = DpiHelper.LogicalToDeviceUnitsY(MAX_HEIGHT); + } + + isScalingInitialized = true; + } + + SupportsItemClick = false; + this.parentToolStrip = parentToolStrip; + } + + protected override void Dispose(bool disposing) { + if (disposing && this.HasDropDownItems) { + this.DropDown.Dispose(); + } + + base.Dispose(disposing); + } + + /// + protected internal override Padding DefaultMargin { + get { + return Padding.Empty; + } + } + + /// + public override bool HasDropDownItems { + get { + return this.ParentInternal.OverflowItems.Count > 0; + } + } + + + internal override bool OppositeDropDownAlign { + get { return true; } + } + + internal ToolStrip ParentToolStrip { + get { return parentToolStrip; } + } + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool RightToLeftAutoMirrorImage { + get { + return base.RightToLeftAutoMirrorImage; + } + set { + base.RightToLeftAutoMirrorImage = value; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripOverflowButtonAccessibleObject(this); + } + + + /// + protected override ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripOverflow(this); + } + + /// + public override Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = constrainingSize; + if (this.ParentInternal != null) { + if (this.ParentInternal.Orientation == Orientation.Horizontal) { + preferredSize.Width = Math.Min(constrainingSize.Width, maxWidth); + } + else { + preferredSize.Height = Math.Min(constrainingSize.Height, maxHeight); + } + } + return preferredSize + this.Padding.Size; + } + + // make sure the Overflow button extends from edge-edge. (Ignore Padding/Margin). + internal protected override void SetBounds(Rectangle bounds) { + if (ParentInternal != null && ParentInternal.LayoutEngine is ToolStripSplitStackLayout) { + + if (ParentInternal.Orientation == Orientation.Horizontal) { + bounds.Height = ParentInternal.Height; + bounds.Y = 0; + } + else { + bounds.Width = ParentInternal.Width; + bounds.X = 0; + } + } + base.SetBounds(bounds); + } + + /// + protected override void OnPaint(PaintEventArgs e) { + if (this.ParentInternal != null) { + ToolStripRenderer renderer = this.ParentInternal.Renderer; + renderer.DrawOverflowButtonBackground(new ToolStripItemRenderEventArgs(e.Graphics, this)); + } + } + + internal class ToolStripOverflowButtonAccessibleObject : ToolStripDropDownItemAccessibleObject { + private string stockName; + + public ToolStripOverflowButtonAccessibleObject(ToolStripOverflowButton owner) : base(owner){ + } + + + public override string Name { + get { + string name = Owner.AccessibleName; + if (name != null) { + return name; + } + if (string.IsNullOrEmpty(stockName)) { + stockName = SR.GetString(SR.ToolStripOptions); + } + return stockName; + } + set { + base.Name = value; + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_MenuItemControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPanel.cs b/WindowsForms/Managed/System/WinForms/ToolStripPanel.cs new file mode 100644 index 000000000..597d2b45b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPanel.cs @@ -0,0 +1,1613 @@ +//#define DEBUG_PAINT +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +/* +*/ + +namespace System.Windows.Forms { + using System.Drawing; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + using System.ComponentModel.Design.Serialization; + using System.Security.Permissions; + using System.Security; + using System.Globalization; + using System.Windows.Forms.Internal; + + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [Designer("System.Windows.Forms.Design.ToolStripPanelDesigner, " + AssemblyRef.SystemDesign)] + [ToolboxBitmapAttribute(typeof(ToolStripPanel), "ToolStripPanel_standalone.bmp")] + public class ToolStripPanel : ContainerControl, IArrangedElement { + + + private Orientation orientation = Orientation.Horizontal; + private static readonly Padding rowMargin = new Padding(3,0,0,0); + private Padding scaledRowMargin = rowMargin; + private ToolStripRendererSwitcher rendererSwitcher = null; + private Type currentRendererType = typeof(System.Type); + private BitVector32 state = new BitVector32(); + private ToolStripContainer owner; + + +#if DEBUG + internal static TraceSwitch ToolStripPanelDebug = new TraceSwitch("ToolStripPanelDebug", "Debug code for rafting mouse movement"); + internal static TraceSwitch ToolStripPanelFeedbackDebug = new TraceSwitch("ToolStripPanelFeedbackDebug", "Debug code for rafting feedback"); + internal static TraceSwitch ToolStripPanelMissingRowDebug = new TraceSwitch("ToolStripPanelMissingRowDebug", "Debug code for rafting feedback"); + +#else + internal static TraceSwitch ToolStripPanelDebug; + internal static TraceSwitch ToolStripPanelFeedbackDebug; + internal static TraceSwitch ToolStripPanelMissingRowDebug; +#endif + + [ThreadStatic] + private static Rectangle lastFeedbackRect = Rectangle.Empty; + + + // properties + private static readonly int PropToolStripPanelRowCollection = PropertyStore.CreateKey(); + + // states + private static readonly int stateLocked = BitVector32.CreateMask(); + private static readonly int stateBeginInit = BitVector32.CreateMask(stateLocked); + private static readonly int stateChangingZOrder = BitVector32.CreateMask(stateBeginInit); + private static readonly int stateInJoin = BitVector32.CreateMask(stateChangingZOrder); + private static readonly int stateEndInit = BitVector32.CreateMask(stateInJoin); + private static readonly int stateLayoutSuspended = BitVector32.CreateMask(stateEndInit); + private static readonly int stateRightToLeftChanged = BitVector32.CreateMask(stateLayoutSuspended); + + + + // events + internal static readonly Padding DragMargin = new Padding(10); + + private static readonly object EventRendererChanged = new object(); + + /// + public ToolStripPanel() { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledRowMargin = DpiHelper.LogicalToDeviceUnits(rowMargin); + } + + SuspendLayout(); + AutoScaleMode = AutoScaleMode.None; + InitFlowLayout(); + this.AutoSize = true; + this.MinimumSize = Size.Empty; // consider 1,1 + this.state[stateLocked | stateBeginInit | stateChangingZOrder] = false; + this.TabStop = false; + + ToolStripManager.ToolStripPanels.Add(this); + // not setting ControlStyles.AllPaintingInWmPaint as we dont do any painting in OnPaint... all + // is done in OnPaintBackground... so its better to show the rafting container in WM_ERASEBACKGROUND. + SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | /*ControlStyles.AllPaintingInWmPaint |*/ControlStyles.SupportsTransparentBackColor, true); + SetStyle(ControlStyles.Selectable, false); + ResumeLayout(true); + } + + internal ToolStripPanel(ToolStripContainer owner) + : this() { + this.owner = owner; + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + + ] + public override bool AllowDrop { + get { return base.AllowDrop; } + set { base.AllowDrop = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override bool AutoScroll { + get { return base.AutoScroll; } + set { base.AutoScroll = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new Size AutoScrollMargin { + get { return base.AutoScrollMargin; } + set { base.AutoScrollMargin = value; } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public new Size AutoScrollMinSize { + get { return base.AutoScrollMinSize; } + set { base.AutoScrollMinSize = value; } + } + + + /// + [ + DefaultValue(true), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return base.AutoSize; + } + set { + base.AutoSize = value; + } + + } + + + /// + /// Override base AutoSizeChanged to we can change visibility/browsability attributes + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + public new event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + protected override Padding DefaultPadding { + get { return Padding.Empty; } + } + protected override Padding DefaultMargin { + get { return Padding.Empty; } + } + + public Padding RowMargin { + get { return scaledRowMargin; } + set { + scaledRowMargin = value; + LayoutTransaction.DoLayout(this, this, "RowMargin"); + } + } + + /// + public override DockStyle Dock { + get { + return base.Dock; + } + set { + base.Dock = value; + + if (value == DockStyle.Left || value == DockStyle.Right) { + Orientation = Orientation.Vertical; + } + else { + Orientation = Orientation.Horizontal; + } + } + } + + internal Rectangle DragBounds { + get { + return LayoutUtils.InflateRect(this.ClientRectangle, DragMargin); + } + } + + internal bool IsInDesignMode { + get { + return DesignMode; + } + + } + + /// + public override LayoutEngine LayoutEngine { + get { + return FlowLayout.Instance; + } + } + + /// + [ + DefaultValue(false), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + public bool Locked { + get { + return state[stateLocked]; + } + set { + state[stateLocked] = value; + } + } + + + /// + public Orientation Orientation { + get { + return orientation; + } + set { + if (orientation != value) { + orientation = value; + scaledRowMargin = LayoutUtils.FlipPadding(scaledRowMargin); + InitFlowLayout(); + foreach (ToolStripPanelRow row in this.RowsInternal) { + row.OnOrientationChanged(); + } + } + } + } + + private ToolStripRendererSwitcher RendererSwitcher { + get { + if (rendererSwitcher == null) { + rendererSwitcher = new ToolStripRendererSwitcher(this); + HandleRendererChanged(this, EventArgs.Empty); + rendererSwitcher.RendererChanged += new EventHandler(HandleRendererChanged); + } + return rendererSwitcher; + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public ToolStripRenderer Renderer { + get { + return RendererSwitcher.Renderer; + } + set { + RendererSwitcher.Renderer = value; + } + } + + + /// + [ + SRDescription(SR.ToolStripRenderModeDescr), + SRCategory(SR.CatAppearance), + ] + public ToolStripRenderMode RenderMode { + get { + return RendererSwitcher.RenderMode; + } + set { + RendererSwitcher.RenderMode = value; + } + } + + [SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripRendererChanged)] + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public event EventHandler RendererChanged { + add { + Events.AddHandler(EventRendererChanged, value); + } + remove { + Events.RemoveHandler(EventRendererChanged, value); + } + } + + /// + /// + /// Collection of child controls. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + SRDescription(SR.ToolStripPanelRowsDescr) + ] + internal ToolStripPanelRowCollection RowsInternal { + get { + ToolStripPanelRowCollection rowCollection = (ToolStripPanelRowCollection)Properties.GetObject(PropToolStripPanelRowCollection); + + if (rowCollection == null) { + rowCollection = CreateToolStripPanelRowCollection(); + Properties.SetObject(PropToolStripPanelRowCollection, rowCollection); + } + return rowCollection; + } + } + + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ToolStripPanelRowsDescr), + SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays") + ] + + public ToolStripPanelRow[] Rows { + get { + ToolStripPanelRow[] rows = new ToolStripPanelRow[RowsInternal.Count]; + RowsInternal.CopyTo(rows,0); + return rows; + } + } + + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new int TabIndex { + get { + return base.TabIndex; + } + set { + base.TabIndex = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabIndexChanged { + add { + base.TabIndexChanged += value; + } + remove { + base.TabIndexChanged -= value; + } + } + + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new bool TabStop { + get { + return base.TabStop; + } + set { + if (AccessibilityImprovements.Level2) { + SetStyle(ControlStyles.Selectable, value); + } + base.TabStop = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabStopChanged { + add { + base.TabStopChanged += value; + } + remove { + base.TabStopChanged -= value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + + #region ISupportInitialize + + public void BeginInit() { + state[stateBeginInit] = true; + } + + + public void EndInit() { + state[stateBeginInit] = false; + state[stateEndInit] = true; + try { + if (!state[stateInJoin]) { + JoinControls(); + } + } + finally { + state[stateEndInit] = false; + } + + } + + #endregion ISupportInitialize + + + /// + private ToolStripPanelRowCollection CreateToolStripPanelRowCollection() { + return new ToolStripPanelRowCollection(this); + } + + protected override Control.ControlCollection CreateControlsInstance() { + return new ToolStripPanelControlCollection(this); + } + + + + /// + /// + /// Disposes of the resources (other than memory) used by + /// the + /// . + /// + protected override void Dispose(bool disposing) { + if (disposing) { + ToolStripManager.ToolStripPanels.Remove(this); + } + base.Dispose(disposing); + } + + private void InitFlowLayout() { + if (Orientation == Orientation.Horizontal) { + FlowLayout.SetFlowDirection(this, FlowDirection.TopDown); + } + else { + FlowLayout.SetFlowDirection(this, FlowDirection.LeftToRight); + } + FlowLayout.SetWrapContents(this, false); + + } + private Point GetStartLocation(ToolStrip toolStripToDrag) { + if (toolStripToDrag.IsCurrentlyDragging + && Orientation == Orientation.Horizontal + && toolStripToDrag.RightToLeft == RightToLeft.Yes ) { + // the grip is on the right side, not left. + return new Point(toolStripToDrag.Right,toolStripToDrag.Top); + } + return toolStripToDrag.Location; + } + + private void HandleRendererChanged(object sender, EventArgs e) { + OnRendererChanged(e); + } + + + + +#if DEBUG_PAINT + /// + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + Graphics g = e.Graphics; + foreach (ToolStripPanelRow row in this.RowsInternal) { + g.DrawRectangle(SystemPens.Highlight, row.Bounds); + row.PaintColumns(e); + } + } +#endif + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnPaintBackground(PaintEventArgs e) { + ToolStripPanelRenderEventArgs rea = new ToolStripPanelRenderEventArgs(e.Graphics, this); + Renderer.DrawToolStripPanelBackground(rea); + + if (!rea.Handled) { + base.OnPaintBackground(e); + } + } + + protected override void OnControlAdded(ControlEventArgs e) { + base.OnControlAdded(e); + + if (!state[stateBeginInit] && !state[stateInJoin]) { + if (!state[stateLayoutSuspended]) { + this.Join(e.Control as ToolStrip, e.Control.Location); + } + else { + BeginInit(); + } + } + + } + + protected override void OnControlRemoved(ControlEventArgs e) { + ISupportToolStripPanel controlToBeDragged = e.Control as ISupportToolStripPanel; + + if (controlToBeDragged != null) { + if (controlToBeDragged.ToolStripPanelRow != null/* && controlToBeDragged.ToolStripPanelRow != owner*/) { + controlToBeDragged.ToolStripPanelRow.ControlsInternal.Remove(e.Control); + } + } + base.OnControlRemoved(e); + } + + /// + protected override void OnLayout(LayoutEventArgs e) { + if (e.AffectedComponent != ParentInternal && e.AffectedComponent as Control != null) { + ISupportToolStripPanel draggedControl = e.AffectedComponent as ISupportToolStripPanel; + if (draggedControl != null && this.RowsInternal.Contains(draggedControl.ToolStripPanelRow)) { + // there's a problem in the base onlayout... if toolstrip needs more space it talks to us + // not the row that needs layout. + LayoutTransaction.DoLayout(draggedControl.ToolStripPanelRow, e.AffectedComponent as IArrangedElement, e.AffectedProperty); + } + } + base.OnLayout(e); + } + + internal override void OnLayoutSuspended() { + base.OnLayoutSuspended(); + state[stateLayoutSuspended] = true; + + } + + internal override void OnLayoutResuming(bool resumeLayout) { + base.OnLayoutResuming(resumeLayout); + state[stateLayoutSuspended] = false; + if (state[stateBeginInit]) { + EndInit(); + } + } + + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + + if (!state[stateBeginInit]) { + if (Controls.Count > 0) { + // rejoin the controls on the other side of the toolstrippanel. + SuspendLayout(); + Control[] controls = new Control[this.Controls.Count]; + Point[] controlLocations = new Point[this.Controls.Count]; + int j = 0; + foreach (ToolStripPanelRow row in this.RowsInternal) { + foreach (Control control in row.ControlsInternal) { + controls[j] = control; + controlLocations[j] = new Point(row.Bounds.Width - control.Right, control.Top); + j++; + } + } + + this.Controls.Clear(); + + for ( int i = 0; i < controls.Length; i++) { + Join(controls[i] as ToolStrip, controlLocations[i]); + } + + ResumeLayout(true); + } + } + else { + state[stateRightToLeftChanged] = true; + } + + } + + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected virtual void OnRendererChanged(EventArgs e) { + Renderer.InitializePanel(this); + + this.Invalidate(); + + EventHandler handler = (EventHandler)Events[EventRendererChanged]; + if (handler != null) handler(this, e); + + } + + /// + /// + /// We want to Set ToolStripPanel at DesignTime when the ToolStripPanel is added to the Form, + /// + protected override void OnParentChanged(EventArgs e) { + PerformUpdate(); + base.OnParentChanged(e); + } + + protected override void OnDockChanged(EventArgs e) { + PerformUpdate(); + base.OnDockChanged(e); + } + + + internal void PerformUpdate() { + PerformUpdate(false); + } + internal void PerformUpdate(bool forceLayout) { + + if (!state[stateBeginInit] && !state[stateInJoin]) { + JoinControls(forceLayout); + } + + } + private void ResetRenderMode() { + RendererSwitcher.ResetRenderMode(); + } + + private bool ShouldSerializeRenderMode() { + return RendererSwitcher.ShouldSerializeRenderMode(); + } + + private bool ShouldSerializeDock() { + return (owner == null && (Dock != DockStyle.None)); + } + + private void JoinControls() { + JoinControls(false); + } + private void JoinControls(bool forceLayout) { + // undone: config - shift to other container + ToolStripPanelControlCollection controls = this.Controls as ToolStripPanelControlCollection; + if (controls.Count > 0) { + controls.Sort(); + + // since Join is going to mess with our order - make a copy. (ugh). + Control[] controlArray = new Control[controls.Count]; + controls.CopyTo(controlArray, 0); + for (int i = 0; i < controlArray.Length; i++) { + int numRows = RowsInternal.Count; + ISupportToolStripPanel draggedControl = controlArray[i] as ISupportToolStripPanel; + + if (draggedControl != null && draggedControl.ToolStripPanelRow != null && !draggedControl.IsCurrentlyDragging) { + ToolStripPanelRow row = draggedControl.ToolStripPanelRow; + if (row.Bounds.Contains(controlArray[i].Location)) { + // this toolstrip does not require join. + continue; + } + } + if (controlArray[i].AutoSize) { + controlArray[i].Size = controlArray[i].PreferredSize; + } + Point controlLocation = controlArray[i].Location; + + // right to left has changed while layout was deferred... + if (state[stateRightToLeftChanged]) { + controlLocation = new Point(this.Width - controlArray[i].Right, controlLocation.Y); + } + this.Join(controlArray[i] as ToolStrip, controlArray[i].Location); + if (numRows < RowsInternal.Count || forceLayout) { + // OK this is wierd but here we're in the midst of a suspend layout. + // the only way we can deterministically place these guys is to force a layout + // each time we've added a new row. Otherwise we wont find the correct + // row to add the control to (PointToRow will fail as Row.Bounds isnt set yet) + OnLayout(new LayoutEventArgs(this, PropertyNames.Rows)); + } + } + } + state[stateRightToLeftChanged] = false; + + } + + #region Feedback + + [ThreadStatic] + private static FeedbackRectangle feedbackRect; + + private void GiveToolStripPanelFeedback(ToolStrip toolStripToDrag, Point screenLocation) { + + if (Orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes) { + // paint the feedback in the correct location when RTL.Yes + screenLocation.Offset(-toolStripToDrag.Width,0); + } + if (CurrentFeedbackRect == null) { + Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: creating NEW feedback at " + screenLocation.ToString()); + + CurrentFeedbackRect = new FeedbackRectangle(toolStripToDrag.ClientRectangle); + } + + if (!CurrentFeedbackRect.Visible) { + Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: Showing NEW feedback at " + screenLocation.ToString()); + toolStripToDrag.SuspendCaputureMode(); + try { + CurrentFeedbackRect.Show(screenLocation); + toolStripToDrag.CaptureInternal = true; + } + finally { + toolStripToDrag.ResumeCaputureMode(); + } + } + else { + Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: Moving feedback to " + screenLocation.ToString()); + CurrentFeedbackRect.Move(screenLocation); + + } + } + + + + internal static void ClearDragFeedback() { +#if DEBUG + if (ToolStripPanelFeedbackDebug.TraceVerbose){ + Debug.WriteLine("FEEDBACK: clearing old feedback at "/*+ new StackTrace().ToString()*/); + } +#endif + FeedbackRectangle oldFeedback = feedbackRect; + feedbackRect = null; + if (oldFeedback != null) { + oldFeedback.Dispose(); + } + + } + + + private static FeedbackRectangle CurrentFeedbackRect { + get { + return feedbackRect; + } + set { + feedbackRect = value; + } + } + + + // The FeedbackRectangle happens to encapsulate a toolstripdropdown + // with a special region. The feedback rectangle exposes the minimum + // API so the underlying implementation can be replaced if necessary. + private class FeedbackRectangle : IDisposable { + private FeedbackDropDown dropDown; + + public FeedbackRectangle(Rectangle bounds) { + dropDown = new FeedbackDropDown(bounds); + + } + public bool Visible { + get { + if (dropDown != null && !dropDown.IsDisposed) { + return dropDown.Visible; + } + return false; + } + set { + if (dropDown != null && !dropDown.IsDisposed) { + dropDown.Visible = value; + } + } + } + + public void Show(Point newLocation) { + dropDown.Show(newLocation); + } + public void Move(Point newLocation) { + dropDown.MoveTo(newLocation); + } + + protected void Dispose(bool disposing) { + if (disposing) { + if (dropDown != null) { + Visible = false; + dropDown.Dispose(); + dropDown = null; + } + } + } + + public void Dispose() { + Dispose(true); + } + + ~FeedbackRectangle() { + Dispose(false); + } + + private class FeedbackDropDown : ToolStripDropDown { + + private const int MAX_PAINTS_TO_SERVICE = 20; + private int _numPaintsServiced = 0; // member variable to protect against re-entrancy + + public FeedbackDropDown(Rectangle bounds) : base(){ + SetStyle(ControlStyles.AllPaintingInWmPaint, false); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.CacheText, true); + this.AutoClose = false; + this.AutoSize = false; + this.DropShadowEnabled = false; + this.Bounds = bounds; + + // caching as this is unlikely to change during the lifetime + // of the dropdown. + + Rectangle regionRect = bounds; //create a region the size of the client area + regionRect.Inflate(-1,-1); //squish down by one pixel + + Region rgn = new Region(bounds); // create region + rgn.Exclude(regionRect); // exclude the center part + + // set it into the toolstripdropdown’s region + IntSecurity.ChangeWindowRegionForTopLevel.Assert(); + this.Region = rgn; + // RevertAssert automatically called here when exiting function. + + } + + // ForceSynchronousPaint - peeks through the message queue, looking for WM_PAINTs + // calls UpdateWindow on the hwnd to force the paint to happen now. + // + // When we're changing the location of the feedback dropdown, we need to + // force WM_PAINTS to happen, as things that dont respond to WM_ERASEBKGND + // have bits of the dropdown region drawn all over them. + private void ForceSynchronousPaint() { + if (!IsDisposed) { + if (_numPaintsServiced == 0) { // protect against re-entrancy. + try { + NativeMethods.MSG msg = new NativeMethods.MSG(); + while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, IntPtr.Zero), + NativeMethods.WM_PAINT, NativeMethods.WM_PAINT, + NativeMethods.PM_REMOVE)) { + SafeNativeMethods.UpdateWindow(new HandleRef(null, msg.hwnd)); + + // Infinite loop protection + if (_numPaintsServiced++ > MAX_PAINTS_TO_SERVICE) { + Debug.Fail("somehow we've gotten ourself in a situation where we're pumping an unreasonable number of paint messages, investigate."); + break; + } + } + } + finally { + _numPaintsServiced = 0; + } + } + } + } + + protected override void OnPaint(PaintEventArgs e) { + } + protected override void OnPaintBackground(PaintEventArgs e) { + // respond to everything in WM_ERASEBKGND + Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(e.Graphics, this)); + Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(e.Graphics, this)); + } + protected override void OnOpening(CancelEventArgs e) { + base.OnOpening(e); + e.Cancel = false; + } + + public void MoveTo(Point newLocation) { + this.Location = newLocation; + // if we dont force a paint here, we'll only send WM_ERASEBKGNDs right away + // and leave rectangles all over controls that dont respond to that window message. + ForceSynchronousPaint(); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m){ + if (m.Msg == NativeMethods.WM_NCHITTEST){ + m.Result = (IntPtr)NativeMethods.HTTRANSPARENT; + } + base.WndProc(ref m); + } + } + } + + #endregion Feedback + + #region JoinAndMove + + public void Join(ToolStrip toolStripToDrag) { + Join(toolStripToDrag, Point.Empty); + } + + public void Join(ToolStrip toolStripToDrag, int row) { + if (row < 0) { + throw new ArgumentOutOfRangeException("row", SR.GetString(SR.IndexOutOfRange, row.ToString(CultureInfo.CurrentCulture))); + } + + Point location = Point.Empty; + Rectangle dragRect = Rectangle.Empty; + if (row >= RowsInternal.Count) { + dragRect = DragBounds; + } + else { + dragRect = this.RowsInternal[row].DragBounds; + } + + if (Orientation == Orientation.Horizontal) { + location = new Point(0, dragRect.Bottom -1); + } + else { + location = new Point(dragRect.Right -1, 0); + } + + Join(toolStripToDrag, location); + + } + + public void Join(ToolStrip toolStripToDrag, int x, int y) { + Join(toolStripToDrag, new Point(x, y)); + } + + [ + SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using ToolStrip instead of Control intentionally + ] + public void Join(ToolStrip toolStripToDrag, Point location) { + if (toolStripToDrag == null) { + throw new ArgumentNullException("toolStripToDrag"); + } + if (!state[stateBeginInit] && !state[stateInJoin]) { + try { + state[stateInJoin] = true; + toolStripToDrag.ParentInternal = this; + MoveInsideContainer(toolStripToDrag, location); + } + finally { + state[stateInJoin] = false; + } + } + else { + this.Controls.Add(toolStripToDrag); + toolStripToDrag.Location = location; + } + } + + internal void MoveControl(ToolStrip toolStripToDrag, Point screenLocation) { + ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel; + + if (draggedControl == null) { + Debug.Fail("Move called on immovable object."); + return; + } + + Point clientLocation = PointToClient(screenLocation); + if (!this.DragBounds.Contains(clientLocation)) { + Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "RC.MoveControl - Point {0} is not in current rafting container drag bounds {1}, calling MoveOutsideContainer", clientLocation, DragBounds)); + MoveOutsideContainer(toolStripToDrag, screenLocation); + return; + } + else { + Join(toolStripToDrag as ToolStrip, clientLocation); + } + } + + private void MoveInsideContainer(ToolStrip toolStripToDrag, Point clientLocation) { + ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel; + // if the point is not in this rafting container forward on to the appropriate container. + + if (draggedControl.IsCurrentlyDragging && !this.DragBounds.Contains(clientLocation)) { + return; + } + + // we know we're moving inside the container. + + bool changedRow = false; + + ClearDragFeedback(); + + // VSWhidbey 439542 - in design mode we get bogus values for client location. + if (toolStripToDrag.Site != null && toolStripToDrag.Site.DesignMode && IsHandleCreated) { + + if (clientLocation.X < 0 || clientLocation.Y < 0) { + Point currentCursorLoc = PointToClient(WindowsFormsUtils.LastCursorPoint); + if (ClientRectangle.Contains(currentCursorLoc)) { + clientLocation = currentCursorLoc; + } + } + } + + + // + // Point INSIDE this rafting container + // + + ToolStripPanelRow currentToolStripPanelRow = draggedControl.ToolStripPanelRow; + +#if DEBUG + bool debugModeOnly_ChangedContainers = currentToolStripPanelRow != null ? + (currentToolStripPanelRow.ToolStripPanel != this) : true; +#endif + + // Dev10 bug 477755 - in design mode toolstrip location jump back after being set. + bool pointInCurrentRow = false; + if (currentToolStripPanelRow != null && currentToolStripPanelRow.Visible && currentToolStripPanelRow.ToolStripPanel == this) { + if (toolStripToDrag.IsCurrentlyDragging) { + // Dragging with mouse, use DragBounds to check + pointInCurrentRow = currentToolStripPanelRow.DragBounds.Contains(clientLocation); + } + else { + // Location is set directly, use normal Bounds to check + pointInCurrentRow = currentToolStripPanelRow.Bounds.Contains(clientLocation); + } + } + + if (pointInCurrentRow) { + // Point INSIDE same rafting row + Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, "RC.MoveControl - Point " + clientLocation + "is in the same row as the control" + draggedControl.ToolStripPanelRow.DragBounds); + draggedControl.ToolStripPanelRow.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), clientLocation); + } + else { + // Point OUTSIDE current rafting row. + + + + Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, "RC.MoveControl - Point " + clientLocation + " is outside the current rafting row."); + + ToolStripPanelRow row = PointToRow(clientLocation); + if (row == null) { + Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tThere is no row corresponding to this point, creating a new one.")); + + // there's no row at this point so lets create one + int index = this.RowsInternal.Count; + + if (Orientation == Orientation.Horizontal) { + // if it's above the first row, insert at the front. + index = (clientLocation.Y <= this.Padding.Left) ? 0 : index; + } + else { // Orientation.Vertical + // if it's before the first row, insert at the front. + index = (clientLocation.X <= this.Padding.Left) ? 0 : index; + } + + ToolStripPanelRow previousRow = null; + if (this.RowsInternal.Count > 0) { + if (index == 0) { + previousRow = this.RowsInternal[0]; + } + else if (index > 0) { + previousRow = this.RowsInternal[index - 1]; + } + } + if (previousRow != null /* there was a previous row */ + && previousRow.ControlsInternal.Count == 1 /*toolStripToDrag*/ + && previousRow.ControlsInternal.Contains(toolStripToDrag)) { + // if the previous row already contains this control + // it's futile to create a new row, we're just going to wind + // up disposing this one and causing great amounts of flicker. + row = previousRow; + + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "Reusing previous row"); + // Move the ToolStrip to the new Location in the existing row. + if (toolStripToDrag.IsInDesignMode) { + Point endLocation = (Orientation == Orientation.Horizontal) ? new Point (clientLocation.X, row.Bounds.Y) : new Point (row.Bounds.X, clientLocation.Y); + draggedControl.ToolStripPanelRow.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), endLocation); + } + } + else { + // Create a new row and insert it. + // + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "Inserting a new row at " + index.ToString(CultureInfo.InvariantCulture)); + row = new ToolStripPanelRow(this); + this.RowsInternal.Insert(index, row); + + } + } + else if (!row.CanMove(toolStripToDrag)) { + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tThere was a row, but we cant add the control to it, creating/inserting new row.")); + + // we have a row at that point, but its too full or doesnt want + // anyone to join it. + int index = RowsInternal.IndexOf(row); + + if (currentToolStripPanelRow != null && currentToolStripPanelRow.ControlsInternal.Count == 1) { + if (index > 0 && index-1 == RowsInternal.IndexOf(currentToolStripPanelRow)) { + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tAttempts to leave the current row failed as there's no space in the next row. Since there's only one control, just keep the row.")); + return; + } + } + row = new ToolStripPanelRow(this); + this.RowsInternal.Insert(index, row); + clientLocation.Y = row.Bounds.Y; + } + + changedRow = (currentToolStripPanelRow != row); + if (!changedRow) { + if (currentToolStripPanelRow != null && currentToolStripPanelRow.ControlsInternal.Count >1) { + // force a leave/re-enter to occur. + currentToolStripPanelRow.LeaveRow(toolStripToDrag); + currentToolStripPanelRow = null; + changedRow = true; + } + } + if (changedRow) { + Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tCalling JoinRow.")); + if (currentToolStripPanelRow != null) { + currentToolStripPanelRow.LeaveRow(toolStripToDrag); + } + row.JoinRow(toolStripToDrag, clientLocation); + } + + if (changedRow && draggedControl.IsCurrentlyDragging) { + // force the layout of the new row. + for (int i = 0; i < RowsInternal.Count; i++) { + LayoutTransaction.DoLayout(RowsInternal[i], this, PropertyNames.Rows); + } + + + if (RowsInternal.IndexOf(row) > 0) { + // VSWhidbey 327225 - when joining a new row, move the cursor to to the location of + // the grip, otherwise budging the mouse can pull it down into the next row. + IntSecurity.AdjustCursorPosition.Assert(); + try { + Point cursorLoc = toolStripToDrag.PointToScreen(toolStripToDrag.GripRectangle.Location); + if (Orientation == Orientation.Vertical) { + cursorLoc.X += toolStripToDrag.GripRectangle.Width /2; + cursorLoc.Y = Cursor.Position.Y; + } + else { + cursorLoc.Y += toolStripToDrag.GripRectangle.Height /2; + cursorLoc.X = Cursor.Position.X; + } + Cursor.Position = cursorLoc; + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + } + + +#if DEBUG + Debug_VerifyOneToOneCellRowControlMatchup(); +// Debug_VerifyCountRows(); + if (draggedControl.IsCurrentlyDragging && changedRow && !debugModeOnly_ChangedContainers) { + // if we have changed containers, we're in a SuspendLayout. + Debug_VerifyNoOverlaps(); + } +#endif + } + + private void MoveOutsideContainer(ToolStrip toolStripToDrag, Point screenLocation) { + + // look for another rafting container. + ToolStripPanel panel = ToolStripManager.ToolStripPanelFromPoint(toolStripToDrag, screenLocation); + if (panel != null) { + using (new LayoutTransaction(panel, panel, null)) { + panel.MoveControl(toolStripToDrag, screenLocation); + } + toolStripToDrag.PerformLayout(); +#if DEBUG + ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel; + if (draggedControl.IsCurrentlyDragging) { + Debug_VerifyNoOverlaps(); + } +#endif + } + else { + GiveToolStripPanelFeedback(toolStripToDrag, screenLocation); + } + + } + + /// + /// + /// Given a point within the ToolStripPanel client area - + /// it returns the row. If no such row exists, returns null + /// + public ToolStripPanelRow PointToRow(Point clientLocation) { + // PERF: since we're using the PropertyStore for this.RowsInternal, its actually + // faster to use foreach. + foreach (ToolStripPanelRow row in this.RowsInternal) { + Rectangle bounds = LayoutUtils.InflateRect(row.Bounds, row.Margin); + + // at this point we may not be sized correctly. Guess. + if (ParentInternal != null) { + if (Orientation == Orientation.Horizontal && (bounds.Width == 0)) { + bounds.Width = ParentInternal.DisplayRectangle.Width; + } + else if (Orientation == Orientation.Vertical && (bounds.Height == 0)) { + bounds.Height = ParentInternal.DisplayRectangle.Height; + } + } + + + if (bounds.Contains(clientLocation)) { + return row; + } + } + return null; + } + + + #endregion JoinAndMove + + + [Conditional("DEBUG")] + private void Debug_VerifyOneToOneCellRowControlMatchup() { + for (int i = 0; i < this.RowsInternal.Count; i++) { + ToolStripPanelRow row = this.RowsInternal[i]; + foreach (ToolStripPanelCell cell in row.Cells) { + + if (cell.Control != null) { + ToolStripPanelRow currentlyAssignedRow = ((ISupportToolStripPanel)cell.Control).ToolStripPanelRow; + if (currentlyAssignedRow != row) { + int goodRowIndex = (currentlyAssignedRow != null) ? RowsInternal.IndexOf(currentlyAssignedRow) : -1; + if (goodRowIndex == -1) { + Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ToolStripPanelRow has not been assigned! Should be set to {0}.", i)); + } + else { + Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Detected orphan cell! {0} is in row {1}. It shouldnt have a cell in {2}! \r\n\r\nTurn on DEBUG_PAINT in ToolStripPanel and ToolStripPanelRow to investigate.", cell.Control.Name, goodRowIndex, i)); + } + } + } + else { + Debug.Fail("why do we have a cell with a null control in this row?"); + } + } + } + } + + [Conditional("DEBUG")] + private void Debug_PrintRows() { + for (int i = 0; i < RowsInternal.Count; i++) { + Debug.Write("Row " + i.ToString(CultureInfo.CurrentCulture) + ": "); + for (int j = 0; j < RowsInternal[i].ControlsInternal.Count; j++) { + Debug.Write(String.Format(CultureInfo.CurrentCulture, "[{0} {1}] ", RowsInternal[i].ControlsInternal[j].Name, ((ToolStripPanelCell)RowsInternal[i].Cells[j]).Margin)); + } + Debug.Write("\r\n"); + } + } + + [Conditional("DEBUG")] + private void Debug_VerifyCountRows() { + Debug.Assert(RowsInternal.Count <= Controls.Count, "How did the number of rows get larger than the number of controls?"); + } + + [Conditional("DEBUG")] + private void Debug_VerifyNoOverlaps() { + foreach (Control c1 in this.Controls) { + foreach (Control c2 in this.Controls) { + if (c1 == c2) { + continue; + } + Rectangle intersection = c1.Bounds; + intersection.Intersect(c2.Bounds); + + if (!LayoutUtils.IsZeroWidthOrHeight(intersection)) { + ISupportToolStripPanel draggedToolStrip1 = c1 as ISupportToolStripPanel; + ISupportToolStripPanel draggedToolStrip2 = c2 as ISupportToolStripPanel; + + string fail = String.Format(CultureInfo.CurrentCulture, + "OVERLAP detection:\r\n{0}: {1} row {2} row bounds {3}", + c1.Name == null ? "" : c1.Name, + c1.Bounds, + !RowsInternal.Contains(draggedToolStrip1.ToolStripPanelRow) ? "unknown" : RowsInternal.IndexOf(draggedToolStrip1.ToolStripPanelRow).ToString(CultureInfo.CurrentCulture), + draggedToolStrip1.ToolStripPanelRow.Bounds); + + fail += String.Format(CultureInfo.CurrentCulture, + "\r\n{0}: {1} row {2} row bounds {3}", + c2.Name == null ? "" : c2.Name, + c2.Bounds, + !RowsInternal.Contains(draggedToolStrip2.ToolStripPanelRow) ? "unknown" : RowsInternal.IndexOf(draggedToolStrip2.ToolStripPanelRow).ToString(CultureInfo.CurrentCulture), + draggedToolStrip2.ToolStripPanelRow.Bounds); + Debug.Fail(fail); + + } + } + + } + + } + + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + return RowsInternal; + } + } + + /// + [ + ListBindable(false), + ComVisible(false) + ] + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] + public class ToolStripPanelRowCollection : ArrangedElementCollection, IList { + + private ToolStripPanel owner; + /// + public ToolStripPanelRowCollection(ToolStripPanel owner) { + this.owner = owner; + } + + /// + /// + /// [To be supplied.] + /// + public ToolStripPanelRowCollection(ToolStripPanel owner, ToolStripPanelRow[] value) { + this.owner = owner; + AddRange(value); + } + + /// + /// + /// + /// + public new virtual ToolStripPanelRow this[int index] { + get { + return (ToolStripPanelRow)(InnerList[index]); + } + } + + /// + /// + /// [To be supplied.] + /// + public int Add(ToolStripPanelRow value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + int retVal = InnerList.Add(value); + OnAdd(value, retVal); + return retVal; + + } + + + /// + /// + /// [To be supplied.] + /// + public void AddRange(ToolStripPanelRow[] value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + ToolStripPanel currentOwner = this.owner; + if (currentOwner != null) { + currentOwner.SuspendLayout(); + } + try { + for (int i = 0; i < value.Length; i++) { + this.Add(value[i]); + } + } + finally { + if (currentOwner != null) { + currentOwner.ResumeLayout(); + } + } + } + /// + /// + /// [To be supplied.] + /// + public void AddRange(ToolStripPanelRowCollection value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + ToolStripPanel currentOwner = this.owner; + if (currentOwner != null) { + currentOwner.SuspendLayout(); + } + try { + int currentCount = value.Count; + for (int i = 0; i < currentCount; i++) { + this.Add(value[i]); + } + } + finally { + if (currentOwner != null) { + currentOwner.ResumeLayout(); + } + } + + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(ToolStripPanelRow value) { + return InnerList.Contains(value); + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { + if (owner != null) { + owner.SuspendLayout(); + } + try { + while (Count != 0) { + RemoveAt(Count - 1); + } + } + finally { + if (owner != null) { + owner.ResumeLayout(); + } + } + } + + void IList.Clear() { Clear(); } + bool IList.IsFixedSize { get { return InnerList.IsFixedSize; } } + bool IList.Contains(object value) { return InnerList.Contains(value); } + bool IList.IsReadOnly { get { return InnerList.IsReadOnly; } } + void IList.RemoveAt(int index) { RemoveAt(index); } + void IList.Remove(object value) { Remove(value as ToolStripPanelRow); } + int IList.Add(object value) { return Add(value as ToolStripPanelRow); } + int IList.IndexOf(object value) { return IndexOf(value as ToolStripPanelRow); } + void IList.Insert(int index, object value) { Insert(index, value as ToolStripPanelRow); } + + /// + /// + object IList.this[int index] { + get { return InnerList[index]; } + set { throw new NotSupportedException(SR.GetString(SR.ToolStripCollectionMustInsertAndRemove)); /* InnerList[index] = value; */ } + } + + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(ToolStripPanelRow value) { + return InnerList.IndexOf(value); + } + + /// + public void Insert(int index, ToolStripPanelRow value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + InnerList.Insert(index, value); + OnAdd(value, index); + + } + + private void OnAdd(ToolStripPanelRow value, int index) { + if (owner != null) { + LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent); + } + } + + /// + /// Do proper cleanup of ownership, etc. + /// + private void OnAfterRemove(ToolStripPanelRow row) { + +#if DEBUG + if (ToolStripPanelMissingRowDebug.TraceVerbose) { + if (row != null) { + Debug.Write("Removing row: "); + row.Debug_PrintRowID(); + Debug.WriteLine(new StackTrace().ToString()); + } + } +#endif + + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(ToolStripPanelRow value) { + InnerList.Remove(value); + OnAfterRemove(value); + } + + /// + /// + /// [To be supplied.] + /// + public void RemoveAt(int index) { + ToolStripPanelRow item = null; + if (index < Count && index >= 0) { + item = (ToolStripPanelRow)(InnerList[index]); + } + InnerList.RemoveAt(index); + OnAfterRemove(item); + } + + + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(ToolStripPanelRow[] array, int index) { + InnerList.CopyTo(array, index); + } + + + + } + + internal class ToolStripPanelControlCollection : WindowsFormsUtils.TypedControlCollection { + + private ToolStripPanel owner; + + public ToolStripPanelControlCollection(ToolStripPanel owner) + : base(owner, typeof(ToolStrip)) { + this.owner = owner; + } + + internal override void AddInternal(Control value) { + if (value != null) { + using (new LayoutTransaction(value, value, PropertyNames.Parent)) { + base.AddInternal(value); + } + } + else { + base.AddInternal(value); + } + + } + + internal void Sort() { + if (owner.Orientation == Orientation.Horizontal) { + InnerList.Sort(new YXComparer()); + } + else { + InnerList.Sort(new XYComparer()); + } + } + + + // sort by X, then Y + public class XYComparer : IComparer { + public XYComparer() { } + public int Compare(object first, object second) { + Control one = first as Control; + Control two = second as Control; + + if (one.Bounds.X < two.Bounds.X) { + return -1; + } + + if (one.Bounds.X == two.Bounds.X) { + if (one.Bounds.Y < two.Bounds.Y) { + return -1; + } + return 1; + } + return 1; + + } + } + + // sort by Y, then X + public class YXComparer : IComparer { + public YXComparer() { } + public int Compare(object first, object second) { + Control one = first as Control; + Control two = second as Control; + + if (one.Bounds.Y < two.Bounds.Y) { + return -1; + } + + if (one.Bounds.Y == two.Bounds.Y) { + if (one.Bounds.X < two.Bounds.X) { + return -1; + } + return 1; + } + return 1; + + } + } + + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPanelCell.cs b/WindowsForms/Managed/System/WinForms/ToolStripPanelCell.cs new file mode 100644 index 000000000..46c0f3803 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPanelCell.cs @@ -0,0 +1,380 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Drawing; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Globalization; + + + + /// this class is a container for toolstrips on a rafting row. + /// you can set layout styles on this container all day long and not + /// affect the underlying toolstrip's properties.... so if its + /// removed from a rafting container its still got its defaults + /// set up for it. + internal class ToolStripPanelCell : ArrangedElement { + + + private ToolStrip _wrappedToolStrip = null; + private ToolStripPanelRow parent = null; + private Size maxSize = LayoutUtils.MaxSize; + private bool currentlySizing = false; + private bool currentlyDragging = false; + private bool restoreOnVisibleChanged = false; + + private Rectangle cachedBounds = Rectangle.Empty; +#if DEBUG + private string cellID; + [ThreadStatic] + private static int cellCount; +#endif + + public ToolStripPanelCell(Control control):this(null, control) { + + } + public ToolStripPanelCell(ToolStripPanelRow parent, Control control) { + +#if DEBUG + + // Ensure 1:1 Cell/ToolStripPanel mapping + cellID = string.Format(CultureInfo.CurrentCulture, "{0}.{1}", control.Name, ++cellCount); + Debug.Assert(cellCount <= ToolStripManager.ToolStrips.Count, "who is allocating an extra toolstrippanel cell?"); +#endif + + this.ToolStripPanelRow = parent; + this._wrappedToolStrip = control as ToolStrip; + if (control == null) { + throw new ArgumentNullException("control"); + } + else if (_wrappedToolStrip == null) { + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TypedControlCollectionShouldBeOfType, typeof(ToolStrip).Name)), control.GetType().Name); + } + CommonProperties.SetAutoSize(this, true); + _wrappedToolStrip.LocationChanging += new ToolStripLocationCancelEventHandler(this.OnToolStripLocationChanging); + _wrappedToolStrip.VisibleChanged += new EventHandler(this.OnToolStripVisibleChanged); + + } + + public Rectangle CachedBounds { + get { return cachedBounds; } + set { cachedBounds = value; + Debug.Assert(cachedBounds.X >= 0 && cachedBounds.Y >= 0, "cached bounds are outside of the client area, investigate"); + } + } + + + public Control Control { + get { return _wrappedToolStrip; } + } + + // ToolStripPanelCell is considered to be Visible if the wrapped control is InDesignMode. + // This property is accessed from the ToolStripPanelRow in cases where we are moving the toolStrip around + // during a drag operation. + public bool ControlInDesignMode { + get { return (_wrappedToolStrip != null && _wrappedToolStrip.IsInDesignMode); } + } + + public IArrangedElement InnerElement { + get { return _wrappedToolStrip as IArrangedElement; } + } + + public ISupportToolStripPanel DraggedControl { + get { return _wrappedToolStrip as ISupportToolStripPanel; } + } + + + public ToolStripPanelRow ToolStripPanelRow { + get { return parent; } + set { + if (parent != value) { + if (parent != null) { + ((IList)parent.Cells).Remove(this); + } + parent = value; + this.Margin = Padding.Empty; + + } + + } + } + + public override bool Visible { + get { + if (Control != null && Control.ParentInternal == ToolStripPanelRow.ToolStripPanel) { + return InnerElement.ParticipatesInLayout; + } + return false; + } + set { + Control.Visible = value; + } + } + + public Size MaximumSize { + get { return maxSize; } + } + + + public override LayoutEngine LayoutEngine { + get { return DefaultLayout.Instance; } + } + + protected override IArrangedElement GetContainer() { + return parent; + } + + public int Grow(int growBy) { + if (ToolStripPanelRow.Orientation == Orientation.Vertical) { + return GrowVertical(growBy); + } + else { + return GrowHorizontal(growBy); + } + + + } + + private int GrowVertical(int growBy) { + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Height >= Control.PreferredSize.Height) { + // nothing to grow. + return 0; + } + + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Height + growBy >= Control.PreferredSize.Height) { + int freed = Control.PreferredSize.Height - MaximumSize.Height; + maxSize=LayoutUtils.MaxSize; + return freed; + } + + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Height + growBy < Control.PreferredSize.Height) { + maxSize.Height += growBy; + return growBy; + } + return 0; + + } + + private int GrowHorizontal(int growBy) { + + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Width >= Control.PreferredSize.Width) { + // nothing to grow. + return 0; + } + + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Width + growBy >= Control.PreferredSize.Width) { + int freed = Control.PreferredSize.Width - MaximumSize.Width; + maxSize=LayoutUtils.MaxSize; + return freed; + } + + // Grow ---] + // Pref [ ] + // Max [ ] + if (MaximumSize.Width + growBy < Control.PreferredSize.Width) { + maxSize.Width += growBy; + return growBy; + } + return 0; + + } + protected override void Dispose(bool disposing) { + try { + if (disposing) { + if (_wrappedToolStrip != null) { +#if DEBUG + cellCount--; +#endif + _wrappedToolStrip.LocationChanging -= new ToolStripLocationCancelEventHandler(this.OnToolStripLocationChanging); + _wrappedToolStrip.VisibleChanged -= new EventHandler(this.OnToolStripVisibleChanged); + } + _wrappedToolStrip = null; + if (parent != null) { + ((IList)parent.Cells).Remove(this); + } + parent = null; + } +#if DEBUG + else { + cellCount--; + } +#endif + + } + finally { + base.Dispose(disposing); + } + } + + protected override ArrangedElementCollection GetChildren() { + return ArrangedElementCollection.Empty; + } + + public override Size GetPreferredSize(Size constrainingSize) { + ISupportToolStripPanel draggedControl = DraggedControl; + Size preferredSize = Size.Empty; + + if (draggedControl.Stretch) { + if (ToolStripPanelRow.Orientation == Orientation.Horizontal) { + constrainingSize.Width = ToolStripPanelRow.Bounds.Width; + preferredSize = _wrappedToolStrip.GetPreferredSize(constrainingSize); + preferredSize.Width = constrainingSize.Width; + } + else { + constrainingSize.Height = ToolStripPanelRow.Bounds.Height; + preferredSize = _wrappedToolStrip.GetPreferredSize(constrainingSize); + preferredSize.Height = constrainingSize.Height; + } + } + else { + preferredSize = (!_wrappedToolStrip.AutoSize) ? _wrappedToolStrip.Size : _wrappedToolStrip.GetPreferredSize(constrainingSize); + } + + + // return LayoutUtils.IntersectSizes(constrainingSize, preferredSize); + return preferredSize; + } + + protected override void SetBoundsCore(Rectangle bounds, BoundsSpecified specified) { + + currentlySizing = true; + this.CachedBounds = bounds; + try + { + if (DraggedControl.IsCurrentlyDragging) { + if (ToolStripPanelRow.Cells[ToolStripPanelRow.Cells.Count -1] == this) { + Rectangle displayRectangle = ToolStripPanelRow.DisplayRectangle; + if (ToolStripPanelRow.Orientation == Orientation.Horizontal) { + int spaceToFree = bounds.Right - displayRectangle.Right; + if (spaceToFree > 0 && bounds.Width > spaceToFree) { + bounds.Width -= spaceToFree; + } + } + else { + int spaceToFree = bounds.Bottom - displayRectangle.Bottom; + if (spaceToFree > 0 && bounds.Height > spaceToFree) { + bounds.Height -= spaceToFree; + } + } + } + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelMouseDebug.TraceVerbose, "[CELL] DRAGGING calling SetBounds " + bounds.ToString()); + base.SetBoundsCore(bounds,specified); + InnerElement.SetBounds(bounds, specified); + } + else { + if (!ToolStripPanelRow.CachedBoundsMode) { + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelMouseDebug.TraceVerbose, "[CELL] NOT DRAGGING calling SetBounds " + bounds.ToString()); + base.SetBoundsCore(bounds,specified); + InnerElement.SetBounds(bounds, specified); + + } + + } + + } + finally + { + currentlySizing = false; + } + } + + public int Shrink(int shrinkBy) { + if (ToolStripPanelRow.Orientation == Orientation.Vertical) { + return ShrinkVertical(shrinkBy); + } + else { + return ShrinkHorizontal(shrinkBy); + } + } + + private int ShrinkHorizontal(int shrinkBy) { + return 0; + } + + private int ShrinkVertical(int shrinkBy) { + return 0; + } + + /// + /// New EventHandler for The LocationChanging so that ToolStripPanelCell Listens to the Location Property on the ToolStrips's being changed. + /// The ToolStrip needs to Raft (Join) to the approriate Location Depending on the new Location w.r.t to the oldLocation ... + /// Hence the need for this event listener. + /// + private void OnToolStripLocationChanging(object sender, ToolStripLocationCancelEventArgs e) + { + if (ToolStripPanelRow == null) { + return; + } + if (!currentlySizing && !currentlyDragging) { + try { + currentlyDragging = true; + Point newloc = e.NewLocation; + // detect if we havent yet performed a layout - force one so we can + // properly join to the row. + if (ToolStripPanelRow != null && ToolStripPanelRow.Bounds == Rectangle.Empty) { + ToolStripPanelRow.ToolStripPanel.PerformUpdate(true); + } + if (_wrappedToolStrip != null) { + ToolStripPanelRow.ToolStripPanel.Join(_wrappedToolStrip, newloc); + } + } + finally { + currentlyDragging = false; + e.Cancel = true; + } + } + } + + private void OnToolStripVisibleChanged(object sender, EventArgs e) { + + if (_wrappedToolStrip != null + && !_wrappedToolStrip.IsInDesignMode + && !_wrappedToolStrip.IsCurrentlyDragging + && !_wrappedToolStrip.IsDisposed // ensure we have a live-runtime only toolstrip. + && !_wrappedToolStrip.Disposing) { + + // VSWhidbey 395502 - rejoin the row when visibility is toggled. + // we dont want to do this logic at DT, as the DropSourceBehavior + // will set the toolstrip visible = false. + if (!Control.Visible) { + // if we are becoming visible = false, remember if we were in a toolstrippanelrow at the time. + restoreOnVisibleChanged = (ToolStripPanelRow != null && ((IList)ToolStripPanelRow.Cells).Contains(this)); + } + else if (restoreOnVisibleChanged) { + try { + // if we are becoming visible = true, and we ARE in a toolstrippanelrow, rejoin. + if (ToolStripPanelRow != null && ((IList)ToolStripPanelRow.Cells).Contains(this)) { + ToolStripPanelRow.ToolStripPanel.Join(_wrappedToolStrip, _wrappedToolStrip.Location); + } + } + finally { + restoreOnVisibleChanged = false; + } + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventArgs.cs new file mode 100644 index 000000000..60ec4dfb7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventArgs.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + + /// + /// ToolStripPanelRenderEventArgs + /// + public class ToolStripPanelRenderEventArgs : EventArgs { + + private ToolStripPanel toolStripPanel = null; + private Graphics graphics = null; + private bool handled = false; + + /// + /// This class represents all the information to render the toolStrip + /// + public ToolStripPanelRenderEventArgs(Graphics g, ToolStripPanel toolStripPanel) { + this.toolStripPanel = toolStripPanel; + this.graphics = g; + } + + + /// + /// the graphics object to draw with + /// + public Graphics Graphics { + get { + return graphics; + } + } + + /// + /// Represents which toolStrip was affected by the click + /// + public ToolStripPanel ToolStripPanel { + get { + return toolStripPanel; + } + } + + public bool Handled { + get { return handled; } + set { handled = value; } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventHandler.cs new file mode 100644 index 000000000..c62ccec6a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPanelRenderEventHandler.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of the ToolStripPanel is being rendered + /// + /// + public delegate void ToolStripPanelRenderEventHandler(object sender, ToolStripPanelRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPanelRow.cs b/WindowsForms/Managed/System/WinForms/ToolStripPanelRow.cs new file mode 100644 index 000000000..cd1186337 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPanelRow.cs @@ -0,0 +1,2325 @@ +//#define DEBUG_PAINT +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +/* +*/ +namespace System.Windows.Forms { + using System.Drawing; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// + [ToolboxItem(false)] + public class ToolStripPanelRow : Component, IArrangedElement { + private Rectangle bounds = Rectangle.Empty; + private ToolStripPanel parent = null; + private BitVector32 state = new BitVector32(); + private PropertyStore propertyStore = new PropertyStore(); // Contains all properties that are not always set. + private int suspendCount = 0; + private ToolStripPanelRowManager rowManager = null; + + + private const int MINALLOWEDWIDTH = 50; + private int minAllowedWidth = MINALLOWEDWIDTH; + + private static readonly int stateVisible = BitVector32.CreateMask(); + private static readonly int stateDisposing = BitVector32.CreateMask(stateVisible); + private static readonly int stateLocked = BitVector32.CreateMask(stateDisposing); + private static readonly int stateInitialized = BitVector32.CreateMask(stateLocked); + private static readonly int stateCachedBoundsMode = BitVector32.CreateMask(stateInitialized); + private static readonly int stateInLayout = BitVector32.CreateMask(stateCachedBoundsMode); + + + + + + private static readonly int PropControlsCollection = PropertyStore.CreateKey(); + +#if DEBUG + internal static TraceSwitch ToolStripPanelRowCreationDebug = new TraceSwitch("ToolStripPanelRowCreationDebug", "Debug code for rafting row creation"); +#else + internal static TraceSwitch ToolStripPanelRowCreationDebug ; +#endif + +#if DEBUG + private static int rowCreationCount = 0; + private int thisRowID; +#endif + + + /// + public ToolStripPanelRow(ToolStripPanel parent) : this(parent, true){ + } + + + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + internal ToolStripPanelRow(ToolStripPanel parent, bool visible) { +#if DEBUG + thisRowID = ++rowCreationCount; +#endif + if (DpiHelper.EnableToolStripHighDpiImprovements) { + minAllowedWidth = DpiHelper.LogicalToDeviceUnitsX(MINALLOWEDWIDTH); + } + + this.parent = parent; + this.state[stateVisible] = visible; + this.state[stateDisposing | stateLocked| stateInitialized] = false; + + Debug.WriteLineIf(ToolStripPanelRowCreationDebug.TraceVerbose, "Created new ToolStripPanelRow"); + + using (LayoutTransaction lt = new LayoutTransaction(parent, this, null)) { + this.Margin = DefaultMargin; + CommonProperties.SetAutoSize(this, true); + } + + } + + /// + public Rectangle Bounds { + get { + return bounds; + } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ControlControlsDescr), SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public Control[] Controls { + get { + Control[] controls = new Control[ControlsInternal.Count]; + ControlsInternal.CopyTo(controls,0); + return controls; + } + } + + + /// + /// + /// Collection of child controls. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ControlControlsDescr)] + internal ToolStripPanelRowControlCollection ControlsInternal { + get { + ToolStripPanelRowControlCollection controlsCollection = (ToolStripPanelRowControlCollection)Properties.GetObject(PropControlsCollection); + + if (controlsCollection == null) { + controlsCollection = CreateControlsInstance(); + Properties.SetObject(PropControlsCollection, controlsCollection); + } + + return controlsCollection; + } + } + + internal ArrangedElementCollection Cells { + get { + return ControlsInternal.Cells; + } + } + + internal bool CachedBoundsMode { + get { + return state[stateCachedBoundsMode]; + } + set { + state[stateCachedBoundsMode] = value; + } + } + + + + private ToolStripPanelRowManager RowManager { + get { + if (rowManager == null) { + rowManager = (Orientation == Orientation.Horizontal) ? new HorizontalRowManager(this) as ToolStripPanelRowManager + : new VerticalRowManager(this) as ToolStripPanelRowManager; + Initialized = true; + } + + return rowManager; + } + } + + /// + protected virtual Padding DefaultMargin { + get { + ToolStripPanelCell cell = RowManager.GetNextVisibleCell(0, /*forward*/true); + if (cell != null && cell.DraggedControl != null) { + if (cell.DraggedControl.Stretch) { + Padding padding = ToolStripPanel.RowMargin; + // clear out the padding. + if (Orientation == Orientation.Horizontal) { + padding.Left = 0; + padding.Right = 0; + } + else { + padding.Top = 0; + padding.Bottom = 0; + } + return padding; + } + } + return ToolStripPanel.RowMargin; + + } + } + + /// + protected virtual Padding DefaultPadding { + get { return Padding.Empty; } + } + + /// + public Rectangle DisplayRectangle { + get { + return RowManager.DisplayRectangle; + } + } + + /// + public LayoutEngine LayoutEngine { + get { + return FlowLayout.Instance; + } + } + + /// + internal bool Locked { + get { + return state[stateLocked]; + } + } + + /// + private bool Initialized { + get { + return state[stateInitialized]; + } + set { + state[stateInitialized] = value; + } + } + + /// + public Padding Margin { + get { return CommonProperties.GetMargin(this); } + set { if (Margin != value ) CommonProperties.SetMargin(this, value); } + } + + /// + public virtual Padding Padding { + get { return CommonProperties.GetPadding(this, DefaultPadding); } + set { + if (Padding != value) CommonProperties.SetPadding(this, value); + } + } + + internal Control ParentInternal { + get { + return parent; + } + } + + /// + /// Retrieves our internal property storage object. If you have a property + /// whose value is not always set, you should store it in here to save + /// space. + /// + internal PropertyStore Properties { + get { + return propertyStore; + } + } + + /// + public ToolStripPanel ToolStripPanel { + get { + return parent; + } + } + + internal bool Visible { + get { + return state[stateVisible]; + } + } + + /// + public Orientation Orientation { + get { + return ToolStripPanel.Orientation; + } + } + +#if DEBUG + internal void Debug_PrintRowID() { + Debug.Write(thisRowID.ToString(CultureInfo.CurrentCulture)); + } +#endif + + /// + /// + /// returns true if there is enough space to "raft" the control + /// ow returns false + /// + public bool CanMove(ToolStrip toolStripToDrag) { + return !ToolStripPanel.Locked && !Locked && RowManager.CanMove(toolStripToDrag); + } + + /// + private ToolStripPanelRowControlCollection CreateControlsInstance() { + return new ToolStripPanelRowControlCollection(this); + } + + /// + protected override void Dispose(bool disposing) { + try { + if (disposing) { + + Debug.WriteLineIf(ToolStripPanelRowCreationDebug.TraceVerbose, "Disposed ToolStripPanelRow"); + state[stateDisposing] = true; + this.ControlsInternal.Clear(); + } + } + finally { + state[stateDisposing] = false; + base.Dispose(disposing); + } + } + + /// + protected internal virtual void OnControlAdded(Control control, int index) { + + + // if previously added - remove. + ISupportToolStripPanel controlToBeDragged = control as ISupportToolStripPanel; + + if (controlToBeDragged != null) { + controlToBeDragged.ToolStripPanelRow = this; + } + RowManager.OnControlAdded(control, index); + } + + /// + protected internal virtual void OnOrientationChanged() { + this.rowManager = null; + } + + /// + protected void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { + ((IArrangedElement)this).PerformLayout((IArrangedElement)this, PropertyNames.Size); + + RowManager.OnBoundsChanged(oldBounds,newBounds); + } + + /// + protected internal virtual void OnControlRemoved(Control control, int index) { + if (!state[stateDisposing]) { + this.SuspendLayout(); + RowManager.OnControlRemoved(control, index); + + // if previously added - remove. + ISupportToolStripPanel controlToBeDragged = control as ISupportToolStripPanel; + + if (controlToBeDragged != null && controlToBeDragged.ToolStripPanelRow == this) { + controlToBeDragged.ToolStripPanelRow = null; + } + + this.ResumeLayout(true); + if (this.ControlsInternal.Count <= 0) { + ToolStripPanel.RowsInternal.Remove(this); + Dispose(); + } + } + } + + internal Size GetMinimumSize(ToolStrip toolStrip) { + if (toolStrip.MinimumSize == Size.Empty) { + return new Size(minAllowedWidth,minAllowedWidth); + } + else { + return toolStrip.MinimumSize; + } + } + + private void ApplyCachedBounds() { + for (int i = 0; i < this.Cells.Count; i++) { + IArrangedElement element = Cells[i] as IArrangedElement; + if (element.ParticipatesInLayout) { + ToolStripPanelCell cell = element as ToolStripPanelCell; + element.SetBounds(cell.CachedBounds, BoundsSpecified.None); +// Debug.Assert( cell.Control == null || cell.CachedBounds.Location == cell.Control.Bounds.Location, "CachedBounds out of sync with bounds!"); + } + } + } + + /// + protected virtual void OnLayout(LayoutEventArgs e) { + if (Initialized && !state[stateInLayout]) { + state[stateInLayout] = true; + try { + this.Margin = DefaultMargin; + CachedBoundsMode = true; + try { + // dont layout in the constructor that's just tacky. + bool parentNeedsLayout = LayoutEngine.Layout(this, e); + } + finally { + CachedBoundsMode = false; + } + + ToolStripPanelCell cell = RowManager.GetNextVisibleCell(this.Cells.Count -1, /*forward*/false); + if (cell == null) { + ApplyCachedBounds(); + } + else if (Orientation == Orientation.Horizontal) { + OnLayoutHorizontalPostFix(); + } + else { + OnLayoutVerticalPostFix(); + } + + } + finally { + state[stateInLayout] = false; + } + } + } + + + + private void OnLayoutHorizontalPostFix() { + + ToolStripPanelCell cell = RowManager.GetNextVisibleCell(this.Cells.Count -1, /*forward*/false); + if (cell == null) { + ApplyCachedBounds(); + return; + } + // figure out how much space we actually need to free. + int spaceToFree = cell.CachedBounds.Right - RowManager.DisplayRectangle.Right; + + if (spaceToFree <= 0) { + // we're all good. Just apply the cached bounds. + ApplyCachedBounds(); + return; + } + // STEP 1 remove empty space in the row. + + // since layout sisuspended, we'll need to watch changes to the margin + // as a result of calling FreeSpaceFromRow. + int[] margins = new int[Cells.Count]; + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + margins[i] = c.Margin.Left; + } + + spaceToFree -= RowManager.FreeSpaceFromRow(spaceToFree); + + // now apply those changes to the cached bounds. + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + Rectangle cachedBounds = c.CachedBounds; + cachedBounds.X -= Math.Max(0, margins[i] - c.Margin.Left); + c.CachedBounds = cachedBounds; + } + + if (spaceToFree <= 0) { + ApplyCachedBounds(); + return; + } + + + + // STEP 2 change the size of the remaing ToolStrips from Right to Left. + int[] cellOffsets = null; + for (int i = Cells.Count-1; i >= 0; i--) { + ToolStripPanelCell currentCell =Cells[i] as ToolStripPanelCell; + if (currentCell.Visible) { + Size minSize = GetMinimumSize(currentCell.Control as ToolStrip); + Rectangle cachedBounds = currentCell.CachedBounds; + + // found some space to free. + if (cachedBounds.Width > minSize.Width) { + spaceToFree -= (cachedBounds.Width - minSize.Width); + // make sure we dont take more space than we need - if spaceToFree is less than 0, add back in. + cachedBounds.Width = (spaceToFree < 0) ? minSize.Width + -spaceToFree : minSize.Width; + + // we're not reperforming a layout, so we need to adjust the next cell + for (int j = i+1; j < Cells.Count; j++) { + if (cellOffsets == null) { + cellOffsets = new int[Cells.Count]; + } + cellOffsets[j] += Math.Max(0,currentCell.CachedBounds.Width-cachedBounds.Width); + } + currentCell.CachedBounds = cachedBounds; + } + } + if (spaceToFree <= 0) { + break; + } + } + + // fixup for items before it shrinking. + if (cellOffsets != null) { + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + Rectangle cachedBounds = c.CachedBounds; + cachedBounds.X -= cellOffsets[i]; + c.CachedBounds = cachedBounds; + } + } + + ApplyCachedBounds(); + + } + + + private void OnLayoutVerticalPostFix() { + + ToolStripPanelCell cell = RowManager.GetNextVisibleCell(this.Cells.Count -1, /*forward*/false); + // figure out how much space we actually need to free. + int spaceToFree = cell.CachedBounds.Bottom - RowManager.DisplayRectangle.Bottom; + + if (spaceToFree <= 0) { + // we're all good. Just apply the cached bounds. + ApplyCachedBounds(); + return; + } + // STEP 1 remove empty space in the row. + + // since layout sisuspended, we'll need to watch changes to the margin + // as a result of calling FreeSpaceFromRow. + int[] margins = new int[Cells.Count]; + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + margins[i] = c.Margin.Top; + } + + spaceToFree -= RowManager.FreeSpaceFromRow(spaceToFree); + + // now apply those changes to the cached bounds. + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + Rectangle cachedBounds = c.CachedBounds; + cachedBounds.X = Math.Max(0, cachedBounds.X - margins[i] - c.Margin.Top); + c.CachedBounds = cachedBounds; + } + + if (spaceToFree <= 0) { + ApplyCachedBounds(); + return; + } + + + + // STEP 2 change the size of the remaing ToolStrips from Bottom to Top. + int[] cellOffsets = null; + for (int i = Cells.Count-1; i >= 0; i--) { + ToolStripPanelCell currentCell =Cells[i] as ToolStripPanelCell; + if (currentCell.Visible) { + Size minSize = GetMinimumSize(currentCell.Control as ToolStrip); + Rectangle cachedBounds = currentCell.CachedBounds; + + // found some space to free. + if (cachedBounds.Height > minSize.Height) { + spaceToFree -= (cachedBounds.Height - minSize.Height); + // make sure we dont take more space than we need - if spaceToFree is less than 0, add back in. + cachedBounds.Height = (spaceToFree < 0) ? minSize.Height + -spaceToFree : minSize.Height; + + // we're not reperforming a layout, so we need to adjust the next cell + for (int j = i+1; j < Cells.Count; j++) { + if (cellOffsets == null) { + cellOffsets = new int[Cells.Count]; + } + cellOffsets[j] += Math.Max(0,currentCell.CachedBounds.Height-cachedBounds.Height); + } + currentCell.CachedBounds = cachedBounds; + } + } + if (spaceToFree <= 0) { + break; + } + } + + // fixup for items before it shrinking. + if (cellOffsets != null) { + for (int i = 0; i < Cells.Count; i++) { + ToolStripPanelCell c = Cells[i] as ToolStripPanelCell; + Rectangle cachedBounds = c.CachedBounds; + cachedBounds.Y -= cellOffsets[i]; + c.CachedBounds = cachedBounds; + } + } + + ApplyCachedBounds(); + + } + +#if DEBUG_PAINT + internal void PaintColumns(PaintEventArgs e) { + Graphics g = e.Graphics; + + using (Pen pen = new Pen(Color.Green)) { + g.DrawRectangle(pen, this.DisplayRectangle); + } + + foreach (ToolStripPanelCell c in this.Cells) { + Rectangle inner = c.Bounds; + Rectangle b = LayoutUtils.InflateRect(inner, c.Margin); + if ((b.Width > 0) && (b.Height > 0)) { + using(Brush brush = new System.Drawing.Drawing2D.LinearGradientBrush(b, ProfessionalColors.ButtonSelectedGradientBegin, ProfessionalColors.ButtonSelectedGradientEnd, System.Drawing.Drawing2D.LinearGradientMode.Horizontal)) { + g.FillRectangle(brush, b); + g.DrawRectangle(SystemPens.ControlDarkDark, b.X, b.Y, b.Width -1, b.Height -1); + g.DrawRectangle(Pens.HotPink, inner.X, inner.Y, inner.Width -1, inner.Height -1); + if (c.Control != null) { + g.DrawString("BAD\r\n" + c.Control.Name, ToolStripPanel.Font, SystemBrushes.ControlText, inner); + } + else { + g.DrawString("BAD\r\n" + "NULL control", ToolStripPanel.Font, SystemBrushes.ControlText, inner); + + } + } + } + } + } +#endif + + private void SetBounds(Rectangle bounds) { + if (bounds != this.bounds) { + Rectangle oldBounds = this.bounds; + + this.bounds = bounds; + OnBoundsChanged(oldBounds, bounds); + } + } + + private void SuspendLayout() { + suspendCount++; + } + + private void ResumeLayout(bool performLayout) { + suspendCount--; + if (performLayout) { + ((IArrangedElement)this).PerformLayout(this, null); + } + } + + /// + /// + /// + /// + ArrangedElementCollection IArrangedElement.Children { + get { + return Cells; + } + } + + /// + /// + /// Should not be exposed as this returns an unexposed type. + /// + /// + IArrangedElement IArrangedElement.Container { + get { + return this.ToolStripPanel; + } + } + + /// + /// + /// + + + Rectangle IArrangedElement.DisplayRectangle { + get { + Rectangle displayRectangle = this.Bounds; + + return displayRectangle; + } + } + + /// + /// + /// + + + bool IArrangedElement.ParticipatesInLayout { + get { + return Visible; + } + } + + /// + /// + PropertyStore IArrangedElement.Properties { + get { + return this.Properties; + } + } + + /// + /// + Size IArrangedElement.GetPreferredSize(Size constrainingSize) { + Size preferredSize = LayoutEngine.GetPreferredSize(this, constrainingSize - Padding.Size) + Padding.Size; + + if (Orientation == Orientation.Horizontal && ParentInternal != null) { + preferredSize.Width = DisplayRectangle.Width; + } + else { + preferredSize.Height = DisplayRectangle.Height; + } + + return preferredSize; + } + + // Sets the bounds for an element. + /// + /// + void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) { + // in this case the parent is telling us to refresh our bounds - dont + // call PerformLayout + SetBounds(bounds); + } + + + /// + /// + /// + + + void IArrangedElement.PerformLayout(IArrangedElement container, string propertyName) { + if (suspendCount <= 0) { + OnLayout(new LayoutEventArgs(container, propertyName)); + } + } + + #region MouseStuff + +#if DEBUG + internal static readonly TraceSwitch ToolStripPanelMouseDebug = new TraceSwitch("ToolStripPanelMouse", "Debug WinBar WM_MOUSEACTIVATE code"); +#else + internal static readonly TraceSwitch ToolStripPanelMouseDebug; +#endif + + + internal Rectangle DragBounds { + get { + return RowManager.DragBounds; + } + } + + /// + internal void MoveControl(ToolStrip movingControl, Point startClientLocation, Point endClientLocation) { + RowManager.MoveControl(movingControl, startClientLocation, endClientLocation); + } + + // + + + internal void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag) { + RowManager.JoinRow(toolStripToDrag, locationToDrag); + } + + internal void LeaveRow(ToolStrip toolStripToDrag) { + RowManager.LeaveRow(toolStripToDrag); + if (ControlsInternal.Count == 0) { + ToolStripPanel.RowsInternal.Remove(this); + Dispose(); + } + } + + [Conditional("DEBUG")] + private void PrintPlacements(int index) { + /* Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Results:\r\n\t-------"); + Debug.Indent(); + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "ToolStripPanelRow: " + this.Bounds.ToString()); + + float sumColWidths = 0F; + int sumWidths = 0; + + for (int i = 0; i < this.Controls.Count - 1; i++) { + string indicator = (i == index) ? "*" : " "; + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, String.Format("{0} {1} Column Width {2} Control Size {3}", indicator, this.Controls[i].Name, TableLayoutSettings.ColumnStyles[i].Width, this.Controls[i].Bounds)); + sumColWidths += TableLayoutSettings.ColumnStyles[i].Width; + sumWidths += this.Controls[i].Width; + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Total Column Width " + sumColWidths.ToString() + " Total control widths " + sumWidths.ToString()); + Debug.Unindent(); + */ + } + #endregion + + + private abstract class ToolStripPanelRowManager { + private FlowLayoutSettings flowLayoutSettings = null; + + private ToolStripPanelRow owner = null; + + public ToolStripPanelRowManager(ToolStripPanelRow owner) { + this.owner = owner; + } + + public virtual bool CanMove(ToolStrip toolStripToDrag) { + ISupportToolStripPanel raftingControl = toolStripToDrag as ISupportToolStripPanel; + if (raftingControl != null) { + if (raftingControl.Stretch) { + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "TSP RM CanMove returns false - the item moving is stretched."); + return false; + } + } + foreach (Control c in Row.ControlsInternal) { + raftingControl = c as ISupportToolStripPanel; + if (raftingControl != null) { + if (raftingControl.Stretch) { + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "TSP RM CanMove returns false - the row already contains a stretched item."); + return false; + } + } + } + return true; + } + + public virtual Rectangle DragBounds { + get { return Rectangle.Empty; } + } + + public virtual Rectangle DisplayRectangle { + get { return Rectangle.Empty; } + } + + public ToolStripPanel ToolStripPanel { + get { return owner.ToolStripPanel; } + } + + public ToolStripPanelRow Row { + get { return owner; } + } + + public FlowLayoutSettings FlowLayoutSettings { + get { + if (flowLayoutSettings == null) { + flowLayoutSettings = new FlowLayoutSettings(owner); + } + + return flowLayoutSettings; + } + } + + protected internal virtual int FreeSpaceFromRow(int spaceToFree) { + return 0; + } + + protected virtual int Grow(int index, int growBy) { + int freedSpace = 0; + if (index >= 0 && index < Row.ControlsInternal.Count - 1) { + ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index]; + if (cell.Visible) { + freedSpace = cell.Grow(growBy); + } + } + return freedSpace; + } + + public ToolStripPanelCell GetNextVisibleCell(int index, bool forward) { + if (forward) { + for (int i = index; i < Row.Cells.Count; i++) { + ToolStripPanelCell cell = Row.Cells[i] as ToolStripPanelCell; + if ((cell.Visible || (owner.parent.Visible && cell.ControlInDesignMode)) && cell.ToolStripPanelRow == this.owner) { + return cell; + } + } + } + else { + for (int i = index; i >=0; i--) { + ToolStripPanelCell cell = Row.Cells[i] as ToolStripPanelCell; + if ((cell.Visible || (owner.parent.Visible && cell.ControlInDesignMode)) && cell.ToolStripPanelRow == this.owner) { + return cell; + } + } + + } + return null; + + } + + /// + /// grows all controls after the index to be their preferred size. + /// reports back how much space was used. + /// + protected virtual int GrowControlsAfter(int index, int growBy) { + if (growBy < 0) { + Debug.Fail("why was a negative number given to growControlsAfter?"); + return 0; + } + + int spaceToFree = growBy; + + for (int i = index + 1; i < Row.ControlsInternal.Count; i++) { + // grow the n+1 item first if it was previously shrunk. + int freedSpace = Grow(i, spaceToFree); + + if (freedSpace >= 0) { + spaceToFree -= freedSpace; + if (spaceToFree <= 0) { + return growBy; + } + } + } + + return growBy - spaceToFree; + } + + /// + /// grows all controls before the index to be their preferred size. + /// reports back how much space was used. + /// + protected virtual int GrowControlsBefore(int index, int growBy) { + if (growBy < 0) { + Debug.Fail("why was a negative number given to growControlsAfter?"); + return 0; + } + + int spaceToFree = growBy; + + // grow the n-1 item first if it was previously shrunk. + for (int i = index - 1; i >= 0; i--) { + spaceToFree -= Grow(i, spaceToFree); + if (spaceToFree <= 0) { + return growBy; // we've already gotten all the free space. + } + } + + return growBy - spaceToFree; + } + + + + public virtual void MoveControl(ToolStrip movingControl, Point startClientLocation, Point endClientLocation) { + // ToolStripPanel.Join(movingControl, endScreenLocation); + } + public virtual void LeaveRow(ToolStrip toolStripToDrag) { + } + + public virtual void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag) { + } + + protected internal virtual void OnControlAdded(Control c, int index) { + } + + protected internal virtual void OnControlRemoved(Control c, int index) { + } + + protected internal virtual void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { + } + } + + private class HorizontalRowManager : ToolStripPanelRowManager { + private const int DRAG_BOUNDS_INFLATE = 4; + + + public HorizontalRowManager(ToolStripPanelRow owner): base (owner) { + owner.SuspendLayout(); + FlowLayoutSettings.WrapContents = false; + FlowLayoutSettings.FlowDirection = FlowDirection.LeftToRight; + owner.ResumeLayout(false); + } + + public override Rectangle DisplayRectangle { + get { + Rectangle displayRect = ((IArrangedElement)Row).DisplayRectangle; + + if (ToolStripPanel != null) { + Rectangle raftingDisplayRectangle = ToolStripPanel.DisplayRectangle; + + if ((!ToolStripPanel.Visible || LayoutUtils.IsZeroWidthOrHeight(raftingDisplayRectangle)) && (ToolStripPanel.ParentInternal != null)){ + + // if were layed out before we're visible we have the wrong display rectangle, so we need to calculate it. + displayRect.Width = ToolStripPanel.ParentInternal.DisplayRectangle.Width - (ToolStripPanel.Margin.Horizontal + ToolStripPanel.Padding.Horizontal) - Row.Margin.Horizontal; + } + else { + displayRect.Width = raftingDisplayRectangle.Width - Row.Margin.Horizontal; + + } + } + + return displayRect; + } + } + + public override Rectangle DragBounds { + get { + Rectangle dragBounds = Row.Bounds; + int index = ToolStripPanel.RowsInternal.IndexOf(Row); + + if (index > 0) { + Rectangle previousRowBounds = ToolStripPanel.RowsInternal[index - 1].Bounds; + int y = previousRowBounds.Y + previousRowBounds.Height - (previousRowBounds.Height >> 2); + + dragBounds.Height += dragBounds.Y - y; + dragBounds.Y = y; + } + + if (index < ToolStripPanel.RowsInternal.Count - 1) { + Rectangle nextRowBounds = ToolStripPanel.RowsInternal[index + 1].Bounds; + + dragBounds.Height += (nextRowBounds.Height >> 2) + Row.Margin.Bottom + ToolStripPanel.RowsInternal[index + 1].Margin.Top; + } + + dragBounds.Width += Row.Margin.Horizontal + ToolStripPanel.Padding.Horizontal +5; + dragBounds.X -= Row.Margin.Left + ToolStripPanel.Padding.Left +4; + return dragBounds; + } + } + + /// + /// returns true if there is enough space to "raft" the control + /// ow returns false + /// + public override bool CanMove(ToolStrip toolStripToDrag) { + + if (base.CanMove(toolStripToDrag)) { + Size totalSize = Size.Empty; + + for (int i = 0; i < Row.ControlsInternal.Count; i++ ){ + totalSize += Row.GetMinimumSize(Row.ControlsInternal[i] as ToolStrip); + } + + totalSize += Row.GetMinimumSize(toolStripToDrag as ToolStrip); + return totalSize.Width < DisplayRectangle.Width; + } + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "HorizontalRM.CanMove returns false - not enough room"); + return false; + } + + protected internal override int FreeSpaceFromRow(int spaceToFree) { + int requiredSpace = spaceToFree; + // take a look at the last guy. if his right edge exceeds + // the new bounds, then we should go ahead and push him into view. + + if (spaceToFree > 0){ + // we should shrink the last guy and then move him. + ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + if (lastCellOnRow == null) { + return 0; + } + Padding cellMargin = lastCellOnRow.Margin; + + // only check margin.left as we are only concerned with getting right edge of + // the toolstrip into view. (space after the fact doesnt count). + if (cellMargin.Left >= spaceToFree) { + cellMargin.Left -= spaceToFree; + cellMargin.Right = 0; + spaceToFree = 0; + + } + else { + spaceToFree -= lastCellOnRow.Margin.Left; + cellMargin.Left = 0; + cellMargin.Right = 0; + } + lastCellOnRow.Margin = cellMargin; + + // start moving the toolstrips before this guy. + spaceToFree -= MoveLeft(Row.Cells.Count -1, spaceToFree); + + if (spaceToFree > 0) { + spaceToFree -= lastCellOnRow.Shrink(spaceToFree); + } + } + return requiredSpace - Math.Max(0,spaceToFree); + } + + public override void MoveControl(ToolStrip movingControl, Point clientStartLocation, Point clientEndLocation) { + if (Row.Locked) { + return; + } + + if (DragBounds.Contains(clientEndLocation)) { + int index = Row.ControlsInternal.IndexOf(movingControl); + int deltaX = clientEndLocation.X - clientStartLocation.X; + + if (deltaX < 0) { + // moving to the left + MoveLeft(index, deltaX * -1); + } + else { + MoveRight(index, deltaX); + } + } + else { + base.MoveControl(movingControl, clientStartLocation, clientEndLocation); + } + } + + private int MoveLeft(int index, int spaceToFree) { + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft: " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + int freedSpace = 0; + + Row.SuspendLayout(); + try { + if (spaceToFree == 0 || index < 0) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Early EXIT - 0 "); + return 0; + } + + + // remove all margins starting from the index. + for (int i = index; i >= 0; i--) { + ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + int requiredSpace = spaceToFree - freedSpace; + + Padding cellMargin = cell.Margin; + + if (cellMargin.Horizontal >= requiredSpace) { + freedSpace += requiredSpace; + + cellMargin.Left -= requiredSpace; + cellMargin.Right = 0; + cell.Margin = cellMargin; + + } + else { + freedSpace += cell.Margin.Horizontal; + cellMargin.Left = 0; + cellMargin.Right = 0; + cell.Margin = cellMargin; + } + + if (freedSpace >= spaceToFree) { + // add the space we freed to the next guy. + if (index +1 < Row.Cells.Count) { + cell = GetNextVisibleCell(index+1, /*forward*/true); + if (cell != null) { + cellMargin = cell.Margin; + cellMargin.Left += spaceToFree; + cell.Margin = cellMargin; + } + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + return spaceToFree; + } + } + } + finally { + Row.ResumeLayout(true); + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture)); + return freedSpace; + } + + private int MoveRight(int index, int spaceToFree) { + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight: " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + int freedSpace = 0; + Row.SuspendLayout(); + try { + + if (spaceToFree == 0 || index < 0 || index >= Row.ControlsInternal.Count) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Early EXIT - 0 "); + return 0; + } + + + ToolStripPanelCell cell; + Padding cellMargin; + + // remove all margins after this point in the index. + for (int i = index+1; i < Row.Cells.Count; i++) { + cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + int requiredSpace = spaceToFree - freedSpace; + + cellMargin = cell.Margin; + + if (cellMargin.Horizontal >= requiredSpace) { + freedSpace += requiredSpace; + + cellMargin.Left -= requiredSpace; + cellMargin.Right = 0; + cell.Margin = cellMargin; + + } + else { + freedSpace += cell.Margin.Horizontal; + cellMargin.Left = 0; + cellMargin.Right = 0; + cell.Margin = cellMargin; + } + + break; + } + + // add in the space at the end of the row. + if (Row.Cells.Count > 0 && (spaceToFree > freedSpace)) { + ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count -1, /*forward*/false); + if (lastCell != null) { + freedSpace += DisplayRectangle.Right - lastCell.Bounds.Right; + } + else { + freedSpace += DisplayRectangle.Width; + } + + } + + + // set the margin of the control that's moving. + if (spaceToFree <= freedSpace) { + // add the space we freed to the first guy. + cell = GetNextVisibleCell(index, /*forward*/true); + if (cell == null) { + cell = Row.Cells[index] as ToolStripPanelCell; + } + Debug.Assert(cell != null, "Dont expect cell to be null here, what's going on?"); + + if (cell != null) { + cellMargin = cell.Margin; + cellMargin.Left += spaceToFree; + cell.Margin = cellMargin; + } + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + return spaceToFree; + } + + // Now start shrinking. + for (int i = index+1; i < Row.Cells.Count; i++) { + cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + int requiredSpace = spaceToFree - freedSpace; + freedSpace += cell.Shrink(requiredSpace); + + if (spaceToFree >= freedSpace) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered (Shrink): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + Row.ResumeLayout(true); + return spaceToFree; + } + + } + + if (Row.Cells.Count == 1) { + cell = GetNextVisibleCell(index,/*forward*/true); + if (cell != null) { + cellMargin = cell.Margin; + cellMargin.Left += freedSpace; + cell.Margin = cellMargin; + } + } + + } + finally { + Row.ResumeLayout(true); + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveRight Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture)); + + return freedSpace; + } + + + public override void LeaveRow(ToolStrip toolStripToDrag) { + // this code is here to properly add space to the next control when the + // toolStripToDrag has been removed from the row. + Row.SuspendLayout(); + int index = Row.ControlsInternal.IndexOf(toolStripToDrag); + if (index >= 0) { + if (index < Row.ControlsInternal.Count -1 /*not the last one in the row*/) { + ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index]; + if (cell.Visible) { + int spaceOccupiedByCell = cell.Margin.Horizontal + cell.Bounds.Width; + + // add the space occupied by the cell to the next one. + ToolStripPanelCell nextCell = GetNextVisibleCell(index+1, /*forward*/true); + if (nextCell != null) { + Padding nextCellMargin = nextCell.Margin; + nextCellMargin.Left += spaceOccupiedByCell; + nextCell.Margin = nextCellMargin; + } + } + } + // remove the control from the row. + ((IList)Row.Cells).RemoveAt(index); + } + Row.ResumeLayout(true); + } + + protected internal override void OnControlAdded(Control control, int index) { + } + + protected internal override void OnControlRemoved(Control control, int index) { + } + + public override void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag) { + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Horizontal JoinRow called " ); + int index; + + + if (!Row.ControlsInternal.Contains(toolStripToDrag)) { + Row.SuspendLayout(); + + try { + if (Row.ControlsInternal.Count > 0) { + + // walk through the columns and determine which column you want to insert into. + for (index = 0; index < Row.Cells.Count; index++) { + ToolStripPanelCell cell = Row.Cells[index] as ToolStripPanelCell; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + + // [: ] [: x ] + if (Row.Cells[index].Bounds.Contains(locationToDrag)) { + break; + } + + // take into account the following scenarios + // [: ] x [: ] + // x [: ] [: ] + if (Row.Cells[index].Bounds.X >= locationToDrag.X) { + break; + } + + } + + Control controlToPushAside = Row.ControlsInternal[index]; + // Plop the new control in the midst of the row in question. + if (index < Row.ControlsInternal.Count) { + Row.ControlsInternal.Insert(index, toolStripToDrag); + } + else { + Row.ControlsInternal.Add(toolStripToDrag); + } + + // since layout is suspended the control may not be set to its preferred size yet + int controlToDragWidth = (toolStripToDrag.AutoSize) ? toolStripToDrag.PreferredSize.Width : toolStripToDrag.Width; + + + // + // now make it look like it belongs in the row. + // + // PUSH the controls after it to the right + + int requiredSpace = controlToDragWidth; + if (index == 0) { + // make sure we account for the left side + requiredSpace += locationToDrag.X; + } + int freedSpace = 0; + + if (index < Row.ControlsInternal.Count -1) { + ToolStripPanelCell nextCell = (ToolStripPanelCell)Row.Cells[index+1]; + Padding nextCellMargin = nextCell.Margin; + + // if we've already got the empty space + // (available to us via the margin) use that. + if (nextCellMargin.Left > requiredSpace) { + nextCellMargin.Left -= requiredSpace; + nextCell.Margin = nextCellMargin; + freedSpace = requiredSpace; + } + else { + // otherwise we've got to + // push all controls after this point to the right + // this dumps the extra stuff into the margin of index+1 + freedSpace = MoveRight(index+1, requiredSpace - freedSpace); + + // refetch the margin for "index+1" and remove the freed space + // from it - we want to actually put this to use on the control + // before this one - we're making room for the control at + // position "index" + if (freedSpace > 0) { + nextCellMargin = nextCell.Margin; + nextCellMargin.Left = Math.Max(0, nextCellMargin.Left - freedSpace); + nextCell.Margin = nextCellMargin; + } + + } + + + } + else { + // we're adding to the end. + ToolStripPanelCell nextCell = GetNextVisibleCell(Row.Cells.Count-2, /*forward*/false); + ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + + // count the stuff at the end of the row as freed space + if (nextCell != null && lastCell != null) { + Padding lastCellMargin = lastCell.Margin; + lastCellMargin.Left = Math.Max(0,locationToDrag.X - nextCell.Bounds.Right); + lastCell.Margin = lastCellMargin; + freedSpace=requiredSpace; + } + } + + // If we still need more space, then... + // PUSH the controls before it to the left + if (freedSpace < requiredSpace && index > 0) { + freedSpace = MoveLeft(index - 1, requiredSpace - freedSpace); + } + + if (index == 0) { + // if the index is zero and there were controls in the row + // we need to take care of pushing over the new cell. + if (freedSpace - controlToDragWidth > 0) { + ToolStripPanelCell newCell = Row.Cells[index] as ToolStripPanelCell; + Padding newCellMargin = newCell.Margin; + newCellMargin.Left = freedSpace - controlToDragWidth; + newCell.Margin = newCellMargin; + } + } + + + + + } + else { + + // we're adding to the beginning. + Row.ControlsInternal.Add(toolStripToDrag); + +#if DEBUG + ISupportToolStripPanel ctg = toolStripToDrag as ISupportToolStripPanel; + ToolStripPanelRow newPanelRow = ctg.ToolStripPanelRow; + Debug.Assert(newPanelRow == Row, "we should now be in the new panel row."); +#endif + if (Row.Cells.Count >0 || toolStripToDrag.IsInDesignMode) { + // we're adding to the beginning. + ToolStripPanelCell cell = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + if (cell == null && toolStripToDrag.IsInDesignMode) { + cell = (ToolStripPanelCell)Row.Cells[Row.Cells.Count-1]; + } + + if (cell != null) { + Padding cellMargin = cell.Margin; + cellMargin.Left = Math.Max(0,locationToDrag.X-Row.Margin.Left); + cell.Margin = cellMargin; + } + } + + + + } + } + finally { + Row.ResumeLayout(true); + } + } + } + + protected internal override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { + base.OnBoundsChanged(oldBounds, newBounds); + } + + + + } + + private class VerticalRowManager : ToolStripPanelRowManager { + + private const int DRAG_BOUNDS_INFLATE = 4; + + public VerticalRowManager(ToolStripPanelRow owner): base (owner) { + owner.SuspendLayout(); + FlowLayoutSettings.WrapContents = false; + FlowLayoutSettings.FlowDirection = FlowDirection.TopDown; + owner.ResumeLayout(false); + } + + + public override Rectangle DisplayRectangle { + get { + Rectangle displayRect = ((IArrangedElement)Row).DisplayRectangle; + + if (ToolStripPanel != null) { + Rectangle raftingDisplayRectangle = ToolStripPanel.DisplayRectangle; + + if ((!ToolStripPanel.Visible || LayoutUtils.IsZeroWidthOrHeight(raftingDisplayRectangle)) && (ToolStripPanel.ParentInternal != null)){ + // if were layed out before we're visible we have the wrong display rectangle, so we need to calculate it. + displayRect.Height = ToolStripPanel.ParentInternal.DisplayRectangle.Height - (ToolStripPanel.Margin.Vertical + ToolStripPanel.Padding.Vertical) - Row.Margin.Vertical; + } + else { + displayRect.Height = raftingDisplayRectangle.Height - Row.Margin.Vertical; + } + } + + return displayRect; + } + } + public override Rectangle DragBounds { + get { + Rectangle dragBounds = Row.Bounds; + int index = ToolStripPanel.RowsInternal.IndexOf(Row); + + /// + if (index > 0) { + Rectangle previousRowBounds = ToolStripPanel.RowsInternal[index - 1].Bounds; + int x = previousRowBounds.X + previousRowBounds.Width - (previousRowBounds.Width >> 2); + + dragBounds.Width += dragBounds.X - x; + dragBounds.X = x; + } + + if (index < ToolStripPanel.RowsInternal.Count - 1) { + Rectangle nextRowBounds = ToolStripPanel.RowsInternal[index + 1].Bounds; + + dragBounds.Width += (nextRowBounds.Width >> 2) + Row.Margin.Right + ToolStripPanel.RowsInternal[index + 1].Margin.Left; + } + + dragBounds.Height += Row.Margin.Vertical + ToolStripPanel.Padding.Vertical +5; + dragBounds.Y -= Row.Margin.Top+ ToolStripPanel.Padding.Top +4; + + + return dragBounds; + } + } + + /// + /// returns true if there is enough space to "raft" the control + /// ow returns false + /// + public override bool CanMove(ToolStrip toolStripToDrag) { + + if (base.CanMove(toolStripToDrag)) { + Size totalSize = Size.Empty; + + for (int i = 0; i < Row.ControlsInternal.Count; i++ ){ + totalSize += Row.GetMinimumSize(Row.ControlsInternal[i] as ToolStrip); + } + + totalSize += Row.GetMinimumSize(toolStripToDrag); + return totalSize.Height < DisplayRectangle.Height; + } + + Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "VerticalRM.CanMove returns false - not enough room"); + return false; + } + protected internal override int FreeSpaceFromRow(int spaceToFree) { + int requiredSpace = spaceToFree; + // take a look at the last guy. if his right edge exceeds + // the new bounds, then we should go ahead and push him into view. + + if (spaceToFree > 0){ + // we should shrink the last guy and then move him. + ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + if (lastCellOnRow == null) { + return 0; + } + Padding cellMargin = lastCellOnRow.Margin; + + // only check margin.left as we are only concerned with getting right edge of + // the toolstrip into view. (space after the fact doesnt count). + if (cellMargin.Top >= spaceToFree) { + cellMargin.Top -= spaceToFree; + cellMargin.Bottom = 0; + spaceToFree = 0; + + } + else { + spaceToFree -= lastCellOnRow.Margin.Top; + cellMargin.Top = 0; + cellMargin.Bottom = 0; + } + lastCellOnRow.Margin = cellMargin; + + // start moving the toolstrips before this guy. + spaceToFree -= MoveUp(Row.Cells.Count -1, spaceToFree); + + if (spaceToFree > 0) { + spaceToFree -= lastCellOnRow.Shrink(spaceToFree); + } + } + return requiredSpace - Math.Max(0,spaceToFree); + } + + + + public override void MoveControl(ToolStrip movingControl, Point clientStartLocation, Point clientEndLocation) { + + if (Row.Locked) { + return; + } + if (DragBounds.Contains(clientEndLocation)) { + int index = Row.ControlsInternal.IndexOf(movingControl); + int deltaY = clientEndLocation.Y - clientStartLocation.Y; + + if (deltaY < 0) { + // moving to the left + MoveUp(index, deltaY * -1); + } + else { + MoveDown(index, deltaY); + } + } + else { + base.MoveControl(movingControl, clientStartLocation, clientEndLocation); + } + } + + + + private int MoveUp(int index, int spaceToFree) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp: " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + int freedSpace = 0; + + Row.SuspendLayout(); + try { + if (spaceToFree == 0 || index < 0) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp Early EXIT - 0 "); + return 0; + } + + + // remove all margins starting from the index. + for (int i = index; i >= 0; i--) { + ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + int requiredSpace = spaceToFree - freedSpace; + + Padding cellMargin = cell.Margin; + + if (cellMargin.Vertical >= requiredSpace) { + freedSpace += requiredSpace; + + cellMargin.Top -= requiredSpace; + cellMargin.Bottom = 0; + cell.Margin = cellMargin; + + } + else { + freedSpace += cell.Margin.Vertical; + cellMargin.Top = 0; + cellMargin.Bottom = 0; + cell.Margin = cellMargin; + } + + if (freedSpace >= spaceToFree) { + // add the space we freed to the next guy. + if (index +1 < Row.Cells.Count) { + cell = GetNextVisibleCell(index+1, /*forward*/true); + if (cell != null) { + cellMargin = cell.Margin; + cellMargin.Top += spaceToFree; + cell.Margin = cellMargin; + } + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveUp Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + return spaceToFree; + } + } + } + finally { + Row.ResumeLayout(true); + } + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveLeft Recovered Partial (Shrink): " + freedSpace.ToString(CultureInfo.InvariantCulture)); + + return freedSpace; + } + + private int MoveDown(int index, int spaceToFree) { + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown: " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + int freedSpace = 0; + Row.SuspendLayout(); + try { + + if (spaceToFree == 0 || index < 0 || index >= Row.ControlsInternal.Count) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Early EXIT - 0 "); + return 0; + } + + + ToolStripPanelCell cell; + Padding cellMargin; + + // remove all margins after this point in the index. + for (int i = index+1; i < Row.Cells.Count; i++) { + cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + + int requiredSpace = spaceToFree - freedSpace; + + cellMargin = cell.Margin; + + if (cellMargin.Vertical >= requiredSpace) { + freedSpace += requiredSpace; + + cellMargin.Top -= requiredSpace; + cellMargin.Bottom = 0; + cell.Margin = cellMargin; + + } + else { + freedSpace += cell.Margin.Vertical; + cellMargin.Top = 0; + cellMargin.Bottom = 0; + cell.Margin = cellMargin; + } + + break; + } + + // add in the space at the end of the row. + if (Row.Cells.Count > 0 && (spaceToFree > freedSpace)) { + ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count -1, /*forward*/false); + if (lastCell != null) { + freedSpace += DisplayRectangle.Bottom - lastCell.Bounds.Bottom; + } + else { + freedSpace += DisplayRectangle.Height; + } + } + + // set the margin of the control that's moving. + if (spaceToFree <= freedSpace) { + // add the space we freed to the first guy. + cell = (ToolStripPanelCell)Row.Cells[index]; + cellMargin = cell.Margin; + cellMargin.Top += spaceToFree; + cell.Margin = cellMargin; + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered (Margin only): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + return spaceToFree; + } + + // Now start shrinking. + for (int i = index+1; i < Row.Cells.Count; i++) { + cell = (ToolStripPanelCell)Row.Cells[i]; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + int requiredSpace = spaceToFree - freedSpace; + freedSpace += cell.Shrink(requiredSpace); + + if (spaceToFree >= freedSpace) { + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered (Shrink): " + spaceToFree.ToString(CultureInfo.InvariantCulture)); + Row.ResumeLayout(true); + return spaceToFree; + } + + } + + if (Row.Cells.Count == 1) { + cell = GetNextVisibleCell(index,/*forward*/true); + if (cell != null) { + cellMargin = cell.Margin; + cellMargin.Top += freedSpace; + cell.Margin = cellMargin; + } + } + + } + finally { + Row.ResumeLayout(true); + } + + int recoveredSpace = spaceToFree - freedSpace; + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "MoveDown Recovered Partial (Shrink): " + recoveredSpace.ToString(CultureInfo.InvariantCulture)); + + return recoveredSpace; + } + + protected internal override void OnBoundsChanged(Rectangle oldBounds, Rectangle newBounds) { + + base.OnBoundsChanged(oldBounds, newBounds); + + // if our bounds have changed - we should shove the toolbars up so they're in view. + if (Row.Cells.Count > 0) { + + // take a look at the last guy. if his right edge exceeds + // the new bounds, then we should go ahead and push him into view. + ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count -1, /*forward=*/false); + int spaceToFree = (lastCell != null)? lastCell.Bounds.Bottom - newBounds.Height : 0; + + if (spaceToFree > 0){ + // we should shrink the last guy and then move him. + ToolStripPanelCell lastCellOnRow = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + + Padding cellMargin = lastCellOnRow.Margin; + + // only check margin.left as we are only concerned with getting bottom edge of + // the toolstrip into view. (space after the fact doesnt count). + if (cellMargin.Top >= spaceToFree) { + + cellMargin.Top -= spaceToFree; + cellMargin.Bottom = 0; + lastCellOnRow.Margin = cellMargin; + spaceToFree = 0; + + } + else { + spaceToFree -= lastCellOnRow.Margin.Top; + cellMargin.Top = 0; + cellMargin.Bottom = 0; + lastCellOnRow.Margin = cellMargin; + } + spaceToFree -= lastCellOnRow.Shrink(spaceToFree); + // start moving the toolstrips before this guy. + MoveUp(Row.Cells.Count -1, spaceToFree); + } + + } + + } + + + + + protected internal override void OnControlRemoved(Control c, int index) { + + } + + protected internal override void OnControlAdded(Control control, int index) { + + } + + + public override void JoinRow(ToolStrip toolStripToDrag, Point locationToDrag) { + + Debug.WriteLineIf(ToolStripPanelMouseDebug.TraceVerbose, "Vertical JoinRow called " ); + int index; + + if (!Row.ControlsInternal.Contains(toolStripToDrag)) { + Row.SuspendLayout(); + try { + if (Row.ControlsInternal.Count > 0) { + + + // walk through the columns and determine which column you want to insert into. + for (index = 0; index < Row.Cells.Count; index++) { + ToolStripPanelCell cell = Row.Cells[index] as ToolStripPanelCell; + if (!cell.Visible && !cell.ControlInDesignMode) { + continue; + } + // [: ] [: x ] + if (cell.Bounds.Contains(locationToDrag)) { + break; + } + + // take into account the following scenarios + // [: ] x [: ] + // x [: ] [: ] + if (cell.Bounds.Y >= locationToDrag.Y) { + break; + } + + } + + Control controlToPushAside = Row.ControlsInternal[index]; + // Plop the new control in the midst of the row in question. + if (index < Row.ControlsInternal.Count) { + Row.ControlsInternal.Insert(index, toolStripToDrag); + } + else { + Row.ControlsInternal.Add(toolStripToDrag); + } + + // since layout is suspended the control may not be set to its preferred size yet + int controlToDragWidth = (toolStripToDrag.AutoSize) ? toolStripToDrag.PreferredSize.Height : toolStripToDrag.Height; + + // + // now make it look like it belongs in the row. + // + // PUSH the controls after it to the right + + int requiredSpace = controlToDragWidth; + + if (index == 0) { + // make sure we account for the left side + requiredSpace += locationToDrag.Y; + } + int freedSpace = 0; + + if (index < Row.ControlsInternal.Count -1) { + + ToolStripPanelCell nextCell = GetNextVisibleCell(index+1, /*forward*/true); + if (nextCell != null) { + Padding nextCellMargin = nextCell.Margin; + + // if we've already got the empty space + // (available to us via the margin) use that. + if (nextCellMargin.Top > requiredSpace) { + nextCellMargin.Top -= requiredSpace; + nextCell.Margin = nextCellMargin; + freedSpace = requiredSpace; + } + else { + // otherwise we've got to + // push all controls after this point to the right + // this dumps the extra stuff into the margin of index+1 + freedSpace = MoveDown(index+1, requiredSpace - freedSpace); + + // refetch the margin for "index+1" and remove the freed space + // from it - we want to actually put this to use on the control + // before this one - we're making room for the control at + // position "index" + if (freedSpace > 0) { + nextCellMargin = nextCell.Margin; + nextCellMargin.Top -= freedSpace; + nextCell.Margin = nextCellMargin; + } + + } + } + + + } + else { + // we're adding to the end. + ToolStripPanelCell nextCell = GetNextVisibleCell(Row.Cells.Count-2, /*forward*/false); + ToolStripPanelCell lastCell = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + + // count the stuff at the end of the row as freed space + if (nextCell != null && lastCell != null) { + Padding lastCellMargin = lastCell.Margin; + lastCellMargin.Top = Math.Max(0,locationToDrag.Y - nextCell.Bounds.Bottom); + lastCell.Margin = lastCellMargin; + freedSpace=requiredSpace; + } + + } + + // If we still need more space, then... + // PUSH the controls before it to the left + if (freedSpace < requiredSpace && index > 0) { + freedSpace = MoveUp(index - 1, requiredSpace - freedSpace); + } + + + if (index == 0) { + // if the index is zero and there were controls in the row + // we need to take care of pushing over the new cell. + if (freedSpace - controlToDragWidth > 0) { + ToolStripPanelCell newCell = Row.Cells[index] as ToolStripPanelCell; + Padding newCellMargin = newCell.Margin; + newCellMargin.Top = freedSpace - controlToDragWidth; + newCell.Margin = newCellMargin; + } + } + + } + else { + + // we're adding to the beginning. + Row.ControlsInternal.Add(toolStripToDrag); + +#if DEBUG + ISupportToolStripPanel ctg = toolStripToDrag as ISupportToolStripPanel; + ToolStripPanelRow newPanelRow = ctg.ToolStripPanelRow; + Debug.Assert(newPanelRow == Row, "we should now be in the new panel row."); +#endif + if (Row.Cells.Count >0) { + ToolStripPanelCell cell = GetNextVisibleCell(Row.Cells.Count-1, /*forward*/false); + if (cell != null) { + Padding cellMargin = cell.Margin; + cellMargin.Top = Math.Max(0,locationToDrag.Y-Row.Margin.Top); + cell.Margin = cellMargin; + } + } + } + } + finally { + Row.ResumeLayout(true); + } + } + } + + public override void LeaveRow(ToolStrip toolStripToDrag) { + // this code is here to properly add space to the next control when the + // toolStripToDrag has been removed from the row. + Row.SuspendLayout(); + int index = Row.ControlsInternal.IndexOf(toolStripToDrag); + if (index >= 0) { + if (index < Row.ControlsInternal.Count -1 /*not the last one in the row*/) { + + ToolStripPanelCell cell = (ToolStripPanelCell)Row.Cells[index]; + if (cell.Visible) { + int spaceOccupiedByCell = cell.Margin.Vertical + cell.Bounds.Height; + + // add the space occupied by the cell to the next one. + ToolStripPanelCell nextCell = GetNextVisibleCell(index+1, /*forward*/true); + if (nextCell != null) { + Padding nextCellMargin = nextCell.Margin; + nextCellMargin.Top += spaceOccupiedByCell; + nextCell.Margin = nextCellMargin; + } + } + } + // remove the control from the row. + ((IList)Row.Cells).RemoveAt(index); + } + Row.ResumeLayout(true); + } + } + + + + /// + /// ToolStripPanelRowControlCollection + /// + /// this class represents the collection of controls on a particular row. + /// when you add and remove controls from this collection - you also add and remove + /// controls to and from the ToolStripPanel.Control's collection (which happens + /// to be externally readonly.) + /// + /// This class is used to represent the IArrangedElement.Children for the ToolStripPanelRow - + /// which means that this collection represents the IArrangedElements to layout for + /// a particular ToolStripPanelRow. + /// + /// We need to keep copies of the controls in both the ToolStripPanelRowControlCollection and + /// the ToolStripPanel.Control collection as the ToolStripPanel.Control collection + /// is responsible for parenting and unparenting the controls (ToolStripPanelRows do NOT derive from + /// Control and thus are NOT hwnd backed). + /// + /// + internal class ToolStripPanelRowControlCollection : ArrangedElementCollection, IList, IEnumerable { + private ToolStripPanelRow owner; + private ArrangedElementCollection cellCollection; + + /// + public ToolStripPanelRowControlCollection(ToolStripPanelRow owner) { + this.owner = owner; + } + + + /// + public ToolStripPanelRowControlCollection(ToolStripPanelRow owner, Control[] value) { + this.owner = owner; + AddRange(value); + } + + /// + public new virtual Control this[int index] { + get { + return GetControl(index); + } + } + + public ArrangedElementCollection Cells { + get { + if (cellCollection == null) { + cellCollection = new ArrangedElementCollection(InnerList); + } + return cellCollection; + } + } + + + /// + public ToolStripPanel ToolStripPanel { + get { + return owner.ToolStripPanel; + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int Add(Control value) { + ISupportToolStripPanel control = value as ISupportToolStripPanel; + + if (value == null) { + throw new ArgumentNullException("value"); + } + if (control == null) { + throw new NotSupportedException(SR.GetString(SR.TypedControlCollectionShouldBeOfType, typeof(ToolStrip).Name)); + } + + int index = InnerList.Add(control.ToolStripPanelCell); + + OnAdd(control, index); + return index; + } + + /// + /// + /// [To be supplied.] + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void AddRange(Control[] value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + + ToolStripPanel currentOwner = ToolStripPanel; + + if (currentOwner != null) { + currentOwner.SuspendLayout(); + } + + try { + for (int i = 0; i < value.Length; i++) { + this.Add(value[i]); + } + } + finally { + if (currentOwner != null) { + currentOwner.ResumeLayout(); + } + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(Control value) { + for (int i = 0; i < Count; i++) { + if (GetControl(i) == value) { + return true; + } + } + return false; + } + + /// + /// + /// [To be supplied.] + /// + public virtual void Clear() { + if (owner != null) { + ToolStripPanel.SuspendLayout(); + } + + try { + while (Count != 0) { + RemoveAt(Count - 1); + } + } + finally { + if (owner != null) { + ToolStripPanel.ResumeLayout(); + } + } + } + + public override IEnumerator GetEnumerator() { return new ToolStripPanelCellToControlEnumerator(InnerList); } + + private Control GetControl(int index) { + Control control = null; + ToolStripPanelCell cell = null; + + if (index < Count && index >= 0) { + cell = (ToolStripPanelCell)(InnerList[index]); + control = (cell != null) ? cell.Control : null; + } + return control; + + } + private int IndexOfControl(Control c) { + for (int i = 0; i < Count; i++) { + ToolStripPanelCell cell = (ToolStripPanelCell)(InnerList[i]); + if (cell.Control == c) { + return i; + } + } + return -1; + + } + + + + + void IList.Clear() { Clear(); } + + bool IList.IsFixedSize { get { return InnerList.IsFixedSize; } } + + bool IList.Contains(object value) { return InnerList.Contains(value); } + + bool IList.IsReadOnly { get { return InnerList.IsReadOnly; } } + + void IList.RemoveAt(int index) { RemoveAt(index); } + + void IList.Remove(object value) { Remove(value as Control); } + + int IList.Add(object value) { return Add(value as Control); } + + int IList.IndexOf(object value) { return IndexOf(value as Control); } + + void IList.Insert(int index, object value) { Insert(index, value as Control); } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(Control value) { + for (int i = 0; i < Count; i++) { + if (GetControl(i) == value) { + return i; + } + } + return -1; + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void Insert(int index, Control value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + ISupportToolStripPanel control = value as ISupportToolStripPanel; + if (control == null) { + throw new NotSupportedException(SR.GetString(SR.TypedControlCollectionShouldBeOfType, typeof(ToolStrip).Name)); + } + + InnerList.Insert(index, control.ToolStripPanelCell); + OnAdd(control, index); + } + + /// + /// Do proper cleanup of ownership, etc. + /// + private void OnAfterRemove(Control control, int index) { + if (owner != null) { + // unfortunately we dont know the index of the control in the ToolStripPanel's + // control collection, as all rows share this collection. + // To unparent this control we need to use Remove instead of RemoveAt. + using (LayoutTransaction t = new LayoutTransaction(ToolStripPanel, control, PropertyNames.Parent)) { + owner.ToolStripPanel.Controls.Remove(control); + owner.OnControlRemoved(control, index); + } + } + } + + private void OnAdd(ISupportToolStripPanel controlToBeDragged, int index) { + if (owner != null) { + LayoutTransaction layoutTransaction = null; + if (ToolStripPanel != null && ToolStripPanel.ParentInternal != null) { + layoutTransaction = new LayoutTransaction(ToolStripPanel, ToolStripPanel.ParentInternal, PropertyNames.Parent); + + } + try { + + if (controlToBeDragged != null) { + + controlToBeDragged.ToolStripPanelRow = owner; + + Control control = controlToBeDragged as Control; + + if (control != null) { + control.ParentInternal = owner.ToolStripPanel; + owner.OnControlAdded(control, index); + } + } + + } + finally { + if (layoutTransaction != null) { + layoutTransaction.Dispose(); + } + } + + } + } + + /// + /// + /// [To be supplied.] + /// + + [EditorBrowsable(EditorBrowsableState.Never)] + public void Remove(Control value) { + int index = IndexOfControl(value); + RemoveAt(index); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void RemoveAt(int index) { + if (index >= 0 && index < Count) { + Control control = GetControl(index); + ToolStripPanelCell cell = InnerList[index] as ToolStripPanelCell; + InnerList.RemoveAt(index); + OnAfterRemove(control, index); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void CopyTo(Control[] array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (index < 0) { + throw new ArgumentOutOfRangeException("index"); + } + + if (index >= array.Length || InnerList.Count > array.Length - index) { + throw new ArgumentException(SR.GetString(SR.ToolStripPanelRowControlCollectionIncorrectIndexLength)); + } + + for (int i = 0; i < InnerList.Count; i++) { + array[index++] = GetControl(i); + } + } + + /// We want to pretend like we're only holding controls... so everywhere we've returned controls. + /// but the problem is if you do a foreach, you'll get the cells not the controls. So we've got + /// to sort of write a wrapper class around the ArrayList enumerator. + private class ToolStripPanelCellToControlEnumerator : IEnumerator, ICloneable { + private IEnumerator arrayListEnumerator; + + internal ToolStripPanelCellToControlEnumerator(ArrayList list) { + arrayListEnumerator = ((IEnumerable)list).GetEnumerator(); + } + + public virtual Object Current { + get { + ToolStripPanelCell cell = arrayListEnumerator.Current as ToolStripPanelCell; + Debug.Assert(cell != null, "Expected ToolStripPanel cells only!!!" + arrayListEnumerator.Current.GetType().ToString()); + return (cell == null) ? null : cell.Control; + } + } + + + public Object Clone() { + return MemberwiseClone(); + } + + public virtual bool MoveNext() { + return arrayListEnumerator.MoveNext(); + } + + public virtual void Reset() { + arrayListEnumerator.Reset(); + } + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripPointType.cs b/WindowsForms/Managed/System/WinForms/ToolStripPointType.cs new file mode 100644 index 000000000..930c709e4 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripPointType.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + internal enum ToolStripPointType { + ToolStripCoords, + ScreenCoords, + ToolStripItemCoords + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripProfessionalLowResolutionRenderer.cs b/WindowsForms/Managed/System/WinForms/ToolStripProfessionalLowResolutionRenderer.cs new file mode 100644 index 000000000..22bf8f507 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripProfessionalLowResolutionRenderer.cs @@ -0,0 +1,87 @@ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + + internal class ToolStripProfessionalLowResolutionRenderer : ToolStripProfessionalRenderer { + + public ToolStripProfessionalLowResolutionRenderer() { + } + + internal override ToolStripRenderer RendererOverride { + get { + return null; + } + } + + protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + if( e.ToolStrip is ToolStripDropDown ) { + base.OnRenderToolStripBackground(e); + } + } + + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + + if(e.ToolStrip is MenuStrip) { + return; + } + else if (e.ToolStrip is StatusStrip ) { + return; + } + else if (e.ToolStrip is ToolStripDropDown) { + base.OnRenderToolStripBorder(e); + } + else { + RenderToolStripBorderInternal(e); + } + } + + + private void RenderToolStripBorderInternal(ToolStripRenderEventArgs e) { + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + Graphics g = e.Graphics; + + // have to create a pen here because we're not allowed to modify the SystemPens. + using( Pen p = new Pen(SystemColors.ButtonShadow) ) { + p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; + + bool oddWidth = ((bounds.Width & 0x1) == 0x1); + bool oddHeight = ((bounds.Height & 0x1) == 0x1); + int indent = 2; + + // top + g.DrawLine(p, bounds.X + indent, bounds.Y, bounds.Width - 1, bounds.Y); + // bottom + g.DrawLine(p, bounds.X + indent, bounds.Height - 1, bounds.Width - 1, bounds.Height - 1); + + // left + g.DrawLine(p, bounds.X, bounds.Y + indent, bounds.X, bounds.Height - 1); + // right + g.DrawLine(p, bounds.Width - 1, bounds.Y + indent, bounds.Width - 1, bounds.Height - 1); + + // connecting pixels + + // top left conntecting pixel - always drawn + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(1, 1, 1, 1)); + + if (oddWidth) { + // top right pixel + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(bounds.Width - 2, 1, 1, 1)); + } + // bottom conntecting pixels - drawn only if height is odd + if(oddHeight) { + // bottom left + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(1, bounds.Height - 2, 1, 1)); + + } + + // top and bottom right conntecting pixel - drawn only if height and width are odd + if( oddHeight && oddWidth ) { + // bottom right + g.FillRectangle(SystemBrushes.ButtonShadow, new Rectangle(bounds.Width - 2, bounds.Height - 2, 1, 1)); + } + + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripProfessionalRenderer.cs b/WindowsForms/Managed/System/WinForms/ToolStripProfessionalRenderer.cs new file mode 100644 index 000000000..5ed20a534 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripProfessionalRenderer.cs @@ -0,0 +1,1491 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Drawing; + using System.Windows.Forms; + using System.Drawing.Drawing2D; + using System.Diagnostics; + using System.Windows.Forms.Layout; + + /// + /// + /// Summary description for ProfessionalToolStripRenderer. + /// + public class ToolStripProfessionalRenderer : ToolStripRenderer { + private const int GRIP_PADDING = 4; + private int gripPadding = GRIP_PADDING; + + private const int ICON_WELL_GRADIENT_WIDTH = 12; + private int iconWellGradientWidth = ICON_WELL_GRADIENT_WIDTH; + + private static readonly Size onePix = new Size(1, 1); + + private bool isScalingInitialized = false; + private const int OVERFLOW_BUTTON_WIDTH = 12; + private const int OVERFLOW_ARROW_WIDTH = 9; + private const int OVERFLOW_ARROW_HEIGHT = 5; + private const int OVERFLOW_ARROW_OFFSETY = 8; + private int overflowButtonWidth = OVERFLOW_BUTTON_WIDTH; + private int overflowArrowWidth = OVERFLOW_ARROW_WIDTH; + private int overflowArrowHeight = OVERFLOW_ARROW_HEIGHT; + private int overflowArrowOffsetY = OVERFLOW_ARROW_OFFSETY; + + private const int DROP_DOWN_MENU_ITEM_PAINT_PADDING_SIZE = 1; + private Padding scaledDropDownMenuItemPaintPadding = new Padding(DROP_DOWN_MENU_ITEM_PAINT_PADDING_SIZE + 1, 0, DROP_DOWN_MENU_ITEM_PAINT_PADDING_SIZE, 0); + private ProfessionalColorTable professionalColorTable; + private bool roundedEdges = true; + private ToolStripRenderer toolStripHighContrastRenderer; + private ToolStripRenderer toolStripLowResolutionRenderer; + + /// + public ToolStripProfessionalRenderer() { + } + internal ToolStripProfessionalRenderer(bool isDefault) : base(isDefault) { + } + + public ToolStripProfessionalRenderer(ProfessionalColorTable professionalColorTable) { + this.professionalColorTable = professionalColorTable; + } + + public ProfessionalColorTable ColorTable { + get { + if (professionalColorTable == null) { + return ProfessionalColors.ColorTable; + } + return professionalColorTable; + } + } + + internal override ToolStripRenderer RendererOverride { + get { + if (DisplayInformation.HighContrast) { + return HighContrastRenderer; + } + if (DisplayInformation.LowResolution) { + return LowResolutionRenderer; + } + return null; + } + } + + internal ToolStripRenderer HighContrastRenderer { + get { + if (toolStripHighContrastRenderer == null) { + toolStripHighContrastRenderer = new ToolStripHighContrastRenderer(/*renderLikeSystem*/false); + } + return toolStripHighContrastRenderer; + } + } + + internal ToolStripRenderer LowResolutionRenderer { + get { + if (toolStripLowResolutionRenderer == null) { + toolStripLowResolutionRenderer = new ToolStripProfessionalLowResolutionRenderer(); + } + return toolStripLowResolutionRenderer; + } + } + + + + public bool RoundedEdges { + get { + return roundedEdges; + } + set { + roundedEdges = value; + } + } + + + private bool UseSystemColors { + get { return (ColorTable.UseSystemColors || !ToolStripManager.VisualStylesEnabled); } + } + + /// + protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderToolStripBackground(e); + return; + } + + ToolStrip toolStrip = e.ToolStrip; + + if (!ShouldPaintBackground(toolStrip)) { + return; + } + + if (toolStrip is ToolStripDropDown) { + RenderToolStripDropDownBackground(e); + } + else if (toolStrip is MenuStrip) { + RenderMenuStripBackground(e); + } + else if (toolStrip is StatusStrip) { + RenderStatusStripBackground(e); + } + else { + RenderToolStripBackgroundInternal(e); + } + } + + /// + protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) { + ScaleObjectSizesIfNeeded(e.ToolStrip.DeviceDpi); + + if (RendererOverride != null) { + base.OnRenderOverflowButtonBackground(e); + return; + } + + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + + // fill in the background colors + bool rightToLeft = (item.RightToLeft == RightToLeft.Yes); + RenderOverflowBackground(e, rightToLeft); + + bool horizontal = (e.ToolStrip.Orientation == Orientation.Horizontal); + + Rectangle overflowArrowRect = Rectangle.Empty; + if (rightToLeft) { + overflowArrowRect = new Rectangle(0, item.Height - overflowArrowOffsetY, overflowArrowWidth, overflowArrowHeight); + } + else { + overflowArrowRect = new Rectangle(item.Width - overflowButtonWidth, item.Height - overflowArrowOffsetY, overflowArrowWidth, overflowArrowHeight); + } + + ArrowDirection direction = (horizontal) ? ArrowDirection.Down : ArrowDirection.Right; + + // in RTL the white highlight goes BEFORE the black triangle. + int rightToLeftShift = (rightToLeft && horizontal) ? -1: 1; + + // draw highlight + overflowArrowRect.Offset(1*rightToLeftShift, 1); + RenderArrowInternal(g, overflowArrowRect, direction, SystemBrushes.ButtonHighlight); + + // draw black triangle + overflowArrowRect.Offset(-1*rightToLeftShift, -1); + Point middle = RenderArrowInternal(g, overflowArrowRect, direction, SystemBrushes.ControlText); + + // draw lines + if (horizontal) { + rightToLeftShift = (rightToLeft) ? -2 : 0; + // width of the both lines is 1 pixel and lines are drawn next to each other, this the highlight line is 1 pixel below the black line + g.DrawLine(SystemPens.ControlText, + middle.X - ToolStripRenderer.Offset2X, + overflowArrowRect.Y - ToolStripRenderer.Offset2Y, + middle.X + ToolStripRenderer.Offset2X, + overflowArrowRect.Y - ToolStripRenderer.Offset2Y); + g.DrawLine(SystemPens.ButtonHighlight, + middle.X - ToolStripRenderer.Offset2X + 1 + rightToLeftShift, + overflowArrowRect.Y - ToolStripRenderer.Offset2Y + 1, + middle.X + ToolStripRenderer.Offset2X + 1 + rightToLeftShift, + overflowArrowRect.Y - ToolStripRenderer.Offset2Y + 1); + } + else { + g.DrawLine(SystemPens.ControlText, + overflowArrowRect.X, + middle.Y - ToolStripRenderer.Offset2Y, + overflowArrowRect.X, + middle.Y + ToolStripRenderer.Offset2Y); + g.DrawLine(SystemPens.ButtonHighlight, + overflowArrowRect.X + 1, + middle.Y - ToolStripRenderer.Offset2Y + 1, + overflowArrowRect.X + 1, + middle.Y + ToolStripRenderer.Offset2Y + 1); + } + } + + /// + protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderDropDownButtonBackground(e); + return; + } + + ToolStripDropDownItem item = e.Item as ToolStripDropDownItem; + + if (item != null && item.Pressed && item.HasDropDownItems) { + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + + RenderPressedGradient(e.Graphics, bounds); + } + else { + RenderItemInternal(e, /*useHotBorder =*/true); + } + } + + /// + protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderSeparator(e); + return; + } + + RenderSeparatorInternal(e.Graphics, e.Item, new Rectangle(Point.Empty, e.Item.Size), e.Vertical); + } + + /// + protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderSplitButtonBackground(e); + return; + } + + ToolStripSplitButton item = e.Item as ToolStripSplitButton; + Graphics g = e.Graphics; + + if (item != null) { + + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + if (item.BackgroundImage != null) { + Rectangle fillRect = (item.Selected) ? item.ContentRectangle : bounds; + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + + bool buttonPressedOrSelected = (item.Pressed || item.ButtonPressed || item.Selected || item.ButtonSelected); + + if (buttonPressedOrSelected) { + RenderItemInternal(e, /*useHotBorder =*/true); + } + + if (item.ButtonPressed) { + Rectangle buttonBounds = item.ButtonBounds; + // We subtract 1 from each side except the right. + // This is because we've already drawn the border, and we don't + // want to draw over it. We don't do the right edge, because we + // drew the border around the whole control, not the button. + Padding deflatePadding = item.RightToLeft == RightToLeft.Yes ? new Padding(0, 1, 1, 1) : new Padding(1, 1, 0, 1); + buttonBounds = LayoutUtils.DeflateRect(buttonBounds, deflatePadding); + RenderPressedButtonFill(g, buttonBounds); + } + else if (item.Pressed) { + RenderPressedGradient(e.Graphics, bounds); + } + Rectangle dropDownRect = item.DropDownButtonBounds; + + if (buttonPressedOrSelected && !item.Pressed) { + using (Brush b = new SolidBrush(ColorTable.ButtonSelectedBorder)) { + g.FillRectangle(b, item.SplitterBounds); + } + } + DrawArrow(new ToolStripArrowRenderEventArgs(g, item, dropDownRect, SystemColors.ControlText, ArrowDirection.Down)); + } + } + + protected override void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderToolStripStatusLabelBackground(e); + return; + } + + RenderLabelInternal(e); + ToolStripStatusLabel item = e.Item as ToolStripStatusLabel; + ControlPaint.DrawBorder3D(e.Graphics, new Rectangle(0,0,item.Width, item.Height), item.BorderStyle, (Border3DSide)item.BorderSides); + } + + /// + protected override void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderLabelBackground(e); + return; + } + + RenderLabelInternal(e); + } + + + /// + protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderButtonBackground(e); + return; + } + + ToolStripButton item = e.Item as ToolStripButton; + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + + if (item.CheckState == CheckState.Unchecked) { + RenderItemInternal(e, /*useHotBorder = */ true); + } + else { + Rectangle fillRect = (item.Selected) ? item.ContentRectangle :bounds; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + + if (UseSystemColors) { + if (item.Selected) { + RenderPressedButtonFill(g, bounds); + } + else { + RenderCheckedButtonFill(g, bounds); + } + + using (Pen p = new Pen(ColorTable.ButtonSelectedBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + + } + else { + if (item.Selected) { + RenderPressedButtonFill(g,bounds); + } + else { + RenderCheckedButtonFill(g,bounds); + } + using (Pen p = new Pen(ColorTable.ButtonSelectedBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + + } + } + + } + + /// + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderToolStripBorder(e); + return; + } + + ToolStrip toolStrip = e.ToolStrip; + Graphics g = e.Graphics; + + + if (toolStrip is ToolStripDropDown) { + RenderToolStripDropDownBorder(e); + } + else if (toolStrip is MenuStrip) { + } + else if (toolStrip is StatusStrip) { + RenderStatusStripBorder(e); + } + else { + Rectangle bounds = new Rectangle(Point.Empty, toolStrip.Size); + + // draw the shadow lines on the bottom and right + using (Pen p = new Pen(ColorTable.ToolStripBorder)) { + if (toolStrip.Orientation == Orientation.Horizontal) { + // horizontal line at bottom + g.DrawLine(p, bounds.Left, bounds.Height - 1, bounds.Right, bounds.Height - 1); + if (RoundedEdges) { + // one pix corner rounding (right bottom) + g.DrawLine(p, bounds.Width - 2, bounds.Height - 2, bounds.Width - 1, bounds.Height - 3); + } + } + else { + // draw vertical line on the right + g.DrawLine(p, bounds.Width -1, 0,bounds.Width -1, bounds.Height - 1); + if (RoundedEdges) { + // one pix corner rounding (right bottom) + g.DrawLine(p, bounds.Width - 2, bounds.Height - 2, bounds.Width - 1, bounds.Height - 3); + } + + } + } + + if (RoundedEdges) { + // OverflowButton rendering + if (toolStrip.OverflowButton.Visible) { + RenderOverflowButtonEffectsOverBorder(e); + } + else { + + // Draw 1PX edging to round off the toolStrip + Rectangle edging = Rectangle.Empty; + if (toolStrip.Orientation == Orientation.Horizontal) { + edging = new Rectangle(bounds.Width - 1, 3, 1, bounds.Height - 3); + } + else { + edging = new Rectangle(3, bounds.Height -1, bounds.Width -3, bounds.Height - 1); + + } + ScaleObjectSizesIfNeeded(toolStrip.DeviceDpi); + FillWithDoubleGradient(ColorTable.OverflowButtonGradientBegin, ColorTable.OverflowButtonGradientMiddle, ColorTable.OverflowButtonGradientEnd, e.Graphics, edging, iconWellGradientWidth, iconWellGradientWidth, LinearGradientMode.Vertical, /*flipHorizontal=*/false); + RenderToolStripCurve(e); + } + } + } + } + + /// + protected override void OnRenderGrip(ToolStripGripRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderGrip(e); + return; + } + + ScaleObjectSizesIfNeeded(e.ToolStrip.DeviceDpi); + + Graphics g = e.Graphics; + Rectangle bounds = e.GripBounds; + ToolStrip toolStrip = e.ToolStrip; + + bool rightToLeft = (e.ToolStrip.RightToLeft == RightToLeft.Yes); + + int height = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Height : bounds.Width; + int width = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Width : bounds.Height; + + int numRectangles = (height - (gripPadding * 2)) / 4; + + + if (numRectangles > 0) { + // a MenuStrip starts its grip lower and has fewer grip rectangles. + int yOffset = (toolStrip is MenuStrip) ? 2 : 0; + + Rectangle[] shadowRects = new Rectangle[numRectangles]; + int startY = gripPadding + 1 + yOffset; + int startX = (width / 2); + + for (int i = 0; i < numRectangles; i++) { + shadowRects[i] = (toolStrip.Orientation == Orientation.Horizontal) ? + new Rectangle(startX, startY, 2, 2) : + new Rectangle(startY, startX, 2,2); + + startY += 4; + } + + // in RTL the GripLight rects should paint to the left of the GripDark rects. + int xOffset = (rightToLeft) ? 1 : -1; + + if (rightToLeft) { + // scoot over the rects in RTL so they fit within the bounds. + for (int i = 0; i < numRectangles; i++) { + shadowRects[i].Offset(-xOffset, 0); + } + } + + using (Brush b = new SolidBrush(ColorTable.GripLight)) { + g.FillRectangles(b, shadowRects); + } + + for (int i = 0; i < numRectangles; i++) { + shadowRects[i].Offset(xOffset, -1); + } + + using (Brush b = new SolidBrush(ColorTable.GripDark)) { + g.FillRectangles(b, shadowRects); + } + } + } + + /// + protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderMenuItemBackground(e); + return; + } + + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + + if (item is MdiControlStrip.SystemMenuItem) { + return; // no highlights are painted behind a system menu item + } + + + if (item.IsOnDropDown) { + ScaleObjectSizesIfNeeded(item.DeviceDpi); + + bounds = LayoutUtils.DeflateRect(bounds, scaledDropDownMenuItemPaintPadding); + + if (item.Selected) { + Color borderColor = ColorTable.MenuItemBorder; + if (item.Enabled) { + if (UseSystemColors) { + borderColor = SystemColors.Highlight; + RenderSelectedButtonFill(g, bounds); + } + else { + using (Brush b = new SolidBrush(ColorTable.MenuItemSelected)) { + g.FillRectangle(b, bounds); + } + } + } + // draw selection border - always drawn regardless of Enabled. + using (Pen p = new Pen(borderColor)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + else { + Rectangle fillRect = bounds; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + else if (item.Owner != null && item.BackColor != item.Owner.BackColor) { + using (Brush b = new SolidBrush(item.BackColor)) { + g.FillRectangle(b, fillRect); + } + } + } + } + else { + + if (item.Pressed) { + // Toplevel toolstrip rendering + RenderPressedGradient(g, bounds); + + } + else if (item.Selected) { + //Hot, Pressed behavior + // Fill with orange + Color borderColor = ColorTable.MenuItemBorder; + + if (item.Enabled) { + + if (UseSystemColors) { + borderColor = SystemColors.Highlight; + RenderSelectedButtonFill(g, bounds); + } + else { + using (Brush b = new LinearGradientBrush(bounds, ColorTable.MenuItemSelectedGradientBegin, ColorTable.MenuItemSelectedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, bounds); + } + } + } + + // draw selection border - always drawn regardless of Enabled. + using (Pen p = new Pen(borderColor)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + else { + Rectangle fillRect = bounds; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + else if (item.Owner != null && item.BackColor != item.Owner.BackColor) { + using (Brush b = new SolidBrush(item.BackColor)) { + g.FillRectangle(b, fillRect); + } + } + } + } + + } + + /// + protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderArrow(e); + return; + } + + ToolStripItem item = e.Item; + + if (item is ToolStripDropDownItem) { + e.DefaultArrowColor = (item.Enabled) ? SystemColors.ControlText : SystemColors.ControlDark; + } + + base.OnRenderArrow(e); + } + + /// + protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderImageMargin(e); + return; + } + + ScaleObjectSizesIfNeeded(e.ToolStrip.DeviceDpi); + + Graphics g = e.Graphics; + Rectangle bounds = e.AffectedBounds; + bounds.Y += 2; + bounds.Height -= 4; /*shrink to accomodate 1PX line*/ + RightToLeft rightToLeft = e.ToolStrip.RightToLeft; + Color begin = (rightToLeft == RightToLeft.No) ? ColorTable.ImageMarginGradientBegin : ColorTable.ImageMarginGradientEnd; + Color end = (rightToLeft == RightToLeft.No) ? ColorTable.ImageMarginGradientEnd : ColorTable.ImageMarginGradientBegin; + + FillWithDoubleGradient(begin, ColorTable.ImageMarginGradientMiddle, end, e.Graphics, bounds, iconWellGradientWidth, iconWellGradientWidth, LinearGradientMode.Horizontal, /*flipHorizontal=*/(e.ToolStrip.RightToLeft == RightToLeft.Yes)); + } + + /// + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderItemText(e); + return; + } + + if (e.Item is ToolStripMenuItem && (e.Item.Selected || e.Item.Pressed)) { + e.DefaultTextColor = e.Item.ForeColor; + } + + base.OnRenderItemText(e); + } + + /// + protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderItemCheck(e); + return; + } + + RenderCheckBackground(e); + base.OnRenderItemCheck(e); + } + /// + protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderItemImage(e); + return; + } + + Rectangle imageRect = e.ImageRectangle; + Image image = e.Image; + + if (e.Item is ToolStripMenuItem) { + ToolStripMenuItem item = e.Item as ToolStripMenuItem; + if (item.CheckState != CheckState.Unchecked) { + ToolStripDropDownMenu dropDownMenu = item.ParentInternal as ToolStripDropDownMenu; + if (dropDownMenu != null && !dropDownMenu.ShowCheckMargin && dropDownMenu.ShowImageMargin) { + RenderCheckBackground(e); + } + } + } + + if (imageRect != Rectangle.Empty && image != null) { + if (!e.Item.Enabled) { + base.OnRenderItemImage(e); + return; + } + + // Since office images dont scoot one px we have to override all painting but enabled = false; + if (e.Item.ImageScaling == ToolStripItemImageScaling.None) { + e.Graphics.DrawImage(image, imageRect, new Rectangle(Point.Empty, imageRect.Size), GraphicsUnit.Pixel); + } + else { + e.Graphics.DrawImage(image, imageRect); + } + } + } + + protected override void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderToolStripPanelBackground(e); + return; + } + + ToolStripPanel toolStripPanel = e.ToolStripPanel; + + if (!ShouldPaintBackground(toolStripPanel)) { + return; + } + // dont paint background effects + e.Handled = true; + + RenderBackgroundGradient(e.Graphics, toolStripPanel, ColorTable.ToolStripPanelGradientBegin, ColorTable.ToolStripPanelGradientEnd); + } + + protected override void OnRenderToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e) { + if (RendererOverride != null) { + base.OnRenderToolStripContentPanelBackground(e); + return; + } + + ToolStripContentPanel toolStripContentPanel = e.ToolStripContentPanel; + + if (!ShouldPaintBackground(toolStripContentPanel)) { + return; + } + + if( SystemInformation.InLockedTerminalSession() ) { // see ddb#191714 + return; + } + + // dont paint background effects + e.Handled = true; + + e.Graphics.Clear(ColorTable.ToolStripContentPanelGradientEnd); + + // RenderBackgroundGradient(e.Graphics, toolStripContentPanel, ColorTable.ToolStripContentPanelGradientBegin, ColorTable.ToolStripContentPanelGradientEnd); + } + + #region PrivatePaintHelpers + + // consider make public + internal override Region GetTransparentRegion(ToolStrip toolStrip) { + if (toolStrip is ToolStripDropDown || toolStrip is MenuStrip || toolStrip is StatusStrip) { + return null; + } + if (!RoundedEdges) { + return null; + } + Rectangle bounds = new Rectangle(Point.Empty, toolStrip.Size); + + // Render curve + // eat away at the corners by drawing the parent background + // + if (toolStrip.ParentInternal != null) { + // + // Paint pieces of the parent here to give toolStrip rounded effect + // + Point topLeft = Point.Empty; + Point topRight = new Point(bounds.Width - 1, 0); + Point bottomLeft = new Point(0, bounds.Height - 1); + Point bottomRight = new Point(bounds.Width - 1, bounds.Height - 1); + + // Pixels to eat away with the parent background + // Grip side + Rectangle topLeftParentHorizontalPixels = new Rectangle(topLeft, onePix); + Rectangle bottomLeftParentHorizontalPixels = new Rectangle(bottomLeft, new Size(2, 1)); + Rectangle bottomLeftParentVerticalPixels = new Rectangle(bottomLeft.X, bottomLeft.Y - 1, 1, 2); + + // OverflowSide + Rectangle bottomRightHorizontalPixels = new Rectangle(bottomRight.X - 1, bottomRight.Y, 2, 1); + Rectangle bottomRightVerticalPixels = new Rectangle(bottomRight.X, bottomRight.Y - 1, 1, 2); + + // TopSide + Rectangle topRightHorizontalPixels, topRightVerticalPixels; + + if (toolStrip.OverflowButton.Visible) { + topRightHorizontalPixels = new Rectangle(topRight.X - 1, topRight.Y, 1, 1); + topRightVerticalPixels = new Rectangle(topRight.X, topRight.Y, 1, 2); + } + else { + topRightHorizontalPixels = new Rectangle(topRight.X - 2, topRight.Y, 2, 1); + topRightVerticalPixels = new Rectangle(topRight.X, topRight.Y, 1, 3); + } + + Region parentRegionToPaint = new Region(topLeftParentHorizontalPixels); + parentRegionToPaint.Union(topLeftParentHorizontalPixels); + parentRegionToPaint.Union(bottomLeftParentHorizontalPixels); + parentRegionToPaint.Union(bottomLeftParentVerticalPixels); + parentRegionToPaint.Union(bottomRightHorizontalPixels); + parentRegionToPaint.Union(bottomRightVerticalPixels); + parentRegionToPaint.Union(topRightHorizontalPixels); + parentRegionToPaint.Union(topRightVerticalPixels); + + return parentRegionToPaint; + } + return null; + } + + + // + // We want to make sure the overflow button looks like it's the last thing on + // the toolbar. This touches up the few pixels that get clobbered by painting the + // border. + // + private void RenderOverflowButtonEffectsOverBorder(ToolStripRenderEventArgs e) { + + ToolStrip toolStrip = e.ToolStrip; + ToolStripItem item = toolStrip.OverflowButton; + if (!item.Visible) { + return; + } + + Graphics g = e.Graphics; + + Color overflowBottomLeftShadow, overflowTopShadow; + + if (item.Pressed) { + overflowBottomLeftShadow = ColorTable.ButtonPressedGradientBegin; + overflowTopShadow = overflowBottomLeftShadow; + } + else if (item.Selected) { + overflowBottomLeftShadow = ColorTable.ButtonSelectedGradientMiddle; + overflowTopShadow = overflowBottomLeftShadow; + } + else { + overflowBottomLeftShadow = ColorTable.ToolStripBorder; + overflowTopShadow = ColorTable.ToolStripGradientMiddle; + } + + // Extend the gradient color over the border. + using (Brush b = new SolidBrush(overflowBottomLeftShadow)) { + g.FillRectangle(b, toolStrip.Width - 1, toolStrip.Height - 2, 1, 1); + g.FillRectangle(b, toolStrip.Width - 2, toolStrip.Height - 1, 1, 1); + } + + + using (Brush b = new SolidBrush(overflowTopShadow)) { + g.FillRectangle(b, toolStrip.Width - 2, 0, 1, 1); + g.FillRectangle(b, toolStrip.Width - 1, 1, 1, 1); + } + + } + + /// + /// This function paints with three colors, beginning, middle, and end. + /// it paints: + /// (1)the entire bounds in the middle color + /// (2)gradient from beginning to middle of width firstGradientWidth + /// (3)gradient from middle to end of width secondGradientWidth + /// + /// if there isnt enough room to do (2) and (3) it merges into a single gradient from beginning to end. + /// + private void FillWithDoubleGradient(Color beginColor, Color middleColor, Color endColor, Graphics g, Rectangle bounds, int firstGradientWidth, int secondGradientWidth, LinearGradientMode mode, bool flipHorizontal) { + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + Rectangle endGradient = bounds; + Rectangle beginGradient = bounds; + bool useDoubleGradient = true; + + if (mode == LinearGradientMode.Horizontal) { + if (flipHorizontal) { + Color temp = endColor; + endColor = beginColor; + beginColor = temp; + } + + beginGradient.Width = firstGradientWidth; + endGradient.Width = secondGradientWidth + 1; + endGradient.X = bounds.Right - endGradient.Width; + useDoubleGradient = (bounds.Width > (firstGradientWidth + secondGradientWidth)); + } + else { + beginGradient.Height = firstGradientWidth; + endGradient.Height = secondGradientWidth + 1; + endGradient.Y = bounds.Bottom - endGradient.Height; + useDoubleGradient = (bounds.Height > (firstGradientWidth + secondGradientWidth)); + } + + if (useDoubleGradient) { + // Fill with middleColor + using (Brush b = new SolidBrush(middleColor)) { + g.FillRectangle(b, bounds); + } + + // draw first gradient + using (Brush b = new LinearGradientBrush(beginGradient, beginColor, middleColor, mode)) { + g.FillRectangle(b, beginGradient); + } + + // draw second gradient + using (LinearGradientBrush b = new LinearGradientBrush(endGradient, middleColor, endColor, mode)) { + if (mode == LinearGradientMode.Horizontal) { + endGradient.X += 1; + endGradient.Width -= 1; + } + else { + endGradient.Y += 1; + endGradient.Height -= 1; + } + + g.FillRectangle(b, endGradient); + } + } + else { + // not big enough for a swath in the middle. lets just do a single gradient. + using (Brush b = new LinearGradientBrush(bounds, beginColor, endColor, mode)) { + g.FillRectangle(b, bounds); + } + } + } + + + private void RenderStatusStripBorder(ToolStripRenderEventArgs e) { + e.Graphics.DrawLine(SystemPens.ButtonHighlight, 0,0,e.ToolStrip.Width, 0); + } + + private void RenderStatusStripBackground(ToolStripRenderEventArgs e) { + StatusStrip statusStrip = e.ToolStrip as StatusStrip; + RenderBackgroundGradient(e.Graphics, statusStrip, ColorTable.StatusStripGradientBegin, ColorTable.StatusStripGradientEnd, statusStrip.Orientation); + } + + + private void RenderCheckBackground(ToolStripItemImageRenderEventArgs e) { + // bug# VSO:528418 check box is not centered and not square. Below, position will never be negative as, on a 96% dpi, difference between item height (22) and rectangle height(16) is minimum 6. + Rectangle bounds = DpiHelper.IsScalingRequired ? new Rectangle(e.ImageRectangle.Left-2, (e.Item.Height - e.ImageRectangle.Height )/2- 1, e.ImageRectangle.Width+4, e.ImageRectangle.Height +2) : + new Rectangle(e.ImageRectangle.Left - 2, 1, e.ImageRectangle.Width + 4, e.Item.Height - 2); + Graphics g = e.Graphics; + + if (!UseSystemColors) { + Color fill = (e.Item.Selected) ? ColorTable.CheckSelectedBackground : ColorTable.CheckBackground; + fill = (e.Item.Pressed) ? ColorTable.CheckPressedBackground : fill; + using (Brush b = new SolidBrush(fill)) { + g.FillRectangle(b, bounds); + } + + using (Pen p = new Pen(ColorTable.ButtonSelectedBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + else { + if (e.Item.Pressed) { + RenderPressedButtonFill(g, bounds); + } + else { + RenderSelectedButtonFill(g, bounds); + } + g.DrawRectangle(SystemPens.Highlight, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + + private void RenderPressedGradient(Graphics g, Rectangle bounds) { + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + + // Paints a horizontal gradient similar to the image margin. + using (Brush b = new LinearGradientBrush(bounds, ColorTable.MenuItemPressedGradientBegin, ColorTable.MenuItemPressedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, bounds); + } + + // draw a box around the gradient + using (Pen p = new Pen(ColorTable.MenuBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + + private void RenderMenuStripBackground(ToolStripRenderEventArgs e) { + RenderBackgroundGradient(e.Graphics, e.ToolStrip, ColorTable.MenuStripGradientBegin, ColorTable.MenuStripGradientEnd, e.ToolStrip.Orientation); + } + + private static void RenderLabelInternal(ToolStripItemRenderEventArgs e) { + Graphics g = e.Graphics; + ToolStripItem item = e.Item; + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + + Rectangle fillRect = (item.Selected) ? item.ContentRectangle :bounds; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + + } + + private void RenderBackgroundGradient(Graphics g, Control control, Color beginColor, Color endColor) { + RenderBackgroundGradient(g,control,beginColor,endColor,Orientation.Horizontal); + } + // renders the overall gradient + private void RenderBackgroundGradient(Graphics g, Control control, Color beginColor, Color endColor, Orientation orientation) { + + if (control.RightToLeft == RightToLeft.Yes) { + Color temp = beginColor; + beginColor = endColor; + endColor = temp; + } + + if (orientation == Orientation.Horizontal) { + Control parent = control.ParentInternal; + if (parent != null) { + Rectangle gradientBounds = new Rectangle(Point.Empty, parent.Size); + if (!LayoutUtils.IsZeroWidthOrHeight(gradientBounds)) { + using (LinearGradientBrush b = new LinearGradientBrush(gradientBounds, beginColor, endColor, LinearGradientMode.Horizontal)){ + b.TranslateTransform(parent.Width - control.Location.X, parent.Height -control.Location.Y); + g.FillRectangle(b, new Rectangle(Point.Empty, control.Size)); + } + } + } + else { + Rectangle gradientBounds = new Rectangle(Point.Empty, control.Size); + if (!LayoutUtils.IsZeroWidthOrHeight(gradientBounds)) { + // dont have a parent that we know about so go ahead and paint the gradient as if there wasnt another container. + using (LinearGradientBrush b = new LinearGradientBrush(gradientBounds, beginColor, endColor, LinearGradientMode.Horizontal)){ + g.FillRectangle(b, gradientBounds); + } + } + } + } + else { + using (Brush b = new SolidBrush(beginColor)) { + g.FillRectangle(b, new Rectangle(Point.Empty, control.Size)); + } + } + } + + + private void RenderToolStripBackgroundInternal(ToolStripRenderEventArgs e) { + ScaleObjectSizesIfNeeded(e.ToolStrip.DeviceDpi); + + ToolStrip toolStrip = e.ToolStrip; + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + + // fill up the background + LinearGradientMode mode = (toolStrip.Orientation == Orientation.Horizontal) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal; + FillWithDoubleGradient(ColorTable.ToolStripGradientBegin, ColorTable.ToolStripGradientMiddle, ColorTable.ToolStripGradientEnd, e.Graphics, bounds, iconWellGradientWidth, iconWellGradientWidth, mode, /*flipHorizontal=*/false); + + } + + private void RenderToolStripDropDownBackground(ToolStripRenderEventArgs e) { + ToolStrip toolStrip = e.ToolStrip; + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + + using (Brush b = new SolidBrush(ColorTable.ToolStripDropDownBackground)) { + e.Graphics.FillRectangle(b, bounds); + } + } + + private void RenderToolStripDropDownBorder(ToolStripRenderEventArgs e) { + ToolStripDropDown toolStripDropDown = e.ToolStrip as ToolStripDropDown; + Graphics g = e.Graphics; + + if (toolStripDropDown != null) { + Rectangle bounds = new Rectangle(Point.Empty, toolStripDropDown.Size); + + using (Pen p = new Pen(ColorTable.MenuBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + + if (!(toolStripDropDown is ToolStripOverflow)) { + // make the neck connected. + using (Brush b = new SolidBrush(ColorTable.ToolStripDropDownBackground)) { + g.FillRectangle(b, e.ConnectedArea); + } + } + } + } + + private void RenderOverflowBackground(ToolStripItemRenderEventArgs e, bool rightToLeft) { + ScaleObjectSizesIfNeeded(e.Item.DeviceDpi); + + Graphics g = e.Graphics; + ToolStripOverflowButton item = e.Item as ToolStripOverflowButton; + Rectangle overflowBoundsFill = new Rectangle(Point.Empty, e.Item.Size); + Rectangle bounds = overflowBoundsFill; + + bool drawCurve = (RoundedEdges && (!(item.GetCurrentParent() is MenuStrip))); + bool horizontal = (e.ToolStrip.Orientation == Orientation.Horizontal); + // undone RTL + + if (horizontal) { + overflowBoundsFill.X += overflowBoundsFill.Width - overflowButtonWidth + 1; + overflowBoundsFill.Width = overflowButtonWidth; + if (rightToLeft) { + overflowBoundsFill = LayoutUtils.RTLTranslate(overflowBoundsFill, bounds); + } + } + else { + overflowBoundsFill.Y = overflowBoundsFill.Height - overflowButtonWidth + 1; + overflowBoundsFill.Height = overflowButtonWidth; + } + + Color overflowButtonGradientBegin, overflowButtonGradientMiddle, overflowButtonGradientEnd, overflowBottomLeftShadow, overflowTopShadow; + + if (item.Pressed) { + overflowButtonGradientBegin = ColorTable.ButtonPressedGradientBegin; + overflowButtonGradientMiddle = ColorTable.ButtonPressedGradientMiddle; + overflowButtonGradientEnd = ColorTable.ButtonPressedGradientEnd; + overflowBottomLeftShadow = ColorTable.ButtonPressedGradientBegin; + overflowTopShadow = overflowBottomLeftShadow; + } + else if (item.Selected) { + overflowButtonGradientBegin = ColorTable.ButtonSelectedGradientBegin; + overflowButtonGradientMiddle = ColorTable.ButtonSelectedGradientMiddle; + overflowButtonGradientEnd = ColorTable.ButtonSelectedGradientEnd; + overflowBottomLeftShadow = ColorTable.ButtonSelectedGradientMiddle; + overflowTopShadow = overflowBottomLeftShadow; + } + else { + overflowButtonGradientBegin = ColorTable.OverflowButtonGradientBegin; + overflowButtonGradientMiddle = ColorTable.OverflowButtonGradientMiddle; + overflowButtonGradientEnd = ColorTable.OverflowButtonGradientEnd; + overflowBottomLeftShadow = ColorTable.ToolStripBorder; + overflowTopShadow = (horizontal) ? ColorTable.ToolStripGradientMiddle : ColorTable.ToolStripGradientEnd; + } + + if (drawCurve) { + // draw shadow pixel on bottom left +1, +1 + using (Pen p = new Pen(overflowBottomLeftShadow/*Color.HotPink*/)) { + + Point start = new Point(overflowBoundsFill.Left - 1, overflowBoundsFill.Height - 2); + Point end = new Point(overflowBoundsFill.Left, overflowBoundsFill.Height - 2); + if (rightToLeft) { + start.X = overflowBoundsFill.Right +1; + end.X = overflowBoundsFill.Right; + } + g.DrawLine(p, start, end); + } + } + LinearGradientMode mode = (horizontal) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal; + + // fill main body + FillWithDoubleGradient(overflowButtonGradientBegin, overflowButtonGradientMiddle, overflowButtonGradientEnd, g, overflowBoundsFill, iconWellGradientWidth, iconWellGradientWidth, mode, false); + + // render shadow pixels (ToolStrip only) + if (drawCurve) { + // top left and top right shadow pixels + using (Brush b = new SolidBrush(overflowTopShadow/*Color.Orange*/)) { + if (horizontal) { + Point top1 = new Point(overflowBoundsFill.X - 2, 0); + Point top2 = new Point(overflowBoundsFill.X - 1, 1); + + if (rightToLeft) { + top1.X = overflowBoundsFill.Right + 1; + top2.X = overflowBoundsFill.Right; + } + g.FillRectangle(b, top1.X, top1.Y, 1, 1); + g.FillRectangle(b, top2.X, top2.Y, 1, 1); + } + else { + g.FillRectangle(b, overflowBoundsFill.Width - 3, overflowBoundsFill.Top - 1, 1, 1); + g.FillRectangle(b, overflowBoundsFill.Width - 2, overflowBoundsFill.Top - 2, 1, 1); + } + } + + using (Brush b = new SolidBrush(overflowButtonGradientBegin/*Color.Green*/)) { + if (horizontal) { + Rectangle fillRect = new Rectangle(overflowBoundsFill.X - 1, 0, 1, 1); + if (rightToLeft) { + fillRect.X = overflowBoundsFill.Right; + } + g.FillRectangle(b, fillRect); + } + else { + g.FillRectangle(b, overflowBoundsFill.X, overflowBoundsFill.Top - 1, 1, 1); + } + } + } + + } + + private void RenderToolStripCurve(ToolStripRenderEventArgs e) { + Rectangle bounds = new Rectangle(Point.Empty, e.ToolStrip.Size); + ToolStrip toolStrip = e.ToolStrip; + Rectangle displayRect = toolStrip.DisplayRectangle; + + Graphics g = e.Graphics; + + Point topLeft = Point.Empty; + Point topRight = new Point(bounds.Width - 1, 0); + Point bottomLeft = new Point(0, bounds.Height - 1); + + // + // Add in shadow pixels - the detail that makes them look round + // + // Draw in rounded shadow pixels on the top left & right + // consider: if this is slow use precanned corners. + using (Brush b = new SolidBrush(ColorTable.ToolStripGradientMiddle)) { + // there are two shadow rects (one pixel wide) on the top + Rectangle topLeftShadowRect = new Rectangle(topLeft, onePix); + topLeftShadowRect.X += 1; + + // second shadow rect + Rectangle topLeftShadowRect2 = new Rectangle(topLeft, onePix); + topLeftShadowRect2.Y += 1; + + // on the right there are two more shadow rects + Rectangle topRightShadowRect = new Rectangle(topRight, onePix); + topRightShadowRect.X -= 2; // was 2? + + // second top right shadow pix + Rectangle topRightShadowRect2 = topRightShadowRect; + topRightShadowRect2.Y += 1; + topRightShadowRect2.X += 1; + + Rectangle[] paintRects = new Rectangle[] { topLeftShadowRect, topLeftShadowRect2, topRightShadowRect, topRightShadowRect2 }; + + // prevent the painting of anything that would obscure an item. + for (int i = 0; i < paintRects.Length; i++) { + if (displayRect.IntersectsWith(paintRects[i])) { + paintRects[i] = Rectangle.Empty; + } + } + g.FillRectangles(b, paintRects); + + } + + // Draw in rounded shadow pixels on the bottom left + using (Brush b = new SolidBrush(ColorTable.ToolStripGradientEnd)) { + + // this gradient is the one just before the dark shadow line starts on pixel #3. + Point gradientCopyPixel = bottomLeft; + gradientCopyPixel.Offset(1, -1); + if (!displayRect.Contains(gradientCopyPixel)) { + g.FillRectangle(b, new Rectangle(gradientCopyPixel, onePix)); + } + + // set the one dark pixel in the bottom left hand corner + Rectangle otherBottom = new Rectangle(bottomLeft.X, bottomLeft.Y - 2, 1, 1); + if (!displayRect.IntersectsWith(otherBottom)) { + g.FillRectangle(b, otherBottom); + } + } + } + + private void RenderSelectedButtonFill(Graphics g, Rectangle bounds) { + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + + if (!UseSystemColors) { + using (Brush b = new LinearGradientBrush(bounds, ColorTable.ButtonSelectedGradientBegin, ColorTable.ButtonSelectedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, bounds); + } + } + else { + Color fillColor = ColorTable.ButtonSelectedHighlight; + using (Brush b = new SolidBrush(fillColor)) { + g.FillRectangle(b, bounds); + } + } + } + private void RenderCheckedButtonFill(Graphics g, Rectangle bounds) { + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + + if (!UseSystemColors) { + using (Brush b = new LinearGradientBrush(bounds, ColorTable.ButtonCheckedGradientBegin, ColorTable.ButtonCheckedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, bounds); + } + } + else { + + Color fillColor = ColorTable.ButtonCheckedHighlight; + + using (Brush b = new SolidBrush(fillColor)) { + g.FillRectangle(b, bounds); + + } + } + } + + private void RenderSeparatorInternal(Graphics g, ToolStripItem item, Rectangle bounds, bool vertical) { + Color foreColor = ColorTable.SeparatorDark; + Color highlightColor = ColorTable.SeparatorLight; + Pen foreColorPen = new Pen(foreColor); + Pen highlightColorPen = new Pen(highlightColor); + + // undone emplore caching. + bool disposeForeColorPen = true; + bool disposeHighlightColorColorPen = true; + bool isASeparator = item is ToolStripSeparator; + bool isAHorizontalSeparatorNotOnDropDownMenu = false; + + if (isASeparator) { + if (vertical) { + if (!item.IsOnDropDown) { + // center so that it matches office + bounds.Y +=3; + bounds.Height = Math.Max(0, bounds.Height -6); + } + } + else { + // offset after the image margin + ToolStripDropDownMenu dropDownMenu = item.GetCurrentParent() as ToolStripDropDownMenu; + if (dropDownMenu != null) { + if (dropDownMenu.RightToLeft == RightToLeft.No) { + // scoot over by the padding (that will line you up with the text - but go two PX before so that it visually looks + // like the line meets up with the text). + bounds.X += dropDownMenu.Padding.Left -2; + bounds.Width = dropDownMenu.Width - bounds.X; + } + else { + // scoot over by the padding (that will line you up with the text - but go two PX before so that it visually looks + // like the line meets up with the text). + bounds.X += 2; + bounds.Width = dropDownMenu.Width - bounds.X - dropDownMenu.Padding.Right; + + } + } + else { + isAHorizontalSeparatorNotOnDropDownMenu = true; + + } + } + } + try { + if (vertical) { + if (bounds.Height >= 4) { + bounds.Inflate(0, -2); // scoot down 2PX and start drawing + } + + bool rightToLeft = (item.RightToLeft == RightToLeft.Yes); + Pen leftPen = (rightToLeft) ? highlightColorPen : foreColorPen; + Pen rightPen = (rightToLeft) ? foreColorPen : highlightColorPen; + + // Draw dark line + int startX = bounds.Width / 2; + + g.DrawLine(leftPen, startX, bounds.Top, startX, bounds.Bottom - 1); + + // Draw highlight one pixel to the right + startX++; + g.DrawLine(rightPen, startX, bounds.Top + 1, startX, bounds.Bottom); + } + else { + // + // horizontal separator + // Draw dark line + + if (isAHorizontalSeparatorNotOnDropDownMenu && bounds.Width >= 4) { + bounds.Inflate(-2, 0); // scoot down 2PX and start drawing + } + int startY = bounds.Height / 2; + + g.DrawLine(foreColorPen, bounds.Left, startY, bounds.Right - 1, startY); + + if (!isASeparator || isAHorizontalSeparatorNotOnDropDownMenu) { + // Draw highlight one pixel to the right + startY++; + g.DrawLine(highlightColorPen, bounds.Left + 1, startY, bounds.Right - 1, startY); + } + } + } + finally { + if (disposeForeColorPen && foreColorPen != null) { + foreColorPen.Dispose(); + } + + if (disposeHighlightColorColorPen && highlightColorPen != null) { + highlightColorPen.Dispose(); + } + } + } + + private void RenderPressedButtonFill(Graphics g, Rectangle bounds) { + + if ((bounds.Width == 0) || (bounds.Height == 0)) { + return; // can't new up a linear gradient brush with no dimension. + } + if (!UseSystemColors) { + using (Brush b = new LinearGradientBrush(bounds, ColorTable.ButtonPressedGradientBegin, ColorTable.ButtonPressedGradientEnd, LinearGradientMode.Vertical)) { + g.FillRectangle(b, bounds); + } + } + else { + + Color fillColor = ColorTable.ButtonPressedHighlight; + using (Brush b = new SolidBrush(fillColor)) { + g.FillRectangle(b, bounds); + } + } + } + + private void RenderItemInternal(ToolStripItemRenderEventArgs e, bool useHotBorder) { + Graphics g = e.Graphics; + ToolStripItem item = e.Item; + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + bool drawHotBorder = false; + + Rectangle fillRect = (item.Selected) ? item.ContentRectangle :bounds; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, bounds, fillRect); + } + + if (item.Pressed) { + RenderPressedButtonFill(g, bounds); + drawHotBorder = useHotBorder; + } + else if (item.Selected) { + RenderSelectedButtonFill(g, bounds); + drawHotBorder = useHotBorder; + } + else if (item.Owner != null && item.BackColor != item.Owner.BackColor) { + using (Brush b = new SolidBrush(item.BackColor)) { + g.FillRectangle(b, bounds); + } + } + + if (drawHotBorder) { + using (Pen p = new Pen(ColorTable.ButtonSelectedBorder)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + } + + private void ScaleObjectSizesIfNeeded(int currentDeviceDpi) { + if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + if (previousDeviceDpi!=currentDeviceDpi) { + ToolStripRenderer.ScaleArrowOffsetsIfNeeded(currentDeviceDpi); + overflowButtonWidth = DpiHelper.LogicalToDeviceUnits(OVERFLOW_BUTTON_WIDTH, currentDeviceDpi); + overflowArrowWidth = DpiHelper.LogicalToDeviceUnits(OVERFLOW_ARROW_WIDTH, currentDeviceDpi); + overflowArrowHeight = DpiHelper.LogicalToDeviceUnits(OVERFLOW_ARROW_HEIGHT, currentDeviceDpi); + overflowArrowOffsetY = DpiHelper.LogicalToDeviceUnits(OVERFLOW_ARROW_OFFSETY, currentDeviceDpi); + + gripPadding = DpiHelper.LogicalToDeviceUnits(GRIP_PADDING, currentDeviceDpi); + iconWellGradientWidth = DpiHelper.LogicalToDeviceUnits(ICON_WELL_GRADIENT_WIDTH, currentDeviceDpi); + int scaledSize = DpiHelper.LogicalToDeviceUnits(DROP_DOWN_MENU_ITEM_PAINT_PADDING_SIZE, currentDeviceDpi); + scaledDropDownMenuItemPaintPadding = new Padding(scaledSize + 1, 0, scaledSize, 0); + previousDeviceDpi = currentDeviceDpi; + isScalingInitialized = true; + return; + } + } + if (isScalingInitialized) { + return; + } + if (DpiHelper.IsScalingRequired) { + ToolStripRenderer.ScaleArrowOffsetsIfNeeded(); + overflowButtonWidth = DpiHelper.LogicalToDeviceUnitsX(OVERFLOW_BUTTON_WIDTH); + overflowArrowWidth = DpiHelper.LogicalToDeviceUnitsX(OVERFLOW_ARROW_WIDTH); + overflowArrowHeight = DpiHelper.LogicalToDeviceUnitsY(OVERFLOW_ARROW_HEIGHT); + overflowArrowOffsetY = DpiHelper.LogicalToDeviceUnitsY(OVERFLOW_ARROW_OFFSETY); + + if (DpiHelper.EnableToolStripHighDpiImprovements) { + gripPadding = DpiHelper.LogicalToDeviceUnitsY(GRIP_PADDING); + iconWellGradientWidth = DpiHelper.LogicalToDeviceUnitsX(ICON_WELL_GRADIENT_WIDTH); + int scaledSize = DpiHelper.LogicalToDeviceUnitsX(DROP_DOWN_MENU_ITEM_PAINT_PADDING_SIZE); + scaledDropDownMenuItemPaintPadding = new Padding(scaledSize + 1, 0, scaledSize, 0); + } + } + isScalingInitialized = true; + } + + // This draws differently sized arrows than the base one... + // used only for drawing the overflow button madness. + private Point RenderArrowInternal(Graphics g, Rectangle dropDownRect, ArrowDirection direction, Brush brush) { + + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + + // if the width is odd - favor pushing it over one pixel right. + middle.X += (dropDownRect.Width % 2); + + Point[] arrow = null; + + switch (direction) { + case ArrowDirection.Up: + arrow = new Point[] { + new Point(middle.X - ToolStripRenderer.Offset2X, middle.Y + 1), + new Point(middle.X + ToolStripRenderer.Offset2X + 1, middle.Y + 1), + new Point(middle.X, middle.Y - ToolStripRenderer.Offset2Y) + }; + break; + + case ArrowDirection.Left: + arrow = new Point[] { + new Point(middle.X + ToolStripRenderer.Offset2X, middle.Y - ToolStripRenderer.Offset2Y - 1), + new Point(middle.X + ToolStripRenderer.Offset2X, middle.Y + ToolStripRenderer.Offset2Y + 1), + new Point(middle.X - 1, middle.Y) + }; + break; + + case ArrowDirection.Right: + arrow = new Point[] { + new Point(middle.X - ToolStripRenderer.Offset2X, middle.Y - ToolStripRenderer.Offset2Y - 1), + new Point(middle.X - ToolStripRenderer.Offset2X, middle.Y + ToolStripRenderer.Offset2Y + 1), + new Point(middle.X + 1, middle.Y) + }; + break; + + case ArrowDirection.Down: + default: + arrow = new Point[] { + new Point(middle.X - ToolStripRenderer.Offset2X, middle.Y - 1), + new Point(middle.X + ToolStripRenderer.Offset2X + 1, middle.Y - 1), + new Point(middle.X, middle.Y + ToolStripRenderer.Offset2Y) + }; + break; + } + g.FillPolygon(brush, arrow); + + return middle; + } + + #endregion PrivatePaintHelpers + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripProgressBar.cs b/WindowsForms/Managed/System/WinForms/ToolStripProgressBar.cs new file mode 100644 index 000000000..5a3237fe3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripProgressBar.cs @@ -0,0 +1,579 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using System.Security; + using System.Security.Permissions; + + /// + [DefaultProperty("Value")] + public class ToolStripProgressBar : ToolStripControlHost { + + internal static readonly object EventRightToLeftLayoutChanged = new object(); + + private static readonly Padding defaultMargin = new Padding(1, 2, 1, 1); + private static readonly Padding defaultStatusStripMargin = new Padding(1, 3, 1, 3); + private Padding scaledDefaultMargin = defaultMargin; + private Padding scaledDefaultStatusStripMargin = defaultStatusStripMargin; + + + /// + public ToolStripProgressBar() + : base(CreateControlInstance()) { + ToolStripProgressBarControl toolStripProgressBarControl = Control as ToolStripProgressBarControl; + if (toolStripProgressBarControl != null) { + toolStripProgressBarControl.Owner = this; + } + + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin); + scaledDefaultStatusStripMargin = DpiHelper.LogicalToDeviceUnits(defaultStatusStripMargin); + } + } + + public ToolStripProgressBar(string name) : this() { + this.Name = name; + } + + /// + /// + /// Create a strongly typed accessor for the class + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ProgressBar ProgressBar { + get { + return this.Control as ProgressBar; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + /// Specify what size you want the item to start out at + /// + /// + protected override System.Drawing.Size DefaultSize { + get { + return new Size(100,15); + } + } + + /// + /// + /// Specify how far from the edges you want to be + /// + /// + protected internal override Padding DefaultMargin { + get { + if (this.Owner != null && this.Owner is StatusStrip) { + return scaledDefaultStatusStripMargin; + } + else { + return scaledDefaultMargin; + } + } + } + + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarMarqueeAnimationSpeed) + ] + public int MarqueeAnimationSpeed { + get { return ProgressBar.MarqueeAnimationSpeed; } + set { ProgressBar.MarqueeAnimationSpeed = value; } + } + + + [ + DefaultValue(100), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMaximumDescr) + ] + public int Maximum { + get { + return ProgressBar.Maximum; + } + set { + ProgressBar.Maximum = value; + } + } + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.ProgressBarMinimumDescr) + ] + public int Minimum { + get { + return ProgressBar.Minimum; + } + set { + ProgressBar.Minimum = value; + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return ProgressBar.RightToLeftLayout; + } + + set { + ProgressBar.RightToLeftLayout = value; + } + } + + /// + /// + /// Wrap some commonly used properties + /// + /// + [ + DefaultValue(10), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStepDescr) + ] + public int Step { + get { + return ProgressBar.Step; + } + set { + ProgressBar.Step = value; + } + } + + /// + /// + /// Wrap some commonly used properties + /// + /// + [ + DefaultValue(ProgressBarStyle.Blocks), + SRCategory(SR.CatBehavior), + SRDescription(SR.ProgressBarStyleDescr) + ] + public ProgressBarStyle Style { + get { + return ProgressBar.Style; + } + set { + ProgressBar.Style = value; + } + } + + /// + /// + /// Hide the property. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text + { + get + { + return Control.Text; + } + set + { + Control.Text = value; + } + } + + + /// + /// + /// Wrap some commonly used properties + /// + /// + [ + DefaultValue(0), + SRCategory(SR.CatBehavior), + Bindable(true), + SRDescription(SR.ProgressBarValueDescr) + ] + public int Value { + get { + return ProgressBar.Value; + } + set { + ProgressBar.Value = value; + } + } + + /// + /// Constructs the new instance of the accessibility object for this ToolStripProgressBar ToolStrip item. + /// + /// + /// The new instance of the accessibility object for this ToolStripProgressBar ToolStrip item + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripProgressBarAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + private static Control CreateControlInstance() { + ProgressBar progressBar = AccessibilityImprovements.Level3 ? new ToolStripProgressBarControl() : new ProgressBar(); + progressBar.Size = new Size(100,15); + return progressBar; + } + + private void HandleRightToLeftLayoutChanged(object sender, EventArgs e) { + OnRightToLeftLayoutChanged(e); + } + + /// + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + RaiseEvent(EventRightToLeftLayoutChanged, e); + } + + /// + protected override void OnSubscribeControlEvents(Control control) { + ProgressBar bar = control as ProgressBar; + if (bar != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + bar.RightToLeftLayoutChanged += new EventHandler(HandleRightToLeftLayoutChanged); + } + + base.OnSubscribeControlEvents(control); + } + + /// + protected override void OnUnsubscribeControlEvents(Control control) { + + ProgressBar bar = control as ProgressBar; + if (bar != null) { + // Please keep this alphabetized and in sync with Subscribe + // + bar.RightToLeftLayoutChanged -= new EventHandler(HandleRightToLeftLayoutChanged); + } + base.OnUnsubscribeControlEvents(control); + + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event KeyEventHandler KeyDown + { + add + { + base.KeyDown += value; + } + remove + { + base.KeyDown -= value; + } + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event KeyPressEventHandler KeyPress + { + add + { + base.KeyPress += value; + } + remove + { + base.KeyPress -= value; + } + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event KeyEventHandler KeyUp + { + add + { + base.KeyUp += value; + } + remove + { + base.KeyUp -= value; + } + } + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler LocationChanged + { + add + { + base.LocationChanged += value; + } + remove + { + base.LocationChanged -= value; + } + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler OwnerChanged + { + add + { + base.OwnerChanged += value; + } + remove + { + base.OwnerChanged -= value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EventRightToLeftLayoutChanged, value); + } + remove { + Events.RemoveHandler(EventRightToLeftLayoutChanged, value); + } + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler TextChanged + { + add + { + base.TextChanged += value; + } + remove + { + base.TextChanged -= value; + } + } + + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event EventHandler Validated + { + add + { + base.Validated += value; + } + remove + { + base.Validated -= value; + } + } + + /// + /// + /// Hide the event. + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + new public event CancelEventHandler Validating + { + add + { + base.Validating += value; + } + remove + { + base.Validating -= value; + } + } + public void Increment(int value) { + ProgressBar.Increment(value); + } + + public void PerformStep() { + ProgressBar.PerformStep(); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripProgressBarAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripProgressBar ownerItem = null; + + public ToolStripProgressBarAccessibleObject(ToolStripProgressBar ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + + return AccessibleRole.ProgressBar; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild || + direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + return this.ownerItem.ProgressBar.AccessibilityObject; + } + + // Handle Parent and other directions in base ToolStripItem.FragmentNavigate() method. + return base.FragmentNavigate(direction); + } + } + + internal class ToolStripProgressBarControl : ProgressBar { + + private ToolStripProgressBar ownerItem; + + public ToolStripProgressBar Owner { + get { return ownerItem; } + set { ownerItem = value; } + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripProgressBarControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + } + + internal class ToolStripProgressBarControlAccessibleObject : ProgressBar.ProgressBarAccessibleObject { + public ToolStripProgressBarControlAccessibleObject(ToolStripProgressBarControl toolStripProgressBarControl) : base(toolStripProgressBarControl) { + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + var toolStripProgressBarControl = this.Owner as ToolStripProgressBarControl; + if (toolStripProgressBarControl != null) { + return toolStripProgressBarControl.Owner.Owner.AccessibilityObject; + } + + return base.FragmentRoot; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var toolStripProgressBarControl = Owner as ToolStripProgressBarControl; + if (toolStripProgressBarControl != null) { + return toolStripProgressBarControl.Owner.AccessibilityObject.FragmentNavigate(direction); + } + break; + } + + return base.FragmentNavigate(direction); + } + + internal override object GetPropertyValue(int propertyID) { + return base.GetPropertyValue(propertyID); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripRenderEventArgs.cs new file mode 100644 index 000000000..58d17f882 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRenderEventArgs.cs @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + + + /// + /// + /// ToolStripRenderEventArgs + /// + public class ToolStripRenderEventArgs : EventArgs { + + private ToolStrip toolStrip = null; + private Graphics graphics = null; + private Rectangle affectedBounds = Rectangle.Empty; + private Color backColor = Color.Empty; + + /// + /// + /// This class represents all the information to render the toolStrip + /// + public ToolStripRenderEventArgs(Graphics g, ToolStrip toolStrip) { + this.toolStrip = toolStrip; + this.graphics = g; + this.affectedBounds = new Rectangle(Point.Empty, toolStrip.Size); + + + } + + /// + /// + /// This class represents all the information to render the toolStrip + /// + public ToolStripRenderEventArgs(Graphics g, ToolStrip toolStrip, Rectangle affectedBounds, Color backColor) { + this.toolStrip = toolStrip; + this.affectedBounds = affectedBounds; + this.graphics = g; + this.backColor = backColor; + } + + /// + /// + /// the bounds to draw in + /// + public Rectangle AffectedBounds { + get { + return affectedBounds; + } + } + + /// + /// + /// the back color to draw with. + /// + public Color BackColor { + get { + if (backColor == Color.Empty) { + // get the user specified color + backColor = toolStrip.RawBackColor; + if (backColor == Color.Empty) { + if (toolStrip is ToolStripDropDown) { + backColor = SystemColors.Menu; + } + else if (toolStrip is MenuStrip) { + backColor = SystemColors.MenuBar; + } + else { + backColor = SystemColors.Control; + } + } + } + return backColor; + } + } + /// + /// + /// the graphics object to draw with + /// + public Graphics Graphics { + get { + return graphics; + } + } + + /// + /// + /// Represents which toolStrip was affected by the click + /// + public ToolStrip ToolStrip { + get { + return toolStrip; + } + } + + /// + public Rectangle ConnectedArea { + get { + ToolStripDropDown dropDown = toolStrip as ToolStripDropDown; + if (dropDown != null) { + ToolStripDropDownItem ownerItem = dropDown.OwnerItem as ToolStripDropDownItem; + + if (ownerItem is MdiControlStrip.SystemMenuItem) { + // there's no connected rect between a system menu item and a dropdown. + return Rectangle.Empty; + } + if (ownerItem !=null && ownerItem.ParentInternal != null && !ownerItem.IsOnDropDown) { + // translate the item into our coordinate system. + Rectangle itemBounds = new Rectangle(toolStrip.PointToClient(ownerItem.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords)), ownerItem.Size); + Rectangle bounds = ToolStrip.Bounds; + + Rectangle overlap = ToolStrip.ClientRectangle; + overlap.Inflate(1,1); + if (overlap.IntersectsWith(itemBounds)) { + switch (ownerItem.DropDownDirection) { + case ToolStripDropDownDirection.AboveLeft: + case ToolStripDropDownDirection.AboveRight: + // Consider... the shadow effect interferes with the connected area. + return Rectangle.Empty; + // return new Rectangle(itemBounds.X+1, bounds.Height -2, itemBounds.Width -2, 2); + case ToolStripDropDownDirection.BelowRight: + case ToolStripDropDownDirection.BelowLeft: + overlap.Intersect(itemBounds); + if (overlap.Height == 2) { + return new Rectangle(itemBounds.X+1, 0, itemBounds.Width -2, 2); + } + // if its overlapping more than one pixel, this means we've pushed it to obscure + // the menu item. in this case pretend it's not connected. + return Rectangle.Empty; + case ToolStripDropDownDirection.Right: + case ToolStripDropDownDirection.Left: + return Rectangle.Empty; + // Consider... the shadow effect interferes with the connected area. + // return new Rectangle(bounds.Width-2, 1, 2, itemBounds.Height-2); + } + } + } + } + return Rectangle.Empty; + } + } + + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripRenderEventHandler.cs new file mode 100644 index 000000000..7dd47ab67 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRenderEventHandler.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + + + /// + /// + /// + /// Called when the background of the winbar is being rendered + /// + /// + public delegate void ToolStripRenderEventHandler(object sender, ToolStripRenderEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRenderMode.cs b/WindowsForms/Managed/System/WinForms/ToolStripRenderMode.cs new file mode 100644 index 000000000..509ee0e7f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRenderMode.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + + /// + public enum ToolStripRenderMode { + /// + [Browsable(false)] + Custom, + /// + System, + /// + Professional, + /// + ManagerRenderMode + } + + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs b/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs new file mode 100644 index 000000000..6b403aab9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs @@ -0,0 +1,1093 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Drawing.Text; + using System.Windows.Forms.Internal; + using System.Drawing.Imaging; + using System.ComponentModel; + using System.Windows.Forms.Layout; + using System.Security; + using System.Security.Permissions; + + /// + public abstract class ToolStripRenderer { + + private static readonly object EventRenderSplitButtonBackground = new object(); + private static readonly object EventRenderItemBackground = new object(); + private static readonly object EventRenderItemImage = new object(); + private static readonly object EventRenderItemText = new object(); + private static readonly object EventRenderToolStripBackground = new object(); + private static readonly object EventRenderGrip = new object(); + private static readonly object EventRenderButtonBackground = new object(); + private static readonly object EventRenderLabelBackground = new object(); + private static readonly object EventRenderMenuItemBackground = new object(); + private static readonly object EventRenderDropDownButtonBackground = new object(); + private static readonly object EventRenderOverflowButtonBackground = new object(); + private static readonly object EventRenderImageMargin = new object(); + private static readonly object EventRenderBorder = new object(); + private static readonly object EventRenderArrow = new object(); + private static readonly object EventRenderStatusStripPanelBackground = new object(); + private static readonly object EventRenderToolStripStatusLabelBackground = new object(); + private static readonly object EventRenderSeparator = new object(); + private static readonly object EventRenderItemCheck = new object(); + private static readonly object EventRenderToolStripPanelBackground = new object(); + private static readonly object EventRenderToolStripContentPanelBackground = new object(); + + private static readonly object EventRenderStatusStripSizingGrip = new object(); + + private static ColorMatrix disabledImageColorMatrix; + + private EventHandlerList events; + private bool isAutoGenerated = false; + + private static bool isScalingInitialized = false; + internal int previousDeviceDpi = DpiHelper.DeviceDpi; + + // arrows are rendered as isosceles triangles, whose heights are half the base in order to have 45 degree angles + // Offset2X is half of the base + // Offset2Y is height of the isosceles triangle + private static int OFFSET_2PIXELS = 2; + private static int OFFSET_4PIXELS = 4; + protected static int Offset2X = OFFSET_2PIXELS; + protected static int Offset2Y = OFFSET_2PIXELS; + private static int offset4X = OFFSET_4PIXELS; + private static int offset4Y = OFFSET_4PIXELS; + + // this is used in building up the half pyramid of rectangles that are drawn in a + // status strip sizing grip. + private static Rectangle[] baseSizeGripRectangles = new Rectangle[] { new Rectangle(8,0,2,2), + new Rectangle(8,4,2,2), + new Rectangle(8,8,2,2), + new Rectangle(4,4,2,2), + new Rectangle(4,8,2,2), + new Rectangle(0,8,2,2) }; + + protected ToolStripRenderer() { + } + + internal ToolStripRenderer(bool isAutoGenerated) { + this.isAutoGenerated = isAutoGenerated; + } + // this is used in building disabled images. + private static ColorMatrix DisabledImageColorMatrix { + get { + if (disabledImageColorMatrix == null) { + // VSWhidbey 233470 + // this is the result of a GreyScale matrix multiplied by a transparency matrix of .5 + + + + float[][] greyscale = new float[5][]; + greyscale[0] = new float[5] {0.2125f, 0.2125f, 0.2125f, 0, 0}; + greyscale[1] = new float[5] {0.2577f, 0.2577f, 0.2577f, 0, 0}; + greyscale[2] = new float[5] {0.0361f, 0.0361f, 0.0361f, 0, 0}; + greyscale[3] = new float[5] {0, 0, 0, 1, 0}; + greyscale[4] = new float[5] {0.38f, 0.38f, 0.38f, 0, 1}; + + float[][] transparency = new float[5][]; + transparency[0] = new float[5] {1, 0, 0, 0, 0}; + transparency[1] = new float[5] {0, 1, 0, 0, 0}; + transparency[2] = new float[5] {0, 0, 1, 0, 0}; + transparency[3] = new float[5] {0, 0, 0, .7F, 0}; + transparency[4] = new float[5] {0, 0, 0, 0, 0}; + + + disabledImageColorMatrix = ControlPaint.MultiplyColorMatrix(transparency, greyscale); + + } + + return disabledImageColorMatrix; + } + } + + + /// + /// Gets the list of event handlers that are attached to this component. + /// + private EventHandlerList Events { + get { + if (events == null) { + events = new EventHandlerList(); + } + return events; + } + } + + internal bool IsAutoGenerated { + get { return isAutoGenerated; } + } + + // if we're in a low contrast, high resolution situation, use this renderer under the covers instead. + internal virtual ToolStripRenderer RendererOverride { + get { + return null; + } + } + + /// ----------------------------------------------------------------------------- + /// SECREVIEW VSWhidbey 250785 - all stock renderer events require AllWindowsPermission + /// use the private AddHandler/RemoveHandler to ensure that security checks are + /// made. + /// ----------------------------------------------------------------------------- + + + /// + public event ToolStripArrowRenderEventHandler RenderArrow { + add { + AddHandler(EventRenderArrow, value); + } + remove { + RemoveHandler(EventRenderArrow, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderToolStripBackground { + add { + AddHandler(EventRenderToolStripBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripBackground, value); + } + } + + + public event ToolStripPanelRenderEventHandler RenderToolStripPanelBackground { + add { + AddHandler(EventRenderToolStripPanelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripPanelBackground, value); + } + } + + public event ToolStripContentPanelRenderEventHandler RenderToolStripContentPanelBackground { + add { + AddHandler(EventRenderToolStripContentPanelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripContentPanelBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderToolStripBorder { + add { + AddHandler(EventRenderBorder, value); + } + remove { + RemoveHandler(EventRenderBorder, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderButtonBackground { + add { + AddHandler(EventRenderButtonBackground, value); + } + remove { + RemoveHandler(EventRenderButtonBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderDropDownButtonBackground { + add { + AddHandler(EventRenderDropDownButtonBackground, value); + } + remove { + RemoveHandler(EventRenderDropDownButtonBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderOverflowButtonBackground { + add { + AddHandler(EventRenderOverflowButtonBackground, value); + } + remove { + RemoveHandler(EventRenderOverflowButtonBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripGripRenderEventHandler RenderGrip { + add { + AddHandler(EventRenderGrip, value); + } + remove { + RemoveHandler(EventRenderGrip, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderItemBackground { + add { + AddHandler(EventRenderItemBackground, value); + } + remove { + RemoveHandler(EventRenderItemBackground, value); + } + } + /// + /// + /// Draws the split button + /// + public event ToolStripItemImageRenderEventHandler RenderItemImage { + add { + AddHandler(EventRenderItemImage, value); + } + remove { + RemoveHandler(EventRenderItemImage, value); + } + } + /// + /// + /// Draws the checkmark + /// + public event ToolStripItemImageRenderEventHandler RenderItemCheck { + add { + AddHandler(EventRenderItemCheck, value); + } + remove { + RemoveHandler(EventRenderItemCheck, value); + } + } + /// + /// + /// Draws the split button + /// + public event ToolStripItemTextRenderEventHandler RenderItemText { + add { + AddHandler(EventRenderItemText, value); + } + remove { + RemoveHandler(EventRenderItemText, value); + } + } + + public event ToolStripRenderEventHandler RenderImageMargin { + add { + AddHandler(EventRenderImageMargin, value); + } + remove { + RemoveHandler(EventRenderImageMargin, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderLabelBackground { + add { + AddHandler(EventRenderLabelBackground, value); + } + remove { + RemoveHandler(EventRenderLabelBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderMenuItemBackground { + add { + AddHandler(EventRenderMenuItemBackground, value); + } + remove { + RemoveHandler(EventRenderMenuItemBackground, value); + } + } + + /// + /// + /// Draws the split button + /// + public event ToolStripItemRenderEventHandler RenderToolStripStatusLabelBackground { + add { + AddHandler(EventRenderToolStripStatusLabelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripStatusLabelBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderStatusStripSizingGrip { + add { + AddHandler(EventRenderStatusStripSizingGrip, value); + } + remove { + RemoveHandler(EventRenderStatusStripSizingGrip, value); + } + } + + /// + /// + /// Draws the split button + /// + public event ToolStripItemRenderEventHandler RenderSplitButtonBackground { + add { + AddHandler(EventRenderSplitButtonBackground, value); + } + remove { + RemoveHandler(EventRenderSplitButtonBackground, value); + } + } + + + /// + public event ToolStripSeparatorRenderEventHandler RenderSeparator { + add { + AddHandler(EventRenderSeparator, value); + } + remove { + RemoveHandler(EventRenderSeparator, value); + } + } + +#region EventHandlerSecurity + /// ----------------------------------------------------------------------------- + /// SECREVIEW VSWhidbey 250785 - all stock renderer events require AllWindowsPermission + /// use the private AddHandler/RemoveHandler to ensure that security checks are + /// made. + /// ----------------------------------------------------------------------------- + + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + private void AddHandler(object key, Delegate value) { + Events.AddHandler(key, value); + } + + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + private void RemoveHandler(object key, Delegate value) { + Events.RemoveHandler(key, value); + } +#endregion + + /// + public static Image CreateDisabledImage(Image normalImage) { + return CreateDisabledImage(normalImage, null); + } + + /// + public void DrawArrow(ToolStripArrowRenderEventArgs e) { + OnRenderArrow(e); + + ToolStripArrowRenderEventHandler eh = Events[EventRenderArrow] as ToolStripArrowRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the background color + /// + public void DrawToolStripBackground(ToolStripRenderEventArgs e) { + OnRenderToolStripBackground(e); + + ToolStripRenderEventHandler eh = Events[EventRenderToolStripBackground] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + + /// + /// + /// Draw the background color + /// + public void DrawGrip(ToolStripGripRenderEventArgs e) { + OnRenderGrip(e); + ToolStripGripRenderEventHandler eh = Events[EventRenderGrip] as ToolStripGripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the item's background. + /// + public void DrawItemBackground(ToolStripItemRenderEventArgs e) + { + OnRenderItemBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderItemBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + /// + /// + /// Draw the background color + /// + public void DrawImageMargin(ToolStripRenderEventArgs e) { + OnRenderImageMargin(e); + + ToolStripRenderEventHandler eh = Events[EventRenderImageMargin] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the background color + /// + public void DrawLabelBackground(ToolStripItemRenderEventArgs e) + { + OnRenderLabelBackground(e); + ToolStripItemRenderEventHandler eh = Events[EventRenderLabelBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + public void DrawToolStripBorder(ToolStripRenderEventArgs e) + { + OnRenderToolStripBorder(e); + + ToolStripRenderEventHandler eh = Events[EventRenderBorder] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the item's background. + /// + public void DrawDropDownButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderDropDownButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderDropDownButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawOverflowButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderOverflowButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderOverflowButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw image + /// + public void DrawItemImage(ToolStripItemImageRenderEventArgs e) { + OnRenderItemImage(e); + + ToolStripItemImageRenderEventHandler eh = Events[EventRenderItemImage] as ToolStripItemImageRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw image + /// + public void DrawItemCheck(ToolStripItemImageRenderEventArgs e) { + OnRenderItemCheck(e); + + ToolStripItemImageRenderEventHandler eh = Events[EventRenderItemCheck] as ToolStripItemImageRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw text + /// + public void DrawItemText(ToolStripItemTextRenderEventArgs e) { + OnRenderItemText(e); + + ToolStripItemTextRenderEventHandler eh = Events[EventRenderItemText] as ToolStripItemTextRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawMenuItemBackground(ToolStripItemRenderEventArgs e) + { + OnRenderMenuItemBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderMenuItemBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the background color + /// + public void DrawSplitButton(ToolStripItemRenderEventArgs e) { + + OnRenderSplitButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderSplitButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + /// + /// + /// Draw the background color + /// + public void DrawToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + + OnRenderToolStripStatusLabelBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderToolStripStatusLabelBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + // + public void DrawStatusStripSizingGrip(ToolStripRenderEventArgs e) { + + OnRenderStatusStripSizingGrip(e); + + ToolStripRenderEventHandler eh = Events[EventRenderStatusStripSizingGrip] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + /// + /// + /// Draw the separator + /// + public void DrawSeparator(ToolStripSeparatorRenderEventArgs e) { + OnRenderSeparator(e); + ToolStripSeparatorRenderEventHandler eh = Events[EventRenderSeparator] as ToolStripSeparatorRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + public void DrawToolStripPanelBackground(ToolStripPanelRenderEventArgs e) { + OnRenderToolStripPanelBackground(e); + ToolStripPanelRenderEventHandler eh = Events[EventRenderToolStripPanelBackground] as ToolStripPanelRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + public void DrawToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e) { + OnRenderToolStripContentPanelBackground(e); + ToolStripContentPanelRenderEventHandler eh = Events[EventRenderToolStripContentPanelBackground] as ToolStripContentPanelRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + // consider make public + internal virtual Region GetTransparentRegion(ToolStrip toolStrip) { + return null; + } + + + /// + protected internal virtual void Initialize(ToolStrip toolStrip){ + } + + /// + protected internal virtual void InitializePanel(ToolStripPanel toolStripPanel){ + } + + /// + protected internal virtual void InitializeContentPanel(ToolStripContentPanel contentPanel){ + } + + + /// + protected internal virtual void InitializeItem (ToolStripItem item){ + } + + protected static void ScaleArrowOffsetsIfNeeded() { + if (isScalingInitialized) { + return; + } + + if (DpiHelper.IsScalingRequired) { + Offset2X = DpiHelper.LogicalToDeviceUnitsX(OFFSET_2PIXELS); + Offset2Y = DpiHelper.LogicalToDeviceUnitsY(OFFSET_2PIXELS); + offset4X = DpiHelper.LogicalToDeviceUnitsX(OFFSET_4PIXELS); + offset4Y = DpiHelper.LogicalToDeviceUnitsY(OFFSET_4PIXELS); + } + isScalingInitialized = true; + + } + + protected static void ScaleArrowOffsetsIfNeeded(int dpi) { + Offset2X = DpiHelper.LogicalToDeviceUnits(OFFSET_2PIXELS, dpi); + Offset2Y = DpiHelper.LogicalToDeviceUnits(OFFSET_2PIXELS, dpi); + offset4X = DpiHelper.LogicalToDeviceUnits(OFFSET_4PIXELS, dpi); + offset4Y = DpiHelper.LogicalToDeviceUnits(OFFSET_4PIXELS, dpi); + } + + /// + protected virtual void OnRenderArrow(ToolStripArrowRenderEventArgs e){ + + if (RendererOverride != null) { + RendererOverride.OnRenderArrow(e); + return; + } + Graphics g = e.Graphics; + Rectangle dropDownRect = e.ArrowRectangle; + using (Brush brush = new SolidBrush(e.ArrowColor)) { + + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + // if the width is odd - favor pushing it over one pixel right. + //middle.X += (dropDownRect.Width % 2); + + Point[] arrow = null; + + // We need the Elvis operator here, since at design time at this point Item can be null. + if (e.Item?.DeviceDpi != previousDeviceDpi && DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + previousDeviceDpi = e.Item.DeviceDpi; + ScaleArrowOffsetsIfNeeded(e.Item.DeviceDpi); + } + else { + ScaleArrowOffsetsIfNeeded(); + } + + // using (offset4X - Offset2X) instead of (Offset2X) to compensate for rounding error in scaling + int horizontalOffset = DpiHelper.EnableToolStripHighDpiImprovements ? offset4X - Offset2X : Offset2X; + + switch (e.Direction) { + case ArrowDirection.Up: + + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y + 1), + new Point(middle.X + Offset2X + 1, middle.Y + 1), + new Point(middle.X, middle.Y - Offset2Y)}; + + break; + case ArrowDirection.Left: + arrow = new Point[] { + new Point(middle.X + Offset2X, middle.Y - offset4Y), + new Point(middle.X + Offset2X, middle.Y + offset4Y), + new Point(middle.X - horizontalOffset, middle.Y)}; + + break; + case ArrowDirection.Right: + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y - offset4Y), + new Point(middle.X - Offset2X, middle.Y + offset4Y), + new Point(middle.X + horizontalOffset, middle.Y)}; + + break; + case ArrowDirection.Down: + default: + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y - 1), + new Point(middle.X + Offset2X + 1, middle.Y - 1), + new Point(middle.X, middle.Y + Offset2Y) }; + break; + } + g.FillPolygon(brush, arrow); + } + } + + /// + /// + /// Draw the winbar background. ToolStrip users should override this if they want to draw differently. + /// + protected virtual void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripBackground(e); + return; + } + } + + /// + /// + /// Draw the border around the ToolStrip. This should be done as the last step. + /// + protected virtual void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripBorder(e); + return; + } + } + + /// + /// + /// Draw the grip. ToolStrip users should override this if they want to draw differently. + /// + protected virtual void OnRenderGrip(ToolStripGripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderGrip(e); + return; + } + } + + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderItemBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemBackground(e); + return; + } + + } + + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderImageMargin(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderImageMargin(e); + return; + } + + } + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderButtonBackground(e); + return; + } + + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderDropDownButtonBackground(e); + return; + } + + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderOverflowButtonBackground(e); + return; + } + } + /// + /// + /// Draw the item'si mage. ToolStrip users should override this function to change the + /// drawing of all images. + /// + protected virtual void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemImage(e); + return; + } + + Rectangle imageRect = e.ImageRectangle; + Image image = e.Image; + + if (imageRect != Rectangle.Empty && image != null) { + bool disposeImage = false; + if (e.ShiftOnPress && e.Item.Pressed) { + imageRect.X +=1; + } + if (!e.Item.Enabled) { + image = CreateDisabledImage(image, e.ImageAttributes); + disposeImage = true; + } + if (e.Item.ImageScaling == ToolStripItemImageScaling.None) { + e.Graphics.DrawImage(image, imageRect, new Rectangle(Point.Empty,imageRect.Size), GraphicsUnit.Pixel); + + } + else { + e.Graphics.DrawImage(image, imageRect); + } + + if (disposeImage) { + image.Dispose(); + } + } + } + + + /// + protected virtual void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemCheck(e); + return; + } + + Rectangle imageRect = e.ImageRectangle; + Image image = e.Image; + + if (imageRect != Rectangle.Empty && image != null) { + if (!e.Item.Enabled) { + image = CreateDisabledImage(image, e.ImageAttributes); + } + + e.Graphics.DrawImage(image, imageRect, 0, 0, imageRect.Width, + imageRect.Height, GraphicsUnit.Pixel, e.ImageAttributes); + } + } + + + /// + /// + /// Draw the item's text. ToolStrip users should override this function to change the + /// drawing of all text. + /// + protected virtual void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemText(e); + return; + } + + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + Color textColor = e.TextColor; + Font textFont = e.TextFont; + string text = e.Text; + Rectangle textRect = e.TextRectangle; + TextFormatFlags textFormat = e.TextFormat; + // if we're disabled draw in a different color. + textColor = (item.Enabled) ? textColor : SystemColors.GrayText; + + if (e.TextDirection != ToolStripTextDirection.Horizontal && textRect.Width > 0 && textRect.Height > 0) { + // Perf: this is a bit heavy handed.. perhaps we can share the bitmap. + Size textSize = LayoutUtils.FlipSize(textRect.Size); + using (Bitmap textBmp = new Bitmap(textSize.Width, textSize.Height,PixelFormat.Format32bppPArgb)) { + + using (Graphics textGraphics = Graphics.FromImage(textBmp)) { + // now draw the text.. + textGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; + TextRenderer.DrawText(textGraphics, text, textFont, new Rectangle(Point.Empty, textSize), textColor, textFormat); + textBmp.RotateFlip((e.TextDirection == ToolStripTextDirection.Vertical90) ? RotateFlipType.Rotate90FlipNone : RotateFlipType.Rotate270FlipNone); + g.DrawImage(textBmp, textRect); + } + } + } + else { + TextRenderer.DrawText(g, text, textFont, textRect, textColor, textFormat); + } + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderLabelBackground(e); + return; + } + + + } + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderMenuItemBackground(e); + return; + } + } + + /// + /// + /// Draws a toolbar separator. ToolStrip users should override this function to change the + /// drawing of all separators. + /// + protected virtual void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderSeparator(e); + return; + } + + } + + protected virtual void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripPanelBackground(e); + return; + } + } + + protected virtual void OnRenderToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripContentPanelBackground(e); + return; + } + } + + /// + protected virtual void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripStatusLabelBackground(e); + return; + } + } + + protected virtual void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderStatusStripSizingGrip(e); + return; + } + + Graphics g = e.Graphics; + StatusStrip statusStrip = e.ToolStrip as StatusStrip; + + // we have a set of stock rectangles. Translate them over to where the grip is to be drawn + // for the white set, then translate them up and right one pixel for the grey. + + + if (statusStrip != null) { + Rectangle sizeGripBounds = statusStrip.SizeGripBounds; + + if (!LayoutUtils.IsZeroWidthOrHeight(sizeGripBounds)) { + Rectangle[] whiteRectangles = new Rectangle[baseSizeGripRectangles.Length]; + Rectangle[] greyRectangles = new Rectangle[baseSizeGripRectangles.Length]; + + for (int i = 0; i < baseSizeGripRectangles.Length; i++) { + Rectangle baseRect = baseSizeGripRectangles[i]; + if (statusStrip.RightToLeft == RightToLeft.Yes) { + baseRect.X = sizeGripBounds.Width - baseRect.X - baseRect.Width; + } + baseRect.Offset(sizeGripBounds.X, sizeGripBounds.Bottom - 12 /*height of pyramid (10px) + 2px padding from bottom*/); + whiteRectangles[i] = baseRect; + if (statusStrip.RightToLeft == RightToLeft.Yes) { + baseRect.Offset(1, -1); + } + else { + baseRect.Offset(-1, -1); + } + greyRectangles[i] = baseRect; + + } + + g.FillRectangles(SystemBrushes.ButtonHighlight, whiteRectangles); + g.FillRectangles(SystemBrushes.ButtonShadow, greyRectangles); + } + } + + } + /// + /// + /// Draw the item's background. + /// + protected virtual void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderSplitButtonBackground(e); + return; + } + } + + // VSWhidbey 252459: only paint background effects if no backcolor has been set or no background image has been set. + internal bool ShouldPaintBackground (Control control) { + return (control.RawBackColor == Color.Empty && control.BackgroundImage == null); + } + + private static Image CreateDisabledImage(Image normalImage, ImageAttributes imgAttrib) { + if (imgAttrib == null) { + imgAttrib = new ImageAttributes(); + } + + imgAttrib.ClearColorKey(); + imgAttrib.SetColorMatrix(DisabledImageColorMatrix); + + Size size = normalImage.Size; + Bitmap disabledBitmap = new Bitmap(size.Width, size.Height); + using (Graphics graphics = Graphics.FromImage(disabledBitmap)) { + + graphics.DrawImage(normalImage, + new Rectangle(0, 0, size.Width, size.Height), + 0, 0, size.Width, size.Height, + GraphicsUnit.Pixel, + imgAttrib); + } + + return disabledBitmap; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs.back b/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs.back new file mode 100644 index 000000000..6b403aab9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRenderer.cs.back @@ -0,0 +1,1093 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Drawing.Text; + using System.Windows.Forms.Internal; + using System.Drawing.Imaging; + using System.ComponentModel; + using System.Windows.Forms.Layout; + using System.Security; + using System.Security.Permissions; + + /// + public abstract class ToolStripRenderer { + + private static readonly object EventRenderSplitButtonBackground = new object(); + private static readonly object EventRenderItemBackground = new object(); + private static readonly object EventRenderItemImage = new object(); + private static readonly object EventRenderItemText = new object(); + private static readonly object EventRenderToolStripBackground = new object(); + private static readonly object EventRenderGrip = new object(); + private static readonly object EventRenderButtonBackground = new object(); + private static readonly object EventRenderLabelBackground = new object(); + private static readonly object EventRenderMenuItemBackground = new object(); + private static readonly object EventRenderDropDownButtonBackground = new object(); + private static readonly object EventRenderOverflowButtonBackground = new object(); + private static readonly object EventRenderImageMargin = new object(); + private static readonly object EventRenderBorder = new object(); + private static readonly object EventRenderArrow = new object(); + private static readonly object EventRenderStatusStripPanelBackground = new object(); + private static readonly object EventRenderToolStripStatusLabelBackground = new object(); + private static readonly object EventRenderSeparator = new object(); + private static readonly object EventRenderItemCheck = new object(); + private static readonly object EventRenderToolStripPanelBackground = new object(); + private static readonly object EventRenderToolStripContentPanelBackground = new object(); + + private static readonly object EventRenderStatusStripSizingGrip = new object(); + + private static ColorMatrix disabledImageColorMatrix; + + private EventHandlerList events; + private bool isAutoGenerated = false; + + private static bool isScalingInitialized = false; + internal int previousDeviceDpi = DpiHelper.DeviceDpi; + + // arrows are rendered as isosceles triangles, whose heights are half the base in order to have 45 degree angles + // Offset2X is half of the base + // Offset2Y is height of the isosceles triangle + private static int OFFSET_2PIXELS = 2; + private static int OFFSET_4PIXELS = 4; + protected static int Offset2X = OFFSET_2PIXELS; + protected static int Offset2Y = OFFSET_2PIXELS; + private static int offset4X = OFFSET_4PIXELS; + private static int offset4Y = OFFSET_4PIXELS; + + // this is used in building up the half pyramid of rectangles that are drawn in a + // status strip sizing grip. + private static Rectangle[] baseSizeGripRectangles = new Rectangle[] { new Rectangle(8,0,2,2), + new Rectangle(8,4,2,2), + new Rectangle(8,8,2,2), + new Rectangle(4,4,2,2), + new Rectangle(4,8,2,2), + new Rectangle(0,8,2,2) }; + + protected ToolStripRenderer() { + } + + internal ToolStripRenderer(bool isAutoGenerated) { + this.isAutoGenerated = isAutoGenerated; + } + // this is used in building disabled images. + private static ColorMatrix DisabledImageColorMatrix { + get { + if (disabledImageColorMatrix == null) { + // VSWhidbey 233470 + // this is the result of a GreyScale matrix multiplied by a transparency matrix of .5 + + + + float[][] greyscale = new float[5][]; + greyscale[0] = new float[5] {0.2125f, 0.2125f, 0.2125f, 0, 0}; + greyscale[1] = new float[5] {0.2577f, 0.2577f, 0.2577f, 0, 0}; + greyscale[2] = new float[5] {0.0361f, 0.0361f, 0.0361f, 0, 0}; + greyscale[3] = new float[5] {0, 0, 0, 1, 0}; + greyscale[4] = new float[5] {0.38f, 0.38f, 0.38f, 0, 1}; + + float[][] transparency = new float[5][]; + transparency[0] = new float[5] {1, 0, 0, 0, 0}; + transparency[1] = new float[5] {0, 1, 0, 0, 0}; + transparency[2] = new float[5] {0, 0, 1, 0, 0}; + transparency[3] = new float[5] {0, 0, 0, .7F, 0}; + transparency[4] = new float[5] {0, 0, 0, 0, 0}; + + + disabledImageColorMatrix = ControlPaint.MultiplyColorMatrix(transparency, greyscale); + + } + + return disabledImageColorMatrix; + } + } + + + /// + /// Gets the list of event handlers that are attached to this component. + /// + private EventHandlerList Events { + get { + if (events == null) { + events = new EventHandlerList(); + } + return events; + } + } + + internal bool IsAutoGenerated { + get { return isAutoGenerated; } + } + + // if we're in a low contrast, high resolution situation, use this renderer under the covers instead. + internal virtual ToolStripRenderer RendererOverride { + get { + return null; + } + } + + /// ----------------------------------------------------------------------------- + /// SECREVIEW VSWhidbey 250785 - all stock renderer events require AllWindowsPermission + /// use the private AddHandler/RemoveHandler to ensure that security checks are + /// made. + /// ----------------------------------------------------------------------------- + + + /// + public event ToolStripArrowRenderEventHandler RenderArrow { + add { + AddHandler(EventRenderArrow, value); + } + remove { + RemoveHandler(EventRenderArrow, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderToolStripBackground { + add { + AddHandler(EventRenderToolStripBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripBackground, value); + } + } + + + public event ToolStripPanelRenderEventHandler RenderToolStripPanelBackground { + add { + AddHandler(EventRenderToolStripPanelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripPanelBackground, value); + } + } + + public event ToolStripContentPanelRenderEventHandler RenderToolStripContentPanelBackground { + add { + AddHandler(EventRenderToolStripContentPanelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripContentPanelBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderToolStripBorder { + add { + AddHandler(EventRenderBorder, value); + } + remove { + RemoveHandler(EventRenderBorder, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderButtonBackground { + add { + AddHandler(EventRenderButtonBackground, value); + } + remove { + RemoveHandler(EventRenderButtonBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderDropDownButtonBackground { + add { + AddHandler(EventRenderDropDownButtonBackground, value); + } + remove { + RemoveHandler(EventRenderDropDownButtonBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderOverflowButtonBackground { + add { + AddHandler(EventRenderOverflowButtonBackground, value); + } + remove { + RemoveHandler(EventRenderOverflowButtonBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripGripRenderEventHandler RenderGrip { + add { + AddHandler(EventRenderGrip, value); + } + remove { + RemoveHandler(EventRenderGrip, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderItemBackground { + add { + AddHandler(EventRenderItemBackground, value); + } + remove { + RemoveHandler(EventRenderItemBackground, value); + } + } + /// + /// + /// Draws the split button + /// + public event ToolStripItemImageRenderEventHandler RenderItemImage { + add { + AddHandler(EventRenderItemImage, value); + } + remove { + RemoveHandler(EventRenderItemImage, value); + } + } + /// + /// + /// Draws the checkmark + /// + public event ToolStripItemImageRenderEventHandler RenderItemCheck { + add { + AddHandler(EventRenderItemCheck, value); + } + remove { + RemoveHandler(EventRenderItemCheck, value); + } + } + /// + /// + /// Draws the split button + /// + public event ToolStripItemTextRenderEventHandler RenderItemText { + add { + AddHandler(EventRenderItemText, value); + } + remove { + RemoveHandler(EventRenderItemText, value); + } + } + + public event ToolStripRenderEventHandler RenderImageMargin { + add { + AddHandler(EventRenderImageMargin, value); + } + remove { + RemoveHandler(EventRenderImageMargin, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderLabelBackground { + add { + AddHandler(EventRenderLabelBackground, value); + } + remove { + RemoveHandler(EventRenderLabelBackground, value); + } + } + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripItemRenderEventHandler RenderMenuItemBackground { + add { + AddHandler(EventRenderMenuItemBackground, value); + } + remove { + RemoveHandler(EventRenderMenuItemBackground, value); + } + } + + /// + /// + /// Draws the split button + /// + public event ToolStripItemRenderEventHandler RenderToolStripStatusLabelBackground { + add { + AddHandler(EventRenderToolStripStatusLabelBackground, value); + } + remove { + RemoveHandler(EventRenderToolStripStatusLabelBackground, value); + } + } + + /// + /// + /// Occurs when the display style has changed + /// + public event ToolStripRenderEventHandler RenderStatusStripSizingGrip { + add { + AddHandler(EventRenderStatusStripSizingGrip, value); + } + remove { + RemoveHandler(EventRenderStatusStripSizingGrip, value); + } + } + + /// + /// + /// Draws the split button + /// + public event ToolStripItemRenderEventHandler RenderSplitButtonBackground { + add { + AddHandler(EventRenderSplitButtonBackground, value); + } + remove { + RemoveHandler(EventRenderSplitButtonBackground, value); + } + } + + + /// + public event ToolStripSeparatorRenderEventHandler RenderSeparator { + add { + AddHandler(EventRenderSeparator, value); + } + remove { + RemoveHandler(EventRenderSeparator, value); + } + } + +#region EventHandlerSecurity + /// ----------------------------------------------------------------------------- + /// SECREVIEW VSWhidbey 250785 - all stock renderer events require AllWindowsPermission + /// use the private AddHandler/RemoveHandler to ensure that security checks are + /// made. + /// ----------------------------------------------------------------------------- + + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + private void AddHandler(object key, Delegate value) { + Events.AddHandler(key, value); + } + + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + private void RemoveHandler(object key, Delegate value) { + Events.RemoveHandler(key, value); + } +#endregion + + /// + public static Image CreateDisabledImage(Image normalImage) { + return CreateDisabledImage(normalImage, null); + } + + /// + public void DrawArrow(ToolStripArrowRenderEventArgs e) { + OnRenderArrow(e); + + ToolStripArrowRenderEventHandler eh = Events[EventRenderArrow] as ToolStripArrowRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the background color + /// + public void DrawToolStripBackground(ToolStripRenderEventArgs e) { + OnRenderToolStripBackground(e); + + ToolStripRenderEventHandler eh = Events[EventRenderToolStripBackground] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + + /// + /// + /// Draw the background color + /// + public void DrawGrip(ToolStripGripRenderEventArgs e) { + OnRenderGrip(e); + ToolStripGripRenderEventHandler eh = Events[EventRenderGrip] as ToolStripGripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the item's background. + /// + public void DrawItemBackground(ToolStripItemRenderEventArgs e) + { + OnRenderItemBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderItemBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + /// + /// + /// Draw the background color + /// + public void DrawImageMargin(ToolStripRenderEventArgs e) { + OnRenderImageMargin(e); + + ToolStripRenderEventHandler eh = Events[EventRenderImageMargin] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the background color + /// + public void DrawLabelBackground(ToolStripItemRenderEventArgs e) + { + OnRenderLabelBackground(e); + ToolStripItemRenderEventHandler eh = Events[EventRenderLabelBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + public void DrawToolStripBorder(ToolStripRenderEventArgs e) + { + OnRenderToolStripBorder(e); + + ToolStripRenderEventHandler eh = Events[EventRenderBorder] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the item's background. + /// + public void DrawDropDownButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderDropDownButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderDropDownButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawOverflowButtonBackground(ToolStripItemRenderEventArgs e) + { + OnRenderOverflowButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderOverflowButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw image + /// + public void DrawItemImage(ToolStripItemImageRenderEventArgs e) { + OnRenderItemImage(e); + + ToolStripItemImageRenderEventHandler eh = Events[EventRenderItemImage] as ToolStripItemImageRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw image + /// + public void DrawItemCheck(ToolStripItemImageRenderEventArgs e) { + OnRenderItemCheck(e); + + ToolStripItemImageRenderEventHandler eh = Events[EventRenderItemCheck] as ToolStripItemImageRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw text + /// + public void DrawItemText(ToolStripItemTextRenderEventArgs e) { + OnRenderItemText(e); + + ToolStripItemTextRenderEventHandler eh = Events[EventRenderItemText] as ToolStripItemTextRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Draw the item's background. + /// + public void DrawMenuItemBackground(ToolStripItemRenderEventArgs e) + { + OnRenderMenuItemBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderMenuItemBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + + /// + /// + /// Draw the background color + /// + public void DrawSplitButton(ToolStripItemRenderEventArgs e) { + + OnRenderSplitButtonBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderSplitButtonBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + /// + /// + /// Draw the background color + /// + public void DrawToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + + OnRenderToolStripStatusLabelBackground(e); + + ToolStripItemRenderEventHandler eh = Events[EventRenderToolStripStatusLabelBackground] as ToolStripItemRenderEventHandler; + if (eh != null) { + eh(this, e); + } + + } + + // + public void DrawStatusStripSizingGrip(ToolStripRenderEventArgs e) { + + OnRenderStatusStripSizingGrip(e); + + ToolStripRenderEventHandler eh = Events[EventRenderStatusStripSizingGrip] as ToolStripRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + /// + /// + /// Draw the separator + /// + public void DrawSeparator(ToolStripSeparatorRenderEventArgs e) { + OnRenderSeparator(e); + ToolStripSeparatorRenderEventHandler eh = Events[EventRenderSeparator] as ToolStripSeparatorRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + public void DrawToolStripPanelBackground(ToolStripPanelRenderEventArgs e) { + OnRenderToolStripPanelBackground(e); + ToolStripPanelRenderEventHandler eh = Events[EventRenderToolStripPanelBackground] as ToolStripPanelRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + public void DrawToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e) { + OnRenderToolStripContentPanelBackground(e); + ToolStripContentPanelRenderEventHandler eh = Events[EventRenderToolStripContentPanelBackground] as ToolStripContentPanelRenderEventHandler; + if (eh != null) { + eh(this, e); + } + } + + // consider make public + internal virtual Region GetTransparentRegion(ToolStrip toolStrip) { + return null; + } + + + /// + protected internal virtual void Initialize(ToolStrip toolStrip){ + } + + /// + protected internal virtual void InitializePanel(ToolStripPanel toolStripPanel){ + } + + /// + protected internal virtual void InitializeContentPanel(ToolStripContentPanel contentPanel){ + } + + + /// + protected internal virtual void InitializeItem (ToolStripItem item){ + } + + protected static void ScaleArrowOffsetsIfNeeded() { + if (isScalingInitialized) { + return; + } + + if (DpiHelper.IsScalingRequired) { + Offset2X = DpiHelper.LogicalToDeviceUnitsX(OFFSET_2PIXELS); + Offset2Y = DpiHelper.LogicalToDeviceUnitsY(OFFSET_2PIXELS); + offset4X = DpiHelper.LogicalToDeviceUnitsX(OFFSET_4PIXELS); + offset4Y = DpiHelper.LogicalToDeviceUnitsY(OFFSET_4PIXELS); + } + isScalingInitialized = true; + + } + + protected static void ScaleArrowOffsetsIfNeeded(int dpi) { + Offset2X = DpiHelper.LogicalToDeviceUnits(OFFSET_2PIXELS, dpi); + Offset2Y = DpiHelper.LogicalToDeviceUnits(OFFSET_2PIXELS, dpi); + offset4X = DpiHelper.LogicalToDeviceUnits(OFFSET_4PIXELS, dpi); + offset4Y = DpiHelper.LogicalToDeviceUnits(OFFSET_4PIXELS, dpi); + } + + /// + protected virtual void OnRenderArrow(ToolStripArrowRenderEventArgs e){ + + if (RendererOverride != null) { + RendererOverride.OnRenderArrow(e); + return; + } + Graphics g = e.Graphics; + Rectangle dropDownRect = e.ArrowRectangle; + using (Brush brush = new SolidBrush(e.ArrowColor)) { + + Point middle = new Point(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2); + // if the width is odd - favor pushing it over one pixel right. + //middle.X += (dropDownRect.Width % 2); + + Point[] arrow = null; + + // We need the Elvis operator here, since at design time at this point Item can be null. + if (e.Item?.DeviceDpi != previousDeviceDpi && DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) { + previousDeviceDpi = e.Item.DeviceDpi; + ScaleArrowOffsetsIfNeeded(e.Item.DeviceDpi); + } + else { + ScaleArrowOffsetsIfNeeded(); + } + + // using (offset4X - Offset2X) instead of (Offset2X) to compensate for rounding error in scaling + int horizontalOffset = DpiHelper.EnableToolStripHighDpiImprovements ? offset4X - Offset2X : Offset2X; + + switch (e.Direction) { + case ArrowDirection.Up: + + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y + 1), + new Point(middle.X + Offset2X + 1, middle.Y + 1), + new Point(middle.X, middle.Y - Offset2Y)}; + + break; + case ArrowDirection.Left: + arrow = new Point[] { + new Point(middle.X + Offset2X, middle.Y - offset4Y), + new Point(middle.X + Offset2X, middle.Y + offset4Y), + new Point(middle.X - horizontalOffset, middle.Y)}; + + break; + case ArrowDirection.Right: + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y - offset4Y), + new Point(middle.X - Offset2X, middle.Y + offset4Y), + new Point(middle.X + horizontalOffset, middle.Y)}; + + break; + case ArrowDirection.Down: + default: + arrow = new Point[] { + new Point(middle.X - Offset2X, middle.Y - 1), + new Point(middle.X + Offset2X + 1, middle.Y - 1), + new Point(middle.X, middle.Y + Offset2Y) }; + break; + } + g.FillPolygon(brush, arrow); + } + } + + /// + /// + /// Draw the winbar background. ToolStrip users should override this if they want to draw differently. + /// + protected virtual void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripBackground(e); + return; + } + } + + /// + /// + /// Draw the border around the ToolStrip. This should be done as the last step. + /// + protected virtual void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripBorder(e); + return; + } + } + + /// + /// + /// Draw the grip. ToolStrip users should override this if they want to draw differently. + /// + protected virtual void OnRenderGrip(ToolStripGripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderGrip(e); + return; + } + } + + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderItemBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemBackground(e); + return; + } + + } + + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderImageMargin(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderImageMargin(e); + return; + } + + } + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderButtonBackground(e); + return; + } + + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderDropDownButtonBackground(e); + return; + } + + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderOverflowButtonBackground(e); + return; + } + } + /// + /// + /// Draw the item'si mage. ToolStrip users should override this function to change the + /// drawing of all images. + /// + protected virtual void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemImage(e); + return; + } + + Rectangle imageRect = e.ImageRectangle; + Image image = e.Image; + + if (imageRect != Rectangle.Empty && image != null) { + bool disposeImage = false; + if (e.ShiftOnPress && e.Item.Pressed) { + imageRect.X +=1; + } + if (!e.Item.Enabled) { + image = CreateDisabledImage(image, e.ImageAttributes); + disposeImage = true; + } + if (e.Item.ImageScaling == ToolStripItemImageScaling.None) { + e.Graphics.DrawImage(image, imageRect, new Rectangle(Point.Empty,imageRect.Size), GraphicsUnit.Pixel); + + } + else { + e.Graphics.DrawImage(image, imageRect); + } + + if (disposeImage) { + image.Dispose(); + } + } + } + + + /// + protected virtual void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemCheck(e); + return; + } + + Rectangle imageRect = e.ImageRectangle; + Image image = e.Image; + + if (imageRect != Rectangle.Empty && image != null) { + if (!e.Item.Enabled) { + image = CreateDisabledImage(image, e.ImageAttributes); + } + + e.Graphics.DrawImage(image, imageRect, 0, 0, imageRect.Width, + imageRect.Height, GraphicsUnit.Pixel, e.ImageAttributes); + } + } + + + /// + /// + /// Draw the item's text. ToolStrip users should override this function to change the + /// drawing of all text. + /// + protected virtual void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderItemText(e); + return; + } + + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + Color textColor = e.TextColor; + Font textFont = e.TextFont; + string text = e.Text; + Rectangle textRect = e.TextRectangle; + TextFormatFlags textFormat = e.TextFormat; + // if we're disabled draw in a different color. + textColor = (item.Enabled) ? textColor : SystemColors.GrayText; + + if (e.TextDirection != ToolStripTextDirection.Horizontal && textRect.Width > 0 && textRect.Height > 0) { + // Perf: this is a bit heavy handed.. perhaps we can share the bitmap. + Size textSize = LayoutUtils.FlipSize(textRect.Size); + using (Bitmap textBmp = new Bitmap(textSize.Width, textSize.Height,PixelFormat.Format32bppPArgb)) { + + using (Graphics textGraphics = Graphics.FromImage(textBmp)) { + // now draw the text.. + textGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; + TextRenderer.DrawText(textGraphics, text, textFont, new Rectangle(Point.Empty, textSize), textColor, textFormat); + textBmp.RotateFlip((e.TextDirection == ToolStripTextDirection.Vertical90) ? RotateFlipType.Rotate90FlipNone : RotateFlipType.Rotate270FlipNone); + g.DrawImage(textBmp, textRect); + } + } + } + else { + TextRenderer.DrawText(g, text, textFont, textRect, textColor, textFormat); + } + } + + /// + /// + /// Draw the button background + /// + protected virtual void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderLabelBackground(e); + return; + } + + + } + /// + /// + /// Draw the items background + /// + protected virtual void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderMenuItemBackground(e); + return; + } + } + + /// + /// + /// Draws a toolbar separator. ToolStrip users should override this function to change the + /// drawing of all separators. + /// + protected virtual void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderSeparator(e); + return; + } + + } + + protected virtual void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripPanelBackground(e); + return; + } + } + + protected virtual void OnRenderToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripContentPanelBackground(e); + return; + } + } + + /// + protected virtual void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderToolStripStatusLabelBackground(e); + return; + } + } + + protected virtual void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderStatusStripSizingGrip(e); + return; + } + + Graphics g = e.Graphics; + StatusStrip statusStrip = e.ToolStrip as StatusStrip; + + // we have a set of stock rectangles. Translate them over to where the grip is to be drawn + // for the white set, then translate them up and right one pixel for the grey. + + + if (statusStrip != null) { + Rectangle sizeGripBounds = statusStrip.SizeGripBounds; + + if (!LayoutUtils.IsZeroWidthOrHeight(sizeGripBounds)) { + Rectangle[] whiteRectangles = new Rectangle[baseSizeGripRectangles.Length]; + Rectangle[] greyRectangles = new Rectangle[baseSizeGripRectangles.Length]; + + for (int i = 0; i < baseSizeGripRectangles.Length; i++) { + Rectangle baseRect = baseSizeGripRectangles[i]; + if (statusStrip.RightToLeft == RightToLeft.Yes) { + baseRect.X = sizeGripBounds.Width - baseRect.X - baseRect.Width; + } + baseRect.Offset(sizeGripBounds.X, sizeGripBounds.Bottom - 12 /*height of pyramid (10px) + 2px padding from bottom*/); + whiteRectangles[i] = baseRect; + if (statusStrip.RightToLeft == RightToLeft.Yes) { + baseRect.Offset(1, -1); + } + else { + baseRect.Offset(-1, -1); + } + greyRectangles[i] = baseRect; + + } + + g.FillRectangles(SystemBrushes.ButtonHighlight, whiteRectangles); + g.FillRectangles(SystemBrushes.ButtonShadow, greyRectangles); + } + } + + } + /// + /// + /// Draw the item's background. + /// + protected virtual void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { + if (RendererOverride != null) { + RendererOverride.OnRenderSplitButtonBackground(e); + return; + } + } + + // VSWhidbey 252459: only paint background effects if no backcolor has been set or no background image has been set. + internal bool ShouldPaintBackground (Control control) { + return (control.RawBackColor == Color.Empty && control.BackgroundImage == null); + } + + private static Image CreateDisabledImage(Image normalImage, ImageAttributes imgAttrib) { + if (imgAttrib == null) { + imgAttrib = new ImageAttributes(); + } + + imgAttrib.ClearColorKey(); + imgAttrib.SetColorMatrix(DisabledImageColorMatrix); + + Size size = normalImage.Size; + Bitmap disabledBitmap = new Bitmap(size.Width, size.Height); + using (Graphics graphics = Graphics.FromImage(disabledBitmap)) { + + graphics.DrawImage(normalImage, + new Rectangle(0, 0, size.Width, size.Height), + 0, 0, size.Width, size.Height, + GraphicsUnit.Pixel, + imgAttrib); + } + + return disabledBitmap; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripRendererSwitcher.cs b/WindowsForms/Managed/System/WinForms/ToolStripRendererSwitcher.cs new file mode 100644 index 000000000..c00f99c0d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripRendererSwitcher.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Collections.Specialized; + using System.ComponentModel; + + + // this class encapsulates the logic for Renderer and RenderMode so it can + // be shared across classes. + internal class ToolStripRendererSwitcher { + + private static readonly int stateUseDefaultRenderer = BitVector32.CreateMask(); + private static readonly int stateAttachedRendererChanged = BitVector32.CreateMask(stateUseDefaultRenderer); + + + private ToolStripRenderer renderer = null; + private Type currentRendererType = typeof(System.Type); + private BitVector32 state = new BitVector32(); + + private ToolStripRenderMode defaultRenderMode = ToolStripRenderMode.ManagerRenderMode; + + public ToolStripRendererSwitcher(Control owner, ToolStripRenderMode defaultRenderMode) : this(owner) { + this.defaultRenderMode = defaultRenderMode; + this.RenderMode = defaultRenderMode; + } + + + public ToolStripRendererSwitcher(Control owner) { + state[stateUseDefaultRenderer] = true; + state[stateAttachedRendererChanged] = false; + owner.Disposed += new EventHandler(OnControlDisposed); + owner.VisibleChanged += new EventHandler(OnControlVisibleChanged); + if (owner.Visible) { + OnControlVisibleChanged(owner, EventArgs.Empty); + } + + } + + public ToolStripRenderer Renderer { + get { + if (RenderMode == ToolStripRenderMode.ManagerRenderMode) { + return ToolStripManager.Renderer; + } + // always return a valid renderer so our paint code + // doesn't have to be bogged down by checks for null. + + state[stateUseDefaultRenderer] = false; + if (renderer == null) { + Renderer = ToolStripManager.CreateRenderer(RenderMode); + } + return renderer; + + } + set { + // if the value happens to be null, the next get + // will autogenerate a new ToolStripRenderer. + if (renderer != value) { + state[stateUseDefaultRenderer] = (value == null); + renderer = value; + currentRendererType = (renderer != null) ? renderer.GetType() : typeof(System.Type); + + OnRendererChanged(EventArgs.Empty); + } + } + } + + public ToolStripRenderMode RenderMode { + get { + if (state[stateUseDefaultRenderer]) { + return ToolStripRenderMode.ManagerRenderMode; + } + if (renderer != null && !renderer.IsAutoGenerated) { + return ToolStripRenderMode.Custom; + } + // check the type of the currently set renderer. + // types are cached as this may be called frequently. + if (currentRendererType == ToolStripManager.ProfessionalRendererType) { + return ToolStripRenderMode.Professional; + } + if (currentRendererType == ToolStripManager.SystemRendererType) { + return ToolStripRenderMode.System; + } + return ToolStripRenderMode.Custom; + + } + set { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripRenderMode.Custom, (int)ToolStripRenderMode.ManagerRenderMode)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripRenderMode)); + } + if (value == ToolStripRenderMode.Custom) { + throw new NotSupportedException(SR.GetString(SR.ToolStripRenderModeUseRendererPropertyInstead)); + } + + if (value == ToolStripRenderMode.ManagerRenderMode) { + if (!state[stateUseDefaultRenderer] ) { + state[stateUseDefaultRenderer] = true; + OnRendererChanged(EventArgs.Empty); + } + } + else { + state[stateUseDefaultRenderer] = false; + Renderer = ToolStripManager.CreateRenderer(value); + } + } + } + + public event EventHandler RendererChanged; + + private void OnRendererChanged(EventArgs e) { + if (this.RendererChanged != null) { + this.RendererChanged(this, e); + } + } + private void OnDefaultRendererChanged(object sender, EventArgs e) { + if (state[stateUseDefaultRenderer]) { + OnRendererChanged(e); + } + } + + private void OnControlDisposed(object sender, EventArgs e) { + if (state[stateAttachedRendererChanged]) { + ToolStripManager.RendererChanged -= new EventHandler(OnDefaultRendererChanged); + state[stateAttachedRendererChanged] = false; + } + } + + private void OnControlVisibleChanged(object sender, EventArgs e) { + Control control = sender as Control; + if (control != null) { + if (control.Visible) { + if (!state[stateAttachedRendererChanged]) { + ToolStripManager.RendererChanged += new EventHandler(OnDefaultRendererChanged); + state[stateAttachedRendererChanged] = true; + } + } + else { + if (state[stateAttachedRendererChanged]) { + ToolStripManager.RendererChanged -= new EventHandler(OnDefaultRendererChanged); + state[stateAttachedRendererChanged] = false; + } + } + } + } + + public bool ShouldSerializeRenderMode() { + // We should NEVER serialize custom. + return (RenderMode != defaultRenderMode && RenderMode != ToolStripRenderMode.Custom); + } + public void ResetRenderMode() { + this.RenderMode = defaultRenderMode; + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripScrollButton.cs b/WindowsForms/Managed/System/WinForms/ToolStripScrollButton.cs new file mode 100644 index 000000000..0ab547132 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripScrollButton.cs @@ -0,0 +1,199 @@ + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Diagnostics; + using System.Windows.Forms.ButtonInternal; + using System.Security.Permissions; + using System.Security; + + + /// + /// + /// A non selectable winbar item + /// + internal class ToolStripScrollButton : ToolStripControlHost { + private bool up = true; + + [ThreadStatic] + private static Bitmap upScrollImage; + + [ThreadStatic] + private static Bitmap downScrollImage; + + const int AUTOSCROLL_UPDATE = 50; + private static readonly int AUTOSCROLL_PAUSE = SystemInformation.DoubleClickTime; + + + private Timer mouseDownTimer; + + public ToolStripScrollButton(bool up) : base(CreateControlInstance(up)) { + this.up = up; + } + + + private static Control CreateControlInstance(bool up) { + StickyLabel label = new StickyLabel(); + label.ImageAlign = ContentAlignment.MiddleCenter; + label.Image = (up) ? UpImage : DownImage; + return label; + } + + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal override Padding DefaultMargin { + get { + return Padding.Empty; + } + } + protected override Padding DefaultPadding { + get { + return Padding.Empty; + } + } + + private static Image DownImage { + get { + if (downScrollImage == null) { + downScrollImage = new Bitmap(typeof(ToolStripScrollButton), "ScrollButtonDown.bmp"); + downScrollImage.MakeTransparent(Color.White); + + } + return downScrollImage; + } + } + + + internal StickyLabel Label { + get{ + return Control as StickyLabel; + } + } + + private static Image UpImage { + get { + if (upScrollImage == null) { + upScrollImage = new Bitmap(typeof(ToolStripScrollButton), "ScrollButtonUp.bmp"); + upScrollImage.MakeTransparent(Color.White); + + } + return upScrollImage; + } + } + + private Timer MouseDownTimer { + get{ + if (mouseDownTimer == null) { + mouseDownTimer = new Timer(); + } + return mouseDownTimer; + } + } + + protected override void Dispose(bool disposing) { + if (disposing) { + if (mouseDownTimer != null) { + mouseDownTimer.Enabled = false; + mouseDownTimer.Dispose(); + mouseDownTimer = null; + } + } + base.Dispose(disposing); + } + protected override void OnMouseDown (MouseEventArgs e) { + UnsubscribeAll(); + + base.OnMouseDown(e); + Scroll(); + + MouseDownTimer.Interval = AUTOSCROLL_PAUSE; + MouseDownTimer.Tick += new EventHandler(OnInitialAutoScrollMouseDown); + MouseDownTimer.Enabled = true; + } + + protected override void OnMouseUp (MouseEventArgs e) { + UnsubscribeAll(); + base.OnMouseUp(e); + } + + protected override void OnMouseLeave (EventArgs e) { + UnsubscribeAll(); + } + private void UnsubscribeAll() { + MouseDownTimer.Enabled = false; + MouseDownTimer.Tick -= new EventHandler(OnInitialAutoScrollMouseDown); + MouseDownTimer.Tick -= new EventHandler(OnAutoScrollAccellerate); + } + + private void OnAutoScrollAccellerate(object sender, EventArgs e) { + Scroll(); + } + + private void OnInitialAutoScrollMouseDown(object sender, EventArgs e) { + MouseDownTimer.Tick -= new EventHandler(OnInitialAutoScrollMouseDown); + + Scroll(); + MouseDownTimer.Interval = AUTOSCROLL_UPDATE; + MouseDownTimer.Tick += new EventHandler(OnAutoScrollAccellerate); + } + + public override Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = Size.Empty; + preferredSize.Height = (Label.Image != null) ? Label.Image.Height + 4 : 0; + preferredSize.Width = (ParentInternal != null) ? ParentInternal.Width - 2 : preferredSize.Width; // Two for border + return preferredSize; + } + + private void Scroll() { + ToolStripDropDownMenu parent = this.ParentInternal as ToolStripDropDownMenu; + if (parent != null && Label.Enabled) { + parent.ScrollInternal(up); + } + } + + internal class StickyLabel : Label { + + public StickyLabel() { + } + private bool freezeLocationChange = false; + + public bool FreezeLocationChange { + get { return freezeLocationChange; } + } + + protected override void SetBoundsCore(int x,int y,int width, int height, BoundsSpecified specified) + { + if (((specified & BoundsSpecified.Location) != 0) && FreezeLocationChange) { + return; + } + base.SetBoundsCore(x, y, width, height, specified); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + + if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) { + // SECREVIEW: + // NOTE this is the same as ToolStripDropDown, except we NEVER want to raise the key events. + DefWndProc(ref m); + return; + } + + base.WndProc(ref m); + } + } + } +} + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSeparator.cs b/WindowsForms/Managed/System/WinForms/ToolStripSeparator.cs new file mode 100644 index 000000000..7486f4b6b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSeparator.cs @@ -0,0 +1,508 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.ComponentModel; + using System.Windows.Forms.Design; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Versioning; + + /// + /// + /// + /// Called when the background of the winbar is being rendered + /// + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)] + public class ToolStripSeparator : ToolStripItem { + private const int WINBAR_SEPARATORTHICKNESS = 6; + private const int WINBAR_SEPARATORHEIGHT = 23; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public ToolStripSeparator() { + this.ForeColor = SystemColors.ControlDark; + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool AutoToolTip { + get { + return base.AutoToolTip; + } + set { + base.AutoToolTip = value; + } + } + + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + + } + set { + base.BackgroundImage = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + public override bool CanSelect { + get { + return DesignMode; + } + } + + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(WINBAR_SEPARATORTHICKNESS, WINBAR_SEPARATORTHICKNESS); + } + } + + + /// + protected internal override Padding DefaultMargin { + get { + return Padding.Empty; + } + } + + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool DoubleClickEnabled { + get { + return base.DoubleClickEnabled; + } + set { + base.DoubleClickEnabled = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override bool Enabled { + get { + return base.Enabled; + } + set { + base.Enabled = value; + } + + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler EnabledChanged { + add { + base.EnabledChanged += value; + } + remove { + base.EnabledChanged -= value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ToolStripItemDisplayStyle DisplayStyle { + get { + return base.DisplayStyle; + } + set { + base.DisplayStyle = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler DisplayStyleChanged { + add { + base.DisplayStyleChanged += value; + } + remove { + base.DisplayStyleChanged -= value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ContentAlignment ImageAlign { + get { + return base.ImageAlign; + } + set { + base.ImageAlign = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + return base.Image; + } + set { + base.Image = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + RefreshProperties(RefreshProperties.Repaint), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new int ImageIndex { + get { + return base.ImageIndex; + } + set { + base.ImageIndex = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new string ImageKey { + get { + return base.ImageKey; + } + set { + base.ImageKey = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Color ImageTransparentColor { + get { + return base.ImageTransparentColor; + } + set { + base.ImageTransparentColor = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ToolStripItemImageScaling ImageScaling { + get { + return base.ImageScaling; + } + set { + base.ImageScaling = value; + } + } + + + private bool IsVertical { + get { + ToolStrip parent = this.ParentInternal; + + if (parent == null) { + parent = Owner; + } + ToolStripDropDownMenu dropDownMenu = parent as ToolStripDropDownMenu; + if (dropDownMenu != null) { + return false; + } + switch (parent.LayoutStyle) { + case ToolStripLayoutStyle.VerticalStackWithOverflow: + return false; + case ToolStripLayoutStyle.HorizontalStackWithOverflow: + case ToolStripLayoutStyle.Flow: + case ToolStripLayoutStyle.Table: + default: + return true; + } + } + } + + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new ContentAlignment TextAlign { + get { + return base.TextAlign; + } + set { + base.TextAlign = value; + } + } + + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(ToolStripTextDirection.Horizontal)] + public override ToolStripTextDirection TextDirection { + get { + return base.TextDirection; + } + set { + base.TextDirection = value; + } + } + + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new TextImageRelation TextImageRelation { + get { + return base.TextImageRelation; + } + set { + base.TextImageRelation = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new string ToolTipText { + get { + return base.ToolTipText; + } + set { + base.ToolTipText = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new bool RightToLeftAutoMirrorImage { + get { + return base.RightToLeftAutoMirrorImage; + } + set { + base.RightToLeftAutoMirrorImage = value; + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + return new ToolStripSeparatorAccessibleObject(this); + } + + + /// + public override Size GetPreferredSize(Size constrainingSize) { + ToolStrip parent = this.ParentInternal; + + if (parent == null) { + parent = Owner; + } + if (parent == null) { + return new Size(WINBAR_SEPARATORTHICKNESS, WINBAR_SEPARATORTHICKNESS); + } + + ToolStripDropDownMenu dropDownMenu = parent as ToolStripDropDownMenu; + if (dropDownMenu != null) { + return new Size(parent.Width - (parent.Padding.Horizontal - dropDownMenu.ImageMargin.Width), WINBAR_SEPARATORTHICKNESS); + } + else { + if (parent.LayoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow || parent.LayoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) { + // we dont actually know what size to make it, so just keep it a stock size. + constrainingSize.Width = WINBAR_SEPARATORHEIGHT; + constrainingSize.Height = WINBAR_SEPARATORHEIGHT; + } + if (IsVertical) { + return new Size(WINBAR_SEPARATORTHICKNESS, constrainingSize.Height); + } + else { + return new Size(constrainingSize.Width, WINBAR_SEPARATORTHICKNESS); + } + } + + } + + + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + if (this.Owner != null && this.ParentInternal != null) { + this.Renderer.DrawSeparator(new ToolStripSeparatorRenderEventArgs(e.Graphics, this, IsVertical)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void OnFontChanged(EventArgs e) { + // PERF: dont call base, we dont care if the font changes + RaiseEvent(EventFontChanged, e); + } + + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + [EditorBrowsable(EditorBrowsableState.Never)] + internal override bool ShouldSerializeForeColor() { + return (ForeColor != SystemColors.ControlDark); + } + + internal protected override void SetBounds(Rectangle rect) { + + ToolStripDropDownMenu dropDownMenu = this.Owner as ToolStripDropDownMenu; + if (dropDownMenu != null) { + + // Scooch over by the padding amount. The padding is added to + // the ToolStripDropDownMenu to keep the non-menu item riffraff + // aligned to the text rectangle. When flow layout comes through to set our position + // via IArrangedElement DEFY IT! + if (dropDownMenu != null) { + rect.X = 2; + rect.Width = dropDownMenu.Width -4; + } + } + base.SetBounds(rect); + } + + /// + /// An implementation of AccessibleChild for use with ToolStripItems + /// + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripSeparatorAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripSeparator ownerItem = null; + + public ToolStripSeparatorAccessibleObject(ToolStripSeparator ownerItem): base(ownerItem) { + this.ownerItem = ownerItem; + } + + public override AccessibleRole Role { + get { + AccessibleRole role = ownerItem.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + else { + return AccessibleRole.Separator; + } + + } + } + + internal override object GetPropertyValue(int propertyID) { + if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_SeparatorControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + } + } diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventArgs.cs b/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventArgs.cs new file mode 100644 index 000000000..7c90232df --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventArgs.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Drawing; + /// + /// + /// This class represents all the information to render the winbar + /// + public class ToolStripSeparatorRenderEventArgs : ToolStripItemRenderEventArgs { + + private bool vertical = false; + + /// + /// + /// This class represents all the information to render the winbar + /// + public ToolStripSeparatorRenderEventArgs(Graphics g, ToolStripSeparator separator, bool vertical) : base(g, separator) { + this.vertical = vertical; + } + + /// + /// + /// the graphics object to draw with + /// + public bool Vertical { + get { + return vertical; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventHandler.cs b/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventHandler.cs new file mode 100644 index 000000000..a10cdcc2d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSeparatorRenderEventHandler.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System; + + /// + /// + /// + /// Called when the background of a winbar item is being rendered + /// + /// + public delegate void ToolStripSeparatorRenderEventHandler(object sender, ToolStripSeparatorRenderEventArgs e); +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSettings.cs b/WindowsForms/Managed/System/WinForms/ToolStripSettings.cs new file mode 100644 index 000000000..b9ae213db --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSettings.cs @@ -0,0 +1,407 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Configuration; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + using System.Text; + using System.Text.RegularExpressions; + + /// + /// + /// A settings class used by the ToolStripManager to save toolstrip settings. + /// + internal class ToolStripSettings : ApplicationSettingsBase { + + internal ToolStripSettings(string settingsKey) : base(settingsKey) {} + + [UserScopedSetting] + [DefaultSettingValue("true")] + public bool IsDefault { + get { + return (bool) this["IsDefault"]; + } + set { + this["IsDefault"] = value; + } + } + + [UserScopedSetting] + public string ItemOrder { + get { + return this["ItemOrder"] as string; + } + set { + this["ItemOrder"] = value; + } + } + + [UserScopedSetting] + public string Name { + get { + return this["Name"] as string; + } + set { + this["Name"] = value; + } + } + + [UserScopedSetting] + [DefaultSettingValue("0,0")] + public Point Location { + get { + return (Point) this["Location"]; + } + set { + this["Location"] = value; + } + } + + [UserScopedSetting] + [DefaultSettingValue("0,0")] + public Size Size { + get { + return (Size) this["Size"]; + } + set { + this["Size"] = value; + } + } + + [UserScopedSetting] + public string ToolStripPanelName { + get { + return this["ToolStripPanelName"] as string; + } + set { + this["ToolStripPanelName"] = value; + } + } + + [UserScopedSetting] + [DefaultSettingValue("true")] + public bool Visible { + get { + return (bool) this["Visible"]; + } + set { + this["Visible"] = value; + } + } + + + public override void Save() { + this.IsDefault = false; + base.Save(); + } + } + + /// + /// + /// Helper class used by ToolStripManager that implements most of the logic to save out and apply + /// settings for toolstrips on a form. + /// + internal class ToolStripSettingsManager { + private Form form; + private string formKey; + + internal ToolStripSettingsManager(Form owner, string formKey) { + this.form = owner; + this.formKey = formKey; + } + + internal void Load() { + ArrayList savedToolStripSettingsObjects = new ArrayList(); + + foreach (ToolStrip toolStrip in FindToolStrips(true, form.Controls)) { + if (toolStrip != null && !string.IsNullOrEmpty(toolStrip.Name)) { + ToolStripSettings toolStripSettings = new ToolStripSettings(GetSettingsKey(toolStrip)); + + // Check if we have settings saved out for this toolstrip. If so, add it to our apply list. + if (!toolStripSettings.IsDefault) { + savedToolStripSettingsObjects.Add(new SettingsStub(toolStripSettings)); + } + } + } + + ApplySettings(savedToolStripSettingsObjects); + } + + internal void Save() { + foreach (ToolStrip toolStrip in FindToolStrips(true, form.Controls)) { + if (toolStrip != null && !string.IsNullOrEmpty(toolStrip.Name)) { + ToolStripSettings toolStripSettings = new ToolStripSettings(GetSettingsKey(toolStrip)); + SettingsStub stub = new SettingsStub(toolStrip); + + toolStripSettings.ItemOrder = stub.ItemOrder; + toolStripSettings.Name = stub.Name; + toolStripSettings.Location = stub.Location; + toolStripSettings.Size = stub.Size; + toolStripSettings.ToolStripPanelName = stub.ToolStripPanelName; + toolStripSettings.Visible = stub.Visible; + + toolStripSettings.Save(); + } + } + } + + internal static string GetItemOrder(ToolStrip toolStrip) { + StringBuilder itemNames = new StringBuilder(toolStrip.Items.Count); + + for (int i = 0; i < toolStrip.Items.Count; i++) { + itemNames.Append((toolStrip.Items[i].Name == null) ? "null" : toolStrip.Items[i].Name); + if (i != toolStrip.Items.Count - 1) { + itemNames.Append(","); + } + } + return itemNames.ToString(); + } + + private void ApplySettings(ArrayList toolStripSettingsToApply) { + if (toolStripSettingsToApply.Count == 0) { + return; + } + + SuspendAllLayout(form); + + + // iterate through all the toolstrips and build up a hash of where the items + // are right now. + Dictionary itemLocationHash = BuildItemOriginationHash(); + + // build up a hash of where we want the ToolStrips to go + Dictionary> toolStripPanelDestinationHash = new Dictionary>(); + + foreach (SettingsStub toolStripSettings in toolStripSettingsToApply) { + object destinationPanel = !string.IsNullOrEmpty(toolStripSettings.ToolStripPanelName) ? toolStripSettings.ToolStripPanelName : null; + + if (destinationPanel == null) { + // Not in a panel. + if (!string.IsNullOrEmpty(toolStripSettings.Name)) { + // apply the toolstrip settings. + ToolStrip toolStrip = ToolStripManager.FindToolStrip(form, toolStripSettings.Name); + ApplyToolStripSettings(toolStrip, toolStripSettings, itemLocationHash); + } + } + else { + // This toolStrip is in a ToolStripPanel. We will process it below. + if (!toolStripPanelDestinationHash.ContainsKey(destinationPanel)) { + toolStripPanelDestinationHash[destinationPanel] = new List(); + } + + toolStripPanelDestinationHash[destinationPanel].Add(toolStripSettings); + } + } + + // build up a list of the toolstrippanels to party on + ArrayList toolStripPanels = FindToolStripPanels(true, form.Controls); + + foreach (ToolStripPanel toolStripPanel in toolStripPanels) { + // set all the controls to visible false/ + foreach (Control c in toolStripPanel.Controls) { + c.Visible = false; + } + + string toolStripPanelName = toolStripPanel.Name; + + // Handle the ToolStripPanels inside a ToolStripContainer + if (String.IsNullOrEmpty(toolStripPanelName) && toolStripPanel.Parent is ToolStripContainer && !String.IsNullOrEmpty(toolStripPanel.Parent.Name)) { + toolStripPanelName = toolStripPanel.Parent.Name + "." + toolStripPanel.Dock.ToString(); + } + toolStripPanel.BeginInit(); + // get the associated toolstrips for this panel + if (toolStripPanelDestinationHash.ContainsKey(toolStripPanelName)) { + List stubSettings = toolStripPanelDestinationHash[toolStripPanelName]; + + if (stubSettings != null) { + foreach (SettingsStub settings in stubSettings) { + if (!string.IsNullOrEmpty(settings.Name)) { + // apply the toolstrip settings. + ToolStrip toolStrip = ToolStripManager.FindToolStrip(form, settings.Name); + ApplyToolStripSettings(toolStrip, settings, itemLocationHash); + toolStripPanel.Join(toolStrip, settings.Location); + } + } + } + } + toolStripPanel.EndInit(); + } + + ResumeAllLayout(form, true); + } + + private void ApplyToolStripSettings(ToolStrip toolStrip, SettingsStub settings, Dictionary itemLocationHash) { + if (toolStrip != null) { + toolStrip.Visible = settings.Visible; + toolStrip.Size = settings.Size; + + // Apply the item order changes. + string itemNames = settings.ItemOrder; + if (!string.IsNullOrEmpty(itemNames)) { + string[] keys = itemNames.Split(','); + Regex r = new Regex("(\\S+)"); + + // Shuffle items according to string. + for (int i = 0; ((i < toolStrip.Items.Count) && (i < keys.Length)); i++) { + Match match = r.Match(keys[i]); + if (match != null && match.Success) { + string key = match.Value; + if (!string.IsNullOrEmpty(key) && itemLocationHash.ContainsKey(key)) { + toolStrip.Items.Insert(i, itemLocationHash[key].Items[key]); + } + } + } + } + } + } + + private Dictionary BuildItemOriginationHash() { + ArrayList toolStrips = FindToolStrips(true, form.Controls); + Dictionary itemLocationHash = new Dictionary(); + + if (toolStrips != null) { + foreach (ToolStrip toolStrip in toolStrips) { + foreach (ToolStripItem item in toolStrip.Items) { + if (!string.IsNullOrEmpty(item.Name)) { + Debug.Assert(!itemLocationHash.ContainsKey(item.Name), "WARNING: ToolStripItem name not unique."); + itemLocationHash[item.Name] = toolStrip; + } + } + } + } + + return itemLocationHash; + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private ArrayList FindControls(Type baseType, bool searchAllChildren, Control.ControlCollection controlsToLookIn, ArrayList foundControls) { + if ((controlsToLookIn == null) || (foundControls == null)) { + return null; + } + + try { + // Perform breadth first search - as it's likely people will want controls belonging + // to the same parent close to each other. + + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null) { + continue; + } + + if (baseType.IsAssignableFrom(controlsToLookIn[i].GetType())) { + foundControls.Add(controlsToLookIn[i]); + } + } + + // Optional recurive search for controls in child collections. + + if (searchAllChildren) { + for (int i = 0; i < controlsToLookIn.Count; i++) { + if (controlsToLookIn[i] == null || controlsToLookIn[i] is Form) { + continue; + } + if ((controlsToLookIn[i].Controls != null) && controlsToLookIn[i].Controls.Count > 0) { + // if it has a valid child collecion, append those results to our collection + foundControls = FindControls(baseType, searchAllChildren, controlsToLookIn[i].Controls, foundControls); + } + } + } + } + catch (Exception e) { + if (ClientUtils.IsCriticalException(e)) { + throw; + } + } + return foundControls; + } + + private ArrayList FindToolStripPanels(bool searchAllChildren, Control.ControlCollection controlsToLookIn) { + return FindControls(typeof(ToolStripPanel), true, form.Controls, new ArrayList()); + } + + private ArrayList FindToolStrips(bool searchAllChildren, Control.ControlCollection controlsToLookIn) { + return FindControls(typeof(ToolStrip), true, form.Controls, new ArrayList()); + } + + private string GetSettingsKey(ToolStrip toolStrip) { + if (toolStrip != null) { + return formKey + "." + toolStrip.Name; + } + + return String.Empty; + } + + private void ResumeAllLayout(Control start, bool performLayout) { + Control.ControlCollection controlsCollection = start.Controls; + + for (int i = 0; i < controlsCollection.Count; i++) { + ResumeAllLayout(controlsCollection[i], performLayout); + } + + start.ResumeLayout(performLayout); + } + + private void SuspendAllLayout(Control start) { + start.SuspendLayout(); + + Control.ControlCollection controlsCollection = start.Controls; + for (int i = 0; i < controlsCollection.Count; i++) { + SuspendAllLayout(controlsCollection[i]); + } + } + + /// + /// Light weight structure that captures the properties we want to save as settings. + /// + private struct SettingsStub { + public bool Visible; + public string ToolStripPanelName; + public Point Location; + public Size Size; + public string ItemOrder; + public string Name; + + public SettingsStub(ToolStrip toolStrip) { + this.ToolStripPanelName = String.Empty; + ToolStripPanel parentPanel = toolStrip.Parent as ToolStripPanel; + + if (parentPanel != null) { + if (!String.IsNullOrEmpty(parentPanel.Name)) { + this.ToolStripPanelName = parentPanel.Name; + } + else if (parentPanel.Parent is ToolStripContainer && !String.IsNullOrEmpty(parentPanel.Parent.Name)) { + // Handle the case when the ToolStripPanel belongs to a ToolStripContainer. + this.ToolStripPanelName = parentPanel.Parent.Name + "." + parentPanel.Dock.ToString(); + } + + Debug.Assert(!String.IsNullOrEmpty(this.ToolStripPanelName), "ToolStrip was parented to a panel, but we couldn't figure out its name."); + } + + this.Visible = toolStrip.Visible; + this.Size = toolStrip.Size; + this.Location = toolStrip.Location; + this.Name = toolStrip.Name; + this.ItemOrder = GetItemOrder(toolStrip); + + } + public SettingsStub(ToolStripSettings toolStripSettings) { + this.ToolStripPanelName = toolStripSettings.ToolStripPanelName; + this.Visible = toolStripSettings.Visible; + this.Size = toolStripSettings.Size; + this.Location = toolStripSettings.Location; + this.Name = toolStripSettings.Name; + this.ItemOrder = toolStripSettings.ItemOrder; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSplitButton.cs b/WindowsForms/Managed/System/WinForms/ToolStripSplitButton.cs new file mode 100644 index 000000000..38d2f6d78 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSplitButton.cs @@ -0,0 +1,926 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.Security; + using System.Security.Permissions; + using System.Drawing; + using System.Windows.Forms; + using System.Drawing.Imaging; + using System.ComponentModel; + using System.Windows.Forms.Design; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Diagnostics; + using System.Windows.Forms.Layout; + using System.Runtime.Versioning; + + /// + /// + [ + ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip), + DefaultEvent("ButtonClick") + ] + public class ToolStripSplitButton : ToolStripDropDownItem { + + private ToolStripItem defaultItem = null; + private ToolStripSplitButtonButton splitButtonButton = null; + private Rectangle dropDownButtonBounds = Rectangle.Empty; + private ToolStripSplitButtonButtonLayout splitButtonButtonLayout = null; + private int dropDownButtonWidth = 0; + private int splitterWidth = 1; + private Rectangle splitterBounds = Rectangle.Empty; + private byte openMouseId = 0; + private long lastClickTime = 0; + + private const int DEFAULT_DROPDOWN_WIDTH = 11; + + private static readonly object EventDefaultItemChanged = new object(); + private static readonly object EventButtonClick = new object(); + private static readonly object EventButtonDoubleClick = new object(); + private static readonly object EventDropDownOpened = new object(); + private static readonly object EventDropDownClosed = new object(); + + private static bool isScalingInitialized = false; + private static int scaledDropDownButtonWidth = DEFAULT_DROPDOWN_WIDTH; + + /// + /// + /// Summary of ToolStripSplitButton. + /// + public ToolStripSplitButton() { + Initialize(); // all additional work should be done in Initialize + } + public ToolStripSplitButton(string text):base(text,null,(EventHandler)null) { + Initialize(); + } + public ToolStripSplitButton(Image image):base(null,image,(EventHandler)null) { + Initialize(); + } + public ToolStripSplitButton(string text, Image image):base(text,image,(EventHandler)null) { + Initialize(); + } + public ToolStripSplitButton(string text, Image image, EventHandler onClick):base(text,image,onClick) { + Initialize(); + } + public ToolStripSplitButton(string text, Image image, EventHandler onClick, string name) :base(text,image,onClick,name){ + Initialize(); + } + public ToolStripSplitButton(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) { + Initialize(); + } + + [DefaultValue(true)] + public new bool AutoToolTip { + get { + return base.AutoToolTip; + } + set { + base.AutoToolTip = value; + } + } + + + /// + /// + /// Summary of ToolStripSplitButton. + /// + [Browsable(false)] + public Rectangle ButtonBounds { + get { + //Rectangle bounds = SplitButtonButton.Bounds; + //bounds.Offset(this.Bounds.Location); + return SplitButtonButton.Bounds; + } + } + + /// + /// + /// Summary of ButtonPressed. + /// + [Browsable(false)] + public bool ButtonPressed { + get { + return SplitButtonButton.Pressed; + + } + } + + /// + /// + /// Summary of ButtonPressed. + /// + [Browsable(false)] + public bool ButtonSelected { + get { + return SplitButtonButton.Selected || DropDownButtonPressed; + } + } + + /// + /// + /// Occurs when the button portion of a split button is clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripSplitButtonOnButtonClickDescr) + ] + public event EventHandler ButtonClick { + add { + Events.AddHandler(EventButtonClick, value); + } + remove { + Events.RemoveHandler(EventButtonClick, value); + } + } + /// + /// + /// Occurs when the utton portion of a split button is double clicked. + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripSplitButtonOnButtonDoubleClickDescr) + ] + public event EventHandler ButtonDoubleClick { + add { + Events.AddHandler(EventButtonDoubleClick, value); + } + remove { + Events.RemoveHandler(EventButtonDoubleClick, value); + } + } + + + protected override bool DefaultAutoToolTip { + get { + return true; + } + } + + /// + /// + /// Summary of DefaultItem. + /// + [DefaultValue(null), Browsable(false)] + public ToolStripItem DefaultItem { + get { + return defaultItem; + } + set { + if (defaultItem != value) { + OnDefaultItemChanged(new EventArgs()); + defaultItem = value; + } + } + } + + + /// + /// + /// Occurs when the default item has changed + /// + [ + SRCategory(SR.CatAction), + SRDescription(SR.ToolStripSplitButtonOnDefaultItemChangedDescr) + ] + public event EventHandler DefaultItemChanged { + add { + Events.AddHandler(EventDefaultItemChanged, value); + } + remove { + Events.RemoveHandler(EventDefaultItemChanged, value); + } + } + + /// + /// + /// specifies the default behavior of these items on ToolStripDropDowns when clicked. + /// + internal protected override bool DismissWhenClicked { + get { + return DropDown.Visible != true; + } + + } + + internal override Rectangle DropDownButtonArea { + get { return this.DropDownButtonBounds; } + } + + /// + /// + /// The bounds of the DropDown in ToolStrip coordinates. + /// + [Browsable(false)] + public Rectangle DropDownButtonBounds { + get { + return dropDownButtonBounds; + } + + } + /// + /// + /// Summary of DropDownButtonBounds. + /// + [Browsable(false)] + public bool DropDownButtonPressed { + get { + // + return DropDown.Visible; + } + } + /// + /// + /// Summary of DropDownButtonSelected. + /// + [Browsable(false)] + public bool DropDownButtonSelected{ + get { + return this.Selected; + } + } + /// + /// + /// Summary of DropDownButtonWidth. + /// + [ + SRCategory(SR.CatLayout), + SRDescription(SR.ToolStripSplitButtonDropDownButtonWidthDescr) + ] + public int DropDownButtonWidth { + get{ + return dropDownButtonWidth; + } + set { + if (value < 0) { + // throw if less than 0. + throw new ArgumentOutOfRangeException("DropDownButtonWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "DropDownButtonWidth", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (dropDownButtonWidth != value) { + dropDownButtonWidth = value; + InvalidateSplitButtonLayout(); + InvalidateItemLayout(PropertyNames.DropDownButtonWidth, true); + } + } + } + + /// + /// This is here for serialization purposes. + /// + private int DefaultDropDownButtonWidth { + get { + // lets start off with a size roughly equivalent to a combobox dropdown + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + scaledDropDownButtonWidth = DpiHelper.LogicalToDeviceUnitsX(DEFAULT_DROPDOWN_WIDTH); + } + isScalingInitialized = true; + } + + return scaledDropDownButtonWidth; + } + } + + + + /// + /// Just used as a convenience to help manage layout + /// + private ToolStripSplitButtonButton SplitButtonButton { + get { + if (splitButtonButton == null) { + splitButtonButton = new ToolStripSplitButtonButton(this); + } + splitButtonButton.Image = this.Image; + splitButtonButton.Text = this.Text; + splitButtonButton.BackColor = this.BackColor; + splitButtonButton.ForeColor = this.ForeColor; + splitButtonButton.Font = this.Font; + splitButtonButton.ImageAlign = this.ImageAlign; + splitButtonButton.TextAlign = this.TextAlign; + splitButtonButton.TextImageRelation = this.TextImageRelation; + return splitButtonButton; + } + } + /// + /// Summary of SplitButtonButtonLayout. + /// + internal ToolStripItemInternalLayout SplitButtonButtonLayout { + get { + // For preferred size caching reasons, we need to keep our two + // internal layouts (button, dropdown button) in sync. + + if (InternalLayout != null /*if layout is invalid - calls CreateInternalLayout - which resets splitButtonButtonLayout to null*/ + && splitButtonButtonLayout == null) { + splitButtonButtonLayout = new ToolStripSplitButtonButtonLayout(this); + } + return splitButtonButtonLayout; + } + } + + /// + /// + /// the width of the separator between the default and drop down button + /// + [ + SRDescription(SR.ToolStripSplitButtonSplitterWidthDescr), + SRCategory(SR.CatLayout), + Browsable(false), + EditorBrowsable(EditorBrowsableState.Advanced) + ] + internal int SplitterWidth { + get { + return splitterWidth; + } + set { + if (value < 0) { + splitterWidth = 0; + } + else { + splitterWidth = value; + } + InvalidateSplitButtonLayout(); + } + } + /// + /// + /// the boundaries of the separator between the default and drop down button, exposed for custom + /// painting purposes. + /// + [Browsable(false)] + public Rectangle SplitterBounds { + get { + return splitterBounds; + } + } + /// + /// Summary of CalculateLayout. + /// + private void CalculateLayout() { + + // Figure out where the DropDown image goes. + Rectangle dropDownButtonBounds = new Rectangle(Point.Empty, this.Size); + Rectangle splitButtonButtonBounds = Rectangle.Empty; + + + dropDownButtonBounds = new Rectangle(Point.Empty, new Size(Math.Min(this.Width, DropDownButtonWidth), this.Height)); + + // Figure out the height and width of the selected item. + int splitButtonButtonWidth = Math.Max(0, this.Width - dropDownButtonBounds.Width); + int splitButtonButtonHeight = Math.Max(0, this.Height); + + splitButtonButtonBounds = new Rectangle(Point.Empty, new Size(splitButtonButtonWidth, splitButtonButtonHeight)); + + // grow the selected item by one since we're overlapping the borders. + splitButtonButtonBounds.Width -= splitterWidth; + + if (this.RightToLeft == RightToLeft.No) { + // the dropdown button goes on the right + dropDownButtonBounds.Offset(splitButtonButtonBounds.Right+splitterWidth, 0); + splitterBounds = new Rectangle(splitButtonButtonBounds.Right, splitButtonButtonBounds.Top, splitterWidth, splitButtonButtonBounds.Height); + } + else { + // the split button goes on the right. + splitButtonButtonBounds.Offset(DropDownButtonWidth+splitterWidth, 0); + splitterBounds = new Rectangle(dropDownButtonBounds.Right, dropDownButtonBounds.Top, splitterWidth, dropDownButtonBounds.Height); + + } + + this.SplitButtonButton.SetBounds(splitButtonButtonBounds); + this.SetDropDownButtonBounds(dropDownButtonBounds); + + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripSplitButtonUiaProvider(this); + } + else if (AccessibilityImprovements.Level1) { + return new ToolStripSplitButtonExAccessibleObject(this); + } + else { + return new ToolStripSplitButtonAccessibleObject(this); + } + } + + protected override ToolStripDropDown CreateDefaultDropDown() { + // AutoGenerate a Winbar DropDown - set the property so we hook events + return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true); + } + + + internal override ToolStripItemInternalLayout CreateInternalLayout() { + // whenever the master layout is invalidated - invalidate the splitbuttonbutton layout. + this.splitButtonButtonLayout = null; + return new ToolStripItemInternalLayout(this); + + } + + /// + public override Size GetPreferredSize(Size constrainingSize) { + Size preferredSize = SplitButtonButtonLayout.GetPreferredSize(constrainingSize); + preferredSize.Width += DropDownButtonWidth + SplitterWidth + Padding.Horizontal; + return preferredSize; + } + + /// + /// Summary of InvalidateSplitButtonLayout. + /// + private void InvalidateSplitButtonLayout() { + this.splitButtonButtonLayout = null; + CalculateLayout(); + } + + private void Initialize() { + dropDownButtonWidth = DefaultDropDownButtonWidth; + SupportsSpaceKey = true; + } + + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessDialogKey(Keys keyData) { + if (Enabled && (keyData == Keys.Enter || (SupportsSpaceKey && keyData == Keys.Space))) { + PerformButtonClick(); + return true; + } + + return base.ProcessDialogKey(keyData); + } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + // checking IsMnemonic is not necessary - toolstrip does this for us + PerformButtonClick(); + return true; + } + + /// + /// + /// called when the button portion of a split button is clicked + /// if there is a default item, this will route the click to the default item + /// + protected virtual void OnButtonClick(System.EventArgs e) { + + if (DefaultItem != null) { + DefaultItem.FireEvent(ToolStripItemEventType.Click); + } + + EventHandler handler = (EventHandler)Events[EventButtonClick]; + if (handler != null) handler(this, e); + + } + + /// + /// + /// called when the button portion of a split button is double clicked + /// if there is a default item, this will route the double click to the default item + /// + public virtual void OnButtonDoubleClick(System.EventArgs e) { + if (DefaultItem != null) { + DefaultItem.FireEvent(ToolStripItemEventType.DoubleClick); + } + + EventHandler handler = (EventHandler)Events[EventButtonDoubleClick]; + if (handler != null) handler(this,e); + } + + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected virtual void OnDefaultItemChanged(EventArgs e) { + InvalidateSplitButtonLayout(); + if (CanRaiseEvents) { + EventHandler eh = Events[EventDefaultItemChanged] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + } + + /// + /// + /// Summary of OnMouseDown. + /// + protected override void OnMouseDown(MouseEventArgs e) { + + if (DropDownButtonBounds.Contains(e.Location)) { + if (e.Button == MouseButtons.Left) { + + if (!DropDown.Visible) { + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + this.ShowDropDown(/*mousePress = */true); + } + } + } + else { + SplitButtonButton.Push(true); + } + + } + + + /// + /// + /// Summary of OnMouseUp. + /// + protected override void OnMouseUp(MouseEventArgs e) { + if (!Enabled) { + return; + } + + + SplitButtonButton.Push(false); + + if (DropDownButtonBounds.Contains(e.Location)) { + if (e.Button == MouseButtons.Left) { + if (DropDown.Visible) { + Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); + byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); + if (closeMouseId != openMouseId) { + openMouseId = 0; // reset the mouse id, we should never get this value from toolstrip. + ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); + Select(); + } + } + } + } + Point clickPoint = new Point(e.X, e.Y); + if ((e.Button == MouseButtons.Left) && this.SplitButtonButton.Bounds.Contains(clickPoint)) { + bool shouldFireDoubleClick = false; + if (DoubleClickEnabled) { + long newTime = DateTime.Now.Ticks; + long deltaTicks = newTime - lastClickTime; + lastClickTime = newTime; + // use >= for cases where the succession of click events is so fast it's not picked up by + // DateTime resolution. + Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking"); + // if we've seen a mouse up less than the double click time ago, we should fire. + if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) { + shouldFireDoubleClick = true; + } + } + if (shouldFireDoubleClick) { + OnButtonDoubleClick(new System.EventArgs()); + // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime. + lastClickTime = 0; + } + else { + OnButtonClick(new System.EventArgs()); + } + } + + } + /// + protected override void OnMouseLeave(EventArgs e) { + openMouseId = 0; // reset the mouse id, we should never get this value from toolstrip. + SplitButtonButton.Push(false); + base.OnMouseLeave(e); + } + + /// + /// + /// Summary of OnRightToLeftChanged. + /// + protected override void OnRightToLeftChanged(EventArgs e) { + base.OnRightToLeftChanged(e); + InvalidateSplitButtonLayout(); + } + /// + /// + /// Summary of OnPaint. + /// + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + ToolStripRenderer renderer = this.Renderer; + if (renderer != null) { + InvalidateSplitButtonLayout(); + Graphics g = e.Graphics; + + renderer.DrawSplitButton(new ToolStripItemRenderEventArgs(g, this)); + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.None) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, SplitButtonButtonLayout.ImageRectangle)); + } + + if ((DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.None) { + renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, SplitButtonButton.Text, SplitButtonButtonLayout.TextRectangle, this.ForeColor, this.Font, SplitButtonButtonLayout.TextFormat)); + } + } + } + + public void PerformButtonClick() { + if (Enabled && Available) { + PerformClick(); + OnButtonClick(EventArgs.Empty); + } + } + + /// + /// + /// Resets the RightToLeft to be the default. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void ResetDropDownButtonWidth() { + DropDownButtonWidth = DefaultDropDownButtonWidth; + } + + /// + /// Summary of SetDropDownBounds. + /// + private void SetDropDownButtonBounds(Rectangle rect) { + dropDownButtonBounds = rect; + } + /// + /// Determines if the property needs to be persisted. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual bool ShouldSerializeDropDownButtonWidth() { + return (DropDownButtonWidth != DefaultDropDownButtonWidth); + } + + /// + /// This class represents the item to the left of the dropdown [ A |v] (e.g the "A") + /// It exists so that we can use our existing methods for text and image layout + /// and have a place to stick certain state information like pushed and selected + /// Note since this is NOT an actual item hosted on the Winbar - it wont get things + /// like MouseOver, wont be laid out by the ToolStrip, etc etc. This is purely internal + /// convenience. + /// + private class ToolStripSplitButtonButton : ToolStripButton { + + private ToolStripSplitButton owner = null; + + public ToolStripSplitButtonButton(ToolStripSplitButton owner) { + this.owner = owner; + } + + public override bool Enabled { + get { + return owner.Enabled; + } + set { + // do nothing + } + } + + + public override ToolStripItemDisplayStyle DisplayStyle { + get { + return owner.DisplayStyle; + } + set { + // do nothing + } + } + + public override Padding Padding { + get { + return this.owner.Padding; + } + set { + // do nothing + } + } + + + public override ToolStripTextDirection TextDirection { + get { + return owner.TextDirection; + } + } + + + public override Image Image { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + get { + if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { + return owner.Image; + } + else { + return null; + } + } + set { + // do nothing + } + } + + public override bool Selected { + get { + + if (owner != null) { + return owner.Selected; + } + return base.Selected; + } + } + + public override string Text { + get { + if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) { + return owner.Text; + } + else { + return null; + } + } + set { + // do nothing + } + } + + } + + /// + /// This class performs internal layout for the "split button button" portion of a split button. + /// Its main job is to make sure the inner button has the same parent as the split button, so + /// that layout can be performed using the correct graphics context. + /// + private class ToolStripSplitButtonButtonLayout : ToolStripItemInternalLayout { + + ToolStripSplitButton owner; + + public ToolStripSplitButtonButtonLayout(ToolStripSplitButton owner) : base(owner.SplitButtonButton) { + this.owner = owner; + } + + protected override ToolStripItem Owner { + get { return owner; } + } + + protected override ToolStrip ParentInternal { + get { + return owner.ParentInternal; + } + } + public override Rectangle ImageRectangle { + get { + Rectangle imageRect = base.ImageRectangle; + // translate to ToolStripItem coordinates + imageRect.Offset(owner.SplitButtonButton.Bounds.Location); + return imageRect; + } + } + + public override Rectangle TextRectangle { + get { + Rectangle textRect = base.TextRectangle; + // translate to ToolStripItem coordinates + textRect.Offset(owner.SplitButtonButton.Bounds.Location); + return textRect; + } + } + } + + /// + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] + public class ToolStripSplitButtonAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject { + private ToolStripSplitButton owner; + + public ToolStripSplitButtonAccessibleObject(ToolStripSplitButton item) : base(item) { + owner = item; + } + + /// + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public override void DoDefaultAction() { + owner.PerformButtonClick(); + } + } + + /// + internal class ToolStripSplitButtonExAccessibleObject: ToolStripSplitButtonAccessibleObject { + + private ToolStripSplitButton ownerItem; + + public ToolStripSplitButtonExAccessibleObject(ToolStripSplitButton item) + : base(item) { + ownerItem = item; + } + + internal override object GetPropertyValue(int propertyID) { + if (propertyID == NativeMethods.UIA_ControlTypePropertyId) { + return NativeMethods.UIA_ButtonControlTypeId; + } + else { + return base.GetPropertyValue(propertyID); + } + } + + internal override bool IsIAccessibleExSupported() { + if (ownerItem != null) { + return true; + } + else { + return base.IsIAccessibleExSupported(); + } + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ExpandCollapsePatternId && ownerItem.HasDropDownItems) { + return true; + } + else { + return base.IsPatternSupported(patternId); + } + } + + internal override void Expand() { + DoDefaultAction(); + } + + internal override void Collapse() { + if (ownerItem != null && ownerItem.DropDown != null && ownerItem.DropDown.Visible) { + ownerItem.DropDown.Close(); + } + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return ownerItem.DropDown.Visible ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return DropDownItemsCount > 0 ? ownerItem.DropDown.Items[0].AccessibilityObject : null; + case UnsafeNativeMethods.NavigateDirection.LastChild: + return DropDownItemsCount > 0 ? ownerItem.DropDown.Items[ownerItem.DropDown.Items.Count - 1].AccessibilityObject : null; + } + return base.FragmentNavigate(direction); + } + + private int DropDownItemsCount { + get { + // Do not expose child items when the drop-down is collapsed to prevent Narrator from announcing + // invisible menu items when Narrator is in item's mode (CAPSLOCK + Arrow Left/Right) or + // in scan mode (CAPSLOCK + Space) + if (AccessibilityImprovements.Level3 && ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) { + return 0; + } + + return ownerItem.DropDownItems.Count; + } + } + } + + internal class ToolStripSplitButtonUiaProvider : ToolStripDropDownItemAccessibleObject { + private ToolStripSplitButton _owner; + private ToolStripSplitButtonExAccessibleObject _accessibleObject; + + public ToolStripSplitButtonUiaProvider(ToolStripSplitButton owner) : base(owner) { + _owner = owner; + _accessibleObject = new ToolStripSplitButtonExAccessibleObject(owner); + } + + public override void DoDefaultAction() { + _accessibleObject.DoDefaultAction(); + } + + internal override object GetPropertyValue(int propertyID) { + return _accessibleObject.GetPropertyValue(propertyID); + } + + internal override bool IsIAccessibleExSupported() { + return true; + } + + internal override bool IsPatternSupported(int patternId) { + return _accessibleObject.IsPatternSupported(patternId); + } + + internal override void Expand() { + DoDefaultAction(); + } + + internal override void Collapse() { + _accessibleObject.Collapse(); + } + + internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState { + get { + return _accessibleObject.ExpandCollapseState; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + return _accessibleObject.FragmentNavigate(direction); + } + } + } +} + + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSplitStackLayout.cs b/WindowsForms/Managed/System/WinForms/ToolStripSplitStackLayout.cs new file mode 100644 index 000000000..052b2bb70 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSplitStackLayout.cs @@ -0,0 +1,645 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + using System; + using System.Windows.Forms.Layout; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + + internal class ToolStripSplitStackLayout : LayoutEngine { + private int backwardsWalkingIndex; + private int forwardsWalkingIndex; + private ToolStrip toolStrip; + private int overflowSpace; + private bool overflowRequired; + private Point noMansLand; + private Rectangle displayRectangle = Rectangle.Empty; + +#if DEBUG + internal static readonly TraceSwitch DebugLayoutTraceSwitch = new TraceSwitch("DebugLayout", "Debug ToolStrip Layout code"); +#else + internal static readonly TraceSwitch DebugLayoutTraceSwitch; +#endif + internal ToolStripSplitStackLayout(ToolStrip owner) { + this.toolStrip = owner; + } + + // this is the index we use to send items to the overflow + // if we run out of room + protected int BackwardsWalkingIndex { + get { + return backwardsWalkingIndex; + } + set { + backwardsWalkingIndex = value; + } + } + + // this is the index we use to walk the items and make + // decisions if there is enough room. + protected int ForwardsWalkingIndex { + get { + return forwardsWalkingIndex; + } + set { + forwardsWalkingIndex = value; + } + } + + private Size OverflowButtonSize { + get { + ToolStrip toolStrip = this.ToolStrip; + if (!toolStrip.CanOverflow) { + return Size.Empty; + } + + // since we havent parented the item yet - the auto size wont have reset the size yet. + Size overflowButtonSize = toolStrip.OverflowButton.AutoSize ? toolStrip.OverflowButton.GetPreferredSize(displayRectangle.Size) : toolStrip.OverflowButton.Size; + + return overflowButtonSize + toolStrip.OverflowButton.Margin.Size; + } + } + + private int OverflowSpace { + get { + return overflowSpace; + } + set { + overflowSpace = value; + } + } + + private bool OverflowRequired { + get { + return overflowRequired; + } + set { + overflowRequired = value; + } + } + + // the current winbar we're operating over. + public ToolStrip ToolStrip { + get { + return toolStrip; + } + } + + // + // This method will mark whether items should be placed in the overflow or on the main winbar. + // + private void CalculatePlacementsHorizontal() { + ResetItemPlacements(); + + ToolStrip toolStrip = this.ToolStrip; + int currentWidth = 0; //toolStrip.Padding.Horizontal; + + if (ToolStrip.CanOverflow) { + // determine the locations of all the items. + for (ForwardsWalkingIndex = 0; ForwardsWalkingIndex < toolStrip.Items.Count; ForwardsWalkingIndex++) { + ToolStripItem item = toolStrip.Items[ForwardsWalkingIndex]; + + if (!((IArrangedElement)item).ParticipatesInLayout) { + // skip over items not participating in layout. E.G. not visible items + continue; + } + + // if we have something set to overflow always we need to show an overflow button + if (item.Overflow == ToolStripItemOverflow.Always) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "OverflowRequired - item set to alaways overflow: {0} ", item)); } +#endif + OverflowRequired = true; + } + + if (item.Overflow != ToolStripItemOverflow.Always && item.Placement == ToolStripItemPlacement.None) { + // since we havent parented the item yet - the auto size wont have reset the size yet. + Size itemSize = item.AutoSize ? item.GetPreferredSize(this.displayRectangle.Size) : item.Size; + + currentWidth += itemSize.Width + item.Margin.Horizontal; + + int overflowWidth = (OverflowRequired) ? OverflowButtonSize.Width : 0; + + if (currentWidth > displayRectangle.Width - overflowWidth) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine("SendNextItemToOverflow to fres space for " + item.ToString()); } +#endif + int spaceRecovered = SendNextItemToOverflow((currentWidth + overflowWidth) - displayRectangle.Width, true); + + currentWidth -= spaceRecovered; + } + } + } + } + + PlaceItems(); + } + + // + // This method will mark whether items should be placed in the overflow or on the main winbar. + // + private void CalculatePlacementsVertical() { + ResetItemPlacements(); + + ToolStrip toolStrip = this.ToolStrip; + int currentHeight = 0; //toolStrip.Padding.Vertical; + + if (ToolStrip.CanOverflow) { + // determine the locations of all the items. + for (ForwardsWalkingIndex = 0; ForwardsWalkingIndex < ToolStrip.Items.Count; ForwardsWalkingIndex++) { + ToolStripItem item = toolStrip.Items[ForwardsWalkingIndex]; + + if (!((IArrangedElement)item).ParticipatesInLayout) { + // skip over items not participating in layout. E.G. not visible items + continue; + } + + // if we have something set to overflow always we need to show an overflow button + if (item.Overflow == ToolStripItemOverflow.Always) { + OverflowRequired = true; +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "OverflowRequired - item set to always overflow: {0} ", item)); +#endif + } + + if (item.Overflow != ToolStripItemOverflow.Always && item.Placement == ToolStripItemPlacement.None) { + // since we havent parented the item yet - the auto size wont have reset the size yet. + Size itemSize = item.AutoSize ? item.GetPreferredSize(displayRectangle.Size) : item.Size; + int overflowWidth = (OverflowRequired) ? OverflowButtonSize.Height : 0; + + currentHeight += itemSize.Height + item.Margin.Vertical; +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Adding {0} Size {1} to currentHeight = {2}", item.ToString(), itemSize, currentHeight)); } +#endif + if (currentHeight > displayRectangle.Height - overflowWidth) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Got to {0} and realized that currentHeight = {1} is larger than displayRect {2} minus overflow {3}", item.ToString(), currentHeight, displayRectangle, overflowWidth)); } +#endif + int spaceRecovered = SendNextItemToOverflow(currentHeight - displayRectangle.Height, false); + + currentHeight -= spaceRecovered; + } + } + } + } + + PlaceItems(); + } + + internal override Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) { + // undone be more clever here - perhaps figure out the biggest element and return that. + if (!(container is ToolStrip)) { + throw new NotSupportedException(SR.GetString(SR.ToolStripSplitStackLayoutContainerMustBeAToolStrip)); + } + + if (toolStrip.LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow) { + return ToolStrip.GetPreferredSizeHorizontal(container, proposedConstraints); + } + else { + return ToolStrip.GetPreferredSizeVertical(container, proposedConstraints); + } + } + + + // each time we perform a complete layout this stuff should be re-initialized + private void InvalidateLayout() { + forwardsWalkingIndex = 0; + backwardsWalkingIndex = -1; + overflowSpace = 0; + overflowRequired = false; + displayRectangle = Rectangle.Empty; + } + + + + // this is part of the LayoutEngine - called when we need to do a layout. + internal override bool LayoutCore(IArrangedElement container, LayoutEventArgs layoutEventArgs) { + if (!(container is ToolStrip)) { + throw new NotSupportedException(SR.GetString(SR.ToolStripSplitStackLayoutContainerMustBeAToolStrip)); + } + + InvalidateLayout(); + this.displayRectangle = toolStrip.DisplayRectangle; + + // pick a location that's outside of the displayed region to send + // items that will potentially clobber/overlay others. + noMansLand = displayRectangle.Location; + noMansLand.X += toolStrip.ClientSize.Width + 1; + noMansLand.Y += toolStrip.ClientSize.Height + 1; + + if (toolStrip.LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow) { + LayoutHorizontal(); + } + else { + LayoutVertical(); + } + + return CommonProperties.GetAutoSize(container); + } + + private bool LayoutHorizontal() { + ToolStrip toolStrip = ToolStrip; + Rectangle clientRectangle = toolStrip.ClientRectangle; +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine("_________________________\r\nHorizontal Layout:" + toolStrip.ToString() + displayRectangle.ToString()); } +#endif + int lastRight = displayRectangle.Right;// - toolStrip.Padding.Right; + int lastLeft = displayRectangle.Left;// + toolStrip.Padding.Left; + bool needsMoreSpace = false; + Size itemSize = Size.Empty; + Rectangle alignedLeftItems = Rectangle.Empty; + Rectangle alignedRightItems = Rectangle.Empty; + + + // this will determine where the item should be placed. + CalculatePlacementsHorizontal(); + + bool needOverflow = toolStrip.CanOverflow && ((OverflowRequired) || (OverflowSpace >= OverflowButtonSize.Width)); + toolStrip.OverflowButton.Visible = needOverflow; + + // if we require the overflow, it should stick up against the edge of the toolstrip. + if (needOverflow) { + if (toolStrip.RightToLeft == RightToLeft.No) { + lastRight = clientRectangle.Right; + } + else { + lastLeft = clientRectangle.Left; + } + } + + for (int j = -1; j < toolStrip.Items.Count; j++) { + ToolStripItem item = null; + + if (j == -1) { + // the first time through place the overflow button if its required. + if (needOverflow) { + item = toolStrip.OverflowButton; + item.SetPlacement(ToolStripItemPlacement.Main); + itemSize = OverflowButtonSize; + } + else { + item = toolStrip.OverflowButton; + item.SetPlacement(ToolStripItemPlacement.None); + continue; + } + } + else { + item = toolStrip.Items[j]; + + if (!((IArrangedElement)item).ParticipatesInLayout) { + // skip over items not participating in layout. E.G. not visible items + continue; + } + + // since we havent parented the item yet - the auto size wont have reset the size yet. + itemSize = item.AutoSize ? item.GetPreferredSize(Size.Empty) : item.Size; + } + + // if it turns out we dont need the overflow (because there are no Overflow.Always items and the width of everything + // in the overflow is less than the width of the overflow button then reset the placement of the as needed items to + // main. + if (!needOverflow && (item.Overflow == ToolStripItemOverflow.AsNeeded && item.Placement == ToolStripItemPlacement.Overflow)) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Resetting {0} to Main - we dont need it to overflow", item)); } +#endif + item.SetPlacement(ToolStripItemPlacement.Main); + } + + + // Now do the guts of setting X, Y and parenting. + // We need to honor left to right and head and tail. + // In RTL.Yes, Head is to the Right, Tail is to the Left + // In RTL.No, Head is to the Left, Tail is to the Right + if ((item != null) && (item.Placement == ToolStripItemPlacement.Main)) { + int x = displayRectangle.Left; + int y = displayRectangle.Top; + Padding itemMargin = item.Margin; + + if (((item.Alignment == ToolStripItemAlignment.Right) && (toolStrip.RightToLeft == RightToLeft.No)) || ((item.Alignment == ToolStripItemAlignment.Left) && (toolStrip.RightToLeft == RightToLeft.Yes))) { + // lastRight x Margin.Right + // [Item]<----------[Item]----------->| + // Margin.Left + // this item should be placed to the right + // we work backwards from the right edge - that is place items from right to left. + + x = lastRight - (itemMargin.Right + itemSize.Width); + y += itemMargin.Top; + lastRight = x - itemMargin.Left; + alignedRightItems = (alignedRightItems == Rectangle.Empty) ? new Rectangle(x,y,itemSize.Width, itemSize.Height) + : Rectangle.Union(alignedRightItems, new Rectangle(x,y,itemSize.Width, itemSize.Height)); + + } + else { + // x Margin.Right lastLeft + // |<----------[Item]------------>| + // Margin.Left + // this item should be placed to the left + // we work forwards from the left - that is place items from left to right + x = lastLeft + itemMargin.Left; + y += itemMargin.Top; + lastLeft = x + itemSize.Width + itemMargin.Right; + alignedLeftItems = (alignedLeftItems == Rectangle.Empty) ? new Rectangle(x,y,itemSize.Width, itemSize.Height) + : Rectangle.Union(alignedLeftItems, new Rectangle(x,y,itemSize.Width, itemSize.Height)); + + } + + item.ParentInternal = ToolStrip; + + Point itemLocation = new Point(x,y); + if (!clientRectangle.Contains(x, y)) { + item.SetPlacement(ToolStripItemPlacement.None); + } + else if (alignedRightItems.Width > 0 && alignedLeftItems.Width > 0 && alignedRightItems.IntersectsWith(alignedLeftItems)) { + itemLocation = noMansLand; + item.SetPlacement(ToolStripItemPlacement.None); + } + + if (item.AutoSize) { + // autosized items stretch from edge-edge + itemSize.Height = Math.Max(displayRectangle.Height - itemMargin.Vertical, 0); + } + else { + // non autosized items are vertically centered + Rectangle bounds = LayoutUtils.VAlign(item.Size, displayRectangle, AnchorStyles.None); + itemLocation.Y = bounds.Y; + } + + SetItemLocation(item, itemLocation, itemSize); + } + else { + item.ParentInternal = (item.Placement == ToolStripItemPlacement.Overflow) ? toolStrip.OverflowButton.DropDown : null; + } +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Item {0} Placement {1} Bounds {2} Parent {3}", item.ToString(), item.Placement.ToString(), item.Bounds.ToString(), (item.ParentInternal == null) ? "null" : item.ParentInternal.ToString())); } +#endif + } + // + return needsMoreSpace; + } + + private bool LayoutVertical() { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine("_________________________\r\nVertical Layout" + displayRectangle.ToString()); } +#endif + ToolStrip toolStrip = ToolStrip; + Rectangle clientRectangle = toolStrip.ClientRectangle; + int lastBottom = displayRectangle.Bottom; + int lastTop = displayRectangle.Top; + bool needsMoreSpace = false; + Size itemSize = Size.Empty; + Rectangle alignedLeftItems = Rectangle.Empty; + Rectangle alignedRightItems = Rectangle.Empty; + + Size toolStripPreferredSize = displayRectangle.Size; + DockStyle dock = toolStrip.Dock; + if (toolStrip.AutoSize && (!toolStrip.IsInToolStripPanel && (dock == DockStyle.Left) || (dock == DockStyle.Right))) { + // if we're autosizing, make sure we pad out items to the preferred width, not the + // width of the display rectangle. + toolStripPreferredSize = ToolStrip.GetPreferredSizeVertical(toolStrip, Size.Empty) - toolStrip.Padding.Size; + } + CalculatePlacementsVertical(); + + bool needOverflow = toolStrip.CanOverflow && ((OverflowRequired) || (OverflowSpace >= OverflowButtonSize.Height)); + + toolStrip.OverflowButton.Visible = needOverflow; + + for (int j = -1; j < ToolStrip.Items.Count; j++) { + ToolStripItem item = null; + + if (j == -1) { + // the first time through place the overflow button if its required. + if (needOverflow) { + item = toolStrip.OverflowButton; + item.SetPlacement(ToolStripItemPlacement.Main); + } + else { + item = toolStrip.OverflowButton; + item.SetPlacement(ToolStripItemPlacement.None); + continue; + } + itemSize = OverflowButtonSize; + + } + else { + item = toolStrip.Items[j]; + + if (!((IArrangedElement)item).ParticipatesInLayout) { + // skip over items not participating in layout. E.G. not visible items + continue; + } + // since we havent parented the item yet - the auto size wont have reset the size yet. + itemSize = item.AutoSize ? item.GetPreferredSize(Size.Empty) : item.Size; + + } + + // if it turns out we dont need the overflow (because there are no Overflow.Always items and the height of everything + // in the overflow is less than the width of the overflow button then reset the placement of the as needed items to + // main. + if (!needOverflow && (item.Overflow == ToolStripItemOverflow.AsNeeded && item.Placement == ToolStripItemPlacement.Overflow)) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Resetting {0} to Main - we dont need it to overflow", item)); } +#endif + item.SetPlacement(ToolStripItemPlacement.Main); + } + + // Now do the guts of setting X, Y and parenting. + // Vertical split stack management ignores left to right. + // Items aligned to the Head are placed from Top to Bottom + // Items aligned to the Tail are placed from Bottom to Top + if ((item != null) && (item.Placement == ToolStripItemPlacement.Main)) { + Padding itemMargin = item.Margin; + int x = displayRectangle.Left + itemMargin.Left; + int y = displayRectangle.Top; + + switch (item.Alignment) { + case ToolStripItemAlignment.Right: + y = lastBottom - (itemMargin.Bottom + itemSize.Height); + lastBottom = y - itemMargin.Top; + alignedRightItems = (alignedRightItems == Rectangle.Empty) ? new Rectangle(x,y,itemSize.Width, itemSize.Height) + : Rectangle.Union(alignedRightItems, new Rectangle(x,y,itemSize.Width, itemSize.Height)); + break; + + case ToolStripItemAlignment.Left: + default: + y = lastTop + itemMargin.Top; + lastTop = y + itemSize.Height + itemMargin.Bottom; + alignedLeftItems = (alignedLeftItems == Rectangle.Empty) ? new Rectangle(x,y,itemSize.Width, itemSize.Height) + : Rectangle.Union(alignedLeftItems, new Rectangle(x,y,itemSize.Width, itemSize.Height)); + break; + } + item.ParentInternal = ToolStrip; + Point itemLocation = new Point(x,y); + + + if (!clientRectangle.Contains(x, y)) { + item.SetPlacement(ToolStripItemPlacement.None); + } + else if (alignedRightItems.Width > 0 && alignedLeftItems.Width > 0 && alignedRightItems.IntersectsWith(alignedLeftItems)) { + itemLocation = noMansLand; + item.SetPlacement(ToolStripItemPlacement.None); + } + + if (item.AutoSize) { + // autosized items stretch from edge-edge + itemSize.Width = Math.Max(toolStripPreferredSize.Width - itemMargin.Horizontal -1, 0); + } + else { + // non autosized items are horizontally centered + Rectangle bounds = LayoutUtils.HAlign(item.Size, displayRectangle, AnchorStyles.None); + itemLocation.X = bounds.X; + } + + SetItemLocation(item, itemLocation, itemSize); + } + else { + item.ParentInternal = (item.Placement == ToolStripItemPlacement.Overflow) ? toolStrip.OverflowButton.DropDown : null; + } +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Item {0} Placement {1} Bounds {2} Parent {3}", item.ToString(), item.Placement.ToString(), item.Bounds.ToString(), (item.ParentInternal == null) ? "null" : item.ParentInternal.ToString())); } +#endif + } + + + // + return needsMoreSpace; + } + + private void SetItemLocation(ToolStripItem item, Point itemLocation, Size itemSize) { + // make sure that things that dont fit within the display rectangle arent laid out. + if ((item.Placement == ToolStripItemPlacement.Main) + && !(item is ToolStripOverflowButton)) { // overflow buttons can be placed outside the display rect. + + bool horizontal = (ToolStrip.LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow); + Rectangle displayRect = displayRectangle; + Rectangle itemBounds = new Rectangle(itemLocation, itemSize); + // in horizontal if something bleeds over the top/bottom that's ok - its left/right we care about + // same in vertical. + if (horizontal) { + if ((itemBounds.Right > displayRectangle.Right) + || (itemBounds.Left < displayRectangle.Left)) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "[SplitStack.SetItemLocation] Sending Item {0} to NoMansLand as it doesnt fit horizontally within the DRect", item)); } +#endif + itemLocation = noMansLand; + item.SetPlacement(ToolStripItemPlacement.None); + } + + } + else { + if ((itemBounds.Bottom > displayRectangle.Bottom) + || (itemBounds.Top < displayRectangle.Top)) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "[SplitStack.SetItemLocation] Sending Item {0} to NoMansLand as it doesnt fit verticallu within the DRect", item)); } +#endif + + itemLocation = noMansLand; + item.SetPlacement(ToolStripItemPlacement.None); + } + } + } + item.SetBounds(new Rectangle(itemLocation, itemSize)); + + } + + private void PlaceItems() { + ToolStrip toolStrip = this.ToolStrip; + + for (int i = 0; i < toolStrip.Items.Count; i++) { + ToolStripItem item = toolStrip.Items[i]; + // if we havent placed the items, place them now. + if (item.Placement == ToolStripItemPlacement.None) { + if (item.Overflow != ToolStripItemOverflow.Always) { + // as needed items will have already been placed into the overflow if they + // needed to move over. + item.SetPlacement(ToolStripItemPlacement.Main); + } + else { + item.SetPlacement(ToolStripItemPlacement.Overflow); + } + } + } + } + + private void ResetItemPlacements() { + ToolStrip toolStrip = this.ToolStrip; + + for (int i = 0; i < toolStrip.Items.Count; i++) { + if (toolStrip.Items[i].Placement == ToolStripItemPlacement.Overflow) { + toolStrip.Items[i].ParentInternal = null; + } + + toolStrip.Items[i].SetPlacement(ToolStripItemPlacement.None); + } + } + + // + // This method is called when we are walking through the item collection and we have realized that we + // need to free up "X" amount of space to be able to fit an item onto the winbar. + private int SendNextItemToOverflow(int spaceNeeded, bool horizontal) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "SendNextItemToOverflow attempting to free {0}", spaceNeeded)); } +#endif + Debug.Indent(); + + int freedSpace = 0; + int backIndex = BackwardsWalkingIndex; + + BackwardsWalkingIndex = (backIndex == -1) ? ToolStrip.Items.Count - 1 : backIndex -1; + for (; BackwardsWalkingIndex >= 0; BackwardsWalkingIndex--) { + ToolStripItem item = ToolStrip.Items[BackwardsWalkingIndex]; + + if (!((IArrangedElement)item).ParticipatesInLayout) { + // skip over items not participating in layout. E.G. not visible items + continue; + } + Padding itemMargin = item.Margin; + + // look for items that say they're ok for overflowing. + // not looking at ones that Always overflow - as the forward walker already skips these. + if (item.Overflow == ToolStripItemOverflow.AsNeeded && item.Placement != ToolStripItemPlacement.Overflow) { +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Found candidate for sending to overflow {0}", item.ToString())); } +#endif + + // since we havent parented the item yet - the auto size wont have reset the size yet. + Size itemSize = item.AutoSize ? item.GetPreferredSize(displayRectangle.Size) : item.Size; + + if (BackwardsWalkingIndex <= ForwardsWalkingIndex) { + // we've found an item that the forwards walking guy has already marched past, + // we need to let him know how much space we're freeing by sending this guy over + // to the overflow. + freedSpace += (horizontal) ? itemSize.Width + itemMargin.Horizontal : itemSize.Height + itemMargin.Vertical; +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Sweet! {0} FreedSpace - which is now {1}", itemSize, freedSpace.ToString(CultureInfo.InvariantCulture))); } +#endif + } + + // send the item to the overflow. + item.SetPlacement(ToolStripItemPlacement.Overflow); + if (this.OverflowRequired == false) { + // this is the first item we're sending down. + // we now need to account for the width or height of the overflow button + spaceNeeded += (horizontal) ? OverflowButtonSize.Width : OverflowButtonSize.Height; +#if DEBUG + if (DebugLayoutTraceSwitch.TraceVerbose) { Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Turns out we now need an overflow button, space needed now: {0}", spaceNeeded.ToString(CultureInfo.InvariantCulture))); } +#endif + this.OverflowRequired = true; + } + + this.OverflowSpace += (horizontal) ? itemSize.Width + itemMargin.Horizontal : itemSize.Height + itemMargin.Vertical; + } + + if (freedSpace > spaceNeeded) { + break; + } + } + + Debug.Unindent(); + return freedSpace; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripStatusLabel.cs b/WindowsForms/Managed/System/WinForms/ToolStripStatusLabel.cs new file mode 100644 index 000000000..d5cf89125 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripStatusLabel.cs @@ -0,0 +1,303 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Design; + using System.Diagnostics; + using System.Windows.Forms.ButtonInternal; + using System.Security.Permissions; + using System.Security; + using System.Windows.Forms.Layout; + using System.Windows.Forms.Design; + using System.Runtime.InteropServices; + using Automation; + + + /// + /// + /// A non selectable winbar item + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)] + public class ToolStripStatusLabel : ToolStripLabel, IAutomationLiveRegion { + + private static readonly Padding defaultMargin = new Padding(0, 3, 0, 2); + private Padding scaledDefaultMargin = defaultMargin; + + private Border3DStyle borderStyle = Border3DStyle.Flat; + private ToolStripStatusLabelBorderSides borderSides = ToolStripStatusLabelBorderSides.None; + private bool spring = false; + private AutomationLiveSetting liveSetting; + + + /// + /// + /// A non selectable winbar item + /// + public ToolStripStatusLabel() { + Initialize(); + } + public ToolStripStatusLabel(string text):base(text,null,false,null) { + Initialize(); + } + public ToolStripStatusLabel(Image image):base(null,image,false,null) { + Initialize(); + } + public ToolStripStatusLabel(string text, Image image):base(text,image,false,null) { + Initialize(); + } + public ToolStripStatusLabel(string text, Image image, EventHandler onClick):base(text,image,/*isLink=*/false,onClick,null) { + Initialize(); + } + public ToolStripStatusLabel(string text, Image image, EventHandler onClick, string name) :base(text,image,/*isLink=*/false,onClick, name) { + Initialize(); + } + + /// + /// Creates a new AccessibleObject for this ToolStripStatusLabel instance. + /// The AccessibleObject instance returned by this method supports UIA Live Region feature. + /// However the new object is only available in applications that are recompiled to target + /// .NET Framework 4.7.3 or opted into this feature using compatibility switches. + /// + /// + /// AccessibleObject for this ToolStripStatusLabel instance. + /// + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripStatusLabelAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + /// + /// Creates an instance of the object that defines how image and text + /// gets laid out in the ToolStripItem + /// + internal override ToolStripItemInternalLayout CreateInternalLayout() { + return new ToolStripStatusLabelLayout(this); + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public new ToolStripItemAlignment Alignment { + get { + return base.Alignment; + } + set { + base.Alignment = value; + } + } + + + /// + [ + DefaultValue(Border3DStyle.Flat), + SRDescription(SR.ToolStripStatusLabelBorderStyleDescr), + SRCategory(SR.CatAppearance) + ] + public Border3DStyle BorderStyle { + get { + return borderStyle; + } + set { + if (!ClientUtils.IsEnumValid_NotSequential(value, + (int)value, + (int)Border3DStyle.Adjust, + (int)Border3DStyle.Bump, + (int)Border3DStyle.Etched, + (int)Border3DStyle.Flat, + (int)Border3DStyle.Raised, + (int)Border3DStyle.RaisedInner, + (int)Border3DStyle.RaisedOuter, + (int)Border3DStyle.Sunken, + (int)Border3DStyle.SunkenInner, + (int)Border3DStyle.SunkenOuter + )) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(Border3DStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + Invalidate(); + } + } + } + + /// + [ + DefaultValue(ToolStripStatusLabelBorderSides.None), + SRDescription(SR.ToolStripStatusLabelBorderSidesDescr), + SRCategory(SR.CatAppearance) + ] + public ToolStripStatusLabelBorderSides BorderSides { + get { + return borderSides; + } + set { + // no Enum.IsDefined as this is a flags enum. + if (borderSides != value) { + borderSides = value; + LayoutTransaction.DoLayout(Owner,this, PropertyNames.BorderStyle); + Invalidate(); + } + } + } + + /// + /// Called by all constructors of ToolStripButton. + /// + private void Initialize() + { + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin); + } + } + + protected internal override Padding DefaultMargin { + get { + return scaledDefaultMargin; + } + } + + [ + DefaultValue(false), + SRDescription(SR.ToolStripStatusLabelSpringDescr), + SRCategory(SR.CatAppearance) + ] + public bool Spring { + get { return spring; } + set { + if (spring != value) { + spring = value; + if (ParentInternal != null) { + LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Spring); + } + + } + } + } + + /// + /// Indicates the "politeness" level that a client should use + /// to notify the user of changes to the live region. + /// + [ + SRCategory(SR.CatAccessibility), + DefaultValue(AutomationLiveSetting.Off), + SRDescription(SR.LiveRegionAutomationLiveSettingDescr), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always) + ] + public AutomationLiveSetting LiveSetting { + get { + return liveSetting; + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutomationLiveSetting.Off, (int)AutomationLiveSetting.Assertive)) { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutomationLiveSetting)); + } + liveSetting = value; + } + } + + protected override void OnTextChanged(EventArgs e) { + base.OnTextChanged(e); + if (AccessibilityImprovements.Level3 && LiveSetting != AutomationLiveSetting.Off) { + AccessibilityObject.RaiseLiveRegionChanged(); + } + } + + public override System.Drawing.Size GetPreferredSize(System.Drawing.Size constrainingSize) { + if (BorderSides != ToolStripStatusLabelBorderSides.None) { + return base.GetPreferredSize(constrainingSize) + new Size(4, 4); + } + else { + return base.GetPreferredSize(constrainingSize); + } + } + + + + /// + /// + /// Inheriting classes should override this method to handle this event. + /// + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { + + if (this.Owner != null) { + ToolStripRenderer renderer = this.Renderer; + + renderer.DrawToolStripStatusLabelBackground(new ToolStripItemRenderEventArgs(e.Graphics, this)); + + if ((DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { + renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(e.Graphics, this, InternalLayout.ImageRectangle)); + } + + PaintText(e.Graphics); + } + } + + [ComVisible(true)] + internal class ToolStripStatusLabelAccessibleObject : ToolStripLabelAccessibleObject { + private ToolStripStatusLabel ownerItem; + + public ToolStripStatusLabelAccessibleObject(ToolStripStatusLabel ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + /// + /// Raises the LiveRegionChanged UIA event. + /// To make this method effective, the applications must be recompiled to target .NET Framework 4.7.3 + /// or opted into this feature using compatibility switches. + /// + /// True if operation succeeds, False otherwise. + public override bool RaiseLiveRegionChanged() { + return RaiseAutomationEvent(NativeMethods.UIA_LiveRegionChangedEventId); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_LiveSettingPropertyId: + return ownerItem.LiveSetting; + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_TextControlTypeId; + } + + return base.GetPropertyValue(propertyID); + } + } + + /// + /// This class performs internal layout for the "split button button" portion of a split button. + /// Its main job is to make sure the inner button has the same parent as the split button, so + /// that layout can be performed using the correct graphics context. + /// + private class ToolStripStatusLabelLayout : ToolStripItemInternalLayout { + + ToolStripStatusLabel owner; + + public ToolStripStatusLabelLayout(ToolStripStatusLabel owner) : base(owner) { + this.owner = owner; + } + + protected override ToolStripItemLayoutOptions CommonLayoutOptions() { + ToolStripItemLayoutOptions layoutOptions = base.CommonLayoutOptions(); + layoutOptions.borderSize = 0; + return layoutOptions; + } + } + + } + +} + + + + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripStatusLabelBorderSides.cs b/WindowsForms/Managed/System/WinForms/ToolStripStatusLabelBorderSides.cs new file mode 100644 index 000000000..68ff34aae --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripStatusLabelBorderSides.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing.Design; + + [System.Runtime.InteropServices.ComVisible(true), + Editor("System.Windows.Forms.Design.BorderSidesEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Flags] + public enum ToolStripStatusLabelBorderSides { + All = Border3DSide.Top | Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right, // not mapped to Border3DSide.All because we NEVER want to fill the middle. + Bottom = Border3DSide.Bottom, + Left = Border3DSide.Left, + Right = Border3DSide.Right, + Top = Border3DSide.Top, + None = 0 + } +} + diff --git a/WindowsForms/Managed/System/WinForms/ToolStripSystemRenderer.cs b/WindowsForms/Managed/System/WinForms/ToolStripSystemRenderer.cs new file mode 100644 index 000000000..2a6d8d5ef --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripSystemRenderer.cs @@ -0,0 +1,716 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Imaging; + using System.ComponentModel; + using System.Windows.Forms.Layout; + + /// + public class ToolStripSystemRenderer : ToolStripRenderer { + + [ThreadStatic()] + private static VisualStyleRenderer renderer = null; + private ToolStripRenderer toolStripHighContrastRenderer; + + public ToolStripSystemRenderer() { + } + internal ToolStripSystemRenderer(bool isDefault) : base(isDefault) { + } + internal override ToolStripRenderer RendererOverride { + get { + if (DisplayInformation.HighContrast) { + return HighContrastRenderer; + } + return null; + } + } + + internal ToolStripRenderer HighContrastRenderer { + get { + if (toolStripHighContrastRenderer == null) { + toolStripHighContrastRenderer = new ToolStripHighContrastRenderer(/*renderLikeSystem*/true); + } + return toolStripHighContrastRenderer; + } + } + + + /// + /// Draw the background color + /// + private static VisualStyleRenderer VisualStyleRenderer { + get { + if (Application.RenderWithVisualStyles) { + if (renderer == null && VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolBar.Button.Normal)) { + renderer = new VisualStyleRenderer(VisualStyleElement.ToolBar.Button.Normal); + } + } + else { + renderer = null; + } + return renderer; + } + + } + + /// + /// Fill the item's background as bounded by the rectangle + /// + private static void FillBackground(Graphics g, Rectangle bounds, Color backColor) { + // Fill the background with the item's back color + if (backColor.IsSystemColor) { + g.FillRectangle(SystemBrushes.FromSystemColor(backColor), bounds); + } + else { + using (Brush backBrush = new SolidBrush(backColor)) { + g.FillRectangle(backBrush, bounds); + } + } + } + + /// + /// returns true if you are required to dispose the pen + /// + private static bool GetPen(Color color, ref Pen pen) { + if (color.IsSystemColor) { + pen = SystemPens.FromSystemColor(color); + return false; + } + else{ + pen = new Pen(color); + return true; + } + } + /// + /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// + private static int GetItemState(ToolStripItem item) { + return (int)GetToolBarState(item); + } + /// + /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// + private static int GetSplitButtonDropDownItemState(ToolStripSplitButton item) { + return (int)GetSplitButtonToolBarState(item, true); + } + /// + /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// + private static int GetSplitButtonItemState(ToolStripSplitButton item) { + return (int)GetSplitButtonToolBarState(item, false); + } + /// + /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// + private static ToolBarState GetSplitButtonToolBarState(ToolStripSplitButton button, bool dropDownButton) { + ToolBarState state = ToolBarState.Normal; + + if (button != null) { + if (!button.Enabled) { + state = ToolBarState.Disabled; + } + else if (dropDownButton){ + if (button.DropDownButtonPressed || button.ButtonPressed) { + state = ToolBarState.Pressed; + } + else if (button.DropDownButtonSelected || button.ButtonSelected) { + state = ToolBarState.Hot; + } + } + else{ + if (button.ButtonPressed) { + state = ToolBarState.Pressed; + } + else if (button.ButtonSelected) { + state = ToolBarState.Hot; + } + } + } + return state; + } + + /// + /// translates the winbar item state into a toolbar state, which is something the renderer understands + /// + private static ToolBarState GetToolBarState(ToolStripItem item) { + ToolBarState state = ToolBarState.Normal; + if (item != null) { + if (!item.Enabled) { + state = ToolBarState.Disabled; + } + if (item is ToolStripButton && ((ToolStripButton)item).Checked) { + if (((ToolStripButton)item).Selected && AccessibilityImprovements.Level1) { + state = ToolBarState.Hot; // we'd prefer HotChecked here, but Color Theme uses the same color as Checked + } + else { + state = ToolBarState.Checked; + } + } + else if (item.Pressed) { + state = ToolBarState.Pressed; + } + else if (item.Selected) { + state = ToolBarState.Hot; + } + } + return state; + } + + + + /// + /// + /// Draw the winbar background. ToolStrip users should override this if they want to draw differently. + /// + protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { + ToolStrip toolStrip = e.ToolStrip; + Graphics g = e.Graphics; + Rectangle bounds= e.AffectedBounds; + + if (!ShouldPaintBackground(toolStrip)) { + return; + } + + if (toolStrip is StatusStrip) { + RenderStatusStripBackground(e); + } + else { + if (DisplayInformation.HighContrast) { + FillBackground(g, bounds, SystemColors.ButtonFace); + } + else if (DisplayInformation.LowResolution) { + FillBackground(g, bounds, (toolStrip is ToolStripDropDown) ? SystemColors.ControlLight : e.BackColor); + } + else if (toolStrip.IsDropDown) { + FillBackground(g, bounds, (!ToolStripManager.VisualStylesEnabled) ? + e.BackColor : SystemColors.Menu); + } + else if (toolStrip is MenuStrip) { + FillBackground(g, bounds, (!ToolStripManager.VisualStylesEnabled) ? + e.BackColor : SystemColors.MenuBar); + } + else if (ToolStripManager.VisualStylesEnabled && VisualStyleRenderer.IsElementDefined(VisualStyleElement.Rebar.Band.Normal)) { + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + + vsRenderer.SetParameters(VisualStyleElement.ToolBar.Bar.Normal); + vsRenderer.DrawBackground(g, bounds); + } + else { + FillBackground(g, bounds, (!ToolStripManager.VisualStylesEnabled) ? + e.BackColor : SystemColors.MenuBar); + } + } + + } + + /// + /// + /// Draw the border around the ToolStrip. This should be done as the last step. + /// + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { + ToolStrip toolStrip = e.ToolStrip; + Graphics g = e.Graphics; + Rectangle bounds = e.ToolStrip.ClientRectangle; + + if (toolStrip is StatusStrip) { + RenderStatusStripBorder(e); + } + else if (toolStrip is ToolStripDropDown) { + ToolStripDropDown toolStripDropDown = toolStrip as ToolStripDropDown; + + // Paint the border for the window depending on whether or not we have a drop shadow effect. + if (toolStripDropDown.DropShadowEnabled && ToolStripManager.VisualStylesEnabled) { + bounds.Width -= 1; + bounds.Height -= 1; + e.Graphics.DrawRectangle(new Pen(SystemColors.ControlDark), bounds); + } + else { + ControlPaint.DrawBorder3D(e.Graphics, bounds, Border3DStyle.Raised); + } + } + else { + if (ToolStripManager.VisualStylesEnabled) { + e.Graphics.DrawLine(SystemPens.ButtonHighlight, 0,bounds.Bottom-1,bounds.Width, bounds.Bottom-1); + e.Graphics.DrawLine(SystemPens.InactiveBorder, 0,bounds.Bottom-2,bounds.Width,bounds.Bottom-2); + } + else { + e.Graphics.DrawLine(SystemPens.ButtonHighlight, 0,bounds.Bottom-1,bounds.Width, bounds.Bottom-1); + e.Graphics.DrawLine(SystemPens.ButtonShadow, 0,bounds.Bottom-2,bounds.Width,bounds.Bottom-2); + } + } + } + + /// + /// + /// Draw the grip. ToolStrip users should override this if they want to draw differently. + /// + protected override void OnRenderGrip(ToolStripGripRenderEventArgs e) { + + Graphics g = e.Graphics; + Rectangle bounds = new Rectangle(Point.Empty, e.GripBounds.Size); + bool verticalGrip = e.GripDisplayStyle == ToolStripGripDisplayStyle.Vertical; + + if (ToolStripManager.VisualStylesEnabled && VisualStyleRenderer.IsElementDefined(VisualStyleElement.Rebar.Gripper.Normal)) { + + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + + if (verticalGrip) { + vsRenderer.SetParameters(VisualStyleElement.Rebar.Gripper.Normal); + + bounds.Height = ((bounds.Height -2/*number of pixels for border*/) / 4) * 4; // make sure height is an even interval of 4. + bounds.Y = Math.Max(0,(e.GripBounds.Height - bounds.Height -2/*number of pixels for border*/) / 2); + } + else { + vsRenderer.SetParameters(VisualStyleElement.Rebar.GripperVertical.Normal); + } + vsRenderer.DrawBackground(g, bounds); + } + else { + // do some fixup so that we dont paint from end to end. + Color backColor = e.ToolStrip.BackColor; + FillBackground(g, bounds, backColor); + + if (verticalGrip) { + if (bounds.Height >= 4) { + bounds.Inflate(0,-2); // scoot down 2PX and start drawing + } + bounds.Width = 3; + } + else { + if (bounds.Width >= 4) { + bounds.Inflate(-2,0); // scoot over 2PX and start drawing + } + bounds.Height = 3; + } + + RenderSmall3DBorderInternal(g, bounds, ToolBarState.Hot, (e.ToolStrip.RightToLeft == RightToLeft.Yes)); + + } + } + + /// + /// + /// Draw the items background + /// + protected override void OnRenderItemBackground(ToolStripItemRenderEventArgs e) { + } + + /// + /// + /// Draw the items background + /// + protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { + } + /// + /// + /// Draw the button background + /// + protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) { + RenderItemInternal(e); + } + + /// + /// + /// Draw the button background + /// + protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e) { + RenderItemInternal(e); + } + + /// + /// + /// Draw the button background + /// + protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) { + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + + + if (ToolStripManager.VisualStylesEnabled && VisualStyleRenderer.IsElementDefined(VisualStyleElement.Rebar.Chevron.Normal)) { + VisualStyleElement chevronElement = VisualStyleElement.Rebar.Chevron.Normal; + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + vsRenderer.SetParameters(chevronElement.ClassName, chevronElement.Part, GetItemState(item)); + vsRenderer.DrawBackground(g, new Rectangle(Point.Empty, item.Size)); + } + else { + RenderItemInternal(e); + Color arrowColor = item.Enabled ? SystemColors.ControlText : SystemColors.ControlDark; + + DrawArrow(new ToolStripArrowRenderEventArgs(g, item, new Rectangle(Point.Empty, item.Size), arrowColor, ArrowDirection.Down)); + } + } + + /// + /// + /// Draw the button background + /// + protected override void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) { + RenderLabelInternal(e); + } + /// + /// + /// Draw the items background + /// + protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { + + ToolStripMenuItem item = e.Item as ToolStripMenuItem; + Graphics g = e.Graphics; + + if (item is MdiControlStrip.SystemMenuItem) { + return; // no highlights are painted behind a system menu item + } + + // + + if (item != null) { + Rectangle bounds = new Rectangle(Point.Empty, item.Size); + if (item.IsTopLevel && !ToolStripManager.VisualStylesEnabled) { + // CLASSIC MODE (3D edges) + // Draw box highlight for toplevel items in downlevel platforms + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, item.ContentRectangle, item.ContentRectangle); + } + else if (item.RawBackColor != Color.Empty) { + FillBackground(g, item.ContentRectangle, item.BackColor); + } + // Toplevel menu items do 3D borders. + ToolBarState state = GetToolBarState(item); + RenderSmall3DBorderInternal(g, bounds, state, (item.RightToLeft == RightToLeft.Yes)); + } + else { + // XP++ MODE (no 3D edges) + // Draw blue filled highlight for toplevel items in themed platforms + // or items parented to a drop down + Rectangle fillRect = new Rectangle(Point.Empty, item.Size); + if (item.IsOnDropDown) { + // VSWhidbey 518568: scoot in by 2 pixels when selected + fillRect.X += 2; + fillRect.Width -= 3; //its already 1 away from the right edge + } + + if (item.Selected || item.Pressed) { + // VSO 382373 - Legacy behavior is to always paint the menu item background. + // The correct behavior is to only paint the background if the menu item is + // enabled. + if (!AccessibilityImprovements.Level1 || item.Enabled) { + g.FillRectangle(SystemBrushes.Highlight, fillRect); + } + + if (AccessibilityImprovements.Level1) { + Color borderColor = ToolStripManager.VisualStylesEnabled ? + SystemColors.Highlight : ProfessionalColors.MenuItemBorder; + + // draw selection border - always drawn regardless of Enabled. + using (Pen p = new Pen(borderColor)) { + g.DrawRectangle(p, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + } + } + } + else { + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, item.ContentRectangle, fillRect); + } + else if (!ToolStripManager.VisualStylesEnabled && (item.RawBackColor != Color.Empty)) { + FillBackground(g, fillRect, item.BackColor); + } + } + } + + } + + } + + /// + /// + /// Draws a toolbar separator. ToolStrip users should override this function to change the + /// drawing of all separators. + /// + protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { + RenderSeparatorInternal(e.Graphics, e.Item, new Rectangle(Point.Empty, e.Item.Size), e.Vertical); + } + + + /// + protected override void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e) { + RenderLabelInternal(e); + ToolStripStatusLabel item = e.Item as ToolStripStatusLabel; + + ControlPaint.DrawBorder3D(e.Graphics, new Rectangle(0,0,item.Width-1, item.Height-1), item.BorderStyle, (Border3DSide)item.BorderSides); + } + + /// + /// + /// Draw the item's background. + /// + protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) { + + ToolStripSplitButton splitButton = e.Item as ToolStripSplitButton; + Graphics g = e.Graphics; + + bool rightToLeft = (splitButton.RightToLeft == RightToLeft.Yes); + Color arrowColor = splitButton.Enabled ? SystemColors.ControlText : SystemColors.ControlDark; + + + // in right to left - we need to swap the parts so we dont draw v][ toolStripSplitButton + VisualStyleElement splitButtonDropDownPart = (rightToLeft) ? VisualStyleElement.ToolBar.SplitButton.Normal : VisualStyleElement.ToolBar.SplitButtonDropDown.Normal; + VisualStyleElement splitButtonPart = (rightToLeft) ? VisualStyleElement.ToolBar.DropDownButton.Normal : VisualStyleElement.ToolBar.SplitButton.Normal; + + Rectangle bounds = new Rectangle(Point.Empty, splitButton.Size); + if (ToolStripManager.VisualStylesEnabled + && VisualStyleRenderer.IsElementDefined(splitButtonDropDownPart) + && VisualStyleRenderer.IsElementDefined(splitButtonPart)) { + + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + + + // Draw the SplitButton Button portion of it. + vsRenderer.SetParameters(splitButtonPart.ClassName, splitButtonPart.Part, GetSplitButtonItemState(splitButton)); + + + // the lovely Windows theming for split button comes in three pieces: + // SplitButtonDropDown: [ v | + // Separator: | + // SplitButton: | ] + // this is great except if you want to swap the button in RTL. In this case we need + // to use the DropDownButton instead of the SplitButtonDropDown and paint the arrow ourselves. + Rectangle splitButtonBounds = splitButton.ButtonBounds; + if (rightToLeft) { + // scoot to the left so we dont draw double shadow like so: ][ + splitButtonBounds.Inflate(2,0); + } + // Draw the button portion of it. + vsRenderer.DrawBackground(g, splitButtonBounds); + + // Draw the SplitButton DropDownButton portion of it. + vsRenderer.SetParameters(splitButtonDropDownPart.ClassName, splitButtonDropDownPart.Part, GetSplitButtonDropDownItemState(splitButton)); + + // Draw the drop down button portion + vsRenderer.DrawBackground(g, splitButton.DropDownButtonBounds); + + // fill in the background image + Rectangle fillRect = splitButton.ContentRectangle; + if (splitButton.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, splitButton.BackgroundImage, splitButton.BackColor, splitButton.BackgroundImageLayout, fillRect, fillRect); + } + + // draw the separator over it. + RenderSeparatorInternal(g,splitButton, splitButton.SplitterBounds, true); + + // and of course, now if we're in RTL we now need to paint the arrow + // because we're no longer using a part that has it built in. + if (rightToLeft || splitButton.BackgroundImage != null) { + DrawArrow(new ToolStripArrowRenderEventArgs(g, splitButton, splitButton.DropDownButtonBounds, arrowColor, ArrowDirection.Down)); + } + } + else { + + // Draw the split button button + Rectangle splitButtonButtonRect = splitButton.ButtonBounds; + + if (splitButton.BackgroundImage != null) { + // fill in the background image + Rectangle fillRect = (splitButton.Selected) ? splitButton.ContentRectangle :bounds; + if (splitButton.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, splitButton.BackgroundImage, splitButton.BackColor, splitButton.BackgroundImageLayout, bounds, fillRect); + } + } + else { + FillBackground(g,splitButtonButtonRect, splitButton.BackColor); + } + + ToolBarState state = GetSplitButtonToolBarState(splitButton, false); + + RenderSmall3DBorderInternal(g, splitButtonButtonRect, state, rightToLeft); + + // draw the split button drop down + Rectangle dropDownRect = splitButton.DropDownButtonBounds; + + // fill the color in the dropdown button + if (splitButton.BackgroundImage == null) { + FillBackground(g, dropDownRect, splitButton.BackColor); + } + + state = GetSplitButtonToolBarState(splitButton, true); + + if ((state == ToolBarState.Pressed) || (state == ToolBarState.Hot)) { + RenderSmall3DBorderInternal(g, dropDownRect, state, rightToLeft); + } + + DrawArrow(new ToolStripArrowRenderEventArgs(g, splitButton, dropDownRect, arrowColor, ArrowDirection.Down)); + + + } + + } + + /// + /// This exists mainly so that buttons, labels and items, etc can share the same implementation. + /// If OnRenderButton called OnRenderItem we would never be able to change the implementation + /// as it would be a breaking change. If in v1, the user overrode OnRenderItem to draw green triangles + /// and in v2 we decided to add a feature to button that would require us to no longer call OnRenderItem - + /// the user's version of OnRenderItem would not get called when he upgraded his framework. Hence + /// everyone should just call this private shared method. Users need to override each item they want + /// to change the look and feel of. + /// + private void RenderItemInternal(ToolStripItemRenderEventArgs e) { + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + + ToolBarState state = GetToolBarState(item); + VisualStyleElement toolBarElement = VisualStyleElement.ToolBar.Button.Normal; + + + if (ToolStripManager.VisualStylesEnabled + && (VisualStyleRenderer.IsElementDefined(toolBarElement))) { + + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + vsRenderer.SetParameters(toolBarElement.ClassName, toolBarElement.Part, (int)state); + vsRenderer.DrawBackground(g, new Rectangle(Point.Empty, item.Size)); + } + else { + RenderSmall3DBorderInternal(g, new Rectangle(Point.Empty, item.Size), state, (item.RightToLeft == RightToLeft.Yes)); + } + + Rectangle fillRect = item.ContentRectangle; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, fillRect, fillRect); + } + else { + ToolStrip parent = item.GetCurrentParent(); + if ((parent != null) && (state != ToolBarState.Checked) && (item.BackColor != parent.BackColor)) { + FillBackground(g, fillRect, item.BackColor); + } + } + + } + + + /// + /// + private void RenderSeparatorInternal(Graphics g, ToolStripItem item, Rectangle bounds, bool vertical) { + VisualStyleElement separator = (vertical) ? VisualStyleElement.ToolBar.SeparatorHorizontal.Normal : VisualStyleElement.ToolBar.SeparatorVertical.Normal; + + if (ToolStripManager.VisualStylesEnabled + && (VisualStyleRenderer.IsElementDefined(separator))){ + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + + vsRenderer.SetParameters(separator.ClassName, separator.Part, GetItemState(item)); + vsRenderer.DrawBackground(g, bounds); + } + else { + + Color foreColor = item.ForeColor; + Color backColor = item.BackColor; + + Pen foreColorPen = SystemPens.ControlDark; + bool disposeForeColorPen = GetPen(foreColor, ref foreColorPen); + + try { + if (vertical) { + if (bounds.Height >= 4) { + bounds.Inflate(0,-2); // scoot down 2PX and start drawing + } + + bool rightToLeft = (item.RightToLeft == RightToLeft.Yes); + Pen leftPen = (rightToLeft) ? SystemPens.ButtonHighlight : foreColorPen; + Pen rightPen = (rightToLeft) ? foreColorPen : SystemPens.ButtonHighlight; + + // Draw dark line + int startX = bounds.Width / 2; + g.DrawLine(leftPen, startX, bounds.Top, startX, bounds.Bottom); + + // Draw highlight one pixel to the right + startX++; + g.DrawLine(rightPen, startX, bounds.Top, startX, bounds.Bottom); + + } + else { + // + // horizontal separator + if (bounds.Width >= 4) { + bounds.Inflate(-2,0); // scoot over 2PX and start drawing + } + + // Draw dark line + int startY = bounds.Height / 2; + g.DrawLine(foreColorPen, bounds.Left, startY, bounds.Right, startY); + + // Draw highlight one pixel to the right + startY++; + g.DrawLine(SystemPens.ButtonHighlight, bounds.Left, startY, bounds.Right, startY); + } + + } + finally { + if (disposeForeColorPen && foreColorPen != null) { + foreColorPen.Dispose(); + } + } + } + } + + private void RenderSmall3DBorderInternal (Graphics g, Rectangle bounds, ToolBarState state, bool rightToLeft) { + if ((state == ToolBarState.Hot) ||(state == ToolBarState.Pressed) || (state == ToolBarState.Checked)) { + Pen leftPen, topPen, rightPen,bottomPen; + topPen = (state == ToolBarState.Hot) ? SystemPens.ButtonHighlight : SystemPens.ButtonShadow; + bottomPen = (state == ToolBarState.Hot)? SystemPens.ButtonShadow : SystemPens.ButtonHighlight; + + leftPen = (rightToLeft) ? bottomPen : topPen; + rightPen = (rightToLeft) ? topPen : bottomPen; + + g.DrawLine(topPen, bounds.Left, bounds.Top, bounds.Right -1, bounds.Top); + g.DrawLine(leftPen, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom-1); + g.DrawLine(rightPen, bounds.Right-1, bounds.Top, bounds.Right -1, bounds.Bottom-1); + g.DrawLine(bottomPen, bounds.Left, bounds.Bottom-1, bounds.Right -1, bounds.Bottom -1); + + } + } + + private void RenderStatusStripBorder(ToolStripRenderEventArgs e) { + if (!Application.RenderWithVisualStyles) { + e.Graphics.DrawLine(SystemPens.ButtonHighlight, 0,0,e.ToolStrip.Width, 0); + } + } + + private static void RenderStatusStripBackground(ToolStripRenderEventArgs e) { + if (Application.RenderWithVisualStyles) { + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + vsRenderer.SetParameters(VisualStyleElement.Status.Bar.Normal); + vsRenderer.DrawBackground(e.Graphics,new Rectangle(0,0,e.ToolStrip.Width-1, e.ToolStrip.Height-1)); + } + else { + if (!SystemInformation.InLockedTerminalSession()) { // see ddb#191714 + e.Graphics.Clear(e.BackColor); + } + } + } + + private static void RenderLabelInternal(ToolStripItemRenderEventArgs e) { + // dont call RenderItemInternal, as we NEVER want to paint hot. + ToolStripItem item = e.Item; + Graphics g = e.Graphics; + + Rectangle fillRect = item.ContentRectangle; + + if (item.BackgroundImage != null) { + ControlPaint.DrawBackgroundImage(g, item.BackgroundImage, item.BackColor, item.BackgroundImageLayout, fillRect, fillRect); + } + else { + VisualStyleRenderer vsRenderer = VisualStyleRenderer; + + if (vsRenderer == null || (item.BackColor != SystemColors.Control)) { + FillBackground(g, fillRect, item.BackColor); + } + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripTextBox.cs b/WindowsForms/Managed/System/WinForms/ToolStripTextBox.cs new file mode 100644 index 000000000..ff88e5f17 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripTextBox.cs @@ -0,0 +1,955 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System; + using System.Drawing; + using System.Drawing.Design; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms.Layout; + using System.Collections.Specialized; + using System.Runtime.InteropServices; + using System.Windows.Forms.Design; + using System.Security; + using System.Security.Permissions; + using Microsoft.Win32; + + /// + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.MenuStrip | ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)] + public class ToolStripTextBox : ToolStripControlHost { + + internal static readonly object EventTextBoxTextAlignChanged = new object(); + internal static readonly object EventAcceptsTabChanged = new object(); + internal static readonly object EventBorderStyleChanged = new object(); + internal static readonly object EventHideSelectionChanged = new object(); + internal static readonly object EventReadOnlyChanged = new object(); + internal static readonly object EventMultilineChanged = new object(); + internal static readonly object EventModifiedChanged = new object(); + + private static readonly Padding defaultMargin = new Padding(1, 0, 1, 0); + private static readonly Padding defaultDropDownMargin = new Padding(1); + private Padding scaledDefaultMargin = defaultMargin; + private Padding scaledDefaultDropDownMargin = defaultDropDownMargin; + + /// + public ToolStripTextBox() : base(CreateControlInstance()) { + ToolStripTextBoxControl textBox = Control as ToolStripTextBoxControl; + textBox.Owner = this; + + if (DpiHelper.EnableToolStripHighDpiImprovements) { + scaledDefaultMargin = DpiHelper.LogicalToDeviceUnits(defaultMargin); + scaledDefaultDropDownMargin = DpiHelper.LogicalToDeviceUnits(defaultDropDownMargin); + } + } + + public ToolStripTextBox(string name) : this() { + this.Name = name; + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ToolStripTextBox(Control c): base(c) { + throw new NotSupportedException(SR.GetString(SR.ToolStripMustSupplyItsOwnTextBox)); + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + ] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected internal override Padding DefaultMargin { + get { + if (IsOnDropDown) { + return scaledDefaultDropDownMargin; + } + else { + return scaledDefaultMargin; + } + } + } + + /// + protected override Size DefaultSize { + get { + return new Size(100,22); + } + } + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public TextBox TextBox { + get{ + return Control as TextBox; + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripTextBoxAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + [System.Runtime.InteropServices.ComVisible(true)] + internal class ToolStripTextBoxAccessibleObject : ToolStripItemAccessibleObject { + private ToolStripTextBox ownerItem = null; + + public ToolStripTextBoxAccessibleObject(ToolStripTextBox ownerItem) : base(ownerItem) { + this.ownerItem = ownerItem; + } + + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + + return AccessibleRole.Text; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + if (direction == UnsafeNativeMethods.NavigateDirection.FirstChild || + direction == UnsafeNativeMethods.NavigateDirection.LastChild) { + return this.ownerItem.TextBox.AccessibilityObject; + } + + // Handle Parent and other directions in base ToolStripItem.FragmentNavigate() method. + return base.FragmentNavigate(direction); + } + } + + private static Control CreateControlInstance() { + TextBox textBox = new ToolStripTextBoxControl(); + textBox.BorderStyle = BorderStyle.Fixed3D; + textBox.AutoSize = true; + return textBox; + } + + + /// + public override Size GetPreferredSize(Size constrainingSize) { + // dont call TextBox.GPS because it will grow and shrink as the text changes. + Rectangle bounds = CommonProperties.GetSpecifiedBounds(TextBox); + return new Size(bounds.Width, TextBox.PreferredHeight); + } + private void HandleAcceptsTabChanged(object sender, EventArgs e) { + OnAcceptsTabChanged(e); + } + private void HandleBorderStyleChanged(object sender, EventArgs e) { + OnBorderStyleChanged(e); + } + private void HandleHideSelectionChanged(object sender, EventArgs e) { + OnHideSelectionChanged(e); + } + private void HandleModifiedChanged(object sender, EventArgs e) { + OnModifiedChanged(e); + } + private void HandleMultilineChanged(object sender, EventArgs e) { + OnMultilineChanged(e); + } + private void HandleReadOnlyChanged(object sender, EventArgs e) { + OnReadOnlyChanged(e); + } + private void HandleTextBoxTextAlignChanged(object sender, EventArgs e) { + RaiseEvent(EventTextBoxTextAlignChanged, e); + } + /// + protected virtual void OnAcceptsTabChanged(EventArgs e) { + RaiseEvent(EventAcceptsTabChanged, e); + } + /// + protected virtual void OnBorderStyleChanged(EventArgs e) { + RaiseEvent(EventBorderStyleChanged, e); + } + /// + protected virtual void OnHideSelectionChanged(EventArgs e) { + RaiseEvent(EventHideSelectionChanged, e); + } + /// + protected virtual void OnModifiedChanged(EventArgs e) { + RaiseEvent(EventModifiedChanged, e); + } + /// + protected virtual void OnMultilineChanged(EventArgs e) { + RaiseEvent(EventMultilineChanged, e); + } + /// + protected virtual void OnReadOnlyChanged(EventArgs e) { + RaiseEvent(EventReadOnlyChanged, e); + } + + + /// + protected override void OnSubscribeControlEvents(Control control) { + TextBox textBox = control as TextBox; + if (textBox != null) { + // Please keep this alphabetized and in sync with Unsubscribe + // + textBox.AcceptsTabChanged += new EventHandler(HandleAcceptsTabChanged); + textBox.BorderStyleChanged += new EventHandler(HandleBorderStyleChanged); + textBox.HideSelectionChanged += new EventHandler(HandleHideSelectionChanged); + textBox.ModifiedChanged += new EventHandler(HandleModifiedChanged); + textBox.MultilineChanged += new EventHandler(HandleMultilineChanged); + textBox.ReadOnlyChanged += new EventHandler(HandleReadOnlyChanged); + textBox.TextAlignChanged += new EventHandler(HandleTextBoxTextAlignChanged); + + } + + base.OnSubscribeControlEvents(control); + } + + /// + protected override void OnUnsubscribeControlEvents(Control control) { + + TextBox textBox = control as TextBox; + if (textBox != null) { + // Please keep this alphabetized and in sync with Subscribe + // + textBox.AcceptsTabChanged -= new EventHandler(HandleAcceptsTabChanged); + textBox.BorderStyleChanged -= new EventHandler(HandleBorderStyleChanged); + textBox.HideSelectionChanged -= new EventHandler(HandleHideSelectionChanged); + textBox.ModifiedChanged -= new EventHandler(HandleModifiedChanged); + textBox.MultilineChanged -= new EventHandler(HandleMultilineChanged); + textBox.ReadOnlyChanged -= new EventHandler(HandleReadOnlyChanged); + textBox.TextAlignChanged -= new EventHandler(HandleTextBoxTextAlignChanged); + } + base.OnUnsubscribeControlEvents(control); + + } + + internal override bool ShouldSerializeFont() { + return Font != ToolStripManager.DefaultFont; + } + + + +#region WrappedProperties + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxAcceptsTabDescr) + ] + public bool AcceptsTab { + get { return TextBox.AcceptsTab; } + set { TextBox.AcceptsTab = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxAcceptsReturnDescr) + ] + public bool AcceptsReturn { + get { return TextBox.AcceptsReturn; } + set { TextBox.AcceptsReturn = value; } + } + + /// + [ + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.TextBoxAutoCompleteCustomSourceDescr), + Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public System.Windows.Forms.AutoCompleteStringCollection AutoCompleteCustomSource { + get { return TextBox.AutoCompleteCustomSource; } + set { TextBox.AutoCompleteCustomSource = value; } + } + + /// + [ + DefaultValue(AutoCompleteMode.None), + SRDescription(SR.TextBoxAutoCompleteModeDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteMode AutoCompleteMode { + get { return TextBox.AutoCompleteMode; } + set { TextBox.AutoCompleteMode = value; } + } + + /// + [ + DefaultValue(AutoCompleteSource.None), + SRDescription(SR.TextBoxAutoCompleteSourceDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public AutoCompleteSource AutoCompleteSource { + get { return TextBox.AutoCompleteSource; } + set { TextBox.AutoCompleteSource = value; } + } + + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.TextBoxBorderDescr) + ] + public BorderStyle BorderStyle { + get { return TextBox.BorderStyle; } + set { TextBox.BorderStyle = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxCanUndoDescr) + ] + public bool CanUndo { + get { return TextBox.CanUndo; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(CharacterCasing.Normal), + SRDescription(SR.TextBoxCharacterCasingDescr) + ] + public CharacterCasing CharacterCasing { + get { return TextBox.CharacterCasing; } + set { TextBox.CharacterCasing = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TextBoxHideSelectionDescr) + ] + public bool HideSelection { + get { return TextBox.HideSelection; } + set { TextBox.HideSelection = value; } + } + + /// + [ + SRCategory(SR.CatAppearance), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Localizable(true), + SRDescription(SR.TextBoxLinesDescr), + Editor("System.Windows.Forms.Design.StringArrayEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public string[] Lines { + get { return TextBox.Lines; } + set { TextBox.Lines = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(32767), + Localizable(true), + SRDescription(SR.TextBoxMaxLengthDescr) + ] + public int MaxLength { + get { return TextBox.MaxLength; } + set { TextBox.MaxLength = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxModifiedDescr) + ] + public bool Modified { + get { return TextBox.Modified; } + set { TextBox.Modified = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + Localizable(true), + SRDescription(SR.TextBoxMultilineDescr), + RefreshProperties(RefreshProperties.All), + Browsable(false),EditorBrowsable(EditorBrowsableState.Never) + ] + public bool Multiline { + get { return TextBox.Multiline; } + set { TextBox.Multiline = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TextBoxReadOnlyDescr) + ] + public bool ReadOnly { + get { return TextBox.ReadOnly; } + set { TextBox.ReadOnly = value; } + } + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectedTextDescr) + ] + public string SelectedText { + get { return TextBox.SelectedText; } + set { TextBox.SelectedText = value; } + } + + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectionLengthDescr) + ] + public int SelectionLength { + get { return TextBox.SelectionLength; } + set { TextBox.SelectionLength = value; } + } + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TextBoxSelectionStartDescr) + ] + public int SelectionStart { + get { return TextBox.SelectionStart; } + set { TextBox.SelectionStart = value; } + } + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TextBoxShortcutsEnabledDescr) + ] + public bool ShortcutsEnabled { + get { return TextBox.ShortcutsEnabled; } + set { TextBox.ShortcutsEnabled = value; } + } + /// + [Browsable(false)] + public int TextLength { + get { return TextBox.TextLength; } + } + + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(HorizontalAlignment.Left), + SRDescription(SR.TextBoxTextAlignDescr) + ] + public HorizontalAlignment TextBoxTextAlign { + get { return TextBox.TextAlign; } + set { TextBox.TextAlign = value; } + } + + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + DefaultValue(true), + SRDescription(SR.TextBoxWordWrapDescr), + Browsable(false),EditorBrowsable(EditorBrowsableState.Never) + ] + public bool WordWrap { + get { return TextBox.WordWrap; } + set { TextBox.WordWrap = value; } + } + + +#endregion WrappedProperties + + + +#region WrappedEvents + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnAcceptsTabChangedDescr)] + public event EventHandler AcceptsTabChanged { + add { + Events.AddHandler(EventAcceptsTabChanged, value); + } + remove { + Events.RemoveHandler(EventAcceptsTabChanged, value); + } + } + + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnBorderStyleChangedDescr)] + public event EventHandler BorderStyleChanged { + add { + Events.AddHandler(EventBorderStyleChanged, value); + } + remove { + Events.RemoveHandler(EventBorderStyleChanged, value); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnHideSelectionChangedDescr)] + public event EventHandler HideSelectionChanged { + add { + Events.AddHandler(EventHideSelectionChanged, value); + } + remove { + Events.RemoveHandler(EventHideSelectionChanged, value); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnModifiedChangedDescr)] + public event EventHandler ModifiedChanged { + add { + Events.AddHandler(EventModifiedChanged, value); + } + remove { + Events.RemoveHandler(EventModifiedChanged, value); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnMultilineChangedDescr),Browsable(false),EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler MultilineChanged { + add { + Events.AddHandler(EventMultilineChanged, value); + } + remove { + Events.RemoveHandler(EventMultilineChanged, value); + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnReadOnlyChangedDescr)] + public event EventHandler ReadOnlyChanged { + add { + Events.AddHandler(EventReadOnlyChanged, value); + } + remove { + Events.RemoveHandler(EventReadOnlyChanged, value); + } + } + + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ToolStripTextBoxTextBoxTextAlignChangedDescr)] + public event EventHandler TextBoxTextAlignChanged { + add { + Events.AddHandler(EventTextBoxTextAlignChanged, value); + } + remove { + Events.RemoveHandler(EventTextBoxTextAlignChanged, value); + } + } +#endregion WrappedEvents + +#region WrappedMethods + /// + public void AppendText(string text) { TextBox.AppendText(text); } + /// + public void Clear(){ TextBox.Clear(); } + /// + public void ClearUndo() {TextBox.ClearUndo(); } + /// + public void Copy() {TextBox.Copy(); } + /// + public void Cut() {TextBox.Copy(); } + /// + public void DeselectAll() { TextBox.DeselectAll(); } + /// + public char GetCharFromPosition(System.Drawing.Point pt) { return TextBox.GetCharFromPosition(pt); } + /// + public int GetCharIndexFromPosition(System.Drawing.Point pt) { return TextBox.GetCharIndexFromPosition(pt); } + /// + public int GetFirstCharIndexFromLine(int lineNumber) { return TextBox.GetFirstCharIndexFromLine(lineNumber); } + /// + public int GetFirstCharIndexOfCurrentLine() { return TextBox.GetFirstCharIndexOfCurrentLine(); } + /// + public int GetLineFromCharIndex(int index) { return TextBox.GetLineFromCharIndex(index); } + /// + public System.Drawing.Point GetPositionFromCharIndex(int index) { return TextBox.GetPositionFromCharIndex(index); } + /// + public void Paste() { TextBox.Paste(); } + /// + public void ScrollToCaret() { TextBox.ScrollToCaret(); } + /// + public void Select(int start, int length) { TextBox.Select(start, length); } + /// + public void SelectAll() { TextBox.SelectAll(); } + /// + public void Undo() { TextBox.Undo(); } +#endregion + private class ToolStripTextBoxControl : TextBox { + private bool mouseIsOver = false; + private ToolStripTextBox ownerItem; + private bool isFontSet = true; + private bool alreadyHooked; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")] // FXCop doesnt understand that setting Font changes the value of isFontSet + public ToolStripTextBoxControl() { + // required to make the text box height match the combo. + this.Font = ToolStripManager.DefaultFont; + isFontSet = false; + } + + + // returns the distance from the client rect to the upper left hand corner of the control + private NativeMethods.RECT AbsoluteClientRECT { + get { + NativeMethods.RECT rect = new NativeMethods.RECT(); + CreateParams cp = CreateParams; + + AdjustWindowRectEx(ref rect, cp.Style, HasMenu, cp.ExStyle); + + // the coordinates we get back are negative, we need to translate this back to positive. + int offsetX = -rect.left; // one to get back to 0,0, another to translate + int offsetY = -rect.top; + + // fetch the client rect, then apply the offset. + UnsafeNativeMethods.GetClientRect(new HandleRef(this, this.Handle), ref rect); + + rect.left += offsetX; + rect.right += offsetX; + rect.top += offsetY; + rect.bottom += offsetY; + + return rect; + } + } + private Rectangle AbsoluteClientRectangle { + get { + NativeMethods.RECT rect = AbsoluteClientRECT; + return Rectangle.FromLTRB(rect.top, rect.top, rect.right, rect.bottom); + } + } + + + private ProfessionalColorTable ColorTable { + get { + if (Owner != null) { + ToolStripProfessionalRenderer renderer = Owner.Renderer as ToolStripProfessionalRenderer; + if (renderer != null) { + return renderer.ColorTable; + } + } + return ProfessionalColors.ColorTable; + } + } + + private bool IsPopupTextBox { + get { + return ((BorderStyle == BorderStyle.Fixed3D) && + (Owner != null && (Owner.Renderer is ToolStripProfessionalRenderer))); + } + } + + internal bool MouseIsOver { + get { return mouseIsOver; } + set { + if (mouseIsOver != value) { + mouseIsOver = value; + if (!Focused) { + InvalidateNonClient(); + } + } + } + } + + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + isFontSet = ShouldSerializeFont(); + } + } + + public ToolStripTextBox Owner { + get { return ownerItem; } + set { ownerItem = value; } + } + + internal override bool SupportsUiaProviders { + get { + return AccessibilityImprovements.Level3; + } + } + + private void InvalidateNonClient() { + if (!IsPopupTextBox) { + return; + } + NativeMethods.RECT absoluteClientRectangle = AbsoluteClientRECT; + HandleRef hNonClientRegion = NativeMethods.NullHandleRef; + HandleRef hClientRegion = NativeMethods.NullHandleRef; + HandleRef hTotalRegion = NativeMethods.NullHandleRef; + + try { + // get the total client area, then exclude the client by using XOR + hTotalRegion = new HandleRef(this, SafeNativeMethods.CreateRectRgn(0, 0, this.Width, this.Height)); + hClientRegion = new HandleRef(this, SafeNativeMethods.CreateRectRgn(absoluteClientRectangle.left, absoluteClientRectangle.top, absoluteClientRectangle.right, absoluteClientRectangle.bottom)); + hNonClientRegion = new HandleRef(this, SafeNativeMethods.CreateRectRgn(0,0,0,0)); + + SafeNativeMethods.CombineRgn(hNonClientRegion, hTotalRegion, hClientRegion, NativeMethods.RGN_XOR); + + // Call RedrawWindow with the region. + NativeMethods.RECT ignored = new NativeMethods.RECT(); + SafeNativeMethods.RedrawWindow(new HandleRef(this, Handle), + ref ignored , hNonClientRegion, + NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_ERASE | + NativeMethods.RDW_UPDATENOW | NativeMethods.RDW_ERASENOW | + NativeMethods.RDW_FRAME); + } + finally { + // clean up our regions. + try { + if (hNonClientRegion.Handle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(hNonClientRegion); + } + } + finally { + try { + if (hClientRegion.Handle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(hClientRegion); + } + } + finally { + if (hTotalRegion.Handle != IntPtr.Zero) { + SafeNativeMethods.DeleteObject(hTotalRegion); + } + } + } + + } + + } + + protected override void OnGotFocus(EventArgs e) { + base.OnGotFocus(e); + InvalidateNonClient(); + } + + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + InvalidateNonClient(); + + } + + protected override void OnMouseEnter(EventArgs e) { + base.OnMouseEnter(e); + MouseIsOver = true; + + } + + protected override void OnMouseLeave(EventArgs e) { + base.OnMouseLeave(e); + MouseIsOver = false; + } + + + private void HookStaticEvents(bool hook) { + if (hook) { + if (!alreadyHooked) { + try { + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally{ + alreadyHooked = true; + } + } + } + else if (alreadyHooked) { + try { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + finally { + alreadyHooked = false; + } + } + + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { + if (e.Category == UserPreferenceCategory.Window) { + if (!isFontSet) { + this.Font = ToolStripManager.DefaultFont; + } + } + } + + protected override void OnVisibleChanged(EventArgs e) { + base.OnVisibleChanged(e); + if (!Disposing && !IsDisposed) { + HookStaticEvents(Visible); + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + if (AccessibilityImprovements.Level3) { + return new ToolStripTextBoxControlAccessibleObject(this); + } + + return base.CreateAccessibilityInstance(); + } + + protected override void Dispose(bool disposing) { + if(disposing) { + HookStaticEvents(false); + } + base.Dispose(disposing); + } + + private void WmNCPaint(ref Message m) { + + if (!IsPopupTextBox) { + base.WndProc(ref m); + return; + } + + // Paint over the edges of the text box. + + // Using GetWindowDC instead of GetDCEx as GetDCEx seems to return a null handle and a last error of + // the operation succeeded. We're not going to use the clipping rect anyways - so it's not + // that bigga deal. + HandleRef hdc = new HandleRef(this, UnsafeNativeMethods.GetWindowDC(new HandleRef(this,m.HWnd))); + if (hdc.Handle == IntPtr.Zero) { + throw new Win32Exception(); + } + try { + // Dont set the clipping region based on the WParam - windows seems to hack out the two pixels + // intended for the non-client border. + + Color outerBorderColor = (MouseIsOver || Focused) ? ColorTable.TextBoxBorder : this.BackColor; + Color innerBorderColor = this.BackColor; + + if (!Enabled) { + outerBorderColor = SystemColors.ControlDark; + innerBorderColor = SystemColors.Control; + } + using (Graphics g = Graphics.FromHdcInternal(hdc.Handle)) { + + Rectangle clientRect = AbsoluteClientRectangle; + + // could have set up a clip and fill-rectangled, thought this would be faster. + using (Brush b = new SolidBrush(innerBorderColor)) { + g.FillRectangle(b, 0, 0, this.Width, clientRect.Top); // top border + g.FillRectangle(b, 0, 0, clientRect.Left, this.Height); // left border + g.FillRectangle(b, 0, clientRect.Bottom, this.Width, this.Height - clientRect.Height); // bottom border + g.FillRectangle(b, clientRect.Right, 0, this.Width - clientRect.Right, this.Height); // right border + } + + // paint the outside rect. + using (Pen p = new Pen(outerBorderColor)) { + g.DrawRectangle(p, 0, 0, this.Width - 1, this.Height - 1); + } + + } + } + finally { + UnsafeNativeMethods.ReleaseDC(new HandleRef(this, this.Handle),hdc); + } + // we've handled WM_NCPAINT. + m.Result = IntPtr.Zero; + + } + protected override void WndProc(ref Message m) { + if (m.Msg == NativeMethods.WM_NCPAINT) { + WmNCPaint(ref m); + return; + } + else { + base.WndProc(ref m); + } + } + } + + private class ToolStripTextBoxControlAccessibleObject : Control.ControlAccessibleObject { + public ToolStripTextBoxControlAccessibleObject(ToolStripTextBoxControl toolStripTextBoxControl) : base(toolStripTextBoxControl) { + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot { + get { + var toolStripTextBoxControl = this.Owner as ToolStripTextBoxControl; + if (toolStripTextBoxControl != null) { + return toolStripTextBoxControl.Owner.Owner.AccessibilityObject; + } + + return base.FragmentRoot; + } + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) { + switch (direction) { + case UnsafeNativeMethods.NavigateDirection.Parent: + case UnsafeNativeMethods.NavigateDirection.PreviousSibling: + case UnsafeNativeMethods.NavigateDirection.NextSibling: + var toolStripTextBoxControl = Owner as ToolStripTextBoxControl; + if (toolStripTextBoxControl != null) { + return toolStripTextBoxControl.Owner.AccessibilityObject.FragmentNavigate(direction); + } + break; + } + + return base.FragmentNavigate(direction); + } + + internal override object GetPropertyValue(int propertyID) { + switch (propertyID) { + case NativeMethods.UIA_ControlTypePropertyId: + return NativeMethods.UIA_EditControlTypeId; + case NativeMethods.UIA_HasKeyboardFocusPropertyId: + return (State & AccessibleStates.Focused) == AccessibleStates.Focused; + } + + return base.GetPropertyValue(propertyID); + } + + internal override bool IsPatternSupported(int patternId) { + if (patternId == NativeMethods.UIA_ValuePatternId) { + return true; + } + + return base.IsPatternSupported(patternId); + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/ToolStripTextDirection.cs b/WindowsForms/Managed/System/WinForms/ToolStripTextDirection.cs new file mode 100644 index 000000000..aedc1f6ce --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolStripTextDirection.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + using System; + + /// + public enum ToolStripTextDirection { + /// + Inherit, + /// + Horizontal, + /// + Vertical90, + /// + Vertical270 + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolTip.cs b/WindowsForms/Managed/System/WinForms/ToolTip.cs new file mode 100644 index 000000000..feacc0b6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolTip.cs @@ -0,0 +1,2722 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using Hashtable = System.Collections.Hashtable; + using System.Drawing; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Drawing.Design; + using System.Globalization; + using Collections.Generic; + + /// + /// + /// + /// Provides a small pop-up window containing a line of text + /// that describes the purpose of a tool or control (usually represented as a + /// graphical + /// object) in a program. + /// + /// + [ + ProvideProperty("ToolTip", typeof(Control)), + DefaultEvent("Popup"), + ToolboxItemFilter("System.Windows.Forms"), + SRDescription(SR.DescriptionToolTip) + ] + public class ToolTip : Component, IExtenderProvider { + + const int DEFAULT_DELAY = 500; + const int RESHOW_RATIO = 5; + const int AUTOPOP_RATIO = 10; + + const int XBALLOONOFFSET = 10; + const int YBALLOONOFFSET = 8; + + private const int TOP_LOCATION_INDEX = 0; + private const int RIGHT_LOCATION_INDEX = 1; + private const int BOTTOM_LOCATION_INDEX = 2; + private const int LEFT_LOCATION_INDEX = 3; + private const int LOCATION_TOTAL = 4; + + Hashtable tools = new Hashtable(); + int[] delayTimes = new int[4]; + bool auto = true; + bool showAlways = false; + ToolTipNativeWindow window = null; + Control topLevelControl = null; + bool active = true; + bool ownerDraw = false; + object userData; + Color backColor = SystemColors.Info; + Color foreColor = SystemColors.InfoText; + bool isBalloon; + bool isDisposing; + string toolTipTitle = string.Empty; + ToolTipIcon toolTipIcon = (ToolTipIcon)0; + ToolTipTimer timer; + Hashtable owners = new Hashtable(); + bool stripAmpersands = false; + bool useAnimation = true; + bool useFading = true; + int originalPopupDelay = 0; + + // setting TTM_TRACKPOSITION will cause redundant POP and Draw Messages.. + // Hence we gaurd against this by having this private Flag.. + bool trackPosition = false; + + PopupEventHandler onPopup; + DrawToolTipEventHandler onDraw; + + // Adding a tool twice breaks the ToolTip, so we need to track which + // tools are created to prevent this... + // + Hashtable created = new Hashtable(); + + private bool cancelled = false; + + /// + /// + /// + /// Initializes a new instance of the class, given the container. + /// + /// + public ToolTip(IContainer cont) : this() { + if (cont == null) { + throw new ArgumentNullException("cont"); + } + + cont.Add(this); + } + + /// + /// + /// + /// Initializes a new instance of the class in its default state. + /// + /// + public ToolTip() { + window = new ToolTipNativeWindow(this); + auto = true; + delayTimes[NativeMethods.TTDT_AUTOMATIC] = DEFAULT_DELAY; + AdjustBaseFromAuto(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the control is currently active. + /// + /// + [ + SRDescription(SR.ToolTipActiveDescr), + DefaultValue(true) + ] + public bool Active { + get { + return active; + } + + set { + if (active != value) { + active = value; + + //lets not actually activate the tooltip if we're in the designer (just set the value) + if (!DesignMode && GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (value==true)? 1: 0, 0); + } + } + } + } + + internal void HideToolTip(IKeyboardToolTip currentTool) { + this.Hide(currentTool.GetOwnerWindow()); + } + + /// + /// + /// + /// Gets or sets + /// the time (in milliseconds) that passes before the appears. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipAutomaticDelayDescr), + DefaultValue(DEFAULT_DELAY) + ] + public int AutomaticDelay { + get { + return delayTimes[NativeMethods.TTDT_AUTOMATIC]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("AutomaticDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutomaticDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_AUTOMATIC, value); + } + } + + internal string GetCaptionForTool(Control tool) { + Debug.Assert(tool != null, "tool should not be null"); + return ((TipInfo)this.tools[tool])?.Caption; + } + + /// + /// + /// + /// Gets or sets the initial delay for the control. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipAutoPopDelayDescr) + ] + public int AutoPopDelay { + get { + return delayTimes[NativeMethods.TTDT_AUTOPOP]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("AutoPopDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutoPopDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_AUTOPOP, value); + } + } + + /// + /// + /// + /// Gets or sets the BackColor for the control. + /// + /// + [ + SRDescription(SR.ToolTipBackColorDescr), + DefaultValue(typeof(Color),"Info") + ] + public Color BackColor { + get { + return backColor; + } + + set { + backColor = value; + if (GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(backColor), 0); + } + } + } + + /// + /// + /// The createParams to create the window. + /// + /// + protected virtual CreateParams CreateParams { + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + CreateParams cp = new CreateParams(); + if (TopLevelControl != null && !TopLevelControl.IsDisposed) { + cp.Parent = TopLevelControl.Handle; + } + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + if (showAlways) { + cp.Style = NativeMethods.TTS_ALWAYSTIP; + } + if (isBalloon) { + cp.Style |= NativeMethods.TTS_BALLOON; + } + if (!stripAmpersands) { + cp.Style |= NativeMethods.TTS_NOPREFIX; + } + if (!useAnimation) { + cp.Style |= NativeMethods.TTS_NOANIMATE; + } + if (!useFading) { + cp.Style |= NativeMethods.TTS_NOFADE; + } + cp.ExStyle = 0; + cp.Caption = null; + + return cp; + } + } + + /// + /// + /// + /// Gets or sets the ForeColor for the control. + /// + /// + [ + SRDescription(SR.ToolTipForeColorDescr), + DefaultValue(typeof(Color),"InfoText") + ] + public Color ForeColor { + get { + return foreColor; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.ToolTipEmptyColor, "ForeColor")); + } + + foreColor = value; + if (GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(foreColor), 0); + } + + } + } + + internal IntPtr Handle { + get { + if (!GetHandleCreated()) { + CreateHandle(); + } + return window.Handle; + } + } + + /// + /// SECREVIEW: Pattern to check if the caller has a permission without bubbling up a security exception + /// + private bool HasAllWindowsPermission { + get { + try { + IntSecurity.AllWindows.Demand(); + return true; + } + catch (SecurityException) { + } + + return false; + } + } + + /// + /// + /// + /// Gets or sets the IsBalloon for the control. + /// + /// + [ + SRDescription(SR.ToolTipIsBalloonDescr), + DefaultValue(false) + ] + public bool IsBalloon { + get { + return isBalloon; + } + + set { + if (isBalloon != value) { + isBalloon = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + + } + } + + // refer VsWhidbey 498263: ToolTips should be shown only on active Windows. + private bool IsWindowActive(IWin32Window window) + { + Control windowControl = window as Control; + // We want to enter in the IF block only if ShowParams does not return SW_SHOWNOACTIVATE. + // for ToolStripDropDown ShowParams returns SW_SHOWNOACTIVATE, in which case we DONT want to check IsWindowActive and hence return true. + if (windowControl != null && + (windowControl.ShowParams & 0xF) != NativeMethods.SW_SHOWNOACTIVATE) + { + IntPtr hWnd = UnsafeNativeMethods.GetActiveWindow(); + IntPtr rootHwnd =UnsafeNativeMethods.GetAncestor(new HandleRef(window, window.Handle), NativeMethods.GA_ROOT); + if (hWnd != rootHwnd) + { + TipInfo tt = (TipInfo)tools[windowControl]; + if (tt != null && (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) + { + tools.Remove(windowControl); + DestroyRegion(windowControl); + } + return false; + } + } + return true; + } + + /// + /// + /// + /// Gets or sets the initial delay for + /// the + /// control. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipInitialDelayDescr) + ] + public int InitialDelay { + get { + return delayTimes[NativeMethods.TTDT_INITIAL]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("InitialDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "InitialDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_INITIAL, value); + } + } + + /// + /// + /// Indicates whether the ToolTip will be drawn by the system or the user. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ToolTipOwnerDrawDescr) + ] + public bool OwnerDraw + { + get + { + return ownerDraw; + } + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + set + { + ownerDraw = value; + } + } + + /// + /// + /// + /// Gets or sets the length of time (in milliseconds) that + /// it takes subsequent ToolTip instances to appear as the mouse pointer moves from + /// one ToolTip region to + /// another. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipReshowDelayDescr) + ] + public int ReshowDelay { + get { + return delayTimes[NativeMethods.TTDT_RESHOW]; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("ReshowDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "ReshowDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_RESHOW, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// appears even when its parent control is not active. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.ToolTipShowAlwaysDescr) + ] + public bool ShowAlways { + get { + return showAlways; + } + set { + if (showAlways != value) { + showAlways = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + + /// + /// + /// + /// When set to true, any ampersands in the Text property are not displayed. + /// + /// + [ + SRDescription(SR.ToolTipStripAmpersandsDescr), + Browsable(true), + DefaultValue(false) + ] + public bool StripAmpersands { + get { + return stripAmpersands; + } + set { + if (stripAmpersands != value) { + stripAmpersands = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// + /// Gets or sets an Icon on the ToolTip. + /// + /// + [ + DefaultValue(ToolTipIcon.None), + SRDescription(SR.ToolTipToolTipIconDescr) + ] + public ToolTipIcon ToolTipIcon { + get { + return toolTipIcon; + } + set { + if (toolTipIcon != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolTipIcon.None, (int)ToolTipIcon.Error)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolTipIcon)); + } + toolTipIcon = value; + if (toolTipIcon > 0 && GetHandleCreated()) { + // If the title is null/empty, the icon won't display. + string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); + + // Tooltip need to be updated to reflect the changes in the icon because + // this operation directly affects the size of the tooltip + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); + + } + } + } + } + + + /// + /// + /// Gets or sets the title of the ToolTip. + /// + /// + [ + DefaultValue(""), + SRDescription(SR.ToolTipTitleDescr) + ] + public string ToolTipTitle + { + get + { + return toolTipTitle; + } + set + { + if (value == null) + { + value = string.Empty; + } + + if (toolTipTitle != value) + { + toolTipTitle = value; + if (GetHandleCreated()) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, toolTipTitle); + + // Tooltip need to be updated to reflect the changes in the titletext because + // this operation directly affects the size of the tooltip + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); + } + } + } + } + + private Control TopLevelControl { + get { + Control baseVar = null; + if (topLevelControl == null) { + Control[] regions = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(regions, 0); + if (regions != null && regions.Length > 0) { + for (int i=0; i + /// + /// + /// When set to true, animations are used when tooltip is shown or hidden. + /// + /// + [ + SRDescription(SR.ToolTipUseAnimationDescr), + Browsable(true), + DefaultValue(true) + ] + public bool UseAnimation { + get { + return useAnimation; + } + set { + if (useAnimation != value) { + useAnimation = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + + /// + /// + /// + /// When set to true, a fade effect is used when tooltips are shown or hidden. + /// + /// + [ + SRDescription(SR.ToolTipUseFadingDescr), + Browsable(true), + DefaultValue(true) + ] + public bool UseFading { + get { + return useFading; + } + set { + if (useFading != value) { + useFading = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Fires in OwnerDraw mode when the tooltip needs to be drawn. + /// + [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipDrawEventDescr)] + public event DrawToolTipEventHandler Draw + { + add + { + onDraw += value; + } + remove + { + onDraw -= value; + } + } + + /// + /// + /// Fires when the tooltip is just about to be shown. + /// + [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipPopupEventDescr)] + public event PopupEventHandler Popup + { + add + { + onPopup += value; + } + remove + { + onPopup -= value; + } + } + + + /// + /// + /// Adjusts the other delay values based on the Automatic value. + /// + /// + private void AdjustBaseFromAuto() { + delayTimes[NativeMethods.TTDT_RESHOW] = delayTimes[NativeMethods.TTDT_AUTOMATIC] / RESHOW_RATIO; + delayTimes[NativeMethods.TTDT_AUTOPOP] = delayTimes[NativeMethods.TTDT_AUTOMATIC] * AUTOPOP_RATIO; + delayTimes[NativeMethods.TTDT_INITIAL] = delayTimes[NativeMethods.TTDT_AUTOMATIC]; + } + + private void HandleCreated(object sender, EventArgs eventargs) { + // Reset the toplevel control when the owner's handle is recreated. + ClearTopLevelControlEvents(); + topLevelControl = null; + + Control control = (Control)sender; + this.CreateRegion(control); + this.CheckNativeToolTip(control); + this.CheckCompositeControls(control); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Hook(control, this); + } + } + + private void CheckNativeToolTip(Control associatedControl) { + + //Wait for the Handle Creation.. + if (!GetHandleCreated()) { + return; + } + + TreeView treeView = associatedControl as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + treeView.SetToolTip(this,GetToolTip(associatedControl)); + } + } + + if (associatedControl is ToolBar) { + ((ToolBar)associatedControl).SetToolTip(this); + } + + TabControl tabControl = associatedControl as TabControl; + if (tabControl!= null) { + if (tabControl.ShowToolTips) + { + tabControl.SetToolTip(this, GetToolTip(associatedControl)); + } + } + + if (associatedControl is ListView) { + ((ListView)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); + } + + if (associatedControl is StatusBar) { + ((StatusBar)associatedControl).SetToolTip(this); + } + + // Label now has its own Tooltip for AutoEllipsis... + // So this control too falls in special casing... + // We need to disable the LABEL AutoEllipsis tooltip and show + // this tooltip always... + if (associatedControl is Label) { + ((Label)associatedControl).SetToolTip(this); + } + + + } + + private void CheckCompositeControls(Control associatedControl) { + if (associatedControl is UpDownBase) { + ((UpDownBase)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); + } + } + + private void HandleDestroyed(object sender, EventArgs eventargs) { + Control control = (Control)sender; + this.DestroyRegion(control); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Unhook(control, this); + } + } + + /// + /// + /// Fires the Draw event. + /// + private void OnDraw(DrawToolTipEventArgs e) + { + if(onDraw != null) + { + onDraw(this,e); + } + } + + + /// + /// + /// Fires the Popup event. + /// + private void OnPopup(PopupEventArgs e) + { + if(onPopup != null) + { + onPopup(this,e); + } + } + + private void TopLevelCreated(object sender, EventArgs eventargs) { + CreateHandle(); + CreateAllRegions(); + } + + private void TopLevelDestroyed(object sender, EventArgs eventargs) { + DestoyAllRegions(); + DestroyHandle(); + } + + /// + /// + /// Returns true if the tooltip can offer an extender property to the + /// specified target component. + /// + /// + public bool CanExtend(object target) { + if (target is Control && + !(target is ToolTip)) { + + return true; + } + return false; + } + + private void ClearTopLevelControlEvents() { + + if (this.topLevelControl != null) { + this.topLevelControl.ParentChanged -= new EventHandler(this.OnTopLevelPropertyChanged); + this.topLevelControl.HandleCreated -= new EventHandler(this.TopLevelCreated); + this.topLevelControl.HandleDestroyed -= new EventHandler(this.TopLevelDestroyed); + } + } + + /// + /// + /// Creates the handle for the control. + /// + /// + private void CreateHandle() { + if (GetHandleCreated()) { + return; + } + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + + CreateParams cp = CreateParams; // Avoid reentrant call to CreateHandle (VSWhidbey 570764) + if (GetHandleCreated()) { + return; + } + window.CreateHandle(cp); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + // If in ownerDraw mode, we don't want the default border. + if (ownerDraw) { + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + style &= ~NativeMethods.WS_BORDER; + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); + } + + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + + Debug.Assert(NativeMethods.TTDT_AUTOMATIC == 0, "TTDT_AUTOMATIC != 0"); + + if (auto) { + SetDelayTime(NativeMethods.TTDT_AUTOMATIC, delayTimes[NativeMethods.TTDT_AUTOMATIC]); + delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); + delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); + delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); + } + else { + for (int i=1; i < delayTimes.Length; i++) { + if (delayTimes[i] >= 1) { + SetDelayTime(i, delayTimes[i]); + } + } + } + + // Set active status + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (active == true) ? 1 : 0, 0); + + if (BackColor != SystemColors.Info) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(BackColor), 0); + } + if (ForeColor != SystemColors.InfoText) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(ForeColor), 0); + } + if (toolTipIcon > 0 || !String.IsNullOrEmpty(toolTipTitle)) { + // If the title is null/empty, the icon won't display. + string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); + } + } + + private void CreateAllRegions() { + Control[] ctls = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(ctls, 0); + for (int i=0; i 0; + bool handlesCreated = ctl.IsHandleCreated + && TopLevelControl != null + && TopLevelControl.IsHandleCreated; + if (!created.ContainsKey(ctl) && captionValid + && handlesCreated && !DesignMode) { + + //Call the Sendmessage thru a function.. + SetToolInfo(ctl, caption); + created[ctl] = ctl; + } + if (ctl.IsHandleCreated && topLevelControl == null) { + // Remove first to purge any duplicates... + // + ctl.MouseMove -= new MouseEventHandler(this.MouseMove); + ctl.MouseMove += new MouseEventHandler(this.MouseMove); + } + } + + private void MouseMove(object sender, MouseEventArgs me) { + Control ctl = (Control)sender; + + if (!created.ContainsKey(ctl) + && ctl.IsHandleCreated + && TopLevelControl != null) { + + CreateRegion(ctl); + } + + if (created.ContainsKey(ctl)) { + ctl.MouseMove -= new MouseEventHandler(this.MouseMove); + } + } + + + + /// + /// + /// Destroys the handle for this control. + /// + /// + /// Required by Label to destroy the handle for the toolTip added for AutoEllipses. + internal void DestroyHandle() { + + if (GetHandleCreated()) { + window.DestroyHandle(); + } + } + + private void DestroyRegion(Control ctl) { + + // when the toplevelControl is a form and is Modal, the Handle of the tooltip is releasedbefore we come here. + // In such a case the tool wont get deleted from the tooltip. + // So we dont check "Handle" in the handlesCreate but check it only foe Non-Nodal dialogs later + + bool handlesCreated = ctl.IsHandleCreated + && topLevelControl != null + && topLevelControl.IsHandleCreated + && !this.isDisposing; + + Form topForm = topLevelControl as Form; + if (topForm == null || (topForm != null && !topForm.Modal)) + { + handlesCreated = handlesCreated && GetHandleCreated(); + } + + if (created.ContainsKey(ctl) + && handlesCreated && !DesignMode) { + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_DELTOOL, 0, GetMinTOOLINFO(ctl)); + created.Remove(ctl); + } + } + + /// + /// + /// + /// + /// Disposes of the + /// component. + /// + /// + protected override void Dispose(bool disposing) { + + if (disposing) { + this.isDisposing = true; + try { + ClearTopLevelControlEvents(); + StopTimer(); + + // always destroy the handle... + // + DestroyHandle(); + RemoveAll(); + + window = null; + + //Unhook the DeactiveEvent... + // Lets find the Form for associated Control ... + // and hook up to the Deactivated event to Hide the Shown tooltip + Form baseFrom = TopLevelControl as Form; + if (baseFrom != null) { + baseFrom.Deactivate -= new EventHandler(this.BaseFormDeactivate); + } + } + finally { + this.isDisposing = false; + } + } + base.Dispose(disposing); + } + + /// + /// + /// Returns the delayTime based on the NativeMethods.TTDT_* values. + /// + /// + internal int GetDelayTime(int type) { + if (GetHandleCreated()) { + return (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETDELAYTIME, type, 0); + } + else { + return delayTimes[type]; + } + } + + // Can't be a property -- there is another method called GetHandleCreated + internal bool GetHandleCreated() { + return (window != null ? window.Handle != IntPtr.Zero: false); + } + + /// + /// + /// Returns a new instance of the TOOLINFO_T structure with the minimum + /// required data to uniquely identify a region. This is used primarily + /// for delete operations. NOTE: This cannot force the creation of a handle. + /// + /// + private NativeMethods.TOOLINFO_TOOLTIP GetMinTOOLINFO(Control ctl) { + return this.GetMinToolInfoForHandle(ctl.Handle); + } + + private NativeMethods.TOOLINFO_TOOLTIP GetMinToolInfoForTool(IWin32Window tool) { + return this.GetMinToolInfoForHandle(tool.Handle); + } + + private NativeMethods.TOOLINFO_TOOLTIP GetMinToolInfoForHandle(IntPtr handle) { + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.hwnd = handle; + ti.uFlags |= NativeMethods.TTF_IDISHWND; + ti.uId = handle; + return ti; + } + + /// + /// + /// Returns a detailed TOOLINFO_TOOLTIP structure that represents the specified + /// region. NOTE: This may force the creation of a handle. + /// If the out parameter allocatedString has been set to true, It is the responsibility of the caller + /// to free the string buffer referenced by lpszText (using Marshal.FreeHGlobal). + /// + /// + private NativeMethods.TOOLINFO_TOOLTIP GetTOOLINFO(Control ctl, string caption, out bool allocatedString) { + allocatedString = false; + NativeMethods.TOOLINFO_TOOLTIP ti = GetMinTOOLINFO(ctl); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.uFlags |= NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + + // RightToLeft reading order + // + Control richParent = TopLevelControl; + if (richParent != null && richParent.RightToLeft == RightToLeft.Yes && !ctl.IsMirrored) { + //Indicates that the ToolTip text will be displayed in the opposite direction + //to the text in the parent window. + ti.uFlags |= NativeMethods.TTF_RTLREADING; + } + + if (ctl is TreeView || ctl is ListView) { + TreeView tv = ctl as TreeView; + if (tv != null && tv.ShowNodeToolTips) { + ti.lpszText = NativeMethods.InvalidIntPtr; + } + else { + ListView lv = ctl as ListView; + if (lv != null && lv.ShowItemToolTips) { + ti.lpszText = NativeMethods.InvalidIntPtr; + } + else { + ti.lpszText = Marshal.StringToHGlobalAuto(caption); + allocatedString = true; + } + } + } + else { + ti.lpszText = Marshal.StringToHGlobalAuto(caption); + allocatedString = true; + } + + + return ti; + } + + private NativeMethods.TOOLINFO_TOOLTIP GetWinTOOLINFO(IntPtr hWnd) { + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.hwnd = hWnd; + ti.uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + + // RightToLeft reading order + // + Control richParent = TopLevelControl; + if (richParent != null && richParent.RightToLeft == RightToLeft.Yes) { + bool isWindowMirrored = ((unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hWnd), NativeMethods.GWL_STYLE)) & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL); + //Indicates that the ToolTip text will be displayed in the opposite direction + //to the text in the parent window. + if (!isWindowMirrored) { + ti.uFlags |= NativeMethods.TTF_RTLREADING; + } + } + + ti.uId = ti.hwnd; + return ti; + } + + /// + /// + /// + /// Retrieves the text associated with the specified control. + /// + /// + [ + DefaultValue(""), + Localizable(true), + SRDescription(SR.ToolTipToolTipDescr), + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string GetToolTip(Control control) { + if (control == null) { + return String.Empty; + } + TipInfo tt = (TipInfo)tools[control]; + if (tt == null || tt.Caption == null) { + return ""; + } + else { + return tt.Caption; + } + } + + /// + /// + /// Returns the HWND of the window that is at the specified point. This + /// handles special cases where one Control owns multiple HWNDs (i.e. ComboBox). + /// + /// + private IntPtr GetWindowFromPoint(Point screenCoords, ref bool success) { + Control baseVar = TopLevelControl; + //Special case the ActiveX Controls. + if (baseVar != null && baseVar.IsActiveX) + { + //find the matching HWnd matching the ScreenCoord and find if the Control has a Tooltip. + IntPtr hwndControl = UnsafeNativeMethods.WindowFromPoint(screenCoords.X, screenCoords.Y); + if (hwndControl != IntPtr.Zero) + { + Control currentControl = Control.FromHandleInternal(hwndControl); + if (currentControl != null && tools != null && tools.ContainsKey(currentControl)) + { + return hwndControl; + } + } + return IntPtr.Zero; + } + + IntPtr baseHwnd = IntPtr.Zero; + + if (baseVar != null) { + baseHwnd = baseVar.Handle; + } + + IntPtr hwnd = IntPtr.Zero; + bool finalMatch = false; + while (!finalMatch) { + Point pt = screenCoords; + if (baseVar != null) { + pt = baseVar.PointToClientInternal(screenCoords); + } + IntPtr found = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, baseHwnd), pt.X, pt.Y, NativeMethods.CWP_SKIPINVISIBLE); + + if (found == baseHwnd) { + hwnd = found; + finalMatch = true; + } + else if (found == IntPtr.Zero) { + finalMatch = true; + } + else { + baseVar = Control.FromHandleInternal(found); + if (baseVar == null) { + baseVar = Control.FromChildHandleInternal(found); + if (baseVar != null) { + hwnd = baseVar.Handle; + } + finalMatch = true; + } + else { + baseHwnd = baseVar.Handle; + } + } + } + + if (hwnd != IntPtr.Zero) { + Control ctl = Control.FromHandleInternal(hwnd); + if (ctl != null) { + Control current = ctl; + while (current != null && current.Visible) { + current = current.ParentInternal; + } + if (current != null) { + hwnd = IntPtr.Zero; + } + success = true; + } + } + + return hwnd; + } + + private void OnTopLevelPropertyChanged(object s, EventArgs e) { + ClearTopLevelControlEvents(); + this.topLevelControl = null; + + // We must re-aquire this control. If the existing top level control's handle + // was never created, but the new parent has a handle, if we don't re-get + // the top level control here we won't ever create the tooltip handle. + // + this.topLevelControl = TopLevelControl; + } + + /// + /// + /// + /// + private void RecreateHandle() { + if (!DesignMode) { + if (GetHandleCreated()) { + DestroyHandle(); + } + created.Clear(); + CreateHandle(); + CreateAllRegions(); + } + } + + + /// + /// + /// + /// Removes all of the tooltips currently associated + /// with the control. + /// + /// + public void RemoveAll() { + Control[] regions = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(regions, 0); + for (int i=0; i + /// + /// Sets the delayTime based on the NativeMethods.TTDT_* values. + /// + /// + private void SetDelayTime(int type, int time) { + if (type == NativeMethods.TTDT_AUTOMATIC) { + auto = true; + } + else { + auto = false; + } + + delayTimes[type] = time; + + if (GetHandleCreated() && time >= 0) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETDELAYTIME, type, time); + + // Update everyone else if automatic is set... we need to do this + // to preserve value in case of handle recreation. + // + if (auto) { + delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); + delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); + delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); + } + } + else if (auto) { + AdjustBaseFromAuto(); + } + } + + /// + /// + /// + /// Associates text with the specified control. + /// + /// + public void SetToolTip(Control control, string caption) { + + TipInfo info = new TipInfo(caption, TipInfo.Type.Auto); + SetToolTipInternal(control, info); + + } + + /// + /// + /// + /// Associates + /// + /// Returns true if the AutomaticDelay property should be persisted. + /// + /// + private bool ShouldSerializeAutomaticDelay() { + if (auto) { + if (AutomaticDelay != DEFAULT_DELAY) { + return true; + } + } + return false; + } + + /// + /// + /// Returns true if the AutoPopDelay property should be persisted. + /// + /// + private bool ShouldSerializeAutoPopDelay() { + return !auto; + } + + /// + /// + /// Returns true if the InitialDelay property should be persisted. + /// + /// + private bool ShouldSerializeInitialDelay() { + return !auto; + } + + /// + /// + /// Returns true if the ReshowDelay property should be persisted. + /// + /// + private bool ShouldSerializeReshowDelay() { + return !auto; + } + + + /// + /// + /// Shows a tooltip for specified text, window, and hotspot + /// + /// + private void ShowTooltip(string text, IWin32Window win, int duration) { + if (win == null) { + throw new ArgumentNullException("win"); + } + + Control associatedControl = win as Control; + if (associatedControl != null) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(associatedControl, associatedControl.Handle), ref r); + + Cursor currentCursor = Cursor.CurrentInternal; + Point cursorLocation = Cursor.Position; + Point p = cursorLocation; + + Screen screen = Screen.FromPoint(cursorLocation); + + // Place the tool tip on the associated control if its not already there + if ( cursorLocation.X < r.left || cursorLocation.X > r.right || + cursorLocation.Y < r.top || cursorLocation.Y > r.bottom ) { + + // calculate the dimensions of the visible rectangle which + // is used to estimate the upper x,y of the tooltip placement + NativeMethods.RECT visibleRect = new NativeMethods.RECT(); + visibleRect.left = (r.left < screen.WorkingArea.Left) ? screen.WorkingArea.Left:r.left; + visibleRect.top = (r.top < screen.WorkingArea.Top) ? screen.WorkingArea.Top:r.top; + visibleRect.right = (r.right > screen.WorkingArea.Right) ? screen.WorkingArea.Right:r.right; + visibleRect.bottom = (r.bottom > screen.WorkingArea.Bottom) ? screen.WorkingArea.Bottom:r.bottom; + + p.X = visibleRect.left + (visibleRect.right - visibleRect.left)/2; + p.Y = visibleRect.top + (visibleRect.bottom - visibleRect.top)/2; + associatedControl.PointToClientInternal(p); + SetTrackPosition(p.X, p.Y); + SetTool(win, text, TipInfo.Type.SemiAbsolute, p); + + if (duration > 0) { + StartTimer(window, duration); + } + + } + else { + + TipInfo tt = (TipInfo)tools[associatedControl]; + if (tt == null) { + tt = new TipInfo(text, TipInfo.Type.SemiAbsolute); + } + else { + tt.TipType |= TipInfo.Type.SemiAbsolute; + tt.Caption = text; + } + tt.Position = p; + + if (duration > 0) { + if (this.originalPopupDelay == 0) { + this.originalPopupDelay = AutoPopDelay; + } + AutoPopDelay = duration; + } + SetToolTipInternal(associatedControl, tt); + } + } + } + + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window) { + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + // Check if the foreground window is the TopLevelWindow + if (HasAllWindowsPermission && IsWindowActive(window)) { + ShowTooltip(text, window, 0); + } + + } + + /// + /// + /// + /// Associates with the specified control + /// and displays it for the specified duration. + /// + /// + public void Show(string text, IWin32Window window, int duration) { + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + ShowTooltip(text, window, duration); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, Point point) { + if (window == null) { + throw new ArgumentNullException("window"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + //Set The ToolTips... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + point.X; + int pointY = r.top + point.Y; + + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, Point point, int duration) { + if (window == null) { + throw new ArgumentNullException("window"); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + //Set The ToolTips... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + point.X; + int pointY = r.top + point.Y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + StartTimer(window, duration); + } + } + + + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, int x, int y) { + if (window == null) { + throw new ArgumentNullException("window"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + x; + int pointY = r.top + y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, int x, int y, int duration) { + if (window == null) { + throw new ArgumentNullException("window"); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + x; + int pointY = r.top + y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + StartTimer(window, duration); + } + } + + internal void ShowKeyboardToolTip(string text, IKeyboardToolTip tool, int duration) { + if (tool == null) { + throw new ArgumentNullException(nameof(tool)); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException(nameof(duration), SR.GetString(SR.InvalidLowBoundArgumentEx, nameof(duration), (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + Rectangle toolRectangle = tool.GetNativeScreenRectangle(); + // At first, place the tooltip at the middle of the tool (default location) + int pointX = (toolRectangle.Left + toolRectangle.Right) / 2; + int pointY = (toolRectangle.Top + toolRectangle.Bottom) / 2; + this.SetTool(tool.GetOwnerWindow(), text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + + // Then look for a better ToolTip location + Size bubbleSize; + if (this.TryGetBubbleSize(tool, toolRectangle, out bubbleSize)) { + Point optimalPoint = this.GetOptimalToolTipPosition(tool, toolRectangle, bubbleSize.Width, bubbleSize.Height); + + // The optimal point should be used as a tracking position + pointX = optimalPoint.X; + pointY = optimalPoint.Y; + + // Update TipInfo for the tool with optimal position + TipInfo tipInfo = (TipInfo)(tools[tool] ?? tools[tool.GetOwnerWindow()]); + tipInfo.Position = new Point(pointX, pointY); + + // Ensure that the tooltip bubble is moved to the optimal position even when a mouse tooltip is being replaced with a keyboard tooltip + this.Reposition(optimalPoint, bubbleSize); + } + + SetTrackPosition(pointX, pointY); + StartTimer(tool.GetOwnerWindow(), duration); + } + + private bool TryGetBubbleSize(IKeyboardToolTip tool, Rectangle toolRectangle, out Size bubbleSize) { + // Get bubble size to use it for optimal position calculation + IntPtr bubbleSizeInt = UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_GETBUBBLESIZE, 0, this.GetMinToolInfoForTool(tool.GetOwnerWindow())); + if (bubbleSizeInt.ToInt32() != NativeMethods.S_FALSE) { + int width = NativeMethods.Util.LOWORD(bubbleSizeInt); + int height = NativeMethods.Util.HIWORD(bubbleSizeInt); + bubbleSize = new Size(width, height); + return true; + } + else { + bubbleSize = Size.Empty; + return false; + } + } + + private Point GetOptimalToolTipPosition(IKeyboardToolTip tool, Rectangle toolRectangle, int width, int height) { + // Possible tooltip locations are tied to the tool rectangle bounds + int centeredX = toolRectangle.Left + toolRectangle.Width / 2 - width / 2; // tooltip will be aligned with tool vertically + int centeredY = toolRectangle.Top + toolRectangle.Height / 2 - height / 2; // tooltip will be aligned with tool horizontally + + Rectangle[] possibleLocations = new Rectangle[LOCATION_TOTAL]; + possibleLocations[TOP_LOCATION_INDEX] = new Rectangle(centeredX, toolRectangle.Top - height, width, height); + possibleLocations[RIGHT_LOCATION_INDEX] = new Rectangle(toolRectangle.Right, centeredY, width, height); + possibleLocations[BOTTOM_LOCATION_INDEX] = new Rectangle(centeredX, toolRectangle.Bottom, width, height); + possibleLocations[LEFT_LOCATION_INDEX] = new Rectangle(toolRectangle.Left - width, centeredY, width, height); + + + // Neighboring tools should not be overlapped (ideally) by tooltip + IList neighboringToolsRectangles = tool.GetNeighboringToolsRectangles(); + + // Weights are used to determine which one of the possible location overlaps least area of the neighboring tools + long[] locationWeights = new long[LOCATION_TOTAL]; + + // Check if the possible locations intersect with the neighboring tools + for (int i = 0; i < possibleLocations.Length; i++) { + foreach (Rectangle neighboringToolRectangle in neighboringToolsRectangles) { + Rectangle intersection = Rectangle.Intersect(possibleLocations[i], neighboringToolRectangle); + checked { + locationWeights[i] += Math.Abs((long)intersection.Width * intersection.Height); // Intersection is a weight + } + } + } + + // Calculate clipped area of possible locations i.e. area which is located outside the screen area + Rectangle screenBounds = SystemInformation.VirtualScreen; + long[] locationClippedAreas = new long[LOCATION_TOTAL]; + for (int i = 0; i < possibleLocations.Length; i++) { + Rectangle locationAreaWithinScreen = Rectangle.Intersect(screenBounds, possibleLocations[i]); + checked { + locationClippedAreas[i] = (Math.Abs((long)possibleLocations[i].Width) - Math.Abs((long)locationAreaWithinScreen.Width)) + * (Math.Abs((long)possibleLocations[i].Height) - Math.Abs((long)locationAreaWithinScreen.Height)); + } + } + + // Calculate area of possible locations within top level control rectangle + long[] locationWithinTopControlAreas = new long[LOCATION_TOTAL]; + Rectangle topContainerBounds = ((IKeyboardToolTip)this.TopLevelControl)?.GetNativeScreenRectangle() ?? Rectangle.Empty; + if (!topContainerBounds.IsEmpty) { + for (int i = 0; i < possibleLocations.Length; i++) { + Rectangle locationWithinTopControlRectangle = Rectangle.Intersect(topContainerBounds, possibleLocations[i]); + checked { + locationWithinTopControlAreas[i] = Math.Abs((long)locationWithinTopControlRectangle.Height * locationWithinTopControlRectangle.Width); + } + } + } + + // Pick optimal location + long leastWeight = locationWeights[0]; + long leastClippedArea = locationClippedAreas[0]; + long biggestAreaWithinTopControl = locationWithinTopControlAreas[0]; + int locationIndex = 0; + Rectangle optimalLocation = possibleLocations[0]; + bool rtlEnabled = tool.HasRtlModeEnabled(); + for (int i = 1; i < possibleLocations.Length; i++) { + if (this.IsCompetingLocationBetter(leastClippedArea, leastWeight, biggestAreaWithinTopControl, locationIndex, + locationClippedAreas[i], locationWeights[i], locationWithinTopControlAreas[i], i, + rtlEnabled)) { + leastWeight = locationWeights[i]; + leastClippedArea = locationClippedAreas[i]; + biggestAreaWithinTopControl = locationWithinTopControlAreas[i]; + locationIndex = i; + optimalLocation = possibleLocations[i]; + } + } + + return new Point(optimalLocation.Left, optimalLocation.Top); + } + + private bool IsCompetingLocationBetter(long originalLocationClippedArea, + long originalLocationWeight, + long originalLocationAreaWithinTopControl, + int originalIndex, + long competingLocationClippedArea, + long competingLocationWeight, + long competingLocationAreaWithinTopControl, + int competingIndex, + bool rtlEnabled) { + // Prefer location with less clipped area + if (competingLocationClippedArea < originalLocationClippedArea) { + return true; + } + // Otherwise prefer location with less weight + else if (competingLocationWeight < originalLocationWeight) { + return true; + } + else if (competingLocationWeight == originalLocationWeight && competingLocationClippedArea == originalLocationClippedArea) { + // Prefer locations located within top level control + if (competingLocationAreaWithinTopControl > originalLocationAreaWithinTopControl) { + return true; + } + else if (competingLocationAreaWithinTopControl == originalLocationAreaWithinTopControl) { + switch (originalIndex) { + case TOP_LOCATION_INDEX: + // Top location is the least preferred location + return true; + case BOTTOM_LOCATION_INDEX: + // Right and Left locations are preferred instead of Bottom location + if (competingIndex == LEFT_LOCATION_INDEX || competingIndex == RIGHT_LOCATION_INDEX) { + return true; + } + break; + case RIGHT_LOCATION_INDEX: + // When RTL is enabled Left location is preferred + if (rtlEnabled && competingIndex == LEFT_LOCATION_INDEX) { + return true; + } + break; + case LEFT_LOCATION_INDEX: + // When RTL is disabled Right location is preferred + if (!rtlEnabled && competingIndex == RIGHT_LOCATION_INDEX) { + return true; + } + break; + default: + throw new NotSupportedException("Unsupported location index value"); + } + } + } + + return false; + } + + /// + /// Private Function to encapsulate TTM_TRACKPOSITION so that this doesnt fire an extra POP event + /// + private void SetTrackPosition(int pointX, int pointY) + { + try + { + trackPosition = true; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(pointX, pointY)); + } + finally + { + trackPosition= false; + } + } + + /// + /// + /// + /// Hides with the specified control. + /// + /// + public void Hide(IWin32Window win) { + if (win == null) { + throw new ArgumentNullException("win"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission) { + if (window == null) + { + return; + } + + // VSWhidbey 562045: Only send message if we actually have a ToolTip at this point. + if (GetHandleCreated()) { + IntPtr hWnd = Control.GetSafeHandle(win); + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKACTIVATE, 0, GetWinTOOLINFO(hWnd)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_DELTOOL, 0, GetWinTOOLINFO(hWnd)); + } + StopTimer(); + + //Check if the passed in IWin32Window is a Control... + Control tool = win as Control; + if (tool == null) { + owners.Remove(win.Handle); + } + else + { + if (tools.ContainsKey(tool)) { + SetToolInfo(tool, GetToolTip(tool)); + } + else { + owners.Remove(win.Handle); + } + // Lets find the Form for associated Control ... + // and hook up to the Deactivated event to Hide the Shown tooltip + Form baseFrom = tool.FindFormInternal(); + if (baseFrom != null) { + baseFrom.Deactivate -= new EventHandler(this.BaseFormDeactivate); + } + } + + // VSWhidbey 562045 - clear off the toplevel control. + ClearTopLevelControlEvents(); + topLevelControl = null; + } + } + + private void BaseFormDeactivate(System.Object sender, System.EventArgs e){ + HideAllToolTips(); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutFormDeactivation(this); + } + } + + private void HideAllToolTips() { + Control[] ctls = new Control[owners.Values.Count]; + owners.Values.CopyTo(ctls, 0); + for (int i=0; i + /// + /// Starts the timer hiding Positioned ToolTips + /// + private void StartTimer(IWin32Window owner, int interval) { + + if (timer == null) { + timer = new ToolTipTimer(owner); + // Add the timer handler + timer.Tick += new EventHandler(TimerHandler); + } + timer.Interval = interval; + timer.Start(); + } + + /// + /// + /// Stops the timer for hiding Positioned ToolTips + /// + protected void StopTimer() { + //VSWhidbey 363538: hold a local ref to timer + //so that a posted message doesn't null this + //out during disposal. + ToolTipTimer timerRef = timer; + + if (timerRef != null) { + timerRef.Stop(); + timerRef.Dispose(); + timer = null; + } + } + + /// + /// + /// Generates updown events when the timer calls this function. + /// + private void TimerHandler(object source, EventArgs args) { + Hide(((ToolTipTimer)source).Host); + } + + /// + /// + /// + /// Finalizes garbage collection. + /// + /// + ~ToolTip() { + DestroyHandle(); + } + + /// + /// + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + " InitialDelay: " + InitialDelay.ToString(CultureInfo.CurrentCulture) + ", ShowAlways: " + ShowAlways.ToString(CultureInfo.CurrentCulture); + } + + private void Reposition(Point tipPosition, Size tipSize) { + Point moveToLocation = tipPosition; + Screen screen = Screen.FromPoint(moveToLocation); + + // Re-adjust the X position of the tool tip if it bleeds off the screen working area + if (moveToLocation.X + tipSize.Width > screen.WorkingArea.Right) { + moveToLocation.X = screen.WorkingArea.Right - tipSize.Width; + } + + // re-adjust the Y position of the tool tip if it bleeds off the screen working area. + if (moveToLocation.Y + tipSize.Height> screen.WorkingArea.Bottom) { + moveToLocation.Y = screen.WorkingArea.Bottom - tipSize.Height; + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + moveToLocation.X, moveToLocation.Y, tipSize.Width, tipSize.Height, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOOWNERZORDER); + } + + /// + /// + /// Handles the WM_MOVE message. + /// + /// + private void WmMove() { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) + { + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + TipInfo tt = (TipInfo)tools[win]; + if (win == null || tt==null) { + return; + } + + // Treeview handles its own ToolTips. + TreeView treeView = win as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + return; + } + } + + // Reposition the tooltip when its about to be shown.. since the tooltip can go out of screen workingarea bounds + // Reposition would check the bounds for us. + if (tt.Position != Point.Empty) + { + Reposition(tt.Position, r.Size); + } + } + } + + + /// + /// + /// Handles the WM_MOUSEACTIVATE message. + /// + /// + private void WmMouseActivate(ref Message msg) { + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(win, Control.GetSafeHandle(win)), ref r); + Point cursorLocation = Cursor.Position; + + // Do not activate the mouse if its within the bounds of the + // the associated tool + if (cursorLocation.X >= r.left && cursorLocation.X <= r.right && + cursorLocation.Y >= r.top && cursorLocation.Y <= r.bottom) { + msg.Result = (IntPtr)NativeMethods.MA_NOACTIVATE; + } + } + } + + + + /// + /// + /// Handles the WM_WINDOWFROMPOINT message. + /// + /// + private void WmWindowFromPoint(ref Message msg) { + NativeMethods.POINT sc = (NativeMethods.POINT)msg.GetLParam(typeof(NativeMethods.POINT)); + Point screenCoords = new Point(sc.x, sc.y); + bool result = false; + msg.Result = GetWindowFromPoint(screenCoords, ref result); + } + + + + /// + /// + /// Handles the TTN_SHOW message. + /// + /// + private void WmShow() { + + + //Get the Bounds.... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + Control toolControl = win as Control; + + Size currentTooltipSize = r.Size; + PopupEventArgs e = new PopupEventArgs(win, toolControl, IsBalloon, currentTooltipSize); + OnPopup(e); + + DataGridView dataGridView = toolControl as DataGridView; + if (dataGridView != null && dataGridView.CancelToolTipPopup(this)) + { + // The dataGridView cancelled the tooltip. + e.Cancel = true; + } + + // We need to re-get the rectangle of the tooltip here because + // any of the tooltip attributes/properties could have been updated + // during the popup event; in which case the size of the tooltip is + // affected. e.ToolTipSize is respected over r.Size + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + currentTooltipSize = (e.ToolTipSize == currentTooltipSize) ? r.Size:e.ToolTipSize; + + + if (IsBalloon) { + // Get the text display rectangle + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ADJUSTRECT, 1, ref r); + if (r.Size.Height > currentTooltipSize.Height) currentTooltipSize.Height = r.Size.Height; + } + + // Set the max possible size of the tooltip to the size we received. + // This prevents the operating system from drawing incorrect rectangles + // when determing the correct display rectangle. VSWhidbey + + if (currentTooltipSize != r.Size) + { + Screen screen = Screen.FromPoint(Cursor.Position); + int maxwidth = (IsBalloon) ? + Math.Min(currentTooltipSize.Width-2*XBALLOONOFFSET, screen.WorkingArea.Width): + Math.Min(currentTooltipSize.Width, screen.WorkingArea.Width); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, maxwidth); + } + + if (e.Cancel) { + cancelled = true; + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); + + } + else { + cancelled = false; + // Only width/height changes are respected, so set top,left to what we got earlier + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + r.left, r.top, currentTooltipSize.Width, currentTooltipSize.Height, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); + } + } + } + + /// + /// + /// Handles the WM_WINDOWPOSCHANGED message. + /// We need to Hide the window since the native tooltip actually calls SetWindowPos in its TTN_SHOW even if we cancel showing the + /// tooltip : Hence we need to listen to the WindowPosChanged message can hide the window ourselves. + /// Refer to VsWhidbey : 490044 for more details. + /// + /// + private bool WmWindowPosChanged() { + if (cancelled) + { + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), NativeMethods.SW_HIDE); + return true; + } + return false; + } + + + + /// + /// + /// Handles the WM_WINDOWPOSCHANGING message. + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + if (cancelled || isDisposing) + { + return; + } + + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + + Cursor currentCursor = Cursor.CurrentInternal; + Point cursorPos = Cursor.Position; + + + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null || !IsWindowActive(win)) { + return; + } + + TipInfo tt = null; + if (win != null) + { + tt = (TipInfo)tools[win]; + if (tt == null) { + return; + } + + // Treeview handles its own ToolTips. + TreeView treeView = win as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + return; + } + } + } + + if (IsBalloon) { + wp->cx += 2*XBALLOONOFFSET; + return; + } + + if ( (tt.TipType & TipInfo.Type.Auto) != 0 && window != null ) + { + window.DefWndProc(ref m); + return; + } + + if ( ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0) && tt.Position == Point.Empty ) { + + Screen screen = Screen.FromPoint(cursorPos); + if (currentCursor != null) + { + wp->x = cursorPos.X; + // Since HotSpot requires a security demand .. we assert this and revert Assert immediately + try { + IntSecurity.ObjectFromWin32Handle.Assert(); + + wp->y = cursorPos.Y; + if (wp->y + wp->cy + currentCursor.Size.Height - currentCursor.HotSpot.Y > screen.WorkingArea.Bottom) { + wp->y = cursorPos.Y - wp->cy; + } + else { + wp->y = cursorPos.Y + currentCursor.Size.Height - currentCursor.HotSpot.Y; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + if (wp->x + wp->cx >screen.WorkingArea.Right) { + wp->x = screen.WorkingArea.Right - wp->cx; + } + + } + else if ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0 && tt.Position != Point.Empty) { + + Screen screen = Screen.FromPoint(tt.Position); + wp->x = tt.Position.X; + if (wp->x + wp->cx >screen.WorkingArea.Right) { + wp->x = screen.WorkingArea.Right - wp->cx; + } + wp->y = tt.Position.Y; + + if (wp->y + wp->cy > screen.WorkingArea.Bottom) { + wp->y = screen.WorkingArea.Bottom - wp->cy; + } + } + } + + m.Result = IntPtr.Zero; + } + + /// + /// + /// Called just before the tooltip is hidden + /// + /// + private void WmPop() { + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + Control control = win as Control; + TipInfo tt = (TipInfo)tools[win]; + if (tt == null) { + return; + } + + // Must reset the maxwidth to the screen size. + // This is required to resolve VSWhidbey bug# 363408 + if ((tt.TipType & TipInfo.Type.Auto) != 0 || (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) { + Screen screen = Screen.FromPoint(Cursor.Position); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, screen.WorkingArea.Width); + } + + // For non-auto tips (those showned through + // the show(...) methods, we need to dissassociate + // them from the tip control. + if ((tt.TipType & TipInfo.Type.Auto) == 0) { + + tools.Remove(control); + owners.Remove(win.Handle); + + control.HandleCreated -= new EventHandler(this.HandleCreated); + control.HandleDestroyed -= new EventHandler(this.HandleDestroyed); + created.Remove(control); + + if (originalPopupDelay != 0) { + AutoPopDelay = originalPopupDelay; + originalPopupDelay = 0; + } + } + else { + // Clear all other flags except for the + // Auto flag to ensure automatic tips can still show + tt.TipType = TipInfo.Type.Auto; + tt.Position = Point.Empty; + tools[control] = tt; + } + } + } + + /// + /// + /// WNDPROC + /// + /// + private void WndProc(ref Message msg) { + + + switch (msg.Msg) { + + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) msg.GetLParam(typeof(NativeMethods.NMHDR)); + if (nmhdr.code == NativeMethods.TTN_SHOW && !trackPosition) { + WmShow(); + } + else if (nmhdr.code == NativeMethods.TTN_POP) { + WmPop(); + if (window != null) { + window.DefWndProc(ref msg); + } + } + break; + + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref msg); + break; + + case NativeMethods.WM_WINDOWPOSCHANGED: + if (!WmWindowPosChanged() && window != null) + { + window.DefWndProc(ref msg); + } + break; + + case NativeMethods.WM_MOUSEACTIVATE: + WmMouseActivate(ref msg); + break; + + case NativeMethods.WM_MOVE: + WmMove(); + break; + + case NativeMethods.TTM_WINDOWFROMPOINT: + WmWindowFromPoint(ref msg); + break; + + case NativeMethods.WM_PRINTCLIENT: + goto case NativeMethods.WM_PAINT; + + case NativeMethods.WM_PAINT: + if (ownerDraw && !isBalloon && !trackPosition) + { + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + IntPtr dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this,Handle),ref ps); + Graphics g = Graphics.FromHdcInternal(dc); + try { + Rectangle bounds = new Rectangle(ps.rcPaint_left,ps.rcPaint_top, + ps.rcPaint_right - ps.rcPaint_left, + ps.rcPaint_bottom - ps.rcPaint_top); + if (bounds == Rectangle.Empty ) { + return; + } + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti)); + if (ret != 0) { + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + Control ac = Control.FromHandleInternal(ti.hwnd); + if (win == null) { + win = (IWin32Window)ac; + } + Font font; + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font = Font.FromHfont(UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_GETFONT, 0, 0)); + } + catch (ArgumentException) { + // VSWhidbey 209345 - if the current default tooltip font is a non-TrueType font, then + // Font.FromHfont throws this exception, so fall back to the default control font. + font = Control.DefaultFont; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + OnDraw(new DrawToolTipEventArgs(g, win, ac, bounds, GetToolTip(ac), + BackColor, ForeColor, font)); + + break; + } + } + finally + { + g.Dispose(); + UnsafeNativeMethods.EndPaint(new HandleRef(this,Handle),ref ps); + } + } + + //If not OwnerDraw, fall through + goto default; + default: + if (window != null) { + window.DefWndProc(ref msg); + } + break; + } + } + + /// + /// + /// + /// + private class ToolTipNativeWindow : NativeWindow { + ToolTip control; + + internal ToolTipNativeWindow(ToolTip control) { + this.control = control; + } + + + protected override void WndProc(ref Message m) { + if (control != null) { + control.WndProc(ref m); + } + } + } + + private class ToolTipTimer : Timer { + IWin32Window host; + + public ToolTipTimer(IWin32Window owner) : base(){ + this.host = owner; + } + + public IWin32Window Host { + get { + return host; + } + } + } + + + private class TipInfo { + + [Flags] + public enum Type { + None = 0x0000, + Auto = 0x0001, + Absolute = 0x0002, + SemiAbsolute = 0x0004 + }; + + public Type TipType = Type.Auto; + private string caption; + private string designerText; + public Point Position = Point.Empty; + + public TipInfo(string caption, Type type) { + this.caption = caption; + this.TipType = type; + if (type == Type.Auto) { + this.designerText = caption; + } + } + + public string Caption { + get { + return ((this.TipType & (Type.Absolute | Type.SemiAbsolute)) != 0) ? caption:designerText; + } + set { + this.caption = value; + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolTip.cs.back b/WindowsForms/Managed/System/WinForms/ToolTip.cs.back new file mode 100644 index 000000000..feacc0b6d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolTip.cs.back @@ -0,0 +1,2722 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System; + using System.Windows.Forms; + using System.Windows.Forms.Design; + using Hashtable = System.Collections.Hashtable; + using System.Drawing; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Drawing.Design; + using System.Globalization; + using Collections.Generic; + + /// + /// + /// + /// Provides a small pop-up window containing a line of text + /// that describes the purpose of a tool or control (usually represented as a + /// graphical + /// object) in a program. + /// + /// + [ + ProvideProperty("ToolTip", typeof(Control)), + DefaultEvent("Popup"), + ToolboxItemFilter("System.Windows.Forms"), + SRDescription(SR.DescriptionToolTip) + ] + public class ToolTip : Component, IExtenderProvider { + + const int DEFAULT_DELAY = 500; + const int RESHOW_RATIO = 5; + const int AUTOPOP_RATIO = 10; + + const int XBALLOONOFFSET = 10; + const int YBALLOONOFFSET = 8; + + private const int TOP_LOCATION_INDEX = 0; + private const int RIGHT_LOCATION_INDEX = 1; + private const int BOTTOM_LOCATION_INDEX = 2; + private const int LEFT_LOCATION_INDEX = 3; + private const int LOCATION_TOTAL = 4; + + Hashtable tools = new Hashtable(); + int[] delayTimes = new int[4]; + bool auto = true; + bool showAlways = false; + ToolTipNativeWindow window = null; + Control topLevelControl = null; + bool active = true; + bool ownerDraw = false; + object userData; + Color backColor = SystemColors.Info; + Color foreColor = SystemColors.InfoText; + bool isBalloon; + bool isDisposing; + string toolTipTitle = string.Empty; + ToolTipIcon toolTipIcon = (ToolTipIcon)0; + ToolTipTimer timer; + Hashtable owners = new Hashtable(); + bool stripAmpersands = false; + bool useAnimation = true; + bool useFading = true; + int originalPopupDelay = 0; + + // setting TTM_TRACKPOSITION will cause redundant POP and Draw Messages.. + // Hence we gaurd against this by having this private Flag.. + bool trackPosition = false; + + PopupEventHandler onPopup; + DrawToolTipEventHandler onDraw; + + // Adding a tool twice breaks the ToolTip, so we need to track which + // tools are created to prevent this... + // + Hashtable created = new Hashtable(); + + private bool cancelled = false; + + /// + /// + /// + /// Initializes a new instance of the class, given the container. + /// + /// + public ToolTip(IContainer cont) : this() { + if (cont == null) { + throw new ArgumentNullException("cont"); + } + + cont.Add(this); + } + + /// + /// + /// + /// Initializes a new instance of the class in its default state. + /// + /// + public ToolTip() { + window = new ToolTipNativeWindow(this); + auto = true; + delayTimes[NativeMethods.TTDT_AUTOMATIC] = DEFAULT_DELAY; + AdjustBaseFromAuto(); + } + + /// + /// + /// + /// Gets or sets a value indicating whether the control is currently active. + /// + /// + [ + SRDescription(SR.ToolTipActiveDescr), + DefaultValue(true) + ] + public bool Active { + get { + return active; + } + + set { + if (active != value) { + active = value; + + //lets not actually activate the tooltip if we're in the designer (just set the value) + if (!DesignMode && GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (value==true)? 1: 0, 0); + } + } + } + } + + internal void HideToolTip(IKeyboardToolTip currentTool) { + this.Hide(currentTool.GetOwnerWindow()); + } + + /// + /// + /// + /// Gets or sets + /// the time (in milliseconds) that passes before the appears. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipAutomaticDelayDescr), + DefaultValue(DEFAULT_DELAY) + ] + public int AutomaticDelay { + get { + return delayTimes[NativeMethods.TTDT_AUTOMATIC]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("AutomaticDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutomaticDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_AUTOMATIC, value); + } + } + + internal string GetCaptionForTool(Control tool) { + Debug.Assert(tool != null, "tool should not be null"); + return ((TipInfo)this.tools[tool])?.Caption; + } + + /// + /// + /// + /// Gets or sets the initial delay for the control. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipAutoPopDelayDescr) + ] + public int AutoPopDelay { + get { + return delayTimes[NativeMethods.TTDT_AUTOPOP]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("AutoPopDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutoPopDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_AUTOPOP, value); + } + } + + /// + /// + /// + /// Gets or sets the BackColor for the control. + /// + /// + [ + SRDescription(SR.ToolTipBackColorDescr), + DefaultValue(typeof(Color),"Info") + ] + public Color BackColor { + get { + return backColor; + } + + set { + backColor = value; + if (GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(backColor), 0); + } + } + } + + /// + /// + /// The createParams to create the window. + /// + /// + protected virtual CreateParams CreateParams { + [ + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) + ] + get { + CreateParams cp = new CreateParams(); + if (TopLevelControl != null && !TopLevelControl.IsDisposed) { + cp.Parent = TopLevelControl.Handle; + } + cp.ClassName = NativeMethods.TOOLTIPS_CLASS; + if (showAlways) { + cp.Style = NativeMethods.TTS_ALWAYSTIP; + } + if (isBalloon) { + cp.Style |= NativeMethods.TTS_BALLOON; + } + if (!stripAmpersands) { + cp.Style |= NativeMethods.TTS_NOPREFIX; + } + if (!useAnimation) { + cp.Style |= NativeMethods.TTS_NOANIMATE; + } + if (!useFading) { + cp.Style |= NativeMethods.TTS_NOFADE; + } + cp.ExStyle = 0; + cp.Caption = null; + + return cp; + } + } + + /// + /// + /// + /// Gets or sets the ForeColor for the control. + /// + /// + [ + SRDescription(SR.ToolTipForeColorDescr), + DefaultValue(typeof(Color),"InfoText") + ] + public Color ForeColor { + get { + return foreColor; + } + + set { + if (value.IsEmpty) { + throw new ArgumentException(SR.GetString(SR.ToolTipEmptyColor, "ForeColor")); + } + + foreColor = value; + if (GetHandleCreated()) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(foreColor), 0); + } + + } + } + + internal IntPtr Handle { + get { + if (!GetHandleCreated()) { + CreateHandle(); + } + return window.Handle; + } + } + + /// + /// SECREVIEW: Pattern to check if the caller has a permission without bubbling up a security exception + /// + private bool HasAllWindowsPermission { + get { + try { + IntSecurity.AllWindows.Demand(); + return true; + } + catch (SecurityException) { + } + + return false; + } + } + + /// + /// + /// + /// Gets or sets the IsBalloon for the control. + /// + /// + [ + SRDescription(SR.ToolTipIsBalloonDescr), + DefaultValue(false) + ] + public bool IsBalloon { + get { + return isBalloon; + } + + set { + if (isBalloon != value) { + isBalloon = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + + } + } + + // refer VsWhidbey 498263: ToolTips should be shown only on active Windows. + private bool IsWindowActive(IWin32Window window) + { + Control windowControl = window as Control; + // We want to enter in the IF block only if ShowParams does not return SW_SHOWNOACTIVATE. + // for ToolStripDropDown ShowParams returns SW_SHOWNOACTIVATE, in which case we DONT want to check IsWindowActive and hence return true. + if (windowControl != null && + (windowControl.ShowParams & 0xF) != NativeMethods.SW_SHOWNOACTIVATE) + { + IntPtr hWnd = UnsafeNativeMethods.GetActiveWindow(); + IntPtr rootHwnd =UnsafeNativeMethods.GetAncestor(new HandleRef(window, window.Handle), NativeMethods.GA_ROOT); + if (hWnd != rootHwnd) + { + TipInfo tt = (TipInfo)tools[windowControl]; + if (tt != null && (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) + { + tools.Remove(windowControl); + DestroyRegion(windowControl); + } + return false; + } + } + return true; + } + + /// + /// + /// + /// Gets or sets the initial delay for + /// the + /// control. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipInitialDelayDescr) + ] + public int InitialDelay { + get { + return delayTimes[NativeMethods.TTDT_INITIAL]; + } + + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("InitialDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "InitialDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_INITIAL, value); + } + } + + /// + /// + /// Indicates whether the ToolTip will be drawn by the system or the user. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.ToolTipOwnerDrawDescr) + ] + public bool OwnerDraw + { + get + { + return ownerDraw; + } + [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] + set + { + ownerDraw = value; + } + } + + /// + /// + /// + /// Gets or sets the length of time (in milliseconds) that + /// it takes subsequent ToolTip instances to appear as the mouse pointer moves from + /// one ToolTip region to + /// another. + /// + /// + [ + RefreshProperties(RefreshProperties.All), + SRDescription(SR.ToolTipReshowDelayDescr) + ] + public int ReshowDelay { + get { + return delayTimes[NativeMethods.TTDT_RESHOW]; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("ReshowDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "ReshowDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + SetDelayTime(NativeMethods.TTDT_RESHOW, value); + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the + /// appears even when its parent control is not active. + /// + /// + [ + DefaultValue(false), + SRDescription(SR.ToolTipShowAlwaysDescr) + ] + public bool ShowAlways { + get { + return showAlways; + } + set { + if (showAlways != value) { + showAlways = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + + /// + /// + /// + /// When set to true, any ampersands in the Text property are not displayed. + /// + /// + [ + SRDescription(SR.ToolTipStripAmpersandsDescr), + Browsable(true), + DefaultValue(false) + ] + public bool StripAmpersands { + get { + return stripAmpersands; + } + set { + if (stripAmpersands != value) { + stripAmpersands = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// + /// Gets or sets an Icon on the ToolTip. + /// + /// + [ + DefaultValue(ToolTipIcon.None), + SRDescription(SR.ToolTipToolTipIconDescr) + ] + public ToolTipIcon ToolTipIcon { + get { + return toolTipIcon; + } + set { + if (toolTipIcon != value) { + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolTipIcon.None, (int)ToolTipIcon.Error)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolTipIcon)); + } + toolTipIcon = value; + if (toolTipIcon > 0 && GetHandleCreated()) { + // If the title is null/empty, the icon won't display. + string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); + + // Tooltip need to be updated to reflect the changes in the icon because + // this operation directly affects the size of the tooltip + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); + + } + } + } + } + + + /// + /// + /// Gets or sets the title of the ToolTip. + /// + /// + [ + DefaultValue(""), + SRDescription(SR.ToolTipTitleDescr) + ] + public string ToolTipTitle + { + get + { + return toolTipTitle; + } + set + { + if (value == null) + { + value = string.Empty; + } + + if (toolTipTitle != value) + { + toolTipTitle = value; + if (GetHandleCreated()) + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, toolTipTitle); + + // Tooltip need to be updated to reflect the changes in the titletext because + // this operation directly affects the size of the tooltip + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); + } + } + } + } + + private Control TopLevelControl { + get { + Control baseVar = null; + if (topLevelControl == null) { + Control[] regions = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(regions, 0); + if (regions != null && regions.Length > 0) { + for (int i=0; i + /// + /// + /// When set to true, animations are used when tooltip is shown or hidden. + /// + /// + [ + SRDescription(SR.ToolTipUseAnimationDescr), + Browsable(true), + DefaultValue(true) + ] + public bool UseAnimation { + get { + return useAnimation; + } + set { + if (useAnimation != value) { + useAnimation = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + + /// + /// + /// + /// When set to true, a fade effect is used when tooltips are shown or hidden. + /// + /// + [ + SRDescription(SR.ToolTipUseFadingDescr), + Browsable(true), + DefaultValue(true) + ] + public bool UseFading { + get { + return useFading; + } + set { + if (useFading != value) { + useFading = value; + if (GetHandleCreated()) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Fires in OwnerDraw mode when the tooltip needs to be drawn. + /// + [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipDrawEventDescr)] + public event DrawToolTipEventHandler Draw + { + add + { + onDraw += value; + } + remove + { + onDraw -= value; + } + } + + /// + /// + /// Fires when the tooltip is just about to be shown. + /// + [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipPopupEventDescr)] + public event PopupEventHandler Popup + { + add + { + onPopup += value; + } + remove + { + onPopup -= value; + } + } + + + /// + /// + /// Adjusts the other delay values based on the Automatic value. + /// + /// + private void AdjustBaseFromAuto() { + delayTimes[NativeMethods.TTDT_RESHOW] = delayTimes[NativeMethods.TTDT_AUTOMATIC] / RESHOW_RATIO; + delayTimes[NativeMethods.TTDT_AUTOPOP] = delayTimes[NativeMethods.TTDT_AUTOMATIC] * AUTOPOP_RATIO; + delayTimes[NativeMethods.TTDT_INITIAL] = delayTimes[NativeMethods.TTDT_AUTOMATIC]; + } + + private void HandleCreated(object sender, EventArgs eventargs) { + // Reset the toplevel control when the owner's handle is recreated. + ClearTopLevelControlEvents(); + topLevelControl = null; + + Control control = (Control)sender; + this.CreateRegion(control); + this.CheckNativeToolTip(control); + this.CheckCompositeControls(control); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Hook(control, this); + } + } + + private void CheckNativeToolTip(Control associatedControl) { + + //Wait for the Handle Creation.. + if (!GetHandleCreated()) { + return; + } + + TreeView treeView = associatedControl as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + treeView.SetToolTip(this,GetToolTip(associatedControl)); + } + } + + if (associatedControl is ToolBar) { + ((ToolBar)associatedControl).SetToolTip(this); + } + + TabControl tabControl = associatedControl as TabControl; + if (tabControl!= null) { + if (tabControl.ShowToolTips) + { + tabControl.SetToolTip(this, GetToolTip(associatedControl)); + } + } + + if (associatedControl is ListView) { + ((ListView)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); + } + + if (associatedControl is StatusBar) { + ((StatusBar)associatedControl).SetToolTip(this); + } + + // Label now has its own Tooltip for AutoEllipsis... + // So this control too falls in special casing... + // We need to disable the LABEL AutoEllipsis tooltip and show + // this tooltip always... + if (associatedControl is Label) { + ((Label)associatedControl).SetToolTip(this); + } + + + } + + private void CheckCompositeControls(Control associatedControl) { + if (associatedControl is UpDownBase) { + ((UpDownBase)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); + } + } + + private void HandleDestroyed(object sender, EventArgs eventargs) { + Control control = (Control)sender; + this.DestroyRegion(control); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.Unhook(control, this); + } + } + + /// + /// + /// Fires the Draw event. + /// + private void OnDraw(DrawToolTipEventArgs e) + { + if(onDraw != null) + { + onDraw(this,e); + } + } + + + /// + /// + /// Fires the Popup event. + /// + private void OnPopup(PopupEventArgs e) + { + if(onPopup != null) + { + onPopup(this,e); + } + } + + private void TopLevelCreated(object sender, EventArgs eventargs) { + CreateHandle(); + CreateAllRegions(); + } + + private void TopLevelDestroyed(object sender, EventArgs eventargs) { + DestoyAllRegions(); + DestroyHandle(); + } + + /// + /// + /// Returns true if the tooltip can offer an extender property to the + /// specified target component. + /// + /// + public bool CanExtend(object target) { + if (target is Control && + !(target is ToolTip)) { + + return true; + } + return false; + } + + private void ClearTopLevelControlEvents() { + + if (this.topLevelControl != null) { + this.topLevelControl.ParentChanged -= new EventHandler(this.OnTopLevelPropertyChanged); + this.topLevelControl.HandleCreated -= new EventHandler(this.TopLevelCreated); + this.topLevelControl.HandleDestroyed -= new EventHandler(this.TopLevelDestroyed); + } + } + + /// + /// + /// Creates the handle for the control. + /// + /// + private void CreateHandle() { + if (GetHandleCreated()) { + return; + } + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try { + + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TAB_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + + CreateParams cp = CreateParams; // Avoid reentrant call to CreateHandle (VSWhidbey 570764) + if (GetHandleCreated()) { + return; + } + window.CreateHandle(cp); + } + finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + + // If in ownerDraw mode, we don't want the default border. + if (ownerDraw) { + int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + style &= ~NativeMethods.WS_BORDER; + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); + } + + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + + Debug.Assert(NativeMethods.TTDT_AUTOMATIC == 0, "TTDT_AUTOMATIC != 0"); + + if (auto) { + SetDelayTime(NativeMethods.TTDT_AUTOMATIC, delayTimes[NativeMethods.TTDT_AUTOMATIC]); + delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); + delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); + delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); + } + else { + for (int i=1; i < delayTimes.Length; i++) { + if (delayTimes[i] >= 1) { + SetDelayTime(i, delayTimes[i]); + } + } + } + + // Set active status + // + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (active == true) ? 1 : 0, 0); + + if (BackColor != SystemColors.Info) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(BackColor), 0); + } + if (ForeColor != SystemColors.InfoText) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(ForeColor), 0); + } + if (toolTipIcon > 0 || !String.IsNullOrEmpty(toolTipTitle)) { + // If the title is null/empty, the icon won't display. + string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); + } + } + + private void CreateAllRegions() { + Control[] ctls = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(ctls, 0); + for (int i=0; i 0; + bool handlesCreated = ctl.IsHandleCreated + && TopLevelControl != null + && TopLevelControl.IsHandleCreated; + if (!created.ContainsKey(ctl) && captionValid + && handlesCreated && !DesignMode) { + + //Call the Sendmessage thru a function.. + SetToolInfo(ctl, caption); + created[ctl] = ctl; + } + if (ctl.IsHandleCreated && topLevelControl == null) { + // Remove first to purge any duplicates... + // + ctl.MouseMove -= new MouseEventHandler(this.MouseMove); + ctl.MouseMove += new MouseEventHandler(this.MouseMove); + } + } + + private void MouseMove(object sender, MouseEventArgs me) { + Control ctl = (Control)sender; + + if (!created.ContainsKey(ctl) + && ctl.IsHandleCreated + && TopLevelControl != null) { + + CreateRegion(ctl); + } + + if (created.ContainsKey(ctl)) { + ctl.MouseMove -= new MouseEventHandler(this.MouseMove); + } + } + + + + /// + /// + /// Destroys the handle for this control. + /// + /// + /// Required by Label to destroy the handle for the toolTip added for AutoEllipses. + internal void DestroyHandle() { + + if (GetHandleCreated()) { + window.DestroyHandle(); + } + } + + private void DestroyRegion(Control ctl) { + + // when the toplevelControl is a form and is Modal, the Handle of the tooltip is releasedbefore we come here. + // In such a case the tool wont get deleted from the tooltip. + // So we dont check "Handle" in the handlesCreate but check it only foe Non-Nodal dialogs later + + bool handlesCreated = ctl.IsHandleCreated + && topLevelControl != null + && topLevelControl.IsHandleCreated + && !this.isDisposing; + + Form topForm = topLevelControl as Form; + if (topForm == null || (topForm != null && !topForm.Modal)) + { + handlesCreated = handlesCreated && GetHandleCreated(); + } + + if (created.ContainsKey(ctl) + && handlesCreated && !DesignMode) { + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_DELTOOL, 0, GetMinTOOLINFO(ctl)); + created.Remove(ctl); + } + } + + /// + /// + /// + /// + /// Disposes of the + /// component. + /// + /// + protected override void Dispose(bool disposing) { + + if (disposing) { + this.isDisposing = true; + try { + ClearTopLevelControlEvents(); + StopTimer(); + + // always destroy the handle... + // + DestroyHandle(); + RemoveAll(); + + window = null; + + //Unhook the DeactiveEvent... + // Lets find the Form for associated Control ... + // and hook up to the Deactivated event to Hide the Shown tooltip + Form baseFrom = TopLevelControl as Form; + if (baseFrom != null) { + baseFrom.Deactivate -= new EventHandler(this.BaseFormDeactivate); + } + } + finally { + this.isDisposing = false; + } + } + base.Dispose(disposing); + } + + /// + /// + /// Returns the delayTime based on the NativeMethods.TTDT_* values. + /// + /// + internal int GetDelayTime(int type) { + if (GetHandleCreated()) { + return (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETDELAYTIME, type, 0); + } + else { + return delayTimes[type]; + } + } + + // Can't be a property -- there is another method called GetHandleCreated + internal bool GetHandleCreated() { + return (window != null ? window.Handle != IntPtr.Zero: false); + } + + /// + /// + /// Returns a new instance of the TOOLINFO_T structure with the minimum + /// required data to uniquely identify a region. This is used primarily + /// for delete operations. NOTE: This cannot force the creation of a handle. + /// + /// + private NativeMethods.TOOLINFO_TOOLTIP GetMinTOOLINFO(Control ctl) { + return this.GetMinToolInfoForHandle(ctl.Handle); + } + + private NativeMethods.TOOLINFO_TOOLTIP GetMinToolInfoForTool(IWin32Window tool) { + return this.GetMinToolInfoForHandle(tool.Handle); + } + + private NativeMethods.TOOLINFO_TOOLTIP GetMinToolInfoForHandle(IntPtr handle) { + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.hwnd = handle; + ti.uFlags |= NativeMethods.TTF_IDISHWND; + ti.uId = handle; + return ti; + } + + /// + /// + /// Returns a detailed TOOLINFO_TOOLTIP structure that represents the specified + /// region. NOTE: This may force the creation of a handle. + /// If the out parameter allocatedString has been set to true, It is the responsibility of the caller + /// to free the string buffer referenced by lpszText (using Marshal.FreeHGlobal). + /// + /// + private NativeMethods.TOOLINFO_TOOLTIP GetTOOLINFO(Control ctl, string caption, out bool allocatedString) { + allocatedString = false; + NativeMethods.TOOLINFO_TOOLTIP ti = GetMinTOOLINFO(ctl); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.uFlags |= NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + + // RightToLeft reading order + // + Control richParent = TopLevelControl; + if (richParent != null && richParent.RightToLeft == RightToLeft.Yes && !ctl.IsMirrored) { + //Indicates that the ToolTip text will be displayed in the opposite direction + //to the text in the parent window. + ti.uFlags |= NativeMethods.TTF_RTLREADING; + } + + if (ctl is TreeView || ctl is ListView) { + TreeView tv = ctl as TreeView; + if (tv != null && tv.ShowNodeToolTips) { + ti.lpszText = NativeMethods.InvalidIntPtr; + } + else { + ListView lv = ctl as ListView; + if (lv != null && lv.ShowItemToolTips) { + ti.lpszText = NativeMethods.InvalidIntPtr; + } + else { + ti.lpszText = Marshal.StringToHGlobalAuto(caption); + allocatedString = true; + } + } + } + else { + ti.lpszText = Marshal.StringToHGlobalAuto(caption); + allocatedString = true; + } + + + return ti; + } + + private NativeMethods.TOOLINFO_TOOLTIP GetWinTOOLINFO(IntPtr hWnd) { + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + ti.hwnd = hWnd; + ti.uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; + + // RightToLeft reading order + // + Control richParent = TopLevelControl; + if (richParent != null && richParent.RightToLeft == RightToLeft.Yes) { + bool isWindowMirrored = ((unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hWnd), NativeMethods.GWL_STYLE)) & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL); + //Indicates that the ToolTip text will be displayed in the opposite direction + //to the text in the parent window. + if (!isWindowMirrored) { + ti.uFlags |= NativeMethods.TTF_RTLREADING; + } + } + + ti.uId = ti.hwnd; + return ti; + } + + /// + /// + /// + /// Retrieves the text associated with the specified control. + /// + /// + [ + DefaultValue(""), + Localizable(true), + SRDescription(SR.ToolTipToolTipDescr), + Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) + ] + public string GetToolTip(Control control) { + if (control == null) { + return String.Empty; + } + TipInfo tt = (TipInfo)tools[control]; + if (tt == null || tt.Caption == null) { + return ""; + } + else { + return tt.Caption; + } + } + + /// + /// + /// Returns the HWND of the window that is at the specified point. This + /// handles special cases where one Control owns multiple HWNDs (i.e. ComboBox). + /// + /// + private IntPtr GetWindowFromPoint(Point screenCoords, ref bool success) { + Control baseVar = TopLevelControl; + //Special case the ActiveX Controls. + if (baseVar != null && baseVar.IsActiveX) + { + //find the matching HWnd matching the ScreenCoord and find if the Control has a Tooltip. + IntPtr hwndControl = UnsafeNativeMethods.WindowFromPoint(screenCoords.X, screenCoords.Y); + if (hwndControl != IntPtr.Zero) + { + Control currentControl = Control.FromHandleInternal(hwndControl); + if (currentControl != null && tools != null && tools.ContainsKey(currentControl)) + { + return hwndControl; + } + } + return IntPtr.Zero; + } + + IntPtr baseHwnd = IntPtr.Zero; + + if (baseVar != null) { + baseHwnd = baseVar.Handle; + } + + IntPtr hwnd = IntPtr.Zero; + bool finalMatch = false; + while (!finalMatch) { + Point pt = screenCoords; + if (baseVar != null) { + pt = baseVar.PointToClientInternal(screenCoords); + } + IntPtr found = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, baseHwnd), pt.X, pt.Y, NativeMethods.CWP_SKIPINVISIBLE); + + if (found == baseHwnd) { + hwnd = found; + finalMatch = true; + } + else if (found == IntPtr.Zero) { + finalMatch = true; + } + else { + baseVar = Control.FromHandleInternal(found); + if (baseVar == null) { + baseVar = Control.FromChildHandleInternal(found); + if (baseVar != null) { + hwnd = baseVar.Handle; + } + finalMatch = true; + } + else { + baseHwnd = baseVar.Handle; + } + } + } + + if (hwnd != IntPtr.Zero) { + Control ctl = Control.FromHandleInternal(hwnd); + if (ctl != null) { + Control current = ctl; + while (current != null && current.Visible) { + current = current.ParentInternal; + } + if (current != null) { + hwnd = IntPtr.Zero; + } + success = true; + } + } + + return hwnd; + } + + private void OnTopLevelPropertyChanged(object s, EventArgs e) { + ClearTopLevelControlEvents(); + this.topLevelControl = null; + + // We must re-aquire this control. If the existing top level control's handle + // was never created, but the new parent has a handle, if we don't re-get + // the top level control here we won't ever create the tooltip handle. + // + this.topLevelControl = TopLevelControl; + } + + /// + /// + /// + /// + private void RecreateHandle() { + if (!DesignMode) { + if (GetHandleCreated()) { + DestroyHandle(); + } + created.Clear(); + CreateHandle(); + CreateAllRegions(); + } + } + + + /// + /// + /// + /// Removes all of the tooltips currently associated + /// with the control. + /// + /// + public void RemoveAll() { + Control[] regions = new Control[tools.Keys.Count]; + tools.Keys.CopyTo(regions, 0); + for (int i=0; i + /// + /// Sets the delayTime based on the NativeMethods.TTDT_* values. + /// + /// + private void SetDelayTime(int type, int time) { + if (type == NativeMethods.TTDT_AUTOMATIC) { + auto = true; + } + else { + auto = false; + } + + delayTimes[type] = time; + + if (GetHandleCreated() && time >= 0) { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETDELAYTIME, type, time); + + // Update everyone else if automatic is set... we need to do this + // to preserve value in case of handle recreation. + // + if (auto) { + delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); + delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); + delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); + } + } + else if (auto) { + AdjustBaseFromAuto(); + } + } + + /// + /// + /// + /// Associates text with the specified control. + /// + /// + public void SetToolTip(Control control, string caption) { + + TipInfo info = new TipInfo(caption, TipInfo.Type.Auto); + SetToolTipInternal(control, info); + + } + + /// + /// + /// + /// Associates + /// + /// Returns true if the AutomaticDelay property should be persisted. + /// + /// + private bool ShouldSerializeAutomaticDelay() { + if (auto) { + if (AutomaticDelay != DEFAULT_DELAY) { + return true; + } + } + return false; + } + + /// + /// + /// Returns true if the AutoPopDelay property should be persisted. + /// + /// + private bool ShouldSerializeAutoPopDelay() { + return !auto; + } + + /// + /// + /// Returns true if the InitialDelay property should be persisted. + /// + /// + private bool ShouldSerializeInitialDelay() { + return !auto; + } + + /// + /// + /// Returns true if the ReshowDelay property should be persisted. + /// + /// + private bool ShouldSerializeReshowDelay() { + return !auto; + } + + + /// + /// + /// Shows a tooltip for specified text, window, and hotspot + /// + /// + private void ShowTooltip(string text, IWin32Window win, int duration) { + if (win == null) { + throw new ArgumentNullException("win"); + } + + Control associatedControl = win as Control; + if (associatedControl != null) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(associatedControl, associatedControl.Handle), ref r); + + Cursor currentCursor = Cursor.CurrentInternal; + Point cursorLocation = Cursor.Position; + Point p = cursorLocation; + + Screen screen = Screen.FromPoint(cursorLocation); + + // Place the tool tip on the associated control if its not already there + if ( cursorLocation.X < r.left || cursorLocation.X > r.right || + cursorLocation.Y < r.top || cursorLocation.Y > r.bottom ) { + + // calculate the dimensions of the visible rectangle which + // is used to estimate the upper x,y of the tooltip placement + NativeMethods.RECT visibleRect = new NativeMethods.RECT(); + visibleRect.left = (r.left < screen.WorkingArea.Left) ? screen.WorkingArea.Left:r.left; + visibleRect.top = (r.top < screen.WorkingArea.Top) ? screen.WorkingArea.Top:r.top; + visibleRect.right = (r.right > screen.WorkingArea.Right) ? screen.WorkingArea.Right:r.right; + visibleRect.bottom = (r.bottom > screen.WorkingArea.Bottom) ? screen.WorkingArea.Bottom:r.bottom; + + p.X = visibleRect.left + (visibleRect.right - visibleRect.left)/2; + p.Y = visibleRect.top + (visibleRect.bottom - visibleRect.top)/2; + associatedControl.PointToClientInternal(p); + SetTrackPosition(p.X, p.Y); + SetTool(win, text, TipInfo.Type.SemiAbsolute, p); + + if (duration > 0) { + StartTimer(window, duration); + } + + } + else { + + TipInfo tt = (TipInfo)tools[associatedControl]; + if (tt == null) { + tt = new TipInfo(text, TipInfo.Type.SemiAbsolute); + } + else { + tt.TipType |= TipInfo.Type.SemiAbsolute; + tt.Caption = text; + } + tt.Position = p; + + if (duration > 0) { + if (this.originalPopupDelay == 0) { + this.originalPopupDelay = AutoPopDelay; + } + AutoPopDelay = duration; + } + SetToolTipInternal(associatedControl, tt); + } + } + } + + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window) { + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + // Check if the foreground window is the TopLevelWindow + if (HasAllWindowsPermission && IsWindowActive(window)) { + ShowTooltip(text, window, 0); + } + + } + + /// + /// + /// + /// Associates with the specified control + /// and displays it for the specified duration. + /// + /// + public void Show(string text, IWin32Window window, int duration) { + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + ShowTooltip(text, window, duration); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, Point point) { + if (window == null) { + throw new ArgumentNullException("window"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + //Set The ToolTips... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + point.X; + int pointY = r.top + point.Y; + + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, Point point, int duration) { + if (window == null) { + throw new ArgumentNullException("window"); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + //Set The ToolTips... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + point.X; + int pointY = r.top + point.Y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + StartTimer(window, duration); + } + } + + + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, int x, int y) { + if (window == null) { + throw new ArgumentNullException("window"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + x; + int pointY = r.top + y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + } + } + + /// + /// + /// + /// Associates with the specified control and displays it. + /// + /// + public void Show(string text, IWin32Window window, int x, int y, int duration) { + if (window == null) { + throw new ArgumentNullException("window"); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission && IsWindowActive(window)) { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); + int pointX = r.left + x; + int pointY = r.top + y; + SetTrackPosition(pointX, pointY); + SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + StartTimer(window, duration); + } + } + + internal void ShowKeyboardToolTip(string text, IKeyboardToolTip tool, int duration) { + if (tool == null) { + throw new ArgumentNullException(nameof(tool)); + } + if (duration < 0) { + throw new ArgumentOutOfRangeException(nameof(duration), SR.GetString(SR.InvalidLowBoundArgumentEx, nameof(duration), (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + Rectangle toolRectangle = tool.GetNativeScreenRectangle(); + // At first, place the tooltip at the middle of the tool (default location) + int pointX = (toolRectangle.Left + toolRectangle.Right) / 2; + int pointY = (toolRectangle.Top + toolRectangle.Bottom) / 2; + this.SetTool(tool.GetOwnerWindow(), text, TipInfo.Type.Absolute, new Point(pointX, pointY)); + + // Then look for a better ToolTip location + Size bubbleSize; + if (this.TryGetBubbleSize(tool, toolRectangle, out bubbleSize)) { + Point optimalPoint = this.GetOptimalToolTipPosition(tool, toolRectangle, bubbleSize.Width, bubbleSize.Height); + + // The optimal point should be used as a tracking position + pointX = optimalPoint.X; + pointY = optimalPoint.Y; + + // Update TipInfo for the tool with optimal position + TipInfo tipInfo = (TipInfo)(tools[tool] ?? tools[tool.GetOwnerWindow()]); + tipInfo.Position = new Point(pointX, pointY); + + // Ensure that the tooltip bubble is moved to the optimal position even when a mouse tooltip is being replaced with a keyboard tooltip + this.Reposition(optimalPoint, bubbleSize); + } + + SetTrackPosition(pointX, pointY); + StartTimer(tool.GetOwnerWindow(), duration); + } + + private bool TryGetBubbleSize(IKeyboardToolTip tool, Rectangle toolRectangle, out Size bubbleSize) { + // Get bubble size to use it for optimal position calculation + IntPtr bubbleSizeInt = UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_GETBUBBLESIZE, 0, this.GetMinToolInfoForTool(tool.GetOwnerWindow())); + if (bubbleSizeInt.ToInt32() != NativeMethods.S_FALSE) { + int width = NativeMethods.Util.LOWORD(bubbleSizeInt); + int height = NativeMethods.Util.HIWORD(bubbleSizeInt); + bubbleSize = new Size(width, height); + return true; + } + else { + bubbleSize = Size.Empty; + return false; + } + } + + private Point GetOptimalToolTipPosition(IKeyboardToolTip tool, Rectangle toolRectangle, int width, int height) { + // Possible tooltip locations are tied to the tool rectangle bounds + int centeredX = toolRectangle.Left + toolRectangle.Width / 2 - width / 2; // tooltip will be aligned with tool vertically + int centeredY = toolRectangle.Top + toolRectangle.Height / 2 - height / 2; // tooltip will be aligned with tool horizontally + + Rectangle[] possibleLocations = new Rectangle[LOCATION_TOTAL]; + possibleLocations[TOP_LOCATION_INDEX] = new Rectangle(centeredX, toolRectangle.Top - height, width, height); + possibleLocations[RIGHT_LOCATION_INDEX] = new Rectangle(toolRectangle.Right, centeredY, width, height); + possibleLocations[BOTTOM_LOCATION_INDEX] = new Rectangle(centeredX, toolRectangle.Bottom, width, height); + possibleLocations[LEFT_LOCATION_INDEX] = new Rectangle(toolRectangle.Left - width, centeredY, width, height); + + + // Neighboring tools should not be overlapped (ideally) by tooltip + IList neighboringToolsRectangles = tool.GetNeighboringToolsRectangles(); + + // Weights are used to determine which one of the possible location overlaps least area of the neighboring tools + long[] locationWeights = new long[LOCATION_TOTAL]; + + // Check if the possible locations intersect with the neighboring tools + for (int i = 0; i < possibleLocations.Length; i++) { + foreach (Rectangle neighboringToolRectangle in neighboringToolsRectangles) { + Rectangle intersection = Rectangle.Intersect(possibleLocations[i], neighboringToolRectangle); + checked { + locationWeights[i] += Math.Abs((long)intersection.Width * intersection.Height); // Intersection is a weight + } + } + } + + // Calculate clipped area of possible locations i.e. area which is located outside the screen area + Rectangle screenBounds = SystemInformation.VirtualScreen; + long[] locationClippedAreas = new long[LOCATION_TOTAL]; + for (int i = 0; i < possibleLocations.Length; i++) { + Rectangle locationAreaWithinScreen = Rectangle.Intersect(screenBounds, possibleLocations[i]); + checked { + locationClippedAreas[i] = (Math.Abs((long)possibleLocations[i].Width) - Math.Abs((long)locationAreaWithinScreen.Width)) + * (Math.Abs((long)possibleLocations[i].Height) - Math.Abs((long)locationAreaWithinScreen.Height)); + } + } + + // Calculate area of possible locations within top level control rectangle + long[] locationWithinTopControlAreas = new long[LOCATION_TOTAL]; + Rectangle topContainerBounds = ((IKeyboardToolTip)this.TopLevelControl)?.GetNativeScreenRectangle() ?? Rectangle.Empty; + if (!topContainerBounds.IsEmpty) { + for (int i = 0; i < possibleLocations.Length; i++) { + Rectangle locationWithinTopControlRectangle = Rectangle.Intersect(topContainerBounds, possibleLocations[i]); + checked { + locationWithinTopControlAreas[i] = Math.Abs((long)locationWithinTopControlRectangle.Height * locationWithinTopControlRectangle.Width); + } + } + } + + // Pick optimal location + long leastWeight = locationWeights[0]; + long leastClippedArea = locationClippedAreas[0]; + long biggestAreaWithinTopControl = locationWithinTopControlAreas[0]; + int locationIndex = 0; + Rectangle optimalLocation = possibleLocations[0]; + bool rtlEnabled = tool.HasRtlModeEnabled(); + for (int i = 1; i < possibleLocations.Length; i++) { + if (this.IsCompetingLocationBetter(leastClippedArea, leastWeight, biggestAreaWithinTopControl, locationIndex, + locationClippedAreas[i], locationWeights[i], locationWithinTopControlAreas[i], i, + rtlEnabled)) { + leastWeight = locationWeights[i]; + leastClippedArea = locationClippedAreas[i]; + biggestAreaWithinTopControl = locationWithinTopControlAreas[i]; + locationIndex = i; + optimalLocation = possibleLocations[i]; + } + } + + return new Point(optimalLocation.Left, optimalLocation.Top); + } + + private bool IsCompetingLocationBetter(long originalLocationClippedArea, + long originalLocationWeight, + long originalLocationAreaWithinTopControl, + int originalIndex, + long competingLocationClippedArea, + long competingLocationWeight, + long competingLocationAreaWithinTopControl, + int competingIndex, + bool rtlEnabled) { + // Prefer location with less clipped area + if (competingLocationClippedArea < originalLocationClippedArea) { + return true; + } + // Otherwise prefer location with less weight + else if (competingLocationWeight < originalLocationWeight) { + return true; + } + else if (competingLocationWeight == originalLocationWeight && competingLocationClippedArea == originalLocationClippedArea) { + // Prefer locations located within top level control + if (competingLocationAreaWithinTopControl > originalLocationAreaWithinTopControl) { + return true; + } + else if (competingLocationAreaWithinTopControl == originalLocationAreaWithinTopControl) { + switch (originalIndex) { + case TOP_LOCATION_INDEX: + // Top location is the least preferred location + return true; + case BOTTOM_LOCATION_INDEX: + // Right and Left locations are preferred instead of Bottom location + if (competingIndex == LEFT_LOCATION_INDEX || competingIndex == RIGHT_LOCATION_INDEX) { + return true; + } + break; + case RIGHT_LOCATION_INDEX: + // When RTL is enabled Left location is preferred + if (rtlEnabled && competingIndex == LEFT_LOCATION_INDEX) { + return true; + } + break; + case LEFT_LOCATION_INDEX: + // When RTL is disabled Right location is preferred + if (!rtlEnabled && competingIndex == RIGHT_LOCATION_INDEX) { + return true; + } + break; + default: + throw new NotSupportedException("Unsupported location index value"); + } + } + } + + return false; + } + + /// + /// Private Function to encapsulate TTM_TRACKPOSITION so that this doesnt fire an extra POP event + /// + private void SetTrackPosition(int pointX, int pointY) + { + try + { + trackPosition = true; + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(pointX, pointY)); + } + finally + { + trackPosition= false; + } + } + + /// + /// + /// + /// Hides with the specified control. + /// + /// + public void Hide(IWin32Window win) { + if (win == null) { + throw new ArgumentNullException("win"); + } + + //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 + if (HasAllWindowsPermission) { + if (window == null) + { + return; + } + + // VSWhidbey 562045: Only send message if we actually have a ToolTip at this point. + if (GetHandleCreated()) { + IntPtr hWnd = Control.GetSafeHandle(win); + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKACTIVATE, 0, GetWinTOOLINFO(hWnd)); + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_DELTOOL, 0, GetWinTOOLINFO(hWnd)); + } + StopTimer(); + + //Check if the passed in IWin32Window is a Control... + Control tool = win as Control; + if (tool == null) { + owners.Remove(win.Handle); + } + else + { + if (tools.ContainsKey(tool)) { + SetToolInfo(tool, GetToolTip(tool)); + } + else { + owners.Remove(win.Handle); + } + // Lets find the Form for associated Control ... + // and hook up to the Deactivated event to Hide the Shown tooltip + Form baseFrom = tool.FindFormInternal(); + if (baseFrom != null) { + baseFrom.Deactivate -= new EventHandler(this.BaseFormDeactivate); + } + } + + // VSWhidbey 562045 - clear off the toplevel control. + ClearTopLevelControlEvents(); + topLevelControl = null; + } + } + + private void BaseFormDeactivate(System.Object sender, System.EventArgs e){ + HideAllToolTips(); + + if (!AccessibilityImprovements.UseLegacyToolTipDisplay) { + KeyboardToolTipStateMachine.Instance.NotifyAboutFormDeactivation(this); + } + } + + private void HideAllToolTips() { + Control[] ctls = new Control[owners.Values.Count]; + owners.Values.CopyTo(ctls, 0); + for (int i=0; i + /// + /// Starts the timer hiding Positioned ToolTips + /// + private void StartTimer(IWin32Window owner, int interval) { + + if (timer == null) { + timer = new ToolTipTimer(owner); + // Add the timer handler + timer.Tick += new EventHandler(TimerHandler); + } + timer.Interval = interval; + timer.Start(); + } + + /// + /// + /// Stops the timer for hiding Positioned ToolTips + /// + protected void StopTimer() { + //VSWhidbey 363538: hold a local ref to timer + //so that a posted message doesn't null this + //out during disposal. + ToolTipTimer timerRef = timer; + + if (timerRef != null) { + timerRef.Stop(); + timerRef.Dispose(); + timer = null; + } + } + + /// + /// + /// Generates updown events when the timer calls this function. + /// + private void TimerHandler(object source, EventArgs args) { + Hide(((ToolTipTimer)source).Host); + } + + /// + /// + /// + /// Finalizes garbage collection. + /// + /// + ~ToolTip() { + DestroyHandle(); + } + + /// + /// + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + " InitialDelay: " + InitialDelay.ToString(CultureInfo.CurrentCulture) + ", ShowAlways: " + ShowAlways.ToString(CultureInfo.CurrentCulture); + } + + private void Reposition(Point tipPosition, Size tipSize) { + Point moveToLocation = tipPosition; + Screen screen = Screen.FromPoint(moveToLocation); + + // Re-adjust the X position of the tool tip if it bleeds off the screen working area + if (moveToLocation.X + tipSize.Width > screen.WorkingArea.Right) { + moveToLocation.X = screen.WorkingArea.Right - tipSize.Width; + } + + // re-adjust the Y position of the tool tip if it bleeds off the screen working area. + if (moveToLocation.Y + tipSize.Height> screen.WorkingArea.Bottom) { + moveToLocation.Y = screen.WorkingArea.Bottom - tipSize.Height; + } + + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + moveToLocation.X, moveToLocation.Y, tipSize.Width, tipSize.Height, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOOWNERZORDER); + } + + /// + /// + /// Handles the WM_MOVE message. + /// + /// + private void WmMove() { + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) + { + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + TipInfo tt = (TipInfo)tools[win]; + if (win == null || tt==null) { + return; + } + + // Treeview handles its own ToolTips. + TreeView treeView = win as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + return; + } + } + + // Reposition the tooltip when its about to be shown.. since the tooltip can go out of screen workingarea bounds + // Reposition would check the bounds for us. + if (tt.Position != Point.Empty) + { + Reposition(tt.Position, r.Size); + } + } + } + + + /// + /// + /// Handles the WM_MOUSEACTIVATE message. + /// + /// + private void WmMouseActivate(ref Message msg) { + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(win, Control.GetSafeHandle(win)), ref r); + Point cursorLocation = Cursor.Position; + + // Do not activate the mouse if its within the bounds of the + // the associated tool + if (cursorLocation.X >= r.left && cursorLocation.X <= r.right && + cursorLocation.Y >= r.top && cursorLocation.Y <= r.bottom) { + msg.Result = (IntPtr)NativeMethods.MA_NOACTIVATE; + } + } + } + + + + /// + /// + /// Handles the WM_WINDOWFROMPOINT message. + /// + /// + private void WmWindowFromPoint(ref Message msg) { + NativeMethods.POINT sc = (NativeMethods.POINT)msg.GetLParam(typeof(NativeMethods.POINT)); + Point screenCoords = new Point(sc.x, sc.y); + bool result = false; + msg.Result = GetWindowFromPoint(screenCoords, ref result); + } + + + + /// + /// + /// Handles the TTN_SHOW message. + /// + /// + private void WmShow() { + + + //Get the Bounds.... + NativeMethods.RECT r = new NativeMethods.RECT(); + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + Control toolControl = win as Control; + + Size currentTooltipSize = r.Size; + PopupEventArgs e = new PopupEventArgs(win, toolControl, IsBalloon, currentTooltipSize); + OnPopup(e); + + DataGridView dataGridView = toolControl as DataGridView; + if (dataGridView != null && dataGridView.CancelToolTipPopup(this)) + { + // The dataGridView cancelled the tooltip. + e.Cancel = true; + } + + // We need to re-get the rectangle of the tooltip here because + // any of the tooltip attributes/properties could have been updated + // during the popup event; in which case the size of the tooltip is + // affected. e.ToolTipSize is respected over r.Size + UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); + currentTooltipSize = (e.ToolTipSize == currentTooltipSize) ? r.Size:e.ToolTipSize; + + + if (IsBalloon) { + // Get the text display rectangle + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ADJUSTRECT, 1, ref r); + if (r.Size.Height > currentTooltipSize.Height) currentTooltipSize.Height = r.Size.Height; + } + + // Set the max possible size of the tooltip to the size we received. + // This prevents the operating system from drawing incorrect rectangles + // when determing the correct display rectangle. VSWhidbey + + if (currentTooltipSize != r.Size) + { + Screen screen = Screen.FromPoint(Cursor.Position); + int maxwidth = (IsBalloon) ? + Math.Min(currentTooltipSize.Width-2*XBALLOONOFFSET, screen.WorkingArea.Width): + Math.Min(currentTooltipSize.Width, screen.WorkingArea.Width); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, maxwidth); + } + + if (e.Cancel) { + cancelled = true; + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + 0, 0, 0, 0, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); + + } + else { + cancelled = false; + // Only width/height changes are respected, so set top,left to what we got earlier + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), + NativeMethods.HWND_TOPMOST, + r.left, r.top, currentTooltipSize.Width, currentTooltipSize.Height, + NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); + } + } + } + + /// + /// + /// Handles the WM_WINDOWPOSCHANGED message. + /// We need to Hide the window since the native tooltip actually calls SetWindowPos in its TTN_SHOW even if we cancel showing the + /// tooltip : Hence we need to listen to the WindowPosChanged message can hide the window ourselves. + /// Refer to VsWhidbey : 490044 for more details. + /// + /// + private bool WmWindowPosChanged() { + if (cancelled) + { + SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), NativeMethods.SW_HIDE); + return true; + } + return false; + } + + + + /// + /// + /// Handles the WM_WINDOWPOSCHANGING message. + /// + /// + private unsafe void WmWindowPosChanging(ref Message m) { + if (cancelled || isDisposing) + { + return; + } + + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + + Cursor currentCursor = Cursor.CurrentInternal; + Point cursorPos = Cursor.Position; + + + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null || !IsWindowActive(win)) { + return; + } + + TipInfo tt = null; + if (win != null) + { + tt = (TipInfo)tools[win]; + if (tt == null) { + return; + } + + // Treeview handles its own ToolTips. + TreeView treeView = win as TreeView; + if (treeView != null) { + if (treeView.ShowNodeToolTips) { + return; + } + } + } + + if (IsBalloon) { + wp->cx += 2*XBALLOONOFFSET; + return; + } + + if ( (tt.TipType & TipInfo.Type.Auto) != 0 && window != null ) + { + window.DefWndProc(ref m); + return; + } + + if ( ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0) && tt.Position == Point.Empty ) { + + Screen screen = Screen.FromPoint(cursorPos); + if (currentCursor != null) + { + wp->x = cursorPos.X; + // Since HotSpot requires a security demand .. we assert this and revert Assert immediately + try { + IntSecurity.ObjectFromWin32Handle.Assert(); + + wp->y = cursorPos.Y; + if (wp->y + wp->cy + currentCursor.Size.Height - currentCursor.HotSpot.Y > screen.WorkingArea.Bottom) { + wp->y = cursorPos.Y - wp->cy; + } + else { + wp->y = cursorPos.Y + currentCursor.Size.Height - currentCursor.HotSpot.Y; + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + if (wp->x + wp->cx >screen.WorkingArea.Right) { + wp->x = screen.WorkingArea.Right - wp->cx; + } + + } + else if ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0 && tt.Position != Point.Empty) { + + Screen screen = Screen.FromPoint(tt.Position); + wp->x = tt.Position.X; + if (wp->x + wp->cx >screen.WorkingArea.Right) { + wp->x = screen.WorkingArea.Right - wp->cx; + } + wp->y = tt.Position.Y; + + if (wp->y + wp->cy > screen.WorkingArea.Bottom) { + wp->y = screen.WorkingArea.Bottom - wp->cy; + } + } + } + + m.Result = IntPtr.Zero; + } + + /// + /// + /// Called just before the tooltip is hidden + /// + /// + private void WmPop() { + + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); + if (ret != 0) { + + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + if (win == null) { + win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); + } + + if (win == null) { + return; + } + + Control control = win as Control; + TipInfo tt = (TipInfo)tools[win]; + if (tt == null) { + return; + } + + // Must reset the maxwidth to the screen size. + // This is required to resolve VSWhidbey bug# 363408 + if ((tt.TipType & TipInfo.Type.Auto) != 0 || (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) { + Screen screen = Screen.FromPoint(Cursor.Position); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, screen.WorkingArea.Width); + } + + // For non-auto tips (those showned through + // the show(...) methods, we need to dissassociate + // them from the tip control. + if ((tt.TipType & TipInfo.Type.Auto) == 0) { + + tools.Remove(control); + owners.Remove(win.Handle); + + control.HandleCreated -= new EventHandler(this.HandleCreated); + control.HandleDestroyed -= new EventHandler(this.HandleDestroyed); + created.Remove(control); + + if (originalPopupDelay != 0) { + AutoPopDelay = originalPopupDelay; + originalPopupDelay = 0; + } + } + else { + // Clear all other flags except for the + // Auto flag to ensure automatic tips can still show + tt.TipType = TipInfo.Type.Auto; + tt.Position = Point.Empty; + tools[control] = tt; + } + } + } + + /// + /// + /// WNDPROC + /// + /// + private void WndProc(ref Message msg) { + + + switch (msg.Msg) { + + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) msg.GetLParam(typeof(NativeMethods.NMHDR)); + if (nmhdr.code == NativeMethods.TTN_SHOW && !trackPosition) { + WmShow(); + } + else if (nmhdr.code == NativeMethods.TTN_POP) { + WmPop(); + if (window != null) { + window.DefWndProc(ref msg); + } + } + break; + + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref msg); + break; + + case NativeMethods.WM_WINDOWPOSCHANGED: + if (!WmWindowPosChanged() && window != null) + { + window.DefWndProc(ref msg); + } + break; + + case NativeMethods.WM_MOUSEACTIVATE: + WmMouseActivate(ref msg); + break; + + case NativeMethods.WM_MOVE: + WmMove(); + break; + + case NativeMethods.TTM_WINDOWFROMPOINT: + WmWindowFromPoint(ref msg); + break; + + case NativeMethods.WM_PRINTCLIENT: + goto case NativeMethods.WM_PAINT; + + case NativeMethods.WM_PAINT: + if (ownerDraw && !isBalloon && !trackPosition) + { + NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); + IntPtr dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this,Handle),ref ps); + Graphics g = Graphics.FromHdcInternal(dc); + try { + Rectangle bounds = new Rectangle(ps.rcPaint_left,ps.rcPaint_top, + ps.rcPaint_right - ps.rcPaint_left, + ps.rcPaint_bottom - ps.rcPaint_top); + if (bounds == Rectangle.Empty ) { + return; + } + NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); + ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); + int ret = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti)); + if (ret != 0) { + IWin32Window win = (IWin32Window)owners[ti.hwnd]; + Control ac = Control.FromHandleInternal(ti.hwnd); + if (win == null) { + win = (IWin32Window)ac; + } + Font font; + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font = Font.FromHfont(UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_GETFONT, 0, 0)); + } + catch (ArgumentException) { + // VSWhidbey 209345 - if the current default tooltip font is a non-TrueType font, then + // Font.FromHfont throws this exception, so fall back to the default control font. + font = Control.DefaultFont; + } + finally { + CodeAccessPermission.RevertAssert(); + } + + OnDraw(new DrawToolTipEventArgs(g, win, ac, bounds, GetToolTip(ac), + BackColor, ForeColor, font)); + + break; + } + } + finally + { + g.Dispose(); + UnsafeNativeMethods.EndPaint(new HandleRef(this,Handle),ref ps); + } + } + + //If not OwnerDraw, fall through + goto default; + default: + if (window != null) { + window.DefWndProc(ref msg); + } + break; + } + } + + /// + /// + /// + /// + private class ToolTipNativeWindow : NativeWindow { + ToolTip control; + + internal ToolTipNativeWindow(ToolTip control) { + this.control = control; + } + + + protected override void WndProc(ref Message m) { + if (control != null) { + control.WndProc(ref m); + } + } + } + + private class ToolTipTimer : Timer { + IWin32Window host; + + public ToolTipTimer(IWin32Window owner) : base(){ + this.host = owner; + } + + public IWin32Window Host { + get { + return host; + } + } + } + + + private class TipInfo { + + [Flags] + public enum Type { + None = 0x0000, + Auto = 0x0001, + Absolute = 0x0002, + SemiAbsolute = 0x0004 + }; + + public Type TipType = Type.Auto; + private string caption; + private string designerText; + public Point Position = Point.Empty; + + public TipInfo(string caption, Type type) { + this.caption = caption; + this.TipType = type; + if (type == Type.Auto) { + this.designerText = caption; + } + } + + public string Caption { + get { + return ((this.TipType & (Type.Absolute | Type.SemiAbsolute)) != 0) ? caption:designerText; + } + set { + this.caption = value; + } + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/ToolTipIcon.cs b/WindowsForms/Managed/System/WinForms/ToolTipIcon.cs new file mode 100644 index 000000000..7442f8291 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ToolTipIcon.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + public enum ToolTipIcon { + + /// + /// + /// + /// No Icon. + /// + /// + None = 0, + + /// + /// + /// + /// A Information Icon. + /// + /// + Info = 1, + + /// + /// + /// + /// A Warning Icon. + /// + /// + Warning = 2, + + + /// + /// + /// + /// A Error Icon. + /// + /// + Error = 3 + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TrackBar.cs b/WindowsForms/Managed/System/WinForms/TrackBar.cs new file mode 100644 index 000000000..b8417f1c8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TrackBar.cs @@ -0,0 +1,1198 @@ + //------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using Microsoft.Win32; + using System.ComponentModel; + using System.Drawing; + using System.Windows.Forms; + using System.ComponentModel.Design; + using System.Windows.Forms.Layout; + using System.Globalization; + + /// + /// + /// The TrackBar is a scrollable control similar to the ScrollBar, but + /// has a different UI. You can configure ranges through which it should + /// scroll, and also define increments for off-button clicks. It can be + /// aligned horizontally or vertically. You can also configure how many + /// 'ticks' are shown for the total range of values + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Value"), + DefaultEvent("Scroll"), + DefaultBindingProperty("Value"), + Designer("System.Windows.Forms.Design.TrackBarDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionTrackBar) + ] + public class TrackBar : Control, ISupportInitialize { + + private static readonly object EVENT_SCROLL = new object(); + private static readonly object EVENT_VALUECHANGED = new object(); + private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object(); + + private bool autoSize = true; + private int largeChange = 5; + private int maximum = 10; + private int minimum = 0; + private Orientation orientation = System.Windows.Forms.Orientation.Horizontal; + private int value = 0; + private int smallChange = 1; + private int tickFrequency = 1; + private TickStyle tickStyle = System.Windows.Forms.TickStyle.BottomRight; + + private int requestedDim; + + // Mouse wheel movement + private int cumulativeWheelData = 0; + + // Disable value range checking while initializing the control + private bool initializing = false; + + private bool rightToLeftLayout = false; + + /// + /// + /// Creates a new TrackBar control with a default range of 0..10 and + /// ticks shown every value. + /// + public TrackBar() + : base() { + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.UseTextForAccessibility, false); + requestedDim = PreferredDimension; + } + + /// + /// + /// Indicates if the control is being auto-sized. If true, the + /// TrackBar will adjust either its height or width [depending on + /// orientation] to make sure that only the required amount of + /// space is used. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TrackBarAutoSizeDescr), + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) + ] + public override bool AutoSize { + get { + return autoSize; + } + + set { + // Note that we intentionally do not call base. Labels size themselves by + // overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize + // continue to return false to keep our LayoutEngines from messing with TextBoxes. + // This is done for backwards compatibility since the new AutoSize behavior differs. + if (autoSize != value) { + autoSize = value; + if (orientation == Orientation.Horizontal) { + SetStyle(ControlStyles.FixedHeight, autoSize); + SetStyle(ControlStyles.FixedWidth, false); + } + else { + SetStyle(ControlStyles.FixedWidth, autoSize); + SetStyle(ControlStyles.FixedHeight, false); + } + AdjustSize(); + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// This is called when creating a window. Inheriting classes can override + /// this to add extra functionality, but should not forget to first call + /// base.getCreateParams() to make sure the control continues to work + /// correctly. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_TRACKBAR; + + switch (tickStyle) { + case TickStyle.None: + cp.Style |= NativeMethods.TBS_NOTICKS; + break; + case TickStyle.TopLeft: + cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_TOP); + break; + case TickStyle.BottomRight: + cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_BOTTOM); + break; + case TickStyle.Both: + cp.Style |= (NativeMethods.TBS_AUTOTICKS | NativeMethods.TBS_BOTH); + break; + } + + if (orientation == Orientation.Vertical) { + cp.Style |= NativeMethods.TBS_VERT; // HORIZ == 0 + } + + if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) { + //We want to turn on mirroring for Trackbar explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + return cp; + } + } + + /// + /// + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(104, PreferredDimension); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + base.FontChanged += value; + } + remove { + base.FontChanged -= value; + } + } + + /// + /// + /// The current foreground color of the TrackBar. Note that users + /// are unable to change this. It is always Color.WINDOWTEXT + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return SystemColors.WindowText; + } + set { + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + base.ForeColorChanged += value; + } + remove { + base.ForeColorChanged -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public ImeMode ImeMode { + get { + return base.ImeMode; + } + set { + base.ImeMode = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { + base.ImeModeChanged += value; + } + remove { + base.ImeModeChanged -= value; + } + } + + /// + /// + /// The number of ticks by which the TrackBar will change when an + /// event considered a "large change" occurs. These include, Clicking the + /// mouse to the side of the button, or using the PgUp/PgDn keys on the + /// keyboard. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(5), + SRDescription(SR.TrackBarLargeChangeDescr) + ] + public int LargeChange { + get { + return largeChange; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("LargeChange", SR.GetString(SR.TrackBarLargeChangeError, value)); + } + + if (largeChange != value) { + largeChange = value; + if (IsHandleCreated) + { + SendMessage(NativeMethods.TBM_SETPAGESIZE, 0, value); + } + } + } + } + + /// + /// + /// The upper limit of the range this TrackBar is working with. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(10), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.TrackBarMaximumDescr) + ] + public int Maximum { + get { + return maximum; + } + set { + if (maximum != value) { + if (value < minimum) { + minimum = value; + } + SetRange(minimum, value); + } + } + } + + /// + /// + /// The lower limit of the range this TrackBar is working with. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + RefreshProperties(RefreshProperties.All), + SRDescription(SR.TrackBarMinimumDescr) + ] + public int Minimum { + get { + return minimum; + } + set { + if (minimum != value) { + if (value > maximum) { + maximum = value; + } + SetRange(value, maximum); + } + } + } + + /// + /// + /// The orientation for this TrackBar. Valid values are from + /// the Orientation enumeration. The control currently supports being + /// oriented horizontally and vertically. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(Orientation.Horizontal), + Localizable(true), + SRDescription(SR.TrackBarOrientationDescr) + ] + public Orientation Orientation { + get { + return orientation; + } + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)Orientation.Horizontal, (int)Orientation.Vertical)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(Orientation)); + } + + if (orientation != value) { + orientation = value; + + if (orientation == Orientation.Horizontal) { + SetStyle(ControlStyles.FixedHeight, autoSize); + SetStyle(ControlStyles.FixedWidth, false); + Width = requestedDim; + } + else { + SetStyle(ControlStyles.FixedHeight, false); + SetStyle(ControlStyles.FixedWidth, autoSize); + Height = requestedDim; + } + + if (IsHandleCreated) { + Rectangle r = Bounds; + RecreateHandle(); + SetBounds(r.X, r.Y, r.Height, r.Width, BoundsSpecified.All); + AdjustSize(); + } + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// Little private routine that helps with auto-sizing. + /// + /// + private int PreferredDimension { + get { + int cyhscroll = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYHSCROLL); + + // this is our preferred size + // + return((cyhscroll * 8) / 3); + } + } + + /// + /// + /// Redraw control, if the handle's created + /// + /// + private void RedrawControl() + { + if (IsHandleCreated) + { + //The '1' in the call to SendMessage below indicates that the + //trackbar should be redrawn (see TBM_SETRANGEMAX in MSDN) + SendMessage(NativeMethods.TBM_SETRANGEMAX, 1, maximum); + Invalidate(); + } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft property is true, mirroring will be turned on on the trackbar. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + /// + /// + /// The number of ticks by which the TrackBar will change when an + /// event considered a "small change" occurs. These are most commonly + /// seen by using the arrow keys to move the TrackBar thumb around. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(1), + SRDescription(SR.TrackBarSmallChangeDescr) + ] + public int SmallChange { + get { + return smallChange; + } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("SmallChange", SR.GetString(SR.TrackBarSmallChangeError, value)); + } + if (smallChange != value) { + smallChange = value; + if (IsHandleCreated) + { + SendMessage(NativeMethods.TBM_SETLINESIZE, 0, value); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// Indicates how the TrackBar control will draw itself. This affects + /// both where the ticks will be drawn in relation to the moveable thumb, + /// and how the thumb itself will be drawn. values are taken from the + /// TickStyle enumeration. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(TickStyle.BottomRight), + SRDescription(SR.TrackBarTickStyleDescr) + ] + public TickStyle TickStyle { + get { + return tickStyle; + } + set { + // Confirm that value is a valid enum + //valid values are 0x0 to 0x3 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TickStyle.None, (int)TickStyle.Both)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(TickStyle)); + } + + if (tickStyle != value) { + tickStyle = value; + RecreateHandle(); + } + } + } + + /// + /// + /// Indicates just how many ticks will be drawn. For a TrackBar with a + /// range of 0..100, it might be impractical to draw all 100 ticks for a + /// very small control. Passing in a value of 5 here would only draw + /// 20 ticks -- i.e. Each tick would represent 5 units in the TrackBars + /// range of values. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(1), + SRDescription(SR.TrackBarTickFrequencyDescr) + ] + public int TickFrequency { + get { + return tickFrequency; + } + set { + // SEC. + + if (tickFrequency != value) { + tickFrequency = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.TBM_SETTICFREQ, value, 0); + Invalidate(); + } + } + } + } + + /// + /// + /// The current location of the TrackBar thumb. This value must + /// be between the lower and upper limits of the TrackBar range, of course. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(0), + Bindable(true), + SRDescription(SR.TrackBarValueDescr) + ] + public int Value { + get { + GetTrackBarValue(); + return value; + } + set { + if (this.value != value) { + if (!initializing && ((value < minimum) || (value > maximum))) + throw new ArgumentOutOfRangeException("Value", SR.GetString(SR.InvalidBoundArgument, "Value", (value).ToString(CultureInfo.CurrentCulture), "'Minimum'", "'Maximum'")); + this.value = value; + SetTrackBarPosition(); + OnValueChanged(EventArgs.Empty); + } + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Click { + add { + base.Click += value; + } + remove { + base.Click -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler DoubleClick { + add { + base.DoubleClick += value; + } + remove { + base.DoubleClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseClick { + add { + base.MouseClick += value; + } + remove { + base.MouseClick -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseDoubleClick { + add { + base.MouseDoubleClick += value; + } + remove { + base.MouseDoubleClick -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value); + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TrackBarOnScrollDescr)] + public event EventHandler Scroll { + add { + Events.AddHandler(EVENT_SCROLL, value); + } + remove { + Events.RemoveHandler(EVENT_SCROLL, value); + } + } + + /// + /// + /// TrackBar Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.valueChangedEventDescr)] + public event EventHandler ValueChanged { + add { + Events.AddHandler(EVENT_VALUECHANGED, value); + } + remove { + Events.RemoveHandler(EVENT_VALUECHANGED, value); + } + } + + /// + /// + /// Enforces autoSizing + /// + /// + private void AdjustSize() { + if (IsHandleCreated) { + int saveDim = requestedDim; + try { + if (orientation == Orientation.Horizontal) + Height = autoSize ? PreferredDimension : saveDim; + else + Width = autoSize ? PreferredDimension : saveDim; + } + finally { + requestedDim = saveDim; + } + } + } + + /// + /// + /// Handles tasks required when the control is being initialized. + /// + /// + public void BeginInit() { + initializing = true; + } + + // Constrain the current value of the control to be within + // the minimum and maximum. + // + private void ConstrainValue() { + + // Don't constrain the value while we're initializing the control + if (initializing) { + return; + } + + Debug.Assert(minimum <= maximum, "Minimum should be <= Maximum"); + + // Keep the current value within the minimum and maximum + if (Value < minimum) { + Value = minimum; + } + if (Value > maximum) { + Value = maximum; + } + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_BAR_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// Called when initialization of the control is complete. + /// + /// + public void EndInit() { + initializing = false; + + // Make sure the value is constrained by the minimum and maximum + ConstrainValue(); + } + + private void GetTrackBarValue() { + if (IsHandleCreated) { + value = unchecked( (int) (long)SendMessage(NativeMethods.TBM_GETPOS, 0, 0)); + + // See SetTrackBarValue() for a description of why we sometimes reflect the trackbar value + // + + if (orientation == Orientation.Vertical) { + // Reflect value + value = Minimum + Maximum - value; + } + + // Reflect for a RightToLeft horizontal trackbar + // + if (orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes && !IsMirrored) { + value = Minimum + Maximum - value; + } + } + } + + /// + /// + /// Handling special input keys, such as pgup, pgdown, home, end, etc... + /// + protected override bool IsInputKey(Keys keyData) { + if ((keyData & Keys.Alt) == Keys.Alt) return false; + switch (keyData & Keys.KeyCode) { + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + return base.IsInputKey(keyData); + } + + /// + /// + /// + /// + protected override void OnHandleCreated(EventArgs e) { + base.OnHandleCreated(e); + SendMessage(NativeMethods.TBM_SETRANGEMIN, 0, minimum); + SendMessage(NativeMethods.TBM_SETRANGEMAX, 0, maximum); + SendMessage(NativeMethods.TBM_SETTICFREQ, tickFrequency, 0); + SendMessage(NativeMethods.TBM_SETPAGESIZE, 0, largeChange); + SendMessage(NativeMethods.TBM_SETLINESIZE, 0, smallChange); + SetTrackBarPosition(); + AdjustSize(); + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler; + if (eh != null) { + eh(this, e); + } + } + + /// + /// + /// Actually fires the "scroll" event. Inheriting classes should override + /// this method in favor of actually adding an EventHandler for this + /// event. Inheriting classes should not forget to call + /// base.onScroll(e) + /// + protected virtual void OnScroll(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_SCROLL]; + if (handler != null) handler(this,e); + } + + /// + /// + /// Raises the event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnMouseWheel(MouseEventArgs e) { + base.OnMouseWheel( e ); + + HandledMouseEventArgs hme = e as HandledMouseEventArgs; + if (hme != null) { + if (hme.Handled) { + return; + } + hme.Handled = true; + } + + if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) { + return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down. + } + + int wheelScrollLines = SystemInformation.MouseWheelScrollLines; + if (wheelScrollLines == 0) { + return; // Do not scroll when the user system setting is 0 lines per notch + } + + Debug.Assert(this.cumulativeWheelData > -NativeMethods.WHEEL_DELTA, "cumulativeWheelData is too small"); + Debug.Assert(this.cumulativeWheelData < NativeMethods.WHEEL_DELTA, "cumulativeWheelData is too big"); + this.cumulativeWheelData += e.Delta; + + float partialNotches; + partialNotches = (float)this.cumulativeWheelData / (float)NativeMethods.WHEEL_DELTA; + + if (wheelScrollLines == -1) { + wheelScrollLines = TickFrequency; + } + + // Evaluate number of bands to scroll + int scrollBands = (int)((float)wheelScrollLines * partialNotches); + + if (scrollBands != 0) { + int absScrollBands; + if (scrollBands > 0) { + absScrollBands = scrollBands; + Value = Math.Min(absScrollBands+Value, Maximum); + this.cumulativeWheelData -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + else { + absScrollBands = -scrollBands; + Value = Math.Max(Value-absScrollBands, Minimum); + this.cumulativeWheelData -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + } + + if (e.Delta != Value) { + OnScroll(EventArgs.Empty); + OnValueChanged(EventArgs.Empty); + } + } + + /// + /// + /// Actually fires the "valueChanged" event. + /// + /// + protected virtual void OnValueChanged(EventArgs e) { + EventHandler handler = (EventHandler)Events[EVENT_VALUECHANGED]; + if (handler != null) handler(this,e); + } + + /// + /// + /// This method is called by the control when any property changes. Inheriting + /// controls can overide this method to get property change notification on + /// basic properties. Inherting controls must call base.propertyChanged. + /// + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + RedrawControl(); + } + + /// + protected override void OnSystemColorsChanged (EventArgs e) { + base.OnSystemColorsChanged (e); + RedrawControl(); + } + + /// + /// + /// Overrides Control.setBoundsCore to enforce autoSize. + /// + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + //SetBoundsCore .. sets the height for a control in designer .. we should obey the requested + //height is Autosize is false.. + //if (IsHandleCreated) { + requestedDim = (orientation == Orientation.Horizontal) + ? height + : width; + + if (autoSize) { + if (orientation == Orientation.Horizontal) { + if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) + height = PreferredDimension; + } + else { + if ((specified & BoundsSpecified.Width) != BoundsSpecified.None) + width = PreferredDimension; + } + } + //} + base.SetBoundsCore(x, y, width, height, specified); + } + + /// + /// + /// Lets you set the the entire range for the TrackBar control at once. + /// The values passed are both the lower and upper limits to the range + /// with which the control will work. + /// + public void SetRange(int minValue, int maxValue) { + if (minimum != minValue || maximum != maxValue) { + + // The Minimum and Maximum properties contain the logic for + // ensuring that minValue <= maxValue. It is possible, however, + // that this function will be called somewhere other than from + // these two properties, so we'll check that here anyway. + // + if (minValue > maxValue) { + // We'll just adjust maxValue to match minValue + maxValue = minValue; + } + + minimum = minValue; + maximum = maxValue; + + if (IsHandleCreated) { + SendMessage(NativeMethods.TBM_SETRANGEMIN, 0, minimum); + + // We must repaint the trackbar after changing + // the range. The '1' in the call to + // SendMessage below indicates that the trackbar + // should be redrawn (see TBM_SETRANGEMAX in MSDN) + SendMessage(NativeMethods.TBM_SETRANGEMAX, 1, maximum); + + Invalidate(); + } + + // When we change the range, the comctl32 trackbar's internal position can change + // (because of the reflection that occurs with vertical trackbars) + // so we make sure to explicitly set the trackbar position. + // + if (value < minimum) { + value = minimum; + } + if (value > maximum) { + value = maximum; + } + SetTrackBarPosition(); + } + } + + private void SetTrackBarPosition() { + if (IsHandleCreated) { + + // There are two situations where we want to reflect the track bar position: + // + // 1. For a vertical trackbar, it seems to make more sense for the trackbar to increase in value + // as the slider moves up the trackbar (this is opposite what the underlying winctl control does) + // + // 2. For a RightToLeft horizontal trackbar, we want to reflect the position. + // + int reflectedValue = value; + + // 1. Reflect for a vertical trackbar + // + if (orientation == Orientation.Vertical) { + reflectedValue = Minimum + Maximum - value; + } + + // 2. Reflect for a RightToLeft horizontal trackbar + // + if (orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes && !IsMirrored) { + reflectedValue = Minimum + Maximum - value; + } + + SendMessage(NativeMethods.TBM_SETPOS, 1, reflectedValue); + } + } + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + return s + ", Minimum: " + Minimum.ToString(CultureInfo.CurrentCulture) + ", Maximum: " + Maximum.ToString(CultureInfo.CurrentCulture) + ", Value: " + Value.ToString(CultureInfo.CurrentCulture); + } + + /// + /// + /// The button's window procedure. Inheriting classes can override this + /// to add extra functionality, but should not forget to call + /// base.wndProc(m); to ensure the button continues to function properly. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_REFLECT+NativeMethods.WM_HSCROLL: + case NativeMethods.WM_REFLECT+NativeMethods.WM_VSCROLL: + switch (NativeMethods.Util.LOWORD(m.WParam)) { + case NativeMethods.TB_LINEUP: + case NativeMethods.TB_LINEDOWN: + case NativeMethods.TB_PAGEUP: + case NativeMethods.TB_PAGEDOWN: + //case NativeMethods.TB_THUMBPOSITION: + case NativeMethods.TB_THUMBTRACK: + case NativeMethods.TB_TOP: + case NativeMethods.TB_BOTTOM: + case NativeMethods.TB_ENDTRACK: + if (value != Value) { + OnScroll(EventArgs.Empty); + OnValueChanged(EventArgs.Empty); + } + break; + } + break; + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TrackBarRenderer.cs b/WindowsForms/Managed/System/WinForms/TrackBarRenderer.cs new file mode 100644 index 000000000..cbf2fbe73 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TrackBarRenderer.cs @@ -0,0 +1,271 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + +using System; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; + + /// + /// + /// + /// This is a rendering class for the TrackBar control. + /// + /// + public sealed class TrackBarRenderer { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + const int lineWidth = 2; + + //cannot instantiate + private TrackBarRenderer() { + } + + /// + /// + /// + /// Returns true if this class is supported for the current OS and user/application settings, + /// otherwise returns false. + /// + /// + public static bool IsSupported { + get { + return VisualStyleRenderer.IsSupported; // no downlevel support + } + } + + /// + /// + /// + /// Renders a horizontal track. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawHorizontalTrack(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.TrackBar.Track.Normal, 1); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical track. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawVerticalTrack(Graphics g, Rectangle bounds) { + InitializeRenderer(VisualStyleElement.TrackBar.TrackVertical.Normal, 1); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawHorizontalThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.Thumb.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a vertical thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawVerticalThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbVertical.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a constant size left pointing thumb centered in the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawLeftPointingThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbLeft.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a constant size right pointing thumb centered in the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawRightPointingThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbRight.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a constant size top pointing thumb centered in the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawTopPointingThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbTop.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a constant size bottom pointing thumb centered in the given bounds. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawBottomPointingThumb(Graphics g, Rectangle bounds, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbBottom.Normal, (int)state); + + visualStyleRenderer.DrawBackground(g, bounds); + } + + /// + /// + /// + /// Renders a horizontal tick. + /// + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawHorizontalTicks(Graphics g, Rectangle bounds, int numTicks, EdgeStyle edgeStyle) { + if (numTicks <= 0 || bounds.Height <= 0 || bounds.Width <= 0 || g == null) { + return; + } + + InitializeRenderer(VisualStyleElement.TrackBar.Ticks.Normal, 1); + + //trivial case -- avoid calcs + if (numTicks == 1) { + visualStyleRenderer.DrawEdge(g, new Rectangle(bounds.X, bounds.Y, lineWidth, bounds.Height), Edges.Left, edgeStyle, EdgeEffects.None); + return; + } + + float inc = ((float)bounds.Width - lineWidth) / ((float)numTicks - 1); + + while (numTicks > 0) { + //draw the nth tick + float x = bounds.X + ((float)(numTicks - 1)) * inc; + visualStyleRenderer.DrawEdge(g, new Rectangle((int)Math.Round(x), bounds.Y, lineWidth, bounds.Height), Edges.Left, edgeStyle, EdgeEffects.None); + numTicks--; + } + } + + /// + /// + /// + /// Renders a vertical tick. + /// + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static void DrawVerticalTicks(Graphics g, Rectangle bounds, int numTicks, EdgeStyle edgeStyle) { + if (numTicks<=0 || bounds.Height <= 0 || bounds.Width<=0 || g == null ) { + return; + } + + InitializeRenderer(VisualStyleElement.TrackBar.TicksVertical.Normal, 1); + + //trivial case + if (numTicks == 1) { + visualStyleRenderer.DrawEdge(g, new Rectangle(bounds.X, bounds.Y, bounds.Width, lineWidth), Edges.Top, edgeStyle, EdgeEffects.None); + return; + } + + float inc = ((float)bounds.Height - lineWidth) / ((float)numTicks - 1); + + while (numTicks > 0) { + //draw the nth tick + float y = bounds.Y + ((float)(numTicks - 1)) * inc; + visualStyleRenderer.DrawEdge(g, new Rectangle(bounds.X, (int)Math.Round(y), bounds.Width, lineWidth), Edges.Top, edgeStyle, EdgeEffects.None); + numTicks--; + } + } + + /// + /// + /// + /// Returns the size of a left pointing thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetLeftPointingThumbSize(Graphics g, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbLeft.Normal, (int)state); + + return (visualStyleRenderer.GetPartSize(g, ThemeSizeType.True)); + } + + /// + /// + /// + /// Returns the size of a right pointing thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetRightPointingThumbSize(Graphics g, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbRight.Normal, (int)state); + + return (visualStyleRenderer.GetPartSize(g, ThemeSizeType.True)); + } + + /// + /// + /// + /// Returns the size of a top pointing thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetTopPointingThumbSize(Graphics g, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbTop.Normal, (int)state); + + return (visualStyleRenderer.GetPartSize(g, ThemeSizeType.True)); + } + + /// + /// + /// + /// Returns the size of a bottom pointing thumb. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Graphics instead of IDeviceContext intentionally + public static Size GetBottomPointingThumbSize(Graphics g, TrackBarThumbState state) { + InitializeRenderer(VisualStyleElement.TrackBar.ThumbBottom.Normal, (int)state); + + return (visualStyleRenderer.GetPartSize(g, ThemeSizeType.True)); + } + + private static void InitializeRenderer(VisualStyleElement element, int state) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(element.ClassName, element.Part, state); + } + else { + visualStyleRenderer.SetParameters(element.ClassName, element.Part, state); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNode.cs b/WindowsForms/Managed/System/WinForms/TreeNode.cs new file mode 100644 index 000000000..48e0ae478 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNode.cs @@ -0,0 +1,1978 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + using System.Text; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Security; + using System.Security.Permissions; + + using System; + using System.Drawing.Design; + using System.Collections; + using System.Globalization; + using System.Windows.Forms; + using System.ComponentModel; + using System.IO; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Implements a node of a . + /// + /// + /// + [ + TypeConverterAttribute(typeof(TreeNodeConverter)), Serializable, + DefaultProperty("Text"), + SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly") + ] + public class TreeNode : MarshalByRefObject, ICloneable, ISerializable { + private const int SHIFTVAL = 12; + private const int CHECKED = 2 << SHIFTVAL; + private const int UNCHECKED = 1 << SHIFTVAL; + private const int ALLOWEDIMAGES = 14; + + //the threshold value used to optimize AddRange and Clear operations for a big number of nodes + internal const int MAX_TREENODES_OPS = 200; + + // we use it to store font and color data in a minimal-memory-cost manner + // ie. nodes which don't use fancy fonts or colors (ie. that use the TreeView settings for these) + // will take up less memory than those that do. + internal OwnerDrawPropertyBag propBag = null; + internal IntPtr handle; + internal string text; + internal string name; + + // note: as the checked state of a node is user controlled, and this variable is simply for + // state caching when a node hasn't yet been realized, you should use the Checked property to + // find out the check state of a node, and not this member variable. + //private bool isChecked = false; + private const int TREENODESTATE_isChecked = 0x00000001; + + private System.Collections.Specialized.BitVector32 treeNodeState; + + private TreeNodeImageIndexer imageIndexer; + private TreeNodeImageIndexer selectedImageIndexer; + private TreeNodeImageIndexer stateImageIndexer; + + private string toolTipText = ""; + private ContextMenu contextMenu = null; + private ContextMenuStrip contextMenuStrip = null; + internal bool nodesCleared = false; + + // We need a special way to defer to the TreeView's image + // list for indexing purposes. + internal class TreeNodeImageIndexer : ImageList.Indexer { + private TreeNode owner; + + /// + public enum ImageListType { + /// + Default, + /// + State + } + private ImageListType imageListType; + + /// + public TreeNodeImageIndexer(TreeNode node, ImageListType imageListType) { + owner = node; + this.imageListType = imageListType; + } + + /// + public override ImageList ImageList { + get { + if (owner.TreeView != null) { + if (imageListType == ImageListType.State) { + return owner.TreeView.StateImageList; + } + else { + return owner.TreeView.ImageList; + } + } + else { + return null; + } + } + set { Debug.Assert(false, "We should never set the image list"); } + } + + } + + + + internal TreeNodeImageIndexer ImageIndexer { + get { + //Demand create the imageIndexer + if (imageIndexer == null) { + imageIndexer = new TreeNodeImageIndexer(this, TreeNodeImageIndexer.ImageListType.Default); + } + return imageIndexer; + } + } + + internal TreeNodeImageIndexer SelectedImageIndexer { + get { + //Demand create the imageIndexer + if (selectedImageIndexer == null) { + selectedImageIndexer = new TreeNodeImageIndexer(this, TreeNodeImageIndexer.ImageListType.Default); + } + + return selectedImageIndexer; + + } + } + + internal TreeNodeImageIndexer StateImageIndexer { + get { + //Demand create the imageIndexer + if (stateImageIndexer == null) { + stateImageIndexer = new TreeNodeImageIndexer(this, TreeNodeImageIndexer.ImageListType.State); + } + return stateImageIndexer; + } + } + + + internal int index; // our index into our parents child array + internal int childCount; + internal TreeNode[] children; + internal TreeNode parent; + internal TreeView treeView; + private bool expandOnRealization = false; + private bool collapseOnRealization = false; + private TreeNodeCollection nodes = null; + object userData; + + private readonly static int insertMask = + NativeMethods.TVIF_TEXT + | NativeMethods.TVIF_IMAGE + | NativeMethods.TVIF_SELECTEDIMAGE; + + /// + /// + /// Creates a TreeNode object. + /// + public TreeNode() { + treeNodeState = new System.Collections.Specialized.BitVector32(); + } + + internal TreeNode(TreeView treeView) : this() { + this.treeView = treeView; + } + + /// + /// + /// Creates a TreeNode object. + /// + public TreeNode(string text) : this() { + this.text = text; + } + + /// + /// + /// Creates a TreeNode object. + /// + public TreeNode(string text, TreeNode[] children) : this() { + this.text = text; + this.Nodes.AddRange(children); + } + + /// + /// + /// Creates a TreeNode object. + /// + public TreeNode(string text, int imageIndex, int selectedImageIndex) : this() { + this.text = text; + this.ImageIndexer.Index = imageIndex; + this.SelectedImageIndexer.Index = selectedImageIndex; + } + + /// + /// + /// Creates a TreeNode object. + /// + public TreeNode(string text, int imageIndex, int selectedImageIndex, TreeNode[] children) : this() { + this.text = text; + this.ImageIndexer.Index = imageIndex; + this.SelectedImageIndexer.Index = selectedImageIndex; + this.Nodes.AddRange(children); + } + + /** + * Constructor used in deserialization + */ + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + [ + SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors") // Changing Deserialize to be non-virtual + // would be a breaking change. + ] + protected TreeNode(SerializationInfo serializationInfo, StreamingContext context) : this() { + Deserialize(serializationInfo, context); + } + + /// + /// + /// The background color of this node. + /// If null, the color used will be the default color from the TreeView control that this + /// node is attached to + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeBackColorDescr) + ] + public Color BackColor { + get { + if (propBag==null) return Color.Empty; + return propBag.BackColor; + } + set { + // get the old value + Color oldbk = this.BackColor; + // If we're setting the color to the default again, delete the propBag if it doesn't contain + // useful data. + if (value.IsEmpty) { + if (propBag!=null) { + propBag.BackColor = Color.Empty; + RemovePropBagIfEmpty(); + } + if (!oldbk.IsEmpty) InvalidateHostTree(); + return; + } + + // Not the default, so if necessary create a new propBag, and fill it with the backcolor + + if (propBag==null) propBag = new OwnerDrawPropertyBag(); + propBag.BackColor = value; + if (!value.Equals(oldbk)) InvalidateHostTree(); + } + } + + /// + /// + /// The bounding rectangle for the node (text area only). The coordinates + /// are relative to the upper left corner of the TreeView control. + /// + [Browsable(false)] + public Rectangle Bounds { + get { + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return Rectangle.Empty; + } + NativeMethods.RECT rc = new NativeMethods.RECT(); + unsafe { *((IntPtr *) &rc.left) = Handle; } + // wparam: 1=include only text, 0=include entire line + if ((int)UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_GETITEMRECT, 1, ref rc) == 0) { + // This means the node is not visible + // + return Rectangle.Empty; + } + return Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + } + + /// + /// + /// The bounding rectangle for the node (full row). The coordinates + /// are relative to the upper left corner of the TreeView control. + /// + internal Rectangle RowBounds { + get { + TreeView tv = this.TreeView; + NativeMethods.RECT rc = new NativeMethods.RECT(); + unsafe { *((IntPtr *) &rc.left) = Handle; } + // wparam: 1=include only text, 0=include entire line + if (tv == null || tv.IsDisposed) { + return Rectangle.Empty; + } + if ((int)UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_GETITEMRECT, 0, ref rc) == 0) { + // This means the node is not visible + // + return Rectangle.Empty; + } + return Rectangle.FromLTRB(rc.left, rc.top, rc.right, rc.bottom); + } + } + + internal bool CheckedStateInternal { + get { + return treeNodeState[TREENODESTATE_isChecked]; + } + set { + treeNodeState[TREENODESTATE_isChecked] = value; + } + } + + // Checked does sanity checking and fires Before/AfterCheck events, then forwards to this + // property to get/set the actual checked value. + internal bool CheckedInternal { + get { + return CheckedStateInternal; + } + set { + CheckedStateInternal = value; + if (handle == IntPtr.Zero) + return; + + TreeView tv = this.TreeView; + if (tv == null || !tv.IsHandleCreated || tv.IsDisposed) + return; + + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + item.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; + item.hItem = handle; + item.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; + item.state |= value ? CHECKED : UNCHECKED; + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_SETITEM, 0, ref item); + + + } + } + + /// + /// + /// Indicates whether the node's checkbox is checked. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeCheckedDescr), + DefaultValue(false) + ] + public bool Checked { + get { +#if DEBUG + if(handle != IntPtr.Zero) { + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + item.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; + item.hItem = handle; + item.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; + UnsafeNativeMethods.SendMessage(new HandleRef(null, TreeView.Handle), NativeMethods.TVM_GETITEM, 0, ref item); + Debug.Assert(!TreeView.CheckBoxes || ((item.state >> SHIFTVAL) > 1) == CheckedInternal, + "isChecked on node '" + Name + "' did not match the state in TVM_GETITEM."); + } +#endif + return CheckedInternal; + } + set { + TreeView tv = TreeView; + if (tv != null) { + bool eventReturn = tv.TreeViewBeforeCheck(this, TreeViewAction.Unknown); + if (!eventReturn) { + CheckedInternal = value; + tv.TreeViewAfterCheck(this, TreeViewAction.Unknown); + } + } + else { + CheckedInternal = value; + } + } + } + + /// + /// + /// The contextMenu associated with this tree node. The contextMenu + /// will be shown when the user right clicks the mouse on the control. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr) + ] + public virtual ContextMenu ContextMenu { + get { + return contextMenu; + } + set { + contextMenu = value; + } + } + + /// + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.ControlContextMenuDescr) + ] + public virtual ContextMenuStrip ContextMenuStrip { + get { + return contextMenuStrip; + } + set { + contextMenuStrip = value; + } + } + + /// + /// + /// The first child node of this node. + /// + [Browsable(false)] + public TreeNode FirstNode { + get { + if (childCount == 0) return null; + return children[0]; + } + } + + private TreeNode FirstVisibleParent { + get { + TreeNode node = this; + while (node != null && node.Bounds.IsEmpty) { + node = node.Parent; + } + return node; + } + } + + /// + /// + /// The foreground color of this node. + /// If null, the color used will be the default color from the TreeView control that this + /// node is attached to + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeForeColorDescr) + ] + public Color ForeColor { + get { + if (propBag == null) return Color.Empty; + return propBag.ForeColor; + } + set { + Color oldfc = this.ForeColor; + // If we're setting the color to the default again, delete the propBag if it doesn't contain + // useful data. + if (value.IsEmpty) { + if (propBag != null) { + propBag.ForeColor = Color.Empty; + RemovePropBagIfEmpty(); + } + if (!oldfc.IsEmpty) InvalidateHostTree(); + return; + } + + // Not the default, so if necessary create a new propBag, and fill it with the new forecolor + + if (propBag == null) propBag = new OwnerDrawPropertyBag(); + propBag.ForeColor = value; + if (!value.Equals(oldfc)) InvalidateHostTree(); + } + } + + /// + /// + /// Returns the full path of this node. + /// The path consists of the labels of each of the nodes from the root to this node, + /// each separated by the pathSeperator. + /// + [Browsable(false)] + public string FullPath { + get { + TreeView tv = TreeView; + if (tv != null) { + StringBuilder path = new StringBuilder(); + GetFullPath(path, tv.PathSeparator); + return path.ToString(); + } + else + throw new InvalidOperationException(SR.GetString(SR.TreeNodeNoParent)); + } + } + + /// + /// + /// The HTREEITEM handle associated with this node. If the handle + /// has not yet been created, this will force handle creation. + /// + [Browsable(false)] + public IntPtr Handle { + get { + if (handle == IntPtr.Zero) { + TreeView.CreateControl(); // force handle creation + } + return handle; + } + } + + /// + /// + /// The index of the image to be displayed when the node is in the unselected state. + /// The image is contained in the ImageList referenced by the imageList property. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeImageIndexDescr), + TypeConverterAttribute(typeof(TreeViewImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + DefaultValue(-1), + RelatedImageList("TreeView.ImageList") + ] + public int ImageIndex { + get { return ImageIndexer.Index;} + set { + ImageIndexer.Index = value; + UpdateNode(NativeMethods.TVIF_IMAGE); + } + } + + + /// + /// + /// The index of the image to be displayed when the node is in the unselected state. + /// The image is contained in the ImageList referenced by the imageList property. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeImageKeyDescr), + TypeConverterAttribute(typeof(TreeViewImageKeyConverter)), + DefaultValue(""), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + RelatedImageList("TreeView.ImageList") + ] + public string ImageKey { + get {return ImageIndexer.Key;} + set { + ImageIndexer.Key = value; + UpdateNode(NativeMethods.TVIF_IMAGE); + } + } + + + /// + /// + /// Returns the position of this node in relation to its siblings + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeIndexDescr), + ] + public int Index { + get { return index;} + } + + /// + /// + /// Specifies whether this node is being edited by the user. + /// + [Browsable(false)] + public bool IsEditing { + get { + TreeView tv = TreeView; + + if (tv != null) + return tv.editNode == this; + + return false; + } + } + + /// + /// + /// Specifies whether this node is in the expanded state. + /// + [Browsable(false)] + public bool IsExpanded { + get { + if (handle == IntPtr.Zero) { + return expandOnRealization; + } + return(State & NativeMethods.TVIS_EXPANDED) != 0; + } + } + + /// + /// + /// Specifies whether this node is in the selected state. + /// + [Browsable(false)] + public bool IsSelected { + get { + if (handle == IntPtr.Zero) return false; + return(State & NativeMethods.TVIS_SELECTED) != 0; + } + } + + /// + /// + /// Specifies whether this node is visible. + /// + [Browsable(false)] + public bool IsVisible { + get { + if (handle == IntPtr.Zero) return false; + TreeView tv = this.TreeView; + if (tv.IsDisposed) { + return false; + } + + NativeMethods.RECT rc = new NativeMethods.RECT(); + unsafe { *((IntPtr *) &rc.left) = Handle; } + + bool visible = ((int)UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_GETITEMRECT, 1, ref rc) != 0); + if (visible) { + Size size = tv.ClientSize; + visible = (rc.bottom > 0 && rc.right > 0 && rc.top < size.Height && rc.left < size.Width); + } + return visible; + } + } + + /// + /// + /// The last child node of this node. + /// + [Browsable(false)] + public TreeNode LastNode { + get { + if (childCount == 0) return null; + return children[childCount-1]; + } + } + + + /// + /// + /// This denotes the depth of nesting of the treenode. + /// + [Browsable(false)] + public int Level { + get { + if (this.Parent == null) { + return 0; + } + else { + return Parent.Level + 1; + } + } + } + + + + /// + /// + /// The next sibling node. + /// + [Browsable(false)] + public TreeNode NextNode { + get { + if (index+1 < parent.Nodes.Count) { + return parent.Nodes[index+1]; + } + else { + return null; + } + } + } + + /// + /// + /// The next visible node. It may be a child, sibling, + /// or a node from another branch. + /// + [Browsable(false)] + public TreeNode NextVisibleNode { + get { + // TVGN_NEXTVISIBLE can only be sent if the specified node is visible. + // So before sending, we check if this node is visible. If not, we find the first visible parent. + // + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return null; + } + + TreeNode node = FirstVisibleParent; + + if (node != null) { + IntPtr next = UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), + NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_NEXTVISIBLE, node.Handle); + if (next != IntPtr.Zero) { + return tv.NodeFromHandle(next); + } + } + + return null; + } + } + + /// + /// + /// The font that will be used to draw this node + /// If null, the font used will be the default font from the TreeView control that this + /// node is attached to. + /// NOTE: If the node font is larger than the default font from the TreeView control, then + /// the node will be clipped. + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeNodeFontDescr), + DefaultValue(null) + ] + public Font NodeFont { + get { + if (propBag==null) return null; + return propBag.Font; + } + set { + Font oldfont = this.NodeFont; + // If we're setting the font to the default again, delete the propBag if it doesn't contain + // useful data. + if (value==null) { + if (propBag!=null) { + propBag.Font = null; + RemovePropBagIfEmpty(); + } + if (oldfont != null) InvalidateHostTree(); + return; + } + + // Not the default, so if necessary create a new propBag, and fill it with the font + + if (propBag==null) propBag = new OwnerDrawPropertyBag(); + propBag.Font = value; + if (!value.Equals(oldfont)) InvalidateHostTree(); + } + } + + /// + /// + /// [To be supplied.] + /// + [ + ListBindable(false), + Browsable(false) + ] + public TreeNodeCollection Nodes { + get { + if (nodes == null) { + nodes = new TreeNodeCollection(this); + } + return nodes; + } + } + + /// + /// + /// Retrieves parent node. + /// + [Browsable(false)] + public TreeNode Parent { + get { + TreeView tv = TreeView; + + // Don't expose the virtual root publicly + if (tv != null && parent == tv.root) { + return null; + } + + return parent; + } + } + + /// + /// + /// The previous sibling node. + /// + [Browsable(false)] + public TreeNode PrevNode { + get { + //fixedIndex is used for perf. optimization in case of adding big ranges of nodes + int currentInd = index; + int fixedInd = parent.Nodes.FixedIndex; + + if (fixedInd > 0) { + currentInd = fixedInd; + } + + if (currentInd > 0 && currentInd <= parent.Nodes.Count) { + return parent.Nodes[currentInd-1]; + } + else { + return null; + } + } + } + + /// + /// + /// The next visible node. It may be a parent, sibling, + /// or a node from another branch. + /// + [Browsable(false)] + public TreeNode PrevVisibleNode { + get { + // TVGN_PREVIOUSVISIBLE can only be sent if the specified node is visible. + // So before sending, we check if this node is visible. If not, we find the first visible parent. + // + TreeNode node = FirstVisibleParent; + TreeView tv = this.TreeView; + + if (node != null) { + if (tv == null || tv.IsDisposed) { + return null; + } + IntPtr prev = UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), + NativeMethods.TVM_GETNEXTITEM, + NativeMethods.TVGN_PREVIOUSVISIBLE, node.Handle); + if (prev != IntPtr.Zero) { + return tv.NodeFromHandle(prev); + } + } + + return null; + } + } + + /// + /// + /// The index of the image displayed when the node is in the selected state. + /// The image is contained in the ImageList referenced by the imageList property. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeSelectedImageIndexDescr), + TypeConverterAttribute(typeof(TreeViewImageIndexConverter)), + DefaultValue(-1), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RelatedImageList("TreeView.ImageList") + ] + public int SelectedImageIndex { + get { + return SelectedImageIndexer.Index; + } + set { + SelectedImageIndexer.Index = value; + UpdateNode(NativeMethods.TVIF_SELECTEDIMAGE); + } + } + + /// + /// + /// The index of the image displayed when the node is in the selected state. + /// The image is contained in the ImageList referenced by the imageList property. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeSelectedImageKeyDescr), + TypeConverterAttribute(typeof(TreeViewImageKeyConverter)), + DefaultValue(""), + RefreshProperties(RefreshProperties.Repaint), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RelatedImageList("TreeView.ImageList") + ] + public string SelectedImageKey { + get { + return SelectedImageIndexer.Key; + } + set { + SelectedImageIndexer.Key = value; + UpdateNode(NativeMethods.TVIF_SELECTEDIMAGE); + } + } + + /// + /// + /// Retrieve state bits for this node + /// + /// + internal int State { + get { + if (handle == IntPtr.Zero) + return 0; + + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return 0; + } + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + item.hItem = Handle; + item.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; + item.stateMask = NativeMethods.TVIS_SELECTED | NativeMethods.TVIS_EXPANDED; + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_GETITEM, 0, ref item); + return item.state; + } + } + + + + /// + /// + /// The key of the StateImage that the user want to display. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeStateImageKeyDescr), + TypeConverterAttribute(typeof(ImageKeyConverter)), + DefaultValue(""), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + RelatedImageList("TreeView.StateImageList") + ] + public string StateImageKey { + get { + return StateImageIndexer.Key; } + set { + if (StateImageIndexer.Key != value) { + StateImageIndexer.Key = value; + if (treeView != null && !treeView.CheckBoxes) + { + UpdateNode(NativeMethods.TVIF_STATE); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Localizable(true), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + DefaultValue(-1), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeNodeStateImageIndexDescr), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + RefreshProperties(RefreshProperties.Repaint), + RelatedImageList("TreeView.StateImageList") + ] + public int StateImageIndex { + get { + return (treeView == null || treeView.StateImageList == null) ? -1:StateImageIndexer.Index; + } + set { + if (value < -1 || value > ALLOWEDIMAGES) { + throw new ArgumentOutOfRangeException("StateImageIndex", SR.GetString(SR.InvalidArgument, "StateImageIndex", (value).ToString(CultureInfo.CurrentCulture))); + } + StateImageIndexer.Index = value; + if (treeView != null && !treeView.CheckBoxes) + { + UpdateNode(NativeMethods.TVIF_STATE); + } + } + } + + // + /// + [ + SRCategory(SR.CatData), + Localizable(false), + Bindable(true), + SRDescription(SR.ControlTagDescr), + DefaultValue(null), + TypeConverter(typeof(StringConverter)), + ] + public object Tag { + get { + return userData; + } + set { + userData = value; + } + } + + /// + /// + /// The label text for the tree node + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeTextDescr) + ] + public string Text { + get { + return text == null ? "" : text; + } + set { + this.text = value; + UpdateNode(NativeMethods.TVIF_TEXT); + } + } + + + /// + /// + /// The ToolTip text that will be displayed when the mouse hovers over the node. + /// + [ + Localizable(false), + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeToolTipTextDescr), + DefaultValue("") + ] + public string ToolTipText { + get { + return toolTipText; + } + set { + toolTipText = value; + } + } + + /// + /// + /// The name for the tree node - useful for indexing. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeNodeNodeNameDescr) + ] + public string Name { + get { + return name == null ? "" : name; + } + set { + this.name = value; + } + } + + + + + /// + /// + /// Return the TreeView control this node belongs to. + /// + [Browsable(false)] + public TreeView TreeView { + get { + if (treeView == null) + treeView = FindTreeView(); + return treeView; + } + } + + /// + /// + /// Adds a new child node at the appropriate sorted position + /// + /// + internal int AddSorted(TreeNode node) { + int index = 0; + int iMin, iLim, iT; + string nodeText = node.Text; + TreeView parentTreeView = TreeView; + + if (childCount > 0) { + if (parentTreeView.TreeViewNodeSorter == null) + { + CompareInfo compare = Application.CurrentCulture.CompareInfo; + + // Optimize for the case where they're already sorted + if (compare.Compare(children[childCount-1].Text, nodeText) <= 0) + index = childCount; + else { + // Insert at appropriate sorted spot + for (iMin = 0, iLim = childCount; iMin < iLim;) { + iT = (iMin + iLim) / 2; + if (compare.Compare(children[iT].Text, nodeText) <= 0) + iMin = iT + 1; + else + iLim = iT; + } + index = iMin; + } + } + else + { + IComparer sorter = parentTreeView.TreeViewNodeSorter; + // Insert at appropriate sorted spot + for (iMin = 0, iLim = childCount; iMin < iLim;) { + iT = (iMin + iLim) / 2; + if (sorter.Compare(children[iT] /*previous*/, node/*current*/) <= 0) + iMin = iT + 1; + else + iLim = iT; + } + index = iMin; + } + } + + node.SortChildren(parentTreeView); + InsertNodeAt(index, node); + + return index; + } + + /// + /// + /// Returns a TreeNode object for the given HTREEITEM handle + /// + public static TreeNode FromHandle(TreeView tree, IntPtr handle) { + // SECREVIEW: + // Demand before we pass the TreeNode form handle. + IntSecurity.ControlFromHandleOrLocation.Demand(); + return tree.NodeFromHandle(handle); + } + + private void SortChildren(TreeView parentTreeView) { + // + if (childCount > 0) { + TreeNode[] newOrder = new TreeNode[childCount]; + if (parentTreeView == null || parentTreeView.TreeViewNodeSorter == null) + { + CompareInfo compare = Application.CurrentCulture.CompareInfo; + for (int i = 0; i < childCount; i++) { + int min = -1; + for (int j = 0; j < childCount; j++) { + if (children[j] == null) + continue; + if (min == -1) { + min = j; + continue; + } + if (compare.Compare(children[j].Text, children[min].Text) <= 0) + min = j; + } + + Debug.Assert(min != -1, "Bad sorting"); + newOrder[i] = children[min]; + children[min] = null; + newOrder[i].index = i; + newOrder[i].SortChildren(parentTreeView); + } + children = newOrder; + } + else + { + IComparer sorter = parentTreeView.TreeViewNodeSorter; + for (int i = 0; i < childCount; i++) { + int min = -1; + for (int j = 0; j < childCount; j++) { + if (children[j] == null) + continue; + if (min == -1) { + min = j; + continue; + } + if (sorter.Compare(children[j] /*previous*/, children[min] /*current*/) <= 0) + min = j; + } + + Debug.Assert(min != -1, "Bad sorting"); + newOrder[i] = children[min]; + children[min] = null; + newOrder[i].index = i; + newOrder[i].SortChildren(parentTreeView); + } + children = newOrder; + + } + } + } + + + /// + /// + /// Initiate editing of the node's label. + /// Only effective if LabelEdit property is true. + /// + public void BeginEdit() { + if (handle != IntPtr.Zero) { + TreeView tv = TreeView; + if (tv.LabelEdit == false) + throw new InvalidOperationException(SR.GetString(SR.TreeNodeBeginEditFailed)); + if (!tv.Focused) + tv.FocusInternal(); + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_EDITLABEL, 0, handle); + } + } + + /// + /// Called by the tree node collection to clear all nodes. We optimize here if + /// this is the root node. + /// + internal void Clear() { + + + // This is a node that is a child of some other node. We have + // to selectively remove children here. + // + bool isBulkOperation = false; + TreeView tv = TreeView; + + + + try + { + + if (tv != null) { + tv.nodesCollectionClear = true; + + if (tv != null && childCount > MAX_TREENODES_OPS) { + isBulkOperation = true; + tv.BeginUpdate(); + } + } + + while(childCount > 0) { + children[childCount - 1].Remove(true); + } + children = null; + + + if (tv != null && isBulkOperation) { + tv.EndUpdate(); + } + } + finally + { + if (tv != null) { + tv.nodesCollectionClear = false; + } + nodesCleared = true; + } + } + + /// + /// + /// Clone the entire subtree rooted at this node. + /// + public virtual object Clone() { + Type clonedType = this.GetType(); + TreeNode node = null; + + if (clonedType == typeof(TreeNode)){ + node = new TreeNode(text, ImageIndexer.Index, SelectedImageIndexer.Index); + } + else { + // SECREVIEW : Late-binding does not represent a security thread, see bug#411899 for more info.. + // + node = (TreeNode)Activator.CreateInstance(clonedType); + } + + node.Text = text; + node.Name = name; + node.ImageIndexer.Index = ImageIndexer.Index; + node.SelectedImageIndexer.Index = SelectedImageIndexer.Index; + + node.StateImageIndexer.Index = StateImageIndexer.Index; + node.ToolTipText = toolTipText; + node.ContextMenu = contextMenu; + node.ContextMenuStrip = contextMenuStrip; + + // only set the key if it's set to something useful + if ( ! (string.IsNullOrEmpty(ImageIndexer.Key))) { + node.ImageIndexer.Key = ImageIndexer.Key; + } + + // only set the key if it's set to something useful + if (!(string.IsNullOrEmpty(SelectedImageIndexer.Key))) { + node.SelectedImageIndexer.Key = SelectedImageIndexer.Key; + } + + // only set the key if it's set to something useful + if (!(string.IsNullOrEmpty(StateImageIndexer.Key))) { + node.StateImageIndexer.Key = StateImageIndexer.Key; + } + + if (childCount > 0) { + node.children = new TreeNode[childCount]; + for (int i = 0; i < childCount; i++) + node.Nodes.Add((TreeNode)children[i].Clone()); + } + + // Clone properties + // + if (propBag != null) { + node.propBag = OwnerDrawPropertyBag.Copy(propBag); + } + node.Checked = this.Checked; + node.Tag = this.Tag; + + return node; + } + + private void CollapseInternal(bool ignoreChildren) + { + TreeView tv = TreeView; + bool setSelection = false; + collapseOnRealization = false; + expandOnRealization = false; + + if (tv == null || !tv.IsHandleCreated) { + collapseOnRealization = true; + return; + } + + //terminating condition for recursion... + // + if (ignoreChildren) + { + DoCollapse(tv); + } + else { + if (!ignoreChildren && childCount > 0) { + // Virtual root should collapse all its children + for (int i = 0; i < childCount; i++) { + if (tv.SelectedNode == children[i]) { + setSelection = true; + } + children[i].DoCollapse(tv); + children[i].Collapse(); + } + } + DoCollapse(tv); + } + + if (setSelection) + tv.SelectedNode = this; + tv.Invalidate(); + collapseOnRealization = false; + + } + + /// + /// + /// Collapse the node ignoring its children while collapsing the parent + /// + public void Collapse(bool ignoreChildren) + { + CollapseInternal(ignoreChildren); + } + + /// + /// + /// Collapse the node. + /// + public void Collapse() { + CollapseInternal(false); + } + + /// + /// + /// Windows TreeView doesn't send the proper notifications on collapse, so we do it manually. + /// + private void DoCollapse(TreeView tv) { + if ((State & NativeMethods.TVIS_EXPANDED) != 0) { + TreeViewCancelEventArgs e = new TreeViewCancelEventArgs(this, false, TreeViewAction.Collapse); + tv.OnBeforeCollapse(e); + if (!e.Cancel) { + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_EXPAND, NativeMethods.TVE_COLLAPSE, Handle); + tv.OnAfterCollapse(new TreeViewEventArgs(this)); + } + } + } + + /// + /// + /// [To be supplied.] + /// + protected virtual void Deserialize(SerializationInfo serializationInfo, StreamingContext context) { + + int childCount = 0; + int imageIndex = -1; + string imageKey = null; + + int selectedImageIndex = -1; + string selectedImageKey = null; + + int stateImageIndex = -1; + string stateImageKey = null; + + foreach (SerializationEntry entry in serializationInfo) { + switch (entry.Name) { + case "PropBag": + // SEC + propBag = (OwnerDrawPropertyBag)serializationInfo.GetValue(entry.Name, typeof(OwnerDrawPropertyBag)); + break; + case "Text": + Text = serializationInfo.GetString(entry.Name); + break; + case "ToolTipText": + ToolTipText = serializationInfo.GetString(entry.Name); + break; + case "Name": + Name = serializationInfo.GetString(entry.Name); + break; + case "IsChecked": + CheckedStateInternal = serializationInfo.GetBoolean(entry.Name); + break; + case "ImageIndex": + imageIndex = serializationInfo.GetInt32(entry.Name); + break; + case "SelectedImageIndex": + selectedImageIndex = serializationInfo.GetInt32(entry.Name); + break; + case "ImageKey": + imageKey = serializationInfo.GetString(entry.Name); + break; + case "SelectedImageKey": + selectedImageKey= serializationInfo.GetString(entry.Name); + break; + case "StateImageKey": + stateImageKey = serializationInfo.GetString(entry.Name); + break; + case "StateImageIndex": + stateImageIndex = serializationInfo.GetInt32(entry.Name); + break; + case "ChildCount": + childCount = serializationInfo.GetInt32(entry.Name); + break; + case "UserData": + userData = entry.Value; + break; + } + } + + // let imagekey take precidence + if (imageKey != null) { + ImageKey = imageKey; + } + else if (imageIndex != -1) { + ImageIndex = imageIndex; + } + + // let selectedimagekey take precidence + if (selectedImageKey != null) { + SelectedImageKey = selectedImageKey; + } + else if (selectedImageIndex != -1) { + SelectedImageIndex = selectedImageIndex; + } + + // let stateimagekey take precidence + if (stateImageKey != null) { + StateImageKey = stateImageKey; + } + else if (stateImageIndex != -1) { + StateImageIndex = stateImageIndex; + } + + if (childCount > 0) { + TreeNode[] childNodes = new TreeNode[childCount]; + + for (int i = 0; i < childCount; i++) { + // SEC + childNodes[i] = (TreeNode)serializationInfo.GetValue("children" + i, typeof(TreeNode)); + } + Nodes.AddRange(childNodes); + } + } + + /// + /// + /// Terminate the editing of any tree view item's label. + /// + public void EndEdit(bool cancel) { + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return; + } + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_ENDEDITLABELNOW, cancel?1:0, 0); + } + + /// + /// + /// Makes sure there is enough room to add n children + /// + /// + internal void EnsureCapacity(int num) { + Debug.Assert(num > 0,"required capacity can not be less than 1"); + int size = num; + if (size < 4) { + size = 4; + } + if (children == null) { + children = new TreeNode[size]; + } + else if (childCount + num > children.Length) { + int newSize = childCount + num; + if (num == 1) { + newSize = childCount * 2; + } + TreeNode[] bigger = new TreeNode[newSize]; + System.Array.Copy(children, 0, bigger, 0, childCount); + children = bigger; + } + } + + /// + /// + /// Ensures the the node's StateImageIndex value is properly set. + /// + /// + private void EnsureStateImageValue() + { + if (treeView == null) { + return; + } + + if (treeView.CheckBoxes && treeView.StateImageList != null) { + + if (!String.IsNullOrEmpty(this.StateImageKey)) { + this.StateImageIndex = (this.Checked) ? 1 : 0; + this.StateImageKey = treeView.StateImageList.Images.Keys[this.StateImageIndex]; + } + else { + this.StateImageIndex = (this.Checked) ? 1 : 0; + } + } + } + + /// + /// + /// Ensure that the node is visible, expanding nodes and scrolling the + /// TreeView control as necessary. + /// + public void EnsureVisible() { + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return; + } + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_ENSUREVISIBLE, 0, Handle); + } + + /// + /// + /// Expand the node. + /// + public void Expand() { + TreeView tv = TreeView; + if (tv == null || !tv.IsHandleCreated) { + expandOnRealization = true; + return; + } + + ResetExpandedState(tv); + if (!IsExpanded) { + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_EXPAND, NativeMethods.TVE_EXPAND, Handle); + } + expandOnRealization = false; + } + + + /// + /// + /// Expand the node. + /// + public void ExpandAll() { + Expand(); + for (int i = 0; i < childCount; i++) { + children[i].ExpandAll(); + } + + } + /// + /// + /// Locate this tree node's containing tree view control by scanning + /// up to the virtual root, whose treeView pointer we know to be + /// correct + /// + internal TreeView FindTreeView() { + TreeNode node = this; + while (node.parent != null) + node = node.parent; + return node.treeView; + } + + /// + /// + /// Helper function for getFullPath(). + /// + private void GetFullPath(StringBuilder path, string pathSeparator) { + if (parent != null) { + parent.GetFullPath(path, pathSeparator); + if (parent.parent != null) + path.Append(pathSeparator); + path.Append(this.text); + } + } + + /// + /// + /// Returns number of child nodes. + /// + public int GetNodeCount(bool includeSubTrees) { + int total = childCount; + if (includeSubTrees) { + for (int i = 0; i < childCount; i++) + total += children[i].GetNodeCount(true); + } + return total; + } + + /// + /// + /// Helper function to add node at a given index after all validation has been done + /// + /// + internal void InsertNodeAt(int index, TreeNode node) { + EnsureCapacity(1); + node.parent = this; + node.index = index; + for (int i = childCount; i > index; --i) { + (children[i] = children[i-1]).index = i; + } + children[index] = node; + childCount++; + node.Realize(false); + + if (TreeView != null && node == TreeView.selectedNode) + TreeView.SelectedNode = node; // communicate this to the handle + } + + /// + /// + /// Invalidates the treeview control that is hosting this node + /// + private void InvalidateHostTree() { + if (treeView != null && treeView.IsHandleCreated) treeView.Invalidate(); + } + + /// + /// + /// + /// + internal void Realize(bool insertFirst) { + // Debug.assert(handle == 0, "Node already realized"); + TreeView tv = this.TreeView; + if (tv == null || !tv.IsHandleCreated || tv.IsDisposed) + return; + + if (parent != null) { // Never realize the virtual root + + if (tv.InvokeRequired) { + throw new InvalidOperationException(SR.GetString(SR.InvalidCrossThreadControlCall)); + } + + NativeMethods.TV_INSERTSTRUCT tvis = new NativeMethods.TV_INSERTSTRUCT(); + tvis.item_mask = insertMask; + tvis.hParent = parent.handle; + TreeNode prev = PrevNode; + if (insertFirst || prev == null) { + tvis.hInsertAfter = (IntPtr)NativeMethods.TVI_FIRST; + } + else { + tvis.hInsertAfter = prev.handle; + // Debug.assert(tvis.hInsertAfter != 0); + } + + tvis.item_pszText = Marshal.StringToHGlobalAuto(text); + tvis.item_iImage = (ImageIndexer.ActualIndex == -1) ? tv.ImageIndexer.ActualIndex : ImageIndexer.ActualIndex; + tvis.item_iSelectedImage = (SelectedImageIndexer.ActualIndex == -1) ? tv.SelectedImageIndexer.ActualIndex : SelectedImageIndexer.ActualIndex; + tvis.item_mask = NativeMethods.TVIF_TEXT; + + tvis.item_stateMask = 0; + tvis.item_state = 0; + + if (tv.CheckBoxes) { + tvis.item_mask |= NativeMethods.TVIF_STATE; + tvis.item_stateMask |= NativeMethods.TVIS_STATEIMAGEMASK; + tvis.item_state |= CheckedInternal ? CHECKED : UNCHECKED; + } + else if (tv.StateImageList != null && StateImageIndexer.ActualIndex >= 0) { + tvis.item_mask |= NativeMethods.TVIF_STATE; + tvis.item_stateMask = NativeMethods.TVIS_STATEIMAGEMASK; + tvis.item_state = ((StateImageIndexer.ActualIndex + 1) << SHIFTVAL); + } + + + if (tvis.item_iImage >= 0) tvis.item_mask |= NativeMethods.TVIF_IMAGE; + if (tvis.item_iSelectedImage >= 0) tvis.item_mask |= NativeMethods.TVIF_SELECTEDIMAGE; + + // If you are editing when you add a new node, then the edit control + // gets placed in the wrong place. You must restore the edit mode + // asynchronously (PostMessage) after the add is complete + // to get the expected behavior. + // + bool editing = false; + IntPtr editHandle = UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_GETEDITCONTROL, 0, 0); + if (editHandle != IntPtr.Zero) { + // currently editing... + // + editing = true; + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_ENDEDITLABELNOW, 0 /* fCancel==FALSE */, 0); + } + + handle = UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_INSERTITEM, 0, ref tvis); + tv.nodeTable[handle] = this; + + // Lets update the Lparam to the Handle .... + UpdateNode(NativeMethods.TVIF_PARAM); + + Marshal.FreeHGlobal(tvis.item_pszText); + + if (editing) { + UnsafeNativeMethods.PostMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_EDITLABEL, IntPtr.Zero, handle); + } + + SafeNativeMethods.InvalidateRect(new HandleRef(tv, tv.Handle), null, false); + + if (parent.nodesCleared && (insertFirst || prev == null) && !tv.Scrollable) { + // We need to Redraw the TreeView ... + // If and only If we are not scrollable ... + // and this is the FIRST NODE to get added.. + // This is Comctl quirk where it just doesn't draw + // the first node after a Clear( ) if Scrollable == false. + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.WM_SETREDRAW, 1, 0); + nodesCleared = false; + } + + } + + for (int i = childCount - 1; i >= 0; i--) + children[i].Realize(true); + + // If node expansion was requested before the handle was created, + // we can expand it now. + if (expandOnRealization) { + Expand(); + } + + // If node collapse was requested before the handle was created, + // we can expand it now. + if (collapseOnRealization) { + Collapse(); + } + } + + /// + /// + /// Remove this node from the TreeView control. Child nodes are also removed from the + /// TreeView, but are still attached to this node. + /// + public void Remove() { + Remove(true); + } + + /// + /// + /// + /// + internal void Remove(bool notify) { + bool expanded = IsExpanded; + + // unlink our children + // + + for (int i = 0; i < childCount; i++) + children[i].Remove(false); + // children = null; + // unlink ourself + if (notify && parent != null) { + for (int i = index; i < parent.childCount-1; ++i) { + (parent.children[i] = parent.children[i+1]).index = i; + } + + // Fix Dev10 Bug 473773 - TreeViewNodeCollection.AddRange adds nodes in incorrect order + // should always release the last node + parent.children[parent.childCount - 1] = null; + parent.childCount--; + parent = null; + } + // Expand when we are realized the next time. + expandOnRealization = expanded; + + // unrealize ourself + TreeView tv = this.TreeView; + if (tv == null || tv.IsDisposed) { + return; + } + + if (handle != IntPtr.Zero) { + if (notify && tv.IsHandleCreated) + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_DELETEITEM, 0, handle); + treeView.nodeTable.Remove(handle); + handle = IntPtr.Zero; + } + treeView = null; + } + + /// + /// + /// Removes the propBag object if it's now devoid of useful data + /// + /// + private void RemovePropBagIfEmpty() { + if (propBag==null) return; + if (propBag.IsEmpty()) propBag = null; + return; + } + + private void ResetExpandedState(TreeView tv) { + Debug.Assert(tv.IsHandleCreated, "nonexistent handle"); + + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + item.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; + item.hItem = handle; + item.stateMask = NativeMethods.TVIS_EXPANDEDONCE; + item.state = 0; + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_SETITEM, 0, ref item); + } + + private bool ShouldSerializeBackColor() { + return BackColor != Color.Empty; + } + + private bool ShouldSerializeForeColor() { + return ForeColor != Color.Empty; + } + + /// + /// + /// Saves this TreeNode object to the given data stream. + /// + /// Review: Changing this would break VB users. so suppresing this message. + /// SECREVIEW: Since ISerializable.GetObjectData and Deserialize require SerializationFormatter - locking down. + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.SerializationFormatter), + SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + protected virtual void Serialize(SerializationInfo si, StreamingContext context) { + if (propBag != null) { + si.AddValue("PropBag", propBag, typeof(OwnerDrawPropertyBag)); + } + + si.AddValue("Text", text); + si.AddValue("ToolTipText", toolTipText); + si.AddValue("Name", Name); + si.AddValue("IsChecked", treeNodeState[TREENODESTATE_isChecked]); + si.AddValue("ImageIndex", ImageIndexer.Index); + si.AddValue("ImageKey", ImageIndexer.Key); + si.AddValue("SelectedImageIndex", SelectedImageIndexer.Index); + si.AddValue("SelectedImageKey", SelectedImageIndexer.Key); + + if (this.treeView != null && this.treeView.StateImageList != null) { + si.AddValue("StateImageIndex", StateImageIndexer.Index); + } + + if (this.treeView != null && this.treeView.StateImageList != null) { + si.AddValue("StateImageKey", StateImageIndexer.Key); + } + + si.AddValue("ChildCount", childCount); + + if (childCount > 0) { + for (int i = 0; i < childCount; i++) { + si.AddValue("children" + i, children[i], typeof(TreeNode)); + } + } + + if (userData != null && userData.GetType().IsSerializable) { + si.AddValue("UserData", userData, userData.GetType()); + } + } + /// + /// + /// Toggle the state of the node. Expand if collapsed or collapse if + /// expanded. + /// + public void Toggle() { + Debug.Assert(parent != null, "toggle on virtual root"); + + // I don't use the TVE_TOGGLE message 'cuz Windows TreeView doesn't send the appropriate + // notifications when collapsing. + if (IsExpanded) { + Collapse(); + } + else { + Expand(); + } + } + + + /// + /// + /// Returns the label text for the tree node + /// + public override string ToString() { + return "TreeNode: " + (text == null ? "" : text); + } + + /// + /// + /// Tell the TreeView to refresh this node + /// + private void UpdateNode(int mask) { + if (handle == IntPtr.Zero) return; + TreeView tv = TreeView; + Debug.Assert(tv != null, "TreeNode has handle but no TreeView"); + + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + item.mask = NativeMethods.TVIF_HANDLE | mask; + item.hItem = handle; + if ((mask & NativeMethods.TVIF_TEXT) != 0) + item.pszText = Marshal.StringToHGlobalAuto(text); + if ((mask & NativeMethods.TVIF_IMAGE) != 0) + item.iImage = (ImageIndexer.ActualIndex == -1) ? tv.ImageIndexer.ActualIndex : ImageIndexer.ActualIndex; + if ((mask & NativeMethods.TVIF_SELECTEDIMAGE) != 0) + item.iSelectedImage = (SelectedImageIndexer.ActualIndex == -1) ? tv.SelectedImageIndexer.ActualIndex : SelectedImageIndexer.ActualIndex; + if ((mask & NativeMethods.TVIF_STATE) != 0) { + item.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; + if (StateImageIndexer.ActualIndex != -1) { + item.state = ((StateImageIndexer.ActualIndex + 1) << SHIFTVAL); + } + // VSWhidbey 143401: ActualIndex == -1 means "don't use custom image list" + // so just leave item.state set to zero, that tells the unmanaged control + // to use no state image for this node. + } + if ((mask & NativeMethods.TVIF_PARAM) != 0) { + item.lParam = handle; + } + + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_SETITEM, 0, ref item); + if ((mask & NativeMethods.TVIF_TEXT) != 0) { + Marshal.FreeHGlobal(item.pszText); + if (tv.Scrollable) + tv.ForceScrollbarUpdate(false); + } + } + + internal void UpdateImage () + { + TreeView tv = this.TreeView; + if (tv.IsDisposed) { + return; + } + + NativeMethods.TV_ITEM item = new NativeMethods.TV_ITEM(); + + item.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_IMAGE; + item.hItem = Handle; + item.iImage = Math.Max(0, ((ImageIndexer.ActualIndex >= tv.ImageList.Images.Count) ? tv.ImageList.Images.Count - 1 : ImageIndexer.ActualIndex)); + UnsafeNativeMethods.SendMessage(new HandleRef(tv, tv.Handle), NativeMethods.TVM_SETITEM, 0, ref item); + } + + /// + /// + /// ISerializable private implementation + /// + /// + [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { + Serialize(si, context); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeClickEventArgs.cs b/WindowsForms/Managed/System/WinForms/TreeNodeClickEventArgs.cs new file mode 100644 index 000000000..086f1fae5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeClickEventArgs.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the + /// or event. + /// + /// + public class TreeNodeMouseClickEventArgs : MouseEventArgs { + + private TreeNode node; + + + /// + /// + /// [To be supplied.] + /// + public TreeNodeMouseClickEventArgs(TreeNode node, MouseButtons button, int clicks, int x, int y) + : base(button, clicks, x, y, 0) { + this.node = node; + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode Node { + get { + return node; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeClickEventHandler.cs b/WindowsForms/Managed/System/WinForms/TreeNodeClickEventHandler.cs new file mode 100644 index 000000000..89bf2de25 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeClickEventHandler.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will + /// handle the , , , or event of a + /// . + /// + /// + /// + public delegate void TreeNodeMouseClickEventHandler(object sender, TreeNodeMouseClickEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeCollection.cs b/WindowsForms/Managed/System/WinForms/TreeNodeCollection.cs new file mode 100644 index 000000000..e02770399 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeCollection.cs @@ -0,0 +1,630 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* +*/ +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + + using System; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Diagnostics; + using System.Drawing.Design; + using System.Globalization; + + /// + /// + /// [To be supplied.] + /// + [ + Editor("System.Windows.Forms.Design.TreeNodeCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) + ] + public class TreeNodeCollection : IList { + private TreeNode owner; + + /// A caching mechanism for key accessor + /// We use an index here rather than control so that we don't have lifetime + /// issues by holding on to extra references. + private int lastAccessedIndex = -1; + + //this index is used to optimize performance of AddRange + //items are added from last to first after this index + //(to work around TV_INSertItem comctl32 perf issue with consecutive adds in the end of the list) + private int fixedIndex = -1; + + + internal TreeNodeCollection(TreeNode owner) { + this.owner = owner; + } + + /// + /// + internal int FixedIndex + { + get { + return fixedIndex; + } + set { + fixedIndex = value; + } + } + + + /// + /// + /// [To be supplied.] + /// + public virtual TreeNode this[int index] { + get { + if (index < 0 || index >= owner.childCount) { + throw new ArgumentOutOfRangeException("index"); + } + return owner.children[index]; + } + set { + if (index < 0 || index >= owner.childCount) + throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); + value.parent = owner; + value.index = index; + owner.children[index] = value; + value.Realize(false); + } + } + + /// + /// + object IList.this[int index] { + get { + return this[index]; + } + set { + if (value is TreeNode) { + this[index] = (TreeNode)value; + } + else { + throw new ArgumentException(SR.GetString(SR.TreeNodeCollectionBadTreeNode), "value"); + } + } + } + + /// + /// + /// Retrieves the child control with the specified key. + /// + public virtual TreeNode this[string key] { + get { + // We do not support null and empty string as valid keys. + if (string.IsNullOrEmpty(key)){ + return null; + } + + // Search for the key in our collection + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + return this[index]; + } + else { + return null; + } + + } + } + /// + /// + /// [To be supplied.] + /// + // VSWhidbey 152051: Make this property available to Intellisense. (Removed the EditorBrowsable attribute.) + [Browsable(false)] + public int Count { + get { + return owner.childCount; + } + } + + /// + /// + object ICollection.SyncRoot { + get { + return this; + } + } + + /// + /// + bool ICollection.IsSynchronized { + get { + return false; + } + } + + /// + /// + bool IList.IsFixedSize { + get { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string text) { + TreeNode tn = new TreeNode(text); + Add(tn); + return tn; + } + + // <-- NEW ADD OVERLOADS IN WHIDBEY + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string key, string text) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + Add(tn); + return tn; + } + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string key, string text, int imageIndex) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageIndex = imageIndex; + Add(tn); + return tn; + } + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string key, string text, string imageKey) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageKey = imageKey; + Add(tn); + return tn; + } + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string key, string text, int imageIndex, int selectedImageIndex) { + TreeNode tn = new TreeNode(text, imageIndex, selectedImageIndex); + tn.Name = key; + Add(tn); + return tn; + } + + /// + /// + /// Creates a new child node under this node. Child node is positioned after siblings. + /// + public virtual TreeNode Add(string key, string text, string imageKey, string selectedImageKey) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageKey = imageKey; + tn.SelectedImageKey = selectedImageKey; + Add(tn); + return tn; + } + + // END - NEW ADD OVERLOADS IN WHIDBEY --> + + /// + /// + /// [To be supplied.] + /// + public virtual void AddRange(TreeNode[] nodes) { + if (nodes == null) { + throw new ArgumentNullException("nodes"); + } + if (nodes.Length == 0) + return; + TreeView tv = owner.TreeView; + if (tv != null && nodes.Length > TreeNode.MAX_TREENODES_OPS) { + tv.BeginUpdate(); + } + owner.Nodes.FixedIndex = owner.childCount; + owner.EnsureCapacity(nodes.Length); + for (int i = nodes.Length-1 ; i >= 0; i--) { + AddInternal(nodes[i],i); + } + owner.Nodes.FixedIndex = -1; + if (tv != null && nodes.Length > TreeNode.MAX_TREENODES_OPS) { + tv.EndUpdate(); + } + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode[] Find (string key, bool searchAllChildren) { + ArrayList foundNodes = FindInternal(key, searchAllChildren, this, new ArrayList()); + + // + TreeNode[] stronglyTypedFoundNodes = new TreeNode[foundNodes.Count]; + foundNodes.CopyTo(stronglyTypedFoundNodes, 0); + + return stronglyTypedFoundNodes; + } + + private ArrayList FindInternal(string key, bool searchAllChildren, TreeNodeCollection treeNodeCollectionToLookIn, ArrayList foundTreeNodes) { + if ((treeNodeCollectionToLookIn == null) || (foundTreeNodes == null)) { + return null; + } + + // Perform breadth first search - as it's likely people will want tree nodes belonging + // to the same parent close to each other. + + for (int i = 0; i < treeNodeCollectionToLookIn.Count; i++) { + if (treeNodeCollectionToLookIn[i] == null){ + continue; + } + + if (WindowsFormsUtils.SafeCompareStrings(treeNodeCollectionToLookIn[i].Name, key, /* ignoreCase = */ true)) { + foundTreeNodes.Add(treeNodeCollectionToLookIn[i]); + } + } + + // Optional recurive search for controls in child collections. + + if (searchAllChildren){ + for (int i = 0; i < treeNodeCollectionToLookIn.Count; i++) { + if (treeNodeCollectionToLookIn[i] == null){ + continue; + } + if ((treeNodeCollectionToLookIn[i].Nodes != null) && treeNodeCollectionToLookIn[i].Nodes.Count > 0){ + // if it has a valid child collecion, append those results to our collection + foundTreeNodes = FindInternal(key, searchAllChildren, treeNodeCollectionToLookIn[i].Nodes, foundTreeNodes); + } + } + } + return foundTreeNodes; + } + + /// + /// + /// Adds a new child node to this node. Child node is positioned after siblings. + /// + public virtual int Add(TreeNode node) { + return AddInternal(node, 0); + } + + + private int AddInternal(TreeNode node, int delta) { + if (node == null) { + throw new ArgumentNullException("node"); + } + if (node.handle != IntPtr.Zero) + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, node.Text), "node"); + + // If the TreeView is sorted, index is ignored + TreeView tv = owner.TreeView; + if (tv != null && tv.Sorted) { + return owner.AddSorted(node); + } + node.parent = owner; + int fixedIndex = owner.Nodes.FixedIndex; + if (fixedIndex != -1) { + node.index = fixedIndex + delta; + } + else { + //if fixedIndex != -1 capacity was ensured by AddRange + Debug.Assert(delta == 0,"delta should be 0"); + owner.EnsureCapacity(1); + node.index = owner.childCount; + } + owner.children[node.index] = node; + owner.childCount++; + node.Realize(false); + + if (tv != null && node == tv.selectedNode) + tv.SelectedNode = node; // communicate this to the handle + + if (tv != null && tv.TreeViewNodeSorter != null) { + tv.Sort(); + } + + return node.index; + } + + /// + /// + int IList.Add(object node) { + if (node == null) { + throw new ArgumentNullException("node"); + } + else if (node is TreeNode) { + return Add((TreeNode)node); + } + else + { + return Add(node.ToString()).index; + } + } + + /// + /// + /// [To be supplied.] + /// + public bool Contains(TreeNode node) { + return IndexOf(node) != -1; + } + + /// + /// + /// Returns true if the collection contains an item with the specified key, false otherwise. + /// + public virtual bool ContainsKey(string key) { + return IsValidIndex(IndexOfKey(key)); + } + + + /// + /// + bool IList.Contains(object node) { + if (node is TreeNode) { + return Contains((TreeNode)node); + } + else { + return false; + } + } + + /// + /// + /// [To be supplied.] + /// + public int IndexOf(TreeNode node) { + for(int index=0; index < Count; ++index) { + if (this[index] == node) { + return index; + } + } + return -1; + } + + /// + /// + int IList.IndexOf(object node) { + if (node is TreeNode) { + return IndexOf((TreeNode)node); + } + else { + return -1; + } + } + + + /// + /// + /// The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1. + /// + public virtual int IndexOfKey(String key) { + // Step 0 - Arg validation + if (string.IsNullOrEmpty(key)){ + return -1; // we dont support empty or null keys. + } + + // step 1 - check the last cached item + if (IsValidIndex(lastAccessedIndex)) + { + if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) { + return lastAccessedIndex; + } + } + + // step 2 - search for the item + for (int i = 0; i < this.Count; i ++) { + if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) { + lastAccessedIndex = i; + return i; + } + } + + // step 3 - we didn't find it. Invalidate the last accessed index and return -1. + lastAccessedIndex = -1; + return -1; + } + + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual void Insert(int index, TreeNode node) { + if (node.handle != IntPtr.Zero) + throw new ArgumentException(SR.GetString(SR.OnlyOneControl, node.Text), "node"); + + // If the TreeView is sorted, index is ignored + TreeView tv = owner.TreeView; + if (tv != null && tv.Sorted) { + owner.AddSorted(node); + return; + } + + if (index < 0) index = 0; + if (index > owner.childCount) index = owner.childCount; + owner.InsertNodeAt(index, node); + } + + /// + /// + void IList.Insert(int index, object node) { + if (node is TreeNode) { + Insert(index, (TreeNode)node); + } + else { + throw new ArgumentException(SR.GetString(SR.TreeNodeCollectionBadTreeNode), "node"); + } + } + + // <-- NEW INSERT OVERLOADS IN WHIDBEY + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string text) { + TreeNode tn = new TreeNode(text); + Insert(index, tn); + return tn; + } + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string key, string text) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + Insert(index, tn); + return tn; + } + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string key, string text, int imageIndex) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageIndex = imageIndex; + Insert(index, tn); + return tn; + } + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string key, string text, string imageKey) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageKey = imageKey; + Insert(index, tn); + return tn; + } + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string key, string text, int imageIndex, int selectedImageIndex) { + TreeNode tn = new TreeNode(text, imageIndex, selectedImageIndex); + tn.Name = key; + Insert(index, tn); + return tn; + } + + /// + /// + /// Inserts a new child node on this node. Child node is positioned as specified by index. + /// + public virtual TreeNode Insert(int index, string key, string text, string imageKey, string selectedImageKey) { + TreeNode tn = new TreeNode(text); + tn.Name = key; + tn.ImageKey = imageKey; + tn.SelectedImageKey = selectedImageKey; + Insert(index, tn); + return tn; + } + + // END - NEW INSERT OVERLOADS IN WHIDBEY --> + + /// + /// + /// Determines if the index is valid for the collection. + /// + /// + private bool IsValidIndex(int index) { + return ((index >= 0) && (index < this.Count)); + } + + /// + /// + /// Remove all nodes from the tree view. + /// + public virtual void Clear() { + owner.Clear(); + } + + /// + /// + /// [To be supplied.] + /// + public void CopyTo(Array dest, int index) { + if (owner.childCount > 0) { + System.Array.Copy(owner.children, 0, dest, index, owner.childCount); + } + } + + /// + /// + /// [To be supplied.] + /// + public void Remove(TreeNode node) { + node.Remove(); + } + + /// + /// + void IList.Remove(object node) { + if (node is TreeNode ) { + Remove((TreeNode)node); + } + } + + /// + /// + /// [To be supplied.] + /// + public virtual void RemoveAt(int index) { + this[index].Remove(); + } + + /// + /// + /// Removes the child control with the specified key. + /// + public virtual void RemoveByKey(string key) { + int index = IndexOfKey(key); + if (IsValidIndex(index)) { + RemoveAt(index); + } + } + + + /// + /// + /// [To be supplied.] + /// + public IEnumerator GetEnumerator() { + return new WindowsFormsUtils.ArraySubsetEnumerator(owner.children, owner.childCount); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeConverter.cs b/WindowsForms/Managed/System/WinForms/TreeNodeConverter.cs new file mode 100644 index 000000000..c630d9bba --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeConverter.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + + /// + /// + /// TreeNodeConverter is a class that can be used to convert + /// TreeNode objects from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class TreeNodeConverter : TypeConverter { + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(InstanceDescriptor) && value is TreeNode) { + TreeNode node = (TreeNode)value; + MemberInfo info = null; + object[] args = null; + + if (node.ImageIndex == -1 || node.SelectedImageIndex == -1) { + if (node.Nodes.Count == 0) { + info = typeof(TreeNode).GetConstructor(new Type[] {typeof(string)}); + args = new object[] {node.Text}; + } + else { + info = typeof(TreeNode).GetConstructor(new Type[] {typeof(string), typeof(TreeNode[])}); + + TreeNode[] nodesArray = new TreeNode[node.Nodes.Count]; + node.Nodes.CopyTo(nodesArray, 0); + + args = new object[] {node.Text, nodesArray}; + } + } + else { + if (node.Nodes.Count == 0) { + info = typeof(TreeNode).GetConstructor(new Type[] { + typeof(string), + typeof(int), + typeof(int)}); + args = new object[] { + node.Text, + node.ImageIndex, + node.SelectedImageIndex}; + } + else { + info = typeof(TreeNode).GetConstructor(new Type[] { + typeof(string), + typeof(int), + typeof(int), + typeof(TreeNode[])}); + + TreeNode[] nodesArray = new TreeNode[node.Nodes.Count]; + node.Nodes.CopyTo(nodesArray, 0); + + args = new object[] { + node.Text, + node.ImageIndex, + node.SelectedImageIndex, + nodesArray}; + } + } + + if (info != null) { + return new InstanceDescriptor(info, args, false); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEvent.cs b/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEvent.cs new file mode 100644 index 000000000..d710c4c9a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEvent.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the event. + /// + /// + [System.Runtime.InteropServices.ComVisible(true)] + public class TreeNodeMouseHoverEventArgs : EventArgs { + readonly TreeNode node; + + /// + /// + /// [To be supplied.] + /// + public TreeNodeMouseHoverEventArgs(TreeNode node) { + this.node = node; + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode Node { + get { return node; } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEventHandler.cs b/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEventHandler.cs new file mode 100644 index 000000000..2fc0a7d19 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeMouseHoverEventHandler.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the + /// + /// event of a + /// + /// . + /// + /// + /// + public delegate void TreeNodeMouseHoverEventHandler(object sender, TreeNodeMouseHoverEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/TreeNodeState.cs b/WindowsForms/Managed/System/WinForms/TreeNodeState.cs new file mode 100644 index 000000000..3d11b4570 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeNodeState.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + /// + /// + /// + /// + /// Gives state information about a TreeView node. Used with owner draw. + /// + /// + /// + + [Flags] + public enum TreeNodeStates { + /// + /// + /// + /// [To be supplied.] + /// + /// + Checked = NativeMethods.CDIS_CHECKED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Default = NativeMethods.CDIS_DEFAULT, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Focused = NativeMethods.CDIS_FOCUS, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Grayed = NativeMethods.CDIS_GRAYED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Hot = NativeMethods.CDIS_HOT, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Indeterminate = NativeMethods.CDIS_INDETERMINATE, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Marked = NativeMethods.CDIS_MARKED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + Selected = NativeMethods.CDIS_SELECTED, + + /// + /// + /// + /// [To be supplied.] + /// + /// + ShowKeyboardCues = NativeMethods.CDIS_SHOWKEYBOARDCUES + + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeView.cs b/WindowsForms/Managed/System/WinForms/TreeView.cs new file mode 100644 index 000000000..bb726604f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeView.cs @@ -0,0 +1,3419 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System; + using System.Security.Permissions; + using System.Security; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Drawing.Design; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Collections; + using Microsoft.Win32; + using System.Reflection; + using System.Windows.Forms.Layout; + using System.Globalization; + using System.Windows.Forms.VisualStyles; + + /// + /// + /// + /// Displays a hierarchical list of items, or nodes. Each + /// node includes a caption and an optional bitmap. The user can select a node. If + /// it has sub-nodes, the user can collapse or expand the node. + /// + /// + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + DefaultProperty("Nodes"), + DefaultEvent("AfterSelect"), + Docking(DockingBehavior.Ask), + Designer("System.Windows.Forms.Design.TreeViewDesigner, " + AssemblyRef.SystemDesign), + SRDescription(SR.DescriptionTreeView) + ] + public class TreeView : Control { + + private const int MaxIndent = 32000; // Maximum allowable TreeView indent + private const string backSlash = "\\"; + private const int DefaultTreeViewIndent = 19; + + private DrawTreeNodeEventHandler onDrawNode; + private NodeLabelEditEventHandler onBeforeLabelEdit; + private NodeLabelEditEventHandler onAfterLabelEdit; + private TreeViewCancelEventHandler onBeforeCheck; + private TreeViewEventHandler onAfterCheck; + private TreeViewCancelEventHandler onBeforeCollapse; + private TreeViewEventHandler onAfterCollapse; + private TreeViewCancelEventHandler onBeforeExpand; + private TreeViewEventHandler onAfterExpand; + private TreeViewCancelEventHandler onBeforeSelect; + private TreeViewEventHandler onAfterSelect; + private ItemDragEventHandler onItemDrag; + private TreeNodeMouseHoverEventHandler onNodeMouseHover; + private EventHandler onRightToLeftLayoutChanged; + + internal TreeNode selectedNode = null; + private ImageList.Indexer imageIndexer; + private ImageList.Indexer selectedImageIndexer; + private bool setOddHeight = false; + private TreeNode prevHoveredNode = null; + private bool hoveredAlready = false; + private bool rightToLeftLayout = false; + + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private IntPtr hNodeMouseDown = IntPtr.Zero;//ensures we fire nodeclick on the correct node + + private const int TREEVIEWSTATE_hideSelection = 0x00000001; + private const int TREEVIEWSTATE_labelEdit = 0x00000002; + private const int TREEVIEWSTATE_scrollable = 0x00000004; + private const int TREEVIEWSTATE_checkBoxes = 0x00000008; + private const int TREEVIEWSTATE_showLines = 0x00000010; + private const int TREEVIEWSTATE_showPlusMinus = 0x00000020; + private const int TREEVIEWSTATE_showRootLines = 0x00000040; + private const int TREEVIEWSTATE_sorted = 0x00000080; + private const int TREEVIEWSTATE_hotTracking = 0x00000100; + private const int TREEVIEWSTATE_fullRowSelect = 0x00000200; + private const int TREEVIEWSTATE_showNodeToolTips = 0x00000400; + private const int TREEVIEWSTATE_doubleclickFired = 0x00000800; + private const int TREEVIEWSTATE_mouseUpFired = 0x00001000; + private const int TREEVIEWSTATE_showTreeViewContextMenu = 0x00002000; + private const int TREEVIEWSTATE_lastControlValidated = 0x00004000; + private const int TREEVIEWSTATE_stopResizeWindowMsgs = 0x00008000;//VSWhidbey 466949 + private const int TREEVIEWSTATE_ignoreSelects = 0x00010000;//VSWhidbey 384294, 490763. + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 treeViewState; // see TREEVIEWSTATE_ consts above + + private static bool isScalingInitialized = false; + private static Size? scaledStateImageSize = null; + private static Size? ScaledStateImageSize { + get { + if (!isScalingInitialized) { + if (DpiHelper.IsScalingRequired) { + scaledStateImageSize = DpiHelper.LogicalToDeviceUnits(new Size(16, 16)); + } + isScalingInitialized = true; + } + return scaledStateImageSize; + } + } + + /// + /// + internal ImageList.Indexer ImageIndexer { + get { + if (imageIndexer == null) { + imageIndexer = new ImageList.Indexer(); + } + imageIndexer.ImageList = ImageList; + return imageIndexer; + } + } + + /// + /// + internal ImageList.Indexer SelectedImageIndexer { + get { + if (selectedImageIndexer == null) { + selectedImageIndexer = new ImageList.Indexer(); + } + selectedImageIndexer.ImageList = ImageList; + + return selectedImageIndexer; + } + } + + private ImageList imageList; + private int indent = -1; + private int itemHeight = -1; + private string pathSeparator = backSlash; + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + + internal TreeNodeCollection nodes = null; + internal TreeNode editNode; + internal TreeNode root; + internal Hashtable nodeTable = new Hashtable(); + internal bool nodesCollectionClear = false; //this is set when the treeNodeCollection is getting cleared and used by TreeView + private MouseButtons downButton; + private TreeViewDrawMode drawMode = TreeViewDrawMode.Normal; + + //Properties newly added to TreeView.... + private ImageList internalStateImageList; + private TreeNode topNode; + private ImageList stateImageList; + private Color lineColor; + private string controlToolTipText = null; + + // Sorting + private IComparer treeViewNodeSorter = null; + + + //Events + private TreeNodeMouseClickEventHandler onNodeMouseClick; + private TreeNodeMouseClickEventHandler onNodeMouseDoubleClick; + + /// + /// + /// Creates a TreeView control + /// + public TreeView() + : base() { + + this.treeViewState = new System.Collections.Specialized.BitVector32(TREEVIEWSTATE_showRootLines | + TREEVIEWSTATE_showPlusMinus | + TREEVIEWSTATE_showLines | + TREEVIEWSTATE_scrollable | + TREEVIEWSTATE_hideSelection); + + root = new TreeNode(this); + + // TreeView must always have an ImageIndex. + SelectedImageIndexer.Index = 0; + ImageIndexer.Index = 0; + + SetStyle(ControlStyles.UserPaint, false); + SetStyle(ControlStyles.StandardClick, false); + SetStyle(ControlStyles.UseTextForAccessibility, false); + } + + /// + /// + /// The background color for this control. Specifying null for + /// this parameter sets the + /// control's background color to its parent's background color. + /// + public override Color BackColor { + get { + if (ShouldSerializeBackColor()) { + return base.BackColor; + } + else { + return SystemColors.Window; + } + } + + set { + base.BackColor = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); + + // This is to get around a problem in the comctl control where the lines + // connecting nodes don't get the new BackColor. This messages forces + // reconstruction of the line bitmaps without changing anything else. + SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); + } + } + } + + /// + /// + /// [To be supplied.] + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// The border style of the window. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.borderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + if (borderStyle != value) { + //verify that 'value' is a valid enum type... + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + borderStyle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// The value of the CheckBoxes property. The CheckBoxes + /// property determines if check boxes are shown next to node in the + /// tree view. + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(false), + SRDescription(SR.TreeViewCheckBoxesDescr) + ] + public bool CheckBoxes { + get { + return treeViewState[TREEVIEWSTATE_checkBoxes]; + } + + set { + if (CheckBoxes != value) { + treeViewState[TREEVIEWSTATE_checkBoxes] = value; + if (IsHandleCreated) { + if (CheckBoxes) { + UpdateStyles(); + } else { + // Going from true to false requires recreation + + // Reset the Checked state after setting the checkboxes (this was Everett behavior) + // The implementation of the TreeNode.Checked property has changed in Whidbey + // So we need to explicit set the Checked state to false to keep the everett behavior. + UpdateCheckedState(root, false); + RecreateHandle(); + } + } + } + } + } + + /// + /// + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ClassName = NativeMethods.WC_TREEVIEW; + + + // V#45599 Keep the scrollbar if we are just updating styles... + // + if (IsHandleCreated) { + int currentStyle = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + cp.Style |= (currentStyle & (NativeMethods.WS_HSCROLL | NativeMethods.WS_VSCROLL)); + } + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + + if (!Scrollable) + cp.Style |= NativeMethods.LVS_NOSCROLL; + + if (!HideSelection) + cp.Style |= NativeMethods.TVS_SHOWSELALWAYS; + if (LabelEdit) + cp.Style |= NativeMethods.TVS_EDITLABELS; + if (ShowLines) + cp.Style |= NativeMethods.TVS_HASLINES; + if (ShowPlusMinus) + cp.Style |= NativeMethods.TVS_HASBUTTONS; + if (ShowRootLines) + cp.Style |= NativeMethods.TVS_LINESATROOT; + if (HotTracking) + cp.Style |= NativeMethods.TVS_TRACKSELECT; + if (FullRowSelect) + cp.Style |= NativeMethods.TVS_FULLROWSELECT; + if (setOddHeight) { + cp.Style |= NativeMethods.TVS_NONEVENHEIGHT; + } + + // Don't set TVS_CHECKBOXES here if the window isn't created yet. + // See OnHandleCreated for explanation + if (ShowNodeToolTips && IsHandleCreated && !DesignMode) { + cp.Style |= NativeMethods.TVS_INFOTIP; + } + + + // Don't set TVS_CHECKBOXES here if the window isn't created yet. + // See OnHandleCreated for explanation + if (CheckBoxes && IsHandleCreated) + cp.Style |= NativeMethods.TVS_CHECKBOXES; + + // Don't call IsMirrored from CreateParams. That will lead to some nasty problems, since + // IsMirrored ends up calling CreateParams - you dig! + if (RightToLeft == RightToLeft.Yes) { + if (RightToLeftLayout == true) { + //We want to turn on mirroring for TreeView explicitly. + cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL; + //Don't need these styles when mirroring is turned on. + cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR); + } + else { + cp.Style |= NativeMethods.TVS_RTLREADING; + } + } + + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(121, 97); + } + } + + /// + /// + /// This property is overridden and hidden from statement completion + /// on controls that are based on Win32 Native Controls. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool DoubleBuffered { + get { + return base.DoubleBuffered; + } + set { + base.DoubleBuffered = value; + } + } + + /// + /// + /// The current foreground color for this control, which is the + /// color the control uses to draw its text. + /// + public override Color ForeColor { + get { + if (ShouldSerializeForeColor()) { + return base.ForeColor; + } + else { + return SystemColors.WindowText; + } + } + + set { + base.ForeColor = value; + if (IsHandleCreated) + SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); + } + } + + /// + /// + /// Determines whether the selection highlight spans across the width of the TreeView. + /// This property will have no effect if ShowLines is true. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TreeViewFullRowSelectDescr) + ] + public bool FullRowSelect { + get { return treeViewState[TREEVIEWSTATE_fullRowSelect];} + set { + if (FullRowSelect != value) { + treeViewState[TREEVIEWSTATE_fullRowSelect] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// The HideSelection property specifies whether the selected node will + /// be highlighted even when the TreeView loses focus. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TreeViewHideSelectionDescr) + ] + public bool HideSelection { + get { + return treeViewState[TREEVIEWSTATE_hideSelection]; + } + + set { + if (HideSelection != value) { + treeViewState[TREEVIEWSTATE_hideSelection] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// The value of the HotTracking property. The HotTracking + /// property determines if nodes are highlighted as the mousepointer + /// passes over them. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TreeViewHotTrackingDescr) + ] + public bool HotTracking { + get { + return treeViewState[TREEVIEWSTATE_hotTracking]; + } + + set { + if (HotTracking != value) { + treeViewState[TREEVIEWSTATE_hotTracking] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// The default image index for nodes in the tree view. + /// + [ + DefaultValue(-1), + SRCategory(SR.CatBehavior), + Localizable(true), + RefreshProperties(RefreshProperties.Repaint), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SRDescription(SR.TreeViewImageIndexDescr), + RelatedImageList("ImageList") + ] + public int ImageIndex { + get { + if (imageList == null) { + return -1; + } + if (ImageIndexer.Index >= imageList.Images.Count) { + return Math.Max(0, imageList.Images.Count - 1); + } + return ImageIndexer.Index; + } + + set { + // If (none) is selected in the image index editor, we'll just adjust this to + // mean image index 0. This is because a treeview must always have an image index - + // even if no imagelist exists we want the image index to be 0. + // + if (value == -1) { + value = 0; + } + + if (value < 0) { + throw new ArgumentOutOfRangeException("ImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "ImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + + if (ImageIndexer.Index != value) { + ImageIndexer.Index = value; + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// The default image index for nodes in the tree view. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(""), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.TreeViewImageKeyDescr), + RelatedImageList("ImageList") + ] + public string ImageKey { + get { + return ImageIndexer.Key; + } + + set { + if (ImageIndexer.Key != value) { + ImageIndexer.Key = value; + if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { + ImageIndex = (ImageList != null) ? 0:-1; + } + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// Returns the image list control that is bound to the tree view. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.TreeViewImageListDescr), + RefreshProperties(RefreshProperties.Repaint) + ] + public ImageList ImageList { + get { + return imageList; + } + set { + if (value != imageList) { + + DetachImageListHandlers(); + + imageList = value; + + AttachImageListHandlers(); + + // Update TreeView's images + // + if (IsHandleCreated) { + SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, + value==null? IntPtr.Zero: value.Handle); + if (StateImageList != null && StateImageList.Images.Count > 0) { + SetStateImageList(internalStateImageList.Handle); + } + } + UpdateCheckedState(root, true); + } + } + } + + private void AttachImageListHandlers() { + if (imageList != null) { + //NOTE: any handlers added here should be removed in DetachImageListHandlers + imageList.RecreateHandle += new EventHandler(ImageListRecreateHandle); + imageList.Disposed += new EventHandler(DetachImageList); + imageList.ChangeHandle += new EventHandler(ImageListChangedHandle); + } + } + + private void DetachImageListHandlers() { + if (imageList != null) { + imageList.RecreateHandle -= new EventHandler(ImageListRecreateHandle); + imageList.Disposed -= new EventHandler(DetachImageList); + imageList.ChangeHandle -= new EventHandler(ImageListChangedHandle); + } + } + + private void AttachStateImageListHandlers() { + if (stateImageList != null) { + //NOTE: any handlers added here should be removed in DetachStateImageListHandlers + stateImageList.RecreateHandle += new EventHandler(StateImageListRecreateHandle); + stateImageList.Disposed += new EventHandler(DetachStateImageList); + stateImageList.ChangeHandle += new EventHandler(StateImageListChangedHandle); + } + } + + private void DetachStateImageListHandlers() { + if (stateImageList != null) { + stateImageList.RecreateHandle -= new EventHandler(StateImageListRecreateHandle); + stateImageList.Disposed -= new EventHandler(DetachStateImageList); + stateImageList.ChangeHandle -= new EventHandler(StateImageListChangedHandle); + } + } + + + /// + /// + /// Returns the state image list control that is bound to the tree view. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(null), + SRDescription(SR.TreeViewStateImageListDescr) + ] + public ImageList StateImageList { + get { + return stateImageList; + } + set { + if (value != stateImageList) { + + DetachStateImageListHandlers(); + stateImageList = value; + AttachStateImageListHandlers(); + + // Update TreeView's images + // + if (IsHandleCreated) + { + UpdateNativeStateImageList(); + + // We need to update the checks + // and stateimage value for each node. + UpdateCheckedState(root, true); + + if((value == null || stateImageList.Images.Count == 0) && CheckBoxes) { + // Requires Handle Recreate to force on the checkBoxes and states.. + RecreateHandle(); + } + else { + // The TreeView shows up the state imageList after sending this message even if the nodes dont have any stateImageIndex set. + // In order to avoid that we refresh nodes which would "reset" the images to none. + // This causes flicker but gives us the right behavior + RefreshNodes(); + } + } + + + } + } + } + + + /// + /// + /// The indentation level in pixels. + /// + [ + Localizable(true), + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeViewIndentDescr) + ] + public int Indent { + get { + if (indent != -1) { + return indent; + } + else if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); + } + return DefaultTreeViewIndent; + } + + set { + if (indent != value) { + if (value < 0) { + throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidLowBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (value > MaxIndent) { + throw new ArgumentOutOfRangeException("Indent", SR.GetString(SR.InvalidHighBoundArgumentEx, "Indent", (value).ToString(CultureInfo.CurrentCulture), (MaxIndent).ToString(CultureInfo.CurrentCulture))); + } + indent = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.TVM_SETINDENT, value, 0); + indent = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETINDENT, 0, 0)); + } + } + } + } + + /// + /// + /// The height of every item in the tree view, in pixels. + /// + [ + SRCategory(SR.CatAppearance), + SRDescription(SR.TreeViewItemHeightDescr) + ] + public int ItemHeight { + get { + if (itemHeight != -1) { + return itemHeight; + } + + if (IsHandleCreated) { + return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); + } + else { + if (CheckBoxes && (DrawMode == TreeViewDrawMode.OwnerDrawAll)) { + return Math.Max(16, FontHeight + 3); + } + return FontHeight + 3; + } + } + + set { + if (itemHeight != value) { + if (value < 1) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidLowBoundArgumentEx, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (1).ToString(CultureInfo.CurrentCulture))); + } + if (value >= Int16.MaxValue) { + throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidHighBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), Int16.MaxValue.ToString(CultureInfo.CurrentCulture))); + } + itemHeight = value; + if (IsHandleCreated) { + if (itemHeight % 2 != 0) { + setOddHeight = true; + try { + RecreateHandle(); + } + finally { + setOddHeight = false; + } + } + + SendMessage(NativeMethods.TVM_SETITEMHEIGHT, value, 0); + itemHeight = unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETITEMHEIGHT, 0, 0)); + } + } + } + } + + /// + /// + /// The LabelEdit property determines if the label text + /// of nodes in the tree view is editable. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TreeViewLabelEditDescr) + ] + public bool LabelEdit { + get { + return treeViewState[TREEVIEWSTATE_labelEdit]; + } + set { + if (LabelEdit != value) { + treeViewState[TREEVIEWSTATE_labelEdit] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + + /// + /// + /// This is the color of the lines that connect the nodes of the Treeview. + /// + [ + SRCategory(SR.CatBehavior), + SRDescription(SR.TreeViewLineColorDescr), + DefaultValue(typeof(Color), "Black") + ] + public Color LineColor { + get { + if (IsHandleCreated) { + int intColor = unchecked((int)(long)SendMessage(NativeMethods.TVM_GETLINECOLOR, 0, 0)); + return ColorTranslator.FromWin32(intColor); + } + return lineColor; + } + set { + if (lineColor != value) { + lineColor = value; + if (IsHandleCreated) { + SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); + + } + } + } + } + + /// + /// + /// The collection of nodes associated with this TreeView control + /// + [ + SRCategory(SR.CatBehavior), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content), + Localizable(true), + SRDescription(SR.TreeViewNodesDescr), + MergableProperty(false) + ] + public TreeNodeCollection Nodes { + get { + if (nodes == null) { + nodes = new TreeNodeCollection(root); + } + return nodes; + } + } + + /// + /// + /// + /// Indicates the drawing mode for the tree view. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(TreeViewDrawMode.Normal), + SRDescription(SR.TreeViewDrawModeDescr) + ] + public TreeViewDrawMode DrawMode { + get { + return drawMode; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)TreeViewDrawMode.Normal, (int)TreeViewDrawMode.OwnerDrawAll)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(TreeViewDrawMode)); + } + + if (drawMode != value) { + drawMode = value; + Invalidate(); + // We need to invalidate when the Control resizes when the we support custom draw. + if (DrawMode == TreeViewDrawMode.OwnerDrawAll) + { + SetStyle(ControlStyles.ResizeRedraw, true); + } + } + } + } + + /// + /// + /// The delimeter string used by TreeNode.getFullPath(). + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue("\\"), + SRDescription(SR.TreeViewPathSeparatorDescr) + ] + public string PathSeparator { + get { + return pathSeparator; + } + set { + pathSeparator = value; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never) + ] + public new event EventHandler PaddingChanged { + add { base.PaddingChanged += value; } + remove { base.PaddingChanged -= value; } + } + + /// + /// + /// This is used for international applications where the language + /// is written from RightToLeft. When this property is true, + // and the RightToLeft is true, mirroring will be turned on on the form, and + /// control placement and text will be from right to left. + /// + [ + SRCategory(SR.CatAppearance), + Localizable(true), + DefaultValue(false), + SRDescription(SR.ControlRightToLeftLayoutDescr) + ] + public virtual bool RightToLeftLayout { + get { + + return rightToLeftLayout; + } + + set { + if (value != rightToLeftLayout) { + rightToLeftLayout = value; + using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) { + OnRightToLeftLayoutChanged(EventArgs.Empty); + } + } + } + } + + + /// + /// + /// [To be supplied.] + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TreeViewScrollableDescr) + ] + public bool Scrollable { + get { + return treeViewState[TREEVIEWSTATE_scrollable]; + } + set { + if (Scrollable != value) { + treeViewState[TREEVIEWSTATE_scrollable] = value; + RecreateHandle(); + } + } + } + + /// + /// + /// The image index that a node will display when selected. + /// The index applies to the ImageList referred to by the imageList property, + /// + [ + DefaultValue(-1), + SRCategory(SR.CatBehavior), + TypeConverterAttribute(typeof(NoneExcludedImageIndexConverter)), + Localizable(true), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + SRDescription(SR.TreeViewSelectedImageIndexDescr), + RelatedImageList("ImageList") + ] + public int SelectedImageIndex { + get { + if (imageList == null) { + return -1; + } + if (SelectedImageIndexer.Index >= imageList.Images.Count) { + return Math.Max(0, imageList.Images.Count - 1); + } + return SelectedImageIndexer.Index; + } + set { + // If (none) is selected in the image index editor, we'll just adjust this to + // mean image index 0. This is because a treeview must always have an image index - + // even if no imagelist exists we want the image index to be 0. + // + if (value == -1) { + value = 0; + } + + if (value < 0) { + throw new ArgumentOutOfRangeException("SelectedImageIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectedImageIndex", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); + } + if (SelectedImageIndexer.Index != value) { + SelectedImageIndexer.Index = value; + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// The default image index for nodes in the tree view. + /// + [ + SRCategory(SR.CatBehavior), + Localizable(true), + TypeConverterAttribute(typeof(ImageKeyConverter)), + Editor("System.Windows.Forms.Design.ImageIndexEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), + DefaultValue(""), + RefreshProperties(RefreshProperties.Repaint), + SRDescription(SR.TreeViewSelectedImageKeyDescr), + RelatedImageList("ImageList") + ] + public string SelectedImageKey { + get { + return SelectedImageIndexer.Key; + } + + set { + if (SelectedImageIndexer.Key != value) { + SelectedImageIndexer.Key = value; + + if (String.IsNullOrEmpty(value) || value.Equals(SR.GetString(SR.toStringNone))) { + SelectedImageIndex = (ImageList != null) ? 0 : -1; + } + if (IsHandleCreated) { + RecreateHandle(); + } + } + } + } + + /// + /// + /// The currently selected tree node, or null if nothing is selected. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TreeViewSelectedNodeDescr) + ] + public TreeNode SelectedNode { + get { + if (IsHandleCreated) { + Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); + IntPtr hItem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_CARET, 0); + if (hItem == IntPtr.Zero) + return null; + return NodeFromHandle(hItem); + } + else if (selectedNode != null && selectedNode.TreeView == this) { + return selectedNode; + } + else { + return null; + } + } + set { + if (IsHandleCreated && (value == null || value.TreeView == this)) { + // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, + // selectedNode != null even though the handle is created. We will call set_SelectedNode + // to inform the handle that the selected node has been added to the TreeView. + Debug.Assert(selectedNode == null || selectedNode.TreeView != this, "handle is created, but we're still caching selectedNode"); + + IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); + SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_CARET, hnode); + selectedNode = null; + } + else { + selectedNode = value; + } + } + } + + /// + /// + /// The ShowLines property determines if lines are drawn between + /// nodes in the tree view. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TreeViewShowLinesDescr) + ] + public bool ShowLines { + get { + return treeViewState[TREEVIEWSTATE_showLines]; + } + set { + if (ShowLines != value) { + treeViewState[TREEVIEWSTATE_showLines] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// The ShowLines property determines whether or not the tooltips willbe displayed on the nodes + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TreeViewShowShowNodeToolTipsDescr) + ] + public bool ShowNodeToolTips { + get { + return treeViewState[TREEVIEWSTATE_showNodeToolTips]; + } + set { + if (ShowNodeToolTips != value) { + treeViewState[TREEVIEWSTATE_showNodeToolTips] = value; + if (ShowNodeToolTips) + RecreateHandle(); + } + } + } + + /// + /// + /// The ShowPlusMinus property determines if the "plus/minus" + /// expand button is shown next to tree nodes that have children. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TreeViewShowPlusMinusDescr) + ] + public bool ShowPlusMinus { + get { + return treeViewState[TREEVIEWSTATE_showPlusMinus]; + } + set { + if (ShowPlusMinus != value) { + treeViewState[TREEVIEWSTATE_showPlusMinus] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// Determines if lines are draw between nodes at the root of + /// the tree view. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.TreeViewShowRootLinesDescr) + ] + public bool ShowRootLines { + get { return treeViewState[TREEVIEWSTATE_showRootLines];} + set { + if (ShowRootLines != value) { + treeViewState[TREEVIEWSTATE_showRootLines] = value; + if (IsHandleCreated) { + UpdateStyles(); + } + } + } + } + + /// + /// + /// The Sorted property determines if nodes in the tree view are sorted. + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.TreeViewSortedDescr), + Browsable(false), EditorBrowsable(EditorBrowsableState.Never) + ] + public bool Sorted { + get { + return treeViewState[TREEVIEWSTATE_sorted]; + } + set { + if (Sorted != value) { + treeViewState[TREEVIEWSTATE_sorted] = value; + if (Sorted && TreeViewNodeSorter == null && Nodes.Count >= 1) { + RefreshNodes(); + } + } + } + } + + + /// + /// + /// The sorting comparer for this TreeView. + /// + [ + SRCategory(SR.CatBehavior), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TreeViewNodeSorterDescr) + ] + public IComparer TreeViewNodeSorter { + get { + return treeViewNodeSorter; + } + set { + if (treeViewNodeSorter != value) { + treeViewNodeSorter = value; + if (value != null) { + Sort(); + } + } + } + } + + /// + /// + /// [To be supplied.] + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// The first visible node in the TreeView. Initially + /// the first root node is at the top of the TreeView, but if the + /// contents have been scrolled another node may be at the top. + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TreeViewTopNodeDescr) + ] + public TreeNode TopNode { + get { + if (IsHandleCreated) { + IntPtr hitem = SendMessage(NativeMethods.TVM_GETNEXTITEM, NativeMethods.TVGN_FIRSTVISIBLE, 0); + return(hitem == IntPtr.Zero ? null : NodeFromHandle(hitem)); + } + return topNode; + } + set { + if (IsHandleCreated && (value == null || value.TreeView == this)) { + // This class invariant is not quite correct -- if the selected node does not belong to this Treeview, + // selectedNode != null even though the handle is created. We will call set_SelectedNode + // to inform the handle that the selected node has been added to the TreeView. + Debug.Assert(topNode == null || topNode.TreeView != this, "handle is created, but we're still caching selectedNode"); + + IntPtr hnode = (value == null ? IntPtr.Zero : value.Handle); + SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_FIRSTVISIBLE, hnode); + topNode = null; + } + else { + topNode = value; + } + + } + } + + /// + /// + /// The count of fully visible nodes in the tree view. This number + /// may be greater than the number of nodes in the control. + /// The control calculates this value by dividing the height of the + /// client window by the height of an item + /// + [ + SRCategory(SR.CatAppearance), + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.TreeViewVisibleCountDescr) + ] + public int VisibleCount { + get { + if (IsHandleCreated) + return unchecked( (int) (long)SendMessage(NativeMethods.TVM_GETVISIBLECOUNT, 0, 0)); + + return 0; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeEditDescr)] + public event NodeLabelEditEventHandler BeforeLabelEdit { + add { + onBeforeLabelEdit += value; + } + remove { + onBeforeLabelEdit -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterEditDescr)] + public event NodeLabelEditEventHandler AfterLabelEdit { + add { + onAfterLabelEdit += value; + } + remove { + onAfterLabelEdit -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCheckDescr)] + public event TreeViewCancelEventHandler BeforeCheck { + add { + onBeforeCheck += value; + } + remove { + onBeforeCheck -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCheckDescr)] + public event TreeViewEventHandler AfterCheck { + add { + onAfterCheck += value; + } + remove { + onAfterCheck -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeCollapseDescr)] + public event TreeViewCancelEventHandler BeforeCollapse { + add { + onBeforeCollapse += value; + } + remove { + onBeforeCollapse -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterCollapseDescr)] + public event TreeViewEventHandler AfterCollapse { + add { + onAfterCollapse += value; + } + remove { + onAfterCollapse -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeExpandDescr)] + public event TreeViewCancelEventHandler BeforeExpand { + add { + onBeforeExpand += value; + } + remove { + onBeforeExpand -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterExpandDescr)] + public event TreeViewEventHandler AfterExpand { + add { + onAfterExpand += value; + } + remove { + onAfterExpand -= value; + } + } + + /// + /// + /// Fires when a TreeView node needs to be drawn. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewDrawNodeEventDescr)] + public event DrawTreeNodeEventHandler DrawNode { + add { + onDrawNode += value; + } + remove { + onDrawNode -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.ListViewItemDragDescr)] + public event ItemDragEventHandler ItemDrag { + add { + onItemDrag += value; + } + remove { + onItemDrag -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatAction), SRDescription(SR.TreeViewNodeMouseHoverDescr)] + public event TreeNodeMouseHoverEventHandler NodeMouseHover { + add { + onNodeMouseHover += value; + } + remove { + onNodeMouseHover -= value; + } + } + + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewBeforeSelectDescr)] + public event TreeViewCancelEventHandler BeforeSelect { + add { + onBeforeSelect += value; + } + remove { + onBeforeSelect -= value; + } + } + + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewAfterSelectDescr)] + public event TreeViewEventHandler AfterSelect { + add { + onAfterSelect += value; + } + remove { + onAfterSelect -= value; + } + } + + /// + /// + /// TreeView Onpaint. + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { + base.Paint += value; + } + remove { + base.Paint -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseClickDescr)] + public event TreeNodeMouseClickEventHandler NodeMouseClick { + add { + onNodeMouseClick += value; + } + remove { + onNodeMouseClick -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.TreeViewNodeMouseDoubleClickDescr)] + public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick { + add { + onNodeMouseDoubleClick += value; + } + remove { + onNodeMouseDoubleClick -= value; + } + } + + /// + /// + /// [To be supplied.] + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)] + public event EventHandler RightToLeftLayoutChanged { + add { + onRightToLeftLayoutChanged += value; + } + remove { + onRightToLeftLayoutChanged -= value; + } + } + + + /// + /// + /// Disables redrawing of the tree view. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// tree view is deferred until the call to endUpdate(). + /// + public void BeginUpdate() { + BeginUpdateInternal(); + } + + /// + /// + /// Collapses all nodes at the root level. + /// + public void CollapseAll() { + root.Collapse(); + } + + /// + /// + /// + /// + protected override void CreateHandle() { + if (!RecreatingHandle) { + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + try { + NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); + icc.dwICC = NativeMethods.ICC_TREEVIEW_CLASSES; + SafeNativeMethods.InitCommonControlsEx(icc); + } finally { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } + } + base.CreateHandle(); + } + + /// + /// + /// Resets the imageList to null. We wire this method up to the imageList's + /// Dispose event, so that we don't hang onto an imageList that's gone away. + /// + /// + private void DetachImageList(object sender, EventArgs e) { + ImageList = null; + } + + /// + /// + /// Resets the stateimageList to null. We wire this method up to the stateimageList's + /// Dispose event, so that we don't hang onto an stateimageList that's gone away. + /// + /// + private void DetachStateImageList(object sender, EventArgs e) { + internalStateImageList = null; + StateImageList = null; + + } + + /// + /// + /// + /// + protected override void Dispose(bool disposing) { + if (disposing) { + + foreach (TreeNode node in Nodes) { + node.ContextMenu = null; + } + + // + lock(this) { + DetachImageListHandlers(); + imageList = null; + DetachStateImageListHandlers(); + stateImageList = null; + } + + } + + base.Dispose(disposing); + } + + /// + /// + /// Reenables redrawing of the tree view. A call to beginUpdate() must be + /// balanced by a following call to endUpdate(). Following a call to + /// beginUpdate(), any redrawing caused by operations performed on the + /// combo box is deferred until the call to endUpdate(). + /// + public void EndUpdate() { + EndUpdateInternal(); + } + + /// + /// + /// Expands all nodes at the root level. + /// + public void ExpandAll() { + root.ExpandAll(); + } + + /// + /// + /// Forces the TreeView to recalculate all its nodes widths so that it updates the + /// scrollbars as appropriate. + /// + /// + internal void ForceScrollbarUpdate(bool delayed) { + + // ForceScrollbarUpdate call WM_SETREDRAW( FALSE ) followed by WM_SETREDRAW( TRUE ) + // So if TreeView.BeginUpdate is called + // ForceScrollbarUpdate effectively causes tree view to ignore BeginUpdate and cause control to update on every change. + // So gaurd against this scenario by using the new internal method on Control. + if (!IsUpdating()) + { + if (IsHandleCreated) { + SendMessage(NativeMethods.WM_SETREDRAW, 0, 0); + if (delayed) + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero); + else + SendMessage(NativeMethods.WM_SETREDRAW, 1, 0); + } + } + } + + /// + /// + /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. + /// + /// + internal void SetToolTip(ToolTip toolTip, string toolTipText) { + if (toolTip != null) { + UnsafeNativeMethods.SendMessage(new HandleRef(toolTip, toolTip.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TVM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0); + controlToolTipText = toolTipText; + } + } + + + /// + /// + /// Gives the information about which part of the treeNode is at the given point. + /// + public TreeViewHitTestInfo HitTest(Point pt) { + return HitTest(pt.X, pt.Y); + } + + /// + /// + /// Gives the information about which part of the treeNode is at the given x, y. + /// + public TreeViewHitTestInfo HitTest(int x, int y) { + NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); + tvhi.pt_x = x; + tvhi.pt_y = y; + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); + TreeNode node = (hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); + TreeViewHitTestLocations loc = (TreeViewHitTestLocations)tvhi.flags; + return (new TreeViewHitTestInfo(node, loc)); + } + + /// + /// + /// Defined so that a tree node can use it + /// + /// + /// + + internal bool TreeViewBeforeCheck(TreeNode node, TreeViewAction actionTaken) { + TreeViewCancelEventArgs tvce = new TreeViewCancelEventArgs(node, false, actionTaken); + OnBeforeCheck(tvce); + return (tvce.Cancel); + } + + internal void TreeViewAfterCheck(TreeNode node, TreeViewAction actionTaken) { + OnAfterCheck(new TreeViewEventArgs(node, actionTaken)); + } + + + /// + /// + /// Returns count of nodes at root, optionally including all subtrees. + /// + public int GetNodeCount(bool includeSubTrees) { + return root.GetNodeCount(includeSubTrees); + } + + /// + /// + /// Returns the TreeNode at the given location in tree view coordinates. + /// + public TreeNode GetNodeAt(Point pt) { + return GetNodeAt(pt.X, pt.Y); + } + + /// + /// + /// Returns the TreeNode at the given location in tree view coordinates. + /// + public TreeNode GetNodeAt(int x, int y) { + NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); + + tvhi.pt_x = x; + tvhi.pt_y = y; + + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); + + return(hnode == IntPtr.Zero ? null : NodeFromHandle(hnode)); + } + + private void ImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + IntPtr handle = (ImageList == null) ? IntPtr.Zero : ImageList.Handle; + SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, handle); + } + } + + private void UpdateImagesRecursive ( TreeNode node ) + { + node.UpdateImage(); + // VSWhidbey 208200: Iterate only through the Nodes collection rather than the + // array since an item might have been removed from the collection, and + // correspondingly "removed" from the array, but still exist in the array + // since the array isn't actually re-dimensioned down to a smaller size. + foreach (TreeNode child in node.Nodes) { + UpdateImagesRecursive(child); + } + } + + private void ImageListChangedHandle(object sender, EventArgs e) { + if ((null != sender) && (sender == imageList) && IsHandleCreated) { + BeginUpdate(); + foreach (TreeNode node in Nodes) { + UpdateImagesRecursive(node); + } + EndUpdate(); + } + } + + private void StateImageListRecreateHandle(object sender, EventArgs e) { + if (IsHandleCreated) { + IntPtr handle = IntPtr.Zero; + if (internalStateImageList != null) { + handle = internalStateImageList.Handle; + } + SetStateImageList(handle); + } + } + + private void StateImageListChangedHandle(object sender, EventArgs e) { + if ((null != sender) && (sender == stateImageList) && IsHandleCreated) { + // Since the native treeview requires the state imagelist to be 1-indexed we need to + // re add the images if the original collection had changed. + if (stateImageList != null && stateImageList.Images.Count > 0) { + Image[] images = new Image[stateImageList.Images.Count + 1]; + images[0] = stateImageList.Images[0]; + for (int i = 1; i <= stateImageList.Images.Count ; i++) { + images[i] = stateImageList.Images[i -1]; + } + + if (internalStateImageList != null) + { + internalStateImageList.Images.Clear(); + internalStateImageList.Images.AddRange(images); + } + else + { + internalStateImageList = new ImageList(); + internalStateImageList.Images.AddRange(images); + } + + Debug.Assert(internalStateImageList != null, "Why are changing images when the Imagelist is null?"); + if (internalStateImageList != null) + { + if (ScaledStateImageSize != null) + { + internalStateImageList.ImageSize = (Size)ScaledStateImageSize; + } + + SetStateImageList(internalStateImageList.Handle); + } + } + else //stateImageList == null || stateImageList.Images.Count = 0; + { + UpdateCheckedState(root, true); + } + } + } + + /// + /// + /// + /// + /// Overridden to handle RETURN key. + /// + /// + protected override bool IsInputKey(Keys keyData) { + // If in edit mode, treat Return as an input key, so the form doesn't grab it + // and treat it as clicking the Form.AcceptButton. Similarly for Escape + // and Form.CancelButton. + if (editNode != null && (keyData & Keys.Alt) == 0) { + switch (keyData & Keys.KeyCode) { + case Keys.Return: + case Keys.Escape: + case Keys.PageUp: + case Keys.PageDown: + case Keys.Home: + case Keys.End: + return true; + } + } + return base.IsInputKey(keyData); + } + + /// + /// + /// Note this can be null - particularly if any windows messages get generated during + /// the insertion of a tree node (TVM_INSERTITEM) + /// + /// + internal TreeNode NodeFromHandle(IntPtr handle) { + TreeNode node = (TreeNode)nodeTable[handle]; + + return node; + } + + /// + /// + /// Fires the DrawNode event. + /// + protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) { + if (onDrawNode != null) onDrawNode(this, e); + } + + /// + /// + /// + /// + protected override void OnHandleCreated(EventArgs e) { + + TreeNode savedSelectedNode = this.selectedNode; + this.selectedNode = null; + + base.OnHandleCreated(e); + + //ComCtl 5 has some bug fixes that, to enable, require us to send the control + //a CCM_SETVERSION with 5 as the version. The one we want in particular is + //the fix for the node clipping issue when a font is set by means of CDRF_NEWFONT. + //The fix is not perfect, but the behavior is better than before. + int version = unchecked((int)(long)SendMessage(NativeMethods.CCM_GETVERSION, 0, 0)); + if (version < 5) { + SendMessage(NativeMethods.CCM_SETVERSION, 5, 0); + } + + // Workaround for problem in TreeView where it doesn't recognize the TVS_CHECKBOXES + // style if it is set before the window is created. To get around the problem, + // we set it here after the window is created, and we make sure we don't set it + // in getCreateParams so that this will actually change the value of the bit. + // This seems to make the Treeview happy. + if (CheckBoxes) { + int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + style |= NativeMethods.TVS_CHECKBOXES; + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); + } + + if (ShowNodeToolTips && !DesignMode) { + int style = unchecked((int)(UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); + style |= NativeMethods.TVS_INFOTIP; + UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); + } + + Color c; + c = BackColor; + if (c != SystemColors.Window) + SendMessage(NativeMethods.TVM_SETBKCOLOR, 0, ColorTranslator.ToWin32(c)); + c = ForeColor; + + if (c != SystemColors.WindowText) + SendMessage(NativeMethods.TVM_SETTEXTCOLOR, 0, ColorTranslator.ToWin32(c)); + + /// put the linecolor into the native control only if Set ... + if (lineColor != Color.Empty) { + SendMessage(NativeMethods.TVM_SETLINECOLOR, 0, ColorTranslator.ToWin32(lineColor)); + } + + if (imageList != null) + SendMessage(NativeMethods.TVM_SETIMAGELIST, 0, imageList.Handle); + + if (stateImageList != null) + { + UpdateNativeStateImageList(); + } + + if (indent != -1) { + SendMessage(NativeMethods.TVM_SETINDENT, indent, 0); + } + + if (itemHeight != -1) { + SendMessage(NativeMethods.TVM_SETITEMHEIGHT, ItemHeight, 0); + } + + // Fix for bug 331158. Essentially we are setting the width to be infinite so that the + // TreeView never thinks it needs a scrollbar when the first node is created + // during the first handle creation. + // + // This is set back to the oldSize after the Realize method. + int oldSize = 0; + try + { + + treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = true; + oldSize = this.Width; + int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOMOVE; + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, Int32.MaxValue, this.Height, flags); + + root.Realize(false); + + if (oldSize != 0) + { + SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.NullHandleRef, this.Left, this.Top, oldSize, this.Height, flags); + } + } + finally + { + treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs] = false; + } + + SelectedNode = savedSelectedNode; + } + + // Replace the native control's ImageList with our current stateImageList + // set the value of internalStateImageList to the new list + private void UpdateNativeStateImageList() + { + if (stateImageList != null && stateImageList.Images.Count > 0) + { + ImageList newImageList = new ImageList(); + if (ScaledStateImageSize != null) + { + newImageList.ImageSize = (Size)ScaledStateImageSize; + } + + Image[] images = new Image[stateImageList.Images.Count + 1]; + images[0] = stateImageList.Images[0]; + for (int i = 1; i <= stateImageList.Images.Count; i++) + { + images[i] = stateImageList.Images[i - 1]; + } + newImageList.Images.AddRange(images); + SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, newImageList.Handle); + + if (internalStateImageList != null) + { + internalStateImageList.Dispose(); + } + internalStateImageList = newImageList; + } + } + + private void SetStateImageList(IntPtr handle) + { + // In certain cases (TREEVIEWSTATE_checkBoxes) e.g., the Native TreeView leaks the imagelist + // even if set by us. To prevent any leaks, we always destroy what was there after setting a new list. + IntPtr handleOld = SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, handle); + if ((handleOld != IntPtr.Zero) && (handleOld != handle)) + { + SafeNativeMethods.ImageList_Destroy_Native(new HandleRef(this, handleOld)); + } + } + + // Destroying the tree-view control does not destroy the native state image list. See DevDiv #303906 + // We must destroy it explicitly. + private void DestroyNativeStateImageList(bool reset) + { + IntPtr handle = SendMessage(NativeMethods.TVM_GETIMAGELIST, NativeMethods.TVSIL_STATE, IntPtr.Zero); + if (handle != IntPtr.Zero) + { + SafeNativeMethods.ImageList_Destroy_Native(new HandleRef(this, handle)); + if (reset) + { + SendMessage(NativeMethods.TVM_SETIMAGELIST, NativeMethods.TVSIL_STATE, IntPtr.Zero); + } + } + } + + /// + /// + /// + /// + protected override void OnHandleDestroyed(EventArgs e) { + selectedNode = SelectedNode; + + // Unfortunately, to avoid the native tree view leaking it's State Image List, we need to + // destroy it ourselves here. See DevDiv Bug #307248. + DestroyNativeStateImageList(true); + + // for the case when we are NOT being disposed, we'll be recreating the internal state imagelist + // in OnHandleCreate, so it is ok to completely Dispose here + if (internalStateImageList != null) + { + internalStateImageList.Dispose(); + internalStateImageList = null; + } + + base.OnHandleDestroyed(e); + } + + + /// + /// + /// We keep track of if we've hovered already so we don't fire multiple hover events + /// + /// + protected override void OnMouseLeave(EventArgs e) { + hoveredAlready = false; + base.OnMouseLeave(e); + } + + /// + /// + /// In order for the MouseHover event to fire for each item in a TreeView, + /// the node the mouse is hovering over is found. Each time a new node is hovered + /// over a new event is raised. + /// + protected override void OnMouseHover(EventArgs e) { + + /// Hover events need to be caught for each node + /// within the TreeView so the appropriate + /// NodeHovered event can be raised. + + NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + tvhip.pt_x = pos.X; + tvhip.pt_y = pos.Y; + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); + + if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { + TreeNode tn = NodeFromHandle(hnode); + if (tn != prevHoveredNode && tn != null) { + OnNodeMouseHover( new TreeNodeMouseHoverEventArgs(tn)); + prevHoveredNode = tn; + } + } + + if (!hoveredAlready) { + base.OnMouseHover(e); + hoveredAlready = true; + } + + ResetMouseEventArgs(); + + } + + /// + /// + /// Fires the beforeLabelEdit event. + /// + protected virtual void OnBeforeLabelEdit(NodeLabelEditEventArgs e) { + Contract.Requires(e != null); + if (onBeforeLabelEdit != null) onBeforeLabelEdit(this, e); + } + + /// + /// + /// Fires the afterLabelEdit event. + /// + protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) { + Contract.Requires(e != null); + if (onAfterLabelEdit != null) onAfterLabelEdit(this, e); + } + + /// + /// + /// Fires the beforeCheck event. + /// + protected virtual void OnBeforeCheck(TreeViewCancelEventArgs e) { + Contract.Requires(e != null); + if (onBeforeCheck != null) onBeforeCheck(this, e); + } + + /// + /// + /// Fires the afterCheck event. + /// + protected virtual void OnAfterCheck(TreeViewEventArgs e) { + Contract.Requires(e != null); + if (onAfterCheck != null) onAfterCheck(this, e); + } + + /// + /// + /// Fires the beforeCollapse event. + /// + protected internal virtual void OnBeforeCollapse(TreeViewCancelEventArgs e) { + Contract.Requires(e != null); + if (onBeforeCollapse != null) onBeforeCollapse(this, e); + } + + /// + /// + /// Fires the afterCollapse event. + /// + protected internal virtual void OnAfterCollapse(TreeViewEventArgs e) { + Contract.Requires(e != null); + if (onAfterCollapse != null) onAfterCollapse(this, e); + } + + /// + /// + /// Fires the beforeExpand event. + /// + protected virtual void OnBeforeExpand(TreeViewCancelEventArgs e) { + Contract.Requires(e != null); + if (onBeforeExpand != null) onBeforeExpand(this, e); + } + + /// + /// + /// Fires the afterExpand event. + /// + protected virtual void OnAfterExpand(TreeViewEventArgs e) { + Contract.Requires(e != null); + if (onAfterExpand != null) onAfterExpand(this, e); + } + + /// + /// + /// Fires the ItemDrag event. + /// + protected virtual void OnItemDrag(ItemDragEventArgs e) { + Contract.Requires(e != null); + if (onItemDrag != null) onItemDrag(this, e); + } + + /// + /// + /// Fires the NodeMouseHover event. + /// + protected virtual void OnNodeMouseHover(TreeNodeMouseHoverEventArgs e) { + Contract.Requires(e != null); + if (onNodeMouseHover != null) onNodeMouseHover(this, e); + } + + /// + /// + /// Fires the beforeSelect event. + /// + protected virtual void OnBeforeSelect(TreeViewCancelEventArgs e) { + Contract.Requires(e != null); + if (onBeforeSelect != null) onBeforeSelect(this, e); + } + + /// + /// + /// Fires the afterSelect event. + /// + protected virtual void OnAfterSelect(TreeViewEventArgs e) { + Contract.Requires(e != null); + if (onAfterSelect != null) onAfterSelect(this, e); + } + + /// + /// + /// Fires the onNodeMouseClick event. + /// + protected virtual void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) { + Contract.Requires(e != null); + if (onNodeMouseClick != null) onNodeMouseClick(this, e); + } + + /// + /// + /// Fires the onNodeMouseDoubleClick event. + /// + protected virtual void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) { + Contract.Requires(e != null); + if (onNodeMouseDoubleClick != null) onNodeMouseDoubleClick(this, e); + } + + /// + /// + /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks + /// + /// + protected override void OnKeyDown(KeyEventArgs e) { + base.OnKeyDown(e); + if (e.Handled) return; + // if it's a space, send the check notifications and toggle the checkbox if we're not + // cancelled. + if (CheckBoxes && (e.KeyData & Keys.KeyCode) == Keys.Space) { + TreeNode node = this.SelectedNode; + if (node != null) { + bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByKeyboard); + if (!eventReturn) { + node.CheckedInternal = !node.CheckedInternal; + TreeViewAfterCheck(node, TreeViewAction.ByKeyboard); + } + e.Handled = true; + return; + } + } + } + + /// + /// + /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks + /// + /// + protected override void OnKeyUp(KeyEventArgs e) { + base.OnKeyUp(e); + if (e.Handled) return; + // eat the space key + if ((e.KeyData & Keys.KeyCode) == Keys.Space) { + e.Handled = true; + return; + } + } + + /// + /// + /// Handles the OnBeforeCheck / OnAfterCheck for keyboard clicks + /// + /// + protected override void OnKeyPress(KeyPressEventArgs e) { + base.OnKeyPress(e); + if (e.Handled) return; + // eat the space key + if (e.KeyChar == ' ') e.Handled = true; + } + + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnRightToLeftLayoutChanged(EventArgs e) { + Contract.Requires(e != null); + if (GetAnyDisposingInHierarchy()) { + return; + } + + if (RightToLeft == RightToLeft.Yes) { + RecreateHandle(); + } + + if (onRightToLeftLayoutChanged != null) { + onRightToLeftLayoutChanged(this, e); + } + } + + + + // Refresh the nodes by clearing the tree and adding the nodes back again + // + private void RefreshNodes() { + TreeNode[] nodes = new TreeNode[Nodes.Count]; + Nodes.CopyTo(nodes, 0); + + Nodes.Clear(); + Nodes.AddRange(nodes); + } + + /// + /// + /// This resets the indentation to the system default. + /// + private void ResetIndent() { + indent = -1; + // is this overkill? + RecreateHandle(); + } + + /// + /// + /// This resets the item height to the system default. + /// + private void ResetItemHeight() { + itemHeight = -1; + RecreateHandle(); + } + + /// + /// + /// Retrieves true if the indent should be persisted in code gen. + /// + private bool ShouldSerializeIndent() { + return(indent != -1); + } + + /// + /// + /// Retrieves true if the itemHeight should be persisted in code gen. + /// + private bool ShouldSerializeItemHeight() { + return(itemHeight != -1); + } + + private bool ShouldSerializeSelectedImageIndex() { + if (imageList != null) { + return (SelectedImageIndex != 0); + } + return (SelectedImageIndex != -1); + } + + + private bool ShouldSerializeImageIndex() { + if (imageList != null) { + return (ImageIndex != 0); + } + return (ImageIndex != -1); + } + + + /// + /// + /// Updated the sorted order + /// + public void Sort() { + Sorted = true; + RefreshNodes(); + } + + + /// + /// + /// Returns a string representation for this control. + /// + /// + public override string ToString() { + + string s = base.ToString(); + if (Nodes != null) { + s += ", Nodes.Count: " + Nodes.Count.ToString(CultureInfo.CurrentCulture); + if (Nodes.Count > 0) + s += ", Nodes[0]: " + Nodes[0].ToString(); + } + return s; + } + + private unsafe void TvnBeginDrag(MouseButtons buttons, NativeMethods.NMTREEVIEW* nmtv) { + NativeMethods.TV_ITEM item = nmtv->itemNew; + + // Check for invalid node handle + if (item.hItem == IntPtr.Zero) { + return; + } + + TreeNode node = NodeFromHandle(item.hItem); + + OnItemDrag(new ItemDragEventArgs(buttons, node)); + } + + private unsafe IntPtr TvnExpanding(NativeMethods.NMTREEVIEW* nmtv) { + NativeMethods.TV_ITEM item = nmtv->itemNew; + + // Check for invalid node handle + if (item.hItem == IntPtr.Zero) { + return IntPtr.Zero; + } + + TreeViewCancelEventArgs e = null; + if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { + e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Expand); + OnBeforeExpand(e); + } + else { + e = new TreeViewCancelEventArgs(NodeFromHandle(item.hItem), false, TreeViewAction.Collapse); + OnBeforeCollapse(e); + } + return (IntPtr)(e.Cancel? 1: 0); + } + + private unsafe void TvnExpanded(NativeMethods.NMTREEVIEW* nmtv) { + NativeMethods.TV_ITEM item = nmtv->itemNew; + + // Check for invalid node handle + if (item.hItem == IntPtr.Zero) { + return; + } + + TreeViewEventArgs e; + TreeNode node = NodeFromHandle(item.hItem); + + // Note that IsExpanded is invalid for the moment, so we use item item.state to branch. + if ((item.state & NativeMethods.TVIS_EXPANDED) == 0) { + e = new TreeViewEventArgs(node, TreeViewAction.Collapse); + OnAfterCollapse(e); + } + else { + e = new TreeViewEventArgs(node, TreeViewAction.Expand); + OnAfterExpand(e); + } + } + + private unsafe IntPtr TvnSelecting(NativeMethods.NMTREEVIEW* nmtv) { + if (treeViewState[ TREEVIEWSTATE_ignoreSelects]) + { + return (IntPtr)1; + } + // Check for invalid node handle + if (nmtv->itemNew.hItem == IntPtr.Zero) { + return IntPtr.Zero; + } + + TreeNode node = NodeFromHandle(nmtv->itemNew.hItem); + + TreeViewAction action = TreeViewAction.Unknown; + switch(nmtv->action) { + case NativeMethods.TVC_BYKEYBOARD: + action = TreeViewAction.ByKeyboard; + break; + case NativeMethods.TVC_BYMOUSE: + action = TreeViewAction.ByMouse; + break; + } + + TreeViewCancelEventArgs e = new TreeViewCancelEventArgs(node, false, action); + OnBeforeSelect(e); + + return (IntPtr)(e.Cancel? 1: 0); + } + + private unsafe void TvnSelected(NativeMethods.NMTREEVIEW* nmtv) { + if (nodesCollectionClear) //if called thru the Clear( ) of treeNodeCollection then just return... + { + return; + } + if (nmtv->itemNew.hItem != IntPtr.Zero) { + TreeViewAction action = TreeViewAction.Unknown; + switch(nmtv->action) { + case NativeMethods.TVC_BYKEYBOARD: + action = TreeViewAction.ByKeyboard; + break; + case NativeMethods.TVC_BYMOUSE: + action = TreeViewAction.ByMouse; + break; + } + OnAfterSelect(new TreeViewEventArgs(NodeFromHandle(nmtv->itemNew.hItem), action)); + } + + // TreeView doesn't properly revert back to the unselected image + // if the unselected image is blank. + // + NativeMethods.RECT rc = new NativeMethods.RECT(); + *((IntPtr *) &rc.left) = nmtv->itemOld.hItem; + if (nmtv->itemOld.hItem != IntPtr.Zero) { + if (unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_GETITEMRECT, 1, ref rc)) != 0) + SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), ref rc, true); + } + } + + private IntPtr TvnBeginLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { + + // Check for invalid node handle + if (nmtvdi.item.hItem == IntPtr.Zero) { + return IntPtr.Zero; + } + + TreeNode editingNode = NodeFromHandle(nmtvdi.item.hItem); + NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(editingNode); + OnBeforeLabelEdit(e); + if (!e.CancelEdit) + editNode = editingNode; + return (IntPtr)(e.CancelEdit ? 1 : 0); + } + + private IntPtr TvnEndLabelEdit(NativeMethods.NMTVDISPINFO nmtvdi) { + editNode = null; + + // Check for invalid node handle + if (nmtvdi.item.hItem == IntPtr.Zero) { + return (IntPtr)1; + } + + TreeNode node = NodeFromHandle(nmtvdi.item.hItem); + string newText = (nmtvdi.item.pszText == IntPtr.Zero ? null : Marshal.PtrToStringAuto(nmtvdi.item.pszText)); + NodeLabelEditEventArgs e = new NodeLabelEditEventArgs(node, newText); + OnAfterLabelEdit(e); + if (newText != null && !e.CancelEdit && node != null) { + node.text = newText; + if (Scrollable) + ForceScrollbarUpdate(true); + } + return (IntPtr)(e.CancelEdit ? 0 : 1); + } + + internal override void UpdateStylesCore() { + base.UpdateStylesCore(); + if (IsHandleCreated && CheckBoxes) { + if (StateImageList != null) { + // Setting the TVS_CHECKBOXES window style also causes the TreeView to display the default checkbox + // images rather than the user specified StateImageList. We send a TVM_SETIMAGELIST to restore the + // user's images. + if (internalStateImageList != null) { + SetStateImageList(internalStateImageList.Handle); + } + + } + } + } + + // Setting the NativeMethods.TVS_CHECKBOXES style clears the checked state + private void UpdateCheckedState(TreeNode node, bool update) { + // This looks funny, but CheckedInternal returns the cached isChecked value and the internal + // setter will blindly issue TVM_SETITEM so this gets us back in sync. + if (update) + { + node.CheckedInternal = node.CheckedInternal; + for (int i = node.Nodes.Count - 1; i >= 0; i--) { + UpdateCheckedState(node.Nodes[i], update); + } + } + else + { + node.CheckedInternal = false; + for (int i = node.Nodes.Count - 1; i >= 0; i--) { + UpdateCheckedState(node.Nodes[i], update); + } + } + } + + private void WmMouseDown(ref Message m, MouseButtons button, int clicks) { + // Required to put the TreeView in sane-state for painting proper highlighting of selectedNodes. + // If the user shows the ContextMenu bu overiding the WndProc( ), then the treeview + // goes into the weird state where the high-light gets locked to the node on which the ContextMenu was shown. + // So we need to get the native TREEIVEW out of this weird state. + // Refer to VSWhidbey : 249191. + SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); + + // Windows TreeView pushes its own message loop in WM_xBUTTONDOWN, so fire the + // event before calling defWndProc or else it won't get fired until the button + // comes back up. + OnMouseDown(new MouseEventArgs(button, clicks, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + + //If Validation is cancelled dont fire any events through the Windows TreeView's message loop... + if (!ValidationCancelled) { + DefWndProc(ref m); + } + + } + + + + /// + /// + /// Performs custom draw handling + /// + /// + private void CustomDraw(ref Message m) { + NativeMethods.NMTVCUSTOMDRAW nmcd = (NativeMethods.NMTVCUSTOMDRAW)m.GetLParam(typeof(NativeMethods.NMTVCUSTOMDRAW)); + + // Find out which stage we're drawing + switch (nmcd.nmcd.dwDrawStage) { + // Do we want OwnerDraw for this paint cycle? + case NativeMethods.CDDS_PREPAINT: + m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYITEMDRAW; // yes, we do... + return; + // We've got opt-in on owner draw for items - so handle each one. + case NativeMethods.CDDS_ITEMPREPAINT: + // get the node + Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPREPAINT"); + TreeNode node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); + + if (node == null) { + // this can happen if we are presently inserting the node - it hasn't yet + // been added to the handle table + + m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); + return; + } + + + int state = nmcd.nmcd.uItemState; + + + // The commctrl TreeView allows you to draw the whole row of a node + // or nothing at all. The way we provide OwnerDrawText is by asking it + // to draw everything but the text - to do this, we set text color same + // as background color. + if (drawMode == TreeViewDrawMode.OwnerDrawText) + { + nmcd.clrText = nmcd.clrTextBk; + Marshal.StructureToPtr(nmcd, m.LParam, false); + m.Result = (IntPtr) (NativeMethods.CDRF_NEWFONT | NativeMethods.CDRF_NOTIFYPOSTPAINT); + return; + } + else if (drawMode == TreeViewDrawMode.OwnerDrawAll) + { + Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); + + DrawTreeNodeEventArgs e; + + try { + Rectangle bounds = node.RowBounds; + + NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); + si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); + si.fMask = NativeMethods.SIF_POS; + if (UnsafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ,si) != false) { + // VsW : 432718 + // need to get the correct bounds if horizontal scroll bar is shown. + // In this case the bounds.X needs to be negative and width needs to be updated to the increased width (scrolled region). + int value = si.nPos; + if (value > 0) + { + bounds.X -= value; + bounds.Width += value; + } + } + e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (state)); + OnDrawNode(e); + } + finally { + g.Dispose(); + } + + if (!e.DrawDefault) { + m.Result = (IntPtr)(NativeMethods.CDRF_SKIPDEFAULT); + return; + } + } + + //TreeViewDrawMode.Normal case +#if DEBUGGING + // Diagnostic output + Debug.WriteLine("Itemstate: "+state); + Debug.WriteLine("Itemstate: "+ + "\nDISABLED" + (((state & NativeMethods.CDIS_DISABLED) != 0) ? "TRUE" : "FALSE") + + "\nHOT" + (((state & NativeMethods.CDIS_HOT) != 0) ? "TRUE" : "FALSE") + + "\nGRAYED" + (((state & NativeMethods.CDIS_GRAYED) != 0) ? "TRUE" : "FALSE") + + "\nSELECTED" + (((state & NativeMethods.CDIS_SELECTED) != 0) ? "TRUE" : "FALSE") + + "\nFOCUS" + (((state & NativeMethods.CDIS_FOCUS) != 0) ? "TRUE" : "FALSE") + + "\nDEFAULT" + (((state & NativeMethods.CDIS_DEFAULT) != 0) ? "TRUE" : "FALSE") + + "\nMARKED" + (((state & NativeMethods.CDIS_MARKED) != 0) ? "TRUE" : "FALSE") + + "\nINDETERMINATE" + (((state & NativeMethods.CDIS_INDETERMINATE) != 0) ? "TRUE" : "FALSE")); +#endif + + OwnerDrawPropertyBag renderinfo = GetItemRenderStyles(node,state); + + // TreeView has problems with drawing items at times; it gets confused + // as to which colors apply to which items (see focus rectangle shifting; + // when one item is selected, click and hold on another). This needs to be fixed. + + bool colordelta = false; + Color riFore = renderinfo.ForeColor; + Color riBack = renderinfo.BackColor; + if (renderinfo != null && !riFore.IsEmpty) { + nmcd.clrText = ColorTranslator.ToWin32(riFore); + colordelta = true; + } + if (renderinfo != null && !riBack.IsEmpty) { + nmcd.clrTextBk = ColorTranslator.ToWin32(riBack); + colordelta = true; + } + if (colordelta) { + Marshal.StructureToPtr(nmcd, m.LParam, false); + } + if (renderinfo != null && renderinfo.Font != null) { + // Mess with the DC directly... + SafeNativeMethods.SelectObject(new HandleRef(nmcd.nmcd, nmcd.nmcd.hdc), new HandleRef(renderinfo, renderinfo.FontHandle)); + // There is a problem in winctl that clips node fonts if the fontsize + // is larger than the treeview font size. The behavior is much better in comctl 5 and above. + m.Result = (IntPtr)NativeMethods.CDRF_NEWFONT; + return; + } + + // fall through and do the default drawing work + goto default; + + case (NativeMethods.CDDS_ITEMPOSTPAINT): + //User draws only the text in OwnerDrawText mode, as explained in comments above + if (drawMode == TreeViewDrawMode.OwnerDrawText) + { + Debug.Assert(nmcd.nmcd.dwItemSpec != IntPtr.Zero, "Invalid node handle in ITEMPOSTPAINT"); + + // Get the node + node = NodeFromHandle((IntPtr)nmcd.nmcd.dwItemSpec); + + if (node == null) { + // this can happen if we are presently inserting the node - it hasn't yet + // been added to the handle table + return; + } + + + + Graphics g = Graphics.FromHdcInternal(nmcd.nmcd.hdc); + + DrawTreeNodeEventArgs e; + + try { + Rectangle bounds = node.Bounds; + Size textSize = TextRenderer.MeasureText(node.Text, node.TreeView.Font); + Point textLoc = new Point(bounds.X -1, bounds.Y); // required to center the text + bounds = new Rectangle(textLoc, new Size(textSize.Width, bounds.Height)); + + e = new DrawTreeNodeEventArgs(g, node, bounds, (TreeNodeStates) (nmcd.nmcd.uItemState)); + OnDrawNode(e); + + if (e.DrawDefault) { + //Simulate default text drawing here + TreeNodeStates curState = e.State; + + Font font = (node.NodeFont != null) ? node.NodeFont : node.TreeView.Font; + Color color = (((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) && node.TreeView.Focused) ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor; + + // Draw the actual node. + if ((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) + { + g.FillRectangle(SystemBrushes.Highlight, bounds); + ControlPaint.DrawFocusRectangle(g, bounds, color, SystemColors.Highlight); + TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); + } + else + { + // For Dev10 bug 478621: We need to draw the BackColor of + // nodes same as BackColor of treeview. + using (Brush brush = new SolidBrush(BackColor)) + { + g.FillRectangle(brush, bounds); + } + + TextRenderer.DrawText(g, e.Node.Text, font, bounds, color, TextFormatFlags.Default); + } + } + } + finally { + g.Dispose(); + } + + m.Result = (IntPtr)NativeMethods.CDRF_NOTIFYSUBITEMDRAW; + return; + } + + goto default; + + default: + // just in case we get a spurious message, tell it to do the right thing + m.Result = (IntPtr)NativeMethods.CDRF_DODEFAULT; + return; + } + } + + /// + /// + /// Generates colors for each item. This can be overridden to provide colors on a per state/per node + /// basis, rather than using the ForeColor/BackColor/NodeFont properties on TreeNode. + /// + /// + /// + protected OwnerDrawPropertyBag GetItemRenderStyles(TreeNode node, int state) { + OwnerDrawPropertyBag retval = new OwnerDrawPropertyBag(); + if (node == null || node.propBag == null) return retval; + + // we only change colors if we're displaying things normally + if ((state & (NativeMethods.CDIS_SELECTED | NativeMethods.CDIS_GRAYED | NativeMethods.CDIS_HOT | NativeMethods.CDIS_DISABLED))==0) { + retval.ForeColor = node.propBag.ForeColor; + retval.BackColor = node.propBag.BackColor; + } + retval.Font = node.propBag.Font; + return retval; + } + + private unsafe bool WmShowToolTip(ref Message m) + { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR*)m.LParam; + IntPtr tooltipHandle = nmhdr->hwndFrom; + + + NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + tvhip.pt_x = pos.X; + tvhip.pt_y = pos.Y; + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); + + if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { + + TreeNode tn = NodeFromHandle(hnode); + if (tn != null) + { + if (!ShowNodeToolTips) // default ToolTips + { + Rectangle bounds = tn.Bounds; + bounds.Location = this.PointToScreen(bounds.Location); + + UnsafeNativeMethods.SendMessage(new HandleRef(this, tooltipHandle), NativeMethods.TTM_ADJUSTRECT, 1, ref bounds); + SafeNativeMethods.SetWindowPos(new HandleRef(this, tooltipHandle), + NativeMethods.HWND_TOPMOST, bounds.Left, bounds.Top, 0, 0, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER); + return true; + } + } + } + return false; + } + + /// + /// + /// + /// + private void WmNeedText(ref Message m) { + NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + string tipText = controlToolTipText; + + NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + tvhip.pt_x = pos.X; + tvhip.pt_y = pos.Y; + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); + if (hnode != IntPtr.Zero && ((tvhip.flags & NativeMethods.TVHT_ONITEM) != 0)) { + TreeNode tn = NodeFromHandle(hnode); + if (ShowNodeToolTips && tn != null && (!string.IsNullOrEmpty(tn.ToolTipText))) { + tipText = tn.ToolTipText; + } + else if (tn != null && tn.Bounds.Right > this.Bounds.Right) { + tipText = tn.Text; + } + else { + tipText = null; + } + } + ttt.lpszText = tipText; + ttt.hinst = IntPtr.Zero; + + // RightToLeft reading order + // + if (RightToLeft == RightToLeft.Yes) { + ttt.uFlags |= NativeMethods.TTF_RTLREADING; + } + Marshal.StructureToPtr(ttt, m.LParam, false); + } + + + private unsafe void WmNotify(ref Message m) { + NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam; + + // Custom draw code is handled separately. + // + if ((nmhdr->code == NativeMethods.NM_CUSTOMDRAW)) { + CustomDraw(ref m); + } + else { + + NativeMethods.NMTREEVIEW* nmtv = (NativeMethods.NMTREEVIEW*)m.LParam; + + switch (nmtv->nmhdr.code) { + case NativeMethods.TVN_ITEMEXPANDINGA: + case NativeMethods.TVN_ITEMEXPANDINGW: + m.Result = TvnExpanding(nmtv); + break; + case NativeMethods.TVN_ITEMEXPANDEDA: + case NativeMethods.TVN_ITEMEXPANDEDW: + TvnExpanded(nmtv); + break; + case NativeMethods.TVN_SELCHANGINGA: + case NativeMethods.TVN_SELCHANGINGW: + m.Result = TvnSelecting(nmtv); + break; + case NativeMethods.TVN_SELCHANGEDA: + case NativeMethods.TVN_SELCHANGEDW: + TvnSelected(nmtv); + break; + case NativeMethods.TVN_BEGINDRAGA: + case NativeMethods.TVN_BEGINDRAGW: + TvnBeginDrag(MouseButtons.Left, nmtv); + break; + case NativeMethods.TVN_BEGINRDRAGA: + case NativeMethods.TVN_BEGINRDRAGW: + TvnBeginDrag(MouseButtons.Right, nmtv); + break; + case NativeMethods.TVN_BEGINLABELEDITA: + case NativeMethods.TVN_BEGINLABELEDITW: + m.Result = TvnBeginLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); + break; + case NativeMethods.TVN_ENDLABELEDITA: + case NativeMethods.TVN_ENDLABELEDITW: + m.Result = TvnEndLabelEdit((NativeMethods.NMTVDISPINFO)m.GetLParam(typeof(NativeMethods.NMTVDISPINFO))); + break; + case NativeMethods.NM_CLICK: + case NativeMethods.NM_RCLICK: + MouseButtons button = MouseButtons.Left; + + NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); + Point pos = Cursor.Position; + pos = PointToClientInternal(pos); + tvhip.pt_x = pos.X; + tvhip.pt_y = pos.Y; + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); + if (nmtv->nmhdr.code != NativeMethods.NM_CLICK + || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { + button = nmtv->nmhdr.code == NativeMethods.NM_CLICK + ? MouseButtons.Left : MouseButtons.Right; + } + + // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when + // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. + // We work around that by calling OnMouseUp here. + if (nmtv->nmhdr.code != NativeMethods.NM_CLICK + || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0 || FullRowSelect) { + if (hnode != IntPtr.Zero && !ValidationCancelled) { + OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), button, 1, pos.X, pos.Y)); + OnClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); + OnMouseClick(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); + + } + } + if (nmtv->nmhdr.code == NativeMethods.NM_RCLICK) { + TreeNode treeNode = NodeFromHandle(hnode); + if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null)) { + ShowContextMenu(treeNode); + } + else { + treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = true; + SendMessage(NativeMethods.WM_CONTEXTMENU, Handle, SafeNativeMethods.GetMessagePos()); + } + m.Result = (IntPtr)1; + + } + + if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) { + if (nmtv->nmhdr.code != NativeMethods.NM_CLICK + || (tvhip.flags & NativeMethods.TVHT_ONITEM) != 0) { + // The treeview's WndProc doesn't get the WM_LBUTTONUP messages when + // LBUTTONUP happens on TVHT_ONITEM. This is a comctl quirk. + // We work around that by calling OnMouseUp here. + OnMouseUp(new MouseEventArgs(button, 1, pos.X, pos.Y, 0)); + treeViewState[TREEVIEWSTATE_mouseUpFired] = true; + } + + } + break; + } + } + } + + /// + /// + /// Shows the context menu for the Treenode. + /// + /// + private void ShowContextMenu(TreeNode treeNode) { + + if (treeNode.ContextMenu != null || treeNode.ContextMenuStrip != null) { + + + ContextMenu contextMenu = treeNode.ContextMenu; + ContextMenuStrip menu = treeNode.ContextMenuStrip; + + if (contextMenu != null) + { + + NativeMethods.POINT pt = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(pt); + + // VS7 #38994 + // The solution to this problem was found in MSDN Article ID: Q135788. + // Summary: the current window must be made the foreground window + // before calling TrackPopupMenuEx, and a task switch must be + // forced after the call. + + UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this, this.Handle)); + + contextMenu.OnPopup( EventArgs.Empty ); + + SafeNativeMethods.TrackPopupMenuEx(new HandleRef(contextMenu, contextMenu.Handle), + NativeMethods.TPM_VERTICAL, + pt.x, + pt.y, + new HandleRef(this, this.Handle), + null); + + // Force task switch (see above) + UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), NativeMethods.WM_NULL, IntPtr.Zero, IntPtr.Zero); + } + // VsWhidbey : 432712. + // Need to send TVM_SELECTITEM to highlight the node while the contextMenuStrip is being shown. + else if (menu != null) + { + UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, treeNode.Handle); + menu.ShowInternal(this, PointToClient(MousePosition),/*keyboardActivated*/false); + menu.Closing += new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); + } + } + } + + // VsWhidbey : 432712. + // Need to send TVM_SELECTITEM to reset the node-highlighting while the contextMenuStrip is being closed so that the treeview reselects the SelectedNode. + private void ContextMenuStripClosing(object sender, ToolStripDropDownClosingEventArgs e) + { + ContextMenuStrip strip = sender as ContextMenuStrip; + // Unhook the Event. + strip.Closing -= new ToolStripDropDownClosingEventHandler(this.ContextMenuStripClosing); + SendMessage(NativeMethods.TVM_SELECTITEM, NativeMethods.TVGN_DROPHILITE, null); + } + + private void WmPrint(ref Message m) { + base.WndProc(ref m); + + if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { + IntSecurity.UnmanagedCode.Assert(); + try { + using (Graphics g = Graphics.FromHdc(m.WParam)) { + Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); + g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); + rect.Inflate(-1, -1); + g.DrawRectangle(SystemPens.Window, rect); + } + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_WINDOWPOSCHANGING: + case NativeMethods.WM_NCCALCSIZE: + case NativeMethods.WM_WINDOWPOSCHANGED: + case NativeMethods.WM_SIZE: + // While we are changing size of treeView to avoid the scrollbar; dont respond to the window-sizing messages. + if (treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs]) + { + //Debug.WriteLineIf(treeViewState[TREEVIEWSTATE_stopResizeWindowMsgs], "Sending message directly to DefWndProc() : " + m.ToString()); + DefWndProc(ref m); + } + else + { + base.WndProc(ref m); + } + break; + case NativeMethods.WM_HSCROLL: + base.WndProc(ref m); + if (DrawMode == TreeViewDrawMode.OwnerDrawAll) + { + //VsW : 432718 + Invalidate(); + } + break; + + case NativeMethods.WM_PRINT: + WmPrint(ref m); + break; + case NativeMethods.TVM_SETITEMA: + case NativeMethods.TVM_SETITEMW: + base.WndProc(ref m); + if (this.CheckBoxes) { + NativeMethods.TV_ITEM item = (NativeMethods.TV_ITEM) m.GetLParam(typeof(NativeMethods.TV_ITEM)); + // Check for invalid node handle + if (item.hItem != IntPtr.Zero) { + NativeMethods.TV_ITEM item1 = new NativeMethods.TV_ITEM(); + item1.mask = NativeMethods.TVIF_HANDLE | NativeMethods.TVIF_STATE; + item1.hItem = item.hItem; + item1.stateMask = NativeMethods.TVIS_STATEIMAGEMASK; + UnsafeNativeMethods.SendMessage(new HandleRef(null, this.Handle), NativeMethods.TVM_GETITEM, 0, ref item1); + + TreeNode node = NodeFromHandle(item.hItem); + node.CheckedStateInternal = ((item1.state >> 12) > 1); + } + } + break; + case NativeMethods.WM_NOTIFY: + NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR)); + switch (nmhdr.code) { + case NativeMethods.TTN_GETDISPINFOA: + case NativeMethods.TTN_GETDISPINFOW: + // MSDN: + // Setting the max width has the added benefit of enabling multiline + // tool tips! + // + UnsafeNativeMethods.SendMessage(new HandleRef(nmhdr, nmhdr.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); + WmNeedText(ref m); + m.Result = (IntPtr)1; + return; + case NativeMethods.TTN_SHOW: + if (WmShowToolTip(ref m)) + { + m.Result = (IntPtr)1; + return; + } + else + { + base.WndProc(ref m); + break; + } + + default: + base.WndProc(ref m); + break; + } + break; + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: + WmNotify(ref m); + break; + case NativeMethods.WM_LBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Left, 2); + //just maintain state and fire double click.. in final mouseUp... + treeViewState[TREEVIEWSTATE_doubleclickFired] = true; + //fire Up in the Wndproc !! + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + //problem getting the UP... outside the control... + // + CaptureInternal = true; + break; + case NativeMethods.WM_LBUTTONDOWN: + try + { + treeViewState[TREEVIEWSTATE_ignoreSelects] = true; + FocusInternal(); + } + finally + { + treeViewState[ TREEVIEWSTATE_ignoreSelects] = false; + } + //Always Reset the MouseupFired.... + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + NativeMethods.TV_HITTESTINFO tvhip = new NativeMethods.TV_HITTESTINFO(); + tvhip.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); + tvhip.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); + hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhip); + + // This gets around the TreeView behavior of temporarily moving the selection + // highlight to a node when the user clicks on its checkbox. + if ((tvhip.flags & NativeMethods.TVHT_ONITEMSTATEICON) != 0) { + //We donot pass the Message to the Control .. so fire MouseDowm ... + OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + if (!ValidationCancelled && CheckBoxes) + { + TreeNode node = NodeFromHandle(hNodeMouseDown); + bool eventReturn = TreeViewBeforeCheck(node, TreeViewAction.ByMouse); + if (!eventReturn && node != null) { + node.CheckedInternal = !node.CheckedInternal; + TreeViewAfterCheck(node, TreeViewAction.ByMouse); + } + } + m.Result = IntPtr.Zero; + } + else { + WmMouseDown(ref m, MouseButtons.Left, 1); + } + downButton = MouseButtons.Left; + break; + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_RBUTTONUP: + NativeMethods.TV_HITTESTINFO tvhi = new NativeMethods.TV_HITTESTINFO(); + tvhi.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); + tvhi.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); + IntPtr hnode = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhi); + //Important for CheckBoxes ... click needs to be fired ... + // + if(hnode != IntPtr.Zero) { + if (!ValidationCancelled && !treeViewState[TREEVIEWSTATE_doubleclickFired] & !treeViewState[TREEVIEWSTATE_mouseUpFired]) { + //OnClick(EventArgs.Empty); + + //If the hit-tested node here is the same as the node we hit-tested + //on mouse down then we will fire our OnNodeMoseClick event. + if (hnode == hNodeMouseDown) { + OnNodeMouseClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); + } + + OnClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseClick(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + + if (treeViewState[TREEVIEWSTATE_doubleclickFired]) { + treeViewState[TREEVIEWSTATE_doubleclickFired] = false; + if (!ValidationCancelled) { + //OnDoubleClick(EventArgs.Empty); + OnNodeMouseDoubleClick(new TreeNodeMouseClickEventArgs(NodeFromHandle(hnode), downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam))); + OnDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + OnMouseDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + } + } + } + if (!treeViewState[TREEVIEWSTATE_mouseUpFired]) + OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); + treeViewState[TREEVIEWSTATE_doubleclickFired] = false; + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + CaptureInternal = false; + + //always clear our hit-tested node we cached on mouse down + hNodeMouseDown = IntPtr.Zero; + break; + case NativeMethods.WM_MBUTTONDBLCLK: + //fire Up in the Wndproc !! + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + WmMouseDown(ref m, MouseButtons.Middle, 2); + break; + case NativeMethods.WM_MBUTTONDOWN: + //Always Reset the MouseupFired.... + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + WmMouseDown(ref m, MouseButtons.Middle, 1); + downButton = MouseButtons.Middle; + break; + case NativeMethods.WM_MOUSELEAVE: + // if the mouse leaves and then reenters the TreeView + // NodeHovered events should be raised. + prevHoveredNode = null; + base.WndProc(ref m); + break; + case NativeMethods.WM_RBUTTONDBLCLK: + WmMouseDown(ref m, MouseButtons.Right, 2); + //just maintain state and fire double click.. in final mouseUp... + treeViewState[TREEVIEWSTATE_doubleclickFired] = true; + //fire Up in the Wndproc !! + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + //problem getting the UP... outside the control... + // + CaptureInternal = true; + break; + case NativeMethods.WM_RBUTTONDOWN: + //Always Reset the MouseupFired.... + treeViewState[TREEVIEWSTATE_mouseUpFired] = false; + //Cache the hit-tested node for verification when mouse up is fired + NativeMethods.TV_HITTESTINFO tvhit = new NativeMethods.TV_HITTESTINFO(); + tvhit.pt_x = NativeMethods.Util.SignedLOWORD(m.LParam); + tvhit.pt_y = NativeMethods.Util.SignedHIWORD(m.LParam); + hNodeMouseDown = UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TVM_HITTEST, 0, tvhit); + + WmMouseDown(ref m, MouseButtons.Right, 1); + downButton = MouseButtons.Right; + break; + //# VS7 15052 + case NativeMethods.WM_SYSCOLORCHANGE: + SendMessage(NativeMethods.TVM_SETINDENT, Indent, 0); + base.WndProc(ref m); + break; + case NativeMethods.WM_SETFOCUS: + // If we get focus through the LBUttonDown .. we might have done the validation... + // so skip it.. + if (treeViewState[TREEVIEWSTATE_lastControlValidated]) + { + treeViewState[TREEVIEWSTATE_lastControlValidated] = false; + WmImeSetFocus(); + DefWndProc(ref m); + this.InvokeGotFocus(this, EventArgs.Empty); + } + else + { + base.WndProc(ref m); + } + break; + case NativeMethods.WM_CONTEXTMENU: + if (treeViewState[TREEVIEWSTATE_showTreeViewContextMenu]) { + treeViewState[TREEVIEWSTATE_showTreeViewContextMenu] = false; + base.WndProc(ref m); + } + else { + // this is the Shift + F10 Case.... + TreeNode treeNode = SelectedNode; + if (treeNode != null && (treeNode.ContextMenu != null || treeNode.ContextMenuStrip !=null)) { + Point client; + client = new Point(treeNode.Bounds.X , treeNode.Bounds.Y + treeNode.Bounds.Height / 2); + // VisualStudio7 # 156, only show the context menu when clicked in the client area + if (ClientRectangle.Contains( client )) { + if (treeNode.ContextMenu != null) { + treeNode.ContextMenu.Show(this, client); + } + else if (treeNode.ContextMenuStrip !=null) { + bool keyboardActivated = (unchecked((int)(long)m.LParam) == -1); + treeNode.ContextMenuStrip.ShowInternal(this, client, keyboardActivated); + } + } + } + else { + // in this case we dont have a selected node. The base + // will ensure we're constrained to the client area. + base.WndProc (ref m); + } + } + break; + + + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewAction.cs b/WindowsForms/Managed/System/WinForms/TreeViewAction.cs new file mode 100644 index 000000000..5aab8105e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewAction.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// This enum is used to specify the action that caused a TreeViewEventArgs. + /// + public enum TreeViewAction { + + /// + /// + /// The action is unknown. + /// + Unknown = 0, + + /// + /// + /// The event was caused by a keystroke. + /// + ByKeyboard = 1, + + /// + /// + /// The event was caused by a mouse click. + /// + ByMouse = 2, + + /// + /// + /// The tree node is collapsing. + /// + Collapse = 3, + + /// + /// + /// The tree node is expanding. + /// + Expand = 4, + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewCancelEvent.cs b/WindowsForms/Managed/System/WinForms/TreeViewCancelEvent.cs new file mode 100644 index 000000000..f39849c6f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewCancelEvent.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the , + /// , + /// , + /// or event. + /// + /// + /// + public class TreeViewCancelEventArgs : CancelEventArgs { + private TreeNode node; + private TreeViewAction action; + + /// + /// + /// [To be supplied.] + /// + public TreeViewCancelEventArgs(TreeNode node, bool cancel, TreeViewAction action) + : base(cancel) { + this.node = node; + this.action = action; + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode Node { + get { + return node; + } + } + + /// + /// + /// [To be supplied.] + /// + public TreeViewAction Action { + get { + return action; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewCancelEventHandler.cs b/WindowsForms/Managed/System/WinForms/TreeViewCancelEventHandler.cs new file mode 100644 index 000000000..3b965d93e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewCancelEventHandler.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will + /// handle the , , , or event of a + /// . + /// + /// + /// + public delegate void TreeViewCancelEventHandler(object sender, TreeViewCancelEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewDrawMode.cs b/WindowsForms/Managed/System/WinForms/TreeViewDrawMode.cs new file mode 100644 index 000000000..b7c4aa7c7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewDrawMode.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace System.Windows.Forms { + + /// + /// + /// + /// Specifies responsibility for drawing TreeView nodes. + /// + /// + + public enum TreeViewDrawMode { + /// + /// + /// + /// The operating system paints the nodes of the TreeView. + /// + /// + /// + Normal = 0, + + /// + /// + /// + /// The user needs to paint the text only. + /// + /// + OwnerDrawText = 1, + + /// + /// + /// + /// The user paints the entire row corresponding to a node, including lines and boxes. + /// + /// + OwnerDrawAll = 2, + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewEvent.cs b/WindowsForms/Managed/System/WinForms/TreeViewEvent.cs new file mode 100644 index 000000000..9e27cb16d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewEvent.cs @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + /// + /// + /// + /// Provides data for the , , , or event. + /// + /// + public class TreeViewEventArgs : EventArgs { + TreeNode node; + TreeViewAction action = TreeViewAction.Unknown; + + /// + /// + /// [To be supplied.] + /// + public TreeViewEventArgs(TreeNode node) { + this.node = node; + } + + /// + /// + /// [To be supplied.] + /// + public TreeViewEventArgs(TreeNode node, TreeViewAction action) { + this.node = node; + this.action = action; + } + + /// + /// + /// [To be supplied.] + /// + public TreeNode Node { + get { + return node; + } + } + + /// + /// + /// An event specific action-flag. + /// + public TreeViewAction Action { + get { + return action; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewEventHandler.cs b/WindowsForms/Managed/System/WinForms/TreeViewEventHandler.cs new file mode 100644 index 000000000..c4ee25d3b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewEventHandler.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + + + /// + /// + /// + /// Represents the method that will handle the , , , or + /// event of a + /// . + /// + /// + public delegate void TreeViewEventHandler(object sender, TreeViewEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewHitTestInfo.cs b/WindowsForms/Managed/System/WinForms/TreeViewHitTestInfo.cs new file mode 100644 index 000000000..0a6371550 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewHitTestInfo.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// Specifies the return value for HITTEST on treeview. + /// + /// + public class TreeViewHitTestInfo { + + private TreeViewHitTestLocations loc; + private TreeNode node; + + /// + /// + /// Creates a TreeViewHitTestInfo instance. + /// + public TreeViewHitTestInfo(TreeNode hitNode, TreeViewHitTestLocations hitLocation) { + this.node = hitNode; + this.loc = hitLocation; + } + + + /// + /// + /// This gives the exact location returned by hit test on treeview. + /// + public TreeViewHitTestLocations Location { + get { + return loc; + } + } + + /// + /// + /// This gives the node returned by hit test on treeview. + /// + public TreeNode Node { + get { + return node; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TreeViewHitTestLocation.cs b/WindowsForms/Managed/System/WinForms/TreeViewHitTestLocation.cs new file mode 100644 index 000000000..2193e7554 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewHitTestLocation.cs @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// + /// Specifies the + /// return value for HITTEST on treeview. + /// + /// + [ + Flags, + System.Runtime.InteropServices.ComVisible(true) + ] + public enum TreeViewHitTestLocations { + + /// + /// + /// + /// No Information. + /// + /// + None = NativeMethods.TVHT_NOWHERE, + + /// + /// + /// + /// On Image. + /// + /// + Image = NativeMethods.TVHT_ONITEMICON, + + /// + /// + /// + /// On Label. + /// + /// + Label = NativeMethods.TVHT_ONITEMLABEL, + + /// + /// + /// + /// Indent. + /// + /// + Indent = NativeMethods.TVHT_ONITEMINDENT, + + /// + /// + /// + /// AboveClientArea. + /// + /// + AboveClientArea = NativeMethods.TVHT_ABOVE, + + /// + /// + /// + /// BelowClientArea. + /// + /// + BelowClientArea = NativeMethods.TVHT_BELOW, + + /// + /// + /// + /// LeftOfClientArea. + /// + /// + LeftOfClientArea = NativeMethods.TVHT_TOLEFT, + + /// + /// + /// + /// RightOfClientArea. + /// + /// + RightOfClientArea = NativeMethods.TVHT_TORIGHT, + + /// + /// + /// + /// RightOfNode. + /// + /// + RightOfLabel = NativeMethods.TVHT_ONITEMRIGHT, + + /// + /// + /// + /// StateImage. + /// + /// + StateImage = NativeMethods.TVHT_ONITEMSTATEICON, + + /// + /// + /// + /// PlusMinus. + /// + /// + PlusMinus = NativeMethods.TVHT_ONITEMBUTTON, + } +} + diff --git a/WindowsForms/Managed/System/WinForms/TreeViewImageIndexConverter.cs b/WindowsForms/Managed/System/WinForms/TreeViewImageIndexConverter.cs new file mode 100644 index 000000000..e5f206be8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewImageIndexConverter.cs @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ + + +namespace System.Windows.Forms { + + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.Globalization; + using System.Collections.Specialized; + + /// + /// + /// TreeViewImageIndexConverter is a class that can be used to convert + /// image index values one data type to another. + /// + public class TreeViewImageIndexConverter : ImageIndexConverter { + + /// + protected override bool IncludeNoneAsStandardValue { + get { + return false; + } + } + + /// + /// + /// + /// Converts the given value object to a 32-bit signed integer object. + /// + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + string strValue = value as string; + if (strValue != null) { + + if (String.Compare(strValue, SR.GetString(SR.toStringDefault), true, culture) == 0) { + return -1; + } + else if (String.Compare(strValue, SR.GetString(SR.toStringNone), true, culture) == 0) { + return -2; + } + } + return base.ConvertFrom(context, culture, value); + } + + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + + if (destinationType == typeof(string) && value is int) { + int intValue = (int)value; + if (intValue == -1) { + return SR.GetString(SR.toStringDefault); + } + else if (intValue == -2) { + return SR.GetString(SR.toStringNone); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (context != null && context.Instance != null) { + object instance = context.Instance; + + PropertyDescriptor imageListProp = ImageListUtils.GetImageListProperty(context.PropertyDescriptor, ref instance); + + while (instance != null && imageListProp == null) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(instance); + + foreach (PropertyDescriptor prop in props) { + if (typeof(ImageList).IsAssignableFrom(prop.PropertyType)) { + imageListProp = prop; + break; + } + } + + if (imageListProp == null) { + // We didn't find the image list in this component. See if the + // component has a "parent" property. If so, walk the tree... + // + PropertyDescriptor parentProp = props[ParentImageListProperty]; + if (parentProp != null) { + instance = parentProp.GetValue(instance); + } + else { + // Stick a fork in us, we're done. + // + instance = null; + } + } + } + + if (imageListProp != null) { + ImageList imageList = (ImageList)imageListProp.GetValue(instance); + + if (imageList != null) { + // Create array to contain standard values + // + object[] values; + int nImages = imageList.Images.Count+2; + values = new object[nImages]; + values[nImages-2] = -1; + values[nImages-1] = -2; + + // Fill in the array + // + for (int i = 0; i < nImages-2; i++) { + values[i] = i; + } + return new StandardValuesCollection(values); + } + } + } + + return new StandardValuesCollection(new object[] { -1, -2 }); + } + } +} // Namespace system.windows.forms + diff --git a/WindowsForms/Managed/System/WinForms/TreeViewImageKeyConverter.cs b/WindowsForms/Managed/System/WinForms/TreeViewImageKeyConverter.cs new file mode 100644 index 000000000..532363d8f --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TreeViewImageKeyConverter.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.Drawing; + using System.Diagnostics; + using System.Globalization; + using System.Reflection; + using System.Collections.Specialized; + + /// + /// + /// ImageIndexConverter is a class that can be used to convert + /// image index values one data type to another. + /// + public class TreeViewImageKeyConverter : ImageKeyConverter { + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException("destinationType"); + } + + if (destinationType == typeof(string) && (value == null)) { + return SR.GetString(SR.toStringDefault); + } + else { + string strValue = value as string; + if (strValue != null && (strValue.Length == 0)) { + return SR.GetString(SR.toStringDefault); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + } +} + diff --git a/WindowsForms/Managed/System/WinForms/Triangle.cs b/WindowsForms/Managed/System/WinForms/Triangle.cs new file mode 100644 index 000000000..640f78db1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/Triangle.cs @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + using System.Windows.Forms; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// This class fully encapsulates the painting logic for a triangle. (Used by DataGrid) + /// + internal static class Triangle { + private const double TRI_HEIGHT_RATIO = 2.5; + private const double TRI_WIDTH_RATIO = 0.8; + + /* Commenting this overload out until someone actually needs it again... + public static void Paint(Graphics g, Rectangle bounds, TriangleDirection dir, Brush backBr, Pen backPen) { + Paint(g, bounds, dir, backBr, backPen, true); + } + */ + + /* Commenting this overload out until someone actually needs it again... + public static void Paint(Graphics g, Rectangle bounds, TriangleDirection dir, + Brush backBr, Pen backPen, bool opaque) { + // build an equilateral triangle centered on the midpoint of the rect. + Point[] points = BuildTrianglePoints(dir, bounds); + + if (opaque) + g.FillPolygon(backBr, points); + g.DrawPolygon(backPen, points); + } + */ + + public static void Paint(Graphics g, Rectangle bounds, TriangleDirection dir, Brush backBr, + Pen backPen1, Pen backPen2, Pen backPen3, bool opaque) + { + // build an equilateral triangle centered on the midpoint of the rect. + Point[] points = BuildTrianglePoints(dir, bounds); + + if (opaque) + g.FillPolygon(backBr, points); + g.DrawLine(backPen1, points[0], points[1]); + g.DrawLine(backPen2, points[1], points[2]); + g.DrawLine(backPen3, points[2], points[0]); + } + + private static Point[] BuildTrianglePoints(TriangleDirection dir, + Rectangle bounds) + { + Point[] points = new Point[3]; + + int updnWidth =(int)(bounds.Width * TRI_WIDTH_RATIO); + if (updnWidth % 2 == 1) + updnWidth++; + int updnHeight =(int)Math.Ceiling((updnWidth/2) * TRI_HEIGHT_RATIO); + + int lrWidth =(int)(bounds.Height * TRI_WIDTH_RATIO); + if (lrWidth % 2 == 0) + lrWidth++; + int lrHeight =(int)Math.Ceiling((lrWidth/2) * TRI_HEIGHT_RATIO); + + switch (dir) { + case TriangleDirection.Up: + { + points[0] = new Point(0, updnHeight); + points[1] = new Point(updnWidth, updnHeight); + points[2] = new Point(updnWidth / 2, 0); + } + break; + case TriangleDirection.Down: + { + points[0] = new Point(0, 0); + points[1] = new Point(updnWidth, 0); + points[2] = new Point(updnWidth / 2, updnHeight); + } + break; + case TriangleDirection.Left: + { + points[0] = new Point(lrWidth, 0); + points[1] = new Point(lrWidth, lrHeight); + points[2] = new Point(0, lrHeight / 2); + } + break; + case TriangleDirection.Right: + { + points[0] = new Point(0, 0); + points[1] = new Point(0, lrHeight); + points[2] = new Point(lrWidth, lrHeight / 2); + } + break; + default: + Debug.Fail("Wrong triangle enum"); + break; + } + + // we need to center our triangles into the bounds given. + // NOTE: On the up/down case, the offsets are different! + switch (dir) { + case TriangleDirection.Up: + case TriangleDirection.Down: + OffsetPoints(points, + bounds.X +(bounds.Width - updnHeight)/2, + bounds.Y +(bounds.Height - updnWidth)/2); + break; + case TriangleDirection.Left: + case TriangleDirection.Right: + OffsetPoints(points, + bounds.X +(bounds.Width - lrWidth)/2, + bounds.Y +(bounds.Height - lrHeight)/2); + break; + } + return points; + } + + private static void OffsetPoints(Point[] points, int xOffset, int yOffset) { + for (int i = 0; i < points.Length; i++) { + points[i].X += xOffset; + points[i].Y += yOffset; + } + } + } + + /// + /// + /// + /// [To be supplied.] + /// + internal enum TriangleDirection { + /// + /// + /// + /// [To be supplied.] + /// + Up, + /// + /// + /// + /// [To be supplied.] + /// + Down, + /// + /// + /// + /// [To be supplied.] + /// + Left, + /// + /// + /// + /// [To be supplied.] + /// + Right + } +} diff --git a/WindowsForms/Managed/System/WinForms/TrustManager.cs b/WindowsForms/Managed/System/WinForms/TrustManager.cs new file mode 100644 index 000000000..b30e47415 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TrustManager.cs @@ -0,0 +1,1274 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +using System; +using System.Security; +using System.Security.Policy; +using System.Windows.Forms; +using System.Globalization; +using System.Text; +using System.IO; +using System.Collections; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Security.Permissions; +using System.Xml; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Deployment.Internal; +using System.Deployment.Internal.Isolation; +using System.Deployment.Internal.Isolation.Manifest; +using System.Deployment.Internal.CodeSigning; +using Microsoft.Win32; +using System.Runtime.InteropServices; + +namespace System.Security.Policy +{ + /// + internal class TrustManager : IApplicationTrustManager + { + public const string PromptingLevelKeyName = @"Software\Microsoft\.NETFramework\Security\TrustManager\PromptingLevel"; + + /// + public TrustManager() + { + } + + // IApplicationTrustManager Implementation + + /// + [ + HostProtection(UI=true) + ] + public ApplicationTrust DetermineApplicationTrust(ActivationContext activationContext, TrustManagerContext trustManagerContext) + { + if (activationContext == null) + { + throw new ArgumentNullException("activationContext"); + } + + ApplicationSecurityInfo info = new ApplicationSecurityInfo(activationContext); + ApplicationTrustExtraInfo appTrustExtraInfo = new ApplicationTrustExtraInfo(); + // ISSUE - fix this.... + HostContextInternal hostContextInternal = new HostContextInternal(trustManagerContext); + + ICMS cms = (ICMS)InternalActivationContextHelper.GetDeploymentComponentManifest(activationContext); + + ParsedData parsedData = new ParsedData(); + if (ParseManifest(cms, parsedData)) + { + appTrustExtraInfo.RequestsShellIntegration = parsedData.RequestsShellIntegration; + } + + string deploymentUrl = GetDeploymentUrl(info); + string zoneName = GetZoneNameFromDeploymentUrl(deploymentUrl); + MemoryStream ms; + PromptsAllowed promptsAllowed; + + if (!ExtractManifestContent(cms, out ms)) + { + // Block prompt + return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/); + } + + bool distrustedPublisher, trustedPublisher, noCertificate; + AnalyzeCertificate(parsedData, ms, out distrustedPublisher, out trustedPublisher, out noCertificate); + + /// Check whether application manifest allows to use deployment manifest certificate. + /// If not then we have to use application manifest certificate instead. + ICMS applicationCms = (ICMS)InternalActivationContextHelper.GetApplicationComponentManifest(activationContext); + ParsedData applicationParsedData = new ParsedData(); + if (ParseManifest(applicationCms, applicationParsedData)) + { + if (applicationParsedData.UseManifestForTrust) + { + MemoryStream applicationMs; + if (ExtractManifestContent(applicationCms, out applicationMs)) + { + /// Use the old parsedData. + bool applicationDistrustedPublisher, applicationTrustedPublisher, applicationNoCertificate; + AnalyzeCertificate(parsedData, applicationMs, out applicationDistrustedPublisher, out applicationTrustedPublisher, out applicationNoCertificate); + distrustedPublisher = applicationDistrustedPublisher; + trustedPublisher = applicationTrustedPublisher; + noCertificate = applicationNoCertificate; + parsedData.AppName = applicationParsedData.AppName; + parsedData.AppPublisher = applicationParsedData.AppPublisher; + parsedData.SupportUrl = applicationParsedData.SupportUrl; + } + } + } + + if (distrustedPublisher) + { + promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData); + if (promptsAllowed == PromptsAllowed.None) + { + // No prompt allowed, return Do Not Trust. + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/); + } + return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/); + } + + if (noCertificate) + { + parsedData.AuthenticodedPublisher = null; + parsedData.Certificate = null; + } + + if (!hostContextInternal.IgnorePersistedDecision) + { + // Check if there are previously trusted versions installed. + ArrayList matchingTrusts; + if (SearchPreviousTrustedVersion(activationContext, out matchingTrusts)) + { + Debug.Assert(matchingTrusts != null && matchingTrusts.Count > 0); + + // Found a matching app, with normally a different version. + if (ExistingTrustApplicable(info, matchingTrusts)) + { + // There is at least one old version that requires at the same or more permissions. + // ExistingTrustApplicable removed the non-applicable version from the matchingTrusts arrays. + Debug.Assert(matchingTrusts != null && matchingTrusts.Count > 0); + + // Check if the new app requires shell integration while none of the old ones did + if (appTrustExtraInfo.RequestsShellIntegration && + !SomePreviousTrustedVersionRequiresShellIntegration(matchingTrusts) && + !trustedPublisher) + { + promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData); + switch (promptsAllowed) + { + case PromptsAllowed.None: + // No prompt allowed, return Do Not Trust. + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/); + case PromptsAllowed.BlockingOnly: + return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/); + case PromptsAllowed.All: + // New app requires shell integration - bring up the Basic Install Prompt + return BasicInstallPrompt(activationContext, + parsedData, + deploymentUrl, + hostContextInternal, + info, + appTrustExtraInfo, + zoneName, + AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/); + } + } + + // No prompt, return Trust & Persist. + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, true /*trust*/, hostContextInternal.Persist /*persist*/); + } + } + } + + bool permissionElevationRequired = AppRequestsBeyondDefaultTrust(info); + if (!permissionElevationRequired || trustedPublisher) + { + if (!trustedPublisher) + { + Debug.Assert(!permissionElevationRequired); + promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData); + switch (promptsAllowed) + { + case PromptsAllowed.BlockingOnly: + return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, permissionElevationRequired); + case PromptsAllowed.None: + // XBaps should also prompt in InternetZone + // Originally xbaps were silently trusted, along with other ClickOnce apps + case PromptsAllowed.All: + // App shell integrates and is not from a trusted deployer, bring up the Basic Install Prompt. + return BasicInstallPrompt(activationContext, + parsedData, + deploymentUrl, + hostContextInternal, + info, + appTrustExtraInfo, + zoneName, + false /*permissionElevationRequired*/); + } + } + else + { + // App does not shell integrate and does not run in "Internet" zone, or is from a trusted deployer, return Trust + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, true /*trust*/, hostContextInternal.Persist /*persist*/); + } + } + + promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData); + switch (promptsAllowed) + { + case PromptsAllowed.None: + // No prompt allowed, return Do Not Trust. + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/); + case PromptsAllowed.BlockingOnly: + return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, true /*permissionElevationRequired*/); + default: // PromptsAllowed.All: + // Bring up the HighRisk Install Prompt if the app shell integrates, or the HighRisk Run Prompt otherwise. + return HighRiskPrompt(activationContext, parsedData, deploymentUrl, hostContextInternal, info, appTrustExtraInfo, zoneName); + } + } + + // ISecurityEncodable implementation + + /// + public SecurityElement ToXml() + { + SecurityElement elRoot = new SecurityElement("IApplicationTrustManager"); + elRoot.AddAttribute("class", SecurityElement.Escape(this.GetType().AssemblyQualifiedName)); + elRoot.AddAttribute("version", "1"); + return elRoot; + } + + /// + public void FromXml(SecurityElement element) + { + if (element == null) + { + throw new ArgumentNullException("element"); + } + + if (!String.Equals(element.Tag, "IApplicationTrustManager", StringComparison.Ordinal)) + { + throw new ArgumentException(SR.GetString(SR.TrustManagerBadXml ,"IApplicationTrustManager")); + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static string DefaultBrowserExePath + { + get + { + try + { + string strBrowserPath = null; + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try + { + RegistryKey key = Registry.ClassesRoot.OpenSubKey("http\\shell\\open\\command"); + if (key != null) + { + string strBrowserCommand = (string) key.GetValue(string.Empty); + key.Close(); + + if (strBrowserCommand != null) + { + strBrowserCommand = strBrowserCommand.Trim(); + if (strBrowserCommand.Length != 0) + { + if (strBrowserCommand[0] == '\"') + { + int closingQuoteIndex = strBrowserCommand.IndexOf('"', 1); + if (closingQuoteIndex != -1) + { + strBrowserPath = strBrowserCommand.Substring(1, closingQuoteIndex - 1); + } + } + else + { + int firstSpaceIndex = strBrowserCommand.IndexOf(' '); + if (firstSpaceIndex != -1) + { + strBrowserPath = strBrowserCommand.Substring(0, firstSpaceIndex); + } + else + { + strBrowserPath = strBrowserCommand; + } + } + } + } + } + } + finally + { + System.Security.CodeAccessPermission.RevertAssert(); + } + return strBrowserPath; + } + catch (Exception ex) + { + Debug.Fail("Exception occurred while locating default browser executable: " + ex.Message); + return null; + } + } + } + + private static void ProcessSignerInfo(SignedCmiManifest2 signedManifest, out bool distrustedPublisher, out bool noCertificate) + { + Debug.Assert(signedManifest != null); + Debug.Assert(signedManifest.AuthenticodeSignerInfo != null); + + const int TRUST_E_REVOKED = unchecked((int)0x80092010); + distrustedPublisher = false; + noCertificate = false; + + // + // AuthenticodeSignerInfo can be null if the manifest is only strong name signed + // but not authenticode signed. This can happen when the strong name + // signature is invalid. + // + int error = signedManifest.AuthenticodeSignerInfo.ErrorCode; + if (error == Win32.TRUST_E_EXPLICIT_DISTRUST || error == TRUST_E_REVOKED) + { + distrustedPublisher = true; + return; + } + + if (error == Win32.TRUST_E_SUBJECT_NOT_TRUSTED) + { + // Certificate is valid but publisher is not in trusted list + return; + } + + // No certificate is equivalent to strong name signing only - run app only with user consent + noCertificate = true; + return; + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static bool AnalyzeCertificate(ParsedData parsedData, MemoryStream ms, out bool distrustedPublisher, out bool trustedPublisher, out bool noCertificate) + { + Debug.Assert(parsedData != null); + Debug.Assert(ms != null); + + distrustedPublisher = false; + trustedPublisher = false; + noCertificate = false; + SignedCmiManifest2 signedManifest = null; + XmlDocument deploymentManifestXmlDom = null; + try + { + deploymentManifestXmlDom = new XmlDocument(); + deploymentManifestXmlDom.PreserveWhitespace = true; + deploymentManifestXmlDom.Load(ms); + + signedManifest = new SignedCmiManifest2(deploymentManifestXmlDom, true); + + signedManifest.Verify(CmiManifestVerifyFlags.None); + } + catch (Exception e) + { + if (!(e is CryptographicException)) + { + return false; + } + if (signedManifest.StrongNameSignerInfo != null && signedManifest.StrongNameSignerInfo.ErrorCode != Win32.TRUST_E_BAD_DIGEST) { + if (signedManifest.AuthenticodeSignerInfo == null) + { + return false; + } + ProcessSignerInfo(signedManifest, out distrustedPublisher, out noCertificate); + return true; + } + // try SHA1 + try + { + signedManifest = new SignedCmiManifest2(deploymentManifestXmlDom, false); + signedManifest.Verify(CmiManifestVerifyFlags.None); + } + catch (Exception ex) + { + if (!(ex is CryptographicException)) + { + return false; + } + + if (signedManifest.AuthenticodeSignerInfo != null) + { + ProcessSignerInfo(signedManifest, out distrustedPublisher, out noCertificate); + return true; + } + return false; + } + + } + finally + { + if (signedManifest != null && + signedManifest.AuthenticodeSignerInfo != null && + signedManifest.AuthenticodeSignerInfo.SignerChain != null) + { + parsedData.Certificate = signedManifest.AuthenticodeSignerInfo.SignerChain.ChainElements[0].Certificate; + parsedData.AuthenticodedPublisher = parsedData.Certificate.GetNameInfo(X509NameType.SimpleName, false); + } + } + + if (signedManifest == null || signedManifest.AuthenticodeSignerInfo == null) + { + noCertificate = true; + } + else + { + trustedPublisher = true; + } + + return true; + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static bool AppRequestsBeyondDefaultTrust(ApplicationSecurityInfo info) + { + Debug.Assert(info != null); + + try + { + PermissionSet permSetDefaultZone = SecurityManager.GetStandardSandbox(info.ApplicationEvidence); + PermissionSet permSetRequested = GetRequestedPermissionSet(info); + + if (permSetDefaultZone == null && permSetRequested != null) + { + // No permissions are granted, and this launch requests some permissions + return true; + } + else if (permSetDefaultZone != null && permSetRequested == null) + { + // Some permissions are granted, and this launch does not require any permissions + return false; + } + + Debug.Assert(permSetDefaultZone != null); + Debug.Assert(permSetRequested != null); + + return !permSetRequested.IsSubsetOf(permSetDefaultZone); + } + catch (Exception) + { + return true; + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static ApplicationTrust BasicInstallPrompt(ActivationContext activationContext, + ParsedData parsedData, + String deploymentUrl, + HostContextInternal hostContextInternal, + ApplicationSecurityInfo info, + ApplicationTrustExtraInfo appTrustExtraInfo, + string zoneName, + bool permissionElevationRequired) + { + DialogResult ret; + TrustManagerPromptOptions options = CompletePromptOptions(permissionElevationRequired ? TrustManagerPromptOptions.RequiresPermissions : TrustManagerPromptOptions.None, appTrustExtraInfo, zoneName, info); + try + { + TrustManagerPromptUIThread basicInstallDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName, + DefaultBrowserExePath, + parsedData.SupportUrl, + GetHostFromDeploymentUrl(deploymentUrl), + parsedData.AuthenticodedPublisher /*publisherName*/, + parsedData.Certificate, + options); + ret = basicInstallDialog.ShowDialog(); + } + catch (Exception ex) + { + Debug.Fail("Error occurred while showing basic install dialog: " + ex.Message); + ret = DialogResult.No; + } + + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, ret == DialogResult.OK /*trust*/, hostContextInternal.Persist && ret == DialogResult.OK /*persist*/); + } + + private static TrustManagerPromptOptions CompletePromptOptions(TrustManagerPromptOptions options, + ApplicationTrustExtraInfo appTrustExtraInfo, + string zoneName, + ApplicationSecurityInfo info) + { + if (appTrustExtraInfo.RequestsShellIntegration) + { + options |= TrustManagerPromptOptions.AddsShortcut; + } + if (zoneName != null) + { + if (string.Compare(zoneName, "Internet", true, CultureInfo.InvariantCulture) == 0) + { + options |= TrustManagerPromptOptions.InternetSource; + } + else if (string.Compare(zoneName, "TrustedSites", true, CultureInfo.InvariantCulture) == 0) + { + options |= TrustManagerPromptOptions.TrustedSitesSource; + } + else if (string.Compare(zoneName, "UntrustedSites", true, CultureInfo.InvariantCulture) == 0) + { + options |= TrustManagerPromptOptions.UntrustedSitesSource; + } + else if (string.Compare(zoneName, "LocalIntranet", true, CultureInfo.InvariantCulture) == 0) + { + options |= TrustManagerPromptOptions.LocalNetworkSource; + } + else if (string.Compare(zoneName, "MyComputer", true, CultureInfo.InvariantCulture) == 0) + { + options |= TrustManagerPromptOptions.LocalComputerSource; + } + } + if (info != null) + { + PermissionSet pset = info.DefaultRequestSet; + if (pset != null && pset.IsUnrestricted()) + { + options |= TrustManagerPromptOptions.WillHaveFullTrust; + } + } + return options; + } + + private static ApplicationTrust CreateApplicationTrust(ActivationContext activationContext, + ApplicationSecurityInfo info, + ApplicationTrustExtraInfo appTrustExtraInfo, + bool trust, + bool persist) + { + ApplicationTrust appTrust = new ApplicationTrust(activationContext.Identity); + + appTrust.ExtraInfo = appTrustExtraInfo; + appTrust.IsApplicationTrustedToRun = trust; + appTrust.DefaultGrantSet = new PolicyStatement(info.DefaultRequestSet, (PolicyStatementAttribute) 0); + appTrust.Persist = persist; + + return appTrust; + } + + private static bool ExistingTrustApplicable(ApplicationSecurityInfo info, + ArrayList matchingTrusts) + { + Debug.Assert(info != null); + Debug.Assert(matchingTrusts != null); + + int entry = 0; + while (entry < matchingTrusts.Count) + { + ApplicationTrust matchingTrust = (ApplicationTrust) matchingTrusts[entry]; + if (!matchingTrust.IsApplicationTrustedToRun) + { + // Microsoft: Can this ever happen? I have serious doubts... + matchingTrusts.RemoveAt(entry); + } + + PermissionSet permSetRequested = GetRequestedPermissionSet(info); + PermissionSet permSetGranted = matchingTrust.DefaultGrantSet.PermissionSet; //PolicyStatement makes of copy of its permission set here. + + if (permSetGranted == null && permSetRequested != null) + { + // No permissions were granted, and this launch requests some permissions + matchingTrusts.RemoveAt(entry); + } + else if (permSetGranted != null && permSetRequested == null) + { + // Some permissions were granted, and this launch does not require any permissions + entry++; + continue; + } + + Debug.Assert(permSetGranted != null); + Debug.Assert(permSetRequested != null); + + if (permSetRequested.IsSubsetOf(permSetGranted)) + { + entry++; // Found a version that requires at least as much + } + else + { + // This version does not require as much + matchingTrusts.RemoveAt(entry); + } + } + return matchingTrusts.Count > 0; + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private unsafe static bool ExtractManifestContent(ICMS cms, out MemoryStream ms) + { + ms = new MemoryStream(); + try + { + System.Runtime.InteropServices.ComTypes.IStream pStream = cms as System.Runtime.InteropServices.ComTypes.IStream; + if (pStream == null) + { + return false; + } + + byte[] pv = new byte[4096]; + int size = 4096; + do { + pStream.Read(pv, size, new IntPtr(&size)); + ms.Write(pv, 0, size); + } + while (size == 4096); + ms.Position = 0; + return true; + } + catch (Exception) + { + Debug.Fail("Exception occurred in ExtractDeploymentManifestContent."); + return false; + } + } + + private static bool IsInternetZone(string zoneName) + { + if (string.Compare(zoneName, "Internet", true, CultureInfo.InvariantCulture) == 0) + { + return true; + } + + return false; + } + + private static PromptingLevel GetDefaultPromptingLevel(string zoneName) + { + PromptingLevel promptingLevel; + switch (zoneName) + { + case "Internet": + case "LocalIntranet": + case "MyComputer": + case "TrustedSites": + promptingLevel = PromptingLevel.Prompt; + break; + case "UntrustedSites": + promptingLevel = PromptingLevel.Disabled; + break; + default: + promptingLevel = PromptingLevel.Disabled; + break; + } + return promptingLevel; + } + + private static string GetDeploymentUrl(ApplicationSecurityInfo info) + { + Debug.Assert(info != null); + Evidence appEvidence = info.ApplicationEvidence; + + Url deploymentUrl = appEvidence.GetHostEvidence(); + if (deploymentUrl != null) + { + return deploymentUrl.Value; + } + + // Couldn't find deployment Url + return null; + } + + private static PermissionSet GetRequestedPermissionSet(ApplicationSecurityInfo info) + { + Debug.Assert(info != null); + + PermissionSet pset = info.DefaultRequestSet; + PermissionSet permSetRequested = null; + if (pset != null) + { + permSetRequested = pset.Copy(); + } + return permSetRequested; + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static string GetHostFromDeploymentUrl(string deploymentUrl) + { + if (deploymentUrl == null) + { + return string.Empty; + } + string host = null; + try + { + System.Uri uri = new System.Uri(deploymentUrl); + if (uri.Scheme == System.Uri.UriSchemeHttp || uri.Scheme == System.Uri.UriSchemeHttps) + { + host = uri.Host; + } + if (string.IsNullOrEmpty(host)) + { + host = uri.AbsolutePath; + int separatorIndex = -1; + if (string.IsNullOrEmpty(uri.Host) && host.StartsWith("/")) + { + host = host.TrimStart('/'); + separatorIndex = host.IndexOf('/'); + } + else if (uri.LocalPath.Length > 2 && (uri.LocalPath[1] == ':' || uri.LocalPath.StartsWith("\\\\"))) + { + host = uri.LocalPath; + separatorIndex = host.LastIndexOf('\\'); + } + if (separatorIndex != -1) + { + host = host.Remove(separatorIndex); + } + } + } + catch (Exception ex) + { + Debug.Fail("Exception occurred in GetHostFromDeploymentUrl: " + ex.Message); + return string.Empty; + } + return host; + } + + private static PromptsAllowed GetPromptsAllowed(HostContextInternal hostContextInternal, + string zoneName, + ParsedData parsedData) + { + Debug.Assert(hostContextInternal != null); + Debug.Assert(zoneName != null); + Debug.Assert(parsedData != null); + + if (hostContextInternal.NoPrompt) + { + return PromptsAllowed.None; + } + + PromptingLevel promptingLevel = GetZonePromptingLevel(zoneName); + if (promptingLevel == PromptingLevel.Disabled || + (promptingLevel == PromptingLevel.PromptOnlyForAuthenticode && parsedData.AuthenticodedPublisher == null)) + { + return PromptsAllowed.BlockingOnly; + } + + return PromptsAllowed.All; + } + + private static string GetZoneNameFromDeploymentUrl(String deploymentUrl) + { + Zone zone = Zone.CreateFromUrl(deploymentUrl); + if (zone == null || zone.SecurityZone == SecurityZone.NoZone) + { + return "UntrustedSites"; + } + switch (zone.SecurityZone) + { + case SecurityZone.Internet: + return "Internet"; + case SecurityZone.Intranet: + return "LocalIntranet"; + case SecurityZone.MyComputer: + return "MyComputer"; + case SecurityZone.Trusted: + return "TrustedSites"; + case SecurityZone.Untrusted: + return "UntrustedSites"; + default: + Debug.Fail("Unexpected SecurityZone in GetZoneNameFromDeploymentUrl"); + return "UntrustedSites"; + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static PromptingLevel GetZonePromptingLevel(string zoneName) + { + try + { + string promptingLevelStr = null; + new RegistryPermission(PermissionState.Unrestricted).Assert(); + try + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(PromptingLevelKeyName)) + { + if (key != null) + { + promptingLevelStr = (string)key.GetValue(zoneName); + } + } + } + finally + { + System.Security.CodeAccessPermission.RevertAssert(); + } + + if (string.IsNullOrEmpty(promptingLevelStr)) + { + return GetDefaultPromptingLevel(zoneName); + } + else if (string.Compare(promptingLevelStr, "Enabled", true, CultureInfo.InvariantCulture) == 0) + { + return PromptingLevel.Prompt; + } + else if (string.Compare(promptingLevelStr, "Disabled", true, CultureInfo.InvariantCulture) == 0) + { + return PromptingLevel.Disabled; + } + else if (string.Compare(promptingLevelStr, "AuthenticodeRequired", true, CultureInfo.InvariantCulture) == 0) + { + return PromptingLevel.PromptOnlyForAuthenticode; + } + else + { + return GetDefaultPromptingLevel(zoneName); + } + } + catch (Exception ex) + { + Debug.Fail("Exception occurred in GetZonePromptingLevel: " + ex.Message); + return GetDefaultPromptingLevel(zoneName); + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static ApplicationTrust HighRiskPrompt(ActivationContext activationContext, + ParsedData parsedData, + String deploymentUrl, + HostContextInternal hostContextInternal, + ApplicationSecurityInfo info, + ApplicationTrustExtraInfo appTrustExtraInfo, + string zoneName) + { + DialogResult ret; + TrustManagerPromptOptions options = CompletePromptOptions(TrustManagerPromptOptions.RequiresPermissions, appTrustExtraInfo, zoneName, info); + try + { + TrustManagerPromptUIThread highRiskDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName, + DefaultBrowserExePath, + parsedData.SupportUrl, + GetHostFromDeploymentUrl(deploymentUrl), + parsedData.AuthenticodedPublisher /*publisherName*/, + parsedData.Certificate, + options); + ret = highRiskDialog.ShowDialog(); + } + catch (Exception ex) + { + Debug.Fail("Error occurred while showing high risk dialog: " + ex.Message); + ret = DialogResult.No; + } + + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, ret == DialogResult.OK /*trust*/, hostContextInternal.Persist && ret == DialogResult.OK /*persist*/); + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static ApplicationTrust BlockingPrompt(ActivationContext activationContext, + ParsedData parsedData, + String deploymentUrl, + ApplicationSecurityInfo info, + ApplicationTrustExtraInfo appTrustExtraInfo, + string zoneName, + bool permissionElevationRequired) + { + TrustManagerPromptOptions options = CompletePromptOptions(permissionElevationRequired ? (TrustManagerPromptOptions.StopApp | TrustManagerPromptOptions.RequiresPermissions) : TrustManagerPromptOptions.StopApp, + appTrustExtraInfo, zoneName, info); + try + { + TrustManagerPromptUIThread errorDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName, + DefaultBrowserExePath, + parsedData.SupportUrl, + GetHostFromDeploymentUrl(deploymentUrl), + parsedData.AuthenticodedPublisher /*publisherName*/, + parsedData.Certificate, + options); + errorDialog.ShowDialog(); + } + catch (Exception ex) + { + Debug.Fail("Error occurred while showing error dialog: " + ex.Message); + } + + // Trust Manager should prompt, but is not allowed to. Return Don't Trust and Don't Persist. + return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/); + } + + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") + ] + private static bool ParseManifest(ICMS cms, ParsedData parsedData) + { + // parsedData is safely initialized to requestsShellIntegration:false/supportUrl:null/authenticodedPublisher:null //deploymentProviderCodebase:null + try + { + if (cms != null) + { + // Extract SupportUrl and Shell Visibility + if (cms.MetadataSectionEntry != null) + { + IMetadataSectionEntry metaDataSectionEntry = cms.MetadataSectionEntry as IMetadataSectionEntry; + if (metaDataSectionEntry != null) + { + IDescriptionMetadataEntry description = metaDataSectionEntry.DescriptionData; + if (description != null) + { + parsedData.SupportUrl = description.SupportUrl; + parsedData.AppName = description.Product; + parsedData.AppPublisher = description.Publisher; + } + + IDeploymentMetadataEntry deployment = metaDataSectionEntry.DeploymentData; + if (deployment != null) + { + parsedData.RequestsShellIntegration = ((deployment.DeploymentFlags & + (uint)(CMS_ASSEMBLY_DEPLOYMENT_FLAG.CMS_ASSEMBLY_DEPLOYMENT_FLAG_INSTALL)) != 0); + // parsedData.DeploymentProviderCodebase = deployment.DeploymentProviderCodebase; + } + + if ((metaDataSectionEntry.ManifestFlags & (uint)CMS_MANIFEST_FLAG.CMS_MANIFEST_FLAG_USEMANIFESTFORTRUST) != 0) + { + parsedData.UseManifestForTrust = true; + } + else + { + parsedData.UseManifestForTrust = false; + } + } + } + } + } + catch (Exception e) + { + Debug.Fail("Error parsing manifest: " + e.Message); + return false; + } + return true; + } + + private static bool SomePreviousTrustedVersionRequiresShellIntegration(ArrayList /*ApplicationTrust[]*/ matchingTrusts) + { + Debug.Assert(matchingTrusts != null); + foreach (ApplicationTrust matchingTrust in matchingTrusts) + { + ApplicationTrustExtraInfo matchingAppTrustExtraInfo = matchingTrust.ExtraInfo as ApplicationTrustExtraInfo; + if (matchingAppTrustExtraInfo != null && matchingAppTrustExtraInfo.RequestsShellIntegration) + { + return true; + } + if (null == matchingAppTrustExtraInfo && matchingTrust.DefaultGrantSet.PermissionSet.IsUnrestricted()) + { // Full trust trumps RequestsShellIntegration, so return true in this case. + return true; + } + } + // No previous trusted version requires shell integration + return false; + } + + private static bool SearchPreviousTrustedVersion(ActivationContext activationContext, + out ArrayList matchingTrusts) + { + // No match found by default + matchingTrusts = null; + + ApplicationTrustCollection appTrusts = ApplicationSecurityManager.UserApplicationTrusts; + foreach (ApplicationTrust appTrust in appTrusts) { + IDefinitionAppId appTrustAppId = IsolationInterop.AppIdAuthority.TextToDefinition(0, appTrust.ApplicationIdentity.FullName); + IDefinitionAppId actCtxAppId = IsolationInterop.AppIdAuthority.TextToDefinition(0, activationContext.Identity.FullName); + if (IsolationInterop.AppIdAuthority.AreDefinitionsEqual((uint) IAPPIDAUTHORITY_ARE_DEFINITIONS_EQUAL_FLAGS.IAPPIDAUTHORITY_ARE_DEFINITIONS_EQUAL_FLAG_IGNORE_VERSION, appTrustAppId, actCtxAppId)) + { + // Found an older matching app (or same version app which should never occur in theory) + if (matchingTrusts == null) + { + matchingTrusts = new ArrayList(); + } + matchingTrusts.Add(appTrust); + } + } + + // (matchingTrusts != null) <==> Found a prior version that was allowed to run. + return (matchingTrusts != null); + } + + private enum PromptingLevel + { + /// + /// + /// In the cases the Trust Manager needs to prompt, it needs to show the blocking prompt + /// + /// + Disabled = 0, + /// + /// + /// Prompting is allowed for strong named and authenticode signed application deployments + /// + /// + Prompt = 1, + /// + /// + /// Prompting is allowed for authenticode signed application deployments only + /// + /// + PromptOnlyForAuthenticode = 2 + } + + private enum PromptsAllowed + { + /// + /// + /// The Trust Manager is allowed to show any prompt + /// + /// + All = 0, + /// + /// + /// The Trust Manager is only allowed to show the blocking prompt + /// + /// + BlockingOnly = 1, + /// + /// + /// The Trust Manager is not allowed to prompt at all + /// + /// + None = 2 + } + } + + [Serializable] + internal class ApplicationTrustExtraInfo + { + private bool requestsShellIntegration = true; // If manifest parsing fails, we assume shell integration is required. + + public ApplicationTrustExtraInfo() + { + } + + public bool RequestsShellIntegration + { + get + { + return this.requestsShellIntegration; + } + + set + { + this.requestsShellIntegration = value; + } + } + } + + internal class TrustManagerPromptUIThread { + private string m_appName, m_defaultBrowserExePath, m_supportUrl, m_deploymentUrl, m_publisherName; + private X509Certificate2 m_certificate; + private TrustManagerPromptOptions m_options; + private DialogResult m_ret = DialogResult.No; + + public TrustManagerPromptUIThread(string appName, string defaultBrowserExePath, string supportUrl, string deploymentUrl, + string publisherName, X509Certificate2 certificate, TrustManagerPromptOptions options) { + this.m_appName = appName; + this.m_defaultBrowserExePath = defaultBrowserExePath; + this.m_supportUrl = supportUrl; + this.m_deploymentUrl = deploymentUrl; + this.m_publisherName = publisherName; + this.m_certificate = certificate; + this.m_options = options; + } + + public DialogResult ShowDialog() { + System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowDialogWork)); + thread.SetApartmentState(System.Threading.ApartmentState.STA); + thread.Start(); + thread.Join(); + return m_ret; + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void ShowDialogWork() + { + try { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + using (TrustManagerPromptUI trustManagerDialog = new TrustManagerPromptUI(this.m_appName, this.m_defaultBrowserExePath, + this.m_supportUrl, this.m_deploymentUrl, + this.m_publisherName, this.m_certificate, this.m_options)) { + m_ret = trustManagerDialog.ShowDialog(); + } + } + catch { + } + finally { + Application.ExitThread(); //explicitly call Dispose [DevDiv2 bug 184375, OleUnitinialize not being called] + } + } + } + + internal class ParsedData + { + private bool requestsShellIntegration; /* == false by default. shellVisible attribute is optional */ + private string appName; + private string appPublisher; + private string supportUrl; + private string authenticodedPublisher; + private bool disallowTrustOverride; + private X509Certificate2 certificate; +// private string deploymentProviderCodebase; + + public ParsedData() + { + } + + public bool RequestsShellIntegration + { + get + { + return this.requestsShellIntegration; + } + set + { + this.requestsShellIntegration = value; + } + } + + public X509Certificate2 Certificate + { + get + { + return this.certificate; + } + set + { + this.certificate = value; + } + } + + public string AppName + { + get + { + return this.appName; + } + set + { + this.appName = value; + } + } + + public string AppPublisher + { + get + { + return this.appPublisher; + } + set + { + this.appPublisher = value; + } + } + + public string AuthenticodedPublisher + { + get + { + return this.authenticodedPublisher; + } + set + { + this.authenticodedPublisher = value; + } + } + + public bool UseManifestForTrust + { + get + { + return disallowTrustOverride; + } + + set + { + disallowTrustOverride = value; + } + } + +// public string DeploymentProviderCodebase +// { +// get +// { +// return this.deploymentProviderCodebase; +// } +// set +// { +// this.deploymentProviderCodebase = value; +// } +// } + + public string SupportUrl + { + get + { + return this.supportUrl; + } + set + { + this.supportUrl = value; + } + } + } + + internal class HostContextInternal + { + private bool ignorePersistedDecision; + private bool noPrompt; + private bool persist; + private ApplicationIdentity previousAppId; + + public HostContextInternal(TrustManagerContext trustManagerContext) + { + if (trustManagerContext == null) + { + // Used in case DetermineApplicationTrust is not given a TrustManagerContext object. + this.persist = true; + } + else + { + // ISSUE - fix this... + // Note that exclusiveGrant is never set. It is read in CreateApplicationTrust however. + this.ignorePersistedDecision = trustManagerContext.IgnorePersistedDecision; + this.noPrompt = trustManagerContext.NoPrompt; + this.persist = trustManagerContext.Persist; + this.previousAppId = trustManagerContext.PreviousApplicationIdentity; + } + } + + public bool IgnorePersistedDecision + { + get + { + return this.ignorePersistedDecision; + } + } + + public bool NoPrompt + { + get + { + return this.noPrompt; + } + } + + public bool Persist + { + get + { + return this.persist; + } + } + + public ApplicationIdentity PreviousAppId + { + get + { + return this.previousAppId; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TrustManagerMoreInformation.cs b/WindowsForms/Managed/System/WinForms/TrustManagerMoreInformation.cs new file mode 100644 index 000000000..8d14e8c2a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TrustManagerMoreInformation.cs @@ -0,0 +1,444 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Win32; + +namespace System.Security.Policy +{ + internal class TrustManagerMoreInformation : System.Windows.Forms.Form + { + private System.ComponentModel.IContainer components = null; + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.Label lblPublisher; + private System.Windows.Forms.Label lblPublisherContent; + private System.Windows.Forms.Label lblMachineAccess; + private System.Windows.Forms.Label lblMachineAccessContent; + private System.Windows.Forms.Label lblInstallation; + private System.Windows.Forms.Label lblInstallationContent; + private System.Windows.Forms.Label lblLocation; + private System.Windows.Forms.Label lblLocationContent; + private System.Windows.Forms.PictureBox pictureBoxPublisher; + private System.Windows.Forms.PictureBox pictureBoxMachineAccess; + private System.Windows.Forms.PictureBox pictureBoxLocation; + private System.Windows.Forms.PictureBox pictureBoxInstallation; + private System.Windows.Forms.Button btnClose; + + internal TrustManagerMoreInformation(TrustManagerPromptOptions options, String publisherName) + { + InitializeComponent(); + this.Font = SystemFonts.MessageBoxFont; + lblMachineAccess.Font = lblPublisher.Font = lblInstallation.Font = lblLocation.Font = new Font(lblMachineAccess.Font, FontStyle.Bold); + FillContent(options, publisherName); + + if (AccessibilityImprovements.Level2) + { + // The form + this.AccessibleName = this.Text; + + // The outer panel + this.tableLayoutPanel.AccessibleName = string.Empty; + + // Publisher + this.pictureBoxPublisher.AccessibleName = this.pictureBoxPublisher.AccessibleDescription; + this.pictureBoxPublisher.AccessibleRole = AccessibleRole.Graphic; + this.lblPublisher.AccessibleName = this.lblPublisher.Text; + this.lblPublisherContent.AccessibleName = this.lblPublisherContent.Text; + + // Machine Access + this.pictureBoxMachineAccess.AccessibleName = this.pictureBoxMachineAccess.AccessibleDescription; + this.pictureBoxMachineAccess.AccessibleRole = AccessibleRole.Graphic; + this.lblMachineAccess.AccessibleName = this.lblMachineAccess.Text; + this.lblMachineAccessContent.AccessibleName = this.lblMachineAccessContent.Text; + + // Installation + this.pictureBoxInstallation.AccessibleName = this.pictureBoxInstallation.AccessibleDescription; + this.pictureBoxInstallation.AccessibleRole = AccessibleRole.Graphic; + this.lblInstallation.AccessibleName = this.lblInstallation.Text; + this.lblInstallationContent.AccessibleName = this.lblInstallationContent.Text; + + // Location + this.pictureBoxLocation.AccessibleName = this.pictureBoxLocation.AccessibleDescription; + this.pictureBoxLocation.AccessibleRole = AccessibleRole.Graphic; + this.lblLocation.AccessibleName = this.lblLocation.Text; + this.lblLocationContent.AccessibleName = this.lblLocationContent.Text; + + // Close button + this.btnClose.AccessibleName = this.btnClose.Text; + + // Re-order controls to fix Narrator's Scan Mode navigation + this.tableLayoutPanel.Controls.SetChildIndex(this.pictureBoxPublisher, 0); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblPublisher, 1); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblPublisherContent, 2); + this.tableLayoutPanel.Controls.SetChildIndex(this.pictureBoxMachineAccess, 3); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblMachineAccess, 4); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblMachineAccessContent, 5); + this.tableLayoutPanel.Controls.SetChildIndex(this.pictureBoxInstallation, 6); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblInstallation, 7); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblInstallationContent, 8); + this.tableLayoutPanel.Controls.SetChildIndex(this.pictureBoxLocation, 9); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblLocation, 10); + this.tableLayoutPanel.Controls.SetChildIndex(this.lblLocationContent, 11); + this.tableLayoutPanel.Controls.SetChildIndex(this.btnClose, 12); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + private void FillContent(TrustManagerPromptOptions options, String publisherName) + { + LoadWarningBitmap((publisherName == null) ? TrustManagerWarningLevel.Red : TrustManagerWarningLevel.Green, this.pictureBoxPublisher); + LoadWarningBitmap(((options & (TrustManagerPromptOptions.RequiresPermissions | TrustManagerPromptOptions.WillHaveFullTrust)) != 0) ? TrustManagerWarningLevel.Red : TrustManagerWarningLevel.Green, this.pictureBoxMachineAccess); + LoadWarningBitmap(((options & TrustManagerPromptOptions.AddsShortcut) != 0) ? TrustManagerWarningLevel.Yellow : TrustManagerWarningLevel.Green, this.pictureBoxInstallation); + + TrustManagerWarningLevel locationWarningLevel; + if ((options & (TrustManagerPromptOptions.LocalNetworkSource | + TrustManagerPromptOptions.LocalComputerSource | + TrustManagerPromptOptions.TrustedSitesSource)) != 0) + { + locationWarningLevel = TrustManagerWarningLevel.Green; + } + else if ((options & TrustManagerPromptOptions.UntrustedSitesSource) != 0) + { + locationWarningLevel = TrustManagerWarningLevel.Red; + } + else + { + Debug.Assert((options & TrustManagerPromptOptions.InternetSource) != 0); + locationWarningLevel = TrustManagerWarningLevel.Yellow; + } + LoadWarningBitmap(locationWarningLevel, this.pictureBoxLocation); + + if (publisherName == null) + { + this.lblPublisherContent.Text = SR.GetString(SR.TrustManagerMoreInfo_UnknownPublisher); + } + else + { + this.lblPublisherContent.Text = SR.GetString(SR.TrustManagerMoreInfo_KnownPublisher, publisherName); + } + + if ((options & (TrustManagerPromptOptions.RequiresPermissions | TrustManagerPromptOptions.WillHaveFullTrust)) != 0) + { + this.lblMachineAccessContent.Text = SR.GetString(SR.TrustManagerMoreInfo_UnsafeAccess); + } + else + { + this.lblMachineAccessContent.Text = SR.GetString(SR.TrustManagerMoreInfo_SafeAccess); + } + + if ((options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.Text = SR.GetString(SR.TrustManagerMoreInfo_InstallTitle); + this.lblInstallationContent.Text = SR.GetString(SR.TrustManagerMoreInfo_WithShortcut); + } + else + { + this.Text = SR.GetString(SR.TrustManagerMoreInfo_RunTitle); + this.lblInstallationContent.Text = SR.GetString(SR.TrustManagerMoreInfo_WithoutShortcut); + } + + string source; + if ((options & TrustManagerPromptOptions.LocalNetworkSource) != 0) + { + source = SR.GetString(SR.TrustManagerMoreInfo_LocalNetworkSource); + } + else if ((options & TrustManagerPromptOptions.LocalComputerSource) != 0) + { + source = SR.GetString(SR.TrustManagerMoreInfo_LocalComputerSource); + } + else if ((options & TrustManagerPromptOptions.InternetSource) != 0) + { + source = SR.GetString(SR.TrustManagerMoreInfo_InternetSource); + } + else if ((options & TrustManagerPromptOptions.TrustedSitesSource) != 0) + { + source = SR.GetString(SR.TrustManagerMoreInfo_TrustedSitesSource); + } + else + { + Debug.Assert((options & TrustManagerPromptOptions.UntrustedSitesSource) != 0); + source = SR.GetString(SR.TrustManagerMoreInfo_UntrustedSitesSource); + } + this.lblLocationContent.Text = SR.GetString(SR.TrustManagerMoreInfo_Location, source); + } + + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TrustManagerMoreInformation)); + this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.pictureBoxPublisher = new System.Windows.Forms.PictureBox(); + this.pictureBoxMachineAccess = new System.Windows.Forms.PictureBox(); + this.pictureBoxInstallation = new System.Windows.Forms.PictureBox(); + this.pictureBoxLocation = new System.Windows.Forms.PictureBox(); + this.lblPublisher = new System.Windows.Forms.Label(); + this.lblPublisherContent = new System.Windows.Forms.Label(); + this.lblMachineAccess = new System.Windows.Forms.Label(); + this.lblMachineAccessContent = new System.Windows.Forms.Label(); + this.lblInstallation = new System.Windows.Forms.Label(); + this.lblInstallationContent = new System.Windows.Forms.Label(); + this.lblLocation = new System.Windows.Forms.Label(); + this.lblLocationContent = new System.Windows.Forms.Label(); + this.btnClose = new System.Windows.Forms.Button(); + this.tableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxPublisher)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMachineAccess)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxInstallation)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLocation)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel + // + resources.ApplyResources(this.tableLayoutPanel, "tableLayoutPanel"); + this.tableLayoutPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 389F)); + this.tableLayoutPanel.Controls.Add(this.pictureBoxPublisher, 0, 0); + this.tableLayoutPanel.Controls.Add(this.pictureBoxMachineAccess, 0, 2); + this.tableLayoutPanel.Controls.Add(this.pictureBoxInstallation, 0, 4); + this.tableLayoutPanel.Controls.Add(this.pictureBoxLocation, 0, 6); + this.tableLayoutPanel.Controls.Add(this.lblPublisher, 1, 0); + this.tableLayoutPanel.Controls.Add(this.lblPublisherContent, 1, 1); + this.tableLayoutPanel.Controls.Add(this.lblMachineAccess, 1, 2); + this.tableLayoutPanel.Controls.Add(this.lblMachineAccessContent, 1, 3); + this.tableLayoutPanel.Controls.Add(this.lblInstallation, 1, 4); + this.tableLayoutPanel.Controls.Add(this.lblInstallationContent, 1, 5); + this.tableLayoutPanel.Controls.Add(this.lblLocation, 1, 6); + this.tableLayoutPanel.Controls.Add(this.lblLocationContent, 1, 7); + this.tableLayoutPanel.Controls.Add(this.btnClose, 1, 8); + this.tableLayoutPanel.Margin = new System.Windows.Forms.Padding(12); + this.tableLayoutPanel.Name = "tableLayoutPanel"; + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // pictureBoxPublisher + // + resources.ApplyResources(this.pictureBoxPublisher, "pictureBoxPublisher"); + this.pictureBoxPublisher.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.pictureBoxPublisher.Name = "pictureBoxPublisher"; + this.tableLayoutPanel.SetRowSpan(this.pictureBoxPublisher, 2); + this.pictureBoxPublisher.TabStop = false; + // + // pictureBoxMachineAccess + // + resources.ApplyResources(this.pictureBoxMachineAccess, "pictureBoxMachineAccess"); + this.pictureBoxMachineAccess.Margin = new System.Windows.Forms.Padding(0, 10, 3, 0); + this.pictureBoxMachineAccess.Name = "pictureBoxMachineAccess"; + this.tableLayoutPanel.SetRowSpan(this.pictureBoxMachineAccess, 2); + this.pictureBoxMachineAccess.TabStop = false; + // + // pictureBoxInstallation + // + resources.ApplyResources(this.pictureBoxInstallation, "pictureBoxInstallation"); + this.pictureBoxInstallation.Margin = new System.Windows.Forms.Padding(0, 10, 3, 0); + this.pictureBoxInstallation.Name = "pictureBoxInstallation"; + this.tableLayoutPanel.SetRowSpan(this.pictureBoxInstallation, 2); + this.pictureBoxInstallation.TabStop = false; + // + // pictureBoxLocation + // + resources.ApplyResources(this.pictureBoxLocation, "pictureBoxLocation"); + this.pictureBoxLocation.Margin = new System.Windows.Forms.Padding(0, 10, 3, 0); + this.pictureBoxLocation.Name = "pictureBoxLocation"; + this.tableLayoutPanel.SetRowSpan(this.pictureBoxLocation, 2); + this.pictureBoxLocation.TabStop = false; + // + // lblPublisher + // + resources.ApplyResources(this.lblPublisher, "lblPublisher"); + this.lblPublisher.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.lblPublisher.Name = "lblPublisher"; + // + // lblPublisherContent + // + resources.ApplyResources(this.lblPublisherContent, "lblPublisherContent"); + this.lblPublisherContent.Margin = new System.Windows.Forms.Padding(3, 0, 0, 10); + this.lblPublisherContent.Name = "lblPublisherContent"; + // + // lblMachineAccess + // + resources.ApplyResources(this.lblMachineAccess, "lblMachineAccess"); + this.lblMachineAccess.Margin = new System.Windows.Forms.Padding(3, 10, 0, 0); + this.lblMachineAccess.Name = "lblMachineAccess"; + // + // lblMachineAccessContent + // + resources.ApplyResources(this.lblMachineAccessContent, "lblMachineAccessContent"); + this.lblMachineAccessContent.Margin = new System.Windows.Forms.Padding(3, 0, 0, 10); + this.lblMachineAccessContent.Name = "lblMachineAccessContent"; + // + // lblInstallation + // + resources.ApplyResources(this.lblInstallation, "lblInstallation"); + this.lblInstallation.Margin = new System.Windows.Forms.Padding(3, 10, 0, 0); + this.lblInstallation.Name = "lblInstallation"; + // + // lblInstallationContent + // + resources.ApplyResources(this.lblInstallationContent, "lblInstallationContent"); + this.lblInstallationContent.Margin = new System.Windows.Forms.Padding(3, 0, 0, 10); + this.lblInstallationContent.Name = "lblInstallationContent"; + // + // lblLocation + // + resources.ApplyResources(this.lblLocation, "lblLocation"); + this.lblLocation.Margin = new System.Windows.Forms.Padding(3, 10, 0, 0); + this.lblLocation.Name = "lblLocation"; + // + // lblLocationContent + // + resources.ApplyResources(this.lblLocationContent, "lblLocationContent"); + this.lblLocationContent.Margin = new System.Windows.Forms.Padding(3, 0, 0, 10); + this.lblLocationContent.Name = "lblLocationContent"; + // + // btnClose + // + resources.ApplyResources(this.btnClose, "btnClose"); + this.btnClose.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnClose.Margin = new System.Windows.Forms.Padding(0, 10, 0, 0); + this.btnClose.MinimumSize = new System.Drawing.Size(75, 23); + this.btnClose.Name = "btnClose"; + this.btnClose.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0); + this.tableLayoutPanel.SetColumnSpan(this.btnClose, 2); + // + // TrustManagerMoreInformation + // + this.AcceptButton = this.btnClose; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.CancelButton = this.btnClose; + this.Controls.Add(this.tableLayoutPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TrustManagerMoreInformation"; + this.tableLayoutPanel.ResumeLayout(false); + this.tableLayoutPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxPublisher)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMachineAccess)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxInstallation)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLocation)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + } + + [ + SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity") + ] + private static void LoadWarningBitmap(TrustManagerWarningLevel warningLevel, System.Windows.Forms.PictureBox pictureBox) + { + Bitmap bitmap; + switch (warningLevel) + { + case TrustManagerWarningLevel.Green: + if (!LocalAppContextSwitches.UseLegacyImages) + { + bitmap = QueryDPiMatchedSmallBitmap("TrustManagerOK.ico"); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerOKSm.bmp"); + } + pictureBox.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_LowRisk), pictureBox.AccessibleDescription); + break; + case TrustManagerWarningLevel.Yellow: + if (!LocalAppContextSwitches.UseLegacyImages) + { + bitmap = QueryDPiMatchedSmallBitmap("TrustManagerWarning.ico"); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerWarningSm.bmp"); + } + pictureBox.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_MediumRisk), pictureBox.AccessibleDescription); + break; + default: + Debug.Assert(warningLevel == TrustManagerWarningLevel.Red); + if (!LocalAppContextSwitches.UseLegacyImages) + { + bitmap = QueryDPiMatchedSmallBitmap("TrustManagerHighRisk.ico"); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerHighRiskSm.bmp"); + } + pictureBox.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_HighRisk), pictureBox.AccessibleDescription); + break; + } + if (bitmap != null) + { + bitmap.MakeTransparent(); + pictureBox.Image = bitmap; + } + } + + internal static Bitmap QueryDPiMatchedSmallBitmap(string iconName) + { + var icon = new Icon(typeof(System.Windows.Forms.Form), iconName); + + // Querying 96 dpi icon. And smaller bit map is half that size. + icon = new Icon(icon, icon.Width / 2, icon.Height / 2); + + if(icon != null) + { + return icon.ToBitmap(); + } + + return null; + + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + } + + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.Window) + { + this.Font = SystemFonts.MessageBoxFont; + this.lblLocation.Font = + this.lblInstallation.Font = + this.lblMachineAccess.Font = + this.lblPublisher.Font = new Font(this.Font, FontStyle.Bold); + } + Invalidate(); // Workaround a bug where the form's background does not repaint properly + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TrustManagerPromptUI.cs b/WindowsForms/Managed/System/WinForms/TrustManagerPromptUI.cs new file mode 100644 index 000000000..0414e6a88 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TrustManagerPromptUI.cs @@ -0,0 +1,853 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography.X509Certificates; +using System.Runtime.InteropServices; +using Microsoft.Win32; + +namespace System.Security.Policy +{ + [Flags] + internal enum TrustManagerPromptOptions + { + None = 0x0000, + StopApp = 0x0001, + RequiresPermissions = 0x0002, + WillHaveFullTrust = 0x0004, + AddsShortcut = 0x0008, + LocalNetworkSource = 0x0010, + LocalComputerSource = 0x0020, + InternetSource = 0x0040, + TrustedSitesSource = 0x0080, + UntrustedSitesSource = 0x0100 + } + + internal enum TrustManagerWarningLevel + { + Green = 1, + Yellow = 2, + Red = 3 + } + + + internal class TrustManagerPromptUI : System.Windows.Forms.Form + { + private System.ComponentModel.IContainer components = null; + + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnInstall; + private System.Windows.Forms.Label lblFrom; + private System.Windows.Forms.Label lblName; + private System.Windows.Forms.Label lblPublisher; + private System.Windows.Forms.Label lblQuestion; + private System.Windows.Forms.LinkLabel linkLblFromUrl; + private System.Windows.Forms.LinkLabel linkLblMoreInformation; + private System.Windows.Forms.LinkLabel linkLblName; + private System.Windows.Forms.LinkLabel linkLblPublisher; + private System.Windows.Forms.PictureBox pictureBoxQuestion; + private System.Windows.Forms.PictureBox pictureBoxWarning; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanelButtons; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanelInfo; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanelOuter; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanelQuestion; + private System.Windows.Forms.ToolTip toolTipFromUrl; + + private string m_appName; + private string m_defaultBrowserExePath; + private string m_supportUrl; + private string m_deploymentUrl; + private string m_publisherName; + private X509Certificate2 m_certificate; + private TrustManagerPromptOptions m_options; + private Label lineLabel; + private TableLayoutPanel warningTextTableLayoutPanel; + private bool controlToolTip; + + internal TrustManagerPromptUI(string appName, + string defaultBrowserExePath, + string supportUrl, + string deploymentUrl, + string publisherName, + X509Certificate2 certificate, + TrustManagerPromptOptions options) + { + this.m_appName = appName; + this.m_defaultBrowserExePath = defaultBrowserExePath; + this.m_supportUrl = supportUrl; + this.m_deploymentUrl = deploymentUrl; + this.m_publisherName = publisherName; + this.m_certificate = certificate; + this.m_options = options; + + InitializeComponent(); + LoadResources(); + + if (AccessibilityImprovements.Level2) + { + // The outer pane + this.tableLayoutPanelOuter.AccessibleName = string.Empty; + + // The "Do you want to install this application?" question pane and inner controls + this.tableLayoutPanelQuestion.AccessibleName = string.Empty; + this.lblQuestion.AccessibleName = this.lblQuestion.Text; + this.pictureBoxQuestion.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_GlobeIcon); + this.pictureBoxQuestion.AccessibleRole = AccessibleRole.Graphic; + + // The application information pane and inner controls + this.tableLayoutPanelInfo.AccessibleName = string.Empty; + this.lblName.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_Name); + this.linkLblName.AccessibleName = this.linkLblName.Text; + this.lblFrom.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_From); + this.linkLblFromUrl.AccessibleName = this.linkLblFromUrl.Text; + this.lblPublisher.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_Publisher); + this.linkLblPublisher.AccessibleName = this.linkLblPublisher.Text; + + // The buttons pane and inner controls + this.tableLayoutPanelButtons.AccessibleName = string.Empty; + this.btnInstall.AccessibleName = StripOutAccelerator(this.btnInstall.Text); + this.btnCancel.AccessibleName = StripOutAccelerator(this.btnCancel.Text); + + // The security summary pane and inner controls + this.warningTextTableLayoutPanel.AccessibleName = string.Empty; + this.pictureBoxWarning.AccessibleName = this.pictureBoxWarning.AccessibleDescription; + this.pictureBoxWarning.AccessibleRole = AccessibleRole.Graphic; + this.linkLblMoreInformation.AccessibleName = this.linkLblMoreInformation.Text; + + // The line separator + this.lineLabel.AccessibleName = string.Empty; + + // Assigning accessible role to be in consistant with other dialogs. + this.lineLabel.AccessibleRole = AccessibleRole.Separator; + + // Re-order panes to fix Narrator's Scan Mode navigation + this.tableLayoutPanelOuter.Controls.SetChildIndex(this.tableLayoutPanelQuestion, 0); + this.tableLayoutPanelOuter.Controls.SetChildIndex(this.tableLayoutPanelInfo, 1); + this.tableLayoutPanelOuter.Controls.SetChildIndex(this.tableLayoutPanelButtons, 2); + this.tableLayoutPanelOuter.Controls.SetChildIndex(this.warningTextTableLayoutPanel, 3); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + controlToolTip = false; + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TrustManagerPromptUI)); + this.tableLayoutPanelOuter = new System.Windows.Forms.TableLayoutPanel(); + this.warningTextTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.pictureBoxWarning = new System.Windows.Forms.PictureBox(); + this.linkLblMoreInformation = new System.Windows.Forms.LinkLabel(); + this.tableLayoutPanelQuestion = new System.Windows.Forms.TableLayoutPanel(); + this.lblQuestion = new System.Windows.Forms.Label(); + this.pictureBoxQuestion = new System.Windows.Forms.PictureBox(); + this.tableLayoutPanelButtons = new System.Windows.Forms.TableLayoutPanel(); + this.btnInstall = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.tableLayoutPanelInfo = new System.Windows.Forms.TableLayoutPanel(); + this.lblName = new System.Windows.Forms.Label(); + this.lblFrom = new System.Windows.Forms.Label(); + this.lblPublisher = new System.Windows.Forms.Label(); + this.linkLblName = new System.Windows.Forms.LinkLabel(); + this.linkLblFromUrl = new System.Windows.Forms.LinkLabel(); + this.linkLblPublisher = new System.Windows.Forms.LinkLabel(); + this.lineLabel = new System.Windows.Forms.Label(); + this.toolTipFromUrl = new System.Windows.Forms.ToolTip(this.components); + this.tableLayoutPanelOuter.SuspendLayout(); + this.warningTextTableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxWarning)).BeginInit(); + this.tableLayoutPanelQuestion.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQuestion)).BeginInit(); + this.tableLayoutPanelButtons.SuspendLayout(); + this.tableLayoutPanelInfo.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanelOuter + // + resources.ApplyResources(this.tableLayoutPanelOuter, "tableLayoutPanelOuter"); + this.tableLayoutPanelOuter.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanelOuter.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 510F)); + this.tableLayoutPanelOuter.Controls.Add(this.warningTextTableLayoutPanel, 0, 4); + this.tableLayoutPanelOuter.Controls.Add(this.tableLayoutPanelQuestion, 0, 0); + this.tableLayoutPanelOuter.Controls.Add(this.tableLayoutPanelButtons, 0, 2); + this.tableLayoutPanelOuter.Controls.Add(this.tableLayoutPanelInfo, 0, 1); + this.tableLayoutPanelOuter.Controls.Add(this.lineLabel, 0, 3); + this.tableLayoutPanelOuter.Margin = new System.Windows.Forms.Padding(0, 0, 0, 12); + this.tableLayoutPanelOuter.Name = "tableLayoutPanelOuter"; + this.tableLayoutPanelOuter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelOuter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelOuter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelOuter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelOuter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // warningTextTableLayoutPanel + // + resources.ApplyResources(this.warningTextTableLayoutPanel, "warningTextTableLayoutPanel"); + this.warningTextTableLayoutPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.warningTextTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.warningTextTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.warningTextTableLayoutPanel.Controls.Add(this.pictureBoxWarning, 0, 0); + this.warningTextTableLayoutPanel.Controls.Add(this.linkLblMoreInformation, 1, 0); + this.warningTextTableLayoutPanel.Margin = new System.Windows.Forms.Padding(12, 6, 0, 0); + this.warningTextTableLayoutPanel.Name = "warningTextTableLayoutPanel"; + this.warningTextTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // pictureBoxWarning + // + resources.ApplyResources(this.pictureBoxWarning, "pictureBoxWarning"); + this.pictureBoxWarning.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.pictureBoxWarning.Name = "pictureBoxWarning"; + this.pictureBoxWarning.TabStop = false; + // + // linkLblMoreInformation + // + resources.ApplyResources(this.linkLblMoreInformation, "linkLblMoreInformation"); + this.linkLblMoreInformation.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.linkLblMoreInformation.Name = "linkLblMoreInformation"; + this.linkLblMoreInformation.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.TrustManagerPromptUI_ShowMoreInformation); + // + // tableLayoutPanelQuestion + // + resources.ApplyResources(this.tableLayoutPanelQuestion, "tableLayoutPanelQuestion"); + this.tableLayoutPanelQuestion.BackColor = System.Drawing.SystemColors.Window; + this.tableLayoutPanelQuestion.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanelQuestion.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 58F)); + this.tableLayoutPanelQuestion.Controls.Add(this.lblQuestion, 0, 0); + this.tableLayoutPanelQuestion.Controls.Add(this.pictureBoxQuestion, 1, 0); + this.tableLayoutPanelQuestion.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanelQuestion.Name = "tableLayoutPanelQuestion"; + this.tableLayoutPanelQuestion.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // lblQuestion + // + resources.ApplyResources(this.lblQuestion, "lblQuestion"); + this.lblQuestion.Margin = new System.Windows.Forms.Padding(12, 12, 12, 0); + this.lblQuestion.Name = "lblQuestion"; + // + // pictureBoxQuestion + // + resources.ApplyResources(this.pictureBoxQuestion, "pictureBoxQuestion"); + this.pictureBoxQuestion.Margin = new System.Windows.Forms.Padding(0); + this.pictureBoxQuestion.Name = "pictureBoxQuestion"; + this.pictureBoxQuestion.TabStop = false; + // + // tableLayoutPanelButtons + // + resources.ApplyResources(this.tableLayoutPanelButtons, "tableLayoutPanelButtons"); + this.tableLayoutPanelButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanelButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanelButtons.Controls.Add(this.btnInstall, 0, 0); + this.tableLayoutPanelButtons.Controls.Add(this.btnCancel, 1, 0); + this.tableLayoutPanelButtons.Margin = new System.Windows.Forms.Padding(0, 6, 12, 12); + this.tableLayoutPanelButtons.Name = "tableLayoutPanelButtons"; + this.tableLayoutPanelButtons.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // btnInstall + // + resources.ApplyResources(this.btnInstall, "btnInstall"); + this.btnInstall.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.btnInstall.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.btnInstall.MinimumSize = new System.Drawing.Size(75, 23); + this.btnInstall.Name = "btnInstall"; + this.btnInstall.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0); + // + // btnCancel + // + resources.ApplyResources(this.btnCancel, "btnCancel"); + this.btnCancel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.btnCancel.MinimumSize = new System.Drawing.Size(75, 23); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0); + // + // tableLayoutPanelInfo + // + resources.ApplyResources(this.tableLayoutPanelInfo, "tableLayoutPanelInfo"); + this.tableLayoutPanelInfo.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanelInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanelInfo.Controls.Add(this.lblName, 0, 0); + this.tableLayoutPanelInfo.Controls.Add(this.linkLblName, 0, 1); + this.tableLayoutPanelInfo.Controls.Add(this.lblFrom, 0, 2); + this.tableLayoutPanelInfo.Controls.Add(this.linkLblFromUrl, 0, 3); + this.tableLayoutPanelInfo.Controls.Add(this.lblPublisher, 0, 4); + this.tableLayoutPanelInfo.Controls.Add(this.linkLblPublisher, 0, 5); + this.tableLayoutPanelInfo.Margin = new System.Windows.Forms.Padding(30, 22, 12, 3); + this.tableLayoutPanelInfo.Name = "tableLayoutPanelInfo"; + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanelInfo.RowStyles.Add(new System.Windows.Forms.RowStyle()); + // + // lblName + // + resources.ApplyResources(this.lblName, "lblName"); + this.lblName.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.lblName.Name = "lblName"; + // + // lblFrom + // + resources.ApplyResources(this.lblFrom, "lblFrom"); + this.lblFrom.Margin = new System.Windows.Forms.Padding(0, 8, 3, 0); + this.lblFrom.Name = "lblFrom"; + // + // lblPublisher + // + resources.ApplyResources(this.lblPublisher, "lblPublisher"); + this.lblPublisher.Margin = new System.Windows.Forms.Padding(0, 8, 3, 0); + this.lblPublisher.Name = "lblPublisher"; + // + // linkLblName + // + resources.ApplyResources(this.linkLblName, "linkLblName"); + this.linkLblName.AutoEllipsis = true; + this.linkLblName.Margin = new System.Windows.Forms.Padding(3, 0, 3, 8); + this.linkLblName.Name = "linkLblName"; + this.linkLblName.TabStop = true; + this.linkLblName.UseMnemonic = false; + this.linkLblName.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.TrustManagerPromptUI_ShowSupportPage); + // + // linkLblFromUrl + // + resources.ApplyResources(this.linkLblFromUrl, "linkLblFromUrl"); + this.linkLblFromUrl.AutoEllipsis = true; + this.linkLblFromUrl.Margin = new System.Windows.Forms.Padding(3, 0, 3, 8); + this.linkLblFromUrl.Name = "linkLblFromUrl"; + this.linkLblFromUrl.TabStop = true; + this.linkLblFromUrl.UseMnemonic = false; + this.linkLblFromUrl.MouseEnter += new System.EventHandler(this.linkLblFromUrl_MouseEnter); + this.linkLblFromUrl.MouseLeave += new System.EventHandler(this.linkLblFromUrl_MouseLeave); + // + // linkLblPublisher + // + resources.ApplyResources(this.linkLblPublisher, "linkLblPublisher"); + this.linkLblPublisher.AutoEllipsis = true; + this.linkLblPublisher.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.linkLblPublisher.Name = "linkLblPublisher"; + this.linkLblPublisher.TabStop = true; + this.linkLblPublisher.UseMnemonic = false; + this.linkLblPublisher.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.TrustManagerPromptUI_ShowPublisherCertificate); + // + // lineLabel + // + resources.ApplyResources(this.lineLabel, "lineLabel"); + this.lineLabel.BackColor = System.Drawing.SystemColors.ControlDark; + this.lineLabel.Margin = new System.Windows.Forms.Padding(0); + this.lineLabel.Name = "lineLabel"; + // + // TrustManagerPromptUI + // + this.AcceptButton = this.btnCancel; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.CancelButton = this.btnCancel; + this.Controls.Add(this.tableLayoutPanelOuter); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TrustManagerPromptUI"; + + // Bug# 398538 - Explicitly setting RighToLeft property for RightToLeft cultures at runtime for TrustManager dialog. + if (SR.GetString(SR.RTL) != "RTL_False") + { + this.RightToLeft = RightToLeft.Yes; + this.RightToLeftLayout = true; + } + + this.VisibleChanged += new System.EventHandler(this.TrustManagerPromptUI_VisibleChanged); + this.Load += new System.EventHandler(this.TrustManagerPromptUI_Load); + this.tableLayoutPanelOuter.ResumeLayout(false); + this.tableLayoutPanelOuter.PerformLayout(); + this.warningTextTableLayoutPanel.ResumeLayout(false); + this.warningTextTableLayoutPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxWarning)).EndInit(); + this.tableLayoutPanelQuestion.ResumeLayout(false); + this.tableLayoutPanelQuestion.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQuestion)).EndInit(); + this.tableLayoutPanelButtons.ResumeLayout(false); + this.tableLayoutPanelButtons.PerformLayout(); + this.tableLayoutPanelInfo.ResumeLayout(false); + this.tableLayoutPanelInfo.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + } + + + [ + SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity") + ] + private void LoadGlobeBitmap() + { + Bitmap bitmap; + lock (typeof(System.Windows.Forms.Form)) + { + if (!LocalAppContextSwitches.UseLegacyImages) + { + var globeIcon = new Icon(typeof(System.Windows.Forms.Form), "TrustManagerGlobe.ico"); + bitmap = globeIcon.ToBitmap(); + } + else + { + Bitmap globeBmp = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerGlobe.bmp"); + this.ScaleBitmapLogicalToDevice(ref globeBmp); + bitmap = globeBmp; + } + } + if (bitmap != null) + { + bitmap.MakeTransparent(); + this.pictureBoxQuestion.Image = bitmap; + } + } + + [ + SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // OK to use " " + ] + private void LoadResources() + { + SuspendAllLayout(this); + + LoadGlobeBitmap(); + + UpdateFonts(); + + // Pick the buttons + if ((this.m_options & TrustManagerPromptOptions.StopApp) != 0) + { + this.btnInstall.Visible = false; + this.btnCancel.Text = SR.GetString(SR.TrustManagerPromptUI_Close); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.OK; + } + else + { + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.btnCancel.Text = SR.GetString(SR.TrustManagerPromptUI_DoNotInstall); + } + else + { + this.btnCancel.Text = SR.GetString(SR.TrustManagerPromptUI_DoNotRun); + } + this.btnInstall.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + + // Set the links + this.linkLblName.Links.Clear(); + this.linkLblPublisher.Links.Clear(); + this.linkLblFromUrl.Links.Clear(); + this.linkLblMoreInformation.Links.Clear(); + + this.linkLblName.Text = this.m_appName; + if (this.m_defaultBrowserExePath != null && + this.m_certificate != null && + this.m_supportUrl != null && + this.m_supportUrl.Length > 0) + { + this.linkLblName.Links.Add(0, this.m_appName.Length, this.m_supportUrl); + } + if (this.linkLblName.Links.Count == 0) + { + // Since there is no link to activate, remove the potential accelerator + this.lblName.Text = StripOutAccelerator(this.lblName.Text); + } + + this.linkLblFromUrl.Text = this.m_deploymentUrl; + + if (this.m_publisherName == null) + { + this.linkLblPublisher.Text = SR.GetString(SR.TrustManagerPromptUI_UnknownPublisher); + if (this.m_certificate != null) + { + this.linkLblPublisher.Links.Add(0, this.linkLblPublisher.Text.Length); + } + } + else + { + this.linkLblPublisher.Text = this.m_publisherName; + Debug.Assert(this.m_certificate != null); + if (this.m_publisherName.Length > 0) + { + this.linkLblPublisher.Links.Add(0, this.m_publisherName.Length); + } + } + if (this.linkLblPublisher.Links.Count == 0) + { + // Since there is no link to activate, remove the potential accelerator + this.lblPublisher.Text = StripOutAccelerator(this.lblPublisher.Text); + } + + // Choose a title + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.Text = SR.GetString(SR.TrustManagerPromptUI_InstallTitle); + } + else + { + this.Text = SR.GetString(SR.TrustManagerPromptUI_RunTitle); + this.btnInstall.Text = SR.GetString(SR.TrustManagerPromptUI_Run); + } + + // Choose a question + if ((this.m_options & TrustManagerPromptOptions.StopApp) != 0) + { + this.lblQuestion.Text = SR.GetString(SR.TrustManagerPromptUI_BlockedApp); + } + else if (this.m_publisherName == null) + { + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.lblQuestion.Text = SR.GetString(SR.TrustManagerPromptUI_NoPublisherInstallQuestion); + } + else + { + this.lblQuestion.Text = SR.GetString(SR.TrustManagerPromptUI_NoPublisherRunQuestion); + } + } + else if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.lblQuestion.Text = SR.GetString(SR.TrustManagerPromptUI_InstallQuestion); + } + else + { + this.lblQuestion.Text = SR.GetString(SR.TrustManagerPromptUI_RunQuestion); + } + + // Choose warning text + if ((this.m_options & TrustManagerPromptOptions.StopApp) != 0) + { + // App is blocked - do not display a More Information link + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_InstalledAppBlockedWarning); + } + else + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_RunAppBlockedWarning); + } + this.linkLblMoreInformation.TabStop = false; + this.linkLblMoreInformation.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_WarningAccessibleDescription); + this.linkLblMoreInformation.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_WarningAccessibleName); + } + else + { + string strMoreInfo = SR.GetString(SR.TrustManagerPromptUI_MoreInformation); + + if ((this.m_options & TrustManagerPromptOptions.LocalComputerSource) != 0) + { + // App comes from local machine + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_InstallFromLocalMachineWarning, strMoreInfo); + } + else + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_RunFromLocalMachineWarning, strMoreInfo); + } + } + else if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_InstallWarning, strMoreInfo); + } + else + { + this.linkLblMoreInformation.Text = SR.GetString(SR.TrustManagerPromptUI_RunWarning, strMoreInfo); + } + this.linkLblMoreInformation.TabStop = true; + this.linkLblMoreInformation.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_MoreInformationAccessibleDescription); + this.linkLblMoreInformation.AccessibleName = SR.GetString(SR.TrustManagerPromptUI_MoreInformationAccessibleName); + this.linkLblMoreInformation.Links.Add(new System.Windows.Forms.LinkLabel.Link(this.linkLblMoreInformation.Text.Length - strMoreInfo.Length, strMoreInfo.Length)); + } + + // Choose the warning icon and its accessible description + if ((this.m_options & TrustManagerPromptOptions.StopApp) != 0 || this.m_publisherName == null) + { + if ((this.m_options & TrustManagerPromptOptions.RequiresPermissions) == 0 && + (this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + LoadWarningBitmap(TrustManagerWarningLevel.Yellow); + } + else + { + LoadWarningBitmap(TrustManagerWarningLevel.Red); + } + } + else if ((this.m_options & TrustManagerPromptOptions.RequiresPermissions) == 0) + { + Debug.Assert(m_certificate != null); + LoadWarningBitmap(TrustManagerWarningLevel.Green); + } + else + { + LoadWarningBitmap(TrustManagerWarningLevel.Yellow); + } + + // Set the context sensitive accessible descriptions + if ((this.m_options & TrustManagerPromptOptions.StopApp) != 0) + { + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_InstallBlocked); + } + else + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_RunBlocked); + } + } + else if ((this.m_options & TrustManagerPromptOptions.RequiresPermissions) != 0) + { + if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_InstallWithElevatedPermissions); + } + else + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_RunWithElevatedPermissions); + } + } + else if ((this.m_options & TrustManagerPromptOptions.AddsShortcut) != 0) + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_InstallConfirmation); + } + else + { + this.AccessibleDescription = SR.GetString(SR.TrustManagerPromptUI_AccessibleDescription_RunConfirmation); + } + + ResumeAllLayout(this, true); + } + + [ + SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity") + ] + private void LoadWarningBitmap(TrustManagerWarningLevel warningLevel) + { + Bitmap bitmap; + switch (warningLevel) + { + case TrustManagerWarningLevel.Green: + if (!LocalAppContextSwitches.UseLegacyImages) + { + var icon = new Icon(typeof(System.Windows.Forms.Form), "TrustManagerOK.ico"); + bitmap = icon.ToBitmap(); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerOK.bmp"); + } + this.pictureBoxWarning.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_LowRisk), this.pictureBoxWarning.AccessibleDescription); + break; + case TrustManagerWarningLevel.Yellow: + if (!LocalAppContextSwitches.UseLegacyImages) + { + var icon = new Icon(typeof(System.Windows.Forms.Form), "TrustManagerWarning.ico"); + bitmap = icon.ToBitmap(); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerWarning.bmp"); + } + this.pictureBoxWarning.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_MediumRisk), this.pictureBoxWarning.AccessibleDescription); + break; + default: + Debug.Assert(warningLevel == TrustManagerWarningLevel.Red); + if (!LocalAppContextSwitches.UseLegacyImages) + { + var icon = new Icon(typeof(System.Windows.Forms.Form), "TrustManagerHighRisk.ico"); + bitmap = icon.ToBitmap(); + } + else + { + bitmap = new Bitmap(typeof(System.Windows.Forms.Form), "TrustManagerHighRisk.bmp"); + } + this.pictureBoxWarning.AccessibleDescription = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TrustManager_WarningIconAccessibleDescription_HighRisk), this.pictureBoxWarning.AccessibleDescription); + break; + } + if (bitmap != null) + { + bitmap.MakeTransparent(); + this.pictureBoxWarning.Image = bitmap; + } + } + + private static string StripOutAccelerator(string text) + { + // &Name: ==> Name: + // Name(&N): ==> Name: + int ampIndex = text.IndexOf('&'); + if (ampIndex != -1) + { + if (ampIndex > 0 && text[ampIndex - 1] == '(' && + text.Length > ampIndex + 2 && text[ampIndex + 2] == ')') + { + return text.Remove(ampIndex - 1, 4); + } + else + { + return text.Replace("&", ""); + } + } + return text; + } + + private void TrustManagerPromptUI_Load(object sender, EventArgs e) + { + this.ActiveControl = this.btnCancel; + } + + private void linkLblFromUrl_MouseEnter(object sender, EventArgs e) + { + if (!controlToolTip && toolTipFromUrl != null) + { + IntSecurity.AllWindows.Assert(); + try + { + controlToolTip = true; + toolTipFromUrl.Show(linkLblFromUrl.Text, linkLblFromUrl); + UnsafeNativeMethods.SendMessage(new HandleRef(toolTipFromUrl, toolTipFromUrl.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, 600); + } + finally + { + System.Security.CodeAccessPermission.RevertAssert(); + controlToolTip = false; + } + } + } + + private void linkLblFromUrl_MouseLeave(object sender, EventArgs e) + { + if (!controlToolTip && toolTipFromUrl != null && toolTipFromUrl.GetHandleCreated()) + { + toolTipFromUrl.RemoveAll(); + IntSecurity.AllWindows.Assert(); + try + { + toolTipFromUrl.Hide(this); + } + finally + { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") // Intentionally eating exceptions + ] + private void TrustManagerPromptUI_ShowMoreInformation(System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + try + { + using (TrustManagerMoreInformation moreInformation = new TrustManagerMoreInformation(m_options, m_publisherName)) + { + moreInformation.ShowDialog(this); + } + } + catch (Exception ex) + { + Debug.Fail("Error occurred while showing More Information: " + ex.Message); + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") // Intentionally eating exceptions + ] + private void TrustManagerPromptUI_ShowPublisherCertificate(System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + try + { + Debug.Assert(m_certificate != null); + X509Certificate2UI.DisplayCertificate(m_certificate, this.Handle); + } + catch (Exception ex) + { + Debug.Fail("Couldn't display certificate: " + ex.Message); + } + } + + [ + SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes") // Intentionally eating exceptions + ] + private void TrustManagerPromptUI_ShowSupportPage(System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + try + { + Debug.Assert(this.m_defaultBrowserExePath != null); + Debug.Assert(this.m_certificate != null); + System.Diagnostics.Process.Start(this.m_defaultBrowserExePath, e.Link.LinkData.ToString()); + } + catch (Exception ex) + { + Debug.Fail("Couldn't launch support Url: " + ex.Message); + } + } + + private void TrustManagerPromptUI_VisibleChanged(System.Object sender, System.EventArgs e) + { + if (this.Visible && Form.ActiveForm != this) + { + // Make this form the active form of the application + this.Activate(); + // Give focus to the Cancel/OK button + this.ActiveControl = this.btnCancel; + } + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + } + + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category == UserPreferenceCategory.Window) + { + UpdateFonts(); + } + Invalidate(); // Workaround a bug where the form's background does not repaint properly + } + + private void UpdateFonts() + { + // Choose the fonts. + this.Font = SystemFonts.MessageBoxFont; + this.lblQuestion.Font = + this.lblPublisher.Font = + this.lblFrom.Font = + this.lblName.Font = new Font(this.Font, FontStyle.Bold); + + // Make sure the link labels don't wrap. + this.linkLblPublisher.MaximumSize = + this.linkLblFromUrl.MaximumSize = + this.linkLblName.MaximumSize = new Size(0, this.Font.Height + 2); + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TypeValidationEventArgs.cs b/WindowsForms/Managed/System/WinForms/TypeValidationEventArgs.cs new file mode 100644 index 000000000..f157dd66c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TypeValidationEventArgs.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// TypeValidationEventArgs. Provides data for the TypeValidationEventHandler event. + /// + public class TypeValidationEventArgs : EventArgs + { + private Type validatingType; + private string message; + private bool isValidInput; + private object returnValue; + private bool cancel; + + public TypeValidationEventArgs(Type validatingType, bool isValidInput, object returnValue, string message) + { + this.validatingType = validatingType; + this.isValidInput = isValidInput; + this.returnValue = returnValue; + this.message = message; + } + + /// + /// Specifies whether focus should be allowed to be shifted from the control. + /// + public bool Cancel + { + get + { + return this.cancel; + } + set + { + this.cancel = value; + } + } + + /// + /// The exception thrown by the validating object while performing the data validation. + /// + public bool IsValidInput + { + get + { + return this.isValidInput; + } + } + + /// + /// A message about the validation operation. Intended to be populated with an exception information if + /// any thrown. + /// + public string Message + { + get + { + return this.message; + } + } + + /// + /// The value returned from the Parse method. + /// + public object ReturnValue + { + get + { + return this.returnValue; + } + } + + /// + /// The position where the test failed the mask constraint. + /// + public Type ValidatingType + { + get + { + return this.validatingType; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/TypeValidationEventHandler.cs b/WindowsForms/Managed/System/WinForms/TypeValidationEventHandler.cs new file mode 100644 index 000000000..889c567da --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/TypeValidationEventHandler.cs @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + /// + /// Describes a delegate for an event that has a TypeValidationEventArgs as a parameter. + /// + public delegate void TypeValidationEventHandler(object sender, TypeValidationEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/UICuesEvent.cs b/WindowsForms/Managed/System/WinForms/UICuesEvent.cs new file mode 100644 index 000000000..0c53ac5cb --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UICuesEvent.cs @@ -0,0 +1,131 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + /// + /// + /// + /// Specifies UI state. + /// + /// + [Flags] + public enum UICues { + + /// + /// + /// Focus rectangles are shown after the change. + /// + ShowFocus = 0x01, + + /// + /// + /// Keyboard cues are underlined after the change. + /// + ShowKeyboard = 0x02, + + /// + /// + /// [To be supplied.] + /// + Shown = ShowFocus | ShowKeyboard, + + /// + /// + /// The state of the focus cues has changed. + /// + ChangeFocus = 0x04, + + /// + /// + /// The state of the keyboard cues has changed. + /// + ChangeKeyboard = 0x08, + + /// + /// + /// [To be supplied.] + /// + Changed = ChangeFocus | ChangeKeyboard, + + /// + /// + /// [To be supplied.] + /// + None = 0x00, + } + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class UICuesEventArgs : EventArgs { + + private readonly UICues uicues; + + /// + /// + /// [To be supplied.] + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public UICuesEventArgs(UICues uicues) { + this.uicues = uicues; + } + + /// + /// + /// Focus rectangles are shown after the change. + /// + public bool ShowFocus { + get { + return (uicues & UICues.ShowFocus) != 0; + } + } + + /// + /// + /// Keyboard cues are underlined after the change. + /// + public bool ShowKeyboard { + get { + return (uicues & UICues.ShowKeyboard) != 0; + } + } + + /// + /// + /// The state of the focus cues has changed. + /// + public bool ChangeFocus { + get { + return (uicues & UICues.ChangeFocus) != 0; + } + } + + /// + /// + /// The state of the keyboard cues has changed. + /// + public bool ChangeKeyboard { + get { + return (uicues & UICues.ChangeKeyboard) != 0; + } + } + + /// + /// + /// [To be supplied.] + /// + public UICues Changed { + get { + return (uicues & UICues.Changed); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/UICuesEventHandler.cs b/WindowsForms/Managed/System/WinForms/UICuesEventHandler.cs new file mode 100644 index 000000000..8aa70e92e --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UICuesEventHandler.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + /// + /// + /// + /// Represents a method that will handle the event of a . + /// + /// + public delegate void UICuesEventHandler(object sender, UICuesEventArgs e); +} diff --git a/WindowsForms/Managed/System/WinForms/UnsafeNativeMethods.cs b/WindowsForms/Managed/System/WinForms/UnsafeNativeMethods.cs new file mode 100644 index 000000000..a3f73f006 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UnsafeNativeMethods.cs @@ -0,0 +1,9014 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; + +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32[]):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.IntPtr):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Object&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Object&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Text.StringBuilder):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+CHARFORMAT2A):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+CHARFORMATA):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+CHARFORMATW):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+CHARRANGE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+EDITSTREAM):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+EDITSTREAM64):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+FINDTEXT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+HDLAYOUT&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LOGFONT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVBKIMAGE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVCOLUMN):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVCOLUMN_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVFINDINFO&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVGROUP):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVHITTESTINFO&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVHITTESTINFO&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVHITTESTINFO):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVINSERTMARK):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVINSERTMARK):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVITEM&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVTILEVIEWINFO):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVTILEVIEWINFO):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+MCHITTESTINFO):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+MSG):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+MSG):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+PARAFORMAT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+POINT):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+RECT&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+REPASTESPECIAL):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+SIZE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+SYSTEMTIME):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+SYSTEMTIMEARRAY):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TBBUTTON&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TBBUTTONINFO&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TCITEM_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TEXTRANGE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TOOLINFO_T):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TV_HITTESTINFO):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TV_INSERTSTRUCT&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TV_ITEM&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Runtime.InteropServices.HandleRef,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+GETTEXTLENGTHEX,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Drawing.Rectangle&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+POINT,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PostMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.IntPtr):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PostMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.IntPtr):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods..ctor()")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+SMsoComponentManager..ctor()")] + +// Verified that current call stacks that use these don't expose them in an unsafe fashion. +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.ClipCursor(System.Windows.Forms.NativeMethods+COMRECT):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.ClipCursor(System.Windows.Forms.NativeMethods+RECT&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetClientRect(System.Runtime.InteropServices.HandleRef,System.IntPtr):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetProcAddress(System.Runtime.InteropServices.HandleRef,System.String):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetRegionData(System.Runtime.InteropServices.HandleRef,System.Int32,System.IntPtr):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalFree(System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalLock(System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalUnlock(System.Runtime.InteropServices.HandleRef):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IsChild(System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+HDLAYOUT&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.ImmNotifyIME(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetAncestor(System.Runtime.InteropServices.HandleRef,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetFocus():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SetFocus(System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CoCreateInstance(System.Guid&,System.Object,System.Int32,System.Guid&):System.Object")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CoRegisterMessageFilter(System.Runtime.InteropServices.HandleRef,System.IntPtr&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(System.Windows.Forms.NativeMethods+MSG&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.EnableScrollBar(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.EnumThreadWindows(System.Int32,System.Windows.Forms.NativeMethods+EnumThreadWindowsCallback,System.Runtime.InteropServices.HandleRef):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetForegroundWindow():System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetMenu(System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetMenuItemInfo(System.Runtime.InteropServices.HandleRef,System.Int32,System.Boolean,System.Windows.Forms.NativeMethods+MENUITEMINFO_T_RW):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetMessageA(System.Windows.Forms.NativeMethods+MSG&,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetMessageW(System.Windows.Forms.NativeMethods+MSG&,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetObject(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+BITMAP):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetShortPathName(System.String,System.String,System.UInt32):System.UInt32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetSystemPowerStatus(System.Windows.Forms.NativeMethods+SYSTEM_POWER_STATUS&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetTempFileName(System.String,System.String,System.Int32,System.Text.StringBuilder):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetWindowText(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCloseHandle(System.Runtime.InteropServices.HandleRef):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCreateCompatibleDC(System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntCreateIC(System.String,System.String,System.String,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntDuplicateHandle(System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.IntPtr&,System.Int32,System.Boolean,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IntOleInitialize(System.Int32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.IsDialogMessage(System.Runtime.InteropServices.HandleRef,System.Windows.Forms.NativeMethods+MSG&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleUninitialize():System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.PostThreadMessage(System.Int32,System.Int32,System.IntPtr,System.IntPtr):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Object&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+CHARFORMAT2A):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+EDITSTREAM64):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVBKIMAGE):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVGROUP):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVINSERTMARK):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+LVTILEVIEWINFO):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Windows.Forms.NativeMethods+TOOLINFO_TOOLTIP):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Runtime.InteropServices.HandleRef,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+GETTEXTLENGTHEX,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef,System.Int32,System.Windows.Forms.NativeMethods+POINT,System.Windows.Forms.NativeMethods+LVINSERTMARK):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SystemParametersInfo(System.Int32,System.Int32,System.IntPtr[],System.Int32):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.TranslateMessage(System.Windows.Forms.NativeMethods+MSG&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.WaitMessage():System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetShortPathName(System.String,System.Text.StringBuilder,System.UInt32):System.UInt32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetWindowLong32(System.Runtime.InteropServices.HandleRef,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GetWindowLongPtr64(System.Runtime.InteropServices.HandleRef,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.OleCreateIPictureIndirect(System.Object,System.Guid&,System.Boolean):System.Windows.Forms.UnsafeNativeMethods+IPicture")] + +// Verified that current call stacks that use this seem to be safe. +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+ThemingScope.GetCurrentActCtx(System.IntPtr&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+ThemingScope.ActivateActCtx(System.IntPtr,System.IntPtr&):System.Boolean")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+ThemingScope.CreateActCtx(System.Windows.Forms.UnsafeNativeMethods+ThemingScope+ACTCTX&):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+ThemingScope.DeactivateActCtx(System.Int32,System.IntPtr):System.Boolean")] + +// SEC +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemory(System.Runtime.InteropServices.HandleRef,System.Runtime.InteropServices.HandleRef,System.Int32):System.Void")] + +// SEC +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalAlloc(System.Int32,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.GlobalReAlloc(System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemoryW(System.IntPtr,System.String,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemoryW(System.IntPtr,System.Char[],System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemoryA(System.IntPtr,System.String,System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.CopyMemory(System.IntPtr,System.Byte[],System.Int32):System.Void")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.WideCharToMultiByte(System.Int32,System.Int32,System.String,System.Int32,System.Byte[],System.Int32,System.IntPtr,System.IntPtr):System.Int32")] + +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SetWindowLongPtr64(System.Runtime.InteropServices.HandleRef,System.Int32,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SetWindowLongPtr32(System.Runtime.InteropServices.HandleRef,System.Int32,System.Runtime.InteropServices.HandleRef):System.IntPtr")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods.SHLoadIndirectString(System.String,System.Text.StringBuilder,System.UInt32,System.IntPtr):System.UInt32")] + +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+Shell32.SHCreateShellItem(System.IntPtr,System.IntPtr,System.IntPtr,System.Windows.Forms.FileDialogNative+IShellItem&):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+Shell32.SHGetFolderPathExPrivate(System.Guid&,System.UInt32,System.IntPtr,System.Text.StringBuilder,System.UInt32):System.Int32")] +[assembly: SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage", Scope="member", Target="System.Windows.Forms.UnsafeNativeMethods+Shell32.SHILCreateFromPath(System.String,System.IntPtr&,System.UInt32&):System.Int32")] + +namespace System.Windows.Forms { + + using Accessibility; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.ConstrainedExecution; + using System; + using System.Security.Permissions; + using System.Collections; + using System.IO; + using System.Text; + using System.Security; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Drawing; + + using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + + [ + SuppressUnmanagedCodeSecurity() + ] + internal static class UnsafeNativeMethods { + + [DllImport(ExternDll.Shlwapi, CharSet=CharSet.Unicode, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + internal static extern uint SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved); + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.None)] + public static extern int ReadClassStg(HandleRef pStg, [In, Out] ref Guid pclsid); + + [DllImport(ExternDll.Ole32, SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true)] + internal extern static void CoTaskMemFree(IntPtr pv); + + [DllImport(ExternDll.User32)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetClassName(HandleRef hwnd, StringBuilder lpClassName, int nMaxCount); + + //SetClassLong won't work correctly for 64-bit: we should use SetClassLongPtr instead. On + //32-bit, SetClassLongPtr is just #defined as SetClassLong. SetClassLong really should + //take/return int instead of IntPtr/HandleRef, but since we're running this only for 32-bit + //it'll be OK. + public static IntPtr SetClassLong(HandleRef hWnd, int nIndex, IntPtr dwNewLong) + { + if (IntPtr.Size == 4) + { + return SetClassLongPtr32(hWnd, nIndex, dwNewLong); + } + return SetClassLongPtr64(hWnd, nIndex, dwNewLong); + } + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "SetClassLong")] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetClassLongPtr32(HandleRef hwnd, int nIndex, IntPtr dwNewLong); + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "SetClassLongPtr")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetClassLongPtr64(HandleRef hwnd, int nIndex, IntPtr dwNewLong); + + [DllImport(ExternDll.Ole32, ExactSpelling=true, PreserveSig=false)] + [ResourceExposure(ResourceScope.Process)] + public static extern UnsafeNativeMethods.IClassFactory2 CoGetClassObject( + [In] + ref Guid clsid, + int dwContext, + int serverInfo, + [In] + ref Guid refiid); + + [return: MarshalAs(UnmanagedType.Interface)][DllImport(ExternDll.Ole32, ExactSpelling=true, PreserveSig=false)] + [ResourceExposure(ResourceScope.Process)] + public static extern object CoCreateInstance( + [In] + ref Guid clsid, + [MarshalAs(UnmanagedType.Interface)] + object punkOuter, + int context, + [In] + ref Guid iid); + + //This marshals differently than NativeMethods.POINTSTRUCT + internal struct POINTSTRUCT { + public int x; + public int y; + + public POINTSTRUCT(int x, int y) { + this.x = x; + this.y = y; + } + } + + // We need this for Vista specific features/fixes + private static readonly Version VistaOSVersion = new Version(6, 0); + + /// + /// Used to tell if Vista API's are supported + /// + internal static bool IsVista + { + get + { + OperatingSystem os = Environment.OSVersion; + if (os == null) + return false; + + return (os.Platform == PlatformID.Win32NT) && + (os.Version.CompareTo(VistaOSVersion) >= 0); + } + } + + [DllImport(ExternDll.Kernel32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.AppDomain)] + public static extern int GetLocaleInfo(int Locale,int LCType,StringBuilder lpLCData,int cchData) ; + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.None)] + public static extern int WriteClassStm(IStream pStream, ref Guid clsid); + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.Machine)] + public static extern int ReadClassStg(IStorage pStorage, [Out]out Guid clsid); + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.Machine)] + public static extern int ReadClassStm(IStream pStream, [Out]out Guid clsid); + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.Machine)] + public static extern int OleLoadFromStream(IStream pStorage, ref Guid iid, out IOleObject pObject); + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.None)] + public static extern int OleSaveToStream(IPersistStream pPersistStream, IStream pStream); + + [DllImport(ExternDll.Ole32)] + [ResourceExposure(ResourceScope.None)] + public static extern int CoGetMalloc(int dwReserved, out IMalloc pMalloc); + + /* Commenting this out until someone actually needs to use it again... + [DllImport(ExternDll.Ole32)] + public static extern int OleSetMenuDescriptor(IntPtr hOleMenu, IntPtr hWndFrame, IntPtr hWndActiveObject, IOleInPlaceFrame frame, IOleInPlaceActiveObject activeObject); + */ + + /* Commenting this out until someone actually needs to use it again... + [DllImport(ExternDll.Kernel32)] + public static extern bool IsBadReadPtr(HandleRef ptr, int size); + */ + + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PageSetupDlg([In, Out] NativeMethods.PAGESETUPDLG lppsd); + + [DllImport(ExternDll.Comdlg32, EntryPoint = "PrintDlg", SetLastError = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PrintDlg_32([In, Out] NativeMethods.PRINTDLG_32 lppd); + + [DllImport(ExternDll.Comdlg32, EntryPoint = "PrintDlg", SetLastError = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PrintDlg_64([In, Out] NativeMethods.PRINTDLG_64 lppd); + + public static bool PrintDlg([In, Out] NativeMethods.PRINTDLG lppd) { + if (IntPtr.Size == 4) { + NativeMethods.PRINTDLG_32 lppd_32 = lppd as NativeMethods.PRINTDLG_32; + if (lppd_32 == null) { + throw new System.NullReferenceException("PRINTDLG data is null"); + } + return PrintDlg_32(lppd_32); + } + else { + NativeMethods.PRINTDLG_64 lppd_64 = lppd as NativeMethods.PRINTDLG_64; + if (lppd_64 == null) { + throw new System.NullReferenceException("PRINTDLG data is null"); + } + return PrintDlg_64(lppd_64); + } + } + + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int PrintDlgEx([In, Out] NativeMethods.PRINTDLGEX lppdex); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int OleGetClipboard(ref IComDataObject data); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int OleSetClipboard(IComDataObject pDataObj); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int OleFlushClipboard(); + [DllImport(ExternDll.Oleaut32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern void OleCreatePropertyFrameIndirect(NativeMethods.OCPFIPARAMS p); + [DllImport(ExternDll.Oleaut32, EntryPoint = "OleCreateFontIndirect", ExactSpelling = true, PreserveSig = false)] + [ResourceExposure(ResourceScope.Process)] + public static extern UnsafeNativeMethods.IFont OleCreateIFontIndirect(NativeMethods.FONTDESC fd, ref Guid iid); + [DllImport(ExternDll.Oleaut32, EntryPoint = "OleCreatePictureIndirect", ExactSpelling = true, PreserveSig = false)] + [ResourceExposure(ResourceScope.Machine)] + public static extern UnsafeNativeMethods.IPicture OleCreateIPictureIndirect([MarshalAs(UnmanagedType.AsAny)]object pictdesc, ref Guid iid, bool fOwn); + [DllImport(ExternDll.Oleaut32, EntryPoint = "OleCreatePictureIndirect", ExactSpelling = true, PreserveSig = false)] + [ResourceExposure(ResourceScope.Machine)] + public static extern UnsafeNativeMethods.IPictureDisp OleCreateIPictureDispIndirect([MarshalAs(UnmanagedType.AsAny)] object pictdesc, ref Guid iid, bool fOwn); + // cpb: #8309 -- next two methods, refiid arg must be IPicture.iid + [DllImport(ExternDll.Oleaut32, PreserveSig = false)] + [ResourceExposure(ResourceScope.Machine)] + public static extern UnsafeNativeMethods.IPicture OleCreatePictureIndirect(NativeMethods.PICTDESC pictdesc, [In]ref Guid refiid, bool fOwn); + [DllImport(ExternDll.Oleaut32, PreserveSig = false)] + [ResourceExposure(ResourceScope.Process)] + public static extern UnsafeNativeMethods.IFont OleCreateFontIndirect(NativeMethods.tagFONTDESC fontdesc, [In]ref Guid refiid); + [DllImport(ExternDll.Oleaut32, ExactSpelling = true)] + [ResourceExposure(ResourceScope.None)] + public static extern int VarFormat(ref object pvarIn, HandleRef pstrFormat, int iFirstDay, int iFirstWeek, uint dwFlags, [In, Out]ref IntPtr pbstr); + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int DragQueryFile(HandleRef hDrop, int iFile, StringBuilder lpszFile, int cch); + + public static int DragQueryFileLongPath(HandleRef hDrop, int iFile, StringBuilder lpszFile) + { + if (null != lpszFile && 0 != lpszFile.Capacity && iFile != unchecked((int)0xFFFFFFFF)) + { + int resultValue = 0; + + // iterating by allocating chunk of memory each time we find the length is not sufficient. + // Performance should not be an issue for current MAX_PATH length due to this + if ((resultValue = DragQueryFile(hDrop, iFile, lpszFile, lpszFile.Capacity)) == lpszFile.Capacity) + { + // passing null for buffer will return actual number of charectors in the file name. + // So, one extra call would be suffice to avoid while loop in case of long path. + int capacity = DragQueryFile(hDrop, iFile, null, 0); + if (capacity < NativeMethods.MAX_UNICODESTRING_LEN) + { + lpszFile.EnsureCapacity(capacity); + resultValue = DragQueryFile(hDrop, iFile, lpszFile, capacity); + } + else + { + resultValue = 0; + } + } + + lpszFile.Length = resultValue; + return resultValue; // what ever the result. + } + else + { + return DragQueryFile(hDrop, iFile, lpszFile, lpszFile.Capacity); + } + } + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnumChildWindows(HandleRef hwndParent, NativeMethods.EnumChildrenCallback lpEnumFunc, HandleRef lParam); + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ShellExecute(HandleRef hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto, EntryPoint="ShellExecute", BestFitMapping = false)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ShellExecute_NoBFM(HandleRef hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetScrollPos(HandleRef hWnd, int nBar, int nPos, bool bRedraw); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnableScrollBar(HandleRef hWnd, int nBar, int value); + + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int Shell_NotifyIcon(int message, NativeMethods.NOTIFYICONDATA pnid); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool InsertMenuItem(HandleRef hMenu, int uItem, bool fByPosition, NativeMethods.MENUITEMINFO_T lpmii); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetMenu(HandleRef hWnd); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetMenuItemInfo(HandleRef hMenu, int uItem, bool fByPosition, [In, Out] NativeMethods.MENUITEMINFO_T lpmii); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetMenuItemInfo(HandleRef hMenu, int uItem, bool fByPosition, [In, Out] NativeMethods.MENUITEMINFO_T_RW lpmii); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool SetMenuItemInfo(HandleRef hMenu, int uItem, bool fByPosition, NativeMethods.MENUITEMINFO_T lpmii); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="CreateMenu", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateMenu(); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateMenu() { + return System.Internal.HandleCollector.Add(IntCreateMenu(), NativeMethods.CommonHandles.Menu); + } + + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetOpenFileName([In, Out] NativeMethods.OPENFILENAME_I ofn); + + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EndDialog(HandleRef hWnd, IntPtr result); + + public const int MB_PRECOMPOSED = 0x00000001; + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Unicode, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern int MultiByteToWideChar(int CodePage, int dwFlags, byte[] lpMultiByteStr, int cchMultiByte, char[] lpWideCharStr, int cchWideChar); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern int WideCharToMultiByte(int codePage, int flags, [MarshalAs(UnmanagedType.LPWStr)]string wideStr, int chars, [In,Out]byte[] pOutBytes, int bufferBytes, IntPtr defaultChar, IntPtr pDefaultUsed); + + [DllImport(ExternDll.Kernel32, SetLastError=true, ExactSpelling=true, EntryPoint="RtlMoveMemory", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemory(HandleRef destData, HandleRef srcData, int size); + [DllImport(ExternDll.Kernel32, ExactSpelling=true, EntryPoint="RtlMoveMemory")] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemory(IntPtr pdst, byte[] psrc, int cb); + + [DllImport(ExternDll.Kernel32, ExactSpelling = true, EntryPoint = "RtlMoveMemory", CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemoryW(IntPtr pdst, string psrc, int cb); + [DllImport(ExternDll.Kernel32, ExactSpelling = true, EntryPoint = "RtlMoveMemory", CharSet = CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemoryW(IntPtr pdst, char[] psrc, int cb); + + [DllImport(ExternDll.Kernel32, ExactSpelling = true, EntryPoint = "RtlMoveMemory", CharSet = CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemoryA(IntPtr pdst, string psrc, int cb); + [DllImport(ExternDll.Kernel32, ExactSpelling = true, EntryPoint = "RtlMoveMemory", CharSet = CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern void CopyMemoryA(IntPtr pdst, char[] psrc, int cb); + + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, EntryPoint="DuplicateHandle", SetLastError=true)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntDuplicateHandle(HandleRef processSource, HandleRef handleSource, HandleRef processTarget, ref IntPtr handleTarget, int desiredAccess, bool inheritHandle, int options); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr DuplicateHandle(HandleRef processSource, HandleRef handleSource, HandleRef processTarget, ref IntPtr handleTarget, int desiredAccess, bool inheritHandle, int options) { + IntPtr ret = IntDuplicateHandle(processSource, handleSource, processTarget, ref handleTarget, + desiredAccess, inheritHandle, options); + System.Internal.HandleCollector.Add(handleTarget, NativeMethods.CommonHandles.Kernel); + return ret; + } + + [DllImport(ExternDll.Ole32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern UnsafeNativeMethods.IStorage StgOpenStorageOnILockBytes(UnsafeNativeMethods.ILockBytes iLockBytes, UnsafeNativeMethods.IStorage pStgPriority, int grfMode, int sndExcluded, int reserved); + [DllImport(ExternDll.Ole32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetHGlobalFromILockBytes(UnsafeNativeMethods.ILockBytes pLkbyt); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetWindowsHookEx(int hookid, NativeMethods.HookProc pfnhook, HandleRef hinst, int threadid); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetKeyboardState(byte [] keystate); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="keybd_event", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void Keybd_event(byte vk, byte scan, int flags, int extrainfo); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetKeyboardState(byte [] keystate); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool UnhookWindowsHookEx(HandleRef hhook); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern short GetAsyncKeyState(int vkey); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr CallNextHookEx(HandleRef hhook, int code, IntPtr wparam, IntPtr lparam); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int ScreenToClient( HandleRef hWnd, [In, Out] NativeMethods.POINT pt ); + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length); + public static StringBuilder GetModuleFileNameLongPath(HandleRef hModule) + { + StringBuilder buffer = new StringBuilder(NativeMethods.MAX_PATH); + int noOfTimes = 1; + int length = 0; + // Iterating by allocating chunk of memory each time we find the length is not sufficient. + // Performance should not be an issue for current MAX_PATH length due to this change. + while (((length = GetModuleFileName(hModule, buffer, buffer.Capacity)) == buffer.Capacity) + && Marshal.GetLastWin32Error() == NativeMethods.ERROR_INSUFFICIENT_BUFFER + && buffer.Capacity < NativeMethods.MAX_UNICODESTRING_LEN) + { + noOfTimes += 2; // Increasing buffer size by 520 in each iteration. + int capacity = noOfTimes * NativeMethods.MAX_PATH < NativeMethods.MAX_UNICODESTRING_LEN ? noOfTimes * NativeMethods.MAX_PATH : NativeMethods.MAX_UNICODESTRING_LEN; + buffer.EnsureCapacity(capacity); + } + buffer.Length = length; + return buffer; + } + [DllImport(ExternDll.User32, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsDialogMessage(HandleRef hWndDlg, [In, Out] ref NativeMethods.MSG msg); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool TranslateMessage([In, Out] ref NativeMethods.MSG msg); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DispatchMessage([In] ref NativeMethods.MSG msg); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DispatchMessageA([In] ref NativeMethods.MSG msg); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DispatchMessageW([In] ref NativeMethods.MSG msg); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int PostThreadMessage(int id, int msg, IntPtr wparam, IntPtr lparam); + [DllImport(ExternDll.Ole32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + public static extern int CoRegisterMessageFilter(HandleRef newFilter, ref IntPtr oldMsgFilter); + [DllImport(ExternDll.Ole32, ExactSpelling=true, EntryPoint="OleInitialize", SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + private static extern int IntOleInitialize(int val); + public static int OleInitialize() { + return IntOleInitialize(0); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool EnumThreadWindows(int dwThreadId, NativeMethods.EnumThreadWindowsCallback lpfn, HandleRef lParam); + [return:MarshalAs(UnmanagedType.Bool)] [DllImport(ExternDll.Kernel32, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public extern static bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendDlgItemMessage(HandleRef hDlg, int nIDDlgItem, int Msg, IntPtr wParam, IntPtr lParam); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern int OleUninitialize(); + [DllImport(ExternDll.Comdlg32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetSaveFileName([In, Out] NativeMethods.OPENFILENAME_I ofn); + [DllImport(ExternDll.User32, EntryPoint="ChildWindowFromPointEx", ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr _ChildWindowFromPointEx(HandleRef hwndParent, POINTSTRUCT pt, int uFlags); + public static IntPtr ChildWindowFromPointEx(HandleRef hwndParent, int x, int y, int uFlags) { + POINTSTRUCT ps = new POINTSTRUCT(x, y); + return _ChildWindowFromPointEx(hwndParent, ps, uFlags); + } + [DllImport(ExternDll.Kernel32, EntryPoint="CloseHandle", ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntCloseHandle(HandleRef handle); + public static bool CloseHandle(HandleRef handle) { + System.Internal.HandleCollector.Remove((IntPtr)handle, NativeMethods.CommonHandles.Kernel); + return IntCloseHandle(handle); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="CreateCompatibleDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateCompatibleDC(HandleRef hDC); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateCompatibleDC(HandleRef hDC) { + return System.Internal.HandleCollector.Add(IntCreateCompatibleDC(hDC), NativeMethods.CommonHandles.CompatibleHDC); + } + + #region SendKeys SendInput functionality + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BlockInput([In, MarshalAs(UnmanagedType.Bool)] bool fBlockIt); + + [DllImport(ExternDll.User32, ExactSpelling = true, SetLastError=true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern uint SendInput(uint nInputs, NativeMethods.INPUT[] pInputs, int cbSize); + + #endregion + + /* + [DllImport(ExternDll.Kernel32, EntryPoint = "CreateFileMapping", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr IntCreateFileMapping(HandleRef hFile, IntPtr lpAttributes, int flProtect, int dwMaxSizeHi, int dwMaxSizeLow, string lpName); + public static IntPtr CreateFileMapping(HandleRef hFile, IntPtr lpAttributes, int flProtect, int dwMaxSizeHi, int dwMaxSizeLow, string lpName) { + return System.Internal.HandleCollector.Add(IntCreateFileMapping(hFile, lpAttributes, flProtect, dwMaxSizeHi, dwMaxSizeLow, lpName), NativeMethods.CommonHandles.Kernel); + } + + [DllImport(ExternDll.Kernel32, EntryPoint = "OpenFileMapping", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr IntOpenFileMapping(int dwDesiredAccess, bool bInheritHandle, string lpName); + public static IntPtr OpenFileMapping(int dwDesiredAccess, bool bInheritHandle, string lpName) { + return System.Internal.HandleCollector.Add(IntOpenFileMapping(dwDesiredAccess, bInheritHandle, lpName), NativeMethods.CommonHandles.Kernel); + } +*/ + + [DllImport(ExternDll.Kernel32, EntryPoint = "MapViewOfFile", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] + [ResourceExposure(ResourceScope.Process)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + private static extern IntPtr IntMapViewOfFile(HandleRef hFileMapping, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr MapViewOfFile(HandleRef hFileMapping, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap) { + return System.Internal.HandleCollector.Add(IntMapViewOfFile(hFileMapping, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap), NativeMethods.CommonHandles.Kernel); + } + + [DllImport(ExternDll.Kernel32, EntryPoint = "UnmapViewOfFile", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntUnmapViewOfFile(HandleRef pvBaseAddress); + public static bool UnmapViewOfFile(HandleRef pvBaseAddress) { + System.Internal.HandleCollector.Remove((IntPtr)pvBaseAddress, NativeMethods.CommonHandles.Kernel); + return IntUnmapViewOfFile(pvBaseAddress); + } + + [DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = "GetDCEx", CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntGetDCEx(HandleRef hWnd, HandleRef hrgnClip, int flags); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr GetDCEx(HandleRef hWnd, HandleRef hrgnClip, int flags) { + return System.Internal.HandleCollector.Add(IntGetDCEx(hWnd, hrgnClip, flags), NativeMethods.CommonHandles.HDC); + } + + // GetObject stuff + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, [In, Out] NativeMethods.BITMAP bm); + + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, [In, Out] NativeMethods.LOGPEN lp); + public static int GetObject(HandleRef hObject, NativeMethods.LOGPEN lp) { + return GetObject(hObject, Marshal.SizeOf(typeof(NativeMethods.LOGPEN)), lp); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, [In, Out] NativeMethods.LOGBRUSH lb); + public static int GetObject(HandleRef hObject, NativeMethods.LOGBRUSH lb) { + return GetObject(hObject, Marshal.SizeOf(typeof(NativeMethods.LOGBRUSH)), lb); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, [In, Out] NativeMethods.LOGFONT lf); + public static int GetObject(HandleRef hObject, NativeMethods.LOGFONT lp) { + return GetObject(hObject, Marshal.SizeOf(typeof(NativeMethods.LOGFONT)), lp); + } + + //HPALETTE + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, ref int nEntries); + [DllImport(ExternDll.Gdi32, SetLastError=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObject(HandleRef hObject, int nSize, int[] nEntries); + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetObjectType(HandleRef hObject); + + [DllImport(ExternDll.User32, EntryPoint="CreateAcceleratorTable", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateAcceleratorTable(/*ACCEL*/ HandleRef pentries, int cCount); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateAcceleratorTable(/*ACCEL*/ HandleRef pentries, int cCount) { + return System.Internal.HandleCollector.Add(IntCreateAcceleratorTable(pentries, cCount), NativeMethods.CommonHandles.Accelerator); + } + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="DestroyAcceleratorTable", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntDestroyAcceleratorTable(HandleRef hAccel); + public static bool DestroyAcceleratorTable(HandleRef hAccel) { + System.Internal.HandleCollector.Remove((IntPtr)hAccel, NativeMethods.CommonHandles.Accelerator); + return IntDestroyAcceleratorTable(hAccel); + } + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern short VkKeyScan(char key); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetCapture(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetCapture(HandleRef hwnd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetFocus(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetCursorPos([In, Out] NativeMethods.POINT pt); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern short GetKeyState(int keyCode); + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + [SuppressMessage("Microsoft.Security", "CA2101:SpecifyMarshalingForPInvokeStringArguments")] + public static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, uint cchBuffer); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="SetWindowRgn", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern int IntSetWindowRgn(HandleRef hwnd, HandleRef hrgn, bool fRedraw); + public static int SetWindowRgn(HandleRef hwnd, HandleRef hrgn, bool fRedraw) { + int retval = IntSetWindowRgn(hwnd, hrgn, fRedraw); + if (retval != 0) { + // we do this because after a SetWindowRgn call, the system owns the region + // so we don't need to bother cleaning it up. + // + System.Internal.HandleCollector.Remove((IntPtr)hrgn, NativeMethods.CommonHandles.GDI); + } + return retval; + } + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetWindowText(HandleRef hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern void GetTempFileName(string tempDirName, string prefixName, int unique, StringBuilder sb); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetWindowText(HandleRef hWnd, string text); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GlobalAlloc(int uFlags, int dwBytes); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GlobalReAlloc(HandleRef handle, int bytes, int flags); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GlobalLock(HandleRef handle); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GlobalUnlock(HandleRef handle); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GlobalFree(HandleRef handle); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GlobalSize(HandleRef handle); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmSetConversionStatus(HandleRef hIMC, int conversion, int sentence); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmGetConversionStatus(HandleRef hIMC, ref int conversion, ref int sentence); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ImmGetContext(HandleRef hWnd); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmReleaseContext(HandleRef hWnd, HandleRef hIMC); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ImmAssociateContext(HandleRef hWnd, HandleRef hIMC); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmDestroyContext(HandleRef hIMC); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr ImmCreateContext(); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmSetOpenStatus(HandleRef hIMC, bool open); + + [DllImport(ExternDll.Imm32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmGetOpenStatus(HandleRef hIMC); + + [DllImport(ExternDll.Imm32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ImmNotifyIME(HandleRef hIMC, int dwAction, int dwIndex, int dwValue); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetFocus(HandleRef hWnd); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetParent(HandleRef hWnd); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetAncestor(HandleRef hWnd, int flags); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsChild(HandleRef hWndParent, HandleRef hwnd); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + public static extern bool IsZoomed(HandleRef hWnd); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr FindWindow(string className, string windowName); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int MapWindowPoints(HandleRef hWndFrom, HandleRef hWndTo, [In, Out] ref NativeMethods.RECT rect, int cPoints); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int MapWindowPoints(HandleRef hWndFrom, HandleRef hWndTo, [In, Out] NativeMethods.POINT pt, int cPoints); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, bool wParam, int lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int[] lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int[] wParam, int[] lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, ref int wParam, ref int lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, string lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, IntPtr wParam, string lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, StringBuilder lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.TOOLINFO_T lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.TOOLINFO_TOOLTIP lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.TBBUTTON lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.TBBUTTONINFO lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.TV_ITEM lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.TV_INSERTSTRUCT lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.TV_HITTESTINFO lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVBKIMAGE lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.LVHITTESTINFO lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.TCITEM_T lParam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref NativeMethods.HDLAYOUT hdlayout); + + //for Tooltips + // + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, HandleRef wParam, int lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, HandleRef lParam); + + + // For RichTextBox + // + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeMethods.PARAFORMAT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeMethods.CHARFORMATA lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeMethods.CHARFORMAT2A lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeMethods.CHARFORMATW lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SendMessage(HandleRef hWnd, int msg, int wParam, [Out, MarshalAs(UnmanagedType.IUnknown)]out object editOle); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.CHARRANGE lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.FINDTEXT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.TEXTRANGE lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.POINT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, NativeMethods.POINT wParam, int lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.REPASTESPECIAL lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.EDITSTREAM lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.EDITSTREAM64 lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, NativeMethods.GETTEXTLENGTHEX wParam, int lParam); + + // For Button + // + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out] NativeMethods.SIZE lParam); + + // For ListView + // + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out] ref NativeMethods.LVFINDINFO lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVHITTESTINFO lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVCOLUMN_T lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out] ref NativeMethods.LVITEM lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVCOLUMN lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVGROUP lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, NativeMethods.POINT wParam, [In, Out] NativeMethods.LVINSERTMARK lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.LVINSERTMARK lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out] NativeMethods.LVTILEVIEWINFO lParam); + + // For MonthCalendar + // + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.MCHITTESTINFO lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.SYSTEMTIME lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.SYSTEMTIMEARRAY lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, [In, Out] NativeMethods.LOGFONT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.MSG lParam); + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, IntPtr wParam, [In, Out] ref NativeMethods.RECT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, ref short wParam, ref short lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, [In, Out, MarshalAs(UnmanagedType.Bool)] ref bool wParam, IntPtr lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, int wParam, IntPtr lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, int wParam, [In, Out] ref NativeMethods.RECT lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, int wParam, [In, Out] ref Rectangle lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, IntPtr wParam, NativeMethods.ListViewCompareCallback pfnCompare); + + [DllImport(ExternDll.User32, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SendMessageTimeout(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult); + + public const int SMTO_ABORTIFHUNG = 0x0002; + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetParent(HandleRef hWnd, HandleRef hWndParent); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetWindowRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetWindow(HandleRef hWnd, int uCmd); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetDlgItem(HandleRef hWnd, int nIDDlgItem); + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetModuleHandle(string modName); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefMDIChildProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, + IntPtr wParam, IntPtr lParam); + +/* + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern IntPtr GetProp(HandleRef hWnd, int atom); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern IntPtr GetProp(HandleRef hWnd, string name); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern IntPtr RemoveProp(HandleRef hWnd, short atom); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern IntPtr RemoveProp(HandleRef hWnd, string propName); + */ + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern short GlobalDeleteAtom(short atom); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Ansi)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetProcAddress(HandleRef hModule, string lpProcName); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetClassInfo(HandleRef hInst, string lpszClass, [In, Out] NativeMethods.WNDCLASS_I wc); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetClassInfo(HandleRef hInst, string lpszClass, IntPtr h); +/* + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + public static extern int SHGetFolderPath(HandleRef hwndOwner, int nFolder, HandleRef hToken, int dwFlags, StringBuilder lpszPath); +*/ + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetSystemMetrics(int nIndex); + + // This API is available starting Windows10 RS1 + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetSystemMetricsForDpi(int nIndex, uint dpi); + + /// + /// Tries to get system metrics for the dpi. dpi is ignored if "GetSystemMetricsForDpi" is not available on the OS that this application is running. + /// + /// index + /// dpi requested + /// returns system metrics for dpi + public static int TryGetSystemMetricsForDpi(int nIndex, uint dpi) { + if (ApiHelper.IsApiAvailable(ExternDll.User32, "GetSystemMetricsForDpi")) { + return GetSystemMetricsForDpi(nIndex, dpi); + } + else { + Debug.Assert(false, "GetSystemMetricsForDpi() is not available on this OS"); + return GetSystemMetrics(nIndex); + } + } + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, ref NativeMethods.RECT rc, int nUpdate); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, ref int value, int ignore); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, ref bool value, int ignore); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, ref NativeMethods.HIGHCONTRAST_I rc, int nUpdate); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, [In, Out] NativeMethods.NONCLIENTMETRICS metrics, int nUpdate); + + // This API is available starting Windows10 RS1 + [DllImport(ExternDll.User32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfoForDpi(int nAction, int nParam, [In, Out] NativeMethods.NONCLIENTMETRICS metrics, int nUpdate, uint dpi); + + /// + /// Tries to get system parameter info for the dpi. dpi is ignored if "SystemParametersInfoForDpi()" API is not available on the OS that this application is running. + /// + public static bool TrySystemParametersInfoForDpi(int nAction, int nParam, [In, Out] NativeMethods.NONCLIENTMETRICS metrics, int nUpdate, uint dpi) { + if(ApiHelper.IsApiAvailable(ExternDll.User32, "SystemParametersInfoForDpi")) { + return SystemParametersInfoForDpi(nAction, nParam, metrics, nUpdate, dpi); + } + else { + Debug.Assert(false, "SystemParametersInfoForDpi() is not available on this OS"); + return SystemParametersInfo(nAction, nParam, metrics, nUpdate); + } + } +/* + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern bool SystemParametersInfo(int nAction, int nParam, [In, Out] NativeMethods.ICONMETRICS iconMetrics, int nUpdate); +*/ + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, [In, Out] NativeMethods.LOGFONT font, int nUpdate); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, bool [] flag, bool nUpdate); + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetComputerName(StringBuilder lpBuffer, int[] nSize); + [DllImport(ExternDll.Advapi32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetUserName(StringBuilder lpBuffer, int[] nSize); + [DllImport(ExternDll.User32, ExactSpelling=true)] + [ResourceExposure(ResourceScope.Machine)] + public static extern IntPtr GetProcessWindowStation(); + [DllImport(ExternDll.User32, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetUserObjectInformation(HandleRef hObj, int nIndex, [MarshalAs(UnmanagedType.LPStruct)] NativeMethods.USEROBJECTFLAGS pvBuffer, int nLength, ref int lpnLengthNeeded); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int ClientToScreen( HandleRef hWnd, [In, Out] NativeMethods.POINT pt ); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetForegroundWindow(); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetDesktopWindow(); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RegisterDragDrop(HandleRef hwnd, UnsafeNativeMethods.IOleDropTarget target); + [DllImport(ExternDll.Ole32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int RevokeDragDrop(HandleRef hwnd); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PeekMessage([In, Out] ref NativeMethods.MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); + [DllImport(ExternDll.User32, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PeekMessageW([In, Out] ref NativeMethods.MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); + [DllImport(ExternDll.User32, CharSet=CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PeekMessageA([In, Out] ref NativeMethods.MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); + +/* + [DllImport(ExternDll.User32, CharSet=CharSet.Auto), + SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + public static extern bool SetProp(HandleRef hWnd, int atom, HandleRef data); + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + public static extern bool SetProp(HandleRef hWnd, string propName, HandleRef data); +*/ + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool PostMessage(HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam); + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern short GlobalAddAtom(string atomName); + [DllImport(ExternDll.Oleacc, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr LresultFromObject(ref Guid refiid, IntPtr wParam, HandleRef pAcc); + [DllImport(ExternDll.Oleacc, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int CreateStdAccessibleObject(HandleRef hWnd, int objID, ref Guid refiid, [In, Out, MarshalAs(UnmanagedType.Interface)] ref object pAcc); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void NotifyWinEvent(int winEvent, HandleRef hwnd, int objType, int objID); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetMenuItemID(HandleRef hMenu, int nPos); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetSubMenu(HandleRef hwnd, int index); + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetMenuItemCount(HandleRef hMenu); + [DllImport(ExternDll.Oleaut32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern void GetErrorInfo(int reserved, [In, Out] ref UnsafeNativeMethods.IErrorInfo errorInfo); + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="BeginPaint", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr IntBeginPaint(HandleRef hWnd, [In, Out] ref NativeMethods.PAINTSTRUCT lpPaint); + public static IntPtr BeginPaint(HandleRef hWnd, [In, Out, MarshalAs(UnmanagedType.LPStruct)] ref NativeMethods.PAINTSTRUCT lpPaint) { + return System.Internal.HandleCollector.Add(IntBeginPaint(hWnd, ref lpPaint), NativeMethods.CommonHandles.HDC); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="EndPaint", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntEndPaint(HandleRef hWnd, ref NativeMethods.PAINTSTRUCT lpPaint); + public static bool EndPaint(HandleRef hWnd, [In, MarshalAs(UnmanagedType.LPStruct)] ref NativeMethods.PAINTSTRUCT lpPaint) { + System.Internal.HandleCollector.Remove(lpPaint.hdc, NativeMethods.CommonHandles.HDC); + return IntEndPaint(hWnd, ref lpPaint); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="GetDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntGetDC(HandleRef hWnd); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr GetDC(HandleRef hWnd) { + return System.Internal.HandleCollector.Add(IntGetDC(hWnd), NativeMethods.CommonHandles.HDC); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="GetWindowDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntGetWindowDC(HandleRef hWnd); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr GetWindowDC(HandleRef hWnd) { + return System.Internal.HandleCollector.Add(IntGetWindowDC(hWnd), NativeMethods.CommonHandles.HDC); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="ReleaseDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC); + public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) { + System.Internal.HandleCollector.Remove((IntPtr)hDC, NativeMethods.CommonHandles.HDC); + return IntReleaseDC(hWnd, hDC); + } + + [DllImport(ExternDll.Gdi32, SetLastError=true, EntryPoint="CreateDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateDC(string lpszDriver, string lpszDeviceName, string lpszOutput, HandleRef devMode); + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateDC(string lpszDriver) { + return System.Internal.HandleCollector.Add(IntCreateDC(lpszDriver, null, null, NativeMethods.NullHandleRef), NativeMethods.CommonHandles.HDC); + } + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateDC(string lpszDriverName, string lpszDeviceName, string lpszOutput, HandleRef /*DEVMODE*/ lpInitData) { + return System.Internal.HandleCollector.Add(IntCreateDC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData), NativeMethods.CommonHandles.HDC); + } + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SystemParametersInfo(int nAction, int nParam, [In, Out] IntPtr[] rc, int nUpdate); + + [DllImport(ExternDll.User32, EntryPoint="SendMessage", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + //public extern static IntPtr SendCallbackMessage(HandleRef hWnd, int Msg, IntPtr wParam, UnsafeNativeMethods.IRichTextBoxOleCallback lParam); + public extern static IntPtr SendCallbackMessage(HandleRef hWnd, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImport(ExternDll.Shell32, ExactSpelling=true, CharSet=CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern void DragAcceptFiles(HandleRef hWnd, bool fAccept); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetDeviceCaps(HandleRef hDC, int nIndex); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetScrollInfo(HandleRef hWnd, int fnBar, NativeMethods.SCROLLINFO si); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int SetScrollInfo(HandleRef hWnd, int fnBar, NativeMethods.SCROLLINFO si, bool redraw); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetActiveWindow(); + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr LoadLibrary(string libname); + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto, SetLastError=true, BestFitMapping=false)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr LoadLibraryEx(string lpModuleName, IntPtr hFile, uint dwFlags); + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool FreeLibrary(HandleRef hModule); + + //GetWindowLong won't work correctly for 64-bit: we should use GetWindowLongPtr instead. On + //32-bit, GetWindowLongPtr is just #defined as GetWindowLong. GetWindowLong really should + //take/return int instead of IntPtr/HandleRef, but since we're running this only for 32-bit + //it'll be OK. + public static IntPtr GetWindowLong(HandleRef hWnd, int nIndex) + { + if (IntPtr.Size == 4) + { + return GetWindowLong32(hWnd, nIndex); + } + return GetWindowLongPtr64(hWnd, nIndex); + } + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "GetWindowLong")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex); + + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "GetWindowLongPtr")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex); + + //SetWindowLong won't work correctly for 64-bit: we should use SetWindowLongPtr instead. On + //32-bit, SetWindowLongPtr is just #defined as SetWindowLong. SetWindowLong really should + //take/return int instead of IntPtr/HandleRef, but since we're running this only for 32-bit + //it'll be OK. + public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong) + { + if (IntPtr.Size == 4) + { + return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); + } + return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + } + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLong")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, HandleRef dwNewLong); + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLongPtr")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, HandleRef dwNewLong); + + public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, NativeMethods.WndProc wndproc) + { + if (IntPtr.Size == 4) + { + return SetWindowLongPtr32(hWnd, nIndex, wndproc); + } + return SetWindowLongPtr64(hWnd, nIndex, wndproc); + } + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLong")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, NativeMethods.WndProc wndproc); + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, EntryPoint = "SetWindowLongPtr")] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, NativeMethods.WndProc wndproc); + + [DllImport(ExternDll.Ole32, PreserveSig = false)] + [ResourceExposure(ResourceScope.None)] + public static extern UnsafeNativeMethods.ILockBytes CreateILockBytesOnHGlobal(HandleRef hGlobal, bool fDeleteOnRelease); + [DllImport(ExternDll.Ole32, PreserveSig=false)] + [ResourceExposure(ResourceScope.None)] + public static extern UnsafeNativeMethods.IStorage StgCreateDocfileOnILockBytes(UnsafeNativeMethods.ILockBytes iLockBytes, int grfMode, int reserved); + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="CreatePopupMenu", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreatePopupMenu(); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreatePopupMenu() { + return System.Internal.HandleCollector.Add(IntCreatePopupMenu(), NativeMethods.CommonHandles.Menu); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool RemoveMenu(HandleRef hMenu, int uPosition, int uFlags); + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="DestroyMenu", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntDestroyMenu(HandleRef hMenu); + public static bool DestroyMenu(HandleRef hMenu) { + System.Internal.HandleCollector.Remove((IntPtr)hMenu, NativeMethods.CommonHandles.Menu); + return IntDestroyMenu(hMenu); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetForegroundWindow(HandleRef hWnd); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr GetSystemMenu(HandleRef hWnd, bool bRevert); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefFrameProc(IntPtr hWnd, IntPtr hWndClient, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool TranslateMDISysAccel(IntPtr hWndClient, [In, Out] ref NativeMethods.MSG msg); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetLayeredWindowAttributes(HandleRef hwnd, int crKey, byte bAlpha, int dwFlags); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static bool SetMenu(HandleRef hWnd, HandleRef hMenu); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetWindowPlacement(HandleRef hWnd, ref NativeMethods.WINDOWPLACEMENT placement); + + + [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void GetStartupInfo([In, Out] NativeMethods.STARTUPINFO_I startupinfo_i); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetMenuDefaultItem(HandleRef hwnd, int nIndex, bool pos); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool EnableMenuItem(HandleRef hMenu, int UIDEnabledItem, int uEnable); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr SetActiveWindow(HandleRef hWnd); + + [DllImport(ExternDll.Gdi32, SetLastError=true, EntryPoint="CreateIC", CharSet=System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr IntCreateIC(string lpszDriverName, string lpszDeviceName, string lpszOutput, HandleRef /*DEVMODE*/ lpInitData); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateIC(string lpszDriverName, string lpszDeviceName, string lpszOutput, HandleRef /*DEVMODE*/ lpInitData) { + return System.Internal.HandleCollector.Add(IntCreateIC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData), NativeMethods.CommonHandles.HDC); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ClipCursor(ref NativeMethods.RECT rcClip); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool ClipCursor(NativeMethods.COMRECT rcClip); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SetCursor(HandleRef hcursor); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetCursorPos(int x, int y); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public extern static int ShowCursor(bool bShow); + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="DestroyCursor", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntDestroyCursor(HandleRef hCurs); + public static bool DestroyCursor(HandleRef hCurs) { + System.Internal.HandleCollector.Remove((IntPtr)hCurs, NativeMethods.CommonHandles.Cursor); + return IntDestroyCursor(hCurs); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IsWindow(HandleRef hWnd); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, EntryPoint="DeleteDC", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool IntDeleteDC(HandleRef hDC); + public static bool DeleteDC(HandleRef hDC) { + System.Internal.HandleCollector.Remove((IntPtr)hDC, NativeMethods.CommonHandles.HDC); + return IntDeleteDC(hDC); + } + + public static bool DeleteCompatibleDC(HandleRef hDC) + { + System.Internal.HandleCollector.Remove((IntPtr)hDC, NativeMethods.CommonHandles.CompatibleHDC); + return IntDeleteDC(hDC); + } + + public const int LAYOUT_RTL = 0x00000001; + public const int LAYOUT_BITMAPORIENTATIONPRESERVED = 0x00000008; + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Ansi)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetMessageA([In, Out] ref NativeMethods.MSG msg, HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Unicode)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetMessageW([In, Out] ref NativeMethods.MSG msg, HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, IntPtr lparam); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetClientRect(HandleRef hWnd, [In, Out] ref NativeMethods.RECT rect); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetClientRect(HandleRef hWnd, IntPtr rect); + + [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(ExternDll.User32, EntryPoint="WindowFromPoint", ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + private static extern IntPtr _WindowFromPoint(POINTSTRUCT pt); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr WindowFromPoint(int x, int y) { + POINTSTRUCT ps = new POINTSTRUCT(x, y); + return _WindowFromPoint(ps); + } + [DllImport(ExternDll.User32, SetLastError=true, ExactSpelling = true)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr WindowFromDC( HandleRef hDC ); + [DllImport(ExternDll.User32, EntryPoint="CreateWindowEx", CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr IntCreateWindowEx(int dwExStyle, string lpszClassName, + string lpszWindowName, int style, int x, int y, int width, int height, + HandleRef hWndParent, HandleRef hMenu, HandleRef hInst, [MarshalAs(UnmanagedType.AsAny)] object pvParam); + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr CreateWindowEx(int dwExStyle, string lpszClassName, + string lpszWindowName, int style, int x, int y, int width, int height, + HandleRef hWndParent, HandleRef hMenu, HandleRef hInst, [MarshalAs(UnmanagedType.AsAny)]object pvParam) { + return IntCreateWindowEx(dwExStyle, lpszClassName, + lpszWindowName, style, x, y, width, height, hWndParent, hMenu, + hInst, pvParam); + } + + [DllImport(ExternDll.User32, ExactSpelling=true, EntryPoint="DestroyWindow", CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool IntDestroyWindow(HandleRef hWnd); + public static bool DestroyWindow(HandleRef hWnd) { + return IntDestroyWindow(hWnd); + } + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern bool UnregisterClass(string className, HandleRef hInstance); + + [DllImport(ExternDll.Gdi32, SetLastError=true, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.Process)] + public static extern IntPtr GetStockObject(int nIndex); + + [DllImport(ExternDll.User32, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + public static extern short RegisterClass(NativeMethods.WNDCLASS_D wc); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void PostQuitMessage(int nExitCode); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern void WaitMessage(); + + [DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetWindowPlacement(HandleRef hWnd, [In] ref NativeMethods.WINDOWPLACEMENT placement); + + // This method is not available until Windows 8.1 + [DllImport(ExternDll.User32, ExactSpelling=true, SetLastError = true)] + [ResourceExposure(ResourceScope.None)] + public static extern uint GetDpiForWindow(HandleRef hWnd); + + // For system power status + // + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetSystemPowerStatus([In, Out] ref NativeMethods.SYSTEM_POWER_STATUS systemPowerStatus); + + [DllImport(ExternDll.Powrprof, ExactSpelling=true, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool SetSuspendState(bool hiberate, bool forceCritical, bool disableWakeEvent); + + //for RegionData + [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern int GetRegionData(HandleRef hRgn, int size, IntPtr lpRgnData); + + public unsafe static NativeMethods.RECT[] GetRectsFromRegion(IntPtr hRgn) { + NativeMethods.RECT[] regionRects = null; + IntPtr pBytes = IntPtr.Zero; + try { + // see how much memory we need to allocate + int regionDataSize = GetRegionData(new HandleRef(null, hRgn), 0, IntPtr.Zero); + if (regionDataSize != 0) { + pBytes = Marshal.AllocCoTaskMem(regionDataSize); + // get the data + int ret = GetRegionData(new HandleRef(null, hRgn), regionDataSize, pBytes); + if (ret == regionDataSize) { + // cast to the structure + NativeMethods.RGNDATAHEADER* pRgnDataHeader = (NativeMethods.RGNDATAHEADER*)pBytes; + if (pRgnDataHeader->iType == 1) { // expecting RDH_RECTANGLES + regionRects = new NativeMethods.RECT[pRgnDataHeader->nCount]; + + Debug.Assert(regionDataSize == pRgnDataHeader->cbSizeOfStruct + pRgnDataHeader->nCount * pRgnDataHeader->nRgnSize); + Debug.Assert(Marshal.SizeOf(typeof(NativeMethods.RECT)) == pRgnDataHeader->nRgnSize || pRgnDataHeader->nRgnSize == 0); + + // use the header size as the offset, and cast each rect in. + int rectStart = pRgnDataHeader->cbSizeOfStruct; + for (int i = 0; i < pRgnDataHeader->nCount; i++) { + // use some fancy pointer math to just copy the rect bits directly into the array. + regionRects[i] = *((NativeMethods.RECT*)((byte*)pBytes + rectStart + (Marshal.SizeOf(typeof(NativeMethods.RECT)) * i))); + } + } + } + } + } + finally { + if (pBytes != IntPtr.Zero) { + Marshal.FreeCoTaskMem(pBytes); + } + } + return regionRects; + } + + /* Unused + // for GetUserNameEx + public enum EXTENDED_NAME_FORMAT { + NameUnknown = 0, + NameFullyQualifiedDN = 1, + NameSamCompatible = 2, + NameDisplay = 3, + NameUniqueId = 6, + NameCanonical = 7, + NameUserPrincipal = 8, + NameCanonicalEx = 9, + NameServicePrincipal = 10 + } + */ + + [ComImport(), Guid("00000122-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleDropTarget { + [PreserveSig] + int OleDragEnter( + [In, MarshalAs(UnmanagedType.Interface)] + object pDataObj, + [In, MarshalAs(UnmanagedType.U4)] + int grfKeyState, + [In] + POINTSTRUCT pt, + [In, Out] + ref int pdwEffect); + + [PreserveSig] + int OleDragOver( + [In, MarshalAs(UnmanagedType.U4)] + int grfKeyState, + [In] + POINTSTRUCT pt, + [In, Out] + ref int pdwEffect); + + [PreserveSig] + int OleDragLeave(); + + [PreserveSig] + int OleDrop( + [In, MarshalAs(UnmanagedType.Interface)] + object pDataObj, + [In, MarshalAs(UnmanagedType.U4)] + int grfKeyState, + [In] + POINTSTRUCT pt, + [In, Out] + ref int pdwEffect); + } + + [ComImport(), Guid("00000121-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleDropSource { + + [PreserveSig] + int OleQueryContinueDrag( + int fEscapePressed, + [In, MarshalAs(UnmanagedType.U4)] + int grfKeyState); + + [PreserveSig] + int OleGiveFeedback( + [In, MarshalAs(UnmanagedType.U4)] + int dwEffect); + } + + [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleMessageFilter { + + [PreserveSig] + int HandleInComingCall( + int dwCallType, + IntPtr hTaskCaller, + int dwTickCount, + /* LPINTERFACEINFO */ IntPtr lpInterfaceInfo); + + [PreserveSig] + int RetryRejectedCall( + IntPtr hTaskCallee, + int dwTickCount, + int dwRejectType); + + [PreserveSig] + int MessagePending( + IntPtr hTaskCallee, + int dwTickCount, + int dwPendingType); + } + + [ + ComImport(), + Guid("B196B289-BAB4-101A-B69C-00AA00341D07"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IOleControlSite { + + [PreserveSig] + int OnControlInfoChanged(); + + [PreserveSig] + int LockInPlaceActive(int fLock); + + [PreserveSig] + int GetExtendedControl( + [Out, MarshalAs(UnmanagedType.IDispatch)] + out object ppDisp); + + [PreserveSig] + int TransformCoords( + [In, Out] + NativeMethods._POINTL pPtlHimetric, + [In, Out] + NativeMethods.tagPOINTF pPtfContainer, + [In, MarshalAs(UnmanagedType.U4)] + int dwFlags); + + [PreserveSig] + int TranslateAccelerator( + [In] + ref NativeMethods.MSG pMsg, + [In, MarshalAs(UnmanagedType.U4)] + int grfModifiers); + + [PreserveSig] + int OnFocus(int fGotFocus); + + [PreserveSig] + int ShowPropertyFrame(); + + } + + [ComImport(), Guid("00000118-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleClientSite { + + [PreserveSig] + int SaveObject(); + + [PreserveSig] + int GetMoniker( + [In, MarshalAs(UnmanagedType.U4)] + int dwAssign, + [In, MarshalAs(UnmanagedType.U4)] + int dwWhichMoniker, + [Out, MarshalAs(UnmanagedType.Interface)] + out object moniker); + + [PreserveSig] + int GetContainer(out IOleContainer container); + + [PreserveSig] + int ShowObject(); + + [PreserveSig] + int OnShowWindow(int fShow); + + [PreserveSig] + int RequestNewObjectLayout(); + } + + [ComImport(), Guid("00000119-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceSite { + + IntPtr GetWindow(); + + [PreserveSig] + int ContextSensitiveHelp(int fEnterMode); + + [PreserveSig] + int CanInPlaceActivate(); + + [PreserveSig] + int OnInPlaceActivate(); + + [PreserveSig] + int OnUIActivate(); + + [PreserveSig] + int GetWindowContext( + [Out, MarshalAs(UnmanagedType.Interface)] + out UnsafeNativeMethods.IOleInPlaceFrame ppFrame, + [Out, MarshalAs(UnmanagedType.Interface)] + out UnsafeNativeMethods.IOleInPlaceUIWindow ppDoc, + [Out] + NativeMethods.COMRECT lprcPosRect, + [Out] + NativeMethods.COMRECT lprcClipRect, + [In, Out] + NativeMethods.tagOIFI lpFrameInfo); + + [PreserveSig] + int Scroll( + NativeMethods.tagSIZE scrollExtant); + + [PreserveSig] + int OnUIDeactivate( + int fUndoable); + + [PreserveSig] + int OnInPlaceDeactivate(); + + [PreserveSig] + int DiscardUndoState(); + + [PreserveSig] + int DeactivateAndUndo(); + + [PreserveSig] + int OnPosRectChange( + [In] + NativeMethods.COMRECT lprcPosRect); + } + + [ComImport(), Guid("742B0E01-14E6-101B-914E-00AA00300CAB"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISimpleFrameSite { + + [PreserveSig] + int PreMessageFilter( + IntPtr hwnd, + [In, MarshalAs(UnmanagedType.U4)] + int msg, + IntPtr wp, + IntPtr lp, + [In, Out] + ref IntPtr plResult, + [In, Out, MarshalAs(UnmanagedType.U4)] + ref int pdwCookie); + + [PreserveSig] + int PostMessageFilter( + IntPtr hwnd, + [In, MarshalAs(UnmanagedType.U4)] + int msg, + IntPtr wp, + IntPtr lp, + [In, Out] + ref IntPtr plResult, + [In, MarshalAs(UnmanagedType.U4)] + int dwCookie); + } + + [ComImport(), Guid("40A050A0-3C31-101B-A82E-08002B2B2337"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVBGetControl { + + [PreserveSig] + int EnumControls( + int dwOleContF, + int dwWhich, + [Out] + out IEnumUnknown ppenum); + } + + [ComImport(), Guid("91733A60-3F4C-101B-A3F6-00AA0034E4E9"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IGetVBAObject { + + [PreserveSig] + int GetObject( + [In] + ref Guid riid, + [Out, MarshalAs(UnmanagedType.LPArray)] + IVBFormat[] rval, + int dwReserved); + } + + [ComImport(), Guid("9BFBBC02-EFF1-101A-84ED-00AA00341D07"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPropertyNotifySink { + void OnChanged(int dispID); + + [PreserveSig] + int OnRequestEdit(int dispID); + } + + [ComImport(), Guid("9849FD60-3768-101B-8D72-AE6164FFE3CF"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVBFormat { + + [PreserveSig] + int Format( + [In] + ref object var, + IntPtr pszFormat, + IntPtr lpBuffer, + short cpBuffer, + int lcid, + short firstD, + short firstW, + [Out, MarshalAs(UnmanagedType.LPArray)] + short[] result); + } + + [ComImport(), Guid("00000100-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumUnknown { + + [PreserveSig] + int Next( + [In, MarshalAs(UnmanagedType.U4)] + int celt, + [Out] + IntPtr rgelt, + IntPtr pceltFetched); + + [PreserveSig] + int Skip( + [In, MarshalAs(UnmanagedType.U4)] + int celt); + + void Reset(); + + void Clone( + [Out] + out IEnumUnknown ppenum); + } + + [ComImport(), Guid("0000011B-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleContainer { + + [PreserveSig] + int ParseDisplayName( + [In, MarshalAs(UnmanagedType.Interface)] + object pbc, + [In, MarshalAs(UnmanagedType.BStr)] + string pszDisplayName, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pchEaten, + [Out, MarshalAs(UnmanagedType.LPArray)] + object[] ppmkOut); + + [PreserveSig] + int EnumObjects( + [In, MarshalAs(UnmanagedType.U4)] + int grfFlags, + [Out] + out IEnumUnknown ppenum); + + [PreserveSig] + int LockContainer( + bool fLock); + } + + [ComImport(), Guid("00000116-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceFrame { + + IntPtr GetWindow(); + + [PreserveSig] + int ContextSensitiveHelp(int fEnterMode); + + [PreserveSig] + int GetBorder( + [Out] + NativeMethods.COMRECT lprectBorder); + + [PreserveSig] + int RequestBorderSpace( + [In] + NativeMethods.COMRECT pborderwidths); + + [PreserveSig] + int SetBorderSpace( + [In] + NativeMethods.COMRECT pborderwidths); + + [PreserveSig] + int SetActiveObject( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleInPlaceActiveObject pActiveObject, + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszObjName); + + [PreserveSig] + int InsertMenus( + [In] + IntPtr hmenuShared, + [In, Out] + NativeMethods.tagOleMenuGroupWidths lpMenuWidths); + + [PreserveSig] + int SetMenu( + [In] + IntPtr hmenuShared, + [In] + IntPtr holemenu, + [In] + IntPtr hwndActiveObject); + + [PreserveSig] + int RemoveMenus( + [In] + IntPtr hmenuShared); + + [PreserveSig] + int SetStatusText( + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszStatusText); + + [PreserveSig] + int EnableModeless( + bool fEnable); + + [PreserveSig] + int TranslateAccelerator( + [In] + ref NativeMethods.MSG lpmsg, + [In, MarshalAs(UnmanagedType.U2)] + short wID); + } + + // Used to control the webbrowser appearance and provide DTE to script via window.external + [ComVisible(true), ComImport(), Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IDocHostUIHandler { + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int ShowContextMenu( + [In, MarshalAs(UnmanagedType.U4)] + int dwID, + [In] + NativeMethods.POINT pt, + [In, MarshalAs(UnmanagedType.Interface)] + object pcmdtReserved, + [In, MarshalAs(UnmanagedType.Interface)] + object pdispReserved); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int GetHostInfo( + [In, Out] + NativeMethods.DOCHOSTUIINFO info); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int ShowUI( + [In, MarshalAs(UnmanagedType.I4)] + int dwID, + [In] + UnsafeNativeMethods.IOleInPlaceActiveObject activeObject, + [In] + NativeMethods.IOleCommandTarget commandTarget, + [In] + UnsafeNativeMethods.IOleInPlaceFrame frame, + [In] + UnsafeNativeMethods.IOleInPlaceUIWindow doc); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int HideUI(); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int UpdateUI(); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int EnableModeless( + [In, MarshalAs(UnmanagedType.Bool)] + bool fEnable); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int OnDocWindowActivate( + [In, MarshalAs(UnmanagedType.Bool)] + bool fActivate); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int OnFrameWindowActivate( + [In, MarshalAs(UnmanagedType.Bool)] + bool fActivate); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int ResizeBorder( + [In] + NativeMethods.COMRECT rect, + [In] + UnsafeNativeMethods.IOleInPlaceUIWindow doc, + bool fFrameWindow); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int TranslateAccelerator( + [In] + ref NativeMethods.MSG msg, + [In] + ref Guid group, + [In, MarshalAs(UnmanagedType.I4)] + int nCmdID); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int GetOptionKeyPath( + [Out, MarshalAs(UnmanagedType.LPArray)] + String[] pbstrKey, + [In, MarshalAs(UnmanagedType.U4)] + int dw); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int GetDropTarget( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleDropTarget pDropTarget, + [Out, MarshalAs(UnmanagedType.Interface)] + out UnsafeNativeMethods.IOleDropTarget ppDropTarget); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int GetExternal( + [Out, MarshalAs(UnmanagedType.Interface)] + out object ppDispatch); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int TranslateUrl( + [In, MarshalAs(UnmanagedType.U4)] + int dwTranslate, + [In, MarshalAs(UnmanagedType.LPWStr)] + string strURLIn, + [Out, MarshalAs(UnmanagedType.LPWStr)] + out string pstrURLOut); + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int FilterDataObject( + IComDataObject pDO, + out IComDataObject ppDORet); + } + + [SuppressUnmanagedCodeSecurity, ComImport(), Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), + TypeLibType(TypeLibTypeFlags.FHidden | TypeLibTypeFlags.FDual | TypeLibTypeFlags.FOleAutomation)] + public interface IWebBrowser2 { + // + // IWebBrowser members + [DispId(100)] void GoBack(); + [DispId(101)] void GoForward(); + [DispId(102)] void GoHome(); + [DispId(103)] void GoSearch(); + [DispId(104)] void Navigate([In] string Url, [In] ref object flags, + [In] ref object targetFrameName, [In] ref object postData, + [In] ref object headers); + [DispId(-550)] void Refresh(); + [DispId(105)] void Refresh2([In] ref object level); + [DispId(106)] void Stop(); + [DispId(200)] object Application {[return: MarshalAs(UnmanagedType.IDispatch)]get;} + [DispId(201)] object Parent {[return: MarshalAs(UnmanagedType.IDispatch)]get;} + [DispId(202)] object Container {[return: MarshalAs(UnmanagedType.IDispatch)]get;} + [DispId(203)] object Document {[return: MarshalAs(UnmanagedType.IDispatch)]get;} + [DispId(204)] bool TopLevelContainer {get;} + [DispId(205)] string Type {get;} + [DispId(206)] int Left {get; set;} + [DispId(207)] int Top {get; set;} + [DispId(208)] int Width {get; set;} + [DispId(209)] int Height {get; set;} + [DispId(210)] string LocationName {get;} + [DispId(211)] string LocationURL {get;} + [DispId(212)] bool Busy {get;} + // + // IWebBrowserApp members + [DispId(300)] void Quit(); + [DispId(301)] void ClientToWindow([Out]out int pcx, [Out]out int pcy); + [DispId(302)] void PutProperty([In] string property, [In] object vtValue); + [DispId(303)] object GetProperty([In] string property); + [DispId(0)] string Name {get;} + [DispId(-515)] int HWND {get;} + [DispId(400)] string FullName {get;} + [DispId(401)] string Path {get;} + [DispId(402)] bool Visible {get; set;} + [DispId(403)] bool StatusBar {get; set;} + [DispId(404)] string StatusText {get; set;} + [DispId(405)] int ToolBar {get; set;} + [DispId(406)] bool MenuBar {get; set;} + [DispId(407)] bool FullScreen {get; set;} + // + // IWebBrowser2 members + [DispId(500)] void Navigate2([In] ref object URL, [In] ref object flags, + [In] ref object targetFrameName, [In] ref object postData, + [In] ref object headers); + [DispId(501)] NativeMethods.OLECMDF QueryStatusWB([In] NativeMethods.OLECMDID cmdID); + [DispId(502)] void ExecWB([In] NativeMethods.OLECMDID cmdID, + [In] NativeMethods.OLECMDEXECOPT cmdexecopt, + ref object pvaIn, + IntPtr pvaOut); + [DispId(503)] void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, + [In] ref object pvarSize); + [DispId(-525)] WebBrowserReadyState ReadyState {get;} + [DispId(550)] bool Offline {get; set;} + [DispId(551)] bool Silent {get; set;} + [DispId(552)] bool RegisterAsBrowser {get; set;} + [DispId(553)] bool RegisterAsDropTarget {get; set;} + [DispId(554)] bool TheaterMode {get; set;} + [DispId(555)] bool AddressBar {get; set;} + [DispId(556)] bool Resizable {get; set;} + } + + [ComImport(), Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DWebBrowserEvents2 { + [DispId(102)] void StatusTextChange([In] string text); + [DispId(108)] void ProgressChange([In] int progress, [In] int progressMax); + [DispId(105)] void CommandStateChange([In] long command, [In] bool enable); + [DispId(106)] void DownloadBegin(); + [DispId(104)] void DownloadComplete(); + [DispId(113)] void TitleChange([In] string text); + [DispId(112)] void PropertyChange([In] string szProperty); + [DispId(250)] void BeforeNavigate2([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, + [In] ref object URL, [In] ref object flags, + [In] ref object targetFrameName, [In] ref object postData, + [In] ref object headers, [In, Out] ref bool cancel); + [DispId(251)] void NewWindow2([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object pDisp, + [In, Out] ref bool cancel); + [DispId(252)] void NavigateComplete2([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, + [In] ref object URL); + [DispId(259)] void DocumentComplete([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, + [In] ref object URL); + [DispId(253)] void OnQuit(); + [DispId(254)] void OnVisible([In] bool visible); + [DispId(255)] void OnToolBar([In] bool toolBar); + [DispId(256)] void OnMenuBar([In] bool menuBar); + [DispId(257)] void OnStatusBar([In] bool statusBar); + [DispId(258)] void OnFullScreen([In] bool fullScreen); + [DispId(260)] void OnTheaterMode([In] bool theaterMode); + [DispId(262)] void WindowSetResizable([In] bool resizable); + [DispId(264)] void WindowSetLeft([In] int left); + [DispId(265)] void WindowSetTop([In] int top); + [DispId(266)] void WindowSetWidth([In] int width); + [DispId(267)] void WindowSetHeight([In] int height); + [DispId(263)] void WindowClosing([In] bool isChildWindow, [In, Out] ref bool cancel); + [DispId(268)] void ClientToHostWindow([In, Out] ref long cx, [In, Out] ref long cy); + [DispId(269)] void SetSecureLockIcon([In] int secureLockIcon); + [DispId(270)] void FileDownload([In, Out] ref bool cancel); + [DispId(271)] void NavigateError([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, + [In] ref object URL, [In] ref object frame, [In] ref object statusCode, [In, Out] ref bool cancel); + [DispId(225)] void PrintTemplateInstantiation([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp); + [DispId(226)] void PrintTemplateTeardown([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp); + [DispId(227)] void UpdatePageStatus([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, + [In] ref object nPage, [In] ref object fDone); + [DispId(272)] void PrivacyImpactedStateChange([In] bool bImpacted); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("626FC520-A41E-11cf-A731-00A0C9082637"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLDocument { + [return: MarshalAs(UnmanagedType.IDispatch)] object GetScript(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("332C4425-26CB-11D0-B483-00C04FD90119"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLDocument2 { + [return: MarshalAs(UnmanagedType.IDispatch)] object GetScript(); + IHTMLElementCollection GetAll(); + IHTMLElement GetBody(); + IHTMLElement GetActiveElement(); + IHTMLElementCollection GetImages(); + IHTMLElementCollection GetApplets(); + IHTMLElementCollection GetLinks(); + IHTMLElementCollection GetForms(); + IHTMLElementCollection GetAnchors(); + void SetTitle(string p); + string GetTitle(); + IHTMLElementCollection GetScripts(); + void SetDesignMode(string p); + string GetDesignMode(); + [return: MarshalAs(UnmanagedType.Interface)] object GetSelection(); + string GetReadyState(); + [return: MarshalAs(UnmanagedType.Interface)] object GetFrames(); + IHTMLElementCollection GetEmbeds(); + IHTMLElementCollection GetPlugins(); + void SetAlinkColor(object c); + object GetAlinkColor(); + void SetBgColor(object c); + object GetBgColor(); + void SetFgColor(object c); + object GetFgColor(); + void SetLinkColor(object c); + object GetLinkColor(); + void SetVlinkColor(object c); + object GetVlinkColor(); + string GetReferrer(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLLocation GetLocation(); + string GetLastModified(); + void SetUrl(string p); + string GetUrl(); + void SetDomain(string p); + string GetDomain(); + void SetCookie(string p); + string GetCookie(); + void SetExpando(bool p); + bool GetExpando(); + void SetCharset(string p); + string GetCharset(); + void SetDefaultCharset(string p); + string GetDefaultCharset(); + string GetMimeType(); + string GetFileSize(); + string GetFileCreatedDate(); + string GetFileModifiedDate(); + string GetFileUpdatedDate(); + string GetSecurity(); + string GetProtocol(); + string GetNameProp(); + int Write([In, MarshalAs(UnmanagedType.SafeArray)] object[] psarray); + int WriteLine([In, MarshalAs(UnmanagedType.SafeArray)] object[] psarray); + [return: MarshalAs(UnmanagedType.Interface)] object Open(string mimeExtension, object name, object features, object replace); + void Close(); + void Clear(); + bool QueryCommandSupported(string cmdID); + bool QueryCommandEnabled(string cmdID); + bool QueryCommandState(string cmdID); + bool QueryCommandIndeterm(string cmdID); + string QueryCommandText( + string cmdID); + Object QueryCommandValue(string cmdID); + bool ExecCommand(string cmdID, + bool showUI, Object value); + bool ExecCommandShowHelp(string cmdID); + IHTMLElement CreateElement(string eTag); + void SetOnhelp(Object p); + Object GetOnhelp(); + void SetOnclick(Object p); + Object GetOnclick(); + void SetOndblclick(Object p); + Object GetOndblclick(); + void SetOnkeyup(Object p); + Object GetOnkeyup(); + void SetOnkeydown(Object p); + Object GetOnkeydown(); + void SetOnkeypress(Object p); + Object GetOnkeypress(); + void SetOnmouseup(Object p); + Object GetOnmouseup(); + void SetOnmousedown(Object p); + Object GetOnmousedown(); + void SetOnmousemove(Object p); + Object GetOnmousemove(); + void SetOnmouseout(Object p); + Object GetOnmouseout(); + void SetOnmouseover(Object p); + Object GetOnmouseover(); + void SetOnreadystatechange(Object p); + Object GetOnreadystatechange(); + void SetOnafterupdate(Object p); + Object GetOnafterupdate(); + void SetOnrowexit(Object p); + Object GetOnrowexit(); + void SetOnrowenter(Object p); + Object GetOnrowenter(); + void SetOndragstart(Object p); + Object GetOndragstart(); + void SetOnselectstart(Object p); + Object GetOnselectstart(); + IHTMLElement ElementFromPoint(int x, int y); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLWindow2 GetParentWindow(); + [return: MarshalAs(UnmanagedType.Interface)] object GetStyleSheets(); + void SetOnbeforeupdate(Object p); + Object GetOnbeforeupdate(); + void SetOnerrorupdate(Object p); + Object GetOnerrorupdate(); + string toString(); + [return: MarshalAs(UnmanagedType.Interface)] object CreateStyleSheet(string bstrHref,int lIndex); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F485-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLDocument3 { + void ReleaseCapture(); + void Recalc([In] bool fForce); + object CreateTextNode([In] string text); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetDocumentElement(); + string GetUniqueID(); + bool AttachEvent([In] string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + void DetachEvent([In] string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + void SetOnrowsdelete([In] Object p); + Object GetOnrowsdelete(); + void SetOnrowsinserted([In] Object p); + Object GetOnrowsinserted(); + void SetOncellchange([In] Object p); + Object GetOncellchange(); + void SetOndatasetchanged([In] Object p); + Object GetOndatasetchanged(); + void SetOndataavailable([In] Object p); + Object GetOndataavailable(); + void SetOndatasetcomplete([In] Object p); + Object GetOndatasetcomplete(); + void SetOnpropertychange([In] Object p); + Object GetOnpropertychange(); + void SetDir([In] string p); + string GetDir(); + void SetOncontextmenu([In] Object p); + Object GetOncontextmenu(); + void SetOnstop([In] Object p); + Object GetOnstop(); + object CreateDocumentFragment(); + object GetParentDocument(); + void SetEnableDownload([In] bool p); + bool GetEnableDownload(); + void SetBaseUrl([In] string p); + string GetBaseUrl(); + [return: MarshalAs(UnmanagedType.IDispatch)] object GetChildNodes(); + void SetInheritStyleSheets([In] bool p); + bool GetInheritStyleSheets(); + void SetOnbeforeeditfocus([In] Object p); + Object GetOnbeforeeditfocus(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElementCollection GetElementsByName([In] string v); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetElementById([In] string v); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElementCollection GetElementsByTagName([In] string v); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F69A-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLDocument4 { + void Focus(); + bool HasFocus(); + void SetOnselectionchange(object p); + object GetOnselectionchange(); + object GetNamespaces(); + object createDocumentFromUrl(string bstrUrl, string bstrOptions); + void SetMedia(string bstrMedia); + string GetMedia(); + object CreateEventObject([In, Optional] ref object eventObject); + bool FireEvent(string eventName); + object CreateRenderStyle(string bstr); + void SetOncontrolselect(object p); + object GetOncontrolselect(); + string GetURLUnencoded(); + } + + + [ComImport(), Guid("3050f613-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLDocumentEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1026)] bool onstop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1027)] void onbeforeeditfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1037)] void onselectionchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("332C4426-26CB-11D0-B483-00C04FD90119"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLFramesCollection2 { + object Item(ref object idOrName); + int GetLength(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("332C4427-26CB-11D0-B483-00C04FD90119"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLWindow2 { + [return: MarshalAs(UnmanagedType.IDispatch)] object Item([In] ref object pvarIndex); + int GetLength(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLFramesCollection2 GetFrames(); + void SetDefaultStatus([In] string p); + string GetDefaultStatus(); + void SetStatus([In] string p); + string GetStatus(); + int SetTimeout([In] string expression, [In] int msec, [In] ref Object language); + void ClearTimeout([In] int timerID); + void Alert([In] string message); + bool Confirm([In] string message); + [return: MarshalAs(UnmanagedType.Struct)] object Prompt([In] string message, [In] string defstr); + object GetImage(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLLocation GetLocation(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IOmHistory GetHistory(); + void Close(); + void SetOpener([In] object p); + [return: MarshalAs(UnmanagedType.IDispatch)] object GetOpener(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IOmNavigator GetNavigator(); + void SetName([In] string p); + string GetName(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLWindow2 GetParent(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLWindow2 Open([In] string URL, [In] string name, [In] string features, [In] bool replace); + object GetSelf(); + object GetTop(); + object GetWindow(); + void Navigate([In] string URL); + void SetOnfocus([In] Object p); + Object GetOnfocus(); + void SetOnblur([In] Object p); + Object GetOnblur(); + void SetOnload([In] Object p); + Object GetOnload(); + void SetOnbeforeunload(Object p); + Object GetOnbeforeunload(); + void SetOnunload([In] Object p); + Object GetOnunload(); + void SetOnhelp(Object p); + Object GetOnhelp(); + void SetOnerror([In] Object p); + Object GetOnerror(); + void SetOnresize([In] Object p); + Object GetOnresize(); + void SetOnscroll([In] Object p); + Object GetOnscroll(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLDocument2 GetDocument(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLEventObj GetEvent(); + object Get_newEnum(); + Object ShowModalDialog([In] string dialog, [In] ref Object varArgIn, [In] ref Object varOptions); + void ShowHelp([In] string helpURL, [In] Object helpArg, [In] string features); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLScreen GetScreen(); + object GetOption(); + void Focus(); + bool GetClosed(); + void Blur(); + void Scroll([In] int x, [In] int y); + object GetClientInformation(); + int SetInterval([In] string expression, [In] int msec, [In] ref Object language); + void ClearInterval([In] int timerID); + void SetOffscreenBuffering([In] Object p); + Object GetOffscreenBuffering(); + [return: MarshalAs(UnmanagedType.Struct)] Object ExecScript([In] string code, [In] string language); + string toString(); + void ScrollBy([In] int x, [In] int y); + void ScrollTo([In] int x, [In] int y); + void MoveTo([In] int x, [In] int y); + void MoveBy([In] int x, [In] int y); + void ResizeTo([In] int x, [In] int y); + void ResizeBy([In] int x, [In] int y); + object GetExternal(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f4ae-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLWindow3 { + int GetScreenLeft(); + int GetScreenTop(); + bool AttachEvent(string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + void DetachEvent(string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + int SetTimeout([In]ref object expression, int msec, [In] ref object language); + int SetInterval([In]ref object expression, int msec, [In] ref object language); + void Print(); + void SetBeforePrint(object o); + object GetBeforePrint(); + void SetAfterPrint(object o); + object GetAfterPrint(); + object GetClipboardData(); + object ShowModelessDialog(string url, object varArgIn, object options); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f6cf-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLWindow4 { + [return: MarshalAs(UnmanagedType.IDispatch)] object CreatePopup([In] ref object reserved); + [return: MarshalAs(UnmanagedType.Interface)] object frameElement(); + } + + + [ComImport(), Guid("3050f625-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLWindowEvents2 { + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1008)] void onunload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)] bool onerror(string description, string url, int line); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1017)] void onbeforeunload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1024)] void onbeforeprint(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1025)] void onafterprint(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f666-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLPopup { + void show(int x, int y, int w, int h, ref object element); + void hide(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLDocument GetDocument(); + bool IsOpen(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f35c-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLScreen { + int GetColorDepth(); + void SetBufferDepth(int d); + int GetBufferDepth(); + int GetWidth(); + int GetHeight(); + void SetUpdateInterval(int i); + int GetUpdateInterval(); + int GetAvailHeight(); + int GetAvailWidth(); + bool GetFontSmoothingEnabled(); + } + + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("163BB1E0-6E00-11CF-837A-48DC04C10000"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLLocation { + void SetHref([In] string p); + string GetHref(); + void SetProtocol([In] string p); + string GetProtocol(); + void SetHost([In] string p); + string GetHost(); + void SetHostname([In] string p); + string GetHostname(); + void SetPort([In] string p); + string GetPort(); + void SetPathname([In] string p); + string GetPathname(); + void SetSearch([In] string p); + string GetSearch(); + void SetHash([In] string p); + string GetHash(); + void Reload([In] bool flag); + void Replace([In] string bstr); + void Assign([In] string bstr); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("FECEAAA2-8405-11CF-8BA1-00AA00476DA6"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IOmHistory { + short GetLength(); + void Back(); + void Forward(); + void Go([In] ref Object pvargdistance); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("FECEAAA5-8405-11CF-8BA1-00AA00476DA6"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IOmNavigator { + string GetAppCodeName(); + string GetAppName(); + string GetAppVersion(); + string GetUserAgent(); + bool JavaEnabled(); + bool TaintEnabled(); + object GetMimeTypes(); + object GetPlugins(); + bool GetCookieEnabled(); + object GetOpsProfile(); + string GetCpuClass(); + string GetSystemLanguage(); + string GetBrowserLanguage(); + string GetUserLanguage(); + string GetPlatform(); + string GetAppMinorVersion(); + int GetConnectionSpeed(); + bool GetOnLine(); + object GetUserProfile(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F32D-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLEventObj { + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetSrcElement(); + bool GetAltKey(); + bool GetCtrlKey(); + bool GetShiftKey(); + void SetReturnValue(object p); + object GetReturnValue(); + void SetCancelBubble(bool p); + bool GetCancelBubble(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetFromElement(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetToElement(); + void SetKeyCode([In] int p); + int GetKeyCode(); + int GetButton(); + string GetEventType(); + string GetQualifier(); + int GetReason(); + int GetX(); + int GetY(); + int GetClientX(); + int GetClientY(); + int GetOffsetX(); + int GetOffsetY(); + int GetScreenX(); + int GetScreenY(); + object GetSrcFilter(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f48B-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLEventObj2 { + void SetAttribute(string attributeName, object attributeValue, int lFlags); + object GetAttribute(string attributeName, int lFlags); + bool RemoveAttribute(string attributeName, int lFlags); + void SetPropertyName(string name); + string GetPropertyName(); + void SetBookmarks(ref object bm); + object GetBookmarks(); + void SetRecordset(ref object rs); + object GetRecordset(); + void SetDataFld(string df); + string GetDataFld(); + void SetBoundElements(ref object be); + object GetBoundElements(); + void SetRepeat(bool r); + bool GetRepeat(); + void SetSrcUrn(string urn); + string GetSrcUrn(); + void SetSrcElement(ref object se); + object GetSrcElement(); + void SetAltKey(bool alt); + bool GetAltKey(); + void SetCtrlKey(bool ctrl); + bool GetCtrlKey(); + void SetShiftKey(bool shift); + bool GetShiftKey(); + void SetFromElement(ref object element); + object GetFromElement(); + void SetToElement(ref object element); + object GetToElement(); + void SetButton(int b); + int GetButton(); + void SetType(string type); + string GetType(); + void SetQualifier(string q); + string GetQualifier(); + void SetReason(int r); + int GetReason(); + void SetX(int x); + int GetX(); + void SetY(int y); + int GetY(); + void SetClientX(int x); + int GetClientX(); + void SetClientY(int y); + int GetClientY(); + void SetOffsetX(int x); + int GetOffsetX(); + void SetOffsetY(int y); + int GetOffsetY(); + void SetScreenX(int x); + int GetScreenX(); + void SetScreenY(int y); + int GetScreenY(); + void SetSrcFilter(ref object filter); + object GetSrcFilter(); + object GetDataTransfer(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f814-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLEventObj4 { + int GetWheelDelta(); + }; + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F21F-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLElementCollection { + string toString(); + void SetLength(int p); + int GetLength(); + [return: MarshalAs(UnmanagedType.Interface)] object Get_newEnum(); + [return: MarshalAs(UnmanagedType.IDispatch)] object Item(object idOrName, object index); + [return: MarshalAs(UnmanagedType.Interface)] object Tags(object tagName); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F1FF-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLElement { + void SetAttribute(string attributeName, object attributeValue, int lFlags); + object GetAttribute(string attributeName, int lFlags); + bool RemoveAttribute(string strAttributeName, int lFlags); + void SetClassName(string p); + string GetClassName(); + void SetId(string p); + string GetId(); + string GetTagName(); + IHTMLElement GetParentElement(); + IHTMLStyle GetStyle(); + void SetOnhelp(Object p); + Object GetOnhelp(); + void SetOnclick(Object p); + Object GetOnclick(); + void SetOndblclick(Object p); + Object GetOndblclick(); + void SetOnkeydown(Object p); + Object GetOnkeydown(); + void SetOnkeyup(Object p); + Object GetOnkeyup(); + void SetOnkeypress(Object p); + Object GetOnkeypress(); + void SetOnmouseout(Object p); + Object GetOnmouseout(); + void SetOnmouseover(Object p); + Object GetOnmouseover(); + void SetOnmousemove(Object p); + Object GetOnmousemove(); + void SetOnmousedown(Object p); + Object GetOnmousedown(); + void SetOnmouseup(Object p); + Object GetOnmouseup(); + [return: MarshalAs(UnmanagedType.Interface)] IHTMLDocument2 GetDocument(); + void SetTitle(string p); + string GetTitle(); + void SetLanguage(string p); + string GetLanguage(); + void SetOnselectstart(Object p); + Object GetOnselectstart(); + void ScrollIntoView(object varargStart); + bool Contains(IHTMLElement pChild); + int GetSourceIndex(); + Object GetRecordNumber(); + void SetLang(string p); + string GetLang(); + int GetOffsetLeft(); + int GetOffsetTop(); + int GetOffsetWidth(); + int GetOffsetHeight(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement GetOffsetParent(); + void SetInnerHTML(string p); + string GetInnerHTML(); + void SetInnerText(string p); + string GetInnerText(); + void SetOuterHTML(string p); + string GetOuterHTML(); + void SetOuterText(string p); + string GetOuterText(); + void InsertAdjacentHTML(string @where, + string html); + void InsertAdjacentText(string @where, + string text); + IHTMLElement GetParentTextEdit(); + bool GetIsTextEdit(); + void Click(); + [return: MarshalAs(UnmanagedType.Interface)] object GetFilters(); + void SetOndragstart(Object p); + Object GetOndragstart(); + string toString(); + void SetOnbeforeupdate(Object p); + Object GetOnbeforeupdate(); + void SetOnafterupdate(Object p); + Object GetOnafterupdate(); + void SetOnerrorupdate(Object p); + Object GetOnerrorupdate(); + void SetOnrowexit(Object p); + Object GetOnrowexit(); + void SetOnrowenter(Object p); + Object GetOnrowenter(); + void SetOndatasetchanged(Object p); + Object GetOndatasetchanged(); + void SetOndataavailable(Object p); + Object GetOndataavailable(); + void SetOndatasetcomplete(Object p); + Object GetOndatasetcomplete(); + void SetOnfilterchange(Object p); + Object GetOnfilterchange(); + [return: MarshalAs(UnmanagedType.IDispatch)] object GetChildren(); + [return: MarshalAs(UnmanagedType.IDispatch)] object GetAll(); + } + + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f434-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLElement2 { + string ScopeName(); + void SetCapture(bool containerCapture); + void ReleaseCapture(); + void SetOnLoseCapture(object v); + object GetOnLoseCapture(); + string GetComponentFromPoint(int x, int y); + void DoScroll(object component); + void SetOnScroll(object v); + object GetOnScroll(); + void SetOnDrag(object v); + object GetOnDrag(); + void SetOnDragEnd(object v); + object GetOnDragEnd(); + void SetOnDragEnter(object v); + object GetOnDragEnter(); + void SetOnDragOver(object v); + object GetOnDragOver(); + void SetOnDragleave(object v); + object GetOnDragLeave(); + void SetOnDrop(object v); + object GetOnDrop(); + void SetOnBeforeCut(object v); + object GetOnBeforeCut(); + void SetOnCut(object v); + object GetOnCut(); + void SetOnBeforeCopy(object v); + object GetOnBeforeCopy(); + void SetOnCopy(object v); + object GetOnCopy(object p); + void SetOnBeforePaste(object v); + object GetOnBeforePaste(object p); + void SetOnPaste(object v); + object GetOnPaste(object p); + object GetCurrentStyle(); + void SetOnPropertyChange(object v); + object GetOnPropertyChange(object p); + object GetClientRects(); + object GetBoundingClientRect(); + void SetExpression(string propName, string expression, string language); + object GetExpression(string propName); + bool RemoveExpression(string propName); + void SetTabIndex(int v); + short GetTabIndex(); + void Focus(); + void SetAccessKey(string v); + string GetAccessKey(); + void SetOnBlur(object v); + object GetOnBlur(); + void SetOnFocus(object v); + object GetOnFocus(); + void SetOnResize(object v); + object GetOnResize(); + void Blur(); + void AddFilter(object pUnk); + void RemoveFilter(object pUnk); + int ClientHeight(); + int ClientWidth(); + int ClientTop(); + int ClientLeft(); + bool AttachEvent(string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + void DetachEvent(string ev, [In, MarshalAs(UnmanagedType.IDispatch)] object pdisp); + object ReadyState(); + void SetOnReadyStateChange(object v); + object GetOnReadyStateChange(); + void SetOnRowsDelete(object v); + object GetOnRowsDelete(); + void SetOnRowsInserted(object v); + object GetOnRowsInserted(); + void SetOnCellChange(object v); + object GetOnCellChange(); + void SetDir(string v); + string GetDir(); + object CreateControlRange(); + int GetScrollHeight(); + int GetScrollWidth(); + void SetScrollTop(int v); + int GetScrollTop(); + void SetScrollLeft(int v); + int GetScrollLeft(); + void ClearAttributes(); + void MergeAttributes(object mergeThis); + void SetOnContextMenu(object v); + object GetOnContextMenu(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement + InsertAdjacentElement(string @where, + [In, MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement insertedElement); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement + applyElement([In, MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElement apply, + string @where); + string GetAdjacentText(string @where); + string ReplaceAdjacentText(string @where,string newText); + bool CanHaveChildren(); + int AddBehavior(string url, ref object oFactory); + bool RemoveBehavior(int cookie); + object GetRuntimeStyle(); + object GetBehaviorUrns(); + void SetTagUrn(string v); + string GetTagUrn(); + void SetOnBeforeEditFocus(object v); + object GetOnBeforeEditFocus(); + int GetReadyStateValue(); + [return: MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IHTMLElementCollection GetElementsByTagName(string v); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f673-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLElement3 { + void MergeAttributes(object mergeThis, object pvarFlags); + bool IsMultiLine(); + bool CanHaveHTML(); + void SetOnLayoutComplete(object v); + object GetOnLayoutComplete(); + void SetOnPage(object v); + object GetOnPage(); + void SetInflateBlock(bool v); + bool GetInflateBlock(); + void SetOnBeforeDeactivate(object v); + object GetOnBeforeDeactivate(); + void SetActive(); + void SetContentEditable(string v); + string GetContentEditable(); + bool IsContentEditable(); + void SetHideFocus(bool v); + bool GetHideFocus(); + void SetDisabled(bool v); + bool GetDisabled(); + bool IsDisabled(); + void SetOnMove(object v); + object GetOnMove(); + void SetOnControlSelect(object v); + object GetOnControlSelect(); + bool FireEvent(string bstrEventName, IntPtr pvarEventObject); + void SetOnResizeStart(object v); + object GetOnResizeStart(); + void SetOnResizeEnd(object v); + object GetOnResizeEnd(); + void SetOnMoveStart(object v); + object GetOnMoveStart(); + void SetOnMoveEnd(object v); + object GetOnMoveEnd(); + void SetOnMouseEnter(object v); + object GetOnMouseEnter(); + void SetOnMouseLeave(object v); + object GetOnMouseLeave(); + void SetOnActivate(object v); + object GetOnActivate(); + void SetOnDeactivate(object v); + object GetOnDeactivate(); + bool DragDrop(); + int GlyphMode(); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050f5da-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + public interface IHTMLDOMNode { + long GetNodeType(); + IHTMLDOMNode GetParentNode(); + bool HasChildNodes(); + object GetChildNodes(); + object GetAttributes(); + IHTMLDOMNode InsertBefore(IHTMLDOMNode newChild, object refChild); + IHTMLDOMNode RemoveChild(IHTMLDOMNode oldChild); + IHTMLDOMNode ReplaceChild(IHTMLDOMNode newChild, IHTMLDOMNode oldChild); + IHTMLDOMNode CloneNode(bool fDeep); + IHTMLDOMNode RemoveNode(bool fDeep); + IHTMLDOMNode SwapNode(IHTMLDOMNode otherNode); + IHTMLDOMNode ReplaceNode(IHTMLDOMNode replacement); + IHTMLDOMNode AppendChild(IHTMLDOMNode newChild); + string NodeName(); + void SetNodeValue(object v); + object GetNodeValue(); + IHTMLDOMNode FirstChild(); + IHTMLDOMNode LastChild(); + IHTMLDOMNode PreviousSibling(); + IHTMLDOMNode NextSibling(); + }; + + [ComImport(), Guid("3050f60f-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f610-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLAnchorEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f611-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLAreaEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f617-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLButtonElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f612-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLControlElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f614-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLFormElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1007)] bool onsubmit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1015)] bool onreset(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f7ff-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLFrameSiteEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f616-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLImgEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1000)] void onabort(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61a-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLInputFileElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412082)] bool onchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412102)] void onselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1000)] void onabort(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61b-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLInputImageEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412080)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412083)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412084)] void onabort(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f618-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLInputTextElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1001)] bool onchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1006)] void onselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1001)] void onabort(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61c-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLLabelEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61d-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLLinkElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412080)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412083)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61e-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLMapEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f61f-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLMarqueeElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412092)] void onbounce(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412086)] void onfinish(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412085)] void onstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f619-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLOptionButtonElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412082)] bool onchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f622-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLSelectElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147412082)] void onchange_void(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f615-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLStyleElementEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1003)] void onload(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)] void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f623-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLTableEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f624-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLTextContainerEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1001)] void onchange_void(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1006)] void onselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [ComImport(), Guid("3050f621-98b5-11cf-bb82-00aa00bdce0b"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), + TypeLibType(TypeLibTypeFlags.FHidden)] + public interface DHTMLScriptEvents2 { + [DispId(-2147418102)] bool onhelp(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-600)] bool onclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-601)] bool ondblclick(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-603)] bool onkeypress(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-602)] void onkeydown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-604)] void onkeyup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418103)] void onmouseout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418104)] void onmouseover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-606)] void onmousemove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-605)] void onmousedown(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-607)] void onmouseup(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418100)] bool onselectstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418095)] void onfilterchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418101)] bool ondragstart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418108)] bool onbeforeupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418107)] void onafterupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418099)] bool onerrorupdate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418106)] bool onrowexit(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418105)] void onrowenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418098)] void ondatasetchanged(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418097)] void ondataavailable(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418096)] void ondatasetcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418094)] void onlosecapture(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418093)] void onpropertychange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1014)] void onscroll(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418111)] void onfocus(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418112)] void onblur(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1016)] void onresize(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418092)] bool ondrag(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418091)] void ondragend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418090)] bool ondragenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418089)] bool ondragover(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418088)] void ondragleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418087)] bool ondrop(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418083)] bool onbeforecut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418086)] bool oncut(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418082)] bool onbeforecopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418085)] bool oncopy(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418081)] bool onbeforepaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418084)] bool onpaste(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1023)] bool oncontextmenu(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418080)] void onrowsdelete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418079)] void onrowsinserted(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-2147418078)] void oncellchange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(-609)] void onreadystatechange(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1030)] void onlayoutcomplete(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1031)] void onpage(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1042)] void onmouseenter(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1043)] void onmouseleave(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1044)] void onactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1045)] void ondeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1034)] bool onbeforedeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1047)] bool onbeforeactivate(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1048)] void onfocusin(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1049)] void onfocusout(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1035)] void onmove(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1036)] bool oncontrolselect(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1038)] bool onmovestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1039)] void onmoveend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1040)] bool onresizestart(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1041)] void onresizeend(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1033)] bool onmousewheel(UnsafeNativeMethods.IHTMLEventObj evtObj); + [DispId(1002)]void onerror(UnsafeNativeMethods.IHTMLEventObj evtObj); + } + + [SuppressUnmanagedCodeSecurity, ComVisible(true), Guid("3050F25E-98B5-11CF-BB82-00AA00BDCE0B"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + internal interface IHTMLStyle { + void SetFontFamily(string p); + string GetFontFamily(); + void SetFontStyle(string p); + string GetFontStyle(); + void SetFontObject(string p); + string GetFontObject(); + void SetFontWeight(string p); + string GetFontWeight(); + void SetFontSize(object p); + object GetFontSize(); + void SetFont(string p); + string GetFont(); + void SetColor(object p); + object GetColor(); + void SetBackground(string p); + string GetBackground(); + void SetBackgroundColor(object p); + object GetBackgroundColor(); + void SetBackgroundImage(string p); + string GetBackgroundImage(); + void SetBackgroundRepeat(string p); + string GetBackgroundRepeat(); + void SetBackgroundAttachment(string p); + string GetBackgroundAttachment(); + void SetBackgroundPosition(string p); + string GetBackgroundPosition(); + void SetBackgroundPositionX(object p); + object GetBackgroundPositionX(); + void SetBackgroundPositionY(object p); + object GetBackgroundPositionY(); + void SetWordSpacing(object p); + object GetWordSpacing(); + void SetLetterSpacing(object p); + object GetLetterSpacing(); + void SetTextDecoration(string p); + string GetTextDecoration(); + void SetTextDecorationNone(bool p); + bool GetTextDecorationNone(); + void SetTextDecorationUnderline(bool p); + bool GetTextDecorationUnderline(); + void SetTextDecorationOverline(bool p); + bool GetTextDecorationOverline(); + void SetTextDecorationLineThrough(bool p); + bool GetTextDecorationLineThrough(); + void SetTextDecorationBlink(bool p); + bool GetTextDecorationBlink(); + void SetVerticalAlign(object p); + object GetVerticalAlign(); + void SetTextTransform(string p); + string GetTextTransform(); + void SetTextAlign(string p); + string GetTextAlign(); + void SetTextIndent(object p); + object GetTextIndent(); + void SetLineHeight(object p); + object GetLineHeight(); + void SetMarginTop(object p); + object GetMarginTop(); + void SetMarginRight(object p); + object GetMarginRight(); + void SetMarginBottom(object p); + object GetMarginBottom(); + void SetMarginLeft(object p); + object GetMarginLeft(); + void SetMargin(string p); + string GetMargin(); + void SetPaddingTop(object p); + object GetPaddingTop(); + void SetPaddingRight(object p); + object GetPaddingRight(); + void SetPaddingBottom(object p); + object GetPaddingBottom(); + void SetPaddingLeft(object p); + object GetPaddingLeft(); + void SetPadding(string p); + string GetPadding(); + void SetBorder(string p); + string GetBorder(); + void SetBorderTop(string p); + string GetBorderTop(); + void SetBorderRight(string p); + string GetBorderRight(); + void SetBorderBottom(string p); + string GetBorderBottom(); + void SetBorderLeft(string p); + string GetBorderLeft(); + void SetBorderColor(string p); + string GetBorderColor(); + void SetBorderTopColor(object p); + object GetBorderTopColor(); + void SetBorderRightColor(object p); + object GetBorderRightColor(); + void SetBorderBottomColor(object p); + object GetBorderBottomColor(); + void SetBorderLeftColor(object p); + object GetBorderLeftColor(); + void SetBorderWidth(string p); + string GetBorderWidth(); + void SetBorderTopWidth(object p); + object GetBorderTopWidth(); + void SetBorderRightWidth(object p); + object GetBorderRightWidth(); + void SetBorderBottomWidth(object p); + object GetBorderBottomWidth(); + void SetBorderLeftWidth(object p); + object GetBorderLeftWidth(); + void SetBorderStyle(string p); + string GetBorderStyle(); + void SetBorderTopStyle(string p); + string GetBorderTopStyle(); + void SetBorderRightStyle(string p); + string GetBorderRightStyle(); + void SetBorderBottomStyle(string p); + string GetBorderBottomStyle(); + void SetBorderLeftStyle(string p); + string GetBorderLeftStyle(); + void SetWidth(object p); + object GetWidth(); + void SetHeight(object p); + object GetHeight(); + void SetStyleFloat(string p); + string GetStyleFloat(); + void SetClear(string p); + string GetClear(); + void SetDisplay(string p); + string GetDisplay(); + void SetVisibility(string p); + string GetVisibility(); + void SetListStyleType(string p); + string GetListStyleType(); + void SetListStylePosition(string p); + string GetListStylePosition(); + void SetListStyleImage(string p); + string GetListStyleImage(); + void SetListStyle(string p); + string GetListStyle(); + void SetWhiteSpace(string p); + string GetWhiteSpace(); + void SetTop(object p); + object GetTop(); + void SetLeft(object p); + object GetLeft(); + string GetPosition(); + void SetZIndex(object p); + object GetZIndex(); + void SetOverflow(string p); + string GetOverflow(); + void SetPageBreakBefore(string p); + string GetPageBreakBefore(); + void SetPageBreakAfter(string p); + string GetPageBreakAfter(); + void SetCssText(string p); + string GetCssText(); + void SetPixelTop(int p); + int GetPixelTop(); + void SetPixelLeft(int p); + int GetPixelLeft(); + void SetPixelWidth(int p); + int GetPixelWidth(); + void SetPixelHeight(int p); + int GetPixelHeight(); + void SetPosTop(float p); + float GetPosTop(); + void SetPosLeft(float p); + float GetPosLeft(); + void SetPosWidth(float p); + float GetPosWidth(); + void SetPosHeight(float p); + float GetPosHeight(); + void SetCursor(string p); + string GetCursor(); + void SetClip(string p); + string GetClip(); + void SetFilter(string p); + string GetFilter(); + void SetAttribute(string strAttributeName, object AttributeValue, int lFlags); + object GetAttribute(string strAttributeName, int lFlags); + bool RemoveAttribute(string strAttributeName, int lFlags); + } + + [ComImport(), + Guid("39088D7E-B71E-11D1-8F39-00C04FD946D0"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IExtender { + + int Align {get; set;} + + bool Enabled {get; set;} + + int Height {get; set;} + + int Left {get; set;} + + bool TabStop {get; set;} + + int Top {get; set;} + + bool Visible {get; set;} + + int Width {get; set;} + + string Name {[return: MarshalAs(UnmanagedType.BStr)]get;} + + object Parent {[return: MarshalAs(UnmanagedType.Interface)]get;} + + IntPtr Hwnd {get;} + + object Container {[return: MarshalAs(UnmanagedType.Interface)]get;} + + void Move( + [In, MarshalAs(UnmanagedType.Interface)] + object left, + [In, MarshalAs(UnmanagedType.Interface)] + object top, + [In, MarshalAs(UnmanagedType.Interface)] + object width, + [In, MarshalAs(UnmanagedType.Interface)] + object height); + } + + [ComImport(), + Guid("8A701DA0-4FEB-101B-A82E-08002B2B2337"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IGetOleObject { + [return: MarshalAs(UnmanagedType.Interface)] + object GetOleObject(ref Guid riid); + } + + [ + ComImport(), + Guid("CB2F6722-AB3A-11d2-9C40-00C04FA30A3E"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + internal interface ICorRuntimeHost { + + [PreserveSig()] int CreateLogicalThreadState(); + [PreserveSig()] int DeleteLogicalThreadState(); + [PreserveSig()] int SwitchInLogicalThreadState( + [In] ref uint pFiberCookie); // [in] Cookie that indicates the fiber to use. + + [PreserveSig()] int SwitchOutLogicalThreadState( + out uint FiberCookie); // [out] Cookie that indicates the fiber being switched out. + + [PreserveSig()] int LocksHeldByLogicalThread( // Return code. + out uint pCount // [out] Number of locks that the current thread holds. + ); + + [PreserveSig()] int MapFile( + IntPtr hFile, // [in] HANDLE for file + out IntPtr hMapAddress); // [out] HINSTANCE for mapped file + + //================================================================= + // + // New hosting methods + // + // Returns an object for configuring the runtime prior to + // it starting. If the runtime has been initialized this + // routine returns an error. See ICorConfiguration. + [PreserveSig()] int GetConfiguration([MarshalAs(UnmanagedType.IUnknown)] out object pConfiguration); + + // Starts the runtime. This is equivalent to CoInitializeCor(); + [PreserveSig()] int Start(); + + // Terminates the runtime, This is equivalent CoUninitializeCor(); + [PreserveSig()] int Stop(); + + // Creates a domain in the runtime. The identity array is + // a pointer to an array TYPE containing IIdentity objects defining + // the security identity. + [PreserveSig()] int CreateDomain(string pwzFriendlyName, + [MarshalAs(UnmanagedType.IUnknown)] object pIdentityArray, // Optional + [MarshalAs(UnmanagedType.IUnknown)] out object pAppDomain); + + // Returns the default domain. + [PreserveSig()] int GetDefaultDomain([MarshalAs(UnmanagedType.IUnknown)] out object pAppDomain); + + + // Enumerate currently existing domains. + [PreserveSig()] int EnumDomains(out IntPtr hEnum); + + // Returns S_FALSE when there are no more domains. A domain + // is passed out only when S_OK is returned. + [PreserveSig()] int NextDomain(IntPtr hEnum, + [MarshalAs(UnmanagedType.IUnknown)] out object pAppDomain); + + // Close the enumeration, releasing resources + [PreserveSig()] int CloseEnum(IntPtr hEnum); + + [PreserveSig()] int CreateDomainEx(string pwzFriendlyName, // Optional + [MarshalAs(UnmanagedType.IUnknown)] object pSetup, // Optional + [MarshalAs(UnmanagedType.IUnknown)] object pEvidence, // Optional + [MarshalAs(UnmanagedType.IUnknown)] out object pAppDomain); + + [PreserveSig()] int CreateDomainSetup([MarshalAs(UnmanagedType.IUnknown)] out object pAppDomainSetup); + + [PreserveSig()] int CreateEvidence([MarshalAs(UnmanagedType.IUnknown)] out object pEvidence); + + [PreserveSig()] int UnloadDomain([MarshalAs(UnmanagedType.IUnknown)] object pAppDomain); + + // Returns the thread's domain. + [PreserveSig()] int CurrentDomain([MarshalAs(UnmanagedType.IUnknown)] out object pAppDomain); + } + + [ + ComImport(), + Guid("CB2F6723-AB3A-11d2-9C40-00C04FA30A3E") + ] + internal class CorRuntimeHost + { + } + + [ComImport(), + Guid("000C0601-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IMsoComponentManager { + + /// + /// + /// Return in *ppvObj an implementation of interface iid for service + /// guidService (same as IServiceProvider::QueryService). + /// Return NOERROR if the requested service is supported, otherwise return + /// NULL in *ppvObj and an appropriate error (eg E_FAIL, E_NOINTERFACE). + /// + [PreserveSig] + int QueryService( + ref Guid guidService, + ref Guid iid, + [MarshalAs(UnmanagedType.Interface)] + out object ppvObj); + + /// + /// + /// Standard FDebugMessage method. + /// Since IMsoComponentManager is a reference counted interface, + /// MsoDWGetChkMemCounter should be used when processing the + /// msodmWriteBe message. + /// + [PreserveSig] + bool FDebugMessage( + IntPtr hInst, + int msg, + IntPtr wParam, + IntPtr lParam); + + /// + /// + /// Register component piComponent and its registration info pcrinfo with + /// this component manager. Return in *pdwComponentID a cookie which will + /// identify the component when it calls other IMsoComponentManager + /// methods. + /// Return TRUE if successful, FALSE otherwise. + /// + [PreserveSig] + bool FRegisterComponent( + IMsoComponent component, + NativeMethods.MSOCRINFOSTRUCT pcrinfo, + out IntPtr dwComponentID); + + /// + /// + /// Undo the registration of the component identified by dwComponentID + /// (the cookie returned from the FRegisterComponent method). + /// Return TRUE if successful, FALSE otherwise. + /// + [PreserveSig] + bool FRevokeComponent(IntPtr dwComponentID); + + /// + /// + /// Update the registration info of the component identified by + /// dwComponentID (the cookie returned from FRegisterComponent) with the + /// new registration information pcrinfo. + /// Typically this is used to update the idle time registration data, but + /// can be used to update other registration data as well. + /// Return TRUE if successful, FALSE otherwise. + /// + [PreserveSig] + bool FUpdateComponentRegistration(IntPtr dwComponentID,NativeMethods.MSOCRINFOSTRUCT pcrinfo); + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) has been activated. + /// The active component gets the chance to process messages before they + /// are dispatched (via IMsoComponent::FPreTranslateMessage) and typically + /// gets first chance at idle time after the host. + /// This method fails if another component is already Exclusively Active. + /// In this case, FALSE is returned and SetLastError is set to + /// msoerrACompIsXActive (comp usually need not take any special action + /// in this case). + /// Return TRUE if successful. + /// + [PreserveSig] + bool FOnComponentActivate(IntPtr dwComponentID); + + /// + /// + /// Called to inform component manager that component identified by + /// dwComponentID (cookie returned from FRegisterComponent) wishes + /// to perform a tracking operation (such as mouse tracking). + /// The component calls this method with fTrack == TRUE to begin the + /// tracking operation and with fTrack == FALSE to end the operation. + /// During the tracking operation the component manager routes messages + /// to the tracking component (via IMsoComponent::FPreTranslateMessage) + /// rather than to the active component. When the tracking operation ends, + /// the component manager should resume routing messages to the active + /// component. + /// Note: component manager should perform no idle time processing during a + /// tracking operation other than give the tracking component idle + /// time via IMsoComponent::FDoIdle. + /// Note: there can only be one tracking component at a time. + /// Return TRUE if successful, FALSE otherwise. + /// + [PreserveSig] + bool FSetTrackingComponent(IntPtr dwComponentID, [In, MarshalAs(UnmanagedType.Bool)] bool fTrack); + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) is entering the state + /// identified by uStateID (msocstateXXX value). (For convenience when + /// dealing with sub CompMgrs, the host can call this method passing 0 for + /// dwComponentID.) + /// Component manager should notify all other interested components within + /// the state context indicated by uContext (a msoccontextXXX value), + /// excluding those within the state context of a CompMgr in rgpicmExclude, + /// via IMsoComponent::OnEnterState (see "Comments on State Contexts", + /// above). + /// Component Manager should also take appropriate action depending on the + /// value of uStateID (see msocstate comments, above). + /// dwReserved is reserved for future use and should be zero. + /// + /// rgpicmExclude (can be NULL) is an array of cpicmExclude CompMgrs (can + /// include root CompMgr and/or sub CompMgrs); components within the state + /// context of a CompMgr appearing in this array should NOT be notified of + /// the state change (note: if uContext is msoccontextMine, the only + /// CompMgrs in rgpicmExclude that are checked for exclusion are those that + /// are sub CompMgrs of this Component Manager, since all other CompMgrs + /// are outside of this Component Manager's state context anyway.) + /// + /// Note: Calls to this method are symmetric with calls to + /// FOnComponentExitState. + /// That is, if n OnComponentEnterState calls are made, the component is + /// considered to be in the state until n FOnComponentExitState calls are + /// made. Before revoking its registration a component must make a + /// sufficient number of FOnComponentExitState calls to offset any + /// outstanding OnComponentEnterState calls it has made. + /// + /// Note: inplace objects should not call this method with + /// uStateID == msocstateModal when entering modal state. Such objects + /// should call IOleInPlaceFrame::EnableModeless instead. + /// + [PreserveSig] + void OnComponentEnterState(IntPtr dwComponentID,int uStateID,int uContext,int cpicmExclude,/* IMsoComponentManger** */ int rgpicmExclude,int dwReserved); + + /// + /// + /// Notify component manager that component identified by dwComponentID + /// (cookie returned from FRegisterComponent) is exiting the state + /// identified by uStateID (a msocstateXXX value). (For convenience when + /// dealing with sub CompMgrs, the host can call this method passing 0 for + /// dwComponentID.) + /// uContext, cpicmExclude, and rgpicmExclude are as they are in + /// OnComponentEnterState. + /// Component manager should notify all appropriate interested components + /// (taking into account uContext, cpicmExclude, rgpicmExclude) via + /// IMsoComponent::OnEnterState (see "Comments on State Contexts", above). + /// Component Manager should also take appropriate action depending on + /// the value of uStateID (see msocstate comments, above). + /// Return TRUE if, at the end of this call, the state is still in effect + /// at the root of this component manager's state context + /// (because the host or some other component is still in the state), + /// otherwise return FALSE (ie. return what FInState would return). + /// Caller can normally ignore the return value. + /// + /// Note: n calls to this method are symmetric with n calls to + /// OnComponentEnterState (see OnComponentEnterState comments, above). + /// + [PreserveSig] + bool FOnComponentExitState( + IntPtr dwComponentID, + int uStateID, + int uContext, + int cpicmExclude, + /* IMsoComponentManager** */ int rgpicmExclude); + + /// + /// + /// Return TRUE if the state identified by uStateID (a msocstateXXX value) + /// is in effect at the root of this component manager's state context, + /// FALSE otherwise (see "Comments on State Contexts", above). + /// pvoid is reserved for future use and should be NULL. + /// + [PreserveSig] + bool FInState(int uStateID,/* PVOID */ IntPtr pvoid); + + /// + /// + /// Called periodically by a component during IMsoComponent::FDoIdle. + /// Return TRUE if component can continue its idle time processing, + /// FALSE if not (in which case component returns from FDoIdle.) + /// + [PreserveSig] + bool FContinueIdle(); + + /// + /// + /// Component identified by dwComponentID (cookie returned from + /// FRegisterComponent) wishes to push a message loop for reason uReason. + /// uReason is one the values from the msoloop enumeration (above). + /// pvLoopData is data private to the component. + /// The component manager should push its message loop, + /// calling IMsoComponent::FContinueMessageLoop(uReason, pvLoopData) + /// during each loop iteration (see IMsoComponent::FContinueMessageLoop + /// comments). When IMsoComponent::FContinueMessageLoop returns FALSE, the + /// component manager terminates the loop. + /// Returns TRUE if component manager terminates loop because component + /// told it to (by returning FALSE from IMsoComponent::FContinueMessageLoop), + /// FALSE if it had to terminate the loop for some other reason. In the + /// latter case, component should perform any necessary action (such as + /// cleanup). + /// + [PreserveSig] + bool FPushMessageLoop(IntPtr dwComponentID,int uReason,/* PVOID */ int pvLoopData); + + /// + /// + /// Cause the component manager to create a "sub" component manager, which + /// will be one of its children in the hierarchical tree of component + /// managers used to maintiain state contexts (see "Comments on State + /// Contexts", above). + /// piunkOuter is the controlling unknown (can be NULL), riid is the + /// desired IID, and *ppvObj returns the created sub component manager. + /// piunkServProv (can be NULL) is a ptr to an object supporting + /// IServiceProvider interface to which the created sub component manager + /// will delegate its IMsoComponentManager::QueryService calls. + /// (see objext.h or docobj.h for definition of IServiceProvider). + /// Returns TRUE if successful. + /// + [PreserveSig] + bool FCreateSubComponentManager( + [MarshalAs(UnmanagedType.Interface)] + object punkOuter, + [MarshalAs(UnmanagedType.Interface)] + object punkServProv, + ref Guid riid, + out IntPtr ppvObj); + + /// + /// + /// Return in *ppicm an AddRef'ed ptr to this component manager's parent + /// in the hierarchical tree of component managers used to maintain state + /// contexts (see "Comments on State Contexts", above). + /// Returns TRUE if the parent is returned, FALSE if no parent exists or + /// some error occurred. + /// + [PreserveSig] + bool FGetParentComponentManager( + out IMsoComponentManager ppicm); + + /// + /// + /// Return in *ppic an AddRef'ed ptr to the current active or tracking + /// component (as indicated by dwgac (a msogacXXX value)), and + /// its registration information in *pcrinfo. ppic and/or pcrinfo can be + /// NULL if caller is not interested these values. If pcrinfo is not NULL, + /// caller should set pcrinfo->cbSize before calling this method. + /// Returns TRUE if the component indicated by dwgac exists, FALSE if no + /// such component exists or some error occurred. + /// dwReserved is reserved for future use and should be zero. + /// + [PreserveSig] + bool FGetActiveComponent( + int dwgac, + [Out, MarshalAs(UnmanagedType.LPArray)] + IMsoComponent[] ppic, + NativeMethods.MSOCRINFOSTRUCT pcrinfo, + int dwReserved); + } + + [ComImport(), + Guid("000C0600-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IMsoComponent { + + /// + /// + /// Standard FDebugMessage method. + /// Since IMsoComponentManager is a reference counted interface, + /// MsoDWGetChkMemCounter should be used when processing the + /// msodmWriteBe message. + /// + [PreserveSig] + bool FDebugMessage( + IntPtr hInst, + int msg, + IntPtr wParam, + IntPtr lParam); + + /// + /// + /// Give component a chance to process the message pMsg before it is + /// translated and dispatched. Component can do TranslateAccelerator + /// do IsDialogMessage, modify pMsg, or take some other action. + /// Return TRUE if the message is consumed, FALSE otherwise. + /// + [PreserveSig] + bool FPreTranslateMessage(ref NativeMethods.MSG msg); + + /// + /// + /// Notify component when app enters or exits (as indicated by fEnter) + /// the state identified by uStateID (a value from olecstate enumeration). + /// Component should take action depending on value of uStateID + /// (see olecstate comments, above). + /// + /// Note: If n calls are made with TRUE fEnter, component should consider + /// the state to be in effect until n calls are made with FALSE fEnter. + /// + /// Note: Components should be aware that it is possible for this method to + /// be called with FALSE fEnter more times than it was called with TRUE + /// fEnter (so, for example, if component is maintaining a state counter + /// (incremented when this method is called with TRUE fEnter, decremented + /// when called with FALSE fEnter), the counter should not be decremented + /// for FALSE fEnter if it is already at zero.) + /// + [PreserveSig] + void OnEnterState( + int uStateID, + bool fEnter); + + /// + /// + /// Notify component when the host application gains or loses activation. + /// If fActive is TRUE, the host app is being activated and dwOtherThreadID + /// is the ID of the thread owning the window being deactivated. + /// If fActive is FALSE, the host app is being deactivated and + /// dwOtherThreadID is the ID of the thread owning the window being + /// activated. + /// Note: this method is not called when both the window being activated + /// and the one being deactivated belong to the host app. + /// + [PreserveSig] + void OnAppActivate( + bool fActive, + int dwOtherThreadID); + + /// + /// + /// Notify the active component that it has lost its active status because + /// the host or another component has become active. + /// + [PreserveSig] + void OnLoseActivation(); + + /// + /// + /// Notify component when a new object is being activated. + /// If pic is non-NULL, then it is the component that is being activated. + /// In this case, fSameComponent is TRUE if pic is the same component as + /// the callee of this method, and pcrinfo is the reg info of pic. + /// If pic is NULL and fHostIsActivating is TRUE, then the host is the + /// object being activated, and pchostinfo is its host info. + /// If pic is NULL and fHostIsActivating is FALSE, then there is no current + /// active object. + /// + /// If pic is being activated and pcrinfo->grf has the + /// olecrfExclusiveBorderSpace bit set, component should hide its border + /// space tools (toolbars, status bars, etc.); + /// component should also do this if host is activating and + /// pchostinfo->grfchostf has the olechostfExclusiveBorderSpace bit set. + /// In either of these cases, component should unhide its border space + /// tools the next time it is activated. + /// + /// if pic is being activated and pcrinfo->grf has the + /// olecrfExclusiveActivation bit is set, then pic is being activated in + /// "ExclusiveActive" mode. + /// Component should retrieve the top frame window that is hosting pic + /// (via pic->HwndGetWindow(olecWindowFrameToplevel, 0)). + /// If this window is different from component's own top frame window, + /// component should disable its windows and do other things it would do + /// when receiving OnEnterState(olecstateModal, TRUE) notification. + /// Otherwise, if component is top-level, + /// it should refuse to have its window activated by appropriately + /// processing WM_MOUSEACTIVATE (but see WM_MOUSEACTIVATE NOTE, above). + /// Component should remain in one of these states until the + /// ExclusiveActive mode ends, indicated by a future call to + /// OnActivationChange with ExclusiveActivation bit not set or with NULL + /// pcrinfo. + /// + [PreserveSig] + void OnActivationChange( + IMsoComponent component, + bool fSameComponent, + int pcrinfo, + bool fHostIsActivating, + int pchostinfo, + int dwReserved); + + /// + /// + /// Give component a chance to do idle time tasks. grfidlef is a group of + /// bit flags taken from the enumeration of oleidlef values (above), + /// indicating the type of idle tasks to perform. + /// Component may periodically call IOleComponentManager::FContinueIdle; + /// if this method returns FALSE, component should terminate its idle + /// time processing and return. + /// Return TRUE if more time is needed to perform the idle time tasks, + /// FALSE otherwise. + /// Note: If a component reaches a point where it has no idle tasks + /// and does not need FDoIdle calls, it should remove its idle task + /// registration via IOleComponentManager::FUpdateComponentRegistration. + /// Note: If this method is called on while component is performing a + /// tracking operation, component should only perform idle time tasks that + /// it deems are appropriate to perform during tracking. + /// + [PreserveSig] + bool FDoIdle( + int grfidlef); + + + /// + /// + /// Called during each iteration of a message loop that the component + /// pushed. uReason and pvLoopData are the reason and the component private + /// data that were passed to IOleComponentManager::FPushMessageLoop. + /// This method is called after peeking the next message in the queue + /// (via PeekMessage) but before the message is removed from the queue. + /// The peeked message is passed in the pMsgPeeked param (NULL if no + /// message is in the queue). This method may be additionally called when + /// the next message has already been removed from the queue, in which case + /// pMsgPeeked is passed as NULL. + /// Return TRUE if the message loop should continue, FALSE otherwise. + /// If FALSE is returned, the component manager terminates the loop without + /// removing pMsgPeeked from the queue. + /// + [PreserveSig] + bool FContinueMessageLoop( + int uReason, + int pvLoopData, + [MarshalAs(UnmanagedType.LPArray)] NativeMethods.MSG[] pMsgPeeked); + + + /// + /// + /// Called when component manager wishes to know if the component is in a + /// state in which it can terminate. If fPromptUser is FALSE, component + /// should simply return TRUE if it can terminate, FALSE otherwise. + /// If fPromptUser is TRUE, component should return TRUE if it can + /// terminate without prompting the user; otherwise it should prompt the + /// user, either 1.) asking user if it can terminate and returning TRUE + /// or FALSE appropriately, or 2.) giving an indication as to why it + /// cannot terminate and returning FALSE. + /// + [PreserveSig] + bool FQueryTerminate( + bool fPromptUser); + + /// + /// + /// Called when component manager wishes to terminate the component's + /// registration. Component should revoke its registration with component + /// manager, release references to component manager and perform any + /// necessary cleanup. + /// + [PreserveSig] + void Terminate(); + + /// + /// + /// Called to retrieve a window associated with the component, as specified + /// by dwWhich, a olecWindowXXX value (see olecWindow, above). + /// dwReserved is reserved for future use and should be zero. + /// Component should return the desired window or NULL if no such window + /// exists. + /// + + [PreserveSig] + IntPtr HwndGetWindow( + int dwWhich, + int dwReserved); + } + + [ComVisible(true), Guid("8CC497C0-A1DF-11ce-8098-00AA0047BE5D"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual), + SuppressUnmanagedCodeSecurity()] + public interface ITextDocument { + string GetName(); + object GetSelection(); + int GetStoryCount(); + object GetStoryRanges(); + int GetSaved(); + void SetSaved(int value); + object GetDefaultTabStop(); + void SetDefaultTabStop(object value); + void New(); + void Open(object pVar, int flags, int codePage); + void Save(object pVar, int flags, int codePage); + int Freeze(); + int Unfreeze(); + void BeginEditCollection(); + void EndEditCollection(); + int Undo(int count); + int Redo(int count); + [return: MarshalAs(UnmanagedType.Interface)] ITextRange Range(int cp1, int cp2); + [return: MarshalAs(UnmanagedType.Interface)] ITextRange RangeFromPoint(int x, int y); + }; + + [ComVisible(true), Guid("8CC497C2-A1DF-11ce-8098-00AA0047BE5D"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual), + SuppressUnmanagedCodeSecurity()] + public interface ITextRange { + string GetText(); + void SetText(string text); + object GetChar(); + void SetChar(object ch); + [return: MarshalAs(UnmanagedType.Interface)] ITextRange GetDuplicate(); + [return: MarshalAs(UnmanagedType.Interface)] ITextRange GetFormattedText(); + void SetFormattedText([In, MarshalAs(UnmanagedType.Interface)] ITextRange range); + int GetStart(); + void SetStart(int cpFirst); + int GetEnd(); + void SetEnd(int cpLim); + object GetFont(); + void SetFont(object font); + object GetPara(); + void SetPara(object para); + int GetStoryLength(); + int GetStoryType(); + void Collapse(int start); + int Expand(int unit); + int GetIndex(int unit); + void SetIndex(int unit, int index, int extend); + void SetRange(int cpActive, int cpOther); + int InRange([In, MarshalAs(UnmanagedType.Interface)] ITextRange range); + int InStory([In, MarshalAs(UnmanagedType.Interface)] ITextRange range); + int IsEqual([In, MarshalAs(UnmanagedType.Interface)] ITextRange range); + void Select(); + int StartOf(int unit, int extend); + int EndOf(int unit, int extend); + int Move(int unit, int count); + int MoveStart(int unit, int count); + int MoveEnd(int unit, int count); + int MoveWhile(object cset, int count); + int MoveStartWhile(object cset, int count); + int MoveEndWhile(object cset, int count); + int MoveUntil(object cset, int count); + int MoveStartUntil(object cset, int count); + int MoveEndUntil(object cset, int count); + int FindText(string text, int cch, int flags); + int FindTextStart(string text, int cch, int flags); + int FindTextEnd(string text, int cch, int flags); + int Delete(int unit, int count); + void Cut([Out] out object pVar); + void Copy([Out] out object pVar); + void Paste(object pVar, int format); + int CanPaste(object pVar, int format); + int CanEdit(); + void ChangeCase(int type); + void GetPoint(int type, [Out] out int x, [Out] out int y); + void SetPoint(int x, int y, int type, int extend); + void ScrollIntoView(int value); + object GetEmbeddedObject(); + }; + + [ComImport(), Guid("00020D03-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IRichEditOleCallback { + [PreserveSig] + int GetNewStorage(out IStorage ret); + + [PreserveSig] + int GetInPlaceContext(IntPtr lplpFrame, IntPtr lplpDoc, IntPtr lpFrameInfo); + + [PreserveSig] + int ShowContainerUI(int fShow); + + [PreserveSig] + int QueryInsertObject(ref Guid lpclsid, IntPtr lpstg, int cp); + + [PreserveSig] + int DeleteObject(IntPtr lpoleobj); + + [PreserveSig] + int QueryAcceptData(IComDataObject lpdataobj, /* CLIPFORMAT* */ IntPtr lpcfFormat, int reco, int fReally, IntPtr hMetaPict); + + [PreserveSig] + int ContextSensitiveHelp(int fEnterMode); + + [PreserveSig] + int GetClipboardData(NativeMethods.CHARRANGE lpchrg, int reco, IntPtr lplpdataobj); + + [PreserveSig] + int GetDragDropEffect(bool fDrag, int grfKeyState, ref int pdwEffect); + + [PreserveSig] + int GetContextMenu(short seltype, IntPtr lpoleobj, NativeMethods.CHARRANGE lpchrg, out IntPtr hmenu); + } + + [ComImport(), Guid("00000115-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceUIWindow { + IntPtr GetWindow(); + + [PreserveSig] + int ContextSensitiveHelp( + + int fEnterMode); + + + [PreserveSig] + int GetBorder( + [Out] + NativeMethods.COMRECT lprectBorder); + + + [PreserveSig] + int RequestBorderSpace( + [In] + NativeMethods.COMRECT pborderwidths); + + + [PreserveSig] + int SetBorderSpace( + [In] + NativeMethods.COMRECT pborderwidths); + + + void SetActiveObject( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleInPlaceActiveObject pActiveObject, + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszObjName); + + + } + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("00000117-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceActiveObject { + + [PreserveSig] + int GetWindow( out IntPtr hwnd ); + + + void ContextSensitiveHelp( + + int fEnterMode); + + + [PreserveSig] + int TranslateAccelerator( + [In] + ref NativeMethods.MSG lpmsg); + + + void OnFrameWindowActivate( + + bool fActivate); + + + void OnDocWindowActivate( + + int fActivate); + + + void ResizeBorder( + [In] + NativeMethods.COMRECT prcBorder, + [In] + UnsafeNativeMethods.IOleInPlaceUIWindow pUIWindow, + + bool fFrameWindow); + + + void EnableModeless( + + int fEnable); + + + } + [ComImport(), Guid("00000114-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleWindow { + + [PreserveSig] + int GetWindow( [Out]out IntPtr hwnd ); + + + void ContextSensitiveHelp( + + int fEnterMode); + } + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("00000113-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceObject { + + [PreserveSig] + int GetWindow( [Out]out IntPtr hwnd ); + + + void ContextSensitiveHelp( + + int fEnterMode); + + + void InPlaceDeactivate(); + + + [PreserveSig] + int UIDeactivate(); + + + void SetObjectRects( + [In] + NativeMethods.COMRECT lprcPosRect, + [In] + NativeMethods.COMRECT lprcClipRect); + + + void ReactivateAndUndo(); + + + } + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("00000112-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleObject { + + [PreserveSig] + int SetClientSite( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleClientSite pClientSite); + + + UnsafeNativeMethods.IOleClientSite GetClientSite(); + + [PreserveSig] + int SetHostNames( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szContainerApp, + [In, MarshalAs(UnmanagedType.LPWStr)] + string szContainerObj); + + [PreserveSig] + int Close( + + int dwSaveOption); + + [PreserveSig] + int SetMoniker( + [In, MarshalAs(UnmanagedType.U4)] + int dwWhichMoniker, + [In, MarshalAs(UnmanagedType.Interface)] + object pmk); + + [PreserveSig] + int GetMoniker( + [In, MarshalAs(UnmanagedType.U4)] + int dwAssign, + [In, MarshalAs(UnmanagedType.U4)] + int dwWhichMoniker, + [Out, MarshalAs(UnmanagedType.Interface)] + out object moniker); + + [PreserveSig] + int InitFromData( + [In, MarshalAs(UnmanagedType.Interface)] + IComDataObject pDataObject, + + int fCreation, + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved); + + [PreserveSig] + int GetClipboardData( + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved, + out IComDataObject data); + + [PreserveSig] + int DoVerb( + + int iVerb, + [In] + IntPtr lpmsg, + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleClientSite pActiveSite, + + int lindex, + + IntPtr hwndParent, + [In] + NativeMethods.COMRECT lprcPosRect); + + [PreserveSig] + int EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e); + + [PreserveSig] + int OleUpdate(); + + [PreserveSig] + int IsUpToDate(); + + [PreserveSig] + int GetUserClassID( + [In, Out] + ref Guid pClsid); + + [PreserveSig] + int GetUserType( + [In, MarshalAs(UnmanagedType.U4)] + int dwFormOfType, + [Out, MarshalAs(UnmanagedType.LPWStr)] + out string userType); + + [PreserveSig] + int SetExtent( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + [In] + NativeMethods.tagSIZEL pSizel); + + [PreserveSig] + int GetExtent( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + [Out] + NativeMethods.tagSIZEL pSizel); + + [PreserveSig] + int Advise( + IAdviseSink pAdvSink, + out int cookie); + + [PreserveSig] + int Unadvise( + [In, MarshalAs(UnmanagedType.U4)] + int dwConnection); + + [PreserveSig] + int EnumAdvise(out IEnumSTATDATA e); + + [PreserveSig] + int GetMiscStatus( + [In, MarshalAs(UnmanagedType.U4)] + int dwAspect, + out int misc); + + [PreserveSig] + int SetColorScheme( + [In] + NativeMethods.tagLOGPALETTE pLogpal); + } + + [ComImport(), Guid("1C2056CC-5EF4-101B-8BC8-00AA003E3B29"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleInPlaceObjectWindowless { + + [PreserveSig] + int SetClientSite( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleClientSite pClientSite); + + [PreserveSig] + int GetClientSite(out UnsafeNativeMethods.IOleClientSite site); + + [PreserveSig] + int SetHostNames( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szContainerApp, + [In, MarshalAs(UnmanagedType.LPWStr)] + string szContainerObj); + + [PreserveSig] + int Close( + + int dwSaveOption); + + [PreserveSig] + int SetMoniker( + [In, MarshalAs(UnmanagedType.U4)] + int dwWhichMoniker, + [In, MarshalAs(UnmanagedType.Interface)] + object pmk); + + [PreserveSig] + int GetMoniker( + [In, MarshalAs(UnmanagedType.U4)] + int dwAssign, + [In, MarshalAs(UnmanagedType.U4)] + int dwWhichMoniker, + [Out, MarshalAs(UnmanagedType.Interface)] + out object moniker); + + [PreserveSig] + int InitFromData( + [In, MarshalAs(UnmanagedType.Interface)] + IComDataObject pDataObject, + + int fCreation, + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved); + + [PreserveSig] + int GetClipboardData( + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved, + out IComDataObject data); + + [PreserveSig] + int DoVerb( + + int iVerb, + [In] + IntPtr lpmsg, + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IOleClientSite pActiveSite, + + int lindex, + + IntPtr hwndParent, + [In] + NativeMethods.COMRECT lprcPosRect); + + [PreserveSig] + int EnumVerbs(out UnsafeNativeMethods.IEnumOLEVERB e); + + [PreserveSig] + int OleUpdate(); + + [PreserveSig] + int IsUpToDate(); + + [PreserveSig] + int GetUserClassID( + [In, Out] + ref Guid pClsid); + + [PreserveSig] + int GetUserType( + [In, MarshalAs(UnmanagedType.U4)] + int dwFormOfType, + [Out, MarshalAs(UnmanagedType.LPWStr)] + out string userType); + + [PreserveSig] + int SetExtent( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + [In] + NativeMethods.tagSIZEL pSizel); + + [PreserveSig] + int GetExtent( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + [Out] + NativeMethods.tagSIZEL pSizel); + + [PreserveSig] + int Advise( + [In, MarshalAs(UnmanagedType.Interface)] + IAdviseSink pAdvSink, + out int cookie); + + [PreserveSig] + int Unadvise( + [In, MarshalAs(UnmanagedType.U4)] + int dwConnection); + + [PreserveSig] + int EnumAdvise(out IEnumSTATDATA e); + + [PreserveSig] + int GetMiscStatus( + [In, MarshalAs(UnmanagedType.U4)] + int dwAspect, + out int misc); + + [PreserveSig] + int SetColorScheme( + [In] + NativeMethods.tagLOGPALETTE pLogpal); + + [PreserveSig] + int OnWindowMessage( + [In, MarshalAs(UnmanagedType.U4)] int msg, + [In, MarshalAs(UnmanagedType.U4)] int wParam, + [In, MarshalAs(UnmanagedType.U4)] int lParam, + [Out, MarshalAs(UnmanagedType.U4)] int plResult); + + [PreserveSig] + int GetDropTarget( + [Out, MarshalAs(UnmanagedType.Interface)] object ppDropTarget); + + }; + + + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("B196B288-BAB4-101A-B69C-00AA00341D07"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleControl { + [PreserveSig] + int GetControlInfo( + [Out] + NativeMethods.tagCONTROLINFO pCI); + + [PreserveSig] + int OnMnemonic( + [In] + ref NativeMethods.MSG pMsg); + + [PreserveSig] + int OnAmbientPropertyChange( + + int dispID); + + [PreserveSig] + int FreezeEvents( + + int bFreeze); + + } + [ComImport(), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleServiceProvider { + [PreserveSig] + int QueryService( + [In] + ref Guid guidService, + [In] + ref Guid riid, + out IntPtr ppvObject); + } + [ComImport(), Guid("0000010d-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IViewObject { + [PreserveSig] + int Draw( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [In] + NativeMethods.tagDVTARGETDEVICE ptd, + + IntPtr hdcTargetDev, + + IntPtr hdcDraw, + [In] + NativeMethods.COMRECT lprcBounds, + [In] + NativeMethods.COMRECT lprcWBounds, + + IntPtr pfnContinue, + [In] + int dwContinue); + + + [PreserveSig] + int GetColorSet( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [In] + NativeMethods.tagDVTARGETDEVICE ptd, + + IntPtr hicTargetDev, + [Out] + NativeMethods.tagLOGPALETTE ppColorSet); + + [PreserveSig] + int Freeze( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [Out] + IntPtr pdwFreeze); + + [PreserveSig] + int Unfreeze( + [In, MarshalAs(UnmanagedType.U4)] + int dwFreeze); + + + void SetAdvise( + [In, MarshalAs(UnmanagedType.U4)] + int aspects, + [In, MarshalAs(UnmanagedType.U4)] + int advf, + [In, MarshalAs(UnmanagedType.Interface)] + IAdviseSink pAdvSink); + + + void GetAdvise( + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + int[] paspects, + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + int[] advf, + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + IAdviseSink[] pAdvSink); + } + [ComImport(), Guid("00000127-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IViewObject2 /* : IViewObject */ { + void Draw( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [In] + NativeMethods.tagDVTARGETDEVICE ptd, + + IntPtr hdcTargetDev, + + IntPtr hdcDraw, + [In] + NativeMethods.COMRECT lprcBounds, + [In] + NativeMethods.COMRECT lprcWBounds, + + IntPtr pfnContinue, + [In] + int dwContinue); + + + [PreserveSig] + int GetColorSet( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [In] + NativeMethods.tagDVTARGETDEVICE ptd, + + IntPtr hicTargetDev, + [Out] + NativeMethods.tagLOGPALETTE ppColorSet); + + + [PreserveSig] + int Freeze( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + + IntPtr pvAspect, + [Out] + IntPtr pdwFreeze); + + + [PreserveSig] + int Unfreeze( + [In, MarshalAs(UnmanagedType.U4)] + int dwFreeze); + + + void SetAdvise( + [In, MarshalAs(UnmanagedType.U4)] + int aspects, + [In, MarshalAs(UnmanagedType.U4)] + int advf, + [In, MarshalAs(UnmanagedType.Interface)] + IAdviseSink pAdvSink); + + + void GetAdvise( + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + int[] paspects, + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + int[] advf, + // These can be NULL if caller doesn't want them + [In, Out, MarshalAs(UnmanagedType.LPArray)] + IAdviseSink[] pAdvSink); + + + void GetExtent( + [In, MarshalAs(UnmanagedType.U4)] + int dwDrawAspect, + + int lindex, + [In] + NativeMethods.tagDVTARGETDEVICE ptd, + [Out] + NativeMethods.tagSIZEL lpsizel); + } + + [ComImport(), Guid("0000010C-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersist { + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + void GetClassID( + [Out] + out Guid pClassID); + } + + [ComImport(), Guid("37D84F60-42CB-11CE-8135-00AA004BB851"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistPropertyBag { + void GetClassID( + [Out] + out Guid pClassID); + + + void InitNew(); + + + void Load( + [In, MarshalAs(UnmanagedType.Interface)] + IPropertyBag pPropBag, + [In, MarshalAs(UnmanagedType.Interface)] + IErrorLog pErrorLog); + + + void Save( + [In, MarshalAs(UnmanagedType.Interface)] + IPropertyBag pPropBag, + [In, MarshalAs(UnmanagedType.Bool)] + bool fClearDirty, + [In, MarshalAs(UnmanagedType.Bool)] + bool fSaveAllProperties); + } + [ + ComImport(), + Guid("CF51ED10-62FE-11CF-BF86-00A0C9034836"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IQuickActivate { + void QuickActivate( + [In] + UnsafeNativeMethods.tagQACONTAINER pQaContainer, + [Out] + UnsafeNativeMethods.tagQACONTROL pQaControl); + + + void SetContentExtent( + [In] + NativeMethods.tagSIZEL pSizel); + + + void GetContentExtent( + [Out] + NativeMethods.tagSIZEL pSizel); + + } + + [ComImport(), Guid("000C060B-0000-0000-C000-000000000046"), + SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + public class SMsoComponentManager + { + } + + [ComImport(), Guid("55272A00-42CB-11CE-8135-00AA004BB851"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IPropertyBag { + [PreserveSig] + int Read( + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszPropName, + [In, Out] + ref object pVar, + [In] + IErrorLog pErrorLog); + + [PreserveSig] + int Write( + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszPropName, + [In] + ref object pVar); + } + + [ComImport(), Guid("3127CA40-446E-11CE-8135-00AA004BB851"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IErrorLog { + void AddError( + [In, MarshalAs(UnmanagedType.LPWStr)] + string pszPropName_p0, + [In, MarshalAs(UnmanagedType.Struct)] + NativeMethods.tagEXCEPINFO pExcepInfo_p1); + + } + + [ComImport(), Guid("00000109-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistStream { + + void GetClassID([Out] out Guid pClassId); + + [PreserveSig] + int IsDirty(); + + + void Load( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream pstm); + + + void Save( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream pstm, + [In, MarshalAs(UnmanagedType.Bool)] + bool fClearDirty); + + + long GetSizeMax(); + + + } + + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("7FD52380-4E07-101B-AE2D-08002B2EC713"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistStreamInit { + void GetClassID( + [Out] + out Guid pClassID); + + + [PreserveSig] + int IsDirty(); + + + void Load( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream pstm); + + + void Save( + [In, MarshalAs(UnmanagedType.Interface)] + IStream pstm, + [In, MarshalAs(UnmanagedType.Bool)] + bool fClearDirty); + + + void GetSizeMax( + [Out, MarshalAs(UnmanagedType.LPArray)] + long pcbSize); + + + void InitNew(); + + + } + + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("B196B286-BAB4-101A-B69C-00AA00341D07"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IConnectionPoint { + + [PreserveSig] + int GetConnectionInterface(out Guid iid); + + + [PreserveSig] + int GetConnectionPointContainer( + [MarshalAs(UnmanagedType.Interface)] + ref IConnectionPointContainer pContainer); + + + [PreserveSig] + int Advise( + [In, MarshalAs(UnmanagedType.Interface)] + object pUnkSink, + ref int cookie); + + + [PreserveSig] + int Unadvise( + + int cookie); + + [PreserveSig] + int EnumConnections(out object pEnum); + + } + + [ComImport(), Guid("0000010A-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistStorage { + void GetClassID( + [Out] + out Guid pClassID); + + [PreserveSig] + int IsDirty(); + + void InitNew(IStorage pstg); + + [PreserveSig] + int Load(IStorage pstg); + + void Save(IStorage pStgSave, bool fSameAsLoad); + + void SaveCompleted(IStorage pStgNew); + + void HandsOffStorage(); + } + + [ComImport(), Guid("00020404-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumVariant { + [PreserveSig] + int Next( + [In, MarshalAs(UnmanagedType.U4)] + int celt, + [In, Out] + IntPtr rgvar, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pceltFetched); + + void Skip( + [In, MarshalAs(UnmanagedType.U4)] + int celt); + + void Reset(); + + void Clone( + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.IEnumVariant[] ppenum); + } + + [ComImport(), Guid("00000104-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumOLEVERB { + + + [PreserveSig] + int Next( + [MarshalAs(UnmanagedType.U4)] + int celt, + [Out] + NativeMethods.tagOLEVERB rgelt, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pceltFetched); + + [PreserveSig] + int Skip( + [In, MarshalAs(UnmanagedType.U4)] + int celt); + + + void Reset(); + + + void Clone( + out IEnumOLEVERB ppenum); + } + + // INTERFACE: IEnumACString + + // This interface was implemented to return autocomplete strings + // into the caller's buffer (to reduce the number of memory allocations + // A sort index is also returned to control the order of items displayed + // by autocomplete. The sort index should be set to zero if unused + // The NextItem method increments the current index by one (similar to Next + // when one item is requested + + //public interface IEnumString + //Do not declare IEnumString here -- use IEnumString from interopservices. + // even if it looks like it works, if you declaring the marshalling incorrectly, it will barf on appverifier. + + + //------------------------------------------------------------------------- + // IAutoComplete interface + // [Member functions] + // IAutoComplete::Init(hwndEdit, punkACL, pwszRegKeyPath, pwszQuickComplete) + // This function initializes an AutoComplete object, telling it + // what control to subclass, and what list of strings to process. + // IAutoComplete::Enable(fEnable) + // This function enables or disables the AutoComplete functionality. + //------------------------------------------------------------------------- + + [SuppressUnmanagedCodeSecurity, ComImport(), Guid("00bb2762-6a77-11d0-a535-00c04fd7d062"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAutoComplete + { + + int Init( + [In] HandleRef hwndEdit, // hwnd of editbox or editbox deriviative. + [In] System.Runtime.InteropServices.ComTypes.IEnumString punkACL, // Pointer to object containing string to complete from. (IEnumString *) + [In] string pwszRegKeyPath, // + [In] string pwszQuickComplete + ); + void Enable([In] bool fEnable); // Is it enabled? + } + + + [SuppressUnmanagedCodeSecurity, ComImport(), Guid("EAC04BC0-3791-11d2-BB95-0060977B464C"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + + public interface IAutoComplete2 + { + int Init( + [In] HandleRef hwndEdit, // hwnd of editbox or editbox deriviative. + [In] System.Runtime.InteropServices.ComTypes.IEnumString punkACL, // Pointer to object containing string to complete from. (IEnumString *) + [In] string pwszRegKeyPath, // + [In] string pwszQuickComplete + ); + void Enable([In] bool fEnable); // Is it enabled? + + int SetOptions([In] int dwFlag); + void GetOptions([Out] IntPtr pdwFlag); + } + + [SuppressUnmanagedCodeSecurity, ComImport(), Guid("0000000C-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IStream { + + int Read( + + IntPtr buf, + + int len); + + + int Write( + + IntPtr buf, + + int len); + + [return: MarshalAs(UnmanagedType.I8)] + long Seek( + [In, MarshalAs(UnmanagedType.I8)] + long dlibMove, + + int dwOrigin); + + + void SetSize( + [In, MarshalAs(UnmanagedType.I8)] + long libNewSize); + + [return: MarshalAs(UnmanagedType.I8)] + long CopyTo( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream pstm, + [In, MarshalAs(UnmanagedType.I8)] + long cb, + [Out, MarshalAs(UnmanagedType.LPArray)] + long[] pcbRead); + + + void Commit( + + int grfCommitFlags); + + + void Revert(); + + + void LockRegion( + [In, MarshalAs(UnmanagedType.I8)] + long libOffset, + [In, MarshalAs(UnmanagedType.I8)] + long cb, + + int dwLockType); + + + void UnlockRegion( + [In, MarshalAs(UnmanagedType.I8)] + long libOffset, + [In, MarshalAs(UnmanagedType.I8)] + long cb, + + int dwLockType); + + + void Stat( + [Out] + NativeMethods.STATSTG pStatstg, + int grfStatFlag); + + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream Clone(); + } + + public abstract class CharBuffer + { + + public static CharBuffer CreateBuffer(int size) + { + if (Marshal.SystemDefaultCharSize == 1) + { + return new AnsiCharBuffer(size); + } + return new UnicodeCharBuffer(size); + } + + public abstract IntPtr AllocCoTaskMem(); + public abstract string GetString(); + public abstract void PutCoTaskMem(IntPtr ptr); + public abstract void PutString(string s); + } + + public class AnsiCharBuffer : CharBuffer + { + + internal byte[] buffer; + internal int offset; + + public AnsiCharBuffer(int size) + { + buffer = new byte[size]; + } + + public override IntPtr AllocCoTaskMem() + { + IntPtr result = Marshal.AllocCoTaskMem(buffer.Length); + Marshal.Copy(buffer, 0, result, buffer.Length); + return result; + } + + public override string GetString() + { + int i = offset; + while (i < buffer.Length && buffer[i] != 0) + i++; + string result = Encoding.Default.GetString(buffer, offset, i - offset); + if (i < buffer.Length) + i++; + offset = i; + return result; + } + + public override void PutCoTaskMem(IntPtr ptr) + { + Marshal.Copy(ptr, buffer, 0, buffer.Length); + offset = 0; + } + + public override void PutString(string s) + { + byte[] bytes = Encoding.Default.GetBytes(s); + int count = Math.Min(bytes.Length, buffer.Length - offset); + Array.Copy(bytes, 0, buffer, offset, count); + offset += count; + if (offset < buffer.Length) buffer[offset++] = 0; + } + } + + public class UnicodeCharBuffer : CharBuffer + { + + internal char[] buffer; + internal int offset; + + public UnicodeCharBuffer(int size) + { + buffer = new char[size]; + } + + public override IntPtr AllocCoTaskMem() + { + IntPtr result = Marshal.AllocCoTaskMem(buffer.Length * 2); + Marshal.Copy(buffer, 0, result, buffer.Length); + return result; + } + + public override String GetString() + { + int i = offset; + while (i < buffer.Length && buffer[i] != 0) i++; + string result = new string(buffer, offset, i - offset); + if (i < buffer.Length) i++; + offset = i; + return result; + } + + public override void PutCoTaskMem(IntPtr ptr) + { + Marshal.Copy(ptr, buffer, 0, buffer.Length); + offset = 0; + } + + public override void PutString(string s) + { + int count = Math.Min(s.Length, buffer.Length - offset); + s.CopyTo(0, buffer, offset, count); + offset += count; + if (offset < buffer.Length) buffer[offset++] = (char)0; + } + } + + public class ComStreamFromDataStream : IStream { + protected Stream dataStream; + + // to support seeking ahead of the stream length... + private long virtualPosition = -1; + + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public ComStreamFromDataStream(Stream dataStream) + { + if (dataStream == null) throw new ArgumentNullException("dataStream"); + this.dataStream = dataStream; + } + + private void ActualizeVirtualPosition() { + if (virtualPosition == -1) return; + + if (virtualPosition > dataStream.Length) + dataStream.SetLength(virtualPosition); + + dataStream.Position = virtualPosition; + + virtualPosition = -1; + } + + public IStream Clone() { + NotImplemented(); + return null; + } + + public void Commit(int grfCommitFlags) { + dataStream.Flush(); + // Extend the length of the file if needed. + ActualizeVirtualPosition(); + } + + public long CopyTo(IStream pstm, long cb, long[] pcbRead) { + int bufsize = 4096; // one page + IntPtr buffer = Marshal.AllocHGlobal(bufsize); + if (buffer == IntPtr.Zero) throw new OutOfMemoryException(); + long written = 0; + try { + while (written < cb) { + int toRead = bufsize; + if (written + toRead > cb) toRead = (int) (cb - written); + int read = Read(buffer, toRead); + if (read == 0) break; + if (pstm.Write(buffer, read) != read) { + throw EFail("Wrote an incorrect number of bytes"); + } + written += read; + } + } + finally { + Marshal.FreeHGlobal(buffer); + } + if (pcbRead != null && pcbRead.Length > 0) { + pcbRead[0] = written; + } + + return written; + } + + public Stream GetDataStream() { + return dataStream; + } + + public void LockRegion(long libOffset, long cb, int dwLockType) { + } + + protected static ExternalException EFail(string msg) { + ExternalException e = new ExternalException(msg, NativeMethods.E_FAIL); + throw e; + } + + protected static void NotImplemented() { + ExternalException e = new ExternalException(SR.GetString(SR.UnsafeNativeMethodsNotImplemented), NativeMethods.E_NOTIMPL); + throw e; + } + + public int Read(IntPtr buf, /* cpr: int offset,*/ int length) { + // System.Text.Out.WriteLine("IStream::Read(" + length + ")"); + byte[] buffer = new byte[length]; + int count = Read(buffer, length); + Marshal.Copy(buffer, 0, buf, count); + return count; + } + + public int Read(byte[] buffer, /* cpr: int offset,*/ int length) { + ActualizeVirtualPosition(); + return dataStream.Read(buffer, 0, length); + } + + public void Revert() { + NotImplemented(); + } + + public long Seek(long offset, int origin) { + // Console.WriteLine("IStream::Seek("+ offset + ", " + origin + ")"); + long pos = virtualPosition; + if (virtualPosition == -1) { + pos = dataStream.Position; + } + long len = dataStream.Length; + switch (origin) { + case NativeMethods.STREAM_SEEK_SET: + if (offset <= len) { + dataStream.Position = offset; + virtualPosition = -1; + } + else { + virtualPosition = offset; + } + break; + case NativeMethods.STREAM_SEEK_END: + if (offset <= 0) { + dataStream.Position = len + offset; + virtualPosition = -1; + } + else { + virtualPosition = len + offset; + } + break; + case NativeMethods.STREAM_SEEK_CUR: + if (offset+pos <= len) { + dataStream.Position = pos + offset; + virtualPosition = -1; + } + else { + virtualPosition = offset + pos; + } + break; + } + if (virtualPosition != -1) { + return virtualPosition; + } + else { + return dataStream.Position; + } + } + + public void SetSize(long value) { + dataStream.SetLength(value); + } + + public void Stat(NativeMethods.STATSTG pstatstg, int grfStatFlag) { + pstatstg.type = 2; // STGTY_STREAM + pstatstg.cbSize = dataStream.Length; + pstatstg.grfLocksSupported = 2; //LOCK_EXCLUSIVE + } + + public void UnlockRegion(long libOffset, long cb, int dwLockType) { + } + + public int Write(IntPtr buf, /* cpr: int offset,*/ int length) { + byte[] buffer = new byte[length]; + Marshal.Copy(buf, buffer, 0, length); + return Write(buffer, length); + } + + public int Write(byte[] buffer, /* cpr: int offset,*/ int length) { + ActualizeVirtualPosition(); + dataStream.Write(buffer, 0, length); + return length; + } + } + [ComImport(), Guid("0000000B-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IStorage { + + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream CreateStream( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + [In, MarshalAs(UnmanagedType.U4)] + int grfMode, + [In, MarshalAs(UnmanagedType.U4)] + int reserved1, + [In, MarshalAs(UnmanagedType.U4)] + int reserved2); + + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream OpenStream( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + + IntPtr reserved1, + [In, MarshalAs(UnmanagedType.U4)] + int grfMode, + [In, MarshalAs(UnmanagedType.U4)] + int reserved2); + + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStorage CreateStorage( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + [In, MarshalAs(UnmanagedType.U4)] + int grfMode, + [In, MarshalAs(UnmanagedType.U4)] + int reserved1, + [In, MarshalAs(UnmanagedType.U4)] + int reserved2); + + [return: MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStorage OpenStorage( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + + IntPtr pstgPriority, // must be null + [In, MarshalAs(UnmanagedType.U4)] + int grfMode, + + IntPtr snbExclude, + [In, MarshalAs(UnmanagedType.U4)] + int reserved); + + + void CopyTo( + + int ciidExclude, + [In, MarshalAs(UnmanagedType.LPArray)] + Guid[] pIIDExclude, + + IntPtr snbExclude, + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStorage stgDest); + + + void MoveElementTo( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStorage stgDest, + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsNewName, + [In, MarshalAs(UnmanagedType.U4)] + int grfFlags); + + + void Commit( + + int grfCommitFlags); + + + void Revert(); + + + void EnumElements( + [In, MarshalAs(UnmanagedType.U4)] + int reserved1, + // void * + IntPtr reserved2, + [In, MarshalAs(UnmanagedType.U4)] + int reserved3, + [Out, MarshalAs(UnmanagedType.Interface)] + out object ppVal); // IEnumSTATSTG + + + void DestroyElement( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName); + + + void RenameElement( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsOldName, + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsNewName); + + + void SetElementTimes( + [In, MarshalAs(UnmanagedType.BStr)] + string pwcsName, + [In] + NativeMethods.FILETIME pctime, + [In] + NativeMethods.FILETIME patime, + [In] + NativeMethods.FILETIME pmtime); + + + void SetClass( + [In] + ref Guid clsid); + + + void SetStateBits( + + int grfStateBits, + + int grfMask); + + + void Stat( + [Out] + NativeMethods.STATSTG pStatStg, + int grfStatFlag); + } + + [ComImport(), Guid("B196B28F-BAB4-101A-B69C-00AA00341D07"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IClassFactory2 { + + + void CreateInstance( + [In, MarshalAs(UnmanagedType.Interface)] + object unused, + [In] + ref Guid refiid, + [Out, MarshalAs(UnmanagedType.LPArray)] + object[] ppunk); + + + void LockServer( + + int fLock); + + + void GetLicInfo( + [Out] + NativeMethods.tagLICINFO licInfo); + + + void RequestLicKey( + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrKey); + + + void CreateInstanceLic( + [In, MarshalAs(UnmanagedType.Interface)] + object pUnkOuter, + [In, MarshalAs(UnmanagedType.Interface)] + object pUnkReserved, + [In] + ref Guid riid, + [In, MarshalAs(UnmanagedType.BStr)] + string bstrKey, + [Out, MarshalAs(UnmanagedType.Interface)] + out object ppVal); + } + [SuppressUnmanagedCodeSecurity, ComImport(), + Guid("B196B284-BAB4-101A-B69C-00AA00341D07"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IConnectionPointContainer { + + [return: MarshalAs(UnmanagedType.Interface)] + object EnumConnectionPoints(); + + [PreserveSig] + int FindConnectionPoint([In] ref Guid guid, [Out, MarshalAs(UnmanagedType.Interface)]out IConnectionPoint ppCP); + + } + + [ComImport(), Guid("B196B285-BAB4-101A-B69C-00AA00341D07"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumConnectionPoints { + [PreserveSig] + int Next(int cConnections, out IConnectionPoint pCp, out int pcFetched); + + [PreserveSig] + int Skip(int cSkip); + + void Reset(); + + IEnumConnectionPoints Clone(); + } + + + [ComImport(), Guid("00020400-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IDispatch { + int GetTypeInfoCount(); + + [return: MarshalAs(UnmanagedType.Interface)] + ITypeInfo GetTypeInfo( + [In, MarshalAs(UnmanagedType.U4)] + int iTInfo, + [In, MarshalAs(UnmanagedType.U4)] + int lcid); + + + [PreserveSig] + int GetIDsOfNames( + [In] + ref Guid riid, + [In, MarshalAs(UnmanagedType.LPArray)] + string[] rgszNames, + [In, MarshalAs(UnmanagedType.U4)] + int cNames, + [In, MarshalAs(UnmanagedType.U4)] + int lcid, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] rgDispId); + + + [PreserveSig] + int Invoke( + + int dispIdMember, + [In] + ref Guid riid, + [In, MarshalAs(UnmanagedType.U4)] + int lcid, + [In, MarshalAs(UnmanagedType.U4)] + int dwFlags, + [Out, In] + NativeMethods.tagDISPPARAMS pDispParams, + [Out, MarshalAs(UnmanagedType.LPArray)] + object[] pVarResult, + [Out, In] + NativeMethods.tagEXCEPINFO pExcepInfo, + [Out, MarshalAs(UnmanagedType.LPArray)] + IntPtr [] pArgErr); + + } + + [ComImport(), Guid("00020401-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITypeInfo { + [PreserveSig] + int GetTypeAttr(ref IntPtr pTypeAttr); + + + [PreserveSig] + int GetTypeComp( + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeComp[] ppTComp); + + + [PreserveSig] + int GetFuncDesc( + [In, MarshalAs(UnmanagedType.U4)] + int index, ref IntPtr pFuncDesc); + + + [PreserveSig] + int GetVarDesc( + [In, MarshalAs(UnmanagedType.U4)] + int index, ref IntPtr pVarDesc); + + + [PreserveSig] + int GetNames( + + int memid, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] rgBstrNames, + [In, MarshalAs(UnmanagedType.U4)] + int cMaxNames, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pcNames); + + + [PreserveSig] + int GetRefTypeOfImplType( + [In, MarshalAs(UnmanagedType.U4)] + int index, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pRefType); + + + [PreserveSig] + int GetImplTypeFlags( + [In, MarshalAs(UnmanagedType.U4)] + int index, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pImplTypeFlags); + + + [PreserveSig] + int GetIDsOfNames(IntPtr rgszNames, int cNames, IntPtr pMemId); + + + [PreserveSig] + int Invoke(); + + + [PreserveSig] + int GetDocumentation( + + int memid, + ref string pBstrName, + ref string pBstrDocString, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pdwHelpContext, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrHelpFile); + + + [PreserveSig] + int GetDllEntry( + + int memid, + + NativeMethods.tagINVOKEKIND invkind, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrDllName, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrName, + [Out, MarshalAs(UnmanagedType.LPArray)] + short[] pwOrdinal); + + + [PreserveSig] + int GetRefTypeInfo( + + IntPtr hreftype, + ref ITypeInfo pTypeInfo); + + + [PreserveSig] + int AddressOfMember(); + + + [PreserveSig] + int CreateInstance( + [In] + ref Guid riid, + [Out, MarshalAs(UnmanagedType.LPArray)] + object[] ppvObj); + + + [PreserveSig] + int GetMops( + + int memid, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrMops); + + + [PreserveSig] + int GetContainingTypeLib( + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeLib[] ppTLib, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pIndex); + + [PreserveSig] + void ReleaseTypeAttr(IntPtr typeAttr); + + [PreserveSig] + void ReleaseFuncDesc(IntPtr funcDesc); + + [PreserveSig] + void ReleaseVarDesc(IntPtr varDesc); + + } + [ComImport(), Guid("00020403-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITypeComp { + void RemoteBind( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szName, + [In, MarshalAs(UnmanagedType.U4)] + int lHashVal, + [In, MarshalAs(UnmanagedType.U2)] + short wFlags, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeInfo[] ppTInfo, + [Out, MarshalAs(UnmanagedType.LPArray)] + NativeMethods.tagDESCKIND[] pDescKind, + [Out, MarshalAs(UnmanagedType.LPArray)] + NativeMethods.tagFUNCDESC[] ppFuncDesc, + [Out, MarshalAs(UnmanagedType.LPArray)] + NativeMethods.tagVARDESC[] ppVarDesc, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeComp[] ppTypeComp, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pDummy); + + + void RemoteBindType( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szName, + [In, MarshalAs(UnmanagedType.U4)] + int lHashVal, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeInfo[] ppTInfo); + + } + + [ComImport(), Guid("00020402-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITypeLib { + + void RemoteGetTypeInfoCount( + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pcTInfo); + + + void GetTypeInfo( + [In, MarshalAs(UnmanagedType.U4)] + int index, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeInfo[] ppTInfo); + + + void GetTypeInfoType( + [In, MarshalAs(UnmanagedType.U4)] + int index, + [Out, MarshalAs(UnmanagedType.LPArray)] + NativeMethods.tagTYPEKIND[] pTKind); + + + void GetTypeInfoOfGuid( + [In] + ref Guid guid, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeInfo[] ppTInfo); + + + void RemoteGetLibAttr( + IntPtr ppTLibAttr, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pDummy); + + + void GetTypeComp( + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeComp[] ppTComp); + + + void RemoteGetDocumentation( + + int index, + [In, MarshalAs(UnmanagedType.U4)] + int refPtrFlags, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrName, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrDocString, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pdwHelpContext, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrHelpFile); + + + void RemoteIsName( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szNameBuf, + [In, MarshalAs(UnmanagedType.U4)] + int lHashVal, + [Out, MarshalAs(UnmanagedType.LPArray)] + IntPtr [] pfName, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrLibName); + + + void RemoteFindName( + [In, MarshalAs(UnmanagedType.LPWStr)] + string szNameBuf, + [In, MarshalAs(UnmanagedType.U4)] + int lHashVal, + [Out, MarshalAs(UnmanagedType.LPArray)] + UnsafeNativeMethods.ITypeInfo[] ppTInfo, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] rgMemId, + [In, Out, MarshalAs(UnmanagedType.LPArray)] + short[] pcFound, + [Out, MarshalAs(UnmanagedType.LPArray)] + string[] pBstrLibName); + + + void LocalReleaseTLibAttr(); + } + + [ComImport(), + Guid("DF0B3D60-548F-101B-8E65-08002B2BD119"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISupportErrorInfo { + + int InterfaceSupportsErrorInfo( + [In] ref Guid riid); + + + } + + [ComImport(), + Guid("1CF2B120-547D-101B-8E65-08002B2BD119"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IErrorInfo { + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + [PreserveSig] + int GetGUID( + [Out] + out Guid pguid); + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + [PreserveSig] + int GetSource( + [In, Out, MarshalAs(UnmanagedType.BStr)] + ref string pBstrSource); + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + [PreserveSig] + int GetDescription( + [In, Out, MarshalAs(UnmanagedType.BStr)] + ref string pBstrDescription); + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + [PreserveSig] + int GetHelpFile( + [In, Out, MarshalAs(UnmanagedType.BStr)] + ref string pBstrHelpFile); + + [ System.Security.SuppressUnmanagedCodeSecurityAttribute()] + [PreserveSig] + int GetHelpContext( + [In, Out, MarshalAs(UnmanagedType.U4)] + ref int pdwHelpContext); + + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class tagQACONTAINER + { + [MarshalAs(UnmanagedType.U4)] + public int cbSize = Marshal.SizeOf(typeof(tagQACONTAINER)); + + public UnsafeNativeMethods.IOleClientSite pClientSite; + + [MarshalAs(UnmanagedType.Interface)] + public object pAdviseSink = null; + + public UnsafeNativeMethods.IPropertyNotifySink pPropertyNotifySink; + + [MarshalAs(UnmanagedType.Interface)] + public object pUnkEventSink = null; + + [MarshalAs(UnmanagedType.U4)] + public int dwAmbientFlags; + + [MarshalAs(UnmanagedType.U4)] + public UInt32 colorFore; + + [MarshalAs(UnmanagedType.U4)] + public UInt32 colorBack; + + [MarshalAs(UnmanagedType.Interface)] + public object pFont; + + [MarshalAs(UnmanagedType.Interface)] + public object pUndoMgr = null; + + [MarshalAs(UnmanagedType.U4)] + public int dwAppearance; + + public int lcid; + + public IntPtr hpal = IntPtr.Zero; + + [MarshalAs(UnmanagedType.Interface)] + public object pBindHost = null; + + // visual basic6 uses a old version of the struct that is missing these two fields. + // So, ActiveX sourcing does not work, with the EE trying to read off the + // end of the stack to get to these variables. If I do not define these, + // Office or any of the other hosts will hopefully get nulls, otherwise they + // will crash. + // + //public UnsafeNativeMethods.IOleControlSite pControlSite; + + //public UnsafeNativeMethods.IOleServiceProvider pServiceProvider; + } + + [StructLayout(LayoutKind.Sequential)/*leftover(noAutoOffset)*/] + public sealed class tagQACONTROL + { + [MarshalAs(UnmanagedType.U4)/*leftover(offset=0, cbSize)*/] + public int cbSize = Marshal.SizeOf(typeof(tagQACONTROL)); + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=4, dwMiscStatus)*/] + public int dwMiscStatus = 0; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=8, dwViewStatus)*/] + public int dwViewStatus = 0; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=12, dwEventCookie)*/] + public int dwEventCookie = 0; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=16, dwPropNotifyCookie)*/] + public int dwPropNotifyCookie = 0; + + [MarshalAs(UnmanagedType.U4)/*leftover(offset=20, dwPointerActivationPolicy)*/] + public int dwPointerActivationPolicy = 0; + + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("E44C3566-915D-4070-99C6-047BFF5A08F5")] + [ComVisible(true)] + public interface ILegacyIAccessibleProvider { + + void Select(int flagsSelect); + + void DoDefaultAction(); + + void SetValue([MarshalAs(UnmanagedType.LPWStr)] string szValue); + + [return: MarshalAs(UnmanagedType.Interface)] + Accessibility.IAccessible GetIAccessible(); + + int ChildId { get; } + + string Name { get; } + + string Value { get; } + + string Description { get; } + + uint Role { get; } + + uint State { get; } + + string Help { get; } + + string KeyboardShortcut { get; } + + object[] /* IRawElementProviderSimple[] */ GetSelection(); + + string DefaultAction { get; } + } + + [ComImport(), Guid("0000000A-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface ILockBytes { + + + void ReadAt( + [In, MarshalAs(UnmanagedType.U8)] + long ulOffset, + [Out] + IntPtr pv, + [In, MarshalAs(UnmanagedType.U4)] + int cb, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pcbRead); + + + void WriteAt( + [In, MarshalAs(UnmanagedType.U8)] + long ulOffset, + + IntPtr pv, + [In, MarshalAs(UnmanagedType.U4)] + int cb, + [Out, MarshalAs(UnmanagedType.LPArray)] + int[] pcbWritten); + + + void Flush(); + + + void SetSize( + [In, MarshalAs(UnmanagedType.U8)] + long cb); + + + void LockRegion( + [In, MarshalAs(UnmanagedType.U8)] + long libOffset, + [In, MarshalAs(UnmanagedType.U8)] + long cb, + [In, MarshalAs(UnmanagedType.U4)] + int dwLockType); + + + void UnlockRegion( + [In, MarshalAs(UnmanagedType.U8)] + long libOffset, + [In, MarshalAs(UnmanagedType.U8)] + long cb, + [In, MarshalAs(UnmanagedType.U4)] + int dwLockType); + + + void Stat( + [Out] + NativeMethods.STATSTG pstatstg, + [In, MarshalAs(UnmanagedType.U4)] + int grfStatFlag); + + } + + [StructLayout(LayoutKind.Sequential), + SuppressUnmanagedCodeSecurity()] + public class OFNOTIFY + { + // hdr was a by-value NMHDR structure + public IntPtr hdr_hwndFrom = IntPtr.Zero; + public IntPtr hdr_idFrom = IntPtr.Zero; + public int hdr_code = 0; + + public IntPtr lpOFN = IntPtr.Zero; + public IntPtr pszFile = IntPtr.Zero; + } + + internal static bool IsComObject(object o) + { + return Marshal.IsComObject(o); + } + + internal static int ReleaseComObject(object objToRelease) + { + return Marshal.ReleaseComObject(objToRelease); + } + + [ReflectionPermission(SecurityAction.Assert, Unrestricted=true)] + public static object PtrToStructure(IntPtr lparam, Type cls) { + return Marshal.PtrToStructure(lparam, cls); + } + + [ReflectionPermission(SecurityAction.Assert, Unrestricted=true)] + public static void PtrToStructure(IntPtr lparam, object data) { + Marshal.PtrToStructure(lparam, data); + } + + internal static int SizeOf(Type t) + { + return Marshal.SizeOf(t); + } + + internal static void ThrowExceptionForHR(int errorCode) + { + Marshal.ThrowExceptionForHR(errorCode); + } + + + public delegate int BrowseCallbackProc( + IntPtr hwnd, + int msg, + IntPtr lParam, + IntPtr lpData); + + [Flags] + public enum BrowseInfos + { + NewDialogStyle = 0x0040, // Use the new dialog layout with the ability to resize + HideNewFolderButton = 0x0200 // Don't display the 'New Folder' button + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public class BROWSEINFO + { + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr hwndOwner; //HWND hwndOwner; // HWND of the owner for the dialog + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr pidlRoot; //LPCITEMIDLIST pidlRoot; // Root ITEMIDLIST + + // For interop purposes, send over a buffer of MAX_PATH size. + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr pszDisplayName; //LPWSTR pszDisplayName; // Return display name of item selected. + + public string lpszTitle; //LPCWSTR lpszTitle; // text to go in the banner over the tree. + public int ulFlags; //UINT ulFlags; // Flags that control the return stuff + public BrowseCallbackProc lpfn; //BFFCALLBACK lpfn; // Call back pointer + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr lParam; //LPARAM lParam; // extra info that's passed back in callbacks + public int iImage; //int iImage; // output var: where to return the Image index. + } + + [SuppressUnmanagedCodeSecurity(), + SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class Shell32 + { + [DllImport(ExternDll.Shell32)] + [ResourceExposure(ResourceScope.None)] + public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl); + //SHSTDAPI SHGetSpecialFolderLocation(HWND hwnd, int csidl, LPITEMIDLIST *ppidl); + + [DllImport(ExternDll.Shell32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern bool SHGetPathFromIDListEx(IntPtr pidl, IntPtr pszPath, int cchPath, int flags); + //SHSTDAPI_(BOOL) SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath); + + public static bool SHGetPathFromIDListLongPath(IntPtr pidl, ref IntPtr pszPath) + { + int noOfTimes = 1; + // This is how size was allocated in the calling method. + int bufferSize = NativeMethods.MAX_PATH * Marshal.SystemDefaultCharSize; + int length = NativeMethods.MAX_PATH; + bool result = false; + + // SHGetPathFromIDListEx returns false in case of insufficient buffer. + // This method does not distinguish between insufficient memory and an error. Until we get a proper solution, + // this logic would work. In the worst case scenario, loop exits when length reaches unicode string length. + while ((result = SHGetPathFromIDListEx(pidl, pszPath, length, 0)) == false + && length < NativeMethods.MAX_UNICODESTRING_LEN) + { + string path = Marshal.PtrToStringAuto(pszPath); + + if (path.Length != 0 && path.Length < length) + break; + + noOfTimes += 2; //520 chars capacity increase in each iteration. + length = noOfTimes * length >= NativeMethods.MAX_UNICODESTRING_LEN + ? NativeMethods.MAX_UNICODESTRING_LEN : noOfTimes * length; + pszPath = Marshal.ReAllocHGlobal(pszPath, (IntPtr)((length + 1) * Marshal.SystemDefaultCharSize)); + } + + return result; + } + + [DllImport(ExternDll.Shell32, CharSet=CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr SHBrowseForFolder([In] BROWSEINFO lpbi); + //SHSTDAPI_(LPITEMIDLIST) SHBrowseForFolderW(LPBROWSEINFOW lpbi); + + [DllImport(ExternDll.Shell32)] + [ResourceExposure(ResourceScope.None)] + public static extern int SHGetMalloc([Out, MarshalAs(UnmanagedType.LPArray)] UnsafeNativeMethods.IMalloc[] ppMalloc); + //SHSTDAPI SHGetMalloc(LPMALLOC * ppMalloc); + + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport(ExternDll.Shell32, PreserveSig = true)] + [ResourceExposure(ResourceScope.None)] + private static extern int SHGetKnownFolderPath(ref Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath); + + public static int SHGetFolderPathEx(ref Guid rfid, uint dwFlags, IntPtr hToken, StringBuilder pszPath) + { + if (IsVista) + { + IntPtr path = IntPtr.Zero; + int result = -1; + if ((result = SHGetKnownFolderPath(ref rfid, dwFlags, hToken, out path)) == NativeMethods.S_OK) + { + pszPath.Append(Marshal.PtrToStringAuto(path)); + CoTaskMemFree(path); + } + return result; + } + throw new NotSupportedException(); + } + + [DllImport(ExternDll.Shell32, PreserveSig = true)] + [ResourceExposure(ResourceScope.None)] + public static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out FileDialogNative.IShellItem ppsi); + + [DllImport(ExternDll.Shell32, PreserveSig = true)] + [ResourceExposure(ResourceScope.None)] + public static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)]string pszPath, out IntPtr ppIdl, ref uint rgflnOut); + } + + [ + ComImport(), + Guid("00000002-0000-0000-c000-000000000046"), + System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), + SuppressUnmanagedCodeSecurity() + ] + public interface IMalloc + { + [PreserveSig] + IntPtr Alloc(int cb); + + [PreserveSig] + IntPtr Realloc(IntPtr pv, int cb); + + [PreserveSig] + void Free(IntPtr pv); + + [PreserveSig] + int GetSize(IntPtr pv); + + [PreserveSig] + int DidAlloc(IntPtr pv); + + [PreserveSig] + void HeapMinimize(); + } + + [ + ComImport, + Guid("00000126-0000-0000-C000-000000000046"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IRunnableObject + { + void GetRunningClass(out Guid guid); + + [PreserveSig] + int Run(IntPtr lpBindContext); + bool IsRunning(); + void LockRunning(bool fLock, bool fLastUnlockCloses); + void SetContainedObject(bool fContained); + } + + [ComVisible(true), ComImport(), Guid("B722BCC7-4E68-101B-A2BC-00AA00404770"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleDocumentSite + { + + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int ActivateMe( + [In, MarshalAs(UnmanagedType.Interface)] + IOleDocumentView pViewToActivate); + + } + + [ComVisible(true), Guid("B722BCC6-4E68-101B-A2BC-00AA00404770"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IOleDocumentView + { + + void SetInPlaceSite( + [In, MarshalAs(UnmanagedType.Interface)] + IOleInPlaceSite pIPSite); + + [return: MarshalAs(UnmanagedType.Interface)] + IOleInPlaceSite GetInPlaceSite(); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetDocument(); + + + void SetRect( + [In] + ref NativeMethods.RECT prcView); + + + void GetRect( + [In, Out] + ref NativeMethods.RECT prcView); + + + void SetRectComplex( + [In] + NativeMethods.RECT prcView, + [In] + NativeMethods.RECT prcHScroll, + [In] + NativeMethods.RECT prcVScroll, + [In] + NativeMethods.RECT prcSizeBox); + + + void Show(bool fShow); + + + [PreserveSig] + int UIActivate(bool fUIActivate); + + + void Open(); + + [PreserveSig] + int Close( + [In, MarshalAs(UnmanagedType.U4)] + int dwReserved); + + + void SaveViewState( + [In, MarshalAs(UnmanagedType.Interface)] + IStream pstm); + + + void ApplyViewState( + [In, MarshalAs(UnmanagedType.Interface)] + IStream pstm); + + + void Clone( + [In, MarshalAs(UnmanagedType.Interface)] + IOleInPlaceSite pIPSiteNew, + [Out, MarshalAs(UnmanagedType.LPArray)] + IOleDocumentView[] ppViewNew); + + + } + + [ + ComImport, + Guid("b722bcc5-4e68-101b-a2bc-00aa00404770"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IOleDocument + { + + [PreserveSig] + int CreateView(IOleInPlaceSite pIPSite, + IStream pstm, + int dwReserved, + out IOleDocumentView ppView); + + [PreserveSig] + int GetDocMiscStatus( + out int pdwStatus); + + int EnumViews( + out object ppEnum, + out IOleDocumentView ppView); + } + + [ + Guid("0000011e-0000-0000-C000-000000000046"), ComImport, InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IOleCache + { + int Cache(ref FORMATETC pformatetc,int advf); + + void Uncache(int dwConnection); + + object EnumCache(/*[out] IEnumSTATDATA **ppenumSTATDATA*/); + + void InitCache(IComDataObject pDataObject); + + void SetData(ref FORMATETC pformatetc, ref STGMEDIUM pmedium, bool fRelease); + } + + + + + + [ComImport, + TypeLibType(0x1050), + Guid("618736E0-3C3D-11CF-810C-00AA00389B71"), + ] + public interface IAccessibleInternal { + [return: MarshalAs(UnmanagedType.IDispatch)] + [DispId(unchecked((int)0xFFFFEC78))] + [TypeLibFunc(0x0040)] + object get_accParent(); + + [DispId(unchecked((int)0xFFFFEC77))] + [TypeLibFunc(0x0040)] + int get_accChildCount(); + + [return: MarshalAs(UnmanagedType.IDispatch)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC76))] + object get_accChild([In][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.BStr)] + [DispId(unchecked((int)0xFFFFEC75))] + [TypeLibFunc(0x0040)] + string get_accName([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.BStr)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC74))] + string get_accValue([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.BStr)] + [DispId(unchecked((int)0xFFFFEC73))] + [TypeLibFunc(0x0040)] + string get_accDescription([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.Struct)] + [DispId(unchecked((int)0xFFFFEC72))] + [TypeLibFunc(0x0040)] + object get_accRole([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.Struct)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC71))] + object get_accState([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.BStr)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC70))] + string get_accHelp([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [DispId(unchecked((int)0xFFFFEC6F))] + [TypeLibFunc(0x0040)] + int get_accHelpTopic([Out][MarshalAs(UnmanagedType.BStr)] out string pszHelpFile, + [In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.BStr)] + [DispId(unchecked((int)0xFFFFEC6E))] + [TypeLibFunc(0x0040)] + string get_accKeyboardShortcut([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.Struct)] + [DispId(unchecked((int)0xFFFFEC6D))] + [TypeLibFunc(0x0040)] + object get_accFocus(); + + [return: MarshalAs(UnmanagedType.Struct)] + [DispId(unchecked((int)0xFFFFEC6C))] + [TypeLibFunc(0x0040)] + object get_accSelection(); + + [return: MarshalAs(UnmanagedType.BStr)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC6B))] + string get_accDefaultAction([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [DispId(unchecked((int)0xFFFFEC6A))] + [TypeLibFunc(0x0040)] + void accSelect([In] int flagsSelect, + [In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [DispId(unchecked((int)0xFFFFEC69))] + [TypeLibFunc(0x0040)] + void accLocation([Out] out int pxLeft, + [Out] out int pyTop, + [Out] out int pcxWidth, + [Out] out int pcyHeight, + [In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [return: MarshalAs(UnmanagedType.Struct)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC68))] + object accNavigate([In] int navDir, + [In][Optional][MarshalAs(UnmanagedType.Struct)] object varStart); + + [return: MarshalAs(UnmanagedType.Struct)] + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC67))] + object accHitTest([In] int xLeft, + [In] int yTop); + + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC66))] + void accDoDefaultAction([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild); + + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC75))] + void set_accName([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild, + [In][MarshalAs(UnmanagedType.BStr)] string pszName); + + [TypeLibFunc(0x0040)] + [DispId(unchecked((int)0xFFFFEC74))] + void set_accValue([In][Optional][MarshalAs(UnmanagedType.Struct)] object varChild, + [In][MarshalAs(UnmanagedType.BStr)] string pszValue); + } + + [ + ComImport(), + Guid("BEF6E002-A874-101A-8BBA-00AA00300CAB"), + System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] + public interface IFont + { + + [return: MarshalAs(UnmanagedType.BStr)] + string GetName(); + + void SetName( + [In, MarshalAs(UnmanagedType.BStr)] + string pname); + + [return: MarshalAs(UnmanagedType.U8)] + long GetSize(); + + void SetSize( + [In, MarshalAs(UnmanagedType.U8)] + long psize); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetBold(); + + void SetBold( + [In, MarshalAs(UnmanagedType.Bool)] + bool pbold); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetItalic(); + + void SetItalic( + [In, MarshalAs(UnmanagedType.Bool)] + bool pitalic); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetUnderline(); + + void SetUnderline( + [In, MarshalAs(UnmanagedType.Bool)] + bool punderline); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetStrikethrough(); + + void SetStrikethrough( + [In, MarshalAs(UnmanagedType.Bool)] + bool pstrikethrough); + + [return: MarshalAs(UnmanagedType.I2)] + short GetWeight(); + + void SetWeight( + [In, MarshalAs(UnmanagedType.I2)] + short pweight); + + [return: MarshalAs(UnmanagedType.I2)] + short GetCharset(); + + void SetCharset( + [In, MarshalAs(UnmanagedType.I2)] + short pcharset); + + IntPtr GetHFont(); + + void Clone( + out UnsafeNativeMethods.IFont ppfont); + + [System.Runtime.InteropServices.PreserveSig] + int IsEqual( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IFont pfontOther); + + void SetRatio( + int cyLogical, + int cyHimetric); + + void QueryTextMetrics(out IntPtr ptm); + + void AddRefHfont( + IntPtr hFont); + + void ReleaseHfont( + IntPtr hFont); + + void SetHdc( + IntPtr hdc); + } + + [ComImport(), Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] + public interface IPicture + { + IntPtr GetHandle(); + + IntPtr GetHPal(); + + [return: MarshalAs(UnmanagedType.I2)] + short GetPictureType(); + + int GetWidth(); + + int GetHeight(); + + void Render( + IntPtr hDC, + int x, + int y, + int cx, + int cy, + int xSrc, + int ySrc, + int cxSrc, + int cySrc, + IntPtr rcBounds + ); + + void SetHPal( + IntPtr phpal); + + IntPtr GetCurDC(); + + void SelectPicture( + IntPtr hdcIn, + [Out, MarshalAs(UnmanagedType.LPArray)] + IntPtr[] phdcOut, + [Out, MarshalAs(UnmanagedType.LPArray)] + IntPtr[] phbmpOut); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetKeepOriginalFormat(); + + void SetKeepOriginalFormat( + [In, MarshalAs(UnmanagedType.Bool)] + bool pfkeep); + + void PictureChanged(); + + [PreserveSig] + int SaveAsFile( + [In, MarshalAs(UnmanagedType.Interface)] + UnsafeNativeMethods.IStream pstm, + + int fSaveMemCopy, + [Out] + out int pcbSize); + + int GetAttributes(); + } + + [ComImport(), Guid("7BF80981-BF32-101A-8BBB-00AA00300CAB"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)] + public interface IPictureDisp + { + IntPtr Handle { get;} + + IntPtr HPal { get;} + + short PictureType { get;} + + int Width { get;} + + int Height { get;} + + void Render( + IntPtr hdc, + int x, + int y, + int cx, + int cy, + int xSrc, + int ySrc, + int cxSrc, + int cySrc); + } + + /// + /// + /// This class provides static methods to create, activate and deactivate the theming scope. + /// + [SuppressUnmanagedCodeSecurity] + internal class ThemingScope + { + private static ACTCTX enableThemingActivationContext; + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private static IntPtr hActCtx; + private static bool contextCreationSucceeded; + + /// + /// We now use explicitactivate everywhere and use this method to determine if we + /// really need to activate the activationcontext. This should be pretty fast. + /// + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + private static bool IsContextActive() + { + IntPtr current = IntPtr.Zero; + + if (contextCreationSucceeded && GetCurrentActCtx(out current)) + { + return current == hActCtx; + } + return false; + } + + /// + /// Activate() does nothing if a theming context is already active on the current thread, which is good + /// for perf reasons. However, in some cases, like in the Timer callback, we need to put another context + /// on the stack even if one is already present. In such cases, this method helps - you get to manage + /// the cookie yourself though. + /// + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static IntPtr Activate() + { + IntPtr userCookie = IntPtr.Zero; + + if (Application.UseVisualStyles && contextCreationSucceeded && OSFeature.Feature.IsPresent(OSFeature.Themes)) + { + if (!IsContextActive()) + { + if (!ActivateActCtx(hActCtx, out userCookie)) + { + // Be sure cookie always zero if activation failed + userCookie = IntPtr.Zero; + } + } + } + + return userCookie; + } + + /// + /// Use this to deactivate a context activated by calling ExplicitActivate. + /// + public static IntPtr Deactivate(IntPtr userCookie) + { + if (userCookie != IntPtr.Zero && OSFeature.Feature.IsPresent(OSFeature.Themes)) + { + if (DeactivateActCtx(0, userCookie)) + { + // deactivation succeeded... + userCookie = IntPtr.Zero; + } + } + + return userCookie; + } + + [ + // Ok to suppress because Microsoft apps cannot bleed accross different AppDomains + // and ThemingScope class is not public so external code can't lock on typeof(ThemingScope). + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity") + ] + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + public static bool CreateActivationContext(string dllPath, int nativeResourceManifestID) + { + lock (typeof(ThemingScope)) + { + if (!contextCreationSucceeded && OSFeature.Feature.IsPresent(OSFeature.Themes)) + { + + enableThemingActivationContext = new ACTCTX(); + + enableThemingActivationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX)); + enableThemingActivationContext.lpSource = dllPath; + enableThemingActivationContext.lpResourceName = (IntPtr)nativeResourceManifestID; + enableThemingActivationContext.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; + + hActCtx = CreateActCtx(ref enableThemingActivationContext); + contextCreationSucceeded = (hActCtx != new IntPtr(-1)); + } + + return contextCreationSucceeded; + } + } + + // All the pinvoke goo... + [DllImport(ExternDll.Kernel32)] + [ResourceExposure(ResourceScope.Process)] + private extern static IntPtr CreateActCtx(ref ACTCTX actctx); + [DllImport(ExternDll.Kernel32)] + [ResourceExposure(ResourceScope.Process)] + private extern static bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie); + [DllImport(ExternDll.Kernel32)] + [ResourceExposure(ResourceScope.None)] + private extern static bool DeactivateActCtx(int dwFlags, IntPtr lpCookie); + [DllImport(ExternDll.Kernel32)] + [ResourceExposure(ResourceScope.Process)] + private extern static bool GetCurrentActCtx(out IntPtr handle); + + private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004; + private const int ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x008; + + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + private struct ACTCTX + { + public int cbSize; + public uint dwFlags; + public string lpSource; + public ushort wProcessorArchitecture; + public ushort wLangId; + public string lpAssemblyDirectory; + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr lpResourceName; + public string lpApplicationName; + } + } + + // ClickOnce related interop + [ + StructLayout(LayoutKind.Sequential), + System.Security.SuppressUnmanagedCodeSecurityAttribute() + ] + internal class PROCESS_INFORMATION + { + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr hProcess = IntPtr.Zero; + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + public IntPtr hThread = IntPtr.Zero; + public int dwProcessId = 0; + public int dwThreadId = 0; + + // Note this will guarantee we will always free the handles + // so unless you duplicate the handles from PROCESS_INFORMATION class + // do not close those handles. + ~PROCESS_INFORMATION() + { + Close(); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal void Close() + { + if (hProcess != (IntPtr)0 && hProcess != (IntPtr)INVALID_HANDLE_VALUE) + { + CloseHandle(new HandleRef(this, hProcess)); + hProcess = INVALID_HANDLE_VALUE; + } + + if (hThread != (IntPtr)0 && hThread != (IntPtr)INVALID_HANDLE_VALUE) + { + CloseHandle(new HandleRef(this, hThread)); + hThread = INVALID_HANDLE_VALUE; + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + + [DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)] + [ResourceExposure(ResourceScope.None)] + private static extern bool CloseHandle(HandleRef handle); + } + + [DllImport(ExternDll.Clr, CharSet=CharSet.Unicode, PreserveSig=false, SetLastError=false, BestFitMapping=false, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + internal static extern void CorLaunchApplication(UInt32 hostType, + string applicationFullName, + int manifestPathsCount, + string[] manifestPaths, + int activationDataCount, + string[] activationData, + PROCESS_INFORMATION processInformation); + + // UIAutomationCore methods + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode)] + internal static extern int UiaHostProviderFromHwnd(HandleRef hwnd, out IRawElementProviderSimple provider); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode)] + internal static extern IntPtr UiaReturnRawElementProvider(HandleRef hwnd, IntPtr wParam, IntPtr lParam, IRawElementProviderSimple el); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode)] + internal static extern bool UiaClientsAreListening(); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int UiaRaiseAutomationEvent(IRawElementProviderSimple provider, int id); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int UiaRaiseAutomationPropertyChangedEvent(IRawElementProviderSimple provider, int id, object oldValue, object newValue); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int UiaRaiseNotificationEvent( + IRawElementProviderSimple provider, + Automation.AutomationNotificationKind notificationKind, + Automation.AutomationNotificationProcessing notificationProcessing, + string notificationText, + string activityId); + + [DllImport(ExternDll.UiaCore, CharSet = CharSet.Unicode)] + internal static extern int UiaRaiseStructureChangedEvent(IRawElementProviderSimple provider, StructureChangeType structureChangeType, int[] runtimeId, int runtimeIdLen); + + // UIAutomation interfaces and enums + // obtained from UIAutomation source code + + /// + /// Logical structure change flags + /// + [ComVisible(true)] + [Guid("e4cfef41-071d-472c-a65c-c14f59ea81eb")] + public enum StructureChangeType { + /// Logical child added + ChildAdded, + /// Logical child removed + ChildRemoved, + /// Logical children invalidated + ChildrenInvalidated, + /// Logical children were bulk added + ChildrenBulkAdded, + /// Logical children were bulk removed + ChildrenBulkRemoved, + /// The order of the children below their parent has changed. + ChildrenReordered, + } + + [ComVisible(true)] + [Guid("76d12d7e-b227-4417-9ce2-42642ffa896a")] + public enum ExpandCollapseState { + /// No children are showing + Collapsed, + /// All children are showing + Expanded, + /// Not all children are showing + PartiallyExpanded, + /// Does not expand or collapse + LeafNode + } + + [Flags] + public enum ProviderOptions { + /// Indicates that this is a client-side provider + ClientSideProvider = 0x0001, + /// Indicates that this is a server-side provider + ServerSideProvider = 0x0002, + /// Indicates that this is a non-client-area provider + NonClientAreaProvider = 0x0004, + /// Indicates that this is an override provider + OverrideProvider = 0x0008, + + /// Indicates that this provider handles its own focus, and does not want + /// UIA to set focus to the nearest HWND on its behalf when AutomationElement.SetFocus + /// is used. This option is typically used by providers for HWNDs that appear to take + /// focus without actually receiving actual Win32 focus, such as menus and dropdowns + ProviderOwnsSetFocus = 0x0010, + + /// Indicates that this provider expects to be called according to COM threading rules: + /// if the provider is in a Single-Threaded Apartment, it will be called only on the apartment + /// thread. Only Server-side providers can use this option. + UseComThreading = 0x0020 + } + + public static readonly Guid guid_IAccessibleEx = new Guid("{F8B80ADA-2C44-48D0-89BE-5FF23C9CD875}"); + + /// + /// The interface representing containers that manage selection. + /// + /// + /// Client code uses this public interface; server implementers implent the + /// ISelectionProvider public interface instead. + /// + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("fb8b03af-3bdf-48d4-bd36-1a65793be168")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISelectionProvider { + /// + /// Get the currently selected elements + /// + /// An AutomationElement array containing the currently selected elements + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + /* IRawElementProviderSimple */ object[] GetSelection(); + + /// + /// Indicates whether the control allows more than one element to be selected + /// + /// Boolean indicating whether the control allows more than one element to be selected + /// If this is false, then the control is a single-select ccntrol + bool CanSelectMultiple { + [return: MarshalAs(UnmanagedType.Bool)] + get; + } + + /// + /// Indicates whether the control requires at least one element to be selected + /// + /// Boolean indicating whether the control requires at least one element to be selected + /// If this is false, then the control allows all elements to be unselected + bool IsSelectionRequired { + [return: MarshalAs(UnmanagedType.Bool)] + get; + } + } + + /// + /// Define a Selectable Item (only supported on logical elements that are a + /// child of an Element that supports SelectionPattern and is itself selectable). + /// This allows for manipulation of Selection from the element itself. + /// + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComVisible(true)] + [Guid("2acad808-b2d4-452d-a407-91ff1ad167b2")] + public interface ISelectionItemProvider + { + /// + /// Sets the current element as the selection + /// This clears the selection from other elements in the container. + /// + void Select(); + + /// + /// Adds current element to selection. + /// + void AddToSelection(); + + /// + /// Removes current element from selection. + /// + void RemoveFromSelection(); + + /// + /// Check whether an element is selected. + /// + /// Returns true if the element is selected. + bool IsSelected { [return: MarshalAs(UnmanagedType.Bool)] get; } + + /// + /// The logical element that supports the SelectionPattern for this Item. + /// + /// Returns a IRawElementProviderSimple. + IRawElementProviderSimple SelectionContainer { [return: MarshalAs(UnmanagedType.Interface)] get; } + } + + /// + /// Implemented by providers which want to provide information about or want to + /// reposition contained HWND-based elements. + /// + [ComVisible(true)] + [Guid("1d5df27c-8947-4425-b8d9-79787bb460b8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IRawElementProviderHwndOverride : IRawElementProviderSimple { + /// + /// Request a provider for the specified component. The returned provider can supply additional + /// properties or override properties of the specified component. + /// + /// The window handle of the component. + /// Return the provider for the specified component, or null if the component is not being overridden. + [return: MarshalAs(UnmanagedType.Interface)] + IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd); + } + + /// + /// Critical:Elevates to Unmanaged code permission + /// + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IServiceProvider { + /// + /// Critical elevates via a SUC. + /// + [SuppressUnmanagedCodeSecurity, SecurityCritical] + [PreserveSig] + int QueryService(ref Guid service, ref Guid riid, out IntPtr ppvObj); + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComVisible(true)] + [ComImport()] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliantAttribute(false)] + [Guid("F8B80ADA-2C44-48D0-89BE-5FF23C9CD875")] + internal interface IAccessibleEx { + // Returns the IAccessibleEx for specified child. Returns + // S_OK/NULL if this implementation does not use child ids, + // or does not have an IAccessibleEx for the specified child, + // or already represents a child element. + // idChild must be normalized; ie. client must have previously + // used get_accChild to check whether it actually has its own + // IAccessible. Only idChild values that do not have a corresponding + // IAccessible can be used here. + + [return: MarshalAs(UnmanagedType.IUnknown)] + object GetObjectForChild(int idChild); + + // Returns an IAccessible and idChild pair for this IAccessibleEx. + // Implementation must return fully normalized idChild values: ie. + // it is not required to call get_accChild on the resulting pair. + // + // For IAccessible implementations that do not use child ids, this + // just returns the corresponding IAccessible and CHILDID_SELF. + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int GetIAccessiblePair( + [Out, MarshalAs(UnmanagedType.Interface)] + out object /*UnsafeNativeMethods.IAccessible*/ ppAcc, + [Out] out int pidChild); + + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4)] + int[] GetRuntimeId(); + + // Some wrapper-based implementations (notably UIABridge) can't reasonably wrap all + // IRawElementProviderSimple elements returned as property values or patterns, so + // these elements won't QI to IAccessibleEx. Where this is the case, the original + // IAccessibleEx that the property was retreived from must implement this method + // so that the client can get an IAccessibleEx. + // + // Usage for a client is as follows: + // When an IRawElementProviderSimple is obtained as a property value, + // - first try to QI to IAccessibleEx + // - if that fails, call this method on the source IAccessibleEx + [return: MarshalAs(UnmanagedType.I4)] + [PreserveSig] + int ConvertReturnedElement( + [In, MarshalAs(UnmanagedType.Interface)] + object /*UnsafeNativeMethods.IRawElementProviderSimple*/ pIn, + [Out, MarshalAs(UnmanagedType.Interface)] + out object /*UnsafeNativeMethods.IAccessibleEx*/ ppRetValOut); + } + + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComVisible(true)] + [ComImport()] + [Guid("d847d3a5-cab0-4a98-8c32-ecb45c59ad24")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IExpandCollapseProvider { + /// + /// Blocking method that returns after the element has been expanded. + /// + void Expand(); + + /// + /// Blocking method that returns after the element has been collapsed. + /// + void Collapse(); + + ///indicates an element's current Collapsed or Expanded state + ExpandCollapseState ExpandCollapseState { + get; + } + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComVisible(true)] + [ComImport()] + [Guid("c7935180-6fb3-4201-b174-7df73adbf64a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IValueProvider { + /// + /// Request to set the value that this UI element is representing + /// + /// Value to set the UI to + void SetValue([MarshalAs(UnmanagedType.LPWStr)] string value); + + ///Value of a value control, as a a string. + string Value { + get; + } + + ///Indicates that the value can only be read, not modified. + ///returns True if the control is read-only + bool IsReadOnly { + [return: MarshalAs(UnmanagedType.Bool)] // CLR bug? Without this, only lower SHORT of BOOL*pRetVal param is updated. + get; + } + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("36dc7aef-33e6-4691-afe1-2be7274b3d33")] + public interface IRangeValueProvider { + void SetValue(double value); + + double Value { get; } + + bool IsReadOnly { [return: MarshalAs(UnmanagedType.Bool)] get; } + + double Maximum { get; } + + double Minimum { get; } + + double LargeChange { get; } + + double SmallChange { get; } + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("D6DD68D1-86FD-4332-8666-9ABEDEA2D24C")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliantAttribute(false)] + public interface IRawElementProviderSimple { + /// + /// Indicates the type of provider this is, for example, whether it is a client-side + /// or server-side provider. + /// + /// + /// Providers must specify at least either one of ProviderOptions.ClientSideProvider + /// or ProviderOptions.ServerSideProvider. + /// + /// UIAutomation treats different types of providers + /// differently - for example, events from server-side provider are broadcast to all listening + /// clients, whereas events from client-side providers remain in that client. + /// + ProviderOptions ProviderOptions { + get; + } + + /// + /// Get a pattern interface from this object + /// + /// Identifier indicating the interface to return + /// Returns the interface as an object, if supported; otherwise returns null/ + [return: MarshalAs(UnmanagedType.IUnknown)] + object GetPatternProvider(int patternId); + + /// + /// Request value of specified property from an element. + /// + /// Identifier indicating the property to return + /// Returns a ValInfo indicating whether the element supports this property, or has no value for it. + object GetPropertyValue(int propertyId); + + // Only native impl roots need to return something for this, + // proxies always return null (cause we already know their HWNDs) + // If proxies create themselves when handling winvents events, then they + // also need to implement this so we can determine the HWND. Still only + // lives on a root, however. + /// + /// Returns a base provider for this element. + /// + /// Typically only used by elements that correspond directly to a Win32 Window Handle, + /// in which case the implementation returns AutomationInteropProvider.BaseElementFromHandle( hwnd ). + /// + IRawElementProviderSimple HostRawElementProvider { + get; + } + } + + /// + /// Directions for navigation the UIAutomation tree + /// + [ComVisible(true)] + [Guid("670c3006-bf4c-428b-8534-e1848f645122")] + public enum NavigateDirection { + /// Navigate to parent + Parent, + /// Navigate to next sibling + NextSibling, + /// Navigate to previous sibling + PreviousSibling, + /// Navigate to first child + FirstChild, + /// Navigate to last child + LastChild, + } + + /// + /// Implemented by providers to expose elements that are part of + /// a structure more than one level deep. For simple one-level + /// structures which have no children, IRawElementProviderSimple + /// can be used instead. + /// + /// The root node of the fragment must support the IRawElementProviderFragmentRoot + /// interface, which is derived from this, and has some additional methods. + /// + [ComVisible(true)] + [Guid("f7063da8-8359-439c-9297-bbc5299a7d87")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [CLSCompliant(false)] + public interface IRawElementProviderFragment : IRawElementProviderSimple { + /// + /// Request to return the element in the specified direction + /// + /// Indicates the direction in which to navigate + /// Returns the element in the specified direction + [return: MarshalAs(UnmanagedType.IUnknown)] + object /*IRawElementProviderFragment*/ Navigate(NavigateDirection direction); + + + /// + /// Gets the runtime ID of an elemenent. This should be unique + /// among elements on a desktop. + /// + /// + /// Proxy implementations should return null for the top-level proxy which + /// correpsonds to the HWND; and should return an array which starts + /// with AutomationInteropProvider.AppendRuntimeId, followed by values + /// which are then unique within that proxy's HWNDs. + /// + int[] GetRuntimeId(); + + /// + /// Return a bounding rectangle of this element + /// + NativeMethods.UiaRect BoundingRectangle { + get; + } + + /// + /// If this UI is capable of hosting other UI that also supports UIAutomation, and + /// the subtree rooted at this element contains such hosted UI fragments, this should return + /// an array of those fragments. + /// + /// If this UI does not host other UI, it may return null. + /// + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + object[] /*IRawElementProviderSimple[]*/ GetEmbeddedFragmentRoots(); + + /// + /// Request that focus is set to this item. + /// The UIAutomation framework will ensure that the UI hosting this fragment is already + /// focused before calling this method, so this method should only update its internal + /// focus state; it should not attempt to give its own HWND the focus, for example. + /// + void SetFocus(); + + /// + /// Return the element that is the root node of this fragment of UI. + /// + IRawElementProviderFragmentRoot FragmentRoot { + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + } + + /// + /// The root element in a fragment of UI must support this interface. Other + /// elements in the same fragment need to support the IRawElementProviderFragment + /// interface. + /// + [ComVisible(true)] + [Guid("620ce2a5-ab8f-40a9-86cb-de3c75599b58")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [CLSCompliant(false)] + public interface IRawElementProviderFragmentRoot : IRawElementProviderFragment { + /// + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + /// x coordinate of point to check + /// y coordinate of point to check + /// Return the child element at the specified point, if one exists, + /// otherwise return this element if the point is on this element, + /// otherwise return null. + /// + [return: MarshalAs(UnmanagedType.IUnknown)] + object /*IRawElementProviderFragment*/ ElementProviderFromPoint(double x, double y); + + /// + /// Return the element in this fragment which has the keyboard focus, + /// + /// Return the element in this fragment which has the keyboard focus, + /// if any; otherwise return null. + [return: MarshalAs(UnmanagedType.IUnknown)] + object /*IRawElementProviderFragment*/ GetFocus(); + } + + [Flags] + public enum ToggleState { + ToggleState_Off = 0, + ToggleState_On = 1, + ToggleState_Indeterminate = 2 + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("56D00BD0-C4F4-433C-A836-1A52A57E0892")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliantAttribute(false)] + public interface IToggleProvider { + void Toggle(); + + ///indicates an element's current on or off state + ToggleState ToggleState { + get; + } + } + + [Flags] + public enum RowOrColumnMajor { + RowOrColumnMajor_RowMajor = 0, + RowOrColumnMajor_ColumnMajor = 1, + RowOrColumnMajor_Indeterminate = 2 + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("9c860395-97b3-490a-b52a-858cc22af166")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface ITableProvider { + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + object[] /*IRawElementProviderSimple[]*/ GetRowHeaders(); + + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + object[] /*IRawElementProviderSimple[]*/ GetColumnHeaders(); + + RowOrColumnMajor RowOrColumnMajor { + get; + } + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("b9734fa6-771f-4d78-9c90-2517999349cd")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface ITableItemProvider { + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + object[] /*IRawElementProviderSimple[]*/ GetRowHeaderItems(); + + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + object[] /*IRawElementProviderSimple[]*/ GetColumnHeaderItems(); + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("b17d6187-0907-464b-a168-0ef17a1572b1")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface IGridProvider { + [return: MarshalAs(UnmanagedType.IUnknown)] + object /*IRawElementProviderSimple*/ GetItem(int row, int column); + + int RowCount { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + + int ColumnCount { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + } + + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("d02541f1-fb81-4d64-ae32-f520f8a6dbd1")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface IGridItemProvider { + int Row { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + + int Column { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + + int RowSpan { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + + int ColumnSpan { + [return: MarshalAs(UnmanagedType.I4)] + get; + } + + IRawElementProviderSimple ContainingGrid { + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + } + + /// + /// Implemented by objects that have a single, unambiguous, action associated with them. + /// These objects are usually stateless, and invoking them does not change their own state, + /// but causes something to happen in the larger context of the app the control is in. + /// + /// Examples of UI that implments this includes: + /// Push buttons + /// Hyperlinks + /// Menu items + /// + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("54fcb24b-e18e-47a2-b4d3-eccbe77599a2")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface IInvokeProvider { + /// + /// Request that the control initiate its action. + /// Should return immediately without blocking. + /// There is no way to determine what happened, when it happend, or whether + /// anything happened at all. + /// + void Invoke(); + } + + /// + /// Implemented by objects in a known Scrollable context, such as ListItems, ListViewItems, TreeViewItems, and Tabs. + /// This allows them to be scrolled into view using known API's based on the control in question. + /// + [SecurityCritical(SecurityCriticalScope.Everything)] + [ComImport()] + [ComVisible(true)] + [Guid("2360c714-4bf1-4b26-ba65-9b21316127eb")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [CLSCompliant(false)] + public interface IScrollItemProvider { + /// + /// Scrolls the windows containing this automation element to make this element visible. + /// InvalidOperationException should be thrown if item becomes unable to be scrolled. Makes + /// no guarantees about where the item will be in the scrolled window. + /// + void ScrollIntoView(); + } + + public static IntPtr LoadLibraryFromSystemPathIfAvailable(string libraryName) { + IntPtr module = IntPtr.Zero; + + // KB2533623 introduced the LOAD_LIBRARY_SEARCH_SYSTEM32 flag. It also introduced + // the AddDllDirectory function. We test for presence of AddDllDirectory as an + // indirect evidence for the support of LOAD_LIBRARY_SEARCH_SYSTEM32 flag. + IntPtr kernel32 = GetModuleHandle(ExternDll.Kernel32); + if (kernel32 != IntPtr.Zero) { + if (GetProcAddress(new HandleRef(null, kernel32), "AddDllDirectory") != IntPtr.Zero) { + module = LoadLibraryEx(libraryName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_SEARCH_SYSTEM32); + } else { + // LOAD_LIBRARY_SEARCH_SYSTEM32 is not supported on this OS. + // Fall back to using plain ol' LoadLibrary + // There is risk that this call might fail, or that it might be + // susceptible to DLL hijacking. + module = LoadLibrary(libraryName); + } + } + return module; + } + + /// + /// Retrieves a value that describes the Device Guard policy enforcement status for .NET dynamic code. + /// introduced in RS4 (Win10 1803) + /// + /// On success, returns true if the Device Guard policy enforces .NET Dynamic Code policy; otherwise, returns false. + /// This method returns S_OK if successful or a failure code otherwise. + [DllImport(ExternDll.Wldp, ExactSpelling = true)] + [ResourceExposure(ResourceScope.None)] + private static extern int WldpIsDynamicCodePolicyEnabled([Out] out int enabled); + + internal static bool IsDynamicCodePolicyEnabled() { + if (!ApiHelper.IsApiAvailable(ExternDll.Wldp, "WldpIsDynamicCodePolicyEnabled")) { + return false; + } + // Default to a compatible case + int isEnabled = 0; + int result = UnsafeNativeMethods.WldpIsDynamicCodePolicyEnabled(out isEnabled); + return ((result == NativeMethods.S_OK) && (isEnabled != 0)); + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/UpDownBase.cs b/WindowsForms/Managed/System/WinForms/UpDownBase.cs new file mode 100644 index 000000000..7e4f7b8f3 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UpDownBase.cs @@ -0,0 +1,1960 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.InteropServices; + using System.ComponentModel; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Windows.Forms.VisualStyles; + using System.Drawing; + using Microsoft.Win32; + + /// + /// + /// Implements the basic + /// functionality required by an up-down control. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.UpDownBaseDesigner, " + AssemblyRef.SystemDesign), + System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors") // Shipped in Everett + ] + public abstract class UpDownBase : ContainerControl { + + private const int DefaultWheelScrollLinesPerPage = 1; + private const int DefaultButtonsWidth = 16; + private const int DefaultControlWidth = 120; + private const int ThemedBorderWidth = 1; // width of custom border we draw when themed + private const BorderStyle DefaultBorderStyle = BorderStyle.Fixed3D; + private static readonly bool DefaultInterceptArrowKeys = true; + private const LeftRightAlignment DefaultUpDownAlign = LeftRightAlignment.Right; + private const int DefaultTimerInterval = 500; + + //////////////////////////////////////////////////////////////////////// + // Member variables + // + //////////////////////////////////////////////////////////////////////// + + // Child controls + internal UpDownEdit upDownEdit; // See nested class at end of this file + internal UpDownButtons upDownButtons; // See nested class at end of this file + + // Intercept arrow keys? + private bool interceptArrowKeys = DefaultInterceptArrowKeys; + + // If true, the updown buttons will be drawn on the left-hand side of the control. + private LeftRightAlignment upDownAlign = DefaultUpDownAlign; + + // userEdit is true when the text of the control has been changed, + // and the internal value of the control has not yet been updated. + // We do not always want to keep the internal value up-to-date, + // hence this variable. + private bool userEdit = false; + + /// + /// + /// The current border for this edit control. + /// + private BorderStyle borderStyle = DefaultBorderStyle; + + // Mouse wheel movement + private int wheelDelta = 0; + + // Indicates if the edit text is being changed + private bool changingText = false; + + // Indicates whether we have doubleClicked + private bool doubleClickFired = false; + + internal int defaultButtonsWidth = DefaultButtonsWidth; + + /// + /// + /// + /// Initializes a new instance of the + /// class. + /// + /// + public UpDownBase() { + if (DpiHelper.IsScalingRequired) { + defaultButtonsWidth = LogicalToDeviceUnits(DefaultButtonsWidth); + } + + upDownButtons = new UpDownButtons(this); + upDownEdit = new UpDownEdit(this); + upDownEdit.BorderStyle = BorderStyle.None; + upDownEdit.AutoSize = false; + upDownEdit.KeyDown += new KeyEventHandler(this.OnTextBoxKeyDown); + upDownEdit.KeyPress += new KeyPressEventHandler(this.OnTextBoxKeyPress); + upDownEdit.TextChanged += new EventHandler(this.OnTextBoxTextChanged); + upDownEdit.LostFocus += new EventHandler(this.OnTextBoxLostFocus); + upDownEdit.Resize += new EventHandler(this.OnTextBoxResize); + upDownButtons.TabStop = false; + upDownButtons.Size = new Size(defaultButtonsWidth, PreferredHeight); + upDownButtons.UpDown += new UpDownEventHandler(this.OnUpDown); + + Controls.AddRange(new Control[] { upDownButtons, upDownEdit} ); + + SetStyle(ControlStyles.Opaque | ControlStyles.FixedHeight | ControlStyles.ResizeRedraw, true); + SetStyle(ControlStyles.StandardClick, false); + SetStyle(ControlStyles.UseTextForAccessibility, false); + } + + //////////////////////////////////////////////////////////////////////// + // Properties + // + //////////////////////////////////////////////////////////////////////// + + // AutoScroll is not relevant to an UpDownBase + /// + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AutoScroll { + get { + return false; + } + set { + // Don't allow AutoScroll to be set to anything + } + } + + // AutoScrollMargin is not relevant to an UpDownBase + /// + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public Size AutoScrollMargin { + get { + return base.AutoScrollMargin; + } + set { + base.AutoScrollMargin = value; + } + } + + // AutoScrollMinSize is not relevant to an UpDownBase + /// + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public Size AutoScrollMinSize { + get { + return base.AutoScrollMinSize; + } + set { + base.AutoScrollMinSize = value; + } + } + + /// + /// + /// Override to re-expose AutoSize. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)] + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + new public event EventHandler AutoSizeChanged + { + add + { + base.AutoSizeChanged += value; + } + remove + { + base.AutoSizeChanged -= value; + } + } + + /// + /// + /// + /// + /// Gets or sets the background color for the + /// text box portion of the up-down control. + /// + /// + public override Color BackColor { + get { + return upDownEdit.BackColor; + } + set { + base.BackColor = value; // Don't remove this or you will break serialization. See VSWhidbey #517574 + upDownEdit.BackColor = value; + Invalidate(); // VSWhidbey #335074 + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + base.BackgroundImageChanged += value; + } + remove { + base.BackgroundImageChanged -= value; + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + base.BackgroundImageLayoutChanged += value; + } + remove { + base.BackgroundImageLayoutChanged -= value; + } + } + + /// + /// + /// + /// Gets or sets the border style for + /// the up-down control. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.Fixed3D), + DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), + SRDescription(SR.UpDownBaseBorderStyleDescr) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + if (borderStyle != value) { + borderStyle = value; + RecreateHandle(); + } + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether the text + /// property is being changed internally by its parent class. + /// + /// + protected bool ChangingText { + get { + return changingText; + } + + set { + changingText = value; + } + } + + /// + /// + /// + /// + public override ContextMenu ContextMenu { + get { + return base.ContextMenu; + } + set { + base.ContextMenu = value; + this.upDownEdit.ContextMenu = value; + } + } + + public override ContextMenuStrip ContextMenuStrip { + get { + return base.ContextMenuStrip; + } + set { + base.ContextMenuStrip = value; + this.upDownEdit.ContextMenuStrip = value; + } + } + + + /// + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + + cp.Style &= (~NativeMethods.WS_BORDER); + if (!Application.RenderWithVisualStyles) { + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + } + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + return new Size(DefaultControlWidth, PreferredHeight); + } + } + + // DockPadding is not relevant to UpDownBase + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public DockPaddingEdges DockPadding { + get { + return base.DockPadding; + } + } + + /// + /// + /// Returns true if this control has focus. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.ControlFocusedDescr) + ] + public override bool Focused { + get { + return upDownEdit.Focused; + } + } + + /// + /// + /// + /// + /// Indicates the foreground color for the control. + /// + /// + public override Color ForeColor { + get { + return upDownEdit.ForeColor; + } + set { + base.ForeColor = value; + upDownEdit.ForeColor = value; + } + } + + /// + /// + /// + /// Gets or sets a value indicating whether + /// the user can use the UP + /// ARROW and DOWN ARROW keys to select values. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(true), + SRDescription(SR.UpDownBaseInterceptArrowKeysDescr) + ] + public bool InterceptArrowKeys { + + get { + return interceptArrowKeys; + } + + set { + interceptArrowKeys = value; + } + } + + /// + public override Size MaximumSize { + get { return base.MaximumSize; } + set { + base.MaximumSize = new Size(value.Width, 0); + } + } + + /// + public override Size MinimumSize { + get { return base.MinimumSize; } + set { + base.MinimumSize = new Size(value.Width, 0); + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MouseEnter { + add { + base.MouseEnter += value; + } + remove { + base.MouseEnter -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MouseLeave { + add { + base.MouseLeave += value; + } + remove { + base.MouseLeave -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler MouseHover { + add { + base.MouseHover += value; + } + remove { + base.MouseHover -= value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event MouseEventHandler MouseMove { + add { + base.MouseMove += value; + } + remove { + base.MouseMove -= value; + } + } + + /// + /// + /// + /// Gets the height of + /// the up-down control. + /// + /// + [ + SRCategory(SR.CatLayout), + Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(SR.UpDownBasePreferredHeightDescr) + ] + public int PreferredHeight { + get { + + int height = FontHeight; + + // Adjust for the border style + if (borderStyle != BorderStyle.None) { + height += SystemInformation.BorderSize.Height * 4 + 3; + } + else { + height += 3; + } + + return height; + } + } + + /// + /// + /// + /// Gets or sets + /// a + /// value + /// indicating whether the text may only be changed by the + /// use + /// of the up or down buttons. + /// + /// + [ + SRCategory(SR.CatBehavior), + DefaultValue(false), + SRDescription(SR.UpDownBaseReadOnlyDescr) + ] + public bool ReadOnly { + + get { + return upDownEdit.ReadOnly; + } + + set { + upDownEdit.ReadOnly = value; + } + } + + /// + /// + /// + /// Gets or sets the text + /// displayed in the up-down control. + /// + /// + [ + Localizable(true) + ] + public override string Text { + get { + return upDownEdit.Text; + } + + set { + upDownEdit.Text = value; + // The text changed event will at this point be triggered. + // After returning, the value of UserEdit will reflect + // whether or not the current upDownEditbox text is in sync + // with any internally stored values. If UserEdit is true, + // we must validate the text the user typed or set. + + ChangingText = false; + // Details: Usually, the code in the Text changed event handler + // sets ChangingText back to false. + // If the text hasn't actually changed though, the event handler + // never fires. ChangingText should always be false on exit from + // this property. + + if (UserEdit) { + ValidateEditText(); + } + } + } + + /// + /// + /// + /// Gets or + /// sets the alignment of the text in the up-down + /// control. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(HorizontalAlignment.Left), + SRDescription(SR.UpDownBaseTextAlignDescr) + ] + public HorizontalAlignment TextAlign { + get { + return upDownEdit.TextAlign; + } + set { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); + } + upDownEdit.TextAlign = value; + } + } + + internal TextBox TextBox { + get { + return upDownEdit; + } + } + + /// + /// + /// + /// Gets + /// or sets the + /// alignment + /// of the up and down buttons on the up-down control. + /// + /// + [ + Localizable(true), + SRCategory(SR.CatAppearance), + DefaultValue(LeftRightAlignment.Right), + SRDescription(SR.UpDownBaseAlignmentDescr) + ] + public LeftRightAlignment UpDownAlign { + + get { + return upDownAlign; + } + + set { + //valid values are 0x0 to 0x1 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)LeftRightAlignment.Left, (int)LeftRightAlignment.Right)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(LeftRightAlignment)); + } + + if (upDownAlign != value) { + + upDownAlign = value; + PositionControls(); + Invalidate(); + } + } + } + + internal UpDownButtons UpDownButtonsInternal { + get { + return upDownButtons; + } + } + + /// + /// + /// + /// Gets + /// or sets a value indicating whether a value has been entered by the + /// user. + /// + /// + protected bool UserEdit { + get { + return userEdit; + } + + set { + userEdit = value; + } + } + + + //////////////////////////////////////////////////////////////////////// + // Methods + // + //////////////////////////////////////////////////////////////////////// + + + /// + /// + /// + /// When overridden in a derived class, handles the pressing of the down button + /// on the up-down control. + /// + /// + public abstract void DownButton(); + + // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose + // constraints on their size. + internal override Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight) { + return base.ApplyBoundsConstraints(suggestedX,suggestedY, proposedWidth, PreferredHeight); + } + + /// + /// Gets an accessible name. + /// + /// The base name. + /// The accessible name. + internal string GetAccessibleName(string baseName) { + if (baseName == null) { + if (AccessibilityImprovements.Level3) { + return SR.GetString(SR.SpinnerAccessibleName); + } + else if (AccessibilityImprovements.Level1) { + return this.GetType().Name; + } + } + + return baseName; + } + + /// + /// + /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting. + /// For UpDown controls, scale the width of the up/down buttons. + /// Must call the base class method to get the current DPI values. This method is invoked only when + /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has + /// EnableDpiChangedMessageHandling config switch turned on. + /// + protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) { + base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew); + defaultButtonsWidth = LogicalToDeviceUnits(DefaultButtonsWidth); + upDownButtons.Width = defaultButtonsWidth; + } + + /// + /// + /// + /// When overridden in a derived class, raises the Changed event. + /// event. + /// + protected virtual void OnChanged(object source, EventArgs e) { + } + + /// + /// + /// + /// + /// Initialize the updown. Adds the upDownEdit and updown buttons. + /// + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + PositionControls(); + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + } + + /// + /// + /// + /// + /// Tear down the updown. + /// + /// + protected override void OnHandleDestroyed(EventArgs e) + { + SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.UserPreferenceChanged); + base.OnHandleDestroyed(e); + } + + /// + /// + /// Handles painting the buttons on the control. + /// + /// + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + + Rectangle editBounds = upDownEdit.Bounds; + if (Application.RenderWithVisualStyles) { + if (borderStyle != BorderStyle.None) { + Rectangle bounds = ClientRectangle; + Rectangle clipBounds = e.ClipRectangle; + + //Draw a themed textbox-like border, which is what the spin control does + VisualStyleRenderer vsr = new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Normal); + int border = ThemedBorderWidth; + Rectangle clipLeft = new Rectangle(bounds.Left, bounds.Top, border, bounds.Height); + Rectangle clipTop = new Rectangle(bounds.Left, bounds.Top, bounds.Width, border); + Rectangle clipRight = new Rectangle(bounds.Right - border, bounds.Top, border, bounds.Height); + Rectangle clipBottom = new Rectangle(bounds.Left, bounds.Bottom - border, bounds.Width, border); + clipLeft.Intersect(clipBounds); + clipTop.Intersect(clipBounds); + clipRight.Intersect(clipBounds); + clipBottom.Intersect(clipBounds); + vsr.DrawBackground(e.Graphics, bounds, clipLeft, HandleInternal); + vsr.DrawBackground(e.Graphics, bounds, clipTop, HandleInternal); + vsr.DrawBackground(e.Graphics, bounds, clipRight, HandleInternal); + vsr.DrawBackground(e.Graphics, bounds, clipBottom, HandleInternal); + // Draw rectangle around edit control with background color + using (Pen pen = new Pen(BackColor)) { + Rectangle backRect = editBounds; + backRect.X--; + backRect.Y--; + backRect.Width++; + backRect.Height++; + e.Graphics.DrawRectangle(pen, backRect); + } + } + } + else { + // Draw rectangle around edit control with background color + using (Pen pen = new Pen(BackColor, Enabled ? 2 : 1)) + { + Rectangle backRect = editBounds; + backRect.Inflate(1, 1); + if (!Enabled) + { + backRect.X--; + backRect.Y--; + backRect.Width++; + backRect.Height++; + } + e.Graphics.DrawRectangle(pen, backRect); + } + } + if (!Enabled && BorderStyle != BorderStyle.None && !upDownEdit.ShouldSerializeBackColor()) { + //draws a grayed rectangled around the upDownEdit, since otherwise we will have a white + //border around the upDownEdit, which is inconsistent with Windows' behavior + //we only want to do this when BackColor is not serialized, since otherwise + //we should display the backcolor instead of the usual grayed textbox. + editBounds.Inflate(1, 1); + ControlPaint.DrawBorder(e.Graphics, editBounds, SystemColors.Control, ButtonBorderStyle.Solid); + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnTextBoxKeyDown(object source, KeyEventArgs e) { + this.OnKeyDown(e); + if (interceptArrowKeys) { + + // Intercept up arrow + if (e.KeyData == Keys.Up) { + UpButton(); + e.Handled = true; + } + + // Intercept down arrow + else if (e.KeyData == Keys.Down) { + DownButton(); + e.Handled = true; + } + } + + // Perform text validation if ENTER is pressed + // + if (e.KeyCode == Keys.Return && UserEdit) { + ValidateEditText(); + } + } + + /// + /// + /// Raises the + /// event. + /// + protected virtual void OnTextBoxKeyPress(object source, KeyPressEventArgs e) { + this.OnKeyPress(e); + + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnTextBoxLostFocus(object source, EventArgs e) { + if (UserEdit) { + ValidateEditText(); + } + } + + /// + /// + /// Raises the event. + /// + protected virtual void OnTextBoxResize(object source, EventArgs e) { + this.Height = PreferredHeight; + PositionControls(); + } + + /// + /// + /// Raises the TextBoxTextChanged event. + /// event. + /// + protected virtual void OnTextBoxTextChanged(object source, EventArgs e) { + if (changingText) { + Debug.Assert(UserEdit == false, "OnTextBoxTextChanged() - UserEdit == true"); + ChangingText = false; + } + else { + UserEdit = true; + } + + this.OnTextChanged(e); + OnChanged(source, new EventArgs()); + } + + /// + /// Called from the UpDownButtons member. Provided for derived controls to have a finer way to handle the event. + /// + internal virtual void OnStartTimer() { + } + + internal virtual void OnStopTimer() { + } + + /// + /// Raises the event. + /// + protected override void OnMouseDown(MouseEventArgs e) { + if (e.Clicks == 2 && e.Button == MouseButtons.Left) { + doubleClickFired = true; + } + + base.OnMouseDown(e); + } + + /// + /// + /// + /// Raises the event. + /// + /// + /// + protected override void OnMouseUp(MouseEventArgs mevent) { + if (mevent.Button == MouseButtons.Left) { + Point pt = PointToScreen(new Point(mevent.X, mevent.Y)); + if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle && !ValidationCancelled) { + if (!doubleClickFired) { + OnClick(mevent); + OnMouseClick(mevent); + } + else { + doubleClickFired = false; + OnDoubleClick(mevent); + OnMouseDoubleClick(mevent); + } + } + doubleClickFired = false; + } + base.OnMouseUp(mevent); + } + + /// + /// + /// Raises the event. + /// + protected override void OnMouseWheel(MouseEventArgs e) { + base.OnMouseWheel(e); + HandledMouseEventArgs hme = e as HandledMouseEventArgs; + if (hme != null) { + if (hme.Handled) { + return; + } + hme.Handled = true; + } + + if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None) { + return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down. + } + + int wheelScrollLines = SystemInformation.MouseWheelScrollLines; + if (wheelScrollLines == 0) { + return; // Do not scroll when the user system setting is 0 lines per notch + } + + Debug.Assert(this.wheelDelta > -NativeMethods.WHEEL_DELTA, "wheelDelta is too smal"); + Debug.Assert(this.wheelDelta < NativeMethods.WHEEL_DELTA, "wheelDelta is too big"); + this.wheelDelta += e.Delta; + + float partialNotches; + partialNotches = (float)this.wheelDelta / (float)NativeMethods.WHEEL_DELTA; + + if (wheelScrollLines == -1) { + wheelScrollLines = DefaultWheelScrollLinesPerPage; + } + + // Evaluate number of bands to scroll + int scrollBands = (int)((float)wheelScrollLines * partialNotches); + if (scrollBands != 0) { + int absScrollBands; + if (scrollBands > 0) { + absScrollBands = scrollBands; + while (absScrollBands > 0) { + UpButton(); + absScrollBands--; + } + this.wheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + else { + absScrollBands = -scrollBands; + while (absScrollBands > 0) { + DownButton(); + absScrollBands--; + } + this.wheelDelta -= (int)((float)scrollBands * ((float)NativeMethods.WHEEL_DELTA / (float)wheelScrollLines)); + } + } + } + + + /// + /// + /// + /// Handle the layout event. The size of the upDownEdit control, and the + /// position of the UpDown control must be modified. + /// + protected override void OnLayout(LayoutEventArgs e) { + + PositionControls(); + base.OnLayout(e); + } + + /// + /// + /// + /// + /// Raises the FontChanged event. + /// + /// + protected override void OnFontChanged(EventArgs e) { + // Clear the font height cache + FontHeight = -1; + + Height = PreferredHeight; + PositionControls(); + + base.OnFontChanged(e); + } + + /// + /// + /// + /// Handles UpDown events, which are generated by clicking on + /// the updown buttons in the child updown control. + /// + /// + private void OnUpDown(object source, UpDownEventArgs e) { + // Modify the value + if (e.ButtonID == (int)ButtonID.Up) + UpButton(); + else if (e.ButtonID == (int)ButtonID.Down) + DownButton(); + } + + /// + /// + /// Calculates the size and position of the upDownEdit control and + /// the updown buttons. + /// + private void PositionControls() { + Rectangle upDownEditBounds = Rectangle.Empty, + upDownButtonsBounds = Rectangle.Empty; + + Rectangle clientArea = new Rectangle(Point.Empty, ClientSize); + int totalClientWidth = clientArea.Width; + bool themed = Application.RenderWithVisualStyles; + BorderStyle borderStyle = BorderStyle; + + + // determine how much to squish in - Fixed3d and FixedSingle have 2PX border + int borderWidth = (borderStyle == BorderStyle.None) ? 0 : 2; + clientArea.Inflate(-borderWidth, -borderWidth); + + // Reposition and resize the upDownEdit control + // + if (upDownEdit != null) { + upDownEditBounds = clientArea; + upDownEditBounds.Size = new Size(clientArea.Width - defaultButtonsWidth, clientArea.Height); + } + + // Reposition and resize the updown buttons + // + if (upDownButtons != null) { + int borderFixup = (themed) ? 1: 2; + if (borderStyle == BorderStyle.None) { + borderFixup = 0; + } + upDownButtonsBounds = new Rectangle(/*x*/clientArea.Right - defaultButtonsWidth+borderFixup, + /*y*/clientArea.Top-borderFixup, + /*w*/defaultButtonsWidth, + /*h*/clientArea.Height+(borderFixup*2)); + } + + // Right to left translation + LeftRightAlignment updownAlign = UpDownAlign; + updownAlign = RtlTranslateLeftRight(updownAlign); + + // left/right updown align translation + if (updownAlign == LeftRightAlignment.Left) { + // if the buttons are aligned to the left, swap position of text box/buttons + upDownButtonsBounds.X = totalClientWidth - upDownButtonsBounds.Right; + upDownEditBounds.X = totalClientWidth - upDownEditBounds.Right; + } + + // apply locations + if (upDownEdit != null) { + upDownEdit.Bounds = upDownEditBounds; + } + if (upDownButtons != null) { + upDownButtons.Bounds = upDownButtonsBounds; + upDownButtons.Invalidate(); + } + + } + + /// + /// + /// + /// Selects a range of + /// text in the up-down control. + /// + /// + public void Select(int start, int length) { + upDownEdit.Select(start, length); + } + + + /// + /// Child controls run their + /// + private MouseEventArgs TranslateMouseEvent(Control child, MouseEventArgs e) { + if (child != null && IsHandleCreated) { + // same control as PointToClient or PointToScreen, just + // with two specific controls in mind. + NativeMethods.POINT point = new NativeMethods.POINT(e.X, e.Y); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(child, child.Handle), new HandleRef(this, Handle), point, 1); + return new MouseEventArgs(e.Button, e.Clicks, point.x, point.y , e.Delta); + } + return e; + } + + /// + /// + /// + /// When overridden in a derived class, handles the pressing of the up button on the up-down control. + /// + /// + public abstract void UpButton(); + + /// + /// + /// + /// When overridden + /// in a derived class, updates the text displayed in the up-down control. + /// + /// + protected abstract void UpdateEditText(); + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs pref) { + if (pref.Category == UserPreferenceCategory.Locale) { + UpdateEditText(); + } + } + + /// + /// + /// + /// When overridden in a + /// derived class, validates the text displayed in the up-down control. + /// + /// + protected virtual void ValidateEditText() { + } + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_SETFOCUS: + if (!HostedInWin32DialogManager) { + if (ActiveControl == null) { + SetActiveControlInternal(TextBox); + } + else { + FocusActiveControlInternal(); + } + } + else { + if (TextBox.CanFocus){ + UnsafeNativeMethods.SetFocus(new HandleRef(TextBox, TextBox.Handle)); + } + base.WndProc(ref m); + } + break; + case NativeMethods.WM_KILLFOCUS: + DefWndProc(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + /// + /// + /// + /// This Function sets the ToolTip for this composite control. + /// + internal void SetToolTip(ToolTip toolTip, string caption) { + toolTip.SetToolTip(this.upDownEdit , caption); + toolTip.SetToolTip(this.upDownButtons , caption); + } + + internal class UpDownEdit : TextBox{ + ///////////////////////////////////////////////////////////////////// + // Member variables + // + ///////////////////////////////////////////////////////////////////// + + // Parent control + private UpDownBase parent; + private bool doubleClickFired = false; + ///////////////////////////////////////////////////////////////////// + // Constructors + // + ///////////////////////////////////////////////////////////////////// + + internal UpDownEdit(UpDownBase parent) + : base() { + + SetStyle(ControlStyles.FixedHeight | + ControlStyles.FixedWidth, true); + + SetStyle(ControlStyles.Selectable, false); + + this.parent = parent; + } + + public override string Text { + get { + return base.Text; + } + set { + bool valueChanged = (value != base.Text); + base.Text = value; + if (valueChanged && AccessibilityImprovements.Level1) { + AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); + } + } + } + + protected override AccessibleObject CreateAccessibilityInstance() { + return new UpDownEditAccessibleObject(this, parent); + } + + protected override void OnMouseDown(MouseEventArgs e) { + if (e.Clicks == 2 && e.Button == MouseButtons.Left) { + doubleClickFired = true; + } + parent.OnMouseDown(parent.TranslateMouseEvent(this, e)); + } + + /// + /// + /// + /// Handles detecting when the mouse button is released. + /// + /// + protected override void OnMouseUp(MouseEventArgs e) { + + Point pt = new Point(e.X,e.Y); + pt = PointToScreen(pt); + + MouseEventArgs me = parent.TranslateMouseEvent(this, e); + if (e.Button == MouseButtons.Left) { + if (!parent.ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + if (!doubleClickFired) { + parent.OnClick(me); + parent.OnMouseClick(me); + } + else { + doubleClickFired = false; + parent.OnDoubleClick(me); + parent.OnMouseDoubleClick(me); + } + } + doubleClickFired = false; + } + + parent.OnMouseUp(me); + } + + internal override void WmContextMenu(ref Message m) { + // VSWhidbey 521337: want to make the SourceControl to be the UpDownBase, not the Edit. + if (ContextMenu == null && ContextMenuStrip != null) { + WmContextMenu(ref m, parent); + } + else { + WmContextMenu(ref m, this); + } + } + + + /// + /// + /// Raises the + /// event. + /// + protected override void OnKeyUp(KeyEventArgs e) { + parent.OnKeyUp(e); + } + + protected override void OnGotFocus(EventArgs e) { + parent.SetActiveControlInternal(this); + parent.InvokeGotFocus(parent, e); + } + + protected override void OnLostFocus(EventArgs e) { + parent.InvokeLostFocus(parent, e); + } + + // Microsoft: Focus fixes. The XXXUpDown control will + // also fire a Leave event. We don't want + // to fire two of them. + // protected override void OnLeave(EventArgs e) { + // parent.OnLeave(e); + // } + + // Create our own accessibility object to map the accessible name + // back to our parent. They should track. + internal class UpDownEditAccessibleObject : ControlAccessibleObject { + UpDownBase parent; + + public UpDownEditAccessibleObject(UpDownEdit owner, UpDownBase parent) : base(owner) { + this.parent = parent; + } + + public override string Name { + get { + return parent.AccessibilityObject.Name; + } + set { + parent.AccessibilityObject.Name = value; + } + } + + public override string KeyboardShortcut { + get { + return parent.AccessibilityObject.KeyboardShortcut; + } + } + } + } + + /// + /// + /// + /// Nested class UpDownButtons + /// + /// A control representing the pair of buttons on the end of the upDownEdit control. + /// This class handles drawing the updown buttons, and detecting mouse actions + /// on these buttons. Acceleration on the buttons is handled. The control + /// sends UpDownEventArgss to the parent UpDownBase class when a button is pressed, + /// or when the acceleration determines that another event should be generated. + /// + internal class UpDownButtons : Control { + // + + ///////////////////////////////////////////////////////////////////// + // Member variables + // + ///////////////////////////////////////////////////////////////////// + + // Parent control + private UpDownBase parent; + + // Button state + private ButtonID pushed = ButtonID.None; + private ButtonID captured = ButtonID.None; + private ButtonID mouseOver = ButtonID.None; + + // UpDown event handler + private UpDownEventHandler upDownEventHandler; + + // Timer + private Timer timer; // generates UpDown events + private int timerInterval; // milliseconds between events + + private bool doubleClickFired = false; + + ///////////////////////////////////////////////////////////////////// + // Constructors + // + ///////////////////////////////////////////////////////////////////// + + internal UpDownButtons(UpDownBase parent) + + : base() { + + SetStyle(ControlStyles.Opaque | ControlStyles.FixedHeight | + ControlStyles.FixedWidth, true); + + SetStyle(ControlStyles.Selectable, false); + + this.parent = parent; + } + + + ///////////////////////////////////////////////////////////////////// + // Methods + // + ///////////////////////////////////////////////////////////////////// + + /// + /// + /// + /// Adds a handler for the updown button event. + /// + public event UpDownEventHandler UpDown { + add { + upDownEventHandler += value; + } + remove { + upDownEventHandler -= value; + } + } + + // Called when the mouse button is pressed - we need to start + // spinning the value of the updown. + // + private void BeginButtonPress(MouseEventArgs e) { + + int half_height = Size.Height / 2; + + if (e.Y < half_height) { + + // Up button + // + pushed = captured = ButtonID.Up; + Invalidate(); + + } + else { + + // Down button + // + pushed = captured = ButtonID.Down; + Invalidate(); + } + + // Capture the mouse + // + CaptureInternal = true; + + // Generate UpDown event + // + OnUpDown(new UpDownEventArgs((int)pushed)); + + // Start the timer for new updown events + // + StartTimer(); + } + + protected override AccessibleObject CreateAccessibilityInstance() { + return new UpDownButtonsAccessibleObject(this); + } + + // Called when the mouse button is released - we need to stop + // spinning the value of the updown. + // + private void EndButtonPress() { + + pushed = ButtonID.None; + captured = ButtonID.None; + + // Stop the timer + StopTimer(); + + // Release the mouse + CaptureInternal = false; + + // Redraw the buttons + Invalidate(); + } + + /// + /// + /// + /// Handles detecting mouse hits on the buttons. This method + /// detects which button was hit (up or down), fires a + /// updown event, captures the mouse, and starts a timer + /// for repeated updown events. + /// + /// + protected override void OnMouseDown(MouseEventArgs e) { + // Begin spinning the value + // + + // Focus the parent + // + this.parent.FocusInternal(); + + if (!parent.ValidationCancelled && e.Button == MouseButtons.Left) { + BeginButtonPress(e); + } + if (e.Clicks == 2 && e.Button == MouseButtons.Left) { + doubleClickFired = true; + } + // At no stage should a button be pushed, and the mouse + // not captured. + // + Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None), + "Invalid button pushed/captured combination"); + + parent.OnMouseDown(parent.TranslateMouseEvent(this, e)); + } + + /// + /// + /// + /// Handles detecting mouse movement. + /// + /// + protected override void OnMouseMove(MouseEventArgs e) { + + // If the mouse is captured by the buttons (i.e. an updown button + // was pushed, and the mouse button has not yet been released), + // determine the new state of the buttons depending on where + // the mouse pointer has moved. + + if (Capture) { + + // Determine button area + + Rectangle rect = ClientRectangle; + rect.Height /= 2; + + if (captured == ButtonID.Down) { + rect.Y += rect.Height; + } + + // Test if the mouse has moved outside the button area + + if (rect.Contains(e.X, e.Y)) { + + // Inside button + // Repush the button if necessary + + if (pushed != captured) { + + // Restart the timer + StartTimer(); + + pushed = captured; + Invalidate(); + } + + } + else { + + // Outside button + // Retain the capture, but pop the button up whilst + // the mouse remains outside the button and the + // mouse button remains pressed. + + if (pushed != ButtonID.None) { + + // Stop the timer for updown events + StopTimer(); + + pushed = ButtonID.None; + Invalidate(); + } + } + } + + //Logic for seeing which button is Hot if any + Rectangle rectUp = ClientRectangle, rectDown = ClientRectangle; + rectUp.Height /= 2; + rectDown.Y += rectDown.Height / 2; + + //Check if the mouse is on the upper or lower button. Note that it could be in neither. + if (rectUp.Contains(e.X, e.Y)) { + mouseOver = ButtonID.Up; + Invalidate(); + } + else if (rectDown.Contains(e.X, e.Y)) { + mouseOver = ButtonID.Down; + Invalidate(); + } + + // At no stage should a button be pushed, and the mouse + // not captured. + Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None), + "Invalid button pushed/captured combination"); + + parent.OnMouseMove(parent.TranslateMouseEvent(this, e)); + } + + /// + /// + /// + /// Handles detecting when the mouse button is released. + /// + /// + protected override void OnMouseUp(MouseEventArgs e) { + + if (!parent.ValidationCancelled && e.Button == MouseButtons.Left) { + EndButtonPress(); + } + + // At no stage should a button be pushed, and the mouse + // not captured. + Debug.Assert(!(pushed != ButtonID.None && captured == ButtonID.None), + "Invalid button pushed/captured combination"); + + Point pt = new Point(e.X,e.Y); + pt = PointToScreen(pt); + + MouseEventArgs me = parent.TranslateMouseEvent(this, e); + if (e.Button == MouseButtons.Left) { + if (!parent.ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { + if (!doubleClickFired) { + this.parent.OnClick(me); + } + else { + doubleClickFired = false; + this.parent.OnDoubleClick(me); + this.parent.OnMouseDoubleClick(me); + } + } + doubleClickFired = false; + } + + parent.OnMouseUp(me); + } + + /// + /// + /// + /// Handles detecting when the mouse leaves. + /// + /// + protected override void OnMouseLeave(EventArgs e) { + mouseOver = ButtonID.None; + Invalidate(); + + parent.OnMouseLeave(e); + } + + /// + /// + /// Handles painting the buttons on the control. + /// + /// + protected override void OnPaint(PaintEventArgs e) { + int half_height = ClientSize.Height / 2; + + /* Draw the up and down buttons */ + + if (Application.RenderWithVisualStyles) { + VisualStyleRenderer vsr = new VisualStyleRenderer(mouseOver == ButtonID.Up ? VisualStyleElement.Spin.Up.Hot : VisualStyleElement.Spin.Up.Normal); + + if (!Enabled) { + vsr.SetParameters(VisualStyleElement.Spin.Up.Disabled); + } + else if (pushed == ButtonID.Up) { + vsr.SetParameters(VisualStyleElement.Spin.Up.Pressed); + } + + vsr.DrawBackground(e.Graphics, new Rectangle(0, 0, parent.defaultButtonsWidth, half_height), HandleInternal); + + if (!Enabled) { + vsr.SetParameters(VisualStyleElement.Spin.Down.Disabled); + } + else if (pushed == ButtonID.Down) { + vsr.SetParameters(VisualStyleElement.Spin.Down.Pressed); + } + else { + vsr.SetParameters(mouseOver == ButtonID.Down ? VisualStyleElement.Spin.Down.Hot : VisualStyleElement.Spin.Down.Normal); + } + + vsr.DrawBackground(e.Graphics, new Rectangle(0, half_height, parent.defaultButtonsWidth, half_height), HandleInternal); + } + else { + ControlPaint.DrawScrollButton(e.Graphics, + new Rectangle(0, 0, parent.defaultButtonsWidth, half_height), + ScrollButton.Up, + pushed == ButtonID.Up ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive)); + + ControlPaint.DrawScrollButton(e.Graphics, + new Rectangle(0, half_height, parent.defaultButtonsWidth, half_height), + ScrollButton.Down, + pushed == ButtonID.Down ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive)); + } + + if (half_height != (ClientSize.Height + 1) / 2) { + // When control has odd height, a line needs to be drawn below the buttons with the backcolor. + using (Pen pen = new Pen(this.parent.BackColor)) { + Rectangle clientRect = ClientRectangle; + e.Graphics.DrawLine(pen, clientRect.Left, clientRect.Bottom - 1, clientRect.Right - 1, clientRect.Bottom - 1); + } + } + + base.OnPaint(e); // raise paint event, just in case this inner class goes public some day + } + + /// + /// + /// Occurs when the UpDown buttons are pressed and when the acceleration timer tick event is raised. + /// + protected virtual void OnUpDown(UpDownEventArgs upevent) { + if (upDownEventHandler != null) + upDownEventHandler(this, upevent); + } + + /// + /// + /// Starts the timer for generating updown events + /// + protected void StartTimer() { + parent.OnStartTimer(); + if (timer == null) { + timer = new Timer(); // generates UpDown events + // Add the timer handler + timer.Tick += new EventHandler(TimerHandler); + } + + this.timerInterval = DefaultTimerInterval; + + timer.Interval = this.timerInterval; + timer.Start(); + } + + /// + /// + /// Stops the timer for generating updown events + /// + protected void StopTimer() { + if (timer != null) { + timer.Stop(); + timer.Dispose(); + timer = null; + } + parent.OnStopTimer(); + } + + /// + /// + /// Generates updown events when the timer calls this function. + /// + private void TimerHandler(object source, EventArgs args) { + + // Make sure we've got mouse capture + if (!Capture) { + EndButtonPress(); + return; + } + + // onUpDown method calls customer's ValueCHanged event handler which might enter the message loop and + // process the mouse button up event, which results in timer being disposed + OnUpDown(new UpDownEventArgs((int)pushed)); + + if (timer != null) { + // Accelerate timer. + this.timerInterval *= 7; + this.timerInterval /= 10; + + if (this.timerInterval < 1) { + this.timerInterval = 1; + } + + timer.Interval = this.timerInterval; + } + } + + internal class UpDownButtonsAccessibleObject : ControlAccessibleObject { + + private DirectionButtonAccessibleObject upButton; + private DirectionButtonAccessibleObject downButton; + + public UpDownButtonsAccessibleObject(UpDownButtons owner) : base(owner) { + } + + public override string Name { + get { + string baseName = base.Name; + if (baseName == null || baseName.Length == 0) { + if (AccessibilityImprovements.Level3) { + // For AI.Level3 spinner is already announced so use type name. + return Owner.ParentInternal.GetType().Name; + } + return SR.GetString(SR.SpinnerAccessibleName); + } + return baseName; + } + set { + base.Name = value; + } + } + + /// + /// + /// [To be supplied.] + /// + public override AccessibleRole Role { + get { + AccessibleRole role = Owner.AccessibleRole; + if (role != AccessibleRole.Default) { + return role; + } + return AccessibleRole.SpinButton; + } + } + + private DirectionButtonAccessibleObject UpButton { + get { + if (upButton == null) { + upButton = new DirectionButtonAccessibleObject(this, true); + } + return upButton; + } + } + + private DirectionButtonAccessibleObject DownButton { + get { + if (downButton == null) { + downButton = new DirectionButtonAccessibleObject(this, false); + } + return downButton; + } + } + + + + /// + /// + /// + public override AccessibleObject GetChild(int index) { + + // Up button + // + if (index == 0) { + return UpButton; + } + + // Down button + // + if (index == 1) { + return DownButton; + } + + return null; + } + + /// + /// + /// + public override int GetChildCount() { + return 2; + } + + internal class DirectionButtonAccessibleObject : AccessibleObject { + private bool up; + private UpDownButtonsAccessibleObject parent; + + public DirectionButtonAccessibleObject(UpDownButtonsAccessibleObject parent, bool up) { + this.parent = parent; + this.up = up; + } + + public override Rectangle Bounds { + get { + // Get button bounds + // + Rectangle bounds = ((UpDownButtons)parent.Owner).Bounds; + bounds.Height /= 2; + if (!up) { + bounds.Y += bounds.Height; + } + + // Convert to screen co-ords + // + return (((UpDownButtons)parent.Owner).ParentInternal).RectangleToScreen(bounds); + } + } + + public override string Name { + get { + if (up) { + return SR.GetString(SR.UpDownBaseUpButtonAccName); + } + return SR.GetString(SR.UpDownBaseDownButtonAccName); + } + set { + } + } + + public override AccessibleObject Parent { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get { + return parent; + } + } + + public override AccessibleRole Role { + get { + return AccessibleRole.PushButton; + } + } + } + } + + } // end class UpDownButtons + + // Button identifiers + + internal enum ButtonID { + None = 0, + Up = 1, + Down = 2, + } + } // end class UpDownBase +} + diff --git a/WindowsForms/Managed/System/WinForms/UpDownEvent.cs b/WindowsForms/Managed/System/WinForms/UpDownEvent.cs new file mode 100644 index 000000000..f8e471af5 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UpDownEvent.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// + /// Provides data for the UpDownEvent + /// + /// + public class UpDownEventArgs : EventArgs { + + int buttonID; + + /// + /// + /// [To be supplied.] + /// + public UpDownEventArgs(int buttonPushed) { + buttonID = buttonPushed; + } + + /// + /// + /// [To be supplied.] + /// + public int ButtonID { + get { + return buttonID; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/UpDownEventHandler.cs b/WindowsForms/Managed/System/WinForms/UpDownEventHandler.cs new file mode 100644 index 000000000..7fd20046a --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UpDownEventHandler.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + + /// + /// + /// + /// + /// UpDownEventHandler + /// A delegate for an updown event handler. + /// + /// + public delegate void UpDownEventHandler(object source, UpDownEventArgs e); + +} diff --git a/WindowsForms/Managed/System/WinForms/UserControl.cs b/WindowsForms/Managed/System/WinForms/UserControl.cs new file mode 100644 index 000000000..e78ed375d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/UserControl.cs @@ -0,0 +1,382 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using Microsoft.Win32; + using System; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.ComponentModel.Design.Serialization; + using System.Diagnostics; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Security.Permissions; + using System.Windows.Forms.Design; + using System.Windows.Forms.Layout; + + /// + /// + /// Represents an empty control that can be used in the Forms Designer to create other controls. By extending form, UserControl inherits all of + /// the standard positioning and mnemonic handling code that is necessary + /// in a user control. + /// + [ + ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + Designer("System.Windows.Forms.Design.UserControlDocumentDesigner, " + AssemblyRef.SystemDesign, typeof(IRootDesigner)), + Designer("System.Windows.Forms.Design.ControlDesigner, " + AssemblyRef.SystemDesign), + DesignerCategory("UserControl"), + DefaultEvent("Load") + ] + public class UserControl : ContainerControl { + private static readonly object EVENT_LOAD = new object(); + private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None; + + /// + /// + /// Creates a new UserControl object. A vast majority of people + /// will not want to instantiate this class directly, but will be a + /// sub-class of it. + /// + public UserControl() { + SetScrollState(ScrollStateAutoScrolling, false); + SetState(STATE_VISIBLE, true); + SetState(STATE_TOPLEVEL, false); + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + } + + /// + /// Override to re-expose AutoSize. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), + DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public override bool AutoSize + { + get + { + return base.AutoSize; + } + set + { + base.AutoSize = value; + } + } + + /// + /// Re-expose AutoSizeChanged. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler AutoSizeChanged { + add { + base.AutoSizeChanged += value; + } + remove { + base.AutoSizeChanged -= value; + } + } + + /// + /// Allows the control to optionally shrink when AutoSize is true. + /// + [ + SRDescription(SR.ControlAutoSizeModeDescr), + SRCategory(SR.CatLayout), + Browsable(true), + DefaultValue(AutoSizeMode.GrowOnly), + Localizable(true) + ] + public AutoSizeMode AutoSizeMode { + get { + return GetAutoSizeMode(); + } + set { + if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode)); + } + + if (GetAutoSizeMode() != value) { + SetAutoSizeMode(value); + Control toLayout = DesignMode || ParentInternal == null ? this : ParentInternal; + + if(toLayout != null) { + // DefaultLayout does not keep anchor information until it needs to. When + // AutoSize became a common property, we could no longer blindly call into + // DefaultLayout, so now we do a special InitLayout just for DefaultLayout. + if(toLayout.LayoutEngine == DefaultLayout.Instance) { + toLayout.LayoutEngine.InitLayout(this, BoundsSpecified.Size); + } + LayoutTransaction.DoLayout(toLayout, this, PropertyNames.AutoSize); + } + } + } + } + + /// + /// + /// Indicates whether controls in this container will be automatically validated when the focus changes. + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public override AutoValidate AutoValidate { + get { + return base.AutoValidate; + } + set { + base.AutoValidate = value; + } + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(true), + EditorBrowsable(EditorBrowsableState.Always), + ] + public new event EventHandler AutoValidateChanged { + add { + base.AutoValidateChanged += value; + } + remove { + base.AutoValidateChanged -= value; + } + } + + /// + /// + /// + /// Indicates the borderstyle for the UserControl. + /// + /// + [ + SRCategory(SR.CatAppearance), + DefaultValue(BorderStyle.None), + SRDescription(SR.UserControlBorderStyleDescr), + Browsable(true), EditorBrowsable(EditorBrowsableState.Always) + ] + public BorderStyle BorderStyle { + get { + return borderStyle; + } + + set { + if (borderStyle != value) { + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + } + + borderStyle = value; + UpdateStyles(); + } + } + } + + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info.This is required as we now need to pass the + /// styles for appropriate BorderStyle that is set by the user. + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT; + + cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); + cp.Style &= (~NativeMethods.WS_BORDER); + + switch (borderStyle) { + case BorderStyle.Fixed3D: + cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + break; + case BorderStyle.FixedSingle: + cp.Style |= NativeMethods.WS_BORDER; + break; + } + return cp; + } + } + + /// + /// + /// The default size for this user control. + /// + protected override Size DefaultSize { + get { + return new Size(150, 150); + } + } + + /// + /// + /// Occurs before the control becomes visible. + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.UserControlOnLoadDescr)] + public event EventHandler Load { + add { + Events.AddHandler(EVENT_LOAD, value); + } + remove { + Events.RemoveHandler(EVENT_LOAD, value); + } + } + + /// + [ + Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + Bindable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public override string Text { + get { + return base.Text; + } + set { + base.Text = value; + } + } + + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + base.TextChanged += value; + } + remove { + base.TextChanged -= value; + } + } + + /// + /// + /// Validates all selectable child controls in the container, including descendants. This is + /// equivalent to calling ValidateChildren(ValidationConstraints.Selectable). See + /// for details of exactly which child controls will be validated. + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren() { + return base.ValidateChildren(); + } + + /// + /// + /// Validates all the child controls in the container. Exactly which controls are + /// validated and which controls are skipped is determined by . + /// + [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] + public override bool ValidateChildren(ValidationConstraints validationConstraints) { + return base.ValidateChildren(validationConstraints); + } + + private bool FocusInside() { + if (!IsHandleCreated) return false; + + IntPtr hwndFocus = UnsafeNativeMethods.GetFocus(); + if (hwndFocus == IntPtr.Zero) return false; + + IntPtr hwnd = Handle; + if (hwnd == hwndFocus || SafeNativeMethods.IsChild(new HandleRef(this, hwnd), new HandleRef(null, hwndFocus))) + return true; + + return false; + } + + /// + /// + /// Raises the CreateControl event. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnCreateControl() { + base.OnCreateControl(); + + OnLoad(EventArgs.Empty); + } + + /// + /// + /// The Load event is fired before the control becomes visible for the first time. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnLoad(EventArgs e) { + // There is no good way to explain this event except to say + // that it's just another name for OnControlCreated. + EventHandler handler = (EventHandler)Events[EVENT_LOAD]; + if (handler != null) handler(this,e); + } + + /// + /// + /// OnResize override to invalidate entire control in Stetch mode + /// + /// + protected override void OnResize(EventArgs e) { + base.OnResize(e); + if (BackgroundImage != null) { + Invalidate(); + } + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnMouseDown(MouseEventArgs e) { + if (!FocusInside()) + FocusInternal(); + base.OnMouseDown(e); + } + + private void WmSetFocus(ref Message m) { + if (!HostedInWin32DialogManager) { + IntSecurity.ModifyFocus.Assert(); + try { + if (ActiveControl == null) + SelectNextControl(null, true, true, true, false); + } + finally { + System.Security.CodeAccessPermission.RevertAssert(); + } + } + if (!ValidationCancelled) { + base.WndProc(ref m); + } + + } + + /// + /// + /// [To be supplied.] + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_SETFOCUS: + WmSetFocus(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/VScrollBar.cs b/WindowsForms/Managed/System/WinForms/VScrollBar.cs new file mode 100644 index 000000000..f403a490d --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VScrollBar.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + + using System; + using System.Security.Permissions; + using System.Windows.Forms; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Runtime.InteropServices; + + /// + /// + /// Represents + /// a standard Windows vertical scroll bar. + /// + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [SRDescription(SR.DescriptionVScrollBar)] + public class VScrollBar : ScrollBar { + + private const int VERTICAL_SCROLLBAR_HEIGHT = 80; + + /// + /// + /// + /// + /// Returns the parameters needed to create the handle. Inheriting classes + /// can override this to provide extra functionality. They should not, + /// however, forget to call base.getCreateParams() first to get the struct + /// filled up with the basic info. + /// + /// + protected override CreateParams CreateParams { + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + get { + CreateParams cp = base.CreateParams; + cp.Style |= NativeMethods.SBS_VERT; + return cp; + } + } + + /// + /// + /// Deriving classes can override this to configure a default size for their control. + /// This is more efficient than setting the size in the control's constructor. + /// + protected override Size DefaultSize { + get { + if (DpiHelper.EnableDpiChangedHighDpiImprovements) { + return new Size(SystemInformation.GetVerticalScrollBarWidthForDpi(this.deviceDpi), LogicalToDeviceUnits(VERTICAL_SCROLLBAR_HEIGHT)); + } + else { + return new Size(SystemInformation.VerticalScrollBarWidth, VERTICAL_SCROLLBAR_HEIGHT); + } + } + } + + /// + /// + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public override RightToLeft RightToLeft { + get { + return RightToLeft.No; + } + set { + } + } + /// + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftChanged { + add { + base.RightToLeftChanged += value; + } + remove { + base.RightToLeftChanged -= value; + } + } + + } +} diff --git a/WindowsForms/Managed/System/WinForms/VScrollProperties.cs b/WindowsForms/Managed/System/WinForms/VScrollProperties.cs new file mode 100644 index 000000000..561e442ff --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VScrollProperties.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + using System.Runtime.InteropServices; + using System.Diagnostics; + using System; + using System.Security.Permissions; + using System.Runtime.Serialization.Formatters; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + using System.Windows.Forms; + + /// + /// + /// + /// Basic Properties for VScroll. + /// + /// + public class VScrollProperties : ScrollProperties { + + /// + public VScrollProperties(ScrollableControl container) : base(container) { + } + + internal override int PageSize { + get { + return ParentControl.ClientRectangle.Height; + } + } + + internal override int Orientation { + get { + return NativeMethods.SB_VERT; + } + } + + internal override int HorizontalDisplayPosition { + get { + return ParentControl.DisplayRectangle.X; + } + } + + internal override int VerticalDisplayPosition { + get { + return -this.value; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/ValidationConstraints.cs b/WindowsForms/Managed/System/WinForms/ValidationConstraints.cs new file mode 100644 index 000000000..f00ccae53 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/ValidationConstraints.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms +{ + using System; + + /// + /// + /// Determines which child controls in a ContainerControl will be validated + /// by the method. + /// + [Flags] + public enum ValidationConstraints { + /// + /// + /// All child controls and their descendants are validated. + /// + None = 0x00, + + /// + /// + /// Child control must be selectable to be validated. + /// + /// Note: This flag allows validation of a control that has the ControlStyles.Selectable style, ie. has + /// the ability to be selected, even when that control is currently unselectable because it has been + /// disabled or hidden. To prevent validation of selectable controls that are currently disabled or hidden, + /// include the or modes. + /// + Selectable = 0x01, + + /// + /// + /// Child control must be enabled to be validated (Control.Enabled = true). + /// + Enabled = 0x02, + + /// + /// + /// Child control must be visible to be validated (Control.Visible = true). + /// + Visible = 0x04, + + /// + /// + /// Child control must be a tab stops to be validated (Control.TabStop = true). + /// + TabStop = 0x08, + + /// + /// + /// Only immediate children of container control are validated. Descendants are not validated. + /// + ImmediateChildren = 0x10, + } +} diff --git a/WindowsForms/Managed/System/WinForms/View.cs b/WindowsForms/Managed/System/WinForms/View.cs new file mode 100644 index 000000000..e6cbe7cb7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/View.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms { + + using System.Diagnostics; + using System; + using System.ComponentModel; + using System.Drawing; + using Microsoft.Win32; + + + + /// + /// + /// + /// Specifies how list items are displayed in + /// a control. + /// + /// + public enum View { + + /// + /// + /// Each item appears as a full-sized icon with a label below it. + /// + LargeIcon = NativeMethods.LVS_ICON, + + /// + /// + /// + /// Each item appears on a seperate line with further + /// information about each item arranged in columns. The left + /// most column + /// contains a small icon and + /// label, and subsequent columns contain subitems as specified by the application. A + /// column displays a header which can display a caption for the + /// column. The user can resize each column at runtime. + /// + /// + Details = NativeMethods.LVS_REPORT, + + /// + /// + /// + /// Each item appears as a small icon with a label to its right. + /// + /// + SmallIcon = NativeMethods.LVS_SMALLICON, + + /// + /// + /// + /// Each item + /// appears as a small icon with a label to its right. + /// Items are arranged in columns with no column headers. + /// + /// + List = NativeMethods.LVS_LIST, + + /// + /// + /// + /// Tile view. + /// + /// + Tile = NativeMethods.LV_VIEW_TILE, + + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleElement.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleElement.cs new file mode 100644 index 000000000..dfcd3a02b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleElement.cs @@ -0,0 +1,6455 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System.Diagnostics.CodeAnalysis; + + +// Using nested types here is an intentional design. +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+SizingBarLeft")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+BackgroundLeft")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+BackgroundBottom")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+SizingBarTop")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+TaskbarClock+Time")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+BackgroundTop")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+SizingBarBottom")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+TaskbarClock")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+BackgroundRight")] +[assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope="type", Target="System.Windows.Forms.VisualStyles.VisualStyleElement+Taskbar+SizingBarRight")] +[assembly: SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { + + /// + /// + /// + /// Encapsulates the class, part and state of the "element" you wish to draw using + /// the VisualStyleRenderer. + /// Usage pattern is something like this: new VisualStyleRenderer(VisualStyleElement.Window.Caption.Active); + /// + /// + public class VisualStyleElement { + internal static readonly int Count = 25; //UPDATE THIS WHEN CLASSES ARE ADDED/REMOVED! + private string className; + private int part; + private int state; + + private VisualStyleElement(string className, int part, int state) { + this.className = className; + this.part = part; + this.state = state; + } + + public static VisualStyleElement CreateElement(string className, int part, int state) { + return new VisualStyleElement(className, part, state); + } + + public string ClassName { + get { + return className; + } + } + + public int Part { + get { + return part; + } + } + + public int State { + get { + return state; + } + } + + public static class Button { + private static readonly string className = "BUTTON"; + + public static class PushButton { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _default; + + public static VisualStyleElement Default { + get { + if (_default == null) { + _default = new VisualStyleElement(className, part, 5); + } + + return _default; + } + } + } + + public static class RadioButton { + private static readonly int part = 2; + // in Win10 RS3 a new part was added to BUTTONPARTS enum in vsstyle.h - BP_RADIOBUTTON_HCDISABLED = 8 + internal static readonly int HighContrastDisabledPart = 8; + + private static VisualStyleElement uncheckednormal; + + public static VisualStyleElement UncheckedNormal { + get { + if (uncheckednormal == null) { + uncheckednormal = new VisualStyleElement(className, part, 1); + } + + return uncheckednormal; + } + } + + private static VisualStyleElement uncheckedhot; + + public static VisualStyleElement UncheckedHot { + get { + if (uncheckedhot == null) { + uncheckedhot = new VisualStyleElement(className, part, 2); + } + + return uncheckedhot; + } + } + + private static VisualStyleElement uncheckedpressed; + + public static VisualStyleElement UncheckedPressed { + get { + if (uncheckedpressed == null) { + uncheckedpressed = new VisualStyleElement(className, part, 3); + } + + return uncheckedpressed; + } + } + + private static VisualStyleElement uncheckeddisabled; + + public static VisualStyleElement UncheckedDisabled { + get { + if (uncheckeddisabled == null) { + uncheckeddisabled = new VisualStyleElement(className, part, 4); + } + + return uncheckeddisabled; + } + } + + private static VisualStyleElement checkednormal; + + public static VisualStyleElement CheckedNormal { + get { + if (checkednormal == null) { + checkednormal = new VisualStyleElement(className, part, 5); + } + + return checkednormal; + } + } + + private static VisualStyleElement checkedhot; + + public static VisualStyleElement CheckedHot { + get { + if (checkedhot == null) { + checkedhot = new VisualStyleElement(className, part, 6); + } + + return checkedhot; + } + } + + private static VisualStyleElement checkedpressed; + + public static VisualStyleElement CheckedPressed { + get { + if (checkedpressed == null) { + checkedpressed = new VisualStyleElement(className, part, 7); + } + + return checkedpressed; + } + } + + private static VisualStyleElement checkeddisabled; + + public static VisualStyleElement CheckedDisabled { + get { + if (checkeddisabled == null) { + checkeddisabled = new VisualStyleElement(className, part, 8); + } + + return checkeddisabled; + } + } + } + + public static class CheckBox { + private static readonly int part = 3; + // in Win10 RS3 a new part was added to BUTTONPARTS enum in vsstyle.h - BP_CHECKBOX_HCDISABLED = 9 + internal static readonly int HighContrastDisabledPart = 9; + + private static VisualStyleElement uncheckednormal; + + public static VisualStyleElement UncheckedNormal { + get { + if (uncheckednormal == null) { + uncheckednormal = new VisualStyleElement(className, part, 1); + } + + return uncheckednormal; + } + } + + private static VisualStyleElement uncheckedhot; + + public static VisualStyleElement UncheckedHot { + get { + if (uncheckedhot == null) { + uncheckedhot = new VisualStyleElement(className, part, 2); + } + + return uncheckedhot; + } + } + + private static VisualStyleElement uncheckedpressed; + + public static VisualStyleElement UncheckedPressed { + get { + if (uncheckedpressed == null) { + uncheckedpressed = new VisualStyleElement(className, part, 3); + } + + return uncheckedpressed; + } + } + + private static VisualStyleElement uncheckeddisabled; + + public static VisualStyleElement UncheckedDisabled { + get { + if (uncheckeddisabled == null) { + uncheckeddisabled = new VisualStyleElement(className, part, 4); + } + + return uncheckeddisabled; + } + } + + private static VisualStyleElement checkednormal; + + public static VisualStyleElement CheckedNormal { + get { + if (checkednormal == null) { + checkednormal = new VisualStyleElement(className, part, 5); + } + + return checkednormal; + } + } + + private static VisualStyleElement checkedhot; + + public static VisualStyleElement CheckedHot { + get { + if (checkedhot == null) { + checkedhot = new VisualStyleElement(className, part, 6); + } + + return checkedhot; + } + } + + private static VisualStyleElement checkedpressed; + + public static VisualStyleElement CheckedPressed { + get { + if (checkedpressed == null) { + checkedpressed = new VisualStyleElement(className, part, 7); + } + + return checkedpressed; + } + } + + private static VisualStyleElement checkeddisabled; + + public static VisualStyleElement CheckedDisabled { + get { + if (checkeddisabled == null) { + checkeddisabled = new VisualStyleElement(className, part, 8); + } + + return checkeddisabled; + } + } + + private static VisualStyleElement mixednormal; + + public static VisualStyleElement MixedNormal { + get { + if (mixednormal == null) { + mixednormal = new VisualStyleElement(className, part, 9); + } + + return mixednormal; + } + } + + private static VisualStyleElement mixedhot; + + public static VisualStyleElement MixedHot { + get { + if (mixedhot == null) { + mixedhot = new VisualStyleElement(className, part, 10); + } + + return mixedhot; + } + } + + private static VisualStyleElement mixedpressed; + + public static VisualStyleElement MixedPressed { + get { + if (mixedpressed == null) { + mixedpressed = new VisualStyleElement(className, part, 11); + } + + return mixedpressed; + } + } + + private static VisualStyleElement mixeddisabled; + + public static VisualStyleElement MixedDisabled { + get { + if (mixeddisabled == null) { + mixeddisabled = new VisualStyleElement(className, part, 12); + } + + return mixeddisabled; + } + } + } + + public static class GroupBox { + private static readonly int part = 4; + // in Win10 RS3 a new part was added to BUTTONPARTS enum in vsstyle.h - BP_GROUPBOX_HCDISABLED = 10 + internal static readonly int HighContrastDisabledPart = 10; + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 2); + } + + return disabled; + } + } + } + + public static class UserButton { + private static readonly int part = 5; + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } //END BUTTON + + public static class ComboBox { + private static readonly string className = "COMBOBOX"; + + public static class DropDownButton { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + // Following parts exist on Vista and later releases only + internal static class Border + { + // Paints a rectangle with a 1 pixel edge + round corners, + // and fills it with a color + private const int part = 4; + + // possible states for various fill color: + // 2 - semi transparent + // 3 - Window Color (white by default) + // 4 - disabled (light gray by default) + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal + { + get + { + if (normal == null) + { + normal = new VisualStyleElement(className, part, 3); + } + return normal; + } + } + } + + internal static class ReadOnlyButton + { + // Paints a button for non-editable comboboxes (DropDownStyle=DropDownList) + private const int part = 5; + + // possible states: + // 1 - disabled (gray by default) + // 2 - normal (blue by default) + // 3 - pressed (dark blue by default) + // 4 - flat (light gray by default) + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal + { + get + { + if (normal == null) + { + normal = new VisualStyleElement(className, part, 2); + } + return normal; + } + } + } + + internal static class DropDownButtonRight + { + // Paints a dropdownbutton with right angles on the left side for + // editable comboboxes (DropDownStyle=DropDown) for RTL=false case + private const int part = 6; + + // possible states: + // 1 - normal (transparent background with just normal arrow) + // 2 - hot (arrow with blue background by default) + // 3 - pressed (arrow with dark blue background by default) + // 4 - disabled (transparent background with just light arrow) + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal + { + get + { + if (normal == null) + { + normal = new VisualStyleElement(className, part, 1); + } + return normal; + } + } + + /* Unused for now + private static VisualStyleElement hot; + + public static VisualStyleElement Hot + { + get + { + if (hot == null) + { + hot = new VisualStyleElement(className, part, 2); + } + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed + { + get + { + if (pressed == null) + { + pressed = new VisualStyleElement(className, part, 3); + } + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled + { + get + { + if (disabled == null) + { + disabled = new VisualStyleElement(className, part, 4); + } + return disabled; + } + } + */ + } + + internal static class DropDownButtonLeft + { + // Paints a dropdownbutton with right angles on the right side for + // editable comboboxes (DropDownStyle=DropDown) for RTL=true case + private const int part = 7; + + // possible states: + // 1 - transparent normal (just normal arrow) + // 2 - normal (blue by default) + // 3 - pressed (dark blue by default) + // 4 - transparent disabled (just light arrow) + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal + { + get + { + if (normal == null) + { + normal = new VisualStyleElement(className, part, 2); + } + return normal; + } + } + } + + /* Unused for now + internal static class CueBanner + { + // Paints a rectangle with sharp edges and fills it. Default edge color is black, + // default fill color is white. + private const int part = 8; + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal + { + get + { + if (normal == null) + { + normal = new VisualStyleElement(className, part, 1); + } + return normal; + } + } + } + */ + } //END COMBOBOX + + public static class Page { + private static readonly string className = "PAGE"; + + public static class Up { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class Down { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class UpHorizontal { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class DownHorizontal { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + } //END PAGE + + public static class Spin { + private static readonly string className = "SPIN"; + + public static class Up { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class Down { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class UpHorizontal { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class DownHorizontal { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + } //END SPIN + + public static class ScrollBar { + private static readonly string className = "SCROLLBAR"; + + public static class ArrowButton { + private static readonly int part = 1; + + private static VisualStyleElement upnormal; + + public static VisualStyleElement UpNormal { + get { + if (upnormal == null) { + upnormal = new VisualStyleElement(className, part, 1); + } + + return upnormal; + } + } + + private static VisualStyleElement uphot; + + public static VisualStyleElement UpHot { + get { + if (uphot == null) { + uphot = new VisualStyleElement(className, part, 2); + } + + return uphot; + } + } + + private static VisualStyleElement uppressed; + + public static VisualStyleElement UpPressed { + get { + if (uppressed == null) { + uppressed = new VisualStyleElement(className, part, 3); + } + + return uppressed; + } + } + + private static VisualStyleElement updisabled; + + public static VisualStyleElement UpDisabled { + get { + if (updisabled == null) { + updisabled = new VisualStyleElement(className, part, 4); + } + + return updisabled; + } + } + + private static VisualStyleElement downnormal; + + public static VisualStyleElement DownNormal { + get { + if (downnormal == null) { + downnormal = new VisualStyleElement(className, part, 5); + } + + return downnormal; + } + } + + private static VisualStyleElement downhot; + + public static VisualStyleElement DownHot { + get { + if (downhot == null) { + downhot = new VisualStyleElement(className, part, 6); + } + + return downhot; + } + } + + private static VisualStyleElement downpressed; + + public static VisualStyleElement DownPressed { + get { + if (downpressed == null) { + downpressed = new VisualStyleElement(className, part, 7); + } + + return downpressed; + } + } + + private static VisualStyleElement downdisabled; + + public static VisualStyleElement DownDisabled { + get { + if (downdisabled == null) { + downdisabled = new VisualStyleElement(className, part, 8); + } + + return downdisabled; + } + } + + private static VisualStyleElement leftnormal; + + public static VisualStyleElement LeftNormal { + get { + if (leftnormal == null) { + leftnormal = new VisualStyleElement(className, part, 9); + } + + return leftnormal; + } + } + + private static VisualStyleElement lefthot; + + public static VisualStyleElement LeftHot { + get { + if (lefthot == null) { + lefthot = new VisualStyleElement(className, part, 10); + } + + return lefthot; + } + } + + private static VisualStyleElement leftpressed; + + public static VisualStyleElement LeftPressed { + get { + if (leftpressed == null) { + leftpressed = new VisualStyleElement(className, part, 11); + } + + return leftpressed; + } + } + + private static VisualStyleElement leftdisabled; + + public static VisualStyleElement LeftDisabled { + get { + if (leftdisabled == null) { + leftdisabled = new VisualStyleElement(className, part, 12); + } + + return leftdisabled; + } + } + + private static VisualStyleElement rightnormal; + + public static VisualStyleElement RightNormal { + get { + if (rightnormal == null) { + rightnormal = new VisualStyleElement(className, part, 13); + } + + return rightnormal; + } + } + + private static VisualStyleElement righthot; + + public static VisualStyleElement RightHot { + get { + if (righthot == null) { + righthot = new VisualStyleElement(className, part, 14); + } + + return righthot; + } + } + + private static VisualStyleElement rightpressed; + + public static VisualStyleElement RightPressed { + get { + if (rightpressed == null) { + rightpressed = new VisualStyleElement(className, part, 15); + } + + return rightpressed; + } + } + + private static VisualStyleElement rightdisabled; + + public static VisualStyleElement RightDisabled { + get { + if (rightdisabled == null) { + rightdisabled = new VisualStyleElement(className, part, 16); + } + + return rightdisabled; + } + } + } + + public static class ThumbButtonHorizontal { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class ThumbButtonVertical { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class RightTrackHorizontal { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class LeftTrackHorizontal { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class LowerTrackVertical { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class UpperTrackVertical { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class GripperHorizontal { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class GripperVertical { + private static readonly int part = 9; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SizeBox { + private static readonly int part = 10; + + + private static VisualStyleElement rightalign; + + public static VisualStyleElement RightAlign { + get { + if (rightalign == null) { + rightalign = new VisualStyleElement(className, part, 1); + } + + return rightalign; + } + } + + private static VisualStyleElement leftalign; + + public static VisualStyleElement LeftAlign { + get { + if (leftalign == null) { + leftalign = new VisualStyleElement(className, part, 2); + } + + return leftalign; + } + } + } + } // END SCROLLBAR + + public static class Tab { + private static readonly string className = "TAB"; + + public static class TabItem { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class TabItemLeftEdge { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class TabItemRightEdge { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class TabItemBothEdges { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class TopTabItem { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + } + + public static class TopTabItemLeftEdge { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class TopTabItemRightEdge { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + } + + public static class TopTabItemBothEdges { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Pane { + private static readonly int part = 9; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + + public static class Body { + private static readonly int part = 10; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + } // END TAB + + public static class ExplorerBar { + private static readonly string className = "EXPLORERBAR"; + + public static class HeaderBackground { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + + public static class HeaderClose { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class HeaderPin { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement selectednormal; + + public static VisualStyleElement SelectedNormal { + get { + if (selectednormal == null) { + selectednormal = new VisualStyleElement(className, part, 4); + } + + return selectednormal; + } + } + + private static VisualStyleElement selectedhot; + + public static VisualStyleElement SelectedHot { + get { + if (selectedhot == null) { + selectedhot = new VisualStyleElement(className, part, 5); + } + + return selectedhot; + } + } + + private static VisualStyleElement selectedpressed; + + public static VisualStyleElement SelectedPressed { + get { + if (selectedpressed == null) { + selectedpressed = new VisualStyleElement(className, part, 6); + } + + return selectedpressed; + } + } + } + + public static class IEBarMenu { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class NormalGroupBackground { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + + public static class NormalGroupCollapse { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class NormalGroupExpand { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + + public static class NormalGroupHead { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + + public static class SpecialGroupBackground { + private static readonly int part = 9; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + + + public static class SpecialGroupCollapse { + private static readonly int part = 10; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + + public static class SpecialGroupExpand { + private static readonly int part = 11; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + + public static class SpecialGroupHead { + private static readonly int part = 12; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + } // END EXPLORERBAR + + public static class Header { + private static readonly string className = "HEADER"; + + public static class Item { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class ItemLeft { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class ItemRight { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class SortArrow { + private static readonly int part = 4; + + + private static VisualStyleElement sortedup; + + public static VisualStyleElement SortedUp { + get { + if (sortedup == null) { + sortedup = new VisualStyleElement(className, part, 1); + } + + return sortedup; + } + } + + + private static VisualStyleElement sorteddown; + + public static VisualStyleElement SortedDown { + get { + if (sorteddown == null) { + sorteddown = new VisualStyleElement(className, part, 2); + } + + return sorteddown; + } + } + } + } // END HEADER + + public static class ListView { + private static readonly string className = "LISTVIEW"; + + public static class Item { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement selected; + + public static VisualStyleElement Selected { + get { + if (selected == null) { + selected = new VisualStyleElement(className, part, 3); + } + + return selected; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement selectednotfocus; + + public static VisualStyleElement SelectedNotFocus { + get { + if (selectednotfocus == null) { + selectednotfocus = new VisualStyleElement(className, part, 5); + } + + return selectednotfocus; + } + } + } + + public static class Group { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Detail { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SortedDetail { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class EmptyText { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + } // END LISTVIEW + + public static class MenuBand { + private static readonly string className = "MENUBAND"; + + public static class NewApplicationButton { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _checked; + + public static VisualStyleElement Checked { + get { + if (_checked == null) { + _checked = new VisualStyleElement(className, part, 5); + } + + return _checked; + } + } + + private static VisualStyleElement hotchecked; + + public static VisualStyleElement HotChecked { + get { + if (hotchecked == null) { + hotchecked = new VisualStyleElement(className, part, 6); + } + + return hotchecked; + } + } + } + + + public static class Separator { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END MENUBAND + + public static class Menu { + private static readonly string className = "MENU"; + + public static class Item { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement selected; + + public static VisualStyleElement Selected { + get { + if (selected == null) { + selected = new VisualStyleElement(className, part, 2); + } + + return selected; + } + } + + private static VisualStyleElement demoted; + + public static VisualStyleElement Demoted { + get { + if (demoted == null) { + demoted = new VisualStyleElement(className, part, 3); + } + + return demoted; + } + } + } + + public static class DropDown { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BarItem { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BarDropDown { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Chevron { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Separator { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END MENU + + public static class ProgressBar { + private static readonly string className = "PROGRESS"; + + public static class Bar { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BarVertical { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Chunk { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class ChunkVertical { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END PROGRESSBAR + + public static class Rebar { + private static readonly string className = "REBAR"; + + public static class Gripper { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class GripperVertical { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Band { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + + } + + public static class Chevron { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class ChevronVertical { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + } // END REBAR + + public static class StartPanel { + private static readonly string className = "STARTPANEL"; + + public static class UserPane { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class MorePrograms { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class MoreProgramsArrow { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class ProgList { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class ProgListSeparator { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class PlaceList { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class PlaceListSeparator { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + //The verb, not the noun. Matches "Log Off" button. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public static class LogOff + { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + //The verb, not the noun. Matches "Log Off" button. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public static class LogOffButtons + { + private static readonly int part = 9; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + + public static class UserPicture { + private static readonly int part = 10; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Preview { + private static readonly int part = 11; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END STARTPANEL + + public static class Status { + private static readonly string className = "STATUS"; + + public static class Bar { + private static readonly int part; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Pane { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class GripperPane { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Gripper { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END STATUS + + public static class TaskBand { + private static readonly string className = "TASKBAND"; + + public static class GroupCount { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class FlashButton { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class FlashButtonGroupMenu { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END TASKBAND + + public static class TaskbarClock { + private static readonly string className = "CLOCK"; + + public static class Time { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + } + } // END TASKBARCLOCK + + public static class Taskbar { + private static readonly string className = "TASKBAR"; + + public static class BackgroundBottom { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BackgroundRight { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BackgroundTop { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class BackgroundLeft { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SizingBarBottom { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SizingBarRight { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SizingBarTop { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SizingBarLeft { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END TASKBAR + + public static class ToolBar { + private static readonly string className = "TOOLBAR"; + + // + internal static class Bar { + private static readonly int part; + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Button { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _checked; + + public static VisualStyleElement Checked { + get { + if (_checked == null) { + _checked = new VisualStyleElement(className, part, 5); + } + + return _checked; + } + } + + private static VisualStyleElement hotchecked; + + public static VisualStyleElement HotChecked { + get { + if (hotchecked == null) { + hotchecked = new VisualStyleElement(className, part, 6); + } + + return hotchecked; + } + } + } + + public static class DropDownButton { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _checked; + + public static VisualStyleElement Checked { + get { + if (_checked == null) { + _checked = new VisualStyleElement(className, part, 5); + } + + return _checked; + } + } + + private static VisualStyleElement hotchecked; + + public static VisualStyleElement HotChecked { + get { + if (hotchecked == null) { + hotchecked = new VisualStyleElement(className, part, 6); + } + + return hotchecked; + } + } + } + + public static class SplitButton { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _checked; + + public static VisualStyleElement Checked { + get { + if (_checked == null) { + _checked = new VisualStyleElement(className, part, 5); + } + + return _checked; + } + } + + private static VisualStyleElement hotchecked; + + public static VisualStyleElement HotChecked { + get { + if (hotchecked == null) { + hotchecked = new VisualStyleElement(className, part, 6); + } + + return hotchecked; + } + } + } + + public static class SplitButtonDropDown { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement _checked; + + public static VisualStyleElement Checked { + get { + if (_checked == null) { + _checked = new VisualStyleElement(className, part, 5); + } + + return _checked; + } + } + + private static VisualStyleElement hotchecked; + + public static VisualStyleElement HotChecked { + get { + if (hotchecked == null) { + hotchecked = new VisualStyleElement(className, part, 6); + } + + return hotchecked; + } + } + } + + public static class SeparatorHorizontal { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SeparatorVertical { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + } // END TOOLBAR + + public static class ToolTip { + private static readonly string className = "TOOLTIP"; + + public static class Standard { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement link; + + public static VisualStyleElement Link { + get { + if (link == null) { + link = new VisualStyleElement(className, part, 2); + } + + return link; + } + } + } + + public static class StandardTitle { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Balloon { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement link; + + public static VisualStyleElement Link { + get { + if (link == null) { + link = new VisualStyleElement(className, part, 2); + } + + return link; + } + } + } + + public static class BalloonTitle { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class Close { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + } + } // END TOOLTIP + + public static class TrackBar { + private static readonly string className = "TRACKBAR"; + + public static class Track { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + } + + public static class TrackVertical { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + } + + public static class Thumb { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class ThumbBottom { + private static readonly int part = 4; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class ThumbTop { + private static readonly int part = 5; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class ThumbVertical { + private static readonly int part = 6; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class ThumbLeft { + private static readonly int part = 7; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class ThumbRight { + private static readonly int part = 8; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 4); + } + + return focused; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 5); + } + + return disabled; + } + } + } + + public static class Ticks { + private static readonly int part = 9; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + } + + public static class TicksVertical { + private static readonly int part = 10; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + } + } // END TRACKBAR + + public static class TreeView { + private static readonly string className = "TREEVIEW"; + + + public static class Item { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement selected; + + public static VisualStyleElement Selected { + get { + if (selected == null) { + selected = new VisualStyleElement(className, part, 3); + } + + return selected; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement selectednotfocus; + + public static VisualStyleElement SelectedNotFocus { + get { + if (selectednotfocus == null) { + selectednotfocus = new VisualStyleElement(className, part, 5); + } + + return selectednotfocus; + } + } + } + + public static class Glyph { + private static readonly int part = 2; + + private static VisualStyleElement closed; + + public static VisualStyleElement Closed { + get { + if (closed == null) { + closed = new VisualStyleElement(className, part, 1); + } + + return closed; + } + } + + private static VisualStyleElement opened; + + public static VisualStyleElement Opened { + get { + if (opened == null) { + opened = new VisualStyleElement(className, part, 2); + } + + return opened; + } + } + } + + public static class Branch { + private static readonly int part = 3; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END TREEVIEW + + // In Dev10, we replace outline +/- glyphs in PropertyGrid with triangle glyphs (Dev10 Bug 573429). + // Therefore, a new element ExplorerTreeView.Glyph is defined here. + // Due to limited resouce and schedule, we don't define all elements now, + // and keep it internal use only. + // We'll consider to publish it later when we have enough resource. + internal static class ExplorerTreeView { + private static readonly string className = "Explorer::TreeView"; + + public static class Glyph { + private static readonly int part = 2; + + private static VisualStyleElement closed; + + public static VisualStyleElement Closed { + get { + if (closed == null) { + closed = new VisualStyleElement(className, part, 1); + } + + return closed; + } + } + + private static VisualStyleElement opened; + + public static VisualStyleElement Opened { + get { + if (opened == null) { + opened = new VisualStyleElement(className, part, 2); + } + + return opened; + } + } + } + } // END Explorer::Tree + + public static class TextBox { + private static readonly string className = "EDIT"; + + public static class TextEdit { + private static readonly int part = 1; + + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement selected; + + public static VisualStyleElement Selected { + get { + if (selected == null) { + selected = new VisualStyleElement(className, part, 3); + } + + return selected; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + + private static VisualStyleElement focused; + + public static VisualStyleElement Focused { + get { + if (focused == null) { + focused = new VisualStyleElement(className, part, 5); + } + + return focused; + } + } + + private static VisualStyleElement _readonly; + + public static VisualStyleElement ReadOnly { + get { + if (_readonly == null) { + _readonly = new VisualStyleElement(className, part, 6); + } + + return _readonly; + } + } + + private static VisualStyleElement assist; + + public static VisualStyleElement Assist { + get { + if (assist == null) { + assist = new VisualStyleElement(className, part, 7); + } + + return assist; + } + } + } + + public static class Caret { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END TEXTBOX + + public static class TrayNotify { + private static readonly string className = "TRAYNOTIFY"; + + public static class Background { + private static readonly int part = 1; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class AnimateBackground { + private static readonly int part = 2; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END TRAYNOTIFY + + public static class Window { + private static readonly string className = "WINDOW"; + + public static class Caption { + private static readonly int part = 1; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class SmallCaption { + private static readonly int part = 2; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class MinCaption { + private static readonly int part = 3; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class SmallMinCaption { + private static readonly int part = 4; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class MaxCaption { + private static readonly int part = 5; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class SmallMaxCaption { + private static readonly int part = 6; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 3); + } + + return disabled; + } + } + } + + public static class FrameLeft { + private static readonly int part = 7; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class FrameRight { + private static readonly int part = 8; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class FrameBottom { + private static readonly int part = 9; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class SmallFrameLeft { + private static readonly int part = 10; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class SmallFrameRight { + private static readonly int part = 11; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class SmallFrameBottom { + private static readonly int part = 12; + + + private static VisualStyleElement active; + + public static VisualStyleElement Active { + get { + if (active == null) { + active = new VisualStyleElement(className, part, 1); + } + + return active; + } + } + + private static VisualStyleElement inactive; + + public static VisualStyleElement Inactive { + get { + if (inactive == null) { + inactive = new VisualStyleElement(className, part, 2); + } + + return inactive; + } + } + } + + public static class SysButton { + private static readonly int part = 13; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MdiSysButton { + private static readonly int part = 14; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MinButton { + private static readonly int part = 15; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MdiMinButton { + private static readonly int part = 16; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MaxButton { + private static readonly int part = 17; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class CloseButton { + private static readonly int part = 18; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class SmallCloseButton { + private static readonly int part = 19; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MdiCloseButton { + private static readonly int part = 20; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class RestoreButton { + private static readonly int part = 21; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MdiRestoreButton { + private static readonly int part = 22; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class HelpButton { + private static readonly int part = 23; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class MdiHelpButton { + private static readonly int part = 24; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class HorizontalScroll { + private static readonly int part = 25; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class HorizontalThumb { + private static readonly int part = 26; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class VerticalScroll { + private static readonly int part = 27; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class VerticalThumb { + private static readonly int part = 28; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 1); + } + + return normal; + } + } + + private static VisualStyleElement hot; + + public static VisualStyleElement Hot { + get { + if (hot == null) { + hot = new VisualStyleElement(className, part, 2); + } + + return hot; + } + } + + private static VisualStyleElement pressed; + + public static VisualStyleElement Pressed { + get { + if (pressed == null) { + pressed = new VisualStyleElement(className, part, 3); + } + + return pressed; + } + } + + private static VisualStyleElement disabled; + + public static VisualStyleElement Disabled { + get { + if (disabled == null) { + disabled = new VisualStyleElement(className, part, 4); + } + + return disabled; + } + } + } + + public static class Dialog { + private static readonly int part = 29; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class CaptionSizingTemplate { + private static readonly int part = 30; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SmallCaptionSizingTemplate { + private static readonly int part = 31; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class FrameLeftSizingTemplate { + private static readonly int part = 32; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SmallFrameLeftSizingTemplate { + private static readonly int part = 33; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + //Not used as compound word here + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public static class FrameRightSizingTemplate + { + private static readonly int part = 34; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + //Not used as compound word here + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public static class SmallFrameRightSizingTemplate + { + private static readonly int part = 35; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class FrameBottomSizingTemplate { + private static readonly int part = 36; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + + public static class SmallFrameBottomSizingTemplate { + private static readonly int part = 37; + + + private static VisualStyleElement normal; + + public static VisualStyleElement Normal { + get { + if (normal == null) { + normal = new VisualStyleElement(className, part, 0); + } + + return normal; + } + } + } + } // END WINDOW + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleInformation.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleInformation.cs new file mode 100644 index 000000000..463ec65f2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleInformation.cs @@ -0,0 +1,314 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { + + using System; + using System.Text; + using System.Drawing; + using System.Windows.Forms; + using System.Runtime.InteropServices; + using System.Diagnostics.CodeAnalysis; + + + /// + /// + /// + /// Provides information about the current visual style. + /// NOTE: + /// 1) These properties (except SupportByOS, which is always meaningful) are meaningful only + /// if visual styles are supported and have currently been applied by the user. + /// 2) A subset of these use VisualStyleRenderer objects, so they are + /// not meaningful unless VisualStyleRenderer.IsSupported is true. + /// + /// + public static class VisualStyleInformation { + + //Make this per-thread, so that different threads can safely use these methods. + [ThreadStatic] + private static VisualStyleRenderer visualStyleRenderer = null; + + /// + /// + /// + /// Used to find whether visual styles are supported by the current OS. Same as + /// using the OSFeature class to see if themes are supported. + /// + /// + public static bool IsSupportedByOS { + get { + return (OSFeature.Feature.IsPresent(OSFeature.Themes)); + } + } + + /// + /// + /// + /// Returns true if a visual style has currently been applied by the user, else false. + /// + /// + public static bool IsEnabledByUser { + get { + if (!IsSupportedByOS) { + return false; + } + + return (SafeNativeMethods.IsAppThemed()); + } + } + + internal static string ThemeFilename { + get { + if (IsEnabledByUser) { + StringBuilder filename = new StringBuilder(512); + SafeNativeMethods.GetCurrentThemeName(filename, filename.Capacity, null, 0, null, 0); + return (filename.ToString()); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's color scheme name. + /// + public static string ColorScheme { + get { + if (IsEnabledByUser) { + StringBuilder colorScheme = new StringBuilder(512); + SafeNativeMethods.GetCurrentThemeName(null, 0, colorScheme, colorScheme.Capacity, null, 0); + return (colorScheme.ToString()); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's size name. + /// + public static string Size { + get { + if (IsEnabledByUser) { + StringBuilder size = new StringBuilder(512); + SafeNativeMethods.GetCurrentThemeName(null, 0, null, 0, size, size.Capacity); + return (size.ToString()); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's display name. + /// + public static string DisplayName { + get { + if (IsEnabledByUser) { + StringBuilder name = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.DisplayName, name, name.Capacity); + return name.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's company. + /// + public static string Company { + get { + if (IsEnabledByUser) { + StringBuilder company = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Company, company, company.Capacity); + return company.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The name of the current visual style's author. + /// + public static string Author { + get { + if (IsEnabledByUser) { + StringBuilder author = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Author, author, author.Capacity); + return author.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's copyright information. + /// + public static string Copyright { + get { + if (IsEnabledByUser) { + StringBuilder copyright = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Copyright, copyright, copyright.Capacity); + return copyright.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's url. + /// + [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] + public static string Url { + [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")] + get { + if (IsEnabledByUser) { + StringBuilder url = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Url, url, url.Capacity); + return url.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's version. + /// + public static string Version { + get { + if (IsEnabledByUser) { + StringBuilder version = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Version, version, version.Capacity); + return version.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// The current visual style's description. + /// + public static string Description { + get { + if (IsEnabledByUser) { + StringBuilder description = new StringBuilder(512); + SafeNativeMethods.GetThemeDocumentationProperty(ThemeFilename, VisualStyleDocProperty.Description, description, description.Capacity); + return description.ToString(); + } + + return String.Empty; + } + } + + /// + /// + /// Returns true if the current theme supports flat menus, else false. + /// + public static bool SupportsFlatMenus { + get { + if (Application.RenderWithVisualStyles) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Window.Caption.Active); + } + else { + visualStyleRenderer.SetParameters(VisualStyleElement.Window.Caption.Active); + } + + return (SafeNativeMethods.GetThemeSysBool(new HandleRef(null, visualStyleRenderer.Handle), VisualStyleSystemProperty.SupportsFlatMenus)); + } + + return false; + } + } + + /// + /// + /// The minimum color depth supported by the current visual style. + /// + public static int MinimumColorDepth { + get { + if (Application.RenderWithVisualStyles) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Window.Caption.Active); + } + else { + visualStyleRenderer.SetParameters(VisualStyleElement.Window.Caption.Active); + } + + int mcDepth = 0; + + SafeNativeMethods.GetThemeSysInt(new HandleRef(null, visualStyleRenderer.Handle), VisualStyleSystemProperty.MinimumColorDepth, ref mcDepth); + return mcDepth; + } + + return 0; + } + } + + /// + /// + /// Border Color that Windows XP renders for controls like TextBox and ComboBox. + /// + public static Color TextControlBorder { + get { + if (Application.RenderWithVisualStyles) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Normal); + } + else { + visualStyleRenderer.SetParameters(VisualStyleElement.TextBox.TextEdit.Normal); + } + Color borderColor = visualStyleRenderer.GetColor(ColorProperty.BorderColor); + return borderColor; + } + + return SystemColors.WindowFrame; + } + } + + + /// + /// + /// This is the color buttons and tab pages are highlighted with when they are moused over on themed OS. + /// + public static Color ControlHighlightHot { + get { + if (Application.RenderWithVisualStyles) { + if (visualStyleRenderer == null) { + visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Button.PushButton.Normal); + + } + else { + visualStyleRenderer.SetParameters(VisualStyleElement.Button.PushButton.Normal); + } + Color accentColor = visualStyleRenderer.GetColor(ColorProperty.AccentColorHint); + return accentColor; + } + + return SystemColors.ButtonHighlight; + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleRenderer.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleRenderer.cs new file mode 100644 index 000000000..90f7eb70b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleRenderer.cs @@ -0,0 +1,1182 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { + using System; + using System.Drawing; + using System.Windows.Forms.Internal; + using System.Text; + using System.Windows.Forms; + using System.Collections; + using System.ComponentModel; + using System.Globalization; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using Microsoft.Win32; + using System.Security; + using System.Security.Permissions; + + /// + /// + /// + /// This class provides full feature parity with UxTheme API. + /// + /// + public sealed class VisualStyleRenderer { + private const TextFormatFlags AllGraphicsProperties = TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.PreserveGraphicsTranslateTransform; + + internal const int EdgeAdjust = 0x2000; //used with Edges in VisualStyleRenderer.DrawThemeEdge + private string _class; + private int part; + private int state; + private int lastHResult = 0; + private static int numberOfPossibleClasses = VisualStyleElement.Count; //used as size for themeHandles + + [ThreadStatic] + private static Hashtable themeHandles = null; //per-thread cache of ThemeHandle objects. + [ThreadStatic] + private static long threadCacheVersion = 0; + + private static long globalCacheVersion = 0; + + static VisualStyleRenderer() { + SystemEvents.UserPreferenceChanging += new UserPreferenceChangingEventHandler(OnUserPreferenceChanging); + } + + /// + /// Check if visual styles is supported for client area. + /// + private static bool AreClientAreaVisualStylesSupported { + get { + return (VisualStyleInformation.IsEnabledByUser && + ((Application.VisualStyleState & VisualStyleState.ClientAreaEnabled) == VisualStyleState.ClientAreaEnabled)); + } + } + /// + /// + /// + /// Returns true if visual styles are 1) supported by the OS 2) enabled in the client area + /// and 3) currently applied to this application. Otherwise, it returns false. Note that + /// if it returns false, attempting to instantiate/use objects of this class + /// will result in exceptions being thrown. + /// + /// + public static bool IsSupported { + get { + bool supported = AreClientAreaVisualStylesSupported; + if (supported) { + // VSWhidbey #171915: In some cases, this check isn't enough, since the theme handle creation + // could fail for some other reason. Try creating a theme handle here - if successful, return true, + // else return false. + IntPtr hTheme = GetHandle("BUTTON", false); //Button is an arbitrary choice. + supported = (hTheme != IntPtr.Zero); + } + + return supported; + + } + } + + /// + /// + /// + /// Returns true if the element is defined by the current visual style, else false. + /// Note: + /// 1) Throws an exception if IsSupported is false, since it is illegal to call it in that case. + /// 2) The underlying API does not validate states. So if you pass in invalid state values, + /// we might still return true. When you use an invalid state to render, you get the default + /// state instead. + /// + /// + public static bool IsElementDefined(VisualStyleElement element) { + if (element == null) { + throw new ArgumentNullException("element"); + } + + return IsCombinationDefined(element.ClassName, element.Part); + } + + internal static bool IsCombinationDefined(string className, int part) { + bool returnVal = false; + + if (!IsSupported) { + if (!VisualStyleInformation.IsEnabledByUser) { + throw new InvalidOperationException(SR.GetString(SR.VisualStyleNotActive)); + } + else { + throw new InvalidOperationException(SR.GetString(SR.VisualStylesDisabledInClientArea)); + } + } + + if (className == null) { + throw new ArgumentNullException("className"); + } + + IntPtr hTheme = GetHandle(className, false); + + if (hTheme != IntPtr.Zero) { + // IsThemePartDefined doesn't work for part = 0, although there are valid parts numbered 0. We + // allow these explicitly here. + if (part == 0) { + returnVal = true; + } + else { + returnVal = SafeNativeMethods.IsThemePartDefined(new HandleRef(null, hTheme), part, 0); + } + } + + //if the combo isn't defined, check the validity of our theme handle cache + if (!returnVal) { + using (ThemeHandle tHandle = ThemeHandle.Create(className, false)) { + if (tHandle != null) { + returnVal = SafeNativeMethods.IsThemePartDefined(new HandleRef(null, tHandle.NativeHandle), part, 0); + } + + //if we did, in fact get a new correct theme handle, our cache is out of date -- update it now. + if (returnVal) { + RefreshCache(); + } + } + } + + return returnVal; + } + + /// + /// + /// + /// Constructor takes a VisualStyleElement. + /// + /// + public VisualStyleRenderer(VisualStyleElement element) : this(element.ClassName, element.Part, element.State) { + } + + /// + /// + /// + /// Constructor takes weakly typed parameters - left for extensibility (using classes, parts or states + /// not defined in the VisualStyleElement class.) + /// + /// + public VisualStyleRenderer(string className, int part, int state) { + if (!IsCombinationDefined(className, part)) { //internally this call takes care of IsSupported. + throw new ArgumentException(SR.GetString(SR.VisualStylesInvalidCombination)); + } + + this._class = className; + this.part = part; + this.state = state; + } + + /// + /// + /// + /// Returns the current _class. Use SetParameters to set. + /// + /// + public string Class { + get { + return _class; + } + } + + /// + /// + /// + /// Returns the current part. Use SetParameters to set. + /// + /// + public int Part { + get { + return part; + } + } + + /// + /// + /// + /// Returns the current state. Use SetParameters to set. + /// + /// + public int State { + get { + return state; + } + } + + /// + /// + /// + /// Returns the underlying HTheme handle. + /// NOTE: The handle gets invalidated when the theme changes or the user disables theming. When that + /// happens, the user should requery this property to get the correct handle. To know when the + /// theme changed, hook on to SystemEvents.UserPreferenceChanged and look for ThemeChanged + /// category. + /// + /// + public IntPtr Handle { + get { + if (!IsSupported) { + if (!VisualStyleInformation.IsEnabledByUser) { + throw new InvalidOperationException(SR.GetString(SR.VisualStyleNotActive)); + } + else { + throw new InvalidOperationException(SR.GetString(SR.VisualStylesDisabledInClientArea)); + } + } + + return GetHandle(_class); + } + } + + /// + /// + /// + /// Used to set a new VisualStyleElement on this VisualStyleRenderer instance. + /// + /// + public void SetParameters(VisualStyleElement element) { + if (element == null) { + throw new ArgumentNullException("element"); + } + + SetParameters(element.ClassName, element.Part, element.State); + } + + /// + /// + /// + /// Used to set the _class, part and state that the VisualStyleRenderer object references. + /// These parameters cannot be set individually. + /// This method is present for extensibility. + /// + /// + public void SetParameters(string className, int part, int state) { + if (!IsCombinationDefined(className, part)) { //internally this call takes care of IsSupported. + throw new ArgumentException(SR.GetString(SR.VisualStylesInvalidCombination)); + } + + this._class = className; + this.part = part; + this.state = state; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public void DrawBackground(IDeviceContext dc, Rectangle bounds) { + DrawBackground(dc, bounds, IntPtr.Zero); + } + + internal void DrawBackground(IDeviceContext dc, Rectangle bounds, IntPtr hWnd) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ){ + HandleRef hdc = new HandleRef(wgr, wgr.WindowsGraphics.DeviceContext.Hdc); + if (IntPtr.Zero != hWnd) { + using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) { + lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, new NativeMethods.COMRECT(bounds), null); + } + } + else { + lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, Handle), hdc, part, state, new NativeMethods.COMRECT(bounds), null); + } + } + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public void DrawBackground(IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle) { + DrawBackground(dc, bounds, clipRectangle, IntPtr.Zero); + } + + internal void DrawBackground(IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle, IntPtr hWnd) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + if (clipRectangle.Width < 0 || clipRectangle.Height < 0) { + return; + } + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + if (IntPtr.Zero != hWnd) { + using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) { + lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, new NativeMethods.COMRECT(bounds), new NativeMethods.COMRECT(clipRectangle)); + } + } + else { + lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, Handle), hdc, part, state, new NativeMethods.COMRECT(bounds), new NativeMethods.COMRECT(clipRectangle)); + } + } + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Rectangle DrawEdge(IDeviceContext dc, Rectangle bounds, Edges edges, EdgeStyle style, EdgeEffects effects) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + + if (!ClientUtils.IsEnumValid_Masked(edges, (int)edges,(UInt32)(Edges.Left | Edges.Top | Edges.Right | Edges.Bottom | Edges.Diagonal))) { + throw new InvalidEnumArgumentException("edges", (int)edges, typeof(Edges)); + } + + if (!ClientUtils.IsEnumValid_NotSequential(style, (int)style, (int)EdgeStyle.Raised,(int)EdgeStyle.Sunken,(int)EdgeStyle.Etched,(int)EdgeStyle.Bump )) { + throw new InvalidEnumArgumentException("style", (int)style, typeof(EdgeStyle)); + } + + if (!ClientUtils.IsEnumValid_Masked(effects, (int)effects, (UInt32)(EdgeEffects.FillInterior | EdgeEffects.Flat | EdgeEffects.Soft | EdgeEffects.Mono))) { + throw new InvalidEnumArgumentException("effects", (int)effects, typeof(EdgeEffects)); + } + + NativeMethods.COMRECT rect = new NativeMethods.COMRECT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.DrawThemeEdge( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), (int) style, (int) edges | (int) effects | EdgeAdjust, rect ); + } + + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// This method uses Graphics.DrawImage as a backup if themed drawing does not work. + /// + /// + public void DrawImage(Graphics g, Rectangle bounds, Image image) { + if (g == null) { + throw new ArgumentNullException("g"); + } + + if (image == null) { + throw new ArgumentNullException("image"); + } + + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + + g.DrawImage(image, bounds); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// This method uses Graphics.DrawImage as a backup if themed drawing does not work. + /// + /// + public void DrawImage(Graphics g, Rectangle bounds, ImageList imageList, int imageIndex) { + if (g == null) { + throw new ArgumentNullException("g"); + } + + if (imageList == null) { + throw new ArgumentNullException("imageList"); + } + + if (imageIndex < 0 || imageIndex >= imageList.Images.Count) { + throw new ArgumentOutOfRangeException("imageIndex", SR.GetString(SR.InvalidArgument, "imageIndex", imageIndex.ToString(CultureInfo.CurrentCulture))); + } + + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + + // VSWhidbey #282742: DrawThemeIcon currently seems to do nothing, but still return S_OK. As a workaround, + // we call DrawImage on the graphics object itself for now. A bug has been opened in Windows OS Bugs on this. + + //int returnVal = NativeMethods.S_FALSE; + //using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + // HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + // returnVal = SafeNativeMethods.DrawThemeIcon( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), new HandleRef( this, imageList.Handle ), imageIndex ); + //} + + //if (returnVal != NativeMethods.S_OK) { + g.DrawImage(imageList.Images[imageIndex], bounds); + //} + } + + /// + /// + /// + /// Given a graphics object and bounds to draw in, this method effectively asks the passed in + /// control's parent to draw itself in there (it sends WM_ERASEBKGND & WM_PRINTCLIENT messages + /// to the parent). + /// + /// + public void DrawParentBackground(IDeviceContext dc, Rectangle bounds, Control childControl) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + + if (childControl == null) { + throw new ArgumentNullException("childControl"); + } + + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + + if (childControl.Handle != IntPtr.Zero) { + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.DrawThemeParentBackground( new HandleRef( this, childControl.Handle ), hdc, new NativeMethods.COMRECT( bounds ) ); + } + } + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw) { + DrawText(dc, bounds, textToDraw, false); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled) { + DrawText(dc, bounds, textToDraw, drawDisabled, TextFormatFlags.HorizontalCenter); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled, TextFormatFlags flags) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + if (bounds.Width < 0 || bounds.Height < 0) { + return; + } + + int disableFlag = drawDisabled?0x1:0; + + if (!String.IsNullOrEmpty(textToDraw)) { + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.DrawThemeText( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, disableFlag, new NativeMethods.COMRECT( bounds ) ); + } + } + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Rectangle GetBackgroundContentRectangle(IDeviceContext dc, Rectangle bounds) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + if (bounds.Width < 0 || bounds.Height < 0) { + return Rectangle.Empty; + } + + NativeMethods.COMRECT rect = new NativeMethods.COMRECT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeBackgroundContentRect( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), rect ); + } + + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Rectangle GetBackgroundExtent(IDeviceContext dc, Rectangle contentBounds) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + if (contentBounds.Width < 0 || contentBounds.Height < 0) { + return Rectangle.Empty; + } + + NativeMethods.COMRECT rect = new NativeMethods.COMRECT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeBackgroundExtent( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( contentBounds ), rect ); + } + + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// Computes the region for a regular or partially transparent background that is bounded by a specified + /// rectangle. Return null if the region cannot be created. + /// [See win32 equivalent.] + /// + /// + [SuppressUnmanagedCodeSecurity, + SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + public Region GetBackgroundRegion(IDeviceContext dc, Rectangle bounds) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + if (bounds.Width < 0 || bounds.Height < 0) { + return null; + } + + IntPtr hRegion = IntPtr.Zero; + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeBackgroundRegion( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), ref hRegion ); + } + + // GetThemeBackgroundRegion returns a null hRegion if it fails to create one, it could be because the bounding + // box is too big. For more info see code in %xpsrc%\shell\themes\uxtheme\imagefile.cpp if you have an enlistment to it. + + if (hRegion == IntPtr.Zero) { + return null; + } + + // From the GDI+ sources it doesn't appear as if they take ownership of the hRegion, so this is safe to do. + // We need to DeleteObject in order to not leak. DevDiv Bugs 169791 + Region region = Region.FromHrgn(hRegion); + SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, hRegion)); + return region; + + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public bool GetBoolean(BooleanProperty prop) { + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)BooleanProperty.Transparent, (int)BooleanProperty.SourceShrink)){ + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(BooleanProperty)); + } + + bool val = false; + lastHResult = SafeNativeMethods.GetThemeBool(new HandleRef(this, Handle), part, state, (int)prop, ref val); + return val; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Color GetColor(ColorProperty prop) { + //valid values are 0xed9 to 0xeef + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)ColorProperty.BorderColor, (int)ColorProperty.AccentColorHint)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(ColorProperty)); + } + + int color = 0; + lastHResult = SafeNativeMethods.GetThemeColor(new HandleRef(this, Handle), part, state, (int)prop, ref color); + return ColorTranslator.FromWin32(color); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public int GetEnumValue(EnumProperty prop) { + //valid values are 0xfa1 to 0xfaf + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)EnumProperty.BackgroundType, (int)EnumProperty.TrueSizeScalingType)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(EnumProperty)); + } + + int val = 0; + lastHResult = SafeNativeMethods.GetThemeEnumValue(new HandleRef(this, Handle), part, state, (int)prop, ref val); + return val; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public string GetFilename(FilenameProperty prop) { + //valid values are 0xbb9 to 0xbc0 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)FilenameProperty.ImageFile, (int)FilenameProperty.GlyphImageFile)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(FilenameProperty)); + } + + StringBuilder filename = new StringBuilder(512); + lastHResult = SafeNativeMethods.GetThemeFilename(new HandleRef(this, Handle), part, state, (int)prop, filename, filename.Capacity); + return filename.ToString(); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// Returns null if the returned font was not true type, since GDI+ does not support it. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] + public Font GetFont(IDeviceContext dc, FontProperty prop) + { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + //valid values are 0xa29 to 0xa29 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)FontProperty.GlyphFont, (int)FontProperty.GlyphFont)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(FontProperty)); + } + + NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeFont( new HandleRef( this, Handle ), hdc, part, state, (int) prop, logfont ); + } + + Font font = null; + + //check for a failed HR. + if (NativeMethods.Succeeded(lastHResult)) { + + + // SECREVIEW: Safe to assert here, since the logfont comes from a native api and not from the + // caller of this method. The system creates the font handle. + // + IntSecurity.ObjectFromWin32Handle.Assert(); + try { + font = Font.FromLogFont(logfont); + } + catch (Exception e) { + if (ClientUtils.IsSecurityOrCriticalException(e)) { + throw; + } + + //Looks like the font was not true type + font = null; + } + } + + return font; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public int GetInteger(IntegerProperty prop) { + //valid values are 0x961 to 0x978 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)IntegerProperty.ImageCount, (int)IntegerProperty.MinDpi5)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(IntegerProperty)); + } + + int val = 0; + lastHResult = SafeNativeMethods.GetThemeInt(new HandleRef(this, Handle), part, state, (int)prop, ref val); + return val; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Size GetPartSize(IDeviceContext dc, ThemeSizeType type) { + return GetPartSize(dc, type, IntPtr.Zero); + } + + internal Size GetPartSize(IDeviceContext dc, ThemeSizeType type, IntPtr hWnd) { + if (dc == null) { + throw new ArgumentNullException("dc"); + } + + // valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(type, (int)type, (int)ThemeSizeType.Minimum, (int)ThemeSizeType.Draw)) { + throw new InvalidEnumArgumentException("type", (int)type, typeof(ThemeSizeType)); + } + + NativeMethods.SIZE size = new NativeMethods.SIZE(); + + using (WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties)) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + if (DpiHelper.EnableDpiChangedMessageHandling && (IntPtr.Zero != hWnd)) { + using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) { + lastHResult = SafeNativeMethods.GetThemePartSize(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, null, type, size); + } + } + else { + lastHResult = SafeNativeMethods.GetThemePartSize( new HandleRef(this, Handle), hdc, part, state, null, type, size); + } + } + + return new Size(size.cx, size.cy); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Size GetPartSize(IDeviceContext dc, Rectangle bounds, ThemeSizeType type) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + //valid values are 0x0 to 0x2 + if (!ClientUtils.IsEnumValid(type, (int)type, (int)ThemeSizeType.Minimum, (int)ThemeSizeType.Draw)) + { + throw new InvalidEnumArgumentException("type", (int)type, typeof(ThemeSizeType)); + } + + NativeMethods.SIZE size = new NativeMethods.SIZE(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemePartSize( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), type, size ); + } + + return new Size(size.cx, size.cy); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Point GetPoint(PointProperty prop) { + //valid values are 0xd49 to 0xd50 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)PointProperty.Offset, (int)PointProperty.MinSize5)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(PointProperty)); + } + + NativeMethods.POINT point = new NativeMethods.POINT(); + lastHResult = SafeNativeMethods.GetThemePosition(new HandleRef(this, Handle), part, state, (int)prop, point); + return new Point(point.x, point.y); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Padding GetMargins(IDeviceContext dc, MarginProperty prop) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + //valid values are 0xe11 to 0xe13 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)MarginProperty.SizingMargins, (int)MarginProperty.CaptionMargins)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(MarginProperty)); + } + + NativeMethods.MARGINS margins = new NativeMethods.MARGINS(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeMargins( new HandleRef( this, Handle ), hdc, part, state, (int) prop, ref margins ); + } + + return new Padding(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + } + + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public string GetString(StringProperty prop) { + //valid values are 0xc81 to 0xc81 + if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)StringProperty.Text, (int)StringProperty.Text)) + { + throw new InvalidEnumArgumentException("prop", (int)prop, typeof(StringProperty)); + } + + StringBuilder aString = new StringBuilder(512); + lastHResult = SafeNativeMethods.GetThemeString(new HandleRef(this, Handle), part, state, (int)prop, aString, aString.Capacity); + return aString.ToString(); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Rectangle GetTextExtent(IDeviceContext dc, string textToDraw, TextFormatFlags flags) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + if (String.IsNullOrEmpty(textToDraw)) { + throw new ArgumentNullException("textToDraw"); + } + + NativeMethods.COMRECT rect = new NativeMethods.COMRECT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeTextExtent( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, null, rect ); + } + + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public Rectangle GetTextExtent(IDeviceContext dc, Rectangle bounds, string textToDraw, TextFormatFlags flags) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + if (String.IsNullOrEmpty(textToDraw)) { + throw new ArgumentNullException("textToDraw"); + } + + NativeMethods.COMRECT rect = new NativeMethods.COMRECT(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeTextExtent( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, new NativeMethods.COMRECT( bounds ), rect ); + } + + return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public TextMetrics GetTextMetrics(IDeviceContext dc) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + TextMetrics tm = new TextMetrics(); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.GetThemeTextMetrics( new HandleRef( this, Handle ), hdc, part, state, ref tm ); + } + + return tm; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public HitTestCode HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, Point pt, HitTestOptions options) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + int htCode = 0; + NativeMethods.POINTSTRUCT point = new NativeMethods.POINTSTRUCT(pt.X, pt.Y); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.HitTestThemeBackground( new HandleRef( this, Handle ), hdc, part, state, (int) options, new NativeMethods.COMRECT( backgroundRectangle ), NativeMethods.NullHandleRef, point, ref htCode ); + } + + return (HitTestCode)htCode; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public HitTestCode HitTestBackground(Graphics g, Rectangle backgroundRectangle, Region region, Point pt, HitTestOptions options) { + if (g == null) { + throw new ArgumentNullException("g"); + } + + IntPtr hRgn = region.GetHrgn(g); + + return HitTestBackground(g, backgroundRectangle, hRgn, pt, options); + } + + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + // PM team has reviewed and decided on naming changes already + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public HitTestCode HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, IntPtr hRgn, Point pt, HitTestOptions options) { + if( dc == null ){ + throw new ArgumentNullException("dc"); + } + + int htCode = 0; + NativeMethods.POINTSTRUCT point = new NativeMethods.POINTSTRUCT(pt.X, pt.Y); + + using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) { + HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc ); + lastHResult = SafeNativeMethods.HitTestThemeBackground( new HandleRef( this, Handle ), hdc, part, state, (int) options, new NativeMethods.COMRECT( backgroundRectangle ), new HandleRef( this, hRgn ), point, ref htCode ); + } + + return (HitTestCode)htCode; + } + + /// + /// + /// + /// [See win32 equivalent.] + /// + /// + public bool IsBackgroundPartiallyTransparent() { + return (SafeNativeMethods.IsThemeBackgroundPartiallyTransparent(new HandleRef(this, Handle), part, state)); + } + + /// + /// + /// This is similar to GetLastError in Win32. It returns the last HRESULT returned from a native call + /// into theme apis. We eat the errors and let the user handle any errors that occurred. + /// + public int LastHResult { + get { + return lastHResult; + } + } + + /// + /// + /// + /// Instantiates the ThemeHandle cache hashtable. + /// + /// + private static void CreateThemeHandleHashtable() { + themeHandles = new Hashtable(numberOfPossibleClasses); + } + + /// + /// + /// + /// Handles the ThemeChanged event. Basically, we need to ensure all per-thread theme handle + /// caches are refreshed. + /// + /// + private static void OnUserPreferenceChanging(object sender, UserPreferenceChangingEventArgs ea) { + if (ea.Category == UserPreferenceCategory.VisualStyle) { + // Let all threads know their cached handles are no longer valid; + // cache refresh will happen at next handle access. + // Note that if the theme changes 2^sizeof(long) times before a thread uses + // its handle, this whole version hack won't work, but I don't see that happening. + globalCacheVersion++; + } + } + + /// + /// + /// + /// Refreshes this thread's theme handle cache. + /// + /// + private static void RefreshCache() { + ThemeHandle tHandle = null; + + if (themeHandles != null) { + string[] classNames = new string[themeHandles.Keys.Count]; + themeHandles.Keys.CopyTo(classNames, 0); + + foreach (string className in classNames) { + tHandle = (ThemeHandle) themeHandles[className]; + if (tHandle != null) { + tHandle.Dispose(); + } + + // We don't call IsSupported here, since that could cause RefreshCache to be called again, + // leading to stack overflow. + if (AreClientAreaVisualStylesSupported) { + tHandle = ThemeHandle.Create(className, false); + if (tHandle != null) { + themeHandles[className] = tHandle; + } + } + } + } + } + + private static IntPtr GetHandle(string className) { + return GetHandle(className, true); + } + + /// + /// + /// + /// Retrieves a IntPtr theme handle for the given class from the themeHandle cache. If its not + /// present in the cache, it creates a new ThemeHandle object and stores it there. + /// + /// + private static IntPtr GetHandle(string className, bool throwExceptionOnFail) { + ThemeHandle tHandle; + + if (themeHandles == null) { + CreateThemeHandleHashtable(); + } + + + if (threadCacheVersion != globalCacheVersion) { + RefreshCache(); + threadCacheVersion = globalCacheVersion; + } + + if (!themeHandles.Contains(className)) { // see if it is already in cache + tHandle = ThemeHandle.Create(className, throwExceptionOnFail); + if (tHandle == null) { + return IntPtr.Zero; + } + themeHandles.Add(className, tHandle); + } + else { + tHandle = (ThemeHandle) themeHandles[className]; + } + + return tHandle.NativeHandle; + } + + // This wrapper class is needed for safely cleaning up TLS cache of handles. + private class ThemeHandle : IDisposable { + private IntPtr _hTheme = IntPtr.Zero; + + private ThemeHandle(IntPtr hTheme) { + _hTheme = hTheme; + } + + public IntPtr NativeHandle { + get { + return _hTheme; + } + } + + public static ThemeHandle Create(string className, bool throwExceptionOnFail) { + return Create(className, throwExceptionOnFail, new HandleRef(null, IntPtr.Zero)); + } + + internal static ThemeHandle Create(string className, bool throwExceptionOnFail, HandleRef hWndRef) { + // HThemes require an HWND when display scaling is different between monitors. + IntPtr hTheme = IntPtr.Zero; + + try + { + hTheme = SafeNativeMethods.OpenThemeData(hWndRef, className); + } + catch (Exception e) + { + //We don't want to eat critical exceptions + if (ClientUtils.IsSecurityOrCriticalException(e)) + { + throw; + } + + if (throwExceptionOnFail) + { + throw new InvalidOperationException(SR.GetString(SR.VisualStyleHandleCreationFailed), e); + } + else + { + return null; + } + } + + if (hTheme == IntPtr.Zero) { + if (throwExceptionOnFail) { + throw new InvalidOperationException(SR.GetString(SR.VisualStyleHandleCreationFailed)); + } + else { + return null; + } + } + return new ThemeHandle(hTheme); + } + + public void Dispose() { + if (_hTheme != IntPtr.Zero) { + SafeNativeMethods.CloseThemeData(new HandleRef(null, _hTheme)); + _hTheme = IntPtr.Zero; + } + GC.SuppressFinalize(this); + } + + ~ThemeHandle() { + Dispose(); + } + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleState.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleState.cs new file mode 100644 index 000000000..aab4fced1 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleState.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { + + /// + /// + /// + /// Determines whether visual styles are enabled. + /// + /// + + public enum VisualStyleState { + /// + /// + /// + /// Visual styles are not enabled. + /// + /// + NoneEnabled = 0, + + /// + /// + /// + /// Visual styles enabled only for client area. + /// + /// + ClientAreaEnabled = NativeMethods.STAP_ALLOW_CONTROLS, + + /// + /// + /// + /// Visual styles enabled only for non-client area. + /// + /// + NonClientAreaEnabled = NativeMethods.STAP_ALLOW_NONCLIENT, + + /// + /// + /// + /// Visual styles enabled only for client and non-client areas. + /// + /// + ClientAndNonClientAreasEnabled = NativeMethods.STAP_ALLOW_NONCLIENT | NativeMethods.STAP_ALLOW_CONTROLS + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleStates.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleStates.cs new file mode 100644 index 000000000..e0aaf35d8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleStates.cs @@ -0,0 +1,321 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +// This file contains the state enums used by the control renderer classes. + +using System.Diagnostics.CodeAnalysis; +[assembly: SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum ComboBoxState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3, + /// + Disabled = 4 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum CheckBoxState { + /// + UncheckedNormal = 1, + /// + UncheckedHot = 2, + /// + UncheckedPressed = 3, + /// + UncheckedDisabled = 4, + /// + CheckedNormal = 5, + /// + CheckedHot = 6, + /// + CheckedPressed = 7, + /// + CheckedDisabled = 8, + /// + MixedNormal = 9, + /// + MixedHot = 10, + /// + MixedPressed = 11, + /// + MixedDisabled = 12 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum GroupBoxState { + /// + Normal = 1, + /// + Disabled = 2 + } + + // Used by some DataGridView classes. + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + internal enum HeaderItemState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum PushButtonState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3, + /// + Disabled = 4, + /// + Default = 5 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum RadioButtonState { + /// + UncheckedNormal = 1, + /// + UncheckedHot = 2, + /// + UncheckedPressed = 3, + /// + UncheckedDisabled = 4, + /// + CheckedNormal = 5, + /// + CheckedHot = 6, + /// + CheckedPressed = 7, + /// + CheckedDisabled = 8 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum ScrollBarArrowButtonState { + /// + UpNormal = 1, + /// + UpHot = 2, + /// + UpPressed = 3, + /// + UpDisabled = 4, + /// + DownNormal = 5, + /// + DownHot = 6, + /// + DownPressed = 7, + /// + DownDisabled = 8, + /// + LeftNormal = 9, + /// + LeftHot = 10, + /// + LeftPressed = 11, + /// + LeftDisabled = 12, + /// + RightNormal = 13, + /// + RightHot = 14, + /// + RightPressed = 15, + /// + RightDisabled = 16 + } + + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum ScrollBarState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3, + /// + Disabled = 4, + + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum ScrollBarSizeBoxState { + /// + RightAlign = 1, + /// + LeftAlign = 2 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum TabItemState { + /// + Normal = 1, + /// + Hot = 2, + /// + Selected = 3, + /// + Disabled = 4, + //Focused = 5 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue"), + SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags") + ] + public enum TextBoxState { + /// + Normal = 1, + /// + Hot = 2, + /// + Selected = 3, + /// + Disabled = 4, + //Focused = 5, + /// + Readonly = 6, + /// + Assist = 7 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum ToolBarState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3, + /// + Disabled = 4, + /// + Checked = 5, + /// + HotChecked = 6 + } + + /// + /// + /// + /// [To be supplied] + /// + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") + ] + public enum TrackBarThumbState { + /// + Normal = 1, + /// + Hot = 2, + /// + Pressed = 3, + //Focused = 4, + /// + Disabled = 5 + } +} diff --git a/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleTypesAndProperties.cs b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleTypesAndProperties.cs new file mode 100644 index 000000000..4e0051ab7 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/VisualStyles/VisualStyleTypesAndProperties.cs @@ -0,0 +1,1210 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +// This file contains the enums defining various ThemeData Types and Properties. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")] + +namespace System.Windows.Forms.VisualStyles { +using System.Diagnostics.CodeAnalysis; + + /// + public enum BackgroundType + { + /// + ImageFile = 0, + /// + BorderFill = 1, + /// + None = 2, + // TM_ENUM(0, BT, IMAGEFILE) + // TM_ENUM(1, BT, BORDERFILL) + // TM_ENUM(2, BT, NONE) + } + + /// + public enum BorderType + { + /// + Rectangle = 0, + /// + RoundedRectangle = 1, + /// + Ellipse = 2, + // TM_ENUM(0, BT, RECT) + // TM_ENUM(1, BT, ROUNDRECT) + // TM_ENUM(2, BT, ELLIPSE) + } + + /// + public enum ImageOrientation + { + /// + Vertical = 0, + /// + Horizontal = 1, + // TM_ENUM(0, IL, VERTICAL) + // TM_ENUM(1, IL, HORIZONTAL) + } + + /// + public enum SizingType + { + /// + FixedSize = 0, + /// + Stretch = 1, + /// + Tile = 2, + // TM_ENUM(0, ST, TRUESIZE) + // TM_ENUM(1, ST, STRETCH) + // TM_ENUM(2, ST, TILE) + } + + /// + public enum FillType + { + /// + Solid = 0, + /// + VerticalGradient = 1, + /// + HorizontalGradient = 2, + /// + RadialGradient = 3, + /// + TileImage = 4, + // TM_ENUM(0, FT, SOLID) + // TM_ENUM(1, FT, VERTGRADIENT) + // TM_ENUM(2, FT, HORZGRADIENT) + // TM_ENUM(3, FT, RADIALGRADIENT) + // TM_ENUM(4, FT, TILEIMAGE) + } + + /// + public enum HorizontalAlign + { + /// + Left = 0, + /// + Center = 1, + /// + Right = 2, + // TM_ENUM(0, HA, LEFT) + // TM_ENUM(1, HA, CENTER) + // TM_ENUM(2, HA, RIGHT) + } + + /// + public enum ContentAlignment + { + /// + Left = 0, + /// + Center = 1, + /// + Right = 2, + // TM_ENUM(0, CA, LEFT) + // TM_ENUM(1, CA, CENTER) + // TM_ENUM(2, CA, RIGHT) + } + + /// + public enum VerticalAlignment + { + /// + Top = 0, + /// + Center = 1, + /// + Bottom = 2, + // TM_ENUM(0, VA, TOP) + // TM_ENUM(1, VA, CENTER) + // TM_ENUM(2, VA, BOTTOM) + } + + /// + public enum OffsetType + { + /// + TopLeft = 0, + /// + TopRight = 1, + /// + TopMiddle = 2, + /// + BottomLeft = 3, + /// + BottomRight = 4, + /// + BottomMiddle = 5, + /// + MiddleLeft = 6, + /// + MiddleRight = 7, + /// + LeftOfCaption = 8, + /// + RightOfCaption = 9, + /// + LeftOfLastButton = 10, + /// + RightOfLastButton = 11, + /// + AboveLastButton = 12, + /// + BelowLastButton = 13, + // TM_ENUM(0, OT, TOPLEFT) + // TM_ENUM(1, OT, TOPRIGHT) + // TM_ENUM(2, OT, TOPMIDDLE) + // TM_ENUM(3, OT, BOTTOMLEFT) + // TM_ENUM(4, OT, BOTTOMRIGHT) + // TM_ENUM(5, OT, BOTTOMMIDDLE) + // TM_ENUM(6, OT, MIDDLELEFT) + // TM_ENUM(7, OT, MIDDLERIGHT) + // TM_ENUM(8, OT, LEFTOFCAPTION) + // TM_ENUM(9, OT, RIGHTOFCAPTION) + // TM_ENUM(10, OT, LEFTOFLASTBUTTON) + // TM_ENUM(11, OT, RIGHTOFLASTBUTTON) + // TM_ENUM(12, OT, ABOVELASTBUTTON) + // TM_ENUM(13, OT, BELOWLASTBUTTON) + } + + /// + public enum IconEffect + { + /// + None = 0, + /// + Glow = 1, + /// + Shadow = 2, + /// + Pulse = 3, + /// + Alpha = 4, + // TM_ENUM(0, ICE, NONE) + // TM_ENUM(1, ICE, GLOW) + // TM_ENUM(2, ICE, SHADOW) + // TM_ENUM(3, ICE, PULSE) + // TM_ENUM(4, ICE, ALPHA) + } + + /// + public enum TextShadowType + { + /// + None = 0, + /// + Single = 1, + /// + Continuous = 2, + // TM_ENUM(0, TST, NONE) + // TM_ENUM(1, TST, SINGLE) + // TM_ENUM(2, TST, CONTINUOUS) + } + + /// + public enum GlyphType + { + /// + None = 0, + /// + ImageGlyph = 1, + /// + FontGlyph = 2, + // TM_ENUM(0, GT, NONE) + // TM_ENUM(1, GT, IMAGEGLYPH) + // TM_ENUM(2, GT, FONTGLYPH) + } + + /// + public enum ImageSelectType + { + /// + None = 0, + /// + Size = 1, + /// + Dpi = 2, + // TM_ENUM(0, IST, NONE) + // TM_ENUM(1, IST, SIZE) + // TM_ENUM(2, IST, DPI) + } + + /// + public enum TrueSizeScalingType + { + /// + None = 0, + /// + Size = 1, + /// + Dpi = 2, + // TM_ENUM(0, TSST, NONE) + // TM_ENUM(1, TSST, SIZE) + // TM_ENUM(2, TSST, DPI) + } + + /// + public enum GlyphFontSizingType + { + /// + None = 0, + /// + Size = 1, + /// + Dpi = 2, + // TM_ENUM(0, GFST, NONE) + // TM_ENUM(1, GFST, SIZE) + // TM_ENUM(2, GFST, DPI) + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum ColorProperty + { + /// + BorderColor = 3801, + /// + FillColor = 3802, + /// + TextColor = 3803, + /// + EdgeLightColor = 3804, + /// + EdgeHighlightColor = 3805, + /// + EdgeShadowColor = 3806, + /// + EdgeDarkShadowColor = 3807, + /// + EdgeFillColor = 3808, + /// + TransparentColor = 3809, + /// + GradientColor1 = 3810, + /// + GradientColor2 = 3811, + /// + GradientColor3 = 3812, + /// + GradientColor4 = 3813, + /// + GradientColor5 = 3814, + /// + ShadowColor = 3815, + /// + GlowColor = 3816, + /// + TextBorderColor = 3817, + /// + TextShadowColor = 3818, + /// + GlyphTextColor = 3819, + /// + GlyphTransparentColor = 3820, + /// + FillColorHint = 3821, + /// + BorderColorHint = 3822, + /// + AccentColorHint = 3823 + // TM_PROP(3801, TMT, BORDERCOLOR, COLOR) // color of borders for BorderFill + // TM_PROP(3802, TMT, FILLCOLOR, COLOR) // color of bg fill + // TM_PROP(3803, TMT, TEXTCOLOR, COLOR) // color text is drawn in + // TM_PROP(3804, TMT, EDGELIGHTCOLOR, COLOR) // edge color + // TM_PROP(3805, TMT, EDGEHIGHLIGHTCOLOR, COLOR) // edge color + // TM_PROP(3806, TMT, EDGESHADOWCOLOR, COLOR) // edge color + // TM_PROP(3807, TMT, EDGEDKSHADOWCOLOR, COLOR) // edge color + // TM_PROP(3808, TMT, EDGEFILLCOLOR, COLOR) // edge color + // TM_PROP(3809, TMT, TRANSPARENTCOLOR, COLOR) // color of pixels that are treated as transparent (not drawn) + // TM_PROP(3810, TMT, GRADIENTCOLOR1, COLOR) // first color in gradient + // TM_PROP(3811, TMT, GRADIENTCOLOR2, COLOR) // second color in gradient + // TM_PROP(3812, TMT, GRADIENTCOLOR3, COLOR) // third color in gradient + // TM_PROP(3813, TMT, GRADIENTCOLOR4, COLOR) // forth color in gradient + // TM_PROP(3814, TMT, GRADIENTCOLOR5, COLOR) // fifth color in gradient + // TM_PROP(3815, TMT, SHADOWCOLOR, COLOR) // color of text shadow + // TM_PROP(3816, TMT, GLOWCOLOR, COLOR) // color of glow produced by DrawThemeIcon + // TM_PROP(3817, TMT, TEXTBORDERCOLOR, COLOR) // color of text border + // TM_PROP(3818, TMT, TEXTSHADOWCOLOR, COLOR) // color of text shadow + // TM_PROP(3819, TMT, GLYPHTEXTCOLOR, COLOR) // color that font-based glyph is drawn with + // TM_PROP(3820, TMT, GLYPHTRANSPARENTCOLOR, COLOR) // color of transparent pixels in GlyphImageFile + // TM_PROP(3821, TMT, FILLCOLORHINT, COLOR) // hint about fill color used (for custom controls) + // TM_PROP(3822, TMT, BORDERCOLORHINT, COLOR) // hint about border color used (for custom controls) + // TM_PROP(3823, TMT, ACCENTCOLORHINT, COLOR) // hint about accent color used (for custom controls) + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // EnumProperty maps to native enum. + ] + public enum EnumProperty + { + /// + BackgroundType = 4001, + /// + BorderType = 4002, + /// + FillType = 4003, + /// + SizingType = 4004, + /// + HorizontalAlignment = 4005, + /// + ContentAlignment = 4006, + /// + VerticalAlignment = 4007, + /// + OffsetType = 4008, + /// + IconEffect = 4009, + /// + TextShadowType = 4010, + /// + ImageLayout = 4011, + /// + GlyphType = 4012, + /// + ImageSelectType = 4013, + /// + GlyphFontSizingType = 4014, + /// + TrueSizeScalingType = 4015 + // TM_PROP(4001, TMT, BGTYPE, ENUM) // basic drawing type for each part + // TM_PROP(4002, TMT, BORDERTYPE, ENUM) // type of border for BorderFill parts + // TM_PROP(4003, TMT, FILLTYPE, ENUM) // fill shape for BorderFill parts + // TM_PROP(4004, TMT, SIZINGTYPE, ENUM) // how to size ImageFile parts + // TM_PROP(4005, TMT, HALIGN, ENUM) // horizontal alignment for TRUESIZE parts & glyphs + // TM_PROP(4006, TMT, CONTENTALIGNMENT, ENUM) // custom window prop: how text is aligned in caption + // TM_PROP(4007, TMT, VALIGN, ENUM) // horizontal alignment for TRUESIZE parts & glyphs + // TM_PROP(4008, TMT, OFFSETTYPE, ENUM) // how window part should be placed + // TM_PROP(4009, TMT, ICONEFFECT, ENUM) // type of effect to use with DrawThemeIcon + // TM_PROP(4010, TMT, TEXTSHADOWTYPE, ENUM) // type of shadow to draw with text + // TM_PROP(4011, TMT, IMAGELAYOUT, ENUM) // how multiple images are arranged (horz. or vert.) + // TM_PROP(4012, TMT, GLYPHTYPE, ENUM) // controls type of glyph in imagefile objects + // TM_PROP(4013, TMT, IMAGESELECTTYPE, ENUM) // controls when to select from IMAGEFILE1...IMAGEFILE5 + // TM_PROP(4014, TMT, GLYPHFONTSIZINGTYPE, ENUM) // controls when to select a bigger/small glyph font size + // TM_PROP(4015, TMT, TRUESIZESCALINGTYPE, ENUM) // controls how TrueSize image is scaled + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum FilenameProperty + { + /// + ImageFile = 3001, + /// + ImageFile1 = 3002, + /// + ImageFile2 = 3003, + /// + ImageFile3 = 3004, + /// + ImageFile4 = 3005, + /// + ImageFile5 = 3006, + /// + StockImageFile = 3007, + /// + GlyphImageFile = 3008 + // TM_PROP(3001, TMT, IMAGEFILE, FILENAME) // the filename of the image (or basename, for mult. images) + // TM_PROP(3002, TMT, IMAGEFILE1, FILENAME) // multiresolution image file + // TM_PROP(3003, TMT, IMAGEFILE2, FILENAME) // multiresolution image file + // TM_PROP(3004, TMT, IMAGEFILE3, FILENAME) // multiresolution image file + // TM_PROP(3005, TMT, IMAGEFILE4, FILENAME) // multiresolution image file + // TM_PROP(3006, TMT, IMAGEFILE5, FILENAME) // multiresolution image file + // TM_PROP(3007, TMT, STOCKIMAGEFILE, FILENAME) // These are the only images that you can call GetThemeBitmap on + // TM_PROP(3008, TMT, GLYPHIMAGEFILE, FILENAME) // the filename for the glyph image + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum FontProperty + { + /// + GlyphFont = 2601 + // TM_PROP(2601, TMT, GLYPHFONT, FONT) // the font that the glyph is drawn with + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum IntegerProperty + { + /// + ImageCount = 2401, + /// + AlphaLevel = 2402, + /// + BorderSize = 2403, + /// + RoundCornerWidth = 2404, + /// + RoundCornerHeight = 2405, + /// + GradientRatio1 = 2406, + /// + GradientRatio2 = 2407, + /// + GradientRatio3 = 2408, + /// + GradientRatio4 = 2409, + /// + GradientRatio5 = 2410, + /// + ProgressChunkSize = 2411, + /// + ProgressSpaceSize = 2412, + /// + Saturation = 2413, + /// + TextBorderSize = 2414, + /// + AlphaThreshold = 2415, + /// + Width = 2416, + /// + Height = 2417, + /// + GlyphIndex = 2418, + /// + TrueSizeStretchMark = 2419, + /// + MinDpi1 = 2420, + /// + MinDpi2 = 2421, + /// + MinDpi3 = 2422, + /// + MinDpi4 = 2423, + /// + MinDpi5 = 2424 + // TM_PROP(2401, TMT, IMAGECOUNT, INT) // the number of state images in an imagefile + // TM_PROP(2402, TMT, ALPHALEVEL, INT) // (0-255) alpha value for an icon (DrawThemeIcon part) + // TM_PROP(2403, TMT, BORDERSIZE, INT) // the size of the border line for bgtype=BorderFill + // TM_PROP(2404, TMT, ROUNDCORNERWIDTH, INT) // (0-100) % of roundness for rounded rects + // TM_PROP(2405, TMT, ROUNDCORNERHEIGHT, INT) // (0-100) % of roundness for rounded rects + // TM_PROP(2406, TMT, GRADIENTRATIO1, INT) // (0-255) - amt of gradient color 1 to use (all must total=255) + // TM_PROP(2407, TMT, GRADIENTRATIO2, INT) // (0-255) - amt of gradient color 2 to use (all must total=255) + // TM_PROP(2408, TMT, GRADIENTRATIO3, INT) // (0-255) - amt of gradient color 3 to use (all must total=255) + // TM_PROP(2409, TMT, GRADIENTRATIO4, INT) // (0-255) - amt of gradient color 4 to use (all must total=255) + // TM_PROP(2410, TMT, GRADIENTRATIO5, INT) // (0-255) - amt of gradient color 5 to use (all must total=255) + // TM_PROP(2411, TMT, PROGRESSCHUNKSIZE, INT) // size of progress control chunks + // TM_PROP(2412, TMT, PROGRESSSPACESIZE, INT) // size of progress control spaces + // TM_PROP(2413, TMT, SATURATION, INT) // (0-255) amt of saturation for DrawThemeIcon() part + // TM_PROP(2414, TMT, TEXTBORDERSIZE, INT) // size of border around text chars + // TM_PROP(2415, TMT, ALPHATHRESHOLD, INT) // (0-255) the min. alpha value of a pixel that is solid + // TM_PROP(2416, TMT, WIDTH, SIZE) // custom window prop: size of part (min. window) + // TM_PROP(2417, TMT, HEIGHT, SIZE) // custom window prop: size of part (min. window) + // TM_PROP(2418, TMT, GLYPHINDEX, INT) // for font-based glyphs, the char index into the font + // TM_PROP(2419, TMT, TRUESIZESTRETCHMARK, INT) // stretch TrueSize image when target exceeds source by this percent + // TM_PROP(2420, TMT, MINDPI1, INT) // min DPI ImageFile1 was designed for + // TM_PROP(2421, TMT, MINDPI2, INT) // min DPI ImageFile1 was designed for + // TM_PROP(2422, TMT, MINDPI3, INT) // min DPI ImageFile1 was designed for + // TM_PROP(2423, TMT, MINDPI4, INT) // min DPI ImageFile1 was designed for + // TM_PROP(2424, TMT, MINDPI5, INT) // min DPI ImageFile1 was designed for + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum PointProperty + { + /// + Offset = 3401, + /// + TextShadowOffset = 3402, + /// + MinSize = 3403, + /// + MinSize1 = 3404, + /// + MinSize2 = 3405, + /// + MinSize3 = 3406, + /// + MinSize4 = 3407, + /// + MinSize5 = 3408 + // TM_PROP(3401, TMT, OFFSET, POSITION) // for window part layout + // TM_PROP(3402, TMT, TEXTSHADOWOFFSET, POSITION) // where char shadows are drawn, relative to orig. chars + // TM_PROP(3403, TMT, MINSIZE, POSITION) // min dest rect than ImageFile was designed for + // TM_PROP(3404, TMT, MINSIZE1, POSITION) // min dest rect than ImageFile1 was designed for + // TM_PROP(3405, TMT, MINSIZE2, POSITION) // min dest rect than ImageFile2 was designed for + // TM_PROP(3406, TMT, MINSIZE3, POSITION) // min dest rect than ImageFile3 was designed for + // TM_PROP(3407, TMT, MINSIZE4, POSITION) // min dest rect than ImageFile4 was designed for + // TM_PROP(3408, TMT, MINSIZE5, POSITION) // min dest rect than ImageFile5 was designed for + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum MarginProperty + { + /// + SizingMargins = 3601, + /// + ContentMargins = 3602, + /// + CaptionMargins = 3603 + // TM_PROP(3601, TMT, SIZINGMARGINS, MARGINS) // margins used for 9-grid sizing + // TM_PROP(3602, TMT, CONTENTMARGINS, MARGINS) // margins that define where content can be placed + // TM_PROP(3603, TMT, CAPTIONMARGINS, MARGINS) // margins that define where caption text can be placed + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum StringProperty + { + /// + Text = 3201 + //TM_PROP(3201, TMT, TEXT, STRING) + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum BooleanProperty + { + /// + Transparent = 2201, + /// + AutoSize = 2202, + /// + BorderOnly = 2203, + /// + Composited = 2204, + /// + BackgroundFill = 2205, + /// + GlyphTransparent = 2206, + /// + GlyphOnly = 2207, + /// + AlwaysShowSizingBar = 2208, + /// + MirrorImage = 2209, + /// + UniformSizing = 2210, + /// + IntegralSizing = 2211, + /// + SourceGrow = 2212, + /// + SourceShrink = 2213 + + // TM_PROP(2201, TMT, TRANSPARENT, BOOL) // image has transparent areas (see TransparentColor) + // TM_PROP(2202, TMT, AUTOSIZE, BOOL) // if TRUE, nonclient caption width varies with text extent + // TM_PROP(2203, TMT, BORDERONLY, BOOL) // only draw the border area of the image + // TM_PROP(2204, TMT, COMPOSITED, BOOL) // control will handle the composite drawing + // TM_PROP(2205, TMT, BGFILL, BOOL) // if TRUE, TRUESIZE images should be drawn on bg fill + // TM_PROP(2206, TMT, GLYPHTRANSPARENT, BOOL) // glyph has transparent areas (see GlyphTransparentColor) + // TM_PROP(2207, TMT, GLYPHONLY, BOOL) // only draw glyph (not background) + // TM_PROP(2208, TMT, ALWAYSSHOWSIZINGBAR, BOOL) + // TM_PROP(2209, TMT, MIRRORIMAGE, BOOL) // default=TRUE means image gets mirrored in RTL (Mirror) windows + // TM_PROP(2210, TMT, UNIFORMSIZING, BOOL) // if TRUE, height & width must be uniformly sized + // TM_PROP(2211, TMT, INTEGRALSIZING, BOOL) // for TRUESIZE and Border sizing; if TRUE, factor must be integer + // TM_PROP(2212, TMT, SOURCEGROW, BOOL) // if TRUE, will scale up src image when needed + // TM_PROP(2213, TMT, SOURCESHRINK, BOOL) // if TRUE, will scale down src image when needed + } + + // Some other misc enums + + /// + [Flags] + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum Edges + { + /// + Left = 0x0001, + /// + Top = 0x0002, + /// + Right = 0x0004, + /// + Bottom = 0x0008, + /// + Diagonal = 0x0010, + + // #define BF_LEFT 0x0001 + // #define BF_TOP 0x0002 + // #define BF_RIGHT 0x0004 + // #define BF_BOTTOM 0x0008 + // + // #define BF_TOPLEFT (BF_TOP | BF_LEFT) + // #define BF_TOPRIGHT (BF_TOP | BF_RIGHT) + // #define BF_BOTTOMLEFT (BF_BOTTOM | BF_LEFT) + // #define BF_BOTTOMRIGHT (BF_BOTTOM | BF_RIGHT) + // #define BF_RECT (BF_LEFT | BF_TOP | BF_RIGHT | BF_BOTTOM) + // + // #define BF_DIAGONAL 0x0010 + + // // For diagonal lines, the BF_RECT flags specify the end point of the + // // vector bounded by the rectangle parameter. + // #define BF_DIAGONAL_ENDTOPRIGHT (BF_DIAGONAL | BF_TOP | BF_RIGHT) + // #define BF_DIAGONAL_ENDTOPLEFT (BF_DIAGONAL | BF_TOP | BF_LEFT) + // #define BF_DIAGONAL_ENDBOTTOMLEFT (BF_DIAGONAL | BF_BOTTOM | BF_LEFT) + // #define BF_DIAGONAL_ENDBOTTOMRIGHT (BF_DIAGONAL | BF_BOTTOM | BF_RIGHT) + } + + /// + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum EdgeStyle + { + /// + Raised = 0x0001 | 0x0004, + /// + Sunken = 0x0002 | 0x0008, + /// + Etched = 0x0002 | 0x0004, + /// + Bump = 0x0001 | 0x0008 + + // #define BDR_RAISEDOUTER 0x0001 + // #define BDR_SUNKENOUTER 0x0002 + // #define BDR_RAISEDINNER 0x0004 + // #define BDR_SUNKENINNER 0x0008 + // #define EDGE_RAISED (BDR_RAISEDOUTER | BDR_RAISEDINNER) + // #define EDGE_SUNKEN (BDR_SUNKENOUTER | BDR_SUNKENINNER) + // #define EDGE_ETCHED (BDR_SUNKENOUTER | BDR_RAISEDINNER) + // #define EDGE_BUMP (BDR_RAISEDOUTER | BDR_SUNKENINNER) + } + + /// + [Flags] + public enum EdgeEffects + { + /// + None = 0, + /// + FillInterior = 0x0800, + /// + Flat = 0x1000, + /// + Soft = 0x4000, + /// + Mono = 0x8000, + // #define BF_SOFT 0x1000 /* For softer buttons */ + // #define BF_FLAT 0x4000 /* For flat rather than 3D borders */ + // #define BF_MONO 0x8000 /* For monochrome borders */ + } + + /// + public struct TextMetrics + { + + private int height; + private int ascent; + private int descent; + private int internalLeading; + private int externalLeading; + private int aveCharWidth; + private int maxCharWidth; + private int weight; + private int overhang; + private int digitizedAspectX; + private int digitizedAspectY; + private char firstChar; + private char lastChar; + private char defaultChar; + private char breakChar; + private bool italic; + private bool underlined; + private bool struckOut; + private TextMetricsPitchAndFamilyValues pitchAndFamily; + private TextMetricsCharacterSet charSet; + + /// + public int Height + { + get + { + return height; + } set + { + height = value; + } + } + + /// + public int Ascent + { + get + { + return ascent; + } + set + { + ascent = value; + } + } + /// + public int Descent + { + get + { + return descent; + } + set + { + descent = value; + } + } + + /// + public int InternalLeading + { + get + { + return internalLeading; + } + set + { + internalLeading = value; + } + } + + /// + public int ExternalLeading + { + get + { + return externalLeading; + } + set + { + externalLeading = value; + } + } + + /// + public int AverageCharWidth + { + get + { + return aveCharWidth; + } + set + { + aveCharWidth = value; + } + } + + /// + public int MaxCharWidth + { + get + { + return maxCharWidth; + } + set + { + maxCharWidth = value; + } + } + + /// + public int Weight + { + get + { + return weight; + } + set + { + weight = value; + } + } + + /// + public int Overhang + { + get + { + return overhang; + } + set + { + overhang = value; + } + } + + /// + public int DigitizedAspectX + { + get + { + return digitizedAspectX; + } + set + { + digitizedAspectX = value; + } + } + + /// + public int DigitizedAspectY + { + get + { + return digitizedAspectY; + } + set + { + digitizedAspectY = value; + } + } + + /// + public char FirstChar + { + get + { + return firstChar; + } + set + { + firstChar = value; + } + } + + /// + public char LastChar + { + get + { + return lastChar; + } + set + { + lastChar = value; + } + } + + /// + public char DefaultChar + { + get + { + return defaultChar; + } + set + { + defaultChar = value; + } + } + + /// + public char BreakChar + { + get + { + return breakChar; + } + set + { + breakChar = value; + } + } + + /// + public bool Italic + { + get + { + return italic; + } + set + { + italic = value; + } + } + + /// + public bool Underlined + { + get + { + return underlined; + } + set + { + underlined = value; + } + } + + /// + public bool StruckOut + { + get + { + return struckOut; + } + set + { + struckOut = value; + } + } + + /// + public TextMetricsPitchAndFamilyValues PitchAndFamily + { + get + { + return pitchAndFamily; + } + set + { + pitchAndFamily = value; + } + } + + /// + public TextMetricsCharacterSet CharSet + { + get + { + return charSet; + } + set + { + charSet = value; + } + } + } + + /// + [Flags] + public enum TextMetricsPitchAndFamilyValues + { + /// + FixedPitch = 0x01, + /// + Vector = 0x02, + /// + TrueType = 0x04, + /// + Device = 0x08 + + // #define TMPF_FIXED_PITCH 0x01 + // #define TMPF_VECTOR 0x02 + // #define TMPF_DEVICE 0x08 + // #define TMPF_TRUETYPE 0x04 + } + + /// + public enum TextMetricsCharacterSet + { + /// + Ansi = 0, + /// + Baltic = 186, + /// + ChineseBig5 = 136, + /// + Default = 1, + /// + EastEurope = 238, + /// + Gb2312 = 134, + /// + Greek = 161, + /// + Hangul = 129, + /// + Mac = 77, + /// + Oem = 255, + /// + Russian = 204, + /// + ShiftJis = 128, + /// + Symbol = 2, + /// + Turkish = 162, + /// + Vietnamese = 163, + /// + Johab = 130, + /// + Arabic = 178, + /// + Hebrew = 177, + /// + Thai = 222, + // #define ANSI_CHARSET 0 + // #define BALTIC_CHARSET 186 + // #define CHINESEBIG5_CHARSET 136 + // #define DEFAULT_CHARSET 1 + // #define EASTEUROPE_CHARSET 238 + // #define GB2312_CHARSET 134 + // #define GREEK_CHARSET 161 + // #define HANGUL_CHARSET 129 + // #define MAC_CHARSET 77 + // #define OEM_CHARSET 255 + // #define RUSSIAN_CHARSET 204 + // #define SHIFTJIS_CHARSET 128 + // #define SYMBOL_CHARSET 2 + // #define TURKISH_CHARSET 162 + // #define VIETNAMESE_CHARSET 163 + + // Korean + // #define JOHAB_CHARSET 130 + + // Middle East + // #define ARABIC_CHARSET 178 + // #define HEBREW_CHARSET 177 + + // Thai + // #define THAI_CHARSET 222 + } + + /// + [Flags] + [ + SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue") // Maps to native enum. + ] + public enum HitTestOptions + { + /// + BackgroundSegment = 0x0000, + /// + FixedBorder = 0x0002, + /// + Caption = 0x0004, + /// + ResizingBorderLeft = 0x0010, + /// + ResizingBorderTop = 0x0020, + /// + ResizingBorderRight = 0x0040, + /// + ResizingBorderBottom = 0x0080, + /// + ResizingBorder = ResizingBorderLeft | ResizingBorderTop | ResizingBorderRight | ResizingBorderBottom, + /// + SizingTemplate = 0x0100, + /// + SystemSizingMargins = 0x0200 + + // Theme background segment hit test flag (default). possible return values are: + // HTCLIENT: hit test succeeded in the middle background segment + // HTTOP, HTLEFT, HTTOPLEFT, etc: // hit test succeeded in the the respective theme background segment. + //#define HTTB_BACKGROUNDSEG 0x0000 + + // Fixed border hit test option. possible return values are: + // HTCLIENT: hit test succeeded in the middle background segment + // HTBORDER: hit test succeeded in any other background segment + //#define HTTB_FIXEDBORDER 0x0002 // Return code may be either HTCLIENT or HTBORDER. + + // Caption hit test option. Possible return values are: + // HTCAPTION: hit test succeeded in the top, top left, or top right background segments + // HTNOWHERE or another return code, depending on absence or presence of accompanying flags, resp. + //#define HTTB_CAPTION 0x0004 + + // Resizing border hit test flags. Possible return values are: + // HTCLIENT: hit test succeeded in middle background segment + // HTTOP, HTTOPLEFT, HTLEFT, HTRIGHT, etc: hit test succeeded in the respective system resizing zone + // HTBORDER: hit test failed in middle segment and resizing zones, but succeeded in a background border segment + //#define HTTB_RESIZINGBORDER_LEFT 0x0010 // Hit test left resizing border, + //#define HTTB_RESIZINGBORDER_TOP 0x0020 // Hit test top resizing border + //#define HTTB_RESIZINGBORDER_RIGHT 0x0040 // Hit test right resizing border + //#define HTTB_RESIZINGBORDER_BOTTOM 0x0080 // Hit test bottom resizing border + + //#define HTTB_RESIZINGBORDER (HTTB_RESIZINGBORDER_LEFT|HTTB_RESIZINGBORDER_TOP|\ + // HTTB_RESIZINGBORDER_RIGHT|HTTB_RESIZINGBORDER_BOTTOM) + + // Resizing border is specified as a template, not just window edges. + // This option is mutually exclusive with HTTB_SYSTEMSIZINGWIDTH; HTTB_SIZINGTEMPLATE takes precedence + //#define HTTB_SIZINGTEMPLATE 0x0100 + + // Use system resizing border width rather than theme content margins. + // This option is mutually exclusive with HTTB_SIZINGTEMPLATE, which takes precedence. + //#define HTTB_SYSTEMSIZINGMARGINS 0x0200 + } + + /// + public enum HitTestCode + { + /// + Nowhere = 0, + /// + Client = 1, + /// + Left = 10, + /// + Right = 11, + /// + Top = 12, + /// + Bottom = 15, + /// + TopLeft = 13, + /// + TopRight = 14, + /// + BottomLeft = 16, + /// + BottomRight = 17 + // #define HTNOWHERE 0 + // #define HTCLIENT 1 + // #define HTLEFT 10 + // #define HTRIGHT 11 + // #define HTTOP 12 + // #define HTTOPLEFT 13 + // #define HTTOPRIGHT 14 + // #define HTBOTTOM 15 + // #define HTBOTTOMLEFT 16 + // #define HTBOTTOMRIGHT 17 + + } + + /// + public enum ThemeSizeType { + /// + Minimum = 0, + /// + True = 1, + /// + Draw = 2 + } + + // Internal enums for VisualStyleInformation + + internal struct VisualStyleDocProperty + { + internal static string DisplayName = "DisplayName"; + internal static string Company = "Company"; + internal static string Author = "Author"; + internal static string Copyright = "Copyright"; + internal static string Url = "Url"; + internal static string Version = "Version"; + internal static string Description = "Description"; + } + + internal struct VisualStyleSystemProperty + { + internal static int SupportsFlatMenus = 1001; + internal static int MinimumColorDepth = 1301; + } +} diff --git a/WindowsForms/Managed/System/WinForms/WebBrowser.cs b/WindowsForms/Managed/System/WinForms/WebBrowser.cs new file mode 100644 index 000000000..b9761ea71 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowser.cs @@ -0,0 +1,2103 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Printing; +using System.Windows.Forms; +using System.Security.Permissions; +using System.Security; +using System.Runtime.InteropServices; +using System.Net; +using System.Text; + +using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + +namespace System.Windows.Forms { + /// + /// + /// + /// This is a wrapper over the native WebBrowser control implemented in shdocvw.dll. + /// + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust"), + PermissionSetAttribute(SecurityAction.InheritanceDemand, Name="FullTrust"), + DefaultProperty("Url"), DefaultEvent("DocumentCompleted"), + Docking(DockingBehavior.AutoDock), + SRDescription(SR.DescriptionWebBrowser), + Designer("System.Windows.Forms.Design.WebBrowserDesigner, " + AssemblyRef.SystemDesign)] + public class WebBrowser : WebBrowserBase { + private static bool createdInIE; + + // Reference to the native ActiveX control's IWebBrowser2 + // Do not reference this directly. Use the AxIWebBrowser2 + // property instead. + private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2; + + private AxHost.ConnectionPointCookie cookie; // To hook up events from the native WebBrowser + private Stream documentStreamToSetOnLoad; + private WebBrowserEncryptionLevel encryptionLevel = WebBrowserEncryptionLevel.Insecure; + private object objectForScripting; + private WebBrowserEvent webBrowserEvent; + internal string statusText = ""; + + + private const int WEBBROWSERSTATE_webBrowserShortcutsEnabled = 0x00000001; + private const int WEBBROWSERSTATE_documentStreamJustSet = 0x00000002; + private const int WEBBROWSERSTATE_isWebBrowserContextMenuEnabled = 0x00000004; + private const int WEBBROWSERSTATE_canGoBack = 0x00000008; + private const int WEBBROWSERSTATE_canGoForward = 0x00000010; + private const int WEBBROWSERSTATE_scrollbarsEnabled = 0x00000020; + private const int WEBBROWSERSTATE_allowNavigation = 0x00000040; + + // PERF: take all the bools and put them into a state variable + private System.Collections.Specialized.BitVector32 webBrowserState; // see TREEVIEWSTATE_ consts above + + + // + // 8856f961-340a-11d0-a96b-00c04fd705a2 is the clsid for the native webbrowser control + // + /// + /// + /// + /// Creates an instance of the control. + /// + /// + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + public WebBrowser() : base("8856f961-340a-11d0-a96b-00c04fd705a2") { + CheckIfCreatedInIE(); + + webBrowserState = new System.Collections.Specialized.BitVector32(WEBBROWSERSTATE_isWebBrowserContextMenuEnabled | + WEBBROWSERSTATE_webBrowserShortcutsEnabled | WEBBROWSERSTATE_scrollbarsEnabled); + AllowNavigation = true; + } + + + // + // Public properties: + // + + + /// + /// + /// + /// Specifies whether the WebBrowser control may navigate to another page once + /// it has been loaded. NOTE: it will always be able to navigate before being loaded. + /// "Loaded" here means setting Url, DocumentText, or DocumentStream. + /// + /// + [SRDescription(SR.WebBrowserAllowNavigationDescr), + SRCategory(SR.CatBehavior), DefaultValue(true)] + public bool AllowNavigation + { + get + { + return webBrowserState[WEBBROWSERSTATE_allowNavigation]; + } + set + { + webBrowserState[WEBBROWSERSTATE_allowNavigation] = value; + if (webBrowserEvent != null) + { + webBrowserEvent.AllowNavigation = value; + } + } + } + + /// + /// + /// + /// Specifies whether the WebBrowser control will receive drop notifcations. + /// Maps to IWebBrowser2:RegisterAsDropTarget. + /// Note that this does not mean that the WebBrowser control integrates with + /// Windows Forms drag/drop i.e. the DragDrop event does not fire. It does + /// control whether you can drag new documents into the browser control. + /// + /// + [SRDescription(SR.WebBrowserAllowWebBrowserDropDescr), + SRCategory(SR.CatBehavior), DefaultValue(true)] + public bool AllowWebBrowserDrop { + get { + return this.AxIWebBrowser2.RegisterAsDropTarget; + } + set { + //Note: you lose this value when you load a new document: the value needs to be refreshed in + //OnDocumentCompleted. + if (value != AllowWebBrowserDrop) { + this.AxIWebBrowser2.RegisterAsDropTarget = value; + this.Refresh(); + } + } + } + + /// + /// + /// + /// Specifies whether the browser control shows script errors in dialogs or not. + /// Maps to IWebBrowser2:Silent. + /// + /// + [SRDescription(SR.WebBrowserScriptErrorsSuppressedDescr), + SRCategory(SR.CatBehavior), DefaultValue(false)] + public bool ScriptErrorsSuppressed { + get { + return this.AxIWebBrowser2.Silent; + } + set { + if (value != ScriptErrorsSuppressed) { + this.AxIWebBrowser2.Silent = value; + } + } + } + + /// + /// + /// + /// Specifies whether the browser control Shortcuts are enabled. + /// Maps to IDocHostUIHandler:TranslateAccelerator event. + /// + /// + [SRDescription(SR.WebBrowserWebBrowserShortcutsEnabledDescr), + SRCategory(SR.CatBehavior), DefaultValue(true)] + public bool WebBrowserShortcutsEnabled { + get { + return webBrowserState[WEBBROWSERSTATE_webBrowserShortcutsEnabled]; + } + set { + webBrowserState[WEBBROWSERSTATE_webBrowserShortcutsEnabled] = value; + } + } + + /// + /// + /// + /// If true, there is navigation history such that calling GoBack() will succeed. + /// Defaults to false. After that it's value is kept up to date by hooking the + /// DWebBrowserEvents2:CommandStateChange. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool CanGoBack { + get { + return CanGoBackInternal; + } + } + + /// + /// Returns the current WEBBROWSERSTATE_canGoBack value so that this value can be accessed + /// from child classes. + /// + internal bool CanGoBackInternal { + get { + return webBrowserState[WEBBROWSERSTATE_canGoBack]; + } + set { + if (value != CanGoBackInternal) { + webBrowserState[WEBBROWSERSTATE_canGoBack] = value; + OnCanGoBackChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// + /// If true, there is navigation history such that calling GoForward() will succeed. + /// Defaults to false. After that it's value is kept up to date by hooking the + /// DWebBrowserEvents2:CommandStateChange. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool CanGoForward { + get { + return CanGoForwardInternal; + } + } + + /// + /// Returns the current WEBBROWSERSTATE_canGoForward value so that this value can + /// be accessed from child classes. + /// + internal bool CanGoForwardInternal { + get { + return webBrowserState[WEBBROWSERSTATE_canGoForward]; + } + set { + if (value != CanGoForwardInternal) { + webBrowserState[WEBBROWSERSTATE_canGoForward] = value; + OnCanGoForwardChanged(EventArgs.Empty); + } + } + } + + /// + /// + /// + /// The HtmlDocument for page hosted in the html page. If no page is loaded, it returns null. + /// Maps to IWebBrowser2:Document. + /// Requires WebPermission for Url specified by Document.Domain. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public HtmlDocument Document { + get { + object objDoc = this.AxIWebBrowser2.Document; + if (objDoc != null) { + // Document is not necessarily an IHTMLDocument, it might be an office document as well. + UnsafeNativeMethods.IHTMLDocument2 iHTMLDocument2 = null; + try { + iHTMLDocument2 = objDoc as UnsafeNativeMethods.IHTMLDocument2; + } + catch (InvalidCastException) { + } + if (iHTMLDocument2 != null) { + UnsafeNativeMethods.IHTMLLocation iHTMLLocation = iHTMLDocument2.GetLocation(); + if (iHTMLLocation != null) { + string href = iHTMLLocation.GetHref(); + if (!string.IsNullOrEmpty(href)) + { + Uri url = new Uri(href); + WebBrowser.EnsureUrlConnectPermission(url); // Security check + return new HtmlDocument (ShimManager, iHTMLDocument2 as UnsafeNativeMethods.IHTMLDocument); + } + } + } + } + return null; + } + } + + /// + /// + /// + /// Get/sets the stream for the html document. + /// Uses the IPersisteStreamInit interface on the HtmlDocument to set/retrieve the html stream. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Stream DocumentStream { + get { + HtmlDocument htmlDocument = this.Document; + if (htmlDocument == null) { + return null; + } + else { + UnsafeNativeMethods.IPersistStreamInit psi = htmlDocument.DomDocument as UnsafeNativeMethods.IPersistStreamInit; + Debug.Assert(psi != null, "Object isn't an IPersistStreamInit!"); + if (psi == null) { + return null; + } + else { + MemoryStream memoryStream = new MemoryStream(); + UnsafeNativeMethods.IStream iStream = (UnsafeNativeMethods.IStream)new UnsafeNativeMethods.ComStreamFromDataStream(memoryStream); + psi.Save(iStream, false); + return new MemoryStream(memoryStream.GetBuffer(), 0, (int)memoryStream.Length, false); + } + } + } + set { + this.documentStreamToSetOnLoad = value; + try { + webBrowserState[WEBBROWSERSTATE_documentStreamJustSet] = true; + // Lets navigate to "about:blank" so that we get a "clean" document + this.Url = new Uri("about:blank"); + } + finally { + webBrowserState[WEBBROWSERSTATE_documentStreamJustSet] = false; + } + } + } + + /// + /// + /// + /// Sets/sets the text of the contained html page. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string DocumentText { + get { + Stream stream = this.DocumentStream; + if (stream == null) + { + return ""; + } + StreamReader reader = new StreamReader(stream); + stream.Position = 0; + return reader.ReadToEnd(); + } + set { + if (value == null) + { + value = ""; + } + //string length is a good initial guess for capacity -- + //if it needs more room, it'll take it. + MemoryStream ms = new MemoryStream(value.Length); + StreamWriter sw = new StreamWriter(ms, Encoding.UTF8 ); + sw.Write(value); + sw.Flush(); + ms.Position = 0; + this.DocumentStream = ms; + } + } + + /// + /// + /// + /// The title of the html page currently loaded. If none are loaded, returns empty string. + /// Maps to IWebBrowser2:LocationName. + /// Requires WebPermission for Url specified by Document.Domain. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string DocumentTitle { + get { + string documentTitle; + + HtmlDocument htmlDocument = this.Document; + if (htmlDocument == null) { + documentTitle = this.AxIWebBrowser2.LocationName; + } + else { + UnsafeNativeMethods.IHTMLDocument2 htmlDocument2 = htmlDocument.DomDocument as UnsafeNativeMethods.IHTMLDocument2; + Debug.Assert(htmlDocument2 != null, "The HtmlDocument object must implement IHTMLDocument2."); + try { + documentTitle = htmlDocument2.GetTitle(); + } + catch (COMException) { + documentTitle = ""; + } + } + return documentTitle; + } + } + + /// + /// + /// + /// A string containing the MIME type of the document hosted in the browser control. + /// If none are loaded, returns empty string. Maps to IHTMLDocument2:mimeType. + /// Requires WebPermission for Url specified by Document.Domain. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string DocumentType { + get { + string docType = ""; + HtmlDocument htmlDocument = this.Document; + if (htmlDocument != null) { + UnsafeNativeMethods.IHTMLDocument2 htmlDocument2 = htmlDocument.DomDocument as UnsafeNativeMethods.IHTMLDocument2; + Debug.Assert(htmlDocument2 != null, "The HtmlDocument object must implement IHTMLDocument2."); + try { + docType = htmlDocument2.GetMimeType(); + } + catch (COMException) { + docType = ""; + } + } + return docType; + } + } + + /// + /// + /// + /// Initially set to WebBrowserEncryptionLevel.Insecure. + /// After that it's kept up to date by hooking the DWebBrowserEvents2:SetSecureLockIcon. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WebBrowserEncryptionLevel EncryptionLevel { + get { + if (this.Document == null) { + encryptionLevel = WebBrowserEncryptionLevel.Unknown; + } + return encryptionLevel; + } + } + + /// + /// + /// + /// True if the browser is engaged in navigation or download. Maps to IWebBrowser2:Busy. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool IsBusy { + get { + if (this.Document == null) { + return false; + } + else { + return this.AxIWebBrowser2.Busy; + } + } + } + + /// + /// + /// + /// Gets the offline state of the browser control. Maps to IWebBrowser2:Offline. + /// + /// + [SRDescription(SR.WebBrowserIsOfflineDescr), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool IsOffline { + get { + return this.AxIWebBrowser2.Offline; + } + } + + /// + /// + /// + /// Indicates whether to use the WebBrowser context menu. + /// It's technically possible to have both the WebBrowser & Windows Forms context + /// menu enabled, but making this property effect the behavior of the Windows Form + /// context menu does not lead to a clean OM. Maps to sinking the + /// IDocHostUIHandler:ShowContextMenu + /// + /// + [SRDescription(SR.WebBrowserIsWebBrowserContextMenuEnabledDescr), + SRCategory(SR.CatBehavior), DefaultValue(true)] + public bool IsWebBrowserContextMenuEnabled { + get { + return webBrowserState[WEBBROWSERSTATE_isWebBrowserContextMenuEnabled]; + } + set { + webBrowserState[WEBBROWSERSTATE_isWebBrowserContextMenuEnabled] = value; + } + } + + /// + /// + /// + /// Allows the host application to provide an object that the contained html + /// pages can access programatically in script. The object specified here + /// will be accessible in script as the "window.external" object via IDispatch + /// COM interop. Maps to an implementation of the IDocUIHandler.GetExternal event. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object ObjectForScripting + { + get + { + return objectForScripting; + } + set + { + if (value != null) + { + Type t = value.GetType(); + if (!Marshal.IsTypeVisibleFromCom(t)) + { + throw new ArgumentException(SR.GetString(SR.WebBrowserObjectForScriptingComVisibleOnly)); + } + } + objectForScripting = value; + } + } + + /// + /// + /// + /// [To be supplied.] + /// + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) + ] + public new Padding Padding { + get { return base.Padding; } + set { base.Padding = value;} + } + + /// + /// + /// [To be supplied.] + /// + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr) + ] + public new event EventHandler PaddingChanged + { + add + { + base.PaddingChanged += value; + } + remove + { + base.PaddingChanged -= value; + } + } + + /// + /// + /// + /// Gets the ReadyState of the browser control. (ex.. document loading vs. load complete). + /// Maps to IWebBrowser2:ReadyState. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WebBrowserReadyState ReadyState { + get { + if (this.Document == null) { + return WebBrowserReadyState.Uninitialized; + } + else { + return (WebBrowserReadyState)this.AxIWebBrowser2.ReadyState; + } + } + } + + /// + /// + /// + /// The text that would be displayed in the IE status bar. + /// There is no direct WebBrowser property that maps to this. This property is + /// initially an empty string. After that the value is kept up to date via the + /// DWebBrowserEvents2:StatusTextChange event. + /// Requires WebPermission for Url specified by Document.Domain (indirect access + /// to user browsing stats). + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual string StatusText { + get { + if (this.Document == null) { + statusText = ""; + } + return statusText; + } + } + + /// + /// + /// + /// The url of the HtmlDocument for page hosted in the html page. + /// Get Maps to IWebBrowser2:LocationUrl. Set is the equivalent of calling Navigate(Url). + /// Note this means that setting the Url property & then reading it immediately may not + /// return the result that you just set (since the get always returns the url you are currently at). + /// + /// + // VSWhidbey 82018: Make this property bindable. + [ + SRDescription(SR.WebBrowserUrlDescr), + Bindable(true), + SRCategory(SR.CatBehavior), + TypeConverter(typeof(System.Windows.Forms.WebBrowserUriTypeConverter)), + DefaultValue(null) + ] + public Uri Url { + get { + string urlString = this.AxIWebBrowser2.LocationURL; + //NOTE: If we weren't going to require FullTrust, we'd need to require permissions here + if (string.IsNullOrEmpty(urlString)) + { + return null; + } + try + { + return new Uri(urlString); + } + catch (UriFormatException) + { + return null; + } + } + set { + if (value != null && value.ToString() == "") + { + value = null; + } + this.PerformNavigateHelper(ReadyNavigateToUrl(value), false, null, null, null); + } + } + + /// + /// + /// + /// Returns the version property of IE. + /// Determined by reading the file version of mshtml.dll in the %system% directory. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Version Version { + get { + string mshtmlPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "mshtml.dll"); + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(mshtmlPath); + return new Version(fvi.FileMajorPart, fvi.FileMinorPart, fvi.FileBuildPart, fvi.FilePrivatePart); + } + } + + + + // + // Public methods: + // + + /// + /// + /// + /// Navigates the browser to the previous page in the navigation history list. + /// Maps to IWebBrowser2:GoBack. + /// Returns true if the operation succeeds, else returns false. It will return + /// false if there is no page in the navigation history to go back to. + /// + /// + public bool GoBack() { + bool retVal = true; + try + { + this.AxIWebBrowser2.GoBack(); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + retVal = false; + } + return retVal; + } + + /// + /// + /// + /// Navigates the browser to the next page in the navigation history list. + /// Maps to IWebBrowser2:GoForward. + /// Returns true if the operation succeeds, else returns false. It will return + /// false if there is no page in the navigation history to go forward to. + /// + /// + public bool GoForward() { + bool retVal = true; + try + { + this.AxIWebBrowser2.GoForward(); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + retVal = false; + } + return retVal; + } + + /// + /// + /// + /// Navigates the browser to user's homepage. Maps to IWebBrowser2:GoHome. + /// + /// + public void GoHome() { + this.AxIWebBrowser2.GoHome(); + } + + /// + /// + /// + /// Navigates the browser to user's default search page. Maps to IWebBrowser2:GoSearch. + /// + /// + public void GoSearch() { + this.AxIWebBrowser2.GoSearch(); + } + + /// + /// + /// + /// Navigates to the specified Uri's AbsolutePath + /// + /// + public void Navigate(Uri url) + { + Url = url; // Does null check in PerformNavigate2 + } + + /// + /// + /// + /// String overload for Navigate(Uri) + /// + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads"), + SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public void Navigate(string urlString) + { + PerformNavigateHelper(ReadyNavigateToUrl(urlString), false, null, null, null); + } + + /// + /// + /// + /// Navigates the specified frame to the specified URL. + /// If the frame name is invalid, it opens a new window (not ideal, but it's the current behavior). + /// Maps to IWebBrowser2:Navigate. + /// + /// + public void Navigate(Uri url, string targetFrameName) + { + PerformNavigateHelper(ReadyNavigateToUrl(url), false, targetFrameName, null, null); + } + + /// + /// + /// + /// String overload for Navigate(Uri, string) + /// + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads"), + SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public void Navigate(string urlString, string targetFrameName) + { + PerformNavigateHelper(ReadyNavigateToUrl(urlString), false, targetFrameName, null, null); + } + + /// + /// + /// + /// Opens a new window if newWindow is true, navigating it to the specified URL. Maps to IWebBrowser2:Navigate. + /// + /// + public void Navigate(Uri url, bool newWindow) { + PerformNavigateHelper(ReadyNavigateToUrl(url), newWindow, null, null, null); + } + + /// + /// + /// + /// String overload for Navigate(Uri, bool) + /// + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads"), + SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public void Navigate(string urlString, bool newWindow) + { + PerformNavigateHelper(ReadyNavigateToUrl(urlString), newWindow, null, null, null); + } + + /// + /// + /// + /// Navigates to the specified Uri's AbsolutePath with specified args + /// + /// + public void Navigate(Uri url, string targetFrameName, byte[] postData, string additionalHeaders) + { + PerformNavigateHelper(ReadyNavigateToUrl(url), false, targetFrameName, postData, additionalHeaders); + } + + /// + /// + /// + /// String overload for Navigate(Uri, string, byte[], string) + /// + /// + /// Note: We intentionally have a string overload (apparently Mort wants one). We don't have + /// string overloads call Uri overloads because that breaks Uris that aren't fully qualified + /// (things like "www.microsoft.com") that the underlying objects support and we don't want to + /// break. + [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads"), + SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public void Navigate(string urlString, string targetFrameName, byte[] postData, string additionalHeaders) + { + PerformNavigateHelper(ReadyNavigateToUrl(urlString), false, targetFrameName, postData, additionalHeaders); + } + + /// + /// + /// + /// Prints the html document to the default printer w/ no print dialog. + /// Maps to IWebBrowser2:ExecWB w/ IDM_PRINT flag & LECMDEXECOPT_DONTPROMPTUSER. + /// + /// + public void Print() { + IntSecurity.DefaultPrinting.Demand(); + + object nullObjectArray = null; + try + { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PRINT, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + } + + /// + /// + /// + /// Refreshes the current page. Maps to IWebBrowser2:Refresh. + /// + /// + public override void Refresh() { + try + { + if (ShouldSerializeDocumentText()) + { + string text = this.DocumentText; + this.AxIWebBrowser2.Refresh(); + this.DocumentText = text; + } + else + { + this.AxIWebBrowser2.Refresh(); + } + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + } + } + + /// + /// + /// + /// Refreshes the current page w/ the specified refresh option. The refresh option + /// controls how much is loaded out of the browser cache vs. rechecking the server for. + /// Maps to IWebBrowser2:Refresh2 + /// + /// + public void Refresh(WebBrowserRefreshOption opt) { + object level = (object) opt; + try { + if (ShouldSerializeDocumentText()) + { + string text = this.DocumentText; + this.AxIWebBrowser2.Refresh2(ref level); + this.DocumentText = text; + } + else + { + this.AxIWebBrowser2.Refresh2(ref level); + } + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Enables/disables the webbrowser's scrollbars. + /// + /// + [SRDescription(SR.WebBrowserScrollBarsEnabledDescr), + SRCategory(SR.CatBehavior), DefaultValue(true)] + public bool ScrollBarsEnabled { + get { + return webBrowserState[WEBBROWSERSTATE_scrollbarsEnabled]; + } + set { + if (value != webBrowserState[WEBBROWSERSTATE_scrollbarsEnabled]) { + webBrowserState[WEBBROWSERSTATE_scrollbarsEnabled] = value; + this.Refresh(); + } + } + } + + /// + /// + /// + /// Opens the IE page setup dialog for the current page. + /// Maps to IWebBrowser2:ExecWebBrowser w/ IDM_PAGESETUP flag & LECMDEXECOPT_PROMPTUSER. + /// + /// + public void ShowPageSetupDialog() { + IntSecurity.SafePrinting.Demand(); + + object nullObjectArray = null; + try { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PAGESETUP, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Opens the IE print dialog. + /// Maps to IWebBrowser2:ExecWebBrowser w/ IDM_PRINT flag & OLECMDEXECOPT_PROMPTUSER. + /// + /// + public void ShowPrintDialog() { + IntSecurity.SafePrinting.Demand(); + + object nullObjectArray = null; + + try { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PRINT, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Opens the IE print preview dialog. Maps to IWebBrowser2:ExecWebBrowser w/ IDM_PRINTPREVIEW flag. + /// + /// + public void ShowPrintPreviewDialog() { + IntSecurity.SafePrinting.Demand(); + + object nullObjectArray = null; + + try { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PRINTPREVIEW, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Opens the properties dialog for the current html page. + /// Maps to IWebBrowser2:ExecWebBrowser w/ IDM_PROPERTIES flag & LECMDEXECOPT_PROMPTUSER. + /// + /// + public void ShowPropertiesDialog() { + object nullObjectArray = null; + + try { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PROPERTIES, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Opens the IE File-Save dialog. + /// Maps to IWebBrowser2:ExecWebBrowser w/ IDM_SAVEAS flag & LECMDEXECOPT_PROMPTUSER. + /// + /// + public void ShowSaveAsDialog() { + IntSecurity.FileDialogSaveFile.Demand(); + + object nullObjectArray = null; + + try { + this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_SAVEAS, NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, ref nullObjectArray, IntPtr.Zero); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + /// + /// + /// + /// Stops the current navigation. Maps to IWebBrowser2:Stop. + /// + /// + public void Stop() { + try { + this.AxIWebBrowser2.Stop(); + } + catch (Exception ex) { + if (ClientUtils.IsSecurityOrCriticalException(ex)) { + throw; + } + } + } + + // + // Public events: + // + /// + /// + /// + /// Occurs when the IE back button would change from enabled to disabled or vice versa. + /// Maps to DWebBrowserEvents2:CommandStateChange w/ CSC_NAVIGATEBACK. + /// + /// + [Browsable(false), SRCategory(SR.CatPropertyChanged), SRDescription(SR.WebBrowserCanGoBackChangedDescr)] + public event EventHandler CanGoBackChanged; + /// + /// + /// + /// Occurs when the IE forward button would change from enabled to disabled or vice versa. + /// Maps to DWebBrowserEvents2:CommandStateChange w/ CSC_NAVIGATEFORWARD. + /// + /// + [Browsable(false), SRCategory(SR.CatPropertyChanged), SRDescription(SR.WebBrowserCanGoForwardChangedDescr)] + public event EventHandler CanGoForwardChanged; + /// + /// + /// + /// Occurs when the document hosted in the web browser control is fully loaded. + /// This is conceptially similar to Form.Load(). You need to wait until this event fires + /// before doing anything that manipulates the html page, ex. reading the Document + /// property of the webbrowser control. Maps to DWebBrowserEvents2:DocumentComplete. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.WebBrowserDocumentCompletedDescr)] + public event WebBrowserDocumentCompletedEventHandler DocumentCompleted; + /// + /// + /// + /// Occurs whenever the title text changes. The Title is the html page title + /// or the file path/url if not title is available. This is the text you see as + /// the title of the IE window preceeding "Microsoft Internet Explorer". + /// Maps to DWebBrowserEvents2:TitleChange. + /// + /// + [Browsable(false), SRCategory(SR.CatPropertyChanged), SRDescription(SR.WebBrowserDocumentTitleChangedDescr)] + public event EventHandler DocumentTitleChanged; + /// + /// + /// + /// Occurs whenever encryption level changes. + /// Can be used to set a custom security lock icon similar to what IE shows when + /// you go to an https site. Maps to DWebBrowserEvents2:SetSecureLockIcon. + /// + /// + [Browsable(false), SRCategory(SR.CatPropertyChanged), SRDescription(SR.WebBrowserEncryptionLevelChangedDescr)] + public event EventHandler EncryptionLevelChanged; + /// + /// + /// + /// Occurs when a file download occurs. + /// Can be used to cancel file downloads. Maps to DWebBrowserEvents2:FileDownload. + /// + /// + [SRCategory(SR.CatBehavior), SRDescription(SR.WebBrowserFileDownloadDescr)] + public event EventHandler FileDownload; + /// + /// + /// + /// Occurs after browser control navigation occurs. + /// Fires after browser navigation is complete. Maps to DWebBrowserEvents2:NavigateComplete. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.WebBrowserNavigatedDescr)] + public event WebBrowserNavigatedEventHandler Navigated; + /// + /// + /// + /// Occurs before browser control navigation occurs. + /// Fires before browser navigation occurs. Allows navigation to be canceled if + /// NavigatingEventArgs.Cancel is set to false. Maps to DWebBrowserEvents2:BeforeNavigate2. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.WebBrowserNavigatingDescr)] + public event WebBrowserNavigatingEventHandler Navigating; + /// + /// + /// + /// Occurs when a new browser window is created. + /// Can be used to cancel the creation of the new browser window. Maps to DWebBrowserEvents2:NewWindow2. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.WebBrowserNewWindowDescr)] + public event CancelEventHandler NewWindow; + /// + /// + /// + /// Occurs when an update to the progress of a download occurs. + /// Fires whenever the browser control has updated info on the download. Can be + /// used to provide a download status bar and display the number of bytes downloaded. + /// Maps to DWebBrowserEvents2:ProgressChange. + /// + /// + [SRCategory(SR.CatAction), SRDescription(SR.WebBrowserProgressChangedDescr)] + public event WebBrowserProgressChangedEventHandler ProgressChanged; + /// + /// + /// + /// Occurs whenever the status text changes. + /// Can be used to keep a status bar populated with uptodate text. + /// Maps to DWebBrowserEvents2:StatusTextChange. + /// + /// + [Browsable(false), SRCategory(SR.CatPropertyChanged), SRDescription(SR.WebBrowserStatusTextChangedDescr)] + public event EventHandler StatusTextChanged; + + + + // + // public overrides: + // + + /// + /// + /// Returns true if this control (or any of its child windows) has focus. + /// + public override bool Focused { + get { + if (base.Focused) { + return true; + } + IntPtr hwndFocus = UnsafeNativeMethods.GetFocus(); + return hwndFocus != IntPtr.Zero + && SafeNativeMethods.IsChild(new HandleRef(this, this.Handle), new HandleRef(null, hwndFocus)); + } + } + + + // + // protected overrides: + // + // + // + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if (htmlShimManager != null) + { + htmlShimManager.Dispose(); + } + DetachSink(); + ActiveXSite.Dispose(); + } + base.Dispose(disposing); + } + + /// + /// + /// + /// Overrides the default size property of Control to specify a bigger default size of 250 x 250. + /// + /// + protected override Size DefaultSize { + get { + return new Size(250, 250); + } + } + + /// + /// + /// + /// Retrieves IWebBrowser2 from the native object. Overriding classes should first call base.AttachInterfaces. + /// + /// + protected override void AttachInterfaces(object nativeActiveXObject) { + this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject; + } + + /// + /// + /// + /// Discards the IWebBrowser2 reference. Overriding classes should call base.DetachInterfaces. + /// + /// + protected override void DetachInterfaces() { + this.axIWebBrowser2 = null; + } + + /// + /// + /// + /// Returns a WebBrowserSite object. + /// + /// + protected override WebBrowserSiteBase CreateWebBrowserSiteBase() { + return new WebBrowserSite(this); + } + + /// + /// + /// + /// Attaches to the DWebBrowserEvents2 connection point. + /// + /// + protected override void CreateSink() { + object ax = this.activeXInstance; + if (ax != null) { + webBrowserEvent = new WebBrowserEvent(this); + webBrowserEvent.AllowNavigation = AllowNavigation; + this.cookie = new AxHost.ConnectionPointCookie(ax, webBrowserEvent, + typeof(UnsafeNativeMethods.DWebBrowserEvents2)); + } + } + + /// + /// + /// + /// Releases the DWebBrowserEvents2 connection point. + /// + /// + protected override void DetachSink() { + //If we have a cookie get rid of it + if (this.cookie != null) { + this.cookie.Disconnect(); + this.cookie = null; + } + } + + /// + internal override void OnTopMostActiveXParentChanged(EventArgs e) { + if (TopMostParent.IsIEParent) { + WebBrowser.createdInIE = true; + CheckIfCreatedInIE(); + } + else { + WebBrowser.createdInIE = false; + base.OnTopMostActiveXParentChanged(e); + } + } + + + + // + // protected virtuals: + // + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnCanGoBackChanged(EventArgs e) + { + if (this.CanGoBackChanged != null) + { + this.CanGoBackChanged(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnCanGoForwardChanged(EventArgs e) + { + if (this.CanGoForwardChanged != null) + { + this.CanGoForwardChanged(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e) + { + this.AxIWebBrowser2.RegisterAsDropTarget = AllowWebBrowserDrop; + if (this.DocumentCompleted != null) + { + this.DocumentCompleted(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnDocumentTitleChanged(EventArgs e) + { + if (this.DocumentTitleChanged != null) + { + this.DocumentTitleChanged(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnEncryptionLevelChanged(EventArgs e) + { + if (this.EncryptionLevelChanged != null) + { + this.EncryptionLevelChanged(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnFileDownload(EventArgs e) + { + if (this.FileDownload != null) + { + this.FileDownload(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnNavigated(WebBrowserNavigatedEventArgs e) + { + if (this.Navigated != null) + { + this.Navigated(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnNavigating(WebBrowserNavigatingEventArgs e) + { + if (this.Navigating != null) + { + this.Navigating(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnNewWindow(CancelEventArgs e) + { + if (this.NewWindow != null) + { + this.NewWindow(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnProgressChanged(WebBrowserProgressChangedEventArgs e) + { + if (this.ProgressChanged != null) + { + this.ProgressChanged(this, e); + } + } + + /// + /// + /// + /// Raises the event. + /// + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + // SECREVIEW: This seems safe, but could anything dangerous occur here? + protected virtual void OnStatusTextChanged(EventArgs e) + { + if (this.StatusTextChanged != null) + { + this.StatusTextChanged(this, e); + } + } + + +#region ShimSupport + private HtmlShimManager htmlShimManager; + internal HtmlShimManager ShimManager { + get { + if (htmlShimManager == null) { + htmlShimManager = new HtmlShimManager(); + } + return htmlShimManager; + } + } +#endregion + + + // + // Private methods: + // + private void CheckIfCreatedInIE() { + if (WebBrowser.createdInIE) { + if (this.ParentInternal != null) { + this.ParentInternal.Controls.Remove(this); + this.Dispose(); + } + else { + this.Dispose(); + throw new NotSupportedException(SR.GetString(SR.WebBrowserInIENotSupported)); + } + } + } + + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + internal static void EnsureUrlConnectPermission(Uri url) { + // + WebPermission permission = new WebPermission(NetworkAccess.Connect, url.AbsoluteUri); + permission.Demand(); + } + + private string ReadyNavigateToUrl(string urlString) { + if (string.IsNullOrEmpty(urlString)) { + urlString = "about:blank"; + } + + // + // Nullify any calls to set_DocumentStream which may still be pending + if (!webBrowserState[WEBBROWSERSTATE_documentStreamJustSet]) { + this.documentStreamToSetOnLoad = null; + } + + return urlString; + } + + private string ReadyNavigateToUrl(Uri url) + { + string urlString; + if (url == null) + { + urlString = "about:blank"; + } + else + { + if (!url.IsAbsoluteUri) + { + throw new ArgumentException(SR.GetString(SR.WebBrowserNavigateAbsoluteUri, "uri")); + } + + // Characters outside of US-ASCII may appear in Windows file paths and accordingly they’re allowed in file URIs. + // Therefore, do not use the escaped AbsoluteUri for file schemes. Can't use ToString() either since the correct + // syntax for file schemas includes percent escaped characters. We are stuck with OriginalString and hope that + // it is well-formed. + urlString = url.IsFile ? url.OriginalString : url.AbsoluteUri; + } + + return ReadyNavigateToUrl(urlString); + } + + private void PerformNavigateHelper(string urlString, bool newWindow, string targetFrameName, byte[] postData, string headers) + { + object objUrlString = (object)urlString; + object objFlags = (object) (newWindow ? 1 : 0); + object objTargetFrameName = (object)targetFrameName; + object objPostData = (object)postData; + object objHeaders = (object)headers; + PerformNavigate2(ref objUrlString, ref objFlags, ref objTargetFrameName, ref objPostData, ref objHeaders); + } + + private void PerformNavigate2(ref object URL, ref object flags, ref object targetFrameName, ref object postData, ref object headers) + { + try { + this.AxIWebBrowser2.Navigate2(ref URL, ref flags, ref targetFrameName, ref postData, ref headers); + } + catch (COMException ce) { + if ((uint)unchecked(ce.ErrorCode) != (uint)unchecked(0x800704c7)) { + // "the operation was canceled by the user" - navigation failed + // ignore this error, IE has already alerted the user. + throw; + } + } + } + + private bool ShouldSerializeDocumentText() { + return IsValidUrl; + } + + bool IsValidUrl + { + get + { + return Url == null || Url.AbsoluteUri == "about:blank"; + } + } + + private bool ShouldSerializeUrl() { + return !ShouldSerializeDocumentText(); + } + + /// + /// Returns TRUE if there is a context menu to show + /// Returns FALSE otherwise + /// + private bool ShowContextMenu(int x, int y) { + ContextMenuStrip contextMenuStrip = ContextMenuStrip; + ContextMenu contextMenu = contextMenuStrip != null ? null : ContextMenu; + + if (contextMenuStrip != null || contextMenu != null) { + Point client; + bool keyboardActivated = false; + // X will be exactly -1 when the user invokes the context menu from the keyboard + // + if (x == -1) { + keyboardActivated = true; + client = new Point(Width / 2, Height / 2); + } else { + client = PointToClientInternal(new Point(x, y)); + } + + if (ClientRectangle.Contains(client)) { + if (contextMenuStrip != null) { + contextMenuStrip.ShowInternal(this, client, keyboardActivated); + } else if (contextMenu != null) { + contextMenu.Show(this, client); + } + + return true; + } else { + return false; + } + } else { + return false; + } + } + + /// + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_CONTEXTMENU: + int x = NativeMethods.Util.SignedLOWORD(m.LParam); + int y = NativeMethods.Util.SignedHIWORD(m.LParam); + + if (!ShowContextMenu(x, y)) { + DefWndProc(ref m); + } + break; + default: + base.WndProc(ref m); + break; + } + } + + private UnsafeNativeMethods.IWebBrowser2 AxIWebBrowser2 { + get { + if (this.axIWebBrowser2 == null) { + if (!this.IsDisposed) { + // This should call AttachInterfaces + TransitionUpTo(WebBrowserHelper.AXState.InPlaceActive); + } + else { + throw new System.ObjectDisposedException(GetType().Name); + } + } + // We still don't have this.axIWebBrowser2. Throw an exception. + if (this.axIWebBrowser2 == null) { + throw new InvalidOperationException(SR.GetString(SR.WebBrowserNoCastToIWebBrowser2)); + } + return this.axIWebBrowser2; + } + } + + + + // + // WebBrowserSite class: + // + // + // We slap InheritanceDemand on this class so that only users with + // UnmanagedCode permissions can override this type. + // + /// + /// + /// + /// Provides a default WebBrowserSite implementation for use in the CreateWebBrowserSite + /// method in the WebBrowser class. + /// + /// + [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + ComVisible(false)] + protected class WebBrowserSite : WebBrowserSiteBase, UnsafeNativeMethods.IDocHostUIHandler + { + /// + /// + /// + /// Creates an instance of the class. + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public WebBrowserSite(WebBrowser host) : base(host) { + } + + + // + // IDocHostUIHandler Implementation + // + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, NativeMethods.POINT pt, object pcmdtReserved, object pdispReserved) { + WebBrowser wb = (WebBrowser)this.Host; + + if (wb.IsWebBrowserContextMenuEnabled) { + // let MSHTML display its UI + return NativeMethods.S_FALSE; + } else { + if (pt.x == 0 && pt.y == 0) { + // IDocHostUIHandler::ShowContextMenu sends (0,0) when the context menu is invoked via the keyboard + // make it (-1, -1) for the WebBrowser::ShowContextMenu method + pt.x = -1; + pt.y = -1; + } + wb.ShowContextMenu(pt.x, pt.y); + // MSHTML should not display its context menu because we displayed ours + return NativeMethods.S_OK; + } + } + + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.GetHostInfo(NativeMethods.DOCHOSTUIINFO info) { + WebBrowser wb = (WebBrowser)this.Host; + + info.dwDoubleClick = (int)NativeMethods.DOCHOSTUIDBLCLICK.DEFAULT; + info.dwFlags = (int)NativeMethods.DOCHOSTUIFLAG.NO3DOUTERBORDER | + (int)NativeMethods.DOCHOSTUIFLAG.DISABLE_SCRIPT_INACTIVE; + + if (wb.ScrollBarsEnabled) { + info.dwFlags |= (int)NativeMethods.DOCHOSTUIFLAG.FLAT_SCROLLBAR; + } + else { + info.dwFlags |= (int)NativeMethods.DOCHOSTUIFLAG.SCROLL_NO; + } + + if (Application.RenderWithVisualStyles) { + info.dwFlags |= (int)NativeMethods.DOCHOSTUIFLAG.THEME; + } + else { + info.dwFlags |= (int)NativeMethods.DOCHOSTUIFLAG.NOTHEME; + } + + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.EnableModeless(bool fEnable) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.ShowUI(int dwID, UnsafeNativeMethods.IOleInPlaceActiveObject activeObject, + NativeMethods.IOleCommandTarget commandTarget, UnsafeNativeMethods.IOleInPlaceFrame frame, + UnsafeNativeMethods.IOleInPlaceUIWindow doc) { + return NativeMethods.S_FALSE; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.HideUI() { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.UpdateUI() { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.OnDocWindowActivate(bool fActivate) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.ResizeBorder(NativeMethods.COMRECT rect, UnsafeNativeMethods.IOleInPlaceUIWindow doc, bool fFrameWindow) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.GetDropTarget(UnsafeNativeMethods.IOleDropTarget pDropTarget, out UnsafeNativeMethods.IOleDropTarget ppDropTarget) { + // + // Set to null no matter what we return, to prevent the marshaller + // from going crazy if the pointer points to random stuff. + ppDropTarget = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.GetExternal(out object ppDispatch) { + WebBrowser wb = (WebBrowser)this.Host; + ppDispatch = wb.ObjectForScripting; + return NativeMethods.S_OK; + } + + /// + /// + [SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")] + int UnsafeNativeMethods.IDocHostUIHandler.TranslateAccelerator(ref NativeMethods.MSG msg, ref Guid group, int nCmdID) { + // + // Returning S_FALSE will allow the native control to do default processing, + // i.e., execute the shortcut key. Returning S_OK will cancel the shortcut key. + + WebBrowser wb = (WebBrowser)this.Host; + + if (!wb.WebBrowserShortcutsEnabled) + { + int keyCode = (int)msg.wParam | (int)Control.ModifierKeys; + + if (msg.message != NativeMethods.WM_CHAR + && Enum.IsDefined(typeof(Shortcut), (Shortcut)keyCode)) { + return NativeMethods.S_OK; + } + return NativeMethods.S_FALSE; + } + return NativeMethods.S_FALSE; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strUrlIn, out string pstrUrlOut) { + // + // Set to null no matter what we return, to prevent the marshaller + // from going crazy if the pointer points to random stuff. + pstrUrlOut = null; + return NativeMethods.S_FALSE; + } + + /// + /// + int UnsafeNativeMethods.IDocHostUIHandler.FilterDataObject(IComDataObject pDO, out IComDataObject ppDORet) + { + // + // Set to null no matter what we return, to prevent the marshaller + // from going crazy if the pointer points to random stuff. + ppDORet = null; + return NativeMethods.S_FALSE; + } + + // + // Internal methods + // + internal override void OnPropertyChanged(int dispid) { + if (dispid != NativeMethods.ActiveX.DISPID_READYSTATE) { + base.OnPropertyChanged(dispid); + } + } + } + + + // + // Private classes: + // + [ClassInterface(ClassInterfaceType.None)] + private class WebBrowserEvent : StandardOleMarshalObject, UnsafeNativeMethods.DWebBrowserEvents2{ + + private WebBrowser parent; + private bool allowNavigation; + private bool haveNavigated = false; + + public WebBrowserEvent(WebBrowser parent) { + this.parent = parent; + } + + public bool AllowNavigation + { + get + { + return allowNavigation; + } + set + { + allowNavigation = value; + } + } + + public void CommandStateChange(long command, bool enable) { + if (command == NativeMethods.CSC_NAVIGATEBACK) { + this.parent.CanGoBackInternal = enable; + } + else if (command == NativeMethods.CSC_NAVIGATEFORWARD) { + this.parent.CanGoForwardInternal = enable; + } + } + + public void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel) { + Debug.Assert(parent != null, "Parent should have been set"); + //Note: we want to allow navigation if we haven't already navigated. + if (AllowNavigation || !haveNavigated) + { + Debug.Assert(urlObject == null || urlObject is string, "invalid url type"); + Debug.Assert(targetFrameName == null || targetFrameName is string, "invalid targetFrameName type"); + Debug.Assert(headers == null || headers is string, "invalid headers type"); + // + // Due to a bug in the interop code where the variant.bstr value gets set + // to -1 on return back to native code, if the original value was null, we + // have to set targetFrameName and headers to "". + if (targetFrameName == null) { + targetFrameName = ""; + } + if (headers == null) { + headers = ""; + } + + string urlString = urlObject == null ? "" : (string)urlObject; + WebBrowserNavigatingEventArgs e = new WebBrowserNavigatingEventArgs( + new Uri(urlString), targetFrameName == null ? "" : (string)targetFrameName); + this.parent.OnNavigating(e); + cancel = e.Cancel; + } + else + { + cancel = true; + } + } + + public void DocumentComplete(object pDisp, ref object urlObject) { + Debug.Assert(urlObject == null || urlObject is string, "invalid url"); + haveNavigated = true; + if (this.parent.documentStreamToSetOnLoad != null && (string)urlObject == "about:blank") { + HtmlDocument htmlDocument = this.parent.Document; + if (htmlDocument != null) { + UnsafeNativeMethods.IPersistStreamInit psi = htmlDocument.DomDocument as UnsafeNativeMethods.IPersistStreamInit; + Debug.Assert(psi != null, "The Document does not implement IPersistStreamInit"); + UnsafeNativeMethods.IStream iStream = (UnsafeNativeMethods.IStream)new UnsafeNativeMethods.ComStreamFromDataStream( + this.parent.documentStreamToSetOnLoad); + psi.Load(iStream); + htmlDocument.Encoding = "unicode"; + } + this.parent.documentStreamToSetOnLoad = null; + } + else { + string urlString = urlObject == null ? "" : urlObject.ToString(); + WebBrowserDocumentCompletedEventArgs e = new WebBrowserDocumentCompletedEventArgs( + new Uri(urlString)); + this.parent.OnDocumentCompleted(e); + } + } + + public void TitleChange(string text) { + this.parent.OnDocumentTitleChanged(EventArgs.Empty); + } + + public void SetSecureLockIcon(int secureLockIcon) { + this.parent.encryptionLevel = (WebBrowserEncryptionLevel)secureLockIcon; + this.parent.OnEncryptionLevelChanged(EventArgs.Empty); + } + + public void NavigateComplete2(object pDisp, ref object urlObject) { + Debug.Assert(urlObject == null || urlObject is string, "invalid url type"); + string urlString = urlObject == null ? "" : (string)urlObject; + WebBrowserNavigatedEventArgs e = new WebBrowserNavigatedEventArgs( + new Uri(urlString)); + this.parent.OnNavigated(e); + } + + public void NewWindow2(ref object ppDisp, ref bool cancel) { + CancelEventArgs e = new CancelEventArgs(); + this.parent.OnNewWindow(e); + cancel = e.Cancel; + } + + public void ProgressChange(int progress, int progressMax) { + WebBrowserProgressChangedEventArgs e = new WebBrowserProgressChangedEventArgs(progress, progressMax); + this.parent.OnProgressChanged(e); + } + + public void StatusTextChange(string text) { + this.parent.statusText = (text == null) ? "" : text; + this.parent.OnStatusTextChanged(EventArgs.Empty); + } + + public void DownloadBegin() { + this.parent.OnFileDownload(EventArgs.Empty); + } + + public void FileDownload(ref bool cancel) { } + public void PrivacyImpactedStateChange(bool bImpacted) { } + public void UpdatePageStatus(object pDisp, ref object nPage, ref object fDone) { } + public void PrintTemplateTeardown(object pDisp) { } + public void PrintTemplateInstantiation(object pDisp) { } + public void NavigateError(object pDisp, ref object url, ref object frame, ref object statusCode, ref bool cancel) { } + public void ClientToHostWindow(ref long cX, ref long cY) { } + public void WindowClosing(bool isChildWindow, ref bool cancel) { } + public void WindowSetHeight(int height) { } + public void WindowSetWidth(int width) { } + public void WindowSetTop(int top) { } + public void WindowSetLeft(int left) { } + public void WindowSetResizable(bool resizable) { } + public void OnTheaterMode(bool theaterMode) { } + public void OnFullScreen(bool fullScreen) { } + public void OnStatusBar(bool statusBar) { } + public void OnMenuBar(bool menuBar) { } + public void OnToolBar(bool toolBar) { } + public void OnVisible(bool visible) { } + public void OnQuit() { } + public void PropertyChange(string szProperty) { } + public void DownloadComplete() { } + } + } + + + // + // Public enums: + // + + /// + /// + /// + /// Specifies the EncryptionLevel of the document in the WebBrowser control. + /// Returned by the property. + /// + /// + public enum WebBrowserEncryptionLevel { + /// + Insecure = 0, + /// + Mixed = 1, + /// + Unknown = 2, + /// + Bit40 = 3, + /// + Bit56 = 4, + /// + Fortezza = 5, + /// + Bit128 = 6 + } + + /// + /// + /// + /// Specifies the ReadyState of the WebBrowser control. + /// Returned by the property. + /// + /// + public enum WebBrowserReadyState { + /// + Uninitialized = 0, + /// + Loading = 1, + /// + Loaded = 2, + /// + Interactive = 3, + /// + Complete = 4 + } + + /// + /// + /// + /// Specifies the RefreshOptions in the method. + /// + /// + public enum WebBrowserRefreshOption { + /// + Normal = 0, + /// + IfExpired = 1, + /// + Continue = 2, + /// + Completely = 3 + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserBase.cs b/WindowsForms/Managed/System/WinForms/WebBrowserBase.cs new file mode 100644 index 000000000..9d019f423 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserBase.cs @@ -0,0 +1,1998 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using System.Configuration.Assemblies; +using System.Runtime.Remoting; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System; +using System.Reflection; +using System.Globalization; +using System.Security.Permissions; +using Microsoft.Win32; +using System.Collections.Specialized; +using System.IO; +using System.Drawing; +using System.Windows.Forms.Design; +using System.Windows.Forms.ComponentModel; +using System.Windows.Forms.ComponentModel.Com2Interop; +using System.ComponentModel.Design; +using System.Drawing.Imaging; +using System.Drawing.Design; +using System.Security; + +namespace System.Windows.Forms { + /// + /// + /// + /// Wraps ActiveX controls and exposes them as fully featured windows forms controls + /// (by inheriting from Control). Some of Control's properties that don't make sense + /// for ActiveX controls are blocked here (by setting Browsable attributes on some and + /// throwing exceptions from others), to make life easy for the inheritors. + /// + /// Inheritors of this class simply need to concentrate on defining & implementing the + /// properties/methods/events of the specific ActiveX control they are wrapping, the + /// default properties etc and the code to implement the activation etc. are + /// encapsulated in the class below. + /// + /// The classid of the ActiveX control is specified in the constructor. + /// + /// + [ComVisible(true), + ClassInterface(ClassInterfaceType.AutoDispatch), + PermissionSetAttribute(SecurityAction.LinkDemand, Name="FullTrust"), + PermissionSetAttribute(SecurityAction.InheritanceDemand, Name="FullTrust"), + DefaultProperty("Name"), DefaultEvent("Enter"), + Designer("System.Windows.Forms.Design.AxDesigner, " + AssemblyRef.SystemDesign)] + public class WebBrowserBase : Control { + // + // Privates fields: + // + private WebBrowserHelper.AXState axState = WebBrowserHelper.AXState.Passive; + private WebBrowserHelper.AXState axReloadingState = WebBrowserHelper.AXState.Passive; + private WebBrowserHelper.AXEditMode axEditMode = WebBrowserHelper.AXEditMode.None; + private bool inRtlRecreate=false; + private BitVector32 axHostState = new BitVector32(); + private WebBrowserHelper.SelectionStyle selectionStyle = WebBrowserHelper.SelectionStyle.NotSelected; + private int noComponentChange = 0; + private WebBrowserSiteBase axSite; + private ContainerControl containingControl; + private IntPtr hwndFocus = IntPtr.Zero; + private EventHandler selectionChangeHandler; + private Guid clsid; + // Pointers to the ActiveX object: Interface pointers are cached for perf. + private UnsafeNativeMethods.IOleObject axOleObject; + private UnsafeNativeMethods.IOleInPlaceObject axOleInPlaceObject; + private UnsafeNativeMethods.IOleInPlaceActiveObject axOleInPlaceActiveObject; + private UnsafeNativeMethods.IOleControl axOleControl; + private WebBrowserBaseNativeWindow axWindow; + // We need to change the size of the inner ActiveX control before the + //WebBrowserBase control's size is changed (i.e., before WebBrowserBase.Bounds + //is changed) for better visual effect. We use this field to know what size + //the WebBrowserBase control is changing to. + private Size webBrowserBaseChangingSize = Size.Empty; + private WebBrowserContainer wbContainer = null; + + // This flags the WebBrowser not to process dialog keys when the ActiveX control is doing it + // and calls back into the WebBrowser for some reason. See DDB#147045 + private bool ignoreDialogKeys; + + // + // Internal fields: + // + internal WebBrowserContainer container; + internal object activeXInstance; // this is internal to avoid the security demand + + + + // + // SECURITY NOTE: This must be internal, because by specifying a + // clsid here, someone will be able to create a native com object. + // + /// + /// + /// + /// Creates a new instance of a Microsoft control which wraps an ActiveX control + /// given by the clsid parameter. + /// + /// + internal WebBrowserBase(string clsidString) : base() { + if (Application.OleRequired() != ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.AXMTAThread, clsidString)); + } + + this.SetStyle(ControlStyles.UserPaint, false); + + this.clsid = new Guid(clsidString); + this.webBrowserBaseChangingSize.Width = -1; // Invalid value. Use WebBrowserBase.Bounds instead, when this is the case. + this.SetAXHostState(WebBrowserHelper.isMaskEdit, this.clsid.Equals(WebBrowserHelper.maskEdit_Clsid)); + } + + + + // + // Public properties: + // + + /// + /// + /// + /// Returns the native webbrowser object that this control wraps. Needs FullTrust to access. + /// + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object ActiveXInstance { + get { + return activeXInstance; + } + } + + // + // Virtual methods: + // + // The following are virtual methods that derived-classes can override + // (only in full-trust though). + // + + // + // The native ActiveX control QI's for interfaces on it's site to see if + // it needs to change its behavior. Since the WebBrowserSiteBaseBase class is generic, + // it only implements site interfaces that are generic to all sites. QI's + // for any more specific interfaces will fail. This is a problem if anyone + // wants to support any other interfaces on the site. In order to overcome + // this, one needs to extend WebBrowserSiteBaseBase and implement any additional interfaces + // needed. + // + // ActiveX wrapper controls that derive from this class should override the + // below method and return their own WebBrowserSiteBaseBase derived object. + // + // This method is protected by an InheritanceDemand because extending a + // site is strictly an advanced feature for which one needs UnmanagedCode + // permissions. + // + /// + /// + /// + /// Returns an object that will be set as the site for the native ActiveX control. + /// Implementors of the site can derive from class. + /// + /// + protected virtual WebBrowserSiteBase CreateWebBrowserSiteBase() { + return new WebBrowserSiteBase(this); + } + + /// + /// + /// + /// This will be called when the native ActiveX control has just been created. + /// Inheritors of this class can override this method to cast the nativeActiveXObject + /// parameter to the appropriate interface. They can then cache this interface + /// value in a member variable. However, they must release this value when + /// DetachInterfaces is called (by setting the cached interface variable to null). + /// + /// + protected virtual void AttachInterfaces(object nativeActiveXObject) { + } + + /// + /// + /// + /// See AttachInterfaces for a description of when to override DetachInterfaces. + /// + /// + protected virtual void DetachInterfaces() { + } + + /// + /// + /// + /// This will be called when we are ready to start listening to events. + /// Inheritors can override this method to hook their own connection points. + /// + /// + protected virtual void CreateSink() { + } + + /// + /// + /// + /// This will be called when it is time to stop listening to events. + /// This is where inheritors have to disconnect their connection points. + /// + /// + protected virtual void DetachSink() { + } + + //DrawToBitmap doesn't work for this control, so we should hide it. We'll + //still call base so that this has a chance to work if it can. + [EditorBrowsable(EditorBrowsableState.Never)] + new public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) + { + base.DrawToBitmap(bitmap, targetBounds); + } + + + // + // Overriding methods: Overrides of some of Control's virtual methods. + // + + // + // Sets the site of this component. A non-null value indicates that this + // component has been added to a container, and a null value indicates that + // this component is being removed from a container. + // + /// + public override ISite Site { + set { + bool hadSelectionHandler = this.RemoveSelectionHandler(); + + base.Site = value; + + if (hadSelectionHandler) { + this.AddSelectionHandler(); + } + } + } + + // + // We have to resize the ActiveX control when our size changes. + // + /// + internal override void OnBoundsUpdate(int x, int y, int width, int height) + { + // + // If the ActiveX control is already InPlaceActive, make sure + // it's bounds also change. + if (this.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive) { + try { + this.webBrowserBaseChangingSize.Width = width; + this.webBrowserBaseChangingSize.Height = height; + this.AXInPlaceObject.SetObjectRects(new NativeMethods.COMRECT(new Rectangle(0, 0, width, height)), WebBrowserHelper.GetClipRect()); + } + finally { + this.webBrowserBaseChangingSize.Width = -1; // Invalid value. Use WebBrowserBase.Bounds instead, when this is the case. + } + } + + base.OnBoundsUpdate(x, y, width, height); + } + + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] + protected override bool ProcessDialogKey(Keys keyData) + { + return ignoreDialogKeys ? false : base.ProcessDialogKey(keyData); + } + + // + // Let us assume that TAB key was pressed. In this case, we should first + // give a chance to the ActiveX control to see if it wants to change focus + // to other subitems within it. If we did not give the ActiveX control the + // first chance to handle the key stroke, and called base.PreProcessMessage, + // the focus would be changed to the next control on the form! We don't want + // that!! + // + // If the ActiveX control doesn't want to handle the key, it calls back into + // WebBrowserSiteBase's IOleControlSite.TranslateAccelerator implementation. There, we + // set a flag and call back into this method. In this method, we first check + // if this flag is set. If so, we call base.PreProcessMessage. + // + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] + public override bool PreProcessMessage(ref Message msg) + { + if (IsUserMode) { + if (this.GetAXHostState(WebBrowserHelper.siteProcessedInputKey)) { + // In this case, the control called us back through IOleControlSite + // and is now giving us a chance to see if we want to process it. + return base.PreProcessMessage(ref msg); + } + + + // Convert Message to NativeMethods.MSG + NativeMethods.MSG win32Message = new NativeMethods.MSG(); + win32Message.message = msg.Msg; + win32Message.wParam = msg.WParam; + win32Message.lParam = msg.LParam; + win32Message.hwnd = msg.HWnd; + + this.SetAXHostState(WebBrowserHelper.siteProcessedInputKey, false); + try + { + if (axOleInPlaceObject != null) + { + // Give the ActiveX control a chance to process this key by calling + // IOleInPlaceActiveObject::TranslateAccelerator. + int hr = axOleInPlaceActiveObject.TranslateAccelerator(ref win32Message); + + if (hr == NativeMethods.S_OK) + { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message translated to " + win32Message); + return true; + } + else + { + // + // win32Message may have been modified. Lets copy it back. + msg.Msg = win32Message.message; + msg.WParam = win32Message.wParam; + msg.LParam = win32Message.lParam; + msg.HWnd = win32Message.hwnd; + + if (hr == NativeMethods.S_FALSE) + { + // Same code as in AxHost (ignore dialog keys here, as a fix for ASURT 139468). + // We have the same problem here (see DDB#147045). + bool ret = false; + + ignoreDialogKeys = true; + try + { + ret = base.PreProcessMessage(ref msg); + } + finally + { + ignoreDialogKeys = false; + } + return ret; + } + else if (this.GetAXHostState(WebBrowserHelper.siteProcessedInputKey)) + { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message processed by site. Calling base.PreProcessMessage() " + msg); + return base.PreProcessMessage(ref msg); + } + else + { + Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "\t Message not processed by site. Returning false. " + msg); + return false; + } + } + } + } + finally { + this.SetAXHostState(WebBrowserHelper.siteProcessedInputKey, false); + } + } + + return false; + } + + + // + // Process a mnemonic character. This is done by manufacturing a + // WM_SYSKEYDOWN message and passing it to the ActiveX control. + // + // We can't decide just by ourselves whether we can process the + // mnemonic. We have to ask the ActiveX control for it. + // + /// + [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")] + [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] + protected internal override bool ProcessMnemonic(char charCode) { + bool processed = false; + + if (CanSelect) { + try { + NativeMethods.tagCONTROLINFO ctlInfo = new NativeMethods.tagCONTROLINFO(); + int hr = this.axOleControl.GetControlInfo(ctlInfo); + if (NativeMethods.Succeeded(hr)) { + // + // Sadly, we don't have a message so we must fake one ourselves. + // The message we are faking is a WM_SYSKEYDOWN with the right + // alt key setting. + NativeMethods.MSG msg = new NativeMethods.MSG(); + msg.hwnd = IntPtr.Zero; + msg.message = NativeMethods.WM_SYSKEYDOWN; + msg.wParam = (IntPtr) Char.ToUpper(charCode, CultureInfo.CurrentCulture); + msg.lParam = (IntPtr) 0x20180001; + msg.time = SafeNativeMethods.GetTickCount(); + NativeMethods.POINT p = new NativeMethods.POINT(); + UnsafeNativeMethods.GetCursorPos(p); + msg.pt_x = p.x; + msg.pt_y = p.y; + if (SafeNativeMethods.IsAccelerator(new HandleRef(ctlInfo, ctlInfo.hAccel), ctlInfo.cAccel, ref msg, null)) { + this.axOleControl.OnMnemonic(ref msg); + FocusInternal(); + processed = true; + } + } + } + catch (Exception ex) { + if (ClientUtils.IsCriticalException(ex)) { + throw; + } + Debug.Fail("error in processMnemonic"); + } + } + return processed; + } + + // + // Certain messages are forwarder directly to the ActiveX control, + // others are first processed by the wndproc of Control + // + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode), + PermissionSetAttribute(SecurityAction.LinkDemand, Name="FullTrust"), + PermissionSetAttribute(SecurityAction.InheritanceDemand, Name="FullTrust")] + protected override void WndProc(ref Message m) { + switch (m.Msg) { + // + // Things we explicitly ignore and pass to the ActiveX's windproc + // + case NativeMethods.WM_ERASEBKGND: + case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFYFORMAT: + case NativeMethods.WM_SETCURSOR: + case NativeMethods.WM_SYSCOLORCHANGE: + case NativeMethods.WM_LBUTTONDBLCLK: + case NativeMethods.WM_LBUTTONUP: + case NativeMethods.WM_MBUTTONDBLCLK: + case NativeMethods.WM_MBUTTONUP: + case NativeMethods.WM_RBUTTONDBLCLK: + case NativeMethods.WM_RBUTTONUP: + case NativeMethods.WM_CONTEXTMENU: + // + // Some of the MSComCtl controls respond to this message to do some + // custom painting. So, we should just pass this message through. + case NativeMethods.WM_DRAWITEM: + DefWndProc(ref m); + break; + + case NativeMethods.WM_COMMAND: + if (!ReflectMessageInternal(m.LParam, ref m)) + DefWndProc(ref m); + break; + + case NativeMethods.WM_HELP: + // We want to both fire the event, and let the ActiveX have the message... + base.WndProc(ref m); + DefWndProc(ref m); + break; + + case NativeMethods.WM_LBUTTONDOWN: + case NativeMethods.WM_MBUTTONDOWN: + case NativeMethods.WM_RBUTTONDOWN: + case NativeMethods.WM_MOUSEACTIVATE: + if (!DesignMode) { + if (containingControl != null && containingControl.ActiveControl != this) { + FocusInternal(); + } + } + DefWndProc(ref m); + break; + + case NativeMethods.WM_KILLFOCUS: + hwndFocus = (IntPtr)m.WParam; + try { + base.WndProc(ref m); + } + finally { + hwndFocus = IntPtr.Zero; + } + break; + + case NativeMethods.WM_DESTROY: + // + // If we are currently in a state of InPlaceActive or above, + // we should first reparent the ActiveX control to our parking + // window before we transition to a state below InPlaceActive. + // Otherwise we face all sorts of problems when we try to + // transition back to a state >= InPlaceActive. + // + if (this.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive) { + IntPtr hwndInPlaceObject; + if (NativeMethods.Succeeded(this.AXInPlaceObject.GetWindow(out hwndInPlaceObject))) { + Application.ParkHandle(new HandleRef(this.AXInPlaceObject, hwndInPlaceObject)); + } + } + + if (RecreatingHandle) { + axReloadingState = axState; + } + + // + // If the ActiveX control was holding on to our handle we need + // to get it to throw it away. This, we do by transitioning it + // down below InPlaceActivate (because it is when transitioning + // up to InPlaceActivate that the ActiveX control grabs our handle). + TransitionDownTo(WebBrowserHelper.AXState.Running); + + if (this.axWindow != null) { + this.axWindow.ReleaseHandle(); + } + + OnHandleDestroyed(EventArgs.Empty); + break; + + default: + if (m.Msg == WebBrowserHelper.REGMSG_MSG) { + m.Result = (IntPtr)WebBrowserHelper.REGMSG_RETVAL; + } + else { + base.WndProc(ref m); + } + break; + } + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnParentChanged(EventArgs e) { + Control parent = ParentInternal; + if ((Visible && parent != null && parent.Visible) || this.IsHandleCreated) { + TransitionUpTo(WebBrowserHelper.AXState.InPlaceActive); + } + base.OnParentChanged(e); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnVisibleChanged(EventArgs e) { + if (Visible && !Disposing && !IsDisposed) { + TransitionUpTo(WebBrowserHelper.AXState.InPlaceActive); + } + base.OnVisibleChanged(e); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnGotFocus(EventArgs e) { + if (this.ActiveXState < WebBrowserHelper.AXState.UIActive) { + TransitionUpTo(WebBrowserHelper.AXState.UIActive); + } + base.OnGotFocus(e); + } + + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnLostFocus(EventArgs e) { + base.OnLostFocus(e); + + // If the focus goes from our control window to one of the child windows, + // we should not deactivate. + if (!this.ContainsFocus) { + TransitionDownTo(WebBrowserHelper.AXState.InPlaceActive); + } + } + + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnRightToLeftChanged(EventArgs e) { + //Do nothing: no point in recreating the handle when we don't obey RTL + } + + // + // Can't select the control until the ActiveX control is InPlaceActive. + // + internal override bool CanSelectCore() { + return this.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive ? + base.CanSelectCore() : false; + } + + internal override bool AllowsKeyboardToolTip() { + return false; + } + + // + // We have to inform the ActiveX control that an ambient property + // has changed. + // + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnFontChanged(EventArgs e) { + base.OnFontChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_FONT); + } + + // + // We have to inform the ActiveX control that an ambient property + // has changed. + // + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnForeColorChanged(EventArgs e) { + base.OnForeColorChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR); + } + + // + // We have to inform the ActiveX control that an ambient property + // has changed. + // + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected override void OnBackColorChanged(EventArgs e) { + base.OnBackColorChanged(e); + AmbientChanged(NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR); + } + + internal override void RecreateHandleCore() { + if (!inRtlRecreate) { + base.RecreateHandleCore(); + } + } + + + // + // TransitionDownTo Passive when we are being disposed. + // + /// + protected override void Dispose(bool disposing) { + if (disposing) { + TransitionDownTo(WebBrowserHelper.AXState.Passive); + } + base.Dispose(disposing); + } + + + + + // + // Internal helper methods: + // + +#if false + // FxCop: Currently not used + internal Guid ClassId { + get { + return clsid; + } + } +#endif + + internal WebBrowserHelper.AXState ActiveXState { + get { + return axState; + } + set { + axState = value; + } + } + + internal bool GetAXHostState(int mask) { + return this.axHostState[mask]; + } + + internal void SetAXHostState(int mask, bool value) { + this.axHostState[mask] = value; + } + + internal IntPtr GetHandleNoCreate() { + return IsHandleCreated ? Handle : IntPtr.Zero; + } + + internal void TransitionUpTo(WebBrowserHelper.AXState state) { + if (!this.GetAXHostState(WebBrowserHelper.inTransition)) { + this.SetAXHostState(WebBrowserHelper.inTransition, true); + + try { + while (state > this.ActiveXState) { + switch (this.ActiveXState) { + case WebBrowserHelper.AXState.Passive: + TransitionFromPassiveToLoaded(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Loaded, "Failed transition"); + break; + case WebBrowserHelper.AXState.Loaded: + TransitionFromLoadedToRunning(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Running, "Failed transition"); + break; + case WebBrowserHelper.AXState.Running: + TransitionFromRunningToInPlaceActive(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Failed transition"); + break; + case WebBrowserHelper.AXState.InPlaceActive: + TransitionFromInPlaceActiveToUIActive(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.UIActive, "Failed transition"); + break; + default: + Debug.Fail("bad state"); + this.ActiveXState++; // To exit the loop + break; + } + } + } + finally { + this.SetAXHostState(WebBrowserHelper.inTransition, false); + } + } + } + + internal void TransitionDownTo(WebBrowserHelper.AXState state) { + if (!this.GetAXHostState(WebBrowserHelper.inTransition)) { + this.SetAXHostState(WebBrowserHelper.inTransition, true); + + try { + while (state < this.ActiveXState) { + switch (this.ActiveXState) { + case WebBrowserHelper.AXState.UIActive: + TransitionFromUIActiveToInPlaceActive(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Failed transition"); + break; + case WebBrowserHelper.AXState.InPlaceActive: + TransitionFromInPlaceActiveToRunning(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Running, "Failed transition"); + break; + case WebBrowserHelper.AXState.Running: + TransitionFromRunningToLoaded(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Loaded, "Failed transition"); + break; + case WebBrowserHelper.AXState.Loaded: + TransitionFromLoadedToPassive(); + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Passive, "Failed transition"); + break; + default: + Debug.Fail("bad state"); + this.ActiveXState--; // To exit the loop + break; + } + } + } + finally { + this.SetAXHostState(WebBrowserHelper.inTransition, false); + } + } + } + + internal bool DoVerb(int verb) { + int hr = this.axOleObject.DoVerb(verb, IntPtr.Zero, this.ActiveXSite, 0, this.Handle, + new NativeMethods.COMRECT(this.Bounds)); + + Debug.Assert(hr == NativeMethods.S_OK, String.Format(CultureInfo.CurrentCulture, "DoVerb call failed for verb 0x{0:X}", verb)); + return hr == NativeMethods.S_OK; + } + + // + // Returns this control's logically containing form. + // At design time this is always the form being designed. + // At runtime it is the parent form. + // By default, the parent form performs that function. It is + // however possible for another form higher in the parent chain + // to serve in that role. The logical container of this + // control determines the set of logical sibling control. + // This property exists only to enable some specific + // behaviors of ActiveX controls. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] + internal ContainerControl ContainingControl { + get { + if (containingControl == null || + this.GetAXHostState(WebBrowserHelper.recomputeContainingControl)) { + containingControl = FindContainerControlInternal(); + } + + return containingControl; + } + } + + internal WebBrowserContainer CreateWebBrowserContainer() + { + if (wbContainer == null) + { + wbContainer = new WebBrowserContainer(this); + } + return wbContainer; + } + + internal WebBrowserContainer GetParentContainer() { + if (container == null) { + container = WebBrowserContainer.FindContainerForControl(this); + } + if (container == null) { + container = CreateWebBrowserContainer(); + container.AddControl(this); + } + return container; + } + + internal void SetEditMode(WebBrowserHelper.AXEditMode em) { + this.axEditMode = em; + } + + internal void SetSelectionStyle(WebBrowserHelper.SelectionStyle selectionStyle) { + if (DesignMode) { + ISelectionService iss = WebBrowserHelper.GetSelectionService(this); + this.selectionStyle = selectionStyle; + if (iss != null && iss.GetComponentSelected(this)) { + // The ActiveX Host designer will offer an extender property + // called "SelectionStyle" + PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["SelectionStyle"]; + if (prop != null && prop.PropertyType == typeof(int)) { + prop.SetValue(this, (int)selectionStyle); + } + } + } + } + + internal void AddSelectionHandler() { + if (!this.GetAXHostState(WebBrowserHelper.addedSelectionHandler)) { + this.SetAXHostState(WebBrowserHelper.addedSelectionHandler, true); + + ISelectionService iss = WebBrowserHelper.GetSelectionService(this); + if (iss != null) { + iss.SelectionChanging += SelectionChangeHandler; + } + } + } + + internal bool RemoveSelectionHandler() { + bool retVal = this.GetAXHostState(WebBrowserHelper.addedSelectionHandler); + if (retVal) { + this.SetAXHostState(WebBrowserHelper.addedSelectionHandler, false); + + ISelectionService iss = WebBrowserHelper.GetSelectionService(this); + if (iss != null) { + iss.SelectionChanging -= SelectionChangeHandler; + } + } + return retVal; + } + + internal void AttachWindow(IntPtr hwnd) { + UnsafeNativeMethods.SetParent(new HandleRef(null, hwnd), new HandleRef(this, this.Handle)); + + if (this.axWindow != null) { + this.axWindow.ReleaseHandle(); + } + this.axWindow = new WebBrowserBaseNativeWindow(this); + this.axWindow.AssignHandle(hwnd, false); + + UpdateZOrder(); + UpdateBounds(); + + Size extent = Size; + extent = SetExtent(extent.Width, extent.Height); + + Point location = Location; + Bounds = new Rectangle(location.X, location.Y, extent.Width, extent.Height); + } + + internal bool IsUserMode { + get { + return Site == null || !DesignMode; + } + } + + internal void MakeDirty() { + ISite iSite = this.Site; + if (iSite != null) { + IComponentChangeService ccs = (IComponentChangeService)iSite.GetService(typeof(IComponentChangeService)); + if (ccs != null) { + ccs.OnComponentChanging(this, null); + ccs.OnComponentChanged(this, null, null, null); + } + } + } + + internal int NoComponentChangeEvents { + get { + return noComponentChange; + } + + set { + noComponentChange = value; + } + } + + + + + + + + // + // Private helper methods: + // + + private void StartEvents() { + if (!this.GetAXHostState(WebBrowserHelper.sinkAttached)) { + this.SetAXHostState(WebBrowserHelper.sinkAttached, true); + CreateSink(); + } + this.ActiveXSite.StartEvents(); + } + + private void StopEvents() { + if (this.GetAXHostState(WebBrowserHelper.sinkAttached)) { + this.SetAXHostState(WebBrowserHelper.sinkAttached, false); + DetachSink(); + } + this.ActiveXSite.StopEvents(); + } + + private void TransitionFromPassiveToLoaded() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Passive, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.Passive) { + // + // First, create the ActiveX control + Debug.Assert(activeXInstance == null, "activeXInstance must be null"); + this.activeXInstance = UnsafeNativeMethods.CoCreateInstance(ref clsid, null, NativeMethods.CLSCTX_INPROC_SERVER, ref NativeMethods.ActiveX.IID_IUnknown); + Debug.Assert(activeXInstance != null, "w/o an exception being thrown we must have an object..."); + + // + // We are now Loaded! + this.ActiveXState = WebBrowserHelper.AXState.Loaded; + + // + // Lets give them a chance to cast the ActiveX object + // to the appropriate interfaces. + this.AttachInterfacesInternal(); + } + } + + private void TransitionFromLoadedToPassive() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Loaded, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.Loaded) { + // + // Need to make sure that we don't handle any PropertyChanged + // notifications at this point. + this.NoComponentChangeEvents++; + try { + // + // Release the activeXInstance + if (activeXInstance != null) { + // + // Lets first get the cached interface pointers of activeXInstance released. + this.DetachInterfacesInternal(); + + Marshal.FinalReleaseComObject(activeXInstance); + this.activeXInstance = null; + } + } + finally { + this.NoComponentChangeEvents--; + } + + // + // We are now Passive! + this.ActiveXState = WebBrowserHelper.AXState.Passive; + } + } + + private void TransitionFromLoadedToRunning() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Loaded, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.Loaded) { + // + // See if the ActiveX control returns OLEMISC_SETCLIENTSITEFIRST + int bits = 0; + int hr = this.axOleObject.GetMiscStatus(NativeMethods.ActiveX.DVASPECT_CONTENT, out bits); + if (NativeMethods.Succeeded(hr) && ((bits & NativeMethods.ActiveX.OLEMISC_SETCLIENTSITEFIRST) != 0)) { + // + // Simply setting the site to the ActiveX control should activate it. + // And this will take us to the Running state. + this.axOleObject.SetClientSite(this.ActiveXSite); + } + + // + // We start receiving events now (but we do this only if we are not + // in DesignMode). + if (!DesignMode) { + StartEvents(); + } + + // + // We are now Running! + this.ActiveXState = WebBrowserHelper.AXState.Running; + } + } + + private void TransitionFromRunningToLoaded() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Running, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.Running) { + StopEvents(); + + // + // Remove ourselves from our parent container... + WebBrowserContainer parentContainer = this.GetParentContainer(); + if (parentContainer != null) { + parentContainer.RemoveControl(this); + } + + // + // Now inform the ActiveX control that it's been un-sited. + this.axOleObject.SetClientSite(null); + + // + // We are now Loaded! + this.ActiveXState = WebBrowserHelper.AXState.Loaded; + } + } + + private void TransitionFromRunningToInPlaceActive() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.Running, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.Running) { + try + { + DoVerb(NativeMethods.OLEIVERB_INPLACEACTIVATE); + } + catch (Exception t) + { + throw new TargetInvocationException(SR.GetString(SR.AXNohWnd, GetType().Name), t); + } + + CreateControl(true); + + // + // We are now InPlaceActive! + this.ActiveXState = WebBrowserHelper.AXState.InPlaceActive; + } + } + + private void TransitionFromInPlaceActiveToRunning() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive) { + // + // First, lets make sure we transfer the ContainingControl's ActiveControl + // before we InPlaceDeactivate. + ContainerControl f = ContainingControl; + if (f != null && f.ActiveControl == this) { + f.SetActiveControlInternal(null); + } + + // + // Now, InPlaceDeactivate. + this.AXInPlaceObject.InPlaceDeactivate(); + + // + // We are now Running! + this.ActiveXState = WebBrowserHelper.AXState.Running; + } + } + + private void TransitionFromInPlaceActiveToUIActive() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.InPlaceActive) { + try { + DoVerb(NativeMethods.OLEIVERB_UIACTIVATE); + } + catch (Exception t) { + throw new TargetInvocationException(SR.GetString(SR.AXNohWnd, GetType().Name), t); + } + + // + // We are now UIActive + this.ActiveXState = WebBrowserHelper.AXState.UIActive; + } + } + + private void TransitionFromUIActiveToInPlaceActive() { + Debug.Assert(this.ActiveXState == WebBrowserHelper.AXState.UIActive, "Wrong start state to transition from"); + if (this.ActiveXState == WebBrowserHelper.AXState.UIActive) { + int hr = this.AXInPlaceObject.UIDeactivate(); + Debug.Assert(NativeMethods.Succeeded(hr), "Failed to UIDeactivate"); + + // We are now InPlaceActive + this.ActiveXState = WebBrowserHelper.AXState.InPlaceActive; + } + } + + internal WebBrowserSiteBase ActiveXSite { + get { + if (axSite == null) { + this.axSite = CreateWebBrowserSiteBase(); + } + return axSite; + } + } + + private void AttachInterfacesInternal() { + Debug.Assert(activeXInstance != null, "The native control is null"); + this.axOleObject = (UnsafeNativeMethods.IOleObject)activeXInstance; + this.axOleInPlaceObject = (UnsafeNativeMethods.IOleInPlaceObject)activeXInstance; + this.axOleInPlaceActiveObject = (UnsafeNativeMethods.IOleInPlaceActiveObject)activeXInstance; + this.axOleControl = (UnsafeNativeMethods.IOleControl)activeXInstance; + // + // Lets give the inheriting classes a chance to cast + // the ActiveX object to the appropriate interfaces. + AttachInterfaces(activeXInstance); + } + + private void DetachInterfacesInternal() { + this.axOleObject = null; + this.axOleInPlaceObject = null; + this.axOleInPlaceActiveObject = null; + this.axOleControl = null; + // + // Lets give the inheriting classes a chance to release + // their cached interfaces of the ActiveX object. + DetachInterfaces(); + } + + // + // We need to change the ActiveX control's state when selection changes. + private EventHandler SelectionChangeHandler { + get { + if (this.selectionChangeHandler == null) { + this.selectionChangeHandler = new EventHandler(this.OnNewSelection); + } + return this.selectionChangeHandler; + } + } + + // + // We need to do special stuff (convert window messages to interface calls) + // during design time when selection changes. + private void OnNewSelection(Object sender, EventArgs e) { + if (this.DesignMode) { + ISelectionService iss = WebBrowserHelper.GetSelectionService(this); + if (iss != null) { + // We are no longer selected. + if (!iss.GetComponentSelected(this)) { + // + // We need to exit editmode if we were in one. + if (this.EditMode) { + this.GetParentContainer().OnExitEditMode(this); + this.SetEditMode(WebBrowserHelper.AXEditMode.None); + } + this.SetSelectionStyle(WebBrowserHelper.SelectionStyle.Selected); + this.RemoveSelectionHandler(); + } + else { + // + // The AX Host designer will offer an extender property called "SelectionStyle" + PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["SelectionStyle"]; + if (prop != null && prop.PropertyType == typeof(int)) { + int curSelectionStyle = (int)prop.GetValue(this); + if (curSelectionStyle != (int)this.selectionStyle) { + prop.SetValue(this, selectionStyle); + } + } + } + } + } + } + + private Size SetExtent(int width, int height) { + NativeMethods.tagSIZEL sz = new NativeMethods.tagSIZEL(); + sz.cx = width; + sz.cy = height; + bool resetExtents = DesignMode; + try { + Pixel2hiMetric(sz, sz); + this.axOleObject.SetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + } + catch (COMException) { + resetExtents = true; + } + if (resetExtents) { + this.axOleObject.GetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + try { + this.axOleObject.SetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + } + catch (COMException e) { + Debug.Fail(e.ToString()); + } + } + return GetExtent(); + } + + private Size GetExtent() { + NativeMethods.tagSIZEL sz = new NativeMethods.tagSIZEL(); + this.axOleObject.GetExtent(NativeMethods.ActiveX.DVASPECT_CONTENT, sz); + HiMetric2Pixel(sz, sz); + return new Size(sz.cx, sz.cy); + } + + private void HiMetric2Pixel(NativeMethods.tagSIZEL sz, NativeMethods.tagSIZEL szout) { + NativeMethods._POINTL phm = new NativeMethods._POINTL(); + phm.x = sz.cx; + phm.y = sz.cy; + NativeMethods.tagPOINTF pcont = new NativeMethods.tagPOINTF(); + ((UnsafeNativeMethods.IOleControlSite)this.ActiveXSite).TransformCoords(phm, pcont, NativeMethods.ActiveX.XFORMCOORDS_SIZE | NativeMethods.ActiveX.XFORMCOORDS_HIMETRICTOCONTAINER); + szout.cx = (int)pcont.x; + szout.cy = (int)pcont.y; + } + + private void Pixel2hiMetric(NativeMethods.tagSIZEL sz, NativeMethods.tagSIZEL szout) { + NativeMethods.tagPOINTF pcont = new NativeMethods.tagPOINTF(); + pcont.x = (float) sz.cx; + pcont.y = (float) sz.cy; + NativeMethods._POINTL phm = new NativeMethods._POINTL(); + ((UnsafeNativeMethods.IOleControlSite)this.ActiveXSite).TransformCoords(phm, pcont, NativeMethods.ActiveX.XFORMCOORDS_SIZE | NativeMethods.ActiveX.XFORMCOORDS_CONTAINERTOHIMETRIC); + szout.cx = phm.x; + szout.cy = phm.y; + } + + private bool EditMode { + get { + return this.axEditMode != WebBrowserHelper.AXEditMode.None; + } + } + + //Find the uppermost ContainerControl that this control lives in + internal ContainerControl FindContainerControlInternal() { + if (Site != null) { + IDesignerHost host = (IDesignerHost)Site.GetService(typeof(IDesignerHost)); + if (host != null) { + IComponent comp = host.RootComponent; + if (comp != null && comp is ContainerControl) { + return (ContainerControl)comp; + } + } + } + + ContainerControl cc = null; + for (Control control = this; control != null; control = control.ParentInternal) { + ContainerControl tempCC = control as ContainerControl; + if (tempCC != null) + cc = tempCC; + } + + if (cc == null) { + cc = Control.FromHandle(UnsafeNativeMethods.GetParent(new HandleRef(this, Handle))) as ContainerControl; + } + + // Never use the parking window for this: its hwnd can be destroyed at any time. + if (cc is Application.ParkingWindow) + { + cc = null; + } + + this.SetAXHostState(WebBrowserHelper.recomputeContainingControl, cc == null); + + return cc; + } + + private void AmbientChanged(int dispid) { + if (activeXInstance != null) { + try { + Invalidate(); + this.axOleControl.OnAmbientPropertyChange(dispid); + } + catch (Exception ex) { + if (ClientUtils.IsCriticalException(ex)) { + throw; + } + Debug.Fail(ex.ToString()); + } + } + } + +#if false + // FxCop: Currently not used + private Object GetAmbientProperty(int dispid) { + + Control richParent = ParentInternal; + + switch (dispid) { + case NativeMethods.ActiveX.DISPID_AMBIENT_USERMODE: + return !DesignMode; + case NativeMethods.ActiveX.DISPID_AMBIENT_AUTOCLIP: + return true; + case NativeMethods.ActiveX.DISPID_AMBIENT_MESSAGEREFLECT: + return true; + case NativeMethods.ActiveX.DISPID_AMBIENT_UIDEAD: + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYASDEFAULT: + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_FONT: + if (richParent != null) { + return WebBrowserHelper.GetIFontFromFont(richParent.Font); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_SHOWGRABHANDLES: + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_SHOWHATCHING: + return false; + case NativeMethods.ActiveX.DISPID_AMBIENT_BACKCOLOR: + if (richParent != null) { + return WebBrowserHelper.GetOleColorFromColor(richParent.BackColor); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_FORECOLOR: + if (richParent != null) { + return WebBrowserHelper.GetOleColorFromColor(richParent.ForeColor); + } + return null; + case NativeMethods.ActiveX.DISPID_AMBIENT_DISPLAYNAME: + string rval = this.GetParentContainer().GetNameForControl(this); + if (rval == null) rval = ""; + return rval; + case NativeMethods.ActiveX.DISPID_AMBIENT_LOCALEID: + return Thread.CurrentThread.CurrentCulture.LCID; + case NativeMethods.ActiveX.DISPID_AMBIENT_RIGHTTOLEFT: + Control ctl = this; + while (ctl != null) { + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.No) + return false; + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.Yes) + return true; + if (ctl.RightToLeft == System.Windows.Forms.RightToLeft.Inherit) + ctl = ctl.Parent; + } + return null; + default: + return null; + } + } +#endif + + internal UnsafeNativeMethods.IOleInPlaceObject AXInPlaceObject { + get { + return this.axOleInPlaceObject; + } + } + + // --------------------------------------------------------------- + // The following properties implemented in the Control class don't make + // sense for ActiveX controls. So we block them here. + // --------------------------------------------------------------- + + // + // Overridden properties: + // + + /// + protected override Size DefaultSize { + get { + return new Size(75, 23); + } + } + + + // + // Overridden methods: + // + + /// + protected override bool IsInputChar(char charCode) { + return true; + } + + /// + /// + /// Inheriting classes should override this method to find out when the + /// handle has been created. Call base.OnHandleCreated first. + /// + [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnHandleCreated(EventArgs e) { + // + // ASURT 43741 This is needed to prevent some controls (for e.g. Office Web Components) from + // failing to InPlaceActivate() when they call RegisterDragDrop() but do not call + // OleInitialize(). The EE calls CoInitializeEx() on the thread, but I believe + // that is not good enough for DragDrop. + // + if (Application.OleRequired() != System.Threading.ApartmentState.STA) { + throw new ThreadStateException(SR.GetString(SR.ThreadMustBeSTA)); + } + + base.OnHandleCreated(e); + + // make sure we restore whatever running state whad prior to the handle recreate. + // + if (axReloadingState != WebBrowserHelper.AXState.Passive && axReloadingState != axState) { + if (axState < axReloadingState) { + TransitionUpTo(axReloadingState); + } + else { + TransitionDownTo(axReloadingState); + } + axReloadingState = WebBrowserHelper.AXState.Passive; + } + + } + + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Color BackColor { + get { + return base.BackColor; + } + set { + base.BackColor = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Font Font { + get { + return base.Font; + } + set { + base.Font = value; + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Color ForeColor { + get { + return base.ForeColor; + } + set { + base.ForeColor = value; + } + } + + /// + /// Hide ImeMode: it doesn't make sense for this control + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public ImeMode ImeMode + { + get + { + return base.ImeMode; + } + set + { + base.ImeMode = value; + } + } + + // + // Properties blocked at design time and run time: + // + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserAllowDropNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserBackgroundImageNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserBackgroundImageLayoutNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Cursor Cursor { + get { + return base.Cursor; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserCursorNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + new public bool Enabled + { + get + { + return base.Enabled; + } + set + { + throw new NotSupportedException(SR.GetString(SR.WebBrowserEnabledNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Localizable(false)] + public override RightToLeft RightToLeft + { + get + { + return RightToLeft.No; + } + set + { + throw new NotSupportedException(SR.GetString(SR.WebBrowserRightToLeftNotSupported)); + } + } + + /// + // VSWhidbey 82018: Override this property so that the Bindable attribute can be set to false. + [ + Browsable(false), + EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + Bindable(false) + ] + public override string Text { + get { + return ""; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserTextNotSupported)); + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new bool UseWaitCursor { + get { + return base.UseWaitCursor; + } + set { + throw new NotSupportedException(SR.GetString(SR.WebBrowserUseWaitCursorNotSupported)); + } + } + + + // + // Unavailable events + // + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageLayoutChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackgroundImageLayoutChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler Enter { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Enter")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler Leave { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Leave")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseCaptureChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseCaptureChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseClick")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseDoubleClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseDoubleClick")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackColorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackColorChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BackgroundImageChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BackgroundImageChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler BindingContextChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "BindingContextChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler CursorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "CursorChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler EnabledChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "EnabledChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler FontChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "FontChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ForeColorChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ForeColorChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler RightToLeftChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "RightToLeftChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler TextChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "TextChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler Click { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Click")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragDrop { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragDrop")); + } + remove { + } + } + + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragEnter { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragEnter")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event DragEventHandler DragOver { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragOver")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DragLeave { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DragLeave")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event GiveFeedbackEventHandler GiveFeedback { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "GiveFeedback")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")] //Everett + new public event HelpEventHandler HelpRequested + { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "HelpRequested")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event PaintEventHandler Paint { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Paint")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event QueryContinueDragEventHandler QueryContinueDrag { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "QueryContinueDrag")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event QueryAccessibilityHelpEventHandler QueryAccessibilityHelp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "QueryAccessibilityHelp")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler DoubleClick { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "DoubleClick")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler ImeModeChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ImeModeChanged")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyEventHandler KeyDown { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyDown")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyPressEventHandler KeyPress { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyPress")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event KeyEventHandler KeyUp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "KeyUp")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event LayoutEventHandler Layout { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "Layout")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseDown { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseDown")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseEnter { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseEnter")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseLeave { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseLeave")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler MouseHover { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseHover")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseMove { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseMove")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseUp { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseUp")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event MouseEventHandler MouseWheel { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "MouseWheel")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event UICuesEventHandler ChangeUICues { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "ChangeUICues")); + } + remove { + } + } + + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + new public event EventHandler StyleChanged { + add { + throw new NotSupportedException(SR.GetString(SR.AXAddInvalidEvent, "StyleChanged")); + } + remove { + } + } + + /// + /// + /// Defines a window that the ActiveX window is attached to so that we can override it's wndproc. + /// + /// + private class WebBrowserBaseNativeWindow : NativeWindow { + private WebBrowserBase WebBrowserBase; + + public WebBrowserBaseNativeWindow(WebBrowserBase ax) { + this.WebBrowserBase = ax; + } + + /// + /// + /// Pass messages on to the NotifyIcon object's wndproc handler. + /// + /// + protected override void WndProc(ref Message m) { + switch (m.Msg) { + case NativeMethods.WM_WINDOWPOSCHANGING: + WmWindowPosChanging(ref m); + break; + default: + base.WndProc(ref m); + break; + } + } + + private unsafe void WmWindowPosChanging(ref Message m) { + NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; + wp->x = 0; + wp->y = 0; + Size s = WebBrowserBase.webBrowserBaseChangingSize; + if (s.Width == -1) { // Invalid value. Use WebBrowserBase.Bounds instead, when this is the case. + wp->cx = WebBrowserBase.Width; + wp->cy = WebBrowserBase.Height; + } + else { + wp->cx = s.Width; + wp->cy = s.Height; + } + m.Result = (IntPtr)0; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserContainer.cs b/WindowsForms/Managed/System/WinForms/WebBrowserContainer.cs new file mode 100644 index 000000000..0767eaa41 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserContainer.cs @@ -0,0 +1,393 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; +using System; +using System.Reflection; +using System.Globalization; +using System.Security.Permissions; +using System.Collections; +using System.Drawing; +using System.Windows.Forms.Design; +using System.ComponentModel.Design; +using System.Security; + +namespace System.Windows.Forms { + internal class WebBrowserContainer : UnsafeNativeMethods.IOleContainer, UnsafeNativeMethods.IOleInPlaceFrame { + // + // Private fields: + // + private WebBrowserBase parent; + private IContainer assocContainer; // associated IContainer... + // the assocContainer may be null, in which case all this container does is + // forward [de]activation messages to the requisite container... + private WebBrowserBase siteUIActive; + private WebBrowserBase siteActive; + private Hashtable containerCache = new Hashtable(); // name -> Control + private Hashtable components = null; // Control -> any + private WebBrowserBase ctlInEditMode = null; + + internal WebBrowserContainer(WebBrowserBase parent) { + this.parent = parent; + } + + // + // IOleContainer methods: + // + int UnsafeNativeMethods.IOleContainer.ParseDisplayName(Object pbc, string pszDisplayName, int[] pchEaten, Object[] ppmkOut) { + if (ppmkOut != null) + ppmkOut[0] = null; + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleContainer.EnumObjects(int grfFlags, out UnsafeNativeMethods.IEnumUnknown ppenum) { + ppenum = null; + if ((grfFlags & 1) != 0) { // 1 == OLECONTF_EMBEDDINGS + Debug.Assert(parent != null, "gotta have it..."); + ArrayList list = new ArrayList(); + ListAXControls(list, true); + if (list.Count > 0) { + Object[] temp = new Object[list.Count]; + list.CopyTo(temp, 0); + ppenum = new AxHost.EnumUnknown(temp); + return NativeMethods.S_OK; + } + } + ppenum = new AxHost.EnumUnknown(null); + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleContainer.LockContainer(bool fLock) { + return NativeMethods.E_NOTIMPL; + } + + // + // IOleInPlaceFrame methods: + // + IntPtr UnsafeNativeMethods.IOleInPlaceFrame.GetWindow() { + return parent.Handle; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.ContextSensitiveHelp(int fEnterMode) { + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.GetBorder(NativeMethods.COMRECT lprectBorder) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.RequestBorderSpace(NativeMethods.COMRECT pborderwidths) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.SetBorderSpace(NativeMethods.COMRECT pborderwidths) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.SetActiveObject(UnsafeNativeMethods.IOleInPlaceActiveObject pActiveObject, string pszObjName) { + if (pActiveObject == null) { + if (ctlInEditMode != null) { + ctlInEditMode.SetEditMode(WebBrowserHelper.AXEditMode.None); + ctlInEditMode = null; + } + return NativeMethods.S_OK; + } + WebBrowserBase ctl = null; + UnsafeNativeMethods.IOleObject oleObject = pActiveObject as UnsafeNativeMethods.IOleObject; + if (oleObject != null) { + UnsafeNativeMethods.IOleClientSite clientSite = null; + try { + clientSite = oleObject.GetClientSite(); + WebBrowserSiteBase webBrowserSiteBase = clientSite as WebBrowserSiteBase; + if (webBrowserSiteBase != null) + { + ctl = webBrowserSiteBase.GetAXHost(); + } + } + catch (COMException t) { + Debug.Fail(t.ToString()); + } + if (ctlInEditMode != null) { + Debug.Fail("control " + ctlInEditMode.ToString() + " did not reset its edit mode to null"); + ctlInEditMode.SetSelectionStyle(WebBrowserHelper.SelectionStyle.Selected); + ctlInEditMode.SetEditMode(WebBrowserHelper.AXEditMode.None); + } + + if (ctl == null) { + ctlInEditMode = null; + } + else { + if (!ctl.IsUserMode) { + ctlInEditMode = ctl; + ctl.SetEditMode(WebBrowserHelper.AXEditMode.Object); + ctl.AddSelectionHandler(); + ctl.SetSelectionStyle(WebBrowserHelper.SelectionStyle.Active); + } + } + } + + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.InsertMenus(IntPtr hmenuShared, NativeMethods.tagOleMenuGroupWidths lpMenuWidths) { + return NativeMethods.S_OK; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.SetMenu(IntPtr hmenuShared, IntPtr holemenu, IntPtr hwndActiveObject) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.RemoveMenus(IntPtr hmenuShared) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.SetStatusText(string pszStatusText) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.EnableModeless(bool fEnable) { + return NativeMethods.E_NOTIMPL; + } + + int UnsafeNativeMethods.IOleInPlaceFrame.TranslateAccelerator(ref NativeMethods.MSG lpmsg, short wID) { + return NativeMethods.S_FALSE; + } + + + // + // Private helper methods: + // + private void ListAXControls(ArrayList list, bool fuseOcx) { + Hashtable components = GetComponents(); + if (components == null) + { + return; + } + Control[] ctls = new Control[components.Keys.Count]; + components.Keys.CopyTo(ctls, 0); + if (ctls != null) { + for (int i = 0; i < ctls.Length; i++) { + Control ctl = ctls[i]; + WebBrowserBase webBrowserBase = ctl as WebBrowserBase; + if (webBrowserBase != null) { + if (fuseOcx) { + object ax = webBrowserBase.activeXInstance; + if (ax != null) { + list.Add(ax); + } + } + else { + list.Add(ctl); + } + } + } + } + } + + private Hashtable GetComponents() { + return GetComponents(GetParentsContainer()); + } + + private IContainer GetParentsContainer() { + // + IContainer rval = GetParentIContainer(); + Debug.Assert(rval == null || assocContainer == null || rval == assocContainer, + "mismatch between getIPD & aContainer"); + return rval == null ? assocContainer : rval; + } + + private IContainer GetParentIContainer() { + ISite site = parent.Site; + if (site != null && site.DesignMode) return site.Container; + return null; + } + + private Hashtable GetComponents(IContainer cont) { + FillComponentsTable(cont); + return components; + } + + private void FillComponentsTable(IContainer container) { + if (container != null) { + ComponentCollection comps = container.Components; + if (comps != null) { + components = new Hashtable(); + foreach (IComponent comp in comps) { + if (comp is Control && comp != parent && comp.Site != null) { + components.Add(comp, comp); + } + } + return; + } + } + + Debug.Assert(parent.Site == null, "Parent is sited but we could not find IContainer!!!"); + + bool checkHashTable = true; + Control[] ctls = new Control[containerCache.Values.Count]; + containerCache.Values.CopyTo(ctls, 0); + if (ctls != null) { + if (ctls.Length > 0 && components == null) { + components = new Hashtable(); + checkHashTable = false; + } + for (int i = 0; i < ctls.Length; i ++) { + if (checkHashTable && !components.Contains(ctls[i])) { + components.Add(ctls[i], ctls[i]); + } + } + } + + GetAllChildren(this.parent); + } + + private void GetAllChildren(Control ctl) { + if (ctl == null) + return; + + if (components == null) { + components = new Hashtable(); + } + + if (ctl != this.parent && !components.Contains(ctl)) + components.Add(ctl, ctl); + + foreach(Control c in ctl.Controls) { + GetAllChildren(c); + } + } + + private bool RegisterControl(WebBrowserBase ctl) { + ISite site = ctl.Site; + if (site != null) { + IContainer cont = site.Container; + if (cont != null) { + if (assocContainer != null) { + return cont == assocContainer; + } + else { + assocContainer = cont; + IComponentChangeService ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (ccs != null) { + ccs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved); + } + return true; + } + } + } + return false; + } + + private void OnComponentRemoved(object sender, ComponentEventArgs e) { + Control c = e.Component as Control; + if (sender == assocContainer && c != null) { + RemoveControl(c); + } + } + + // + // Internal helper methods: + // + internal void AddControl(Control ctl) { + if (containerCache.Contains(ctl)) + throw new ArgumentException(SR.GetString(SR.AXDuplicateControl, GetNameForControl(ctl)), "ctl"); + + containerCache.Add(ctl, ctl); + + if (assocContainer == null) { + ISite site = ctl.Site; + if (site != null) { + assocContainer = site.Container; + IComponentChangeService ccs = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + if (ccs != null) { + ccs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved); + } + } + } + } + + internal void RemoveControl(Control ctl) { + //ctl may not be in containerCache: Remove is a no-op if it's not there. + containerCache.Remove(ctl); + } + + internal static WebBrowserContainer FindContainerForControl(WebBrowserBase ctl) { + if (ctl != null) { + if (ctl.container != null) + { + return ctl.container; + } + ScrollableControl f = ctl.ContainingControl; + if (f != null) { + WebBrowserContainer container = ctl.CreateWebBrowserContainer(); + if (container.RegisterControl(ctl)) { + container.AddControl(ctl); + return container; + } + } + } + return null; + } + + internal string GetNameForControl(Control ctl) { + string name = (ctl.Site != null) ? ctl.Site.Name : ctl.Name; + return name ?? ""; + } + + internal void OnUIActivate(WebBrowserBase site) { + // The ShDocVw control repeatedly calls OnUIActivate() with the same + // site. This causes the assert below to fire. + // + if (siteUIActive == site) + return; + + if (siteUIActive != null && siteUIActive != site) { + WebBrowserBase tempSite = siteUIActive; + tempSite.AXInPlaceObject.UIDeactivate(); + } + site.AddSelectionHandler(); + Debug.Assert(siteUIActive == null, "Object did not call OnUIDeactivate"); + siteUIActive = site; + ContainerControl f = site.ContainingControl; + if (f != null && f.Contains(site)) { + f.SetActiveControlInternal(site); + } + } + + internal void OnUIDeactivate(WebBrowserBase site) { +#if DEBUG + if (siteUIActive != null) { + Debug.Assert(siteUIActive == site, "deactivating when not active..."); + } +#endif // DEBUG + + siteUIActive = null; + site.RemoveSelectionHandler(); + site.SetSelectionStyle(WebBrowserHelper.SelectionStyle.Selected); + site.SetEditMode(WebBrowserHelper.AXEditMode.None); + } + + internal void OnInPlaceDeactivate(WebBrowserBase site) { + if (siteActive == site) { + siteActive = null; + ContainerControl parentContainer = parent.FindContainerControlInternal(); + if (parentContainer != null) { + parentContainer.SetActiveControlInternal(null); + } + } + } + + internal void OnExitEditMode(WebBrowserBase ctl) { + Debug.Assert(ctlInEditMode == null || ctlInEditMode == ctl, "who is exiting edit mode?"); + if (ctlInEditMode == ctl) { + ctlInEditMode = null; + } + } + } + +} diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserDocumentCompletedEventHandler.cs b/WindowsForms/Managed/System/WinForms/WebBrowserDocumentCompletedEventHandler.cs new file mode 100644 index 000000000..117d67c2b --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserDocumentCompletedEventHandler.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Net; + +namespace System.Windows.Forms { + + /// + /// + /// + /// Delegate to the WebBrowser DocumentCompleted event. + /// + /// + public delegate void WebBrowserDocumentCompletedEventHandler(object sender, WebBrowserDocumentCompletedEventArgs e); + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class WebBrowserDocumentCompletedEventArgs : EventArgs { + private Uri url; + + /// + /// + /// + /// Creates an instance of the class. + /// + /// + public WebBrowserDocumentCompletedEventArgs(Uri url) { + this.url = url; + } + + /// + /// + /// + /// Url of the Document. + /// + /// + public Uri Url { + get { + WebBrowser.EnsureUrlConnectPermission(url); + return this.url; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserHelper.cs b/WindowsForms/Managed/System/WinForms/WebBrowserHelper.cs new file mode 100644 index 000000000..4279a6810 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserHelper.cs @@ -0,0 +1,170 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using System.Configuration.Assemblies; +using System.Runtime.Remoting; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.ComponentModel; +using System.Diagnostics; +using System; +using System.Reflection; +using System.Globalization; +using System.Security.Permissions; +using Microsoft.Win32; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Drawing; +using System.Windows.Forms.Design; +using System.Windows.Forms.ComponentModel; +using System.Windows.Forms.ComponentModel.Com2Interop; +using System.ComponentModel.Design; +using System.Windows.Forms; +using System.Drawing.Imaging; +using System.Drawing.Design; + +namespace System.Windows.Forms { + // + // This class contains static properties/methods that are internal. + // It also has types that make sense only for ActiveX hosting classes. + // In other words, this is a helper class for the ActiveX hosting classes. + // + internal static class WebBrowserHelper { + // + // Types: + // + + // + // Enumeration of the different states of the ActiveX control + internal enum AXState { + Passive = 0, // Not loaded + Loaded = 1, // Loaded, but no server [ocx created] + Running = 2, // Server running, invisible [depersisted] + InPlaceActive = 4, // Server in-place active [visible] + UIActive = 8 // Used only by WebBrowserSiteBase + } + // + // Enumeration of the different Edit modes + internal enum AXEditMode { + None = 0, // object not being edited + Object = 1, // object provided an edit verb and we invoked it + Host = 2 // we invoked our own edit verb + }; + // + // Enumeration of Selection Styles + internal enum SelectionStyle { + NotSelected = 0, + Selected = 1, + Active = 2 + }; + + + // + // Static members: + // + + // + // BitVector32 masks for various internal state flags. + internal static readonly int sinkAttached = BitVector32.CreateMask(); + internal static readonly int manualUpdate = BitVector32.CreateMask(sinkAttached); + internal static readonly int setClientSiteFirst = BitVector32.CreateMask(manualUpdate); + internal static readonly int addedSelectionHandler = BitVector32.CreateMask(setClientSiteFirst); + internal static readonly int siteProcessedInputKey = BitVector32.CreateMask(addedSelectionHandler); + internal static readonly int inTransition = BitVector32.CreateMask(siteProcessedInputKey); + internal static readonly int processingKeyUp = BitVector32.CreateMask(inTransition); + internal static readonly int isMaskEdit = BitVector32.CreateMask(processingKeyUp); + internal static readonly int recomputeContainingControl = BitVector32.CreateMask(isMaskEdit); + // + // Gets the LOGPIXELSX of the screen DC. + private static int logPixelsX = -1; + private static int logPixelsY = -1; + private const int HMperInch = 2540; + // + // Special guids + private static Guid ifont_Guid = typeof(UnsafeNativeMethods.IFont).GUID; + internal static Guid windowsMediaPlayer_Clsid = new Guid("{22d6f312-b0f6-11d0-94ab-0080c74c7e95}"); + internal static Guid comctlImageCombo_Clsid = new Guid("{a98a24c0-b06f-3684-8c12-c52ae341e0bc}"); + internal static Guid maskEdit_Clsid = new Guid("{c932ba85-4374-101b-a56c-00aa003668dc}"); + // + // Window message to check if we have already sub-classed + internal static readonly int REGMSG_MSG = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_subclassCheck"); + internal const int REGMSG_RETVAL = 123; + + + // + // Static helper methods: + // + + internal static int Pix2HM(int pix, int logP) { + return(HMperInch * pix + ( logP >> 1)) / logP; + } + + internal static int HM2Pix(int hm, int logP) { + return(logP * hm + HMperInch / 2) / HMperInch; + } + + // + // We cache LOGPIXELSX for optimization + internal static int LogPixelsX { + get { + if (logPixelsX == -1) { + IntPtr hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + if (hDC != IntPtr.Zero) { + logPixelsX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSX); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); + } + } + return logPixelsX; + } + } + internal static void ResetLogPixelsX() { + logPixelsX = -1; + } + + // + // We cache LOGPIXELSY for optimization + internal static int LogPixelsY { + get { + if (logPixelsY == -1) { + IntPtr hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); + if (hDC != IntPtr.Zero) { + logPixelsY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSY); + UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); + } + } + return logPixelsY; + } + } + internal static void ResetLogPixelsY() { + logPixelsY = -1; + } + + + // + // Gets the selection service from the control's site + internal static ISelectionService GetSelectionService(Control ctl) { + ISite site = ctl.Site; + if (site != null) { + Object o = site.GetService(typeof(ISelectionService)); + Debug.Assert(o == null || o is ISelectionService, "service must implement ISelectionService"); + if (o is ISelectionService) { + return(ISelectionService) o; + } + } + return null; + } + + // + // Returns a big COMRECT + internal static NativeMethods.COMRECT GetClipRect() { + return new NativeMethods.COMRECT(new Rectangle(0, 0, 32000, 32000)); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserNavigatedEventHandler.cs b/WindowsForms/Managed/System/WinForms/WebBrowserNavigatedEventHandler.cs new file mode 100644 index 000000000..eb3fb28d8 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserNavigatedEventHandler.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +using System.Net; + +namespace System.Windows.Forms { + + /// + /// + /// + /// Delegate to the WebBrowser Navigated event. + /// + /// + public delegate void WebBrowserNavigatedEventHandler(object sender, WebBrowserNavigatedEventArgs e); + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class WebBrowserNavigatedEventArgs : EventArgs { + private Uri url; + + /// + /// + /// + /// Creates an instance of the class. + /// + /// + public WebBrowserNavigatedEventArgs(Uri url) { + this.url = url; + } + + /// + /// + /// + /// Url the browser navigated to. + /// + /// + public Uri Url { + get { + WebBrowser.EnsureUrlConnectPermission(url); + return this.url; + } + } + } + +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserNavigatingEventHandler.cs b/WindowsForms/Managed/System/WinForms/WebBrowserNavigatingEventHandler.cs new file mode 100644 index 000000000..63608ee18 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserNavigatingEventHandler.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +using System.Net; +using System.ComponentModel; + +namespace System.Windows.Forms { + + /// + /// + /// + /// Delegate to the WebBrowser Navigating event. + /// + /// + public delegate void WebBrowserNavigatingEventHandler(object sender, WebBrowserNavigatingEventArgs e); + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class WebBrowserNavigatingEventArgs : CancelEventArgs { + private Uri url; + private string targetFrameName; + + /// + /// + /// + /// Creates an instance of the class. + /// + /// + public WebBrowserNavigatingEventArgs(Uri url, string targetFrameName) { + this.url = url; + this.targetFrameName = targetFrameName; + } + + /// + /// + /// + /// Url the browser is navigating to. + /// + /// + public Uri Url { + get { + WebBrowser.EnsureUrlConnectPermission(url); + return this.url; + } + } + + /// + /// + /// + /// In case an individual frame is about to be navigated, this contains the frame name. + /// + /// + public string TargetFrameName { + get { + WebBrowser.EnsureUrlConnectPermission(url); + return this.targetFrameName; + } + } + } + +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserProgressChangedEventHandler.cs b/WindowsForms/Managed/System/WinForms/WebBrowserProgressChangedEventHandler.cs new file mode 100644 index 000000000..ed58d8ba2 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserProgressChangedEventHandler.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + + +namespace System.Windows.Forms { + + /// + /// + /// + /// Delegate to the WebBrowser ProgressChanged event. + /// + /// + public delegate void WebBrowserProgressChangedEventHandler(object sender, WebBrowserProgressChangedEventArgs e); + + /// + /// + /// + /// Provides data for the event. + /// + /// + public class WebBrowserProgressChangedEventArgs : EventArgs { + private long currentProgress; + private long maximumProgress; + + /// + /// + /// + /// Creates an instance of the class. + /// + /// + public WebBrowserProgressChangedEventArgs(long currentProgress, long maximumProgress) { + this.currentProgress = currentProgress; + this.maximumProgress = maximumProgress; + } + + /// + /// + /// + /// Specifies current number of bytes donwloaded. CurrentProgress/MaximumProgress*100 = progress percentage. + /// + /// + public long CurrentProgress { + get { + return currentProgress; + } + } + + /// + /// + /// + /// Specifies total number of bytes of item being downloaded. + /// CurrentProgress/MaximumProgress*100 = progress percentage. + /// + /// + public long MaximumProgress { + get { + return maximumProgress; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserSiteBase.cs b/WindowsForms/Managed/System/WinForms/WebBrowserSiteBase.cs new file mode 100644 index 000000000..0002d0828 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserSiteBase.cs @@ -0,0 +1,500 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using System.Configuration.Assemblies; +using System.Runtime.Remoting; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.ComponentModel; +using System.Diagnostics; +using System; +using System.Reflection; +using System.Globalization; +using System.Security.Permissions; +using Microsoft.Win32; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Drawing; +using System.Windows.Forms.Design; +using System.Windows.Forms.ComponentModel; +using System.Windows.Forms.ComponentModel.Com2Interop; +using System.ComponentModel.Design; +using System.Windows.Forms; +using System.Drawing.Imaging; +using System.Drawing.Design; +using System.Security; + +namespace System.Windows.Forms { + /// + /// + /// + /// This class implements the necessary interfaces required for an ActiveX site. + /// + /// This class is public, but has an internal constructor so that external + /// users can only reference the Type (cannot instantiate it directly). + /// Other classes have to inherit this class and expose it to the outside world. + /// + /// This class does not have any public property/method/event by itself. + /// All implementations of the site interface methods are private, which + /// means that inheritors who want to override even a single method of one + /// of these interfaces will have to implement the whole interface. + /// + /// + public class WebBrowserSiteBase + : UnsafeNativeMethods.IOleControlSite, UnsafeNativeMethods.IOleClientSite, UnsafeNativeMethods.IOleInPlaceSite, UnsafeNativeMethods.ISimpleFrameSite, UnsafeNativeMethods.IPropertyNotifySink, IDisposable { + + private WebBrowserBase host; + private AxHost.ConnectionPointCookie connectionPoint; + + // + // The constructor takes an WebBrowserBase as a parameter, so unfortunately, + // this cannot be used as a standalone site. It has to be used in conjunction + // with WebBrowserBase. Perhaps we can change it in future. + // + internal WebBrowserSiteBase(WebBrowserBase h) { + if (h == null) { + throw new ArgumentNullException("h"); + } + this.host = h; + } + + /// + /// + /// + /// Dispose(release the cookie) + /// + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// + /// + /// Release the cookie if we're disposing + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + StopEvents(); + } + } + /// + /// + /// + /// Retrieves the WebBrowserBase object set in the constructor. + /// + /// + internal WebBrowserBase Host { + get { + return this.host; + } + } + + // + // Interface implementations: + // + + // + // IOleControlSite methods: + // + /// + /// + int UnsafeNativeMethods.IOleControlSite.OnControlInfoChanged() { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.LockInPlaceActive(int fLock) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.GetExtendedControl(out object ppDisp) { + ppDisp = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.TransformCoords(NativeMethods._POINTL pPtlHimetric, NativeMethods.tagPOINTF pPtfContainer, int dwFlags) { + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_HIMETRICTOCONTAINER) != 0) { + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_SIZE) != 0) { + pPtfContainer.x = (float) WebBrowserHelper.HM2Pix(pPtlHimetric.x, WebBrowserHelper.LogPixelsX); + pPtfContainer.y = (float) WebBrowserHelper.HM2Pix(pPtlHimetric.y, WebBrowserHelper.LogPixelsY); + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_POSITION) != 0) { + pPtfContainer.x = (float) WebBrowserHelper.HM2Pix(pPtlHimetric.x, WebBrowserHelper.LogPixelsX); + pPtfContainer.y = (float) WebBrowserHelper.HM2Pix(pPtlHimetric.y, WebBrowserHelper.LogPixelsY); + } + else { + return NativeMethods.E_INVALIDARG; + } + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_CONTAINERTOHIMETRIC) != 0) { + if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_SIZE) != 0) { + pPtlHimetric.x = WebBrowserHelper.Pix2HM((int)pPtfContainer.x, WebBrowserHelper.LogPixelsX); + pPtlHimetric.y = WebBrowserHelper.Pix2HM((int)pPtfContainer.y, WebBrowserHelper.LogPixelsY); + } + else if ((dwFlags & NativeMethods.ActiveX.XFORMCOORDS_POSITION) != 0) { + pPtlHimetric.x = WebBrowserHelper.Pix2HM((int)pPtfContainer.x, WebBrowserHelper.LogPixelsX); + pPtlHimetric.y = WebBrowserHelper.Pix2HM((int)pPtfContainer.y, WebBrowserHelper.LogPixelsY); + } + else { + return NativeMethods.E_INVALIDARG; + } + } + else { + return NativeMethods.E_INVALIDARG; + } + + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.TranslateAccelerator(ref NativeMethods.MSG pMsg, int grfModifiers) { + Debug.Assert(!this.Host.GetAXHostState(WebBrowserHelper.siteProcessedInputKey), "Re-entering UnsafeNativeMethods.IOleControlSite.TranslateAccelerator!!!"); + this.Host.SetAXHostState(WebBrowserHelper.siteProcessedInputKey, true); + + Message msg = new Message(); + msg.Msg = pMsg.message; + msg.WParam = pMsg.wParam; + msg.LParam = pMsg.lParam; + msg.HWnd = pMsg.hwnd; + + try { + bool f = ((Control)this.Host).PreProcessControlMessage(ref msg) == PreProcessControlState.MessageProcessed; + return f ? NativeMethods.S_OK : NativeMethods.S_FALSE; + } + finally { + this.Host.SetAXHostState(WebBrowserHelper.siteProcessedInputKey, false); + } + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.OnFocus(int fGotFocus) { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleControlSite.ShowPropertyFrame() { + return NativeMethods.E_NOTIMPL; + } + + // + // IOleClientSite methods: + // + /// + /// + int UnsafeNativeMethods.IOleClientSite.SaveObject() { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IOleClientSite.GetMoniker(int dwAssign, int dwWhichMoniker, out Object moniker) { + moniker = null; + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IOleClientSite.GetContainer(out UnsafeNativeMethods.IOleContainer container) { + container = this.Host.GetParentContainer(); + return NativeMethods.S_OK; + } + + + /// + /// + int UnsafeNativeMethods.IOleClientSite.ShowObject() { + if (this.Host.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive) { + IntPtr hwnd; + if (NativeMethods.Succeeded(this.Host.AXInPlaceObject.GetWindow(out hwnd))) { + if (this.Host.GetHandleNoCreate() != hwnd) { + if (hwnd != IntPtr.Zero) { + this.Host.AttachWindow(hwnd); + this.OnActiveXRectChange(new NativeMethods.COMRECT(this.Host.Bounds)); + } + } + } + else if (this.Host.AXInPlaceObject is UnsafeNativeMethods.IOleInPlaceObjectWindowless) { + throw new InvalidOperationException(SR.GetString(SR.AXWindowlessControl)); + } + } + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleClientSite.OnShowWindow(int fShow) { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleClientSite.RequestNewObjectLayout() { + return NativeMethods.E_NOTIMPL; + } + + // + // IOleInPlaceSite methods: + // + /// + /// + IntPtr UnsafeNativeMethods.IOleInPlaceSite.GetWindow() { + try + { + return UnsafeNativeMethods.GetParent(new HandleRef(Host, Host.Handle)); + } + catch (Exception t) + { + Debug.Fail(t.ToString()); + throw; + } + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.ContextSensitiveHelp(int fEnterMode) { + return NativeMethods.E_NOTIMPL; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.CanInPlaceActivate() { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.OnInPlaceActivate() { + this.Host.ActiveXState = WebBrowserHelper.AXState.InPlaceActive; + this.OnActiveXRectChange(new NativeMethods.COMRECT(this.Host.Bounds)); + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.OnUIActivate() { + this.Host.ActiveXState = WebBrowserHelper.AXState.UIActive; + this.Host.GetParentContainer().OnUIActivate(this.Host); + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.GetWindowContext(out UnsafeNativeMethods.IOleInPlaceFrame ppFrame, out UnsafeNativeMethods.IOleInPlaceUIWindow ppDoc, + NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect, NativeMethods.tagOIFI lpFrameInfo) { + ppDoc = null; + ppFrame = this.Host.GetParentContainer(); + + lprcPosRect.left = this.Host.Bounds.X; + lprcPosRect.top = this.Host.Bounds.Y; + lprcPosRect.right = this.Host.Bounds.Width + this.Host.Bounds.X; + lprcPosRect.bottom = this.Host.Bounds.Height + this.Host.Bounds.Y; + + lprcClipRect = WebBrowserHelper.GetClipRect(); + if (lpFrameInfo != null) { + lpFrameInfo.cb = Marshal.SizeOf(typeof(NativeMethods.tagOIFI)); + lpFrameInfo.fMDIApp = false; + lpFrameInfo.hAccel = IntPtr.Zero; + lpFrameInfo.cAccelEntries = 0; + lpFrameInfo.hwndFrame = (this.Host.ParentInternal == null) ? IntPtr.Zero : this.Host.ParentInternal.Handle; + } + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.Scroll(NativeMethods.tagSIZE scrollExtant) { + return NativeMethods.S_FALSE; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.OnUIDeactivate(int fUndoable) { + this.Host.GetParentContainer().OnUIDeactivate(this.Host); + if (this.Host.ActiveXState > WebBrowserHelper.AXState.InPlaceActive) { + this.Host.ActiveXState = WebBrowserHelper.AXState.InPlaceActive; + } + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.OnInPlaceDeactivate() { + if (this.Host.ActiveXState == WebBrowserHelper.AXState.UIActive) { + ((UnsafeNativeMethods.IOleInPlaceSite)this).OnUIDeactivate(0); + } + + this.Host.GetParentContainer().OnInPlaceDeactivate(this.Host); + this.Host.ActiveXState = WebBrowserHelper.AXState.Running; + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.DiscardUndoState() { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.DeactivateAndUndo() { + return this.Host.AXInPlaceObject.UIDeactivate(); + } + + /// + /// + int UnsafeNativeMethods.IOleInPlaceSite.OnPosRectChange(NativeMethods.COMRECT lprcPosRect) { + return this.OnActiveXRectChange(lprcPosRect); + } + + // + // ISimpleFrameSite methods: + // + /// + /// + int UnsafeNativeMethods.ISimpleFrameSite.PreMessageFilter(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref IntPtr plResult, ref int pdwCookie) { + return NativeMethods.S_OK; + } + + /// + /// + int UnsafeNativeMethods.ISimpleFrameSite.PostMessageFilter(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref IntPtr plResult, int dwCookie) { + return NativeMethods.S_FALSE; + } + + // + // IPropertyNotifySink methods: + // + /// + /// + void UnsafeNativeMethods.IPropertyNotifySink.OnChanged(int dispid) { + // Some controls fire OnChanged() notifications when getting values of some properties. ASURT 20190. + // To prevent this kind of recursion, we check to see if we are already inside a OnChanged() call. + // + if (this.Host.NoComponentChangeEvents != 0) + return; + + this.Host.NoComponentChangeEvents++; + try + { + OnPropertyChanged(dispid); + } + catch (Exception t) + { + Debug.Fail(t.ToString()); + throw; + } + finally { + this.Host.NoComponentChangeEvents--; + } + } + + /// + /// + int UnsafeNativeMethods.IPropertyNotifySink.OnRequestEdit(int dispid) { + return NativeMethods.S_OK; + } + + + // + // Virtual overrides: + // + internal virtual void OnPropertyChanged(int dispid) { + try + { + ISite site = this.Host.Site; + if (site != null) + { + IComponentChangeService changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + + if (changeService != null) + { + try + { + changeService.OnComponentChanging(this.Host, null); + } + catch (CheckoutException coEx) + { + if (coEx == CheckoutException.Canceled) + { + return; + } + throw coEx; + } + + // Now notify the change service that the change was successful. + // + changeService.OnComponentChanged(this.Host, null, null, null); + } + } + } + catch (Exception t) + { + Debug.Fail(t.ToString()); + throw; + } + } + + + // + // Internal helper methods: + // + internal WebBrowserBase GetAXHost() { + return this.Host; + } + + internal void StartEvents() { + if (connectionPoint != null) + return; + + Object nativeObject = this.Host.activeXInstance; + if (nativeObject != null) { + try + { + connectionPoint = new AxHost.ConnectionPointCookie(nativeObject, this, typeof(UnsafeNativeMethods.IPropertyNotifySink)); + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + } + } + } + + internal void StopEvents() { + if (connectionPoint != null) { + connectionPoint.Disconnect(); + connectionPoint = null; + } + } + + private int OnActiveXRectChange(NativeMethods.COMRECT lprcPosRect) { + this.Host.AXInPlaceObject.SetObjectRects( + NativeMethods.COMRECT.FromXYWH(0, 0, lprcPosRect.right - lprcPosRect.left, lprcPosRect.bottom - lprcPosRect.top), + WebBrowserHelper.GetClipRect()); + this.Host.MakeDirty(); + return NativeMethods.S_OK; + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WebBrowserUriTypeConverter.cs b/WindowsForms/Managed/System/WinForms/WebBrowserUriTypeConverter.cs new file mode 100644 index 000000000..e46705989 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WebBrowserUriTypeConverter.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +using System; +using System.ComponentModel; + +namespace System.Windows.Forms +{ + class WebBrowserUriTypeConverter : UriTypeConverter + { + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + //The UriTypeConverter gives back a relative Uri for things like "www.microsoft.com". If + //the Uri is relative, we'll try sticking "http://" on the front to see whether that fixes it up. + Uri uri = base.ConvertFrom(context, culture, value) as Uri; + if (uri != null && !string.IsNullOrEmpty(uri.OriginalString) && !uri.IsAbsoluteUri) + { + try + { + uri = new Uri("http://" + uri.OriginalString.Trim()); + } + catch (UriFormatException) + { + //We can't throw "http://" on the front: just return the original (relative) Uri, + //which will throw an exception with reasonable text later. + } + } + return uri; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/WinCategoryAttribute.cs b/WindowsForms/Managed/System/WinForms/WinCategoryAttribute.cs new file mode 100644 index 000000000..409e4852c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WinCategoryAttribute.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System; + using System.ComponentModel; + using System.Diagnostics; + + /// + /// + /// + /// + /// CategoryAttribute that can access Microsoft localized strings. + /// + /// + [AttributeUsage(AttributeTargets.All)] + internal sealed class WinCategoryAttribute : CategoryAttribute { + + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public WinCategoryAttribute(string category) : base(category) { + } + + /// + /// + /// This method is called the first time the category property + /// is accessed. It provides a way to lookup a localized string for + /// the given category. Classes may override this to add their + /// own localized names to categories. If a localized string is + /// available for the given value, the method should return it. + /// Otherwise, it should return null. + /// + protected override string GetLocalizedString(string value) { + string localizedValue = base.GetLocalizedString(value); + if (localizedValue == null) { + localizedValue = (string)SR.GetObject("WinFormsCategory" + value); + } + // This attribute is internal, and we should never have a missing resource string. + // + Debug.Assert(localizedValue != null, "All Windows Forms category attributes should have localized strings. Category '" + value + "' not found."); + return localizedValue; + } + } +} diff --git a/WindowsForms/Managed/System/WinForms/WinFormsSecurity.cs b/WindowsForms/Managed/System/WinForms/WinFormsSecurity.cs new file mode 100644 index 000000000..e6f8ebbf9 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WinFormsSecurity.cs @@ -0,0 +1,510 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +/* + */ +namespace System.Windows.Forms { + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.IO; + using System.Security.Permissions; + using System.Security; + using System.Drawing.Printing; + using System.Runtime.Versioning; + + internal static class IntSecurity { + public static readonly TraceSwitch SecurityDemand = new TraceSwitch("SecurityDemand", "Trace when security demands occur."); +#if DEBUG + public static readonly BooleanSwitch MapSafeTopLevelToSafeSub = new BooleanSwitch("MapSafeTopLevelToSafeSub", "Maps the SafeTopLevelWindow UI permission to SafeSubWindow permission. Must restart to take effect."); +#endif + + private static CodeAccessPermission adjustCursorClip; + private static CodeAccessPermission affectMachineState; + private static CodeAccessPermission affectThreadBehavior; + private static CodeAccessPermission allPrinting; + private static PermissionSet allPrintingAndUnmanagedCode; // Can't assert twice in the same method + private static CodeAccessPermission allWindows; + private static CodeAccessPermission clipboardRead; + private static CodeAccessPermission clipboardOwn; + private static PermissionSet clipboardWrite; // Can't assert twice in the same method + private static CodeAccessPermission changeWindowRegionForTopLevel; + private static CodeAccessPermission controlFromHandleOrLocation; + private static CodeAccessPermission createAnyWindow; + private static CodeAccessPermission createGraphicsForControl; + private static CodeAccessPermission defaultPrinting; + private static CodeAccessPermission fileDialogCustomization; + private static CodeAccessPermission fileDialogOpenFile; + private static CodeAccessPermission fileDialogSaveFile; + private static CodeAccessPermission getCapture; + private static CodeAccessPermission getParent; + private static CodeAccessPermission manipulateWndProcAndHandles; + private static CodeAccessPermission modifyCursor; + private static CodeAccessPermission modifyFocus; + private static CodeAccessPermission objectFromWin32Handle; + private static CodeAccessPermission safePrinting; + private static CodeAccessPermission safeSubWindows; + private static CodeAccessPermission safeTopLevelWindows; + private static CodeAccessPermission sendMessages; + private static CodeAccessPermission sensitiveSystemInformation; + private static CodeAccessPermission transparentWindows; + private static CodeAccessPermission topLevelWindow; + private static CodeAccessPermission unmanagedCode; + private static CodeAccessPermission unrestrictedWindows; + private static CodeAccessPermission windowAdornmentModification; + + /* Unused permissions + private static CodeAccessPermission win32HandleManipulation; + private static CodeAccessPermission minimizeWindowProgrammatically; + private static CodeAccessPermission restrictedWebBrowserPermission; + private static CodeAccessPermission noPrinting; + private static CodeAccessPermission topMostWindow; + private static CodeAccessPermission unrestrictedEnvironment; + private static CodeAccessPermission autoComplete; + private static CodeAccessPermission defaultWebBrowserPermission; + private static CodeAccessPermission screenDC; + */ + + // + // Property accessors for permissions. Don't allocate permissions up front -- always + // demand create them. A great many codepaths never need all these permissions so it is wasteful to + // create them. + // + + + public static CodeAccessPermission AdjustCursorClip { + get { + if (adjustCursorClip == null) { + adjustCursorClip = AllWindows; + } + return adjustCursorClip; + } + } + + public static CodeAccessPermission AdjustCursorPosition { + get { + return AllWindows; + } + } + + public static CodeAccessPermission AffectMachineState { + get { + if (affectMachineState == null) { + affectMachineState = UnmanagedCode; + } + return affectMachineState; + } + } + + public static CodeAccessPermission AffectThreadBehavior { + get { + if (affectThreadBehavior == null) { + affectThreadBehavior = UnmanagedCode; + } + return affectThreadBehavior; + } + } + + public static CodeAccessPermission AllPrinting { + get { + if (allPrinting == null) { + allPrinting = new PrintingPermission(PrintingPermissionLevel.AllPrinting); + } + return allPrinting; + } + } + + public static PermissionSet AllPrintingAndUnmanagedCode { // Can't assert twice in the same method. See ASURT 52788. + get { + if (allPrintingAndUnmanagedCode == null) { + PermissionSet temp = new PermissionSet(PermissionState.None); + temp.SetPermission(IntSecurity.UnmanagedCode); + temp.SetPermission(IntSecurity.AllPrinting); + allPrintingAndUnmanagedCode = temp; + } + return allPrintingAndUnmanagedCode; + } + } + + public static CodeAccessPermission AllWindows { + get { + if (allWindows == null) { + allWindows = new UIPermission(UIPermissionWindow.AllWindows); + } + return allWindows; + } + } + + public static CodeAccessPermission ClipboardRead { + get { + if (clipboardRead == null) { + clipboardRead = new UIPermission(UIPermissionClipboard.AllClipboard); + } + return clipboardRead; + } + } + + public static CodeAccessPermission ClipboardOwn { + get { + if (clipboardOwn == null) { + clipboardOwn = new UIPermission(UIPermissionClipboard.OwnClipboard); + } + return clipboardOwn; + } + } + + public static PermissionSet ClipboardWrite { // Can't assert OwnClipboard & UnmanagedCode in the same context, need permission set. + get { + if (clipboardWrite == null) { + clipboardWrite = new PermissionSet(PermissionState.None); + clipboardWrite.SetPermission(IntSecurity.UnmanagedCode); + clipboardWrite.SetPermission(IntSecurity.ClipboardOwn); + } + return clipboardWrite; + } + } + + public static CodeAccessPermission ChangeWindowRegionForTopLevel { + get { + if (changeWindowRegionForTopLevel == null) { + changeWindowRegionForTopLevel = AllWindows; + } + return changeWindowRegionForTopLevel; + } + } + + public static CodeAccessPermission ControlFromHandleOrLocation { + get { + if (controlFromHandleOrLocation == null) { + controlFromHandleOrLocation = AllWindows; + } + return controlFromHandleOrLocation; + } + } + + public static CodeAccessPermission CreateAnyWindow { + get { + if (createAnyWindow == null) { + createAnyWindow = SafeSubWindows; + } + return createAnyWindow; + } + } + + public static CodeAccessPermission CreateGraphicsForControl { + get { + if (createGraphicsForControl == null) { + createGraphicsForControl = SafeSubWindows; + } + return createGraphicsForControl; + } + } + + public static CodeAccessPermission DefaultPrinting { + get { + if (defaultPrinting == null) { + defaultPrinting = new PrintingPermission(PrintingPermissionLevel.DefaultPrinting); + } + return defaultPrinting; + } + } + + public static CodeAccessPermission FileDialogCustomization { + get { + if (fileDialogCustomization == null) { + fileDialogCustomization = new FileIOPermission(PermissionState.Unrestricted); + } + return fileDialogCustomization; + } + } + + public static CodeAccessPermission FileDialogOpenFile { + get { + if (fileDialogOpenFile == null) { + fileDialogOpenFile = new FileDialogPermission(FileDialogPermissionAccess.Open); + } + return fileDialogOpenFile; + } + } + + public static CodeAccessPermission FileDialogSaveFile { + get { + if (fileDialogSaveFile == null) { + fileDialogSaveFile = new FileDialogPermission(FileDialogPermissionAccess.Save); + } + return fileDialogSaveFile; + } + } + + /* Unused + public static CodeAccessPermission RestrictedWebBrowserPermission { + get { + if (restrictedWebBrowserPermission == null) { + restrictedWebBrowserPermission = new WebBrowserPermission(WebBrowserPermissionLevel.Restricted); + } + return restrictedWebBrowserPermission; + } + } + + public static CodeAccessPermission DefaultWebBrowserPermission { + get { + if (defaultWebBrowserPermission == null) { + defaultWebBrowserPermission = new WebBrowserPermission(WebBrowserPermissionLevel.Default); + } + return defaultWebBrowserPermission; + } + } + */ + + public static CodeAccessPermission GetCapture { + get { + if (getCapture == null) { + getCapture = AllWindows; + } + return getCapture; + } + } + + public static CodeAccessPermission GetParent { + get { + if (getParent == null) { + getParent = AllWindows; + } + return getParent; + } + } + + public static CodeAccessPermission ManipulateWndProcAndHandles { + get { + if (manipulateWndProcAndHandles == null) { + manipulateWndProcAndHandles = AllWindows; + } + return manipulateWndProcAndHandles; + } + } + + /* Unused + public static CodeAccessPermission MinimizeWindowProgrammatically { + get { + if (minimizeWindowProgrammatically == null) { + minimizeWindowProgrammatically = AllWindows; + } + return minimizeWindowProgrammatically; + } + } + */ + + public static CodeAccessPermission ModifyCursor { + get { + if (modifyCursor == null) { + modifyCursor = SafeSubWindows; + } + return modifyCursor; + } + } + + public static CodeAccessPermission ModifyFocus { + get { + if (modifyFocus == null) { + modifyFocus = AllWindows; + } + return modifyFocus; + } + } + + /* Unused + public static CodeAccessPermission NoPrinting { + get { + if (noPrinting == null) { + noPrinting = new PrintingPermission(PrintingPermissionLevel.NoPrinting); + } + return noPrinting; + } + } + */ + + public static CodeAccessPermission ObjectFromWin32Handle { + get { + if (objectFromWin32Handle == null) { + objectFromWin32Handle = UnmanagedCode; + } + return objectFromWin32Handle; + } + } + + public static CodeAccessPermission SafePrinting { + get { + if (safePrinting == null) { + safePrinting = new PrintingPermission(PrintingPermissionLevel.SafePrinting); + } + return safePrinting; + } + } + + public static CodeAccessPermission SafeSubWindows { + get { + if (safeSubWindows == null) { + safeSubWindows = new UIPermission(UIPermissionWindow.SafeSubWindows); + } + return safeSubWindows; + } + } + + public static CodeAccessPermission SafeTopLevelWindows { + get { + if (safeTopLevelWindows == null) { + safeTopLevelWindows = new UIPermission(UIPermissionWindow.SafeTopLevelWindows); + } + return safeTopLevelWindows; + } + } + + public static CodeAccessPermission SendMessages { + get { + if (sendMessages == null) { + sendMessages = UnmanagedCode; + } + return sendMessages; + } + } + + public static CodeAccessPermission SensitiveSystemInformation { + get { + if (sensitiveSystemInformation == null) { + sensitiveSystemInformation = new EnvironmentPermission(PermissionState.Unrestricted); + } + return sensitiveSystemInformation; + } + } + + /* Unused + public static CodeAccessPermission ScreenDC { + get { + if (screenDC == null) { + screenDC = AllWindows; + } + return screenDC; + } + } + */ + + public static CodeAccessPermission TransparentWindows { + get { + if (transparentWindows == null) { + transparentWindows = AllWindows; + } + return transparentWindows; + } + } + + public static CodeAccessPermission TopLevelWindow { + get { + if (topLevelWindow == null) { + topLevelWindow = SafeTopLevelWindows; + } + return topLevelWindow; + } + } + + /* Unused + public static CodeAccessPermission TopMostWindow { + get { + if (topMostWindow == null) { + topMostWindow = AllWindows; + } + return topMostWindow; + } + } + */ + + public static CodeAccessPermission UnmanagedCode { + get { + if (unmanagedCode == null) { + unmanagedCode = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); + } + return unmanagedCode; + } + } + + /* + public static CodeAccessPermission UnrestrictedEnvironment { + get { + if (unrestrictedEnvironment == null) { + unrestrictedEnvironment = new EnvironmentPermission(PermissionState.Unrestricted); + } + return unrestrictedEnvironment; + } + } + */ + + public static CodeAccessPermission UnrestrictedWindows { + get { + if (unrestrictedWindows == null) { + unrestrictedWindows = AllWindows; + } + return unrestrictedWindows; + } + } + + /* FXCop avoid unused code + public static CodeAccessPermission Win32HandleManipulation { + get { + if (win32HandleManipulation == null) { + win32HandleManipulation = UnmanagedCode; + } + return win32HandleManipulation; + } + } + */ + + public static CodeAccessPermission WindowAdornmentModification { + get { + if (windowAdornmentModification == null) { + windowAdornmentModification = AllWindows; + } + return windowAdornmentModification; + } + } + + /* unused + public static CodeAccessPermission AutoComplete { + get { + if (autoComplete == null) { + autoComplete = UnmanagedCode; + } + return autoComplete; + } + } + */ + + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + internal static string UnsafeGetFullPath(string fileName) { + string full = fileName; + + FileIOPermission fiop = new FileIOPermission( PermissionState.None ); + fiop.AllFiles = FileIOPermissionAccess.PathDiscovery; + fiop.Assert(); + try { + full = Path.GetFullPath(fileName); + } + finally { + CodeAccessPermission.RevertAssert(); + } + return full; + } + + /// SECREVIEW: ReviewImperativeSecurity + /// vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active. + /// reason for exclude: fileName is a local variable and not subject to race conditions. + [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] + internal static void DemandFileIO(FileIOPermissionAccess access, string fileName) { + new FileIOPermission(access, UnsafeGetFullPath(fileName)).Demand(); + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WinFormsUtils.cs b/WindowsForms/Managed/System/WinForms/WinFormsUtils.cs new file mode 100644 index 000000000..af7e0673c --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WinFormsUtils.cs @@ -0,0 +1,779 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.WindowsFormsUtils..ctor()")] + +/* + */ +namespace System.Windows.Forms { + using System.Runtime.Serialization.Formatters; + using System.Runtime.Remoting; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System; + using System.Security; + using System.Security.Permissions; + using System.Globalization; + using System.Windows.Forms; + using System.Drawing.Design; + using System.ComponentModel; + using System.Windows.Forms.ComponentModel; + using System.Collections; + using System.Drawing; + using System.Windows.Forms.Internal; + using Microsoft.Win32; + using System.Text; + using Util = NativeMethods.Util; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + using System.Reflection; + + // Miscellaneous Windows Forms utilities + internal sealed class WindowsFormsUtils { + + // A better initializer than Size.Empty to force code using uninitialized to noticably fail. + public static readonly Size UninitializedSize = new Size(-7199369, -5999471); + private static bool _targetsAtLeast_v4_5 = RunningOnCheck("TargetsAtLeast_Desktop_V4_5"); + + + public static readonly ContentAlignment AnyRightAlign = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight; + public static readonly ContentAlignment AnyLeftAlign = ContentAlignment.TopLeft | ContentAlignment.MiddleLeft | ContentAlignment.BottomLeft; + public static readonly ContentAlignment AnyTopAlign = ContentAlignment.TopLeft | ContentAlignment.TopCenter | ContentAlignment.TopRight; + public static readonly ContentAlignment AnyBottomAlign = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight; + public static readonly ContentAlignment AnyMiddleAlign = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight; + public static readonly ContentAlignment AnyCenterAlign = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter; + + /// + /// The GetMessagePos function retrieves the cursor position for the last message + /// retrieved by the GetMessage function. + /// + public static Point LastCursorPoint { + get { + int lastXY = SafeNativeMethods.GetMessagePos(); + return new Point(NativeMethods.Util.SignedLOWORD(lastXY),NativeMethods.Util.SignedHIWORD(lastXY)) ; + } + } + + /// this graphics requires disposal. + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public static Graphics CreateMeasurementGraphics() { + return Graphics.FromHdcInternal(WindowsGraphicsCacheManager.MeasurementGraphics.DeviceContext.Hdc); + } + + // If you want to know if a piece of text contains one and only one & + // this is your function. If you have a character "t" and want match it to &Text + // Control.IsMnemonic is a better bet. + public static bool ContainsMnemonic(string text) { + if (text != null) { + int textLength = text.Length; + int firstAmpersand = text.IndexOf('&', 0); + if (firstAmpersand >=0 && firstAmpersand <= /*second to last char=*/textLength -2) { + // we found one ampersand and it's either the first character + // or the second to last character + // or a character in between + + // We're so close! make sure we don't have a double ampersand now. + int secondAmpersand = text.IndexOf('&', firstAmpersand+1); + if (secondAmpersand == -1) { + // didn't find a second one in the string. + return true; + } + } + } + return false; + } + + internal static Rectangle ConstrainToScreenWorkingAreaBounds(Rectangle bounds) { + return ConstrainToBounds(Screen.GetWorkingArea(bounds), bounds); + } + + /// given a rectangle, constrain it to fit onto the current screen. + /// + internal static Rectangle ConstrainToScreenBounds(Rectangle bounds) { + return ConstrainToBounds(Screen.FromRectangle(bounds).Bounds, bounds); + } + + internal static Rectangle ConstrainToBounds(Rectangle constrainingBounds, Rectangle bounds) { + // use screen instead of SystemInformation.WorkingArea for better multimon support. + if (!constrainingBounds.Contains(bounds)) { + // make sure size does not exceed working area. + bounds.Size = new Size(Math.Min(constrainingBounds.Width -2, bounds.Width), + Math.Min(constrainingBounds.Height -2, bounds.Height)); + + // X calculations + // + // scooch so it will fit on the screen. + if (bounds.Right > constrainingBounds.Right) { + // its too far to the right. + bounds.X = constrainingBounds.Right - bounds.Width; + } + else if (bounds.Left < constrainingBounds.Left) { + // its too far to the left. + bounds.X = constrainingBounds.Left; + } + + // Y calculations + // + // scooch so it will fit on the screen. + if (bounds.Bottom > constrainingBounds.Bottom) { + // its too far to the bottom. + bounds.Y = constrainingBounds.Bottom - 1 - bounds.Height; + } + else if (bounds.Top < constrainingBounds.Top) { + // its too far to the top. + bounds.Y = constrainingBounds.Top; + } + } + return bounds; + } + +#if DEBUG_FOCUS + /// + /// FOCUS debugging code. This is really handy if you stick it in Application.Idle event. + /// It will watch when the focus has changed and will print out the new guy who's gotten focus. + /// If it is an unmanaged window, it will report the window text. You'll need to #define DEBUG_FOCUS + /// or use something like /define:DEBUG_FOCUS in the CSC_FLAGS and either sync the + /// Application.Idle event or stick this in Application.FDoIdle. + /// + /// This can be used in conjunction with the ControlKeyboardRouting trace switch to debug keyboard + /// handling problems. See Control.cs. + /// + [ThreadStatic] + private static IntPtr lastFocusHwnd = IntPtr.Zero; + internal static DebugFocus() { + IntPtr focusHwnd = UnsafeNativeMethods.GetFocus(); + if (focusHwnd != lastFocusHwnd) { + lastFocusHwnd = focusHwnd; + if (focusHwnd != IntPtr.Zero) { + Debug.WriteLine("FOCUS watch: new focus: " + focusHwnd.ToString() +GetControlInformation(focusHwnd) ); + } + else { + Debug.WriteLine("FOCUS watch: no one has focus"); + } + } + return false; + } +#endif + + // + // adds an extra & to to the text so that Fish & Chips can be displayed on a menu item without underlining + // anything. This is used in MDIWindowList as we use the MDIChildForm.Text as menu item text. + // Fish & Chips --> Fish && Chips + internal static string EscapeTextWithAmpersands(string text) { + if (text == null) { + return null; + } + + int index = text.IndexOf('&'); + + if (index == -1) { + return text; + } + + StringBuilder str = new StringBuilder(text.Substring(0, index)); + for(; index < text.Length; ++index) { + if (text[index] == '&') { + str.Append("&"); + } + if (index < text.Length) { + str.Append(text[index]); + } + } + return str.ToString(); + } + + // helper function for generating information about a particular control + // use AssertControlInformation if sticking in an assert - then the work + // to figure out the control info will only be done when the assertion is false. + internal static string GetControlInformation(IntPtr hwnd) { + if (hwnd == IntPtr.Zero) { + return "Handle is IntPtr.Zero"; + } + string ret = ""; // in RETAIL just return empty string +#if DEBUG + try { + int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(null, hwnd)); + StringBuilder sb = new StringBuilder(textLen+1); + UnsafeNativeMethods.GetWindowText(new HandleRef(null, hwnd), sb, sb.Capacity); + + string typeOfControl = "Unknown"; + string nameOfControl = "Name: "; + Control c = Control.FromHandle(hwnd); + if (c != null) { + typeOfControl = c.GetType().ToString(); + if (!string.IsNullOrEmpty(c.Name)) { + nameOfControl += c.Name; + } + else { + nameOfControl += "Unknown"; + + // some extra debug info for toolstripdropdowns... + if (c is ToolStripDropDown) { + ToolStripDropDown dd = c as ToolStripDropDown; + if (dd.OwnerItem != null) { + nameOfControl += "\r\n\tOwnerItem: " + dd.OwnerItem.ToString(); + } + } + } + } + ret = sb.ToString() + "\r\n\tType: " + typeOfControl + "\r\n\t" + nameOfControl + "\r\n"; + } + catch (SecurityException) { + // some suites run under DEBUG - just eat this exception + } +#endif + return ret; + } + + // only fetch the information on a false assertion. + internal static string AssertControlInformation(bool condition, Control control) { + if (condition) { + return string.Empty; + } + else { + return GetControlInformation(control.Handle); + } + } + + // Algorithm suggested by Damien Morton + internal static int GetCombinedHashCodes(params int[] args) + { + const int k = -1640531535; + int h = -757577119; + for (int i = 0; i < args.Length; i++) + { + h = (args[i] ^ h) * k; + } + return h; + } + + // Retrieves the mnemonic from a given string, or zero if no mnemonic. + // As used by the Control.Mnemonic to get mnemonic from Control.Text. + // + // Boolean argument determines whether returned char is converted to upper + // case or lower case (always one or the other - case is never preserved). + public static char GetMnemonic(string text, bool bConvertToUpperCase) { + char mnemonic = (char)0; + + if (text != null) { + int len = text.Length; + for (int i = 0; i < len - 1; i++) { + if (text[i] == '&') { + if (text[i + 1] == '&') + { + // we have an escaped &, so we need to skip it. + // + i++; + continue; + } + if (bConvertToUpperCase) { + mnemonic = Char.ToUpper(text[i+1], CultureInfo.CurrentCulture); + } + else { + mnemonic = Char.ToLower(text[i+1], CultureInfo.CurrentCulture); + } + break; + } + } + } + return mnemonic; + } + + // for a given handle, finds the toplevel handle + public static HandleRef GetRootHWnd(HandleRef hwnd) { + IntPtr rootHwnd = UnsafeNativeMethods.GetAncestor(new HandleRef(hwnd, hwnd.Handle), NativeMethods.GA_ROOT); + return new HandleRef(hwnd.Wrapper, rootHwnd); + } + + // for a given control, finds the toplevel handle + public static HandleRef GetRootHWnd(Control control) { + return GetRootHWnd(new HandleRef(control, control.Handle)); + } + + // Strips all keyboard mnemonic prefixes from a given string, eg. turning "He&lp" into "Help". + // Note: Be careful not to call this multiple times on the same string, otherwise you'll turn + // something like "Fi&sh && Chips" into "Fish & Chips" on the first call, and then "Fish Chips" + // on the second call. + public static string TextWithoutMnemonics(string text) { + if (text == null) { + return null; + } + + int index = text.IndexOf('&'); + + if (index == -1) { + return text; + } + + StringBuilder str = new StringBuilder(text.Substring(0, index)); + for(; index < text.Length; ++index) { + if (text[index] == '&') { + index++; // Skip this & and copy the next character instead + } + if (index < text.Length) { + str.Append(text[index]); + } + } + return str.ToString(); + } + + + /// Translates a point from one control's coordinate system to the other + /// same as: + /// controlTo.PointToClient(controlFrom.PointToScreen(point)) + /// but slightly more performant. + public static Point TranslatePoint(Point point, Control fromControl, Control toControl) { + NativeMethods.POINT pt = new NativeMethods.POINT(point.X, point.Y); + UnsafeNativeMethods.MapWindowPoints(new HandleRef(fromControl, fromControl.Handle), new HandleRef(toControl, toControl.Handle), pt, 1); + return new Point(pt.x, pt.y); + } + + + // Compares the strings using invariant culture for Turkish-I support. Returns true if they match. + // + // If your strings are symbolic (returned from APIs, not from user) the following calls + // are faster than this method: + // + // String.Equals(s1, s2, StringComparison.Ordinal) + // String.Equals(s1, s2, StringComparison.OrdinalIgnoreCase) + // + public static bool SafeCompareStrings(string string1, string string2, bool ignoreCase) { + if ((string1 == null) || (string2 == null)) { + // if either key is null, we should return false + return false; + } + + // Because String.Compare returns an ordering, it can not terminate early if lengths are not the same. + // Also, equivalent characters can be encoded in different byte sequences, so it can not necessarily + // terminate on the first byte which doesn't match. Hence this optimization. + if (string1.Length != string2.Length) { + return false; + } + + return String.Compare(string1, string2, ignoreCase, CultureInfo.InvariantCulture) == 0; + } + + // RotateLeft(0xFF000000, 4) -> 0xF000000F + public static int RotateLeft(int value, int nBits) { + Debug.Assert(Marshal.SizeOf(typeof(int)) == 4, "The impossible has happened."); + + nBits = nBits % 32; + return value << nBits | (value >> (32 - nBits)); + } + + public static string GetComponentName(IComponent component, string defaultNameValue) { + Debug.Assert(component != null, "component passed here cannot be null"); + string result = string.Empty; + if (string.IsNullOrEmpty(defaultNameValue)) { + if(component.Site != null) { + result = component.Site.Name; + } + if(result == null) { + result = string.Empty; + } + } else { + result = defaultNameValue; + } + return result; + } + + public static class EnumValidator { + + // IsValidContentAlignment + // Valid values are 0x001,0x002,0x004, 0x010,0x020,0x040, 0x100, 0x200,0x400 + // Method for verifying + // Verify that the number passed in has only one bit on + // Verify that the bit that is on is a valid bit by bitwise anding it to a mask. + // + public static bool IsValidContentAlignment(ContentAlignment contentAlign) { + if (ClientUtils.GetBitCount((uint)contentAlign) != 1) { + return false; + } + // to calculate: + // foreach (int val in Enum.GetValues(typeof(ContentAlignment))) { mask |= val; } + int contentAlignmentMask = 0x777; + return ((contentAlignmentMask & (int)contentAlign) != 0); + } + + // IsEnumWithinShiftedRange + // shifts off the number of bits specified by numBitsToShift + // - makes sure the bits we've shifted off are just zeros + // - then compares if the resulting value is between minValAfterShift and maxValAfterShift + // + // EXAMPLE: + // MessageBoxIcon. Valid values are 0x0, 0x10, 0x20, 0x30, 0x40 + // Method for verifying: chop off the last 0 by shifting right 4 bits, verify resulting number is between 0 & 4. + // + // WindowsFormsUtils.EnumValidator.IsEnumWithinShiftedRange(icon, /*numBitsToShift*/4, /*min*/0x0,/*max*/0x4) + // + public static bool IsEnumWithinShiftedRange(Enum enumValue, int numBitsToShift, int minValAfterShift, int maxValAfterShift){ + int iValue = Convert.ToInt32(enumValue, CultureInfo.InvariantCulture); + int remainder = iValue >> numBitsToShift; + if (remainder << numBitsToShift != iValue) { + // there were bits that we shifted out. + return false; + } + return (remainder >= minValAfterShift && remainder <= maxValAfterShift); + } + + // IsValidTextImageRelation + // valid values are 0,1,2,4,8 + // Method for verifying + // Verify that the number is between 0 and 8 + // Verify that the bit that is on - thus forcing it to be a power of two. + // + public static bool IsValidTextImageRelation(TextImageRelation relation) { + return ClientUtils.IsEnumValid(relation, (int)relation, (int)TextImageRelation.Overlay, (int)TextImageRelation.TextBeforeImage,1); + } + + public static bool IsValidArrowDirection(ArrowDirection direction) { + switch (direction) { + case ArrowDirection.Up: + case ArrowDirection.Down: + case ArrowDirection.Left: + case ArrowDirection.Right: + return true; + default: + return false; + } + } + } + + // To enumerate over only part of an array. + public class ArraySubsetEnumerator : IEnumerator { + private object[] array; // Perhaps this should really be typed Array, but then we suffer a performance penalty. + private int total; + private int current; + + public ArraySubsetEnumerator(object[] array, int count) { + Debug.Assert(count == 0 || array != null, "if array is null, count should be 0"); + Debug.Assert(array == null || count <= array.Length, "Trying to enumerate more than the array contains"); + this.array = array; + this.total = count; + current = -1; + } + + public bool MoveNext() { + if (current < total - 1) { + current++; + return true; + } + else + return false; + } + + public void Reset() { + current = -1; + } + + public object Current { + get { + if (current == -1) + return null; + else + return array[current]; + } + } + } + + /// + /// This is a ControlCollection which can be made readonly. In readonly mode, this + /// ControlCollection throws NotSupportedExceptions for any operation that attempts + /// to modify the collection. + /// + internal class ReadOnlyControlCollection : Control.ControlCollection { + + private readonly bool _isReadOnly; + + + public ReadOnlyControlCollection(Control owner, bool isReadOnly) : base(owner) { + _isReadOnly = isReadOnly; + } + + public override void Add(Control value) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + AddInternal(value); + } + + internal virtual void AddInternal(Control value) { + base.Add(value); + } + + public override void Clear() { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + base.Clear(); + } + + internal virtual void RemoveInternal(Control value) { + base.Remove(value); + } + + public override void RemoveByKey(string key) { + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + base.RemoveByKey(key); + } + + public override bool IsReadOnly { + get { return _isReadOnly; } + } + } + + /// + /// This control collection only allows a specific type of control + /// into the controls collection. It optionally supports readonlyness. + /// + internal class TypedControlCollection : ReadOnlyControlCollection { + + private Type typeOfControl; + private Control ownerControl; + + public TypedControlCollection(Control owner, Type typeOfControl, bool isReadOnly) : base(owner, isReadOnly) { + this.typeOfControl = typeOfControl; + this.ownerControl = owner; + } + + public TypedControlCollection(Control owner, Type typeOfControl) : base(owner, /*isReadOnly*/false) { + this.typeOfControl = typeOfControl; + this.ownerControl = owner; + } + + public override void Add(Control value) { + //Check parenting first for consistency + Control.CheckParentingCycle(ownerControl, value); + + if (value == null) { + throw new ArgumentNullException("value"); + } + if (IsReadOnly) { + throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection)); + } + if (!typeOfControl.IsAssignableFrom(value.GetType())) { + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.TypedControlCollectionShouldBeOfType, typeOfControl.Name)), value.GetType().Name); + } + base.Add(value); + } + } + + /// + /// DCMapping is used to change the mapping and clip region of the + /// the specified device context to the given bounds. When the + /// DCMapping is disposed, the original mapping and clip rectangle + /// are restored. + /// + /// Example: + /// + /// using(WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, new Rectangle(10,10, 50, 50) { + /// // inside here the hDC's mapping of (0,0) is inset by (10,10) and + /// // all painting is clipped at (0,0) - (50,50) + /// } + /// + /// To use with GDI+ you can get the hDC from the Graphics object. You'd want to do this in a situation where + /// you're handing off a graphics object to someone, and you want the world translated some amount X,Y. This + /// works better than g.TranslateTransform(x,y) - as if someone calls g.GetHdc and does a GDI operation - their + /// world is NOT transformed. + /// + /// HandleRef hDC = new HandleRef(this, originalGraphics.GetHdc()); + /// try { + /// using(WindowsFormsUtils.DCMapping mapping = new WindowsFormsUtils.DCMapping(hDC, new Rectangle(10,10, 50, 50) { + /// + /// // DO NOT ATTEMPT TO USE originalGraphics here - you'll get an Object Busy error + /// // rather ask the mapping object for a graphics object. + /// mapping.Graphics.DrawRectangle(Pens.Black, rect); + /// } + /// } + /// finally { g.ReleaseHdc(hDC.Handle);} + /// + /// PERF: DCMapping is a structure so that it will allocate on the stack rather than in GC managed + /// memory. This way disposing the object does not force a GC. Since DCMapping objects aren't + /// likely to be passed between functions rather used and disposed in the same one, this reduces + /// overhead. + /// + internal struct DCMapping : IDisposable { + + private DeviceContext dc; + private Graphics graphics; + Rectangle translatedBounds; + + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + public DCMapping(HandleRef hDC, Rectangle bounds) { + if (hDC.Handle == IntPtr.Zero) { + throw new ArgumentNullException("hDC"); + } + + bool success; + NativeMethods.POINT viewportOrg = new NativeMethods.POINT(); + HandleRef hOriginalClippingRegion = NativeMethods.NullHandleRef; + NativeMethods.RegionFlags originalRegionType = NativeMethods.RegionFlags.NULLREGION; + + this.translatedBounds = bounds; + this.graphics = null; + this.dc = DeviceContext.FromHdc(hDC.Handle); + + this.dc.SaveHdc(); + + // Retrieve the x-coordinates and y-coordinates of the viewport origin for the specified device context. + success = SafeNativeMethods.GetViewportOrgEx(hDC, viewportOrg); + Debug.Assert(success, "GetViewportOrgEx() failed."); + + // Create a new rectangular clipping region based off of the bounds specified, shifted over by the x & y specified in the viewport origin. + HandleRef hClippingRegion = new HandleRef(null, SafeNativeMethods.CreateRectRgn(viewportOrg.x + bounds.Left, viewportOrg.y + bounds.Top, viewportOrg.x + bounds.Right, viewportOrg.y + bounds.Bottom)); + Debug.Assert(hClippingRegion.Handle != IntPtr.Zero, "CreateRectRgn() failed."); + + try + { + // Create an empty region oriented at 0,0 so we can populate it with the original clipping region of the hDC passed in. + hOriginalClippingRegion = new HandleRef(this, SafeNativeMethods.CreateRectRgn(0, 0, 0, 0)); + Debug.Assert(hOriginalClippingRegion.Handle != IntPtr.Zero, "CreateRectRgn() failed."); + + // Get the clipping region from the hDC: result = {-1 = error, 0 = no region, 1 = success} per MSDN + int result = SafeNativeMethods.GetClipRgn(hDC, hOriginalClippingRegion); + Debug.Assert(result != -1, "GetClipRgn() failed."); + + // Shift the viewpoint origint by coordinates specified in "bounds". + NativeMethods.POINT lastViewPort = new NativeMethods.POINT(); + success = SafeNativeMethods.SetViewportOrgEx(hDC, viewportOrg.x + bounds.Left, viewportOrg.y + bounds.Top, lastViewPort); + Debug.Assert(success, "SetViewportOrgEx() failed."); + + if (result != 0) + { + // Get the origninal clipping region so we can determine its type (we'll check later if we've restored the region back properly.) + NativeMethods.RECT originalClipRect = new NativeMethods.RECT(); + originalRegionType = (NativeMethods.RegionFlags)SafeNativeMethods.GetRgnBox(hOriginalClippingRegion, ref originalClipRect); + Debug.Assert(originalRegionType != NativeMethods.RegionFlags.ERROR, "ERROR returned from SelectClipRgn while selecting the original clipping region.."); + + if (originalRegionType == NativeMethods.RegionFlags.SIMPLEREGION) + { + // Find the intersection of our clipping region and the current clipping region (our parent's) + // Returns a NULLREGION, the two didn't intersect. + // Returns a SIMPLEREGION, the two intersected + // Resulting region (stuff that was in hOriginalClippingRegion AND hClippingRegion is placed in hClippingRegion + NativeMethods.RegionFlags combineResult = (NativeMethods.RegionFlags)SafeNativeMethods.CombineRgn(hClippingRegion, hClippingRegion, hOriginalClippingRegion, NativeMethods.RGN_AND); + Debug.Assert((combineResult == NativeMethods.RegionFlags.SIMPLEREGION) || + (combineResult == NativeMethods.RegionFlags.NULLREGION), + "SIMPLEREGION or NULLREGION expected."); + } + } + else + { + // If there was no clipping region, then the result is a simple region. + // We don't need to keep track of the original now, since it is empty. + SafeNativeMethods.DeleteObject(hOriginalClippingRegion); + hOriginalClippingRegion = new HandleRef(null, IntPtr.Zero); + originalRegionType = NativeMethods.RegionFlags.SIMPLEREGION; + } + + // Select the new clipping region; make sure it's a SIMPLEREGION or NULLREGION + NativeMethods.RegionFlags selectResult = (NativeMethods.RegionFlags)SafeNativeMethods.SelectClipRgn(hDC, hClippingRegion); + Debug.Assert((selectResult == NativeMethods.RegionFlags.SIMPLEREGION || + selectResult == NativeMethods.RegionFlags.NULLREGION), + "SIMPLEREGION or NULLLREGION expected."); + + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + this.dc.RestoreHdc(); + this.dc.Dispose(); + } + finally { + // Delete the new clipping region, as the clipping region for the HDC is now set + // to this rectangle. Hold on to hOriginalClippingRegion, as we'll need to restore + // it when this object is disposed. + success = SafeNativeMethods.DeleteObject(hClippingRegion); + Debug.Assert(success, "DeleteObject(hClippingRegion) failed."); + + if (hOriginalClippingRegion.Handle != IntPtr.Zero) { + success = SafeNativeMethods.DeleteObject(hOriginalClippingRegion); + Debug.Assert(success, "DeleteObject(hOriginalClippingRegion) failed."); + } + } + } + + public void Dispose() { + if( graphics != null ){ + // Reset GDI+ if used. + // we need to dispose the graphics object first, as it will do + // some restoration to the ViewPort and ClipRectangle to restore the hDC to + // the same state it was created in + graphics.Dispose(); + graphics = null; + } + + if (this.dc != null) { + // Now properly reset GDI. + this.dc.RestoreHdc(); + this.dc.Dispose(); + this.dc = null; + } + } + + /// + /// Allows you to get the graphics object based off of the translated HDC. + /// Note this will be disposed when the DCMapping object is disposed. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public Graphics Graphics { + [ResourceExposure(ResourceScope.Process)] + [ResourceConsumption(ResourceScope.Process)] + get { + Debug.Assert(this.dc != null, "unexpected null dc!"); + + if (this.graphics == null) { + this.graphics = Graphics.FromHdcInternal(dc.Hdc); + this.graphics.SetClip(new Rectangle(Point.Empty, translatedBounds.Size)); + } + return this.graphics; + } + } + } + // All 4.x version of the .NET framework are installed in place. For compatibility reasons, we + // want to know when an application is targeting netfx 4.5 or later vs 4.0 + internal static bool TargetsAtLeast_v4_5 { + get { + return _targetsAtLeast_v4_5; + } + } + + [SecuritySafeCritical] + [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)] + private static bool RunningOnCheck(string propertyName) { + Type binaryCompatibitlityType; + try { + binaryCompatibitlityType = typeof(Object).GetTypeInfo().Assembly.GetType("System.Runtime.Versioning.BinaryCompatibility", false); + } + catch (TypeLoadException) { + return false; + } + + if (binaryCompatibitlityType == null) { + return false; + } + + PropertyInfo runningOnV4_5_Property = binaryCompatibitlityType.GetProperty(propertyName, Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Static); + if (runningOnV4_5_Property == null) { + return false; + } + + return (bool)runningOnV4_5_Property.GetValue(null); + } + } + +} + + + diff --git a/WindowsForms/Managed/System/WinForms/WindowsFormsSectionHandler.cs b/WindowsForms/Managed/System/WinForms/WindowsFormsSectionHandler.cs new file mode 100644 index 000000000..6816d3085 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WindowsFormsSectionHandler.cs @@ -0,0 +1,63 @@ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope="member", Target="System.Windows.Forms.WindowsFormsSectionHandler..ctor()")] + +namespace System.Windows.Forms { + using System; + using System.Diagnostics; + using System.Configuration; + + public sealed class WindowsFormsSection : ConfigurationSection { + internal const bool JitDebuggingDefault = false; + + private static ConfigurationPropertyCollection s_properties; + private static ConfigurationProperty s_propJitDebugging; + + internal static WindowsFormsSection GetSection() { + WindowsFormsSection section = null; + + try { + section = (WindowsFormsSection) System.Configuration.PrivilegedConfigurationManager.GetSection("system.windows.forms"); + } + catch { + Debug.Fail("Exception loading config for windows forms"); + section = new WindowsFormsSection(); + } + + return section; + } + + private static ConfigurationPropertyCollection EnsureStaticPropertyBag() { + if (s_properties == null) { + s_propJitDebugging = new ConfigurationProperty("jitDebugging", typeof(bool), JitDebuggingDefault, ConfigurationPropertyOptions.None); + + ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection(); + properties.Add(s_propJitDebugging); + s_properties = properties; + } + + return s_properties; + } + + public WindowsFormsSection() { + EnsureStaticPropertyBag(); + } + + protected override ConfigurationPropertyCollection Properties { + get { + return EnsureStaticPropertyBag(); + } + } + + [ConfigurationProperty("jitDebugging", DefaultValue=JitDebuggingDefault)] + public bool JitDebugging { + get { + return (bool) base[s_propJitDebugging]; + } + + set { + base[s_propJitDebugging] = value; + } + } + } +} + diff --git a/WindowsForms/Managed/System/WinForms/WindowsFormsSynchronizationContext.cs b/WindowsForms/Managed/System/WinForms/WindowsFormsSynchronizationContext.cs new file mode 100644 index 000000000..4bdf86c44 --- /dev/null +++ b/WindowsForms/Managed/System/WinForms/WindowsFormsSynchronizationContext.cs @@ -0,0 +1,187 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.Threading; +using System.Windows.Forms; +using System.Diagnostics; +using System.ComponentModel; +using System.Security; +using System.Security.Permissions; + +namespace System.Windows.Forms +{ + /// + /// + /// SynchronizationContext subclass used by the Windows Forms package. + /// + public sealed class WindowsFormsSynchronizationContext : SynchronizationContext, IDisposable { + private Control controlToSendTo; + private WeakReference destinationThreadRef; + + //ThreadStatics won't get initialized per thread: easiest to just invert the value. + [ThreadStatic] + private static bool dontAutoInstall; + + [ThreadStatic] + private static bool inSyncContextInstallation; + + [ThreadStatic] + private static SynchronizationContext previousSyncContext; + + + /// + public WindowsFormsSynchronizationContext() { + DestinationThread = Thread.CurrentThread; //store the current thread to ensure its still alive during an invoke. + Application.ThreadContext context = Application.ThreadContext.FromCurrent(); + Debug.Assert(context != null); + if (context != null) { + controlToSendTo = context.MarshalingControl; + } + Debug.Assert(controlToSendTo.IsHandleCreated, "Marshaling control should have created its handle in its ctor."); + } + + private WindowsFormsSynchronizationContext(Control marshalingControl, Thread destinationThread) { + controlToSendTo = marshalingControl; + this.DestinationThread = destinationThread; + Debug.Assert(controlToSendTo.IsHandleCreated, "Marshaling control should have created its handle in its ctor."); + } + + // VSWhidbey 476889: Directly holding onto the Thread can prevent ThreadStatics from finalizing. + private Thread DestinationThread { + get { + if ((destinationThreadRef != null) && (destinationThreadRef.IsAlive)) { + return destinationThreadRef.Target as Thread; + } + return null; + } + set { + if (value != null) { + destinationThreadRef = new WeakReference(value); + } + } + } + public void Dispose() { + if (controlToSendTo != null) { + if (!controlToSendTo.IsDisposed) { + controlToSendTo.Dispose(); + } + controlToSendTo = null; + } + } + + /// + // This is never called because we decide whether to Send or Post and we always post + public override void Send(SendOrPostCallback d, Object state) { + Thread destinationThread = DestinationThread; + if (destinationThread == null || !destinationThread.IsAlive) { + throw new InvalidAsynchronousStateException(SR.GetString(SR.ThreadNoLongerValid)); + } + + Debug.Assert(controlToSendTo != null, "Should always have the marshaling control by this point"); + + if (controlToSendTo != null) { + controlToSendTo.Invoke(d, new object[] { state }); + } + } + + /// + public override void Post(SendOrPostCallback d, Object state) { + Debug.Assert(controlToSendTo != null, "Should always have the marshaling control by this point"); + + if (controlToSendTo != null) { + controlToSendTo.BeginInvoke(d, new object[] { state }); + } + } + + /// + public override SynchronizationContext CreateCopy() { + return new WindowsFormsSynchronizationContext(controlToSendTo, DestinationThread); + } + + /// + // Determines whether we install the WindowsFormsSynchronizationContext when we create a control, or + // when we start a message loop. Default: true. + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static bool AutoInstall { + get { + return !dontAutoInstall; + } + set { + dontAutoInstall = !value; + } + } + + // Instantiate and install a WF op sync context, and save off the old one. + internal static void InstallIfNeeded() { + // Exit if we shouldn't auto-install, if we've already installed and we haven't uninstalled, + // or if we're being called recursively (creating the WF + // async op sync context can create a parking window control). + if (!AutoInstall || inSyncContextInstallation) { + return; + } + + if (SynchronizationContext.Current == null) { + previousSyncContext = null; + } + + if (previousSyncContext != null) { + return; + } + + inSyncContextInstallation = true; + try { + SynchronizationContext currentContext = AsyncOperationManager.SynchronizationContext; + //Make sure we either have no sync context or that we have one of type SynchronizationContext + if (currentContext == null || currentContext.GetType() == typeof(SynchronizationContext)) { + previousSyncContext = currentContext; + + // SECREVIEW : WindowsFormsSynchronizationContext.cctor generates a call to SetTopLevel on the MarshallingControl (hidden sycn hwnd), + // this call demands TopLevelWindow (UIPermissionWindow.SafeTopLevelWindows) so an assert for that permission is needed here. + // The assert is safe, we are creating a thread context and the sync window is hidden. + new PermissionSet(PermissionState.Unrestricted).Assert(); + try { + AsyncOperationManager.SynchronizationContext = new WindowsFormsSynchronizationContext(); + } + finally { + CodeAccessPermission.RevertAssert(); + } + } + } + finally { + inSyncContextInstallation = false; + } + } + + public static void Uninstall() { + Uninstall(true); + } + + internal static void Uninstall(bool turnOffAutoInstall) { + if (AutoInstall) { + WindowsFormsSynchronizationContext winFormsSyncContext = AsyncOperationManager.SynchronizationContext as WindowsFormsSynchronizationContext; + if (winFormsSyncContext != null) { + try { + new PermissionSet(PermissionState.Unrestricted).Assert(); + if (previousSyncContext == null) { + AsyncOperationManager.SynchronizationContext = new SynchronizationContext(); + } + else { + AsyncOperationManager.SynchronizationContext = previousSyncContext; + } + } + finally { + previousSyncContext = null; + CodeAccessPermission.RevertAssert(); + } + } + } + if (turnOffAutoInstall) { + AutoInstall = false; + } + } + } +}